Merge "Fix issues from merge"
diff --git a/Android.bp b/Android.bp
index b50593d..e825610 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,6 +27,7 @@
 
 java_library {
     name: "framework",
+    installable: true,
 
     srcs: [
         // From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS
@@ -58,6 +59,7 @@
         "core/java/android/app/IActivityController.aidl",
         "core/java/android/app/IActivityManager.aidl",
         "core/java/android/app/IActivityPendingResult.aidl",
+        "core/java/android/app/IActivityTaskManager.aidl",
         "core/java/android/app/IAlarmCompleteListener.aidl",
         "core/java/android/app/IAlarmListener.aidl",
         "core/java/android/app/IAlarmManager.aidl",
@@ -83,6 +85,7 @@
         "core/java/android/app/IUidObserver.aidl",
         "core/java/android/app/IUiAutomationConnection.aidl",
         "core/java/android/app/IUiModeManager.aidl",
+        "core/java/android/app/IUriGrantsManager.aidl",
         "core/java/android/app/IUserSwitchObserver.aidl",
         "core/java/android/app/IWallpaperManager.aidl",
         "core/java/android/app/IWallpaperManagerCallback.aidl",
@@ -91,6 +94,7 @@
         "core/java/android/app/trust/IStrongAuthTracker.aidl",
         "core/java/android/app/trust/ITrustManager.aidl",
         "core/java/android/app/trust/ITrustListener.aidl",
+        "core/java/android/app/backup/IBackupCallback.aidl",
         "core/java/android/app/backup/IBackupManager.aidl",
         "core/java/android/app/backup/IBackupObserver.aidl",
         "core/java/android/app/backup/IBackupManagerMonitor.aidl",
@@ -103,6 +107,7 @@
         "core/java/android/app/timedetector/ITimeDetectorService.aidl",
         "core/java/android/app/timezone/ICallback.aidl",
         "core/java/android/app/timezone/IRulesManager.aidl",
+        "core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl",
         "core/java/android/app/usage/ICacheQuotaService.aidl",
         "core/java/android/app/usage/IStorageStatsManager.aidl",
         "core/java/android/app/usage/IUsageStatsManager.aidl",
@@ -147,12 +152,14 @@
         "core/java/android/hardware/IConsumerIrService.aidl",
         "core/java/android/hardware/ISerialManager.aidl",
         "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
+        "core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
         "core/java/android/hardware/display/IDisplayManager.aidl",
         "core/java/android/hardware/display/IDisplayManagerCallback.aidl",
         "core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
         "core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl",
+        "core/java/android/hardware/face/IFaceService.aidl",
+        "core/java/android/hardware/face/IFaceServiceReceiver.aidl",
         "core/java/android/hardware/fingerprint/IFingerprintService.aidl",
-        "core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl",
         "core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl",
         "core/java/android/hardware/hdmi/IHdmiControlCallback.aidl",
         "core/java/android/hardware/hdmi/IHdmiControlService.aidl",
@@ -474,6 +481,8 @@
         "media/java/android/media/tv/ITvRemoteServiceInput.aidl",
         "media/java/android/service/media/IMediaBrowserService.aidl",
         "media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl",
+        "telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl",
+        "telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl",
         "telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl",
         "telecomm/java/com/android/internal/telecom/ICallScreeningService.aidl",
         "telecomm/java/com/android/internal/telecom/IVideoCallback.aidl",
@@ -569,6 +578,7 @@
         "telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl",
         "telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl",
         "wifi/java/android/net/wifi/ISoftApCallback.aidl",
+        "wifi/java/android/net/wifi/ITrafficStateCallback.aidl",
         "wifi/java/android/net/wifi/IWifiManager.aidl",
         "wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl",
         "wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl",
@@ -646,6 +656,8 @@
             "system/bt/binder",
             "system/security/keystore/binder",
         ],
+
+        generate_get_transaction_name: true
     },
 
     exclude_srcs: [
@@ -702,6 +714,7 @@
 // specified on the build command line.
 java_library {
     name: "framework-oahl-backward-compatibility",
+    installable: true,
     srcs: [
         "core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java",
     ],
@@ -730,22 +743,24 @@
     name: "framework-javastream-protos",
     depfile: true,
 
+    tool_files: [ "tools/genprotos.sh", ],
     tools: [
         "aprotoc",
         "protoc-gen-javastream",
         "soong_zip",
     ],
 
-    cmd: "mkdir -p $(genDir)/$(in) " +
-        "&& $(location aprotoc) " +
-        "  --plugin=$(location protoc-gen-javastream) " +
-        "  --dependency_out=$(depfile) " +
-        "  --javastream_out=$(genDir)/$(in) " +
-        "  -Iexternal/protobuf/src " +
-        "  -I . " +
-        "  $(in) " +
-        "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
-
+    // TODO This should not be needed. If you set a custom OUT_DIR or OUT_DIR_COMMON_BASE you can
+    // end up with a command that is extremely long, potentially going passed MAX_ARG_STRLEN due to
+    // the way sbox rewrites the command. See b/70221552.
+    cmd: "$(location tools/genprotos.sh) " +
+              " $(location aprotoc) " +
+              " $(location protoc-gen-javastream) " +
+              " $(location soong_zip) " +
+              " $(genDir) " +
+              " $(depfile) " +
+              " $(in) " +
+              " $(out)",
     srcs: [
         "core/proto/**/*.proto",
         "libs/incident/**/*.proto",
@@ -757,6 +772,7 @@
 // ============================================================
 java_library {
     name: "ext",
+    installable: true,
     no_framework_libs: true,
     static_libs: [
         "libphonenumber-platform",
@@ -990,6 +1006,7 @@
      "-since $(location 26/public/api/android.txt) 26 " +
      "-since $(location 27/public/api/android.txt) 27 " +
      "-since $(location 28/public/api/android.txt) 28 " +
+     "-since $(location api/current.txt) Q " +
      "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " +
      "-overview $(location core/java/overview.html) " +
      // Federate Support Library references against local API file.
@@ -997,7 +1014,7 @@
      "-federationapi SupportLib $(location current/support-api.txt) "
 
 doc_defaults {
-    name: "framework-docs-default",
+    name: "api-stubs-default",
     srcs: [
         ":opt-telephony-srcs",
         ":opt-net-voip-srcs",
@@ -1047,7 +1064,7 @@
 
 droiddoc {
     name: "api-stubs-docs",
-    defaults: ["framework-docs-default"],
+    defaults: ["api-stubs-default"],
     arg_files: [
         "core/res/AndroidManifest.xml",
         ":api-version-xml",
@@ -1079,7 +1096,7 @@
 
 droiddoc {
     name: "system-api-stubs-docs",
-    defaults: ["framework-docs-default"],
+    defaults: ["api-stubs-default"],
     arg_files: [
         "core/res/AndroidManifest.xml",
         ":api-version-xml",
@@ -1113,7 +1130,7 @@
 
 droiddoc {
     name: "test-api-stubs-docs",
-    defaults: ["framework-docs-default"],
+    defaults: ["api-stubs-default"],
     arg_files: [
         "core/res/AndroidManifest.xml",
         ":api-version-xml",
@@ -1138,6 +1155,288 @@
     },
 }
 
+doc_defaults {
+    name: "framework-docs-default",
+    srcs: [
+        "test-base/src/**/*.java",
+        ":opt-telephony-srcs",
+        ":opt-net-voip-srcs",
+        ":openjdk_javadoc_files",
+        ":non_openjdk_javadoc_files",
+        ":android_icu4j_src_files_for_docs",
+        ":gen-ojluni-jaif-annotated-srcs",
+        "test-mock/src/**/*.java",
+        "test-runner/src/**/*.java",
+    ],
+    exclude_srcs: [
+        ":annotated_ojluni_files",
+    ],
+    srcs_lib: "framework",
+    srcs_lib_whitelist_dirs: frameworks_base_subdirs,
+    srcs_lib_whitelist_pkgs: packages_to_document,
+    libs: [
+        "conscrypt",
+        "bouncycastle",
+        "voip-common",
+        "android.test.mock",
+        "android-support-annotations",
+        "android-support-compat",
+        "android-support-core-ui",
+        "android-support-core-utils",
+        "android-support-customtabs",
+        "android-support-design",
+        "android-support-dynamic-animation",
+        "android-support-exifinterface",
+        "android-support-fragment",
+        "android-support-media-compat",
+        "android-support-percent",
+        "android-support-recommendation",
+        "android-support-transition",
+        "android-support-tv-provider",
+        "android-support-v7-cardview",
+        "android-support-v7-gridlayout",
+        "android-support-v7-mediarouter",
+        "android-support-v7-palette",
+        "android-support-v7-preference",
+        "android-support-v13",
+        "android-support-v14-preference",
+        "android-support-v17-leanback",
+        "android-support-v17-preference-leanback",
+        "android-support-wear",
+        "android-support-vectordrawable",
+        "android-support-animatedvectordrawable",
+        "android-support-v7-appcompat",
+        "android-support-v7-recyclerview",
+        "android-support-emoji",
+        "android-support-emoji-appcompat",
+        "android-support-emoji-bundled",
+        "android-support-v8-renderscript",
+        "android-support-multidex",
+        "android-support-multidex-instrumentation",
+    ],
+    local_sourcepaths: frameworks_base_subdirs,
+    html_dirs: [
+        "docs/html",
+    ],
+    knowntags: [
+        "docs/knowntags.txt",
+        ":known-oj-tags",
+    ],
+    custom_template: "droiddoc-templates-sdk",
+    resourcesdir: "docs/html/reference/images/",
+    resourcesoutdir: "reference/android/images/",
+    hdf: [
+        "dac true",
+        "sdk.codename O",
+        "sdk.preview.version 1",
+        "sdk.version 7.0",
+        "sdk.rel.id 1",
+        "sdk.preview 0",
+    ],
+    arg_files: [
+        "core/res/AndroidManifest.xml",
+        ":api-version-xml",
+        "core/java/overview.html",
+        ":current-support-api",
+        "api/current.txt",
+    ],
+    create_stubs: false,
+}
+
+droiddoc {
+    name: "doc-comment-check-docs",
+    defaults: ["framework-docs-default"],
+    args: framework_docs_args + " -referenceonly -parsecomments",
+    installable: false,
+}
+
+droiddoc {
+    name: "offline-sdk-docs",
+    defaults: ["framework-docs-default"],
+    hdf: [
+        "android.whichdoc offline",
+    ],
+    proofread_file: "offline-sdk-docs-proofrerad.txt",
+    args: framework_docs_args + " -offlinemode -title \"Android SDK\"",
+    write_sdk_values: true,
+    static_doc_index_redirect: "docs/docs-preview-index.html",
+}
+
+droiddoc {
+    name: "offline-sdk-referenceonly-docs",
+    defaults: ["framework-docs-default"],
+    hdf: [
+        "android.whichdoc offline",
+    ],
+    proofread_file: "offline-sdk-referenceonly-docs-proofrerad.txt",
+    args: framework_docs_args + " -offlinemode -title \"Android SDK\" -referenceonly",
+    write_sdk_values: true,
+    static_doc_index_redirect: "docs/docs-documentation-redirect.html",
+    static_doc_properties: "docs/source.properties",
+}
+
+droiddoc {
+    name: "offline-system-sdk-referenceonly-docs",
+    defaults: ["framework-docs-default"],
+    hdf: [
+        "android.whichdoc offline",
+    ],
+    proofread_file: "offline-system-sdk-referenceonly-docs-proofrerad.txt",
+    args: framework_docs_args + " -hide 101 -hide 104 -hide 108" +
+          " -showAnnotation android.annotation.SystemApi " +
+          " -offlinemode -title \"Android System SDK\" -referenceonly",
+    write_sdk_values: true,
+    static_doc_index_redirect: "docs/docs-documentation-redirect.html",
+    static_doc_properties: "docs/source.properties",
+}
+
+droiddoc {
+    name: "online-sdk-docs",
+    defaults: ["framework-docs-default"],
+    hdf: [
+        "android.whichdoc online",
+        "android.hasSamples true",
+    ],
+    proofread_file: "online-sdk-docs-proofrerad.txt",
+    args: framework_docs_args +
+        " -toroot / -samplegroup Admin " +
+        " -samplegroup Background " +
+        " -samplegroup Connectivity " +
+        " -samplegroup Content " +
+        " -samplegroup Input " +
+        " -samplegroup Media " +
+        " -samplegroup Notification " +
+        " -samplegroup RenderScript " +
+        " -samplegroup Security " +
+        " -samplegroup Sensors " +
+        " -samplegroup System " +
+        " -samplegroup Testing " +
+        " -samplegroup UI " +
+        " -samplegroup Views " +
+        " -samplegroup Wearable -samplesdir development/samples/browseable ",
+}
+
+droiddoc {
+    name: "online-system-api-sdk-docs",
+    defaults: ["framework-docs-default"],
+    hdf: [
+        "android.whichdoc online",
+        "android.hasSamples true",
+    ],
+    proofread_file: "online-system-api-sdk-docs-proofrerad.txt",
+    args: framework_docs_args +
+        " -referenceonly " +
+        " -showAnnotation android.annotation.SystemApi " +
+        " -title \"Android SDK - Including system APIs.\" " +
+        " -hide 101 " +
+        " -hide 104 " +
+        " -hide 108 " +
+        " -toroot / -samplegroup Admin " +
+        " -samplegroup Background " +
+        " -samplegroup Connectivity " +
+        " -samplegroup Content " +
+        " -samplegroup Input " +
+        " -samplegroup Media " +
+        " -samplegroup Notification " +
+        " -samplegroup RenderScript " +
+        " -samplegroup Security " +
+        " -samplegroup Sensors " +
+        " -samplegroup System " +
+        " -samplegroup Testing " +
+        " -samplegroup UI " +
+        " -samplegroup Views " +
+        " -samplegroup Wearable -samplesdir development/samples/browseable ",
+    installable: false,
+}
+
+droiddoc {
+    name: "ds-docs",
+    defaults: ["framework-docs-default"],
+    hdf: [
+        "android.whichdoc online",
+        "android.hasSamples true",
+    ],
+    proofread_file: "ds-docs-proofrerad.txt",
+    args: framework_docs_args +
+        " -toroot / -samplegroup Admin " +
+        " -samplegroup Background " +
+        " -samplegroup Connectivity " +
+        " -samplegroup Content " +
+        " -samplegroup Input " +
+        " -samplegroup Media " +
+        " -samplegroup Notification " +
+        " -samplegroup RenderScript " +
+        " -samplegroup Security " +
+        " -samplegroup Sensors " +
+        " -samplegroup System " +
+        " -samplegroup Testing " +
+        " -samplegroup UI " +
+        " -samplegroup Views " +
+        " -samplegroup Wearable -devsite -samplesdir development/samples/browseable ",
+}
+
+droiddoc {
+    name: "ds-static-docs",
+    defaults: ["framework-docs-default"],
+    hdf: [
+        "android.whichdoc online",
+    ],
+    proofread_file: "ds-static-docs-proofrerad.txt",
+    args: framework_docs_args +
+          " -staticonly " +
+          " -toroot / " +
+          " -devsite " +
+          " -ignoreJdLinks ",
+}
+
+droiddoc {
+    name: "ds-ref-navtree-docs",
+    defaults: ["framework-docs-default"],
+    hdf: [
+        "android.whichdoc online",
+    ],
+    proofread_file: "ds-ref-navtree-docs-proofrerad.txt",
+    args: framework_docs_args +
+          " -toroot / " +
+          " -atLinksNavtree " +
+          " -navtreeonly ",
+}
+
+droiddoc {
+    name: "online-sdk-dev-docs",
+    defaults: ["framework-docs-default"],
+    hdf: [
+        "android.whichdoc online",
+        "android.hasSamples true",
+    ],
+    proofread_file: "online-sdk-dev-docs-proofrerad.txt",
+    args: framework_docs_args +
+        " -toroot / -samplegroup Admin " +
+        " -samplegroup Background " +
+        " -samplegroup Connectivity " +
+        " -samplegroup Content " +
+        " -samplegroup Input " +
+        " -samplegroup Media " +
+        " -samplegroup Notification " +
+        " -samplegroup RenderScript " +
+        " -samplegroup Security " +
+        " -samplegroup Sensors " +
+        " -samplegroup System " +
+        " -samplegroup Testing " +
+        " -samplegroup UI " +
+        " -samplegroup Views " +
+        " -samplegroup Wearable -samplesdir development/samples/browseable ",
+}
+
+droiddoc {
+    name: "hidden-docs",
+    defaults: ["framework-docs-default"],
+    proofread_file: "hidden-docs-proofrerad.txt",
+    args: framework_docs_args +
+          " -referenceonly " +
+          " -title \"Android SDK - Including hidden APIs.\"",
+}
+
 droiddoc {
     name: "hwbinder-stubs-docs",
     srcs: [
@@ -1171,13 +1470,15 @@
 
 droiddoc {
     name: "hiddenapi-lists",
-    defaults: ["framework-docs-default"],
+    defaults: ["api-stubs-default"],
     arg_files: [
         "core/res/AndroidManifest.xml",
         ":api-version-xml",
         "core/java/overview.html",
         ":current-support-api",
+        "api/current.txt",
     ],
+    dex_api_filename: "public-dex.txt",
     private_dex_api_filename: "private-dex.txt",
     removed_dex_api_filename: "removed-dex.txt",
     args: framework_docs_args +
@@ -1188,6 +1489,25 @@
           " -showAnnotation android.annotation.TestApi",
 }
 
+droiddoc {
+    name: "hiddenapi-mappings",
+    defaults: ["api-stubs-default"],
+    arg_files: [
+        "core/res/AndroidManifest.xml",
+        ":api-version-xml",
+        "core/java/overview.html",
+        ":current-support-api",
+        "api/current.txt",
+    ],
+    dex_mapping_filename: "dex-mapping.txt",
+    args: framework_docs_args +
+          " -referenceonly" +
+          " -nodocs" +
+          " -showUnannotated" +
+          " -showAnnotation android.annotation.SystemApi" +
+          " -showAnnotation android.annotation.TestApi",
+}
+
 filegroup {
     name: "apache-http-stubs-sources",
     srcs: [
@@ -1221,13 +1541,8 @@
     "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo"
 
 doc_defaults {
-    name: "metalava-framework-docs-default",
+    name: "metalava-api-stubs-default",
     srcs: [
-        // test mock src files.
-        "test-mock/src/android/test/mock/**/*.java",
-        // test runner excluding mock src files.
-        "test-runner/src/**/*.java",
-        "test-base/src/**/*.java",
         ":opt-telephony-srcs",
         ":opt-net-voip-srcs",
         ":openjdk_javadoc_files",
@@ -1255,11 +1570,16 @@
     local_sourcepaths: frameworks_base_subdirs,
     installable: false,
     metalava_enabled: true,
+    metalava_annotations_enabled: true,
+    metalava_previous_api: ":public-api-for-metalava-annotations",
+    metalava_merge_annotations_dirs: [
+        "metalava-manual",
+    ],
 }
 
 droiddoc {
     name: "metalava-api-stubs-docs",
-    defaults: ["metalava-framework-docs-default"],
+    defaults: ["metalava-api-stubs-default"],
     api_tag_name: "METALAVA_PUBLIC",
     api_filename: "public_api.txt",
     private_api_filename: "private.txt",
@@ -1272,7 +1592,7 @@
 
 droiddoc {
     name: "metalava-system-api-stubs-docs",
-    defaults: ["metalava-framework-docs-default"],
+    defaults: ["metalava-api-stubs-default"],
     api_tag_name: "METALAVA_SYSTEM",
     api_filename: "system-api.txt",
     private_api_filename: "system-private.txt",
@@ -1286,7 +1606,7 @@
 
 droiddoc {
     name: "metalava-test-api-stubs-docs",
-    defaults: ["metalava-framework-docs-default"],
+    defaults: ["metalava-api-stubs-default"],
     api_tag_name: "METALAVA_TEST",
     api_filename: "test-api.txt",
     removed_api_filename: "test-removed.txt",
diff --git a/Android.mk b/Android.mk
index 031809c..edbb94d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -171,15 +171,17 @@
     -hidePackage com.android.server
 
 # Convert an sdk level to a "since" argument.
-since-arg = -since $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/$(1)/public/api/android.*) $(1)
+since-arg = -since $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/$(1)/public/api/android.$(2)) $(1)
 
-finalized_sdks := $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.xml,%,\
-    $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.xml))
-finalized_sdks += $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.txt,%,\
-    $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.txt))
-finalized_sdks := $(call numerically_sort,$(finalized_sdks))
+finalized_xml_sdks := $(call numerically_sort,\
+    $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.xml,%,\
+    $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.xml)))
+finalized_txt_sdks := $(call numerically_sort,\
+     $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.txt,%,\
+    $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.txt)))
 
-framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_sdks),$(call since-arg,$(sdk)))
+framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_xml_sdks),$(call since-arg,$(sdk),xml))
+framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_txt_sdks),$(call since-arg,$(sdk),txt))
 ifneq ($(PLATFORM_VERSION_CODENAME),REL)
   framework_docs_LOCAL_DROIDDOC_OPTIONS += \
       -since ./frameworks/base/api/current.txt $(PLATFORM_VERSION_CODENAME)
@@ -311,364 +313,13 @@
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE))
 
-# ====  check javadoc comments but don't generate docs ========
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-
-LOCAL_MODULE := doc-comment-check
-
-LOCAL_DROIDDOC_OPTIONS:=\
-		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-		-referenceonly \
-		-parsecomments
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-LOCAL_UNINSTALLABLE_MODULE := true
-
-include $(BUILD_DROIDDOC)
-
-# Run this for checkbuild
-checkbuild: doc-comment-check-docs
-# Check comment when you are updating the API
-update-api: doc-comment-check-docs
-
-# ====  static html in the sdk ==================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-
-LOCAL_MODULE := offline-sdk
-
-LOCAL_DROIDDOC_OPTIONS:=\
-		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-		-offlinemode \
-		-title "Android SDK" \
-		-proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \
-		-sdkvalues $(OUT_DOCS) \
-		-hdf android.whichdoc offline
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-include $(BUILD_DROIDDOC)
-
-static_doc_index_redirect := $(out_dir)/index.html
-$(static_doc_index_redirect): \
-	$(LOCAL_PATH)/docs/docs-preview-index.html | $(ACP)
-	$(hide) mkdir -p $(dir $@)
-	$(hide) $(ACP) $< $@
-
-$(full_target): $(static_doc_index_redirect)
-
-
-# ==== Public API static reference docs ==================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-
-LOCAL_MODULE := offline-sdk-referenceonly
-
-LOCAL_DROIDDOC_OPTIONS:=\
-		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-		-offlinemode \
-		-title "Android SDK" \
-		-proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \
-		-sdkvalues $(OUT_DOCS) \
-		-hdf android.whichdoc offline \
-		-referenceonly
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-include $(BUILD_DROIDDOC)
-
-static_doc_index_redirect := $(out_dir)/index.html
-$(static_doc_index_redirect): $(LOCAL_PATH)/docs/docs-documentation-redirect.html
-	$(copy-file-to-target)
-
-static_doc_properties := $(out_dir)/source.properties
-$(static_doc_properties): \
-	$(LOCAL_PATH)/docs/source.properties | $(ACP)
-	$(hide) mkdir -p $(dir $@)
-	$(hide) $(ACP) $< $@
-
-$(full_target): $(static_doc_index_redirect)
-$(full_target): $(static_doc_properties)
-
-
-# ==== System API static reference docs ==================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-
-LOCAL_MODULE := offline-system-sdk-referenceonly
-
-LOCAL_DROIDDOC_OPTIONS:=\
-		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-		-hide 101 -hide 104 -hide 108 \
-		-showAnnotation android.annotation.SystemApi \
-		-offlinemode \
-		-title "Android System SDK" \
-		-proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \
-		-sdkvalues $(OUT_DOCS) \
-		-hdf android.whichdoc offline \
-		-referenceonly
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-include $(BUILD_DROIDDOC)
-
-static_doc_index_redirect := $(out_dir)/index.html
-$(static_doc_index_redirect): $(LOCAL_PATH)/docs/docs-documentation-redirect.html
-	$(copy-file-to-target)
-
-static_doc_properties := $(out_dir)/source.properties
-$(static_doc_properties): \
-	$(LOCAL_PATH)/docs/source.properties | $(ACP)
-	$(hide) mkdir -p $(dir $@)
-	$(hide) $(ACP) $< $@
-
-$(full_target): $(static_doc_index_redirect)
-$(full_target): $(static_doc_properties)
-$(full_target): $(framework_built)
-
-
-# ==== docs for the web (on the androiddevdocs app engine server) =======================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-LOCAL_ADDITIONAL_HTML_DIR:=docs/html-intl /
-
-LOCAL_MODULE := online-sdk
-
-LOCAL_DROIDDOC_OPTIONS:= \
-		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-		-toroot / \
-		-hdf android.whichdoc online \
-		$(sample_groups) \
-		-hdf android.hasSamples true \
-		-samplesdir $(samples_dir)
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-include $(BUILD_DROIDDOC)
-
-# ==== docs for the web (on the androiddevdocs app engine server) =======================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-LOCAL_ADDITIONAL_HTML_DIR:=docs/html-intl /
-
-LOCAL_MODULE := online-system-api-sdk
-
-LOCAL_DROIDDOC_OPTIONS:= \
-		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-		-referenceonly \
-		-showAnnotation android.annotation.SystemApi \
-		-title "Android SDK - Including system APIs." \
-		-toroot / \
-		-hide 101 \
-		-hide 104 \
-		-hide 108 \
-		-hdf android.whichdoc online \
-		$(sample_groups) \
-		-hdf android.hasSamples true \
-		-samplesdir $(samples_dir)
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-LOCAL_UNINSTALLABLE_MODULE := true
-
-include $(BUILD_DROIDDOC)
-
-# ==== docs for the web (on the devsite app engine server) =======================
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-# specify a second html input dir and an output path relative to OUT_DIR)
-LOCAL_ADDITIONAL_HTML_DIR:=docs/html-intl /
-
-LOCAL_MODULE := ds
-
-LOCAL_DROIDDOC_OPTIONS:= \
-		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-		-toroot / \
-		-hdf android.whichdoc online \
-		-devsite \
-		-yamlV2 \
-		$(sample_groups) \
-		-hdf android.hasSamples true \
-		-samplesdir $(samples_dir)
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-include $(BUILD_DROIDDOC)
-
-# ==== docs for the web (on the devsite app engine server) =======================
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-# specify a second html input dir and an output path relative to OUT_DIR)
-LOCAL_ADDITIONAL_HTML_DIR:=docs/html-intl /
-
-LOCAL_MODULE := ds-static
-
-LOCAL_DROIDDOC_OPTIONS:= \
-		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-		-hdf android.whichdoc online \
-		-staticonly \
-		-toroot / \
-		-devsite \
-		-ignoreJdLinks
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-include $(BUILD_DROIDDOC)
-
-# ==== generates full navtree for resolving @links in ds postprocessing ====
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-
-LOCAL_MODULE := ds-ref-navtree
-
-LOCAL_DROIDDOC_OPTIONS:= \
-		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-		-hdf android.whichdoc online \
-		-toroot / \
-		-atLinksNavtree \
-		-navtreeonly
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-include $(BUILD_DROIDDOC)
-
-# ==== site updates for docs (on the androiddevdocs app engine server) =======================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-LOCAL_ADDITIONAL_HTML_DIR:=docs/html-intl /
-
-LOCAL_MODULE := online-sdk-dev
-
-LOCAL_DROIDDOC_OPTIONS:= \
-		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-		-toroot / \
-		-hdf android.whichdoc online \
-		$(sample_groups) \
-		-hdf android.hasSamples true \
-		-samplesdir $(samples_dir)
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-include $(BUILD_DROIDDOC)
-
-# ==== docs that have all of the stuff that's @hidden =======================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-
-LOCAL_MODULE := hidden
-LOCAL_DROIDDOC_OPTIONS:=\
-		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-		-referenceonly \
-		-title "Android SDK - Including hidden APIs."
-#		-hidden
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-include $(BUILD_DROIDDOC)
+# sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip.
+# So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to
+# $(OUT_DOCS)/offline-sdk.
+$(OUT_DOCS)/offline-sdk-timestamp: $(OUT_DOCS)/offline-sdk-docs-docs.zip
+	$(hide) rm -rf $(OUT_DOCS)/offline-sdk
+	$(hide) mkdir -p $(OUT_DOCS)/offline-sdk
+	( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1
 
 # ====  java proto device library (for test only)  ==============================
 include $(CLEAR_VARS)
@@ -712,13 +363,15 @@
 LOCAL_SRC_GREYLIST := frameworks/base/config/hiddenapi-light-greylist.txt
 LOCAL_SRC_VENDOR_LIST := frameworks/base/config/hiddenapi-vendor-list.txt
 LOCAL_SRC_FORCE_BLACKLIST := frameworks/base/config/hiddenapi-force-blacklist.txt
-LOCAL_SRC_PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+LOCAL_SRC_PUBLIC_API := $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST)
+LOCAL_SRC_PRIVATE_API := $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST)
 LOCAL_SRC_REMOVED_API := $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)
 
 LOCAL_SRC_ALL := \
 	$(LOCAL_SRC_GREYLIST) \
 	$(LOCAL_SRC_VENDOR_LIST) \
 	$(LOCAL_SRC_FORCE_BLACKLIST) \
+	$(LOCAL_SRC_PUBLIC_API) \
 	$(LOCAL_SRC_PRIVATE_API) \
 	$(LOCAL_SRC_REMOVED_API)
 
@@ -770,7 +423,7 @@
     "writeObject\(Ljava/io/ObjectOutputStream;\)V" \
     "writeReplace\(\)Ljava/lang/Object;"
 $(LOCAL_LIGHT_GREYLIST): $(LOCAL_SRC_ALL)
-	sort $(LOCAL_SRC_GREYLIST) $(LOCAL_SRC_VENDOR_LIST) \
+	sort $(LOCAL_SRC_GREYLIST) $(LOCAL_SRC_VENDOR_LIST) $(PRIVATE_GREYLIST_INPUTS) \
 	     <(grep -E "\->("$(subst $(space),"|",$(REGEX_SERIALIZATION))")$$" \
 	               $(LOCAL_SRC_PRIVATE_API)) \
 	     <(comm -12 <(sort $(LOCAL_SRC_REMOVED_API)) <(sort $(LOCAL_SRC_PRIVATE_API))) \
@@ -791,7 +444,8 @@
 #   (4) subtract entries shared with LOCAL_LIGHT_GREYLIST
 $(LOCAL_DARK_GREYLIST): $(LOCAL_SRC_ALL) $(LOCAL_LIGHT_GREYLIST)
 	comm -13 <(sort $(LOCAL_LIGHT_GREYLIST) $(LOCAL_SRC_FORCE_BLACKLIST)) \
-	         <(sed 's/\->.*//' $(LOCAL_LIGHT_GREYLIST) | sed 's/\(.*\/\).*/\1/' | sort | uniq | \
+	         <(cat $(LOCAL_SRC_PUBLIC_API) $(LOCAL_LIGHT_GREYLIST) | \
+	               sed 's/\->.*//' | sed 's/\(.*\/\).*/\1/' | sort | uniq | \
 	               while read PKG_NAME; do \
 	                   grep -E "^$${PKG_NAME}[^/;]*;" $(LOCAL_SRC_PRIVATE_API); \
 	               done | sort | uniq) \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 9e172e7..2247e43 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -245,6 +245,8 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/host/common/obj/JAVA_LIBRARIES/platformprotos_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/statsdprotolite_intermediates)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.mediadrm.signer.jar)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.location.provider.jar)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.future.usb.accessory.jar)
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
 # ******************************************************************
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 1f8ab21..ae42882 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,13 +1,14 @@
 [Hook Scripts]
 checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
-                  -fw core/java/android/
+                  -fw core/
                       graphics/java/android
-                      core/tests/coretests/src/android/
                       packages/PrintRecommendationService/
                       packages/PrintSpooler/
+                      packages/PackageInstaller/
                       services/print/
                       services/usb/
                       telephony/
+                      wifi/
 
 api_lint_hook = ${REPO_ROOT}/frameworks/base/tools/apilint/apilint_sha.sh ${PREUPLOAD_COMMIT}
 
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 a93fa32..62662e4 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
@@ -69,7 +69,13 @@
     public void testFocus_noService() throws Throwable {
         resetService();
 
-        focusTest(false);
+        mActivityRule.runOnUiThread(() -> {
+            BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            while (state.keepRunning()) {
+                mUsername.requestFocus();
+                mPassword.requestFocus();
+            }
+        });
     }
 
     /**
@@ -81,7 +87,20 @@
         MyAutofillService.newCannedResponse().reply();
         setService();
 
-        focusTest(true);
+        // Must first focus in a field to trigger autofill and wait for service response
+        // outside the loop
+        mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+        MyAutofillService.getLastFillRequest();
+        // Then focus on password so loop start with focus away from username
+        mActivityRule.runOnUiThread(() -> mPassword.requestFocus());
+
+        mActivityRule.runOnUiThread(() -> {
+            BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            while (state.keepRunning()) {
+                mUsername.requestFocus();
+                mPassword.requestFocus();
+            }
+        });
     }
 
     /**
@@ -95,7 +114,45 @@
                 .reply();
         setService();
 
-        focusTest(true);
+        // Callback is used to slow down the calls made to the autofill server so the
+        // app is not crashed due to binder exhaustion. But the time spent waiting for the callbacks
+        // is not measured here...
+        MyAutofillCallback callback = new MyAutofillCallback();
+        mAfm.registerCallback(callback);
+
+        // Must first trigger autofill and wait for service response outside the loop
+        mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+        MyAutofillService.getLastFillRequest();
+        callback.expectEvent(mUsername, EVENT_INPUT_SHOWN);
+
+        // Then focus on password so loop start with focus away from username
+        mActivityRule.runOnUiThread(() -> mPassword.requestFocus());
+        callback.expectEvent(mUsername, EVENT_INPUT_HIDDEN);
+        callback.expectEvent(mPassword, EVENT_INPUT_SHOWN);
+
+
+        // NOTE: we cannot run the whole loop inside the UI thread, because the autofill callback
+        // is called on it, which would cause a deadlock on expectEvent().
+        try {
+            BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            while (state.keepRunning()) {
+                mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+                state.pauseTiming(); // Ignore time spent waiting for callbacks
+                callback.expectEvent(mPassword, EVENT_INPUT_HIDDEN);
+                callback.expectEvent(mUsername, EVENT_INPUT_SHOWN);
+                state.resumeTiming();
+                mActivityRule.runOnUiThread(() -> mPassword.requestFocus());
+                state.pauseTiming(); // Ignore time spent waiting for callbacks
+                callback.expectEvent(mUsername, EVENT_INPUT_HIDDEN);
+                callback.expectEvent(mPassword, EVENT_INPUT_SHOWN);
+                state.resumeTiming();
+            }
+
+            // Sanity check
+            callback.assertNoAsyncErrors();
+        } finally {
+            mAfm.unregisterCallback(callback);
+        }
     }
 
     /**
@@ -110,23 +167,41 @@
                 .reply();
         setService();
 
-        focusTest(true);
-    }
+        // Callback is used to slow down the calls made to the autofill server so the
+        // app is not crashed due to binder exhaustion. But the time spent waiting for the callbacks
+        // is not measured here...
+        MyAutofillCallback callback = new MyAutofillCallback();
+        mAfm.registerCallback(callback);
 
-    private void focusTest(boolean waitForService) throws Throwable {
-        // Must first focus in a field to trigger autofill and wait for service response
-        // outside the loop
+        // Must first trigger autofill and wait for service response outside the loop
         mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
-        if (waitForService) {
-            MyAutofillService.getLastFillRequest();
-        }
-        mActivityRule.runOnUiThread(() -> {
+        MyAutofillService.getLastFillRequest();
+        callback.expectEvent(mUsername, EVENT_INPUT_SHOWN);
+
+        // Then focus on password so loop start with focus away from username
+        mActivityRule.runOnUiThread(() -> mPassword.requestFocus());
+        callback.expectEvent(mUsername, EVENT_INPUT_HIDDEN);
+
+        // NOTE: we cannot run the whole loop inside the UI thread, because the autofill callback
+        // is called on it, which would cause a deadlock on expectEvent().
+        try {
             BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
             while (state.keepRunning()) {
-                mUsername.requestFocus();
-                mPassword.requestFocus();
+                mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
+                state.pauseTiming(); // Ignore time spent waiting for callbacks
+                callback.expectEvent(mUsername, EVENT_INPUT_SHOWN);
+                state.resumeTiming();
+                mActivityRule.runOnUiThread(() -> mPassword.requestFocus());
+                state.pauseTiming(); // Ignore time spent waiting for callbacks
+                callback.expectEvent(mUsername, EVENT_INPUT_HIDDEN);
+                state.resumeTiming();
             }
-        });
+
+            // Sanity check
+            callback.assertNoAsyncErrors();
+        } finally {
+            mAfm.unregisterCallback(callback);
+        }
     }
 
     /**
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java
new file mode 100644
index 0000000..9cdeb48
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.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.app;
+
+import static org.junit.Assert.fail;
+
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Benchmarks for {@link android.content.res.Resources}.
+ */
+@LargeTest
+public class ResourcesPerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private AssetManager mAsset;
+    private Resources mRes;
+
+    private int mTextId;
+    private int mColorId;
+    private int mIntegerId;
+    private int mLayoutId;
+
+    @Before
+    public void setUp() {
+        mAsset = new AssetManager();
+        mAsset.addAssetPath("/system/framework/framework-res.apk");
+        mRes = new Resources(mAsset, null, null);
+
+        mTextId = mRes.getIdentifier("cancel", "string", "android");
+        mColorId = mRes.getIdentifier("transparent", "color", "android");
+        mIntegerId = mRes.getIdentifier("config_shortAnimTime", "integer", "android");
+        mLayoutId = mRes.getIdentifier("two_line_list_item", "layout", "android");
+    }
+
+    @After
+    public void tearDown() {
+        mAsset.close();
+    }
+
+    @Test
+    public void getText() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mRes.getText(mTextId);
+        }
+    }
+
+    @Test
+    public void getColor() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mRes.getColor(mColorId, null);
+        }
+    }
+
+    @Test
+    public void getInteger() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mRes.getInteger(mIntegerId);
+        }
+    }
+
+    @Test
+    public void getLayoutAndTravese() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            try (XmlResourceParser parser = mRes.getLayout(mLayoutId)) {
+                while (parser.next() != XmlPullParser.END_DOCUMENT) {
+                    // Walk the entire tree
+                }
+            } catch (IOException | XmlPullParserException exception) {
+                fail("Parsing of the layout failed. Something is really broken");
+            }
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
index dfbabeb..a283e06 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
@@ -47,8 +47,7 @@
     public void testCreateRenderNodeNoName() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            RenderNode node = RenderNode.create(null, null);
-            node.destroy();
+            RenderNode.create(null, null);
         }
     }
 
@@ -56,8 +55,7 @@
     public void testCreateRenderNode() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            RenderNode node = RenderNode.create("LinearLayout", null);
-            node.destroy();
+            RenderNode.create("LinearLayout", null);
         }
     }
 
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index e4a8503..66a2600d 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -22,6 +22,9 @@
 import android.support.test.runner.AndroidJUnit4;
 
 import com.android.internal.os.BinderCallsStats;
+import com.android.internal.os.BinderInternal.CallSession;
+
+import java.util.Random;
 
 import org.junit.After;
 import org.junit.Before;
@@ -45,7 +48,7 @@
 
     @Before
     public void setUp() {
-        mBinderCallsStats = new BinderCallsStats(true);
+        mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
     }
 
     @After
@@ -54,25 +57,31 @@
 
     @Test
     public void timeCallSession() {
-        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        Binder b = new Binder();
-        int i = 0;
-        while (state.keepRunning()) {
-            BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, i % 100);
-            mBinderCallsStats.callEnded(s, 0, 0);
-            i++;
-        }
+        mBinderCallsStats.setDetailedTracking(true);
+        runScenario();
+    }
+
+    @Test
+    public void timeCallSessionOnePercentSampling() {
+        mBinderCallsStats.setDetailedTracking(false);
+        mBinderCallsStats.setSamplingInterval(100);
+        runScenario();
     }
 
     @Test
     public void timeCallSessionTrackingDisabled() {
-        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        Binder b = new Binder();
-        mBinderCallsStats = new BinderCallsStats(false);
-        while (state.keepRunning()) {
-            BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, 0);
-            mBinderCallsStats.callEnded(s, 0, 0);
-        }
+        mBinderCallsStats.setDetailedTracking(false);
+        runScenario();
     }
 
+    private void runScenario() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Binder b = new Binder();
+        while (state.keepRunning()) {
+            for (int i = 0; i < 1000; i++) {
+                CallSession s = mBinderCallsStats.callStarted(b, i % 100);
+                mBinderCallsStats.callEnded(s, 0, 0);
+            }
+        }
+    }
 }
diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
new file mode 100644
index 0000000..8e5cfaa
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.ShellHelper;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class TracePerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @BeforeClass
+    public static void startTracing() {
+        ShellHelper.runShellCommandRaw("atrace -c --async_start -a *");
+    }
+
+    @AfterClass
+    public static void endTracing() {
+        ShellHelper.runShellCommandRaw("atrace --async_stop");
+    }
+
+    @Before
+    public void verifyTracingEnabled() {
+        Assert.assertTrue(Trace.isEnabled());
+    }
+
+    @Test
+    public void testEnabled() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Trace.isEnabled();
+        }
+    }
+
+    @Test
+    public void testBeginEndSection() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Trace.beginSection("testBeginEndSection");
+            Trace.endSection();
+        }
+    }
+
+    @Test
+    public void testAsyncBeginEnd() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Trace.beginAsyncSection("testAsyncBeginEnd", 42);
+            Trace.endAsyncSection("testAsyncBeginEnd", 42);
+        }
+    }
+
+    @Test
+    public void testCounter() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Trace.setCounter("testCounter", 123);
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
index 586c385..64f2800 100644
--- a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
@@ -46,7 +46,7 @@
     private static final float SPACING_ADD = 10f;
     private static final float SPACING_MULT = 1.5f;
 
-    @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
+    @Parameterized.Parameters(name = "cached {3} {1}chars {0}")
     public static Collection cases() {
         final List<Object[]> params = new ArrayList<>();
         for (int length : new int[]{128}) {
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
index 9d11f29..194a88c 100644
--- a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
@@ -40,7 +40,7 @@
 
     private static final boolean[] BOOLEANS = new boolean[]{false, true};
 
-    @Parameterized.Parameters(name = "cached={4},{1}chars,{0}")
+    @Parameterized.Parameters(name = "cached {4} {1}chars {0}")
     public static Collection cases() {
         final List<Object[]> params = new ArrayList<>();
         for (int length : new int[]{128}) {
diff --git a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
index 6768798..ad5a34e 100644
--- a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
@@ -42,7 +42,7 @@
 
     private static final boolean[] BOOLEANS = new boolean[]{false, true};
 
-    @Parameterized.Parameters(name = "cached={1},{0}chars")
+    @Parameterized.Parameters(name = "cached {1} {0}chars")
     public static Collection cases() {
         final List<Object[]> params = new ArrayList<>();
         for (int length : new int[]{128}) {
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
index bfdb758..deb2b0a 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
@@ -50,7 +50,7 @@
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
+    @Parameterized.Parameters(name = "cached {3} {1}chars {0}")
     public static Collection cases() {
         final List<Object[]> params = new ArrayList<>();
         for (int length : new int[]{128}) {
diff --git a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
index ff2d57e..4bbe404 100644
--- a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
@@ -51,7 +51,7 @@
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
+    @Parameterized.Parameters(name = "cached {3} {1}chars {0}")
     public static Collection cases() {
         final List<Object[]> params = new ArrayList<>();
         for (int length : new int[]{128}) {
@@ -92,6 +92,8 @@
         Canvas.freeTextLayoutCaches();
         final CharSequence text = createRandomText(mLength);
         final TextView textView = new TextView(InstrumentationRegistry.getTargetContext());
+        textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE);
+
         textView.setText(text);
         state.resumeTiming();
 
@@ -119,6 +121,7 @@
         final RenderNode node = RenderNode.create("benchmark", null);
         final CharSequence text = createRandomText(mLength);
         final TextView textView = new TextView(InstrumentationRegistry.getTargetContext());
+        textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE);
         textView.setText(text);
         state.resumeTiming();
 
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 cae87fb..895547d 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
@@ -37,6 +37,14 @@
     @NonNull
     public static String runShellCommand(@NonNull String template, Object...args) {
         String command = String.format(template, args);
+        return runShellCommandRaw(command);
+    }
+
+    /**
+     * Runs a Shell command, returning a trimmed response.
+     */
+    @NonNull
+    public static String runShellCommandRaw(@NonNull String command) {
         UiAutomation automan = InstrumentationRegistry.getInstrumentation()
                 .getUiAutomation();
         ParcelFileDescriptor pfd = automan.executeShellCommand(command);
diff --git a/api/current.txt b/api/current.txt
index 1fe5bc7..897a8d9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -272,6 +272,7 @@
     field public static final int allowBackup = 16843392; // 0x1010280
     field public static final int allowClearUserData = 16842757; // 0x1010005
     field public static final int allowEmbedded = 16843765; // 0x10103f5
+    field public static final int allowForceDark = 16844171; // 0x101058b
     field public static final int allowParallelSyncs = 16843570; // 0x1010332
     field public static final int allowSingleTap = 16843353; // 0x1010259
     field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -1294,6 +1295,7 @@
     field public static final int summaryColumn = 16843426; // 0x10102a2
     field public static final int summaryOff = 16843248; // 0x10101f0
     field public static final int summaryOn = 16843247; // 0x10101ef
+    field public static final int supportsAmbientMode = 16844172; // 0x101058c
     field public static final int supportsAssist = 16844016; // 0x10104f0
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
     field public static final int supportsLocalInteraction = 16844047; // 0x101050f
@@ -1321,7 +1323,7 @@
     field public static final int targetName = 16843853; // 0x101044d
     field public static final int targetPackage = 16842785; // 0x1010021
     field public static final int targetProcesses = 16844097; // 0x1010541
-    field public static final int targetSandboxVersion = 16844108; // 0x101054c
+    field public static final deprecated int targetSandboxVersion = 16844108; // 0x101054c
     field public static final int targetSdkVersion = 16843376; // 0x1010270
     field public static final int taskAffinity = 16842770; // 0x1010012
     field public static final int taskCloseEnterAnimation = 16842942; // 0x10100be
@@ -2818,6 +2820,7 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
     field public static final int SHOW_MODE_AUTO = 0; // 0x0
     field public static final int SHOW_MODE_HIDDEN = 1; // 0x1
+    field public static final int SHOW_MODE_WITH_HARD_KEYBOARD = 2; // 0x2
   }
 
   public static abstract class AccessibilityService.GestureResultCallback {
@@ -3951,22 +3954,16 @@
     field public int uid;
   }
 
-  public static class ActivityManager.RecentTaskInfo implements android.os.Parcelable {
+  public static class ActivityManager.RecentTaskInfo extends android.app.TaskInfo implements android.os.Parcelable {
     ctor public ActivityManager.RecentTaskInfo();
     method public int describeContents();
     method public void readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.ActivityManager.RecentTaskInfo> CREATOR;
-    field public int affiliatedTaskId;
-    field public android.content.ComponentName baseActivity;
-    field public android.content.Intent baseIntent;
-    field public java.lang.CharSequence description;
-    field public int id;
-    field public int numActivities;
-    field public android.content.ComponentName origActivity;
-    field public int persistentId;
-    field public android.app.ActivityManager.TaskDescription taskDescription;
-    field public android.content.ComponentName topActivity;
+    field public deprecated int affiliatedTaskId;
+    field public deprecated java.lang.CharSequence description;
+    field public deprecated int id;
+    field public deprecated int persistentId;
   }
 
   public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable {
@@ -4030,19 +4027,16 @@
     field public int uid;
   }
 
-  public static class ActivityManager.RunningTaskInfo implements android.os.Parcelable {
+  public static class ActivityManager.RunningTaskInfo extends android.app.TaskInfo implements android.os.Parcelable {
     ctor public ActivityManager.RunningTaskInfo();
     method public int describeContents();
     method public void readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.ActivityManager.RunningTaskInfo> CREATOR;
-    field public android.content.ComponentName baseActivity;
-    field public java.lang.CharSequence description;
-    field public int id;
-    field public int numActivities;
-    field public int numRunning;
-    field public android.graphics.Bitmap thumbnail;
-    field public android.content.ComponentName topActivity;
+    field public deprecated java.lang.CharSequence description;
+    field public deprecated int id;
+    field public deprecated int numRunning;
+    field public deprecated android.graphics.Bitmap thumbnail;
   }
 
   public static class ActivityManager.TaskDescription implements android.os.Parcelable {
@@ -4211,8 +4205,8 @@
   }
 
   public class AppOpsManager {
-    method public int checkOp(java.lang.String, int, java.lang.String);
-    method public int checkOpNoThrow(java.lang.String, int, java.lang.String);
+    method public deprecated int checkOp(java.lang.String, int, java.lang.String);
+    method public deprecated int checkOpNoThrow(java.lang.String, int, java.lang.String);
     method public void checkPackage(int, java.lang.String);
     method public void finishOp(java.lang.String, int, java.lang.String);
     method public int noteOp(java.lang.String, int, java.lang.String);
@@ -4223,10 +4217,15 @@
     method public int startOp(java.lang.String, int, java.lang.String);
     method public int startOpNoThrow(java.lang.String, int, java.lang.String);
     method public void startWatchingMode(java.lang.String, java.lang.String, android.app.AppOpsManager.OnOpChangedListener);
+    method public void startWatchingMode(java.lang.String, java.lang.String, int, android.app.AppOpsManager.OnOpChangedListener);
     method public void stopWatchingMode(android.app.AppOpsManager.OnOpChangedListener);
+    method public int unsafeCheckOp(java.lang.String, int, java.lang.String);
+    method public int unsafeCheckOpNoThrow(java.lang.String, int, java.lang.String);
+    method public int unsafeCheckOpRaw(java.lang.String, int, java.lang.String);
     field public static final int MODE_ALLOWED = 0; // 0x0
     field public static final int MODE_DEFAULT = 3; // 0x3
     field public static final int MODE_ERRORED = 2; // 0x2
+    field public static final int MODE_FOREGROUND = 4; // 0x4
     field public static final int MODE_IGNORED = 1; // 0x1
     field public static final java.lang.String OPSTR_ADD_VOICEMAIL = "android:add_voicemail";
     field public static final java.lang.String OPSTR_ANSWER_PHONE_CALLS = "android:answer_phone_calls";
@@ -4262,6 +4261,7 @@
     field public static final java.lang.String OPSTR_WRITE_CONTACTS = "android:write_contacts";
     field public static final java.lang.String OPSTR_WRITE_EXTERNAL_STORAGE = "android:write_external_storage";
     field public static final java.lang.String OPSTR_WRITE_SETTINGS = "android:write_settings";
+    field public static final int WATCH_FOREGROUND_CHANGES = 1; // 0x1
   }
 
   public static abstract interface AppOpsManager.OnOpChangedListener {
@@ -6085,6 +6085,17 @@
     method public void setDefaultTab(int);
   }
 
+  public class TaskInfo {
+    field public android.content.ComponentName baseActivity;
+    field public android.content.Intent baseIntent;
+    field public boolean isRunning;
+    field public int numActivities;
+    field public android.content.ComponentName origActivity;
+    field public android.app.ActivityManager.TaskDescription taskDescription;
+    field public int taskId;
+    field public android.content.ComponentName topActivity;
+  }
+
   public class TaskStackBuilder {
     method public android.app.TaskStackBuilder addNextIntent(android.content.Intent);
     method public android.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent);
@@ -6114,8 +6125,10 @@
   }
 
   public final class UiAutomation {
+    method public void adoptShellPermissionIdentity();
     method public void clearWindowAnimationFrameStats();
     method public boolean clearWindowContentFrameStats(int);
+    method public void dropShellPermissionIdentity();
     method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
     method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
@@ -6269,6 +6282,7 @@
     method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
     method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
     method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
+    method public boolean supportsAmbientMode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
   }
@@ -9129,6 +9143,7 @@
     method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
     method public final android.net.Uri canonicalize(android.net.Uri) throws android.os.RemoteException;
     method public void close();
+    method public static void closeQuietly(android.content.ContentProviderClient);
     method public int delete(android.net.Uri, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
     method public android.content.ContentProvider getLocalContentProvider();
     method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String) throws android.os.RemoteException;
@@ -10131,6 +10146,7 @@
     field public static final int FLAG_ACTIVITY_SINGLE_TOP = 536870912; // 0x20000000
     field public static final int FLAG_ACTIVITY_TASK_ON_HOME = 16384; // 0x4000
     field public static final int FLAG_DEBUG_LOG_RESOLUTION = 8; // 0x8
+    field public static final int FLAG_DIRECT_BOOT_AUTO = 256; // 0x100
     field public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 16; // 0x10
     field public static final int FLAG_FROM_BACKGROUND = 4; // 0x4
     field public static final int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 64; // 0x40
@@ -11382,6 +11398,7 @@
     field public static final int INSTALL_REASON_USER = 4; // 0x4
     field public static final int MATCH_ALL = 131072; // 0x20000
     field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
+    field public static final int MATCH_DIRECT_BOOT_AUTO = 268435456; // 0x10000000
     field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
     field public static final int MATCH_DIRECT_BOOT_UNAWARE = 262144; // 0x40000
     field public static final int MATCH_DISABLED_COMPONENTS = 512; // 0x200
@@ -12660,12 +12677,14 @@
     method public static void appendColumns(java.lang.StringBuilder, java.lang.String[]);
     method public void appendWhere(java.lang.CharSequence);
     method public void appendWhereEscapeString(java.lang.String);
+    method public void appendWhereStandalone(java.lang.CharSequence);
     method public java.lang.String buildQuery(java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     method public deprecated java.lang.String buildQuery(java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     method public static java.lang.String buildQueryString(boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     method public java.lang.String buildUnionQuery(java.lang.String[], java.lang.String, java.lang.String);
     method public java.lang.String buildUnionSubQuery(java.lang.String, java.lang.String[], java.util.Set<java.lang.String>, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     method public deprecated java.lang.String buildUnionSubQuery(java.lang.String, java.lang.String[], java.util.Set<java.lang.String>, int, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, java.lang.String);
+    method public int delete(android.database.sqlite.SQLiteDatabase, java.lang.String, java.lang.String[]);
     method public java.lang.String getTables();
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String);
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
@@ -12675,6 +12694,7 @@
     method public void setProjectionMap(java.util.Map<java.lang.String, java.lang.String>);
     method public void setStrict(boolean);
     method public void setTables(java.lang.String);
+    method public int update(android.database.sqlite.SQLiteDatabase, android.content.ContentValues, java.lang.String, java.lang.String[]);
   }
 
   public class SQLiteReadOnlyDatabaseException extends android.database.sqlite.SQLiteException {
@@ -15160,6 +15180,48 @@
 
 package android.graphics.fonts {
 
+  public class Font {
+    method public android.graphics.fonts.FontVariationAxis[] getAxes();
+    method public int getTtcIndex();
+    method public int getWeight();
+    method public boolean isItalic();
+    field public static final int FONT_WEIGHT_BLACK = 900; // 0x384
+    field public static final int FONT_WEIGHT_BOLD = 700; // 0x2bc
+    field public static final int FONT_WEIGHT_EXTRA_BOLD = 800; // 0x320
+    field public static final int FONT_WEIGHT_EXTRA_LIGHT = 200; // 0xc8
+    field public static final int FONT_WEIGHT_LIGHT = 300; // 0x12c
+    field public static final int FONT_WEIGHT_MEDIUM = 500; // 0x1f4
+    field public static final int FONT_WEIGHT_NORMAL = 400; // 0x190
+    field public static final int FONT_WEIGHT_SEMI_BOLD = 600; // 0x258
+    field public static final int FONT_WEIGHT_THIN = 100; // 0x64
+  }
+
+  public static class Font.Builder {
+    ctor public Font.Builder(java.nio.ByteBuffer);
+    ctor public Font.Builder(java.io.File) throws java.io.IOException;
+    ctor public Font.Builder(java.io.FileDescriptor) throws java.io.IOException;
+    ctor public Font.Builder(java.io.FileDescriptor, long, long) throws java.io.IOException;
+    ctor public Font.Builder(android.content.res.AssetManager, java.lang.String) throws java.io.IOException;
+    ctor public Font.Builder(android.content.res.Resources, int) throws java.io.IOException;
+    method public android.graphics.fonts.Font build();
+    method public android.graphics.fonts.Font.Builder setFontVariationSettings(java.lang.String);
+    method public android.graphics.fonts.Font.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]);
+    method public android.graphics.fonts.Font.Builder setItalic(boolean);
+    method public android.graphics.fonts.Font.Builder setTtcIndex(int);
+    method public android.graphics.fonts.Font.Builder setWeight(int);
+  }
+
+  public class FontFamily {
+    method public android.graphics.fonts.Font getFont(int);
+    method public int getFontCount();
+  }
+
+  public static class FontFamily.Builder {
+    ctor public FontFamily.Builder(android.graphics.fonts.Font);
+    method public android.graphics.fonts.FontFamily.Builder addFont(android.graphics.fonts.Font);
+    method public android.graphics.fonts.FontFamily build();
+  }
+
   public final class FontVariationAxis {
     ctor public FontVariationAxis(java.lang.String, float);
     method public static android.graphics.fonts.FontVariationAxis[] fromFontVariationSettings(java.lang.String);
@@ -17117,6 +17179,8 @@
     field public static final int GAMAL = 12; // 0xc
     field public static final int HAH = 13; // 0xd
     field public static final int HAMZA_ON_HEH_GOAL = 14; // 0xe
+    field public static final int HANIFI_ROHINGYA_KINNA_YA = 100; // 0x64
+    field public static final int HANIFI_ROHINGYA_PA = 101; // 0x65
     field public static final int HE = 15; // 0xf
     field public static final int HEH = 16; // 0x10
     field public static final int HEH_GOAL = 17; // 0x11
@@ -17368,6 +17432,8 @@
     field public static final int CHEROKEE_ID = 32; // 0x20
     field public static final android.icu.lang.UCharacter.UnicodeBlock CHEROKEE_SUPPLEMENT;
     field public static final int CHEROKEE_SUPPLEMENT_ID = 255; // 0xff
+    field public static final android.icu.lang.UCharacter.UnicodeBlock CHESS_SYMBOLS;
+    field public static final int CHESS_SYMBOLS_ID = 281; // 0x119
     field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_COMPATIBILITY;
     field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_COMPATIBILITY_FORMS;
     field public static final int CJK_COMPATIBILITY_FORMS_ID = 83; // 0x53
@@ -17444,6 +17510,8 @@
     field public static final int DEVANAGARI_ID = 15; // 0xf
     field public static final android.icu.lang.UCharacter.UnicodeBlock DINGBATS;
     field public static final int DINGBATS_ID = 56; // 0x38
+    field public static final android.icu.lang.UCharacter.UnicodeBlock DOGRA;
+    field public static final int DOGRA_ID = 282; // 0x11a
     field public static final android.icu.lang.UCharacter.UnicodeBlock DOMINO_TILES;
     field public static final int DOMINO_TILES_ID = 171; // 0xab
     field public static final android.icu.lang.UCharacter.UnicodeBlock DUPLOYAN;
@@ -17479,6 +17547,8 @@
     field public static final int GEOMETRIC_SHAPES_EXTENDED_ID = 227; // 0xe3
     field public static final int GEOMETRIC_SHAPES_ID = 54; // 0x36
     field public static final android.icu.lang.UCharacter.UnicodeBlock GEORGIAN;
+    field public static final android.icu.lang.UCharacter.UnicodeBlock GEORGIAN_EXTENDED;
+    field public static final int GEORGIAN_EXTENDED_ID = 283; // 0x11b
     field public static final int GEORGIAN_ID = 29; // 0x1d
     field public static final android.icu.lang.UCharacter.UnicodeBlock GEORGIAN_SUPPLEMENT;
     field public static final int GEORGIAN_SUPPLEMENT_ID = 135; // 0x87
@@ -17496,6 +17566,8 @@
     field public static final int GREEK_ID = 8; // 0x8
     field public static final android.icu.lang.UCharacter.UnicodeBlock GUJARATI;
     field public static final int GUJARATI_ID = 18; // 0x12
+    field public static final android.icu.lang.UCharacter.UnicodeBlock GUNJALA_GONDI;
+    field public static final int GUNJALA_GONDI_ID = 284; // 0x11c
     field public static final android.icu.lang.UCharacter.UnicodeBlock GURMUKHI;
     field public static final int GURMUKHI_ID = 17; // 0x11
     field public static final android.icu.lang.UCharacter.UnicodeBlock HALFWIDTH_AND_FULLWIDTH_FORMS;
@@ -17510,6 +17582,8 @@
     field public static final int HANGUL_JAMO_ID = 30; // 0x1e
     field public static final android.icu.lang.UCharacter.UnicodeBlock HANGUL_SYLLABLES;
     field public static final int HANGUL_SYLLABLES_ID = 74; // 0x4a
+    field public static final android.icu.lang.UCharacter.UnicodeBlock HANIFI_ROHINGYA;
+    field public static final int HANIFI_ROHINGYA_ID = 285; // 0x11d
     field public static final android.icu.lang.UCharacter.UnicodeBlock HANUNOO;
     field public static final int HANUNOO_ID = 99; // 0x63
     field public static final android.icu.lang.UCharacter.UnicodeBlock HATRAN;
@@ -17528,6 +17602,8 @@
     field public static final int IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION_ID = 267; // 0x10b
     field public static final android.icu.lang.UCharacter.UnicodeBlock IMPERIAL_ARAMAIC;
     field public static final int IMPERIAL_ARAMAIC_ID = 186; // 0xba
+    field public static final android.icu.lang.UCharacter.UnicodeBlock INDIC_SIYAQ_NUMBERS;
+    field public static final int INDIC_SIYAQ_NUMBERS_ID = 286; // 0x11e
     field public static final android.icu.lang.UCharacter.UnicodeBlock INSCRIPTIONAL_PAHLAVI;
     field public static final int INSCRIPTIONAL_PAHLAVI_ID = 190; // 0xbe
     field public static final android.icu.lang.UCharacter.UnicodeBlock INSCRIPTIONAL_PARTHIAN;
@@ -17606,6 +17682,8 @@
     field public static final int MAHAJANI_ID = 233; // 0xe9
     field public static final android.icu.lang.UCharacter.UnicodeBlock MAHJONG_TILES;
     field public static final int MAHJONG_TILES_ID = 170; // 0xaa
+    field public static final android.icu.lang.UCharacter.UnicodeBlock MAKASAR;
+    field public static final int MAKASAR_ID = 287; // 0x11f
     field public static final android.icu.lang.UCharacter.UnicodeBlock MALAYALAM;
     field public static final int MALAYALAM_ID = 23; // 0x17
     field public static final android.icu.lang.UCharacter.UnicodeBlock MANDAIC;
@@ -17620,6 +17698,10 @@
     field public static final int MATHEMATICAL_ALPHANUMERIC_SYMBOLS_ID = 93; // 0x5d
     field public static final android.icu.lang.UCharacter.UnicodeBlock MATHEMATICAL_OPERATORS;
     field public static final int MATHEMATICAL_OPERATORS_ID = 47; // 0x2f
+    field public static final android.icu.lang.UCharacter.UnicodeBlock MAYAN_NUMERALS;
+    field public static final int MAYAN_NUMERALS_ID = 288; // 0x120
+    field public static final android.icu.lang.UCharacter.UnicodeBlock MEDEFAIDRIN;
+    field public static final int MEDEFAIDRIN_ID = 289; // 0x121
     field public static final android.icu.lang.UCharacter.UnicodeBlock MEETEI_MAYEK;
     field public static final android.icu.lang.UCharacter.UnicodeBlock MEETEI_MAYEK_EXTENSIONS;
     field public static final int MEETEI_MAYEK_EXTENSIONS_ID = 213; // 0xd5
@@ -17689,6 +17771,8 @@
     field public static final int OLD_PERMIC_ID = 241; // 0xf1
     field public static final android.icu.lang.UCharacter.UnicodeBlock OLD_PERSIAN;
     field public static final int OLD_PERSIAN_ID = 140; // 0x8c
+    field public static final android.icu.lang.UCharacter.UnicodeBlock OLD_SOGDIAN;
+    field public static final int OLD_SOGDIAN_ID = 290; // 0x122
     field public static final android.icu.lang.UCharacter.UnicodeBlock OLD_SOUTH_ARABIAN;
     field public static final int OLD_SOUTH_ARABIAN_ID = 187; // 0xbb
     field public static final android.icu.lang.UCharacter.UnicodeBlock OLD_TURKIC;
@@ -17753,6 +17837,8 @@
     field public static final int SINHALA_ID = 24; // 0x18
     field public static final android.icu.lang.UCharacter.UnicodeBlock SMALL_FORM_VARIANTS;
     field public static final int SMALL_FORM_VARIANTS_ID = 84; // 0x54
+    field public static final android.icu.lang.UCharacter.UnicodeBlock SOGDIAN;
+    field public static final int SOGDIAN_ID = 291; // 0x123
     field public static final android.icu.lang.UCharacter.UnicodeBlock SORA_SOMPENG;
     field public static final int SORA_SOMPENG_ID = 218; // 0xda
     field public static final android.icu.lang.UCharacter.UnicodeBlock SOYOMBO;
@@ -17877,6 +17963,7 @@
     field public static final int OTHER = 0; // 0x0
     field public static final int REGIONAL_INDICATOR = 13; // 0xd
     field public static final int SINGLE_QUOTE = 15; // 0xf
+    field public static final int WSEGSPACE = 22; // 0x16
     field public static final int ZWJ = 21; // 0x15
   }
 
@@ -18008,6 +18095,7 @@
     field public static final int EMOJI_MODIFIER = 59; // 0x3b
     field public static final int EMOJI_MODIFIER_BASE = 60; // 0x3c
     field public static final int EMOJI_PRESENTATION = 58; // 0x3a
+    field public static final int EXTENDED_PICTOGRAPHIC = 64; // 0x40
     field public static final int EXTENDER = 8; // 0x8
     field public static final int FULL_COMPOSITION_EXCLUSION = 9; // 0x9
     field public static final int GENERAL_CATEGORY = 4101; // 0x1005
@@ -18138,6 +18226,7 @@
     field public static final int DEMOTIC_EGYPTIAN = 69; // 0x45
     field public static final int DESERET = 9; // 0x9
     field public static final int DEVANAGARI = 10; // 0xa
+    field public static final int DOGRA = 178; // 0xb2
     field public static final int DUPLOYAN = 135; // 0x87
     field public static final int EASTERN_SYRIAC = 97; // 0x61
     field public static final int EGYPTIAN_HIEROGLYPHS = 71; // 0x47
@@ -18150,9 +18239,11 @@
     field public static final int GRANTHA = 137; // 0x89
     field public static final int GREEK = 14; // 0xe
     field public static final int GUJARATI = 15; // 0xf
+    field public static final int GUNJALA_GONDI = 179; // 0xb3
     field public static final int GURMUKHI = 16; // 0x10
     field public static final int HAN = 17; // 0x11
     field public static final int HANGUL = 18; // 0x12
+    field public static final int HANIFI_ROHINGYA = 182; // 0xb6
     field public static final int HANUNOO = 43; // 0x2b
     field public static final int HAN_WITH_BOPOMOFO = 172; // 0xac
     field public static final int HARAPPAN_INDUS = 77; // 0x4d
@@ -18195,6 +18286,7 @@
     field public static final int LYCIAN = 107; // 0x6b
     field public static final int LYDIAN = 108; // 0x6c
     field public static final int MAHAJANI = 160; // 0xa0
+    field public static final int MAKASAR = 180; // 0xb4
     field public static final int MALAYALAM = 26; // 0x1a
     field public static final int MANDAEAN = 84; // 0x54
     field public static final int MANDAIC = 84; // 0x54
@@ -18203,6 +18295,7 @@
     field public static final int MASARAM_GONDI = 175; // 0xaf
     field public static final int MATHEMATICAL_NOTATION = 128; // 0x80
     field public static final int MAYAN_HIEROGLYPHS = 85; // 0x55
+    field public static final int MEDEFAIDRIN = 181; // 0xb5
     field public static final int MEITEI_MAYEK = 115; // 0x73
     field public static final int MENDE = 140; // 0x8c
     field public static final int MEROITIC = 86; // 0x56
@@ -18228,6 +18321,7 @@
     field public static final int OLD_NORTH_ARABIAN = 142; // 0x8e
     field public static final int OLD_PERMIC = 89; // 0x59
     field public static final int OLD_PERSIAN = 61; // 0x3d
+    field public static final int OLD_SOGDIAN = 184; // 0xb8
     field public static final int OLD_SOUTH_ARABIAN = 133; // 0x85
     field public static final int OL_CHIKI = 109; // 0x6d
     field public static final int ORIYA = 31; // 0x1f
@@ -18254,6 +18348,7 @@
     field public static final int SIMPLIFIED_HAN = 73; // 0x49
     field public static final int SINDHI = 145; // 0x91
     field public static final int SINHALA = 33; // 0x21
+    field public static final int SOGDIAN = 183; // 0xb7
     field public static final int SORA_SOMPENG = 152; // 0x98
     field public static final int SOYOMBO = 176; // 0xb0
     field public static final int SUNDANESE = 113; // 0x71
@@ -21195,6 +21290,7 @@
     field public static final android.icu.util.VersionInfo UCOL_BUILDER_VERSION;
     field public static final android.icu.util.VersionInfo UCOL_RUNTIME_VERSION;
     field public static final android.icu.util.VersionInfo UNICODE_10_0;
+    field public static final android.icu.util.VersionInfo UNICODE_11_0;
     field public static final android.icu.util.VersionInfo UNICODE_1_0;
     field public static final android.icu.util.VersionInfo UNICODE_1_0_1;
     field public static final android.icu.util.VersionInfo UNICODE_1_1_0;
@@ -27620,6 +27716,7 @@
     method public java.util.Date getValidNotAfterDate();
     method public deprecated java.lang.String getValidNotBefore();
     method public java.util.Date getValidNotBeforeDate();
+    method public java.security.cert.X509Certificate getX509Certificate();
     method public static android.net.http.SslCertificate restoreState(android.os.Bundle);
     method public static android.os.Bundle saveState(android.net.http.SslCertificate);
   }
@@ -32122,6 +32219,7 @@
 
   public class Binder implements android.os.IBinder {
     ctor public Binder();
+    ctor public Binder(java.lang.String);
     method public void attachInterface(android.os.IInterface, java.lang.String);
     method public static final long clearCallingIdentity();
     method public void dump(java.io.FileDescriptor, java.lang.String[]);
@@ -32537,6 +32635,21 @@
     ctor public FileUriExposedException(java.lang.String);
   }
 
+  public class FileUtils {
+    method public static void closeQuietly(java.lang.AutoCloseable);
+    method public static void closeQuietly(java.io.FileDescriptor);
+    method public static long copy(java.io.File, java.io.File) throws java.io.IOException;
+    method public static long copy(java.io.File, java.io.File, android.os.CancellationSignal, java.util.concurrent.Executor, android.os.FileUtils.ProgressListener) throws java.io.IOException;
+    method public static long copy(java.io.InputStream, java.io.OutputStream) throws java.io.IOException;
+    method public static long copy(java.io.InputStream, java.io.OutputStream, android.os.CancellationSignal, java.util.concurrent.Executor, android.os.FileUtils.ProgressListener) throws java.io.IOException;
+    method public static long copy(java.io.FileDescriptor, java.io.FileDescriptor) throws java.io.IOException;
+    method public static long copy(java.io.FileDescriptor, java.io.FileDescriptor, android.os.CancellationSignal, java.util.concurrent.Executor, android.os.FileUtils.ProgressListener) throws java.io.IOException;
+  }
+
+  public static abstract interface FileUtils.ProgressListener {
+    method public abstract void onProgress(long);
+  }
+
   public class Handler {
     ctor public Handler();
     ctor public Handler(android.os.Handler.Callback);
@@ -32803,6 +32916,7 @@
     method public void readMap(java.util.Map, java.lang.ClassLoader);
     method public <T extends android.os.Parcelable> T readParcelable(java.lang.ClassLoader);
     method public android.os.Parcelable[] readParcelableArray(java.lang.ClassLoader);
+    method public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(java.util.List<T>, java.lang.ClassLoader);
     method public android.os.PersistableBundle readPersistableBundle();
     method public android.os.PersistableBundle readPersistableBundle(java.lang.ClassLoader);
     method public java.io.Serializable readSerializable();
@@ -32849,6 +32963,7 @@
     method public void writeNoException();
     method public void writeParcelable(android.os.Parcelable, int);
     method public <T extends android.os.Parcelable> void writeParcelableArray(T[], int);
+    method public <T extends android.os.Parcelable> void writeParcelableList(java.util.List<T>, int);
     method public void writePersistableBundle(android.os.PersistableBundle);
     method public void writeSerializable(java.io.Serializable);
     method public void writeSize(android.util.Size);
@@ -33200,7 +33315,9 @@
     method public android.os.StrictMode.VmPolicy.Builder detectAll();
     method public android.os.StrictMode.VmPolicy.Builder detectCleartextNetwork();
     method public android.os.StrictMode.VmPolicy.Builder detectContentUriWithoutPermission();
+    method public android.os.StrictMode.VmPolicy.Builder detectCredentialProtectedWhileLocked();
     method public android.os.StrictMode.VmPolicy.Builder detectFileUriExposure();
+    method public android.os.StrictMode.VmPolicy.Builder detectImplicitDirectBoot();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
@@ -33248,8 +33365,12 @@
   }
 
   public final class Trace {
+    method public static void beginAsyncSection(java.lang.String, int);
     method public static void beginSection(java.lang.String);
+    method public static void endAsyncSection(java.lang.String, int);
     method public static void endSection();
+    method public static boolean isEnabled();
+    method public static void setCounter(java.lang.String, int);
   }
 
   public class TransactionTooLargeException extends android.os.RemoteException {
@@ -33602,6 +33723,9 @@
   public final class ContentUriWithoutPermissionViolation extends android.os.strictmode.Violation {
   }
 
+  public final class CredentialProtectedWhileLockedViolation extends android.os.strictmode.Violation {
+  }
+
   public final class CustomViolation extends android.os.strictmode.Violation {
   }
 
@@ -33614,6 +33738,9 @@
   public final class FileUriExposedViolation extends android.os.strictmode.Violation {
   }
 
+  public final class ImplicitDirectBootViolation extends android.os.strictmode.Violation {
+  }
+
   public class InstanceCountViolation extends android.os.strictmode.Violation {
     method public long getNumberOfInstances();
   }
@@ -35495,11 +35622,11 @@
 
   protected static abstract interface ContactsContract.ContactOptionsColumns {
     field public static final java.lang.String CUSTOM_RINGTONE = "custom_ringtone";
-    field public static final java.lang.String LAST_TIME_CONTACTED = "last_time_contacted";
+    field public static final deprecated java.lang.String LAST_TIME_CONTACTED = "last_time_contacted";
     field public static final java.lang.String PINNED = "pinned";
     field public static final java.lang.String SEND_TO_VOICEMAIL = "send_to_voicemail";
     field public static final java.lang.String STARRED = "starred";
-    field public static final java.lang.String TIMES_CONTACTED = "times_contacted";
+    field public static final deprecated java.lang.String TIMES_CONTACTED = "times_contacted";
   }
 
   protected static abstract interface ContactsContract.ContactStatusColumns {
@@ -35521,7 +35648,7 @@
     method public static java.io.InputStream openContactPhotoInputStream(android.content.ContentResolver, android.net.Uri, boolean);
     method public static java.io.InputStream openContactPhotoInputStream(android.content.ContentResolver, android.net.Uri);
     field public static final android.net.Uri CONTENT_FILTER_URI;
-    field public static final android.net.Uri CONTENT_FREQUENT_URI;
+    field public static final deprecated android.net.Uri CONTENT_FREQUENT_URI;
     field public static final android.net.Uri CONTENT_GROUP_URI;
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact";
     field public static final android.net.Uri CONTENT_LOOKUP_URI;
@@ -35629,7 +35756,7 @@
   protected static abstract interface ContactsContract.DataColumnsWithJoins implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactStatusColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.DataUsageStatColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.StatusColumns {
   }
 
-  public static final class ContactsContract.DataUsageFeedback {
+  public static final deprecated class ContactsContract.DataUsageFeedback {
     ctor public ContactsContract.DataUsageFeedback();
     field public static final android.net.Uri DELETE_USAGE_URI;
     field public static final android.net.Uri FEEDBACK_URI;
@@ -35640,8 +35767,8 @@
   }
 
   protected static abstract interface ContactsContract.DataUsageStatColumns {
-    field public static final java.lang.String LAST_TIME_USED = "last_time_used";
-    field public static final java.lang.String TIMES_USED = "times_used";
+    field public static final deprecated java.lang.String LAST_TIME_USED = "last_time_used";
+    field public static final deprecated java.lang.String TIMES_USED = "times_used";
   }
 
   public static final class ContactsContract.DeletedContacts implements android.provider.ContactsContract.DeletedContactsColumns {
@@ -39025,6 +39152,7 @@
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
+    field public static final int FLAG_DELAY_SAVE = 4; // 0x4
     field public static final int FLAG_DONT_SAVE_ON_FINISH = 2; // 0x2
     field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1
     field public static final int NEGATIVE_BUTTON_STYLE_CANCEL = 0; // 0x0
@@ -39648,6 +39776,7 @@
     field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10
     field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8
     field public static final int SHOW_SOURCE_ASSIST_GESTURE = 4; // 0x4
+    field public static final int SHOW_SOURCE_PUSH_TO_TALK = 32; // 0x20
     field public static final int SHOW_WITH_ASSIST = 1; // 0x1
     field public static final int SHOW_WITH_SCREENSHOT = 2; // 0x2
   }
@@ -39738,9 +39867,11 @@
     method public int getDesiredMinimumHeight();
     method public int getDesiredMinimumWidth();
     method public android.view.SurfaceHolder getSurfaceHolder();
+    method public boolean isInAmbientMode();
     method public boolean isPreview();
     method public boolean isVisible();
     method public void notifyColorsChanged();
+    method public void onAmbientModeChanged(boolean, boolean);
     method public void onApplyWindowInsets(android.view.WindowInsets);
     method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean);
     method public android.app.WallpaperColors onComputeColors();
@@ -40884,6 +41015,16 @@
     field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
   }
 
+  public abstract class CallRedirectionService extends android.app.Service {
+    ctor public CallRedirectionService();
+    method public final void cancelCall();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+    method public final void placeCallUnmodified();
+    method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallRedirectionService";
+  }
+
   public abstract class CallScreeningService extends android.app.Service {
     ctor public CallScreeningService();
     method public android.os.IBinder onBind(android.content.Intent);
@@ -41000,6 +41141,7 @@
     method public void onReject(java.lang.String);
     method public void onSeparate();
     method public void onShowIncomingCallUi();
+    method public void onSilence();
     method public void onStartRtt(android.telecom.Connection.RttTextStream);
     method public void onStateChanged(int);
     method public void onStopDtmfTone();
@@ -41793,6 +41935,7 @@
     field public static final java.lang.String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
     field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
     field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
+    field public static final java.lang.String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
     field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
     field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
     field public static final java.lang.String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool";
@@ -42392,8 +42535,10 @@
     method public java.lang.CharSequence getDisplayName();
     method public java.lang.String getIccId();
     method public int getIconTint();
-    method public int getMcc();
-    method public int getMnc();
+    method public deprecated int getMcc();
+    method public java.lang.String getMccString();
+    method public deprecated int getMnc();
+    method public java.lang.String getMncString();
     method public java.lang.String getNumber();
     method public int getSimSlotIndex();
     method public int getSubscriptionId();
@@ -42487,6 +42632,8 @@
     method public java.lang.String getImei();
     method public java.lang.String getImei(int);
     method public java.lang.String getLine1Number();
+    method public java.lang.String getManufacturerCode();
+    method public java.lang.String getManufacturerCode(int);
     method public java.lang.String getMeid();
     method public java.lang.String getMeid(int);
     method public java.lang.String getMmsUAProfUrl();
@@ -42511,6 +42658,8 @@
     method public int getSimState();
     method public int getSimState(int);
     method public java.lang.String getSubscriberId();
+    method public java.lang.String getTypeAllocationCode();
+    method public java.lang.String getTypeAllocationCode(int);
     method public java.lang.String getVisualVoicemailPackageName();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
@@ -42723,7 +42872,8 @@
     method public int getAuthType();
     method public java.lang.String getEntryName();
     method public int getId();
-    method public java.net.InetAddress getMmsProxyAddress();
+    method public deprecated java.net.InetAddress getMmsProxyAddress();
+    method public java.lang.String getMmsProxyAddressAsString();
     method public int getMmsProxyPort();
     method public android.net.Uri getMmsc();
     method public int getMvnoType();
@@ -42731,7 +42881,8 @@
     method public java.lang.String getOperatorNumeric();
     method public java.lang.String getPassword();
     method public int getProtocol();
-    method public java.net.InetAddress getProxyAddress();
+    method public deprecated java.net.InetAddress getProxyAddress();
+    method public java.lang.String getProxyAddressAsString();
     method public int getProxyPort();
     method public int getRoamingProtocol();
     method public java.lang.String getUser();
@@ -42751,7 +42902,7 @@
     field public static final int PROTOCOL_IPV6 = 1; // 0x1
     field public static final int PROTOCOL_PPP = 3; // 0x3
     field public static final int TYPE_CBS = 128; // 0x80
-    field public static final int TYPE_DEFAULT = 17; // 0x11
+    field public static final int TYPE_DEFAULT = 1; // 0x1
     field public static final int TYPE_DUN = 8; // 0x8
     field public static final int TYPE_EMERGENCY = 512; // 0x200
     field public static final int TYPE_FOTA = 32; // 0x20
@@ -42770,7 +42921,8 @@
     method public android.telephony.data.ApnSetting.Builder setAuthType(int);
     method public android.telephony.data.ApnSetting.Builder setCarrierEnabled(boolean);
     method public android.telephony.data.ApnSetting.Builder setEntryName(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setMmsProxyAddress(java.net.InetAddress);
+    method public deprecated android.telephony.data.ApnSetting.Builder setMmsProxyAddress(java.net.InetAddress);
+    method public android.telephony.data.ApnSetting.Builder setMmsProxyAddress(java.lang.String);
     method public android.telephony.data.ApnSetting.Builder setMmsProxyPort(int);
     method public android.telephony.data.ApnSetting.Builder setMmsc(android.net.Uri);
     method public android.telephony.data.ApnSetting.Builder setMvnoType(int);
@@ -42778,7 +42930,8 @@
     method public android.telephony.data.ApnSetting.Builder setOperatorNumeric(java.lang.String);
     method public android.telephony.data.ApnSetting.Builder setPassword(java.lang.String);
     method public android.telephony.data.ApnSetting.Builder setProtocol(int);
-    method public android.telephony.data.ApnSetting.Builder setProxyAddress(java.net.InetAddress);
+    method public deprecated android.telephony.data.ApnSetting.Builder setProxyAddress(java.net.InetAddress);
+    method public android.telephony.data.ApnSetting.Builder setProxyAddress(java.lang.String);
     method public android.telephony.data.ApnSetting.Builder setProxyPort(int);
     method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(int);
     method public android.telephony.data.ApnSetting.Builder setUser(java.lang.String);
@@ -44719,6 +44872,7 @@
   public class Linkify {
     ctor public Linkify();
     method public static final boolean addLinks(android.text.Spannable, int);
+    method public static final boolean addLinks(android.text.Spannable, int, android.text.util.Linkify.UrlSpanFactory);
     method public static final boolean addLinks(android.widget.TextView, int);
     method public static final void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String);
     method public static final void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
@@ -44726,6 +44880,7 @@
     method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String);
     method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
     method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
+    method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter, android.text.util.Linkify.UrlSpanFactory);
     field public static final int ALL = 15; // 0xf
     field public static final int EMAIL_ADDRESSES = 2; // 0x2
     field public static final deprecated int MAP_ADDRESSES = 8; // 0x8
@@ -44744,6 +44899,11 @@
     method public abstract java.lang.String transformUrl(java.util.regex.Matcher, java.lang.String);
   }
 
+  public static class Linkify.UrlSpanFactory {
+    ctor public Linkify.UrlSpanFactory();
+    method public android.text.style.URLSpan create(java.lang.String);
+  }
+
   public class Rfc822Token {
     ctor public Rfc822Token(java.lang.String, java.lang.String, java.lang.String);
     method public java.lang.String getAddress();
@@ -45942,8 +46102,8 @@
 
   public final class Display {
     method public long getAppVsyncOffsetNanos();
-    method public android.view.DisplayCutout getCutout();
     method public void getCurrentSizeRange(android.graphics.Point, android.graphics.Point);
+    method public android.view.DisplayCutout getCutout();
     method public int getDisplayId();
     method public int getFlags();
     method public android.view.Display.HdrCapabilities getHdrCapabilities();
@@ -48713,6 +48873,7 @@
     method public void dispatchOnGlobalLayout();
     method public boolean dispatchOnPreDraw();
     method public boolean isAlive();
+    method public void registerFrameCommitCallback(java.lang.Runnable);
     method public deprecated void removeGlobalOnLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
     method public void removeOnDrawListener(android.view.ViewTreeObserver.OnDrawListener);
     method public void removeOnGlobalFocusChangeListener(android.view.ViewTreeObserver.OnGlobalFocusChangeListener);
@@ -48722,6 +48883,7 @@
     method public void removeOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
     method public void removeOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener);
     method public void removeOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener);
+    method public boolean unregisterFrameCommitCallback(java.lang.Runnable);
   }
 
   public static abstract interface ViewTreeObserver.OnDrawListener {
@@ -49365,18 +49527,18 @@
     method public void setCheckable(boolean);
     method public void setChecked(boolean);
     method public void setClassName(java.lang.CharSequence);
-    method public void setClickable(boolean);
+    method public deprecated void setClickable(boolean);
     method public void setCollectionInfo(android.view.accessibility.AccessibilityNodeInfo.CollectionInfo);
     method public void setCollectionItemInfo(android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo);
     method public void setContentDescription(java.lang.CharSequence);
     method public void setContentInvalid(boolean);
-    method public void setContextClickable(boolean);
-    method public void setDismissable(boolean);
+    method public deprecated void setContextClickable(boolean);
+    method public deprecated void setDismissable(boolean);
     method public void setDrawingOrder(int);
     method public void setEditable(boolean);
     method public void setEnabled(boolean);
     method public void setError(java.lang.CharSequence);
-    method public void setFocusable(boolean);
+    method public deprecated void setFocusable(boolean);
     method public void setFocused(boolean);
     method public void setHeading(boolean);
     method public void setHintText(java.lang.CharSequence);
@@ -49387,7 +49549,7 @@
     method public void setLabeledBy(android.view.View);
     method public void setLabeledBy(android.view.View, int);
     method public void setLiveRegion(int);
-    method public void setLongClickable(boolean);
+    method public deprecated void setLongClickable(boolean);
     method public void setMaxTextLength(int);
     method public void setMovementGranularities(int);
     method public void setMultiLine(boolean);
@@ -49398,7 +49560,7 @@
     method public void setPassword(boolean);
     method public void setRangeInfo(android.view.accessibility.AccessibilityNodeInfo.RangeInfo);
     method public void setScreenReaderFocusable(boolean);
-    method public void setScrollable(boolean);
+    method public deprecated void setScrollable(boolean);
     method public void setSelected(boolean);
     method public void setShowingHintText(boolean);
     method public void setSource(android.view.View);
@@ -57576,14 +57738,14 @@
     method public void setContextClassLoader(java.lang.ClassLoader);
     method public final void setDaemon(boolean);
     method public static void setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler);
-    method public final void setName(java.lang.String);
+    method public final synchronized void setName(java.lang.String);
     method public final void setPriority(int);
     method public void setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler);
     method public static void sleep(long) throws java.lang.InterruptedException;
     method public static void sleep(long, int) throws java.lang.InterruptedException;
     method public synchronized void start();
     method public final deprecated void stop();
-    method public final deprecated void stop(java.lang.Throwable);
+    method public final deprecated synchronized void stop(java.lang.Throwable);
     method public final deprecated void suspend();
     method public static void yield();
     field public static final int MAX_PRIORITY = 10; // 0xa
@@ -70156,7 +70318,7 @@
   public final class Pattern implements java.io.Serializable {
     method public java.util.function.Predicate<java.lang.String> asPredicate();
     method public static java.util.regex.Pattern compile(java.lang.String);
-    method public static java.util.regex.Pattern compile(java.lang.String, int) throws java.util.regex.PatternSyntaxException;
+    method public static java.util.regex.Pattern compile(java.lang.String, int);
     method public int flags();
     method public java.util.regex.Matcher matcher(java.lang.CharSequence);
     method public static boolean matches(java.lang.String, java.lang.CharSequence);
@@ -70617,7 +70779,7 @@
     ctor public InflaterInputStream(java.io.InputStream);
     method protected void fill() throws java.io.IOException;
     field protected byte[] buf;
-    field protected boolean closed;
+    field protected deprecated boolean closed;
     field protected java.util.zip.Inflater inf;
     field protected int len;
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 1fcfcf6..6a00815 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -269,6 +269,7 @@
 
   public class AppOpsManager {
     method public static java.lang.String[] getOpStrs();
+    method public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, java.lang.String, int[]);
     method public void setMode(java.lang.String, int, java.lang.String, int);
     method public void setUidMode(java.lang.String, int, int);
     field public static final java.lang.String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
@@ -316,6 +317,33 @@
     field public static final java.lang.String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
   }
 
+  public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getDuration();
+    method public long getLastAccessBackgroundTime();
+    method public long getLastAccessForegroundTime();
+    method public long getLastAccessTime();
+    method public long getLastRejectBackgroundTime();
+    method public long getLastRejectForegroundTime();
+    method public long getLastRejectTime();
+    method public int getMode();
+    method public java.lang.String getOpStr();
+    method public java.lang.String getProxyPackageName();
+    method public int getProxyUid();
+    method public boolean isRunning();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEntry> CREATOR;
+  }
+
+  public static final class AppOpsManager.PackageOps implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.List<android.app.AppOpsManager.OpEntry> getOps();
+    method public java.lang.String getPackageName();
+    method public int getUid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR;
+  }
+
   public class BroadcastOptions {
     method public static android.app.BroadcastOptions makeBasic();
     method public void setDontSendToRestrictedApps(boolean);
@@ -1174,6 +1202,9 @@
   }
 
   public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+    field public int backgroundRequestDetailResourceId;
+    field public int backgroundRequestResourceId;
+    field public int requestDetailResourceId;
     field public int requestRes;
   }
 
@@ -1181,6 +1212,7 @@
     field public static final int FLAG_REMOVED = 2; // 0x2
     field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
+    field public java.lang.String backgroundPermission;
     field public int requestRes;
   }
 
@@ -3895,6 +3927,7 @@
     method public void readFromParcel(android.os.Parcel);
     method public int sectionCount();
     method public void setAll(boolean);
+    method public void setPrivacyPolicy(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
   }
@@ -4338,7 +4371,10 @@
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
     field public static final java.lang.String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
+    field public static final java.lang.String HUSH_GESTURE_USED = "hush_gesture_used";
     field public static final java.lang.String INSTANT_APPS_ENABLED = "instant_apps_enabled";
+    field public static final java.lang.String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count";
+    field public static final java.lang.String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
   }
 
   public final class TimeZoneRulesDataContract {
@@ -4491,9 +4527,11 @@
   public abstract class AutofillFieldClassificationService extends android.app.Service {
     method public android.os.IBinder onBind(android.content.Intent);
     method public float[][] onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>);
+    field public static final java.lang.String RESOURCE_AVAILABLE_ALGORITHMS = "autofill_field_classification_available_algorithms";
+    field public static final java.lang.String RESOURCE_DEFAULT_ALGORITHM = "autofill_field_classification_default_algorithm";
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService";
-    field public static final java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms";
-    field public static final java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
+    field public static final deprecated java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms";
+    field public static final deprecated java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
   }
 
 }
@@ -4631,6 +4669,8 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
     field public static final java.lang.String KEY_PEOPLE = "key_people";
+    field public static final java.lang.String KEY_SMART_ACTIONS = "key_smart_actions";
+    field public static final java.lang.String KEY_SMART_REPLIES = "key_smart_replies";
     field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
   }
@@ -5146,8 +5186,8 @@
     method public int[] getAvailableServices();
     method public android.telephony.CellIdentity getCellIdentity();
     method public int getDomain();
-    method public int getReasonForDenial();
     method public int getRegState();
+    method public int getRejectCause();
     method public int getTransportType();
     method public boolean isEmergencyEnabled();
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 961026b..b88c760 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -81,7 +81,7 @@
 package android.os {
 
   public class Build {
-    field public static final boolean PERMISSIONS_REVIEW_REQUIRED;
+    field public static final boolean PERMISSIONS_REVIEW_REQUIRED = true;
   }
 
   public final class PowerManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index 9be252f..63ece40 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -30,25 +30,12 @@
     method public long getTotalRam();
     method public int getUidImportance(int);
     method public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
-    method public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
-    method public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
-    method public void resizeStack(int, android.graphics.Rect) throws java.lang.SecurityException;
-    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 static boolean supportsMultiWindow(android.content.Context);
-    method public static boolean supportsSplitScreenMultiWindow(android.content.Context);
-    field public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; // 0x1
-    field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
   }
 
   public static abstract interface ActivityManager.OnUidImportanceListener {
     method public abstract void onUidImportance(int, int);
   }
 
-  public static class ActivityManager.StackId {
-    field public static final int INVALID_STACK_ID = -1; // 0xffffffff
-  }
-
   public static class ActivityManager.TaskDescription implements android.os.Parcelable {
     method public java.lang.String getIconFilename();
     method public int getIconResource();
@@ -61,6 +48,19 @@
     method public void setTaskOverlay(boolean, boolean);
   }
 
+  public class ActivityTaskManager {
+    method public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
+    method public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
+    method public void resizeStack(int, android.graphics.Rect) throws java.lang.SecurityException;
+    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 static boolean supportsMultiWindow(android.content.Context);
+    method public static boolean supportsSplitScreenMultiWindow(android.content.Context);
+    field public static final int INVALID_STACK_ID = -1; // 0xffffffff
+    field public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; // 0x1
+    field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
+  }
+
   public class AppOpsManager {
     method public static int getNumOps();
     method public static java.lang.String[] getOpStrs();
@@ -130,6 +130,12 @@
     method public android.content.ComponentName getEffectsSuppressor();
   }
 
+  public final class PictureInPictureParams implements android.os.Parcelable {
+    method public java.util.List<android.app.RemoteAction> getActions();
+    method public float getAspectRatio();
+    method public android.graphics.Rect getSourceRectHint();
+  }
+
   public class TimePickerDialog extends android.app.AlertDialog implements android.content.DialogInterface.OnClickListener android.widget.TimePicker.OnTimeChangedListener {
     method public android.widget.TimePicker getTimePicker();
   }
@@ -222,6 +228,14 @@
 
 }
 
+package android.bluetooth {
+
+  public final class BluetoothClass implements android.os.Parcelable {
+    method public int getClassOfDevice();
+  }
+
+}
+
 package android.content {
 
   public abstract class ContentResolver {
@@ -251,6 +265,8 @@
   public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
     method public boolean isPrivilegedApp();
     method public boolean isSystemApp();
+    field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8
+    field public int privateFlags;
   }
 
   public class LauncherApps {
@@ -258,24 +274,29 @@
   }
 
   public abstract class PackageManager {
+    method public abstract boolean arePermissionsIndividuallyControlled();
     method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
     method public abstract int getInstallReason(java.lang.String, android.os.UserHandle);
-    method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
+    method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method public abstract java.lang.String[] getNamesForUids(int[]);
     method public abstract java.lang.String getPermissionControllerPackageName();
     method public abstract java.lang.String getServicesSystemSharedLibraryPackageName();
     method public abstract java.lang.String getSharedSystemSharedLibraryPackageName();
-    method public abstract boolean isPermissionReviewModeEnabled();
+    method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     field public static final java.lang.String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
     field public static final java.lang.String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
     field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
+    field public static final java.lang.String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
+    field public static final java.lang.String SYSTEM_SHARED_LIBRARY_SHARED = "android.ext.shared";
   }
 
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
+    field public java.lang.String backgroundPermission;
   }
 
   public final class ShortcutInfo implements android.os.Parcelable {
@@ -301,9 +322,6 @@
   public final class SQLiteDebug {
     method public static void dump(android.util.Printer, java.lang.String[]);
     method public static android.database.sqlite.SQLiteDebug.PagerStats getDatabaseInfo();
-    field public static final boolean DEBUG_SQL_LOG;
-    field public static final boolean DEBUG_SQL_STATEMENTS;
-    field public static final boolean DEBUG_SQL_TIME;
   }
 
   public static class SQLiteDebug.DbStats {
@@ -625,6 +643,10 @@
 
 package android.os {
 
+  public class Build {
+    method public static boolean is64BitAbi(java.lang.String);
+  }
+
   public static class Build.VERSION {
     field public static final int FIRST_SDK_INT;
     field public static final int RESOURCES_SDK_INT;
@@ -637,6 +659,7 @@
 
   public class Environment {
     method public static java.io.File buildPath(java.io.File, java.lang.String...);
+    method public static java.io.File getStorageDirectory();
   }
 
   public class IncidentManager {
@@ -654,12 +677,14 @@
     method public void readFromParcel(android.os.Parcel);
     method public int sectionCount();
     method public void setAll(boolean);
+    method public void setPrivacyPolicy(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
   }
 
-  public final class PowerManager {
-    method public void nap(long);
+  public final class MessageQueue {
+    method public int postSyncBarrier();
+    method public void removeSyncBarrier(int);
   }
 
   public class Process {
@@ -680,23 +705,12 @@
   }
 
   public final class StrictMode {
+    method public static void conditionallyCheckInstanceCounts();
     method public static void setViolationLogger(android.os.StrictMode.ViolationLogger);
-    field public static final int DETECT_CUSTOM = 8; // 0x8
-    field public static final int DETECT_DISK_READ = 2; // 0x2
-    field public static final int DETECT_DISK_WRITE = 1; // 0x1
-    field public static final int DETECT_NETWORK = 4; // 0x4
-    field public static final int DETECT_RESOURCE_MISMATCH = 16; // 0x10
-    field public static final int DETECT_UNBUFFERED_IO = 32; // 0x20
-    field public static final int DETECT_VM_ACTIVITY_LEAKS = 1024; // 0x400
-    field public static final int DETECT_VM_CLEARTEXT_NETWORK = 16384; // 0x4000
-    field public static final int DETECT_VM_CLOSABLE_LEAKS = 512; // 0x200
-    field public static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 32768; // 0x8000
-    field public static final int DETECT_VM_CURSOR_LEAKS = 256; // 0x100
-    field public static final int DETECT_VM_FILE_URI_EXPOSURE = 8192; // 0x2000
-    field public static final int DETECT_VM_INSTANCE_LEAKS = 2048; // 0x800
-    field public static final int DETECT_VM_NON_SDK_API_USAGE = 1073741824; // 0x40000000
-    field public static final int DETECT_VM_REGISTRATION_LEAKS = 4096; // 0x1000
-    field public static final int DETECT_VM_UNTAGGED_SOCKET = -2147483648; // 0x80000000
+  }
+
+  public static final class StrictMode.ThreadPolicy.Builder {
+    method public android.os.StrictMode.ThreadPolicy.Builder detectExplicitGc();
   }
 
   public static final class StrictMode.ViolationInfo implements android.os.Parcelable {
@@ -704,9 +718,8 @@
     ctor public StrictMode.ViolationInfo(android.os.Parcel, boolean);
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
-    method public int getPolicyMask();
     method public java.lang.String getStackTrace();
-    method public int getViolationBit();
+    method public java.lang.Class<? extends android.os.strictmode.Violation> getViolationClass();
     method public java.lang.String getViolationDetails();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.os.StrictMode.ViolationInfo> CREATOR;
@@ -724,6 +737,7 @@
   }
 
   public class SystemProperties {
+    method public static java.lang.String get(java.lang.String);
     method public static java.lang.String get(java.lang.String, java.lang.String);
   }
 
@@ -743,7 +757,17 @@
     method public static android.os.VibrationEffect get(int);
     method public static android.os.VibrationEffect get(int, boolean);
     method public static android.os.VibrationEffect get(android.net.Uri, android.content.Context);
+    method public abstract long getDuration();
     method protected static int scale(int, float, int);
+    field public static final int EFFECT_CLICK = 0; // 0x0
+    field public static final int EFFECT_DOUBLE_CLICK = 1; // 0x1
+    field public static final int EFFECT_HEAVY_CLICK = 5; // 0x5
+    field public static final int EFFECT_POP = 4; // 0x4
+    field public static final int EFFECT_STRENGTH_LIGHT = 0; // 0x0
+    field public static final int EFFECT_STRENGTH_MEDIUM = 1; // 0x1
+    field public static final int EFFECT_STRENGTH_STRONG = 2; // 0x2
+    field public static final int EFFECT_THUD = 3; // 0x3
+    field public static final int EFFECT_TICK = 2; // 0x2
     field public static final int[] RINGTONES;
   }
 
@@ -784,6 +808,86 @@
     field public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Waveform> CREATOR;
   }
 
+  public class WorkSource implements android.os.Parcelable {
+    ctor public WorkSource(int);
+    method public boolean add(int);
+    method public boolean add(int, java.lang.String);
+    method public deprecated android.os.WorkSource addReturningNewbs(android.os.WorkSource);
+    method public int get(int);
+    method public java.lang.String getName(int);
+    method public deprecated android.os.WorkSource[] setReturningDiffs(android.os.WorkSource);
+    method public int size();
+  }
+
+}
+
+package android.os.health {
+
+  public class HealthKeys {
+    ctor public HealthKeys();
+    field public static final int BASE_PACKAGE = 40000; // 0x9c40
+    field public static final int BASE_PID = 20000; // 0x4e20
+    field public static final int BASE_PROCESS = 30000; // 0x7530
+    field public static final int BASE_SERVICE = 50000; // 0xc350
+    field public static final int BASE_UID = 10000; // 0x2710
+    field public static final int TYPE_COUNT = 5; // 0x5
+    field public static final int TYPE_MEASUREMENT = 1; // 0x1
+    field public static final int TYPE_MEASUREMENTS = 4; // 0x4
+    field public static final int TYPE_STATS = 2; // 0x2
+    field public static final int TYPE_TIMER = 0; // 0x0
+    field public static final int TYPE_TIMERS = 3; // 0x3
+    field public static final int UNKNOWN_KEY = 0; // 0x0
+  }
+
+  public static abstract class HealthKeys.Constant implements java.lang.annotation.Annotation {
+  }
+
+  public static class HealthKeys.Constants {
+    ctor public HealthKeys.Constants(java.lang.Class);
+    method public java.lang.String getDataType();
+    method public int getIndex(int, int);
+    method public int[] getKeys(int);
+    method public int getSize(int);
+  }
+
+  public class HealthStats {
+    ctor public HealthStats(android.os.Parcel);
+  }
+
+  public class HealthStatsParceler implements android.os.Parcelable {
+    ctor public HealthStatsParceler(android.os.health.HealthStatsWriter);
+    ctor public HealthStatsParceler(android.os.Parcel);
+    method public int describeContents();
+    method public android.os.health.HealthStats getHealthStats();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.os.health.HealthStatsParceler> CREATOR;
+  }
+
+  public class HealthStatsWriter {
+    ctor public HealthStatsWriter(android.os.health.HealthKeys.Constants);
+    method public void addMeasurement(int, long);
+    method public void addMeasurements(int, java.lang.String, long);
+    method public void addStats(int, java.lang.String, android.os.health.HealthStatsWriter);
+    method public void addTimer(int, int, long);
+    method public void addTimers(int, java.lang.String, android.os.health.TimerStat);
+    method public void flattenToParcel(android.os.Parcel);
+  }
+
+}
+
+package android.os.storage {
+
+  public final class StorageVolume implements android.os.Parcelable {
+    method public java.lang.String getPath();
+  }
+
+}
+
+package android.os.strictmode {
+
+  public final class ExplicitGcViolation extends android.os.strictmode.Violation {
+  }
+
 }
 
 package android.print {
@@ -944,6 +1048,8 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
     field public static final java.lang.String KEY_PEOPLE = "key_people";
+    field public static final java.lang.String KEY_SMART_ACTIONS = "key_smart_actions";
+    field public static final java.lang.String KEY_SMART_REPLIES = "key_smart_replies";
     field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
   }
@@ -1367,6 +1473,11 @@
     method public static int getLongPressTooltipHideTimeout();
   }
 
+  public final class ViewTreeObserver {
+    method public void registerFrameCommitCallback(java.lang.Runnable);
+    method public boolean unregisterFrameCommitCallback(java.lang.Runnable);
+  }
+
   public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
     field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
     field public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 64; // 0x40
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 641ae00..043b7f9 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -16,10 +16,13 @@
 
 package com.android.commands.bmgr;
 
+import android.annotation.IntDef;
 import android.app.backup.BackupManager;
+import android.app.backup.BackupManagerMonitor;
 import android.app.backup.BackupProgress;
 import android.app.backup.BackupTransport;
 import android.app.backup.IBackupManager;
+import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
 import android.app.backup.IRestoreObserver;
 import android.app.backup.IRestoreSession;
@@ -28,6 +31,7 @@
 import android.content.ComponentName;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -36,10 +40,13 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
 public final class Bmgr {
@@ -247,7 +254,7 @@
     }
 
     // IBackupObserver generically usable for any backup/init operation
-    abstract class Observer extends IBackupObserver.Stub {
+    private static abstract class Observer extends IBackupObserver.Stub {
         private final Object trigger = new Object();
 
         @GuardedBy("trigger")
@@ -295,7 +302,7 @@
         }
     }
 
-    class BackupObserver extends Observer {
+    private static class BackupObserver extends Observer {
         @Override
         public void onUpdate(String currentPackage, BackupProgress backupProgress) {
             super.onUpdate(currentPackage, backupProgress);
@@ -347,7 +354,7 @@
         }
     }
 
-    private void backupNowAllPackages(boolean nonIncrementalBackup) {
+    private void backupNowAllPackages(boolean nonIncrementalBackup, @Monitor int monitorState) {
         int userId = UserHandle.USER_SYSTEM;
         IPackageManager mPm =
                 IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
@@ -372,20 +379,27 @@
                 System.err.println(e.toString());
                 System.err.println(BMGR_NOT_RUNNING_ERR);
             }
-            backupNowPackages(Arrays.asList(filteredPackages), nonIncrementalBackup);
+            backupNowPackages(Arrays.asList(filteredPackages), nonIncrementalBackup, monitorState);
         }
     }
 
-    private void backupNowPackages(List<String> packages, boolean nonIncrementalBackup) {
+    private void backupNowPackages(
+            List<String> packages, boolean nonIncrementalBackup, @Monitor int monitorState) {
         int flags = 0;
         if (nonIncrementalBackup) {
             flags |= BackupManager.FLAG_NON_INCREMENTAL_BACKUP;
         }
         try {
             BackupObserver observer = new BackupObserver();
-            // TODO: implement monitor here?
-            int err = mBmgr.requestBackup(packages.toArray(new String[packages.size()]), observer,
-                    null, flags);
+            BackupMonitor monitor =
+                    (monitorState != Monitor.OFF)
+                            ? new BackupMonitor(monitorState == Monitor.VERBOSE)
+                            : null;
+            int err = mBmgr.requestBackup(
+                    packages.toArray(new String[packages.size()]),
+                    observer,
+                    monitor,
+                    flags);
             if (err == 0) {
                 // Off and running -- wait for the backup to complete
                 observer.waitForCompletion();
@@ -402,6 +416,7 @@
         String pkg;
         boolean backupAll = false;
         boolean nonIncrementalBackup = false;
+        @Monitor int monitor = Monitor.OFF;
         ArrayList<String> allPkgs = new ArrayList<String>();
         while ((pkg = nextArg()) != null) {
             if (pkg.equals("--all")) {
@@ -410,6 +425,10 @@
                 nonIncrementalBackup = true;
             } else if (pkg.equals("--incremental")) {
                 nonIncrementalBackup = false;
+            } else if (pkg.equals("--monitor")) {
+                monitor = Monitor.NORMAL;
+            } else if (pkg.equals("--monitor-verbose")) {
+                monitor = Monitor.VERBOSE;
             } else {
                 if (!allPkgs.contains(pkg)) {
                     allPkgs.add(pkg);
@@ -420,14 +439,14 @@
             if (allPkgs.size() == 0) {
                 System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
                         "incremental backup for all packages.");
-                backupNowAllPackages(nonIncrementalBackup);
+                backupNowAllPackages(nonIncrementalBackup, monitor);
             } else {
                 System.err.println("Provide only '--all' flag or list of packages.");
             }
         } else if (allPkgs.size() > 0) {
             System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
                     "incremental backup for " + allPkgs.size() +" requested packages.");
-            backupNowPackages(allPkgs, nonIncrementalBackup);
+            backupNowPackages(allPkgs, nonIncrementalBackup, monitor);
         } else {
             System.err.println("Provide '--all' flag or list of packages.");
         }
@@ -824,7 +843,7 @@
         System.err.println("       bmgr run");
         System.err.println("       bmgr wipe TRANSPORT PACKAGE");
         System.err.println("       bmgr fullbackup PACKAGE...");
-        System.err.println("       bmgr backupnow --all|PACKAGE...");
+        System.err.println("       bmgr backupnow [--monitor|--monitor-verbose] --all|PACKAGE...");
         System.err.println("       bmgr cancel backups");
         System.err.println("");
         System.err.println("The 'backup' command schedules a backup pass for the named package.");
@@ -878,9 +897,164 @@
         System.err.println("");
         System.err.println("The 'backupnow' command runs an immediate backup for one or more packages.");
         System.err.println("    --all flag runs backup for all eligible packages.");
+        System.err.println("    --monitor flag prints monitor events.");
+        System.err.println("    --monitor-verbose flag prints monitor events with all keys.");
         System.err.println("For each package it will run key/value or full data backup ");
         System.err.println("depending on the package's manifest declarations.");
         System.err.println("The data is sent via the currently active transport.");
         System.err.println("The 'cancel backups' command cancels all running backups.");
     }
+
+    private static class BackupMonitor extends IBackupManagerMonitor.Stub {
+        private final boolean mVerbose;
+
+        private BackupMonitor(boolean verbose) {
+            mVerbose = verbose;
+        }
+
+        @Override
+        public void onEvent(Bundle event) throws RemoteException {
+            StringBuilder out = new StringBuilder();
+            int id = event.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);
+            int category = event.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY);
+            out.append("=> Event{").append(eventCategoryToString(category));
+            out.append(" / ").append(eventIdToString(id));
+            String packageName = event.getString(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME);
+            if (packageName != null) {
+                out.append(" : package = ").append(packageName);
+                if (event.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)) {
+                    long version =
+                            event.getLong(
+                                    BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION);
+                    out.append("(v").append(version).append(")");
+                }
+            }
+            if (mVerbose) {
+                Set<String> remainingKeys = new ArraySet<>(event.keySet());
+                remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);
+                remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY);
+                remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME);
+                remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION);
+                remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION);
+                if (!remainingKeys.isEmpty()) {
+                    out.append(", other keys =");
+                    for (String key : remainingKeys) {
+                        out.append(" ").append(key);
+                    }
+                }
+            }
+            out.append("}");
+            System.out.println(out.toString());
+        }
+    }
+
+    private static String eventCategoryToString(int eventCategory) {
+        switch (eventCategory) {
+            case BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT:
+                return "TRANSPORT";
+            case BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT:
+                return "AGENT";
+            case BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY:
+                return "BACKUP_MANAGER_POLICY";
+            default:
+                return "UNKNOWN_CATEGORY";
+        }
+    }
+
+    private static String eventIdToString(int eventId) {
+        switch (eventId) {
+            case BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_CANCEL:
+                return "FULL_BACKUP_CANCEL";
+            case BackupManagerMonitor.LOG_EVENT_ID_ILLEGAL_KEY:
+                return "ILLEGAL_KEY";
+            case BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND:
+                return "NO_DATA_TO_SEND";
+            case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_INELIGIBLE:
+                return "PACKAGE_INELIGIBLE";
+            case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_KEY_VALUE_PARTICIPANT:
+                return "PACKAGE_KEY_VALUE_PARTICIPANT";
+            case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_STOPPED:
+                return "PACKAGE_STOPPED";
+            case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_FOUND:
+                return "PACKAGE_NOT_FOUND";
+            case BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED:
+                return "BACKUP_DISABLED";
+            case BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED:
+                return "DEVICE_NOT_PROVISIONED";
+            case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT:
+                return "PACKAGE_TRANSPORT_NOT_PRESENT";
+            case BackupManagerMonitor.LOG_EVENT_ID_ERROR_PREFLIGHT:
+                return "ERROR_PREFLIGHT";
+            case BackupManagerMonitor.LOG_EVENT_ID_QUOTA_HIT_PREFLIGHT:
+                return "QUOTA_HIT_PREFLIGHT";
+            case BackupManagerMonitor.LOG_EVENT_ID_EXCEPTION_FULL_BACKUP:
+                return "EXCEPTION_FULL_BACKUP";
+            case BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL:
+                return "KEY_VALUE_BACKUP_CANCEL";
+            case BackupManagerMonitor.LOG_EVENT_ID_NO_RESTORE_METADATA_AVAILABLE:
+                return "NO_RESTORE_METADATA_AVAILABLE";
+            case BackupManagerMonitor.LOG_EVENT_ID_NO_PM_METADATA_RECEIVED:
+                return "NO_PM_METADATA_RECEIVED";
+            case BackupManagerMonitor.LOG_EVENT_ID_PM_AGENT_HAS_NO_METADATA:
+                return "PM_AGENT_HAS_NO_METADATA";
+            case BackupManagerMonitor.LOG_EVENT_ID_LOST_TRANSPORT:
+                return "LOST_TRANSPORT";
+            case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_PRESENT:
+                return "PACKAGE_NOT_PRESENT";
+            case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER:
+                return "RESTORE_VERSION_HIGHER";
+            case BackupManagerMonitor.LOG_EVENT_ID_APP_HAS_NO_AGENT:
+                return "APP_HAS_NO_AGENT";
+            case BackupManagerMonitor.LOG_EVENT_ID_SIGNATURE_MISMATCH:
+                return "SIGNATURE_MISMATCH";
+            case BackupManagerMonitor.LOG_EVENT_ID_CANT_FIND_AGENT:
+                return "CANT_FIND_AGENT";
+            case BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT:
+                return "KEY_VALUE_RESTORE_TIMEOUT";
+            case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_ANY_VERSION:
+                return "RESTORE_ANY_VERSION";
+            case BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH:
+                return "VERSIONS_MATCH";
+            case BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER:
+                return "VERSION_OF_BACKUP_OLDER";
+            case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH:
+                return "FULL_RESTORE_SIGNATURE_MISMATCH";
+            case BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO_AGENT:
+                return "SYSTEM_APP_NO_AGENT";
+            case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE:
+                return "FULL_RESTORE_ALLOW_BACKUP_FALSE";
+            case BackupManagerMonitor.LOG_EVENT_ID_APK_NOT_INSTALLED:
+                return "APK_NOT_INSTALLED";
+            case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK:
+                return "CANNOT_RESTORE_WITHOUT_APK";
+            case BackupManagerMonitor.LOG_EVENT_ID_MISSING_SIGNATURE:
+                return "MISSING_SIGNATURE";
+            case BackupManagerMonitor.LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE:
+                return "EXPECTED_DIFFERENT_PACKAGE";
+            case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_VERSION:
+                return "UNKNOWN_VERSION";
+            case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_TIMEOUT:
+                return "FULL_RESTORE_TIMEOUT";
+            case BackupManagerMonitor.LOG_EVENT_ID_CORRUPT_MANIFEST:
+                return "CORRUPT_MANIFEST";
+            case BackupManagerMonitor.LOG_EVENT_ID_WIDGET_METADATA_MISMATCH:
+                return "WIDGET_METADATA_MISMATCH";
+            case BackupManagerMonitor.LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION:
+                return "WIDGET_UNKNOWN_VERSION";
+            case BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES:
+                return "NO_PACKAGES";
+            case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL:
+                return "TRANSPORT_IS_NULL";
+            default:
+                return "UNKNOWN_ID";
+        }
+    }
+
+    @IntDef({Monitor.OFF, Monitor.NORMAL, Monitor.VERBOSE})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface Monitor {
+        int OFF = 0;
+        int NORMAL = 1;
+        int VERBOSE = 2;
+    }
 }
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 453fb72..6943dab 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -27,7 +27,9 @@
 
 LOCAL_SHARED_LIBRARIES += \
     libandroidthings \
+    libandroidthings_protos \
     libchrome \
+    libprotobuf-cpp-lite \
 
 LOCAL_STATIC_LIBRARIES += \
     libjsoncpp
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 8ffe5bf..048fb43 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -859,12 +859,12 @@
         mTimeCheckThread = nullptr;
     }
 
-    releaseAnimation(animation);
-
     if (clockFontInitialized) {
         glDeleteTextures(1, &animation->clockFont.texture.name);
     }
 
+    releaseAnimation(animation);
+
     return false;
 }
 
diff --git a/cmds/bootanimation/iot/Android.mk b/cmds/bootanimation/iot/Android.mk
index 8b475d3..3d288e4 100644
--- a/cmds/bootanimation/iot/Android.mk
+++ b/cmds/bootanimation/iot/Android.mk
@@ -25,9 +25,11 @@
 
 LOCAL_SHARED_LIBRARIES := \
     libandroidthings \
+    libandroidthings_protos \
     libbase \
     libchrome \
     liblog \
+    libprotobuf-cpp-lite \
 
 LOCAL_STATIC_LIBRARIES += \
     libjsoncpp
diff --git a/cmds/bootanimation/iot/BootParameters.cpp b/cmds/bootanimation/iot/BootParameters.cpp
index 2cf1c19..30a9b28 100644
--- a/cmds/bootanimation/iot/BootParameters.cpp
+++ b/cmds/bootanimation/iot/BootParameters.cpp
@@ -22,10 +22,13 @@
 #include <fcntl.h>
 
 #include <android-base/file.h>
+#include <json/json.h>
 #include <utils/Log.h>
 
 using android::base::ReadFileToString;
 using android::base::RemoveFileIfExists;
+using android::base::WriteStringToFile;
+using Json::ArrayIndex;
 using Json::Reader;
 using Json::Value;
 
@@ -33,23 +36,34 @@
 
 namespace {
 
-// Keys for supporting a silent boot and user-defined BootAction parameters.
-constexpr const char *kKeySilentBoot = "silent_boot";
-constexpr const char* kKeyParams = "params";
+// Keys for deprecated parameters. Devices that OTA from N to O and that used
+// the hidden BootParameters API will store these in the JSON blob. To support
+// the transition from N to O, these keys are mapped to the new parameters.
+constexpr const char *kKeyLegacyVolume = "volume";
+constexpr const char *kKeyLegacyAnimationsDisabled = "boot_animation_disabled";
+constexpr const char *kKeyLegacyParamNames = "param_names";
+constexpr const char *kKeyLegacyParamValues = "param_values";
 
-constexpr const char* kNextBootFile = "/data/misc/bootanimation/next_boot.json";
-constexpr const char* kLastBootFile = "/data/misc/bootanimation/last_boot.json";
+constexpr const char *kNextBootFile = "/data/misc/bootanimation/next_boot.proto";
+constexpr const char *kLastBootFile = "/data/misc/bootanimation/last_boot.proto";
 
-void swapBootConfigs() {
-    // rename() will fail if next_boot.json doesn't exist, so delete
-    // last_boot.json manually first.
+constexpr const char *kLegacyNextBootFile = "/data/misc/bootanimation/next_boot.json";
+constexpr const char *kLegacyLastBootFile = "/data/misc/bootanimation/last_boot.json";
+
+void removeLegacyFiles() {
     std::string err;
-    if (!RemoveFileIfExists(kLastBootFile, &err))
-        ALOGE("Unable to delete last boot file: %s", err.c_str());
+    if (!RemoveFileIfExists(kLegacyLastBootFile, &err)) {
+        ALOGW("Unable to delete %s: %s", kLegacyLastBootFile, err.c_str());
+    }
 
-    if (rename(kNextBootFile, kLastBootFile) && errno != ENOENT)
-        ALOGE("Unable to swap boot files: %s", strerror(errno));
+    err.clear();
+    if (!RemoveFileIfExists(kLegacyNextBootFile, &err)) {
+        ALOGW("Unable to delete %s: %s", kLegacyNextBootFile, err.c_str());
+    }
+}
 
+void createNextBootFile() {
+    errno = 0;
     int fd = open(kNextBootFile, O_CREAT, DEFFILEMODE);
     if (fd == -1) {
         ALOGE("Unable to create next boot file: %s", strerror(errno));
@@ -64,54 +78,120 @@
 
 }  // namespace
 
+// Renames the 'next' boot file to the 'last' file and reads its contents.
+bool BootParameters::swapAndLoadBootConfigContents(const char *lastBootFile,
+                                                   const char *nextBootFile,
+                                                   std::string *contents) {
+    if (!ReadFileToString(nextBootFile, contents)) {
+        RemoveFileIfExists(lastBootFile);
+        return false;
+    }
+
+    errno = 0;
+    if (rename(nextBootFile, lastBootFile) && errno != ENOENT)
+        ALOGE("Unable to swap boot files: %s", strerror(errno));
+
+    return true;
+}
+
 BootParameters::BootParameters() {
-    swapBootConfigs();
     loadParameters();
 }
 
-void BootParameters::loadParameters() {
-    std::string contents;
-    if (!ReadFileToString(kLastBootFile, &contents)) {
-        if (errno != ENOENT)
-            ALOGE("Unable to read from %s: %s", kLastBootFile, strerror(errno));
+// Saves the boot parameters state to disk so the framework can read it.
+void BootParameters::storeParameters() {
+    errno = 0;
+    if (!WriteStringToFile(mProto.SerializeAsString(), kLastBootFile)) {
+        ALOGE("Failed to write boot parameters to %s: %s", kLastBootFile, strerror(errno));
+    }
 
+    // WriteStringToFile sets the file permissions to 0666, but these are not
+    // honored by the system.
+    errno = 0;
+    if (chmod(kLastBootFile, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) {
+        ALOGE("Failed to set permissions for %s: %s", kLastBootFile, strerror(errno));
+    }
+}
+
+// Load the boot parameters from disk, try the old location and format if the
+// file does not exist. Note:
+// - Parse errors result in defaults being used (a normal boot).
+// - Legacy boot parameters default to a silent boot.
+void BootParameters::loadParameters() {
+    // Precedence is given to the new file format (.proto).
+    std::string contents;
+    if (swapAndLoadBootConfigContents(kLastBootFile, kNextBootFile, &contents)) {
+        parseBootParameters(contents);
+    } else if (swapAndLoadBootConfigContents(kLegacyLastBootFile, kLegacyNextBootFile, &contents)) {
+        parseLegacyBootParameters(contents);
+        storeParameters();
+        removeLegacyFiles();
+    }
+
+    createNextBootFile();
+}
+
+void BootParameters::parseBootParameters(const std::string &contents) {
+    if (!mProto.ParseFromString(contents)) {
+        ALOGW("Failed to parse parameters from %s", kLastBootFile);
         return;
     }
 
-    loadParameters(contents);
+    loadStateFromProto();
 }
 
-// If the boot parameters -
-// - File is missing, we assume a normal, non-silent boot.
-// - Are well-formed, initially assume a normal, non-silent boot and parse.
-void BootParameters::loadParameters(const std::string& raw_json) {
-  if (!Reader().parse(raw_json, mJson)) {
-    return;
-  }
-
-  parseBootParameters();
-}
-
-void BootParameters::parseBootParameters() {
-    // A missing key returns a safe, missing value.
-    // Ignore invalid or missing JSON parameters.
-    Value &jsonValue = mJson[kKeySilentBoot];
-    if (jsonValue.isBool()) {
-        mIsSilentBoot = jsonValue.asBool();
+// Parses the JSON in the proto.
+void BootParameters::parseLegacyBootParameters(const std::string &contents) {
+    Value json;
+    if (!Reader().parse(contents, json)) {
+        ALOGW("Failed to parse parameters from %s", kLegacyLastBootFile);
+        return;
     }
 
-    jsonValue = mJson[kKeyParams];
-    if (jsonValue.isObject()) {
-        // getMemberNames returns a copy of the keys which must be stored.
-        mKeys = jsonValue.getMemberNames();
-        for (auto &key : mKeys) {
-            Value &value = jsonValue[key];
-            if (value.isString()) {
-                mParameters.push_back(
-                    {.key = key.c_str(), .value = value.asCString()});
+    int volume = 0;
+    bool bootAnimationDisabled = true;
+
+    Value &jsonValue = json[kKeyLegacyVolume];
+    if (jsonValue.isIntegral()) {
+        volume = jsonValue.asInt();
+    }
+
+    jsonValue = json[kKeyLegacyAnimationsDisabled];
+    if (jsonValue.isIntegral()) {
+        bootAnimationDisabled = jsonValue.asInt() == 1;
+    }
+
+    // Assume a silent boot unless all of the following are true -
+    // 1. The volume is neither 0 nor -1000 (the legacy default value).
+    // 2. The boot animations are explicitly enabled.
+    // Note: brightness was never used.
+    mProto.set_silent_boot((volume == 0) || (volume == -1000) || bootAnimationDisabled);
+
+    Value &keys = json[kKeyLegacyParamNames];
+    Value &values = json[kKeyLegacyParamValues];
+    if (keys.isArray() && values.isArray() && (keys.size() == values.size())) {
+        for (ArrayIndex i = 0; i < keys.size(); ++i) {
+            auto &key = keys[i];
+            auto &value = values[i];
+            if (key.isString() && value.isString()) {
+                auto userParameter = mProto.add_user_parameter();
+                userParameter->set_key(key.asString());
+                userParameter->set_value(value.asString());
             }
         }
     }
+
+    loadStateFromProto();
+}
+
+void BootParameters::loadStateFromProto() {
+    // A missing key returns a safe, default value.
+    // Ignore invalid or missing parameters.
+    mIsSilentBoot = mProto.silent_boot();
+
+    for (const auto &param : mProto.user_parameter()) {
+        mParameters.push_back({.key = param.key().c_str(), .value = param.value().c_str()});
+    }
 }
 
 }  // namespace android
diff --git a/cmds/bootanimation/iot/BootParameters.h b/cmds/bootanimation/iot/BootParameters.h
index 49c34dd..cbd1ca6 100644
--- a/cmds/bootanimation/iot/BootParameters.h
+++ b/cmds/bootanimation/iot/BootParameters.h
@@ -22,7 +22,7 @@
 #include <vector>
 
 #include <boot_action/boot_action.h>  // libandroidthings native API.
-#include <json/json.h>
+#include <boot_parameters.pb.h>
 
 namespace android {
 
@@ -39,22 +39,33 @@
     // Returns the additional boot parameters that were set on reboot.
     const std::vector<ABootActionParameter>& getParameters() const { return mParameters; }
 
-    // Exposed for testing. Updates the parameters with new JSON values.
-    void loadParameters(const std::string& raw_json);
-private:
+    // Exposed for testing. Sets the parameters to the serialized proto.
+    void parseBootParameters(const std::string &contents);
+
+    // For devices that OTA from N to O.
+    // Exposed for testing. Sets the parameters to the raw JSON.
+    void parseLegacyBootParameters(const std::string &contents);
+
+    // Exposed for testing. Loads the contents from |nextBootFile| and replaces
+    // |lastBootFile| with |nextBootFile|.
+    static bool swapAndLoadBootConfigContents(const char *lastBootFile, const char *nextBootFile,
+                                              std::string *contents);
+
+  private:
     void loadParameters();
 
-    void parseBootParameters();
+    // Replaces the legacy JSON blob with the updated version, allowing the
+    // framework to read it.
+    void storeParameters();
+
+    void loadStateFromProto();
 
     bool mIsSilentBoot = false;
 
     std::vector<ABootActionParameter> mParameters;
 
-    // Store parsed JSON because mParameters makes a shallow copy.
-    Json::Value mJson;
-
-    // Store parameter keys because mParameters makes a shallow copy.
-    Json::Value::Members mKeys;
+    // Store the proto because mParameters makes a shallow copy.
+    android::things::proto::BootParameters mProto;
 };
 
 }  // namespace android
diff --git a/cmds/bootanimation/iot/BootParameters_test.cpp b/cmds/bootanimation/iot/BootParameters_test.cpp
index 27d7d6f..d55bce6 100644
--- a/cmds/bootanimation/iot/BootParameters_test.cpp
+++ b/cmds/bootanimation/iot/BootParameters_test.cpp
@@ -16,6 +16,13 @@
 
 #include "BootParameters.h"
 
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <boot_parameters.pb.h>
 #include <gtest/gtest.h>
 
 namespace android {
@@ -23,50 +30,51 @@
 namespace {
 
 TEST(BootParametersTest, TestNoBootParametersIsNotSilent) {
-    BootParameters boot_parameters = BootParameters();
-    boot_parameters.loadParameters("");
+    android::things::proto::BootParameters proto;
 
-    ASSERT_FALSE(boot_parameters.isSilentBoot());
-    ASSERT_EQ(0u, boot_parameters.getParameters().size());
+    BootParameters bootParameters = BootParameters();
+    bootParameters.parseBootParameters(proto.SerializeAsString());
+
+    ASSERT_FALSE(bootParameters.isSilentBoot());
+    ASSERT_EQ(0u, bootParameters.getParameters().size());
 }
 
 TEST(BootParametersTest, TestParseIsSilent) {
-    BootParameters boot_parameters = BootParameters();
-    boot_parameters.loadParameters(R"(
-    {
-      "silent_boot":true,
-      "params":{}
-    }
-    )");
+    android::things::proto::BootParameters proto;
+    proto.set_silent_boot(true);
 
-    ASSERT_TRUE(boot_parameters.isSilentBoot());
+    BootParameters bootParameters = BootParameters();
+    bootParameters.parseBootParameters(proto.SerializeAsString());
+
+    ASSERT_TRUE(bootParameters.isSilentBoot());
 }
 
 TEST(BootParametersTest, TestParseIsNotSilent) {
-    BootParameters boot_parameters = BootParameters();
-    boot_parameters.loadParameters(R"(
-    {
-      "silent_boot":false,
-      "params":{}
-    }
-    )");
+    android::things::proto::BootParameters proto;
+    proto.set_silent_boot(false);
 
-    ASSERT_FALSE(boot_parameters.isSilentBoot());
+    BootParameters bootParameters = BootParameters();
+    bootParameters.parseBootParameters(proto.SerializeAsString());
+
+    ASSERT_FALSE(bootParameters.isSilentBoot());
 }
 
 TEST(BootParametersTest, TestParseBootParameters) {
-    BootParameters boot_parameters = BootParameters();
-    boot_parameters.loadParameters(R"(
-    {
-      "silent_boot":false,
-      "params":{
-        "key1":"value1",
-        "key2":"value2"
-      }
-    }
-    )");
+    android::things::proto::BootParameters proto;
+    proto.set_silent_boot(false);
 
-    auto &parameters = boot_parameters.getParameters();
+    auto userParameter = proto.add_user_parameter();
+    userParameter->set_key("key1");
+    userParameter->set_value("value1");
+
+    userParameter = proto.add_user_parameter();
+    userParameter->set_key("key2");
+    userParameter->set_value("value2");
+
+    BootParameters bootParameters = BootParameters();
+    bootParameters.parseBootParameters(proto.SerializeAsString());
+
+    auto &parameters = bootParameters.getParameters();
     ASSERT_EQ(2u, parameters.size());
     ASSERT_STREQ(parameters[0].key, "key1");
     ASSERT_STREQ(parameters[0].value, "value1");
@@ -74,35 +82,182 @@
     ASSERT_STREQ(parameters[1].value, "value2");
 }
 
-TEST(BootParametersTest, TestParseMissingParametersIsNotSilent) {
-    BootParameters boot_parameters = BootParameters();
-    boot_parameters.loadParameters(R"(
+TEST(BootParametersTest, TestParseLegacyDisableBootAnimationIsSilent) {
+    BootParameters bootParameters = BootParameters();
+    bootParameters.parseLegacyBootParameters(R"(
     {
-      "params":{}
+      "brightness":200,
+      "volume":100,
+      "boot_animation_disabled":1,
+      "param_names":[],
+      "param_values":[]
     }
     )");
 
-    ASSERT_FALSE(boot_parameters.isSilentBoot());
+    ASSERT_TRUE(bootParameters.isSilentBoot());
 }
 
-TEST(BootParametersTest, TestParseMalformedParametersAreSkipped) {
-    BootParameters boot_parameters = BootParameters();
-    boot_parameters.loadParameters(R"(
+TEST(BootParametersTest, TestParseLegacyZeroVolumeIsSilent) {
+    BootParameters bootParameters = BootParameters();
+    bootParameters.parseLegacyBootParameters(R"(
     {
-      "silent_boot":false,
-      "params":{
-        "key1":123,
-        "key2":"value2"
-      }
+      "brightness":200,
+      "volume":0,
+      "boot_animation_disabled":0,
+      "param_names":[],
+      "param_values":[]
     }
     )");
 
-    auto &parameters = boot_parameters.getParameters();
+    ASSERT_TRUE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseLegacyDefaultVolumeIsSilent) {
+    BootParameters bootParameters = BootParameters();
+    bootParameters.parseLegacyBootParameters(R"(
+    {
+      "brightness":200,
+      "volume":-1000,
+      "boot_animation_disabled":0,
+      "param_names":[],
+      "param_values":[]
+    }
+    )");
+
+    ASSERT_TRUE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseLegacyNotSilent) {
+    BootParameters bootParameters = BootParameters();
+    bootParameters.parseLegacyBootParameters(R"(
+    {
+      "brightness":200,
+      "volume":500,
+      "boot_animation_disabled":0,
+      "param_names":[],
+      "param_values":[]
+    }
+    )");
+
+    ASSERT_FALSE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseLegacyParameters) {
+    BootParameters bootParameters = BootParameters();
+    bootParameters.parseLegacyBootParameters(R"(
+    {
+      "brightness":200,
+      "volume":100,
+      "boot_animation_disabled":1,
+      "param_names":["key1", "key2"],
+      "param_values":["value1", "value2"]
+    }
+    )");
+
+    auto parameters = bootParameters.getParameters();
+    ASSERT_EQ(2u, parameters.size());
+    ASSERT_STREQ(parameters[0].key, "key1");
+    ASSERT_STREQ(parameters[0].value, "value1");
+    ASSERT_STREQ(parameters[1].key, "key2");
+    ASSERT_STREQ(parameters[1].value, "value2");
+}
+
+TEST(BootParametersTest, TestParseLegacyZeroParameters) {
+    BootParameters bootParameters = BootParameters();
+    bootParameters.parseLegacyBootParameters(R"(
+    {
+      "brightness":200,
+      "volume":100,
+      "boot_animation_disabled":1,
+      "param_names":[],
+      "param_values":[]
+    }
+    )");
+
+    ASSERT_EQ(0u, bootParameters.getParameters().size());
+}
+
+TEST(BootParametersTest, TestMalformedLegacyParametersAreSkipped) {
+    BootParameters bootParameters = BootParameters();
+    bootParameters.parseLegacyBootParameters(R"(
+    {
+      "brightness":500,
+      "volume":500,
+      "boot_animation_disabled":0,
+      "param_names":["key1", "key2"],
+      "param_values":[1, "value2"]
+    }
+    )");
+
+    auto parameters = bootParameters.getParameters();
     ASSERT_EQ(1u, parameters.size());
     ASSERT_STREQ(parameters[0].key, "key2");
     ASSERT_STREQ(parameters[0].value, "value2");
 }
 
+TEST(BootParametersTest, TestLegacyUnequalParameterSizesAreSkipped) {
+    BootParameters bootParameters = BootParameters();
+    bootParameters.parseLegacyBootParameters(R"(
+    {
+      "brightness":500,
+      "volume":500,
+      "boot_animation_disabled":0,
+      "param_names":["key1", "key2"],
+      "param_values":["value1"]
+    }
+    )");
+
+    ASSERT_EQ(0u, bootParameters.getParameters().size());
+}
+
+TEST(BootParametersTest, TestMissingLegacyBootParametersIsSilent) {
+    BootParameters bootParameters = BootParameters();
+    bootParameters.parseLegacyBootParameters(R"(
+    {
+      "brightness":500
+    }
+    )");
+
+    EXPECT_TRUE(bootParameters.isSilentBoot());
+    ASSERT_EQ(0u, bootParameters.getParameters().size());
+}
+
+TEST(BootParametersTest, TestLastFileIsRemovedOnError) {
+    TemporaryFile lastFile;
+    TemporaryDir tempDir;
+    std::string nonExistentFilePath(std::string(tempDir.path) + "/nonexistent");
+    std::string contents;
+
+    BootParameters::swapAndLoadBootConfigContents(lastFile.path, nonExistentFilePath.c_str(),
+                                                  &contents);
+
+    struct stat buf;
+    ASSERT_EQ(-1, lstat(lastFile.path, &buf));
+    ASSERT_TRUE(contents.empty());
+}
+
+TEST(BootParametersTest, TestNextFileIsRemovedLastFileExistsOnSuccess) {
+    TemporaryFile lastFile;
+    TemporaryFile nextFile;
+
+    base::WriteStringToFile("foo", nextFile.path);
+
+    std::string contents;
+    // Expected side effects:
+    // - |next_file| is moved to |last_file|
+    // - |contents| is the contents of |next_file| before being moved.
+    BootParameters::swapAndLoadBootConfigContents(lastFile.path, nextFile.path, &contents);
+
+    struct stat buf;
+    ASSERT_EQ(0, lstat(lastFile.path, &buf));
+    ASSERT_EQ(-1, lstat(nextFile.path, &buf));
+    ASSERT_EQ(contents, "foo");
+
+    contents.clear();
+    ASSERT_TRUE(base::ReadFileToString(lastFile.path, &contents));
+    ASSERT_EQ(contents, "foo");
+}
+
 }  // namespace
 
 }  // namespace android
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 6e0bd3a..36e51b9 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -462,7 +462,7 @@
                 IBinder token = new Binder();
                 try {
                     ContentProviderHolder holder = activityManager.getContentProviderExternal(
-                            providerName, mUserId, token);
+                            providerName, mUserId, token, "*cmd*");
                     if (holder == null) {
                         throw new IllegalStateException("Could not find provider: " + providerName);
                     }
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 7c1a555..376b13c 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -46,6 +46,7 @@
     private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner";
     private static final String COMMAND_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
     private static final String COMMAND_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
+    private static final String COMMAND_FORCE_NETWORK_LOGS = "force-network-logs";
     private static final String COMMAND_FORCE_SECURITY_LOGS = "force-security-logs";
 
     private IDevicePolicyManager mDevicePolicyManager;
@@ -84,6 +85,9 @@
                 "feature development to prevent triggering restriction on setting freeze " +
                 "periods.\n" +
                 "\n" +
+                "dpm " + COMMAND_FORCE_NETWORK_LOGS + ": makes all network logs available to " +
+                "the DPC and triggers DeviceAdminReceiver.onNetworkLogsAvailable() if needed.\n" +
+                "\n" +
                 "dpm " + COMMAND_FORCE_SECURITY_LOGS + ": makes all security logs available to " +
                 "the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed.");
     }
@@ -114,6 +118,9 @@
             case COMMAND_CLEAR_FREEZE_PERIOD_RECORD:
                 runClearFreezePeriodRecord();
                 break;
+            case COMMAND_FORCE_NETWORK_LOGS:
+                runForceNetworkLogs();
+                break;
             case COMMAND_FORCE_SECURITY_LOGS:
                 runForceSecurityLogs();
                 break;
@@ -122,6 +129,18 @@
         }
     }
 
+    private void runForceNetworkLogs() throws RemoteException, InterruptedException {
+        while (true) {
+            final long toWait = mDevicePolicyManager.forceNetworkLogs();
+            if (toWait == 0) {
+                break;
+            }
+            System.out.println("We have to wait for " + toWait + " milliseconds...");
+            Thread.sleep(toWait);
+        }
+        System.out.println("Success");
+    }
+
     private void runForceSecurityLogs() throws RemoteException, InterruptedException {
         while (true) {
             final long toWait = mDevicePolicyManager.forceSecurityLogs();
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 5cc4fc4..b3e287b 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -42,7 +42,6 @@
 namespace uhid {
 
 static const char* UHID_PATH = "/dev/uhid";
-static const size_t UHID_MAX_NAME_LENGTH = 128;
 
 static struct {
     jmethodID onDeviceOpen;
@@ -90,8 +89,13 @@
 }
 
 Device* Device::open(int32_t id, const char* name, int32_t vid, int32_t pid,
-        std::unique_ptr<uint8_t[]> descriptor, size_t descriptorSize,
-        std::unique_ptr<DeviceCallback> callback) {
+        std::vector<uint8_t> descriptor, std::unique_ptr<DeviceCallback> callback) {
+
+    size_t size = descriptor.size();
+    if (size > HID_MAX_DESCRIPTOR_SIZE) {
+        LOGE("Received invalid hid report with descriptor size %zu, skipping", size);
+        return nullptr;
+    }
 
     int fd = ::open(UHID_PATH, O_RDWR | O_CLOEXEC);
     if (fd < 0) {
@@ -102,10 +106,10 @@
     struct uhid_event ev;
     memset(&ev, 0, sizeof(ev));
     ev.type = UHID_CREATE2;
-    strncpy((char*)ev.u.create2.name, name, UHID_MAX_NAME_LENGTH);
-    memcpy(&ev.u.create2.rd_data, descriptor.get(),
-            descriptorSize * sizeof(ev.u.create2.rd_data[0]));
-    ev.u.create2.rd_size = descriptorSize;
+    strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name));
+    memcpy(&ev.u.create2.rd_data, descriptor.data(),
+            size * sizeof(ev.u.create2.rd_data[0]));
+    ev.u.create2.rd_size = size;
     ev.u.create2.bus = BUS_BLUETOOTH;
     ev.u.create2.vendor = vid;
     ev.u.create2.product = pid;
@@ -156,12 +160,17 @@
     mFd = -1;
 }
 
-void Device::sendReport(uint8_t* report, size_t reportSize) {
+void Device::sendReport(const std::vector<uint8_t>& report) const {
+    if (report.size() > UHID_DATA_MAX) {
+        LOGE("Received invalid report of size %zu, skipping", report.size());
+        return;
+    }
+
     struct uhid_event ev;
     memset(&ev, 0, sizeof(ev));
     ev.type = UHID_INPUT2;
-    ev.u.input2.size = reportSize;
-    memcpy(&ev.u.input2.data, report, reportSize);
+    ev.u.input2.size = report.size();
+    memcpy(&ev.u.input2.data, report.data(), report.size() * sizeof(ev.u.input2.data[0]));
     ssize_t ret = TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
     if (ret < 0 || ret != sizeof(ev)) {
         LOGE("Failed to send hid event: %s", strerror(errno));
@@ -191,12 +200,13 @@
 
 } // namespace uhid
 
-std::unique_ptr<uint8_t[]> getData(JNIEnv* env, jbyteArray javaArray, size_t& outSize) {
+std::vector<uint8_t> getData(JNIEnv* env, jbyteArray javaArray) {
     ScopedByteArrayRO scopedArray(env, javaArray);
-    outSize = scopedArray.size();
-    std::unique_ptr<uint8_t[]> data(new uint8_t[outSize]);
-    for (size_t i = 0; i < outSize; i++) {
-        data[i] = static_cast<uint8_t>(scopedArray[i]);
+    size_t size = scopedArray.size();
+    std::vector<uint8_t> data;
+    data.reserve(size);
+    for (size_t i = 0; i < size; i++) {
+        data.push_back(static_cast<uint8_t>(scopedArray[i]));
     }
     return data;
 }
@@ -208,23 +218,20 @@
         return 0;
     }
 
-    size_t size;
-    std::unique_ptr<uint8_t[]> desc = getData(env, rawDescriptor, size);
+    std::vector<uint8_t> desc = getData(env, rawDescriptor);
 
     std::unique_ptr<uhid::DeviceCallback> cb(new uhid::DeviceCallback(env, callback));
 
     uhid::Device* d = uhid::Device::open(
-            id, reinterpret_cast<const char*>(name.c_str()), vid, pid,
-            std::move(desc), size, std::move(cb));
+            id, reinterpret_cast<const char*>(name.c_str()), vid, pid, desc, std::move(cb));
     return reinterpret_cast<jlong>(d);
 }
 
 static void sendReport(JNIEnv* env, jclass /* clazz */, jlong ptr, jbyteArray rawReport) {
-    size_t size;
-    std::unique_ptr<uint8_t[]> report = getData(env, rawReport, size);
+    std::vector<uint8_t> report = getData(env, rawReport);
     uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
     if (d) {
-        d->sendReport(report.get(), size);
+        d->sendReport(report);
     } else {
         LOGE("Could not send report, Device* is null!");
     }
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index 149456d..61a1f76 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -15,6 +15,7 @@
  */
 
 #include <memory>
+#include <vector>
 
 #include <jni.h>
 
@@ -38,13 +39,12 @@
 class Device {
 public:
     static Device* open(int32_t id, const char* name, int32_t vid, int32_t pid,
-            std::unique_ptr<uint8_t[]> descriptor, size_t descriptorSize,
-            std::unique_ptr<DeviceCallback> callback);
+            std::vector<uint8_t> descriptor, std::unique_ptr<DeviceCallback> callback);
 
     Device(int32_t id, int fd, std::unique_ptr<DeviceCallback> callback);
     ~Device();
 
-    void sendReport(uint8_t* report, size_t reportSize);
+    void sendReport(const std::vector<uint8_t>& report) const;
     void close();
 
     int handleEvents(int events);
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
index 4c4d1b9..012310c 100644
--- a/cmds/incident_helper/src/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -97,7 +97,7 @@
 
     size_t lastIndex = 0;
     int i = 0;
-    while (headerNames[i] != NULL) {
+    while (headerNames[i] != nullptr) {
         std::string s = headerNames[i];
         lastIndex = line.find(s, lastIndex);
         if (lastIndex == std::string::npos) {
@@ -237,18 +237,18 @@
 Reader::Reader(const int fd)
 {
     mFile = fdopen(fd, "r");
-    mStatus = mFile == NULL ? "Invalid fd " + std::to_string(fd) : "";
+    mStatus = mFile == nullptr ? "Invalid fd " + std::to_string(fd) : "";
 }
 
 Reader::~Reader()
 {
-    if (mFile != NULL) fclose(mFile);
+    if (mFile != nullptr) fclose(mFile);
 }
 
 bool Reader::readLine(std::string* line) {
-    if (mFile == NULL) return false;
+    if (mFile == nullptr) return false;
 
-    char* buf = NULL;
+    char* buf = nullptr;
     size_t len = 0;
     ssize_t read = getline(&buf, &len, mFile);
     if (read != -1) {
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
index 418dc3f..809a771 100644
--- a/cmds/incident_helper/src/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -73,7 +73,8 @@
         case 2006:
             return new BatteryTypeParser();
         default:
-            return NULL;
+            // Return no op parser when no specific ones are implemented.
+            return new NoopParser();
     }
 }
 
@@ -97,7 +98,7 @@
 
     fprintf(stderr, "Pasring section %d...\n", sectionID);
     TextParserBase* parser = selectParser(sectionID);
-    if (parser != NULL) {
+    if (parser != nullptr) {
         fprintf(stderr, "Running parser: %s\n", parser->name.string());
         status_t err = parser->Parse(STDIN_FILENO, STDOUT_FILENO);
         if (err != NO_ERROR) {
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
index eed68b9..21ced9c 100644
--- a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
@@ -109,7 +109,7 @@
             nextToUsage = false;
 
             // NAME is not in the list since we need to modify the end of the CMD index.
-            const char* headerNames[] = { "PID", "TID", "USER", "PR", "NI", "CPU", "S", "VIRT", "RES", "PCY", "CMD", NULL };
+            const char* headerNames[] = { "PID", "TID", "USER", "PR", "NI", "CPU", "S", "VIRT", "RES", "PCY", "CMD", nullptr };
             if (!getColumnIndices(columnIndices, headerNames, line)) {
                 return -1;
             }
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
index 0615c74..2a89c920 100644
--- a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
@@ -75,18 +75,13 @@
             } else return BAD_VALUE;
             // expect part 2 starts with "type"
             if (stripPrefix(&record[2], "type")) {
-                // expect the rest of part 2 has number of (pageBlockOrder + 2) parts
                 // An example looks like:
                 // header line:      type    0   1   2 3 4 5 6 7 8 9 10
                 // record line: Unmovable  426 279 226 1 1 1 0 0 2 2  0
-                // The pageBlockOrder = 10 and it's zero-indexed. so total parts
-                // are 10 + 1(zero-indexed) + 1(the type part) = 12.
                 record_t pageCounts = parseRecord(record[2]);
-                int pageCountsSize = pageBlockOrder + 2;
-                if ((int)pageCounts.size() != pageCountsSize) return BAD_VALUE;
 
                 proto.write(PageTypeInfoProto::MigrateType::TYPE, pageCounts[0]);
-                for (auto i=1; i<pageCountsSize; i++) {
+                for (size_t i=1; i<pageCounts.size(); i++) {
                     proto.write(PageTypeInfoProto::MigrateType::FREE_PAGES_COUNT, toInt(pageCounts[i]));
                 }
             } else return BAD_VALUE;
@@ -125,4 +120,4 @@
 
     fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
     return NO_ERROR;
-}
\ No newline at end of file
+}
diff --git a/cmds/incident_helper/src/parsers/PsParser.cpp b/cmds/incident_helper/src/parsers/PsParser.cpp
index 8d64064..d3cb4be 100644
--- a/cmds/incident_helper/src/parsers/PsParser.cpp
+++ b/cmds/incident_helper/src/parsers/PsParser.cpp
@@ -48,7 +48,7 @@
         if (nline++ == 0) {
             header = parseHeader(line, DEFAULT_WHITESPACE);
 
-            const char* headerNames[] = { "LABEL", "USER", "PID", "TID", "PPID", "VSZ", "RSS", "WCHAN", "ADDR", "S", "PRI", "NI", "RTPRIO", "SCH", "PCY", "TIME", "CMD", NULL };
+            const char* headerNames[] = { "LABEL", "USER", "PID", "TID", "PPID", "VSZ", "RSS", "WCHAN", "ADDR", "S", "PRI", "NI", "RTPRIO", "SCH", "PCY", "TIME", "CMD", nullptr };
             if (!getColumnIndices(columnIndices, headerNames, line)) {
                 return -1;
             }
diff --git a/cmds/incident_helper/testdata/pagetypeinfo.txt b/cmds/incident_helper/testdata/pagetypeinfo.txt
index d45ddc4..c65b5a1 100644
--- a/cmds/incident_helper/testdata/pagetypeinfo.txt
+++ b/cmds/incident_helper/testdata/pagetypeinfo.txt
@@ -1,5 +1,5 @@
-Page block order: 10
-Pages per block:  1024
+Page block order: 9
+Pages per block:  512
 
 Free pages count per migrate type at order       0      1      2      3      4      5      6      7      8      9     10 
 Node    0, zone      DMA, type    Unmovable    426    279    226      1      1      1      0      0      2      2      0 
diff --git a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
index 9bad7be..5688681 100644
--- a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
+++ b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
@@ -54,8 +54,8 @@
     PageTypeInfoParser parser;
     PageTypeInfoProto expected;
 
-    expected.set_page_block_order(10);
-    expected.set_pages_per_block(1024);
+    expected.set_page_block_order(9);
+    expected.set_pages_per_block(512);
 
     PageTypeInfoProto::MigrateType* mt1 = expected.add_migrate_types();
     mt1->set_node(0);
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index e305b54..e92cf94 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -314,6 +314,19 @@
             mThrottler->dump(out);
             return NO_ERROR;
         }
+        if (!args[0].compare(String8("section"))) {
+            int id = atoi(args[1]);
+            int idx = 0;
+            while (SECTION_LIST[idx] != NULL) {
+                const Section* section = SECTION_LIST[idx];
+                if (section->id == id) {
+                    fprintf(out, "Section[%d] %s\n", id, section->name.string());
+                    break;
+                }
+                idx++;
+            }
+            return NO_ERROR;
+        }
     }
     return cmd_help(out);
 }
@@ -321,8 +334,9 @@
 status_t IncidentService::cmd_help(FILE* out) {
     fprintf(out, "usage: adb shell cmd incident privacy print <section_id>\n");
     fprintf(out, "usage: adb shell cmd incident privacy parse <section_id> < proto.txt\n");
-    fprintf(out, "    Prints/parses for the section id.\n");
-    fprintf(out, "\n");
+    fprintf(out, "    Prints/parses for the section id.\n\n");
+    fprintf(out, "usage: adb shell cmd incident section <section_id>\n");
+    fprintf(out, "    Prints section id and its name.\n\n");
     fprintf(out, "usage: adb shell cmd incident throttler\n");
     fprintf(out, "    Prints the current throttler state\n");
     return NO_ERROR;
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index bdfcd98..87799b3 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -151,11 +151,10 @@
 }
 
 // ================================================================================
-Section::Section(int i, int64_t timeoutMs, bool userdebugAndEngOnly, bool deviceSpecific)
+Section::Section(int i, int64_t timeoutMs, bool userdebugAndEngOnly)
     : id(i),
       timeoutMs(timeoutMs),
-      userdebugAndEngOnly(userdebugAndEngOnly),
-      deviceSpecific(deviceSpecific) {}
+      userdebugAndEngOnly(userdebugAndEngOnly) {}
 
 Section::~Section() {}
 
@@ -240,9 +239,8 @@
 // ================================================================================
 static inline bool isSysfs(const char* filename) { return strncmp(filename, "/sys/", 5) == 0; }
 
-FileSection::FileSection(int id, const char* filename, const bool deviceSpecific,
-                         const int64_t timeoutMs)
-    : Section(id, timeoutMs, false, deviceSpecific), mFilename(filename) {
+FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
+    : Section(id, timeoutMs, false), mFilename(filename) {
     name = "file ";
     name += filename;
     mIsSysfs = isSysfs(filename);
@@ -256,7 +254,9 @@
     unique_fd fd(open(mFilename, O_RDONLY | O_CLOEXEC));
     if (fd.get() == -1) {
         ALOGW("[%s] failed to open file", this->name.string());
-        return this->deviceSpecific ? NO_ERROR : -errno;
+        // There may be some devices/architectures that won't have the file.
+        // Just return here without an error.
+        return NO_ERROR;
     }
 
     FdBuffer buffer;
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index a031a15..302b4ef 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -41,11 +41,9 @@
     const int id;
     const int64_t timeoutMs;  // each section must have a timeout
     const bool userdebugAndEngOnly;
-    const bool deviceSpecific;
     String8 name;
 
-    Section(int id, int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS, bool userdebugAndEngOnly = false,
-            bool deviceSpecific = false);
+    Section(int id, int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS, bool userdebugAndEngOnly = false);
     virtual ~Section();
 
     virtual status_t Execute(ReportRequestSet* requests) const = 0;
@@ -78,7 +76,7 @@
  */
 class FileSection : public Section {
 public:
-    FileSection(int id, const char* filename, bool deviceSpecific = false,
+    FileSection(int id, const char* filename,
                 int64_t timeoutMs = 5000 /* 5 seconds */);
     virtual ~FileSection();
 
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 3c338b3..9b684a0 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -144,15 +144,15 @@
 }
 
 TEST_F(SectionTest, FileSectionNotExist) {
-    FileSection fs1(NOOP_PARSER, "notexist", false, QUICK_TIMEOUT_MS);
-    ASSERT_EQ(NAME_NOT_FOUND, fs1.Execute(&requests));
+    FileSection fs1(NOOP_PARSER, "notexist", QUICK_TIMEOUT_MS);
+    ASSERT_EQ(NO_ERROR, fs1.Execute(&requests));
 
-    FileSection fs2(NOOP_PARSER, "notexist", true, QUICK_TIMEOUT_MS);
+    FileSection fs2(NOOP_PARSER, "notexist", QUICK_TIMEOUT_MS);
     ASSERT_EQ(NO_ERROR, fs2.Execute(&requests));
 }
 
 TEST_F(SectionTest, FileSectionTimeout) {
-    FileSection fs(TIMEOUT_PARSER, tf.path, false, QUICK_TIMEOUT_MS);
+    FileSection fs(TIMEOUT_PARSER, tf.path, QUICK_TIMEOUT_MS);
     ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
     ASSERT_TRUE(requests.sectionStats(TIMEOUT_PARSER)->timed_out());
 }
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 5e3b85ba..61c185f 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -41,7 +41,7 @@
     src/external/SubsystemSleepStatePuller.cpp \
     src/external/ResourceHealthManagerPuller.cpp \
     src/external/ResourceThermalManagerPuller.cpp \
-    src/external/StatsPullerManagerImpl.cpp \
+    src/external/StatsPullerManager.cpp \
     src/external/puller_util.cpp \
     src/logd/LogEvent.cpp \
     src/logd/LogListener.cpp \
@@ -60,7 +60,6 @@
     src/metrics/MetricsManager.cpp \
     src/metrics/metrics_manager_util.cpp \
     src/packages/UidMap.cpp \
-    src/perfetto/perfetto_config.proto \
     src/storage/StorageManager.cpp \
     src/StatsLogProcessor.cpp \
     src/StatsService.cpp \
@@ -71,10 +70,9 @@
     src/guardrail/StatsdStats.cpp \
     src/socket/StatsSocketListener.cpp
 
-# TODO: Once statsd is using a blueprint file, migrate to the proper filegroups.
+# TODO(b/110563449): Once statsd is using a blueprint file, migrate to the proper filegroups.
 statsd_common_src += \
-    ../../../../system/extras/perfprofd/binder_interface/aidl/android/os/IPerfProfd.aidl \
-    src/perfprofd/perfprofd_config.proto
+    ../../../../system/extras/perfprofd/binder_interface/aidl/android/os/IPerfProfd.aidl
 
 statsd_common_c_includes := \
     $(LOCAL_PATH)/src \
@@ -84,8 +82,7 @@
     $(LOCAL_PATH)/../../core/java
 
 statsd_common_static_libraries := \
-    libhealthhalutils \
-    libplatformprotos \
+    libhealthhalutils
 
 statsd_common_shared_libraries := \
     libbase \
@@ -234,7 +231,8 @@
 
 LOCAL_STATIC_LIBRARIES := \
     $(statsd_common_static_libraries) \
-    libgmock
+    libgmock \
+    libplatformprotos
 
 LOCAL_PROTOC_OPTIMIZE_TYPE := full
 
@@ -256,8 +254,6 @@
 LOCAL_SRC_FILES := \
     src/stats_log.proto \
     src/statsd_config.proto \
-    src/perfetto/perfetto_config.proto \
-    src/perfprofd/perfprofd_config.proto \
     src/atoms.proto
 
 LOCAL_PROTOC_OPTIMIZE_TYPE := lite
@@ -301,7 +297,6 @@
 LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
                         libprotobuf-cpp-full
 
-
 LOCAL_STATIC_JAVA_LIBRARIES := \
     platformprotoslite
 
@@ -319,7 +314,8 @@
 LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
 
 LOCAL_STATIC_LIBRARIES := \
-    $(statsd_common_static_libraries)
+    $(statsd_common_static_libraries) \
+    libplatformprotos
 
 LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
     libgtest_prod \
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index 50ed18d..067b6ed 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -362,11 +362,12 @@
 sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
                                               const ConfigKey& key) {
     sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
-    sp<StatsLogProcessor> processor = new StatsLogProcessor(
-        uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec * NS_PER_SEC,
-        [](const ConfigKey&){return true;});
+    sp<StatsLogProcessor> processor =
+            new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+                                  timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; });
     processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config);
     return processor;
 }
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index f150f07..7b6d29b 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -141,6 +141,9 @@
         case FLOAT:
             float_value = from.float_value;
             break;
+        case DOUBLE:
+            double_value = from.double_value;
+            break;
         case STRING:
             str_value = from.str_value;
             break;
@@ -157,6 +160,8 @@
             return std::to_string(long_value) + "[L]";
         case FLOAT:
             return std::to_string(float_value) + "[F]";
+        case DOUBLE:
+            return std::to_string(double_value) + "[D]";
         case STRING:
             return str_value + "[S]";
         default:
@@ -174,6 +179,8 @@
             return long_value == that.long_value;
         case FLOAT:
             return float_value == that.float_value;
+        case DOUBLE:
+            return double_value == that.double_value;
         case STRING:
             return str_value == that.str_value;
         default:
@@ -190,6 +197,8 @@
             return long_value != that.long_value;
         case FLOAT:
             return float_value != that.float_value;
+        case DOUBLE:
+            return double_value != that.double_value;
         case STRING:
             return str_value != that.str_value;
         default:
@@ -207,6 +216,8 @@
             return long_value < that.long_value;
         case FLOAT:
             return float_value < that.float_value;
+        case DOUBLE:
+            return double_value < that.double_value;
         case STRING:
             return str_value < that.str_value;
         default:
@@ -214,6 +225,142 @@
     }
 }
 
+bool Value::operator>(const Value& that) const {
+    if (type != that.getType()) return type > that.getType();
+
+    switch (type) {
+        case INT:
+            return int_value > that.int_value;
+        case LONG:
+            return long_value > that.long_value;
+        case FLOAT:
+            return float_value > that.float_value;
+        case DOUBLE:
+            return double_value > that.double_value;
+        case STRING:
+            return str_value > that.str_value;
+        default:
+            return false;
+    }
+}
+
+bool Value::operator>=(const Value& that) const {
+    if (type != that.getType()) return type >= that.getType();
+
+    switch (type) {
+        case INT:
+            return int_value >= that.int_value;
+        case LONG:
+            return long_value >= that.long_value;
+        case FLOAT:
+            return float_value >= that.float_value;
+        case DOUBLE:
+            return double_value >= that.double_value;
+        case STRING:
+            return str_value >= that.str_value;
+        default:
+            return false;
+    }
+}
+
+Value Value::operator-(const Value& that) const {
+    Value v;
+    if (type != that.type) {
+        ALOGE("Can't operate on different value types, %d, %d", type, that.type);
+        return v;
+    }
+    if (type == STRING) {
+        ALOGE("Can't operate on string value type");
+        return v;
+    }
+
+    switch (type) {
+        case INT:
+            v.setInt(int_value - that.int_value);
+            break;
+        case LONG:
+            v.setLong(long_value - that.long_value);
+            break;
+        case FLOAT:
+            v.setFloat(float_value - that.float_value);
+            break;
+        case DOUBLE:
+            v.setDouble(double_value - that.double_value);
+            break;
+        default:
+            break;
+    }
+    return v;
+}
+
+Value& Value::operator=(const Value& that) {
+    type = that.type;
+    switch (type) {
+        case INT:
+            int_value = that.int_value;
+            break;
+        case LONG:
+            long_value = that.long_value;
+            break;
+        case FLOAT:
+            float_value = that.float_value;
+            break;
+        case DOUBLE:
+            double_value = that.double_value;
+            break;
+        case STRING:
+            str_value = that.str_value;
+            break;
+        default:
+            break;
+    }
+    return *this;
+}
+
+Value& Value::operator+=(const Value& that) {
+    if (type != that.type) {
+        ALOGE("Can't operate on different value types, %d, %d", type, that.type);
+        return *this;
+    }
+    if (type == STRING) {
+        ALOGE("Can't operate on string value type");
+        return *this;
+    }
+
+    switch (type) {
+        case INT:
+            int_value += that.int_value;
+            break;
+        case LONG:
+            long_value += that.long_value;
+            break;
+        case FLOAT:
+            float_value += that.float_value;
+            break;
+        case DOUBLE:
+            double_value += that.double_value;
+            break;
+        default:
+            break;
+    }
+    return *this;
+}
+
+double Value::getDouble() const {
+    switch (type) {
+        case INT:
+            return int_value;
+        case LONG:
+            return long_value;
+        case FLOAT:
+            return float_value;
+        case DOUBLE:
+            return double_value;
+        default:
+            return 0;
+    }
+}
+
 bool equalDimensions(const std::vector<Matcher>& dimension_a,
                      const std::vector<Matcher>& dimension_b) {
     bool eq = dimension_a.size() == dimension_b.size();
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 02c49b9..b1b885e 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -32,7 +32,7 @@
 const int32_t kClearLastBitDeco = 0x7f;
 const int32_t kClearAllPositionMatcherMask = 0xffff00ff;
 
-enum Type { UNKNOWN, INT, LONG, FLOAT, STRING };
+enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING };
 
 int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth);
 
@@ -212,7 +212,7 @@
  * the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are
  * equal. Nothing can beat the performance of this matching algorithm.
  *
- * TODO: ADD EXAMPLE HERE.
+ * TODO(b/110561213): ADD EXAMPLE HERE.
  */
 struct Matcher {
     Matcher(const Field& matcher, int32_t mask) : mMatcher(matcher), mMask(mask){};
@@ -283,6 +283,11 @@
         type = FLOAT;
     }
 
+    Value(double v) {
+        double_value = v;
+        type = DOUBLE;
+    }
+
     Value(const std::string& v) {
         str_value = v;
         type = STRING;
@@ -298,10 +303,21 @@
         type = LONG;
     }
 
+    void setFloat(float v) {
+        float_value = v;
+        type = FLOAT;
+    }
+
+    void setDouble(double v) {
+        double_value = v;
+        type = DOUBLE;
+    }
+
     union {
         int32_t int_value;
         int64_t long_value;
         float float_value;
+        double double_value;
     };
     std::string str_value;
 
@@ -313,12 +329,19 @@
         return type;
     }
 
+    double getDouble() const;
+
     Value(const Value& from);
 
     bool operator==(const Value& that) const;
     bool operator!=(const Value& that) const;
 
     bool operator<(const Value& that) const;
+    bool operator>(const Value& that) const;
+    bool operator>=(const Value& that) const;
+    Value operator-(const Value& that) const;
+    Value& operator+=(const Value& that);
+    Value& operator=(const Value& that);
 };
 
 /**
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 7103034..af8b3af 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -65,8 +65,6 @@
     for (const auto& value : values) {
         for (size_t i = 0; i < matcherFields.size(); ++i) {
             const auto& matcher = matcherFields[i];
-            // TODO: potential optimization here to break early because all fields are naturally
-            // sorted.
             if (value.mField.matches(matcher)) {
                 output->addValue(value);
                 output->mutableValue(num_matches)->mField.setTag(value.mField.getTag());
@@ -196,4 +194,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 1378ce5..ab0aa25 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -72,18 +72,20 @@
 #define STATS_DATA_DIR "/data/misc/stats-data"
 
 StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
+                                     const sp<StatsPullerManager>& pullerManager,
                                      const sp<AlarmMonitor>& anomalyAlarmMonitor,
                                      const sp<AlarmMonitor>& periodicAlarmMonitor,
                                      const int64_t timeBaseNs,
                                      const std::function<bool(const ConfigKey&)>& sendBroadcast)
     : mUidMap(uidMap),
+      mPullerManager(pullerManager),
       mAnomalyAlarmMonitor(anomalyAlarmMonitor),
       mPeriodicAlarmMonitor(periodicAlarmMonitor),
       mSendBroadcast(sendBroadcast),
       mTimeBaseNs(timeBaseNs),
       mLargestTimestampSeen(0),
       mLastTimestampSeen(0) {
-    mStatsPullerManager.ForceClearPullerCache();
+    mPullerManager->ForceClearPullerCache();
 }
 
 StatsLogProcessor::~StatsLogProcessor() {
@@ -238,7 +240,7 @@
 
     int64_t curTimeSec = getElapsedRealtimeSec();
     if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) {
-        mStatsPullerManager.ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC);
+        mPullerManager->ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC);
         mLastPullerCacheClearTimeSec = curTimeSec;
     }
 
@@ -266,8 +268,8 @@
         const int64_t timestampNs, const ConfigKey& key, const StatsdConfig& config) {
     VLOG("Updated configuration for key %s", key.ToString().c_str());
     sp<MetricsManager> newMetricsManager =
-        new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap,
-                           mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
+            new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap, mPullerManager,
+                               mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
     if (newMetricsManager->isConfigValid()) {
         mUidMap->OnConfigUpdated(key);
         if (newMetricsManager->shouldAddUidMapListener()) {
@@ -453,7 +455,7 @@
     mLastBroadcastTimes.erase(key);
 
     if (mMetricsManagers.empty()) {
-        mStatsPullerManager.ForceClearPullerCache();
+        mPullerManager->ForceClearPullerCache();
     }
 }
 
@@ -538,7 +540,7 @@
 
 void StatsLogProcessor::informPullAlarmFired(const int64_t timestampNs) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
-    mStatsPullerManager.OnAlarmFired(timestampNs);
+    mPullerManager->OnAlarmFired(timestampNs);
 }
 
 int64_t StatsLogProcessor::getLastReportTimeNs(const ConfigKey& key) {
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index b175b3c..05cf0c1 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -45,7 +45,8 @@
 
 class StatsLogProcessor : public ConfigListener {
 public:
-    StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+    StatsLogProcessor(const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerManager,
+                      const sp<AlarmMonitor>& anomalyAlarmMonitor,
                       const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor,
                       const int64_t timeBaseNs,
                       const std::function<bool(const ConfigKey&)>& sendBroadcast);
@@ -126,7 +127,7 @@
 
     sp<UidMap> mUidMap;  // Reference to the UidMap to lookup app name and version for each uid.
 
-    StatsPullerManager mStatsPullerManager;
+    sp<StatsPullerManager> mPullerManager;  // Reference to StatsPullerManager
 
     sp<AlarmMonitor> mAnomalyAlarmMonitor;
 
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 10c04f6..0c241fc 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -150,25 +150,26 @@
 
       }))  {
     mUidMap = new UidMap();
+    mPullerManager = new StatsPullerManager();
     StatsPuller::SetUidMap(mUidMap);
     mConfigManager = new ConfigManager();
-    mProcessor = new StatsLogProcessor(mUidMap, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
-                                       getElapsedRealtimeNs(), [this](const ConfigKey& key) {
-        sp<IStatsCompanionService> sc = getStatsCompanionService();
-        auto receiver = mConfigManager->GetConfigReceiver(key);
-        if (sc == nullptr) {
-            VLOG("Could not find StatsCompanionService");
-            return false;
-        } else if (receiver == nullptr) {
-            VLOG("Statscompanion could not find a broadcast receiver for %s",
-                 key.ToString().c_str());
-            return false;
-        } else {
-            sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
-            return true;
-        }
-    }
-    );
+    mProcessor = new StatsLogProcessor(
+            mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
+            getElapsedRealtimeNs(), [this](const ConfigKey& key) {
+                sp<IStatsCompanionService> sc = getStatsCompanionService();
+                auto receiver = mConfigManager->GetConfigReceiver(key);
+                if (sc == nullptr) {
+                    VLOG("Could not find StatsCompanionService");
+                    return false;
+                } else if (receiver == nullptr) {
+                    VLOG("Statscompanion could not find a broadcast receiver for %s",
+                         key.ToString().c_str());
+                    return false;
+                } else {
+                    sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
+                    return true;
+                }
+            });
 
     mConfigManager->AddListener(mProcessor);
 
@@ -402,7 +403,7 @@
     fprintf(out, "\n              *Note: If both UID and NAME are omitted then all configs will\n");
     fprintf(out, "\n                     be removed from memory and disk!\n");
     fprintf(out, "\n");
-    fprintf(out, "usage: adb shell cmd stats dump-report [UID] NAME [--proto]\n");
+    fprintf(out, "usage: adb shell cmd stats dump-report [UID] NAME [--include_current_bucket] [--proto]\n");
     fprintf(out, "  Dump all metric data for a configuration.\n");
     fprintf(out, "  UID           The uid of the configuration. It is only possible to pass\n");
     fprintf(out, "                the UID parameter on eng builds. If UID is omitted the\n");
@@ -439,7 +440,6 @@
     if (argCount == 2) {
         // Automatically pick the UID
         uid = IPCThreadState::self()->getCallingUid();
-        // TODO: What if this isn't a binder call? Should we fail?
         name.assign(args[1].c_str(), args[1].size());
         good = true;
     } else if (argCount == 3) {
@@ -492,7 +492,6 @@
             if (argCount == 3) {
                 // Automatically pick the UID
                 uid = IPCThreadState::self()->getCallingUid();
-                // TODO: What if this isn't a binder call? Should we fail?
                 name.assign(args[2].c_str(), args[2].size());
                 good = true;
             } else if (argCount == 4) {
@@ -568,16 +567,20 @@
         int argCount = args.size();
         bool good = false;
         bool proto = false;
+        bool includeCurrentBucket = false;
         int uid;
         string name;
         if (!std::strcmp("--proto", args[argCount-1].c_str())) {
             proto = true;
             argCount -= 1;
         }
+        if (!std::strcmp("--include_current_bucket", args[argCount-1].c_str())) {
+            includeCurrentBucket = true;
+            argCount -= 1;
+        }
         if (argCount == 2) {
             // Automatically pick the UID
             uid = IPCThreadState::self()->getCallingUid();
-            // TODO: What if this isn't a binder call? Should we fail?
             name.assign(args[1].c_str(), args[1].size());
             good = true;
         } else if (argCount == 3) {
@@ -602,8 +605,7 @@
         if (good) {
             vector<uint8_t> data;
             mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(),
-                                     false /* include_current_bucket*/, ADB_DUMP, &data);
-            // TODO: print the returned StatsLogReport to file instead of printing to logcat.
+                                     includeCurrentBucket, ADB_DUMP, &data);
             if (proto) {
                 for (size_t i = 0; i < data.size(); i ++) {
                     fprintf(out, "%c", data[i]);
@@ -711,7 +713,7 @@
 status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
     int s = atoi(args[1].c_str());
     vector<shared_ptr<LogEvent> > stats;
-    if (mStatsPullerManager.Pull(s, getElapsedRealtimeNs(), &stats)) {
+    if (mPullerManager->Pull(s, getElapsedRealtimeNs(), &stats)) {
         for (const auto& it : stats) {
             fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
         }
@@ -739,7 +741,7 @@
     VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
             ipc->getCallingPid(), ipc->getCallingUid());
     if (checkCallingPermission(String16(kPermissionDump))) {
-        int cleared = mStatsPullerManager.ForceClearPullerCache();
+        int cleared = mPullerManager->ForceClearPullerCache();
         fprintf(out, "Puller removed %d cached data!\n", cleared);
         return NO_ERROR;
     } else {
@@ -870,7 +872,7 @@
     }
     VLOG("StatsService::statsCompanionReady linking to statsCompanion.");
     IInterface::asBinder(statsCompanion)->linkToDeath(this);
-    mStatsPullerManager.SetStatsCompanionService(statsCompanion);
+    mPullerManager->SetStatsCompanionService(statsCompanion);
     mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion);
     mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion);
     SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion);
@@ -1014,7 +1016,7 @@
     mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
     mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
     SubscriberReporter::getInstance().setStatsCompanionService(nullptr);
-    mStatsPullerManager.SetStatsCompanionService(nullptr);
+    mPullerManager->SetStatsCompanionService(nullptr);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index b3a4776..ed90050 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -49,7 +49,6 @@
     virtual ~StatsService();
 
     /** The anomaly alarm registered with AlarmManager won't be updated by less than this. */
-    // TODO: Consider making this configurable. And choose a good number.
     const uint32_t MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS = 5;
 
     virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
@@ -246,9 +245,9 @@
     sp<UidMap> mUidMap;
 
     /**
-     * Fetches external metrics.
+     * Fetches external metrics
      */
-    StatsPullerManager mStatsPullerManager;
+    sp<StatsPullerManager> mPullerManager;
 
     /**
      * Tracks the configurations that have been passed to statsd.
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
index 78f0c2b..bc36dad 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
@@ -60,7 +60,7 @@
         ALOGW("Asked to add a 0-time alarm.");
         return;
     }
-    // TODO: Ensure that refractory period is respected.
+    // TODO(b/110563466): Ensure that refractory period is respected.
     VLOG("Adding alarm with time %u", alarm->timestampSec);
     mPq.push(alarm);
     if (mRegisteredAlarmTimeSec < 1 ||
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index f32efee..ee111cd 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -208,7 +208,8 @@
 }
 
 void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key) {
-    // TODO: Why receive timestamp? RefractoryPeriod should always be based on real time right now.
+    // TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on
+    // real time right now.
     if (isInRefractoryPeriod(timestampNs, key)) {
         VLOG("Skipping anomaly declaration since within refractory period");
         return;
@@ -216,7 +217,8 @@
     if (mAlert.has_refractory_period_secs()) {
         mRefractoryPeriodEndsSec[key] = ((timestampNs + NS_PER_SEC - 1) / NS_PER_SEC) // round up
                                         + mAlert.refractory_period_secs();
-        // TODO: If we had access to the bucket_size_millis, consider calling resetStorage()
+        // TODO(b/110563466): If we had access to the bucket_size_millis, consider
+        // calling resetStorage()
         // if (mAlert.refractory_period_secs() > mNumOfPastBuckets * bucketSizeNs) {resetStorage();}
     }
 
@@ -230,7 +232,7 @@
 
     StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
 
-    // TODO: This should also take in the const MetricDimensionKey& key?
+    // TODO(b/110564268): This should also take in the const MetricDimensionKey& key?
     android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(),
                                mConfigKey.GetId(), mAlert.id());
 }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index cdb72ab..d7a926f 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -48,7 +48,7 @@
     oneof pushed {
         // For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
         BleScanStateChanged ble_scan_state_changed = 2;
-        // 3 is available for use
+        ProcessStateChanged process_state_changed = 3;
         BleScanResultReceived ble_scan_result_received = 4;
         SensorStateChanged sensor_state_changed = 5;
         GpsScanStateChanged gps_scan_state_changed = 6;
@@ -59,7 +59,12 @@
         LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 11;
         MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12;
         WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13;
-        // 14 - 19 are available
+        ActivityManagerSleepStateChanged activity_manager_sleep_state_changed = 14;
+        MemoryFactorStateChanged memory_factor_state_changed = 15;
+        ExcessiveCpuUsageReported excessive_cpu_usage_reported = 16;
+        CachedKillReported cached_kill_reported = 17;
+        ProcessMemoryStatReported process_memory_stat_reported = 18;
+        // 19 is available
         BatterySaverModeStateChanged battery_saver_mode_state_changed = 20;
         DeviceIdleModeStateChanged device_idle_mode_state_changed = 21;
         DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22;
@@ -121,12 +126,11 @@
         ANROccurred anr_occurred = 79;
         WTFOccurred wtf_occurred = 80;
         LowMemReported low_mem_reported = 81;
-
-
+        GenericAtom generic_atom = 82;
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10022
+    // Next: 10024
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000;
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -150,6 +154,8 @@
         RemainingBatteryCapacity remaining_battery_capacity = 10019;
         FullBatteryCapacity full_battery_capacity = 10020;
         Temperature temperature = 10021;
+        BinderCalls binder_calls = 10022;
+        BinderCallsExceptions binder_calls_exceptions = 10023;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
@@ -209,7 +215,8 @@
 }
 
 /**
- * Logs that the state of a process state, as per the activity manager, has changed.
+ * Logs that the process state of the uid, as determined by ActivityManager
+ * (i.e. the highest process state of that uid's processes) has changed.
  *
  * Logged from:
  *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -222,6 +229,112 @@
 }
 
 /**
+ * Logs process state change of a process, as per the activity manager.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
+ */
+message ProcessStateChanged {
+    optional int32 uid = 1;
+    optional string process_name = 2;
+    optional string package_name = 3;
+    // TODO: remove this when validation is done
+    optional int64 version = 5;
+    // The state, from frameworks/base/core/proto/android/app/enums.proto.
+    optional android.app.ProcessStateEnum state = 4;
+}
+
+/**
+ * Logs when ActivityManagerService sleep state is changed.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+ */
+message ActivityManagerSleepStateChanged {
+    // TODO: import frameworks proto
+    enum State {
+        UNKNOWN = 0;
+        ASLEEP = 1;
+        AWAKE = 2;
+    }
+    optional State state = 1 [(stateFieldOption).option = EXCLUSIVE];
+}
+
+/**
+ * Logs when system memory state changes.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message MemoryFactorStateChanged {
+    // TODO: import frameworks proto
+    enum State {
+        MEMORY_UNKNOWN = 0;
+        NORMAL = 1;     // normal.
+        MODERATE = 2;   // moderate memory pressure.
+        LOW = 3;        // low memory.
+        CRITICAL = 4;   // critical memory.
+
+    }
+    optional State factor = 1 [(stateFieldOption).option = EXCLUSIVE];
+}
+
+/**
+ * Logs when app is using too much cpu, according to ActivityManagerService.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message ExcessiveCpuUsageReported {
+    optional int32 uid = 1;
+    optional string process_name = 2;
+    optional string package_name = 3;
+    // package version. TODO: remove this when validation is done
+    optional int64 version = 4;
+}
+
+/**
+ * Logs when a cached process is killed, along with its pss.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message CachedKillReported {
+    optional int32 uid = 1;
+    optional string process_name = 2;
+    optional string package_name = 3;
+    // TODO: remove this when validation is done
+    optional int64 version = 5;
+    optional int64 pss = 4;
+}
+
+/**
+ * Logs when memory stats of a process is reported.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
+ */
+message ProcessMemoryStatReported {
+    optional int32 uid = 1;
+    optional string process_name = 2;
+    optional string package_name = 3;
+    //TODO: remove this when validation is done
+    optional int64 version = 9;
+    optional int64 pss = 4;
+    optional int64 uss = 5;
+    optional int64 rss = 6;
+    enum Type {
+        ADD_PSS_INTERNAL_SINGLE = 0;
+        ADD_PSS_INTERNAL_ALL_MEM = 1;
+        ADD_PSS_INTERNAL_ALL_POLL = 2;
+        ADD_PSS_EXTERNAL = 3;
+        ADD_PSS_EXTERNAL_SLOW = 4;
+    }
+    optional Type type = 7;
+    optional int64 duration = 8;
+}
+
+/**
  * Logs that a process started, finished, crashed, or ANRed.
  *
  * Logged from:
@@ -1614,6 +1727,17 @@
     optional uint64 timestamp_millis = 1 [(stateFieldOption).option = EXCLUSIVE];
 }
 
+/**
+ * An atom for generic metrics logging. Available from Android Q.
+ */
+message GenericAtom {
+    // The uid of the application that sent this custom atom.
+    optional int32 uid = 1 [(is_uid) = true];
+
+    // An event_id indicates the type of event.
+    optional int32 event_id = 2;
+}
+
 //////////////////////////////////////////////////////////////////////
 // Pulled atoms below this line //
 //////////////////////////////////////////////////////////////////////
@@ -1975,3 +2099,71 @@
     // Temperature in tenths of a degree C.
     optional int32 temperature_dC = 3;
 }
+
+/**
+ * Pulls the statistics of calls to Binder.
+ *
+ * Binder stats are cumulative from boot unless somebody reset the data using
+ * > adb shell dumpsys binder_calls_stats --reset
+ *
+ * Next tag: 14
+ */
+message BinderCalls {
+    optional int32 uid = 1 [(is_uid) = true];
+    // Fully qualified class name of the API call.
+    //
+    // This is a system server class name.
+    //
+    // TODO(gaillard): figure out if binder call stats includes data from isolated uids, if a uid
+    // gets recycled and we have isolated uids, we might attribute the data incorrectly.
+    // TODO(gaillard): there is a high dimensions cardinality, figure out if we should drop the less
+    // commonly used APIs.
+    optional string service_class_name = 2;
+    // Method name of the API call. It can also be a transaction code if we cannot
+    // resolve it to a name. See Binder#getTransactionName.
+    //
+    // This is a system server method name.
+    optional string service_method_name = 3;
+    // Total number of API calls.
+    optional int64 call_count = 4;
+    // True if the screen was interactive PowerManager#isInteractive at the end of the call.
+    optional bool screen_interactive = 13;
+    // Total number of API calls we have data recorded for. If we collected data for all the calls,
+    // call_count will be equal to recorded_call_count.
+    //
+    // If recorded_call_count is different than call_count, it means data collection has been
+    // sampled. All the fields below will be sampled in this case.
+    optional int64 recorded_call_count = 12;
+    // Number of exceptions thrown by the API.
+    optional int64 recorded_exception_count = 5;
+    // Total latency of all API calls.
+    // Average can be computed using total_latency_micros / recorded_call_count.
+    optional int64 recorded_total_latency_micros = 6;
+    // Maximum latency of one API call.
+    optional int64 recorded_max_latency_micros = 7;
+    // Total CPU usage of all API calls.
+    // Average can be computed using total_cpu_micros / recorded_call_count.
+    // Total can be computed using total_cpu_micros / recorded_call_count * call_count.
+    optional int64 recorded_total_cpu_micros = 8;
+    // Maximum CPU usage of one API call.
+    optional int64 recorded_max_cpu_micros = 9;
+    // Maximum parcel reply size of one API call.
+    optional int64 recorded_max_reply_size_bytes = 10;
+    // Maximum parcel request size of one API call.
+    optional int64 recorded_max_request_size_bytes = 11;
+}
+
+/**
+ * Pulls the statistics of exceptions during calls to Binder.
+ *
+ * Binder stats are cumulative from boot unless somebody reset the data using
+ * > adb shell dumpsys binder_calls_stats --reset
+ */
+message BinderCallsExceptions {
+    // Exception class name, e.g. java.lang.IllegalArgumentException.
+    //
+    // This is an exception class name thrown by the system server.
+    optional string exception_class_name = 1;
+    // Total number of exceptions.
+    optional int64 exception_count = 2;
+}
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
index 0554483..e44351b 100644
--- a/cmds/statsd/src/external/Perfetto.cpp
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -113,7 +113,7 @@
         return false;
     }
 
-    std::string cfgProto = config.trace_config().SerializeAsString();
+    const std::string& cfgProto = config.trace_config();
     size_t bytesWritten = fwrite(cfgProto.data(), 1, cfgProto.size(), writePipeStream);
     fclose(writePipeStream);
     if (bytesWritten != cfgProto.size() || cfgProto.size() == 0) {
diff --git a/cmds/statsd/src/external/Perfprofd.cpp b/cmds/statsd/src/external/Perfprofd.cpp
index ff237e8..1678f10 100644
--- a/cmds/statsd/src/external/Perfprofd.cpp
+++ b/cmds/statsd/src/external/Perfprofd.cpp
@@ -55,17 +55,8 @@
       return false;
     }
 
-    // Add protobufs can't be described in AIDL, we need to re-serialize
-    // the config proto to send it.
-    std::vector<uint8_t> proto_serialized;
-    {
-      const auto& config_proto = config.perfprofd_config();
-      int size = config_proto.ByteSize();
-      proto_serialized.resize(size);
-      ::google::protobuf::uint8* target_ptr =
-          reinterpret_cast<::google::protobuf::uint8*>(proto_serialized.data());
-      config_proto.SerializeWithCachedSizesToArray(target_ptr);
-    }
+    auto* data = reinterpret_cast<const uint8_t*>(config.perfprofd_config().data());
+    std::vector<uint8_t> proto_serialized(data, data + config.perfprofd_config().size());
 
     // TODO: alert-id etc?
 
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
index 3741202..ae97d7a 100644
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
@@ -54,7 +54,7 @@
 ResourceHealthManagerPuller::ResourceHealthManagerPuller(int tagId) : StatsPuller(tagId) {
 }
 
-// TODO: add other health atoms (eg. Temperature).
+// TODO(b/110565992): add other health atoms (eg. Temperature).
 bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
     if (!getHealthHal()) {
         ALOGE("Health Hal not loaded");
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index b29e979..436a880 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -18,10 +18,10 @@
 #include "Log.h"
 
 #include "StatsPuller.h"
+#include "StatsPullerManager.h"
 #include "guardrail/StatsdStats.h"
 #include "puller_util.h"
 #include "stats_log_util.h"
-#include "StatsPullerManagerImpl.h"
 
 namespace android {
 namespace os {
@@ -35,7 +35,7 @@
 // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
 StatsPuller::StatsPuller(const int tagId)
     : mTagId(tagId) {
-    mCoolDownNs = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
+    mCoolDownNs = StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
     VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
 }
 
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index caac677..22cb2f5 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -37,6 +37,8 @@
 
     virtual ~StatsPuller() {}
 
+    // Pulls the data. The returned data will have elapsedTimeNs set as timeNs
+    // and will have wallClockTimeNs set as current wall clock time.
     bool Pull(const int64_t timeNs, std::vector<std::shared_ptr<LogEvent>>* data);
 
     // Clear cache immediately
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
new file mode 100644
index 0000000..e6e8455
--- /dev/null
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -0,0 +1,360 @@
+/*
+ * 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.
+ */
+
+#define DEBUG false
+#include "Log.h"
+
+#include <android/os/IStatsCompanionService.h>
+#include <cutils/log.h>
+#include <math.h>
+#include <stdint.h>
+#include <algorithm>
+#include "../StatsService.h"
+#include "../logd/LogEvent.h"
+#include "../stats_log_util.h"
+#include "../statscompanion_util.h"
+#include "ResourceHealthManagerPuller.h"
+#include "ResourceThermalManagerPuller.h"
+#include "StatsCompanionServicePuller.h"
+#include "StatsPullerManager.h"
+#include "SubsystemSleepStatePuller.h"
+#include "statslog.h"
+
+#include <iostream>
+
+using std::make_shared;
+using std::map;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+using std::list;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Values smaller than this may require to update the alarm.
+const int64_t NO_ALARM_UPDATE = INT64_MAX;
+
+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)}},
+        // wifi_bytes_transfer_by_fg_bg
+        {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
+         {{3, 4, 5, 6},
+          {2},
+          1 * NS_PER_SEC,
+          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)}},
+        // mobile_bytes_transfer_by_fg_bg
+        {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
+         {{3, 4, 5, 6},
+          {2},
+          1 * NS_PER_SEC,
+          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)}},
+        // kernel_wakelock
+        {android::util::KERNEL_WAKELOCK,
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+        // subsystem_sleep_state
+        {android::util::SUBSYSTEM_SLEEP_STATE,
+         {{}, {}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
+        // cpu_time_per_freq
+        {android::util::CPU_TIME_PER_FREQ,
+         {{3},
+          {2},
+          1 * NS_PER_SEC,
+          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)}},
+        // 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},
+          {2, 3},
+          1 * NS_PER_SEC,
+          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)}},
+        // cpu_cluster_time
+        // the throttling is 3sec, handled in
+        // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
+        {android::util::CPU_CLUSTER_TIME,
+         {{3},
+          {2},
+          1 * NS_PER_SEC,
+          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)}},
+        // modem_activity_info
+        {android::util::MODEM_ACTIVITY_INFO,
+         {{},
+          {},
+          1 * NS_PER_SEC,
+          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)}},
+        // system_elapsed_realtime
+        {android::util::SYSTEM_ELAPSED_REALTIME,
+         {{},
+          {},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
+        // system_uptime
+        {android::util::SYSTEM_UPTIME,
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+        // disk_space
+        {android::util::DISK_SPACE,
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_SPACE)}},
+        // remaining_battery_capacity
+        {android::util::REMAINING_BATTERY_CAPACITY,
+         {{},
+          {},
+          1 * NS_PER_SEC,
+          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)}},
+        // process_memory_state
+        {android::util::PROCESS_MEMORY_STATE,
+         {{4, 5, 6, 7, 8},
+          {2, 3},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
+        // temperature
+        {android::util::TEMPERATURE, {{}, {}, 1, new ResourceThermalManagerPuller()}},
+        // binder_calls
+        {android::util::BINDER_CALLS,
+         {{4, 5, 6, 8, 12},
+          {2, 3, 7, 9, 10, 11, 13},
+          1 * NS_PER_SEC,
+          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)}}
+        };
+
+StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
+}
+
+bool StatsPullerManager::Pull(const int tagId, const int64_t timeNs,
+                                  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);
+        VLOG("pulled %d items", (int)data->size());
+        return ret;
+    } else {
+        VLOG("Unknown tagId %d", tagId);
+        return false;  // Return early since we don't know what to pull.
+    }
+}
+
+bool StatsPullerManager::PullerForMatcherExists(int tagId) const {
+    return kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end();
+}
+
+void StatsPullerManager::updateAlarmLocked() {
+    if (mNextPullTimeNs == NO_ALARM_UPDATE) {
+        VLOG("No need to set alarms. Skipping");
+        return;
+    }
+
+    sp<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
+    if (statsCompanionServiceCopy != nullptr) {
+        statsCompanionServiceCopy->setPullingAlarm(mNextPullTimeNs / 1000000);
+    } else {
+        VLOG("StatsCompanionService not available. Alarm not set.");
+    }
+    return;
+}
+
+void StatsPullerManager::SetStatsCompanionService(
+        sp<IStatsCompanionService> statsCompanionService) {
+    AutoMutex _l(mLock);
+    sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
+    mStatsCompanionService = statsCompanionService;
+    for (const auto& pulledAtom : kAllPullAtomInfo) {
+        pulledAtom.second.puller->SetStatsCompanionService(statsCompanionService);
+    }
+    if (mStatsCompanionService != nullptr) {
+        updateAlarmLocked();
+    }
+}
+
+void StatsPullerManager::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
+                                              int64_t nextPullTimeNs, int64_t intervalNs) {
+    AutoMutex _l(mLock);
+    auto& receivers = mReceivers[tagId];
+    for (auto it = receivers.begin(); it != receivers.end(); it++) {
+        if (it->receiver == receiver) {
+            VLOG("Receiver already registered of %d", (int)receivers.size());
+            return;
+        }
+    }
+    ReceiverInfo receiverInfo;
+    receiverInfo.receiver = receiver;
+
+    // Round it to the nearest minutes. This is the limit of alarm manager.
+    // In practice, we should always have larger buckets.
+    int64_t roundedIntervalNs = intervalNs / NS_PER_SEC / 60 * NS_PER_SEC * 60;
+    // Scheduled pulling should be at least 1 min apart.
+    // This can be lower in cts tests, in which case we round it to 1 min.
+    if (roundedIntervalNs < 60 * (int64_t)NS_PER_SEC) {
+        roundedIntervalNs = 60 * (int64_t)NS_PER_SEC;
+    }
+
+    receiverInfo.intervalNs = roundedIntervalNs;
+    receiverInfo.nextPullTimeNs = nextPullTimeNs;
+    receivers.push_back(receiverInfo);
+
+    // There is only one alarm for all pulled events. So only set it to the smallest denom.
+    if (nextPullTimeNs < mNextPullTimeNs) {
+        VLOG("Updating next pull time %lld", (long long)mNextPullTimeNs);
+        mNextPullTimeNs = nextPullTimeNs;
+        updateAlarmLocked();
+    }
+    VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
+}
+
+void StatsPullerManager::UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
+    AutoMutex _l(mLock);
+    if (mReceivers.find(tagId) == mReceivers.end()) {
+        VLOG("Unknown pull code or no receivers: %d", tagId);
+        return;
+    }
+    auto& receivers = mReceivers.find(tagId)->second;
+    for (auto it = receivers.begin(); it != receivers.end(); it++) {
+        if (receiver == it->receiver) {
+            receivers.erase(it);
+            VLOG("Puller for tagId %d unregistered of %d", tagId, (int)receivers.size());
+            return;
+        }
+    }
+}
+
+void StatsPullerManager::OnAlarmFired(const int64_t currentTimeNs) {
+    AutoMutex _l(mLock);
+
+    int64_t minNextPullTimeNs = NO_ALARM_UPDATE;
+
+    vector<pair<int, vector<ReceiverInfo*>>> needToPull =
+            vector<pair<int, vector<ReceiverInfo*>>>();
+    for (auto& pair : mReceivers) {
+        vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
+        if (pair.second.size() != 0) {
+            for (ReceiverInfo& receiverInfo : pair.second) {
+                if (receiverInfo.nextPullTimeNs <= currentTimeNs) {
+                    receivers.push_back(&receiverInfo);
+                } else {
+                    if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) {
+                        minNextPullTimeNs = receiverInfo.nextPullTimeNs;
+                    }
+                }
+            }
+            if (receivers.size() > 0) {
+                needToPull.push_back(make_pair(pair.first, receivers));
+            }
+        }
+    }
+
+    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.");
+                }
+            }
+        }
+    }
+
+    VLOG("mNextPullTimeNs: %lld updated to %lld", (long long)mNextPullTimeNs,
+         (long long)minNextPullTimeNs);
+    mNextPullTimeNs = minNextPullTimeNs;
+    updateAlarmLocked();
+}
+
+int StatsPullerManager::ForceClearPullerCache() {
+    int totalCleared = 0;
+    for (const auto& pulledAtom : kAllPullAtomInfo) {
+        totalCleared += pulledAtom.second.puller->ForceClearCache();
+    }
+    return totalCleared;
+}
+
+int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
+    int totalCleared = 0;
+    for (const auto& pulledAtom : kAllPullAtomInfo) {
+        totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampNs);
+    }
+    return totalCleared;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 50ffe17..bbf5d9d 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -16,54 +16,95 @@
 
 #pragma once
 
-#include "StatsPullerManagerImpl.h"
+#include <android/os/IStatsCompanionService.h>
+#include <binder/IServiceManager.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+#include <list>
+#include <string>
+#include <unordered_map>
+#include <vector>
+#include "PullDataReceiver.h"
+#include "StatsPuller.h"
+#include "logd/LogEvent.h"
 
 namespace android {
 namespace os {
 namespace statsd {
 
-class StatsPullerManager {
- public:
-    virtual ~StatsPullerManager() {}
+typedef struct {
+    // The field numbers of the fields that need to be summed when merging
+    // isolated uid with host uid.
+    std::vector<int> additiveFields;
+    // The field numbers of the fields that can't be merged when merging
+    // data belong to isolated uid and host uid.
+    std::vector<int> nonAdditiveFields;
+    // How long should the puller wait before doing an actual pull again. Default
+    // 1 sec. Set this to 0 if this is handled elsewhere.
+    int64_t coolDownNs = 1 * NS_PER_SEC;
+    // The actual puller
+    sp<StatsPuller> puller;
+} PullAtomInfo;
 
+class StatsPullerManager : public virtual RefBase {
+public:
+    StatsPullerManager();
+
+    virtual ~StatsPullerManager() {
+    }
+
+    // Registers a receiver for tagId. It will be pulled on the nextPullTimeNs
+    // and then every intervalNs thereafter.
     virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
-                                  int64_t intervalNs) {
-        mPullerManager.RegisterReceiver(tagId, receiver, nextPullTimeNs, intervalNs);
-    };
+                                  int64_t intervalNs);
 
-    virtual void UnRegisterReceiver(int tagId, wp <PullDataReceiver> receiver) {
-        mPullerManager.UnRegisterReceiver(tagId, receiver);
-    };
+    // Stop listening on a tagId.
+    virtual void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
 
     // Verify if we know how to pull for this matcher
-    bool PullerForMatcherExists(int tagId) {
-        return mPullerManager.PullerForMatcherExists(tagId);
-    }
+    bool PullerForMatcherExists(int tagId) const;
 
-    void OnAlarmFired(const int64_t currentTimeNs) {
-        mPullerManager.OnAlarmFired(currentTimeNs);
-    }
+    void OnAlarmFired(const int64_t timeNs);
 
-    virtual bool Pull(const int tagId, const int64_t timesNs,
-                      vector<std::shared_ptr<LogEvent>>* data) {
-        return mPullerManager.Pull(tagId, timesNs, data);
-    }
+    // 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);
 
-    int ForceClearPullerCache() {
-        return mPullerManager.ForceClearPullerCache();
-    }
+    // Clear pull data cache immediately.
+    int ForceClearPullerCache();
 
-    void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
-        mPullerManager.SetStatsCompanionService(statsCompanionService);
-    }
+    // Clear pull data cache if it is beyond respective cool down time.
+    int ClearPullerCacheIfNecessary(int64_t timestampNs);
 
-    int ClearPullerCacheIfNecessary(int64_t timestampNs) {
-        return mPullerManager.ClearPullerCacheIfNecessary(timestampNs);
-    }
+    void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
 
- private:
-    StatsPullerManagerImpl
-        & mPullerManager = StatsPullerManagerImpl::GetInstance();
+    const static std::map<int, PullAtomInfo> kAllPullAtomInfo;
+
+private:
+    sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+
+    typedef struct {
+        int64_t nextPullTimeNs;
+        int64_t intervalNs;
+        wp<PullDataReceiver> receiver;
+    } ReceiverInfo;
+
+    // mapping from simple matcher tagId to receivers
+    std::map<int, std::list<ReceiverInfo>> mReceivers;
+
+    // locks for data receiver and StatsCompanionService changes
+    Mutex mLock;
+
+    void updateAlarmLocked();
+
+    int64_t mNextPullTimeNs;
+
+    FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
+    FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
+    FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+    FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
deleted file mode 100644
index c020f9c..0000000
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ /dev/null
@@ -1,352 +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.
- */
-
-#define DEBUG false
-#include "Log.h"
-
-#include <android/os/IStatsCompanionService.h>
-#include <cutils/log.h>
-#include <math.h>
-#include <stdint.h>
-#include <algorithm>
-#include "../StatsService.h"
-#include "../logd/LogEvent.h"
-#include "../stats_log_util.h"
-#include "../statscompanion_util.h"
-#include "ResourceHealthManagerPuller.h"
-#include "ResourceThermalManagerPuller.h"
-#include "StatsCompanionServicePuller.h"
-#include "StatsPullerManagerImpl.h"
-#include "SubsystemSleepStatePuller.h"
-#include "statslog.h"
-
-#include <iostream>
-
-using std::make_shared;
-using std::map;
-using std::shared_ptr;
-using std::string;
-using std::vector;
-using std::list;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Values smaller than this may require to update the alarm.
-const int64_t NO_ALARM_UPDATE = INT64_MAX;
-
-const std::map<int, PullAtomInfo> StatsPullerManagerImpl::kAllPullAtomInfo = {
-        // wifi_bytes_transfer
-        {android::util::WIFI_BYTES_TRANSFER,
-         {{2, 3, 4, 5},
-          {},
-          1 * NS_PER_SEC,
-          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},
-          {2},
-          1 * NS_PER_SEC,
-          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)}},
-        // mobile_bytes_transfer_by_fg_bg
-        {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
-         {{3, 4, 5, 6},
-          {2},
-          1 * NS_PER_SEC,
-          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)}},
-        // kernel_wakelock
-        {android::util::KERNEL_WAKELOCK,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
-        // subsystem_sleep_state
-        {android::util::SUBSYSTEM_SLEEP_STATE,
-         {{}, {}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
-        // cpu_time_per_freq
-        {android::util::CPU_TIME_PER_FREQ,
-         {{3},
-          {2},
-          1 * NS_PER_SEC,
-          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)}},
-        // 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},
-          {2, 3},
-          1 * NS_PER_SEC,
-          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)}},
-        // cpu_cluster_time
-        // the throttling is 3sec, handled in
-        // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
-        {android::util::CPU_CLUSTER_TIME,
-         {{3},
-          {2},
-          1 * NS_PER_SEC,
-          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)}},
-        // modem_activity_info
-        {android::util::MODEM_ACTIVITY_INFO,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          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)}},
-        // system_elapsed_realtime
-        {android::util::SYSTEM_ELAPSED_REALTIME,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
-        // system_uptime
-        {android::util::SYSTEM_UPTIME,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
-        // disk_space
-        {android::util::DISK_SPACE,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_SPACE)}},
-        // remaining_battery_capacity
-        {android::util::REMAINING_BATTERY_CAPACITY,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          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)}},
-        // process_memory_state
-        {android::util::PROCESS_MEMORY_STATE,
-         {{4, 5, 6, 7, 8},
-          {2, 3},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
-        // temperature
-        {android::util::TEMPERATURE, {{}, {}, 1, new ResourceThermalManagerPuller()}}};
-
-StatsPullerManagerImpl::StatsPullerManagerImpl() : mNextPullTimeNs(NO_ALARM_UPDATE) {
-}
-
-bool StatsPullerManagerImpl::Pull(const int tagId, const int64_t timeNs,
-                                  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);
-        VLOG("pulled %d items", (int)data->size());
-        return ret;
-    } else {
-        VLOG("Unknown tagId %d", tagId);
-        return false;  // Return early since we don't know what to pull.
-    }
-}
-
-StatsPullerManagerImpl& StatsPullerManagerImpl::GetInstance() {
-    static StatsPullerManagerImpl instance;
-    return instance;
-}
-
-bool StatsPullerManagerImpl::PullerForMatcherExists(int tagId) const {
-    return kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end();
-}
-
-void StatsPullerManagerImpl::updateAlarmLocked() {
-    if (mNextPullTimeNs == NO_ALARM_UPDATE) {
-        VLOG("No need to set alarms. Skipping");
-        return;
-    }
-
-    sp<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
-    if (statsCompanionServiceCopy != nullptr) {
-        statsCompanionServiceCopy->setPullingAlarm(mNextPullTimeNs / 1000000);
-    } else {
-        VLOG("StatsCompanionService not available. Alarm not set.");
-    }
-    return;
-}
-
-void StatsPullerManagerImpl::SetStatsCompanionService(
-        sp<IStatsCompanionService> statsCompanionService) {
-    AutoMutex _l(mLock);
-    sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
-    mStatsCompanionService = statsCompanionService;
-    for (const auto& pulledAtom : kAllPullAtomInfo) {
-        pulledAtom.second.puller->SetStatsCompanionService(statsCompanionService);
-    }
-    if (mStatsCompanionService != nullptr) {
-        updateAlarmLocked();
-    }
-}
-
-void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
-                                              int64_t nextPullTimeNs, int64_t intervalNs) {
-    AutoMutex _l(mLock);
-    auto& receivers = mReceivers[tagId];
-    for (auto it = receivers.begin(); it != receivers.end(); it++) {
-        if (it->receiver == receiver) {
-            VLOG("Receiver already registered of %d", (int)receivers.size());
-            return;
-        }
-    }
-    ReceiverInfo receiverInfo;
-    receiverInfo.receiver = receiver;
-
-    // Round it to the nearest minutes. This is the limit of alarm manager.
-    // In practice, we should always have larger buckets.
-    int64_t roundedIntervalNs = intervalNs / NS_PER_SEC / 60 * NS_PER_SEC * 60;
-    // Scheduled pulling should be at least 1 min apart.
-    // This can be lower in cts tests, in which case we round it to 1 min.
-    if (roundedIntervalNs < 60 * (int64_t)NS_PER_SEC) {
-        roundedIntervalNs = 60 * (int64_t)NS_PER_SEC;
-    }
-
-    receiverInfo.intervalNs = roundedIntervalNs;
-    receiverInfo.nextPullTimeNs = nextPullTimeNs;
-    receivers.push_back(receiverInfo);
-
-    // There is only one alarm for all pulled events. So only set it to the smallest denom.
-    if (nextPullTimeNs < mNextPullTimeNs) {
-        VLOG("Updating next pull time %lld", (long long)mNextPullTimeNs);
-        mNextPullTimeNs = nextPullTimeNs;
-        updateAlarmLocked();
-    }
-    VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
-}
-
-void StatsPullerManagerImpl::UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
-    AutoMutex _l(mLock);
-    if (mReceivers.find(tagId) == mReceivers.end()) {
-        VLOG("Unknown pull code or no receivers: %d", tagId);
-        return;
-    }
-    auto& receivers = mReceivers.find(tagId)->second;
-    for (auto it = receivers.begin(); it != receivers.end(); it++) {
-        if (receiver == it->receiver) {
-            receivers.erase(it);
-            VLOG("Puller for tagId %d unregistered of %d", tagId, (int)receivers.size());
-            return;
-        }
-    }
-}
-
-void StatsPullerManagerImpl::OnAlarmFired(const int64_t currentTimeNs) {
-    AutoMutex _l(mLock);
-
-    int64_t minNextPullTimeNs = NO_ALARM_UPDATE;
-
-    vector<pair<int, vector<ReceiverInfo*>>> needToPull =
-            vector<pair<int, vector<ReceiverInfo*>>>();
-    for (auto& pair : mReceivers) {
-        vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
-        if (pair.second.size() != 0) {
-            for (ReceiverInfo& receiverInfo : pair.second) {
-                if (receiverInfo.nextPullTimeNs <= currentTimeNs) {
-                    receivers.push_back(&receiverInfo);
-                } else {
-                    if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) {
-                        minNextPullTimeNs = receiverInfo.nextPullTimeNs;
-                    }
-                }
-            }
-            if (receivers.size() > 0) {
-                needToPull.push_back(make_pair(pair.first, receivers));
-            }
-        }
-    }
-
-    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.");
-                }
-            }
-        }
-    }
-
-    VLOG("mNextPullTimeNs: %lld updated to %lld", (long long)mNextPullTimeNs,
-         (long long)minNextPullTimeNs);
-    mNextPullTimeNs = minNextPullTimeNs;
-    updateAlarmLocked();
-}
-
-int StatsPullerManagerImpl::ForceClearPullerCache() {
-    int totalCleared = 0;
-    for (const auto& pulledAtom : kAllPullAtomInfo) {
-        totalCleared += pulledAtom.second.puller->ForceClearCache();
-    }
-    return totalCleared;
-}
-
-int StatsPullerManagerImpl::ClearPullerCacheIfNecessary(int64_t timestampNs) {
-    int totalCleared = 0;
-    for (const auto& pulledAtom : kAllPullAtomInfo) {
-        totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampNs);
-    }
-    return totalCleared;
-}
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
deleted file mode 100644
index 56d04b4..0000000
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ /dev/null
@@ -1,102 +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.
- */
-
-#pragma once
-
-#include <android/os/IStatsCompanionService.h>
-#include <binder/IServiceManager.h>
-#include <utils/RefBase.h>
-#include <utils/threads.h>
-#include <string>
-#include <unordered_map>
-#include <vector>
-#include <list>
-#include "PullDataReceiver.h"
-#include "StatsPuller.h"
-#include "logd/LogEvent.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-typedef struct {
-  // The field numbers of the fields that need to be summed when merging
-  // isolated uid with host uid.
-  std::vector<int> additiveFields;
-  // The field numbers of the fields that can't be merged when merging
-  // data belong to isolated uid and host uid.
-  std::vector<int> nonAdditiveFields;
-  // How long should the puller wait before doing an actual pull again. Default
-  // 1 sec. Set this to 0 if this is handled elsewhere.
-  int64_t coolDownNs = 1 * NS_PER_SEC;
-  // The actual puller
-  sp<StatsPuller> puller;
-} PullAtomInfo;
-
-class StatsPullerManagerImpl : public virtual RefBase {
-public:
-    static StatsPullerManagerImpl& GetInstance();
-
-    void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
-                          int64_t intervalNs);
-
-    void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
-
-    // Verify if we know how to pull for this matcher
-    bool PullerForMatcherExists(int tagId) const;
-
-    void OnAlarmFired(const int64_t timeNs);
-
-    bool Pull(const int tagId, const int64_t timeNs, vector<std::shared_ptr<LogEvent>>* data);
-
-    int ForceClearPullerCache();
-
-    int ClearPullerCacheIfNecessary(int64_t timestampNs);
-
-    void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
-
-    const static std::map<int, PullAtomInfo> kAllPullAtomInfo;
-
-   private:
-    StatsPullerManagerImpl();
-
-    sp<IStatsCompanionService> mStatsCompanionService = nullptr;
-
-    typedef struct {
-        int64_t nextPullTimeNs;
-        int64_t intervalNs;
-        wp<PullDataReceiver> receiver;
-    } ReceiverInfo;
-
-    // mapping from simple matcher tagId to receivers
-    std::map<int, std::list<ReceiverInfo>> mReceivers;
-
-    // locks for data receiver and StatsCompanionService changes
-    Mutex mLock;
-
-    void updateAlarmLocked();
-
-    int64_t mNextPullTimeNs;
-
-    FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
-    FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
-    FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
-    FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
-};
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index 57fe10e..ea7fa97 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -17,7 +17,7 @@
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
 
-#include "StatsPullerManagerImpl.h"
+#include "StatsPullerManager.h"
 #include "puller_util.h"
 #include "statslog.h"
 
@@ -107,8 +107,8 @@
  */
 void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
                                 int tagId) {
-    if (StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) ==
-        StatsPullerManagerImpl::kAllPullAtomInfo.end()) {
+    if (StatsPullerManager::kAllPullAtomInfo.find(tagId) ==
+        StatsPullerManager::kAllPullAtomInfo.end()) {
         VLOG("Unknown pull atom id %d", tagId);
         return;
     }
@@ -121,9 +121,9 @@
         uidField = it->second;  // uidField is the field number in proto,
     }
     const vector<int>& additiveFields =
-            StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.additiveFields;
+            StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields;
     const vector<int>& nonAdditiveFields =
-            StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields;
+            StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields;
 
     // map of host uid to their position in the original vector
     map<int, vector<int>> hostPosition;
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 764366f..a955511 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -100,10 +100,10 @@
 const int FIELD_ID_UID_MAP_DELETED_APPS = 4;
 
 const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
+        {android::util::BINDER_CALLS, {6000, 10000}},
         {android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
 };
 
-// TODO: add stats for pulled atoms.
 StatsdStats::StatsdStats() {
     mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1);
     mStartTimeSec = getWallClockSec();
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 74541d3..9fb2cd8 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -86,7 +86,6 @@
     static StatsdStats& getInstance();
     ~StatsdStats(){};
 
-    // TODO: set different limit if the device is low ram.
     const static int kDimensionKeySizeSoftLimit = 500;
     const static int kDimensionKeySizeHardLimit = 800;
 
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 4e4f146..04d34f3 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -273,7 +273,7 @@
 }
 
 int64_t LogEvent::GetLong(size_t key, status_t* err) const {
-    // TODO: encapsulate the magical operations all in Field struct as a static function.
+    // TODO(b/110561208): encapsulate the magical operations in Field struct as static functions
     int field = getSimpleField(key);
     for (const auto& value : mValues) {
         if (value.mField.getField() == field) {
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 6aa68da..2f15d0f 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -82,7 +82,6 @@
     if (err != NO_ERROR) {
         return err;
     }
-    // TODO: Do we need to tweak thread priority?
     err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
     if (err != NO_ERROR) {
         pthread_attr_destroy(&attr);
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
index 4f30a04..88ab4e6 100644
--- a/cmds/statsd/src/matchers/LogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -86,8 +86,6 @@
     // The collection of the event tag ids that this LogMatchingTracker cares. So we can quickly
     // return kNotMatched when we receive an event with an id not in the list. This is especially
     // useful when we have a complex CombinationLogMatcherTracker.
-    // TODO: Consider use an array instead of stl set. In reality, the number of the tag ids a
-    // LogMatchingTracker cares is only a few.
     std::set<int> mAtomIds;
 };
 
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 43f53e0..bd94800 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -66,9 +66,8 @@
 CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard,
-                                         const int64_t startTimeNs)
-    : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
-    // TODO: evaluate initial conditions. and set mConditionMet.
+                                         const int64_t timeBaseNs, const int64_t startTimeNs)
+    : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard) {
     if (metric.has_bucket()) {
         mBucketSizeNs =
                 TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -101,6 +100,10 @@
 
     mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
 
+    flushIfNeededLocked(startTimeNs);
+    // Adjust start for partial bucket
+    mCurrentBucketStartTimeNs = startTimeNs;
+
     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mTimeBaseNs);
 }
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 139c083..39d4ae2 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -40,10 +40,9 @@
 
 class CountMetricProducer : public MetricProducer {
 public:
-    // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
     CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric,
                         const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int64_t startTimeNs);
+                        const int64_t timeBaseNs, const int64_t startTimeNs);
 
     virtual ~CountMetricProducer();
 
@@ -80,7 +79,6 @@
 
     void flushCurrentBucketLocked(const int64_t& eventTimeNs) override;
 
-    // TODO: Add a lock to mPastBuckets.
     std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets;
 
     // The current bucket (may be a partial bucket).
@@ -100,6 +98,7 @@
     FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
     FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade);
     FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket);
+    FRIEND_TEST(CountMetricProducerTest, TestFirstBucket);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 62237bc..9d9e5be 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -68,17 +68,14 @@
                                                const bool nesting,
                                                const sp<ConditionWizard>& wizard,
                                                const FieldMatcher& internalDimensions,
-                                               const int64_t startTimeNs)
-    : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
+                                               const int64_t timeBaseNs, const int64_t startTimeNs)
+    : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
       mAggregationType(metric.aggregation_type()),
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
       mStopAllIndex(stopAllIndex),
       mNested(nesting),
       mContainANYPositionInInternalDimensions(false) {
-    // TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
-    // them in the base class, because the proto generated CountMetric, and DurationMetric are
-    // not related. Maybe we should add a template in the future??
     if (metric.has_bucket()) {
         mBucketSizeNs =
                 TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -131,6 +128,9 @@
                                                mMetric2ConditionLinks.begin()->conditionFields);
         }
     }
+    flushIfNeededLocked(startTimeNs);
+    // Adjust start for partial bucket
+    mCurrentBucketStartTimeNs = startTimeNs;
     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mTimeBaseNs);
 }
@@ -434,8 +434,6 @@
     VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
     mCondition = conditionMet;
     flushIfNeededLocked(eventTime);
-    // TODO: need to populate the condition change time from the event which triggers the condition
-    // change, instead of using current time.
     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
         for (auto& pair : whatIt.second) {
             pair.second->onConditionChanged(conditionMet, eventTime);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 88e455a..12addb8 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -42,7 +42,7 @@
                            const int conditionIndex, const size_t startIndex,
                            const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
                            const sp<ConditionWizard>& wizard,
-                           const FieldMatcher& internalDimensions, const int64_t startTimeNs);
+                           const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs);
 
     virtual ~DurationMetricProducer();
 
@@ -115,7 +115,6 @@
     ConditionState mUnSlicedPartCondition;
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
-    // TODO: Add a lock to mPastBuckets.
     std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
 
     // The duration trackers in the current bucket.
@@ -142,6 +141,7 @@
     FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade);
     FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket);
     FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates);
+    FRIEND_TEST(DurationMetricTrackerTest, TestFirstBucket);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 62d1105..7f7aa37 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -33,7 +33,6 @@
 
 class EventMetricProducer : public MetricProducer {
 public:
-    // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
     EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric,
                         const int conditionIndex, const sp<ConditionWizard>& wizard,
                         const int64_t startTimeNs);
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index aabd361..fbe0b21 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -72,10 +72,11 @@
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
                                          const int64_t timeBaseNs, const int64_t startTimeNs,
-                                         shared_ptr<StatsPullerManager> statsPullerManager)
+                                         const sp<StatsPullerManager>& pullerManager)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
-      mStatsPullerManager(statsPullerManager),
+      mPullerManager(pullerManager),
       mPullTagId(pullTagId),
+      mIsPulled(pullTagId != -1),
       mMinBucketSizeNs(metric.min_bucket_size_nanos()),
       mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
                                           StatsdStats::kAtomDimensionKeySizeLimitMap.end()
@@ -101,7 +102,6 @@
         translateFieldMatcher(metric.gauge_fields_filter().fields(), &mFieldMatchers);
     }
 
-    // TODO: use UidMap if uid->pkg_name is required
     if (metric.has_dimensions_in_what()) {
         translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
         mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
@@ -126,9 +126,15 @@
 
     flushIfNeededLocked(startTimeNs);
     // Kicks off the puller immediately.
-    if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
-        mStatsPullerManager->RegisterReceiver(
-                mPullTagId, this, getCurrentBucketEndTimeNs(), mBucketSizeNs);
+    if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+        mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
+                                         mBucketSizeNs);
+    }
+
+    // Adjust start for partial bucket
+    mCurrentBucketStartTimeNs = startTimeNs;
+    if (mIsPulled) {
+        pullLocked(startTimeNs);
     }
 
     VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
@@ -136,19 +142,10 @@
          mConditionSliced);
 }
 
-// for testing
-GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
-                                         const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard, const int pullTagId,
-                                         const int64_t timeBaseNs, const int64_t startTimeNs)
-    : GaugeMetricProducer(key, metric, conditionIndex, wizard, pullTagId, timeBaseNs, startTimeNs,
-                          make_shared<StatsPullerManager>()) {
-}
-
 GaugeMetricProducer::~GaugeMetricProducer() {
     VLOG("~GaugeMetricProducer() called");
-    if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
-        mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
+    if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+        mPullerManager->UnRegisterReceiver(mPullTagId, this);
     }
 }
 
@@ -308,7 +305,6 @@
     protoOutput->end(protoToken);
 
     mPastBuckets.clear();
-    // TODO: Clear mDimensionKeyMap once the report is dumped.
 }
 
 void GaugeMetricProducer::pullLocked(const int64_t timestampNs) {
@@ -334,13 +330,11 @@
     if (!triggerPuller) {
         return;
     }
-
     vector<std::shared_ptr<LogEvent>> allData;
-    if (!mStatsPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
-        ALOGE("Gauge Stats puller failed for tag: %d", mPullTagId);
+    if (!mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
+        ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
         return;
     }
-
     for (const auto& data : allData) {
         onMatchedLogEventLocked(0, *data);
     }
@@ -351,8 +345,7 @@
     VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId);
     flushIfNeededLocked(eventTimeNs);
     mCondition = conditionMet;
-
-    if (mPullTagId != -1) {
+    if (mIsPulled) {
         pullLocked(eventTimeNs);
     }  // else: Push mode. No need to proactively pull the gauge data.
 }
@@ -365,7 +358,7 @@
     // If the condition is sliced, mCondition is true if any of the dimensions is true. And we will
     // pull for every dimension.
     mCondition = overallCondition;
-    if (mPullTagId != -1) {
+    if (mIsPulled) {
         pullLocked(eventTimeNs);
     }  // else: Push mode. No need to proactively pull the gauge data.
 }
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index c74f792..cc65440 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -33,7 +33,7 @@
 namespace statsd {
 
 struct GaugeAtom {
-    GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs, int wallClockNs)
+    GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs, int64_t wallClockNs)
         : mFields(fields), mElapsedTimestamps(elapsedTimeNs), mWallClockTimestampNs(wallClockNs) {
     }
     std::shared_ptr<vector<FieldValue>> mFields;
@@ -58,7 +58,8 @@
 public:
     GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
                         const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs);
+                        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
+                        const sp<StatsPullerManager>& pullerManager);
 
     virtual ~GaugeMetricProducer();
 
@@ -76,7 +77,7 @@
         }
         flushCurrentBucketLocked(eventTimeNs);
         mCurrentBucketStartTimeNs = eventTimeNs;
-        if (mPullTagId != -1) {
+        if (mIsPulled) {
             pullLocked(eventTimeNs);
         }
     };
@@ -94,13 +95,6 @@
                             android::util::ProtoOutputStream* protoOutput) override;
     void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
 
-    // for testing
-    GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
-                        const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int pullTagId,
-                        const int64_t timeBaseNs, const int64_t startTimeNs,
-                        std::shared_ptr<StatsPullerManager> statsPullerManager);
-
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
 
@@ -123,12 +117,14 @@
 
     int mTagId;
 
-    std::shared_ptr<StatsPullerManager> mStatsPullerManager;
+    sp<StatsPullerManager> mPullerManager;
     // tagId for pulled data. -1 if this is not pulled
     const int mPullTagId;
 
+    // if this is pulled metric
+    const bool mIsPulled;
+
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
-    // TODO: Add a lock to mPastBuckets.
     std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
 
     // The current partial bucket.
@@ -166,12 +162,13 @@
 
     const size_t mGaugeAtomsPerDimensionLimit;
 
-    FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition);
-    FRIEND_TEST(GaugeMetricProducerTest, TestWithSlicedCondition);
-    FRIEND_TEST(GaugeMetricProducerTest, TestNoCondition);
+    FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition);
+    FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition);
+    FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition);
     FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade);
     FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade);
-    FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
+    FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection);
+    FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 4fac0e1..0e5ef4d 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -56,10 +56,12 @@
 
 MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
                                const int64_t timeBaseNs, const int64_t currentTimeNs,
-                               const sp<UidMap> &uidMap,
+                               const sp<UidMap>& uidMap,
+                               const sp<StatsPullerManager>& pullerManager,
                                const sp<AlarmMonitor>& anomalyAlarmMonitor,
                                const sp<AlarmMonitor>& periodicAlarmMonitor)
-    : mConfigKey(key), mUidMap(uidMap),
+    : mConfigKey(key),
+      mUidMap(uidMap),
       mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1),
       mTtlEndNs(-1),
       mLastReportTimeNs(currentTimeNs),
@@ -67,12 +69,11 @@
     // Init the ttl end timestamp.
     refreshTtl(timeBaseNs);
 
-    mConfigValid =
-            initStatsdConfig(key, config, *uidMap, anomalyAlarmMonitor, periodicAlarmMonitor,
-                             timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers,
-                             mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers,
-                             mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap,
-                             mTrackerToConditionMap, mNoReportMetricIds);
+    mConfigValid = initStatsdConfig(
+            key, config, *uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
+            mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
+            mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds);
 
     mHashStringsInReport = config.hash_strings_in_metric_report();
 
@@ -237,7 +238,6 @@
 
     if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) {
         // Check that app breadcrumb reported fields are valid.
-        // TODO: Find a way to make these checks easier to maintain.
         status_t err = NO_ERROR;
 
         // Uid is 3rd from last field and must match the caller's uid,
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 6f4db48..dfbb69f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "external/StatsPullerManager.h"
 #include "anomaly/AlarmMonitor.h"
 #include "anomaly/AlarmTracker.h"
 #include "anomaly/AnomalyTracker.h"
@@ -36,9 +37,10 @@
 // A MetricsManager is responsible for managing metrics from one single config source.
 class MetricsManager : public PackageInfoListener {
 public:
-    MetricsManager(const ConfigKey& configKey, const StatsdConfig& config,
-                   const int64_t timeBaseNs, const int64_t currentTimeNs,
-                   const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+    MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const int64_t timeBaseNs,
+                   const int64_t currentTimeNs, const sp<UidMap>& uidMap,
+                   const sp<StatsPullerManager>& pullerManager,
+                   const sp<AlarmMonitor>& anomalyAlarmMonitor,
                    const sp<AlarmMonitor>& periodicAlarmMonitor);
 
     virtual ~MetricsManager();
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 41e55cb..16447e8 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define DEBUG false  // STOPSHIP if true
+#define DEBUG true  // STOPSHIP if true
 #include "Log.h"
 
 #include "ValueMetricProducer.h"
@@ -27,7 +27,7 @@
 
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
-using android::util::FIELD_TYPE_FLOAT;
+using android::util::FIELD_TYPE_DOUBLE;
 using android::util::FIELD_TYPE_INT32;
 using android::util::FIELD_TYPE_INT64;
 using android::util::FIELD_TYPE_MESSAGE;
@@ -64,7 +64,8 @@
 const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
 const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
 // for ValueBucketInfo
-const int FIELD_ID_VALUE = 3;
+const int FIELD_ID_VALUE_LONG = 7;
+const int FIELD_ID_VALUE_DOUBLE = 8;
 const int FIELD_ID_BUCKET_NUM = 4;
 const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
 const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
@@ -73,12 +74,13 @@
 ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
-                                         const int64_t timeBaseNs, const int64_t startTimestampNs,
-                                         shared_ptr<StatsPullerManager> statsPullerManager)
+                                         const int64_t timeBaseNs, const int64_t startTimeNs,
+                                         const sp<StatsPullerManager>& pullerManager)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
+      mPullerManager(pullerManager),
       mValueField(metric.value_field()),
-      mStatsPullerManager(statsPullerManager),
       mPullTagId(pullTagId),
+      mIsPulled(pullTagId != -1),
       mMinBucketSizeNs(metric.min_bucket_size_nanos()),
       mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
                                           StatsdStats::kAtomDimensionKeySizeLimitMap.end()
@@ -88,8 +90,9 @@
                                           StatsdStats::kAtomDimensionKeySizeLimitMap.end()
                                   ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second
                                   : StatsdStats::kDimensionKeySizeHardLimit),
-      mUseAbsoluteValueOnReset(metric.use_absolute_value_on_reset()) {
-    // TODO: valuemetric for pushed events may need unlimited bucket length
+      mUseAbsoluteValueOnReset(metric.use_absolute_value_on_reset()),
+      mAggregationType(metric.aggregation_type()),
+      mValueType(metric.aggregation_type() == ValueMetric::AVG ? DOUBLE : LONG) {
     int64_t bucketSizeMills = 0;
     if (metric.has_bucket()) {
         bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
@@ -124,30 +127,28 @@
     mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
             HasPositionALL(metric.dimensions_in_condition());
 
+    flushIfNeededLocked(startTimeNs);
     // Kicks off the puller immediately.
-    flushIfNeededLocked(startTimestampNs);
-    if (mPullTagId != -1) {
-        mStatsPullerManager->RegisterReceiver(
-            mPullTagId, this, mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
+    if (mIsPulled) {
+        mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
+                                         mBucketSizeNs);
     }
 
+    // TODO: Only do this for partial buckets like first bucket. All other buckets should use
+    // flushIfNeeded to adjust start and end to bucket boundaries.
+    // Adjust start for partial bucket
+    mCurrentBucketStartTimeNs = startTimeNs;
+    if (mIsPulled) {
+        pullLocked(startTimeNs);
+    }
     VLOG("value metric %lld created. bucket size %lld start_time: %lld",
         (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs);
 }
 
-// for testing
-ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
-                                         const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard, const int pullTagId,
-                                         const int64_t timeBaseNs, const int64_t startTimeNs)
-    : ValueMetricProducer(key, metric, conditionIndex, wizard, pullTagId, timeBaseNs, startTimeNs,
-                          make_shared<StatsPullerManager>()) {
-}
-
 ValueMetricProducer::~ValueMetricProducer() {
     VLOG("~ValueMetricProducer() called");
-    if (mPullTagId != -1) {
-        mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
+    if (mIsPulled) {
+        mPullerManager->UnRegisterReceiver(mPullTagId, this);
     }
 }
 
@@ -255,11 +256,15 @@
                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
                                    (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
             }
-
-            protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE, (long long)bucket.mValue);
+            if (mValueType == LONG) {
+                protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_LONG,
+                                   (long long)bucket.mValueLong);
+            } else {
+                protoOutput->write(FIELD_TYPE_DOUBLE | FIELD_ID_VALUE_DOUBLE, bucket.mValueDouble);
+            }
             protoOutput->end(bucketInfoToken);
-            VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
-                 (long long)bucket.mBucketEndNs, (long long)bucket.mValue);
+            VLOG("\t bucket [%lld - %lld] count: %lld, %.2f", (long long)bucket.mBucketStartNs,
+                 (long long)bucket.mBucketEndNs, (long long)bucket.mValueLong, bucket.mValueDouble);
         }
         protoOutput->end(wrapperToken);
     }
@@ -281,17 +286,20 @@
 
     flushIfNeededLocked(eventTimeNs);
 
-    if (mPullTagId != -1) {
-        vector<shared_ptr<LogEvent>> allData;
-        if (mStatsPullerManager->Pull(mPullTagId, eventTimeNs, &allData)) {
-            if (allData.size() == 0) {
-                return;
-            }
-            for (const auto& data : allData) {
-                onMatchedLogEventLocked(0, *data);
-            }
+    if (mIsPulled) {
+        pullLocked(eventTimeNs);
+    }
+}
+
+void ValueMetricProducer::pullLocked(const int64_t timestampNs) {
+    vector<std::shared_ptr<LogEvent>> allData;
+    if (mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
+        if (allData.size() == 0) {
+            return;
         }
-        return;
+        for (const auto& data : allData) {
+            onMatchedLogEventLocked(0, *data);
+        }
     }
 }
 
@@ -308,12 +316,14 @@
         int64_t eventTime = mTimeBaseNs +
             ((realEventTime - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs;
 
+        // close the end of the bucket
         mCondition = false;
         for (const auto& data : allData) {
             data->setElapsedTimestampNs(eventTime - 1);
             onMatchedLogEventLocked(0, *data);
         }
 
+        // start a new bucket
         mCondition = true;
         for (const auto& data : allData) {
             data->setElapsedTimestampNs(eventTime);
@@ -331,10 +341,10 @@
             (unsigned long)mCurrentSlicedBucket.size());
     if (verbose) {
         for (const auto& it : mCurrentSlicedBucket) {
-            fprintf(out, "\t(what)%s\t(condition)%s  (value)%lld\n",
-                it.first.getDimensionKeyInWhat().toString().c_str(),
-                it.first.getDimensionKeyInCondition().toString().c_str(),
-                (unsigned long long)it.second.sum);
+            fprintf(out, "\t(what)%s\t(condition)%s  (value)%s\n",
+                    it.first.getDimensionKeyInWhat().toString().c_str(),
+                    it.first.getDimensionKeyInCondition().toString().c_str(),
+                    it.second.value.toString().c_str());
         }
     }
 }
@@ -359,6 +369,27 @@
     return false;
 }
 
+const Value getDoubleOrLong(const Value& value) {
+    Value v;
+    switch (value.type) {
+        case INT:
+            v.setLong(value.int_value);
+            break;
+        case LONG:
+            v.setLong(value.long_value);
+            break;
+        case FLOAT:
+            v.setDouble(value.float_value);
+            break;
+        case DOUBLE:
+            v.setDouble(value.double_value);
+            break;
+        default:
+            break;
+    }
+    return v;
+}
+
 void ValueMetricProducer::onMatchedLogEventInternalLocked(
         const size_t matcherIndex, const MetricDimensionKey& eventKey,
         const ConditionKey& conditionKey, bool condition,
@@ -377,19 +408,25 @@
     }
     Interval& interval = mCurrentSlicedBucket[eventKey];
 
-    int error = 0;
-    const int64_t value = event.GetLong(mField, &error);
-    if (error < 0) {
+    if (mField > event.size()) {
+        VLOG("Failed to extract value field %d from atom %s. %d", mField, event.ToString().c_str(),
+             (int)event.size());
         return;
     }
+    Value value = getDoubleOrLong(event.getValues()[mField - 1].mValue);
 
-    if (mPullTagId != -1) { // for pulled events
+    Value diff;
+    bool hasDiff = false;
+    if (mIsPulled) {
+        // Always require condition for pulled events. In the case of no condition, only pull
+        // on bucket boundaries, in which we fake condition changes.
         if (mCondition == true) {
             if (!interval.startUpdated) {
                 interval.start = value;
                 interval.startUpdated = true;
             } else {
-                // skip it if there is already value recorded for the start
+                // Skip it if there is already value recorded for the start. Happens when puller
+                // takes too long to finish. In this case we take the previous value.
                 VLOG("Already recorded value for this dimension %s", eventKey.toString().c_str());
             }
         } else {
@@ -397,31 +434,55 @@
             // If not, take absolute value or drop it, based on config.
             if (interval.startUpdated) {
                 if (value >= interval.start) {
-                    interval.sum += (value - interval.start);
-                    interval.hasValue = true;
+                    diff = (value - interval.start);
+                    hasDiff = true;
                 } else {
                     if (mUseAbsoluteValueOnReset) {
-                        interval.sum += value;
-                        interval.hasValue = true;
+                        diff = value;
+                        hasDiff = true;
                     } else {
-                        VLOG("Dropping data for atom %d, prev: %lld, now: %lld", mPullTagId,
-                             (long long)interval.start, (long long)value);
+                        VLOG("Dropping data for atom %d, prev: %s, now: %s", mPullTagId,
+                             interval.start.toString().c_str(), value.toString().c_str());
                     }
                 }
                 interval.startUpdated = false;
             } else {
-                VLOG("No start for matching end %lld", (long long)value);
-                interval.tainted += 1;
+                VLOG("No start for matching end %s", value.toString().c_str());
             }
         }
-    } else {    // for pushed events, only accumulate when condition is true
-        if (mCondition == true || mConditionTrackerIndex < 0) {
-            interval.sum += value;
-            interval.hasValue = true;
+    } else {
+        // for pushed events, only aggregate when sliced condition is true
+        if (condition == true || mConditionTrackerIndex < 0) {
+            diff = value;
+            hasDiff = true;
         }
     }
+    if (hasDiff) {
+        if (interval.hasValue) {
+            switch (mAggregationType) {
+                case ValueMetric::SUM:
+                // for AVG, we add up and take average when flushing the bucket
+                case ValueMetric::AVG:
+                    interval.value += diff;
+                    break;
+                case ValueMetric::MIN:
+                    interval.value = diff < interval.value ? diff : interval.value;
+                    break;
+                case ValueMetric::MAX:
+                    interval.value = diff > interval.value ? diff : interval.value;
+                    break;
+                default:
+                    break;
+            }
+        } else {
+            interval.value = diff;
+            interval.hasValue = true;
+        }
+        interval.sampleSize += 1;
+    }
 
-    long wholeBucketVal = interval.sum;
+    // TODO: propgate proper values down stream when anomaly support doubles
+    long wholeBucketVal = interval.value.long_value;
     auto prev = mCurrentFullBucket.find(eventKey);
     if (prev != mCurrentFullBucket.end()) {
         wholeBucketVal += prev->second;
@@ -468,18 +529,15 @@
 
     if (info.mBucketEndNs - mCurrentBucketStartTimeNs >= mMinBucketSizeNs) {
         // The current bucket is large enough to keep.
-        int tainted = 0;
         for (const auto& slice : mCurrentSlicedBucket) {
-            tainted += slice.second.tainted;
-            tainted += slice.second.startUpdated;
             if (slice.second.hasValue) {
-                info.mValue = slice.second.sum;
+                info.mValueLong = slice.second.value.long_value;
+                info.mValueDouble = (double)slice.second.value.long_value / slice.second.sampleSize;
                 // it will auto create new vector of ValuebucketInfo if the key is not found.
                 auto& bucketList = mPastBuckets[slice.first];
                 bucketList.push_back(info);
             }
         }
-        VLOG("%d tainted pairs in the bucket", tainted);
     } else {
         mSkippedBuckets.emplace_back(info.mBucketStartNs, info.mBucketEndNs);
     }
@@ -488,7 +546,8 @@
         // Accumulate partial buckets with current value and then send to anomaly tracker.
         if (mCurrentFullBucket.size() > 0) {
             for (const auto& slice : mCurrentSlicedBucket) {
-                mCurrentFullBucket[slice.first] += slice.second.sum;
+                // TODO: fix this when anomaly can accept double values
+                mCurrentFullBucket[slice.first] += slice.second.value.long_value;
             }
             for (const auto& slice : mCurrentFullBucket) {
                 for (auto& tracker : mAnomalyTrackers) {
@@ -503,7 +562,9 @@
             for (const auto& slice : mCurrentSlicedBucket) {
                 for (auto& tracker : mAnomalyTrackers) {
                     if (tracker != nullptr) {
-                        tracker->addPastBucket(slice.first, slice.second.sum, mCurrentBucketNum);
+                        // TODO: fix this when anomaly can accept double values
+                        tracker->addPastBucket(slice.first, slice.second.value.long_value,
+                                               mCurrentBucketNum);
                     }
                 }
             }
@@ -511,7 +572,8 @@
     } else {
         // Accumulate partial bucket.
         for (const auto& slice : mCurrentSlicedBucket) {
-            mCurrentFullBucket[slice.first] += slice.second.sum;
+            // TODO: fix this when anomaly can accept double values
+            mCurrentFullBucket[slice.first] += slice.second.value.long_value;
         }
     }
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index cb6b051..b2f0b6f 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -23,6 +23,7 @@
 #include "../condition/ConditionTracker.h"
 #include "../external/PullDataReceiver.h"
 #include "../external/StatsPullerManager.h"
+#include "../stats_log_util.h"
 #include "MetricProducer.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
@@ -33,17 +34,20 @@
 struct ValueBucket {
     int64_t mBucketStartNs;
     int64_t mBucketEndNs;
-    int64_t mValue;
+    int64_t mValueLong;
+    double mValueDouble;
 };
 
 class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
 public:
     ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
                         const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs);
+                        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
+                        const sp<StatsPullerManager>& pullerManager);
 
     virtual ~ValueMetricProducer();
 
+    // Process data pulled on bucket boundary.
     void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
 
     // ValueMetric needs special logic if it's a pulled atom.
@@ -51,9 +55,9 @@
                           const int64_t version) override {
         std::lock_guard<std::mutex> lock(mMutex);
 
-        if (mPullTagId != -1 && (mCondition == true || mConditionTrackerIndex < 0) ) {
+        if (mIsPulled && (mCondition == true || mConditionTrackerIndex < 0)) {
             vector<shared_ptr<LogEvent>> allData;
-            mStatsPullerManager->Pull(mPullTagId, eventTimeNs, &allData);
+            mPullerManager->Pull(mPullTagId, eventTimeNs, &allData);
             if (allData.size() == 0) {
                 // This shouldn't happen since this valuemetric is not useful now.
             }
@@ -112,33 +116,29 @@
 
     void dropDataLocked(const int64_t dropTimeNs) override;
 
+    sp<StatsPullerManager> mPullerManager;
+
     const FieldMatcher mValueField;
 
-    std::shared_ptr<StatsPullerManager> mStatsPullerManager;
-
-    // for testing
-    ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
-                        const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
-                        std::shared_ptr<StatsPullerManager> statsPullerManager);
-
     // tagId for pulled data. -1 if this is not pulled
     const int mPullTagId;
 
+    // if this is pulled metric
+    const bool mIsPulled;
+
     int mField;
 
     // internal state of a bucket.
     typedef struct {
         // Pulled data always come in pair of <start, end>. This holds the value
-        // for start. The diff (end - start) is added to sum.
-        int64_t start;
+        // for start. The diff (end - start) is taken as the real value.
+        Value start;
         // Whether the start data point is updated
         bool startUpdated;
-        // If end data point comes before the start, record this pair as tainted
-        // and the value is not added to the running sum.
-        int tainted;
-        // Running sum of known pairs in this bucket
-        int64_t sum;
+        // Current value, depending on the aggregation type.
+        Value value;
+        // Number of samples collected.
+        int sampleSize;
         // If this dimension has any non-tainted value. If not, don't report the
         // dimension.
         bool hasValue;
@@ -149,7 +149,6 @@
     std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
-    // TODO: Add a lock to mPastBuckets.
     std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
 
     // Pairs of (elapsed start, elapsed end) denoting buckets that were skipped.
@@ -160,6 +159,8 @@
     // Util function to check whether the specified dimension hits the guardrail.
     bool hitGuardRailLocked(const MetricDimensionKey& newKey);
 
+    void pullLocked(const int64_t timestampNs);
+
     static const size_t kBucketSize = sizeof(ValueBucket{});
 
     const size_t mDimensionSoftLimit;
@@ -168,7 +169,11 @@
 
     const bool mUseAbsoluteValueOnReset;
 
-    FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
+    const ValueMetric::AggregationType mAggregationType;
+
+    const Type mValueType;
+
+    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
     FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
@@ -182,6 +187,12 @@
     FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2);
     FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition3);
+    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin);
+    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax);
+    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
+    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
+    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSumSliced);
+    FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 149b318..ccb1d43 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -44,7 +44,6 @@
     int64_t lastStartTime;
     // existing duration in current bucket.
     int64_t lastDuration;
-    // TODO: Optimize the way we track sliced condition in duration metrics.
     // cache the HashableDimensionKeys we need to query the condition for this duration event.
     ConditionKey conditionKeys;
 
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index b833dfc..956383a 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -326,7 +326,6 @@
 
 int64_t OringDurationTracker::predictAnomalyTimestampNs(
         const DurationAnomalyTracker& anomalyTracker, const int64_t eventTimestampNs) const {
-    // TODO: Unit-test this and see if it can be done more efficiently (e.g. use int32).
 
     // The anomaly threshold.
     const int64_t thresholdNs = anomalyTracker.getAnomalyThreshold();
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 811a00e..ff48d02 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -103,7 +103,6 @@
         }
         allConditionTrackers[condition_it->second]->setSliced(true);
         allConditionTrackers[it->second]->setSliced(true);
-        // TODO: We need to verify the link is valid.
     }
     conditionIndex = condition_it->second;
 
@@ -169,7 +168,6 @@
 bool isStateTracker(const SimplePredicate& simplePredicate, vector<Matcher>* primaryKeys) {
     // 1. must not have "stop". must have "dimension"
     if (!simplePredicate.has_stop() && simplePredicate.has_dimensions()) {
-        // TODO: need to check the start atom matcher too.
         auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(
                 simplePredicate.dimensions().field());
         // 2. must be based on a state atom.
@@ -262,9 +260,10 @@
     return true;
 }
 
-bool initMetrics(const ConfigKey& key, const StatsdConfig& config,
-                 const int64_t timeBaseTimeNs, const int64_t currentTimeNs,
-                 UidMap& uidMap, const unordered_map<int64_t, int>& logTrackerMap,
+bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
+                 const int64_t currentTimeNs, UidMap& uidMap,
+                 const sp<StatsPullerManager>& pullerManager,
+                 const unordered_map<int64_t, int>& logTrackerMap,
                  const unordered_map<int64_t, int>& conditionTrackerMap,
                  const vector<sp<LogMatchingTracker>>& allAtomMatchers,
                  vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -313,7 +312,7 @@
         }
 
         sp<MetricProducer> countProducer =
-                new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs);
+                new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs);
         allMetricProducers.push_back(countProducer);
     }
 
@@ -383,7 +382,7 @@
 
         sp<MetricProducer> durationMetric = new DurationMetricProducer(
                 key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
-                trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs);
+                trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs, currentTimeNs);
 
         allMetricProducers.push_back(durationMetric);
     }
@@ -465,9 +464,9 @@
             }
         }
 
-        sp<MetricProducer> valueProducer = new ValueMetricProducer(key, metric, conditionIndex,
-                                                                   wizard, pullTagId,
-                                                                   timeBaseTimeNs, currentTimeNs);
+        sp<MetricProducer> valueProducer =
+                new ValueMetricProducer(key, metric, conditionIndex, wizard, pullTagId,
+                                        timeBaseTimeNs, currentTimeNs, pullerManager);
         allMetricProducers.push_back(valueProducer);
     }
 
@@ -525,8 +524,9 @@
             }
         }
 
-        sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
-                key, metric, conditionIndex, wizard, pullTagId, timeBaseTimeNs, currentTimeNs);
+        sp<MetricProducer> gaugeProducer =
+                new GaugeMetricProducer(key, metric, conditionIndex, wizard, pullTagId,
+                                        timeBaseTimeNs, currentTimeNs, pullerManager);
         allMetricProducers.push_back(gaugeProducer);
     }
     for (int i = 0; i < config.no_report_metric_size(); ++i) {
@@ -645,10 +645,10 @@
 }
 
 bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
+                      const sp<StatsPullerManager>& pullerManager,
                       const sp<AlarmMonitor>& anomalyAlarmMonitor,
-                      const sp<AlarmMonitor>& periodicAlarmMonitor,
-                      const int64_t timeBaseNs, const int64_t currentTimeNs,
-                      set<int>& allTagIds,
+                      const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+                      const int64_t currentTimeNs, set<int>& allTagIds,
                       vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
                       vector<sp<MetricProducer>>& allMetricProducers,
@@ -674,9 +674,8 @@
         return false;
     }
 
-    if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap,
-                     logTrackerMap, conditionTrackerMap,
-                     allAtomMatchers, allConditionTrackers, allMetricProducers,
+    if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap, pullerManager, logTrackerMap,
+                     conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers,
                      conditionToMetricMap, trackerToMetricMap, metricProducerMap,
                      noReportMetricIds)) {
         ALOGE("initMetricProducers failed");
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index d749bf4..c660149 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -23,7 +23,7 @@
 
 #include "../anomaly/AlarmTracker.h"
 #include "../condition/ConditionTracker.h"
-#include "../external/StatsPullerManagerImpl.h"
+#include "../external/StatsPullerManager.h"
 #include "../matchers/LogMatchingTracker.h"
 #include "../metrics/MetricProducer.h"
 
@@ -81,9 +81,8 @@
 //                          the list of MetricProducer index
 // [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
 bool initMetrics(
-        const ConfigKey& key, const StatsdConfig& config,
-        const int64_t timeBaseTimeNs, const int64_t currentTimeNs,
-        UidMap& uidMap,
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
+        const int64_t currentTimeNs, UidMap& uidMap, const sp<StatsPullerManager>& pullerManager,
         const std::unordered_map<int64_t, int>& logTrackerMap,
         const std::unordered_map<int64_t, int>& conditionTrackerMap,
         const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
@@ -97,10 +96,10 @@
 // Initialize MetricsManager from StatsdConfig.
 // Parameters are the members of MetricsManager. See MetricsManager for declaration.
 bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
+                      const sp<StatsPullerManager>& pullerManager,
                       const sp<AlarmMonitor>& anomalyAlarmMonitor,
-                      const sp<AlarmMonitor>& periodicAlarmMonitor,
-                      const int64_t timeBaseNs, const int64_t currentTimeNs,
-                      std::set<int>& allTagIds,
+                      const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+                      const int64_t currentTimeNs, std::set<int>& allTagIds,
                       std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       std::vector<sp<MetricProducer>>& allMetricProducers,
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 514eec7..91f2030 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -146,7 +146,6 @@
 
     void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output);
 
-    // TODO: Use shared_mutex for improved read-locking if a library can be found in Android.
     mutable mutex mMutex;
     mutable mutex mIsolatedMutex;
 
diff --git a/cmds/statsd/src/perfetto/perfetto_config.proto b/cmds/statsd/src/perfetto/perfetto_config.proto
deleted file mode 100644
index 56d12f8..0000000
--- a/cmds/statsd/src/perfetto/perfetto_config.proto
+++ /dev/null
@@ -1,60 +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.
- */
-
-syntax = "proto2";
-
-package perfetto.protos;
-
-message DataSourceConfig {
-  message FtraceConfig {
-    repeated string event_names = 1;
-  }
-
-  optional string name = 1;
-
-  optional uint32 target_buffer = 2;
-
-  optional FtraceConfig ftrace_config = 100;
-}
-
-message TraceConfig {
-  message BufferConfig {
-    optional uint32 size_kb = 1;
-
-    enum OptimizeFor {
-      DEFAULT = 0;
-      ONE_SHOT_READ = 1;
-
-    }
-    optional OptimizeFor optimize_for = 3;
-
-    enum FillPolicy {
-      UNSPECIFIED = 0;
-      RING_BUFFER = 1;
-    }
-    optional FillPolicy fill_policy = 4;
-  }
-  repeated BufferConfig buffers = 1;
-
-  message DataSource {
-    optional protos.DataSourceConfig config = 1;
-
-    repeated string producer_name_filter = 2;
-  }
-  repeated DataSource data_sources = 2;
-
-  optional uint32 duration_ms = 3;
-}
diff --git a/cmds/statsd/src/perfprofd/perfprofd_config.proto b/cmds/statsd/src/perfprofd/perfprofd_config.proto
deleted file mode 120000
index 2e3305f..0000000
--- a/cmds/statsd/src/perfprofd/perfprofd_config.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../system/extras/perfprofd/binder_interface/perfprofd_config.proto
\ No newline at end of file
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 2fe17da..db7e680 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -106,7 +106,13 @@
 
   optional int64 end_bucket_elapsed_nanos = 2;
 
-  optional int64 value = 3;
+  optional int64 value = 3 [deprecated = true];
+
+  oneof values {
+      int64 value_long = 7;
+
+      double value_double = 8;
+  }
 
   optional int64 bucket_num = 4;
 
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 638fbb9..26dfda3 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -21,9 +21,6 @@
 option java_package = "com.android.internal.os";
 option java_outer_classname = "StatsdConfigProto";
 
-import "frameworks/base/cmds/statsd/src/perfetto/perfetto_config.proto";
-import "frameworks/base/cmds/statsd/src/perfprofd/perfprofd_config.proto";
-
 enum Position {
   POSITION_UNKNOWN = 0;
 
@@ -262,6 +259,9 @@
 
   enum AggregationType {
     SUM = 1;
+    MIN = 2;
+    MAX = 3;
+    AVG = 4;
   }
   optional AggregationType aggregation_type = 8 [default = SUM];
 
@@ -301,11 +301,21 @@
 }
 
 message PerfettoDetails {
-  optional perfetto.protos.TraceConfig trace_config = 1;
+  // The |trace_config| field is a proto-encoded message of type
+  // perfetto.protos.TraceConfig defined in
+  // //external/perfetto/protos/perfetto/config/. On device,
+  // statsd doesn't need to deserialize the message as it's just
+  // passed binary-encoded to the perfetto cmdline client.
+  optional bytes trace_config = 1;
 }
 
 message PerfprofdDetails {
-  optional android.perfprofd.ProfilingConfig perfprofd_config = 1;
+  // The |perfprofd_config| field is a proto-encoded message of type
+  // android.perfprofd.ProfilingConfig defined in
+  // //system/extras/perfprofd/. On device, statsd doesn't need to
+  // deserialize the message as it's just passed binary-encoded to
+  // the perfprofd service.
+  optional bytes perfprofd_config = 1;
 }
 
 message BroadcastSubscriberDetails {
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 1f81812..3ebc8a4 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -57,7 +57,7 @@
     }
     // When index ends before hitting 3, file name is corrupted. We
     // intentionally put -1 at index 0 to indicate the error to caller.
-    // TODO: consider removing files with unexpected name format.
+    // TODO(b/110563137): consider removing files with unexpected name format.
     if (index < 3) {
         result[0] = -1;
     }
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index c253bc1..a9305ac 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -312,7 +312,8 @@
     dim.addValue(FieldValue(field4, value4));
 
     SubscriberReporter::getStatsDimensionsValue(dim);
-    // TODO: can't test anything here because SubscriberReport class doesn't have any read api.
+    // TODO(b/110562792): can't test anything here because StatsDimensionsValue class doesn't
+    // have any read api.
 }
 
 TEST(AtomMatcherTest, TestWriteDimensionToProto) {
@@ -483,4 +484,4 @@
 }  // namespace android
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
\ No newline at end of file
+#endif
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 07378db..8fbb58a 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -39,8 +39,6 @@
 
 #ifdef __ANDROID__
 
-// TODO: ADD MORE TEST CASES.
-
 const ConfigKey kConfigKey(0, 12345);
 
 const long timeBaseSec = 1000;
@@ -271,6 +269,7 @@
 
 TEST(MetricsManagerTest, TestGoodConfig) {
     UidMap uidMap;
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildGoodConfig();
@@ -285,13 +284,11 @@
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
 
-    EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap,
-                                 anomalyAlarmMonitor, periodicAlarmMonitor,
-                                 timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
-                                 allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                 allAlarmTrackers,
-                                 conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-                                 noReportMetricIds));
+    EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+                                 periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+                                 allAtomMatchers, allConditionTrackers, allMetricProducers,
+                                 allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+                                 trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
     EXPECT_EQ(1u, allMetricProducers.size());
     EXPECT_EQ(1u, allAnomalyTrackers.size());
     EXPECT_EQ(1u, noReportMetricIds.size());
@@ -299,6 +296,7 @@
 
 TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
     UidMap uidMap;
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildDimensionMetricsWithMultiTags();
@@ -313,17 +311,16 @@
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
-                                  anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
-                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                  allAlarmTrackers,
-                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-                                  noReportMetricIds));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
+                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+                                  trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
     UidMap uidMap;
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildCircleMatchers();
@@ -338,17 +335,16 @@
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
-                                  anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
-                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                  allAlarmTrackers,
-                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-                                  noReportMetricIds));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
+                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+                                  trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestMissingMatchers) {
     UidMap uidMap;
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildMissingMatchers();
@@ -362,17 +358,16 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
-                                  anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
-                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                  allAlarmTrackers,
-                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-                                  noReportMetricIds));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
+                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+                                  trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestMissingPredicate) {
     UidMap uidMap;
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildMissingPredicate();
@@ -386,17 +381,16 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
-                                  anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
-                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                  allAlarmTrackers,
-                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-                                  noReportMetricIds));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
+                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+                                  trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestCirclePredicateDependency) {
     UidMap uidMap;
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildCirclePredicates();
@@ -411,17 +405,16 @@
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
-                                  anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
-                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                  allAlarmTrackers,
-                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-                                  noReportMetricIds));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
+                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+                                  trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
     UidMap uidMap;
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
     StatsdConfig config = buildAlertWithUnknownMetric();
@@ -436,13 +429,11 @@
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     std::set<int64_t> noReportMetricIds;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
-                                  anomalyAlarmMonitor, periodicAlarmMonitor,
-                                  timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
-                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                  allAlarmTrackers,
-                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-                                  noReportMetricIds));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
+                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+                                  trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
 }
 
 #else
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 76f3d81..ecc57f5 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -44,13 +44,13 @@
  */
 class MockMetricsManager : public MetricsManager {
 public:
-    MockMetricsManager() : MetricsManager(
-        ConfigKey(1, 12345), StatsdConfig(), 1000, 1000,
-        new UidMap(),
-        new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
-                         [](const sp<IStatsCompanionService>&){}),
-        new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
-                         [](const sp<IStatsCompanionService>&){})) {
+    MockMetricsManager()
+        : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, 1000, new UidMap(),
+                         new StatsPullerManager(),
+                         new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t) {},
+                                          [](const sp<IStatsCompanionService>&) {}),
+                         new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t) {},
+                                          [](const sp<IStatsCompanionService>&) {})) {
     }
 
     MOCK_METHOD0(byteSize, size_t());
@@ -60,11 +60,12 @@
 
 TEST(StatsLogProcessorTest, TestRateLimitByteSize) {
     sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
     // Construct the processor with a dummy sendBroadcast function that does nothing.
-    StatsLogProcessor p(m, anomalyAlarmMonitor, periodicAlarmMonitor, 0,
-        [](const ConfigKey& key) {return true;});
+    StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, 0,
+                        [](const ConfigKey& key) { return true; });
 
     MockMetricsManager mockMetricsManager;
 
@@ -79,11 +80,15 @@
 
 TEST(StatsLogProcessorTest, TestRateLimitBroadcast) {
     sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> subscriberAlarmMonitor;
     int broadcastCount = 0;
-    StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
-                        [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
+    StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+                        [&broadcastCount](const ConfigKey& key) {
+                            broadcastCount++;
+                            return true;
+                        });
 
     MockMetricsManager mockMetricsManager;
 
@@ -105,11 +110,15 @@
 
 TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) {
     sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> subscriberAlarmMonitor;
     int broadcastCount = 0;
-    StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
-                        [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
+    StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+                        [&broadcastCount](const ConfigKey& key) {
+                            broadcastCount++;
+                            return true;
+                        });
 
     MockMetricsManager mockMetricsManager;
 
@@ -143,12 +152,16 @@
 TEST(StatsLogProcessorTest, TestUidMapHasSnapshot) {
     // Setup simple config key corresponding to empty config.
     sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     m->updateMap(1, {1, 2}, {1, 2}, {String16("p1"), String16("p2")});
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> subscriberAlarmMonitor;
     int broadcastCount = 0;
-    StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
-                        [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
+    StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+                        [&broadcastCount](const ConfigKey& key) {
+                            broadcastCount++;
+                            return true;
+                        });
     ConfigKey key(3, 4);
     StatsdConfig config = MakeConfig(true);
     p.OnConfigUpdated(0, key, config);
@@ -168,12 +181,16 @@
 TEST(StatsLogProcessorTest, TestEmptyConfigHasNoUidMap) {
     // Setup simple config key corresponding to empty config.
     sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     m->updateMap(1, {1, 2}, {1, 2}, {String16("p1"), String16("p2")});
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> subscriberAlarmMonitor;
     int broadcastCount = 0;
-    StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
-                        [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
+    StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+                        [&broadcastCount](const ConfigKey& key) {
+                            broadcastCount++;
+                            return true;
+                        });
     ConfigKey key(3, 4);
     StatsdConfig config = MakeConfig(false);
     p.OnConfigUpdated(0, key, config);
@@ -191,11 +208,15 @@
 TEST(StatsLogProcessorTest, TestReportIncludesSubConfig) {
     // Setup simple config key corresponding to empty config.
     sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> subscriberAlarmMonitor;
     int broadcastCount = 0;
-    StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
-                        [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
+    StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+                        [&broadcastCount](const ConfigKey& key) {
+                            broadcastCount++;
+                            return true;
+                        });
     ConfigKey key(3, 4);
     StatsdConfig config;
     auto annotation = config.add_annotation();
@@ -220,11 +241,15 @@
 TEST(StatsLogProcessorTest, TestOutOfOrderLogs) {
     // Setup simple config key corresponding to empty config.
     sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> subscriberAlarmMonitor;
     int broadcastCount = 0;
-    StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
-                        [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
+    StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+                        [&broadcastCount](const ConfigKey& key) {
+                            broadcastCount++;
+                            return true;
+                        });
 
     LogEvent event1(0, 1 /*logd timestamp*/, 1001 /*elapsedRealtime*/);
     event1.init();
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index e23131d..99082cc 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -40,11 +40,12 @@
 
 TEST(UidMapTest, TestIsolatedUID) {
     sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> subscriberAlarmMonitor;
     // Construct the processor with a dummy sendBroadcast function that does nothing.
-    StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
-        [](const ConfigKey& key) {return true;});
+    StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+                        [](const ConfigKey& key) { return true; });
     LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
     addEvent.write(100);  // parent UID
     addEvent.write(101);  // isolated UID
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index eca5690..d98395e 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -66,7 +66,7 @@
         baseTimeNs, configAddedTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-    processor->mStatsPullerManager.ForceClearPullerCache();
+    processor->mPullerManager->ForceClearPullerCache();
 
     int startBucketNum = processor->mMetricsManagers.begin()->second->
             mAllMetricProducers[0]->getCurrentBucketNum();
@@ -74,12 +74,11 @@
 
     // When creating the config, the gauge metric producer should register the alarm at the
     // end of the current bucket.
-    EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
     EXPECT_EQ(bucketSizeNs,
-              StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
-                    second.front().intervalNs);
-    int64_t& nextPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
-            second.front().nextPullTimeNs;
+              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+    int64_t& nextPullTimeNs =
+            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
     EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
 
     auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
@@ -212,7 +211,7 @@
         baseTimeNs, configAddedTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-    processor->mStatsPullerManager.ForceClearPullerCache();
+    processor->mPullerManager->ForceClearPullerCache();
 
     int startBucketNum = processor->mMetricsManagers.begin()->second->
             mAllMetricProducers[0]->getCurrentBucketNum();
@@ -313,7 +312,7 @@
         baseTimeNs, configAddedTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-    processor->mStatsPullerManager.ForceClearPullerCache();
+    processor->mPullerManager->ForceClearPullerCache();
 
     int startBucketNum = processor->mMetricsManagers.begin()->second->
             mAllMetricProducers[0]->getCurrentBucketNum();
@@ -321,12 +320,11 @@
 
     // When creating the config, the gauge metric producer should register the alarm at the
     // end of the current bucket.
-    EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
     EXPECT_EQ(bucketSizeNs,
-              StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
-                    second.front().intervalNs);
-    int64_t& nextPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
-            second.front().nextPullTimeNs;
+              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+    int64_t& nextPullTimeNs =
+            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
     EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
 
     auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 11aaab0..cc8894b 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -99,7 +99,6 @@
 
 // If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests,
 // we should use the real API which will clear the data after dump data is called.
-// TODO: better refactor the code so that the tests are not so verbose.
 TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) {
     auto config = CreateStatsdConfig();
     uint64_t bucketStartTimeNs = 10000000000;
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index dd28d36..f2e8f58 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -66,7 +66,7 @@
         baseTimeNs, configAddedTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-    processor->mStatsPullerManager.ForceClearPullerCache();
+    processor->mPullerManager->ForceClearPullerCache();
 
     int startBucketNum = processor->mMetricsManagers.begin()->second->
             mAllMetricProducers[0]->getCurrentBucketNum();
@@ -74,12 +74,11 @@
 
     // When creating the config, the gauge metric producer should register the alarm at the
     // end of the current bucket.
-    EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
     EXPECT_EQ(bucketSizeNs,
-              StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
-                    second.front().intervalNs);
-    int64_t& expectedPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
-            second.front().nextPullTimeNs;
+              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+    int64_t& expectedPullTimeNs =
+            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
     EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
 
     auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
@@ -142,23 +141,23 @@
 
     EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(0).has_value());
+    EXPECT_TRUE(data.bucket_info(0).has_value_long());
 
     EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(1).has_value());
+    EXPECT_TRUE(data.bucket_info(1).has_value_long());
 
     EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(2).has_value());
+    EXPECT_TRUE(data.bucket_info(2).has_value_long());
 
     EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(3).has_value());
+    EXPECT_TRUE(data.bucket_info(3).has_value_long());
 
     EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(4).has_value());
+    EXPECT_TRUE(data.bucket_info(4).has_value_long());
 }
 
 TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
@@ -173,7 +172,7 @@
         baseTimeNs, configAddedTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-    processor->mStatsPullerManager.ForceClearPullerCache();
+    processor->mPullerManager->ForceClearPullerCache();
 
     int startBucketNum = processor->mMetricsManagers.begin()->second->
             mAllMetricProducers[0]->getCurrentBucketNum();
@@ -181,12 +180,11 @@
 
     // When creating the config, the gauge metric producer should register the alarm at the
     // end of the current bucket.
-    EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
     EXPECT_EQ(bucketSizeNs,
-              StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
-                    second.front().intervalNs);
-    int64_t& expectedPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
-            second.front().nextPullTimeNs;
+              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+    int64_t& expectedPullTimeNs =
+            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
     EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
 
     // Screen off/on/off events.
@@ -250,15 +248,15 @@
 
     EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(0).has_value());
+    EXPECT_TRUE(data.bucket_info(0).has_value_long());
 
     EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(1).has_value());
+    EXPECT_TRUE(data.bucket_info(1).has_value_long());
 
     EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
     EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
-    EXPECT_TRUE(data.bucket_info(2).has_value());
+    EXPECT_TRUE(data.bucket_info(2).has_value_long());
 }
 
 #else
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 9a8919e..67c704e 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -37,6 +37,19 @@
 
 const ConfigKey kConfigKey(0, 12345);
 
+TEST(CountMetricProducerTest, TestFirstBucket) {
+    CountMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      5, 600 * NS_PER_SEC + NS_PER_SEC/2);
+    EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(10, countProducer.mCurrentBucketNum);
+    EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
+}
+
 TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
     int64_t bucketStartTimeNs = 10000000000;
     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
@@ -56,8 +69,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      bucketStartTimeNs);
-    countProducer.setBucketSize(60 * NS_PER_SEC);
+                                      bucketStartTimeNs, bucketStartTimeNs);
 
     // 2 events in bucket 1.
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
@@ -119,8 +131,7 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
-    countProducer.setBucketSize(60 * NS_PER_SEC);
+    CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, bucketStartTimeNs);
 
     countProducer.onConditionChanged(true, bucketStartTimeNs);
     countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
@@ -181,8 +192,7 @@
     EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue));
 
     CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
-                                      bucketStartTimeNs);
-    countProducer.setBucketSize(60 * NS_PER_SEC);
+                                      bucketStartTimeNs, bucketStartTimeNs);
 
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
     countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
@@ -221,8 +231,7 @@
     event1.init();
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
-                                      bucketStartTimeNs);
-    countProducer.setBucketSize(60 * NS_PER_SEC);
+                                      bucketStartTimeNs, bucketStartTimeNs);
 
     sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
     EXPECT_TRUE(anomalyTracker != nullptr);
@@ -280,8 +289,7 @@
     event1.init();
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
-                                      bucketStartTimeNs);
-    countProducer.setBucketSize(60 * NS_PER_SEC);
+                                      bucketStartTimeNs, bucketStartTimeNs);
 
     // Bucket is flushed yet.
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
@@ -337,8 +345,7 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      bucketStartTimeNs);
-    countProducer.setBucketSize(60 * NS_PER_SEC);
+                                      bucketStartTimeNs, bucketStartTimeNs);
 
     sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
 
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 7ef8c5b..b540964 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -39,6 +39,23 @@
 
 const ConfigKey kConfigKey(0, 12345);
 
+TEST(DurationMetricTrackerTest, TestFirstBucket) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    DurationMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+    FieldMatcher dimensions;
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
+            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC/2);
+
+    EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(10, durationProducer.mCurrentBucketNum);
+    EXPECT_EQ(660000000005, durationProducer.getCurrentBucketEndTimeNs());
+}
+
 TEST(DurationMetricTrackerTest, TestNoCondition) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     int64_t bucketStartTimeNs = 10000000000;
@@ -58,8 +75,7 @@
     FieldMatcher dimensions;
     DurationMetricProducer durationProducer(
             kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
-            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
-    durationProducer.setBucketSize(60 * NS_PER_SEC);
+            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
@@ -100,8 +116,7 @@
     FieldMatcher dimensions;
     DurationMetricProducer durationProducer(
             kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
-            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
-    durationProducer.setBucketSize(60 * NS_PER_SEC);
+            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     EXPECT_FALSE(durationProducer.mCondition);
     EXPECT_FALSE(durationProducer.isConditionSliced());
@@ -151,8 +166,7 @@
     FieldMatcher dimensions;
     DurationMetricProducer durationProducer(
             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
-            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
-    durationProducer.setBucketSize(60 * NS_PER_SEC);
+            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     LogEvent start_event(tagId, startTimeNs);
     start_event.init();
@@ -206,8 +220,7 @@
     FieldMatcher dimensions;
     DurationMetricProducer durationProducer(
             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
-            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
-    durationProducer.setBucketSize(60 * NS_PER_SEC);
+            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     LogEvent start_event(tagId, startTimeNs);
     start_event.init();
@@ -261,8 +274,7 @@
     FieldMatcher dimensions;
     DurationMetricProducer durationProducer(
             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
-            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
-    durationProducer.setBucketSize(60 * NS_PER_SEC);
+            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
     EXPECT_TRUE(anomalyTracker != nullptr);
@@ -300,8 +312,7 @@
     FieldMatcher dimensions;
     DurationMetricProducer durationProducer(
             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
-            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
-    durationProducer.setBucketSize(60 * NS_PER_SEC);
+            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     LogEvent start_event(tagId, startTimeNs);
     start_event.init();
@@ -348,8 +359,7 @@
     FieldMatcher dimensions;
     DurationMetricProducer durationProducer(
             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
-            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
-    durationProducer.setBucketSize(60 * NS_PER_SEC);
+            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     LogEvent start_event(tagId, startTimeNs);
     start_event.init();
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 3a15466..d2fd95c 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -54,8 +54,8 @@
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
 
-    // TODO: get the report and check the content after the ProtoOutputStream change is done.
-    // eventProducer.onDumpReport();
+    // TODO(b/110561136): get the report and check the content after the ProtoOutputStream change
+    // is done eventProducer.onDumpReport();
 }
 
 TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) {
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 698ce72..2fda858 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -47,7 +47,33 @@
 const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
 const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
 
-TEST(GaugeMetricProducerTest, TestNoCondition) {
+/*
+ * Tests that the first bucket works correctly
+ */
+TEST(GaugeMetricProducerTest, TestFirstBucket) {
+    GaugeMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_gauge_fields_filter()->set_include_all(false);
+    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+    gaugeFieldMatcher->set_field(tagId);
+    gaugeFieldMatcher->add_child()->set_field(1);
+    gaugeFieldMatcher->add_child()->set_field(3);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    // statsd started long ago.
+    // The metric starts in the middle of the bucket
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      -1, 5, 600 * NS_PER_SEC + NS_PER_SEC/2, pullerManager);
+
+    EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum);
+    EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs());
+}
+
+TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) {
     GaugeMetric metric;
     metric.set_id(metricId);
     metric.set_bucket(ONE_MINUTE);
@@ -59,12 +85,19 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    // TODO: pending refactor of StatsPullerManager
-    // For now we still need this so that it doesn't do real pulling.
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    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) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                event->write(3);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
@@ -86,11 +119,12 @@
     EXPECT_EQ(10, it->mValue.int_value);
     it++;
     EXPECT_EQ(11, it->mValue.int_value);
-    EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+    EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms
+        .front().mFields->begin()->mValue.int_value);
 
     allData.clear();
-    std::shared_ptr<LogEvent> event2 =
-            std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10);
+    std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10);
     event2->write(24);
     event2->write("some value");
     event2->write(25);
@@ -106,7 +140,7 @@
     EXPECT_EQ(25, it->mValue.int_value);
     // One dimension.
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
     it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
     EXPECT_EQ(INT, it->mValue.getType());
     EXPECT_EQ(10L, it->mValue.int_value);
@@ -118,7 +152,7 @@
     EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
     // One dimension.
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
-    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size());
     it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
     EXPECT_EQ(INT, it->mValue.getType());
     EXPECT_EQ(24L, it->mValue.int_value);
@@ -140,8 +174,7 @@
     alert.set_trigger_if_sum_gt(25);
     alert.set_num_buckets(100);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       -1 /* -1 means no pulling */, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
@@ -211,11 +244,11 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    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))
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
@@ -269,7 +302,7 @@
                          ->mValue.int_value);
 }
 
-TEST(GaugeMetricProducerTest, TestWithCondition) {
+TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) {
     GaugeMetric metric;
     metric.set_id(metricId);
     metric.set_bucket(ONE_MINUTE);
@@ -280,8 +313,7 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    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, _, _))
@@ -296,8 +328,8 @@
                 return true;
             }));
 
-    GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId,
-                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
     gaugeProducer.setBucketSize(60 * NS_PER_SEC);
 
     gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
@@ -340,7 +372,7 @@
                             ->mValue.int_value);
 }
 
-TEST(GaugeMetricProducerTest, TestWithSlicedCondition) {
+TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) {
     const int conditionTag = 65;
     GaugeMetric metric;
     metric.set_id(1111111);
@@ -372,8 +404,7 @@
                         return ConditionState::kTrue;
                     }));
 
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    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, _, _))
@@ -417,14 +448,14 @@
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
 }
 
-TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
+TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) {
     sp<AlarmMonitor> alarmMonitor;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    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));
 
     GaugeMetric metric;
     metric.set_id(metricId);
@@ -472,7 +503,7 @@
                            .mFields->begin()
                            ->mValue.int_value);
     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
-            std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec);
+              std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec);
 
     std::shared_ptr<LogEvent> event3 =
             std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10);
@@ -487,7 +518,7 @@
                            .mFields->begin()
                            ->mValue.int_value);
     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
-            std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+              std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
 
     // The event4 does not have the gauge field. Thus the current bucket value is 0.
     std::shared_ptr<LogEvent> event4 =
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index e3a8a55..57aab97 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -50,9 +50,9 @@
 const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
 
 /*
- * Tests pulled atoms with no conditions
+ * Tests that the first bucket works correctly
  */
-TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
+TEST(ValueMetricProducerTest, TestFirstBucket) {
     ValueMetric metric;
     metric.set_id(metricId);
     metric.set_bucket(ONE_MINUTE);
@@ -60,16 +60,46 @@
     metric.mutable_value_field()->add_child()->set_field(2);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    // TODO: pending refactor of StatsPullerManager
-    // For now we still need this so that it doesn't do real pulling.
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    // statsd started long ago.
+    // The metric starts in the middle of the bucket
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      -1, 5, 600 * NS_PER_SEC + NS_PER_SEC/2, pullerManager);
+
+    EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
+    EXPECT_EQ(660000000005, valueProducer.getCurrentBucketEndTimeNs());
+}
+
+/*
+ * Tests pulled atoms with no conditions
+ */
+TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
+    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);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    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) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                event->write(tagId);
+                event->write(3);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -83,14 +113,13 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
-    // startUpdated:true tainted:0 sum:0 start:11
+    // startUpdated:true sum:0 start:11
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
-    EXPECT_EQ(11, curInterval.start);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(11, curInterval.start.long_value);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -102,13 +131,12 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // tartUpdated:false tainted:0 sum:12
+    // tartUpdated:false sum:12
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
+    EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -119,13 +147,12 @@
     valueProducer.onDataPulled(allData);
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // startUpdated:false tainted:0 sum:12
+    // startUpdated:false sum:12
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValue);
+    EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
 }
 
 /*
@@ -140,14 +167,13 @@
     metric.set_use_absolute_value_on_reset(true);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    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));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -161,12 +187,10 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
-    EXPECT_EQ(11, curInterval.start);
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(11, curInterval.start.long_value);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     allData.clear();
@@ -180,11 +204,10 @@
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValue);
+    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -196,11 +219,10 @@
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second.back().mValue);
+    EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
 }
 
 /*
@@ -214,14 +236,13 @@
     metric.mutable_value_field()->add_child()->set_field(2);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    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));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -235,12 +256,10 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
-    EXPECT_EQ(11, curInterval.start);
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(11, curInterval.start.long_value);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     allData.clear();
@@ -254,8 +273,7 @@
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     allData.clear();
@@ -268,11 +286,10 @@
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second.back().mValue);
+    EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
 }
 
 /*
@@ -287,12 +304,22 @@
     metric.set_condition(StringToId("SCREEN_ON"));
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    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, _, _))
+            // should not take effect
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                event->write(tagId);
+                event->write(3);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
@@ -316,17 +343,15 @@
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // startUpdated:false tainted:0 sum:0 start:100
-    EXPECT_EQ(100, curInterval.start);
+    // startUpdated:false sum:0 start:100
+    EXPECT_EQ(100, curInterval.start.long_value);
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     vector<shared_ptr<LogEvent>> allData;
@@ -341,19 +366,19 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // startUpdated:false tainted:0 sum:0 start:110
-    EXPECT_EQ(110, curInterval.start);
+    // startUpdated:false sum:0 start:110
+    EXPECT_EQ(110, curInterval.start.long_value);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValue);
+    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
 
     valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // startUpdated:false tainted:0 sum:0 start:110
-    EXPECT_EQ(10, curInterval.sum);
+    // startUpdated:false sum:0 start:110
+    EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(false, curInterval.startUpdated);
 }
 
@@ -365,11 +390,9 @@
     metric.mutable_value_field()->add_child()->set_field(2);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -408,11 +431,11 @@
     metric.mutable_value_field()->add_child()->set_field(2);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    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))
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
@@ -425,7 +448,6 @@
             }));
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -441,7 +463,7 @@
     valueProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
     EXPECT_EQ(eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs);
-    EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValue);
+    EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValueLong);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
@@ -452,7 +474,7 @@
     valueProducer.onDataPulled(allData);
     EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
     EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
-    EXPECT_EQ(30L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mValue);
+    EXPECT_EQ(30L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mValueLong);
 }
 
 TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
@@ -464,11 +486,11 @@
     metric.set_condition(StringToId("SCREEN_ON"));
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    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))
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
@@ -491,7 +513,6 @@
             }));
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
 
     valueProducer.onConditionChanged(false, bucket2StartTimeNs-100);
@@ -502,7 +523,7 @@
     EXPECT_EQ(bucket2StartTimeNs-50, valueProducer.mCurrentBucketStartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
     EXPECT_EQ(bucketStartTimeNs, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
-    EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValue);
+    EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValueLong);
     EXPECT_FALSE(valueProducer.mCondition);
 }
 
@@ -514,12 +535,10 @@
     metric.mutable_value_field()->add_child()->set_field(2);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -533,19 +552,20 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(10, curInterval.sum);
+    EXPECT_EQ(10, curInterval.value.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(30, curInterval.sum);
+    EXPECT_EQ(30, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue);
+    EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
 }
 
 TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
@@ -556,12 +576,10 @@
     metric.mutable_value_field()->add_child()->set_field(2);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, -1, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -583,7 +601,7 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(20, curInterval.sum);
+    EXPECT_EQ(20, curInterval.value.long_value);
 
     shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 30);
     event3->write(1);
@@ -594,7 +612,7 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(50, curInterval.sum);
+    EXPECT_EQ(50, curInterval.value.long_value);
 
     valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
     shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 40);
@@ -606,12 +624,12 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    EXPECT_EQ(50, curInterval.sum);
+    EXPECT_EQ(50, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(50, valueProducer.mPastBuckets.begin()->second.back().mValue);
+    EXPECT_EQ(50, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
 }
 
 TEST(ValueMetricProducerTest, TestAnomalyDetection) {
@@ -631,9 +649,10 @@
     metric.mutable_value_field()->add_child()->set_field(2);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
+                                      -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs,
+                                      pullerManager);
 
     sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
 
@@ -705,14 +724,13 @@
     metric.mutable_value_field()->add_child()->set_field(2);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    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));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
     // pull 1
@@ -728,11 +746,10 @@
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
 
-    // startUpdated:true tainted:0 sum:0 start:11
+    // startUpdated:true sum:0 start:11
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
-    EXPECT_EQ(11, curInterval.start);
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(11, curInterval.start.long_value);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // pull 2 at correct time
@@ -746,13 +763,12 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // tartUpdated:false tainted:0 sum:12
+    // tartUpdated:false sum:12
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
+    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
 
     // pull 3 come late.
     // The previous bucket gets closed with error. (Has start value 23, no ending)
@@ -767,14 +783,13 @@
     valueProducer.onDataPulled(allData);
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // startUpdated:false tainted:0 sum:12
+    // startUpdated:false sum:12
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(36, curInterval.start);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(36, curInterval.start.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
+    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
 }
 
 /*
@@ -790,12 +805,12 @@
     metric.set_condition(StringToId("SCREEN_ON"));
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    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(Return(false))
             // condition becomes true
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
@@ -821,25 +836,22 @@
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // startUpdated:false tainted:0 sum:0 start:100
-    EXPECT_EQ(100, curInterval.start);
+    // startUpdated:false sum:0 start:100
+    EXPECT_EQ(100, curInterval.start.long_value);
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // pull on bucket boundary come late, condition change happens before it
     valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     EXPECT_EQ(false, curInterval.startUpdated);
-    EXPECT_EQ(1, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // Now the alarm is delivered.
@@ -855,8 +867,7 @@
 
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     EXPECT_EQ(false, curInterval.startUpdated);
-    EXPECT_EQ(1, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 }
 
@@ -873,12 +884,12 @@
     metric.set_condition(StringToId("SCREEN_ON"));
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillRepeatedly(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
 
     EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+            .WillOnce(Return(false))
             // condition becomes true
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
@@ -915,34 +926,30 @@
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // startUpdated:false tainted:0 sum:0 start:100
-    EXPECT_EQ(100, curInterval.start);
+    // startUpdated:false sum:0 start:100
+    EXPECT_EQ(100, curInterval.start.long_value);
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // pull on bucket boundary come late, condition change happens before it
     valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     EXPECT_EQ(false, curInterval.startUpdated);
-    EXPECT_EQ(1, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // condition changed to true again, before the pull alarm is delivered
     valueProducer.onConditionChanged(true, bucket2StartTimeNs + 25);
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(130, curInterval.start);
-    EXPECT_EQ(1, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(130, curInterval.start.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // Now the alarm is delivered, but it is considered late, it has no effect
@@ -957,9 +964,8 @@
 
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(130, curInterval.start);
-    EXPECT_EQ(1, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(130, curInterval.start.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 }
 
@@ -976,12 +982,12 @@
     metric.set_condition(StringToId("SCREEN_ON"));
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager =
-            make_shared<StrictMock<MockStatsPullerManager>>();
+    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(Return(false))
             // condition becomes true
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
                                 vector<std::shared_ptr<LogEvent>>* data) {
@@ -1007,17 +1013,15 @@
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    // startUpdated:false tainted:0 sum:0 start:100
-    EXPECT_EQ(100, curInterval.start);
+    // startUpdated:false sum:0 start:100
+    EXPECT_EQ(100, curInterval.start.long_value);
     EXPECT_EQ(true, curInterval.startUpdated);
-    EXPECT_EQ(0, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // pull on bucket boundary come late, condition change happens before it.
@@ -1025,8 +1029,7 @@
     valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     EXPECT_EQ(false, curInterval.startUpdated);
-    EXPECT_EQ(1, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // Alarm is delivered in time, but the pull is very slow, and pullers are called in order,
@@ -1042,11 +1045,241 @@
 
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
     EXPECT_EQ(false, curInterval.startUpdated);
-    EXPECT_EQ(1, curInterval.tainted);
-    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 }
 
+TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
+    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_aggregation_type(ValueMetric::MIN);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
+
+    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+    event1->write(1);
+    event1->write(10);
+    event1->init();
+    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
+    event2->write(1);
+    event2->write(20);
+    event2->init();
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(10, curInterval.value.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(10, curInterval.value.long_value);
+
+    valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+}
+
+TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
+    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_aggregation_type(ValueMetric::MAX);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
+
+    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+    event1->write(1);
+    event1->write(10);
+    event1->init();
+    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
+    event2->write(1);
+    event2->write(20);
+    event2->init();
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(10, curInterval.value.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(20, curInterval.value.long_value);
+
+    valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+}
+
+TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
+    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_aggregation_type(ValueMetric::AVG);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
+
+    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+    event1->write(1);
+    event1->write(10);
+    event1->init();
+    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
+    event2->write(1);
+    event2->write(15);
+    event2->init();
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval;
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(10, curInterval.value.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(1, curInterval.sampleSize);
+
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(25, curInterval.value.long_value);
+    EXPECT_EQ(2, curInterval.sampleSize);
+
+    valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(12.5, valueProducer.mPastBuckets.begin()->second.back().mValueDouble);
+}
+
+TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
+    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_aggregation_type(ValueMetric::SUM);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
+
+    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+    event1->write(1);
+    event1->write(10);
+    event1->init();
+    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
+    event2->write(1);
+    event2->write(15);
+    event2->init();
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(10, curInterval.value.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(25, curInterval.value.long_value);
+
+    valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(25, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+}
+
+TEST(ValueMetricProducerTest, TestPushedAggregateSumSliced) {
+    string slicedConditionName = "UID";
+    const int conditionTagId = 2;
+    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(1);
+    metric.set_aggregation_type(ValueMetric::SUM);
+
+    metric.set_condition(StringToId(slicedConditionName));
+    MetricConditionLink* link = metric.add_links();
+    link->set_condition(StringToId(slicedConditionName));
+    buildSimpleAtomFieldMatcher(tagId, 2, link->mutable_fields_in_what());
+    buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
+
+    LogEvent event1(tagId, bucketStartTimeNs + 10);
+    event1.write(10);  // value
+    event1.write("111"); // uid
+    event1.init();
+    ConditionKey key1;
+    key1[StringToId(slicedConditionName)] =
+        {getMockedDimensionKey(conditionTagId, 2, "111")};
+
+    LogEvent event2(tagId, bucketStartTimeNs + 20);
+    event2.write(15);
+    event2.write("222");
+    event2.init();
+    ConditionKey key2;
+    key2[StringToId(slicedConditionName)] =
+        {getMockedDimensionKey(conditionTagId, 2, "222")};
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse));
+    EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue));
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, -1, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
+
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(false, curInterval.hasValue);
+
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(15, curInterval.value.long_value);
+
+    valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(15, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index e0c98cb..b8b1a1d 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -452,14 +452,16 @@
 sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
                                               const StatsdConfig& config, const ConfigKey& key) {
     sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     sp<AlarmMonitor> anomalyAlarmMonitor =
         new AlarmMonitor(1,  [](const sp<IStatsCompanionService>&, int64_t){},
                 [](const sp<IStatsCompanionService>&){});
     sp<AlarmMonitor> periodicAlarmMonitor =
         new AlarmMonitor(1,  [](const sp<IStatsCompanionService>&, int64_t){},
                 [](const sp<IStatsCompanionService>&){});
-    sp<StatsLogProcessor> processor = new StatsLogProcessor(
-        uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, [](const ConfigKey&){return true;});
+    sp<StatsLogProcessor> processor =
+            new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+                                  timeBaseNs, [](const ConfigKey&) { return true; });
     processor->OnConfigUpdated(currentTimeNs, key, config);
     return processor;
 }
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index 6538515..950a258 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -62,7 +62,7 @@
             IBinder token = new Binder();
             try {
                 ContentProviderHolder holder = activityManager.getContentProviderExternal(
-                        providerName, UserHandle.USER_SYSTEM, token);
+                        providerName, UserHandle.USER_SYSTEM, token, "*uiautomator*");
                 if (holder == null) {
                     throw new IllegalStateException("Could not find provider: " + providerName);
                 }
diff --git a/config/hiddenapi-force-blacklist.txt b/config/hiddenapi-force-blacklist.txt
index 3a9e2d1..dca3b52 100644
--- a/config/hiddenapi-force-blacklist.txt
+++ b/config/hiddenapi-force-blacklist.txt
@@ -1,38 +1,38 @@
 Ldalvik/system/VMRuntime;->setHiddenApiExemptions([Ljava/lang/String;)V
 Ljava/lang/invoke/MethodHandles$Lookup;->IMPL_LOOKUP:Ljava/lang/invoke/MethodHandles$Lookup;
 Ljava/lang/invoke/VarHandle;->acquireFence()V
-Ljava/lang/invoke/VarHandle;->compareAndExchange([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->compareAndExchangeAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->compareAndExchangeRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->compareAndSet([[Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->compareAndExchange([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->compareAndExchangeAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->compareAndExchangeRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->compareAndSet([Ljava/lang/Object;)Z
 Ljava/lang/invoke/VarHandle;->fullFence()V
-Ljava/lang/invoke/VarHandle;->get([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndAdd([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndAddAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndAddRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseAnd([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseAndAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseAndRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseOr([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseOrAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseOrRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseXor([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseXorAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseXorRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndSet([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndSetAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndSetRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getOpaque([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getVolatile([[Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->get([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndAdd([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndAddAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndAddRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseAnd([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseAndAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseAndRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseOr([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseOrAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseOrRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseXor([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseXorAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseXorRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndSet([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndSetAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndSetRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getOpaque([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getVolatile([Ljava/lang/Object;)Ljava/lang/Object;
 Ljava/lang/invoke/VarHandle;->loadLoadFence()V
 Ljava/lang/invoke/VarHandle;->releaseFence()V
-Ljava/lang/invoke/VarHandle;->set([[Ljava/lang/Object;)V
-Ljava/lang/invoke/VarHandle;->setOpaque([[Ljava/lang/Object;)V
-Ljava/lang/invoke/VarHandle;->setRelease([[Ljava/lang/Object;)V
-Ljava/lang/invoke/VarHandle;->setVolatile([[Ljava/lang/Object;)V
+Ljava/lang/invoke/VarHandle;->set([Ljava/lang/Object;)V
+Ljava/lang/invoke/VarHandle;->setOpaque([Ljava/lang/Object;)V
+Ljava/lang/invoke/VarHandle;->setRelease([Ljava/lang/Object;)V
+Ljava/lang/invoke/VarHandle;->setVolatile([Ljava/lang/Object;)V
 Ljava/lang/invoke/VarHandle;->storeStoreFence()V
-Ljava/lang/invoke/VarHandle;->weakCompareAndSet([[Ljava/lang/Object;)Z
-Ljava/lang/invoke/VarHandle;->weakCompareAndSetAcquire([[Ljava/lang/Object;)Z
-Ljava/lang/invoke/VarHandle;->weakCompareAndSetPlain([[Ljava/lang/Object;)Z
-Ljava/lang/invoke/VarHandle;->weakCompareAndSetRelease([[Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->weakCompareAndSet([Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->weakCompareAndSetAcquire([Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->weakCompareAndSetPlain([Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->weakCompareAndSetRelease([Ljava/lang/Object;)Z
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index ab6150f..4f60b0c 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1,26 +1,5 @@
-Landroid/accessibilityservice/AccessibilityService;->mInfo:Landroid/accessibilityservice/AccessibilityServiceInfo;
-Landroid/accessibilityservice/AccessibilityService;->mWindowToken:Landroid/os/IBinder;
-Landroid/accessibilityservice/AccessibilityServiceInfo;->setCapabilities(I)V
 Landroid/accessibilityservice/IAccessibilityServiceConnection$Stub;-><init>()V
 Landroid/accessibilityservice/IAccessibilityServiceConnection$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accessibilityservice/IAccessibilityServiceConnection;
-Landroid/accounts/Account;->accessId:Ljava/lang/String;
-Landroid/accounts/Account;->TAG:Ljava/lang/String;
-Landroid/accounts/AccountAndUser;-><init>(Landroid/accounts/Account;I)V
-Landroid/accounts/AccountAndUser;->account:Landroid/accounts/Account;
-Landroid/accounts/AccountAndUser;->userId:I
-Landroid/accounts/AccountAuthenticatorResponse;-><init>(Landroid/accounts/IAccountAuthenticatorResponse;)V
-Landroid/accounts/AccountManager$AmsTask;->mActivity:Landroid/app/Activity;
-Landroid/accounts/AccountManager$AmsTask;->mHandler:Landroid/os/Handler;
-Landroid/accounts/AccountManager$AmsTask;->mResponse:Landroid/accounts/IAccountManagerResponse;
-Landroid/accounts/AccountManager$GetAuthTokenByTypeAndFeaturesTask;->mAuthTokenType:Ljava/lang/String;
-Landroid/accounts/AccountManager$GetAuthTokenByTypeAndFeaturesTask;->mLoginOptions:Landroid/os/Bundle;
-Landroid/accounts/AccountManager$GetAuthTokenByTypeAndFeaturesTask;->mMyCallback:Landroid/accounts/AccountManagerCallback;
-Landroid/accounts/AccountManager;-><init>(Landroid/content/Context;Landroid/accounts/IAccountManager;)V
-Landroid/accounts/AccountManager;->confirmCredentialsAsUser(Landroid/accounts/Account;Landroid/os/Bundle;Landroid/app/Activity;Landroid/accounts/AccountManagerCallback;Landroid/os/Handler;Landroid/os/UserHandle;)Landroid/accounts/AccountManagerFuture;
-Landroid/accounts/AccountManager;->getAccountsByTypeAsUser(Ljava/lang/String;Landroid/os/UserHandle;)[Landroid/accounts/Account;
-Landroid/accounts/AccountManager;->mContext:Landroid/content/Context;
-Landroid/accounts/AuthenticatorDescription;-><init>(Landroid/os/Parcel;)V
-Landroid/accounts/AuthenticatorDescription;-><init>(Ljava/lang/String;)V
 Landroid/accounts/IAccountAuthenticator$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/accounts/IAccountAuthenticator$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/accounts/IAccountAuthenticator$Stub;-><init>()V
@@ -51,13 +30,6 @@
 Landroid/accounts/IAccountManagerResponse$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accounts/IAccountManagerResponse;
 Landroid/accounts/IAccountManagerResponse;->onError(ILjava/lang/String;)V
 Landroid/accounts/IAccountManagerResponse;->onResult(Landroid/os/Bundle;)V
-Landroid/animation/Animator;->reverse()V
-Landroid/animation/ArgbEvaluator;->getInstance()Landroid/animation/ArgbEvaluator;
-Landroid/animation/LayoutTransition;->cancel()V
-Landroid/animation/LayoutTransition;->cancel(I)V
-Landroid/animation/ValueAnimator;->animateValue(F)V
-Landroid/animation/ValueAnimator;->mDuration:J
-Landroid/animation/ValueAnimator;->sDurationScale:F
 Landroid/app/ActionBar;->collapseActionView()Z
 Landroid/app/ActionBar;->DISPLAY_TITLE_MULTIPLE_LINES:I
 Landroid/app/ActionBar;->setShowHideAnimationEnabled(Z)V
@@ -111,13 +83,6 @@
 Landroid/app/ActivityManager$MemoryInfo;->hiddenAppThreshold:J
 Landroid/app/ActivityManager$MemoryInfo;->secondaryServerThreshold:J
 Landroid/app/ActivityManager$MemoryInfo;->visibleAppThreshold:J
-Landroid/app/ActivityManager$RecentTaskInfo;->affiliatedTaskColor:I
-Landroid/app/ActivityManager$RecentTaskInfo;->firstActiveTime:J
-Landroid/app/ActivityManager$RecentTaskInfo;->lastActiveTime:J
-Landroid/app/ActivityManager$RecentTaskInfo;->resizeMode:I
-Landroid/app/ActivityManager$RecentTaskInfo;->stackId:I
-Landroid/app/ActivityManager$RecentTaskInfo;->supportsSplitScreenMultiWindow:Z
-Landroid/app/ActivityManager$RecentTaskInfo;->userId:I
 Landroid/app/ActivityManager$RunningAppProcessInfo;->flags:I
 Landroid/app/ActivityManager$RunningAppProcessInfo;->FLAG_HAS_ACTIVITIES:I
 Landroid/app/ActivityManager$RunningAppProcessInfo;->FLAG_PERSISTENT:I
@@ -137,6 +102,7 @@
 Landroid/app/ActivityManager$StackInfo;->visible:Z
 Landroid/app/ActivityManager$TaskDescription;->getBackgroundColor()I
 Landroid/app/ActivityManager$TaskDescription;->getInMemoryIcon()Landroid/graphics/Bitmap;
+Landroid/app/ActivityManager$TaskDescription;->setIcon(Landroid/graphics/Bitmap;)V
 Landroid/app/ActivityManager$TaskSnapshot;->getContentInsets()Landroid/graphics/Rect;
 Landroid/app/ActivityManager$TaskSnapshot;->getOrientation()I
 Landroid/app/ActivityManager$TaskSnapshot;->getScale()F
@@ -355,21 +321,9 @@
 Landroid/app/ApplicationPackageManager;->getPackageSizeInfoAsUser(Ljava/lang/String;ILandroid/content/pm/IPackageStatsObserver;)V
 Landroid/app/ApplicationPackageManager;->mPM:Landroid/content/pm/IPackageManager;
 Landroid/app/ApplicationPackageManager;->shouldShowRequestPermissionRationale(Ljava/lang/String;)Z
-Landroid/app/AppOpsManager$OpEntry;->getDuration()I
-Landroid/app/AppOpsManager$OpEntry;->getLastAccessBackgroundTime()J
-Landroid/app/AppOpsManager$OpEntry;->getLastAccessForegroundTime()J
-Landroid/app/AppOpsManager$OpEntry;->getLastAccessTime()J
-Landroid/app/AppOpsManager$OpEntry;->getLastRejectBackgroundTime()J
-Landroid/app/AppOpsManager$OpEntry;->getLastRejectForegroundTime()J
-Landroid/app/AppOpsManager$OpEntry;->getLastRejectTime()J
-Landroid/app/AppOpsManager$OpEntry;->getMode()I
-Landroid/app/AppOpsManager$OpEntry;->getRejectTime()J
 Landroid/app/AppOpsManager$PackageOps;-><init>(Ljava/lang/String;ILjava/util/List;)V
-Landroid/app/AppOpsManager$PackageOps;->CREATOR:Landroid/os/Parcelable$Creator;
 Landroid/app/AppOpsManager;->checkOp(IILjava/lang/String;)I
 Landroid/app/AppOpsManager;->checkOpNoThrow(IILjava/lang/String;)I
-Landroid/app/AppOpsManager;->getOpsForPackage(ILjava/lang/String;[I)Ljava/util/List;
-Landroid/app/AppOpsManager;->MODE_FOREGROUND:I
 Landroid/app/AppOpsManager;->mService:Lcom/android/internal/app/IAppOpsService;
 Landroid/app/AppOpsManager;->noteOp(I)I
 Landroid/app/AppOpsManager;->noteOp(IILjava/lang/String;)I
@@ -457,9 +411,6 @@
 Landroid/app/AppOpsManager;->resetAllModes()V
 Landroid/app/AppOpsManager;->setRestriction(III[Ljava/lang/String;)V
 Landroid/app/AppOpsManager;->sOpPerms:[Ljava/lang/String;
-Landroid/app/AppOpsManager;->startWatchingMode(Ljava/lang/String;Ljava/lang/String;ILandroid/app/AppOpsManager$OnOpChangedListener;)V
-Landroid/app/AppOpsManager;->unsafeCheckOpRaw(Ljava/lang/String;ILjava/lang/String;)I
-Landroid/app/AppOpsManager;->WATCH_FOREGROUND_CHANGES:I
 Landroid/app/AppOpsManager;->_NUM_OP:I
 Landroid/app/assist/AssistContent;-><init>(Landroid/os/Parcel;)V
 Landroid/app/assist/AssistContent;->mClipData:Landroid/content/ClipData;
@@ -554,7 +505,7 @@
 Landroid/app/DownloadManager$Request;->mUri:Landroid/net/Uri;
 Landroid/app/DownloadManager;->getWhereArgsForIds([J)[Ljava/lang/String;
 Landroid/app/DownloadManager;->getWhereClauseForIds([J)Ljava/lang/String;
-Landroid/app/DownloadManager;->restartDownload([[J)V
+Landroid/app/DownloadManager;->restartDownload([J)V
 Landroid/app/DownloadManager;->setAccessAllDownloads(Z)V
 Landroid/app/DownloadManager;->setAccessFilename(Z)V
 Landroid/app/DownloadManager;->UNDERLYING_COLUMNS:[Ljava/lang/String;
@@ -871,9 +822,6 @@
 Landroid/app/PictureInPictureArgs;-><init>()V
 Landroid/app/PictureInPictureArgs;->setActions(Ljava/util/List;)V
 Landroid/app/PictureInPictureArgs;->setAspectRatio(F)V
-Landroid/app/PictureInPictureParams;->getActions()Ljava/util/List;
-Landroid/app/PictureInPictureParams;->getAspectRatio()F
-Landroid/app/PictureInPictureParams;->getSourceRectHint()Landroid/graphics/Rect;
 Landroid/app/Presentation;->createPresentationContext(Landroid/content/Context;Landroid/view/Display;I)Landroid/content/Context;
 Landroid/app/ProgressDialog;->mMessageView:Landroid/widget/TextView;
 Landroid/app/ProgressDialog;->mProgress:Landroid/widget/ProgressBar;
@@ -953,6 +901,11 @@
 Landroid/app/StatusBarManager;->mContext:Landroid/content/Context;
 Landroid/app/StatusBarManager;->mToken:Landroid/os/IBinder;
 Landroid/app/StatusBarManager;->setIconVisibility(Ljava/lang/String;Z)V
+Landroid/app/TaskInfo;->lastActiveTime:J
+Landroid/app/TaskInfo;->resizeMode:I
+Landroid/app/TaskInfo;->stackId:I
+Landroid/app/TaskInfo;->supportsSplitScreenMultiWindow:Z
+Landroid/app/TaskInfo;->userId:I
 Landroid/app/TaskStackListener;-><init>()V
 Landroid/app/TimePickerDialog;->mTimePicker:Landroid/widget/TimePicker;
 Landroid/app/trust/ITrustManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -1027,211 +980,12 @@
 Landroid/appwidget/AppWidgetManager;->getInstalledProvidersForProfile(ILandroid/os/UserHandle;Ljava/lang/String;)Ljava/util/List;
 Landroid/appwidget/AppWidgetManager;->mService:Lcom/android/internal/appwidget/IAppWidgetService;
 Landroid/appwidget/AppWidgetProviderInfo;->providerInfo:Landroid/content/pm/ActivityInfo;
-Landroid/bluetooth/BluetoothA2dp;->ACTION_ACTIVE_DEVICE_CHANGED:Ljava/lang/String;
-Landroid/bluetooth/BluetoothA2dp;->ACTION_CODEC_CONFIG_CHANGED:Ljava/lang/String;
-Landroid/bluetooth/BluetoothA2dp;->close()V
-Landroid/bluetooth/BluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothA2dp;->disableOptionalCodecs(Landroid/bluetooth/BluetoothDevice;)V
-Landroid/bluetooth/BluetoothA2dp;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothA2dp;->enableOptionalCodecs(Landroid/bluetooth/BluetoothDevice;)V
-Landroid/bluetooth/BluetoothA2dp;->getActiveDevice()Landroid/bluetooth/BluetoothDevice;
-Landroid/bluetooth/BluetoothA2dp;->getCodecStatus(Landroid/bluetooth/BluetoothDevice;)Landroid/bluetooth/BluetoothCodecStatus;
-Landroid/bluetooth/BluetoothA2dp;->getOptionalCodecsEnabled(Landroid/bluetooth/BluetoothDevice;)I
-Landroid/bluetooth/BluetoothA2dp;->getPriority(Landroid/bluetooth/BluetoothDevice;)I
-Landroid/bluetooth/BluetoothA2dp;->OPTIONAL_CODECS_NOT_SUPPORTED:I
-Landroid/bluetooth/BluetoothA2dp;->OPTIONAL_CODECS_PREF_DISABLED:I
-Landroid/bluetooth/BluetoothA2dp;->OPTIONAL_CODECS_PREF_ENABLED:I
-Landroid/bluetooth/BluetoothA2dp;->OPTIONAL_CODECS_PREF_UNKNOWN:I
-Landroid/bluetooth/BluetoothA2dp;->OPTIONAL_CODECS_SUPPORTED:I
-Landroid/bluetooth/BluetoothA2dp;->OPTIONAL_CODECS_SUPPORT_UNKNOWN:I
-Landroid/bluetooth/BluetoothA2dp;->setActiveDevice(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothA2dp;->setCodecConfigPreference(Landroid/bluetooth/BluetoothDevice;Landroid/bluetooth/BluetoothCodecConfig;)V
-Landroid/bluetooth/BluetoothA2dp;->setOptionalCodecsEnabled(Landroid/bluetooth/BluetoothDevice;I)V
-Landroid/bluetooth/BluetoothA2dp;->stateToString(I)Ljava/lang/String;
-Landroid/bluetooth/BluetoothA2dp;->supportsOptionalCodecs(Landroid/bluetooth/BluetoothDevice;)I
-Landroid/bluetooth/BluetoothA2dpSink;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothAdapter;->disable(Z)Z
-Landroid/bluetooth/BluetoothAdapter;->factoryReset()Z
-Landroid/bluetooth/BluetoothAdapter;->getBluetoothManager()Landroid/bluetooth/IBluetoothManager;
-Landroid/bluetooth/BluetoothAdapter;->getBluetoothService(Landroid/bluetooth/IBluetoothManagerCallback;)Landroid/bluetooth/IBluetooth;
-Landroid/bluetooth/BluetoothAdapter;->getConnectionState()I
-Landroid/bluetooth/BluetoothAdapter;->getDiscoverableTimeout()I
-Landroid/bluetooth/BluetoothAdapter;->getLeState()I
-Landroid/bluetooth/BluetoothAdapter;->getUuids()[Landroid/os/ParcelUuid;
-Landroid/bluetooth/BluetoothAdapter;->listenUsingEncryptedRfcommWithServiceRecord(Ljava/lang/String;Ljava/util/UUID;)Landroid/bluetooth/BluetoothServerSocket;
-Landroid/bluetooth/BluetoothAdapter;->listenUsingRfcommOn(IZZ)Landroid/bluetooth/BluetoothServerSocket;
-Landroid/bluetooth/BluetoothAdapter;->mService:Landroid/bluetooth/IBluetooth;
-Landroid/bluetooth/BluetoothAdapter;->setDiscoverableTimeout(I)V
-Landroid/bluetooth/BluetoothAdapter;->setScanMode(I)Z
-Landroid/bluetooth/BluetoothAdapter;->setScanMode(II)Z
-Landroid/bluetooth/BluetoothClass;-><init>(I)V
-Landroid/bluetooth/BluetoothClass;->doesClassMatch(I)Z
-Landroid/bluetooth/BluetoothClass;->PROFILE_A2DP:I
-Landroid/bluetooth/BluetoothClass;->PROFILE_HEADSET:I
-Landroid/bluetooth/BluetoothCodecConfig;
-Landroid/bluetooth/BluetoothCodecConfig;-><init>(IIIIIJJJJ)V
-Landroid/bluetooth/BluetoothCodecConfig;->BITS_PER_SAMPLE_16:I
-Landroid/bluetooth/BluetoothCodecConfig;->BITS_PER_SAMPLE_24:I
-Landroid/bluetooth/BluetoothCodecConfig;->BITS_PER_SAMPLE_32:I
-Landroid/bluetooth/BluetoothCodecConfig;->BITS_PER_SAMPLE_NONE:I
-Landroid/bluetooth/BluetoothCodecConfig;->CHANNEL_MODE_MONO:I
-Landroid/bluetooth/BluetoothCodecConfig;->CHANNEL_MODE_NONE:I
-Landroid/bluetooth/BluetoothCodecConfig;->CHANNEL_MODE_STEREO:I
-Landroid/bluetooth/BluetoothCodecConfig;->CODEC_PRIORITY_DEFAULT:I
-Landroid/bluetooth/BluetoothCodecConfig;->CODEC_PRIORITY_DISABLED:I
-Landroid/bluetooth/BluetoothCodecConfig;->CODEC_PRIORITY_HIGHEST:I
-Landroid/bluetooth/BluetoothCodecConfig;->getBitsPerSample()I
-Landroid/bluetooth/BluetoothCodecConfig;->getChannelMode()I
-Landroid/bluetooth/BluetoothCodecConfig;->getCodecPriority()I
-Landroid/bluetooth/BluetoothCodecConfig;->getCodecSpecific1()J
-Landroid/bluetooth/BluetoothCodecConfig;->getCodecSpecific2()J
-Landroid/bluetooth/BluetoothCodecConfig;->getCodecSpecific3()J
-Landroid/bluetooth/BluetoothCodecConfig;->getCodecSpecific4()J
-Landroid/bluetooth/BluetoothCodecConfig;->getCodecType()I
-Landroid/bluetooth/BluetoothCodecConfig;->getSampleRate()I
-Landroid/bluetooth/BluetoothCodecConfig;->SAMPLE_RATE_176400:I
-Landroid/bluetooth/BluetoothCodecConfig;->SAMPLE_RATE_192000:I
-Landroid/bluetooth/BluetoothCodecConfig;->SAMPLE_RATE_44100:I
-Landroid/bluetooth/BluetoothCodecConfig;->SAMPLE_RATE_48000:I
-Landroid/bluetooth/BluetoothCodecConfig;->SAMPLE_RATE_88200:I
-Landroid/bluetooth/BluetoothCodecConfig;->SAMPLE_RATE_96000:I
-Landroid/bluetooth/BluetoothCodecConfig;->SAMPLE_RATE_NONE:I
-Landroid/bluetooth/BluetoothCodecConfig;->setCodecPriority(I)V
-Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_AAC:I
-Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_APTX:I
-Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_APTX_HD:I
-Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_INVALID:I
-Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_LDAC:I
-Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_MAX:I
-Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_SBC:I
-Landroid/bluetooth/BluetoothCodecStatus;
-Landroid/bluetooth/BluetoothCodecStatus;->EXTRA_CODEC_STATUS:Ljava/lang/String;
-Landroid/bluetooth/BluetoothCodecStatus;->getCodecConfig()Landroid/bluetooth/BluetoothCodecConfig;
-Landroid/bluetooth/BluetoothCodecStatus;->getCodecsLocalCapabilities()[Landroid/bluetooth/BluetoothCodecConfig;
-Landroid/bluetooth/BluetoothCodecStatus;->getCodecsSelectableCapabilities()[Landroid/bluetooth/BluetoothCodecConfig;
-Landroid/bluetooth/BluetoothDevice;-><init>(Ljava/lang/String;)V
-Landroid/bluetooth/BluetoothDevice;->ACTION_ALIAS_CHANGED:Ljava/lang/String;
-Landroid/bluetooth/BluetoothDevice;->ACTION_DISAPPEARED:Ljava/lang/String;
-Landroid/bluetooth/BluetoothDevice;->ACTION_PAIRING_CANCEL:Ljava/lang/String;
-Landroid/bluetooth/BluetoothDevice;->ACTION_SDP_RECORD:Ljava/lang/String;
-Landroid/bluetooth/BluetoothDevice;->cancelPairingUserInput()Z
-Landroid/bluetooth/BluetoothDevice;->connectGatt(Landroid/content/Context;ZLandroid/bluetooth/BluetoothGattCallback;IZILandroid/os/Handler;)Landroid/bluetooth/BluetoothGatt;
-Landroid/bluetooth/BluetoothDevice;->convertPinToBytes(Ljava/lang/String;)[B
-Landroid/bluetooth/BluetoothDevice;->createBond(I)Z
-Landroid/bluetooth/BluetoothDevice;->createInsecureRfcommSocket(I)Landroid/bluetooth/BluetoothSocket;
-Landroid/bluetooth/BluetoothDevice;->createRfcommSocket(I)Landroid/bluetooth/BluetoothSocket;
-Landroid/bluetooth/BluetoothDevice;->createScoSocket()Landroid/bluetooth/BluetoothSocket;
-Landroid/bluetooth/BluetoothDevice;->EXTRA_REASON:Ljava/lang/String;
-Landroid/bluetooth/BluetoothDevice;->EXTRA_SDP_SEARCH_STATUS:Ljava/lang/String;
-Landroid/bluetooth/BluetoothDevice;->getAlias()Ljava/lang/String;
-Landroid/bluetooth/BluetoothDevice;->getAliasName()Ljava/lang/String;
-Landroid/bluetooth/BluetoothDevice;->getBatteryLevel()I
-Landroid/bluetooth/BluetoothDevice;->getMessageAccessPermission()I
-Landroid/bluetooth/BluetoothDevice;->getPhonebookAccessPermission()I
-Landroid/bluetooth/BluetoothDevice;->getService()Landroid/bluetooth/IBluetooth;
-Landroid/bluetooth/BluetoothDevice;->isBluetoothDock()Z
-Landroid/bluetooth/BluetoothDevice;->isBondingInitiatedLocally()Z
-Landroid/bluetooth/BluetoothDevice;->setAlias(Ljava/lang/String;)Z
-Landroid/bluetooth/BluetoothDevice;->setMessageAccessPermission(I)Z
-Landroid/bluetooth/BluetoothDevice;->setPasskey(I)Z
-Landroid/bluetooth/BluetoothDevice;->setSimAccessPermission(I)Z
-Landroid/bluetooth/BluetoothDevice;->UNBOND_REASON_AUTH_FAILED:I
-Landroid/bluetooth/BluetoothDevice;->UNBOND_REASON_AUTH_REJECTED:I
-Landroid/bluetooth/BluetoothDevice;->UNBOND_REASON_AUTH_TIMEOUT:I
-Landroid/bluetooth/BluetoothDevice;->UNBOND_REASON_DISCOVERY_IN_PROGRESS:I
-Landroid/bluetooth/BluetoothDevice;->UNBOND_REASON_REMOTE_AUTH_CANCELED:I
-Landroid/bluetooth/BluetoothDevice;->UNBOND_REASON_REMOTE_DEVICE_DOWN:I
-Landroid/bluetooth/BluetoothDevice;->UNBOND_REASON_REPEATED_ATTEMPTS:I
-Landroid/bluetooth/BluetoothGatt;->connect(Ljava/lang/Boolean;Landroid/bluetooth/BluetoothGattCallback;Landroid/os/Handler;)Z
-Landroid/bluetooth/BluetoothGatt;->mAuthRetryState:I
-Landroid/bluetooth/BluetoothGatt;->mAutoConnect:Z
-Landroid/bluetooth/BluetoothGatt;->mCallback:Landroid/bluetooth/BluetoothGattCallback;
-Landroid/bluetooth/BluetoothGatt;->mClientIf:I
-Landroid/bluetooth/BluetoothGatt;->mDeviceBusy:Ljava/lang/Boolean;
-Landroid/bluetooth/BluetoothGatt;->mService:Landroid/bluetooth/IBluetoothGatt;
-Landroid/bluetooth/BluetoothGatt;->mTransport:I
-Landroid/bluetooth/BluetoothGatt;->refresh()Z
-Landroid/bluetooth/BluetoothGatt;->unregisterApp()V
-Landroid/bluetooth/BluetoothGattCharacteristic;->mInstance:I
-Landroid/bluetooth/BluetoothGattCharacteristic;->mService:Landroid/bluetooth/BluetoothGattService;
-Landroid/bluetooth/BluetoothGattCharacteristic;->setKeySize(I)V
-Landroid/bluetooth/BluetoothGattCharacteristic;->setService(Landroid/bluetooth/BluetoothGattService;)V
-Landroid/bluetooth/BluetoothGattDescriptor;->mCharacteristic:Landroid/bluetooth/BluetoothGattCharacteristic;
-Landroid/bluetooth/BluetoothGattDescriptor;->mInstance:I
-Landroid/bluetooth/BluetoothGattDescriptor;->setCharacteristic(Landroid/bluetooth/BluetoothGattCharacteristic;)V
-Landroid/bluetooth/BluetoothGattService;->mDevice:Landroid/bluetooth/BluetoothDevice;
-Landroid/bluetooth/BluetoothGattService;->setAdvertisePreferred(Z)V
-Landroid/bluetooth/BluetoothGattService;->setInstanceId(I)V
-Landroid/bluetooth/BluetoothHeadset;->ACTION_ACTIVE_DEVICE_CHANGED:Ljava/lang/String;
-Landroid/bluetooth/BluetoothHeadset;->close()V
-Landroid/bluetooth/BluetoothHeadset;->connectAudio()Z
-Landroid/bluetooth/BluetoothHeadset;->disconnectAudio()Z
-Landroid/bluetooth/BluetoothHeadset;->getActiveDevice()Landroid/bluetooth/BluetoothDevice;
-Landroid/bluetooth/BluetoothHeadset;->getAudioState(Landroid/bluetooth/BluetoothDevice;)I
-Landroid/bluetooth/BluetoothHeadset;->getPriority(Landroid/bluetooth/BluetoothDevice;)I
-Landroid/bluetooth/BluetoothHeadset;->isEnabled()Z
-Landroid/bluetooth/BluetoothHeadset;->setActiveDevice(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothHeadset;->startScoUsingVirtualVoiceCall()Z
-Landroid/bluetooth/BluetoothHeadset;->stopScoUsingVirtualVoiceCall()Z
-Landroid/bluetooth/BluetoothHeadsetClient;->acceptCall(Landroid/bluetooth/BluetoothDevice;I)Z
-Landroid/bluetooth/BluetoothHeadsetClient;->connect(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothHeadsetClient;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothHeadsetClient;->getAudioState(Landroid/bluetooth/BluetoothDevice;)I
-Landroid/bluetooth/BluetoothHeadsetClient;->rejectCall(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothHeadsetClientCall;->getId()I
-Landroid/bluetooth/BluetoothHeadsetClientCall;->getNumber()Ljava/lang/String;
-Landroid/bluetooth/BluetoothHeadsetClientCall;->getState()I
-Landroid/bluetooth/BluetoothHeadsetClientCall;->isMultiParty()Z
-Landroid/bluetooth/BluetoothHeadsetClientCall;->isOutgoing()Z
-Landroid/bluetooth/BluetoothHearingAid;->ACTION_ACTIVE_DEVICE_CHANGED:Ljava/lang/String;
-Landroid/bluetooth/BluetoothHearingAid;->getActiveDevices()Ljava/util/List;
-Landroid/bluetooth/BluetoothHearingAid;->setActiveDevice(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothMap;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothMapClient;->sendMessage(Landroid/bluetooth/BluetoothDevice;[Landroid/net/Uri;Ljava/lang/String;Landroid/app/PendingIntent;Landroid/app/PendingIntent;)Z
-Landroid/bluetooth/BluetoothPan;-><init>(Landroid/content/Context;Landroid/bluetooth/BluetoothProfile$ServiceListener;)V
-Landroid/bluetooth/BluetoothPan;->close()V
-Landroid/bluetooth/BluetoothPan;->connect(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothPan;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothPan;->doBind()Z
-Landroid/bluetooth/BluetoothPan;->isEnabled()Z
-Landroid/bluetooth/BluetoothPan;->isTetheringOn()Z
-Landroid/bluetooth/BluetoothPan;->isValidDevice(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothPan;->log(Ljava/lang/String;)V
-Landroid/bluetooth/BluetoothPan;->setBluetoothTethering(Z)V
-Landroid/bluetooth/BluetoothPbap;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothProfile;->A2DP_SINK:I
-Landroid/bluetooth/BluetoothProfile;->AVRCP_CONTROLLER:I
-Landroid/bluetooth/BluetoothProfile;->PAN:I
-Landroid/bluetooth/BluetoothProfile;->PRIORITY_AUTO_CONNECT:I
-Landroid/bluetooth/BluetoothProfile;->PRIORITY_UNDEFINED:I
-Landroid/bluetooth/BluetoothSap;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z
-Landroid/bluetooth/BluetoothServerSocket;->mSocket:Landroid/bluetooth/BluetoothSocket;
-Landroid/bluetooth/BluetoothSocket;->EADDRINUSE:I
-Landroid/bluetooth/BluetoothSocket;->flush()V
-Landroid/bluetooth/BluetoothSocket;->mPfd:Landroid/os/ParcelFileDescriptor;
-Landroid/bluetooth/BluetoothSocket;->mPort:I
-Landroid/bluetooth/BluetoothSocket;->mSocket:Landroid/net/LocalSocket;
-Landroid/bluetooth/BluetoothUuid;->AdvAudioDist:Landroid/os/ParcelUuid;
-Landroid/bluetooth/BluetoothUuid;->AudioSink:Landroid/os/ParcelUuid;
-Landroid/bluetooth/BluetoothUuid;->containsAnyUuid([Landroid/os/ParcelUuid;[Landroid/os/ParcelUuid;)Z
-Landroid/bluetooth/BluetoothUuid;->Handsfree:Landroid/os/ParcelUuid;
-Landroid/bluetooth/BluetoothUuid;->Hogp:Landroid/os/ParcelUuid;
-Landroid/bluetooth/BluetoothUuid;->HSP:Landroid/os/ParcelUuid;
-Landroid/bluetooth/BluetoothUuid;->is16BitUuid(Landroid/os/ParcelUuid;)Z
-Landroid/bluetooth/BluetoothUuid;->is32BitUuid(Landroid/os/ParcelUuid;)Z
-Landroid/bluetooth/BluetoothUuid;->isAdvAudioDist(Landroid/os/ParcelUuid;)Z
-Landroid/bluetooth/BluetoothUuid;->isAudioSource(Landroid/os/ParcelUuid;)Z
-Landroid/bluetooth/BluetoothUuid;->isAvrcpTarget(Landroid/os/ParcelUuid;)Z
-Landroid/bluetooth/BluetoothUuid;->isUuidPresent([Landroid/os/ParcelUuid;Landroid/os/ParcelUuid;)Z
-Landroid/bluetooth/BluetoothUuid;->NAP:Landroid/os/ParcelUuid;
-Landroid/bluetooth/BluetoothUuid;->ObexObjectPush:Landroid/os/ParcelUuid;
-Landroid/bluetooth/BluetoothUuid;->PBAP_PSE:Landroid/os/ParcelUuid;
-Landroid/bluetooth/BluetoothUuid;->RESERVED_UUIDS:[Landroid/os/ParcelUuid;
 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
 Landroid/bluetooth/IBluetooth;->getAddress()Ljava/lang/String;
 Landroid/bluetooth/IBluetooth;->getRemoteAlias(Landroid/bluetooth/BluetoothDevice;)Ljava/lang/String;
 Landroid/bluetooth/IBluetooth;->isEnabled()Z
@@ -1265,7 +1019,6 @@
 Landroid/bluetooth/IBluetoothManagerCallback$Stub;-><init>()V
 Landroid/bluetooth/IBluetoothPbap$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothPbap;
 Landroid/bluetooth/IBluetoothStateChangeCallback$Stub;-><init>()V
-Landroid/bluetooth/le/ScanRecord;->parseFromBytes([B)Landroid/bluetooth/le/ScanRecord;
 Landroid/content/AsyncTaskLoader;->mExecutor:Ljava/util/concurrent/Executor;
 Landroid/content/AsyncTaskLoader;->waitForLoader()V
 Landroid/content/BroadcastReceiver$PendingResult;-><init>(ILjava/lang/String;Landroid/os/Bundle;IZZLandroid/os/IBinder;II)V
@@ -1460,8 +1213,6 @@
 Landroid/content/pm/ApplicationInfo;->isPackageUnavailable(Landroid/content/pm/PackageManager;)Z
 Landroid/content/pm/ApplicationInfo;->nativeLibraryRootDir:Ljava/lang/String;
 Landroid/content/pm/ApplicationInfo;->primaryCpuAbi:Ljava/lang/String;
-Landroid/content/pm/ApplicationInfo;->privateFlags:I
-Landroid/content/pm/ApplicationInfo;->PRIVATE_FLAG_PRIVILEGED:I
 Landroid/content/pm/ApplicationInfo;->resourceDirs:[Ljava/lang/String;
 Landroid/content/pm/ApplicationInfo;->scanPublicSourceDir:Ljava/lang/String;
 Landroid/content/pm/ApplicationInfo;->scanSourceDir:Ljava/lang/String;
@@ -1473,10 +1224,15 @@
 Landroid/content/pm/ComponentInfo;->getComponentName()Landroid/content/ComponentName;
 Landroid/content/pm/IPackageDataObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->onRemoveCompleted(Ljava/lang/String;Z)V
 Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver;
+Landroid/content/pm/IPackageDataObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/content/pm/IPackageDataObserver$Stub;->TRANSACTION_onRemoveCompleted:I
 Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V
 Landroid/content/pm/IPackageDeleteObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IPackageDeleteObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver;
+Landroid/content/pm/IPackageDeleteObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/content/pm/IPackageDeleteObserver$Stub;->TRANSACTION_packageDeleted:I
 Landroid/content/pm/IPackageDeleteObserver2$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IPackageDeleteObserver2$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/content/pm/IPackageDeleteObserver2$Stub;-><init>()V
@@ -1506,6 +1262,7 @@
 Landroid/content/pm/IPackageManager$Stub$Proxy;->getAppOpPermissionPackages(Ljava/lang/String;)[Ljava/lang/String;
 Landroid/content/pm/IPackageManager$Stub$Proxy;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice;
 Landroid/content/pm/IPackageManager$Stub$Proxy;->getInstallLocation()I
+Landroid/content/pm/IPackageManager$Stub$Proxy;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
 Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
 Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackagesForUid(I)[Ljava/lang/String;
 Landroid/content/pm/IPackageManager$Stub$Proxy;->getSystemSharedLibraryNames()[Ljava/lang/String;
@@ -1574,6 +1331,8 @@
 Landroid/content/pm/IPackageStatsObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/content/pm/IPackageStatsObserver$Stub;-><init>()V
 Landroid/content/pm/IPackageStatsObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageStatsObserver;
+Landroid/content/pm/IPackageStatsObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/content/pm/IPackageStatsObserver$Stub;->TRANSACTION_onGetStatsCompleted:I
 Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IShortcutService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IShortcutService;
 Landroid/content/pm/LauncherActivityInfo;->mActivityInfo:Landroid/content/pm/ActivityInfo;
@@ -1736,6 +1495,7 @@
 Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Ljava/io/File;Z)V
 Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Z)V
 Landroid/content/pm/PackageParser;->generateActivityInfo(Landroid/content/pm/PackageParser$Activity;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ActivityInfo;
+Landroid/content/pm/PackageParser;->generateApplicationInfo(Landroid/content/pm/PackageParser$Package;ILandroid/content/pm/PackageUserState;)Landroid/content/pm/ApplicationInfo;
 Landroid/content/pm/PackageParser;->generateApplicationInfo(Landroid/content/pm/PackageParser$Package;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ApplicationInfo;
 Landroid/content/pm/PackageParser;->generateInstrumentationInfo(Landroid/content/pm/PackageParser$Instrumentation;I)Landroid/content/pm/InstrumentationInfo;
 Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;)Landroid/content/pm/PackageInfo;
@@ -1747,6 +1507,7 @@
 Landroid/content/pm/PackageParser;->mCallback:Landroid/content/pm/PackageParser$Callback;
 Landroid/content/pm/PackageParser;->NEW_PERMISSIONS:[Landroid/content/pm/PackageParser$NewPermissionInfo;
 Landroid/content/pm/PackageParser;->parseBaseApk(Ljava/lang/String;Landroid/content/res/Resources;Landroid/content/res/XmlResourceParser;I[Ljava/lang/String;)Landroid/content/pm/PackageParser$Package;
+Landroid/content/pm/PackageParser;->parseBaseApplication(Landroid/content/pm/PackageParser$Package;Landroid/content/res/Resources;Landroid/content/res/XmlResourceParser;I[Ljava/lang/String;)Z
 Landroid/content/pm/PackageParser;->parseMonolithicPackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package;
 Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package;
 Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/content/pm/PackageParser$Package;
@@ -1830,6 +1591,7 @@
 Landroid/content/res/AssetManager;->setConfiguration(IILjava/lang/String;IIIIIIIIIIIIIII)V
 Landroid/content/res/AssetManager;->sSystem:Landroid/content/res/AssetManager;
 Landroid/content/res/ColorStateList$ColorStateListFactory;-><init>(Landroid/content/res/ColorStateList;)V
+Landroid/content/res/ColorStateList;-><init>()V
 Landroid/content/res/ColorStateList;->canApplyTheme()Z
 Landroid/content/res/ColorStateList;->getColors()[I
 Landroid/content/res/ColorStateList;->getStates()[[I
@@ -1892,6 +1654,7 @@
 Landroid/content/res/Resources;->updateSystemConfiguration(Landroid/content/res/Configuration;Landroid/util/DisplayMetrics;Landroid/content/res/CompatibilityInfo;)V
 Landroid/content/res/ResourcesImpl;-><init>(Landroid/content/res/AssetManager;Landroid/util/DisplayMetrics;Landroid/content/res/Configuration;Landroid/view/DisplayAdjustments;)V
 Landroid/content/res/ResourcesImpl;->getAssets()Landroid/content/res/AssetManager;
+Landroid/content/res/ResourcesImpl;->getDisplayMetrics()Landroid/util/DisplayMetrics;
 Landroid/content/res/ResourcesImpl;->getValue(ILandroid/util/TypedValue;Z)V
 Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object;
 Landroid/content/res/ResourcesImpl;->mAnimatorCache:Landroid/content/res/ConfigurationBoundResourceCache;
@@ -2040,574 +1803,21 @@
 Landroid/database/sqlite/SqliteWrapper;->checkSQLiteException(Landroid/content/Context;Landroid/database/sqlite/SQLiteException;)V
 Landroid/database/sqlite/SqliteWrapper;->delete(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I
 Landroid/database/sqlite/SqliteWrapper;->update(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I
-Landroid/ddm/DdmHandleAppName;->getAppName()Ljava/lang/String;
-Landroid/ddm/DdmHandleAppName;->setAppName(Ljava/lang/String;I)V
-Landroid/filterfw/core/Filter;-><init>(Ljava/lang/String;)V
-Landroid/filterfw/core/Filter;->isAvailable(Ljava/lang/String;)Z
-Landroid/filterfw/core/Filter;->setInputValue(Ljava/lang/String;Ljava/lang/Object;)V
-Landroid/filterfw/core/FilterContext;->getFrameManager()Landroid/filterfw/core/FrameManager;
-Landroid/filterfw/core/FilterContext;->getGLEnvironment()Landroid/filterfw/core/GLEnvironment;
-Landroid/filterfw/core/FilterGraph;->getFilter(Ljava/lang/String;)Landroid/filterfw/core/Filter;
-Landroid/filterfw/core/FilterGraph;->tearDown(Landroid/filterfw/core/FilterContext;)V
-Landroid/filterfw/core/Frame;->getBitmap()Landroid/graphics/Bitmap;
-Landroid/filterfw/core/Frame;->getFormat()Landroid/filterfw/core/FrameFormat;
-Landroid/filterfw/core/Frame;->getTimestamp()J
-Landroid/filterfw/core/Frame;->release()Landroid/filterfw/core/Frame;
-Landroid/filterfw/core/Frame;->setInts([I)V
-Landroid/filterfw/core/Frame;->setTimestamp(J)V
-Landroid/filterfw/core/FrameFormat;->getHeight()I
-Landroid/filterfw/core/FrameFormat;->getTarget()I
-Landroid/filterfw/core/FrameFormat;->getWidth()I
-Landroid/filterfw/core/FrameFormat;->mutableCopy()Landroid/filterfw/core/MutableFrameFormat;
-Landroid/filterfw/core/FrameManager;->duplicateFrame(Landroid/filterfw/core/Frame;)Landroid/filterfw/core/Frame;
-Landroid/filterfw/core/FrameManager;->newBoundFrame(Landroid/filterfw/core/FrameFormat;IJ)Landroid/filterfw/core/Frame;
-Landroid/filterfw/core/FrameManager;->newFrame(Landroid/filterfw/core/FrameFormat;)Landroid/filterfw/core/Frame;
-Landroid/filterfw/core/GLEnvironment;->activate()V
-Landroid/filterfw/core/GLEnvironment;->activateSurfaceWithId(I)V
-Landroid/filterfw/core/GLEnvironment;->deactivate()V
-Landroid/filterfw/core/GLEnvironment;->isActive()Z
-Landroid/filterfw/core/GLEnvironment;->registerSurfaceFromMediaRecorder(Landroid/media/MediaRecorder;)I
-Landroid/filterfw/core/GLEnvironment;->setSurfaceTimestamp(J)V
-Landroid/filterfw/core/GLEnvironment;->swapBuffers()V
-Landroid/filterfw/core/GLEnvironment;->unregisterSurfaceId(I)V
-Landroid/filterfw/core/GLFrame;->generateMipMap()V
-Landroid/filterfw/core/GLFrame;->getTextureId()I
-Landroid/filterfw/core/GLFrame;->setBitmap(Landroid/graphics/Bitmap;)V
-Landroid/filterfw/core/GLFrame;->setTextureParameter(II)V
-Landroid/filterfw/core/GraphRunner;->getError()Ljava/lang/Exception;
-Landroid/filterfw/core/GraphRunner;->getGraph()Landroid/filterfw/core/FilterGraph;
-Landroid/filterfw/core/GraphRunner;->run()V
-Landroid/filterfw/core/GraphRunner;->setDoneCallback(Landroid/filterfw/core/GraphRunner$OnRunnerDoneListener;)V
-Landroid/filterfw/core/GraphRunner;->stop()V
-Landroid/filterfw/core/MutableFrameFormat;-><init>(II)V
-Landroid/filterfw/core/MutableFrameFormat;->setBytesPerSample(I)V
-Landroid/filterfw/core/MutableFrameFormat;->setDimensions(II)V
-Landroid/filterfw/core/Program;->process(Landroid/filterfw/core/Frame;Landroid/filterfw/core/Frame;)V
-Landroid/filterfw/core/Program;->process([Landroid/filterfw/core/Frame;Landroid/filterfw/core/Frame;)V
-Landroid/filterfw/core/Program;->setHostValue(Ljava/lang/String;Ljava/lang/Object;)V
-Landroid/filterfw/core/ShaderProgram;-><init>(Landroid/filterfw/core/FilterContext;Ljava/lang/String;)V
-Landroid/filterfw/core/ShaderProgram;->createIdentity(Landroid/filterfw/core/FilterContext;)Landroid/filterfw/core/ShaderProgram;
-Landroid/filterfw/core/ShaderProgram;->process([Landroid/filterfw/core/Frame;Landroid/filterfw/core/Frame;)V
-Landroid/filterfw/core/ShaderProgram;->setHostValue(Ljava/lang/String;Ljava/lang/Object;)V
-Landroid/filterfw/core/ShaderProgram;->setMaximumTileSize(I)V
-Landroid/filterfw/core/ShaderProgram;->setSourceRect(FFFF)V
-Landroid/filterfw/core/ShaderProgram;->setSourceRegion(Landroid/filterfw/geometry/Quad;)V
-Landroid/filterfw/format/ImageFormat;->create(I)Landroid/filterfw/core/MutableFrameFormat;
-Landroid/filterfw/format/ImageFormat;->create(II)Landroid/filterfw/core/MutableFrameFormat;
-Landroid/filterfw/format/ImageFormat;->create(IIII)Landroid/filterfw/core/MutableFrameFormat;
-Landroid/filterfw/geometry/Point;-><init>()V
-Landroid/filterfw/geometry/Point;-><init>(FF)V
-Landroid/filterfw/geometry/Point;->x:F
-Landroid/filterfw/geometry/Point;->y:F
-Landroid/filterfw/geometry/Quad;-><init>()V
-Landroid/filterfw/geometry/Quad;-><init>(Landroid/filterfw/geometry/Point;Landroid/filterfw/geometry/Point;Landroid/filterfw/geometry/Point;Landroid/filterfw/geometry/Point;)V
-Landroid/filterfw/geometry/Quad;->p0:Landroid/filterfw/geometry/Point;
-Landroid/filterfw/geometry/Quad;->p1:Landroid/filterfw/geometry/Point;
-Landroid/filterfw/geometry/Quad;->p2:Landroid/filterfw/geometry/Point;
-Landroid/filterfw/geometry/Quad;->p3:Landroid/filterfw/geometry/Point;
-Landroid/filterfw/GraphEnvironment;-><init>()V
-Landroid/filterfw/GraphEnvironment;->getRunner(II)Landroid/filterfw/core/GraphRunner;
-Landroid/filterfw/GraphEnvironment;->loadGraph(Landroid/content/Context;I)I
-Landroid/graphics/BaseCanvas;->mNativeCanvasWrapper:J
-Landroid/graphics/Bitmap$Config;->nativeInt:I
-Landroid/graphics/Bitmap$Config;->nativeToConfig(I)Landroid/graphics/Bitmap$Config;
-Landroid/graphics/Bitmap;-><init>(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;)V
-Landroid/graphics/Bitmap;->createAshmemBitmap()Landroid/graphics/Bitmap;
-Landroid/graphics/Bitmap;->createAshmemBitmap(Landroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;
-Landroid/graphics/Bitmap;->getDefaultDensity()I
-Landroid/graphics/Bitmap;->mHeight:I
-Landroid/graphics/Bitmap;->mNativePtr:J
-Landroid/graphics/Bitmap;->mNinePatchChunk:[B
-Landroid/graphics/Bitmap;->mNinePatchInsets:Landroid/graphics/NinePatch$InsetStruct;
-Landroid/graphics/Bitmap;->mWidth:I
-Landroid/graphics/Bitmap;->nativeReconfigure(JIIIZ)V
-Landroid/graphics/Bitmap;->reinit(IIZ)V
-Landroid/graphics/Bitmap;->scaleFromDensity(III)I
-Landroid/graphics/Bitmap;->setDefaultDensity(I)V
-Landroid/graphics/Bitmap;->setNinePatchChunk([B)V
-Landroid/graphics/BitmapFactory;->nativeDecodeAsset(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;
-Landroid/graphics/BitmapFactory;->nativeDecodeByteArray([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;
-Landroid/graphics/BitmapFactory;->nativeDecodeFileDescriptor(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;
-Landroid/graphics/BitmapFactory;->nativeDecodeStream(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;
-Landroid/graphics/BitmapRegionDecoder;-><init>(J)V
-Landroid/graphics/BitmapRegionDecoder;->nativeNewInstance([BIIZ)Landroid/graphics/BitmapRegionDecoder;
-Landroid/graphics/BitmapShader;->mBitmap:Landroid/graphics/Bitmap;
-Landroid/graphics/BitmapShader;->mTileX:I
-Landroid/graphics/BitmapShader;->mTileY:I
-Landroid/graphics/Camera;->native_instance:J
-Landroid/graphics/Canvas;-><init>(J)V
-Landroid/graphics/Canvas;->freeCaches()V
-Landroid/graphics/Canvas;->freeTextLayoutCaches()V
-Landroid/graphics/Canvas;->getGL()Ljavax/microedition/khronos/opengles/GL;
-Landroid/graphics/Canvas;->getNativeCanvasWrapper()J
-Landroid/graphics/Canvas;->mBitmap:Landroid/graphics/Bitmap;
-Landroid/graphics/Canvas;->release()V
-Landroid/graphics/Canvas;->setScreenDensity(I)V
-Landroid/graphics/CanvasProperty;->createFloat(F)Landroid/graphics/CanvasProperty;
-Landroid/graphics/CanvasProperty;->createPaint(Landroid/graphics/Paint;)Landroid/graphics/CanvasProperty;
-Landroid/graphics/ColorMatrixColorFilter;->mMatrix:Landroid/graphics/ColorMatrix;
-Landroid/graphics/ColorMatrixColorFilter;->setColorMatrix(Landroid/graphics/ColorMatrix;)V
-Landroid/graphics/drawable/AnimatedImageDrawable;->onAnimationEnd()V
-Landroid/graphics/drawable/AnimatedRotateDrawable;->setFramesCount(I)V
-Landroid/graphics/drawable/AnimatedRotateDrawable;->setFramesDuration(I)V
-Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState;->mStateIds:Landroid/util/SparseIntArray;
-Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState;->mTransitions:Landroid/util/LongSparseLongArray;
-Landroid/graphics/drawable/AnimatedStateListDrawable;->mState:Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState;
-Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;->callOnFinished(Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V
-Landroid/graphics/drawable/AnimatedVectorDrawable;->forceAnimationOnUI()V
-Landroid/graphics/drawable/AnimatedVectorDrawable;->mAnimatedVectorState:Landroid/graphics/drawable/AnimatedVectorDrawable$AnimatedVectorDrawableState;
-Landroid/graphics/drawable/AnimatedVectorDrawable;->mAnimatorSet:Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator;
-Landroid/graphics/drawable/AnimationDrawable;->mCurFrame:I
-Landroid/graphics/drawable/BitmapDrawable;->getTint()Landroid/content/res/ColorStateList;
-Landroid/graphics/drawable/BitmapDrawable;->getTintMode()Landroid/graphics/PorterDuff$Mode;
-Landroid/graphics/drawable/BitmapDrawable;->mBitmapState:Landroid/graphics/drawable/BitmapDrawable$BitmapState;
-Landroid/graphics/drawable/BitmapDrawable;->mTargetDensity:I
-Landroid/graphics/drawable/BitmapDrawable;->setBitmap(Landroid/graphics/Bitmap;)V
-Landroid/graphics/drawable/ClipDrawable;->mState:Landroid/graphics/drawable/ClipDrawable$ClipState;
-Landroid/graphics/drawable/ColorDrawable$ColorState;->mUseColor:I
-Landroid/graphics/drawable/ColorDrawable;->mPaint:Landroid/graphics/Paint;
-Landroid/graphics/drawable/Drawable;->inflateWithAttributes(Landroid/content/res/Resources;Lorg/xmlpull/v1/XmlPullParser;Landroid/content/res/TypedArray;I)V
-Landroid/graphics/drawable/Drawable;->mCallback:Ljava/lang/ref/WeakReference;
-Landroid/graphics/drawable/Drawable;->mSrcDensityOverride:I
-Landroid/graphics/drawable/Drawable;->parseTintMode(ILandroid/graphics/PorterDuff$Mode;)Landroid/graphics/PorterDuff$Mode;
-Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;-><init>(Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;Landroid/graphics/drawable/DrawableContainer;Landroid/content/res/Resources;)V
-Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mConstantPadding:Landroid/graphics/Rect;
-Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mDrawables:[Landroid/graphics/drawable/Drawable;
-Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mHasColorFilter:Z
-Landroid/graphics/drawable/DrawableContainer;->mDrawableContainerState:Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;
-Landroid/graphics/drawable/DrawableContainer;->mLastDrawable:Landroid/graphics/drawable/Drawable;
-Landroid/graphics/drawable/DrawableInflater;->mClassLoader:Ljava/lang/ClassLoader;
-Landroid/graphics/drawable/DrawableWrapper;->mState:Landroid/graphics/drawable/DrawableWrapper$DrawableWrapperState;
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mAngle:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mGradient:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mGradientColors:[I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mHeight:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mInnerRadius:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mInnerRadiusRatio:F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mOrientation:Landroid/graphics/drawable/GradientDrawable$Orientation;
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mPadding:Landroid/graphics/Rect;
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mPositions:[F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mRadius:F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mRadiusArray:[F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mShape:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mSolidColors:Landroid/content/res/ColorStateList;
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeDashGap:F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeDashWidth:F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeWidth:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mThickness:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mThicknessRatio:F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mWidth:I
-Landroid/graphics/drawable/GradientDrawable;->mFillPaint:Landroid/graphics/Paint;
-Landroid/graphics/drawable/GradientDrawable;->mGradientState:Landroid/graphics/drawable/GradientDrawable$GradientState;
-Landroid/graphics/drawable/GradientDrawable;->mPadding:Landroid/graphics/Rect;
-Landroid/graphics/drawable/GradientDrawable;->mStrokePaint:Landroid/graphics/Paint;
-Landroid/graphics/drawable/Icon;->createWithResource(Landroid/content/res/Resources;I)Landroid/graphics/drawable/Icon;
-Landroid/graphics/drawable/Icon;->getBitmap()Landroid/graphics/Bitmap;
-Landroid/graphics/drawable/Icon;->getDataBytes()[B
-Landroid/graphics/drawable/Icon;->getDataLength()I
-Landroid/graphics/drawable/Icon;->getDataOffset()I
-Landroid/graphics/drawable/Icon;->getResources()Landroid/content/res/Resources;
-Landroid/graphics/drawable/Icon;->hasTint()Z
-Landroid/graphics/drawable/Icon;->mString1:Ljava/lang/String;
-Landroid/graphics/drawable/Icon;->mType:I
-Landroid/graphics/drawable/InsetDrawable;->mState:Landroid/graphics/drawable/InsetDrawable$InsetState;
-Landroid/graphics/drawable/LayerDrawable$ChildDrawable;->mDrawable:Landroid/graphics/drawable/Drawable;
-Landroid/graphics/drawable/LayerDrawable$LayerState;->mChildren:[Landroid/graphics/drawable/LayerDrawable$ChildDrawable;
-Landroid/graphics/drawable/LayerDrawable;->addLayer(Landroid/graphics/drawable/LayerDrawable$ChildDrawable;)I
-Landroid/graphics/drawable/LayerDrawable;->ensurePadding()V
-Landroid/graphics/drawable/LayerDrawable;->mLayerState:Landroid/graphics/drawable/LayerDrawable$LayerState;
-Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;->mNinePatch:Landroid/graphics/NinePatch;
-Landroid/graphics/drawable/NinePatchDrawable;->mNinePatchState:Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;
-Landroid/graphics/drawable/RippleDrawable$RippleState;->mColor:Landroid/content/res/ColorStateList;
-Landroid/graphics/drawable/RippleDrawable;->getRipplePaint()Landroid/graphics/Paint;
-Landroid/graphics/drawable/RippleDrawable;->mDensity:I
-Landroid/graphics/drawable/RippleDrawable;->mState:Landroid/graphics/drawable/RippleDrawable$RippleState;
-Landroid/graphics/drawable/RippleDrawable;->setForceSoftware(Z)V
-Landroid/graphics/drawable/RotateDrawable;->mState:Landroid/graphics/drawable/RotateDrawable$RotateState;
-Landroid/graphics/drawable/ScaleDrawable;->mState:Landroid/graphics/drawable/ScaleDrawable$ScaleState;
-Landroid/graphics/drawable/StateListDrawable$StateListState;->addStateSet([ILandroid/graphics/drawable/Drawable;)I
-Landroid/graphics/drawable/StateListDrawable;->extractStateSet(Landroid/util/AttributeSet;)[I
-Landroid/graphics/drawable/StateListDrawable;->mStateListState:Landroid/graphics/drawable/StateListDrawable$StateListState;
-Landroid/graphics/drawable/StateListDrawable;->updateStateFromTypedArray(Landroid/content/res/TypedArray;)V
-Landroid/graphics/drawable/TransitionDrawable;->mAlpha:I
-Landroid/graphics/drawable/TransitionDrawable;->mCrossFade:Z
-Landroid/graphics/drawable/TransitionDrawable;->mTo:I
-Landroid/graphics/drawable/VectorDrawable$VGroup;->setPivotX(F)V
-Landroid/graphics/drawable/VectorDrawable$VGroup;->setPivotY(F)V
-Landroid/graphics/drawable/VectorDrawable$VGroup;->setRotation(F)V
-Landroid/graphics/drawable/VectorDrawable$VGroup;->setTranslateX(F)V
-Landroid/graphics/drawable/VectorDrawable$VGroup;->setTranslateY(F)V
-Landroid/graphics/drawable/VectorDrawable;->getTargetByName(Ljava/lang/String;)Ljava/lang/Object;
-Landroid/graphics/drawable/VectorDrawable;->mTintFilter:Landroid/graphics/PorterDuffColorFilter;
-Landroid/graphics/drawable/VectorDrawable;->setAllowCaching(Z)V
-Landroid/graphics/FontFamily;-><init>()V
-Landroid/graphics/FontFamily;-><init>([Ljava/lang/String;I)V
-Landroid/graphics/FontFamily;->abortCreation()V
-Landroid/graphics/FontFamily;->addFontFromAssetManager(Landroid/content/res/AssetManager;Ljava/lang/String;IZIII[Landroid/graphics/fonts/FontVariationAxis;)Z
-Landroid/graphics/FontFamily;->addFontFromBuffer(Ljava/nio/ByteBuffer;I[Landroid/graphics/fonts/FontVariationAxis;II)Z
-Landroid/graphics/FontFamily;->freeze()Z
-Landroid/graphics/FontFamily;->mNativePtr:J
-Landroid/graphics/FontListParser;->parse(Ljava/io/InputStream;)Landroid/text/FontConfig;
-Landroid/graphics/fonts/FontVariationAxis;->mStyleValue:F
-Landroid/graphics/fonts/FontVariationAxis;->mTag:I
-Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V
-Landroid/graphics/GraphicBuffer;->createFromExisting(IIIIJ)Landroid/graphics/GraphicBuffer;
-Landroid/graphics/GraphicBuffer;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/graphics/GraphicBuffer;->mNativeObject:J
-Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I
-Landroid/graphics/ImageFormat;->Y8:I
-Landroid/graphics/LightingColorFilter;->setColorAdd(I)V
-Landroid/graphics/LightingColorFilter;->setColorMultiply(I)V
-Landroid/graphics/LinearGradient;->mColor0:I
-Landroid/graphics/LinearGradient;->mColor1:I
-Landroid/graphics/LinearGradient;->mColors:[I
-Landroid/graphics/LinearGradient;->mPositions:[F
-Landroid/graphics/LinearGradient;->mTileMode:Landroid/graphics/Shader$TileMode;
-Landroid/graphics/LinearGradient;->mX0:F
-Landroid/graphics/LinearGradient;->mX1:F
-Landroid/graphics/LinearGradient;->mY0:F
-Landroid/graphics/LinearGradient;->mY1:F
-Landroid/graphics/Matrix;->IDENTITY_MATRIX:Landroid/graphics/Matrix;
-Landroid/graphics/Matrix;->native_instance:J
-Landroid/graphics/Movie;-><init>(J)V
-Landroid/graphics/Movie;->mNativeMovie:J
-Landroid/graphics/NinePatch$InsetStruct;-><init>(IIIIIIIIFIF)V
-Landroid/graphics/NinePatch;->mBitmap:Landroid/graphics/Bitmap;
-Landroid/graphics/NinePatch;->mNativeChunk:J
-Landroid/graphics/Outline;->mRect:Landroid/graphics/Rect;
-Landroid/graphics/Paint;->getNativeInstance()J
-Landroid/graphics/Paint;->getTextRunAdvances([CIIIIZ[FI)F
-Landroid/graphics/Paint;->getTextRunCursor([CIIIII)I
-Landroid/graphics/Paint;->mNativePaint:J
-Landroid/graphics/Paint;->mTypeface:Landroid/graphics/Typeface;
-Landroid/graphics/Paint;->setCompatibilityScaling(F)V
-Landroid/graphics/Paint;->setHyphenEdit(I)V
-Landroid/graphics/Path;->isSimplePath:Z
-Landroid/graphics/Path;->rects:Landroid/graphics/Region;
-Landroid/graphics/pdf/PdfRenderer;->doClose()V
-Landroid/graphics/pdf/PdfRenderer;->mCurrentPage:Landroid/graphics/pdf/PdfRenderer$Page;
-Landroid/graphics/Picture;->mNativePicture:J
-Landroid/graphics/PorterDuff$Mode;->nativeInt:I
-Landroid/graphics/PorterDuffColorFilter;->getColor()I
-Landroid/graphics/PorterDuffColorFilter;->getMode()Landroid/graphics/PorterDuff$Mode;
-Landroid/graphics/RadialGradient;->mCenterColor:I
-Landroid/graphics/RadialGradient;->mColors:[I
-Landroid/graphics/RadialGradient;->mEdgeColor:I
-Landroid/graphics/RadialGradient;->mPositions:[F
-Landroid/graphics/RadialGradient;->mRadius:F
-Landroid/graphics/RadialGradient;->mTileMode:Landroid/graphics/Shader$TileMode;
-Landroid/graphics/RadialGradient;->mX:F
-Landroid/graphics/RadialGradient;->mY:F
-Landroid/graphics/Rect;->printShortString(Ljava/io/PrintWriter;)V
-Landroid/graphics/Rect;->scale(F)V
-Landroid/graphics/Region$Op;->nativeInt:I
-Landroid/graphics/Region;-><init>(JI)V
-Landroid/graphics/Region;->mNativeRegion:J
-Landroid/graphics/Region;->recycle()V
-Landroid/graphics/Region;->scale(F)V
-Landroid/graphics/Shader$TileMode;->nativeInt:I
-Landroid/graphics/SurfaceTexture;->mFrameAvailableListener:J
-Landroid/graphics/SurfaceTexture;->mOnFrameAvailableHandler:Landroid/os/Handler;
-Landroid/graphics/SurfaceTexture;->mProducer:J
-Landroid/graphics/SurfaceTexture;->mSurfaceTexture:J
-Landroid/graphics/SurfaceTexture;->nativeDetachFromGLContext()I
-Landroid/graphics/SurfaceTexture;->postEventFromNative(Ljava/lang/ref/WeakReference;)V
-Landroid/graphics/SweepGradient;->mColor0:I
-Landroid/graphics/SweepGradient;->mColor1:I
-Landroid/graphics/SweepGradient;->mColors:[I
-Landroid/graphics/SweepGradient;->mCx:F
-Landroid/graphics/SweepGradient;->mCy:F
-Landroid/graphics/SweepGradient;->mPositions:[F
-Landroid/graphics/TableMaskFilter;->CreateClipTable(II)Landroid/graphics/TableMaskFilter;
-Landroid/graphics/TemporaryBuffer;->obtain(I)[C
-Landroid/graphics/TemporaryBuffer;->recycle([C)V
-Landroid/graphics/Typeface;-><init>(J)V
-Landroid/graphics/Typeface;->createFromFamilies([Landroid/graphics/FontFamily;)Landroid/graphics/Typeface;
-Landroid/graphics/Typeface;->createFromFamiliesWithDefault([Landroid/graphics/FontFamily;II)Landroid/graphics/Typeface;
-Landroid/graphics/Typeface;->createFromFamiliesWithDefault([Landroid/graphics/FontFamily;Ljava/lang/String;II)Landroid/graphics/Typeface;
-Landroid/graphics/Typeface;->mStyle:I
-Landroid/graphics/Typeface;->nativeCreateFromArray([JII)J
-Landroid/graphics/Typeface;->nativeCreateWeightAlias(JI)J
-Landroid/graphics/Typeface;->native_instance:J
-Landroid/graphics/Typeface;->sDefaults:[Landroid/graphics/Typeface;
-Landroid/graphics/Typeface;->setDefault(Landroid/graphics/Typeface;)V
-Landroid/graphics/Typeface;->sSystemFallbackMap:Ljava/util/Map;
-Landroid/graphics/Typeface;->sSystemFontMap:Ljava/util/Map;
-Landroid/graphics/Xfermode;->porterDuffMode:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_GOOD:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_IMAGER_DIRTY:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_INSUFFICIENT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_PARTIAL:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_TOO_FAST:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_TOO_SLOW:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_CANCELED:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_HW_NOT_PRESENT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_HW_UNAVAILABLE:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_LOCKOUT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_LOCKOUT_PERMANENT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_NO_BIOMETRICS:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_NO_SPACE:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_TIMEOUT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_UNABLE_TO_PROCESS:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_USER_CANCELED:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_VENDOR:I
 Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_VENDOR_BASE:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_GOOD:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_IMAGER_DIRTY:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_INSUFFICIENT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_PARTIAL:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_TOO_FAST:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_TOO_SLOW:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_CANCELED:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_HW_NOT_PRESENT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_HW_UNAVAILABLE:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_LOCKOUT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_LOCKOUT_PERMANENT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_NO_FINGERPRINTS:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_NO_SPACE:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_TIMEOUT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_UNABLE_TO_PROCESS:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_USER_CANCELED:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_VENDOR:I
 Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_VENDOR_BASE:I
-Landroid/hardware/Camera$Parameters;->copyFrom(Landroid/hardware/Camera$Parameters;)V
-Landroid/hardware/Camera$Parameters;->dump()V
-Landroid/hardware/Camera$Parameters;->splitArea(Ljava/lang/String;)Ljava/util/ArrayList;
-Landroid/hardware/camera2/CameraCharacteristics$Key;-><init>(Ljava/lang/String;Landroid/hardware/camera2/utils/TypeReference;)V
-Landroid/hardware/camera2/CameraCharacteristics$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
-Landroid/hardware/camera2/CameraCharacteristics$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V
-Landroid/hardware/camera2/CameraCharacteristics$Key;->getNativeKey()Landroid/hardware/camera2/impl/CameraMetadataNative$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->CONTROL_MAX_REGIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->LED_AVAILABLE_LEDS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->LENS_INFO_SHADING_MAP_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->LOGICAL_MULTI_CAMERA_PHYSICAL_IDS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->mProperties:Landroid/hardware/camera2/impl/CameraMetadataNative;
-Landroid/hardware/camera2/CameraCharacteristics;->QUIRKS_USE_PARTIAL_RESULT:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_CHARACTERISTICS_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_REQUEST_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_RESULT_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_AVAILABLE_SESSION_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->REQUEST_MAX_NUM_OUTPUT_STREAMS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_FORMATS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_JPEG_MIN_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_JPEG_SIZES:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_PROCESSED_SIZES:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CameraCharacteristics;->SCALER_AVAILABLE_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;
-Landroid/hardware/camera2/CaptureRequest$Builder;->setPartOfCHSRequestList(Z)V
-Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Landroid/hardware/camera2/utils/TypeReference;)V
-Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V
-Landroid/hardware/camera2/CaptureRequest$Key;->getNativeKey()Landroid/hardware/camera2/impl/CameraMetadataNative$Key;
-Landroid/hardware/camera2/CaptureRequest;->getTargets()Ljava/util/Collection;
-Landroid/hardware/camera2/CaptureRequest;->JPEG_GPS_COORDINATES:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->JPEG_GPS_PROCESSING_METHOD:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->JPEG_GPS_TIMESTAMP:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->LED_TRANSMIT:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->mLogicalCameraSettings:Landroid/hardware/camera2/impl/CameraMetadataNative;
-Landroid/hardware/camera2/CaptureRequest;->REQUEST_ID:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->TONEMAP_CURVE_BLUE:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->TONEMAP_CURVE_GREEN:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureRequest;->TONEMAP_CURVE_RED:Landroid/hardware/camera2/CaptureRequest$Key;
-Landroid/hardware/camera2/CaptureResult$Key;-><init>(Ljava/lang/String;Landroid/hardware/camera2/utils/TypeReference;)V
-Landroid/hardware/camera2/CaptureResult$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
-Landroid/hardware/camera2/CaptureResult$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V
-Landroid/hardware/camera2/CaptureResult$Key;->getNativeKey()Landroid/hardware/camera2/impl/CameraMetadataNative$Key;
-Landroid/hardware/camera2/CaptureResult;->JPEG_GPS_COORDINATES:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->JPEG_GPS_PROCESSING_METHOD:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->JPEG_GPS_TIMESTAMP:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->LED_TRANSMIT:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->mResults:Landroid/hardware/camera2/impl/CameraMetadataNative;
-Landroid/hardware/camera2/CaptureResult;->QUIRKS_PARTIAL_RESULT:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->REQUEST_FRAME_COUNT:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->REQUEST_ID:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_FACE_IDS:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_FACE_LANDMARKS:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_FACE_RECTANGLES:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_FACE_SCORES:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_LENS_SHADING_MAP:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_TIMESTAMPS:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_X_SHIFTS:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_Y_SHIFTS:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_PREDICTED_COLOR_GAINS:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->STATISTICS_PREDICTED_COLOR_TRANSFORM:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->SYNC_FRAME_NUMBER:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_BLUE:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_GREEN:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_RED:Landroid/hardware/camera2/CaptureResult$Key;
-Landroid/hardware/camera2/impl/CameraMetadataNative$Key;->getTag()I
-Landroid/hardware/camera2/impl/CameraMetadataNative;->mMetadataPtr:J
-Landroid/hardware/camera2/impl/CameraMetadataNative;->nativeGetTagFromKeyLocal(Ljava/lang/String;)I
-Landroid/hardware/camera2/impl/CameraMetadataNative;->nativeGetTypeFromTagLocal(I)I
-Landroid/hardware/camera2/impl/CameraMetadataNative;->nativeReadValues(I)[B
-Landroid/hardware/camera2/utils/SurfaceUtils;->getSurfaceSize(Landroid/view/Surface;)Landroid/util/Size;
-Landroid/hardware/camera2/utils/TypeReference;-><init>()V
-Landroid/hardware/camera2/utils/TypeReference;->createSpecializedTypeReference(Ljava/lang/reflect/Type;)Landroid/hardware/camera2/utils/TypeReference;
-Landroid/hardware/Camera;->addCallbackBuffer([BI)V
-Landroid/hardware/Camera;->addRawImageCallbackBuffer([B)V
-Landroid/hardware/Camera;->CAMERA_HAL_API_VERSION_1_0:I
-Landroid/hardware/Camera;->getEmptyParameters()Landroid/hardware/Camera$Parameters;
-Landroid/hardware/Camera;->mNativeContext:J
-Landroid/hardware/Camera;->native_getParameters()Ljava/lang/String;
-Landroid/hardware/Camera;->native_setParameters(Ljava/lang/String;)V
-Landroid/hardware/Camera;->native_setup(Ljava/lang/Object;IILjava/lang/String;)I
-Landroid/hardware/Camera;->openLegacy(II)Landroid/hardware/Camera;
-Landroid/hardware/Camera;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V
-Landroid/hardware/Camera;->previewEnabled()Z
-Landroid/hardware/Camera;->setPreviewSurface(Landroid/view/Surface;)V
-Landroid/hardware/display/DisplayManager;->ACTION_WIFI_DISPLAY_STATUS_CHANGED:Ljava/lang/String;
-Landroid/hardware/display/DisplayManager;->connectWifiDisplay(Ljava/lang/String;)V
-Landroid/hardware/display/DisplayManager;->disconnectWifiDisplay()V
-Landroid/hardware/display/DisplayManager;->EXTRA_WIFI_DISPLAY_STATUS:Ljava/lang/String;
-Landroid/hardware/display/DisplayManager;->forgetWifiDisplay(Ljava/lang/String;)V
-Landroid/hardware/display/DisplayManager;->getWifiDisplayStatus()Landroid/hardware/display/WifiDisplayStatus;
-Landroid/hardware/display/DisplayManager;->pauseWifiDisplay()V
-Landroid/hardware/display/DisplayManager;->renameWifiDisplay(Ljava/lang/String;Ljava/lang/String;)V
-Landroid/hardware/display/DisplayManager;->resumeWifiDisplay()V
-Landroid/hardware/display/DisplayManager;->startWifiDisplayScan()V
-Landroid/hardware/display/DisplayManager;->stopWifiDisplayScan()V
-Landroid/hardware/display/DisplayManagerGlobal;->disconnectWifiDisplay()V
-Landroid/hardware/display/DisplayManagerGlobal;->getDisplayIds()[I
-Landroid/hardware/display/DisplayManagerGlobal;->getDisplayInfo(I)Landroid/view/DisplayInfo;
-Landroid/hardware/display/DisplayManagerGlobal;->getWifiDisplayStatus()Landroid/hardware/display/WifiDisplayStatus;
-Landroid/hardware/display/DisplayManagerGlobal;->mDm:Landroid/hardware/display/IDisplayManager;
-Landroid/hardware/display/DisplayManagerGlobal;->sInstance:Landroid/hardware/display/DisplayManagerGlobal;
 Landroid/hardware/display/IDisplayManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/display/IDisplayManager;
 Landroid/hardware/display/IDisplayManager;->getDisplayInfo(I)Landroid/view/DisplayInfo;
-Landroid/hardware/display/WifiDisplay;->canConnect()Z
-Landroid/hardware/display/WifiDisplay;->equals(Landroid/hardware/display/WifiDisplay;)Z
-Landroid/hardware/display/WifiDisplay;->getDeviceAddress()Ljava/lang/String;
-Landroid/hardware/display/WifiDisplay;->getDeviceAlias()Ljava/lang/String;
-Landroid/hardware/display/WifiDisplay;->getDeviceName()Ljava/lang/String;
-Landroid/hardware/display/WifiDisplay;->isAvailable()Z
-Landroid/hardware/display/WifiDisplay;->isRemembered()Z
-Landroid/hardware/display/WifiDisplayStatus;->DISPLAY_STATE_CONNECTED:I
-Landroid/hardware/display/WifiDisplayStatus;->DISPLAY_STATE_CONNECTING:I
-Landroid/hardware/display/WifiDisplayStatus;->DISPLAY_STATE_NOT_CONNECTED:I
-Landroid/hardware/display/WifiDisplayStatus;->FEATURE_STATE_ON:I
-Landroid/hardware/display/WifiDisplayStatus;->getActiveDisplay()Landroid/hardware/display/WifiDisplay;
-Landroid/hardware/display/WifiDisplayStatus;->getActiveDisplayState()I
-Landroid/hardware/display/WifiDisplayStatus;->getDisplays()[Landroid/hardware/display/WifiDisplay;
-Landroid/hardware/display/WifiDisplayStatus;->getFeatureState()I
-Landroid/hardware/display/WifiDisplayStatus;->getScanState()I
-Landroid/hardware/display/WifiDisplayStatus;->mActiveDisplay:Landroid/hardware/display/WifiDisplay;
-Landroid/hardware/display/WifiDisplayStatus;->mDisplays:[Landroid/hardware/display/WifiDisplay;
-Landroid/hardware/display/WifiDisplayStatus;->SCAN_STATE_NOT_SCANNING:I
-Landroid/hardware/fingerprint/Fingerprint;->getFingerId()I
-Landroid/hardware/fingerprint/Fingerprint;->getName()Ljava/lang/CharSequence;
-Landroid/hardware/fingerprint/FingerprintManager$AuthenticationResult;->getFingerprint()Landroid/hardware/fingerprint/Fingerprint;
-Landroid/hardware/fingerprint/FingerprintManager;->getAuthenticatorId()J
-Landroid/hardware/fingerprint/FingerprintManager;->getEnrolledFingerprints()Ljava/util/List;
-Landroid/hardware/fingerprint/FingerprintManager;->getEnrolledFingerprints(I)Ljava/util/List;
 Landroid/hardware/fingerprint/IFingerprintService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/hardware/fingerprint/IFingerprintService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/fingerprint/IFingerprintService;
-Landroid/hardware/HardwareBuffer;-><init>(J)V
-Landroid/hardware/HardwareBuffer;->mNativeObject:J
 Landroid/hardware/ICameraService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/ICameraService;
 Landroid/hardware/input/IInputManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager;
 Landroid/hardware/input/IInputManager$Stub;->TRANSACTION_injectInputEvent:I
 Landroid/hardware/input/IInputManager;->injectInputEvent(Landroid/view/InputEvent;I)Z
-Landroid/hardware/input/InputManager;->createInputForwarder(I)Landroid/app/IInputForwarder;
-Landroid/hardware/input/InputManager;->getInstance()Landroid/hardware/input/InputManager;
-Landroid/hardware/input/InputManager;->injectInputEvent(Landroid/view/InputEvent;I)Z
-Landroid/hardware/input/InputManager;->INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH:I
-Landroid/hardware/input/InputManager;->mIm:Landroid/hardware/input/IInputManager;
-Landroid/hardware/input/InputManager;->setPointerIconType(I)V
 Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V
 Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService;
-Landroid/hardware/Sensor;->getHandle()I
-Landroid/hardware/Sensor;->mFlags:I
-Landroid/hardware/Sensor;->TYPE_DEVICE_ORIENTATION:I
-Landroid/hardware/Sensor;->TYPE_PICK_UP_GESTURE:I
-Landroid/hardware/SensorEvent;-><init>(I)V
-Landroid/hardware/SensorManager;-><init>()V
-Landroid/hardware/SerialManager;->getSerialPorts()[Ljava/lang/String;
-Landroid/hardware/SerialManager;->openSerialPort(Ljava/lang/String;I)Landroid/hardware/SerialPort;
-Landroid/hardware/SerialPort;->close()V
-Landroid/hardware/SerialPort;->mNativeContext:I
-Landroid/hardware/SerialPort;->write(Ljava/nio/ByteBuffer;I)V
-Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;-><init>(II)V
-Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;->confidenceLevel:I
-Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;->userId:I
-Landroid/hardware/soundtrigger/SoundTrigger$GenericRecognitionEvent;-><init>(IIZIIIZLandroid/media/AudioFormat;[B)V
-Landroid/hardware/soundtrigger/SoundTrigger$GenericSoundModel;-><init>(Ljava/util/UUID;Ljava/util/UUID;[B)V
-Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;-><init>(IILjava/lang/String;Ljava/lang/String;[I)V
-Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;->id:I
-Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;->locale:Ljava/lang/String;
-Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;->recognitionModes:I
-Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;->text:Ljava/lang/String;
-Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;->users:[I
-Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionEvent;-><init>(IIZIIIZLandroid/media/AudioFormat;[B[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V
-Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionEvent;->keyphraseExtras:[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;
-Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;-><init>(III[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;)V
-Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;->coarseConfidenceLevel:I
-Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;->confidenceLevels:[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;
-Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;->id:I
-Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;->recognitionModes:I
-Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel;-><init>(Ljava/util/UUID;Ljava/util/UUID;[B[Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;)V
-Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel;->keyphrases:[Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;
-Landroid/hardware/soundtrigger/SoundTrigger$ModuleProperties;-><init>(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIZIZIZ)V
-Landroid/hardware/soundtrigger/SoundTrigger$ModuleProperties;->id:I
-Landroid/hardware/soundtrigger/SoundTrigger$ModuleProperties;->maxSoundModels:I
-Landroid/hardware/soundtrigger/SoundTrigger$ModuleProperties;->uuid:Ljava/util/UUID;
-Landroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;-><init>(ZZ[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;[B)V
-Landroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;->captureRequested:Z
-Landroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;->data:[B
-Landroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;->keyphrases:[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;
-Landroid/hardware/soundtrigger/SoundTrigger$RecognitionEvent;-><init>(IIZIIIZLandroid/media/AudioFormat;[B)V
-Landroid/hardware/soundtrigger/SoundTrigger$RecognitionEvent;->captureAvailable:Z
-Landroid/hardware/soundtrigger/SoundTrigger$RecognitionEvent;->captureSession:I
-Landroid/hardware/soundtrigger/SoundTrigger$RecognitionEvent;->data:[B
-Landroid/hardware/soundtrigger/SoundTrigger$RecognitionEvent;->soundModelHandle:I
-Landroid/hardware/soundtrigger/SoundTrigger$RecognitionEvent;->status:I
-Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;->data:[B
-Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;->uuid:Ljava/util/UUID;
-Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;->vendorUuid:Ljava/util/UUID;
-Landroid/hardware/soundtrigger/SoundTrigger$SoundModelEvent;-><init>(II[B)V
-Landroid/hardware/soundtrigger/SoundTrigger;->attachModule(ILandroid/hardware/soundtrigger/SoundTrigger$StatusListener;Landroid/os/Handler;)Landroid/hardware/soundtrigger/SoundTriggerModule;
-Landroid/hardware/soundtrigger/SoundTrigger;->listModules(Ljava/util/ArrayList;)I
-Landroid/hardware/soundtrigger/SoundTriggerModule;->detach()V
-Landroid/hardware/soundtrigger/SoundTriggerModule;->loadSoundModel(Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;[I)I
-Landroid/hardware/soundtrigger/SoundTriggerModule;->mId:I
-Landroid/hardware/soundtrigger/SoundTriggerModule;->mNativeContext:J
-Landroid/hardware/soundtrigger/SoundTriggerModule;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V
-Landroid/hardware/soundtrigger/SoundTriggerModule;->startRecognition(ILandroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I
-Landroid/hardware/soundtrigger/SoundTriggerModule;->stopRecognition(I)I
-Landroid/hardware/soundtrigger/SoundTriggerModule;->unloadSoundModel(I)I
-Landroid/hardware/SystemSensorManager$BaseEventQueue;->dispatchAdditionalInfoEvent(III[F[I)V
-Landroid/hardware/SystemSensorManager$BaseEventQueue;->dispatchFlushCompleteEvent(I)V
-Landroid/hardware/SystemSensorManager$BaseEventQueue;->dispatchSensorEvent(I[FIJ)V
 Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager;
-Landroid/hardware/usb/UsbDevice;->mInterfaces:[Landroid/hardware/usb/UsbInterface;
-Landroid/hardware/usb/UsbDeviceConnection;->mNativeContext:J
-Landroid/hardware/usb/UsbManager;-><init>(Landroid/content/Context;Landroid/hardware/usb/IUsbManager;)V
-Landroid/hardware/usb/UsbManager;->ACTION_USB_STATE:Ljava/lang/String;
-Landroid/hardware/usb/UsbManager;->getPorts()[Landroid/hardware/usb/UsbPort;
-Landroid/hardware/usb/UsbManager;->getPortStatus(Landroid/hardware/usb/UsbPort;)Landroid/hardware/usb/UsbPortStatus;
-Landroid/hardware/usb/UsbManager;->isFunctionEnabled(Ljava/lang/String;)Z
-Landroid/hardware/usb/UsbManager;->setCurrentFunction(Ljava/lang/String;Z)V
-Landroid/hardware/usb/UsbManager;->setPortRoles(Landroid/hardware/usb/UsbPort;II)V
-Landroid/hardware/usb/UsbManager;->USB_CONNECTED:Ljava/lang/String;
-Landroid/hardware/usb/UsbManager;->USB_DATA_UNLOCKED:Ljava/lang/String;
-Landroid/hardware/usb/UsbManager;->USB_FUNCTION_NONE:Ljava/lang/String;
-Landroid/hardware/usb/UsbPortStatus;->getCurrentDataRole()I
-Landroid/hardware/usb/UsbPortStatus;->getCurrentMode()I
-Landroid/hardware/usb/UsbPortStatus;->getCurrentPowerRole()I
-Landroid/hardware/usb/UsbPortStatus;->getSupportedRoleCombinations()I
-Landroid/hardware/usb/UsbPortStatus;->isConnected()Z
-Landroid/hardware/usb/UsbPortStatus;->isRoleCombinationSupported(II)Z
-Landroid/hardware/usb/UsbRequest;->mBuffer:Ljava/nio/ByteBuffer;
-Landroid/hardware/usb/UsbRequest;->mLength:I
-Landroid/hardware/usb/UsbRequest;->mNativeContext:J
 Landroid/icu/impl/CurrencyData;-><init>()V
 Landroid/icu/text/ArabicShaping;-><init>(I)V
 Landroid/icu/text/ArabicShaping;->isAlefMaksouraChar(C)Z
@@ -2631,7 +1841,6 @@
 Landroid/icu/text/Transliterator;->transliterate(Landroid/icu/text/Replaceable;Landroid/icu/text/Transliterator$Position;Ljava/lang/String;)V
 Landroid/icu/text/Transliterator;->transliterate(Ljava/lang/String;)Ljava/lang/String;
 Landroid/icu/text/UFormat;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
-Landroid/icu/text/UForwardCharacterIterator;->DONE:I
 Landroid/icu/util/Calendar;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
 Landroid/icu/util/PersianCalendar;-><init>(Ljava/util/Locale;)V
 Landroid/icu/util/UResourceBundle;->getBundleInstance(Ljava/lang/String;Landroid/icu/util/ULocale;)Landroid/icu/util/UResourceBundle;
@@ -2640,26 +1849,6 @@
 Landroid/icu/util/UResourceBundle;->getType()I
 Landroid/icu/util/UResourceBundleIterator;->hasNext()Z
 Landroid/icu/util/UResourceBundleIterator;->next()Landroid/icu/util/UResourceBundle;
-Landroid/inputmethodservice/InputMethodService$SettingsObserver;->shouldShowImeWithHardKeyboard()Z
-Landroid/inputmethodservice/InputMethodService;->mExtractEditText:Landroid/inputmethodservice/ExtractEditText;
-Landroid/inputmethodservice/InputMethodService;->mExtractView:Landroid/view/View;
-Landroid/inputmethodservice/InputMethodService;->mRootView:Landroid/view/View;
-Landroid/inputmethodservice/InputMethodService;->mSettingsObserver:Landroid/inputmethodservice/InputMethodService$SettingsObserver;
-Landroid/inputmethodservice/InputMethodService;->mTheme:I
-Landroid/inputmethodservice/InputMethodService;->mTmpInsets:Landroid/inputmethodservice/InputMethodService$Insets;
-Landroid/inputmethodservice/InputMethodService;->onExtractedDeleteText(II)V
-Landroid/inputmethodservice/InputMethodService;->onExtractedReplaceText(IILjava/lang/CharSequence;)V
-Landroid/inputmethodservice/InputMethodService;->onExtractedSetSpan(Ljava/lang/Object;III)V
-Landroid/inputmethodservice/Keyboard;->mModifierKeys:Ljava/util/List;
-Landroid/inputmethodservice/Keyboard;->mTotalHeight:I
-Landroid/inputmethodservice/Keyboard;->mTotalWidth:I
-Landroid/inputmethodservice/Keyboard;->resize(II)V
-Landroid/inputmethodservice/KeyboardView;->mKeyBackground:Landroid/graphics/drawable/Drawable;
-Landroid/inputmethodservice/KeyboardView;->mLabelTextSize:I
-Landroid/inputmethodservice/KeyboardView;->mPreviewText:Landroid/widget/TextView;
-Landroid/inputmethodservice/KeyboardView;->openPopupIfRequired(Landroid/view/MotionEvent;)Z
-Landroid/inputmethodservice/KeyboardView;->repeatKey()Z
-Landroid/inputmethodservice/KeyboardView;->showKey(I)V
 Landroid/location/Country;-><init>(Ljava/lang/String;I)V
 Landroid/location/Country;->getCountryIso()Ljava/lang/String;
 Landroid/location/Country;->getSource()I
@@ -3159,80 +2348,6 @@
 Landroid/media/TimedText;->getObject(I)Ljava/lang/Object;
 Landroid/media/ToneGenerator;->mNativeContext:J
 Landroid/media/TtmlRenderer;-><init>(Landroid/content/Context;)V
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_16_9:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_1_1:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_2_3:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_3_2:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_4_3:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->AVAILABILITY_AVAILABLE:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->AVAILABILITY_FREE_WITH_SUBSCRIPTION:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->AVAILABILITY_PAID_CONTENT:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_AUTHOR:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_AVAILABILITY:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_BROWSABLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_CONTENT_ID:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_DURATION_MILLIS:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_INTENT_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_INTERACTION_COUNT:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_INTERACTION_TYPE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_INTERNAL_PROVIDER_ID:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_ITEM_COUNT:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_LAST_PLAYBACK_POSITION_MILLIS:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_LIVE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_LOGO_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_OFFER_PRICE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_POSTER_ART_ASPECT_RATIO:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_PREVIEW_VIDEO_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_RELEASE_DATE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_STARTING_PRICE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_THUMBNAIL_ASPECT_RATIO:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_TRANSIENT:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_TYPE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_FANS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_FOLLOWERS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_LIKES:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_LISTENS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_THUMBS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_VIEWERS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_VIEWS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_ALBUM:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_ARTIST:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_CHANNEL:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_CLIP:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_EVENT:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_MOVIE:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_PLAYLIST:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_STATION:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_TRACK:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_TV_EPISODE:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_TV_SEASON:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_TV_SERIES:I
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_AUDIO_LANGUAGE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_CANONICAL_GENRE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_CONTENT_RATING:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_EPISODE_DISPLAY_NUMBER:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_EPISODE_TITLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_DATA:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_FLAG1:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_FLAG2:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_FLAG3:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_FLAG4:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_LONG_DESCRIPTION:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_POSTER_ART_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_REVIEW_RATING:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_REVIEW_RATING_STYLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEARCHABLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEASON_DISPLAY_NUMBER:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEASON_TITLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SHORT_DESCRIPTION:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_THUMBNAIL_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_TITLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_VERSION_NUMBER:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_VIDEO_HEIGHT:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_VIDEO_WIDTH:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->REVIEW_RATING_STYLE_PERCENTAGE:I
-Landroid/media/tv/TvContract$ProgramColumns;->REVIEW_RATING_STYLE_STARS:I
-Landroid/media/tv/TvContract$ProgramColumns;->REVIEW_RATING_STYLE_THUMBS_UP_DOWN:I
 Landroid/media/tv/TvInputInfo;->getComponent()Landroid/content/ComponentName;
 Landroid/media/tv/TvInputService$Session;->mOverlayFrame:Landroid/graphics/Rect;
 Landroid/media/VolumeShaper$Configuration;-><init>(IIIDI[F[F)V
@@ -3724,28 +2839,6 @@
 Landroid/net/wifi/WifiSsid;->getOctets()[B
 Landroid/net/wifi/WifiSsid;->NONE:Ljava/lang/String;
 Landroid/net/wifi/WifiSsid;->octets:Ljava/io/ByteArrayOutputStream;
-Landroid/nfc/cardemulation/AidGroup;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Landroid/nfc/cardemulation/AidGroup;->aids:Ljava/util/List;
-Landroid/nfc/cardemulation/AidGroup;->category:Ljava/lang/String;
-Landroid/nfc/cardemulation/AidGroup;->createFromXml(Lorg/xmlpull/v1/XmlPullParser;)Landroid/nfc/cardemulation/AidGroup;
-Landroid/nfc/cardemulation/AidGroup;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/nfc/cardemulation/AidGroup;->description:Ljava/lang/String;
-Landroid/nfc/cardemulation/AidGroup;->getAids()Ljava/util/List;
-Landroid/nfc/cardemulation/AidGroup;->getCategory()Ljava/lang/String;
-Landroid/nfc/cardemulation/AidGroup;->writeAsXml(Lorg/xmlpull/v1/XmlSerializer;)V
-Landroid/nfc/cardemulation/ApduServiceInfo;-><init>(Landroid/content/pm/PackageManager;Landroid/content/pm/ResolveInfo;Z)V
-Landroid/nfc/cardemulation/ApduServiceInfo;-><init>(Landroid/content/pm/ResolveInfo;ZLjava/lang/String;Ljava/util/ArrayList;Ljava/util/ArrayList;ZIILjava/lang/String;)V
-Landroid/nfc/cardemulation/ApduServiceInfo;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/nfc/cardemulation/ApduServiceInfo;->getDescription()Ljava/lang/String;
-Landroid/nfc/cardemulation/ApduServiceInfo;->getSettingsActivityName()Ljava/lang/String;
-Landroid/nfc/cardemulation/ApduServiceInfo;->getUid()I
-Landroid/nfc/cardemulation/ApduServiceInfo;->isOnHost()Z
-Landroid/nfc/cardemulation/ApduServiceInfo;->loadBanner(Landroid/content/pm/PackageManager;)Landroid/graphics/drawable/Drawable;
-Landroid/nfc/cardemulation/ApduServiceInfo;->mDynamicAidGroups:Ljava/util/HashMap;
-Landroid/nfc/cardemulation/ApduServiceInfo;->mService:Landroid/content/pm/ResolveInfo;
-Landroid/nfc/cardemulation/ApduServiceInfo;->mStaticAidGroups:Ljava/util/HashMap;
-Landroid/nfc/cardemulation/ApduServiceInfo;->requiresUnlock()Z
-Landroid/nfc/ErrorCodes;->isError(I)Z
 Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_enable:I
 Landroid/nfc/INfcAdapterExtras;->authenticate(Ljava/lang/String;[B)V
 Landroid/nfc/INfcAdapterExtras;->close(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle;
@@ -3754,21 +2847,6 @@
 Landroid/nfc/INfcAdapterExtras;->open(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle;
 Landroid/nfc/INfcAdapterExtras;->setCardEmulationRoute(Ljava/lang/String;I)V
 Landroid/nfc/INfcAdapterExtras;->transceive(Ljava/lang/String;[B)Landroid/os/Bundle;
-Landroid/nfc/NdefRecord;->mId:[B
-Landroid/nfc/NfcActivityManager;->mAdapter:Landroid/nfc/NfcAdapter;
-Landroid/nfc/NfcAdapter;->attemptDeadServiceRecovery(Ljava/lang/Exception;)V
-Landroid/nfc/NfcAdapter;->getAdapterState()I
-Landroid/nfc/NfcAdapter;->getContext()Landroid/content/Context;
-Landroid/nfc/NfcAdapter;->getDefaultAdapter()Landroid/nfc/NfcAdapter;
-Landroid/nfc/NfcAdapter;->getNfcAdapter(Landroid/content/Context;)Landroid/nfc/NfcAdapter;
-Landroid/nfc/NfcAdapter;->getNfcAdapterExtrasInterface()Landroid/nfc/INfcAdapterExtras;
-Landroid/nfc/NfcAdapter;->getService()Landroid/nfc/INfcAdapter;
-Landroid/nfc/NfcAdapter;->setNdefPushMessageCallback(Landroid/nfc/NfcAdapter$CreateNdefMessageCallback;Landroid/app/Activity;I)V
-Landroid/nfc/NfcAdapter;->sService:Landroid/nfc/INfcAdapter;
-Landroid/nfc/NfcManager;-><init>(Landroid/content/Context;)V
-Landroid/nfc/Tag;->getServiceHandle()I
-Landroid/nfc/Tag;->getTagService()Landroid/nfc/INfcTag;
-Landroid/nfc/Tag;->mId:[B
 Landroid/opengl/EGL14;->eglGetDisplay(J)Landroid/opengl/EGLDisplay;
 Landroid/opengl/GLES20;->glGetActiveAttrib(IIILjava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;B)V
 Landroid/opengl/GLES20;->glGetActiveUniform(IIILjava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;B)V
@@ -3937,7 +3015,6 @@
 Landroid/os/Environment;->getDataSystemDirectory()Ljava/io/File;
 Landroid/os/Environment;->getLegacyExternalStorageObbDirectory()Ljava/io/File;
 Landroid/os/Environment;->getOemDirectory()Ljava/io/File;
-Landroid/os/Environment;->getStorageDirectory()Ljava/io/File;
 Landroid/os/Environment;->getVendorDirectory()Ljava/io/File;
 Landroid/os/Environment;->initForCurrentUser()V
 Landroid/os/Environment;->maybeTranslateEmulatedPathToInternal(Ljava/io/File;)Ljava/io/File;
@@ -3964,18 +3041,6 @@
 Landroid/os/Handler;->mCallback:Landroid/os/Handler$Callback;
 Landroid/os/Handler;->mLooper:Landroid/os/Looper;
 Landroid/os/Handler;->mMessenger:Landroid/os/IMessenger;
-Landroid/os/health/HealthKeys$Constants;-><init>(Ljava/lang/Class;)V
-Landroid/os/health/HealthStats;-><init>(Landroid/os/Parcel;)V
-Landroid/os/health/HealthStatsParceler;-><init>(Landroid/os/health/HealthStatsWriter;)V
-Landroid/os/health/HealthStatsParceler;-><init>(Landroid/os/Parcel;)V
-Landroid/os/health/HealthStatsParceler;->getHealthStats()Landroid/os/health/HealthStats;
-Landroid/os/health/HealthStatsWriter;-><init>(Landroid/os/health/HealthKeys$Constants;)V
-Landroid/os/health/HealthStatsWriter;->addMeasurement(IJ)V
-Landroid/os/health/HealthStatsWriter;->addMeasurements(ILjava/lang/String;J)V
-Landroid/os/health/HealthStatsWriter;->addStats(ILjava/lang/String;Landroid/os/health/HealthStatsWriter;)V
-Landroid/os/health/HealthStatsWriter;->addTimer(IIJ)V
-Landroid/os/health/HealthStatsWriter;->addTimers(ILjava/lang/String;Landroid/os/health/TimerStat;)V
-Landroid/os/health/HealthStatsWriter;->flattenToParcel(Landroid/os/Parcel;)V
 Landroid/os/health/SystemHealthManager;-><init>()V
 Landroid/os/health/SystemHealthManager;->from(Landroid/content/Context;)Landroid/os/health/SystemHealthManager;
 Landroid/os/HwParcel;-><init>(Z)V
@@ -4051,8 +3116,6 @@
 Landroid/os/MessageQueue;->mQuitAllowed:Z
 Landroid/os/MessageQueue;->nativePollOnce(JI)V
 Landroid/os/MessageQueue;->next()Landroid/os/Message;
-Landroid/os/MessageQueue;->postSyncBarrier()I
-Landroid/os/MessageQueue;->removeSyncBarrier(I)V
 Landroid/os/Parcel$ReadWriteHelper;-><init>()V
 Landroid/os/Parcel;->getGlobalAllocCount()J
 Landroid/os/Parcel;->getGlobalAllocSize()J
@@ -4064,13 +3127,11 @@
 Landroid/os/Parcel;->readCreator(Landroid/os/Parcelable$Creator;Ljava/lang/ClassLoader;)Landroid/os/Parcelable;
 Landroid/os/Parcel;->readExceptionCode()I
 Landroid/os/Parcel;->readParcelableCreator(Ljava/lang/ClassLoader;)Landroid/os/Parcelable$Creator;
-Landroid/os/Parcel;->readParcelableList(Ljava/util/List;Ljava/lang/ClassLoader;)Ljava/util/List;
 Landroid/os/Parcel;->readRawFileDescriptor()Ljava/io/FileDescriptor;
 Landroid/os/Parcel;->writeArrayMap(Landroid/util/ArrayMap;)V
 Landroid/os/Parcel;->writeArraySet(Landroid/util/ArraySet;)V
 Landroid/os/Parcel;->writeCharSequence(Ljava/lang/CharSequence;)V
 Landroid/os/Parcel;->writeParcelableCreator(Landroid/os/Parcelable;)V
-Landroid/os/Parcel;->writeParcelableList(Ljava/util/List;I)V
 Landroid/os/ParcelableParcel;-><init>(Ljava/lang/ClassLoader;)V
 Landroid/os/ParcelableParcel;->CREATOR:Landroid/os/Parcelable$ClassLoaderCreator;
 Landroid/os/ParcelableParcel;->getClassLoader()Ljava/lang/ClassLoader;
@@ -4154,6 +3215,7 @@
 Landroid/os/SELinux;->restoreconRecursive(Ljava/io/File;)Z
 Landroid/os/ServiceManager;-><init>()V
 Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;)V
+Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;Z)V
 Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;ZI)V
 Landroid/os/ServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder;
 Landroid/os/ServiceManager;->getIServiceManager()Landroid/os/IServiceManager;
@@ -4217,7 +3279,6 @@
 Landroid/os/storage/StorageVolume;->getFatVolumeId()I
 Landroid/os/storage/StorageVolume;->getMaxFileSize()J
 Landroid/os/storage/StorageVolume;->getOwner()Landroid/os/UserHandle;
-Landroid/os/storage/StorageVolume;->getPath()Ljava/lang/String;
 Landroid/os/storage/StorageVolume;->getPathFile()Ljava/io/File;
 Landroid/os/storage/StorageVolume;->getUserLabel()Ljava/lang/String;
 Landroid/os/storage/StorageVolume;->mDescription:Ljava/lang/String;
@@ -4238,6 +3299,7 @@
 Landroid/os/storage/VolumeInfo;->getDiskId()Ljava/lang/String;
 Landroid/os/storage/VolumeInfo;->getEnvironmentForState(I)Ljava/lang/String;
 Landroid/os/storage/VolumeInfo;->getFsUuid()Ljava/lang/String;
+Landroid/os/storage/VolumeInfo;->getInternalPath()Ljava/io/File;
 Landroid/os/storage/VolumeInfo;->getInternalPathForUser(I)Ljava/io/File;
 Landroid/os/storage/VolumeInfo;->getMountUserId()I
 Landroid/os/storage/VolumeInfo;->getPath()Ljava/io/File;
@@ -4262,7 +3324,6 @@
 Landroid/os/StrictMode$ThreadPolicy;->mask:I
 Landroid/os/StrictMode$VmPolicy$Builder;->mMask:I
 Landroid/os/StrictMode$VmPolicy;->mask:I
-Landroid/os/StrictMode;->conditionallyCheckInstanceCounts()V
 Landroid/os/StrictMode;->disableDeathOnFileUriExposure()V
 Landroid/os/StrictMode;->enableDeathOnFileUriExposure()V
 Landroid/os/StrictMode;->enterCriticalSpan(Ljava/lang/String;)Landroid/os/StrictMode$Span;
@@ -4273,6 +3334,7 @@
 Landroid/os/StrictMode;->sLastVmViolationTime:Ljava/util/HashMap;
 Landroid/os/StrictMode;->sWindowManager:Landroid/util/Singleton;
 Landroid/os/StrictMode;->violationsBeingTimed:Ljava/lang/ThreadLocal;
+Landroid/os/SystemClock;-><init>()V
 Landroid/os/SystemClock;->currentThreadTimeMicro()J
 Landroid/os/SystemClock;->currentTimeMicro()J
 Landroid/os/SystemProperties;-><init>()V
@@ -4357,6 +3419,7 @@
 Landroid/os/UserManager;->getUserStartRealtime()J
 Landroid/os/UserManager;->getUserUnlockRealtime()J
 Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
+Landroid/os/UserManager;->isDeviceInDemoMode(Landroid/content/Context;)Z
 Landroid/os/UserManager;->isGuestUser(I)Z
 Landroid/os/UserManager;->isLinkedUser()Z
 Landroid/os/UserManager;->isUserAdmin(I)Z
@@ -4376,19 +3439,11 @@
 Landroid/os/VintfRuntimeInfo;->getOsName()Ljava/lang/String;
 Landroid/os/VintfRuntimeInfo;->getOsRelease()Ljava/lang/String;
 Landroid/os/VintfRuntimeInfo;->getOsVersion()Ljava/lang/String;
-Landroid/os/WorkSource;-><init>(I)V
 Landroid/os/WorkSource;-><init>(Landroid/os/Parcel;)V
-Landroid/os/WorkSource;->add(I)Z
-Landroid/os/WorkSource;->add(ILjava/lang/String;)Z
-Landroid/os/WorkSource;->addReturningNewbs(Landroid/os/WorkSource;)Landroid/os/WorkSource;
-Landroid/os/WorkSource;->get(I)I
-Landroid/os/WorkSource;->getName(I)Ljava/lang/String;
 Landroid/os/WorkSource;->mNames:[Ljava/lang/String;
 Landroid/os/WorkSource;->mNum:I
 Landroid/os/WorkSource;->mUids:[I
-Landroid/os/WorkSource;->setReturningDiffs(Landroid/os/WorkSource;)[Landroid/os/WorkSource;
 Landroid/os/WorkSource;->sGoneWork:Landroid/os/WorkSource;
-Landroid/os/WorkSource;->size()I
 Landroid/os/WorkSource;->sNewbWork:Landroid/os/WorkSource;
 Landroid/os/WorkSource;->sTmpWorkSource:Landroid/os/WorkSource;
 Landroid/os/WorkSource;->updateLocked(Landroid/os/WorkSource;ZZ)Z
@@ -4469,10 +3524,6 @@
 Landroid/preference/VolumePreference$VolumeStore;->volume:I
 Landroid/preference/VolumePreference;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
 Landroid/preference/VolumePreference;->mStreamType:I
-Landroid/print/PrinterId;->getServiceName()Landroid/content/ComponentName;
-Landroid/print/PrintJobInfo;->getAdvancedOptions()Landroid/os/Bundle;
-Landroid/print/PrintJobInfo;->getDocumentInfo()Landroid/print/PrintDocumentInfo;
-Landroid/print/PrintManager;->addPrintJobStateChangeListener(Landroid/print/PrintManager$PrintJobStateChangeListener;)V
 Landroid/provider/Browser;->getVisitedHistory(Landroid/content/ContentResolver;)[Ljava/lang/String;
 Landroid/provider/Browser;->sendString(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V
 Landroid/provider/BrowserContract$Accounts;->CONTENT_URI:Landroid/net/Uri;
@@ -4487,9 +3538,6 @@
 Landroid/provider/CalendarContract$CalendarAlerts;->rescheduleMissedAlarms(Landroid/content/ContentResolver;Landroid/content/Context;Landroid/app/AlarmManager;)V
 Landroid/provider/CalendarContract$CalendarAlerts;->scheduleAlarm(Landroid/content/Context;Landroid/app/AlarmManager;J)V
 Landroid/provider/CallLog$Calls;->addCall(Lcom/android/internal/telephony/CallerInfo;Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIILandroid/telecom/PhoneAccountHandle;JILjava/lang/Long;ZLandroid/os/UserHandle;Z)Landroid/net/Uri;
-Landroid/provider/ContactsContract$ContactCounts;->EXTRA_ADDRESS_BOOK_INDEX:Ljava/lang/String;
-Landroid/provider/ContactsContract$ContactCounts;->EXTRA_ADDRESS_BOOK_INDEX_COUNTS:Ljava/lang/String;
-Landroid/provider/ContactsContract$ContactCounts;->EXTRA_ADDRESS_BOOK_INDEX_TITLES:Ljava/lang/String;
 Landroid/provider/ContactsContract$Contacts$AggregationSuggestions;->builder()Landroid/provider/ContactsContract$Contacts$AggregationSuggestions$Builder;
 Landroid/provider/ContactsContract$Contacts;->CORP_CONTENT_URI:Landroid/net/Uri;
 Landroid/provider/ContactsContract$QuickContact;->composeQuickContactsIntent(Landroid/content/Context;Landroid/graphics/Rect;Landroid/net/Uri;I[Ljava/lang/String;)Landroid/content/Intent;
@@ -4562,6 +3610,7 @@
 Landroid/provider/Settings$Global;->ZEN_MODE_IMPORTANT_INTERRUPTIONS:I
 Landroid/provider/Settings$Global;->ZEN_MODE_NO_INTERRUPTIONS:I
 Landroid/provider/Settings$Global;->ZEN_MODE_OFF:I
+Landroid/provider/Settings$NameValueCache;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String;
 Landroid/provider/Settings$NameValueCache;->mProviderHolder:Landroid/provider/Settings$ContentProviderHolder;
 Landroid/provider/Settings$Secure;->ACCESSIBILITY_AUTOCLICK_ENABLED:Ljava/lang/String;
 Landroid/provider/Settings$Secure;->ACCESSIBILITY_CAPTIONING_TYPEFACE:Ljava/lang/String;
@@ -4600,10 +3649,12 @@
 Landroid/provider/Settings$Secure;->SETTINGS_TO_BACKUP:[Ljava/lang/String;
 Landroid/provider/Settings$Secure;->SMS_DEFAULT_APPLICATION:Ljava/lang/String;
 Landroid/provider/Settings$Secure;->sNameValueCache:Landroid/provider/Settings$NameValueCache;
+Landroid/provider/Settings$Secure;->sProviderHolder:Landroid/provider/Settings$ContentProviderHolder;
 Landroid/provider/Settings$Secure;->VOICE_RECOGNITION_SERVICE:Ljava/lang/String;
 Landroid/provider/Settings$System;->AIRPLANE_MODE_TOGGLEABLE_RADIOS:Ljava/lang/String;
 Landroid/provider/Settings$System;->CAR_DOCK_SOUND:Ljava/lang/String;
 Landroid/provider/Settings$System;->CAR_UNDOCK_SOUND:Ljava/lang/String;
+Landroid/provider/Settings$System;->CLONE_TO_MANAGED_PROFILE:Ljava/util/Set;
 Landroid/provider/Settings$System;->DESK_DOCK_SOUND:Ljava/lang/String;
 Landroid/provider/Settings$System;->DESK_UNDOCK_SOUND:Ljava/lang/String;
 Landroid/provider/Settings$System;->DOCK_SOUNDS_ENABLED:Ljava/lang/String;
@@ -4615,6 +3666,9 @@
 Landroid/provider/Settings$System;->LOCKSCREEN_SOUNDS_ENABLED:Ljava/lang/String;
 Landroid/provider/Settings$System;->LOCK_SOUND:Ljava/lang/String;
 Landroid/provider/Settings$System;->MASTER_MONO:Ljava/lang/String;
+Landroid/provider/Settings$System;->MOVED_TO_GLOBAL:Ljava/util/HashSet;
+Landroid/provider/Settings$System;->MOVED_TO_SECURE:Ljava/util/HashSet;
+Landroid/provider/Settings$System;->MOVED_TO_SECURE_THEN_GLOBAL:Ljava/util/HashSet;
 Landroid/provider/Settings$System;->NOTIFICATION_LIGHT_PULSE:Ljava/lang/String;
 Landroid/provider/Settings$System;->POINTER_LOCATION:Ljava/lang/String;
 Landroid/provider/Settings$System;->POINTER_SPEED:Ljava/lang/String;
@@ -4629,6 +3683,7 @@
 Landroid/provider/Settings$System;->sProviderHolder:Landroid/provider/Settings$ContentProviderHolder;
 Landroid/provider/Settings$System;->TTY_MODE:Ljava/lang/String;
 Landroid/provider/Settings$System;->UNLOCK_SOUND:Ljava/lang/String;
+Landroid/provider/Settings$System;->VALIDATORS:Ljava/util/Map;
 Landroid/provider/Settings$System;->VIBRATE_IN_SILENT:Ljava/lang/String;
 Landroid/provider/Settings;->ACTION_TRUSTED_CREDENTIALS_USER:Ljava/lang/String;
 Landroid/provider/Settings;->ACTION_USER_DICTIONARY_INSERT:Ljava/lang/String;
@@ -4916,97 +3971,7 @@
 Landroid/R$styleable;->Window:[I
 Landroid/R$styleable;->Window_windowBackground:I
 Landroid/R$styleable;->Window_windowFrame:I
-Landroid/renderscript/BaseObj;->mRS:Landroid/renderscript/RenderScript;
-Landroid/renderscript/Element;->createUser(Landroid/renderscript/RenderScript;Landroid/renderscript/Element$DataType;)Landroid/renderscript/Element;
-Landroid/renderscript/FileA3D$EntryType;->MESH:Landroid/renderscript/FileA3D$EntryType;
-Landroid/renderscript/FileA3D$IndexEntry;->getEntryType()Landroid/renderscript/FileA3D$EntryType;
-Landroid/renderscript/FileA3D$IndexEntry;->getObject()Landroid/renderscript/BaseObj;
-Landroid/renderscript/FileA3D;->createFromResource(Landroid/renderscript/RenderScript;Landroid/content/res/Resources;I)Landroid/renderscript/FileA3D;
-Landroid/renderscript/FileA3D;->getIndexEntry(I)Landroid/renderscript/FileA3D$IndexEntry;
-Landroid/renderscript/Font$Style;->ITALIC:Landroid/renderscript/Font$Style;
-Landroid/renderscript/Font;->create(Landroid/renderscript/RenderScript;Landroid/content/res/Resources;Ljava/lang/String;Landroid/renderscript/Font$Style;F)Landroid/renderscript/Font;
-Landroid/renderscript/Matrix4f;->mMat:[F
-Landroid/renderscript/Mesh$AllocationBuilder;-><init>(Landroid/renderscript/RenderScript;)V
-Landroid/renderscript/Mesh$AllocationBuilder;->addIndexSetAllocation(Landroid/renderscript/Allocation;Landroid/renderscript/Mesh$Primitive;)Landroid/renderscript/Mesh$AllocationBuilder;
-Landroid/renderscript/Mesh$AllocationBuilder;->addIndexSetType(Landroid/renderscript/Mesh$Primitive;)Landroid/renderscript/Mesh$AllocationBuilder;
-Landroid/renderscript/Mesh$AllocationBuilder;->addVertexAllocation(Landroid/renderscript/Allocation;)Landroid/renderscript/Mesh$AllocationBuilder;
-Landroid/renderscript/Mesh$AllocationBuilder;->create()Landroid/renderscript/Mesh;
-Landroid/renderscript/Mesh$Primitive;->POINT:Landroid/renderscript/Mesh$Primitive;
-Landroid/renderscript/Mesh$Primitive;->TRIANGLE:Landroid/renderscript/Mesh$Primitive;
-Landroid/renderscript/Mesh$TriangleMeshBuilder;-><init>(Landroid/renderscript/RenderScript;II)V
-Landroid/renderscript/Mesh$TriangleMeshBuilder;->addTriangle(III)Landroid/renderscript/Mesh$TriangleMeshBuilder;
-Landroid/renderscript/Mesh$TriangleMeshBuilder;->addVertex(FF)Landroid/renderscript/Mesh$TriangleMeshBuilder;
-Landroid/renderscript/Mesh$TriangleMeshBuilder;->create(Z)Landroid/renderscript/Mesh;
-Landroid/renderscript/Mesh;->getVertexAllocation(I)Landroid/renderscript/Allocation;
-Landroid/renderscript/Program$BaseProgramBuilder;-><init>(Landroid/renderscript/RenderScript;)V
-Landroid/renderscript/Program$BaseProgramBuilder;->mConstantCount:I
-Landroid/renderscript/Program$BaseProgramBuilder;->mConstants:[Landroid/renderscript/Type;
-Landroid/renderscript/Program$BaseProgramBuilder;->mInputCount:I
-Landroid/renderscript/Program$BaseProgramBuilder;->mInputs:[Landroid/renderscript/Element;
-Landroid/renderscript/Program$BaseProgramBuilder;->mOutputCount:I
-Landroid/renderscript/Program$BaseProgramBuilder;->mOutputs:[Landroid/renderscript/Element;
-Landroid/renderscript/Program$BaseProgramBuilder;->mRS:Landroid/renderscript/RenderScript;
-Landroid/renderscript/Program$BaseProgramBuilder;->mShader:Ljava/lang/String;
-Landroid/renderscript/Program$BaseProgramBuilder;->mTextureCount:I
-Landroid/renderscript/Program$TextureType;->TEXTURE_2D:Landroid/renderscript/Program$TextureType;
-Landroid/renderscript/ProgramFragment$Builder;-><init>(Landroid/renderscript/RenderScript;)V
-Landroid/renderscript/ProgramFragment$Builder;->create()Landroid/renderscript/ProgramFragment;
-Landroid/renderscript/ProgramFragmentFixedFunction$Builder$EnvMode;->MODULATE:Landroid/renderscript/ProgramFragmentFixedFunction$Builder$EnvMode;
-Landroid/renderscript/ProgramFragmentFixedFunction$Builder$EnvMode;->REPLACE:Landroid/renderscript/ProgramFragmentFixedFunction$Builder$EnvMode;
-Landroid/renderscript/ProgramFragmentFixedFunction$Builder$Format;->ALPHA:Landroid/renderscript/ProgramFragmentFixedFunction$Builder$Format;
-Landroid/renderscript/ProgramFragmentFixedFunction$Builder$Format;->RGB:Landroid/renderscript/ProgramFragmentFixedFunction$Builder$Format;
-Landroid/renderscript/ProgramFragmentFixedFunction$Builder$Format;->RGBA:Landroid/renderscript/ProgramFragmentFixedFunction$Builder$Format;
-Landroid/renderscript/ProgramFragmentFixedFunction$Builder;-><init>(Landroid/renderscript/RenderScript;)V
-Landroid/renderscript/ProgramFragmentFixedFunction$Builder;->create()Landroid/renderscript/ProgramFragmentFixedFunction;
-Landroid/renderscript/ProgramFragmentFixedFunction$Builder;->setTexture(Landroid/renderscript/ProgramFragmentFixedFunction$Builder$EnvMode;Landroid/renderscript/ProgramFragmentFixedFunction$Builder$Format;I)Landroid/renderscript/ProgramFragmentFixedFunction$Builder;
-Landroid/renderscript/ProgramFragmentFixedFunction$Builder;->setVaryingColor(Z)Landroid/renderscript/ProgramFragmentFixedFunction$Builder;
-Landroid/renderscript/ProgramRaster$Builder;-><init>(Landroid/renderscript/RenderScript;)V
-Landroid/renderscript/ProgramRaster$Builder;->create()Landroid/renderscript/ProgramRaster;
-Landroid/renderscript/ProgramRaster$Builder;->setPointSpriteEnabled(Z)Landroid/renderscript/ProgramRaster$Builder;
-Landroid/renderscript/ProgramStore$BlendDstFunc;->ONE:Landroid/renderscript/ProgramStore$BlendDstFunc;
-Landroid/renderscript/ProgramStore$BlendDstFunc;->ONE_MINUS_SRC_ALPHA:Landroid/renderscript/ProgramStore$BlendDstFunc;
-Landroid/renderscript/ProgramStore$BlendDstFunc;->ZERO:Landroid/renderscript/ProgramStore$BlendDstFunc;
-Landroid/renderscript/ProgramStore$BlendSrcFunc;->ONE:Landroid/renderscript/ProgramStore$BlendSrcFunc;
-Landroid/renderscript/ProgramStore$BlendSrcFunc;->SRC_ALPHA:Landroid/renderscript/ProgramStore$BlendSrcFunc;
-Landroid/renderscript/ProgramStore$Builder;-><init>(Landroid/renderscript/RenderScript;)V
-Landroid/renderscript/ProgramStore$Builder;->create()Landroid/renderscript/ProgramStore;
-Landroid/renderscript/ProgramStore$Builder;->setBlendFunc(Landroid/renderscript/ProgramStore$BlendSrcFunc;Landroid/renderscript/ProgramStore$BlendDstFunc;)Landroid/renderscript/ProgramStore$Builder;
-Landroid/renderscript/ProgramStore$Builder;->setDepthFunc(Landroid/renderscript/ProgramStore$DepthFunc;)Landroid/renderscript/ProgramStore$Builder;
-Landroid/renderscript/ProgramStore$Builder;->setDepthMaskEnabled(Z)Landroid/renderscript/ProgramStore$Builder;
-Landroid/renderscript/ProgramStore$Builder;->setDitherEnabled(Z)Landroid/renderscript/ProgramStore$Builder;
-Landroid/renderscript/ProgramStore$DepthFunc;->ALWAYS:Landroid/renderscript/ProgramStore$DepthFunc;
-Landroid/renderscript/ProgramStore$DepthFunc;->LESS:Landroid/renderscript/ProgramStore$DepthFunc;
-Landroid/renderscript/ProgramStore;->BLEND_ALPHA_DEPTH_NONE(Landroid/renderscript/RenderScript;)Landroid/renderscript/ProgramStore;
-Landroid/renderscript/ProgramVertex$Builder;-><init>(Landroid/renderscript/RenderScript;)V
-Landroid/renderscript/ProgramVertex$Builder;->addInput(Landroid/renderscript/Element;)Landroid/renderscript/ProgramVertex$Builder;
-Landroid/renderscript/ProgramVertex$Builder;->create()Landroid/renderscript/ProgramVertex;
-Landroid/renderscript/ProgramVertexFixedFunction$Builder;-><init>(Landroid/renderscript/RenderScript;)V
-Landroid/renderscript/ProgramVertexFixedFunction$Builder;->create()Landroid/renderscript/ProgramVertexFixedFunction;
-Landroid/renderscript/ProgramVertexFixedFunction$Constants;-><init>(Landroid/renderscript/RenderScript;)V
-Landroid/renderscript/ProgramVertexFixedFunction$Constants;->setProjection(Landroid/renderscript/Matrix4f;)V
-Landroid/renderscript/ProgramVertexFixedFunction;->bindConstants(Landroid/renderscript/ProgramVertexFixedFunction$Constants;)V
-Landroid/renderscript/RenderScript;->create(Landroid/content/Context;I)Landroid/renderscript/RenderScript;
-Landroid/renderscript/RenderScript;->create(Landroid/content/Context;ILandroid/renderscript/RenderScript$ContextType;I)Landroid/renderscript/RenderScript;
-Landroid/renderscript/RenderScript;->getMinorID()J
-Landroid/renderscript/RenderScript;->mMessageCallback:Landroid/renderscript/RenderScript$RSMessageHandler;
-Landroid/renderscript/RenderScript;->nScriptCCreate(Ljava/lang/String;Ljava/lang/String;[BI)J
-Landroid/renderscript/RenderScript;->sPointerSize:I
-Landroid/renderscript/RenderScript;->validate()V
-Landroid/renderscript/RenderScriptCacheDir;->mCacheDir:Ljava/io/File;
-Landroid/renderscript/RenderScriptCacheDir;->setupDiskCache(Ljava/io/File;)V
-Landroid/renderscript/RenderScriptGL$SurfaceConfig;-><init>()V
-Landroid/renderscript/RenderScriptGL$SurfaceConfig;->setDepth(II)V
-Landroid/renderscript/RenderScriptGL;-><init>(Landroid/content/Context;Landroid/renderscript/RenderScriptGL$SurfaceConfig;)V
-Landroid/renderscript/RenderScriptGL;->bindProgramRaster(Landroid/renderscript/ProgramRaster;)V
-Landroid/renderscript/RenderScriptGL;->bindProgramStore(Landroid/renderscript/ProgramStore;)V
-Landroid/renderscript/RenderScriptGL;->bindProgramVertex(Landroid/renderscript/ProgramVertex;)V
-Landroid/renderscript/RenderScriptGL;->bindRootScript(Landroid/renderscript/Script;)V
-Landroid/renderscript/RenderScriptGL;->setSurface(Landroid/view/SurfaceHolder;II)V
-Landroid/renderscript/RSSurfaceView;-><init>(Landroid/content/Context;)V
-Landroid/renderscript/RSSurfaceView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
-Landroid/renderscript/Script$Builder;-><init>(Landroid/renderscript/RenderScript;)V
-Landroid/renderscript/Script$Builder;->mRS:Landroid/renderscript/RenderScript;
-Landroid/security/Credentials;->convertToPem([[Ljava/security/cert/Certificate;)[B
+Landroid/security/Credentials;->convertToPem([Ljava/security/cert/Certificate;)[B
 Landroid/security/Credentials;->getInstance()Landroid/security/Credentials;
 Landroid/security/Credentials;->install(Landroid/content/Context;Ljava/lang/String;[B)V
 Landroid/security/Credentials;->install(Landroid/content/Context;Ljava/security/KeyPair;)V
@@ -5143,17 +4108,6 @@
 Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V
 Landroid/service/wallpaper/WallpaperService;->MSG_WINDOW_RESIZED:I
 Landroid/speech/IRecognitionListener;->onEvent(ILandroid/os/Bundle;)V
-Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String;
-Landroid/speech/tts/TextToSpeech;->mConnectingServiceConnection:Landroid/speech/tts/TextToSpeech$Connection;
-Landroid/speech/tts/TextToSpeech;->mCurrentEngine:Ljava/lang/String;
-Landroid/speech/tts/TextToSpeech;->mInitListener:Landroid/speech/tts/TextToSpeech$OnInitListener;
-Landroid/speech/tts/TtsEngines;-><init>(Landroid/content/Context;)V
-Landroid/speech/tts/TtsEngines;->getEngines()Ljava/util/List;
-Landroid/speech/tts/TtsEngines;->getLocalePrefForEngine(Ljava/lang/String;)Ljava/util/Locale;
-Landroid/speech/tts/TtsEngines;->getSettingsIntent(Ljava/lang/String;)Landroid/content/Intent;
-Landroid/speech/tts/TtsEngines;->normalizeTTSLocale(Ljava/util/Locale;)Ljava/util/Locale;
-Landroid/speech/tts/TtsEngines;->parseLocaleString(Ljava/lang/String;)Ljava/util/Locale;
-Landroid/speech/tts/TtsEngines;->updateLocalePrefForEngine(Ljava/lang/String;Ljava/util/Locale;)V
 Landroid/system/Int32Ref;->value:I
 Landroid/system/OsConstants;-><init>()V
 Landroid/system/OsConstants;->AF_NETLINK:I
@@ -5210,26 +4164,6 @@
 Landroid/system/OsConstants;->XATTR_REPLACE:I
 Landroid/system/OsConstants;->_LINUX_CAPABILITY_VERSION_3:I
 Landroid/system/StructTimeval;->fromMillis(J)Landroid/system/StructTimeval;
-Landroid/telecom/AudioState;->isMuted:Z
-Landroid/telecom/AudioState;->route:I
-Landroid/telecom/AudioState;->supportedRouteMask:I
-Landroid/telecom/Call$Details;->CAPABILITY_CAN_UPGRADE_TO_VIDEO:I
-Landroid/telecom/Connection$VideoProvider;-><init>(Landroid/os/Looper;)V
-Landroid/telecom/Phone;->setProximitySensorOff(Z)V
-Landroid/telecom/Phone;->setProximitySensorOn()V
-Landroid/telecom/PhoneAccountHandle;-><init>(Landroid/os/Parcel;)V
-Landroid/telecom/PhoneAccountHandle;->mComponentName:Landroid/content/ComponentName;
-Landroid/telecom/PhoneAccountHandle;->mId:Ljava/lang/String;
-Landroid/telecom/TelecomManager;->EXTRA_IS_HANDOVER:Ljava/lang/String;
-Landroid/telecom/TelecomManager;->getCallCapablePhoneAccounts(Z)Ljava/util/List;
-Landroid/telecom/TelecomManager;->getCurrentTtyMode()I
-Landroid/telecom/TelecomManager;->getSimCallManager(I)Landroid/telecom/PhoneAccountHandle;
-Landroid/telecom/TelecomManager;->getSystemDialerPackage()Ljava/lang/String;
-Landroid/telecom/TelecomManager;->getUserSelectedOutgoingPhoneAccount()Landroid/telecom/PhoneAccountHandle;
-Landroid/telecom/TelecomManager;->setDefaultDialer(Ljava/lang/String;)Z
-Landroid/telecom/TelecomManager;->setUserSelectedOutgoingPhoneAccount(Landroid/telecom/PhoneAccountHandle;)V
-Landroid/telecom/TelecomManager;->TTY_MODE_OFF:I
-Landroid/telecom/VideoCallImpl;->destroy()V
 Landroid/telephony/CarrierConfigManager;->KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY:Ljava/lang/String;
 Landroid/telephony/CarrierConfigManager;->KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL:Ljava/lang/String;
 Landroid/telephony/CarrierMessagingServiceManager;-><init>()V
@@ -5372,6 +4306,7 @@
 Landroid/telephony/ServiceState;->mCdmaRoamingIndicator:I
 Landroid/telephony/ServiceState;->mCssIndicator:Z
 Landroid/telephony/ServiceState;->mIsManualNetworkSelection:Z
+Landroid/telephony/ServiceState;->mIsUsingCarrierAggregation:Z
 Landroid/telephony/ServiceState;->mNetworkId:I
 Landroid/telephony/ServiceState;->mSystemId:I
 Landroid/telephony/ServiceState;->newFromBundle(Landroid/os/Bundle;)Landroid/telephony/ServiceState;
@@ -5650,7 +4585,6 @@
 Landroid/text/SpannableStringBuilder;->sendToSpanWatchers(III)V
 Landroid/text/SpannableStringBuilder;->substring(II)Ljava/lang/String;
 Landroid/text/SpannableStringInternal;-><init>(Ljava/lang/CharSequence;II)V
-Landroid/text/SpannableStringInternal;->charAt(I)C
 Landroid/text/SpannableStringInternal;->checkRange(Ljava/lang/String;II)V
 Landroid/text/SpannableStringInternal;->COLUMNS:I
 Landroid/text/SpannableStringInternal;->copySpans(Landroid/text/SpannableStringInternal;II)V
@@ -5658,14 +4592,12 @@
 Landroid/text/SpannableStringInternal;->EMPTY:[Ljava/lang/Object;
 Landroid/text/SpannableStringInternal;->END:I
 Landroid/text/SpannableStringInternal;->FLAGS:I
-Landroid/text/SpannableStringInternal;->getChars(II[CI)V
 Landroid/text/SpannableStringInternal;->getSpanEnd(Ljava/lang/Object;)I
 Landroid/text/SpannableStringInternal;->getSpanFlags(Ljava/lang/Object;)I
 Landroid/text/SpannableStringInternal;->getSpans(IILjava/lang/Class;)[Ljava/lang/Object;
 Landroid/text/SpannableStringInternal;->getSpanStart(Ljava/lang/Object;)I
 Landroid/text/SpannableStringInternal;->isIndexFollowsNextLine(I)Z
 Landroid/text/SpannableStringInternal;->isOutOfCopyRange(IIII)Z
-Landroid/text/SpannableStringInternal;->length()I
 Landroid/text/SpannableStringInternal;->mSpanCount:I
 Landroid/text/SpannableStringInternal;->mSpanData:[I
 Landroid/text/SpannableStringInternal;->mSpans:[Ljava/lang/Object;
@@ -5735,6 +4667,7 @@
 Landroid/transition/Scene;->setCurrentScene(Landroid/view/View;Landroid/transition/Scene;)V
 Landroid/transition/Transition;->cancel()V
 Landroid/transition/Transition;->end()V
+Landroid/transition/Transition;->getRunningAnimators()Landroid/util/ArrayMap;
 Landroid/transition/TransitionManager;->getRunningTransitions()Landroid/util/ArrayMap;
 Landroid/transition/TransitionManager;->sPendingTransitions:Ljava/util/ArrayList;
 Landroid/transition/TransitionManager;->sRunningTransitions:Ljava/lang/ThreadLocal;
@@ -5871,21 +4804,6 @@
 Landroid/view/AccessibilityIterators$AbstractTextSegmentIterator;->mText:Ljava/lang/String;
 Landroid/view/ActionProvider;->reset()V
 Landroid/view/ActionProvider;->setSubUiVisibilityListener(Landroid/view/ActionProvider$SubUiVisibilityListener;)V
-Landroid/view/animation/Animation;->detach()V
-Landroid/view/animation/Animation;->getInvalidateRegion(IIIILandroid/graphics/RectF;Landroid/view/animation/Transformation;)V
-Landroid/view/animation/Animation;->initializeInvalidateRegion(IIII)V
-Landroid/view/animation/Animation;->mListener:Landroid/view/animation/Animation$AnimationListener;
-Landroid/view/animation/Animation;->mPreviousRegion:Landroid/graphics/RectF;
-Landroid/view/animation/Animation;->mPreviousTransformation:Landroid/view/animation/Transformation;
-Landroid/view/animation/Animation;->mRegion:Landroid/graphics/RectF;
-Landroid/view/animation/Animation;->mTransformation:Landroid/view/animation/Transformation;
-Landroid/view/animation/AnimationUtils;->createAnimationFromXml(Landroid/content/Context;Lorg/xmlpull/v1/XmlPullParser;Landroid/view/animation/AnimationSet;Landroid/util/AttributeSet;)Landroid/view/animation/Animation;
-Landroid/view/animation/Transformation;->printShortString(Ljava/io/PrintWriter;)V
-Landroid/view/animation/TranslateAnimation;->mFromXValue:F
-Landroid/view/animation/TranslateAnimation;->mFromYValue:F
-Landroid/view/animation/TranslateAnimation;->mToXValue:F
-Landroid/view/animation/TranslateAnimation;->mToYValue:F
-Landroid/view/animation/TranslateYAnimation;-><init>(IFIF)V
 Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/view/autofill/IAutoFillManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/autofill/IAutoFillManager;
 Landroid/view/Choreographer$CallbackQueue;->addCallbackLocked(JLjava/lang/Object;Ljava/lang/Object;)V
@@ -6181,6 +5099,7 @@
 Landroid/view/SurfaceControl;->HIDDEN:I
 Landroid/view/SurfaceControl;->hide()V
 Landroid/view/SurfaceControl;->openTransaction()V
+Landroid/view/SurfaceControl;->screenshot(Landroid/graphics/Rect;III)Landroid/graphics/Bitmap;
 Landroid/view/SurfaceControl;->screenshot(Landroid/graphics/Rect;IIIIZI)Landroid/graphics/Bitmap;
 Landroid/view/SurfaceControl;->screenshot(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;IIIIZZ)V
 Landroid/view/SurfaceControl;->setDisplayLayerStack(Landroid/os/IBinder;I)V
@@ -6500,7 +5419,6 @@
 Landroid/view/ViewHierarchyEncoder;->addProperty(Ljava/lang/String;Z)V
 Landroid/view/ViewOverlay;->getOverlayView()Landroid/view/ViewGroup;
 Landroid/view/ViewOverlay;->isEmpty()Z
-Landroid/view/ViewPropertyAnimator;->mRTBackend:Landroid/view/ViewPropertyAnimatorRT;
 Landroid/view/ViewRootImpl$CalledFromWrongThreadException;-><init>(Ljava/lang/String;)V
 Landroid/view/ViewRootImpl;->addConfigCallback(Landroid/view/ViewRootImpl$ConfigChangedCallback;)V
 Landroid/view/ViewRootImpl;->cancelInvalidate(Landroid/view/View;)V
@@ -6681,7 +5599,6 @@
 Landroid/webkit/WebResourceResponse;->mImmutable:Z
 Landroid/webkit/WebResourceResponse;->mStatusCode:I
 Landroid/webkit/WebSettings$TextSize;->value:I
-Landroid/webkit/WebSyncManager;->mHandler:Landroid/os/Handler;
 Landroid/webkit/WebSyncManager;->syncFromRamToFlash()V
 Landroid/webkit/WebView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;IILjava/util/Map;Z)V
 Landroid/webkit/WebView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;ILjava/util/Map;Z)V
@@ -6974,6 +5891,7 @@
 Landroid/widget/ListView;->fillSpecific(II)Landroid/view/View;
 Landroid/widget/ListView;->fillUp(II)Landroid/view/View;
 Landroid/widget/ListView;->getHeightForPosition(I)I
+Landroid/widget/ListView;->isDirectChildHeaderOrFooter(Landroid/view/View;)Z
 Landroid/widget/ListView;->makeAndAddView(IIZIZ)Landroid/view/View;
 Landroid/widget/ListView;->mAreAllItemsSelectable:Z
 Landroid/widget/ListView;->mDivider:Landroid/graphics/drawable/Drawable;
@@ -7353,73 +6271,6 @@
 Lcom/android/ims/internal/IImsVideoCallCallback;->receiveSessionModifyResponse(ILandroid/telecom/VideoProfile;Landroid/telecom/VideoProfile;)V
 Lcom/android/ims/internal/IImsVideoCallProvider$Stub;-><init>()V
 Lcom/android/ims/internal/IImsVideoCallProvider;->setCallback(Lcom/android/ims/internal/IImsVideoCallCallback;)V
-Lcom/android/ims/internal/uce/common/CapInfo;-><init>()V
-Lcom/android/ims/internal/uce/common/CapInfo;->setCapTimestamp(J)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setCdViaPresenceSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setExts([Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFtHttpSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFtSnFSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFtSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFtThumbSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFullSnFGroupChatSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setGeoPullFtSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setGeoPullSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setGeoPushSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setImSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setIpVideoSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setIpVoiceSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setIsSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setRcsIpVideoCallSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setRcsIpVideoOnlyCallSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setRcsIpVoiceCallSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setSmSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setSpSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setVsDuringCSSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setVsSupported(Z)V
-Lcom/android/ims/internal/uce/common/StatusCode;-><init>()V
-Lcom/android/ims/internal/uce/common/StatusCode;->setStatusCode(I)V
-Lcom/android/ims/internal/uce/common/UceLong;->getUceLong()J
-Lcom/android/ims/internal/uce/common/UceLong;->setUceLong(J)V
-Lcom/android/ims/internal/uce/presence/PresCmdId;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresCmdId;->setCmdId(I)V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;->setCmdId(Lcom/android/ims/internal/uce/presence/PresCmdId;)V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;->setRequestId(I)V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;->setStatus(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;->setUserData(I)V
-Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;->setPublishTrigeerType(I)V
-Lcom/android/ims/internal/uce/presence/PresResInfo;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresResInfo;->setDisplayName(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInfo;->setInstanceInfo(Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;)V
-Lcom/android/ims/internal/uce/presence/PresResInfo;->setResUri(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setPresentityUri(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setReason(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setResId(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setResInstanceState(I)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setTupleInfo([Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setFullState(Z)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setListName(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setPresSubscriptionState(Lcom/android/ims/internal/uce/presence/PresSubscriptionState;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setRequestId(I)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setSubscriptionExpireTime(I)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setSubscriptionTerminatedReason(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setUri(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setVersion(I)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setCmdId(Lcom/android/ims/internal/uce/presence/PresCmdId;)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setReasonPhrase(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setRequestId(I)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setRetryAfter(I)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setSipResponseCode(I)V
-Lcom/android/ims/internal/uce/presence/PresSubscriptionState;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresSubscriptionState;->setPresSubscriptionState(I)V
-Lcom/android/ims/internal/uce/presence/PresTupleInfo;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresTupleInfo;->setContactUri(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresTupleInfo;->setFeatureTag(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresTupleInfo;->setTimestamp(Ljava/lang/String;)V
 Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
 Lcom/android/internal/app/AlertController$AlertParams;-><init>(Landroid/content/Context;)V
 Lcom/android/internal/app/AlertController$AlertParams;->apply(Lcom/android/internal/app/AlertController;)V
@@ -7522,6 +6373,7 @@
 Lcom/android/internal/appwidget/IAppWidgetService;->bindAppWidgetId(Ljava/lang/String;IILandroid/content/ComponentName;Landroid/os/Bundle;)Z
 Lcom/android/internal/appwidget/IAppWidgetService;->bindRemoteViewsService(Ljava/lang/String;ILandroid/content/Intent;Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/app/IServiceConnection;I)Z
 Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetIds(Landroid/content/ComponentName;)[I
+Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetViews(Ljava/lang/String;I)Landroid/widget/RemoteViews;
 Lcom/android/internal/backup/IBackupTransport$Stub;-><init>()V
 Lcom/android/internal/backup/IBackupTransport$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/backup/IBackupTransport;
 Lcom/android/internal/backup/IBackupTransport;->clearBackupData(Landroid/content/pm/PackageInfo;)I
@@ -7629,7 +6481,6 @@
 Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWifiRunningTime(JI)J
 Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWifiScanTime(JI)J
 Lcom/android/internal/os/BatteryStatsImpl;-><init>(Landroid/os/Parcel;)V
-Lcom/android/internal/os/BatteryStatsImpl;->commitPendingDataToDisk()V
 Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryRealtime(JI)J
 Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryTimeRemaining(J)J
 Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryUptime(JI)J
@@ -7738,6 +6589,9 @@
 Lcom/android/internal/R$attr;->text:I
 Lcom/android/internal/R$attr;->title:I
 Lcom/android/internal/R$attr;->webViewStyle:I
+Lcom/android/internal/R$bool;-><init>()V
+Lcom/android/internal/R$bool;->config_automatic_brightness_available:I
+Lcom/android/internal/R$bool;->config_intrusiveNotificationLed:I
 Lcom/android/internal/R$bool;->config_mms_content_disposition_support:I
 Lcom/android/internal/R$bool;->config_showNavigationBar:I
 Lcom/android/internal/R$dimen;-><init>()V
@@ -8161,6 +7015,8 @@
 Lcom/android/internal/R$styleable;->Window:[I
 Lcom/android/internal/R$styleable;->WindowAnimation:[I
 Lcom/android/internal/R$styleable;->Window_windowActionBarFullscreenDecorLayout:I
+Lcom/android/internal/R$styleable;->Window_windowBackground:I
+Lcom/android/internal/R$styleable;->Window_windowFullscreen:I
 Lcom/android/internal/R$styleable;->Window_windowIsFloating:I
 Lcom/android/internal/R$styleable;->Window_windowIsTranslucent:I
 Lcom/android/internal/R$styleable;->Window_windowShowWallpaper:I
@@ -8217,7 +7073,7 @@
 Lcom/android/internal/telephony/GsmAlphabet;->stringToGsm7BitPacked(Ljava/lang/String;IZII)[B
 Lcom/android/internal/telephony/GsmAlphabet;->stringToGsm7BitPackedWithHeader(Ljava/lang/String;[BII)[B
 Lcom/android/internal/telephony/GsmAlphabet;->stringToGsm8BitPacked(Ljava/lang/String;)[B
-Lcom/android/internal/telephony/ICarrierConfigLoader;->getConfigForSubId(I)Landroid/os/PersistableBundle;
+Lcom/android/internal/telephony/ICarrierConfigLoader;->getConfigForSubId(ILjava/lang/String;)Landroid/os/PersistableBundle;
 Lcom/android/internal/telephony/IMms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IMms;
 Lcom/android/internal/telephony/IPhoneStateListener$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneStateListener;
 Lcom/android/internal/telephony/IPhoneStateListener;->onCallForwardingIndicatorChanged(Z)V
@@ -8230,6 +7086,7 @@
 Lcom/android/internal/telephony/IPhoneStateListener;->onSignalStrengthChanged(I)V
 Lcom/android/internal/telephony/IPhoneStateListener;->onSignalStrengthsChanged(Landroid/telephony/SignalStrength;)V
 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
@@ -8376,11 +7233,7 @@
 Lcom/android/internal/util/AsyncChannel;->sendMessageSynchronously(Landroid/os/Message;)Landroid/os/Message;
 Lcom/android/internal/util/AsyncChannel;->STATUS_SUCCESSFUL:I
 Lcom/android/internal/util/FastPrintWriter;-><init>(Ljava/io/OutputStream;)V
-Lcom/android/internal/util/JournaledFile;-><init>(Ljava/io/File;Ljava/io/File;)V
-Lcom/android/internal/util/JournaledFile;->chooseForRead()Ljava/io/File;
-Lcom/android/internal/util/JournaledFile;->chooseForWrite()Ljava/io/File;
-Lcom/android/internal/util/JournaledFile;->commit()V
-Lcom/android/internal/util/JournaledFile;->rollback()V
+Lcom/android/internal/util/HexDump;->toHexString([BZ)Ljava/lang/String;
 Lcom/android/internal/util/XmlUtils;->convertValueToBoolean(Ljava/lang/CharSequence;Z)Z
 Lcom/android/internal/util/XmlUtils;->convertValueToInt(Ljava/lang/CharSequence;I)I
 Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap;
@@ -8415,6 +7268,8 @@
 Lcom/android/internal/view/menu/MenuBuilder;->addMenuPresenter(Lcom/android/internal/view/menu/MenuPresenter;Landroid/content/Context;)V
 Lcom/android/internal/view/menu/MenuBuilder;->collapseItemActionView(Lcom/android/internal/view/menu/MenuItemImpl;)Z
 Lcom/android/internal/view/menu/MenuBuilder;->getContext()Landroid/content/Context;
+Lcom/android/internal/view/menu/MenuBuilder;->getHeaderIcon()Landroid/graphics/drawable/Drawable;
+Lcom/android/internal/view/menu/MenuBuilder;->getHeaderTitle()Ljava/lang/CharSequence;
 Lcom/android/internal/view/menu/MenuBuilder;->getNonActionItems()Ljava/util/ArrayList;
 Lcom/android/internal/view/menu/MenuBuilder;->getRootMenu()Lcom/android/internal/view/menu/MenuBuilder;
 Lcom/android/internal/view/menu/MenuBuilder;->getVisibleItems()Ljava/util/ArrayList;
@@ -8432,11 +7287,14 @@
 Lcom/android/internal/view/menu/MenuItemImpl;->requestsActionButton()Z
 Lcom/android/internal/view/menu/MenuItemImpl;->requiresActionButton()Z
 Lcom/android/internal/view/menu/MenuItemImpl;->setActionViewExpanded(Z)V
+Lcom/android/internal/view/menu/MenuItemImpl;->setExclusiveCheckable(Z)V
 Lcom/android/internal/view/menu/MenuItemImpl;->setMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V
 Lcom/android/internal/view/menu/MenuPopupHelper;-><init>(Landroid/content/Context;Lcom/android/internal/view/menu/MenuBuilder;)V
 Lcom/android/internal/view/menu/MenuPopupHelper;-><init>(Landroid/content/Context;Lcom/android/internal/view/menu/MenuBuilder;Landroid/view/View;)V
 Lcom/android/internal/view/menu/MenuPopupHelper;->dismiss()V
+Lcom/android/internal/view/menu/MenuPopupHelper;->getPopup()Lcom/android/internal/view/menu/MenuPopup;
 Lcom/android/internal/view/menu/MenuPopupHelper;->mForceShowIcon:Z
+Lcom/android/internal/view/menu/MenuPopupHelper;->setAnchorView(Landroid/view/View;)V
 Lcom/android/internal/view/menu/MenuPopupHelper;->setForceShowIcon(Z)V
 Lcom/android/internal/view/menu/MenuPopupHelper;->setGravity(I)V
 Lcom/android/internal/view/menu/MenuPopupHelper;->show()V
@@ -8465,6 +7323,7 @@
 Lcom/android/internal/widget/IRemoteViewsFactory;->hasStableIds()Z
 Lcom/android/internal/widget/IRemoteViewsFactory;->isCreated()Z
 Lcom/android/internal/widget/IRemoteViewsFactory;->onDataSetChanged()V
+Lcom/android/internal/widget/ScrollBarUtils;->getThumbLength(IIII)I
 Lcom/android/internal/widget/ScrollingTabContainerView;-><init>(Landroid/content/Context;)V
 Lcom/android/internal/widget/ScrollingTabContainerView;->addTab(Landroid/app/ActionBar$Tab;IZ)V
 Lcom/android/internal/widget/ScrollingTabContainerView;->addTab(Landroid/app/ActionBar$Tab;Z)V
@@ -8536,6 +7395,8 @@
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->setNpnProtocols([B)V
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->setSoWriteTimeout(I)V
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->setUseSessionTickets(Z)V
+Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setHostname(Ljava/lang/String;)V
+Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setUseSessionTickets(Z)V
 Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostname()Ljava/lang/String;
 Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostnameOrIP()Ljava/lang/String;
 Lcom/android/org/conscrypt/ConscryptSocketBase;->getSoWriteTimeout()I
@@ -8766,6 +7627,7 @@
 Ljava/lang/reflect/Field;->getOffset()I
 Ljava/lang/reflect/Parameter;-><init>(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V
 Ljava/lang/reflect/Proxy;->invoke(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/Runtime;-><init>()V
 Ljava/lang/Runtime;->load(Ljava/lang/String;Ljava/lang/ClassLoader;)V
 Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V
 Ljava/lang/Runtime;->loadLibrary0(Ljava/lang/ClassLoader;Ljava/lang/String;)V
@@ -9004,46 +7866,6 @@
 Ljava/util/zip/Inflater;->len:I
 Ljava/util/zip/Inflater;->needDict:Z
 Ljava/util/zip/Inflater;->off:I
-Ljava/util/zip/ZipConstants;->CENATT:I
-Ljava/util/zip/ZipConstants;->CENATX:I
-Ljava/util/zip/ZipConstants;->CENCOM:I
-Ljava/util/zip/ZipConstants;->CENCRC:I
-Ljava/util/zip/ZipConstants;->CENDSK:I
-Ljava/util/zip/ZipConstants;->CENEXT:I
-Ljava/util/zip/ZipConstants;->CENFLG:I
-Ljava/util/zip/ZipConstants;->CENHDR:I
-Ljava/util/zip/ZipConstants;->CENHOW:I
-Ljava/util/zip/ZipConstants;->CENLEN:I
-Ljava/util/zip/ZipConstants;->CENNAM:I
-Ljava/util/zip/ZipConstants;->CENOFF:I
-Ljava/util/zip/ZipConstants;->CENSIG:J
-Ljava/util/zip/ZipConstants;->CENSIZ:I
-Ljava/util/zip/ZipConstants;->CENTIM:I
-Ljava/util/zip/ZipConstants;->CENVEM:I
-Ljava/util/zip/ZipConstants;->CENVER:I
-Ljava/util/zip/ZipConstants;->ENDCOM:I
-Ljava/util/zip/ZipConstants;->ENDHDR:I
-Ljava/util/zip/ZipConstants;->ENDOFF:I
-Ljava/util/zip/ZipConstants;->ENDSIG:J
-Ljava/util/zip/ZipConstants;->ENDSIZ:I
-Ljava/util/zip/ZipConstants;->ENDSUB:I
-Ljava/util/zip/ZipConstants;->ENDTOT:I
-Ljava/util/zip/ZipConstants;->EXTCRC:I
-Ljava/util/zip/ZipConstants;->EXTHDR:I
-Ljava/util/zip/ZipConstants;->EXTLEN:I
-Ljava/util/zip/ZipConstants;->EXTSIG:J
-Ljava/util/zip/ZipConstants;->EXTSIZ:I
-Ljava/util/zip/ZipConstants;->LOCCRC:I
-Ljava/util/zip/ZipConstants;->LOCEXT:I
-Ljava/util/zip/ZipConstants;->LOCFLG:I
-Ljava/util/zip/ZipConstants;->LOCHDR:I
-Ljava/util/zip/ZipConstants;->LOCHOW:I
-Ljava/util/zip/ZipConstants;->LOCLEN:I
-Ljava/util/zip/ZipConstants;->LOCNAM:I
-Ljava/util/zip/ZipConstants;->LOCSIG:J
-Ljava/util/zip/ZipConstants;->LOCSIZ:I
-Ljava/util/zip/ZipConstants;->LOCTIM:I
-Ljava/util/zip/ZipConstants;->LOCVER:I
 Ljava/util/zip/ZipEntry;-><init>(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V
 Ljava/util/zip/ZipEntry;->method:I
 Ljava/util/zip/ZipFile;->close(J)V
@@ -9058,6 +7880,16 @@
 Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
 Ljavax/net/ssl/SSLSocketFactory;->createSocket(Ljava/net/Socket;Ljava/io/InputStream;Z)Ljava/net/Socket;
 Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
+Llibcore/icu/ICU;->addLikelySubtags(Ljava/util/Locale;)Ljava/util/Locale;
+Llibcore/io/Memory;->peekByte(J)B
+Llibcore/io/Memory;->peekByteArray(J[BII)V
+Llibcore/io/Memory;->peekInt(JZ)I
+Llibcore/io/Memory;->peekLong(JZ)J
+Llibcore/io/Memory;->pokeByte(JB)V
+Llibcore/io/Memory;->pokeByteArray(J[BII)V
+Llibcore/io/Memory;->pokeInt(JIZ)V
+Llibcore/io/Memory;->pokeLong(JJZ)V
+Llibcore/io/Streams;->copy(Ljava/io/InputStream;Ljava/io/OutputStream;)I
 Llibcore/util/BasicLruCache;->map:Ljava/util/LinkedHashMap;
 Llibcore/util/ZoneInfo;->mTransitions:[J
 Lorg/apache/http/conn/ssl/AbstractVerifier;->BAD_COUNTRY_2LDS:[Ljava/lang/String;
@@ -9080,6 +7912,8 @@
 Lorg/ccil/cowan/tagsoup/ElementType;->theNamespace:Ljava/lang/String;
 Lorg/ccil/cowan/tagsoup/ElementType;->theParent:Lorg/ccil/cowan/tagsoup/ElementType;
 Lorg/ccil/cowan/tagsoup/ElementType;->theSchema:Lorg/ccil/cowan/tagsoup/Schema;
+Lorg/ccil/cowan/tagsoup/HTMLSchema;-><init>()V
+Lorg/ccil/cowan/tagsoup/Parser;-><init>()V
 Lorg/ccil/cowan/tagsoup/Schema;->theElementTypes:Ljava/util/HashMap;
 Lorg/ccil/cowan/tagsoup/Schema;->theEntities:Ljava/util/HashMap;
 Lorg/ccil/cowan/tagsoup/Schema;->thePrefix:Ljava/lang/String;
@@ -9134,6 +7968,7 @@
 Lorg/xml/sax/helpers/NamespaceSupport;->contexts:[Lorg/xml/sax/helpers/NamespaceSupport$Context;
 Lorg/xml/sax/helpers/NamespaceSupport;->currentContext:Lorg/xml/sax/helpers/NamespaceSupport$Context;
 Lorg/xml/sax/helpers/NamespaceSupport;->EMPTY_ENUMERATION:Ljava/util/Enumeration;
+Lorg/xml/sax/helpers/NamespaceSupport;->namespaceDeclUris:Z
 Lorg/xml/sax/helpers/ParserAdapter;->attAdapter:Lorg/xml/sax/helpers/ParserAdapter$AttributeListAdapter;
 Lorg/xml/sax/helpers/ParserAdapter;->atts:Lorg/xml/sax/helpers/AttributesImpl;
 Lorg/xml/sax/helpers/ParserAdapter;->checkNotParsing(Ljava/lang/String;Ljava/lang/String;)V
@@ -9153,6 +7988,7 @@
 Lorg/xml/sax/helpers/ParserAdapter;->reportError(Ljava/lang/String;)V
 Lorg/xml/sax/helpers/ParserAdapter;->setup(Lorg/xml/sax/Parser;)V
 Lorg/xml/sax/helpers/ParserAdapter;->setupParser()V
+Lorg/xml/sax/helpers/ParserAdapter;->uris:Z
 Lorg/xml/sax/helpers/XMLFilterImpl;->contentHandler:Lorg/xml/sax/ContentHandler;
 Lorg/xml/sax/helpers/XMLFilterImpl;->dtdHandler:Lorg/xml/sax/DTDHandler;
 Lorg/xml/sax/helpers/XMLFilterImpl;->entityResolver:Lorg/xml/sax/EntityResolver;
@@ -9177,10 +8013,84 @@
 Lorg/xml/sax/SAXParseException;->lineNumber:I
 Lorg/xml/sax/SAXParseException;->publicId:Ljava/lang/String;
 Lorg/xml/sax/SAXParseException;->systemId:Ljava/lang/String;
+Lsun/misc/Cleaner;->clean()V
+Lsun/misc/Unsafe;->addressSize()I
+Lsun/misc/Unsafe;->allocateInstance(Ljava/lang/Class;)Ljava/lang/Object;
+Lsun/misc/Unsafe;->allocateMemory(J)J
+Lsun/misc/Unsafe;->arrayBaseOffset(Ljava/lang/Class;)I
+Lsun/misc/Unsafe;->arrayIndexScale(Ljava/lang/Class;)I
+Lsun/misc/Unsafe;->compareAndSwapInt(Ljava/lang/Object;JII)Z
+Lsun/misc/Unsafe;->compareAndSwapLong(Ljava/lang/Object;JJJ)Z
+Lsun/misc/Unsafe;->compareAndSwapObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z
+Lsun/misc/Unsafe;->copyMemory(JJJ)V
+Lsun/misc/Unsafe;->copyMemoryFromPrimitiveArray(Ljava/lang/Object;JJJ)V
+Lsun/misc/Unsafe;->copyMemoryToPrimitiveArray(JLjava/lang/Object;JJ)V
+Lsun/misc/Unsafe;->freeMemory(J)V
+Lsun/misc/Unsafe;->fullFence()V
+Lsun/misc/Unsafe;->getAndAddInt(Ljava/lang/Object;JI)I
+Lsun/misc/Unsafe;->getAndAddLong(Ljava/lang/Object;JJ)J
+Lsun/misc/Unsafe;->getAndSetInt(Ljava/lang/Object;JI)I
+Lsun/misc/Unsafe;->getAndSetLong(Ljava/lang/Object;JJ)J
+Lsun/misc/Unsafe;->getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;
+Lsun/misc/Unsafe;->getArrayBaseOffsetForComponentType(Ljava/lang/Class;)I
+Lsun/misc/Unsafe;->getArrayIndexScaleForComponentType(Ljava/lang/Class;)I
+Lsun/misc/Unsafe;->getBoolean(Ljava/lang/Object;J)Z
+Lsun/misc/Unsafe;->getByte(J)B
+Lsun/misc/Unsafe;->getByte(Ljava/lang/Object;J)B
+Lsun/misc/Unsafe;->getChar(J)C
+Lsun/misc/Unsafe;->getChar(Ljava/lang/Object;J)C
+Lsun/misc/Unsafe;->getDouble(J)D
+Lsun/misc/Unsafe;->getDouble(Ljava/lang/Object;J)D
+Lsun/misc/Unsafe;->getFloat(J)F
+Lsun/misc/Unsafe;->getFloat(Ljava/lang/Object;J)F
+Lsun/misc/Unsafe;->getInt(J)I
+Lsun/misc/Unsafe;->getInt(Ljava/lang/Object;J)I
+Lsun/misc/Unsafe;->getIntVolatile(Ljava/lang/Object;J)I
+Lsun/misc/Unsafe;->getLong(J)J
+Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J
+Lsun/misc/Unsafe;->getLongVolatile(Ljava/lang/Object;J)J
+Lsun/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object;
+Lsun/misc/Unsafe;->getObjectVolatile(Ljava/lang/Object;J)Ljava/lang/Object;
+Lsun/misc/Unsafe;->getShort(J)S
+Lsun/misc/Unsafe;->getShort(Ljava/lang/Object;J)S
+Lsun/misc/Unsafe;->getUnsafe()Lsun/misc/Unsafe;
+Lsun/misc/Unsafe;->INVALID_FIELD_OFFSET:I
+Lsun/misc/Unsafe;->loadFence()V
+Lsun/misc/Unsafe;->objectFieldOffset(Ljava/lang/reflect/Field;)J
+Lsun/misc/Unsafe;->pageSize()I
+Lsun/misc/Unsafe;->park(ZJ)V
+Lsun/misc/Unsafe;->putBoolean(Ljava/lang/Object;JZ)V
+Lsun/misc/Unsafe;->putByte(JB)V
+Lsun/misc/Unsafe;->putByte(Ljava/lang/Object;JB)V
+Lsun/misc/Unsafe;->putChar(JC)V
+Lsun/misc/Unsafe;->putChar(Ljava/lang/Object;JC)V
+Lsun/misc/Unsafe;->putDouble(JD)V
+Lsun/misc/Unsafe;->putDouble(Ljava/lang/Object;JD)V
+Lsun/misc/Unsafe;->putFloat(JF)V
+Lsun/misc/Unsafe;->putFloat(Ljava/lang/Object;JF)V
+Lsun/misc/Unsafe;->putInt(JI)V
+Lsun/misc/Unsafe;->putInt(Ljava/lang/Object;JI)V
+Lsun/misc/Unsafe;->putIntVolatile(Ljava/lang/Object;JI)V
+Lsun/misc/Unsafe;->putLong(JJ)V
+Lsun/misc/Unsafe;->putLong(Ljava/lang/Object;JJ)V
+Lsun/misc/Unsafe;->putLongVolatile(Ljava/lang/Object;JJ)V
+Lsun/misc/Unsafe;->putObject(Ljava/lang/Object;JLjava/lang/Object;)V
+Lsun/misc/Unsafe;->putObjectVolatile(Ljava/lang/Object;JLjava/lang/Object;)V
+Lsun/misc/Unsafe;->putOrderedInt(Ljava/lang/Object;JI)V
+Lsun/misc/Unsafe;->putOrderedLong(Ljava/lang/Object;JJ)V
+Lsun/misc/Unsafe;->putOrderedObject(Ljava/lang/Object;JLjava/lang/Object;)V
+Lsun/misc/Unsafe;->putShort(JS)V
+Lsun/misc/Unsafe;->putShort(Ljava/lang/Object;JS)V
+Lsun/misc/Unsafe;->setMemory(JJB)V
+Lsun/misc/Unsafe;->storeFence()V
 Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
 Lsun/misc/Unsafe;->THE_ONE:Lsun/misc/Unsafe;
+Lsun/misc/Unsafe;->unpark(Ljava/lang/Object;)V
 Lsun/misc/URLClassPath$JarLoader;->getJarFile()Ljava/util/jar/JarFile;
 Lsun/misc/URLClassPath;->lmap:Ljava/util/HashMap;
 Lsun/misc/URLClassPath;->loaders:Ljava/util/ArrayList;
 Lsun/misc/URLClassPath;->urls:Ljava/util/Stack;
+Lsun/nio/ch/DirectBuffer;->cleaner()Lsun/misc/Cleaner;
+Lsun/security/x509/AlgorithmId;->get(Ljava/lang/String;)Lsun/security/x509/AlgorithmId;
+Lsun/security/x509/AlgorithmId;->getName()Ljava/lang/String;
 Lsun/security/x509/AVA;->hasRFC2253Keyword()Z
diff --git a/config/hiddenapi-p-light-greylist.txt b/config/hiddenapi-p-light-greylist.txt
index 1bcb685..e360879 100644
--- a/config/hiddenapi-p-light-greylist.txt
+++ b/config/hiddenapi-p-light-greylist.txt
@@ -387,10 +387,6 @@
 Landroid/app/AppOpsManager$OpEntry;->getOp()I
 Landroid/app/AppOpsManager$OpEntry;->getRejectTime()J
 Landroid/app/AppOpsManager$OpEntry;->getTime()J
-Landroid/app/AppOpsManager$OpEntry;->isRunning()Z
-Landroid/app/AppOpsManager$PackageOps;->getOps()Ljava/util/List;
-Landroid/app/AppOpsManager$PackageOps;->getPackageName()Ljava/lang/String;
-Landroid/app/AppOpsManager$PackageOps;->getUid()I
 Landroid/app/AppOpsManager;->OP_AUDIO_NOTIFICATION_VOLUME:I
 Landroid/app/AppOpsManager;->OP_COARSE_LOCATION:I
 Landroid/app/AppOpsManager;->OP_FINE_LOCATION:I
@@ -1630,8 +1626,6 @@
 Landroid/hardware/display/IDisplayManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/display/IDisplayManager;
 Landroid/hardware/display/WifiDisplayStatus;->mActiveDisplay:Landroid/hardware/display/WifiDisplay;
 Landroid/hardware/display/WifiDisplayStatus;->mDisplays:[Landroid/hardware/display/WifiDisplay;
-Landroid/hardware/fingerprint/Fingerprint;->getFingerId()I
-Landroid/hardware/fingerprint/Fingerprint;->getName()Ljava/lang/CharSequence;
 Landroid/hardware/fingerprint/IFingerprintService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/hardware/input/IInputManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager;
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 48f44e6..4b4250d 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -1,7 +1,5 @@
-Landroid/accounts/AccountManager;-><init>(Landroid/content/Context;Landroid/accounts/IAccountManager;Landroid/os/Handler;)V
 Landroid/app/Activity;->managedQuery(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
 Landroid/app/Activity;->registerRemoteAnimations(Landroid/view/RemoteAnimationDefinition;)V
-Landroid/app/ActivityManager$RecentTaskInfo;->configuration:Landroid/content/res/Configuration;
 Landroid/app/ActivityManager$TaskDescription;->loadTaskDescriptionIcon(Ljava/lang/String;I)Landroid/graphics/Bitmap;
 Landroid/app/ActivityManager$TaskSnapshot;->getSnapshot()Landroid/graphics/GraphicBuffer;
 Landroid/app/ActivityManagerNative;->broadcastStickyIntent(Landroid/content/Intent;Ljava/lang/String;I)V
@@ -13,10 +11,6 @@
 Landroid/app/ActivityView;->startActivity(Landroid/content/Intent;)V
 Landroid/app/AppOpsManager$OpEntry;->getOp()I
 Landroid/app/AppOpsManager$OpEntry;->getTime()J
-Landroid/app/AppOpsManager$OpEntry;->isRunning()Z
-Landroid/app/AppOpsManager$PackageOps;->getOps()Ljava/util/List;
-Landroid/app/AppOpsManager$PackageOps;->getPackageName()Ljava/lang/String;
-Landroid/app/AppOpsManager$PackageOps;->getUid()I
 Landroid/app/AppOpsManager;->getPackagesForOps([I)Ljava/util/List;
 Landroid/app/AppOpsManager;->getToken(Lcom/android/internal/app/IAppOpsService;)Landroid/os/IBinder;
 Landroid/app/IActivityController$Stub;-><init>()V
@@ -44,6 +38,7 @@
 Landroid/app/NotificationManager;->cancelAsUser(Ljava/lang/String;ILandroid/os/UserHandle;)V
 Landroid/app/StatusBarManager;->removeIcon(Ljava/lang/String;)V
 Landroid/app/StatusBarManager;->setIcon(Ljava/lang/String;IILjava/lang/String;)V
+Landroid/app/TaskInfo;->configuration:Landroid/content/res/Configuration;
 Landroid/app/TaskStackListener;->onActivityDismissingDockedStack()V
 Landroid/app/TaskStackListener;->onActivityForcedResizable(Ljava/lang/String;II)V
 Landroid/app/TaskStackListener;->onActivityLaunchOnSecondaryDisplayFailed()V
@@ -59,7 +54,6 @@
 Landroid/app/TaskStackListener;->onTaskSnapshotChanged(ILandroid/app/ActivityManager$TaskSnapshot;)V
 Landroid/app/TaskStackListener;->onTaskStackChanged()V
 Landroid/app/WallpaperColors;-><init>(Landroid/graphics/Color;Landroid/graphics/Color;Landroid/graphics/Color;I)V
-Landroid/bluetooth/BluetoothHeadset;->phoneStateChanged(IIILjava/lang/String;I)V
 Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V
 Landroid/companion/AssociationRequest;->getDeviceFilters()Ljava/util/List;
 Landroid/companion/AssociationRequest;->isSingleDevice()Z
@@ -97,14 +91,6 @@
 Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V
 Landroid/database/sqlite/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
 Landroid/database/sqlite/SqliteWrapper;->query(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
-Landroid/graphics/Bitmap;->createGraphicBufferHandle()Landroid/graphics/GraphicBuffer;
-Landroid/graphics/Bitmap;->createHardwareBitmap(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;
-Landroid/graphics/drawable/Drawable;->isProjected()Z
-Landroid/graphics/drawable/Drawable;->updateTintFilter(Landroid/graphics/PorterDuffColorFilter;Landroid/content/res/ColorStateList;Landroid/graphics/PorterDuff$Mode;)Landroid/graphics/PorterDuffColorFilter;
-Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
-Landroid/hardware/display/DisplayManagerGlobal;->getInstance()Landroid/hardware/display/DisplayManagerGlobal;
-Landroid/hardware/display/DisplayManagerGlobal;->getRealDisplay(I)Landroid/view/Display;
-Landroid/hardware/location/GeofenceHardware;-><init>(Landroid/hardware/location/IGeofenceHardware;)V
 Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
 Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
 Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
@@ -374,8 +360,6 @@
 Landroid/os/UserHandle;->isSameApp(II)Z
 Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
 Landroid/os/UserManager;->isAdminUser()Z
-Landroid/print/PrintDocumentAdapter$LayoutResultCallback;-><init>()V
-Landroid/print/PrintDocumentAdapter$WriteResultCallback;-><init>()V
 Landroid/provider/CalendarContract$Events;->PROVIDER_WRITABLE_COLUMNS:[Ljava/lang/String;
 Landroid/provider/ContactsContract$CommonDataKinds$Phone;->getDisplayLabel(Landroid/content/Context;ILjava/lang/CharSequence;)Ljava/lang/CharSequence;
 Landroid/provider/Settings$Global;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String;
@@ -400,13 +384,6 @@
 Landroid/system/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
 Landroid/system/PacketSocketAddress;-><init>(I[B)V
 Landroid/system/PacketSocketAddress;-><init>(SI)V
-Landroid/telecom/ParcelableCall;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/telecom/ParcelableCall;->getConnectTimeMillis()J
-Landroid/telecom/ParcelableCall;->getDisconnectCause()Landroid/telecom/DisconnectCause;
-Landroid/telecom/ParcelableCall;->getHandle()Landroid/net/Uri;
-Landroid/telecom/ParcelableCall;->getId()Ljava/lang/String;
-Landroid/telecom/TelecomManager;->from(Landroid/content/Context;)Landroid/telecom/TelecomManager;
-Landroid/telecom/VideoProfile$CameraCapabilities;-><init>(IIZF)V
 Landroid/telephony/euicc/DownloadableSubscription;->encodedActivationCode:Ljava/lang/String;
 Landroid/telephony/euicc/DownloadableSubscription;->setAccessRules([Landroid/telephony/UiccAccessRule;)V
 Landroid/telephony/euicc/DownloadableSubscription;->setCarrierName(Ljava/lang/String;)V
@@ -615,31 +592,6 @@
 Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
 Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V
 Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/uce/common/CapInfo;->getCapTimestamp()J
-Lcom/android/ims/internal/uce/common/CapInfo;->isCdViaPresenceSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFtHttpSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFtSnFSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFtSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFtThumbSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFullSnFGroupChatSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isGeoPullFtSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isGeoPullSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isGeoPushSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isImSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isIpVideoSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isIpVoiceSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isIsSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isRcsIpVideoCallSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isRcsIpVideoOnlyCallSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isRcsIpVoiceCallSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isSmSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isSpSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isVsDuringCSSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isVsSupported()Z
-Lcom/android/ims/internal/uce/common/StatusCode;->getStatusCode()I
-Lcom/android/ims/internal/uce/common/UceLong;-><init>()V
-Lcom/android/ims/internal/uce/common/UceLong;->getClientId()I
-Lcom/android/ims/internal/uce/common/UceLong;->setClientId(I)V
 Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V
 Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V
 Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V
@@ -655,24 +607,6 @@
 Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;-><init>()V
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;->getCapInfo()Lcom/android/ims/internal/uce/common/CapInfo;
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;->getSdp()Ljava/lang/String;
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;->setCapInfo(Lcom/android/ims/internal/uce/common/CapInfo;)V
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;->setSdp(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/options/OptionsCmdId;-><init>()V
-Lcom/android/ims/internal/uce/options/OptionsCmdId;->setCmdId(I)V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;-><init>()V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setCapInfo(Lcom/android/ims/internal/uce/common/CapInfo;)V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setCmdId(Lcom/android/ims/internal/uce/options/OptionsCmdId;)V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setStatus(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setUserData(I)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;-><init>()V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setCmdId(Lcom/android/ims/internal/uce/options/OptionsCmdId;)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setReasonPhrase(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setRequestId(I)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setRetryAfter(I)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setSipResponseCode(I)V
 Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
 Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V
 Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V
@@ -691,18 +625,6 @@
 Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/PresCapInfo;->getCapInfo()Lcom/android/ims/internal/uce/common/CapInfo;
-Lcom/android/ims/internal/uce/presence/PresCapInfo;->getContactUri()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresCapInfo;->mContactUri:Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getMediaType()I
-Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceDesc()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceId()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceVer()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getCmdId()Lcom/android/ims/internal/uce/presence/PresCmdId;
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getReasonPhrase()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getRequestId()I
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getRetryAfter()I
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getSipResponseCode()I
 Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V
 Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
 Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 829a9444..5b73eaa 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.UnsupportedAppUsage;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
@@ -31,7 +32,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
@@ -398,17 +398,55 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "SHOW_MODE_" }, value = {
             SHOW_MODE_AUTO,
-            SHOW_MODE_HIDDEN
+            SHOW_MODE_HIDDEN,
+            SHOW_MODE_WITH_HARD_KEYBOARD
     })
     public @interface SoftKeyboardShowMode {}
 
+    /**
+     * Allow the system to control when the soft keyboard is shown.
+     * @see SoftKeyboardController
+     */
     public static final int SHOW_MODE_AUTO = 0;
+
+    /**
+     * Never show the soft keyboard.
+     * @see SoftKeyboardController
+     */
     public static final int SHOW_MODE_HIDDEN = 1;
 
+    /**
+     * Allow the soft keyboard to be shown, even if a hard keyboard is connected
+     * @see SoftKeyboardController
+     */
+    public static final int SHOW_MODE_WITH_HARD_KEYBOARD = 2;
+
+    /**
+     * Mask used to cover the show modes supported in public API
+     * @hide
+     */
+    public static final int SHOW_MODE_MASK = 0x03;
+
+    /**
+     * Bit used to hold the old value of the hard IME setting to restore when a service is shut
+     * down.
+     * @hide
+     */
+    public static final int SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE = 0x20000000;
+
+    /**
+     * Bit for show mode setting to indicate that the user has overridden the hard keyboard
+     * behavior.
+     * @hide
+     */
+    public static final int SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN = 0x40000000;
+
     private int mConnectionId = AccessibilityInteractionClient.NO_ID;
 
+    @UnsupportedAppUsage
     private AccessibilityServiceInfo mInfo;
 
+    @UnsupportedAppUsage
     private IBinder mWindowToken;
 
     private WindowManager mWindowManager;
@@ -1147,7 +1185,27 @@
     }
 
     /**
-     * Used to control and query the soft keyboard show mode.
+     * Used to control, query, and listen for changes to the soft keyboard show mode.
+     * <p>
+     * Accessibility services may request to override the decisions normally made about whether or
+     * not the soft keyboard is shown.
+     * <p>
+     * If multiple services make conflicting requests, the last request is honored. A service may
+     * register a listener to find out if the mode has changed under it.
+     * <p>
+     * If the user takes action to override the behavior behavior requested by an accessibility
+     * service, the user's request takes precendence, the show mode will be reset to
+     * {@link AccessibilityService#SHOW_MODE_AUTO}, and services will no longer be able to control
+     * that aspect of the soft keyboard's behavior.
+     * <p>
+     * Note: Because soft keyboards are independent apps, the framework does not have total control
+     * over their behavior. They may choose to show themselves, or not, without regard to requests
+     * made here. So the framework will make a best effort to deliver the behavior requested, but
+     * cannot guarantee success.
+     *
+     * @see AccessibilityService#SHOW_MODE_AUTO
+     * @see AccessibilityService#SHOW_MODE_HIDDEN
+     * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
      */
     public static final class SoftKeyboardController {
         private final AccessibilityService mService;
@@ -1217,7 +1275,8 @@
          * @param listener the listener to remove, must be non-null
          * @return {@code true} if the listener was removed, {@code false} otherwise
          */
-        public boolean removeOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
+        public boolean removeOnShowModeChangedListener(
+                @NonNull OnShowModeChangedListener listener) {
             if (mListeners == null) {
                 return false;
             }
@@ -1289,32 +1348,32 @@
         }
 
         /**
-         * Returns the show mode of the soft keyboard. The default show mode is
-         * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
-         * focused. An AccessibilityService can also request the show mode
-         * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
+         * Returns the show mode of the soft keyboard.
          *
          * @return the current soft keyboard show mode
+         *
+         * @see AccessibilityService#SHOW_MODE_AUTO
+         * @see AccessibilityService#SHOW_MODE_HIDDEN
+         * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
          */
         @SoftKeyboardShowMode
         public int getShowMode() {
-           try {
-               return Settings.Secure.getInt(mService.getContentResolver(),
-                       Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
-           } catch (Settings.SettingNotFoundException e) {
-               Log.v(LOG_TAG, "Failed to obtain the soft keyboard mode", e);
-               // The settings hasn't been changed yet, so it's value is null. Return the default.
-               return 0;
-           }
+            final IAccessibilityServiceConnection connection =
+                    AccessibilityInteractionClient.getInstance().getConnection(
+                            mService.mConnectionId);
+            if (connection != null) {
+                try {
+                    return connection.getSoftKeyboardShowMode();
+                } catch (RemoteException re) {
+                    Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re);
+                    re.rethrowFromSystemServer();
+                }
+            }
+            return SHOW_MODE_AUTO;
         }
 
         /**
-         * Sets the soft keyboard show mode. The default show mode is
-         * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
-         * focused. An AccessibilityService can also request the show mode
-         * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. The
-         * The lastto this method will be honored, regardless of any previous calls (including those
-         * made by other AccessibilityServices).
+         * Sets the soft keyboard show mode.
          * <p>
          * <strong>Note:</strong> If the service is not yet connected (e.g.
          * {@link AccessibilityService#onServiceConnected()} has not yet been called) or the
@@ -1322,6 +1381,10 @@
          *
          * @param showMode the new show mode for the soft keyboard
          * @return {@code true} on success
+         *
+         * @see AccessibilityService#SHOW_MODE_AUTO
+         * @see AccessibilityService#SHOW_MODE_HIDDEN
+         * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
          */
         public boolean setShowMode(@SoftKeyboardShowMode int showMode) {
            final IAccessibilityServiceConnection connection =
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index ed684d7..be2e2fa 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.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -695,6 +696,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCapabilities(int capabilities) {
         mCapabilities = capabilities;
     }
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 037aeb0..276131f 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -87,6 +87,8 @@
 
     boolean setSoftKeyboardShowMode(int showMode);
 
+    int getSoftKeyboardShowMode();
+
     void setSoftKeyboardCallbackEnabled(boolean enabled);
 
     boolean isAccessibilityButtonAvailable();
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
index b6e85f1..f07f5ec 100644
--- a/core/java/android/accounts/Account.java
+++ b/core/java/android/accounts/Account.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Parcelable;
 import android.os.Parcel;
@@ -36,6 +37,7 @@
  * suitable for use as the key of a {@link java.util.Map}
  */
 public class Account implements Parcelable {
+    @UnsupportedAppUsage
     private static final String TAG = "Account";
 
     @GuardedBy("sAccessedAccounts")
@@ -43,6 +45,7 @@
 
     public final String name;
     public final String type;
+    @UnsupportedAppUsage
     private final @Nullable String accessId;
 
     public boolean equals(Object o) {
diff --git a/core/java/android/accounts/AccountAndUser.java b/core/java/android/accounts/AccountAndUser.java
index 04157cc..b0d5343 100644
--- a/core/java/android/accounts/AccountAndUser.java
+++ b/core/java/android/accounts/AccountAndUser.java
@@ -16,15 +16,20 @@
 
 package android.accounts;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * Used to store the Account and the UserId this account is associated with.
  *
  * @hide
  */
 public class AccountAndUser {
+    @UnsupportedAppUsage
     public Account account;
+    @UnsupportedAppUsage
     public int userId;
 
+    @UnsupportedAppUsage
     public AccountAndUser(Account account, int userId) {
         this.account = account;
         this.userId = userId;
diff --git a/core/java/android/accounts/AccountAuthenticatorResponse.java b/core/java/android/accounts/AccountAuthenticatorResponse.java
index 41f26ac..bcc9f90 100644
--- a/core/java/android/accounts/AccountAuthenticatorResponse.java
+++ b/core/java/android/accounts/AccountAuthenticatorResponse.java
@@ -16,6 +16,7 @@
 
 package android.accounts;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.Parcel;
@@ -33,6 +34,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public AccountAuthenticatorResponse(IAccountAuthenticatorResponse response) {
         mAccountAuthenticatorResponse = response;
     }
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 5176d71e..3189d08 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -27,6 +27,7 @@
 import android.annotation.SystemService;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.BroadcastBehavior;
+import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -336,6 +337,7 @@
     public static final String ACCOUNT_ACCESS_TOKEN_TYPE =
             "com.android.AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE";
 
+    @UnsupportedAppUsage
     private final Context mContext;
     private final IAccountManager mService;
     private final Handler mMainHandler;
@@ -409,6 +411,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public AccountManager(Context context, IAccountManager service) {
         mContext = context;
         mService = service;
@@ -418,6 +421,7 @@
     /**
      * @hide used for testing only
      */
+    @UnsupportedAppUsage
     public AccountManager(Context context, IAccountManager service, Handler handler) {
         mContext = context;
         mService = service;
@@ -685,6 +689,7 @@
 
     /** @hide Same as {@link #getAccountsByType(String)} but for a specific user. */
     @NonNull
+    @UnsupportedAppUsage
     public Account[] getAccountsByTypeAsUser(String type, UserHandle userHandle) {
         try {
             return mService.getAccountsAsUser(type, userHandle.getIdentifier(),
@@ -2014,6 +2019,7 @@
      * Same as {@link #confirmCredentials(Account, Bundle, Activity, AccountManagerCallback, Handler)}
      * but for the specified user.
      */
+    @UnsupportedAppUsage
     public AccountManagerFuture<Bundle> confirmCredentialsAsUser(final Account account,
             final Bundle options,
             final Activity activity,
@@ -2225,9 +2231,12 @@
     }
 
     private abstract class AmsTask extends FutureTask<Bundle> implements AccountManagerFuture<Bundle> {
+        @UnsupportedAppUsage
         final IAccountManagerResponse mResponse;
+        @UnsupportedAppUsage
         final Handler mHandler;
         final AccountManagerCallback<Bundle> mCallback;
+        @UnsupportedAppUsage
         final Activity mActivity;
         public AmsTask(Activity activity, Handler handler, AccountManagerCallback<Bundle> callback) {
             super(new Callable<Bundle>() {
@@ -2552,10 +2561,13 @@
         }
         volatile AccountManagerFuture<Bundle> mFuture = null;
         final String mAccountType;
+        @UnsupportedAppUsage
         final String mAuthTokenType;
         final String[] mFeatures;
         final Bundle mAddAccountOptions;
+        @UnsupportedAppUsage
         final Bundle mLoginOptions;
+        @UnsupportedAppUsage
         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 5d9abb0..6875867 100644
--- a/core/java/android/accounts/AuthenticatorDescription.java
+++ b/core/java/android/accounts/AuthenticatorDescription.java
@@ -16,6 +16,7 @@
 
 package android.accounts;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcelable;
 import android.os.Parcel;
 
@@ -76,6 +77,7 @@
         return new AuthenticatorDescription(type);
     }
 
+    @UnsupportedAppUsage
     private AuthenticatorDescription(String type) {
         this.type = type;
         this.packageName = null;
@@ -86,6 +88,7 @@
         this.customTokens = false;
     }
 
+    @UnsupportedAppUsage
     private AuthenticatorDescription(Parcel source) {
         this.type = source.readString();
         this.packageName = source.readString();
diff --git a/core/java/android/accounts/ChooseAccountActivity.java b/core/java/android/accounts/ChooseAccountActivity.java
index 6a43686..4af22bf 100644
--- a/core/java/android/accounts/ChooseAccountActivity.java
+++ b/core/java/android/accounts/ChooseAccountActivity.java
@@ -17,6 +17,7 @@
 
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
@@ -72,8 +73,8 @@
 
         try {
             IBinder activityToken = getActivityToken();
-            mCallingUid = ActivityManager.getService().getLaunchedFromUid(activityToken);
-            mCallingPackage = ActivityManager.getService().getLaunchedFromPackage(
+            mCallingUid = ActivityTaskManager.getService().getLaunchedFromUid(activityToken);
+            mCallingPackage = ActivityTaskManager.getService().getLaunchedFromPackage(
                     activityToken);
         } catch (RemoteException re) {
             // Couldn't figure out caller details
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 6680ce6..57c1083 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -15,6 +15,7 @@
  */
 package android.accounts;
 
+import android.app.ActivityTaskManager;
 import com.google.android.collect.Sets;
 
 import android.app.Activity;
@@ -147,8 +148,8 @@
 
         try {
             IBinder activityToken = getActivityToken();
-            mCallingUid = ActivityManager.getService().getLaunchedFromUid(activityToken);
-            mCallingPackage = ActivityManager.getService().getLaunchedFromPackage(
+            mCallingUid = ActivityTaskManager.getService().getLaunchedFromUid(activityToken);
+            mCallingPackage = ActivityTaskManager.getService().getLaunchedFromPackage(
                     activityToken);
             if (mCallingUid != 0 && mCallingPackage != null) {
                 Bundle restrictions = UserManager.get(this)
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 4ebcc44..17d54d2 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -17,6 +17,7 @@
 package android.animation;
 
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ConstantState;
 
@@ -460,6 +461,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void reverse() {
         throw new IllegalStateException("Reverse is not supported");
     }
diff --git a/core/java/android/animation/ArgbEvaluator.java b/core/java/android/animation/ArgbEvaluator.java
index a96bee6..5b69d18 100644
--- a/core/java/android/animation/ArgbEvaluator.java
+++ b/core/java/android/animation/ArgbEvaluator.java
@@ -16,6 +16,8 @@
 
 package android.animation;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * This evaluator can be used to perform type interpolation between integer
  * values that represent ARGB colors.
@@ -31,6 +33,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static ArgbEvaluator getInstance() {
         return sInstance;
     }
diff --git a/core/java/android/animation/IntEvaluator.java b/core/java/android/animation/IntEvaluator.java
index 34fb0dc..1de2ae7 100644
--- a/core/java/android/animation/IntEvaluator.java
+++ b/core/java/android/animation/IntEvaluator.java
@@ -24,7 +24,7 @@
     /**
      * This function returns the result of linearly interpolating the start and end values, with
      * <code>fraction</code> representing the proportion between the start and end values. The
-     * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
+     * calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>,
      * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
      * and <code>t</code> is <code>fraction</code>.
      *
@@ -39,4 +39,4 @@
         int startInt = startValue;
         return (int)(startInt + fraction * (endValue - startInt));
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 5a23fdd..5b3813d 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -16,6 +16,7 @@
 
 package android.animation;
 
+import android.annotation.UnsupportedAppUsage;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
@@ -1070,6 +1071,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void cancel() {
         if (currentChangingAnimations.size() > 0) {
             LinkedHashMap<View, Animator> currentAnimCopy =
@@ -1105,6 +1107,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void cancel(int transitionType) {
         switch (transitionType) {
             case CHANGE_APPEARING:
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index cc95eb6..a0464df 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -19,6 +19,7 @@
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Looper;
 import android.os.Trace;
 import android.util.AndroidRuntimeException;
@@ -75,6 +76,7 @@
     /**
      * Internal constants
      */
+    @UnsupportedAppUsage
     private static float sDurationScale = 1.0f;
 
     /**
@@ -200,6 +202,7 @@
     //
 
     // How long the animation should last in ms
+    @UnsupportedAppUsage
     private long mDuration = 300;
 
     // The amount of time in ms to delay starting the animation after start() is called. Note
@@ -1534,6 +1537,7 @@
      * @param fraction The elapsed fraction of the animation.
      */
     @CallSuper
+    @UnsupportedAppUsage
     void animateValue(float fraction) {
         fraction = mInterpolator.getInterpolation(fraction);
         mCurrentFraction = fraction;
diff --git a/core/java/android/annotation/UnsupportedAppUsage.java b/core/java/android/annotation/UnsupportedAppUsage.java
new file mode 100644
index 0000000..05de3e8
--- /dev/null
+++ b/core/java/android/annotation/UnsupportedAppUsage.java
@@ -0,0 +1,60 @@
+/*
+ * 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.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a class member, that is not part of the SDK, is used by apps.
+ * Since the member is not part of the SDK, such use is not supported.
+ *
+ * This annotation acts as a heads up that changing a given method or field
+ * may affect apps, potentially breaking them when the next Android version is
+ * released. In some cases, for members that are heavily used, this annotation
+ * may imply restrictions on changes to the member.
+ *
+ * This annotation also results in access to the member being permitted by the
+ * runtime, with a warning being generated in debug builds.
+ *
+ * For more details, see go/UnsupportedAppUsage.
+ *
+ * {@hide}
+ */
+@Retention(CLASS)
+@Target({CONSTRUCTOR, METHOD, FIELD})
+public @interface UnsupportedAppUsage {
+
+    /**
+     * Associates a bug tracking the work to add a public alternative to this API. Optional.
+     *
+     * @return ID of the associated tracking bug
+     */
+    long trackingBug() default 0;
+
+    /**
+     * For debug use only. The expected dex signature to be generated for this API, used to verify
+     * parts of the build process.
+     *
+     * @return A dex API signature.
+     */
+    String expectedSignature() default "";
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a6d2be0..6638dd9 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1086,7 +1086,7 @@
      *
      * @param savedInstanceState contains the saved state
      */
-    final void performRestoreInstanceState(Bundle savedInstanceState) {
+    final void performRestoreInstanceState(@NonNull Bundle savedInstanceState) {
         onRestoreInstanceState(savedInstanceState);
         restoreManagedDialogs(savedInstanceState);
     }
@@ -1100,8 +1100,8 @@
      * @param savedInstanceState contains the saved state
      * @param persistentState contains the persistable saved state
      */
-    final void performRestoreInstanceState(Bundle savedInstanceState,
-            PersistableBundle persistentState) {
+    final void performRestoreInstanceState(@Nullable Bundle savedInstanceState,
+            @Nullable PersistableBundle persistentState) {
         onRestoreInstanceState(savedInstanceState, persistentState);
         if (savedInstanceState != null) {
             restoreManagedDialogs(savedInstanceState);
@@ -1128,7 +1128,7 @@
      * @see #onResume
      * @see #onSaveInstanceState
      */
-    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
         if (mWindow != null) {
             Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
             if (windowState != null) {
@@ -1149,8 +1149,12 @@
      *
      * <p>If this method is called {@link #onRestoreInstanceState(Bundle)} will not be called.
      *
-     * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}.
-     * @param persistentState the data most recently supplied in {@link #onSaveInstanceState}.
+     * <p>At least one of {@code savedInstanceState} or {@code persistentState} will not be null.
+     *
+     * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}
+     *     or null.
+     * @param persistentState the data most recently supplied in {@link #onSaveInstanceState}
+     *     or null.
      *
      * @see #onRestoreInstanceState(Bundle)
      * @see #onCreate
@@ -1158,8 +1162,8 @@
      * @see #onResume
      * @see #onSaveInstanceState
      */
-    public void onRestoreInstanceState(Bundle savedInstanceState,
-            PersistableBundle persistentState) {
+    public void onRestoreInstanceState(@Nullable Bundle savedInstanceState,
+            @Nullable PersistableBundle persistentState) {
         if (savedInstanceState != null) {
             onRestoreInstanceState(savedInstanceState);
         }
@@ -1442,7 +1446,7 @@
     public boolean isVoiceInteractionRoot() {
         try {
             return mVoiceInteractor != null
-                    && ActivityManager.getService().isRootVoiceInteraction(mToken);
+                    && ActivityTaskManager.getService().isRootVoiceInteraction(mToken);
         } catch (RemoteException e) {
         }
         return false;
@@ -1465,7 +1469,7 @@
      */
     public boolean isLocalVoiceInteractionSupported() {
         try {
-            return ActivityManager.getService().supportsLocalVoiceInteraction();
+            return ActivityTaskManager.getService().supportsLocalVoiceInteraction();
         } catch (RemoteException re) {
         }
         return false;
@@ -1479,7 +1483,7 @@
      */
     public void startLocalVoiceInteraction(Bundle privateOptions) {
         try {
-            ActivityManager.getService().startLocalVoiceInteraction(mToken, privateOptions);
+            ActivityTaskManager.getService().startLocalVoiceInteraction(mToken, privateOptions);
         } catch (RemoteException re) {
         }
     }
@@ -1508,7 +1512,7 @@
      */
     public void stopLocalVoiceInteraction() {
         try {
-            ActivityManager.getService().stopLocalVoiceInteraction(mToken);
+            ActivityTaskManager.getService().stopLocalVoiceInteraction(mToken);
         } catch (RemoteException re) {
         }
     }
@@ -1545,7 +1549,7 @@
      *
      * @param outState The bundle to save the state to.
      */
-    final void performSaveInstanceState(Bundle outState) {
+    final void performSaveInstanceState(@NonNull Bundle outState) {
         onSaveInstanceState(outState);
         saveManagedDialogs(outState);
         mActivityTransitionState.saveState(outState);
@@ -1562,7 +1566,8 @@
      * @param outState The bundle to save the state to.
      * @param outPersistentState The bundle to save persistent state to.
      */
-    final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
+    final void performSaveInstanceState(@NonNull Bundle outState,
+            @NonNull PersistableBundle outPersistentState) {
         onSaveInstanceState(outState, outPersistentState);
         saveManagedDialogs(outState);
         storeHasCurrentPermissionRequest(outState);
@@ -1618,7 +1623,7 @@
      * @see #onRestoreInstanceState
      * @see #onPause
      */
-    protected void onSaveInstanceState(Bundle outState) {
+    protected void onSaveInstanceState(@NonNull Bundle outState) {
         outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
 
         outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
@@ -1648,7 +1653,8 @@
      * @see #onRestoreInstanceState(Bundle, PersistableBundle)
      * @see #onPause
      */
-    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
+    public void onSaveInstanceState(@NonNull Bundle outState,
+            @NonNull PersistableBundle outPersistentState) {
         onSaveInstanceState(outState);
     }
 
@@ -1886,7 +1892,7 @@
      */
     public boolean showAssist(Bundle args) {
         try {
-            return ActivityManager.getService().showAssistFromActivity(mToken, args);
+            return ActivityTaskManager.getService().showAssistFromActivity(mToken, args);
         } catch (RemoteException e) {
         }
         return false;
@@ -2019,7 +2025,8 @@
         if (mDoReportFullyDrawn) {
             mDoReportFullyDrawn = false;
             try {
-                ActivityManager.getService().reportActivityFullyDrawn(mToken, mRestoredFromBundle);
+                ActivityTaskManager.getService().reportActivityFullyDrawn(
+                        mToken, mRestoredFromBundle);
             } catch (RemoteException e) {
             }
         }
@@ -2066,7 +2073,7 @@
      */
     public boolean isInMultiWindowMode() {
         try {
-            return ActivityManager.getService().isInMultiWindowMode(mToken);
+            return ActivityTaskManager.getService().isInMultiWindowMode(mToken);
         } catch (RemoteException e) {
         }
         return false;
@@ -2113,7 +2120,7 @@
      */
     public boolean isInPictureInPictureMode() {
         try {
-            return ActivityManager.getService().isInPictureInPictureMode(mToken);
+            return ActivityTaskManager.getService().isInPictureInPictureMode(mToken);
         } catch (RemoteException e) {
         }
         return false;
@@ -2168,7 +2175,7 @@
                 throw new IllegalStateException("Activity must be resumed to enter"
                         + " picture-in-picture");
             }
-            return ActivityManagerNative.getDefault().enterPictureInPictureMode(mToken, params);
+            return ActivityTaskManager.getService().enterPictureInPictureMode(mToken, params);
         } catch (RemoteException e) {
             return false;
         }
@@ -2194,7 +2201,7 @@
             if (params == null) {
                 throw new IllegalArgumentException("Expected non-null picture-in-picture params");
             }
-            ActivityManagerNative.getDefault().setPictureInPictureParams(mToken, params);
+            ActivityTaskManager.getService().setPictureInPictureParams(mToken, params);
         } catch (RemoteException e) {
         }
     }
@@ -2207,7 +2214,7 @@
      */
     public int getMaxNumPictureInPictureActions() {
         try {
-            return ActivityManagerNative.getDefault().getMaxNumPictureInPictureActions(mToken);
+            return ActivityTaskManager.getService().getMaxNumPictureInPictureActions(mToken);
         } catch (RemoteException e) {
             return 0;
         }
@@ -3321,7 +3328,7 @@
      */
     @Override
     public void exitFreeformMode() throws RemoteException {
-        ActivityManager.getService().exitFreeformMode(mToken);
+        ActivityTaskManager.getService().exitFreeformMode(mToken);
     }
 
     /**
@@ -4405,9 +4412,9 @@
     /**
      * Requests permissions to be granted to this application. These permissions
      * must be requested in your manifest, they should not be granted to your app,
-     * and they should have protection level {@link android.content.pm.PermissionInfo
-     * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
-     * the platform or a third-party app.
+     * and they should have protection level {@link
+     * android.content.pm.PermissionInfo#PROTECTION_DANGEROUS dangerous}, regardless
+     * whether they are declared by the platform or a third-party app.
      * <p>
      * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
      * are granted at install time if requested in the manifest. Signature permissions
@@ -4451,7 +4458,7 @@
      * result callbacks including {@link #onRequestPermissionsResult(int, String[], int[])}.
      * </p>
      * <p>
-     * The <a href="http://developer.android.com/samples/RuntimePermissions/index.html">
+     * The <a href="https://github.com/googlesamples/android-RuntimePermissions">
      * RuntimePermissions</a> sample app demonstrates how to use this method to
      * request permissions at run time.
      * </p>
@@ -4833,7 +4840,7 @@
                 fillInIntent.prepareToLeaveProcess(this);
                 resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
             }
-            int result = ActivityManager.getService()
+            int result = ActivityTaskManager.getService()
                 .startActivityIntentSender(mMainThread.getApplicationThread(),
                         intent != null ? intent.getTarget() : null,
                         intent != null ? intent.getWhitelistToken() : null,
@@ -5065,7 +5072,7 @@
                 }
                 intent.migrateExtraStreamToClipData();
                 intent.prepareToLeaveProcess(this);
-                result = ActivityManager.getService()
+                result = ActivityTaskManager.getService()
                     .startActivity(mMainThread.getApplicationThread(), getBasePackageName(),
                             intent, intent.resolveTypeIfNeeded(getContentResolver()), mToken,
                             mEmbeddedID, requestCode, ActivityManager.START_FLAG_ONLY_IF_NEEDED,
@@ -5136,7 +5143,7 @@
             try {
                 intent.migrateExtraStreamToClipData();
                 intent.prepareToLeaveProcess(this);
-                return ActivityManager.getService()
+                return ActivityTaskManager.getService()
                     .startNextMatchingActivity(mToken, intent, options);
             } catch (RemoteException e) {
                 // Empty
@@ -5351,7 +5358,7 @@
      */
     public void overridePendingTransition(int enterAnim, int exitAnim) {
         try {
-            ActivityManager.getService().overridePendingTransition(
+            ActivityTaskManager.getService().overridePendingTransition(
                     mToken, getPackageName(), enterAnim, exitAnim);
         } catch (RemoteException e) {
         }
@@ -5476,7 +5483,7 @@
     @Nullable
     public String getCallingPackage() {
         try {
-            return ActivityManager.getService().getCallingPackage(mToken);
+            return ActivityTaskManager.getService().getCallingPackage(mToken);
         } catch (RemoteException e) {
             return null;
         }
@@ -5499,7 +5506,7 @@
     @Nullable
     public ComponentName getCallingActivity() {
         try {
-            return ActivityManager.getService().getCallingActivity(mToken);
+            return ActivityTaskManager.getService().getCallingActivity(mToken);
         } catch (RemoteException e) {
             return null;
         }
@@ -5603,7 +5610,7 @@
                 if (resultData != null) {
                     resultData.prepareToLeaveProcess(this);
                 }
-                if (ActivityManager.getService()
+                if (ActivityTaskManager.getService()
                         .finishActivity(mToken, resultCode, resultData, finishTask)) {
                     mFinished = true;
                 }
@@ -5653,7 +5660,7 @@
             throw new IllegalStateException("Can not be called to deliver a result");
         }
         try {
-            if (ActivityManager.getService().finishActivityAffinity(mToken)) {
+            if (ActivityTaskManager.getService().finishActivityAffinity(mToken)) {
                 mFinished = true;
             }
         } catch (RemoteException e) {
@@ -5699,7 +5706,7 @@
     public void finishActivity(int requestCode) {
         if (mParent == null) {
             try {
-                ActivityManager.getService()
+                ActivityTaskManager.getService()
                     .finishSubActivity(mToken, mEmbeddedID, requestCode);
             } catch (RemoteException e) {
                 // Empty
@@ -5719,7 +5726,7 @@
      */
     public void finishActivityFromChild(@NonNull Activity child, int requestCode) {
         try {
-            ActivityManager.getService()
+            ActivityTaskManager.getService()
                 .finishSubActivity(mToken, child.mEmbeddedID, requestCode);
         } catch (RemoteException e) {
             // Empty
@@ -5747,7 +5754,7 @@
      */
     public boolean releaseInstance() {
         try {
-            return ActivityManager.getService().releaseActivityInstance(mToken);
+            return ActivityTaskManager.getService().releaseActivityInstance(mToken);
         } catch (RemoteException e) {
             // Empty
         }
@@ -5862,7 +5869,7 @@
     public void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) {
         if (mParent == null) {
             try {
-                ActivityManager.getService().setRequestedOrientation(
+                ActivityTaskManager.getService().setRequestedOrientation(
                         mToken, requestedOrientation);
             } catch (RemoteException e) {
                 // Empty
@@ -5885,7 +5892,7 @@
     public int getRequestedOrientation() {
         if (mParent == null) {
             try {
-                return ActivityManager.getService()
+                return ActivityTaskManager.getService()
                         .getRequestedOrientation(mToken);
             } catch (RemoteException e) {
                 // Empty
@@ -5904,8 +5911,7 @@
      */
     public int getTaskId() {
         try {
-            return ActivityManager.getService()
-                .getTaskForActivity(mToken, false);
+            return ActivityTaskManager.getService().getTaskForActivity(mToken, false);
         } catch (RemoteException e) {
             return -1;
         }
@@ -5920,7 +5926,7 @@
     @Override
     public boolean isTaskRoot() {
         try {
-            return ActivityManager.getService().getTaskForActivity(mToken, true) >= 0;
+            return ActivityTaskManager.getService().getTaskForActivity(mToken, true) >= 0;
         } catch (RemoteException e) {
             return false;
         }
@@ -5939,8 +5945,7 @@
      */
     public boolean moveTaskToBack(boolean nonRoot) {
         try {
-            return ActivityManager.getService().moveActivityTaskToBack(
-                    mToken, nonRoot);
+            return ActivityTaskManager.getService().moveActivityTaskToBack(mToken, nonRoot);
         } catch (RemoteException e) {
             // Empty
         }
@@ -6115,7 +6120,7 @@
             }
         }
         try {
-            ActivityManager.getService().setTaskDescription(mToken, mTaskDescription);
+            ActivityTaskManager.getService().setTaskDescription(mToken, mTaskDescription);
         } catch (RemoteException e) {
         }
     }
@@ -6390,7 +6395,7 @@
      */
     public boolean isImmersive() {
         try {
-            return ActivityManager.getService().isImmersive(mToken);
+            return ActivityTaskManager.getService().isImmersive(mToken);
         } catch (RemoteException e) {
             return false;
         }
@@ -6408,7 +6413,7 @@
             return false;
         }
         try {
-            return ActivityManager.getService().isTopOfTask(getActivityToken());
+            return ActivityTaskManager.getService().isTopOfTask(getActivityToken());
         } catch (RemoteException e) {
             return false;
         }
@@ -6434,7 +6439,7 @@
     public void convertFromTranslucent() {
         try {
             mTranslucentCallback = null;
-            if (ActivityManager.getService().convertFromTranslucent(mToken)) {
+            if (ActivityTaskManager.getService().convertFromTranslucent(mToken)) {
                 WindowManagerGlobal.getInstance().changeCanvasOpacity(mToken, true);
             }
         } catch (RemoteException e) {
@@ -6473,7 +6478,7 @@
         boolean drawComplete;
         try {
             mTranslucentCallback = callback;
-            mChangeCanvasToTranslucent = ActivityManager.getService().convertToTranslucent(
+            mChangeCanvasToTranslucent = ActivityTaskManager.getService().convertToTranslucent(
                     mToken, options == null ? null : options.toBundle());
             WindowManagerGlobal.getInstance().changeCanvasOpacity(mToken, false);
             drawComplete = true;
@@ -6519,7 +6524,7 @@
     ActivityOptions getActivityOptions() {
         try {
             return ActivityOptions.fromBundle(
-                    ActivityManager.getService().getActivityOptions(mToken));
+                    ActivityTaskManager.getService().getActivityOptions(mToken));
         } catch (RemoteException e) {
         }
         return null;
@@ -6664,7 +6669,7 @@
      */
     public void setImmersive(boolean i) {
         try {
-            ActivityManager.getService().setImmersive(mToken, i);
+            ActivityTaskManager.getService().setImmersive(mToken, i);
         } catch (RemoteException e) {
             // pass
         }
@@ -6727,7 +6732,7 @@
     public void setVrModeEnabled(boolean enabled, @NonNull ComponentName requestedComponent)
           throws PackageManager.NameNotFoundException {
         try {
-            if (ActivityManager.getService().setVrMode(mToken, enabled, requestedComponent)
+            if (ActivityTaskManager.getService().setVrMode(mToken, enabled, requestedComponent)
                     != 0) {
                 throw new PackageManager.NameNotFoundException(
                         requestedComponent.flattenToString());
@@ -6848,8 +6853,7 @@
             if (info.taskAffinity == null) {
                 return false;
             }
-            return ActivityManager.getService()
-                    .shouldUpRecreateTask(mToken, info.taskAffinity);
+            return ActivityTaskManager.getService().shouldUpRecreateTask(mToken, info.taskAffinity);
         } catch (RemoteException e) {
             return false;
         } catch (NameNotFoundException e) {
@@ -6901,7 +6905,7 @@
             }
             try {
                 upIntent.prepareToLeaveProcess(this);
-                return ActivityManager.getService().navigateUpTo(mToken, upIntent,
+                return ActivityTaskManager.getService().navigateUpTo(mToken, upIntent,
                         resultCode, resultData);
             } catch (RemoteException e) {
                 return false;
@@ -7508,7 +7512,7 @@
      */
     public void startLockTask() {
         try {
-            ActivityManager.getService().startLockTaskModeByToken(mToken);
+            ActivityTaskManager.getService().startLockTaskModeByToken(mToken);
         } catch (RemoteException e) {
         }
     }
@@ -7531,7 +7535,7 @@
      */
     public void stopLockTask() {
         try {
-            ActivityManager.getService().stopLockTaskModeByToken(mToken);
+            ActivityTaskManager.getService().stopLockTaskModeByToken(mToken);
         } catch (RemoteException e) {
         }
     }
@@ -7543,7 +7547,7 @@
      */
     public void showLockTaskEscapeMessage() {
         try {
-            ActivityManager.getService().showLockTaskEscapeMessage(mToken);
+            ActivityTaskManager.getService().showLockTaskEscapeMessage(mToken);
         } catch (RemoteException e) {
         }
     }
@@ -7813,7 +7817,7 @@
      */
     public void setDisablePreviewScreenshots(boolean disable) {
         try {
-            ActivityManager.getService().setDisablePreviewScreenshots(mToken, disable);
+            ActivityTaskManager.getService().setDisablePreviewScreenshots(mToken, disable);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to call setDisablePreviewScreenshots", e);
         }
@@ -7834,7 +7838,7 @@
      */
     public void setShowWhenLocked(boolean showWhenLocked) {
         try {
-            ActivityManager.getService().setShowWhenLocked(mToken, showWhenLocked);
+            ActivityTaskManager.getService().setShowWhenLocked(mToken, showWhenLocked);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to call setShowWhenLocked", e);
         }
@@ -7860,7 +7864,7 @@
      */
     public void setTurnScreenOn(boolean turnScreenOn) {
         try {
-            ActivityManager.getService().setTurnScreenOn(mToken, turnScreenOn);
+            ActivityTaskManager.getService().setTurnScreenOn(mToken, turnScreenOn);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to call setTurnScreenOn", e);
         }
@@ -7876,7 +7880,7 @@
     @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
     public void registerRemoteAnimations(RemoteAnimationDefinition definition) {
         try {
-            ActivityManager.getService().registerRemoteAnimations(mToken, definition);
+            ActivityTaskManager.getService().registerRemoteAnimations(mToken, definition);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to call registerRemoteAnimations", e);
         }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e7d3525..1105ed6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+
 import android.Manifest;
 import android.annotation.DrawableRes;
 import android.annotation.IntDef;
@@ -28,7 +30,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
@@ -60,7 +61,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.WorkSource;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.util.Singleton;
@@ -123,8 +123,6 @@
 public class ActivityManager {
     private static String TAG = "ActivityManager";
 
-    private static int gMaxRecentTasks = -1;
-
     private final Context mContext;
 
     private static volatile boolean sSystemReady = false;
@@ -746,86 +744,9 @@
             SystemProperties.getBoolean("debug.force_low_ram", false);
 
     /** @hide */
-    @TestApi
-    public static class StackId {
-
-        private StackId() {
-        }
-
-        /** Invalid stack ID. */
-        public static final int INVALID_STACK_ID = -1;
-
-    }
-
-    /**
-     * Parameter to {@link android.app.IActivityManager#setTaskWindowingModeSplitScreenPrimary}
-     * which specifies the position of the created docked stack at the top half of the screen if
-     * in portrait mode or at the left half of the screen if in landscape mode.
-     * @hide
-     */
-    @TestApi
-    public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0;
-
-    /**
-     * Parameter to {@link android.app.IActivityManager#setTaskWindowingModeSplitScreenPrimary}
-     * which
-     * specifies the position of the created docked stack at the bottom half of the screen if
-     * in portrait mode or at the right half of the screen if in landscape mode.
-     * @hide
-     */
-    @TestApi
-    public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1;
-
-    /**
-     * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
-     * that the resize doesn't need to preserve the window, and can be skipped if bounds
-     * is unchanged. This mode is used by window manager in most cases.
-     * @hide
-     */
-    public static final int RESIZE_MODE_SYSTEM = 0;
-
-    /**
-     * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
-     * that the resize should preserve the window if possible.
-     * @hide
-     */
-    public static final int RESIZE_MODE_PRESERVE_WINDOW   = (0x1 << 0);
-
-    /**
-     * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
-     * that the resize should be performed even if the bounds appears unchanged.
-     * @hide
-     */
-    public static final int RESIZE_MODE_FORCED = (0x1 << 1);
-
-    /**
-     * Input parameter to {@link android.app.IActivityManager#resizeTask} used by window
-     * manager during a screen rotation.
-     * @hide
-     */
-    public static final int RESIZE_MODE_SYSTEM_SCREEN_ROTATION = RESIZE_MODE_PRESERVE_WINDOW;
-
-    /**
-     * Input parameter to {@link android.app.IActivityManager#resizeTask} used when the
-     * resize is due to a drag action.
-     * @hide
-     */
-    public static final int RESIZE_MODE_USER = RESIZE_MODE_PRESERVE_WINDOW;
-
-    /**
-     * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
-     * that the resize should preserve the window if possible, and should not be skipped
-     * even if the bounds is unchanged. Usually used to force a resizing when a drag action
-     * is ending.
-     * @hide
-     */
-    public static final int RESIZE_MODE_USER_FORCED =
-            RESIZE_MODE_PRESERVE_WINDOW | RESIZE_MODE_FORCED;
-
-    /** @hide */
     public int getFrontActivityScreenCompatMode() {
         try {
-            return getService().getFrontActivityScreenCompatMode();
+            return getTaskService().getFrontActivityScreenCompatMode();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -834,7 +755,7 @@
     /** @hide */
     public void setFrontActivityScreenCompatMode(int mode) {
         try {
-            getService().setFrontActivityScreenCompatMode(mode);
+            getTaskService().setFrontActivityScreenCompatMode(mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -977,57 +898,13 @@
     }
 
     /**
-     * Return the maximum number of recents entries that we will maintain and show.
+     * TODO(b/80414790): Remove once no longer on hiddenapi-light-greylist.txt
      * @hide
+     * @deprecated Use {@link ActivityTaskManager#getMaxRecentTasksStatic()}
      */
+    @Deprecated
     static public int getMaxRecentTasksStatic() {
-        if (gMaxRecentTasks < 0) {
-            return gMaxRecentTasks = isLowRamDeviceStatic() ? 36 : 48;
-        }
-        return gMaxRecentTasks;
-    }
-
-    /**
-     * Return the default limit on the number of recents that an app can make.
-     * @hide
-     */
-    static public int getDefaultAppRecentsLimitStatic() {
-        return getMaxRecentTasksStatic() / 6;
-    }
-
-    /**
-     * Return the maximum limit on the number of recents that an app can make.
-     * @hide
-     */
-    static public int getMaxAppRecentsLimitStatic() {
-        return getMaxRecentTasksStatic() / 2;
-    }
-
-    /**
-     * Returns true if the system supports at least one form of multi-window.
-     * E.g. freeform, split-screen, picture-in-picture.
-     * @hide
-     */
-    @TestApi
-    static public boolean supportsMultiWindow(Context context) {
-        // On watches, multi-window is used to present essential system UI, and thus it must be
-        // supported regardless of device memory characteristics.
-        boolean isWatch = context.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_WATCH);
-        return (!isLowRamDeviceStatic() || isWatch)
-                && Resources.getSystem().getBoolean(
-                    com.android.internal.R.bool.config_supportsMultiWindow);
-    }
-
-    /**
-     * Returns true if the system supports split screen multi-window.
-     * @hide
-     */
-    @TestApi
-    static public boolean supportsSplitScreenMultiWindow(Context context) {
-        return supportsMultiWindow(context)
-                && Resources.getSystem().getBoolean(
-                    com.android.internal.R.bool.config_supportsSplitScreenMultiWindow);
+        return ActivityTaskManager.getMaxRecentTasksStatic();
     }
 
     /** @removed */
@@ -1305,7 +1182,7 @@
         public static Bitmap loadTaskDescriptionIcon(String iconFilename, int userId) {
             if (iconFilename != null) {
                 try {
-                    return getService().getTaskDescriptionIcon(iconFilename,
+                    return getTaskService().getTaskDescriptionIcon(iconFilename,
                             userId);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
@@ -1446,197 +1323,66 @@
      * Information you can retrieve about tasks that the user has most recently
      * started or visited.
      */
-    public static class RecentTaskInfo implements Parcelable {
+    public static class RecentTaskInfo extends TaskInfo implements Parcelable {
         /**
          * If this task is currently running, this is the identifier for it.
          * If it is not running, this will be -1.
+         *
+         * @deprecated As of {@link android.os.Build.VERSION_CODES#Q}, use
+         * {@link RecentTaskInfo#taskId} to get the task id and {@link RecentTaskInfo#isRunning}
+         * to determine if it is running.
          */
+        @Deprecated
         public int id;
 
         /**
          * The true identifier of this task, valid even if it is not running.
+         *
+         * @deprecated As of {@link android.os.Build.VERSION_CODES#Q}, use
+         * {@link RecentTaskInfo#taskId}.
          */
+        @Deprecated
         public int persistentId;
 
         /**
-         * The original Intent used to launch the task.  You can use this
-         * Intent to re-launch the task (if it is no longer running) or bring
-         * the current task to the front.
-         */
-        public Intent baseIntent;
-
-        /**
-         * If this task was started from an alias, this is the actual
-         * activity component that was initially started; the component of
-         * the baseIntent in this case is the name of the actual activity
-         * implementation that the alias referred to.  Otherwise, this is null.
-         */
-        public ComponentName origActivity;
-
-        /**
-         * The actual activity component that started the task.
-         * @hide
-         */
-        @Nullable
-        public ComponentName realActivity;
-
-        /**
          * Description of the task's last state.
+         *
+         * @deprecated As of {@link android.os.Build.VERSION_CODES#Q}, currently always null.
          */
+        @Deprecated
         public CharSequence description;
 
         /**
-         * The id of the ActivityStack this Task was on most recently.
-         * @hide
-         */
-        public int stackId;
-
-        /**
-         * The id of the user the task was running as.
-         * @hide
-         */
-        public int userId;
-
-        /**
-         * The first time this task was active.
-         * @hide
-         */
-        public long firstActiveTime;
-
-        /**
-         * The last time this task was active.
-         * @hide
-         */
-        public long lastActiveTime;
-
-        /**
-         * The recent activity values for the highest activity in the stack to have set the values.
-         * {@link Activity#setTaskDescription(android.app.ActivityManager.TaskDescription)}.
-         */
-        public TaskDescription taskDescription;
-
-        /**
          * Task affiliation for grouping with other tasks.
+         *
+         * @deprecated As of {@link android.os.Build.VERSION_CODES#Q}, currently always 0.
          */
+        @Deprecated
         public int affiliatedTaskId;
 
-        /**
-         * Task affiliation color of the source task with the affiliated task id.
-         *
-         * @hide
-         */
-        public int affiliatedTaskColor;
-
-        /**
-         * The component launched as the first activity in the task.
-         * This can be considered the "application" of this task.
-         */
-        public ComponentName baseActivity;
-
-        /**
-         * The activity component at the top of the history stack of the task.
-         * This is what the user is currently doing.
-         */
-        public ComponentName topActivity;
-
-        /**
-         * Number of activities in this task.
-         */
-        public int numActivities;
-
-        /**
-         * The bounds of the task.
-         * @hide
-         */
-        public Rect bounds;
-
-        /**
-         * True if the task can go in the docked stack.
-         * @hide
-         */
-        public boolean supportsSplitScreenMultiWindow;
-
-        /**
-         * The resize mode of the task. See {@link ActivityInfo#resizeMode}.
-         * @hide
-         */
-        public int resizeMode;
-
-        /**
-         * The current configuration this task is in.
-         * @hide
-         */
-        final public Configuration configuration = new Configuration();
-
         public RecentTaskInfo() {
         }
 
+        private RecentTaskInfo(Parcel source) {
+            readFromParcel(source);
+        }
+
         @Override
         public int describeContents() {
             return 0;
         }
 
+        public void readFromParcel(Parcel source) {
+            id = source.readInt();
+            persistentId = source.readInt();
+            super.readFromParcel(source);
+        }
+
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(id);
             dest.writeInt(persistentId);
-            if (baseIntent != null) {
-                dest.writeInt(1);
-                baseIntent.writeToParcel(dest, 0);
-            } else {
-                dest.writeInt(0);
-            }
-            ComponentName.writeToParcel(origActivity, dest);
-            ComponentName.writeToParcel(realActivity, dest);
-            TextUtils.writeToParcel(description, dest,
-                    Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
-            if (taskDescription != null) {
-                dest.writeInt(1);
-                taskDescription.writeToParcel(dest, 0);
-            } else {
-                dest.writeInt(0);
-            }
-            dest.writeInt(stackId);
-            dest.writeInt(userId);
-            dest.writeLong(lastActiveTime);
-            dest.writeInt(affiliatedTaskId);
-            dest.writeInt(affiliatedTaskColor);
-            ComponentName.writeToParcel(baseActivity, dest);
-            ComponentName.writeToParcel(topActivity, dest);
-            dest.writeInt(numActivities);
-            if (bounds != null) {
-                dest.writeInt(1);
-                bounds.writeToParcel(dest, 0);
-            } else {
-                dest.writeInt(0);
-            }
-            dest.writeInt(supportsSplitScreenMultiWindow ? 1 : 0);
-            dest.writeInt(resizeMode);
-            configuration.writeToParcel(dest, flags);
-        }
-
-        public void readFromParcel(Parcel source) {
-            id = source.readInt();
-            persistentId = source.readInt();
-            baseIntent = source.readInt() > 0 ? Intent.CREATOR.createFromParcel(source) : null;
-            origActivity = ComponentName.readFromParcel(source);
-            realActivity = ComponentName.readFromParcel(source);
-            description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
-            taskDescription = source.readInt() > 0 ?
-                    TaskDescription.CREATOR.createFromParcel(source) : null;
-            stackId = source.readInt();
-            userId = source.readInt();
-            lastActiveTime = source.readLong();
-            affiliatedTaskId = source.readInt();
-            affiliatedTaskColor = source.readInt();
-            baseActivity = ComponentName.readFromParcel(source);
-            topActivity = ComponentName.readFromParcel(source);
-            numActivities = source.readInt();
-            bounds = source.readInt() > 0 ?
-                    Rect.CREATOR.createFromParcel(source) : null;
-            supportsSplitScreenMultiWindow = source.readInt() == 1;
-            resizeMode = source.readInt();
-            configuration.readFromParcel(source);
+            super.writeToParcel(dest, flags);
         }
 
         public static final Creator<RecentTaskInfo> CREATOR
@@ -1649,8 +1395,40 @@
             }
         };
 
-        private RecentTaskInfo(Parcel source) {
-            readFromParcel(source);
+        /**
+         * @hide
+         */
+        public void dump(PrintWriter pw, String indent) {
+            final String activityType = WindowConfiguration.activityTypeToString(
+                    configuration.windowConfiguration.getActivityType());
+            final String windowingMode = WindowConfiguration.activityTypeToString(
+                    configuration.windowConfiguration.getActivityType());
+
+            pw.println(); pw.print("   ");
+            pw.print(" id=" + persistentId);
+            pw.print(" stackId=" + stackId);
+            pw.print(" userId=" + userId);
+            pw.print(" hasTask=" + (id != -1));
+            pw.print(" lastActiveTime=" + lastActiveTime);
+            pw.println(); pw.print("   ");
+            pw.print(" baseIntent=" + baseIntent);
+            pw.println(); pw.print("   ");
+            pw.print(" isExcluded="
+                    + ((baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0));
+            pw.print(" activityType=" + activityType);
+            pw.print(" windowingMode=" + windowingMode);
+            pw.print(" supportsSplitScreenMultiWindow=" + supportsSplitScreenMultiWindow);
+            if (taskDescription != null) {
+                pw.println(); pw.print("   ");
+                final ActivityManager.TaskDescription td = taskDescription;
+                pw.print(" taskDescription {");
+                pw.print(" colorBackground=#" + Integer.toHexString(td.getBackgroundColor()));
+                pw.print(" colorPrimary=#" + Integer.toHexString(td.getPrimaryColor()));
+                pw.print(" iconRes=" + (td.getIconResource() != 0));
+                pw.print(" iconBitmap=" + (td.getIconFilename() != null
+                        || td.getInMemoryIcon() != null));
+                pw.println(" }");
+            }
         }
     }
 
@@ -1705,7 +1483,7 @@
             if (maxNum < 0) {
                 throw new IllegalArgumentException("The requested number of tasks should be >= 0");
             }
-            return getService().getRecentTasks(maxNum, flags, mContext.getUserId()).getList();
+            return getTaskService().getRecentTasks(maxNum, flags, mContext.getUserId()).getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1719,119 +1497,62 @@
      * the system may have killed its process and is only holding on to its
      * last state in order to restart it when the user returns.
      */
-    public static class RunningTaskInfo implements Parcelable {
+    public static class RunningTaskInfo extends TaskInfo implements Parcelable {
+
         /**
          * A unique identifier for this task.
+         *
+         * @deprecated As of {@link android.os.Build.VERSION_CODES#Q}, use
+         * {@link RunningTaskInfo#taskId}.
          */
+        @Deprecated
         public int id;
 
         /**
-         * The stack that currently contains this task.
-         * @hide
+         * Thumbnail representation of the task's current state.
+         *
+         * @deprecated As of {@link android.os.Build.VERSION_CODES#Q}, currently always null.
          */
-        public int stackId;
-
-        /**
-         * The component launched as the first activity in the task.  This can
-         * be considered the "application" of this task.
-         */
-        public ComponentName baseActivity;
-
-        /**
-         * The activity component at the top of the history stack of the task.
-         * This is what the user is currently doing.
-         */
-        public ComponentName topActivity;
-
-        /**
-         * Thumbnail representation of the task's current state.  Currently
-         * always null.
-         */
+        @Deprecated
         public Bitmap thumbnail;
 
         /**
          * Description of the task's current state.
+         *
+         * @deprecated As of {@link android.os.Build.VERSION_CODES#Q}, currently always null.
          */
+        @Deprecated
         public CharSequence description;
 
         /**
-         * Number of activities in this task.
+         * Number of activities that are currently running (not stopped and persisted) in this task.
+         *
+         * @deprecated As of {@link android.os.Build.VERSION_CODES#Q}, currently always 0.
          */
-        public int numActivities;
-
-        /**
-         * Number of activities that are currently running (not stopped
-         * and persisted) in this task.
-         */
+        @Deprecated
         public int numRunning;
 
-        /**
-         * Last time task was run. For sorting.
-         * @hide
-         */
-        public long lastActiveTime;
-
-        /**
-         * True if the task can go in the docked stack.
-         * @hide
-         */
-        public boolean supportsSplitScreenMultiWindow;
-
-        /**
-         * The resize mode of the task. See {@link ActivityInfo#resizeMode}.
-         * @hide
-         */
-        public int resizeMode;
-
-        /**
-         * The full configuration the task is currently running in.
-         * @hide
-         */
-        final public Configuration configuration = new Configuration();
-
         public RunningTaskInfo() {
         }
 
+        private RunningTaskInfo(Parcel source) {
+            readFromParcel(source);
+        }
+
+        @Override
         public int describeContents() {
             return 0;
         }
 
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(id);
-            dest.writeInt(stackId);
-            ComponentName.writeToParcel(baseActivity, dest);
-            ComponentName.writeToParcel(topActivity, dest);
-            if (thumbnail != null) {
-                dest.writeInt(1);
-                thumbnail.writeToParcel(dest, 0);
-            } else {
-                dest.writeInt(0);
-            }
-            TextUtils.writeToParcel(description, dest,
-                    Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
-            dest.writeInt(numActivities);
-            dest.writeInt(numRunning);
-            dest.writeInt(supportsSplitScreenMultiWindow ? 1 : 0);
-            dest.writeInt(resizeMode);
-            configuration.writeToParcel(dest, flags);
-        }
-
         public void readFromParcel(Parcel source) {
             id = source.readInt();
-            stackId = source.readInt();
-            baseActivity = ComponentName.readFromParcel(source);
-            topActivity = ComponentName.readFromParcel(source);
-            if (source.readInt() != 0) {
-                thumbnail = Bitmap.CREATOR.createFromParcel(source);
-            } else {
-                thumbnail = null;
-            }
-            description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
-            numActivities = source.readInt();
-            numRunning = source.readInt();
-            supportsSplitScreenMultiWindow = source.readInt() != 0;
-            resizeMode = source.readInt();
-            configuration.readFromParcel(source);
+            super.readFromParcel(source);
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(id);
+            super.writeToParcel(dest, flags);
         }
 
         public static final Creator<RunningTaskInfo> CREATOR = new Creator<RunningTaskInfo>() {
@@ -1842,10 +1563,6 @@
                 return new RunningTaskInfo[size];
             }
         };
-
-        private RunningTaskInfo(Parcel source) {
-            readFromParcel(source);
-        }
     }
 
     /**
@@ -1858,7 +1575,7 @@
         ArrayList<AppTask> tasks = new ArrayList<AppTask>();
         List<IBinder> appTasks;
         try {
-            appTasks = getService().getAppTasks(mContext.getPackageName());
+            appTasks = getTaskService().getAppTasks(mContext.getPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1883,7 +1600,7 @@
     private void ensureAppTaskThumbnailSizeLocked() {
         if (mAppTaskThumbnailSize == null) {
             try {
-                mAppTaskThumbnailSize = getService().getAppTaskThumbnailSize();
+                mAppTaskThumbnailSize = getTaskService().getAppTaskThumbnailSize();
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -1948,7 +1665,7 @@
             description = new TaskDescription();
         }
         try {
-            return getService().addAppTask(activity.getActivityToken(),
+            return getTaskService().addAppTask(activity.getActivityToken(),
                     intent, description, thumbnail);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1991,103 +1708,7 @@
     public List<RunningTaskInfo> getRunningTasks(int maxNum)
             throws SecurityException {
         try {
-            return getService().getTasks(maxNum);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Sets the windowing mode for a specific task. Only works on tasks of type
-     * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}
-     * @param taskId The id of the task to set the windowing mode for.
-     * @param windowingMode The windowing mode to set for the task.
-     * @param toTop If the task should be moved to the top once the windowing mode changes.
-     * @hide
-     */
-    @TestApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop)
-            throws SecurityException {
-        try {
-            getService().setTaskWindowingMode(taskId, windowingMode, toTop);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Moves the input task to the primary-split-screen stack.
-     * @param taskId Id of task to move.
-     * @param createMode The mode the primary split screen stack should be created in if it doesn't
-     *                  exist already. See
-     *                   {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
-     *                   and
-     *                   {@link android.app.ActivityManager
-     *                        #SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
-     * @param toTop If the task and stack should be moved to the top.
-     * @param animate Whether we should play an animation for the moving the task
-     * @param initialBounds If the primary stack gets created, it will use these bounds for the
-     *                      docked stack. Pass {@code null} to use default bounds.
-     * @param showRecents If the recents activity should be shown on the other side of the task
-     *                    going into split-screen mode.
-     * @hide
-     */
-    @TestApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
-            boolean animate, Rect initialBounds, boolean showRecents) throws SecurityException {
-        try {
-            getService().setTaskWindowingModeSplitScreenPrimary(taskId, createMode, toTop, animate,
-                    initialBounds, showRecents);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Resizes the input stack id to the given bounds.
-     * @param stackId Id of the stack to resize.
-     * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
-     * @hide
-     */
-    @TestApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void resizeStack(int stackId, Rect bounds) throws SecurityException {
-        try {
-            getService().resizeStack(stackId, bounds, false /* allowResizeInDockedMode */,
-                    false /* preserveWindows */, false /* animate */, -1 /* animationDuration */);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Removes stacks in the windowing modes from the system if they are of activity type
-     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
-     *
-     * @hide
-     */
-    @TestApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void removeStacksInWindowingModes(int[] windowingModes) throws SecurityException {
-        try {
-            getService().removeStacksInWindowingModes(windowingModes);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Removes stack of the activity types from the system.
-     *
-     * @hide
-     */
-    @TestApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void removeStacksWithActivityTypes(int[] activityTypes) throws SecurityException {
-        try {
-            getService().removeStacksWithActivityTypes(activityTypes);
+            return getTaskService().getTasks(maxNum);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2296,7 +1917,7 @@
     @RequiresPermission(android.Manifest.permission.REORDER_TASKS)
     public void moveTaskToFront(int taskId, @MoveTaskFlags int flags, Bundle options) {
         try {
-            getService().moveTaskToFront(taskId, flags, options);
+            getTaskService().moveTaskToFront(taskId, flags, options);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2778,7 +2399,6 @@
         return clearApplicationUserData(mContext.getPackageName(), null);
     }
 
-
     /**
      * Permits an application to get the persistent URI permissions granted to another.
      *
@@ -2790,17 +2410,13 @@
      * @return list of granted URI permissions
      *
      * @hide
+     * @deprecated use {@link UriGrantsManager#getGrantedUriPermissions(String)} instead.
      */
+    @Deprecated
     public ParceledListSlice<GrantedUriPermission> getGrantedUriPermissions(
             @Nullable String packageName) {
-        try {
-            @SuppressWarnings("unchecked")
-            final ParceledListSlice<GrantedUriPermission> castedList = getService()
-                    .getGrantedUriPermissions(packageName, mContext.getUserId());
-            return castedList;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return ((UriGrantsManager) mContext.getSystemService(Context.URI_GRANTS_SERVICE))
+                .getGrantedUriPermissions(packageName);
     }
 
     /**
@@ -2811,14 +2427,12 @@
      * @param packageName application to clear its granted permissions
      *
      * @hide
+     * @deprecated use {@link UriGrantsManager#clearGrantedUriPermissions(String)} instead.
      */
+    @Deprecated
     public void clearGrantedUriPermissions(String packageName) {
-        try {
-            getService().clearGrantedUriPermissions(packageName,
-                    mContext.getUserId());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        ((UriGrantsManager) mContext.getSystemService(Context.URI_GRANTS_SERVICE))
+                .clearGrantedUriPermissions(packageName);
     }
 
     /**
@@ -3299,16 +2913,32 @@
          */
         public int processState;
 
+        /**
+         * Whether the app is focused in multi-window environment.
+         * @hide
+         */
+        public boolean isFocused;
+
+        /**
+         * Copy of {@link com.android.server.am.ProcessRecord#lastActivityTime} of the process.
+         * @hide
+         */
+        public long lastActivityTime;
+
         public RunningAppProcessInfo() {
             importance = IMPORTANCE_FOREGROUND;
             importanceReasonCode = REASON_UNKNOWN;
             processState = PROCESS_STATE_IMPORTANT_FOREGROUND;
+            isFocused = false;
+            lastActivityTime = 0;
         }
 
         public RunningAppProcessInfo(String pProcessName, int pPid, String pArr[]) {
             processName = pProcessName;
             pid = pPid;
             pkgList = pArr;
+            isFocused = false;
+            lastActivityTime = 0;
         }
 
         public int describeContents() {
@@ -3329,6 +2959,8 @@
             ComponentName.writeToParcel(importanceReasonComponent, dest);
             dest.writeInt(importanceReasonImportance);
             dest.writeInt(processState);
+            dest.writeInt(isFocused ? 1 : 0);
+            dest.writeLong(lastActivityTime);
         }
 
         public void readFromParcel(Parcel source) {
@@ -3345,6 +2977,8 @@
             importanceReasonComponent = ComponentName.readFromParcel(source);
             importanceReasonImportance = source.readInt();
             processState = source.readInt();
+            isFocused = source.readInt() != 0;
+            lastActivityTime = source.readLong();
         }
 
         public static final Creator<RunningAppProcessInfo> CREATOR =
@@ -3803,7 +3437,7 @@
     @TestApi
     public void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) {
         try {
-            getService().alwaysShowUnsupportedCompileSdkWarning(activity);
+            getTaskService().alwaysShowUnsupportedCompileSdkWarning(activity);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4126,6 +3760,10 @@
         return IActivityManagerSingleton.get();
     }
 
+    private static IActivityTaskManager getTaskService() {
+        return ActivityTaskManager.getService();
+    }
+
     private static final Singleton<IActivityManager> IActivityManagerSingleton =
             new Singleton<IActivityManager>() {
                 @Override
@@ -4241,7 +3879,7 @@
      */
     public int getLockTaskModeState() {
         try {
-            return getService().getLockTaskModeState();
+            return getTaskService().getLockTaskModeState();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4263,7 +3901,7 @@
      */
     public static void setVrThread(int tid) {
         try {
-            getService().setVrThread(tid);
+            getTaskService().setVrThread(tid);
         } catch (RemoteException e) {
             // pass
         }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 1d5f49a..87366db 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -18,19 +18,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ComponentName;
 import android.content.IIntentSender;
 import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.Bundle;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.UserInfo;
 import android.os.IBinder;
-import android.os.SystemClock;
-import android.service.voice.IVoiceInteractionSession;
-import android.util.SparseIntArray;
 import android.view.RemoteAnimationAdapter;
 
-import com.android.internal.app.IVoiceInteractor;
-
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -40,68 +35,11 @@
  */
 public abstract class ActivityManagerInternal {
 
-    /**
-     * Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew
-     * the splash screen.
-     */
-    public static final int APP_TRANSITION_SPLASH_SCREEN =
-              AppProtoEnums.APP_TRANSITION_SPLASH_SCREEN; // 1
 
-    /**
-     * Type for {@link #notifyAppTransitionStarting}: The transition was started because we all
-     * app windows were drawn
-     */
-    public static final int APP_TRANSITION_WINDOWS_DRAWN =
-              AppProtoEnums.APP_TRANSITION_WINDOWS_DRAWN; // 2
-
-    /**
-     * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
-     * timeout.
-     */
-    public static final int APP_TRANSITION_TIMEOUT =
-              AppProtoEnums.APP_TRANSITION_TIMEOUT; // 3
-
-    /**
-     * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
-     * we drew a task snapshot.
-     */
-    public static final int APP_TRANSITION_SNAPSHOT =
-              AppProtoEnums.APP_TRANSITION_SNAPSHOT; // 4
-
-    /**
-     * Type for {@link #notifyAppTransitionStarting}: The transition was started because it was a
-     * recents animation and we only needed to wait on the wallpaper.
-     */
-    public static final int APP_TRANSITION_RECENTS_ANIM =
-            AppProtoEnums.APP_TRANSITION_RECENTS_ANIM; // 5
-
-    /**
-     * The bundle key to extract the assist data.
-     */
-    public static final String ASSIST_KEY_DATA = "data";
-
-    /**
-     * The bundle key to extract the assist structure.
-     */
-    public static final String ASSIST_KEY_STRUCTURE = "structure";
-
-    /**
-     * The bundle key to extract the assist content.
-     */
-    public static final String ASSIST_KEY_CONTENT = "content";
-
-    /**
-     * The bundle key to extract the assist receiver extras.
-     */
-    public static final String ASSIST_KEY_RECEIVER_EXTRAS = "receiverExtras";
-
-
-    /**
-     * Grant Uri permissions from one app to another. This method only extends
-     * permission grants if {@code callingUid} has permission to them.
-     */
-    public abstract void grantUriPermissionFromIntent(int callingUid, String targetPkg,
-            Intent intent, int targetUserId);
+    // Access modes for handleIncomingUser.
+    public static final int ALLOW_NON_FULL = 0;
+    public static final int ALLOW_NON_FULL_IN_PROFILE = 1;
+    public static final int ALLOW_FULL_ONLY = 2;
 
     /**
      * Verify that calling app has access to the given provider.
@@ -118,81 +56,6 @@
             String processName, String abiOverride, int uid, Runnable crashHandler);
 
     /**
-     * Acquires a sleep token for the specified display with the specified tag.
-     *
-     * @param tag A string identifying the purpose of the token (eg. "Dream").
-     * @param displayId The display to apply the sleep token to.
-     */
-    public abstract SleepToken acquireSleepToken(@NonNull String tag, int displayId);
-
-    /**
-     * Sleep tokens cause the activity manager to put the top activity to sleep.
-     * They are used by components such as dreams that may hide and block interaction
-     * with underlying activities.
-     */
-    public static abstract class SleepToken {
-
-        /**
-         * Releases the sleep token.
-         */
-        public abstract void release();
-    }
-
-    /**
-     * Returns home activity for the specified user.
-     *
-     * @param userId ID of the user or {@link android.os.UserHandle#USER_ALL}
-     */
-    public abstract ComponentName getHomeActivityForUser(int userId);
-
-    /**
-     * Called when a user has been deleted. This can happen during normal device usage
-     * or just at startup, when partially removed users are purged. Any state persisted by the
-     * ActivityManager should be purged now.
-     *
-     * @param userId The user being cleaned up.
-     */
-    public abstract void onUserRemoved(int userId);
-
-    public abstract void onLocalVoiceInteractionStarted(IBinder callingActivity,
-            IVoiceInteractionSession mSession,
-            IVoiceInteractor mInteractor);
-
-    /**
-     * Callback for window manager to let activity manager know that we are finally starting the
-     * app transition;
-     *
-     * @param reasons A map from windowing mode to a reason integer why the transition was started,
-     *                which must be one of the APP_TRANSITION_* values.
-     * @param timestamp The time at which the app transition started in
-     *                  {@link SystemClock#uptimeMillis()} timebase.
-     */
-    public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp);
-
-    /**
-     * Callback for window manager to let activity manager know that the app transition was
-     * cancelled.
-     */
-    public abstract void notifyAppTransitionCancelled();
-
-    /**
-     * Callback for window manager to let activity manager know that the app transition is finished.
-     */
-    public abstract void notifyAppTransitionFinished();
-
-    /**
-     * Returns the top activity from each of the currently visible stacks. The first entry will be
-     * the focused activity.
-     */
-    public abstract List<IBinder> getTopVisibleActivities();
-
-    /**
-     * Callback for window manager to let activity manager know that docked stack changes its
-     * minimized state.
-     */
-    public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
-
-    /**
      * Kill foreground apps from the specified user.
      */
     public abstract void killForegroundAppsForUser(int userHandle);
@@ -216,37 +79,6 @@
             boolean adding);
 
     /**
-     * Updates and persists the {@link Configuration} for a given user.
-     *
-     * @param values the configuration to update
-     * @param userId the user to update the configuration for
-     */
-    public abstract void updatePersistentConfigurationForUser(@NonNull Configuration values,
-            int userId);
-
-    /**
-     * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
-     *
-     * - DO NOT call it with the calling UID cleared.
-     * - All the necessary caller permission checks must be done at callsites.
-     *
-     * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
-     */
-    public abstract int startActivitiesAsPackage(String packageName,
-            int userId, Intent[] intents, Bundle bOptions);
-
-    /**
-     * Start activity {@code intent} without calling user-id check.
-     *
-     * - DO NOT call it with the calling UID cleared.
-     * - The caller must do the calling user ID check.
-     *
-     * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
-     */
-    public abstract int startActivityAsUser(IApplicationThread caller, String callingPackage,
-            Intent intent, @Nullable Bundle options, int userId);
-
-    /**
      * Get the procstate for the UID.  The return value will be between
      * {@link ActivityManager#MIN_PROCESS_STATE} and {@link ActivityManager#MAX_PROCESS_STATE}.
      * Note if the UID doesn't exist, it'll return {@link ActivityManager#PROCESS_STATE_NONEXISTENT}
@@ -255,25 +87,11 @@
     public abstract int getUidProcessState(int uid);
 
     /**
-     * Called when Keyguard flags might have changed.
-     *
-     * @param callback Callback to run after activity visibilities have been reevaluated. This can
-     *                 be used from window manager so that when the callback is called, it's
-     *                 guaranteed that all apps have their visibility updated accordingly.
-     */
-    public abstract void notifyKeyguardFlagsChanged(@Nullable Runnable callback);
-
-    /**
      * @return {@code true} if system is ready, {@code false} otherwise.
      */
     public abstract boolean isSystemReady();
 
     /**
-     * Called when the trusted state of Keyguard has changed.
-     */
-    public abstract void notifyKeyguardTrustedChanged();
-
-    /**
      * Sets if the given pid has an overlay UI or not.
      *
      * @param pid The pid we are setting overlay UI for.
@@ -301,18 +119,6 @@
     public abstract void notifyNetworkPolicyRulesUpdated(int uid, long procStateSeq);
 
     /**
-     * Called after the voice interaction service has changed.
-     */
-    public abstract void notifyActiveVoiceInteractionServiceChanged(ComponentName component);
-
-    /**
-     * Called after virtual display Id is updated by
-     * {@link com.android.server.vr.Vr2dDisplay} with a specific
-     * {@param vr2dDisplayId}.
-     */
-    public abstract void setVr2dDisplayId(int vr2dDisplayId);
-
-    /**
      * Saves the current activity manager state and includes the saved state in the next dump of
      * activity manager.
      */
@@ -324,40 +130,11 @@
     public abstract void clearSavedANRState();
 
     /**
-     * Set focus on an activity.
-     * @param token The IApplicationToken for the activity
-     */
-    public abstract void setFocusedActivity(IBinder token);
-
-    /**
-     * Set a uid that is allowed to bypass stopped app switches, launching an app
-     * whenever it wants.
-     *
-     * @param type Type of the caller -- unique string the caller supplies to identify itself
-     * and disambiguate with other calles.
-     * @param uid The uid of the app to be allowed, or -1 to clear the uid for this type.
-     * @param userId The user it is allowed for.
-     */
-    public abstract void setAllowAppSwitches(@NonNull String type, int uid, int userId);
-
-    /**
      * @return true if runtime was restarted, false if it's normal boot
      */
     public abstract boolean isRuntimeRestarted();
 
     /**
-     * Returns {@code true} if {@code uid} is running an activity from {@code packageName}.
-     */
-    public abstract boolean hasRunningActivity(int uid, @Nullable String packageName);
-
-    public interface ScreenObserver {
-        public void onAwakeStateChanged(boolean isAwake);
-        public void onKeyguardStateChanged(boolean isShowing);
-    }
-
-    public abstract void registerScreenObserver(ScreenObserver observer);
-
-    /**
      * Returns if more users can be started without stopping currently running users.
      */
     public abstract boolean canStartMoreUsers();
@@ -378,21 +155,6 @@
     public abstract int getMaxRunningUsers();
 
     /**
-     * Returns is the caller has the same uid as the Recents component
-     */
-    public abstract boolean isCallerRecents(int callingUid);
-
-    /**
-     * Returns whether the recents component is the home activity for the given user.
-     */
-    public abstract boolean isRecentsComponentHomeActivity(int userId);
-
-    /**
-     * Cancels any currently running recents animation.
-     */
-    public abstract void cancelRecentsAnimation(boolean restoreHomeStackPosition);
-
-    /**
      * Whether an UID is active or idle.
      */
     public abstract boolean isUidActive(int uid);
@@ -403,8 +165,65 @@
     public abstract List<ProcessMemoryState> getMemoryStateForProcesses();
 
     /**
-     * This enforces {@code func} can only be called if either the caller is Recents activity or
-     * has {@code permission}.
+     * Checks to see if the calling pid is allowed to handle the user. Returns adjusted user id as
+     * needed.
      */
-    public abstract void enforceCallerIsRecentsOrHasPermission(String permission, String func);
+    public abstract int handleIncomingUser(int callingPid, int callingUid, int userId,
+            boolean allowAll, int allowMode, String name, String callerPackage);
+
+    /** Checks if the calling binder pid as the permission. */
+    public abstract void enforceCallingPermission(String permission, String func);
+
+    /** Returns the current user id. */
+    public abstract int getCurrentUserId();
+
+    /** Returns true if the user is running. */
+    public abstract boolean isUserRunning(int userId, int flags);
+
+    /** Trims memory usage in the system by removing/stopping unused application processes. */
+    public abstract void trimApplications();
+
+    /** Returns the screen compatibility mode for the given application. */
+    public abstract int getPackageScreenCompatMode(ApplicationInfo ai);
+
+    /** Sets the screen compatibility mode for the given application. */
+    public abstract void setPackageScreenCompatMode(ApplicationInfo ai, int mode);
+
+    /** Closes all system dialogs. */
+    public abstract void closeSystemDialogs(String reason);
+
+    /** Kill the processes in the list due to their tasks been removed. */
+    public abstract void killProcessesForRemovedTask(ArrayList<Object> procsToKill);
+
+    /**
+     * Returns {@code true} if {@code uid} is running an activity from {@code packageName}.
+     */
+    public abstract boolean hasRunningActivity(int uid, @Nullable String packageName);
+
+    public abstract void updateOomAdj();
+    public abstract void sendForegroundProfileChanged(int userId);
+
+    /**
+     * Returns whether the given user requires credential entry at this time. This is used to
+     * intercept activity launches for work apps when the Work Challenge is present.
+     */
+    public abstract boolean shouldConfirmCredentials(int userId);
+
+    /**
+     * @return The intent used to launch the home activity.
+     */
+    public abstract Intent getHomeIntent();
+
+    public abstract int[] getCurrentProfileIds();
+    public abstract UserInfo getCurrentUser();
+    public abstract void ensureNotSpecialUser(int userId);
+    public abstract boolean isCurrentProfile(int userId);
+    public abstract boolean hasStartedUserState(int userId);
+    public abstract void finishUserSwitch(Object uss);
+
+    /** Schedule the execution of all pending app GCs. */
+    public abstract void scheduleAppGcs();
+
+    /** Gets the task id for a given activity. */
+    public abstract int getTaskIdForActivity(@NonNull IBinder token, boolean onlyRoot);
 }
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index ecd99a7..8914535 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,7 +17,7 @@
 package android.app;
 
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.INVALID_DISPLAY;
@@ -302,7 +302,6 @@
     private int mResultCode;
     private int mExitCoordinatorIndex;
     private PendingIntent mUsageTimeReport;
-    private boolean mLockTaskMode = false;
     private int mLaunchDisplayId = INVALID_DISPLAY;
     @WindowConfiguration.WindowingMode
     private int mLaunchWindowingMode = WINDOWING_MODE_UNDEFINED;
@@ -310,6 +309,7 @@
     private int mLaunchActivityType = ACTIVITY_TYPE_UNDEFINED;
     private int mLaunchTaskId = -1;
     private int mSplitScreenCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+    private boolean mLockTaskMode = false;
     private boolean mDisallowEnterPictureInPictureWhileLaunching;
     private boolean mTaskOverlay;
     private boolean mTaskOverlayCanResume;
@@ -946,7 +946,7 @@
             mAnimationFinishedListener = IRemoteCallback.Stub.asInterface(
                     opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER));
         }
-        mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT);
+        mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT, -1);
         mAppVerificationBundle = opts.getBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE);
         if (opts.containsKey(KEY_SPECS_FUTURE)) {
             mSpecsFuture = IAppTransitionAnimationSpecsFuture.Stub.asInterface(opts.getBinder(
@@ -1442,17 +1442,37 @@
                 b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
                 break;
         }
-        b.putBoolean(KEY_LOCK_TASK_MODE, mLockTaskMode);
-        b.putInt(KEY_LAUNCH_DISPLAY_ID, mLaunchDisplayId);
-        b.putInt(KEY_LAUNCH_WINDOWING_MODE, mLaunchWindowingMode);
-        b.putInt(KEY_LAUNCH_ACTIVITY_TYPE, mLaunchActivityType);
-        b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
-        b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
-        b.putBoolean(KEY_TASK_OVERLAY_CAN_RESUME, mTaskOverlayCanResume);
-        b.putBoolean(KEY_AVOID_MOVE_TO_FRONT, mAvoidMoveToFront);
-        b.putInt(KEY_SPLIT_SCREEN_CREATE_MODE, mSplitScreenCreateMode);
-        b.putBoolean(KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING,
-                mDisallowEnterPictureInPictureWhileLaunching);
+        if (mLockTaskMode) {
+            b.putBoolean(KEY_LOCK_TASK_MODE, mLockTaskMode);
+        }
+        if (mLaunchDisplayId != INVALID_DISPLAY) {
+            b.putInt(KEY_LAUNCH_DISPLAY_ID, mLaunchDisplayId);
+        }
+        if (mLaunchWindowingMode != WINDOWING_MODE_UNDEFINED) {
+            b.putInt(KEY_LAUNCH_WINDOWING_MODE, mLaunchWindowingMode);
+        }
+        if (mLaunchActivityType != ACTIVITY_TYPE_UNDEFINED) {
+            b.putInt(KEY_LAUNCH_ACTIVITY_TYPE, mLaunchActivityType);
+        }
+        if (mLaunchTaskId != -1) {
+            b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
+        }
+        if (mTaskOverlay) {
+            b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
+        }
+        if (mTaskOverlayCanResume) {
+            b.putBoolean(KEY_TASK_OVERLAY_CAN_RESUME, mTaskOverlayCanResume);
+        }
+        if (mAvoidMoveToFront) {
+            b.putBoolean(KEY_AVOID_MOVE_TO_FRONT, mAvoidMoveToFront);
+        }
+        if (mSplitScreenCreateMode != SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT) {
+            b.putInt(KEY_SPLIT_SCREEN_CREATE_MODE, mSplitScreenCreateMode);
+        }
+        if (mDisallowEnterPictureInPictureWhileLaunching) {
+            b.putBoolean(KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING,
+                    mDisallowEnterPictureInPictureWhileLaunching);
+        }
         if (mAnimSpecs != null) {
             b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
         }
@@ -1462,7 +1482,9 @@
         if (mSpecsFuture != null) {
             b.putBinder(KEY_SPECS_FUTURE, mSpecsFuture.asBinder());
         }
-        b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint);
+        if (mRotationAnimationHint != -1) {
+            b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint);
+        }
         if (mAppVerificationBundle != null) {
             b.putBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE, mAppVerificationBundle);
         }
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
new file mode 100644
index 0000000..2f18b89
--- /dev/null
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -0,0 +1,266 @@
+/*
+ * 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.app;
+
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Singleton;
+
+/**
+ * This class gives information about, and interacts with activities and their containers like task,
+ * stacks, and displays.
+ *
+ * @hide
+ */
+@TestApi
+@SystemService(Context.ACTIVITY_TASK_SERVICE)
+public class ActivityTaskManager {
+
+    /** Invalid stack ID. */
+    public static final int INVALID_STACK_ID = -1;
+
+    /**
+     * Parameter to {@link IActivityTaskManager#setTaskWindowingModeSplitScreenPrimary} which
+     * specifies the position of the created docked stack at the top half of the screen if
+     * in portrait mode or at the left half of the screen if in landscape mode.
+     */
+    public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0;
+
+    /**
+     * Parameter to {@link IActivityTaskManager#setTaskWindowingModeSplitScreenPrimary} which
+     * specifies the position of the created docked stack at the bottom half of the screen if
+     * in portrait mode or at the right half of the screen if in landscape mode.
+     */
+    public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1;
+
+    /**
+     * Input parameter to {@link IActivityTaskManager#resizeTask} which indicates
+     * that the resize doesn't need to preserve the window, and can be skipped if bounds
+     * is unchanged. This mode is used by window manager in most cases.
+     * @hide
+     */
+    public static final int RESIZE_MODE_SYSTEM = 0;
+
+    /**
+     * Input parameter to {@link IActivityTaskManager#resizeTask} which indicates
+     * that the resize should preserve the window if possible.
+     * @hide
+     */
+    public static final int RESIZE_MODE_PRESERVE_WINDOW   = (0x1 << 0);
+
+    /**
+     * Input parameter to {@link IActivityTaskManager#resizeTask} used when the
+     * resize is due to a drag action.
+     * @hide
+     */
+    public static final int RESIZE_MODE_USER = RESIZE_MODE_PRESERVE_WINDOW;
+
+    /**
+     * Input parameter to {@link IActivityTaskManager#resizeTask} used by window
+     * manager during a screen rotation.
+     * @hide
+     */
+    public static final int RESIZE_MODE_SYSTEM_SCREEN_ROTATION = RESIZE_MODE_PRESERVE_WINDOW;
+
+    /**
+     * Input parameter to {@link IActivityTaskManager#resizeTask} which indicates
+     * that the resize should be performed even if the bounds appears unchanged.
+     * @hide
+     */
+    public static final int RESIZE_MODE_FORCED = (0x1 << 1);
+
+    /**
+     * Input parameter to {@link IActivityTaskManager#resizeTask} which indicates
+     * that the resize should preserve the window if possible, and should not be skipped
+     * even if the bounds is unchanged. Usually used to force a resizing when a drag action
+     * is ending.
+     * @hide
+     */
+    public static final int RESIZE_MODE_USER_FORCED =
+            RESIZE_MODE_PRESERVE_WINDOW | RESIZE_MODE_FORCED;
+
+    private static int sMaxRecentTasks = -1;
+
+    ActivityTaskManager(Context context, Handler handler) {
+    }
+
+    /** @hide */
+    public static IActivityTaskManager getService() {
+        return IActivityTaskManagerSingleton.get();
+    }
+
+    private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
+            new Singleton<IActivityTaskManager>() {
+                @Override
+                protected IActivityTaskManager create() {
+                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
+                    return IActivityTaskManager.Stub.asInterface(b);
+                }
+            };
+
+    /**
+     * Sets the windowing mode for a specific task. Only works on tasks of type
+     * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}
+     * @param taskId The id of the task to set the windowing mode for.
+     * @param windowingMode The windowing mode to set for the task.
+     * @param toTop If the task should be moved to the top once the windowing mode changes.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+    public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop)
+            throws SecurityException {
+        try {
+            getService().setTaskWindowingMode(taskId, windowingMode, toTop);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Moves the input task to the primary-split-screen stack.
+     * @param taskId Id of task to move.
+     * @param createMode The mode the primary split screen stack should be created in if it doesn't
+     *                   exist already. See
+     *                   {@link ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
+     *                   and
+     *                   {@link android.app.ActivityManager
+     *                        #SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
+     * @param toTop If the task and stack should be moved to the top.
+     * @param animate Whether we should play an animation for the moving the task
+     * @param initialBounds If the primary stack gets created, it will use these bounds for the
+     *                      docked stack. Pass {@code null} to use default bounds.
+     * @param showRecents If the recents activity should be shown on the other side of the task
+     *                    going into split-screen mode.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+    public void setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
+            boolean animate, Rect initialBounds, boolean showRecents) throws SecurityException {
+        try {
+            getService().setTaskWindowingModeSplitScreenPrimary(taskId, createMode, toTop, animate,
+                    initialBounds, showRecents);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Resizes the input stack id to the given bounds.
+     * @param stackId Id of the stack to resize.
+     * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+    public void resizeStack(int stackId, Rect bounds) throws SecurityException {
+        try {
+            getService().resizeStack(stackId, bounds, false /* allowResizeInDockedMode */,
+                    false /* preserveWindows */, false /* animate */, -1 /* animationDuration */);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Removes stacks in the windowing modes from the system if they are of activity type
+     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+    public void removeStacksInWindowingModes(int[] windowingModes) throws SecurityException {
+        try {
+            getService().removeStacksInWindowingModes(windowingModes);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** Removes stack of the activity types from the system. */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+    public void removeStacksWithActivityTypes(int[] activityTypes) throws SecurityException {
+        try {
+            getService().removeStacksWithActivityTypes(activityTypes);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Removes all visible recent tasks from the system.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.REMOVE_TASKS)
+    public void removeAllVisibleRecentTasks() {
+        try {
+            getService().removeAllVisibleRecentTasks();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the maximum number of recents entries that we will maintain and show.
+     * @hide
+     */
+    public static int getMaxRecentTasksStatic() {
+        if (sMaxRecentTasks < 0) {
+            return sMaxRecentTasks = ActivityManager.isLowRamDeviceStatic() ? 36 : 48;
+        }
+        return sMaxRecentTasks;
+    }
+
+    /**
+     * Return the default limit on the number of recents that an app can make.
+     * @hide
+     */
+    public static int getDefaultAppRecentsLimitStatic() {
+        return getMaxRecentTasksStatic() / 6;
+    }
+
+    /**
+     * Return the maximum limit on the number of recents that an app can make.
+     * @hide
+     */
+    public static int getMaxAppRecentsLimitStatic() {
+        return getMaxRecentTasksStatic() / 2;
+    }
+
+    /**
+     * Returns true if the system supports at least one form of multi-window.
+     * E.g. freeform, split-screen, picture-in-picture.
+     */
+    public static boolean supportsMultiWindow(Context context) {
+        // On watches, multi-window is used to present essential system UI, and thus it must be
+        // supported regardless of device memory characteristics.
+        boolean isWatch = context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_WATCH);
+        return (!ActivityManager.isLowRamDeviceStatic() || isWatch)
+                && Resources.getSystem().getBoolean(
+                com.android.internal.R.bool.config_supportsMultiWindow);
+    }
+
+    /** Returns true if the system supports split screen multi-window. */
+    public static boolean supportsSplitScreenMultiWindow(Context context) {
+        return supportsMultiWindow(context)
+                && Resources.getSystem().getBoolean(
+                com.android.internal.R.bool.config_supportsSplitScreenMultiWindow);
+    }
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3696919..2a3fc04 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -35,6 +35,7 @@
 import android.app.servertransaction.ActivityRelaunchItem;
 import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
 import android.app.servertransaction.PendingTransactionActions;
 import android.app.servertransaction.PendingTransactionActions.StopInfo;
 import android.app.servertransaction.TransactionExecutor;
@@ -176,6 +177,7 @@
 import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -257,6 +259,9 @@
     final H mH = new H();
     final Executor mExecutor = new HandlerExecutor(mH);
     final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
+    /** The activities to be truly destroyed (not include relaunch). */
+    final Map<IBinder, ClientTransactionItem> mActivitiesToBeDestroyed =
+            Collections.synchronizedMap(new ArrayMap<IBinder, ClientTransactionItem>());
     // List of new activities (via ActivityRecord.nextIdle) that should
     // be reported when next we idle.
     ActivityClientRecord mNewActivities = null;
@@ -1840,7 +1845,7 @@
             }
             if (a != null) {
                 mNewActivities = null;
-                IActivityManager am = ActivityManager.getService();
+                IActivityTaskManager am = ActivityTaskManager.getService();
                 ActivityClientRecord prev;
                 do {
                     if (localLOGV) Slog.v(
@@ -2727,7 +2732,8 @@
     }
 
     public final Activity getActivity(IBinder token) {
-        return mActivities.get(token).activity;
+        final ActivityClientRecord activityRecord = mActivities.get(token);
+        return activityRecord != null ? activityRecord.activity : null;
     }
 
     @Override
@@ -2993,7 +2999,7 @@
     private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
         final int displayId;
         try {
-            displayId = ActivityManager.getService().getActivityDisplayId(r.token);
+            displayId = ActivityTaskManager.getService().getActivityDisplayId(r.token);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3061,7 +3067,7 @@
         } else {
             // If there was an error, for any reason, tell the activity manager to stop us.
             try {
-                ActivityManager.getService()
+                ActivityTaskManager.getService()
                         .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                                 Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
             } catch (RemoteException ex) {
@@ -3073,6 +3079,10 @@
     }
 
     private void reportSizeConfigurations(ActivityClientRecord r) {
+        if (mActivitiesToBeDestroyed.containsKey(r.token)) {
+            // Size configurations of a destroyed activity is meaningless.
+            return;
+        }
         Configuration[] configurations = r.activity.getResources().getSizeConfigurations();
         if (configurations == null) {
             return;
@@ -3093,7 +3103,7 @@
             }
         }
         try {
-            ActivityManager.getService().reportSizeConfigurations(r.token,
+            ActivityTaskManager.getService().reportSizeConfigurations(r.token,
                     horizontal.copyKeys(), vertical.copyKeys(), smallest.copyKeys());
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
@@ -3210,7 +3220,7 @@
         structure.setAcquisitionEndTime(SystemClock.uptimeMillis());
 
         mLastAssistStructures.add(new WeakReference<>(structure));
-        IActivityManager mgr = ActivityManager.getService();
+        IActivityTaskManager mgr = ActivityTaskManager.getService();
         try {
             mgr.reportAssistContextExtras(cmd.requestToken, data, structure, content, referrer);
         } catch (RemoteException e) {
@@ -3821,6 +3831,13 @@
             // We didn't actually resume the activity, so skipping any follow-up actions.
             return;
         }
+        if (mActivitiesToBeDestroyed.containsKey(token)) {
+            // Although the activity is resumed, it is going to be destroyed. So the following
+            // UI operations are unnecessary and also prevents exception because its token may
+            // be gone that window manager cannot recognize it. All necessary cleanup actions
+            // performed below will be done while handling destruction.
+            return;
+        }
 
         final Activity a = r.activity;
 
@@ -3838,7 +3855,7 @@
         boolean willBeVisible = !a.mStartedActivity;
         if (!willBeVisible) {
             try {
-                willBeVisible = ActivityManager.getService().willActivityBeVisible(
+                willBeVisible = ActivityTaskManager.getService().willActivityBeVisible(
                         a.getActivityToken());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
@@ -4290,7 +4307,7 @@
 
             // Tell activity manager we slept.
             try {
-                ActivityManager.getService().activitySlept(r.token);
+                ActivityTaskManager.getService().activitySlept(r.token);
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
@@ -4474,6 +4491,11 @@
     }
 
     @Override
+    public Map<IBinder, ClientTransactionItem> getActivitiesToBeDestroyed() {
+        return mActivitiesToBeDestroyed;
+    }
+
+    @Override
     public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
             boolean getNonConfigInstance, String reason) {
         ActivityClientRecord r = performDestroyActivity(token, finishing,
@@ -4537,7 +4559,7 @@
         }
         if (finishing) {
             try {
-                ActivityManager.getService().activityDestroyed(token);
+                ActivityTaskManager.getService().activityDestroyed(token);
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
@@ -4790,7 +4812,7 @@
     @Override
     public void reportRelaunch(IBinder token, PendingTransactionActions pendingActions) {
         try {
-            ActivityManager.getService().activityRelaunched(token);
+            ActivityTaskManager.getService().activityRelaunched(token);
             final ActivityClientRecord r = mActivities.get(token);
             if (pendingActions.shouldReportRelaunchToWindowManager() && r != null
                     && r.window != null) {
@@ -6515,7 +6537,7 @@
                                 + " used=" + (dalvikUsed/1024));
                         mSomeActivitiesChanged = false;
                         try {
-                            mgr.releaseSomeActivities(mAppThread);
+                            ActivityTaskManager.getService().releaseSomeActivities(mAppThread);
                         } catch (RemoteException e) {
                             throw e.rethrowFromSystemServer();
                         }
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index e469098..c7fa33d 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -64,6 +64,7 @@
     private StateCallback mActivityViewCallback;
 
     private IActivityManager mActivityManager;
+    private IActivityTaskManager mActivityTaskManager;
     private IInputForwarder mInputForwarder;
     // Temp container to store view coordinates on screen.
     private final int[] mLocationOnScreen = new int[2];
@@ -85,6 +86,7 @@
         super(context, attrs, defStyle);
 
         mActivityManager = ActivityManager.getService();
+        mActivityTaskManager = ActivityTaskManager.getService();
         mSurfaceView = new SurfaceView(context);
         mSurfaceCallback = new SurfaceCallback();
         mSurfaceView.getHolder().addCallback(mSurfaceCallback);
@@ -349,7 +351,7 @@
         mInputForwarder = InputManager.getInstance().createInputForwarder(displayId);
         mTaskStackListener = new TaskStackListenerImpl();
         try {
-            mActivityManager.registerTaskStackListener(mTaskStackListener);
+            mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to register task stack listener", e);
         }
@@ -369,7 +371,7 @@
 
         if (mTaskStackListener != null) {
             try {
-                mActivityManager.unregisterTaskStackListener(mTaskStackListener);
+                mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to unregister task stack listener", e);
             }
@@ -474,7 +476,7 @@
             // Find the topmost task on our virtual display - it will define the background
             // color of the surface view during resizing.
             final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
-            final List<StackInfo> stackInfoList = mActivityManager.getAllStackInfos();
+            final List<StackInfo> stackInfoList = mActivityTaskManager.getAllStackInfos();
 
             // Iterate through stacks from top to bottom.
             final int stackCount = stackInfoList.size();
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 124f933..f76f911 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -28,7 +28,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.os.WorkSource;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -958,6 +957,7 @@
      *
      * @param millis time in milliseconds since the Epoch
      */
+    @RequiresPermission(android.Manifest.permission.SET_TIME)
     public void setTime(long millis) {
         try {
             mService.setTime(millis);
@@ -981,6 +981,7 @@
      * @param timeZone one of the Olson ids from the list returned by
      *     {@link java.util.TimeZone#getAvailableIDs}
      */
+    @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE)
     public void setTimeZone(String timeZone) {
         if (TextUtils.isEmpty(timeZone)) {
             return;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 973ca12..c3404a5 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -114,7 +114,6 @@
      * returned from {@link #checkOp}, {@link #noteOp}, {@link #startOp}; rather, when this
      * mode is set, these functions will return {@link #MODE_ALLOWED} when the app being
      * checked is currently in the foreground, otherwise {@link #MODE_IGNORED}.
-     * @hide
      */
     public static final int MODE_FOREGROUND = 4;
 
@@ -122,7 +121,6 @@
      * Flag for {@link #startWatchingMode(String, String, int, OnOpChangedListener)}:
      * Also get reports if the foreground state of an op's uid changes.  This only works
      * when watching a particular op, not when watching a package.
-     * @hide
      */
     public static final int WATCH_FOREGROUND_CHANGES = 1 << 0;
 
@@ -352,8 +350,10 @@
     public static final int OP_START_FOREGROUND = 76;
     /** @hide */
     public static final int OP_BLUETOOTH_SCAN = 77;
+    /** @hide Use the face authentication API. */
+    public static final int OP_USE_FACE = 78;
     /** @hide */
-    public static final int _NUM_OP = 78;
+    public static final int _NUM_OP = 79;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -598,6 +598,9 @@
     /** @hide */
     public static final String OPSTR_BLUETOOTH_SCAN = "android:bluetooth_scan";
 
+    /** @hide Use the face authentication API. */
+    public static final String OPSTR_USE_FACE = "android:use_face";
+
     // Warning: If an permission is added here it also has to be added to
     // com.android.packageinstaller.permission.utils.EventLogger
     private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = {
@@ -735,6 +738,7 @@
             OP_MANAGE_IPSEC_TUNNELS,            // MANAGE_IPSEC_HANDOVERS
             OP_START_FOREGROUND,                // START_FOREGROUND
             OP_COARSE_LOCATION,                 // BLUETOOTH_SCAN
+            OP_USE_FACE,                        // FACE
     };
 
     /**
@@ -819,6 +823,7 @@
             OPSTR_MANAGE_IPSEC_TUNNELS,
             OPSTR_START_FOREGROUND,
             OPSTR_BLUETOOTH_SCAN,
+            OPSTR_USE_FACE,
     };
 
     /**
@@ -904,6 +909,7 @@
             "MANAGE_IPSEC_TUNNELS",
             "START_FOREGROUND",
             "BLUETOOTH_SCAN",
+            "USE_FACE",
     };
 
     /**
@@ -989,6 +995,7 @@
             null, // no permission for OP_MANAGE_IPSEC_TUNNELS
             Manifest.permission.FOREGROUND_SERVICE,
             null, // no permission for OP_BLUETOOTH_SCAN
+            Manifest.permission.USE_BIOMETRIC,
     };
 
     /**
@@ -1075,6 +1082,7 @@
             null, // MANAGE_IPSEC_TUNNELS
             null, // START_FOREGROUND
             null, // maybe should be UserManager.DISALLOW_SHARE_LOCATION, //BLUETOOTH_SCAN
+            null, // USE_FACE
     };
 
     /**
@@ -1160,6 +1168,7 @@
             false, // MANAGE_IPSEC_HANDOVERS
             false, // START_FOREGROUND
             true, // BLUETOOTH_SCAN
+            false, // USE_FACE
     };
 
     /**
@@ -1244,6 +1253,7 @@
             AppOpsManager.MODE_ERRORED,  // MANAGE_IPSEC_TUNNELS
             AppOpsManager.MODE_ALLOWED,  // OP_START_FOREGROUND
             AppOpsManager.MODE_ALLOWED,  // OP_BLUETOOTH_SCAN
+            AppOpsManager.MODE_ALLOWED,  // USE_FACE
     };
 
     /**
@@ -1332,6 +1342,7 @@
             false, // MANAGE_IPSEC_TUNNELS
             false, // START_FOREGROUND
             false, // BLUETOOTH_SCAN
+            false, // USE_FACE
     };
 
     /**
@@ -1487,11 +1498,15 @@
      * Class holding all of the operation information associated with an app.
      * @hide
      */
-    public static class PackageOps implements Parcelable {
+    @SystemApi
+    public static final class PackageOps implements Parcelable {
         private final String mPackageName;
         private final int mUid;
         private final List<OpEntry> mEntries;
 
+        /**
+         * @hide
+         */
         public PackageOps(String packageName, int uid, List<OpEntry> entries) {
             mPackageName = packageName;
             mUid = uid;
@@ -1550,7 +1565,8 @@
      * Class holding the information about one unique operation of an application.
      * @hide
      */
-    public static class OpEntry implements Parcelable {
+    @SystemApi
+    public static final class OpEntry implements Parcelable {
         private final int mOp;
         private final int mMode;
         private final long[] mTimes;
@@ -1560,6 +1576,9 @@
         private final boolean mRunning;
         private final String mProxyPackageName;
 
+        /**
+         * @hide
+         */
         public OpEntry(int op, int mode, long time, long rejectTime, int duration,
                 int proxyUid, String proxyPackage) {
             mOp = op;
@@ -1574,6 +1593,9 @@
             mProxyPackageName = proxyPackage;
         }
 
+        /**
+         * @hide
+         */
         public OpEntry(int op, int mode, long[] times, long[] rejectTimes, int duration,
                 boolean running, int proxyUid, String proxyPackage) {
             mOp = op;
@@ -1588,55 +1610,104 @@
             mProxyPackageName = proxyPackage;
         }
 
+        /**
+         * @hide
+         */
         public OpEntry(int op, int mode, long[] times, long[] rejectTimes, int duration,
                 int proxyUid, String proxyPackage) {
             this(op, mode, times, rejectTimes, duration, duration == -1, proxyUid, proxyPackage);
         }
 
+        /**
+         * @hide
+         */
         public int getOp() {
             return mOp;
         }
 
+        /**
+         * Return this entry's op string name, such as {@link #OPSTR_COARSE_LOCATION}.
+         */
+        public String getOpStr() {
+            return sOpToString[mOp];
+        }
+
+        /**
+         * Return this entry's current mode, such as {@link #MODE_ALLOWED}.
+         */
         public int getMode() {
             return mMode;
         }
 
+        /**
+         * @hide
+         */
         public long getTime() {
             return maxTime(mTimes, 0, _NUM_UID_STATE);
         }
 
+        /**
+         * Return the last wall clock time this op was accessed by the app.
+         */
         public long getLastAccessTime() {
             return maxTime(mTimes, 0, _NUM_UID_STATE);
         }
 
+        /**
+         * Return the last wall clock time this op was accessed by the app while in the foreground.
+         */
         public long getLastAccessForegroundTime() {
             return maxTime(mTimes, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
         }
 
+        /**
+         * Return the last wall clock time this op was accessed by the app while in the background.
+         */
         public long getLastAccessBackgroundTime() {
             return maxTime(mTimes, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
         }
 
+        /**
+         * @hide
+         */
         public long getLastTimeFor(int uidState) {
             return mTimes[uidState];
         }
 
+        /**
+         * @hide
+         */
         public long getRejectTime() {
             return maxTime(mRejectTimes, 0, _NUM_UID_STATE);
         }
 
+        /**
+         * Return the last wall clock time the app made an attempt to access this op but
+         * was rejected.
+         */
         public long getLastRejectTime() {
             return maxTime(mRejectTimes, 0, _NUM_UID_STATE);
         }
 
+        /**
+         * Return the last wall clock time the app made an attempt to access this op while in
+         * the foreground but was rejected.
+         */
         public long getLastRejectForegroundTime() {
             return maxTime(mRejectTimes, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
         }
 
+        /**
+         * Return the last wall clock time the app made an attempt to access this op while in
+         * the background but was rejected.
+         */
         public long getLastRejectBackgroundTime() {
             return maxTime(mRejectTimes, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
         }
 
+        /**
+         * @hide
+         */
         public long getLastRejectTimeFor(int uidState) {
             return mRejectTimes[uidState];
         }
@@ -1759,6 +1830,7 @@
      * @param ops The set of operations you are interested in, or null if you want all of them.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
     public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, int[] ops) {
         try {
@@ -1931,7 +2003,6 @@
      * @param packageName The name of the application to monitor.
      * @param flags Option flags: any combination of {@link #WATCH_FOREGROUND_CHANGES} or 0.
      * @param callback Where to report changes.
-     * @hide
      */
     public void startWatchingMode(String op, String packageName, int flags,
             final OnOpChangedListener callback) {
@@ -2126,6 +2197,14 @@
      * causing the app to crash).
      * @throws SecurityException If the app has been configured to crash on this op.
      */
+    public int unsafeCheckOp(String op, int uid, String packageName) {
+        return checkOp(strOpToOp(op), uid, packageName);
+    }
+
+    /**
+     * @deprecated Renamed to {@link #unsafeCheckOp(String, int, String)}.
+     */
+    @Deprecated
     public int checkOp(String op, int uid, String packageName) {
         return checkOp(strOpToOp(op), uid, packageName);
     }
@@ -2134,6 +2213,14 @@
      * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it
      * returns {@link #MODE_ERRORED}.
      */
+    public int unsafeCheckOpNoThrow(String op, int uid, String packageName) {
+        return checkOpNoThrow(strOpToOp(op), uid, packageName);
+    }
+
+    /**
+     * @deprecated Renamed to {@link #unsafeCheckOpNoThrow(String, int, String)}.
+     */
+    @Deprecated
     public int checkOpNoThrow(String op, int uid, String packageName) {
         return checkOpNoThrow(strOpToOp(op), uid, packageName);
     }
@@ -2141,7 +2228,6 @@
     /**
      * Like {@link #checkOp} but returns the <em>raw</em> mode associated with the op.
      * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
-     * @hide
      */
     public int unsafeCheckOpRaw(String op, int uid, String packageName) {
         try {
@@ -2401,7 +2487,7 @@
      */
     public int noteProxyOpNoThrow(int op, String proxiedPackageName) {
         try {
-            return mService.noteProxyOperation(op, mContext.getOpPackageName(),
+            return mService.noteProxyOperation(op, Process.myUid(), mContext.getOpPackageName(),
                     Binder.getCallingUid(), proxiedPackageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 24c5d23..f5d5e6e 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -18,12 +18,55 @@
 
 import android.util.SparseIntArray;
 
+import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.TriFunction;
+
 /**
  * App ops service local interface.
  *
  * @hide Only for use within the system server.
  */
 public abstract class AppOpsManagerInternal {
+    /** Interface to override app ops checks via composition */
+    public interface CheckOpsDelegate {
+        /**
+         * Allows overriding check operation behavior.
+         *
+         * @param code The op code to check.
+         * @param uid The UID for which to check.
+         * @param packageName The package for which to check.
+         * @param superImpl The super implementation.
+         * @return The app op check result.
+         */
+        int checkOperation(int code, int uid, String packageName,
+                TriFunction<Integer, Integer, String, Integer> superImpl);
+
+        /**
+         * Allows overriding check audio operation behavior.
+         *
+         * @param code The op code to check.
+         * @param usage The audio op usage.
+         * @param uid The UID for which to check.
+         * @param packageName The package for which to check.
+         * @param superImpl The super implementation.
+         * @return The app op check result.
+         */
+        int checkAudioOperation(int code, int usage, int uid, String packageName,
+                QuadFunction<Integer, Integer, Integer, String, Integer> superImpl);
+
+        /**
+         * Allows overriding note operation behavior.
+         *
+         * @param code The op code to note.
+         * @param uid The UID for which to note.
+         * @param packageName The package for which to note.
+         * @param superImpl The super implementation.
+         * @return The app op note result.
+         */
+        int noteOperation(int code, int uid, String packageName,
+                TriFunction<Integer, Integer, String, Integer> superImpl);
+    }
+
     /**
      * Set the currently configured device and profile owners.  Specifies the package uid (value)
      * that has been configured for each user (key) that has one.  These will be allowed privileged
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 4531f53..6a58d9b 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -17,6 +17,8 @@
 package android.app;
 
 import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ComponentCallbacks;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
@@ -58,13 +60,13 @@
     public LoadedApk mLoadedApk;
 
     public interface ActivityLifecycleCallbacks {
-        void onActivityCreated(Activity activity, Bundle savedInstanceState);
-        void onActivityStarted(Activity activity);
-        void onActivityResumed(Activity activity);
-        void onActivityPaused(Activity activity);
-        void onActivityStopped(Activity activity);
-        void onActivitySaveInstanceState(Activity activity, Bundle outState);
-        void onActivityDestroyed(Activity activity);
+        void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState);
+        void onActivityStarted(@NonNull Activity activity);
+        void onActivityResumed(@NonNull Activity activity);
+        void onActivityPaused(@NonNull Activity activity);
+        void onActivityStopped(@NonNull Activity activity);
+        void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState);
+        void onActivityDestroyed(@NonNull Activity activity);
     }
 
     /**
@@ -213,7 +215,8 @@
         mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
     }
 
-    /* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) {
+    /* package */ void dispatchActivityCreated(@NonNull Activity activity,
+            @Nullable Bundle savedInstanceState) {
         Object[] callbacks = collectActivityLifecycleCallbacks();
         if (callbacks != null) {
             for (int i=0; i<callbacks.length; i++) {
@@ -223,7 +226,7 @@
         }
     }
 
-    /* package */ void dispatchActivityStarted(Activity activity) {
+    /* package */ void dispatchActivityStarted(@NonNull Activity activity) {
         Object[] callbacks = collectActivityLifecycleCallbacks();
         if (callbacks != null) {
             for (int i=0; i<callbacks.length; i++) {
@@ -232,7 +235,7 @@
         }
     }
 
-    /* package */ void dispatchActivityResumed(Activity activity) {
+    /* package */ void dispatchActivityResumed(@NonNull Activity activity) {
         Object[] callbacks = collectActivityLifecycleCallbacks();
         if (callbacks != null) {
             for (int i=0; i<callbacks.length; i++) {
@@ -241,7 +244,7 @@
         }
     }
 
-    /* package */ void dispatchActivityPaused(Activity activity) {
+    /* package */ void dispatchActivityPaused(@NonNull Activity activity) {
         Object[] callbacks = collectActivityLifecycleCallbacks();
         if (callbacks != null) {
             for (int i=0; i<callbacks.length; i++) {
@@ -250,7 +253,7 @@
         }
     }
 
-    /* package */ void dispatchActivityStopped(Activity activity) {
+    /* package */ void dispatchActivityStopped(@NonNull Activity activity) {
         Object[] callbacks = collectActivityLifecycleCallbacks();
         if (callbacks != null) {
             for (int i=0; i<callbacks.length; i++) {
@@ -259,7 +262,8 @@
         }
     }
 
-    /* package */ void dispatchActivitySaveInstanceState(Activity activity, Bundle outState) {
+    /* package */ void dispatchActivitySaveInstanceState(@NonNull Activity activity,
+            @NonNull Bundle outState) {
         Object[] callbacks = collectActivityLifecycleCallbacks();
         if (callbacks != null) {
             for (int i=0; i<callbacks.length; i++) {
@@ -269,7 +273,7 @@
         }
     }
 
-    /* package */ void dispatchActivityDestroyed(Activity activity) {
+    /* package */ void dispatchActivityDestroyed(@NonNull Activity activity) {
         Object[] callbacks = collectActivityLifecycleCallbacks();
         if (callbacks != null) {
             for (int i=0; i<callbacks.length; i++) {
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index e645261..8874554 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -29,6 +29,7 @@
 import android.provider.Settings;
 import android.util.Printer;
 import android.util.Slog;
+
 import com.android.internal.util.FastPrintWriter;
 
 import java.io.PrintWriter;
@@ -333,6 +334,12 @@
         public String stackTrace;
 
         /**
+         * Crash tag for some context.
+         * @hide
+         */
+        public String crashTag;
+
+        /**
          * Create an uninitialized instance of CrashInfo.
          */
         public CrashInfo() {
@@ -416,6 +423,7 @@
             throwMethodName = in.readString();
             throwLineNumber = in.readInt();
             stackTrace = in.readString();
+            crashTag = in.readString();
         }
 
         /**
@@ -430,6 +438,7 @@
             dest.writeString(throwMethodName);
             dest.writeInt(throwLineNumber);
             dest.writeString(stackTrace);
+            dest.writeString(crashTag);
             int total = dest.dataPosition()-start;
             if (Binder.CHECK_PARCEL_SIZE && total > 20*1024) {
                 Slog.d("Error", "ERR: exClass=" + exceptionClassName);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0e44833..09a614c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -73,6 +73,7 @@
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.StrictMode;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -149,15 +150,16 @@
     @Override
     public PackageInfo getPackageInfo(String packageName, int flags)
             throws NameNotFoundException {
-        return getPackageInfoAsUser(packageName, flags, mContext.getUserId());
+        return getPackageInfoAsUser(packageName, flags, getUserId());
     }
 
     @Override
     public PackageInfo getPackageInfo(VersionedPackage versionedPackage, int flags)
             throws NameNotFoundException {
+        final int userId = getUserId();
         try {
-            PackageInfo pi = mPM.getPackageInfoVersioned(versionedPackage, flags,
-                    mContext.getUserId());
+            PackageInfo pi = mPM.getPackageInfoVersioned(versionedPackage,
+                    updateFlagsForPackage(flags, userId), userId);
             if (pi != null) {
                 return pi;
             }
@@ -171,7 +173,8 @@
     public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
             throws NameNotFoundException {
         try {
-            PackageInfo pi = mPM.getPackageInfo(packageName, flags, userId);
+            PackageInfo pi = mPM.getPackageInfo(packageName,
+                    updateFlagsForPackage(flags, userId), userId);
             if (pi != null) {
                 return pi;
             }
@@ -262,8 +265,10 @@
     @Override
     public int[] getPackageGids(String packageName, int flags)
             throws NameNotFoundException {
+        final int userId = getUserId();
         try {
-            int[] gids = mPM.getPackageGids(packageName, flags, mContext.getUserId());
+            int[] gids = mPM.getPackageGids(packageName,
+                    updateFlagsForPackage(flags, userId), userId);
             if (gids != null) {
                 return gids;
             }
@@ -276,7 +281,7 @@
 
     @Override
     public int getPackageUid(String packageName, int flags) throws NameNotFoundException {
-        return getPackageUidAsUser(packageName, flags, mContext.getUserId());
+        return getPackageUidAsUser(packageName, flags, getUserId());
     }
 
     @Override
@@ -288,7 +293,8 @@
     public int getPackageUidAsUser(String packageName, int flags, int userId)
             throws NameNotFoundException {
         try {
-            int uid = mPM.getPackageUid(packageName, flags, userId);
+            int uid = mPM.getPackageUid(packageName,
+                    updateFlagsForPackage(flags, userId), userId);
             if (uid >= 0) {
                 return uid;
             }
@@ -336,9 +342,15 @@
     }
 
     @Override
-    public boolean isPermissionReviewModeEnabled() {
+    public boolean arePermissionsIndividuallyControlled() {
         return mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_permissionReviewRequired);
+                com.android.internal.R.bool.config_permissionsIndividuallyControlled);
+    }
+
+    @Override
+    public boolean isWirelessConsentModeEnabled() {
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_wirelessConsentRequired);
     }
 
     @Override
@@ -374,14 +386,15 @@
     @Override
     public ApplicationInfo getApplicationInfo(String packageName, int flags)
             throws NameNotFoundException {
-        return getApplicationInfoAsUser(packageName, flags, mContext.getUserId());
+        return getApplicationInfoAsUser(packageName, flags, getUserId());
     }
 
     @Override
     public ApplicationInfo getApplicationInfoAsUser(String packageName, int flags, int userId)
             throws NameNotFoundException {
         try {
-            ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags, userId);
+            ApplicationInfo ai = mPM.getApplicationInfo(packageName,
+                    updateFlagsForApplication(flags, userId), userId);
             if (ai != null) {
                 // This is a temporary hack. Callers must use
                 // createPackageContext(packageName).getApplicationInfo() to
@@ -423,8 +436,10 @@
     @Override
     public ActivityInfo getActivityInfo(ComponentName className, int flags)
             throws NameNotFoundException {
+        final int userId = getUserId();
         try {
-            ActivityInfo ai = mPM.getActivityInfo(className, flags, mContext.getUserId());
+            ActivityInfo ai = mPM.getActivityInfo(className,
+                    updateFlagsForComponent(flags, userId, null), userId);
             if (ai != null) {
                 return ai;
             }
@@ -438,8 +453,10 @@
     @Override
     public ActivityInfo getReceiverInfo(ComponentName className, int flags)
             throws NameNotFoundException {
+        final int userId = getUserId();
         try {
-            ActivityInfo ai = mPM.getReceiverInfo(className, flags, mContext.getUserId());
+            ActivityInfo ai = mPM.getReceiverInfo(className,
+                    updateFlagsForComponent(flags, userId, null), userId);
             if (ai != null) {
                 return ai;
             }
@@ -453,8 +470,10 @@
     @Override
     public ServiceInfo getServiceInfo(ComponentName className, int flags)
             throws NameNotFoundException {
+        final int userId = getUserId();
         try {
-            ServiceInfo si = mPM.getServiceInfo(className, flags, mContext.getUserId());
+            ServiceInfo si = mPM.getServiceInfo(className,
+                    updateFlagsForComponent(flags, userId, null), userId);
             if (si != null) {
                 return si;
             }
@@ -468,8 +487,10 @@
     @Override
     public ProviderInfo getProviderInfo(ComponentName className, int flags)
             throws NameNotFoundException {
+        final int userId = getUserId();
         try {
-            ProviderInfo pi = mPM.getProviderInfo(className, flags, mContext.getUserId());
+            ProviderInfo pi = mPM.getProviderInfo(className,
+                    updateFlagsForComponent(flags, userId, null), userId);
             if (pi != null) {
                 return pi;
             }
@@ -492,7 +513,7 @@
     /** @hide */
     @Override
     public @NonNull List<SharedLibraryInfo> getSharedLibraries(int flags) {
-        return getSharedLibrariesAsUser(flags, mContext.getUserId());
+        return getSharedLibrariesAsUser(flags, getUserId());
     }
 
     /** @hide */
@@ -535,7 +556,7 @@
     @Override
     public ChangedPackages getChangedPackages(int sequenceNumber) {
         try {
-            return mPM.getChangedPackages(sequenceNumber, mContext.getUserId());
+            return mPM.getChangedPackages(sequenceNumber, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -578,7 +599,7 @@
     @Override
     public int checkPermission(String permName, String pkgName) {
         try {
-            return mPM.checkPermission(permName, pkgName, mContext.getUserId());
+            return mPM.checkPermission(permName, pkgName, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -587,7 +608,7 @@
     @Override
     public boolean isPermissionRevokedByPolicy(String permName, String pkgName) {
         try {
-            return mPM.isPermissionRevokedByPolicy(permName, pkgName, mContext.getUserId());
+            return mPM.isPermissionRevokedByPolicy(permName, pkgName, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -681,7 +702,7 @@
     public boolean shouldShowRequestPermissionRationale(String permission) {
         try {
             return mPM.shouldShowRequestPermissionRationale(permission,
-                    mContext.getPackageName(), mContext.getUserId());
+                    mContext.getPackageName(), getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -769,7 +790,7 @@
     @SuppressWarnings("unchecked")
     @Override
     public List<PackageInfo> getInstalledPackages(int flags) {
-        return getInstalledPackagesAsUser(flags, mContext.getUserId());
+        return getInstalledPackagesAsUser(flags, getUserId());
     }
 
     /** @hide */
@@ -778,7 +799,7 @@
     public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
         try {
             ParceledListSlice<PackageInfo> parceledList =
-                    mPM.getInstalledPackages(flags, userId);
+                    mPM.getInstalledPackages(updateFlagsForPackage(flags, userId), userId);
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -792,10 +813,11 @@
     @Override
     public List<PackageInfo> getPackagesHoldingPermissions(
             String[] permissions, int flags) {
-        final int userId = mContext.getUserId();
+        final int userId = getUserId();
         try {
             ParceledListSlice<PackageInfo> parceledList =
-                    mPM.getPackagesHoldingPermissions(permissions, flags, userId);
+                    mPM.getPackagesHoldingPermissions(permissions,
+                            updateFlagsForPackage(flags, userId), userId);
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -808,7 +830,7 @@
     @SuppressWarnings("unchecked")
     @Override
     public List<ApplicationInfo> getInstalledApplications(int flags) {
-        return getInstalledApplicationsAsUser(flags, mContext.getUserId());
+        return getInstalledApplicationsAsUser(flags, getUserId());
     }
 
     /** @hide */
@@ -817,7 +839,7 @@
     public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
         try {
             ParceledListSlice<ApplicationInfo> parceledList =
-                    mPM.getInstalledApplications(flags, userId);
+                    mPM.getInstalledApplications(updateFlagsForApplication(flags, userId), userId);
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -832,8 +854,7 @@
     @Override
     public List<InstantAppInfo> getInstantApps() {
         try {
-            ParceledListSlice<InstantAppInfo> slice =
-                    mPM.getInstantApps(mContext.getUserId());
+            ParceledListSlice<InstantAppInfo> slice = mPM.getInstantApps(getUserId());
             if (slice != null) {
                 return slice.getList();
             }
@@ -847,8 +868,7 @@
     @Override
     public Drawable getInstantAppIcon(String packageName) {
         try {
-            Bitmap bitmap = mPM.getInstantAppIcon(
-                    packageName, mContext.getUserId());
+            Bitmap bitmap = mPM.getInstantAppIcon(packageName, getUserId());
             if (bitmap != null) {
                 return new BitmapDrawable(null, bitmap);
             }
@@ -866,7 +886,7 @@
     @Override
     public boolean isInstantApp(String packageName) {
         try {
-            return mPM.isInstantApp(packageName, mContext.getUserId());
+            return mPM.isInstantApp(packageName, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -886,8 +906,7 @@
     @Override
     public @NonNull byte[] getInstantAppCookie() {
         try {
-            final byte[] cookie = mPM.getInstantAppCookie(
-                    mContext.getPackageName(), mContext.getUserId());
+            final byte[] cookie = mPM.getInstantAppCookie(mContext.getPackageName(), getUserId());
             if (cookie != null) {
                 return cookie;
             } else {
@@ -910,8 +929,7 @@
                     + getInstantAppCookieMaxBytes());
         }
         try {
-            mPM.setInstantAppCookie(mContext.getPackageName(),
-                    cookie, mContext.getUserId());
+            mPM.setInstantAppCookie(mContext.getPackageName(), cookie, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -920,8 +938,7 @@
     @Override
     public boolean setInstantAppCookie(@NonNull byte[] cookie) {
         try {
-            return mPM.setInstantAppCookie(mContext.getPackageName(),
-                    cookie, mContext.getUserId());
+            return mPM.setInstantAppCookie(mContext.getPackageName(), cookie, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -929,7 +946,7 @@
 
     @Override
     public ResolveInfo resolveActivity(Intent intent, int flags) {
-        return resolveActivityAsUser(intent, flags, mContext.getUserId());
+        return resolveActivityAsUser(intent, flags, getUserId());
     }
 
     @Override
@@ -938,7 +955,7 @@
             return mPM.resolveIntent(
                 intent,
                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                flags,
+                updateFlagsForComponent(flags, userId, intent),
                 userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -948,7 +965,7 @@
     @Override
     public List<ResolveInfo> queryIntentActivities(Intent intent,
                                                    int flags) {
-        return queryIntentActivitiesAsUser(intent, flags, mContext.getUserId());
+        return queryIntentActivitiesAsUser(intent, flags, getUserId());
     }
 
     /** @hide Same as above but for a specific user */
@@ -957,10 +974,11 @@
     public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
             int flags, int userId) {
         try {
-            ParceledListSlice<ResolveInfo> parceledList =
-                    mPM.queryIntentActivities(intent,
-                            intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                            flags, userId);
+            ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentActivities(
+                    intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    updateFlagsForComponent(flags, userId, intent),
+                    userId);
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -972,9 +990,9 @@
 
     @Override
     @SuppressWarnings("unchecked")
-    public List<ResolveInfo> queryIntentActivityOptions(
-        ComponentName caller, Intent[] specifics, Intent intent,
-        int flags) {
+    public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, Intent[] specifics,
+            Intent intent, int flags) {
+        final int userId = getUserId();
         final ContentResolver resolver = mContext.getContentResolver();
 
         String[] specificTypes = null;
@@ -995,9 +1013,14 @@
         }
 
         try {
-            ParceledListSlice<ResolveInfo> parceledList =
-                    mPM.queryIntentActivityOptions(caller, specifics, specificTypes, intent,
-                    intent.resolveTypeIfNeeded(resolver), flags, mContext.getUserId());
+            ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentActivityOptions(
+                    caller,
+                    specifics,
+                    specificTypes,
+                    intent,
+                    intent.resolveTypeIfNeeded(resolver),
+                    updateFlagsForComponent(flags, userId, intent),
+                    userId);
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1014,10 +1037,11 @@
     @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) {
         try {
-            ParceledListSlice<ResolveInfo> parceledList =
-                    mPM.queryIntentReceivers(intent,
-                            intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                            flags,  userId);
+            ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentReceivers(
+                    intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    updateFlagsForComponent(flags, userId, intent),
+                    userId);
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1029,7 +1053,7 @@
 
     @Override
     public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
-        return queryBroadcastReceiversAsUser(intent, flags, mContext.getUserId());
+        return queryBroadcastReceiversAsUser(intent, flags, getUserId());
     }
 
     @Override
@@ -1039,7 +1063,7 @@
             return mPM.resolveService(
                 intent,
                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                flags,
+                updateFlagsForComponent(flags, userId, intent),
                 userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1048,17 +1072,18 @@
 
     @Override
     public ResolveInfo resolveService(Intent intent, int flags) {
-        return resolveServiceAsUser(intent, flags, mContext.getUserId());
+        return resolveServiceAsUser(intent, flags, getUserId());
     }
 
     @Override
     @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
         try {
-            ParceledListSlice<ResolveInfo> parceledList =
-                    mPM.queryIntentServices(intent,
+            ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentServices(
+                    intent,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    flags, userId);
+                    updateFlagsForComponent(flags, userId, intent),
+                    userId);
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1070,7 +1095,7 @@
 
     @Override
     public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
-        return queryIntentServicesAsUser(intent, flags, mContext.getUserId());
+        return queryIntentServicesAsUser(intent, flags, getUserId());
     }
 
     @Override
@@ -1078,10 +1103,11 @@
     public List<ResolveInfo> queryIntentContentProvidersAsUser(
             Intent intent, int flags, int userId) {
         try {
-            ParceledListSlice<ResolveInfo> parceledList =
-                    mPM.queryIntentContentProviders(intent,
-                            intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                            flags, userId);
+            ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentContentProviders(
+                    intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    updateFlagsForComponent(flags, userId, intent),
+                    userId);
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1093,19 +1119,20 @@
 
     @Override
     public List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags) {
-        return queryIntentContentProvidersAsUser(intent, flags, mContext.getUserId());
+        return queryIntentContentProvidersAsUser(intent, flags, getUserId());
     }
 
     @Override
     public ProviderInfo resolveContentProvider(String name, int flags) {
-        return resolveContentProviderAsUser(name, flags, mContext.getUserId());
+        return resolveContentProviderAsUser(name, flags, getUserId());
     }
 
     /** @hide **/
     @Override
     public ProviderInfo resolveContentProviderAsUser(String name, int flags, int userId) {
         try {
-            return mPM.resolveContentProvider(name, flags, userId);
+            return mPM.resolveContentProvider(name,
+                    updateFlagsForComponent(flags, userId, null), userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1122,8 +1149,8 @@
     public List<ProviderInfo> queryContentProviders(String processName,
             int uid, int flags, String metaDataKey) {
         try {
-            ParceledListSlice<ProviderInfo> slice =
-                    mPM.queryContentProviders(processName, uid, flags, metaDataKey);
+            ParceledListSlice<ProviderInfo> slice = mPM.queryContentProviders(processName, uid,
+                    updateFlagsForComponent(flags, UserHandle.getUserId(uid), null), metaDataKey);
             return slice != null ? slice.getList() : Collections.<ProviderInfo>emptyList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1517,6 +1544,73 @@
         mPM = pm;
     }
 
+    /**
+     * Update given flags when being used to request {@link PackageInfo}.
+     */
+    private int updateFlagsForPackage(int flags, int userId) {
+        if ((flags & (GET_ACTIVITIES | GET_RECEIVERS | GET_SERVICES | GET_PROVIDERS)) != 0) {
+            // Caller is asking for component details, so they'd better be
+            // asking for specific Direct Boot matching behavior
+            if ((flags & (MATCH_DIRECT_BOOT_UNAWARE
+                    | MATCH_DIRECT_BOOT_AWARE
+                    | MATCH_DIRECT_BOOT_AUTO)) == 0) {
+                onImplicitDirectBoot(userId);
+            }
+        }
+        return flags;
+    }
+
+    /**
+     * Update given flags when being used to request {@link ApplicationInfo}.
+     */
+    private int updateFlagsForApplication(int flags, int userId) {
+        return updateFlagsForPackage(flags, userId);
+    }
+
+    /**
+     * Update given flags when being used to request {@link ComponentInfo}.
+     */
+    private int updateFlagsForComponent(int flags, int userId, Intent intent) {
+        if (intent != null) {
+            if ((intent.getFlags() & Intent.FLAG_DIRECT_BOOT_AUTO) != 0) {
+                flags |= MATCH_DIRECT_BOOT_AUTO;
+            }
+        }
+
+        // Caller is asking for component details, so they'd better be
+        // asking for specific Direct Boot matching behavior
+        if ((flags & (MATCH_DIRECT_BOOT_UNAWARE
+                | MATCH_DIRECT_BOOT_AWARE
+                | MATCH_DIRECT_BOOT_AUTO)) == 0) {
+            onImplicitDirectBoot(userId);
+        }
+        return flags;
+    }
+
+    private void onImplicitDirectBoot(int userId) {
+        // Only report if someone is relying on implicit behavior while the user
+        // is locked; code running when unlocked is going to see both aware and
+        // unaware components.
+        if (StrictMode.vmImplicitDirectBootEnabled()) {
+            // We can cache the unlocked state for the userId we're running as,
+            // since any relocking of that user will always result in our
+            // process being killed to release any CE FDs we're holding onto.
+            if (userId == UserHandle.myUserId()) {
+                if (mUserUnlocked) {
+                    return;
+                } else if (mContext.getSystemService(UserManager.class)
+                        .isUserUnlockingOrUnlocked(userId)) {
+                    mUserUnlocked = true;
+                } else {
+                    StrictMode.onImplicitDirectBoot();
+                }
+            } else if (!mContext.getSystemService(UserManager.class)
+                    .isUserUnlockingOrUnlocked(userId)) {
+                StrictMode.onImplicitDirectBoot();
+            }
+        }
+    }
+
     @Nullable
     private Drawable getCachedIcon(@NonNull ResourceName name) {
         synchronized (sSync) {
@@ -1730,7 +1824,7 @@
     @Override
     public int installExistingPackage(String packageName, int installReason)
             throws NameNotFoundException {
-        return installExistingPackageAsUser(packageName, installReason, mContext.getUserId());
+        return installExistingPackageAsUser(packageName, installReason, getUserId());
     }
 
     @Override
@@ -2089,7 +2183,7 @@
 
     @Override
     public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
-        deletePackageAsUser(packageName, observer, flags, mContext.getUserId());
+        deletePackageAsUser(packageName, observer, flags, getUserId());
     }
 
     @Override
@@ -2107,7 +2201,7 @@
     public void clearApplicationUserData(String packageName,
                                          IPackageDataObserver observer) {
         try {
-            mPM.clearApplicationUserData(packageName, observer, mContext.getUserId());
+            mPM.clearApplicationUserData(packageName, observer, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2158,7 +2252,7 @@
         try {
             return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
                     launcherExtras, dialogMessage, mContext.getOpPackageName(),
-                    mContext.getUserId());
+                    getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2169,7 +2263,7 @@
         final PersistableBundle extras;
         try {
             extras = mPM.getSuspendedPackageAppExtras(mContext.getOpPackageName(),
-                    mContext.getUserId());
+                    getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2189,7 +2283,7 @@
     @Override
     public boolean isPackageSuspended(String packageName) throws NameNotFoundException {
         try {
-            return isPackageSuspendedForUser(packageName, mContext.getUserId());
+            return isPackageSuspendedForUser(packageName, getUserId());
         } catch (IllegalArgumentException ie) {
             throw new NameNotFoundException(packageName);
         }
@@ -2197,7 +2291,7 @@
 
     @Override
     public boolean isPackageSuspended() {
-        return isPackageSuspendedForUser(mContext.getOpPackageName(), mContext.getUserId());
+        return isPackageSuspendedForUser(mContext.getOpPackageName(), getUserId());
     }
 
     /** @hide */
@@ -2247,7 +2341,7 @@
     public void addPreferredActivity(IntentFilter filter,
                                      int match, ComponentName[] set, ComponentName activity) {
         try {
-            mPM.addPreferredActivity(filter, match, set, activity, mContext.getUserId());
+            mPM.addPreferredActivity(filter, match, set, activity, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2267,7 +2361,7 @@
     public void replacePreferredActivity(IntentFilter filter,
                                          int match, ComponentName[] set, ComponentName activity) {
         try {
-            mPM.replacePreferredActivity(filter, match, set, activity, mContext.getUserId());
+            mPM.replacePreferredActivity(filter, match, set, activity, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2316,7 +2410,7 @@
     public void setComponentEnabledSetting(ComponentName componentName,
                                            int newState, int flags) {
         try {
-            mPM.setComponentEnabledSetting(componentName, newState, flags, mContext.getUserId());
+            mPM.setComponentEnabledSetting(componentName, newState, flags, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2325,7 +2419,7 @@
     @Override
     public int getComponentEnabledSetting(ComponentName componentName) {
         try {
-            return mPM.getComponentEnabledSetting(componentName, mContext.getUserId());
+            return mPM.getComponentEnabledSetting(componentName, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2336,7 +2430,7 @@
                                              int newState, int flags) {
         try {
             mPM.setApplicationEnabledSetting(packageName, newState, flags,
-                    mContext.getUserId(), mContext.getOpPackageName());
+                    getUserId(), mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2345,7 +2439,7 @@
     @Override
     public int getApplicationEnabledSetting(String packageName) {
         try {
-            return mPM.getApplicationEnabledSetting(packageName, mContext.getUserId());
+            return mPM.getApplicationEnabledSetting(packageName, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2457,7 +2551,7 @@
             if (mInstaller == null) {
                 try {
                     mInstaller = new PackageInstaller(mPM.getPackageInstaller(),
-                            mContext.getPackageName(), mContext.getUserId());
+                            mContext.getPackageName(), getUserId());
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
@@ -2469,7 +2563,7 @@
     @Override
     public boolean isPackageAvailable(String packageName) {
         try {
-            return mPM.isPackageAvailable(packageName, mContext.getUserId());
+            return mPM.isPackageAvailable(packageName, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2509,7 +2603,7 @@
         if (itemInfo.showUserIcon != UserHandle.USER_NULL) {
             return dr;
         }
-        return getUserBadgedIcon(dr, new UserHandle(mContext.getUserId()));
+        return getUserBadgedIcon(dr, new UserHandle(getUserId()));
     }
 
     /**
@@ -2657,6 +2751,9 @@
     private final ContextImpl mContext;
     private final IPackageManager mPM;
 
+    /** Assume locked until we hear otherwise */
+    private volatile boolean mUserUnlocked = false;
+
     private static final Object sSync = new Object();
     private static ArrayMap<ResourceName, WeakReference<Drawable.ConstantState>> sIconCache
             = new ArrayMap<ResourceName, WeakReference<Drawable.ConstantState>>();
@@ -2701,7 +2798,7 @@
     @Override
     public boolean canRequestPackageInstalls() {
         try {
-            return mPM.canRequestPackageInstalls(mContext.getPackageName(), mContext.getUserId());
+            return mPM.canRequestPackageInstalls(mContext.getPackageName(), getUserId());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -2811,7 +2908,7 @@
     @Override
     public CharSequence getHarmfulAppWarning(String packageName) {
         try {
-            return mPM.getHarmfulAppWarning(packageName, mContext.getUserId());
+            return mPM.getHarmfulAppWarning(packageName, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -2820,7 +2917,7 @@
     @Override
     public void setHarmfulAppWarning(String packageName, CharSequence warning) {
         try {
-            mPM.setHarmfulAppWarning(packageName, warning, mContext.getUserId());
+            mPM.setHarmfulAppWarning(packageName, warning, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index d9c7cf3..b5295d1 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -16,6 +16,7 @@
 package android.app;
 
 import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
 import android.app.servertransaction.PendingTransactionActions;
 import android.app.servertransaction.TransactionExecutor;
 import android.content.Intent;
@@ -29,6 +30,7 @@
 import com.android.internal.content.ReferrerIntent;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * Defines operations that a {@link android.app.servertransaction.ClientTransaction} or its items
@@ -64,6 +66,8 @@
 
     abstract void sendMessage(int what, Object obj);
 
+    /** Get activity instance for the token. */
+    public abstract Activity getActivity(IBinder token);
 
     // Prepare phase related logic and handlers. Methods that inform about about pending changes or
     // do other internal bookkeeping.
@@ -78,6 +82,9 @@
     // Execute phase related logic and handlers. Methods here execute actual lifecycle transactions
     // and deliver callbacks.
 
+    /** Get activity and its corresponding transaction item which are going to destroy. */
+    public abstract Map<IBinder, ClientTransactionItem> getActivitiesToBeDestroyed();
+
     /** Destroy the activity. */
     public abstract void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
             boolean getNonConfigInstance, String reason);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9511786..2eafb32 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -80,6 +80,8 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 
+import dalvik.system.BlockGuard;
+
 import libcore.io.Memory;
 
 import java.io.File;
@@ -923,7 +925,7 @@
     @Override
     public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
         try {
-            ActivityManager.getService().startActivityAsUser(
+            ActivityTaskManager.getService().startActivityAsUser(
                 mMainThread.getApplicationThread(), getBasePackageName(), intent,
                 intent.resolveTypeIfNeeded(getContentResolver()),
                 null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options,
@@ -985,7 +987,7 @@
                 fillInIntent.prepareToLeaveProcess(this);
                 resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
             }
-            int result = ActivityManager.getService()
+            int result = ActivityTaskManager.getService()
                 .startActivityIntentSender(mMainThread.getApplicationThread(),
                         intent != null ? intent.getTarget() : null,
                         intent != null ? intent.getWhitelistToken() : null,
@@ -2521,7 +2523,11 @@
 
     private File makeFilename(File base, String name) {
         if (name.indexOf(File.separatorChar) < 0) {
-            return new File(base, name);
+            final File res = new File(base, name);
+            // We report as filesystem access here to give us the best shot at
+            // detecting apps that will pass the path down to native code.
+            BlockGuard.getVmPolicy().onPathAccess(res.getPath());
+            return res;
         }
         throw new IllegalArgumentException(
                 "File " + name + " contains a path separator");
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index d22f578..16360b3 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -105,7 +105,6 @@
             in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
             int flags, in ProfilerInfo profilerInfo, in Bundle options);
     void unhandledBack();
-
     boolean finishActivity(in IBinder token, int code, in Intent data, int finishTask);
     Intent registerReceiver(in IApplicationThread caller, in String callerPackage,
             in IIntentReceiver receiver, in IntentFilter filter,
@@ -119,25 +118,16 @@
     oneway void finishReceiver(in IBinder who, int resultCode, in String resultData, in Bundle map,
             boolean abortBroadcast, int flags);
     void attachApplication(in IApplicationThread app, long startSeq);
-    oneway void activityIdle(in IBinder token, in Configuration config,
-            in boolean stopProfiling);
-    void activityPaused(in IBinder token);
-    oneway void activityStopped(in IBinder token, in Bundle state,
-            in PersistableBundle persistentState, in CharSequence description);
-    String getCallingPackage(in IBinder token);
-    ComponentName getCallingActivity(in IBinder token);
     List<ActivityManager.RunningTaskInfo> getTasks(int maxNum);
     List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, int ignoreActivityType,
             int ignoreWindowingMode);
     void moveTaskToFront(int task, int flags, in Bundle options);
-    void moveTaskBackwards(int task);
     int getTaskForActivity(in IBinder token, in boolean onlyRoot);
     ContentProviderHolder getContentProvider(in IApplicationThread caller,
             in String name, int userId, boolean stable);
     void publishContentProviders(in IApplicationThread caller,
             in List<ContentProviderHolder> providers);
     boolean refContentProvider(in IBinder connection, int stableDelta, int unstableDelta);
-    void finishSubActivity(in IBinder token, in String resultWho, int requestCode);
     PendingIntent getRunningServiceControlPanel(in ComponentName service);
     ComponentName startService(in IApplicationThread caller, in Intent service,
             in String resolvedType, boolean requireForeground, in String callingPackage, int userId);
@@ -148,7 +138,6 @@
             in String callingPackage, int userId);
     boolean unbindService(in IServiceConnection connection);
     void publishService(in IBinder token, in Intent intent, in IBinder service);
-    void activityResumed(in IBinder token);
     void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
     void setAgentApp(in String packageName, @nullable String agent);
     void setAlwaysFinish(boolean enabled);
@@ -174,8 +163,6 @@
      */
     boolean updateConfiguration(in Configuration values);
     boolean stopServiceToken(in ComponentName className, in IBinder token, int startId);
-    ComponentName getActivityClassForToken(in IBinder token);
-    String getPackageForToken(in IBinder token);
     void setProcessLimit(int max);
     int getProcessLimit();
     int checkPermission(in String permission, int pid, int uid);
@@ -195,7 +182,6 @@
 
     ParceledListSlice getRecentTasks(int maxNum, int flags, int userId);
     oneway void serviceDoneExecuting(in IBinder token, int type, int startId, int res);
-    oneway void activityDestroyed(in IBinder token);
     IIntentSender getIntentSender(int type, in String packageName, in IBinder token,
             in String resultWho, int requestCode, in Intent[] intents, in String[] resolvedTypes,
             int flags, in Bundle options, int userId);
@@ -204,13 +190,10 @@
     void registerIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
     void unregisterIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
     void enterSafeMode();
-    boolean startNextMatchingActivity(in IBinder callingActivity,
-            in Intent intent, in Bundle options);
     void noteWakeupAlarm(in IIntentSender sender, in WorkSource workSource, int sourceUid,
             in String sourcePkg, in String tag);
     void removeContentProvider(in IBinder connection, boolean stable);
     void setRequestedOrientation(in IBinder token, int requestedOrientation);
-    int getRequestedOrientation(in IBinder token);
     void unbindFinished(in IBinder token, in Intent service, boolean doRebind);
     void setProcessImportant(in IBinder token, int pid, boolean isForeground, String reason);
     void setServiceForeground(in ComponentName className, in IBinder token,
@@ -223,7 +206,6 @@
     void forceStopPackage(in String packageName, int userId);
     boolean killPids(in int[] pids, in String reason, boolean secure);
     List<ActivityManager.RunningServiceInfo> getServices(int maxNum, int flags);
-    ActivityManager.TaskDescription getTaskDescription(int taskId);
     // Retrieve running application processes in the system
     List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses();
     // Get device configuration
@@ -246,64 +228,31 @@
     void closeSystemDialogs(in String reason);
     Debug.MemoryInfo[] getProcessMemoryInfo(in int[] pids);
     void killApplicationProcess(in String processName, int uid);
-    int startActivityIntentSender(in IApplicationThread caller,
-            in IIntentSender target, in IBinder whitelistToken, in Intent fillInIntent,
-            in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
-            int flagsMask, int flagsValues, in Bundle options);
-    void overridePendingTransition(in IBinder token, in String packageName,
-            int enterAnim, int exitAnim);
     // Special low-level communication with activity manager.
     boolean handleApplicationWtf(in IBinder app, in String tag, boolean system,
             in ApplicationErrorReport.ParcelableCrashInfo crashInfo);
     void killBackgroundProcesses(in String packageName, int userId);
     boolean isUserAMonkey();
-    WaitResult startActivityAndWait(in IApplicationThread caller, in String callingPackage,
-            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
-            int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
-            int userId);
-    boolean willActivityBeVisible(in IBinder token);
-    int startActivityWithConfig(in IApplicationThread caller, in String callingPackage,
-            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
-            int requestCode, int startFlags, in Configuration newConfig,
-            in Bundle options, int userId);
     // Retrieve info of applications installed on external media that are currently
     // running.
     List<ApplicationInfo> getRunningExternalApplications();
     void finishHeavyWeightApp();
-    // A StrictMode violation to be handled.  The violationMask is a
-    // subset of the original StrictMode policy bitmask, with only the
-    // bit violated and penalty bits to be executed by the
-    // ActivityManagerService remaining set.
-    void handleApplicationStrictModeViolation(in IBinder app, int violationMask,
+    // A StrictMode violation to be handled.
+    void handleApplicationStrictModeViolation(in IBinder app, int penaltyMask,
             in StrictMode.ViolationInfo crashInfo);
-    boolean isImmersive(in IBinder token);
-    void setImmersive(in IBinder token, boolean immersive);
     boolean isTopActivityImmersive();
     void crashApplication(int uid, int initialPid, in String packageName, int userId, in String message);
     String getProviderMimeType(in Uri uri, int userId);
-    IBinder newUriPermissionOwner(in String name);
-    void grantUriPermissionFromOwner(in IBinder owner, int fromUid, in String targetPkg,
-            in Uri uri, int mode, int sourceUserId, int targetUserId);
-    void revokeUriPermissionFromOwner(in IBinder owner, in Uri uri, int mode, int userId);
-    int checkGrantUriPermission(int callingUid, in String targetPkg, in Uri uri,
-            int modeFlags, int userId);
     // Cause the specified process to dump the specified heap.
     boolean dumpHeap(in String process, int userId, boolean managed, boolean mallocInfo,
             boolean runGc, in String path, in ParcelFileDescriptor fd,
             in RemoteCallback finishCallback);
-    int startActivities(in IApplicationThread caller, in String callingPackage,
-            in Intent[] intents, in String[] resolvedTypes, in IBinder resultTo,
-            in Bundle options, int userId);
     boolean isUserRunning(int userid, int flags);
-    oneway void activitySlept(in IBinder token);
-    int getFrontActivityScreenCompatMode();
-    void setFrontActivityScreenCompatMode(int mode);
     int getPackageScreenCompatMode(in String packageName);
     void setPackageScreenCompatMode(in String packageName, int mode);
     boolean getPackageAskScreenCompat(in String packageName);
     void setPackageAskScreenCompat(in String packageName, boolean ask);
     boolean switchUser(int userid);
-    void setFocusedTask(int taskId);
     boolean removeTask(int taskId);
     void registerProcessObserver(in IProcessObserver observer);
     void unregisterProcessObserver(in IProcessObserver observer);
@@ -313,15 +262,12 @@
     void showBootMessage(in CharSequence msg, boolean always);
     void killAllBackgroundProcesses();
     ContentProviderHolder getContentProviderExternal(in String name, int userId,
-            in IBinder token);
+            in IBinder token, String tag);
     void removeContentProviderExternal(in String name, in IBinder token);
     // Get memory information about the calling process.
     void getMyMemoryState(out ActivityManager.RunningAppProcessInfo outInfo);
     boolean killProcessesBelowForeground(in String reason);
     UserInfo getCurrentUser();
-    boolean shouldUpRecreateTask(in IBinder token, in String destAffinity);
-    boolean navigateUpTo(in IBinder token, in Intent target, int resultCode,
-            in Intent resultData);
     /**
      * Informs ActivityManagerService that the keyguard is showing.
      *
@@ -333,7 +279,6 @@
      */
     void setLockScreenShown(boolean showingKeyguard, boolean showingAod,
             int secondaryDisplayShowing);
-    boolean finishActivityAffinity(in IBinder token);
     // This is not public because you need to be very careful in how you
     // manage your activity to make sure it is always the uid you expect.
     int getLaunchedFromUid(in IBinder activityToken);
@@ -380,12 +325,8 @@
      */
     void requestWifiBugReport(in String shareTitle, in String shareDescription);
 
-    long inputDispatchingTimedOut(int pid, boolean aboveSystem, in String reason);
     void clearPendingBackup();
     Intent getIntentForIntentSender(in IIntentSender sender);
-    Bundle getAssistContextExtras(int requestType);
-    void reportAssistContextExtras(in IBinder token, in Bundle extras,
-            in AssistStructure structure, in AssistContent content, in Uri referrer);
     // This is not public because you need to be very careful in how you
     // manage your activity to make sure it is always the uid you expect.
     String getLaunchedFromPackage(in IBinder activityToken);
@@ -393,14 +334,7 @@
     void setUserIsMonkey(boolean monkey);
     void hang(in IBinder who, boolean allowRestart);
 
-    /**
-     * Sets the windowing mode for a specific task. Only works on tasks of type
-     * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}
-     * @param taskId The id of the task to set the windowing mode for.
-     * @param windowingMode The windowing mode to set for the task.
-     * @param toTop If the task should be moved to the top once the windowing mode changes.
-     */
-    void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
+    List<ActivityManager.StackInfo> getAllStackInfos();
     void moveTaskToStack(int taskId, int stackId, boolean toTop);
     /**
      * Resizes the input stack id to the given bounds.
@@ -418,98 +352,43 @@
      */
     void resizeStack(int stackId, in Rect bounds, boolean allowResizeInDockedMode,
             boolean preserveWindows, boolean animate, int animationDuration);
-    List<ActivityManager.StackInfo> getAllStackInfos();
     void setFocusedStack(int stackId);
     ActivityManager.StackInfo getFocusedStackInfo();
-    ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType);
-    boolean convertFromTranslucent(in IBinder token);
-    boolean convertToTranslucent(in IBinder token, in Bundle options);
-    void notifyActivityDrawn(in IBinder token);
-    void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle);
     void restart();
     void performIdleMaintenance();
-    void takePersistableUriPermission(in Uri uri, int modeFlags, String toPackage, int userId);
-    void releasePersistableUriPermission(in Uri uri, int modeFlags, String toPackage, int userId);
-    ParceledListSlice getPersistedUriPermissions(in String packageName, boolean incoming);
     void appNotRespondingViaProvider(in IBinder connection);
     Rect getTaskBounds(int taskId);
-    int getActivityDisplayId(in IBinder activityToken);
     boolean setProcessMemoryTrimLevel(in String process, int uid, int level);
 
 
     // Start of L transactions
     String getTagForIntentSender(in IIntentSender sender, in String prefix);
     boolean startUserInBackground(int userid);
-    void startLockTaskModeByToken(in IBinder token);
-    void stopLockTaskModeByToken(in IBinder token);
     boolean isInLockTaskMode();
-    void setTaskDescription(in IBinder token, in ActivityManager.TaskDescription values);
-    int startVoiceActivity(in String callingPackage, int callingPid, int callingUid,
-            in Intent intent, in String resolvedType, in IVoiceInteractionSession session,
-            in IVoiceInteractor interactor, int flags, in ProfilerInfo profilerInfo,
-            in Bundle options, int userId);
-    int startAssistantActivity(in String callingPackage, int callingPid, int callingUid,
-            in Intent intent, in String resolvedType, in Bundle options, int userId);
     void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver,
             in IRecentsAnimationRunner recentsAnimationRunner);
     void cancelRecentsAnimation(boolean restoreHomeStackPosition);
     int startActivityFromRecents(int taskId, in Bundle options);
-    Bundle getActivityOptions(in IBinder token);
-    List<IBinder> getAppTasks(in String callingPackage);
     void startSystemLockTaskMode(int taskId);
-    void stopSystemLockTaskMode();
-    void finishVoiceTask(in IVoiceInteractionSession session);
     boolean isTopOfTask(in IBinder token);
-    void notifyLaunchTaskBehindComplete(in IBinder token);
-    void notifyEnterAnimationComplete(in IBinder token);
-    int startActivityAsCaller(in IApplicationThread caller, in String callingPackage,
-            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
-            int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
-            boolean ignoreTargetSecurity, int userId);
-    int addAppTask(in IBinder activityToken, in Intent intent,
-            in ActivityManager.TaskDescription description, in Bitmap thumbnail);
-    Point getAppTaskThumbnailSize();
-    boolean releaseActivityInstance(in IBinder token);
-    void releaseSomeActivities(in IApplicationThread app);
     void bootAnimationComplete();
-    Bitmap getTaskDescriptionIcon(in String filename, int userId);
-    boolean launchAssistIntent(in Intent intent, int requestType, in String hint, int userHandle,
-            in Bundle args);
-    void startInPlaceAnimationOnFrontMostApplication(in Bundle opts);
     int checkPermissionWithToken(in String permission, int pid, int uid,
             in IBinder callerToken);
     void registerTaskStackListener(in ITaskStackListener listener);
     void unregisterTaskStackListener(in ITaskStackListener listener);
-
     void notifyCleartextNetwork(int uid, in byte[] firstPacket);
-    int createStackOnDisplay(int displayId);
     void setTaskResizeable(int taskId, int resizeableMode);
-    boolean requestAssistContextExtras(int requestType, in IAssistDataReceiver receiver,
-            in Bundle receiverExtras, in IBinder activityToken,
-            boolean focused, boolean newSessionId);
     void resizeTask(int taskId, in Rect bounds, int resizeMode);
     int getLockTaskModeState();
     void setDumpHeapDebugLimit(in String processName, int uid, long maxMemSize,
             in String reportPackage);
     void dumpHeapFinished(in String path);
-    void setVoiceKeepAwake(in IVoiceInteractionSession session, boolean keepAwake);
     void updateLockTaskPackages(int userId, in String[] packages);
     void noteAlarmStart(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String tag);
     void noteAlarmFinish(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String tag);
     int getPackageProcessState(in String packageName, in String callingPackage);
-    oneway void showLockTaskEscapeMessage(in IBinder token);
     void updateDeviceOwner(in String packageName);
-    /**
-     * Notify the system that the keyguard is going away.
-     *
-     * @param flags See {@link android.view.WindowManagerPolicyConstants#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
-     *              etc.
-     */
-    void keyguardGoingAway(int flags);
     int getUidProcessState(int uid, in String callingPackage);
-    boolean isAssistDataAllowedOnCurrentActivity();
-    boolean showAssistFromActivity(in IBinder token, in Bundle args);
-    boolean isRootVoiceInteraction(in IBinder token);
 
 
     // Start of N transactions
@@ -524,37 +403,12 @@
      * different stack.
      */
     void positionTaskInStack(int taskId, int stackId, int position);
-    void exitFreeformMode(in IBinder token);
-    void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
-            in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
-    boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
-            boolean animate, in Rect initialBounds, boolean showRecents);
-    /**
-     * Dismisses split-screen multi-window mode.
-     * {@param toTop} If true the current primary split-screen stack will be placed or left on top.
-     */
-    void dismissSplitScreenMode(boolean toTop);
-    /**
-     * Dismisses PiP
-     * @param animate True if the dismissal should be animated.
-     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
-     *                          default animation duration should be used.
-     */
-    void dismissPip(boolean animate, int animationDuration);
     void suppressResizeConfigChanges(boolean suppress);
-    void moveTasksToFullscreenStack(int fromStackId, boolean onTop);
     boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds);
     boolean isAppStartModeDisabled(int uid, in String packageName);
     boolean unlockUser(int userid, in byte[] token, in byte[] secret,
             in IProgressListener listener);
-    boolean isInMultiWindowMode(in IBinder token);
-    boolean isInPictureInPictureMode(in IBinder token);
     void killPackageDependents(in String packageName, int userId);
-    boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params);
-    void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params);
-    int getMaxNumPictureInPictureActions(in IBinder token);
-    void activityRelaunched(in IBinder token);
-    IBinder getUriPermissionOwnerForActivity(in IBinder activityToken);
     /**
      * Resizes the docked stack, and all other stacks as the result of the dock stack bounds change.
      *
@@ -577,44 +431,10 @@
     void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds,
             in Rect tempDockedTaskInsetBounds,
             in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
-    /**
-     * Sets whether we are currently in an interactive split screen resize operation where we
-     * are changing the docked stack size.
-     */
-    void setSplitScreenResizing(boolean resizing);
-    int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
-    // Gets the URI permissions granted to an arbitrary package (or all packages if null)
-    // NOTE: this is different from getPersistedUriPermissions(), which returns the URIs the package
-    // granted to another packages (instead of those granted to it).
-    ParceledListSlice getGrantedUriPermissions(in String packageName, int userId);
-    // Clears the URI permissions granted to an arbitrary package.
-    void clearGrantedUriPermissions(in String packageName, int userId);
     boolean isAppForeground(int uid);
-    void startLocalVoiceInteraction(in IBinder token, in Bundle options);
-    void stopLocalVoiceInteraction(in IBinder token);
-    boolean supportsLocalVoiceInteraction();
-    void notifyPinnedStackAnimationStarted();
-    void notifyPinnedStackAnimationEnded();
     void removeStack(int stackId);
-    /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
-     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
-     */
-    void removeStacksInWindowingModes(in int[] windowingModes);
-    /** Removes stack of the activity types from the system. */
-    void removeStacksWithActivityTypes(in int[] activityTypes);
     void makePackageIdle(String packageName, int userId);
     int getMemoryTrimLevel();
-    /**
-     * Resizes the pinned stack.
-     *
-     * @param pinnedBounds The bounds for the pinned stack.
-     * @param tempPinnedTaskBounds The temporary bounds for the tasks in the pinned stack, which
-     *                             might be different from the stack bounds to allow more
-     *                             flexibility while resizing, or {@code null} if they should be the
-     *                             same as the stack bounds.
-     */
-    void resizePinnedStack(in Rect pinnedBounds, in Rect tempPinnedTaskBounds);
     boolean isVrModePackageEnabled(in ComponentName packageName);
     void notifyLockedProfile(int userId);
     void startConfirmDeviceCredentialIntent(in Intent intent, in Bundle options);
@@ -625,7 +445,6 @@
     boolean isBackgroundRestricted(in String packageName);
 
     // Start of N MR1 transactions
-    void setVrThread(int tid);
     void setRenderThread(int tid);
     /**
      * Lets activity manager know whether the calling process is currently showing "top-level" UI
@@ -638,25 +457,9 @@
     void setHasTopUi(boolean hasTopUi);
 
     // Start of O transactions
-    /**
-     * Updates override configuration applied to specific display.
-     * @param values Update values for display configuration. If null is passed it will request the
-     *               Window Manager to compute new config for the specified display.
-     * @param displayId Id of the display to apply the config to.
-     * @throws RemoteException
-     * @return Returns true if the configuration was updated.
-     */
-    boolean updateDisplayOverrideConfiguration(in Configuration values, int displayId);
-    void moveStackToDisplay(int stackId, int displayId);
-    boolean requestAutofillData(in IAssistDataReceiver receiver, in Bundle receiverExtras,
-            in IBinder activityToken, int flags);
-    void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback,
-            in CharSequence message);
     int restartUserInBackground(int userId);
-
     /** Cancels the window transitions for the given task. */
     void cancelTaskWindowTransition(int taskId);
-
     /**
      * @param taskId the id of the task to retrieve the sAutoapshots for
      * @param reducedResolution if set, if the snapshot needs to be loaded from disk, this will load
@@ -664,38 +467,16 @@
      * @return a graphic buffer representing a screenshot of a task
      */
     ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution);
-
     void scheduleApplicationInfoChanged(in List<String> packageNames, int userId);
     void setPersistentVrThread(int tid);
 
     void waitForNetworkStateUpdate(long procStateSeq);
-
-    /**
-     * See {@link android.app.Activity#setDisablePreviewScreenshots}
-     */
-    void setDisablePreviewScreenshots(IBinder token, boolean disable);
-
-    /**
-     * Return the user id of last resumed activity.
-     */
-    int getLastResumedActivityUserId();
-
     /**
      * Add a bare uid to the background restrictions whitelist.  Only the system uid may call this.
      */
     void backgroundWhitelistUid(int uid);
 
     // Start of P transactions
-    void updateLockTaskFeatures(int userId, int flags);
-
-    // WARNING: when these transactions are updated, check if they are any callers on the native
-    // side. If so, make sure they are using the correct transaction ids and arguments.
-    // If a transaction which will also be used on the native side is being inserted, add it
-    // alongside with other transactions of this kind at the top of this file.
-
-    void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
-    void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
-
     /**
      *  Similar to {@link #startUserInBackground(int userId), but with a listener to report
      *  user unlock progress.
@@ -703,17 +484,17 @@
     boolean startUserInBackgroundWithListener(int userid, IProgressListener unlockProgressListener);
 
     /**
-     * Registers remote animations for a specific activity.
+     * Method for the shell UID to start deletating its permission identity to an
+     * active instrumenation. The shell can delegate permissions only to one active
+     * instrumentation at a time. An active instrumentation is one running and
+     * started from the shell.
      */
-    void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition);
+    void startDelegateShellPermissionIdentity(int uid);
 
     /**
-     * Registers a remote animation to be run for all activity starts from a certain package during
-     * a short predefined amount of time.
+     * Method for the shell UID to stop deletating its permission identity to an
+     * active instrumenation. An active instrumentation is one running and
+     * started from the shell.
      */
-    void registerRemoteAnimationForNextActivityStart(in String packageName,
-           in RemoteAnimationAdapter adapter);
-
-    /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
-    void alwaysShowUnsupportedCompileSdkWarning(in ComponentName activity);
+    void stopDelegateShellPermissionIdentity();
 }
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
new file mode 100644
index 0000000..46664c6
--- /dev/null
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -0,0 +1,425 @@
+/*
+ * 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.app;
+
+import android.app.ActivityManager;
+import android.app.ApplicationErrorReport;
+import android.app.ContentProviderHolder;
+import android.app.GrantedUriPermission;
+import android.app.IApplicationThread;
+import android.app.IActivityController;
+import android.app.IAppTask;
+import android.app.IAssistDataReceiver;
+import android.app.IInstrumentationWatcher;
+import android.app.IProcessObserver;
+import android.app.IServiceConnection;
+import android.app.IStopUserCallback;
+import android.app.ITaskStackListener;
+import android.app.IUiAutomationConnection;
+import android.app.IUidObserver;
+import android.app.IUserSwitchObserver;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.PictureInPictureParams;
+import android.app.ProfilerInfo;
+import android.app.WaitResult;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
+import android.content.ComponentName;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ProviderInfo;
+import android.content.pm.UserInfo;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.IBinder;
+import android.os.IProgressListener;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.StrictMode;
+import android.os.WorkSource;
+import android.service.voice.IVoiceInteractionSession;
+import android.view.IRecentsAnimationRunner;
+import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationAdapter;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.policy.IKeyguardDismissCallback;
+
+import java.util.List;
+
+/**
+ * System private API for talking with the activity task manager that handles how activities are
+ * managed on screen.
+ *
+ * {@hide}
+ */
+interface IActivityTaskManager {
+    int startActivity(in IApplicationThread caller, in String callingPackage, in Intent intent,
+            in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
+            int flags, in ProfilerInfo profilerInfo, in Bundle options);
+    int startActivities(in IApplicationThread caller, in String callingPackage,
+            in Intent[] intents, in String[] resolvedTypes, in IBinder resultTo,
+            in Bundle options, int userId);
+    int startActivityAsUser(in IApplicationThread caller, in String callingPackage,
+            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
+            int requestCode, int flags, in ProfilerInfo profilerInfo,
+            in Bundle options, int userId);
+    boolean startNextMatchingActivity(in IBinder callingActivity,
+            in Intent intent, in Bundle options);
+    int startActivityIntentSender(in IApplicationThread caller,
+            in IIntentSender target, in IBinder whitelistToken, in Intent fillInIntent,
+            in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
+            int flagsMask, int flagsValues, in Bundle options);
+    WaitResult startActivityAndWait(in IApplicationThread caller, in String callingPackage,
+            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
+            int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
+            int userId);
+    int startActivityWithConfig(in IApplicationThread caller, in String callingPackage,
+            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
+            int requestCode, int startFlags, in Configuration newConfig,
+            in Bundle options, int userId);
+    int startVoiceActivity(in String callingPackage, int callingPid, int callingUid,
+            in Intent intent, in String resolvedType, in IVoiceInteractionSession session,
+            in IVoiceInteractor interactor, int flags, in ProfilerInfo profilerInfo,
+            in Bundle options, int userId);
+    int startAssistantActivity(in String callingPackage, int callingPid, int callingUid,
+            in Intent intent, in String resolvedType, in Bundle options, int userId);
+    void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver,
+            in IRecentsAnimationRunner recentsAnimationRunner);
+    int startActivityFromRecents(int taskId, in Bundle options);
+    int startActivityAsCaller(in IApplicationThread caller, in String callingPackage,
+            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
+            int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
+            boolean ignoreTargetSecurity, int userId);
+
+    void unhandledBack();
+    boolean finishActivity(in IBinder token, int code, in Intent data, int finishTask);
+    boolean finishActivityAffinity(in IBinder token);
+
+    oneway void activityIdle(in IBinder token, in Configuration config,
+            in boolean stopProfiling);
+    void activityResumed(in IBinder token);
+    void activityPaused(in IBinder token);
+    void activityStopped(in IBinder token, in Bundle state,
+            in PersistableBundle persistentState, in CharSequence description);
+    oneway void activityDestroyed(in IBinder token);
+    void activityRelaunched(in IBinder token);
+    oneway void activitySlept(in IBinder token);
+    int getFrontActivityScreenCompatMode();
+    void setFrontActivityScreenCompatMode(int mode);
+    String getCallingPackage(in IBinder token);
+    ComponentName getCallingActivity(in IBinder token);
+    void setFocusedTask(int taskId);
+    boolean removeTask(int taskId);
+    void removeAllVisibleRecentTasks();
+    List<ActivityManager.RunningTaskInfo> getTasks(int maxNum);
+    List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, int ignoreActivityType,
+            int ignoreWindowingMode);
+    boolean shouldUpRecreateTask(in IBinder token, in String destAffinity);
+    boolean navigateUpTo(in IBinder token, in Intent target, int resultCode,
+            in Intent resultData);
+    void moveTaskToFront(int task, int flags, in Bundle options);
+    int getTaskForActivity(in IBinder token, in boolean onlyRoot);
+    void finishSubActivity(in IBinder token, in String resultWho, int requestCode);
+    ParceledListSlice getRecentTasks(int maxNum, int flags, int userId);
+    boolean willActivityBeVisible(in IBinder token);
+    void setRequestedOrientation(in IBinder token, int requestedOrientation);
+    int getRequestedOrientation(in IBinder token);
+    boolean convertFromTranslucent(in IBinder token);
+    boolean convertToTranslucent(in IBinder token, in Bundle options);
+    void notifyActivityDrawn(in IBinder token);
+    void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle);
+    int getActivityDisplayId(in IBinder activityToken);
+    boolean isImmersive(in IBinder token);
+    void setImmersive(in IBinder token, boolean immersive);
+    boolean isTopActivityImmersive();
+    boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot);
+    ActivityManager.TaskDescription getTaskDescription(int taskId);
+    void overridePendingTransition(in IBinder token, in String packageName,
+            int enterAnim, int exitAnim);
+    int getLaunchedFromUid(in IBinder activityToken);
+    String getLaunchedFromPackage(in IBinder activityToken);
+    void reportAssistContextExtras(in IBinder token, in Bundle extras,
+            in AssistStructure structure, in AssistContent content, in Uri referrer);
+
+    void setFocusedStack(int stackId);
+    ActivityManager.StackInfo getFocusedStackInfo();
+    Rect getTaskBounds(int taskId);
+
+    void cancelRecentsAnimation(boolean restoreHomeStackPosition);
+    void startLockTaskModeByToken(in IBinder token);
+    void stopLockTaskModeByToken(in IBinder token);
+    boolean isInLockTaskMode();
+    int getLockTaskModeState();
+    void setTaskDescription(in IBinder token, in ActivityManager.TaskDescription values);
+    Bundle getActivityOptions(in IBinder token);
+    List<IBinder> getAppTasks(in String callingPackage);
+    void startSystemLockTaskMode(int taskId);
+    void stopSystemLockTaskMode();
+    void finishVoiceTask(in IVoiceInteractionSession session);
+    boolean isTopOfTask(in IBinder token);
+    void notifyLaunchTaskBehindComplete(in IBinder token);
+    void notifyEnterAnimationComplete(in IBinder token);
+    int addAppTask(in IBinder activityToken, in Intent intent,
+            in ActivityManager.TaskDescription description, in Bitmap thumbnail);
+    Point getAppTaskThumbnailSize();
+    boolean releaseActivityInstance(in IBinder token);
+    void releaseSomeActivities(in IApplicationThread app);
+    Bitmap getTaskDescriptionIcon(in String filename, int userId);
+    void startInPlaceAnimationOnFrontMostApplication(in Bundle opts);
+    void registerTaskStackListener(in ITaskStackListener listener);
+    void unregisterTaskStackListener(in ITaskStackListener listener);
+    void setTaskResizeable(int taskId, int resizeableMode);
+    void exitFreeformMode(in IBinder token);
+    void resizeTask(int taskId, in Rect bounds, int resizeMode);
+    void moveStackToDisplay(int stackId, int displayId);
+    void removeStack(int stackId);
+
+    /**
+     * Sets the windowing mode for a specific task. Only works on tasks of type
+     * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}
+     * @param taskId The id of the task to set the windowing mode for.
+     * @param windowingMode The windowing mode to set for the task.
+     * @param toTop If the task should be moved to the top once the windowing mode changes.
+     */
+    void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
+    void moveTaskToStack(int taskId, int stackId, boolean toTop);
+    /**
+     * Resizes the input stack id to the given bounds.
+     *
+     * @param stackId Id of the stack to resize.
+     * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
+     * @param allowResizeInDockedMode True if the resize should be allowed when the docked stack is
+     *                                active.
+     * @param preserveWindows True if the windows of activities contained in the stack should be
+     *                        preserved.
+     * @param animate True if the stack resize should be animated.
+     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
+     *                          default animation duration should be used.
+     * @throws RemoteException
+     */
+    void resizeStack(int stackId, in Rect bounds, boolean allowResizeInDockedMode,
+            boolean preserveWindows, boolean animate, int animationDuration);
+    boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
+            boolean animate, in Rect initialBounds, boolean showRecents);
+
+
+    /**
+     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+     */
+    void removeStacksInWindowingModes(in int[] windowingModes);
+    /** Removes stack of the activity types from the system. */
+    void removeStacksWithActivityTypes(in int[] activityTypes);
+
+    List<ActivityManager.StackInfo> getAllStackInfos();
+    ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType);
+
+    /**
+     * Informs ActivityManagerService that the keyguard is showing.
+     *
+     * @param showingKeyguard True if the keyguard is showing, false otherwise.
+     * @param showingAod True if AOD is showing, false otherwise.
+     * @param secondaryDisplayShowing The displayId of the secondary display on which the keyguard
+     *        is showing, or INVALID_DISPLAY if there is no such display. Only meaningful if
+     *        showing is true.
+     */
+    void setLockScreenShown(boolean showingKeyguard, boolean showingAod,
+            int secondaryDisplayShowing);
+    Bundle getAssistContextExtras(int requestType);
+    boolean launchAssistIntent(in Intent intent, int requestType, in String hint, int userHandle,
+            in Bundle args);
+    boolean requestAssistContextExtras(int requestType, in IAssistDataReceiver receiver,
+            in Bundle receiverExtras, in IBinder activityToken,
+            boolean focused, boolean newSessionId);
+    boolean requestAutofillData(in IAssistDataReceiver receiver, in Bundle receiverExtras,
+            in IBinder activityToken, int flags);
+    boolean isAssistDataAllowedOnCurrentActivity();
+    boolean showAssistFromActivity(in IBinder token, in Bundle args);
+    boolean isRootVoiceInteraction(in IBinder token);
+    oneway void showLockTaskEscapeMessage(in IBinder token);
+
+    /**
+     * Notify the system that the keyguard is going away.
+     *
+     * @param flags See
+     *              {@link android.view.WindowManagerPolicyConstants#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
+     *              etc.
+     */
+    void keyguardGoingAway(int flags);
+    ComponentName getActivityClassForToken(in IBinder token);
+    String getPackageForToken(in IBinder token);
+
+    /**
+     * Try to place task to provided position. The final position might be different depending on
+     * current user and stacks state. The task will be moved to target stack if it's currently in
+     * different stack.
+     */
+    void positionTaskInStack(int taskId, int stackId, int position);
+    void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
+            in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
+    /**
+     * Dismisses split-screen multi-window mode.
+     * {@param toTop} If true the current primary split-screen stack will be placed or left on top.
+     */
+    void dismissSplitScreenMode(boolean toTop);
+
+    /**
+     * Dismisses PiP
+     * @param animate True if the dismissal should be animated.
+     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
+     *                          default animation duration should be used.
+     */
+    void dismissPip(boolean animate, int animationDuration);
+    void suppressResizeConfigChanges(boolean suppress);
+    void moveTasksToFullscreenStack(int fromStackId, boolean onTop);
+    boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds);
+    boolean isInMultiWindowMode(in IBinder token);
+    boolean isInPictureInPictureMode(in IBinder token);
+    boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params);
+    void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params);
+    int getMaxNumPictureInPictureActions(in IBinder token);
+    IBinder getUriPermissionOwnerForActivity(in IBinder activityToken);
+
+    /**
+     * Resizes the docked stack, and all other stacks as the result of the dock stack bounds change.
+     *
+     * @param dockedBounds The bounds for the docked stack.
+     * @param tempDockedTaskBounds The temporary bounds for the tasks in the docked stack, which
+     *                             might be different from the stack bounds to allow more
+     *                             flexibility while resizing, or {@code null} if they should be the
+     *                             same as the stack bounds.
+     * @param tempDockedTaskInsetBounds The temporary bounds for the tasks to calculate the insets.
+     *                                  When resizing, we usually "freeze" the layout of a task. To
+     *                                  achieve that, we also need to "freeze" the insets, which
+     *                                  gets achieved by changing task bounds but not bounds used
+     *                                  to calculate the insets in this transient state
+     * @param tempOtherTaskBounds The temporary bounds for the tasks in all other stacks, or
+     *                            {@code null} if they should be the same as the stack bounds.
+     * @param tempOtherTaskInsetBounds Like {@code tempDockedTaskInsetBounds}, but for the other
+     *                                 stacks.
+     * @throws RemoteException
+     */
+    void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds,
+            in Rect tempDockedTaskInsetBounds,
+            in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
+
+    /**
+     * Sets whether we are currently in an interactive split screen resize operation where we
+     * are changing the docked stack size.
+     */
+    void setSplitScreenResizing(boolean resizing);
+    int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
+    void startLocalVoiceInteraction(in IBinder token, in Bundle options);
+    void stopLocalVoiceInteraction(in IBinder token);
+    boolean supportsLocalVoiceInteraction();
+    void notifyPinnedStackAnimationStarted();
+    void notifyPinnedStackAnimationEnded();
+
+    /**
+     * Resizes the pinned stack.
+     *
+     * @param pinnedBounds The bounds for the pinned stack.
+     * @param tempPinnedTaskBounds The temporary bounds for the tasks in the pinned stack, which
+     *                             might be different from the stack bounds to allow more
+     *                             flexibility while resizing, or {@code null} if they should be the
+     *                             same as the stack bounds.
+     */
+    void resizePinnedStack(in Rect pinnedBounds, in Rect tempPinnedTaskBounds);
+
+    /**
+     * Updates override configuration applied to specific display.
+     * @param values Update values for display configuration. If null is passed it will request the
+     *               Window Manager to compute new config for the specified display.
+     * @param displayId Id of the display to apply the config to.
+     * @throws RemoteException
+     * @return Returns true if the configuration was updated.
+     */
+    boolean updateDisplayOverrideConfiguration(in Configuration values, int displayId);
+    void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback,
+            in CharSequence message);
+
+    /** Cancels the window transitions for the given task. */
+    void cancelTaskWindowTransition(int taskId);
+
+    /**
+     * @param taskId the id of the task to retrieve the sAutoapshots for
+     * @param reducedResolution if set, if the snapshot needs to be loaded from disk, this will load
+     *                          a reduced resolution of it, which is much faster
+     * @return a graphic buffer representing a screenshot of a task
+     */
+    ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution);
+
+    /**
+     * See {@link android.app.Activity#setDisablePreviewScreenshots}
+     */
+    void setDisablePreviewScreenshots(IBinder token, boolean disable);
+
+    /**
+     * Return the user id of last resumed activity.
+     */
+    int getLastResumedActivityUserId();
+
+    /**
+     * Updates global configuration and applies changes to the entire system.
+     * @param values Update values for global configuration. If null is passed it will request the
+     *               Window Manager to compute new config for the default display.
+     * @throws RemoteException
+     * @return Returns true if the configuration was updated.
+     */
+    boolean updateConfiguration(in Configuration values);
+    void updateLockTaskFeatures(int userId, int flags);
+
+    void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
+    void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
+
+    /**
+     * Registers remote animations for a specific activity.
+     */
+    void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition);
+
+    /**
+     * Registers a remote animation to be run for all activity starts from a certain package during
+     * a short predefined amount of time.
+     */
+    void registerRemoteAnimationForNextActivityStart(in String packageName,
+           in RemoteAnimationAdapter adapter);
+
+    /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
+    void alwaysShowUnsupportedCompileSdkWarning(in ComponentName activity);
+
+    void setVrThread(int tid);
+    void setPersistentVrThread(int tid);
+    void stopAppSwitches();
+    void resumeAppSwitches();
+    void setActivityController(in IActivityController watcher, boolean imAMonkey);
+    void setVoiceKeepAwake(in IVoiceInteractionSession session, boolean keepAwake);
+}
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 3aeef14..4517446 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.app.backup.IBackupCallback;
 import android.app.backup.IBackupManager;
 import android.os.ParcelFileDescriptor;
  
@@ -55,7 +56,7 @@
     void doBackup(in ParcelFileDescriptor oldState,
             in ParcelFileDescriptor data,
             in ParcelFileDescriptor newState,
-            long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags);
+            long quotaBytes, IBackupCallback callbackBinder, int transportFlags);
 
     /**
      * Restore an entire data snapshot to the application.
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index d01938b..ac4bf7d 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -47,7 +47,8 @@
             in ParcelFileDescriptor source);
     void grantRuntimePermission(String packageName, String permission, int userId);
     void revokeRuntimePermission(String packageName, String permission, int userId);
-
+    void adoptShellPermissionIdentity(int uid);
+    void dropShellPermissionIdentity();
     // Called from the system process.
     oneway void shutdown();
 }
diff --git a/core/java/android/app/IUriGrantsManager.aidl b/core/java/android/app/IUriGrantsManager.aidl
new file mode 100644
index 0000000..928c627
--- /dev/null
+++ b/core/java/android/app/IUriGrantsManager.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.app;
+
+import android.content.pm.ParceledListSlice;
+import android.net.Uri;
+import android.os.IBinder;
+
+/**
+ * Interface for managing an app's permission to access a particular URI.
+ * {@hide}
+ */
+interface IUriGrantsManager {
+    void takePersistableUriPermission(in Uri uri, int modeFlags, String toPackage, int userId);
+    void releasePersistableUriPermission(in Uri uri, int modeFlags, String toPackage, int userId);
+    void grantUriPermissionFromOwner(in IBinder owner, int fromUid, in String targetPkg,
+            in Uri uri, int mode, int sourceUserId, int targetUserId);
+    /**
+     * Gets the URI permissions granted to an arbitrary package (or all packages if null)
+     * NOTE: this is different from getPersistedUriPermissions(), which returns the URIs the package
+     * granted to another packages (instead of those granted to it).
+     */
+    ParceledListSlice getGrantedUriPermissions(in String packageName, int userId);
+    /** Clears the URI permissions granted to an arbitrary package. */
+    void clearGrantedUriPermissions(in String packageName, int userId);
+    ParceledListSlice getPersistedUriPermissions(in String packageName, boolean incoming);
+}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index c7618fe..d9969a7 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1313,7 +1313,8 @@
      * @param activity The activity being restored.
      * @param savedInstanceState The previously saved state being restored.
      */
-    public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) {
+    public void callActivityOnRestoreInstanceState(@NonNull Activity activity,
+            @NonNull Bundle savedInstanceState) {
         activity.performRestoreInstanceState(savedInstanceState);
     }
 
@@ -1322,11 +1323,12 @@
      * method.  The default implementation simply calls through to that method.
      *
      * @param activity The activity being restored.
-     * @param savedInstanceState The previously saved state being restored.
+     * @param savedInstanceState The previously saved state being restored (or null).
      * @param persistentState The previously persisted state (or null)
      */
-    public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState,
-            PersistableBundle persistentState) {
+    public void callActivityOnRestoreInstanceState(@NonNull Activity activity,
+            @Nullable Bundle savedInstanceState,
+            @Nullable PersistableBundle persistentState) {
         activity.performRestoreInstanceState(savedInstanceState, persistentState);
     }
 
@@ -1335,11 +1337,12 @@
      * The default implementation simply calls through to that method.
      *
      * @param activity The activity being created.
-     * @param icicle The previously frozen state (or null) to pass through to
+     * @param savedInstanceState The previously saved state (or null) to pass through to
      *               onPostCreate().
      */
-    public void callActivityOnPostCreate(Activity activity, Bundle icicle) {
-        activity.onPostCreate(icicle);
+    public void callActivityOnPostCreate(@NonNull Activity activity,
+            @Nullable Bundle savedInstanceState) {
+        activity.onPostCreate(savedInstanceState);
     }
 
     /**
@@ -1347,12 +1350,14 @@
      * The default implementation simply calls through to that method.
      *
      * @param activity The activity being created.
-     * @param icicle The previously frozen state (or null) to pass through to
+     * @param savedInstanceState The previously frozen state (or null) to pass through to
      *               onPostCreate().
+     * @param persistentState The previously persisted state (or null)
      */
-    public void callActivityOnPostCreate(Activity activity, Bundle icicle,
-            PersistableBundle persistentState) {
-        activity.onPostCreate(icicle, persistentState);
+    public void callActivityOnPostCreate(@NonNull Activity activity,
+            @Nullable Bundle savedInstanceState,
+            @Nullable PersistableBundle persistentState) {
+        activity.onPostCreate(savedInstanceState, persistentState);
     }
 
     /**
@@ -1439,7 +1444,8 @@
      * @param activity The activity being saved.
      * @param outState The bundle to pass to the call.
      */
-    public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) {
+    public void callActivityOnSaveInstanceState(@NonNull Activity activity,
+            @NonNull Bundle outState) {
         activity.performSaveInstanceState(outState);
     }
 
@@ -1450,8 +1456,8 @@
      * @param outState The bundle to pass to the call.
      * @param outPersistentState The persistent bundle to pass to the call.
      */
-    public void callActivityOnSaveInstanceState(Activity activity, Bundle outState,
-            PersistableBundle outPersistentState) {
+    public void callActivityOnSaveInstanceState(@NonNull Activity activity,
+            @NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
         activity.performSaveInstanceState(outState, outPersistentState);
     }
 
@@ -1665,7 +1671,7 @@
         try {
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
-            int result = ActivityManager.getService()
+            int result = ActivityTaskManager.getService()
                 .startActivity(whoThread, who.getBasePackageName(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                         token, target != null ? target.mEmbeddedID : null,
@@ -1736,7 +1742,7 @@
                 intents[i].prepareToLeaveProcess(who);
                 resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver());
             }
-            int result = ActivityManager.getService()
+            int result = ActivityTaskManager.getService()
                 .startActivities(whoThread, who.getBasePackageName(), intents, resolvedTypes,
                         token, options, userId);
             checkStartActivityResult(result, intents[0]);
@@ -1803,7 +1809,7 @@
         try {
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
-            int result = ActivityManager.getService()
+            int result = ActivityTaskManager.getService()
                 .startActivity(whoThread, who.getBasePackageName(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                         token, target, requestCode, 0, null, options);
@@ -1870,7 +1876,7 @@
         try {
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
-            int result = ActivityManager.getService()
+            int result = ActivityTaskManager.getService()
                 .startActivityAsUser(whoThread, who.getBasePackageName(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                         token, resultWho,
@@ -1916,7 +1922,7 @@
         try {
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
-            int result = ActivityManager.getService()
+            int result = ActivityTaskManager.getService()
                 .startActivityAsCaller(whoThread, who.getBasePackageName(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                         token, target != null ? target.mEmbeddedID : null,
diff --git a/core/java/android/app/JobSchedulerImpl.java b/core/java/android/app/JobSchedulerImpl.java
index 4ac44f7..5494e2a 100644
--- a/core/java/android/app/JobSchedulerImpl.java
+++ b/core/java/android/app/JobSchedulerImpl.java
@@ -17,16 +17,15 @@
 // in android.app so ContextImpl has package access
 package android.app;
 
+import android.app.job.IJobScheduler;
 import android.app.job.JobInfo;
 import android.app.job.JobScheduler;
-import android.app.job.IJobScheduler;
+import android.app.job.JobSnapshot;
 import android.app.job.JobWorkItem;
-import android.content.Intent;
 import android.os.RemoteException;
 
 import java.util.List;
 
-
 /**
  * Concrete implementation of the JobScheduler interface
  * @hide 
@@ -98,4 +97,22 @@
             return null;
         }
     }
+
+    @Override
+    public List<JobInfo> getStartedJobs() {
+        try {
+            return mBinder.getStartedJobs();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public List<JobSnapshot> getAllJobSnapshots() {
+        try {
+            return mBinder.getAllJobSnapshots();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
 }
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index b512e5c..9ceecd9 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -513,7 +513,8 @@
     public void requestDismissKeyguard(@NonNull Activity activity, @Nullable CharSequence message,
             @Nullable KeyguardDismissCallback callback) {
         try {
-            mAm.dismissKeyguard(activity.getActivityToken(), new IKeyguardDismissCallback.Stub() {
+            ActivityTaskManager.getService().dismissKeyguard(
+                    activity.getActivityToken(), new IKeyguardDismissCallback.Stub() {
                 @Override
                 public void onDismissError() throws RemoteException {
                     if (callback != null && !activity.isDestroyed()) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 7c9cb73..74d3c0d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -202,6 +202,11 @@
      */
     private static final int MAX_REPLY_HISTORY = 5;
 
+    /**
+     * Maximum numbers of action buttons in a notification.
+     * @hide
+     */
+    public static final int MAX_ACTION_BUTTONS = 3;
 
     /**
      * If the notification contained an unsent draft for a RemoteInput when the user clicked on it,
@@ -2769,7 +2774,6 @@
      */
     private void fixDuplicateExtras() {
         if (extras != null) {
-            fixDuplicateExtra(mSmallIcon, EXTRA_SMALL_ICON);
             fixDuplicateExtra(mLargeIcon, EXTRA_LARGE_ICON);
         }
     }
@@ -3152,8 +3156,6 @@
         public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
                 = "android.rebuild.hudViewActionCount";
 
-        private static final int MAX_ACTION_BUTTONS = 3;
-
         private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
                 SystemProperties.getBoolean("notifications.only_title", true);
 
@@ -4474,9 +4476,9 @@
                 mTextColorsAreForBackground = backgroundColor;
                 if (!hasForegroundColor() || !isColorized()) {
                     mPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext,
-                            backgroundColor);
+                            backgroundColor, mInNightMode);
                     mSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext,
-                            backgroundColor);
+                            backgroundColor, mInNightMode);
                     if (backgroundColor != COLOR_DEFAULT && isColorized()) {
                         mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
                                 mPrimaryTextColor, backgroundColor, 4.5);
@@ -5261,7 +5263,7 @@
                     // background color
                     background = outResultColor[0].getDefaultColor();
                     int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
-                            background);
+                            background, mInNightMode);
                     button.setTextColor(R.id.action0, textColor);
                     rippleColor = textColor;
                 } else if (mN.color != COLOR_DEFAULT && !isColorized() && mTintActionButtons) {
@@ -5441,7 +5443,7 @@
                     com.android.internal.R.color.notification_material_background_color);
             if (mN.color == COLOR_DEFAULT) {
                 ensureColors();
-                color = ContrastColorUtil.resolveDefaultColor(mContext, background);
+                color = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode);
             } else {
                 color = ContrastColorUtil.resolveContrastColor(mContext, mN.color,
                         background, mInNightMode);
@@ -5460,7 +5462,8 @@
             }
             int background = mContext.getColor(
                     com.android.internal.R.color.notification_material_background_color);
-            mNeutralColor = ContrastColorUtil.resolveDefaultColor(mContext, background);
+            mNeutralColor = ContrastColorUtil.resolveDefaultColor(mContext, background,
+                    mInNightMode);
             if (Color.alpha(mNeutralColor) < 255) {
                 // alpha doesn't go well for color filters, so let's blend it manually
                 mNeutralColor = ContrastColorUtil.compositeColors(mNeutralColor, background);
@@ -7163,8 +7166,8 @@
         }
 
         public static final class Message {
-
-            static final String KEY_TEXT = "text";
+            /** @hide */
+            public static final String KEY_TEXT = "text";
             static final String KEY_TIMESTAMP = "time";
             static final String KEY_SENDER = "sender";
             static final String KEY_SENDER_PERSON = "sender_person";
@@ -7831,10 +7834,13 @@
 
             // If the action buttons should not be tinted, then just use the default
             // notification color. Otherwise, just use the passed-in color.
+            Configuration currentConfig = mBuilder.mContext.getResources().getConfiguration();
+            boolean inNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+                    == Configuration.UI_MODE_NIGHT_YES;
             int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized()
                     ? color
                     : ContrastColorUtil.resolveColor(mBuilder.mContext,
-                            Notification.COLOR_DEFAULT);
+                            Notification.COLOR_DEFAULT, inNightMode);
 
             button.setDrawableTint(R.id.action0, false, tintColor,
                     PorterDuff.Mode.SRC_ATOP);
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index ba355f9..03fd139 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -38,6 +38,7 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.Arrays;
 
 /**
@@ -644,7 +645,7 @@
 
     @Nullable
     private Uri restoreSoundUri(Context context, @Nullable Uri uri) {
-        if (uri == null) {
+        if (uri == null || Uri.EMPTY.equals(uri)) {
             return null;
         }
         ContentResolver contentResolver = context.getContentResolver();
@@ -680,7 +681,7 @@
 
     private Uri getSoundForBackup(Context context) {
         Uri sound = getSound();
-        if (sound == null) {
+        if (sound == null || Uri.EMPTY.equals(sound)) {
             return null;
         }
         Uri canonicalSound = context.getContentResolver().canonicalize(sound);
@@ -942,6 +943,32 @@
         return result;
     }
 
+    /** @hide */
+    public void dump(PrintWriter pw, String prefix, boolean redacted) {
+        String redactedName = redacted ? TextUtils.trimToLengthWithEllipsis(mName, 3) : mName;
+        String output = "NotificationChannel{"
+                + "mId='" + mId + '\''
+                + ", mName=" + redactedName
+                + ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
+                + ", mImportance=" + mImportance
+                + ", mBypassDnd=" + mBypassDnd
+                + ", mLockscreenVisibility=" + mLockscreenVisibility
+                + ", mSound=" + mSound
+                + ", mLights=" + mLights
+                + ", mLightColor=" + mLightColor
+                + ", mVibration=" + Arrays.toString(mVibration)
+                + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields)
+                + ", mFgServiceShown=" + mFgServiceShown
+                + ", mVibrationEnabled=" + mVibrationEnabled
+                + ", mShowBadge=" + mShowBadge
+                + ", mDeleted=" + mDeleted
+                + ", mGroup='" + mGroup + '\''
+                + ", mAudioAttributes=" + mAudioAttributes
+                + ", mBlockableSystem=" + mBlockableSystem
+                + '}';
+        pw.println(prefix + output);
+    }
+
     @Override
     public String toString() {
         return "NotificationChannel{"
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index 7313b0d..edaae75 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -181,6 +182,7 @@
      * @return the aspect ratio. If none is set, return 0.
      * @hide
      */
+    @TestApi
     public float getAspectRatio() {
         if (mAspectRatio != null) {
             return mAspectRatio.floatValue();
@@ -205,6 +207,7 @@
      * @return the set of user actions.
      * @hide
      */
+    @TestApi
     public List<RemoteAction> getActions() {
         return mUserActions;
     }
@@ -231,6 +234,7 @@
      * @return the source rect hint
      * @hide
      */
+    @TestApi
     public Rect getSourceRectHint() {
         return mSourceRectHint;
     }
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 49faf40..ed6d2f5 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -951,7 +951,7 @@
         try {
             Intent intent = new Intent(Intent.ACTION_ASSIST);
             if (inclContext) {
-                IActivityManager am = ActivityManager.getService();
+                IActivityTaskManager am = ActivityTaskManager.getService();
                 Bundle extras = am.getAssistContextExtras(ActivityManager.ASSIST_CONTEXT_BASIC);
                 if (extras != null) {
                     intent.replaceExtras(extras);
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index b83b44d..c8a8313 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -132,9 +132,10 @@
      */
     public void disable(int what) {
         try {
+            final int userId = Binder.getCallingUserHandle().getIdentifier();
             final IStatusBarService svc = getService();
             if (svc != null) {
-                svc.disable(what, mToken, mContext.getPackageName());
+                svc.disableForUser(what, mToken, mContext.getPackageName(), userId);
             }
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
@@ -149,9 +150,10 @@
      */
     public void disable2(@Disable2Flags int what) {
         try {
+            final int userId = Binder.getCallingUserHandle().getIdentifier();
             final IStatusBarService svc = getService();
             if (svc != null) {
-                svc.disable2(what, mToken, mContext.getPackageName());
+                svc.disable2ForUser(what, mToken, mContext.getPackageName(), userId);
             }
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e8ca4ff9..003f364 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -26,6 +26,7 @@
 import android.app.slice.SliceManager;
 import android.app.timedetector.TimeDetector;
 import android.app.timezone.RulesManager;
+import android.app.timezonedetector.TimeZoneDetector;
 import android.app.trust.TrustManager;
 import android.app.usage.IStorageStatsManager;
 import android.app.usage.IUsageStatsManager;
@@ -54,6 +55,8 @@
 import android.hardware.SystemSensorManager;
 import android.hardware.camera2.CameraManager;
 import android.hardware.display.DisplayManager;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceService;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IFingerprintService;
 import android.hardware.hdmi.HdmiControlManager;
@@ -215,6 +218,22 @@
                 return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
             }});
 
+        registerService(Context.ACTIVITY_TASK_SERVICE, ActivityTaskManager.class,
+                new CachedServiceFetcher<ActivityTaskManager>() {
+            @Override
+            public ActivityTaskManager createService(ContextImpl ctx) {
+                return new ActivityTaskManager(
+                        ctx.getOuterContext(), ctx.mMainThread.getHandler());
+            }});
+
+        registerService(Context.URI_GRANTS_SERVICE, UriGrantsManager.class,
+                new CachedServiceFetcher<UriGrantsManager>() {
+            @Override
+            public UriGrantsManager createService(ContextImpl ctx) {
+                return new UriGrantsManager(
+                        ctx.getOuterContext(), ctx.mMainThread.getHandler());
+            }});
+
         registerService(Context.ALARM_SERVICE, AlarmManager.class,
                 new CachedServiceFetcher<AlarmManager>() {
             @Override
@@ -518,7 +537,7 @@
                 new CachedServiceFetcher<CarrierConfigManager>() {
             @Override
             public CarrierConfigManager createService(ContextImpl ctx) {
-                return new CarrierConfigManager();
+                return new CarrierConfigManager(ctx.getOuterContext());
             }});
 
         registerService(Context.TELECOM_SERVICE, TelecomManager.class,
@@ -783,6 +802,22 @@
                 return new FingerprintManager(ctx.getOuterContext(), service);
             }});
 
+        registerService(Context.FACE_SERVICE, FaceManager.class,
+                new CachedServiceFetcher<FaceManager>() {
+                    @Override
+                    public FaceManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        final IBinder binder;
+                        if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) {
+                            binder = ServiceManager.getServiceOrThrow(Context.FACE_SERVICE);
+                        } else {
+                            binder = ServiceManager.getService(Context.FACE_SERVICE);
+                        }
+                        IFaceService service = IFaceService.Stub.asInterface(binder);
+                        return new FaceManager(ctx.getOuterContext(), service);
+                    }
+                });
+
         registerService(Context.TV_INPUT_SERVICE, TvInputManager.class,
                 new CachedServiceFetcher<TvInputManager>() {
             @Override
@@ -1007,6 +1042,13 @@
                             throws ServiceNotFoundException {
                         return new TimeDetector();
                     }});
+        registerService(Context.TIME_ZONE_DETECTOR_SERVICE, TimeZoneDetector.class,
+                new CachedServiceFetcher<TimeZoneDetector>() {
+                    @Override
+                    public TimeZoneDetector createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        return new TimeZoneDetector();
+                    }});
     }
 
     /**
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
new file mode 100644
index 0000000..970a088
--- /dev/null
+++ b/core/java/android/app/TaskInfo.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 android.app;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Stores information about a particular Task.
+ */
+public class TaskInfo {
+    private static final String TAG = "TaskInfo";
+
+    /**
+     * The id of the user the task was running as.
+     * @hide
+     */
+    public int userId;
+
+    /**
+     * The id of the ActivityStack that currently contains this task.
+     * @hide
+     */
+    public int stackId;
+
+    /**
+     * The identifier for this task.
+     */
+    public int taskId;
+
+    /**
+     * Whether or not this task has any running activities.
+     */
+    public boolean isRunning;
+
+    /**
+     * The base intent of the task (generally the intent that launched the task). This intent can
+     * be used to relaunch the task (if it is no longer running) or brought to the front if it is.
+     */
+    public Intent baseIntent;
+
+    /**
+     * The component of the first activity in the task, can be considered the "application" of this
+     * task.
+     */
+    public ComponentName baseActivity;
+
+    /**
+     * The component of the top activity in the task, currently showing to the user.
+     */
+    public ComponentName topActivity;
+
+    /**
+     * The component of the target activity if this task was started from an activity alias.
+     * Otherwise, this is null.
+     */
+    public ComponentName origActivity;
+
+    /**
+     * The component of the activity that started this task (may be the component of the activity
+     * alias).
+     * @hide
+     */
+    public ComponentName realActivity;
+
+    /**
+     * The number of activities in this task (including running).
+     */
+    public int numActivities;
+
+    /**
+     * The last time this task was active since boot (including time spent in sleep).
+     * @hide
+     */
+    public long lastActiveTime;
+
+    /**
+     * The recent activity values for the highest activity in the stack to have set the values.
+     * {@link Activity#setTaskDescription(android.app.ActivityManager.TaskDescription)}.
+     */
+    public ActivityManager.TaskDescription taskDescription;
+
+    /**
+     * True if the task can go in the split-screen primary stack.
+     * @hide
+     */
+    public boolean supportsSplitScreenMultiWindow;
+
+    /**
+     * The resize mode of the task. See {@link ActivityInfo#resizeMode}.
+     * @hide
+     */
+    public int resizeMode;
+
+    /**
+     * The current configuration of the task.
+     * @hide
+     */
+    public final Configuration configuration = new Configuration();
+
+    TaskInfo() {
+        // Do nothing
+    }
+
+    private TaskInfo(Parcel source) {
+        readFromParcel(source);
+    }
+
+    /**
+     * @param reducedResolution
+     * @return
+     * @hide
+     */
+    public ActivityManager.TaskSnapshot getTaskSnapshot(boolean reducedResolution) {
+        try {
+            return ActivityManager.getService().getTaskSnapshot(taskId, reducedResolution);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to get task snapshot, taskId=" + taskId, e);
+            return null;
+        }
+    }
+
+    /**
+     * Reads the TaskInfo from a parcel.
+     */
+    void readFromParcel(Parcel source) {
+        userId = source.readInt();
+        stackId = source.readInt();
+        taskId = source.readInt();
+        isRunning = source.readBoolean();
+        baseIntent = source.readInt() != 0
+                ? Intent.CREATOR.createFromParcel(source)
+                : null;
+        baseActivity = ComponentName.readFromParcel(source);
+        topActivity = ComponentName.readFromParcel(source);
+        origActivity = ComponentName.readFromParcel(source);
+        realActivity = ComponentName.readFromParcel(source);
+
+        numActivities = source.readInt();
+        lastActiveTime = source.readLong();
+
+        taskDescription = source.readInt() != 0
+                ? ActivityManager.TaskDescription.CREATOR.createFromParcel(source)
+                : null;
+        supportsSplitScreenMultiWindow = source.readBoolean();
+        resizeMode = source.readInt();
+        configuration.readFromParcel(source);
+    }
+
+    /**
+     * Writes the TaskInfo to a parcel.
+     */
+    void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(userId);
+        dest.writeInt(stackId);
+        dest.writeInt(taskId);
+        dest.writeBoolean(isRunning);
+
+        if (baseIntent != null) {
+            dest.writeInt(1);
+            baseIntent.writeToParcel(dest, 0);
+        } else {
+            dest.writeInt(0);
+        }
+        ComponentName.writeToParcel(baseActivity, dest);
+        ComponentName.writeToParcel(topActivity, dest);
+        ComponentName.writeToParcel(origActivity, dest);
+        ComponentName.writeToParcel(realActivity, dest);
+
+        dest.writeInt(numActivities);
+        dest.writeLong(lastActiveTime);
+
+        if (taskDescription != null) {
+            dest.writeInt(1);
+            taskDescription.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeBoolean(supportsSplitScreenMultiWindow);
+        dest.writeInt(resizeMode);
+        configuration.writeToParcel(dest, flags);
+    }
+
+    @Override
+    public String toString() {
+        return "TaskInfo{userId=" + userId + " stackId=" + stackId + " taskId=" + taskId
+                + " isRunning=" + isRunning
+                + " baseIntent=" + baseIntent + " baseActivity=" + baseActivity
+                + " topActivity=" + topActivity + " origActivity=" + origActivity
+                + " realActivity=" + realActivity
+                + " numActivities=" + numActivities
+                + " lastActiveTime=" + lastActiveTime
+                + " supportsSplitScreenMultiWindow=" + supportsSplitScreenMultiWindow
+                + " resizeMode=" + resizeMode;
+    }
+}
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 5662aea..44f28796 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -33,6 +33,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -50,6 +51,7 @@
 import android.view.accessibility.IAccessibilityInteractionConnection;
 
 import com.android.internal.util.function.pooled.PooledLambda;
+
 import libcore.io.IoUtils;
 
 import java.io.IOException;
@@ -347,6 +349,46 @@
     }
 
     /**
+     * Adopt the permission identity of the shell UID. This allows you to call APIs protected
+     * permissions which normal apps cannot hold but are granted to the shell UID. If you
+     * already adopted the shell permission identity this method would be a no-op.
+     * Note that your permission state becomes that of the shell UID and it is not a
+     * combination of your and the shell UID permissions.
+     *
+     * @see #dropShellPermissionIdentity()
+     */
+    public void adoptShellPermissionIdentity() {
+        synchronized (mLock) {
+            throwIfNotConnectedLocked();
+        }
+        try {
+            // Calling out without a lock held.
+            mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid());
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error executing adopting shell permission identity!", re);
+        }
+    }
+
+    /**
+     * Drop the shell permission identity adopted by a previous call to
+     * {@link #adoptShellPermissionIdentity()}. If you did not adopt the shell permission
+     * identity this method would be a no-op.
+     *
+     * @see #adoptShellPermissionIdentity()
+     */
+    public void dropShellPermissionIdentity() {
+        synchronized (mLock) {
+            throwIfNotConnectedLocked();
+        }
+        try {
+            // Calling out without a lock held.
+            mUiAutomationConnection.dropShellPermissionIdentity();
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error executing dropping shell permission identity!", re);
+        }
+    }
+
+    /**
      * Performs a global action. Such an action can be performed at any moment
      * regardless of the current application or user location in that application.
      * For example going back, going home, opening recents, etc.
@@ -999,6 +1041,8 @@
      *
      * @param command The command to execute.
      * @return A file descriptor to the standard output stream.
+     *
+     * @see #adoptShellPermissionIdentity()
      */
     public ParcelFileDescriptor executeShellCommand(String command) {
         synchronized (mLock) {
@@ -1081,22 +1125,6 @@
         return result;
     }
 
-    private static float getDegreesForRotation(int value) {
-        switch (value) {
-            case Surface.ROTATION_90: {
-                return 360f - 90f;
-            }
-            case Surface.ROTATION_180: {
-                return 360f - 180f;
-            }
-            case Surface.ROTATION_270: {
-                return 360f - 270f;
-            } default: {
-                return 0;
-            }
-        }
-    }
-
     private boolean isConnectedLocked() {
         return mConnectionId != CONNECTION_ID_UNDEFINED;
     }
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index d3828ab..ac3f2e7 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -30,6 +30,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.util.Log;
 import android.view.IWindowManager;
 import android.view.InputEvent;
 import android.view.SurfaceControl;
@@ -37,7 +38,6 @@
 import android.view.WindowContentFrameStats;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.IAccessibilityManager;
-import android.util.Log;
 
 import libcore.io.IoUtils;
 
@@ -71,6 +71,9 @@
     private final IPackageManager mPackageManager = IPackageManager.Stub
             .asInterface(ServiceManager.getService("package"));
 
+    private final IActivityManager mActivityManager = IActivityManager.Stub
+            .asInterface(ServiceManager.getService("activity"));
+
     private final Object mLock = new Object();
 
     private final Binder mToken = new Binder();
@@ -274,6 +277,36 @@
         }
     }
 
+    @Override
+    public void adoptShellPermissionIdentity(int uid) throws RemoteException {
+        synchronized (mLock) {
+            throwIfCalledByNotTrustedUidLocked();
+            throwIfShutdownLocked();
+            throwIfNotConnectedLocked();
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mActivityManager.startDelegateShellPermissionIdentity(uid);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void dropShellPermissionIdentity() throws RemoteException {
+        synchronized (mLock) {
+            throwIfCalledByNotTrustedUidLocked();
+            throwIfShutdownLocked();
+            throwIfNotConnectedLocked();
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mActivityManager.stopDelegateShellPermissionIdentity();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     public class Repeater implements Runnable {
         // Continuously read readFrom and write back to writeTo until EOF is encountered
         private final InputStream readFrom;
diff --git a/core/java/android/app/UriGrantsManager.java b/core/java/android/app/UriGrantsManager.java
new file mode 100644
index 0000000..5096f73
--- /dev/null
+++ b/core/java/android/app/UriGrantsManager.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 android.app;
+
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Singleton;
+
+/**
+ * Class for managing an app's permission to access a particular {@link android.net.Uri}.
+ *
+ * @hide
+ */
+@SystemService(Context.URI_GRANTS_SERVICE)
+public class UriGrantsManager {
+
+    private final Context mContext;
+
+    UriGrantsManager(Context context, Handler handler) {
+        mContext = context;
+    }
+
+    /** @hide */
+    public static IUriGrantsManager getService() {
+        return IUriGrantsManagerSingleton.get();
+    }
+
+    private static final Singleton<IUriGrantsManager> IUriGrantsManagerSingleton =
+            new Singleton<IUriGrantsManager>() {
+                @Override
+                protected IUriGrantsManager create() {
+                    final IBinder b = ServiceManager.getService(Context.URI_GRANTS_SERVICE);
+                    return IUriGrantsManager.Stub.asInterface(b);
+                }
+            };
+
+    /**
+     * Permits an application to clear the persistent URI permissions granted to another.
+     *
+     * <p>Typically called by Settings, requires {@code CLEAR_APP_GRANTED_URI_PERMISSIONS}.
+     *
+     * @param packageName application to clear its granted permissions
+     *
+     * @hide
+     */
+    public void clearGrantedUriPermissions(String packageName) {
+        try {
+            getService().clearGrantedUriPermissions(packageName, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Permits an application to get the persistent URI permissions granted to another.
+     *
+     * <p>Typically called by Settings or DocumentsUI, requires
+     * {@code GET_APP_GRANTED_URI_PERMISSIONS}.
+     *
+     * @param packageName application to look for the granted permissions, or {@code null} to get
+     * granted permissions for all applications
+     * @return list of granted URI permissions
+     *
+     * @hide
+     */
+    public ParceledListSlice<GrantedUriPermission> getGrantedUriPermissions(
+            @Nullable String packageName) {
+        try {
+            @SuppressWarnings("unchecked")
+            final ParceledListSlice<GrantedUriPermission> castedList = getService()
+                    .getGrantedUriPermissions(packageName, mContext.getUserId());
+            return castedList;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 35a1789..9873a81 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -315,12 +315,13 @@
     }
 
     /**
-     * Returns whether a wallpaper was optimized or not for ambient mode.
+     * Returns whether a wallpaper was optimized or not for ambient mode and can be drawn in there.
      *
-     * @return {@code true} if wallpaper can draw in ambient mode.
-     * @hide
+     * @see WallpaperService.Engine#onAmbientModeChanged(boolean, boolean)
+     * @see WallpaperService.Engine#isInAmbientMode()
+     * @return {@code true} if wallpaper can draw when in ambient mode.
      */
-    public boolean getSupportsAmbientMode() {
+    public boolean supportsAmbientMode() {
         return mSupportsAmbientMode;
     }
 
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index fde756c..6ad6c25 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -996,17 +996,29 @@
     }
 
     /**
-     * If the current wallpaper is a live wallpaper component, return the
-     * information about that wallpaper.  Otherwise, if it is a static image,
-     * simply return null.
+     * Returns the information about the wallpaper if the current wallpaper is
+     * a live wallpaper component. Otherwise, if the wallpaper is a static image,
+     * this returns null.
      */
     public WallpaperInfo getWallpaperInfo() {
+        return getWallpaperInfo(mContext.getUserId());
+    }
+
+    /**
+     * Returns the information about the wallpaper if the current wallpaper is
+     * a live wallpaper component. Otherwise, if the wallpaper is a static image,
+     * this returns null.
+     *
+     * @param userId Owner of the wallpaper.
+     * @hide
+     */
+    public WallpaperInfo getWallpaperInfo(int userId) {
         try {
             if (sGlobals.mService == null) {
                 Log.w(TAG, "WallpaperService not running");
                 throw new RuntimeException(new DeadSystemException());
             } else {
-                return sGlobals.mService.getWallpaperInfo(mContext.getUserId());
+                return sGlobals.mService.getWallpaperInfo(userId);
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 21d6762..e6fb5dc 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -124,6 +124,24 @@
     })
     public @interface ActivityType {}
 
+    /** The current always on top status of the configuration. */
+    private @AlwaysOnTop int mAlwaysOnTop;
+
+    /** Always on top is currently not defined. */
+    private static final int ALWAYS_ON_TOP_UNDEFINED = 0;
+    /** Always on top is currently on for this configuration. */
+    private static final int ALWAYS_ON_TOP_ON = 1;
+    /** Always on top is currently off for this configuration. */
+    private static final int ALWAYS_ON_TOP_OFF = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "ALWAYS_ON_TOP_" }, value = {
+            ALWAYS_ON_TOP_UNDEFINED,
+            ALWAYS_ON_TOP_ON,
+            ALWAYS_ON_TOP_OFF,
+    })
+    private @interface AlwaysOnTop {}
+
     /** Bit that indicates that the {@link #mBounds} changed.
      * @hide */
     public static final int WINDOW_CONFIG_BOUNDS = 1 << 0;
@@ -136,13 +154,16 @@
     /** Bit that indicates that the {@link #mActivityType} changed.
      * @hide */
     public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 3;
-
+    /** Bit that indicates that the {@link #mAlwaysOnTop} changed.
+     * @hide */
+    public static final int WINDOW_CONFIG_ALWAYS_ON_TOP = 1 << 4;
     /** @hide */
     @IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
             WINDOW_CONFIG_BOUNDS,
             WINDOW_CONFIG_APP_BOUNDS,
             WINDOW_CONFIG_WINDOWING_MODE,
-            WINDOW_CONFIG_ACTIVITY_TYPE
+            WINDOW_CONFIG_ACTIVITY_TYPE,
+            WINDOW_CONFIG_ALWAYS_ON_TOP,
     })
     public @interface WindowConfig {}
 
@@ -168,6 +189,7 @@
         dest.writeParcelable(mAppBounds, flags);
         dest.writeInt(mWindowingMode);
         dest.writeInt(mActivityType);
+        dest.writeInt(mAlwaysOnTop);
     }
 
     private void readFromParcel(Parcel source) {
@@ -175,6 +197,7 @@
         mAppBounds = source.readParcelable(Rect.class.getClassLoader());
         mWindowingMode = source.readInt();
         mActivityType = source.readInt();
+        mAlwaysOnTop = source.readInt();
     }
 
     @Override
@@ -222,6 +245,21 @@
         setAppBounds(rect.left, rect.top, rect.right, rect.bottom);
     }
 
+
+
+    /**
+     * Sets whether this window should be always on top.
+     * @param alwaysOnTop {@code true} to set window always on top, otherwise {@code false}
+     * @hide
+     */
+    public void setAlwaysOnTop(boolean alwaysOnTop) {
+        mAlwaysOnTop = alwaysOnTop ? ALWAYS_ON_TOP_ON : ALWAYS_ON_TOP_OFF;
+    }
+
+    private void setAlwaysOnTop(@AlwaysOnTop int alwaysOnTop) {
+        mAlwaysOnTop = alwaysOnTop;
+    }
+
     /**
      * @see #setAppBounds(Rect)
      * @see #getAppBounds()
@@ -281,6 +319,7 @@
         setAppBounds(other.mAppBounds);
         setWindowingMode(other.mWindowingMode);
         setActivityType(other.mActivityType);
+        setAlwaysOnTop(other.mAlwaysOnTop);
     }
 
     /** Set this object to completely undefined.
@@ -295,6 +334,7 @@
         setBounds(null);
         setWindowingMode(WINDOWING_MODE_UNDEFINED);
         setActivityType(ACTIVITY_TYPE_UNDEFINED);
+        setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED);
     }
 
     /**
@@ -326,6 +366,11 @@
             changed |= WINDOW_CONFIG_ACTIVITY_TYPE;
             setActivityType(delta.mActivityType);
         }
+        if (delta.mAlwaysOnTop != ALWAYS_ON_TOP_UNDEFINED
+                && mAlwaysOnTop != delta.mAlwaysOnTop) {
+            changed |= WINDOW_CONFIG_ALWAYS_ON_TOP;
+            setAlwaysOnTop(delta.mAlwaysOnTop);
+        }
         return changed;
     }
 
@@ -364,6 +409,11 @@
             changes |= WINDOW_CONFIG_ACTIVITY_TYPE;
         }
 
+        if ((compareUndefined || other.mAlwaysOnTop != ALWAYS_ON_TOP_UNDEFINED)
+                && mAlwaysOnTop != other.mAlwaysOnTop) {
+            changes |= WINDOW_CONFIG_ALWAYS_ON_TOP;
+        }
+
         return changes;
     }
 
@@ -398,6 +448,8 @@
         if (n != 0) return n;
         n = mActivityType - that.mActivityType;
         if (n != 0) return n;
+        n = mAlwaysOnTop - that.mAlwaysOnTop;
+        if (n != 0) return n;
 
         // if (n != 0) return n;
         return n;
@@ -425,6 +477,7 @@
 
         result = 31 * result + mWindowingMode;
         result = 31 * result + mActivityType;
+        result = 31 * result + mAlwaysOnTop;
         return result;
     }
 
@@ -434,7 +487,9 @@
         return "{ mBounds=" + mBounds
                 + " mAppBounds=" + mAppBounds
                 + " mWindowingMode=" + windowingModeToString(mWindowingMode)
-                + " mActivityType=" + activityTypeToString(mActivityType) + "}";
+                + " mActivityType=" + activityTypeToString(mActivityType)
+                + " mAlwaysOnTop=" + alwaysOnTopToString(mAlwaysOnTop)
+                + "}";
     }
 
     /**
@@ -520,7 +575,8 @@
      * @hide
      */
     public boolean isAlwaysOnTop() {
-        return mWindowingMode == WINDOWING_MODE_PINNED;
+        return mWindowingMode == WINDOWING_MODE_PINNED
+                || (mWindowingMode == WINDOWING_MODE_FREEFORM && mAlwaysOnTop == ALWAYS_ON_TOP_ON);
     }
 
     /**
@@ -597,4 +653,14 @@
         }
         return String.valueOf(applicationType);
     }
+
+    /** @hide */
+    public static String alwaysOnTopToString(@AlwaysOnTop int alwaysOnTop) {
+        switch (alwaysOnTop) {
+            case ALWAYS_ON_TOP_UNDEFINED: return "undefined";
+            case ALWAYS_ON_TOP_ON: return "on";
+            case ALWAYS_ON_TOP_OFF: return "off";
+        }
+        return String.valueOf(alwaysOnTop);
+    }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 22367b2..dfdb7eb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -72,6 +72,7 @@
 import android.security.keystore.KeyAttestationException;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.security.keystore.StrongBoxUnavailableException;
 import android.service.restrictions.RestrictionsReceiver;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
@@ -1774,6 +1775,13 @@
     public static final int ID_TYPE_MEID = 8;
 
     /**
+     * Service-specific error code for {@link #generateKeyPair}:
+     * Indicates the call has failed due to StrongBox unavailability.
+     * @hide
+     */
+    public static final int KEY_GEN_STRONGBOX_UNAVAILABLE = 1;
+
+    /**
      * Specifies that the calling app should be granted access to the installed credentials
      * immediately. Otherwise, access to the credentials will be gated by user approval.
      * For use with {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)}
@@ -3237,8 +3245,8 @@
 
     /**
      * Called by a device/profile owner to set the timeout after which unlocking with secondary, non
-     * strong auth (e.g. fingerprint, trust agents) times out, i.e. the user has to use a strong
-     * authentication method like password, pin or pattern.
+     * strong auth (e.g. fingerprint, face, trust agents) times out, i.e. the user has to use a
+     * strong authentication method like password, pin or pattern.
      *
      * <p>This timeout is used internally to reset the timer to require strong auth again after
      * specified timeout each time it has been successfully used.
@@ -3710,7 +3718,6 @@
             | DevicePolicyManager.KEYGUARD_DISABLE_IRIS
             | DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
 
-
     /**
      * Disable all current and future keyguard customizations.
      */
@@ -4154,8 +4161,11 @@
      * Called by a device or profile owner, or delegated certificate installer, to generate a
      * new private/public key pair. If the device supports key generation via secure hardware,
      * this method is useful for creating a key in KeyChain that never left the secure hardware.
-     *
      * Access to the key is controlled the same way as in {@link #installKeyPair}.
+     *
+     * <p>Because this method might take several seconds to complete, it should only be called from
+     * a worker thread. This method returns {@code null} when called from the main thread.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
      *            {@code null} if calling from a delegated certificate installer.
      * @param algorithm The key generation algorithm, see {@link java.security.KeyPairGenerator}.
@@ -4188,6 +4198,8 @@
      *         {@code keySpec} does not contain an attestation challenge.
      * @throws UnsupportedOperationException if Device ID attestation was requested but the
      *         underlying hardware does not support it.
+     * @throws StrongBoxUnavailableException if the use of StrongBox for key generation was
+     *         specified in {@code keySpec} but the device does not have one.
      * @see KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])
      */
     public AttestedKeyPair generateKeyPair(@Nullable ComponentName admin,
@@ -4228,6 +4240,15 @@
         } catch (InterruptedException e) {
             Log.w(TAG, "Interrupted while generating key", e);
             Thread.currentThread().interrupt();
+        } catch (ServiceSpecificException e) {
+            Log.w(TAG, String.format("Key Generation failure: %d", e.errorCode));
+            switch (e.errorCode) {
+                case KEY_GEN_STRONGBOX_UNAVAILABLE:
+                    throw new StrongBoxUnavailableException("No StrongBox for key generation.");
+                default:
+                    throw new RuntimeException(
+                            String.format("Unknown error while generating key: %d", e.errorCode));
+            }
         }
         return null;
     }
@@ -4898,10 +4919,10 @@
     /**
      * @hide
      */
-    public void reportFailedFingerprintAttempt(int userHandle) {
+    public void reportFailedBiometricAttempt(int userHandle) {
         if (mService != null) {
             try {
-                mService.reportFailedFingerprintAttempt(userHandle);
+                mService.reportFailedBiometricAttempt(userHandle);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -4911,10 +4932,10 @@
     /**
      * @hide
      */
-    public void reportSuccessfulFingerprintAttempt(int userHandle) {
+    public void reportSuccessfulBiometricAttempt(int userHandle) {
         if (mService != null) {
             try {
-                mService.reportSuccessfulFingerprintAttempt(userHandle);
+                mService.reportSuccessfulBiometricAttempt(userHandle);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -7701,6 +7722,9 @@
      * LockTask mode will be registered, but will only take effect when the device leaves LockTask
      * mode.
      *
+     * <p>This policy does not have any effect while on the lock screen, where the status bar will
+     * not be disabled. Using LockTask instead of this method is recommended.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param disabled {@code true} disables the status bar, {@code false} reenables it.
      * @return {@code false} if attempting to disable the status bar failed. {@code true} otherwise.
@@ -8300,6 +8324,22 @@
     }
 
     /**
+     * Makes all accumulated network logs available to DPC in a new batch.
+     * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0.
+     * @hide
+     */
+    public long forceNetworkLogs() {
+        if (mService == null) {
+            return -1;
+        }
+        try {
+            return mService.forceNetworkLogs();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Forces a batch of security logs to be fetched from logd and makes it available for DPC.
      * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0.
      * @hide
@@ -9398,9 +9438,9 @@
      * <ul>
      *   <li>{@link ApnSetting#getOperatorNumeric()}</li>
      *   <li>{@link ApnSetting#getApnName()}</li>
-     *   <li>{@link ApnSetting#getProxyAddress()}</li>
+     *   <li>{@link ApnSetting#getProxyAddressAsString()}</li>
      *   <li>{@link ApnSetting#getProxyPort()}</li>
-     *   <li>{@link ApnSetting#getMmsProxyAddress()}</li>
+     *   <li>{@link ApnSetting#getMmsProxyAddressAsString()}</li>
      *   <li>{@link ApnSetting#getMmsProxyPort()}</li>
      *   <li>{@link ApnSetting#getMmsc()}</li>
      *   <li>{@link ApnSetting#isEnabled()}</li>
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 37508cd..c95bc5b 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -132,8 +132,8 @@
     void reportPasswordChanged(int userId);
     void reportFailedPasswordAttempt(int userHandle);
     void reportSuccessfulPasswordAttempt(int userHandle);
-    void reportFailedFingerprintAttempt(int userHandle);
-    void reportSuccessfulFingerprintAttempt(int userHandle);
+    void reportFailedBiometricAttempt(int userHandle);
+    void reportSuccessfulBiometricAttempt(int userHandle);
     void reportKeyguardDismissed(int userHandle);
     void reportKeyguardSecured(int userHandle);
 
@@ -347,6 +347,7 @@
     boolean isSecurityLoggingEnabled(in ComponentName admin);
     ParceledListSlice retrieveSecurityLogs(in ComponentName admin);
     ParceledListSlice retrievePreRebootSecurityLogs(in ComponentName admin);
+    long forceNetworkLogs();
     long forceSecurityLogs();
 
     boolean isUninstallInQueue(String packageName);
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index ef41b10..15bcbe3 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -620,7 +620,7 @@
         String mIdType;
         String mIdEntry;
 
-        // TODO: once we have more flags, it might be better to store the individual
+        // TODO(b/37567426): once we have more flags, it might be better to store the individual
         // fields (viewId and childId) of the field.
         AutofillId mAutofillId;
         @View.AutofillType int mAutofillType = View.AUTOFILL_TYPE_NONE;
@@ -664,7 +664,7 @@
         static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000;
         static final int FLAGS_OPAQUE = 0x00008000;
 
-        // TODO: autofill data is made of many fields and ideally we should verify
+        // TODO(b/37567426): autofill data is made of many fields and ideally we should verify
         // one-by-one to optimize what's sent over, but there isn't enough flag bits for that, we'd
         // need to create a 'flags2' or 'autoFillFlags' field and add these flags there.
         // So, to keep thinkg simpler for now, let's just use on flag for all of them...
@@ -2167,13 +2167,13 @@
         if (autofillId == null) {
             Log.i(TAG, prefix + " NO autofill ID");
         } else {
-            Log.i(TAG, prefix + "Autofill info: id= " + autofillId
+            Log.i(TAG, prefix + "  Autofill info: id= " + autofillId
                     + ", type=" + node.getAutofillType()
                     + ", options=" + Arrays.toString(node.getAutofillOptions())
                     + ", hints=" + Arrays.toString(node.getAutofillHints())
                     + ", value=" + node.getAutofillValue()
                     + ", sanitized=" + node.isSanitized()
-                    + ", importantFor=" + node.getImportantForAutofill());
+                    + ", important=" + node.getImportantForAutofill());
         }
 
         final int NCHILDREN = node.getChildCount();
@@ -2237,6 +2237,22 @@
         return mWindowNodes.get(index);
     }
 
+    // TODO(b/35708678): temporary method that disable one-way warning flag on binder.
+    /** @hide */
+    public void ensureDataForAutofill() {
+        if (mHaveData) {
+            return;
+        }
+        mHaveData = true;
+        Binder.allowBlocking(mReceiveChannel);
+        try {
+            ParcelTransferReader reader = new ParcelTransferReader(mReceiveChannel);
+            reader.go();
+        } finally {
+            Binder.defaultBlocking(mReceiveChannel);
+        }
+    }
+
     /** @hide */
     public void ensureData() {
         if (mHaveData) {
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 9657922..097dd9c 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -43,7 +43,6 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.util.Collection;
 import java.util.LinkedList;
 import java.util.Map;
 import java.util.Set;
@@ -833,7 +832,7 @@
         }
 
         if (excludes != null &&
-                isFileSpecifiedInPathList(destination, excludes)) {
+                BackupUtils.isFileSpecifiedInPathList(destination, excludes)) {
             if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
                 Log.v(FullBackup.TAG_XML_PARSER,
                         "onRestoreFile: \"" + destinationCanonicalPath + "\": listed in"
@@ -847,7 +846,8 @@
             // it's a small list), we'll go through and look for it.
             boolean explicitlyIncluded = false;
             for (Set<PathWithRequiredFlags> domainIncludes : includes.values()) {
-                explicitlyIncluded |= isFileSpecifiedInPathList(destination, domainIncludes);
+                explicitlyIncluded |=
+                        BackupUtils.isFileSpecifiedInPathList(destination, domainIncludes);
                 if (explicitlyIncluded) {
                     break;
                 }
@@ -866,33 +866,6 @@
     }
 
     /**
-     * @return True if the provided file is either directly in the provided list, or the provided
-     * file is within a directory in the list.
-     */
-    private boolean isFileSpecifiedInPathList(File file,
-            Collection<PathWithRequiredFlags> canonicalPathList) throws IOException {
-        for (PathWithRequiredFlags canonical : canonicalPathList) {
-            String canonicalPath = canonical.getPath();
-            File fileFromList = new File(canonicalPath);
-            if (fileFromList.isDirectory()) {
-                if (file.isDirectory()) {
-                    // If they are both directories check exact equals.
-                    return file.equals(fileFromList);
-                } else {
-                    // O/w we have to check if the file is within the directory from the list.
-                    return file.getCanonicalPath().startsWith(canonicalPath);
-                }
-            } else {
-                if (file.equals(fileFromList)) {
-                    // Need to check the explicit "equals" so we don't end up with substrings.
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
      * Only specialized platform agents should overload this entry point to support
      * restores to crazy non-app locations.
      * @hide
@@ -968,11 +941,13 @@
         private static final String TAG = "BackupServiceBinder";
 
         @Override
-        public void doBackup(ParcelFileDescriptor oldState,
+        public void doBackup(
+                ParcelFileDescriptor oldState,
                 ParcelFileDescriptor data,
                 ParcelFileDescriptor newState,
-                long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags)
-                throws RemoteException {
+                long quotaBytes,
+                IBackupCallback callbackBinder,
+                int transportFlags) throws RemoteException {
             // Ensure that we're running with the app's normal permission level
             long ident = Binder.clearCallingIdentity();
 
@@ -996,7 +971,7 @@
 
                 Binder.restoreCallingIdentity(ident);
                 try {
-                    callbackBinder.opComplete(token, 0);
+                    callbackBinder.operationComplete(0);
                 } catch (RemoteException e) {
                     // we'll time out anyway, so we're safe
                 }
diff --git a/core/java/android/app/backup/BackupUtils.java b/core/java/android/app/backup/BackupUtils.java
new file mode 100644
index 0000000..8cf8a84
--- /dev/null
+++ b/core/java/android/app/backup/BackupUtils.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.app.backup;
+
+import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+/** @hide */
+public class BackupUtils {
+
+    private BackupUtils() {}
+
+    /**
+     * Returns {@code true} if {@code file} is either directly in {@code canonicalPathList} or is a
+     * file contained in a directory in the list.
+     */
+    public static boolean isFileSpecifiedInPathList(
+            File file, Collection<PathWithRequiredFlags> canonicalPathList) throws IOException {
+        for (PathWithRequiredFlags canonical : canonicalPathList) {
+            String canonicalPath = canonical.getPath();
+            File fileFromList = new File(canonicalPath);
+            if (fileFromList.isDirectory()) {
+                if (file.isDirectory()) {
+                    // If they are both directories check exact equals.
+                    if (file.equals(fileFromList)) {
+                        return true;
+                    }
+                } else {
+                    // O/w we have to check if the file is within the directory from the list.
+                    if (file.toPath().startsWith(canonicalPath)) {
+                        return true;
+                    }
+                }
+            } else if (file.equals(fileFromList)) {
+                // Need to check the explicit "equals" so we don't end up with substrings.
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/core/java/android/app/backup/IBackupCallback.aidl b/core/java/android/app/backup/IBackupCallback.aidl
new file mode 100644
index 0000000..9582a58
--- /dev/null
+++ b/core/java/android/app/backup/IBackupCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.app.backup;
+
+import android.app.backup.IBackupManager;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Callback interface made for responding to one-way calls from the system.
+ *
+ * @hide
+ */
+oneway interface IBackupCallback {
+    void operationComplete(long result);
+}
diff --git a/core/java/android/app/backup/SharedPreferencesBackupHelper.java b/core/java/android/app/backup/SharedPreferencesBackupHelper.java
index 939616b..302a8ef 100644
--- a/core/java/android/app/backup/SharedPreferencesBackupHelper.java
+++ b/core/java/android/app/backup/SharedPreferencesBackupHelper.java
@@ -51,8 +51,8 @@
  *     // identify the SharedPreferenceBackupHelper's data.
  *     static final String MY_PREFS_BACKUP_KEY = "myprefs";
  *
- *     // Simply allocate a helper and install it
- *     void onCreate() {
+ *     // Allocate a helper and install it.
+ *     public void onCreate() {
  *         SharedPreferencesBackupHelper helper =
  *                 new SharedPreferencesBackupHelper(this, PREFS_DISPLAY, PREFS_SCORES);
  *         addHelper(MY_PREFS_BACKUP_KEY, helper);
@@ -117,7 +117,7 @@
      */
     public void restoreEntity(BackupDataInputStream data) {
         Context context = mContext;
-        
+
         String key = data.getKey();
         if (DEBUG) Log.d(TAG, "got entity '" + key + "' size=" + data.size());
 
diff --git a/core/java/android/app/job/IJobScheduler.aidl b/core/java/android/app/job/IJobScheduler.aidl
index e94da0c..53b33c2 100644
--- a/core/java/android/app/job/IJobScheduler.aidl
+++ b/core/java/android/app/job/IJobScheduler.aidl
@@ -17,6 +17,7 @@
 package android.app.job;
 
 import android.app.job.JobInfo;
+import android.app.job.JobSnapshot;
 import android.app.job.JobWorkItem;
 
  /**
@@ -31,4 +32,6 @@
     void cancelAll();
     List<JobInfo> getAllPendingJobs();
     JobInfo getPendingJob(int jobId);
+    List<JobInfo> getStartedJobs();
+    List<JobSnapshot> getAllJobSnapshots();
 }
diff --git a/core/java/android/app/job/JobScheduler.java b/core/java/android/app/job/JobScheduler.java
index 0deb2e1..08b1c2b 100644
--- a/core/java/android/app/job/JobScheduler.java
+++ b/core/java/android/app/job/JobScheduler.java
@@ -172,4 +172,20 @@
      *     if the supplied job ID does not correspond to any job.
      */
     public abstract @Nullable JobInfo getPendingJob(int jobId);
-}
+
+    /**
+     * <b>For internal system callers only!</b>
+     * Returns a list of all currently-executing jobs.
+     * @hide
+     */
+    public abstract List<JobInfo> getStartedJobs();
+
+    /**
+     * <b>For internal system callers only!</b>
+     * Returns a snapshot of the state of all jobs known to the system.
+     *
+     * <p class="note">This is a slow operation, so it should be called sparingly.
+     * @hide
+     */
+    public abstract List<JobSnapshot> getAllJobSnapshots();
+}
\ No newline at end of file
diff --git a/core/java/android/app/job/JobSnapshot.aidl b/core/java/android/app/job/JobSnapshot.aidl
new file mode 100644
index 0000000..d40f4e3
--- /dev/null
+++ b/core/java/android/app/job/JobSnapshot.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.job;
+
+parcelable JobSnapshot;
diff --git a/core/java/android/app/job/JobSnapshot.java b/core/java/android/app/job/JobSnapshot.java
new file mode 100644
index 0000000..d6cc70d
--- /dev/null
+++ b/core/java/android/app/job/JobSnapshot.java
@@ -0,0 +1,111 @@
+/*
+ * 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.app.job;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Current-state snapshot of a scheduled job.  These snapshots are not used in apps;
+ * they exist only within the system process across the local call surface where JobStatus
+ * is not directly accessible at build time.
+ *
+ * Constraints that the underlying job does not require are always reported as
+ * being currently satisfied.
+ * @hide
+ */
+public class JobSnapshot implements Parcelable {
+    private final JobInfo mJob;
+    private final int mSatisfiedConstraints;
+    private final boolean mIsRunnable;
+
+    public JobSnapshot(JobInfo info, int satisfiedMask, boolean runnable) {
+        mJob = info;
+        mSatisfiedConstraints = satisfiedMask;
+        mIsRunnable = runnable;
+    }
+
+    public JobSnapshot(Parcel in) {
+        mJob = JobInfo.CREATOR.createFromParcel(in);
+        mSatisfiedConstraints = in.readInt();
+        mIsRunnable = in.readBoolean();
+    }
+
+    private boolean satisfied(int flag) {
+        return (mSatisfiedConstraints & flag) != 0;
+    }
+
+    /**
+     * Is this job actually runnable at this moment?
+     */
+    public boolean isRunnable() {
+        return mIsRunnable;
+    }
+
+    /**
+     * @see JobInfo.Builder#setRequiresCharging(boolean)
+     */
+    public boolean isChargingSatisfied() {
+        return !mJob.isRequireCharging()
+                || satisfied(JobInfo.CONSTRAINT_FLAG_CHARGING);
+    }
+
+    /**
+     * @see JobInfo.Builder#setRequiresBatteryNotLow(boolean)
+     */
+    public boolean isBatteryNotLowSatisfied() {
+        return !mJob.isRequireBatteryNotLow()
+                || satisfied(JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW);
+    }
+
+    /**
+     * @see JobInfo.Builder#setRequiresDeviceIdle(boolean)
+     */
+    public boolean isRequireDeviceIdleSatisfied() {
+        return !mJob.isRequireDeviceIdle()
+                || satisfied(JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW);
+    }
+
+    public boolean isRequireStorageNotLowSatisfied() {
+        return !mJob.isRequireStorageNotLow()
+                || satisfied(JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        mJob.writeToParcel(out, flags);
+        out.writeInt(mSatisfiedConstraints);
+        out.writeBoolean(mIsRunnable);
+    }
+
+    public static final Creator<JobSnapshot> CREATOR = new Creator<JobSnapshot>() {
+        @Override
+        public JobSnapshot createFromParcel(Parcel in) {
+            return new JobSnapshot(in);
+        }
+
+        @Override
+        public JobSnapshot[] newArray(int size) {
+            return new JobSnapshot[size];
+        }
+    };
+}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 08ad2f0..2a33342 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -26,6 +26,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -164,7 +165,6 @@
         ObjectPool.recycle(this);
     }
 
-
     // Parcelable implementation
 
     /** Write to Parcel. */
@@ -237,4 +237,23 @@
         result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
         return result;
     }
+
+    /** Dump transaction items callback items and final lifecycle state request. */
+    public void dump(String prefix, PrintWriter pw) {
+        pw.append(prefix).println("ClientTransaction{");
+        pw.append(prefix).print("  callbacks=[");
+        final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0;
+        if (size > 0) {
+            pw.println();
+            for (int i = 0; i < size; i++) {
+                pw.append(prefix).append("    ").println(mActivityCallbacks.get(i).toString());
+            }
+            pw.append(prefix).println("  ]");
+        } else {
+            pw.println("]");
+        }
+        pw.append(prefix).append("  stateRequest=").println(mLifecycleStateRequest != null
+                ? mLifecycleStateRequest.toString() : null);
+        pw.append(prefix).println("}");
+    }
 }
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index b443166..5941486 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -33,6 +33,11 @@
     private int mConfigChanges;
 
     @Override
+    public void preExecute(ClientTransactionHandler client, IBinder token) {
+        client.getActivitiesToBeDestroyed().put(token, this);
+    }
+
+    @Override
     public void execute(ClientTransactionHandler client, IBinder token,
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index 0c1eab5..cae93b3 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -19,6 +19,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -60,7 +61,7 @@
         }
         try {
             // TODO(lifecycler): Use interface callback instead of AMS.
-            ActivityManager.getService().activityPaused(token);
+            ActivityTaskManager.getService().activityPaused(token);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java
index af7b7a2..52ba8fb 100644
--- a/core/java/android/app/servertransaction/PendingTransactionActions.java
+++ b/core/java/android/app/servertransaction/PendingTransactionActions.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityThread.DEBUG_MEMORY_TRIM;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.os.Build;
 import android.os.Bundle;
@@ -141,7 +142,7 @@
             try {
                 if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + mActivity);
                 // TODO(lifecycler): Use interface callback instead of AMS.
-                ActivityManager.getService().activityStopped(
+                ActivityTaskManager.getService().activityStopped(
                         mActivity.token, mState, mPersistentState, mDescription);
             } catch (RemoteException ex) {
                 // Dump statistics about bundle to help developers debug
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index 909eec7..a5b6f7c 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -19,6 +19,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -58,7 +59,7 @@
             PendingTransactionActions pendingActions) {
         try {
             // TODO(lifecycler): Use interface callback instead of AMS.
-            ActivityManager.getService().activityResumed(token);
+            ActivityTaskManager.getService().activityResumed(token);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 43a2b4c..20e0da3 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -24,7 +24,11 @@
 import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
 import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+import static android.app.servertransaction.TransactionExecutorHelper.getShortActivityName;
+import static android.app.servertransaction.TransactionExecutorHelper.getStateName;
 import static android.app.servertransaction.TransactionExecutorHelper.lastCallbackRequestingState;
+import static android.app.servertransaction.TransactionExecutorHelper.tId;
+import static android.app.servertransaction.TransactionExecutorHelper.transactionToString;
 
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
@@ -35,6 +39,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * Class that manages transaction execution in the correct order.
@@ -62,25 +67,47 @@
      * either remain in the initial state, or last state needed by a callback.
      */
     public void execute(ClientTransaction transaction) {
+        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");
+
         final IBinder token = transaction.getActivityToken();
-        log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);
+        if (token != null) {
+            final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed =
+                    mTransactionHandler.getActivitiesToBeDestroyed();
+            final ClientTransactionItem destroyItem = activitiesToBeDestroyed.get(token);
+            if (destroyItem != null) {
+                if (transaction.getLifecycleStateRequest() == destroyItem) {
+                    // It is going to execute the transaction that will destroy activity with the
+                    // token, so the corresponding to-be-destroyed record can be removed.
+                    activitiesToBeDestroyed.remove(token);
+                }
+                if (mTransactionHandler.getActivityClient(token) == null) {
+                    // The activity has not been created but has been requested to destroy, so all
+                    // transactions for the token are just like being cancelled.
+                    Slog.w(TAG, tId(transaction) + "Skip pre-destroyed transaction:\n"
+                            + transactionToString(transaction, mTransactionHandler));
+                    return;
+                }
+            }
+        }
+
+        if (DEBUG_RESOLVER) Slog.d(TAG, transactionToString(transaction, mTransactionHandler));
 
         executeCallbacks(transaction);
 
         executeLifecycleState(transaction);
         mPendingActions.clear();
-        log("End resolving transaction");
+        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
     }
 
     /** Cycle through all states requested by callbacks and execute them at proper times. */
     @VisibleForTesting
     public void executeCallbacks(ClientTransaction transaction) {
         final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
-        if (callbacks == null) {
+        if (callbacks == null || callbacks.isEmpty()) {
             // No callbacks to execute, return early.
             return;
         }
-        log("Resolving callbacks");
+        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callbacks in transaction");
 
         final IBinder token = transaction.getActivityToken();
         ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
@@ -97,12 +124,12 @@
         final int size = callbacks.size();
         for (int i = 0; i < size; ++i) {
             final ClientTransactionItem item = callbacks.get(i);
-            log("Resolving callback: " + item);
+            if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
             final int postExecutionState = item.getPostExecutionState();
             final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
                     item.getPostExecutionState());
             if (closestPreExecutionState != UNDEFINED) {
-                cycleToPath(r, closestPreExecutionState);
+                cycleToPath(r, closestPreExecutionState, transaction);
             }
 
             item.execute(mTransactionHandler, token, mPendingActions);
@@ -116,7 +143,7 @@
                 // Skip the very last transition and perform it by explicit state request instead.
                 final boolean shouldExcludeLastTransition =
                         i == lastCallbackRequestingState && finalState == postExecutionState;
-                cycleToPath(r, postExecutionState, shouldExcludeLastTransition);
+                cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction);
             }
         }
     }
@@ -128,10 +155,14 @@
             // No lifecycle request, return early.
             return;
         }
-        log("Resolving lifecycle state: " + lifecycleItem);
 
         final IBinder token = transaction.getActivityToken();
         final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+        if (DEBUG_RESOLVER) {
+            Slog.d(TAG, tId(transaction) + "Resolving lifecycle state: "
+                    + lifecycleItem + " for activity: "
+                    + getShortActivityName(token, mTransactionHandler));
+        }
 
         if (r == null) {
             // Ignore requests for non-existent client records for now.
@@ -139,7 +170,7 @@
         }
 
         // Cycle to the state right before the final requested state.
-        cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */);
+        cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);
 
         // Execute the final transition with proper parameters.
         lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
@@ -148,8 +179,8 @@
 
     /** Transition the client between states. */
     @VisibleForTesting
-    public void cycleToPath(ActivityClientRecord r, int finish) {
-        cycleToPath(r, finish, false /* excludeLastState */);
+    public void cycleToPath(ActivityClientRecord r, int finish, ClientTransaction transaction) {
+        cycleToPath(r, finish, false /* excludeLastState */, transaction);
     }
 
     /**
@@ -157,20 +188,30 @@
      * sequence. This is used when resolving lifecycle state request, when the last transition must
      * be performed with some specific parameters.
      */
-    private void cycleToPath(ActivityClientRecord r, int finish,
-            boolean excludeLastState) {
+    private void cycleToPath(ActivityClientRecord r, int finish, boolean excludeLastState,
+            ClientTransaction transaction) {
         final int start = r.getLifecycleState();
-        log("Cycle from: " + start + " to: " + finish + " excludeLastState:" + excludeLastState);
+        if (DEBUG_RESOLVER) {
+            Slog.d(TAG, tId(transaction) + "Cycle activity: "
+                    + getShortActivityName(r.token, mTransactionHandler)
+                    + " from: " + getStateName(start) + " to: " + getStateName(finish)
+                    + " excludeLastState: " + excludeLastState);
+        }
         final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
-        performLifecycleSequence(r, path);
+        performLifecycleSequence(r, path, transaction);
     }
 
     /** Transition the client through previously initialized state sequence. */
-    private void performLifecycleSequence(ActivityClientRecord r, IntArray path) {
+    private void performLifecycleSequence(ActivityClientRecord r, IntArray path,
+            ClientTransaction transaction) {
         final int size = path.size();
         for (int i = 0, state; i < size; i++) {
             state = path.get(i);
-            log("Transitioning to state: " + state);
+            if (DEBUG_RESOLVER) {
+                Slog.d(TAG, tId(transaction) + "Transitioning activity: "
+                        + getShortActivityName(r.token, mTransactionHandler)
+                        + " to state: " + getStateName(state));
+            }
             switch (state) {
                 case ON_CREATE:
                     mTransactionHandler.handleLaunchActivity(r, mPendingActions,
@@ -206,8 +247,4 @@
             }
         }
     }
-
-    private static void log(String message) {
-        if (DEBUG_RESOLVER) Slog.d(TAG, message);
-    }
 }
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 01b13a2..0ea8c3c 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -26,11 +26,16 @@
 import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
 import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
 
+import android.app.Activity;
 import android.app.ActivityThread.ActivityClientRecord;
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
 import android.util.IntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.List;
 
 /**
@@ -243,4 +248,73 @@
 
         return lastRequestingCallback;
     }
+
+    /** Dump transaction to string. */
+    static String transactionToString(ClientTransaction transaction,
+            ClientTransactionHandler transactionHandler) {
+        final StringWriter stringWriter = new StringWriter();
+        final PrintWriter pw = new PrintWriter(stringWriter);
+        final String prefix = tId(transaction);
+        transaction.dump(prefix, pw);
+        pw.append(prefix + "Target activity: ")
+                .println(getActivityName(transaction.getActivityToken(), transactionHandler));
+        return stringWriter.toString();
+    }
+
+    /** @return A string in format "tId:<transaction hashcode> ". */
+    static String tId(ClientTransaction transaction) {
+        return "tId:" + transaction.hashCode() + " ";
+    }
+
+    /** Get activity string name for provided token. */
+    static String getActivityName(IBinder token, ClientTransactionHandler transactionHandler) {
+        final Activity activity = getActivityForToken(token, transactionHandler);
+        if (activity != null) {
+            return activity.getComponentName().getClassName();
+        }
+        return "Not found for token: " + token;
+    }
+
+    /** Get short activity class name for provided token. */
+    static String getShortActivityName(IBinder token, ClientTransactionHandler transactionHandler) {
+        final Activity activity = getActivityForToken(token, transactionHandler);
+        if (activity != null) {
+            return activity.getComponentName().getShortClassName();
+        }
+        return "Not found for token: " + token;
+    }
+
+    private static Activity getActivityForToken(IBinder token,
+            ClientTransactionHandler transactionHandler) {
+        if (token == null) {
+            return null;
+        }
+        return transactionHandler.getActivity(token);
+    }
+
+    /** Get lifecycle state string name. */
+    static String getStateName(int state) {
+        switch (state) {
+            case UNDEFINED:
+                return "UNDEFINED";
+            case PRE_ON_CREATE:
+                return "PRE_ON_CREATE";
+            case ON_CREATE:
+                return "ON_CREATE";
+            case ON_START:
+                return "ON_START";
+            case ON_RESUME:
+                return "ON_RESUME";
+            case ON_PAUSE:
+                return "ON_PAUSE";
+            case ON_STOP:
+                return "ON_STOP";
+            case ON_DESTROY:
+                return "ON_DESTROY";
+            case ON_RESTART:
+                return "ON_RESTART";
+            default:
+                throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
+        }
+    }
 }
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 69852f3..6f73d02 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -33,7 +33,7 @@
     // Perms.
     void grantSlicePermission(String callingPkg, String toPkg, in Uri uri);
     void revokeSlicePermission(String callingPkg, String toPkg, in Uri uri);
-    int checkSlicePermission(in Uri uri, String pkg, int pid, int uid,
+    int checkSlicePermission(in Uri uri, String callingPkg, String pkg, int pid, int uid,
             in String[] autoGrantPermissions);
     void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
 }
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 26498e8..955093d 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -430,7 +430,8 @@
      */
     public @PermissionResult int checkSlicePermission(@NonNull Uri uri, int pid, int uid) {
         try {
-            return mService.checkSlicePermission(uri, null, pid, uid, null);
+            return mService.checkSlicePermission(uri, mContext.getPackageName(), null, pid, uid,
+                    null);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -487,7 +488,8 @@
             if (pkg == null) {
                 throw new SecurityException("No pkg specified");
             }
-            int result = mService.checkSlicePermission(uri, pkg, pid, uid, autoGrantPermissions);
+            int result = mService.checkSlicePermission(uri, mContext.getPackageName(), pkg, pid,
+                    uid, autoGrantPermissions);
             if (result == PERMISSION_DENIED) {
                 throw new SecurityException("User " + uid + " does not have slice permission for "
                         + uri + ".");
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index bdbe111..f624446 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -16,6 +16,8 @@
 
 package android.app.timedetector;
 
+import android.app.timedetector.TimeSignal;
+
 /**
  * System private API to comunicate with time detector service.
  *
@@ -30,5 +32,5 @@
  * {@hide}
  */
 interface ITimeDetectorService {
-  void stubbedCall();
+  void suggestTime(in TimeSignal timeSignal);
 }
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index ac1e223..052050d 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -16,6 +16,7 @@
 
 package android.app.timedetector;
 
+import android.annotation.NonNull;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.os.RemoteException;
@@ -40,15 +41,16 @@
     }
 
     /**
-     * Does nothing.
-     * TODO: Remove this when the service implementation is built out.
+     * Suggests the current time to the detector. The detector may ignore the signal if better
+     * signals are available such as those that come from more reliable sources or were
+     * determined more recently.
      */
-    public void stubbedCall() {
+    public void suggestTime(@NonNull TimeSignal timeSignal) {
         if (DEBUG) {
-            Log.d(TAG, "stubbedCall called");
+            Log.d(TAG, "suggestTime called: " + timeSignal);
         }
         try {
-            mITimeDetectorService.stubbedCall();
+            mITimeDetectorService.suggestTime(timeSignal);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/timedetector/TimeSignal.aidl b/core/java/android/app/timedetector/TimeSignal.aidl
new file mode 100644
index 0000000..d2ec357
--- /dev/null
+++ b/core/java/android/app/timedetector/TimeSignal.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.app.timedetector;
+
+parcelable TimeSignal;
\ No newline at end of file
diff --git a/core/java/android/app/timedetector/TimeSignal.java b/core/java/android/app/timedetector/TimeSignal.java
new file mode 100644
index 0000000..7ba03cc
--- /dev/null
+++ b/core/java/android/app/timedetector/TimeSignal.java
@@ -0,0 +1,110 @@
+/*
+ * 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.app.timedetector;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.TimestampedValue;
+
+import java.util.Objects;
+
+/**
+ * A time signal from a named source. The value consists of the number of milliseconds elapsed since
+ * 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime clock when that number was
+ * established. The elapsed realtime clock is considered accurate but volatile, so time signals
+ * must not be persisted across device resets.
+ *
+ * @hide
+ */
+public final class TimeSignal implements Parcelable {
+
+    public static final Parcelable.Creator<TimeSignal> CREATOR =
+            new Parcelable.Creator<TimeSignal>() {
+                public TimeSignal createFromParcel(Parcel in) {
+                    return TimeSignal.createFromParcel(in);
+                }
+
+                public TimeSignal[] newArray(int size) {
+                    return new TimeSignal[size];
+                }
+            };
+
+    public static final String SOURCE_ID_NITZ = "nitz";
+
+    private final String mSourceId;
+    private final TimestampedValue<Long> mUtcTime;
+
+    public TimeSignal(String sourceId, TimestampedValue<Long> utcTime) {
+        mSourceId = Objects.requireNonNull(sourceId);
+        mUtcTime = Objects.requireNonNull(utcTime);
+    }
+
+    private static TimeSignal createFromParcel(Parcel in) {
+        String sourceId = in.readString();
+        TimestampedValue<Long> utcTime =
+                TimestampedValue.readFromParcel(in, null /* classLoader */, Long.class);
+        return new TimeSignal(sourceId, utcTime);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mSourceId);
+        TimestampedValue.writeToParcel(dest, mUtcTime);
+    }
+
+    @NonNull
+    public String getSourceId() {
+        return mSourceId;
+    }
+
+    @NonNull
+    public TimestampedValue<Long> getUtcTime() {
+        return mUtcTime;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        TimeSignal that = (TimeSignal) o;
+        return Objects.equals(mSourceId, that.mSourceId)
+                && Objects.equals(mUtcTime, that.mUtcTime);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSourceId, mUtcTime);
+    }
+
+    @Override
+    public String toString() {
+        return "TimeSignal{"
+                + "mSourceId='" + mSourceId + '\''
+                + ", mUtcTime=" + mUtcTime
+                + '}';
+    }
+}
diff --git a/core/java/android/app/timezone/RulesState.aidl b/core/java/android/app/timezone/RulesState.aidl
index f789120..665220d 100644
--- a/core/java/android/app/timezone/RulesState.aidl
+++ b/core/java/android/app/timezone/RulesState.aidl
@@ -14,4 +14,6 @@
  * limitations under the License.
  */
 
+package android.app.timezone;
+
 parcelable RulesState;
\ No newline at end of file
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
new file mode 100644
index 0000000..ef2cbab
--- /dev/null
+++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
@@ -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 android.app.timezonedetector;
+
+/**
+ * System private API to comunicate with time zone detector service.
+ *
+ * <p>Used by parts of the Android system with signals associated with the device's time zone to
+ * provide information to the Time Zone Detector Service.
+ *
+ * <p>Use the {@link android.app.timezonedetector.TimeZoneDetector} class rather than going through
+ * this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService}
+ * for more complete documentation.
+ *
+ *
+ * {@hide}
+ */
+interface ITimeZoneDetectorService {
+  void stubbedCall();
+}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
new file mode 100644
index 0000000..be3c764
--- /dev/null
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.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 android.app.timezonedetector;
+
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.Log;
+
+/**
+ * The interface through which system components can send signals to the TimeZoneDetectorService.
+ * @hide
+ */
+@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
+public final class TimeZoneDetector {
+
+    private static final String TAG = "timezonedetector.TimeZoneDetector";
+    private static final boolean DEBUG = false;
+
+    private final ITimeZoneDetectorService mITimeZoneDetectorService;
+
+    public TimeZoneDetector() throws ServiceNotFoundException {
+        mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface(
+                ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE));
+
+    }
+    /**
+     * Does nothing.
+     * TODO: Remove this when the service implementation is built out.
+     */
+    public void stubbedCall() {
+        if (DEBUG) {
+            Log.d(TAG, "stubbedCall called");
+        }
+        try {
+            mITimeZoneDetectorService.stubbedCall();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 6d65e3e..9985cc0 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -17,6 +17,7 @@
 package android.app.trust;
 
 import android.app.trust.ITrustListener;
+import android.hardware.biometrics.BiometricSourceType;
 
 /**
  * System private API to comunicate with trust service.
@@ -34,6 +35,6 @@
     boolean isDeviceLocked(int userId);
     boolean isDeviceSecure(int userId);
     boolean isTrustUsuallyManaged(int userId);
-    void unlockedByFingerprintForUser(int userId);
-    void clearAllFingerprints();
+    void unlockedByBiometricForUser(int userId, in BiometricSourceType source);
+    void clearAllBiometricRecognized(in BiometricSourceType target);
 }
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 8ab0b70..fb27bed 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -20,6 +20,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.hardware.biometrics.BiometricSourceType;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -195,26 +196,28 @@
     }
 
     /**
-     * Updates the trust state for the user due to the user unlocking via fingerprint.
-     * Should only be called if user authenticated via fingerprint and bouncer can be skipped.
+     * Updates the trust state for the user due to the user unlocking via a biometric sensor.
+     * Should only be called if user authenticated via fingerprint, face, or iris and bouncer
+     * can be skipped.
+     *
      * @param userId
      */
     @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
-    public void unlockedByFingerprintForUser(int userId) {
+    public void unlockedByBiometricForUser(int userId, BiometricSourceType source) {
         try {
-            mService.unlockedByFingerprintForUser(userId);
+            mService.unlockedByBiometricForUser(userId, source);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Clears authenticated fingerprints for all users.
+     * Clears authentication by the specified biometric type for all users.
      */
     @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
-    public void clearAllFingerprints() {
+    public void clearAllBiometricRecognized(BiometricSourceType source) {
         try {
-            mService.clearAllFingerprints();
+            mService.clearAllBiometricRecognized(source);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 94fd138..966f902 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -21,6 +21,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -117,6 +118,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @UnsupportedAppUsage
     public static final String ACTION_ACTIVE_DEVICE_CHANGED =
             "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED";
 
@@ -137,6 +139,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @UnsupportedAppUsage
     public static final String ACTION_CODEC_CONFIG_CHANGED =
             "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED";
 
@@ -160,6 +163,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1;
 
     /**
@@ -167,6 +171,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0;
 
     /**
@@ -174,6 +179,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int OPTIONAL_CODECS_SUPPORTED = 1;
 
     /**
@@ -182,6 +188,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1;
 
     /**
@@ -189,6 +196,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int OPTIONAL_CODECS_PREF_DISABLED = 0;
 
     /**
@@ -196,6 +204,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int OPTIONAL_CODECS_PREF_ENABLED = 1;
 
     private Context mContext;
@@ -268,6 +277,7 @@
         return true;
     }
 
+    @UnsupportedAppUsage
     /*package*/ void close() {
         mServiceListener = null;
         IBluetoothManager mgr = mAdapter.getBluetoothManager();
@@ -315,6 +325,7 @@
      * @return false on immediate error, true otherwise
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
         try {
@@ -357,6 +368,7 @@
      * @return false on immediate error, true otherwise
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
         try {
@@ -460,6 +472,7 @@
      * @return false on immediate error, true otherwise
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean setActiveDevice(@Nullable BluetoothDevice device) {
         if (DBG) log("setActiveDevice(" + device + ")");
         try {
@@ -490,6 +503,7 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     @Nullable
+    @UnsupportedAppUsage
     public BluetoothDevice getActiveDevice() {
         if (VDBG) log("getActiveDevice()");
         try {
@@ -556,6 +570,7 @@
      * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @UnsupportedAppUsage
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
         try {
@@ -671,6 +686,7 @@
      * @return the current codec status
      * @hide
      */
+    @UnsupportedAppUsage
     public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
         try {
@@ -698,6 +714,7 @@
      * @param codecConfig the codec configuration preference
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCodecConfigPreference(BluetoothDevice device,
                                          BluetoothCodecConfig codecConfig) {
         if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
@@ -723,6 +740,7 @@
      * active A2DP Bluetooth device.
      * @hide
      */
+    @UnsupportedAppUsage
     public void enableOptionalCodecs(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
         enableDisableOptionalCodecs(device, true);
@@ -735,6 +753,7 @@
      * active A2DP Bluetooth device.
      * @hide
      */
+    @UnsupportedAppUsage
     public void disableOptionalCodecs(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
         enableDisableOptionalCodecs(device, false);
@@ -775,6 +794,7 @@
      * OPTIONAL_CODECS_SUPPORTED.
      * @hide
      */
+    @UnsupportedAppUsage
     public int supportsOptionalCodecs(BluetoothDevice device) {
         try {
             mServiceLock.readLock().lock();
@@ -799,6 +819,7 @@
      * OPTIONAL_CODECS_PREF_DISABLED.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getOptionalCodecsEnabled(BluetoothDevice device) {
         try {
             mServiceLock.readLock().lock();
@@ -824,6 +845,7 @@
      * OPTIONAL_CODECS_PREF_DISABLED.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
         try {
             if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
@@ -854,6 +876,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static String stateToString(int state) {
         switch (state) {
             case STATE_DISCONNECTED:
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index 13f0aaf..fda2f89 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -278,6 +279,7 @@
      * @return false on immediate error, true otherwise
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
         final IBluetoothA2dpSink service = mService;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 1b9e27c..3c22905 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -23,6 +23,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
 import android.bluetooth.le.BluetoothLeAdvertiser;
 import android.bluetooth.le.BluetoothLeScanner;
@@ -633,6 +634,7 @@
     private static PeriodicAdvertisingManager sPeriodicAdvertisingManager;
 
     private final IBluetoothManager mManagerService;
+    @UnsupportedAppUsage
     private IBluetooth mService;
     private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
 
@@ -988,6 +990,7 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     @AdapterState
+    @UnsupportedAppUsage
     public int getLeState() {
         int state = BluetoothAdapter.STATE_OFF;
 
@@ -1098,6 +1101,7 @@
      * @return true to indicate adapter shutdown has begun, or false on immediate error
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean disable(boolean persist) {
 
         try {
@@ -1149,6 +1153,7 @@
      * @return true to indicate that the config file was successfully cleared
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean factoryReset() {
         try {
             mServiceLock.readLock().lock();
@@ -1172,6 +1177,7 @@
      * @return the UUIDs supported by the local Bluetooth Adapter.
      * @hide
      */
+    @UnsupportedAppUsage
     public ParcelUuid[] getUuids() {
         if (getState() != STATE_ON) {
             return null;
@@ -1438,6 +1444,7 @@
      * @return true if the scan mode was set, false otherwise
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean setScanMode(@ScanMode int mode, int duration) {
         if (getState() != STATE_ON) {
             return false;
@@ -1456,6 +1463,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public boolean setScanMode(int mode) {
         if (getState() != STATE_ON) {
             return false;
@@ -1465,6 +1473,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public int getDiscoverableTimeout() {
         if (getState() != STATE_ON) {
             return -1;
@@ -1483,6 +1492,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public void setDiscoverableTimeout(int timeout) {
         if (getState() != STATE_ON) {
             return;
@@ -2007,6 +2017,7 @@
      * #STATE_CONNECTING} or {@link #STATE_DISCONNECTED}
      * @hide
      */
+    @UnsupportedAppUsage
     public int getConnectionState() {
         if (getState() != STATE_ON) {
             return BluetoothAdapter.STATE_DISCONNECTED;
@@ -2094,6 +2105,7 @@
      * permissions, or channel in use.
      * @hide
      */
+    @UnsupportedAppUsage
     public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm,
             boolean min16DigitPin) throws IOException {
         BluetoothServerSocket socket =
@@ -2206,6 +2218,7 @@
      * permissions, or channel in use.
      * @hide
      */
+    @UnsupportedAppUsage
     public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid)
             throws IOException {
         return createNewRfcommSocketAndRecord(name, uuid, false, true);
@@ -2749,6 +2762,7 @@
         return true;
     }
 
+    @UnsupportedAppUsage
     /*package*/ IBluetoothManager getBluetoothManager() {
         return mManagerService;
     }
@@ -2756,6 +2770,7 @@
     private final ArrayList<IBluetoothManagerCallback> mProxyServiceStateCallbacks =
             new ArrayList<IBluetoothManagerCallback>();
 
+    @UnsupportedAppUsage
     /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) {
         synchronized (mProxyServiceStateCallbacks) {
             if (cb == null) {
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index f22ea6e..3a78cbd 100755
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -16,6 +16,8 @@
 
 package android.bluetooth;
 
+import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -63,6 +65,7 @@
     private final int mClass;
 
     /** @hide */
+    @UnsupportedAppUsage
     public BluetoothClass(int classInt) {
         mClass = classInt;
     }
@@ -291,6 +294,7 @@
      *
      * @hide
      */
+    @TestApi
     public int getClassOfDevice() {
         return mClass;
     }
@@ -322,8 +326,10 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public static final int PROFILE_HEADSET = 0;
     /** @hide */
+    @UnsupportedAppUsage
     public static final int PROFILE_A2DP = 1;
     /** @hide */
     public static final int PROFILE_OPP = 2;
@@ -346,6 +352,7 @@
      * @return True if this device might support specified profile.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean doesClassMatch(int profile) {
         if (profile == PROFILE_A2DP) {
             if (hasService(Service.RENDER)) {
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
index e3a6e06..79c0a3a 100644
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -32,34 +33,58 @@
     // Add an entry for each source codec here.
     // NOTE: The values should be same as those listed in the following file:
     //   hardware/libhardware/include/hardware/bt_av.h
+    @UnsupportedAppUsage
     public static final int SOURCE_CODEC_TYPE_SBC = 0;
+    @UnsupportedAppUsage
     public static final int SOURCE_CODEC_TYPE_AAC = 1;
+    @UnsupportedAppUsage
     public static final int SOURCE_CODEC_TYPE_APTX = 2;
+    @UnsupportedAppUsage
     public static final int SOURCE_CODEC_TYPE_APTX_HD = 3;
+    @UnsupportedAppUsage
     public static final int SOURCE_CODEC_TYPE_LDAC = 4;
+    @UnsupportedAppUsage
     public static final int SOURCE_CODEC_TYPE_MAX = 5;
 
+    @UnsupportedAppUsage
     public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
 
+    @UnsupportedAppUsage
     public static final int CODEC_PRIORITY_DISABLED = -1;
+    @UnsupportedAppUsage
     public static final int CODEC_PRIORITY_DEFAULT = 0;
+    @UnsupportedAppUsage
     public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000;
 
+    @UnsupportedAppUsage
     public static final int SAMPLE_RATE_NONE = 0;
+    @UnsupportedAppUsage
     public static final int SAMPLE_RATE_44100 = 0x1 << 0;
+    @UnsupportedAppUsage
     public static final int SAMPLE_RATE_48000 = 0x1 << 1;
+    @UnsupportedAppUsage
     public static final int SAMPLE_RATE_88200 = 0x1 << 2;
+    @UnsupportedAppUsage
     public static final int SAMPLE_RATE_96000 = 0x1 << 3;
+    @UnsupportedAppUsage
     public static final int SAMPLE_RATE_176400 = 0x1 << 4;
+    @UnsupportedAppUsage
     public static final int SAMPLE_RATE_192000 = 0x1 << 5;
 
+    @UnsupportedAppUsage
     public static final int BITS_PER_SAMPLE_NONE = 0;
+    @UnsupportedAppUsage
     public static final int BITS_PER_SAMPLE_16 = 0x1 << 0;
+    @UnsupportedAppUsage
     public static final int BITS_PER_SAMPLE_24 = 0x1 << 1;
+    @UnsupportedAppUsage
     public static final int BITS_PER_SAMPLE_32 = 0x1 << 2;
 
+    @UnsupportedAppUsage
     public static final int CHANNEL_MODE_NONE = 0;
+    @UnsupportedAppUsage
     public static final int CHANNEL_MODE_MONO = 0x1 << 0;
+    @UnsupportedAppUsage
     public static final int CHANNEL_MODE_STEREO = 0x1 << 1;
 
     private final int mCodecType;
@@ -72,6 +97,7 @@
     private final long mCodecSpecific3;
     private final long mCodecSpecific4;
 
+    @UnsupportedAppUsage
     public BluetoothCodecConfig(int codecType, int codecPriority,
             int sampleRate, int bitsPerSample,
             int channelMode, long codecSpecific1,
@@ -276,6 +302,7 @@
      *
      * @return the codec type
      */
+    @UnsupportedAppUsage
     public int getCodecType() {
         return mCodecType;
     }
@@ -296,6 +323,7 @@
      *
      * @return the codec priority
      */
+    @UnsupportedAppUsage
     public int getCodecPriority() {
         return mCodecPriority;
     }
@@ -307,6 +335,7 @@
      *
      * @param codecPriority the codec priority
      */
+    @UnsupportedAppUsage
     public void setCodecPriority(int codecPriority) {
         mCodecPriority = codecPriority;
     }
@@ -324,6 +353,7 @@
      *
      * @return the codec sample rate
      */
+    @UnsupportedAppUsage
     public int getSampleRate() {
         return mSampleRate;
     }
@@ -338,6 +368,7 @@
      *
      * @return the codec bits per sample
      */
+    @UnsupportedAppUsage
     public int getBitsPerSample() {
         return mBitsPerSample;
     }
@@ -351,6 +382,7 @@
      *
      * @return the codec channel mode
      */
+    @UnsupportedAppUsage
     public int getChannelMode() {
         return mChannelMode;
     }
@@ -360,6 +392,7 @@
      *
      * @return a codec specific value1.
      */
+    @UnsupportedAppUsage
     public long getCodecSpecific1() {
         return mCodecSpecific1;
     }
@@ -369,6 +402,7 @@
      *
      * @return a codec specific value2
      */
+    @UnsupportedAppUsage
     public long getCodecSpecific2() {
         return mCodecSpecific2;
     }
@@ -378,6 +412,7 @@
      *
      * @return a codec specific value3
      */
+    @UnsupportedAppUsage
     public long getCodecSpecific3() {
         return mCodecSpecific3;
     }
@@ -387,6 +422,7 @@
      *
      * @return a codec specific value4
      */
+    @UnsupportedAppUsage
     public long getCodecSpecific4() {
         return mCodecSpecific4;
     }
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
index 3a05e70..78560d2 100644
--- a/core/java/android/bluetooth/BluetoothCodecStatus.java
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -37,6 +38,7 @@
      * This extra represents the current codec status of the A2DP
      * profile.
      */
+    @UnsupportedAppUsage
     public static final String EXTRA_CODEC_STATUS =
             "android.bluetooth.codec.extra.CODEC_STATUS";
 
@@ -137,6 +139,7 @@
      *
      * @return the current codec configuration
      */
+    @UnsupportedAppUsage
     public BluetoothCodecConfig getCodecConfig() {
         return mCodecConfig;
     }
@@ -146,6 +149,7 @@
      *
      * @return an array with the codecs local capabilities
      */
+    @UnsupportedAppUsage
     public BluetoothCodecConfig[] getCodecsLocalCapabilities() {
         return mCodecsLocalCapabilities;
     }
@@ -155,6 +159,7 @@
      *
      * @return an array with the codecs selectable capabilities
      */
+    @UnsupportedAppUsage
     public BluetoothCodecConfig[] getCodecsSelectableCapabilities() {
         return mCodecsSelectableCapabilities;
     }
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 7a6b72e..818a749 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -21,6 +21,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Handler;
 import android.os.Parcel;
@@ -115,6 +116,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @UnsupportedAppUsage
     public static final String ACTION_DISAPPEARED =
             "android.bluetooth.device.action.DISAPPEARED";
 
@@ -186,6 +188,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @UnsupportedAppUsage
     public static final String ACTION_ALIAS_CHANGED =
             "android.bluetooth.device.action.ALIAS_CHANGED";
 
@@ -306,6 +309,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON";
 
     /**
@@ -346,6 +350,7 @@
 
     /** @hide */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @UnsupportedAppUsage
     public static final String ACTION_SDP_RECORD =
             "android.bluetooth.device.action.SDP_RECORD";
 
@@ -390,6 +395,7 @@
             "android.bluetooth.device.action.PAIRING_REQUEST";
     /** @hide */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @UnsupportedAppUsage
     public static final String ACTION_PAIRING_CANCEL =
             "android.bluetooth.device.action.PAIRING_CANCEL";
 
@@ -481,6 +487,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int UNBOND_REASON_AUTH_FAILED = 1;
 
     /**
@@ -489,6 +496,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int UNBOND_REASON_AUTH_REJECTED = 2;
 
     /**
@@ -503,6 +511,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4;
 
     /**
@@ -510,6 +519,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5;
 
     /**
@@ -517,6 +527,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int UNBOND_REASON_AUTH_TIMEOUT = 6;
 
     /**
@@ -524,6 +535,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7;
 
     /**
@@ -532,6 +544,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8;
 
     /**
@@ -610,6 +623,7 @@
             "android.bluetooth.device.extra.SDP_RECORD";
 
     /** @hide */
+    @UnsupportedAppUsage
     public static final String EXTRA_SDP_SEARCH_STATUS =
             "android.bluetooth.device.extra.SDP_SEARCH_STATUS";
     /**
@@ -720,6 +734,7 @@
     private final String mAddress;
 
     /*package*/
+    @UnsupportedAppUsage
     static IBluetooth getService() {
         synchronized (BluetoothDevice.class) {
             if (sService == null) {
@@ -763,6 +778,7 @@
      * @throws IllegalArgumentException address is invalid
      * @hide
      */
+    @UnsupportedAppUsage
     /*package*/ BluetoothDevice(String address) {
         getService();  // ensures sService is initialized
         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
@@ -887,6 +903,7 @@
      * @return the Bluetooth alias, or null if no alias or there was a problem
      * @hide
      */
+    @UnsupportedAppUsage
     public String getAlias() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -911,6 +928,7 @@
      * @return true on success, false on error
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean setAlias(String alias) {
         final IBluetooth service = sService;
         if (service == null) {
@@ -934,6 +952,7 @@
      * @see #getAlias()
      * @see #getName()
      */
+    @UnsupportedAppUsage
     public String getAliasName() {
         String name = getAlias();
         if (name == null) {
@@ -952,6 +971,7 @@
      * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @UnsupportedAppUsage
     public int getBatteryLevel() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1010,6 +1030,7 @@
      * @throws IllegalArgumentException if an invalid transport was specified
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean createBond(int transport) {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1063,6 +1084,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public boolean isBondingInitiatedLocally() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1355,6 +1377,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public boolean setPasskey(int passkey) {
         //TODO(BT)
         /*
@@ -1395,6 +1418,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public boolean cancelPairingUserInput() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1410,6 +1434,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public boolean isBluetoothDock() {
         // TODO(BT)
         /*
@@ -1435,6 +1460,7 @@
      * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getPhonebookAccessPermission() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1479,6 +1505,7 @@
      * {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getMessageAccessPermission() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1501,6 +1528,7 @@
      * @return Whether the value has been successfully set.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean setMessageAccessPermission(int value) {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1543,6 +1571,7 @@
      * @return Whether the value has been successfully set.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean setSimAccessPermission(int value) {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1581,6 +1610,7 @@
      * permissions
      * @hide
      */
+    @UnsupportedAppUsage
     public BluetoothSocket createRfcommSocket(int channel) throws IOException {
         if (!isBluetoothEnabled()) {
             Log.e(TAG, "Bluetooth is not enabled");
@@ -1733,6 +1763,7 @@
      * permissions.
      * @hide
      */
+    @UnsupportedAppUsage
     public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
         if (!isBluetoothEnabled()) {
             Log.e(TAG, "Bluetooth is not enabled");
@@ -1752,6 +1783,7 @@
      * permissions.
      * @hide
      */
+    @UnsupportedAppUsage
     public BluetoothSocket createScoSocket() throws IOException {
         if (!isBluetoothEnabled()) {
             Log.e(TAG, "Bluetooth is not enabled");
@@ -1769,6 +1801,7 @@
      * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin.
      * @hide
      */
+    @UnsupportedAppUsage
     public static byte[] convertPinToBytes(String pin) {
         if (pin == null) {
             return null;
@@ -1900,6 +1933,7 @@
      * operations.
      * @hide
      */
+    @UnsupportedAppUsage
     public BluetoothGatt connectGatt(Context context, boolean autoConnect,
             BluetoothGattCallback callback, int transport,
             boolean opportunistic, int phy, Handler handler) {
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 457119d..78248ef 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Handler;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
@@ -41,16 +42,23 @@
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
+    @UnsupportedAppUsage
     private IBluetoothGatt mService;
+    @UnsupportedAppUsage
     private volatile BluetoothGattCallback mCallback;
     private Handler mHandler;
+    @UnsupportedAppUsage
     private int mClientIf;
     private BluetoothDevice mDevice;
+    @UnsupportedAppUsage
     private boolean mAutoConnect;
+    @UnsupportedAppUsage
     private int mAuthRetryState;
     private int mConnState;
     private final Object mStateLock = new Object();
+    @UnsupportedAppUsage
     private Boolean mDeviceBusy = false;
+    @UnsupportedAppUsage
     private int mTransport;
     private int mPhy;
     private boolean mOpportunistic;
@@ -810,6 +818,7 @@
     /**
      * Unregister the current application and callbacks.
      */
+    @UnsupportedAppUsage
     private void unregisterApp() {
         if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
         if (mService == null || mClientIf == 0) return;
@@ -845,6 +854,7 @@
      * automatically connect as soon as the remote device becomes available (true).
      * @return true, if the connection attempt was initiated successfully
      */
+    @UnsupportedAppUsage
     /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
             Handler handler) {
         if (DBG) {
@@ -1407,6 +1417,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean refresh() {
         if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
         if (mService == null || mClientIf == 0) return false;
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index 243ad35..6d46b3a 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -15,6 +15,7 @@
  */
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.ParcelUuid;
 import android.os.Parcelable;
@@ -182,6 +183,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     protected int mInstance;
 
     /**
@@ -218,6 +220,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     protected BluetoothGattService mService;
 
     /**
@@ -381,6 +384,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     /*package*/ void setService(BluetoothGattService service) {
         mService = service;
     }
@@ -464,6 +468,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setKeySize(int keySize) {
         mKeySize = keySize;
     }
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
index 217a5ab..3ffbb9e 100644
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.ParcelUuid;
 import android.os.Parcelable;
@@ -100,6 +101,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     protected int mInstance;
 
     /**
@@ -114,6 +116,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     protected BluetoothGattCharacteristic mCharacteristic;
 
     /**
@@ -205,6 +208,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) {
         mCharacteristic = characteristic;
     }
diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java
index ce1dc1c..8e740ee 100644
--- a/core/java/android/bluetooth/BluetoothGattService.java
+++ b/core/java/android/bluetooth/BluetoothGattService.java
@@ -15,6 +15,7 @@
  */
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.ParcelUuid;
 import android.os.Parcelable;
@@ -48,6 +49,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     protected BluetoothDevice mDevice;
 
     /**
@@ -265,6 +267,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setInstanceId(int instanceId) {
         mInstanceId = instanceId;
     }
@@ -382,6 +385,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setAdvertisePreferred(boolean advertisePreferred) {
         mAdvertisePreferred = advertisePreferred;
     }
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 0c91a20..636b1b9 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -22,6 +22,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Binder;
@@ -110,6 +111,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @UnsupportedAppUsage
     public static final String ACTION_ACTIVE_DEVICE_CHANGED =
             "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED";
 
@@ -401,6 +403,7 @@
      * results once close() has been called. Multiple invocations of close()
      * are ok.
      */
+    @UnsupportedAppUsage
     /*package*/ void close() {
         if (VDBG) log("close()");
 
@@ -602,6 +605,7 @@
      * @return priority of the device
      * @hide
      */
+    @UnsupportedAppUsage
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
         final IBluetoothHeadset service = mService;
@@ -719,6 +723,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public int getAudioState(BluetoothDevice device) {
         if (VDBG) log("getAudioState");
         final IBluetoothHeadset service = mService;
@@ -846,6 +851,7 @@
      * @return false if there was some error such as there is no active headset
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean connectAudio() {
         final IBluetoothHeadset service = mService;
         if (service != null && isEnabled()) {
@@ -872,6 +878,7 @@
      * @return false if audio is not connected, or on error, true otherwise
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean disconnectAudio() {
         final IBluetoothHeadset service = mService;
         if (service != null && isEnabled()) {
@@ -909,6 +916,7 @@
      * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @UnsupportedAppUsage
     public boolean startScoUsingVirtualVoiceCall() {
         if (DBG) log("startScoUsingVirtualVoiceCall()");
         final IBluetoothHeadset service = mService;
@@ -938,6 +946,7 @@
      * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @UnsupportedAppUsage
     public boolean stopScoUsingVirtualVoiceCall() {
         if (DBG) log("stopScoUsingVirtualVoiceCall()");
         final IBluetoothHeadset service = mService;
@@ -962,6 +971,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
             int type) {
         final IBluetoothHeadset service = mService;
@@ -1060,6 +1070,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
+    @UnsupportedAppUsage
     public boolean setActiveDevice(@Nullable BluetoothDevice device) {
         if (DBG) {
             Log.d(TAG, "setActiveDevice: " + device);
@@ -1089,6 +1100,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.BLUETOOTH)
+    @UnsupportedAppUsage
     public BluetoothDevice getActiveDevice() {
         if (VDBG) {
             Log.d(TAG, "getActiveDevice");
@@ -1163,6 +1175,7 @@
         }
     };
 
+    @UnsupportedAppUsage
     private boolean isEnabled() {
         return mAdapter.getState() == BluetoothAdapter.STATE_ON;
     }
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 397b906..ec18d42 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -476,6 +477,7 @@
      * @return <code>true</code> if command has been issued successfully; <code>false</code>
      * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent.
      */
+    @UnsupportedAppUsage
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
         final IBluetoothHeadsetClient service = mService;
@@ -498,6 +500,7 @@
      * @return <code>true</code> if command has been issued successfully; <code>false</code>
      * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent.
      */
+    @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
         final IBluetoothHeadsetClient service = mService;
@@ -720,6 +723,7 @@
      * @return <code>true</code> if command has been issued successfully; <code>false</code>
      * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
      */
+    @UnsupportedAppUsage
     public boolean acceptCall(BluetoothDevice device, int flag) {
         if (DBG) log("acceptCall()");
         final IBluetoothHeadsetClient service = mService;
@@ -766,6 +770,7 @@
      * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is not
      * supported.</p>
      */
+    @UnsupportedAppUsage
     public boolean rejectCall(BluetoothDevice device) {
         if (DBG) log("rejectCall()");
         final IBluetoothHeadsetClient service = mService;
@@ -943,6 +948,7 @@
      *
      * Note: This is an internal function and shouldn't be exposed
      */
+    @UnsupportedAppUsage
     public int getAudioState(BluetoothDevice device) {
         if (VDBG) log("getAudioState");
         final IBluetoothHeadsetClient service = mService;
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
index d46b2e3..e02a2f4 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -143,6 +144,7 @@
      *
      * @return call id.
      */
+    @UnsupportedAppUsage
     public int getId() {
         return mId;
     }
@@ -162,6 +164,7 @@
      *
      * @return state of this particular phone call.
      */
+    @UnsupportedAppUsage
     public int getState() {
         return mState;
     }
@@ -171,6 +174,7 @@
      *
      * @return string representing phone number.
      */
+    @UnsupportedAppUsage
     public String getNumber() {
         return mNumber;
     }
@@ -189,6 +193,7 @@
      *
      * @return <code>true</code> if call is a multi party call, <code>false</code> otherwise.
      */
+    @UnsupportedAppUsage
     public boolean isMultiParty() {
         return mMultiParty;
     }
@@ -198,6 +203,7 @@
      *
      * @return <code>true</code> if its outgoing call, <code>false</code> otherwise.
      */
+    @UnsupportedAppUsage
     public boolean isOutgoing() {
         return mOutgoing;
     }
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 159e165..606f00a 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -21,6 +21,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -108,6 +109,7 @@
      * receive.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @UnsupportedAppUsage
     public static final String ACTION_ACTIVE_DEVICE_CHANGED =
             "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED";
 
@@ -401,6 +403,7 @@
      * @return false on immediate error, true otherwise
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean setActiveDevice(@Nullable BluetoothDevice device) {
         if (DBG) log("setActiveDevice(" + device + ")");
         try {
@@ -432,6 +435,7 @@
      * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @UnsupportedAppUsage
     public List<BluetoothDevice> getActiveDevices() {
         if (VDBG) log("getActiveDevices()");
         try {
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 0fa1d5d..98c23c6 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -233,6 +234,7 @@
      * @param device Remote Bluetooth Device
      * @return false on error, true otherwise
      */
+    @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
         final IBluetoothMap service = mService;
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 4f21d93..183be5f 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -358,6 +359,7 @@
      * @param deliveredIntent intent issued when message is delivered
      * @return true if the message is enqueued, false on error
      */
+    @UnsupportedAppUsage
     public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
             PendingIntent sentIntent, PendingIntent deliveredIntent) {
         if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 9f401eb..58be732 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -129,6 +130,7 @@
      * Create a BluetoothPan proxy object for interacting with the local
      * Bluetooth Service which handles the Pan profile
      */
+    @UnsupportedAppUsage
     /*package*/ BluetoothPan(Context context, ServiceListener l) {
         mContext = context;
         mServiceListener = l;
@@ -142,6 +144,7 @@
         doBind();
     }
 
+    @UnsupportedAppUsage
     boolean doBind() {
         Intent intent = new Intent(IBluetoothPan.class.getName());
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
@@ -154,6 +157,7 @@
         return true;
     }
 
+    @UnsupportedAppUsage
     /*package*/ void close() {
         if (VDBG) log("close()");
 
@@ -236,6 +240,7 @@
      * @return false on immediate error, true otherwise
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
         final IBluetoothPan service = mPanService;
@@ -276,6 +281,7 @@
      * @return false on immediate error, true otherwise
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
         final IBluetoothPan service = mPanService;
@@ -348,6 +354,7 @@
         return BluetoothProfile.STATE_DISCONNECTED;
     }
 
+    @UnsupportedAppUsage
     public void setBluetoothTethering(boolean value) {
         if (DBG) log("setBluetoothTethering(" + value + ")");
         final IBluetoothPan service = mPanService;
@@ -360,6 +367,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public boolean isTetheringOn() {
         if (VDBG) log("isTetheringOn()");
         final IBluetoothPan service = mPanService;
@@ -392,14 +400,17 @@
         }
     };
 
+    @UnsupportedAppUsage
     private boolean isEnabled() {
         return mAdapter.getState() == BluetoothAdapter.STATE_ON;
     }
 
+    @UnsupportedAppUsage
     private static boolean isValidDevice(BluetoothDevice device) {
         return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
     }
 
+    @UnsupportedAppUsage
     private static void log(String msg) {
         Log.d(TAG, msg);
     }
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index c60e9e0..ae264e1 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -17,6 +17,7 @@
 package android.bluetooth;
 
 import android.annotation.SdkConstant;
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -281,6 +282,7 @@
      */
     // TODO: This is currently being used by SettingsLib and will be used in the future.
     // TODO: Must specify target device. Implement this in the service.
+    @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
         log("disconnect()");
         final IBluetoothPbap service = mService;
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 6aeb94d..9777b5c 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -20,6 +20,7 @@
 import android.Manifest;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 
 import java.util.List;
 
@@ -85,6 +86,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     int PAN = 5;
 
     /**
@@ -122,6 +124,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     int A2DP_SINK = 11;
 
     /**
@@ -129,6 +132,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     int AVRCP_CONTROLLER = 12;
 
     /**
@@ -192,6 +196,7 @@
      *
      * @hide
      **/
+    @UnsupportedAppUsage
     int PRIORITY_AUTO_CONNECT = 1000;
 
     /**
@@ -217,6 +222,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     int PRIORITY_UNDEFINED = -1;
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index c51e39a..1b73206 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -280,6 +281,7 @@
      * @return false on error, true otherwise
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
         final IBluetoothSap service = mService;
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index ebb7f18..ba4b5a5 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Handler;
 import android.os.ParcelUuid;
 import android.util.Log;
@@ -69,6 +70,7 @@
 
     private static final String TAG = "BluetoothServerSocket";
     private static final boolean DBG = false;
+    @UnsupportedAppUsage
     /*package*/ final BluetoothSocket mSocket;
     private Handler mHandler;
     private int mMessage;
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 09a5b59..780f896 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.net.LocalSocket;
 import android.os.ParcelFileDescriptor;
 import android.os.ParcelUuid;
@@ -110,6 +111,7 @@
     public static final int TYPE_L2CAP_LE = 4;
 
     /*package*/ static final int EBADFD = 77;
+    @UnsupportedAppUsage
     /*package*/ static final int EADDRINUSE = 98;
 
     /*package*/ static final int SEC_FLAG_ENCRYPT = 1;
@@ -129,10 +131,13 @@
     private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */
     private boolean mAuthMitm = false;   /* when true Man-in-the-middle protection will be enabled*/
     private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */
+    @UnsupportedAppUsage
     private ParcelFileDescriptor mPfd;
+    @UnsupportedAppUsage
     private LocalSocket mSocket;
     private InputStream mSocketIS;
     private OutputStream mSocketOS;
+    @UnsupportedAppUsage
     private int mPort;  /* RFCOMM channel or L2CAP psm */
     private int mFd;
     private String mServiceName;
@@ -517,6 +522,7 @@
      *
      * @throws IOException if an i/o error occurs.
      */
+    @UnsupportedAppUsage
     /*package*/ void flush() throws IOException {
         if (mSocketOS == null) throw new IOException("flush is called on null OutputStream");
         if (VDBG) Log.d(TAG, "flush: " + mSocketOS);
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 605dbd2..fdbfec0 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.ParcelUuid;
 
 import java.nio.ByteBuffer;
@@ -37,16 +38,20 @@
      * The following 128 bit values are calculated as:
      *  uuid * 2^96 + BASE_UUID
      */
+    @UnsupportedAppUsage
     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
     public static final ParcelUuid AdvAudioDist =
             ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB");
+    @UnsupportedAppUsage
     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
     public static final ParcelUuid Handsfree =
             ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid Handsfree_AG =
@@ -55,20 +60,24 @@
             ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid AvrcpTarget =
             ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB");
+    @UnsupportedAppUsage
     public static final ParcelUuid ObexObjectPush =
             ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
     public static final ParcelUuid Hid =
             ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb");
+    @UnsupportedAppUsage
     public static final ParcelUuid Hogp =
             ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb");
     public static final ParcelUuid PANU =
             ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB");
+    @UnsupportedAppUsage
     public static final ParcelUuid NAP =
             ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid BNEP =
             ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid PBAP_PCE =
             ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB");
+    @UnsupportedAppUsage
     public static final ParcelUuid PBAP_PSE =
             ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid MAP =
@@ -92,10 +101,12 @@
     /** Length of bytes for 128 bit UUID */
     public static final int UUID_BYTES_128_BIT = 16;
 
+    @UnsupportedAppUsage
     public static final ParcelUuid[] RESERVED_UUIDS = {
             AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
             ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP};
 
+    @UnsupportedAppUsage
     public static boolean isAudioSource(ParcelUuid uuid) {
         return uuid.equals(AudioSource);
     }
@@ -104,6 +115,7 @@
         return uuid.equals(AudioSink);
     }
 
+    @UnsupportedAppUsage
     public static boolean isAdvAudioDist(ParcelUuid uuid) {
         return uuid.equals(AdvAudioDist);
     }
@@ -120,6 +132,7 @@
         return uuid.equals(AvrcpController);
     }
 
+    @UnsupportedAppUsage
     public static boolean isAvrcpTarget(ParcelUuid uuid) {
         return uuid.equals(AvrcpTarget);
     }
@@ -162,6 +175,7 @@
      * @param uuidArray - Array of ParcelUuids
      * @param uuid
      */
+    @UnsupportedAppUsage
     public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) {
         if ((uuidArray == null || uuidArray.length == 0) && uuid == null) {
             return true;
@@ -183,6 +197,7 @@
      * @param uuidA - List of ParcelUuids
      * @param uuidB - List of ParcelUuids
      */
+    @UnsupportedAppUsage
     public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) {
         if (uuidA == null && uuidB == null) return true;
 
@@ -330,6 +345,7 @@
      * @param parcelUuid
      * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise.
      */
+    @UnsupportedAppUsage
     public static boolean is16BitUuid(ParcelUuid parcelUuid) {
         UUID uuid = parcelUuid.getUuid();
         if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
@@ -345,6 +361,7 @@
      * @param parcelUuid
      * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise.
      */
+    @UnsupportedAppUsage
     public static boolean is32BitUuid(ParcelUuid parcelUuid) {
         UUID uuid = parcelUuid.getUuid();
         if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 0fb4ba1..13c5ff6 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -411,7 +411,14 @@
         try {
             gatt = mBluetoothManager.getBluetoothGatt();
         } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
+            Log.e(TAG, "Failed to get Bluetooth GATT - ", e);
+            postStartSetFailure(handler, callback,
+                    AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
+            return;
+        }
+
+        if (gatt == null) {
+            Log.e(TAG, "Bluetooth GATT is null");
             postStartSetFailure(handler, callback,
                     AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
             return;
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 347fc4d..f5e2a09 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -441,8 +441,8 @@
                 if (status == BluetoothGatt.GATT_SUCCESS) {
                     try {
                         if (mScannerId == -1) {
-                            // Registration succeeds after timeout, unregister client.
-                            mBluetoothGatt.unregisterClient(scannerId);
+                            // Registration succeeds after timeout, unregister scanner.
+                            mBluetoothGatt.unregisterScanner(scannerId);
                         } else {
                             mScannerId = scannerId;
                             mBluetoothGatt.startScan(mScannerId, mSettings, mFilters,
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
index 04dd060..07ed18d 100644
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -17,6 +17,7 @@
 package android.bluetooth.le;
 
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.bluetooth.BluetoothUuid;
 import android.os.ParcelUuid;
 import android.util.ArrayMap;
@@ -174,6 +175,7 @@
      * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
      * @hide
      */
+    @UnsupportedAppUsage
     public static ScanRecord parseFromBytes(byte[] scanRecord) {
         if (scanRecord == null) {
             return null;
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index cdeaea3..7bde871 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -21,6 +21,7 @@
 import static android.app.AppOpsManager.MODE_ERRORED;
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Trace.TRACE_TAG_DATABASE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -42,6 +43,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.text.TextUtils;
@@ -235,6 +237,7 @@
                 // Return an empty cursor for all columns.
                 return new MatrixCursor(cursor.getColumnNames(), 0);
             }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "query");
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.query(
@@ -242,6 +245,7 @@
                         CancellationSignal.fromTransport(cancellationSignal));
             } finally {
                 setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -249,7 +253,12 @@
         public String getType(Uri uri) {
             validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            return ContentProvider.this.getType(uri);
+            Trace.traceBegin(TRACE_TAG_DATABASE, "getType");
+            try {
+                return ContentProvider.this.getType(uri);
+            } finally {
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
         }
 
         @Override
@@ -260,11 +269,13 @@
             if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return rejectInsert(uri, initialValues);
             }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "insert");
             final String original = setCallingPackage(callingPkg);
             try {
                 return maybeAddUserId(ContentProvider.this.insert(uri, initialValues), userId);
             } finally {
                 setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -275,11 +286,13 @@
             if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "bulkInsert");
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.bulkInsert(uri, initialValues);
             } finally {
                 setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -312,6 +325,7 @@
                     }
                 }
             }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "applyBatch");
             final String original = setCallingPackage(callingPkg);
             try {
                 ContentProviderResult[] results = ContentProvider.this.applyBatch(operations);
@@ -326,6 +340,7 @@
                 return results;
             } finally {
                 setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -336,11 +351,13 @@
             if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "delete");
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.delete(uri, selection, selectionArgs);
             } finally {
                 setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -352,11 +369,13 @@
             if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "update");
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.update(uri, values, selection, selectionArgs);
             } finally {
                 setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -367,12 +386,14 @@
             validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             enforceFilePermission(callingPkg, uri, mode, callerToken);
+            Trace.traceBegin(TRACE_TAG_DATABASE, "openFile");
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.openFile(
                         uri, mode, CancellationSignal.fromTransport(cancellationSignal));
             } finally {
                 setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -383,12 +404,14 @@
             validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             enforceFilePermission(callingPkg, uri, mode, null);
+            Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile");
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.openAssetFile(
                         uri, mode, CancellationSignal.fromTransport(cancellationSignal));
             } finally {
                 setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -396,11 +419,13 @@
         public Bundle call(
                 String callingPkg, String method, @Nullable String arg, @Nullable Bundle extras) {
             Bundle.setDefusable(extras, true);
+            Trace.traceBegin(TRACE_TAG_DATABASE, "call");
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.call(method, arg, extras);
             } finally {
                 setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -408,7 +433,12 @@
         public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
             validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            return ContentProvider.this.getStreamTypes(uri, mimeTypeFilter);
+            Trace.traceBegin(TRACE_TAG_DATABASE, "getStreamTypes");
+            try {
+                return ContentProvider.this.getStreamTypes(uri, mimeTypeFilter);
+            } finally {
+                Trace.traceEnd(TRACE_TAG_DATABASE);
+            }
         }
 
         @Override
@@ -418,12 +448,14 @@
             validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             enforceFilePermission(callingPkg, uri, "r", null);
+            Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile");
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.openTypedAssetFile(
                         uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
             } finally {
                 setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -440,11 +472,13 @@
             if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return null;
             }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "canonicalize");
             final String original = setCallingPackage(callingPkg);
             try {
                 return maybeAddUserId(ContentProvider.this.canonicalize(uri), userId);
             } finally {
                 setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -456,11 +490,13 @@
             if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return null;
             }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "uncanonicalize");
             final String original = setCallingPackage(callingPkg);
             try {
                 return maybeAddUserId(ContentProvider.this.uncanonicalize(uri), userId);
             } finally {
                 setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
@@ -472,12 +508,14 @@
             if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return false;
             }
+            Trace.traceBegin(TRACE_TAG_DATABASE, "refresh");
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.refresh(uri, args,
                         CancellationSignal.fromTransport(cancellationSignal));
             } finally {
                 setCallingPackage(original);
+                Trace.traceEnd(TRACE_TAG_DATABASE);
             }
         }
 
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 2d490a0..9d8c318 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -39,6 +39,8 @@
 
 import dalvik.system.CloseGuard;
 
+import libcore.io.IoUtils;
+
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -560,14 +562,17 @@
         return ContentProvider.coerceToLocalContentProvider(mContentProvider);
     }
 
+    /**
+     * Closes the given object quietly, ignoring any checked exceptions. Does
+     * nothing if the given object is {@code null}.
+     */
+    public static void closeQuietly(ContentProviderClient client) {
+        IoUtils.closeQuietly(client);
+    }
+
     /** {@hide} */
     public static void releaseQuietly(ContentProviderClient client) {
-        if (client != null) {
-            try {
-                client.release();
-            } catch (Exception ignored) {
-            }
-        }
+        IoUtils.closeQuietly(client);
     }
 
     private class NotRespondingRunnable implements Runnable {
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index af09f15..0ca7dae 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -26,6 +26,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
+import android.app.UriGrantsManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
@@ -260,6 +261,13 @@
      */
     public static final String QUERY_ARG_SQL_SORT_ORDER = "android:query-arg-sql-sort-order";
 
+    /** {@hide} */
+    public static final String QUERY_ARG_SQL_GROUP_BY = "android:query-arg-sql-group-by";
+    /** {@hide} */
+    public static final String QUERY_ARG_SQL_HAVING = "android:query-arg-sql-having";
+    /** {@hide} */
+    public static final String QUERY_ARG_SQL_LIMIT = "android:query-arg-sql-limit";
+
     /**
      * Specifies the list of columns against which to sort results. When first column values
      * are identical, records are then sorted based on second column values, and so on.
@@ -2136,7 +2144,7 @@
             @Intent.AccessUriMode int modeFlags) {
         Preconditions.checkNotNull(uri, "uri");
         try {
-            ActivityManager.getService().takePersistableUriPermission(
+            UriGrantsManager.getService().takePersistableUriPermission(
                     ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
                     resolveUserId(uri));
         } catch (RemoteException e) {
@@ -2152,7 +2160,7 @@
         Preconditions.checkNotNull(toPackage, "toPackage");
         Preconditions.checkNotNull(uri, "uri");
         try {
-            ActivityManager.getService().takePersistableUriPermission(
+            UriGrantsManager.getService().takePersistableUriPermission(
                     ContentProvider.getUriWithoutUserId(uri), modeFlags, toPackage,
                     resolveUserId(uri));
         } catch (RemoteException e) {
@@ -2172,7 +2180,7 @@
             @Intent.AccessUriMode int modeFlags) {
         Preconditions.checkNotNull(uri, "uri");
         try {
-            ActivityManager.getService().releasePersistableUriPermission(
+            UriGrantsManager.getService().releasePersistableUriPermission(
                     ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
                     resolveUserId(uri));
         } catch (RemoteException e) {
@@ -2192,7 +2200,7 @@
      */
     public @NonNull List<UriPermission> getPersistedUriPermissions() {
         try {
-            return ActivityManager.getService()
+            return UriGrantsManager.getService()
                     .getPersistedUriPermissions(mPackageName, true).getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2208,7 +2216,7 @@
      */
     public @NonNull List<UriPermission> getOutgoingPersistedUriPermissions() {
         try {
-            return ActivityManager.getService()
+            return UriGrantsManager.getService()
                     .getPersistedUriPermissions(mPackageName, false).getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index 6f93798..b06dd7a 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -18,11 +18,15 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -32,16 +36,20 @@
 public final class ContentValues implements Parcelable {
     public static final String TAG = "ContentValues";
 
-    /** Holds the actual values */
+    /**
+     * @hide
+     * @deprecated kept around for lame people doing reflection
+     */
+    @Deprecated
     private HashMap<String, Object> mValues;
 
+    private final ArrayMap<String, Object> mMap;
+
     /**
      * Creates an empty set of values using the default initial size
      */
     public ContentValues() {
-        // Choosing a default size of 8 based on analysis of typical
-        // consumption by applications.
-        mValues = new HashMap<String, Object>(8);
+        mMap = new ArrayMap<>();
     }
 
     /**
@@ -50,7 +58,8 @@
      * @param size the initial size of the set of values
      */
     public ContentValues(int size) {
-        mValues = new HashMap<String, Object>(size, 1.0f);
+        Preconditions.checkArgumentNonnegative(size);
+        mMap = new ArrayMap<>(size);
     }
 
     /**
@@ -59,18 +68,24 @@
      * @param from the values to copy
      */
     public ContentValues(ContentValues from) {
-        mValues = new HashMap<String, Object>(from.mValues);
+        Objects.requireNonNull(from);
+        mMap = new ArrayMap<>(from.mMap);
     }
 
     /**
-     * Creates a set of values copied from the given HashMap. This is used
-     * by the Parcel unmarshalling code.
-     *
-     * @param values the values to start with
-     * {@hide}
+     * @hide
+     * @deprecated kept around for lame people doing reflection
      */
-    private ContentValues(HashMap<String, Object> values) {
-        mValues = values;
+    @Deprecated
+    private ContentValues(HashMap<String, Object> from) {
+        mMap = new ArrayMap<>();
+        mMap.putAll(from);
+    }
+
+    /** {@hide} */
+    private ContentValues(Parcel in) {
+        mMap = new ArrayMap<>(in.readInt());
+        in.readArrayMap(mMap, null);
     }
 
     @Override
@@ -78,12 +93,17 @@
         if (!(object instanceof ContentValues)) {
             return false;
         }
-        return mValues.equals(((ContentValues) object).mValues);
+        return mMap.equals(((ContentValues) object).mMap);
+    }
+
+    /** {@hide} */
+    public ArrayMap<String, Object> getValues() {
+        return mMap;
     }
 
     @Override
     public int hashCode() {
-        return mValues.hashCode();
+        return mMap.hashCode();
     }
 
     /**
@@ -93,7 +113,7 @@
      * @param value the data for the value to put
      */
     public void put(String key, String value) {
-        mValues.put(key, value);
+        mMap.put(key, value);
     }
 
     /**
@@ -102,7 +122,7 @@
      * @param other the ContentValues from which to copy
      */
     public void putAll(ContentValues other) {
-        mValues.putAll(other.mValues);
+        mMap.putAll(other.mMap);
     }
 
     /**
@@ -112,7 +132,7 @@
      * @param value the data for the value to put
      */
     public void put(String key, Byte value) {
-        mValues.put(key, value);
+        mMap.put(key, value);
     }
 
     /**
@@ -122,7 +142,7 @@
      * @param value the data for the value to put
      */
     public void put(String key, Short value) {
-        mValues.put(key, value);
+        mMap.put(key, value);
     }
 
     /**
@@ -132,7 +152,7 @@
      * @param value the data for the value to put
      */
     public void put(String key, Integer value) {
-        mValues.put(key, value);
+        mMap.put(key, value);
     }
 
     /**
@@ -142,7 +162,7 @@
      * @param value the data for the value to put
      */
     public void put(String key, Long value) {
-        mValues.put(key, value);
+        mMap.put(key, value);
     }
 
     /**
@@ -152,7 +172,7 @@
      * @param value the data for the value to put
      */
     public void put(String key, Float value) {
-        mValues.put(key, value);
+        mMap.put(key, value);
     }
 
     /**
@@ -162,7 +182,7 @@
      * @param value the data for the value to put
      */
     public void put(String key, Double value) {
-        mValues.put(key, value);
+        mMap.put(key, value);
     }
 
     /**
@@ -172,7 +192,7 @@
      * @param value the data for the value to put
      */
     public void put(String key, Boolean value) {
-        mValues.put(key, value);
+        mMap.put(key, value);
     }
 
     /**
@@ -182,7 +202,7 @@
      * @param value the data for the value to put
      */
     public void put(String key, byte[] value) {
-        mValues.put(key, value);
+        mMap.put(key, value);
     }
 
     /**
@@ -191,7 +211,7 @@
      * @param key the name of the value to make null
      */
     public void putNull(String key) {
-        mValues.put(key, null);
+        mMap.put(key, null);
     }
 
     /**
@@ -200,7 +220,7 @@
      * @return the number of values
      */
     public int size() {
-        return mValues.size();
+        return mMap.size();
     }
 
     /**
@@ -211,7 +231,7 @@
      * TODO: consider exposing this new method publicly
      */
     public boolean isEmpty() {
-        return mValues.isEmpty();
+        return mMap.isEmpty();
     }
 
     /**
@@ -220,14 +240,14 @@
      * @param key the name of the value to remove
      */
     public void remove(String key) {
-        mValues.remove(key);
+        mMap.remove(key);
     }
 
     /**
      * Removes all values.
      */
     public void clear() {
-        mValues.clear();
+        mMap.clear();
     }
 
     /**
@@ -237,7 +257,7 @@
      * @return {@code true} if the value is present, {@code false} otherwise
      */
     public boolean containsKey(String key) {
-        return mValues.containsKey(key);
+        return mMap.containsKey(key);
     }
 
     /**
@@ -249,7 +269,7 @@
      *         was previously added with the given {@code key}
      */
     public Object get(String key) {
-        return mValues.get(key);
+        return mMap.get(key);
     }
 
     /**
@@ -259,7 +279,7 @@
      * @return the String for the value
      */
     public String getAsString(String key) {
-        Object value = mValues.get(key);
+        Object value = mMap.get(key);
         return value != null ? value.toString() : null;
     }
 
@@ -270,7 +290,7 @@
      * @return the Long value, or {@code null} if the value is missing or cannot be converted
      */
     public Long getAsLong(String key) {
-        Object value = mValues.get(key);
+        Object value = mMap.get(key);
         try {
             return value != null ? ((Number) value).longValue() : null;
         } catch (ClassCastException e) {
@@ -295,7 +315,7 @@
      * @return the Integer value, or {@code null} if the value is missing or cannot be converted
      */
     public Integer getAsInteger(String key) {
-        Object value = mValues.get(key);
+        Object value = mMap.get(key);
         try {
             return value != null ? ((Number) value).intValue() : null;
         } catch (ClassCastException e) {
@@ -320,7 +340,7 @@
      * @return the Short value, or {@code null} if the value is missing or cannot be converted
      */
     public Short getAsShort(String key) {
-        Object value = mValues.get(key);
+        Object value = mMap.get(key);
         try {
             return value != null ? ((Number) value).shortValue() : null;
         } catch (ClassCastException e) {
@@ -345,7 +365,7 @@
      * @return the Byte value, or {@code null} if the value is missing or cannot be converted
      */
     public Byte getAsByte(String key) {
-        Object value = mValues.get(key);
+        Object value = mMap.get(key);
         try {
             return value != null ? ((Number) value).byteValue() : null;
         } catch (ClassCastException e) {
@@ -370,7 +390,7 @@
      * @return the Double value, or {@code null} if the value is missing or cannot be converted
      */
     public Double getAsDouble(String key) {
-        Object value = mValues.get(key);
+        Object value = mMap.get(key);
         try {
             return value != null ? ((Number) value).doubleValue() : null;
         } catch (ClassCastException e) {
@@ -395,7 +415,7 @@
      * @return the Float value, or {@code null} if the value is missing or cannot be converted
      */
     public Float getAsFloat(String key) {
-        Object value = mValues.get(key);
+        Object value = mMap.get(key);
         try {
             return value != null ? ((Number) value).floatValue() : null;
         } catch (ClassCastException e) {
@@ -420,7 +440,7 @@
      * @return the Boolean value, or {@code null} if the value is missing or cannot be converted
      */
     public Boolean getAsBoolean(String key) {
-        Object value = mValues.get(key);
+        Object value = mMap.get(key);
         try {
             return (Boolean) value;
         } catch (ClassCastException e) {
@@ -448,7 +468,7 @@
      *         {@code byte[]}
      */
     public byte[] getAsByteArray(String key) {
-        Object value = mValues.get(key);
+        Object value = mMap.get(key);
         if (value instanceof byte[]) {
             return (byte[]) value;
         } else {
@@ -462,7 +482,7 @@
      * @return a set of all of the keys and values
      */
     public Set<Map.Entry<String, Object>> valueSet() {
-        return mValues.entrySet();
+        return mMap.entrySet();
     }
 
     /**
@@ -471,30 +491,31 @@
      * @return a set of all of the keys
      */
     public Set<String> keySet() {
-        return mValues.keySet();
+        return mMap.keySet();
     }
 
     public static final Parcelable.Creator<ContentValues> CREATOR =
             new Parcelable.Creator<ContentValues>() {
-        @SuppressWarnings({"deprecation", "unchecked"})
+        @Override
         public ContentValues createFromParcel(Parcel in) {
-            // TODO - what ClassLoader should be passed to readHashMap?
-            HashMap<String, Object> values = in.readHashMap(null);
-            return new ContentValues(values);
+            return new ContentValues(in);
         }
 
+        @Override
         public ContentValues[] newArray(int size) {
             return new ContentValues[size];
         }
     };
 
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    @SuppressWarnings("deprecation")
+    @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeMap(mValues);
+        parcel.writeInt(mMap.size());
+        parcel.writeArrayMap(mMap);
     }
 
     /**
@@ -503,7 +524,7 @@
      */
     @Deprecated
     public void putStringArrayList(String key, ArrayList<String> value) {
-        mValues.put(key, value);
+        mMap.put(key, value);
     }
 
     /**
@@ -513,7 +534,7 @@
     @SuppressWarnings("unchecked")
     @Deprecated
     public ArrayList<String> getStringArrayList(String key) {
-        return (ArrayList<String>) mValues.get(key);
+        return (ArrayList<String>) mMap.get(key);
     }
 
     /**
@@ -523,7 +544,7 @@
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        for (String name : mValues.keySet()) {
+        for (String name : mMap.keySet()) {
             String value = getAsString(name);
             if (sb.length() > 0) sb.append(" ");
             sb.append(name + "=" + value);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1da77a2..6abe777 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3005,6 +3005,7 @@
             NSD_SERVICE,
             AUDIO_SERVICE,
             FINGERPRINT_SERVICE,
+            //@hide: FACE_SERVICE,
             MEDIA_ROUTER_SERVICE,
             TELEPHONY_SERVICE,
             TELEPHONY_SUBSCRIPTION_SERVICE,
@@ -3060,6 +3061,7 @@
             CROSS_PROFILE_APPS_SERVICE,
             //@hide: SYSTEM_UPDATE_SERVICE,
             //@hide: TIME_DETECTOR_SERVICE,
+            //@hide: TIME_ZONE_DETECTOR_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceName {}
@@ -3328,6 +3330,26 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.ActivityTaskManager} for interacting with the global system state.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.ActivityTaskManager
+     * @hide
+     */
+    public static final String ACTIVITY_TASK_SERVICE = "activity_task";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.UriGrantsManager} for interacting with the global system state.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.UriGrantsManager
+     * @hide
+     */
+    public static final String URI_GRANTS_SERVICE = "uri_grants";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.app.AlarmManager} for receiving intents at a
      * time of your choosing.
      *
@@ -3642,6 +3664,18 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.hardware.face.FaceManager} for handling management
+     * of face authentication.
+     *
+     * @hide
+     * @see #getSystemService
+     * @see android.hardware.face.FaceManager
+     */
+    public static final String FACE_SERVICE = "face";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a
      * {@link android.media.MediaRouter} for controlling and managing
      * routing of media.
      *
@@ -4221,6 +4255,15 @@
     public static final String TIME_DETECTOR_SERVICE = "time_detector";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.app.timezonedetector.ITimeZoneDetectorService}.
+     * @hide
+     *
+     * @see #getSystemService(String)
+     */
+    public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3dfabdd..78738e9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4602,15 +4602,6 @@
     public static final String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
 
     /**
-     * A {@link IntentSender} to start after ephemeral installation success.
-     * @deprecated Use {@link #EXTRA_INSTANT_APP_SUCCESS).
-     * @removed
-     * @hide
-     */
-    @Deprecated
-    public static final String EXTRA_EPHEMERAL_SUCCESS = "android.intent.extra.EPHEMERAL_SUCCESS";
-
-    /**
      * A {@link IntentSender} to start after instant app installation success.
      * @hide
      */
@@ -4619,15 +4610,6 @@
             "android.intent.extra.INSTANT_APP_SUCCESS";
 
     /**
-     * A {@link IntentSender} to start after ephemeral installation failure.
-     * @deprecated Use {@link #EXTRA_INSTANT_APP_FAILURE).
-     * @removed
-     * @hide
-     */
-    @Deprecated
-    public static final String EXTRA_EPHEMERAL_FAILURE = "android.intent.extra.EPHEMERAL_FAILURE";
-
-    /**
      * A {@link IntentSender} to start after instant app installation failure.
      * @hide
      */
@@ -4636,15 +4618,6 @@
             "android.intent.extra.INSTANT_APP_FAILURE";
 
     /**
-     * The host name that triggered an ephemeral resolution.
-     * @deprecated Use {@link #EXTRA_INSTANT_APP_HOSTNAME).
-     * @removed
-     * @hide
-     */
-    @Deprecated
-    public static final String EXTRA_EPHEMERAL_HOSTNAME = "android.intent.extra.EPHEMERAL_HOSTNAME";
-
-    /**
      * The host name that triggered an instant app resolution.
      * @hide
      */
@@ -4653,15 +4626,6 @@
             "android.intent.extra.INSTANT_APP_HOSTNAME";
 
     /**
-     * An opaque token to track ephemeral resolution.
-     * @deprecated Use {@link #EXTRA_INSTANT_APP_TOKEN).
-     * @removed
-     * @hide
-     */
-    @Deprecated
-    public static final String EXTRA_EPHEMERAL_TOKEN = "android.intent.extra.EPHEMERAL_TOKEN";
-
-    /**
      * An opaque token to track instant app resolution.
      * @hide
      */
@@ -5408,19 +5372,23 @@
     public static final int FLAG_GRANT_PREFIX_URI_PERMISSION = 0x00000080;
 
     /**
-     * Internal flag used to indicate that a system component has done their
-     * homework and verified that they correctly handle packages and components
-     * that come and go over time. In particular:
-     * <ul>
-     * <li>Apps installed on external storage, which will appear to be
-     * uninstalled while the the device is ejected.
-     * <li>Apps with encryption unaware components, which will appear to not
-     * exist while the device is locked.
-     * </ul>
-     *
-     * @hide
+     * Flag used to automatically match intents based on their Direct Boot
+     * awareness and the current user state.
+     * <p>
+     * Since the default behavior is to automatically apply the current user
+     * state, this is effectively a sentinel value that doesn't change the
+     * output of any queries based on its presence or absence.
+     * <p>
+     * Instead, this value can be useful in conjunction with
+     * {@link android.os.StrictMode.VmPolicy.Builder#detectImplicitDirectBoot()}
+     * to detect when a caller is relying on implicit automatic matching,
+     * instead of confirming the explicit behavior they want.
      */
-    public static final int FLAG_DEBUG_TRIAGED_MISSING = 0x00000100;
+    public static final int FLAG_DIRECT_BOOT_AUTO = 0x00000100;
+
+    /** {@hide} */
+    @Deprecated
+    public static final int FLAG_DEBUG_TRIAGED_MISSING = FLAG_DIRECT_BOOT_AUTO;
 
     /**
      * Internal flag used to indicate ephemeral applications should not be
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index d65e051..13b67fd 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -477,6 +477,7 @@
      *
      * {@hide}
      */
+    @TestApi
     public static final int PRIVATE_FLAG_PRIVILEGED = 1<<3;
 
     /**
@@ -649,6 +650,7 @@
      * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
      * @hide
      */
+    @TestApi
     public @ApplicationInfoPrivateFlags int privateFlags;
 
     /**
@@ -1120,6 +1122,9 @@
     /** @hide */
     public String[] splitClassLoaderNames;
 
+    /** @hide */
+    public boolean hiddenUntilInstalled;
+
     /**
      * Represents the default policy. The actual policy used will depend on other properties of
      * the application, e.g. the target SDK version.
@@ -1460,6 +1465,7 @@
         compileSdkVersion = orig.compileSdkVersion;
         compileSdkVersionCodename = orig.compileSdkVersionCodename;
         mHiddenApiPolicy = orig.mHiddenApiPolicy;
+        hiddenUntilInstalled = orig.hiddenUntilInstalled;
     }
 
     public String toString() {
@@ -1534,6 +1540,7 @@
         dest.writeString(compileSdkVersionCodename);
         dest.writeString(appComponentFactory);
         dest.writeInt(mHiddenApiPolicy);
+        dest.writeInt(hiddenUntilInstalled ? 1 : 0);
     }
 
     public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1605,6 +1612,7 @@
         compileSdkVersionCodename = source.readString();
         appComponentFactory = source.readString();
         mHiddenApiPolicy = source.readInt();
+        hiddenUntilInstalled = source.readInt() != 0;
     }
 
     /**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c988fa9..bc5b32c 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -598,6 +598,9 @@
     boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, int userId);
     boolean getApplicationHiddenSettingAsUser(String packageName, int userId);
 
+    void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden);
+    boolean setSystemAppInstallState(String packageName, boolean installed, int userId);
+
     IPackageInstaller getPackageInstaller();
 
     boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 2f3bf63..f99c52f 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -121,8 +121,7 @@
             "android.content.pm.action.SESSION_COMMITTED";
 
     /** {@hide} */
-    public static final String
-            ACTION_CONFIRM_PERMISSIONS = "android.content.pm.action.CONFIRM_PERMISSIONS";
+    public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
 
     /**
      * An integer session ID that an operation is working with.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 43b6984..c0b3400 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -147,6 +147,7 @@
             GET_DISABLED_COMPONENTS,
             GET_DISABLED_UNTIL_USED_COMPONENTS,
             GET_UNINSTALLED_PACKAGES,
+            MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PackageInfoFlags {}
@@ -164,6 +165,7 @@
             MATCH_STATIC_SHARED_LIBRARIES,
             GET_DISABLED_UNTIL_USED_COMPONENTS,
             GET_UNINSTALLED_PACKAGES,
+            MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ApplicationInfoFlags {}
@@ -177,6 +179,7 @@
             MATCH_DEFAULT_ONLY,
             MATCH_DISABLED_COMPONENTS,
             MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+            MATCH_DIRECT_BOOT_AUTO,
             MATCH_DIRECT_BOOT_AWARE,
             MATCH_DIRECT_BOOT_UNAWARE,
             MATCH_SYSTEM_ONLY,
@@ -200,6 +203,7 @@
             MATCH_DISABLED_COMPONENTS,
             MATCH_DISABLED_UNTIL_USED_COMPONENTS,
             MATCH_DEFAULT_ONLY,
+            MATCH_DIRECT_BOOT_AUTO,
             MATCH_DIRECT_BOOT_AWARE,
             MATCH_DIRECT_BOOT_UNAWARE,
             MATCH_SYSTEM_ONLY,
@@ -504,22 +508,35 @@
     public static final int GET_SIGNING_CERTIFICATES = 0x08000000;
 
     /**
-     * Internal flag used to indicate that a system component has done their
-     * homework and verified that they correctly handle packages and components
-     * that come and go over time. In particular:
+     * Querying flag: automatically match components based on their Direct Boot
+     * awareness and the current user state.
+     * <p>
+     * Since the default behavior is to automatically apply the current user
+     * state, this is effectively a sentinel value that doesn't change the
+     * output of any queries based on its presence or absence.
+     * <p>
+     * Instead, this value can be useful in conjunction with
+     * {@link android.os.StrictMode.VmPolicy.Builder#detectImplicitDirectBoot()}
+     * to detect when a caller is relying on implicit automatic matching,
+     * instead of confirming the explicit behavior they want, using a
+     * combination of these flags:
      * <ul>
-     * <li>Apps installed on external storage, which will appear to be
-     * uninstalled while the the device is ejected.
-     * <li>Apps with encryption unaware components, which will appear to not
-     * exist while the device is locked.
+     * <li>{@link #MATCH_DIRECT_BOOT_AWARE}
+     * <li>{@link #MATCH_DIRECT_BOOT_UNAWARE}
+     * <li>{@link #MATCH_DIRECT_BOOT_AUTO}
      * </ul>
-     *
-     * @see #MATCH_UNINSTALLED_PACKAGES
-     * @see #MATCH_DIRECT_BOOT_AWARE
-     * @see #MATCH_DIRECT_BOOT_UNAWARE
+     */
+    public static final int MATCH_DIRECT_BOOT_AUTO = 0x10000000;
+
+    /** @hide */
+    @Deprecated
+    public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO;
+
+    /**
+     * Internal flag used to indicate that a package is a hidden system app.
      * @hide
      */
-    public static final int MATCH_DEBUG_TRIAGED_MISSING = 0x10000000;
+    public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS =  0x20000000;
 
     /**
      * Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when
@@ -2245,12 +2262,20 @@
     /**
      * 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.fingerprint";
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_FACE = "android.hardware.face";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports portrait orientation
      * screens.  For backwards compatibility, you can assume that if neither
      * this nor {@link #FEATURE_SCREEN_LANDSCAPE} is set then the device supports
@@ -2912,6 +2937,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
 
     /**
@@ -2923,6 +2949,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final String SYSTEM_SHARED_LIBRARY_SHARED = "android.ext.shared";
 
     /**
@@ -3242,12 +3269,19 @@
             @PermissionInfoFlags int flags) throws NameNotFoundException;
 
     /**
-     * Returns true if Permission Review Mode is enabled, false otherwise.
+     * Returns true if some permissions are individually controlled
      *
      * @hide
      */
     @TestApi
-    public abstract boolean isPermissionReviewModeEnabled();
+    public abstract boolean arePermissionsIndividuallyControlled();
+
+    /**
+     * Returns true if wireless consent mode is enabled
+     *
+     * @hide
+     */
+    public abstract boolean isWirelessConsentModeEnabled();
 
     /**
      * Retrieve all of the information we know about a particular group of
@@ -3555,6 +3589,7 @@
      *
      * @hide
      */
+    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
     public abstract void grantRuntimePermission(@NonNull String packageName,
@@ -3581,6 +3616,7 @@
      *
      * @hide
      */
+    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
     public abstract void revokeRuntimePermission(@NonNull String packageName,
@@ -4847,7 +4883,8 @@
      * on the system for other users, also install it for the specified user.
      * @hide
      */
-     @RequiresPermission(anyOf = {
+    @RequiresPermission(anyOf = {
+            Manifest.permission.INSTALL_EXISTING_PACKAGES,
             Manifest.permission.INSTALL_PACKAGES,
             Manifest.permission.INTERACT_ACROSS_USERS_FULL})
     public abstract int installExistingPackageAsUser(String packageName, @UserIdInt int userId)
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index f30b3fe..a2a48328 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -29,9 +29,12 @@
 import android.os.PersistableBundle;
 import android.util.SparseArray;
 
+import com.android.internal.util.function.TriFunction;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.function.BiFunction;
 
 /**
  * Package manager local system service interface.
@@ -45,6 +48,7 @@
     public static final int PACKAGE_VERIFIER = 3;
     public static final int PACKAGE_BROWSER = 4;
     public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
+    public static final int PACKAGE_PERMISSION_CONTROLLER = 6;
     @IntDef(value = {
         PACKAGE_SYSTEM,
         PACKAGE_SETUP_WIZARD,
@@ -52,6 +56,7 @@
         PACKAGE_VERIFIER,
         PACKAGE_BROWSER,
         PACKAGE_SYSTEM_TEXT_CLASSIFIER,
+        PACKAGE_PERMISSION_CONTROLLER,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KnownPackage {}
@@ -64,6 +69,32 @@
         void onPackageRemoved(@NonNull String packageName);
     }
 
+    /** Interface to override permission checks via composition */
+    public interface CheckPermissionDelegate {
+        /**
+         * Allows overriding check permission behavior.
+         *
+         * @param permName The permission to check.
+         * @param pkgName The package for which to check.
+         * @param userId The user for which to check.
+         * @param superImpl The super implementation.
+         * @return The check permission result.
+         */
+        int checkPermission(String permName, String pkgName, int userId,
+                TriFunction<String, String, Integer, Integer> superImpl);
+
+        /**
+         * Allows overriding check UID permission behavior.
+         *
+         * @param permName The permission to check.
+         * @param uid The UID for which to check.
+         * @param superImpl The super implementation.
+         * @return The check permission result.
+         */
+        int checkUidPermission(String permName, int uid,
+                BiFunction<String, Integer, Integer> superImpl);
+    }
+
     /**
      * Provider for package names.
      */
@@ -628,4 +659,25 @@
      */
     public abstract boolean hasSignatureCapability(int serverUid, int clientUid,
             @PackageParser.SigningDetails.CertCapabilities int capability);
+
+    /**
+     * Get the delegate to influence permission checking.
+     *
+     * @return The delegate instance or null to clear.
+     */
+    public abstract @Nullable CheckPermissionDelegate getCheckPermissionDelegate();
+
+    /**
+     * Set a delegate to influence permission checking.
+     *
+     * @param delegate A delegate instance or null to clear.
+     */
+    public abstract void setCheckPermissionDelegate(@Nullable CheckPermissionDelegate delegate);
+
+    /**
+     * Get appIds of all available apps which specified android:sharedUserId in the manifest.
+     *
+     * @return a SparseArray mapping from appId to it's sharedUserId.
+     */
+    public abstract SparseArray<String> getAppsWithSharedUserIds();
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 9dbb2df..b4e9ad1 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -46,7 +46,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
-import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -646,11 +646,19 @@
      */
     private static boolean checkUseInstalledOrHidden(int flags, PackageUserState state,
             ApplicationInfo appInfo) {
+        // Returns false if the package is hidden system app until installed.
+        if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
+                && !state.installed
+                && appInfo != null && appInfo.hiddenUntilInstalled) {
+            return false;
+        }
+
         // If available for the target user, or trying to match uninstalled packages and it's
         // a system app.
         return state.isAvailable(flags)
                 || (appInfo != null && appInfo.isSystemApp()
-                        && (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0);
+                        && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
+                        || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
     }
 
     public static boolean isAvailable(PackageUserState state) {
@@ -1974,7 +1982,7 @@
         String str = sa.getNonConfigurationString(
                 com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
         if (str != null && str.length() > 0) {
-            String nameError = validateName(str, true, false);
+            String nameError = validateName(str, true, true);
             if (nameError != null && !"android".equals(pkg.packageName)) {
                 outError[0] = "<manifest> specifies bad sharedUserId name \""
                     + str + "\": " + nameError;
@@ -2206,10 +2214,10 @@
                             com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
                     if (val != null) {
                         if (val.type == TypedValue.TYPE_STRING && val.string != null) {
-                            targetCode = minCode = val.string.toString();
+                            minCode = val.string.toString();
                         } else {
                             // If it's not a string, it's an integer.
-                            targetVers = minVers = val.data;
+                            minVers = val.data;
                         }
                     }
 
@@ -2225,6 +2233,9 @@
                             // If it's not a string, it's an integer.
                             targetVers = val.data;
                         }
+                    } else {
+                        targetVers = minVers;
+                        targetCode = minCode;
                     }
 
                     sa.recycle();
@@ -3097,6 +3108,14 @@
                 0);
         perm.info.requestRes = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestPermissionGroup_request, 0);
+        perm.info.requestDetailResourceId = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_requestDetail, 0);
+        perm.info.backgroundRequestResourceId = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_backgroundRequest,
+                0);
+        perm.info.backgroundRequestDetailResourceId = sa.getResourceId(
+                com.android.internal.R.styleable
+                        .AndroidManifestPermissionGroup_backgroundRequestDetail, 0);
         perm.info.flags = sa.getInt(
                 com.android.internal.R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags, 0);
         perm.info.priority = sa.getInt(
@@ -3151,6 +3170,19 @@
         perm.info.requestRes = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestPermission_request, 0);
 
+        if (sa.hasValue(
+                com.android.internal.R.styleable.AndroidManifestPermission_backgroundPermission)) {
+            if ("android".equals(owner.packageName)) {
+                perm.info.backgroundPermission = sa.getNonResourceString(
+                        com.android.internal.R.styleable
+                                .AndroidManifestPermission_backgroundPermission);
+            } else {
+                Slog.w(TAG, owner.packageName + " defines permission '" + perm.info.name
+                        + "' with a background permission. Only the 'android' package can do "
+                        + "that.");
+            }
+        }
+
         perm.info.protectionLevel = sa.getInt(
                 com.android.internal.R.styleable.AndroidManifestPermission_protectionLevel,
                 PermissionInfo.PROTECTION_NORMAL);
@@ -4232,7 +4264,7 @@
                     ActivityInfo.DOCUMENT_LAUNCH_NONE);
             a.info.maxRecents = sa.getInt(
                     R.styleable.AndroidManifestActivity_maxRecents,
-                    ActivityManager.getDefaultAppRecentsLimitStatic());
+                    ActivityTaskManager.getDefaultAppRecentsLimitStatic());
             a.info.configChanges = getActivityConfigChanges(
                     sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
                     sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0));
diff --git a/core/java/android/content/pm/PermissionGroupInfo.java b/core/java/android/content/pm/PermissionGroupInfo.java
index 7c4478d..8cf66d8 100644
--- a/core/java/android/content/pm/PermissionGroupInfo.java
+++ b/core/java/android/content/pm/PermissionGroupInfo.java
@@ -45,6 +45,42 @@
     public @StringRes int requestRes;
 
     /**
+     * A string resource identifier (in the package's resources) used as subtitle when requesting
+     * only access while in the foreground.
+     *
+     * From the "requestDetail" attribute or, if not set, {@link
+     * android.content.res.ResourceId#ID_NULL}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @StringRes int requestDetailResourceId;
+
+    /**
+     * A string resource identifier (in the package's resources) used when requesting background
+     * access. Also used when requesting both foreground and background access.
+     *
+     * From the "backgroundRequest" attribute or, if not set, {@link
+     * android.content.res.ResourceId#ID_NULL}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @StringRes int backgroundRequestResourceId;
+
+    /**
+     * A string resource identifier (in the package's resources) used as subtitle when requesting
+     * background access.
+     *
+     * From the "backgroundRequestDetail" attribute or, if not set, {@link
+     * android.content.res.ResourceId#ID_NULL}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @StringRes int backgroundRequestDetailResourceId;
+
+    /**
      * The description string provided in the AndroidManifest file, if any.  You
      * probably don't want to use this, since it will be null if the description
      * is in a resource.  You probably want
@@ -76,6 +112,9 @@
         super(orig);
         descriptionRes = orig.descriptionRes;
         requestRes = orig.requestRes;
+        requestDetailResourceId = orig.requestDetailResourceId;
+        backgroundRequestResourceId = orig.backgroundRequestResourceId;
+        backgroundRequestDetailResourceId = orig.backgroundRequestDetailResourceId;
         nonLocalizedDescription = orig.nonLocalizedDescription;
         flags = orig.flags;
         priority = orig.priority;
@@ -119,6 +158,9 @@
         super.writeToParcel(dest, parcelableFlags);
         dest.writeInt(descriptionRes);
         dest.writeInt(requestRes);
+        dest.writeInt(requestDetailResourceId);
+        dest.writeInt(backgroundRequestResourceId);
+        dest.writeInt(backgroundRequestDetailResourceId);
         TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
         dest.writeInt(flags);
         dest.writeInt(priority);
@@ -138,6 +180,9 @@
         super(source);
         descriptionRes = source.readInt();
         requestRes = source.readInt();
+        requestDetailResourceId = source.readInt();
+        backgroundRequestResourceId = source.readInt();
+        backgroundRequestDetailResourceId = source.readInt();
         nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
         flags = source.readInt();
         priority = source.readInt();
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 938409a..535ef00 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -285,6 +285,21 @@
     public int requestRes;
 
     /**
+     * Some permissions only grant access while the app is in foreground. Some of these permissions
+     * allow to add background capabilities by adding another permission.
+     *
+     * If this is such a permission, this is the name of the permission adding the background
+     * access.
+     *
+     * From the "backgroundPermission" attribute or, if not set null
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public String backgroundPermission;
+
+    /**
      * The description string provided in the AndroidManifest file, if any.  You
      * probably don't want to use this, since it will be null if the description
      * is in a resource.  You probably want
@@ -373,6 +388,7 @@
         protectionLevel = orig.protectionLevel;
         flags = orig.flags;
         group = orig.group;
+        backgroundPermission = orig.backgroundPermission;
         descriptionRes = orig.descriptionRes;
         requestRes = orig.requestRes;
         nonLocalizedDescription = orig.nonLocalizedDescription;
@@ -436,6 +452,7 @@
         dest.writeInt(protectionLevel);
         dest.writeInt(flags);
         dest.writeString(group);
+        dest.writeString(backgroundPermission);
         dest.writeInt(descriptionRes);
         dest.writeInt(requestRes);
         TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
@@ -475,6 +492,7 @@
         protectionLevel = source.readInt();
         flags = source.readInt();
         group = source.readString();
+        backgroundPermission = source.readString();
         descriptionRes = source.readInt();
         requestRes = source.readInt();
         nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 7adea6a8..ef0dce3 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -642,8 +642,7 @@
      *           tool. This integer encodes the package, type, and resource
      *           entry. The value 0 is an invalid identifier.
      * 
-     * @return Resource dimension value multiplied by the appropriate 
-     * metric.
+     * @return Resource dimension value multiplied by the appropriate metric to convert to pixels.
      * 
      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
      *
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 3d019f0..7a8ab60 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -16,6 +16,7 @@
 
 package android.database;
 
+import android.annotation.Nullable;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.OperationApplicationException;
@@ -34,6 +35,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.io.FileNotFoundException;
 import java.io.PrintStream;
 import java.text.Collator;
@@ -216,6 +219,92 @@
     }
 
     /**
+     * Bind the given selection with the given selection arguments.
+     * <p>
+     * Internally assumes that '?' is only ever used for arguments, and doesn't
+     * appear as a literal or escaped value.
+     * <p>
+     * This method is typically useful for trusted code that needs to cook up a
+     * fully-bound selection.
+     *
+     * @hide
+     */
+    public static @Nullable String bindSelection(@Nullable String selection,
+            @Nullable Object... selectionArgs) {
+        if (selection == null) return null;
+        // If no arguments provided, so we can't bind anything
+        if (ArrayUtils.isEmpty(selectionArgs)) return selection;
+        // If no bindings requested, so we can shortcut
+        if (selection.indexOf('?') == -1) return selection;
+
+        // Track the chars immediately before and after each bind request, to
+        // decide if it needs additional whitespace added
+        char before = ' ';
+        char after = ' ';
+
+        int argIndex = 0;
+        final int len = selection.length();
+        final StringBuilder res = new StringBuilder(len);
+        for (int i = 0; i < len; ) {
+            char c = selection.charAt(i++);
+            if (c == '?') {
+                // Assume this bind request is guarded until we find a specific
+                // trailing character below
+                after = ' ';
+
+                // Sniff forward to see if the selection is requesting a
+                // specific argument index
+                int start = i;
+                for (; i < len; i++) {
+                    c = selection.charAt(i);
+                    if (c < '0' || c > '9') {
+                        after = c;
+                        break;
+                    }
+                }
+                if (start != i) {
+                    argIndex = Integer.parseInt(selection.substring(start, i)) - 1;
+                }
+
+                // Manually bind the argument into the selection, adding
+                // whitespace when needed for clarity
+                final Object arg = selectionArgs[argIndex++];
+                if (before != ' ' && before != '=') res.append(' ');
+                switch (DatabaseUtils.getTypeOfObject(arg)) {
+                    case Cursor.FIELD_TYPE_NULL:
+                        res.append("NULL");
+                        break;
+                    case Cursor.FIELD_TYPE_INTEGER:
+                        res.append(((Number) arg).longValue());
+                        break;
+                    case Cursor.FIELD_TYPE_FLOAT:
+                        res.append(((Number) arg).doubleValue());
+                        break;
+                    case Cursor.FIELD_TYPE_BLOB:
+                        throw new IllegalArgumentException("Blobs not supported");
+                    case Cursor.FIELD_TYPE_STRING:
+                    default:
+                        if (arg instanceof Boolean) {
+                            // Provide compatibility with legacy applications which may pass
+                            // Boolean values in bind args.
+                            res.append(((Boolean) arg).booleanValue() ? 1 : 0);
+                        } else {
+                            res.append('\'');
+                            res.append(arg.toString());
+                            res.append('\'');
+                        }
+                        break;
+                }
+                if (after != ' ') res.append(' ');
+            } else {
+                res.append(c);
+                before = c;
+            }
+        }
+        return res.toString();
+    }
+
+    /**
      * Returns data type of the given object's value.
      *<p>
      * Returned values are
diff --git a/core/java/android/database/OWNERS b/core/java/android/database/OWNERS
index 84e81e4..592a852 100644
--- a/core/java/android/database/OWNERS
+++ b/core/java/android/database/OWNERS
@@ -1,2 +1,3 @@
-fkupolov@google.com
-omakoto@google.com
\ No newline at end of file
+omakoto@google.com
+jsharkey@google.com
+yamasani@google.com
\ No newline at end of file
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 101fb82..47a5138 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -19,6 +19,7 @@
 import android.database.Cursor;
 import android.database.CursorWindow;
 import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDebug.Consts;
 import android.database.sqlite.SQLiteDebug.DbStats;
 import android.os.CancellationSignal;
 import android.os.OperationCanceledException;
@@ -37,7 +38,6 @@
 import java.util.Date;
 import java.util.Map;
 
-
 /**
  * Represents a SQLite database connection.
  * Each connection wraps an instance of a native <code>sqlite3</code> object.
@@ -210,7 +210,7 @@
     private void open() {
         mConnectionPtr = nativeOpen(mConfiguration.path, mConfiguration.openFlags,
                 mConfiguration.label,
-                SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME,
+                SQLiteDebug.Consts.DEBUG_SQL_STATEMENTS, SQLiteDebug.Consts.DEBUG_SQL_TIME,
                 mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount);
         setPageSize();
         setForeignKeyModeFromConfiguration();
@@ -609,7 +609,9 @@
                 applyBlockGuardPolicy(statement);
                 attachCancellationSignal(cancellationSignal);
                 try {
-                    return nativeExecuteForLong(mConnectionPtr, statement.mStatementPtr);
+                    long ret = nativeExecuteForLong(mConnectionPtr, statement.mStatementPtr);
+                    mRecentOperations.setResult(ret);
+                    return ret;
                 } finally {
                     detachCancellationSignal(cancellationSignal);
                 }
@@ -652,7 +654,9 @@
                 applyBlockGuardPolicy(statement);
                 attachCancellationSignal(cancellationSignal);
                 try {
-                    return nativeExecuteForString(mConnectionPtr, statement.mStatementPtr);
+                    String ret = nativeExecuteForString(mConnectionPtr, statement.mStatementPtr);
+                    mRecentOperations.setResult(ret);
+                    return ret;
                 } finally {
                     detachCancellationSignal(cancellationSignal);
                 }
@@ -1091,7 +1095,7 @@
         printer.println("  isPrimaryConnection: " + mIsPrimaryConnection);
         printer.println("  onlyAllowReadOnlyOperations: " + mOnlyAllowReadOnlyOperations);
 
-        mRecentOperations.dump(printer, verbose);
+        mRecentOperations.dump(printer);
 
         if (verbose) {
             mPreparedStatementCache.dump(printer);
@@ -1312,12 +1316,17 @@
         private int mIndex;
         private int mGeneration;
         private final SQLiteConnectionPool mPool;
+        private long mResultLong = Long.MIN_VALUE;
+        private String mResultString;
 
         OperationLog(SQLiteConnectionPool pool) {
             mPool = pool;
         }
 
         public int beginOperation(String kind, String sql, Object[] bindArgs) {
+            mResultLong = Long.MIN_VALUE;
+            mResultString = null;
+
             synchronized (mOperations) {
                 final int index = (mIndex + 1) % MAX_RECENT_OPERATIONS;
                 Operation operation = mOperations[index];
@@ -1335,6 +1344,9 @@
                 operation.mStartTime = SystemClock.uptimeMillis();
                 operation.mKind = kind;
                 operation.mSql = sql;
+                operation.mPath = mPool.getPath();
+                operation.mResultLong = Long.MIN_VALUE;
+                operation.mResultString = null;
                 if (bindArgs != null) {
                     if (operation.mBindArgs == null) {
                         operation.mBindArgs = new ArrayList<Object>();
@@ -1390,6 +1402,14 @@
             }
         }
 
+        public void setResult(long longResult) {
+            mResultLong = longResult;
+        }
+
+        public void setResult(String stringResult) {
+            mResultString = stringResult;
+        }
+
         private boolean endOperationDeferLogLocked(int cookie) {
             final Operation operation = getOperationLocked(cookie);
             if (operation != null) {
@@ -1401,7 +1421,7 @@
                 operation.mFinished = true;
                 final long execTime = operation.mEndTime - operation.mStartTime;
                 mPool.onStatementExecuted(execTime);
-                return SQLiteDebug.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery(
+                return SQLiteDebug.Consts.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery(
                         execTime);
             }
             return false;
@@ -1409,8 +1429,10 @@
 
         private void logOperationLocked(int cookie, String detail) {
             final Operation operation = getOperationLocked(cookie);
+            operation.mResultLong = mResultLong;
+            operation.mResultString = mResultString;
             StringBuilder msg = new StringBuilder();
-            operation.describe(msg, false);
+            operation.describe(msg, true);
             if (detail != null) {
                 msg.append(", ").append(detail);
             }
@@ -1440,7 +1462,7 @@
             }
         }
 
-        public void dump(Printer printer, boolean verbose) {
+        public void dump(Printer printer) {
             synchronized (mOperations) {
                 printer.println("  Most recently executed operations:");
                 int index = mIndex;
@@ -1457,7 +1479,7 @@
                         String formattedStartTime = opDF.format(new Date(operation.mStartWallTime));
                         msg.append(formattedStartTime);
                         msg.append("] ");
-                        operation.describe(msg, verbose);
+                        operation.describe(msg, false); // Never dump bingargs in a bugreport
                         printer.println(msg.toString());
 
                         if (index > 0) {
@@ -1491,8 +1513,11 @@
         public boolean mFinished;
         public Exception mException;
         public int mCookie;
+        public String mPath;
+        public long mResultLong; // MIN_VALUE means "value not set".
+        public String mResultString;
 
-        public void describe(StringBuilder msg, boolean verbose) {
+        public void describe(StringBuilder msg, boolean allowDetailedLog) {
             msg.append(mKind);
             if (mFinished) {
                 msg.append(" took ").append(mEndTime - mStartTime).append("ms");
@@ -1504,7 +1529,9 @@
             if (mSql != null) {
                 msg.append(", sql=\"").append(trimSqlForDisplay(mSql)).append("\"");
             }
-            if (verbose && mBindArgs != null && mBindArgs.size() != 0) {
+            final boolean dumpDetails = allowDetailedLog && Consts.DEBUG_LOG_DETAILED
+                    && mBindArgs != null && mBindArgs.size() != 0;
+            if (dumpDetails) {
                 msg.append(", bindArgs=[");
                 final int count = mBindArgs.size();
                 for (int i = 0; i < count; i++) {
@@ -1524,9 +1551,16 @@
                 }
                 msg.append("]");
             }
+            msg.append(", path=").append(mPath);
             if (mException != null) {
                 msg.append(", exception=\"").append(mException.getMessage()).append("\"");
             }
+            if (mResultLong != Long.MIN_VALUE) {
+                msg.append(", result=").append(mResultLong);
+            }
+            if (mResultString != null) {
+                msg.append(", result=\"").append(mResultString).append("\"");
+            }
         }
 
         private String getStatus() {
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index e519302..3ee348b 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -1183,6 +1183,10 @@
         return "SQLiteConnectionPool: " + mConfiguration.path;
     }
 
+    public String getPath() {
+        return mConfiguration.path;
+    }
+
     private static final class ConnectionWaiter {
         public ConnectionWaiter mNext;
         public Thread mThread;
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 6adae25..a4b989a 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -189,7 +189,8 @@
      */
     public static final int CONFLICT_NONE = 0;
 
-    private static final String[] CONFLICT_VALUES = new String[]
+    /** {@hide} */
+    public static final String[] CONFLICT_VALUES = new String[]
             {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
 
     /**
@@ -1748,7 +1749,8 @@
         executeSql(sql, bindArgs);
     }
 
-    private int executeSql(String sql, Object[] bindArgs) throws SQLException {
+    /** {@hide} */
+    public int executeSql(String sql, Object[] bindArgs) throws SQLException {
         acquireReference();
         try {
             final int statementType = DatabaseUtils.getSqlStatementType(sql);
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index d392521..1c66204 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -18,6 +18,7 @@
 
 import android.annotation.TestApi;
 import android.os.Build;
+import android.os.Process;
 import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Printer;
@@ -34,35 +35,53 @@
     private static native void nativeGetPagerStats(PagerStats stats);
 
     /**
-     * Controls the printing of informational SQL log messages.
+     * Inner class to avoid getting the value frozen in zygote.
      *
-     * Enable using "adb shell setprop log.tag.SQLiteLog VERBOSE".
+     * {@hide}
      */
-    public static final boolean DEBUG_SQL_LOG =
-            Log.isLoggable("SQLiteLog", Log.VERBOSE);
+    public static final class Consts {
+        /**
+         * Controls the printing of informational SQL log messages.
+         *
+         * Enable using "adb shell setprop log.tag.SQLiteLog VERBOSE".
+         */
+        public static final boolean DEBUG_SQL_LOG =
+                Log.isLoggable("SQLiteLog", Log.VERBOSE);
 
-    /**
-     * Controls the printing of SQL statements as they are executed.
-     *
-     * Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE".
-     */
-    public static final boolean DEBUG_SQL_STATEMENTS =
-            Log.isLoggable("SQLiteStatements", Log.VERBOSE);
+        /**
+         * Controls the printing of SQL statements as they are executed.
+         *
+         * Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE".
+         */
+        public static final boolean DEBUG_SQL_STATEMENTS =
+                Log.isLoggable("SQLiteStatements", Log.VERBOSE);
 
-    /**
-     * Controls the printing of wall-clock time taken to execute SQL statements
-     * as they are executed.
-     *
-     * Enable using "adb shell setprop log.tag.SQLiteTime VERBOSE".
-     */
-    public static final boolean DEBUG_SQL_TIME =
-            Log.isLoggable("SQLiteTime", Log.VERBOSE);
+        /**
+         * Controls the printing of wall-clock time taken to execute SQL statements
+         * as they are executed.
+         *
+         * Enable using "adb shell setprop log.tag.SQLiteTime VERBOSE".
+         */
+        public static final boolean DEBUG_SQL_TIME =
+                Log.isLoggable("SQLiteTime", Log.VERBOSE);
 
-    /**
-     * True to enable database performance testing instrumentation.
-     * @hide
-     */
-    public static final boolean DEBUG_LOG_SLOW_QUERIES = Build.IS_DEBUGGABLE;
+
+        /**
+         * True to enable database performance testing instrumentation.
+         */
+        public static final boolean DEBUG_LOG_SLOW_QUERIES = Build.IS_DEBUGGABLE;
+
+        private static final String SLOW_QUERY_THRESHOLD_PROP = "db.log.slow_query_threshold";
+
+        private static final String SLOW_QUERY_THRESHOLD_UID_PROP =
+                SLOW_QUERY_THRESHOLD_PROP + "." + Process.myUid();
+
+        /**
+         * Whether to add detailed information to slow query log.
+         */
+        public static final boolean DEBUG_LOG_DETAILED = Build.IS_DEBUGGABLE
+                && SystemProperties.getBoolean("db.log.detailed", false);
+    }
 
     private SQLiteDebug() {
     }
@@ -75,14 +94,19 @@
      * be considered slow.  If the value does not exist or is negative, then no queries will
      * be considered slow.
      *
+     * To enable it for a specific UID, "db.log.slow_query_threshold.UID" could also be used.
+     *
      * This value can be changed dynamically while the system is running.
      * For example, "adb shell setprop db.log.slow_query_threshold 200" will
      * log all queries that take 200ms or longer to run.
      * @hide
      */
-    public static final boolean shouldLogSlowQuery(long elapsedTimeMillis) {
-        int slowQueryMillis = SystemProperties.getInt("db.log.slow_query_threshold", -1);
-        return slowQueryMillis >= 0 && elapsedTimeMillis >= slowQueryMillis;
+    public static boolean shouldLogSlowQuery(long elapsedTimeMillis) {
+        final int slowQueryMillis = Math.min(
+                SystemProperties.getInt(Consts.SLOW_QUERY_THRESHOLD_PROP, Integer.MAX_VALUE),
+                SystemProperties.getInt(Consts.SLOW_QUERY_THRESHOLD_UID_PROP,
+                        Integer.MAX_VALUE));
+        return elapsedTimeMillis >= slowQueryMillis;
     }
 
     /**
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index c6c676f..06560f2 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -16,17 +16,26 @@
 
 package android.database.sqlite;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentValues;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
+import android.os.Build;
 import android.os.CancellationSignal;
 import android.os.OperationCanceledException;
 import android.provider.BaseColumns;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 
+import libcore.util.EmptyArray;
+
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Set;
 import java.util.regex.Pattern;
 
@@ -34,8 +43,7 @@
  * This is a convenience class that helps build SQL queries to be sent to
  * {@link SQLiteDatabase} objects.
  */
-public class SQLiteQueryBuilder
-{
+public class SQLiteQueryBuilder {
     private static final String TAG = "SQLiteQueryBuilder";
     private static final Pattern sLimitPattern =
             Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
@@ -91,13 +99,10 @@
      *
      * @param inWhere the chunk of text to append to the WHERE clause.
      */
-    public void appendWhere(CharSequence inWhere) {
+    public void appendWhere(@NonNull CharSequence inWhere) {
         if (mWhereClause == null) {
             mWhereClause = new StringBuilder(inWhere.length() + 16);
         }
-        if (mWhereClause.length() == 0) {
-            mWhereClause.append('(');
-        }
         mWhereClause.append(inWhere);
     }
 
@@ -111,17 +116,35 @@
      * @param inWhere the chunk of text to append to the WHERE clause. it will be escaped
      * to avoid SQL injection attacks
      */
-    public void appendWhereEscapeString(String inWhere) {
+    public void appendWhereEscapeString(@NonNull String inWhere) {
         if (mWhereClause == null) {
             mWhereClause = new StringBuilder(inWhere.length() + 16);
         }
-        if (mWhereClause.length() == 0) {
-            mWhereClause.append('(');
-        }
         DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
     }
 
     /**
+     * Add a standalone chunk to the {@code WHERE} clause of this query.
+     * <p>
+     * This method differs from {@link #appendWhere(CharSequence)} in that it
+     * automatically appends {@code AND} to any existing {@code WHERE} clause
+     * already under construction before appending the given standalone
+     * expression wrapped in parentheses.
+     *
+     * @param inWhere the standalone expression to append to the {@code WHERE}
+     *            clause. It will be wrapped in parentheses when it's appended.
+     */
+    public void appendWhereStandalone(@NonNull CharSequence inWhere) {
+        if (mWhereClause == null) {
+            mWhereClause = new StringBuilder(inWhere.length() + 16);
+        }
+        if (mWhereClause.length() > 0) {
+            mWhereClause.append(" AND ");
+        }
+        mWhereClause.append('(').append(inWhere).append(')');
+    }
+
+    /**
      * Sets the projection map for the query.  The projection map maps
      * from column names that the caller passes into query to database
      * column names. This is useful for renaming columns as well as
@@ -376,6 +399,11 @@
             return null;
         }
 
+        final String sql;
+        final String unwrappedSql = buildQuery(
+                projectionIn, selection, groupBy, having,
+                sortOrder, limit);
+
         if (mStrict && selection != null && selection.length() > 0) {
             // Validate the user-supplied selection to detect syntactic anomalies
             // in the selection string that could indicate a SQL injection attempt.
@@ -384,25 +412,165 @@
             // originally specified. An attacker cannot create an expression that
             // would escape the SQL expression while maintaining balanced parentheses
             // in both the wrapped and original forms.
-            String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy,
+
+            // NOTE: The ordering of the below operations is important; we must
+            // execute the wrapped query to ensure the untrusted clause has been
+            // fully isolated.
+
+            // Validate the unwrapped query
+            db.validateSql(unwrappedSql, cancellationSignal); // will throw if query is invalid
+
+            // Execute wrapped query for extra protection
+            final String wrappedSql = buildQuery(projectionIn, wrap(selection), groupBy,
                     having, sortOrder, limit);
-            db.validateSql(sqlForValidation, cancellationSignal); // will throw if query is invalid
+            sql = wrappedSql;
+        } else {
+            // Execute unwrapped query
+            sql = unwrappedSql;
         }
 
-        String sql = buildQuery(
-                projectionIn, selection, groupBy, having,
-                sortOrder, limit);
-
+        final String[] sqlArgs = selectionArgs;
         if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Performing query: " + sql);
+            if (Build.IS_DEBUGGABLE) {
+                Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+            } else {
+                Log.d(TAG, sql);
+            }
         }
         return db.rawQueryWithFactory(
-                mFactory, sql, selectionArgs,
+                mFactory, sql, sqlArgs,
                 SQLiteDatabase.findEditTable(mTables),
                 cancellationSignal); // will throw if query is invalid
     }
 
     /**
+     * Perform an update by combining all current settings and the
+     * information passed into this method.
+     *
+     * @param db the database to update on
+     * @param selection A filter declaring which rows to return,
+     *   formatted as an SQL WHERE clause (excluding the WHERE
+     *   itself). Passing null will return all rows for the given URL.
+     * @param selectionArgs You may include ?s in selection, which
+     *   will be replaced by the values from selectionArgs, in order
+     *   that they appear in the selection. The values will be bound
+     *   as Strings.
+     * @return the number of rows updated
+     */
+    public int update(@NonNull SQLiteDatabase db, @NonNull ContentValues values,
+            @Nullable String selection, @Nullable String[] selectionArgs) {
+        Objects.requireNonNull(mTables, "No tables defined");
+        Objects.requireNonNull(db, "No database defined");
+        Objects.requireNonNull(values, "No values defined");
+
+        final String sql;
+        final String unwrappedSql = buildUpdate(values, selection);
+
+        if (mStrict) {
+            // Validate the user-supplied selection to detect syntactic anomalies
+            // in the selection string that could indicate a SQL injection attempt.
+            // The idea is to ensure that the selection clause is a valid SQL expression
+            // by compiling it twice: once wrapped in parentheses and once as
+            // originally specified. An attacker cannot create an expression that
+            // would escape the SQL expression while maintaining balanced parentheses
+            // in both the wrapped and original forms.
+
+            // NOTE: The ordering of the below operations is important; we must
+            // execute the wrapped query to ensure the untrusted clause has been
+            // fully isolated.
+
+            // Validate the unwrapped query
+            db.validateSql(unwrappedSql, null); // will throw if query is invalid
+
+            // Execute wrapped query for extra protection
+            final String wrappedSql = buildUpdate(values, wrap(selection));
+            sql = wrappedSql;
+        } else {
+            // Execute unwrapped query
+            sql = unwrappedSql;
+        }
+
+        if (selectionArgs == null) {
+            selectionArgs = EmptyArray.STRING;
+        }
+        final ArrayMap<String, Object> rawValues = values.getValues();
+        final int valuesLength = rawValues.size();
+        final Object[] sqlArgs = new Object[valuesLength + selectionArgs.length];
+        for (int i = 0; i < sqlArgs.length; i++) {
+            if (i < valuesLength) {
+                sqlArgs[i] = rawValues.valueAt(i);
+            } else {
+                sqlArgs[i] = selectionArgs[i - valuesLength];
+            }
+        }
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            if (Build.IS_DEBUGGABLE) {
+                Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+            } else {
+                Log.d(TAG, sql);
+            }
+        }
+        return db.executeSql(sql, sqlArgs);
+    }
+
+    /**
+     * Perform a delete by combining all current settings and the
+     * information passed into this method.
+     *
+     * @param db the database to delete on
+     * @param selection A filter declaring which rows to return,
+     *   formatted as an SQL WHERE clause (excluding the WHERE
+     *   itself). Passing null will return all rows for the given URL.
+     * @param selectionArgs You may include ?s in selection, which
+     *   will be replaced by the values from selectionArgs, in order
+     *   that they appear in the selection. The values will be bound
+     *   as Strings.
+     * @return the number of rows deleted
+     */
+    public int delete(@NonNull SQLiteDatabase db, @Nullable String selection,
+            @Nullable String[] selectionArgs) {
+        Objects.requireNonNull(mTables, "No tables defined");
+        Objects.requireNonNull(db, "No database defined");
+
+        final String sql;
+        final String unwrappedSql = buildDelete(selection);
+
+        if (mStrict) {
+            // Validate the user-supplied selection to detect syntactic anomalies
+            // in the selection string that could indicate a SQL injection attempt.
+            // The idea is to ensure that the selection clause is a valid SQL expression
+            // by compiling it twice: once wrapped in parentheses and once as
+            // originally specified. An attacker cannot create an expression that
+            // would escape the SQL expression while maintaining balanced parentheses
+            // in both the wrapped and original forms.
+
+            // NOTE: The ordering of the below operations is important; we must
+            // execute the wrapped query to ensure the untrusted clause has been
+            // fully isolated.
+
+            // Validate the unwrapped query
+            db.validateSql(unwrappedSql, null); // will throw if query is invalid
+
+            // Execute wrapped query for extra protection
+            final String wrappedSql = buildDelete(wrap(selection));
+            sql = wrappedSql;
+        } else {
+            // Execute unwrapped query
+            sql = unwrappedSql;
+        }
+
+        final String[] sqlArgs = selectionArgs;
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            if (Build.IS_DEBUGGABLE) {
+                Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+            } else {
+                Log.d(TAG, sql);
+            }
+        }
+        return db.executeSql(sql, sqlArgs);
+    }
+
+    /**
      * Construct a SELECT statement suitable for use in a group of
      * SELECT statements that will be joined through UNION operators
      * in buildUnionQuery.
@@ -434,28 +602,10 @@
             String[] projectionIn, String selection, String groupBy,
             String having, String sortOrder, String limit) {
         String[] projection = computeProjection(projectionIn);
-
-        StringBuilder where = new StringBuilder();
-        boolean hasBaseWhereClause = mWhereClause != null && mWhereClause.length() > 0;
-
-        if (hasBaseWhereClause) {
-            where.append(mWhereClause.toString());
-            where.append(')');
-        }
-
-        // Tack on the user's selection, if present.
-        if (selection != null && selection.length() > 0) {
-            if (hasBaseWhereClause) {
-                where.append(" AND ");
-            }
-
-            where.append('(');
-            where.append(selection);
-            where.append(')');
-        }
+        String where = computeWhere(selection);
 
         return buildQueryString(
-                mDistinct, mTables, projection, where.toString(),
+                mDistinct, mTables, projection, where,
                 groupBy, having, sortOrder, limit);
     }
 
@@ -472,6 +622,42 @@
         return buildQuery(projectionIn, selection, groupBy, having, sortOrder, limit);
     }
 
+    /** {@hide} */
+    public String buildUpdate(ContentValues values, String selection) {
+        if (values == null || values.isEmpty()) {
+            throw new IllegalArgumentException("Empty values");
+        }
+
+        StringBuilder sql = new StringBuilder(120);
+        sql.append("UPDATE ");
+        sql.append(mTables);
+        sql.append(" SET ");
+
+        final ArrayMap<String, Object> rawValues = values.getValues();
+        for (int i = 0; i < rawValues.size(); i++) {
+            if (i > 0) {
+                sql.append(',');
+            }
+            sql.append(rawValues.keyAt(i));
+            sql.append("=?");
+        }
+
+        final String where = computeWhere(selection);
+        appendClause(sql, " WHERE ", where);
+        return sql.toString();
+    }
+
+    /** {@hide} */
+    public String buildDelete(String selection) {
+        StringBuilder sql = new StringBuilder(120);
+        sql.append("DELETE FROM ");
+        sql.append(mTables);
+
+        final String where = computeWhere(selection);
+        appendClause(sql, " WHERE ", where);
+        return sql.toString();
+    }
+
     /**
      * Construct a SELECT statement suitable for use in a group of
      * SELECT statements that will be joined through UNION operators
@@ -645,4 +831,37 @@
         }
         return null;
     }
+
+    private @Nullable String computeWhere(@Nullable String selection) {
+        final boolean hasInternal = !TextUtils.isEmpty(mWhereClause);
+        final boolean hasExternal = !TextUtils.isEmpty(selection);
+
+        if (hasInternal || hasExternal) {
+            final StringBuilder where = new StringBuilder();
+            if (hasInternal) {
+                where.append('(').append(mWhereClause).append(')');
+            }
+            if (hasInternal && hasExternal) {
+                where.append(" AND ");
+            }
+            if (hasExternal) {
+                where.append('(').append(selection).append(')');
+            }
+            return where.toString();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Wrap given argument in parenthesis, unless it's {@code null} or
+     * {@code ()}, in which case return it verbatim.
+     */
+    private @Nullable String wrap(@Nullable String arg) {
+        if (TextUtils.isEmpty(arg)) {
+            return arg;
+        } else {
+            return "(" + arg + ")";
+        }
+    }
 }
diff --git a/core/java/android/ddm/DdmHandleAppName.java b/core/java/android/ddm/DdmHandleAppName.java
index 7e39e47..9560787 100644
--- a/core/java/android/ddm/DdmHandleAppName.java
+++ b/core/java/android/ddm/DdmHandleAppName.java
@@ -16,6 +16,7 @@
 
 package android.ddm;
 
+import android.annotation.UnsupportedAppUsage;
 import org.apache.harmony.dalvik.ddmc.Chunk;
 import org.apache.harmony.dalvik.ddmc.ChunkHandler;
 import org.apache.harmony.dalvik.ddmc.DdmServer;
@@ -69,6 +70,7 @@
      * before or after DDMS connects.  For the latter we need to send up
      * an APNM message.
      */
+    @UnsupportedAppUsage
     public static void setAppName(String name, int userId) {
         if (name == null || name.length() == 0)
             return;
@@ -79,6 +81,7 @@
         sendAPNM(name, userId);
     }
 
+    @UnsupportedAppUsage
     public static String getAppName() {
         return mAppName;
     }
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 9350aab..8e96f56 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -164,6 +165,7 @@
     private static final int CAMERA_MSG_PREVIEW_METADATA = 0x400;
     private static final int CAMERA_MSG_FOCUS_MOVE       = 0x800;
 
+    @UnsupportedAppUsage
     private long mNativeContext; // accessed by native methods
     private EventHandler mEventHandler;
     private ShutterCallback mShutterCallback;
@@ -236,6 +238,7 @@
      * Camera HAL device API version 1.0
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int CAMERA_HAL_API_VERSION_1_0 = 0x100;
 
     /**
@@ -451,6 +454,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static Camera openLegacy(int cameraId, int halVersion) {
         if (halVersion < CAMERA_HAL_API_VERSION_1_0) {
             throw new IllegalArgumentException("Invalid HAL version " + halVersion);
@@ -571,9 +575,7 @@
     /**
      * An empty Camera for testing purpose.
      */
-    Camera() {
-        initAppOps();
-    }
+    Camera() {}
 
     private void initAppOps() {
         IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
@@ -606,6 +608,7 @@
         release();
     }
 
+    @UnsupportedAppUsage
     private native final int native_setup(Object camera_this, int cameraId, int halVersion,
                                            String packageName);
 
@@ -718,6 +721,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public native final void setPreviewSurface(Surface surface) throws IOException;
 
     /**
@@ -841,6 +845,7 @@
      * FIXME: Unhide before release
      * @hide
      */
+    @UnsupportedAppUsage
     public native final boolean previewEnabled();
 
     /**
@@ -1014,11 +1019,13 @@
      *
      * {@hide}
      */
+    @UnsupportedAppUsage
     public final void addRawImageCallbackBuffer(byte[] callbackBuffer)
     {
         addCallbackBuffer(callbackBuffer, CAMERA_MSG_RAW_IMAGE);
     }
 
+    @UnsupportedAppUsage
     private final void addCallbackBuffer(byte[] callbackBuffer, int msgType)
     {
         // CAMERA_MSG_VIDEO_FRAME may be allowed in the future.
@@ -1265,6 +1272,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     private static void postEventFromNative(Object camera_ref,
                                             int what, int arg1, int arg2, Object obj)
     {
@@ -2077,7 +2085,9 @@
         mDetailedErrorCallback = cb;
     }
 
+    @UnsupportedAppUsage
     private native final void native_setParameters(String params);
+    @UnsupportedAppUsage
     private native final String native_getParameters();
 
     /**
@@ -2126,6 +2136,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static Parameters getEmptyParameters() {
         Camera camera = new Camera();
         return camera.new Parameters();
@@ -2660,6 +2671,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public void copyFrom(Parameters other) {
             if (other == null) {
                 throw new NullPointerException("other must not be null");
@@ -2691,6 +2703,7 @@
          * @deprecated
          */
         @Deprecated
+        @UnsupportedAppUsage
         public void dump() {
             Log.e(TAG, "dump: size=" + mMap.size());
             for (String k : mMap.keySet()) {
@@ -4409,6 +4422,7 @@
         // Splits a comma delimited string to an ArrayList of Area objects.
         // Example string: "(-10,-10,0,0,300),(0,0,10,10,700)". Return null if
         // the passing string is null or the size is 0 or (0,0,0,0,0).
+        @UnsupportedAppUsage
         private ArrayList<Area> splitArea(String str) {
             if (str == null || str.charAt(0) != '('
                     || str.charAt(str.length() - 1) != ')') {
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 9aa3f40..c17aabb 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.LongDef;
 import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -89,6 +90,7 @@
     public static final int S_UI8        = 0x35;
 
     // Note: do not rename, this field is used by native code
+    @UnsupportedAppUsage
     private long mNativeObject;
 
     // Invoked on destruction
@@ -182,6 +184,7 @@
      * Private use only. See {@link #create(int, int, int, int, long)}. May also be
      * called from JNI using an already allocated native <code>HardwareBuffer</code>.
      */
+    @UnsupportedAppUsage
     private HardwareBuffer(long nativeObject) {
         mNativeObject = nativeObject;
 
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 7297426..4aa6fab 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -18,6 +18,7 @@
 package android.hardware;
 
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
 
 /**
@@ -504,6 +505,7 @@
      *
      * @hide Expected to be used internally for always on display.
      */
+    @UnsupportedAppUsage
     public static final int TYPE_PICK_UP_GESTURE = 25;
 
     /**
@@ -543,6 +545,7 @@
      * @hide Expected to be used internally for auto-rotate and speaker rotation.
      *
      */
+    @UnsupportedAppUsage
     public static final int TYPE_DEVICE_ORIENTATION = 27;
 
     /**
@@ -891,6 +894,7 @@
     private String  mStringType;
     private String  mRequiredPermission;
     private int     mMaxDelay;
+    @UnsupportedAppUsage
     private int     mFlags;
     private int     mId;
 
@@ -1014,6 +1018,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public int getHandle() {
         return mHandle;
     }
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index bbd04a3..8c910b2 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -16,6 +16,8 @@
 
 package android.hardware;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * This class represents a {@link android.hardware.Sensor Sensor} event and
  * holds information such as the sensor's type, the time-stamp, accuracy and of
@@ -649,6 +651,7 @@
      */
     public long timestamp;
 
+    @UnsupportedAppUsage
     SensorEvent(int valueSize) {
         values = new float[valueSize];
     }
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index cbddc91..3250428 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Build;
 import android.os.Handler;
@@ -368,6 +369,7 @@
     /**
      * {@hide}
      */
+    @UnsupportedAppUsage
     public SensorManager() {
     }
 
diff --git a/core/java/android/hardware/SerialManager.java b/core/java/android/hardware/SerialManager.java
index 610f6a5..571c3cc 100644
--- a/core/java/android/hardware/SerialManager.java
+++ b/core/java/android/hardware/SerialManager.java
@@ -17,6 +17,7 @@
 package android.hardware;
 
 import android.annotation.SystemService;
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -46,6 +47,7 @@
      *
      * @return names of available serial ports
      */
+    @UnsupportedAppUsage
     public String[] getSerialPorts() {
         try {
             return mService.getSerialPorts();
@@ -65,6 +67,7 @@
      * @param speed at which to open the serial port
      * @return the serial port
      */
+    @UnsupportedAppUsage
     public SerialPort openSerialPort(String name, int speed) throws IOException {
         try {
             ParcelFileDescriptor pfd = mService.openSerialPort(name);
diff --git a/core/java/android/hardware/SerialPort.java b/core/java/android/hardware/SerialPort.java
index 5d83d9c..78ac3c0 100644
--- a/core/java/android/hardware/SerialPort.java
+++ b/core/java/android/hardware/SerialPort.java
@@ -16,6 +16,7 @@
 
 package android.hardware;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.ParcelFileDescriptor;
 
 import java.io.FileDescriptor;
@@ -31,6 +32,7 @@
     private static final String TAG = "SerialPort";
 
     // used by the JNI code
+    @UnsupportedAppUsage
     private int mNativeContext;
     private final String mName;
     private ParcelFileDescriptor mFileDescriptor;
@@ -59,6 +61,7 @@
     /**
      * Closes the serial port
      */
+    @UnsupportedAppUsage
     public void close() throws IOException {
         if (mFileDescriptor != null) {
             mFileDescriptor.close();
@@ -102,6 +105,7 @@
      * @param buffer to write
      * @param length number of bytes to write
      */
+    @UnsupportedAppUsage
     public void write(ByteBuffer buffer, int length) throws IOException {
         if (buffer.isDirect()) {
             native_write_direct(buffer, length);
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 1174cb6..7abfabf 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -16,6 +16,7 @@
 
 package android.hardware;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -758,10 +759,13 @@
             if (sensor == null) throw new NullPointerException();
             return nativeDisableSensor(mNativeSensorEventQueue, sensor.getHandle());
         }
+        @UnsupportedAppUsage
         protected abstract void dispatchSensorEvent(int handle, float[] values, int accuracy,
                 long timestamp);
+        @UnsupportedAppUsage
         protected abstract void dispatchFlushCompleteEvent(int handle);
 
+        @UnsupportedAppUsage
         protected void dispatchAdditionalInfoEvent(
                 int handle, int type, int serial, float[] floatValues, int[] intValues) {
             // default implementation is do nothing
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index c811999..e7c5116 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -33,7 +33,50 @@
      * Container for biometric data
      * @hide
      */
-    abstract class BiometricIdentifier implements Parcelable {}
+    abstract class Identifier implements Parcelable {
+        private CharSequence mName;
+        private int mBiometricId;
+        private long mDeviceId; // physical device this is associated with
+
+        public Identifier() {}
+
+        public Identifier(CharSequence name, int biometricId, long deviceId) {
+            mName = name;
+            mBiometricId = biometricId;
+            mDeviceId = deviceId;
+        }
+
+        /**
+         * Gets the human-readable name for the given biometric.
+         * @return name given to the biometric
+         */
+        public CharSequence getName() {
+            return mName;
+        }
+
+        /**
+         * Gets the device-specific biometric id.  Used by Settings to map a name to a specific
+         * biometric template.
+         */
+        public int getBiometricId() {
+            return mBiometricId;
+        }
+
+        /**
+         * Device this biometric belongs to.
+         */
+        public long getDeviceId() {
+            return mDeviceId;
+        }
+
+        public void setName(CharSequence name) {
+            mName = name;
+        }
+
+        public void setDeviceId(long deviceId) {
+            mDeviceId = deviceId;
+        }
+    }
 
     /**
      * Container for callback data from {@link BiometricAuthenticator#authenticate(
@@ -42,7 +85,7 @@
      * AuthenticationCallback)}
      */
     class AuthenticationResult {
-        private BiometricIdentifier mIdentifier;
+        private Identifier mIdentifier;
         private CryptoObject mCryptoObject;
         private int mUserId;
 
@@ -58,7 +101,7 @@
          * @param userId
          * @hide
          */
-        public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+        public AuthenticationResult(CryptoObject crypto, Identifier identifier,
                 int userId) {
             mCryptoObject = crypto;
             mIdentifier = identifier;
@@ -80,7 +123,7 @@
          * operations.
          * @hide
          */
-        public BiometricIdentifier getId() {
+        public Identifier getId() {
             return mIdentifier;
         }
 
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index a037289..5cf8f45 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -19,6 +19,10 @@
 
 /**
  * Interface containing all of the biometric modality agnostic constants.
+ *
+ * NOTE: The error messages must be consistent between BiometricConstants, Biometric*Constants,
+ *       and the frameworks/support/biometric/.../BiometricConstants files.
+ *
  * @hide
  */
 public interface BiometricConstants {
@@ -106,6 +110,13 @@
     int BIOMETRIC_ERROR_HW_NOT_PRESENT = 12;
 
     /**
+     * The user pressed the negative button. This is a placeholder that is currently only used
+     * by the support library.
+     * @hide
+     */
+    int BIOMETRIC_ERROR_NEGATIVE_BUTTON = 13;
+
+    /**
      * @hide
      */
     int BIOMETRIC_ERROR_VENDOR_BASE = 1000;
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
new file mode 100644
index 0000000..4aa1e76
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -0,0 +1,237 @@
+/*
+ * 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.biometrics;
+
+import android.hardware.face.FaceManager;
+
+/**
+ * Interface containing all of the face-specific constants.
+ *
+ * NOTE: The error messages must be consistent between BiometricConstants, Biometric*Constants,
+ *       and the frameworks/support/biometric/.../BiometricConstants files.
+ *
+ * @hide
+ */
+public interface BiometricFaceConstants {
+    //
+    // Error messages from face authentication hardware during initialization, enrollment,
+    // authentication or removal. Must agree with the list in HAL h file
+    //
+    /**
+     * The hardware is unavailable. Try again later.
+     */
+    public static final int FACE_ERROR_HW_UNAVAILABLE = 1;
+
+    /**
+     * Error state returned when the sensor was unable to process the current image.
+     */
+    public static final int FACE_ERROR_UNABLE_TO_PROCESS = 2;
+
+    /**
+     * Error state returned when the current request has been running too long. This is intended to
+     * prevent programs from waiting for the face authentication sensor indefinitely. The timeout is
+     * platform and sensor-specific, but is generally on the order of 30 seconds.
+     */
+    public static final int FACE_ERROR_TIMEOUT = 3;
+
+    /**
+     * Error state returned for operations like enrollment; the operation cannot be completed
+     * because there's not enough storage remaining to complete the operation.
+     */
+    public static final int FACE_ERROR_NO_SPACE = 4;
+
+    /**
+     * The operation was canceled because the face authentication sensor is unavailable. For
+     * example, this may happen when the user is switched, the device is locked or another pending
+     * operation prevents or disables it.
+     */
+    public static final int FACE_ERROR_CANCELED = 5;
+
+    /**
+     * The {@link FaceManager#remove} call failed. Typically this will happen when the
+     * provided face id was incorrect.
+     *
+     * @hide
+     */
+    public static final int FACE_ERROR_UNABLE_TO_REMOVE = 6;
+
+    /**
+     * The operation was canceled because the API is locked out due to too many attempts.
+     * This occurs after 5 failed attempts, and lasts for 30 seconds.
+     */
+    public static final int FACE_ERROR_LOCKOUT = 7;
+
+    /**
+     * Hardware vendors may extend this list if there are conditions that do not fall under one of
+     * the above categories. Vendors are responsible for providing error strings for these errors.
+     * These messages are typically reserved for internal operations such as enrollment, but may be
+     * used to express vendor errors not covered by the ones in HAL h file. Applications are
+     * expected to show the error message string if they happen, but are advised not to rely on the
+     * message id since they will be device and vendor-specific
+     */
+    public static final int FACE_ERROR_VENDOR = 8;
+
+    /**
+     * The operation was canceled because FACE_ERROR_LOCKOUT occurred too many times.
+     * Face authentication is disabled until the user unlocks with strong authentication
+     * (PIN/Pattern/Password)
+     */
+    public static final int FACE_ERROR_LOCKOUT_PERMANENT = 9;
+
+    /**
+     * The user canceled the operation. Upon receiving this, applications should use alternate
+     * authentication (e.g. a password). The application should also provide the means to return
+     * to face authentication, such as a "use face authentication" button.
+     */
+    public static final int FACE_ERROR_USER_CANCELED = 10;
+
+    /**
+     * The user does not have a face enrolled.
+     */
+    public static final int FACE_ERROR_NOT_ENROLLED = 11;
+
+    /**
+     * The device does not have a face sensor. This message will propagate if the calling app
+     * ignores the result from PackageManager.hasFeature(FEATURE_FACE) and calls
+     * this API anyway. Apps should always check for the feature before calling this API.
+     */
+    public static final int FACE_ERROR_HW_NOT_PRESENT = 12;
+
+    /**
+     * The user pressed the negative button. This is a placeholder that is currently only used
+     * by the support library.
+     * @hide
+     */
+    public static final int FACE_ERROR_NEGATIVE_BUTTON = 13;
+
+    /**
+     * @hide
+     */
+    public static final int FACE_ERROR_VENDOR_BASE = 1000;
+
+    //
+    // Image acquisition messages. These will not be sent to the user, since they conflict with
+    // existing constants. These must agree with face@1.0/types.hal.
+    //
+
+    /**
+     * The image acquired was good.
+     */
+    public static final int FACE_ACQUIRED_GOOD = 0;
+
+    /**
+     * The face image was not good enough to process due to a detected condition.
+     * (See {@link #FACE_ACQUIRED_TOO_BRIGHT or @link #FACE_ACQUIRED_TOO_DARK}).
+     */
+    public static final int FACE_ACQUIRED_INSUFFICIENT = 1;
+
+    /**
+     * The face image was too bright due to too much ambient light.
+     * For example, it's reasonable to return this after multiple
+     * {@link #FACE_ACQUIRED_INSUFFICIENT}
+     * The user is expected to take action to retry in better lighting conditions
+     * when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_BRIGHT = 2;
+
+    /**
+     * The face image was too dark due to illumination light obscured.
+     * For example, it's reasonable to return this after multiple
+     * {@link #FACE_ACQUIRED_INSUFFICIENT}
+     * The user is expected to take action to retry in better lighting conditions
+     * when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_DARK = 3;
+
+    /**
+     * The detected face is too close to the sensor, and the image can't be processed.
+     * The user should be informed to move farther from the sensor when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_CLOSE = 4;
+
+    /**
+     * The detected face is too small, as the user might be too far from the sensor.
+     * The user should be informed to move closer to the sensor when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_FAR = 5;
+
+    /**
+     * Only the upper part of the face was detected. The sensor field of view is too high.
+     * The user should be informed to move up with respect to the sensor when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_HIGH = 6;
+
+    /**
+     * Only the lower part of the face was detected. The sensor field of view is too low.
+     * The user should be informed to move down with respect to the sensor when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_LOW = 7;
+
+    /**
+     * Only the right part of the face was detected. The sensor field of view is too far right.
+     * The user should be informed to move to the right with respect to the sensor
+     * when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_RIGHT = 8;
+
+    /**
+     * Only the left part of the face was detected. The sensor field of view is too far left.
+     * The user should be informed to move to the left with respect to the sensor
+     * when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_LEFT = 9;
+
+    /**
+     * The user's eyes have strayed away from the sensor. If this message is sent, the user should
+     * be informed to look at the device. If the user can't be found in the frame, one of the other
+     * acquisition messages should be sent, e.g. FACE_ACQUIRED_NOT_DETECTED.
+     */
+    public static final int FACE_ACQUIRED_POOR_GAZE = 10;
+
+    /**
+     * No face was detected in front of the sensor.
+     * The user should be informed to point the sensor to a face when this is returned.
+     */
+    public static final int FACE_ACQUIRED_NOT_DETECTED = 11;
+
+    /**
+     * Too much motion was detected.
+     * The user should be informed to keep their face steady relative to the
+     * sensor.
+     */
+    public static final int FACE_ACQUIRED_TOO_MUCH_MOTION = 12;
+
+    /**
+     * The sensor needs to be re-calibrated. This is an unexpected condition, and should only be
+     * sent if a serious, uncorrectable, and unrecoverable calibration issue is detected which
+     * requires user intervention, e.g. re-enrolling. The expected response to this message is to
+     * direct the user to re-enroll.
+     */
+    public static final int FACE_ACQUIRED_RECALIBRATE = 13;
+
+    /**
+     * Hardware vendors may extend this list if there are conditions that do not fall under one of
+     * the above categories. Vendors are responsible for providing error strings for these errors.
+     *
+     * @hide
+     */
+    public static final int FACE_ACQUIRED_VENDOR = 13;
+    /**
+     * @hide
+     */
+    public static final int FACE_ACQUIRED_VENDOR_BASE = 1000;
+}
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index 638f525..d583d78 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -20,6 +20,10 @@
 
 /**
  * Interface containing all of the fingerprint-specific constants.
+ *
+ * NOTE: The error messages must be consistent between BiometricConstants, Biometric*Constants,
+ *       and the frameworks/support/biometric/.../BiometricConstants files.
+ *
  * @hide
  */
 public interface BiometricFingerprintConstants {
@@ -107,6 +111,13 @@
     public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12;
 
     /**
+     * The user pressed the negative button. This is a placeholder that is currently only used
+     * by the support library.
+     * @hide
+     */
+    public static final int FINGERPRINT_ERROR_NEGATIVE_BUTTON = 13;
+
+    /**
      * @hide
      */
     public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 1c9de45..bad418a 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -293,7 +293,7 @@
          * @param userId
          * @hide
          */
-        public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+        public AuthenticationResult(CryptoObject crypto, Identifier identifier,
                 int userId) {
             super(crypto, identifier, userId);
         }
diff --git a/core/java/android/hardware/biometrics/BiometricSourceType.aidl b/core/java/android/hardware/biometrics/BiometricSourceType.aidl
new file mode 100644
index 0000000..15440d8
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricSourceType.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.biometrics;
+
+/**
+ * @hide
+ */
+parcelable BiometricSourceType;
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/BiometricSourceType.java b/core/java/android/hardware/biometrics/BiometricSourceType.java
new file mode 100644
index 0000000..4a08cf2
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricSourceType.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 android.hardware.biometrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public enum BiometricSourceType implements Parcelable {
+    FINGERPRINT,
+    FACE,
+    IRIS;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(name());
+    }
+
+    public static final Creator<BiometricSourceType> CREATOR = new Creator<BiometricSourceType>() {
+        @Override
+        public BiometricSourceType createFromParcel(final Parcel source) {
+            return BiometricSourceType.valueOf(source.readString());
+        }
+
+        @Override
+        public BiometricSourceType[] newArray(final int size) {
+            return new BiometricSourceType[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl b/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
new file mode 100644
index 0000000..ee033bf
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.biometrics;
+
+import android.os.IRemoteCallback;
+
+/**
+ * Callback when lockout period expired and clients are allowed to authenticate again.
+ * @hide
+ */
+oneway interface IBiometricServiceLockoutResetCallback {
+
+    /**
+     * A wakelock will be held until the reciever calls back into {@param callback}
+     */
+    void onLockoutReset(long deviceId, IRemoteCallback callback);
+}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 87c64cd..f7d1b8d 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.PublicKey;
 import android.hardware.camera2.impl.SyntheticKey;
@@ -72,6 +73,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public Key(String name, Class<T> type, long vendorId) {
             mKey = new CameraMetadataNative.Key<T>(name,  type, vendorId);
         }
@@ -90,6 +92,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public Key(String name, Class<T> type) {
             mKey = new CameraMetadataNative.Key<T>(name,  type);
         }
@@ -99,6 +102,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public Key(String name, TypeReference<T> typeReference) {
             mKey = new CameraMetadataNative.Key<T>(name,  typeReference);
         }
@@ -168,6 +172,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public CameraMetadataNative.Key<T> getNativeKey() {
             return mKey;
         }
@@ -180,6 +185,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     private final CameraMetadataNative mProperties;
     private List<CameraCharacteristics.Key<?>> mKeys;
     private List<CaptureRequest.Key<?>> mAvailableRequestKeys;
@@ -767,6 +773,7 @@
      * @see CaptureRequest#CONTROL_AWB_REGIONS
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<int[]> CONTROL_MAX_REGIONS =
             new Key<int[]>("android.control.maxRegions", int[].class);
 
@@ -872,6 +879,7 @@
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<android.hardware.camera2.params.HighSpeedVideoConfiguration[]> CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS =
             new Key<android.hardware.camera2.params.HighSpeedVideoConfiguration[]>("android.control.availableHighSpeedVideoConfigurations", android.hardware.camera2.params.HighSpeedVideoConfiguration[].class);
 
@@ -1140,6 +1148,7 @@
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<android.util.Size> LENS_INFO_SHADING_MAP_SIZE =
             new Key<android.util.Size>("android.lens.info.shadingMapSize", android.util.Size.class);
 
@@ -1249,7 +1258,9 @@
      * <p>If this device is the largest or only camera device with a given facing, then this
      * position will be <code>(0, 0, 0)</code>; a camera device with a lens optical center located 3 cm
      * from the main sensor along the +X axis (to the right from the user's perspective) will
-     * report <code>(0.03, 0, 0)</code>.</p>
+     * report <code>(0.03, 0, 0)</code>.  Note that this means that, for many computer vision
+     * applications, the position needs to be negated to convert it to a translation from the
+     * camera to the origin.</p>
      * <p>To transform a pixel coordinates between two cameras facing the same direction, first
      * the source camera {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} must be corrected for.  Then the source
      * camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the
@@ -1261,7 +1272,8 @@
      * <p>To compare this against a real image from the destination camera, the destination camera
      * image then needs to be corrected for radial distortion before comparison or sampling.</p>
      * <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is GYROSCOPE, then this position is relative to
-     * the center of the primary gyroscope on the device.</p>
+     * the center of the primary gyroscope on the device. The axis definitions are the same as
+     * with PRIMARY_CAMERA.</p>
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
@@ -1293,13 +1305,15 @@
      * </code></pre>
      * <p>which can then be combined with the camera pose rotation
      * <code>R</code> and translation <code>t</code> ({@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} and
-     * {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}, respective) to calculate the
+     * {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}, respectively) to calculate the
      * complete transform from world coordinates to pixel
      * coordinates:</p>
-     * <pre><code>P = [ K 0   * [ R t
-     *      0 1 ]     0 1 ]
+     * <pre><code>P = [ K 0   * [ R -Rt
+     *      0 1 ]      0 1 ]
      * </code></pre>
-     * <p>and with <code>p_w</code> being a point in the world coordinate system
+     * <p>(Note the negation of poseTranslation when mapping from camera
+     * to world coordinates, and multiplication by the rotation).</p>
+     * <p>With <code>p_w</code> being a point in the world coordinate system
      * and <code>p_s</code> being a point in the camera active pixel array
      * coordinate system, and with the mapping including the
      * homogeneous division by z:</p>
@@ -1321,6 +1335,13 @@
      * activeArraySize rectangle), to determine the final pixel
      * coordinate of the world point for processed (non-RAW)
      * output buffers.</p>
+     * <p>For camera devices, the center of pixel <code>(x,y)</code> is located at
+     * coordinate <code>(x + 0.5, y + 0.5)</code>.  So on a device with a
+     * precorrection active array of size <code>(10,10)</code>, the valid pixel
+     * indices go from <code>(0,0)-(9,9)</code>, and an perfectly-built camera would
+     * have an optical center at the exact center of the pixel grid, at
+     * coordinates <code>(5.0, 5.0)</code>, which is the top-left corner of pixel
+     * <code>(5,5)</code>.</p>
      * <p><b>Units</b>:
      * Pixels in the
      * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
@@ -1479,6 +1500,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final Key<Byte> QUIRKS_USE_PARTIAL_RESULT =
             new Key<Byte>("android.quirks.usePartialResult", byte.class);
 
@@ -1516,6 +1538,7 @@
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<int[]> REQUEST_MAX_NUM_OUTPUT_STREAMS =
             new Key<int[]>("android.request.maxNumOutputStreams", int[].class);
 
@@ -1755,6 +1778,7 @@
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<int[]> REQUEST_AVAILABLE_REQUEST_KEYS =
             new Key<int[]>("android.request.availableRequestKeys", int[].class);
 
@@ -1780,6 +1804,7 @@
      * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<int[]> REQUEST_AVAILABLE_RESULT_KEYS =
             new Key<int[]>("android.request.availableResultKeys", int[].class);
 
@@ -1792,6 +1817,7 @@
      * <p>This key is available on all devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<int[]> REQUEST_AVAILABLE_CHARACTERISTICS_KEYS =
             new Key<int[]>("android.request.availableCharacteristicsKeys", int[].class);
 
@@ -1838,6 +1864,7 @@
      * <p>This key is available on all devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<int[]> REQUEST_AVAILABLE_SESSION_KEYS =
             new Key<int[]>("android.request.availableSessionKeys", int[].class);
 
@@ -1858,6 +1885,7 @@
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<int[]> REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS =
             new Key<int[]>("android.request.availablePhysicalCameraRequestKeys", int[].class);
 
@@ -1873,6 +1901,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final Key<int[]> SCALER_AVAILABLE_FORMATS =
             new Key<int[]>("android.scaler.availableFormats", int[].class);
 
@@ -1895,6 +1924,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final Key<long[]> SCALER_AVAILABLE_JPEG_MIN_DURATIONS =
             new Key<long[]>("android.scaler.availableJpegMinDurations", long[].class);
 
@@ -1913,6 +1943,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final Key<android.util.Size[]> SCALER_AVAILABLE_JPEG_SIZES =
             new Key<android.util.Size[]>("android.scaler.availableJpegSizes", android.util.Size[].class);
 
@@ -1954,6 +1985,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final Key<long[]> SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS =
             new Key<long[]>("android.scaler.availableProcessedMinDurations", long[].class);
 
@@ -1978,6 +2010,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final Key<android.util.Size[]> SCALER_AVAILABLE_PROCESSED_SIZES =
             new Key<android.util.Size[]>("android.scaler.availableProcessedSizes", android.util.Size[].class);
 
@@ -2033,6 +2066,7 @@
      * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<android.hardware.camera2.params.ReprocessFormatsMap> SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP =
             new Key<android.hardware.camera2.params.ReprocessFormatsMap>("android.scaler.availableInputOutputFormatsMap", android.hardware.camera2.params.ReprocessFormatsMap.class);
 
@@ -2125,6 +2159,7 @@
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<android.hardware.camera2.params.StreamConfiguration[]> SCALER_AVAILABLE_STREAM_CONFIGURATIONS =
             new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.scaler.availableStreamConfigurations", android.hardware.camera2.params.StreamConfiguration[].class);
 
@@ -2147,6 +2182,7 @@
      * @see CaptureRequest#SENSOR_FRAME_DURATION
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> SCALER_AVAILABLE_MIN_FRAME_DURATIONS =
             new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.scaler.availableMinFrameDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
 
@@ -2215,6 +2251,7 @@
      * @see CaptureRequest#SENSOR_FRAME_DURATION
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> SCALER_AVAILABLE_STALL_DURATIONS =
             new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.scaler.availableStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
 
@@ -2365,13 +2402,25 @@
      * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, is defined relative to the active array rectangle given in
      * this field, with <code>(0, 0)</code> being the top-left of this rectangle.</p>
      * <p>The active array may be smaller than the full pixel array, since the full array may
-     * include black calibration pixels or other inactive regions, and geometric correction
-     * resulting in scaling or cropping may have been applied.</p>
+     * include black calibration pixels or other inactive regions.</p>
+     * <p>For devices that do not support {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the active
+     * array must be the same as {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.</p>
+     * <p>For devices that support {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the active array must
+     * be enclosed by {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}. The difference between
+     * pre-correction active array and active array accounts for scaling or cropping caused
+     * by lens geometric distortion correction.</p>
+     * <p>In general, application should always refer to active array size for controls like
+     * metering regions or crop region. Two exceptions are when the application is dealing with
+     * RAW image buffers (RAW_SENSOR, RAW10, RAW12 etc), or when application explicitly set
+     * {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} to OFF. In these cases, application should refer
+     * to {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.</p>
      * <p><b>Units</b>: Pixel coordinates on the image sensor</p>
      * <p>This key is available on all devices.</p>
      *
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
     @PublicKey
     public static final Key<android.graphics.Rect> SENSOR_INFO_ACTIVE_ARRAY_SIZE =
@@ -2616,9 +2665,9 @@
      * <ol>
      * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}.</li>
      * </ol>
-     * <p>If all of the geometric distortion fields are no-ops, this rectangle will be the same
-     * as the post-distortion-corrected rectangle given in
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
+     * <p>If the camera device doesn't support geometric distortion correction, or all of the
+     * geometric distortion fields are no-ops, this rectangle will be the same as the
+     * post-distortion-corrected rectangle given in {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
      * <p>This rectangle is defined relative to the full pixel array; (0,0) is the top-left of
      * the full pixel array, and the size of the full pixel array is given by
      * {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize}.</p>
@@ -3110,6 +3159,7 @@
      * @see #LED_AVAILABLE_LEDS_TRANSMIT
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<int[]> LED_AVAILABLE_LEDS =
             new Key<int[]>("android.led.availableLeds", int[].class);
 
@@ -3124,12 +3174,26 @@
      * the following code snippet can be used:</p>
      * <pre><code>// Returns true if the device supports the required hardware level, or better.
      * boolean isHardwareLevelSupported(CameraCharacteristics c, int requiredLevel) {
+     *     final int[] sortedHwLevels = {
+     *         CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+     *         CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
+     *         CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+     *         CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+     *         CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+     *     };
      *     int deviceLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
-     *     if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
-     *         return requiredLevel == deviceLevel;
+     *     if (requiredLevel == deviceLevel) {
+     *         return true;
      *     }
-     *     // deviceLevel is not LEGACY, can use numerical sort
-     *     return requiredLevel &lt;= deviceLevel;
+     *
+     *     for (int sortedlevel : sortedHwLevels) {
+     *         if (sortedlevel == requiredLevel) {
+     *             return true;
+     *         } else if (sortedlevel == deviceLevel) {
+     *             return false;
+     *         }
+     *     }
+     *     return false; // Should never reach here
      * }
      * </code></pre>
      * <p>At a high level, the levels are:</p>
@@ -3143,6 +3207,8 @@
      *   post-processing settings, and image capture at a high rate.</li>
      * <li><code>LEVEL_3</code> devices additionally support YUV reprocessing and RAW image capture, along
      *   with additional output stream configurations.</li>
+     * <li><code>EXTERNAL</code> devices are similar to <code>LIMITED</code> devices with exceptions like some sensor or
+     *   lens information not reorted or less stable framerates.</li>
      * </ul>
      * <p>See the individual level enums for full descriptions of the supported capabilities.  The
      * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} entry describes the device's capabilities at a
@@ -3280,6 +3346,7 @@
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<android.hardware.camera2.params.StreamConfiguration[]> DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS =
             new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.depth.availableDepthStreamConfigurations", android.hardware.camera2.params.StreamConfiguration[].class);
 
@@ -3306,6 +3373,7 @@
      * @see CaptureRequest#SENSOR_FRAME_DURATION
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS =
             new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDepthMinFrameDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
 
@@ -3329,6 +3397,7 @@
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS =
             new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDepthStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
 
@@ -3372,6 +3441,7 @@
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<byte[]> LOGICAL_MULTI_CAMERA_PHYSICAL_IDS =
             new Key<byte[]>("android.logicalMultiCamera.physicalIds", byte[].class);
 
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index f47d464..ce88697 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -44,19 +44,27 @@
  * {@link android.Manifest.permission#CAMERA Camera} permission in its manifest
  * in order to access camera devices.</p>
  *
- * <p>A given camera device may provide support at one of two levels: limited or
- * full. If a device only supports the limited level, then Camera2 exposes a
- * feature set that is roughly equivalent to the older
+ * <p>A given camera device may provide support at one of several levels defined
+ * in {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}.
+ * If a device supports {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY} level,
+ * the camera device is running in backward compatibility mode and has minimum camera2 API support.
+ * If a device supports the {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}
+ * level, then Camera2 exposes a feature set that is roughly equivalent to the older
  * {@link android.hardware.Camera Camera} API, although with a cleaner and more
- * efficient interface.  Devices that implement the full level of support
+ * efficient interface.
+ * If a device supports the {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}
+ * level, then the device is a removable camera that provides similar but slightly less features
+ * as the {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} level.
+ * Devices that implement the {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL} or
+ * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_3 LEVEL3} level of support
  * provide substantially improved capabilities over the older camera
- * API. Applications that target the limited level devices will run unchanged on
- * the full-level devices; if your application requires a full-level device for
+ * API. If your application requires a full-level device for
  * proper operation, declare the "android.hardware.camera.level.full" feature in your
  * manifest.</p>
  *
  * @see CameraManager#openCamera
  * @see android.Manifest.permission#CAMERA
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
  */
 public abstract class CameraDevice implements AutoCloseable {
 
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 411a97e..47bef12 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.PublicKey;
 import android.hardware.camera2.impl.SyntheticKey;
@@ -107,6 +108,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public Key(String name, Class<T> type, long vendorId) {
             mKey = new CameraMetadataNative.Key<T>(name, type, vendorId);
         }
@@ -116,6 +118,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public Key(String name, Class<T> type) {
             mKey = new CameraMetadataNative.Key<T>(name, type);
         }
@@ -125,6 +128,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public Key(String name, TypeReference<T> typeReference) {
             mKey = new CameraMetadataNative.Key<T>(name, typeReference);
         }
@@ -194,6 +198,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public CameraMetadataNative.Key<T> getNativeKey() {
             return mKey;
         }
@@ -223,6 +228,7 @@
     private static final ArraySet<Surface> mEmptySurfaceSet = new ArraySet<Surface>();
 
     private String mLogicalCameraId;
+    @UnsupportedAppUsage
     private CameraMetadataNative mLogicalCameraSettings;
     private final HashMap<String, CameraMetadataNative> mPhysicalCameraSettings =
             new HashMap<String, CameraMetadataNative>();
@@ -598,6 +604,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public Collection<Surface> getTargets() {
         return Collections.unmodifiableCollection(mSurfaceSet);
     }
@@ -886,6 +893,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public void setPartOfCHSRequestList(boolean partOfCHSList) {
             mRequest.mIsPartOfCHSRequestList = partOfCHSList;
         }
@@ -1269,11 +1277,26 @@
      * Otherwise will always be present.</p>
      * <p>The maximum number of regions supported by the device is determined by the value
      * of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AE android.control.maxRegionsAe}.</p>
-     * <p>The coordinate system is based on the active pixel array,
-     * with (0,0) being the top-left pixel in the active pixel array, and
+     * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+     * the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
-     * bottom-right pixel in the active pixel array.</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+     * active pixel array.</p>
+     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system depends on the mode being set.
+     * When the distortion correction mode is OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+     * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+     * pixel in the pre-correction active pixel array.
+     * When the distortion correction mode is not OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the active array, and
+     * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+     * active pixel array.</p>
      * <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
      * for every pixel in the area. This means that a large metering area
      * with the same weight as a smaller area will have more effect in
@@ -1289,15 +1312,20 @@
      * region and output only the intersection rectangle as the metering region in the result
      * metadata.  If the region is entirely outside the crop region, it will be ignored and
      * not reported in the result metadata.</p>
-     * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+     * distortion correction capability and mode</p>
      * <p><b>Range of valid values:</b><br>
      * Coordinates must be between <code>[(0,0), (width, height))</code> of
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+     * depending on distortion correction capability and mode</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#CONTROL_MAX_REGIONS_AE
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
     @PublicKey
     public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS =
@@ -1443,11 +1471,26 @@
      * Otherwise will always be present.</p>
      * <p>The maximum number of focus areas supported by the device is determined by the value
      * of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AF android.control.maxRegionsAf}.</p>
-     * <p>The coordinate system is based on the active pixel array,
-     * with (0,0) being the top-left pixel in the active pixel array, and
+     * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+     * the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
-     * bottom-right pixel in the active pixel array.</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+     * active pixel array.</p>
+     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system depends on the mode being set.
+     * When the distortion correction mode is OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+     * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+     * pixel in the pre-correction active pixel array.
+     * When the distortion correction mode is not OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the active array, and
+     * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+     * active pixel array.</p>
      * <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
      * for every pixel in the area. This means that a large metering area
      * with the same weight as a smaller area will have more effect in
@@ -1464,15 +1507,20 @@
      * region and output only the intersection rectangle as the metering region in the result
      * metadata. If the region is entirely outside the crop region, it will be ignored and
      * not reported in the result metadata.</p>
-     * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+     * distortion correction capability and mode</p>
      * <p><b>Range of valid values:</b><br>
      * Coordinates must be between <code>[(0,0), (width, height))</code> of
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+     * depending on distortion correction capability and mode</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#CONTROL_MAX_REGIONS_AF
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
     @PublicKey
     public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS =
@@ -1612,11 +1660,26 @@
      * Otherwise will always be present.</p>
      * <p>The maximum number of regions supported by the device is determined by the value
      * of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AWB android.control.maxRegionsAwb}.</p>
-     * <p>The coordinate system is based on the active pixel array,
-     * with (0,0) being the top-left pixel in the active pixel array, and
+     * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+     * the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
-     * bottom-right pixel in the active pixel array.</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+     * active pixel array.</p>
+     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system depends on the mode being set.
+     * When the distortion correction mode is OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+     * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+     * pixel in the pre-correction active pixel array.
+     * When the distortion correction mode is not OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the active array, and
+     * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+     * active pixel array.</p>
      * <p>The weight must range from 0 to 1000, and represents a weight
      * for every pixel in the area. This means that a large metering area
      * with the same weight as a smaller area will have more effect in
@@ -1632,15 +1695,20 @@
      * region and output only the intersection rectangle as the metering region in the result
      * metadata.  If the region is entirely outside the crop region, it will be ignored and
      * not reported in the result metadata.</p>
-     * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+     * distortion correction capability and mode</p>
      * <p><b>Range of valid values:</b><br>
      * Coordinates must be between <code>[(0,0), (width, height))</code> of
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+     * depending on distortion correction capability and mode</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#CONTROL_MAX_REGIONS_AWB
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
     @PublicKey
     public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS =
@@ -2072,6 +2140,7 @@
      * <p>This key is available on all devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<double[]> JPEG_GPS_COORDINATES =
             new Key<double[]>("android.jpeg.gpsCoordinates", double[].class);
 
@@ -2082,6 +2151,7 @@
      * <p>This key is available on all devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<String> JPEG_GPS_PROCESSING_METHOD =
             new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
 
@@ -2092,6 +2162,7 @@
      * <p>This key is available on all devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<Long> JPEG_GPS_TIMESTAMP =
             new Key<Long>("android.jpeg.gpsTimestamp", long.class);
 
@@ -2427,15 +2498,24 @@
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<Integer> REQUEST_ID =
             new Key<Integer>("android.request.id", int.class);
 
     /**
      * <p>The desired region of the sensor to read out for this capture.</p>
      * <p>This control can be used to implement digital zoom.</p>
-     * <p>The crop region coordinate system is based off
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the
-     * top-left corner of the sensor active array.</p>
+     * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
+     * the top-left pixel of the active array.</p>
+     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system depends on the mode being set.
+     * When the distortion correction mode is OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+     * When the distortion correction mode is not OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
      * <p>Output streams use this rectangle to produce their output,
      * cropping to a smaller region if necessary to maintain the
      * stream's aspect ratio, then scaling the sensor input to
@@ -2454,20 +2534,30 @@
      * outputs will crop horizontally (pillarbox), and 16:9
      * streams will match exactly. These additional crops will
      * be centered within the crop region.</p>
-     * <p>The width and height of the crop region cannot
-     * be set to be smaller than
+     * <p>If the coordinate system is {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, the width and height
+     * of the crop region cannot be set to be smaller than
      * <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and
      * <code>floor( activeArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>, respectively.</p>
+     * <p>If the coordinate system is {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, the width
+     * and height of the crop region cannot be set to be smaller than
+     * <code>floor( preCorrectionActiveArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>
+     * and
+     * <code>floor( preCorrectionActiveArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>,
+     * respectively.</p>
      * <p>The camera device may adjust the crop region to account
      * for rounding and other hardware requirements; the final
      * crop region used will be included in the output capture
      * result.</p>
      * <p><b>Units</b>: Pixel coordinates relative to
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
+     * capability and mode</p>
      * <p>This key is available on all devices.</p>
      *
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
     @PublicKey
     public static final Key<android.graphics.Rect> SCALER_CROP_REGION =
@@ -2785,8 +2875,14 @@
             new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
 
     /**
-     * <p>A control for selecting whether OIS position information is included in output
-     * result metadata.</p>
+     * <p>A control for selecting whether optical stabilization (OIS) position
+     * information is included in output result metadata.</p>
+     * <p>Since optical image stabilization generally involves motion much faster than the duration
+     * of individualq image exposure, multiple OIS samples can be included for a single capture
+     * result. For example, if the OIS reporting operates at 200 Hz, a typical camera operating
+     * at 30fps may have 6-7 OIS samples per capture result. This information can be combined
+     * with the rolling shutter skew to account for lens motion during image exposure in
+     * post-processing algorithms.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
@@ -2818,6 +2914,7 @@
      * @see CaptureRequest#TONEMAP_MODE
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<float[]> TONEMAP_CURVE_BLUE =
             new Key<float[]>("android.tonemap.curveBlue", float[].class);
 
@@ -2835,6 +2932,7 @@
      * @see CaptureRequest#TONEMAP_MODE
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<float[]> TONEMAP_CURVE_GREEN =
             new Key<float[]>("android.tonemap.curveGreen", float[].class);
 
@@ -2897,6 +2995,7 @@
      * @see CaptureRequest#TONEMAP_MODE
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<float[]> TONEMAP_CURVE_RED =
             new Key<float[]>("android.tonemap.curveRed", float[].class);
 
@@ -3071,6 +3170,7 @@
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<Boolean> LED_TRANSMIT =
             new Key<Boolean>("android.led.transmit", boolean.class);
 
@@ -3186,15 +3286,28 @@
      * any correction at all would slow down capture rate.  Every output stream will have a
      * similar amount of enhancement applied.</p>
      * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
-     * applied to any RAW output.  Metadata coordinates such as face rectangles or metering
-     * regions are also not affected by correction.</p>
-     * <p>Applications enabling distortion correction need to pay extra attention when converting
-     * image coordinates between corrected output buffers and the sensor array. For example, if
-     * the app supports tap-to-focus and enables correction, it then has to apply the distortion
-     * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
-     * calculate the tap position on the sensor active array to be used with
-     * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
-     * they need to be drawn on top of the corrected output buffers.</p>
+     * applied to any RAW output.</p>
+     * <p>This control will be on by default on devices that support this control. Applications
+     * disabling distortion correction need to pay extra attention with the coordinate system of
+     * metering regions, crop region, and face rectangles. When distortion correction is OFF,
+     * metadata coordinates follow the coordinate system of
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}. When distortion is not OFF, metadata
+     * coordinates follow the coordinate system of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.  The
+     * camera device will map these metadata fields to match the corrected image produced by the
+     * camera device, for both capture requests and results.  However, this mapping is not very
+     * precise, since rectangles do not generally map to rectangles when corrected.  Only linear
+     * scaling between the active array and precorrection active array coordinates is
+     * performed. Applications that require precise correction of metadata need to undo that
+     * linear scaling, and apply a more complete correction that takes into the account the app's
+     * own requirements.</p>
+     * <p>The full list of metadata that is affected in this way by distortion correction is:</p>
+     * <ul>
+     * <li>{@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}</li>
+     * <li>{@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}</li>
+     * <li>{@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions}</li>
+     * <li>{@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}</li>
+     * <li>{@link CaptureResult#STATISTICS_FACES android.statistics.faces}</li>
+     * </ul>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
@@ -3205,9 +3318,15 @@
      * {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CaptureRequest#CONTROL_AE_REGIONS
      * @see CaptureRequest#CONTROL_AF_REGIONS
+     * @see CaptureRequest#CONTROL_AWB_REGIONS
      * @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
      * @see CameraCharacteristics#LENS_DISTORTION
+     * @see CaptureRequest#SCALER_CROP_REGION
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+     * @see CaptureResult#STATISTICS_FACES
      * @see #DISTORTION_CORRECTION_MODE_OFF
      * @see #DISTORTION_CORRECTION_MODE_FAST
      * @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 361d83d..007794f 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.CaptureResultExtras;
 import android.hardware.camera2.impl.PublicKey;
@@ -78,6 +79,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public Key(String name, Class<T> type, long vendorId) {
             mKey = new CameraMetadataNative.Key<T>(name, type, vendorId);
         }
@@ -96,6 +98,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public Key(String name, Class<T> type) {
             mKey = new CameraMetadataNative.Key<T>(name, type);
         }
@@ -105,6 +108,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public Key(String name, TypeReference<T> typeReference) {
             mKey = new CameraMetadataNative.Key<T>(name, typeReference);
         }
@@ -174,6 +178,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public CameraMetadataNative.Key<T> getNativeKey() {
             return mKey;
         }
@@ -184,6 +189,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     private final CameraMetadataNative mResults;
     private final CaptureRequest mRequest;
     private final int mSequenceId;
@@ -730,11 +736,26 @@
      * Otherwise will always be present.</p>
      * <p>The maximum number of regions supported by the device is determined by the value
      * of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AE android.control.maxRegionsAe}.</p>
-     * <p>The coordinate system is based on the active pixel array,
-     * with (0,0) being the top-left pixel in the active pixel array, and
+     * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+     * the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
-     * bottom-right pixel in the active pixel array.</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+     * active pixel array.</p>
+     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system depends on the mode being set.
+     * When the distortion correction mode is OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+     * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+     * pixel in the pre-correction active pixel array.
+     * When the distortion correction mode is not OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the active array, and
+     * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+     * active pixel array.</p>
      * <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
      * for every pixel in the area. This means that a large metering area
      * with the same weight as a smaller area will have more effect in
@@ -750,15 +771,20 @@
      * region and output only the intersection rectangle as the metering region in the result
      * metadata.  If the region is entirely outside the crop region, it will be ignored and
      * not reported in the result metadata.</p>
-     * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+     * distortion correction capability and mode</p>
      * <p><b>Range of valid values:</b><br>
      * Coordinates must be between <code>[(0,0), (width, height))</code> of
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+     * depending on distortion correction capability and mode</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#CONTROL_MAX_REGIONS_AE
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
     @PublicKey
     public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS =
@@ -1152,11 +1178,26 @@
      * Otherwise will always be present.</p>
      * <p>The maximum number of focus areas supported by the device is determined by the value
      * of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AF android.control.maxRegionsAf}.</p>
-     * <p>The coordinate system is based on the active pixel array,
-     * with (0,0) being the top-left pixel in the active pixel array, and
+     * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+     * the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
-     * bottom-right pixel in the active pixel array.</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+     * active pixel array.</p>
+     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system depends on the mode being set.
+     * When the distortion correction mode is OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+     * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+     * pixel in the pre-correction active pixel array.
+     * When the distortion correction mode is not OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the active array, and
+     * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+     * active pixel array.</p>
      * <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
      * for every pixel in the area. This means that a large metering area
      * with the same weight as a smaller area will have more effect in
@@ -1173,15 +1214,20 @@
      * region and output only the intersection rectangle as the metering region in the result
      * metadata. If the region is entirely outside the crop region, it will be ignored and
      * not reported in the result metadata.</p>
-     * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+     * distortion correction capability and mode</p>
      * <p><b>Range of valid values:</b><br>
      * Coordinates must be between <code>[(0,0), (width, height))</code> of
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+     * depending on distortion correction capability and mode</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#CONTROL_MAX_REGIONS_AF
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
     @PublicKey
     public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS =
@@ -1730,11 +1776,26 @@
      * Otherwise will always be present.</p>
      * <p>The maximum number of regions supported by the device is determined by the value
      * of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AWB android.control.maxRegionsAwb}.</p>
-     * <p>The coordinate system is based on the active pixel array,
-     * with (0,0) being the top-left pixel in the active pixel array, and
+     * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+     * the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
-     * bottom-right pixel in the active pixel array.</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+     * active pixel array.</p>
+     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system depends on the mode being set.
+     * When the distortion correction mode is OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+     * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+     * pixel in the pre-correction active pixel array.
+     * When the distortion correction mode is not OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the active array, and
+     * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+     * active pixel array.</p>
      * <p>The weight must range from 0 to 1000, and represents a weight
      * for every pixel in the area. This means that a large metering area
      * with the same weight as a smaller area will have more effect in
@@ -1750,15 +1811,20 @@
      * region and output only the intersection rectangle as the metering region in the result
      * metadata.  If the region is entirely outside the crop region, it will be ignored and
      * not reported in the result metadata.</p>
-     * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+     * distortion correction capability and mode</p>
      * <p><b>Range of valid values:</b><br>
      * Coordinates must be between <code>[(0,0), (width, height))</code> of
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+     * depending on distortion correction capability and mode</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#CONTROL_MAX_REGIONS_AWB
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
     @PublicKey
     public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS =
@@ -2398,6 +2464,7 @@
      * <p>This key is available on all devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<double[]> JPEG_GPS_COORDINATES =
             new Key<double[]>("android.jpeg.gpsCoordinates", double[].class);
 
@@ -2408,6 +2475,7 @@
      * <p>This key is available on all devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<String> JPEG_GPS_PROCESSING_METHOD =
             new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
 
@@ -2418,6 +2486,7 @@
      * <p>This key is available on all devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<Long> JPEG_GPS_TIMESTAMP =
             new Key<Long>("android.jpeg.gpsTimestamp", long.class);
 
@@ -2792,7 +2861,9 @@
      * <p>If this device is the largest or only camera device with a given facing, then this
      * position will be <code>(0, 0, 0)</code>; a camera device with a lens optical center located 3 cm
      * from the main sensor along the +X axis (to the right from the user's perspective) will
-     * report <code>(0.03, 0, 0)</code>.</p>
+     * report <code>(0.03, 0, 0)</code>.  Note that this means that, for many computer vision
+     * applications, the position needs to be negated to convert it to a translation from the
+     * camera to the origin.</p>
      * <p>To transform a pixel coordinates between two cameras facing the same direction, first
      * the source camera {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} must be corrected for.  Then the source
      * camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the
@@ -2804,7 +2875,8 @@
      * <p>To compare this against a real image from the destination camera, the destination camera
      * image then needs to be corrected for radial distortion before comparison or sampling.</p>
      * <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is GYROSCOPE, then this position is relative to
-     * the center of the primary gyroscope on the device.</p>
+     * the center of the primary gyroscope on the device. The axis definitions are the same as
+     * with PRIMARY_CAMERA.</p>
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
@@ -2836,13 +2908,15 @@
      * </code></pre>
      * <p>which can then be combined with the camera pose rotation
      * <code>R</code> and translation <code>t</code> ({@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} and
-     * {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}, respective) to calculate the
+     * {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}, respectively) to calculate the
      * complete transform from world coordinates to pixel
      * coordinates:</p>
-     * <pre><code>P = [ K 0   * [ R t
-     *      0 1 ]     0 1 ]
+     * <pre><code>P = [ K 0   * [ R -Rt
+     *      0 1 ]      0 1 ]
      * </code></pre>
-     * <p>and with <code>p_w</code> being a point in the world coordinate system
+     * <p>(Note the negation of poseTranslation when mapping from camera
+     * to world coordinates, and multiplication by the rotation).</p>
+     * <p>With <code>p_w</code> being a point in the world coordinate system
      * and <code>p_s</code> being a point in the camera active pixel array
      * coordinate system, and with the mapping including the
      * homogeneous division by z:</p>
@@ -2864,6 +2938,13 @@
      * activeArraySize rectangle), to determine the final pixel
      * coordinate of the world point for processed (non-RAW)
      * output buffers.</p>
+     * <p>For camera devices, the center of pixel <code>(x,y)</code> is located at
+     * coordinate <code>(x + 0.5, y + 0.5)</code>.  So on a device with a
+     * precorrection active array of size <code>(10,10)</code>, the valid pixel
+     * indices go from <code>(0,0)-(9,9)</code>, and an perfectly-built camera would
+     * have an optical center at the exact center of the pixel grid, at
+     * coordinates <code>(5.0, 5.0)</code>, which is the top-left corner of pixel
+     * <code>(5,5)</code>.</p>
      * <p><b>Units</b>:
      * Pixels in the
      * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
@@ -3044,6 +3125,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final Key<Boolean> QUIRKS_PARTIAL_RESULT =
             new Key<Boolean>("android.quirks.partialResult", boolean.class);
 
@@ -3062,6 +3144,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final Key<Integer> REQUEST_FRAME_COUNT =
             new Key<Integer>("android.request.frameCount", int.class);
 
@@ -3075,6 +3158,7 @@
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<Integer> REQUEST_ID =
             new Key<Integer>("android.request.id", int.class);
 
@@ -3099,9 +3183,17 @@
     /**
      * <p>The desired region of the sensor to read out for this capture.</p>
      * <p>This control can be used to implement digital zoom.</p>
-     * <p>The crop region coordinate system is based off
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the
-     * top-left corner of the sensor active array.</p>
+     * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
+     * the top-left pixel of the active array.</p>
+     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system depends on the mode being set.
+     * When the distortion correction mode is OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+     * When the distortion correction mode is not OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
      * <p>Output streams use this rectangle to produce their output,
      * cropping to a smaller region if necessary to maintain the
      * stream's aspect ratio, then scaling the sensor input to
@@ -3120,20 +3212,30 @@
      * outputs will crop horizontally (pillarbox), and 16:9
      * streams will match exactly. These additional crops will
      * be centered within the crop region.</p>
-     * <p>The width and height of the crop region cannot
-     * be set to be smaller than
+     * <p>If the coordinate system is {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, the width and height
+     * of the crop region cannot be set to be smaller than
      * <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and
      * <code>floor( activeArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>, respectively.</p>
+     * <p>If the coordinate system is {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, the width
+     * and height of the crop region cannot be set to be smaller than
+     * <code>floor( preCorrectionActiveArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>
+     * and
+     * <code>floor( preCorrectionActiveArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>,
+     * respectively.</p>
      * <p>The camera device may adjust the crop region to account
      * for rounding and other hardware requirements; the final
      * crop region used will be included in the output capture
      * result.</p>
      * <p><b>Units</b>: Pixel coordinates relative to
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
+     * capability and mode</p>
      * <p>This key is available on all devices.</p>
      *
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
     @PublicKey
     public static final Key<android.graphics.Rect> SCALER_CROP_REGION =
@@ -3618,36 +3720,61 @@
      * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<int[]> STATISTICS_FACE_IDS =
             new Key<int[]>("android.statistics.faceIds", int[].class);
 
     /**
      * <p>List of landmarks for detected
      * faces.</p>
-     * <p>The coordinate system is that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+     * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
+     * the top-left pixel of the active array.</p>
+     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system depends on the mode being set.
+     * When the distortion correction mode is OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+     * When the distortion correction mode is not OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
      * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
      * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} == FULL
      * This key is available on all devices.</p>
      *
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<int[]> STATISTICS_FACE_LANDMARKS =
             new Key<int[]>("android.statistics.faceLandmarks", int[].class);
 
     /**
      * <p>List of the bounding rectangles for detected
      * faces.</p>
-     * <p>The coordinate system is that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+     * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
+     * the top-left pixel of the active array.</p>
+     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+     * system depends on the mode being set.
+     * When the distortion correction mode is OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+     * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+     * When the distortion correction mode is not OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
      * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
      * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} != OFF
      * This key is available on all devices.</p>
      *
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<android.graphics.Rect[]> STATISTICS_FACE_RECTANGLES =
             new Key<android.graphics.Rect[]>("android.statistics.faceRectangles", android.graphics.Rect[].class);
 
@@ -3662,6 +3789,7 @@
      * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<byte[]> STATISTICS_FACE_SCORES =
             new Key<byte[]>("android.statistics.faceScores", byte[].class);
 
@@ -3821,6 +3949,7 @@
      * @see CameraCharacteristics#SENSOR_INFO_LENS_SHADING_APPLIED
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<float[]> STATISTICS_LENS_SHADING_MAP =
             new Key<float[]>("android.statistics.lensShadingMap", float[].class);
 
@@ -3844,6 +3973,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final Key<float[]> STATISTICS_PREDICTED_COLOR_GAINS =
             new Key<float[]>("android.statistics.predictedColorGains", float[].class);
 
@@ -3870,6 +4000,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final Key<Rational[]> STATISTICS_PREDICTED_COLOR_TRANSFORM =
             new Key<Rational[]>("android.statistics.predictedColorTransform", Rational[].class);
 
@@ -3977,8 +4108,14 @@
             new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
 
     /**
-     * <p>A control for selecting whether OIS position information is included in output
-     * result metadata.</p>
+     * <p>A control for selecting whether optical stabilization (OIS) position
+     * information is included in output result metadata.</p>
+     * <p>Since optical image stabilization generally involves motion much faster than the duration
+     * of individualq image exposure, multiple OIS samples can be included for a single capture
+     * result. For example, if the OIS reporting operates at 200 Hz, a typical camera operating
+     * at 30fps may have 6-7 OIS samples per capture result. This information can be combined
+     * with the rolling shutter skew to account for lens motion during image exposure in
+     * post-processing algorithms.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
@@ -4006,49 +4143,66 @@
      * @see CaptureResult#SENSOR_TIMESTAMP
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<long[]> STATISTICS_OIS_TIMESTAMPS =
             new Key<long[]>("android.statistics.oisTimestamps", long[].class);
 
     /**
      * <p>An array of shifts of OIS samples, in x direction.</p>
      * <p>The array contains the amount of shifts in x direction, in pixels, based on OIS samples.
-     * A positive value is a shift from left to right in active array coordinate system. For
-     * example, if the optical center is (1000, 500) in active array coordinates, a shift of
-     * (3, 0) puts the new optical center at (1003, 500).</p>
+     * A positive value is a shift from left to right in the pre-correction active array
+     * coordinate system. For example, if the optical center is (1000, 500) in pre-correction
+     * active array coordinates, a shift of (3, 0) puts the new optical center at (1003, 500).</p>
      * <p>The number of shifts must match the number of timestamps in
      * android.statistics.oisTimestamps.</p>
+     * <p>The OIS samples are not affected by whether lens distortion correction is enabled (on
+     * supporting devices). They are always reported in pre-correction active array coordinates,
+     * since the scaling of OIS shifts would depend on the specific spot on the sensor the shift
+     * is needed.</p>
      * <p><b>Units</b>: Pixels in active array.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<float[]> STATISTICS_OIS_X_SHIFTS =
             new Key<float[]>("android.statistics.oisXShifts", float[].class);
 
     /**
      * <p>An array of shifts of OIS samples, in y direction.</p>
      * <p>The array contains the amount of shifts in y direction, in pixels, based on OIS samples.
-     * A positive value is a shift from top to bottom in active array coordinate system. For
-     * example, if the optical center is (1000, 500) in active array coordinates, a shift of
-     * (0, 5) puts the new optical center at (1000, 505).</p>
+     * A positive value is a shift from top to bottom in pre-correction active array coordinate
+     * system. For example, if the optical center is (1000, 500) in active array coordinates, a
+     * shift of (0, 5) puts the new optical center at (1000, 505).</p>
      * <p>The number of shifts must match the number of timestamps in
      * android.statistics.oisTimestamps.</p>
+     * <p>The OIS samples are not affected by whether lens distortion correction is enabled (on
+     * supporting devices). They are always reported in pre-correction active array coordinates,
+     * since the scaling of OIS shifts would depend on the specific spot on the sensor the shift
+     * is needed.</p>
      * <p><b>Units</b>: Pixels in active array.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<float[]> STATISTICS_OIS_Y_SHIFTS =
             new Key<float[]>("android.statistics.oisYShifts", float[].class);
 
     /**
-     * <p>An array of OIS samples.</p>
+     * <p>An array of optical stabilization (OIS) position samples.</p>
      * <p>Each OIS sample contains the timestamp and the amount of shifts in x and y direction,
      * in pixels, of the OIS sample.</p>
-     * <p>A positive value for a shift in x direction is a shift from left to right in active array
-     * coordinate system. For example, if the optical center is (1000, 500) in active array
-     * coordinates, a shift of (3, 0) puts the new optical center at (1003, 500).</p>
-     * <p>A positive value for a shift in y direction is a shift from top to bottom in active array
-     * coordinate system. For example, if the optical center is (1000, 500) in active array
-     * coordinates, a shift of (0, 5) puts the new optical center at (1000, 505).</p>
+     * <p>A positive value for a shift in x direction is a shift from left to right in the
+     * pre-correction active array coordinate system. For example, if the optical center is
+     * (1000, 500) in pre-correction active array coordinates, a shift of (3, 0) puts the new
+     * optical center at (1003, 500).</p>
+     * <p>A positive value for a shift in y direction is a shift from top to bottom in
+     * pre-correction active array coordinate system. For example, if the optical center is
+     * (1000, 500) in active array coordinates, a shift of (0, 5) puts the new optical center at
+     * (1000, 505).</p>
+     * <p>The OIS samples are not affected by whether lens distortion correction is enabled (on
+     * supporting devices). They are always reported in pre-correction active array coordinates,
+     * since the scaling of OIS shifts would depend on the specific spot on the sensor the shift
+     * is needed.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      */
     @PublicKey
@@ -4070,6 +4224,7 @@
      * @see CaptureRequest#TONEMAP_MODE
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<float[]> TONEMAP_CURVE_BLUE =
             new Key<float[]>("android.tonemap.curveBlue", float[].class);
 
@@ -4087,6 +4242,7 @@
      * @see CaptureRequest#TONEMAP_MODE
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<float[]> TONEMAP_CURVE_GREEN =
             new Key<float[]>("android.tonemap.curveGreen", float[].class);
 
@@ -4149,6 +4305,7 @@
      * @see CaptureRequest#TONEMAP_MODE
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<float[]> TONEMAP_CURVE_RED =
             new Key<float[]>("android.tonemap.curveRed", float[].class);
 
@@ -4323,6 +4480,7 @@
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<Boolean> LED_TRANSMIT =
             new Key<Boolean>("android.led.transmit", boolean.class);
 
@@ -4412,6 +4570,7 @@
      * @see #SYNC_FRAME_NUMBER_UNKNOWN
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<Long> SYNC_FRAME_NUMBER =
             new Key<Long>("android.sync.frameNumber", long.class);
 
@@ -4478,15 +4637,28 @@
      * any correction at all would slow down capture rate.  Every output stream will have a
      * similar amount of enhancement applied.</p>
      * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
-     * applied to any RAW output.  Metadata coordinates such as face rectangles or metering
-     * regions are also not affected by correction.</p>
-     * <p>Applications enabling distortion correction need to pay extra attention when converting
-     * image coordinates between corrected output buffers and the sensor array. For example, if
-     * the app supports tap-to-focus and enables correction, it then has to apply the distortion
-     * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
-     * calculate the tap position on the sensor active array to be used with
-     * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
-     * they need to be drawn on top of the corrected output buffers.</p>
+     * applied to any RAW output.</p>
+     * <p>This control will be on by default on devices that support this control. Applications
+     * disabling distortion correction need to pay extra attention with the coordinate system of
+     * metering regions, crop region, and face rectangles. When distortion correction is OFF,
+     * metadata coordinates follow the coordinate system of
+     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}. When distortion is not OFF, metadata
+     * coordinates follow the coordinate system of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.  The
+     * camera device will map these metadata fields to match the corrected image produced by the
+     * camera device, for both capture requests and results.  However, this mapping is not very
+     * precise, since rectangles do not generally map to rectangles when corrected.  Only linear
+     * scaling between the active array and precorrection active array coordinates is
+     * performed. Applications that require precise correction of metadata need to undo that
+     * linear scaling, and apply a more complete correction that takes into the account the app's
+     * own requirements.</p>
+     * <p>The full list of metadata that is affected in this way by distortion correction is:</p>
+     * <ul>
+     * <li>{@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}</li>
+     * <li>{@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}</li>
+     * <li>{@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions}</li>
+     * <li>{@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}</li>
+     * <li>{@link CaptureResult#STATISTICS_FACES android.statistics.faces}</li>
+     * </ul>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
@@ -4497,9 +4669,15 @@
      * {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CaptureRequest#CONTROL_AE_REGIONS
      * @see CaptureRequest#CONTROL_AF_REGIONS
+     * @see CaptureRequest#CONTROL_AWB_REGIONS
      * @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
      * @see CameraCharacteristics#LENS_DISTORTION
+     * @see CaptureRequest#SCALER_CROP_REGION
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+     * @see CaptureResult#STATISTICS_FACES
      * @see #DISTORTION_CORRECTION_MODE_OFF
      * @see #DISTORTION_CORRECTION_MODE_FAST
      * @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 4baf263..0610d7a 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -16,6 +16,7 @@
 
 package android.hardware.camera2.impl;
 
+import android.annotation.UnsupportedAppUsage;
 import android.graphics.ImageFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -231,6 +232,7 @@
          *
          * @return The tag numeric value corresponding to the string
          */
+        @UnsupportedAppUsage
         public final int getTag() {
             if (!mHasTag) {
                 mTag = CameraMetadataNative.getTag(mName, mVendorId);
@@ -747,6 +749,9 @@
         if (faceDetectMode == null) {
             Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE");
             faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE;
+        } else if (faceDetectMode > CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
+            // Face detect mode is larger than FULL, assuming the mode is FULL
+            faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL;
         } else {
             if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) {
                 return new Face[0];
@@ -1185,6 +1190,7 @@
         return true;
     }
 
+    @UnsupportedAppUsage
     private long mMetadataPtr; // native CameraMetadata*
 
     private native long nativeAllocate();
@@ -1199,13 +1205,16 @@
     private native synchronized boolean nativeIsEmpty();
     private native synchronized int nativeGetEntryCount();
 
+    @UnsupportedAppUsage
     private native synchronized byte[] nativeReadValues(int tag);
     private native synchronized void nativeWriteValues(int tag, byte[] src);
     private native synchronized void nativeDump() throws IOException; // dump to ALOGD
 
     private native synchronized ArrayList nativeGetAllVendorKeys(Class keyClass);
+    @UnsupportedAppUsage
     private native synchronized int nativeGetTagFromKeyLocal(String keyName)
             throws IllegalArgumentException;
+    @UnsupportedAppUsage
     private native synchronized int nativeGetTypeFromTagLocal(int tag)
             throws IllegalArgumentException;
     private static native int nativeGetTagFromKey(String keyName, long vendorId)
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 5423ca9..8822f71 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -781,6 +781,7 @@
                     CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE                   ,
                     CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE                       ,
                     CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE                    ,
+                    CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE    ,
                     CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE                    ,
                     CameraCharacteristics.SENSOR_ORIENTATION                              ,
                     CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES     ,
@@ -941,11 +942,12 @@
         // Use the largest jpeg size (by area) for both active array and pixel array
         Size largestJpegSize = getLargestSupportedJpegSizeByArea(p);
         /*
-         * sensor.info.activeArraySize
+         * sensor.info.activeArraySize, and preCorrectionActiveArraySize
          */
         {
             Rect activeArrayRect = ParamsUtils.createRect(largestJpegSize);
             m.set(SENSOR_INFO_ACTIVE_ARRAY_SIZE, activeArrayRect);
+            m.set(SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, activeArrayRect);
         }
 
         /*
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index a040a09..1ee3c93 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -82,9 +82,9 @@
  *
  * </ul>
  *
- * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats can be used for
- * sharing, subject to device support. On prior API levels, only {@link ImageFormat#PRIVATE}
- * format may be used.</p>
+ * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats except
+ * {@link ImageFormat#JPEG} and {@link ImageFormat#RAW_PRIVATE} can be used for sharing, subject to
+ * device support. On prior API levels, only {@link ImageFormat#PRIVATE} format may be used.</p>
  *
  * @see CameraDevice#createCaptureSessionByOutputConfigurations
  *
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index c56b685..414c463 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -725,7 +725,7 @@
      * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration}
      * for the class/size combination (in nanoseconds).
      *
-     * <p>This assumes a the {@code klass} is set up to use {@link ImageFormat#PRIVATE}.
+     * <p>This assumes that the {@code klass} is set up to use {@link ImageFormat#PRIVATE}.
      * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
      *
      * <p>{@code klass} should be one of the ones which is supported by
@@ -870,7 +870,7 @@
     /**
      * Get the stall duration for the class/size combination (in nanoseconds).
      *
-     * <p>This assumes a the {@code klass} is set up to use {@link ImageFormat#PRIVATE}.
+     * <p>This assumes that the {@code klass} is set up to use {@link ImageFormat#PRIVATE}.
      * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
      *
      * <p>{@code klass} should be one of the ones with a non-empty array returned by
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
index 9247844..d3c4505 100644
--- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
@@ -16,6 +16,7 @@
 
 package android.hardware.camera2.utils;
 
+import android.annotation.UnsupportedAppUsage;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.legacy.LegacyCameraDevice;
 import android.hardware.camera2.legacy.LegacyExceptionUtils.BufferQueueAbandonedException;
@@ -77,6 +78,7 @@
      *
      * @throws IllegalArgumentException if the surface is already abandoned.
      */
+    @UnsupportedAppUsage
     public static Size getSurfaceSize(Surface surface) {
         try {
             return LegacyCameraDevice.getSurfaceSize(surface);
diff --git a/core/java/android/hardware/camera2/utils/TypeReference.java b/core/java/android/hardware/camera2/utils/TypeReference.java
index 24ce124..d9ba31b 100644
--- a/core/java/android/hardware/camera2/utils/TypeReference.java
+++ b/core/java/android/hardware/camera2/utils/TypeReference.java
@@ -16,6 +16,7 @@
 
 package android.hardware.camera2.utils;
 
+import android.annotation.UnsupportedAppUsage;
 import java.lang.reflect.Array;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.ParameterizedType;
@@ -55,6 +56,7 @@
      *
      * @see TypeReference
      */
+    @UnsupportedAppUsage
     protected TypeReference() {
         ParameterizedType thisType = (ParameterizedType)getClass().getGenericSuperclass();
 
@@ -136,6 +138,7 @@
      *
      * @throws IllegalArgumentException if {@code type} had any type variables
      */
+    @UnsupportedAppUsage
     public static TypeReference<?> createSpecializedTypeReference(Type type) {
         return new SpecializedBaseTypeReference(type);
     }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index b182fa2..e700cac 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -23,6 +23,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
 import android.app.KeyguardManager;
 import android.content.Context;
 import android.graphics.Point;
@@ -62,6 +63,7 @@
      * </p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final String ACTION_WIFI_DISPLAY_STATUS_CHANGED =
             "android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED";
 
@@ -69,6 +71,7 @@
      * Contains a {@link WifiDisplayStatus} object.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final String EXTRA_WIFI_DISPLAY_STATUS =
             "android.hardware.display.extra.WIFI_DISPLAY_STATUS";
 
@@ -437,6 +440,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void startWifiDisplayScan() {
         mGlobal.startWifiDisplayScan();
     }
@@ -449,6 +453,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void stopWifiDisplayScan() {
         mGlobal.stopWifiDisplayScan();
     }
@@ -466,16 +471,19 @@
      * @param deviceAddress The MAC address of the device to which we should connect.
      * @hide
      */
+    @UnsupportedAppUsage
     public void connectWifiDisplay(String deviceAddress) {
         mGlobal.connectWifiDisplay(deviceAddress);
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public void pauseWifiDisplay() {
         mGlobal.pauseWifiDisplay();
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public void resumeWifiDisplay() {
         mGlobal.resumeWifiDisplay();
     }
@@ -485,6 +493,7 @@
      * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast.
      * @hide
      */
+    @UnsupportedAppUsage
     public void disconnectWifiDisplay() {
         mGlobal.disconnectWifiDisplay();
     }
@@ -504,6 +513,7 @@
      * or empty if no alias should be used.
      * @hide
      */
+    @UnsupportedAppUsage
     public void renameWifiDisplay(String deviceAddress, String alias) {
         mGlobal.renameWifiDisplay(deviceAddress, alias);
     }
@@ -519,6 +529,7 @@
      * @param deviceAddress The MAC address of the device to forget.
      * @hide
      */
+    @UnsupportedAppUsage
     public void forgetWifiDisplay(String deviceAddress) {
         mGlobal.forgetWifiDisplay(deviceAddress);
     }
@@ -531,6 +542,7 @@
      * @return The current Wifi display status.
      * @hide
      */
+    @UnsupportedAppUsage
     public WifiDisplayStatus getWifiDisplayStatus() {
         return mGlobal.getWifiDisplayStatus();
     }
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index d968a3e..7304ab4 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -16,6 +16,7 @@
 
 package android.hardware.display;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
@@ -66,10 +67,12 @@
     public static final int EVENT_DISPLAY_CHANGED = 2;
     public static final int EVENT_DISPLAY_REMOVED = 3;
 
+    @UnsupportedAppUsage
     private static DisplayManagerGlobal sInstance;
 
     private final Object mLock = new Object();
 
+    @UnsupportedAppUsage
     private final IDisplayManager mDm;
 
     private DisplayManagerCallback mCallback;
@@ -91,6 +94,7 @@
      * @return The display manager instance, may be null early in system startup
      * before the display manager has been fully initialized.
      */
+    @UnsupportedAppUsage
     public static DisplayManagerGlobal getInstance() {
         synchronized (DisplayManagerGlobal.class) {
             if (sInstance == null) {
@@ -110,6 +114,7 @@
      * @return Information about the specified display, or null if it does not exist.
      * This object belongs to an internal cache and should be treated as if it were immutable.
      */
+    @UnsupportedAppUsage
     public DisplayInfo getDisplayInfo(int displayId) {
         try {
             synchronized (mLock) {
@@ -146,6 +151,7 @@
      *
      * @return An array containing all display ids.
      */
+    @UnsupportedAppUsage
     public int[] getDisplayIds() {
         try {
             synchronized (mLock) {
@@ -209,6 +215,7 @@
      * @param displayId The logical display id.
      * @return The display object, or null if there is no display with the given id.
      */
+    @UnsupportedAppUsage
     public Display getRealDisplay(int displayId) {
         return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
     }
@@ -337,6 +344,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public void disconnectWifiDisplay() {
         try {
             mDm.disconnectWifiDisplay();
@@ -369,6 +377,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public WifiDisplayStatus getWifiDisplayStatus() {
         try {
             return mDm.getWifiDisplayStatus();
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 504f840..7840fd0 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -23,6 +23,7 @@
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.Surface;
 import android.view.SurfaceControl;
 
 /**
@@ -62,6 +63,15 @@
     public abstract boolean isProximitySensorAvailable();
 
     /**
+     * Take a screenshot of the specified display into the provided {@link Surface}.
+     *
+     * @param displayId The display id to take the screenshot of.
+     * @param outSurface The {@link Surface} to take the screenshot into.
+     * @return True if the screenshot is taken.
+     */
+    public abstract boolean screenshot(int displayId, Surface outSurface);
+
+    /**
      * Returns information about the specified logical display.
      *
      * @param displayId The logical display id.
diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java
index bb32c19..12486e8 100644
--- a/core/java/android/hardware/display/WifiDisplay.java
+++ b/core/java/android/hardware/display/WifiDisplay.java
@@ -16,6 +16,7 @@
 
 package android.hardware.display;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -76,6 +77,7 @@
     /**
      * Gets the MAC address of the Wifi display device.
      */
+    @UnsupportedAppUsage
     public String getDeviceAddress() {
         return mDeviceAddress;
     }
@@ -83,6 +85,7 @@
     /**
      * Gets the name of the Wifi display device.
      */
+    @UnsupportedAppUsage
     public String getDeviceName() {
         return mDeviceName;
     }
@@ -94,6 +97,7 @@
      * provided by the user when renaming the device.
      * </p>
      */
+    @UnsupportedAppUsage
     public String getDeviceAlias() {
         return mDeviceAlias;
     }
@@ -101,6 +105,7 @@
     /**
      * Returns true if device is available, false otherwise.
      */
+    @UnsupportedAppUsage
     public boolean isAvailable() {
         return mIsAvailable;
     }
@@ -108,6 +113,7 @@
     /**
      * Returns true if device can be connected to (not in use), false otherwise.
      */
+    @UnsupportedAppUsage
     public boolean canConnect() {
         return mCanConnect;
     }
@@ -115,6 +121,7 @@
     /**
      * Returns true if device has been remembered, false otherwise.
      */
+    @UnsupportedAppUsage
     public boolean isRemembered() {
         return mIsRemembered;
     }
@@ -136,6 +143,7 @@
      * Returns true if the two displays have the same identity (address, name and alias).
      * This method does not compare the current status of the displays.
      */
+    @UnsupportedAppUsage
     public boolean equals(WifiDisplay other) {
         return other != null
                 && mDeviceAddress.equals(other.mDeviceAddress)
diff --git a/core/java/android/hardware/display/WifiDisplayStatus.java b/core/java/android/hardware/display/WifiDisplayStatus.java
index b645662..c267834 100644
--- a/core/java/android/hardware/display/WifiDisplayStatus.java
+++ b/core/java/android/hardware/display/WifiDisplayStatus.java
@@ -16,6 +16,7 @@
 
 package android.hardware.display;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -34,7 +35,9 @@
     private final int mFeatureState;
     private final int mScanState;
     private final int mActiveDisplayState;
+    @UnsupportedAppUsage
     private final WifiDisplay mActiveDisplay;
+    @UnsupportedAppUsage
     private final WifiDisplay[] mDisplays;
 
     /** Session info needed for Miracast Certification */
@@ -47,18 +50,23 @@
     /** Feature state: Wifi display is turned off in settings. */
     public static final int FEATURE_STATE_OFF = 2;
     /** Feature state: Wifi display is turned on in settings. */
+    @UnsupportedAppUsage
     public static final int FEATURE_STATE_ON = 3;
 
     /** Scan state: Not currently scanning. */
+    @UnsupportedAppUsage
     public static final int SCAN_STATE_NOT_SCANNING = 0;
     /** Scan state: Currently scanning. */
     public static final int SCAN_STATE_SCANNING = 1;
 
     /** Display state: Not connected. */
+    @UnsupportedAppUsage
     public static final int DISPLAY_STATE_NOT_CONNECTED = 0;
     /** Display state: Connecting to active display. */
+    @UnsupportedAppUsage
     public static final int DISPLAY_STATE_CONNECTING = 1;
     /** Display state: Connected to active display. */
+    @UnsupportedAppUsage
     public static final int DISPLAY_STATE_CONNECTED = 2;
 
     public static final Creator<WifiDisplayStatus> CREATOR = new Creator<WifiDisplayStatus>() {
@@ -117,6 +125,7 @@
      * connecting to displays have been met.
      * </p>
      */
+    @UnsupportedAppUsage
     public int getFeatureState() {
         return mFeatureState;
     }
@@ -126,6 +135,7 @@
      *
      * @return One of: {@link #SCAN_STATE_NOT_SCANNING} or {@link #SCAN_STATE_SCANNING}.
      */
+    @UnsupportedAppUsage
     public int getScanState() {
         return mScanState;
     }
@@ -136,6 +146,7 @@
      * @return One of: {@link #DISPLAY_STATE_NOT_CONNECTED}, {@link #DISPLAY_STATE_CONNECTING},
      * or {@link #DISPLAY_STATE_CONNECTED}.
      */
+    @UnsupportedAppUsage
     public int getActiveDisplayState() {
         return mActiveDisplayState;
     }
@@ -144,6 +155,7 @@
      * Gets the Wifi display that is currently active.  It may be connecting or
      * connected.
      */
+    @UnsupportedAppUsage
     public WifiDisplay getActiveDisplay() {
         return mActiveDisplay;
     }
@@ -153,6 +165,7 @@
      * Wifi displays as reported by the most recent scan, and all remembered
      * Wifi displays (not necessarily available at the time).
      */
+    @UnsupportedAppUsage
     public WifiDisplay[] getDisplays() {
         return mDisplays;
     }
diff --git a/core/java/android/hardware/face/Face.aidl b/core/java/android/hardware/face/Face.aidl
new file mode 100644
index 0000000..a7c9141
--- /dev/null
+++ b/core/java/android/hardware/face/Face.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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.face;
+
+/**
+ * @hide
+ */
+parcelable Face;
diff --git a/core/java/android/hardware/face/Face.java b/core/java/android/hardware/face/Face.java
new file mode 100644
index 0000000..d6724d7
--- /dev/null
+++ b/core/java/android/hardware/face/Face.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 android.hardware.face;
+
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container for face metadata.
+ *
+ * @hide
+ */
+public final class Face extends BiometricAuthenticator.Identifier {
+
+    public Face(CharSequence name, int faceId, long deviceId) {
+        super(name, faceId, deviceId);
+    }
+
+    private Face(Parcel in) {
+        super(in.readString(), in.readInt(), in.readLong());
+    }
+
+    /**
+     * Describes the contents.
+     * @return
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Writes to a parcel.
+     * @param out
+     * @param flags Additional flags about how the object should be written.
+     */
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(getName().toString());
+        out.writeInt(getBiometricId());
+        out.writeLong(getDeviceId());
+    }
+
+    public static final Parcelable.Creator<Face> CREATOR = new Parcelable.Creator<Face>() {
+            public Face createFromParcel(Parcel in) {
+                return new Face(in);
+            }
+
+            public Face[] newArray(int size) {
+                return new Face[size];
+            }
+    };
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
new file mode 100644
index 0000000..6a3dd7d
--- /dev/null
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -0,0 +1,1019 @@
+/**
+ * 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.face;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_FACE;
+import static android.Manifest.permission.USE_BIOMETRIC;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.CryptoObject;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.CancellationSignal.OnCancelListener;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A class that coordinates access to the face authentication hardware.
+ * @hide
+ */
+@SystemService(Context.FACE_SERVICE)
+public class FaceManager implements BiometricFaceConstants {
+
+
+    private static final String TAG = "FaceManager";
+    private static final boolean DEBUG = true;
+    private static final int MSG_ENROLL_RESULT = 100;
+    private static final int MSG_ACQUIRED = 101;
+    private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
+    private static final int MSG_AUTHENTICATION_FAILED = 103;
+    private static final int MSG_ERROR = 104;
+    private static final int MSG_REMOVED = 105;
+
+    private IFaceService mService;
+    private final Context mContext;
+    private IBinder mToken = new Binder();
+    private BiometricAuthenticator.AuthenticationCallback mAuthenticationCallback;
+    private EnrollmentCallback mEnrollmentCallback;
+    private RemovalCallback mRemovalCallback;
+    private CryptoObject mCryptoObject;
+    private Face mRemovalFace;
+    private Handler mHandler;
+    private Executor mExecutor;
+
+    private IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
+
+        @Override // binder call
+        public void onEnrollResult(long deviceId, int faceId, int remaining) {
+            mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
+                    new Face(null, faceId, deviceId)).sendToTarget();
+        }
+
+        @Override // binder call
+        public void onAcquired(long deviceId, int acquireInfo, int vendorCode) {
+            mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode, deviceId).sendToTarget();
+        }
+
+        @Override // binder call
+        public void onAuthenticationSucceeded(long deviceId, Face face) {
+            mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, face).sendToTarget();
+        }
+
+        @Override // binder call
+        public void onAuthenticationFailed(long deviceId) {
+            mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
+        }
+
+        @Override // binder call
+        public void onError(long deviceId, int error, int vendorCode) {
+            mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
+        }
+
+        @Override // binder call
+        public void onRemoved(long deviceId, int faceId, int remaining) {
+            mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
+                    new Face(null, faceId, deviceId)).sendToTarget();
+        }
+    };
+
+    /**
+     * @hide
+     */
+    public FaceManager(Context context, IFaceService service) {
+        mContext = context;
+        mService = service;
+        if (mService == null) {
+            Slog.v(TAG, "FaceAuthenticationManagerService was null");
+        }
+        mHandler = new MyHandler(context);
+    }
+
+    /**
+     * Request authentication of a crypto object. This call operates the face recognition hardware
+     * and starts capturing images. It terminates when
+     * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
+     * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
+     * which point the object is no longer valid. The operation can be canceled by using the
+     * provided cancel object.
+     *
+     * @param crypto   object associated with the call or null if none required.
+     * @param cancel   an object that can be used to cancel authentication
+     * @param flags    optional flags; should be 0
+     * @param callback an object to receive authentication events
+     * @param handler  an optional handler to handle callback events
+     * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
+     *                                  by
+     *                                  <a href="{@docRoot}training/articles/keystore.html">Android
+     *                                  Keystore facility</a>.
+     * @throws IllegalStateException    if the crypto primitive is not initialized.
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
+            int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
+        if (callback == null) {
+            throw new IllegalArgumentException("Must supply an authentication callback");
+        }
+
+        if (cancel != null) {
+            if (cancel.isCanceled()) {
+                Log.w(TAG, "authentication already canceled");
+                return;
+            } else {
+                cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
+            }
+        }
+
+        if (mService != null) {
+            try {
+                useHandler(handler);
+                mAuthenticationCallback = callback;
+                mCryptoObject = crypto;
+                long sessionId = crypto != null ? crypto.getOpId() : 0;
+                mService.authenticate(mToken, sessionId, mServiceReceiver, flags,
+                        mContext.getOpPackageName(), null /* bundle */, null /* receiver */);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Remote exception while authenticating: ", e);
+                if (callback != null) {
+                    // Though this may not be a hardware issue, it will cause apps to give up or try
+                    // again later.
+                    callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
+                            getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                }
+            }
+        }
+    }
+
+    /**
+     * Use the provided handler thread for events.
+     */
+    private void useHandler(Handler handler) {
+        if (handler != null) {
+            mHandler = new MyHandler(handler.getLooper());
+        } else if (mHandler.getLooper() != mContext.getMainLooper()) {
+            mHandler = new MyHandler(mContext.getMainLooper());
+        }
+    }
+
+    /**
+     * This method invokes the BiometricPrompt.
+     */
+    private void authenticateWithPrompt(@Nullable android.hardware.biometrics.CryptoObject crypto,
+            @NonNull CancellationSignal cancel,
+            @NonNull Bundle bundle,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull IBiometricPromptReceiver receiver,
+            @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+        mCryptoObject = crypto;
+        if (cancel.isCanceled()) {
+            Slog.w(TAG, "authentication already canceled");
+            return;
+        } else {
+            cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
+        }
+
+        if (mService != null) {
+            try {
+                mExecutor = executor;
+                mAuthenticationCallback = callback;
+                final long sessionId = crypto != null ? crypto.getOpId() : 0;
+                mService.authenticate(mToken, sessionId, mServiceReceiver,
+                        0 /* flags */, mContext.getOpPackageName(), bundle, receiver);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Remote exception while authenticating", e);
+                mExecutor.execute(() -> {
+                    callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
+                            getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                });
+            }
+        }
+    }
+
+    /**
+     * Private method, see {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
+     * BiometricPrompt.AuthenticationCallback)}
+     * @param cancel
+     * @param executor
+     * @param callback
+     * @hide
+     */
+    public void authenticate(
+            @NonNull CancellationSignal cancel,
+            @NonNull Bundle bundle,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull IBiometricPromptReceiver receiver,
+            @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+        if (cancel == null) {
+            throw new IllegalArgumentException("Must supply a cancellation signal");
+        }
+        if (bundle == null) {
+            throw new IllegalArgumentException("Must supply a bundle");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must supply an executor");
+        }
+        if (receiver == null) {
+            throw new IllegalArgumentException("Must supply a receiver");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("Must supply a calback");
+        }
+        authenticateWithPrompt(null, cancel, bundle, executor, receiver, callback);
+    }
+
+    /**
+     * Private method, see {@link BiometricPrompt#authenticate(BiometricPrompt.CryptoObject,
+     * CancellationSignal, Executor, BiometricPrompt.AuthenticationCallback)}
+     * @param crypto
+     * @param cancel
+     * @param executor
+     * @param callback
+     * @hide
+     */
+    public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
+            @NonNull CancellationSignal cancel,
+            @NonNull Bundle bundle,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull IBiometricPromptReceiver receiver,
+            @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+        if (crypto == null) {
+            throw new IllegalArgumentException("Must supply a crypto object");
+        }
+        if (cancel == null) {
+            throw new IllegalArgumentException("Must supply a cancellation signal");
+        }
+        if (bundle == null) {
+            throw new IllegalArgumentException("Must supply a bundle");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must supply an executor");
+        }
+        if (receiver == null) {
+            throw new IllegalArgumentException("Must supply a receiver");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("Must supply a callback");
+        }
+        authenticateWithPrompt(crypto, cancel, bundle, executor, receiver, callback);
+    }
+
+    /**
+     * Request face authentication enrollment. This call operates the face authentication hardware
+     * and starts capturing images. Progress will be indicated by callbacks to the
+     * {@link EnrollmentCallback} object. It terminates when
+     * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
+     * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
+     * which point the object is no longer valid. The operation can be canceled by using the
+     * provided cancel object.
+     *
+     * @param token    a unique token provided by a recent creation or verification of device
+     *                 credentials (e.g. pin, pattern or password).
+     * @param cancel   an object that can be used to cancel enrollment
+     * @param flags    optional flags
+     * @param userId   the user to whom this face will belong to
+     * @param callback an object to receive enrollment events
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FACE)
+    public void enroll(byte[] token, CancellationSignal cancel, int flags,
+            int userId, EnrollmentCallback callback) {
+        if (userId == UserHandle.USER_CURRENT) {
+            userId = getCurrentUserId();
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("Must supply an enrollment callback");
+        }
+
+        if (cancel != null) {
+            if (cancel.isCanceled()) {
+                Log.w(TAG, "enrollment already canceled");
+                return;
+            } else {
+                cancel.setOnCancelListener(new OnEnrollCancelListener());
+            }
+        }
+
+        if (mService != null) {
+            try {
+                mEnrollmentCallback = callback;
+                mService.enroll(mToken, token, userId, mServiceReceiver, flags,
+                        mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                Log.w(TAG, "Remote exception in enroll: ", e);
+                if (callback != null) {
+                    // Though this may not be a hardware issue, it will cause apps to give up or try
+                    // again later.
+                    callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
+                            getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                }
+            }
+        }
+    }
+
+    /**
+     * Requests a pre-enrollment auth token to tie enrollment to the confirmation of
+     * existing device credentials (e.g. pin/pattern/password).
+     *
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FACE)
+    public long preEnroll() {
+        long result = 0;
+        if (mService != null) {
+            try {
+                result = mService.preEnroll(mToken);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Finishes enrollment and cancels the current auth token.
+     *
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FACE)
+    public int postEnroll() {
+        int result = 0;
+        if (mService != null) {
+            try {
+                result = mService.postEnroll(mToken);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Sets the active user. This is meant to be used to select the current profile for enrollment
+     * to allow separate enrolled faces for a work profile
+     *
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FACE)
+    public void setActiveUser(int userId) {
+        if (mService != null) {
+            try {
+                mService.setActiveUser(userId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Remove given face template from face hardware and/or protected storage.
+     *
+     * @param face     the face item to remove
+     * @param userId   the user who this face belongs to
+     * @param callback an optional callback to verify that face templates have been
+     *                 successfully removed. May be null if no callback is required.
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FACE)
+    public void remove(Face face, int userId, RemovalCallback callback) {
+        if (mService != null) {
+            try {
+                mRemovalCallback = callback;
+                mRemovalFace = face;
+                mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Remote exception in remove: ", e);
+                if (callback != null) {
+                    callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE,
+                            getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                }
+            }
+        }
+    }
+
+    /**
+     * Obtain the enrolled face template.
+     *
+     * @return the current face item
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    public List<Face> getEnrolledFaces(int userId) {
+        if (mService != null) {
+            try {
+                return mService.getEnrolledFaces(userId, mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Obtain the enrolled face template.
+     *
+     * @return the current face item
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    public List<Face> getEnrolledFaces() {
+        return getEnrolledFaces(UserHandle.myUserId());
+    }
+
+    /**
+     * Determine if there is a face enrolled.
+     *
+     * @return true if a face is enrolled, false otherwise
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    public boolean hasEnrolledFaces() {
+        if (mService != null) {
+            try {
+                return mService.hasEnrolledFaces(
+                        UserHandle.myUserId(), mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    @RequiresPermission(allOf = {
+            USE_BIOMETRIC,
+            INTERACT_ACROSS_USERS})
+    public boolean hasEnrolledFaces(int userId) {
+        if (mService != null) {
+            try {
+                return mService.hasEnrolledFaces(userId, mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Determine if face authentication sensor hardware is present and functional.
+     *
+     * @return true if hardware is present and functional, false otherwise.
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    public boolean isHardwareDetected() {
+        if (mService != null) {
+            try {
+                long deviceId = 0; /* TODO: plumb hardware id to FPMS */
+                return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Log.w(TAG, "isFaceHardwareDetected(): Service not connected!");
+        }
+        return false;
+    }
+
+    /**
+     * Retrieves the authenticator token for binding keys to the lifecycle
+     * of the calling user's face. Used only by internal clients.
+     *
+     * @hide
+     */
+    public long getAuthenticatorId() {
+        if (mService != null) {
+            try {
+                return mService.getAuthenticatorId(mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Log.w(TAG, "getAuthenticatorId(): Service not connected!");
+        }
+        return 0;
+    }
+
+    /**
+     * Reset the lockout timer when asked to do so by keyguard.
+     *
+     * @param token an opaque token returned by password confirmation.
+     * @hide
+     */
+    public void resetTimeout(byte[] token) {
+        if (mService != null) {
+            try {
+                mService.resetTimeout(token);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Log.w(TAG, "resetTimeout(): Service not connected!");
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void addLockoutResetCallback(final LockoutResetCallback callback) {
+        if (mService != null) {
+            try {
+                final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
+                mService.addLockoutResetCallback(
+                        new IBiometricServiceLockoutResetCallback.Stub() {
+
+                            @Override
+                            public void onLockoutReset(long deviceId,
+                                    IRemoteCallback serverCallback)
+                                    throws RemoteException {
+                                try {
+                                    final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
+                                            PowerManager.PARTIAL_WAKE_LOCK,
+                                            "faceLockoutResetCallback");
+                                    wakeLock.acquire();
+                                    mHandler.post(() -> {
+                                        try {
+                                            callback.onLockoutReset();
+                                        } finally {
+                                            wakeLock.release();
+                                        }
+                                    });
+                                } finally {
+                                    serverCallback.sendResult(null /* data */);
+                                }
+                            }
+                        });
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Log.w(TAG, "addLockoutResetCallback(): Service not connected!");
+        }
+    }
+
+    private int getCurrentUserId() {
+        try {
+            return ActivityManager.getService().getCurrentUser().id;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private void cancelEnrollment() {
+        if (mService != null) {
+            try {
+                mService.cancelEnrollment(mToken);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    private void cancelAuthentication(CryptoObject cryptoObject) {
+        if (mService != null) {
+            try {
+                mService.cancelAuthentication(mToken, mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    private String getErrorString(int errMsg, int vendorCode) {
+        switch (errMsg) {
+            case FACE_ERROR_HW_UNAVAILABLE:
+                return mContext.getString(
+                        com.android.internal.R.string.face_error_hw_not_available);
+            case FACE_ERROR_UNABLE_TO_PROCESS:
+                return mContext.getString(
+                        com.android.internal.R.string.face_error_unable_to_process);
+            case FACE_ERROR_TIMEOUT:
+                return mContext.getString(com.android.internal.R.string.face_error_timeout);
+            case FACE_ERROR_NO_SPACE:
+                return mContext.getString(com.android.internal.R.string.face_error_no_space);
+            case FACE_ERROR_CANCELED:
+                return mContext.getString(com.android.internal.R.string.face_error_canceled);
+            case FACE_ERROR_LOCKOUT:
+                return mContext.getString(com.android.internal.R.string.face_error_lockout);
+            case FACE_ERROR_LOCKOUT_PERMANENT:
+                return mContext.getString(
+                        com.android.internal.R.string.face_error_lockout_permanent);
+            case FACE_ERROR_NOT_ENROLLED:
+                return mContext.getString(com.android.internal.R.string.face_error_not_enrolled);
+            case FACE_ERROR_HW_NOT_PRESENT:
+                return mContext.getString(com.android.internal.R.string.face_error_hw_not_present);
+            case FACE_ERROR_VENDOR: {
+                String[] msgArray = mContext.getResources().getStringArray(
+                        com.android.internal.R.array.face_error_vendor);
+                if (vendorCode < msgArray.length) {
+                    return msgArray[vendorCode];
+                }
+            }
+        }
+        Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
+        return null;
+    }
+
+    private String getAcquiredString(int acquireInfo, int vendorCode) {
+        switch (acquireInfo) {
+            case FACE_ACQUIRED_GOOD:
+                return null;
+            case FACE_ACQUIRED_INSUFFICIENT:
+                return mContext.getString(R.string.face_acquired_insufficient);
+            case FACE_ACQUIRED_TOO_BRIGHT:
+                return mContext.getString(R.string.face_acquired_too_bright);
+            case FACE_ACQUIRED_TOO_DARK:
+                return mContext.getString(R.string.face_acquired_too_dark);
+            case FACE_ACQUIRED_TOO_CLOSE:
+                return mContext.getString(R.string.face_acquired_too_close);
+            case FACE_ACQUIRED_TOO_FAR:
+                return mContext.getString(R.string.face_acquired_too_far);
+            case FACE_ACQUIRED_TOO_HIGH:
+                return mContext.getString(R.string.face_acquired_too_high);
+            case FACE_ACQUIRED_TOO_LOW:
+                return mContext.getString(R.string.face_acquired_too_low);
+            case FACE_ACQUIRED_TOO_RIGHT:
+                return mContext.getString(R.string.face_acquired_too_right);
+            case FACE_ACQUIRED_TOO_LEFT:
+                return mContext.getString(R.string.face_acquired_too_left);
+            case FACE_ACQUIRED_POOR_GAZE:
+                return mContext.getString(R.string.face_acquired_poor_gaze);
+            case FACE_ACQUIRED_NOT_DETECTED:
+                return mContext.getString(R.string.face_acquired_not_detected);
+            case FACE_ACQUIRED_VENDOR: {
+                String[] msgArray = mContext.getResources().getStringArray(
+                        R.array.face_acquired_vendor);
+                if (vendorCode < msgArray.length) {
+                    return msgArray[vendorCode];
+                }
+            }
+        }
+        Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode);
+        return null;
+    }
+
+    /**
+     * Container for callback data from {@link FaceManager#authenticate(CryptoObject,
+     * CancellationSignal, int, AuthenticationCallback, Handler)}.
+     */
+    public static class AuthenticationResult {
+        private Face mFace;
+        private CryptoObject mCryptoObject;
+        private int mUserId;
+
+        /**
+         * Authentication result
+         *
+         * @param crypto the crypto object
+         * @param face   the recognized face data, if allowed.
+         * @hide
+         */
+        public AuthenticationResult(CryptoObject crypto, Face face, int userId) {
+            mCryptoObject = crypto;
+            mFace = face;
+            mUserId = userId;
+        }
+
+        /**
+         * Obtain the crypto object associated with this transaction
+         *
+         * @return crypto object provided to {@link FaceManager#authenticate
+         * (CryptoObject,
+         * CancellationSignal, int, AuthenticationCallback, Handler)}.
+         */
+        public CryptoObject getCryptoObject() {
+            return mCryptoObject;
+        }
+
+        /**
+         * Obtain the Face associated with this operation. Applications are strongly
+         * discouraged from associating specific faces with specific applications or operations.
+         *
+         * @hide
+         */
+        public Face getFace() {
+            return mFace;
+        }
+
+        /**
+         * Obtain the userId for which this face was authenticated.
+         *
+         * @hide
+         */
+        public int getUserId() {
+            return mUserId;
+        }
+    }
+
+    /**
+     * Callback structure provided to {@link FaceManager#authenticate(CryptoObject,
+     * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link
+     * FaceManager#authenticate(CryptoObject, CancellationSignal,
+     * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening
+     * to face events.
+     */
+    public abstract static class AuthenticationCallback
+            extends BiometricAuthenticator.AuthenticationCallback {
+
+        /**
+         * Called when an unrecoverable error has been encountered and the operation is complete.
+         * No further callbacks will be made on this object.
+         *
+         * @param errorCode An integer identifying the error message
+         * @param errString A human-readable error string that can be shown in UI
+         */
+        public void onAuthenticationError(int errorCode, CharSequence errString) {
+        }
+
+        /**
+         * Called when a recoverable error has been encountered during authentication. The help
+         * string is provided to give the user guidance for what went wrong, such as
+         * "Sensor dirty, please clean it."
+         *
+         * @param helpCode   An integer identifying the error message
+         * @param helpString A human-readable string that can be shown in UI
+         */
+        public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
+        }
+
+        /**
+         * Called when a face is recognized.
+         *
+         * @param result An object containing authentication-related data
+         */
+        public void onAuthenticationSucceeded(AuthenticationResult result) {
+        }
+
+        /**
+         * Called when a face is detected but not recognized.
+         */
+        public void onAuthenticationFailed() {
+        }
+
+        /**
+         * Called when a face image has been acquired, but wasn't processed yet.
+         *
+         * @param acquireInfo one of FACE_ACQUIRED_* constants
+         * @hide
+         */
+        public void onAuthenticationAcquired(int acquireInfo) {
+        }
+
+        /**
+         * @hide
+         * @param result
+         */
+        @Override
+        public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
+            onAuthenticationSucceeded(new AuthenticationResult(
+                    result.getCryptoObject(),
+                    (Face) result.getId(), result.getUserId()));
+        }
+
+    }
+
+    /**
+     * Callback structure provided to {@link FaceManager#enroll(long,
+     * EnrollmentCallback, CancellationSignal, int). Users of {@link #FaceAuthenticationManager()}
+     * must provide an implementation of this to {@link FaceManager#enroll(long,
+     * CancellationSignal, int, EnrollmentCallback) for listening to face enrollment events.
+     *
+     * @hide
+     */
+    public abstract static class EnrollmentCallback {
+
+        /**
+         * Called when an unrecoverable error has been encountered and the operation is complete.
+         * No further callbacks will be made on this object.
+         *
+         * @param errMsgId  An integer identifying the error message
+         * @param errString A human-readable error string that can be shown in UI
+         */
+        public void onEnrollmentError(int errMsgId, CharSequence errString) {
+        }
+
+        /**
+         * Called when a recoverable error has been encountered during enrollment. The help
+         * string is provided to give the user guidance for what went wrong, such as
+         * "Image too dark, uncover light source" or what they need to do next, such as
+         * "Rotate face up / down."
+         *
+         * @param helpMsgId  An integer identifying the error message
+         * @param helpString A human-readable string that can be shown in UI
+         */
+        public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
+        }
+
+        /**
+         * Called as each enrollment step progresses. Enrollment is considered complete when
+         * remaining reaches 0. This function will not be called if enrollment fails. See
+         * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
+         *
+         * @param remaining The number of remaining steps
+         */
+        public void onEnrollmentProgress(int remaining) {
+        }
+    }
+
+    /**
+     * Callback structure provided to {@link #remove}. Users of {@link FaceManager}
+     * may
+     * optionally provide an implementation of this to
+     * {@link #remove(Face, int, RemovalCallback)} for listening to face template
+     * removal events.
+     *
+     * @hide
+     */
+    public abstract static class RemovalCallback {
+
+        /**
+         * Called when the given face can't be removed.
+         *
+         * @param face      The face that the call attempted to remove
+         * @param errMsgId  An associated error message id
+         * @param errString An error message indicating why the face id can't be removed
+         */
+        public void onRemovalError(Face face, int errMsgId, CharSequence errString) {
+        }
+
+        /**
+         * Called when a given face is successfully removed.
+         *
+         * @param face The face template that was removed.
+         */
+        public void onRemovalSucceeded(Face face, int remaining) {
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public abstract static class LockoutResetCallback {
+
+        /**
+         * Called when lockout period expired and clients are allowed to listen for face
+         * authentication
+         * again.
+         */
+        public void onLockoutReset() {
+        }
+    }
+
+    private class OnEnrollCancelListener implements OnCancelListener {
+        @Override
+        public void onCancel() {
+            cancelEnrollment();
+        }
+    }
+
+    private class OnAuthenticationCancelListener implements OnCancelListener {
+        private CryptoObject mCrypto;
+
+        OnAuthenticationCancelListener(CryptoObject crypto) {
+            mCrypto = crypto;
+        }
+
+        @Override
+        public void onCancel() {
+            cancelAuthentication(mCrypto);
+        }
+    }
+
+    private class MyHandler extends Handler {
+        private MyHandler(Context context) {
+            super(context.getMainLooper());
+        }
+
+        private MyHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(android.os.Message msg) {
+            switch (msg.what) {
+                case MSG_ENROLL_RESULT:
+                    sendEnrollResult((Face) msg.obj, msg.arg1 /* remaining */);
+                    break;
+                case MSG_ACQUIRED:
+                    sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */,
+                            msg.arg2 /* vendorCode */);
+                    break;
+                case MSG_AUTHENTICATION_SUCCEEDED:
+                    sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */);
+                    break;
+                case MSG_AUTHENTICATION_FAILED:
+                    sendAuthenticatedFailed();
+                    break;
+                case MSG_ERROR:
+                    sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */,
+                            msg.arg2 /* vendorCode */);
+                    break;
+                case MSG_REMOVED:
+                    sendRemovedResult((Face) msg.obj, msg.arg1 /* remaining */);
+                    break;
+            }
+        }
+    };
+
+    private void sendRemovedResult(Face face, int remaining) {
+        if (mRemovalCallback == null) {
+            return;
+        }
+        if (face == null) {
+            Log.e(TAG, "Received MSG_REMOVED, but face is null");
+            return;
+        }
+        mRemovalCallback.onRemovalSucceeded(face, remaining);
+    }
+
+    private void sendErrorResult(long deviceId, int errMsgId, int vendorCode) {
+        // emulate HAL 2.1 behavior and send real errMsgId
+        final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR
+                ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId;
+        if (mEnrollmentCallback != null) {
+            mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
+                    getErrorString(errMsgId, vendorCode));
+        } else if (mAuthenticationCallback != null) {
+            mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
+                    getErrorString(errMsgId, vendorCode));
+        } else if (mRemovalCallback != null) {
+            mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
+                    getErrorString(errMsgId, vendorCode));
+        }
+    }
+
+    private void sendEnrollResult(Face face, int remaining) {
+        if (mEnrollmentCallback != null) {
+            mEnrollmentCallback.onEnrollmentProgress(remaining);
+        }
+    }
+
+    private void sendAuthenticatedSucceeded(Face face, int userId) {
+        if (mAuthenticationCallback != null) {
+            final BiometricAuthenticator.AuthenticationResult result =
+                    new BiometricAuthenticator.AuthenticationResult(mCryptoObject, face, userId);
+            mAuthenticationCallback.onAuthenticationSucceeded(result);
+        }
+    }
+
+    private void sendAuthenticatedFailed() {
+        if (mAuthenticationCallback != null) {
+            mAuthenticationCallback.onAuthenticationFailed();
+        }
+    }
+
+    private void sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode) {
+        if (mAuthenticationCallback != null) {
+            mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
+        }
+        final String msg = getAcquiredString(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) {
+            mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
+        }
+    }
+}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
new file mode 100644
index 0000000..03bb7ae
--- /dev/null
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -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 android.hardware.face;
+
+import android.os.Bundle;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.face.IFaceServiceReceiver;
+import android.hardware.face.Face;
+
+/**
+ * Communication channel from client to the face service.
+ * @hide
+ */
+interface IFaceService {
+    // Authenticate the given sessionId with a face
+    void authenticate(IBinder token, long sessionId,
+            IFaceServiceReceiver receiver, int flags, String opPackageName,
+            in Bundle bundle, IBiometricPromptReceiver dialogReceiver);
+
+    // Cancel authentication for the given sessionId
+    void cancelAuthentication(IBinder token, String opPackageName);
+
+    // Start face enrollment
+    void enroll(IBinder token, in byte [] cryptoToken, int userId, IFaceServiceReceiver receiver,
+                int flags, String opPackageName);
+
+    // Cancel enrollment in progress
+    void cancelEnrollment(IBinder token);
+
+    // Any errors resulting from this call will be returned to the listener
+    void remove(IBinder token, int faceId, int userId, IFaceServiceReceiver receiver);
+
+    // Rename the face specified by faceId to the given name
+    void rename(int faceId, String name);
+
+    // Get the enrolled face for user.
+    List<Face> getEnrolledFaces(int userId, String opPackageName);
+
+    // Determine if HAL is loaded and ready
+    boolean isHardwareDetected(long deviceId, String opPackageName);
+
+    // Get a pre-enrollment authentication token
+    long preEnroll(IBinder token);
+
+    // Finish an enrollment sequence and invalidate the authentication token
+    int postEnroll(IBinder token);
+
+    // Determine if a user has at least one enrolled face
+    boolean hasEnrolledFaces(int userId, String opPackageName);
+
+    // Gets the number of hardware devices
+    // int getHardwareDeviceCount();
+
+    // Gets the unique device id for hardware enumerated at i
+    // long getHardwareDevice(int i);
+
+    // Gets the authenticator ID for face
+    long getAuthenticatorId(String opPackageName);
+
+    // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
+    void resetTimeout(in byte [] cryptoToken);
+
+    // Add a callback which gets notified when the face lockout period expired.
+    void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
+
+    // Explicitly set the active user (for enrolling work profile)
+    void setActiveUser(int uid);
+
+    // Enumerate all faces
+    void enumerate(IBinder token, int userId, IFaceServiceReceiver receiver);
+}
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
new file mode 100644
index 0000000..16fb690
--- /dev/null
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -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.
+ */
+package android.hardware.face;
+
+import android.hardware.face.Face;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * Communication channel from the FaceService back to FaceAuthenticationManager.
+ * @hide
+ */
+oneway interface IFaceServiceReceiver {
+    void onEnrollResult(long deviceId, int faceId, int remaining);
+    void onAcquired(long deviceId, int acquiredInfo, int vendorCode);
+    void onAuthenticationSucceeded(long deviceId, in Face face);
+    void onAuthenticationFailed(long deviceId);
+    void onError(long deviceId, int error, int vendorCode);
+    void onRemoved(long deviceId, int faceId, int remaining);
+}
diff --git a/core/java/android/hardware/fingerprint/Fingerprint.java b/core/java/android/hardware/fingerprint/Fingerprint.java
index c7ce8fa..bbd3d05 100644
--- a/core/java/android/hardware/fingerprint/Fingerprint.java
+++ b/core/java/android/hardware/fingerprint/Fingerprint.java
@@ -23,62 +23,36 @@
  * Container for fingerprint metadata.
  * @hide
  */
-public final class Fingerprint extends BiometricAuthenticator.BiometricIdentifier {
-    private CharSequence mName;
+public final class Fingerprint extends BiometricAuthenticator.Identifier {
     private int mGroupId;
-    private int mFingerId;
-    private long mDeviceId; // physical device this is associated with
 
     public Fingerprint(CharSequence name, int groupId, int fingerId, long deviceId) {
-        mName = name;
+        super(name, fingerId, deviceId);
         mGroupId = groupId;
-        mFingerId = fingerId;
-        mDeviceId = deviceId;
     }
 
     private Fingerprint(Parcel in) {
-        mName = in.readString();
+        super(in.readString(), in.readInt(), in.readLong());
         mGroupId = in.readInt();
-        mFingerId = in.readInt();
-        mDeviceId = in.readLong();
     }
 
     /**
-     * Gets the human-readable name for the given fingerprint.
-     * @return name given to finger
-     */
-    public CharSequence getName() { return mName; }
-
-    /**
-     * Gets the device-specific finger id.  Used by Settings to map a name to a specific
-     * fingerprint template.
-     * @return device-specific id for this finger
-     * @hide
-     */
-    public int getFingerId() { return mFingerId; }
-
-    /**
      * Gets the group id specified when the fingerprint was enrolled.
      * @return group id for the set of fingerprints this one belongs to.
-     * @hide
      */
-    public int getGroupId() { return mGroupId; }
-
-    /**
-     * Device this fingerprint belongs to.
-     * @hide
-     */
-    public long getDeviceId() { return mDeviceId; }
+    public int getGroupId() {
+        return mGroupId;
+    }
 
     public int describeContents() {
         return 0;
     }
 
     public void writeToParcel(Parcel out, int flags) {
-        out.writeString(mName.toString());
+        out.writeString(getName().toString());
+        out.writeInt(getBiometricId());
+        out.writeLong(getDeviceId());
         out.writeInt(mGroupId);
-        out.writeInt(mFingerId);
-        out.writeLong(mDeviceId);
     }
 
     public static final Parcelable.Creator<Fingerprint> CREATOR
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index ebbfe1c..bf5ab90 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -27,6 +27,7 @@
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
+import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -34,6 +35,7 @@
 import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -188,6 +190,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public Fingerprint getFingerprint() { return mFingerprint; }
 
         /**
@@ -403,7 +406,8 @@
 
     /**
      * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
-     * CancellationSignal, int, AuthenticationCallback, Handler)}
+     * CancellationSignal, int, AuthenticationCallback, Handler)}. This version does not
+     * display the BiometricPrompt.
      * @param userId the user ID that the fingerprint hardware will authenticate for.
      * @hide
      */
@@ -442,9 +446,7 @@
     }
 
     /**
-     * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
-     * CancellationSignal, Bundle, Executor, IBiometricPromptReceiver, AuthenticationCallback)}
-     * @param userId the user ID that the fingerprint hardware will authenticate for.
+     * Per-user version. This method invokes the BiometricPrompt.
      */
     private void authenticate(int userId,
             @Nullable android.hardware.biometrics.CryptoObject crypto,
@@ -657,7 +659,7 @@
         if (mService != null) try {
             mRemovalCallback = callback;
             mRemovalFingerprint = fp;
-            mService.remove(mToken, fp.getFingerId(), fp.getGroupId(), userId, mServiceReceiver);
+            mService.remove(mToken, fp.getBiometricId(), fp.getGroupId(), userId, mServiceReceiver);
         } catch (RemoteException e) {
             Slog.w(TAG, "Remote exception in remove: ", e);
             if (callback != null) {
@@ -718,6 +720,7 @@
      * @hide
      */
     @RequiresPermission(USE_FINGERPRINT)
+    @UnsupportedAppUsage
     public List<Fingerprint> getEnrolledFingerprints(int userId) {
         if (mService != null) try {
             return mService.getEnrolledFingerprints(userId, mContext.getOpPackageName());
@@ -734,6 +737,7 @@
      * @hide
      */
     @RequiresPermission(USE_FINGERPRINT)
+    @UnsupportedAppUsage
     public List<Fingerprint> getEnrolledFingerprints() {
         return getEnrolledFingerprints(mContext.getUserId());
     }
@@ -801,6 +805,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public long getAuthenticatorId() {
         if (mService != null) {
             try {
@@ -841,7 +846,7 @@
             try {
                 final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
                 mService.addLockoutResetCallback(
-                        new IFingerprintServiceLockoutResetCallback.Stub() {
+                        new IBiometricServiceLockoutResetCallback.Stub() {
 
                     @Override
                     public void onLockoutReset(long deviceId, IRemoteCallback serverCallback)
@@ -881,7 +886,7 @@
 
         @Override
         public void handleMessage(android.os.Message msg) {
-            switch(msg.what) {
+            switch (msg.what) {
                 case MSG_ENROLL_RESULT:
                     sendEnrollResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
                     break;
@@ -908,45 +913,45 @@
                     break;
             }
         }
-
-        private void sendRemovedResult(Fingerprint fingerprint, int remaining) {
-            if (mRemovalCallback == null) {
-                return;
-            }
-            if (fingerprint == null) {
-                Slog.e(TAG, "Received MSG_REMOVED, but fingerprint is null");
-                return;
-            }
-
-            int fingerId = fingerprint.getFingerId();
-            int reqFingerId = mRemovalFingerprint.getFingerId();
-            if (reqFingerId != 0 && fingerId != 0 && fingerId != reqFingerId) {
-                Slog.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
-                return;
-            }
-            int groupId = fingerprint.getGroupId();
-            int reqGroupId = mRemovalFingerprint.getGroupId();
-            if (groupId != reqGroupId) {
-                Slog.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId);
-                return;
-            }
-
-            mRemovalCallback.onRemovalSucceeded(fingerprint, remaining);
-        }
-
-        private void sendEnumeratedResult(long deviceId, int fingerId, int groupId) {
-            if (mEnumerateCallback != null) {
-                mEnumerateCallback.onEnumerate(new Fingerprint(null, groupId, fingerId, deviceId));
-            }
-        }
-
-        private void sendEnrollResult(Fingerprint fp, int remaining) {
-            if (mEnrollmentCallback != null) {
-                mEnrollmentCallback.onEnrollmentProgress(remaining);
-            }
-        }
     };
 
+    private void sendRemovedResult(Fingerprint fingerprint, int remaining) {
+        if (mRemovalCallback == null) {
+            return;
+        }
+        if (fingerprint == null) {
+            Slog.e(TAG, "Received MSG_REMOVED, but fingerprint is null");
+            return;
+        }
+
+        int fingerId = fingerprint.getBiometricId();
+        int reqFingerId = mRemovalFingerprint.getBiometricId();
+        if (reqFingerId != 0 && fingerId != 0 && fingerId != reqFingerId) {
+            Slog.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
+            return;
+        }
+        int groupId = fingerprint.getGroupId();
+        int reqGroupId = mRemovalFingerprint.getGroupId();
+        if (groupId != reqGroupId) {
+            Slog.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId);
+            return;
+        }
+
+        mRemovalCallback.onRemovalSucceeded(fingerprint, remaining);
+    }
+
+    private void sendEnumeratedResult(long deviceId, int fingerId, int groupId) {
+        if (mEnumerateCallback != null) {
+            mEnumerateCallback.onEnumerate(new Fingerprint(null, groupId, fingerId, deviceId));
+        }
+    }
+
+    private void sendEnrollResult(Fingerprint fp, int remaining) {
+        if (mEnrollmentCallback != null) {
+            mEnrollmentCallback.onEnrollmentProgress(remaining);
+        }
+    }
+
     private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
         if (mAuthenticationCallback != null) {
             final BiometricAuthenticator.AuthenticationResult result =
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 8aa2183..71a6420 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -17,9 +17,9 @@
 
 import android.os.Bundle;
 import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
 import android.hardware.fingerprint.Fingerprint;
 import java.util.List;
 
@@ -78,7 +78,7 @@
     void resetTimeout(in byte [] cryptoToken);
 
     // Add a callback which gets notified when the fingerprint lockout period expired.
-    void addLockoutResetCallback(IFingerprintServiceLockoutResetCallback callback);
+    void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
 
     // Explicitly set the active user (for enrolling work profile)
     void setActiveUser(int uid);
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl
deleted file mode 100644
index 971e14c..0000000
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.fingerprint;
-
-import android.hardware.fingerprint.Fingerprint;
-import android.os.Bundle;
-import android.os.IRemoteCallback;
-import android.os.UserHandle;
-
-/**
- * Callback when lockout period expired and clients are allowed to authenticate again.
- * @hide
- */
-oneway interface IFingerprintServiceLockoutResetCallback {
-
-    /**
-     * A wakelock will be held until the reciever calls back into {@param callback}
-     */
-    void onLockoutReset(long deviceId, IRemoteCallback callback);
-}
diff --git a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
new file mode 100644
index 0000000..9bebbd2
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.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.hardware.hdmi;
+
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
+/**
+ * HdmiAudioSystemClient represents HDMI-CEC logical device of type Audio System in the Android
+ * system which acts as an audio system device such as sound bar.
+ *
+ * <p>HdmiAudioSystemClient provides methods that control, get information from TV/Display device
+ * connected through HDMI bus.
+ *
+ * @hide
+ */
+public final class HdmiAudioSystemClient extends HdmiClient {
+    private static final String TAG = "HdmiAudioSystemClient";
+
+    private static final int REPORT_AUDIO_STATUS_INTERVAL_MS = 500;
+
+    private final Handler mHandler;
+    private boolean mCanSendAudioStatus = true;
+    private boolean mPendingReportAudioStatus;
+
+    private int mLastVolume;
+    private int mLastMaxVolume;
+    private boolean mLastIsMute;
+
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public HdmiAudioSystemClient(IHdmiControlService service) {
+        this(service, null);
+    }
+
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public HdmiAudioSystemClient(IHdmiControlService service, @Nullable Handler handler) {
+        super(service);
+        mHandler = handler == null ? new Handler() : handler;
+    }
+
+    /** @hide */
+    // TODO(b/110094868): unhide and add @SystemApi for Q
+    @Override
+    public int getDeviceType() {
+        return HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
+    }
+
+    /**
+     * Sends a Report Audio Status HDMI CEC command to TV devices when necessary.
+     *
+     * According to HDMI CEC specification, an audio system can report its audio status when System
+     * Audio Mode is on, so that the TV can display the audio status of external amplifier.
+     *
+     * @hide
+     */
+    public void sendReportAudioStatusCecCommand(boolean isMuteAdjust, int volume, int maxVolume,
+            boolean isMute) {
+        if (isMuteAdjust) {
+            // always report audio status when it's muted/unmuted
+            try {
+                mService.reportAudioStatus(getDeviceType(), volume, maxVolume, isMute);
+            } catch (RemoteException e) {
+                // do nothing. Reporting audio status is optional.
+            }
+            return;
+        }
+
+        mLastVolume = volume;
+        mLastMaxVolume = maxVolume;
+        mLastIsMute = isMute;
+        if (mCanSendAudioStatus) {
+            try {
+                mService.reportAudioStatus(getDeviceType(), volume, maxVolume, isMute);
+                mCanSendAudioStatus = false;
+                mHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (mPendingReportAudioStatus) {
+                            // report audio status if there is any pending message
+                            try {
+                                mService.reportAudioStatus(getDeviceType(), mLastVolume,
+                                        mLastMaxVolume, mLastIsMute);
+                                mHandler.postDelayed(this, REPORT_AUDIO_STATUS_INTERVAL_MS);
+                            }  catch (RemoteException e) {
+                                mCanSendAudioStatus = true;
+                            } finally {
+                                mPendingReportAudioStatus = false;
+                            }
+                        } else {
+                            mCanSendAudioStatus = true;
+                        }
+                    }
+                }, REPORT_AUDIO_STATUS_INTERVAL_MS);
+            } catch (RemoteException e) {
+                // do nothing. Reporting audio status is optional.
+            }
+        } else {
+            // if audio status cannot be sent, send it latter
+            mPendingReportAudioStatus = true;
+        }
+    }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index e34423c..72a6ffe 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -22,10 +22,10 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.pm.PackageManager;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -262,6 +262,8 @@
     private final boolean mHasPlaybackDevice;
     // True if we have a logical device of type TV hosted in the system.
     private final boolean mHasTvDevice;
+    // True if we have a logical device of type audio system hosted in the system.
+    private final boolean mHasAudioSystemDevice;
 
     /**
      * {@hide} - hide this constructor because it has a parameter of type IHdmiControlService,
@@ -280,6 +282,7 @@
         }
         mHasTvDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_TV);
         mHasPlaybackDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PLAYBACK);
+        mHasAudioSystemDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
     }
 
     private static boolean hasDeviceType(int[] types, int type) {
@@ -301,6 +304,7 @@
      * @return {@link HdmiClient} instance. {@code null} on failure.
      * See {@link HdmiDeviceInfo#DEVICE_PLAYBACK}
      * See {@link HdmiDeviceInfo#DEVICE_TV}
+     * See {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM}
      */
     @Nullable
     @SuppressLint("Doclava125")
@@ -313,6 +317,8 @@
                 return mHasTvDevice ? new HdmiTvClient(mService) : null;
             case HdmiDeviceInfo.DEVICE_PLAYBACK:
                 return mHasPlaybackDevice ? new HdmiPlaybackClient(mService) : null;
+            case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
+                return mHasAudioSystemDevice ? new HdmiAudioSystemClient(mService) : null;
             default:
                 return null;
         }
@@ -349,6 +355,24 @@
     }
 
     /**
+     * Gets an object that represents an HDMI-CEC logical device of type audio system on the system.
+     *
+     * <p>Used to send HDMI control messages to other devices like TV through HDMI bus. It is also
+     * possible to communicate with other logical devices hosted in the same system if the system is
+     * configured to host more than one type of HDMI-CEC logical devices.
+     *
+     * @return {@link HdmiAudioSystemClient} instance. {@code null} on failure.
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    @Nullable
+    @SuppressLint("Doclava125")
+    public HdmiAudioSystemClient getAudioSystemClient() {
+        return (HdmiAudioSystemClient) getClient(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+    }
+
+    /**
      * Controls standby mode of the system. It will also try to turn on/off the connected devices if
      * necessary.
      *
diff --git a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
index 874b0c6..df231e6 100644
--- a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
+++ b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
@@ -23,7 +23,8 @@
 /**
  * HdmiPlaybackClient represents HDMI-CEC logical device of type Playback
  * in the Android system which acts as a playback device such as set-top box.
- * It provides with methods that control, get information from TV/Display device
+ *
+ * <p>HdmiPlaybackClient provides methods that control, get information from TV/Display device
  * connected through HDMI bus.
  *
  * @hide
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index a336e5c..f33a137 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -29,8 +29,9 @@
 
 /**
  * HdmiTvClient represents HDMI-CEC logical device of type TV in the Android system
- * which acts as TV/Display. It provides with methods that manage, interact with other
- * devices on the CEC bus.
+ * which acts as TV/Display.
+ *
+ * <p>HdmiTvClient provides methods that manage, interact with other devices on the CEC bus.
  *
  * @hide
  */
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 67e2d18..2b8d00b 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -72,4 +72,5 @@
     void sendMhlVendorCommand(int portId, int offset, int length, in byte[] data);
     void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener);
     void setStandbyMode(boolean isStandbyModeOn);
+    void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
 }
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6ae7a14..6ca5f0c 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -20,6 +20,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemService;
+import android.annotation.UnsupportedAppUsage;
 import android.app.IInputForwarder;
 import android.content.Context;
 import android.media.AudioAttributes;
@@ -64,6 +65,7 @@
 
     private static InputManager sInstance;
 
+    @UnsupportedAppUsage
     private final IInputManager mIm;
 
     // Guarded by mInputDevicesLock
@@ -181,6 +183,7 @@
      * Waits for the event to be delivered to the application and handled.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2;  // see InputDispatcher.h
 
     /** @hide */
@@ -223,6 +226,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static InputManager getInstance() {
         synchronized (InputManager.class) {
             if (sInstance == null) {
@@ -866,6 +870,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean injectInputEvent(InputEvent event, int mode) {
         if (event == null) {
             throw new IllegalArgumentException("event must not be null");
@@ -891,6 +896,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setPointerIconType(int iconId) {
         try {
             mIm.setPointerIconType(iconId);
@@ -938,6 +944,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public IInputForwarder createInputForwarder(int displayId) {
         try {
             return mIm.createInputForwarder(displayId);
@@ -1191,8 +1198,8 @@
          * @hide
          */
         @Override
-        public void vibrate(int uid, String opPkg,
-                VibrationEffect effect, AudioAttributes attributes) {
+        public void vibrate(int uid, String opPkg, VibrationEffect effect,
+                String reason, AudioAttributes attributes) {
             long[] pattern;
             int repeat;
             if (effect instanceof VibrationEffect.OneShot) {
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 0a21083..2335203 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -102,7 +102,7 @@
     /**
      * Sends a message to a nanoapp through the Context Hub Service.
      *
-     * This function returns TRANSACTION_SUCCESS if the message has reached the HAL, but
+     * This function returns RESULT_SUCCESS if the message has reached the HAL, but
      * does not guarantee delivery of the message to the target nanoapp.
      *
      * @param message the message object to send
diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java
index 66dd9fc..23d8d01 100644
--- a/core/java/android/hardware/location/GeofenceHardware.java
+++ b/core/java/android/hardware/location/GeofenceHardware.java
@@ -16,6 +16,7 @@
 package android.hardware.location;
 
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.location.Location;
 import android.os.Build;
 import android.os.RemoteException;
@@ -168,6 +169,7 @@
                     GeofenceHardwareMonitorCallbackWrapper>();
 
     /** @hide */
+    @UnsupportedAppUsage
     public GeofenceHardware(IGeofenceHardware service) {
         mService = service;
     }
diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java
index 4d8e734..562065e 100644
--- a/core/java/android/hardware/location/NanoAppFilter.java
+++ b/core/java/android/hardware/location/NanoAppFilter.java
@@ -85,7 +85,7 @@
         mAppId = in.readLong();
         mAppVersion = in.readInt();
         mVersionRestrictionMask = in.readInt();
-        mAppIdVendorMask = in.readInt();
+        mAppIdVendorMask = in.readLong();
     }
 
     public int describeContents() {
@@ -93,7 +93,6 @@
     }
 
     public void writeToParcel(Parcel out, int flags) {
-
         out.writeLong(mAppId);
         out.writeInt(mAppVersion);
         out.writeInt(mVersionRestrictionMask);
diff --git a/core/java/android/hardware/radio/RadioManager.aidl b/core/java/android/hardware/radio/RadioManager.aidl
index 8a39388..34c05d8 100644
--- a/core/java/android/hardware/radio/RadioManager.aidl
+++ b/core/java/android/hardware/radio/RadioManager.aidl
@@ -20,6 +20,9 @@
 parcelable RadioManager.BandConfig;
 
 /** @hide */
+parcelable RadioManager.BandDescriptor;
+
+/** @hide */
 parcelable RadioManager.ModuleProperties;
 
 /** @hide */
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index dde8a33..007f4bc 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -24,6 +24,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.media.AudioFormat;
 import android.os.Handler;
 import android.os.Parcel;
@@ -72,6 +73,7 @@
      ****************************************************************************/
     public static class ModuleProperties implements Parcelable {
         /** Unique module ID provided by the native service */
+        @UnsupportedAppUsage
         public final int id;
 
         /** human readable voice detection engine implementor */
@@ -81,12 +83,14 @@
         public final String description;
 
         /** Unique voice engine Id (changes with each version) */
+        @UnsupportedAppUsage
         public final UUID uuid;
 
         /** Voice detection engine version */
         public final int version;
 
         /** Maximum number of active sound models */
+        @UnsupportedAppUsage
         public final int maxSoundModels;
 
         /** Maximum number of key phrases */
@@ -114,6 +118,7 @@
          * recognition callback event */
         public final boolean returnsTriggerInEvent;
 
+        @UnsupportedAppUsage
         ModuleProperties(int id, String implementor, String description,
                 String uuid, int version, int maxSoundModels, int maxKeyphrases,
                 int maxUsers, int recognitionModes, boolean supportsCaptureTransition,
@@ -225,15 +230,18 @@
         public static final int TYPE_GENERIC_SOUND = 1;
 
         /** Unique sound model identifier */
+        @UnsupportedAppUsage
         public final UUID uuid;
 
         /** Sound model type (e.g. TYPE_KEYPHRASE); */
         public final int type;
 
         /** Unique sound model vendor identifier */
+        @UnsupportedAppUsage
         public final UUID vendorUuid;
 
         /** Opaque data. For use by vendor implementation and enrollment application */
+        @UnsupportedAppUsage
         public final byte[] data;
 
         public SoundModel(UUID uuid, UUID vendorUuid, int type, byte[] data) {
@@ -289,21 +297,27 @@
      ****************************************************************************/
     public static class Keyphrase implements Parcelable {
         /** Unique identifier for this keyphrase */
+        @UnsupportedAppUsage
         public final int id;
 
         /** Recognition modes supported for this key phrase in the model */
+        @UnsupportedAppUsage
         public final int recognitionModes;
 
         /** Locale of the keyphrase. JAVA Locale string e.g en_US */
+        @UnsupportedAppUsage
         public final String locale;
 
         /** Key phrase text */
+        @UnsupportedAppUsage
         public final String text;
 
         /** Users this key phrase has been trained for. countains sound trigger specific user IDs
          * derived from system user IDs {@link android.os.UserHandle#getIdentifier()}. */
+        @UnsupportedAppUsage
         public final int[] users;
 
+        @UnsupportedAppUsage
         public Keyphrase(int id, int recognitionModes, String locale, String text, int[] users) {
             this.id = id;
             this.recognitionModes = recognitionModes;
@@ -412,8 +426,10 @@
      ****************************************************************************/
     public static class KeyphraseSoundModel extends SoundModel implements Parcelable {
         /** Key phrases in this sound model */
+        @UnsupportedAppUsage
         public final Keyphrase[] keyphrases; // keyword phrases in model
 
+        @UnsupportedAppUsage
         public KeyphraseSoundModel(
                 UUID uuid, UUID vendorUuid, byte[] data, Keyphrase[] keyphrases) {
             super(uuid, vendorUuid, TYPE_KEYPHRASE, data);
@@ -511,6 +527,7 @@
             }
         };
 
+        @UnsupportedAppUsage
         public GenericSoundModel(UUID uuid, UUID vendorUuid, byte[] data) {
             super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data);
         }
@@ -606,6 +623,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public final int status;
         /**
          *
@@ -613,12 +631,14 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public final int soundModelHandle;
         /**
          * True if it is possible to capture audio from this utterance buffered by the hardware
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public final boolean captureAvailable;
         /**
          * Audio session ID to be used when capturing the utterance with an AudioRecord
@@ -626,6 +646,7 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public final int captureSession;
         /**
          * Delay in ms between end of model detection and start of audio available for capture.
@@ -659,9 +680,11 @@
          *
          * @hide
          */
+        @UnsupportedAppUsage
         public final byte[] data;
 
         /** @hide */
+        @UnsupportedAppUsage
         public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
                 int captureSession, int captureDelayMs, int capturePreambleMs,
                 boolean triggerInData, AudioFormat captureFormat, byte[] data) {
@@ -865,6 +888,7 @@
     public static class RecognitionConfig implements Parcelable {
         /** True if the DSP should capture the trigger sound and make it available for further
          * capture. */
+        @UnsupportedAppUsage
         public final boolean captureRequested;
         /**
          * True if the service should restart listening after the DSP triggers.
@@ -873,11 +897,14 @@
         public final boolean allowMultipleTriggers;
         /** List of all keyphrases in the sound model for which recognition should be performed with
          * options for each keyphrase. */
+        @UnsupportedAppUsage
         public final KeyphraseRecognitionExtra keyphrases[];
         /** Opaque data for use by system applications who know about voice engine internals,
          * typically during enrollment. */
+        @UnsupportedAppUsage
         public final byte[] data;
 
+        @UnsupportedAppUsage
         public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
                 KeyphraseRecognitionExtra[] keyphrases, byte[] data) {
             this.captureRequested = captureRequested;
@@ -938,9 +965,12 @@
      * @hide
      */
     public static class ConfidenceLevel implements Parcelable {
+        @UnsupportedAppUsage
         public final int userId;
+        @UnsupportedAppUsage
         public final int confidenceLevel;
 
+        @UnsupportedAppUsage
         public ConfidenceLevel(int userId, int confidenceLevel) {
             this.userId = userId;
             this.confidenceLevel = confidenceLevel;
@@ -1014,19 +1044,24 @@
      */
     public static class KeyphraseRecognitionExtra implements Parcelable {
         /** The keyphrase ID */
+        @UnsupportedAppUsage
         public final int id;
 
         /** Recognition modes matched for this event */
+        @UnsupportedAppUsage
         public final int recognitionModes;
 
         /** Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification
          * is not performed */
+        @UnsupportedAppUsage
         public final int coarseConfidenceLevel;
 
         /** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
          * be recognized (RecognitionConfig) */
+        @UnsupportedAppUsage
         public final ConfidenceLevel[] confidenceLevels;
 
+        @UnsupportedAppUsage
         public KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel,
                 ConfidenceLevel[] confidenceLevels) {
             this.id = id;
@@ -1114,8 +1149,10 @@
      */
     public static class KeyphraseRecognitionEvent extends RecognitionEvent implements Parcelable {
         /** Indicates if the key phrase is present in the buffered audio available for capture */
+        @UnsupportedAppUsage
         public final KeyphraseRecognitionExtra[] keyphraseExtras;
 
+        @UnsupportedAppUsage
         public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
                int captureSession, int captureDelayMs, int capturePreambleMs,
                boolean triggerInData, AudioFormat captureFormat, byte[] data,
@@ -1236,6 +1273,7 @@
      * @hide
      */
     public static class GenericRecognitionEvent extends RecognitionEvent implements Parcelable {
+        @UnsupportedAppUsage
         public GenericRecognitionEvent(int status, int soundModelHandle,
                 boolean captureAvailable, int captureSession, int captureDelayMs,
                 int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat,
@@ -1305,6 +1343,7 @@
         /** New sound model data */
         public final byte[] data;
 
+        @UnsupportedAppUsage
         SoundModelEvent(int status, int soundModelHandle, byte[] data) {
             this.status = status;
             this.soundModelHandle = soundModelHandle;
@@ -1405,6 +1444,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static native int listModules(ArrayList <ModuleProperties> modules);
 
     /**
@@ -1418,6 +1458,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static SoundTriggerModule attachModule(int moduleId,
                                                   StatusListener listener,
                                                   Handler handler) {
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index e23a2bb..838765b 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -16,6 +16,7 @@
 
 package android.hardware.soundtrigger;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -28,8 +29,10 @@
  * @hide
  */
 public class SoundTriggerModule {
+    @UnsupportedAppUsage
     private long mNativeContext;
 
+    @UnsupportedAppUsage
     private int mId;
     private NativeEventHandlerDelegate mEventHandlerDelegate;
 
@@ -56,6 +59,7 @@
      * Detach from this module. The {@link SoundTrigger.StatusListener} callback will not be called
      * anymore and associated resources will be released.
      * */
+    @UnsupportedAppUsage
     public native void detach();
 
     /**
@@ -73,6 +77,7 @@
      *         service fails
      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
      */
+    @UnsupportedAppUsage
     public native int loadSoundModel(SoundTrigger.SoundModel model, int[] soundModelHandle);
 
     /**
@@ -87,6 +92,7 @@
      *         - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
      *         service fails
      */
+    @UnsupportedAppUsage
     public native int unloadSoundModel(int soundModelHandle);
 
     /**
@@ -106,6 +112,7 @@
      *         service fails
      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
      */
+    @UnsupportedAppUsage
     public native int startRecognition(int soundModelHandle, SoundTrigger.RecognitionConfig config);
 
     /**
@@ -121,6 +128,7 @@
      *         service fails
      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
      */
+    @UnsupportedAppUsage
     public native int stopRecognition(int soundModelHandle);
 
     private class NativeEventHandlerDelegate {
@@ -181,6 +189,7 @@
     }
 
     @SuppressWarnings("unused")
+    @UnsupportedAppUsage
     private static void postEventFromNative(Object module_ref,
                                             int what, int arg1, int arg2, Object obj) {
         SoundTriggerModule module = (SoundTriggerModule)((WeakReference)module_ref).get();
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 1e98301..26c5a95 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import com.android.internal.util.Preconditions;
@@ -60,6 +61,7 @@
     private @Nullable Parcelable[] mConfigurations;
 
     /** All interfaces on the device. Initialized on first call to getInterfaceList */
+    @UnsupportedAppUsage
     private @Nullable UsbInterface[] mInterfaces;
 
     /**
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 9e5174a..71297c1 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
@@ -46,6 +47,7 @@
     private Context mContext;
 
     // used by the JNI code
+    @UnsupportedAppUsage
     private long mNativeContext;
 
     private final CloseGuard mCloseGuard = CloseGuard.get();
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 46142e3..3141be4 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -25,6 +25,7 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -87,6 +88,7 @@
      *
      * {@hide}
      */
+    @UnsupportedAppUsage
     public static final String ACTION_USB_STATE =
             "android.hardware.usb.action.USB_STATE";
 
@@ -163,6 +165,7 @@
      *
      * {@hide}
      */
+    @UnsupportedAppUsage
     public static final String USB_CONNECTED = "connected";
 
     /**
@@ -189,6 +192,7 @@
      *
      * {@hide}
      */
+    @UnsupportedAppUsage
     public static final String USB_DATA_UNLOCKED = "unlocked";
 
     /**
@@ -197,6 +201,7 @@
      *
      * {@hide}
      */
+    @UnsupportedAppUsage
     public static final String USB_FUNCTION_NONE = "none";
 
     /**
@@ -363,6 +368,7 @@
     /**
      * {@hide}
      */
+    @UnsupportedAppUsage
     public UsbManager(Context context, IUsbManager service) {
         mContext = context;
         mService = service;
@@ -645,6 +651,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public boolean isFunctionEnabled(String function) {
         try {
             return mService.isFunctionEnabled(function);
@@ -693,6 +700,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public void setCurrentFunction(String functions, boolean usbDataUnlocked) {
         try {
             mService.setCurrentFunction(functions, usbDataUnlocked);
@@ -774,6 +782,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public UsbPort[] getPorts() {
         if (mService == null) {
             return null;
@@ -793,6 +802,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public UsbPortStatus getPortStatus(UsbPort port) {
         Preconditions.checkNotNull(port, "port must not be null");
 
@@ -822,6 +832,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setPortRoles(UsbPort port, int powerRole, int dataRole) {
         Preconditions.checkNotNull(port, "port must not be null");
         UsbPort.checkRoles(powerRole, dataRole);
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index 5c0e81a..2cd8209 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -16,6 +16,7 @@
 
 package android.hardware.usb;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -47,6 +48,7 @@
      *
      * @return True if there is anything connected to the port.
      */
+    @UnsupportedAppUsage
     public boolean isConnected() {
         return mCurrentMode != 0;
     }
@@ -57,6 +59,7 @@
      * @return The current mode: {@link UsbPort#MODE_DFP}, {@link UsbPort#MODE_UFP},
      * or 0 if nothing is connected.
      */
+    @UnsupportedAppUsage
     public int getCurrentMode() {
         return mCurrentMode;
     }
@@ -67,6 +70,7 @@
      * @return The current power role: {@link UsbPort#POWER_ROLE_SOURCE},
      * {@link UsbPort#POWER_ROLE_SINK}, or 0 if nothing is connected.
      */
+    @UnsupportedAppUsage
     public int getCurrentPowerRole() {
         return mCurrentPowerRole;
     }
@@ -77,6 +81,7 @@
      * @return The current data role: {@link UsbPort#DATA_ROLE_HOST},
      * {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if nothing is connected.
      */
+    @UnsupportedAppUsage
     public int getCurrentDataRole() {
         return mCurrentDataRole;
     }
@@ -90,12 +95,14 @@
      * @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.
      */
+    @UnsupportedAppUsage
     public boolean isRoleCombinationSupported(int powerRole, int dataRole) {
         return (mSupportedRoleCombinations &
                 UsbPort.combineRolesAsBit(powerRole, dataRole)) != 0;
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public int getSupportedRoleCombinations() {
         return mSupportedRoleCombinations;
     }
diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java
index f59c87e..7abf3e9 100644
--- a/core/java/android/hardware/usb/UsbRequest.java
+++ b/core/java/android/hardware/usb/UsbRequest.java
@@ -17,6 +17,7 @@
 package android.hardware.usb;
 
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.util.Log;
 
@@ -47,14 +48,17 @@
     static final int MAX_USBFS_BUFFER_SIZE = 16384;
 
     // used by the JNI code
+    @UnsupportedAppUsage
     private long mNativeContext;
 
     private UsbEndpoint mEndpoint;
 
     /** The buffer that is currently being read / written */
+    @UnsupportedAppUsage
     private ByteBuffer mBuffer;
 
     /** The amount of data to read / write when using {@link #queue} */
+    @UnsupportedAppUsage
     private int mLength;
 
     // for client use
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index b4c8a5e..46671b2 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -28,6 +28,7 @@
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.Dialog;
 import android.content.Context;
@@ -343,10 +344,12 @@
 
     InputMethodManager mImm;
     
+    @UnsupportedAppUsage
     int mTheme = 0;
     
     LayoutInflater mInflater;
     TypedArray mThemeAttrs;
+    @UnsupportedAppUsage
     View mRootView;
     SoftInputWindow mWindow;
     boolean mInitialized;
@@ -370,19 +373,6 @@
     InputConnection mStartedInputConnection;
     EditorInfo mInputEditorInfo;
 
-    /**
-     * A token to keep tracking the last IPC that triggered
-     * {@link #doStartInput(InputConnection, EditorInfo, boolean)}. If
-     * {@link #doStartInput(InputConnection, EditorInfo, boolean)} was not caused by IPCs from
-     * {@link com.android.server.InputMethodManagerService}, this needs to remain unchanged.
-     *
-     * <p>Some IPCs to {@link com.android.server.InputMethodManagerService} require this token to
-     * disentangle event flows for various purposes such as better window animation and providing
-     * fine-grained debugging information.</p>
-     */
-    @Nullable
-    private IBinder mStartInputToken;
-
     int mShowInputFlags;
     boolean mShowInputRequested;
     boolean mLastShowInputRequested;
@@ -391,8 +381,10 @@
 
     boolean mFullscreenApplied;
     boolean mIsFullscreen;
+    @UnsupportedAppUsage
     View mExtractView;
     boolean mExtractViewHidden;
+    @UnsupportedAppUsage
     ExtractEditText mExtractEditText;
     ViewGroup mExtractAccessories;
     View mExtractAction;
@@ -415,6 +407,7 @@
      */
     boolean mShouldClearInsetOfPreviousIme;
 
+    @UnsupportedAppUsage
     final Insets mTmpInsets = new Insets();
     final int[] mTmpLocation = new int[2];
 
@@ -528,7 +521,7 @@
         public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
                 @NonNull EditorInfo editorInfo, boolean restarting,
                 @NonNull IBinder startInputToken) {
-            mStartInputToken = startInputToken;
+            mImm.reportStartInput(mToken, startInputToken);
 
             // This needs to be dispatched to interface methods rather than doStartInput().
             // Otherwise IME developers who have overridden those interface methods will lose
@@ -579,8 +572,8 @@
             }
             clearInsetOfPreviousIme();
             // If user uses hard keyboard, IME button should always be shown.
-            mImm.setImeWindowStatus(mToken, mStartInputToken,
-                    mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
+            mImm.setImeWindowStatus(mToken, mapToImeWindowStatus(isInputViewShown()),
+                    mBackDisposition);
             if (resultReceiver != null) {
                 resultReceiver.send(wasVis != isInputViewShown()
                         ? InputMethodManager.RESULT_SHOWN
@@ -824,6 +817,7 @@
             mService.getContentResolver().unregisterContentObserver(this);
         }
 
+        @UnsupportedAppUsage
         private boolean shouldShowImeWithHardKeyboard() {
             // Lazily initialize as needed.
             if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) {
@@ -863,6 +857,7 @@
             return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard  + "}";
         }
     }
+    @UnsupportedAppUsage
     private SettingsObserver mSettingsObserver;
 
     /**
@@ -1059,8 +1054,8 @@
             }
             // If user uses hard keyboard, IME button should always be shown.
             boolean showing = onEvaluateInputViewShown();
-            mImm.setImeWindowStatus(mToken, mStartInputToken,
-                    IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
+            mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
+                    mBackDisposition);
         }
     }
 
@@ -1110,8 +1105,7 @@
             return;
         }
         mBackDisposition = disposition;
-        mImm.setImeWindowStatus(mToken, mStartInputToken, mapToImeWindowStatus(isInputViewShown()),
-                mBackDisposition);
+        mImm.setImeWindowStatus(mToken, mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
     }
 
     /**
@@ -1861,8 +1855,7 @@
 
         final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown());
         if (previousImeWindowStatus != nextImeWindowStatus) {
-            mImm.setImeWindowStatus(mToken, mStartInputToken, nextImeWindowStatus,
-                    mBackDisposition);
+            mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition);
         }
         if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
             if (DEBUG) Log.v(TAG, "showWindow: showing!");
@@ -1887,7 +1880,7 @@
     }
 
     private void doHideWindow() {
-        mImm.setImeWindowStatus(mToken, mStartInputToken, 0, mBackDisposition);
+        mImm.setImeWindowStatus(mToken, 0, mBackDisposition);
         hideWindow();
     }
 
@@ -2507,6 +2500,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void onExtractedDeleteText(int start, int end) {
         InputConnection conn = getCurrentInputConnection();
         if (conn != null) {
@@ -2519,6 +2513,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void onExtractedReplaceText(int start, int end, CharSequence text) {
         InputConnection conn = getCurrentInputConnection();
         if (conn != null) {
@@ -2530,6 +2525,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void onExtractedSetSpan(Object span, int start, int end, int flags) {
         InputConnection conn = getCurrentInputConnection();
         if (conn != null) {
@@ -2869,7 +2865,6 @@
         p.println("  mInputStarted=" + mInputStarted
                 + " mInputViewStarted=" + mInputViewStarted
                 + " mCandidatesViewStarted=" + mCandidatesViewStarted);
-        p.println("  mStartInputToken=" + mStartInputToken);
 
         if (mInputEditorInfo != null) {
             p.println("  mInputEditorInfo:");
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
index a5490ef..ec5f050 100644
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -18,6 +18,7 @@
 
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.annotation.UnsupportedAppUsage;
 import android.annotation.XmlRes;
 import android.content.Context;
 import android.content.res.Resources;
@@ -110,18 +111,21 @@
     private int mKeyHeight;
     
     /** Total height of the keyboard, including the padding and keys */
+    @UnsupportedAppUsage
     private int mTotalHeight;
     
     /** 
      * Total width of the keyboard, including left side gaps and keys, but not any gaps on the
      * right side.
      */
+    @UnsupportedAppUsage
     private int mTotalWidth;
     
     /** List of keys in this keyboard */
     private List<Key> mKeys;
     
     /** List of modifier keys such as Shift & Alt, if any */
+    @UnsupportedAppUsage
     private List<Key> mModifierKeys;
     
     /** Width of the screen available to fit the keyboard */
@@ -623,6 +627,7 @@
         rows.add(row);
     }
 
+    @UnsupportedAppUsage
     final void resize(int newWidth, int newHeight) {
         int numRows = rows.size();
         for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) {
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 16c1f6d..9ca8049 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -16,6 +16,7 @@
 
 package android.inputmethodservice;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
@@ -133,6 +134,7 @@
 
     private Keyboard mKeyboard;
     private int mCurrentKeyIndex = NOT_A_KEY;
+    @UnsupportedAppUsage
     private int mLabelTextSize;
     private int mKeyTextSize;
     private int mKeyTextColor;
@@ -140,6 +142,7 @@
     private int mShadowColor;
     private float mBackgroundDimAmount;
 
+    @UnsupportedAppUsage
     private TextView mPreviewText;
     private PopupWindow mPreviewPopup;
     private int mPreviewTextSizeLarge;
@@ -217,6 +220,7 @@
     private float mOldPointerX;
     private float mOldPointerY;
 
+    @UnsupportedAppUsage
     private Drawable mKeyBackground;
 
     private static final int REPEAT_INTERVAL = 50; // ~20 keys per second
@@ -910,6 +914,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     private void showKey(final int keyIndex) {
         final PopupWindow previewPopup = mPreviewPopup;
         final Key[] keys = mKeys;
@@ -1052,6 +1057,7 @@
                 key.x + key.width + mPaddingLeft, key.y + key.height + mPaddingTop);
     }
 
+    @UnsupportedAppUsage
     private boolean openPopupIfRequired(MotionEvent me) {
         // Check if we have a popup layout specified first.
         if (mPopupLayout == 0) {
@@ -1357,6 +1363,7 @@
         return true;
     }
 
+    @UnsupportedAppUsage
     private boolean repeatKey() {
         Key key = mKeys[mRepeatKeyIndex];
         detectAndSendKey(mCurrentKey, key.x, key.y, mLastTapTime);
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index ab29d99..bd2db92 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -32,6 +32,7 @@
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Objects;
+import java.util.StringJoiner;
 
 /**
  * Describes the properties of a network link.
@@ -48,13 +49,13 @@
 public final class LinkProperties implements Parcelable {
     // The interface described by the network link.
     private String mIfaceName;
-    private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
-    private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>();
-    private ArrayList<InetAddress> mValidatedPrivateDnses = new ArrayList<InetAddress>();
+    private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<>();
+    private ArrayList<InetAddress> mDnses = new ArrayList<>();
+    private ArrayList<InetAddress> mValidatedPrivateDnses = new ArrayList<>();
     private boolean mUsePrivateDns;
     private String mPrivateDnsServerName;
     private String mDomains;
-    private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
+    private ArrayList<RouteInfo> mRoutes = new ArrayList<>();
     private ProxyInfo mHttpProxy;
     private int mMtu;
     // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
@@ -66,15 +67,14 @@
 
     // Stores the properties of links that are "stacked" above this link.
     // Indexed by interface name to allow modification and to prevent duplicates being added.
-    private Hashtable<String, LinkProperties> mStackedLinks =
-        new Hashtable<String, LinkProperties>();
+    private Hashtable<String, LinkProperties> mStackedLinks = new Hashtable<>();
 
     /**
      * @hide
      */
     public static class CompareResult<T> {
-        public final List<T> removed = new ArrayList<T>();
-        public final List<T> added = new ArrayList<T>();
+        public final List<T> removed = new ArrayList<>();
+        public final List<T> added = new ArrayList<>();
 
         public CompareResult() {}
 
@@ -93,12 +93,9 @@
 
         @Override
         public String toString() {
-            String retVal = "removed=[";
-            for (T addr : removed) retVal += addr.toString() + ",";
-            retVal += "] added=[";
-            for (T addr : added) retVal += addr.toString() + ",";
-            retVal += "]";
-            return retVal;
+            return "removed=[" + TextUtils.join(",", removed)
+                    + "] added=[" + TextUtils.join(",", added)
+                    + "]";
         }
     }
 
@@ -120,7 +117,7 @@
     public static ProvisioningChange compareProvisioning(
             LinkProperties before, LinkProperties after) {
         if (before.isProvisioned() && after.isProvisioned()) {
-            // On dualstack networks, DHCPv4 renewals can occasionally fail.
+            // On dual-stack networks, DHCPv4 renewals can occasionally fail.
             // When this happens, IPv6-reachable services continue to function
             // normally but IPv4-only services (naturally) fail.
             //
@@ -131,7 +128,7 @@
             //
             // For users, this is confusing and unexpected behaviour, and is
             // not necessarily easy to diagnose.  Therefore, we treat changing
-            // from a dualstack network to an IPv6-only network equivalent to
+            // from a dual-stack network to an IPv6-only network equivalent to
             // a total loss of provisioning.
             //
             // For one such example of this, see b/18867306.
@@ -139,7 +136,7 @@
             // Additionally, losing IPv6 provisioning can result in TCP
             // connections getting stuck until timeouts fire and other
             // baffling failures. Therefore, loss of either IPv4 or IPv6 on a
-            // previously dualstack network is deemed a lost of provisioning.
+            // previously dual-stack network is deemed a lost of provisioning.
             if ((before.isIPv4Provisioned() && !after.isIPv4Provisioned()) ||
                 (before.isIPv6Provisioned() && !after.isIPv6Provisioned())) {
                 return ProvisioningChange.LOST_PROVISIONING;
@@ -165,22 +162,19 @@
      */
     public LinkProperties(LinkProperties source) {
         if (source != null) {
-            mIfaceName = source.getInterfaceName();
-            for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
-            for (InetAddress i : source.getDnsServers()) mDnses.add(i);
-            for (InetAddress i : source.getValidatedPrivateDnsServers()) {
-                mValidatedPrivateDnses.add(i);
-            }
+            mIfaceName = source.mIfaceName;
+            mLinkAddresses.addAll(source.mLinkAddresses);
+            mDnses.addAll(source.mDnses);
+            mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses);
             mUsePrivateDns = source.mUsePrivateDns;
             mPrivateDnsServerName = source.mPrivateDnsServerName;
-            mDomains = source.getDomains();
-            for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
-            mHttpProxy = (source.getHttpProxy() == null)  ?
-                    null : new ProxyInfo(source.getHttpProxy());
+            mDomains = source.mDomains;
+            mRoutes.addAll(source.mRoutes);
+            mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy);
             for (LinkProperties l: source.mStackedLinks.values()) {
                 addStackedLink(l);
             }
-            setMtu(source.getMtu());
+            setMtu(source.mMtu);
             mTcpBufferSizes = source.mTcpBufferSizes;
         }
     }
@@ -194,7 +188,7 @@
      */
     public void setInterfaceName(String iface) {
         mIfaceName = iface;
-        ArrayList<RouteInfo> newRoutes = new ArrayList<RouteInfo>(mRoutes.size());
+        ArrayList<RouteInfo> newRoutes = new ArrayList<>(mRoutes.size());
         for (RouteInfo route : mRoutes) {
             newRoutes.add(routeWithInterface(route));
         }
@@ -214,8 +208,8 @@
      * @hide
      */
     public List<String> getAllInterfaceNames() {
-        List<String> interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1);
-        if (mIfaceName != null) interfaceNames.add(new String(mIfaceName));
+        List<String> interfaceNames = new ArrayList<>(mStackedLinks.size() + 1);
+        if (mIfaceName != null) interfaceNames.add(mIfaceName);
         for (LinkProperties stacked: mStackedLinks.values()) {
             interfaceNames.addAll(stacked.getAllInterfaceNames());
         }
@@ -229,11 +223,11 @@
      * prefix lengths for each address.  This is a simplified utility alternative to
      * {@link LinkProperties#getLinkAddresses}.
      *
-     * @return An umodifiable {@link List} of {@link InetAddress} for this link.
+     * @return An unmodifiable {@link List} of {@link InetAddress} for this link.
      * @hide
      */
     public List<InetAddress> getAddresses() {
-        List<InetAddress> addresses = new ArrayList<InetAddress>();
+        List<InetAddress> addresses = new ArrayList<>();
         for (LinkAddress linkAddress : mLinkAddresses) {
             addresses.add(linkAddress.getAddress());
         }
@@ -245,7 +239,7 @@
      * @hide
      */
     public List<InetAddress> getAllAddresses() {
-        List<InetAddress> addresses = new ArrayList<InetAddress>();
+        List<InetAddress> addresses = new ArrayList<>();
         for (LinkAddress linkAddress : mLinkAddresses) {
             addresses.add(linkAddress.getAddress());
         }
@@ -322,8 +316,7 @@
      * @hide
      */
     public List<LinkAddress> getAllLinkAddresses() {
-        List<LinkAddress> addresses = new ArrayList<LinkAddress>();
-        addresses.addAll(mLinkAddresses);
+        List<LinkAddress> addresses = new ArrayList<>(mLinkAddresses);
         for (LinkProperties stacked: mStackedLinks.values()) {
             addresses.addAll(stacked.getAllLinkAddresses());
         }
@@ -391,7 +384,7 @@
     /**
      * Returns all the {@link InetAddress} for DNS servers on this link.
      *
-     * @return An umodifiable {@link List} of {@link InetAddress} for DNS servers on
+     * @return An unmodifiable {@link List} of {@link InetAddress} for DNS servers on
      *         this link.
      */
     public List<InetAddress> getDnsServers() {
@@ -643,7 +636,7 @@
      * @hide
      */
     public void ensureDirectlyConnectedRoutes() {
-        for (LinkAddress addr: mLinkAddresses) {
+        for (LinkAddress addr : mLinkAddresses) {
             addRoute(new RouteInfo(addr, null, mIfaceName));
         }
     }
@@ -653,8 +646,7 @@
      * @hide
      */
     public List<RouteInfo> getAllRoutes() {
-        List<RouteInfo> routes = new ArrayList<>();
-        routes.addAll(mRoutes);
+        List<RouteInfo> routes = new ArrayList<>(mRoutes);
         for (LinkProperties stacked: mStackedLinks.values()) {
             routes.addAll(stacked.getAllRoutes());
         }
@@ -685,7 +677,7 @@
     /**
      * Adds a stacked link.
      *
-     * If there is already a stacked link with the same interfacename as link,
+     * If there is already a stacked link with the same interface name as link,
      * that link is replaced with link. Otherwise, link is added to the list
      * of stacked links. If link is null, nothing changes.
      *
@@ -725,9 +717,9 @@
      */
     public @NonNull List<LinkProperties> getStackedLinks() {
         if (mStackedLinks.isEmpty()) {
-            return Collections.EMPTY_LIST;
+            return Collections.emptyList();
         }
-        List<LinkProperties> stacked = new ArrayList<LinkProperties>();
+        List<LinkProperties> stacked = new ArrayList<>();
         for (LinkProperties link : mStackedLinks.values()) {
             stacked.add(new LinkProperties(link));
         }
@@ -761,57 +753,76 @@
 
     @Override
     public String toString() {
-        String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " ");
+        // Space as a separator, so no need for spaces at start/end of the individual fragments.
+        final StringJoiner resultJoiner = new StringJoiner(" ", "{", "}");
 
-        String linkAddresses = "LinkAddresses: [";
-        for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ",";
-        linkAddresses += "] ";
+        if (mIfaceName != null) {
+            resultJoiner.add("InterfaceName:");
+            resultJoiner.add(mIfaceName);
+        }
 
-        String dns = "DnsAddresses: [";
-        for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
-        dns += "] ";
+        resultJoiner.add("LinkAddresses: [");
+        if (!mLinkAddresses.isEmpty()) {
+            resultJoiner.add(TextUtils.join(",", mLinkAddresses));
+        }
+        resultJoiner.add("]");
 
-        String usePrivateDns = "UsePrivateDns: " + mUsePrivateDns + " ";
+        resultJoiner.add("DnsAddresses: [");
+        if (!mDnses.isEmpty()) {
+            resultJoiner.add(TextUtils.join(",", mDnses));
+        }
+        resultJoiner.add("]");
 
-        String privateDnsServerName = "";
+        if (mUsePrivateDns) {
+            resultJoiner.add("UsePrivateDns: true");
+        }
+
         if (mPrivateDnsServerName != null) {
-            privateDnsServerName = "PrivateDnsServerName: " + mPrivateDnsServerName + " ";
+            resultJoiner.add("PrivateDnsServerName:");
+            resultJoiner.add(mPrivateDnsServerName);
         }
 
-        String validatedPrivateDns = "";
         if (!mValidatedPrivateDnses.isEmpty()) {
-            validatedPrivateDns = "ValidatedPrivateDnsAddresses: [";
-            for (InetAddress addr : mValidatedPrivateDnses) {
-                validatedPrivateDns += addr.getHostAddress() + ",";
+            final StringJoiner validatedPrivateDnsesJoiner =
+                    new StringJoiner(",", "ValidatedPrivateDnsAddresses: [", "]");
+            for (final InetAddress addr : mValidatedPrivateDnses) {
+                validatedPrivateDnsesJoiner.add(addr.getHostAddress());
             }
-            validatedPrivateDns += "] ";
+            resultJoiner.add(validatedPrivateDnsesJoiner.toString());
         }
 
-        String domainName = "Domains: " + mDomains;
+        resultJoiner.add("Domains:");
+        resultJoiner.add(mDomains);
 
-        String mtu = " MTU: " + mMtu;
+        resultJoiner.add("MTU:");
+        resultJoiner.add(Integer.toString(mMtu));
 
-        String tcpBuffSizes = "";
         if (mTcpBufferSizes != null) {
-            tcpBuffSizes = " TcpBufferSizes: " + mTcpBufferSizes;
+            resultJoiner.add("TcpBufferSizes:");
+            resultJoiner.add(mTcpBufferSizes);
         }
 
-        String routes = " Routes: [";
-        for (RouteInfo route : mRoutes) routes += route.toString() + ",";
-        routes += "] ";
-        String proxy = (mHttpProxy == null ? "" : " HttpProxy: " + mHttpProxy.toString() + " ");
+        resultJoiner.add("Routes: [");
+        if (!mRoutes.isEmpty()) {
+            resultJoiner.add(TextUtils.join(",", mRoutes));
+        }
+        resultJoiner.add("]");
 
-        String stacked = "";
-        if (mStackedLinks.values().size() > 0) {
-            stacked += " Stacked: [";
-            for (LinkProperties link: mStackedLinks.values()) {
-                stacked += " [" + link.toString() + " ],";
+        if (mHttpProxy != null) {
+            resultJoiner.add("HttpProxy:");
+            resultJoiner.add(mHttpProxy.toString());
+        }
+
+        final Collection<LinkProperties> stackedLinksValues = mStackedLinks.values();
+        if (!stackedLinksValues.isEmpty()) {
+            final StringJoiner stackedLinksJoiner = new StringJoiner(",", "Stacked: [", "]");
+            for (final LinkProperties lp : stackedLinksValues) {
+                stackedLinksJoiner.add("[ " + lp + " ]");
             }
-            stacked += "] ";
+            resultJoiner.add(stackedLinksJoiner.toString());
         }
-        return "{" + ifaceName + linkAddresses + routes + dns + usePrivateDns
-            + privateDnsServerName + validatedPrivateDns + domainName + mtu + tcpBuffSizes + proxy
-            + stacked + "}";
+
+        return resultJoiner.toString();
     }
 
     /**
@@ -1028,7 +1039,7 @@
         if (mDomains == null) {
             if (targetDomains != null) return false;
         } else {
-            if (mDomains.equals(targetDomains) == false) return false;
+            if (!mDomains.equals(targetDomains)) return false;
         }
         return (mDnses.size() == targetDnses.size()) ?
                 mDnses.containsAll(targetDnses) : false;
@@ -1130,7 +1141,6 @@
         return Objects.equals(mTcpBufferSizes, target.mTcpBufferSizes);
     }
 
-    @Override
     /**
      * Compares this {@code LinkProperties} instance against the target
      * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
@@ -1145,13 +1155,14 @@
      * @param obj the object to be tested for equality.
      * @return {@code true} if both objects are equal, {@code false} otherwise.
      */
+    @Override
     public boolean equals(Object obj) {
         if (this == obj) return true;
 
         if (!(obj instanceof LinkProperties)) return false;
 
         LinkProperties target = (LinkProperties) obj;
-        /**
+        /*
          * This method does not check that stacked interfaces are equal, because
          * stacked interfaces are not so much a property of the link as a
          * description of connections between links.
@@ -1258,12 +1269,13 @@
     }
 
 
-    @Override
     /**
-     * generate hashcode based on significant fields
+     * Generate hashcode based on significant fields
+     *
      * Equal objects must produce the same hash code, while unequal objects
      * may have the same hash codes.
      */
+    @Override
     public int hashCode() {
         return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
                 + mLinkAddresses.size() * 31
@@ -1313,7 +1325,7 @@
         } else {
             dest.writeByte((byte)0);
         }
-        ArrayList<LinkProperties> stackedLinks = new ArrayList(mStackedLinks.values());
+        ArrayList<LinkProperties> stackedLinks = new ArrayList<>(mStackedLinks.values());
         dest.writeList(stackedLinks);
     }
 
@@ -1331,7 +1343,7 @@
                 }
                 int addressCount = in.readInt();
                 for (int i = 0; i < addressCount; i++) {
-                    netProp.addLinkAddress((LinkAddress) in.readParcelable(null));
+                    netProp.addLinkAddress(in.readParcelable(null));
                 }
                 addressCount = in.readInt();
                 for (int i = 0; i < addressCount; i++) {
@@ -1353,10 +1365,10 @@
                 netProp.setTcpBufferSizes(in.readString());
                 addressCount = in.readInt();
                 for (int i = 0; i < addressCount; i++) {
-                    netProp.addRoute((RouteInfo) in.readParcelable(null));
+                    netProp.addRoute(in.readParcelable(null));
                 }
                 if (in.readByte() == 1) {
-                    netProp.setHttpProxy((ProxyInfo) in.readParcelable(null));
+                    netProp.setHttpProxy(in.readParcelable(null));
                 }
                 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
                 in.readList(stackedLinks, LinkProperties.class.getClassLoader());
@@ -1377,10 +1389,9 @@
      */
     public static boolean isValidMtu(int mtu, boolean ipv6) {
         if (ipv6) {
-            if (mtu >= MIN_MTU_V6 && mtu <= MAX_MTU) return true;
+            return mtu >= MIN_MTU_V6 && mtu <= MAX_MTU;
         } else {
-            if (mtu >= MIN_MTU && mtu <= MAX_MTU) return true;
+            return mtu >= MIN_MTU && mtu <= MAX_MTU;
         }
-        return false;
     }
 }
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index d75d439..512e35e 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -142,7 +142,7 @@
 
     /**
      * Specify whether or not Private DNS should be bypassed when attempting
-     * to use {@link getAllByName()}/{@link getByName()} methods on the given
+     * to use {@link #getAllByName(String)}/{@link #getByName(String)} methods on the given
      * instance for hostname resolution.
      *
      * @hide
@@ -169,13 +169,6 @@
      * A {@code SocketFactory} that produces {@code Socket}'s bound to this network.
      */
     private class NetworkBoundSocketFactory extends SocketFactory {
-        private final int mNetId;
-
-        public NetworkBoundSocketFactory(int netId) {
-            super();
-            mNetId = netId;
-        }
-
         private Socket connectToHost(String host, int port, SocketAddress localAddress)
                 throws IOException {
             // Lookup addresses only on this Network.
@@ -201,7 +194,8 @@
         }
 
         @Override
-        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
+        public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
+                throws IOException {
             return connectToHost(host, port, new InetSocketAddress(localHost, localPort));
         }
 
@@ -265,7 +259,7 @@
         if (mNetworkBoundSocketFactory == null) {
             synchronized (mLock) {
                 if (mNetworkBoundSocketFactory == null) {
-                    mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(netId);
+                    mNetworkBoundSocketFactory = new NetworkBoundSocketFactory();
                 }
             }
         }
@@ -475,7 +469,7 @@
 
     @Override
     public boolean equals(Object obj) {
-        if (obj instanceof Network == false) return false;
+        if (!(obj instanceof Network)) return false;
         Network other = (Network)obj;
         return this.netId == other.netId;
     }
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 9a5d502..dc1f805 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -126,51 +126,122 @@
     public native static boolean queryUserAccess(int uid, int netId);
 
     /**
-     * Convert a IPv4 address from an integer to an InetAddress.
-     * @param hostAddress an int corresponding to the IPv4 address in network byte order
+     * @see #intToInet4AddressHTL(int)
+     * @deprecated Use either {@link #intToInet4AddressHTH(int)}
+     *             or {@link #intToInet4AddressHTL(int)}
      */
+    @Deprecated
     public static InetAddress intToInetAddress(int hostAddress) {
-        byte[] addressBytes = { (byte)(0xff & hostAddress),
-                                (byte)(0xff & (hostAddress >> 8)),
-                                (byte)(0xff & (hostAddress >> 16)),
-                                (byte)(0xff & (hostAddress >> 24)) };
+        return intToInet4AddressHTL(hostAddress);
+    }
+
+    /**
+     * Convert a IPv4 address from an integer to an InetAddress (0x04030201 -> 1.2.3.4)
+     *
+     * <p>This method uses the higher-order int bytes as the lower-order IPv4 address bytes,
+     * which is an unusual convention. Consider {@link #intToInet4AddressHTH(int)} instead.
+     * @param hostAddress an int coding for an IPv4 address, where higher-order int byte is
+     *                    lower-order IPv4 address byte
+     */
+    public static InetAddress intToInet4AddressHTL(int hostAddress) {
+        return intToInet4AddressHTH(Integer.reverseBytes(hostAddress));
+    }
+
+    /**
+     * Convert a IPv4 address from an integer to an InetAddress (0x01020304 -> 1.2.3.4)
+     * @param hostAddress an int coding for an IPv4 address
+     */
+    public static InetAddress intToInet4AddressHTH(int hostAddress) {
+        byte[] addressBytes = { (byte) (0xff & (hostAddress >> 24)),
+                (byte) (0xff & (hostAddress >> 16)),
+                (byte) (0xff & (hostAddress >> 8)),
+                (byte) (0xff & hostAddress) };
 
         try {
-           return InetAddress.getByAddress(addressBytes);
+            return InetAddress.getByAddress(addressBytes);
         } catch (UnknownHostException e) {
-           throw new AssertionError();
+            throw new AssertionError();
         }
     }
 
     /**
-     * Convert a IPv4 address from an InetAddress to an integer
-     * @param inetAddr is an InetAddress corresponding to the IPv4 address
-     * @return the IP address as an integer in network byte order
+     * @see #inet4AddressToIntHTL(Inet4Address)
+     * @deprecated Use either {@link #inet4AddressToIntHTH(Inet4Address)}
+     *             or {@link #inet4AddressToIntHTL(Inet4Address)}
      */
+    @Deprecated
     public static int inetAddressToInt(Inet4Address inetAddr)
             throws IllegalArgumentException {
-        byte [] addr = inetAddr.getAddress();
-        return ((addr[3] & 0xff) << 24) | ((addr[2] & 0xff) << 16) |
-                ((addr[1] & 0xff) << 8) | (addr[0] & 0xff);
+        return inet4AddressToIntHTL(inetAddr);
     }
 
     /**
-     * Convert a network prefix length to an IPv4 netmask integer
-     * @param prefixLength
-     * @return the IPv4 netmask as an integer in network byte order
+     * Convert an IPv4 address from an InetAddress to an integer (1.2.3.4 -> 0x01020304)
+     *
+     * <p>This conversion can help order IP addresses: considering the ordering
+     * 192.0.2.1 < 192.0.2.2 < ..., resulting ints will follow that ordering if read as unsigned
+     * integers with {@link Integer#toUnsignedLong}.
+     * @param inetAddr is an InetAddress corresponding to the IPv4 address
+     * @return the IP address as integer
      */
+    public static int inet4AddressToIntHTH(Inet4Address inetAddr)
+            throws IllegalArgumentException {
+        byte [] addr = inetAddr.getAddress();
+        return ((addr[0] & 0xff) << 24) | ((addr[1] & 0xff) << 16)
+                | ((addr[2] & 0xff) << 8) | (addr[3] & 0xff);
+    }
+
+    /**
+     * Convert a IPv4 address from an InetAddress to an integer (1.2.3.4 -> 0x04030201)
+     *
+     * <p>This method stores the higher-order IPv4 address bytes in the lower-order int bytes,
+     * which is an unusual convention. Consider {@link #inet4AddressToIntHTH(Inet4Address)} instead.
+     * @param inetAddr is an InetAddress corresponding to the IPv4 address
+     * @return the IP address as integer
+     */
+    public static int inet4AddressToIntHTL(Inet4Address inetAddr) {
+        return Integer.reverseBytes(inet4AddressToIntHTH(inetAddr));
+    }
+
+    /**
+     * @see #prefixLengthToV4NetmaskIntHTL(int)
+     * @deprecated Use either {@link #prefixLengthToV4NetmaskIntHTH(int)}
+     *             or {@link #prefixLengthToV4NetmaskIntHTL(int)}
+     */
+    @Deprecated
     public static int prefixLengthToNetmaskInt(int prefixLength)
             throws IllegalArgumentException {
+        return prefixLengthToV4NetmaskIntHTL(prefixLength);
+    }
+
+    /**
+     * Convert a network prefix length to an IPv4 netmask integer (prefixLength 17 -> 0xffff8000)
+     * @return the IPv4 netmask as an integer
+     */
+    public static int prefixLengthToV4NetmaskIntHTH(int prefixLength)
+            throws IllegalArgumentException {
         if (prefixLength < 0 || prefixLength > 32) {
             throw new IllegalArgumentException("Invalid prefix length (0 <= prefix <= 32)");
         }
-        int value = 0xffffffff << (32 - prefixLength);
-        return Integer.reverseBytes(value);
+        // (int)a << b is equivalent to a << (b & 0x1f): can't shift by 32 (-1 << 32 == -1)
+        return prefixLength == 0 ? 0 : 0xffffffff << (32 - prefixLength);
+    }
+
+    /**
+     * Convert a network prefix length to an IPv4 netmask integer (prefixLength 17 -> 0x0080ffff).
+     *
+     * <p>This method stores the higher-order IPv4 address bytes in the lower-order int bytes,
+     * which is an unusual convention. Consider {@link #prefixLengthToV4NetmaskIntHTH(int)} instead.
+     * @return the IPv4 netmask as an integer
+     */
+    public static int prefixLengthToV4NetmaskIntHTL(int prefixLength)
+            throws IllegalArgumentException {
+        return Integer.reverseBytes(prefixLengthToV4NetmaskIntHTH(prefixLength));
     }
 
     /**
      * Convert a IPv4 netmask integer to a prefix length
-     * @param netmask as an integer in network byte order
+     * @param netmask as an integer (0xff000000 for a /8 subnet)
      * @return the network prefix length
      */
     public static int netmaskIntToPrefixLength(int netmask) {
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index 3cd37bf..fee91fb 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -1,8 +1,20 @@
 set noparent
 
+codewiz@google.com
 ek@google.com
 jsharkey@android.com
 jchalard@google.com
 lorenzo@google.com
+reminv@google.com
 satk@google.com
 silberst@google.com
+
+per-file SSL*=flooey@google.com
+per-file SSL*=narayan@google.com
+per-file SSL*=tobiast@google.com
+per-file Uri*=flooey@google.com
+per-file Uri*=narayan@google.com
+per-file Uri*=tobiast@google.com
+per-file Url*=flooey@google.com
+per-file Url*=narayan@google.com
+per-file Url*=tobiast@google.com
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 1f53587..d7eb477 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -73,10 +73,14 @@
  * Updating Your Security Provider to Protect Against SSL Exploits</a>
  * for further information.</p>
  *
- * <p>One way to verify the server's identity is to use
+ * <p>The recommended way to verify the server's identity is to use
  * {@link HttpsURLConnection#getDefaultHostnameVerifier()} to get a
  * {@link HostnameVerifier} to verify the certificate hostname.
  *
+ * <p><b>Warning</b>: Some methods on this class return connected sockets and some return
+ * unconnected sockets.  For the methods that return connected sockets, setting
+ * connection- or handshake-related properties on those sockets will have no effect.
+ *
  * <p>On development devices, "setprop socket.relaxsslcheck yes" bypasses all
  * SSL certificate and hostname checks for testing purposes.  This setting
  * requires root access.
@@ -442,8 +446,10 @@
     /**
      * {@inheritDoc}
      *
-     * <p>This method verifies the peer's certificate hostname after connecting
-     * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
+     * <p>By default, this method returns a <i>connected</i> socket and verifies the peer's
+     * certificate hostname after connecting; if this instance was created with
+     * {@link #getInsecure(int, SSLSessionCache)}, it returns a socket that is <i>not connected</i>
+     * instead.
      */
     @Override
     public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException {
@@ -459,7 +465,7 @@
     }
 
     /**
-     * Creates a new socket which is not connected to any remote host.
+     * Creates a new socket which is <i>not connected</i> to any remote host.
      * You must use {@link Socket#connect} to connect the socket.
      *
      * <p class="caution"><b>Warning:</b> Hostname verification is not performed
@@ -479,6 +485,8 @@
     /**
      * {@inheritDoc}
      *
+     * <p>This method returns a socket that is <i>not connected</i>.
+     *
      * <p class="caution"><b>Warning:</b> Hostname verification is not performed
      * with this method.  You MUST verify the server's identity after connecting
      * the socket to avoid man-in-the-middle attacks.</p>
@@ -498,6 +506,8 @@
     /**
      * {@inheritDoc}
      *
+     * <p>This method returns a socket that is <i>not connected</i>.
+     *
      * <p class="caution"><b>Warning:</b> Hostname verification is not performed
      * with this method.  You MUST verify the server's identity after connecting
      * the socket to avoid man-in-the-middle attacks.</p>
@@ -515,8 +525,10 @@
     /**
      * {@inheritDoc}
      *
-     * <p>This method verifies the peer's certificate hostname after connecting
-     * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
+     * <p>By default, this method returns a <i>connected</i> socket and verifies the peer's
+     * certificate hostname after connecting; if this instance was created with
+     * {@link #getInsecure(int, SSLSessionCache)}, it returns a socket that is <i>not connected</i>
+     * instead.
      */
     @Override
     public Socket createSocket(String host, int port, InetAddress localAddr, int localPort)
@@ -536,8 +548,10 @@
     /**
      * {@inheritDoc}
      *
-     * <p>This method verifies the peer's certificate hostname after connecting
-     * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
+     * <p>By default, this method returns a <i>connected</i> socket and verifies the peer's
+     * certificate hostname after connecting; if this instance was created with
+     * {@link #getInsecure(int, SSLSessionCache)}, it returns a socket that is <i>not connected</i>
+     * instead.
      */
     @Override
     public Socket createSocket(String host, int port) throws IOException {
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 40d53b7..f033268 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -329,6 +329,14 @@
 
     /**
      * Remove any statistics parameters from the given {@link Socket}.
+     * <p>
+     * In Android 8.1 (API level 27) and lower, a socket is automatically
+     * untagged when it's sent to another process using binder IPC with a
+     * {@code ParcelFileDescriptor} container. In Android 9.0 (API level 28)
+     * and higher, the socket tag is kept when the socket is sent to another
+     * process using binder IPC. You can mimic the previous behavior by
+     * calling {@code untagSocket()} before sending the socket to another
+     * process.
      */
     public static void untagSocket(Socket socket) throws SocketException {
         SocketTagger.get().untag(socket);
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 0fb84b7..b7f5cdf 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -24,8 +24,6 @@
 import android.os.StrictMode;
 import android.util.Log;
 
-import libcore.net.UriCodec;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
@@ -1959,7 +1957,8 @@
         if (s == null) {
             return null;
         }
-        return UriCodec.decode(s, false, StandardCharsets.UTF_8, false);
+        return UriCodec.decode(
+                s, false /* convertPlus */, StandardCharsets.UTF_8, false /* throwOnFailure */);
     }
 
     /**
diff --git a/core/java/android/net/UriCodec.java b/core/java/android/net/UriCodec.java
new file mode 100644
index 0000000..e1470e0
--- /dev/null
+++ b/core/java/android/net/UriCodec.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import java.net.URISyntaxException;
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * Decodes “application/x-www-form-urlencoded” content.
+ *
+ * @hide
+ */
+public final class UriCodec {
+
+    private UriCodec() {}
+
+    /**
+     * Interprets a char as hex digits, returning a number from -1 (invalid char) to 15 ('f').
+     */
+    private static int hexCharToValue(char c) {
+        if ('0' <= c && c <= '9') {
+            return c - '0';
+        }
+        if ('a' <= c && c <= 'f') {
+            return 10 + c - 'a';
+        }
+        if ('A' <= c && c <= 'F') {
+            return 10 + c - 'A';
+        }
+        return -1;
+    }
+
+    private static URISyntaxException unexpectedCharacterException(
+            String uri, String name, char unexpected, int index) {
+        String nameString = (name == null) ? "" :  " in [" + name + "]";
+        return new URISyntaxException(
+                uri, "Unexpected character" + nameString + ": " + unexpected, index);
+    }
+
+    private static char getNextCharacter(String uri, int index, int end, String name)
+             throws URISyntaxException {
+        if (index >= end) {
+            String nameString = (name == null) ? "" :  " in [" + name + "]";
+            throw new URISyntaxException(
+                    uri, "Unexpected end of string" + nameString, index);
+        }
+        return uri.charAt(index);
+    }
+
+    /**
+     * Decode a string according to the rules of this decoder.
+     *
+     * - if {@code convertPlus == true} all ‘+’ chars in the decoded output are converted to ‘ ‘
+     *   (white space)
+     * - if {@code throwOnFailure == true}, an {@link IllegalArgumentException} is thrown for
+     *   invalid inputs. Else, U+FFFd is emitted to the output in place of invalid input octets.
+     */
+    public static String decode(
+            String s, boolean convertPlus, Charset charset, boolean throwOnFailure) {
+        StringBuilder builder = new StringBuilder(s.length());
+        appendDecoded(builder, s, convertPlus, charset, throwOnFailure);
+        return builder.toString();
+    }
+
+    /**
+     * Character to be output when there's an error decoding an input.
+     */
+    private static final char INVALID_INPUT_CHARACTER = '\ufffd';
+
+    private static void appendDecoded(
+            StringBuilder builder,
+            String s,
+            boolean convertPlus,
+            Charset charset,
+            boolean throwOnFailure) {
+        CharsetDecoder decoder = charset.newDecoder()
+                .onMalformedInput(CodingErrorAction.REPLACE)
+                .replaceWith("\ufffd")
+                .onUnmappableCharacter(CodingErrorAction.REPORT);
+        // Holds the bytes corresponding to the escaped chars being read (empty if the last char
+        // wasn't a escaped char).
+        ByteBuffer byteBuffer = ByteBuffer.allocate(s.length());
+        int i = 0;
+        while (i < s.length()) {
+            char c = s.charAt(i);
+            i++;
+            switch (c) {
+                case '+':
+                    flushDecodingByteAccumulator(
+                            builder, decoder, byteBuffer, throwOnFailure);
+                    builder.append(convertPlus ? ' ' : '+');
+                    break;
+                case '%':
+                    // Expect two characters representing a number in hex.
+                    byte hexValue = 0;
+                    for (int j = 0; j < 2; j++) {
+                        try {
+                            c = getNextCharacter(s, i, s.length(), null /* name */);
+                        } catch (URISyntaxException e) {
+                            // Unexpected end of input.
+                            if (throwOnFailure) {
+                                throw new IllegalArgumentException(e);
+                            } else {
+                                flushDecodingByteAccumulator(
+                                        builder, decoder, byteBuffer, throwOnFailure);
+                                builder.append(INVALID_INPUT_CHARACTER);
+                                return;
+                            }
+                        }
+                        i++;
+                        int newDigit = hexCharToValue(c);
+                        if (newDigit < 0) {
+                            if (throwOnFailure) {
+                                throw new IllegalArgumentException(
+                                        unexpectedCharacterException(s, null /* name */, c, i - 1));
+                            } else {
+                                flushDecodingByteAccumulator(
+                                        builder, decoder, byteBuffer, throwOnFailure);
+                                builder.append(INVALID_INPUT_CHARACTER);
+                                break;
+                            }
+                        }
+                        hexValue = (byte) (hexValue * 0x10 + newDigit);
+                    }
+                    byteBuffer.put(hexValue);
+                    break;
+                default:
+                    flushDecodingByteAccumulator(builder, decoder, byteBuffer, throwOnFailure);
+                    builder.append(c);
+            }
+        }
+        flushDecodingByteAccumulator(builder, decoder, byteBuffer, throwOnFailure);
+    }
+
+    private static void flushDecodingByteAccumulator(
+            StringBuilder builder,
+            CharsetDecoder decoder,
+            ByteBuffer byteBuffer,
+            boolean throwOnFailure) {
+        if (byteBuffer.position() == 0) {
+            return;
+        }
+        byteBuffer.flip();
+        try {
+            builder.append(decoder.decode(byteBuffer));
+        } catch (CharacterCodingException e) {
+            if (throwOnFailure) {
+                throw new IllegalArgumentException(e);
+            } else {
+                builder.append(INVALID_INPUT_CHARACTER);
+            }
+        } finally {
+            // Use the byte buffer to write again.
+            byteBuffer.flip();
+            byteBuffer.limit(byteBuffer.capacity());
+        }
+    }
+}
diff --git a/core/java/android/net/http/HttpResponseCache.java b/core/java/android/net/http/HttpResponseCache.java
index 729aff0..7da76d1 100644
--- a/core/java/android/net/http/HttpResponseCache.java
+++ b/core/java/android/net/http/HttpResponseCache.java
@@ -16,9 +16,8 @@
 
 package android.net.http;
 
-import com.android.okhttp.Cache;
-import com.android.okhttp.AndroidShimResponseCache;
-import com.android.okhttp.OkCacheContainer;
+import com.android.okhttp.internalandroidapi.AndroidResponseCacheAdapter;
+import com.android.okhttp.internalandroidapi.HasCacheHolder;
 
 import java.io.Closeable;
 import java.io.File;
@@ -149,12 +148,12 @@
  *       } catch (Exception httpResponseCacheNotAvailable) {
  *       }}</pre>
  */
-public final class HttpResponseCache extends ResponseCache implements Closeable, OkCacheContainer {
+public final class HttpResponseCache extends ResponseCache implements HasCacheHolder, Closeable {
 
-    private final AndroidShimResponseCache delegate;
+    private final AndroidResponseCacheAdapter mDelegate;
 
-    private HttpResponseCache(AndroidShimResponseCache delegate) {
-        this.delegate = delegate;
+    private HttpResponseCache(AndroidResponseCacheAdapter delegate) {
+        mDelegate = delegate;
     }
 
     /**
@@ -184,30 +183,33 @@
         ResponseCache installed = ResponseCache.getDefault();
         if (installed instanceof HttpResponseCache) {
             HttpResponseCache installedResponseCache = (HttpResponseCache) installed;
+            CacheHolder cacheHolder = installedResponseCache.getCacheHolder();
             // don't close and reopen if an equivalent cache is already installed
-            AndroidShimResponseCache trueResponseCache = installedResponseCache.delegate;
-            if (trueResponseCache.isEquivalent(directory, maxSize)) {
+            if (cacheHolder.isEquivalent(directory, maxSize)) {
                 return installedResponseCache;
             } else {
                 // The HttpResponseCache that owns this object is about to be replaced.
-                trueResponseCache.close();
+                installedResponseCache.close();
             }
         }
 
-        AndroidShimResponseCache trueResponseCache =
-                AndroidShimResponseCache.create(directory, maxSize);
-        HttpResponseCache newResponseCache = new HttpResponseCache(trueResponseCache);
-        ResponseCache.setDefault(newResponseCache);
-        return newResponseCache;
+        CacheHolder cacheHolder = CacheHolder.create(directory, maxSize);
+        AndroidResponseCacheAdapter androidResponseCacheAdapter =
+                new AndroidResponseCacheAdapter(cacheHolder);
+        HttpResponseCache responseCache = new HttpResponseCache(androidResponseCacheAdapter);
+        ResponseCache.setDefault(responseCache);
+        return responseCache;
     }
 
-    @Override public CacheResponse get(URI uri, String requestMethod,
+    @Override
+    public CacheResponse get(URI uri, String requestMethod,
             Map<String, List<String>> requestHeaders) throws IOException {
-        return delegate.get(uri, requestMethod, requestHeaders);
+        return mDelegate.get(uri, requestMethod, requestHeaders);
     }
 
-    @Override public CacheRequest put(URI uri, URLConnection urlConnection) throws IOException {
-        return delegate.put(uri, urlConnection);
+    @Override
+    public CacheRequest put(URI uri, URLConnection urlConnection) throws IOException {
+        return mDelegate.put(uri, urlConnection);
     }
 
     /**
@@ -217,7 +219,7 @@
      */
     public long size() {
         try {
-            return delegate.size();
+            return mDelegate.getSize();
         } catch (IOException e) {
             // This can occur if the cache failed to lazily initialize.
             return -1;
@@ -229,7 +231,7 @@
      * its data.
      */
     public long maxSize() {
-        return delegate.maxSize();
+        return mDelegate.getMaxSize();
     }
 
     /**
@@ -239,7 +241,7 @@
      */
     public void flush() {
         try {
-            delegate.flush();
+            mDelegate.flush();
         } catch (IOException ignored) {
         }
     }
@@ -249,7 +251,7 @@
      * supply a response or validate a locally cached response.
      */
     public int getNetworkCount() {
-        return delegate.getNetworkCount();
+        return mDelegate.getNetworkCount();
     }
 
     /**
@@ -258,7 +260,7 @@
      * validated over the network.
      */
     public int getHitCount() {
-        return delegate.getHitCount();
+        return mDelegate.getHitCount();
     }
 
     /**
@@ -267,18 +269,19 @@
      * to handle a redirects and retries.
      */
     public int getRequestCount() {
-        return delegate.getRequestCount();
+        return mDelegate.getRequestCount();
     }
 
     /**
      * Uninstalls the cache and releases any active resources. Stored contents
      * will remain on the filesystem.
      */
-    @Override public void close() throws IOException {
+    @Override
+    public void close() throws IOException {
         if (ResponseCache.getDefault() == this) {
             ResponseCache.setDefault(null);
         }
-        delegate.close();
+        mDelegate.close();
     }
 
     /**
@@ -288,13 +291,12 @@
         if (ResponseCache.getDefault() == this) {
             ResponseCache.setDefault(null);
         }
-        delegate.delete();
+        mDelegate.delete();
     }
 
     /** @hide Needed for OkHttp integration. */
     @Override
-    public Cache getCache() {
-        return delegate.getCache();
+    public CacheHolder getCacheHolder() {
+        return mDelegate.getCacheHolder();
     }
-
 }
diff --git a/core/java/android/net/http/OWNERS b/core/java/android/net/http/OWNERS
new file mode 100644
index 0000000..6b8c9ed
--- /dev/null
+++ b/core/java/android/net/http/OWNERS
@@ -0,0 +1,3 @@
+flooey@google.com
+narayan@google.com
+tobiast@google.com
diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java
index 4c0f418..21ada36 100644
--- a/core/java/android/net/http/SslCertificate.java
+++ b/core/java/android/net/http/SslCertificate.java
@@ -16,8 +16,6 @@
 
 package android.net.http;
 
-import com.android.internal.util.HexDump;
-
 import android.content.Context;
 import android.os.Bundle;
 import android.text.format.DateFormat;
@@ -25,6 +23,9 @@
 import android.view.View;
 import android.widget.TextView;
 
+import com.android.internal.util.HexDump;
+import com.android.org.bouncycastle.asn1.x509.X509Name;
+
 import java.io.ByteArrayInputStream;
 import java.math.BigInteger;
 import java.security.MessageDigest;
@@ -39,8 +40,6 @@
 import java.util.Date;
 import java.util.Vector;
 
-import com.android.org.bouncycastle.asn1.x509.X509Name;
-
 /**
  * SSL certificate info (certificate details) class
  */
@@ -248,6 +247,14 @@
     }
 
     /**
+     * @return The {@code X509Certificate} used to create this {@code SslCertificate} or
+     * {@code null} if no certificate was provided.
+     */
+    public X509Certificate getX509Certificate() {
+        return mX509Certificate;
+    }
+
+    /**
      * Convenience for UI presentation, not intended as public API.
      */
     private static String getSerialNumber(X509Certificate x509Certificate) {
diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java
index 1ad0929..12c2305 100644
--- a/core/java/android/net/metrics/ValidationProbeEvent.java
+++ b/core/java/android/net/metrics/ValidationProbeEvent.java
@@ -37,6 +37,7 @@
     public static final int PROBE_HTTPS     = 2;
     public static final int PROBE_PAC       = 3;
     public static final int PROBE_FALLBACK  = 4;
+    public static final int PROBE_PRIVDNS   = 5;
 
     public static final int DNS_FAILURE = 0;
     public static final int DNS_SUCCESS = 1;
diff --git a/core/java/android/nfc/ErrorCodes.java b/core/java/android/nfc/ErrorCodes.java
index 3adcdc3..98e31ad 100644
--- a/core/java/android/nfc/ErrorCodes.java
+++ b/core/java/android/nfc/ErrorCodes.java
@@ -16,6 +16,8 @@
 
 package android.nfc;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * This class defines all the error codes that can be returned by the service
  * and producing an exception on the application level. These are needed since
@@ -25,6 +27,7 @@
  */
 public class ErrorCodes {
 
+    @UnsupportedAppUsage
     public static boolean isError(int code) {
         if (code < 0) {
             return true;
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 093a9b4..b0090ca 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Parcel;
@@ -279,6 +280,7 @@
 
     private final short mTnf;
     private final byte[] mType;
+    @UnsupportedAppUsage
     private final byte[] mId;
     private final byte[] mPayload;
 
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 958063a..abfa133 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.Application;
 import android.content.ContentProvider;
@@ -45,6 +46,7 @@
     static final String TAG = NfcAdapter.TAG;
     static final Boolean DBG = false;
 
+    @UnsupportedAppUsage
     final NfcAdapter mAdapter;
 
     // All objects in the lists are protected by this
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index c3f23a1..21fed48 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -20,6 +20,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.OnActivityPausedListener;
@@ -325,6 +326,7 @@
     // Final after first constructor, except for
     // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
     // recovery
+    @UnsupportedAppUsage
     static INfcAdapter sService;
     static INfcTag sTagService;
     static INfcCardEmulation sCardEmulationService;
@@ -490,6 +492,7 @@
      * or throws if NFC is not available.
      * @hide
      */
+    @UnsupportedAppUsage
     public static synchronized NfcAdapter getNfcAdapter(Context context) {
         if (!sIsInitialized) {
             sHasNfcFeature = hasNfcFeature();
@@ -593,6 +596,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static NfcAdapter getDefaultAdapter() {
         // introduced in API version 9 (GB 2.3)
         // deprecated in API version 10 (GB 2.3.3)
@@ -615,6 +619,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public Context getContext() {
         return mContext;
     }
@@ -623,6 +628,7 @@
      * Returns the binder interface to the service.
      * @hide
      */
+    @UnsupportedAppUsage
     public INfcAdapter getService() {
         isEnabled();  // NOP call to recover sService if it is stale
         return sService;
@@ -676,6 +682,7 @@
      * NFC service dead - attempt best effort recovery
      * @hide
      */
+    @UnsupportedAppUsage
     public void attemptDeadServiceRecovery(Exception e) {
         Log.e(TAG, "NFC service dead - attempting to recover", e);
         INfcAdapter service = getServiceInterface();
@@ -746,6 +753,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public int getAdapterState() {
         try {
             return sService.getState();
@@ -1227,6 +1235,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
             int flags) {
         if (activity == null) {
@@ -1862,6 +1871,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public INfcAdapterExtras getNfcAdapterExtrasInterface() {
         if (mContext == null) {
             throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
diff --git a/core/java/android/nfc/NfcManager.java b/core/java/android/nfc/NfcManager.java
index 50d6745..71199c9 100644
--- a/core/java/android/nfc/NfcManager.java
+++ b/core/java/android/nfc/NfcManager.java
@@ -17,6 +17,7 @@
 package android.nfc;
 
 import android.annotation.SystemService;
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 
 /**
@@ -44,6 +45,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public NfcManager(Context context) {
         NfcAdapter adapter;
         context = context.getApplicationContext();
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index 154d5a1..ce684cf 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.nfc.tech.IsoDep;
 import android.nfc.tech.MifareClassic;
@@ -110,6 +111,7 @@
  * <p>
  */
 public final class Tag implements Parcelable {
+    @UnsupportedAppUsage
     final byte[] mId;
     final int[] mTechList;
     final String[] mTechStringList;
@@ -235,6 +237,7 @@
      * For use by NfcService only.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getServiceHandle() {
         return mServiceHandle;
     }
@@ -355,6 +358,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public INfcTag getTagService() {
         return mTagService;
     }
diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java
index 78a9401..63776c4 100644
--- a/core/java/android/nfc/cardemulation/AidGroup.java
+++ b/core/java/android/nfc/cardemulation/AidGroup.java
@@ -24,6 +24,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -45,8 +46,11 @@
 
     static final String TAG = "AidGroup";
 
+    @UnsupportedAppUsage
     final List<String> aids;
+    @UnsupportedAppUsage
     final String category;
+    @UnsupportedAppUsage
     final String description;
 
     /**
@@ -79,6 +83,7 @@
         this.description = null;
     }
 
+    @UnsupportedAppUsage
     AidGroup(String category, String description) {
         this.aids = new ArrayList<String>();
         this.category = category;
@@ -88,6 +93,7 @@
     /**
      * @return the category of this AID group
      */
+    @UnsupportedAppUsage
     public String getCategory() {
         return category;
     }
@@ -95,6 +101,7 @@
     /**
      * @return the list of AIDs in this group
      */
+    @UnsupportedAppUsage
     public List<String> getAids() {
         return aids;
     }
@@ -124,6 +131,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public static final Parcelable.Creator<AidGroup> CREATOR =
             new Parcelable.Creator<AidGroup>() {
 
@@ -144,6 +152,7 @@
         }
     };
 
+    @UnsupportedAppUsage
     static public AidGroup createFromXml(XmlPullParser parser) throws XmlPullParserException, IOException {
         String category = null;
         ArrayList<String> aids = new ArrayList<String>();
@@ -185,6 +194,7 @@
         return group;
     }
 
+    @UnsupportedAppUsage
     public void writeAsXml(XmlSerializer out) throws IOException {
         out.startTag(null, "aid-group");
         out.attribute(null, "category", category);
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 218e4f2..e8d801c 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -16,6 +16,7 @@
 
 package android.nfc.cardemulation;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -54,6 +55,7 @@
     /**
      * The service that implements this
      */
+    @UnsupportedAppUsage
     final ResolveInfo mService;
 
     /**
@@ -69,11 +71,13 @@
     /**
      * Mapping from category to static AID group
      */
+    @UnsupportedAppUsage
     final HashMap<String, AidGroup> mStaticAidGroups;
 
     /**
      * Mapping from category to dynamic AID group
      */
+    @UnsupportedAppUsage
     final HashMap<String, AidGroup> mDynamicAidGroups;
 
     /**
@@ -99,6 +103,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
             ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
             boolean requiresUnlock, int bannerResource, int uid,
@@ -120,6 +125,7 @@
         this.mSettingsActivityName = settingsActivityName;
     }
 
+    @UnsupportedAppUsage
     public ApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost) throws
             XmlPullParserException, IOException {
         ServiceInfo si = info.serviceInfo;
@@ -374,18 +380,22 @@
         return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category));
     }
 
+    @UnsupportedAppUsage
     public boolean isOnHost() {
         return mOnHost;
     }
 
+    @UnsupportedAppUsage
     public boolean requiresUnlock() {
         return mRequiresDeviceUnlock;
     }
 
+    @UnsupportedAppUsage
     public String getDescription() {
         return mDescription;
     }
 
+    @UnsupportedAppUsage
     public int getUid() {
         return mUid;
     }
@@ -411,6 +421,7 @@
         return mService.loadIcon(pm);
     }
 
+    @UnsupportedAppUsage
     public Drawable loadBanner(PackageManager pm) {
         Resources res;
         try {
@@ -426,6 +437,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public String getSettingsActivityName() { return mSettingsActivityName; }
 
     @Override
@@ -483,6 +495,7 @@
         dest.writeString(mSettingsActivityName);
     };
 
+    @UnsupportedAppUsage
     public static final Parcelable.Creator<ApduServiceInfo> CREATOR =
             new Parcelable.Creator<ApduServiceInfo>() {
         @Override
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 221abed..9cf7de5 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -247,8 +247,10 @@
      *   - Deferred job metrics.
      * New in version 32:
      *   - Ambient display properly output in data dump.
+     * New in version 33:
+     *   - Fixed bug in min learned capacity updating process.
      */
-    static final int CHECKIN_VERSION = 32;
+    static final int CHECKIN_VERSION = 33;
 
     /**
      * Old version, we hit 9 and ran out of room, need to remove.
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ac87105..b9d9007 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -21,10 +21,9 @@
 import android.util.ExceptionUtils;
 import android.util.Log;
 import android.util.Slog;
-import android.util.SparseIntArray;
 
-import com.android.internal.os.BinderCallsStats;
 import com.android.internal.os.BinderInternal;
+import com.android.internal.os.BinderInternal.CallSession;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
@@ -35,12 +34,7 @@
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * Base class for a remotable object, the core part of a lightweight
@@ -99,6 +93,11 @@
     private static volatile TransactionTracker sTransactionTracker = null;
 
     /**
+     * Global observer for this process.
+     */
+    private static BinderInternal.Observer sObserver = null;
+
+    /**
      * Guestimate of native memory associated with a Binder.
      */
     private static final int NATIVE_ALLOCATION_SIZE = 500;
@@ -112,6 +111,7 @@
                 Binder.class.getClassLoader(), getNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
     }
 
+
     // Transaction tracking code.
 
     /**
@@ -157,6 +157,15 @@
         return sTransactionTracker;
     }
 
+    /**
+     * Get the binder transaction observer for this process.
+     *
+     * @hide
+     */
+    public static void setObserver(@Nullable BinderInternal.Observer observer) {
+        sObserver = observer;
+    }
+
     /** {@hide} */
     static volatile boolean sWarnOnBlocking = false;
 
@@ -397,9 +406,28 @@
     public static final native void blockUntilThreadAvailable();
 
     /**
-     * Default constructor initializes the object.
+     * Default constructor just initializes the object.
+     *
+     * If you're creating a Binder token (a Binder object without an attached interface),
+     * you should use {@link #Binder(String)} instead.
      */
     public Binder() {
+        this(null);
+    }
+
+    /**
+     * Constructor for creating a raw Binder object (token) along with a descriptor.
+     *
+     * The descriptor of binder objects usually specifies the interface they are implementing.
+     * In case of binder tokens, no interface is implemented, and the descriptor can be used
+     * as a sort of tag to help identify the binder token. This will help identify remote
+     * references to these objects more easily when debugging.
+     *
+     * @param descriptor Used to identify the creator of this token, for example the class name.
+     * Instead of creating multiple tokens with the same descriptor, consider adding a suffix to
+     * help identify them.
+     */
+    public Binder(@Nullable String descriptor)  {
         mObject = getNativeBBinderHolder();
         NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
 
@@ -411,6 +439,7 @@
                     klass.getCanonicalName());
             }
         }
+        mDescriptor = descriptor;
     }
 
     /**
@@ -551,6 +580,20 @@
     }
 
     /**
+     * Resolves a transaction code to a human readable name.
+     *
+     * <p>Default implementation is a stub that returns null.
+     * <p>AIDL generated code will return the original method name.
+     *
+     * @param transactionCode The code to resolve.
+     * @return A human readable name.
+     * @hide
+     */
+    public @Nullable String getTransactionName(int transactionCode) {
+        return null;
+    }
+
+    /**
      * Implemented to call the more convenient version
      * {@link #dump(FileDescriptor, PrintWriter, String[])}.
      */
@@ -713,8 +756,10 @@
     // Entry point from android_util_Binder.cpp's onTransact
     private boolean execTransact(int code, long dataObj, long replyObj,
             int flags) {
-        BinderCallsStats binderCallsStats = BinderCallsStats.getInstance();
-        BinderCallsStats.CallSession callSession = binderCallsStats.callStarted(this, code);
+        // Make sure the observer won't change while processing a transaction.
+        final BinderInternal.Observer observer = sObserver;
+        final CallSession callSession =
+                observer != null ? observer.callStarted(this, code) : null;
         Parcel data = Parcel.obtain(dataObj);
         Parcel reply = Parcel.obtain(replyObj);
         // theoretically, we should call transact, which will call onTransact,
@@ -730,7 +775,9 @@
             }
             res = onTransact(code, data, reply, flags);
         } catch (RemoteException|RuntimeException e) {
-            binderCallsStats.callThrewException(callSession);
+            if (observer != null) {
+                observer.callThrewException(callSession, e);
+            }
             if (LOG_RUNTIME_EXCEPTION) {
                 Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
             }
@@ -762,448 +809,10 @@
         // to the main transaction loop to wait for another incoming transaction.  Either
         // way, strict mode begone!
         StrictMode.clearGatheredViolations();
-        binderCallsStats.callEnded(callSession, requestSizeBytes, replySizeBytes);
+        if (observer != null) {
+            observer.callEnded(callSession, requestSizeBytes, replySizeBytes);
+        }
 
         return res;
     }
 }
-
-/**
- * Java proxy for a native IBinder object.
- * Allocated and constructed by the native javaObjectforIBinder function. Never allocated
- * directly from Java code.
- */
-final class BinderProxy implements IBinder {
-    // See android_util_Binder.cpp for the native half of this.
-
-    // Assume the process-wide default value when created
-    volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;
-
-    /*
-     * Map from longs to BinderProxy, retaining only a WeakReference to the BinderProxies.
-     * We roll our own only because we need to lazily remove WeakReferences during accesses
-     * to avoid accumulating junk WeakReference objects. WeakHashMap isn't easily usable
-     * because we want weak values, not keys.
-     * Our hash table is never resized, but the number of entries is unlimited;
-     * performance degrades as occupancy increases significantly past MAIN_INDEX_SIZE.
-     * Not thread-safe. Client ensures there's a single access at a time.
-     */
-    private static final class ProxyMap {
-        private static final int LOG_MAIN_INDEX_SIZE = 8;
-        private static final int MAIN_INDEX_SIZE = 1 <<  LOG_MAIN_INDEX_SIZE;
-        private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
-        // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
-        private static final int CRASH_AT_SIZE = 20_000;
-
-        /**
-         * We next warn when we exceed this bucket size.
-         */
-        private int mWarnBucketSize = 20;
-
-        /**
-         * Increment mWarnBucketSize by WARN_INCREMENT each time we warn.
-         */
-        private static final int WARN_INCREMENT = 10;
-
-        /**
-         * Hash function tailored to native pointers.
-         * Returns a value < MAIN_INDEX_SIZE.
-         */
-        private static int hash(long arg) {
-            return ((int) ((arg >> 2) ^ (arg >> (2 + LOG_MAIN_INDEX_SIZE)))) & MAIN_INDEX_MASK;
-        }
-
-        /**
-         * Return the total number of pairs in the map.
-         */
-        private int size() {
-            int size = 0;
-            for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
-                if (a != null) {
-                    size += a.size();
-                }
-            }
-            return size;
-        }
-
-        /**
-         * Return the total number of pairs in the map containing values that have
-         * not been cleared. More expensive than the above size function.
-         */
-        private int unclearedSize() {
-            int size = 0;
-            for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
-                if (a != null) {
-                    for (WeakReference<BinderProxy> ref : a) {
-                        if (ref.get() != null) {
-                            ++size;
-                        }
-                    }
-                }
-            }
-            return size;
-        }
-
-        /**
-         * Remove ith entry from the hash bucket indicated by hash.
-         */
-        private void remove(int hash, int index) {
-            Long[] keyArray = mMainIndexKeys[hash];
-            ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[hash];
-            int size = valueArray.size();  // KeyArray may have extra elements.
-            // Move last entry into empty slot, and truncate at end.
-            if (index != size - 1) {
-                keyArray[index] = keyArray[size - 1];
-                valueArray.set(index, valueArray.get(size - 1));
-            }
-            valueArray.remove(size - 1);
-            // Just leave key array entry; it's unused. We only trust the valueArray size.
-        }
-
-        /**
-         * Look up the supplied key. If we have a non-cleared entry for it, return it.
-         */
-        BinderProxy get(long key) {
-            int myHash = hash(key);
-            Long[] keyArray = mMainIndexKeys[myHash];
-            if (keyArray == null) {
-                return null;
-            }
-            ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
-            int bucketSize = valueArray.size();
-            for (int i = 0; i < bucketSize; ++i) {
-                long foundKey = keyArray[i];
-                if (key == foundKey) {
-                    WeakReference<BinderProxy> wr = valueArray.get(i);
-                    BinderProxy bp = wr.get();
-                    if (bp != null) {
-                        return bp;
-                    } else {
-                        remove(myHash, i);
-                        return null;
-                    }
-                }
-            }
-            return null;
-        }
-
-        private int mRandom;  // A counter used to generate a "random" index. World's 2nd worst RNG.
-
-        /**
-         * Add the key-value pair to the map.
-         * Requires that the indicated key is not already in the map.
-         */
-        void set(long key, @NonNull BinderProxy value) {
-            int myHash = hash(key);
-            ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
-            if (valueArray == null) {
-                valueArray = mMainIndexValues[myHash] = new ArrayList<>();
-                mMainIndexKeys[myHash] = new Long[1];
-            }
-            int size = valueArray.size();
-            WeakReference<BinderProxy> newWr = new WeakReference<>(value);
-            // First look for a cleared reference.
-            // This ensures that ArrayList size is bounded by the maximum occupancy of
-            // that bucket.
-            for (int i = 0; i < size; ++i) {
-                if (valueArray.get(i).get() == null) {
-                    valueArray.set(i, newWr);
-                    Long[] keyArray = mMainIndexKeys[myHash];
-                    keyArray[i] = key;
-                    if (i < size - 1) {
-                        // "Randomly" check one of the remaining entries in [i+1, size), so that
-                        // needlessly long buckets are eventually pruned.
-                        int rnd = Math.floorMod(++mRandom, size - (i + 1));
-                        if (valueArray.get(i + 1 + rnd).get() == null) {
-                            remove(myHash, i + 1 + rnd);
-                        }
-                    }
-                    return;
-                }
-            }
-            valueArray.add(size, newWr);
-            Long[] keyArray = mMainIndexKeys[myHash];
-            if (keyArray.length == size) {
-                // size >= 1, since we initially allocated one element
-                Long[] newArray = new Long[size + size / 2 + 2];
-                System.arraycopy(keyArray, 0, newArray, 0, size);
-                newArray[size] = key;
-                mMainIndexKeys[myHash] = newArray;
-            } else {
-                keyArray[size] = key;
-            }
-            if (size >= mWarnBucketSize) {
-                final int totalSize = size();
-                Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
-                        + " total = " + totalSize);
-                mWarnBucketSize += WARN_INCREMENT;
-                if (Build.IS_DEBUGGABLE && totalSize >= CRASH_AT_SIZE) {
-                    // Use the number of uncleared entries to determine whether we should
-                    // really report a histogram and crash. We don't want to fundamentally
-                    // change behavior for a debuggable process, so we GC only if we are
-                    // about to crash.
-                    final int totalUnclearedSize = unclearedSize();
-                    if (totalUnclearedSize >= CRASH_AT_SIZE) {
-                        dumpProxyInterfaceCounts();
-                        dumpPerUidProxyCounts();
-                        Runtime.getRuntime().gc();
-                        throw new AssertionError("Binder ProxyMap has too many entries: "
-                                + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
-                                + unclearedSize() + " (uncleared after GC). BinderProxy leak?");
-                    } else if (totalSize > 3 * totalUnclearedSize / 2) {
-                        Log.v(Binder.TAG, "BinderProxy map has many cleared entries: "
-                                + (totalSize - totalUnclearedSize) + " of " + totalSize
-                                + " are cleared");
-                    }
-                }
-            }
-        }
-
-        /**
-         * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
-         */
-        private void dumpProxyInterfaceCounts() {
-            Map<String, Integer> counts = new HashMap<>();
-            for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
-                if (a != null) {
-                    for (WeakReference<BinderProxy> weakRef : a) {
-                        BinderProxy bp = weakRef.get();
-                        String key;
-                        if (bp == null) {
-                            key = "<cleared weak-ref>";
-                        } else {
-                            try {
-                                key = bp.getInterfaceDescriptor();
-                            } catch (Throwable t) {
-                                key = "<exception during getDescriptor>";
-                            }
-                        }
-                        Integer i = counts.get(key);
-                        if (i == null) {
-                            counts.put(key, 1);
-                        } else {
-                            counts.put(key, i + 1);
-                        }
-                    }
-                }
-            }
-            Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
-                    new Map.Entry[counts.size()]);
-            Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
-                    -> b.getValue().compareTo(a.getValue()));
-            Log.v(Binder.TAG, "BinderProxy descriptor histogram (top ten):");
-            int printLength = Math.min(10, sorted.length);
-            for (int i = 0; i < printLength; i++) {
-                Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i].getKey() + " x"
-                        + sorted[i].getValue());
-            }
-        }
-
-        /**
-         * Dump per uid binder proxy counts to the logcat.
-         */
-        private void dumpPerUidProxyCounts() {
-            SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
-            if (counts.size() == 0) return;
-            Log.d(Binder.TAG, "Per Uid Binder Proxy Counts:");
-            for (int i = 0; i < counts.size(); i++) {
-                final int uid = counts.keyAt(i);
-                final int binderCount = counts.valueAt(i);
-                Log.d(Binder.TAG, "UID : " + uid + "  count = " + binderCount);
-            }
-        }
-
-        // Corresponding ArrayLists in the following two arrays always have the same size.
-        // They contain no empty entries. However WeakReferences in the values ArrayLists
-        // may have been cleared.
-
-        // mMainIndexKeys[i][j] corresponds to mMainIndexValues[i].get(j) .
-        // The values ArrayList has the proper size(), the corresponding keys array
-        // is always at least the same size, but may be larger.
-        // If either a particular keys array, or the corresponding values ArrayList
-        // are null, then they both are.
-        private final Long[][] mMainIndexKeys = new Long[MAIN_INDEX_SIZE][];
-        private final ArrayList<WeakReference<BinderProxy>>[] mMainIndexValues =
-                new ArrayList[MAIN_INDEX_SIZE];
-    }
-
-    private static ProxyMap sProxyMap = new ProxyMap();
-
-    /**
-      * Dump proxy debug information.
-      *
-      * Note: this method is not thread-safe; callers must serialize with other
-      * accesses to sProxyMap, in particular {@link #getInstance(long, long)}.
-      *
-      * @hide
-      */
-    private static void dumpProxyDebugInfo() {
-        if (Build.IS_DEBUGGABLE) {
-            sProxyMap.dumpProxyInterfaceCounts();
-            // Note that we don't call dumpPerUidProxyCounts(); this is because this
-            // method may be called as part of the uid limit being hit, and calling
-            // back into the UID tracking code would cause us to try to acquire a mutex
-            // that is held during that callback.
-        }
-    }
-
-    /**
-     * Return a BinderProxy for IBinder.
-     * This method is thread-hostile!  The (native) caller serializes getInstance() calls using
-     * gProxyLock.
-     * If we previously returned a BinderProxy bp for the same iBinder, and bp is still
-     * in use, then we return the same bp.
-     *
-     * @param nativeData C++ pointer to (possibly still empty) BinderProxyNativeData.
-     * Takes ownership of nativeData iff <result>.mNativeData == nativeData, or if
-     * we exit via an exception.  If neither applies, it's the callers responsibility to
-     * recycle nativeData.
-     * @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object.
-     */
-    private static BinderProxy getInstance(long nativeData, long iBinder) {
-        BinderProxy result;
-        try {
-            result = sProxyMap.get(iBinder);
-            if (result != null) {
-                return result;
-            }
-            result = new BinderProxy(nativeData);
-        } catch (Throwable e) {
-            // We're throwing an exception (probably OOME); don't drop nativeData.
-            NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
-                    nativeData);
-            throw e;
-        }
-        NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
-        // The registry now owns nativeData, even if registration threw an exception.
-        sProxyMap.set(iBinder, result);
-        return result;
-    }
-
-    private BinderProxy(long nativeData) {
-        mNativeData = nativeData;
-    }
-
-    /**
-     * Guestimate of native memory associated with a BinderProxy.
-     * This includes the underlying IBinder, associated DeathRecipientList, and KeyedVector
-     * that points back to us. We guess high since it includes a GlobalRef, which
-     * may be in short supply.
-     */
-    private static final int NATIVE_ALLOCATION_SIZE = 1000;
-
-    // Use a Holder to allow static initialization of BinderProxy in the boot image, and
-    // to avoid some initialization ordering issues.
-    private static class NoImagePreloadHolder {
-        public static final long sNativeFinalizer = getNativeFinalizer();
-        public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
-                BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);
-    }
-
-    public native boolean pingBinder();
-    public native boolean isBinderAlive();
-
-    public IInterface queryLocalInterface(String descriptor) {
-        return null;
-    }
-
-    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
-        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
-
-        if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
-            // For now, avoid spamming the log by disabling after we've logged
-            // about this interface at least once
-            mWarnOnBlocking = false;
-            Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
-                    new Throwable());
-        }
-
-        final boolean tracingEnabled = Binder.isTracingEnabled();
-        if (tracingEnabled) {
-            final Throwable tr = new Throwable();
-            Binder.getTransactionTracker().addTrace(tr);
-            StackTraceElement stackTraceElement = tr.getStackTrace()[1];
-            Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
-                    stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
-        }
-        try {
-            return transactNative(code, data, reply, flags);
-        } finally {
-            if (tracingEnabled) {
-                Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
-            }
-        }
-    }
-
-    private static native long getNativeFinalizer();
-    public native String getInterfaceDescriptor() throws RemoteException;
-    public native boolean transactNative(int code, Parcel data, Parcel reply,
-            int flags) throws RemoteException;
-    public native void linkToDeath(DeathRecipient recipient, int flags)
-            throws RemoteException;
-    public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
-
-    public void dump(FileDescriptor fd, String[] args) throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeFileDescriptor(fd);
-        data.writeStringArray(args);
-        try {
-            transact(DUMP_TRANSACTION, data, reply, 0);
-            reply.readException();
-        } finally {
-            data.recycle();
-            reply.recycle();
-        }
-    }
-
-    public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeFileDescriptor(fd);
-        data.writeStringArray(args);
-        try {
-            transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
-        } finally {
-            data.recycle();
-            reply.recycle();
-        }
-    }
-
-    public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, ShellCallback callback,
-            ResultReceiver resultReceiver) throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeFileDescriptor(in);
-        data.writeFileDescriptor(out);
-        data.writeFileDescriptor(err);
-        data.writeStringArray(args);
-        ShellCallback.writeToParcel(callback, data);
-        resultReceiver.writeToParcel(data, 0);
-        try {
-            transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
-            reply.readException();
-        } finally {
-            data.recycle();
-            reply.recycle();
-        }
-    }
-
-    private static final void sendDeathNotice(DeathRecipient recipient) {
-        if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
-        try {
-            recipient.binderDied();
-        }
-        catch (RuntimeException exc) {
-            Log.w("BinderNative", "Uncaught exception from death notification",
-                    exc);
-        }
-    }
-
-    /**
-     * C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
-     * native IBinder object, and a DeathRecipientList.
-     */
-    private final long mNativeData;
-}
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
new file mode 100644
index 0000000..5752b6f
--- /dev/null
+++ b/core/java/android/os/BinderProxy.java
@@ -0,0 +1,588 @@
+/*
+ * 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 android.annotation.NonNull;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BinderInternal;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.io.FileDescriptor;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Java proxy for a native IBinder object.
+ * Allocated and constructed by the native javaObjectforIBinder function. Never allocated
+ * directly from Java code.
+ *
+ * @hide
+ */
+public final class BinderProxy implements IBinder {
+    // See android_util_Binder.cpp for the native half of this.
+
+    // Assume the process-wide default value when created
+    volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;
+
+    /*
+     * Map from longs to BinderProxy, retaining only a WeakReference to the BinderProxies.
+     * We roll our own only because we need to lazily remove WeakReferences during accesses
+     * to avoid accumulating junk WeakReference objects. WeakHashMap isn't easily usable
+     * because we want weak values, not keys.
+     * Our hash table is never resized, but the number of entries is unlimited;
+     * performance degrades as occupancy increases significantly past MAIN_INDEX_SIZE.
+     * Not thread-safe. Client ensures there's a single access at a time.
+     */
+    private static final class ProxyMap {
+        private static final int LOG_MAIN_INDEX_SIZE = 8;
+        private static final int MAIN_INDEX_SIZE = 1 <<  LOG_MAIN_INDEX_SIZE;
+        private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
+        // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
+        private static final int CRASH_AT_SIZE = 20_000;
+
+        /**
+         * We next warn when we exceed this bucket size.
+         */
+        private int mWarnBucketSize = 20;
+
+        /**
+         * Increment mWarnBucketSize by WARN_INCREMENT each time we warn.
+         */
+        private static final int WARN_INCREMENT = 10;
+
+        /**
+         * Hash function tailored to native pointers.
+         * Returns a value < MAIN_INDEX_SIZE.
+         */
+        private static int hash(long arg) {
+            return ((int) ((arg >> 2) ^ (arg >> (2 + LOG_MAIN_INDEX_SIZE)))) & MAIN_INDEX_MASK;
+        }
+
+        /**
+         * Return the total number of pairs in the map.
+         */
+        private int size() {
+            int size = 0;
+            for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+                if (a != null) {
+                    size += a.size();
+                }
+            }
+            return size;
+        }
+
+        /**
+         * Return the total number of pairs in the map containing values that have
+         * not been cleared. More expensive than the above size function.
+         */
+        private int unclearedSize() {
+            int size = 0;
+            for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+                if (a != null) {
+                    for (WeakReference<BinderProxy> ref : a) {
+                        if (ref.get() != null) {
+                            ++size;
+                        }
+                    }
+                }
+            }
+            return size;
+        }
+
+        /**
+         * Remove ith entry from the hash bucket indicated by hash.
+         */
+        private void remove(int hash, int index) {
+            Long[] keyArray = mMainIndexKeys[hash];
+            ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[hash];
+            int size = valueArray.size();  // KeyArray may have extra elements.
+            // Move last entry into empty slot, and truncate at end.
+            if (index != size - 1) {
+                keyArray[index] = keyArray[size - 1];
+                valueArray.set(index, valueArray.get(size - 1));
+            }
+            valueArray.remove(size - 1);
+            // Just leave key array entry; it's unused. We only trust the valueArray size.
+        }
+
+        /**
+         * Look up the supplied key. If we have a non-cleared entry for it, return it.
+         */
+        BinderProxy get(long key) {
+            int myHash = hash(key);
+            Long[] keyArray = mMainIndexKeys[myHash];
+            if (keyArray == null) {
+                return null;
+            }
+            ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
+            int bucketSize = valueArray.size();
+            for (int i = 0; i < bucketSize; ++i) {
+                long foundKey = keyArray[i];
+                if (key == foundKey) {
+                    WeakReference<BinderProxy> wr = valueArray.get(i);
+                    BinderProxy bp = wr.get();
+                    if (bp != null) {
+                        return bp;
+                    } else {
+                        remove(myHash, i);
+                        return null;
+                    }
+                }
+            }
+            return null;
+        }
+
+        private int mRandom;  // A counter used to generate a "random" index. World's 2nd worst RNG.
+
+        /**
+         * Add the key-value pair to the map.
+         * Requires that the indicated key is not already in the map.
+         */
+        void set(long key, @NonNull BinderProxy value) {
+            int myHash = hash(key);
+            ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
+            if (valueArray == null) {
+                valueArray = mMainIndexValues[myHash] = new ArrayList<>();
+                mMainIndexKeys[myHash] = new Long[1];
+            }
+            int size = valueArray.size();
+            WeakReference<BinderProxy> newWr = new WeakReference<>(value);
+            // First look for a cleared reference.
+            // This ensures that ArrayList size is bounded by the maximum occupancy of
+            // that bucket.
+            for (int i = 0; i < size; ++i) {
+                if (valueArray.get(i).get() == null) {
+                    valueArray.set(i, newWr);
+                    Long[] keyArray = mMainIndexKeys[myHash];
+                    keyArray[i] = key;
+                    if (i < size - 1) {
+                        // "Randomly" check one of the remaining entries in [i+1, size), so that
+                        // needlessly long buckets are eventually pruned.
+                        int rnd = Math.floorMod(++mRandom, size - (i + 1));
+                        if (valueArray.get(i + 1 + rnd).get() == null) {
+                            remove(myHash, i + 1 + rnd);
+                        }
+                    }
+                    return;
+                }
+            }
+            valueArray.add(size, newWr);
+            Long[] keyArray = mMainIndexKeys[myHash];
+            if (keyArray.length == size) {
+                // size >= 1, since we initially allocated one element
+                Long[] newArray = new Long[size + size / 2 + 2];
+                System.arraycopy(keyArray, 0, newArray, 0, size);
+                newArray[size] = key;
+                mMainIndexKeys[myHash] = newArray;
+            } else {
+                keyArray[size] = key;
+            }
+            if (size >= mWarnBucketSize) {
+                final int totalSize = size();
+                Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
+                        + " total = " + totalSize);
+                mWarnBucketSize += WARN_INCREMENT;
+                if (Build.IS_DEBUGGABLE && totalSize >= CRASH_AT_SIZE) {
+                    // Use the number of uncleared entries to determine whether we should
+                    // really report a histogram and crash. We don't want to fundamentally
+                    // change behavior for a debuggable process, so we GC only if we are
+                    // about to crash.
+                    final int totalUnclearedSize = unclearedSize();
+                    if (totalUnclearedSize >= CRASH_AT_SIZE) {
+                        dumpProxyInterfaceCounts();
+                        dumpPerUidProxyCounts();
+                        Runtime.getRuntime().gc();
+                        throw new AssertionError("Binder ProxyMap has too many entries: "
+                                + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
+                                + unclearedSize() + " (uncleared after GC). BinderProxy leak?");
+                    } else if (totalSize > 3 * totalUnclearedSize / 2) {
+                        Log.v(Binder.TAG, "BinderProxy map has many cleared entries: "
+                                + (totalSize - totalUnclearedSize) + " of " + totalSize
+                                + " are cleared");
+                    }
+                }
+            }
+        }
+
+        private InterfaceCount[] getSortedInterfaceCounts(int maxToReturn) {
+            if (maxToReturn < 0) {
+                throw new IllegalArgumentException("negative interface count");
+            }
+
+            Map<String, Integer> counts = new HashMap<>();
+            for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+                if (a != null) {
+                    for (WeakReference<BinderProxy> weakRef : a) {
+                        BinderProxy bp = weakRef.get();
+                        String key;
+                        if (bp == null) {
+                            key = "<cleared weak-ref>";
+                        } else {
+                            try {
+                                key = bp.getInterfaceDescriptor();
+                            } catch (Throwable t) {
+                                key = "<exception during getDescriptor>";
+                            }
+                        }
+                        Integer i = counts.get(key);
+                        if (i == null) {
+                            counts.put(key, 1);
+                        } else {
+                            counts.put(key, i + 1);
+                        }
+                    }
+                }
+            }
+            Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
+                    new Map.Entry[counts.size()]);
+
+            Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
+                    -> b.getValue().compareTo(a.getValue()));
+
+            int returnCount = Math.min(maxToReturn, sorted.length);
+            InterfaceCount[] ifaceCounts = new InterfaceCount[returnCount];
+            for (int i = 0; i < returnCount; i++) {
+                ifaceCounts[i] = new InterfaceCount(sorted[i].getKey(), sorted[i].getValue());
+            }
+            return ifaceCounts;
+        }
+
+        static final int MAX_NUM_INTERFACES_TO_DUMP = 10;
+
+        /**
+         * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
+         */
+        private void dumpProxyInterfaceCounts() {
+            final InterfaceCount[] sorted = getSortedInterfaceCounts(MAX_NUM_INTERFACES_TO_DUMP);
+
+            Log.v(Binder.TAG, "BinderProxy descriptor histogram "
+                    + "(top " + Integer.toString(MAX_NUM_INTERFACES_TO_DUMP) + "):");
+            for (int i = 0; i < sorted.length; i++) {
+                Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i]);
+            }
+        }
+
+        /**
+         * Dump per uid binder proxy counts to the logcat.
+         */
+        private void dumpPerUidProxyCounts() {
+            SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
+            if (counts.size() == 0) return;
+            Log.d(Binder.TAG, "Per Uid Binder Proxy Counts:");
+            for (int i = 0; i < counts.size(); i++) {
+                final int uid = counts.keyAt(i);
+                final int binderCount = counts.valueAt(i);
+                Log.d(Binder.TAG, "UID : " + uid + "  count = " + binderCount);
+            }
+        }
+
+        // Corresponding ArrayLists in the following two arrays always have the same size.
+        // They contain no empty entries. However WeakReferences in the values ArrayLists
+        // may have been cleared.
+
+        // mMainIndexKeys[i][j] corresponds to mMainIndexValues[i].get(j) .
+        // The values ArrayList has the proper size(), the corresponding keys array
+        // is always at least the same size, but may be larger.
+        // If either a particular keys array, or the corresponding values ArrayList
+        // are null, then they both are.
+        private final Long[][] mMainIndexKeys = new Long[MAIN_INDEX_SIZE][];
+        private final ArrayList<WeakReference<BinderProxy>>[] mMainIndexValues =
+                new ArrayList[MAIN_INDEX_SIZE];
+    }
+
+    @GuardedBy("sProxyMap")
+    private static final ProxyMap sProxyMap = new ProxyMap();
+
+    /**
+     * Simple pair-value class to store number of binder proxy interfaces live in this process.
+     */
+    public static final class InterfaceCount {
+        private final String mInterfaceName;
+        private final int mCount;
+
+        InterfaceCount(String interfaceName, int count) {
+            mInterfaceName = interfaceName;
+            mCount = count;
+        }
+
+        @Override
+        public String toString() {
+            return mInterfaceName + " x" + Integer.toString(mCount);
+        }
+    }
+
+    /**
+     * Get a sorted array with entries mapping proxy interface names to the number
+     * of live proxies with those names.
+     *
+     * @param num maximum number of proxy interface counts to return. Use
+     *            Integer.MAX_VALUE to retrieve all
+     * @hide
+     */
+    public static InterfaceCount[] getSortedInterfaceCounts(int num) {
+        synchronized (sProxyMap) {
+            return sProxyMap.getSortedInterfaceCounts(num);
+        }
+    }
+
+    /**
+     * Dump proxy debug information.
+     *
+     * @hide
+     */
+    public static void dumpProxyDebugInfo() {
+        if (Build.IS_DEBUGGABLE) {
+            synchronized (sProxyMap) {
+                sProxyMap.dumpProxyInterfaceCounts();
+                sProxyMap.dumpPerUidProxyCounts();
+            }
+        }
+    }
+
+    /**
+     * Return a BinderProxy for IBinder.
+     * If we previously returned a BinderProxy bp for the same iBinder, and bp is still
+     * in use, then we return the same bp.
+     *
+     * @param nativeData C++ pointer to (possibly still empty) BinderProxyNativeData.
+     * Takes ownership of nativeData iff <result>.mNativeData == nativeData, or if
+     * we exit via an exception.  If neither applies, it's the callers responsibility to
+     * recycle nativeData.
+     * @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object.
+     */
+    private static BinderProxy getInstance(long nativeData, long iBinder) {
+        BinderProxy result;
+        synchronized (sProxyMap) {
+            try {
+                result = sProxyMap.get(iBinder);
+                if (result != null) {
+                    return result;
+                }
+                result = new BinderProxy(nativeData);
+            } catch (Throwable e) {
+                // We're throwing an exception (probably OOME); don't drop nativeData.
+                NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
+                        nativeData);
+                throw e;
+            }
+            NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
+            // The registry now owns nativeData, even if registration threw an exception.
+            sProxyMap.set(iBinder, result);
+        }
+        return result;
+    }
+
+    private BinderProxy(long nativeData) {
+        mNativeData = nativeData;
+    }
+
+    /**
+     * Guestimate of native memory associated with a BinderProxy.
+     * This includes the underlying IBinder, associated DeathRecipientList, and KeyedVector
+     * that points back to us. We guess high since it includes a GlobalRef, which
+     * may be in short supply.
+     */
+    private static final int NATIVE_ALLOCATION_SIZE = 1000;
+
+    // Use a Holder to allow static initialization of BinderProxy in the boot image, and
+    // to avoid some initialization ordering issues.
+    private static class NoImagePreloadHolder {
+        public static final long sNativeFinalizer = getNativeFinalizer();
+        public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+                BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);
+    }
+
+    /**
+     * @return false if the hosting process is gone, otherwise whatever the remote returns
+     */
+    public native boolean pingBinder();
+
+    /**
+     * @return false if the hosting process is gone
+     */
+    public native boolean isBinderAlive();
+
+    /**
+     * Retrieve a local interface - always null in case of a proxy
+     */
+    public IInterface queryLocalInterface(String descriptor) {
+        return null;
+    }
+
+    /**
+     * Perform a binder transaction on a proxy.
+     *
+     * @param code The action to perform.  This should
+     * be a number between {@link #FIRST_CALL_TRANSACTION} and
+     * {@link #LAST_CALL_TRANSACTION}.
+     * @param data Marshalled data to send to the target.  Must not be null.
+     * If you are not sending any data, you must create an empty Parcel
+     * that is given here.
+     * @param reply Marshalled data to be received from the target.  May be
+     * null if you are not interested in the return value.
+     * @param flags Additional operation flags.  Either 0 for a normal
+     * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
+     *
+     * @return
+     * @throws RemoteException
+     */
+    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
+        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
+
+        if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
+            // For now, avoid spamming the log by disabling after we've logged
+            // about this interface at least once
+            mWarnOnBlocking = false;
+            Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
+                    new Throwable());
+        }
+
+        final boolean tracingEnabled = Binder.isTracingEnabled();
+        if (tracingEnabled) {
+            final Throwable tr = new Throwable();
+            Binder.getTransactionTracker().addTrace(tr);
+            StackTraceElement stackTraceElement = tr.getStackTrace()[1];
+            Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
+                    stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
+        }
+        try {
+            return transactNative(code, data, reply, flags);
+        } finally {
+            if (tracingEnabled) {
+                Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
+            }
+        }
+    }
+
+    /* Returns the native free function */
+    private static native long getNativeFinalizer();
+    /**
+     *  See {@link IBinder#getInterfaceDescriptor()}
+     */
+    public native String getInterfaceDescriptor() throws RemoteException;
+
+    /**
+     * Native implementation of transact() for proxies
+     */
+    public native boolean transactNative(int code, Parcel data, Parcel reply,
+            int flags) throws RemoteException;
+    /**
+     * See {@link IBinder#linkToDeath(DeathRecipient, int)}
+     */
+    public native void linkToDeath(DeathRecipient recipient, int flags)
+            throws RemoteException;
+    /**
+     * See {@link IBinder#unlinkToDeath}
+     */
+    public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
+
+    /**
+     * Perform a dump on the remote object
+     *
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param args additional arguments to the dump request.
+     * @throws RemoteException
+     */
+    public void dump(FileDescriptor fd, String[] args) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeFileDescriptor(fd);
+        data.writeStringArray(args);
+        try {
+            transact(DUMP_TRANSACTION, data, reply, 0);
+            reply.readException();
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    /**
+     * Perform an asynchronous dump on the remote object
+     *
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param args additional arguments to the dump request.
+     * @throws RemoteException
+     */
+    public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeFileDescriptor(fd);
+        data.writeStringArray(args);
+        try {
+            transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    /**
+     * See {@link IBinder#shellCommand(FileDescriptor, FileDescriptor, FileDescriptor,
+     * String[], ShellCallback, ResultReceiver)}
+     *
+     * @param in The raw file descriptor that an input data stream can be read from.
+     * @param out The raw file descriptor that normal command messages should be written to.
+     * @param err The raw file descriptor that command error messages should be written to.
+     * @param args Command-line arguments.
+     * @param callback Optional callback to the caller's shell to perform operations in it.
+     * @param resultReceiver Called when the command has finished executing, with the result code.
+     * @throws RemoteException
+     */
+    public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+            String[] args, ShellCallback callback,
+            ResultReceiver resultReceiver) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeFileDescriptor(in);
+        data.writeFileDescriptor(out);
+        data.writeFileDescriptor(err);
+        data.writeStringArray(args);
+        ShellCallback.writeToParcel(callback, data);
+        resultReceiver.writeToParcel(data, 0);
+        try {
+            transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
+            reply.readException();
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
+    private static void sendDeathNotice(DeathRecipient recipient) {
+        if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
+        try {
+            recipient.binderDied();
+        } catch (RuntimeException exc) {
+            Log.w("BinderNative", "Uncaught exception from death notification",
+                    exc);
+        }
+    }
+
+    /**
+     * C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
+     * native IBinder object, and a DeathRecipientList.
+     */
+    private final long mNativeData;
+}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 80c534c..6bd2e76 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -165,6 +165,11 @@
     public static final String[] SUPPORTED_64_BIT_ABIS =
             getStringList("ro.product.cpu.abilist64", ",");
 
+    /** {@hide} */
+    @TestApi
+    public static boolean is64BitAbi(String abi) {
+        return VMRuntime.is64BitAbi(abi);
+    }
 
     static {
         /*
@@ -626,7 +631,8 @@
          * October 2013: Android 4.4, KitKat, another tasty treat.
          *
          * <p>Applications targeting this or a later release will get these
-         * new changes in behavior:</p>
+         * new changes in behavior. For more information about this release, see the
+         * <a href="/about/versions/kitkat/">Android KitKat overview</a>.</p>
          * <ul>
          * <li> The default result of
          * {@link android.preference.PreferenceActivity#isValidFragment(String)
@@ -676,7 +682,8 @@
          * November 2014: Lollipop.  A flat one with beautiful shadows.  But still tasty.
          *
          * <p>Applications targeting this or a later release will get these
-         * new changes in behavior:</p>
+         * new changes in behavior.  For more information about this release, see the
+         * <a href="/about/versions/lollipop/">Android Lollipop overview</a>.</p>
          * <ul>
          * <li> {@link android.content.Context#bindService Context.bindService} now
          * requires an explicit Intent, and will throw an exception if given an implicit
@@ -705,6 +712,8 @@
 
         /**
          * March 2015: Lollipop with an extra sugar coating on the outside!
+         * For more information about this release, see the
+         * <a href="/about/versions/android-5.1">Android 5.1 APIs</a>.
          */
         public static final int LOLLIPOP_MR1 = 22;
 
@@ -712,7 +721,8 @@
          * M is for Marshmallow!
          *
          * <p>Applications targeting this or a later release will get these
-         * new changes in behavior:</p>
+         * new changes in behavior. For more information about this release, see the
+         * <a href="/about/versions/marshmallow/">Android 6.0 Marshmallow overview</a>.</p>
          * <ul>
          * <li> Runtime permissions.  Dangerous permissions are no longer granted at
          * install time, but must be requested by the application at runtime through
@@ -743,7 +753,8 @@
          * N is for Nougat.
          *
          * <p>Applications targeting this or a later release will get these
-         * new changes in behavior:</p>
+         * new changes in behavior. For more information about this release, see
+         * the <a href="/about/versions/nougat/">Android Nougat overview</a>.</p>
          * <ul>
          * <li> {@link android.app.DownloadManager.Request#setAllowedNetworkTypes
          * DownloadManager.Request.setAllowedNetworkTypes}
@@ -793,7 +804,9 @@
         public static final int N = 24;
 
         /**
-         * N MR1: Nougat++.
+         * N MR1: Nougat++. For more information about this release, see
+         * <a href="/about/versions/nougat/android-7.1">Android 7.1 for
+         * Developers</a>.
          */
         public static final int N_MR1 = 25;
 
@@ -801,7 +814,8 @@
          * O.
          *
          * <p>Applications targeting this or a later release will get these
-         * new changes in behavior:</p>
+         * new changes in behavior. For more information about this release, see
+         * the <a href="/about/versions/oreo/">Android Oreo overview</a>.</p>
          * <ul>
          * <li><a href="{@docRoot}about/versions/oreo/background.html">Background execution limits</a>
          * are applied to the application.</li>
@@ -890,13 +904,16 @@
          * O MR1.
          *
          * <p>Applications targeting this or a later release will get these
-         * new changes in behavior:</p>
+         * new changes in behavior. For more information about this release, see
+         * <a href="/about/versions/oreo/android-8.1">Android 8.1 features and
+         * APIs</a>.</p>
          * <ul>
          * <li>Apps exporting and linking to apk shared libraries must explicitly
          * enumerate all signing certificates in a consistent order.</li>
          * <li>{@link android.R.attr#screenOrientation} can not be used to request a fixed
          * orientation if the associated activity is not fullscreen and opaque.</li>
          * </ul>
+         *
          */
         public static final int O_MR1 = 27;
 
@@ -904,7 +921,8 @@
          * P.
          *
          * <p>Applications targeting this or a later release will get these
-         * new changes in behavior:</p>
+         * new changes in behavior. For more information about this release, see the
+         * <a href="/about/versions/pie/">Android 9 Pie overview</a>.</p>
          * <ul>
          * <li>{@link android.app.Service#startForeground Service.startForeground} requires
          * that apps hold the permission
@@ -912,6 +930,7 @@
          * <li>{@link android.widget.LinearLayout} will always remeasure weighted children,
          * even if there is no excess space.</li>
          * </ul>
+         *
          */
         public static final int P = 28;
 
@@ -1109,8 +1128,7 @@
      * @removed
      */
     @SystemApi
-    public static final boolean PERMISSIONS_REVIEW_REQUIRED =
-            SystemProperties.getInt("ro.permission_review_required", 0) == 1;
+    public static final boolean PERMISSIONS_REVIEW_REQUIRED = true;
 
     /**
      * Returns the version string for the radio firmware.  May return
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 213260f..e32ed9d 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -33,6 +33,8 @@
 public class Environment {
     private static final String TAG = "Environment";
 
+    // NOTE: keep credential-protected paths in sync with StrictMode.java
+
     private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE";
     private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
     private static final String ENV_ANDROID_DATA = "ANDROID_DATA";
@@ -149,6 +151,7 @@
     }
 
     /** {@hide} */
+    @TestApi
     public static File getStorageDirectory() {
         return DIR_ANDROID_STORAGE;
     }
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 88d6e84..3a3d9ea 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -53,32 +53,35 @@
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.Objects;
+import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Pattern;
 import java.util.zip.CRC32;
 import java.util.zip.CheckedInputStream;
 
 /**
- * Tools for managing files.  Not for public consumption.
- * @hide
+ * Utility methods useful for working with files.
  */
 public class FileUtils {
     private static final String TAG = "FileUtils";
 
-    public static final int S_IRWXU = 00700;
-    public static final int S_IRUSR = 00400;
-    public static final int S_IWUSR = 00200;
-    public static final int S_IXUSR = 00100;
+    /** {@hide} */ public static final int S_IRWXU = 00700;
+    /** {@hide} */ public static final int S_IRUSR = 00400;
+    /** {@hide} */ public static final int S_IWUSR = 00200;
+    /** {@hide} */ public static final int S_IXUSR = 00100;
 
-    public static final int S_IRWXG = 00070;
-    public static final int S_IRGRP = 00040;
-    public static final int S_IWGRP = 00020;
-    public static final int S_IXGRP = 00010;
+    /** {@hide} */ public static final int S_IRWXG = 00070;
+    /** {@hide} */ public static final int S_IRGRP = 00040;
+    /** {@hide} */ public static final int S_IWGRP = 00020;
+    /** {@hide} */ public static final int S_IXGRP = 00010;
 
-    public static final int S_IRWXO = 00007;
-    public static final int S_IROTH = 00004;
-    public static final int S_IWOTH = 00002;
-    public static final int S_IXOTH = 00001;
+    /** {@hide} */ public static final int S_IRWXO = 00007;
+    /** {@hide} */ public static final int S_IROTH = 00004;
+    /** {@hide} */ public static final int S_IWOTH = 00002;
+    /** {@hide} */ public static final int S_IXOTH = 00001;
+
+    private FileUtils() {
+    }
 
     /** Regular expression for safe filenames: no spaces or metacharacters.
       *
@@ -90,10 +93,14 @@
 
     private static final File[] EMPTY = new File[0];
 
-    private static final boolean ENABLE_COPY_OPTIMIZATIONS = true;
+    // non-final so it can be toggled by Robolectric's ShadowFileUtils
+    private static boolean sEnableCopyOptimizations = true;
 
     private static final long COPY_CHECKPOINT_BYTES = 524288;
 
+    /**
+     * Listener that is called periodically as progress is made.
+     */
     public interface ProgressListener {
         public void onProgress(long progress);
     }
@@ -105,6 +112,7 @@
      * @param uid to apply through {@code chown}, or -1 to leave unchanged
      * @param gid to apply through {@code chown}, or -1 to leave unchanged
      * @return 0 on success, otherwise errno.
+     * @hide
      */
     public static int setPermissions(File path, int mode, int uid, int gid) {
         return setPermissions(path.getAbsolutePath(), mode, uid, gid);
@@ -117,6 +125,7 @@
      * @param uid to apply through {@code chown}, or -1 to leave unchanged
      * @param gid to apply through {@code chown}, or -1 to leave unchanged
      * @return 0 on success, otherwise errno.
+     * @hide
      */
     public static int setPermissions(String path, int mode, int uid, int gid) {
         try {
@@ -145,6 +154,7 @@
      * @param uid to apply through {@code chown}, or -1 to leave unchanged
      * @param gid to apply through {@code chown}, or -1 to leave unchanged
      * @return 0 on success, otherwise errno.
+     * @hide
      */
     public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) {
         try {
@@ -166,7 +176,14 @@
         return 0;
     }
 
-    public static void copyPermissions(File from, File to) throws IOException {
+    /**
+     * Copy the owner UID, owner GID, and mode bits from one file to another.
+     *
+     * @param from File where attributes should be copied from.
+     * @param to File where attributes should be copied to.
+     * @hide
+     */
+    public static void copyPermissions(@NonNull File from, @NonNull File to) throws IOException {
         try {
             final StructStat stat = Os.stat(from.getAbsolutePath());
             Os.chmod(to.getAbsolutePath(), stat.st_mode);
@@ -177,8 +194,10 @@
     }
 
     /**
-     * Return owning UID of given path, otherwise -1.
+     * @deprecated use {@link Os#stat(String)} instead.
+     * @hide
      */
+    @Deprecated
     public static int getUid(String path) {
         try {
             return Os.stat(path).st_uid;
@@ -190,6 +209,8 @@
     /**
      * Perform an fsync on the given FileOutputStream.  The stream at this
      * point must be flushed but not yet closed.
+     *
+     * @hide
      */
     public static boolean sync(FileOutputStream stream) {
         try {
@@ -204,6 +225,7 @@
 
     /**
      * @deprecated use {@link #copy(File, File)} instead.
+     * @hide
      */
     @Deprecated
     public static boolean copyFile(File srcFile, File destFile) {
@@ -217,6 +239,7 @@
 
     /**
      * @deprecated use {@link #copy(File, File)} instead.
+     * @hide
      */
     @Deprecated
     public static void copyFileOrThrow(File srcFile, File destFile) throws IOException {
@@ -227,6 +250,7 @@
 
     /**
      * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+     * @hide
      */
     @Deprecated
     public static boolean copyToFile(InputStream inputStream, File destFile) {
@@ -240,6 +264,7 @@
 
     /**
      * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+     * @hide
      */
     @Deprecated
     public static void copyToFileOrThrow(InputStream in, File destFile) throws IOException {
@@ -265,7 +290,7 @@
      * @return number of bytes copied.
      */
     public static long copy(@NonNull File from, @NonNull File to) throws IOException {
-        return copy(from, to, null, null);
+        return copy(from, to, null, null, null);
     }
 
     /**
@@ -274,16 +299,17 @@
      * Attempts to use several optimization strategies to copy the data in the
      * kernel before falling back to a userspace copy as a last resort.
      *
-     * @param listener to be periodically notified as the copy progresses.
      * @param signal to signal if the copy should be cancelled early.
+     * @param executor that listener events should be delivered via.
+     * @param listener to be periodically notified as the copy progresses.
      * @return number of bytes copied.
      */
     public static long copy(@NonNull File from, @NonNull File to,
-            @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
-            throws IOException {
+            @Nullable CancellationSignal signal, @Nullable Executor executor,
+            @Nullable ProgressListener listener) throws IOException {
         try (FileInputStream in = new FileInputStream(from);
                 FileOutputStream out = new FileOutputStream(to)) {
-            return copy(in, out, listener, signal);
+            return copy(in, out, signal, executor, listener);
         }
     }
 
@@ -296,7 +322,7 @@
      * @return number of bytes copied.
      */
     public static long copy(@NonNull InputStream in, @NonNull OutputStream out) throws IOException {
-        return copy(in, out, null, null);
+        return copy(in, out, null, null, null);
     }
 
     /**
@@ -305,22 +331,23 @@
      * Attempts to use several optimization strategies to copy the data in the
      * kernel before falling back to a userspace copy as a last resort.
      *
-     * @param listener to be periodically notified as the copy progresses.
      * @param signal to signal if the copy should be cancelled early.
+     * @param executor that listener events should be delivered via.
+     * @param listener to be periodically notified as the copy progresses.
      * @return number of bytes copied.
      */
     public static long copy(@NonNull InputStream in, @NonNull OutputStream out,
-            @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
-            throws IOException {
-        if (ENABLE_COPY_OPTIMIZATIONS) {
+            @Nullable CancellationSignal signal, @Nullable Executor executor,
+            @Nullable ProgressListener listener) throws IOException {
+        if (sEnableCopyOptimizations) {
             if (in instanceof FileInputStream && out instanceof FileOutputStream) {
                 return copy(((FileInputStream) in).getFD(), ((FileOutputStream) out).getFD(),
-                        listener, signal);
+                        signal, executor, listener);
             }
         }
 
         // Worse case fallback to userspace
-        return copyInternalUserspace(in, out, listener, signal);
+        return copyInternalUserspace(in, out, signal, executor, listener);
     }
 
     /**
@@ -333,7 +360,7 @@
      */
     public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out)
             throws IOException {
-        return copy(in, out, null, null);
+        return copy(in, out, null, null, null);
     }
 
     /**
@@ -342,14 +369,15 @@
      * Attempts to use several optimization strategies to copy the data in the
      * kernel before falling back to a userspace copy as a last resort.
      *
-     * @param listener to be periodically notified as the copy progresses.
      * @param signal to signal if the copy should be cancelled early.
+     * @param executor that listener events should be delivered via.
+     * @param listener to be periodically notified as the copy progresses.
      * @return number of bytes copied.
      */
     public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
-            @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
-            throws IOException {
-        return copy(in, out, listener, signal, Long.MAX_VALUE);
+            @Nullable CancellationSignal signal, @Nullable Executor executor,
+            @Nullable ProgressListener listener) throws IOException {
+        return copy(in, out, Long.MAX_VALUE, signal, executor, listener);
     }
 
     /**
@@ -358,22 +386,24 @@
      * Attempts to use several optimization strategies to copy the data in the
      * kernel before falling back to a userspace copy as a last resort.
      *
-     * @param listener to be periodically notified as the copy progresses.
-     * @param signal to signal if the copy should be cancelled early.
      * @param count the number of bytes to copy.
+     * @param signal to signal if the copy should be cancelled early.
+     * @param executor that listener events should be delivered via.
+     * @param listener to be periodically notified as the copy progresses.
      * @return number of bytes copied.
+     * @hide
      */
-    public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
-            @Nullable ProgressListener listener, @Nullable CancellationSignal signal, long count)
-            throws IOException {
-        if (ENABLE_COPY_OPTIMIZATIONS) {
+    public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out, long count,
+            @Nullable CancellationSignal signal, @Nullable Executor executor,
+            @Nullable ProgressListener listener) throws IOException {
+        if (sEnableCopyOptimizations) {
             try {
                 final StructStat st_in = Os.fstat(in);
                 final StructStat st_out = Os.fstat(out);
                 if (S_ISREG(st_in.st_mode) && S_ISREG(st_out.st_mode)) {
-                    return copyInternalSendfile(in, out, listener, signal, count);
+                    return copyInternalSendfile(in, out, count, signal, executor, listener);
                 } else if (S_ISFIFO(st_in.st_mode) || S_ISFIFO(st_out.st_mode)) {
-                    return copyInternalSplice(in, out, listener, signal, count);
+                    return copyInternalSplice(in, out, count, signal, executor, listener);
                 }
             } catch (ErrnoException e) {
                 throw e.rethrowAsIOException();
@@ -381,15 +411,17 @@
         }
 
         // Worse case fallback to userspace
-        return copyInternalUserspace(in, out, listener, signal, count);
+        return copyInternalUserspace(in, out, count, signal, executor, listener);
     }
 
     /**
      * Requires one of input or output to be a pipe.
+     *
+     * @hide
      */
     @VisibleForTesting
-    public static long copyInternalSplice(FileDescriptor in, FileDescriptor out,
-            ProgressListener listener, CancellationSignal signal, long count)
+    public static long copyInternalSplice(FileDescriptor in, FileDescriptor out, long count,
+            CancellationSignal signal, Executor executor, ProgressListener listener)
             throws ErrnoException {
         long progress = 0;
         long checkpoint = 0;
@@ -405,24 +437,32 @@
                 if (signal != null) {
                     signal.throwIfCanceled();
                 }
-                if (listener != null) {
-                    listener.onProgress(progress);
+                if (executor != null && listener != null) {
+                    final long progressSnapshot = progress;
+                    executor.execute(() -> {
+                        listener.onProgress(progressSnapshot);
+                    });
                 }
                 checkpoint = 0;
             }
         }
-        if (listener != null) {
-            listener.onProgress(progress);
+        if (executor != null && listener != null) {
+            final long progressSnapshot = progress;
+            executor.execute(() -> {
+                listener.onProgress(progressSnapshot);
+            });
         }
         return progress;
     }
 
     /**
      * Requires both input and output to be a regular file.
+     *
+     * @hide
      */
     @VisibleForTesting
-    public static long copyInternalSendfile(FileDescriptor in, FileDescriptor out,
-            ProgressListener listener, CancellationSignal signal, long count)
+    public static long copyInternalSendfile(FileDescriptor in, FileDescriptor out, long count,
+            CancellationSignal signal, Executor executor, ProgressListener listener)
             throws ErrnoException {
         long progress = 0;
         long checkpoint = 0;
@@ -437,33 +477,52 @@
                 if (signal != null) {
                     signal.throwIfCanceled();
                 }
-                if (listener != null) {
-                    listener.onProgress(progress);
+                if (executor != null && listener != null) {
+                    final long progressSnapshot = progress;
+                    executor.execute(() -> {
+                        listener.onProgress(progressSnapshot);
+                    });
                 }
                 checkpoint = 0;
             }
         }
-        if (listener != null) {
-            listener.onProgress(progress);
+        if (executor != null && listener != null) {
+            final long progressSnapshot = progress;
+            executor.execute(() -> {
+                listener.onProgress(progressSnapshot);
+            });
         }
         return progress;
     }
 
+    /** {@hide} */
+    @Deprecated
     @VisibleForTesting
     public static long copyInternalUserspace(FileDescriptor in, FileDescriptor out,
-            ProgressListener listener, CancellationSignal signal, long count) throws IOException {
+            ProgressListener listener, CancellationSignal signal, long count)
+            throws IOException {
+        return copyInternalUserspace(in, out, count, signal, Runnable::run, listener);
+    }
+
+    /** {@hide} */
+    @VisibleForTesting
+    public static long copyInternalUserspace(FileDescriptor in, FileDescriptor out, long count,
+            CancellationSignal signal, Executor executor, ProgressListener listener)
+            throws IOException {
         if (count != Long.MAX_VALUE) {
             return copyInternalUserspace(new SizedInputStream(new FileInputStream(in), count),
-                    new FileOutputStream(out), listener, signal);
+                    new FileOutputStream(out), signal, executor, listener);
         } else {
             return copyInternalUserspace(new FileInputStream(in),
-                    new FileOutputStream(out), listener, signal);
+                    new FileOutputStream(out), signal, executor, listener);
         }
     }
 
+    /** {@hide} */
     @VisibleForTesting
     public static long copyInternalUserspace(InputStream in, OutputStream out,
-            ProgressListener listener, CancellationSignal signal) throws IOException {
+            CancellationSignal signal, Executor executor, ProgressListener listener)
+            throws IOException {
         long progress = 0;
         long checkpoint = 0;
         byte[] buffer = new byte[8192];
@@ -479,14 +538,20 @@
                 if (signal != null) {
                     signal.throwIfCanceled();
                 }
-                if (listener != null) {
-                    listener.onProgress(progress);
+                if (executor != null && listener != null) {
+                    final long progressSnapshot = progress;
+                    executor.execute(() -> {
+                        listener.onProgress(progressSnapshot);
+                    });
                 }
                 checkpoint = 0;
             }
         }
-        if (listener != null) {
-            listener.onProgress(progress);
+        if (executor != null && listener != null) {
+            final long progressSnapshot = progress;
+            executor.execute(() -> {
+                listener.onProgress(progressSnapshot);
+            });
         }
         return progress;
     }
@@ -494,6 +559,7 @@
     /**
      * Check if a filename is "safe" (no metacharacters or spaces).
      * @param file  The file to check
+     * @hide
      */
     public static boolean isFilenameSafe(File file) {
         // Note, we check whether it matches what's known to be safe,
@@ -509,6 +575,7 @@
      * @param ellipsis to add of the file was truncated (can be null)
      * @return the contents of the file, possibly truncated
      * @throws IOException if something goes wrong reading the file
+     * @hide
      */
     public static String readTextFile(File file, int max, String ellipsis) throws IOException {
         InputStream input = new FileInputStream(file);
@@ -563,13 +630,16 @@
         }
     }
 
+    /** {@hide} */
     public static void stringToFile(File file, String string) throws IOException {
         stringToFile(file.getAbsolutePath(), string);
     }
 
-    /*
+    /**
      * Writes the bytes given in {@code content} to the file whose absolute path
      * is {@code filename}.
+     *
+     * @hide
      */
     public static void bytesToFile(String filename, byte[] content) throws IOException {
         if (filename.startsWith("/proc/")) {
@@ -592,18 +662,23 @@
      * @param filename
      * @param string
      * @throws IOException
+     * @hide
      */
     public static void stringToFile(String filename, String string) throws IOException {
         bytesToFile(filename, string.getBytes(StandardCharsets.UTF_8));
     }
 
     /**
-     * Computes the checksum of a file using the CRC32 checksum routine.
-     * The value of the checksum is returned.
+     * Computes the checksum of a file using the CRC32 checksum routine. The
+     * value of the checksum is returned.
      *
-     * @param file  the file to checksum, must not be null
+     * @param file the file to checksum, must not be null
      * @return the checksum value or an exception is thrown.
+     * @deprecated this is a weak hashing algorithm, and should not be used due
+     *             to its potential for collision.
+     * @hide
      */
+    @Deprecated
     public static long checksumCrc32(File file) throws FileNotFoundException, IOException {
         CRC32 checkSummer = new CRC32();
         CheckedInputStream cis = null;
@@ -632,6 +707,7 @@
      * @param minCount Always keep at least this many files.
      * @param minAgeMs Always keep files younger than this age, in milliseconds.
      * @return if any files were deleted.
+     * @hide
      */
     public static boolean deleteOlderFiles(File dir, int minCount, long minAgeMs) {
         if (minCount < 0 || minAgeMs < 0) {
@@ -673,6 +749,8 @@
      * Both files <em>must</em> have been resolved using
      * {@link File#getCanonicalFile()} to avoid symlink or path traversal
      * attacks.
+     *
+     * @hide
      */
     public static boolean contains(File[] dirs, File file) {
         for (File dir : dirs) {
@@ -690,12 +768,15 @@
      * Both files <em>must</em> have been resolved using
      * {@link File#getCanonicalFile()} to avoid symlink or path traversal
      * attacks.
+     *
+     * @hide
      */
     public static boolean contains(File dir, File file) {
         if (dir == null || file == null) return false;
         return contains(dir.getAbsolutePath(), file.getAbsolutePath());
     }
 
+    /** {@hide} */
     public static boolean contains(String dirPath, String filePath) {
         if (dirPath.equals(filePath)) {
             return true;
@@ -706,6 +787,7 @@
         return filePath.startsWith(dirPath);
     }
 
+    /** {@hide} */
     public static boolean deleteContentsAndDir(File dir) {
         if (deleteContents(dir)) {
             return dir.delete();
@@ -714,6 +796,7 @@
         }
     }
 
+    /** {@hide} */
     public static boolean deleteContents(File dir) {
         File[] files = dir.listFiles();
         boolean success = true;
@@ -743,6 +826,8 @@
 
     /**
      * Check if given filename is valid for an ext4 filesystem.
+     *
+     * @hide
      */
     public static boolean isValidExtFilename(String name) {
         return (name != null) && name.equals(buildValidExtFilename(name));
@@ -751,6 +836,8 @@
     /**
      * Mutate the given filename to make it valid for an ext4 filesystem,
      * replacing any invalid characters with "_".
+     *
+     * @hide
      */
     public static String buildValidExtFilename(String name) {
         if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
@@ -792,6 +879,8 @@
 
     /**
      * Check if given filename is valid for a FAT filesystem.
+     *
+     * @hide
      */
     public static boolean isValidFatFilename(String name) {
         return (name != null) && name.equals(buildValidFatFilename(name));
@@ -800,6 +889,8 @@
     /**
      * Mutate the given filename to make it valid for a FAT filesystem,
      * replacing any invalid characters with "_".
+     *
+     * @hide
      */
     public static String buildValidFatFilename(String name) {
         if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
@@ -820,6 +911,7 @@
         return res.toString();
     }
 
+    /** {@hide} */
     @VisibleForTesting
     public static String trimFilename(String str, int maxBytes) {
         final StringBuilder res = new StringBuilder(str);
@@ -827,6 +919,7 @@
         return res.toString();
     }
 
+    /** {@hide} */
     private static void trimFilename(StringBuilder res, int maxBytes) {
         byte[] raw = res.toString().getBytes(StandardCharsets.UTF_8);
         if (raw.length > maxBytes) {
@@ -839,12 +932,14 @@
         }
     }
 
+    /** {@hide} */
     public static String rewriteAfterRename(File beforeDir, File afterDir, String path) {
         if (path == null) return null;
         final File result = rewriteAfterRename(beforeDir, afterDir, new File(path));
         return (result != null) ? result.getAbsolutePath() : null;
     }
 
+    /** {@hide} */
     public static String[] rewriteAfterRename(File beforeDir, File afterDir, String[] paths) {
         if (paths == null) return null;
         final String[] result = new String[paths.length];
@@ -858,6 +953,8 @@
      * Given a path under the "before" directory, rewrite it to live under the
      * "after" directory. For example, {@code /before/foo/bar.txt} would become
      * {@code /after/foo/bar.txt}.
+     *
+     * @hide
      */
     public static File rewriteAfterRename(File beforeDir, File afterDir, File file) {
         if (file == null || beforeDir == null || afterDir == null) return null;
@@ -869,6 +966,7 @@
         return null;
     }
 
+    /** {@hide} */
     private static File buildUniqueFileWithExtension(File parent, String name, String ext)
             throws FileNotFoundException {
         File file = buildFile(parent, name, ext);
@@ -895,6 +993,7 @@
      * 'example.txt' or 'example (1).txt', etc.
      *
      * @throws FileNotFoundException
+     * @hide
      */
     public static File buildUniqueFile(File parent, String mimeType, String displayName)
             throws FileNotFoundException {
@@ -905,6 +1004,8 @@
     /**
      * Generates a unique file name under the given parent directory, keeping
      * any extension intact.
+     *
+     * @hide
      */
     public static File buildUniqueFile(File parent, String displayName)
             throws FileNotFoundException {
@@ -929,6 +1030,8 @@
      * If the display name doesn't have an extension that matches the requested MIME type, the
      * extension is regarded as a part of filename and default extension for that MIME type is
      * appended.
+     *
+     * @hide
      */
     public static String[] splitFileName(String mimeType, String displayName) {
         String name;
@@ -975,6 +1078,7 @@
         return new String[] { name, ext };
     }
 
+    /** {@hide} */
     private static File buildFile(File parent, String name, String ext) {
         if (TextUtils.isEmpty(ext)) {
             return new File(parent, name);
@@ -983,6 +1087,7 @@
         }
     }
 
+    /** {@hide} */
     public static @NonNull String[] listOrEmpty(@Nullable File dir) {
         if (dir == null) return EmptyArray.STRING;
         final String[] res = dir.list();
@@ -993,6 +1098,7 @@
         }
     }
 
+    /** {@hide} */
     public static @NonNull File[] listFilesOrEmpty(@Nullable File dir) {
         if (dir == null) return EMPTY;
         final File[] res = dir.listFiles();
@@ -1003,6 +1109,7 @@
         }
     }
 
+    /** {@hide} */
     public static @NonNull File[] listFilesOrEmpty(@Nullable File dir, FilenameFilter filter) {
         if (dir == null) return EMPTY;
         final File[] res = dir.listFiles(filter);
@@ -1013,6 +1120,7 @@
         }
     }
 
+    /** {@hide} */
     public static @Nullable File newFileOrNull(@Nullable String path) {
         return (path != null) ? new File(path) : null;
     }
@@ -1021,6 +1129,8 @@
      * Creates a directory with name {@code name} under an existing directory {@code baseDir}.
      * Returns a {@code File} object representing the directory on success, {@code null} on
      * failure.
+     *
+     * @hide
      */
     public static @Nullable File createDir(File baseDir, String name) {
         final File dir = new File(baseDir, name);
@@ -1036,6 +1146,8 @@
      * Round the given size of a storage device to a nice round power-of-two
      * value, such as 256MB or 32GB. This avoids showing weird values like
      * "29.5GB" in UI.
+     *
+     * @hide
      */
     public static long roundStorageSize(long size) {
         long val = 1;
@@ -1050,6 +1162,23 @@
         return val * pow;
     }
 
+    /**
+     * Closes the given object quietly, ignoring any checked exceptions. Does
+     * nothing if the given object is {@code null}.
+     */
+    public static void closeQuietly(@Nullable AutoCloseable closeable) {
+        IoUtils.closeQuietly(closeable);
+    }
+
+    /**
+     * Closes the given object quietly, ignoring any checked exceptions. Does
+     * nothing if the given object is {@code null}.
+     */
+    public static void closeQuietly(@Nullable FileDescriptor fd) {
+        IoUtils.closeQuietly(fd);
+    }
+
+    /** {@hide} */
     @VisibleForTesting
     public static class MemoryPipe extends Thread implements AutoCloseable {
         private final FileDescriptor[] pipe;
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index e59c3ae..e8b3ca6 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -23,7 +23,8 @@
 {
     boolean hasVibrator();
     boolean hasAmplitudeControl();
-    void vibrate(int uid, String opPkg, in VibrationEffect effect, int usageHint, IBinder token);
+    void vibrate(int uid, String opPkg, in VibrationEffect effect, int usageHint, String reason,
+            IBinder token);
     void cancelVibrate(IBinder token);
 }
 
diff --git a/core/java/android/os/IncidentReportArgs.java b/core/java/android/os/IncidentReportArgs.java
index 1aeac5f..3ca7f77 100644
--- a/core/java/android/os/IncidentReportArgs.java
+++ b/core/java/android/os/IncidentReportArgs.java
@@ -144,7 +144,6 @@
 
     /**
      * Set this incident report privacy policy spec.
-     * @hide
      */
     public void setPrivacyPolicy(int dest) {
         switch (dest) {
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index b1c33c2..7c2ecc5 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.os.MessageQueueProto;
 import android.util.Log;
 import android.util.Printer;
@@ -458,6 +459,7 @@
      *
      * @hide
      */
+    @TestApi
     public int postSyncBarrier() {
         return postSyncBarrier(SystemClock.uptimeMillis());
     }
@@ -501,6 +503,7 @@
      *
      * @hide
      */
+    @TestApi
     public void removeSyncBarrier(int token) {
         // Remove a sync barrier token from the queue.
         // If the queue is no longer stalled by a barrier then wake it.
diff --git a/core/java/android/os/NullVibrator.java b/core/java/android/os/NullVibrator.java
index b8bdc89..1d0f9d3 100644
--- a/core/java/android/os/NullVibrator.java
+++ b/core/java/android/os/NullVibrator.java
@@ -44,8 +44,8 @@
     }
 
     @Override
-    public void vibrate(int uid, String opPkg,
-            VibrationEffect effect, AudioAttributes attributes) {
+    public void vibrate(int uid, String opPkg, VibrationEffect effect,
+            String reason, AudioAttributes attributes) {
     }
 
     @Override
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 3d4ce61..7e7666a 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -39,7 +39,6 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
@@ -1465,7 +1464,6 @@
      * {@link #readParcelableList(List, ClassLoader)} if required.
      *
      * @see #readParcelableList(List, ClassLoader)
-     * @hide
      */
     public final <T extends Parcelable> void writeParcelableList(@Nullable List<T> val, int flags) {
         if (val == null) {
@@ -2101,19 +2099,6 @@
         }
     }
 
-    /** @deprecated use {@link android.system.Os#open(String, int, int)} */
-    @Deprecated
-    static native FileDescriptor openFileDescriptor(String file, int mode)
-            throws FileNotFoundException;
-
-    /** @deprecated use {@link android.system.Os#dup(FileDescriptor)} */
-    @Deprecated
-    static native FileDescriptor dupFileDescriptor(FileDescriptor orig) throws IOException;
-
-    /** @deprecated use {@link android.system.Os#close(FileDescriptor)} */
-    @Deprecated
-    static native void closeFileDescriptor(FileDescriptor desc) throws IOException;
-
     /**
      * Read a byte value from the parcel at the current dataPosition().
      */
@@ -2570,7 +2555,6 @@
      * list was {@code null}, {@code list} is cleared.
      *
      * @see #writeParcelableList(List, int)
-     * @hide
      */
     @NonNull
     public final <T extends Parcelable> List<T> readParcelableList(@NonNull List<T> list,
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 7556f09..c9edc53 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -188,7 +188,13 @@
         }
         mWrapped = null;
         mFd = fd;
+        IoUtils.setFdOwner(mFd, this);
+
         mCommFd = commChannel;
+        if (mCommFd != null) {
+            IoUtils.setFdOwner(mCommFd, this);
+        }
+
         mGuard.open("close");
     }
 
@@ -682,8 +688,7 @@
             if (mClosed) {
                 throw new IllegalStateException("Already closed");
             }
-            final int fd = getFd();
-            mFd.setInt$(-1);
+            int fd = IoUtils.acquireRawFd(mFd);
             writeCommStatusAndClose(Status.DETACHED, null);
             mClosed = true;
             mGuard.close();
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 9c25848..463a6aa 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -961,7 +961,6 @@
      *
      * @hide Requires signature permission.
      */
-    @TestApi
     public void nap(long time) {
         try {
             mService.nap(time);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 7ce7c92..7caf0b1 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -492,10 +492,11 @@
                                   String instructionSet,
                                   String appDataDir,
                                   String invokeWith,
+                                  String packageName,
                                   String[] zygoteArgs) {
         return zygoteProcess.start(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
-                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
+                    abi, instructionSet, appDataDir, invokeWith, packageName, zygoteArgs);
     }
 
     /** @hide */
@@ -509,10 +510,11 @@
                                   String instructionSet,
                                   String appDataDir,
                                   String invokeWith,
+                                  String packageName,
                                   String[] zygoteArgs) {
         return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
-                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
+                    abi, instructionSet, appDataDir, invokeWith, packageName, zygoteArgs);
     }
 
     /**
diff --git a/core/java/android/os/RedactingFileDescriptor.java b/core/java/android/os/RedactingFileDescriptor.java
new file mode 100644
index 0000000..60eb5c3
--- /dev/null
+++ b/core/java/android/os/RedactingFileDescriptor.java
@@ -0,0 +1,143 @@
+/*
+ * 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.Context;
+import android.os.storage.StorageManager;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Slog;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+
+/**
+ * Variant of {@link FileDescriptor} that allows its creator to specify regions
+ * that should be redacted (appearing as zeros to the reader).
+ *
+ * @hide
+ */
+public class RedactingFileDescriptor {
+    private static final String TAG = "RedactingFileDescriptor";
+    private static final boolean DEBUG = true;
+
+    private final long[] mRedactRanges;
+
+    private FileDescriptor mInner = null;
+    private ParcelFileDescriptor mOuter = null;
+
+    private RedactingFileDescriptor(Context context, File file, long[] redactRanges)
+            throws IOException {
+        mRedactRanges = checkRangesArgument(redactRanges);
+
+        try {
+            try {
+                mInner = Os.open(file.getAbsolutePath(), OsConstants.O_RDONLY, 0);
+                mOuter = context.getSystemService(StorageManager.class)
+                        .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_ONLY, mCallback);
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            }
+        } catch (IOException e) {
+            IoUtils.closeQuietly(mInner);
+            IoUtils.closeQuietly(mOuter);
+            throw e;
+        }
+    }
+
+    private static long[] checkRangesArgument(long[] ranges) {
+        if (ranges.length % 2 != 0) {
+            throw new IllegalArgumentException();
+        }
+        for (int i = 0; i < ranges.length - 1; i += 2) {
+            if (ranges[i] > ranges[i + 1]) {
+                throw new IllegalArgumentException();
+            }
+        }
+        return ranges;
+    }
+
+    /**
+     * Open the given {@link File} and returns a {@link ParcelFileDescriptor}
+     * that offers a redacted, read-only view of the underlying data.
+     *
+     * @param file The underlying file to open.
+     * @param redactRanges List of file offsets that should be redacted, stored
+     *            as {@code [start1, end1, start2, end2, ...]}. Start values are
+     *            inclusive and end values are exclusive.
+     */
+    public static ParcelFileDescriptor open(Context context, File file, long[] redactRanges)
+            throws IOException {
+        return new RedactingFileDescriptor(context, file, redactRanges).mOuter;
+    }
+
+    private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() {
+        @Override
+        public long onGetSize() throws ErrnoException {
+            return Os.fstat(mInner).st_size;
+        }
+
+        @Override
+        public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+            int n = 0;
+            while (n < size) {
+                try {
+                    final int res = Os.pread(mInner, data, n, size - n, offset + n);
+                    if (res == 0) {
+                        break;
+                    } else {
+                        n += res;
+                    }
+                } catch (InterruptedIOException e) {
+                    n += e.bytesTransferred;
+                }
+            }
+
+            // Redact any relevant ranges before returning
+            final long[] ranges = mRedactRanges;
+            for (int i = 0; i < ranges.length; i += 2) {
+                final long start = Math.max(offset, ranges[i]);
+                final long end = Math.min(offset + size, ranges[i + 1]);
+                for (long j = start; j < end; j++) {
+                    data[(int) (j - offset)] = 0;
+                }
+            }
+            return n;
+        }
+
+        @Override
+        public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+            throw new ErrnoException(TAG, OsConstants.EBADF);
+        }
+
+        @Override
+        public void onFsync() throws ErrnoException {
+            Os.fsync(mInner);
+        }
+
+        @Override
+        public void onRelease() {
+            if (DEBUG) Slog.v(TAG, "onRelease()");
+            IoUtils.closeQuietly(mInner);
+        }
+    };
+}
diff --git a/core/java/android/os/SELinux.java b/core/java/android/os/SELinux.java
index 2773da5..94441ca 100644
--- a/core/java/android/os/SELinux.java
+++ b/core/java/android/os/SELinux.java
@@ -18,9 +18,9 @@
 
 import android.util.Slog;
 
-import java.io.IOException;
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.IOException;
 
 /**
  * This class provides access to the centralized jni bindings for
@@ -79,6 +79,13 @@
     public static final native String getPeerContext(FileDescriptor fd);
 
     /**
+     * Get the security context of a file descriptor of a file.
+     * @param fd FileDescriptor of a file.
+     * @return a String representing the file descriptor security context.
+     */
+    public static final native String getFileContext(FileDescriptor fd);
+
+    /**
      * Gets the security context of the current process.
      * @return a String representing the security context of the current process.
      */
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index f224550..5ff6e55 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -16,6 +16,7 @@
 package android.os;
 
 import android.animation.ValueAnimator;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
@@ -27,15 +28,19 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.net.TrafficStats;
 import android.net.Uri;
+import android.os.storage.IStorageManager;
 import android.os.strictmode.CleartextNetworkViolation;
 import android.os.strictmode.ContentUriWithoutPermissionViolation;
+import android.os.strictmode.CredentialProtectedWhileLockedViolation;
 import android.os.strictmode.CustomViolation;
 import android.os.strictmode.DiskReadViolation;
 import android.os.strictmode.DiskWriteViolation;
 import android.os.strictmode.ExplicitGcViolation;
 import android.os.strictmode.FileUriExposedViolation;
+import android.os.strictmode.ImplicitDirectBootViolation;
 import android.os.strictmode.InstanceCountViolation;
 import android.os.strictmode.IntentReceiverLeakedViolation;
 import android.os.strictmode.LeakedClosableViolation;
@@ -68,6 +73,8 @@
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayDeque;
@@ -187,116 +194,105 @@
     // of the Looper.
     private static final int MAX_OFFENSES_PER_LOOP = 10;
 
-    // Byte 1: Thread-policy
+    /** @hide */
+    @IntDef(flag = true, prefix = { "DETECT_THREAD_", "PENALTY_" }, value = {
+            DETECT_THREAD_DISK_WRITE,
+            DETECT_THREAD_DISK_READ,
+            DETECT_THREAD_NETWORK,
+            DETECT_THREAD_CUSTOM,
+            DETECT_THREAD_RESOURCE_MISMATCH,
+            DETECT_THREAD_UNBUFFERED_IO,
+            DETECT_THREAD_EXPLICIT_GC,
+            PENALTY_GATHER,
+            PENALTY_LOG,
+            PENALTY_DIALOG,
+            PENALTY_DEATH,
+            PENALTY_FLASH,
+            PENALTY_DROPBOX,
+            PENALTY_DEATH_ON_NETWORK,
+            PENALTY_DEATH_ON_CLEARTEXT_NETWORK,
+            PENALTY_DEATH_ON_FILE_URI_EXPOSURE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ThreadPolicyMask {}
+
+    // Thread policy: bits 0-15
 
     /** @hide */
-    @TestApi public static final int DETECT_DISK_WRITE = 0x01; // for ThreadPolicy
-
+    private static final int DETECT_THREAD_DISK_WRITE = 1 << 0;
     /** @hide */
-    @TestApi public static final int DETECT_DISK_READ = 0x02; // for ThreadPolicy
-
+    private static final int DETECT_THREAD_DISK_READ = 1 << 1;
     /** @hide */
-    @TestApi public static final int DETECT_NETWORK = 0x04; // for ThreadPolicy
-
-    /**
-     * For StrictMode.noteSlowCall()
-     *
-     * @hide
-     */
-    @TestApi public static final int DETECT_CUSTOM = 0x08; // for ThreadPolicy
-
-    /**
-     * For StrictMode.noteResourceMismatch()
-     *
-     * @hide
-     */
-    @TestApi public static final int DETECT_RESOURCE_MISMATCH = 0x10; // for ThreadPolicy
-
+    private static final int DETECT_THREAD_NETWORK = 1 << 2;
     /** @hide */
-    @TestApi public static final int DETECT_UNBUFFERED_IO = 0x20; // for ThreadPolicy
-
+    private static final int DETECT_THREAD_CUSTOM = 1 << 3;
+    /** @hide */
+    private static final int DETECT_THREAD_RESOURCE_MISMATCH = 1 << 4;
+    /** @hide */
+    private static final int DETECT_THREAD_UNBUFFERED_IO = 1 << 5;
     /** @hide  */
-    public static final int DETECT_EXPLICIT_GC = 0x40;  // for ThreadPolicy
-
-    private static final int ALL_THREAD_DETECT_BITS =
-            DETECT_DISK_WRITE
-                    | DETECT_DISK_READ
-                    | DETECT_NETWORK
-                    | DETECT_CUSTOM
-                    | DETECT_RESOURCE_MISMATCH
-                    | DETECT_UNBUFFERED_IO
-                    | DETECT_EXPLICIT_GC;
-
-    // Byte 2: Process-policy
-
-    /**
-     * Note, a "VM_" bit, not thread.
-     *
-     * @hide
-     */
-    @TestApi public static final int DETECT_VM_CURSOR_LEAKS = 0x01 << 8; // for VmPolicy
-
-    /**
-     * Note, a "VM_" bit, not thread.
-     *
-     * @hide
-     */
-    @TestApi public static final int DETECT_VM_CLOSABLE_LEAKS = 0x02 << 8; // for VmPolicy
-
-    /**
-     * Note, a "VM_" bit, not thread.
-     *
-     * @hide
-     */
-    @TestApi public static final int DETECT_VM_ACTIVITY_LEAKS = 0x04 << 8; // for VmPolicy
+    private static final int DETECT_THREAD_EXPLICIT_GC = 1 << 6;
 
     /** @hide */
-    @TestApi public static final int DETECT_VM_INSTANCE_LEAKS = 0x08 << 8; // for VmPolicy
+    private static final int DETECT_THREAD_ALL = 0x0000ffff;
 
     /** @hide */
-    @TestApi public static final int DETECT_VM_REGISTRATION_LEAKS = 0x10 << 8; // for VmPolicy
+    @IntDef(flag = true, prefix = { "DETECT_THREAD_", "PENALTY_" }, value = {
+            DETECT_VM_CURSOR_LEAKS,
+            DETECT_VM_CLOSABLE_LEAKS,
+            DETECT_VM_ACTIVITY_LEAKS,
+            DETECT_VM_INSTANCE_LEAKS,
+            DETECT_VM_REGISTRATION_LEAKS,
+            DETECT_VM_FILE_URI_EXPOSURE,
+            DETECT_VM_CLEARTEXT_NETWORK,
+            DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION,
+            DETECT_VM_UNTAGGED_SOCKET,
+            DETECT_VM_NON_SDK_API_USAGE,
+            DETECT_VM_IMPLICIT_DIRECT_BOOT,
+            PENALTY_GATHER,
+            PENALTY_LOG,
+            PENALTY_DIALOG,
+            PENALTY_DEATH,
+            PENALTY_FLASH,
+            PENALTY_DROPBOX,
+            PENALTY_DEATH_ON_NETWORK,
+            PENALTY_DEATH_ON_CLEARTEXT_NETWORK,
+            PENALTY_DEATH_ON_FILE_URI_EXPOSURE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VmPolicyMask {}
+
+    // VM policy: bits 0-15
 
     /** @hide */
-    @TestApi public static final int DETECT_VM_FILE_URI_EXPOSURE = 0x20 << 8; // for VmPolicy
+    private static final int DETECT_VM_CURSOR_LEAKS = 1 << 0;
+    /** @hide */
+    private static final int DETECT_VM_CLOSABLE_LEAKS = 1 << 1;
+    /** @hide */
+    private static final int DETECT_VM_ACTIVITY_LEAKS = 1 << 2;
+    /** @hide */
+    private static final int DETECT_VM_INSTANCE_LEAKS = 1 << 3;
+    /** @hide */
+    private static final int DETECT_VM_REGISTRATION_LEAKS = 1 << 4;
+    /** @hide */
+    private static final int DETECT_VM_FILE_URI_EXPOSURE = 1 << 5;
+    /** @hide */
+    private static final int DETECT_VM_CLEARTEXT_NETWORK = 1 << 6;
+    /** @hide */
+    private static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 1 << 7;
+    /** @hide */
+    private static final int DETECT_VM_UNTAGGED_SOCKET = 1 << 8;
+    /** @hide */
+    private static final int DETECT_VM_NON_SDK_API_USAGE = 1 << 9;
+    /** @hide */
+    private static final int DETECT_VM_IMPLICIT_DIRECT_BOOT = 1 << 10;
+    /** @hide */
+    private static final int DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED = 1 << 11;
 
     /** @hide */
-    @TestApi public static final int DETECT_VM_CLEARTEXT_NETWORK = 0x40 << 8; // for VmPolicy
+    private static final int DETECT_VM_ALL = 0x0000ffff;
 
-    /** @hide */
-    @TestApi
-    public static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 0x80 << 8; // for VmPolicy
-
-    /** @hide */
-    @TestApi public static final int DETECT_VM_UNTAGGED_SOCKET = 0x80 << 24; // for VmPolicy
-
-    /** @hide */
-    @TestApi public static final int DETECT_VM_NON_SDK_API_USAGE = 0x40 << 24; // for VmPolicy
-
-    private static final int ALL_VM_DETECT_BITS =
-            DETECT_VM_CURSOR_LEAKS
-                    | DETECT_VM_CLOSABLE_LEAKS
-                    | DETECT_VM_ACTIVITY_LEAKS
-                    | DETECT_VM_INSTANCE_LEAKS
-                    | DETECT_VM_REGISTRATION_LEAKS
-                    | DETECT_VM_FILE_URI_EXPOSURE
-                    | DETECT_VM_CLEARTEXT_NETWORK
-                    | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION
-                    | DETECT_VM_UNTAGGED_SOCKET
-                    | DETECT_VM_NON_SDK_API_USAGE;
-
-
-    // Byte 3: Penalty
-
-    /** {@hide} */
-    public static final int PENALTY_LOG = 0x01 << 16; // normal android.util.Log
-    /** {@hide} */
-    public static final int PENALTY_DIALOG = 0x02 << 16;
-    /** {@hide} */
-    public static final int PENALTY_DEATH = 0x04 << 16;
-    /** {@hide} */
-    public static final int PENALTY_FLASH = 0x10 << 16;
-    /** {@hide} */
-    public static final int PENALTY_DROPBOX = 0x20 << 16;
+    // Penalty policy: bits 16-31
 
     /**
      * Non-public penalty mode which overrides all the other penalty bits and signals that we're in
@@ -307,50 +303,27 @@
      *
      * @hide
      */
-    public static final int PENALTY_GATHER = 0x40 << 16;
+    public static final int PENALTY_GATHER = 1 << 31;
 
-    // Byte 4: Special cases
+    /** {@hide} */
+    public static final int PENALTY_LOG = 1 << 30;
+    /** {@hide} */
+    public static final int PENALTY_DIALOG = 1 << 29;
+    /** {@hide} */
+    public static final int PENALTY_DEATH = 1 << 28;
+    /** {@hide} */
+    public static final int PENALTY_FLASH = 1 << 27;
+    /** {@hide} */
+    public static final int PENALTY_DROPBOX = 1 << 26;
+    /** {@hide} */
+    public static final int PENALTY_DEATH_ON_NETWORK = 1 << 25;
+    /** {@hide} */
+    public static final int PENALTY_DEATH_ON_CLEARTEXT_NETWORK = 1 << 24;
+    /** {@hide} */
+    public static final int PENALTY_DEATH_ON_FILE_URI_EXPOSURE = 1 << 23;
 
-    /**
-     * Death when network traffic is detected on main thread.
-     *
-     * @hide
-     */
-    public static final int PENALTY_DEATH_ON_NETWORK = 0x01 << 24;
-
-    /**
-     * Death when cleartext network traffic is detected.
-     *
-     * @hide
-     */
-    public static final int PENALTY_DEATH_ON_CLEARTEXT_NETWORK = 0x02 << 24;
-
-    /**
-     * Death when file exposure is detected.
-     *
-     * @hide
-     */
-    public static final int PENALTY_DEATH_ON_FILE_URI_EXPOSURE = 0x04 << 24;
-
-    // CAUTION: we started stealing the top bits of Byte 4 for VM above
-
-    /** Mask of all the penalty bits valid for thread policies. */
-    private static final int THREAD_PENALTY_MASK =
-            PENALTY_LOG
-                    | PENALTY_DIALOG
-                    | PENALTY_DEATH
-                    | PENALTY_DROPBOX
-                    | PENALTY_GATHER
-                    | PENALTY_DEATH_ON_NETWORK
-                    | PENALTY_FLASH;
-
-    /** Mask of all the penalty bits valid for VM policies. */
-    private static final int VM_PENALTY_MASK =
-            PENALTY_LOG
-                    | PENALTY_DEATH
-                    | PENALTY_DROPBOX
-                    | PENALTY_DEATH_ON_CLEARTEXT_NETWORK
-                    | PENALTY_DEATH_ON_FILE_URI_EXPOSURE;
+    /** @hide */
+    public static final int PENALTY_ALL = 0xffff0000;
 
     /** {@hide} */
     public static final int NETWORK_POLICY_ACCEPT = 0;
@@ -448,11 +421,12 @@
         /** The default, lax policy which doesn't catch anything. */
         public static final ThreadPolicy LAX = new ThreadPolicy(0, null, null);
 
-        final int mask;
+        final @ThreadPolicyMask int mask;
         final OnThreadViolationListener mListener;
         final Executor mCallbackExecutor;
 
-        private ThreadPolicy(int mask, OnThreadViolationListener listener, Executor executor) {
+        private ThreadPolicy(@ThreadPolicyMask int mask, OnThreadViolationListener listener,
+                Executor executor) {
             this.mask = mask;
             mListener = listener;
             mCallbackExecutor = executor;
@@ -482,7 +456,7 @@
          * </pre>
          */
         public static final class Builder {
-            private int mMask = 0;
+            private @ThreadPolicyMask int mMask = 0;
             private OnThreadViolationListener mListener;
             private Executor mExecutor;
 
@@ -528,52 +502,52 @@
 
             /** Disable the detection of everything. */
             public Builder permitAll() {
-                return disable(ALL_THREAD_DETECT_BITS);
+                return disable(DETECT_THREAD_ALL);
             }
 
             /** Enable detection of network operations. */
             public Builder detectNetwork() {
-                return enable(DETECT_NETWORK);
+                return enable(DETECT_THREAD_NETWORK);
             }
 
             /** Disable detection of network operations. */
             public Builder permitNetwork() {
-                return disable(DETECT_NETWORK);
+                return disable(DETECT_THREAD_NETWORK);
             }
 
             /** Enable detection of disk reads. */
             public Builder detectDiskReads() {
-                return enable(DETECT_DISK_READ);
+                return enable(DETECT_THREAD_DISK_READ);
             }
 
             /** Disable detection of disk reads. */
             public Builder permitDiskReads() {
-                return disable(DETECT_DISK_READ);
+                return disable(DETECT_THREAD_DISK_READ);
             }
 
             /** Enable detection of slow calls. */
             public Builder detectCustomSlowCalls() {
-                return enable(DETECT_CUSTOM);
+                return enable(DETECT_THREAD_CUSTOM);
             }
 
             /** Disable detection of slow calls. */
             public Builder permitCustomSlowCalls() {
-                return disable(DETECT_CUSTOM);
+                return disable(DETECT_THREAD_CUSTOM);
             }
 
             /** Disable detection of mismatches between defined resource types and getter calls. */
             public Builder permitResourceMismatches() {
-                return disable(DETECT_RESOURCE_MISMATCH);
+                return disable(DETECT_THREAD_RESOURCE_MISMATCH);
             }
 
             /** Detect unbuffered input/output operations. */
             public Builder detectUnbufferedIo() {
-                return enable(DETECT_UNBUFFERED_IO);
+                return enable(DETECT_THREAD_UNBUFFERED_IO);
             }
 
             /** Disable detection of unbuffered input/output operations. */
             public Builder permitUnbufferedIo() {
-                return disable(DETECT_UNBUFFERED_IO);
+                return disable(DETECT_THREAD_UNBUFFERED_IO);
             }
 
             /**
@@ -589,17 +563,17 @@
              * resource as an integer to avoid unnecessary type conversion.
              */
             public Builder detectResourceMismatches() {
-                return enable(DETECT_RESOURCE_MISMATCH);
+                return enable(DETECT_THREAD_RESOURCE_MISMATCH);
             }
 
             /** Enable detection of disk writes. */
             public Builder detectDiskWrites() {
-                return enable(DETECT_DISK_WRITE);
+                return enable(DETECT_THREAD_DISK_WRITE);
             }
 
             /** Disable detection of disk writes. */
             public Builder permitDiskWrites() {
-                return disable(DETECT_DISK_WRITE);
+                return disable(DETECT_THREAD_DISK_WRITE);
             }
 
             /**
@@ -607,12 +581,13 @@
              *
              * @hide
              */
+            @TestApi
             public Builder detectExplicitGc() {
                 // TODO(b/3400644): Un-hide this for next API update
                 // TODO(b/3400644): Un-hide ExplicitGcViolation for next API update
                 // TODO(b/3400644): Make DETECT_EXPLICIT_GC a @TestApi for next API update
                 // TODO(b/3400644): Call this from detectAll in next API update
-                return enable(DETECT_EXPLICIT_GC);
+                return enable(DETECT_THREAD_EXPLICIT_GC);
             }
 
             /**
@@ -622,7 +597,7 @@
              */
             public Builder permitExplicitGc() {
                 // TODO(b/3400644): Un-hide this for next API update
-                return disable(DETECT_EXPLICIT_GC);
+                return disable(DETECT_THREAD_EXPLICIT_GC);
             }
 
             /**
@@ -695,13 +670,13 @@
                 return penaltyListener(executor, listener);
             }
 
-            private Builder enable(int bit) {
-                mMask |= bit;
+            private Builder enable(@ThreadPolicyMask int mask) {
+                mMask |= mask;
                 return this;
             }
 
-            private Builder disable(int bit) {
-                mMask &= ~bit;
+            private Builder disable(@ThreadPolicyMask int mask) {
+                mMask &= ~mask;
                 return this;
             }
 
@@ -738,7 +713,7 @@
         /** The default, lax policy which doesn't catch anything. */
         public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP, null, null);
 
-        final int mask;
+        final @VmPolicyMask int mask;
         final OnVmViolationListener mListener;
         final Executor mCallbackExecutor;
 
@@ -746,7 +721,7 @@
         final HashMap<Class, Integer> classInstanceLimit;
 
         private VmPolicy(
-                int mask,
+                @VmPolicyMask int mask,
                 HashMap<Class, Integer> classInstanceLimit,
                 OnVmViolationListener listener,
                 Executor executor) {
@@ -783,7 +758,7 @@
          * </pre>
          */
         public static final class Builder {
-            private int mMask;
+            private @VmPolicyMask int mMask;
             private OnVmViolationListener mListener;
             private Executor mExecutor;
 
@@ -889,8 +864,13 @@
                     detectContentUriWithoutPermission();
                     detectUntaggedSockets();
                 }
+                if (targetSdk >= Build.VERSION_CODES.Q) {
+                    detectCredentialProtectedWhileLocked();
+                }
 
                 // TODO: Decide whether to detect non SDK API usage beyond a certain API level.
+                // TODO: enable detectImplicitDirectBoot() once system is less noisy
+
                 return this;
             }
 
@@ -999,6 +979,51 @@
             }
 
             /**
+             * Detect any implicit reliance on Direct Boot automatic filtering
+             * of {@link PackageManager} values. Violations are only triggered
+             * when implicit calls are made while the user is locked.
+             * <p>
+             * Apps becoming Direct Boot aware need to carefully inspect each
+             * query site and explicitly decide which combination of flags they
+             * want to use:
+             * <ul>
+             * <li>{@link PackageManager#MATCH_DIRECT_BOOT_AWARE}
+             * <li>{@link PackageManager#MATCH_DIRECT_BOOT_UNAWARE}
+             * <li>{@link PackageManager#MATCH_DIRECT_BOOT_AUTO}
+             * </ul>
+             */
+            public Builder detectImplicitDirectBoot() {
+                return enable(DETECT_VM_IMPLICIT_DIRECT_BOOT);
+            }
+
+            /** @hide */
+            public Builder permitImplicitDirectBoot() {
+                return disable(DETECT_VM_IMPLICIT_DIRECT_BOOT);
+            }
+
+            /**
+             * Detect access to filesystem paths stored in credential protected
+             * storage areas while the user is locked.
+             * <p>
+             * When a user is locked, credential protected storage is
+             * unavailable, and files stored in these locations appear to not
+             * exist, which can result in subtle app bugs if they assume default
+             * behaviors or empty states. Instead, apps should store data needed
+             * while a user is locked under device protected storage areas.
+             *
+             * @see Context#createCredentialProtectedStorageContext()
+             * @see Context#createDeviceProtectedStorageContext()
+             */
+            public Builder detectCredentialProtectedWhileLocked() {
+                return enable(DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED);
+            }
+
+            /** @hide */
+            public Builder permitCredentialProtectedWhileLocked() {
+                return disable(DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED);
+            }
+
+            /**
              * Crashes the whole process on violation. This penalty runs at the end of all enabled
              * penalties so you'll still get your logging or other violations before the process
              * dies.
@@ -1059,13 +1084,13 @@
                 return penaltyListener(executor, listener);
             }
 
-            private Builder enable(int bit) {
-                mMask |= bit;
+            private Builder enable(@VmPolicyMask int mask) {
+                mMask |= mask;
                 return this;
             }
 
-            Builder disable(int bit) {
-                mMask &= ~bit;
+            Builder disable(@VmPolicyMask int mask) {
+                mMask &= ~mask;
                 return this;
             }
 
@@ -1130,21 +1155,21 @@
     }
 
     /** @hide */
-    public static void setThreadPolicyMask(final int policyMask) {
+    public static void setThreadPolicyMask(@ThreadPolicyMask int threadPolicyMask) {
         // In addition to the Java-level thread-local in Dalvik's
         // BlockGuard, we also need to keep a native thread-local in
         // Binder in order to propagate the value across Binder calls,
         // even across native-only processes.  The two are kept in
         // sync via the callback to onStrictModePolicyChange, below.
-        setBlockGuardPolicy(policyMask);
+        setBlockGuardPolicy(threadPolicyMask);
 
         // And set the Android native version...
-        Binder.setThreadStrictModePolicy(policyMask);
+        Binder.setThreadStrictModePolicy(threadPolicyMask);
     }
 
     // Sets the policy in Dalvik/libcore (BlockGuard)
-    private static void setBlockGuardPolicy(final int policyMask) {
-        if (policyMask == 0) {
+    private static void setBlockGuardPolicy(@ThreadPolicyMask int threadPolicyMask) {
+        if (threadPolicyMask == 0) {
             BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY);
             return;
         }
@@ -1156,7 +1181,17 @@
             androidPolicy = THREAD_ANDROID_POLICY.get();
             BlockGuard.setThreadPolicy(androidPolicy);
         }
-        androidPolicy.setPolicyMask(policyMask);
+        androidPolicy.setThreadPolicyMask(threadPolicyMask);
+    }
+
+    private static void setBlockGuardVmPolicy(@VmPolicyMask int vmPolicyMask) {
+        // We only need to install BlockGuard for a small subset of VM policies
+        vmPolicyMask &= DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED;
+        if (vmPolicyMask != 0) {
+            BlockGuard.setVmPolicy(VM_ANDROID_POLICY);
+        } else {
+            BlockGuard.setVmPolicy(BlockGuard.LAX_VM_POLICY);
+        }
     }
 
     // Sets up CloseGuard in Dalvik/libcore
@@ -1173,8 +1208,13 @@
      * @return the bitmask of all the DETECT_* and PENALTY_* bits currently enabled
      * @hide
      */
-    public static int getThreadPolicyMask() {
-        return BlockGuard.getThreadPolicy().getPolicyMask();
+    public static @ThreadPolicyMask int getThreadPolicyMask() {
+        final BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+        if (policy instanceof AndroidBlockGuardPolicy) {
+            return ((AndroidBlockGuardPolicy) policy).getThreadPolicyMask();
+        } else {
+            return 0;
+        }
     }
 
     /** Returns the current thread's policy. */
@@ -1206,9 +1246,9 @@
     }
 
     /** @hide */
-    public static int allowThreadDiskWritesMask() {
+    public static @ThreadPolicyMask int allowThreadDiskWritesMask() {
         int oldPolicyMask = getThreadPolicyMask();
-        int newPolicyMask = oldPolicyMask & ~(DETECT_DISK_WRITE | DETECT_DISK_READ);
+        int newPolicyMask = oldPolicyMask & ~(DETECT_THREAD_DISK_WRITE | DETECT_THREAD_DISK_READ);
         if (newPolicyMask != oldPolicyMask) {
             setThreadPolicyMask(newPolicyMask);
         }
@@ -1230,9 +1270,9 @@
     }
 
     /** @hide */
-    public static int allowThreadDiskReadsMask() {
+    public static @ThreadPolicyMask int allowThreadDiskReadsMask() {
         int oldPolicyMask = getThreadPolicyMask();
-        int newPolicyMask = oldPolicyMask & ~(DETECT_DISK_READ);
+        int newPolicyMask = oldPolicyMask & ~(DETECT_THREAD_DISK_READ);
         if (newPolicyMask != oldPolicyMask) {
             setThreadPolicyMask(newPolicyMask);
         }
@@ -1400,32 +1440,6 @@
                         sVmPolicy.mCallbackExecutor);
     }
 
-    /**
-     * Parses the BlockGuard policy mask out from the Exception's getMessage() String value. Kinda
-     * gross, but least invasive. :/
-     *
-     * <p>Input is of the following forms: "policy=137 violation=64" "policy=137 violation=64
-     * msg=Arbitrary text"
-     *
-     * <p>Returns 0 on failure, which is a valid policy, but not a valid policy during a violation
-     * (else there must've been some policy in effect to violate).
-     */
-    private static int parsePolicyFromMessage(String message) {
-        if (message == null || !message.startsWith("policy=")) {
-            return 0;
-        }
-        int spaceIndex = message.indexOf(' ');
-        if (spaceIndex == -1) {
-            return 0;
-        }
-        String policyString = message.substring(7, spaceIndex);
-        try {
-            return Integer.parseInt(policyString);
-        } catch (NumberFormatException e) {
-            return 0;
-        }
-    }
-
     private static final ThreadLocal<ArrayList<ViolationInfo>> violationsBeingTimed =
             new ThreadLocal<ArrayList<ViolationInfo>>() {
                 @Override
@@ -1456,30 +1470,30 @@
     }
 
     private static class AndroidBlockGuardPolicy implements BlockGuard.Policy {
-        private int mPolicyMask;
+        private @ThreadPolicyMask int mThreadPolicyMask;
 
         // Map from violation stacktrace hashcode -> uptimeMillis of
         // last violation.  No locking needed, as this is only
         // accessed by the same thread.
         private ArrayMap<Integer, Long> mLastViolationTime;
 
-        public AndroidBlockGuardPolicy(final int policyMask) {
-            mPolicyMask = policyMask;
+        public AndroidBlockGuardPolicy(@ThreadPolicyMask int threadPolicyMask) {
+            mThreadPolicyMask = threadPolicyMask;
         }
 
         @Override
         public String toString() {
-            return "AndroidBlockGuardPolicy; mPolicyMask=" + mPolicyMask;
+            return "AndroidBlockGuardPolicy; mPolicyMask=" + mThreadPolicyMask;
         }
 
         // Part of BlockGuard.Policy interface:
         public int getPolicyMask() {
-            return mPolicyMask;
+            return mThreadPolicyMask;
         }
 
         // Part of BlockGuard.Policy interface:
         public void onWriteToDisk() {
-            if ((mPolicyMask & DETECT_DISK_WRITE) == 0) {
+            if ((mThreadPolicyMask & DETECT_THREAD_DISK_WRITE) == 0) {
                 return;
             }
             if (tooManyViolationsThisLoop()) {
@@ -1490,7 +1504,7 @@
 
         // Not part of BlockGuard.Policy; just part of StrictMode:
         void onCustomSlowCall(String name) {
-            if ((mPolicyMask & DETECT_CUSTOM) == 0) {
+            if ((mThreadPolicyMask & DETECT_THREAD_CUSTOM) == 0) {
                 return;
             }
             if (tooManyViolationsThisLoop()) {
@@ -1501,7 +1515,7 @@
 
         // Not part of BlockGuard.Policy; just part of StrictMode:
         void onResourceMismatch(Object tag) {
-            if ((mPolicyMask & DETECT_RESOURCE_MISMATCH) == 0) {
+            if ((mThreadPolicyMask & DETECT_THREAD_RESOURCE_MISMATCH) == 0) {
                 return;
             }
             if (tooManyViolationsThisLoop()) {
@@ -1512,7 +1526,7 @@
 
         // Not part of BlockGuard.Policy; just part of StrictMode:
         public void onUnbufferedIO() {
-            if ((mPolicyMask & DETECT_UNBUFFERED_IO) == 0) {
+            if ((mThreadPolicyMask & DETECT_THREAD_UNBUFFERED_IO) == 0) {
                 return;
             }
             if (tooManyViolationsThisLoop()) {
@@ -1523,7 +1537,7 @@
 
         // Part of BlockGuard.Policy interface:
         public void onReadFromDisk() {
-            if ((mPolicyMask & DETECT_DISK_READ) == 0) {
+            if ((mThreadPolicyMask & DETECT_THREAD_DISK_READ) == 0) {
                 return;
             }
             if (tooManyViolationsThisLoop()) {
@@ -1534,10 +1548,10 @@
 
         // Part of BlockGuard.Policy interface:
         public void onNetwork() {
-            if ((mPolicyMask & DETECT_NETWORK) == 0) {
+            if ((mThreadPolicyMask & DETECT_THREAD_NETWORK) == 0) {
                 return;
             }
-            if ((mPolicyMask & PENALTY_DEATH_ON_NETWORK) != 0) {
+            if ((mThreadPolicyMask & PENALTY_DEATH_ON_NETWORK) != 0) {
                 throw new NetworkOnMainThreadException();
             }
             if (tooManyViolationsThisLoop()) {
@@ -1548,7 +1562,7 @@
 
         // Part of BlockGuard.Policy interface:
         public void onExplicitGc() {
-            if ((mPolicyMask & DETECT_EXPLICIT_GC) == 0) {
+            if ((mThreadPolicyMask & DETECT_THREAD_EXPLICIT_GC) == 0) {
                 return;
             }
             if (tooManyViolationsThisLoop()) {
@@ -1557,8 +1571,12 @@
             startHandlingViolationException(new ExplicitGcViolation());
         }
 
-        public void setPolicyMask(int policyMask) {
-            mPolicyMask = policyMask;
+        public @ThreadPolicyMask int getThreadPolicyMask() {
+            return mThreadPolicyMask;
+        }
+
+        public void setThreadPolicyMask(@ThreadPolicyMask int threadPolicyMask) {
+            mThreadPolicyMask = threadPolicyMask;
         }
 
         // Start handling a violation that just started and hasn't
@@ -1567,7 +1585,8 @@
         // thread and, if so, uses it to roughly measure how long the
         // violation took.
         void startHandlingViolationException(Violation e) {
-            final ViolationInfo info = new ViolationInfo(e, mPolicyMask);
+            final int penaltyMask = (mThreadPolicyMask & PENALTY_ALL);
+            final ViolationInfo info = new ViolationInfo(e, penaltyMask);
             info.violationUptimeMillis = SystemClock.uptimeMillis();
             handleViolationWithTimingAttempt(info);
         }
@@ -1596,7 +1615,7 @@
             //
             // TODO: if in gather mode, ignore Looper.myLooper() and always
             //       go into this immediate mode?
-            if (looper == null || (info.mPolicy & THREAD_PENALTY_MASK) == PENALTY_DEATH) {
+            if (looper == null || (info.mPenaltyMask == PENALTY_DEATH)) {
                 info.durationMillis = -1; // unknown (redundant, already set)
                 onThreadPolicyViolation(info);
                 return;
@@ -1667,7 +1686,7 @@
         // to people who push/pop temporary policy in regions of code,
         // hence the policy being passed around.
         void onThreadPolicyViolation(final ViolationInfo info) {
-            if (LOG_V) Log.d(TAG, "onThreadPolicyViolation; policy=" + info.mPolicy);
+            if (LOG_V) Log.d(TAG, "onThreadPolicyViolation; penalty=" + info.mPenaltyMask);
 
             if (info.penaltyEnabled(PENALTY_GATHER)) {
                 ArrayList<ViolationInfo> violations = gatheredViolations.get();
@@ -1708,25 +1727,20 @@
 
             final Violation violation = info.mViolation;
 
-            // The violationMaskSubset, passed to ActivityManager, is a
-            // subset of the original StrictMode policy bitmask, with
-            // only the bit violated and penalty bits to be executed
-            // by the ActivityManagerService remaining set.
-            int violationMaskSubset = 0;
+            // Penalties that ActivityManager should execute on our behalf.
+            int penaltyMask = 0;
 
             if (info.penaltyEnabled(PENALTY_DIALOG)
                     && timeSinceLastViolationMillis > MIN_DIALOG_INTERVAL_MS) {
-                violationMaskSubset |= PENALTY_DIALOG;
+                penaltyMask |= PENALTY_DIALOG;
             }
 
             if (info.penaltyEnabled(PENALTY_DROPBOX) && lastViolationTime == 0) {
-                violationMaskSubset |= PENALTY_DROPBOX;
+                penaltyMask |= PENALTY_DROPBOX;
             }
 
-            if (violationMaskSubset != 0) {
-                violationMaskSubset |= info.getViolationBit();
-
-                final boolean justDropBox = (info.mPolicy & THREAD_PENALTY_MASK) == PENALTY_DROPBOX;
+            if (penaltyMask != 0) {
+                final boolean justDropBox = (info.mPenaltyMask == PENALTY_DROPBOX);
                 if (justDropBox) {
                     // If all we're going to ask the activity manager
                     // to do is dropbox it (the common case during
@@ -1734,13 +1748,13 @@
                     // call synchronously which Binder data suggests
                     // isn't always super fast, despite the implementation
                     // in the ActivityManager trying to be mostly async.
-                    dropboxViolationAsync(violationMaskSubset, info);
+                    dropboxViolationAsync(penaltyMask, info);
                 } else {
-                    handleApplicationStrictModeViolation(violationMaskSubset, info);
+                    handleApplicationStrictModeViolation(penaltyMask, info);
                 }
             }
 
-            if ((info.getPolicyMask() & PENALTY_DEATH) != 0) {
+            if (info.penaltyEnabled(PENALTY_DEATH)) {
                 throw new RuntimeException("StrictMode ThreadPolicy violation", violation);
             }
 
@@ -1753,11 +1767,11 @@
                     executor.execute(
                             () -> {
                                 // Lift violated policy to prevent infinite recursion.
-                                ThreadPolicy oldPolicy = allowThreadViolations();
+                                ThreadPolicy oldPolicy = StrictMode.allowThreadViolations();
                                 try {
                                     listener.onThreadViolation(violation);
                                 } finally {
-                                    setThreadPolicy(oldPolicy);
+                                    StrictMode.setThreadPolicy(oldPolicy);
                                 }
                             });
                 } catch (RejectedExecutionException e) {
@@ -1767,6 +1781,34 @@
         }
     }
 
+    private static final BlockGuard.VmPolicy VM_ANDROID_POLICY = new BlockGuard.VmPolicy() {
+        @Override
+        public void onPathAccess(String path) {
+            if (path == null) return;
+
+            // NOTE: keep credential-protected paths in sync with Environment.java
+            if (path.startsWith("/data/user/")
+                    || path.startsWith("/data/media/")
+                    || path.startsWith("/data/system_ce/")
+                    || path.startsWith("/data/misc_ce/")
+                    || path.startsWith("/data/vendor_ce/")
+                    || path.startsWith("/storage/emulated/")) {
+                final int second = path.indexOf('/', 1);
+                final int third = path.indexOf('/', second + 1);
+                final int fourth = path.indexOf('/', third + 1);
+                if (fourth == -1) return;
+
+                try {
+                    final int userId = Integer.parseInt(path.substring(third + 1, fourth));
+                    onCredentialProtectedPathAccess(path, userId);
+                } catch (NumberFormatException ignored) {
+                }
+            } else if (path.startsWith("/data/data/")) {
+                onCredentialProtectedPathAccess(path, UserHandle.USER_SYSTEM);
+            }
+        }
+    };
+
     /**
      * In the common case, as set by conditionallyEnableDebugLogging, we're just dropboxing any
      * violations but not showing a dialog, not loggging, and not killing the process. In these
@@ -1774,7 +1816,7 @@
      * per-thread and vm-wide violations when applicable.
      */
     private static void dropboxViolationAsync(
-            final int violationMaskSubset, final ViolationInfo info) {
+            final int penaltyMask, final ViolationInfo info) {
         int outstanding = sDropboxCallsInFlight.incrementAndGet();
         if (outstanding > 20) {
             // What's going on?  Let's not make make the situation
@@ -1786,13 +1828,13 @@
         if (LOG_V) Log.d(TAG, "Dropboxing async; in-flight=" + outstanding);
 
         BackgroundThread.getHandler().post(() -> {
-            handleApplicationStrictModeViolation(violationMaskSubset, info);
+            handleApplicationStrictModeViolation(penaltyMask, info);
             int outstandingInner = sDropboxCallsInFlight.decrementAndGet();
             if (LOG_V) Log.d(TAG, "Dropbox complete; in-flight=" + outstandingInner);
         });
     }
 
-    private static void handleApplicationStrictModeViolation(int violationMaskSubset,
+    private static void handleApplicationStrictModeViolation(int penaltyMask,
             ViolationInfo info) {
         final int oldMask = getThreadPolicyMask();
         try {
@@ -1807,7 +1849,7 @@
                 Log.w(TAG, "No activity manager; failed to Dropbox violation.");
             } else {
                 am.handleApplicationStrictModeViolation(
-                        RuntimeInit.getApplicationObject(), violationMaskSubset, info);
+                        RuntimeInit.getApplicationObject(), penaltyMask, info);
             }
         } catch (RemoteException e) {
             if (e instanceof DeadObjectException) {
@@ -1840,6 +1882,7 @@
     }
 
     /** @hide */
+    @TestApi
     public static void conditionallyCheckInstanceCounts() {
         VmPolicy policy = getVmPolicy();
         int policySize = policy.classInstanceLimit.size();
@@ -1894,7 +1937,7 @@
             if (looper != null) {
                 MessageQueue mq = looper.mQueue;
                 if (policy.classInstanceLimit.size() == 0
-                        || (sVmPolicy.mask & VM_PENALTY_MASK) == 0) {
+                        || (sVmPolicy.mask & PENALTY_ALL) == 0) {
                     mq.removeIdleHandler(sProcessIdleHandler);
                     sIsIdlerRegistered = false;
                 } else if (!sIsIdlerRegistered) {
@@ -1933,6 +1976,8 @@
                 VMRuntime.setNonSdkApiUsageConsumer(null);
                 VMRuntime.setDedupeHiddenApiWarnings(true);
             }
+
+            setBlockGuardVmPolicy(sVmPolicy.mask);
         }
     }
 
@@ -1991,6 +2036,16 @@
     }
 
     /** @hide */
+    public static boolean vmImplicitDirectBootEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_IMPLICIT_DIRECT_BOOT) != 0;
+    }
+
+    /** @hide */
+    public static boolean vmCredentialProtectedWhileLockedEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED) != 0;
+    }
+
+    /** @hide */
     public static void onSqliteObjectLeaked(String message, Throwable originStack) {
         onVmPolicyViolation(new SqliteObjectLeakedViolation(message, originStack));
     }
@@ -2026,10 +2081,6 @@
     }
 
     /** @hide */
-    public static final String CLEARTEXT_DETECTED_MSG =
-            "Detected cleartext network traffic from UID ";
-
-    /** @hide */
     public static void onCleartextNetworkDetected(byte[] firstPacket) {
         byte[] rawAddr = null;
         if (firstPacket != null) {
@@ -2045,7 +2096,7 @@
         }
 
         final int uid = android.os.Process.myUid();
-        String msg = CLEARTEXT_DETECTED_MSG + uid;
+        String msg = "Detected cleartext network traffic from UID " + uid;
         if (rawAddr != null) {
             try {
                 msg += " to " + InetAddress.getByAddress(rawAddr);
@@ -2062,6 +2113,47 @@
         onVmPolicyViolation(new UntaggedSocketViolation());
     }
 
+    /** @hide */
+    public static void onImplicitDirectBoot() {
+        onVmPolicyViolation(new ImplicitDirectBootViolation());
+    }
+
+    /** Assume locked until we hear otherwise */
+    private static volatile boolean sUserKeyUnlocked = false;
+
+    private static boolean isUserKeyUnlocked(int userId) {
+        final IStorageManager storage = IStorageManager.Stub
+                .asInterface(ServiceManager.getService("mount"));
+        if (storage != null) {
+            try {
+                return storage.isUserKeyUnlocked(userId);
+            } catch (RemoteException ignored) {
+            }
+        }
+        return false;
+    }
+
+    /** @hide */
+    private static void onCredentialProtectedPathAccess(String path, int userId) {
+        // We can cache the unlocked state for the userId we're running as,
+        // since any relocking of that user will always result in our
+        // process being killed to release any CE FDs we're holding onto.
+        if (userId == UserHandle.myUserId()) {
+            if (sUserKeyUnlocked) {
+                return;
+            } else if (isUserKeyUnlocked(userId)) {
+                sUserKeyUnlocked = true;
+                return;
+            }
+        } else if (isUserKeyUnlocked(userId)) {
+            return;
+        }
+
+        onVmPolicyViolation(new CredentialProtectedWhileLockedViolation(
+                "Accessed credential protected path " + path + " while user " + userId
+                        + " was locked"));
+    }
+
     // Map from VM violation fingerprint to uptime millis.
     private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<>();
 
@@ -2075,7 +2167,9 @@
         final boolean penaltyDropbox = (sVmPolicy.mask & PENALTY_DROPBOX) != 0;
         final boolean penaltyDeath = ((sVmPolicy.mask & PENALTY_DEATH) != 0) || forceDeath;
         final boolean penaltyLog = (sVmPolicy.mask & PENALTY_LOG) != 0;
-        final ViolationInfo info = new ViolationInfo(violation, sVmPolicy.mask);
+
+        final int penaltyMask = (sVmPolicy.mask & PENALTY_ALL);
+        final ViolationInfo info = new ViolationInfo(violation, penaltyMask);
 
         // Erase stuff not relevant for process-wide violations
         info.numAnimationsRunning = 0;
@@ -2104,16 +2198,14 @@
             sLogger.log(info);
         }
 
-        int violationMaskSubset = PENALTY_DROPBOX | (ALL_VM_DETECT_BITS & sVmPolicy.mask);
-
         if (penaltyDropbox) {
             if (penaltyDeath) {
-                handleApplicationStrictModeViolation(violationMaskSubset, info);
+                handleApplicationStrictModeViolation(PENALTY_DROPBOX, info);
             } else {
                 // Common case for userdebug/eng builds.  If no death and
                 // just dropboxing, we can do the ActivityManager call
                 // asynchronously.
-                dropboxViolationAsync(violationMaskSubset, info);
+                dropboxViolationAsync(PENALTY_DROPBOX, info);
             }
         }
 
@@ -2188,7 +2280,7 @@
      * Binder for its current (native) thread-local policy value and synchronize it to libcore's
      * (Java) thread-local policy value.
      */
-    private static void onBinderStrictModePolicyChange(int newPolicy) {
+    private static void onBinderStrictModePolicyChange(@ThreadPolicyMask int newPolicy) {
         setBlockGuardPolicy(newPolicy);
     }
 
@@ -2506,8 +2598,8 @@
         /** Memoized stack trace of full violation. */
         @Nullable private String mStackTrace;
 
-        /** The strict mode policy mask at the time of violation. */
-        private final int mPolicy;
+        /** The strict mode penalty mask at the time of violation. */
+        private final int mPenaltyMask;
 
         /** The wall time duration of the violation, when known. -1 when not known. */
         public int durationMillis = -1;
@@ -2538,9 +2630,9 @@
         public long numInstances = -1;
 
         /** Create an instance of ViolationInfo initialized from an exception. */
-        ViolationInfo(Violation tr, int policy) {
+        ViolationInfo(Violation tr, int penaltyMask) {
             this.mViolation = tr;
-            this.mPolicy = policy;
+            this.mPenaltyMask = penaltyMask;
             violationUptimeMillis = SystemClock.uptimeMillis();
             this.numAnimationsRunning = ValueAnimator.getCurrentAnimationsCount();
             Intent broadcastIntent = ActivityThread.getIntentBeingBroadcast();
@@ -2569,7 +2661,10 @@
             }
         }
 
-        /** Equivalent output to {@link ApplicationErrorReport.CrashInfo#stackTrace}. */
+        /**
+         * Equivalent output to
+         * {@link android.app.ApplicationErrorReport.CrashInfo#stackTrace}.
+         */
         public String getStackTrace() {
             if (mStackTrace == null) {
                 StringWriter sw = new StringWriter();
@@ -2590,6 +2685,10 @@
             return mStackTrace;
         }
 
+        public Class<? extends Violation> getViolationClass() {
+            return mViolation.getClass();
+        }
+
         /**
          * Optional message describing this violation.
          *
@@ -2600,18 +2699,8 @@
             return mViolation.getMessage();
         }
 
-        /**
-         * Policy mask at time of violation.
-         *
-         * @hide
-         */
-        @TestApi
-        public int getPolicyMask() {
-            return mPolicy;
-        }
-
         boolean penaltyEnabled(int p) {
-            return (mPolicy & p) != 0;
+            return (mPenaltyMask & p) != 0;
         }
 
         /**
@@ -2624,51 +2713,6 @@
             mBinderStack.addFirst(t.getStackTrace());
         }
 
-        /**
-         * Retrieve the type of StrictMode violation.
-         *
-         * @hide
-         */
-        @TestApi
-        public int getViolationBit() {
-            if (mViolation instanceof DiskWriteViolation) {
-                return DETECT_DISK_WRITE;
-            } else if (mViolation instanceof DiskReadViolation) {
-                return DETECT_DISK_READ;
-            } else if (mViolation instanceof NetworkViolation) {
-                return DETECT_NETWORK;
-            } else if (mViolation instanceof CustomViolation) {
-                return DETECT_CUSTOM;
-            } else if (mViolation instanceof ResourceMismatchViolation) {
-                return DETECT_RESOURCE_MISMATCH;
-            } else if (mViolation instanceof UnbufferedIoViolation) {
-                return DETECT_UNBUFFERED_IO;
-            } else if (mViolation instanceof SqliteObjectLeakedViolation) {
-                return DETECT_VM_CURSOR_LEAKS;
-            } else if (mViolation instanceof LeakedClosableViolation) {
-                return DETECT_VM_CLOSABLE_LEAKS;
-            } else if (mViolation instanceof InstanceCountViolation) {
-                return DETECT_VM_INSTANCE_LEAKS;
-            } else if (mViolation instanceof IntentReceiverLeakedViolation) {
-                return DETECT_VM_REGISTRATION_LEAKS;
-            } else if (mViolation instanceof ServiceConnectionLeakedViolation) {
-                return DETECT_VM_REGISTRATION_LEAKS;
-            } else if (mViolation instanceof FileUriExposedViolation) {
-                return DETECT_VM_FILE_URI_EXPOSURE;
-            } else if (mViolation instanceof CleartextNetworkViolation) {
-                return DETECT_VM_CLEARTEXT_NETWORK;
-            } else if (mViolation instanceof ContentUriWithoutPermissionViolation) {
-                return DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION;
-            } else if (mViolation instanceof UntaggedSocketViolation) {
-                return DETECT_VM_UNTAGGED_SOCKET;
-            } else if (mViolation instanceof ExplicitGcViolation) {
-                return DETECT_EXPLICIT_GC;
-            } else if (mViolation instanceof NonSdkApiUsedViolation) {
-                return DETECT_VM_NON_SDK_API_USAGE;
-            }
-            throw new IllegalStateException("missing violation bit");
-        }
-
         @Override
         public int hashCode() {
             int result = 17;
@@ -2716,11 +2760,11 @@
                 }
                 mBinderStack.add(traceElements);
             }
-            int rawPolicy = in.readInt();
+            int rawPenaltyMask = in.readInt();
             if (unsetGatheringBit) {
-                mPolicy = rawPolicy & ~PENALTY_GATHER;
+                mPenaltyMask = rawPenaltyMask & ~PENALTY_GATHER;
             } else {
-                mPolicy = rawPolicy;
+                mPenaltyMask = rawPenaltyMask;
             }
             durationMillis = in.readInt();
             violationNumThisLoop = in.readInt();
@@ -2746,7 +2790,7 @@
                 }
             }
             int start = dest.dataPosition();
-            dest.writeInt(mPolicy);
+            dest.writeInt(mPenaltyMask);
             dest.writeInt(durationMillis);
             dest.writeInt(violationNumThisLoop);
             dest.writeInt(numAnimationsRunning);
@@ -2758,8 +2802,8 @@
             if (Binder.CHECK_PARCEL_SIZE && total > 10 * 1024) {
                 Slog.d(
                         TAG,
-                        "VIO: policy="
-                                + mPolicy
+                        "VIO: penalty="
+                                + mPenaltyMask
                                 + " dur="
                                 + durationMillis
                                 + " numLoop="
@@ -2779,7 +2823,7 @@
         /** Dump a ViolationInfo instance to a Printer. */
         public void dump(Printer pw, String prefix) {
             pw.println(prefix + "stackTrace: " + getStackTrace());
-            pw.println(prefix + "policy: " + mPolicy);
+            pw.println(prefix + "penalty: " + mPenaltyMask);
             if (durationMillis != -1) {
                 pw.println(prefix + "durationMillis: " + durationMillis);
             }
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 7d3ba6a..fb34a52 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -96,6 +96,7 @@
      */
     @NonNull
     @SystemApi
+    @TestApi
     public static String get(@NonNull String key) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key);
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index f776c17..c989197 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -67,14 +67,14 @@
     }
 
     @Override
-    public void vibrate(int uid, String opPkg,
-            VibrationEffect effect, AudioAttributes attributes) {
+    public void vibrate(int uid, String opPkg, VibrationEffect effect,
+            String reason, AudioAttributes attributes) {
         if (mService == null) {
             Log.w(TAG, "Failed to vibrate; no vibrator service.");
             return;
         }
         try {
-            mService.vibrate(uid, opPkg, effect, usageForAttributes(attributes), mToken);
+            mService.vibrate(uid, opPkg, effect, usageForAttributes(attributes), reason, mToken);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to vibrate.", e);
         }
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 583f060..a967b3d 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -288,6 +288,19 @@
     }
 
     /**
+     * Checks whether or not tracing is currently enabled. This is useful to avoid intermediate
+     * string creation for trace sections that require formatting. It is not necessary
+     * to guard all Trace method calls as they internally already check this. However it is
+     * recommended to use this to prevent creating any temporary objects that would then be
+     * passed to those methods to reduce runtime cost when tracing isn't enabled.
+     *
+     * @return true if tracing is currently enabled, false otherwise
+     */
+    public static boolean isEnabled() {
+        return isTagEnabled(TRACE_TAG_APP);
+    }
+
+    /**
      * Writes a trace message to indicate that a given section of code has begun. This call must
      * be followed by a corresponding call to {@link #endSection()} on the same thread.
      *
@@ -319,4 +332,42 @@
             nativeTraceEnd(TRACE_TAG_APP);
         }
     }
+
+    /**
+     * Writes a trace message to indicate that a given section of code has
+     * begun. Must be followed by a call to {@link #endAsyncSection(String, int)} with the same
+     * methodName and cookie. Unlike {@link #beginSection(String)} and {@link #endSection()},
+     * asynchronous events do not need to be nested. The name and cookie used to
+     * begin an event must be used to end it.
+     *
+     * @param methodName The method name to appear in the trace.
+     * @param cookie Unique identifier for distinguishing simultaneous events
+     */
+    public static void beginAsyncSection(String methodName, int cookie) {
+        asyncTraceBegin(TRACE_TAG_APP, methodName, cookie);
+    }
+
+    /**
+     * Writes a trace message to indicate that the current method has ended.
+     * Must be called exactly once for each call to {@link #beginAsyncSection(String, int)}
+     * using the same name and cookie.
+     *
+     * @param methodName The method name to appear in the trace.
+     * @param cookie Unique identifier for distinguishing simultaneous events
+     */
+    public static void endAsyncSection(String methodName, int cookie) {
+        asyncTraceEnd(TRACE_TAG_APP, methodName, cookie);
+    }
+
+    /**
+     * Writes trace message to indicate the value of a given counter.
+     *
+     * @param counterName The counter name to appear in the trace.
+     * @param counterValue The counter value.
+     */
+    public static void setCounter(String counterName, int counterValue) {
+        if (isTagEnabled(TRACE_TAG_APP)) {
+            nativeTraceCounter(TRACE_TAG_APP, counterName, counterValue);
+        }
+    }
 }
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 91c69fb..01d85c6 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -54,6 +54,7 @@
      * @see #get(int)
      * @hide
      */
+    @TestApi
     public static final int EFFECT_CLICK = Effect.CLICK;
 
     /**
@@ -62,6 +63,7 @@
      * @see #get(int)
      * @hide
      */
+    @TestApi
     public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK;
 
     /**
@@ -69,6 +71,7 @@
      * @see #get(int)
      * @hide
      */
+    @TestApi
     public static final int EFFECT_TICK = Effect.TICK;
 
     /**
@@ -76,6 +79,7 @@
      * @see #get(int)
      * @hide
      */
+    @TestApi
     public static final int EFFECT_THUD = Effect.THUD;
 
     /**
@@ -83,6 +87,7 @@
      * @see #get(int)
      * @hide
      */
+    @TestApi
     public static final int EFFECT_POP = Effect.POP;
 
     /**
@@ -90,8 +95,20 @@
      * @see #get(int)
      * @hide
      */
+    @TestApi
     public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK;
 
+    /** {@hide} */
+    @TestApi
+    public static final int EFFECT_STRENGTH_LIGHT = EffectStrength.LIGHT;
+
+    /** {@hide} */
+    @TestApi
+    public static final int EFFECT_STRENGTH_MEDIUM = EffectStrength.MEDIUM;
+
+    /** {@hide} */
+    @TestApi
+    public static final int EFFECT_STRENGTH_STRONG = EffectStrength.STRONG;
 
     /**
      * Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
@@ -307,6 +324,7 @@
      *
      * @hide
      */
+    @TestApi
     public abstract long getDuration();
 
     /**
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index d2d8f1e..b5aeba0 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -248,17 +248,17 @@
 
     @RequiresPermission(android.Manifest.permission.VIBRATE)
     public void vibrate(VibrationEffect vibe, AudioAttributes attributes) {
-        vibrate(Process.myUid(), mPackageName, vibe, attributes);
+        vibrate(Process.myUid(), mPackageName, vibe, null, attributes);
     }
 
     /**
-     * Like {@link #vibrate(VibrationEffect, AudioAttributes)}, but allowing the caller to specify
-     * that the vibration is owned by someone else.
+     * Like {@link #vibrate(int, String, VibrationEffect, AudioAttributes)}, but allows the
+     * caller to specify the vibration is owned by someone else and set reason for vibration.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.VIBRATE)
-    public abstract void vibrate(int uid, String opPkg,
-            VibrationEffect vibe, AudioAttributes attributes);
+    public abstract void vibrate(int uid, String opPkg, VibrationEffect vibe,
+            String reason, AudioAttributes attributes);
 
     /**
      * Turn the vibrator off.
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index 3270719..2299ab2 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -2,6 +2,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.os.WorkSourceProto;
 import android.provider.Settings;
@@ -83,6 +84,7 @@
     }
 
     /** @hide */
+    @TestApi
     public WorkSource(int uid) {
         mNum = 1;
         mUids = new int[] { uid, 0 };
@@ -127,16 +129,19 @@
     }
 
     /** @hide */
+    @TestApi
     public int size() {
         return mNum;
     }
 
     /** @hide */
+    @TestApi
     public int get(int index) {
         return mUids[index];
     }
 
     /** @hide */
+    @TestApi
     public String getName(int index) {
         return mNames != null ? mNames[index] : null;
     }
@@ -328,6 +333,7 @@
      *     to be aware of internal differences.
      */
     @Deprecated
+    @TestApi
     public WorkSource[] setReturningDiffs(WorkSource other) {
         synchronized (sTmpWorkSource) {
             sNewbWork = null;
@@ -379,6 +385,7 @@
      * @deprecated meant for unit testing use only. Will be removed in a future API revision.
      */
     @Deprecated
+    @TestApi
     public WorkSource addReturningNewbs(WorkSource other) {
         synchronized (sTmpWorkSource) {
             sNewbWork = null;
@@ -388,6 +395,7 @@
     }
 
     /** @hide */
+    @TestApi
     public boolean add(int uid) {
         if (mNum <= 0) {
             mNames = null;
@@ -407,6 +415,7 @@
     }
 
     /** @hide */
+    @TestApi
     public boolean add(int uid, String name) {
         if (mNum <= 0) {
             insert(0, uid, name);
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 021e72f..067e849 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -227,12 +227,13 @@
                                                   String instructionSet,
                                                   String appDataDir,
                                                   String invokeWith,
+                                                  String packageName,
                                                   String[] zygoteArgs) {
         try {
             return startViaZygote(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
-                    zygoteArgs);
+                    packageName, zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
                     "Starting VM process through Zygote failed");
@@ -366,6 +367,7 @@
                                                       String appDataDir,
                                                       String invokeWith,
                                                       boolean startChildZygote,
+                                                      String packageName,
                                                       String[] extraArgs)
                                                       throws ZygoteStartFailedEx {
         ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -426,6 +428,10 @@
             argsForZygote.add("--start-child-zygote");
         }
 
+        if (packageName != null) {
+            argsForZygote.add("--package-name=" + packageName);
+        }
+
         argsForZygote.add(processClass);
 
         if (extraArgs != null) {
@@ -733,7 +739,7 @@
             result = startViaZygote(processClass, niceName, uid, gid,
                     gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
                     abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
-                    true /* startChildZygote */, extraArgs);
+                    true /* startChildZygote */, null /* packageName */, extraArgs);
         } catch (ZygoteStartFailedEx ex) {
             throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
         }
diff --git a/core/java/android/os/health/HealthKeys.java b/core/java/android/os/health/HealthKeys.java
index 842def3..5d60411 100644
--- a/core/java/android/os/health/HealthKeys.java
+++ b/core/java/android/os/health/HealthKeys.java
@@ -16,10 +16,8 @@
 
 package android.os.health;
 
-import android.os.Parcel;
-import android.os.Parcelable;
+import android.annotation.TestApi;
 
-import java.lang.annotation.Annotation;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -32,6 +30,7 @@
  *
  * @hide
  */
+@TestApi
 public class HealthKeys {
 
     /**
@@ -81,6 +80,7 @@
      *
      * @hide
      */
+    @TestApi
     public static class Constants {
         private final String mDataType;
         private final int[][] mKeys = new int[TYPE_COUNT][];
diff --git a/core/java/android/os/health/HealthStats.java b/core/java/android/os/health/HealthStats.java
index 90d89c5..74ce515 100644
--- a/core/java/android/os/health/HealthStats.java
+++ b/core/java/android/os/health/HealthStats.java
@@ -16,6 +16,7 @@
 
 package android.os.health;
 
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArrayMap;
@@ -126,6 +127,7 @@
      *
      * @hide
      */
+    @TestApi
     public HealthStats(Parcel in) {
         int count;
 
diff --git a/core/java/android/os/health/HealthStatsParceler.java b/core/java/android/os/health/HealthStatsParceler.java
index 28b3694..d358a2e 100644
--- a/core/java/android/os/health/HealthStatsParceler.java
+++ b/core/java/android/os/health/HealthStatsParceler.java
@@ -16,6 +16,7 @@
 
 package android.os.health;
 
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArrayMap;
@@ -35,6 +36,7 @@
  * reuse them.
  * @hide
  */
+@TestApi
 public class HealthStatsParceler implements Parcelable {
     private HealthStatsWriter mWriter;
     private HealthStats mHealthStats;
diff --git a/core/java/android/os/health/HealthStatsWriter.java b/core/java/android/os/health/HealthStatsWriter.java
index 351836b..d4d10b0 100644
--- a/core/java/android/os/health/HealthStatsWriter.java
+++ b/core/java/android/os/health/HealthStatsWriter.java
@@ -16,6 +16,7 @@
 
 package android.os.health;
 
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArrayMap;
@@ -31,6 +32,7 @@
  *
  * @hide
  */
+@TestApi
 public class HealthStatsWriter {
     private final HealthKeys.Constants mConstants;
 
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index aeced951..8b8ae1c 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -71,6 +71,8 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
 
+import dalvik.system.BlockGuard;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
@@ -1131,6 +1133,7 @@
 
     /** {@hide} */
     public void mkdirs(File file) {
+        BlockGuard.getVmPolicy().onPathAccess(file.getAbsolutePath());
         try {
             mStorageManager.mkdirs(mContext.getOpPackageName(), file.getAbsolutePath());
         } catch (RemoteException e) {
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index fd5a22a..9880142 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -17,6 +17,7 @@
 package android.os.storage;
 
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
@@ -162,6 +163,7 @@
      * @return the mount path
      * @hide
      */
+    @TestApi
     public String getPath() {
         return mPath.toString();
     }
diff --git a/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java b/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java
new file mode 100644
index 0000000..12503f6
--- /dev/null
+++ b/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java
@@ -0,0 +1,39 @@
+/*
+ * 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.strictmode;
+
+import android.content.Context;
+
+/**
+ * Subclass of {@code Violation} that is used when a process accesses filesystem
+ * paths stored in credential protected storage areas while the user is locked.
+ * <p>
+ * When a user is locked, credential protected storage is unavailable, and files
+ * stored in these locations appear to not exist, which can result in subtle app
+ * bugs if they assume default behaviors or empty states. Instead, apps should
+ * store data needed while a user is locked under device protected storage
+ * areas.
+ *
+ * @see Context#createCredentialProtectedStorageContext()
+ * @see Context#createDeviceProtectedStorageContext()
+ */
+public final class CredentialProtectedWhileLockedViolation extends Violation {
+    /** @hide */
+    public CredentialProtectedWhileLockedViolation(String message) {
+        super(message);
+    }
+}
diff --git a/core/java/android/os/strictmode/ExplicitGcViolation.java b/core/java/android/os/strictmode/ExplicitGcViolation.java
index c7480f9..583ed1a 100644
--- a/core/java/android/os/strictmode/ExplicitGcViolation.java
+++ b/core/java/android/os/strictmode/ExplicitGcViolation.java
@@ -15,11 +15,14 @@
  */
 package android.os.strictmode;
 
+import android.annotation.TestApi;
+
 /**
  * See #{@link android.os.StrictMode.ThreadPolicy.Builder#detectExplicitGc()}.
  *
  * @hide
  */
+@TestApi
 public final class ExplicitGcViolation extends Violation {
     /** @hide */
     public ExplicitGcViolation() {
diff --git a/core/java/android/os/strictmode/ImplicitDirectBootViolation.java b/core/java/android/os/strictmode/ImplicitDirectBootViolation.java
new file mode 100644
index 0000000..e52e212
--- /dev/null
+++ b/core/java/android/os/strictmode/ImplicitDirectBootViolation.java
@@ -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.
+ */
+
+package android.os.strictmode;
+
+import android.content.pm.PackageManager;
+
+/**
+ * Subclass of {@code Violation} that is used when a process implicitly relies
+ * on automatic Direct Boot filtering.
+ *
+ * @see PackageManager#MATCH_DIRECT_BOOT_AUTO
+ */
+public final class ImplicitDirectBootViolation extends Violation {
+    /** @hide */
+    public ImplicitDirectBootViolation() {
+        super("Implicitly relying on automatic Direct Boot filtering; request explicit"
+                + " filtering with PackageManager.MATCH_DIRECT_BOOT flags");
+    }
+}
diff --git a/core/java/android/os/strictmode/UntaggedSocketViolation.java b/core/java/android/os/strictmode/UntaggedSocketViolation.java
index 836a8b9..3b1ef25 100644
--- a/core/java/android/os/strictmode/UntaggedSocketViolation.java
+++ b/core/java/android/os/strictmode/UntaggedSocketViolation.java
@@ -17,12 +17,8 @@
 
 public final class UntaggedSocketViolation extends Violation {
     /** @hide */
-    public static final String MESSAGE =
-            "Untagged socket detected; use"
-                    + " TrafficStats.setThreadSocketTag() to track all network usage";
-
-    /** @hide */
     public UntaggedSocketViolation() {
-        super(MESSAGE);
+        super("Untagged socket detected; use TrafficStats.setThreadSocketTag() to "
+                + "track all network usage");
     }
 }
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 0ed2526..2a094bb 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -277,7 +277,7 @@
         mHandler.sendEmptyMessage(MSG_INIT_SAMPLE);
         mVolumeObserver = new Observer(mHandler);
         mContext.getContentResolver().registerContentObserver(
-                System.getUriFor(System.VOLUME_SETTINGS[mStreamType]),
+                System.getUriFor(System.VOLUME_SETTINGS_INT[mStreamType]),
                 false, mVolumeObserver);
         mReceiver.setListening(true);
     }
diff --git a/core/java/android/print/PrintDocumentAdapter.java b/core/java/android/print/PrintDocumentAdapter.java
index 2bb7c2e..d1b6efc 100644
--- a/core/java/android/print/PrintDocumentAdapter.java
+++ b/core/java/android/print/PrintDocumentAdapter.java
@@ -16,6 +16,7 @@
 
 package android.print;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.ParcelFileDescriptor;
@@ -260,6 +261,7 @@
         /**
          * @hide
          */
+        @UnsupportedAppUsage
         public WriteResultCallback() {
             /* do nothing - hide constructor */
         }
@@ -304,6 +306,7 @@
         /**
          * @hide
          */
+        @UnsupportedAppUsage
         public LayoutResultCallback() {
             /* do nothing - hide constructor */
         }
diff --git a/core/java/android/print/PrintFileDocumentAdapter.java b/core/java/android/print/PrintFileDocumentAdapter.java
index a5f9305..eb4b315 100644
--- a/core/java/android/print/PrintFileDocumentAdapter.java
+++ b/core/java/android/print/PrintFileDocumentAdapter.java
@@ -118,7 +118,7 @@
         protected Void doInBackground(Void... params) {
             try (InputStream in = new FileInputStream(mFile);
                     OutputStream out = new FileOutputStream(mDestination.getFileDescriptor())) {
-                FileUtils.copy(in, out, null, mCancellationSignal);
+                FileUtils.copy(in, out, mCancellationSignal, null, null);
             } catch (OperationCanceledException e) {
                 // Ignored; already handled below
             } catch (IOException e) {
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 138477e..41f261b 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.annotation.StringRes;
 import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.os.Bundle;
@@ -549,6 +550,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public PrintDocumentInfo getDocumentInfo() {
         return mDocumentInfo;
     }
@@ -639,6 +641,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public Bundle getAdvancedOptions() {
         return mAdvancedOptions;
     }
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index e436bc6..e1ede93 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -22,6 +22,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.Application.ActivityLifecycleCallbacks;
 import android.content.ComponentName;
@@ -311,6 +312,7 @@
      * @param listener The listener to add.
      * @hide
      */
+    @UnsupportedAppUsage
     public void addPrintJobStateChangeListener(PrintJobStateChangeListener listener) {
         if (mService == null) {
             Log.w(LOG_TAG, "Feature android.software.print not available");
diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java
index ff9c0df..659e56f 100644
--- a/core/java/android/print/PrinterId.java
+++ b/core/java/android/print/PrinterId.java
@@ -17,6 +17,7 @@
 package android.print;
 
 import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -57,6 +58,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public @NonNull ComponentName getServiceName() {
         return mServiceName;
     }
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index f7409d0..14a4509 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -187,8 +187,7 @@
     /**
      * A boolean parameter for {@link Contacts#CONTENT_STREQUENT_URI} and
      * {@link Contacts#CONTENT_STREQUENT_FILTER_URI}, which requires the ContactsProvider to
-     * return only phone-related results. For example, frequently contacted person list should
-     * include persons contacted via phone (not email, sms, etc.)
+     * return only phone-related results.
      */
     public static final String STREQUENT_PHONE_ONLY = "strequent_phone_only";
 
@@ -870,13 +869,23 @@
         /**
          * The number of times a contact has been contacted
          * <P>Type: INTEGER</P>
+         *
+         * @deprecated Contacts affinity information is no longer supported as of
+         * Android version {@link android.os.Build.VERSION_CODES#Q}. This column
+         * always contains 0.
          */
+        @Deprecated
         public static final String TIMES_CONTACTED = "times_contacted";
 
         /**
          * The last time a contact was contacted.
          * <P>Type: INTEGER</P>
+         *
+         * @deprecated Contacts affinity information is no longer supported as of
+         * Android version {@link android.os.Build.VERSION_CODES#Q}. This column
+         * always contains 0.
          */
+        @Deprecated
         public static final String LAST_TIME_CONTACTED = "last_time_contacted";
 
         /** @hide Raw value. */
@@ -1313,8 +1322,7 @@
      * of the newly inserted raw contact.</dd>
      * <dt><b>Update</b></dt>
      * <dd>Only certain columns of Contact are modifiable:
-     * {@link #TIMES_CONTACTED}, {@link #LAST_TIME_CONTACTED}, {@link #STARRED},
-     * {@link #CUSTOM_RINGTONE}, {@link #SEND_TO_VOICEMAIL}. Changing any of
+     * {@link #STARRED}, {@link #CUSTOM_RINGTONE}, {@link #SEND_TO_VOICEMAIL}. Changing any of
      * these columns on the Contact also changes them on all constituent raw
      * contacts.</dd>
      * <dt><b>Delete</b></dt>
@@ -1415,27 +1423,6 @@
      * </tr>
      * <tr>
      * <td>int</td>
-     * <td>{@link #TIMES_CONTACTED}</td>
-     * <td>read/write</td>
-     * <td>The number of times the contact has been contacted. See
-     * {@link #markAsContacted}. When raw contacts are aggregated, this field is
-     * computed automatically as the maximum number of times contacted among all
-     * constituent raw contacts. Setting this field automatically changes the
-     * corresponding field on all constituent raw contacts.</td>
-     * </tr>
-     * <tr>
-     * <td>long</td>
-     * <td>{@link #LAST_TIME_CONTACTED}</td>
-     * <td>read/write</td>
-     * <td>The timestamp of the last time the contact was contacted. See
-     * {@link #markAsContacted}. Setting this field also automatically
-     * increments {@link #TIMES_CONTACTED}. When raw contacts are aggregated,
-     * this field is computed automatically as the latest time contacted of all
-     * constituent raw contacts. Setting this field automatically changes the
-     * corresponding field on all constituent raw contacts.</td>
-     * </tr>
-     * <tr>
-     * <td>int</td>
      * <td>{@link #STARRED}</td>
      * <td>read/write</td>
      * <td>An indicator for favorite contacts: '1' if favorite, '0' otherwise.
@@ -1696,16 +1683,12 @@
          * @param resolver the ContentResolver to use
          * @param contactId the person who was contacted
          *
-         * @deprecated The class DataUsageStatUpdater of the Android support library should
-         *     be used instead.
+         * @deprecated Contacts affinity information is no longer supported as of
+         * Android version {@link android.os.Build.VERSION_CODES#Q}. This method
+         * is no-op.
          */
         @Deprecated
         public static void markAsContacted(ContentResolver resolver, long contactId) {
-            Uri uri = ContentUris.withAppendedId(CONTENT_URI, contactId);
-            ContentValues values = new ContentValues();
-            // TIMES_CONTACTED will be incremented when LAST_TIME_CONTACTED is modified.
-            values.put(LR_LAST_TIME_CONTACTED, System.currentTimeMillis());
-            resolver.update(uri, values, null, null);
         }
 
         /**
@@ -1727,15 +1710,21 @@
 
         /**
          * The content:// style URI for this table joined with useful data from
-         * {@link ContactsContract.Data}, filtered to include only starred contacts
-         * and the most frequently contacted contacts.
+         * {@link ContactsContract.Data}, filtered to include only starred contacts.
+         * Frequent contacts are no longer included in the result as of
+         * Android version {@link android.os.Build.VERSION_CODES#Q}.
          */
         public static final Uri CONTENT_STREQUENT_URI = Uri.withAppendedPath(
                 CONTENT_URI, "strequent");
 
         /**
          * The content:// style URI for showing a list of frequently contacted people.
+         *
+         * @deprecated Frequent contacts are no longer supported as of
+         * Android version {@link android.os.Build.VERSION_CODES#Q}.
+         * This URI always returns an empty cursor.
          */
+        @Deprecated
         public static final Uri CONTENT_FREQUENT_URI = Uri.withAppendedPath(
                 CONTENT_URI, "frequent");
 
@@ -2631,27 +2620,6 @@
      * </tr>
      * <tr>
      * <td>int</td>
-     * <td>{@link #TIMES_CONTACTED}</td>
-     * <td>read/write</td>
-     * <td>The number of times the contact has been contacted. To have an effect
-     * on the corresponding value of the aggregate contact, this field
-     * should be set at the time the raw contact is inserted.
-     * After that, this value is typically updated via
-     * {@link ContactsContract.Contacts#markAsContacted}.</td>
-     * </tr>
-     * <tr>
-     * <td>long</td>
-     * <td>{@link #LAST_TIME_CONTACTED}</td>
-     * <td>read/write</td>
-     * <td>The timestamp of the last time the contact was contacted. To have an effect
-     * on the corresponding value of the aggregate contact, this field
-     * should be set at the time the raw contact is inserted.
-     * After that, this value is typically updated via
-     * {@link ContactsContract.Contacts#markAsContacted}.
-     * </td>
-     * </tr>
-     * <tr>
-     * <td>int</td>
      * <td>{@link #STARRED}</td>
      * <td>read/write</td>
      * <td>An indicator for favorite contacts: '1' if favorite, '0' otherwise.
@@ -4286,10 +4254,22 @@
      * Columns in the Data_Usage_Stat table
      */
     protected interface DataUsageStatColumns {
-        /** The last time (in milliseconds) this {@link Data} was used. */
+        /**
+         * The last time (in milliseconds) this {@link Data} was used.
+         * @deprecated Contacts affinity information is no longer supported as of
+         * Android version {@link android.os.Build.VERSION_CODES#Q}.
+         * This column always contains 0.
+         */
+        @Deprecated
         public static final String LAST_TIME_USED = "last_time_used";
 
-        /** The number of times the referenced {@link Data} has been used. */
+        /**
+         * The number of times the referenced {@link Data} has been used.
+         * @deprecated Contacts affinity information is no longer supported as of
+         * Android version {@link android.os.Build.VERSION_CODES#Q}.
+         * This column always contains 0.
+         */
+        @Deprecated
         public static final String TIMES_USED = "times_used";
 
         /** @hide Raw value. */
@@ -4765,18 +4745,6 @@
      * </tr>
      * <tr>
      * <td>int</td>
-     * <td>{@link #TIMES_CONTACTED}</td>
-     * <td>read-only</td>
-     * <td>See {@link ContactsContract.Contacts}.</td>
-     * </tr>
-     * <tr>
-     * <td>long</td>
-     * <td>{@link #LAST_TIME_CONTACTED}</td>
-     * <td>read-only</td>
-     * <td>See {@link ContactsContract.Contacts}.</td>
-     * </tr>
-     * <tr>
-     * <td>int</td>
      * <td>{@link #STARRED}</td>
      * <td>read-only</td>
      * <td>See {@link ContactsContract.Contacts}.</td>
@@ -5221,18 +5189,6 @@
      * </tr>
      * <tr>
      * <td>int</td>
-     * <td>{@link #TIMES_CONTACTED}</td>
-     * <td>read-only</td>
-     * <td>See {@link ContactsContract.Contacts}.</td>
-     * </tr>
-     * <tr>
-     * <td>long</td>
-     * <td>{@link #LAST_TIME_CONTACTED}</td>
-     * <td>read-only</td>
-     * <td>See {@link ContactsContract.Contacts}.</td>
-     * </tr>
-     * <tr>
-     * <td>int</td>
      * <td>{@link #STARRED}</td>
      * <td>read-only</td>
      * <td>See {@link ContactsContract.Contacts}.</td>
@@ -8305,7 +8261,12 @@
      * boolean successful = resolver.delete(DataUsageFeedback.DELETE_USAGE_URI, null, null) > 0;
      * </pre>
      * </p>
+     *
+     * @deprecated Contacts affinity information is no longer supported as of
+     * Android version {@link android.os.Build.VERSION_CODES#Q}.
+     * Both update and delete calls are always ignored.
      */
+    @Deprecated
     public static final class DataUsageFeedback {
 
         /**
@@ -8925,10 +8886,6 @@
          * +<phone>", etc. If you must show the prefix text in the Contacts App, please use a
          * different DATA# column, and update your contacts.xml to point to this new column. </em>
          * </li>
-         * <li>Everytime the user sends a message to a contact, your app may choose to update the
-         * {@link ContactOptionsColumns#TIMES_CONTACTED} entry through DataUsageFeedback class.
-         * Doing this will allow Voice Assistant to bias speech recognition to contacts frequently
-         * contacted, this is particularly useful for contact names that are hard to pronounce.</li>
          * </ul>
          * If the app chooses not to integrate with the Contacts Provider (in particular, when
          * either METADATA_ACCOUNT_TYPE or METADATA_MIMETYPE field is missing), Voice Assistant
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index a80dced..f97c64c 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -100,7 +100,8 @@
     public static final String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
 
     /** {@hide} */
-    public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
+    @Deprecated
+    public static final String EXTRA_PACKAGE_NAME = Intent.EXTRA_PACKAGE_NAME;
 
     /** {@hide} */
     public static final String EXTRA_SHOW_ADVANCED = "android.content.extra.SHOW_ADVANCED";
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 4b45e32..68572dd 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -16,8 +16,11 @@
 
 package android.provider;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ClipData;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -25,22 +28,23 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.UriPermission;
+import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteException;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.graphics.Matrix;
-import android.media.MiniThumbFile;
-import android.media.ThumbnailUtils;
+import android.graphics.Point;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.os.Environment;
-import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.service.media.CameraPrewarmService;
+import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 import libcore.io.IoUtils;
 
 import java.io.File;
@@ -49,7 +53,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -466,6 +469,12 @@
          * The height of the image/video in pixels.
          */
         public static final String HEIGHT = "height";
+
+        /**
+         * Package that contributed this media.
+         * @hide
+         */
+        public static final String OWNER_PACKAGE_NAME = "owner_package_name";
      }
 
     /**
@@ -611,178 +620,79 @@
         }
     }
 
+    /** @hide */
+    public static class ThumbnailConstants {
+        public static final int MINI_KIND = 1;
+        public static final int FULL_SCREEN_KIND = 2;
+        public static final int MICRO_KIND = 3;
+
+        public static final Point MINI_SIZE = new Point(512, 384);
+        public static final Point MICRO_SIZE = new Point(96, 96);
+    }
+
     /**
      * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
      * to be accessed elsewhere.
      */
     private static class InternalThumbnails implements BaseColumns {
-        private static final int MINI_KIND = 1;
-        private static final int FULL_SCREEN_KIND = 2;
-        private static final int MICRO_KIND = 3;
-        private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA};
-        static final int DEFAULT_GROUP_ID = 0;
-        private static final Object sThumbBufLock = new Object();
-        private static byte[] sThumbBuf;
-
-        private static Bitmap getMiniThumbFromFile(
-                Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) {
-            Bitmap bitmap = null;
-            Uri thumbUri = null;
-            try {
-                long thumbId = c.getLong(0);
-                String filePath = c.getString(1);
-                thumbUri = ContentUris.withAppendedId(baseUri, thumbId);
-                ParcelFileDescriptor pfdInput = cr.openFileDescriptor(thumbUri, "r");
-                bitmap = BitmapFactory.decodeFileDescriptor(
-                        pfdInput.getFileDescriptor(), null, options);
-                pfdInput.close();
-            } catch (FileNotFoundException ex) {
-                Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
-            } catch (IOException ex) {
-                Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
-            } catch (OutOfMemoryError ex) {
-                Log.e(TAG, "failed to allocate memory for thumbnail "
-                        + thumbUri + "; " + ex);
-            }
-            return bitmap;
-        }
+        /**
+         * Currently outstanding thumbnail requests that can be cancelled.
+         */
+        @GuardedBy("sPending")
+        private static ArrayMap<Uri, CancellationSignal> sPending = new ArrayMap<>();
 
         /**
-         * This method cancels the thumbnail request so clients waiting for getThumbnail will be
-         * interrupted and return immediately. Only the original process which made the getThumbnail
-         * requests can cancel their own requests.
+         * Make a blocking request to obtain the given thumbnail, generating it
+         * if needed.
          *
-         * @param cr ContentResolver
-         * @param origId original image or video id. use -1 to cancel all requests.
-         * @param groupId the same groupId used in getThumbnail
-         * @param baseUri the base URI of requested thumbnails
+         * @see #cancelThumbnail(ContentResolver, Uri)
          */
-        static void cancelThumbnailRequest(ContentResolver cr, long origId, Uri baseUri,
-                long groupId) {
-            Uri cancelUri = baseUri.buildUpon().appendQueryParameter("cancel", "1")
-                    .appendQueryParameter("orig_id", String.valueOf(origId))
-                    .appendQueryParameter("group_id", String.valueOf(groupId)).build();
-            Cursor c = null;
-            try {
-                c = cr.query(cancelUri, PROJECTION, null, null, null);
+        static @Nullable Bitmap getThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri,
+                int kind, @Nullable BitmapFactory.Options opts) {
+            final Bundle openOpts = new Bundle();
+            if (kind == ThumbnailConstants.MICRO_KIND) {
+                openOpts.putParcelable(ContentResolver.EXTRA_SIZE, ThumbnailConstants.MICRO_SIZE);
+            } else if (kind == ThumbnailConstants.MINI_KIND) {
+                openOpts.putParcelable(ContentResolver.EXTRA_SIZE, ThumbnailConstants.MINI_SIZE);
+            } else {
+                throw new IllegalArgumentException("Unsupported kind: " + kind);
             }
-            finally {
-                if (c != null) c.close();
+
+            CancellationSignal signal = null;
+            synchronized (sPending) {
+                signal = sPending.get(uri);
+                if (signal == null) {
+                    signal = new CancellationSignal();
+                    sPending.put(uri, signal);
+                }
             }
-        }
 
-        /**
-         * This method ensure thumbnails associated with origId are generated and decode the byte
-         * stream from database (MICRO_KIND) or file (MINI_KIND).
-         *
-         * Special optimization has been done to avoid further IPC communication for MICRO_KIND
-         * thumbnails.
-         *
-         * @param cr ContentResolver
-         * @param origId original image or video id
-         * @param kind could be MINI_KIND or MICRO_KIND
-         * @param options this is only used for MINI_KIND when decoding the Bitmap
-         * @param baseUri the base URI of requested thumbnails
-         * @param groupId the id of group to which this request belongs
-         * @return Bitmap bitmap of specified thumbnail kind
-         */
-        static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId, int kind,
-                BitmapFactory.Options options, Uri baseUri, boolean isVideo) {
-            Bitmap bitmap = null;
-            // Log.v(TAG, "getThumbnail: origId="+origId+", kind="+kind+", isVideo="+isVideo);
-            // If the magic is non-zero, we simply return thumbnail if it does exist.
-            // querying MediaProvider and simply return thumbnail.
-            MiniThumbFile thumbFile = MiniThumbFile.instance(
-                    isVideo ? Video.Media.EXTERNAL_CONTENT_URI : Images.Media.EXTERNAL_CONTENT_URI);
-            Cursor c = null;
-            try {
-                long magic = thumbFile.getMagic(origId);
-                if (magic != 0) {
-                    if (kind == MICRO_KIND) {
-                        synchronized (sThumbBufLock) {
-                            if (sThumbBuf == null) {
-                                sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
-                            }
-                            if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
-                                bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
-                                if (bitmap == null) {
-                                    Log.w(TAG, "couldn't decode byte array.");
-                                }
-                            }
-                        }
-                        return bitmap;
-                    } else if (kind == MINI_KIND) {
-                        String column = isVideo ? "video_id=" : "image_id=";
-                        c = cr.query(baseUri, PROJECTION, column + origId, null, null);
-                        if (c != null && c.moveToFirst()) {
-                            bitmap = getMiniThumbFromFile(c, baseUri, cr, options);
-                            if (bitmap != null) {
-                                return bitmap;
-                            }
-                        }
-                    }
-                }
-
-                Uri blockingUri = baseUri.buildUpon().appendQueryParameter("blocking", "1")
-                        .appendQueryParameter("orig_id", String.valueOf(origId))
-                        .appendQueryParameter("group_id", String.valueOf(groupId)).build();
-                if (c != null) c.close();
-                c = cr.query(blockingUri, PROJECTION, null, null, null);
-                // This happens when original image/video doesn't exist.
-                if (c == null) return null;
-
-                // Assuming thumbnail has been generated, at least original image exists.
-                if (kind == MICRO_KIND) {
-                    synchronized (sThumbBufLock) {
-                        if (sThumbBuf == null) {
-                            sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
-                        }
-                        Arrays.fill(sThumbBuf, (byte)0);
-                        if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
-                            bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
-                            if (bitmap == null) {
-                                Log.w(TAG, "couldn't decode byte array.");
-                            }
-                        }
-                    }
-                } else if (kind == MINI_KIND) {
-                    if (c.moveToFirst()) {
-                        bitmap = getMiniThumbFromFile(c, baseUri, cr, options);
-                    }
-                } else {
-                    throw new IllegalArgumentException("Unsupported kind: " + kind);
-                }
-
-                // We probably run out of space, so create the thumbnail in memory.
-                if (bitmap == null) {
-                    Log.v(TAG, "Create the thumbnail in memory: origId=" + origId
-                            + ", kind=" + kind + ", isVideo="+isVideo);
-                    Uri uri = Uri.parse(
-                            baseUri.buildUpon().appendPath(String.valueOf(origId))
-                                    .toString().replaceFirst("thumbnails", "media"));
-                    if (c != null) c.close();
-                    c = cr.query(uri, PROJECTION, null, null, null);
-                    if (c == null || !c.moveToFirst()) {
-                        return null;
-                    }
-                    String filePath = c.getString(1);
-                    if (filePath != null) {
-                        if (isVideo) {
-                            bitmap = ThumbnailUtils.createVideoThumbnail(filePath, kind);
-                        } else {
-                            bitmap = ThumbnailUtils.createImageThumbnail(filePath, kind);
-                        }
-                    }
-                }
-            } catch (SQLiteException ex) {
-                Log.w(TAG, ex);
+            try (AssetFileDescriptor afd = cr.openTypedAssetFileDescriptor(uri,
+                    "image/*", openOpts, signal)) {
+                return BitmapFactory.decodeFileDescriptor(afd.getFileDescriptor(), null, opts);
+            } catch (IOException e) {
+                Log.w(TAG, "Failed to obtain thumbnail for " + uri, e);
+                return null;
             } finally {
-                if (c != null) c.close();
-                // To avoid file descriptor leak in application process.
-                thumbFile.deactivate();
-                thumbFile = null;
+                synchronized (sPending) {
+                    sPending.remove(uri);
+                }
             }
-            return bitmap;
+        }
+
+        /**
+         * This method cancels the thumbnail request so clients waiting for
+         * {@link #getThumbnail} will be interrupted and return immediately.
+         * Only the original process which made the request can cancel their own
+         * requests.
+         */
+        static void cancelThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri) {
+            synchronized (sPending) {
+                final CancellationSignal signal = sPending.get(uri);
+                if (signal != null) {
+                    signal.cancel();
+                }
+            }
         }
     }
 
@@ -916,48 +826,6 @@
                 }
             }
 
-            private static final Bitmap StoreThumbnail(
-                    ContentResolver cr,
-                    Bitmap source,
-                    long id,
-                    float width, float height,
-                    int kind) {
-                // create the matrix to scale it
-                Matrix matrix = new Matrix();
-
-                float scaleX = width / source.getWidth();
-                float scaleY = height / source.getHeight();
-
-                matrix.setScale(scaleX, scaleY);
-
-                Bitmap thumb = Bitmap.createBitmap(source, 0, 0,
-                                                   source.getWidth(),
-                                                   source.getHeight(), matrix,
-                                                   true);
-
-                ContentValues values = new ContentValues(4);
-                values.put(Images.Thumbnails.KIND,     kind);
-                values.put(Images.Thumbnails.IMAGE_ID, (int)id);
-                values.put(Images.Thumbnails.HEIGHT,   thumb.getHeight());
-                values.put(Images.Thumbnails.WIDTH,    thumb.getWidth());
-
-                Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);
-
-                try {
-                    OutputStream thumbOut = cr.openOutputStream(url);
-
-                    thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
-                    thumbOut.close();
-                    return thumb;
-                }
-                catch (FileNotFoundException ex) {
-                    return null;
-                }
-                catch (IOException ex) {
-                    return null;
-                }
-            }
-
             /**
              * Insert an image and create a thumbnail for it.
              *
@@ -990,12 +858,9 @@
                         }
 
                         long id = ContentUris.parseId(url);
-                        // Wait until MINI_KIND thumbnail is generated.
-                        Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id,
-                                Images.Thumbnails.MINI_KIND, null);
-                        // This is for backward compatibility.
-                        Bitmap microThumb = StoreThumbnail(cr, miniThumb, id, 50F, 50F,
-                                Images.Thumbnails.MICRO_KIND);
+                        // Block until we've generated common thumbnails
+                        Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MINI_KIND, null);
+                        Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MICRO_KIND, null);
                     } else {
                         Log.e(TAG, "Failed to create thumbnail, removing original");
                         cr.delete(url, null, null);
@@ -1085,8 +950,9 @@
              * @param origId original image id
              */
             public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
-                InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
-                        InternalThumbnails.DEFAULT_GROUP_ID);
+                final Uri uri = ContentUris.withAppendedId(
+                        Images.Media.EXTERNAL_CONTENT_URI, origId);
+                InternalThumbnails.cancelThumbnail(cr, uri);
             }
 
             /**
@@ -1102,9 +968,9 @@
              */
             public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
                     BitmapFactory.Options options) {
-                return InternalThumbnails.getThumbnail(cr, origId,
-                        InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
-                        EXTERNAL_CONTENT_URI, false);
+                final Uri uri = ContentUris.withAppendedId(
+                        Images.Media.EXTERNAL_CONTENT_URI, origId);
+                return InternalThumbnails.getThumbnail(cr, uri, kind, options);
             }
 
             /**
@@ -1117,7 +983,7 @@
              * @param groupId the same groupId used in getThumbnail.
              */
             public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
-                InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
+                cancelThumbnailRequest(cr, origId);
             }
 
             /**
@@ -1134,8 +1000,7 @@
              */
             public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
                     int kind, BitmapFactory.Options options) {
-                return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
-                        EXTERNAL_CONTENT_URI, false);
+                return getThumbnail(cr, origId, kind, options);
             }
 
             /**
@@ -1193,9 +1058,9 @@
              */
             public static final String KIND = "kind";
 
-            public static final int MINI_KIND = 1;
-            public static final int FULL_SCREEN_KIND = 2;
-            public static final int MICRO_KIND = 3;
+            public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
+            public static final int FULL_SCREEN_KIND = ThumbnailConstants.FULL_SCREEN_KIND;
+            public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
             /**
              * The blob raw data of thumbnail
              * <P>Type: DATA STREAM</P>
@@ -2155,8 +2020,9 @@
              * @param origId original video id
              */
             public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
-                InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
-                        InternalThumbnails.DEFAULT_GROUP_ID);
+                final Uri uri = ContentUris.withAppendedId(
+                        Video.Media.EXTERNAL_CONTENT_URI, origId);
+                InternalThumbnails.cancelThumbnail(cr, uri);
             }
 
             /**
@@ -2172,9 +2038,9 @@
              */
             public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
                     BitmapFactory.Options options) {
-                return InternalThumbnails.getThumbnail(cr, origId,
-                        InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
-                        EXTERNAL_CONTENT_URI, true);
+                final Uri uri = ContentUris.withAppendedId(
+                        Video.Media.EXTERNAL_CONTENT_URI, origId);
+                return InternalThumbnails.getThumbnail(cr, uri, kind, options);
             }
 
             /**
@@ -2191,8 +2057,7 @@
              */
             public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
                     int kind, BitmapFactory.Options options) {
-                return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
-                        EXTERNAL_CONTENT_URI, true);
+                return getThumbnail(cr, origId, kind, options);
             }
 
             /**
@@ -2205,7 +2070,7 @@
              * @param groupId the same groupId used in getThumbnail.
              */
             public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
-                InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
+                cancelThumbnailRequest(cr, origId);
             }
 
             /**
@@ -2263,9 +2128,9 @@
              */
             public static final String KIND = "kind";
 
-            public static final int MINI_KIND = 1;
-            public static final int FULL_SCREEN_KIND = 2;
-            public static final int MICRO_KIND = 3;
+            public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
+            public static final int FULL_SCREEN_KIND = ThumbnailConstants.FULL_SCREEN_KIND;
+            public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
 
             /**
              * The width of the thumbnal
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0215e50..350602c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4161,7 +4161,8 @@
             NOTIFICATION_VIBRATION_INTENSITY,
             HAPTIC_FEEDBACK_INTENSITY,
             DISPLAY_COLOR_MODE,
-            ALARM_ALERT
+            ALARM_ALERT,
+            NOTIFICATION_LIGHT_PULSE,
         };
 
         /**
@@ -4364,6 +4365,7 @@
             VALIDATORS.put(WIFI_STATIC_DNS1, WIFI_STATIC_DNS1_VALIDATOR);
             VALIDATORS.put(WIFI_STATIC_DNS2, WIFI_STATIC_DNS2_VALIDATOR);
             VALIDATORS.put(SHOW_BATTERY_PERCENT, SHOW_BATTERY_PERCENT_VALIDATOR);
+            VALIDATORS.put(NOTIFICATION_LIGHT_PULSE, BOOLEAN_VALIDATOR);
         }
 
         /**
@@ -6012,9 +6014,10 @@
                 new SettingsValidators.ComponentNameListValidator(":");
 
         /**
-         * Whether the hush gesture has ever been used // TODO: beverlyt
+         * Whether the hush gesture has ever been used
          * @hide
          */
+        @SystemApi
         public static final String HUSH_GESTURE_USED = "hush_gesture_used";
 
         private static final Validator HUSH_GESTURE_USED_VALIDATOR = BOOLEAN_VALIDATOR;
@@ -6023,12 +6026,73 @@
          * Number of times the user has manually clicked the ringer toggle
          * @hide
          */
+        @SystemApi
         public static final String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count";
 
         private static final Validator MANUAL_RINGER_TOGGLE_COUNT_VALIDATOR =
                 NON_NEGATIVE_INTEGER_VALIDATOR;
 
         /**
+         * Whether to play a sound for charging events.
+         * @hide
+         */
+        public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled";
+
+        /**
+         * Whether to vibrate for wireless charging events.
+         * @hide
+         */
+        public static final String CHARGING_VIBRATION_ENABLED = "charging_vibration_enabled";
+
+        /**
+         * If 0, turning on dnd manually will last indefinitely.
+         * Else if non-negative, turning on dnd manually will last for this many minutes.
+         * Else (if negative), turning on dnd manually will surface a dialog that prompts
+         * user to specify a duration.
+         * @hide
+         */
+        public static final String ZEN_DURATION = "zen_duration";
+
+        private static final Validator ZEN_DURATION_VALIDATOR = ANY_INTEGER_VALIDATOR;
+
+        /** @hide */ public static final int ZEN_DURATION_PROMPT = -1;
+        /** @hide */ public static final int ZEN_DURATION_FOREVER = 0;
+
+        /**
+         * If nonzero, will show the zen upgrade notification when the user toggles DND on/off.
+         * @hide
+         */
+        public static final String SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification";
+
+        /**
+         * If nonzero, will show the zen update settings suggestion.
+         * @hide
+         */
+        public static final String SHOW_ZEN_SETTINGS_SUGGESTION = "show_zen_settings_suggestion";
+
+        /**
+         * If nonzero, zen has not been updated to reflect new changes.
+         * @hide
+         */
+        public static final String ZEN_SETTINGS_UPDATED = "zen_settings_updated";
+
+        /**
+         * If nonzero, zen setting suggestion has been viewed by user
+         * @hide
+         */
+        public static final String ZEN_SETTINGS_SUGGESTION_VIEWED =
+                "zen_settings_suggestion_viewed";
+
+        /**
+         * Whether the in call notification is enabled to play sound during calls.  The value is
+         * boolean (1 or 0).
+         * @hide
+         */
+        public static final String IN_CALL_NOTIFICATION_ENABLED = "in_call_notification_enabled";
+
+        private static final Validator IN_CALL_NOTIFICATION_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
          * Uri of the slice that's presented on the keyguard.
          * Defaults to a slice with the date and next alarm.
          *
@@ -6982,20 +7046,16 @@
          */
         public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker";
 
-        private static final Validator SELECTED_SPELL_CHECKER_VALIDATOR = COMPONENT_NAME_VALIDATOR;
-
         /**
-         * The {@link ComponentName} string of the selected subtype of the selected spell checker
-         * service which is one of the services managed by the text service manager.
+         * {@link android.view.textservice.SpellCheckerSubtype#hashCode()} of the selected subtype
+         * of the selected spell checker service which is one of the services managed by the text
+         * service manager.
          *
          * @hide
          */
         public static final String SELECTED_SPELL_CHECKER_SUBTYPE =
                 "selected_spell_checker_subtype";
 
-        private static final Validator SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR =
-                COMPONENT_NAME_VALIDATOR;
-
         /**
          * Whether spell checker is enabled or not.
          *
@@ -7003,8 +7063,6 @@
          */
         public static final String SPELL_CHECKER_ENABLED = "spell_checker_enabled";
 
-        private static final Validator SPELL_CHECKER_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
-
         /**
          * What happens when the user presses the Power button while in-call
          * and the screen is on.<br/>
@@ -7123,35 +7181,6 @@
         public static final String UI_NIGHT_MODE = "ui_night_mode";
 
         /**
-         * The current device UI theme mode effect SystemUI and Launcher.<br/>
-         * <b>Values:</b><br/>
-         * 0 - The mode that theme will controlled by wallpaper color.<br/>
-         * 1 - The mode that will always light theme.<br/>
-         * 2 - The mode that will always dark theme.<br/>
-         *
-         * @hide
-         */
-        public static final String THEME_MODE = "theme_mode";
-
-        /**
-         * THEME_MODE value for wallpaper mode.
-         * @hide
-         */
-        public static final int THEME_MODE_WALLPAPER = 0;
-
-        /**
-         * THEME_MODE value for light theme mode.
-         * @hide
-         */
-        public static final int THEME_MODE_LIGHT = 1;
-
-        /**
-         * THEME_MODE value for dark theme mode.
-         * @hide
-         */
-        public static final int THEME_MODE_DARK = 2;
-
-        /**
          * Whether screensavers are enabled.
          * @hide
          */
@@ -7302,7 +7331,7 @@
          * see and assist with all of the user's notifications.
          *
          * @deprecated Use
-         * {@link NotificationManager#isNotificationListenerAccessGranted(ComponentName)}.
+         * {@link NotificationManager#isNotificationAssistantAccessGranted(ComponentName)}.
          * @hide
          */
         @Deprecated
@@ -7318,7 +7347,7 @@
          *
          * @hide
          * @deprecated Use
-         * {@link NotificationManager#isNotificationAssistantAccessGranted(ComponentName)}.
+         * {@link NotificationManager#isNotificationListenerAccessGranted(ComponentName)}.
          */
         @Deprecated
         public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
@@ -7442,9 +7471,6 @@
          */
         public static final String SLEEP_TIMEOUT = "sleep_timeout";
 
-        private static final Validator SLEEP_TIMEOUT_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(-1, Integer.MAX_VALUE);
-
         /**
          * Controls whether double tap to wake is enabled.
          * @hide
@@ -7537,6 +7563,15 @@
         public static final String FLASHLIGHT_ENABLED = "flashlight_enabled";
 
         /**
+         * Whether or not face unlock is allowed on Keyguard.
+         * @hide
+         */
+        public static final String FACE_UNLOCK_KEYGUARD_ENABLED = "face_unlock_keyguard_enabled";
+
+        private static final Validator FACE_UNLOCK_KEYGUARD_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
+        /**
          * Whether the assist gesture should be enabled.
          *
          * @hide
@@ -7553,9 +7588,6 @@
          */
         public static final String ASSIST_GESTURE_SENSITIVITY = "assist_gesture_sensitivity";
 
-        private static final Validator ASSIST_GESTURE_SENSITIVITY_VALIDATOR =
-                new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 1.0f);
-
         /**
          * Whether the assist gesture should silence alerts.
          *
@@ -7585,8 +7617,6 @@
          */
         public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
 
-        private static final Validator ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR = BOOLEAN_VALIDATOR;
-
         /**
          * Control whether Night display is currently activated.
          * @hide
@@ -7914,6 +7944,7 @@
          *
          * @hide
          */
+        @SystemApi
         public static final String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
 
         /** @hide */ public static final int VOLUME_HUSH_OFF = 0;
@@ -8005,14 +8036,10 @@
             WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,            // moved to global
             WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,               // moved to global
             WIFI_NUM_OPEN_NETWORKS_KEPT,                        // moved to global
-            SELECTED_SPELL_CHECKER,
-            SELECTED_SPELL_CHECKER_SUBTYPE,
-            SPELL_CHECKER_ENABLED,
             MOUNT_PLAY_NOTIFICATION_SND,
             MOUNT_UMS_AUTOSTART,
             MOUNT_UMS_PROMPT,
             MOUNT_UMS_NOTIFY_ENABLED,
-            SLEEP_TIMEOUT,
             DOUBLE_TAP_TO_WAKE,
             WAKE_GESTURE_ENABLED,
             LONG_PRESS_TIMEOUT,
@@ -8041,9 +8068,8 @@
             DOZE_PULSE_ON_DOUBLE_TAP,
             NFC_PAYMENT_DEFAULT_COMPONENT,
             AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
+            FACE_UNLOCK_KEYGUARD_ENABLED,
             ASSIST_GESTURE_ENABLED,
-            ASSIST_GESTURE_SENSITIVITY,
-            ASSIST_GESTURE_SETUP_COMPLETE,
             ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
             ASSIST_GESTURE_WAKE_ENABLED,
             VR_DISPLAY_MODE,
@@ -8058,6 +8084,16 @@
             VOLUME_HUSH_GESTURE,
             MANUAL_RINGER_TOGGLE_COUNT,
             HUSH_GESTURE_USED,
+            IN_CALL_NOTIFICATION_ENABLED,
+            LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+            LOCK_SCREEN_SHOW_NOTIFICATIONS,
+            ZEN_DURATION,
+            SHOW_ZEN_UPGRADE_NOTIFICATION,
+            SHOW_ZEN_SETTINGS_SUGGESTION,
+            ZEN_SETTINGS_UPDATED,
+            ZEN_SETTINGS_SUGGESTION_VIEWED,
+            CHARGING_SOUNDS_ENABLED,
+            CHARGING_VIBRATION_ENABLED,
         };
 
         /**
@@ -8134,15 +8170,10 @@
             VALIDATORS.put(WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
                     WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR);
             VALIDATORS.put(WIFI_NUM_OPEN_NETWORKS_KEPT, WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR);
-            VALIDATORS.put(SELECTED_SPELL_CHECKER, SELECTED_SPELL_CHECKER_VALIDATOR);
-            VALIDATORS.put(SELECTED_SPELL_CHECKER_SUBTYPE,
-                    SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR);
-            VALIDATORS.put(SPELL_CHECKER_ENABLED, SPELL_CHECKER_ENABLED_VALIDATOR);
             VALIDATORS.put(MOUNT_PLAY_NOTIFICATION_SND, MOUNT_PLAY_NOTIFICATION_SND_VALIDATOR);
             VALIDATORS.put(MOUNT_UMS_AUTOSTART, MOUNT_UMS_AUTOSTART_VALIDATOR);
             VALIDATORS.put(MOUNT_UMS_PROMPT, MOUNT_UMS_PROMPT_VALIDATOR);
             VALIDATORS.put(MOUNT_UMS_NOTIFY_ENABLED, MOUNT_UMS_NOTIFY_ENABLED_VALIDATOR);
-            VALIDATORS.put(SLEEP_TIMEOUT, SLEEP_TIMEOUT_VALIDATOR);
             VALIDATORS.put(DOUBLE_TAP_TO_WAKE, DOUBLE_TAP_TO_WAKE_VALIDATOR);
             VALIDATORS.put(WAKE_GESTURE_ENABLED, WAKE_GESTURE_ENABLED_VALIDATOR);
             VALIDATORS.put(LONG_PRESS_TIMEOUT, LONG_PRESS_TIMEOUT_VALIDATOR);
@@ -8181,9 +8212,8 @@
             VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
             VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
                     AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR);
+            VALIDATORS.put(FACE_UNLOCK_KEYGUARD_ENABLED, FACE_UNLOCK_KEYGUARD_ENABLED_VALIDATOR);
             VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR);
-            VALIDATORS.put(ASSIST_GESTURE_SENSITIVITY, ASSIST_GESTURE_SENSITIVITY_VALIDATOR);
-            VALIDATORS.put(ASSIST_GESTURE_SETUP_COMPLETE, ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR);
             VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
                     ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR);
             VALIDATORS.put(ASSIST_GESTURE_WAKE_ENABLED, ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR);
@@ -8206,6 +8236,16 @@
                     ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR); //legacy restore setting
             VALIDATORS.put(HUSH_GESTURE_USED, HUSH_GESTURE_USED_VALIDATOR);
             VALIDATORS.put(MANUAL_RINGER_TOGGLE_COUNT, MANUAL_RINGER_TOGGLE_COUNT_VALIDATOR);
+            VALIDATORS.put(IN_CALL_NOTIFICATION_ENABLED, IN_CALL_NOTIFICATION_ENABLED_VALIDATOR);
+            VALIDATORS.put(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(LOCK_SCREEN_SHOW_NOTIFICATIONS, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(ZEN_DURATION, ZEN_DURATION_VALIDATOR);
+            VALIDATORS.put(SHOW_ZEN_UPGRADE_NOTIFICATION, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(SHOW_ZEN_SETTINGS_SUGGESTION, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(ZEN_SETTINGS_UPDATED, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(ZEN_SETTINGS_SUGGESTION_VIEWED, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(CHARGING_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(CHARGING_VIBRATION_ENABLED, BOOLEAN_VALIDATOR);
         }
 
         /**
@@ -8679,16 +8719,20 @@
 
         /**
          * Whether to play a sound for charging events.
+         * @deprecated Use {@link android.provider.Settings.Secure#CHARGING_SOUNDS_ENABLED} instead
          * @hide
          */
+        @Deprecated
         public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled";
 
         private static final Validator CHARGING_SOUNDS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
          * Whether to vibrate for wireless charging events.
+         * @deprecated Use {@link android.provider.Settings.Secure#CHARGING_VIBRATION_ENABLED}
          * @hide
          */
+        @Deprecated
         public static final String CHARGING_VIBRATION_ENABLED = "charging_vibration_enabled";
 
         private static final Validator CHARGING_VIBRATION_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
@@ -9032,6 +9076,14 @@
             "location_background_throttle_package_whitelist";
 
         /**
+         * Maximum staleness allowed for last location when returned to clients with only foreground
+         * location permissions.
+         * @hide
+         */
+        public static final String LOCATION_LAST_LOCATION_MAX_AGE_MILLIS =
+                "location_last_location_max_age_millis";
+
+        /**
         * Whether TV will switch to MHL port when a mobile device is plugged in.
         * (0 = false, 1 = true)
         * @hide
@@ -9168,6 +9220,19 @@
        /** {@hide} */
        public static final String STORAGE_BENCHMARK_INTERVAL = "storage_benchmark_interval";
 
+        /**
+         * Whether or not Settings should enable psd API.
+         * {@hide}
+         */
+        public static final String SETTINGS_USE_PSD_API = "settings_use_psd_api";
+
+        /**
+         * Whether or not Settings should enable external provider API.
+         * {@hide}
+         */
+        public static final String SETTINGS_USE_EXTERNAL_PROVIDER_API =
+                "settings_use_external_provider_api";
+
        /**
         * Sample validity in seconds to configure for the system DNS resolver.
         * {@hide}
@@ -9775,6 +9840,18 @@
                 "recommended_network_evaluator_cache_expiry_ms";
 
         /**
+         * Whether wifi scan throttle is enabled or not.
+         * This is intended to be used via adb commands or a menu in developer option to turn off
+         * the default wifi scan throttling mechanism for apps.
+         *
+         * Type: int (0 for false, 1 for true)
+         * @hide
+         */
+        public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
+
+        private static final Validator WIFI_SCAN_THROTTLE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
         * Settings to allow BLE scans to be enabled even when Bluetooth is turned off for
         * connectivity.
         * @hide
@@ -9919,6 +9996,39 @@
         public static final String WIFI_SCORE_PARAMS =
                 "wifi_score_params";
 
+        /**
+         * Setting to enable logging WifiIsUnusableEvent in metrics
+         * which gets triggered when wifi becomes unusable.
+         * Disabled by default, and setting it to 1 will enable it.
+         * @hide
+         */
+        public static final String WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED =
+                "wifi_is_unusable_event_metrics_enabled";
+
+        /**
+         * The minimum number of txBad the framework has to observe
+         * to trigger a wifi data stall.
+         * @hide
+         */
+        public static final String WIFI_DATA_STALL_MIN_TX_BAD =
+                "wifi_data_stall_min_tx_bad";
+
+        /**
+         * The minimum number of txSuccess the framework has to observe
+         * to trigger a wifi data stall when rxSuccess is 0.
+         * @hide
+         */
+        public static final String WIFI_DATA_STALL_MIN_TX_SUCCESS_WITHOUT_RX =
+                "wifi_data_stall_min_tx_success_without_rx";
+
+        /**
+         * Setting to enable logging Wifi LinkSpeedCounts in metrics.
+         * Disabled by default, and setting it to 1 will enable it.
+         * @hide
+         */
+        public static final String WIFI_LINK_SPEED_METRICS_ENABLED =
+                "wifi_link_speed_metrics_enabled";
+
        /**
         * The maximum number of times we will retry a connection to an access
         * point for which we have failed in acquiring an IP address from DHCP.
@@ -10549,6 +10659,15 @@
         public static final String ACTIVITY_MANAGER_CONSTANTS = "activity_manager_constants";
 
         /**
+         * Feature flag to enable or disable the activity starts logging feature.
+         * Type: int (0 for false, 1 for true)
+         * Default: 0
+         * @hide
+         */
+        public static final String ACTIVITY_STARTS_LOGGING_ENABLED
+                = "activity_starts_logging_enabled";
+
+        /**
          * App ops specific settings.
          * This is encoded as a key=value list, separated by commas. Ex:
          *
@@ -10961,6 +11080,8 @@
          * proc_state_cpu_times_read_delay_ms (long)
          * external_stats_collection_rate_limit_ms (long)
          * battery_level_collection_delay_ms (long)
+         * max_history_files (int)
+         * max_history_buffer_kb (int)
          * </pre>
          *
          * <p>
@@ -11070,6 +11191,7 @@
          *
          * <pre>
          * enabled                  (boolean)
+         * disable_home             (boolean)
          * disable_tilt_to_wake     (boolean)
          * disable_touch_to_wake    (boolean)
          * </pre>
@@ -11272,6 +11394,14 @@
         public static final String EMERGENCY_AFFORDANCE_NEEDED = "emergency_affordance_needed";
 
         /**
+         * Enable faster emergency phone call feature.
+         * The value is a boolean (1 or 0).
+         * @hide
+         */
+        public static final String FASTER_EMERGENCY_PHONE_CALL_ENABLED =
+                "faster_emergency_phone_call_enabled";
+
+        /**
          * See RIL_PreferredNetworkType in ril.h
          * @hide
          */
@@ -11697,18 +11827,27 @@
         public static final String ZEN_MODE_CONFIG_ETAG = "zen_mode_config_etag";
 
         /**
-         * If 0, turning on dnd manually will last indefinitely.
-         * Else if non-negative, turning on dnd manually will last for this many minutes.
-         * Else (if negative), turning on dnd manually will surface a dialog that prompts
-         * user to specify a duration.
+         * @deprecated Use {@link android.provider.Settings.Secure#ZEN_DURATION} instead
          * @hide
          */
+        @Deprecated
         public static final String ZEN_DURATION = "zen_duration";
 
         private static final Validator ZEN_DURATION_VALIDATOR = ANY_INTEGER_VALIDATOR;
 
-        /** @hide */ public static final int ZEN_DURATION_PROMPT = -1;
-        /** @hide */ public static final int ZEN_DURATION_FOREVER = 0;
+        /**
+         * @deprecated Use {@link android.provider.Settings.Secure#ZEN_DURATION_PROMPT} instead
+         * @hide
+         */
+        @Deprecated
+        public static final int ZEN_DURATION_PROMPT = -1;
+
+        /**
+         * @deprecated Use {@link android.provider.Settings.Secure#ZEN_DURATION_FOREVER} instead
+         * @hide
+         */
+        @Deprecated
+        public static final int ZEN_DURATION_FOREVER = 0;
 
         /**
          * Defines global heads up toggle.  One of HEADS_UP_OFF, HEADS_UP_ON.
@@ -12043,6 +12182,33 @@
                 "autofill_compat_mode_allowed_packages";
 
         /**
+         * Level of autofill logging.
+         *
+         * <p>Valid values are
+         * {@link android.view.autofill.AutofillManager#NO_LOGGING},
+         * {@link android.view.autofill.AutofillManager#FLAG_ADD_CLIENT_DEBUG}, or
+         * {@link android.view.autofill.AutofillManager#FLAG_ADD_CLIENT_VERBOSE}.
+         *
+         * @hide
+         */
+        public static final String AUTOFILL_LOGGING_LEVEL = "autofill_logging_level";
+
+        /**
+         * Maximum number of partitions that can be allowed in an autofill session.
+         *
+         * @hide
+         */
+        public static final String AUTOFILL_MAX_PARTITIONS_SIZE = "autofill_max_partitions_size";
+
+        /**
+         * Maximum number of visible datasets in the Autofill dataset picker UI, or {@code 0} to use
+         * the default value from resources.
+         *
+         * @hide
+         */
+        public static final String AUTOFILL_MAX_VISIBLE_DATASETS = "autofill_max_visible_datasets";
+
+        /**
          * Exemptions to the hidden API blacklist.
          *
          * @hide
@@ -12186,6 +12352,7 @@
             VALIDATORS.put(SOFT_AP_TIMEOUT_ENABLED, SOFT_AP_TIMEOUT_ENABLED_VALIDATOR);
             VALIDATORS.put(WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON,
                     WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR);
+            VALIDATORS.put(WIFI_SCAN_THROTTLE_ENABLED, WIFI_SCAN_THROTTLE_ENABLED_VALIDATOR);
             VALIDATORS.put(APP_AUTO_RESTRICTION_ENABLED, APP_AUTO_RESTRICTION_ENABLED_VALIDATOR);
             VALIDATORS.put(ZEN_DURATION, ZEN_DURATION_VALIDATOR);
             VALIDATORS.put(CHARGING_VIBRATION_ENABLED, CHARGING_VIBRATION_ENABLED_VALIDATOR);
@@ -12225,8 +12392,16 @@
         // Certain settings have been moved from global to the per-user secure namespace
         private static final HashSet<String> MOVED_TO_SECURE;
         static {
-            MOVED_TO_SECURE = new HashSet<>(1);
-            MOVED_TO_SECURE.add(Settings.Global.INSTALL_NON_MARKET_APPS);
+            MOVED_TO_SECURE = new HashSet<>(8);
+            MOVED_TO_SECURE.add(Global.INSTALL_NON_MARKET_APPS);
+            MOVED_TO_SECURE.add(Global.ZEN_DURATION);
+            MOVED_TO_SECURE.add(Global.SHOW_ZEN_UPGRADE_NOTIFICATION);
+            MOVED_TO_SECURE.add(Global.SHOW_ZEN_SETTINGS_SUGGESTION);
+            MOVED_TO_SECURE.add(Global.ZEN_SETTINGS_UPDATED);
+            MOVED_TO_SECURE.add(Global.ZEN_SETTINGS_SUGGESTION_VIEWED);
+            MOVED_TO_SECURE.add(Global.CHARGING_SOUNDS_ENABLED);
+            MOVED_TO_SECURE.add(Global.CHARGING_VIBRATION_ENABLED);
+
         }
 
         /** @hide */
@@ -12902,28 +13077,37 @@
          */
         public static final String SHOW_MUTE_IN_CRASH_DIALOG = "show_mute_in_crash_dialog";
 
+
         /**
          * If nonzero, will show the zen upgrade notification when the user toggles DND on/off.
          * @hide
+         * @deprecated - Use {@link android.provider.Settings.Secure#SHOW_ZEN_UPGRADE_NOTIFICATION}
          */
+        @Deprecated
         public static final String SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification";
 
         /**
          * If nonzero, will show the zen update settings suggestion.
          * @hide
+         * @deprecated - Use {@link android.provider.Settings.Secure#SHOW_ZEN_SETTINGS_SUGGESTION}
          */
+        @Deprecated
         public static final String SHOW_ZEN_SETTINGS_SUGGESTION = "show_zen_settings_suggestion";
 
         /**
          * If nonzero, zen has not been updated to reflect new changes.
+         * @deprecated - Use {@link android.provider.Settings.Secure#ZEN_SETTINGS_UPDATED}
          * @hide
          */
+        @Deprecated
         public static final String ZEN_SETTINGS_UPDATED = "zen_settings_updated";
 
         /**
-         * If nonzero, zen setting suggestion has beem viewed by user
+         * If nonzero, zen setting suggestion has been viewed by user
          * @hide
+         * @deprecated - Use {@link android.provider.Settings.Secure#ZEN_SETTINGS_SUGGESTION_VIEWED}
          */
+        @Deprecated
         public static final String ZEN_SETTINGS_SUGGESTION_VIEWED =
                 "zen_settings_suggestion_viewed";
 
@@ -12974,6 +13158,21 @@
          */
         public static final String GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS =
                 "gnss_hal_location_request_duration_millis";
+
+        /**
+         * Binder call stats settings.
+         *
+         * The following strings are supported as keys:
+         * <pre>
+         *     enabled              (boolean)
+         *     detailed_tracking    (boolean)
+         *     upload_data          (boolean)
+         *     sampling_interval    (int)
+         * </pre>
+         *
+         * @hide
+         */
+        public static final String BINDER_CALLS_STATS = "binder_calls_stats";
     }
 
     /**
diff --git a/core/java/android/security/net/config/ManifestConfigSource.java b/core/java/android/security/net/config/ManifestConfigSource.java
index 79115a5..b885e72 100644
--- a/core/java/android/security/net/config/ManifestConfigSource.java
+++ b/core/java/android/security/net/config/ManifestConfigSource.java
@@ -75,7 +75,7 @@
                 // should use the network security config.
                 boolean usesCleartextTraffic =
                         (mApplicationInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) != 0
-                        && mApplicationInfo.targetSandboxVersion < 2;
+                        && !mApplicationInfo.isInstantApp();
                 source = new DefaultConfigSource(usesCleartextTraffic, mApplicationInfo);
             }
             mConfigSource = source;
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 52f48ef..57068fa 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -185,7 +185,7 @@
                 .addCertificatesEntryRef(
                         new CertificatesEntryRef(SystemCertificateSource.getInstance(), false));
         final boolean cleartextTrafficPermitted = info.targetSdkVersion < Build.VERSION_CODES.P
-                && info.targetSandboxVersion < 2;
+                && !info.isInstantApp();
         builder.setCleartextTrafficPermitted(cleartextTrafficPermitted);
         // Applications targeting N and above must opt in into trusting the user added certificate
         // store.
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 1cd76d2..e5e1c92 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -65,16 +65,36 @@
     /**
      * Manifest metadata key for the resource string containing the name of the default field
      * classification algorithm.
+     *
+     * @deprecated Use {@link #RESOURCE_DEFAULT_ALGORITHM} instead.
      */
+    @Deprecated
     public static final String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM =
             "android.autofill.field_classification.default_algorithm";
+
     /**
      * Manifest metadata key for the resource string array containing the names of all field
      * classification algorithms provided by the service.
+     *
+     * @deprecated Use {@link #RESOURCE_AVAILABLE_ALGORITHMS} instead.
      */
+    @Deprecated
     public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
             "android.autofill.field_classification.available_algorithms";
 
+    /**
+     * Name of the resource string containing the name of the default field
+     * classification algorithm.
+     */
+    public static final String RESOURCE_DEFAULT_ALGORITHM =
+            "autofill_field_classification_default_algorithm";
+
+   /**
+    * Name of the resource string array containing the names of all field
+    * classification algorithms provided by the service.
+    */
+    public static final String RESOURCE_AVAILABLE_ALGORITHMS =
+            "autofill_field_classification_available_algorithms";
 
     /** {@hide} **/
     public static final String EXTRA_SCORES = "scores";
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 4943fc8..b845250 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -119,7 +119,8 @@
  *
  * <p>But it is only triggered when all conditions below are met:
  * <ul>
- *   <li>The {@link SaveInfo} associated with the {@link FillResponse} is not {@code null}.
+ *   <li>The {@link SaveInfo} associated with the {@link FillResponse} is not {@code null} neither
+ *       has the {@link #FLAG_DELAY_SAVE} flag.
  *   <li>The {@link AutofillValue}s of all required views (as set by the {@code requiredIds} passed
  *       to the {@link SaveInfo.Builder} constructor are not empty.
  *   <li>The {@link AutofillValue} of at least one view (be it required or optional) has changed
@@ -234,10 +235,26 @@
      */
     public static final int FLAG_DONT_SAVE_ON_FINISH = 0x2;
 
+    /**
+     * Don't trigger the autofill save UI when the autofill context associated with the response
+     * associated with this {@link SaveInfo} is {@link AutofillManager#commit() committed},
+     * but keep its {@link FillContext} so it's delivered in a future
+     * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback) save request} of an
+     * activity belonging to the same task.
+     *
+     * <p>This flag should be used when the service detects that the application uses
+     * multiple screens to implement an autofillable workflow (for example, one screen for the
+     * username field, another for password).
+     */
+    // TODO(b/112051762): improve documentation: add example, document relationship with other
+    // flagss, etc...
+    public static final int FLAG_DELAY_SAVE = 0x4;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "FLAG_" }, value = {
             FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE,
-            FLAG_DONT_SAVE_ON_FINISH
+            FLAG_DONT_SAVE_ON_FINISH,
+            FLAG_DELAY_SAVE
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface SaveInfoFlags{}
@@ -410,14 +427,15 @@
          * Sets flags changing the save behavior.
          *
          * @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE},
-         * {@link #FLAG_DONT_SAVE_ON_FINISH}, or {@code 0}.
+         * {@link #FLAG_DONT_SAVE_ON_FINISH}, {@link #FLAG_DELAY_SAVE}, or {@code 0}.
          * @return This builder.
          */
         public @NonNull Builder setFlags(@SaveInfoFlags int flags) {
             throwIfDestroyed();
 
             mFlags = Preconditions.checkFlagsArgument(flags,
-                    FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE | FLAG_DONT_SAVE_ON_FINISH);
+                    FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE | FLAG_DONT_SAVE_ON_FINISH
+                            | FLAG_DELAY_SAVE);
             return this;
         }
 
@@ -663,14 +681,16 @@
          * Builds a new {@link SaveInfo} instance.
          *
          * @throws IllegalStateException if no
-         * {@link #SaveInfo.Builder(int, AutofillId[]) required ids}
-         * or {@link #setOptionalIds(AutofillId[]) optional ids} were set
+         * {@link #SaveInfo.Builder(int, AutofillId[]) required ids},
+         * or {@link #setOptionalIds(AutofillId[]) optional ids}, or {@link #FLAG_DELAY_SAVE}
+         * were set
          */
         public SaveInfo build() {
             throwIfDestroyed();
             Preconditions.checkState(
-                    !ArrayUtils.isEmpty(mRequiredIds) || !ArrayUtils.isEmpty(mOptionalIds),
-                    "must have at least one required or optional id");
+                    !ArrayUtils.isEmpty(mRequiredIds) || !ArrayUtils.isEmpty(mOptionalIds)
+                            || (mFlags & FLAG_DELAY_SAVE) != 0,
+                    "must have at least one required or optional id or FLAG_DELAYED_SAVE");
             mDestroyed = true;
             return new SaveInfo(this);
         }
diff --git a/core/java/android/service/autofill/SaveRequest.java b/core/java/android/service/autofill/SaveRequest.java
index 4f85e6b..c9b5b55 100644
--- a/core/java/android/service/autofill/SaveRequest.java
+++ b/core/java/android/service/autofill/SaveRequest.java
@@ -52,6 +52,12 @@
     }
 
     /**
+     * Gets the contexts associated with each previous fill request.
+     *
+     * <p><b>Note:</b> Starting on Android {@link android.os.Build.VERSION_CODES#Q}, it could also
+     * include contexts from requests whose {@link SaveInfo} had the
+     * {@link SaveInfo#FLAG_DELAY_SAVE} flag.
+     *
      * @return The contexts associated with each previous fill request.
      */
     public @NonNull List<FillContext> getFillContexts() {
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 7348cf6..e0c354a 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -65,6 +65,18 @@
     public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
 
     /**
+     * Data type: ArrayList of {@link android.app.Notification.Action}.
+     * Used to suggest extra actions for a notification.
+     */
+    public static final String KEY_SMART_ACTIONS = "key_smart_actions";
+
+    /**
+     * Data type: ArrayList of {@link CharSequence}.
+     * Used to suggest smart replies for a notification.
+     */
+    public static final String KEY_SMART_REPLIES = "key_smart_replies";
+
+    /**
      * Create a notification adjustment.
      *
      * @param pkg The package of the notification.
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index b6c6bdc..5a7a83f 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -151,14 +151,14 @@
     @Override
     public String toString() {
         return new StringBuilder(Condition.class.getSimpleName()).append('[')
-            .append("id=").append(id)
-            .append(",summary=").append(summary)
-            .append(",line1=").append(line1)
-            .append(",line2=").append(line2)
-            .append(",icon=").append(icon)
-            .append(",state=").append(stateToString(state))
-            .append(",flags=").append(flags)
-            .append(']').toString();
+                .append("state=").append(stateToString(state))
+                .append(",id=").append(id)
+                .append(",summary=").append(summary)
+                .append(",line1=").append(line1)
+                .append(",line2=").append(line2)
+                .append(",icon=").append(icon)
+                .append(",flags=").append(flags)
+                .append(']').toString();
     }
 
     /** @hide */
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 18e0ab0..81889c1 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -19,6 +19,8 @@
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
@@ -34,6 +36,22 @@
 
 /**
  * A service that helps the user manage notifications.
+ * <p>
+ * Only one notification assistant can be active at a time. Unlike notification listener services,
+ * assistant services can additionally modify certain aspects about notifications
+ * (see {@link Adjustment}) before they are posted.
+ *<p>
+ * A note about managed profiles: Unlike {@link NotificationListenerService listener services},
+ * NotificationAssistantServices are allowed to run in managed profiles
+ * (see {@link DevicePolicyManager#isManagedProfile(ComponentName)}), so they can access the
+ * information they need to create good {@link Adjustment adjustments}. To maintain the contract
+ * with {@link NotificationListenerService}, an assistant service will receive all of the
+ * callbacks from {@link NotificationListenerService} for the current user, managed profiles of
+ * that user, and ones that affect all users. However,
+ * {@link #onNotificationEnqueued(StatusBarNotification)} will only be called for notifications
+ * sent to the current user, and {@link Adjustment adjuments} will only be accepted for the
+ * current user.
+ *
  * @hide
  */
 @SystemApi
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index a7d70d0..09eecd8 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1426,6 +1426,8 @@
         private boolean mShowBadge;
         private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
         private boolean mHidden;
+        private ArrayList<Notification.Action> mSmartActions;
+        private ArrayList<CharSequence> mSmartReplies;
 
         public Ranking() {}
 
@@ -1556,6 +1558,20 @@
         }
 
         /**
+         * @hide
+         */
+        public List<Notification.Action> getSmartActions() {
+            return mSmartActions;
+        }
+
+        /**
+         * @hide
+         */
+        public List<CharSequence> getSmartReplies() {
+            return mSmartReplies;
+        }
+
+        /**
          * Returns whether this notification can be displayed as a badge.
          *
          * @return true if the notification can be displayed as a badge, false otherwise.
@@ -1583,7 +1599,8 @@
                 CharSequence explanation, String overrideGroupKey,
                 NotificationChannel channel, ArrayList<String> overridePeople,
                 ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
-                int userSentiment, boolean hidden) {
+                int userSentiment, boolean hidden, ArrayList<Notification.Action> smartActions,
+                ArrayList<CharSequence> smartReplies) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1599,6 +1616,8 @@
             mShowBadge = showBadge;
             mUserSentiment = userSentiment;
             mHidden = hidden;
+            mSmartActions = smartActions;
+            mSmartReplies = smartReplies;
         }
 
         /**
@@ -1648,6 +1667,8 @@
         private ArrayMap<String, Boolean> mShowBadge;
         private ArrayMap<String, Integer> mUserSentiment;
         private ArrayMap<String, Boolean> mHidden;
+        private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions;
+        private ArrayMap<String, ArrayList<CharSequence>> mSmartReplies;
 
         private RankingMap(NotificationRankingUpdate rankingUpdate) {
             mRankingUpdate = rankingUpdate;
@@ -1676,7 +1697,8 @@
                     getVisibilityOverride(key), getSuppressedVisualEffects(key),
                     getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
                     getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
-                    getShowBadge(key), getUserSentiment(key), getHidden(key));
+                    getShowBadge(key), getUserSentiment(key), getHidden(key), getSmartActions(key),
+                    getSmartReplies(key));
             return rank >= 0;
         }
 
@@ -1814,6 +1836,24 @@
             return hidden == null ? false : hidden.booleanValue();
         }
 
+        private ArrayList<Notification.Action> getSmartActions(String key) {
+            synchronized (this) {
+                if (mSmartActions == null) {
+                    buildSmartActions();
+                }
+            }
+            return mSmartActions.get(key);
+        }
+
+        private ArrayList<CharSequence> getSmartReplies(String key) {
+            synchronized (this) {
+                if (mSmartReplies == null) {
+                    buildSmartReplies();
+                }
+            }
+            return mSmartReplies.get(key);
+        }
+
         // Locked by 'this'
         private void buildRanksLocked() {
             String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1931,6 +1971,24 @@
             }
         }
 
+        // Locked by 'this'
+        private void buildSmartActions() {
+            Bundle smartActions = mRankingUpdate.getSmartActions();
+            mSmartActions = new ArrayMap<>(smartActions.size());
+            for (String key : smartActions.keySet()) {
+                mSmartActions.put(key, smartActions.getParcelableArrayList(key));
+            }
+        }
+
+        // Locked by 'this'
+        private void buildSmartReplies() {
+            Bundle smartReplies = mRankingUpdate.getSmartReplies();
+            mSmartReplies = new ArrayMap<>(smartReplies.size());
+            for (String key : smartReplies.keySet()) {
+                mSmartReplies.put(key, smartReplies.getCharSequenceArrayList(key));
+            }
+        }
+
         // ----------- Parcelable
 
         @Override
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 00c47ec..c67fad0 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -37,12 +37,15 @@
     private final Bundle mShowBadge;
     private final Bundle mUserSentiment;
     private final Bundle mHidden;
+    private final Bundle mSmartActions;
+    private final Bundle mSmartReplies;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
             Bundle visibilityOverrides, Bundle suppressedVisualEffects,
             int[] importance, Bundle explanation, Bundle overrideGroupKeys,
             Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
-            Bundle showBadge, Bundle userSentiment, Bundle hidden) {
+            Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions,
+            Bundle smartReplies) {
         mKeys = keys;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
@@ -56,6 +59,8 @@
         mShowBadge = showBadge;
         mUserSentiment = userSentiment;
         mHidden = hidden;
+        mSmartActions = smartActions;
+        mSmartReplies = smartReplies;
     }
 
     public NotificationRankingUpdate(Parcel in) {
@@ -73,6 +78,8 @@
         mShowBadge = in.readBundle();
         mUserSentiment = in.readBundle();
         mHidden = in.readBundle();
+        mSmartActions = in.readBundle();
+        mSmartReplies = in.readBundle();
     }
 
     @Override
@@ -95,6 +102,8 @@
         out.writeBundle(mShowBadge);
         out.writeBundle(mUserSentiment);
         out.writeBundle(mHidden);
+        out.writeBundle(mSmartActions);
+        out.writeBundle(mSmartReplies);
     }
 
     public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -159,4 +168,12 @@
     public Bundle getHidden() {
         return mHidden;
     }
+
+    public Bundle getSmartActions() {
+        return mSmartActions;
+    }
+
+    public Bundle getSmartReplies() {
+        return mSmartReplies;
+    }
 }
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 5546e80..5ac36afc 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -240,11 +240,29 @@
                 .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
                 .append(",suppressedVisualEffects=").append(suppressedVisualEffects)
                 .append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd)
-                .append(",automaticRules=").append(automaticRules)
-                .append(",manualRule=").append(manualRule)
+                .append(",\nautomaticRules=").append(rulesToString())
+                .append(",\nmanualRule=").append(manualRule)
                 .append(']').toString();
     }
 
+    private String rulesToString() {
+        if (automaticRules.isEmpty()) {
+            return "{}";
+        }
+
+        StringBuilder buffer = new StringBuilder(automaticRules.size() * 28);
+        buffer.append('{');
+        for (int i = 0; i < automaticRules.size(); i++) {
+            if (i > 0) {
+                buffer.append(",\n");
+            }
+            Object value = automaticRules.valueAt(i);
+            buffer.append(value);
+        }
+        buffer.append('}');
+        return buffer.toString();
+    }
+
     private Diff diff(ZenModeConfig to) {
         final Diff d = new Diff();
         if (to == null) {
@@ -498,7 +516,7 @@
                     Boolean allowWhenScreenOff = unsafeBoolean(parser, ALLOW_ATT_SCREEN_OFF);
                     if (allowWhenScreenOff != null) {
                         readSuppressedEffects = true;
-                        if (allowWhenScreenOff) {
+                        if (!allowWhenScreenOff) {
                             rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS
                                     | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
                         }
@@ -506,7 +524,7 @@
                     Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON);
                     if (allowWhenScreenOn != null) {
                         readSuppressedEffects = true;
-                        if (allowWhenScreenOn) {
+                        if (!allowWhenScreenOn) {
                             rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK;
                         }
                     }
@@ -1007,12 +1025,30 @@
         return true;
     }
 
+    /**
+     * Returns whether the conditionId is a valid ScheduleCondition.
+     * If allowNever is true, this will return true even if the ScheduleCondition never occurs.
+     */
+    public static boolean isValidScheduleConditionId(Uri conditionId, boolean allowNever) {
+        ScheduleInfo info;
+        try {
+            info = tryParseScheduleConditionId(conditionId);
+        } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
+            return false;
+        }
+
+        if (info == null || (!allowNever && (info.days == null || info.days.length == 0))) {
+            return false;
+        }
+        return true;
+    }
+
     public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
         final boolean isSchedule =  conditionId != null
-                && conditionId.getScheme().equals(Condition.SCHEME)
-                && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
+                && Condition.SCHEME.equals(conditionId.getScheme())
+                && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority())
                 && conditionId.getPathSegments().size() == 1
-                && conditionId.getPathSegments().get(0).equals(ZenModeConfig.SCHEDULE_PATH);
+                && ZenModeConfig.SCHEDULE_PATH.equals(conditionId.getPathSegments().get(0));
         if (!isSchedule) return null;
         final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
         final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
@@ -1110,10 +1146,10 @@
 
     public static EventInfo tryParseEventConditionId(Uri conditionId) {
         final boolean isEvent = conditionId != null
-                && conditionId.getScheme().equals(Condition.SCHEME)
-                && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
+                && Condition.SCHEME.equals(conditionId.getScheme())
+                && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority())
                 && conditionId.getPathSegments().size() == 1
-                && conditionId.getPathSegments().get(0).equals(EVENT_PATH);
+                && EVENT_PATH.equals(conditionId.getPathSegments().get(0));
         if (!isEvent) return null;
         final EventInfo rt = new EventInfo();
         rt.userId = tryParseInt(conditionId.getQueryParameter("userId"), UserHandle.USER_NULL);
@@ -1322,14 +1358,14 @@
         @Override
         public String toString() {
             return new StringBuilder(ZenRule.class.getSimpleName()).append('[')
-                    .append("enabled=").append(enabled)
+                    .append("id=").append(id)
+                    .append(",enabled=").append(String.valueOf(enabled).toUpperCase())
                     .append(",snoozing=").append(snoozing)
                     .append(",name=").append(name)
                     .append(",zenMode=").append(Global.zenModeToString(zenMode))
                     .append(",conditionId=").append(conditionId)
                     .append(",condition=").append(condition)
                     .append(",component=").append(component)
-                    .append(",id=").append(id)
                     .append(",creationTime=").append(creationTime)
                     .append(",enabler=").append(enabler)
                     .append(']').toString();
@@ -1461,7 +1497,7 @@
             final int N = lines.size();
             for (int i = 0; i < N; i++) {
                 if (i > 0) {
-                    sb.append(',');
+                    sb.append(",\n");
                 }
                 sb.append(lines.get(i));
             }
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index cd177c4..26223f7 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -109,6 +109,12 @@
      */
     public static final int SHOW_SOURCE_ACTIVITY = 1<<4;
 
+    /**
+     * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked
+     * from a physical button.
+     */
+    public static final int SHOW_SOURCE_PUSH_TO_TALK = 1 << 5;
+
     final Context mContext;
     final HandlerCaller mHandlerCaller;
 
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index c48b6d1..29f73b5 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -21,6 +21,7 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.app.Service;
 import android.app.WallpaperColors;
+import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.content.Intent;
@@ -436,8 +437,7 @@
 
         /**
          * Returns true if this engine is running in ambient mode -- that is,
-         * it is being shown in low power mode, in always on display.
-         * @hide
+         * it is being shown in low power mode, on always on display.
          */
         public boolean isInAmbientMode() {
             return mIsInAmbientMode;
@@ -563,10 +563,12 @@
          * Called when the device enters or exits ambient mode.
          *
          * @param inAmbientMode {@code true} if in ambient mode.
-         * @param animated {@code true} if you'll have te opportunity of animating your transition
-         *                 {@code false} when the screen will blank and the wallpaper should be
-         *                 set to ambient mode immediately.
-         * @hide
+         * @param animated {@code true} if you'll have the opportunity of animating your transition
+         *                 {@code false} when the wallpaper should present its ambient version
+         *                 immediately.
+         *
+         * @see #isInAmbientMode()
+         * @see WallpaperInfo#supportsAmbientMode()
          */
         public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) {
         }
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 01562b3..83f14d1 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -20,6 +20,7 @@
 import android.annotation.RawRes;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -33,7 +34,6 @@
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -487,8 +487,9 @@
          * intent. The possible values for this extra are
          * {@link TextToSpeech#SUCCESS} and {@link TextToSpeech#ERROR}.
          *
-         * @deprecated No longer in use. If client ise interested in information about what
-         * changed, is should send ACTION_CHECK_TTS_DATA intent to discover available voices.
+         * @deprecated No longer in use. If client is interested in information about what
+         * changed, it should use the ACTION_CHECK_TTS_DATA
+         * intent to discover available voices.
          */
         @Deprecated
         public static final String EXTRA_TTS_DATA_INSTALLED = "dataInstalled";
@@ -668,8 +669,10 @@
     }
 
     private final Context mContext;
+    @UnsupportedAppUsage
     private Connection mConnectingServiceConnection;
     private Connection mServiceConnection;
+    @UnsupportedAppUsage
     private OnInitListener mInitListener;
     // Written from an unspecified application thread, read from
     // a binder thread.
@@ -686,6 +689,7 @@
     private final Map<CharSequence, Uri> mUtterances;
     private final Bundle mParams = new Bundle();
     private final TtsEngines mEnginesHelper;
+    @UnsupportedAppUsage
     private volatile String mCurrentEngine = null;
 
     /**
@@ -1425,6 +1429,7 @@
      * @return the engine currently in use by this TextToSpeech instance.
      * @hide
      */
+    @UnsupportedAppUsage
     public String getCurrentEngine() {
         return mCurrentEngine;
     }
diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java
index a8c3453..a7b280b 100644
--- a/core/java/android/speech/tts/TtsEngines.java
+++ b/core/java/android/speech/tts/TtsEngines.java
@@ -30,6 +30,7 @@
 
 import static android.provider.Settings.Secure.getString;
 
+import android.annotation.UnsupportedAppUsage;
 import android.provider.Settings;
 import android.speech.tts.TextToSpeech.Engine;
 import android.speech.tts.TextToSpeech.EngineInfo;
@@ -101,6 +102,7 @@
         sNormalizeCountry = Collections.unmodifiableMap(normalizeCountry);
     }
 
+    @UnsupportedAppUsage
     public TtsEngines(Context ctx) {
         mContext = ctx;
     }
@@ -155,6 +157,7 @@
      *
      * @return A list of engine info objects. The list can be empty, but never {@code null}.
      */
+    @UnsupportedAppUsage
     public List<EngineInfo> getEngines() {
         PackageManager pm = mContext.getPackageManager();
         Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
@@ -194,6 +197,7 @@
     /**
      * @return an intent that can launch the settings activity for a given tts engine.
      */
+    @UnsupportedAppUsage
     public Intent getSettingsIntent(String engine) {
         PackageManager pm = mContext.getPackageManager();
         Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
@@ -327,6 +331,7 @@
      * @param engineName the engine to return the locale for.
      * @return the locale preference for this engine. Will be non null.
      */
+    @UnsupportedAppUsage
     public Locale getLocalePrefForEngine(String engineName) {
         return getLocalePrefForEngine(engineName,
                 getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE));
@@ -376,6 +381,7 @@
      * country codes ({@link Locale#getISO3Language()} and {@link Locale#getISO3Country()}),
      * if it fails to do so, we return null.
      */
+    @UnsupportedAppUsage
     public Locale parseLocaleString(String localeString) {
         String language = "", country = "", variant = "";
         if (!TextUtils.isEmpty(localeString)) {
@@ -436,6 +442,7 @@
      * This method tries to convert three-letter language and country codes into their two-letter
      * equivalents. If it fails to do so, it keeps the value from the TTS locale.
      */
+    @UnsupportedAppUsage
     public static Locale normalizeTTSLocale(Locale ttsLocale) {
         String language = ttsLocale.getLanguage();
         if (!TextUtils.isEmpty(language)) {
@@ -514,6 +521,7 @@
      * the passed locale is null, an empty string will be serialized; that empty string, when
      * read back, will evaluate to {@link Locale#getDefault()}.
      */
+    @UnsupportedAppUsage
     public synchronized void updateLocalePrefForEngine(String engineName, Locale newLocale) {
         final String prefList = Settings.Secure.getString(mContext.getContentResolver(),
                 Settings.Secure.TTS_DEFAULT_LOCALE);
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 2367d63..c6c1e8a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1594,7 +1594,7 @@
         }
 
         float get(final int offset) {
-            if (mHorizontals == null) {
+            if (mHorizontals == null || offset < 0 || offset >= mHorizontals.length) {
                 return getHorizontal(offset, mPrimary);
             } else {
                 return mHorizontals[offset - mLineStartOffset];
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index c2c3182..9bf8cd2 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -30,10 +30,6 @@
 import android.text.style.ReplacementSpan;
 import android.util.Pools.SynchronizedPool;
 
-import dalvik.annotation.optimization.CriticalNative;
-
-import libcore.util.NativeAllocationRegistry;
-
 import java.util.Arrays;
 
 /**
@@ -62,9 +58,6 @@
 public class MeasuredParagraph {
     private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
 
-    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
-            MeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);
-
     private MeasuredParagraph() {}  // Use build static functions instead.
 
     private static final SynchronizedPool<MeasuredParagraph> sPool = new SynchronizedPool<>(1);
@@ -128,24 +121,7 @@
     private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
 
     // The native MeasuredParagraph.
-    // See getNativePtr comments.
-    // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead.
-    private /* Maybe Zero */ long mNativePtr = 0;
-    private @Nullable Runnable mNativeObjectCleaner;
-
-    // Associate the native object to this Java object.
-    private void bindNativeObject(/* Non Zero*/ long nativePtr) {
-        mNativePtr = nativePtr;
-        mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr);
-    }
-
-    // Decouple the native object from this Java object and release the native object.
-    private void unbindNativeObject() {
-        if (mNativePtr != 0) {
-            mNativeObjectCleaner.run();
-            mNativePtr = 0;
-        }
-    }
+    private @Nullable NativeMeasuredParagraph mNativeMeasuredParagraph;
 
     // Following two objects are for avoiding object allocation.
     private @NonNull TextPaint mCachedPaint = new TextPaint();
@@ -173,7 +149,7 @@
         mWidths.clear();
         mFontMetrics.clear();
         mSpanEndCache.clear();
-        unbindNativeObject();
+        mNativeMeasuredParagraph = null;
     }
 
     /**
@@ -267,10 +243,10 @@
      * Returns the native ptr of the MeasuredParagraph.
      *
      * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
-     * Returns 0 in other cases.
+     * Returns null in other cases.
      */
-    public /* Maybe Zero */ long getNativePtr() {
-        return mNativePtr;
+    public NativeMeasuredParagraph getNativeMeasuredParagraph() {
+        return mNativeMeasuredParagraph;
     }
 
     /**
@@ -283,7 +259,7 @@
      * @param end the exclusive end offset of the target region in the text
      */
     public float getWidth(int start, int end) {
-        if (mNativePtr == 0) {
+        if (mNativeMeasuredParagraph == null) {
             // We have result in Java.
             final float[] widths = mWidths.getRawArray();
             float r = 0.0f;
@@ -293,7 +269,7 @@
             return r;
         } else {
             // We have result in native.
-            return nGetWidth(mNativePtr, start, end);
+            return mNativeMeasuredParagraph.getWidth(start, end);
         }
     }
 
@@ -305,7 +281,16 @@
      */
     public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
             @NonNull Rect bounds) {
-        nGetBounds(mNativePtr, mCopiedBuffer, start, end, bounds);
+        mNativeMeasuredParagraph.getBounds(mCopiedBuffer, start, end, bounds);
+    }
+
+    /**
+     * Returns a width of the character at the offset.
+     *
+     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
+     */
+    public float getCharWidthAt(@IntRange(from = 0) int offset) {
+        return mNativeMeasuredParagraph.getCharWidthAt(offset);
     }
 
     /**
@@ -364,7 +349,7 @@
         if (mt.mSpanned == null) {
             // No style change by MetricsAffectingSpan. Just measure all text.
             mt.applyMetricsAffectingSpan(
-                    paint, null /* spans */, start, end, 0 /* native static layout ptr */);
+                    paint, null /* spans */, start, end, null /* native builder ptr */);
         } else {
             // There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
             int spanEnd;
@@ -374,7 +359,7 @@
                         MetricAffectingSpan.class);
                 spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
                 mt.applyMetricsAffectingSpan(
-                        paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
+                        paint, spans, spanStart, spanEnd, null /* native builder ptr */);
             }
         }
         return mt;
@@ -406,25 +391,16 @@
             @Nullable MeasuredParagraph recycle) {
         final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
         mt.resetAndAnalyzeBidi(text, start, end, textDir);
+        final NativeMeasuredParagraph.Builder builder = new NativeMeasuredParagraph.Builder();
         if (mt.mTextLength == 0) {
             // Need to build empty native measured text for StaticLayout.
             // TODO: Stop creating empty measured text for empty lines.
-            long nativeBuilderPtr = nInitBuilder();
-            try {
-                mt.bindNativeObject(
-                        nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
-                              computeHyphenation, computeLayout));
-            } finally {
-                nFreeBuilder(nativeBuilderPtr);
-            }
-            return mt;
-        }
-
-        long nativeBuilderPtr = nInitBuilder();
-        try {
+            mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
+                        computeLayout);
+        } else {
             if (mt.mSpanned == null) {
                 // No style change by MetricsAffectingSpan. Just measure all text.
-                mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr);
+                mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, builder);
                 mt.mSpanEndCache.append(end);
             } else {
                 // There may be a MetricsAffectingSpan. Split into span transitions and apply
@@ -437,15 +413,12 @@
                             MetricAffectingSpan.class);
                     spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
                                                        MetricAffectingSpan.class);
-                    mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
-                                                 nativeBuilderPtr);
+                    mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd, builder);
                     mt.mSpanEndCache.append(spanEnd);
                 }
             }
-            mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
-                      computeHyphenation, computeLayout));
-        } finally {
-            nFreeBuilder(nativeBuilderPtr);
+            mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
+                    computeLayout);
         }
 
         return mt;
@@ -517,13 +490,13 @@
     private void applyReplacementRun(@NonNull ReplacementSpan replacement,
                                      @IntRange(from = 0) int start,  // inclusive, in copied buffer
                                      @IntRange(from = 0) int end,  // exclusive, in copied buffer
-                                     /* Maybe Zero */ long nativeBuilderPtr) {
+                                     @Nullable NativeMeasuredParagraph.Builder builder) {
         // Use original text. Shouldn't matter.
         // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
         //       backward compatibility? or Should we initialize them for getFontMetricsInt?
         final float width = replacement.getSize(
                 mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
-        if (nativeBuilderPtr == 0) {
+        if (builder == null) {
             // Assigns all width to the first character. This is the same behavior as minikin.
             mWidths.set(start, width);
             if (end > start + 1) {
@@ -531,24 +504,22 @@
             }
             mWholeWidth += width;
         } else {
-            nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
-                               width);
+            builder.addReplacementRun(mCachedPaint, start, end, width);
         }
     }
 
     private void applyStyleRun(@IntRange(from = 0) int start,  // inclusive, in copied buffer
                                @IntRange(from = 0) int end,  // exclusive, in copied buffer
-                               /* Maybe Zero */ long nativeBuilderPtr) {
+                               @Nullable NativeMeasuredParagraph.Builder builder) {
 
         if (mLtrWithoutBidi) {
             // If the whole text is LTR direction, just apply whole region.
-            if (nativeBuilderPtr == 0) {
+            if (builder == null) {
                 mWholeWidth += mCachedPaint.getTextRunAdvances(
                         mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
                         mWidths.getRawArray(), start);
             } else {
-                nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
-                        false /* isRtl */);
+                builder.addStyleRun(mCachedPaint, start, end, false /* isRtl */);
             }
         } else {
             // If there is multiple bidi levels, split into individual bidi level and apply style.
@@ -558,14 +529,13 @@
             for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
                 if (levelEnd == end || mLevels.get(levelEnd) != level) {  // transition point
                     final boolean isRtl = (level & 0x1) != 0;
-                    if (nativeBuilderPtr == 0) {
+                    if (builder == null) {
                         final int levelLength = levelEnd - levelStart;
                         mWholeWidth += mCachedPaint.getTextRunAdvances(
                                 mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
                                 isRtl, mWidths.getRawArray(), levelStart);
                     } else {
-                        nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart,
-                                levelEnd, isRtl);
+                        builder.addStyleRun(mCachedPaint, levelStart, levelEnd, isRtl);
                     }
                     if (levelEnd == end) {
                         break;
@@ -582,12 +552,12 @@
             @Nullable MetricAffectingSpan[] spans,
             @IntRange(from = 0) int start,  // inclusive, in original text buffer
             @IntRange(from = 0) int end,  // exclusive, in original text buffer
-            /* Maybe Zero */ long nativeBuilderPtr) {
+            @Nullable NativeMeasuredParagraph.Builder builder) {
         mCachedPaint.set(paint);
         // XXX paint should not have a baseline shift, but...
         mCachedPaint.baselineShift = 0;
 
-        final boolean needFontMetrics = nativeBuilderPtr != 0;
+        final boolean needFontMetrics = builder != null;
 
         if (needFontMetrics && mCachedFm == null) {
             mCachedFm = new Paint.FontMetricsInt();
@@ -610,15 +580,14 @@
         final int startInCopiedBuffer = start - mTextStart;
         final int endInCopiedBuffer = end - mTextStart;
 
-        if (nativeBuilderPtr != 0) {
+        if (builder != null) {
             mCachedPaint.getFontMetricsInt(mCachedFm);
         }
 
         if (replacement != null) {
-            applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
-                                nativeBuilderPtr);
+            applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, builder);
         } else {
-            applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr);
+            applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, builder);
         }
 
         if (needFontMetrics) {
@@ -689,59 +658,6 @@
      * This only works if the MeasuredParagraph is computed with buildForStaticLayout.
      */
     public @IntRange(from = 0) int getMemoryUsage() {
-        return nGetMemoryUsage(mNativePtr);
+        return mNativeMeasuredParagraph.getMemoryUsage();
     }
-
-    private static native /* Non Zero */ long nInitBuilder();
-
-    /**
-     * Apply style to make native measured text.
-     *
-     * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
-     * @param paintPtr The native paint pointer to be applied.
-     * @param start The start offset in the copied buffer.
-     * @param end The end offset in the copied buffer.
-     * @param isRtl True if the text is RTL.
-     */
-    private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
-                                            /* Non Zero */ long paintPtr,
-                                            @IntRange(from = 0) int start,
-                                            @IntRange(from = 0) int end,
-                                            boolean isRtl);
-
-    /**
-     * Apply ReplacementRun to make native measured text.
-     *
-     * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
-     * @param paintPtr The native paint pointer to be applied.
-     * @param start The start offset in the copied buffer.
-     * @param end The end offset in the copied buffer.
-     * @param width The width of the replacement.
-     */
-    private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
-                                                  /* Non Zero */ long paintPtr,
-                                                  @IntRange(from = 0) int start,
-                                                  @IntRange(from = 0) int end,
-                                                  @FloatRange(from = 0) float width);
-
-    private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
-                                                 @NonNull char[] text,
-                                                 boolean computeHyphenation,
-                                                 boolean computeLayout);
-
-    private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
-
-    @CriticalNative
-    private static native float nGetWidth(/* Non Zero */ long nativePtr,
-                                         @IntRange(from = 0) int start,
-                                         @IntRange(from = 0) int end);
-
-    @CriticalNative
-    private static native /* Non Zero */ long nGetReleaseFunc();
-
-    @CriticalNative
-    private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
-
-    private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
-            Rect rect);
 }
diff --git a/core/java/android/text/NativeLineBreaker.java b/core/java/android/text/NativeLineBreaker.java
new file mode 100644
index 0000000..a31b336
--- /dev/null
+++ b/core/java/android/text/NativeLineBreaker.java
@@ -0,0 +1,155 @@
+/*
+ * 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.text;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * A native implementation of the line breaker.
+ * TODO: Consider to make this class public.
+ * @hide
+ */
+public class NativeLineBreaker {
+
+    /**
+     * A result object of a line breaking
+     */
+    public static class LineBreaks {
+        public int breakCount;
+        private static final int INITIAL_SIZE = 16;
+        public int[] breaks = new int[INITIAL_SIZE];
+        public float[] widths = new float[INITIAL_SIZE];
+        public float[] ascents = new float[INITIAL_SIZE];
+        public float[] descents = new float[INITIAL_SIZE];
+        public int[] flags = new int[INITIAL_SIZE];
+        // breaks, widths, and flags should all have the same length
+    }
+
+    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+            NativeLineBreaker.class.getClassLoader(), nGetReleaseFunc(), 64);
+
+    private final long mNativePtr;
+
+    /**
+     * A constructor of NativeLineBreaker
+     */
+    public NativeLineBreaker(@Layout.BreakStrategy int breakStrategy,
+            @Layout.HyphenationFrequency int hyphenationFrequency,
+            boolean justify, @Nullable int[] indents) {
+        mNativePtr = nInit(breakStrategy, hyphenationFrequency, justify, indents);
+        sRegistry.registerNativeAllocation(this, mNativePtr);
+    }
+
+    /**
+     * Break text into lines
+     *
+     * @param chars an array of characters
+     * @param measuredPara a result of the text measurement
+     * @param length a length of the target text from the begining
+     * @param firstWidth a width of the first width of the line in this paragraph
+     * @param firstWidthLineCount a number of lines that has the length of the firstWidth
+     * @param restWidth a width of the rest of the lines.
+     * @param variableTabStops an array of tab stop widths
+     * @param defaultTabStop a width of the tab stop
+     * @param indentsOffset an offset of the indents to be used.
+     * @param out output buffer
+     * @return a number of the lines
+     */
+    @NonNull public int computeLineBreaks(
+            @NonNull char[] chars,
+            @NonNull NativeMeasuredParagraph measuredPara,
+            @IntRange(from = 0) int length,
+            @FloatRange(from = 0.0f) float firstWidth,
+            @IntRange(from = 0) int firstWidthLineCount,
+            @FloatRange(from = 0.0f) float restWidth,
+            @Nullable int[] variableTabStops,
+            int defaultTabStop,
+            @IntRange(from = 0) int indentsOffset,
+            @NonNull LineBreaks out) {
+        return nComputeLineBreaks(
+                mNativePtr,
+
+                // Inputs
+                chars,
+                measuredPara.getNativePtr(),
+                length,
+                firstWidth,
+                firstWidthLineCount,
+                restWidth,
+                variableTabStops,
+                defaultTabStop,
+                indentsOffset,
+
+                // Outputs
+                out,
+                out.breaks.length,
+                out.breaks,
+                out.widths,
+                out.ascents,
+                out.descents,
+                out.flags);
+
+    }
+
+    @FastNative
+    private static native long nInit(
+            @Layout.BreakStrategy int breakStrategy,
+            @Layout.HyphenationFrequency int hyphenationFrequency,
+            boolean isJustified,
+            @Nullable int[] indents);
+
+    @CriticalNative
+    private static native long nGetReleaseFunc();
+
+    // populates LineBreaks and returns the number of breaks found
+    //
+    // the arrays inside the LineBreaks objects are passed in as well
+    // to reduce the number of JNI calls in the common case where the
+    // arrays do not have to be resized
+    // The individual character widths will be returned in charWidths. The length of
+    // charWidths must be at least the length of the text.
+    private static native int nComputeLineBreaks(
+            /* non zero */ long nativePtr,
+
+            // Inputs
+            @NonNull char[] text,
+            /* Non Zero */ long measuredTextPtr,
+            @IntRange(from = 0) int length,
+            @FloatRange(from = 0.0f) float firstWidth,
+            @IntRange(from = 0) int firstWidthLineCount,
+            @FloatRange(from = 0.0f) float restWidth,
+            @Nullable int[] variableTabStops,
+            int defaultTabStop,
+            @IntRange(from = 0) int indentsOffset,
+
+            // Outputs
+            @NonNull LineBreaks recycle,
+            @IntRange(from  = 0) int recycleLength,
+            @NonNull int[] recycleBreaks,
+            @NonNull float[] recycleWidths,
+            @NonNull float[] recycleAscents,
+            @NonNull float[] recycleDescents,
+            @NonNull int[] recycleFlags);
+}
diff --git a/core/java/android/text/NativeMeasuredParagraph.java b/core/java/android/text/NativeMeasuredParagraph.java
new file mode 100644
index 0000000..d03674f
--- /dev/null
+++ b/core/java/android/text/NativeMeasuredParagraph.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * A native implementation of measured paragraph.
+ * TODO: Consider to make this class public.
+ * @hide
+ */
+public class NativeMeasuredParagraph {
+    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+            NativeMeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);
+
+    private long mNativePtr;
+
+    // Use builder instead.
+    private NativeMeasuredParagraph(long ptr) {
+        mNativePtr = ptr;
+    }
+
+    /**
+     * Returns a width of the given region
+     */
+    public float getWidth(int start, int end) {
+        return nGetWidth(mNativePtr, start, end);
+    }
+
+    /**
+     * Returns a memory usage of the native object.
+     */
+    public int getMemoryUsage() {
+        return nGetMemoryUsage(mNativePtr);
+    }
+
+    /**
+     * Fills the boundary box of the given region
+     */
+    public void getBounds(char[] buf, int start, int end, Rect rect) {
+        nGetBounds(mNativePtr, buf, start, end, rect);
+    }
+
+    /**
+     * Returns the width of the character at the given offset
+     */
+    public float getCharWidthAt(int offset) {
+        return nGetCharWidthAt(mNativePtr, offset);
+    }
+
+    /**
+     * Returns a native pointer of the underlying native object.
+     */
+    public long getNativePtr() {
+        return mNativePtr;
+    }
+
+    @CriticalNative
+    private static native float nGetWidth(/* Non Zero */ long nativePtr,
+                                         @IntRange(from = 0) int start,
+                                         @IntRange(from = 0) int end);
+
+    @CriticalNative
+    private static native /* Non Zero */ long nGetReleaseFunc();
+
+    @CriticalNative
+    private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
+
+    private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
+            Rect rect);
+
+    @CriticalNative
+    private static native float nGetCharWidthAt(long nativePtr, int offset);
+
+    /**
+     * A builder for the NativeMeasuredParagraph
+     */
+    public static class Builder {
+        private final long mNativePtr;
+
+        public Builder() {
+            mNativePtr = nInitBuilder();
+        }
+
+        /**
+         * Apply styles to given range
+         */
+        public void addStyleRun(@NonNull Paint paint, int start, int end, boolean isRtl) {
+            nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl);
+        }
+
+        /**
+         * Tells native that the given range is replaced with the object of given width.
+         */
+        public void addReplacementRun(@NonNull Paint paint, int start, int end, float width) {
+            nAddReplacementRun(mNativePtr, paint.getNativeInstance(), start, end, width);
+        }
+
+        /**
+         * Build the NativeMeasuredParagraph
+         */
+        public NativeMeasuredParagraph build(char[] text, boolean computeHyphenation,
+                boolean computeLayout) {
+            try {
+                long ptr = nBuildNativeMeasuredParagraph(mNativePtr, text, computeHyphenation,
+                        computeLayout);
+                NativeMeasuredParagraph res = new NativeMeasuredParagraph(ptr);
+                sRegistry.registerNativeAllocation(res, ptr);
+                return res;
+            } finally {
+                nFreeBuilder(mNativePtr);
+            }
+        }
+
+        private static native /* Non Zero */ long nInitBuilder();
+
+        /**
+         * Apply style to make native measured text.
+         *
+         * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+         * @param paintPtr The native paint pointer to be applied.
+         * @param start The start offset in the copied buffer.
+         * @param end The end offset in the copied buffer.
+         * @param isRtl True if the text is RTL.
+         */
+        private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
+                                                /* Non Zero */ long paintPtr,
+                                                @IntRange(from = 0) int start,
+                                                @IntRange(from = 0) int end,
+                                                boolean isRtl);
+        /**
+         * Apply ReplacementRun to make native measured text.
+         *
+         * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+         * @param paintPtr The native paint pointer to be applied.
+         * @param start The start offset in the copied buffer.
+         * @param end The end offset in the copied buffer.
+         * @param width The width of the replacement.
+         */
+        private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
+                                                      /* Non Zero */ long paintPtr,
+                                                      @IntRange(from = 0) int start,
+                                                      @IntRange(from = 0) int end,
+                                                      @FloatRange(from = 0) float width);
+
+        private static native long nBuildNativeMeasuredParagraph(
+                /* Non Zero */ long nativeBuilderPtr,
+                @NonNull char[] text,
+                boolean computeHyphenation,
+                boolean computeLayout);
+
+        private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
+    }
+}
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 369f357..b7ea012 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -437,7 +437,6 @@
     public boolean canUseMeasuredResult(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
             @NonNull TextDirectionHeuristic textDir, @NonNull TextPaint paint,
             @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
-        final TextPaint mtPaint = mParams.getTextPaint();
         return mStart == start
             && mEnd == end
             && mParams.isSameTextMetricsInternal(paint, textDir, strategy, frequency);
@@ -519,6 +518,21 @@
     }
 
     /**
+     * Returns a width of a character at offset
+     *
+     * @param offset an offset of the text.
+     * @return a width of the character.
+     * @hide
+     */
+    public float getCharWidthAt(@IntRange(from = 0) int offset) {
+        Preconditions.checkArgument(0 <= offset && offset < mText.length(), "invalid offset");
+        final int paraIndex = findParaIndex(offset);
+        final int paraStart = getParagraphStart(paraIndex);
+        final int paraEnd = getParagraphEnd(paraIndex);
+        return getMeasuredParagraph(paraIndex).getCharWidthAt(offset - paraStart);
+    }
+
+    /**
      * Returns the size of native PrecomputedText memory usage.
      *
      * Note that this is not guaranteed to be accurate. Must be used only for testing purposes.
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 4b78aa2..cc2869f 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -21,7 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Paint;
-import android.text.AutoGrowArray.FloatArray;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
 import android.text.style.LineHeightSpan;
@@ -32,9 +31,6 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
 
-import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
-
 import java.util.Arrays;
 
 /**
@@ -57,7 +53,7 @@
      *
      *   - Create MeasuredParagraph by MeasuredParagraph.buildForStaticLayout which measures in
      *     native.
-     *   - Run nComputeLineBreaks() to obtain line breaks for the paragraph.
+     *   - Run NativeLineBreaker.computeLineBreaks() to obtain line breaks for the paragraph.
      *
      * After all paragraphs, call finish() to release expensive buffers.
      */
@@ -317,10 +313,17 @@
         /**
          * Set break strategy, useful for selecting high quality or balanced paragraph
          * layout options. The default is {@link Layout#BREAK_STRATEGY_SIMPLE}.
+         * <p/>
+         * Enabling hyphenation with either using {@link Layout#HYPHENATION_FREQUENCY_NORMAL} or
+         * {@link Layout#HYPHENATION_FREQUENCY_FULL} while line breaking is set to one of
+         * {@link Layout#BREAK_STRATEGY_BALANCED}, {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}
+         * improves the structure of text layout however has performance impact and requires more
+         * time to do the text layout.
          *
          * @param breakStrategy break strategy for paragraph layout
          * @return this builder, useful for chaining
          * @see android.widget.TextView#setBreakStrategy
+         * @see #setHyphenationFrequency(int)
          */
         @NonNull
         public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
@@ -333,10 +336,17 @@
          * possible values are defined in {@link Layout}, by constants named with the pattern
          * {@code HYPHENATION_FREQUENCY_*}. The default is
          * {@link Layout#HYPHENATION_FREQUENCY_NONE}.
+         * <p/>
+         * Enabling hyphenation with either using {@link Layout#HYPHENATION_FREQUENCY_NORMAL} or
+         * {@link Layout#HYPHENATION_FREQUENCY_FULL} while line breaking is set to one of
+         * {@link Layout#BREAK_STRATEGY_BALANCED}, {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}
+         * improves the structure of text layout however has performance impact and requires more
+         * time to do the text layout.
          *
          * @param hyphenationFrequency hyphenation frequency for the paragraph
          * @return this builder, useful for chaining
          * @see android.widget.TextView#setHyphenationFrequency
+         * @see #setBreakStrategy(int)
          */
         @NonNull
         public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) {
@@ -586,8 +596,7 @@
         float ellipsizedWidth = b.mEllipsizedWidth;
         TextUtils.TruncateAt ellipsize = b.mEllipsize;
         final boolean addLastLineSpacing = b.mAddLastLineLineSpacing;
-        LineBreaks lineBreaks = new LineBreaks();  // TODO: move to builder to avoid allocation costs
-        FloatArray widths = new FloatArray();
+        NativeLineBreaker.LineBreaks lineBreaks = new NativeLineBreaker.LineBreaks();
 
         mLineCount = 0;
         mEllipsized = false;
@@ -615,7 +624,7 @@
             indents = null;
         }
 
-        final long nativePtr = nInit(
+        final NativeLineBreaker lineBreaker = new NativeLineBreaker(
                 b.mBreakStrategy, b.mHyphenationFrequency,
                 // TODO: Support more justification mode, e.g. letter spacing, stretching.
                 b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
@@ -639,243 +648,219 @@
                     bufEnd, false /* computeLayout */);
         }
 
-        try {
-            for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) {
-                final int paraStart = paraIndex == 0
-                        ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd;
-                final int paraEnd = paragraphInfo[paraIndex].paragraphEnd;
+        for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) {
+            final int paraStart = paraIndex == 0
+                    ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd;
+            final int paraEnd = paragraphInfo[paraIndex].paragraphEnd;
 
-                int firstWidthLineCount = 1;
-                int firstWidth = outerWidth;
-                int restWidth = outerWidth;
+            int firstWidthLineCount = 1;
+            int firstWidth = outerWidth;
+            int restWidth = outerWidth;
 
-                LineHeightSpan[] chooseHt = null;
+            LineHeightSpan[] chooseHt = null;
+            if (spanned != null) {
+                LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
+                        LeadingMarginSpan.class);
+                for (int i = 0; i < sp.length; i++) {
+                    LeadingMarginSpan lms = sp[i];
+                    firstWidth -= sp[i].getLeadingMargin(true);
+                    restWidth -= sp[i].getLeadingMargin(false);
 
-                if (spanned != null) {
-                    LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
-                            LeadingMarginSpan.class);
-                    for (int i = 0; i < sp.length; i++) {
-                        LeadingMarginSpan lms = sp[i];
-                        firstWidth -= sp[i].getLeadingMargin(true);
-                        restWidth -= sp[i].getLeadingMargin(false);
+                    // LeadingMarginSpan2 is odd.  The count affects all
+                    // leading margin spans, not just this particular one
+                    if (lms instanceof LeadingMarginSpan2) {
+                        LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
+                        firstWidthLineCount = Math.max(firstWidthLineCount,
+                                lms2.getLeadingMarginLineCount());
+                    }
+                }
 
-                        // LeadingMarginSpan2 is odd.  The count affects all
-                        // leading margin spans, not just this particular one
-                        if (lms instanceof LeadingMarginSpan2) {
-                            LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
-                            firstWidthLineCount = Math.max(firstWidthLineCount,
-                                    lms2.getLeadingMarginLineCount());
-                        }
+                chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
+
+                if (chooseHt.length == 0) {
+                    chooseHt = null; // So that out() would not assume it has any contents
+                } else {
+                    if (chooseHtv == null || chooseHtv.length < chooseHt.length) {
+                        chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
                     }
 
-                    chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
+                    for (int i = 0; i < chooseHt.length; i++) {
+                        int o = spanned.getSpanStart(chooseHt[i]);
 
-                    if (chooseHt.length == 0) {
-                        chooseHt = null; // So that out() would not assume it has any contents
+                        if (o < paraStart) {
+                            // starts in this layout, before the
+                            // current paragraph
+
+                            chooseHtv[i] = getLineTop(getLineForOffset(o));
+                        } else {
+                            // starts in this paragraph
+
+                            chooseHtv[i] = v;
+                        }
+                    }
+                }
+            }
+            // tab stop locations
+            int[] variableTabStops = null;
+            if (spanned != null) {
+                TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
+                        paraEnd, TabStopSpan.class);
+                if (spans.length > 0) {
+                    int[] stops = new int[spans.length];
+                    for (int i = 0; i < spans.length; i++) {
+                        stops[i] = spans[i].getTabStop();
+                    }
+                    Arrays.sort(stops, 0, stops.length);
+                    variableTabStops = stops;
+                }
+            }
+
+            final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured;
+            final char[] chs = measuredPara.getChars();
+            final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
+            final int[] fmCache = measuredPara.getFontMetrics().getRawArray();
+            int breakCount = lineBreaker.computeLineBreaks(
+                    measuredPara.getChars(),
+                    measuredPara.getNativeMeasuredParagraph(),
+                    paraEnd - paraStart,
+                    firstWidth,
+                    firstWidthLineCount,
+                    restWidth,
+                    variableTabStops,
+                    TAB_INCREMENT,
+                    mLineCount,
+                    lineBreaks);
+
+            final int[] breaks = lineBreaks.breaks;
+            final float[] lineWidths = lineBreaks.widths;
+            final float[] ascents = lineBreaks.ascents;
+            final float[] descents = lineBreaks.descents;
+            final int[] flags = lineBreaks.flags;
+
+            final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
+            final boolean ellipsisMayBeApplied = ellipsize != null
+                    && (ellipsize == TextUtils.TruncateAt.END
+                        || (mMaximumVisibleLineCount == 1
+                                && ellipsize != TextUtils.TruncateAt.MARQUEE));
+            if (0 < remainingLineCount && remainingLineCount < breakCount
+                    && ellipsisMayBeApplied) {
+                // Calculate width
+                float width = 0;
+                int flag = 0;  // XXX May need to also have starting hyphen edit
+                for (int i = remainingLineCount - 1; i < breakCount; i++) {
+                    if (i == breakCount - 1) {
+                        width += lineWidths[i];
                     } else {
-                        if (chooseHtv == null || chooseHtv.length < chooseHt.length) {
-                            chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
-                        }
-
-                        for (int i = 0; i < chooseHt.length; i++) {
-                            int o = spanned.getSpanStart(chooseHt[i]);
-
-                            if (o < paraStart) {
-                                // starts in this layout, before the
-                                // current paragraph
-
-                                chooseHtv[i] = getLineTop(getLineForOffset(o));
-                            } else {
-                                // starts in this paragraph
-
-                                chooseHtv[i] = v;
-                            }
+                        for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
+                            width += measuredPara.getCharWidthAt(j - paraStart);
                         }
                     }
+                    flag |= flags[i] & TAB_MASK;
+                }
+                // Treat the last line and overflowed lines as a single line.
+                breaks[remainingLineCount - 1] = breaks[breakCount - 1];
+                lineWidths[remainingLineCount - 1] = width;
+                flags[remainingLineCount - 1] = flag;
+
+                breakCount = remainingLineCount;
+            }
+
+            // here is the offset of the starting character of the line we are currently
+            // measuring
+            int here = paraStart;
+
+            int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
+            int fmCacheIndex = 0;
+            int spanEndCacheIndex = 0;
+            int breakIndex = 0;
+            for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
+                // retrieve end of span
+                spanEnd = spanEndCache[spanEndCacheIndex++];
+
+                // retrieve cached metrics, order matches above
+                fm.top = fmCache[fmCacheIndex * 4 + 0];
+                fm.bottom = fmCache[fmCacheIndex * 4 + 1];
+                fm.ascent = fmCache[fmCacheIndex * 4 + 2];
+                fm.descent = fmCache[fmCacheIndex * 4 + 3];
+                fmCacheIndex++;
+
+                if (fm.top < fmTop) {
+                    fmTop = fm.top;
+                }
+                if (fm.ascent < fmAscent) {
+                    fmAscent = fm.ascent;
+                }
+                if (fm.descent > fmDescent) {
+                    fmDescent = fm.descent;
+                }
+                if (fm.bottom > fmBottom) {
+                    fmBottom = fm.bottom;
                 }
 
-                // tab stop locations
-                int[] variableTabStops = null;
-                if (spanned != null) {
-                    TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
-                            paraEnd, TabStopSpan.class);
-                    if (spans.length > 0) {
-                        int[] stops = new int[spans.length];
-                        for (int i = 0; i < spans.length; i++) {
-                            stops[i] = spans[i].getTabStop();
-                        }
-                        Arrays.sort(stops, 0, stops.length);
-                        variableTabStops = stops;
-                    }
+                // skip breaks ending before current span range
+                while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
+                    breakIndex++;
                 }
 
-                final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured;
-                final char[] chs = measuredPara.getChars();
-                final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
-                final int[] fmCache = measuredPara.getFontMetrics().getRawArray();
-                // TODO: Stop keeping duplicated width copy in native and Java.
-                widths.resize(chs.length);
+                while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
+                    int endPos = paraStart + breaks[breakIndex];
 
-                // measurement has to be done before performing line breaking
-                // but we don't want to recompute fontmetrics or span ranges the
-                // second time, so we cache those and then use those stored values
+                    boolean moreChars = (endPos < bufEnd);
 
-                int breakCount = nComputeLineBreaks(
-                        nativePtr,
+                    final int ascent = fallbackLineSpacing
+                            ? Math.min(fmAscent, Math.round(ascents[breakIndex]))
+                            : fmAscent;
+                    final int descent = fallbackLineSpacing
+                            ? Math.max(fmDescent, Math.round(descents[breakIndex]))
+                            : fmDescent;
 
-                        // Inputs
-                        chs,
-                        measuredPara.getNativePtr(),
-                        paraEnd - paraStart,
-                        firstWidth,
-                        firstWidthLineCount,
-                        restWidth,
-                        variableTabStops,
-                        TAB_INCREMENT,
-                        mLineCount,
+                    v = out(source, here, endPos,
+                            ascent, descent, fmTop, fmBottom,
+                            v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
+                            flags[breakIndex], needMultiply, measuredPara, bufEnd,
+                            includepad, trackpad, addLastLineSpacing, chs,
+                            paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
+                            paint, moreChars);
 
-                        // Outputs
-                        lineBreaks,
-                        lineBreaks.breaks.length,
-                        lineBreaks.breaks,
-                        lineBreaks.widths,
-                        lineBreaks.ascents,
-                        lineBreaks.descents,
-                        lineBreaks.flags,
-                        widths.getRawArray());
-
-                final int[] breaks = lineBreaks.breaks;
-                final float[] lineWidths = lineBreaks.widths;
-                final float[] ascents = lineBreaks.ascents;
-                final float[] descents = lineBreaks.descents;
-                final int[] flags = lineBreaks.flags;
-
-                final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
-                final boolean ellipsisMayBeApplied = ellipsize != null
-                        && (ellipsize == TextUtils.TruncateAt.END
-                            || (mMaximumVisibleLineCount == 1
-                                    && ellipsize != TextUtils.TruncateAt.MARQUEE));
-                if (0 < remainingLineCount && remainingLineCount < breakCount
-                        && ellipsisMayBeApplied) {
-                    // Calculate width and flag.
-                    float width = 0;
-                    int flag = 0; // XXX May need to also have starting hyphen edit
-                    for (int i = remainingLineCount - 1; i < breakCount; i++) {
-                        if (i == breakCount - 1) {
-                            width += lineWidths[i];
-                        } else {
-                            for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
-                                width += widths.get(j);
-                            }
-                        }
-                        flag |= flags[i] & TAB_MASK;
-                    }
-                    // Treat the last line and overflowed lines as a single line.
-                    breaks[remainingLineCount - 1] = breaks[breakCount - 1];
-                    lineWidths[remainingLineCount - 1] = width;
-                    flags[remainingLineCount - 1] = flag;
-
-                    breakCount = remainingLineCount;
-                }
-
-                // here is the offset of the starting character of the line we are currently
-                // measuring
-                int here = paraStart;
-
-                int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
-                int fmCacheIndex = 0;
-                int spanEndCacheIndex = 0;
-                int breakIndex = 0;
-                for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
-                    // retrieve end of span
-                    spanEnd = spanEndCache[spanEndCacheIndex++];
-
-                    // retrieve cached metrics, order matches above
-                    fm.top = fmCache[fmCacheIndex * 4 + 0];
-                    fm.bottom = fmCache[fmCacheIndex * 4 + 1];
-                    fm.ascent = fmCache[fmCacheIndex * 4 + 2];
-                    fm.descent = fmCache[fmCacheIndex * 4 + 3];
-                    fmCacheIndex++;
-
-                    if (fm.top < fmTop) {
+                    if (endPos < spanEnd) {
+                        // preserve metrics for current span
                         fmTop = fm.top;
-                    }
-                    if (fm.ascent < fmAscent) {
-                        fmAscent = fm.ascent;
-                    }
-                    if (fm.descent > fmDescent) {
-                        fmDescent = fm.descent;
-                    }
-                    if (fm.bottom > fmBottom) {
                         fmBottom = fm.bottom;
+                        fmAscent = fm.ascent;
+                        fmDescent = fm.descent;
+                    } else {
+                        fmTop = fmBottom = fmAscent = fmDescent = 0;
                     }
 
-                    // skip breaks ending before current span range
-                    while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
-                        breakIndex++;
+                    here = endPos;
+                    breakIndex++;
+
+                    if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) {
+                        return;
                     }
-
-                    while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
-                        int endPos = paraStart + breaks[breakIndex];
-
-                        boolean moreChars = (endPos < bufEnd);
-
-                        final int ascent = fallbackLineSpacing
-                                ? Math.min(fmAscent, Math.round(ascents[breakIndex]))
-                                : fmAscent;
-                        final int descent = fallbackLineSpacing
-                                ? Math.max(fmDescent, Math.round(descents[breakIndex]))
-                                : fmDescent;
-                        v = out(source, here, endPos,
-                                ascent, descent, fmTop, fmBottom,
-                                v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
-                                flags[breakIndex], needMultiply, measuredPara, bufEnd,
-                                includepad, trackpad, addLastLineSpacing, chs, widths.getRawArray(),
-                                paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
-                                paint, moreChars);
-
-                        if (endPos < spanEnd) {
-                            // preserve metrics for current span
-                            fmTop = fm.top;
-                            fmBottom = fm.bottom;
-                            fmAscent = fm.ascent;
-                            fmDescent = fm.descent;
-                        } else {
-                            fmTop = fmBottom = fmAscent = fmDescent = 0;
-                        }
-
-                        here = endPos;
-                        breakIndex++;
-
-                        if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) {
-                            return;
-                        }
-                    }
-                }
-
-                if (paraEnd == bufEnd) {
-                    break;
                 }
             }
 
-            if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
-                    && mLineCount < mMaximumVisibleLineCount) {
-                final MeasuredParagraph measuredPara =
-                        MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null);
-                paint.getFontMetricsInt(fm);
-                v = out(source,
-                        bufEnd, bufEnd, fm.ascent, fm.descent,
-                        fm.top, fm.bottom,
-                        v,
-                        spacingmult, spacingadd, null,
-                        null, fm, 0,
-                        needMultiply, measuredPara, bufEnd,
-                        includepad, trackpad, addLastLineSpacing, null,
-                        null, bufStart, ellipsize,
-                        ellipsizedWidth, 0, paint, false);
+            if (paraEnd == bufEnd) {
+                break;
             }
-        } finally {
-            nFinish(nativePtr);
+        }
+
+        if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
+                && mLineCount < mMaximumVisibleLineCount) {
+            final MeasuredParagraph measuredPara =
+                    MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null);
+            paint.getFontMetricsInt(fm);
+            v = out(source,
+                    bufEnd, bufEnd, fm.ascent, fm.descent,
+                    fm.top, fm.bottom,
+                    v,
+                    spacingmult, spacingadd, null,
+                    null, fm, 0,
+                    needMultiply, measuredPara, bufEnd,
+                    includepad, trackpad, addLastLineSpacing, null,
+                    bufStart, ellipsize,
+                    ellipsizedWidth, 0, paint, false);
         }
     }
 
@@ -884,7 +869,7 @@
             final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm,
             final int flags, final boolean needMultiply, @NonNull final MeasuredParagraph measured,
             final int bufEnd, final boolean includePad, final boolean trackPad,
-            final boolean addLastLineLineSpacing, final char[] chs, final float[] widths,
+            final boolean addLastLineLineSpacing, final char[] chs,
             final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth,
             final float textWidth, final TextPaint paint, final boolean moreChars) {
         final int j = mLineCount;
@@ -942,7 +927,7 @@
                     (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
                             ellipsize == TextUtils.TruncateAt.END);
             if (doEllipsis) {
-                calculateEllipsis(start, end, widths, widthStart,
+                calculateEllipsis(start, end, measured, widthStart,
                         ellipsisWidth, ellipsize, j,
                         textWidth, paint, forceEllipsis);
             }
@@ -1026,7 +1011,7 @@
     }
 
     private void calculateEllipsis(int lineStart, int lineEnd,
-                                   float[] widths, int widthStart,
+                                   MeasuredParagraph measured, int widthStart,
                                    float avail, TextUtils.TruncateAt where,
                                    int line, float textWidth, TextPaint paint,
                                    boolean forceEllipsis) {
@@ -1050,9 +1035,10 @@
                 int i;
 
                 for (i = len; i > 0; i--) {
-                    float w = widths[i - 1 + lineStart - widthStart];
+                    float w = measured.getCharWidthAt(i - 1 + lineStart - widthStart);
                     if (w + sum + ellipsisWidth > avail) {
-                        while (i < len && widths[i + lineStart - widthStart] == 0.0f) {
+                        while (i < len
+                                && measured.getCharWidthAt(i + lineStart - widthStart) == 0.0f) {
                             i++;
                         }
                         break;
@@ -1074,7 +1060,7 @@
             int i;
 
             for (i = 0; i < len; i++) {
-                float w = widths[i + lineStart - widthStart];
+                float w = measured.getCharWidthAt(i + lineStart - widthStart);
 
                 if (w + sum + ellipsisWidth > avail) {
                     break;
@@ -1097,10 +1083,12 @@
 
                 float ravail = (avail - ellipsisWidth) / 2;
                 for (right = len; right > 0; right--) {
-                    float w = widths[right - 1 + lineStart - widthStart];
+                    float w = measured.getCharWidthAt(right - 1 + lineStart - widthStart);
 
                     if (w + rsum > ravail) {
-                        while (right < len && widths[right + lineStart - widthStart] == 0.0f) {
+                        while (right < len
+                                && measured.getCharWidthAt(right + lineStart - widthStart)
+                                    == 0.0f) {
                             right++;
                         }
                         break;
@@ -1110,7 +1098,7 @@
 
                 float lavail = avail - ellipsisWidth - rsum;
                 for (left = 0; left < right; left++) {
-                    float w = widths[left + lineStart - widthStart];
+                    float w = measured.getCharWidthAt(left + lineStart - widthStart);
 
                     if (w + lsum > lavail) {
                         break;
@@ -1306,47 +1294,6 @@
                 ? mMaxLineHeight : super.getHeight();
     }
 
-    @FastNative
-    private static native long nInit(
-            @BreakStrategy int breakStrategy,
-            @HyphenationFrequency int hyphenationFrequency,
-            boolean isJustified,
-            @Nullable int[] indents);
-
-    @CriticalNative
-    private static native void nFinish(long nativePtr);
-
-    // populates LineBreaks and returns the number of breaks found
-    //
-    // the arrays inside the LineBreaks objects are passed in as well
-    // to reduce the number of JNI calls in the common case where the
-    // arrays do not have to be resized
-    // The individual character widths will be returned in charWidths. The length of charWidths must
-    // be at least the length of the text.
-    private static native int nComputeLineBreaks(
-            /* non zero */ long nativePtr,
-
-            // Inputs
-            @NonNull char[] text,
-            /* Non Zero */ long measuredTextPtr,
-            @IntRange(from = 0) int length,
-            @FloatRange(from = 0.0f) float firstWidth,
-            @IntRange(from = 0) int firstWidthLineCount,
-            @FloatRange(from = 0.0f) float restWidth,
-            @Nullable int[] variableTabStops,
-            int defaultTabStop,
-            @IntRange(from = 0) int indentsOffset,
-
-            // Outputs
-            @NonNull LineBreaks recycle,
-            @IntRange(from  = 0) int recycleLength,
-            @NonNull int[] recycleBreaks,
-            @NonNull float[] recycleWidths,
-            @NonNull float[] recycleAscents,
-            @NonNull float[] recycleDescents,
-            @NonNull int[] recycleFlags,
-            @NonNull float[] charWidths);
-
     private int mLineCount;
     private int mTopPadding, mBottomPadding;
     private int mColumns;
@@ -1396,8 +1343,7 @@
 
     private static final int DEFAULT_MAX_LINE_HEIGHT = -1;
 
-    // This is used to return three arrays from a single JNI call when
-    // performing line breaking
+    // Unused, here because of gray list private API accesses.
     /*package*/ static class LineBreaks {
         private static final int INITIAL_SIZE = 16;
         public int[] breaks = new int[INITIAL_SIZE];
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index a0daa07..dde4c1d 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -351,8 +351,16 @@
     }
 
     /**
-     * String.split() returns [''] when the string to be split is empty. This returns []. This does
-     * not remove any empty strings from the result. For example split("a,", ","  ) returns {"a", ""}.
+     *
+     * This method yields the same result as {@code text.split(expression, -1)} except that if
+     * {@code text.isEmpty()} then this method returns an empty array whereas
+     * {@code "".split(expression, -1)} would have returned an array with a single {@code ""}.
+     *
+     * The {@code -1} means that trailing empty Strings are not removed from the result; for
+     * example split("a,", ","  ) returns {"a", ""}. Note that whether a leading zero-width match
+     * can result in a leading {@code ""} depends on whether your app
+     * {@link android.content.pm.ApplicationInfo#targetSdkVersion targets an SDK version}
+     * {@code <= 28}; see {@link Pattern#split(CharSequence, int)}.
      *
      * @param text the string to split
      * @param expression the regular expression to match
@@ -369,8 +377,16 @@
     }
 
     /**
-     * Splits a string on a pattern. String.split() returns [''] when the string to be
-     * split is empty. This returns []. This does not remove any empty strings from the result.
+     * Splits a string on a pattern. This method yields the same result as
+     * {@code pattern.split(text, -1)} except that if {@code text.isEmpty()} then this method
+     * returns an empty array whereas {@code pattern.split("", -1)} would have returned an array
+     * with a single {@code ""}.
+     *
+     * The {@code -1} means that trailing empty Strings are not removed from the result;
+     * Note that whether a leading zero-width match can result in a leading {@code ""} depends
+     * on whether your app {@link android.content.pm.ApplicationInfo#targetSdkVersion targets
+     * an SDK version} {@code <= 28}; see {@link Pattern#split(CharSequence, int)}.
+     *
      * @param text the string to split
      * @param pattern the regular expression to match
      * @return an array of strings. The array will be empty if text is empty
@@ -2075,6 +2091,25 @@
         return (T) text.subSequence(0, size);
     }
 
+    /**
+     * Trims the {@code text} to the first {@code size} characters and adds an ellipsis if the
+     * resulting string is shorter than the input. This will result in an output string which is
+     * longer than {@code size} for most inputs.
+     *
+     * @param size length of the result, should be greater than 0
+     *
+     * @hide
+     */
+    @Nullable
+    public static <T extends CharSequence> T trimToLengthWithEllipsis(@Nullable T text,
+            @IntRange(from = 1) int size) {
+        T trimmed = trimToSize(text, size);
+        if (trimmed.length() < text.length()) {
+            trimmed = (T) (trimmed.toString() + "...");
+        }
+        return trimmed;
+    }
+
     private static Object sLock = new Object();
 
     private static char[] sTemp = null;
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index 70175c8..c0ac70e 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -23,8 +23,6 @@
 import android.annotation.Px;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Path.Direction;
 import android.os.Parcel;
 import android.text.Layout;
 import android.text.ParcelableSpan;
@@ -73,7 +71,6 @@
     private final int mGapWidth;
     @Px
     private final int mBulletRadius;
-    private Path mBulletPath = null;
     @ColorInt
     private final int mColor;
     private final boolean mWantColor;
@@ -224,19 +221,7 @@
             final float yPosition = (top + bottom) / 2f;
             final float xPosition = x + dir * mBulletRadius;
 
-            if (canvas.isHardwareAccelerated()) {
-                if (mBulletPath == null) {
-                    mBulletPath = new Path();
-                    mBulletPath.addCircle(0.0f, 0.0f, mBulletRadius, Direction.CW);
-                }
-
-                canvas.save();
-                canvas.translate(xPosition, yPosition);
-                canvas.drawPath(mBulletPath, paint);
-                canvas.restore();
-            } else {
-                canvas.drawCircle(xPosition, yPosition, mBulletRadius, paint);
-            }
+            canvas.drawCircle(xPosition, yPosition, mBulletRadius, paint);
 
             if (mWantColor) {
                 paint.setColor(oldcolor);
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index f3d39de..08cbbe6 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -35,6 +35,7 @@
 import com.android.i18n.phonenumbers.PhoneNumberMatch;
 import com.android.i18n.phonenumbers.PhoneNumberUtil;
 import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency;
+import com.android.internal.annotations.GuardedBy;
 
 import libcore.util.EmptyArray;
 
@@ -63,6 +64,10 @@
  *  does not have a URL scheme prefix, the supplied scheme will be prepended to
  *  create <code>http://example.com</code> when the clickable URL link is
  *  created.
+ *
+ * @see MatchFilter
+ * @see TransformFilter
+ * @see UrlSpanFactory
  */
 
 public class Linkify {
@@ -218,6 +223,44 @@
     }
 
     /**
+     * Factory class to create {@link URLSpan}s. While adding spans to a {@link Spannable},
+     * {@link Linkify} will call {@link UrlSpanFactory#create(String)} function to create a
+     * {@link URLSpan}.
+     *
+     * @see #addLinks(Spannable, int, UrlSpanFactory)
+     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
+     * UrlSpanFactory)
+     */
+    public static class UrlSpanFactory {
+        private static final Object sInstanceLock = new Object();
+
+        @GuardedBy("sInstanceLock")
+        private static volatile UrlSpanFactory sInstance = null;
+
+        private static synchronized UrlSpanFactory getInstance() {
+            if (sInstance == null) {
+                synchronized (sInstanceLock) {
+                    if (sInstance == null) {
+                        sInstance = new UrlSpanFactory();
+                    }
+                }
+            }
+            return sInstance;
+        }
+
+        /**
+         * Factory function that will called by {@link Linkify} in order to create a
+         * {@link URLSpan}.
+         *
+         * @param url URL found
+         * @return a URLSpan instance
+         */
+        public URLSpan create(final String url) {
+            return new URLSpan(url);
+        }
+    }
+
+    /**
      *  Scans the text of the provided Spannable and turns all occurrences
      *  of the link types indicated in the mask into clickable links.
      *  If the mask is nonzero, it also removes any existing URLSpans
@@ -228,24 +271,55 @@
      *  @param mask Mask to define which kinds of links will be searched.
      *
      *  @return True if at least one link is found and applied.
+     *
+     * @see #addLinks(Spannable, int, UrlSpanFactory)
      */
     public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) {
-        return addLinks(text, mask, null);
+        return addLinks(text, mask, null, null);
     }
 
+    /**
+     *  Scans the text of the provided Spannable and turns all occurrences
+     *  of the link types indicated in the mask into clickable links.
+     *  If the mask is nonzero, it also removes any existing URLSpans
+     *  attached to the Spannable, to avoid problems if you call it
+     *  repeatedly on the same text.
+     *
+     *  @param text Spannable whose text is to be marked-up with links
+     *  @param mask mask to define which kinds of links will be searched
+     *  @param urlSpanFactory factory class used to create {@link URLSpan}s
+     *  @return True if at least one link is found and applied.
+     */
+    public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
+            @Nullable UrlSpanFactory urlSpanFactory) {
+        return addLinks(text, mask, null, urlSpanFactory);
+    }
+
+    /**
+     *  Scans the text of the provided Spannable and turns all occurrences of the link types
+     *  indicated in the mask into clickable links. If the mask is nonzero, it also removes any
+     *  existing URLSpans attached to the Spannable, to avoid problems if you call it repeatedly
+     *  on the same text.
+     *
+     * @param text Spannable whose text is to be marked-up with links
+     * @param mask mask to define which kinds of links will be searched
+     * @param context Context to be used while identifying phone numbers
+     * @param urlSpanFactory factory class used to create {@link URLSpan}s
+     * @return true if at least one link is found and applied.
+     */
     private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
-            @Nullable Context context) {
+            @Nullable Context context, @Nullable UrlSpanFactory urlSpanFactory) {
         if (mask == 0) {
             return false;
         }
 
-        URLSpan[] old = text.getSpans(0, text.length(), URLSpan.class);
+        final URLSpan[] old = text.getSpans(0, text.length(), URLSpan.class);
 
         for (int i = old.length - 1; i >= 0; i--) {
             text.removeSpan(old[i]);
         }
 
-        ArrayList<LinkSpec> links = new ArrayList<LinkSpec>();
+        final ArrayList<LinkSpec> links = new ArrayList<LinkSpec>();
 
         if ((mask & WEB_URLS) != 0) {
             gatherLinks(links, text, Patterns.AUTOLINK_WEB_URL,
@@ -274,7 +348,7 @@
         }
 
         for (LinkSpec link: links) {
-            applyLink(link.url, link.start, link.end, text);
+            applyLink(link.url, link.start, link.end, text, urlSpanFactory);
         }
 
         return true;
@@ -290,6 +364,8 @@
      *  @param mask Mask to define which kinds of links will be searched.
      *
      *  @return True if at least one link is found and applied.
+     *
+     *  @see #addLinks(Spannable, int, UrlSpanFactory)
      */
     public static final boolean addLinks(@NonNull TextView text, @LinkifyMask int mask) {
         if (mask == 0) {
@@ -299,7 +375,7 @@
         final Context context = text.getContext();
         final CharSequence t = text.getText();
         if (t instanceof Spannable) {
-            if (addLinks((Spannable) t, mask, context)) {
+            if (addLinks((Spannable) t, mask, context, null)) {
                 addLinkMovementMethod(text);
                 return true;
             }
@@ -308,7 +384,7 @@
         } else {
             SpannableString s = SpannableString.valueOf(t);
 
-            if (addLinks(s, mask, context)) {
+            if (addLinks(s, mask, context, null)) {
                 addLinkMovementMethod(text);
                 text.setText(s);
 
@@ -403,6 +479,8 @@
      *  @param pattern      Regex pattern to be used for finding links
      *  @param scheme       URL scheme string (eg <code>http://</code>) to be
      *                      prepended to the links that do not start with this scheme.
+     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
+     * UrlSpanFactory)
      */
     public static final boolean addLinks(@NonNull Spannable text, @NonNull Pattern pattern,
             @Nullable String scheme) {
@@ -423,6 +501,8 @@
      * @param transformFilter Filter to allow the client code to update the link found.
      *
      * @return True if at least one link is found and applied.
+     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
+     * UrlSpanFactory)
      */
     public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
             @Nullable String scheme, @Nullable MatchFilter matchFilter,
@@ -446,10 +526,39 @@
      * @param transformFilter Filter to allow the client code to update the link found.
      *
      * @return True if at least one link is found and applied.
+     *
+     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
+     * UrlSpanFactory)
      */
     public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
-            @Nullable  String defaultScheme, @Nullable String[] schemes,
+            @Nullable String defaultScheme, @Nullable String[] schemes,
             @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter) {
+        return addLinks(spannable, pattern, defaultScheme, schemes, matchFilter, transformFilter,
+                null);
+    }
+
+    /**
+     * Applies a regex to a Spannable turning the matches into links.
+     *
+     * @param spannable       spannable whose text is to be marked-up with links.
+     * @param pattern         regex pattern to be used for finding links.
+     * @param defaultScheme   the default scheme to be prepended to links if the link does not
+     *                        start with one of the <code>schemes</code> given.
+     * @param schemes         array of schemes (eg <code>http://</code>) to check if the link found
+     *                        contains a scheme. Passing a null or empty value means prepend
+     *                        defaultScheme
+     *                        to all links.
+     * @param matchFilter     the filter that is used to allow the client code additional control
+     *                        over which pattern matches are to be converted into links.
+     * @param transformFilter filter to allow the client code to update the link found.
+     * @param urlSpanFactory  factory class used to create {@link URLSpan}s
+     *
+     * @return True if at least one link is found and applied.
+     */
+    public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
+            @Nullable String defaultScheme, @Nullable String[] schemes,
+            @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter,
+            @Nullable UrlSpanFactory urlSpanFactory) {
         final String[] schemesCopy;
         if (defaultScheme == null) defaultScheme = "";
         if (schemes == null || schemes.length < 1) {
@@ -478,7 +587,7 @@
             if (allowed) {
                 String url = makeUrl(m.group(0), schemesCopy, m, transformFilter);
 
-                applyLink(url, start, end, spannable);
+                applyLink(url, start, end, spannable, urlSpanFactory);
                 hasMatches = true;
             }
         }
@@ -486,9 +595,12 @@
         return hasMatches;
     }
 
-    private static final void applyLink(String url, int start, int end, Spannable text) {
-        URLSpan span = new URLSpan(url);
-
+    private static void applyLink(String url, int start, int end, Spannable text,
+            @Nullable UrlSpanFactory urlSpanFactory) {
+        if (urlSpanFactory == null) {
+            urlSpanFactory = UrlSpanFactory.getInstance();
+        }
+        final URLSpan span = urlSpanFactory.create(url);
         text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
     }
 
diff --git a/core/java/android/util/Base64OutputStream.java b/core/java/android/util/Base64OutputStream.java
index 4535d1c..8378705 100644
--- a/core/java/android/util/Base64OutputStream.java
+++ b/core/java/android/util/Base64OutputStream.java
@@ -117,8 +117,10 @@
                 out.flush();
             }
         } catch (IOException e) {
-            if (thrown != null) {
+            if (thrown == null) {
                 thrown = e;
+            } else {
+                thrown.addSuppressed(e);
             }
         }
 
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index ec045b1..c037cd0 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -33,6 +33,8 @@
 
     public static final String FFLAG_PREFIX = "sys.fflag.";
     public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override.";
+    public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX;
+    public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
 
     private static final Map<String, String> DEFAULT_FLAGS;
     static {
@@ -41,6 +43,8 @@
         DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
         DEFAULT_FLAGS.put("settings_audio_switcher", "true");
         DEFAULT_FLAGS.put("settings_systemui_theme", "true");
+        DEFAULT_FLAGS.put("settings_dynamic_homepage", "false");
+        DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "true");
     }
 
     /**
diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java
index 84c2e83..5718d99 100644
--- a/core/java/android/util/Half.java
+++ b/core/java/android/util/Half.java
@@ -20,8 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
-import sun.misc.FloatingDecimal;
-
 /**
  * <p>The {@code Half} class is a wrapper and a utility class to manipulate half-precision 16-bit
  * <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a>
@@ -1026,7 +1024,7 @@
      *         half-precision float value
      */
     public static @HalfFloat short parseHalf(@NonNull String s) throws NumberFormatException {
-        return toHalf(FloatingDecimal.parseFloat(s));
+        return toHalf(Float.parseFloat(s));
     }
 
     /**
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index 3617aa7..5a74ec0 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -18,9 +18,11 @@
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
-import java.util.Arrays;
+
 import libcore.util.EmptyArray;
 
+import java.util.Arrays;
+
 /**
  * Implements a growing array of int primitives.
  *
@@ -102,7 +104,7 @@
         ensureCapacity(1);
         int rightSegment = mSize - index;
         mSize++;
-        checkBounds(index);
+        ArrayUtils.checkBounds(mSize, index);
 
         if (rightSegment != 0) {
             // Move by 1 all values from the right of 'index'
@@ -175,7 +177,7 @@
      * Returns the value at the specified position in this array.
      */
     public int get(int index) {
-        checkBounds(index);
+        ArrayUtils.checkBounds(mSize, index);
         return mValues[index];
     }
 
@@ -183,7 +185,7 @@
      * Sets the value at the specified position in this array.
      */
     public void set(int index, int value) {
-        checkBounds(index);
+        ArrayUtils.checkBounds(mSize, index);
         mValues[index] = value;
     }
 
@@ -205,7 +207,7 @@
      * Removes the value at the specified index from this array.
      */
     public void remove(int index) {
-        checkBounds(index);
+        ArrayUtils.checkBounds(mSize, index);
         System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1);
         mSize--;
     }
@@ -223,10 +225,4 @@
     public int[] toArray() {
         return Arrays.copyOf(mValues, mSize);
     }
-
-    private void checkBounds(int index) {
-        if (index < 0 || mSize <= index) {
-            throw new ArrayIndexOutOfBoundsException(mSize, index);
-        }
-    }
 }
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 2499afb..1b063e1 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -380,8 +380,8 @@
     /** @hide */ public static final int LOG_ID_SYSTEM = 3;
     /** @hide */ public static final int LOG_ID_CRASH = 4;
 
-    /** @hide */ public static native int println_native(int bufID,
-            int priority, String tag, String msg);
+    /** @hide */
+    public static native int println_native(int bufID, int priority, String tag, String msg);
 
     /**
      * Return the maximum payload the log daemon accepts without truncation.
diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java
index fa98096..5ed1c8c 100644
--- a/core/java/android/util/LongArray.java
+++ b/core/java/android/util/LongArray.java
@@ -106,7 +106,7 @@
         ensureCapacity(1);
         int rightSegment = mSize - index;
         mSize++;
-        checkBounds(index);
+        ArrayUtils.checkBounds(mSize, index);
 
         if (rightSegment != 0) {
             // Move by 1 all values from the right of 'index'
@@ -166,7 +166,7 @@
      * Returns the value at the specified position in this array.
      */
     public long get(int index) {
-        checkBounds(index);
+        ArrayUtils.checkBounds(mSize, index);
         return mValues[index];
     }
 
@@ -174,7 +174,7 @@
      * Sets the value at the specified position in this array.
      */
     public void set(int index, long value) {
-        checkBounds(index);
+        ArrayUtils.checkBounds(mSize, index);
         mValues[index] = value;
     }
 
@@ -196,7 +196,7 @@
      * Removes the value at the specified index from this array.
      */
     public void remove(int index) {
-        checkBounds(index);
+        ArrayUtils.checkBounds(mSize, index);
         System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1);
         mSize--;
     }
@@ -215,12 +215,6 @@
         return Arrays.copyOf(mValues, mSize);
     }
 
-    private void checkBounds(int index) {
-        if (index < 0 || mSize <= index) {
-            throw new ArrayIndexOutOfBoundsException(mSize, index);
-        }
-    }
-
     /**
      * Test if each element of {@code a} equals corresponding element from {@code b}
      */
diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java
index b2e24c3..72865cc 100644
--- a/core/java/android/util/MathUtils.java
+++ b/core/java/android/util/MathUtils.java
@@ -187,6 +187,21 @@
     }
 
     /**
+     * Perform Hermite interpolation between two values.
+     * Eg:
+     *   smoothStep(0, 0.5f, 0.5f) = 1f
+     *   smoothStep(0, 0.5f, 0.25f) = 0.5f
+     *
+     * @param start Left edge.
+     * @param end Right edge.
+     * @param x A value between {@code start} and {@code end}.
+     * @return A number between 0 and 1 representing where {@code x} is in the interpolation.
+     */
+    public static float smoothStep(float start, float end, float x) {
+        return constrain((x - start) / (end - start), 0f, 1f);
+    }
+
+    /**
      * Returns the sum of the two parameters, or throws an exception if the resulting sum would
      * cause an overflow or underflow.
      * @throws IllegalArgumentException when overflow or underflow would occur.
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index b3400ef..af18caa 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -22,32 +22,34 @@
 import libcore.util.EmptyArray;
 
 /**
- * SparseArrays map integers to Objects.  Unlike a normal array of Objects,
- * there can be gaps in the indices.  It is intended to be more memory efficient
- * than using a HashMap to map Integers to Objects, both because it avoids
+ * <code>SparseArray</code> maps integers to Objects and, unlike a normal array of Objects,
+ * its indices can contain gaps. <code>SparseArray</code> is intended to be more memory-efficient
+ * than a
+ * <a href="/reference/java/util/HashMap"><code>HashMap</code></a>, because it avoids
  * auto-boxing keys and its data structure doesn't rely on an extra entry object
  * for each mapping.
  *
  * <p>Note that this container keeps its mappings in an array data structure,
- * using a binary search to find keys.  The implementation is not intended to be appropriate for
+ * using a binary search to find keys. The implementation is not intended to be appropriate for
  * data structures
- * that may contain large numbers of items.  It is generally slower than a traditional
- * HashMap, since lookups require a binary search and adds and removes require inserting
- * and deleting entries in the array.  For containers holding up to hundreds of items,
- * the performance difference is not significant, less than 50%.</p>
+ * that may contain large numbers of items. It is generally slower than a
+ * <code>HashMap</code> because lookups require a binary search,
+ * and adds and removes require inserting
+ * and deleting entries in the array. For containers holding up to hundreds of items,
+ * the performance difference is less than 50%.
  *
  * <p>To help with performance, the container includes an optimization when removing
  * keys: instead of compacting its array immediately, it leaves the removed entry marked
- * as deleted.  The entry can then be re-used for the same key, or compacted later in
- * a single garbage collection step of all removed entries.  This garbage collection will
- * need to be performed at any time the array needs to be grown or the the map size or
- * entry values are retrieved.</p>
+ * as deleted. The entry can then be re-used for the same key or compacted later in
+ * a single garbage collection of all removed entries. This garbage collection
+ * must be performed whenever the array needs to be grown, or when the map size or
+ * entry values are retrieved.
  *
  * <p>It is possible to iterate over the items in this container using
  * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using
- * <code>keyAt(int)</code> with ascending values of the index will return the
- * keys in ascending order, or the values corresponding to the keys in ascending
- * order in the case of <code>valueAt(int)</code>.</p>
+ * <code>keyAt(int)</code> with ascending values of the index returns the
+ * keys in ascending order. In the case of <code>valueAt(int)</code>, the
+ * values corresponding to the keys are returned in ascending order.
  */
 public class SparseArray<E> implements Cloneable {
     private static final Object DELETED = new Object();
@@ -333,7 +335,7 @@
 
     /**
      * Returns an index for which {@link #valueAt} would return the
-     * specified key, or a negative number if no keys map to the
+     * specified value, or a negative number if no keys map to the
      * specified value.
      * <p>Beware that this is a linear search, unlike lookups by key,
      * and that multiple keys can map to the same value and this will
@@ -357,7 +359,7 @@
 
     /**
      * Returns an index for which {@link #valueAt} would return the
-     * specified key, or a negative number if no keys map to the
+     * specified value, or a negative number if no keys map to the
      * specified value.
      * <p>Beware that this is a linear search, unlike lookups by key,
      * and that multiple keys can map to the same value and this will
diff --git a/core/java/android/util/TimestampedValue.java b/core/java/android/util/TimestampedValue.java
new file mode 100644
index 0000000..1289e4d
--- /dev/null
+++ b/core/java/android/util/TimestampedValue.java
@@ -0,0 +1,137 @@
+/*
+ * 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.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.SystemClock;
+
+import java.util.Objects;
+
+/**
+ * A value with an associated reference time. The reference time will typically be provided by the
+ * elapsed realtime clock. The elapsed realtime clock can be obtained using methods like
+ * {@link SystemClock#elapsedRealtime()} or {@link SystemClock#elapsedRealtimeClock()}.
+ * If a suitable clock is used the reference time can be used to identify the age of a value or
+ * ordering between values.
+ *
+ * <p>To read and write a timestamped value from / to a Parcel see
+ * {@link #readFromParcel(Parcel, ClassLoader, Class)} and
+ * {@link #writeToParcel(Parcel, TimestampedValue)}.
+ *
+ * @param <T> the type of the value with an associated timestamp
+ * @hide
+ */
+public final class TimestampedValue<T> {
+    private final long mReferenceTimeMillis;
+    private final T mValue;
+
+    public TimestampedValue(long referenceTimeMillis, T value) {
+        mReferenceTimeMillis = referenceTimeMillis;
+        mValue = value;
+    }
+
+    public long getReferenceTimeMillis() {
+        return mReferenceTimeMillis;
+    }
+
+    public T getValue() {
+        return mValue;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        TimestampedValue<?> that = (TimestampedValue<?>) o;
+        return mReferenceTimeMillis == that.mReferenceTimeMillis
+                && Objects.equals(mValue, that.mValue);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mReferenceTimeMillis, mValue);
+    }
+
+    @Override
+    public String toString() {
+        return "TimestampedValue{"
+                + "mReferenceTimeMillis=" + mReferenceTimeMillis
+                + ", mValue=" + mValue
+                + '}';
+    }
+
+    /**
+     * Read a {@link TimestampedValue} from a parcel that was stored using
+     * {@link #writeToParcel(Parcel, TimestampedValue)}.
+     *
+     * <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
+     * and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
+     * supported by those methods.
+     *
+     * @param in the Parcel to read from
+     * @param classLoader the ClassLoader to pass to {@link Parcel#readValue(ClassLoader)}
+     * @param valueClass the expected type of the value, typically the same as {@code <T>} but can
+     *     also be a subclass
+     * @throws RuntimeException if the value read is not compatible with {@code valueClass} or the
+     *     object could not be read
+     */
+    @SuppressWarnings("unchecked")
+    @NonNull
+    public static <T> TimestampedValue<T> readFromParcel(
+            @NonNull Parcel in, @Nullable ClassLoader classLoader, Class<? extends T> valueClass) {
+        long referenceTimeMillis = in.readLong();
+        T value = (T) in.readValue(classLoader);
+        // Equivalent to static code: if (!(value.getClass() instanceof {valueClass})) {
+        if (value != null && !valueClass.isAssignableFrom(value.getClass())) {
+            throw new RuntimeException("Value was of type " + value.getClass()
+                    + " is not assignable to " + valueClass);
+        }
+        return new TimestampedValue<>(referenceTimeMillis, value);
+    }
+
+    /**
+     * Write a {@link TimestampedValue} to a parcel so that it can be read using
+     * {@link #readFromParcel(Parcel, ClassLoader, Class)}.
+     *
+     * <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
+     * and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
+     * supported by those methods.
+     *
+     * @param dest the Parcel
+     * @param timestampedValue the value
+     * @throws RuntimeException if the value could not be written to the Parcel
+     */
+    public static void writeToParcel(
+            @NonNull Parcel dest, @NonNull TimestampedValue<?> timestampedValue) {
+        dest.writeLong(timestampedValue.mReferenceTimeMillis);
+        dest.writeValue(timestampedValue.mValue);
+    }
+
+    /**
+     * Returns the difference in milliseconds between two instance's reference times.
+     */
+    public static long referenceTimeDifference(
+            @NonNull TimestampedValue<?> one, @NonNull TimestampedValue<?> two) {
+        return one.mReferenceTimeMillis - two.mReferenceTimeMillis;
+    }
+}
diff --git a/core/java/android/util/Xml.java b/core/java/android/util/Xml.java
index 041e8a8..e3b8fec 100644
--- a/core/java/android/util/Xml.java
+++ b/core/java/android/util/Xml.java
@@ -16,27 +16,27 @@
 
 package android.util;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.UnsupportedEncodingException;
-import org.apache.harmony.xml.ExpatReader;
-import org.kxml2.io.KXmlParser;
+import libcore.util.XmlObjectFactory;
+
 import org.xml.sax.ContentHandler;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 import org.xml.sax.XMLReader;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
 import org.xmlpull.v1.XmlSerializer;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+
 /**
  * XML utility methods.
  */
 public class Xml {
-    /** @hide */ public Xml() {}
+    private Xml() {}
 
     /**
      * {@link org.xmlpull.v1.XmlPullParser} "relaxed" feature name.
@@ -52,7 +52,7 @@
     public static void parse(String xml, ContentHandler contentHandler)
             throws SAXException {
         try {
-            XMLReader reader = new ExpatReader();
+            XMLReader reader = XmlObjectFactory.newXMLReader();
             reader.setContentHandler(contentHandler);
             reader.parse(new InputSource(new StringReader(xml)));
         } catch (IOException e) {
@@ -66,7 +66,7 @@
      */
     public static void parse(Reader in, ContentHandler contentHandler)
             throws IOException, SAXException {
-        XMLReader reader = new ExpatReader();
+        XMLReader reader = XmlObjectFactory.newXMLReader();
         reader.setContentHandler(contentHandler);
         reader.parse(new InputSource(in));
     }
@@ -77,7 +77,7 @@
      */
     public static void parse(InputStream in, Encoding encoding,
             ContentHandler contentHandler) throws IOException, SAXException {
-        XMLReader reader = new ExpatReader();
+        XMLReader reader = XmlObjectFactory.newXMLReader();
         reader.setContentHandler(contentHandler);
         InputSource source = new InputSource(in);
         source.setEncoding(encoding.expatName);
@@ -89,7 +89,7 @@
      */
     public static XmlPullParser newPullParser() {
         try {
-            KXmlParser parser = new KXmlParser();
+            XmlPullParser parser = XmlObjectFactory.newXmlPullParser();
             parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
             parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
             return parser;
@@ -102,25 +102,7 @@
      * Creates a new xml serializer.
      */
     public static XmlSerializer newSerializer() {
-        try {
-            return XmlSerializerFactory.instance.newSerializer();
-        } catch (XmlPullParserException e) {
-            throw new AssertionError(e);
-        }
-    }
-
-    /** Factory for xml serializers. Initialized on demand. */
-    static class XmlSerializerFactory {
-        static final String TYPE
-                = "org.kxml2.io.KXmlParser,org.kxml2.io.KXmlSerializer";
-        static final XmlPullParserFactory instance;
-        static {
-            try {
-                instance = XmlPullParserFactory.newInstance(TYPE, null);
-            } catch (XmlPullParserException e) {
-                throw new AssertionError(e);
-            }
-        }
+        return XmlObjectFactory.newXmlSerializer();
     }
 
     /**
diff --git a/core/java/android/util/jar/StrictJarFile.java b/core/java/android/util/jar/StrictJarFile.java
index bc4a19d..11aee2f 100644
--- a/core/java/android/util/jar/StrictJarFile.java
+++ b/core/java/android/util/jar/StrictJarFile.java
@@ -390,6 +390,7 @@
     public static class ZipInflaterInputStream extends InflaterInputStream {
         private final ZipEntry entry;
         private long bytesRead = 0;
+        private boolean closed;
 
         public ZipInflaterInputStream(InputStream is, Inflater inf, int bsize, ZipEntry entry) {
             super(is, inf, bsize);
@@ -424,6 +425,12 @@
             }
             return super.available() == 0 ? 0 : (int) (entry.getSize() - bytesRead);
         }
+
+        @Override
+        public void close() throws IOException {
+            super.close();
+            closed = true;
+        }
     }
 
     /**
diff --git a/core/java/android/util/jar/StrictJarManifest.java b/core/java/android/util/jar/StrictJarManifest.java
index dbb466c..faec099 100644
--- a/core/java/android/util/jar/StrictJarManifest.java
+++ b/core/java/android/util/jar/StrictJarManifest.java
@@ -44,6 +44,9 @@
 
     private static final byte[] VALUE_SEPARATOR = new byte[] { ':', ' ' };
 
+    /** The attribute name "Name". */
+    static final Attributes.Name ATTRIBUTE_NAME_NAME = new Attributes.Name("Name");
+
     private final Attributes mainAttributes;
     private final HashMap<String, Attributes> entries;
 
@@ -276,7 +279,7 @@
         Iterator<String> i = manifest.getEntries().keySet().iterator();
         while (i.hasNext()) {
             String key = i.next();
-            writeEntry(out, Attributes.Name.NAME, key, encoder, buffer);
+            writeEntry(out, ATTRIBUTE_NAME_NAME, key, encoder, buffer);
             Attributes attributes = manifest.entries.get(key);
             Iterator<?> entries = attributes.keySet().iterator();
             while (entries.hasNext()) {
diff --git a/core/java/android/util/jar/StrictJarManifestReader.java b/core/java/android/util/jar/StrictJarManifestReader.java
index 9881bb0..b17abc8 100644
--- a/core/java/android/util/jar/StrictJarManifestReader.java
+++ b/core/java/android/util/jar/StrictJarManifestReader.java
@@ -58,7 +58,7 @@
     public void readEntries(Map<String, Attributes> entries, Map<String, StrictJarManifest.Chunk> chunks) throws IOException {
         int mark = pos;
         while (readHeader()) {
-            if (!Attributes.Name.NAME.equals(name)) {
+            if (!StrictJarManifest.ATTRIBUTE_NAME_NAME.equals(name)) {
                 throw new IOException("Entry is not named");
             }
             String entryNameValue = value;
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 47bda53..496bc9f 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -29,6 +29,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
+import android.graphics.Region.Op;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -325,14 +326,9 @@
      * @hide
      */
     public static DisplayCutout fromBoundingRect(int left, int top, int right, int bottom) {
-        Path path = new Path();
-        path.reset();
-        path.moveTo(left, top);
-        path.lineTo(left, bottom);
-        path.lineTo(right, bottom);
-        path.lineTo(right, top);
-        path.close();
-        return fromBounds(path);
+        Region r = Region.obtain();
+        r.set(left, top, right, bottom);
+        return fromBounds(r);
     }
 
     /**
@@ -340,26 +336,19 @@
      *
      * @hide
      */
-    public static DisplayCutout fromBounds(Path path) {
-        RectF clipRect = new RectF();
-        path.computeBounds(clipRect, false /* unused */);
-        Region clipRegion = Region.obtain();
-        clipRegion.set((int) clipRect.left, (int) clipRect.top,
-                (int) clipRect.right, (int) clipRect.bottom);
-
-        Region bounds = new Region();
-        bounds.setPath(path, clipRegion);
-        clipRegion.recycle();
-        return new DisplayCutout(ZERO_RECT, bounds, false /* copyArguments */);
+    public static DisplayCutout fromBounds(Region region) {
+        return new DisplayCutout(ZERO_RECT, region, false /* copyArguments */);
     }
 
     /**
-     * Creates the bounding path according to @android:string/config_mainBuiltInDisplayCutout.
+     * Creates the display cutout according to
+     * @android:string/config_mainBuiltInDisplayCutoutRectApproximation, which is the closest
+     * rectangle-base approximation of the cutout.
      *
      * @hide
      */
-    public static DisplayCutout fromResources(Resources res, int displayWidth, int displayHeight) {
-        return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
+    public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth, int displayHeight) {
+        return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation),
                 displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT);
     }
 
@@ -369,7 +358,8 @@
      * @hide
      */
     public static Path pathFromResources(Resources res, int displayWidth, int displayHeight) {
-        return pathAndDisplayCutoutFromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
+        return pathAndDisplayCutoutFromSpec(
+                res.getString(R.string.config_mainBuiltInDisplayCutout),
                 displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT).first;
     }
 
@@ -417,6 +407,7 @@
         }
 
         final Path p;
+        final Region r = Region.obtain();
         try {
             p = PathParser.createPathFromPathData(spec);
         } catch (Throwable e) {
@@ -431,6 +422,8 @@
         m.postTranslate(offsetX, 0);
         p.transform(m);
 
+        addToRegion(p, r);
+
         if (bottomSpec != null) {
             final Path bottomPath;
             try {
@@ -443,9 +436,10 @@
             m.postTranslate(0, displayHeight);
             bottomPath.transform(m);
             p.addPath(bottomPath);
+            addToRegion(bottomPath, r);
         }
 
-        final Pair<Path, DisplayCutout> result = new Pair<>(p, fromBounds(p));
+        final Pair<Path, DisplayCutout> result = new Pair<>(p, fromBounds(r));
         synchronized (CACHE_LOCK) {
             sCachedSpec = spec;
             sCachedDisplayWidth = displayWidth;
@@ -456,6 +450,14 @@
         return result;
     }
 
+    private static void addToRegion(Path p, Region r) {
+        final RectF rectF = new RectF();
+        final Rect rect = new Rect();
+        p.computeBounds(rectF, false /* unused */);
+        rectF.round(rect);
+        r.op(rect, Op.UNION);
+    }
+
     private static Region boundingRectsToRegion(List<Rect> rects) {
         Region result = Region.obtain();
         if (rects != null) {
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 671532c..df4d5c4 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -180,13 +180,12 @@
     ///////////////////////////////////////////////////////////////////////////
 
     /**
-     * Draws the specified display list onto this canvas. The display list can only
-     * be drawn if {@link android.view.RenderNode#isValid()} returns true.
+     * Draws the specified display list onto this canvas.
      *
      * @param renderNode The RenderNode to draw.
      */
     public void drawRenderNode(RenderNode renderNode) {
-        nDrawRenderNode(mNativeCanvasWrapper, renderNode.getNativeDisplayList());
+        nDrawRenderNode(mNativeCanvasWrapper, renderNode.mNativeRenderNode);
     }
 
     ///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index f868a00..bedfa9f 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -37,15 +37,10 @@
  * {@hide}
  */
 interface IWindowSession {
-    int add(IWindow window, int seq, in WindowManager.LayoutParams attrs,
-            in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets,
-            out InputChannel outInputChannel);
     int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out Rect outFrame,
             out Rect outContentInsets, out Rect outStableInsets, out Rect outOutsets,
             out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel);
-    int addWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
-            in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets);
     int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out Rect outContentInsets,
             out Rect outStableInsets);
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 1f2aab9..c257364 100644
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -95,6 +95,19 @@
     }
 
     /**
+     * Gets the display id of the event.
+     * @return The display id associated with the event.
+     * @hide
+     */
+    public abstract int getDisplayId();
+
+    /**
+     * Modifies the display id associated with the event
+     * @param displayId
+     * @hide
+     */
+    public abstract void setDisplayId(int displayId);
+    /**
      * Copies the event.
      *
      * @return A deep copy of the event.
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 35546f8..0115d26 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.os.Parcel;
@@ -1246,6 +1248,7 @@
 
     private int mDeviceId;
     private int mSource;
+    private int mDisplayId;
     private int mMetaState;
     private int mAction;
     private int mKeyCode;
@@ -1473,6 +1476,7 @@
         mScanCode = scancode;
         mFlags = flags;
         mSource = source;
+        mDisplayId = INVALID_DISPLAY;
     }
 
     /**
@@ -1497,6 +1501,7 @@
         mDeviceId = deviceId;
         mFlags = flags;
         mSource = InputDevice.SOURCE_KEYBOARD;
+        mDisplayId = INVALID_DISPLAY;
     }
 
     /**
@@ -1511,6 +1516,7 @@
         mMetaState = origEvent.mMetaState;
         mDeviceId = origEvent.mDeviceId;
         mSource = origEvent.mSource;
+        mDisplayId = origEvent.mDisplayId;
         mScanCode = origEvent.mScanCode;
         mFlags = origEvent.mFlags;
         mCharacters = origEvent.mCharacters;
@@ -1537,6 +1543,7 @@
         mMetaState = origEvent.mMetaState;
         mDeviceId = origEvent.mDeviceId;
         mSource = origEvent.mSource;
+        mDisplayId = origEvent.mDisplayId;
         mScanCode = origEvent.mScanCode;
         mFlags = origEvent.mFlags;
         mCharacters = origEvent.mCharacters;
@@ -1564,7 +1571,7 @@
      */
     public static KeyEvent obtain(long downTime, long eventTime, int action,
             int code, int repeat, int metaState,
-            int deviceId, int scancode, int flags, int source, String characters) {
+            int deviceId, int scancode, int flags, int source, int displayId, String characters) {
         KeyEvent ev = obtain();
         ev.mDownTime = downTime;
         ev.mEventTime = eventTime;
@@ -1576,11 +1583,26 @@
         ev.mScanCode = scancode;
         ev.mFlags = flags;
         ev.mSource = source;
+        ev.mDisplayId = displayId;
         ev.mCharacters = characters;
         return ev;
     }
 
     /**
+     * Obtains a (potentially recycled) key event.
+     *
+     * @hide
+     */
+    public static KeyEvent obtain(long downTime, long eventTime, int action,
+            int code, int repeat, int metaState,
+            int deviceId, int scancode, int flags, int source, String characters) {
+        return obtain(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode,
+                flags, source, INVALID_DISPLAY, characters);
+    }
+
+    /**
+
+    /**
      * Obtains a (potentially recycled) copy of another key event.
      *
      * @hide
@@ -1597,6 +1619,7 @@
         ev.mScanCode = other.mScanCode;
         ev.mFlags = other.mFlags;
         ev.mSource = other.mSource;
+        ev.mDisplayId = other.mDisplayId;
         ev.mCharacters = other.mCharacters;
         return ev;
     }
@@ -1683,6 +1706,7 @@
         mMetaState = origEvent.mMetaState;
         mDeviceId = origEvent.mDeviceId;
         mSource = origEvent.mSource;
+        mDisplayId = origEvent.mDisplayId;
         mScanCode = origEvent.mScanCode;
         mFlags = origEvent.mFlags;
         // Don't copy mCharacters, since one way or the other we'll lose it
@@ -1917,6 +1941,18 @@
         mSource = source;
     }
 
+    /** @hide */
+    @Override
+    public final int getDisplayId() {
+        return mDisplayId;
+    }
+
+    /** @hide */
+    @Override
+    public final void setDisplayId(int displayId) {
+        mDisplayId = displayId;
+    }
+
     /**
      * <p>Returns the state of the meta keys.</p>
      *
@@ -2852,6 +2888,7 @@
         msg.append(", downTime=").append(mDownTime);
         msg.append(", deviceId=").append(mDeviceId);
         msg.append(", source=0x").append(Integer.toHexString(mSource));
+        msg.append(", displayId=").append(mDisplayId);
         msg.append(" }");
         return msg.toString();
     }
@@ -2883,6 +2920,9 @@
      * such as "KEYCODE_A", "KEYCODE_DPAD_UP", or an equivalent numeric constant
      * such as "1001" if unknown.
      *
+     * This function is intended to be used mostly for debugging, logging, and testing. It is not
+     * locale-specific and is not intended to be used in a user-facing manner.
+     *
      * @param keyCode The key code.
      * @return The symbolic name of the specified keycode.
      *
@@ -2983,6 +3023,7 @@
     private KeyEvent(Parcel in) {
         mDeviceId = in.readInt();
         mSource = in.readInt();
+        mDisplayId = in.readInt();
         mAction = in.readInt();
         mKeyCode = in.readInt();
         mRepeatCount = in.readInt();
@@ -2999,6 +3040,7 @@
 
         out.writeInt(mDeviceId);
         out.writeInt(mSource);
+        out.writeInt(mDisplayId);
         out.writeInt(mAction);
         out.writeInt(mKeyCode);
         out.writeInt(mRepeatCount);
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 9148c27..344806a 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1943,11 +1943,13 @@
     }
 
     /** @hide */
+    @Override
     public int getDisplayId() {
         return nativeGetDisplayId(mNativePtr);
     }
 
     /** @hide */
+    @Override
     public void setDisplayId(int displayId) {
         nativeSetDisplayId(mNativePtr, displayId);
     }
diff --git a/core/java/android/view/RecordingCanvas.java b/core/java/android/view/RecordingCanvas.java
index 18cc10f..3364483 100644
--- a/core/java/android/view/RecordingCanvas.java
+++ b/core/java/android/view/RecordingCanvas.java
@@ -515,7 +515,7 @@
                             contextStart - paraStart,
                             contextEnd - contextStart,
                             x, y, isRtl, paint.getNativeInstance(),
-                            mp.getNativePtr());
+                            mp.getNativeMeasuredParagraph().getNativePtr());
                     return;
                 }
             }
@@ -536,9 +536,6 @@
             @Nullable int[] colors, int colorOffset, @Nullable short[] indices, int indexOffset,
             int indexCount, @NonNull Paint paint) {
         checkRange(verts.length, vertOffset, vertexCount);
-        if (isHardwareAccelerated()) {
-            return;
-        }
         if (texs != null) {
             checkRange(texs.length, texOffset, vertexCount);
         }
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 7c25fac..e0df59f 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Matrix;
@@ -29,6 +30,9 @@
 
 import libcore.util.NativeAllocationRegistry;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * <p>A display list records a series of graphics related operations and can replay
  * them later. Display lists are usually built by recording operations on a
@@ -139,7 +143,9 @@
                 RenderNode.class.getClassLoader(), nGetNativeFinalizer(), 1024);
     }
 
-    // Do not access directly unless you are ThreadedRenderer
+    /** Not for general use; use only if you are ThreadedRenderer or DisplayListCanvas.
+     * @hide
+     */
     final long mNativeRenderNode;
     private final View mOwningView;
 
@@ -159,15 +165,6 @@
     }
 
     /**
-     * Immediately destroys the RenderNode
-     * Only suitable for testing/benchmarking where waiting for the GC/finalizer
-     * is not feasible.
-     */
-    public void destroy() {
-        // TODO: Removed temporarily
-    }
-
-    /**
      * Creates a new RenderNode that can be used to record batches of
      * drawing operations, and store / apply render properties when drawn.
      *
@@ -219,6 +216,14 @@
     }
 
     /**
+     * Same as {@link #start(int, int)} but with the RenderNode's width & height
+     */
+    public DisplayListCanvas start() {
+        return DisplayListCanvas.obtain(this,
+                nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode));
+    }
+
+    /**
      * Ends the recording for this display list. A display list cannot be
      * replayed if recording is not finished. Calling this method marks
      * the display list valid and {@link #isValid()} will return true.
@@ -251,13 +256,6 @@
         return nIsValid(mNativeRenderNode);
     }
 
-    long getNativeDisplayList() {
-        if (!isValid()) {
-            throw new IllegalStateException("The display list is not valid.");
-        }
-        return mNativeRenderNode;
-    }
-
     ///////////////////////////////////////////////////////////////////////////
     // Matrix manipulation
     ///////////////////////////////////////////////////////////////////////////
@@ -455,6 +453,25 @@
         return nSetHasOverlappingRendering(mNativeRenderNode, hasOverlappingRendering);
     }
 
+    /** @hide */
+    @IntDef({USAGE_BACKGROUND})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UsageHint {}
+
+    /** The default usage hint */
+    public static final int USAGE_UNKNOWN = 0;
+
+    /** Usage is background content */
+    public static final int USAGE_BACKGROUND = 1;
+
+    /**
+     * Provides a hint on what this RenderNode's display list content contains. This hint is used
+     * for automatic content transforms to improve accessibility or similar.
+     */
+    public void setUsageHint(@UsageHint int usageHint) {
+        nSetUsageHint(mNativeRenderNode, usageHint);
+    }
+
     /**
      * Indicates whether the content of this display list overlaps.
      *
@@ -463,7 +480,6 @@
      * @see #setHasOverlappingRendering(boolean)
      */
     public boolean hasOverlappingRendering() {
-        //noinspection SimplifiableIfStatement
         return nHasOverlappingRendering(mNativeRenderNode);
     }
 
@@ -955,6 +971,8 @@
     private static native boolean nSetHasOverlappingRendering(long renderNode,
             boolean hasOverlappingRendering);
     @CriticalNative
+    private static native void nSetUsageHint(long renderNode, int usageHint);
+    @CriticalNative
     private static native boolean nSetElevation(long renderNode, float lift);
     @CriticalNative
     private static native boolean nSetTranslationX(long renderNode, float translationX);
@@ -1009,4 +1027,8 @@
     private static native float nGetPivotX(long renderNode);
     @CriticalNative
     private static native float nGetPivotY(long renderNode);
+    @CriticalNative
+    private static native int nGetWidth(long renderNode);
+    @CriticalNative
+    private static native int nGetHeight(long renderNode);
 }
diff --git a/core/java/android/view/RoundScrollbarRenderer.java b/core/java/android/view/RoundScrollbarRenderer.java
index 1348510..df9e23e 100644
--- a/core/java/android/view/RoundScrollbarRenderer.java
+++ b/core/java/android/view/RoundScrollbarRenderer.java
@@ -31,13 +31,14 @@
     private static final int MAX_SCROLLBAR_ANGLE_SWIPE = 16;
     private static final int MIN_SCROLLBAR_ANGLE_SWIPE = 6;
     private static final float WIDTH_PERCENTAGE = 0.02f;
-    private static final int DEFAULT_THUMB_COLOR = 0x4CFFFFFF;
-    private static final int DEFAULT_TRACK_COLOR = 0x26FFFFFF;
+    private static final int DEFAULT_THUMB_COLOR = 0xFFE8EAED;
+    private static final int DEFAULT_TRACK_COLOR = 0x4CFFFFFF;
 
     private final Paint mThumbPaint = new Paint();
     private final Paint mTrackPaint = new Paint();
     private final RectF mRect = new RectF();
     private final View mParent;
+    private final int mMaskThickness;
 
     public RoundScrollbarRenderer(View parent) {
         // Paints for the round scrollbar.
@@ -52,6 +53,12 @@
         mTrackPaint.setStyle(Paint.Style.STROKE);
 
         mParent = parent;
+
+        // Fetch the resource indicating the thickness of CircularDisplayMask, rounding in the same
+        // way WindowManagerService.showCircularMask does. The scroll bar is inset by this amount so
+        // that it doesn't get clipped.
+        mMaskThickness = parent.getContext().getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.circular_display_mask_thickness);
     }
 
     public void drawRoundScrollbars(Canvas canvas, float alpha, Rect bounds) {
@@ -82,13 +89,13 @@
         startAngle = clamp(startAngle, -SCROLLBAR_ANGLE_RANGE / 2,
                 SCROLLBAR_ANGLE_RANGE / 2 - sweepAngle);
 
-        // Draw the track and the scroll bar.
+        // Draw the track and the thumb.
+        float inset = thumbWidth / 2 + mMaskThickness;
         mRect.set(
-                bounds.left - thumbWidth / 2,
-                bounds.top,
-                bounds.right - thumbWidth / 2,
-                bounds.bottom);
-
+                bounds.left + inset,
+                bounds.top + inset,
+                bounds.right - inset,
+                bounds.bottom - inset);
         canvas.drawArc(mRect, -SCROLLBAR_ANGLE_RANGE / 2, SCROLLBAR_ANGLE_RANGE, false,
                 mTrackPaint);
         canvas.drawArc(mRect, startAngle, sweepAngle, false, mThumbPaint);
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index b5912bc..ee08bf7 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -37,7 +37,12 @@
     }
 
     public SurfaceSession(Surface root) {
-        mNativeClient = nativeCreateScoped(root.mNativeObject);
+        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 ... */
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index db34856..44c1780 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -579,7 +579,7 @@
                                     0.0f, 0.0f,
                                     mScreenRect.height() / (float) mSurfaceHeight);
                         }
-                        if (sizeChanged) {
+                        if (sizeChanged && !creating) {
                             mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
                         }
                     } finally {
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index aa1e407..2930699 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -850,7 +850,9 @@
 
 
     void buildLayer(RenderNode node) {
-        nBuildLayer(mNativeProxy, node.getNativeDisplayList());
+        if (node.isValid()) {
+            nBuildLayer(mNativeProxy, node.mNativeRenderNode);
+        }
     }
 
 
@@ -928,7 +930,7 @@
      * not the RenderNode from a View.
      **/
     public static Bitmap createHardwareBitmap(RenderNode node, int width, int height) {
-        return nCreateHardwareBitmap(node.getNativeDisplayList(), width, height);
+        return nCreateHardwareBitmap(node.mNativeRenderNode, width, height);
     }
 
     /**
@@ -953,6 +955,10 @@
         nSetDebuggingEnabled(enable);
     }
 
+    void allocateBuffers(Surface surface) {
+        nAllocateBuffers(mNativeProxy, surface);
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -1241,4 +1247,5 @@
     private static native void nSetDebuggingEnabled(boolean enabled);
     private static native void nSetIsolatedProcess(boolean enabled);
     private static native void nSetContextPriority(int priority);
+    private static native void nAllocateBuffers(long nativeProxy, Surface window);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4ec07fc..e2e2b00 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -788,6 +788,13 @@
     protected static final String VIEW_LOG_TAG = "View";
 
     /**
+     * The logging tag used by this class when logging verbose, autofill-related messages.
+     */
+    // NOTE: We cannot use android.view.autofill.Helper.sVerbose because that variable is not
+    // set if a session is not started.
+    private static final String AUTOFILL_LOG_TAG = "View.Autofill";
+
+    /**
      * When set to true, apps will draw debugging information about their layouts.
      *
      * @hide
@@ -6112,14 +6119,18 @@
         }
         x += getScrollX();
         y += getScrollY();
-        if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden()) {
+        final boolean canScrollVertically =
+                computeVerticalScrollRange() > computeVerticalScrollExtent();
+        if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden() && canScrollVertically) {
             final Rect touchBounds = mScrollCache.mScrollBarTouchBounds;
             getVerticalScrollBarBounds(null, touchBounds);
             if (touchBounds.contains((int) x, (int) y)) {
                 return true;
             }
         }
-        if (isHorizontalScrollBarEnabled()) {
+        final boolean canScrollHorizontally =
+                computeHorizontalScrollRange() > computeHorizontalScrollExtent();
+        if (isHorizontalScrollBarEnabled() && canScrollHorizontally) {
             final Rect touchBounds = mScrollCache.mScrollBarTouchBounds;
             getHorizontalScrollBarBounds(null, touchBounds);
             if (touchBounds.contains((int) x, (int) y)) {
@@ -6134,18 +6145,18 @@
     }
 
     private boolean isOnVerticalScrollbarThumb(float x, float y) {
-        if (mScrollCache == null) {
+        if (mScrollCache == null || !isVerticalScrollBarEnabled() || isVerticalScrollBarHidden()) {
             return false;
         }
-        if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden()) {
+        final int range = computeVerticalScrollRange();
+        final int extent = computeVerticalScrollExtent();
+        if (range > extent) {
             x += getScrollX();
             y += getScrollY();
             final Rect bounds = mScrollCache.mScrollBarBounds;
             final Rect touchBounds = mScrollCache.mScrollBarTouchBounds;
             getVerticalScrollBarBounds(bounds, touchBounds);
-            final int range = computeVerticalScrollRange();
             final int offset = computeVerticalScrollOffset();
-            final int extent = computeVerticalScrollExtent();
             final int thumbLength = ScrollBarUtils.getThumbLength(bounds.height(), bounds.width(),
                     extent, range);
             final int thumbOffset = ScrollBarUtils.getThumbOffset(bounds.height(), thumbLength,
@@ -6161,18 +6172,19 @@
     }
 
     private boolean isOnHorizontalScrollbarThumb(float x, float y) {
-        if (mScrollCache == null) {
+        if (mScrollCache == null || !isHorizontalScrollBarEnabled()) {
             return false;
         }
-        if (isHorizontalScrollBarEnabled()) {
+        final int range = computeHorizontalScrollRange();
+        final int extent = computeHorizontalScrollExtent();
+        if (range > extent) {
             x += getScrollX();
             y += getScrollY();
             final Rect bounds = mScrollCache.mScrollBarBounds;
             final Rect touchBounds = mScrollCache.mScrollBarTouchBounds;
             getHorizontalScrollBarBounds(bounds, touchBounds);
-            final int range = computeHorizontalScrollRange();
             final int offset = computeHorizontalScrollOffset();
-            final int extent = computeHorizontalScrollExtent();
+
             final int thumbLength = ScrollBarUtils.getThumbLength(bounds.width(), bounds.height(),
                     extent, range);
             final int thumbOffset = ScrollBarUtils.getThumbOffset(bounds.width(), thumbLength,
@@ -7535,7 +7547,7 @@
      */
     public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
         if ((event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)
-                && !TextUtils.isEmpty(getAccessibilityPaneTitle())) {
+                && isAccessibilityPane()) {
             event.getText().add(getAccessibilityPaneTitle());
         }
     }
@@ -7979,10 +7991,9 @@
     private void onProvideVirtualStructureCompat(ViewStructure structure, boolean forAutofill) {
         final AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
         if (provider != null) {
-            if (android.view.autofill.Helper.sVerbose && forAutofill) {
-                Log.v(VIEW_LOG_TAG, "onProvideVirtualStructureCompat() for " + this);
+            if (forAutofill && Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
+                Log.v(AUTOFILL_LOG_TAG, "onProvideVirtualStructureCompat() for " + this);
             }
-
             final AccessibilityNodeInfo info = createAccessibilityNodeInfo();
             structure.setChildCount(1);
             final ViewStructure root = structure.newChild(0);
@@ -8220,8 +8231,8 @@
      */
     public void setAutofillId(@Nullable AutofillId id) {
         // TODO(b/37566627): add unit / CTS test for all possible combinations below
-        if (android.view.autofill.Helper.sVerbose) {
-            Log.v(VIEW_LOG_TAG, "setAutofill(): from " + mAutofillId + " to " + id);
+        if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
+            Log.v(AUTOFILL_LOG_TAG, "setAutofill(): from " + mAutofillId + " to " + id);
         }
         if (isAttachedToWindow()) {
             throw new IllegalStateException("Cannot set autofill id when view is attached");
@@ -8424,6 +8435,11 @@
             final int parentImportance = ((View) parent).getImportantForAutofill();
             if (parentImportance == IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
                     || parentImportance == IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS) {
+                if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
+                    Log.v(AUTOFILL_LOG_TAG, "View (autofillId=" +  getAutofillViewId() + ", "
+                            + getClass() + ") is not important for autofill because parent "
+                            + parent + "'s importance is " + parentImportance);
+                }
                 return false;
             }
             parent = parent.getParent();
@@ -12947,7 +12963,7 @@
                 }
             }
         }
-        if (!TextUtils.isEmpty(getAccessibilityPaneTitle())) {
+        if (isAccessibilityPane()) {
             if (isVisible != oldVisible) {
                 notifyViewAccessibilityStateChangedIfNeeded(isVisible
                         ? AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED
@@ -18637,10 +18653,10 @@
 
                 if ((mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET) != 0) {
                     // Ignore when view already set it through setAutofillId();
-                    if (android.view.autofill.Helper.sDebug) {
-                        Log.d(VIEW_LOG_TAG, "onRestoreInstanceState(): not setting autofillId to "
-                                + baseState.mAutofillViewId + " because view explicitly set it to "
-                                + mAutofillId);
+                    if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.DEBUG)) {
+                        Log.d(AUTOFILL_LOG_TAG, "onRestoreInstanceState(): not setting autofillId "
+                                + "to " + baseState.mAutofillViewId + " because view explicitly set"
+                                + " it to " + mAutofillId);
                     }
                 } else {
                     mAutofillViewId = baseState.mAutofillViewId;
@@ -20426,6 +20442,7 @@
     private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) {
         if (renderNode == null) {
             renderNode = RenderNode.create(drawable.getClass().getName(), this);
+            renderNode.setUsageHint(RenderNode.USAGE_BACKGROUND);
         }
 
         final Rect bounds = drawable.getBounds();
@@ -23982,6 +23999,10 @@
             Log.w(VIEW_LOG_TAG, "startDragAndDrop called on a detached view.");
             return false;
         }
+        if (!mAttachInfo.mViewRootImpl.mSurface.isValid()) {
+            Log.w(VIEW_LOG_TAG, "startDragAndDrop called with an invalid surface.");
+            return false;
+        }
 
         if (data != null) {
             data.prepareToLeaveProcess((flags & View.DRAG_FLAG_GLOBAL) != 0);
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 276f50a..e4c595b 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -531,7 +531,6 @@
             throws IOException {
         RenderNode node = RenderNode.create("ViewDebug", null);
         profileViewAndChildren(view, node, out, true);
-        node.destroy();
     }
 
     private static void profileViewAndChildren(View view, RenderNode node, BufferedWriter out,
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 7944319..d0539ae 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4652,7 +4652,7 @@
      * which is added in order to fade it out in its old location should be removed
      * once the animation is complete.</p>
      *
-     * @param view The view to be added
+     * @param view The view to be added. The view must not have a parent.
      * @param index The index at which this view should be drawn, must be >= 0.
      * This value is relative to the {@link #getChildAt(int) index} values in the normal
      * child list of this container, where any transient view at a particular index will
@@ -4661,9 +4661,14 @@
      * @hide
      */
     public void addTransientView(View view, int index) {
-        if (index < 0) {
+        if (index < 0 || view == null) {
             return;
         }
+        if (view.mParent != null) {
+            throw new IllegalStateException("The specified view already has a parent "
+                    + view.mParent);
+        }
+
         if (mTransientIndices == null) {
             mTransientIndices = new ArrayList<Integer>();
             mTransientViews = new ArrayList<View>();
@@ -4683,7 +4688,9 @@
             mTransientViews.add(view);
         }
         view.mParent = this;
-        view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
+        if (mAttachInfo != null) {
+            view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags & VISIBILITY_MASK));
+        }
         invalidate(true);
     }
 
@@ -4705,7 +4712,9 @@
                 mTransientViews.remove(i);
                 mTransientIndices.remove(i);
                 view.mParent = null;
-                view.dispatchDetachedFromWindow();
+                if (view.mAttachInfo != null) {
+                    view.dispatchDetachedFromWindow();
+                }
                 invalidate(true);
                 return;
             }
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 6c84b63..e3e2069 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -109,11 +109,6 @@
     private ValueAnimator mTempValueAnimator;
 
     /**
-     * A RenderThread-driven backend that may intercept startAnimation
-     */
-    private ViewPropertyAnimatorRT mRTBackend;
-
-    /**
      * This listener is the mechanism by which the underlying Animator causes changes to the
      * properties currently being animated, as well as the cleanup after an animation is
      * complete.
@@ -434,9 +429,6 @@
         mPendingOnStartAction = null;
         mPendingOnEndAction = null;
         mView.removeCallbacks(mAnimationStarter);
-        if (mRTBackend != null) {
-            mRTBackend.cancelAll();
-        }
     }
 
     /**
@@ -859,9 +851,6 @@
      * value accordingly.
      */
     private void startAnimation() {
-        if (mRTBackend != null && mRTBackend.startAnimation(this)) {
-            return;
-        }
         mView.setHasTransientState(true);
         ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
         ArrayList<NameValuesHolder> nameValueList =
diff --git a/core/java/android/view/ViewPropertyAnimatorRT.java b/core/java/android/view/ViewPropertyAnimatorRT.java
deleted file mode 100644
index de96887..0000000
--- a/core/java/android/view/ViewPropertyAnimatorRT.java
+++ /dev/null
@@ -1,138 +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.
- */
-
-package android.view;
-
-import android.animation.TimeInterpolator;
-import android.view.ViewPropertyAnimator.NameValuesHolder;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-
-import com.android.internal.view.animation.FallbackLUTInterpolator;
-
-import java.util.ArrayList;
-
-
-/**
- * This is a RenderThread driven backend for ViewPropertyAnimator.
- */
-class ViewPropertyAnimatorRT {
-
-    private static final Interpolator sLinearInterpolator = new LinearInterpolator();
-
-    private final View mView;
-
-    private RenderNodeAnimator mAnimators[] = new RenderNodeAnimator[RenderNodeAnimator.LAST_VALUE + 1];
-
-    ViewPropertyAnimatorRT(View view) {
-        mView = view;
-    }
-
-    /**
-     * @return true if ViewPropertyAnimatorRT handled the animation,
-     *         false if ViewPropertyAnimator needs to handle it
-     */
-    public boolean startAnimation(ViewPropertyAnimator parent) {
-        cancelAnimators(parent.mPendingAnimations);
-        if (!canHandleAnimator(parent)) {
-            return false;
-        }
-        doStartAnimation(parent);
-        return true;
-    }
-
-    public void cancelAll() {
-        for (int i = 0; i < mAnimators.length; i++) {
-            if (mAnimators[i] != null) {
-                mAnimators[i].cancel();
-                mAnimators[i] = null;
-            }
-        }
-    }
-
-    private void doStartAnimation(ViewPropertyAnimator parent) {
-        int size = parent.mPendingAnimations.size();
-
-        long startDelay = parent.getStartDelay();
-        long duration = parent.getDuration();
-        TimeInterpolator interpolator = parent.getInterpolator();
-        if (interpolator == null) {
-            // Documented to be LinearInterpolator in ValueAnimator.setInterpolator
-            interpolator = sLinearInterpolator;
-        }
-        if (!RenderNodeAnimator.isNativeInterpolator(interpolator)) {
-            interpolator = new FallbackLUTInterpolator(interpolator, duration);
-        }
-        for (int i = 0; i < size; i++) {
-            NameValuesHolder holder = parent.mPendingAnimations.get(i);
-            int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
-
-            final float finalValue = holder.mFromValue + holder.mDeltaValue;
-            RenderNodeAnimator animator = new RenderNodeAnimator(property, finalValue);
-            animator.setStartDelay(startDelay);
-            animator.setDuration(duration);
-            animator.setInterpolator(interpolator);
-            animator.setTarget(mView);
-            animator.start();
-
-            mAnimators[property] = animator;
-        }
-
-        parent.mPendingAnimations.clear();
-    }
-
-    private boolean canHandleAnimator(ViewPropertyAnimator parent) {
-        // TODO: Can we eliminate this entirely?
-        // If RenderNode.animatorProperties() can be toggled to point at staging
-        // instead then RNA can be used as the animators for software as well
-        // as the updateListener fallback paths. If this can be toggled
-        // at the top level somehow, combined with requiresUiRedraw, we could
-        // ensure that RT does not self-animate, allowing for safe driving of
-        // the animators from the UI thread using the same mechanisms
-        // ViewPropertyAnimator does, just with everything sitting on a single
-        // animator subsystem instead of multiple.
-
-        if (parent.getUpdateListener() != null) {
-            return false;
-        }
-        if (parent.getListener() != null) {
-            // TODO support
-            return false;
-        }
-        if (!mView.isHardwareAccelerated()) {
-            // TODO handle this maybe?
-            return false;
-        }
-        if (parent.hasActions()) {
-            return false;
-        }
-        // Here goes nothing...
-        return true;
-    }
-
-    private void cancelAnimators(ArrayList<NameValuesHolder> mPendingAnimations) {
-        int size = mPendingAnimations.size();
-        for (int i = 0; i < size; i++) {
-            NameValuesHolder holder = mPendingAnimations.get(i);
-            int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
-            if (mAnimators[property] != null) {
-                mAnimators[property].cancel();
-                mAnimators[property] = null;
-            }
-        }
-    }
-
-}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index dd24185..97a2d79 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1338,6 +1338,7 @@
                 renderer.setStopped(mStopped);
             }
             if (!mStopped) {
+                mNewSurfaceNeeded = true;
                 scheduleTraversals();
             } else {
                 if (renderer != null) {
@@ -2100,7 +2101,7 @@
                                         & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
                                     // Don't pre-allocate if transparent regions
                                     // are requested as they may not be needed
-                                    mSurface.allocateBuffers();
+                                    mAttachInfo.mThreadedRenderer.allocateBuffers(mSurface);
                                 }
                             } catch (OutOfResourcesException e) {
                                 handleOutOfResourcesException(e);
@@ -3096,13 +3097,27 @@
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
 
         boolean usingAsyncReport = false;
-        if (mReportNextDraw && mAttachInfo.mThreadedRenderer != null
-                && mAttachInfo.mThreadedRenderer.isEnabled()) {
-            usingAsyncReport = true;
-            mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
-                // TODO: Use the frame number
-                pendingDrawFinished();
-            });
+        if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
+            ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
+                    .captureFrameCommitCallbacks();
+            if (mReportNextDraw) {
+                usingAsyncReport = true;
+                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
+                    // TODO: Use the frame number
+                    pendingDrawFinished();
+                    if (commitCallbacks != null) {
+                        for (int i = 0; i < commitCallbacks.size(); i++) {
+                            commitCallbacks.get(i).run();
+                        }
+                    }
+                });
+            } else if (commitCallbacks != null && commitCallbacks.size() > 0) {
+                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
+                    for (int i = 0; i < commitCallbacks.size(); i++) {
+                        commitCallbacks.get(i).run();
+                    }
+                });
+            }
         }
 
         try {
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 0973d0a..efc1807 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -16,6 +16,9 @@
 
 package android.view;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -56,6 +59,9 @@
     private ArrayList<OnDrawListener> mOnDrawListeners;
     private static boolean sIllegalOnDrawModificationIsFatal;
 
+    // These listeners are one-shot
+    private ArrayList<Runnable> mOnFrameCommitListeners;
+
     /** Remains false until #dispatchOnWindowShown() is called. If a listener registers after
      * that the listener will be immediately called. */
     private boolean mWindowShown;
@@ -393,6 +399,14 @@
             }
         }
 
+        if (observer.mOnFrameCommitListeners != null) {
+            if (mOnFrameCommitListeners != null) {
+                mOnFrameCommitListeners.addAll(observer.captureFrameCommitCallbacks());
+            } else {
+                mOnFrameCommitListeners = observer.captureFrameCommitCallbacks();
+            }
+        }
+
         if (observer.mOnTouchModeChangeListeners != null) {
             if (mOnTouchModeChangeListeners != null) {
                 mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
@@ -713,6 +727,49 @@
     }
 
     /**
+     * Adds a frame commit callback. This callback will be invoked when the current rendering
+     * content has been rendered into a frame and submitted to the swap chain. The frame may
+     * not currently be visible on the display when this is invoked, but it has been submitted.
+     * This callback is useful in combination with {@link PixelCopy} to capture the current
+     * rendered content of the UI reliably.
+     *
+     * Note: Only works with hardware rendering. Does nothing otherwise.
+     *
+     * @param callback The callback to invoke when the frame is committed.
+     */
+    @TestApi
+    public void registerFrameCommitCallback(@NonNull Runnable callback) {
+        checkIsAlive();
+        if (mOnFrameCommitListeners == null) {
+            mOnFrameCommitListeners = new ArrayList<>();
+        }
+        mOnFrameCommitListeners.add(callback);
+    }
+
+    @Nullable ArrayList<Runnable> captureFrameCommitCallbacks() {
+        ArrayList<Runnable> ret = mOnFrameCommitListeners;
+        mOnFrameCommitListeners = null;
+        return ret;
+    }
+
+    /**
+     * Attempts to remove the given callback from the list of pending frame complete callbacks.
+     *
+     * @param callback The callback to remove
+     * @return Whether or not the callback was removed. If this returns true the callback will
+     *         not be invoked. If false is returned then the callback was either never added
+     *         or may already be pending execution and was unable to be removed
+     */
+    @TestApi
+    public boolean unregisterFrameCommitCallback(@NonNull Runnable callback) {
+        checkIsAlive();
+        if (mOnFrameCommitListeners == null) {
+            return false;
+        }
+        return mOnFrameCommitListeners.remove(callback);
+    }
+
+    /**
      * Register a callback to be invoked when a view has been scrolled.
      *
      * @param listener The callback to add
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 78e4204..b9f9e5e 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -854,7 +854,7 @@
     }
 
     /**
-     * Set an observer to collect frame stats for each frame rendererd in this window.
+     * Set an observer to collect frame stats for each frame rendered in this window.
      *
      * Must be in hardware rendering mode.
      */
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 4d7b73c..8c7ac73 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1692,6 +1692,15 @@
         public static final int PRIVATE_FLAG_IS_SCREEN_DECOR = 0x00400000;
 
         /**
+         * Flag to indicate that the status bar window is now in an explicit expanded state, meaning
+         * that status bar will not be hidden by any window with flag {@link #FLAG_FULLSCREEN} or
+         * {@link View#SYSTEM_UI_FLAG_FULLSCREEN} set.
+         * This can only be set by {@link LayoutParams#TYPE_STATUS_BAR}.
+         * @hide
+         */
+        public static final int PRIVATE_FLAG_STATUS_BAR_EXPANDED = 0x00800000;
+
+        /**
          * Control flags that are private to the platform.
          * @hide
          */
@@ -1779,7 +1788,11 @@
                 @ViewDebug.FlagToString(
                         mask = PRIVATE_FLAG_IS_SCREEN_DECOR,
                         equals = PRIVATE_FLAG_IS_SCREEN_DECOR,
-                        name = "IS_SCREEN_DECOR")
+                        name = "IS_SCREEN_DECOR"),
+                @ViewDebug.FlagToString(
+                        mask = PRIVATE_FLAG_STATUS_BAR_EXPANDED,
+                        equals = PRIVATE_FLAG_STATUS_BAR_EXPANDED,
+                        name = "STATUS_BAR_EXPANDED")
         })
         @TestApi
         public int privateFlags;
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index da5a1cd..b4d9c53 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -336,7 +336,13 @@
             AccessibilityNodeInfo clone = AccessibilityNodeInfo.obtain(info);
             nodes.put(sourceId, clone);
             if (clone.isAccessibilityFocused()) {
+                if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
+                        && mAccessibilityFocus != sourceId) {
+                    refreshCachedNodeLocked(windowId, mAccessibilityFocus);
+                }
                 mAccessibilityFocus = sourceId;
+            } else if (mAccessibilityFocus == sourceId) {
+                mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
             }
             if (clone.isFocused()) {
                 mInputFocus = sourceId;
@@ -418,20 +424,28 @@
      *
      * @param nodes The nodes in the hosting window.
      * @param rootNodeId The id of the root to evict.
+     *
+     * @return {@code true} if the cache was cleared
      */
-    private void clearSubTreeRecursiveLocked(LongSparseArray<AccessibilityNodeInfo> nodes,
+    private boolean clearSubTreeRecursiveLocked(LongSparseArray<AccessibilityNodeInfo> nodes,
             long rootNodeId) {
         AccessibilityNodeInfo current = nodes.get(rootNodeId);
         if (current == null) {
-            return;
+            // The node isn't in the cache, but its descendents might be.
+            clear();
+            return true;
         }
         nodes.remove(rootNodeId);
         final int childCount = current.getChildCount();
         for (int i = 0; i < childCount; i++) {
             final long childNodeId = current.getChildId(i);
-            clearSubTreeRecursiveLocked(nodes, childNodeId);
+            if (clearSubTreeRecursiveLocked(nodes, childNodeId)) {
+                current.recycle();
+                return true;
+            }
         }
         current.recycle();
+        return false;
     }
 
     /**
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index e8e6537..f2429bd 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -601,22 +601,14 @@
 
     private static final int BOOLEAN_PROPERTY_CHECKED = 0x00000002;
 
-    private static final int BOOLEAN_PROPERTY_FOCUSABLE = 0x00000004;
-
     private static final int BOOLEAN_PROPERTY_FOCUSED = 0x00000008;
 
     private static final int BOOLEAN_PROPERTY_SELECTED = 0x00000010;
 
-    private static final int BOOLEAN_PROPERTY_CLICKABLE = 0x00000020;
-
-    private static final int BOOLEAN_PROPERTY_LONG_CLICKABLE = 0x00000040;
-
     private static final int BOOLEAN_PROPERTY_ENABLED = 0x00000080;
 
     private static final int BOOLEAN_PROPERTY_PASSWORD = 0x00000100;
 
-    private static final int BOOLEAN_PROPERTY_SCROLLABLE = 0x00000200;
-
     private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
 
     private static final int BOOLEAN_PROPERTY_VISIBLE_TO_USER = 0x00000800;
@@ -631,8 +623,6 @@
 
     private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 0x00010000;
 
-    private static final int BOOLEAN_PROPERTY_CONTEXT_CLICKABLE = 0x00020000;
-
     private static final int BOOLEAN_PROPERTY_IMPORTANCE = 0x0040000;
 
     private static final int BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE = 0x0080000;
@@ -1168,6 +1158,10 @@
         mActions.add(action);
     }
 
+    private boolean hasActionWithId(int actionId) {
+        return getActionList().stream().anyMatch(action -> action.getId() == actionId);
+    }
+
     /**
      * Adds an action that can be performed on the node.
      * <p>
@@ -1767,7 +1761,7 @@
      * @return True if the node is focusable.
      */
     public boolean isFocusable() {
-        return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE);
+        return hasActionWithId(ACTION_FOCUS);
     }
 
     /**
@@ -1781,10 +1775,11 @@
      * @param focusable True if the node is focusable.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     * @deprecated Use {@link #addAction(AccessibilityAction)}
+     * with {@link AccessibilityAction#ACTION_FOCUS}
      */
-    public void setFocusable(boolean focusable) {
-        setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable);
-    }
+    @Deprecated
+    public void setFocusable(boolean focusable) { }
 
     /**
      * Gets whether this node is focused.
@@ -1892,7 +1887,7 @@
      * @return True if the node is clickable.
      */
     public boolean isClickable() {
-        return getBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE);
+        return hasActionWithId(ACTION_CLICK);
     }
 
     /**
@@ -1906,10 +1901,11 @@
      * @param clickable True if the node is clickable.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     * @deprecated Use {@link #addAction(AccessibilityAction)}
+     * with {@link AccessibilityAction#ACTION_CLICK}
      */
-    public void setClickable(boolean clickable) {
-        setBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE, clickable);
-    }
+    @Deprecated
+    public void setClickable(boolean clickable) { }
 
     /**
      * Gets whether this node is long clickable.
@@ -1917,7 +1913,7 @@
      * @return True if the node is long clickable.
      */
     public boolean isLongClickable() {
-        return getBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE);
+        return hasActionWithId(ACTION_LONG_CLICK);
     }
 
     /**
@@ -1931,10 +1927,11 @@
      * @param longClickable True if the node is long clickable.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     * @deprecated Use {@link #addAction(AccessibilityAction)}
+     * with {@link AccessibilityAction#ACTION_LONG_CLICK}
      */
-    public void setLongClickable(boolean longClickable) {
-        setBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE, longClickable);
-    }
+    @Deprecated
+    public void setLongClickable(boolean longClickable) { }
 
     /**
      * Gets whether this node is enabled.
@@ -1992,7 +1989,13 @@
      * @return True if the node is scrollable, false otherwise.
      */
     public boolean isScrollable() {
-        return getBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE);
+        return hasActionWithId(ACTION_SCROLL_BACKWARD)
+                || hasActionWithId(ACTION_SCROLL_FORWARD)
+                || hasActionWithId(R.id.accessibilityActionScrollToPosition)
+                || hasActionWithId(R.id.accessibilityActionScrollUp)
+                || hasActionWithId(R.id.accessibilityActionScrollDown)
+                || hasActionWithId(R.id.accessibilityActionScrollLeft)
+                || hasActionWithId(R.id.accessibilityActionScrollRight);
     }
 
     /**
@@ -2006,9 +2009,11 @@
      * @param scrollable True if the node is scrollable, false otherwise.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     * @deprecated Use {@link #addAction(AccessibilityAction)}
      */
+    @Deprecated
+
     public void setScrollable(boolean scrollable) {
-        setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable);
     }
 
     /**
@@ -2199,7 +2204,7 @@
      * @return True if the node is context clickable.
      */
     public boolean isContextClickable() {
-        return getBooleanProperty(BOOLEAN_PROPERTY_CONTEXT_CLICKABLE);
+        return hasActionWithId(R.id.accessibilityActionContextClick);
     }
 
     /**
@@ -2212,10 +2217,11 @@
      *
      * @param contextClickable True if the node is context clickable.
      * @throws IllegalStateException If called from an AccessibilityService.
+     * @deprecated Use {@link #addAction(AccessibilityAction)}
+     * with {@link AccessibilityAction#ACTION_CONTEXT_CLICK}
      */
-    public void setContextClickable(boolean contextClickable) {
-        setBooleanProperty(BOOLEAN_PROPERTY_CONTEXT_CLICKABLE, contextClickable);
-    }
+    @Deprecated
+    public void setContextClickable(boolean contextClickable) { }
 
     /**
      * Gets the node's live region mode.
@@ -2309,7 +2315,7 @@
      * @return If the node can be dismissed.
      */
     public boolean isDismissable() {
-        return getBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE);
+        return hasActionWithId(ACTION_DISMISS);
     }
 
     /**
@@ -2321,10 +2327,11 @@
      * </p>
      *
      * @param dismissable If the node can be dismissed.
+     * @deprecated Use {@link #addAction(AccessibilityAction)}
+     * with {@link AccessibilityAction#ACTION_DISMISS}
      */
-    public void setDismissable(boolean dismissable) {
-        setBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE, dismissable);
-    }
+    @Deprecated
+    public void setDismissable(boolean dismissable) { }
 
     /**
      * Returns whether the node originates from a view considered important for accessibility.
@@ -3915,7 +3922,8 @@
      * </li>
      * <li><strong>Overriden standard actions</strong> - These are actions that override
      * standard actions to customize them. For example, an app may add a label to the
-     * standard {@link #ACTION_CLICK} action to announce that this action clears browsing history.
+     * standard {@link #ACTION_CLICK} action to indicate to the user that this action clears
+     * browsing history.
      * </ul>
      * </p>
      * <p>
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 64686dd..87b7b05 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -19,6 +19,7 @@
 import android.annotation.AnimRes;
 import android.annotation.ColorInt;
 import android.annotation.InterpolatorRes;
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.RectF;
@@ -183,6 +184,7 @@
     /**
      * The animation listener to be notified when the animation starts, ends or repeats.
      */
+    @UnsupportedAppUsage
     AnimationListener mListener;
 
     /**
@@ -211,9 +213,13 @@
     private boolean mMore = true;
     private boolean mOneMoreTime = true;
 
+    @UnsupportedAppUsage
     RectF mPreviousRegion = new RectF();
+    @UnsupportedAppUsage
     RectF mRegion = new RectF();
+    @UnsupportedAppUsage
     Transformation mTransformation = new Transformation();
+    @UnsupportedAppUsage
     Transformation mPreviousTransformation = new Transformation();
 
     private final CloseGuard guard = CloseGuard.get();
@@ -322,6 +328,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void detach() {
         if (mStarted && !mEnded) {
             mEnded = true;
@@ -1046,6 +1053,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void getInvalidateRegion(int left, int top, int right, int bottom,
             RectF invalidate, Transformation transformation) {
 
@@ -1077,6 +1085,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void initializeInvalidateRegion(int left, int top, int right, int bottom) {
         final RectF region = mPreviousRegion;
         region.set(left, top, right, bottom);
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 29f8442..c877b9c 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -19,6 +19,7 @@
 import android.annotation.AnimRes;
 import android.annotation.InterpolatorRes;
 import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
@@ -157,6 +158,7 @@
         return createAnimationFromXml(c, parser, null, Xml.asAttributeSet(parser));
     }
 
+    @UnsupportedAppUsage
     private static Animation createAnimationFromXml(Context c, XmlPullParser parser,
             AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException {
 
diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java
index 8eb5b5c..58da04d 100644
--- a/core/java/android/view/animation/Transformation.java
+++ b/core/java/android/view/animation/Transformation.java
@@ -17,6 +17,7 @@
 package android.view.animation;
 
 import android.annotation.FloatRange;
+import android.annotation.UnsupportedAppUsage;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 
@@ -238,6 +239,7 @@
      * Print short string, to optimize dumping.
      * @hide
      */
+    @UnsupportedAppUsage
     public void printShortString(PrintWriter pw) {
         pw.print("{alpha="); pw.print(mAlpha);
         pw.print(" matrix=");
diff --git a/core/java/android/view/animation/TranslateAnimation.java b/core/java/android/view/animation/TranslateAnimation.java
index 216022b..6c040d4 100644
--- a/core/java/android/view/animation/TranslateAnimation.java
+++ b/core/java/android/view/animation/TranslateAnimation.java
@@ -16,6 +16,7 @@
 
 package android.view.animation;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
@@ -34,13 +35,17 @@
     private int mToYType = ABSOLUTE;
 
     /** @hide */
+    @UnsupportedAppUsage
     protected float mFromXValue = 0.0f;
     /** @hide */
+    @UnsupportedAppUsage
     protected float mToXValue = 0.0f;
 
     /** @hide */
+    @UnsupportedAppUsage
     protected float mFromYValue = 0.0f;
     /** @hide */
+    @UnsupportedAppUsage
     protected float mToYValue = 0.0f;
 
     /** @hide */
diff --git a/core/java/android/view/animation/TranslateYAnimation.java b/core/java/android/view/animation/TranslateYAnimation.java
index 714558d..a6e0ccb 100644
--- a/core/java/android/view/animation/TranslateYAnimation.java
+++ b/core/java/android/view/animation/TranslateYAnimation.java
@@ -16,6 +16,7 @@
 
 package android.view.animation;
 
+import android.annotation.UnsupportedAppUsage;
 import android.graphics.Matrix;
 
 /**
@@ -38,6 +39,7 @@
     /**
      * Constructor. Passes in 0 for the x parameters of TranslateAnimation
      */
+    @UnsupportedAppUsage
     public TranslateYAnimation(int fromYType, float fromYValue, int toYType, float toYValue) {
         super(ABSOLUTE, 0, ABSOLUTE, 0, fromYType, fromYValue, toYType, toYValue);
     }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 1258a9e..32b2f63 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -34,6 +34,7 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.metrics.LogMaker;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Parcelable;
@@ -57,6 +58,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
@@ -72,6 +74,8 @@
 import java.util.Collections;
 import java.util.List;
 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;
@@ -199,10 +203,17 @@
     /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
     /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
 
-
+    /** @hide */ public static final int NO_LOGGING = 0;
     /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
     /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
     /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
+    /** @hide */
+    public static final int DEFAULT_LOGGING_LEVEL = Build.IS_DEBUGGABLE
+            ? AutofillManager.FLAG_ADD_CLIENT_DEBUG
+            : AutofillManager.NO_LOGGING;
+
+    /** @hide */
+    public static final int DEFAULT_MAX_PARTITIONS_SIZE = 10;
 
     /** Which bits in an authentication id are used for the dataset id */
     private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
@@ -572,10 +583,11 @@
 
                 final AutofillClient client = getClient();
                 if (client != null) {
+                    final SyncResultReceiver receiver = new SyncResultReceiver();
                     try {
-                        final boolean sessionWasRestored = mService.restoreSession(mSessionId,
-                                client.autofillClientGetActivityToken(),
-                                mServiceClient.asBinder());
+                        mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
+                                mServiceClient.asBinder(), receiver);
+                        final boolean sessionWasRestored = receiver.getIntResult() == 1;
 
                         if (!sessionWasRestored) {
                             Log.w(TAG, "Session " + mSessionId + " could not be restored");
@@ -691,7 +703,9 @@
      */
     @Nullable public FillEventHistory getFillEventHistory() {
         try {
-            return mService.getFillEventHistory();
+            final SyncResultReceiver receiver = new SyncResultReceiver();
+            mService.getFillEventHistory(receiver);
+            return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
             return null;
@@ -1242,8 +1256,10 @@
     public boolean hasEnabledAutofillServices() {
         if (mService == null) return false;
 
+        final SyncResultReceiver receiver = new SyncResultReceiver();
         try {
-            return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
+            mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(), receiver);
+            return receiver.getIntResult() == 1;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1257,8 +1273,10 @@
     public ComponentName getAutofillServiceComponentName() {
         if (mService == null) return null;
 
+        final SyncResultReceiver receiver = new SyncResultReceiver();
         try {
-            return mService.getAutofillServiceComponentName();
+            mService.getAutofillServiceComponentName(receiver);
+            return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1281,7 +1299,9 @@
      */
     @Nullable public String getUserDataId() {
         try {
-            return mService.getUserDataId();
+            final SyncResultReceiver receiver = new SyncResultReceiver();
+            mService.getUserDataId(receiver);
+            return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
             return null;
@@ -1301,7 +1321,9 @@
      */
     @Nullable public UserData getUserData() {
         try {
-            return mService.getUserData();
+            final SyncResultReceiver receiver = new SyncResultReceiver();
+            mService.getUserData(receiver);
+            return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
             return null;
@@ -1337,8 +1359,10 @@
      * the user.
      */
     public boolean isFieldClassificationEnabled() {
+        final SyncResultReceiver receiver = new SyncResultReceiver();
         try {
-            return mService.isFieldClassificationEnabled();
+            mService.isFieldClassificationEnabled(receiver);
+            return receiver.getIntResult() == 1;
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
             return false;
@@ -1358,8 +1382,10 @@
      */
     @Nullable
     public String getDefaultFieldClassificationAlgorithm() {
+        final SyncResultReceiver receiver = new SyncResultReceiver();
         try {
-            return mService.getDefaultFieldClassificationAlgorithm();
+            mService.getDefaultFieldClassificationAlgorithm(receiver);
+            return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
             return null;
@@ -1376,9 +1402,11 @@
      */
     @NonNull
     public List<String> getAvailableFieldClassificationAlgorithms() {
-        final String[] algorithms;
+        final SyncResultReceiver receiver = new SyncResultReceiver();
         try {
-            algorithms = mService.getAvailableFieldClassificationAlgorithms();
+            mService.getAvailableFieldClassificationAlgorithms(receiver);
+            final String[] algorithms = receiver
+                    .getObjectResult(SyncResultReceiver.TYPE_STRING_ARRAY);
             return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
@@ -1399,8 +1427,10 @@
     public boolean isAutofillSupported() {
         if (mService == null) return false;
 
+        final SyncResultReceiver receiver = new SyncResultReceiver();
         try {
-            return mService.isServiceSupported(mContext.getUserId());
+            mService.isServiceSupported(mContext.getUserId(), receiver);
+            return receiver.getIntResult() == 1;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1435,7 +1465,9 @@
         // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
         // service set the extra and returned RESULT_CANCELED...
 
-        if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
+        if (sDebug) {
+            Log.d(TAG, "onAuthenticationResult(): id= " + authenticationId + ", data=" + data);
+        }
 
         synchronized (mLock) {
             if (!isActiveLocked()) {
@@ -1519,10 +1551,12 @@
             final AutofillClient client = getClient();
             if (client == null) return; // NOTE: getClient() already logged it..
 
-            mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
+            final SyncResultReceiver receiver = new SyncResultReceiver();
+            mService.startSession(client.autofillClientGetActivityToken(),
                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
                     mCallback != null, flags, client.autofillClientGetComponentName(),
-                    isCompatibilityModeEnabledLocked());
+                    isCompatibilityModeEnabledLocked(), receiver);
+            mSessionId = receiver.getIntResult();
             if (mSessionId != NO_SESSION) {
                 mState = STATE_ACTIVE;
             }
@@ -1600,7 +1634,9 @@
             mServiceClient = new AutofillManagerClient(this);
             try {
                 final int userId = mContext.getUserId();
-                final int flags = mService.addClient(mServiceClient, userId);
+                final SyncResultReceiver receiver = new SyncResultReceiver();
+                mService.addClient(mServiceClient, userId, receiver);
+                final int flags = receiver.getIntResult();
                 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
                 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
                 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
@@ -1718,7 +1754,6 @@
             return;
         }
 
-        AutofillCallback callback = null;
         synchronized (mLock) {
             if (mSessionId == sessionId) {
                 AutofillClient client = getClient();
@@ -1803,7 +1838,6 @@
             for (int i = 0; i < itemCount; i++) {
                 final AutofillId id = ids.get(i);
                 final AutofillValue value = values.get(i);
-                final int viewId = id.getViewId();
                 final View view = views[i];
                 if (view == null) {
                     // Most likely view has been removed after the initial request was sent to the
@@ -1923,7 +1957,7 @@
                         mFillableIds.add(id);
                     }
                     if (sVerbose) {
-                        Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
+                        Log.v(TAG, "setTrackedViews(): fillableIds=" + Arrays.toString(fillableIds)
                                 + ", mFillableIds" + mFillableIds);
                     }
                 }
@@ -2124,6 +2158,7 @@
         pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
         pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
         pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
+        pw.print(pfx); pw.print("id of last fill UI shown: "); pw.println(mIdShownFillUi);
         pw.print(pfx); pw.print("tracked views: ");
         if (mTrackedViews == null) {
             pw.println("null");
@@ -2817,4 +2852,104 @@
             }
         }
     }
+
+    /**
+     * @hide
+     */
+    public static final class SyncResultReceiver extends IResultReceiver.Stub {
+
+        private static final String EXTRA = "EXTRA";
+
+        /**
+         * How long to block waiting for {@link IResultReceiver} callbacks when calling server.
+         */
+        private static final long BINDER_TIMEOUT_MS = 5000;
+
+        private static final int TYPE_STRING = 0;
+        private static final int TYPE_STRING_ARRAY = 1;
+        private static final int TYPE_PARCELABLE = 2;
+
+        private final CountDownLatch mLatch  = new CountDownLatch(1);
+        private int mResult;
+        private Bundle mBundle;
+
+        private void waitResult() {
+            try {
+                if (!mLatch.await(BINDER_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                    throw new IllegalStateException("Not called in " + BINDER_TIMEOUT_MS + "ms");
+                }
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+            }
+        }
+
+        /**
+         * Gets the result from an operation that returns an {@code int}.
+         */
+        int getIntResult() {
+            waitResult();
+            return mResult;
+        }
+
+        /**
+         * Gets the result from an operation that returns an {@code Object}.
+         *
+         * @param type type of expected object.
+         */
+        @Nullable
+        @SuppressWarnings("unchecked")
+        <T> T getObjectResult(int type) {
+            waitResult();
+            if (mBundle == null) {
+                return null;
+            }
+            switch (type) {
+                case TYPE_STRING:
+                    return (T) mBundle.getString(EXTRA);
+                case TYPE_STRING_ARRAY:
+                    return (T) mBundle.getStringArray(EXTRA);
+                case TYPE_PARCELABLE:
+                    return (T) mBundle.getParcelable(EXTRA);
+                default:
+                    throw new IllegalArgumentException("unsupported type: " + type);
+            }
+        }
+
+        @Override
+        public void send(int resultCode, Bundle resultData) {
+            mResult = resultCode;
+            mBundle = resultData;
+            mLatch.countDown();
+        }
+
+        /**
+         * Creates a bundle for a {@code String} value.
+         */
+        @NonNull
+        public static Bundle bundleFor(@Nullable String value) {
+            final Bundle bundle = new Bundle();
+            bundle.putString(EXTRA, value);
+            return bundle;
+        }
+
+        /**
+         * Creates a bundle for a {@code String[]} value.
+         */
+        @NonNull
+        public static Bundle bundleFor(@Nullable String[] value) {
+            final Bundle bundle = new Bundle();
+            bundle.putStringArray(EXTRA, value);
+            return bundle;
+        }
+
+        /**
+         * Creates a bundle for a {@code Parcelable} value.
+         */
+        @NonNull
+        public static Bundle bundleFor(@Nullable Parcelable value) {
+            final Bundle bundle = new Bundle();
+            bundle.putParcelable(EXTRA, value);
+            return bundle;
+        }
+    }
 }
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 738ff4d..26aeba5 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -28,37 +28,39 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutoFillManagerClient;
+import com.android.internal.os.IResultReceiver;
 
 /**
  * Mediator between apps being auto-filled and auto-fill service implementations.
  *
  * {@hide}
  */
-interface IAutoFillManager {
+oneway interface IAutoFillManager {
     // Returns flags: FLAG_ADD_CLIENT_ENABLED | FLAG_ADD_CLIENT_DEBUG | FLAG_ADD_CLIENT_VERBOSE
-    int addClient(in IAutoFillManagerClient client, int userId);
+    void addClient(in IAutoFillManagerClient client, int userId, in IResultReceiver result);
     void removeClient(in IAutoFillManagerClient client, int userId);
-    int startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId,
-            in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags,
-            in ComponentName componentName, boolean compatMode);
-    FillEventHistory getFillEventHistory();
-    boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback);
+    void startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId,
+        in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags,
+        in ComponentName componentName, boolean compatMode, in IResultReceiver result);
+    void getFillEventHistory(in IResultReceiver result);
+    void restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback,
+        in IResultReceiver result);
     void updateSession(int sessionId, in AutofillId id, in Rect bounds,
-            in AutofillValue value, int action, int flags, int userId);
+        in AutofillValue value, int action, int flags, int userId);
     void setAutofillFailure(int sessionId, in List<AutofillId> ids, int userId);
     void finishSession(int sessionId, int userId);
     void cancelSession(int sessionId, int userId);
     void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId);
     void setHasCallback(int sessionId, int userId, boolean hasIt);
     void disableOwnedAutofillServices(int userId);
-    boolean isServiceSupported(int userId);
-    boolean isServiceEnabled(int userId, String packageName);
+    void isServiceSupported(int userId, in IResultReceiver result);
+    void isServiceEnabled(int userId, String packageName, in IResultReceiver result);
     void onPendingSaveUi(int operation, IBinder token);
-    UserData getUserData();
-    String getUserDataId();
+    void getUserData(in IResultReceiver result);
+    void getUserDataId(in IResultReceiver result);
     void setUserData(in UserData userData);
-    boolean isFieldClassificationEnabled();
-    ComponentName getAutofillServiceComponentName();
-    String[] getAvailableFieldClassificationAlgorithms();
-    String getDefaultFieldClassificationAlgorithm();
+    void isFieldClassificationEnabled(in IResultReceiver result);
+    void getAutofillServiceComponentName(in IResultReceiver result);
+    void getAvailableFieldClassificationAlgorithms(in IResultReceiver result);
+    void getDefaultFieldClassificationAlgorithm(in IResultReceiver result);
 }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 7e6af49..bb93af5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -798,10 +798,18 @@
     }
 
     /** @hide */
-    public void setImeWindowStatus(IBinder imeToken, IBinder startInputToken, int vis,
-            int backDisposition) {
+    public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) {
         try {
-            mService.setImeWindowStatus(imeToken, startInputToken, vis, backDisposition);
+            mService.setImeWindowStatus(imeToken, vis, backDisposition);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public void reportStartInput(IBinder imeToken, IBinder startInputToken) {
+        try {
+            mService.reportStartInput(imeToken, startInputToken);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1239,7 +1247,7 @@
     }
 
     boolean startInputInner(@InputMethodClient.StartInputReason final int startInputReason,
-            IBinder windowGainingFocus, int controlFlags, int softInputMode,
+            @Nullable IBinder windowGainingFocus, int controlFlags, int softInputMode,
             int windowFlags) {
         final View view;
         synchronized (mH) {
@@ -1256,6 +1264,20 @@
             }
         }
 
+        if (windowGainingFocus == null) {
+            windowGainingFocus = view.getWindowToken();
+            if (windowGainingFocus == null) {
+                Log.e(TAG, "ABORT input: ServedView must be attached to a Window");
+                return false;
+            }
+            controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
+            if (view.onCheckIsTextEditor()) {
+                controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
+            }
+            softInputMode = view.getViewRootImpl().mWindowAttributes.softInputMode;
+            windowFlags = view.getViewRootImpl().mWindowAttributes.flags;
+        }
+
         // Now we need to get an input connection from the served view.
         // This is complicated in a couple ways: we can't be holding our lock
         // when calling out to the view, and we need to make sure we call into
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 10191e0..16eb5af 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -30,6 +30,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 
 import java.util.concurrent.CountDownLatch;
@@ -162,6 +163,17 @@
         }
     }
 
+    @Override
+    public void dump(@NonNull IndentingPrintWriter printWriter) {
+        printWriter.println("SystemTextClassifier:");
+        printWriter.increaseIndent();
+        printWriter.printPair("mFallback", mFallback);
+        printWriter.printPair("mPackageName", mPackageName);
+        printWriter.printPair("mSessionId", mSessionId);
+        printWriter.decreaseIndent();
+        printWriter.println();
+    }
+
     /**
      * Attempts to initialize a new classification session.
      *
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 21b5603..2fc7422 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -20,6 +20,8 @@
 import android.util.KeyValueListParser;
 import android.util.Slog;
 
+import com.android.internal.util.IndentingPrintWriter;
+
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -241,4 +243,25 @@
     private static List<String> parseEntityList(String listStr) {
         return Collections.unmodifiableList(Arrays.asList(listStr.split(ENTITY_LIST_DELIMITER)));
     }
+
+    void dump(IndentingPrintWriter pw) {
+        pw.println("TextClassificationConstants:");
+        pw.increaseIndent();
+        pw.printPair("isLocalTextClassifierEnabled", mLocalTextClassifierEnabled);
+        pw.printPair("isSystemTextClassifierEnabled", mSystemTextClassifierEnabled);
+        pw.printPair("isModelDarkLaunchEnabled", mModelDarkLaunchEnabled);
+        pw.printPair("isSmartSelectionEnabled", mSmartSelectionEnabled);
+        pw.printPair("isSmartTextShareEnabled", mSmartTextShareEnabled);
+        pw.printPair("isSmartLinkifyEnabled", mSmartLinkifyEnabled);
+        pw.printPair("isSmartSelectionAnimationEnabled", mSmartSelectionAnimationEnabled);
+        pw.printPair("getSuggestSelectionMaxRangeLength", mSuggestSelectionMaxRangeLength);
+        pw.printPair("getClassifyTextMaxRangeLength", mClassifyTextMaxRangeLength);
+        pw.printPair("getGenerateLinksMaxTextLength", mGenerateLinksMaxTextLength);
+        pw.printPair("getGenerateLinksLogSampleRate", mGenerateLinksLogSampleRate);
+        pw.printPair("getEntityListDefault", mEntityListDefault);
+        pw.printPair("getEntityListNotEditable", mEntityListNotEditable);
+        pw.printPair("getEntityListEditable", mEntityListEditable);
+        pw.decreaseIndent();
+        pw.println();
+    }
 }
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index dc1194b..201218ba 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -27,6 +27,7 @@
 import android.view.textclassifier.TextClassifier.TextClassifierType;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 
 import java.lang.ref.WeakReference;
@@ -246,6 +247,13 @@
                 : mContext;
     }
 
+    /** @hide **/
+    public void dump(IndentingPrintWriter pw) {
+        getLocalTextClassifier().dump(pw);
+        getSystemTextClassifier().dump(pw);
+        getSettings().dump(pw);
+    }
+
     /** @hide */
     public static TextClassificationConstants getSettings(Context context) {
         Preconditions.checkNotNull(context);
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 24f531d..1505863 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -34,6 +34,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -390,6 +391,11 @@
         return false;
     }
 
+    /** @hide **/
+    default void dump(@NonNull IndentingPrintWriter printWriter) {
+
+    }
+
     /**
      * Configuration object for specifying what entities to identify.
      *
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 6e5751a..29339e9 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -41,6 +41,7 @@
 import android.provider.ContactsContract;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 
 import java.io.File;
@@ -439,6 +440,24 @@
         return builder.setId(createId(text, start, end)).build();
     }
 
+    @Override
+    public void dump(@NonNull IndentingPrintWriter printWriter) {
+        synchronized (mLock) {
+            listAllModelsLocked();
+            printWriter.println("TextClassifierImpl:");
+            printWriter.increaseIndent();
+            printWriter.println("Model file(s):");
+            printWriter.increaseIndent();
+            for (ModelFile modelFile : mAllModelFiles) {
+                printWriter.println(modelFile.toString());
+            }
+            printWriter.decreaseIndent();
+            printWriter.printPair("mFallback", mFallback);
+            printWriter.decreaseIndent();
+            printWriter.println();
+        }
+    }
+
     /**
      * Closes the ParcelFileDescriptor and logs any errors that occur.
      */
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 21ec42b..6644a85 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -222,8 +222,7 @@
     public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
             boolean allowImplicitlySelectedSubtype) {
         try {
-            // Passing null as a locale until we support multiple enabled spell checker subtypes.
-            return mService.getCurrentSpellCheckerSubtype(null, allowImplicitlySelectedSubtype);
+            return mService.getCurrentSpellCheckerSubtype(allowImplicitlySelectedSubtype);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/webkit/WebBackForwardList.java b/core/java/android/webkit/WebBackForwardList.java
index 0c34e3c..4d3bbe4 100644
--- a/core/java/android/webkit/WebBackForwardList.java
+++ b/core/java/android/webkit/WebBackForwardList.java
@@ -57,7 +57,8 @@
     /**
      * Clone the entire object to be used in the UI thread by clients of
      * WebView. This creates a copy that should never be modified by any of the
-     * webkit package classes.
+     * webkit package classes. On Android 4.4 and later there is no need to use
+     * this, as the object is already a read-only copy of the internal state.
      */
     protected abstract WebBackForwardList clone();
 }
diff --git a/core/java/android/webkit/WebHistoryItem.java b/core/java/android/webkit/WebHistoryItem.java
index 74db039..b9e7042 100644
--- a/core/java/android/webkit/WebHistoryItem.java
+++ b/core/java/android/webkit/WebHistoryItem.java
@@ -23,7 +23,7 @@
 /**
  * A convenience class for accessing fields in an entry in the back/forward list
  * of a WebView. Each WebHistoryItem is a snapshot of the requested history
- * item. Each history item may be updated during the load of a page.
+ * item.
  * @see WebBackForwardList
  */
 public abstract class WebHistoryItem implements Cloneable {
@@ -44,8 +44,6 @@
      * history item. See getTargetUrl() for the url that is the actual target of
      * this history item.
      * @return The base url of this history item.
-     * Note: The VM ensures 32-bit atomic read/write operations so we don't have
-     * to synchronize this method.
      */
     public abstract String getUrl();
 
@@ -60,22 +58,20 @@
     /**
      * Return the document title of this history item.
      * @return The document title of this history item.
-     * Note: The VM ensures 32-bit atomic read/write operations so we don't have
-     * to synchronize this method.
      */
     public abstract String getTitle();
 
     /**
      * Return the favicon of this history item or {@code null} if no favicon was found.
      * @return A Bitmap containing the favicon for this history item or {@code null}.
-     * Note: The VM ensures 32-bit atomic read/write operations so we don't have
-     * to synchronize this method.
      */
     @Nullable
     public abstract Bitmap getFavicon();
 
     /**
-     * Clone the history item for use by clients of WebView.
+     * Clone the history item for use by clients of WebView. On Android 4.4 and later
+     * there is no need to use this, as the object is already a read-only copy of the
+     * internal state.
      */
     protected abstract WebHistoryItem clone();
 }
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index cba11a8..a085395 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -987,7 +987,9 @@
      * {@link PluginState#OFF}.
      *
      * @param state a PluginState value
-     * @deprecated Plugins will not be supported in future, and should not be used.
+     * @deprecated Plugins are not supported in API level
+     *             {@link android.os.Build.VERSION_CODES#KITKAT} or later;
+     *             enabling plugins is a no-op.
      */
     @Deprecated
     public abstract void setPluginState(PluginState state);
@@ -1182,7 +1184,9 @@
      *
      * @return the plugin state as a {@link PluginState} value
      * @see #setPluginState
-     * @deprecated Plugins will not be supported in future, and should not be used.
+     * @deprecated Plugins are not supported in API level
+     *             {@link android.os.Build.VERSION_CODES#KITKAT} or later;
+     *             enabling plugins is a no-op.
      */
     @Deprecated
     public abstract PluginState getPluginState();
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4eb85ac..9573f48 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -41,7 +41,6 @@
 import android.os.RemoteException;
 import android.os.StrictMode;
 import android.print.PrintDocumentAdapter;
-import android.security.KeyChain;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
@@ -71,280 +70,25 @@
 import java.util.Map;
 
 /**
- * <p>A View that displays web pages. This class is the basis upon which you
- * can roll your own web browser or simply display some online content within your Activity.
- * It uses the WebKit rendering engine to display
- * web pages and includes methods to navigate forward and backward
- * through a history, zoom in and out, perform text searches and more.
- *
- * <p>Note that, in order for your Activity to access the Internet and load web pages
- * in a WebView, you must add the {@code INTERNET} permissions to your
- * Android Manifest file:
- *
- * <pre>
- * {@code <uses-permission android:name="android.permission.INTERNET" />}
- * </pre>
- *
- * <p>This must be a child of the <a
- * href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code <manifest>}</a>
- * element.
- *
- * <p>For more information, read
- * <a href="{@docRoot}guide/webapps/webview.html">Building Web Apps in WebView</a>.
+ * A View that displays web pages.
  *
  * <h3>Basic usage</h3>
  *
- * <p>By default, a WebView provides no browser-like widgets, does not
- * enable JavaScript and web page errors are ignored. If your goal is only
- * to display some HTML as a part of your UI, this is probably fine;
- * the user won't need to interact with the web page beyond reading
- * it, and the web page won't need to interact with the user. If you
- * actually want a full-blown web browser, then you probably want to
- * invoke the Browser application with a URL Intent rather than show it
- * with a WebView. For example:
- * <pre>
- * Uri uri = Uri.parse("https://www.example.com");
- * Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- * startActivity(intent);
- * </pre>
- * <p>See {@link android.content.Intent} for more information.
  *
- * <p>To provide a WebView in your own Activity, include a {@code <WebView>} in your layout,
- * or set the entire Activity window as a WebView during {@link
- * android.app.Activity#onCreate(Bundle) onCreate()}:
+ * <p>In most cases, we recommend using a standard web browser, like Chrome, to deliver
+ * content to the user. To learn more about web browsers, read the guide on
+ * <a href="/guide/components/intents-common#Browser">
+ * invoking a browser with an intent</a>.
  *
- * <pre class="prettyprint">
- * WebView webview = new WebView(this);
- * setContentView(webview);
- * </pre>
+ * <p>WebView objects allow you to display web content as part of your activity layout, but
+ * lack some of the features of fully-developed browsers. A WebView is useful when
+ * you need increased control over the UI and advanced configuration options that will allow
+ * you to embed web pages in a specially-designed environment for your app.
  *
- * <p>Then load the desired web page:
- *
- * <pre>
- * // Simplest usage: note that an exception will NOT be thrown
- * // if there is an error loading this page (see below).
- * webview.loadUrl("https://example.com/");
- *
- * // OR, you can also load from an HTML string:
- * String summary = "&lt;html>&lt;body>You scored &lt;b>192&lt;/b> points.&lt;/body>&lt;/html>";
- * webview.loadData(summary, "text/html", null);
- * // ... although note that there are restrictions on what this HTML can do.
- * // See {@link #loadData(String,String,String)} and {@link
- * #loadDataWithBaseURL(String,String,String,String,String)} for more info.
- * // Also see {@link #loadData(String,String,String)} for information on encoding special
- * // characters.
- * </pre>
- *
- * <p>A WebView has several customization points where you can add your
- * own behavior. These are:
- *
- * <ul>
- *   <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
- *       This class is called when something that might impact a
- *       browser UI happens, for instance, progress updates and
- *       JavaScript alerts are sent here (see <a
- * href="{@docRoot}guide/developing/debug-tasks.html#DebuggingWebPages">Debugging Tasks</a>).
- *   </li>
- *   <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
- *       It will be called when things happen that impact the
- *       rendering of the content, eg, errors or form submissions. You
- *       can also intercept URL loading here (via {@link
- * android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)
- * shouldOverrideUrlLoading()}).</li>
- *   <li>Modifying the {@link android.webkit.WebSettings}, such as
- * enabling JavaScript with {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean)
- * setJavaScriptEnabled()}. </li>
- *   <li>Injecting Java objects into the WebView using the
- *       {@link android.webkit.WebView#addJavascriptInterface} method. This
- *       method allows you to inject Java objects into a page's JavaScript
- *       context, so that they can be accessed by JavaScript in the page.</li>
- * </ul>
- *
- * <p>Here's a more complicated example, showing error handling,
- *    settings, and progress notification:
- *
- * <pre class="prettyprint">
- * // Let's display the progress in the activity title bar, like the
- * // browser app does.
- * getWindow().requestFeature(Window.FEATURE_PROGRESS);
- *
- * webview.getSettings().setJavaScriptEnabled(true);
- *
- * final Activity activity = this;
- * webview.setWebChromeClient(new WebChromeClient() {
- *   public void onProgressChanged(WebView view, int progress) {
- *     // Activities and WebViews measure progress with different scales.
- *     // The progress meter will automatically disappear when we reach 100%
- *     activity.setProgress(progress * 1000);
- *   }
- * });
- * webview.setWebViewClient(new WebViewClient() {
- *   public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
- *     Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
- *   }
- * });
- *
- * webview.loadUrl("https://developer.android.com/");
- * </pre>
- *
- * <h3>Zoom</h3>
- *
- * <p>To enable the built-in zoom, set
- * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
- * (introduced in API level {@link android.os.Build.VERSION_CODES#CUPCAKE}).
- *
- * <p class="note"><b>Note:</b> Using zoom if either the height or width is set to
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} may lead to undefined behavior
- * and should be avoided.
- *
- * <h3>Cookie and window management</h3>
- *
- * <p>For obvious security reasons, your application has its own
- * cache, cookie store etc.&mdash;it does not share the Browser
- * application's data.
- *
- * <p>By default, requests by the HTML to open new windows are
- * ignored. This is {@code true} whether they be opened by JavaScript or by
- * the target attribute on a link. You can customize your
- * {@link WebChromeClient} to provide your own behavior for opening multiple windows,
- * and render them in whatever manner you want.
- *
- * <p>The standard behavior for an Activity is to be destroyed and
- * recreated when the device orientation or any other configuration changes. This will cause
- * the WebView to reload the current page. If you don't want that, you
- * can set your Activity to handle the {@code orientation} and {@code keyboardHidden}
- * changes, and then just leave the WebView alone. It'll automatically
- * re-orient itself as appropriate. Read <a
- * href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a> for
- * more information about how to handle configuration changes during runtime.
- *
- *
- * <h3>Building web pages to support different screen densities</h3>
- *
- * <p>The screen density of a device is based on the screen resolution. A screen with low density
- * has fewer available pixels per inch, where a screen with high density
- * has more &mdash; sometimes significantly more &mdash; pixels per inch. The density of a
- * screen is important because, other things being equal, a UI element (such as a button) whose
- * height and width are defined in terms of screen pixels will appear larger on the lower density
- * screen and smaller on the higher density screen.
- * For simplicity, Android collapses all actual screen densities into three generalized densities:
- * high, medium, and low.
- * <p>By default, WebView scales a web page so that it is drawn at a size that matches the default
- * appearance on a medium density screen. So, it applies 1.5x scaling on a high density screen
- * (because its pixels are smaller) and 0.75x scaling on a low density screen (because its pixels
- * are bigger).
- * Starting with API level {@link android.os.Build.VERSION_CODES#ECLAIR}, WebView supports DOM, CSS,
- * and meta tag features to help you (as a web developer) target screens with different screen
- * densities.
- * <p>Here's a summary of the features you can use to handle different screen densities:
- * <ul>
- * <li>The {@code window.devicePixelRatio} DOM property. The value of this property specifies the
- * default scaling factor used for the current device. For example, if the value of {@code
- * window.devicePixelRatio} is "1.0", then the device is considered a medium density (mdpi) device
- * and default scaling is not applied to the web page; if the value is "1.5", then the device is
- * considered a high density device (hdpi) and the page content is scaled 1.5x; if the
- * value is "0.75", then the device is considered a low density device (ldpi) and the content is
- * scaled 0.75x.</li>
- * <li>The {@code -webkit-device-pixel-ratio} CSS media query. Use this to specify the screen
- * densities for which this style sheet is to be used. The corresponding value should be either
- * "0.75", "1", or "1.5", to indicate that the styles are for devices with low density, medium
- * density, or high density screens, respectively. For example:
- * <pre>
- * &lt;link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio:1.5)" href="hdpi.css" /&gt;</pre>
- * <p>The {@code hdpi.css} stylesheet is only used for devices with a screen pixel ratio of 1.5,
- * which is the high density pixel ratio.
- * </li>
- * </ul>
- *
- * <h3>HTML5 Video support</h3>
- *
- * <p>In order to support inline HTML5 video in your application you need to have hardware
- * acceleration turned on.
- *
- * <h3>Full screen support</h3>
- *
- * <p>In order to support full screen &mdash; for video or other HTML content &mdash; you need to set a
- * {@link android.webkit.WebChromeClient} and implement both
- * {@link WebChromeClient#onShowCustomView(View, WebChromeClient.CustomViewCallback)}
- * and {@link WebChromeClient#onHideCustomView()}. If the implementation of either of these two methods is
- * missing then the web contents will not be allowed to enter full screen. Optionally you can implement
- * {@link WebChromeClient#getVideoLoadingProgressView()} to customize the View displayed whilst a video
- * is loading.
- *
- * <h3>HTML5 Geolocation API support</h3>
- *
- * <p>For applications targeting Android N and later releases
- * (API level > {@link android.os.Build.VERSION_CODES#M}) the geolocation api is only supported on
- * secure origins such as https. For such applications requests to geolocation api on non-secure
- * origins are automatically denied without invoking the corresponding
- * {@link WebChromeClient#onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback)}
- * method.
- *
- * <h3>Layout size</h3>
- * <p>
- * It is recommended to set the WebView layout height to a fixed value or to
- * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} instead of using
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}.
- * When using {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
- * for the height none of the WebView's parents should use a
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} layout height since that could result in
- * incorrect sizing of the views.
- *
- * <p>Setting the WebView's height to {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
- * enables the following behaviors:
- * <ul>
- * <li>The HTML body layout height is set to a fixed value. This means that elements with a height
- * relative to the HTML body may not be sized correctly. </li>
- * <li>For applications targeting {@link android.os.Build.VERSION_CODES#KITKAT} and earlier SDKs the
- * HTML viewport meta tag will be ignored in order to preserve backwards compatibility. </li>
- * </ul>
- *
- * <p>
- * Using a layout width of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} is not
- * supported. If such a width is used the WebView will attempt to use the width of the parent
- * instead.
- *
- * <h3>Metrics</h3>
- *
- * <p>
- * WebView may upload anonymous diagnostic data to Google when the user has consented. This data
- * helps Google improve WebView. Data is collected on a per-app basis for each app which has
- * instantiated a WebView. An individual app can opt out of this feature by putting the following
- * tag in its manifest's {@code <application>} element:
- * <pre>
- * &lt;manifest&gt;
- *     &lt;application&gt;
- *         ...
- *         &lt;meta-data android:name=&quot;android.webkit.WebView.MetricsOptOut&quot;
- *             android:value=&quot;true&quot; /&gt;
- *     &lt;/application&gt;
- * &lt;/manifest&gt;
- * </pre>
- * <p>
- * Data will only be uploaded for a given app if the user has consented AND the app has not opted
- * out.
- *
- * <h3>Safe Browsing</h3>
- *
- * <p>
- * With Safe Browsing, WebView will block malicious URLs and present a warning UI to the user to
- * allow them to navigate back safely or proceed to the malicious page.
- * <p>
- * Safe Browsing is enabled by default on devices which support it. If your app needs to disable
- * Safe Browsing for all WebViews, it can do so in the manifest's {@code <application>} element:
- * <p>
- * <pre>
- * &lt;manifest&gt;
- *     &lt;application&gt;
- *         ...
- *         &lt;meta-data android:name=&quot;android.webkit.WebView.EnableSafeBrowsing&quot;
- *             android:value=&quot;false&quot; /&gt;
- *     &lt;/application&gt;
- * &lt;/manifest&gt;
- * </pre>
- *
- * <p>
- * Otherwise, see {@link WebSettings#setSafeBrowsingEnabled}.
+ * <p>To learn more about WebView and alternatives for serving web content, read the
+ * documentation on
+ * <a href="/guide/webapps/">
+ * Web-based content</a>.
  *
  */
 // Implementation notes.
@@ -1671,9 +1415,8 @@
     /**
      * Clears the client certificate preferences stored in response
      * to proceeding/cancelling client cert requests. Note that WebView
-     * automatically clears these preferences when it receives a
-     * {@link KeyChain#ACTION_STORAGE_CHANGED} intent. The preferences are
-     * shared by all the WebViews that are created by the embedder application.
+     * automatically clears these preferences when the system keychain is updated.
+     * The preferences are shared by all the WebViews that are created by the embedder application.
      *
      * @param onCleared  A runnable to be invoked when client certs are cleared.
      *                   The runnable will be called in UI thread.
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index f686b66..bdd7a09 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -31,18 +31,25 @@
 public class WebViewClient {
 
     /**
-     * Give the host application a chance to take over the control when a new
-     * url is about to be loaded in the current WebView. If WebViewClient is not
-     * provided, by default WebView will ask Activity Manager to choose the
-     * proper handler for the url. If WebViewClient is provided, return {@code true}
-     * means the host application handles the url, while return {@code false} means the
-     * current WebView handles the url.
-     * This method is not called for requests using the POST "method".
+     * Give the host application a chance to take control when a URL is about to be loaded in the
+     * current WebView. If a WebViewClient is not provided, by default WebView will ask Activity
+     * Manager to choose the proper handler for the URL. If a WebViewClient is provided, returning
+     * {@code true} causes the current WebView to abort loading the URL, while returning
+     * {@code false} causes the WebView to continue loading the URL as usual.
+     *
+     * <p class="note"><b>Note:</b> Do not call {@link WebView#loadUrl(String)} with the same
+     * URL and then return {@code true}. This unnecessarily cancels the current load and starts a
+     * new load with the same URL. The correct way to continue loading a given URL is to simply
+     * return {@code false}, without calling {@link WebView#loadUrl(String)}.
+     *
+     * <p class="note"><b>Note:</b> This method is not called for POST requests.
+     *
+     * <p class="note"><b>Note:</b> This method may be called for subframes and with non-HTTP(S)
+     * schemes; calling {@link WebView#loadUrl(String)} with such a URL will fail.
      *
      * @param view The WebView that is initiating the callback.
-     * @param url The url to be loaded.
-     * @return {@code true} if the host application wants to leave the current WebView
-     *         and handle the url itself, otherwise return {@code false}.
+     * @param url The URL to be loaded.
+     * @return {@code true} to cancel the current load, otherwise return {@code false}.
      * @deprecated Use {@link #shouldOverrideUrlLoading(WebView, WebResourceRequest)
      *             shouldOverrideUrlLoading(WebView, WebResourceRequest)} instead.
      */
@@ -52,26 +59,25 @@
     }
 
     /**
-     * Give the host application a chance to take over the control when a new
-     * url is about to be loaded in the current WebView. If WebViewClient is not
-     * provided, by default WebView will ask Activity Manager to choose the
-     * proper handler for the url. If WebViewClient is provided, return {@code true}
-     * means the host application handles the url, while return {@code false} means the
-     * current WebView handles the url.
+     * Give the host application a chance to take control when a URL is about to be loaded in the
+     * current WebView. If a WebViewClient is not provided, by default WebView will ask Activity
+     * Manager to choose the proper handler for the URL. If a WebViewClient is provided, returning
+     * {@code true} causes the current WebView to abort loading the URL, while returning
+     * {@code false} causes the WebView to continue loading the URL as usual.
      *
-     * <p>Notes:
-     * <ul>
-     * <li>This method is not called for requests using the POST &quot;method&quot;.</li>
-     * <li>This method is also called for subframes with non-http schemes, thus it is
-     * strongly disadvised to unconditionally call {@link WebView#loadUrl(String)}
-     * with the request's url from inside the method and then return {@code true},
-     * as this will make WebView to attempt loading a non-http url, and thus fail.</li>
-     * </ul>
+     * <p class="note"><b>Note:</b> Do not call {@link WebView#loadUrl(String)} with the request's
+     * URL and then return {@code true}. This unnecessarily cancels the current load and starts a
+     * new load with the same URL. The correct way to continue loading a given URL is to simply
+     * return {@code false}, without calling {@link WebView#loadUrl(String)}.
+     *
+     * <p class="note"><b>Note:</b> This method is not called for POST requests.
+     *
+     * <p class="note"><b>Note:</b> This method may be called for subframes and with non-HTTP(S)
+     * schemes; calling {@link WebView#loadUrl(String)} with such a URL will fail.
      *
      * @param view The WebView that is initiating the callback.
      * @param request Object containing the details of the request.
-     * @return {@code true} if the host application wants to leave the current WebView
-     *         and handle the url itself, otherwise return {@code false}.
+     * @return {@code true} to cancel the current load, otherwise return {@code false}.
      */
     public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
         return shouldOverrideUrlLoading(view, request.getUrl().toString());
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 97b62a8..ec5fdc9 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -4337,13 +4337,14 @@
                 translateX = 0;
                 translateY = 0;
             }
+            mEdgeGlowTop.setSize(width, height);
+            mEdgeGlowBottom.setSize(width, height);
             if (!mEdgeGlowTop.isFinished()) {
                 final int restoreCount = canvas.save();
                 canvas.clipRect(translateX, translateY,
                          translateX + width ,translateY + mEdgeGlowTop.getMaxHeight());
                 final int edgeY = Math.min(0, scrollY + mFirstPositionDistanceGuess) + translateY;
                 canvas.translate(translateX, edgeY);
-                mEdgeGlowTop.setSize(width, height);
                 if (mEdgeGlowTop.draw(canvas)) {
                     invalidateTopGlow();
                 }
@@ -4358,7 +4359,6 @@
                         - (clipToPadding ? mPaddingBottom : 0);
                 canvas.translate(edgeX, edgeY);
                 canvas.rotate(180, width, 0);
-                mEdgeGlowBottom.setSize(width, height);
                 if (mEdgeGlowBottom.draw(canvas)) {
                     invalidateBottomGlow();
                 }
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 6c19256..4e77f0b 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -293,7 +293,7 @@
 
     /**
      * @return The callback to be invoked with an item in this AdapterView has
-     *         been clicked, or null id no callback has been set.
+     *         been clicked, or null if no callback has been set.
      */
     @Nullable
     public final OnItemClickListener getOnItemClickListener() {
@@ -365,7 +365,7 @@
 
     /**
      * @return The callback to be invoked with an item in this AdapterView has
-     *         been clicked and held, or null id no callback as been set.
+     *         been clicked and held, or null if no callback has been set.
      */
     public final OnItemLongClickListener getOnItemLongClickListener() {
         return mOnItemLongClickListener;
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 6df76fa..e6f948f 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -21,6 +21,7 @@
 import android.content.DialogInterface;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PermissionGroupInfo;
@@ -590,7 +591,8 @@
     private void addPermToList(List<MyPermissionInfo> permList,
             MyPermissionInfo pInfo) {
         if (pInfo.mLabel == null) {
-            pInfo.mLabel = pInfo.loadLabel(mPm);
+            pInfo.mLabel = pInfo.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+                    | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
         }
         int idx = Collections.binarySearch(permList, pInfo, mPermComparator);
         if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+permList.size());
@@ -611,7 +613,9 @@
                 }
                 MyPermissionGroupInfo group = mPermGroups.get(pInfo.group);
                 if (group != null) {
-                    pInfo.mLabel = pInfo.loadLabel(mPm);
+                    pInfo.mLabel = pInfo.loadSafeLabel(mPm, 20000,
+                            PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+                            | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
                     addPermToList(group.mAllPermissions, pInfo);
                     if (pInfo.mNew) {
                         addPermToList(group.mNewPermissions, pInfo);
@@ -622,14 +626,18 @@
 
         for (MyPermissionGroupInfo pgrp : mPermGroups.values()) {
             if (pgrp.labelRes != 0 || pgrp.nonLocalizedLabel != null) {
-                pgrp.mLabel = pgrp.loadLabel(mPm);
+                pgrp.mLabel = pgrp.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+                        | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
             } else {
                 ApplicationInfo app;
                 try {
                     app = mPm.getApplicationInfo(pgrp.packageName, 0);
-                    pgrp.mLabel = app.loadLabel(mPm);
+                    pgrp.mLabel = app.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+                            | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
                 } catch (NameNotFoundException e) {
-                    pgrp.mLabel = pgrp.loadLabel(mPm);
+                    pgrp.mLabel = pgrp.loadSafeLabel(mPm, 20000,
+                            PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+                            | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
                 }
             }
             mPermGroupsList.add(pgrp);
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
index 621a745..2b1e900 100644
--- a/core/java/android/widget/DateTimeView.java
+++ b/core/java/android/widget/DateTimeView.java
@@ -30,7 +30,6 @@
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
-import android.icu.util.Calendar;
 import android.os.Handler;
 import android.text.format.Time;
 import android.util.AttributeSet;
@@ -41,6 +40,7 @@
 
 import java.text.DateFormat;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Date;
 import java.util.TimeZone;
 
@@ -301,7 +301,7 @@
      */
     private long computeNextMidnight(TimeZone timeZone) {
         Calendar c = Calendar.getInstance();
-        c.setTimeZone(libcore.icu.DateUtilsBridge.icuTimeZone(timeZone));
+        c.setTimeZone(timeZone);
         c.add(Calendar.DAY_OF_MONTH, 1);
         c.set(Calendar.HOUR_OF_DAY, 0);
         c.set(Calendar.MINUTE, 0);
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index b12e854..51e481d 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -80,7 +80,7 @@
  *
  * <p>
  * To learn more about Drawables, see: <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
- * To learn more about working with Bitmaps, see: <a href="{@docRoot}topic/performance/graphics/index.htm">Handling Bitmaps</a>.
+ * To learn more about working with Bitmaps, see: <a href="{@docRoot}topic/performance/graphics/index.html">Handling Bitmaps</a>.
  * </p>
  *
  * @attr ref android.R.styleable#ImageView_adjustViewBounds
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 929496f..11054c8 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -611,7 +611,6 @@
                 mRenderer.destroy();
                 mSurfaceControl.destroy();
                 mSurfaceSession.kill();
-                mBitmapRenderNode.destroy();
                 mHandler.removeCallbacks(mMagnifierUpdater);
                 if (mBitmap != null) {
                     mBitmap.recycle();
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 3d4f546..fc1172e 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -119,8 +119,9 @@
 
         mTextServicesManager = (TextServicesManager) mTextView.getContext().
                 getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
-        if (!mTextServicesManager.isSpellCheckerEnabled()
-                || mCurrentLocale == null
+        if (mCurrentLocale == null
+                || mTextView.length() == 0
+                || !mTextServicesManager.isSpellCheckerEnabled()
                 || mTextServicesManager.getCurrentSpellCheckerSubtype(true) == null) {
             mSpellCheckerSession = null;
         } else {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 4ea2ced..5cadbe4 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -247,6 +247,7 @@
  * @attr ref android.R.styleable#TextView_textColorHint
  * @attr ref android.R.styleable#TextView_textAppearance
  * @attr ref android.R.styleable#TextView_textColorLink
+ * @attr ref android.R.styleable#TextView_textFontWeight
  * @attr ref android.R.styleable#TextView_textSize
  * @attr ref android.R.styleable#TextView_textScaleX
  * @attr ref android.R.styleable#TextView_fontFamily
@@ -3450,7 +3451,7 @@
         ColorStateList mTextColor = null;
         ColorStateList mTextColorHint = null;
         ColorStateList mTextColorLink = null;
-        int mTextSize = 0;
+        int mTextSize = -1;
         String mFontFamily = null;
         Typeface mFontTypeface = null;
         boolean mFontFamilyExplicit = false;
@@ -3661,7 +3662,7 @@
             setHighlightColor(attributes.mTextColorHighlight);
         }
 
-        if (attributes.mTextSize != 0) {
+        if (attributes.mTextSize != -1) {
             setRawTextSize(attributes.mTextSize, true /* shouldRequestLayout */);
         }
 
@@ -4105,9 +4106,16 @@
      * TextView is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}, and the default value for
      * EditText is {@link Layout#BREAK_STRATEGY_SIMPLE}, the latter to avoid the
      * text "dancing" when being edited.
+     * <p/>
+     * Enabling hyphenation with either using {@link Layout#HYPHENATION_FREQUENCY_NORMAL} or
+     * {@link Layout#HYPHENATION_FREQUENCY_FULL} while line breaking is set to one of
+     * {@link Layout#BREAK_STRATEGY_BALANCED}, {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}
+     * improves the structure of text layout however has performance impact and requires more time
+     * to do the text layout.
      *
      * @attr ref android.R.styleable#TextView_breakStrategy
      * @see #getBreakStrategy()
+     * @see #setHyphenationFrequency(int)
      */
     public void setBreakStrategy(@Layout.BreakStrategy int breakStrategy) {
         mBreakStrategy = breakStrategy;
@@ -4133,12 +4141,26 @@
     /**
      * Sets the frequency of automatic hyphenation to use when determining word breaks.
      * The default value for both TextView and {@link EditText} is
-     * {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
-     * Note that the default hyphenation frequency value is set from the theme.
+     * {@link Layout#HYPHENATION_FREQUENCY_NONE}. Note that the default hyphenation frequency value
+     * is set from the theme.
+     * <p/>
+     * Enabling hyphenation with either using {@link Layout#HYPHENATION_FREQUENCY_NORMAL} or
+     * {@link Layout#HYPHENATION_FREQUENCY_FULL} while line breaking is set to one of
+     * {@link Layout#BREAK_STRATEGY_BALANCED}, {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}
+     * improves the structure of text layout however has performance impact and requires more time
+     * to do the text layout.
+     * <p/>
+     * Note: Before Android Q, in the theme hyphenation frequency is set to
+     * {@link Layout#HYPHENATION_FREQUENCY_NORMAL}. The default value is changed into
+     * {@link Layout#HYPHENATION_FREQUENCY_NONE} on Q.
      *
-     * @param hyphenationFrequency The hyphenation frequency to use.
+     * @param hyphenationFrequency the hyphenation frequency to use, one of
+     *                             {@link Layout#HYPHENATION_FREQUENCY_NONE},
+     *                             {@link Layout#HYPHENATION_FREQUENCY_NORMAL},
+     *                             {@link Layout#HYPHENATION_FREQUENCY_FULL}
      * @attr ref android.R.styleable#TextView_hyphenationFrequency
      * @see #getHyphenationFrequency()
+     * @see #getBreakStrategy()
      */
     public void setHyphenationFrequency(@Layout.HyphenationFrequency int hyphenationFrequency) {
         mHyphenationFrequency = hyphenationFrequency;
@@ -6972,7 +6994,8 @@
     public boolean hasOverlappingRendering() {
         // horizontal fading edge causes SaveLayerAlpha, which doesn't support alpha modulation
         return ((getBackground() != null && getBackground().getCurrent() != null)
-                || mSpannable != null || hasSelection() || isHorizontalFadingEdgeEnabled());
+                || mSpannable != null || hasSelection() || isHorizontalFadingEdgeEnabled()
+                || mShadowColor != 0);
     }
 
     /**
diff --git a/core/java/com/android/internal/annotations/GuardedBy.java b/core/java/com/android/internal/annotations/GuardedBy.java
index fc61945..0e63214 100644
--- a/core/java/com/android/internal/annotations/GuardedBy.java
+++ b/core/java/com/android/internal/annotations/GuardedBy.java
@@ -23,10 +23,10 @@
 
 /**
  * Annotation type used to mark a method or field that can only be accessed when
- * holding the referenced lock.
+ * holding the referenced locks.
  */
 @Target({ ElementType.FIELD, ElementType.METHOD })
 @Retention(RetentionPolicy.CLASS)
 public @interface GuardedBy {
-    String value();
+    String[] value();
 }
diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
index 4f41875..7735d84 100644
--- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
+++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
@@ -20,6 +20,7 @@
 
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
@@ -126,7 +127,7 @@
     private OnClickListener mSwitchOldListener = new OnClickListener() {
         public void onClick(View v) {
             try {
-                ActivityManager.getService().moveTaskToFront(mCurTask, 0, null);
+                ActivityTaskManager.getService().moveTaskToFront(mCurTask, 0, null);
             } catch (RemoteException e) {
             }
             finish();
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 0ed9724..768dddd 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -33,7 +33,7 @@
     void stopWatchingMode(IAppOpsCallback callback);
     IBinder getToken(IBinder clientToken);
     int permissionToOpCode(String permission);
-    int noteProxyOperation(int code, String proxyPackageName,
+    int noteProxyOperation(int code, int proxyUid, String proxyPackageName,
                 int callingUid, String callingPackageName);
 
     // Remaining methods are only used in Java.
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 398d087..ae9c5c4 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -16,14 +16,22 @@
 
 package com.android.internal.app;
 
+import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
+
+import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.admin.DevicePolicyManager;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -31,12 +39,11 @@
 import android.os.UserManager;
 import android.util.Slog;
 import android.widget.Toast;
-
 import com.android.internal.annotations.VisibleForTesting;
-
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
-
-import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
+import java.util.Set;
 
 /**
  * This is used in conjunction with
@@ -44,7 +51,6 @@
  * be passed in and out of a managed profile.
  */
 public class IntentForwarderActivity extends Activity  {
-
     public static String TAG = "IntentForwarderActivity";
 
     public static String FORWARD_INTENT_TO_PARENT
@@ -53,6 +59,9 @@
     public static String FORWARD_INTENT_TO_MANAGED_PROFILE
             = "com.android.internal.app.ForwardIntentToManagedProfile";
 
+    private static final Set<String> ALLOWED_TEXT_MESSAGE_SCHEME
+            = new HashSet<>(Arrays.asList("sms", "smsto", "mms", "mmsto"));
+
     private Injector mInjector;
 
     @Override
@@ -93,28 +102,17 @@
                 newIntent.prepareToLeaveUser(callingUserId);
             }
 
-            final android.content.pm.ResolveInfo ri =
-                    mInjector.getPackageManager().resolveActivityAsUser(
-                            newIntent,
-                            MATCH_DEFAULT_ONLY,
-                            targetUserId);
-
-            // Don't show the disclosure if next activity is ResolverActivity or ChooserActivity
-            // as those will already have shown work / personal as neccesary etc.
-            final boolean shouldShowDisclosure = ri == null || ri.activityInfo == null ||
-                    !"android".equals(ri.activityInfo.packageName) ||
-                    !(ResolverActivity.class.getName().equals(ri.activityInfo.name)
-                            || ChooserActivity.class.getName().equals(ri.activityInfo.name));
-
+            final ResolveInfo ri = mInjector.resolveActivityAsUser(newIntent, MATCH_DEFAULT_ONLY,
+                    targetUserId);
             try {
                 startActivityAsCaller(newIntent, null, false, targetUserId);
             } catch (RuntimeException e) {
                 int launchedFromUid = -1;
                 String launchedFromPackage = "?";
                 try {
-                    launchedFromUid = ActivityManager.getService().getLaunchedFromUid(
+                    launchedFromUid = ActivityTaskManager.getService().getLaunchedFromUid(
                             getActivityToken());
-                    launchedFromPackage = ActivityManager.getService().getLaunchedFromPackage(
+                    launchedFromPackage = ActivityTaskManager.getService().getLaunchedFromPackage(
                             getActivityToken());
                 } catch (RemoteException ignored) {
                 }
@@ -124,8 +122,8 @@
                         + ActivityThread.currentProcessName(), e);
             }
 
-            if (shouldShowDisclosure) {
-                Toast.makeText(this, getString(userMessageId), Toast.LENGTH_LONG).show();
+            if (shouldShowDisclosure(ri, intentReceived)) {
+                mInjector.showToast(userMessageId, Toast.LENGTH_LONG);
             }
         } else {
             Slog.wtf(TAG, "the intent: " + intentReceived + " cannot be forwarded from user "
@@ -134,6 +132,35 @@
         finish();
     }
 
+    private boolean shouldShowDisclosure(@Nullable ResolveInfo ri, Intent intent) {
+        if (ri == null || ri.activityInfo == null) {
+            return true;
+        }
+        if (ri.activityInfo.applicationInfo.isSystemApp()
+                && (isDialerIntent(intent) || isTextMessageIntent(intent))) {
+            return false;
+        }
+        return !isTargetResolverOrChooserActivity(ri.activityInfo);
+    }
+
+    private boolean isTextMessageIntent(Intent intent) {
+        return Intent.ACTION_SENDTO.equals(intent.getAction()) && intent.getData() != null
+            && ALLOWED_TEXT_MESSAGE_SCHEME.contains(intent.getData().getScheme());
+    }
+
+    private boolean isDialerIntent(Intent intent) {
+        return Intent.ACTION_DIAL.equals(intent.getAction())
+            || Intent.ACTION_CALL.equals(intent.getAction());
+    }
+
+    private boolean isTargetResolverOrChooserActivity(ActivityInfo activityInfo) {
+        if (!"android".equals(activityInfo.packageName)) {
+            return false;
+        }
+        return ResolverActivity.class.getName().equals(activityInfo.name)
+            || ChooserActivity.class.getName().equals(activityInfo.name);
+    }
+
     /**
      * Check whether the intent can be forwarded to target user. Return the intent used for
      * forwarding if it can be forwarded, {@code null} otherwise.
@@ -241,6 +268,16 @@
         public PackageManager getPackageManager() {
             return IntentForwarderActivity.this.getPackageManager();
         }
+
+        @Override
+        public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
+            return getPackageManager().resolveActivityAsUser(intent, flags, userId);
+        }
+
+        @Override
+        public void showToast(int messageId, int duration) {
+            Toast.makeText(IntentForwarderActivity.this, getString(messageId), duration).show();
+        }
     }
 
     public interface Injector {
@@ -249,5 +286,9 @@
         UserManager getUserManager();
 
         PackageManager getPackageManager();
+
+        ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId);
+
+        void showToast(@StringRes int messageId, int duration);
     }
 }
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index d247013..1d997f5 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -21,6 +21,7 @@
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 
+import java.io.Serializable;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.IllformedLocaleException;
@@ -31,7 +32,7 @@
     private static final HashMap<String, LocaleInfo> sLocaleCache = new HashMap<>();
     private static boolean sFullyInitialized = false;
 
-    public static class LocaleInfo {
+    public static class LocaleInfo implements Serializable {
         private static final int SUGGESTION_TYPE_NONE = 0;
         private static final int SUGGESTION_TYPE_SIM = 1 << 0;
         private static final int SUGGESTION_TYPE_CFG = 1 << 1;
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index f6a69d9..5778544 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -18,6 +18,9 @@
 
 import android.animation.TimeAnimator;
 import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.Intent;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
@@ -25,12 +28,15 @@
 import android.graphics.Path;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.provider.Settings;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
 import android.view.View;
 import android.widget.FrameLayout;
 
+import org.json.JSONObject;
+
 public class PlatLogoActivity extends Activity {
     FrameLayout layout;
     TimeAnimator anim;
@@ -87,7 +93,7 @@
             darkest = 0;
             for (int i=0; i<slots; i++) {
                 palette[i] = Color.HSVToColor(color);
-                color[0] += 360f/slots;
+                color[0] = (color[0] + 360f/slots) % 360f;
                 if (lum(palette[i]) < lum(palette[darkest])) darkest = i;
             }
 
@@ -178,27 +184,97 @@
         bg = new PBackground();
         layout.setBackground(bg);
 
+        final ContentResolver cr = getContentResolver();
+
         layout.setOnTouchListener(new View.OnTouchListener() {
+            final String TOUCH_STATS = "touch.stats";
+
             final PointerCoords pc0 = new PointerCoords();
             final PointerCoords pc1 = new PointerCoords();
 
+            double pressure_min, pressure_max;
+            int maxPointers;
+            int tapCount;
+
             @Override
             public boolean onTouch(View v, MotionEvent event) {
+                final float pressure = event.getPressure();
                 switch (event.getActionMasked()) {
                     case MotionEvent.ACTION_DOWN:
+                        pressure_min = pressure_max = pressure;
+                        // fall through
                     case MotionEvent.ACTION_MOVE:
-                        if (event.getPointerCount() > 1) {
+                        if (pressure < pressure_min) pressure_min = pressure;
+                        if (pressure > pressure_max) pressure_max = pressure;
+                        final int pc = event.getPointerCount();
+                        if (pc > maxPointers) maxPointers = pc;
+                        if (pc > 1) {
                             event.getPointerCoords(0, pc0);
                             event.getPointerCoords(1, pc1);
                             bg.setRadius((float) Math.hypot(pc0.x - pc1.x, pc0.y - pc1.y) / 2f);
                         }
                         break;
+                    case MotionEvent.ACTION_CANCEL:
+                    case MotionEvent.ACTION_UP:
+                        try {
+                            final String touchDataJson = Settings.System.getString(cr, TOUCH_STATS);
+                            final JSONObject touchData = new JSONObject(
+                                    touchDataJson != null ? touchDataJson : "{}");
+                            if (touchData.has("min")) {
+                                pressure_min = Math.min(pressure_min, touchData.getDouble("min"));
+                            }
+                            if (touchData.has("max")) {
+                                pressure_max = Math.max(pressure_max, touchData.getDouble("max"));
+                            }
+                            touchData.put("min", pressure_min);
+                            touchData.put("max", pressure_max);
+                            Settings.System.putString(cr, TOUCH_STATS, touchData.toString());
+                        } catch (Exception e) {
+                            Log.e("PlatLogoActivity", "Can't write touch settings", e);
+                        }
+
+                        if (maxPointers == 1) {
+                            tapCount ++;
+                            if (tapCount < 7) {
+                                bg.randomizePalette();
+                            } else {
+                                launchNextStage();
+                            }
+                        } else {
+                            tapCount = 0;
+                        }
+                        maxPointers = 0;
+                        break;
                 }
                 return true;
             }
         });
     }
 
+    private void launchNextStage() {
+        final ContentResolver cr = getContentResolver();
+
+        if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0) == 0) {
+            // For posterity: the moment this user unlocked the easter egg
+            try {
+                Settings.System.putLong(cr,
+                        Settings.System.EGG_MODE,
+                        System.currentTimeMillis());
+            } catch (RuntimeException e) {
+                Log.e("PlatLogoActivity", "Can't write settings", e);
+            }
+        }
+        try {
+            startActivity(new Intent(Intent.ACTION_MAIN)
+                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_CLEAR_TASK)
+                    .addCategory("com.android.internal.category.PLATLOGO"));
+        } catch (ActivityNotFoundException ex) {
+            Log.e("PlatLogoActivity", "No more eggs.");
+        }
+        finish();
+    }
+
     @Override
     public void onStart() {
         super.onStart();
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index ceb06f5..0a812c6 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -21,6 +21,7 @@
 import android.annotation.UiThread;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.ActivityThread;
 import android.app.VoiceInteractor.PickOptionRequest;
 import android.app.VoiceInteractor.PickOptionRequest.Option;
@@ -260,7 +261,7 @@
         setProfileSwitchMessageId(intent.getContentUserHint());
 
         try {
-            mLaunchedFromUid = ActivityManager.getService().getLaunchedFromUid(
+            mLaunchedFromUid = ActivityTaskManager.getService().getLaunchedFromUid(
                     getActivityToken());
         } catch (RemoteException e) {
             mLaunchedFromUid = -1;
@@ -846,7 +847,7 @@
         } catch (RuntimeException e) {
             String launchedFromPackage;
             try {
-                launchedFromPackage = ActivityManager.getService().getLaunchedFromPackage(
+                launchedFromPackage = ActivityTaskManager.getService().getLaunchedFromPackage(
                         getActivityToken());
             } catch (RemoteException e2) {
                 launchedFromPackage = "??";
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
new file mode 100644
index 0000000..f63c43f
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -0,0 +1,664 @@
+/*
+ * 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.app.procstats;
+
+
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Objects;
+
+public final class AssociationState {
+    private static final String TAG = "ProcessStats";
+    private static final boolean DEBUG = false;
+
+    private final ProcessStats mProcessStats;
+    private final ProcessStats.PackageState mPackageState;
+    private final String mProcessName;
+    private final String mName;
+
+    public final class SourceState {
+        final SourceKey mKey;
+        int mProcStateSeq = -1;
+        int mProcState = ProcessStats.STATE_NOTHING;
+        boolean mInTrackingList;
+        int mNesting;
+        int mCount;
+        long mStartUptime;
+        long mDuration;
+        long mTrackingUptime;
+        int mActiveCount;
+        int mActiveProcState = ProcessStats.STATE_NOTHING;
+        long mActiveStartUptime;
+        long mActiveDuration;
+        DurationsTable mDurations;
+
+        SourceState(SourceKey key) {
+            mKey = key;
+        }
+
+        public AssociationState getAssociationState() {
+            return AssociationState.this;
+        }
+
+        public String getProcessName() {
+            return mKey.mProcess;
+        }
+
+        public int getUid() {
+            return mKey.mUid;
+        }
+
+        public void trackProcState(int procState, int seq, long now) {
+            procState = ProcessState.PROCESS_STATE_TO_STATE[procState];
+            if (seq != mProcStateSeq) {
+                mProcStateSeq = seq;
+                mProcState = procState;
+            } else if (procState < mProcState) {
+                mProcState = procState;
+            }
+            if (procState < ProcessStats.STATE_HOME) {
+                // If the proc state has become better than cached, then we want to
+                // start tracking it to count when it is actually active.  If it drops
+                // down to cached, we will clean it up when we later evaluate all currently
+                // tracked associations in ProcessStats.updateTrackingAssociationsLocked().
+                if (!mInTrackingList) {
+                    mInTrackingList = true;
+                    mTrackingUptime = now;
+                    mProcessStats.mTrackingAssociations.add(this);
+                }
+            }
+        }
+
+        public void stop() {
+            mNesting--;
+            if (mNesting == 0) {
+                mDuration += SystemClock.uptimeMillis() - mStartUptime;
+                mNumActive--;
+                stopTracking(SystemClock.uptimeMillis());
+            }
+        }
+
+        void startActive(long now) {
+            if (mInTrackingList) {
+                if (mActiveStartUptime == 0) {
+                    mActiveStartUptime = now;
+                    mActiveCount++;
+                }
+                if (mActiveProcState != mProcState) {
+                    if (mActiveProcState != ProcessStats.STATE_NOTHING) {
+                        // Currently active proc state changed, need to store the duration
+                        // so far and switch tracking to the new proc state.
+                        final long duration = mActiveDuration + now - mActiveStartUptime;
+                        if (duration != 0) {
+                            if (mDurations == null) {
+                                makeDurations();
+                            }
+                            mDurations.addDuration(mActiveProcState, duration);
+                            mActiveDuration = 0;
+                        }
+                        mActiveStartUptime = now;
+                    }
+                    mActiveProcState = mProcState;
+                }
+            } else {
+                Slog.wtf(TAG, "startActive while not tracking: " + this);
+            }
+        }
+
+        void stopActive(long now) {
+            if (mActiveStartUptime != 0) {
+                if (!mInTrackingList) {
+                    Slog.wtf(TAG, "stopActive while not tracking: " + this);
+                }
+                final long duration = mActiveDuration + now - mActiveStartUptime;
+                if (mDurations != null) {
+                    mDurations.addDuration(mActiveProcState, duration);
+                } else {
+                    mActiveDuration = duration;
+                }
+                mActiveStartUptime = 0;
+            }
+        }
+
+        void makeDurations() {
+            mDurations = new DurationsTable(mProcessStats.mTableData);
+        }
+
+        void stopTracking(long now) {
+            stopActive(now);
+            if (mInTrackingList) {
+                mInTrackingList = false;
+                mProcState = ProcessStats.STATE_NOTHING;
+                // Do a manual search for where to remove, since these objects will typically
+                // be towards the end of the array.
+                final ArrayList<SourceState> list = mProcessStats.mTrackingAssociations;
+                for (int i = list.size() - 1; i >= 0; i--) {
+                    if (list.get(i) == this) {
+                        list.remove(i);
+                        return;
+                    }
+                }
+                Slog.wtf(TAG, "Stop tracking didn't find in tracking list: " + this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(64);
+            sb.append("SourceState{").append(Integer.toHexString(System.identityHashCode(this)))
+                    .append(" ").append(mKey.mProcess).append("/").append(mKey.mUid);
+            if (mProcState != ProcessStats.STATE_NOTHING) {
+                sb.append(" ").append(DumpUtils.STATE_NAMES[mProcState]).append(" #")
+                        .append(mProcStateSeq);
+            }
+            sb.append("}");
+            return sb.toString();
+        }
+    }
+
+    private final static class SourceKey {
+        /**
+         * UID, consider this final.  Not final just to avoid a temporary object during lookup.
+         */
+        int mUid;
+
+        /**
+         * Process name, consider this final.  Not final just to avoid a temporary object during
+         * lookup.
+         */
+        String mProcess;
+
+        SourceKey(int uid, String process) {
+            mUid = uid;
+            mProcess = process;
+        }
+
+        public boolean equals(Object o) {
+            if (!(o instanceof SourceKey)) {
+                return false;
+            }
+            SourceKey s = (SourceKey) o;
+            return s.mUid == mUid && Objects.equals(s.mProcess, mProcess);
+        }
+
+        @Override
+        public int hashCode() {
+            return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode());
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(64);
+            sb.append("SourceKey{");
+            UserHandle.formatUid(sb, mUid);
+            sb.append(' ');
+            sb.append(mProcess);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    /**
+     * All known sources for this target component...  uid -> process name -> source state.
+     */
+    private final ArrayMap<SourceKey, SourceState> mSources = new ArrayMap<>();
+
+    private final SourceKey mTmpSourceKey = new SourceKey(0, null);
+
+    private ProcessState mProc;
+
+    private int mNumActive;
+
+    public AssociationState(ProcessStats processStats, ProcessStats.PackageState packageState,
+            String name, String processName, ProcessState proc) {
+        mProcessStats = processStats;
+        mPackageState = packageState;
+        mName = name;
+        mProcessName = processName;
+        mProc = proc;
+    }
+
+    public int getUid() {
+        return mPackageState.mUid;
+    }
+
+    public String getPackage() {
+        return mPackageState.mPackageName;
+    }
+
+    public String getProcessName() {
+        return mProcessName;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public ProcessState getProcess() {
+        return mProc;
+    }
+
+    public void setProcess(ProcessState proc) {
+        mProc = proc;
+    }
+
+    public SourceState startSource(int uid, String processName) {
+        mTmpSourceKey.mUid = uid;
+        mTmpSourceKey.mProcess = processName;
+        SourceState src = mSources.get(mTmpSourceKey);
+        if (src == null) {
+            SourceKey key = new SourceKey(uid, processName);
+            src = new SourceState(key);
+            mSources.put(key, src);
+        }
+        src.mNesting++;
+        if (src.mNesting == 1) {
+            src.mCount++;
+            src.mStartUptime = SystemClock.uptimeMillis();
+            mNumActive++;
+        }
+        return src;
+    }
+
+    public void add(AssociationState other) {
+        for (int isrc = other.mSources.size() - 1; isrc >= 0; isrc--) {
+            final SourceKey key = other.mSources.keyAt(isrc);
+            final SourceState otherSrc = other.mSources.valueAt(isrc);
+            SourceState mySrc = mSources.get(key);
+            if (mySrc == null) {
+                mySrc = new SourceState(key);
+                mSources.put(key, mySrc);
+            }
+            mySrc.mCount += otherSrc.mCount;
+            mySrc.mDuration += otherSrc.mDuration;
+            mySrc.mActiveCount += otherSrc.mActiveCount;
+            if (otherSrc.mActiveDuration != 0 || otherSrc.mDurations != null) {
+                // Only need to do anything if the other one has some duration data.
+                if (mySrc.mDurations != null) {
+                    // If the target already has multiple durations, just add in whatever
+                    // we have in the other.
+                    if (otherSrc.mDurations != null) {
+                        mySrc.mDurations.addDurations(otherSrc.mDurations);
+                    } else {
+                        mySrc.mDurations.addDuration(otherSrc.mActiveProcState,
+                                otherSrc.mActiveDuration);
+                    }
+                } else if (otherSrc.mDurations != null) {
+                    // The other one has multiple durations, but we don't.  Expand to
+                    // multiple durations and copy over.
+                    mySrc.makeDurations();
+                    mySrc.mDurations.addDurations(otherSrc.mDurations);
+                    if (mySrc.mActiveDuration != 0) {
+                        mySrc.mDurations.addDuration(mySrc.mActiveProcState, mySrc.mActiveDuration);
+                        mySrc.mActiveDuration = 0;
+                        mySrc.mActiveProcState = ProcessStats.STATE_NOTHING;
+                    }
+                } else if (mySrc.mActiveDuration != 0) {
+                    // Both have a single inline duration...  we can either add them together,
+                    // or need to expand to multiple durations.
+                    if (mySrc.mActiveProcState == otherSrc.mActiveProcState) {
+                        mySrc.mDuration += otherSrc.mDuration;
+                    } else {
+                        // The two have durations with different proc states, need to turn
+                        // in to multiple durations.
+                        mySrc.makeDurations();
+                        mySrc.mDurations.addDuration(mySrc.mActiveProcState, mySrc.mActiveDuration);
+                        mySrc.mDurations.addDuration(otherSrc.mActiveProcState,
+                                otherSrc.mActiveDuration);
+                        mySrc.mActiveDuration = 0;
+                        mySrc.mActiveProcState = ProcessStats.STATE_NOTHING;
+                    }
+                } else {
+                    // The other one has a duration, and we know the target doesn't.  Copy over.
+                    mySrc.mActiveProcState = otherSrc.mActiveProcState;
+                    mySrc.mActiveDuration = otherSrc.mActiveDuration;
+                }
+            }
+        }
+    }
+
+    public boolean isInUse() {
+        return mNumActive > 0;
+    }
+
+    public void resetSafely(long now) {
+        if (!isInUse()) {
+            mSources.clear();
+        } else {
+            // We have some active sources...  clear out everything but those.
+            for (int isrc = mSources.size() - 1; isrc >= 0; isrc--) {
+                SourceState src = mSources.valueAt(isrc);
+                if (src.mNesting > 0) {
+                    src.mCount = 1;
+                    src.mStartUptime = now;
+                    src.mDuration = 0;
+                    if (src.mActiveStartUptime > 0) {
+                        src.mActiveCount = 1;
+                        src.mActiveStartUptime = now;
+                    } else {
+                        src.mActiveCount = 0;
+                    }
+                    src.mActiveDuration = 0;
+                    src.mDurations = null;
+                } else {
+                    mSources.removeAt(isrc);
+                }
+            }
+        }
+    }
+
+    public void writeToParcel(ProcessStats stats, Parcel out, long nowUptime) {
+        final int NSRC = mSources.size();
+        out.writeInt(NSRC);
+        for (int isrc = 0; isrc < NSRC; isrc++) {
+            final SourceKey key = mSources.keyAt(isrc);
+            final SourceState src = mSources.valueAt(isrc);
+            out.writeInt(key.mUid);
+            stats.writeCommonString(out, key.mProcess);
+            out.writeInt(src.mCount);
+            out.writeLong(src.mDuration);
+            out.writeInt(src.mActiveCount);
+            if (src.mDurations != null) {
+                out.writeInt(1);
+                src.mDurations.writeToParcel(out);
+            } else {
+                out.writeInt(0);
+                out.writeInt(src.mActiveProcState);
+                out.writeLong(src.mActiveDuration);
+            }
+        }
+    }
+
+    /**
+     * Returns non-null if all else fine, else a String that describes the error that
+     * caused it to fail.
+     */
+    public String readFromParcel(ProcessStats stats, Parcel in, int parcelVersion) {
+        final int NSRC = in.readInt();
+        if (NSRC < 0 || NSRC > 100000) {
+            return "Association with bad src count: " + NSRC;
+        }
+        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 SourceState src = new SourceState(key);
+            src.mCount = in.readInt();
+            src.mDuration = in.readLong();
+            src.mActiveCount = in.readInt();
+            if (in.readInt() != 0) {
+                src.makeDurations();
+                if (!src.mDurations.readFromParcel(in)) {
+                    return "Duration table corrupt: " + key + " <- " + src;
+                }
+            } else {
+                src.mActiveProcState = in.readInt();
+                src.mActiveDuration = in.readLong();
+            }
+            mSources.put(key, src);
+        }
+        return null;
+    }
+
+    public void commitStateTime(long nowUptime) {
+        if (isInUse()) {
+            for (int isrc = mSources.size() - 1; isrc >= 0; isrc--) {
+                SourceState src = mSources.valueAt(isrc);
+                if (src.mNesting > 0) {
+                    src.mDuration += nowUptime - src.mStartUptime;
+                    src.mStartUptime = nowUptime;
+                }
+                if (src.mActiveStartUptime > 0) {
+                    final long duration = src.mActiveDuration + nowUptime - src.mActiveStartUptime;
+                    if (src.mDurations != null) {
+                        src.mDurations.addDuration(src.mActiveProcState, duration);
+                    } else {
+                        src.mActiveDuration = duration;
+                    }
+                    src.mActiveStartUptime = nowUptime;
+                }
+            }
+        }
+    }
+
+    public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
+            long now, long totalTime, boolean dumpDetails, boolean dumpAll) {
+        if (dumpAll) {
+            pw.print(prefix);
+            pw.print("mNumActive=");
+            pw.println(mNumActive);
+        }
+        final int NSRC = mSources.size();
+        for (int isrc = 0; isrc < NSRC; isrc++) {
+            final SourceKey key = mSources.keyAt(isrc);
+            final SourceState src = mSources.valueAt(isrc);
+            pw.print(prefixInner);
+            pw.print("<- ");
+            pw.print(key.mProcess);
+            pw.print(" / ");
+            UserHandle.formatUid(pw, key.mUid);
+            pw.println(":");
+            pw.print(prefixInner);
+            pw.print("   Total count ");
+            pw.print(src.mCount);
+            long duration = src.mDuration;
+            if (src.mNesting > 0) {
+                duration += now - src.mStartUptime;
+            }
+            if (dumpAll) {
+                pw.print(": Duration ");
+                TimeUtils.formatDuration(duration, pw);
+                pw.print(" / ");
+            } else {
+                pw.print(": time ");
+            }
+            DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
+            if (src.mNesting > 0) {
+                pw.print(" (running");
+                if (src.mProcState != ProcessStats.STATE_NOTHING) {
+                    pw.print(" / ");
+                    pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
+                    pw.print(" #");
+                    pw.print(src.mProcStateSeq);
+                }
+                pw.print(")");
+            }
+            pw.println();
+            if (src.mActiveCount > 0 || src.mDurations != null || src.mActiveDuration != 0
+                    || src.mActiveStartUptime != 0) {
+                pw.print(prefixInner);
+                pw.print("   Active count ");
+                pw.print(src.mActiveCount);
+                if (dumpDetails) {
+                    if (dumpAll) {
+                        pw.print(src.mDurations != null ? " (multi-field)" : " (inline)");
+                    }
+                    pw.println(":");
+                    dumpTime(pw, prefixInner, src, totalTime, now, dumpDetails, dumpAll);
+                } else {
+                    pw.print(": ");
+                    dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
+                    pw.println();
+                }
+            }
+            if (dumpAll) {
+                if (src.mInTrackingList) {
+                    pw.print(prefixInner);
+                    pw.print("   mInTrackingList=");
+                    pw.println(src.mInTrackingList);
+                }
+                if (src.mProcState != ProcessStats.STATE_NOTHING) {
+                    pw.print(prefixInner);
+                    pw.print("   mProcState=");
+                    pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
+                    pw.print(" mProcStateSeq=");
+                    pw.println(src.mProcStateSeq);
+                }
+            }
+        }
+    }
+
+    void dumpActiveDurationSummary(PrintWriter pw, final SourceState src, long totalTime,
+            long now, boolean dumpAll) {
+        long duration = dumpTime(null, null, src, totalTime, now, false, false);
+        final boolean isRunning = duration < 0;
+        if (isRunning) {
+            duration = -duration;
+        }
+        if (dumpAll) {
+            pw.print("Duration ");
+            TimeUtils.formatDuration(duration, pw);
+            pw.print(" / ");
+        } else {
+            pw.print("time ");
+        }
+        DumpUtils.printPercent(pw, (double) duration / (double) totalTime);
+        if (src.mActiveStartUptime > 0) {
+            pw.print(" (running)");
+        }
+        pw.println();
+    }
+
+    long dumpTime(PrintWriter pw, String prefix, final SourceState src, long overallTime, long now,
+            boolean dumpDetails, boolean dumpAll) {
+        long totalTime = 0;
+        boolean isRunning = false;
+        for (int iprocstate = 0; iprocstate < ProcessStats.STATE_COUNT; iprocstate++) {
+            long time;
+            if (src.mDurations != null) {
+                time = src.mDurations.getValueForId((byte)iprocstate);
+            } else {
+                time = src.mActiveProcState == iprocstate ? src.mDuration : 0;
+            }
+            final String running;
+            if (src.mActiveStartUptime != 0 && src.mActiveProcState == iprocstate) {
+                running = " (running)";
+                isRunning = true;
+                time += now - src.mActiveStartUptime;
+            } else {
+                running = null;
+            }
+            if (time != 0) {
+                if (pw != null) {
+                    pw.print(prefix);
+                    pw.print("  ");
+                    pw.print(DumpUtils.STATE_LABELS[iprocstate]);
+                    pw.print(": ");
+                    if (dumpAll) {
+                        pw.print("Duration ");
+                        TimeUtils.formatDuration(time, pw);
+                        pw.print(" / ");
+                    } else {
+                        pw.print("time ");
+                    }
+                    DumpUtils.printPercent(pw, (double) time / (double) overallTime);
+                    if (running != null) {
+                        pw.print(running);
+                    }
+                    pw.println();
+                }
+                totalTime += time;
+            }
+        }
+        if (totalTime != 0 && pw != null) {
+            pw.print(prefix);
+            pw.print("  ");
+            pw.print(DumpUtils.STATE_LABEL_TOTAL);
+            pw.print(": ");
+            if (dumpAll) {
+                pw.print("Duration ");
+                TimeUtils.formatDuration(totalTime, pw);
+                pw.print(" / ");
+            } else {
+                pw.print("time ");
+            }
+            DumpUtils.printPercent(pw, (double) totalTime / (double) overallTime);
+            pw.println();
+        }
+        return isRunning ? -totalTime : totalTime;
+    }
+
+    public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers,
+            String associationName, long now) {
+        final int NSRC = mSources.size();
+        for (int isrc = 0; isrc < NSRC; isrc++) {
+            final SourceKey key = mSources.keyAt(isrc);
+            final SourceState src = mSources.valueAt(isrc);
+            pw.print("pkgasc");
+            pw.print(",");
+            pw.print(pkgName);
+            pw.print(",");
+            pw.print(uid);
+            pw.print(",");
+            pw.print(vers);
+            pw.print(",");
+            pw.print(associationName);
+            pw.print(",");
+            pw.print(key.mProcess);
+            pw.print(",");
+            pw.print(key.mUid);
+            pw.print(",");
+            pw.print(src.mCount);
+            long duration = src.mDuration;
+            if (src.mNesting > 0) {
+                duration += now - src.mStartUptime;
+            }
+            pw.print(",");
+            pw.print(duration);
+            pw.print(",");
+            pw.print(src.mActiveCount);
+            final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
+            if (src.mDurations != null) {
+                final int N = src.mDurations.getKeyCount();
+                for (int i=0; i<N; i++) {
+                    final int dkey = src.mDurations.getKeyAt(i);
+                    duration = src.mDurations.getValue(dkey);
+                    if (dkey == src.mActiveProcState) {
+                        duration += timeNow;
+                    }
+                    final int procState = SparseMappingTable.getIdFromKey(dkey);
+                    pw.print(",");
+                    DumpUtils.printArrayEntry(pw, DumpUtils.STATE_TAGS,  procState, 1);
+                    pw.print(':');
+                    pw.print(duration);
+                }
+            } else {
+                duration = src.mActiveDuration + timeNow;
+                if (duration != 0) {
+                    pw.print(",");
+                    DumpUtils.printArrayEntry(pw, DumpUtils.STATE_TAGS,  src.mActiveProcState, 1);
+                    pw.print(':');
+                    pw.print(duration);
+                }
+            }
+            pw.println();
+        }
+    }
+
+    public String toString() {
+        return "AssociationState{" + Integer.toHexString(System.identityHashCode(this))
+                + " " + mName + " pkg=" + mPackageState.mPackageName + " proc="
+                + Integer.toHexString(System.identityHashCode(mProc)) + "}";
+    }
+}
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index e38a844..e6073e5 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -48,6 +48,9 @@
  */
 public final class DumpUtils {
     public static final String[] STATE_NAMES;
+    public static final String[] STATE_LABELS;
+    public static final String STATE_LABEL_TOTAL;
+    public static final String STATE_LABEL_CACHED;
     public static final String[] STATE_NAMES_CSV;
     static final String[] STATE_TAGS;
     static final int[] STATE_PROTO_ENUMS;
@@ -55,52 +58,70 @@
     // Make the mapping easy to update.
     static {
         STATE_NAMES = new String[STATE_COUNT];
-        STATE_NAMES[STATE_PERSISTENT] = "Persist";
-        STATE_NAMES[STATE_TOP] = "Top";
-        STATE_NAMES[STATE_IMPORTANT_FOREGROUND] = "ImpFg";
-        STATE_NAMES[STATE_IMPORTANT_BACKGROUND] = "ImpBg";
-        STATE_NAMES[STATE_BACKUP] = "Backup";
-        STATE_NAMES[STATE_SERVICE] = "Service";
-        STATE_NAMES[STATE_SERVICE_RESTARTING] = "ServRst";
-        STATE_NAMES[STATE_RECEIVER] = "Receivr";
-        STATE_NAMES[STATE_HEAVY_WEIGHT] = "HeavyWt";
-        STATE_NAMES[STATE_HOME] = "Home";
-        STATE_NAMES[STATE_LAST_ACTIVITY] = "LastAct";
-        STATE_NAMES[STATE_CACHED_ACTIVITY] = "CchAct";
-        STATE_NAMES[STATE_CACHED_ACTIVITY_CLIENT] = "CchCAct";
-        STATE_NAMES[STATE_CACHED_EMPTY] = "CchEmty";
+        STATE_NAMES[STATE_PERSISTENT]               = "Persist";
+        STATE_NAMES[STATE_TOP]                      = "Top";
+        STATE_NAMES[STATE_IMPORTANT_FOREGROUND]     = "ImpFg";
+        STATE_NAMES[STATE_IMPORTANT_BACKGROUND]     = "ImpBg";
+        STATE_NAMES[STATE_BACKUP]                   = "Backup";
+        STATE_NAMES[STATE_SERVICE]                  = "Service";
+        STATE_NAMES[STATE_SERVICE_RESTARTING]       = "ServRst";
+        STATE_NAMES[STATE_RECEIVER]                 = "Receivr";
+        STATE_NAMES[STATE_HEAVY_WEIGHT]             = "HeavyWt";
+        STATE_NAMES[STATE_HOME]                     = "Home";
+        STATE_NAMES[STATE_LAST_ACTIVITY]            = "LastAct";
+        STATE_NAMES[STATE_CACHED_ACTIVITY]          = "CchAct";
+        STATE_NAMES[STATE_CACHED_ACTIVITY_CLIENT]   = "CchCAct";
+        STATE_NAMES[STATE_CACHED_EMPTY]             = "CchEmty";
+
+        STATE_LABELS = new String[STATE_COUNT];
+        STATE_LABELS[STATE_PERSISTENT]              = "Persistent";
+        STATE_LABELS[STATE_TOP]                     = "       Top";
+        STATE_LABELS[STATE_IMPORTANT_FOREGROUND]    = "    Imp Fg";
+        STATE_LABELS[STATE_IMPORTANT_BACKGROUND]    = "    Imp Bg";
+        STATE_LABELS[STATE_BACKUP]                  = "    Backup";
+        STATE_LABELS[STATE_SERVICE]                 = "   Service";
+        STATE_LABELS[STATE_SERVICE_RESTARTING]      = "Service Rs";
+        STATE_LABELS[STATE_RECEIVER]                = "  Receiver";
+        STATE_LABELS[STATE_HEAVY_WEIGHT]            = " Heavy Wgt";
+        STATE_LABELS[STATE_HOME]                    = "    (Home)";
+        STATE_LABELS[STATE_LAST_ACTIVITY]           = "(Last Act)";
+        STATE_LABELS[STATE_CACHED_ACTIVITY]         = " (Cch Act)";
+        STATE_LABELS[STATE_CACHED_ACTIVITY_CLIENT]  = "(Cch CAct)";
+        STATE_LABELS[STATE_CACHED_EMPTY]            = "(Cch Emty)";
+        STATE_LABEL_CACHED                          = "  (Cached)";
+        STATE_LABEL_TOTAL                           = "     TOTAL";
 
         STATE_NAMES_CSV = new String[STATE_COUNT];
-        STATE_NAMES_CSV[STATE_PERSISTENT] = "pers";
-        STATE_NAMES_CSV[STATE_TOP] = "top";
-        STATE_NAMES_CSV[STATE_IMPORTANT_FOREGROUND] = "impfg";
-        STATE_NAMES_CSV[STATE_IMPORTANT_BACKGROUND] = "impbg";
-        STATE_NAMES_CSV[STATE_BACKUP] = "backup";
-        STATE_NAMES_CSV[STATE_SERVICE] = "service";
-        STATE_NAMES_CSV[STATE_SERVICE_RESTARTING] = "service-rs";
-        STATE_NAMES_CSV[STATE_RECEIVER] = "receiver";
-        STATE_NAMES_CSV[STATE_HEAVY_WEIGHT] = "heavy";
-        STATE_NAMES_CSV[STATE_HOME] = "home";
-        STATE_NAMES_CSV[STATE_LAST_ACTIVITY] = "lastact";
-        STATE_NAMES_CSV[STATE_CACHED_ACTIVITY] = "cch-activity";
-        STATE_NAMES_CSV[STATE_CACHED_ACTIVITY_CLIENT] = "cch-aclient";
-        STATE_NAMES_CSV[STATE_CACHED_EMPTY] = "cch-empty";
+        STATE_NAMES_CSV[STATE_PERSISTENT]               = "pers";
+        STATE_NAMES_CSV[STATE_TOP]                      = "top";
+        STATE_NAMES_CSV[STATE_IMPORTANT_FOREGROUND]     = "impfg";
+        STATE_NAMES_CSV[STATE_IMPORTANT_BACKGROUND]     = "impbg";
+        STATE_NAMES_CSV[STATE_BACKUP]                   = "backup";
+        STATE_NAMES_CSV[STATE_SERVICE]                  = "service";
+        STATE_NAMES_CSV[STATE_SERVICE_RESTARTING]       = "service-rs";
+        STATE_NAMES_CSV[STATE_RECEIVER]                 = "receiver";
+        STATE_NAMES_CSV[STATE_HEAVY_WEIGHT]             = "heavy";
+        STATE_NAMES_CSV[STATE_HOME]                     = "home";
+        STATE_NAMES_CSV[STATE_LAST_ACTIVITY]            = "lastact";
+        STATE_NAMES_CSV[STATE_CACHED_ACTIVITY]          = "cch-activity";
+        STATE_NAMES_CSV[STATE_CACHED_ACTIVITY_CLIENT]   = "cch-aclient";
+        STATE_NAMES_CSV[STATE_CACHED_EMPTY]             = "cch-empty";
 
         STATE_TAGS = new String[STATE_COUNT];
-        STATE_TAGS[STATE_PERSISTENT] = "p";
-        STATE_TAGS[STATE_TOP] = "t";
-        STATE_TAGS[STATE_IMPORTANT_FOREGROUND] = "f";
-        STATE_TAGS[STATE_IMPORTANT_BACKGROUND] = "b";
-        STATE_TAGS[STATE_BACKUP] = "u";
-        STATE_TAGS[STATE_SERVICE] = "s";
-        STATE_TAGS[STATE_SERVICE_RESTARTING] = "x";
-        STATE_TAGS[STATE_RECEIVER] = "r";
-        STATE_TAGS[STATE_HEAVY_WEIGHT] = "w";
-        STATE_TAGS[STATE_HOME] = "h";
-        STATE_TAGS[STATE_LAST_ACTIVITY] = "l";
-        STATE_TAGS[STATE_CACHED_ACTIVITY] = "a";
-        STATE_TAGS[STATE_CACHED_ACTIVITY_CLIENT] = "c";
-        STATE_TAGS[STATE_CACHED_EMPTY] = "e";
+        STATE_TAGS[STATE_PERSISTENT]                = "p";
+        STATE_TAGS[STATE_TOP]                       = "t";
+        STATE_TAGS[STATE_IMPORTANT_FOREGROUND]      = "f";
+        STATE_TAGS[STATE_IMPORTANT_BACKGROUND]      = "b";
+        STATE_TAGS[STATE_BACKUP]                    = "u";
+        STATE_TAGS[STATE_SERVICE]                   = "s";
+        STATE_TAGS[STATE_SERVICE_RESTARTING]        = "x";
+        STATE_TAGS[STATE_RECEIVER]                  = "r";
+        STATE_TAGS[STATE_HEAVY_WEIGHT]              = "w";
+        STATE_TAGS[STATE_HOME]                      = "h";
+        STATE_TAGS[STATE_LAST_ACTIVITY]             = "l";
+        STATE_TAGS[STATE_CACHED_ACTIVITY]           = "a";
+        STATE_TAGS[STATE_CACHED_ACTIVITY_CLIENT]    = "c";
+        STATE_TAGS[STATE_CACHED_EMPTY]              = "e";
 
         STATE_PROTO_ENUMS = new int[STATE_COUNT];
         STATE_PROTO_ENUMS[STATE_PERSISTENT] = ProcessStatsProto.State.PERSISTENT;
@@ -166,7 +187,7 @@
                 pw.print("SOff/");
                 break;
             case ADJ_SCREEN_ON:
-                pw.print("SOn /");
+                pw.print(" SOn/");
                 break;
             default:
                 pw.print("????/");
@@ -201,11 +222,11 @@
                 if (sep != 0) pw.print(sep);
                 break;
             case ADJ_MEM_FACTOR_MODERATE:
-                pw.print("Mod ");
+                pw.print(" Mod");
                 if (sep != 0) pw.print(sep);
                 break;
             case ADJ_MEM_FACTOR_LOW:
-                pw.print("Low ");
+                pw.print(" Low");
                 if (sep != 0) pw.print(sep);
                 break;
             case ADJ_MEM_FACTOR_CRITICAL:
@@ -362,12 +383,13 @@
         }
     }
 
-    public static void dumpProcessSummaryLocked(PrintWriter pw, String prefix,
+    public static void dumpProcessSummaryLocked(PrintWriter pw, String prefix, String header,
             ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates,
             long now, long totalTime) {
         for (int i=procs.size()-1; i>=0; i--) {
             final ProcessState proc = procs.get(i);
-            proc.dumpSummary(pw, prefix, screenStates, memStates, procStates, now, totalTime);
+            proc.dumpSummary(pw, prefix, header, screenStates, memStates, procStates, now,
+                    totalTime);
         }
     }
 
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index 65bd48f..943c8bd 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -25,6 +25,7 @@
 import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.Slog;
+import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
@@ -62,8 +63,6 @@
 
 import java.io.PrintWriter;
 import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Map;
 
 public final class ProcessState {
     private static final String TAG = "ProcessStats";
@@ -71,7 +70,7 @@
     private static final boolean DEBUG_PARCEL = false;
 
     // Map from process states to the states we track.
-    private static final int[] PROCESS_STATE_TO_STATE = new int[] {
+    static final int[] PROCESS_STATE_TO_STATE = new int[] {
         STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT
         STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT_UI
         STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP
@@ -127,14 +126,18 @@
     private final long mVersion;
     private final DurationsTable mDurations;
     private final PssTable mPssTable;
+    private final long[] mTotalRunningPss = new long[ProcessStats.PSS_COUNT];
 
     private ProcessState mCommonProcess;
-    private int mCurState = STATE_NOTHING;
+    private int mCurCombinedState = STATE_NOTHING;
     private long mStartTime;
 
     private int mLastPssState = STATE_NOTHING;
     private long mLastPssTime;
 
+    private long mTotalRunningStartTime;
+    private long mTotalRunningDuration;
+
     private boolean mActive;
     private int mNumActiveServices;
     private int mNumStartedServices;
@@ -180,8 +183,11 @@
         mPackage = pkg;
         mUid = uid;
         mVersion = vers;
-        mCurState = commonProcess.mCurState;
+        mCurCombinedState = commonProcess.mCurCombinedState;
         mStartTime = now;
+        if (mCurCombinedState != STATE_NOTHING) {
+            mTotalRunningStartTime = now;
+        }
         mDurations = new DurationsTable(commonProcess.mStats.mTableData);
         mPssTable = new PssTable(commonProcess.mStats.mTableData);
     }
@@ -190,6 +196,8 @@
         ProcessState pnew = new ProcessState(this, mPackage, mUid, mVersion, mName, now);
         pnew.mDurations.addDurations(mDurations);
         pnew.mPssTable.copyFrom(mPssTable, PSS_COUNT);
+        System.arraycopy(mTotalRunningPss, 0, pnew.mTotalRunningPss, 0, ProcessStats.PSS_COUNT);
+        pnew.mTotalRunningDuration = getTotalRunningDuration(now);
         pnew.mNumExcessiveCpu = mNumExcessiveCpu;
         pnew.mNumCachedKill = mNumCachedKill;
         pnew.mMinCachedKillPss = mMinCachedKillPss;
@@ -235,7 +243,7 @@
     public void setMultiPackage(boolean val) {
         mMultiPackage = val;
     }
-    
+
     public int getDurationsBucketCount() {
         return mDurations.getKeyCount();
     }
@@ -243,6 +251,10 @@
     public void add(ProcessState other) {
         mDurations.addDurations(other.mDurations);
         mPssTable.mergeStats(other.mPssTable);
+        // Note that we don't touch mTotalRunningPss, because in current use
+        // 'other' is older stats that are being added in to these newer ones.
+        // So the newer ones keep track of the total running time, which is always
+        // the right thing over whatever was in older stats.
         mNumExcessiveCpu += other.mNumExcessiveCpu;
         if (other.mNumCachedKill > 0) {
             addCachedKill(other.mNumCachedKill, other.mMinCachedKillPss,
@@ -277,6 +289,10 @@
         out.writeInt(mMultiPackage ? 1 : 0);
         mDurations.writeToParcel(out);
         mPssTable.writeToParcel(out);
+        for (int i = 0; i < ProcessStats.PSS_COUNT; i++) {
+            out.writeLong(mTotalRunningPss[i]);
+        }
+        out.writeLong(getTotalRunningDuration(now));
         out.writeInt(0);  // was mNumExcessiveWake
         out.writeInt(mNumExcessiveCpu);
         out.writeInt(mNumCachedKill);
@@ -300,6 +316,10 @@
         if (!mPssTable.readFromParcel(in)) {
             return false;
         }
+        for (int i = 0; i < ProcessStats.PSS_COUNT; i++) {
+            mTotalRunningPss[i] = in.readLong();
+        }
+        mTotalRunningDuration = in.readLong();
         in.readInt(); // was mNumExcessiveWake
         mNumExcessiveCpu = in.readInt();
         mNumCachedKill = in.readInt();
@@ -324,7 +344,7 @@
 
     public boolean isInUse() {
         return mActive || mNumActiveServices > 0 || mNumStartedServices > 0
-                || mCurState != STATE_NOTHING;
+                || mCurCombinedState != STATE_NOTHING;
     }
 
     public boolean isActive() {
@@ -333,8 +353,9 @@
 
     public boolean hasAnyData() {
         return !(mDurations.getKeyCount() == 0
-                && mCurState == STATE_NOTHING
-                && mPssTable.getKeyCount() == 0);
+                && mCurCombinedState == STATE_NOTHING
+                && mPssTable.getKeyCount() == 0
+                && mTotalRunningPss[PSS_SAMPLE_COUNT] == 0);
     }
 
     /**
@@ -355,7 +376,7 @@
         }
 
         // First update the common process.
-        mCommonProcess.setState(state, now);
+        mCommonProcess.setCombinedState(state, now);
 
         // If the common process is not multi-package, there is nothing else to do.
         if (!mCommonProcess.mMultiPackage) {
@@ -364,26 +385,45 @@
 
         if (pkgList != null) {
             for (int ip=pkgList.size()-1; ip>=0; ip--) {
-                pullFixedProc(pkgList, ip).setState(state, now);
+                pullFixedProc(pkgList, ip).setCombinedState(state, now);
             }
         }
     }
 
-    public void setState(int state, long now) {
+    public void setCombinedState(int state, long now) {
         ensureNotDead();
-        if (!mDead && (mCurState != state)) {
+        if (!mDead && (mCurCombinedState != state)) {
             //Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state);
             commitStateTime(now);
-            mCurState = state;
+            if (state == STATE_NOTHING) {
+                // We are transitioning to a no longer running state... stop counting run time.
+                mTotalRunningDuration += now - mTotalRunningStartTime;
+                mTotalRunningStartTime = 0;
+            } else if (mCurCombinedState == STATE_NOTHING) {
+                // We previously weren't running...  now starting again, clear out total
+                // running info.
+                mTotalRunningDuration = 0;
+                mTotalRunningStartTime = now;
+                for (int i = ProcessStats.PSS_COUNT - 1; i >= 0; i--) {
+                    mTotalRunningPss[i] = 0;
+                }
+            }
+            mCurCombinedState = state;
         }
     }
 
+    public int getCombinedState() {
+        return mCurCombinedState;
+    }
+
     public void commitStateTime(long now) {
-        if (mCurState != STATE_NOTHING) {
+        if (mCurCombinedState != STATE_NOTHING) {
             long dur = now - mStartTime;
             if (dur > 0) {
-                mDurations.addDuration(mCurState, dur);
+                mDurations.addDuration(mCurCombinedState, dur);
             }
+            mTotalRunningDuration += now - mTotalRunningStartTime;
+            mTotalRunningStartTime = now;
         }
         mStartTime = now;
     }
@@ -430,8 +470,8 @@
             mCommonProcess.incStartedServices(memFactor, now, serviceName);
         }
         mNumStartedServices++;
-        if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) {
-            setState(STATE_SERVICE_RESTARTING + (memFactor*STATE_COUNT), now);
+        if (mNumStartedServices == 1 && mCurCombinedState == STATE_NOTHING) {
+            setCombinedState(STATE_SERVICE_RESTARTING + (memFactor*STATE_COUNT), now);
         }
     }
 
@@ -446,8 +486,8 @@
             mCommonProcess.decStartedServices(memFactor, now, serviceName);
         }
         mNumStartedServices--;
-        if (mNumStartedServices == 0 && (mCurState%STATE_COUNT) == STATE_SERVICE_RESTARTING) {
-            setState(STATE_NOTHING, now);
+        if (mNumStartedServices == 0 && (mCurCombinedState %STATE_COUNT) == STATE_SERVICE_RESTARTING) {
+            setCombinedState(STATE_NOTHING, now);
         } else if (mNumStartedServices < 0) {
             Slog.wtfStack(TAG, "Proc started services underrun: pkg="
                     + mPackage + " uid=" + mUid + " name=" + mName);
@@ -481,16 +521,18 @@
                 break;
         }
         if (!always) {
-            if (mLastPssState == mCurState && SystemClock.uptimeMillis()
+            if (mLastPssState == mCurCombinedState && SystemClock.uptimeMillis()
                     < (mLastPssTime+(30*1000))) {
                 return;
             }
         }
-        mLastPssState = mCurState;
+        mLastPssState = mCurCombinedState;
         mLastPssTime = SystemClock.uptimeMillis();
-        if (mCurState != STATE_NOTHING) {
+        if (mCurCombinedState != STATE_NOTHING) {
             // First update the common process.
-            mCommonProcess.mPssTable.mergeStats(mCurState, 1, pss, pss, pss, uss, uss, uss,
+            mCommonProcess.mPssTable.mergeStats(mCurCombinedState, 1, pss, pss, pss, uss, uss, uss,
+                    rss, rss, rss);
+            PssTable.mergeStats(mCommonProcess.mTotalRunningPss, 0, 1, pss, pss, pss, uss, uss, uss,
                     rss, rss, rss);
 
             // If the common process is not multi-package, there is nothing else to do.
@@ -500,7 +542,10 @@
 
             if (pkgList != null) {
                 for (int ip=pkgList.size()-1; ip>=0; ip--) {
-                    pullFixedProc(pkgList, ip).mPssTable.mergeStats(mCurState, 1,
+                    ProcessState fixedProc = pullFixedProc(pkgList, ip);
+                    fixedProc.mPssTable.mergeStats(mCurCombinedState, 1,
+                            pss, pss, pss, uss, uss, uss, rss, rss, rss);
+                    PssTable.mergeStats(fixedProc.mTotalRunningPss, 0, 1,
                             pss, pss, pss, uss, uss, uss, rss, rss, rss);
                 }
             }
@@ -580,7 +625,7 @@
         ProcessStateHolder holder = pkgList.valueAt(index);
         ProcessState proc = holder.state;
         if (mDead && proc.mCommonProcess != proc) {
-            // Somehow we are contining to use a process state that is dead, because
+            // Somehow we are continuing to use a process state that is dead, because
             // it was not being told it was active during the last commit.  We can recover
             // from this by generating a fresh new state, but this is bad because we
             // are losing whatever data we had in the old process state.
@@ -600,26 +645,31 @@
                         + pkgList.keyAt(index) + "/" + proc.mUid
                         + " for multi-proc " + proc.mName);
             }
-            PackageState pkg = vpkg.get(proc.mVersion);
-            if (pkg == null) {
+            PackageState expkg = vpkg.get(proc.mVersion);
+            if (expkg == null) {
                 throw new IllegalStateException("No existing package "
                         + pkgList.keyAt(index) + "/" + proc.mUid
                         + " for multi-proc " + proc.mName + " version " + proc.mVersion);
             }
             String savedName = proc.mName;
-            proc = pkg.mProcesses.get(proc.mName);
+            proc = expkg.mProcesses.get(proc.mName);
             if (proc == null) {
                 throw new IllegalStateException("Didn't create per-package process "
-                        + savedName + " in pkg " + pkg.mPackageName + "/" + pkg.mUid);
+                        + savedName + " in pkg " + expkg.mPackageName + "/" + expkg.mUid);
             }
             holder.state = proc;
         }
         return proc;
     }
 
+    public long getTotalRunningDuration(long now) {
+        return mTotalRunningDuration +
+                (mTotalRunningStartTime != 0 ? (now - mTotalRunningStartTime) : 0);
+    }
+
     public long getDuration(int state, long now) {
         long time = mDurations.getValueForId((byte)state);
-        if (mCurState == state) {
+        if (mCurCombinedState == state) {
             time += now - mStartTime;
         }
         return time;
@@ -667,7 +717,7 @@
 
     /**
      * Sums up the PSS data and adds it to 'data'.
-     * 
+     *
      * @param data The aggregate data is added here.
      * @param now SystemClock.uptimeMillis()
      */
@@ -724,7 +774,7 @@
             final int key = mDurations.getKeyAt(i);
             final int type = SparseMappingTable.getIdFromKey(key);
             long time = mDurations.getValue(key);
-            if (mCurState == type) {
+            if (mCurCombinedState == type) {
                 time += now - mStartTime;
             }
             final int procState = type % STATE_COUNT;
@@ -769,46 +819,50 @@
         return totalTime;
     }
 
-    public void dumpSummary(PrintWriter pw, String prefix,
+    public void dumpSummary(PrintWriter pw, String prefix, String header,
             int[] screenStates, int[] memStates, int[] procStates,
             long now, long totalTime) {
         pw.print(prefix);
         pw.print("* ");
+        if (header != null) {
+            pw.print(header);
+        }
         pw.print(mName);
         pw.print(" / ");
         UserHandle.formatUid(pw, mUid);
         pw.print(" / v");
         pw.print(mVersion);
         pw.println(":");
-        dumpProcessSummaryDetails(pw, prefix, "         TOTAL: ", screenStates, memStates,
-                procStates, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, "    Persistent: ", screenStates, memStates,
-                new int[] { STATE_PERSISTENT }, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, "           Top: ", screenStates, memStates,
-                new int[] {STATE_TOP}, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, "        Imp Fg: ", screenStates, memStates,
-                new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, "        Imp Bg: ", screenStates, memStates,
-                new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, "        Backup: ", screenStates, memStates,
-                new int[] {STATE_BACKUP}, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, "     Heavy Wgt: ", screenStates, memStates,
-                new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, "       Service: ", screenStates, memStates,
-                new int[] {STATE_SERVICE}, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, "    Service Rs: ", screenStates, memStates,
-                new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, "      Receiver: ", screenStates, memStates,
-                new int[] {STATE_RECEIVER}, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, "         Heavy: ", screenStates, memStates,
-                new int[] {STATE_HOME}, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, "        (Home): ", screenStates, memStates,
-                new int[] {STATE_HOME}, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, "    (Last Act): ", screenStates, memStates,
-                new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true);
-        dumpProcessSummaryDetails(pw, prefix, "      (Cached): ", screenStates, memStates,
-                new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT,
-                        STATE_CACHED_EMPTY}, now, totalTime, true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABEL_TOTAL,
+                screenStates, memStates, procStates, now, totalTime, true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_PERSISTENT],
+                screenStates, memStates, new int[] { STATE_PERSISTENT }, now, totalTime, true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_TOP],
+                screenStates, memStates, new int[] {STATE_TOP}, now, totalTime, true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_IMPORTANT_FOREGROUND],
+                screenStates, memStates, new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime,
+                true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_IMPORTANT_BACKGROUND],
+                screenStates, memStates, new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime,
+                true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_BACKUP],
+                screenStates, memStates, new int[] {STATE_BACKUP}, now, totalTime, true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_SERVICE],
+                screenStates, memStates, new int[] {STATE_SERVICE}, now, totalTime, true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_SERVICE_RESTARTING],
+                screenStates, memStates, new int[] {STATE_SERVICE_RESTARTING}, now, totalTime,
+                true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_RECEIVER],
+                screenStates, memStates, new int[] {STATE_RECEIVER}, now, totalTime, true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_HEAVY_WEIGHT],
+                screenStates, memStates, new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_HOME],
+                screenStates, memStates, new int[] {STATE_HOME}, now, totalTime, true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_LAST_ACTIVITY],
+                screenStates, memStates, new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true);
+        dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABEL_CACHED,
+                screenStates, memStates, new int[] {STATE_CACHED_ACTIVITY,
+                        STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY}, now, totalTime, true);
     }
 
     public void dumpProcessState(PrintWriter pw, String prefix,
@@ -824,8 +878,9 @@
                     final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
                     long time = mDurations.getValueForId((byte)bucket);
                     String running = "";
-                    if (mCurState == bucket) {
+                    if (mCurCombinedState == bucket) {
                         running = " (running)";
+                        time += now - mStartTime;
                     }
                     if (time != 0) {
                         pw.print(prefix);
@@ -839,7 +894,7 @@
                                     printedMem != imem ? imem : STATE_NOTHING, '/');
                             printedMem = imem;
                         }
-                        pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": ");
+                        pw.print(DumpUtils.STATE_LABELS[procStates[ip]]); pw.print(": ");
                         TimeUtils.formatDuration(time, pw); pw.println(running);
                         totalTime += time;
                     }
@@ -854,14 +909,15 @@
             if (memStates.length > 1) {
                 DumpUtils.printMemLabel(pw, STATE_NOTHING, '/');
             }
-            pw.print("TOTAL  : ");
+            pw.print(DumpUtils.STATE_LABEL_TOTAL);
+            pw.print(": ");
             TimeUtils.formatDuration(totalTime, pw);
             pw.println();
         }
     }
 
     public void dumpPss(PrintWriter pw, String prefix,
-            int[] screenStates, int[] memStates, int[] procStates) {
+            int[] screenStates, int[] memStates, int[] procStates, long now) {
         boolean printedHeader = false;
         int printedScreen = -1;
         for (int is=0; is<screenStates.length; is++) {
@@ -871,52 +927,51 @@
                     final int iscreen = screenStates[is];
                     final int imem = memStates[im];
                     final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
-                    long count = getPssSampleCount(bucket);
-                    if (count > 0) {
-                        if (!printedHeader) {
-                            pw.print(prefix);
-                            pw.print("PSS/USS (");
-                            pw.print(mPssTable.getKeyCount());
-                            pw.println(" entries):");
-                            printedHeader = true;
-                        }
-                        pw.print(prefix);
-                        pw.print("  ");
-                        if (screenStates.length > 1) {
-                            DumpUtils.printScreenLabel(pw,
-                                    printedScreen != iscreen ? iscreen : STATE_NOTHING);
-                            printedScreen = iscreen;
-                        }
-                        if (memStates.length > 1) {
-                            DumpUtils.printMemLabel(pw,
-                                    printedMem != imem ? imem : STATE_NOTHING, '/');
-                            printedMem = imem;
-                        }
-                        pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": ");
-                        pw.print(count);
-                        pw.print(" samples ");
-                        DebugUtils.printSizeValue(pw, getPssMinimum(bucket) * 1024);
-                        pw.print(" ");
-                        DebugUtils.printSizeValue(pw, getPssAverage(bucket) * 1024);
-                        pw.print(" ");
-                        DebugUtils.printSizeValue(pw, getPssMaximum(bucket) * 1024);
-                        pw.print(" / ");
-                        DebugUtils.printSizeValue(pw, getPssUssMinimum(bucket) * 1024);
-                        pw.print(" ");
-                        DebugUtils.printSizeValue(pw, getPssUssAverage(bucket) * 1024);
-                        pw.print(" ");
-                        DebugUtils.printSizeValue(pw, getPssUssMaximum(bucket) * 1024);
-                        pw.print(" / ");
-                        DebugUtils.printSizeValue(pw, getPssRssMinimum(bucket) * 1024);
-                        pw.print(" ");
-                        DebugUtils.printSizeValue(pw, getPssRssAverage(bucket) * 1024);
-                        pw.print(" ");
-                        DebugUtils.printSizeValue(pw, getPssRssMaximum(bucket) * 1024);
-                        pw.println();
+                    final int key = mPssTable.getKey((byte)bucket);
+                    if (key == SparseMappingTable.INVALID_KEY) {
+                        continue;
                     }
+                    final long[] table = mPssTable.getArrayForKey(key);
+                    final int tableOffset = SparseMappingTable.getIndexFromKey(key);
+                    if (!printedHeader) {
+                        pw.print(prefix);
+                        pw.print("PSS/USS (");
+                        pw.print(mPssTable.getKeyCount());
+                        pw.println(" entries):");
+                        printedHeader = true;
+                    }
+                    pw.print(prefix);
+                    pw.print("  ");
+                    if (screenStates.length > 1) {
+                        DumpUtils.printScreenLabel(pw,
+                                printedScreen != iscreen ? iscreen : STATE_NOTHING);
+                        printedScreen = iscreen;
+                    }
+                    if (memStates.length > 1) {
+                        DumpUtils.printMemLabel(pw,
+                                printedMem != imem ? imem : STATE_NOTHING, '/');
+                        printedMem = imem;
+                    }
+                    pw.print(DumpUtils.STATE_LABELS[procStates[ip]]); pw.print(": ");
+                    dumpPssSamples(pw, table, tableOffset);
+                    pw.println();
                 }
             }
         }
+        final long totalRunningDuration = getTotalRunningDuration(now);
+        if (totalRunningDuration != 0) {
+            pw.print(prefix);
+            pw.print("Cur time ");
+            TimeUtils.formatDuration(totalRunningDuration, pw);
+            if (mTotalRunningStartTime != 0) {
+                pw.print(" (running)");
+            }
+            if (mTotalRunningPss[PSS_SAMPLE_COUNT] != 0) {
+                pw.print(": ");
+                dumpPssSamples(pw, mTotalRunningPss, 0);
+            }
+            pw.println();
+        }
         if (mNumExcessiveCpu != 0) {
             pw.print(prefix); pw.print("Killed for excessive CPU use: ");
                     pw.print(mNumExcessiveCpu); pw.println(" times");
@@ -930,6 +985,28 @@
         }
     }
 
+    public static void dumpPssSamples(PrintWriter pw, long[] table, int offset) {
+        DebugUtils.printSizeValue(pw, table[offset + PSS_MINIMUM] * 1024);
+        pw.print("-");
+        DebugUtils.printSizeValue(pw, table[offset + PSS_AVERAGE] * 1024);
+        pw.print("-");
+        DebugUtils.printSizeValue(pw, table[offset + PSS_MAXIMUM] * 1024);
+        pw.print("/");
+        DebugUtils.printSizeValue(pw, table[offset + PSS_USS_MINIMUM] * 1024);
+        pw.print("-");
+        DebugUtils.printSizeValue(pw, table[offset + PSS_USS_AVERAGE] * 1024);
+        pw.print("-");
+        DebugUtils.printSizeValue(pw, table[offset + PSS_USS_MAXIMUM] * 1024);
+        pw.print("/");
+        DebugUtils.printSizeValue(pw, table[offset + PSS_RSS_MINIMUM] * 1024);
+        pw.print("-");
+        DebugUtils.printSizeValue(pw, table[offset + PSS_RSS_AVERAGE] * 1024);
+        pw.print("-");
+        DebugUtils.printSizeValue(pw, table[offset + PSS_RSS_MAXIMUM] * 1024);
+        pw.print(" over ");
+        pw.print(table[offset + PSS_SAMPLE_COUNT]);
+    }
+
     private void dumpProcessSummaryDetails(PrintWriter pw, String prefix,
             String label, int[] screenStates, int[] memStates, int[] procStates,
             long now, long totalTime, boolean full) {
@@ -943,7 +1020,9 @@
                 pw.print(prefix);
             }
             if (label != null) {
+                pw.print("  ");
                 pw.print(label);
+                pw.print(": ");
             }
             totals.print(pw, totalTime, full);
             if (prefix != null) {
@@ -1105,6 +1184,21 @@
             dumpAllPssCheckin(pw);
             pw.println();
         }
+        if (mTotalRunningPss[PSS_SAMPLE_COUNT] != 0) {
+            pw.print("pkgrun,");
+            pw.print(pkgName);
+            pw.print(",");
+            pw.print(uid);
+            pw.print(",");
+            pw.print(vers);
+            pw.print(",");
+            pw.print(DumpUtils.collapseString(pkgName, itemName));
+            pw.print(",");
+            pw.print(getTotalRunningDuration(now));
+            pw.print(",");
+            dumpPssSamplesCheckin(pw, mTotalRunningPss, 0);
+            pw.println();
+        }
         if (mNumExcessiveCpu > 0 || mNumCachedKill > 0) {
             pw.print("pkgkills,");
             pw.print(pkgName);
@@ -1147,6 +1241,17 @@
             dumpAllPssCheckin(pw);
             pw.println();
         }
+        if (mTotalRunningPss[PSS_SAMPLE_COUNT] != 0) {
+            pw.print("procrun,");
+            pw.print(procName);
+            pw.print(",");
+            pw.print(uid);
+            pw.print(",");
+            pw.print(getTotalRunningDuration(now));
+            pw.print(",");
+            dumpPssSamplesCheckin(pw, mTotalRunningPss, 0);
+            pw.println();
+        }
         if (mNumExcessiveCpu > 0 || mNumCachedKill > 0) {
             pw.print("kills,");
             pw.print(procName);
@@ -1174,14 +1279,14 @@
             final int key = mDurations.getKeyAt(i);
             final int type = SparseMappingTable.getIdFromKey(key);
             long time = mDurations.getValue(key);
-            if (mCurState == type) {
+            if (mCurCombinedState == type) {
                 didCurState = true;
                 time += now - mStartTime;
             }
             DumpUtils.printProcStateTagAndValue(pw, type, time);
         }
-        if (!didCurState && mCurState != STATE_NOTHING) {
-            DumpUtils.printProcStateTagAndValue(pw, mCurState, now - mStartTime);
+        if (!didCurState && mCurCombinedState != STATE_NOTHING) {
+            DumpUtils.printProcStateTagAndValue(pw, mCurCombinedState, now - mStartTime);
         }
     }
 
@@ -1193,28 +1298,33 @@
             pw.print(',');
             DumpUtils.printProcStateTag(pw, type);
             pw.print(':');
-            pw.print(mPssTable.getValue(key, PSS_SAMPLE_COUNT));
-            pw.print(':');
-            pw.print(mPssTable.getValue(key, PSS_MINIMUM));
-            pw.print(':');
-            pw.print(mPssTable.getValue(key, PSS_AVERAGE));
-            pw.print(':');
-            pw.print(mPssTable.getValue(key, PSS_MAXIMUM));
-            pw.print(':');
-            pw.print(mPssTable.getValue(key, PSS_USS_MINIMUM));
-            pw.print(':');
-            pw.print(mPssTable.getValue(key, PSS_USS_AVERAGE));
-            pw.print(':');
-            pw.print(mPssTable.getValue(key, PSS_USS_MAXIMUM));
-            pw.print(':');
-            pw.print(mPssTable.getValue(key, PSS_RSS_MINIMUM));
-            pw.print(':');
-            pw.print(mPssTable.getValue(key, PSS_RSS_AVERAGE));
-            pw.print(':');
-            pw.print(mPssTable.getValue(key, PSS_RSS_MAXIMUM));
+            dumpPssSamplesCheckin(pw, mPssTable.getArrayForKey(key),
+                    SparseMappingTable.getIndexFromKey(key));
         }
     }
 
+    public static void dumpPssSamplesCheckin(PrintWriter pw, long[] table, int offset) {
+        pw.print(table[offset + PSS_SAMPLE_COUNT]);
+        pw.print(':');
+        pw.print(table[offset + PSS_MINIMUM]);
+        pw.print(':');
+        pw.print(table[offset + PSS_AVERAGE]);
+        pw.print(':');
+        pw.print(table[offset + PSS_MAXIMUM]);
+        pw.print(':');
+        pw.print(table[offset + PSS_USS_MINIMUM]);
+        pw.print(':');
+        pw.print(table[offset + PSS_USS_AVERAGE]);
+        pw.print(':');
+        pw.print(table[offset + PSS_USS_MAXIMUM]);
+        pw.print(':');
+        pw.print(table[offset + PSS_RSS_MINIMUM]);
+        pw.print(':');
+        pw.print(table[offset + PSS_RSS_AVERAGE]);
+        pw.print(':');
+        pw.print(table[offset + PSS_RSS_MAXIMUM]);
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder(128);
@@ -1242,26 +1352,26 @@
         }
 
         // Group proc stats by type (screen state + mem state + process state)
-        Map<Integer, Long> durationByState = new HashMap<>();
+        SparseLongArray durationByState = new SparseLongArray();
         boolean didCurState = false;
         for (int i=0; i<mDurations.getKeyCount(); i++) {
             final int key = mDurations.getKeyAt(i);
             final int type = SparseMappingTable.getIdFromKey(key);
             long time = mDurations.getValue(key);
-            if (mCurState == type) {
+            if (mCurCombinedState == type) {
                 didCurState = true;
                 time += now - mStartTime;
             }
             durationByState.put(type, time);
         }
-        if (!didCurState && mCurState != STATE_NOTHING) {
-            durationByState.put(mCurState, now - mStartTime);
+        if (!didCurState && mCurCombinedState != STATE_NOTHING) {
+            durationByState.put(mCurCombinedState, now - mStartTime);
         }
 
         for (int i=0; i<mPssTable.getKeyCount(); i++) {
             final int key = mPssTable.getKeyAt(i);
             final int type = SparseMappingTable.getIdFromKey(key);
-            if (!durationByState.containsKey(type)) {
+            if (durationByState.indexOfKey(type) < 0) {
                 // state without duration should not have stats!
                 continue;
             }
@@ -1273,36 +1383,35 @@
                     type);
 
             long duration = durationByState.get(type);
-            durationByState.remove(type); // remove the key since it is already being dumped.
+            durationByState.delete(type); // remove the key since it is already being dumped.
             proto.write(ProcessStatsProto.State.DURATION_MS, duration);
 
-            proto.write(ProcessStatsProto.State.SAMPLE_SIZE, mPssTable.getValue(key, PSS_SAMPLE_COUNT));
-            ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.PSS,
-                    mPssTable.getValue(key, PSS_MINIMUM),
-                    mPssTable.getValue(key, PSS_AVERAGE),
-                    mPssTable.getValue(key, PSS_MAXIMUM));
-            ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.USS,
-                    mPssTable.getValue(key, PSS_USS_MINIMUM),
-                    mPssTable.getValue(key, PSS_USS_AVERAGE),
-                    mPssTable.getValue(key, PSS_USS_MAXIMUM));
-            ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.RSS,
-                    mPssTable.getValue(key, PSS_RSS_MINIMUM),
-                    mPssTable.getValue(key, PSS_RSS_AVERAGE),
-                    mPssTable.getValue(key, PSS_RSS_MAXIMUM));
+            mPssTable.writeStatsToProtoForKey(proto, key);
 
             proto.end(stateToken);
         }
 
-        for (Map.Entry<Integer, Long> entry : durationByState.entrySet()) {
+        for (int i = 0; i < durationByState.size(); i++) {
             final long stateToken = proto.start(ProcessStatsProto.STATES);
             DumpUtils.printProcStateTagProto(proto,
                     ProcessStatsProto.State.SCREEN_STATE,
                     ProcessStatsProto.State.MEMORY_STATE,
                     ProcessStatsProto.State.PROCESS_STATE,
-                    entry.getKey());
-            proto.write(ProcessStatsProto.State.DURATION_MS, entry.getValue());
+                    durationByState.keyAt(i));
+            proto.write(ProcessStatsProto.State.DURATION_MS, durationByState.valueAt(i));
             proto.end(stateToken);
         }
+
+        final long totalRunningDuration = getTotalRunningDuration(now);
+        if (totalRunningDuration > 0) {
+            final long stateToken = proto.start(ProcessStatsProto.TOTAL_RUNNING_STATE);
+            proto.write(ProcessStatsProto.State.DURATION_MS, totalRunningDuration);
+            if (mTotalRunningPss[PSS_SAMPLE_COUNT] != 0) {
+                PssTable.writeStatsToProto(proto, mTotalRunningPss, 0);
+            }
+            proto.end(stateToken);
+        }
+
         proto.end(token);
     }
 }
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index d35bddd..d088354 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.app.procstats;
 
+import android.content.ComponentName;
 import android.os.Debug;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -157,7 +158,7 @@
     };
 
     // Current version of the parcel format.
-    private static final int PARCEL_VERSION = 27;
+    private static final int PARCEL_VERSION = 34;
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
     private static final int MAGIC = 0x50535454;
 
@@ -168,6 +169,8 @@
     public final ProcessMap<LongSparseArray<PackageState>> mPackages = new ProcessMap<>();
     public final ProcessMap<ProcessState> mProcesses = new ProcessMap<>();
 
+    public final ArrayList<AssociationState.SourceState> mTrackingAssociations = new ArrayList<>();
+
     public final long[] mMemFactorDurations = new long[ADJ_COUNT];
     public int mMemFactor = STATE_NOTHING;
     public long mStartTime;
@@ -250,6 +253,7 @@
                     final PackageState otherState = versions.valueAt(iv);
                     final int NPROCS = otherState.mProcesses.size();
                     final int NSRVS = otherState.mServices.size();
+                    final int NASCS = otherState.mAssociations.size();
                     for (int iproc=0; iproc<NPROCS; iproc++) {
                         ProcessState otherProc = otherState.mProcesses.valueAt(iproc);
                         if (otherProc.getCommonProcess() != otherProc) {
@@ -277,6 +281,14 @@
                                 otherSvc.getProcessName(), otherSvc.getName());
                         thisSvc.add(otherSvc);
                     }
+                    for (int iasc=0; iasc<NASCS; iasc++) {
+                        AssociationState otherAsc = otherState.mAssociations.valueAt(iasc);
+                        if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid
+                                + " association " + otherAsc.getName());
+                        AssociationState thisAsc = getAssociationStateLocked(pkgName, uid, vers,
+                                otherAsc.getProcessName(), otherAsc.getName());
+                        thisAsc.add(otherAsc);
+                    }
                 }
             }
         }
@@ -478,7 +490,16 @@
                             pkgState.mServices.removeAt(isvc);
                         }
                     }
-                    if (pkgState.mProcesses.size() <= 0 && pkgState.mServices.size() <= 0) {
+                    for (int iasc=pkgState.mAssociations.size()-1; iasc>=0; iasc--) {
+                        final AssociationState as = pkgState.mAssociations.valueAt(iasc);
+                        if (as.isInUse()) {
+                            as.resetSafely(now);
+                        } else {
+                            pkgState.mAssociations.removeAt(iasc);
+                        }
+                    }
+                    if (pkgState.mProcesses.size() <= 0 && pkgState.mServices.size() <= 0
+                            && pkgState.mAssociations.size() <= 0) {
                         vpkgs.removeAt(iv);
                     }
                 }
@@ -708,7 +729,7 @@
         }
     }
 
-    private void writeCommonString(Parcel out, String name) {
+    void writeCommonString(Parcel out, String name) {
         Integer index = mCommonStringToIndex.get(name);
         if (index != null) {
             out.writeInt(index);
@@ -720,7 +741,7 @@
         out.writeString(name);
     }
 
-    private String readCommonString(Parcel in, int version) {
+    String readCommonString(Parcel in, int version) {
         if (version <= 9) {
             return in.readString();
         }
@@ -791,6 +812,10 @@
                     for (int isvc=0; isvc<NSRVS; isvc++) {
                         pkgState.mServices.valueAt(isvc).commitStateTime(now);
                     }
+                    final int NASCS = pkgState.mAssociations.size();
+                    for (int iasc=0; iasc<NASCS; iasc++) {
+                        pkgState.mAssociations.valueAt(iasc).commitStateTime(now);
+                    }
                 }
             }
         }
@@ -874,6 +899,14 @@
                         writeCommonString(out, svc.getProcessName());
                         svc.writeToParcel(out, now);
                     }
+                    final int NASCS = pkgState.mAssociations.size();
+                    out.writeInt(NASCS);
+                    for (int iasc=0; iasc<NASCS; iasc++) {
+                        writeCommonString(out, pkgState.mAssociations.keyAt(iasc));
+                        final AssociationState asc = pkgState.mAssociations.valueAt(iasc);
+                        writeCommonString(out, asc.getProcessName());
+                        asc.writeToParcel(this, out, now);
+                    }
                 }
             }
         }
@@ -1078,7 +1111,7 @@
                 while (NVERS > 0) {
                     NVERS--;
                     final long vers = in.readLong();
-                    PackageState pkgState = new PackageState(pkgName, uid);
+                    PackageState pkgState = new PackageState(this, pkgName, uid, vers);
                     LongSparseArray<PackageState> vpkg = mPackages.get(pkgName, uid);
                     if (vpkg == null) {
                         vpkg = new LongSparseArray<>();
@@ -1157,6 +1190,34 @@
                                 + serviceName + " " + uid + " " + serv);
                         pkgState.mServices.put(serviceName, serv);
                     }
+                    int NASCS = in.readInt();
+                    if (NASCS < 0) {
+                        mReadError = "bad package association count: " + NASCS;
+                        return;
+                    }
+                    while (NASCS > 0) {
+                        NASCS--;
+                        String associationName = readCommonString(in, version);
+                        if (associationName == null) {
+                            mReadError = "bad package association name";
+                            return;
+                        }
+                        String processName = readCommonString(in, version);
+                        AssociationState asc = hadData
+                                ? pkgState.mAssociations.get(associationName) : null;
+                        if (asc == null) {
+                            asc = new AssociationState(this, pkgState, associationName,
+                                    processName, null);
+                        }
+                        String errorMsg = asc.readFromParcel(this, in, version);
+                        if (errorMsg != null) {
+                            mReadError = errorMsg;
+                            return;
+                        }
+                        if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " association: "
+                                + associationName + " " + uid + " " + asc);
+                        pkgState.mAssociations.put(associationName, asc);
+                    }
                 }
             }
         }
@@ -1183,33 +1244,38 @@
     public PackageState getPackageStateLocked(String packageName, int uid, long vers) {
         LongSparseArray<PackageState> vpkg = mPackages.get(packageName, uid);
         if (vpkg == null) {
-            vpkg = new LongSparseArray<PackageState>();
+            vpkg = new LongSparseArray<>();
             mPackages.put(packageName, uid, vpkg);
         }
         PackageState as = vpkg.get(vers);
         if (as != null) {
             return as;
         }
-        as = new PackageState(packageName, uid);
+        as = new PackageState(this, packageName, uid, vers);
         vpkg.put(vers, as);
         return as;
     }
 
     public ProcessState getProcessStateLocked(String packageName, int uid, long vers,
             String processName) {
-        final PackageState pkgState = getPackageStateLocked(packageName, uid, vers);
+        return getProcessStateLocked(getPackageStateLocked(packageName, uid, vers), processName);
+    }
+
+    public ProcessState getProcessStateLocked(PackageState pkgState, String processName) {
         ProcessState ps = pkgState.mProcesses.get(processName);
         if (ps != null) {
             return ps;
         }
-        ProcessState commonProc = mProcesses.get(processName, uid);
+        ProcessState commonProc = mProcesses.get(processName, pkgState.mUid);
         if (commonProc == null) {
-            commonProc = new ProcessState(this, packageName, uid, vers, processName);
-            mProcesses.put(processName, uid, commonProc);
+            commonProc = new ProcessState(this, pkgState.mPackageName, pkgState.mUid,
+                    pkgState.mVersionCode, processName);
+            mProcesses.put(processName, pkgState.mUid, commonProc);
             if (DEBUG) Slog.d(TAG, "GETPROC created new common " + commonProc);
         }
         if (!commonProc.isMultiPackage()) {
-            if (packageName.equals(commonProc.getPackage()) && vers == commonProc.getVersion()) {
+            if (pkgState.mPackageName.equals(commonProc.getPackage())
+                    && pkgState.mVersionCode == commonProc.getVersion()) {
                 // This common process is not in use by multiple packages, and
                 // is for the calling package, so we can just use it directly.
                 ps = commonProc;
@@ -1228,7 +1294,7 @@
                 // First let's make a copy of the current process state and put
                 // that under the now unique state for its original package name.
                 final PackageState commonPkgState = getPackageStateLocked(commonProc.getPackage(),
-                        uid, commonProc.getVersion());
+                        pkgState.mUid, commonProc.getVersion());
                 if (commonPkgState != null) {
                     ProcessState cloned = commonProc.clone(now);
                     if (DEBUG) Slog.d(TAG, "GETPROC setting clone to pkg " + commonProc.getPackage()
@@ -1245,18 +1311,31 @@
                             Slog.d(TAG, "GETPROC leaving proc of " + ss);
                         }
                     }
+                    // Also update active associations.
+                    for (int i=commonPkgState.mAssociations.size()-1; i>=0; i--) {
+                        AssociationState as = commonPkgState.mAssociations.valueAt(i);
+                        if (as.getProcess() == commonProc) {
+                            if (DEBUG) Slog.d(TAG, "GETPROC switching association to cloned: "
+                                    + as);
+                            as.setProcess(cloned);
+                        } else if (DEBUG) {
+                            Slog.d(TAG, "GETPROC leaving proc of " + as);
+                        }
+                    }
                 } else {
                     Slog.w(TAG, "Cloning proc state: no package state " + commonProc.getPackage()
-                            + "/" + uid + " for proc " + commonProc.getName());
+                            + "/" + pkgState.mUid + " for proc " + commonProc.getName());
                 }
                 // And now make a fresh new process state for the new package name.
-                ps = new ProcessState(commonProc, packageName, uid, vers, processName, now);
+                ps = new ProcessState(commonProc, pkgState.mPackageName, pkgState.mUid,
+                        pkgState.mVersionCode, processName, now);
                 if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps);
             }
         } else {
             // The common process is for multiple packages, we need to create a
             // separate object for the per-package data.
-            ps = new ProcessState(commonProc, packageName, uid, vers, processName,
+            ps = new ProcessState(commonProc, pkgState.mPackageName, pkgState.mUid,
+                    pkgState.mVersionCode, processName,
                     SystemClock.uptimeMillis());
             if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps);
         }
@@ -1281,8 +1360,58 @@
         return ss;
     }
 
+    public AssociationState getAssociationStateLocked(String packageName, int uid, long vers,
+            String processName, String className) {
+        final ProcessStats.PackageState pkgs = getPackageStateLocked(packageName, uid, vers);
+        AssociationState as = pkgs.mAssociations.get(className);
+        if (as != null) {
+            if (DEBUG) Slog.d(TAG, "GETASC: returning existing " + as);
+            return as;
+        }
+        final ProcessState procs = processName != null
+                ? getProcessStateLocked(packageName, uid, vers, processName) : null;
+        as = new AssociationState(this, pkgs, className, processName, procs);
+        pkgs.mAssociations.put(className, as);
+        if (DEBUG) Slog.d(TAG, "GETASC: creating " + as + " in " + procs);
+        return as;
+    }
+
+    public void updateTrackingAssociationsLocked(int curSeq, long now) {
+        final int NUM = mTrackingAssociations.size();
+        for (int i = NUM - 1; i >= 0; i--) {
+            final AssociationState.SourceState act = mTrackingAssociations.get(i);
+            if (act.mProcStateSeq != curSeq || act.mProcState >= ProcessStats.STATE_HOME) {
+                // If this association did not get touched the last time we computed
+                // process states, or its state ended up down in cached, then we no
+                // longer have a reason to track it at all.
+                act.stopActive(now);
+                act.mInTrackingList = false;
+                act.mProcState = ProcessStats.STATE_NOTHING;
+                mTrackingAssociations.remove(i);
+            } else {
+                final ProcessState proc = act.getAssociationState().getProcess();
+                if (proc != null) {
+                    final int procState = proc.getCombinedState() % STATE_COUNT;
+                    if (act.mProcState == procState) {
+                        act.startActive(now);
+                    } else {
+                        act.stopActive(now);
+                        if (act.mProcState < procState) {
+                            Slog.w(TAG, "Tracking association " + act + " whose proc state "
+                                    + act.mProcState + " is better than process " + proc
+                                    + " proc state " + procState);
+                        }
+                    }
+                } else {
+                    Slog.wtf(TAG, "Tracking association without process: " + act
+                            + " in " + act.getAssociationState());
+                }
+            }
+        }
+    }
+
     public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary,
-            boolean dumpAll, boolean activeOnly) {
+            boolean dumpDetails, boolean dumpAll, boolean activeOnly) {
         long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
                 mStartTime, now);
         boolean sepNeeded = false;
@@ -1304,6 +1433,7 @@
                     final PackageState pkgState = vpkgs.valueAt(iv);
                     final int NPROCS = pkgState.mProcesses.size();
                     final int NSRVS = pkgState.mServices.size();
+                    final int NASCS = pkgState.mAssociations.size();
                     final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
                     if (!pkgMatch) {
                         boolean procMatch = false;
@@ -1318,7 +1448,7 @@
                             continue;
                         }
                     }
-                    if (NPROCS > 0 || NSRVS > 0) {
+                    if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) {
                         if (!printedHeader) {
                             if (sepNeeded) pw.println();
                             pw.println("Per-Package Stats:");
@@ -1353,7 +1483,7 @@
                             proc.dumpProcessState(pw, "        ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
                                     ALL_PROC_STATES, now);
                             proc.dumpPss(pw, "        ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
-                                    ALL_PROC_STATES);
+                                    ALL_PROC_STATES, now);
                             proc.dumpInternalLocked(pw, "        ", dumpAll);
                         }
                     } else {
@@ -1368,7 +1498,7 @@
                             }
                             procs.add(proc);
                         }
-                        DumpUtils.dumpProcessSummaryLocked(pw, "      ", procs,
+                        DumpUtils.dumpProcessSummaryLocked(pw, "      ", "Prc ", procs,
                                 ALL_SCREEN_ADJ, ALL_MEM_ADJ, NON_CACHED_PROC_STATES,
                                 now, totalTime);
                     }
@@ -1378,14 +1508,14 @@
                             continue;
                         }
                         if (activeOnly && !svc.isInUse()) {
-                            pw.print("      (Not active: ");
+                            pw.print("      (Not active service: ");
                                     pw.print(pkgState.mServices.keyAt(isvc)); pw.println(")");
                             continue;
                         }
                         if (dumpAll) {
                             pw.print("      Service ");
                         } else {
-                            pw.print("      * ");
+                            pw.print("      * Svc ");
                         }
                         pw.print(pkgState.mServices.keyAt(isvc));
                         pw.println(":");
@@ -1393,6 +1523,27 @@
                         svc.dumpStats(pw, "        ", "          ", "    ",
                                 now, totalTime, dumpSummary, dumpAll);
                     }
+                    for (int iasc=0; iasc<NASCS; iasc++) {
+                        AssociationState asc = pkgState.mAssociations.valueAt(iasc);
+                        if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
+                            continue;
+                        }
+                        if (activeOnly && !asc.isInUse()) {
+                            pw.print("      (Not active association: ");
+                            pw.print(pkgState.mAssociations.keyAt(iasc)); pw.println(")");
+                            continue;
+                        }
+                        if (dumpAll) {
+                            pw.print("      Association ");
+                        } else {
+                            pw.print("      * Asc ");
+                        }
+                        pw.print(pkgState.mAssociations.keyAt(iasc));
+                        pw.println(":");
+                        pw.print("        Process: "); pw.println(asc.getProcessName());
+                        asc.dumpStats(pw, "        ", "          ", "    ",
+                                now, totalTime, dumpDetails, dumpAll);
+                    }
                 }
             }
         }
@@ -1407,7 +1558,7 @@
                 int uid = uids.keyAt(iu);
                 numTotalProcs++;
                 final ProcessState proc = uids.valueAt(iu);
-                if (proc.hasAnyData()) {
+                if (!proc.hasAnyData()) {
                     continue;
                 }
                 if (!proc.isMultiPackage()) {
@@ -1436,21 +1587,68 @@
                         pw.print(" entries)"); pw.println(":");
                 proc.dumpProcessState(pw, "        ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
                         ALL_PROC_STATES, now);
-                proc.dumpPss(pw, "        ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES);
+                proc.dumpPss(pw, "        ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES, now);
                 proc.dumpInternalLocked(pw, "        ", dumpAll);
             }
         }
+
         if (dumpAll) {
-            pw.println();
+            if (sepNeeded) {
+                pw.println();
+            }
+            sepNeeded = true;
             pw.print("  Total procs: "); pw.print(numShownProcs);
                     pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
+            if (mTrackingAssociations.size() > 0) {
+                pw.println();
+                pw.println("Tracking associations:");
+                for (int i = 0; i < mTrackingAssociations.size(); i++) {
+                    final AssociationState.SourceState src = mTrackingAssociations.get(i);
+                    final AssociationState asc = src.getAssociationState();
+                    pw.print("  #");
+                    pw.print(i);
+                    pw.print(": ");
+                    pw.print(asc.getProcessName());
+                    pw.print("/");
+                    UserHandle.formatUid(pw, asc.getUid());
+                    pw.print(" <- ");
+                    pw.print(src.getProcessName());
+                    pw.print("/");
+                    UserHandle.formatUid(pw, src.getUid());
+                    pw.println(":");
+                    pw.print("    Tracking for: ");
+                    TimeUtils.formatDuration(now - src.mTrackingUptime, pw);
+                    pw.println();
+                    pw.print("    Component: ");
+                    pw.print(new ComponentName(asc.getPackage(), asc.getName())
+                            .flattenToShortString());
+                    pw.println();
+                    pw.print("    Proc state: ");
+                    if (src.mProcState != ProcessStats.STATE_NOTHING) {
+                        pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
+                    } else {
+                        pw.print("--");
+                    }
+                    pw.print(" #");
+                    pw.println(src.mProcStateSeq);
+                    pw.print("    Process: ");
+                    pw.println(asc.getProcess());
+                    if (src.mActiveCount > 0) {
+                        pw.print("    Active count ");
+                        pw.print(src.mActiveCount);
+                        pw.print(": ");
+                        asc.dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
+                        pw.println();
+                    }
+                }
+            }
         }
 
         if (sepNeeded) {
             pw.println();
         }
         if (dumpSummary) {
-            pw.println("Summary:");
+            pw.println("Process summary:");
             dumpSummaryLocked(pw, reqPackage, now, activeOnly);
         } else {
             dumpTotalsLocked(pw, now);
@@ -1466,13 +1664,15 @@
             pw.print("  mRunning="); pw.println(mRunning);
         }
 
-        dumpFragmentationLocked(pw);
+        if (reqPackage == null) {
+            dumpFragmentationLocked(pw);
+        }
     }
 
     public void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now, boolean activeOnly) {
         long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
                 mStartTime, now);
-        dumpFilteredSummaryLocked(pw, null, "  ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+        dumpFilteredSummaryLocked(pw, null, "  ", null, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
                 ALL_PROC_STATES, NON_CACHED_PROC_STATES, now, totalTime, reqPackage, activeOnly);
         pw.println();
         dumpTotalsLocked(pw, now);
@@ -1607,7 +1807,7 @@
         pw.println();
     }
 
-    void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix,
+    void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix, String prcLabel,
             int[] screenStates, int[] memStates, int[] procStates,
             int[] sortProcStates, long now, long totalTime, String reqPackage, boolean activeOnly) {
         ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
@@ -1617,7 +1817,7 @@
                 pw.println();
                 pw.println(header);
             }
-            DumpUtils.dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates,
+            DumpUtils.dumpProcessSummaryLocked(pw, prefix, prcLabel, procs, screenStates, memStates,
                     sortProcStates, now, totalTime);
         }
     }
@@ -1708,6 +1908,7 @@
                     final PackageState pkgState = vpkgs.valueAt(iv);
                     final int NPROCS = pkgState.mProcesses.size();
                     final int NSRVS = pkgState.mServices.size();
+                    final int NASCS = pkgState.mAssociations.size();
                     for (int iproc=0; iproc<NPROCS; iproc++) {
                         ProcessState proc = pkgState.mProcesses.valueAt(iproc);
                         proc.dumpPackageProcCheckin(pw, pkgName, uid, vers,
@@ -1719,6 +1920,12 @@
                         final ServiceState svc = pkgState.mServices.valueAt(isvc);
                         svc.dumpTimesCheckin(pw, pkgName, uid, vers, serviceName, now);
                     }
+                    for (int iasc=0; iasc<NASCS; iasc++) {
+                        final String associationName = DumpUtils.collapseString(pkgName,
+                                pkgState.mAssociations.keyAt(iasc));
+                        final AssociationState asc = pkgState.mAssociations.valueAt(iasc);
+                        asc.dumpTimesCheckin(pw, pkgName, uid, vers, associationName, now);
+                    }
                 }
             }
         }
@@ -1850,6 +2057,7 @@
     final public static class ProcessStateHolder {
         public final long appVersion;
         public ProcessState state;
+        public PackageState pkg;
 
         public ProcessStateHolder(long _appVersion) {
             appVersion = _appVersion;
@@ -1857,16 +2065,35 @@
     }
 
     public static final class PackageState {
-        public final ArrayMap<String, ProcessState> mProcesses
-                = new ArrayMap<String, ProcessState>();
-        public final ArrayMap<String, ServiceState> mServices
-                = new ArrayMap<String, ServiceState>();
+        public final ProcessStats mProcessStats;
+        public final ArrayMap<String, ProcessState> mProcesses = new ArrayMap<>();
+        public final ArrayMap<String, ServiceState> mServices = new ArrayMap<>();
+        public final ArrayMap<String, AssociationState> mAssociations = new ArrayMap<>();
         public final String mPackageName;
         public final int mUid;
+        public final long mVersionCode;
 
-        public PackageState(String packageName, int uid) {
+        public PackageState(ProcessStats procStats, String packageName, int uid, long versionCode) {
+            mProcessStats = procStats;
             mUid = uid;
             mPackageName = packageName;
+            mVersionCode = versionCode;
+        }
+
+        public AssociationState getAssociationStateLocked(ProcessState proc, String className) {
+            AssociationState as = mAssociations.get(className);
+            if (as != null) {
+                if (DEBUG) Slog.d(TAG, "GETASC: returning existing " + as);
+                if (proc != null) {
+                    as.setProcess(proc);
+                }
+                return as;
+            }
+            as = new AssociationState(mProcessStats, this, className, proc.getName(),
+                    proc);
+            mAssociations.put(className, as);
+            if (DEBUG) Slog.d(TAG, "GETASC: creating " + as + " in " + proc.getName());
+            return as;
         }
     }
 
diff --git a/core/java/com/android/internal/app/procstats/PssTable.java b/core/java/com/android/internal/app/procstats/PssTable.java
index 1e7c566..f858e55 100644
--- a/core/java/com/android/internal/app/procstats/PssTable.java
+++ b/core/java/com/android/internal/app/procstats/PssTable.java
@@ -28,6 +28,10 @@
 import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM;
 import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
 
+import android.service.procstats.ProcessStatsProto;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
+
 /**
  * Class to accumulate PSS data.
  */
@@ -46,18 +50,17 @@
     public void mergeStats(PssTable that) {
         final int N = that.getKeyCount();
         for (int i=0; i<N; i++) {
-            final int key = that.getKeyAt(i);
-            final int state = SparseMappingTable.getIdFromKey(key);
-            mergeStats(state, (int)that.getValue(key, PSS_SAMPLE_COUNT),
-                    that.getValue(key, PSS_MINIMUM),
-                    that.getValue(key, PSS_AVERAGE),
-                    that.getValue(key, PSS_MAXIMUM),
-                    that.getValue(key, PSS_USS_MINIMUM),
-                    that.getValue(key, PSS_USS_AVERAGE),
-                    that.getValue(key, PSS_USS_MAXIMUM),
-                    that.getValue(key, PSS_RSS_MINIMUM),
-                    that.getValue(key, PSS_RSS_AVERAGE),
-                    that.getValue(key, PSS_RSS_MAXIMUM));
+            final int thatKey = that.getKeyAt(i);
+            final int state = SparseMappingTable.getIdFromKey(thatKey);
+
+            final int key = getOrAddKey((byte)state, PSS_COUNT);
+            final long[] stats = getArrayForKey(key);
+            final int statsIndex = SparseMappingTable.getIndexFromKey(key);
+
+            final long[] thatStats = that.getArrayForKey(thatKey);
+            final int thatStatsIndex = SparseMappingTable.getIndexFromKey(thatKey);
+
+            mergeStats(stats, statsIndex, thatStats, thatStatsIndex);
         }
     }
 
@@ -68,64 +71,100 @@
     public void mergeStats(int state, int inCount, long minPss, long avgPss, long maxPss,
             long minUss, long avgUss, long maxUss, long minRss, long avgRss, long maxRss) {
         final int key = getOrAddKey((byte)state, PSS_COUNT);
-        final long count = getValue(key, PSS_SAMPLE_COUNT);
+        final long[] stats = getArrayForKey(key);
+        final int statsIndex = SparseMappingTable.getIndexFromKey(key);
+        mergeStats(stats, statsIndex, inCount, minPss, avgPss, maxPss, minUss, avgUss, maxUss,
+                minRss, avgRss, maxRss);
+    }
+
+    public static void mergeStats(final long[] stats, final int statsIndex,
+            final long[] thatStats, int thatStatsIndex) {
+        mergeStats(stats, statsIndex, (int)thatStats[thatStatsIndex + PSS_SAMPLE_COUNT],
+                thatStats[thatStatsIndex + PSS_MINIMUM],
+                thatStats[thatStatsIndex + PSS_AVERAGE],
+                thatStats[thatStatsIndex + PSS_MAXIMUM],
+                thatStats[thatStatsIndex + PSS_USS_MINIMUM],
+                thatStats[thatStatsIndex + PSS_USS_AVERAGE],
+                thatStats[thatStatsIndex + PSS_USS_MAXIMUM],
+                thatStats[thatStatsIndex + PSS_RSS_MINIMUM],
+                thatStats[thatStatsIndex + PSS_RSS_AVERAGE],
+                thatStats[thatStatsIndex + PSS_RSS_MAXIMUM]);
+    }
+
+    public static void mergeStats(final long[] stats, final int statsIndex, final int inCount,
+            final long minPss, final long avgPss, final long maxPss,
+            final long minUss, final long avgUss, final long maxUss,
+            final long minRss, final long avgRss, final long maxRss) {
+        final long count = stats[statsIndex + PSS_SAMPLE_COUNT];
         if (count == 0) {
-            setValue(key, PSS_SAMPLE_COUNT, inCount);
-            setValue(key, PSS_MINIMUM, minPss);
-            setValue(key, PSS_AVERAGE, avgPss);
-            setValue(key, PSS_MAXIMUM, maxPss);
-            setValue(key, PSS_USS_MINIMUM, minUss);
-            setValue(key, PSS_USS_AVERAGE, avgUss);
-            setValue(key, PSS_USS_MAXIMUM, maxUss);
-            setValue(key, PSS_RSS_MINIMUM, minRss);
-            setValue(key, PSS_RSS_AVERAGE, avgRss);
-            setValue(key, PSS_RSS_MAXIMUM, maxRss);
+            stats[statsIndex + PSS_SAMPLE_COUNT] = inCount;
+            stats[statsIndex + PSS_MINIMUM] = minPss;
+            stats[statsIndex + PSS_AVERAGE] = avgPss;
+            stats[statsIndex + PSS_MAXIMUM] = maxPss;
+            stats[statsIndex + PSS_USS_MINIMUM] = minUss;
+            stats[statsIndex + PSS_USS_AVERAGE] = avgUss;
+            stats[statsIndex + PSS_USS_MAXIMUM] = maxUss;
+            stats[statsIndex + PSS_RSS_MINIMUM] = minRss;
+            stats[statsIndex + PSS_RSS_AVERAGE] = avgRss;
+            stats[statsIndex + PSS_RSS_MAXIMUM] = maxRss;
         } else {
-            setValue(key, PSS_SAMPLE_COUNT, count + inCount);
+            stats[statsIndex + PSS_SAMPLE_COUNT] = count + inCount;
 
-            long val;
-
-            val = getValue(key, PSS_MINIMUM);
-            if (val > minPss) {
-                setValue(key, PSS_MINIMUM, minPss);
+            if (stats[statsIndex + PSS_MINIMUM] > minPss) {
+                stats[statsIndex + PSS_MINIMUM] = minPss;
             }
 
-            val = getValue(key, PSS_AVERAGE);
-            setValue(key, PSS_AVERAGE,
-                    (long)(((val*(double)count)+(avgPss*(double)inCount)) / (count+inCount)));
+            stats[statsIndex + PSS_AVERAGE] = (long)(((stats[statsIndex + PSS_AVERAGE]
+                    * (double)count) + (avgPss * (double)inCount)) / (count + inCount));
 
-            val = getValue(key, PSS_MAXIMUM);
-            if (val < maxPss) {
-                setValue(key, PSS_MAXIMUM, maxPss);
+            if (stats[statsIndex + PSS_MAXIMUM] < maxPss) {
+                stats[statsIndex + PSS_MAXIMUM] = maxPss;
             }
 
-            val = getValue(key, PSS_USS_MINIMUM);
-            if (val > minUss) {
-                setValue(key, PSS_USS_MINIMUM, minUss);
+            if (stats[statsIndex + PSS_USS_MINIMUM] > minUss) {
+                stats[statsIndex + PSS_USS_MINIMUM] = minUss;
             }
 
-            val = getValue(key, PSS_USS_AVERAGE);
-            setValue(key, PSS_USS_AVERAGE,
-                    (long)(((val*(double)count)+(avgUss*(double)inCount)) / (count+inCount)));
+            stats[statsIndex + PSS_USS_AVERAGE] = (long)(((stats[statsIndex + PSS_USS_AVERAGE]
+                    * (double)count) + (avgUss * (double)inCount)) / (count + inCount));
 
-            val = getValue(key, PSS_USS_MAXIMUM);
-            if (val < maxUss) {
-                setValue(key, PSS_USS_MAXIMUM, maxUss);
+            if (stats[statsIndex + PSS_USS_MAXIMUM] < maxUss) {
+                stats[statsIndex + PSS_USS_MAXIMUM] = maxUss;
             }
 
-            val = getValue(key, PSS_RSS_MINIMUM);
-            if (val > minUss) {
-                setValue(key, PSS_RSS_MINIMUM, minUss);
+            if (stats[statsIndex + PSS_RSS_MINIMUM] > minRss) {
+                stats[statsIndex + PSS_RSS_MINIMUM] = minRss;
             }
 
-            val = getValue(key, PSS_RSS_AVERAGE);
-            setValue(key, PSS_RSS_AVERAGE,
-                    (long)(((val*(double)count)+(avgUss*(double)inCount)) / (count+inCount)));
+            stats[statsIndex + PSS_RSS_AVERAGE] = (long)(((stats[statsIndex + PSS_RSS_AVERAGE]
+                    * (double)count) + (avgRss * (double)inCount)) / (count + inCount));
 
-            val = getValue(key, PSS_RSS_MAXIMUM);
-            if (val < maxUss) {
-                setValue(key, PSS_RSS_MAXIMUM, maxUss);
+            if (stats[statsIndex + PSS_RSS_MAXIMUM] < maxRss) {
+                stats[statsIndex + PSS_RSS_MAXIMUM] = maxRss;
             }
         }
     }
+
+    public void writeStatsToProtoForKey(ProtoOutputStream proto, int key) {
+        final long[] stats = getArrayForKey(key);
+        final int statsIndex = SparseMappingTable.getIndexFromKey(key);
+        writeStatsToProto(proto, stats, statsIndex);
+    }
+
+    public static void writeStatsToProto(ProtoOutputStream proto, final long[] stats,
+            final int statsIndex) {
+        proto.write(ProcessStatsProto.State.SAMPLE_SIZE, stats[statsIndex + PSS_SAMPLE_COUNT]);
+        ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.PSS,
+                stats[statsIndex + PSS_MINIMUM],
+                stats[statsIndex + PSS_AVERAGE],
+                stats[statsIndex + PSS_MAXIMUM]);
+        ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.USS,
+                stats[statsIndex + PSS_USS_MINIMUM],
+                stats[statsIndex + PSS_USS_AVERAGE],
+                stats[statsIndex + PSS_USS_MAXIMUM]);
+        ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.RSS,
+                stats[statsIndex + PSS_RSS_MINIMUM],
+                stats[statsIndex + PSS_RSS_AVERAGE],
+                stats[statsIndex + PSS_RSS_MAXIMUM]);
+    }
 }
diff --git a/core/java/com/android/internal/app/procstats/ServiceState.java b/core/java/com/android/internal/app/procstats/ServiceState.java
index 650de2ea..04e61e0 100644
--- a/core/java/com/android/internal/app/procstats/ServiceState.java
+++ b/core/java/com/android/internal/app/procstats/ServiceState.java
@@ -18,30 +18,13 @@
 
 
 import android.os.Parcel;
-import android.os.Parcelable;
 import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.text.format.DateFormat;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.DebugUtils;
-import android.util.Log;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.util.TimeUtils;
 
-import com.android.internal.app.procstats.ProcessStats;
 import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Objects;
 
 public final class ServiceState {
     private static final String TAG = "ProcessStats";
@@ -51,7 +34,8 @@
     public static final int SERVICE_STARTED = 1;
     public static final int SERVICE_BOUND = 2;
     public static final int SERVICE_EXEC = 3;
-    public static final int SERVICE_COUNT = 4;
+    public static final int SERVICE_FOREGROUND = 4;
+    public static final int SERVICE_COUNT = 5;
 
     private final String mPackage;
     private final String mProcessName;
@@ -79,6 +63,10 @@
     private int mExecState = STATE_NOTHING;
     private long mExecStartTime;
 
+    private int mForegroundCount;
+    private int mForegroundState = STATE_NOTHING;
+    private long mForegroundStartTime;
+
     public ServiceState(ProcessStats processStats, String pkg, String name,
             String processName, ProcessState proc) {
         mPackage = pkg;
@@ -121,6 +109,9 @@
             if (mExecState != ProcessStats.STATE_NOTHING) {
                 setExecuting(true, memFactor, now);
             }
+            if (mForegroundState != ProcessStats.STATE_NOTHING) {
+                setForeground(true, memFactor, now);
+            }
         }
     }
 
@@ -133,7 +124,8 @@
                 // There was already an old owner, reset this object for its
                 // new owner.
                 mOwner = newOwner;
-                if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
+                if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING
+                        || mForegroundState != STATE_NOTHING) {
                     long now = SystemClock.uptimeMillis();
                     if (mStarted) {
                         if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
@@ -153,6 +145,12 @@
                                 + mPackage + " service=" + mName + " proc=" + mProc);
                         setExecuting(false, 0, now);
                     }
+                    if (mForegroundState != STATE_NOTHING) {
+                        if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
+                                + " from " + mOwner + " while foreground: pkg="
+                                + mPackage + " service=" + mName + " proc=" + mProc);
+                        setForeground(false, 0, now);
+                    }
                 }
             }
         }
@@ -161,7 +159,8 @@
     public void clearCurrentOwner(Object owner, boolean silently) {
         if (mOwner == owner) {
             mProc.decActiveServices(mName);
-            if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
+            if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING
+                    || mForegroundState != STATE_NOTHING) {
                 long now = SystemClock.uptimeMillis();
                 if (mStarted) {
                     if (!silently) {
@@ -187,6 +186,14 @@
                     }
                     setExecuting(false, 0, now);
                 }
+                if (mForegroundState != STATE_NOTHING) {
+                    if (!silently) {
+                        Slog.wtfStack(TAG, "Service owner " + owner
+                                + " cleared while foreground: pkg=" + mPackage + " service="
+                                + mName + " proc=" + mProc);
+                    }
+                    setForeground(false, 0, now);
+                }
             }
             mOwner = null;
         }
@@ -206,6 +213,7 @@
         mStartedCount += other.mStartedCount;
         mBoundCount += other.mBoundCount;
         mExecCount += other.mExecCount;
+        mForegroundCount += other.mForegroundCount;
     }
 
     public void resetSafely(long now) {
@@ -214,7 +222,9 @@
         mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0;
         mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0;
         mExecCount = mExecState != STATE_NOTHING ? 1 : 0;
-        mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime = now;
+        mForegroundCount = mForegroundState != STATE_NOTHING ? 1 : 0;
+        mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime =
+                mForegroundStartTime = now;
     }
 
     public void writeToParcel(Parcel out, long now) {
@@ -223,6 +233,7 @@
         out.writeInt(mStartedCount);
         out.writeInt(mBoundCount);
         out.writeInt(mExecCount);
+        out.writeInt(mForegroundCount);
     }
 
     public boolean readFromParcel(Parcel in) {
@@ -233,6 +244,7 @@
         mStartedCount = in.readInt();
         mBoundCount = in.readInt();
         mExecCount = in.readInt();
+        mForegroundCount = in.readInt();
         return true;
     }
 
@@ -257,11 +269,17 @@
                     now - mExecStartTime);
             mExecStartTime = now;
         }
+        if (mForegroundState != STATE_NOTHING) {
+            mDurations.addDuration(SERVICE_FOREGROUND + (mForegroundState*SERVICE_COUNT),
+                    now - mForegroundStartTime);
+            mForegroundStartTime = now;
+        }
     }
 
     private void updateRunning(int memFactor, long now) {
         final int state = (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING
-                || mExecState != STATE_NOTHING) ? memFactor : STATE_NOTHING;
+                || mExecState != STATE_NOTHING || mForegroundState != STATE_NOTHING)
+                ? memFactor : STATE_NOTHING;
         if (mRunState != state) {
             if (mRunState != STATE_NOTHING) {
                 mDurations.addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT),
@@ -348,6 +366,24 @@
         }
     }
 
+    public void setForeground(boolean foreground, int memFactor, long now) {
+        if (mOwner == null) {
+            Slog.wtf(TAG, "Foregrounding service " + this + " without owner");
+        }
+        final int state = foreground ? memFactor : STATE_NOTHING;
+        if (mForegroundState != state) {
+            if (mForegroundState != STATE_NOTHING) {
+                mDurations.addDuration(SERVICE_FOREGROUND + (mForegroundState*SERVICE_COUNT),
+                        now - mForegroundStartTime);
+            } else if (foreground) {
+                mForegroundCount++;
+            }
+            mForegroundState = state;
+            mForegroundStartTime = now;
+            updateRunning(memFactor, now);
+        }
+    }
+
     public long getDuration(int opType, int curState, long startTime, int memFactor,
             long now) {
         int state = opType + (memFactor*SERVICE_COUNT);
@@ -366,6 +402,9 @@
         dumpStats(pw, prefix, prefixInner, headerPrefix, "Started",
                 mStartedCount, ServiceState.SERVICE_STARTED, mStartedState,
                 mStartedStartTime, now, totalTime, !dumpSummary || dumpAll);
+        dumpStats(pw, prefix, prefixInner, headerPrefix, "Foreground",
+                mForegroundCount, ServiceState.SERVICE_FOREGROUND, mForegroundState,
+                mForegroundStartTime, now, totalTime, !dumpSummary || dumpAll);
         dumpStats(pw, prefix, prefixInner, headerPrefix, "Bound",
                 mBoundCount, ServiceState.SERVICE_BOUND, mBoundState,
                 mBoundStartTime, now, totalTime, !dumpSummary || dumpAll);
@@ -393,11 +432,19 @@
                 pw.print(" op count "); pw.print(count); pw.println(":");
                 dumpTime(pw, prefixInner, serviceType, state, startTime, now);
             } else {
-                long myTime = dumpTime(null, null, serviceType, state, startTime, now);
+                long myTime = dumpTimeInternal(null, null, serviceType, state, startTime, now,
+                        true);
                 pw.print(prefix); pw.print(headerPrefix); pw.print(header);
                 pw.print(" count "); pw.print(count);
                 pw.print(" / time ");
+                boolean isRunning = myTime < 0;
+                if (isRunning) {
+                    myTime = -myTime;
+                }
                 DumpUtils.printPercent(pw, (double)myTime/(double)totalTime);
+                if (isRunning) {
+                    pw.print(" (running)");
+                }
                 pw.println();
             }
         }
@@ -405,8 +452,14 @@
 
     public long dumpTime(PrintWriter pw, String prefix,
             int serviceType, int curState, long curStartTime, long now) {
+        return dumpTimeInternal(pw, prefix, serviceType, curState, curStartTime, now, false);
+    }
+
+    long dumpTimeInternal(PrintWriter pw, String prefix,
+            int serviceType, int curState, long curStartTime, long now, boolean negativeIfRunning) {
         long totalTime = 0;
         int printedScreen = -1;
+        boolean isRunning = false;
         for (int iscreen=0; iscreen<ProcessStats.ADJ_COUNT; iscreen+=ProcessStats.ADJ_SCREEN_MOD) {
             int printedMem = -1;
             for (int imem=0; imem<ProcessStats.ADJ_MEM_FACTOR_COUNT; imem++) {
@@ -415,6 +468,7 @@
                 String running = "";
                 if (curState == state && pw != null) {
                     running = " (running)";
+                    isRunning = true;
                 }
                 if (time != 0) {
                     if (pw != null) {
@@ -438,7 +492,7 @@
             TimeUtils.formatDuration(totalTime, pw);
             pw.println();
         }
-        return totalTime;
+        return (isRunning && negativeIfRunning) ? -totalTime : totalTime;
     }
 
     public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers,
@@ -447,6 +501,9 @@
                 ServiceState.SERVICE_RUN, mRunCount, mRunState, mRunStartTime, now);
         dumpTimeCheckin(pw, "pkgsvc-start", pkgName, uid, vers, serviceName,
                 ServiceState.SERVICE_STARTED, mStartedCount, mStartedState, mStartedStartTime, now);
+        dumpTimeCheckin(pw, "pkgsvc-fg", pkgName, uid, vers, serviceName,
+                ServiceState.SERVICE_FOREGROUND, mForegroundCount, mForegroundState,
+                mForegroundStartTime, now);
         dumpTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, vers, serviceName,
                 ServiceState.SERVICE_BOUND, mBoundCount, mBoundState, mBoundStartTime, now);
         dumpTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, vers, serviceName,
@@ -497,6 +554,6 @@
     public String toString() {
         return "ServiceState{" + Integer.toHexString(System.identityHashCode(this))
                 + " " + mName + " pkg=" + mPackage + " proc="
-                + Integer.toHexString(System.identityHashCode(this)) + "}";
+                + Integer.toHexString(System.identityHashCode(mProc)) + "}";
     }
 }
diff --git a/core/java/com/android/internal/app/procstats/SparseMappingTable.java b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
index 91b2054..6b8703d 100644
--- a/core/java/com/android/internal/app/procstats/SparseMappingTable.java
+++ b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
@@ -175,7 +175,6 @@
          * Get the value for the given key and offset from that key.
          *
          * @param key   A key as obtained from getKey or getOrAddKey.
-         * @param value The value to set.
          */
         public long getValue(int key) {
             return getValue(key, 0);
@@ -187,7 +186,6 @@
          * @param key   A key as obtained from getKey or getOrAddKey.
          * @param index The offset from that key.  Must be less than the count
          *              provided to getOrAddKey when the space was allocated.
-         * @param value The value to set.
          *
          * @return the value, or 0 in case of an error
          */
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index f4b7032..d0f0272 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -32,10 +32,9 @@
 import android.system.Os;
 import android.system.StructStat;
 import android.util.ArrayMap;
+import android.util.Base64;
 import android.util.Log;
 
-import com.android.org.bouncycastle.util.encoders.Base64;
-
 import libcore.io.IoUtils;
 
 import java.io.BufferedOutputStream;
@@ -323,7 +322,7 @@
         BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor());
         while (changeSet.readNextHeader()) {
             String key = changeSet.getKey();
-            String base64Key = new String(Base64.encode(key.getBytes()));
+            String base64Key = new String(Base64.encode(key.getBytes(), Base64.NO_WRAP));
             int dataSize = changeSet.getDataSize();
             if (DEBUG) {
                 Log.v(TAG, "  Delta operation key " + key + "   size " + dataSize
@@ -705,7 +704,7 @@
 
         public DecodedFilename(File f) {
             file = f;
-            key = new String(Base64.decode(f.getName()));
+            key = new String(Base64.decode(f.getName(), Base64.DEFAULT));
         }
 
         @Override
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index b591163..f89a9d9 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -136,7 +136,7 @@
             return null;
         }
 
-        String mimeType = getTypeForFile(file);
+        String mimeType = getDocumentType(documentId);
         if (!MetadataReader.isSupportedMimeType(mimeType)) {
             return null;
         }
@@ -418,7 +418,19 @@
     @Override
     public String getDocumentType(String documentId) throws FileNotFoundException {
         final File file = getFileForDocId(documentId);
-        return getTypeForFile(file);
+        if (file.isDirectory()) {
+            return Document.MIME_TYPE_DIR;
+        } else {
+            final int lastDot = documentId.lastIndexOf('.');
+            if (lastDot >= 0) {
+                final String extension = documentId.substring(lastDot + 1).toLowerCase();
+                final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+                if (mime != null) {
+                    return mime;
+                }
+            }
+            return MIMETYPE_OCTET_STREAM;
+        }
     }
 
     @Override
@@ -483,7 +495,7 @@
             }
         }
 
-        final String mimeType = getTypeForFile(file);
+        final String mimeType = getDocumentType(docId);
         final String displayName = file.getName();
         if (mimeType.startsWith("image/")) {
             flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
@@ -510,31 +522,10 @@
         return row;
     }
 
-    private static String getTypeForFile(File file) {
-        if (file.isDirectory()) {
-            return Document.MIME_TYPE_DIR;
-        } else {
-            return getTypeForName(file.getName());
-        }
-    }
-
     protected boolean typeSupportsMetadata(String mimeType) {
         return MetadataReader.isSupportedMimeType(mimeType);
     }
 
-    private static String getTypeForName(String name) {
-        final int lastDot = name.lastIndexOf('.');
-        if (lastDot >= 0) {
-            final String extension = name.substring(lastDot + 1).toLowerCase();
-            final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
-            if (mime != null) {
-                return mime;
-            }
-        }
-
-        return MIMETYPE_OCTET_STREAM;
-    }
-
     protected final File getFileForDocId(String docId) throws FileNotFoundException {
         return getFileForDocId(docId, false);
     }
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index 26fb6b64..07bb453 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -27,9 +27,11 @@
 public class AmbientDisplayConfiguration {
 
     private final Context mContext;
+    private final boolean mAlwaysOnByDefault;
 
     public AmbientDisplayConfiguration(Context context) {
         mContext = context;
+        mAlwaysOnByDefault = mContext.getResources().getBoolean(R.bool.config_dozeAlwaysOnEnabled);
     }
 
     public boolean enabled(int user) {
@@ -101,8 +103,8 @@
     }
 
     public boolean alwaysOnEnabled(int user) {
-        return boolSettingDefaultOn(Settings.Secure.DOZE_ALWAYS_ON, user) && alwaysOnAvailable()
-                && !accessibilityInversionEnabled(user);
+        return boolSetting(Settings.Secure.DOZE_ALWAYS_ON, user, mAlwaysOnByDefault ? 1 : 0)
+                && alwaysOnAvailable() && !accessibilityInversionEnabled(user);
     }
 
     public boolean alwaysOnAvailable() {
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index c4d08c7..d1c2799 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -194,7 +194,7 @@
                 reader.finishLine();
             }
         } catch (NullPointerException|NumberFormatException e) {
-            throw new ProtocolException("problem parsing stats", e);
+            throw protocolExceptionWithCause("problem parsing stats", e);
         } finally {
             IoUtils.closeQuietly(reader);
             StrictMode.setThreadPolicy(savedPolicy);
@@ -244,7 +244,7 @@
                 reader.finishLine();
             }
         } catch (NullPointerException|NumberFormatException e) {
-            throw new ProtocolException("problem parsing stats", e);
+            throw protocolExceptionWithCause("problem parsing stats", e);
         } finally {
             IoUtils.closeQuietly(reader);
             StrictMode.setThreadPolicy(savedPolicy);
@@ -341,7 +341,7 @@
                 reader.finishLine();
             }
         } catch (NullPointerException|NumberFormatException e) {
-            throw new ProtocolException("problem parsing idx " + idx, e);
+            throw protocolExceptionWithCause("problem parsing idx " + idx, e);
         } finally {
             IoUtils.closeQuietly(reader);
             StrictMode.setThreadPolicy(savedPolicy);
@@ -378,4 +378,10 @@
 
     @VisibleForTesting
     public static native int nativeReadNetworkStatsDev(NetworkStats stats);
+
+    private static ProtocolException protocolExceptionWithCause(String message, Throwable cause) {
+        ProtocolException pe = new ProtocolException(message);
+        pe.initCause(cause);
+        return pe;
+    }
 }
diff --git a/core/java/com/android/internal/net/OWNERS b/core/java/com/android/internal/net/OWNERS
index ef44ef7..050cb5c 100644
--- a/core/java/com/android/internal/net/OWNERS
+++ b/core/java/com/android/internal/net/OWNERS
@@ -1,7 +1,9 @@
 set noparent
 
+codewiz@google.com
 ek@google.com
 jchalard@google.com
 jsharkey@android.com
 lorenzo@google.com
+reminv@google.com
 satk@google.com
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 921f1fe..5ce5bcd 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -35,6 +35,7 @@
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -195,4 +196,37 @@
             return new VpnConfig[size];
         }
     };
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+                .append("VpnConfig")
+                .append("{ user=").append(user)
+                .append(", interface=").append(interfaze)
+                .append(", session=").append(session)
+                .append(", mtu=").append(mtu)
+                .append(", addresses=").append(toString(addresses))
+                .append(", routes=").append(toString(routes))
+                .append(", dns=").append(toString(dnsServers))
+                .append(", searchDomains=").append(toString(searchDomains))
+                .append(", allowedApps=").append(toString(allowedApplications))
+                .append(", disallowedApps=").append(toString(disallowedApplications))
+                .append(", configureIntent=").append(configureIntent)
+                .append(", startTime=").append(startTime)
+                .append(", legacy=").append(legacy)
+                .append(", blocking=").append(blocking)
+                .append(", allowBypass=").append(allowBypass)
+                .append(", allowIPv4=").append(allowIPv4)
+                .append(", allowIPv6=").append(allowIPv6)
+                .append(", underlyingNetworks=").append(Arrays.toString(underlyingNetworks))
+                .append("}")
+                .toString();
+    }
+
+    static <T> String toString(List<T> ls) {
+        if (ls == null) {
+            return "null";
+        }
+        return Arrays.toString(ls.toArray());
+    }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index a6b29c5..061011b 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -31,6 +31,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SELinux;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -1031,6 +1032,10 @@
         try {
             ParcelFileDescriptor pfd = service.getStatisticsStream();
             if (pfd != null) {
+                if (false) {
+                    Log.d(TAG, "selinux context: "
+                            + SELinux.getFileContext(pfd.getFileDescriptor()));
+                }
                 try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
                     byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor()));
                     Parcel parcel = Parcel.obtain();
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
new file mode 100644
index 0000000..24ad751
--- /dev/null
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -0,0 +1,455 @@
+/*
+ * 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.os.BatteryStats;
+import android.os.Parcel;
+import android.os.StatFs;
+import android.os.SystemClock;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ParseUtils;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * BatteryStatsHistory encapsulates battery history files.
+ * Battery history record is appended into buffer {@link #mHistoryBuffer} and backed up into
+ * {@link #mActiveFile}.
+ * When {@link #mHistoryBuffer} size reaches {@link BatteryStatsImpl.Constants#MAX_HISTORY_BUFFER},
+ * current mActiveFile is closed and a new mActiveFile is open.
+ * History files are under directory /data/system/battery-history/.
+ * History files have name battery-history-<num>.bin. The file number <num> starts from zero and
+ * grows sequentially.
+ * The mActiveFile is always the highest numbered history file.
+ * The lowest number file is always the oldest file.
+ * The highest number file is always the newest file.
+ * The file number grows sequentially and we never skip number.
+ * When count of history files exceeds {@link BatteryStatsImpl.Constants#MAX_HISTORY_FILES},
+ * the lowest numbered file is deleted and a new file is open.
+ *
+ * All interfaces in BatteryStatsHistory should only be called by BatteryStatsImpl and protected by
+ * locks on BatteryStatsImpl object.
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class BatteryStatsHistory {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "BatteryStatsHistory";
+    public static final String HISTORY_DIR = "battery-history";
+    public static final String FILE_SUFFIX = ".bin";
+    private static final int MIN_FREE_SPACE = 100 * 1024 * 1024;
+
+    private final BatteryStatsImpl mStats;
+    private final Parcel mHistoryBuffer;
+    private final File mHistoryDir;
+    /**
+     * The active history file that the history buffer is backed up into.
+     */
+    private AtomicFile mActiveFile;
+    /**
+     * A list of history files with incremental indexes.
+     */
+    private final List<Integer> mFileNumbers = new ArrayList<>();
+
+    /**
+     * A list of small history parcels, used when BatteryStatsImpl object is created from
+     * deserialization of a parcel, such as Settings app or checkin file.
+     */
+    private List<Parcel> mHistoryParcels = null;
+
+    /**
+     * When iterating history files, the current file index.
+     */
+    private int mCurrentFileIndex;
+    /**
+     * When iterating history files, the current file parcel.
+     */
+    private Parcel mCurrentParcel;
+    /**
+     * When iterating history file, the current parcel's Parcel.dataSize().
+     */
+    private int mCurrentParcelEnd;
+    /**
+     * When iterating history files, the current record count.
+     */
+    private int mRecordCount = 0;
+    /**
+     * Used when BatteryStatsImpl object is created from deserialization of a parcel,
+     * such as Settings app or checkin file, to iterate over history parcels.
+     */
+    private int mParcelIndex = 0;
+
+    /**
+     * Constructor
+     * @param stats BatteryStatsImpl object.
+     * @param systemDir typically /data/system
+     * @param historyBuffer The in-memory history buffer.
+     */
+    public BatteryStatsHistory(BatteryStatsImpl stats, File systemDir, Parcel historyBuffer) {
+        mStats = stats;
+        mHistoryBuffer = historyBuffer;
+        mHistoryDir = new File(systemDir, HISTORY_DIR);
+        mHistoryDir.mkdirs();
+        if (!mHistoryDir.exists()) {
+            Slog.wtf(TAG, "HistoryDir does not exist:" + mHistoryDir.getPath());
+        }
+
+        final Set<Integer> dedup = new ArraySet<>();
+        // scan directory, fill mFileNumbers and mActiveFile.
+        mHistoryDir.listFiles(new FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                final int b = name.lastIndexOf(FILE_SUFFIX);
+                if (b <= 0) {
+                    return false;
+                }
+                final Integer c =
+                        ParseUtils.parseInt(name.substring(0, b), -1);
+                if (c != -1) {
+                    dedup.add(c);
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+        });
+        if (!dedup.isEmpty()) {
+            mFileNumbers.addAll(dedup);
+            Collections.sort(mFileNumbers);
+        } else {
+            // No file found, default to have file 0.
+            mFileNumbers.add(0);
+        }
+        createActiveFile();
+    }
+
+    /**
+     * Used when BatteryStatsImpl object is created from deserialization of a parcel,
+     * such as Settings app or checkin file.
+     * @param stats BatteryStatsImpl object.
+     * @param historyBuffer the history buffer inside BatteryStatsImpl
+     */
+    public BatteryStatsHistory(BatteryStatsImpl stats, Parcel historyBuffer) {
+        mStats = stats;
+        mHistoryDir = null;
+        mHistoryBuffer = historyBuffer;
+    }
+    /**
+     * The highest numbered history file is active file that mHistoryBuffer is backed up into.
+     * If file does not exists, truncate() creates a empty file.
+     */
+    private void createActiveFile() {
+        final AtomicFile file = getFile(mFileNumbers.get(mFileNumbers.size() - 1));
+        if (DEBUG) {
+            Slog.d(TAG, "activeHistoryFile:" + file.getBaseFile().getPath());
+        }
+        if (!file.exists()) {
+            try {
+                file.truncate();
+            } catch (IOException e) {
+                Slog.e(TAG, "Error creating history file "+ file.getBaseFile().getPath(), e);
+            }
+        }
+        mActiveFile = file;
+    }
+
+    /**
+     * Create history AtomicFile from file number.
+     * @param num file number.
+     * @return AtomicFile object.
+     */
+    private AtomicFile getFile(int num) {
+        return new AtomicFile(
+                new File(mHistoryDir,  num + FILE_SUFFIX));
+    }
+
+    /**
+     * When {@link #mHistoryBuffer} reaches {@link BatteryStatsImpl.Constants#MAX_HISTORY_BUFFER},
+     * create next history file.
+     */
+    public void createNextFile() {
+        if (mFileNumbers.isEmpty()) {
+            Slog.wtf(TAG, "mFileNumbers should never be empty");
+            return;
+        }
+        // The last number in mFileNumbers is the highest number. The next file number is highest
+        // number plus one.
+        final int next = mFileNumbers.get(mFileNumbers.size() - 1) + 1;
+        mFileNumbers.add(next);
+        createActiveFile();
+
+        // if free disk space is less than 100MB, delete oldest history file.
+        if (!hasFreeDiskSpace()) {
+            int oldest = mFileNumbers.remove(0);
+            getFile(oldest).delete();
+        }
+
+        // if there are more history files than allowed, delete oldest history files.
+        // MAX_HISTORY_FILES can be updated by GService config at run time.
+        while (mFileNumbers.size() > mStats.mConstants.MAX_HISTORY_FILES) {
+            int oldest = mFileNumbers.get(0);
+            getFile(oldest).delete();
+            mFileNumbers.remove(0);
+        }
+    }
+
+    /**
+     * Delete all existing history files. Active history file start from number 0 again.
+     */
+    public void resetAllFiles() {
+        for (Integer i : mFileNumbers) {
+            getFile(i).delete();
+        }
+        mFileNumbers.clear();
+        mFileNumbers.add(0);
+        createActiveFile();
+    }
+
+    /**
+     * Start iterating history files and history buffer.
+     * @return always return true.
+     */
+    public boolean startIteratingHistory() {
+        mRecordCount = 0;
+        mCurrentFileIndex = 0;
+        mCurrentParcel = null;
+        mCurrentParcelEnd = 0;
+        mParcelIndex = 0;
+        return true;
+    }
+
+    /**
+     * Finish iterating history files and history buffer.
+     */
+    public void finishIteratingHistory() {
+        // setDataPosition so mHistoryBuffer Parcel can be written.
+        mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
+        if (DEBUG) {
+            Slog.d(TAG, "Battery history records iterated: " + mRecordCount);
+        }
+    }
+
+    /**
+     * When iterating history files and history buffer, always start from the lowest numbered
+     * history file, when reached the mActiveFile (highest numbered history file), do not read from
+     * mActiveFile, read from history buffer instead because the buffer has more updated data.
+     * @param out a history item.
+     * @return The parcel that has next record. null if finished all history files and history
+     *         buffer
+     */
+    public Parcel getNextParcel(BatteryStats.HistoryItem out) {
+        if (mRecordCount == 0) {
+            // reset out if it is the first record.
+            out.clear();
+        }
+        ++mRecordCount;
+
+        // First iterate through all records in current parcel.
+        if (mCurrentParcel != null)
+        {
+            if (mCurrentParcel.dataPosition() < mCurrentParcelEnd) {
+                // There are more records in current parcel.
+                return mCurrentParcel;
+            } else if (mHistoryBuffer == mCurrentParcel) {
+                // finished iterate through all history files and history buffer.
+                return null;
+            } else if (mHistoryParcels == null
+                    || !mHistoryParcels.contains(mCurrentParcel)) {
+                // current parcel is from history file.
+                mCurrentParcel.recycle();
+            }
+        }
+
+        // Try next available history file.
+        // skip the last file because its data is in history buffer.
+        while (mCurrentFileIndex < mFileNumbers.size() - 1) {
+            mCurrentParcel = null;
+            mCurrentParcelEnd = 0;
+            final Parcel p = Parcel.obtain();
+            AtomicFile file = getFile(mFileNumbers.get(mCurrentFileIndex++));
+            if (readFileToParcel(p, file)) {
+                int bufSize = p.readInt();
+                int curPos = p.dataPosition();
+                mCurrentParcelEnd = curPos + bufSize;
+                mCurrentParcel = p;
+                if (curPos < mCurrentParcelEnd) {
+                    return mCurrentParcel;
+                }
+            } else {
+                p.recycle();
+            }
+        }
+
+        // mHistoryParcels is created when BatteryStatsImpl object is created from deserialization
+        // of a parcel, such as Settings app or checkin file.
+        if (mHistoryParcels != null) {
+            while (mParcelIndex < mHistoryParcels.size()) {
+                final Parcel p = mHistoryParcels.get(mParcelIndex++);
+                if (!skipHead(p)) {
+                    continue;
+                }
+                final int bufSize = p.readInt();
+                final int curPos = p.dataPosition();
+                mCurrentParcelEnd = curPos + bufSize;
+                mCurrentParcel = p;
+                if (curPos < mCurrentParcelEnd) {
+                    return mCurrentParcel;
+                }
+            }
+        }
+
+        // finished iterator through history files (except the last one), now history buffer.
+        if (mHistoryBuffer.dataSize() <= 0) {
+            // buffer is empty.
+            return null;
+        }
+        mHistoryBuffer.setDataPosition(0);
+        mCurrentParcel = mHistoryBuffer;
+        mCurrentParcelEnd = mCurrentParcel.dataSize();
+        return mCurrentParcel;
+    }
+
+    /**
+     * Read history file into a parcel.
+     * @param out the Parcel read into.
+     * @param file the File to read from.
+     * @return true if success, false otherwise.
+     */
+    public boolean readFileToParcel(Parcel out, AtomicFile file) {
+        byte[] raw = null;
+        try {
+            final long start = SystemClock.uptimeMillis();
+            raw = file.readFully();
+            if (DEBUG) {
+                Slog.d(TAG, "readFileToParcel:" + file.getBaseFile().getPath()
+                        + " duration ms:" + (SystemClock.uptimeMillis() - start));
+            }
+        } catch(Exception e) {
+            Slog.e(TAG, "Error reading file "+ file.getBaseFile().getPath(), e);
+            return false;
+        }
+        out.unmarshall(raw, 0, raw.length);
+        out.setDataPosition(0);
+        return skipHead(out);
+    }
+
+    /**
+     * Skip the header part of history parcel.
+     * @param p history parcel to skip head.
+     * @return true if version match, false if not.
+     */
+    private boolean skipHead(Parcel p) {
+        p.setDataPosition(0);
+        final int version = p.readInt();
+        if (version != mStats.VERSION) {
+            return false;
+        }
+        // skip historyBaseTime field.
+        p.readLong();
+        return true;
+    }
+
+    /**
+     * Read all history files and serialize into a big Parcel. This is to send history files to
+     * Settings app since Settings app can not access /data/system directory.
+     * Checkin file also call this method.
+     * @param out the output parcel
+     */
+    public void writeToParcel(Parcel out) {
+        final long start = SystemClock.uptimeMillis();
+        out.writeInt(mFileNumbers.size() - 1);
+        for(int i = 0;  i < mFileNumbers.size() - 1; i++) {
+            AtomicFile file = getFile(mFileNumbers.get(i));
+            byte[] raw = new byte[0];
+            try {
+                raw = file.readFully();
+            } catch(Exception e) {
+                Slog.e(TAG, "Error reading file "+ file.getBaseFile().getPath(), e);
+            }
+            out.writeByteArray(raw);
+        }
+        if (DEBUG) {
+            Slog.d(TAG, "writeToParcel duration ms:" + (SystemClock.uptimeMillis() - start));
+        }
+    }
+
+    /**
+     * This is for Settings app, when Settings app receives big history parcel, it call
+     * this method to parse it into list of parcels.
+     * Checkin file also call this method.
+     * @param in the input parcel.
+     */
+    public void readFromParcel(Parcel in) {
+        final long start = SystemClock.uptimeMillis();
+        mHistoryParcels = new ArrayList<>();
+        final int count = in.readInt();
+        for(int i = 0; i < count; i++) {
+            byte[] temp = in.createByteArray();
+            if (temp.length == 0) {
+                continue;
+            }
+            Parcel p = Parcel.obtain();
+            p.unmarshall(temp, 0, temp.length);
+            p.setDataPosition(0);
+            mHistoryParcels.add(p);
+        }
+        if (DEBUG) {
+            Slog.d(TAG, "readFromParcel duration ms:" + (SystemClock.uptimeMillis() - start));
+        }
+    }
+
+    /**
+     * @return true if there is more than 100MB free disk space left.
+     */
+    private boolean hasFreeDiskSpace() {
+        final StatFs stats = new StatFs(mHistoryDir.getAbsolutePath());
+        return stats.getAvailableBytes() > MIN_FREE_SPACE;
+    }
+
+    public List<Integer> getFilesNumbers() {
+        return mFileNumbers;
+    }
+
+    public AtomicFile getActiveFile() {
+        return mActiveFile;
+    }
+
+    /**
+     * @return the total size of all history files and history buffer.
+     */
+    public int getHistoryUsedSize() {
+        int ret = 0;
+        for(int i = 0; i < mFileNumbers.size() - 1; i++) {
+            ret += getFile(mFileNumbers.get(i)).getBaseFile().length();
+        }
+        ret += mHistoryBuffer.dataSize();
+        if (mHistoryParcels != null) {
+            for(int i = 0; i < mHistoryParcels.size(); i++) {
+                ret += mHistoryParcels.get(i).dataSize();
+            }
+        }
+        return ret;
+    }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d7db2e2..f314872 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -36,7 +36,6 @@
 import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Build;
-import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBatteryPropertiesRegistrar;
 import android.os.Looper;
@@ -90,7 +89,6 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
 
 import libcore.util.EmptyArray;
@@ -110,7 +108,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -140,35 +140,18 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 177 + (USE_OLD_HISTORY ? 1000 : 0);
-
-    // Maximum number of items we will record in the history.
-    private static final int MAX_HISTORY_ITEMS;
-
-    // No, really, THIS is the maximum number of items we will record in the history.
-    private static final int MAX_MAX_HISTORY_ITEMS;
+    static final int VERSION = 185 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
     // in to one common name.
     private static final int MAX_WAKELOCKS_PER_UID;
 
-    static final int MAX_HISTORY_BUFFER; // 256KB
-    static final int MAX_MAX_HISTORY_BUFFER; // 320KB
-
     static {
         if (ActivityManager.isLowRamDeviceStatic()) {
-            MAX_HISTORY_ITEMS = 800;
-            MAX_MAX_HISTORY_ITEMS = 1200;
             MAX_WAKELOCKS_PER_UID = 40;
-            MAX_HISTORY_BUFFER = 96*1024;  // 96KB
-            MAX_MAX_HISTORY_BUFFER = 128*1024; // 128KB
         } else {
-            MAX_HISTORY_ITEMS = 4000;
-            MAX_MAX_HISTORY_ITEMS = 6000;
             MAX_WAKELOCKS_PER_UID = 200;
-            MAX_HISTORY_BUFFER = 512*1024;  // 512KB
-            MAX_MAX_HISTORY_BUFFER = 640*1024;  // 640KB
         }
     }
 
@@ -189,7 +172,7 @@
 
     protected Clocks mClocks;
 
-    private final JournaledFile mFile;
+    private final AtomicFile mStatsFile;
     public final AtomicFile mCheckinFile;
     public final AtomicFile mDailyFile;
 
@@ -229,6 +212,15 @@
     public boolean mPerProcStateCpuTimesAvailable = true;
 
     /**
+     * When per process state cpu times tracking is off, cpu times in KernelSingleUidTimeReader are
+     * not updated. So, when the setting is turned on later, we would end up with huge cpu time
+     * deltas. This flag tracks the case where tracking is turned on from off so that we won't
+     * end up attributing the huge deltas to wrong buckets.
+     */
+    @GuardedBy("this")
+    private boolean mIsPerProcessStateCpuDataStale;
+
+    /**
      * Uids for which per-procstate cpu times need to be updated.
      *
      * Contains uid -> procState mappings.
@@ -401,7 +393,7 @@
             }
             // If the KernelSingleUidTimeReader has stale cpu times, then we shouldn't try to
             // compute deltas since it might result in mis-attributing cpu times to wrong states.
-            if (mKernelSingleUidTimeReader.hasStaleData()) {
+            if (mIsPerProcessStateCpuDataStale) {
                 mPendingUids.clear();
                 return;
             }
@@ -484,9 +476,9 @@
                     mKernelUidCpuFreqTimeReader.getAllUidCpuFreqTimeMs();
             // If the KernelSingleUidTimeReader has stale cpu times, then we shouldn't try to
             // compute deltas since it might result in mis-attributing cpu times to wrong states.
-            if (mKernelSingleUidTimeReader.hasStaleData()) {
+            if (mIsPerProcessStateCpuDataStale) {
                 mKernelSingleUidTimeReader.setAllUidsCpuTimesMs(allUidCpuFreqTimesMs);
-                mKernelSingleUidTimeReader.markDataAsStale(false);
+                mIsPerProcessStateCpuDataStale = false;
                 mPendingUids.clear();
                 return;
             }
@@ -632,11 +624,11 @@
 
     // These are the objects that will want to do something when the device
     // is unplugged from power.
-    protected final TimeBase mOnBatteryTimeBase = new TimeBase();
+    protected final TimeBase mOnBatteryTimeBase = new TimeBase(true);
 
     // These are the objects that will want to do something when the device
     // is unplugged from power *and* the screen is off or doze.
-    protected final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase();
+    protected final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase(true);
 
     // Set to true when we want to distribute CPU across wakelocks for the next
     // CPU update, even if we aren't currently running wake locks.
@@ -663,13 +655,14 @@
     int mNextHistoryTagIdx = 0;
     int mNumHistoryTagChars = 0;
     int mHistoryBufferLastPos = -1;
-    boolean mHistoryOverflow = false;
     int mActiveHistoryStates = 0xffffffff;
     int mActiveHistoryStates2 = 0xffffffff;
     long mLastHistoryElapsedRealtime = 0;
     long mTrackRunningHistoryElapsedRealtime = 0;
     long mTrackRunningHistoryUptime = 0;
 
+    final BatteryStatsHistory mBatteryStatsHistory;
+
     final HistoryItem mHistoryCur = new HistoryItem();
 
     HistoryItem mHistory;
@@ -963,7 +956,7 @@
     protected PowerProfile mPowerProfile;
 
     @GuardedBy("this")
-    private final Constants mConstants;
+    final Constants mConstants;
 
     /*
      * Holds a SamplingTimer associated with each Resource Power Manager state and voter,
@@ -1047,9 +1040,10 @@
 
     public BatteryStatsImpl(Clocks clocks) {
         init(clocks);
-        mFile = null;
+        mStatsFile = null;
         mCheckinFile = null;
         mDailyFile = null;
+        mBatteryStatsHistory = null;
         mHandler = null;
         mPlatformIdleStateCallback = null;
         mUserInfoProvider = null;
@@ -1061,15 +1055,29 @@
         mClocks = clocks;
     }
 
+    /**
+     * TimeBase observer.
+     */
     public interface TimeBaseObs {
         void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime);
         void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime);
+
+        /**
+         * Reset the observer's state, returns true if the timer/counter is inactive
+         * so it can be destroyed.
+         * @param detachIfReset detach if true, no-op if false.
+         * @return Returns true if the timer/counter is inactive and can be destroyed.
+         */
+        boolean reset(boolean detachIfReset);
+        /**
+         * Detach the observer from TimeBase.
+         */
+        void detach();
     }
 
     // methods are protected not private to be VisibleForTesting
     public static class TimeBase {
-        protected final ArrayList<TimeBaseObs> mObservers = new ArrayList<>();
-
+        protected final Collection<TimeBaseObs> mObservers;
         protected long mUptime;
         protected long mRealtime;
 
@@ -1110,15 +1118,29 @@
                     sb.append("mUnpluggedRealtime="); formatTimeMs(sb, mUnpluggedRealtime / 1000);
             pw.println(sb.toString());
         }
+        /**
+         * The mObservers of TimeBase in BatteryStatsImpl object can contain up to 20k entries.
+         * The mObservers of TimeBase in BatteryStatsImpl.Uid object only contains a few or tens of
+         * entries.
+         * mObservers must have good performance on add(), remove(), also be memory efficient.
+         * This is why we provide isLongList parameter for long and short list user cases.
+         * @param isLongList If true, use HashSet for mObservers list.
+         *                   If false, use ArrayList for mObservers list.
+        */
+        public TimeBase(boolean isLongList) {
+            mObservers = isLongList ? new HashSet<>() : new ArrayList<>();
+        }
+
+        public TimeBase() {
+            this(false);
+        }
 
         public void add(TimeBaseObs observer) {
             mObservers.add(observer);
         }
 
         public void remove(TimeBaseObs observer) {
-            if (!mObservers.remove(observer)) {
-                Slog.wtf(TAG, "Removed unknown observer: " + observer);
-            }
+            mObservers.remove(observer);
         }
 
         public boolean hasObserver(TimeBaseObs observer) {
@@ -1211,19 +1233,24 @@
                     mRealtimeStart = realtime;
                     long batteryUptime = mUnpluggedUptime = getUptime(uptime);
                     long batteryRealtime = mUnpluggedRealtime = getRealtime(realtime);
-
-                    for (int i = mObservers.size() - 1; i >= 0; i--) {
-                        mObservers.get(i).onTimeStarted(realtime, batteryUptime, batteryRealtime);
+                    // Normally we do not use Iterator in framework code to avoid alloc/dealloc
+                    // Iterator object, here is an exception because mObservers' type is Collection
+                    // instead of list.
+                    final Iterator<TimeBaseObs> iter = mObservers.iterator();
+                    while (iter.hasNext()) {
+                        iter.next().onTimeStarted(realtime, batteryUptime, batteryRealtime);
                     }
                 } else {
                     mPastUptime += uptime - mUptimeStart;
                     mPastRealtime += realtime - mRealtimeStart;
-
                     long batteryUptime = getUptime(uptime);
                     long batteryRealtime = getRealtime(realtime);
-
-                    for (int i = mObservers.size() - 1; i >= 0; i--) {
-                        mObservers.get(i).onTimeStopped(realtime, batteryUptime, batteryRealtime);
+                    // Normally we do not use Iterator in framework code to avoid alloc/dealloc
+                    // Iterator object, here is an exception because mObservers' type is Collection
+                    // instead of list.
+                    final Iterator<TimeBaseObs> iter = mObservers.iterator();
+                    while (iter.hasNext()) {
+                        iter.next().onTimeStopped(realtime, batteryUptime, batteryRealtime);
                     }
                 }
                 return true;
@@ -1371,15 +1398,18 @@
         /**
          * Clear state of this counter.
          */
-        void reset(boolean detachIfReset) {
+        @Override
+        public boolean reset(boolean detachIfReset) {
             mCount.set(0);
             mLoadedCount = mPluggedCount = mUnpluggedCount = 0;
             if (detachIfReset) {
                 detach();
             }
+            return true;
         }
 
-        void detach() {
+        @Override
+        public void detach() {
             mTimeBase.remove(this);
         }
 
@@ -1475,15 +1505,18 @@
         /**
          * Clear state of this counter.
          */
-        public void reset(boolean detachIfReset) {
+        @Override
+        public boolean reset(boolean detachIfReset) {
             fillArray(mCounts, 0);
             fillArray(mLoadedCounts, 0);
             fillArray(mUnpluggedCounts, 0);
             if (detachIfReset) {
                 detach();
             }
+            return true;
         }
 
+        @Override
         public void detach() {
             mTimeBase.remove(this);
         }
@@ -1646,14 +1679,17 @@
         /**
          * Clear state of this counter.
          */
-        public void reset(boolean detachIfReset) {
+        @Override
+        public boolean reset(boolean detachIfReset) {
             mCount = 0;
             mLoadedCount = mUnpluggedCount = 0;
             if (detachIfReset) {
                 detach();
             }
+            return true;
         }
 
+        @Override
         public void detach() {
             mTimeBase.remove(this);
         }
@@ -1754,6 +1790,7 @@
          * Clear state of this timer.  Returns true if the timer is inactive
          * so can be completely dropped.
          */
+        @Override
         public boolean reset(boolean detachIfReset) {
             mTotalTime = mLoadedTime = mLastTime = mTimeBeforeMark = 0;
             mCount = mLoadedCount = mLastCount = 0;
@@ -1763,6 +1800,7 @@
             return true;
         }
 
+        @Override
         public void detach() {
             mTimeBase.remove(this);
         }
@@ -3154,8 +3192,13 @@
     }
 
     private void readHistoryTag(int index, HistoryTag tag) {
-        tag.string = mReadHistoryStrings[index];
-        tag.uid = mReadHistoryUids[index];
+        if (index < mReadHistoryStrings.length) {
+            tag.string = mReadHistoryStrings[index];
+            tag.uid = mReadHistoryUids[index];
+        } else {
+            tag.string = null;
+            tag.uid = 0;
+        }
         tag.poolIdx = index;
     }
 
@@ -3674,6 +3717,13 @@
         mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
     }
 
+    public void createFakeHistoryEvents(long numEvents) {
+        for(long i = 0; i < numEvents; i++) {
+            noteWifiOnLocked();
+            noteWifiOffLocked();
+        }
+    }
+
     void addHistoryBufferLocked(long elapsedRealtimeMs, HistoryItem cur) {
         if (!mHaveBatteryLevel || !mRecordingHistory) {
             return;
@@ -3736,74 +3786,32 @@
             }
             mHistoryLastWritten.setTo(mHistoryLastLastWritten);
         }
-
-        boolean recordResetDueToOverflow = false;
         final int dataSize = mHistoryBuffer.dataSize();
-        if (dataSize >= MAX_MAX_HISTORY_BUFFER*3) {
-            // Clients can't deal with history buffers this large. This only
-            // really happens when the device is on charger and interacted with
-            // for long periods of time, like in retail mode. Since the device is
-            // most likely charged, when unplugged, stats would have reset anyways.
-            // Reset the stats and mark that we overflowed.
-            // b/32540341
-            resetAllStatsLocked();
 
-            // Mark that we want to set *OVERFLOW* event and the RESET:START
-            // events.
-            recordResetDueToOverflow = true;
-
-        } else if (dataSize >= MAX_HISTORY_BUFFER) {
-            if (!mHistoryOverflow) {
-                mHistoryOverflow = true;
-                addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
-                addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
-                return;
+        if (dataSize >= mConstants.MAX_HISTORY_BUFFER) {
+            //open a new history file.
+            final long start = SystemClock.uptimeMillis();
+            writeHistoryLocked(true);
+            if (DEBUG) {
+                Slog.d(TAG, "addHistoryBufferLocked writeHistoryLocked takes ms:"
+                        + (SystemClock.uptimeMillis() - start));
             }
-
-            // After overflow, we allow various bit-wise states to settle to 0.
-            boolean writeAnyway = false;
-            final int curStates = cur.states & HistoryItem.SETTLE_TO_ZERO_STATES
-                    & mActiveHistoryStates;
-            if (mHistoryLastWritten.states != curStates) {
-                // mActiveHistoryStates keeps track of which bits in .states are now being
-                // forced to 0.
-                int old = mActiveHistoryStates;
-                mActiveHistoryStates &= curStates | ~HistoryItem.SETTLE_TO_ZERO_STATES;
-                writeAnyway |= old != mActiveHistoryStates;
-            }
-            final int curStates2 = cur.states2 & HistoryItem.SETTLE_TO_ZERO_STATES2
-                    & mActiveHistoryStates2;
-            if (mHistoryLastWritten.states2 != curStates2) {
-                // mActiveHistoryStates2 keeps track of which bits in .states2 are now being
-                // forced to 0.
-                int old = mActiveHistoryStates2;
-                mActiveHistoryStates2 &= curStates2 | ~HistoryItem.SETTLE_TO_ZERO_STATES2;
-                writeAnyway |= old != mActiveHistoryStates2;
-            }
-
-            // Once we've reached the maximum number of items, we only
-            // record changes to the battery level and the most interesting states.
-            // Once we've reached the maximum maximum number of items, we only
-            // record changes to the battery level.
-            if (!writeAnyway && mHistoryLastWritten.batteryLevel == cur.batteryLevel &&
-                    (dataSize >= MAX_MAX_HISTORY_BUFFER
-                            || ((mHistoryLastWritten.states^cur.states)
-                                    & HistoryItem.MOST_INTERESTING_STATES) == 0
-                            || ((mHistoryLastWritten.states2^cur.states2)
-                                    & HistoryItem.MOST_INTERESTING_STATES2) == 0)) {
-                return;
-            }
+            mBatteryStatsHistory.createNextFile();
+            mHistoryBuffer.setDataSize(0);
+            mHistoryBuffer.setDataPosition(0);
+            mHistoryBuffer.setDataCapacity(mConstants.MAX_HISTORY_BUFFER / 2);
+            mHistoryBufferLastPos = -1;
+            final long elapsedRealtime = mClocks.elapsedRealtime();
+            final long uptime = mClocks.uptimeMillis();
+            startRecordingHistory(elapsedRealtime, uptime, false);
 
             addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
             return;
         }
 
-        if (dataSize == 0 || recordResetDueToOverflow) {
+        if (dataSize == 0) {
             // The history is currently empty; we need it to start with a time stamp.
             cur.currentTime = System.currentTimeMillis();
-            if (recordResetDueToOverflow) {
-                addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
-            }
             addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_RESET, cur);
         }
         addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
@@ -3891,26 +3899,6 @@
 
         mChangedStates = 0;
         mChangedStates2 = 0;
-
-        if (mNumHistoryItems == MAX_HISTORY_ITEMS
-                || mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
-            addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
-        }
-
-        if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
-            // Once we've reached the maximum number of items, we only
-            // record changes to the battery level and the most interesting states.
-            // Once we've reached the maximum maximum number of items, we only
-            // record changes to the battery level.
-            if (mHistoryEnd != null && mHistoryEnd.batteryLevel
-                    == cur.batteryLevel &&
-                    (mNumHistoryItems >= MAX_MAX_HISTORY_ITEMS
-                            || ((mHistoryEnd.states^(cur.states&mActiveHistoryStates))
-                                    & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
-                return;
-            }
-        }
-
         addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
     }
 
@@ -3965,14 +3953,13 @@
 
         mHistoryBuffer.setDataSize(0);
         mHistoryBuffer.setDataPosition(0);
-        mHistoryBuffer.setDataCapacity(MAX_HISTORY_BUFFER / 2);
+        mHistoryBuffer.setDataCapacity(mConstants.MAX_HISTORY_BUFFER / 2);
         mHistoryLastLastWritten.clear();
         mHistoryLastWritten.clear();
         mHistoryTagPool.clear();
         mNextHistoryTagIdx = 0;
         mNumHistoryTagChars = 0;
         mHistoryBufferLastPos = -1;
-        mHistoryOverflow = false;
         mActiveHistoryStates = 0xffffffff;
         mActiveHistoryStates2 = 0xffffffff;
     }
@@ -4021,7 +4008,9 @@
         try {
             IBatteryPropertiesRegistrar registrar = IBatteryPropertiesRegistrar.Stub.asInterface(
                     ServiceManager.getService("batteryproperties"));
-            registrar.scheduleUpdate();
+            if (registrar != null) {
+                registrar.scheduleUpdate();
+            }
         } catch (RemoteException e) {
             // Ignore.
         }
@@ -6603,38 +6592,69 @@
         return mUidStats;
     }
 
-    private static void detachTimerIfNotNull(BatteryStatsImpl.Timer timer) {
-        if (timer != null) {
-            timer.detach();
-        }
-    }
-
-    private static boolean resetTimerIfNotNull(BatteryStatsImpl.Timer timer,
-            boolean detachIfReset) {
-        if (timer != null) {
-            return timer.reset(detachIfReset);
+    private static <T extends TimeBaseObs> boolean resetIfNotNull(T t, boolean detachIfReset) {
+        if (t != null) {
+            return t.reset(detachIfReset);
         }
         return true;
     }
 
-    private static boolean resetTimerIfNotNull(DualTimer timer, boolean detachIfReset) {
-        if (timer != null) {
-            return timer.reset(detachIfReset);
+    private static <T extends TimeBaseObs> boolean resetIfNotNull(T[] t, boolean detachIfReset) {
+        if (t != null) {
+            boolean ret = true;
+            for (int i = 0; i < t.length; i++) {
+                ret &= resetIfNotNull(t[i], detachIfReset);
+            }
+            return ret;
         }
         return true;
     }
 
-    private static void detachLongCounterIfNotNull(LongSamplingCounter counter) {
-        if (counter != null) {
-            counter.detach();
+    private static <T extends TimeBaseObs> boolean resetIfNotNull(T[][] t, boolean detachIfReset) {
+        if (t != null) {
+            boolean ret = true;
+            for (int i = 0; i < t.length; i++) {
+                ret &= resetIfNotNull(t[i], detachIfReset);
+            }
+            return ret;
         }
+        return true;
     }
 
-    private static void resetLongCounterIfNotNull(LongSamplingCounter counter,
+    private static boolean resetIfNotNull(ControllerActivityCounterImpl counter,
             boolean detachIfReset) {
         if (counter != null) {
             counter.reset(detachIfReset);
         }
+        return true;
+    }
+
+    private static <T extends TimeBaseObs> void detachIfNotNull(T t) {
+        if (t != null) {
+            t.detach();
+        }
+    }
+
+    private static <T extends TimeBaseObs> void detachIfNotNull(T[] t) {
+        if (t != null) {
+            for (int i = 0; i < t.length; i++) {
+                detachIfNotNull(t[i]);
+            }
+        }
+    }
+
+    private static <T extends TimeBaseObs> void detachIfNotNull(T[][] t) {
+        if (t != null) {
+            for (int i = 0; i < t.length; i++) {
+                detachIfNotNull(t[i]);
+            }
+        }
+    }
+
+    private static void detachIfNotNull(ControllerActivityCounterImpl counter) {
+        if (counter != null) {
+            counter.detach();
+        }
     }
 
     /**
@@ -6814,11 +6834,12 @@
             mBsi = bsi;
             mUid = uid;
 
-            mOnBatteryBackgroundTimeBase = new TimeBase();
+            /* Observer list of TimeBase object in Uid is short */
+            mOnBatteryBackgroundTimeBase = new TimeBase(false);
             mOnBatteryBackgroundTimeBase.init(mBsi.mClocks.uptimeMillis() * 1000,
                     mBsi.mClocks.elapsedRealtime() * 1000);
-
-            mOnBatteryScreenOffBackgroundTimeBase = new TimeBase();
+            /* Observer list of TimeBase object in Uid is short */
+            mOnBatteryScreenOffBackgroundTimeBase = new TimeBase(false);
             mOnBatteryScreenOffBackgroundTimeBase.init(mBsi.mClocks.uptimeMillis() * 1000,
                     mBsi.mClocks.elapsedRealtime() * 1000);
 
@@ -6957,6 +6978,7 @@
             }
             if (mProcStateTimeMs[procState] == null
                     || mProcStateTimeMs[procState].getSize() != cpuTimesMs.length) {
+                detachIfNotNull(mProcStateTimeMs[procState]);
                 mProcStateTimeMs[procState] = new LongSamplingCounterArray(
                         mBsi.mOnBatteryTimeBase);
             }
@@ -6970,6 +6992,7 @@
             }
             if (mProcStateScreenOffTimeMs[procState] == null
                     || mProcStateScreenOffTimeMs[procState].getSize() != cpuTimesMs.length) {
+                detachIfNotNull(mProcStateScreenOffTimeMs[procState]);
                 mProcStateScreenOffTimeMs[procState] = new LongSamplingCounterArray(
                         mBsi.mOnBatteryScreenOffTimeBase);
             }
@@ -7574,6 +7597,7 @@
         void makeProcessState(int i, Parcel in) {
             if (i < 0 || i >= NUM_PROCESS_STATE) return;
 
+            detachIfNotNull(mProcessStateTimer[i]);
             if (in == null) {
                 mProcessStateTimer[i] = new StopwatchTimer(mBsi.mClocks, this, PROCESS_STATE, null,
                         mBsi.mOnBatteryTimeBase);
@@ -7637,6 +7661,7 @@
                 collected = new ArrayList<StopwatchTimer>();
                 mBsi.mWifiBatchedScanTimers.put(i, collected);
             }
+            detachIfNotNull(mWifiBatchedScanTimer[i]);
             if (in == null) {
                 mWifiBatchedScanTimer[i] = new StopwatchTimer(mBsi.mClocks, this, WIFI_BATCHED_SCAN,
                         collected, mBsi.mOnBatteryTimeBase);
@@ -7648,6 +7673,7 @@
 
 
         void initUserActivityLocked() {
+            detachIfNotNull(mUserActivityCounters);
             mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES];
             for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
                 mUserActivityCounters[i] = new Counter(mBsi.mOnBatteryTimeBase);
@@ -7816,13 +7842,17 @@
         }
 
         void initNetworkActivityLocked() {
+            detachIfNotNull(mNetworkByteActivityCounters);
             mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+            detachIfNotNull(mNetworkPacketActivityCounters);
             mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
             for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
                 mNetworkByteActivityCounters[i] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
                 mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
             }
+            detachIfNotNull(mMobileRadioActiveTime);
             mMobileRadioActiveTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
+            detachIfNotNull(mMobileRadioActiveCount);
             mMobileRadioActiveCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
         }
 
@@ -7862,27 +7892,22 @@
                 active |= mWifiMulticastEnabled;
             }
 
-            active |= !resetTimerIfNotNull(mAudioTurnedOnTimer, false);
-            active |= !resetTimerIfNotNull(mVideoTurnedOnTimer, false);
-            active |= !resetTimerIfNotNull(mFlashlightTurnedOnTimer, false);
-            active |= !resetTimerIfNotNull(mCameraTurnedOnTimer, false);
-            active |= !resetTimerIfNotNull(mForegroundActivityTimer, false);
-            active |= !resetTimerIfNotNull(mForegroundServiceTimer, false);
-            active |= !resetTimerIfNotNull(mAggregatedPartialWakelockTimer, false);
-            active |= !resetTimerIfNotNull(mBluetoothScanTimer, false);
-            active |= !resetTimerIfNotNull(mBluetoothUnoptimizedScanTimer, false);
-            if (mBluetoothScanResultCounter != null) {
-                mBluetoothScanResultCounter.reset(false);
-            }
-            if (mBluetoothScanResultBgCounter != null) {
-                mBluetoothScanResultBgCounter.reset(false);
-            }
+            active |= !resetIfNotNull(mAudioTurnedOnTimer, false);
+            active |= !resetIfNotNull(mVideoTurnedOnTimer, false);
+            active |= !resetIfNotNull(mFlashlightTurnedOnTimer, false);
+            active |= !resetIfNotNull(mCameraTurnedOnTimer, false);
+            active |= !resetIfNotNull(mForegroundActivityTimer, false);
+            active |= !resetIfNotNull(mForegroundServiceTimer, false);
+            active |= !resetIfNotNull(mAggregatedPartialWakelockTimer, false);
+            active |= !resetIfNotNull(mBluetoothScanTimer, false);
+            active |= !resetIfNotNull(mBluetoothUnoptimizedScanTimer, false);
+
+            resetIfNotNull(mBluetoothScanResultCounter, false);
+            resetIfNotNull(mBluetoothScanResultBgCounter, false);
 
             if (mProcessStateTimer != null) {
                 for (int i = 0; i < NUM_PROCESS_STATE; i++) {
-                    if (mProcessStateTimer[i] != null) {
-                        active |= !mProcessStateTimer[i].reset(false);
-                    }
+                    active |= !resetIfNotNull(mProcessStateTimer[i], false);
                 }
                 active |= (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT);
             }
@@ -7895,75 +7920,37 @@
                 }
             }
 
-            if (mUserActivityCounters != null) {
-                for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
-                    mUserActivityCounters[i].reset(false);
-                }
-            }
+            resetIfNotNull(mUserActivityCounters, false);
 
-            if (mNetworkByteActivityCounters != null) {
-                for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
-                    mNetworkByteActivityCounters[i].reset(false);
-                    mNetworkPacketActivityCounters[i].reset(false);
-                }
-                mMobileRadioActiveTime.reset(false);
-                mMobileRadioActiveCount.reset(false);
-            }
+            resetIfNotNull(mNetworkByteActivityCounters, false);
+            resetIfNotNull(mNetworkPacketActivityCounters, false);
+            resetIfNotNull(mMobileRadioActiveTime, false);
+            resetIfNotNull(mMobileRadioActiveCount, false);
 
-            if (mWifiControllerActivity != null) {
-                mWifiControllerActivity.reset(false);
-            }
+            resetIfNotNull(mWifiControllerActivity, false);
+            resetIfNotNull(mBluetoothControllerActivity, false);
+            resetIfNotNull(mModemControllerActivity, false);
 
-            if (mBluetoothControllerActivity != null) {
-                mBluetoothControllerActivity.reset(false);
-            }
+            resetIfNotNull(mUserCpuTime, false);
+            resetIfNotNull(mSystemCpuTime, false);
 
-            if (mModemControllerActivity != null) {
-                mModemControllerActivity.reset(false);
-            }
+            resetIfNotNull(mCpuClusterSpeedTimesUs, false);
 
-            mUserCpuTime.reset(false);
-            mSystemCpuTime.reset(false);
+            resetIfNotNull(mCpuFreqTimeMs, false);
+            resetIfNotNull(mScreenOffCpuFreqTimeMs, false);
 
-            if (mCpuClusterSpeedTimesUs != null) {
-                for (LongSamplingCounter[] speeds : mCpuClusterSpeedTimesUs) {
-                    if (speeds != null) {
-                        for (LongSamplingCounter speed : speeds) {
-                            if (speed != null) {
-                                speed.reset(false);
-                            }
-                        }
-                    }
-                }
-            }
 
-            if (mCpuFreqTimeMs != null) {
-                mCpuFreqTimeMs.reset(false);
-            }
-            if (mScreenOffCpuFreqTimeMs != null) {
-                mScreenOffCpuFreqTimeMs.reset(false);
-            }
+            resetIfNotNull(mCpuActiveTimeMs, false);
+            resetIfNotNull(mCpuClusterTimesMs, false);
 
-            mCpuActiveTimeMs.reset(false);
-            mCpuClusterTimesMs.reset(false);
+            resetIfNotNull(mProcStateTimeMs, false);
 
-            if (mProcStateTimeMs != null) {
-                for (LongSamplingCounterArray counters : mProcStateTimeMs) {
-                    if (counters != null) {
-                        counters.reset(false);
-                    }
-                }
-            }
-            if (mProcStateScreenOffTimeMs != null) {
-                for (LongSamplingCounterArray counters : mProcStateScreenOffTimeMs) {
-                    if (counters != null) {
-                        counters.reset(false);
-                    }
-                }
-            }
+            resetIfNotNull(mProcStateScreenOffTimeMs, false);
 
-            resetLongCounterIfNotNull(mMobileRadioApWakeupCount, false);
-            resetLongCounterIfNotNull(mWifiRadioApWakeupCount, false);
+            resetIfNotNull(mMobileRadioApWakeupCount, false);
+
+            resetIfNotNull(mWifiRadioApWakeupCount, false);
+
 
             final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap();
             for (int iw=wakeStats.size()-1; iw>=0; iw--) {
@@ -7999,16 +7986,12 @@
             mJobStats.cleanup();
             mJobCompletions.clear();
 
-            mJobsDeferredEventCount.reset(false);
-            mJobsDeferredCount.reset(false);
-            mJobsFreshnessTimeMs.reset(false);
-            for (int ij = 0; ij < JOB_FRESHNESS_BUCKETS.length; ij++) {
-                if (mJobsFreshnessBuckets[ij] != null) {
-                    mJobsFreshnessBuckets[ij].reset(false);
-                }
-            }
+            resetIfNotNull(mJobsDeferredEventCount, false);
+            resetIfNotNull(mJobsDeferredCount, false);
+            resetIfNotNull(mJobsFreshnessTimeMs, false);
+            resetIfNotNull(mJobsFreshnessBuckets, false);
 
-            for (int ise=mSensorStats.size()-1; ise>=0; ise--) {
+            for (int ise = mSensorStats.size() - 1; ise >= 0; ise--) {
                 Sensor s = mSensorStats.valueAt(ise);
                 if (s.reset()) {
                     mSensorStats.removeAt(ise);
@@ -8017,173 +8000,135 @@
                 }
             }
 
-            for (int ip=mProcessStats.size()-1; ip>=0; ip--) {
+            for (int ip = mProcessStats.size() - 1; ip >= 0; ip--) {
                 Proc proc = mProcessStats.valueAt(ip);
                 proc.detach();
             }
             mProcessStats.clear();
-            if (mPids.size() > 0) {
-                for (int i=mPids.size()-1; i>=0; i--) {
-                    Pid pid = mPids.valueAt(i);
-                    if (pid.mWakeNesting > 0) {
-                        active = true;
-                    } else {
-                        mPids.removeAt(i);
-                    }
+
+            for (int i = mPids.size() - 1; i >= 0; i--) {
+                Pid pid = mPids.valueAt(i);
+                if (pid.mWakeNesting > 0) {
+                    active = true;
+                } else {
+                    mPids.removeAt(i);
                 }
             }
-            if (mPackageStats.size() > 0) {
-                Iterator<Map.Entry<String, Pkg>> it = mPackageStats.entrySet().iterator();
-                while (it.hasNext()) {
-                    Map.Entry<String, Pkg> pkgEntry = it.next();
-                    Pkg p = pkgEntry.getValue();
-                    p.detach();
-                    if (p.mServiceStats.size() > 0) {
-                        Iterator<Map.Entry<String, Pkg.Serv>> it2
-                                = p.mServiceStats.entrySet().iterator();
-                        while (it2.hasNext()) {
-                            Map.Entry<String, Pkg.Serv> servEntry = it2.next();
-                            servEntry.getValue().detach();
-                        }
-                    }
-                }
-                mPackageStats.clear();
+
+
+            for(int i = mPackageStats.size() - 1; i >= 0; i--) {
+                Pkg p = mPackageStats.valueAt(i);
+                p.detach();
             }
+            mPackageStats.clear();
 
             mLastStepUserTime = mLastStepSystemTime = 0;
             mCurStepUserTime = mCurStepSystemTime = 0;
 
-            if (!active) {
-                if (mWifiRunningTimer != null) {
-                    mWifiRunningTimer.detach();
-                }
-                if (mFullWifiLockTimer != null) {
-                    mFullWifiLockTimer.detach();
-                }
-                if (mWifiScanTimer != null) {
-                    mWifiScanTimer.detach();
-                }
-                for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) {
-                    if (mWifiBatchedScanTimer[i] != null) {
-                        mWifiBatchedScanTimer[i].detach();
-                    }
-                }
-                if (mWifiMulticastTimer != null) {
-                    mWifiMulticastTimer.detach();
-                }
-                if (mAudioTurnedOnTimer != null) {
-                    mAudioTurnedOnTimer.detach();
-                    mAudioTurnedOnTimer = null;
-                }
-                if (mVideoTurnedOnTimer != null) {
-                    mVideoTurnedOnTimer.detach();
-                    mVideoTurnedOnTimer = null;
-                }
-                if (mFlashlightTurnedOnTimer != null) {
-                    mFlashlightTurnedOnTimer.detach();
-                    mFlashlightTurnedOnTimer = null;
-                }
-                if (mCameraTurnedOnTimer != null) {
-                    mCameraTurnedOnTimer.detach();
-                    mCameraTurnedOnTimer = null;
-                }
-                if (mForegroundActivityTimer != null) {
-                    mForegroundActivityTimer.detach();
-                    mForegroundActivityTimer = null;
-                }
-                if (mForegroundServiceTimer != null) {
-                    mForegroundServiceTimer.detach();
-                    mForegroundServiceTimer = null;
-                }
-                if (mAggregatedPartialWakelockTimer != null) {
-                    mAggregatedPartialWakelockTimer.detach();
-                    mAggregatedPartialWakelockTimer = null;
-                }
-                if (mBluetoothScanTimer != null) {
-                    mBluetoothScanTimer.detach();
-                    mBluetoothScanTimer = null;
-                }
-                if (mBluetoothUnoptimizedScanTimer != null) {
-                    mBluetoothUnoptimizedScanTimer.detach();
-                    mBluetoothUnoptimizedScanTimer = null;
-                }
-                if (mBluetoothScanResultCounter != null) {
-                    mBluetoothScanResultCounter.detach();
-                    mBluetoothScanResultCounter = null;
-                }
-                if (mBluetoothScanResultBgCounter != null) {
-                    mBluetoothScanResultBgCounter.detach();
-                    mBluetoothScanResultBgCounter = null;
-                }
-                if (mUserActivityCounters != null) {
-                    for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
-                        mUserActivityCounters[i].detach();
-                    }
-                }
-                if (mNetworkByteActivityCounters != null) {
-                    for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
-                        mNetworkByteActivityCounters[i].detach();
-                        mNetworkPacketActivityCounters[i].detach();
-                    }
-                }
+            return !active;
+        }
 
-                if (mWifiControllerActivity != null) {
-                    mWifiControllerActivity.detach();
-                }
+        /**
+         * This method MUST be called whenever the Uid object is destructed, otherwise it is a
+         * memory leak in {@link TimeBase#mObservers} list.
+         * Typically the Uid object is destructed when it is removed from
+         * {@link BatteryStatsImpl#mUidStats}
+         */
+        void detachFromTimeBase() {
+            detachIfNotNull(mWifiRunningTimer);
+            detachIfNotNull(mFullWifiLockTimer);
+            detachIfNotNull(mWifiScanTimer);
+            detachIfNotNull(mWifiBatchedScanTimer);
+            detachIfNotNull(mWifiMulticastTimer);
+            detachIfNotNull(mAudioTurnedOnTimer);
+            detachIfNotNull(mVideoTurnedOnTimer);
+            detachIfNotNull(mFlashlightTurnedOnTimer);
 
-                if (mBluetoothControllerActivity != null) {
-                    mBluetoothControllerActivity.detach();
-                }
+            detachIfNotNull(mCameraTurnedOnTimer);
+            detachIfNotNull(mForegroundActivityTimer);
+            detachIfNotNull(mForegroundServiceTimer);
 
-                if (mModemControllerActivity != null) {
-                    mModemControllerActivity.detach();
-                }
+            detachIfNotNull(mAggregatedPartialWakelockTimer);
 
-                mPids.clear();
+            detachIfNotNull(mBluetoothScanTimer);
+            detachIfNotNull(mBluetoothUnoptimizedScanTimer);
+            detachIfNotNull(mBluetoothScanResultCounter);
+            detachIfNotNull(mBluetoothScanResultBgCounter);
 
-                mUserCpuTime.detach();
-                mSystemCpuTime.detach();
+            detachIfNotNull(mProcessStateTimer);
 
-                if (mCpuClusterSpeedTimesUs != null) {
-                    for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) {
-                        if (cpuSpeeds != null) {
-                            for (LongSamplingCounter c : cpuSpeeds) {
-                                if (c != null) {
-                                    c.detach();
-                                }
-                            }
-                        }
-                    }
-                }
+            detachIfNotNull(mVibratorOnTimer);
 
-                if (mCpuFreqTimeMs != null) {
-                    mCpuFreqTimeMs.detach();
-                }
-                if (mScreenOffCpuFreqTimeMs != null) {
-                    mScreenOffCpuFreqTimeMs.detach();
-                }
-                mCpuActiveTimeMs.detach();
-                mCpuClusterTimesMs.detach();
+            detachIfNotNull(mUserActivityCounters);
 
-                if (mProcStateTimeMs != null) {
-                    for (LongSamplingCounterArray counters : mProcStateTimeMs) {
-                        if (counters != null) {
-                            counters.detach();
-                        }
-                    }
-                }
-                if (mProcStateScreenOffTimeMs != null) {
-                    for (LongSamplingCounterArray counters : mProcStateScreenOffTimeMs) {
-                        if (counters != null) {
-                            counters.detach();
-                        }
-                    }
-                }
-                detachLongCounterIfNotNull(mMobileRadioApWakeupCount);
-                detachLongCounterIfNotNull(mWifiRadioApWakeupCount);
+            detachIfNotNull(mNetworkByteActivityCounters);
+            detachIfNotNull(mNetworkPacketActivityCounters);
+
+            detachIfNotNull(mMobileRadioActiveTime);
+            detachIfNotNull(mMobileRadioActiveCount);
+            detachIfNotNull(mMobileRadioApWakeupCount);
+            detachIfNotNull(mWifiRadioApWakeupCount);
+
+            detachIfNotNull(mWifiControllerActivity);
+            detachIfNotNull(mBluetoothControllerActivity);
+            detachIfNotNull(mModemControllerActivity);
+
+            mPids.clear();
+
+            detachIfNotNull(mUserCpuTime);
+            detachIfNotNull(mSystemCpuTime);
+
+            detachIfNotNull(mCpuClusterSpeedTimesUs);
+
+            detachIfNotNull(mCpuActiveTimeMs);
+            detachIfNotNull(mCpuFreqTimeMs);
+
+            detachIfNotNull(mScreenOffCpuFreqTimeMs);
+
+            detachIfNotNull(mCpuClusterTimesMs);
+
+            detachIfNotNull(mProcStateTimeMs);
+
+            detachIfNotNull(mProcStateScreenOffTimeMs);
+
+            final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap();
+            for (int iw = wakeStats.size() - 1; iw >= 0; iw--) {
+                Wakelock wl = wakeStats.valueAt(iw);
+                wl.detachFromTimeBase();
+            }
+            final ArrayMap<String, DualTimer> syncStats = mSyncStats.getMap();
+            for (int is = syncStats.size() - 1; is >= 0; is--) {
+                DualTimer timer = syncStats.valueAt(is);
+                detachIfNotNull(timer);
+            }
+            final ArrayMap<String, DualTimer> jobStats = mJobStats.getMap();
+            for (int ij = jobStats.size() - 1; ij >= 0; ij--) {
+                DualTimer timer = jobStats.valueAt(ij);
+                detachIfNotNull(timer);
             }
 
-            return !active;
+            detachIfNotNull(mJobsDeferredEventCount);
+            detachIfNotNull(mJobsDeferredCount);
+            detachIfNotNull(mJobsFreshnessTimeMs);
+            detachIfNotNull(mJobsFreshnessBuckets);
+
+
+            for (int ise = mSensorStats.size() - 1; ise >= 0; ise--) {
+                Sensor s = mSensorStats.valueAt(ise);
+                s.detachFromTimeBase();
+            }
+
+            for (int ip= mProcessStats.size() - 1; ip >= 0; ip--) {
+                Proc proc = mProcessStats.valueAt(ip);
+                proc.detach();
+            }
+            mProcessStats.clear();
+
+            for(int i = mPackageStats.size() - 1; i >= 0; i--) {
+                Pkg p = mPackageStats.valueAt(i);
+                p.detach();
+            }
+            mPackageStats.clear();
         }
 
         void writeJobCompletionsToParcelLocked(Parcel out) {
@@ -8906,35 +8851,24 @@
 
             boolean reset() {
                 boolean wlactive = false;
-                if (mTimerFull != null) {
-                    wlactive |= !mTimerFull.reset(false);
-                }
-                if (mTimerPartial != null) {
-                    wlactive |= !mTimerPartial.reset(false);
-                }
-                if (mTimerWindow != null) {
-                    wlactive |= !mTimerWindow.reset(false);
-                }
-                if (mTimerDraw != null) {
-                    wlactive |= !mTimerDraw.reset(false);
-                }
+
+                wlactive |= !resetIfNotNull(mTimerFull,false);
+                wlactive |= !resetIfNotNull(mTimerPartial,false);
+                wlactive |= !resetIfNotNull(mTimerWindow,false);
+                wlactive |= !resetIfNotNull(mTimerDraw,false);
+
                 if (!wlactive) {
-                    if (mTimerFull != null) {
-                        mTimerFull.detach();
-                        mTimerFull = null;
-                    }
-                    if (mTimerPartial != null) {
-                        mTimerPartial.detach();
-                        mTimerPartial = null;
-                    }
-                    if (mTimerWindow != null) {
-                        mTimerWindow.detach();
-                        mTimerWindow = null;
-                    }
-                    if (mTimerDraw != null) {
-                        mTimerDraw.detach();
-                        mTimerDraw = null;
-                    }
+                    detachIfNotNull(mTimerFull);
+                    mTimerFull = null;
+
+                    detachIfNotNull(mTimerPartial);
+                    mTimerPartial = null;
+
+                    detachIfNotNull(mTimerWindow);
+                    mTimerWindow = null;
+
+                    detachIfNotNull(mTimerDraw);
+                    mTimerDraw = null;
                 }
                 return !wlactive;
             }
@@ -8968,6 +8902,13 @@
                 default: throw new IllegalArgumentException("type = " + type);
                 }
             }
+
+            public void detachFromTimeBase() {
+                detachIfNotNull(mTimerPartial);
+                detachIfNotNull(mTimerFull);
+                detachIfNotNull(mTimerWindow);
+                detachIfNotNull(mTimerDraw);
+            }
         }
 
         public static class Sensor extends BatteryStats.Uid.Sensor {
@@ -9037,6 +8978,10 @@
             public int getHandle() {
                 return mHandle;
             }
+
+            public void  detachFromTimeBase() {
+                detachIfNotNull(mTimer);
+            }
         }
 
         /**
@@ -9168,7 +9113,16 @@
             public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
             }
 
-            void detach() {
+            @Override
+            public boolean reset(boolean detachIfReset) {
+                if (detachIfReset) {
+                    this.detach();
+                }
+                return true;
+            }
+
+            @Override
+            public void detach() {
                 mActive = false;
                 mBsi.mOnBatteryTimeBase.remove(this);
             }
@@ -9407,8 +9361,23 @@
             public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
             }
 
-            void detach() {
+            @Override
+            public boolean reset(boolean detachIfReset) {
+                if (detachIfReset) {
+                    this.detach();
+                }
+                return true;
+            }
+
+            @Override
+            public void detach() {
                 mBsi.mOnBatteryScreenOffTimeBase.remove(this);
+                for (int j = mWakeupAlarms.size() - 1; j >= 0; j--) {
+                    detachIfNotNull(mWakeupAlarms.valueAt(j));
+                }
+                for (int j = mServiceStats.size() - 1; j >= 0; j--) {
+                    detachIfNotNull(mServiceStats.valueAt(j));
+                }
             }
 
             void readFromParcelLocked(Parcel in) {
@@ -9589,9 +9558,18 @@
                         long baseRealtime) {
                 }
 
+                @Override
+                public boolean reset(boolean detachIfReset) {
+                    if (detachIfReset) {
+                        this.detach();
+                    }
+                    return true;
+                }
+
                 /**
                  * Remove this Serv as a listener from the time base.
                  */
+                @Override
                 public void detach() {
                     mBsi.mOnBatteryTimeBase.remove(this);
                 }
@@ -10081,11 +10059,13 @@
             UserInfoProvider userInfoProvider) {
         init(clocks);
 
-        if (systemDir != null) {
-            mFile = new JournaledFile(new File(systemDir, "batterystats.bin"),
-                    new File(systemDir, "batterystats.bin.tmp"));
+
+        if (systemDir == null) {
+            mStatsFile = null;
+            mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer);
         } else {
-            mFile = null;
+            mStatsFile = new AtomicFile(new File(systemDir, "batterystats.bin"));
+            mBatteryStatsHistory = new BatteryStatsHistory(this, systemDir, mHistoryBuffer);
         }
         mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
         mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
@@ -10186,13 +10166,14 @@
 
     public BatteryStatsImpl(Clocks clocks, Parcel p) {
         init(clocks);
-        mFile = null;
+        mStatsFile = null;
         mCheckinFile = null;
         mDailyFile = null;
         mHandler = null;
         mExternalSync = null;
         mConstants = new Constants(mHandler);
         clearHistoryLocked();
+        mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer);
         readFromParcel(p);
         mPlatformIdleStateCallback = null;
     }
@@ -10309,8 +10290,6 @@
                                 stream = mDailyFile.startWrite();
                                 memStream.writeTo(stream);
                                 stream.flush();
-                                FileUtils.sync(stream);
-                                stream.close();
                                 mDailyFile.finishWrite(stream);
                                 com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
                                         "batterystats-daily",
@@ -10614,21 +10593,16 @@
     }
 
     public int getHistoryTotalSize() {
-        return MAX_HISTORY_BUFFER;
+        return mConstants.MAX_HISTORY_BUFFER * mConstants.MAX_HISTORY_FILES;
     }
 
     public int getHistoryUsedSize() {
-        return mHistoryBuffer.dataSize();
+        return mBatteryStatsHistory.getHistoryUsedSize();
     }
 
     @Override
     public boolean startIteratingHistoryLocked() {
-        if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
-                + " pos=" + mHistoryBuffer.dataPosition());
-        if (mHistoryBuffer.dataSize() <= 0) {
-            return false;
-        }
-        mHistoryBuffer.setDataPosition(0);
+        mBatteryStatsHistory.startIteratingHistory();
         mReadOverflow = false;
         mIteratingHistory = true;
         mReadHistoryStrings = new String[mHistoryTagPool.size()];
@@ -10668,18 +10642,13 @@
 
     @Override
     public boolean getNextHistoryLocked(HistoryItem out) {
-        final int pos = mHistoryBuffer.dataPosition();
-        if (pos == 0) {
-            out.clear();
-        }
-        boolean end = pos >= mHistoryBuffer.dataSize();
-        if (end) {
+        Parcel p = mBatteryStatsHistory.getNextParcel(out);
+        if (p == null) {
             return false;
         }
-
         final long lastRealtime = out.time;
         final long lastWalltime = out.currentTime;
-        readHistoryDelta(mHistoryBuffer, out);
+        readHistoryDelta(p, out);
         if (out.cmd != HistoryItem.CMD_CURRENT_TIME
                 && out.cmd != HistoryItem.CMD_RESET && lastWalltime != 0) {
             out.currentTime = lastWalltime + (out.time - lastRealtime);
@@ -10689,9 +10658,10 @@
 
     @Override
     public void finishIteratingHistoryLocked() {
+        mBatteryStatsHistory.finishIteratingHistory();
         mIteratingHistory = false;
-        mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
         mReadHistoryStrings = null;
+        mReadHistoryUids = null;
     }
 
     @Override
@@ -10859,6 +10829,7 @@
 
         for (int i=0; i<mUidStats.size(); i++) {
             if (mUidStats.valueAt(i).reset(uptimeMillis * 1000, elapsedRealtimeMillis * 1000)) {
+                mUidStats.valueAt(i).detachFromTimeBase();
                 mUidStats.remove(mUidStats.keyAt(i));
                 i--;
             }
@@ -10916,6 +10887,8 @@
         initDischarge();
 
         clearHistoryLocked();
+        mBatteryStatsHistory.resetAllFiles();
+
         mHandler.sendEmptyMessage(MSG_REPORT_RESET_STATS);
     }
 
@@ -12163,11 +12136,13 @@
             }
             final Uid u = getUidStatsLocked(uid);
             if (u.mCpuFreqTimeMs == null || u.mCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) {
+                detachIfNotNull(u.mCpuFreqTimeMs);
                 u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase);
             }
             u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs, onBattery);
             if (u.mScreenOffCpuFreqTimeMs == null ||
                     u.mScreenOffCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) {
+                detachIfNotNull(u.mScreenOffCpuFreqTimeMs);
                 u.mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray(
                         mOnBatteryScreenOffTimeBase);
             }
@@ -12176,6 +12151,7 @@
             if (perClusterTimesAvailable) {
                 if (u.mCpuClusterSpeedTimesUs == null ||
                         u.mCpuClusterSpeedTimesUs.length != numClusters) {
+                    detachIfNotNull(u.mCpuClusterSpeedTimesUs);
                     u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
                 }
                 if (numWakelocks > 0 && mWakeLockAllocationsUs == null) {
@@ -12187,6 +12163,7 @@
                     final int speedsInCluster = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
                     if (u.mCpuClusterSpeedTimesUs[cluster] == null ||
                             u.mCpuClusterSpeedTimesUs[cluster].length != speedsInCluster) {
+                        detachIfNotNull(u.mCpuClusterSpeedTimesUs[cluster]);
                         u.mCpuClusterSpeedTimesUs[cluster]
                                 = new LongSamplingCounter[speedsInCluster];
                     }
@@ -12224,6 +12201,7 @@
                 final Uid u = partialTimers.get(i).mUid;
                 if (u.mCpuClusterSpeedTimesUs == null ||
                         u.mCpuClusterSpeedTimesUs.length != numClusters) {
+                    detachIfNotNull(u.mCpuClusterSpeedTimesUs);
                     u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
                 }
 
@@ -12231,6 +12209,7 @@
                     final int speedsInCluster = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
                     if (u.mCpuClusterSpeedTimesUs[cluster] == null ||
                             u.mCpuClusterSpeedTimesUs[cluster].length != speedsInCluster) {
+                        detachIfNotNull(u.mCpuClusterSpeedTimesUs[cluster]);
                         u.mCpuClusterSpeedTimesUs[cluster]
                                 = new LongSamplingCounter[speedsInCluster];
                     }
@@ -12340,9 +12319,7 @@
             boolean reset = false;
             if (!mNoAutoReset && (oldStatus == BatteryManager.BATTERY_STATUS_FULL
                     || level >= 90
-                    || (mDischargeCurrentLevel < 20 && level >= 80)
-                    || (getHighDischargeAmountSinceCharge() >= 200
-                            && mHistoryBuffer.dataSize() >= MAX_HISTORY_BUFFER))) {
+                    || (mDischargeCurrentLevel < 20 && level >= 80))) {
                 Slog.i(TAG, "Resetting battery stats: level=" + level + " status=" + oldStatus
                         + " dischargeLevel=" + mDischargeCurrentLevel
                         + " lowAmount=" + getLowDischargeAmountSinceCharge()
@@ -12364,8 +12341,6 @@
                                     stream = mCheckinFile.startWrite();
                                     stream.write(parcel.marshall());
                                     stream.flush();
-                                    FileUtils.sync(stream);
-                                    stream.close();
                                     mCheckinFile.finishWrite(stream);
                                     com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
                                             "batterystats-checkin",
@@ -12453,7 +12428,7 @@
             mModStepMode = 0;
         }
         if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
-            if (mFile != null) {
+            if (mStatsFile != null && mBatteryStatsHistory.getActiveFile() != null) {
                 writeAsyncLocked();
             }
         }
@@ -12694,7 +12669,7 @@
         if (mMinLearnedBatteryCapacity == -1) {
             mMinLearnedBatteryCapacity = chargeFullUAh;
         } else {
-            Math.min(mMinLearnedBatteryCapacity, chargeFullUAh);
+            mMinLearnedBatteryCapacity = Math.min(mMinLearnedBatteryCapacity, chargeFullUAh);
         }
         mMaxLearnedBatteryCapacity = Math.max(mMaxLearnedBatteryCapacity, chargeFullUAh);
     }
@@ -13172,6 +13147,12 @@
         mUidStats.put(lastUidForUser, null);
         final int firstIndex = mUidStats.indexOfKey(firstUidForUser);
         final int lastIndex = mUidStats.indexOfKey(lastUidForUser);
+        for (int i = firstIndex; i <= lastIndex; i++) {
+            final Uid uid = mUidStats.valueAt(i);
+            if (uid != null) {
+                uid.detachFromTimeBase();
+            }
+        }
         mUidStats.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
     }
 
@@ -13179,6 +13160,10 @@
      * Remove the statistics object for a particular uid.
      */
     public void removeUidStatsLocked(int uid) {
+        final Uid u = mUidStats.get(uid);
+        if (u != null) {
+            u.detachFromTimeBase();
+        }
         mUidStats.remove(uid);
         mPendingRemovedUids.add(new UidToRemove(uid, mClocks.elapsedRealtime()));
     }
@@ -13244,14 +13229,20 @@
                 = "external_stats_collection_rate_limit_ms";
         public static final String KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS
                 = "battery_level_collection_delay_ms";
+        public static final String KEY_MAX_HISTORY_FILES = "max_history_files";
+        public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
 
-        private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
+        private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = false;
         private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
         private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000;
         private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000;
         private static final long DEFAULT_UID_REMOVE_DELAY_MS = 5L * 60L * 1000L;
         private static final long DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = 600_000;
         private static final long DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS = 300_000;
+        private static final int DEFAULT_MAX_HISTORY_FILES = 32;
+        private static final int DEFAULT_MAX_HISTORY_BUFFER_KB = 128; /*Kilo Bytes*/
+        private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64;
+        private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/
 
         public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
         public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
@@ -13262,12 +13253,21 @@
                 = DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS;
         public long BATTERY_LEVEL_COLLECTION_DELAY_MS
                 = DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS;
+        public int MAX_HISTORY_FILES;
+        public int MAX_HISTORY_BUFFER; /*Bytes*/
 
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
         public Constants(Handler handler) {
             super(handler);
+            if (ActivityManager.isLowRamDeviceStatic()) {
+                MAX_HISTORY_FILES = DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE;
+                MAX_HISTORY_BUFFER = DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB * 1024;
+            } else {
+                MAX_HISTORY_FILES = DEFAULT_MAX_HISTORY_FILES;
+                MAX_HISTORY_BUFFER = DEFAULT_MAX_HISTORY_BUFFER_KB * 1024;
+            }
         }
 
         public void startObserving(ContentResolver resolver) {
@@ -13313,13 +13313,23 @@
                 BATTERY_LEVEL_COLLECTION_DELAY_MS = mParser.getLong(
                         KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS,
                         DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS);
+
+                MAX_HISTORY_FILES = mParser.getInt(KEY_MAX_HISTORY_FILES,
+                        ActivityManager.isLowRamDeviceStatic() ?
+                                DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE
+                        : DEFAULT_MAX_HISTORY_FILES);
+                MAX_HISTORY_BUFFER = mParser.getInt(KEY_MAX_HISTORY_BUFFER_KB,
+                        ActivityManager.isLowRamDeviceStatic() ?
+                                DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB
+                                : DEFAULT_MAX_HISTORY_BUFFER_KB)
+                        * 1024;
             }
         }
 
         private void updateTrackCpuTimesByProcStateLocked(boolean wasEnabled, boolean isEnabled) {
             TRACK_CPU_TIMES_BY_PROC_STATE = isEnabled;
             if (isEnabled && !wasEnabled) {
-                mKernelSingleUidTimeReader.markDataAsStale(true);
+                mIsPerProcessStateCpuDataStale = true;
                 mExternalSync.scheduleCpuSyncDueToSettingChange();
 
                 mNumSingleUidCpuTimeReads = 0;
@@ -13366,6 +13376,10 @@
             pw.println(EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS);
             pw.print(KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS); pw.print("=");
             pw.println(BATTERY_LEVEL_COLLECTION_DELAY_MS);
+            pw.print(KEY_MAX_HISTORY_FILES); pw.print("=");
+            pw.println(MAX_HISTORY_FILES);
+            pw.print(KEY_MAX_HISTORY_BUFFER_KB); pw.print("=");
+            pw.println(MAX_HISTORY_BUFFER/1024);
         }
     }
 
@@ -13417,20 +13431,22 @@
         }
     }
 
-    Parcel mPendingWrite = null;
     final ReentrantLock mWriteLock = new ReentrantLock();
 
     public void writeAsyncLocked() {
-        writeLocked(false);
+        writeStatsLocked(false);
+        writeHistoryLocked(false);
     }
 
     public void writeSyncLocked() {
-        writeLocked(true);
+        writeStatsLocked(true);
+        writeHistoryLocked(true);
     }
 
-    void writeLocked(boolean sync) {
-        if (mFile == null) {
-            Slog.w("BatteryStats", "writeLocked: no file associated with this instance");
+    void writeStatsLocked(boolean sync) {
+        if (mStatsFile == null) {
+            Slog.w(TAG,
+                    "writeStatsLocked: no file associated with this instance");
             return;
         }
 
@@ -13438,52 +13454,71 @@
             return;
         }
 
-        Parcel out = Parcel.obtain();
-        writeSummaryToParcel(out, true);
-        mLastWriteTime = mClocks.elapsedRealtime();
-
-        if (mPendingWrite != null) {
-            mPendingWrite.recycle();
+        final Parcel p = Parcel.obtain();
+        final long start = SystemClock.uptimeMillis();
+        writeSummaryToParcel(p, false/*history is in separate file*/);
+        if (DEBUG) {
+            Slog.d(TAG, "writeSummaryToParcel duration ms:"
+                    + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize());
         }
-        mPendingWrite = out;
+        mLastWriteTime = mClocks.elapsedRealtime();
+        writeParcelToFileLocked(p, mStatsFile, sync);
+    }
 
+    void writeHistoryLocked(boolean sync) {
+        if (mBatteryStatsHistory.getActiveFile() == null) {
+            Slog.w(TAG,
+                    "writeHistoryLocked: no history file associated with this instance");
+            return;
+        }
+
+        if (mShuttingDown) {
+            return;
+        }
+
+        Parcel p = Parcel.obtain();
+        final long start = SystemClock.uptimeMillis();
+        writeHistoryBuffer(p, true, true);
+        if (DEBUG) {
+            Slog.d(TAG, "writeHistoryBuffer duration ms:"
+                    + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize());
+        }
+        writeParcelToFileLocked(p, mBatteryStatsHistory.getActiveFile(), sync);
+    }
+
+    void writeParcelToFileLocked(Parcel p, AtomicFile file, boolean sync) {
         if (sync) {
-            commitPendingDataToDisk();
+            commitPendingDataToDisk(p, file);
         } else {
             BackgroundThread.getHandler().post(new Runnable() {
                 @Override public void run() {
-                    commitPendingDataToDisk();
+                    commitPendingDataToDisk(p, file);
                 }
             });
         }
     }
 
-    public void commitPendingDataToDisk() {
-        final Parcel next;
-        synchronized (this) {
-            next = mPendingWrite;
-            mPendingWrite = null;
-            if (next == null) {
-                return;
-            }
-        }
-
+    private void commitPendingDataToDisk(Parcel p, AtomicFile file) {
         mWriteLock.lock();
+        FileOutputStream fos = null;
         try {
             final long startTime = SystemClock.uptimeMillis();
-            FileOutputStream stream = new FileOutputStream(mFile.chooseForWrite());
-            stream.write(next.marshall());
-            stream.flush();
-            FileUtils.sync(stream);
-            stream.close();
-            mFile.commit();
+            fos = file.startWrite();
+            fos.write(p.marshall());
+            fos.flush();
+            file.finishWrite(fos);
+            if (DEBUG) {
+                Slog.d(TAG, "commitPendingDataToDisk file:" + file.getBaseFile().getPath()
+                        + " duration ms:" + (SystemClock.uptimeMillis() - startTime)
+                        + " bytes:" + p.dataSize());
+            }
             com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
                     "batterystats", SystemClock.uptimeMillis() - startTime);
         } catch (IOException e) {
-            Slog.w("BatteryStats", "Error writing battery statistics", e);
-            mFile.rollback();
+            Slog.w(TAG, "Error writing battery statistics", e);
+            file.failWrite(fos);
         } finally {
-            next.recycle();
+            p.recycle();
             mWriteLock.unlock();
         }
     }
@@ -13493,35 +13528,65 @@
             readDailyStatsLocked();
         }
 
-        if (mFile == null) {
-            Slog.w("BatteryStats", "readLocked: no file associated with this instance");
+        if (mStatsFile == null) {
+            Slog.w(TAG, "readLocked: no file associated with this instance");
+            return;
+        }
+
+        if (mBatteryStatsHistory.getActiveFile() == null) {
+            Slog.w(TAG,
+                    "readLocked: no history file associated with this instance");
             return;
         }
 
         mUidStats.clear();
 
+        Parcel stats = Parcel.obtain();
         try {
-            File file = mFile.chooseForRead();
-            if (!file.exists()) {
-                return;
+            final long start = SystemClock.uptimeMillis();
+            byte[] raw = mStatsFile.readFully();
+            stats.unmarshall(raw, 0, raw.length);
+            stats.setDataPosition(0);
+            readSummaryFromParcel(stats);
+            if (DEBUG) {
+                Slog.d(TAG, "readLocked stats file:" + mStatsFile.getBaseFile().getPath()
+                        + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
+                        - start));
             }
-            FileInputStream stream = new FileInputStream(file);
-
-            byte[] raw = BatteryStatsHelper.readFully(stream);
-            Parcel in = Parcel.obtain();
-            in.unmarshall(raw, 0, raw.length);
-            in.setDataPosition(0);
-            stream.close();
-
-            readSummaryFromParcel(in);
-        } catch(Exception e) {
-            Slog.e("BatteryStats", "Error reading battery statistics", e);
+        } catch (Exception e) {
+            Slog.e(TAG, "Error reading battery statistics", e);
             resetAllStatsLocked();
+        } finally {
+            stats.recycle();
+        }
+
+        Parcel history = Parcel.obtain();
+        try {
+            final long start = SystemClock.uptimeMillis();
+            byte[] raw = mBatteryStatsHistory.getActiveFile().readFully();
+            if (raw.length > 0) {
+                history.unmarshall(raw, 0, raw.length);
+                history.setDataPosition(0);
+                readHistoryBuffer(history, true);
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "readLocked history file::"
+                        + mBatteryStatsHistory.getActiveFile().getBaseFile().getPath()
+                        + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
+                        - start));
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Error reading battery history", e);
+            clearHistoryLocked();
+            mBatteryStatsHistory.resetAllFiles();
+        } finally {
+            history.recycle();
         }
 
         mEndPlatformVersion = Build.ID;
 
-        if (mHistoryBuffer.dataPosition() > 0) {
+        if (mHistoryBuffer.dataPosition() > 0
+                || mBatteryStatsHistory.getFilesNumbers().size() > 1) {
             mRecordingHistory = true;
             final long elapsedRealtime = mClocks.elapsedRealtime();
             final long uptime = mClocks.uptimeMillis();
@@ -13539,37 +13604,22 @@
         return 0;
     }
 
-    void readHistory(Parcel in, boolean andOldHistory) throws ParcelFormatException {
+    void  readHistoryBuffer(Parcel in, boolean andOldHistory) throws ParcelFormatException {
+        final int version = in.readInt();
+        if (version != VERSION) {
+            Slog.w("BatteryStats", "readHistoryBuffer: version got " + version
+                    + ", expected " + VERSION + "; erasing old stats");
+            return;
+        }
+
         final long historyBaseTime = in.readLong();
 
         mHistoryBuffer.setDataSize(0);
         mHistoryBuffer.setDataPosition(0);
-        mHistoryTagPool.clear();
-        mNextHistoryTagIdx = 0;
-        mNumHistoryTagChars = 0;
-
-        int numTags = in.readInt();
-        for (int i=0; i<numTags; i++) {
-            int idx = in.readInt();
-            String str = in.readString();
-            if (str == null) {
-                throw new ParcelFormatException("null history tag string");
-            }
-            int uid = in.readInt();
-            HistoryTag tag = new HistoryTag();
-            tag.string = str;
-            tag.uid = uid;
-            tag.poolIdx = idx;
-            mHistoryTagPool.put(tag, idx);
-            if (idx >= mNextHistoryTagIdx) {
-                mNextHistoryTagIdx = idx+1;
-            }
-            mNumHistoryTagChars += tag.string.length() + 1;
-        }
 
         int bufSize = in.readInt();
         int curPos = in.dataPosition();
-        if (bufSize >= (MAX_MAX_HISTORY_BUFFER*3)) {
+        if (bufSize >= (mConstants.MAX_HISTORY_BUFFER*100)) {
             throw new ParcelFormatException("File corrupt: history data buffer too large " +
                     bufSize);
         } else if ((bufSize&~3) != bufSize) {
@@ -13626,7 +13676,7 @@
         }
     }
 
-    void writeHistory(Parcel out, boolean inclData, boolean andOldHistory) {
+    void writeHistoryBuffer(Parcel out, boolean inclData, boolean andOldHistory) {
         if (DEBUG_HISTORY) {
             StringBuilder sb = new StringBuilder(128);
             sb.append("****************** WRITING mHistoryBaseTime: ");
@@ -13635,19 +13685,14 @@
             TimeUtils.formatDuration(mLastHistoryElapsedRealtime, sb);
             Slog.i(TAG, sb.toString());
         }
+        out.writeInt(VERSION);
         out.writeLong(mHistoryBaseTime + mLastHistoryElapsedRealtime);
         if (!inclData) {
             out.writeInt(0);
             out.writeInt(0);
             return;
         }
-        out.writeInt(mHistoryTagPool.size());
-        for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
-            HistoryTag tag = ent.getKey();
-            out.writeInt(ent.getValue());
-            out.writeString(tag.string);
-            out.writeInt(tag.uid);
-        }
+
         out.writeInt(mHistoryBuffer.dataSize());
         if (DEBUG_HISTORY) Slog.i(TAG, "***************** WRITING HISTORY: "
                 + mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition());
@@ -13678,7 +13723,34 @@
             return;
         }
 
-        readHistory(in, true);
+        boolean inclHistory = in.readBoolean();
+        if (inclHistory) {
+            readHistoryBuffer(in, true);
+            mBatteryStatsHistory.readFromParcel(in);
+        }
+
+        mHistoryTagPool.clear();
+        mNextHistoryTagIdx = 0;
+        mNumHistoryTagChars = 0;
+
+        int numTags = in.readInt();
+        for (int i=0; i<numTags; i++) {
+            int idx = in.readInt();
+            String str = in.readString();
+            if (str == null) {
+                throw new ParcelFormatException("null history tag string");
+            }
+            int uid = in.readInt();
+            HistoryTag tag = new HistoryTag();
+            tag.string = str;
+            tag.uid = uid;
+            tag.poolIdx = idx;
+            mHistoryTagPool.put(tag, idx);
+            if (idx >= mNextHistoryTagIdx) {
+                mNextHistoryTagIdx = idx+1;
+            }
+            mNumHistoryTagChars += tag.string.length() + 1;
+        }
 
         mStartCount = in.readInt();
         mUptime = in.readLong();
@@ -13957,7 +14029,7 @@
                 if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numClusters) {
                     throw new ParcelFormatException("Incompatible cpu cluster arrangement");
                 }
-
+                detachIfNotNull(u.mCpuClusterSpeedTimesUs);
                 u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
                 for (int cluster = 0; cluster < numClusters; cluster++) {
                     if (in.readInt() != 0) {
@@ -13981,11 +14053,14 @@
                     }
                 }
             } else {
+                detachIfNotNull(u.mCpuClusterSpeedTimesUs);
                 u.mCpuClusterSpeedTimesUs = null;
             }
 
+            detachIfNotNull(u.mCpuFreqTimeMs);
             u.mCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked(
                     in, mOnBatteryTimeBase);
+            detachIfNotNull(u.mScreenOffCpuFreqTimeMs);
             u.mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked(
                     in, mOnBatteryScreenOffTimeBase);
 
@@ -13994,6 +14069,7 @@
 
             int length = in.readInt();
             if (length == Uid.NUM_PROCESS_STATE) {
+                detachIfNotNull(u.mProcStateTimeMs);
                 u.mProcStateTimeMs = new LongSamplingCounterArray[length];
                 for (int procState = 0; procState < length; ++procState) {
                     u.mProcStateTimeMs[procState]
@@ -14001,10 +14077,12 @@
                                     in, mOnBatteryTimeBase);
                 }
             } else {
+                detachIfNotNull(u.mProcStateTimeMs);
                 u.mProcStateTimeMs = null;
             }
             length = in.readInt();
             if (length == Uid.NUM_PROCESS_STATE) {
+                detachIfNotNull(u.mProcStateScreenOffTimeMs);
                 u.mProcStateScreenOffTimeMs = new LongSamplingCounterArray[length];
                 for (int procState = 0; procState < length; ++procState) {
                     u.mProcStateScreenOffTimeMs[procState]
@@ -14012,20 +14090,25 @@
                                     in, mOnBatteryScreenOffTimeBase);
                 }
             } else {
+                detachIfNotNull(u.mProcStateScreenOffTimeMs);
                 u.mProcStateScreenOffTimeMs = null;
             }
 
             if (in.readInt() != 0) {
+                detachIfNotNull(u.mMobileRadioApWakeupCount);
                 u.mMobileRadioApWakeupCount = new LongSamplingCounter(mOnBatteryTimeBase);
                 u.mMobileRadioApWakeupCount.readSummaryFromParcelLocked(in);
             } else {
+                detachIfNotNull(u.mMobileRadioApWakeupCount);
                 u.mMobileRadioApWakeupCount = null;
             }
 
             if (in.readInt() != 0) {
+                detachIfNotNull(u.mWifiRadioApWakeupCount);
                 u.mWifiRadioApWakeupCount = new LongSamplingCounter(mOnBatteryTimeBase);
                 u.mWifiRadioApWakeupCount.readSummaryFromParcelLocked(in);
             } else {
+                detachIfNotNull(u.mWifiRadioApWakeupCount);
                 u.mWifiRadioApWakeupCount = null;
             }
 
@@ -14061,6 +14144,7 @@
             u.mJobsDeferredEventCount.readSummaryFromParcelLocked(in);
             u.mJobsDeferredCount.readSummaryFromParcelLocked(in);
             u.mJobsFreshnessTimeMs.readSummaryFromParcelLocked(in);
+            detachIfNotNull(u.mJobsFreshnessBuckets);
             for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) {
                 if (in.readInt() != 0) {
                     u.mJobsFreshnessBuckets[i] = new Counter(u.mBsi.mOnBatteryTimeBase);
@@ -14101,9 +14185,10 @@
             }
             for (int ip = 0; ip < NP; ip++) {
                 String pkgName = in.readString();
+                detachIfNotNull(u.mPackageStats.get(pkgName));
                 Uid.Pkg p = u.getPackageStatsLocked(pkgName);
                 final int NWA = in.readInt();
-                if (NWA > 1000) {
+                if (NWA > 10000) {
                     throw new ParcelFormatException("File corrupt: too many wakeup alarms " + NWA);
                 }
                 p.mWakeupAlarms.clear();
@@ -14114,7 +14199,7 @@
                     p.mWakeupAlarms.put(tag, c);
                 }
                 NS = in.readInt();
-                if (NS > 1000) {
+                if (NS > 10000) {
                     throw new ParcelFormatException("File corrupt: too many services " + NS);
                 }
                 for (int is = 0; is < NS; is++) {
@@ -14146,7 +14231,19 @@
 
         out.writeInt(VERSION);
 
-        writeHistory(out, inclHistory, true);
+        out.writeBoolean(inclHistory);
+        if (inclHistory) {
+            writeHistoryBuffer(out, true, true);
+            mBatteryStatsHistory.writeToParcel(out);
+        }
+
+        out.writeInt(mHistoryTagPool.size());
+        for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
+            HistoryTag tag = ent.getKey();
+            out.writeInt(ent.getValue());
+            out.writeString(tag.string);
+            out.writeInt(tag.uid);
+        }
 
         out.writeInt(mStartCount);
         out.writeLong(computeUptime(NOW_SYS, STATS_SINCE_CHARGED));
@@ -14644,7 +14741,8 @@
             throw new ParcelFormatException("Bad magic number: #" + Integer.toHexString(magic));
         }
 
-        readHistory(in, false);
+        readHistoryBuffer(in, false);
+        mBatteryStatsHistory.readFromParcel(in);
 
         mStartCount = in.readInt();
         mStartClockTime = in.readLong();
@@ -14873,7 +14971,8 @@
 
         out.writeInt(MAGIC);
 
-        writeHistory(out, true, false);
+        writeHistoryBuffer(out, true, false);
+        mBatteryStatsHistory.writeToParcel(out);
 
         out.writeInt(mStartCount);
         out.writeLong(startClockTime);
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index fbb99e4..4aa30f6 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -16,23 +16,39 @@
 
 package com.android.internal.os;
 
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
 import android.os.Binder;
+import android.os.OsProtoEnums;
+import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
+import android.util.Pair;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
+import com.android.internal.os.BinderInternal.CallSession;
+import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Queue;
+import java.util.Random;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.function.ToDoubleFunction;
 
@@ -40,127 +56,335 @@
  * Collects statistics about CPU time spent per binder call across multiple dimensions, e.g.
  * per thread, uid or call description.
  */
-public class BinderCallsStats {
+public class BinderCallsStats implements BinderInternal.Observer {
+    public static final boolean ENABLED_DEFAULT = false;
+    public static final boolean DETAILED_TRACKING_DEFAULT = true;
+    public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 100;
+
+    private static final String TAG = "BinderCallsStats";
     private static final int CALL_SESSIONS_POOL_SIZE = 100;
     private static final int PERIODIC_SAMPLING_INTERVAL = 10;
-    private static final BinderCallsStats sInstance = new BinderCallsStats();
+    private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
+    private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
 
-    private volatile boolean mDetailedTracking = false;
+    // Whether to collect all the data: cpu + exceptions + reply/request sizes.
+    private boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT;
+    // Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out
+    // of 100 requests.
+    private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
     @GuardedBy("mLock")
     private final SparseArray<UidEntry> mUidEntries = new SparseArray<>();
+    @GuardedBy("mLock")
+    private final ArrayMap<String, Integer> mExceptionCounts = new ArrayMap<>();
     private final Queue<CallSession> mCallSessionsPool = new ConcurrentLinkedQueue<>();
     private final Object mLock = new Object();
+    private final Random mRandom;
     private long mStartTime = System.currentTimeMillis();
-    @GuardedBy("mLock")
-    private UidEntry mSampledEntries = new UidEntry(-1);
 
-    private BinderCallsStats() {
-    }
-
-    @VisibleForTesting
-    public BinderCallsStats(boolean detailedTracking) {
-        mDetailedTracking = detailedTracking;
-    }
-
-    public CallSession callStarted(Binder binder, int code) {
-        return callStarted(binder.getClass().getName(), code);
-    }
-
-    private CallSession callStarted(String className, int code) {
-        CallSession s = mCallSessionsPool.poll();
-        if (s == null) {
-            s = new CallSession();
+    // State updated by the broadcast receiver below.
+    private boolean mScreenInteractive;
+    private boolean mCharging;
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case Intent.ACTION_BATTERY_CHANGED:
+                    mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+                    break;
+                case Intent.ACTION_SCREEN_ON:
+                    mScreenInteractive = true;
+                    break;
+                case Intent.ACTION_SCREEN_OFF:
+                    mScreenInteractive = false;
+                    break;
+            }
         }
-        s.callStat.className = className;
-        s.callStat.msg = code;
+    };
+
+    /** Injector for {@link BinderCallsStats}. */
+    public static class Injector {
+        public Random getRandomGenerator() {
+            return new Random();
+        }
+    }
+
+    public BinderCallsStats(Injector injector) {
+        this.mRandom = injector.getRandomGenerator();
+    }
+
+    public void systemReady(Context context) {
+        registerBroadcastReceiver(context);
+        setInitialState(queryScreenInteractive(context), queryIsCharging());
+    }
+
+    /**
+     * Listens for screen/battery state changes.
+     */
+    @VisibleForTesting
+    public void registerBroadcastReceiver(Context context) {
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        filter.addAction(Intent.ACTION_SCREEN_ON);
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        context.registerReceiver(mBroadcastReceiver, filter);
+    }
+
+    /**
+     * Sets the battery/screen initial state.
+     *
+     * This has to be updated *after* the broadcast receiver is installed.
+     */
+    @VisibleForTesting
+    public void setInitialState(boolean isScreenInteractive, boolean isCharging) {
+        this.mScreenInteractive = isScreenInteractive;
+        this.mCharging = isCharging;
+        // Data collected previously was not accurate since the battery/screen state was not set.
+        reset();
+    }
+
+    private boolean queryIsCharging() {
+        final BatteryManagerInternal batteryManager =
+                LocalServices.getService(BatteryManagerInternal.class);
+        if (batteryManager == null) {
+            Slog.wtf(TAG, "BatteryManager null while starting BinderCallsStatsService");
+            // Default to true to not collect any data.
+            return true;
+        } else {
+            return batteryManager.getPlugType() != OsProtoEnums.BATTERY_PLUGGED_NONE;
+        }
+    }
+
+    private boolean queryScreenInteractive(Context context) {
+        final PowerManager powerManager = context.getSystemService(PowerManager.class);
+        final boolean screenInteractive;
+        if (powerManager == null) {
+            Slog.wtf(TAG, "PowerManager null while starting BinderCallsStatsService",
+                    new Throwable());
+            return true;
+        } else {
+            return powerManager.isInteractive();
+        }
+    }
+
+    @Override
+    @Nullable
+    public CallSession callStarted(Binder binder, int code) {
+        if (mCharging) {
+            return null;
+        }
+
+        final CallSession s = obtainCallSession();
+        s.binderClass = binder.getClass();
+        s.transactionCode = code;
         s.exceptionThrown = false;
         s.cpuTimeStarted = -1;
         s.timeStarted = -1;
-
-        synchronized (mLock) {
-            if (mDetailedTracking) {
-                s.cpuTimeStarted = getThreadTimeMicro();
-                s.timeStarted = getElapsedRealtimeMicro();
-            } else {
-                s.sampledCallStat = mSampledEntries.getOrCreate(s.callStat);
-                if (s.sampledCallStat.callCount % PERIODIC_SAMPLING_INTERVAL == 0) {
-                    s.cpuTimeStarted = getThreadTimeMicro();
-                    s.timeStarted = getElapsedRealtimeMicro();
-                }
-            }
+        if (shouldRecordDetailedData()) {
+            s.cpuTimeStarted = getThreadTimeMicro();
+            s.timeStarted = getElapsedRealtimeMicro();
         }
         return s;
     }
 
-    public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
-        Preconditions.checkNotNull(s);
-        synchronized (mLock) {
-            long duration;
-            long latencyDuration;
-            if (mDetailedTracking) {
-                duration = getThreadTimeMicro() - s.cpuTimeStarted;
-                latencyDuration = getElapsedRealtimeMicro() - s.timeStarted;
-            } else {
-                CallStat cs = s.sampledCallStat;
-                // Non-negative time signals beginning of the new sampling interval
-                if (s.cpuTimeStarted >= 0) {
-                    duration = getThreadTimeMicro() - s.cpuTimeStarted;
-                    latencyDuration = getElapsedRealtimeMicro() - s.timeStarted;
-                } else {
-                    // callCount is always incremented, but time only once per sampling interval
-                    long samplesCount = cs.callCount / PERIODIC_SAMPLING_INTERVAL + 1;
-                    duration = cs.cpuTimeMicros / samplesCount;
-                    latencyDuration = cs.latencyMicros / samplesCount;
-                }
-            }
+    private CallSession obtainCallSession() {
+        CallSession s = mCallSessionsPool.poll();
+        return s == null ? new CallSession() : s;
+    }
 
-            int callingUid = getCallingUid();
-
-            UidEntry uidEntry = mUidEntries.get(callingUid);
-            if (uidEntry == null) {
-                uidEntry = new UidEntry(callingUid);
-                mUidEntries.put(callingUid, uidEntry);
-            }
-
-            if (mDetailedTracking) {
-                // Find CallStat entry and update its total time
-                CallStat callStat = uidEntry.getOrCreate(s.callStat);
-                callStat.callCount++;
-                callStat.cpuTimeMicros += duration;
-                callStat.latencyMicros += latencyDuration;
-                callStat.exceptionCount += s.exceptionThrown ? 1 : 0;
-                callStat.maxLatencyMicros = Math.max(callStat.maxLatencyMicros, latencyDuration);
-                callStat.maxRequestSizeBytes =
-                        Math.max(callStat.maxRequestSizeBytes, parcelRequestSize);
-                callStat.maxReplySizeBytes =
-                        Math.max(callStat.maxReplySizeBytes, parcelReplySize);
-            } else {
-                // update sampled timings in the beginning of each interval
-                if (s.cpuTimeStarted >= 0) {
-                    s.sampledCallStat.cpuTimeMicros += duration;
-                    s.sampledCallStat.latencyMicros += latencyDuration;
-                }
-                s.sampledCallStat.callCount++;
-            }
-
-            uidEntry.cpuTimeMicros += duration;
-            uidEntry.callCount++;
+    @Override
+    public void callEnded(@Nullable CallSession s, int parcelRequestSize, int parcelReplySize) {
+        if (s == null) {
+            return;
         }
+
+        processCallEnded(s, parcelRequestSize, parcelReplySize);
+
         if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) {
             mCallSessionsPool.add(s);
         }
     }
 
-    /**
-     * Called if an exception is thrown while executing the binder transaction.
-     *
-     * <li>BinderCallsStats#callEnded will be called afterwards.
-     * <li>Do not throw an exception in this method, it will swallow the original exception thrown
-     * by the binder transaction.
-     */
-    public void callThrewException(CallSession s) {
-        Preconditions.checkNotNull(s);
+    private void processCallEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
+        // Non-negative time signals we need to record data for this call.
+        final boolean recordCall = s.cpuTimeStarted >= 0;
+        final long duration;
+        final long latencyDuration;
+        if (recordCall) {
+            duration = getThreadTimeMicro() - s.cpuTimeStarted;
+            latencyDuration = getElapsedRealtimeMicro() - s.timeStarted;
+        } else {
+            duration = 0;
+            latencyDuration = 0;
+        }
+        final int callingUid = getCallingUid();
+
+        synchronized (mLock) {
+            // This was already checked in #callStart but check again while synchronized.
+            if (mCharging) {
+                return;
+            }
+
+            final UidEntry uidEntry = getUidEntry(callingUid);
+            uidEntry.callCount++;
+
+            if (recordCall) {
+                uidEntry.cpuTimeMicros += duration;
+                uidEntry.recordedCallCount++;
+
+                final CallStat callStat = uidEntry.getOrCreate(
+                        s.binderClass, s.transactionCode, mScreenInteractive);
+                callStat.callCount++;
+                callStat.recordedCallCount++;
+                callStat.cpuTimeMicros += duration;
+                callStat.maxCpuTimeMicros = Math.max(callStat.maxCpuTimeMicros, duration);
+                callStat.latencyMicros += latencyDuration;
+                callStat.maxLatencyMicros =
+                        Math.max(callStat.maxLatencyMicros, latencyDuration);
+                if (mDetailedTracking) {
+                    callStat.exceptionCount += s.exceptionThrown ? 1 : 0;
+                    callStat.maxRequestSizeBytes =
+                            Math.max(callStat.maxRequestSizeBytes, parcelRequestSize);
+                    callStat.maxReplySizeBytes =
+                            Math.max(callStat.maxReplySizeBytes, parcelReplySize);
+                }
+            } else {
+                // Only record the total call count if we already track data for this key.
+                // It helps to keep the memory usage down when sampling is enabled.
+                final CallStat callStat = uidEntry.get(
+                        s.binderClass, s.transactionCode, mScreenInteractive);
+                if (callStat != null) {
+                    callStat.callCount++;
+                }
+            }
+        }
+    }
+
+    private UidEntry getUidEntry(int uid) {
+        UidEntry uidEntry = mUidEntries.get(uid);
+        if (uidEntry == null) {
+            uidEntry = new UidEntry(uid);
+            mUidEntries.put(uid, uidEntry);
+        }
+        return uidEntry;
+    }
+
+    @Override
+    public void callThrewException(@Nullable CallSession s, Exception exception) {
+        if (s == null) {
+            return;
+        }
         s.exceptionThrown = true;
+        try {
+            String className = exception.getClass().getName();
+            synchronized (mLock) {
+                if (mExceptionCounts.size() >= MAX_EXCEPTION_COUNT_SIZE) {
+                    className = EXCEPTION_COUNT_OVERFLOW_NAME;
+                }
+                final Integer count = mExceptionCounts.get(className);
+                mExceptionCounts.put(className, count == null ? 1 : count + 1);
+            }
+        } catch (RuntimeException e) {
+            // Do not propagate the exception. We do not want to swallow original exception.
+            Slog.wtf(TAG, "Unexpected exception while updating mExceptionCounts");
+        }
+    }
+
+    @Nullable
+    private Method getDefaultTransactionNameMethod(Class<? extends Binder> binder) {
+        try {
+            return binder.getMethod("getDefaultTransactionName", int.class);
+        } catch (NoSuchMethodException e) {
+            // The method might not be present for stubs not generated with AIDL.
+            return null;
+        }
+    }
+
+    @Nullable
+    private String resolveTransactionCode(Method getDefaultTransactionName, int transactionCode) {
+        if (getDefaultTransactionName == null) {
+            return null;
+        }
+
+        try {
+            return (String) getDefaultTransactionName.invoke(null, transactionCode);
+        } catch (IllegalAccessException | InvocationTargetException | ClassCastException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * This method is expensive to call.
+     */
+    public ArrayList<ExportedCallStat> getExportedCallStats() {
+        // We do not collect all the data if detailed tracking is off.
+        if (!mDetailedTracking) {
+            return new ArrayList<ExportedCallStat>();
+        }
+
+        ArrayList<ExportedCallStat> resultCallStats = new ArrayList<>();
+        synchronized (mLock) {
+            final int uidEntriesSize = mUidEntries.size();
+            for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++){
+                final UidEntry entry = mUidEntries.valueAt(entryIdx);
+                for (CallStat stat : entry.getCallStatsList()) {
+                    ExportedCallStat exported = new ExportedCallStat();
+                    exported.uid = entry.uid;
+                    exported.className = stat.binderClass.getName();
+                    exported.binderClass = stat.binderClass;
+                    exported.transactionCode = stat.transactionCode;
+                    exported.screenInteractive = stat.screenInteractive;
+                    exported.cpuTimeMicros = stat.cpuTimeMicros;
+                    exported.maxCpuTimeMicros = stat.maxCpuTimeMicros;
+                    exported.latencyMicros = stat.latencyMicros;
+                    exported.maxLatencyMicros = stat.maxLatencyMicros;
+                    exported.recordedCallCount = stat.recordedCallCount;
+                    exported.callCount = stat.callCount;
+                    exported.maxRequestSizeBytes = stat.maxRequestSizeBytes;
+                    exported.maxReplySizeBytes = stat.maxReplySizeBytes;
+                    exported.exceptionCount = stat.exceptionCount;
+                    resultCallStats.add(exported);
+                }
+            }
+        }
+
+        // Resolve codes outside of the lock since it can be slow.
+        ExportedCallStat previous = null;
+        // Cache the previous method/transaction code.
+        Method getDefaultTransactionName = null;
+        String previousMethodName = null;
+        resultCallStats.sort(BinderCallsStats::compareByBinderClassAndCode);
+        for (ExportedCallStat exported : resultCallStats) {
+            final boolean isClassDifferent = previous == null
+                    || !previous.className.equals(exported.className);
+            if (isClassDifferent) {
+                getDefaultTransactionName = getDefaultTransactionNameMethod(exported.binderClass);
+            }
+
+            final boolean isCodeDifferent = previous == null
+                    || previous.transactionCode != exported.transactionCode;
+            final String methodName;
+            if (isClassDifferent || isCodeDifferent) {
+                String resolvedCode = resolveTransactionCode(
+                        getDefaultTransactionName, exported.transactionCode);
+                methodName = resolvedCode == null
+                        ? String.valueOf(exported.transactionCode)
+                        : resolvedCode;
+            } else {
+                methodName = previousMethodName;
+            }
+            previousMethodName = methodName;
+            exported.methodName = methodName;
+        }
+
+        return resultCallStats;
+    }
+
+    /** @hide */
+    public ArrayMap<String, Integer> getExportedExceptionStats() {
+        synchronized (mLock) {
+            return new ArrayMap(mExceptionCounts);
+        }
     }
 
     public void dump(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) {
@@ -171,84 +395,91 @@
 
     private void dumpLocked(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) {
         long totalCallsCount = 0;
+        long totalRecordedCallsCount = 0;
         long totalCpuTime = 0;
         pw.print("Start time: ");
         pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartTime));
-        List<UidEntry> entries = new ArrayList<>();
+        pw.println("Sampling interval period: " + mPeriodicSamplingInterval);
+        final List<UidEntry> entries = new ArrayList<>();
 
-        int uidEntriesSize = mUidEntries.size();
+        final int uidEntriesSize = mUidEntries.size();
         for (int i = 0; i < uidEntriesSize; i++) {
             UidEntry e = mUidEntries.valueAt(i);
             entries.add(e);
             totalCpuTime += e.cpuTimeMicros;
+            totalRecordedCallsCount += e.recordedCallCount;
             totalCallsCount += e.callCount;
         }
 
         entries.sort(Comparator.<UidEntry>comparingDouble(value -> value.cpuTimeMicros).reversed());
-        String datasetSizeDesc = verbose ? "" : "(top 90% by cpu time) ";
-        StringBuilder sb = new StringBuilder();
-        if (mDetailedTracking) {
-            pw.println("Per-UID raw data " + datasetSizeDesc
-                    + "(uid, call_desc, cpu_time_micros, latency_time_micros, "
-                    + "max_latency_time_micros, exception_count, max_request_size_bytes, "
-                    + "max_reply_size_bytes, call_count):");
-            List<UidEntry> topEntries = verbose ? entries
-                    : getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
-            for (UidEntry uidEntry : topEntries) {
-                for (CallStat e : uidEntry.getCallStatsList()) {
-                    sb.setLength(0);
-                    sb.append("    ")
-                            .append(uidEntry.uid).append(",").append(e)
-                            .append(',').append(e.cpuTimeMicros)
-                            .append(',').append(e.latencyMicros)
-                            .append(',').append(e.maxLatencyMicros)
-                            .append(',').append(e.exceptionCount)
-                            .append(',').append(e.maxRequestSizeBytes)
-                            .append(',').append(e.maxReplySizeBytes)
-                            .append(',').append(e.callCount);
-                    pw.println(sb);
-                }
-            }
-            pw.println();
-        } else {
-            pw.println("Sampled stats " + datasetSizeDesc
-                    + "(call_desc, cpu_time, call_count, exception_count):");
-            List<CallStat> sampledStatsList = mSampledEntries.getCallStatsList();
-            // Show all if verbose, otherwise 90th percentile
-            if (!verbose) {
-                sampledStatsList = getHighestValues(sampledStatsList,
-                        value -> value.cpuTimeMicros, 0.9);
-            }
-            for (CallStat e : sampledStatsList) {
-                sb.setLength(0);
-                sb.append("    ").append(e)
-                        .append(',').append(e.cpuTimeMicros * PERIODIC_SAMPLING_INTERVAL)
-                        .append(',').append(e.callCount)
-                        .append(',').append(e.exceptionCount);
-                pw.println(sb);
-            }
-            pw.println();
+        final String datasetSizeDesc = verbose ? "" : "(top 90% by cpu time) ";
+        final StringBuilder sb = new StringBuilder();
+        final List<UidEntry> topEntries = verbose ? entries
+                : getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
+        pw.println("Per-UID raw data " + datasetSizeDesc
+                + "(package/uid, call_desc, screen_interactive, "
+                + "cpu_time_micros, max_cpu_time_micros, "
+                + "latency_time_micros, max_latency_time_micros, exception_count, "
+                + "max_request_size_bytes, max_reply_size_bytes, recorded_call_count, "
+                + "call_count):");
+        final List<ExportedCallStat> exportedCallStats = getExportedCallStats();
+        exportedCallStats.sort(BinderCallsStats::compareByCpuDesc);
+        for (ExportedCallStat e : exportedCallStats) {
+            sb.setLength(0);
+            sb.append("    ")
+                    .append(uidToString(e.uid, appIdToPkgNameMap))
+                    .append(',').append(e.className)
+                    .append('#').append(e.methodName)
+                    .append(',').append(e.screenInteractive)
+                    .append(',').append(e.cpuTimeMicros)
+                    .append(',').append(e.maxCpuTimeMicros)
+                    .append(',').append(e.latencyMicros)
+                    .append(',').append(e.maxLatencyMicros)
+                    .append(',').append(mDetailedTracking ? e.exceptionCount : '_')
+                    .append(',').append(mDetailedTracking ? e.maxRequestSizeBytes : '_')
+                    .append(',').append(mDetailedTracking ? e.maxReplySizeBytes : '_')
+                    .append(',').append(e.recordedCallCount)
+                    .append(',').append(e.callCount);
+            pw.println(sb);
         }
+        pw.println();
         pw.println("Per-UID Summary " + datasetSizeDesc
-                + "(cpu_time, % of total cpu_time, call_count, exception_count, package/uid):");
-        List<UidEntry> summaryEntries = verbose ? entries
+                + "(cpu_time, % of total cpu_time, recorded_call_count, call_count, package/uid):");
+        final List<UidEntry> summaryEntries = verbose ? entries
                 : getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
         for (UidEntry entry : summaryEntries) {
             String uidStr = uidToString(entry.uid, appIdToPkgNameMap);
-            pw.println(String.format("  %10d %3.0f%% %8d %3d %s",
-                    entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime, entry.callCount,
-                    entry.exceptionCount, uidStr));
+            pw.println(String.format("  %10d %3.0f%% %8d %8d %s",
+                        entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
+                        entry.recordedCallCount, entry.callCount, uidStr));
         }
         pw.println();
         pw.println(String.format("  Summary: total_cpu_time=%d, "
-                        + "calls_count=%d, avg_call_cpu_time=%.0f",
-                totalCpuTime, totalCallsCount, (double)totalCpuTime / totalCallsCount));
+                    + "calls_count=%d, avg_call_cpu_time=%.0f",
+                    totalCpuTime, totalCallsCount, (double)totalCpuTime / totalRecordedCallsCount));
+        pw.println();
+
+        pw.println("Exceptions thrown (exception_count, class_name):");
+        final List<Pair<String, Integer>> exceptionEntries = new ArrayList<>();
+        // We cannot use new ArrayList(Collection) constructor because MapCollections does not
+        // implement toArray method.
+        mExceptionCounts.entrySet().iterator().forEachRemaining(
+                (e) -> exceptionEntries.add(Pair.create(e.getKey(), e.getValue())));
+        exceptionEntries.sort((e1, e2) -> Integer.compare(e2.second, e1.second));
+        for (Pair<String, Integer> entry : exceptionEntries) {
+            pw.println(String.format("  %6d %s", entry.second, entry.first));
+        }
+
+        if (mPeriodicSamplingInterval != 1) {
+            pw.println("");
+            pw.println("/!\\ Displayed data is sampled. See sampling interval at the top.");
+        }
     }
 
     private static String uidToString(int uid, Map<Integer, String> pkgNameMap) {
-        int appId = UserHandle.getAppId(uid);
-        String pkgName = pkgNameMap == null ? null : pkgNameMap.get(appId);
-        String uidStr = UserHandle.formatUid(uid);
+        final int appId = UserHandle.getAppId(uid);
+        final String pkgName = pkgNameMap == null ? null : pkgNameMap.get(appId);
+        final String uidStr = UserHandle.formatUid(uid);
         return pkgName == null ? uidStr : pkgName + '/' + uidStr;
     }
 
@@ -260,48 +491,106 @@
         return Binder.getCallingUid();
     }
 
-    private long getElapsedRealtimeMicro() {
+    protected long getElapsedRealtimeMicro() {
         return SystemClock.elapsedRealtimeNanos() / 1000;
     }
 
-    public static BinderCallsStats getInstance() {
-        return sInstance;
+    private boolean shouldRecordDetailedData() {
+        return mRandom.nextInt() % mPeriodicSamplingInterval == 0;
     }
 
+    /**
+     * Sets to true to collect all the data.
+     */
     public void setDetailedTracking(boolean enabled) {
-        if (enabled != mDetailedTracking) {
-            reset();
-            mDetailedTracking = enabled;
+        synchronized (mLock) {
+            if (enabled != mDetailedTracking) {
+                mDetailedTracking = enabled;
+                reset();
+            }
+        }
+    }
+
+    public void setSamplingInterval(int samplingInterval) {
+        synchronized (mLock) {
+            if (samplingInterval != mPeriodicSamplingInterval) {
+                mPeriodicSamplingInterval = samplingInterval;
+                reset();
+            }
         }
     }
 
     public void reset() {
         synchronized (mLock) {
             mUidEntries.clear();
-            mSampledEntries.mCallStats.clear();
+            mExceptionCounts.clear();
             mStartTime = System.currentTimeMillis();
         }
     }
 
-    @VisibleForTesting
-    public static class CallStat {
+    /**
+     * Aggregated data by uid/class/method to be sent through WestWorld.
+     */
+    public static class ExportedCallStat {
+        public int uid;
         public String className;
-        public int msg;
+        public String methodName;
+        public boolean screenInteractive;
         public long cpuTimeMicros;
+        public long maxCpuTimeMicros;
         public long latencyMicros;
         public long maxLatencyMicros;
+        public long callCount;
+        public long recordedCallCount;
         public long maxRequestSizeBytes;
         public long maxReplySizeBytes;
-        public long callCount;
         public long exceptionCount;
 
-        CallStat() {
-        }
+        // Used internally.
+        Class<? extends Binder> binderClass;
+        int transactionCode;
+    }
 
-        CallStat(String className, int msg) {
-            this.className = className;
-            this.msg = msg;
+    @VisibleForTesting
+    public static class CallStat {
+        public Class<? extends Binder> binderClass;
+        public int transactionCode;
+        // True if the screen was interactive when the call ended.
+        public boolean screenInteractive;
+        // Number of calls for which we collected data for. We do not record data for all the calls
+        // when sampling is on.
+        public long recordedCallCount;
+        // Roughly the real number of total calls. We only track only track the API call count once
+        // at least one non-sampled count happened.
+        public long callCount;
+        // Total CPU of all for all the recorded calls.
+        // Approximate total CPU usage can be computed by
+        // cpuTimeMicros * callCount / recordedCallCount
+        public long cpuTimeMicros;
+        public long maxCpuTimeMicros;
+        // Total latency of all for all the recorded calls.
+        // Approximate average latency can be computed by
+        // latencyMicros * callCount / recordedCallCount
+        public long latencyMicros;
+        public long maxLatencyMicros;
+        // The following fields are only computed if mDetailedTracking is set.
+        public long maxRequestSizeBytes;
+        public long maxReplySizeBytes;
+        public long exceptionCount;
+
+        CallStat(Class<? extends Binder> binderClass, int transactionCode,
+                boolean screenInteractive) {
+            this.binderClass = binderClass;
+            this.transactionCode = transactionCode;
+            this.screenInteractive = screenInteractive;
         }
+    }
+
+    /** Key used to store CallStat object in a Map. */
+    public static class CallStatKey {
+        public Class<? extends Binder> binderClass;
+        public int transactionCode;
+        private boolean screenInteractive;
 
         @Override
         public boolean equals(Object o) {
@@ -309,52 +598,64 @@
                 return true;
             }
 
-            CallStat callStat = (CallStat) o;
-
-            return msg == callStat.msg && (className.equals(callStat.className));
+            final CallStatKey key = (CallStatKey) o;
+            return transactionCode == key.transactionCode
+                    && screenInteractive == key.screenInteractive
+                    && (binderClass.equals(key.binderClass));
         }
 
         @Override
         public int hashCode() {
-            int result = className.hashCode();
-            result = 31 * result + msg;
+            int result = binderClass.hashCode();
+            result = 31 * result + transactionCode;
+            result = 31 * result + (screenInteractive ? 1231 : 1237);
             return result;
         }
-
-        @Override
-        public String toString() {
-            return className + "/" + msg;
-        }
     }
 
-    public static class CallSession {
-        long cpuTimeStarted;
-        long timeStarted;
-        boolean exceptionThrown;
-        final CallStat callStat = new CallStat();
-        CallStat sampledCallStat;
-    }
 
     @VisibleForTesting
     public static class UidEntry {
         int uid;
-        public long cpuTimeMicros;
+        // Number of calls for which we collected data for. We do not record data for all the calls
+        // when sampling is on.
+        public long recordedCallCount;
+        // Real number of total calls.
         public long callCount;
-        public int exceptionCount;
+        // Total CPU of all for all the recorded calls.
+        // Approximate total CPU usage can be computed by
+        // cpuTimeMicros * callCount / recordedCallCount
+        public long cpuTimeMicros;
 
         UidEntry(int uid) {
             this.uid = uid;
         }
 
         // Aggregate time spent per each call name: call_desc -> cpu_time_micros
-        Map<CallStat, CallStat> mCallStats = new ArrayMap<>();
+        private Map<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
+        private CallStatKey mTempKey = new CallStatKey();
 
-        CallStat getOrCreate(CallStat callStat) {
-            CallStat mapCallStat = mCallStats.get(callStat);
+        @Nullable
+        CallStat get(Class<? extends Binder> binderClass, int transactionCode,
+                boolean screenInteractive) {
+            // Use a global temporary key to avoid creating new objects for every lookup.
+            mTempKey.binderClass = binderClass;
+            mTempKey.transactionCode = transactionCode;
+            mTempKey.screenInteractive = screenInteractive;
+            return mCallStats.get(mTempKey);
+        }
+
+        CallStat getOrCreate(Class<? extends Binder> binderClass, int transactionCode,
+                boolean screenInteractive) {
+            CallStat mapCallStat = get(binderClass, transactionCode, screenInteractive);
             // Only create CallStat if it's a new entry, otherwise update existing instance
             if (mapCallStat == null) {
-                mapCallStat = new CallStat(callStat.className, callStat.msg);
-                mCallStats.put(mapCallStat, mapCallStat);
+                mapCallStat = new CallStat(binderClass, transactionCode, screenInteractive);
+                CallStatKey key = new CallStatKey();
+                key.binderClass = binderClass;
+                key.transactionCode = transactionCode;
+                key.screenInteractive = screenInteractive;
+                mCallStats.put(key, mapCallStat);
             }
             return mapCallStat;
         }
@@ -362,17 +663,8 @@
         /**
          * Returns list of calls sorted by CPU time
          */
-        public List<CallStat> getCallStatsList() {
-            List<CallStat> callStats = new ArrayList<>(mCallStats.keySet());
-            callStats.sort((o1, o2) -> {
-                if (o1.cpuTimeMicros < o2.cpuTimeMicros) {
-                    return 1;
-                } else if (o1.cpuTimeMicros > o2.cpuTimeMicros) {
-                    return -1;
-                }
-                return 0;
-            });
-            return callStats;
+        public Collection<CallStat> getCallStatsList() {
+            return mCallStats.values();
         }
 
         @Override
@@ -406,8 +698,8 @@
     }
 
     @VisibleForTesting
-    public UidEntry getSampledEntries() {
-        return mSampledEntries;
+    public ArrayMap<String, Integer> getExceptionCounts() {
+        return mExceptionCounts;
     }
 
     @VisibleForTesting
@@ -431,4 +723,21 @@
         return result;
     }
 
+    @VisibleForTesting
+    public BroadcastReceiver getBroadcastReceiver() {
+        return mBroadcastReceiver;
+    }
+
+    private static int compareByCpuDesc(
+            ExportedCallStat a, ExportedCallStat b) {
+        return Long.compare(b.cpuTimeMicros, a.cpuTimeMicros);
+    }
+
+    private static int compareByBinderClassAndCode(
+            ExportedCallStat a, ExportedCallStat b) {
+        int result = a.className.compareTo(b.className);
+        return result != 0
+                ? result
+                : Integer.compare(a.transactionCode, b.transactionCode);
+    }
 }
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index 5bddd2f..0155067 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -17,6 +17,7 @@
 package com.android.internal.os;
 
 import android.annotation.NonNull;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.SystemClock;
@@ -33,7 +34,7 @@
 
 /**
  * Private and debugging Binder APIs.
- * 
+ *
  * @see IBinder
  */
 public class BinderInternal {
@@ -70,18 +71,62 @@
     }
 
     /**
+     * A session used by {@link Observer} in order to keep track of some data.
+     */
+    public static class CallSession {
+        // Binder interface descriptor.
+        public Class<? extends Binder> binderClass;
+        // Binder transaction code.
+        public int transactionCode;
+        // CPU time at the beginning of the call.
+        long cpuTimeStarted;
+        // System time at the beginning of the call.
+        long timeStarted;
+        // Should be set to one when an exception is thrown.
+        boolean exceptionThrown;
+    }
+
+    /**
+     * Allows to track various steps of an API call.
+     */
+    public interface Observer {
+        /**
+         * Called when a binder call starts.
+         *
+         * @return a CallSession to pass to the callEnded method.
+         */
+        CallSession callStarted(Binder binder, int code);
+
+        /**
+         * Called when a binder call stops.
+         *
+         * <li>This method will be called even when an exception is thrown.
+         */
+        void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize);
+
+        /**
+         * Called if an exception is thrown while executing the binder transaction.
+         *
+         * <li>BinderCallsStats#callEnded will be called afterwards.
+         * <li>Do not throw an exception in this method, it will swallow the original exception
+         * thrown by the binder transaction.
+         */
+        public void callThrewException(CallSession s, Exception exception);
+    }
+
+    /**
      * Add the calling thread to the IPC thread pool.  This function does
      * not return until the current process is exiting.
      */
     public static final native void joinThreadPool();
-    
+
     /**
      * Return the system time (as reported by {@link SystemClock#uptimeMillis
      * SystemClock.uptimeMillis()}) that the last garbage collection occurred
      * in this process.  This is not for general application use, and the
      * meaning of "when a garbage collection occurred" will change as the
      * garbage collector evolves.
-     * 
+     *
      * @return Returns the time as per {@link SystemClock#uptimeMillis
      * SystemClock.uptimeMillis()} of the last garbage collection.
      */
@@ -95,7 +140,7 @@
      * other services.
      */
     public static final native IBinder getContextObject();
-    
+
     /**
      * Special for system process to not allow incoming calls to run at
      * background scheduling priority.
@@ -104,14 +149,14 @@
     public static final native void disableBackgroundScheduling(boolean disable);
 
     public static final native void setMaxThreads(int numThreads);
-    
+
     static native final void handleGc();
-    
+
     public static void forceGc(String reason) {
         EventLog.writeEvent(2741, reason);
         VMRuntime.getRuntime().requestConcurrentGC();
     }
-    
+
     static void forceBinderGc() {
         forceGc("Binder");
     }
diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
index 4283917..ad62852 100644
--- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
@@ -53,8 +53,6 @@
     private int mReadErrorCounter;
     @GuardedBy("this")
     private boolean mSingleUidCpuTimesAvailable = true;
-    @GuardedBy("this")
-    private boolean mHasStaleData;
     // We use the freq count obtained from /proc/uid_time_in_state to decide how many longs
     // to read from each /proc/uid/<uid>/time_in_state. On the first read, verify if this is
     // correct and if not, set {@link #mSingleUidCpuTimesAvailable} to false. This flag will
@@ -196,18 +194,6 @@
         return deltaTimesMs;
     }
 
-    public void markDataAsStale(boolean hasStaleData) {
-        synchronized (this) {
-            mHasStaleData = hasStaleData;
-        }
-    }
-
-    public boolean hasStaleData() {
-        synchronized (this) {
-            return mHasStaleData;
-        }
-    }
-
     public void setAllUidsCpuTimesMs(SparseArray<long[]> allUidsCpuTimesMs) {
         synchronized (this) {
             mLastUidCpuTimeMs.clear();
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index a9cd5c8..e37e650 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -30,14 +30,13 @@
 import android.util.Slog;
 import com.android.internal.logging.AndroidConfig;
 import com.android.server.NetworkManagementSocketTagger;
+import dalvik.system.RuntimeHooks;
 import dalvik.system.VMRuntime;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.Objects;
-import java.util.TimeZone;
 import java.util.logging.LogManager;
-import org.apache.harmony.luni.internal.util.TimezoneGetter;
 
 /**
  * Main entry point for runtime initialization.  Not for
@@ -195,19 +194,13 @@
          * the default handler, but not the pre handler.
          */
         LoggingHandler loggingHandler = new LoggingHandler();
-        Thread.setUncaughtExceptionPreHandler(loggingHandler);
+        RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
         Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
 
         /*
-         * Install a TimezoneGetter subclass for ZoneInfo.db
+         * Install a time zone supplier that uses the Android persistent time zone system property.
          */
-        TimezoneGetter.setInstance(new TimezoneGetter() {
-            @Override
-            public String getId() {
-                return SystemProperties.get("persist.sys.timezone");
-            }
-        });
-        TimeZone.setDefault(null);
+        RuntimeHooks.setTimeZoneIdSupplier(() -> SystemProperties.get("persist.sys.timezone"));
 
         /*
          * Sets handler for java.util.logging to use Android log facilities.
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 4ee950a..413f89d 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -132,13 +132,14 @@
      */
     public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
           int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
-          int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {
+          int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
+          String packageName) {
         VM_HOOKS.preFork();
         // Resets nice priority for zygote process.
         resetNicePriority();
         int pid = nativeForkAndSpecialize(
                   uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
-                  fdsToIgnore, startChildZygote, instructionSet, appDataDir);
+                  fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName);
         // Enable tracing as soon as possible for the child process.
         if (pid == 0) {
             Trace.setTracingEnabled(true, runtimeFlags);
@@ -152,7 +153,8 @@
 
     native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
           int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
-          int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir);
+          int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
+          String packageName);
 
     /**
      * Called to do any initialization before starting an application.
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 12761b9..b9c717f 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -239,7 +239,7 @@
         pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                 parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                 parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
-                parsedArgs.instructionSet, parsedArgs.appDataDir);
+                parsedArgs.instructionSet, parsedArgs.appDataDir, parsedArgs.packageName);
 
         try {
             if (pid == 0) {
@@ -426,6 +426,9 @@
         /** from --invoke-with */
         String invokeWith;
 
+        /** from --package-name */
+        String packageName;
+
         /**
          * Any args after and including the first non-option arg
          * (or after a '--')
@@ -674,6 +677,8 @@
                                 "Invalid log sampling rate: " + rateStr, nfe);
                     }
                     expectRuntimeArgs = false;
+                } else if (arg.startsWith("--package-name=")) {
+                    packageName = arg.substring(arg.indexOf('=') + 1);
                 } else {
                     break;
                 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index da19560..8f87f91 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -21,9 +21,6 @@
 
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.icu.impl.CacheValue;
-import android.icu.text.DecimalFormatSymbols;
-import android.icu.util.ULocale;
 import android.opengl.EGL14;
 import android.os.Build;
 import android.os.Environment;
@@ -122,9 +119,9 @@
 
     static void preload(TimingsTraceLog bootTimingsTraceLog) {
         Log.d(TAG, "begin preload");
-        bootTimingsTraceLog.traceBegin("BeginIcuCachePinning");
-        beginIcuCachePinning();
-        bootTimingsTraceLog.traceEnd(); // BeginIcuCachePinning
+        bootTimingsTraceLog.traceBegin("BeginPreload");
+        beginPreload();
+        bootTimingsTraceLog.traceEnd(); // BeginPreload
         bootTimingsTraceLog.traceBegin("PreloadClasses");
         preloadClasses();
         bootTimingsTraceLog.traceEnd(); // PreloadClasses
@@ -142,7 +139,7 @@
         // Ask the WebViewFactory to do any initialization that must run in the zygote process,
         // for memory sharing purposes.
         WebViewFactory.prepareWebViewInZygote();
-        endIcuCachePinning();
+        endPreload();
         warmUpJcaProviders();
         Log.d(TAG, "end preload");
 
@@ -156,27 +153,16 @@
         preload(new TimingsTraceLog("ZygoteInitTiming_lazy", Trace.TRACE_TAG_DALVIK));
     }
 
-    private static void beginIcuCachePinning() {
-        // Pin ICU data in memory from this point that would normally be held by soft references.
-        // Without this, any references created immediately below or during class preloading
-        // would be collected when the Zygote GC runs in gcAndFinalize().
-        Log.i(TAG, "Installing ICU cache reference pinning...");
+    private static void beginPreload() {
+        Log.i(TAG, "Calling ZygoteHooks.beginPreload()");
 
-        CacheValue.setStrength(CacheValue.Strength.STRONG);
-
-        Log.i(TAG, "Preloading ICU data...");
-        // Explicitly exercise code to cache data apps are likely to need.
-        ULocale[] localesToPin = { ULocale.ROOT, ULocale.US, ULocale.getDefault() };
-        for (ULocale uLocale : localesToPin) {
-            new DecimalFormatSymbols(uLocale);
-        }
+        ZygoteHooks.onBeginPreload();
     }
 
-    private static void endIcuCachePinning() {
-        // All cache references created by ICU from this point will be soft.
-        CacheValue.setStrength(CacheValue.Strength.SOFT);
+    private static void endPreload() {
+        ZygoteHooks.onEndPreload();
 
-        Log.i(TAG, "Uninstalled ICU cache reference pinning...");
+        Log.i(TAG, "Called ZygoteHooks.endPreload()");
     }
 
     private static void preloadSharedLibraries() {
@@ -436,15 +422,8 @@
      * softly- and final-reachable objects, along with any other garbage.
      * This is only useful just before a fork().
      */
-    /*package*/ static void gcAndFinalize() {
-        final VMRuntime runtime = VMRuntime.getRuntime();
-
-        /* runFinalizationSync() lets finalizers be called in Zygote,
-         * which doesn't have a HeapWorker thread.
-         */
-        System.gc();
-        runtime.runFinalizationSync();
-        System.gc();
+    private static void gcAndFinalize() {
+        ZygoteHooks.gcAndFinalize();
     }
 
     /**
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 2790324..616520f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -140,14 +140,14 @@
 
     void showShutdownUi(boolean isReboot, String reason);
 
-    // Used to show the dialog when FingerprintService starts authentication
-    void showFingerprintDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
-    // Used to hide the dialog when a finger is authenticated
-    void onFingerprintAuthenticated();
+    // Used to show the dialog when BiometricService starts authentication
+    void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
+    // Used to hide the dialog when a biometric is authenticated
+    void onBiometricAuthenticated();
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
-    void onFingerprintHelp(String message);
+    void onBiometricHelp(String message);
     // Used to set a message - the dialog will dismiss after a certain amount of time
-    void onFingerprintError(String error);
-    // Used to hide the fingerprint dialog when the authenticationclient is stopped
-    void hideFingerprintDialog();
+    void onBiometricError(String error);
+    // Used to hide the biometric dialog when the AuthenticationClient is stopped
+    void hideBiometricDialog();
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 159d49b..a79e15a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -90,14 +90,14 @@
     void showPinningEnterExitToast(boolean entering);
     void showPinningEscapeToast();
 
-    // Used to show the dialog when FingerprintService starts authentication
-    void showFingerprintDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
-    // Used to hide the dialog when a finger is authenticated
-    void onFingerprintAuthenticated();
+    // Used to show the dialog when BiometricService starts authentication
+    void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
+    // Used to hide the dialog when a biometric is authenticated
+    void onBiometricAuthenticated();
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
-    void onFingerprintHelp(String message);
+    void onBiometricHelp(String message);
     // Used to set a message - the dialog will dismiss after a certain amount of time
-    void onFingerprintError(String error);
-    // Used to hide the fingerprint dialog when the authenticationclient is stopped
-    void hideFingerprintDialog();
+    void onBiometricError(String error);
+    // Used to hide the biometric dialog when the AuthenticationClient is stopped
+    void hideBiometricDialog();
 }
diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
index c5940ba..20f2aa0 100644
--- a/core/java/com/android/internal/textservice/ITextServicesManager.aidl
+++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
@@ -30,8 +30,7 @@
  */
 interface ITextServicesManager {
     SpellCheckerInfo getCurrentSpellChecker(String locale);
-    SpellCheckerSubtype getCurrentSpellCheckerSubtype(
-            String locale, boolean allowImplicitlySelectedSubtype);
+    SpellCheckerSubtype getCurrentSpellCheckerSubtype(boolean allowImplicitlySelectedSubtype);
     oneway void getSpellCheckerService(String sciId, in String locale,
             in ITextServicesSessionListener tsListener,
             in ISpellCheckerSessionListener scListener, in Bundle bundle);
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 621619c..4b66267 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -308,6 +308,23 @@
         return array;
     }
 
+    @SuppressWarnings("unchecked")
+    public static @NonNull <T> T[] concatElements(Class<T> kind, @Nullable T[] a, @Nullable T[] b) {
+        final int an = (a != null) ? a.length : 0;
+        final int bn = (b != null) ? b.length : 0;
+        if (an == 0 && bn == 0) {
+            if (kind == String.class) {
+                return (T[]) EmptyArray.STRING;
+            } else if (kind == Object.class) {
+                return (T[]) EmptyArray.OBJECT;
+            }
+        }
+        final T[] res = (T[]) Array.newInstance(kind, an + bn);
+        if (an > 0) System.arraycopy(a, 0, res, 0, an);
+        if (bn > 0) System.arraycopy(b, 0, res, an, bn);
+        return res;
+    }
+
     /**
      * Adds value to given array if not already present, providing set-like
      * behavior.
@@ -626,4 +643,17 @@
     public static @NonNull String[] defeatNullable(@Nullable String[] val) {
         return (val != null) ? val : EmptyArray.STRING;
     }
+
+    /**
+     * Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds.
+     *
+     * @param len length of the array. Must be non-negative
+     * @param index the index to check
+     * @throws ArrayIndexOutOfBoundsException if the {@code index} is out of bounds of the array
+     */
+    public static void checkBounds(int len, int index) {
+        if (index < 0 || len <= index) {
+            throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java
index 60dd86b..16ca4fc 100644
--- a/core/java/com/android/internal/util/ContrastColorUtil.java
+++ b/core/java/com/android/internal/util/ContrastColorUtil.java
@@ -454,9 +454,12 @@
     /**
      * Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT}
      */
-    public static int resolveColor(Context context, int color) {
+    public static int resolveColor(Context context, int color, boolean defaultBackgroundIsDark) {
         if (color == Notification.COLOR_DEFAULT) {
-            return context.getColor(com.android.internal.R.color.notification_default_color_light);
+            int res = defaultBackgroundIsDark
+                    ? com.android.internal.R.color.notification_default_color_dark
+                    : com.android.internal.R.color.notification_default_color_light;
+            return context.getColor(res);
         }
         return color;
     }
@@ -486,7 +489,7 @@
      */
     public static int resolveContrastColor(Context context, int notificationColor,
             int backgroundColor, boolean isDark) {
-        final int resolvedColor = resolveColor(context, notificationColor);
+        final int resolvedColor = resolveColor(context, notificationColor, isDark);
 
         int color = resolvedColor;
         color = ContrastColorUtil.ensureTextContrast(color, backgroundColor, isDark);
@@ -520,7 +523,8 @@
     }
 
     public static int resolveAmbientColor(Context context, int notificationColor) {
-        final int resolvedColor = resolveColor(context, notificationColor);
+        final int resolvedColor = resolveColor(context, notificationColor,
+                true /* defaultBackgroundIsDark */);
 
         int color = resolvedColor;
         color = ContrastColorUtil.ensureTextContrastOnBlack(color);
@@ -538,8 +542,9 @@
         return color;
     }
 
-    public static int resolvePrimaryColor(Context context, int backgroundColor) {
-        boolean useDark = shouldUseDark(backgroundColor);
+    public static int resolvePrimaryColor(Context context, int backgroundColor,
+                                          boolean defaultBackgroundIsDark) {
+        boolean useDark = shouldUseDark(backgroundColor, defaultBackgroundIsDark);
         if (useDark) {
             return context.getColor(
                     com.android.internal.R.color.notification_primary_text_color_light);
@@ -549,8 +554,9 @@
         }
     }
 
-    public static int resolveSecondaryColor(Context context, int backgroundColor) {
-        boolean useDark = shouldUseDark(backgroundColor);
+    public static int resolveSecondaryColor(Context context, int backgroundColor,
+                                            boolean defaultBackgroundIsDark) {
+        boolean useDark = shouldUseDark(backgroundColor, defaultBackgroundIsDark);
         if (useDark) {
             return context.getColor(
                     com.android.internal.R.color.notification_secondary_text_color_light);
@@ -560,8 +566,9 @@
         }
     }
 
-    public static int resolveDefaultColor(Context context, int backgroundColor) {
-        boolean useDark = shouldUseDark(backgroundColor);
+    public static int resolveDefaultColor(Context context, int backgroundColor,
+                                          boolean defaultBackgroundIsDark) {
+        boolean useDark = shouldUseDark(backgroundColor, defaultBackgroundIsDark);
         if (useDark) {
             return context.getColor(
                     com.android.internal.R.color.notification_default_color_light);
@@ -591,12 +598,11 @@
         return ColorUtilsFromCompat.LABToColor(result[0], result[1], result[2]);
     }
 
-    private static boolean shouldUseDark(int backgroundColor) {
-        boolean useDark = backgroundColor == Notification.COLOR_DEFAULT;
-        if (!useDark) {
-            useDark = ColorUtilsFromCompat.calculateLuminance(backgroundColor) > 0.5;
+    private static boolean shouldUseDark(int backgroundColor, boolean defaultBackgroundIsDark) {
+        if (backgroundColor == Notification.COLOR_DEFAULT) {
+            return !defaultBackgroundIsDark;
         }
-        return useDark;
+        return ColorUtilsFromCompat.calculateLuminance(backgroundColor) > 0.5;
     }
 
     public static double calculateLuminance(int backgroundColor) {
diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java
index 7fd83bc..f6d80a5 100644
--- a/core/java/com/android/internal/util/DumpUtils.java
+++ b/core/java/com/android/internal/util/DumpUtils.java
@@ -34,9 +34,18 @@
 /**
  * Helper functions for dumping the state of system services.
  * Test:
- atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
+ atest FrameworksCoreTests:DumpUtilsTest
  */
 public final class DumpUtils {
+
+    /**
+     * List of component names that should be dumped in the bug report critical section.
+     *
+     * @hide
+     */
+    public static final ComponentName[] CRITICAL_SECTION_COMPONENTS = {
+            new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService")
+    };
     private static final String TAG = "DumpUtils";
     private static final boolean DEBUG = false;
 
@@ -213,6 +222,45 @@
     }
 
     /**
+     * Return whether a package should be dumped in the critical section.
+     */
+    private static boolean isCriticalPackage(@Nullable ComponentName cname) {
+        if (cname == null) {
+            return false;
+        }
+
+        for (int i = 0; i < CRITICAL_SECTION_COMPONENTS.length; i++) {
+            if (cname.equals(CRITICAL_SECTION_COMPONENTS[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return whether a package name is considered to be part of the platform and in the critical
+     * section.
+     *
+     * @hide
+     */
+    public static boolean isPlatformCriticalPackage(@Nullable ComponentName.WithComponentName wcn) {
+        return (wcn != null) && isPlatformPackage(wcn.getComponentName()) &&
+                isCriticalPackage(wcn.getComponentName());
+    }
+
+    /**
+     * Return whether a package name is considered to be part of the platform but not in the the
+     * critical section.
+     *
+     * @hide
+     */
+    public static boolean isPlatformNonCriticalPackage(
+            @Nullable ComponentName.WithComponentName wcn) {
+        return (wcn != null) && isPlatformPackage(wcn.getComponentName()) &&
+                !isCriticalPackage(wcn.getComponentName());
+    }
+
+    /**
      * Used for dumping providers and services. Return a predicate for a given filter string.
      * @hide
      */
@@ -238,6 +286,16 @@
             return DumpUtils::isNonPlatformPackage;
         }
 
+        // Dump all platform-critical?
+        if ("all-platform-critical".equals(filterString)) {
+            return DumpUtils::isPlatformCriticalPackage;
+        }
+
+        // Dump all platform-non-critical?
+        if ("all-platform-non-critical".equals(filterString)) {
+            return DumpUtils::isPlatformNonCriticalPackage;
+        }
+
         // Is the filter a component name? If so, do an exact match.
         final ComponentName filterCname = ComponentName.unflattenFromString(filterString);
         if (filterCname != null) {
diff --git a/core/java/com/android/internal/util/HexDump.java b/core/java/com/android/internal/util/HexDump.java
index 7be95d8..af00400 100644
--- a/core/java/com/android/internal/util/HexDump.java
+++ b/core/java/com/android/internal/util/HexDump.java
@@ -16,18 +16,21 @@
 
 package com.android.internal.util;
 
+import android.annotation.Nullable;
+
 public class HexDump
 {
     private final static char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
     private final static char[] HEX_LOWER_CASE_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
 
-    public static String dumpHexString(byte[] array)
-    {
+    public static String dumpHexString(@Nullable byte[] array) {
+        if (array == null) return "(null)";
         return dumpHexString(array, 0, array.length);
     }
 
-    public static String dumpHexString(byte[] array, int offset, int length)
+    public static String dumpHexString(@Nullable byte[] array, int offset, int length)
     {
+        if (array == null) return "(null)";
         StringBuilder result = new StringBuilder();
 
         byte[] line = new byte[16];
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 6c3a58c..989c58b 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -79,6 +79,11 @@
      */
     public static final int ACTION_ROTATE_SCREEN = 6;
 
+    /*
+     * Time between we get a face acquired signal until we start with the unlock animation
+     */
+    public static final int ACTION_FACE_WAKE_AND_UNLOCK = 6;
+
     private static final String[] NAMES = new String[] {
             "expand panel",
             "toggle recents",
@@ -86,7 +91,8 @@
             "check credential",
             "check credential unlocked",
             "turn on screen",
-            "rotate the screen"};
+            "rotate the screen",
+            "face wake-and-unlock" };
 
     private static LatencyTracker sLatencyTracker;
 
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 9ed1ffb..f76eddf 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -74,8 +74,8 @@
     void hideMySoftInput(in IBinder token, int flags);
     void showMySoftInput(in IBinder token, int flags);
     void updateStatusIcon(in IBinder token, String packageName, int iconId);
-    void setImeWindowStatus(in IBinder token, in IBinder startInputToken, int vis,
-            int backDisposition);
+    void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
+    void reportStartInput(in IBinder token, in IBinder startInputToken);
     void registerSuggestionSpansForNotification(in SuggestionSpan[] spans);
     boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index);
     InputMethodSubtype getCurrentInputMethodSubtype();
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index e9472fa..13425e5 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -77,7 +77,11 @@
             final Point size = new Point();
             final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
             try {
-                wm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, size);
+                final Display display = context.getDisplay();
+                final int displayId = display != null
+                        ? display.getDisplayId()
+                        : Display.DEFAULT_DISPLAY;
+                wm.getInitialDisplaySize(displayId, size);
                 return size.x < size.y ?
                         Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
             } catch (RemoteException e) {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index d294933..80d8063 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1528,7 +1528,7 @@
      * @see StrongAuthTracker#isFingerprintAllowedForUser
      */
     public boolean isFingerprintAllowedForUser(int userId) {
-        return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_FINGERPRINT) == 0;
+        return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_BIOMETRIC) == 0;
     }
 
     public boolean isUserInLockdown(int userId) {
@@ -1733,11 +1733,10 @@
         public static final int STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN = 0x20;
 
         /**
-         * Strong auth flags that do not prevent fingerprint from being accepted as auth.
-         *
-         * If any other flags are set, fingerprint is disabled.
+         * Strong auth flags that do not prevent biometric methods from being accepted as auth.
+         * If any other flags are set, biometric authentication is disabled.
          */
-        private static final int ALLOWING_FINGERPRINT = STRONG_AUTH_NOT_REQUIRED
+        private static final int ALLOWING_BIOMETRIC = STRONG_AUTH_NOT_REQUIRED
                 | SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 
         private final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray();
@@ -1784,11 +1783,11 @@
         }
 
         /**
-         * @return true if unlocking with fingerprint alone is allowed for {@param userId} by the
-         * current strong authentication requirements.
+         * @return true if unlocking with a biometric method alone is allowed for {@param userId}
+         * by the current strong authentication requirements.
          */
-        public boolean isFingerprintAllowedForUser(int userId) {
-            return (getStrongAuthForUser(userId) & ~ALLOWING_FINGERPRINT) == 0;
+        public boolean isBiometricAllowedForUser(int userId) {
+            return (getStrongAuthForUser(userId) & ~ALLOWING_BIOMETRIC) == 0;
         }
 
         /**
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 514874b..0f2e9c5 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -60,6 +60,13 @@
 public class MessagingLayout extends FrameLayout {
 
     private static final float COLOR_SHIFT_AMOUNT = 60;
+    /**
+     *  Pattren for filter some ingonable characters.
+     *  p{Z} for any kind of whitespace or invisible separator.
+     *  p{C} for any kind of punctuation character.
+     */
+    private static final Pattern IGNORABLE_CHAR_PATTERN
+            = Pattern.compile("[\\p{C}\\p{Z}]");
     private static final Pattern SPECIAL_CHAR_PATTERN
             = Pattern.compile ("[!@#$%&*()_+=|<>?{}\\[\\]~-]");
     private static final Consumer<MessagingMessage> REMOVE_MESSAGE
@@ -233,7 +240,10 @@
                 continue;
             }
             if (!uniqueNames.containsKey(senderName)) {
-                char c = senderName.charAt(0);
+                // Only use visible characters to get uniqueNames
+                String pureSenderName = IGNORABLE_CHAR_PATTERN
+                        .matcher(senderName).replaceAll("" /* replacement */);
+                char c = pureSenderName.charAt(0);
                 if (uniqueCharacters.containsKey(c)) {
                     // this character was already used, lets make it more unique. We first need to
                     // resolve the existing character if it exists
@@ -245,7 +255,7 @@
                     uniqueNames.put(senderName, findNameSplit((String) senderName));
                 } else {
                     uniqueNames.put(senderName, Character.toString(c));
-                    uniqueCharacters.put(c, senderName);
+                    uniqueCharacters.put(c, pureSenderName);
                 }
             }
         }
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 95534e2..621d5a6 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -258,13 +258,13 @@
 
         // Start watching for new tombstone files; will record them as they occur.
         // This gets registered with the singleton file observer thread.
-        sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) {
+        sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CREATE) {
             @Override
             public void onEvent(int event, String path) {
                 HashMap<String, Long> timestamps = readTimestamps();
                 try {
                     File file = new File(TOMBSTONE_DIR, path);
-                    if (file.isFile()) {
+                    if (file.isFile() && file.getName().startsWith("tombstone_")) {
                         addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE,
                                 TAG_TOMBSTONE);
                     }
diff --git a/core/java/com/android/server/net/OWNERS b/core/java/com/android/server/net/OWNERS
index ce50558..7311eee 100644
--- a/core/java/com/android/server/net/OWNERS
+++ b/core/java/com/android/server/net/OWNERS
@@ -1,6 +1,8 @@
 set noparent
 
+codewiz@google.com
 ek@google.com
 jchalard@google.com
 lorenzo@google.com
+reminv@google.com
 satk@google.com
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index d4903d8..6316da5 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -84,8 +84,8 @@
         "android_view_VelocityTracker.cpp",
         "android_text_AndroidCharacter.cpp",
         "android_text_Hyphenator.cpp",
+        "android_text_LineBreaker.cpp",
         "android_text_MeasuredParagraph.cpp",
-        "android_text_StaticLayout.cpp",
         "android_os_Debug.cpp",
         "android_os_GraphicsEnvironment.cpp",
         "android_os_HidlSupport.cpp",
@@ -126,7 +126,6 @@
         "android/graphics/Camera.cpp",
         "android/graphics/CanvasProperty.cpp",
         "android/graphics/ColorFilter.cpp",
-        "android/graphics/DrawFilter.cpp",
         "android/graphics/FontFamily.cpp",
         "android/graphics/FontUtils.cpp",
         "android/graphics/CreateJavaOutputStreamAdaptor.cpp",
@@ -143,6 +142,7 @@
         "android/graphics/NinePatch.cpp",
         "android/graphics/NinePatchPeeker.cpp",
         "android/graphics/Paint.cpp",
+        "android/graphics/PaintFilter.cpp",
         "android/graphics/Path.cpp",
         "android/graphics/PathMeasure.cpp",
         "android/graphics/PathEffect.cpp",
@@ -154,6 +154,8 @@
         "android/graphics/Typeface.cpp",
         "android/graphics/Utils.cpp",
         "android/graphics/YuvToJpegEncoder.cpp",
+        "android/graphics/fonts/Font.cpp",
+        "android/graphics/fonts/FontFamily.cpp",
         "android/graphics/pdf/PdfDocument.cpp",
         "android/graphics/pdf/PdfEditor.cpp",
         "android/graphics/pdf/PdfRenderer.cpp",
@@ -220,7 +222,6 @@
         "external/skia/src/image",
         "external/skia/src/images",
         "frameworks/base/media/jni",
-        "libcore/include",
         "system/media/camera/include",
         "system/media/private/camera/include",
     ],
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index bd2a8de..7fe095b 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -140,6 +140,8 @@
 extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
 extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
 extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
+extern int register_android_graphics_fonts_Font(JNIEnv* env);
+extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
@@ -183,7 +185,7 @@
 extern int register_android_text_AndroidCharacter(JNIEnv *env);
 extern int register_android_text_Hyphenator(JNIEnv *env);
 extern int register_android_text_MeasuredParagraph(JNIEnv* env);
-extern int register_android_text_StaticLayout(JNIEnv *env);
+extern int register_android_text_LineBreaker(JNIEnv *env);
 extern int register_android_opengl_classes(JNIEnv *env);
 extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
 extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
@@ -1334,7 +1336,7 @@
     REG_JNI(register_android_text_AndroidCharacter),
     REG_JNI(register_android_text_Hyphenator),
     REG_JNI(register_android_text_MeasuredParagraph),
-    REG_JNI(register_android_text_StaticLayout),
+    REG_JNI(register_android_text_LineBreaker),
     REG_JNI(register_android_view_InputDevice),
     REG_JNI(register_android_view_KeyCharacterMap),
     REG_JNI(register_android_os_Process),
@@ -1406,6 +1408,8 @@
     REG_JNI(register_android_graphics_YuvImage),
     REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
     REG_JNI(register_android_graphics_drawable_VectorDrawable),
+    REG_JNI(register_android_graphics_fonts_Font),
+    REG_JNI(register_android_graphics_fonts_FontFamily),
     REG_JNI(register_android_graphics_pdf_PdfDocument),
     REG_JNI(register_android_graphics_pdf_PdfEditor),
     REG_JNI(register_android_graphics_pdf_PdfRenderer),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index e5aea97..02076bd 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -437,6 +437,10 @@
     return NULL;
 }
 
+static bool IsColorSpaceSRGB(SkColorSpace* colorSpace) {
+    return colorSpace == nullptr || colorSpace->isSRGB();
+}
+
 bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,
         int x, int y, int width, int height, const SkBitmap& dstBitmap) {
     void* dst = dstBitmap.getPixels();
@@ -453,8 +457,7 @@
     dst = dstBitmap.getAddr(x, y);
 
     SkColorSpace* colorSpace = dstBitmap.colorSpace();
-    if (dstBitmap.colorType() == kRGBA_F16_SkColorType ||
-            GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+    if (dstBitmap.colorType() == kRGBA_F16_SkColorType || IsColorSpaceSRGB(colorSpace)) {
         // now copy/convert each scanline
         for (int y = 0; y < height; y++) {
             proc(dst, src, width, x, y);
@@ -673,8 +676,8 @@
     SkBitmap bitmap;
     sk_sp<SkColorSpace> colorSpace;
 
-    if (colorType != kN32_SkColorType || xyzD50 == nullptr || transferParameters == nullptr) {
-        colorSpace = GraphicsJNI::colorSpaceForType(colorType);
+    if (xyzD50 == nullptr || transferParameters == nullptr) {
+        colorSpace = SkColorSpace::MakeSRGB();
     } else {
         SkColorSpaceTransferFn p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
         SkMatrix44 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
@@ -1268,7 +1271,7 @@
     if (!bitmapHolder.valid()) return JNI_TRUE;
 
     SkColorSpace* colorSpace = bitmapHolder->info().colorSpace();
-    return GraphicsJNI::isColorSpaceSRGB(colorSpace);
+    return IsColorSpaceSRGB(colorSpace);
 }
 
 static jboolean Bitmap_isSRGBLinear(JNIEnv* env, jobject, jlong bitmapHandle) {
@@ -1340,8 +1343,7 @@
     proc(dst, src, 1);
 
     SkColorSpace* colorSpace = bitmap.colorSpace();
-    if (bitmap.colorType() != kRGBA_F16_SkColorType &&
-            !GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+    if (bitmap.colorType() != kRGBA_F16_SkColorType &&  !IsColorSpaceSRGB(colorSpace)) {
         auto sRGB = SkColorSpace::MakeSRGB();
         auto xform = SkColorSpaceXform::New(colorSpace, sRGB.get());
         xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &dst[0],
@@ -1371,8 +1373,7 @@
     SkColor* d = (SkColor*)dst + offset;
 
     SkColorSpace* colorSpace = bitmap.colorSpace();
-    if (bitmap.colorType() == kRGBA_F16_SkColorType ||
-            GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+    if (bitmap.colorType() == kRGBA_F16_SkColorType || IsColorSpaceSRGB(colorSpace)) {
         while (--height >= 0) {
             proc(d, src, width);
             d += stride;
@@ -1414,8 +1415,7 @@
     }
 
     SkColorSpace* colorSpace = bitmap.colorSpace();
-    if (bitmap.colorType() != kRGBA_F16_SkColorType &&
-            !GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+    if (bitmap.colorType() != kRGBA_F16_SkColorType && !IsColorSpaceSRGB(colorSpace)) {
         auto sRGB = SkColorSpace::MakeSRGB();
         auto xform = SkColorSpaceXform::New(sRGB.get(), colorSpace);
         xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &color,
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 685fcaf..9ae05f4 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -360,7 +360,7 @@
     // use the default.
     SkImageInfo bitmapInfo = decodeInfo;
     if (decodeInfo.colorSpace() && decodeInfo.colorSpace()->isSRGB()) {
-        bitmapInfo = bitmapInfo.makeColorSpace(GraphicsJNI::colorSpaceForType(decodeColorType));
+        bitmapInfo = bitmapInfo.makeColorSpace(decodeInfo.refColorSpace());
     }
 
     if (decodeColorType == kGray_8_SkColorType) {
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index 76d6851..da95497 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -96,10 +96,12 @@
 }
 
 static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) {
-    SkCanvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle)->asSkCanvas();
+    android::Canvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle);
     jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
     Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
-    v->applyToCanvas(canvas);
+    SkMatrix matrix;
+    v->getMatrix(&matrix);
+    canvas->concat(matrix);
 }
 
 static jfloat Camera_dotWithNormal(JNIEnv* env, jobject obj,
diff --git a/core/jni/android/graphics/DrawFilter.cpp b/core/jni/android/graphics/DrawFilter.cpp
deleted file mode 100644
index c1dc0dd..0000000
--- a/core/jni/android/graphics/DrawFilter.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/* libs/android_runtime/android/graphics/ColorFilter.cpp
-**
-** Copyright 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.
-*/
-
-// This file was generated from the C++ include file: SkColorFilter.h
-// Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, 
-// or one of the auxilary file specifications in device/tools/gluemaker.
-
-#include "jni.h"
-#include "GraphicsJNI.h"
-#include <android_runtime/AndroidRuntime.h>
-
-#include "core_jni_helpers.h"
-
-#include "SkDrawFilter.h"
-#include "SkPaintFlagsDrawFilter.h"
-#include "SkPaint.h"
-
-namespace android {
-
-// Custom version of SkPaintFlagsDrawFilter that also calls setFilterQuality.
-class CompatFlagsDrawFilter : public SkPaintFlagsDrawFilter {
-public:
-    CompatFlagsDrawFilter(uint32_t clearFlags, uint32_t setFlags,
-            SkFilterQuality desiredQuality)
-    : SkPaintFlagsDrawFilter(clearFlags, setFlags)
-    , fDesiredQuality(desiredQuality) {
-    }
-
-    virtual bool filter(SkPaint* paint, Type type) {
-        SkPaintFlagsDrawFilter::filter(paint, type);
-        paint->setFilterQuality(fDesiredQuality);
-        return true;
-    }
-
-private:
-    const SkFilterQuality fDesiredQuality;
-};
-
-// Returns whether flags contains FILTER_BITMAP_FLAG. If flags does, remove it.
-static inline bool hadFiltering(jint& flags) {
-    // Equivalent to the Java Paint's FILTER_BITMAP_FLAG.
-    static const uint32_t sFilterBitmapFlag = 0x02;
-
-    const bool result = (flags & sFilterBitmapFlag) != 0;
-    flags &= ~sFilterBitmapFlag;
-    return result;
-}
-
-class SkDrawFilterGlue {
-public:
-
-    static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
-        SkDrawFilter* obj = reinterpret_cast<SkDrawFilter*>(objHandle);
-        SkSafeUnref(obj);
-    }
-
-    static jlong CreatePaintFlagsDF(JNIEnv* env, jobject clazz,
-                                    jint clearFlags, jint setFlags) {
-        if (clearFlags | setFlags) {
-            // Mask both groups of flags to remove FILTER_BITMAP_FLAG, which no
-            // longer has a Skia equivalent flag (instead it corresponds to
-            // calling setFilterQuality), and keep track of which group(s), if
-            // any, had the flag set.
-            const bool turnFilteringOn = hadFiltering(setFlags);
-            const bool turnFilteringOff = hadFiltering(clearFlags);
-
-            SkDrawFilter* filter;
-            if (turnFilteringOn) {
-                // Turning filtering on overrides turning it off.
-                filter = new CompatFlagsDrawFilter(clearFlags, setFlags,
-                        kLow_SkFilterQuality);
-            } else if (turnFilteringOff) {
-                filter = new CompatFlagsDrawFilter(clearFlags, setFlags,
-                        kNone_SkFilterQuality);
-            } else {
-                filter = new SkPaintFlagsDrawFilter(clearFlags, setFlags);
-            }
-            return reinterpret_cast<jlong>(filter);
-        } else {
-            return NULL;
-        }
-    }
-};
-
-static const JNINativeMethod drawfilter_methods[] = {
-    {"nativeDestructor", "(J)V", (void*) SkDrawFilterGlue::finalizer}
-};
-
-static const JNINativeMethod paintflags_methods[] = {
-    {"nativeConstructor","(II)J", (void*) SkDrawFilterGlue::CreatePaintFlagsDF}
-};
-
-int register_android_graphics_DrawFilter(JNIEnv* env) {
-    int result = RegisterMethodsOrDie(env, "android/graphics/DrawFilter", drawfilter_methods,
-                                      NELEM(drawfilter_methods));
-    result |= RegisterMethodsOrDie(env, "android/graphics/PaintFlagsDrawFilter", paintflags_methods,
-                                   NELEM(paintflags_methods));
-    
-    return 0;
-}
-
-}
diff --git a/core/jni/android/graphics/FontUtils.h b/core/jni/android/graphics/FontUtils.h
index 9eaaa49..9f6462e 100644
--- a/core/jni/android/graphics/FontUtils.h
+++ b/core/jni/android/graphics/FontUtils.h
@@ -20,6 +20,8 @@
 #include <jni.h>
 #include <memory>
 
+#include <minikin/Font.h>
+
 namespace minikin {
 class FontFamily;
 }  // namespace minikin
@@ -31,6 +33,11 @@
   std::shared_ptr<minikin::FontFamily> family;
 };
 
+struct FontWrapper {
+  FontWrapper(minikin::Font&& font) : font(std::move(font)) {}
+  minikin::Font font;
+};
+
 // Utility wrapper for java.util.List
 class ListHelper {
 public:
diff --git a/core/jni/android/graphics/GraphicBuffer.cpp b/core/jni/android/graphics/GraphicBuffer.cpp
index ae6fd38..344e22c 100644
--- a/core/jni/android/graphics/GraphicBuffer.cpp
+++ b/core/jni/android/graphics/GraphicBuffer.cpp
@@ -196,8 +196,7 @@
     SkBitmap bitmap;
     bitmap.setInfo(SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
                                      convertPixelFormat(buffer->getPixelFormat()),
-                                     kPremul_SkAlphaType,
-                                     GraphicsJNI::defaultColorSpace()),
+                                     kPremul_SkAlphaType),
                    bytesCount);
 
     if (buffer->getWidth() > 0 && buffer->getHeight() > 0) {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 5d65aee..26af15e 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -454,31 +454,6 @@
     return wrapper;
 }
 
-sk_sp<SkColorSpace> GraphicsJNI::defaultColorSpace() {
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
-    return SkColorSpace::MakeSRGB();
-#else
-    return nullptr;
-#endif
-}
-
-sk_sp<SkColorSpace> GraphicsJNI::linearColorSpace() {
-    return SkColorSpace::MakeSRGBLinear();
-}
-
-sk_sp<SkColorSpace> GraphicsJNI::colorSpaceForType(SkColorType type) {
-    switch (type) {
-        case kRGBA_F16_SkColorType:
-            return linearColorSpace();
-        default:
-            return defaultColorSpace();
-    }
-}
-
-bool GraphicsJNI::isColorSpaceSRGB(SkColorSpace* colorSpace) {
-    return colorSpace == nullptr || colorSpace->isSRGB();
-}
-
 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 7825f1d..9d85cc2 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -104,11 +104,6 @@
             int srcStride, int x, int y, int width, int height,
             const SkBitmap& dstBitmap);
 
-    static sk_sp<SkColorSpace> defaultColorSpace();
-    static sk_sp<SkColorSpace> linearColorSpace();
-    static sk_sp<SkColorSpace> colorSpaceForType(SkColorType type);
-    static bool isColorSpaceSRGB(SkColorSpace* colorSpace);
-
     static SkColorSpaceTransferFn getNativeTransferParameters(JNIEnv* env, jobject transferParams);
     static SkMatrix44 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50);
     static sk_sp<SkColorSpace> getNativeColorSpace(JNIEnv* env, jobject colorSpace);
diff --git a/core/jni/android/graphics/PaintFilter.cpp b/core/jni/android/graphics/PaintFilter.cpp
new file mode 100644
index 0000000..182b22b
--- /dev/null
+++ b/core/jni/android/graphics/PaintFilter.cpp
@@ -0,0 +1,124 @@
+/* libs/android_runtime/android/graphics/ColorFilter.cpp
+**
+** Copyright 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.
+*/
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "core_jni_helpers.h"
+
+#include "hwui/PaintFilter.h"
+#include "SkPaint.h"
+
+namespace android {
+
+class PaintFlagsFilter : public PaintFilter {
+public:
+    PaintFlagsFilter(uint32_t clearFlags, uint32_t setFlags) {
+        fClearFlags = static_cast<uint16_t>(clearFlags & SkPaint::kAllFlags);
+        fSetFlags = static_cast<uint16_t>(setFlags & SkPaint::kAllFlags);
+    }
+    void filter(SkPaint* paint) override {
+        paint->setFlags((paint->getFlags() & ~fClearFlags) | fSetFlags);
+    }
+
+private:
+    uint16_t fClearFlags;
+    uint16_t fSetFlags;
+};
+
+// Custom version of PaintFlagsDrawFilter that also calls setFilterQuality.
+class CompatPaintFlagsFilter : public PaintFlagsFilter {
+public:
+    CompatPaintFlagsFilter(uint32_t clearFlags, uint32_t setFlags, SkFilterQuality desiredQuality)
+    : PaintFlagsFilter(clearFlags, setFlags)
+    , fDesiredQuality(desiredQuality) {
+    }
+
+    virtual void filter(SkPaint* paint) {
+        PaintFlagsFilter::filter(paint);
+        paint->setFilterQuality(fDesiredQuality);
+    }
+
+private:
+    const SkFilterQuality fDesiredQuality;
+};
+
+// Returns whether flags contains FILTER_BITMAP_FLAG. If flags does, remove it.
+static inline bool hadFiltering(jint& flags) {
+    // Equivalent to the Java Paint's FILTER_BITMAP_FLAG.
+    static const uint32_t sFilterBitmapFlag = 0x02;
+
+    const bool result = (flags & sFilterBitmapFlag) != 0;
+    flags &= ~sFilterBitmapFlag;
+    return result;
+}
+
+class PaintFilterGlue {
+public:
+
+    static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
+        PaintFilter* obj = reinterpret_cast<PaintFilter*>(objHandle);
+        SkSafeUnref(obj);
+    }
+
+    static jlong CreatePaintFlagsFilter(JNIEnv* env, jobject clazz,
+                                        jint clearFlags, jint setFlags) {
+        if (clearFlags | setFlags) {
+            // Mask both groups of flags to remove FILTER_BITMAP_FLAG, which no
+            // longer has a Skia equivalent flag (instead it corresponds to
+            // calling setFilterQuality), and keep track of which group(s), if
+            // any, had the flag set.
+            const bool turnFilteringOn = hadFiltering(setFlags);
+            const bool turnFilteringOff = hadFiltering(clearFlags);
+
+            PaintFilter* filter;
+            if (turnFilteringOn) {
+                // Turning filtering on overrides turning it off.
+                filter = new CompatPaintFlagsFilter(clearFlags, setFlags,
+                        kLow_SkFilterQuality);
+            } else if (turnFilteringOff) {
+                filter = new CompatPaintFlagsFilter(clearFlags, setFlags,
+                        kNone_SkFilterQuality);
+            } else {
+                filter = new PaintFlagsFilter(clearFlags, setFlags);
+            }
+            return reinterpret_cast<jlong>(filter);
+        } else {
+            return NULL;
+        }
+    }
+};
+
+static const JNINativeMethod drawfilter_methods[] = {
+    {"nativeDestructor", "(J)V", (void*) PaintFilterGlue::finalizer}
+};
+
+static const JNINativeMethod paintflags_methods[] = {
+    {"nativeConstructor","(II)J", (void*) PaintFilterGlue::CreatePaintFlagsFilter}
+};
+
+int register_android_graphics_DrawFilter(JNIEnv* env) {
+    int result = RegisterMethodsOrDie(env, "android/graphics/DrawFilter", drawfilter_methods,
+                                      NELEM(drawfilter_methods));
+    result |= RegisterMethodsOrDie(env, "android/graphics/PaintFlagsDrawFilter", paintflags_methods,
+                                   NELEM(paintflags_methods));
+
+    return 0;
+}
+
+}
diff --git a/core/jni/android/graphics/fonts/Font.cpp b/core/jni/android/graphics/fonts/Font.cpp
new file mode 100644
index 0000000..2d1d7a0
--- /dev/null
+++ b/core/jni/android/graphics/fonts/Font.cpp
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Minikin"
+
+#include <nativehelper/JNIHelp.h>
+#include <core_jni_helpers.h>
+
+#include "SkData.h"
+#include "SkFontMgr.h"
+#include "SkRefCnt.h"
+#include "SkTypeface.h"
+#include "GraphicsJNI.h"
+#include <nativehelper/ScopedUtfChars.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_util_AssetManager.h>
+#include <androidfw/AssetManager2.h>
+#include "Utils.h"
+#include "FontUtils.h"
+
+#include <hwui/MinikinSkia.h>
+#include <hwui/Typeface.h>
+#include <utils/FatVector.h>
+#include <minikin/FontFamily.h>
+
+#include <memory>
+
+namespace android {
+
+struct NativeFontBuilder {
+    std::vector<minikin::FontVariation> axes;
+};
+
+static inline NativeFontBuilder* toBuilder(jlong ptr) {
+    return reinterpret_cast<NativeFontBuilder*>(ptr);
+}
+
+static inline Asset* toAsset(jlong ptr) {
+    return reinterpret_cast<Asset*>(ptr);
+}
+
+static void releaseAsset(jlong asset) {
+    delete toAsset(asset);
+}
+
+static void releaseFont(jlong font) {
+    delete reinterpret_cast<FontWrapper*>(font);
+}
+
+static void release_global_ref(const void* /*data*/, void* context) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    if (env == nullptr) {
+        JavaVMAttachArgs args;
+        args.version = JNI_VERSION_1_4;
+        args.name = "release_font_data";
+        args.group = nullptr;
+        jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args);
+        if (result != JNI_OK) {
+            ALOGE("failed to attach to thread to release global ref.");
+            return;
+        }
+    }
+
+    jobject obj = reinterpret_cast<jobject>(context);
+    env->DeleteGlobalRef(obj);
+}
+
+// Regular JNI
+static jlong Font_Builder_getNativeAsset(
+    JNIEnv* env, jobject clazz, jobject assetMgr, jstring path, jboolean isAsset, jint cookie) {
+    NPE_CHECK_RETURN_ZERO(env, assetMgr);
+    NPE_CHECK_RETURN_ZERO(env, path);
+
+    Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, assetMgr);
+    if (mgr == nullptr) {
+        return 0;
+    }
+
+    ScopedUtfChars str(env, path);
+    if (str.c_str() == nullptr) {
+        return 0;
+    }
+
+    std::unique_ptr<Asset> asset;
+    {
+      ScopedLock<AssetManager2> locked_mgr(*mgr);
+      if (isAsset) {
+          asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
+      } else if (cookie > 0) {
+          // Valid java cookies are 1-based, but AssetManager cookies are 0-based.
+          asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1),
+                  Asset::ACCESS_BUFFER);
+      } else {
+          asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
+      }
+    }
+
+    return reinterpret_cast<jlong>(asset.release());
+}
+
+// Regular JNI
+static jobject Font_Builder_getAssetBuffer(JNIEnv* env, jobject clazz, jlong nativeAsset) {
+    Asset* asset = toAsset(nativeAsset);
+    return env->NewDirectByteBuffer(const_cast<void*>(asset->getBuffer(false)), asset->getLength());
+}
+
+// CriticalNative
+static jlong Font_Builder_getReleaseNativeAssetFunc() {
+    return reinterpret_cast<jlong>(&releaseAsset);
+}
+
+// Regular JNI
+static jlong Font_Builder_initBuilder(JNIEnv*, jobject) {
+    return reinterpret_cast<jlong>(new NativeFontBuilder());
+}
+
+// Critical Native
+static void Font_Builder_addAxis(jlong builderPtr, jint tag, jfloat value) {
+    toBuilder(builderPtr)->axes.emplace_back(static_cast<minikin::AxisTag>(tag), value);
+}
+
+// Regular JNI
+static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jobject buffer,
+        jint weight, jboolean italic, jint ttcIndex) {
+    NPE_CHECK_RETURN_ZERO(env, buffer);
+    std::unique_ptr<NativeFontBuilder> builder(toBuilder(builderPtr));
+    const void* fontPtr = env->GetDirectBufferAddress(buffer);
+    if (fontPtr == nullptr) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "Not a direct buffer");
+        return 0;
+    }
+    jlong fontSize = env->GetDirectBufferCapacity(buffer);
+    if (fontSize <= 0) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "buffer size must not be zero or negative");
+        return 0;
+    }
+    jobject fontRef = MakeGlobalRefOrDie(env, buffer);
+    sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
+            release_global_ref, reinterpret_cast<void*>(fontRef)));
+
+    uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
+    for (const auto& axis : builder->axes) {
+        skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
+    }
+
+    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));
+
+    SkFontArguments params;
+    params.setCollectionIndex(ttcIndex);
+    params.setAxes(skiaAxes.data(), skiaAxes.size());
+
+    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+    sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params));
+    if (face == nullptr) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "Failed to create internal object. maybe invalid font data.");
+        return 0;
+    }
+    std::shared_ptr<minikin::MinikinFont> minikinFont =
+            std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex,
+                    builder->axes);
+    minikin::Font font = minikin::Font::Builder(minikinFont).setWeight(weight)
+                    .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build();
+    return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
+}
+
+// Critical Native
+static jlong Font_Builder_getReleaseNativeFont() {
+    return reinterpret_cast<jlong>(releaseFont);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gFontBuilderMethods[] = {
+    { "nInitBuilder", "()J", (void*) Font_Builder_initBuilder },
+    { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
+    { "nBuild", "(JLjava/nio/ByteBuffer;IZI)J", (void*) Font_Builder_build },
+    { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont },
+
+    { "nGetNativeAsset", "(Landroid/content/res/AssetManager;Ljava/lang/String;ZI)J",
+      (void*) Font_Builder_getNativeAsset },
+    { "nGetAssetBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) Font_Builder_getAssetBuffer },
+    { "nGetReleaseNativeAssetFunc", "()J", (void*) Font_Builder_getReleaseNativeAssetFunc },
+};
+
+int register_android_graphics_fonts_Font(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods,
+            NELEM(gFontBuilderMethods));
+}
+
+}
diff --git a/core/jni/android/graphics/fonts/FontFamily.cpp b/core/jni/android/graphics/fonts/FontFamily.cpp
new file mode 100644
index 0000000..4597386
--- /dev/null
+++ b/core/jni/android/graphics/fonts/FontFamily.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Minikin"
+
+#include <nativehelper/JNIHelp.h>
+#include <core_jni_helpers.h>
+
+#include "FontUtils.h"
+
+#include <minikin/FontFamily.h>
+
+#include <memory>
+
+namespace android {
+
+struct NativeFamilyBuilder {
+    std::vector<minikin::Font> fonts;
+};
+
+static inline NativeFamilyBuilder* toBuilder(jlong ptr) {
+    return reinterpret_cast<NativeFamilyBuilder*>(ptr);
+}
+
+static inline FontWrapper* toFontWrapper(jlong ptr) {
+    return reinterpret_cast<FontWrapper*>(ptr);
+}
+
+static void releaseFontFamily(jlong family) {
+    delete reinterpret_cast<FontFamilyWrapper*>(family);
+}
+
+// Regular JNI
+static jlong FontFamily_Builder_initBuilder(JNIEnv*, jobject) {
+    return reinterpret_cast<jlong>(new NativeFamilyBuilder());
+}
+
+// Critical Native
+static void FontFamily_Builder_addFont(jlong builderPtr, jlong fontPtr) {
+    toBuilder(builderPtr)->fonts.push_back(toFontWrapper(fontPtr)->font);
+}
+
+// Regular JNI
+static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr) {
+    std::unique_ptr<NativeFamilyBuilder> builder(toBuilder(builderPtr));
+    std::shared_ptr<minikin::FontFamily> family =
+            std::make_shared<minikin::FontFamily>(std::move(builder->fonts));
+    if (family->getCoverage().length() == 0) {
+        // No coverage means minikin rejected given font for some reasons.
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "Failed to create internal object. maybe invalid font data");
+        return 0;
+    }
+    return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
+}
+
+// CriticalNative
+static jlong FontFamily_Builder_GetReleaseFunc() {
+    return reinterpret_cast<jlong>(releaseFontFamily);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gFontFamilyBuilderMethods[] = {
+    { "nInitBuilder", "()J", (void*) FontFamily_Builder_initBuilder },
+    { "nAddFont", "(JJ)V", (void*) FontFamily_Builder_addFont },
+    { "nBuild", "(J)J", (void*) FontFamily_Builder_build },
+
+    { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
+};
+
+int register_android_graphics_fonts_FontFamily(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily$Builder",
+            gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods));
+}
+
+}
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 484b33f..db3bfe6 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -22,13 +22,13 @@
 #include <androidfw/ResourceTypes.h>
 #include <hwui/Canvas.h>
 #include <hwui/Paint.h>
+#include <hwui/PaintFilter.h>
 #include <hwui/Typeface.h>
 #include <minikin/Layout.h>
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/ScopedStringChars.h>
 
 #include "Bitmap.h"
-#include "SkDrawFilter.h"
 #include "SkGraphics.h"
 #include "SkRegion.h"
 #include "SkVertices.h"
@@ -320,9 +320,12 @@
                          jintArray jcolors, jint colorIndex,
                          jshortArray jindices, jint indexIndex,
                          jint indexCount, jlong paintHandle) {
+
+    const int vertexCount = floatCount >> 1;  // 2 floats per SkPoint
+
     AutoJavaFloatArray  vertA(env, jverts, vertIndex + floatCount);
     AutoJavaFloatArray  texA(env, jtexs, texIndex + floatCount);
-    AutoJavaIntArray    colorA(env, jcolors, colorIndex + floatCount);
+    AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
     AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
 
     const float* verts = vertA.ptr() + vertIndex;
@@ -337,7 +340,6 @@
         indices = (const uint16_t*)(indexA.ptr() + indexIndex);
     }
 
-    int vertexCount = floatCount >> 1;  // 2 floats per SkPoint
     SkVertices::VertexMode mode = static_cast<SkVertices::VertexMode>(modeHandle);
     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
     get_canvas(canvasHandle)->drawVertices(SkVertices::MakeCopy(mode, vertexCount,
@@ -582,8 +584,9 @@
     env->ReleaseStringChars(text, jchars);
 }
 
-static void setDrawFilter(jlong canvasHandle, jlong filterHandle) {
-    get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
+static void setPaintFilter(jlong canvasHandle, jlong filterHandle) {
+    PaintFilter* paintFilter = reinterpret_cast<PaintFilter*>(filterHandle);
+    get_canvas(canvasHandle)->setPaintFilter(sk_ref_sp(paintFilter));
 }
 
 static void freeCaches(JNIEnv* env, jobject) {
@@ -633,7 +636,7 @@
     {"nQuickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
     {"nClipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
     {"nClipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
-    {"nSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter},
+    {"nSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setPaintFilter},
 };
 
 // If called from Canvas these are regular JNI
diff --git a/core/jni/android_media_MediaMetricsJNI.h b/core/jni/android_media_MediaMetricsJNI.h
index 16081b4..b3cb4d2 100644
--- a/core/jni/android_media_MediaMetricsJNI.h
+++ b/core/jni/android_media_MediaMetricsJNI.h
@@ -17,7 +17,6 @@
 #ifndef _ANDROID_MEDIA_MEDIAMETRICSJNI_H_
 #define _ANDROID_MEDIA_MEDIAMETRICSJNI_H_
 
-#include <android_runtime/AndroidRuntime.h>
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
 #include <media/MediaAnalyticsItem.h>
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index bf183cc..638b3ea 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -934,146 +934,6 @@
 jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
 jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
 
-
-/* pulled out of bionic */
-extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
-    size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
-extern "C" void free_malloc_leak_info(uint8_t* info);
-#define SIZE_FLAG_ZYGOTE_CHILD  (1<<31)
-
-static size_t gNumBacktraceElements;
-
-/*
- * This is a qsort() callback.
- *
- * See dumpNativeHeap() for comments about the data format and sort order.
- */
-static int compareHeapRecords(const void* vrec1, const void* vrec2)
-{
-    const size_t* rec1 = (const size_t*) vrec1;
-    const size_t* rec2 = (const size_t*) vrec2;
-    size_t size1 = *rec1;
-    size_t size2 = *rec2;
-
-    if (size1 < size2) {
-        return 1;
-    } else if (size1 > size2) {
-        return -1;
-    }
-
-    uintptr_t* bt1 = (uintptr_t*)(rec1 + 2);
-    uintptr_t* bt2 = (uintptr_t*)(rec2 + 2);
-    for (size_t idx = 0; idx < gNumBacktraceElements; idx++) {
-        uintptr_t addr1 = bt1[idx];
-        uintptr_t addr2 = bt2[idx];
-        if (addr1 == addr2) {
-            if (addr1 == 0)
-                break;
-            continue;
-        }
-        if (addr1 < addr2) {
-            return -1;
-        } else if (addr1 > addr2) {
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-/*
- * The get_malloc_leak_info() call returns an array of structs that
- * look like this:
- *
- *   size_t size
- *   size_t allocations
- *   intptr_t backtrace[32]
- *
- * "size" is the size of the allocation, "backtrace" is a fixed-size
- * array of function pointers, and "allocations" is the number of
- * allocations with the exact same size and backtrace.
- *
- * The entries are sorted by descending total size (i.e. size*allocations)
- * then allocation count.  For best results with "diff" we'd like to sort
- * primarily by individual size then stack trace.  Since the entries are
- * fixed-size, and we're allowed (by the current implementation) to mangle
- * them, we can do this in place.
- */
-static void dumpNativeHeap(FILE* fp)
-{
-    uint8_t* info = NULL;
-    size_t overallSize, infoSize, totalMemory, backtraceSize;
-
-    get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
-        &backtraceSize);
-    if (info == NULL) {
-        fprintf(fp, "Native heap dump not available. To enable, run these"
-                    " commands (requires root):\n");
-        fprintf(fp, "# adb shell stop\n");
-        fprintf(fp, "# adb shell setprop libc.debug.malloc.options "
-                    "backtrace\n");
-        fprintf(fp, "# adb shell start\n");
-        return;
-    }
-    assert(infoSize != 0);
-    assert(overallSize % infoSize == 0);
-
-    fprintf(fp, "Android Native Heap Dump v1.0\n\n");
-
-    size_t recordCount = overallSize / infoSize;
-    fprintf(fp, "Total memory: %zu\n", totalMemory);
-    fprintf(fp, "Allocation records: %zd\n", recordCount);
-    fprintf(fp, "Backtrace size: %zd\n", backtraceSize);
-    fprintf(fp, "\n");
-
-    /* re-sort the entries */
-    gNumBacktraceElements = backtraceSize;
-    qsort(info, recordCount, infoSize, compareHeapRecords);
-
-    /* dump the entries to the file */
-    const uint8_t* ptr = info;
-    for (size_t idx = 0; idx < recordCount; idx++) {
-        size_t size = *(size_t*) ptr;
-        size_t allocations = *(size_t*) (ptr + sizeof(size_t));
-        uintptr_t* backtrace = (uintptr_t*) (ptr + sizeof(size_t) * 2);
-
-        fprintf(fp, "z %d  sz %8zu  num %4zu  bt",
-                (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
-                size & ~SIZE_FLAG_ZYGOTE_CHILD,
-                allocations);
-        for (size_t bt = 0; bt < backtraceSize; bt++) {
-            if (backtrace[bt] == 0) {
-                break;
-            } else {
-#ifdef __LP64__
-                fprintf(fp, " %016" PRIxPTR, backtrace[bt]);
-#else
-                fprintf(fp, " %08" PRIxPTR, backtrace[bt]);
-#endif
-            }
-        }
-        fprintf(fp, "\n");
-
-        ptr += infoSize;
-    }
-
-    free_malloc_leak_info(info);
-
-    fprintf(fp, "MAPS\n");
-    const char* maps = "/proc/self/maps";
-    UniqueFile in = MakeUniqueFile(maps, "re");
-    if (in == nullptr) {
-        fprintf(fp, "Could not open %s\n", maps);
-        return;
-    }
-    char buf[BUFSIZ];
-    while (size_t n = fread(buf, sizeof(char), BUFSIZ, in.get())) {
-        fwrite(buf, sizeof(char), n, fp);
-    }
-
-    fprintf(fp, "END\n");
-}
-
 static bool openFile(JNIEnv* env, jobject fileDescriptor, UniqueFile& fp)
 {
     if (fileDescriptor == NULL) {
@@ -1104,6 +964,9 @@
     return true;
 }
 
+/* pulled out of bionic */
+extern "C" void write_malloc_leak_info(FILE* fp);
+
 /*
  * Dump the native heap, writing human-readable output to the specified
  * file descriptor.
@@ -1117,7 +980,9 @@
     }
 
     ALOGD("Native heap dump starting...\n");
-    dumpNativeHeap(fp.get());
+    // Formatting of the native heap dump is handled by malloc debug itself.
+    // See https://android.googlesource.com/platform/bionic/+/master/libc/malloc_debug/README.md#backtrace-heap-dump-format
+    write_malloc_leak_info(fp.get());
     ALOGD("Native heap dump complete.\n");
 }
 
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index 4ecfd4b..dfa5de6 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -29,7 +29,8 @@
 }
 
 void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
-    android_namespace_t* appNamespace = android::FindNamespaceByClassLoader(env, classLoader);
+    android::NativeLoaderNamespace* appNamespace = android::FindNativeLoaderNamespaceByClassLoader(
+        env, classLoader);
     ScopedUtfChars layerPathsChars(env, layerPaths);
     android::GraphicsEnv::getInstance().setLayerPaths(appNamespace, layerPathsChars.c_str());
 }
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 08d9527..163b86b 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -33,6 +33,7 @@
 #include <hidl/ServiceManagement.h>
 #include <hidl/Status.h>
 #include <hidl/HidlTransportSupport.h>
+#include <hwbinder/IPCThreadState.h>
 #include <hwbinder/ProcessState.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <nativehelper/ScopedUtfChars.h>
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index ca5e1e4..f8f841c 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -189,8 +189,7 @@
 void HwBinderDeathRecipientList::remove(const sp<HwBinderDeathRecipient>& recipient) {
     AutoMutex _l(mLock);
 
-    List< sp<HwBinderDeathRecipient> >::iterator iter;
-    for (iter = mList.begin(); iter != mList.end(); iter++) {
+    for (auto iter = mList.begin(); iter != mList.end(); iter++) {
         if (*iter == recipient) {
             mList.erase(iter);
             return;
@@ -201,12 +200,13 @@
 sp<HwBinderDeathRecipient> HwBinderDeathRecipientList::find(jobject recipient) {
     AutoMutex _l(mLock);
 
-    for (const sp<HwBinderDeathRecipient>& deathRecipient : mList) {
-        if (deathRecipient->matches(recipient)) {
-            return deathRecipient;
+    for(auto iter = mList.rbegin(); iter != mList.rend(); iter++) {
+        if ((*iter)->matches(recipient)) {
+            return (*iter);
         }
     }
-    return NULL;
+
+    return nullptr;
 }
 
 Mutex& HwBinderDeathRecipientList::lock() {
diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h
index 63aad0a..4b5a4c8 100644
--- a/core/jni/android_os_HwRemoteBinder.h
+++ b/core/jni/android_os_HwRemoteBinder.h
@@ -20,9 +20,9 @@
 #include <android-base/macros.h>
 #include <hwbinder/Binder.h>
 #include <jni.h>
-#include <utils/List.h>
 #include <utils/Mutex.h>
 #include <utils/RefBase.h>
+#include <vector>
 
 namespace android {
 
@@ -33,7 +33,7 @@
 class HwBinderDeathRecipient;
 
 class HwBinderDeathRecipientList : public RefBase {
-    List< sp<HwBinderDeathRecipient> > mList;
+    std::vector<sp<HwBinderDeathRecipient>> mList;
     Mutex mLock;
 
 public:
@@ -42,6 +42,8 @@
 
     void add(const sp<HwBinderDeathRecipient>& recipient);
     void remove(const sp<HwBinderDeathRecipient>& recipient);
+
+    // finds the most recently added matching death recipient
     sp<HwBinderDeathRecipient> find(jobject recipient);
 
     Mutex& lock();  // Use with care; specifically for mutual exclusion during binder death
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 5e2cd40..bed239f 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -470,90 +470,6 @@
     return NULL;
 }
 
-static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jclass clazz,
-                                                    jstring name, jint mode)
-{
-    if (name == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return NULL;
-    }
-    ScopedUtfChars name8(env, name);
-    if (name8.c_str() == NULL) {
-        return NULL;
-    }
-
-    int flags=0;
-    switch (mode&0x30000000) {
-        case 0:
-        case 0x10000000:
-            flags = O_RDONLY;
-            break;
-        case 0x20000000:
-            flags = O_WRONLY;
-            break;
-        case 0x30000000:
-            flags = O_RDWR;
-            break;
-    }
-
-    if (mode&0x08000000) flags |= O_CREAT;
-    if (mode&0x04000000) flags |= O_TRUNC;
-    if (mode&0x02000000) flags |= O_APPEND;
-
-    int realMode = S_IRWXU|S_IRWXG;
-    if (mode&0x00000001) realMode |= S_IROTH;
-    if (mode&0x00000002) realMode |= S_IWOTH;
-
-    int fd = open(name8.c_str(), flags, realMode);
-    if (fd < 0) {
-        jniThrowException(env, "java/io/FileNotFoundException", strerror(errno));
-        return NULL;
-    }
-    jobject object = jniCreateFileDescriptor(env, fd);
-    if (object == NULL) {
-        close(fd);
-    }
-    return object;
-}
-
-static jobject android_os_Parcel_dupFileDescriptor(JNIEnv* env, jclass clazz, jobject orig)
-{
-    if (orig == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return NULL;
-    }
-    int origfd = jniGetFDFromFileDescriptor(env, orig);
-    if (origfd < 0) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", "bad FileDescriptor");
-        return NULL;
-    }
-
-    int fd = dup(origfd);
-    if (fd < 0) {
-        jniThrowIOException(env, errno);
-        return NULL;
-    }
-    jobject object = jniCreateFileDescriptor(env, fd);
-    if (object == NULL) {
-        close(fd);
-    }
-    return object;
-}
-
-static void android_os_Parcel_closeFileDescriptor(JNIEnv* env, jclass clazz, jobject object)
-{
-    if (object == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return;
-    }
-    int fd = jniGetFDFromFileDescriptor(env, object);
-    if (fd >= 0) {
-        jniSetFileDescriptorOfFD(env, object, -1);
-        //ALOGI("Closing ParcelFileDescriptor %d\n", fd);
-        close(fd);
-    }
-}
-
 static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
 {
     Parcel* parcel = new Parcel();
@@ -796,10 +712,6 @@
     {"nativeReadStrongBinder",    "(J)Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder},
     {"nativeReadFileDescriptor",  "(J)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor},
 
-    {"openFileDescriptor",        "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_openFileDescriptor},
-    {"dupFileDescriptor",         "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_dupFileDescriptor},
-    {"closeFileDescriptor",       "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_closeFileDescriptor},
-
     {"nativeCreate",              "()J", (void*)android_os_Parcel_create},
     {"nativeFreeBuffer",          "(J)J", (void*)android_os_Parcel_freeBuffer},
     {"nativeDestroy",             "(J)V", (void*)android_os_Parcel_destroy},
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 6778b29..8cb1078 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -60,6 +60,41 @@
     return (security_getenforce() == 1) ? true : false;
 }
 
+static jstring getFdConInner(JNIEnv *env, jobject fileDescriptor, bool isSocket) {
+    if (isSELinuxDisabled) {
+        return NULL;
+    }
+
+    if (fileDescriptor == NULL) {
+        jniThrowNullPointerException(env,
+                "Trying to check security context of a null FileDescriptor.");
+        return NULL;
+    }
+
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+    if (env->ExceptionCheck()) {
+        ALOGE("getFdCon => getFD for %p failed", fileDescriptor);
+        return NULL;
+    }
+
+    security_context_t tmp = NULL;
+    int ret;
+    if (isSocket) {
+        ret = getpeercon(fd, &tmp);
+    } else{
+        ret = fgetfilecon(fd, &tmp);
+    }
+    Unique_SecurityContext context(tmp);
+
+    ScopedLocalRef<jstring> contextStr(env, NULL);
+    if (ret != -1) {
+        contextStr.reset(env->NewStringUTF(context.get()));
+    }
+
+    ALOGV("getFdCon(%d) => %s", fd, context.get());
+    return contextStr.release();
+}
+
 /*
  * Function: getPeerCon
  * Purpose: retrieves security context of peer socket
@@ -69,33 +104,19 @@
  * Exceptions: NullPointerException if fileDescriptor object is NULL
  */
 static jstring getPeerCon(JNIEnv *env, jobject, jobject fileDescriptor) {
-    if (isSELinuxDisabled) {
-        return NULL;
-    }
+    return getFdConInner(env, fileDescriptor, true);
+}
 
-    if (fileDescriptor == NULL) {
-        jniThrowNullPointerException(env,
-                "Trying to check security context of a null peer socket.");
-        return NULL;
-    }
-
-    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
-    if (env->ExceptionCheck()) {
-        ALOGE("getPeerCon => getFD for %p failed", fileDescriptor);
-        return NULL;
-    }
-
-    security_context_t tmp = NULL;
-    int ret = getpeercon(fd, &tmp);
-    Unique_SecurityContext context(tmp);
-
-    ScopedLocalRef<jstring> contextStr(env, NULL);
-    if (ret != -1) {
-        contextStr.reset(env->NewStringUTF(context.get()));
-    }
-
-    ALOGV("getPeerCon(%d) => %s", fd, context.get());
-    return contextStr.release();
+/*
+ * Function: getFdCon
+ * Purpose: retrieves security context of a file descriptor.
+ * Parameters:
+ *        fileDescriptor: a FileDescriptor object
+ * Returns: jstring representing the security_context of socket or NULL if error
+ * Exceptions: NullPointerException if fileDescriptor object is NULL
+ */
+static jstring getFdCon(JNIEnv *env, jobject, jobject fileDescriptor) {
+    return getFdConInner(env, fileDescriptor, false);
 }
 
 /*
@@ -326,6 +347,7 @@
     { "getContext"               , "()Ljava/lang/String;"                         , (void*)getCon           },
     { "getFileContext"           , "(Ljava/lang/String;)Ljava/lang/String;"       , (void*)getFileCon       },
     { "getPeerContext"           , "(Ljava/io/FileDescriptor;)Ljava/lang/String;" , (void*)getPeerCon       },
+    { "getFileContext"           , "(Ljava/io/FileDescriptor;)Ljava/lang/String;" , (void*)getFdCon         },
     { "getPidContext"            , "(I)Ljava/lang/String;"                        , (void*)getPidCon        },
     { "isSELinuxEnforced"        , "()Z"                                          , (void*)isSELinuxEnforced},
     { "isSELinuxEnabled"         , "()Z"                                          , (void*)isSELinuxEnabled },
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 4f4e5da..f7dab42 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -14,93 +14,80 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "Trace"
-// #define LOG_NDEBUG 0
-
-#include <inttypes.h>
+#include <jni.h>
 
 #include <cutils/trace.h>
-#include <utils/String8.h>
 #include <log/log.h>
-
 #include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include <nativehelper/ScopedStringChars.h>
+
+#include <array>
 
 namespace android {
 
-static void sanitizeString(String8& utf8Chars) {
-    size_t size = utf8Chars.size();
-    char* str = utf8Chars.lockBuffer(size);
+inline static void sanitizeString(char* str, size_t size) {
     for (size_t i = 0; i < size; i++) {
         char c = str[i];
         if (c == '\0' || c == '\n' || c == '|') {
             str[i] = ' ';
         }
     }
-    utf8Chars.unlockBuffer();
 }
 
-static jlong android_os_Trace_nativeGetEnabledTags(JNIEnv* env, jclass clazz) {
+inline static void getString(JNIEnv* env, jstring jstring, char* outBuffer, jsize maxSize) {
+    jsize size = std::min(env->GetStringLength(jstring), maxSize);
+    env->GetStringUTFRegion(jstring, 0, size, outBuffer);
+    sanitizeString(outBuffer, size);
+    outBuffer[size] = '\0';
+}
+
+template<typename F>
+inline static void withString(JNIEnv* env, jstring jstr, F callback) {
+    std::array<char, 1024> buffer;
+    getString(env, jstr, buffer.data(), buffer.size());
+    callback(buffer.data());
+}
+
+static jlong android_os_Trace_nativeGetEnabledTags(JNIEnv*, jclass) {
     return atrace_get_enabled_tags();
 }
 
-static void android_os_Trace_nativeTraceCounter(JNIEnv* env, jclass clazz,
+static void android_os_Trace_nativeTraceCounter(JNIEnv* env, jclass,
         jlong tag, jstring nameStr, jint value) {
-    ScopedUtfChars name(env, nameStr);
-
-    ALOGV("%s: %" PRId64 " %s %d", __FUNCTION__, tag, name.c_str(), value);
-    atrace_int(tag, name.c_str(), value);
+    withString(env, nameStr, [tag, value](char* str) {
+        atrace_int(tag, str, value);
+    });
 }
 
-static void android_os_Trace_nativeTraceBegin(JNIEnv* env, jclass clazz,
+static void android_os_Trace_nativeTraceBegin(JNIEnv* env, jclass,
         jlong tag, jstring nameStr) {
-    ScopedStringChars jchars(env, nameStr);
-    String8 utf8Chars(reinterpret_cast<const char16_t*>(jchars.get()), jchars.size());
-    sanitizeString(utf8Chars);
-
-    ALOGV("%s: %" PRId64 " %s", __FUNCTION__, tag, utf8Chars.string());
-    atrace_begin(tag, utf8Chars.string());
+    withString(env, nameStr, [tag](char* str) {
+        atrace_begin(tag, str);
+    });
 }
 
-static void android_os_Trace_nativeTraceEnd(JNIEnv* env, jclass clazz,
-        jlong tag) {
-
-    ALOGV("%s: %" PRId64, __FUNCTION__, tag);
+static void android_os_Trace_nativeTraceEnd(JNIEnv*, jclass, jlong tag) {
     atrace_end(tag);
 }
 
-static void android_os_Trace_nativeAsyncTraceBegin(JNIEnv* env, jclass clazz,
+static void android_os_Trace_nativeAsyncTraceBegin(JNIEnv* env, jclass,
         jlong tag, jstring nameStr, jint cookie) {
-    ScopedStringChars jchars(env, nameStr);
-    String8 utf8Chars(reinterpret_cast<const char16_t*>(jchars.get()), jchars.size());
-    sanitizeString(utf8Chars);
-
-    ALOGV("%s: %" PRId64 " %s %d", __FUNCTION__, tag, utf8Chars.string(), cookie);
-    atrace_async_begin(tag, utf8Chars.string(), cookie);
+    withString(env, nameStr, [tag, cookie](char* str) {
+        atrace_async_begin(tag, str, cookie);
+    });
 }
 
-static void android_os_Trace_nativeAsyncTraceEnd(JNIEnv* env, jclass clazz,
+static void android_os_Trace_nativeAsyncTraceEnd(JNIEnv* env, jclass,
         jlong tag, jstring nameStr, jint cookie) {
-    ScopedStringChars jchars(env, nameStr);
-    String8 utf8Chars(reinterpret_cast<const char16_t*>(jchars.get()), jchars.size());
-    sanitizeString(utf8Chars);
-
-    ALOGV("%s: %" PRId64 " %s %d", __FUNCTION__, tag, utf8Chars.string(), cookie);
-    atrace_async_end(tag, utf8Chars.string(), cookie);
+    withString(env, nameStr, [tag, cookie](char* str) {
+        atrace_async_end(tag, str, cookie);
+    });
 }
 
-static void android_os_Trace_nativeSetAppTracingAllowed(JNIEnv* env,
-        jclass clazz, jboolean allowed) {
-    ALOGV("%s: %d", __FUNCTION__, allowed);
-
+static void android_os_Trace_nativeSetAppTracingAllowed(JNIEnv*, jclass, jboolean allowed) {
     atrace_set_debuggable(allowed);
 }
 
-static void android_os_Trace_nativeSetTracingEnabled(JNIEnv* env,
-        jclass clazz, jboolean enabled) {
-    ALOGV("%s: %d", __FUNCTION__, enabled);
-
+static void android_os_Trace_nativeSetTracingEnabled(JNIEnv*, jclass, jboolean enabled) {
     atrace_set_tracing_enabled(enabled);
 }
 
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index e8ef349..e64d2af 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -96,7 +96,7 @@
     return toJavaStringArray(env, cStrings);
 }
 
-static jint verify(JNIEnv* env, jobjectArray packageInfo, android::vintf::DisabledChecks checks) {
+static jint verify(JNIEnv* env, jobjectArray packageInfo, android::vintf::CheckFlags::Type checks) {
     std::vector<std::string> cPackageInfo;
     if (packageInfo) {
         size_t count = env->GetArrayLength(packageInfo);
@@ -116,11 +116,11 @@
 }
 
 static jint android_os_VintfObject_verify(JNIEnv* env, jclass, jobjectArray packageInfo) {
-    return verify(env, packageInfo, ::android::vintf::ENABLE_ALL_CHECKS);
+    return verify(env, packageInfo, ::android::vintf::CheckFlags::ENABLE_ALL_CHECKS);
 }
 
 static jint android_os_VintfObject_verifyWithoutAvb(JNIEnv* env, jclass) {
-    return verify(env, nullptr, ::android::vintf::DISABLE_AVB_CHECK);
+    return verify(env, nullptr, ::android::vintf::CheckFlags::DISABLE_AVB_CHECK);
 }
 
 static jobjectArray android_os_VintfObject_getHalNamesAndVersions(JNIEnv* env, jclass) {
@@ -150,7 +150,7 @@
     }
     jobject jMap = env->NewObject(gHashMapClazz, gHashMapInit);
     for (const auto &vndk : manifest->vendorNdks()) {
-        std::string key = vndk.version();
+        const std::string& key = vndk.version();
         env->CallObjectMethod(jMap, gHashMapPut,
                 env->NewStringUTF(key.c_str()), toJavaStringArray(env, vndk.libraries()));
     }
diff --git a/core/jni/android_text_LineBreaker.cpp b/core/jni/android_text_LineBreaker.cpp
new file mode 100644
index 0000000..dac108e
--- /dev/null
+++ b/core/jni/android_text_LineBreaker.cpp
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "LineBreaker"
+
+#include "unicode/locid.h"
+#include "unicode/brkiter.h"
+#include "utils/misc.h"
+#include "utils/Log.h"
+#include <nativehelper/ScopedStringChars.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
+#include "scoped_nullable_primitive_array.h"
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <algorithm>
+
+#include "SkPaint.h"
+#include "SkTypeface.h"
+#include <hwui/MinikinSkia.h>
+#include <hwui/MinikinUtils.h>
+#include <hwui/Paint.h>
+#include <minikin/FontCollection.h>
+#include <minikin/AndroidLineBreakerHelper.h>
+#include <minikin/MinikinFont.h>
+
+namespace android {
+
+struct JLineBreaksID {
+    jfieldID breaks;
+    jfieldID widths;
+    jfieldID ascents;
+    jfieldID descents;
+    jfieldID flags;
+};
+
+static jclass gLineBreaks_class;
+static JLineBreaksID gLineBreaks_fieldID;
+
+static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) {
+    if (javaArray == nullptr) {
+         return std::vector<float>();
+    } else {
+        ScopedIntArrayRO intArr(env, javaArray);
+        return std::vector<float>(intArr.get(), intArr.get() + intArr.size());
+    }
+}
+
+static inline minikin::android::StaticLayoutNative* toNative(jlong ptr) {
+    return reinterpret_cast<minikin::android::StaticLayoutNative*>(ptr);
+}
+
+// set text and set a number of parameters for creating a layout (width, tabstops, strategy,
+// hyphenFrequency)
+static jlong nInit(JNIEnv* env, jclass /* unused */,
+        jint breakStrategy, jint hyphenationFrequency, jboolean isJustified, jintArray indents) {
+    return reinterpret_cast<jlong>(new minikin::android::StaticLayoutNative(
+            static_cast<minikin::BreakStrategy>(breakStrategy),
+            static_cast<minikin::HyphenationFrequency>(hyphenationFrequency),
+            isJustified,
+            jintArrayToFloatVector(env, indents)));
+}
+
+static void nFinish(jlong nativePtr) {
+    delete toNative(nativePtr);
+}
+
+// CriticalNative
+static jlong nGetReleaseFunc() {
+    return reinterpret_cast<jlong>(nFinish);
+}
+
+static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
+                        jfloatArray recycleWidths, jfloatArray recycleAscents,
+                        jfloatArray recycleDescents, jintArray recycleFlags,
+                        jint recycleLength, const minikin::LineBreakResult& result) {
+    const size_t nBreaks = result.breakPoints.size();
+    if ((size_t)recycleLength < nBreaks) {
+        // have to reallocate buffers
+        recycleBreaks = env->NewIntArray(nBreaks);
+        recycleWidths = env->NewFloatArray(nBreaks);
+        recycleAscents = env->NewFloatArray(nBreaks);
+        recycleDescents = env->NewFloatArray(nBreaks);
+        recycleFlags = env->NewIntArray(nBreaks);
+
+        env->SetObjectField(recycle, gLineBreaks_fieldID.breaks, recycleBreaks);
+        env->SetObjectField(recycle, gLineBreaks_fieldID.widths, recycleWidths);
+        env->SetObjectField(recycle, gLineBreaks_fieldID.ascents, recycleAscents);
+        env->SetObjectField(recycle, gLineBreaks_fieldID.descents, recycleDescents);
+        env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags);
+    }
+    // copy data
+    env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, result.breakPoints.data());
+    env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, result.widths.data());
+    env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, result.ascents.data());
+    env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, result.descents.data());
+    env->SetIntArrayRegion(recycleFlags, 0, nBreaks, result.flags.data());
+}
+
+static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
+        // Inputs
+        jcharArray javaText,
+        jlong measuredTextPtr,
+        jint length,
+        jfloat firstWidth,
+        jint firstWidthLineCount,
+        jfloat restWidth,
+        jintArray variableTabStops,
+        jint defaultTabStop,
+        jint indentsOffset,
+
+        // Outputs
+        jobject recycle,
+        jint recycleLength,
+        jintArray recycleBreaks,
+        jfloatArray recycleWidths,
+        jfloatArray recycleAscents,
+        jfloatArray recycleDescents,
+        jintArray recycleFlags,
+        jfloatArray charWidths) {
+
+    minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
+
+    ScopedCharArrayRO text(env, javaText);
+    ScopedNullableIntArrayRO tabStops(env, variableTabStops);
+
+    minikin::U16StringPiece u16Text(text.get(), length);
+    minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr);
+    minikin::LineBreakResult result = builder->computeBreaks(
+            u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset,
+            tabStops.get(), tabStops.size(), defaultTabStop);
+
+    recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents,
+            recycleFlags, recycleLength, result);
+
+    return static_cast<jint>(result.breakPoints.size());
+}
+
+static const JNINativeMethod gMethods[] = {
+    // Fast Natives
+    {"nInit", "("
+        "I"  // breakStrategy
+        "I"  // hyphenationFrequency
+        "Z"  // isJustified
+        "[I"  // indents
+        ")J", (void*) nInit},
+
+    // Critical Natives
+    {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc},
+
+    // Regular JNI
+    {"nComputeLineBreaks", "("
+        "J"  // nativePtr
+
+        // Inputs
+        "[C"  // text
+        "J"  // MeasuredParagraph ptr.
+        "I"  // length
+        "F"  // firstWidth
+        "I"  // firstWidthLineCount
+        "F"  // restWidth
+        "[I"  // variableTabStops
+        "I"  // defaultTabStop
+        "I"  // indentsOffset
+
+        // Outputs
+        "Landroid/text/NativeLineBreaker$LineBreaks;"  // recycle
+        "I"  // recycleLength
+        "[I"  // recycleBreaks
+        "[F"  // recycleWidths
+        "[F"  // recycleAscents
+        "[F"  // recycleDescents
+        "[I"  // recycleFlags
+        ")I", (void*) nComputeLineBreaks}
+};
+
+int register_android_text_LineBreaker(JNIEnv* env)
+{
+    gLineBreaks_class = MakeGlobalRefOrDie(env,
+            FindClassOrDie(env, "android/text/NativeLineBreaker$LineBreaks"));
+
+    gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I");
+    gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F");
+    gLineBreaks_fieldID.ascents = GetFieldIDOrDie(env, gLineBreaks_class, "ascents", "[F");
+    gLineBreaks_fieldID.descents = GetFieldIDOrDie(env, gLineBreaks_class, "descents", "[F");
+    gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I");
+
+    return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker",
+                                gMethods, NELEM(gMethods));
+}
+
+}
diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp
index 41a81ac..18f509c 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -17,7 +17,6 @@
 #define LOG_TAG "MeasuredParagraph"
 
 #include "GraphicsJNI.h"
-#include "ScopedIcuLocale.h"
 #include "unicode/locid.h"
 #include "unicode/brkiter.h"
 #include "utils/misc.h"
@@ -110,6 +109,10 @@
     return r;
 }
 
+static jfloat nGetCharWidthAt(jlong ptr, jint offset) {
+    return toMeasuredParagraph(ptr)->widths[offset];
+}
+
 // Regular JNI
 static void nGetBounds(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jint start, jint end,
                        jobject bounds) {
@@ -139,23 +142,29 @@
     return static_cast<jint>(toMeasuredParagraph(ptr)->getMemoryUsage());
 }
 
-static const JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMTBuilderMethods[] = {
     // MeasuredParagraphBuilder native functions.
     {"nInitBuilder", "()J", (void*) nInitBuilder},
     {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
     {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
     {"nBuildNativeMeasuredParagraph", "(J[CZZ)J", (void*) nBuildNativeMeasuredParagraph},
     {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
+};
 
+static const JNINativeMethod gMTMethods[] = {
     // MeasuredParagraph native functions.
     {"nGetWidth", "(JII)F", (void*) nGetWidth},  // Critical Natives
     {"nGetBounds", "(J[CIILandroid/graphics/Rect;)V", (void*) nGetBounds},  // Regular JNI
     {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc},  // Critical Natives
     {"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage},  // Critical Native
+    {"nGetCharWidthAt", "(JI)F", (void*) nGetCharWidthAt},  // Critical Native
 };
 
 int register_android_text_MeasuredParagraph(JNIEnv* env) {
-    return RegisterMethodsOrDie(env, "android/text/MeasuredParagraph", gMethods, NELEM(gMethods));
+    return RegisterMethodsOrDie(env, "android/text/NativeMeasuredParagraph",
+            gMTMethods, NELEM(gMTMethods))
+        + RegisterMethodsOrDie(env, "android/text/NativeMeasuredParagraph$Builder",
+            gMTBuilderMethods, NELEM(gMTBuilderMethods));
 }
 
 }
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
deleted file mode 100644
index ad84858..0000000
--- a/core/jni/android_text_StaticLayout.cpp
+++ /dev/null
@@ -1,207 +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.
- */
-
-#define LOG_TAG "StaticLayout"
-
-#include "ScopedIcuLocale.h"
-#include "unicode/locid.h"
-#include "unicode/brkiter.h"
-#include "utils/misc.h"
-#include "utils/Log.h"
-#include <nativehelper/ScopedStringChars.h>
-#include <nativehelper/ScopedPrimitiveArray.h>
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-#include "scoped_nullable_primitive_array.h"
-#include <cstdint>
-#include <vector>
-#include <list>
-#include <algorithm>
-
-#include "SkPaint.h"
-#include "SkTypeface.h"
-#include <hwui/MinikinSkia.h>
-#include <hwui/MinikinUtils.h>
-#include <hwui/Paint.h>
-#include <minikin/FontCollection.h>
-#include <minikin/AndroidLineBreakerHelper.h>
-#include <minikin/MinikinFont.h>
-
-namespace android {
-
-struct JLineBreaksID {
-    jfieldID breaks;
-    jfieldID widths;
-    jfieldID ascents;
-    jfieldID descents;
-    jfieldID flags;
-};
-
-static jclass gLineBreaks_class;
-static JLineBreaksID gLineBreaks_fieldID;
-
-static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) {
-    if (javaArray == nullptr) {
-         return std::vector<float>();
-    } else {
-        ScopedIntArrayRO intArr(env, javaArray);
-        return std::vector<float>(intArr.get(), intArr.get() + intArr.size());
-    }
-}
-
-static inline minikin::android::StaticLayoutNative* toNative(jlong ptr) {
-    return reinterpret_cast<minikin::android::StaticLayoutNative*>(ptr);
-}
-
-// set text and set a number of parameters for creating a layout (width, tabstops, strategy,
-// hyphenFrequency)
-static jlong nInit(JNIEnv* env, jclass /* unused */,
-        jint breakStrategy, jint hyphenationFrequency, jboolean isJustified, jintArray indents) {
-    return reinterpret_cast<jlong>(new minikin::android::StaticLayoutNative(
-            static_cast<minikin::BreakStrategy>(breakStrategy),
-            static_cast<minikin::HyphenationFrequency>(hyphenationFrequency),
-            isJustified,
-            jintArrayToFloatVector(env, indents)));
-}
-
-// CriticalNative
-static void nFinish(jlong nativePtr) {
-    delete toNative(nativePtr);
-}
-
-static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
-                        jfloatArray recycleWidths, jfloatArray recycleAscents,
-                        jfloatArray recycleDescents, jintArray recycleFlags,
-                        jint recycleLength, const minikin::LineBreakResult& result) {
-    const size_t nBreaks = result.breakPoints.size();
-    if ((size_t)recycleLength < nBreaks) {
-        // have to reallocate buffers
-        recycleBreaks = env->NewIntArray(nBreaks);
-        recycleWidths = env->NewFloatArray(nBreaks);
-        recycleAscents = env->NewFloatArray(nBreaks);
-        recycleDescents = env->NewFloatArray(nBreaks);
-        recycleFlags = env->NewIntArray(nBreaks);
-
-        env->SetObjectField(recycle, gLineBreaks_fieldID.breaks, recycleBreaks);
-        env->SetObjectField(recycle, gLineBreaks_fieldID.widths, recycleWidths);
-        env->SetObjectField(recycle, gLineBreaks_fieldID.ascents, recycleAscents);
-        env->SetObjectField(recycle, gLineBreaks_fieldID.descents, recycleDescents);
-        env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags);
-    }
-    // copy data
-    env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, result.breakPoints.data());
-    env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, result.widths.data());
-    env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, result.ascents.data());
-    env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, result.descents.data());
-    env->SetIntArrayRegion(recycleFlags, 0, nBreaks, result.flags.data());
-}
-
-static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
-        // Inputs
-        jcharArray javaText,
-        jlong measuredTextPtr,
-        jint length,
-        jfloat firstWidth,
-        jint firstWidthLineCount,
-        jfloat restWidth,
-        jintArray variableTabStops,
-        jint defaultTabStop,
-        jint indentsOffset,
-
-        // Outputs
-        jobject recycle,
-        jint recycleLength,
-        jintArray recycleBreaks,
-        jfloatArray recycleWidths,
-        jfloatArray recycleAscents,
-        jfloatArray recycleDescents,
-        jintArray recycleFlags,
-        jfloatArray charWidths) {
-
-    minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
-
-    ScopedCharArrayRO text(env, javaText);
-    ScopedNullableIntArrayRO tabStops(env, variableTabStops);
-
-    minikin::U16StringPiece u16Text(text.get(), length);
-    minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr);
-    minikin::LineBreakResult result = builder->computeBreaks(
-            u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset,
-            tabStops.get(), tabStops.size(), defaultTabStop);
-
-    recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents,
-            recycleFlags, recycleLength, result);
-
-    env->SetFloatArrayRegion(charWidths, 0, measuredText->widths.size(),
-                             measuredText->widths.data());
-
-    return static_cast<jint>(result.breakPoints.size());
-}
-
-static const JNINativeMethod gMethods[] = {
-    // Fast Natives
-    {"nInit", "("
-        "I"  // breakStrategy
-        "I"  // hyphenationFrequency
-        "Z"  // isJustified
-        "[I"  // indents
-        ")J", (void*) nInit},
-
-    // Critical Natives
-    {"nFinish", "(J)V", (void*) nFinish},
-
-    // Regular JNI
-    {"nComputeLineBreaks", "("
-        "J"  // nativePtr
-
-        // Inputs
-        "[C"  // text
-        "J"  // MeasuredParagraph ptr.
-        "I"  // length
-        "F"  // firstWidth
-        "I"  // firstWidthLineCount
-        "F"  // restWidth
-        "[I"  // variableTabStops
-        "I"  // defaultTabStop
-        "I"  // indentsOffset
-
-        // Outputs
-        "Landroid/text/StaticLayout$LineBreaks;"  // recycle
-        "I"  // recycleLength
-        "[I"  // recycleBreaks
-        "[F"  // recycleWidths
-        "[F"  // recycleAscents
-        "[F"  // recycleDescents
-        "[I"  // recycleFlags
-        "[F"  // charWidths
-        ")I", (void*) nComputeLineBreaks}
-};
-
-int register_android_text_StaticLayout(JNIEnv* env)
-{
-    gLineBreaks_class = MakeGlobalRefOrDie(env,
-            FindClassOrDie(env, "android/text/StaticLayout$LineBreaks"));
-
-    gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I");
-    gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F");
-    gLineBreaks_fieldID.ascents = GetFieldIDOrDie(env, gLineBreaks_class, "ascents", "[F");
-    gLineBreaks_fieldID.descents = GetFieldIDOrDie(env, gLineBreaks_class, "descents", "[F");
-    gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I");
-
-    return RegisterMethodsOrDie(env, "android/text/StaticLayout", gMethods, NELEM(gMethods));
-}
-
-}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index c3ba9ba..ecad6c0 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -108,7 +108,6 @@
     jclass mClass;
     jmethodID mGetInstance;
     jmethodID mSendDeathNotice;
-    jmethodID mDumpProxyDebugInfo;
 
     // Object state.
     jfieldID mNativeData;  // Field holds native pointer to BinderProxyNativeData.
@@ -155,9 +154,8 @@
 static constexpr int32_t PROXY_WARN_INTERVAL = 5000;
 static constexpr uint32_t GC_INTERVAL = 1000;
 
-// Protected by gProxyLock. We warn if this gets too large.
-static int32_t gNumProxies = 0;
-static int32_t gProxiesWarned = 0;
+static std::atomic<uint32_t> gNumProxies(0);
+static std::atomic<uint32_t> gProxiesWarned(0);
 
 // Number of GlobalRefs held by JavaBBinders.
 static std::atomic<uint32_t> gNumLocalRefsCreated(0);
@@ -632,12 +630,6 @@
     return (BinderProxyNativeData *) env->GetLongField(obj, gBinderProxyOffsets.mNativeData);
 }
 
-static Mutex gProxyLock;
-
-// We may cache a single BinderProxyNativeData node to avoid repeat allocation.
-// All fields are null. Protected by gProxyLock.
-static BinderProxyNativeData *gNativeDataCache;
-
 // If the argument is a JavaBBinder, return the Java object that was used to create it.
 // Otherwise return a BinderProxy for the IBinder. If a previous call was passed the
 // same IBinder, and the original BinderProxy is still alive, return the same BinderProxy.
@@ -652,36 +644,31 @@
         return object;
     }
 
-    // For the rest of the function we will hold this lock, to serialize
-    // looking/creation/destruction of Java proxies for native Binder proxies.
-    AutoMutex _l(gProxyLock);
+    BinderProxyNativeData* nativeData = new BinderProxyNativeData();
+    nativeData->mOrgue = new DeathRecipientList;
+    nativeData->mObject = val;
 
-    BinderProxyNativeData* nativeData = gNativeDataCache;
-    if (nativeData == nullptr) {
-        nativeData = new BinderProxyNativeData();
-    }
-    // gNativeDataCache is now logically empty.
     jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
             gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
     if (env->ExceptionCheck()) {
         // In the exception case, getInstance still took ownership of nativeData.
-        gNativeDataCache = nullptr;
         return NULL;
     }
     BinderProxyNativeData* actualNativeData = getBPNativeData(env, object);
     if (actualNativeData == nativeData) {
-        // New BinderProxy; we still have exclusive access.
-        nativeData->mOrgue = new DeathRecipientList;
-        nativeData->mObject = val;
-        gNativeDataCache = nullptr;
-        ++gNumProxies;
-        if (gNumProxies >= gProxiesWarned + PROXY_WARN_INTERVAL) {
-            ALOGW("Unexpectedly many live BinderProxies: %d\n", gNumProxies);
-            gProxiesWarned = gNumProxies;
+        // Created a new Proxy
+        uint32_t numProxies = gNumProxies.fetch_add(1, std::memory_order_relaxed);
+        uint32_t numLastWarned = gProxiesWarned.load(std::memory_order_relaxed);
+        if (numProxies >= numLastWarned + PROXY_WARN_INTERVAL) {
+            // Multiple threads can get here, make sure only one of them gets to
+            // update the warn counter.
+            if (gProxiesWarned.compare_exchange_strong(numLastWarned,
+                        numLastWarned + PROXY_WARN_INTERVAL, std::memory_order_relaxed)) {
+                ALOGW("Unexpectedly many live BinderProxies: %d\n", numProxies);
+            }
         }
     } else {
-        // nativeData wasn't used. Reuse it the next time.
-        gNativeDataCache = nativeData;
+        delete nativeData;
     }
 
     return object;
@@ -959,8 +946,7 @@
 
 jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz)
 {
-    AutoMutex _l(gProxyLock);
-    return gNumProxies;
+    return gNumProxies.load();
 }
 
 jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz)
@@ -1007,18 +993,6 @@
 static void android_os_BinderInternal_proxyLimitcallback(int uid)
 {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
-    {
-        // Calls into BinderProxy must be serialized
-        AutoMutex _l(gProxyLock);
-        env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
-                                    gBinderProxyOffsets.mDumpProxyDebugInfo);
-    }
-    if (env->ExceptionCheck()) {
-        ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
-        report_exception(env, excep.get(),
-            "*** Uncaught exception in dumpProxyDebugInfo");
-    }
-
     env->CallStaticVoidMethod(gBinderInternalOffsets.mClass,
                               gBinderInternalOffsets.mProxyLimitCallback,
                               uid);
@@ -1367,9 +1341,6 @@
 
 static void BinderProxy_destroy(void* rawNativeData)
 {
-    // Don't race with construction/initialization
-    AutoMutex _l(gProxyLock);
-
     BinderProxyNativeData * nativeData = (BinderProxyNativeData *) rawNativeData;
     LOGDEATH("Destroying BinderProxy: binder=%p drl=%p\n",
             nativeData->mObject.get(), nativeData->mOrgue.get());
@@ -1408,8 +1379,6 @@
             "(JJ)Landroid/os/BinderProxy;");
     gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
             "(Landroid/os/IBinder$DeathRecipient;)V");
-    gBinderProxyOffsets.mDumpProxyDebugInfo = GetStaticMethodIDOrDie(env, clazz, "dumpProxyDebugInfo",
-            "()V");
     gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");
 
     clazz = FindClassOrDie(env, "java/lang/Class");
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 095252c..10da892 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -114,8 +114,8 @@
 
     uint32_t publishedSeq = mNextPublishedSeq++;
     status_t status = mInputPublisher.publishKeyEvent(publishedSeq,
-            event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(),
-            event->getKeyCode(), event->getScanCode(), event->getMetaState(),
+            event->getDeviceId(), event->getSource(), event->getDisplayId(), event->getAction(),
+            event->getFlags(), event->getKeyCode(), event->getScanCode(), event->getMetaState(),
             event->getRepeatCount(), event->getDownTime(), event->getEventTime());
     if (status) {
         ALOGW("Failed to send key event on channel '%s'.  status=%d",
@@ -135,8 +135,7 @@
     for (size_t i = 0; i <= event->getHistorySize(); i++) {
         publishedSeq = mNextPublishedSeq++;
         status_t status = mInputPublisher.publishMotionEvent(publishedSeq,
-                event->getDeviceId(), event->getSource(),
-                event->getDisplayId(),
+                event->getDeviceId(), event->getSource(), event->getDisplayId(),
                 event->getAction(), event->getActionButton(), event->getFlags(),
                 event->getEdgeFlags(), event->getMetaState(), event->getButtonState(),
                 event->getXOffset(), event->getYOffset(),
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index 8a6e745..f010772 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -39,6 +39,7 @@
 
     jfieldID mDeviceId;
     jfieldID mSource;
+    jfieldID mDisplayId;
     jfieldID mMetaState;
     jfieldID mAction;
     jfieldID mKeyCode;
@@ -65,6 +66,7 @@
             event->getScanCode(),
             event->getFlags(),
             event->getSource(),
+            event->getDisplayId(),
             NULL);
     if (env->ExceptionCheck()) {
         ALOGE("An exception occurred while obtaining a key event.");
@@ -79,6 +81,7 @@
         KeyEvent* event) {
     jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId);
     jint source = env->GetIntField(eventObj, gKeyEventClassInfo.mSource);
+    jint displayId = env->GetIntField(eventObj, gKeyEventClassInfo.mDisplayId);
     jint metaState = env->GetIntField(eventObj, gKeyEventClassInfo.mMetaState);
     jint action = env->GetIntField(eventObj, gKeyEventClassInfo.mAction);
     jint keyCode = env->GetIntField(eventObj, gKeyEventClassInfo.mKeyCode);
@@ -88,7 +91,8 @@
     jlong downTime = env->GetLongField(eventObj, gKeyEventClassInfo.mDownTime);
     jlong eventTime = env->GetLongField(eventObj, gKeyEventClassInfo.mEventTime);
 
-    event->initialize(deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
+    event->initialize(deviceId, source, displayId, action, flags, keyCode, scanCode, metaState,
+            repeatCount,
             milliseconds_to_nanoseconds(downTime),
             milliseconds_to_nanoseconds(eventTime));
     return OK;
@@ -131,12 +135,14 @@
     gKeyEventClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
 
     gKeyEventClassInfo.obtain = GetStaticMethodIDOrDie(env, gKeyEventClassInfo.clazz,
-            "obtain", "(JJIIIIIIIILjava/lang/String;)Landroid/view/KeyEvent;");
+            "obtain", "(JJIIIIIIIIILjava/lang/String;)Landroid/view/KeyEvent;");
     gKeyEventClassInfo.recycle = GetMethodIDOrDie(env, gKeyEventClassInfo.clazz,
             "recycle", "()V");
 
     gKeyEventClassInfo.mDeviceId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDeviceId", "I");
     gKeyEventClassInfo.mSource = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mSource", "I");
+    gKeyEventClassInfo.mDisplayId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDisplayId",
+                                                    "I");
     gKeyEventClassInfo.mMetaState = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mMetaState",
                                                     "I");
     gKeyEventClassInfo.mAction = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mAction", "I");
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 37ea810..46b19bd 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -222,6 +222,11 @@
             RenderNode::GENERIC);
 }
 
+static void android_view_RenderNode_setUsageHint(jlong renderNodePtr, jint usageHint) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->setUsageHint(static_cast<UsageHint>(usageHint));
+}
+
 static jboolean android_view_RenderNode_setElevation(jlong renderNodePtr, float elevation) {
     return SET_AND_DIRTY(setElevation, elevation, RenderNode::Z);
 }
@@ -431,6 +436,14 @@
     return renderNode->stagingProperties().getPivotY();
 }
 
+static jint android_view_RenderNode_getWidth(jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getWidth();
+}
+
+static jint android_view_RenderNode_getHeight(jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getHeight();
+}
+
 // ----------------------------------------------------------------------------
 // RenderProperties - Animations
 // ----------------------------------------------------------------------------
@@ -606,6 +619,7 @@
     { "nSetAlpha",             "(JF)Z",  (void*) android_view_RenderNode_setAlpha },
     { "nSetHasOverlappingRendering", "(JZ)Z",
             (void*) android_view_RenderNode_setHasOverlappingRendering },
+    { "nSetUsageHint",    "(JI)V", (void*) android_view_RenderNode_setUsageHint },
     { "nSetElevation",         "(JF)Z",  (void*) android_view_RenderNode_setElevation },
     { "nSetTranslationX",      "(JF)Z",  (void*) android_view_RenderNode_setTranslationX },
     { "nSetTranslationY",      "(JF)Z",  (void*) android_view_RenderNode_setTranslationY },
@@ -648,6 +662,8 @@
 
     { "nGetPivotX",                "(J)F",  (void*) android_view_RenderNode_getPivotX },
     { "nGetPivotY",                "(J)F",  (void*) android_view_RenderNode_getPivotY },
+    { "nGetWidth",                 "(J)I",  (void*) android_view_RenderNode_getWidth },
+    { "nGetHeight",                "(J)I",  (void*) android_view_RenderNode_getHeight },
 };
 
 int register_android_view_RenderNode(JNIEnv* env) {
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index f70cf07..f512ce4 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -332,8 +332,7 @@
     SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
                                          convertPixelFormat(outBuffer.format),
                                          outBuffer.format == PIXEL_FORMAT_RGBX_8888
-                                                 ? kOpaque_SkAlphaType : kPremul_SkAlphaType,
-                                         GraphicsJNI::defaultColorSpace());
+                                                 ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
 
     SkBitmap bitmap;
     ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5b4b5f2..02d6adb 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -446,7 +446,7 @@
 
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     Rect crop(l, t, r, b);
-    transaction->setCrop(ctrl, crop);
+    transaction->setCrop_legacy(ctrl, crop);
 }
 
 static void nativeSetFinalCrop(JNIEnv* env, jclass clazz, jlong transactionObj,
@@ -456,7 +456,7 @@
 
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     Rect crop(l, t, r, b);
-    transaction->setFinalCrop(ctrl, crop);
+    transaction->setFinalCrop_legacy(ctrl, crop);
 }
 
 static void nativeSetLayerStack(JNIEnv* env, jclass clazz, jlong transactionObj,
@@ -799,7 +799,7 @@
 
     {
         auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
-        transaction->deferTransactionUntil(ctrl, handle, frameNumber);
+        transaction->deferTransactionUntil_legacy(ctrl, handle, frameNumber);
     }
 }
 
@@ -811,7 +811,7 @@
     auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     sp<Surface> barrier = reinterpret_cast<Surface *>(surfaceObject);
 
-    transaction->deferTransactionUntil(ctrl, barrier, frameNumber);
+    transaction->deferTransactionUntil_legacy(ctrl, barrier, frameNumber);
 }
 
 static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong transactionObj,
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 2921b37..15319ad 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -94,8 +94,7 @@
         default:
             break;
     }
-    return SkImageInfo::Make(buffer.width, buffer.height, colorType, alphaType,
-            GraphicsJNI::defaultColorSpace());
+    return SkImageInfo::Make(buffer.width, buffer.height, colorType, alphaType);
 }
 
 /**
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 497b289..4a17742 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -1054,6 +1054,13 @@
     Properties::contextPriority = contextPriority;
 }
 
+static void android_view_ThreadedRenderer_allocateBuffers(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jobject jsurface) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    sp<Surface> surface = android_view_Surface_getSurface(env, jsurface);
+    proxy->allocateBuffers(surface);
+}
+
 // ----------------------------------------------------------------------------
 // FrameMetricsObserver
 // ----------------------------------------------------------------------------
@@ -1166,6 +1173,7 @@
     { "nSetDebuggingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDebuggingEnabled },
     { "nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess },
     { "nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority },
+    { "nAllocateBuffers", "(JLandroid/view/Surface;)V", (void*)android_view_ThreadedRenderer_allocateBuffers },
 };
 
 static JavaVM* mJvm = nullptr;
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index cc2646c..dc04269 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -27,6 +27,7 @@
 
 #include <zlib.h>
 
+#include <errno.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
@@ -567,7 +568,14 @@
         return 0;
     }
 
-    ZipFileRO* zipFile = ZipFileRO::openFd(fd, debugFilePath.c_str());
+    int dupedFd = dup(fd);
+    if (dupedFd == -1) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                             "Failed to dup FileDescriptor: %s", strerror(errno));
+        return 0;
+    }
+
+    ZipFileRO* zipFile = ZipFileRO::openFd(dupedFd, debugFilePath.c_str());
 
     return reinterpret_cast<jlong>(zipFile);
 }
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 12165d4..19691e2 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -24,6 +24,7 @@
 #include <sstream>
 #include <string>
 
+#include <android/fdsan.h>
 #include <fcntl.h>
 #include <grp.h>
 #include <inttypes.h>
@@ -45,6 +46,7 @@
 #include <unistd.h>
 
 #include "android-base/logging.h"
+#include <android-base/properties.h>
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <cutils/fs.h>
@@ -68,14 +70,17 @@
 namespace {
 
 using android::String8;
+using android::base::StringAppendF;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
+using android::base::GetBoolProperty;
 
 #define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
                               append(StringPrintf(__VA_ARGS__))
 
 static pid_t gSystemServerPid = 0;
 
+static const char kIsolatedStorage[] = "persist.sys.isolated_storage";
 static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
 static jclass gZygoteClass;
 static jmethodID gCallPostForkChildHooks;
@@ -376,10 +381,32 @@
     return 0;
 }
 
+static bool createPkgSandbox(uid_t uid, const char* package_name, std::string& pkg_sandbox_dir,
+        std::string* error_msg) {
+    // Create /mnt/user/0/package/<package-name>
+    userid_t user_id = multiuser_get_user_id(uid);
+    StringAppendF(&pkg_sandbox_dir, "/%d", user_id);
+    if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0751, AID_ROOT, AID_ROOT) != 0) {
+        *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
+        return false;
+    }
+    StringAppendF(&pkg_sandbox_dir, "/package");
+    if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
+        *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
+        return false;
+    }
+    StringAppendF(&pkg_sandbox_dir, "/%s", package_name);
+    if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0755, uid, uid) != 0) {
+        *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
+        return false;
+    }
+    return true;
+}
+
 // Create a private mount namespace and bind mount appropriate emulated
 // storage for the given user.
 static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
-        bool force_mount_namespace, std::string* error_msg) {
+        bool force_mount_namespace, std::string* error_msg, const char* package_name) {
     // See storage config details at http://source.android.com/tech/storage/
 
     String8 storageSource;
@@ -405,27 +432,44 @@
         return true;
     }
 
-    if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
-            NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
-        *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
-                                  storageSource.string(),
-                                  strerror(errno));
-        return false;
-    }
+    if (GetBoolProperty(kIsolatedStorage, false)) {
+        if (package_name == nullptr) {
+            return true;
+        }
 
-    // Mount user-specific symlink helper into place
-    userid_t user_id = multiuser_get_user_id(uid);
-    const String8 userSource(String8::format("/mnt/user/%d", user_id));
-    if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
-        *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", userSource.string());
-        return false;
-    }
-    if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
-            NULL, MS_BIND, NULL)) == -1) {
-        *error_msg = CREATE_ERROR("Failed to mount %s to /storage/self: %s",
-                                  userSource.string(),
-                                  strerror(errno));
-        return false;
+        std::string pkgSandboxDir("/mnt/user");
+        if (!createPkgSandbox(uid, package_name, pkgSandboxDir, error_msg)) {
+            return false;
+        }
+        if (TEMP_FAILURE_RETRY(mount(pkgSandboxDir.c_str(), "/storage",
+                nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
+            *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
+                    pkgSandboxDir.c_str(), strerror(errno));
+            return false;
+        }
+    } else {
+        if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
+                NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
+            *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
+                                      storageSource.string(),
+                                      strerror(errno));
+            return false;
+        }
+
+        // Mount user-specific symlink helper into place
+        userid_t user_id = multiuser_get_user_id(uid);
+        const String8 userSource(String8::format("/mnt/user/%d", user_id));
+        if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
+            *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", userSource.string());
+            return false;
+        }
+        if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
+                NULL, MS_BIND, NULL)) == -1) {
+            *error_msg = CREATE_ERROR("Failed to mount %s to /storage/self: %s",
+                                      userSource.string(),
+                                      strerror(errno));
+            return false;
+        }
     }
 
     return true;
@@ -535,17 +579,220 @@
   return true;
 }
 
+// Utility routine to specialize a zygote child process.
+static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
+                             jint runtime_flags, jobjectArray javaRlimits,
+                             jlong permittedCapabilities, jlong effectiveCapabilities,
+                             jint mount_external, jstring java_se_info, jstring java_se_name,
+                             bool is_system_server, bool is_child_zygote, jstring instructionSet,
+                             jstring dataDir, jstring packageName) {
+  std::string error_msg;
+
+  auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
+      __attribute__ ((noreturn)) {
+    const char* se_name_c_str = nullptr;
+    std::unique_ptr<ScopedUtfChars> se_name;
+    if (java_se_name != nullptr) {
+      se_name.reset(new ScopedUtfChars(env, java_se_name));
+      se_name_c_str = se_name->c_str();
+    }
+    if (se_name_c_str == nullptr && is_system_server) {
+      se_name_c_str = "system_server";
+    }
+    const std::string& error_msg = (se_name_c_str == nullptr)
+        ? msg
+        : StringPrintf("(%s) %s", se_name_c_str, msg.c_str());
+    env->FatalError(error_msg.c_str());
+    __builtin_unreachable();
+  };
+
+  // Keep capabilities across UID change, unless we're staying root.
+  if (uid != 0) {
+    if (!EnableKeepCapabilities(&error_msg)) {
+      fail_fn(error_msg);
+    }
+  }
+
+  if (!SetInheritable(permittedCapabilities, &error_msg)) {
+    fail_fn(error_msg);
+  }
+  if (!DropCapabilitiesBoundingSet(&error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  bool use_native_bridge = !is_system_server && (instructionSet != NULL)
+      && android::NativeBridgeAvailable();
+  if (use_native_bridge) {
+    ScopedUtfChars isa_string(env, instructionSet);
+    use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());
+  }
+  if (use_native_bridge && dataDir == NULL) {
+    // dataDir should never be null if we need to use a native bridge.
+    // In general, dataDir will never be null for normal applications. It can only happen in
+    // special cases (for isolated processes which are not associated with any app). These are
+    // launched by the framework and should not be emulated anyway.
+    use_native_bridge = false;
+    ALOGW("Native bridge will not be used because dataDir == NULL.");
+  }
+
+  ScopedUtfChars* package_name = nullptr;
+  const char* package_name_c_str = nullptr;
+  if (packageName != nullptr) {
+    package_name = new ScopedUtfChars(env, packageName);
+    package_name_c_str = package_name->c_str();
+  } else if (is_system_server) {
+    package_name_c_str = "android";
+  }
+  bool success = MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg,
+      package_name_c_str);
+  delete package_name;
+  if (!success) {
+    ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
+    if (errno == ENOTCONN || errno == EROFS) {
+      // When device is actively encrypting, we get ENOTCONN here
+      // since FUSE was mounted before the framework restarted.
+      // When encrypted device is booting, we get EROFS since
+      // FUSE hasn't been created yet by init.
+      // In either case, continue without external storage.
+    } else {
+      fail_fn(error_msg);
+    }
+  }
+
+  // If this zygote isn't root, it won't be able to create a process group,
+  // since the directory is owned by root.
+  if (!is_system_server && getuid() == 0) {
+      int rc = createProcessGroup(uid, getpid());
+      if (rc != 0) {
+          if (rc == -EROFS) {
+              ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
+          } else {
+              ALOGE("createProcessGroup(%d, %d) failed: %s", uid, 0/*pid*/, strerror(-rc));
+          }
+      }
+  }
+
+  if (!SetGids(env, javaGids, &error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  if (!SetRLimits(env, javaRlimits, &error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  if (use_native_bridge) {
+    ScopedUtfChars isa_string(env, instructionSet);
+    ScopedUtfChars data_dir(env, dataDir);
+    android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());
+  }
+
+  int rc = setresgid(gid, gid, gid);
+  if (rc == -1) {
+    fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
+  }
+
+  // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
+  // uid from 0, which clears capabilities.  The other alternative is to call
+  // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
+  // b/71859146).  As the result, privileged syscalls used below still need to be accessible in
+  // app process.
+  SetUpSeccompFilter(uid);
+
+  rc = setresuid(uid, uid, uid);
+  if (rc == -1) {
+    fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
+  }
+
+  // The "dumpable" flag of a process, which controls core dump generation, is
+  // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
+  // user or group ID changes. See proc(5) for possible values. In most cases,
+  // the value is 0, so core dumps are disabled for zygote children. However,
+  // when running in a Chrome OS container, the value is already set to 2,
+  // which allows the external crash reporter to collect all core dumps. Since
+  // only system crashes are interested, core dump is disabled for app
+  // processes. This also ensures compliance with CTS.
+  int dumpable = prctl(PR_GET_DUMPABLE);
+  if (dumpable == -1) {
+      ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
+      RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
+  }
+  if (dumpable == 2 && uid >= AID_APP) {
+    if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
+      ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
+      RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
+    }
+  }
+
+  if (NeedsNoRandomizeWorkaround()) {
+      // Work around ARM kernel ASLR lossage (http://b/5817320).
+      int old_personality = personality(0xffffffff);
+      int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
+      if (new_personality == -1) {
+          ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
+      }
+  }
+
+  if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
+                       &error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  if (!SetSchedulerPolicy(&error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  const char* se_info_c_str = NULL;
+  ScopedUtfChars* se_info = NULL;
+  if (java_se_info != NULL) {
+      se_info = new ScopedUtfChars(env, java_se_info);
+      se_info_c_str = se_info->c_str();
+      if (se_info_c_str == NULL) {
+        fail_fn("se_info_c_str == NULL");
+      }
+  }
+  const char* se_name_c_str = NULL;
+  ScopedUtfChars* se_name = NULL;
+  if (java_se_name != NULL) {
+      se_name = new ScopedUtfChars(env, java_se_name);
+      se_name_c_str = se_name->c_str();
+      if (se_name_c_str == NULL) {
+        fail_fn("se_name_c_str == NULL");
+      }
+  }
+  rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
+  if (rc == -1) {
+    fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
+          is_system_server, se_info_c_str, se_name_c_str));
+  }
+
+  // Make it easier to debug audit logs by setting the main thread's name to the
+  // nice name rather than "app_process".
+  if (se_name_c_str == NULL && is_system_server) {
+    se_name_c_str = "system_server";
+  }
+  if (se_name_c_str != NULL) {
+    SetThreadName(se_name_c_str);
+  }
+
+  delete se_info;
+  delete se_name;
+
+  // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
+  UnsetChldSignalHandler();
+
+  env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
+                            is_system_server, is_child_zygote, instructionSet);
+  if (env->ExceptionCheck()) {
+    fail_fn("Error calling post fork hooks.");
+  }
+}
+
 // Utility routine to fork zygote and specialize the child process.
-static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
-                                     jint runtime_flags, jobjectArray javaRlimits,
-                                     jlong permittedCapabilities, jlong effectiveCapabilities,
-                                     jint mount_external,
-                                     jstring java_se_info, jstring java_se_name,
-                                     bool is_system_server, jintArray fdsToClose,
-                                     jintArray fdsToIgnore, bool is_child_zygote,
-                                     jstring instructionSet, jstring dataDir) {
+static pid_t ForkCommon(JNIEnv* env, jstring java_se_name, bool is_system_server,
+                        jintArray fdsToClose, jintArray fdsToIgnore) {
   SetSignalHandlers();
 
+  // Block SIGCHLD prior to fork.
   sigset_t sigchld;
   sigemptyset(&sigchld);
   sigaddset(&sigchld, SIGCHLD);
@@ -599,9 +846,12 @@
     fail_fn(error_msg);
   }
 
+  android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level();
+
   pid_t pid = fork();
 
   if (pid == 0) {
+    // The child process.
     PreApplicationInit();
 
     // Clean up any descriptors which must be closed immediately
@@ -615,186 +865,13 @@
       fail_fn(error_msg);
     }
 
-    if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
-      fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
-    }
+    // Turn fdsan back on.
+    android_fdsan_set_error_level(fdsan_error_level);
+  }
 
-    // Keep capabilities across UID change, unless we're staying root.
-    if (uid != 0) {
-      if (!EnableKeepCapabilities(&error_msg)) {
-        fail_fn(error_msg);
-      }
-    }
-
-    if (!SetInheritable(permittedCapabilities, &error_msg)) {
-      fail_fn(error_msg);
-    }
-    if (!DropCapabilitiesBoundingSet(&error_msg)) {
-      fail_fn(error_msg);
-    }
-
-    bool use_native_bridge = !is_system_server && (instructionSet != NULL)
-        && android::NativeBridgeAvailable();
-    if (use_native_bridge) {
-      ScopedUtfChars isa_string(env, instructionSet);
-      use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());
-    }
-    if (use_native_bridge && dataDir == NULL) {
-      // dataDir should never be null if we need to use a native bridge.
-      // In general, dataDir will never be null for normal applications. It can only happen in
-      // special cases (for isolated processes which are not associated with any app). These are
-      // launched by the framework and should not be emulated anyway.
-      use_native_bridge = false;
-      ALOGW("Native bridge will not be used because dataDir == NULL.");
-    }
-
-    if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
-      ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
-      if (errno == ENOTCONN || errno == EROFS) {
-        // When device is actively encrypting, we get ENOTCONN here
-        // since FUSE was mounted before the framework restarted.
-        // When encrypted device is booting, we get EROFS since
-        // FUSE hasn't been created yet by init.
-        // In either case, continue without external storage.
-      } else {
-        fail_fn(error_msg);
-      }
-    }
-
-    // If this zygote isn't root, it won't be able to create a process group,
-    // since the directory is owned by root.
-    if (!is_system_server && getuid() == 0) {
-        int rc = createProcessGroup(uid, getpid());
-        if (rc != 0) {
-            if (rc == -EROFS) {
-                ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
-            } else {
-                ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));
-            }
-        }
-    }
-
-    std::string error_msg;
-    if (!SetGids(env, javaGids, &error_msg)) {
-      fail_fn(error_msg);
-    }
-
-    if (!SetRLimits(env, javaRlimits, &error_msg)) {
-      fail_fn(error_msg);
-    }
-
-    if (use_native_bridge) {
-      ScopedUtfChars isa_string(env, instructionSet);
-      ScopedUtfChars data_dir(env, dataDir);
-      android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());
-    }
-
-    int rc = setresgid(gid, gid, gid);
-    if (rc == -1) {
-      fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
-    }
-
-    // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
-    // uid from 0, which clears capabilities.  The other alternative is to call
-    // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
-    // b/71859146).  As the result, privileged syscalls used below still need to be accessible in
-    // app process.
-    SetUpSeccompFilter(uid);
-
-    rc = setresuid(uid, uid, uid);
-    if (rc == -1) {
-      fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
-    }
-
-    // The "dumpable" flag of a process, which controls core dump generation, is
-    // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
-    // user or group ID changes. See proc(5) for possible values. In most cases,
-    // the value is 0, so core dumps are disabled for zygote children. However,
-    // when running in a Chrome OS container, the value is already set to 2,
-    // which allows the external crash reporter to collect all core dumps. Since
-    // only system crashes are interested, core dump is disabled for app
-    // processes. This also ensures compliance with CTS.
-    int dumpable = prctl(PR_GET_DUMPABLE);
-    if (dumpable == -1) {
-        ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
-        RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
-    }
-    if (dumpable == 2 && uid >= AID_APP) {
-      if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
-        ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
-        RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
-      }
-    }
-
-    if (NeedsNoRandomizeWorkaround()) {
-        // Work around ARM kernel ASLR lossage (http://b/5817320).
-        int old_personality = personality(0xffffffff);
-        int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
-        if (new_personality == -1) {
-            ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
-        }
-    }
-
-    if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
-                         &error_msg)) {
-      fail_fn(error_msg);
-    }
-
-    if (!SetSchedulerPolicy(&error_msg)) {
-      fail_fn(error_msg);
-    }
-
-    const char* se_info_c_str = NULL;
-    ScopedUtfChars* se_info = NULL;
-    if (java_se_info != NULL) {
-        se_info = new ScopedUtfChars(env, java_se_info);
-        se_info_c_str = se_info->c_str();
-        if (se_info_c_str == NULL) {
-          fail_fn("se_info_c_str == NULL");
-        }
-    }
-    const char* se_name_c_str = NULL;
-    ScopedUtfChars* se_name = NULL;
-    if (java_se_name != NULL) {
-        se_name = new ScopedUtfChars(env, java_se_name);
-        se_name_c_str = se_name->c_str();
-        if (se_name_c_str == NULL) {
-          fail_fn("se_name_c_str == NULL");
-        }
-    }
-    rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
-    if (rc == -1) {
-      fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
-            is_system_server, se_info_c_str, se_name_c_str));
-    }
-
-    // Make it easier to debug audit logs by setting the main thread's name to the
-    // nice name rather than "app_process".
-    if (se_name_c_str == NULL && is_system_server) {
-      se_name_c_str = "system_server";
-    }
-    if (se_name_c_str != NULL) {
-      SetThreadName(se_name_c_str);
-    }
-
-    delete se_info;
-    delete se_name;
-
-    // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
-    UnsetChldSignalHandler();
-
-    env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
-                              is_system_server, is_child_zygote, instructionSet);
-    if (env->ExceptionCheck()) {
-      fail_fn("Error calling post fork hooks.");
-    }
-  } else if (pid > 0) {
-    // the parent process
-
-    // We blocked SIGCHLD prior to a fork, we unblock it here.
-    if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
-      fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
-    }
+  // We blocked SIGCHLD prior to a fork, we unblock it here.
+  if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
+    fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
   }
   return pid;
 }
@@ -833,7 +910,7 @@
         jint runtime_flags, jobjectArray rlimits,
         jint mount_external, jstring se_info, jstring se_name,
         jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote,
-        jstring instructionSet, jstring appDataDir) {
+        jstring instructionSet, jstring appDataDir, jstring packageName) {
     jlong capabilities = 0;
 
     // Grant CAP_WAKE_ALARM to the Bluetooth process.
@@ -881,22 +958,27 @@
     // available.
     capabilities &= GetEffectiveCapabilityMask(env);
 
-    return ForkAndSpecializeCommon(env, uid, gid, gids, runtime_flags,
-            rlimits, capabilities, capabilities, mount_external, se_info,
-            se_name, false, fdsToClose, fdsToIgnore, is_child_zygote == JNI_TRUE,
-            instructionSet, appDataDir);
+    pid_t pid = ForkCommon(env, se_name, false, fdsToClose, fdsToIgnore);
+    if (pid == 0) {
+      SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
+                       capabilities, capabilities,
+                       mount_external, se_info, se_name, false,
+                       is_child_zygote == JNI_TRUE, instructionSet, appDataDir, packageName);
+    }
+    return pid;
 }
 
 static jint com_android_internal_os_Zygote_nativeForkSystemServer(
         JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
         jint runtime_flags, jobjectArray rlimits, jlong permittedCapabilities,
         jlong effectiveCapabilities) {
-  pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
-                                      runtime_flags, rlimits,
-                                      permittedCapabilities, effectiveCapabilities,
-                                      MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
-                                      NULL, false, NULL, NULL);
-  if (pid > 0) {
+  pid_t pid = ForkCommon(env, NULL, true, NULL, NULL);
+  if (pid == 0) {
+      SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
+                       permittedCapabilities, effectiveCapabilities,
+                       MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true,
+                       false, NULL, NULL, nullptr);
+  } else if (pid > 0) {
       // The zygote process checks whether the child process has died or not.
       ALOGI("System server process %d has been created", pid);
       gSystemServerPid = pid;
@@ -909,12 +991,16 @@
           RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
       }
 
-      // Assign system_server to the correct memory cgroup.
-      // Not all devices mount /dev/memcg so check for the file first
-      // to avoid unnecessarily printing errors and denials in the logs.
-      if (!access("/dev/memcg/system/tasks", F_OK) &&
+      bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
+      bool per_app_memcg = GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
+      if (per_app_memcg) {
+          // Assign system_server to the correct memory cgroup.
+          // Not all devices mount /dev/memcg so check for the file first
+          // to avoid unnecessarily printing errors and denials in the logs.
+          if (!access("/dev/memcg/system/tasks", F_OK) &&
                 !WriteStringToFile(StringPrintf("%d", pid), "/dev/memcg/system/tasks")) {
-        ALOGE("couldn't write %d to /dev/memcg/system/tasks", pid);
+              ALOGE("couldn't write %d to /dev/memcg/system/tasks", pid);
+          }
       }
   }
   return pid;
@@ -972,7 +1058,7 @@
     { "nativeSecurityInit", "()V",
       (void *) com_android_internal_os_Zygote_nativeSecurityInit },
     { "nativeForkAndSpecialize",
-      "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
+      "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
       (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
     { "nativeForkSystemServer", "(II[II[[IJJ)I",
       (void *) com_android_internal_os_Zygote_nativeForkSystemServer },
diff --git a/core/proto/android/app/notificationmanager.proto b/core/proto/android/app/notificationmanager.proto
index e991688..183f9d3 100644
--- a/core/proto/android/app/notificationmanager.proto
+++ b/core/proto/android/app/notificationmanager.proto
@@ -42,9 +42,10 @@
         REPEAT_CALLERS = 5;
         // Alarms are prioritized.
         ALARMS = 6;
-        // Media, system, game (catch-all for non-never suppressible sounds) are
-        // prioritized.
-        MEDIA_SYSTEM_OTHER = 7;
+        // Media, game, voice navigation are prioritized.
+        MEDIA = 7;
+        // System (catch-all for non-never suppressible sounds) are prioritized.
+        SYSTEM = 8;
     }
     repeated Category priority_categories = 1;
 
@@ -64,10 +65,32 @@
         // Whether notifications suppressed by DND should not interrupt visually
         // (e.g. with notification lights or by turning the screen on) when the
         // screen is off.
-        SCREEN_OFF = 1;
+        // FULL_SCREEN_INTENT, AMBIENT, and LIGHTS should be used instead.
+        SCREEN_OFF = 1 [deprecated = true];
         // Whether notifications suppressed by DND should not interrupt visually
         // when the screen is on (e.g. by peeking onto the screen).
-        SCREEN_ON = 2;
+        // PEEK should be used instead of ON.
+        SCREEN_ON = 2 [deprecated = true];
+        // Whether full screen intents} from notifications intercepted by DND
+        // are blocked.
+        FULL_SCREEN_INTENT = 3;
+        // Whether lights from notifications intercepted by DND are blocked.
+        LIGHTS = 4;
+        // Whether notifications intercepted by DND are prevented from peeking.
+        PEEK = 5;
+        // Whether notifications intercepted by DND are prevented from
+        // appearing in the status bar, on devices that support status bars.
+        STATUS_BAR = 6;
+        // Whether badges from notifications intercepted by DND are blocked on
+        // devices that support badging.
+        BADGE = 7;
+        // Whether notification intercepted by DND are prevented from appearing
+        // on ambient displays on devices that support ambient display.
+        AMBIENT = 8;
+        // Whether notification intercepted by DND are prevented from appearing
+        // in notification list views like the notification shade or lockscreen
+        // on devices that support those views.
+        NOTIFICATION_LIST = 9;
     }
     repeated SuppressedVisualEffect suppressed_visual_effects = 4;
 }
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 3aea3a7..5f6b6cc 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -71,6 +71,12 @@
         (section).args = "getprop"
     ];
 
+    optional string kernel_version = 1002 [
+        (section).type = SECTION_FILE,
+        (section).args = "/proc/version",
+        (privacy).dest = DEST_AUTOMATIC
+    ];
+
     // Device Logs
     optional android.util.EventLogTagMapProto event_log_tag_map = 1100 [
         (section).type = SECTION_FILE,
@@ -162,7 +168,6 @@
 
     optional CpuFreqProto cpu_freq = 2004 [
         (section).type = SECTION_FILE,
-        (section).device_specific = true,
         (section).args = "/sys/devices/system/cpu/cpufreq/all_time_in_state"
     ];
 
@@ -173,7 +178,6 @@
 
     optional BatteryTypeProto battery_type = 2006 [
         (section).type = SECTION_FILE,
-        (section).device_specific = true,
         (section).args = "/sys/class/power_supply/bms/battery_type"
     ];
 
@@ -184,7 +188,7 @@
     ];
 
     // System Services
-    optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
+    optional com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
         (section).type = SECTION_DUMPSYS,
         (section).args = "fingerprint --proto --incident"
     ];
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 04e5658..040e36a 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -97,7 +97,18 @@
     }
     optional Auto auto = 16;
 
-    optional SettingProto autofill_compat_mode_allowed_packages = 17 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    reserved 17; // Used to be autofill_compat_mode_allowed_packages
+
+    message Autofill {
+      option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+      optional SettingProto compat_mode_allowed_packages = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+      optional SettingProto logging_level = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+      optional SettingProto max_partitions_size = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+      optional SettingProto max_visible_datasets = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Autofill autofill = 140;
+
     optional SettingProto backup_agent_timeout_parameters = 18;
 
     message Battery {
@@ -734,7 +745,7 @@
 
         optional SettingProto car_dock = 1;
         optional SettingProto car_undock = 2;
-        optional SettingProto charging_sounds_enabled = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        reserved 3; // Moved to secure settings Sound.charging_sounds_enabled
         optional SettingProto charging_started = 4;
         optional SettingProto desk_dock = 5;
         optional SettingProto desk_undock = 6;
@@ -930,12 +941,8 @@
         optional SettingProto mode = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto mode_ringer_level = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto mode_config_etag = 3;
-        // If 0, turning on dnd manually will last indefinitely. Else if
-        // non-negative, turning on dnd manually will last for this many minutes.
-        // Else (if negative), turning on dnd manually will surface a dialog that
-        // prompts user to specify a duration.
-        optional SettingProto duration = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        optional SettingProto show_zen_upgrade_notification = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        reserved 4; // Moved to secure settings Zen.duration
+        reserved 5; // Moved to secure settings Zen.show_zen_upgrade_notification
     }
     optional Zen zen = 138;
 
@@ -943,5 +950,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 140;
+    // Next tag = 141;
 }
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 237efd8..f2e8c70 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -300,6 +300,7 @@
         optional SettingProto enabled_policy_access_packages = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto badging = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto show_note_about_notification_hiding = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto in_call_notification_enabled = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Notification notification = 41;
 
@@ -404,6 +405,15 @@
     optional SettingProto skip_first_use_hints = 52 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto sleep_timeout = 53 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto sms_default_application = 54 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Sounds {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto charging_sounds_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto charging_vibration_enabled = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Sounds sounds = 72;
+
     // Defines whether managed profile ringtones should be synced from its
     // parent profile.
     optional SettingProto sync_parent_sounds = 55 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -484,7 +494,22 @@
 
     optional SettingProto wake_gesture_enabled = 68 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
+    message Zen {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // If 0, turning on dnd manually will last indefinitely. Else if
+        // non-negative, turning on dnd manually will last for this many minutes.
+        // Else (if negative), turning on dnd manually will surface a dialog that
+        // prompts user to specify a duration.
+        optional SettingProto duration = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto show_zen_upgrade_notification = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto show_zen_settings_suggestion = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto settings_updated = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto settings_suggestion_viewed = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Zen zen = 71;
+
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 71;
+    // Next tag = 73;
 }
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 9d24588..52c76cc 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -62,11 +62,14 @@
     optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1;
     repeated ActivityDisplayProto displays = 2;
     optional KeyguardControllerProto keyguard_controller = 3;
+    // TODO(b/111541062): Focused stack and resumed activity are now per-display. Topmost instances
+    // can be obtained from top display and these fields can be removed.
     optional int32 focused_stack_id = 4;
     optional .com.android.server.wm.IdentifierProto resumed_activity = 5;
     // Whether or not the home activity is the recents activity. This is needed for the CTS tests to
     // know what activity types to check for when invoking splitscreen multi-window.
     optional bool is_home_recents_component = 6;
+    repeated .com.android.server.wm.IdentifierProto pending_activities = 7;
 }
 
 /* represents ActivityStackSupervisor.ActivityDisplay */
@@ -76,6 +79,8 @@
     optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1;
     optional int32 id = 2;
     repeated ActivityStackProto stacks = 3;
+    optional int32 focused_stack_id = 4;
+    optional .com.android.server.wm.IdentifierProto resumed_activity = 5;
 }
 
 message ActivityStackProto {
diff --git a/core/proto/android/server/face.proto b/core/proto/android/server/face.proto
new file mode 100644
index 0000000..6ecf328
--- /dev/null
+++ b/core/proto/android/server/face.proto
@@ -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.
+ */
+
+syntax = "proto2";
+package com.android.server.biometrics.face;
+
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+option java_outer_classname = "FaceServiceProto";
+
+message FaceServiceDumpProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Each log may include multiple user_id for different users.
+    repeated FaceUserStatsProto users = 1;
+}
+
+message FaceUserStatsProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Refer to the UserHandle documentation.
+    optional int32 user_id = 1;
+
+    // The number of faces registered to this user.
+    optional int32 num_faces = 2;
+
+    // Normal face authentications stats (e.g. lockscreen).
+    optional FaceActionStatsProto normal = 3;
+
+    // Crypto authentications (e.g. to unlock password storage, make secure
+    // purchases, etc).
+    optional FaceActionStatsProto crypto = 4;
+}
+
+message FaceActionStatsProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Number of accepted faces.
+    optional int32 accept = 1;
+
+    // Number of rejected faces.
+    optional int32 reject = 2;
+
+    // Total number of acquisitions. Should be >= accept+reject due to poor
+    // image acquisition in some cases (too high, too low, poor gaze, etc.)
+    optional int32 acquire = 3;
+
+    // Total number of lockouts.
+    optional int32 lockout = 4;
+
+    // Total number of permanent lockouts.
+    optional int32 lockout_permanent = 5;
+}
diff --git a/core/proto/android/server/fingerprint.proto b/core/proto/android/server/fingerprint.proto
index 2a7fbc3..c5eb85c 100644
--- a/core/proto/android/server/fingerprint.proto
+++ b/core/proto/android/server/fingerprint.proto
@@ -15,7 +15,7 @@
  */
 
 syntax = "proto2";
-package com.android.server.fingerprint;
+package com.android.server.biometrics.fingerprint;
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
@@ -46,7 +46,7 @@
     optional PerformanceStatsProto crypto = 4;
 }
 
-// A com.android.server.fingerprint.FingerpintService.PerformanceStats object.
+// A com.android.server.biometrics.fingerprint.FingerpintService.PerformanceStats object.
 message PerformanceStatsProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 7467d8f..3db7f9d 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -24,6 +24,7 @@
 import "frameworks/base/core/proto/android/server/surfaceanimator.proto";
 import "frameworks/base/core/proto/android/view/displaycutout.proto";
 import "frameworks/base/core/proto/android/view/displayinfo.proto";
+import "frameworks/base/core/proto/android/view/enums.proto";
 import "frameworks/base/core/proto/android/view/surface.proto";
 import "frameworks/base/core/proto/android/view/windowlayoutparams.proto";
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
@@ -134,32 +135,8 @@
         APP_STATE_TIMEOUT = 3;
     }
     optional AppState app_transition_state = 1;
-    /* definitions for constants found in {@link com.android.server.wm.AppTransition} */
-    enum TransitionType {
-        TRANSIT_NONE = 0;
-        TRANSIT_UNSET = -1;
-        TRANSIT_ACTIVITY_OPEN = 6;
-        TRANSIT_ACTIVITY_CLOSE = 7;
-        TRANSIT_TASK_OPEN = 8;
-        TRANSIT_TASK_CLOSE = 9;
-        TRANSIT_TASK_TO_FRONT = 10;
-        TRANSIT_TASK_TO_BACK = 11;
-        TRANSIT_WALLPAPER_CLOSE = 12;
-        TRANSIT_WALLPAPER_OPEN = 13;
-        TRANSIT_WALLPAPER_INTRA_OPEN = 14;
-        TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
-        TRANSIT_TASK_OPEN_BEHIND = 16;
-        TRANSIT_TASK_IN_PLACE = 17;
-        TRANSIT_ACTIVITY_RELAUNCH = 18;
-        TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
-        TRANSIT_KEYGUARD_GOING_AWAY = 20;
-        TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
-        TRANSIT_KEYGUARD_OCCLUDE = 22;
-        TRANSIT_KEYGUARD_UNOCCLUDE = 23;
-        TRANSIT_TRANSLUCENT_ACTIVITY_OPEN = 24;
-        TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE = 25;
-    }
-    optional TransitionType last_used_app_transition = 2;
+
+    optional .android.view.TransitionTypeEnum last_used_app_transition = 2;
 }
 
 /* represents DisplayContent */
@@ -288,10 +265,10 @@
     optional int32 stack_id = 4;
     optional .android.view.WindowLayoutParamsProto attributes = 5;
     optional .android.graphics.RectProto given_content_insets = 6;
-    optional .android.graphics.RectProto frame = 7;
-    optional .android.graphics.RectProto containing_frame = 8;
-    optional .android.graphics.RectProto parent_frame = 9;
-    optional .android.graphics.RectProto content_frame = 10;
+    optional .android.graphics.RectProto frame = 7 [deprecated=true];
+    optional .android.graphics.RectProto containing_frame = 8 [deprecated=true];
+    optional .android.graphics.RectProto parent_frame = 9 [deprecated=true];
+    optional .android.graphics.RectProto content_frame = 10 [deprecated=true];
     optional .android.graphics.RectProto content_insets = 11;
     optional .android.graphics.RectProto surface_insets = 12;
     optional WindowStateAnimatorProto animator = 13;
@@ -304,21 +281,24 @@
     optional int32 system_ui_visibility = 21;
     optional bool has_surface = 22;
     optional bool is_ready_for_display = 23;
-    optional .android.graphics.RectProto display_frame = 24;
-    optional .android.graphics.RectProto overscan_frame = 25;
-    optional .android.graphics.RectProto visible_frame = 26;
-    optional .android.graphics.RectProto decor_frame = 27;
-    optional .android.graphics.RectProto outset_frame = 28;
+    optional .android.graphics.RectProto display_frame = 24 [deprecated=true];
+    optional .android.graphics.RectProto overscan_frame = 25 [deprecated=true];
+    optional .android.graphics.RectProto visible_frame = 26 [deprecated=true];
+    optional .android.graphics.RectProto decor_frame = 27 [deprecated=true];
+    optional .android.graphics.RectProto outset_frame = 28 [deprecated=true];
     optional .android.graphics.RectProto overscan_insets = 29;
     optional .android.graphics.RectProto visible_insets = 30;
     optional .android.graphics.RectProto stable_insets = 31;
     optional .android.graphics.RectProto outsets = 32;
-    optional .android.view.DisplayCutoutProto cutout = 33;
+    optional .android.view.DisplayCutoutProto cutout = 33 [deprecated=true];
     optional bool remove_on_exit = 34;
     optional bool destroying = 35;
     optional bool removed = 36;
     optional bool is_on_screen = 37;
     optional bool is_visible = 38;
+    optional bool pending_forced_seamless_rotation = 39;
+    optional int64 finished_forced_seamless_rotation_frame = 40;
+    optional WindowFramesProto window_frames = 41;
 }
 
 message IdentifierProto {
@@ -380,3 +360,19 @@
     optional .android.content.ConfigurationProto full_configuration = 2;
     optional .android.content.ConfigurationProto merged_override_configuration = 3;
 }
+
+/* represents WindowFrames */
+message WindowFramesProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional .android.graphics.RectProto containing_frame = 1;
+    optional .android.graphics.RectProto content_frame = 2;
+    optional .android.graphics.RectProto decor_frame = 3;
+    optional .android.graphics.RectProto display_frame = 4;
+    optional .android.graphics.RectProto frame = 5;
+    optional .android.graphics.RectProto outset_frame = 6;
+    optional .android.graphics.RectProto overscan_frame = 7;
+    optional .android.graphics.RectProto parent_frame = 8;
+    optional .android.graphics.RectProto visible_frame = 9;
+    optional .android.view.DisplayCutoutProto cutout = 10;
+}
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index 15ede0c..b2a88b7 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -174,4 +174,8 @@
         optional android.util.AggStats rss = 8;
     }
     repeated State states = 5;
+
+    // Total time process has been running...  screen_state, memory_state, and process_state
+    // will not be set.
+    optional State total_running_state = 6;
 }
diff --git a/core/proto/android/view/enums.proto b/core/proto/android/view/enums.proto
index 10785ce..0172e78 100644
--- a/core/proto/android/view/enums.proto
+++ b/core/proto/android/view/enums.proto
@@ -42,3 +42,30 @@
     // CPU is not updating it.
     DISPLAY_STATE_ON_SUSPEND = 6;
 }
+
+// Constants found in android.view.WindowManager.
+enum TransitionTypeEnum {
+    TRANSIT_NONE = 0;
+    TRANSIT_UNSET = -1;
+    TRANSIT_ACTIVITY_OPEN = 6;
+    TRANSIT_ACTIVITY_CLOSE = 7;
+    TRANSIT_TASK_OPEN = 8;
+    TRANSIT_TASK_CLOSE = 9;
+    TRANSIT_TASK_TO_FRONT = 10;
+    TRANSIT_TASK_TO_BACK = 11;
+    TRANSIT_WALLPAPER_CLOSE = 12;
+    TRANSIT_WALLPAPER_OPEN = 13;
+    TRANSIT_WALLPAPER_INTRA_OPEN = 14;
+    TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
+    TRANSIT_TASK_OPEN_BEHIND = 16;
+    TRANSIT_TASK_IN_PLACE = 17;
+    TRANSIT_ACTIVITY_RELAUNCH = 18;
+    TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
+    TRANSIT_KEYGUARD_GOING_AWAY = 20;
+    TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
+    TRANSIT_KEYGUARD_OCCLUDE = 22;
+    TRANSIT_KEYGUARD_UNOCCLUDE = 23;
+    TRANSIT_TRANSLUCENT_ACTIVITY_OPEN = 24;
+    TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE = 25;
+    TRANSIT_CRASHING_ACTIVITY_CLOSE = 26;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e33ecb8..f9d81ba 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -262,6 +262,7 @@
 
     <protected-broadcast android:name="android.intent.action.HEADSET_PLUG" />
     <protected-broadcast android:name="android.media.action.HDMI_AUDIO_PLUG" />
+    <protected-broadcast android:name="android.media.action.MICROPHONE_MUTE_CHANGED" />
 
     <protected-broadcast android:name="android.media.AUDIO_BECOMING_NOISY" />
     <protected-broadcast android:name="android.media.RINGER_MODE_CHANGED" />
@@ -598,11 +599,21 @@
     <protected-broadcast android:name="android.app.action.AFFILIATED_PROFILE_TRANSFER_OWNERSHIP_COMPLETE" />
     <protected-broadcast android:name="android.app.action.DATA_SHARING_RESTRICTION_CHANGED" />
     <protected-broadcast android:name="android.app.action.STATSD_STARTED" />
+    <protected-broadcast android:name="com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET" />
+    <protected-broadcast android:name="com.android.server.biometrics.face.ACTION_LOCKOUT_RESET" />
 
     <!-- For IdleController -->
     <protected-broadcast android:name="android.intent.action.DOCK_IDLE" />
     <protected-broadcast android:name="android.intent.action.DOCK_ACTIVE" />
 
+    <!-- Added in Q -->
+
+    <!-- For CarIdlenessTracker -->
+    <protected-broadcast android:name="com.android.server.jobscheduler.GARAGE_MODE_ON" />
+    <protected-broadcast android:name="com.android.server.jobscheduler.GARAGE_MODE_OFF" />
+    <protected-broadcast android:name="com.android.server.jobscheduler.FORCE_IDLE" />
+    <protected-broadcast android:name="com.android.server.jobscheduler.UNFORCE_IDLE" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
@@ -821,6 +832,9 @@
         android:label="@string/permgrouplab_location"
         android:description="@string/permgroupdesc_location"
         android:request="@string/permgrouprequest_location"
+        android:requestDetail="@string/permgrouprequestdetail_location"
+        android:backgroundRequest="@string/permgroupbackgroundrequest_location"
+        android:backgroundRequestDetail="@string/permgroupbackgroundrequestdetail_location"
         android:priority="400" />
 
     <!-- Allows an app to access precise location.
@@ -831,6 +845,7 @@
         android:permissionGroup="android.permission-group.LOCATION"
         android:label="@string/permlab_accessFineLocation"
         android:description="@string/permdesc_accessFineLocation"
+        android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
         android:protectionLevel="dangerous|instant" />
 
     <!-- Allows an app to access approximate location.
@@ -841,6 +856,7 @@
         android:permissionGroup="android.permission-group.LOCATION"
         android:label="@string/permlab_accessCoarseLocation"
         android:description="@string/permdesc_accessCoarseLocation"
+        android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
         android:protectionLevel="dangerous|instant" />
 
     <!-- Allows an app to access location in the background.  If you
@@ -1481,9 +1497,9 @@
         android:protectionLevel="signature|privileged" />
 
     <!-- @hide Allows internal management of Wi-Fi connectivity state when on
-         permission review mode.
+         wireless consent mode.
          <p>Not for use by third-party applications. -->
-    <permission android:name="android.permission.MANAGE_WIFI_WHEN_PERMISSION_REVIEW_REQUIRED"
+    <permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED"
         android:protectionLevel="signature" />
 
     <!-- #SystemApi @hide Allows an app to bypass Private DNS.
@@ -1582,9 +1598,9 @@
     <permission android:name="android.permission.NFC_HANDOVER_STATUS"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @hide Allows internal management of Bluetooth state when on permission review mode.
+    <!-- @hide Allows internal management of Bluetooth state when on wireless consent mode.
          <p>Not for use by third-party applications. -->
-    <permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED"
+    <permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED"
         android:protectionLevel="signature" />
 
     <!-- ================================== -->
@@ -2251,7 +2267,8 @@
         android:description="@string/permdesc_install_shortcut"
         android:protectionLevel="normal"/>
 
-    <!--This permission is no longer supported.
+    <!-- <p class="caution"><strong>Don't use this permission in your app.</strong><br>This
+         permission is no longer supported.
     -->
     <permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
         android:label="@string/permlab_uninstall_shortcut"
@@ -2428,7 +2445,8 @@
     <permission android:name="android.permission.ASEC_RENAME"
         android:protectionLevel="signature" />
 
-    <!-- @SystemApi Allows applications to write the apn settings.
+    <!-- @SystemApi Allows applications to write the apn settings and read sensitive fields of
+         an existing apn settings like user and password.
     <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.WRITE_APN_SETTINGS"
         android:protectionLevel="signature|privileged" />
@@ -3037,6 +3055,15 @@
     <permission android:name="android.permission.INSTALL_PACKAGE_UPDATES"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to install existing system packages. This is a limited
+         version of {@link android.Manifest.permission#INSTALL_PACKAGES}.
+         <p>Not for use by third-party applications.
+         TODO(b/80204953): remove this permission once we have a long-term solution.
+         @hide
+    -->
+    <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows an application to clear user data.
          <p>Not for use by third-party applications
          @hide
@@ -3229,6 +3256,13 @@
     <permission android:name="android.permission.MODIFY_AUDIO_ROUTING"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to modify what effects are applied to all audio
+         (matching certain criteria) from any application.
+         <p>Not for use by third-party applications.</p>
+         @hide -->
+    <permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows an application to capture video output.
          <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
@@ -3481,6 +3515,10 @@
     <permission android:name="android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE"
                 android:protectionLevel="signature" />
 
+    <!-- @hide Internal permission to allows an application to access card content provider. -->
+    <permission android:name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"
+                android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows applications to set a live wallpaper.
          @hide XXX Change to signature once the picker is moved to its
          own apk as Ghod Intended. -->
@@ -3629,6 +3667,14 @@
     <permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
         android:protectionLevel="signature" />
 
+    <!-- Allows managing (adding, removing) facial templates. Reserved for the system. @hide -->
+    <permission android:name="android.permission.MANAGE_FACE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an app to reset face authentication attempt counter. Reserved for the system. @hide -->
+    <permission android:name="android.permission.RESET_FACE_LOCKOUT"
+        android:protectionLevel="signature" />
+
     <!-- Allows an application to control keyguard.  Only allowed for system processes.
         @hide -->
     <permission android:name="android.permission.CONTROL_KEYGUARD"
@@ -3989,6 +4035,11 @@
     <permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS"
                 android:protectionLevel="signature" />
 
+    <!-- Allows an application to read emergency info name.
+         @hide <p>Not for use by third-party applications. -->
+    <permission android:name="com.android.emergency.permission.READ_EMERGENCY_INFO_NAME"
+                android:protectionLevel="signature" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/anim/lock_screen_behind_enter_fade_in.xml b/core/res/res/anim/lock_screen_behind_enter_fade_in.xml
index e9475f5..ff95aea 100644
--- a/core/res/res/anim/lock_screen_behind_enter_fade_in.xml
+++ b/core/res/res/anim/lock_screen_behind_enter_fade_in.xml
@@ -16,7 +16,6 @@
   -->
 
 <alpha xmlns:android="http://schemas.android.com/apk/res/android"
-    android:background="#ff000000"
     android:detachWallpaper="true"
     android:shareInterpolator="false"
     android:interpolator="@interpolator/linear"
diff --git a/core/res/res/drawable-hdpi/ic_grayedout_printer.png b/core/res/res/drawable-hdpi/ic_grayedout_printer.png
deleted file mode 100644
index 5e54970..0000000
--- a/core/res/res/drawable-hdpi/ic_grayedout_printer.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_grayedout_printer.png b/core/res/res/drawable-mdpi/ic_grayedout_printer.png
deleted file mode 100644
index 5e54970..0000000
--- a/core/res/res/drawable-mdpi/ic_grayedout_printer.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_grayedout_printer.png b/core/res/res/drawable-xhdpi/ic_grayedout_printer.png
deleted file mode 100644
index 5e54970..0000000
--- a/core/res/res/drawable-xhdpi/ic_grayedout_printer.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/emergency_icon.xml b/core/res/res/drawable/emergency_icon.xml
index b2ffa2b..c142be3 100644
--- a/core/res/res/drawable/emergency_icon.xml
+++ b/core/res/res/drawable/emergency_icon.xml
@@ -18,7 +18,7 @@
         android:height="24.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0"
-        android:tint="?attr/colorControlNormal">
+        android:tint="?attr/colorError">
     <path
         android:fillColor="#FF000000"
         android:pathData="M6.8,17.3C5.3,15.9 4.5,14.0 4.5,12.0c0.0,-2.0 0.8,-3.8 2.1,-5.2l1.4,1.4c-1.0,1.0 -1.6,2.4 -1.6,3.8c0.0,1.5 0.6,2.9 1.6,3.9L6.8,17.3z"/>
diff --git a/core/res/res/drawable/ic_faster_emergency.xml b/core/res/res/drawable/ic_faster_emergency.xml
new file mode 100644
index 0000000..680dfce
--- /dev/null
+++ b/core/res/res/drawable/ic_faster_emergency.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.
+-->
+<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"
+        android:tint="?attr/colorError">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5C3.9,3,3.01,3.9,3.01,5L3,19c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M19,19L5,19V5h14V19z" />
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M 10.5 17 L 13.5 17 L 13.5 13.5 L 17 13.5 L 17 10.5 L 13.5 10.5 L 13.5 7 L 10.5 7 L 10.5 10.5 L 7 10.5 L 7 13.5 L 10.5 13.5 Z" />
+    <path
+        android:pathData="M0,0h24v24H0V0z" />
+
+</vector>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 4808bba..73db3a1 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Vliegtuigmodus"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Vliegtuigmodus is AAN"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Vliegtuigmodus is AF"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Batterybespaarder"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Batterybespaarder is AF"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Batterybespaarder is AAN"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Instellings"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Help"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Stembystand"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Ligging"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"toegang te verkry tot hierdie toestel se ligging"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot hierdie toestel se ligging?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Die program sal net toegang tot die ligging hê terwyl jy die program gebruik."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; altyd toegang tot toestelligging?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Die program sal altyd toegang tot die ligging hê, selfs wanneer jy nie die program gebruik nie."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"by jou kalender in te gaan"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou kalender?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Hierdie program kan kalendergebeurtenisse op jou foon byvoeg, verwyder of verander. Hierdie program kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of gebeurtenisse verander sonder om hul eienaars in te lig."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Kry toegang tot ekstra liggingverskaffer-bevele"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Gee die program toegang tot ekstra liggingverskaffer-bevele. Dit kan die program dalk toelaat om in te meng met die werking van die GPS of ander liggingbronne."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"verkry toegang tot presiese ligging (GPS- en netwerkgegrond)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Hierdie program kan jou ligging kry op grond van GPS of netwerkhulpbronne soos selfoontorings en Wi-Fi-netwerke. Hierdie liggingdienste moet aangeskakel en op jou foon beskikbaar wees sodat die program hulle kan gebruik. Dit kan batteryverbruik verhoog."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"kry net op die voorgrond toegang tot presiese ligging"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Hierdie program kan jou presiese ligging kry net wanneer dit op die voorgrond is. Hierdie liggingdienste moet aangeskakel wees en op jou foon beskikbaar wees sodat die program hulle kan gebruik. Dit kan veroorsaak dat meer batterykrag gebruik word."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"verkry toegang tot benaderde ligging (netwerkgegrond)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Hierdie program kan jou ligging kry op grond van jou netwerkhulpbronne soos selfoontorings en Wi-Fi-netwerke. Hierdie liggingdienste moet aangeskakel en op jou tablet beskikbaar wees sodat die program hulle kan gebruik."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Hierdie program kan jou ligging kry op grond van jou netwerkhulpbronne soos selfoontorings en Wi-Fi-netwerke. Hierdie liggingdienste moet aangeskakel en op jou TV beskikbaar wees sodat die program hulle kan gebruik."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Hierdie program kan jou ligging kry op grond van jou netwerkhulpbronne soos selfoontorings en Wi-Fi-netwerke. Hierdie liggingdienste moet aangeskakel en op jou foon beskikbaar wees sodat die program hulle kan gebruik."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"kry presiese ligging op die agtergrond"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Hierdie program kan enige tyd terwyl dit op die agtergrond is jou presiese ligging kry. Hierdie liggingdienste moet aangeskakel wees en op jou foon beskikbaar wees sodat die program hulle kan gebruik. Dit kan veroorsaak dat meer batterykrag gebruik word."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"verander jou klankinstellings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Laat die program toe om globale klankinstellings soos volume en watter luidspreker vir uitvoer gebruik word, te verander."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"neem klank op"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Vingerafdrukikoon"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"bestuur gesigstawinghardeware"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Laat program toe om metodes te benut om gesigtemplate vir gebruik by te voeg en uit te vee."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"gebruik gesigstawinghardeware"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Laat die program toe om gesigstawinghardeware vir stawing te gebruik"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Kon nie gesig verwerk nie. Probeer weer."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Gesig is te helder. Probeer met minder lig."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Gesig is te donker. Maak ligbron oop."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Beweeg sensor verder weg van gesig af."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Bring sensor nader aan gesig."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Beweeg sensor na bo."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Beweeg sensor na onder."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Beweeg sensor na regs."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Beweeg sensor na links."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Kyk na die sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Geen gesig bespeur nie."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Hou gesig stil voor toestel."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Gesighardeware is nie beskikbaar nie."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Gesiguittelling is bereik. Probeer weer."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Gesig kan nie geberg word nie."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Gesighandeling is gekanselleer."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Te veel pogings. Probeer later weer."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Te veel pogings. Gesigstawingsensor is gedeaktiveer."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Probeer weer."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Geen gesigte is geregistreer nie."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Hierdie toestel het nie \'n gesigstawingsensor nie"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Gesig <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Gesig-ikoon"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lees sinkroniseer-instellings"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Laat die program toe om die sinkroniseringinstellings van \'n rekening te lees. Byvoorbeeld, dit kan bepaal of die People-program met \'n rekening gesinkroniseer is."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"wissel tussen sinkronisasie aan en af"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi‑Fi het geen internettoegang nie"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tik vir opsies"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Veranderings aan jou warmkolinstellings"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Jou warmkolband het verander."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Hierdie toestel steun nie jou voorkeur vir net 5 GHz nie. Hierdie toestel sal in plaas daarvan die 5 GHz-band gebruik wanneer dit beskikbaar is."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Het oorgeskakel na <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Toestel gebruik <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internettoegang het nie. Heffings kan geld."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Het oorgeskakel van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index dcdf43c..400f304 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"የአውሮፕላን ሁነታ"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"የአውሮፕላንሁነታ በርቷል"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"የአውሮፕላንሁነታ ጠፍቷል"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"የባትሪ ኃይል ቆጣቢ"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"የባትሪ ኃይል ቆጣቢ ጠፍቷል"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"የባትሪ ኃይል ቆጣቢ በርቷል"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"ቅንብሮች"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"ደግፍ"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"የድምጽ እርዳታ"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"መገኛ አካባቢ"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"የዚህን መሣሪያ አካባቢ ይድረሱበት"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; የዚህ መሣሪያ አካባቢን እንዲደርስ ይፈቀድለት?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"መተግበሪያው እርስዎ ሲጠቀሙበት ብቻ ነው የአካባቢው መዳረሻ የሚኖረው።"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ሁልጊዜ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; የዚህ መሣሪያ አካባቢን እንዲደርስ ይፈቀድለት?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"መተግበሪያው ሁልጊዜ የአካባቢው መዳረሽ ይኖረዋል፣ እርስዎ መተግበሪያውን እየተጠቀሙ ባይሆኑም እንኳ።"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"ቀን መቁጠሪያ"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"የእርስዎን ቀን መቁጠሪያ ይድረሱበት"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ቀን መቁጠሪያዎን እንዲደርስ ይፈቀድለት?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ይህ መተግበሪያ በእርስዎ ስልክ ላይ የቀን መቁጠሪያ ክስተቶችን ሊያክል፣ ሊያስወግድ ወይም ሊለውጥ ይችላል። ይህ መተግበሪያ ከቀን መቁጠሪያ የመጡ መስለው የሚታዩ መልእክቶችን ሊልክ ወይም ባለቤቶቹን ሳያሳውቅ ክስተቶችን ሊለውጥ ይችላል።"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ተጨማሪ ሥፍራ አቅራቢ ትዕዛዞችን ድረስ።"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"መተግበሪያው ተጨማሪ የአካባቢ አቅራቢ ትእዛዞችን እንዲደርስ ይፈቅድለታል። ይሄ መተግበሪያው በጂፒኤስ ወይም ሌላ የአካባቢ ምንጮች ስራ ላይ ጣልቃ እንዲገባ ሊፈቅድለት ይችላል።"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"ትክክለኛውን አካባቢ መድረስ (በጂፒኤስ እና አውታረ መረብ ላይ የተመሠረተ)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"ይህ መተግበሪያ እንደ የሕዋስ ማማዎች እና የWi-Fi አውታረ መረቦች ከመሳሰሉ የአውታረ መረብ ምንጮች ላይ በመመርኮዝ የእርስዎን መገኛ አካባቢ ማግኘት ይችላል። እነዚህ የመገኛ አካባቢ አገልግሎቶች መተግበሪያው መጠቀም እንዲችል ሊበሩ እና በእርስዎ ስልክ ላይ ሊገኙ የሚችሉ መሆን አለባቸው። ይህ የባትሪ ፍጆታን ሊጨምር ይችላል።"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"መዳረሻ ከፊት ለፊት ብቻ ትክክለኛ ነው"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ይህ መተግበሪያ ከፊት ላይ ሆኖ ሲበራ ብቻ ትክክለኛውን መገኛ አካባቢ ማግኘት ይችላል። እነዚህ የመገኛ አካባቢ አገልግሎቶች መተግበሪያው መጠቀም እንዲችል ሊበሩ እና በእርስዎ ስልክ ላይ ሊገኙ የሚችሉ መሆን አለባቸው።"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ግምታዊ አካባቢን መድረስ (በአውታረ መረብ ላይ የተመሰረተ)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ይህ መተግበሪያ እንደ የሕዋስ ማማዎች እና የWi-Fi አውታረ መረቦች ከመሳሰሉ የአውታረ መረብ ምንጮች ላይ በመመርኮዝ የእርስዎን መገኛ አካባቢ ማግኘት ይችላል። እነዚህ የመገኛ አካባቢ አገልግሎቶች መተግበሪያው መጠቀም እንዲችል ሊበሩ እና በእርስዎ ጡባዊ ላይ ሊገኙ የሚችሉ መሆን አለባቸው።"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ይህ መተግበሪያ እንደ የሕዋስ ማማዎች እና የWi-Fi አውታረ መረቦች ከመሳሰሉ የአውታረ መረብ ምንጮች ላይ በመመርኮዝ የእርስዎን መገኛ አካባቢ ማግኘት ይችላል። እነዚህ የመገኛ አካባቢ አገልግሎቶች እርስዎ መጠቀም እንዲችሉ ሊበሩ እና በእርስዎ ቴሌቪዥን ላይ ሊገኙ የሚችሉ መሆን አለባቸው።"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ይህ መተግበሪያ እንደ የሕዋስ ማማዎች እና የWi-Fi አውታረ መረቦች ከመሳሰሉ የአውታረ መረብ ምንጮች ላይ በመመርኮዝ የእርስዎን መገኛ አካባቢ ማግኘት ይችላል። እነዚህ የመገኛ አካባቢ አገልግሎቶች መተግበሪያው መጠቀም እንዲችል ሊበሩ እና በእርስዎ ስልክ ላይ ሊገኙ የሚችሉ መሆን አለባቸው።"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"በበስተጀርባ ትክክለኛ መገኛ አካባቢ ላይ ድረስ"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ይህ መተግበሪያ በማናቸውም ጊዜ በበስተጀርባ ሲሆን ብቻ ትክክለኛውን መገኛ አካባቢ ማግኘት ይችላል። እነዚህ የመገኛ አካባቢ አገልግሎቶች መተግበሪያው መጠቀም እንዲችል ሊበሩ እና በእርስዎ ስልክ ላይ ሊገኙ የሚችሉ መሆን አለባቸው።"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"የድምፅ ቅንብሮችን ለውጥ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"መተግበሪያው አንደ የድምጽ መጠን እና ለውጽአት የትኛውን የድምጽ ማጉያ ጥቅም ላይ እንደዋለ የመሳሰሉ ሁለንተናዊ የድምጽ ቅንብሮችን እንዲያስተካክል ይፈቅድለታል።"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ኦዲዮ ይቅዱ"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"የጣት አሻራ አዶ"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"የማረጋገጫ ሃርድዌር ፊትን ያስተዳድሩ"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"መተግበሪያው ጥቅም ላይ እንዲውሉ የፊት ቅንብር ደንቦችን ለማከል እና ለመሰረዝ የሚያስችሉ ስልቶችን እንዲያስጀምር ያስችለዋል።"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"የፊት ማረጋገጫ ሃርድዌር ይጠቀሙ"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"መተግበሪያው የማረጋገጫ ሃርድዌር ለማረጋገጥ ሥራ እንዲጠቀም ያስችለዋል"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"ፊትን መሥራት አልተቻለም። እባክዎ እንደገና ይሞክሩ።"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"ፊት ከልክ በላይ ብሩህ ነው። እባክዎ በዝቅተኛ ብርሃን ውስጥ ይሞክሩት።"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"ፊት ከልክ በላይ ጨለም ያለ ነው። እባክዎ የብርሃን ምንጩን ይግለጹት።"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"እባክዎ ዳሳሹን ከፊት አሁንም ያርቁት።"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"እባክዎ ዳሳሹን ወደ ፊት ያስጠጉት።"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"እባክዎ ዳሳሽን ከፍ ያድርጉት።"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"እባክዎ ዳሳሽን ዝቅ ያድርጉት።"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"እባክዎ ዳስሽን ወደ ቀኝ ያንቀሳቅሱት።"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"እባክዎ ዳስሽን ወደ ግራ ያንቀሳቅሱት።"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"እባክዎ ዳሳሹ ላይ ይመልከቱ።"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"ምንም መልክ አልተገኘም።"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"ፊትዎትን ከመሣሪያው ፊት ለፊት ሳያነቃንቁ ያቆዩት።"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"የፊት ሃርድዌር አይገኝም።"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"የፊት ማብቂያ ጊዜ ደርሷል። እንደገና ይሞክሩ።"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"ፊት ሊከማች አይችልም።"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"የፊት ሥርዓተ ክወና ተሰርዟል።"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"ከልክ በላይ ብዙ ሙከራዎች። በኋላ ላይ እንደገና ይሞክሩ።"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"በጣም ብዙ ሙከራዎች። የፊት ማረጋገጫ ተሰናክሏል።"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"እንደገና ይሞክሩ።"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"ምንም ፊት አልተመዘገበም።"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"ይህ መሣሪያ የፊት ማረጋገጫ ዳሳሽ የለውም"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"ፊት <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"የፊት አዶ"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"የሥምሪያ ቅንብሮች አንብብ"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"መተግበሪያው የአንድ መለያ የማመሳሰል ቅንብሮችን እንዲያነብ ይፈቅድለታል። ለምሳሌ ይህ የሰዎች መተግበሪያ ከመለያ ጋር መመሳሰሉን አለመመሳሰሉን ሊወስን ይችላል።"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ማመሳሰያ በማብራትና በማጥፋት መካከል ቀያይር"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi በይነመረብ መዳረሻ የለውም"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ለአማራጮች መታ ያድርጉ"</string>
+    <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">"ይህ መሣሪያ የእርስዎን ምርጫ ለ5GHz ብቻ አይደግፍም። በምትኩ፣ ይህ መሣሪያ ሲገኝ 5GHz ባንድ ይጠቀማል።"</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>
@@ -1789,7 +1825,7 @@
     <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="new_sms_notification_title" msgid="8442817549127555977">"አዲስ መልእክቶች አለዎት"</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>
     <string name="user_encrypted_message" msgid="4923292604515744267">"ለመክፈት መታ ያድርጉ"</string>
@@ -1846,7 +1882,7 @@
     <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"ረጋ ይበሉና በአቅራቢያ ያለ መጠለያ ይፈልጉ።"</string>
     <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"ወዲያውኑ ከባህር ዳርቻ አካባቢዎች እና የወንዝ ዳርቻ አካባቢዎች ይውጡና እንደ ከፍ ያለ መሬት ያሉ ከአደጋ የተሻለ ደህንነት ወዳቸው ቦታዎች ይሂዱ።"</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"ረጋ ይበሉና በአቅራቢያ ያለ መጠለያ ይፈልጉ።"</string>
-    <string name="etws_primary_default_message_test" msgid="2709597093560037455">"የአስቸኳይ አደጋ መልእክቶች ሙከራ"</string>
+    <string name="etws_primary_default_message_test" msgid="2709597093560037455">"የአስቸኳይ አደጋ መልዕክቶች ሙከራ"</string>
     <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"ምላሽ ስጥ"</string>
     <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
     <string name="mmcc_authentication_reject" msgid="5767701075994754356">"ሲም ለድምጽ አይፈቀድም"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 6778f3d..1470013 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -248,9 +248,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"وضع الطائرة"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"وضع الطائرة قيد التشغيل"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"وضع الطائرة متوقف"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"توفير شحن البطارية"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"توفير شحن البطارية غير مفعّل"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"توفير شحن البطارية مفعّل"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"الإعدادات"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"مساعدة"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"المساعد الصوتي"</string>
@@ -288,6 +285,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"الموقع الجغرافي"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"الوصول إلى موقع هذا الجهاز"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى الموقع الجغرافي لهذا الجهاز؟"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"لن يكون بإمكان التطبيق الوصول إلى الموقع الجغرافي إلا عند استخدامك لهذا التطبيق."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"‏هل تريد السماح دائمًا لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى الموقع الجغرافي لهذا الجهاز؟"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"سيكون بإمكان التطبيق دائمًا الوصول إلى الموقع الجغرافي، حتى عند عدم استخدامك لهذا التطبيق."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"التقويم"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"الوصول تقويمك"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالدخول إلى التقويم؟"</string>
@@ -414,12 +414,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"يمكن لهذا التطبيق إضافة أحداث تقويم أو إزالتها أو تغييرها على الهاتف. كما يمكنه إرسال رسائل يبدو أنها واردة من مالكي التقويم، ويمكنه كذلك تغيير الأحداث بدون إشعار مالكيها."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"الدخول إلى المزيد من أوامر موفر الموقع"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"‏للسماح للتطبيق بالدخول إلى أوامر إضافية لموفر الموقع. قد يتيح هذا للتطبيق التداخل مع تشغيل تقنية نظام تحديد المواقع العالمي (GPS) أو مصادر الموقع الأخرى."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"‏الوصول إلى الموقع الدقيق (استنادًا إلى نظام تحديد المواقع العالمي \"GPS\" والشبكة)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"‏يمكن لهذا التطبيق معرفة موقعك من خلال نظام تحديد المواقع العالمي (GPS) أو مصادر مواقع الشبكات مثل أبراج الجوّال وشبكات Wi-Fi. ويجب تشغيل خدمات المواقع هذه وأن تكون متاحة على الهاتف حتى يتمكن التطبيق من استخدامها. وقد يؤدي هذا إلى زيادة استهلاك طاقة البطارية."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"الوصول إلى الموقع الجغرافي الدقيق في الواجهة الأمامية فقط"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"لا يمكن لهذا التطبيق معرفة موقعك الجغرافي بالضبط إلا عندما يعمل في الخلفية. ويجب تفعيل خدمات الموقع الجغرافي هذه وأن تكون متاحة على الهاتف حتى يتمكن التطبيق من استخدامها. وقد يؤدي هذا إلى زيادة استهلاك طاقة البطارية."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"الوصول إلى الموقع التقريبي (استنادًا إلى الشبكة)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"‏يمكن لهذا التطبيق معرفة موقعك من خلال مصادر الشبكات مثل أبراج الجوّال وشبكات Wi-Fi. ويجب تشغيل خدمات المواقع هذه وأن تكون متاحة على الجهاز اللوحي حتى يتمكن التطبيق من استخدامها."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"‏يمكن لهذا التطبيق معرفة موقعك من خلال مصادر الشبكات مثل أبراج الجوّال وشبكات Wi-Fi. ويجب تشغيل خدمات المواقع هذه وأن تكون متاحة على جهاز التلفزيون حتى يتمكن التطبيق من استخدامها."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"‏يمكن لهذا التطبيق معرفة موقعك من خلال مصادر الشبكات مثل أبراج الجوّال وشبكات Wi-Fi. ويجب تشغيل خدمات المواقع هذه وأن تكون متاحة على الهاتف حتى يتمكن التطبيق من استخدامها."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"الوصول إلى الموقع الجغرافي الدقيق في الخلفية"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"يمكن لهذا التطبيق معرفة موقعك الجغرافي بالضبط في أي وقت يعمل به في الخلفية. ويجب تفعيل خدمات الموقع الجغرافي هذه وأن تكون متاحة على الهاتف حتى يتمكن التطبيق من استخدامها. وقد يؤدي هذا إلى زيادة استهلاك طاقة البطارية."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"تغيير إعداداتك الصوتية"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"للسماح للتطبيق بتعديل إعدادات الصوت العامة مثل مستوى الصوت وأي السماعات يتم استخدامها للاستماع."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"تسجيل الصوت"</string>
@@ -527,6 +529,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"رمز بصمة الإصبع"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"إدارة أجهزة مصادقة الوجه"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"السماح للتطبيق باستدعاء طرق لإضافة نماذج من الوجوه وحذفها"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"استخدام أجهزة مصادقة الوجه"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"السماح للتطبيق باستخدام أجهزة مصادقة الوجه"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"تعذَّر التعرُّف على الوجه. يُرجى إعادة المحاولة."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"الوجه ساطع جدًا. يُرجى إعادة المحاولة بإضاءة أقل."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"الوجه مظلم جدًا. يُرجى الاستعانة بمصدر إضاءة."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"يُرجى إبعاد جهاز الاستشعار عن الوجه."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"يُرجى تقريب جهاز الاستشعار من الوجه."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"يُرجى تحريك جهاز الاستشعار للأعلى."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"يُرجى تحريك جهاز الاستشعار للأسفل."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"يُرجى تحريك جهاز الاستشعار جهة اليمين."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"يُرجى تحريك جهاز الاستشعار جهة اليسار."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"يُرجى النظر إلى جهاز الاستشعار."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"لم يتم رصد أي وجه."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"يُرجى جعل الوجه ثابتًا أمام الجهاز."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"أجهزة مصادقة الوجه غير متاحة."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"انتهت مهلة التعرُّف على الوجه. أعِد المحاولة."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"يتعذَّر حفظ الوجه."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"تمّ إلغاء عملية مصادقة الوجه."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"تمّ إجراء محاولات كثيرة. أعِد المحاولة لاحقًا."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"تمّ إجراء محاولات كثيرة. ميزة مصادقة الوجه متوقفة."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"يُرجى إعادة المحاولة."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"ليس هناك وجه مسجّل."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"لا يحتوي هذا الجهاز على جهاز استشعار مصادقة الوجه."</string>
+    <string name="face_name_template" msgid="7004562145809595384">"الوجه <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"رمز الوجه"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"قراءة إعدادات المزامنة"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"للسماح للتطبيق بقراءة الإعدادات المتزامنة لحساب ما. على سبيل المثال، يمكن أن يؤدي هذا إلى تحديد ما إذا تمت مزامنة تطبيق \"الأشخاص\" مع حساب ما."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"التبديل بين تشغيل المزامنة وإيقافها"</string>
@@ -1188,7 +1221,7 @@
     <string name="android_start_title" product="default" msgid="4536778526365907780">"جارٍ بدء تشغيل الهاتف…"</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_fstrim" msgid="8036718871534640010">"جارٍ تحسين سعة التخزين."</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_0">%1$d</xliff:g> من <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
@@ -1263,6 +1296,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"‏شبكة Wi-Fi غير متصلة بالإنترنت"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"انقر للحصول على الخيارات."</string>
+    <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">"لا يتوافق هذا الجهاز مع إعدادك المفضّل الخاص باستخدام النطاق 5 غيغاهرتز فقط. وسيستخدم الجهاز بدلاً من ذلك النطاق 5 غيغاهرتز عندما يكون متاحًا."</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="NEW_NETWORK">%1$s</xliff:g> عندما لا يتوفر اتصال بالإنترنت في شبكة <xliff:g id="PREVIOUS_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>
@@ -1530,7 +1566,7 @@
     <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>
+    <string name="storage_internal" msgid="3570990907910199483">"سعة التخزين المشتركة الداخلية"</string>
     <string name="storage_sd_card" msgid="3282948861378286745">"‏بطاقة SD"</string>
     <string name="storage_sd_card_label" msgid="6347111320774379257">"‏بطاقة SD من <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb_drive" msgid="6261899683292244209">"‏محرك أقراص USB"</string>
@@ -1951,7 +1987,7 @@
     <string name="app_category_news" msgid="7496506240743986873">"الأخبار والمجلات"</string>
     <string name="app_category_maps" msgid="5878491404538024367">"الخرائط والتنقل"</string>
     <string name="app_category_productivity" msgid="3742083261781538852">"الإنتاجية"</string>
-    <string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"السعة التخزينية للجهاز"</string>
+    <string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"سعة التخزين للجهاز"</string>
     <string name="adb_debugging_notification_channel_tv" msgid="5537766997350092316">"‏تصحيح أخطاء USB"</string>
     <string name="time_picker_hour_label" msgid="2979075098868106450">"ساعة"</string>
     <string name="time_picker_minute_label" msgid="5168864173796598399">"دقيقة"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 4d91614..2f9e444 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"এয়াৰপ্লেইন ম\'ড"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"এয়াৰপ্লেইন ম\'ড অন কৰা আছে"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"এয়াৰপ্লেইন ম\'ড অফ কৰা আছে"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"বেটাৰি সঞ্চয়কাৰী"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"বেটাৰি সঞ্চয়কাৰী অফ হৈ আছে"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"বেটাৰি সঞ্চয়কাৰী অন হৈ আছে"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"ছেটিংসমূহ"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"সহায়"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"কণ্ঠধ্বনিৰে সহায়"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"অৱস্থান"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"এই ডিভাইচৰ অৱস্থান ব্যৱহাৰ কৰিব পাৰে"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক এই ডিভাইচটোৰ অৱস্থান জানিবলৈ অনুমতি দিবনে?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"আপুনি এই এপ্ ব্য়ৱহাৰ কৰি থাকোঁতে ই সদায় অৱস্থান চাব পাৰে।"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক সদায় এই ডিভাইচৰ অৱস্থান চাবলৈ অনুমতি দিবনে?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"আপুনি এই এপ্ ব্য়ৱহাৰ কৰি থকা নাই যদিও ই সদায় অৱস্থান চাব পাৰে।"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"কেলেণ্ডাৰ"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"আপোনাৰ কেলেণ্ডাৰ ব্যৱহাৰ কৰিব পাৰে"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক আপোনাৰ কেলেণ্ডাৰ চাবলৈ অনুমতি দিবনে?"</string>
@@ -291,12 +291,9 @@
     <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>
-    <!-- no translation found for permgrouplab_calllog (8798646184930388160) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_calllog (3006237336748283775) -->
-    <skip />
-    <!-- no translation found for permgrouprequest_calllog (8487355309583773267) -->
-    <skip />
+    <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>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"ফ’ন"</string>
     <string name="permgroupdesc_phone" msgid="6234224354060641055">"ফ\'ন কল কৰিব আৰু পৰিচলনা কৰিব পাৰে"</string>
     <string name="permgrouprequest_phone" msgid="9166979577750581037">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক ফ\'ন কল কৰিবলৈ আৰু পৰিচালনা কৰিবলৈ অনুমতি দিবনে?"</string>
@@ -405,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"এই এপে আপোনাৰ ফ\'নৰ কেলেণ্ডাৰত কার্যক্ৰম যোগ দিব, আঁতৰাব বা সলনি কৰিব পাৰে। ই এনে বাৰ্তা পঠিয়াব পাৰে যিবোৰ কেলেণ্ডাৰৰ গৰাকীৰ পৰা অহা যেন লাগে বা ই গৰাকীক নজনোৱাকৈ কাৰ্যক্ৰম সলনি কৰিব পাৰে৷"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"অতিৰিক্ত অৱস্থান দেখুওৱা নির্দেশত প্ৰৱেশ কৰক"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"অৱস্থানৰ অতিৰিক্ত নির্দেশনাসমূহত প্ৰৱেশ কৰিবলৈ এপক অনুমতি দিয়ে। ইয়ে এপটোক জিপিএছ বা অন্য অৱস্থান উৎসসমূহৰ কাৰ্যকলাপত হস্তক্ষেপ কৰাৰ সুযোগ দিব পাৰে।"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"সঠিক অৱস্থান (জিপিএছ আৰু নেটৱর্ক ভিত্তিক) ব্যৱহাৰ কৰিব পাৰে"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"এই এপটোৱে জিপিএছ বা নেটৱর্কৰ উৎসসমূহ যেনে চেল টাৱাৰ আৰু ৱাই-ফাই নেটৱর্ক আদিক ভিত্তি কৰি আপোনাৰ অৱস্থান নিৰ্ণয় কৰিব পাৰে। এই অৱস্থানৰ সেৱাসমূহ অন হৈ থাকিলে আৰু আপোনাৰ ফ\'নটোত উপলব্ধ হ\'লেহে এপটোৱে সেইবোৰ ব্যৱহাৰ কৰিবলৈ সক্ষম হ\'ব। এই কার্যই অধিক বেটাৰি খৰচ কৰিব পাৰে।"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"কেৱল অগ্ৰভূমিত অৱস্থানৰ সঠিক তথ্য় পাওক"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"এই এপটোৱে যেতিয়া ই নেপথ্যত চলি থাকে তেতিয়া আপোনাৰ সঠিক অৱস্থান নিৰ্ণয় কৰিব পাৰে। এপটোৱে ব্যৱহাৰ কৰিব পৰাকৈ এই অৱস্থান সেৱাসমূহ অন হৈ থাকিবই লাগিব আৰু আপোনাৰ ফ\'নত উপলব্ধ হ\'ব লাগিব। ইয়াৰ ফলত বেটাৰিৰ খৰচ বাঢ়িব পাৰে।"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"অনুমানিক অৱস্থান (নেটৱর্ক ভিত্তিক) ব্যৱহাৰ কৰিব পাৰে"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"চেল টাৱাৰ আৰু ৱাই-ফাই নেটৱর্কৰ দৰে নেটৱর্কৰ উৎসসমূহক ভিত্তি কৰি এই এপটোৱে আপোনাৰ অৱস্থান নিৰ্ণয় কৰিব পাৰে। এই অৱস্থানৰ সেৱাসমূহ অন হৈ থাকিলে আৰু আপোনাৰ টেবলেটটোত উপলব্ধ হ\'লেহে এপটোৱে সেইবোৰ ব্যৱহাৰ কৰিবলৈ সক্ষম হ\'ব।"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"চেল টাৱাৰ আৰু ৱাই-ফাই নেটৱর্কৰ দৰে নেটৱর্কৰ উৎসসমূহক ভিত্তি কৰি এই এপটোৱে আপোনাৰ অৱস্থান নিৰ্ণয় কৰিব পাৰে। এই অৱস্থানৰ সেৱাসমূহ অন হৈ থাকিলে আৰু আপোনাৰ টিভিত উপলব্ধ হ\'লেহে এপটোৱে সেইবোৰ ব্যৱহাৰ কৰিবলৈ সক্ষম হ\'ব।"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"এই এপটোৱে ম\'বাইলৰ টাৱাৰ আৰু ৱাই-ফাইৰ নেটৱৰ্ক আদিৰ দৰে নেটৱৰ্কৰ উৎসসমূহক ভিত্তি কৰি আপোনাৰ অৱস্থান চিনাক্ত কৰিব পাৰে। সেই অৱস্থান সেৱাসমূহ আপোনাৰ ফ\'নত সক্ষম অৱস্থাত থাকিলেহে এপটোৱে সেইবোৰ ব্যৱহাৰ কৰিব পাৰিব।"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"নেপথ্যত সঠিক অৱস্থান পাব পাৰে"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"এই এপটোৱে যেতিয়া ই নেপথ্যত চলি থাকে তেতিয়া আপোনাৰ সঠিক অৱস্থান নিৰ্ণয় কৰিব পাৰে। এপটোৱে ব্যৱহাৰ কৰিব পৰাকৈ এই অৱস্থান সেৱাসমূহ অন হৈ থাকিবই লাগিব আৰু আপোনাৰ ফ\'নত উপলব্ধ হ\'ব লাগিব। ইয়াৰ ফলত বেটাৰিৰ খৰচ বাঢ়িব পাৰে।"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"আপোনাৰ অডিঅ\' ছেটিংসমূহ সলনি কৰক"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"এপটোক ভলিউমৰ দৰে গ্ল\'বেল অডিঅ\' ছেটিংসমূহ যাৰ স্পীকাৰক আউটপুটৰ বাবে ব্যৱহাৰ হয় তাক সলনি কৰিবলৈ অনুমতি দিয়ে৷"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"অডিঅ\' ৰেকর্ড কৰক"</string>
@@ -518,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ফিংগাৰপ্ৰিণ্ট আইকন"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"মুখমণ্ডল সত্যাপন হাৰ্ডৱেৰ পৰিচালনা কৰক"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"মুখমণ্ডলৰ টেম্প্লেট যোগ কৰাৰ বা মচাৰ পদ্ধতি কামত লগাবলৈ আহ্বান কৰিবলৈ এপটোক অনুমতি দিয়ে।"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"মুখমণ্ডল সত্যাপন হাৰ্ডৱেৰ ব্যৱহাৰ কৰক"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"বিশ্বাসযোগ্য়তা প্ৰমাণীকৰণৰ বাবে এপক মুখমণ্ডল সত্যাপন হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰিবলৈ অনুমতি দিয়ে"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"মুখমণ্ডল চিনাক্ত কৰিব পৰা নাই; আকৌ চেষ্টা কৰক।"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"মুখমণ্ডল অত্যন্ত উজ্জ্বল হৈছে। অনুগ্ৰহ কৰি পোহৰ কম থকা ঠাইত চেষ্টা কৰক।"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"মুখমণ্ডল অত্যন্ত আন্ধাৰ হৈছে। অনুগ্ৰহ কৰি পোহৰ থকা ঠাইলৈ যাওক।"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"অনুগ্ৰহ কৰি মুখৰ পৰা ছেন্সৰ অলপ দূৰত ৰাখক।"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"অনুগ্ৰহ কৰি ছেন্সৰটো মুখৰ ওচৰলৈ আনক।"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"অনুগ্ৰহ কৰি ছেন্সৰটো ওপৰলৈ নিয়ক।"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"অনুগ্ৰহ কৰি ছেন্সৰটো তললৈ নিয়ক।"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"অনুগ্ৰহ কৰি ছেন্সৰটো সোঁফাললৈ নিয়ক।"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"অনুগ্ৰহ কৰি ছেন্সৰটো বাওঁফাললৈ নিয়ক।"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"অনুগ্ৰহ কৰি ছেন্সৰটোলৈ চাওক।"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"কোনো মুখমণ্ডল চিনাক্ত কৰিব পৰা নগ’ল।"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"ডিভাইচৰ আগত মুখখন স্থিৰ কৰি ৰাখক।"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"মুখমণ্ডলৰ হাৰ্ডৱেৰ উপলব্ধ নহয়।"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"মুখমণ্ডল গ্ৰহণৰ সময়সীমা উকলি গৈছে। আকৌ চেষ্টা কৰক।"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"মুখমণ্ডল সঞ্চয় কৰিব নোৱাৰি।"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"মুখমণ্ডলৰ প্ৰক্ৰিয়া বাতিল কৰা হ’ল।"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"অত্যধিক ভুল প্ৰয়াস। কিছুসময়ৰ পাছত আকৌ চেষ্টা কৰক।"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"অত্যধিক প্ৰয়াস। মুখমণ্ডলৰ জৰিয়তে সত্যাপন অক্ষম কৰা হ’ল।"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"আকৌ চেষ্টা কৰক।"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"কোনো মুখমণ্ডল যোগ কৰা নহ’ল।"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"এই ডিভাইচটোত মুখমণ্ডল সত্যাপন ছেন্সৰ নাই।"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"মুখমণ্ডল <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"মুখমণ্ডলৰ আইকন"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ছিংকৰ ছেটিংসমূহ পঢ়ক"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"একাউণ্টৰ ছিংক ছেটিংবোৰ পঢ়িবলৈ এপক অনুমতি দিয়ে। যেনে, People এপ কোনো একাউণ্টত ছিংক কৰা হৈছে নে নাই সেয়া নির্ধাৰণ কৰিব পাৰে।"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ছিংকক অন আৰু অফ ট\'গল কৰক"</string>
@@ -1178,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"ৱাই-ফাইত ইন্টাৰনেট নাই"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"অধিক বিকল্পৰ বাবে টিপক"</string>
+    <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_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>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 311a4cf..7ddd88c2 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Təyyarə rejimi"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Uçuş rejimi açıqdır"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Təyyarə rejimi qapalıdır"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Batareya qənaəti"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Batareya qənaəti DEAKTİVDİR"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Batareya qənaəti AKTİVDİR"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Ayarlar"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Yardım"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Səs Yardımçısı"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Yer"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"cihazın yerini bilmək"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə bu cihazın məkanına daxil olmaq icazəsi verilsin?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Tətbiq yalnız ondan istifadə etiyiniz zaman məkanı əldə edə bilər."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə bu cihazın məkanına daxil olmaq icazəsi verilsin?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Tətbiq hətta ondan istifadə etmədiyiniz zaman belə məkanı əldə edə bilər."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Təqvim"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"təqvimə daxil olun"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə təqvimə daxil olmaq icazəsi verilsin?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Bu tətbiq telefonunuzda təqvim tədbirləri əlavə edə, silə və ya dəyişiklik edə bilər. Həmçinin bu tətbiq təqvim sahiblərindən gəlmə ehtimalı olan mesajları göndərə və ya sahiblərinə bildirmədən tədbirlərdə dəyişiklik edə bilər."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"əlavə məkan provayderi əmrlərinə çıxış"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Tətbiqə ekstra məkan provayder əmrlərinə girişə imkan verir. Bu, tətbiqə GPS və ya digər lokal mənbələrlə əməliyyata müdaxiləyə imkan verə bilər."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"dəqiq məkana (GPS və şəbəkə əsasında) giriş"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Bu tətbiq mobil qüllələr və Wi-Fi şəbəkələri kimi şəbəkə mənbələrinin əassında əkanınızı əldə edə bilər. Bu məkan xidmətləri aktiv edilməlidir və telefonda tətbiq tərəfindən istifadə üçün əlçatan olmalıdır. Bu, batareya sərfiyyatını artıra bilər."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"yalnız ön planda dəqiq məkana daxil olun"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Bu tətbiq yalnız ön fonda olduqda dəqiq məkanınızı əldə edə bilər. Tətbiqin bunlardan istifadə etməsi üçün bu məkan xidmətləri aktiv edilməlidir və telefonda əlçatan olmalıdır. Bu, batareya sərfiyyatını artıra bilər."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"təxmini məkana (şəbəkə əsaslı) giriş"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Bu tətbiq mobil qüllələr və Wi-Fi şəbəkələri kimi şəbəkə mənbələrinin əassında əkanınızı əldə edə bilər. Bu məkan xidmətləri aktiv edilməlidir və planşetdə tətbiq tərəfindən istifadə üçün əlçatan olmalıdır."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Bu tətbiq mobil qüllələr və Wi-Fi şəbəkələri kimi şəbəkə mənbələrinin əassında əkanınızı əldə edə bilər. Bu məkan xidmətləri aktiv edilməlidir və TV\'də tətbiq tərəfindən istifadə üçün əlçatan olmalıdır."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Bu tətbiq mobil qüllələr və Wi-Fi şəbəkələri kimi şəbəkə mənbələrinin əassında əkanınızı əldə edə bilər. Bu məkan xidmətləri aktiv edilməlidir və telefonda tətbiq tərəfindən istifadə üçün əlçatan olmalıdır."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"arxa fonda dəqiq məkana daxil olun"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Bu tətbiq istənilən zaman arxa fonda olduqda dəqiq məkanınızı əldə edə bilər. Tətbiqin bunlardan istifadə etməsi üçün bu məkan xidmətləri aktiv edilməlidir və telefonda əlçatan olmalıdır. Bu, batareya sərfiyyatını artıra bilər."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"audio ayarlarınızı dəyişir"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Tətbiqə səs və hansı spikerin çıxış üçün istifadə olunduğu kimi qlobal səs ayarlarını dəyişdirməyə imkan verir."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"səs yaz"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Barmaq izi ikonası"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"üz identifikasiyası proqramını idarə edin"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Proqramdan istifadə üçün barmaq izi şablonlarını əlavə etmək və silmək məqsədilə üsullara müraciət etməyə imkan verir."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"üz identifikasiyası proqramından istifadə edin"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Tətbiqin üz identifikasiyası proqramından identifikasiya zamanı istifadə etməsinə icazə verir"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Üz tanınmadı. Yenidən cəhd edin."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Üz çox parlaqdır. Daha zəif işıqda sınayın."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Üz çox tünddür. İşığı ortaya çıxarın."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Sensoru üzdən kənara gətirin."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Sensoru üzə biraz da yaxınlaşdırın."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Sensoru daha yuxarı gətirin."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Sensoru daha aşağı gətirin."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Sensoru sağa gətirin."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Sensoru sola gətirin."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Sensora baxın."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Üz aşkarlanmadı."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Üzü cihazın qarşısında sabit saxlayın."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Üz proqramı əlçatan deyil."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Üz proqramı taymerinin vaxtı bitdi. Yenidən cəhd edin."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Üz bərpa edilmədi."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Üz əməliyyatı ləğv edildi."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Həddindən çox cəhd. Sonraya saxlayın."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Həddindən çox cəhd. Üz identifikasiyası deaktiv edildi."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Yenidən cəhd edin."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Üz qeydə alınmayıb."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Bu cihazda üz identifikasiyası sensoru yoxdur."</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Üz <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Üz işarəsi"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"sinx ayarlarını oxu"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Tətbiqə hesablar üçün sinxronizasiya nizamlarını oxuma icazəsi verir. Məsələn, bu Şəxslər tətbiqinin sinxronizə olunub-olunmadığını təyin edə bilər."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"sinxronizasiyaya davam edir və onu söndürür"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi şəbəkəsinin internetə girişi yoxdur"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Seçimlər üçün tıklayın"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Hotspot ayarlarınızda dəyişiklik"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Hotspot qrupu dəyişdi."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Bu cihaz yalnız 5GHz üçün tərcihinizi dəstəkləmir. Əvəzinə əlçatan olduqda bu cihaz 5GHz qrupundan istifadə edəcək."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> şəbəkə növünə keçirildi"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> şəbəkəsinin internetə girişi olmadıqda, cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> şəbəkəsini istifadə edir. Xidmət haqqı tutula bilər."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> şəbəkəsindən <xliff:g id="NEW_NETWORK">%2$s</xliff:g> şəbəkəsinə keçirildi"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 7af1d0c..6338e69 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -239,9 +239,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Režim rada u avionu"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Režim rada u avionu je UKLJUČEN"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Režim rada u avionu je ISKLJUČEN"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Ušteda baterije"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Ušteda baterije je ISKLJUČENA"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Ušteda baterije je UKLJUČENA"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Podešavanja"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Pomoć"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Glasovna pomoć"</string>
@@ -279,6 +276,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Lokacija"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"pristupi lokaciji ovog uređaja"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Želite li da omogućite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa lokaciji ovog uređaja?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikacija će imati pristup lokaciji samo dok koristite aplikaciju."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Želite li da omogućite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa lokaciji ovog uređaja?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikacija će uvek imati pristup lokaciji, čak i kada ne koristite aplikaciju."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendar"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"pristupi kalendaru"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Želite li da omogućite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa kalendaru?"</string>
@@ -405,12 +405,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ova aplikaciji može da dodaje, uklanja ili menja događaje iz kalendara na telefonu. Ova aplikacija može da šalje poruke koje izgledaju kao da ih šalju vlasnici kalendara ili da menja događaje bez znanja vlasnika."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pristup dodatnim komandama dobavljača lokacije"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Omogućava aplikaciji da pristupa dodatnim komandama davaoca usluga lokacije. To može da omogući aplikaciji da utiče na rad GPS-a ili drugih izvora lokacije."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"pristup preciznoj lokaciji (utvrđena preko mreže i GPS-a)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Ova aplikacija može da pristupi vašoj lokaciji pomoću GPS-a ili mrežnih izvora lokacija, kao što su mobilni predajnici i Wi-Fi mreže. Ove usluge lokacije moraju da budu uključene i dostupne na telefonu da bi aplikacija mogla da ih koristi. To može da poveća potrošnju baterije."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"pristup preciznoj lokaciji samo u prvom planu"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ova aplikacija može da odredi vašu tačnu lokaciju samo kada radi u prvom planu. Ove usluge lokacije moraju da budu uključene i dostupne na telefonu da bi aplikacija mogla da ih koristi. To može da poveća potrošnju baterije."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"pristup približnoj lokaciji (utvrđena preko mreže)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ova aplikacija može da pristupi vašoj lokaciji pomoću izvora mreže, kao što su mobilni predajnici i Wi-Fi mreže. Ove usluge lokacije moraju da budu uključene i dostupne na tabletu da bi aplikacija mogla da ih koristi."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ova aplikacija može da pristupi vašoj lokaciji pomoću izvora mreže, kao što su mobilni predajnici i Wi-Fi mreže. Ove usluge lokacije moraju da budu uključene i dostupne na TV-u da bi aplikacija mogla da ih koristi."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ova aplikacija može da pristupi vašoj lokaciji pomoću izvora mreže, kao što su mobilni predajnici i Wi-Fi mreže. Ove usluge lokacije moraju da budu uključene i dostupne na telefonu da bi aplikacija mogla da ih koristi."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"pristup preciznoj lokaciji u pozadini"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ova aplikacija može da odredi vašu tačnu lokaciju svaki put kada radi u pozadini. Ove usluge lokacije moraju da budu uključene i dostupne na telefonu da bi aplikacija mogla da ih koristi. To može da poveća potrošnju baterije."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"promena audio podešavanja"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Dozvoljava aplikaciji da menja globalna audio podešavanja kao što su jačina zvuka i izbor zvučnika koji se koristi kao izlaz."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"snimanje audio zapisa"</string>
@@ -518,6 +520,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona otiska prsta"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"upravljanje hardv. za potvrdu identiteta pomoću lica"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Dozvoljava da aplikacija aktivira metode za dodavanje i brisanje šablona lica radi korišćenja."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"korišćenje hardv. za potvrdu identiteta pomoću lica"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Dozvoljava da aplikacija koristi hardver za potvrdu identiteta pomoću lica"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Obrada lica nije uspela. Probajte ponovo."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Lice je presvetlo. Probajte sa slabijim osvetljenjem."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Lice je isuviše tamno. Otkrijte izvor svetla."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Udaljite senzor od lica."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Približite senzor licu."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Pomerite senzor naviše."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Pomerite senzor naniže."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Pomerite senzor udesno."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Pomerite senzor ulevo."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Gledajte u senzor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nije otkriveno nijedno lice."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Zadržite lice ispred uređaja bez pomeranja."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Harvder za lice nije dostupan."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Isteklo je vreme za proveru lica. Probajte ponovo."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Nije moguće sačuvati lice."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Obrada lica je otkazana."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Previše pokušaja. Probajte ponovo kasnije."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Više pokušaja. Potvrda identiteta je onemogućena."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Probajte ponovo."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nije registrovano nijedno lice."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Ovaj uređaj nema senzor za potvrdu identiteta pomoću lica"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona lica"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"čitanje podešavanja sinhronizacije"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Dozvoljava aplikaciji da čita podešavanja sinhronizacije za nalog. Na primer, ovako može da se utvrdi da li je aplikacija Ljudi sinhronizovana sa nalogom."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"uključivanje i isključivanje sinhronizacije"</string>
@@ -1197,6 +1230,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi nema pristup internetu"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Dodirnite za opcije"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Promene podešavanja za hotspot"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Opseg hotspota je promenjen."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Ovaj uređaj ne podržava podešavanje samo za 5 GHz. Uređaj će koristiti opseg od 5 GHz kada bude dostupan."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Prešli ste na tip mreže <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Uređaj koristi tip mreže <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kada tip mreže <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu. Možda će se naplaćivati troškovi."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Prešli ste sa tipa mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na tip mreže <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 2917807..c93fa51 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -220,7 +220,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Параметры тэлефона"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Блакіроўка экрана"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Выключыць"</string>
-    <string name="global_action_emergency" msgid="7112311161137421166">"SOS-выклік"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Экстранны выклік"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Справаздача пра памылкі"</string>
     <string name="global_action_logout" msgid="935179188218826050">"Скончыць сеанс"</string>
     <string name="global_action_screenshot" msgid="8329831278085426283">"Здымак экрана"</string>
@@ -242,9 +242,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Рэжым палёту"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Уключаны рэжым \"У самалёце\""</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Рэжым \"У самалёце\" адключаны"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Эканомія зараду"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Рэжым эканоміі зараду выключаны"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Рэжым эканоміі зараду ўключаны"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Налады"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Дапамога"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Галас. дапамога"</string>
@@ -282,6 +279,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Месцазнаходжанне"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"атрымліваць доступ да месцазнаходжання гэтай прылады"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да звестак аб месцазнаходжанні гэтай прылады?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Праграма будзе мець доступ да звестак пра месцазнаходжанне, толькі калі яна выкарыстоўваецца."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; мець доступ да месцазнаходжання?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Праграма заўсёды будзе мець доступ да звестак пра месцазнаходжанне, нават калі яна не выкарыстоўваецца."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Каляндар"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"атрымліваць доступ да вашага календара"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да вашага календара?"</string>
@@ -408,12 +408,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Гэта праграма можа дадаваць, выдаляць або змяняць падзеі календара на вашым тэлефоне. Гэта праграма можа адпраўляць паведамленні, якія могуць здавацца адпраўленымі ўладальнікамі календара, або змяняць падзеі без апавяшчэння іх уладальнікаў."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"доступ да дадатковых камандаў пастаўшчыка месцазнаходжання"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Дазваляе праграме атрымліваць доступ да дадатковых каманд службаў вызначэння месцазнаходжання. Гэта можа дазволіць праграме ўмешвацца ў функцыянаванне GPS або іншых крыніц даных аб месцазнаходжаннi."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"атрымліваць доступ да дакладнага месцазнаходжання (на аснове GPS і сеткі)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Гэта праграма можа атрымліваць звесткі пра ваша месцазнаходжанне на падставе даных GPS або сеткавых крыніц звестак аб месцазнаходжанні, такіх як вышкі сотавай сувязі і сеткі Wi-Fi. Гэтыя сэрвісы вызначэння месцазнаходжання павінны быць уключаны i даступныя на вашым тэлефоне, каб праграма магла іх выкарыстоўваць. Гэта можа павялічыць спажыванне зараду акумулятара."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"доступ да дакладнага месцазнаходжання толькі ў асноўным рэжыме"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Гэта праграма можа атрымліваць звесткі пра ваша дакладнае месцазнаходжанне толькі ў асноўным рэжыме. Службы геалакацыі павінны быць уключаны і даступныя на вашым тэлефоне, каб праграма магла імі карыстацца. Гэта можа павялічыць спажыванне зараду акумулятара."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"атрымліваць доступ да прыблізнага месцазнаходжання (на аснове даных сеткі)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Гэта праграма можа атрымліваць звесткі пра ваша месцазнаходжанне на падставе даных сеткавых крыніц, такіх як вышкі сотавай сувязі і сеткі Wi-Fi. Гэтыя сэрвісы вызначэння месцазнаходжання павінны быць уключаны i даступныя на вашым планшэце, каб праграма магла іх выкарыстоўваць."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Гэта праграма можа атрымліваць звесткі пра ваша месцазнаходжанне на падставе даных сеткавых крыніц, такіх як вышкі сотавай сувязі і сеткі Wi-Fi. Гэтыя сэрвісы вызначэння месцазнаходжання павінны быць уключаны i даступныя на вашым тэлевізары, каб праграма магла іх выкарыстоўваць."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Гэта праграма можа атрымліваць звесткі пра ваша месцазнаходжанне на падставе даных сеткавых крыніц, такіх як вышкі сотавай сувязі і сеткі Wi-Fi. Гэтыя сэрвісы вызначэння месцазнаходжання павінны быць уключаны i даступныя на вашым тэлефоне, каб праграма магла іх выкарыстоўваць."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"доступ да дакладнага месцазнаходжання ў фонавым рэжыме"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Гэта праграма можа атрымліваць звесткі пра ваша дакладнае месцазнаходжанне ў любы час, калі яна працуе ў фонавым рэжыме. Службы геалакацыі павінны быць уключаны і даступныя на вашым тэлефоне, каб праграма магла імі карыстацца. Гэта можа павялічыць спажыванне зараду акумулятара."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"змяняць налады аудыё"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Дазваляе прыкладанням змяняць глабальныя налады гуку, такія як моц і тое, што дынамік выкарыстоўваецца для выхаду."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"запісваць аўдыё"</string>
@@ -521,6 +523,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Значок адбіткаў пальцаў"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"кіраваць абсталяваннем для распазнавання твару"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Праграма зможа дадаваць і выдаляць шаблоны твару."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"карыстацца абсталяваннем для распазнавання твару"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Праграма зможа выкарыстоўваць абсталяванне распазнавання твару для аўтэнтыфікацыі"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Не ўдалося распазнаць твар. Паўтарыце спробу."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Твар занадта светлы. Паспрабуйце зменшыць святло."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Твар занадта цёмны. Павялічце асвятленне."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Перамясціце датчык далей ад твару."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Наблізьце датчык да твару."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Падыміце датчык вышэй."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Апусціце датчык ніжэй."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Перамясціце датчык управа."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Перамясціце датчык улева."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Глядзіце на датчык."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Твар не знойдзены."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Трымайце твар перад прыладай."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Абсталяванне для распазнавання твару недаступнае."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Час чакання твару выйшаў. Паўтарыце спробу."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Не ўдалося захаваць твар."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Распазнаванне твару скасавана."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Занадта шмат спроб. Паўтарыце спробу пазней."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Занадта шмат спроб. Аўтэнтыфікацыя твару адключана"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Паўтарыце спробу."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Твар не зарэгістраваны."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"На гэтай прыладзе адсутнічае датчык аўтэнтыфікацыі твару"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Твар <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Значок твару"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"чытаць параметры сінхранізацыі"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Дазваляе прыкладанням чытаць параметры сінхранізацыі для ўліковага запісу. Напрыклад, яны могуць вызначыць, цi сiнхранiзавана з улiковым запiсам прыкладанне \"Кантакты\"."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"уключэнне ці адключэнне сінхранізацыi"</string>
@@ -741,7 +774,7 @@
     <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>
-    <string name="lockscreen_emergency_call" msgid="5298642613417801888">"SOS-выклік"</string>
+    <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Экстранны выклік"</string>
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Вярнуцца да выкліку"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Правільна!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Паспрабуйце яшчэ раз"</string>
@@ -1219,6 +1252,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"У Wi-Fi няма доступу да інтэрнэту"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Дакраніцеся, каб убачыць параметры"</string>
+    <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">"Прылада не можа працаваць толькі на частаце 5 ГГц. Гэта частата будзе выкарыстоўвацца, калі гэта магчыма."</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="NEW_NETWORK">%1$s</xliff:g>, калі ў сетцы <xliff:g id="PREVIOUS_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>
@@ -1927,7 +1963,7 @@
     <string name="mmcc_imsi_unknown_in_hlr_msim_template" msgid="5636464607596778986">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> адсутнічае"</string>
     <string name="mmcc_illegal_ms_msim_template" msgid="5994323296399913454">"Дзеянні з SIM <xliff:g id="SIMNUMBER">%d</xliff:g> не дазволены"</string>
     <string name="mmcc_illegal_me_msim_template" msgid="5550259730350571826">"Дзеянні з SIM <xliff:g id="SIMNUMBER">%d</xliff:g> не дазволены"</string>
-    <string name="popup_window_default_title" msgid="4874318849712115433">"Выплыўное акно"</string>
+    <string name="popup_window_default_title" msgid="4874318849712115433">"Усплывальнае акно"</string>
     <string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="shortcut_restored_on_lower_version" msgid="4860853725206702336">"Ярлык адносіцца да старэйшай версіі праграмы або несумяшчальны з ёю"</string>
     <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Не атрымалася аднавіць ярлык, бо праграма не падтрымлівае рэзервовае капіраванне і аднаўленне"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 51f0e2d..d05d78c 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Самолетен режим"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Самолетният режим е ВКЛЮЧЕН"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Самолетният режим е ИЗКЛЮЧЕН"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Режим за запазване на батерията"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Режимът за запазване на батерията е ИЗКЛЮЧЕН"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Режимът за запазване на батерията е ВКЛЮЧЕН"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Настройки"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Помощ"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Гласова помощ"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Местоположение"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"получи достъп до местоположението на това устройство"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до местоположението на това устройство?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Само когато използвате приложението, то ще има достъп до местоположението."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; достъп до местопол. на у-вото?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Дори когато не използвате приложението, то винаги ще има достъп до местоположението."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"има достъп до календара ви"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до календара ви?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Това приложение може да добавя, премахва или променя събития в календара на телефона ви. То е в състояние да изпраща съобщения от името на собственици на календари или да променя събития, без да уведомява собствениците."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"достъп до допълнителни команди на доставчика на местоположение"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Разрешава на приложението достъп до допълнителни команди на доставчика на местоположение. Това може да позволи на приложението да смущава работата на GPS или на другите източници на местоположение."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"достъп до точното местоположение (въз основа на GPS и мрежата)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Приложението може да получава данни за местоположението ви въз основа на GPS или съответните мрежови източници, като клетъчни кули и Wi-Fi. Тези услуги за местоположение трябва да са включени и налице на телефона ви, за да могат да се използват от приложението. Това може да увеличи потреблението на батерията."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"достъп до точното местоположение само на преден план"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Приложението може да получава данни за точното ви местоположение само когато работи на преден план. Тези услуги за местоположение трябва да са включени и налице на телефона ви, за да могат да се използват от приложението. Това може да увеличи потреблението на батерията."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"достъп до приблизителното местоположение (въз основа на мрежата)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Приложението може да получава данни за местоположението ви въз основа на мрежови източници, като клетъчни кули и Wi-Fi. Тези услуги за местоположение трябва да са включени и налице на таблета ви, за да могат да се използват от приложението."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Приложението може да получава данни за местоположението ви въз основа на мрежови източници, като клетъчни кули и Wi-Fi. Тези услуги за местоположение трябва да са включени и налице на телевизора ви, за да могат да се използват от приложението."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Приложението може да получава данни за местоположението ви въз основа на мрежови източници, като клетъчни кули и Wi-Fi. Тези услуги за местоположение трябва да са включени и налице на телефона ви, за да могат да се използват от приложението."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"достъп до точното местоположение на заден план"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Приложението може да получава данни за точното ви местоположение по всяко време, когато работи на заден план. Тези услуги за местоположение трябва да са включени и налице на телефона ви, за да могат да се използват от приложението. Това може да увеличи потреблението на батерията."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"промяна на настройките ви за звука"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Разрешава на приложението да променя глобалните настройки за звука, като например силата и това, кой високоговорител се използва за изход."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"записва звук"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Икона за отпечатък"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"управление на хардуера за удостоверяване с лице"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Разрешава на прил. да извиква методи за добавяне и изтриване на лицеви шаблони за ползване"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"използване на хардуера за удостоверяване с лице"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Разрешава на приложението при необходимост да използва хардуера за удостоверяване с лице"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Лицето не можа да се обработи. Опитайте отново."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Лицето е твърде осветено. Oпитайте на по-тъмно."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Лицето е твърде тъмно. Моля, осветете го по-добре."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Моля, отдалечете сензора от лицето си."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Моля, приближете сензора към лицето си."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Моля, повдигнете сензора."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Моля, преместете сензора надолу."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Моля, преместете сензора надясно."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Моля, преместете сензора наляво."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Моля, гледайте към сензора."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Не е открито лице."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Задръжте лицето си неподвижно пред устройството."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Няма достъп до хардуера за лице."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Времето за изчакване изтече. Опитайте отново."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Лицето не може да бъде съхранено."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Операцията с лице е анулирана."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Твърде много опити. Опитайте отново по-късно."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Твърде много опити. Удост. с лице е деактивирано."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Опитайте отново."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Няма регистрирано лице."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Това устройство няма сензор за удостоверяване с лице"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Лице <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Икона на лице"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"четене на настройките за синхронизиране"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Разрешава на приложението да чете настройките за синхронизиране на профил. Например това може да определи дали приложението Хора е синхронизирано с даден профил."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"включване и изключване на синхронизирането"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi мрежата няма достъп до интернет"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Докоснете за опции"</string>
+    <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">"Това устройство не поддържа предпочитанието ви за използване само на честотната лента от 5 ГХц. Вместо това то ще я ползва, когато е възможно."</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="NEW_NETWORK">%1$s</xliff:g>, когато <xliff:g id="PREVIOUS_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>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index a671101..949d206 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"বিমান মোড"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"বিমান মোড চালু করা আছে"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"বিমান মোড বন্ধ করা আছে"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ব্যাটারি সেভার"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ব্যাটারি সেভার বন্ধ আছে"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ব্যাটারি সেভার চালু আছে"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"সেটিংস"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"সহযোগিতা"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ভয়েস সহায়তা"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"লোকেশন"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"এই ডিভাইসের লোকেশন অ্যাক্সেস"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে এই ডিভাইসের লোকেশন অ্যাক্সেস করতে দেবেন?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"আপনি এই অ্যাপটি ব্যবহার করার সময়েই সেটি আপনার লোকেশন অ্যাক্সেস করতে পারবে।"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;কে সবসময় এই ডিভাইসের লোকেশন অ্যাক্সেস করতে দেবেন?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"এই অ্যাপটি সবসময় আপনার লোকেশন অ্যাক্সেস করতে পারবে, এমনকি আপনি অ্যাপটি ব্যবহার না করার সময়েও।"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"ক্যালেন্ডার"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"আপনার ক্যালেন্ডারে অ্যাক্সেস"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে আপনার ক্যালেন্ডারে অ্যাক্সেস দেবেন?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"এই অ্যাপটি আপনার ফোনে ক্যালেন্ডার ইভেন্টগুলি যোগ করতে, সরাতে বা পরিবর্তিত করতে পারে৷ এই অ্যাপটি মেসেজ পাঠাতে পারে যা ক্যালেন্ডারের মাললিকের থেকে এসেছে বলে মনে হয় বা ইভেন্টগুলিকে তাদের মালিকদের না জানিয়েই পরিবর্তিত করতে পারে৷"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"অতিরিক্ত লোকেশন প্রদানকারী কমান্ডগুলি অ্যাক্সেস করে"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"লোকেশনের সাথে সম্পর্কিত তথ্য প্রদানকারীর অতিরিক্ত কম্যান্ডগুলিকে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ এটি অ্যাপ্লিকেশানটিকে GPS অথবা অন্যান্য লোকেশন নির্ণয়ের সাথে সম্পর্কিত উৎসগুলির ক্রিয়াপ্রণালীর নিয়ন্ত্রণকে মঞ্জুর করতে পারে৷"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"সুনির্দিষ্ট লোকেশন (GPS এবং নেটওয়ার্ক-ভিত্তিক) অ্যাক্সেস করুন"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"মোবাইল টাওয়ার এবং ওয়াই-ফাই নেটওয়ার্কগুলির মত নেটওয়ার্ক লোকেশন উৎসগুলি বা GPS এর উপর ভিত্তি করে এই অ্যাপটি আপনার লোকেশন শনাক্ত করতে পারে৷ এই লোকেশন পরিষেবাগুলি অবশ্যই চালু রাখতে হবে এবং অ্যাপটি যাতে সেগুলি ব্যবহার করতে পারে সেজন্য সেগুলিকে আপনার ফোনে উপলব্ধ করে রাখতে হবে৷ এর জন্য অতিরিক্ত ব্যাটারি খরচ হতে পারে৷"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"শুধুমাত্র অ্যাপটি খোলা থাকলে আপনার যথাযথ লোকেশন অ্যাক্সেস করা"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"এই অ্যাপটি ফোরগ্রাউন্ডে চলতে থাকলে যেকোনও সময়ে আপনার যথাযথ লোকেশন জানতে পারবে। এই লোকেশন পরিষেবাগুলি অবশ্যই চালু রাখতে হবে এবং আপনার ফোনে সেগুলি উপলভ্য থাকতে হবে যাতে অ্যাপটি সেগুলি ব্যবহার করতে পারে। এর জন্য অতিরিক্ত ব্যাটারি খরচ হতে পারে।"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"আনুমানিক লোকেশন (নেটওয়ার্ক-ভিত্তিক) অ্যাক্সেস করুন"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"মোবাইল টাওয়ার এবং ওয়াই-ফাই নেটওয়ার্কগুলির মত নেটওয়ার্ক উৎসগুলির উপর ভিত্তি করে এই অ্যাপটি আপনার লোকেশন শনাক্ত করতে পারে৷ এই লোকেশন পরিষেবাগুলি অবশ্যই চালু রাখতে হবে এবং অ্যাপটি যাতে সেগুলি ব্যবহার করতে পারে সেইজন্য সেগুলিকে আপনার ট্যাবলেটে উপলব্ধ করে রাখতে হবে৷"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"মোবাইল টাওয়ার এবং ওয়াই-ফাই নেটওয়ার্কগুলির মত নেটওয়ার্কের উৎসগুলির উপর ভিত্তি করে এই অ্যাপটি আপনার লোকেশন শনাক্ত করতে পারে৷ এই লোকেশন পরিষেবাগুলি অবশ্যই চালু রাখতে হবে এবং অ্যাপটি যাতে সেগুলি ব্যবহার করতে পারে সেজন্য সেগুলিকে আপনার টিভিতে উপলব্ধ করে রাখতে হবে৷"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"মোবাইল টাওয়ার এবং ওয়াই-ফাই নেটওয়ার্কগুলির মত নেটওয়ার্ক উৎসগুলির উপর ভিত্তি করে এই অ্যাপটি আপনার লোকেশন শনাক্ত করতে পারে৷ এই লোকেশন পরিষেবাগুলি অবশ্যই চালু রাখতে হবে এবং অ্যাপটি যাতে সেগুলি ব্যবহার করতে পারে সেজন্য সেগুলিকে আপনার ফোনে উপলব্ধ করে রাখতে হবে৷"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ব্যাকগ্রাউন্ডে চলতে থাকলে আপনার যথাযথ লোকেশন অ্যাক্সেস করা"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"এই অ্যাপটি ব্যাকগ্রাউন্ডে চলতে থাকলে যেকোনও সময়ে আপনার যথাযথ লোকেশন জানতে পারবে। এই লোকেশন পরিষেবাগুলি অবশ্যই চালু রাখতে হবে এবং আপনার ফোনে সেগুলি উপলভ্য থাকতে হবে যাতে অ্যাপটি সেগুলি ব্যবহার করতে পারে। এর জন্য অতিরিক্ত ব্যাটারি খরচ হতে পারে।"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"আপনার অডিও সেটিংস পরিবর্তন করে"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ভলিউম এবং যেখানে স্পিকার আউটপুট হিসাবে ব্যবহৃত হয় সেই সব ক্ষেত্রে গ্লোবাল অডিও সেটিংসের সংশোধন করতে অ্যাপ্লিকেশনটিকে মঞ্জুর করে৷"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"অডিও রেকর্ড"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"আঙ্গুলের ছাপ আইকন"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"ফেস যাচাইকরণ হার্ডওয়্যার পরিচালনা করুন"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"ব্যবহার করার জন্য ফেস টেম্পলেট যোগ করা এবং মোছার পদ্ধতি গ্রহণ করতে অ্যাপটিকে অনুমতি দেয়৷"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ফেস যাচাইকরণ হার্ডওয়্যার ব্যবহার করুন"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"প্রমাণীকরণের জন্য ফেস যাচাইকরণ হার্ডওয়্যার ব্যবহার করার অনুমতি অ্যাপটিকে দেয়"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"ফেস প্রক্রিয়া করা যায়নি৷ আবার চেষ্টা করুন৷"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"ফেসটি খুব উজ্জ্বল লাগছে। কম আলোতে চেষ্টা করুন।"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"ফেসটি খুব অন্ধকার লাগছে। বেশি আলোতে চেষ্টা করুন।"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"অনুগ্রহ করে ফেস থেকে সেন্সরটি দূরে সরান।"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"অনুগ্রহ করে ফেসের কাছাকাছি সেন্সরটি আনুন।"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"অনুগ্রহ করে সেন্সরটি উঁচুতে নিয়ে যান।"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"অনুগ্রহ করে সেন্সরটি নিচে নিয়ে যান।"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"অনুগ্রহ করে সেন্সরটি ডান দিকে নিয়ে যান।"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"অনুগ্রহ করে সেন্সরটি বাঁ দিকে নিয়ে যান।"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"অনুগ্রহ করে সেন্সরের দিকে তাকান।"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"কোনও ফেস শনাক্ত করা যায়নি।"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"ডিভাইসের সামনে ফেসটি স্থির রাখুন।"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"ফেসের হার্ডওয়্যার উপলভ্য নয়৷"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"ফেসের ছাপ নেওয়ার সময়সীমা শেষ৷ আবার চেষ্টা করুন৷"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"ফেস স্টোর করা যাবে না।"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"ফেস অপারেশন বাতিল করা হয়েছে৷"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"অনেকবার চেষ্টা করা হয়েছে। পরে আবার চেষ্টা করুন।"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"অনেকবার চেষ্টা করা হয়েছে৷ ফেস যাচাইকরণ বন্ধ আছে।"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"আবার চেষ্টা করুন।"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"কোনও ফেস নথিভুক্ত করা হয়নি।"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"এই ডিভাইসে ফেস যাচাইকরণ সেন্সর নেই"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> ফেস"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"ফেস আইকন"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"সিঙ্ক সেটিংস পড়ে"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"অ্যাপ্লিকেশানটিকে একটি অ্যাকাউন্টের জন্য সিঙ্ক সেটিংস পড়ার অনুমতি দেয়৷ উদাহরণস্বরূপ, \'পিপল\' অ্যাপ্লিকেশানটি কোনো অ্যাকাউন্টের সাথে সিঙ্ক করা আছে কিনা তা নির্ধারণ করতে পারে৷"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"সমন্বয় চালু এবং বন্ধ করা টগল করুন"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"ওয়াই-ফাই এ ইন্টারনেট অ্যাক্সেস নেই"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"বিকল্পগুলির জন্য আলতো চাপুন"</string>
+    <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">"এই ডিভাইসটি শুধুমাত্র 5GHz এর জন্য আপনার পছন্দ সমর্থন করে না। পরিবর্তে, এই ডিভাইসটি 5GHz ব্যান্ড ব্যবহার করবে।"</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>
@@ -1314,7 +1350,7 @@
     <string name="ext_media_status_ejecting" msgid="5463887263101234174">"সরিয়ে ফেলা হচ্ছে..."</string>
     <string name="ext_media_status_formatting" msgid="1085079556538644861">"ফরম্যাট করা হচ্ছে..."</string>
     <string name="ext_media_status_missing" msgid="5638633895221670766">"ঢোকানো হয় নি"</string>
-    <string name="activity_list_empty" msgid="1675388330786841066">"কোনো সমরূপ কার্যকলাপ খুঁজে পাওয়া যায়নি৷"</string>
+    <string name="activity_list_empty" msgid="1675388330786841066">"কোনো সমরূপ অ্যাক্টিভিটি খুঁজে পাওয়া যায়নি৷"</string>
     <string name="permlab_route_media_output" msgid="6243022988998972085">"মিডিয়া আউটপুট রুট করুন"</string>
     <string name="permdesc_route_media_output" msgid="4932818749547244346">"অ্যাপ্লিকেশানটিকে অন্যান্য বহিরাগত ডিভাইসে মিডিয়া আউটপুট রুট করার অনুমতি দেয়৷"</string>
     <string name="permlab_readInstallSessions" msgid="3713753067455750349">"ইনস্টল সেশন পড়ুন"</string>
@@ -1474,7 +1510,7 @@
     <string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 আঙ্গুলের ছাপ:"</string>
     <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 আঙ্গুলের ছাপ:"</string>
     <string name="activity_chooser_view_see_all" msgid="4292569383976636200">"সবগুলো দেখুন"</string>
-    <string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"কার্যকলাপ বেছে নিন"</string>
+    <string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"অ্যাক্টিভিটি বেছে নিন"</string>
     <string name="share_action_provider_share_with" msgid="5247684435979149216">"এর সাথে শেয়ার করুন"</string>
     <string name="sending" msgid="3245653681008218030">"পাঠানো হচ্ছে..."</string>
     <string name="launchBrowserDefault" msgid="2057951947297614725">"ব্রাউজার লঞ্চ করতে চান?"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index a9e9393..ccf8aa6 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -34,7 +34,7 @@
     <string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
     <string name="mmiError" msgid="5154499457739052907">"Problem sa povezivanjem ili nevažeći MMI kôd."</string>
     <string name="mmiFdnError" msgid="5224398216385316471">"Operacija je ograničena samo na brojeve fiksnog biranja."</string>
-    <string name="mmiErrorWhileRoaming" msgid="762488890299284230">"Nije moguće promijeniti postavke za preusmjeravanje poziva s vašeg telefona dok ste u romingu."</string>
+    <string name="mmiErrorWhileRoaming" msgid="762488890299284230">"Nije moguće promijeniti postavke prosljeđivanja poziva s vašeg telefona dok ste u romingu."</string>
     <string name="serviceEnabled" msgid="8147278346414714315">"Usluga je omogućena."</string>
     <string name="serviceEnabledFor" msgid="6856228140453471041">"Usluga je omogućena za:"</string>
     <string name="serviceDisabled" msgid="1937553226592516411">"Usluga je onemogućena."</string>
@@ -61,7 +61,7 @@
     <string name="ClirMmi" msgid="7784673673446833091">"ID odlaznog poziva"</string>
     <string name="ColpMmi" msgid="3065121483740183974">"Identifikacija povezane linije"</string>
     <string name="ColrMmi" msgid="4996540314421889589">"Ograničenje identifikacije povezane linije"</string>
-    <string name="CfMmi" msgid="5123218989141573515">"Preusmjeravanje poziva"</string>
+    <string name="CfMmi" msgid="5123218989141573515">"Prosljeđivanje poziva"</string>
     <string name="CwMmi" msgid="9129678056795016867">"Poziv na čekanju"</string>
     <string name="BaMmi" msgid="455193067926770581">"Zabrana poziva"</string>
     <string name="PwdMmi" msgid="7043715687905254199">"Promjena lozinke"</string>
@@ -89,17 +89,17 @@
     <string name="EmergencyCallWarningTitle" msgid="813380189532491336">"Hitni pozivi su nedostupni"</string>
     <string name="EmergencyCallWarningSummary" msgid="1899692069750260619">"Nije moguće uspostaviti hitne pozive putem Wi‑Fi mreže"</string>
     <string name="notification_channel_network_alert" msgid="4427736684338074967">"Upozorenja"</string>
-    <string name="notification_channel_call_forward" msgid="2419697808481833249">"Preusmjeravanje poziva"</string>
+    <string name="notification_channel_call_forward" msgid="2419697808481833249">"Prosljeđivanje poziva"</string>
     <string name="notification_channel_emergency_callback" msgid="6686166232265733921">"Način rada za hitni povratni poziv"</string>
     <string name="notification_channel_mobile_data_status" msgid="4575131690860945836">"Status prijenosa podataka na mobilnoj mreži"</string>
     <string name="notification_channel_sms" msgid="3441746047346135073">"SMS poruke"</string>
     <string name="notification_channel_voice_mail" msgid="3954099424160511919">"Poruke govorne pošte"</string>
-    <string name="notification_channel_wfc" msgid="2130802501654254801">"WiFi pozivanje"</string>
+    <string name="notification_channel_wfc" msgid="2130802501654254801">"Pozivanje putem WiFi-ja"</string>
     <string name="notification_channel_sim" msgid="4052095493875188564">"Status SIM-a"</string>
-    <string name="peerTtyModeFull" msgid="6165351790010341421">"Ravnopravni uređaj zatražio TTY PUNI način rada"</string>
-    <string name="peerTtyModeHco" msgid="5728602160669216784">"Ravnopravni uređaj zatražio TTY HCO način rada"</string>
-    <string name="peerTtyModeVco" msgid="1742404978686538049">"Ravnopravni uređaj zatražio TTY VCO način rada"</string>
-    <string name="peerTtyModeOff" msgid="3280819717850602205">"Ravnopravni uređaj zatražio TTY ISKLJUČENI način rada"</string>
+    <string name="peerTtyModeFull" msgid="6165351790010341421">"Ravnopravni uređaj zatražio načina rada TTY FULL"</string>
+    <string name="peerTtyModeHco" msgid="5728602160669216784">"Ravnopravni uređaj zatražio načina rada TTY HCO"</string>
+    <string name="peerTtyModeVco" msgid="1742404978686538049">"Ravnopravni uređaj zatražio načina rada TTY VCO"</string>
+    <string name="peerTtyModeOff" msgid="3280819717850602205">"Ravnopravni uređaj zatražio načina rada TTY OFF"</string>
     <string name="serviceClassVoice" msgid="1258393812335258019">"Govorna"</string>
     <string name="serviceClassData" msgid="872456782077937893">"Podatke"</string>
     <string name="serviceClassFAX" msgid="5566624998840486475">"Faks"</string>
@@ -122,16 +122,16 @@
     <string name="roamingText11" msgid="4154476854426920970">"Oznaka da je uređaj u roamingu uključena"</string>
     <string name="roamingText12" msgid="1189071119992726320">"Oznaka da je uređaj u roamingu ugašena"</string>
     <string name="roamingTextSearching" msgid="8360141885972279963">"Traženje usluge"</string>
-    <string name="wfcRegErrorTitle" msgid="3855061241207182194">"Nije moguće postaviti WiFi pozivanje"</string>
+    <string name="wfcRegErrorTitle" msgid="3855061241207182194">"Nije moguće postaviti pozivanje putem WiFi-ja"</string>
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="3910386316304772394">"Da biste pozivali i slali poruke koristeći WiFi mrežu, prvo zatražite od operatera da postavi tu uslugu. Zatim ponovo uključite WiFi pozivanje u Postavkama. (Kôd greške: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+    <item msgid="3910386316304772394">"Da biste pozivali i slali poruke koristeći WiFi mrežu, prvo zatražite od operatera da postavi tu uslugu. Zatim ponovo uključite pozivanje putem WiFi-ja u Postavkama. (Kôd greške: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="7372514042696663278">"Došlo je do problema prilikom registracije pozivanja putem WiFi mreže kod vašeg operatera: <xliff:g id="CODE">%1$s</xliff:g>"</item>
   </string-array>
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
-    <item msgid="4397097370387921767">"WiFi pozivanje preko operatera %s"</item>
+    <item msgid="4397097370387921767">"Pozivanje putem WiFi-ja preko operatera %s"</item>
   </string-array>
     <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Isključeno"</string>
     <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferira se WiFi"</string>
@@ -239,9 +239,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Način rada u avionu"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Uključen je način rada u avionu"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Način rada u avionu ugašen"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Ušteda baterije"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Ušteda baterije je ISKLJUČENA"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Ušteda baterije je UKLJUČENA"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Postavke"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Pomoć"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Glasovna pomoć"</string>
@@ -279,6 +276,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Lokacija"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"pristupa lokaciji ovog uređaja"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristup lokaciji ovog uređaja?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Ova aplikacija će moći pristupiti lokaciji uređaja samo kada je koristite."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Uvijek dozvoliti da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupi lokaciji uređaja?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Ova aplikacija će uvijek moći pristupiti lokaciji uređaja, čak i kada je ne koristite."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendar"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"pristupa vašem kalendaru"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupi vašem kalendaru?"</string>
@@ -405,12 +405,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ova aplikacija može dodavati, uklanjati ili mijenjati događaje u kalendaru na vašem telefonu. Aplikacija može slati poruke koje mogu izgledati kao da dolazi od vlasnika kalendara ili promijeniti događaje bez obavještenja vlasnika."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pristup dodatnim informacijama o lokaciji"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Dozvoljava aplikaciji pristup dodatnim naredbama pružatelja lokacija. Ovim se aplikaciji može dozvoliti da ometa rad GPS-a ili drugih izvora lokacija."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"pristup preciznoj lokaciji (utvrđena preko mreže i GPS-a)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Ova aplikacija može odrediti vašu lokaciju na osnovu GPS sistema ili mrežnih izvora za određivanje lokacije kao što su predajnici za mobilnu mrežu i WiFi mreže. Ove usluge za određivanje lokacije moraju biti uključene i omogućene na vašem telefonu kako bi ih aplikacija mogla koristiti. To može uzrokovati veću potrošnju baterije."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"pristup tačnoj lokaciji samo u prvom planu"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ova aplikacija može odrediti vašu tačnu lokaciju samo kada je u prvom planu. Ove usluge lokacije moraju biti uključene i dostupne na vašem telefonu da ih aplikacija može koristiti. To može dovesti do povećane potrošnje baterije."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"pristup približnoj lokaciji (utvrđena preko mreže)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ova aplikacija može odrediti vašu lokaciju na osnovu izvora mreže kao što su predajnici za mobilnu mrežu i WiFi mreže. Ove usluge za određivanje lokacije moraju biti uključene i omogućene na vašem tabletu kako bi ih aplikacija mogla koristiti."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ova aplikacija može odrediti vašu lokaciju na osnovu izvora mreže kao što su predajnici za mobilnu mrežu i WiFi mreže. Ove usluge za određivanje lokacije moraju biti uključene i omogućene na vašem TV-u kako bi ih aplikacija mogla koristiti."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ova aplikacija može odrediti vašu lokaciju na osnovu izvora mreže kao što su predajnici za mobilnu mrežu i WiFi mreže. Ove usluge za određivanje lokacije moraju biti uključene i omogućene na vašem telefonu kako bi ih aplikacija mogla koristiti."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"pristupi tačnoj lokaciji u pozadini"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ova aplikacija može odrediti vašu tačnu lokaciju u svakom trenutku kada je u pozadini. Ove usluge za određivanje lokacije moraju biti uključene i dostupne na vašem telefonu da ih aplikacija može koristiti. To može izazvati povećanje potrošnje baterije."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"izmjene postavki zvuka"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Omogućava aplikaciji izmjenu općih postavki zvuka, kao što su jačina zvuka i izbor izlaznog zvučnika."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"snimanje audiozapisa"</string>
@@ -458,7 +460,7 @@
     <string name="permlab_accessNetworkState" msgid="4951027964348974773">"prikaz mrežnih veza"</string>
     <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Omogućava aplikaciji pregled informacija o mrežnim vezama, npr. koje mreže postoje i koje su povezane."</string>
     <string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ima potpuni pristup mreži"</string>
-    <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Omogućava aplikaciji kreiranje spojnih tačaka sa mrežom i korištenje prilagođenih mrežnih protokola. Preglednik i druge aplikacije omogućavaju slanje podataka na internet, tako da ovo odobrenje nije potrebno za vršenje te radnje."</string>
+    <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Omogućava aplikaciji kreiranje spojnih tačaka s mrežom i korištenje prilagođenih mrežnih protokola. Preglednik i druge aplikacije omogućavaju slanje podataka na internet, tako da ovo odobrenje nije potrebno za vršenje te radnje."</string>
     <string name="permlab_changeNetworkState" msgid="958884291454327309">"izmjene povezivanja na mrežu"</string>
     <string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Dozvoljava aplikaciji izmjenu stanja mrežne povezanosti."</string>
     <string name="permlab_changeTetherState" msgid="5952584964373017960">"izmjene podijeljenog povezivanja"</string>
@@ -502,11 +504,11 @@
     <string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Prst je uklonjen presporo. Pokušajte ponovo."</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nije prepoznat"</string>
+    <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nije prepoznato"</string>
     <string name="fingerprint_authenticated" msgid="5309333983002526448">"Otisak prsta je potvrđen"</string>
     <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardver za otisak prsta nije dostupan."</string>
     <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Otisak prsta se ne može pohraniti. Uklonite postojeći otisak prsta."</string>
-    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Vremensko ograničenje za otisak prsta je isteklo. Pokušajte ponovo."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Vrijeme za prepoznavanje otiska prsta je isteklo. Pokušajte ponovo."</string>
     <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Radnja sa otiskom prsta je otkazana."</string>
     <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Korisnik je otkazao radnju s otiskom prsta."</string>
     <string name="fingerprint_error_lockout" msgid="5536934748136933450">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
@@ -518,6 +520,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona za otisak prsta"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"upravljanje hardverom za autentifikaciju licem"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Omogućava aplikaciji korištenje metoda za dodavanje i brisanje šablona lica za upotrebu."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"upotreba hardvera za autentifikaciju licem"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Omogućava aplikaciji da za autentifikaciju koristi hardver za autentifikaciju licem"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Obrada lica nije uspjela. Pokušajte ponovo."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Lice je presvijetlo. Pokušajte s manje svjetla."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Lice je pretamno. Pojačajte izvor svjetlosti."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Odmaknite senzor od lica."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Približite senzor licu."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Pomjerite senzor prema gore."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Pomjerite senzor prema dolje."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Pomjerite senzor udesno."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Pomjerite senzor ulijevo."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Gledajte u senzor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nije pronađeno nijedno lice."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mirno držite lice ispred uređaja."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardver za prepoznavanje lica nije dostupan."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Vrijeme za prepoznavanje lica je isteklo. Pokušajte ponovo."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Nije moguće pohraniti lice."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Prepoznavanje lica je otkazano."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Previše pokušaja. Autentifikacija lica onemogućena."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Pokušajte ponovo."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nije prijavljeno nijedno lice."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Ovaj uređaj nema senzor za autentifikaciju lica"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona lica"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"čitanje postavki za sinhroniziranje"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Omogućava aplikaciji čitanje postavki sinhroniziranja za račun. Naprimjer, ovim se može utvrditi da li je aplikacija People sinhronizirana sa računom."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"aktiviranje/deaktiviranje sinhroniziranja"</string>
@@ -1199,6 +1232,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"WiFi nema pristup internetu"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Dodirnite za opcije"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Promjene postavki vaše pristupne tačke"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Opseg vaše pristupne tačke je promijenjen."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Ovaj uređaj ne podržava vašu postavku za mreže od isključivo 5GHz. Uređaj će koristiti opseg of 5GHz kada je dostupan."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Prebačeno na: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, uređaj koristi mrežu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata usluge."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Prebačeno iz mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> u <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mrežu"</string>
@@ -1223,7 +1259,7 @@
     <string name="accept" msgid="1645267259272829559">"Prihvati"</string>
     <string name="decline" msgid="2112225451706137894">"Odbijte"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Pozivnica poslana"</string>
-    <string name="wifi_p2p_invitation_to_connect_title" msgid="4958803948658533637">"Poziv za povezivanje"</string>
+    <string name="wifi_p2p_invitation_to_connect_title" msgid="4958803948658533637">"Pozivnica za povezivanje"</string>
     <string name="wifi_p2p_from_message" msgid="570389174731951769">"Pošiljalac:"</string>
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Prima:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Unesite potrebni PIN:"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 686d614..6f79909 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -133,8 +133,8 @@
     <item msgid="4397097370387921767">"Trucades per Wi-Fi %s"</item>
   </string-array>
     <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desactivat"</string>
-    <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferència per a la Wi-Fi"</string>
-    <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferència per a dades mòbils"</string>
+    <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferència per la Wi-Fi"</string>
+    <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferència per dades mòbils"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Només Wi-Fi"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no s\'ha desviat"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mode d\'avió"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Mode d\'avió activat"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Mode d\'avió desactivat"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Estalvi de bateria"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"El mode d\'estalvi de bateria està desactivat"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"El mode d\'estalvi de bateria està activat"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Configuració"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assistència"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Assist. per veu"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Ubicació"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"accedir a la ubicació del dispositiu"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a la ubicació del dispositiu?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"L\'aplicació només tindrà accés a la ubicació quan l\'estiguis utilitzant."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi sempre a la ubicació del dispositiu?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"L\'aplicació tindrà accés sempre a la ubicació, fins i tot quan no l\'estiguis utilitzant."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendari"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"accedir al calendari"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi al calendari?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Aquesta aplicació pot afegir, suprimir i canviar esdeveniments del calendari al telèfon. També pot enviar missatges que sembli que provenen dels propietaris del calendari o canviar esdeveniments sense notificar-los-ho."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accedir a ordres del proveïdor d\'ubicació addicionals"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permet que l\'aplicació accedeixi a ordres addicionals del proveïdor d\'ubicacions; per tant, és possible que l\'aplicació pugui interferir en el funcionament del GPS o d\'altres fonts d\'ubicacions."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"accedir a la ubicació precisa (basada en el GPS i en la xarxa)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Aquesta aplicació pot obtenir la teva ubicació a partir de sistemes GPS i de fonts de xarxa, com ara torres de telefonia mòbil i xarxes Wi-Fi. Aquests serveis d\'ubicació han d\'estar activats i disponibles al telèfon perquè l\'aplicació els pugui utilitzar, i això pot fer que el consum de bateria augmenti."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"accedeix a la ubicació exacta només en primer pla"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Aquesta aplicació pot obtenir la teva ubicació exacta només quan està en primer pla. Aquests serveis d\'ubicació han d\'estar activats i disponibles al telèfon perquè l\'aplicació els pugui utilitzar, i això pot fer que el consum de bateria augmenti."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"accedir a la ubicació aproximada (basada en la xarxa)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Aquesta aplicació pot obtenir la teva ubicació a partir de fonts de xarxa, com ara torres de telefonia mòbil i xarxes Wi-Fi. Aquests serveis d\'ubicació han d\'estar activats i disponibles a la tauleta perquè l\'aplicació els pugui utilitzar."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Aquesta aplicació pot obtenir la teva ubicació a partir de fonts de xarxa, com ara torres de telefonia mòbil i xarxes Wi-Fi. Aquests serveis d\'ubicació han d\'estar activats i disponibles al televisor perquè l\'aplicació els pugui utilitzar."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Aquesta aplicació pot obtenir la teva ubicació a partir de fonts de xarxa, com ara torres de telefonia mòbil i xarxes Wi-Fi. Aquests serveis d\'ubicació han d\'estar activats i disponibles al telèfon perquè l\'aplicació els pugui utilitzar."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"accedeix a la ubicació exacta en segon pla"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Aquesta aplicació pot obtenir la teva ubicació exacta sempre que estigui en segon pla. Aquests serveis d\'ubicació han d\'estar activats i disponibles al telèfon perquè l\'aplicació els pugui utilitzar, i això pot fer que el consum de bateria augmenti."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"canviar la configuració d\'àudio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permet que l\'aplicació modifiqui la configuració d\'àudio general, com ara el volum i l\'altaveu de sortida que es fa servir."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"gravar àudio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona d\'empremta digital"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"gestiona el maquinari d\'autenticació facial"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Permet que l\'aplicació afegeixi i suprimeixi plantilles de cares que es puguin fer servir."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"utilitza el maquinari d\'autenticació facial"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permet que l\'aplicació faci servir maquinari d\'autenticació facial"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Error en processar la cara. Torna-ho a provar."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"La cara brilla massa. Prova amb menys llum."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"La cara és massa fosca. Prova amb més llum."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Allunya el sensor de la cara."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Apropa el sensor a la cara."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Puja el sensor més amunt."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Baixa el sensor més avall."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Mou el sensor cap a la dreta."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Mou el sensor cap a l\'esquerra."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Mira el sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"No s\'ha detectat cap cara."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantén la cara quieta davant del dispositiu."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Maquinari de reconeixement facial no disponible."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"S\'ha esgotat el temps d\'espera. Torna-ho a provar."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"La cara no es pot desar."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"S\'ha cancel·lat el reconeixement facial."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Massa intents. Torna-ho a provar més tard."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Massa intents. Autenticació facial desactivada."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Torna-ho a provar."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"No s\'ha registrat cap cara."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Aquest dispositiu no té sensor d\'autenticació facial"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Cara <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Icona facial"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"llegir la configuració de sincronització"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet que l\'aplicació llegeixi la configuració de sincronització d\'un compte. Per exemple, això pot determinar que l\'aplicació Persones estigui sincronitzada amb un compte."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar o desactivar la sincronització"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"La Wi-Fi no té accés a Internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toca per veure les opcions"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Canvis en la configuració del punt d\'accés Wi-Fi"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Ha canviat la teva banda del punt d\'accés Wi-Fi."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Aquest dispositiu no admet utilitzar exclusivament una banda de 5 GHz. El dispositiu utilitzarà una banda de 5 GHz quan estigui disponible."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Actualment en ús: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"El dispositiu utilitza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> en cas que <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tingui accés a Internet. És possible que s\'hi apliquin càrrecs."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Abans es feia servir la xarxa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>; ara s\'utilitza <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1253,9 +1289,9 @@
     <string name="usb_power_notification_message" msgid="4647527153291917218">"S\'està carregant el dispositiu connectat. Toca per veure més opcions."</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"S\'ha detectat un accessori d\'àudio analògic"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"El dispositiu connectat no és compatible amb aquest telèfon. Toca per obtenir més informació."</string>
-    <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuració USB activada"</string>
+    <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuració per USB activada"</string>
     <string name="adb_active_notification_message" msgid="7463062450474107752">"Toca per desactivar la depuració per USB"</string>
-    <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecciona per desactivar la depuració USB"</string>
+    <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecciona per desactivar la depuració per USB"</string>
     <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"S\'està creant l\'informe d\'errors…"</string>
     <string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Vols compartir l\'informe d\'errors?"</string>
     <string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"S\'està compartint l\'informe d\'errors…"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index ff59be1..40b5e29 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -58,8 +58,8 @@
     </plurals>
     <string name="imei" msgid="2625429890869005782">"IMEI"</string>
     <string name="meid" msgid="4841221237681254195">"MEID"</string>
-    <string name="ClipMmi" msgid="6952821216480289285">"Příchozí identifikace volajícího"</string>
-    <string name="ClirMmi" msgid="7784673673446833091">"Odchozí identifikace volajícího"</string>
+    <string name="ClipMmi" msgid="6952821216480289285">"Příchozí ID volajícího"</string>
+    <string name="ClirMmi" msgid="7784673673446833091">"Odchozí ID volajícího"</string>
     <string name="ColpMmi" msgid="3065121483740183974">"ID připojené linky"</string>
     <string name="ColrMmi" msgid="4996540314421889589">"Omezení ID připojené linky"</string>
     <string name="CfMmi" msgid="5123218989141573515">"Přesměrování hovorů"</string>
@@ -73,12 +73,12 @@
     <string name="RuacMmi" msgid="7827887459138308886">"Odmítnutí nevyžádaných obtěžujících hovorů"</string>
     <string name="CndMmi" msgid="3116446237081575808">"Doručení volaného čísla"</string>
     <string name="DndMmi" msgid="1265478932418334331">"Nerušit"</string>
-    <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Ve výchozím nastavení je identifikace volajícího omezena. Příští hovor: Omezeno"</string>
-    <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Ve výchozím nastavení je identifikace volajícího omezena. Příští hovor: Neomezeno"</string>
-    <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Ve výchozím nastavení není identifikace volajícího omezena. Příští hovor: Omezeno"</string>
-    <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Ve výchozím nastavení není identifikace volajícího omezena. Příští hovor: Neomezeno"</string>
+    <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Ve výchozím nastavení je funkce ID volajícího omezena. Příští hovor: Omezeno"</string>
+    <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Ve výchozím nastavení je funkce ID volajícího omezena. Příští hovor: Neomezeno"</string>
+    <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Ve výchozím nastavení není funkce ID volajícího omezena. Příští hovor: Omezeno"</string>
+    <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Ve výchozím nastavení není funkce ID volajícího omezena. Příští hovor: Neomezeno"</string>
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"Služba není zřízena."</string>
-    <string name="CLIRPermanent" msgid="3377371145926835671">"Nastavení identifikace volajícího nesmíte měnit."</string>
+    <string name="CLIRPermanent" msgid="3377371145926835671">"Nastavení ID volajícího nesmíte měnit."</string>
     <string name="RestrictedOnDataTitle" msgid="5221736429761078014">"Není k dispozici žádná mobilní datová služba"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="6855466023161191166">"Tísňová volání jsou nedostupná"</string>
     <string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"Hlasová volání nejsou k dispozici"</string>
@@ -242,9 +242,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Režim Letadlo"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Režim Letadlo je ZAPNUTÝ"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Režim Letadlo je VYPNUTÝ"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Spořič baterie"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Spořič baterie je VYPNUTÝ"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Spořič baterie je ZAPNUTÝ"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Nastavení"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Asistence"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Hlas. asistence"</string>
@@ -282,6 +279,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Poloha"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"přístup k poloze tohoto zařízení"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k poloze tohoto zařízení?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikace bude mít přístup k poloze, pouze když ji budete používat."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k poloze tohoto zařízení?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikace bude mít přístup k poloze vždy, i když ji nebudete používat."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendář"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"přístup ke kalendáři"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup ke kalendáři?"</string>
@@ -408,12 +408,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Tato aplikace může přidat, odstranit či změnit události v kalendáři uložené v telefonu. Může také odesílat zprávy, které budou zdánlivě přicházet od vlastníka kalendáře, nebo upravovat události bez vědomí vlastníka."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"přístup k dalším příkazům poskytovatele polohy"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Umožňuje aplikaci přístup k dalším příkazům poskytovatele polohy. To aplikaci umožní zasahovat do fungování systému GPS a dalších zdrojů polohy."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"přístup k přesné poloze (pomocí GPS a sítě)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Tato aplikace může zjistit vaši polohu podle GPS a zdrojů polohy sítě, jako jsou vysílací věže nebo sítě Wi-Fi. Aby tyto služby určování polohy mohla aplikace používat, musí být v telefonu dostupné a musí být zapnuté. Tyto služby mohou způsobit rychlejší vybíjení baterie."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"přístup k přesné poloze jen na popředí"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Tato aplikace může zjistit vaši přesnou polohu, jen když běží na popředí. Aby tyto služby určování polohy mohla aplikace používat, musí být v telefonu dostupné a musí být zapnuté. Tyto služby mohou způsobit rychlejší vybíjení baterie."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"přístup k přibližné poloze (pomocí sítě)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Tato aplikace může zjistit vaši polohu podle zdrojů sítě, jako jsou vysílací věže nebo sítě Wi-Fi. Aby tyto služby určování polohy mohla aplikace používat, musí být v tabletu dostupné a musí být zapnuté."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Tato aplikace může zjistit vaši polohu podle zdrojů sítě, jako jsou vysílací věže nebo sítě Wi-Fi. Aby tyto služby určování polohy mohla aplikace používat, musí být v televizi dostupné a musí být zapnuté."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Tato aplikace může zjistit vaši polohu podle zdrojů sítě, jako jsou vysílací věže nebo sítě Wi-Fi. Aby tyto služby určování polohy mohla aplikace používat, musí být v telefonu dostupné a musí být zapnuté."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"přístup k přesné poloze na pozadí"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Tato aplikace může zjistit vaši přesnou polohu vždy, když běží na pozadí. Aby tyto služby určování polohy mohla aplikace používat, musí být v telefonu dostupné a musí být zapnuté. Tyto služby mohou způsobit rychlejší vybíjení baterie."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"změna nastavení zvuku"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Umožňuje aplikaci změnit globální nastavení zvuku, například hlasitost či reproduktor pro výstup zvuku."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"nahrávání zvuku"</string>
@@ -521,6 +523,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona otisku prstů"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"správa hardwaru k ověření obličeje"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Umožňuje aplikaci volat metody k přidání a smazání šablon obličeje, které budou použity."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"použití hardwaru k ověření obličeje"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Umožňuje aplikaci provést ověření pomocí hardwaru k ověření obličeje"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Zpracování obličeje se nezdařilo. Zkuste to znovu."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Obličej je moc jasný. Zkuste to v menším světle."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Obličej je moc tmavý. Odkryjte zdroj světla."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Oddalte senzor od obličeje."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Posuňte senzor blíž k obličeji."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Posuňte senzor výš."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Posuňte senzor níž."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Posuňte senzor doprava."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Posuňte senzor doleva."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Podívejte se do senzoru."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nebyl rozpoznán žádný obličej."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Držte obličej nehybně před zařízením."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Není k dispozici hardware ke snímání obličeje."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Limit ověření obličeje vypršel. Zkuste to znovu."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Obličej nelze uložit."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Operace snímání obličeje byla zrušena."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Příliš mnoho pokusů. Zkuste to později."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Příliš mnoho pokusů. Ověření obličeje je zakázáno."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Zkuste to znovu."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Není zaregistrován žádný obličej."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Toto zařízení nemá snímač ověření obličeje"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Obličej <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona obličeje"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"čtení nastavení synchronizace"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Umožňuje aplikaci číst nastavení synchronizace v účtu. Může například určit, zda je s účtem synchronizována aplikace Lidé."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"vypnutí nebo zapnutí synchronizace"</string>
@@ -1219,6 +1252,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi nemá přístup k internetu"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Klepnutím zobrazíte možnosti"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Změny nastavení hotspotu"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Pásmo hotspotu se změnilo."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Toto zařízení nepodporuje vaše nastavení jen 5GHz pásma. Zařízení použije pásmo 5 GHz, jen když bude dostupné."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Přechod na síť <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Když síť <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nebude mít přístup k internetu, zařízení použije síť <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Mohou být účtovány poplatky."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Přechod ze sítě <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na síť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1299,7 +1335,7 @@
     <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Připojené zařízení není s tímto telefonem kompatibilní. Klepnutím zobrazíte další informace."</string>
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Ladění přes USB připojeno"</string>
     <string name="adb_active_notification_message" msgid="7463062450474107752">"Klepnutím vypnete ladění přes USB"</string>
-    <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Vyberte, chcete-li zakázat ladění USB."</string>
+    <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Vyberte, chcete-li zakázat ladění přes USB."</string>
     <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Vytváření zprávy o chybě…"</string>
     <string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Sdílet zprávu o chybě?"</string>
     <string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Sdílení zprávy o chybě…"</string>
@@ -1803,7 +1839,7 @@
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Sbalit"</string>
     <string name="zen_mode_feature_name" msgid="5254089399895895004">"Nerušit"</string>
-    <string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"Období klidu"</string>
+    <string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"Doba klidu"</string>
     <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Večer v pracovním týdnu"</string>
     <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Víkend"</string>
     <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Událost"</string>
@@ -1884,7 +1920,7 @@
     <string name="app_category_maps" msgid="5878491404538024367">"Mapy a navigace"</string>
     <string name="app_category_productivity" msgid="3742083261781538852">"Produktivita"</string>
     <string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"Úložiště zařízení"</string>
-    <string name="adb_debugging_notification_channel_tv" msgid="5537766997350092316">"Ladění USB"</string>
+    <string name="adb_debugging_notification_channel_tv" msgid="5537766997350092316">"Ladění přes USB"</string>
     <string name="time_picker_hour_label" msgid="2979075098868106450">"hodina"</string>
     <string name="time_picker_minute_label" msgid="5168864173796598399">"minuta"</string>
     <string name="time_picker_header_text" msgid="143536825321922567">"Nastavení času"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index a0e128c..947754f 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Flytilstand"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Flytilstand er TIL"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flytilstand er slået FRA"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Batterisparefunktion"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Batterisparefunktion er slået FRA"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Batterisparefunktion er slået TIL"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Indstillinger"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assistance"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Taleassistent"</string>
@@ -270,12 +267,15 @@
     <string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
     <string name="user_owner_label" msgid="8836124313744349203">"Skift til personlig profil"</string>
     <string name="managed_profile_label" msgid="8947929265267690522">"Skift til arbejdsprofil"</string>
-    <string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktpersoner"</string>
+    <string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakter"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"have adgang til dine kontaktpersoner"</string>
     <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til dine kontaktpersoner?"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Placering"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"få adgang til enhedens placering"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til enhedens placering?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Appen har kun adgang til placeringen, når du bruger appen."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Skal &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; altid have adgang til enhedens placering?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Appen har altid adgang til placeringen, også selvom du ikke bruger appen."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"have adgang til din kalender"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til din kalender?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Denne app kan tilføje, fjerne eller ændre kalenderbegivenheder på din telefon. Denne app kan sende meddelelser, der kan se ud, som om de kommer fra kalenderejere, eller ændre begivenheder uden at give ejeren besked."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få adgang til yderligere kommandoer for placeringsudbyder"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Tillader, at appen kan få adgang til yderligere kommandoer for placeringsudbydere. Dette kan gøre det muligt for appen at forstyrre GPS-funktionen eller andre placeringskilder."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"få adgang til nøjagtig placering (baseret på GPS og netværk)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Denne app kan bestemme din placering ved hjælp af netværkskilder, som f.eks. mobilmaster og Wi-Fi-netværk. Disse placeringstjenester skal være aktiverede og tilgængelige på din telefon, før appen kan bruge dem. Dette kan øge batteriforbruget."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"få kun adgang til nøjagtig placering i forgrunden"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Denne app kan kun få din nøjagtige placering, når den er i forgrunden. Disse placeringstjenester skal være aktiverede og tilgængelige på din telefon, før appen kan bruge dem. Dette kan øge batteriforbruget."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"få adgang til omtrentlig placering (netværksbaseret)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Denne app kan bestemme din placering ved hjælp af netværkskilder, som f.eks. mobilmaster og Wi-Fi-netværk. Disse placeringstjenester skal være aktiverede og tilgængelige på din tablet, før appen kan bruge dem."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Denne app kan bestemme din placering ved hjælp af netværkskilder, som f.eks. mobilmaster og Wi-Fi-netværk. Disse placeringstjenester skal være aktiverede og tilgængelige på dit fjernsyn, før appen kan bruge dem."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Denne app kan bestemme din placering ved hjælp af netværkskilder, som f.eks. mobilmaster og Wi-Fi-netværk. Disse placeringstjenester skal være aktiverede og tilgængelige på din telefon, før appen kan bruge dem."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"få adgang til nøjagtig placering i baggrunden"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Denne app kan få din nøjagtige placering, når den er i baggrunden. Disse placeringstjenester skal være aktiverede og tilgængelige på din telefon, før appen kan bruge dem. Dette kan øge batteriforbruget."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"skifte dine lydindstillinger"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Tillader, at appen kan ændre globale lydindstillinger, som f.eks. lydstyrke og hvilken højttaler der bruges til output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"optage lyd"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon for fingeraftryk"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"administrer hardware til ansigtsgenkendelse"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Tillader, at appen kan bruge metoder til at tilføje og slette ansigtsskabeloner."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"brug hardware til ansigtsgenkendelse"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Tillader, at appen bruger ansigtsgenkendelseshardware til godkendelse"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Ansigtet kunne ikke behandles. Prøv igen."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Ansigtet er for lyst. Prøv i svagere belysning."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Ansigtet er for mørkt. Ryk tættere på en lyskilde."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Flyt sensoren længere væk fra ansigtet."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Ryk sensoren tættere på ansigtet."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Flyt sensoren højere op."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Flyt sensoren længere ned."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Flyt sensoren til højre."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Flyt sensoren til venstre."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Kig på sensoren."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Der er ikke registreret noget ansigt."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Hold ansigtet stille foran enheden."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardwaren til ansigtsregistrering er ikke klar."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Ansigtsgenkendelse fik timeout. Prøv igen."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Ansigtet kan ikke gemmes."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Ansigtshandlingen blev annulleret."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Du har prøvet for mange gange. Prøv igen senere."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"For mange forsøg – Ansigtsgenkendelse deaktiveret."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Prøv igen."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Der er ikke registreret nogen ansigter."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Denne enhed har ingen sensor til ansigtsgenkendelse"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Ansigt <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ansigt"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"læse indstillinger for synkronisering"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Tillader, at appen kan læse synkroniseringsindstillingerne for en konto. Denne tilladelse kan f.eks. fastslå, om appen Personer er synkroniseret med en konto."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"slå synkronisering til og fra"</string>
@@ -587,7 +620,7 @@
     <string name="policylab_resetPassword" msgid="4934707632423915395">"Skifte skærmlås"</string>
     <string name="policydesc_resetPassword" msgid="1278323891710619128">"Skifter skærmlås"</string>
     <string name="policylab_forceLock" msgid="2274085384704248431">"Låse skærmen"</string>
-    <string name="policydesc_forceLock" msgid="1141797588403827138">"Kontrollerer, hvordan og hvornår skærmen låses."</string>
+    <string name="policydesc_forceLock" msgid="1141797588403827138">"Administrerer, hvordan og hvornår skærmen låses."</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Slette alle data"</string>
     <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Slet din tablets data uden varsel ved at gendanne fabriksindstillingerne."</string>
     <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Slet tv\'ets data uden varsel ved at nulstille til fabrinksindstillingerne."</string>
@@ -719,7 +752,7 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Hjem"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Arbejde"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Andet"</string>
-    <string name="quick_contacts_not_available" msgid="746098007828579688">"Der blev ikke fundet nogen applikation, som kan vise denne kontaktperson."</string>
+    <string name="quick_contacts_not_available" msgid="746098007828579688">"Der blev ikke fundet nogen applikation, som kan vise denne kontakt."</string>
     <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Angiv pinkode"</string>
     <string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Angiv PUK- og pinkode"</string>
     <string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK-kode"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi-netværket har ikke internetadgang"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tryk for at se valgmuligheder"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Ændringer af dine indstillinger for hotspot"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Dit hotspotbånd er ændret."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Denne enhed understøtter ikke din præference om kun 5 GHz. Denne enhed vil i stedet bruge 5 GHz-båndet, når det er muligt."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Der blev skiftet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Enheden benytter <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, når der ikke er internetadgang via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Der opkræves muligvis betaling."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Der blev skiftet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index be50d36..8dc0ba4 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Flugmodus"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Flugmodus ist AN."</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flugmodus ist AUS."</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Energiesparmodus"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Energiesparmodus ist aus"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Energiesparmodus ist an"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Einstellungen"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assistent"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Sprachassistent"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Standort"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"auf den Standort deines Geräts zugreifen"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, den Gerätestandort abzurufen?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Die App hat nur Zugriff auf den Gerätestandort, wenn du sie verwendest."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; immer erlauben, den Gerätestandort abzurufen?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Die App hat immer Zugriff auf den Gerätestandort, auch wenn du sie gerade nicht verwendest."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"auf deinen Kalender zugreifen"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; Zugriff auf deinen Kalender erlauben?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Diese App kann Kalendertermine auf deinem Smartphone hinzufügen, entfernen oder ändern. Diese App kann Nachrichten senden, die scheinbar von Kalendereigentümern stammen, oder Termine ohne Benachrichtigung der Eigentümer ändern."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Auf zusätzliche Dienstanbieterbefehle für Standort zugreifen"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ermöglicht der App, auf zusätzliche Standortanbieterbefehle zuzugreifen. Damit könnte die App die Funktionsweise von GPS oder anderen Standortquellen beeinträchtigen."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"Auf genauen Standort zugreifen (GPS- und netzwerkbasiert)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Diese App kann deinen Standort über GPS oder mithilfe von Netzwerkstandortquellen wie Mobilfunkmasten und WLAN ermitteln. Die App kann diese Standortdienste nur verwenden, wenn sie auf deinem Smartphone aktiviert und verfügbar sind. Hierdurch kann sich der Akkuverbrauch erhöhen."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"Nur bei Ausführung im Vordergrund auf den genauen Standort zugreifen"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Diese App kann deinen genauen Standort nur dann ermitteln, wenn sie im Vordergrund ausgeführt wird. Die App kann diese Standortdienste nur verwenden, wenn sie auf deinem Smartphone aktiviert und verfügbar sind. Hierdurch kann sich der Akkuverbrauch erhöhen."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"Auf den ungefähren Standort zugreifen (netzwerkbasiert)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Diese App kann deinen Standort mithilfe von Netzwerkquellen wie Mobilfunkmasten und WLAN ermitteln. Die App kann diese Standortdienste nur verwenden, wenn sie auf deinem Tablet aktiviert und verfügbar sind."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Diese App kann deinen Standort mithilfe von Netzwerkquellen wie Mobilfunkmasten und WLAN ermitteln. Die App kann diese Standortdienste nur verwenden, wenn sie auf deinem Fernseher aktiviert und verfügbar sind."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Diese App kann deinen Standort mithilfe von Netzwerkquellen wie Mobilfunkmasten und WLAN ermitteln. Die App kann diese Standortdienste nur verwenden, wenn sie auf deinem Smartphone aktiviert und verfügbar sind."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"im Hintergrund auf den genauen Standort zugreifen"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Diese App kann deinen genauen Standort jederzeit im Hintergrund ermitteln. Die App kann diese Standortdienste nur verwenden, wenn sie auf deinem Smartphone aktiviert und verfügbar sind. Hierdurch kann sich der Akkuverbrauch erhöhen."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"Audio-Einstellungen ändern"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ermöglicht der App, globale Audio-Einstellungen zu ändern, etwa die Lautstärke und den Lautsprecher für die Ausgabe."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"Audio aufnehmen"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerabdruck-Symbol"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"Gesichtserkennungshardware verwalten"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Ermöglicht der App, Methoden zum Hinzufügen oder Entfernen von Gesichtsvorlagen anzuwenden."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"Gesichtserkennungshardware verwenden"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Ermöglicht der App, für die Authentifizierung Gesichtserkennungshardware zu verwenden"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Kann Gesicht nicht verarbeiten. Versuch es erneut."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Gesicht zu hell. Bei weniger Licht versuchen."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Gesicht zu dunkel. Bei mehr Licht versuchen."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Sensor weiter weg vom Gesicht halten."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Sensor näher an das Gesicht halten."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Sensor höher halten."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Sensor niedriger halten."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Sensor nach rechts bewegen."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Sensor nach links bewegen."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Blick auf den Sensor richten."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Kein Gesicht erkannt."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Gerät vor das Gesicht halten und nicht bewegen."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware zur Gesichtserkennung nicht verfügbar."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Zeitüberschreitung für Gesicht. Versuch es erneut."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Gesicht kann nicht gespeichert werden."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Gesichtserkennung abgebrochen."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Zu viele Versuche. Versuch es später noch einmal."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Zu viele Versuche. Gesichtserkennung deaktiviert."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Versuch es noch einmal."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Kein Gesicht erfasst."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Dieses Gerät hat keinen Gesichtserkennungssensor"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Gesicht <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Gesichtssymbol"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"Synchronisierungseinstellungen lesen"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ermöglicht der App, die Synchronisierungseinstellungen eines Kontos zu lesen. Beispielsweise kann damit festgestellt werden, ob Kontakte mit einem Konto synchronisiert werden."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"Synchronisierung aktivieren oder deaktivieren"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"WLAN hat keinen Internetzugriff"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Für Optionen tippen"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Änderungen an deinen Hotspot-Einstellungen"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Dein Hotspot-Band hat sich geändert."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Dieses Gerät unterstützt die ausschließliche Nutzung von 5 GHz nicht. Es greift aber immer auf das 5-GHz-Band zurück, wenn dieses verfügbar ist."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Zu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> gewechselt"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Auf dem Gerät werden <xliff:g id="NEW_NETWORK">%1$s</xliff:g> genutzt, wenn über <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> kein Internet verfügbar ist. Eventuell fallen Gebühren an."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Von \"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>\" zu \"<xliff:g id="NEW_NETWORK">%2$s</xliff:g>\" gewechselt"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 8444d05..c6acc9e 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Λειτουργία πτήσης"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Η λειτουργία πτήσης είναι ενεργοποιημένη."</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Λειτ. πτήσης είναι ανενεργή"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Εξοικονόμηση μπαταρίας"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Η εξοικονόμηση μπαταρίας είναι ΑΠΕΝΕΡΓΟΠΟΙΗΜΕΝΗ"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Η Εξοικονόμηση μπαταρίας είναι ΕΝΕΡΓΗ"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Ρυθμίσεις"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Βοήθεια"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Φων.υποβοηθ."</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Τοποθεσία"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"έχει πρόσβαση στην τοποθεσία της συσκευής"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; να έχει πρόσβαση στην τοποθεσία αυτής της συσκευής;"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Η εφαρμογή θα έχει πρόσβαση στην τοποθεσία μόνο κατά τη διάρκεια χρήσης της εφαρμογής."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Να επιτρέπεται πάντα στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; να έχει πρόσβαση στην τοποθεσία αυτής της συσκευής;"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Η εφαρμογή θα έχει πάντα πρόσβαση στην τοποθεσία, ακόμα και όταν δεν χρησιμοποιείτε την εφαρμογή."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Ημερολόγιο"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"έχει πρόσβαση στο ημερολόγιό σας"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; να έχει πρόσβαση στο ημερολόγιό σας;"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Αυτή η εφαρμογή μπορεί να προσθέσει, να καταργήσει ή να αλλάξει συμβάντα ημερολογίου στο τηλέφωνό σας. Αυτή η εφαρμογή μπορεί να στέλνει μηνύματα τα οποία μπορεί να φαίνεται ότι προέρχονται από κατόχους ημερολογίου ή να αλλάζει συμβάντα χωρίς να ειδοποιεί τους κατόχους."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"έχει πρόσβαση σε επιπλέον εντολές παρόχου τοποθεσίας"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Επιτρέπει στην εφαρμογή την πρόσβαση σε επιπλέον εντολές παρόχου τοποθεσίας. Αυτό μπορεί να δώσει τη δυνατότητα στην εφαρμογή να παρέμβει στη λειτουργία του GPS ή άλλων πηγών τοποθεσίας."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"έχει πρόσβαση στην ακριβή τοποθεσία (με βάση το GPS και το δίκτυο)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Αυτή η εφαρμογή μπορεί να εντοπίσει την τοποθεσία σας βάσει πηγών τοποθεσίας GPS ή δικτύου, όπως κεραίες κινητής τηλεφωνίας και δίκτυα Wi-Fi. Αυτές οι υπηρεσίες τοποθεσίας θα πρέπει να είναι ενεργοποιημένες και διαθέσιμες στο τηλέφωνό σας, προκειμένου να μπορεί να τις χρησιμοποιήσει η εφαρμογή. Με την ενεργοποίηση αυτής της ρύθμισης, μπορεί να αυξηθεί η κατανάλωση μπαταρίας."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"πρόσβαση στην ακριβή τοποθεσία μόνο στο προσκήνιο"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Αυτή η εφαρμογή μπορεί να ανιχνεύσει την ακριβή τοποθεσία σας όταν βρίσκεται στο προσκήνιο. Αυτές οι υπηρεσίες τοποθεσίας θα πρέπει να είναι ενεργοποιημένες και διαθέσιμες στο τηλέφωνό σας, προκειμένου να μπορεί να τις χρησιμοποιήσει η εφαρμογή. Με την ενεργοποίηση αυτής της ρύθμισης, μπορεί να αυξηθεί η κατανάλωση μπαταρίας."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"έχει πρόσβαση στην τοποθεσία κατά προσέγγιση (με βάση το δίκτυο)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Αυτή η εφαρμογή μπορεί να ανιχνεύσει την τοποθεσία σας βάσει πηγών δικτύου, όπως κεραίες κινητής τηλεφωνίας και δίκτυα Wi-Fi. Αυτές οι υπηρεσίες τοποθεσίας θα πρέπει να είναι ενεργοποιημένες και διαθέσιμες στο tablet που χρησιμοποιείτε, προκειμένου να μπορεί να τις χρησιμοποιήσει η εφαρμογή."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Αυτή η εφαρμογή μπορεί να ανιχνεύσει την τοποθεσία σας βάσει πηγών δικτύου, όπως κεραίες κινητής τηλεφωνίας και δίκτυα Wi-Fi. Αυτές οι υπηρεσίες τοποθεσίας θα πρέπει να είναι ενεργοποιημένες και διαθέσιμες στην τηλεόρασή σας, προκειμένου να μπορεί να τις χρησιμοποιήσει η εφαρμογή."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Αυτή η εφαρμογή μπορεί να ανιχνεύσει την τοποθεσία σας βάσει πηγών δικτύου, όπως κεραίες κινητής τηλεφωνίας και δίκτυα Wi-Fi. Αυτές οι υπηρεσίες τοποθεσίας θα πρέπει να είναι ενεργοποιημένες και διαθέσιμες στο τηλέφωνό σας, προκειμένου να μπορεί να τις χρησιμοποιήσει η εφαρμογή."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"πρόσβαση στην ακριβή τοποθεσία στο παρασκήνιο"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Αυτή η εφαρμογή μπορεί να ανιχνεύσει την ακριβή τοποθεσία σας οποτεδήποτε βρίσκεται στο παρασκήνιο. Αυτές οι υπηρεσίες τοποθεσίας θα πρέπει να είναι ενεργοποιημένες και διαθέσιμες στο τηλέφωνό σας, προκειμένου να μπορεί να τις χρησιμοποιήσει η εφαρμογή. Με την ενεργοποίηση αυτής της ρύθμισης, μπορεί να αυξηθεί η κατανάλωση μπαταρίας."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"αλλάζει τις ρυθμίσεις ήχου"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Επιτρέπει στην εφαρμογή την τροποποίηση καθολικών ρυθμίσεων ήχου, όπως η ένταση και ποιο ηχείο χρησιμοποιείται για έξοδο."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"εγγράφει ήχο"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Εικονίδιο δακτυλικών αποτυπωμάτων"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"διαχείριση υλικολογισμ. ελέγχου ταυτότ. προσώπου"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Επιτρέπει στην εφαρμογή να επικαλείται μεθόδους προσθήκης/διαγραφής προτύπων για χρήση."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"χρήση υλικολογισμικού ελέγχου ταυτότητας προσώπου"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί υλικολογισμικό για έλεγχο ταυτότητας"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Αδυναμία επεξεργασίας προσώπου. Δοκιμάστε ξανά."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Υψηλή φωτεινότητα. Δοκιμάστε χαμηλότερο φωτισμό."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Πολύ σκοτεινό πρόσωπο. Ξεσκεπάστε την πηγή φωτός."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Μετακινήστε τον αισθητήρα πιο μακριά."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Τοποθετήστε τον αισθητήρα πιο κοντά στο πρόσωπο."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Μετακινήστε τον αισθητήρα ψηλότερα."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Μετακινήστε τον αισθητήρα χαμηλότερα."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Μετακινήστε τον αισθητήρα προς τα δεξιά."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Μετακινήστε τον αισθητήρα προς τα αριστερά."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Κοιτάξτε στον αισθητήρα."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Δεν εντοπίστηκε κάποιο πρόσωπο."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Διατηρήστε το πρόσωπο σταθερό μπροστά στη συσκευή."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Το υλικολογισμικό προσώπου δεν είναι διαθέσιμο."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Λήξη χρονικού ορίου προσώπου. Δοκιμάστε ξανά."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Δεν είναι δυνατή η αποθήκευση του προσώπου."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Η ενέργεια προσώπου ακυρώθηκε."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Πάρα πολλές προσπάθειες. Δοκιμάστε ξανά αργότερα."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Πολλές προσπάθειες. Αποτυχία ελέγ. ταυτ. προσώπου."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Δοκιμάστε ξανά."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Δεν έχει καταχωριστεί κάποιο πρόσωπο."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Η συσκευή δεν διαθέτει αισθητήρα ελέγχου ταυτότητας προσώπου"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Πρόσωπο <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Εικ. προσ."</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"διαβάζει τις ρυθμίσεις συγχρονισμού"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Επιτρέπει στην εφαρμογή την ανάγνωση των ρυθμίσεων συγχρονισμού για έναν λογαριασμό. Για παράδειγμα, αυτό μπορεί να καθορίσει εάν η εφαρμογή \"Άτομα\" είναι συγχρονισμένη με έναν λογαριασμό."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ενεργοποιεί/απενεργοποιεί τον συγχρονισμό"</string>
@@ -749,7 +782,7 @@
     <string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Τοποθετήστε μια κάρτα SIM."</string>
     <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"Η κάρτα SIM δεν υπάρχει ή δεν είναι δυνατή η ανάγνωσή της. Τοποθετήστε μια κάρτα SIM."</string>
     <string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"Η κάρτα SIM δεν μπορεί να χρησιμοποιηθεί."</string>
-    <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"Η κάρτα SIM έχει απενεργοποιηθεί οριστικά.\n Επικοινωνήστε με τον παροχέα υπηρεσιών ασύρματου δικτύου για να λάβετε μια νέα κάρτα SIM."</string>
+    <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"Η κάρτα SIM έχει απενεργοποιηθεί οριστικά.\n Επικοινωνήστε με τον πάροχο υπηρεσιών ασύρματου δικτύου για να λάβετε μια νέα κάρτα SIM."</string>
     <string name="lockscreen_transport_prev_description" msgid="6300840251218161534">"Προηγούμενο κομμάτι"</string>
     <string name="lockscreen_transport_next_description" msgid="573285210424377338">"Επόμενο κομμάτι"</string>
     <string name="lockscreen_transport_pause_description" msgid="3980308465056173363">"Παύση"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Το Wi-Fi δεν έχει πρόσβαση στο διαδίκτυο"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Πατήστε για να δείτε τις επιλογές"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Αλλαγές στις ρυθμίσεις σημείου πρόσβασης Wi-Fi"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Το εύρος σημείου πρόσβασης Wi-Fi άλλαξε."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Αυτή η συσκευή δεν υποστηρίζει την προτίμησή σας για τη ζώνη 5 GHz μόνο. Αντ\' αυτού, αυτή η συσκευή θα χρησιμοποιεί τη ζώνη 5 GHz όταν είναι διαθέσιμη."</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="NEW_NETWORK">%1$s</xliff:g> όταν το δίκτυο <xliff:g id="PREVIOUS_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>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 7e132ec..f70ba3b 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Aeroplane mode"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Aeroplane mode is ON"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Aeroplane mode is OFF"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Battery saver"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Battery saver is OFF"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Battery saver is ON"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Settings"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assist"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Location"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"access this device\'s location"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device\'s location?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"The app will only have access to the location while you’re using the app."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Always allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s location?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"The app will always have access to the location, even when you’re not using the app."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"access your calendar"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your calendar?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"This app can add, remove or change calendar events on your phone. This app can send messages that may appear to come from calendar owners or change events without notifying their owners."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"access extra location provider commands"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Allows the app to access extra location provider commands. This may allow the app to interfere with the operation of the GPS or other location sources."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"access precise location (GPS and network-based)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"This app can get your location based on GPS or network location sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"access precise location only in the foreground"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"This app can get your exact location only when it is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"access approximate location (network-based)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can pick up your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"This app can pick up your location based on network sources such as mobile towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"This app can get your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"access precise location in the background"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"This app can get your exact location any time it is in the background. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"change your audio settings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"record audio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerprint icon"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"manage face authentication hardware"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Allows the app to invoke methods to add and delete facial templates for use."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"use face authentication hardware"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Allows the app to use face authentication hardware for authentication"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Couldn’t process face. Please try again."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Face is too bright. Please try in lower light."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Face is too dark. Please uncover light source."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Please move sensor farther away from face."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Please bring sensor closer to face."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Please move sensor higher."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Please move sensor lower."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Please move sensor to the right."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Please move sensor to the left."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Please look at the sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"No face detected."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Keep face steady in front of device."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Face hardware not available."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"No face enrolled."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"This device does not have a face authentication sensor"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Face icon"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi has no Internet access"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Changes to your hotspot settings"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Your hotspot band has changed."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 40ddb23..1ab78f1 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Airplane mode"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Airplane mode is ON"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Airplane mode is OFF"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Battery saver"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Battery saver is OFF"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Battery saver is ON"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Settings"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assist"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Location"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"access this device\'s location"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device\'s location?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"The app will only have access to the location while you’re using the app."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Always allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s location?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"The app will always have access to the location, even when you’re not using the app."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"access your calendar"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your calendar?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"This app can add, remove or change calendar events on your phone. This app can send messages that may appear to come from calendar owners or change events without notifying their owners."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"access extra location provider commands"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Allows the app to access extra location provider commands. This may allow the app to interfere with the operation of the GPS or other location sources."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"access precise location (GPS and network-based)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"This app can get your location based on GPS or network location sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"access precise location only in the foreground"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"This app can get your exact location only when it is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"access approximate location (network-based)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can pick up your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"This app can pick up your location based on network sources such as mobile towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"This app can get your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"access precise location in the background"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"This app can get your exact location any time it is in the background. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"change your audio settings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"record audio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerprint icon"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"manage face authentication hardware"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Allows the app to invoke methods to add and delete facial templates for use."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"use face authentication hardware"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Allows the app to use face authentication hardware for authentication"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Couldn’t process face. Please try again."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Face is too bright. Please try in lower light."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Face is too dark. Please uncover light source."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Please move sensor farther away from face."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Please bring sensor closer to face."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Please move sensor higher."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Please move sensor lower."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Please move sensor to the right."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Please move sensor to the left."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Please look at the sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"No face detected."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Keep face steady in front of device."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Face hardware not available."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"No face enrolled."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"This device does not have a face authentication sensor"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Face icon"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi has no Internet access"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Changes to your hotspot settings"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Your hotspot band has changed."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 7e132ec..f70ba3b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Aeroplane mode"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Aeroplane mode is ON"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Aeroplane mode is OFF"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Battery saver"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Battery saver is OFF"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Battery saver is ON"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Settings"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assist"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Location"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"access this device\'s location"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device\'s location?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"The app will only have access to the location while you’re using the app."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Always allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s location?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"The app will always have access to the location, even when you’re not using the app."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"access your calendar"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your calendar?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"This app can add, remove or change calendar events on your phone. This app can send messages that may appear to come from calendar owners or change events without notifying their owners."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"access extra location provider commands"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Allows the app to access extra location provider commands. This may allow the app to interfere with the operation of the GPS or other location sources."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"access precise location (GPS and network-based)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"This app can get your location based on GPS or network location sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"access precise location only in the foreground"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"This app can get your exact location only when it is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"access approximate location (network-based)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can pick up your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"This app can pick up your location based on network sources such as mobile towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"This app can get your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"access precise location in the background"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"This app can get your exact location any time it is in the background. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"change your audio settings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"record audio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerprint icon"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"manage face authentication hardware"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Allows the app to invoke methods to add and delete facial templates for use."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"use face authentication hardware"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Allows the app to use face authentication hardware for authentication"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Couldn’t process face. Please try again."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Face is too bright. Please try in lower light."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Face is too dark. Please uncover light source."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Please move sensor farther away from face."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Please bring sensor closer to face."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Please move sensor higher."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Please move sensor lower."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Please move sensor to the right."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Please move sensor to the left."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Please look at the sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"No face detected."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Keep face steady in front of device."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Face hardware not available."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"No face enrolled."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"This device does not have a face authentication sensor"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Face icon"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi has no Internet access"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Changes to your hotspot settings"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Your hotspot band has changed."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 7e132ec..f70ba3b 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Aeroplane mode"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Aeroplane mode is ON"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Aeroplane mode is OFF"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Battery saver"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Battery saver is OFF"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Battery saver is ON"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Settings"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assist"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Location"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"access this device\'s location"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device\'s location?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"The app will only have access to the location while you’re using the app."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Always allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s location?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"The app will always have access to the location, even when you’re not using the app."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"access your calendar"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your calendar?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"This app can add, remove or change calendar events on your phone. This app can send messages that may appear to come from calendar owners or change events without notifying their owners."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"access extra location provider commands"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Allows the app to access extra location provider commands. This may allow the app to interfere with the operation of the GPS or other location sources."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"access precise location (GPS and network-based)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"This app can get your location based on GPS or network location sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"access precise location only in the foreground"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"This app can get your exact location only when it is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"access approximate location (network-based)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can pick up your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"This app can pick up your location based on network sources such as mobile towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"This app can get your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"access precise location in the background"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"This app can get your exact location any time it is in the background. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"change your audio settings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"record audio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerprint icon"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"manage face authentication hardware"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Allows the app to invoke methods to add and delete facial templates for use."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"use face authentication hardware"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Allows the app to use face authentication hardware for authentication"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Couldn’t process face. Please try again."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Face is too bright. Please try in lower light."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Face is too dark. Please uncover light source."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Please move sensor farther away from face."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Please bring sensor closer to face."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Please move sensor higher."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Please move sensor lower."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Please move sensor to the right."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Please move sensor to the left."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Please look at the sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"No face detected."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Keep face steady in front of device."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Face hardware not available."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"No face enrolled."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"This device does not have a face authentication sensor"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Face icon"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi has no Internet access"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Changes to your hotspot settings"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Your hotspot band has changed."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 7d11b56..a4c8615 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‏‎‏‎‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‎‎‎‎‎‎‎Airplane mode‎‏‎‎‏‎"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‏‎‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎Airplane mode is ON‎‏‎‎‏‎"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‏‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎Airplane mode is OFF‎‏‎‎‏‎"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‎‎‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‎‎‎‎Battery saver‎‏‎‎‏‎"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‎Battery saver is OFF‎‏‎‎‏‎"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎‏‏‎‏‎‏‏‏‎‎‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‎‏‎Battery saver is ON‎‏‎‎‏‎"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎Settings‎‏‎‎‏‎"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‏‎‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎Assist‎‏‎‎‏‎"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‎Voice Assist‎‏‎‎‏‎"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‎‎‎‎‎‎‎‎‏‏‎‏‎‎‎‎‏‎‏‎‎‎Location‎‏‎‎‏‎"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎‏‏‏‎‏‏‏‎‎‏‎access this device\'s location‎‏‎‎‏‎"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‎‏‎‏‏‎‏‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access this device\'s location?‎‏‎‎‏‎"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‎The app will only have access to the location while you’re using the app.‎‏‎‎‏‎"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎‎Always allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access this device’s location?‎‏‎‎‏‎"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎The app will always have access to the location, even when you’re not using the app.‎‏‎‎‏‎"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‏‎‏‏‏‎‏‎‎‎‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‏‏‎‎Calendar‎‏‎‎‏‎"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‎‏‎‎access your calendar‎‏‎‎‏‎"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access your calendar?‎‏‎‎‏‎"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎This app can add, remove, or change calendar events on your phone. This app can send messages that may appear to come from calendar owners, or change events without notifying their owners.‎‏‎‎‏‎"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎access extra location provider commands‎‏‎‎‏‎"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎‏‎‎‏‏‏‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‏‎Allows the app to access extra location provider commands. This may allow the app to interfere with the operation of the GPS or other location sources.‎‏‎‎‏‎"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‎‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎access precise location (GPS and network-based)‎‏‎‎‏‎"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‏‏‏‎‏‎‏‎‏‏‏‏‏‏‏‏‎‎‎This app can get your location based on GPS or network location sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption.‎‏‎‎‏‎"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‏‏‏‎‏‏‎access precise location only in the foreground‎‏‎‎‏‎"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‎‏‎‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‎‎‎‏‏‎‎‏‏‎‏‎‎This app can get your exact location only when it is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption.‎‏‎‎‏‎"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‎‎access approximate location (network-based)‎‏‎‎‏‎"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‎‎This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them.‎‏‎‎‏‎"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎‎‏‏‏‏‏‎This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them.‎‏‎‎‏‎"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‏‏‎‏‎‎‎‎‎‎‎‎‏‏‎This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them.‎‏‎‎‏‎"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎‎access precise location in the background‎‏‎‎‏‎"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‏‎This app can get your exact location any time it is in the background. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption.‎‏‎‎‏‎"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‎‏‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎change your audio settings‎‏‎‎‏‎"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎Allows the app to modify global audio settings such as volume and which speaker is used for output.‎‏‎‎‏‎"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‏‎‏‎‎‏‏‎record audio‎‏‎‎‏‎"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‎‎‎Fingerprint icon‎‏‎‎‏‎"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‎‏‏‎‎‏‏‏‎‎‏‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‏‎‏‎manage face authentication hardware‎‏‎‎‏‎"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‎‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‏‎‎Allows the app to invoke methods to add and delete facial templates for use.‎‏‎‎‏‎"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‏‎‎‎‏‎‎‎‏‎‏‎‏‏‏‎‏‎‏‏‎‎‏‏‎‏‏‏‎use face authentication hardware‎‏‎‎‏‎"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎Allows the app to use face authentication hardware for authentication‎‏‎‎‏‎"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‏‎‎‏‎‎‎‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎Couldn’t process face. Please try again.‎‏‎‎‏‎"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‏‏‎‎Face is too bright. Please try in lower light.‎‏‎‎‏‎"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎Face is too dark. Please uncover light source.‎‏‎‎‏‎"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‏‏‎‏‎Please move sensor farther from face.‎‏‎‎‏‎"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‎‏‏‏‎Please bring sensor closer to face.‎‏‎‎‏‎"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‎‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‎Please move sensor higher.‎‏‎‎‏‎"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‏‏‎‏‎‏‎Please move sensor lower.‎‏‎‎‏‎"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‏‎‎‎‎Please move sensor to the right.‎‏‎‎‏‎"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‏‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‎Please move sensor to the left.‎‏‎‎‏‎"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎Please look at the sensor.‎‏‎‎‏‎"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‏‏‏‏‎No face detected.‎‏‎‎‏‎"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‎‏‎‏‏‎‏‎‏‏‎‎‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎Keep face steady infront of device.‎‏‎‎‏‎"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎Face hardware not available.‎‏‎‎‏‎"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‎‎Face time out reached. Try again.‎‏‎‎‏‎"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‎‎Face can’t be stored.‎‏‎‎‏‎"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎Face operation canceled.‎‏‎‎‏‎"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‎‎‎‎Too many attempts. Try again later.‎‏‎‎‏‎"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‎‎‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‏‎‏‏‏‎‏‎‎Too many attempts. Facial authentication disabled.‎‏‎‎‏‎"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎Try again.‎‏‎‎‏‎"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎No face enrolled.‎‏‎‎‏‎"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‏‏‏‎This device does not have a face authentication sensor‎‏‎‎‏‎"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‎‎‎‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎Face ‎‏‎‎‏‏‎<xliff:g id="FACEID">%d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎‎‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎‏‏‏‎‎‎‎‏‏‏‏‎Face icon‎‏‎‎‏‎"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‎read sync settings‎‏‎‎‏‎"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‎‎‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account.‎‏‎‎‏‎"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‏‎‏‏‏‎‎‎‏‎‎toggle sync on and off‎‏‎‎‏‎"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎Wi-Fi has no internet access‎‏‎‎‏‎"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎Tap for options‎‏‎‎‏‎"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎Changes to your hotspot settings‎‏‎‎‏‎"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‎‏‎‎‎‎‏‏‎‏‏‎Your hotspot band has changed.‎‏‎‎‏‎"</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‏‎‎‎‎‎‎‎‏‏‎‎‎‎‎‎‏‏‏‎‎‏‎‎‎‏‎This device doesn’t support your preference for 5GHz only. Instead, this device will use the 5GHz band when available.‎‏‎‎‏‎"</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‎‏‎‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‏‎Switched to ‎‏‎‎‏‏‎<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‏‎Device uses ‎‏‎‎‏‏‎<xliff:g id="NEW_NETWORK">%1$s</xliff:g>‎‏‎‎‏‏‏‎ when ‎‏‎‎‏‏‎<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>‎‏‎‎‏‏‏‎ has no internet access. Charges may apply.‎‏‎‎‏‎"</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‎‎‎‎Switched from ‎‏‎‎‏‏‎<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to ‎‏‎‎‏‏‎<xliff:g id="NEW_NETWORK">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 932cc0f..2b7c4bf 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo avión"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"El modo avión está Activado"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"El modo avión está Desactivado"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Ahorro de batería"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Ahorro de batería DESACTIVADO"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Ahorro de batería ACTIVADO"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Configuración"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Asistencia"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Asistente voz"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Ubicación"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"acceder a la ubicación de este dispositivo"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación de este dispositivo?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"La app solo tendrá acceso a la ubicación cuando la uses."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"La app siempre tendrá acceso a la ubicación, incluso cuando no la uses."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendario"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceder al calendario"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a tu calendario?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Esta app puede agregar, quitar o cambiar eventos del calendario en tu teléfono. Puede enviar mensajes que parecen proceder de propietarios del calendario o cambiar eventos sin notificarlos."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acceder a comandos adicionales del proveedor del lugar"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que la aplicación acceda a comandos adicionales del proveedor de ubicación. Esto puede permitirle a la aplicación interferir con el funcionamiento del GPS o de otras fuentes de ubicación."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"acceder a la ubicación precisa (según el GPS y la red)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Esta app puede obtener tu ubicación a través de GPS o servicios de ubicación que usan fuentes de ubicación de red, como torres de telefonía móvil y redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu teléfono para que la app pueda usarlos. Al hacerlo, es posible que se consuma más batería."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"acceder a la ubicación exacta solo en primer plano"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Esta app puede obtener tu ubicación exacta solo cuando está en primer plano. Los servicios de ubicación deben estar activados y disponibles en el teléfono para que la app pueda usarlos. Es posible que aumente el consumo de batería."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acceder a la ubicación aproximada (según la red)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Esta app puede obtener tu ubicación a través de fuentes de red, como torres de telefonía móvil y redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu tablet para que la app pueda usarlos."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Esta app puede obtener tu ubicación a través de fuentes de red, telefonía móvil y redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu TV para que la app pueda usarlos."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Esta app puede obtener tu ubicación a través de fuentes de red, como torres de celulares y redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu teléfono para que la app pueda usarlos."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"acceder a la ubicación exacta en segundo plano"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Esta app puede obtener tu ubicación exacta en cualquier momento mientras esté en segundo plano. Los servicios de ubicación deben estar activados y disponibles en el teléfono para que la app pueda usarlos. Es posible que aumente el consumo de batería."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"cambiar tu configuración de audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que la aplicación modifique la configuración de audio global, por ejemplo, el volumen y el altavoz de salida."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"grabar audio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ícono de huella digital"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"administrar el hardware de autenticación facial"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que la app emplee métodos para agregar y borrar plantillas de rostros para su uso."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"usar el hardware de autenticación facial"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite que la app use el hardware de autenticación facial para reconocerte"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Error al procesar el rostro. Vuelve a intentarlo."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"El rostro se ve muy claro. Prueba con menos luz."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"El rostro se ve muy oscuro. Prueba con más luz."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Aleja el sensor del rostro."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Acerca el sensor al rostro."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Coloca el sensor más arriba."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Coloca el sensor más abajo."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Mueve el sensor hacia la derecha."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Mueve el sensor hacia la izquierda."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Mira al sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"No se detectó ningún rostro."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantén el rostro fijo enfrente del dispositivo."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware de reconocimiento facial no disponible"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Se agotó el tiempo. Vuelve a intentarlo."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"No se puede almacenar el rostro."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Se canceló el reconocimiento facial."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Demasiados intentos. Inténtalo de nuevo más tarde."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Demasiados intentos. Autent. facial inhabilitada."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Vuelve a intentarlo."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"No registraste ningún rostro."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Este dispositivo no tiene sensor de autenticación facial"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Rostro <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ícono cara"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"leer la configuración de sincronización"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Este permiso permite que la aplicación consulte la configuración de sincronización de una cuenta. Esto permite, por ejemplo, determinar si la aplicación Personas está sincronizada con una cuenta."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar y desactivar la sincronización"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"La red Wi-Fi no tiene acceso a Internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Presiona para ver opciones"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Cambios en la configuración de tu hotspot"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Cambió la banda de tu hotspot."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Si bien este dispositivo no admite la opción para conectarse exclusivamente a bandas de 5 GHz, las usará cuando estén disponibles."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Se cambió a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"El dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Se cambió de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 8757795..c4b74af 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo avión"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modo avión activado. Desactivar"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modo avión desactivado. Activar"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Ahorro de batería"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Ahorro de batería desactivado"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Ahorro de batería activado"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Ajustes"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Asistencia"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Asistente voz"</string>
@@ -256,7 +253,7 @@
     <string name="notification_channel_network_alerts" msgid="2895141221414156525">"Alertas de la red"</string>
     <string name="notification_channel_network_available" msgid="4531717914138179517">"Red disponible"</string>
     <string name="notification_channel_vpn" msgid="8330103431055860618">"Estado de la VPN"</string>
-    <string name="notification_channel_device_admin" msgid="1568154104368069249">"Administración del dispositivo"</string>
+    <string name="notification_channel_device_admin" msgid="1568154104368069249">"Administración de dispositivos"</string>
     <string name="notification_channel_alerts" msgid="4496839309318519037">"Alertas"</string>
     <string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demo para tiendas"</string>
     <string name="notification_channel_usb" msgid="9006850475328924681">"Conexión USB"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Ubicación"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"acceder a la ubicación de este dispositivo"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación de este dispositivo?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"La aplicación solo podrá acceder a la ubicación cuando estés usando la aplicación."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"¿Permites que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a ubic. del disp.?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"La aplicación siempre podrá acceder a la ubicación, aunque no estés usando la aplicación."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendario"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceder a tu calendario"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a tu calendario?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Esta aplicación puede añadir, quitar o cambiar eventos de calendario almacenados en tu teléfono. También puede enviar mensajes que parecen provenir del propietario del calendario o cambiar eventos sin notificárselo a su propietario."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acceder a comandos de proveedor de ubicación adicional"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que la aplicación acceda a otros comandos del proveedor de ubicación. De esta forma, la aplicación podrá interferir en el funcionamiento del GPS o de otras fuentes de ubicación."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"acceder a tu ubicación precisa (basada en red y GPS)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Esta aplicación puede obtener tu ubicación a partir del GPS o de fuentes de ubicación de red como las antenas de telefonía móvil y las redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu teléfono para que la aplicación pueda usarlos. Esto puede aumentar el consumo de batería."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"acceder a la ubicación exacta solo en primer plano"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Esta aplicación solo puede obtener tu ubicación exacta cuando está en primer plano. Estos servicios de ubicación deben estar activados y disponibles en tu teléfono para que la aplicación pueda utilizarlos. Es posible que aumente el consumo de batería."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acceder a tu ubicación aproximada (basada en red)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Esta aplicación puede obtener tu ubicación a partir de fuentes de red como las antenas de telefonía móvil y las redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu tablet para que la aplicación pueda usarlos."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Esta aplicación puede obtener tu ubicación a partir de fuentes de red como las antenas de telefonía móvil y las redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu TV para que la aplicación pueda usarlos."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Esta aplicación puede obtener tu ubicación a partir de fuentes de red como las antenas de telefonía móvil y las redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu teléfono para que la aplicación pueda usarlos."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"acceder a la ubicación exacta en segundo plano"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Esta aplicación puede obtener tu ubicación exacta en cualquier momento cuando está en segundo plano. Estos servicios de ubicación deben estar activados y disponibles en tu teléfono para que la aplicación pueda utilizarlos. Es posible que aumente el consumo de batería."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"cambiar la configuración de audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que la aplicación modifique la configuración de audio global (por ejemplo, el volumen y el altavoz de salida)."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"grabar sonido"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icono de huella digital"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"gestionar el hardware de autenticación facial"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que la app use métodos para añadir y suprimir plantillas de caras para su uso."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"usar el hardware de autenticación facial"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite que la aplicación utilice el hardware de autenticación facial para autenticarte"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"No se ha reconocido la cara. Vuelve a intentarlo."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"La cara se ve muy clara. Inténtalo con menos luz."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"La cara se ve muy oscura. Inténtalo con más luz."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Aleja la cara del sensor."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Acerca la cara al sensor."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Coloca el sensor más arriba."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Coloca el sensor más abajo."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Mueve el sensor hacia la derecha."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Mueve el sensor hacia la izquierda."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Mira al sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"No se ha detectado ninguna cara."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantén la cara fija enfrente del dispositivo."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware de reconocimiento facial no disponible."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Has sobrepasado el tiempo. Inténtalo de nuevo."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"No se pueden registrar más caras."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Se ha cancelado el reconocimiento facial."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Demasiados intentos. Inténtalo de nuevo más tarde."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Demasiados intentos. Autent. facial inhabilitada."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Inténtalo de nuevo."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"No has registrado ninguna cara."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Este dispositivo no tiene sensor de autenticación facial"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Cara <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Icono cara"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"leer la configuración de sincronización"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que la aplicación consulte la configuración de sincronización de una cuenta. La aplicación puede utilizar este permiso, por ejemplo, para determinar si la aplicación Contactos está sincronizada con una cuenta."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar y desactivar la sincronización"</string>
@@ -605,7 +638,7 @@
     <string name="policylab_disableCamera" msgid="6395301023152297826">"Inhabilitar cámaras"</string>
     <string name="policydesc_disableCamera" msgid="2306349042834754597">"Evitar el uso de las cámaras del dispositivo"</string>
     <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Inhabilitar algunas funciones del bloqueo de pantalla"</string>
-    <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Evita el uso de algunas funciones del bloqueo de pantalla."</string>
+    <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Evitar el uso de algunas funciones del bloqueo de pantalla"</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Casa"</item>
     <item msgid="869923650527136615">"Móvil"</item>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"La red Wi-Fi no tiene acceso a Internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toca para ver opciones"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Cambios en los ajustes de tu punto de acceso"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"La banda de tu punto de acceso ha cambiado."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Este dispositivo no admite la opción de conectarse exclusivamente a bandas de 5 GHz, pero las usará cuando estén disponibles."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Se ha cambiado a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"El dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Se ha cambiado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index f9de900..1e73bf1 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Lennurežiim"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Lennurežiim on SEES"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Lennurežiim on VÄLJAS"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Akusäästja"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Akusäästja on VÄLJA lülitatud"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Akusäästja on SISSE lülitatud"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Seaded"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Abi"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Häälabi"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Asukoht"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"pääseda juurde selle seadme asukohale"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs selle seadme asukohale?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Rakendusel on juurdepääs asukohale vaid siis, kui rakendust kasutate."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Kas lubada rak. &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; alati juurdepääs seadme asukohale?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Rakendusel on alati juurdepääs asukohale isegi siis, kui te rakendust ei kasuta."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"juurdepääs kalendrile"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs teie kalendrile?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"See rakendus võib teie telefoni kalendrisündmusi lisada, neid eemaldada või muuta. See rakendus võib saata sõnumeid, mis näivad pärinevat kalendri omanikelt, või muuta sündmusi ilma omanikke teavitamata."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"juurdepääs asukohapakkuja lisakäskudele"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Võimaldab rakendusel juurde pääseda asukohapakkuja erikäskudele. See võib lubada rakendusel mõjutada GPS-i või muude asukohaallikate tööd."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"juurdepääs täpsele asukohale (GPS-i ja võrgupõhine)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"See rakendus näeb GPS-i või võrguasukoha allikate (nt mobiilimastid ja WiFi-võrgud) abil teie asukohta. Need asukohateenused peavad olema sisse lülitatud ja teie telefonis saadaval, et rakendus neid kasutada saaks. See võib suurendada akutoite tarbimist."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"juurdepääs täpsele asukohale ainult esiplaanil"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"See rakendus hangib teie täpse asukoha ainult siis, kui see töötab esiplaanil. Need asukohateenused peavad olema sisse lülitatud ja teie telefonis saadaval, et rakendus saaks neid kasutada. See võib suurendada akutoite tarbimist."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"juurdepääs ligikaudsele asukohale (võrgupõhine)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"See rakendus näeb võrguallikate (nt mobiilimastid ja WiFi-võrgud) abil teie asukohta. Need asukohateenused peavad olema sisse lülitatud ja teie tahvelarvutis saadaval, et rakendus neid kasutada saaks."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"See rakendus näeb võrguallikate (nt mobiilimastid ja WiFi-võrgud) abil teie asukohta. Need asukohateenused peavad olema sisse lülitatud ja teie teleris saadaval, et rakendus neid kasutada saaks."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"See rakendus näeb võrguallikate (nt mobiilimastid ja WiFi-võrgud) abil teie asukohta. Need asukohateenused peavad olema sisse lülitatud ja teie telefonis saadaval, et rakendus neid kasutada saaks."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"juurdepääs täpsele asukohale taustal"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"See rakendus hangib teie täpse asukoha alati, kui see töötab taustal. Need asukohateenused peavad olema sisse lülitatud ja teie telefonis saadaval, et rakendus saaks neid kasutada. See võib suurendada akutoite tarbimist."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"muuda heliseadeid"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Võimaldab rakendusel muuta üldiseid heliseadeid, näiteks helitugevust ja seda, millist kõlarit kasutatakse väljundiks."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"salvesta heli"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Sõrmejälje ikoon"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"hallata näo autentimise riistvara"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Lubab rakendusel tühistada meetodid kasutatavate näomallide lisamiseks ja kustutamiseks."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"kasutada näo autentimise riistvara"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Võimaldab rakendusel autentimiseks kasutada näo autentimise riistvara"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Nägu ei õnnestunud töödelda. Proovige uuesti."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Nägu on liiga hele. Proovige hämaramas."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Nägu on liiga tume. Kasutage valgusallikat."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Liigutage andur näost kaugemale."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Liigutage andur näole lähemale."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Liigutage andurit kõrgemale."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Liigutage andurit madalamale."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Liigutage andurit paremale."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Liigutage andurit vasakule."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Vaadake andurit."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nägu ei tuvastatud."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Hoidke nägu seadme ees paigal."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Näotuvastuse riistvara pole saadaval."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Näotuvastuse taimeri ajalõpp. Proovige uuesti."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Nägu ei saa salvestada."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Näotuvastuse toiming tühistati."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Liiga palju katseid. Proovige hiljem uuesti."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Liiga palju katseid. Näotuvastus on keelatud."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Proovige uuesti."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nägu pole registreeritud."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Sellel seadmel pole näotuvastuse andurit"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Nägu <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Näoikoon"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"loe sünkroonimisseadeid"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Võimaldab rakendusel lugeda konto sünkroonimisseadeid. Näiteks võib see määrata, kas rakendus Inimesed on kontoga sünkroonitud."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"lülitage sünkroonimine sisse ja välja"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"WiFi-võrgul pole juurdepääsu Internetile"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Puudutage valikute nägemiseks"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Muudatused teie leviala seadetes"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Teie leviala riba on muutunud."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"See seade ei toeta teie eelistatud ainult 5 GHz riba. Seade kasutab 5 GHz riba ainult siis, kui see on saadaval."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Lülitati võrgule <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Seade kasutab võrku <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, kui võrgul <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> puudub juurdepääs Internetile. Rakenduda võivad tasud."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Lülitati võrgult <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> võrgule <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 9c6eab6..aa6edaa 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Hegaldi modua"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Hegaldi modua AKTIBATUTA dago"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Hegaldi modua DESAKTIBATUTA dago"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Bateria-aurrezlea"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"DESAKTIBATUTA dago bateria-aurrezlea"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"AKTIBATUTA dago bateria-aurrezlea"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Ezarpenak"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Lagundu"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ahots-laguntza"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Kokapena"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"atzitu gailuaren kokapena"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari gailuaren kokapena atzitzea baimendu nahi diozu?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikazioa erabiltzen ari zarenean soilik atzituko du aplikazioak kokapena."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari gailuaren kokapena beti atzitzea baimendu?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikazioak beti atzituko du kokapena, baita aplikazioa erabiltzen ari ez bazara ere."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Egutegia"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"atzitu egutegia"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari egutegia atzitzea baimendu nahi diozu?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Telefonoko gertaerak gehitzeko, kentzeko edo aldatzeko aukera du aplikazioak. Gainera, egutegien jabeenak diruditen mezuak bidal ditzake, eta gertaerak alda ditzake jabeei beraiei jakinarazi gabe."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"atzitu kokapen-hornitzaileen komando gehigarriak"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Kokapen-hornitzailearen agindu gehigarriak atzitzea baimentzen die aplikazioei. Horrela, agian aplikazioek GPSaren edo bestelako kokapenaren iturburuen funtzionamenduan eragina izan dezakete."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"atzitu kokapen zehatza (GPS sisteman eta sarean oinarrituta)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Aplikazioak zure kokapenaren berri izan dezake GPS sistema edo sareen iturburuak (adibidez, telefonia mugikorreko dorreak eta Wi-Fi sareak) erabilita. Kokapen-zerbitzu horiek aktibatuta eta erabilgarri izan behar dituzu telefonoan, aplikazioak erabil ditzan. Baliteke bateria gehiago erabiltzea."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"lortu kokapen zehatza aurreko planoan bakarrik"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Aplikazioak zure kokapen zehatza lor dezake aurreko planoan funtzionatzen duenean bakarrik. Kokapen-zerbitzu horiek aktibatuta eta erabilgarri izan behar dituzu telefonoan, aplikazioak erabil ditzan. Baliteke bateria gehiago erabiltzea."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"atzitu gutxi gorabeherako kokapena (sarean oinarrituta)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Aplikazioak zure kokapenaren berri izan dezake sareen iturburuak (adibidez, telefonia mugikorreko dorreak eta Wi-Fi sareak) erabilita. Kokapen-zerbitzu horiek aktibatuta eta erabilgarri izan behar dituzu tabletan, aplikazioak erabil ditzan."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Aplikazioak zure kokapenaren berri izan dezake sareen iturburuak (adibidez, telefonia mugikorreko dorreak eta Wi-Fi sareak) erabilita. Kokapen-zerbitzu horiek aktibatuta eta erabilgarri izan behar dituzu telebistan, aplikazioak erabil ditzan."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Aplikazioak zure kokapenaren berri izan dezake sareen iturburuak (adibidez, telefonia mugikorreko dorreak eta Wi-Fi sareak) erabilita. Kokapen-zerbitzu horiek aktibatuta eta erabilgarri izan behar dituzu telefonoan, aplikazioak erabil ditzan."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"lortu kokapen zehatza atzeko planoan"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Aplikazioak zure kokapen zehatza lor dezake atzeko planoan funtzionatzen duenean. Kokapen-zerbitzu horiek aktibatuta eta erabilgarri izan behar dituzu telefonoan, aplikazioak erabil ditzan. Baliteke bateria gehiago erabiltzea."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"aldatu audio-ezarpenak"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Audio-ezarpen orokorrak aldatzeko baimena ematen dio; besteak beste, bolumena eta irteerarako zer bozgorailu erabiltzen den."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"grabatu audioa"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Hatz-markaren ikonoa"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"kudeatu aurpegi bidez autentifikatzeko hardwarea"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Aurpegi-txantiloiak gehitu eta ezabatzeko metodoei dei egitea baimentzen dio aplikazioari."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"erabili aurpegi bidez autentifikatzeko hardwarea"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Aurpegi bidez autentifikatzeko hardwarea erabiltzea baimentzen dio aplikazioari"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Ezin izan da prozesatu aurpegia. Saiatu berriro."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Aurpegiak distira gehiegi du. Murriztu argitasuna."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Aurpegia ilunegi dago. Estalgabetu argi-iturburua."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Urrundu sentsorea aurpegitik."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Hurbildu sentsorea aurpegira."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Eraman sentsorea gora."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Eraman sentsorea behera."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Eraman sentsorea eskuinera."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Eraman sentsorea ezkerrera."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Begiratu sentsoreari."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Ez dugu hauteman aurpegirik."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Egon geldi, aurpegia gailuaren aurrean jarrita."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Aurpegia hautemateko hardwarea ez dago erabilgarri."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Gainditu da aurpegiak prozesatzeko denbora-muga. Saiatu berriro."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Ezin da gorde aurpegia."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Utzi da aurpegiaren bidezko eragiketa."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Saiakera gehiegi egin dituzu. Saiatu berriro geroago."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Saiakera gehiegi egin dituzu. Desgaitu egin da autentifikazioa."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Saiatu berriro."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Ez dago aurpegirik erregistratuta."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Gailu honek ez du aurpegia autentifikatzeko sentsorerik"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> aurpegia"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Aurpegiaren ikonoa"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"irakurri sinkronizazio-ezarpenak"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Kontu baten sinkronizazio-ezarpenak irakurtzeko baimena ematen die aplikazioei. Adibidez, Jendea aplikazioa konturen batekin sinkronizatuta dagoen zehatz dezake."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"aktibatu eta desaktibatu sinkronizazioa"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Ezin da konektatu Internetera Wi-Fi bidez"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Sakatu aukerak ikusteko"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Aldaketak egin dira sare publikoaren ezarpenetan"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Aldatu da sare publikoaren banda."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Gailuak ez du onartzen 5 GHz-ko banda soilik erabiltzeko hobespena. Horren ordez, erabilgarri dagoen bakoitzean erabiliko da 5 GHz-ko banda."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> erabiltzen ari zara orain"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> Internetera konektatzeko gauza ez denean, <xliff:g id="NEW_NETWORK">%1$s</xliff:g> erabiltzen du gailuak. Agian kostuak ordaindu beharko dituzu."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> erabiltzen ari zinen, baina <xliff:g id="NEW_NETWORK">%2$s</xliff:g> erabiltzen ari zara orain"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 39a2247..d8067f0 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -221,7 +221,7 @@
     <string name="global_action_logout" msgid="935179188218826050">"پایان جلسه"</string>
     <string name="global_action_screenshot" msgid="8329831278085426283">"عکس صفحه‌نمایش"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"گرفتن گزارش اشکال"</string>
-    <string name="bugreport_message" msgid="398447048750350456">"این گزارش اطلاعات مربوط به وضعیت دستگاه کنونی شما را جمع‌آوری می‌کند تا به صورت یک پیام رایانامه ارسال شود. از زمان شروع گزارش اشکال تا آماده شدن برای ارسال اندکی زمان می‌برد؛ لطفاً شکیبا باشید."</string>
+    <string name="bugreport_message" msgid="398447048750350456">"این گزارش اطلاعات مربوط به وضعیت دستگاه کنونی شما را جمع‌آوری می‌کند تا به صورت یک پیام ایمیل ارسال شود. از زمان شروع گزارش اشکال تا آماده شدن برای ارسال اندکی زمان می‌برد؛ لطفاً شکیبا باشید."</string>
     <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"گزارش تعاملی"</string>
     <string name="bugreport_option_interactive_summary" msgid="229299488536107968">"در بیشتر شرایط از این گزینه استفاده کنید. به شما امکان ردیابی پیشرفت گزارش و وارد کردن جزئیات بیشتری درباره مشکل را می‌دهد. ممکن است برخی از بخش‌هایی را که کمتر استفاده شده و باعث افزایش طول زمان گزارش می‌شود حذف کند."</string>
     <string name="bugreport_option_full_title" msgid="6354382025840076439">"گزارش کامل"</string>
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"حالت هواپیما"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"حالت هواپیما روشن است"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"حالت هواپیما خاموش است"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"بهینه‌سازی باتری"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"بهینه‌سازی باتری خاموش است"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"بهینه‌سازی باتری روشن است"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"تنظیمات"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"دستیار"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"دستیار صوتی"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"مکان"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"دسترسی به موقعیت مکانی این دستگاه"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود به مکان این دستگاه دسترسی پیدا کند؟"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"این برنامه تنها هنگامی که از آن استفاده می‌کنید، به مکان دسترسی خواهد داشت."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"‏همیشه به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود به مکان این دستگاه دسترسی داشته باشد؟"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"این برنامه همیشه به مکان دسترسی خواهد داشت، حتی اگر از آن استفاده نکنید."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"تقویم"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"دسترسی به تقویم شما"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود به تقویم شما دسترسی پیدا کند؟"</string>
@@ -396,18 +396,20 @@
     <string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"این برنامه می‌تواند همه رویدادهای تقویم ذخیره‌شده در رایانه لوحی شما را بخواند و داده‌های تقویم شما را به اشتراک بگذارد یا ذخیره کند."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="8837931557573064315">"این برنامه می‌تواند همه رویدادهای تقویم ذخیره‌شده در تلویزیون شما را بخواند و داده‌های تقویم شما را به اشتراک بگذارد یا ذخیره کند."</string>
     <string name="permdesc_readCalendar" product="default" msgid="4373978642145196715">"این برنامه می‌تواند همه رویدادهای تقویم ذخیره‌شده در تلفن شما را بخواند و داده‌های تقویم شما را به اشتراک بگذارد یا ذخیره کند."</string>
-    <string name="permlab_writeCalendar" msgid="8438874755193825647">"افزودن یا تغییر رویدادهای تقویم و ارسال رایانامه به مهمانان بدون دخالت مالک"</string>
+    <string name="permlab_writeCalendar" msgid="8438874755193825647">"افزودن یا تغییر رویدادهای تقویم و ارسال ایمیل به مهمانان بدون دخالت مالک"</string>
     <string name="permdesc_writeCalendar" product="tablet" msgid="1675270619903625982">"این برنامه می‌تواند در رایانه لوحی شما رویدادهای تقویم اضافه کند، آن‌ها را حذف کند یا تغییر دهد. این برنامه می‌تواند پیام‌هایی ارسال کند که گویی از طرف مالکان تقویم هستند یا رویدادها را بدون اطلاع مالکان تغییر دهد."</string>
     <string name="permdesc_writeCalendar" product="tv" msgid="9017809326268135866">"این برنامه می‌تواند در تلویزیون شما رویدادهای تقویم اضافه کند، آن‌ها را حذف کند یا تغییر دهد. این برنامه می‌تواند پیام‌هایی ارسال کند که گویی از طرف مالکان تقویم هستند یا رویدادها را بدون اطلاع مالکان تغییر دهد."</string>
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"این برنامه می‌تواند در تلفن شما رویدادهای تقویم اضافه کند، آن‌ها را حذف کند یا تغییر دهد. این برنامه می‌تواند پیام‌هایی ارسال کند که گویی از طرف مالکان تقویم هستند یا رویدادها را بدون اطلاع مالکان تغییر دهد."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"دسترسی به فرمان‌های بیشتر ارائه دهنده مکان"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"‏به برنامه اجازه می‌دهد به دستورات ارائه‌دهنده مکان تکمیلی دسترسی داشته باشد. این کار ممکن است به برنامه امکان دهد با کارکرد GPS یا منابع دیگر مکان تداخل داشته باشد."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"‏دسترسی به مکان دقیق (مبتنی بر GPS و شبکه)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"‏این برنامه می‌تواند براساس GPS یا منابع مکان شبکه مانند دکل‌های مخابراتی و شبکه‌های Wi-Fi، مکان شما را تشخیص دهد. این خدمات مکان باید روشن و در تلفن شما دردسترس باشند تا برنامه بتواند از آن‌ها استفاده کند. این کار می‌تواند مصرف باتری را افزایش دهد."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"دسترسی به مکان دقیق فقط در پیش‌زمینه"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"این برنامه فقط زمانی می‌تواند موقعیت مکانی دقیق شما را دریافت کند که در پیش‌زمینه باشد. برای اینکه برنامه بتواند از خدمات مکان استفاده کند، این خدمات باید در تلفنتان روشن و دردسترس باشد. ممکن است با این کار مصرف باتری افزایش یابد."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"دسترسی به مکان تقریبی (مبتنی بر شبکه)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"‏این برنامه می‌تواند براساس منابع شبکه مانند دکل‌های مخابراتی و شبکه‌های Wi-Fi، مکان شما را تشخیص دهد. این خدمات مکان باید روشن و در رایانه لوحی شما دردسترس باشند تا برنامه بتواند از آن‌ها استفاده کند."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"‏این برنامه می‌تواند براساس منابع شبکه مانند دکل‌های مخابراتی و شبکه‌های Wi-Fi، مکان شما را تشخیص دهد. این خدمات مکان باید روشن و در تلویزیون شما دردسترس باشند تا برنامه بتواند از آن‌ها استفاده کند."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"‏این برنامه می‌تواند براساس منابع شبکه مانند دکل‌های مخابراتی و شبکه‌های Wi-Fi، مکان شما را تشخیص دهد. این خدمات مکان باید روشن و در تلفن شما دردسترس باشند تا برنامه بتواند از آن‌ها استفاده کند."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"دسترسی به موقعیت مکانی دقیق در پس‌زمینه"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"هرزمان این برنامه در پس‌زمینه باشد می‌تواند موقعیت مکانی دقیق شما را دریافت کند. برای اینکه برنامه بتواند از خدمات مکان استفاده کند، این خدمات باید در تلفنتان روشن و دردسترس باشد. ممکن است با این کار مصرف باتری افزایش یابد."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"تغییر تنظیمات صوتی"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"به برنامه امکان می‌دهد تنظیمات صوتی کلی مانند میزان صدا و بلندگوی مورد استفاده برای پخش صدا را تغییر دهد."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ضبط صدا"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"نماد اثر انگشت"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"مدیریت سخت‌افزار احراز هویت با چهره"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"به برنامه امکان می‌دهد روش‌هایی را برای افزودن و حذف الگوهای چهره جهت استفاده فرابخواند."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"استفاده از سخت‌افزار احراز هویت با چهره"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"به برنامه امکان می‌دهد از سخت‌افزار احراز هویت با چهره برای احراز هویت استفاده کند"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"چهره پردازش نشد. لطفاً دوباره امتحان کنید."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"چهره خیلی روشن است. لطفاً در نور کمتری دوباره امتحان کنید."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"چهره خیلی تاریک است. لطفاً منبع نور را نپوشانید."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"لطفاً حسگر را از صورتتان دورتر کنید."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"لطفاً حسگر را به صورتتان نزدیک‌تر کنید."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"لطفاً حسگر را بالاتر ببرید."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"لطفاً حسگر را پایین‌تر بیاورید."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"لطفاً حسگر را به راست حرکت دهید."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"لطفاً حسگر را به چپ حرکت دهید."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"لطفاً به حسگر نگاه کنید."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"چهره‌ای شناسایی نشد."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"صورتتان را ثابت در جلوی دستگاه نگه دارید."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"سخت‌افزار چهره دردسترس نیست."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"مهلت زمانی شناسایی چهره تمام شد. دوباره امتحان کنید"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"نمی‌توان چهره را ذخیره کرد."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"عملیات شناسایی چهره لغو شد."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"تعداد زیادی تلاش ناموفق. بعداً دوباره امتحان کنید."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"چندین تلاش ناموفق. احراز هویت با چهره غیرفعال شد."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"دوباره امتحان کنید."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"هیچ چهره‌ای ثبت نشده است."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"این دستگاه حسگر احراز هویت با چهره ندارد."</string>
+    <string name="face_name_template" msgid="7004562145809595384">"چهره <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"نماد چهره"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"خواندن تنظیمات همگام‌سازی"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"به برنامه اجازه می‌دهد تنظیمات را برای یک حساب بخواند. به‌عنوان مثال، این ویژگی می‌تواند تعیین کند آیا حساب «افراد» شما با یک حساب همگام‌سازی شده است."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"تغییر وضعیت همگام‌سازی بین فعال و غیرفعال"</string>
@@ -780,7 +813,7 @@
     <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"بازگشایی قفل حساب"</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_username_hint" msgid="8846881424106484447">"نام کاربری (ایمیل)"</string>
     <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"گذرواژه"</string>
     <string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ورود به سیستم"</string>
     <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"نام کاربر یا گذرواژه نامعتبر است."</string>
@@ -1005,15 +1038,15 @@
     <string name="copyUrl" msgid="2538211579596067402">"‏کپی URL"</string>
     <string name="selectTextMode" msgid="1018691815143165326">"انتخاب متن"</string>
     <string name="undo" msgid="7905788502491742328">"لغو"</string>
-    <string name="redo" msgid="7759464876566803888">"انجام مجدد"</string>
+    <string name="redo" msgid="7759464876566803888">"بازانجام"</string>
     <string name="autofill" msgid="3035779615680565188">"تکمیل خودکار"</string>
     <string name="textSelectionCABTitle" msgid="5236850394370820357">"انتخاب متن"</string>
     <string name="addToDictionary" msgid="4352161534510057874">"افزودن به واژه‌نامه"</string>
     <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>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"‏Wi-Fi به اینترنت دسترسی ندارد"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"برای گزینه‌ها ضربه بزنید"</string>
+    <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_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>
@@ -1530,7 +1566,7 @@
     <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"پین کدها منطبق نیستند"</string>
     <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"‏تلاش‎های زیادی برای کشیدن الگو صورت گرفته است"</string>
     <string name="kg_login_instructions" msgid="1100551261265506448">"‏برای بازگشایی قفل، با حساب Google خود وارد سیستم شوید."</string>
-    <string name="kg_login_username_hint" msgid="5718534272070920364">"نام کاربری (رایانامه)"</string>
+    <string name="kg_login_username_hint" msgid="5718534272070920364">"نام کاربری (ایمیل)"</string>
     <string name="kg_login_password_hint" msgid="9057289103827298549">"گذرواژه"</string>
     <string name="kg_login_submit_button" msgid="5355904582674054702">"ورود به سیستم"</string>
     <string name="kg_login_invalid_input" msgid="5754664119319872197">"نام کاربری یا گذرواژه نامعتبر."</string>
@@ -1545,9 +1581,9 @@
     <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"شما به اشتباه <xliff:g id="NUMBER">%d</xliff:g> بار اقدام به باز کردن قفل رایانه لوحی کرده‌اید. رایانه لوحی اکنون به پیش‌فرض کارخانه بازنشانی می‌شود."</string>
     <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"<xliff:g id="NUMBER">%d</xliff:g> دفعه به صورت نادرست سعی کرده‌اید قفل تلویزیون را باز کنید. اکنون تلویزیون به تنظیمات پیش‌فرض کارخانه بازنشانی خواهد شد."</string>
     <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"شما به اشتباه <xliff:g id="NUMBER">%d</xliff:g> بار اقدام به باز کردن قفل تلفن کرده‌اید. این تلفن اکنون به پیش‌فرض کارخانه بازنشانی می‌شود."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"‏شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‎اید. بعد از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته می‎شود که با استفاده از یک حساب رایانامه قفل رایانه لوحی خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"الگوی بازگشایی‌تان را <xliff:g id="NUMBER_0">%1$d</xliff:g> دفعه به صورت نادرست رسم کرده‌اید. <xliff:g id="NUMBER_1">%2$d</xliff:g> پس از \n تلاش ناموفق دیگر، از شما خواسته می‌شود تا با استفاده از یک حساب رایانامه، قفل تلویزیون‌تان را باز کنید.\n پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"‏شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‌اید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته می‎شود که با استفاده از یک حساب رایانامه قفل تلفن خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"‏شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‎اید. بعد از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته می‎شود که با استفاده از یک حساب ایمیل قفل رایانه لوحی خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"الگوی بازگشایی‌تان را <xliff:g id="NUMBER_0">%1$d</xliff:g> دفعه به صورت نادرست رسم کرده‌اید. <xliff:g id="NUMBER_1">%2$d</xliff:g> پس از \n تلاش ناموفق دیگر، از شما خواسته می‌شود تا با استفاده از یک حساب ایمیل، قفل تلویزیون‌تان را باز کنید.\n پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"‏شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‌اید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته می‎شود که با استفاده از یک حساب ایمیل قفل تلفن خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"حذف"</string>
     <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"میزان صدا را به بالاتر از حد توصیه شده افزایش می‌دهید؟\n\nگوش دادن به صداهای بلند برای مدت طولانی می‌تواند به شنوایی‌تان آسیب وارد کند."</string>
@@ -1842,7 +1878,7 @@
     <string name="autofill_save_type_address" msgid="4936707762193009542">"نشانی"</string>
     <string name="autofill_save_type_credit_card" msgid="7127694776265563071">"کارت‌ اعتباری"</string>
     <string name="autofill_save_type_username" msgid="239040540379769562">"نام کاربری"</string>
-    <string name="autofill_save_type_email_address" msgid="5752949432129262174">"نشانی رایانامه"</string>
+    <string name="autofill_save_type_email_address" msgid="5752949432129262174">"نشانی ایمیل"</string>
     <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"آرام باشید و پناهگاهی در این اطراف پیدا کنید."</string>
     <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"فوراً مناطق ساحلی و محدوده رودخانه را ترک کنید و به جایی امن، مثل ارتفاعات بروید."</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"آرام باشید و پناهگاهی در این اطراف پیدا کنید."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index db3edf3..36a656b0 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Lentokonetila"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Lentokonetila on KÄYTÖSSÄ"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Lentokonetila on POIS KÄYTÖSTÄ"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Virransäästö"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Virransäästö on POISSA KÄYTÖSTÄ"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Virransäästö on KÄYTÖSSÄ"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Asetukset"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Auta"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ääniapuri"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Sijainti"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"käyttää laitteen sijaintia"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tämän laitteen sijainnin käyttöoikeuden?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Sovellus saa sijainnin käyttöoikeuden vain jos käytät sovellusta."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tämän laitteen sijainnin käyttöoikeuden?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Sovellus saa sijainnin käyttöoikeuden aina, vaikka et käyttäisi sovellusta."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalenteri"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"käyttää kalenteria"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; kalenterisi käyttöoikeuden?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Tämä sovellus voi lisätä, poistaa tai muuttaa puhelimen kalenteritapahtumia. Sovellus voi lähettää viestejä, jotka näyttävät tulevan kalenterin omistajilta, tai muuttaa kalenteritapahtumia ilmoittamatta omistajille."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"käytä lisää sijainnintarjoajakomentoja"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Antaa sovelluksen käyttää ylimääräisiä sijaintipalvelukomentoja. Sovellus saattaa tällöin häiritä GPS:n tai muiden sijaintilähteiden toimintaa."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"käyttää tarkkaa sijaintia (GPS- ja verkkopohjainen)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Tämä sovellus voi määrittää sijaintisi matkapuhelinverkon tukiasemien, Wi-Fi-verkkojen ja muiden verkkolähteiden tai GPS-paikannuksen perusteella. Näiden sijaintipalveluiden tulee olla käytössä ja käytettävissä puhelimellasi, jotta sovellus voi käyttää niitä. Tämä voi lisätä akun kulutusta."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"käyttää tarkkaa sijaintia vain etualalla"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Tämä sovellus saa tarkat sijaintitietosi käyttöönsä vain etualalla. Näiden sijaintipalveluiden tulee olla käytössä ja käytettävissä puhelimellasi, jotta sovellus voi käyttää niitä. Tämä voi lisätä akun kulutusta."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"käyttää likimääräistä sijaintia (verkkopohjainen)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Tämä sovellus voi määrittää sijaintisi matkapuhelinverkon tukiasemien, Wi-Fi-verkkojen ja muiden verkkolähteiden perusteella. Näiden sijaintipalveluiden tulee olla käytössä ja käytettävissä tabletillasi, jotta sovellus voi käyttää niitä."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Tämä sovellus voi määrittää sijaintisi matkapuhelinverkon tukiasemien, Wi-Fi-verkkojen ja muiden verkkolähteiden perusteella. Näiden sijaintipalveluiden tulee olla käytössä ja käytettävissä TV:lläsi, jotta sovellus voi käyttää niitä."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Tämä sovellus voi määrittää sijaintisi matkapuhelinverkon tukiasemien, Wi-Fi-verkkojen ja muiden verkkolähteiden perusteella. Näiden sijaintipalveluiden tulee olla käytössä ja käytettävissä puhelimellasi, jotta sovellus voi käyttää niitä."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"käyttää tarkkaa sijaintia taustalla"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Tämä sovellus saa tarkat sijaintitietosi käyttöönsä taustalla. Näiden sijaintipalveluiden tulee olla käytössä ja käytettävissä puhelimellasi, jotta sovellus voi käyttää niitä. Tämä voi lisätä akun kulutusta."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"muuta ääniasetuksia"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Antaa sovelluksen muokata yleisiä ääniasetuksia, kuten äänenvoimakkuutta ja käytettävää kaiutinta."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"tallentaa ääntä"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Sormenjälkikuvake"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"hallinnoida kasvojentodennuslaitteistoa"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Sallii sovelluksen käyttää menetelmiä, joilla voidaan lisätä tai poistaa kasvomalleja."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"käyttää kasvojentodennuslaitteistoa"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Sallii sovelluksen käyttää todennuslaitteistoa todennukseen"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Kasvojen käsittely epäonnistui. Yritä uudelleen."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Kasvokuva on liian kirkas. Kokeile hämärää valoa."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Kasvokuva on liian tumma. Älä peitä valonlähdettä."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Siirrä anturia kauemmas kasvoista."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Tuo anturi lähemmäs kasvoja."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Siirrä anturia korkeammalle."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Siirrä anturia alemmas."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Siirrä anturia oikealle."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Siirrä anturia vasemmalle."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Katso anturia."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Kasvoja ei havaittu."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Pidä kasvot paikoillaan laitteen edessä."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Kasvolaitteisto ei ole käytettävissä."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Kasvotoiminto aikakatkaistiin. Yritä uudelleen."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Kasvoja ei voi tallentaa."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Kasvotoiminto peruutettu"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Liian monta yritystä. Yritä myöhemmin uudelleen."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Liikaa yrityksiä. Kasvojentodennus ei käytössä."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Yritä uudelleen."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Kasvoja ei ole otettu käyttöön."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Laitteessa ei ole kasvojentodennusanturia."</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Kasvot <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Kasvokuvake"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lue synkronointiasetuksia"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Antaa sovelluksen lukea tilien synkronointiasetuksia. Sovellus voi esimerkiksi määrittää, onko Henkilöt-sovellus synkronoitu tilin kanssa."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ota synkronointi käyttöön tai poista se käytöstä"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi ei ole yhteydessä internetiin"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Näytä vaihtoehdot napauttamalla."</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Hotspot-asetustesi muutokset"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Hotspot-taajuutesi on muuttunut."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Tämä laite ei tue asetustasi (vain 5 GHz). Sen sijaan laite käyttää 5 GHz:n taajuutta sen ollessa käytettävissä."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> otettiin käyttöön"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> otetaan käyttöön, kun <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ei voi muodostaa yhteyttä internetiin. Veloitukset ovat mahdollisia."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> poistettiin käytöstä ja <xliff:g id="NEW_NETWORK">%2$s</xliff:g> otettiin käyttöön."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 0c9bfa0..eaa2ba0 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mode Avion"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Le mode Avion est activé."</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Le mode Avion est désactivé."</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Économie d\'énergie"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"La fonction Économie d\'énergie est désactivée"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"La fonction Économie d\'énergie est activée"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Paramètres"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assistance"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Assist. vocale"</string>
@@ -272,22 +269,25 @@
     <string name="managed_profile_label" msgid="8947929265267690522">"Passer au profil professionnel"</string>
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Contacts"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"accéder à vos contacts"</string>
-    <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder à vos contacts?"</string>
+    <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à vos contacts?"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Localisation"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"accéder à la position de cet appareil"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position de cet appareil?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"L\'application aura uniquement accès à la position lorsque vous l\'utilisez."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Toujours autoriser <xliff:g id="APP_NAME">%1$s</xliff:g> à accéder à la position de cet appareil?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"L\'application aura toujours accès à la position, même lorsque vous ne l\'utilisez pas."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"accéder à votre agenda"</string>
-    <string name="permgrouprequest_calendar" msgid="289900767793189421">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder à votre agenda?"</string>
+    <string name="permgrouprequest_calendar" msgid="289900767793189421">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à votre agenda?"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"Messagerie texte"</string>
     <string name="permgroupdesc_sms" msgid="4656988620100940350">"envoyer et afficher des messages texte"</string>
-    <string name="permgrouprequest_sms" msgid="7168124215838204719">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à envoyer et à afficher des messages texte?"</string>
+    <string name="permgrouprequest_sms" msgid="7168124215838204719">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;  à envoyer et à afficher des messages texte?"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"Stockage"</string>
     <string name="permgroupdesc_storage" msgid="637758554581589203">"accéder aux photos, aux contenus multimédias et aux fichiers sur votre appareil"</string>
     <string name="permgrouprequest_storage" msgid="7885942926944299560">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder aux photos, aux médias et aux fichiers de votre appareil?"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microphone"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"enregistrer des fichiers audio"</string>
-    <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à enregistrer de l\'audio?"</string>
+    <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&amp;gt à enregistrer l\'audio?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Appareil photo"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"prendre des photos et filmer des vidéos"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à prendre des photos et à filmer des vidéos?"</string>
@@ -296,7 +296,7 @@
     <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à vos journaux d\'appels?"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Téléphone"</string>
     <string name="permgroupdesc_phone" msgid="6234224354060641055">"faire et gérer des appels téléphoniques"</string>
-    <string name="permgrouprequest_phone" msgid="9166979577750581037">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à faire et à gérer les appels téléphoniques?"</string>
+    <string name="permgrouprequest_phone" msgid="9166979577750581037">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à faire et à gérer les appels téléphoniques?"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Capteurs corporels"</string>
     <string name="permgroupdesc_sensors" msgid="7147968539346634043">"accéder aux données des capteurs sur vos signes vitaux"</string>
     <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder aux données des capteurs pour vos signes vitaux?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Cette application peut ajouter, supprimer et modifier des événements d\'agenda sur votre téléphone. Elle peut aussi envoyer des messages qui pourraient sembler venir des propriétaires d\'agenda en question ou modifier des événements sans avertir leur propriétaire."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accéder aux commandes de fournisseur de position géographique supplémentaires"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permet à l\'application d\'accéder à des commandes de localisation supplémentaires offertes par le fournisseur. Elle est ainsi susceptible d\'interférer avec le bon fonctionnement du GPS ou de toute autre source de localisation."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"accéder à votre position précise (GPS et réseau)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Cette application peut déterminer votre position à l\'aide du système GPS et de différentes sources de localisation sur le réseau, comme les tours de téléphonie cellulaire et les réseaux Wi-Fi. Ces services de localisation doivent être activés et accessibles sur votre téléviseur pour que l\'application puisse les utiliser. Cela peut entraîner une utilisation accrue de la pile."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"accéder à votre position précise seulement en avant-plan"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Cette application peut obtenir votre position exacte seulement lorsqu\'elle fonctionne en avant-plan. Ces services de localisation doivent être activés et accessibles sur votre téléviseur pour que l\'application puisse les utiliser. Cela peut entraîner une utilisation accrue de la pile."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"accéder à votre position approximative (réseau)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Cette application peut déterminer votre position à l\'aide de différentes sources de localisation sur le réseau, comme les tours de téléphonie cellulaire et les réseaux Wi-Fi. Ces services de localisation doivent être activés et accessibles sur votre tablette pour que l\'application puisse les utiliser."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Cette application peut déterminer votre position à l\'aide de différentes sources de localisation sur le réseau, comme les tours de téléphonie cellulaire et les réseaux Wi-Fi. Ces services de localisation doivent être activés et accessibles sur votre téléviseur pour que l\'application puisse les utiliser."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Cette application peut déterminer votre position à l\'aide de différentes sources de localisation sur le réseau, comme les tours de téléphonie cellulaire et les réseaux Wi-Fi. Ces services de localisation doivent être activés et accessibles sur votre téléphone pour que l\'application puisse les utiliser."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"accéder à votre position précise en arrière-plan"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Cette application peut obtenir votre position exacte à tout moment en arrière-plan. Ces services de localisation doivent être activés et accessibles sur votre téléviseur pour que l\'application puisse les utiliser. Cela peut entraîner une utilisation accrue de la pile."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modifier vos paramètres audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permet à l\'application de modifier les paramètres audio généraux, tels que le volume et la sortie audio utilisée."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"enregistrer des fichiers audio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icône d\'empreinte digitale"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"gérer le matériel d\'authentification de visage"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Permet à l\'appli d\'employer des méthodes d\'aj. et de suppr. de modèles de reconn. visage."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"utiliser le matériel d\'authentification de visage"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permet à l\'appli d\'utiliser du matériel de reconnaissance du visage pour l\'authentification"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Impossible de traiter le visage. Réessayez."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Visage trop lumineux. Essayez avec moins de lumière."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Visage trop sombre. Essayez avec plus de lumière."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Veuillez éloigner le capteur du visage."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Veuillez rapprocher le capteur du visage."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Veuillez déplacer le capteur plus haut."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Veuillez déplacer le capteur plus bas."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Veuillez déplacer le capteur vers la droite."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Veuillez déplacer le capteur vers la gauche."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Veuillez regarder le capteur."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Aucun visage détecté."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Gardez le visage stable devant l\'appareil."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Matériel de reconnaissance du visage indisponible."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Temps de reconn. visage écoulé. Veuillez réessayer."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Impossible de stocker le visage."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Opération de reconnaissance du visage annulée."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Trop de tentatives. Veuillez réessayer plus tard."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Trop de tentatives. Capt. reconn. visage désactivé."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Réessayez."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Aucun visage inscrit."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Cet appareil ne possède pas de capteur de reconn. du visage"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Visage <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Icône visage"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lire les paramètres de synchronisation"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet à l\'application d\'accéder aux paramètres de synchronisation d\'un compte. Par exemple, cette autorisation peut permettre de déterminer si l\'application Contacts est synchronisée avec un compte ou non."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activer ou désactiver la synchronisation"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Le réseau Wi-Fi ne dispose d\'aucun accès à Internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Touchez pour afficher les options"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Modifications apportées à vos paramètres de point d\'accès"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"La bande de votre point d\'accès a changé."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Cet appareil ne prend pas en charge votre préférence pour la bande de 5 GHz seulement. Au lieu de cela, cet appareil utilisera la bande de 5 GHz lorsqu\'elle sera disponible."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Passé au réseau <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quand <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas d\'accès à Internet. Des frais peuvent s\'appliquer."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Passé du réseau <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> au <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 3ce604a..e69584b 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mode Avion"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Le mode Avion est activé."</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Le mode Avion est désactivé."</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Économiseur de batterie"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Économiseur de batterie DÉSACTIVÉ"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Économiseur de batterie ACTIVÉ"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Paramètres"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assistance"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Assistance vocale"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Localisation"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"accéder à la position de l\'appareil"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Permettre à &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; d\'accéder à la position de cet appareil ?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"L\'application n\'a accès à la position de l\'appareil que lorsqu\'elle est en cours d\'utilisation."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Toujours autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position de l\'appareil ?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"L\'application a toujours accès à la position de l\'appareil, même lorsqu\'elle n\'est pas en cours d\'utilisation."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"accéder à votre agenda"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Permettre à &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; d\'accéder à votre agenda ?"</string>
@@ -293,7 +293,7 @@
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Permettre à &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; de prendre des photos et de filmer des vidéos ?"</string>
     <string name="permgrouplab_calllog" msgid="8798646184930388160">"Journaux d\'appels"</string>
     <string name="permgroupdesc_calllog" msgid="3006237336748283775">"Lire et écrire les journaux d\'appels du téléphone"</string>
-    <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux journaux d\'appels de votre téléphone ?"</string>
+    <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Permettre à &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; d\'accéder aux journaux d\'appels de votre téléphone ?"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Téléphone"</string>
     <string name="permgroupdesc_phone" msgid="6234224354060641055">"effectuer et gérer des appels téléphoniques"</string>
     <string name="permgrouprequest_phone" msgid="9166979577750581037">"Permettre à &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; de passer et gérer des appels téléphoniques ?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Cette application peut ajouter, supprimer ou modifier les événements d\'agenda enregistrés sur votre téléphone. Elle peut en outre envoyer des messages qui semblent provenir du propriétaire de l\'agenda, ou modifier des événements sans en informer leur propriétaire."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Accès aux commandes de fournisseur de position géographique supplémentaires"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permet à l\'application d\'accéder à des commandes de localisation supplémentaires offertes par le fournisseur. Elle est ainsi susceptible d\'interférer avec le bon fonctionnement du GPS ou de toute autre source de localisation."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"accéder à votre position précise (GPS et réseau)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Cette application peut obtenir votre position via le GPS ou des sources de positionnement de réseau telles que les antennes-relais et les réseaux Wi-Fi. Ces services de localisation doivent être activés et disponibles sur votre téléphone pour que l\'application puisse les utiliser. Ceci peut réduire l\'autonomie de la batterie."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"accéder à la position exacte au premier plan uniquement"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Cette application peut obtenir votre position exacte uniquement lorsqu\'elle s\'exécute au premier plan. Ces services de localisation doivent être activés et disponibles sur votre téléphone pour que l\'application puisse les utiliser. Ceci peut réduire l\'autonomie de la batterie."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"accéder à votre position approximative (selon le réseau)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Cette application peut obtenir votre position via des sources de réseau telles que les antennes-relais et les réseaux Wi-Fi. Ces services de localisation doivent être activés et disponibles sur votre tablette pour que l\'application puisse les utiliser."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Cette application peut obtenir votre position via des sources de réseau telles que les antennes-relais et les réseaux Wi-Fi. Ces services de localisation doivent être activés et disponibles sur votre téléviseur pour que l\'application puisse les utiliser."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Cette application peut obtenir votre position via des sources de réseau telles que les antennes-relais et les réseaux Wi-Fi. Ces services de localisation doivent être activés et disponibles sur votre téléphone pour que l\'application puisse les utiliser."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"accéder à la position exacte en arrière-plan"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Cette application peut obtenir votre position exacte à tout moment lorsqu\'elle s\'exécute en arrière-plan. Ces services de localisation doivent être activés et disponibles sur votre téléphone pour que l\'application puisse les utiliser. Ceci peut réduire l\'autonomie de la batterie."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modifier vos paramètres audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permet à l\'application de modifier les paramètres audio généraux, tels que le volume et la sortie audio utilisée."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"enregistrer des fichiers audio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icône d\'empreinte digitale"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"gérer le matériel d\'authentification faciale"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Autorise l\'appli à invoquer des méthodes pour ajouter et supprimer des modèles de visages."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"utiliser le matériel d\'authentification faciale"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Autorise l\'appli à utiliser le matériel d\'authentification faciale pour l\'authentification"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Impossible reconnaître visage. Veuillez réessayer."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Visage trop éclairé. Veuillez réduire la lumière."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Visage trop sombre. Veuillez accroître la lumière."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Veuillez éloigner le capteur de votre visage."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Veuillez rapprocher le capteur de votre visage."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Veuillez déplacer le capteur vers le haut."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Veuillez déplacer le capteur vers le bas."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Veuillez déplacer le capteur vers la droite."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Veuillez déplacer le capteur vers la gauche."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Veuillez regarder le capteur."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Aucun visage détecté."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Maintenez votre visage stable devant l\'appareil."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Matériel de reconnaissance faciale indisponible."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Délai de détection du visage expiré. Réessayez."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Impossible de stocker les informations du visage."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Opération de reconnaissance faciale annulée."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Trop de tentatives. Réessayez plus tard."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Trop d\'essais. Authentification faciale désactivée."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Réessayez."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Aucun visage enregistré."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Aucun capteur d\'authentification faciale sur cet appareil"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Visage <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Icône visage"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lire les paramètres de synchronisation"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet à l\'application d\'accéder aux paramètres de synchronisation d\'un compte. Par exemple, cette autorisation peut permettre de déterminer si l\'application Contacts est synchronisée avec un compte ou non."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activer/désactiver la synchronisation"</string>
@@ -605,7 +638,7 @@
     <string name="policylab_disableCamera" msgid="6395301023152297826">"Désactiver les appareils photo"</string>
     <string name="policydesc_disableCamera" msgid="2306349042834754597">"Empêcher l\'utilisation de tous les appareils photos"</string>
     <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Désact. options du verr. écran"</string>
-    <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Empêchez l\'utilisation de certaines fonctionnalités du verrouillage de l\'écran."</string>
+    <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Empêcher l\'utilisation de certaines fonctionnalités du verrouillage de l\'écran."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Domicile"</item>
     <item msgid="869923650527136615">"Mobile"</item>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Impossible de se connecter à Internet via le réseau Wi-Fi"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Appuyez ici pour afficher des options."</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Modifications apportées à vos paramètres de point d\'accès"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Votre bande de point d\'accès a été modifiée."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Cet appareil n\'est pas compatible avec votre préférence d\'utilisation de la bande 5 GHz uniquement. Il utilisera la bande 5 GHz lorsqu\'elle sera disponible."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Nouveau réseau : <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> lorsque <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas de connexion Internet. Des frais peuvent s\'appliquer."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Ancien réseau : <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>. Nouveau réseau : <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1858,7 +1894,7 @@
     <string name="mmcc_illegal_ms_msim_template" msgid="5994323296399913454">"Carte SIM <xliff:g id="SIMNUMBER">%d</xliff:g> non autorisée"</string>
     <string name="mmcc_illegal_me_msim_template" msgid="5550259730350571826">"Carte SIM <xliff:g id="SIMNUMBER">%d</xliff:g> non autorisée"</string>
     <string name="popup_window_default_title" msgid="4874318849712115433">"Fenêtre pop-up"</string>
-    <string name="slice_more_content" msgid="8504342889413274608">"<xliff:g id="NUMBER">%1$d</xliff:g> autres"</string>
+    <string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="shortcut_restored_on_lower_version" msgid="4860853725206702336">"La version de l\'application est revenue à une version antérieure ou n\'est pas compatible avec cet raccourci"</string>
     <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Le raccourci ne peut pas être restauré car l\'application n\'accepte pas la sauvegarde et la restauration"</string>
     <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Le raccourci ne peut pas être restauré car la signature de l\'application est différente"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index d80dc01..a5327f6 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo avión"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"O modo avión está activado"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"O modo avión está desactivado"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Aforro de batería"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"A función Aforro de batería está DESACTIVADA"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"A función Aforro de batería está ACTIVADA"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Configuración"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Asistencia"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Asistente voz"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Localización"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"acceder á localización deste dispositivo"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda á localización deste dispositivo?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"A aplicación só terá acceso á localización mentres a esteas utilizando."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda á localización?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"A aplicación sempre terá acceso á localización, aínda que non a esteas utilizando."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendario"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceder ao teu calendario"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda ao teu calendario?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Esta aplicación pode engadir, quitar ou cambiar eventos do calendario almacenados no teu teléfono. Tamén pode enviar mensaxes que parezan dos propietarios do calendario e cambiar eventos sen comunicárllelo aos propietarios."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acceder a comandos adicionais do provedor de localización"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite á aplicación acceder a comandos adicionais de fornecedor de localizacións. É posible que isto provoque que a aplicación interfira co funcionamento do GPS ou doutras fontes da localización."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"acceder á localización precisa (baseada no GPS e na rede)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Esta aplicación pode obter a túa localización a partir do GPS ou de fontes de localización de rede como torres de telecomunicacións e redes wifi. Para que a aplicación poida utilizar os servizos de localización, deben estar activados e dispoñibles no teu teléfono. Ten en conta que con esta acción pode aumentar o consumo de batería."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"acceder á localización exacta só en primeiro plano"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Esta aplicación pode obter a túa localización exacta só cando se atope en primeiro plano. É necesario activar estes servizos de localización e deben estar dispoñibles no teléfono para que a aplicación poida utilizalos. Ademais, poden supoñer un aumento do consumo de batería."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acceder á localización aproximada (baseada na rede)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Esta aplicación pode obter a túa localización a partir de fontes de rede como torres de telecomunicacións e redes wifi. Para que a aplicación poida utilizar os servizos de localización, deben estar activados e dispoñibles na túa tableta."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Esta aplicación pode obter a túa localización a partir de fontes de rede como torres de telecomunicacións e redes wifi. Para que a aplicación poida utilizar os servizos de localización, deben estar activados e dispoñibles na túa televisión."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Esta aplicación pode obter a túa localización a partir de fontes de rede como torres de telecomunicacións e redes wifi. Para que a aplicación poida utilizar os servizos de localización, deben estar activados e dispoñibles no teu teléfono."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"acceder á localización exacta en segundo plano"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Esta aplicación pode obter a túa localización exacta cando estea en segundo plano. É necesario activar estes servizos de localización e deben estar dispoñibles no teléfono para que a aplicación poida utilizalos. Ademais, poden supoñer un aumento do consumo de batería."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"cambiar a configuración de son"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite á aplicación modificar a configuración de audio global, como o volume e que altofalante se utiliza para a saída."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"gravar audio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona de impresión dixital"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"xestionar hardware de autenticación facial"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que a aplicación invoque métodos para engadir e eliminar modelos faciais de uso."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"usar hardware de autenticación facial"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite que a aplicación utilice hardware facial para a autenticación"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Non se puido procesar a cara. Téntao de novo."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"A cara vese demasiado brillante. Proba con menos luz."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"A cara vese demasiado escura. Proba con máis luz."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Afasta o sensor da cara."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Achega o sensor á cara."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Sube máis o sensor."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Baixa o sensor."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Move o sensor cara á dereita."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Move o sensor cara á esquerda."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Mira ao sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Non se detectou ningunha cara."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantén a cara fixa diante do dispositivo."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"O hardware facial non está dispoñible."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Esgotouse o tempo de espera. Téntao de novo."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Non se puido almacenar a cara."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Cancelouse a operación relacionada coa cara"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Demasiados intentos. Téntao de novo máis tarde."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Demasiados intentos. Autenticación desactivada."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Téntao de novo."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Non se rexistrou ningunha cara."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Este dispositivo non ten un sensor de autenticación facial"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Cara <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Icona cara"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler a configuración de sincronización"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite á aplicación ler a configuración de sincronización dunha conta. Por exemplo, esta acción pode determinar se a aplicación Contactos se sincroniza cunha conta."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar e desactivar a sincronización"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"A wifi non ten acceso a Internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toca para ver opcións."</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Cambios na configuración da zona wifi"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Modificouse a banda da zona wifi."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Este dispositivo non admite a opción de conectarse só a bandas de 5 GHz, pero usaraas se están dispoñibles."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Cambiouse a: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ten acceso a Internet. Pódense aplicar cargos."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Cambiouse de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index ea96ddc..d41a923 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"એરપ્લેન મોડ"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"એરપ્લેન મોડ ચાલુ છે."</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"એરપ્લેન મોડ બંધ છે."</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"બૅટરી સેવર"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"બૅટરી સેવર બંધ છે"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"બૅટરી સેવર ચાલુ છે"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"સેટિંગ્સ"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"સહાય"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"વૉઇસ સહાય"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"સ્થાન"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"માત્ર જ્યારે તમે ઍપનો ઉપયોગ કરી રહ્યાં હોય ત્યારે જ ઍપ સ્થાનને ઍક્સેસ કરી શકશે."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને હંમેશાં આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"તમે ઍપનો ઉપયોગ કરી રહ્યાં ન હોય, તો પણ ઍપ હંમેશાં સ્થાનને ઍક્સેસ કરી શકશે."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"કૅલેન્ડર"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"તમારા કેલેન્ડરને ઍક્સેસ કરવાની"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને તમારા કૅલેન્ડરને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"આ ઍપ્લિકેશન, તમારા ફોન પર કૅલેન્ડર ઇવેન્ટ્સ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ્લિકેશન, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં સંદેશા મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ્સ બદલી શકે છે."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરો"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"એપ્લિકેશનને વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને GPS અથવા અન્ય સ્થાન સ્રોતોના ઓપરેશનમાં દખલ કરવાની મંજૂરી આપી શકે છે."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"નિશ્ચિત સ્થાન ઍક્સેસ કરો (GPS અને નેટવર્ક-આધારિત)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"આ ઍપ્લિકેશન, GPS અથવા સેલ ટાવર્સ અને વાઇ-ફાઇ નેટવર્ક્સ જેવા નેટવર્ક સ્રોતોના આધારે તમારું સ્થાન મેળવી શકે છે. ઍપ્લિકેશન દ્વારા આ સ્થાન સેવાઓનો ઉપયોગ કરવામાં સમર્થ થવા માટે તમારા ફોન પર આ ઉપલબ્ધ અને ચાલુ હોવી આવશ્યક છે. આ બૅટરી વપરાશ વધારી શકે છે."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ફૉરગ્રાઉન્ડમાં ફક્ત ચોક્કસ સ્થાન ઍક્સેસ કરો"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"આ ઍપ ફક્ત બૅકગ્રાઉન્ડમાં હોય ત્યારે જ તમારું ચોક્કસ સ્થાન મેળવી શકે છે. ઍપ આ સ્થાન સેવાઓનો ઉપયોગ કરી શકે તે માટે તમારા ફોન પર આ સેવાઓ ઉપલબ્ધ અને ચાલુ હોવી આવશ્યક છે. આ બૅટરી વપરાશ વધારી શકે છે."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"અંદાજિત સ્થાન ઍક્સેસ કરો (નેટવર્ક-આધારિત)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"આ ઍપ્લિકેશન, સેલ ટાવર્સ અને વાઇ-ફાઇ નેટવર્ક્સ જેવા નેટવર્ક સ્રોતોના આધારે તમારું સ્થાન મેળવી શકે છે. ઍપ્લિકેશન દ્વારા આ સ્થાન સેવાઓનો ઉપયોગ કરવામાં સમર્થ થવા માટે તમારા ટેબ્લેટ પર આ ઉપલબ્ધ અને ચાલુ હોવી આવશ્યક છે."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"આ ઍપ્લિકેશન, સેલ ટાવર્સ અને વાઇ-ફાઇ નેટવર્ક્સ જેવા નેટવર્ક સ્રોતોના આધારે તમારું સ્થાન મેળવી શકે છે. ઍપ્લિકેશન દ્વારા આ સ્થાન સેવાઓનો ઉપયોગ કરવામાં સમર્થ થવા માટે તમારા ટીવી પર આ ઉપલબ્ધ અને ચાલુ હોવી આવશ્યક છે."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"આ ઍપ્લિકેશન, સેલ ટાવર્સ અને વાઇ-ફાઇ નેટવર્ક્સ જેવા નેટવર્ક સ્રોતોના આધારે તમારું સ્થાન મેળવી શકે છે. ઍપ્લિકેશન દ્વારા આ સ્થાન સેવાઓનો ઉપયોગ કરવામાં સમર્થ થવા માટે તમારા ફોન પર આ ઉપલબ્ધ અને ચાલુ હોવી આવશ્યક છે."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"બૅકગ્રાઉન્ડમાં ચોક્કસ સ્થાન ઍક્સેસ કરો"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"આ ઍપ બૅકગ્રાઉન્ડમાં હોય ત્યારે કોઈપણ સમયે તમારું ચોક્કસ સ્થાન મેળવી શકે છે. ઍપ આ સ્થાન સેવાઓનો ઉપયોગ કરી શકે તે માટે તમારા ફોન પર આ સેવાઓ ઉપલબ્ધ અને ચાલુ હોવી આવશ્યક છે. આ બૅટરી વપરાશ વધારી શકે છે."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"તમારી ઑડિઓ સેટિંગ્સ બદલો"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"એપ્લિકેશનને વૈશ્વિક ઑડિઓ સેટિંગ્સને સંશોધિત કરવાની મંજૂરી આપે છે, જેમ કે વૉલ્યૂમ અને આઉટપુટ માટે કયા સ્પીકરનો ઉપયોગ કરવો."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ઑડિઓ રેકોર્ડ કરવાની"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ફિંગરપ્રિન્ટ આયકન"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"ચહેરા પ્રમાણીકરણના હાર્ડવેરને મેનેજ કરો"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"ઍપને ઉપયોગ માટે ચહેરાના નમૂના ઉમેરવા અને ડિલીટ કરવાની પદ્ધતિને રદ કરવાની મંજૂરી આપે છે."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ચહેરા પ્રમાણીકરણના હાર્ડવેરનો ઉપયોગ કરો"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ઍપને પ્રમાણીકરણ માટે ચહેરા પ્રમાણીકરણના હાર્ડવેરનો ઉપયોગ કરવાની મંજૂરી આપે છે"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"ચહેરાની પ્રક્રિયા કરી શકાઈ નથી. ફરી પ્રયાસ કરો."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"ચહેરો ખૂબ ચળકે છે. કૃપા કરીને ઓછા પ્રકાશવાળા સ્થાનમાં પ્રયાસ કરો."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"ચહેરો ખૂબ શ્યામ છે. કૃપા કરીને પ્રકાશના સૉર્સ ઉઘાડો."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"કૃપા કરીને સેન્સરને ચહેરાથી દૂર ખસેડો."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"કૃપા કરીને સેન્સરને ચહેરાની નજીક ખસેડો."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"કૃપા કરીને સેન્સરને ઉપર ખસેડો."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"કૃપા કરીને સેન્સર નીચે ખસેડો."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"કૃપા કરીને સેન્સરને જમણી બાજુ ખસેડો."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"કૃપા કરીને સેન્સરને ડાબી બાજુ ખસેડો."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"કૃપા કરીને સેન્સરની સામે જુઓ."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"કોઈ ચહેરો મળ્યો નથી."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"ઉપકરણની સામે ચહેરો સ્થિર રાખો."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"ચહેરા માટેનું હાર્ડવેર ઉપલબ્ધ નથી."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"ચહેરા માટેનો સમય સમાપ્ત થયો. ફરી પ્રયાસ કરો."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"ચહેરો સંગ્રહિત કરી શકાશે નહીં."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"ચહેરા સંબંધિત કાર્યવાહી રદ કરવામાં આવી છે."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"ઘણા બધા પ્રયત્નો. થોડા સમય પછી ફરી પ્રયાસ કરો."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ઘણા બધા પ્રયત્નો. ચહેરાનું પ્રમાણીકરણ બંધ કરવામાં આવ્યું છે."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"ફરી પ્રયાસ કરો."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"કોઈ ચહેરાની નોંધણી કરવામાં આવી નથી."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"આ ઉપકરણમાં ચહેરાના પ્રમાણીકરણ માટે કોઈ સેન્સર નથી"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"ચહેરાનું <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"ચહેરા આઇકન"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"સમન્વયન સેટિંગ્સ વાંચો"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ઍપ્લિકેશનને એકાઉન્ટ માટે સમન્વયન સેટિંગ્સને વાંચવાની મંજૂરી આપે છે. ઉદાહરણ તરીકે, આ એકાઉન્ટ સાથે લોકો ઍપ્લિકેશન સમન્વયિત થઈ છે કે કેમ તે નિર્ધારિત કરી શકે છે."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"સમન્વયન ચાલુ અને બંધ ટોગલ કરો"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"વાઇ-ફાઇને કોઈ ઇન્ટરનેટ ઍક્સેસ નથી"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"વિકલ્પો માટે ટૅપ કરો"</string>
+    <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">"આ ઉપકરણ તમારી ફક્ત 5GHz માટેની પસંદગીને સમર્થન આપતું નથી. તેના બદલે, જ્યારે આ ઉપકરણ જ્યારે 5GHz બેન્ડ ઉપલબ્ધ હશે ત્યારે તેનો ઉપયોગ કરશે."</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>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 981178f..8c7a0d3 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"हवाई जहाज मोड"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"हवाई जहाज मोड चालू है"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"हवाई जहाज मोड बंद है"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"बैटरी सेवर"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"बैटरी सेवर बंद है"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"बैटरी सेवर चालू है"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"सेटिंग"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"सहायता"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"आवाज़ से डिवाइस का इस्तेमाल"</string>
@@ -275,7 +272,10 @@
     <string name="permgrouprequest_contacts" msgid="6032805601881764300">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपने संपर्क देखने की अनुमति देना चाहते हैं?"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"जगह"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"इस डिवाइस की जगह तक पहुंचने दें"</string>
-    <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को इस डिवाइस की जगह की जानकारी देखने की अनुमति देना चाहते हैं?"</string>
+    <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को इस डिवाइस की \'जगह की जानकारी\' ऐक्सेस करने की अनुमति देना चाहते हैं?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ऐप्लिकेशन, डिवाइस की \'जगह की जानकारी\' सिर्फ़ तभी देख पाएगा जब आप इसका इस्तेमाल कर रहे हों."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को हमेशा डिवाइस की \'जगह की जानकारी\' एक्सेस करने दें?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ऐप्लिकेशन के पास हमेशा डिवाइस की \'जगह की जानकारी\' देखने की मंज़ूरी होगी, तब भी जब आप इसका इस्तेमाल न कर रहे हों."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"कैलेंडर"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"अपने कैलेंडर को ऐक्सेस करने"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपना कैलेंडर देखने की अनुमति देना चाहते हैं?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"यह ऐप्लिकेशन आपके फ़ोन पर मौजूद कैलेंडर इवेंट जोड़, निकाल या बदल सकता है. यह ऐप्लिकेशन ऐसे संदेश भेज सकता है जो कैलेंडर स्वामियों से आए हुए लग सकते हैं या यह स्वामियों को सूचित किए बिना इवेंट में बदलाव कर सकता है."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"कुछ और जगह बताने वाले आदेशों तक पहुंच"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ऐप को कुछ और जगह की जानकारी देने वाले आदेशों की पहुंच पाने देता है. इससे ऐप जीपीएस या जगह की जानकारी देने वाले दूसरे स्रोतों के काम में रोक-टोक कर सकता है."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"सटीक जगह की जानकारी लेने दें (जीपीएस और नेटवर्क-आधारित)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"यह ऐप सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क स्रोतों या जीपीएस के आधार पर आपकी जगह पता कर सकता है. जगह की जानकारी आपके फ़ोन पर चालू और मौजूद होनी चाहिए ताकि ऐप उनका इस्तेमाल कर सके. इससे ज़्यादा बैटरी खर्च हो सकती है."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ऐप्लिकेशन \'जगह की सटीक जानकारी\' सिर्फ़ सामने खुली होने पर एक्सेस करे"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"यह ऐप्लिकेशन सिर्फ़ तब आपकी \'जगह की सटीक जानकारी\' का इस्तेमाल कर सकता है जब यह स्क्रीन पर दिखाई दे रहा हो. यह ज़रूरी है कि \'जगह की जानकारी\' वाली ये सेवाएं आपके फ़ोन में मौजूद हों और चालू की गई हों ताकि ऐप्लिकेशन उनका इस्तेमाल कर पाए. ऐसा करने से ज़्यादा बैटरी खर्च हो सकती है."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"अनुमानित जगह की पहुंच दें (नेटवर्क-आधारित)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"यह ऐप सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क के स्रोतों के आधार पर आपकी जगह का पता कर सकता है. ये जगह की जानकारी आपके टैबलेट पर चालू और मौजूद होनी चाहिए ताकि ऐप उनका इस्तेमाल कर सके."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"यह ऐप सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क के स्रोतों के आधार पर आपकी जगह का पता कर सकता है. ये जगह की जानकारी आपके टीवी पर चालू और मौजूद होनी चाहिए ताकि ऐप उनका इस्तेमाल कर सके."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"यह ऐप सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क के स्रोतों के आधार पर आपकी जगह का पता कर सकता है. ये जगह की जानकारी आपके फ़ोन पर चालू और मौजूद होनी चाहिए ताकि ऐप उनका इस्तेमाल कर सके."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"बैकग्राउंड में होने पर \'जगह की सटीक जानकारी\' एक्सेस करें"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"यह ऐप्लिकेशन बैकग्राउंड में चलते हुए, किसी भी समय आपकी \'जगह की सटीक जानकारी\' का इस्तेमाल कर सकता है. यह ज़रूरी है कि \'जगह की जानकारी\' वाली ये सेवाएं आपके फ़ोन में मौजूद हों और चालू की गई हों ताकि ऐप्लिकेशन उनका इस्तेमाल कर पाए. ऐसा करने से ज़्यादा बैटरी खर्च हो सकती है."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"अपनी ऑडियो सेटिंग बदलें"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ऐप्स  को वैश्विक ऑडियो सेटिंग, जैसे वॉल्‍यूम और कौन-सा स्पीकर आउटपुट के लिए उपयोग किया गया, संशोधित करने देता है."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ऑडियो रिकॉर्ड करने"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"फ़िंगरप्रिंट आइकॉन"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"चेहरे की पुष्टि करने वाला हार्डवेयर प्रबंधित करें"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"ऐप्लिकेशन को चेहरे के टेम्पलेट इस्तेमाल के तरीके जोड़ने और मिटाने की मंज़ूरी मिलती है."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"चेहरे की पुष्टि करने वाला हार्डवेयर इस्तेमाल करें"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ऐप्लिकेशन को चेहरे की पुष्टि करने वाले हार्डवेयर का इस्तेमाल करने की मंज़ूरी मिलती है"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"चेहरे की पहचान नहीं हो पाई. कृपया फिर कोशिश करें."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"चेहरे पर रोशनी ज़्यादा है. कृपया कम रोशनी में कोशिश करें."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"चेहरे पर रोशनी बहुत कम है. कृपया रोशनी बढ़ाएं."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"कृपया डिवाइस को चेहरे से दूर ले जाएं."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"कृपया डिवाइस को चेहरे के करीब लाएं."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"कृपया डिवाइस को ऊपर करें."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"कृपया डिवाइस को नीचे की ओर ले जाएं."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"कृपया डिवाइस को चेहरे की दाईं ओर ले जाएं."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"कृपया डिवाइस को चेहरे के बाईं ओर ले जाएं."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"कृपया सेंसर की ओर देखें."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"चेहरे की पहचान नहीं हो पाई."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"डिवाइस के सामने चेहरे को स्थिर रखें."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"चेहरे की पहचान करने वाला हार्डवेयर मौजूद नहीं है."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"चेहरे की पहचान का समय खत्म हुआ. फिर से कोशिश करें."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"चेहरा सेव करने की सीमा पूरी हो गई है."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"चेहरा पहचानने की कार्रवाई रद्द की गई."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"कई बार कोशिश की गई. बाद में कोशिश करें."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"कई बार कोशिश की. चेहरा पहचानने की सुविधा बंद हुई."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"फिर से कोशिश करें."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"कोई चेहरा रजिस्टर नहीं किया गया है."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"इस डिवाइस में चेहरे की पहचान करने वाला सेंसर नहीं है"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"चेहरा <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"चेहरे का आइकॉन"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"समन्वयन सेटिंग पढ़ें"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ऐप्स  को किसी खाते की समन्वयन सेटिंग पढ़ने देता है. उदाहरण के लिए, इससे यह निर्धारित किया जा सकता है कि लोग ऐप्स  किसी खाते के साथ समन्‍वयित है या नहीं."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"समन्‍वयन बंद या चालू टॉगल करें"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"वाई-फ़ाई के लिए इंटरनेट नहीं मिल रहा है"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"विकल्पों के लिए टैप करें"</string>
+    <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">"यह डिवाइस सिर्फ़ 5 गीगाहर्ट्ज़ की आपकी पसंद की सेटिंग पर काम नहीं करता, लेकिन जब भी 5 गीगाहर्ट्ज़ बैंड मौजूद होगा, डिवाइस उसका इस्तेमाल करने लगेगा."</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>
@@ -1673,7 +1709,7 @@
       <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> सेकंड में पुन: प्रयास करें</item>
     </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"बाद में फिर से प्रयास करें"</string>
-    <string name="immersive_cling_title" msgid="8394201622932303336">"पूरे स्क्रीन पर देखें"</string>
+    <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>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index c0b0468..3baaf01 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -239,9 +239,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Način rada u zrakoplovu"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Uključen je način rada u zrakoplovu"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Isključen je način rada u zrakoplovu"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Štednja baterije"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Štednja baterije isključena"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Štednja baterije uključena"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Postavke"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Pomoć"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Glasovna pomoć"</string>
@@ -279,6 +276,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Lokacija"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"pristupiti lokaciji ovog uređaja"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa lokaciji ovog uređaja?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikacija će imati pristup lokaciji samo dok upotrebljavate aplikaciju."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa lokaciji ovog uređaja?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikacija će uvijek imati pristup lokaciji, čak i dok ne upotrebljavate aplikaciju."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendar"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"pristupati kalendaru"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa vašem kalendaru?"</string>
@@ -405,12 +405,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Aplikacija može dodavati, uklanjati ili mijenjati kalendarske događaje na telefonu. Može slati poruke koje izgledaju kao da ih je poslao vlasnik kalendara ili mijenjati događaje bez znanja vlasnika."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pristup dodatnim naredbama davatelja lokacije"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Omogućuje aplikaciji pristup dodatnim naredbama davatelja usluga lokacije. To može omogućiti aplikaciji ometanje rada GPS-a ili drugih izvora lokacije."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"pristupati preciznoj lokaciji (na temelju GPS-a i mreža)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Aplikacija može dohvatiti vašu lokaciju putem GPS-a ili mrežnih izvora lokacije poput baznih stanica i Wi-Fi mreža. Te usluge lokacije moraju biti uključene i dostupne na telefonu da bi ih aplikacija mogla upotrebljavati. To može povećati potrošnju baterije."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"pristupiti preciznoj lokaciji samo u prednjem planu"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Aplikacija može dobiti vašu točnu lokaciju samo kada je u prednjem planu. Te usluge lokacije moraju biti uključene i dostupne na telefonu da bi ih aplikacija mogla upotrebljavati. To može pojačati potrošnju baterije."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"pristupati približnoj lokaciji (na temelju mreža)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Aplikacija može dohvatiti vašu lokaciju putem mrežnih izvora poput baznih stanica i Wi-Fi mreža. Te usluge lokacije moraju biti uključene i dostupne na tabletu da bi ih aplikacija mogla upotrebljavati."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Aplikacija može dohvatiti vašu lokaciju putem mrežnih izvora poput baznih stanica i Wi-Fi mreža. Te usluge lokacije moraju biti uključene i dostupne na televizoru da bi ih aplikacija mogla upotrebljavati."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Aplikacija može dohvatiti vašu lokaciju putem mrežnih izvora poput baznih stanica i Wi-Fi mreža. Te usluge lokacije moraju biti uključene i dostupne na telefonu da bi ih aplikacija mogla upotrebljavati."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"pristupiti preciznoj lokaciji u pozadini"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Aplikacija može dobiti vašu točnu lokaciju svaki put kada je u pozadini. Te usluge lokacije moraju biti uključene i dostupne na telefonu da bi ih aplikacija mogla upotrebljavati. To može pojačati potrošnju baterije."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"promjena postavki zvuka"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Aplikaciji omogućuje izmjenu globalnih postavki zvuka, primjerice glasnoće i zvučnika koji se upotrebljava za izlaz."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"snimanje zvuka"</string>
@@ -518,6 +520,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona otiska prsta"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"upravljati hardverom za autentifikaciju lica"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Aplikaciji omogućuje pozivanje načina za dodavanje i brisanje predložaka lica za upotrebu."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"upotrebljavati hardver za autentifikaciju lica"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Aplikaciji omogućuje upotrebu hardvera za autentifikaciju lica radi autentifikacije"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Obrada lica nije uspjela. Pokušajte ponovo."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Lice je presvijetlo. Smanjite osvjetljenje."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Lice je pretamno. Otkrijte izvor svjetlosti."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Odmaknite senzor od lica."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Približite senzor licu."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Podignite senzor."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Spustite senzor."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Pomaknite senzor udesno."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Pomaknite senzor ulijevo."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Gledajte senzor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Lice nije otkriveno."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Držite lice mirno ispred uređaja."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardver za lice nije dostupan."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Isteklo je vrijeme čekanja za lice. Pokušajte opet"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Nije moguće pohraniti lice."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Otkazana je radnja s licem."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Previše pokušaja. Autentifikacija lica onemogućena"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Pokušajte ponovo."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nije registrirano nijedno lice."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Ovaj uređaj nema senzor za autentifikaciju lica"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona lica"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"čitanje postavki sinkronizacije"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Aplikaciji omogućuje čitanje postavki sinkronizacije za račun. Time se, primjerice, može utvrditi je li aplikacija Osobe sinkronizirana s računom."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"uključivanje/isključivanje sinkronizacije"</string>
@@ -1197,6 +1230,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi nema pristup internetu"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Dodirnite za opcije"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Promjene postavki vaše žarišne točke"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Promijenila se frekvencija vaše žarišne točke."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Ovaj uređaj ne podržava vašu postavku za upotrebu samo 5 GHz. Upotrebljavat će frekvenciju od 5 GHz kada je dostupna."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Prelazak na drugu mrežu: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, na uređaju se upotrebljava <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata naknade."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Mreža je promijenjena: <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> &gt; <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 760ce6b..106f497 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Repülőgép üzemmód"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Repülőgép üzemmód bekapcsolva"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Repülőgép üzemmód kikapcsolva"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Akkumulátorkímélő mód"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Akkumulátorkímélő mód kikapcsolva"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Akkumulátorkímélő mód bekapcsolva"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Beállítások"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Segítség"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Hangsegéd"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Helyadatok"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"hozzáférés az eszköz földrajzi helyéhez"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen az eszköz helyadataihoz?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Az alkalmazás csak akkor férhet hozzá a helyadatokhoz, amikor használja az alkalmazást."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Engedélyezi, hogy a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; hozzáférjen a helyadatokhoz?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Az alkalmazás bármikor hozzáférhet majd a helyadatokhoz, akkor is, amikor nem használja az alkalmazást."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Naptár"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"hozzáférés a naptárhoz"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen a naptárhoz?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Az alkalmazás hozzáadhatja, eltávolíthatja vagy módosíthatja a telefonon található naptáreseményeket. Az alkalmazás olyan üzeneteket küldhet, amelyekről úgy tűnhet, hogy a naptár tulajdonosaitól származnak, illetve a tulajdonosok értesítése nélkül módosíthat eseményeket."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"további helyszolgáltatói parancsok elérése"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Lehetővé teszi az alkalmazás számára további helyszolgáltatói parancsok elérését. Ezáltal az alkalmazás beavatkozhat a GPS vagy más helyforrások működésébe."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"hozzáférés a pontos (GPS- és hálózatalapú) helyadatokhoz"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Az alkalmazás hozzáférhet a GPS-en vagy hálózati helyforrásokon (például az adótornyokon és a Wi-Fi-hálózatokon) alapuló helyadataihoz. A helyszolgáltatásoknak bekapcsolt és elérhető állapotban kell lenniük a telefonon ahhoz, hogy az alkalmazás használhassa őket. Ez növelheti az akkumulátorhasználatot."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"pontos helyadatokhoz való hozzáférés csak előtérben"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ez az alkalmazás csak akkor férhet hozzá az eszköz pontos helyadataihoz, amikor az előtérben fut. A helyszolgáltatásoknak bekapcsolt és hozzáférhető állapotban kell lenniük a telefonon ahhoz, hogy az alkalmazás használhassa őket. Ez növelheti az akkumulátorhasználatot."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"hozzáférés a hozzávetőleges (hálózatalapú) helyadatokhoz"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Az alkalmazás hozzáférhet hálózati forrásokon (például az adótornyokon és a Wi-Fi-hálózatokon) alapuló helyadataihoz. A helyszolgáltatásoknak bekapcsolt és elérhető állapotban kell lenniük a táblagépen ahhoz, hogy az alkalmazás használhassa őket."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Az alkalmazás hozzáférhet hálózati forrásokon (például az adótornyokon és a Wi-Fi-hálózatokon) alapuló helyadataihoz. A helyszolgáltatásoknak bekapcsolt és elérhető állapotban kell lenniük a TV-n ahhoz, hogy az alkalmazás használhassa őket."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Az alkalmazás hozzáférhet hálózati forrásokon (például az adótornyokon és a Wi-Fi-hálózatokon) alapuló helyadataihoz. A helyszolgáltatásoknak bekapcsolt és elérhető állapotban kell lenniük a telefonon ahhoz, hogy az alkalmazás használhassa őket."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"pontos helyadatokhoz való hozzáférés a háttérben"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ez az alkalmazás bármikor hozzáférhet az eszköz pontos helyadataihoz a háttérben. A helyszolgáltatásoknak bekapcsolt és hozzáférhető állapotban kell lenniük a telefonon ahhoz, hogy az alkalmazás használhassa őket. Ez növelheti az akkumulátorhasználatot."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"hangbeállítások módosítása"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Lehetővé teszi az alkalmazás számára az általános hangbeállítások, például a hangerő és a használni kívánt kimeneti hangszóró módosítását."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"hanganyag rögzítése"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ujjlenyomat ikon"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"arcfelismerő hardver kezelése"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Engedélyezi, hogy az alkalmazás arcsablon-hozzáadási és -törlési metódusokat hívjon."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"arcfelismerő hardver használata"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Engedélyezi, hogy az alkalmazás hitelesítésre használja az arcfelismerő hardvert"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Nem sikerült feldolgozni az arcot. Próbálja újra."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Az arc túl világos. Kevesebb fény szükséges."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Az arc túl sötét. Több fény szükséges."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Tartsa az érzékelőt távolabb az arctól."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Tartsa az érzékelőt közelebb az archoz."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Emelje magasabbra az érzékelőt."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Engedje lejjebb az érzékelőt."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Mozdítsa el jobbra az érzékelőt."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Mozdítsa el balra az érzékelőt."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Nézzen az érzékelőbe."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nem észlelhető arc."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Tartsa az eszközt egyenesen az arc előtt."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Az arcfelismerő hardver nem áll rendelkezésre."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Időtúllépés az arcbeolvasásnál. Próbálja újra."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Az arc nem tárolható."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Az arccal kapcsolatos művelet törölve."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Túl sok próbálkozás. Próbálja újra később."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Túl sok próbálkozás. Arcfelismerés letiltva."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Próbálkozzon újra."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nincs regisztrált arc."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Ez az eszköz nem rendelkezik arcfelismerő érzékelővel"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> arc"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Arcikon"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"szinkronizálási beállítások olvasása"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Lehetővé teszi az alkalmazás számára egy fiók szinkronizálási beállításainak beolvasását. Például ellenőrizheti, hogy a Személyek alkalmazás szinkronizálva van-e egy fiókkal."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"szinkronizálás be-és kikapcsolása"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"A Wi-Fi-hálózaton nincs internetkapcsolat"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Koppintson a beállítások megjelenítéséhez"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"A hotspot beállításainak módosítása"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"A hotspot sávja megváltozott."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Ez az eszköz nem támogatja a csak 5 GHz-es sávra vonatkozó beállítást. Az eszköz akkor használ 5 GHz-es sávot, ha a sáv rendelkezésre áll."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Átváltva erre: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> használata, ha nincs internet-hozzáférés <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>-kapcsolaton keresztül. A szolgáltató díjat számíthat fel."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Átváltva <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-hálózatról erre: <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 46f5925..1161062 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -219,16 +219,16 @@
     <string name="global_action_emergency" msgid="7112311161137421166">"Շտապ կանչ"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Վրիպակի զեկույց"</string>
     <string name="global_action_logout" msgid="935179188218826050">"Ավարտել աշխատաշրջանը"</string>
-    <string name="global_action_screenshot" msgid="8329831278085426283">"Էկրանի պատկեր"</string>
+    <string name="global_action_screenshot" msgid="8329831278085426283">"Սքրինշոթ"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Հաղորդել սխալի մասին"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Սա տեղեկություններ կհավաքագրի ձեր սարքի առկա կարգավիճակի մասին և կուղարկի այն էլեկտրոնային նամակով: Որոշակի ժամանակ կպահանջվի վրիպակի մասին զեկուցելու պահից սկսած մինչ ուղարկելը: Խնդրում ենք փոքր-ինչ համբերատար լինել:"</string>
     <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Ինտերակտիվ զեկույց"</string>
-    <string name="bugreport_option_interactive_summary" msgid="229299488536107968">"Հիմնականում օգտագործեք այս տարբերակը: Այն ձեզ թույլ է տալիս հետևել զեկույցի ստեղծման գործընթացին, խնդրի մասին լրացուցիչ տեղեկություններ մուտքագրել և էկրանի պատկերներ կորզել: Կարող է բաց թողնել քիչ օգտագործվող որոշ բաժինները, որոնց ստեղծումը երկար է տևում:"</string>
+    <string name="bugreport_option_interactive_summary" msgid="229299488536107968">"Հիմնականում օգտագործեք այս տարբերակը: Այն ձեզ թույլ է տալիս հետևել զեկույցի ստեղծման գործընթացին, խնդրի մասին լրացուցիչ տեղեկություններ մուտքագրել և սքրինշոթներ ստեղծել: Կարող է բաց թողնել քիչ օգտագործվող որոշ բաժիններ, որոնց ստեղծումը երկար է տևում:"</string>
     <string name="bugreport_option_full_title" msgid="6354382025840076439">"Ամբողջական զեկույց"</string>
     <string name="bugreport_option_full_summary" msgid="7210859858969115745">"Օգտագործեք այս տարբերակը համակարգի միջամտությունը նվազեցնելու համար՝ երբ սարքը չի արձագանքում կամ շատ դանդաղ է աշխատում, կամ երբ ձեզ հարկավոր են զեկույցի բոլոր բաժինները: Թույլ չի տալիս լրացուցիչ տվյալներ մուտքագրել կամ էկրանի լրացուցիչ պատկերներ ստանալ:"</string>
     <plurals name="bugreport_countdown" formatted="false" msgid="6878900193900090368">
-      <item quantity="one">Վրիպակի զեկույցի համար էկրանի պատկերի լուսանկարումը կատարվելու է <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:</item>
-      <item quantity="other">Վրիպակի զեկույցի համար էկրանի պատկերի լուսանկարումը կատարվելու է <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:</item>
+      <item quantity="one">Սքրինշոթը կարվի <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:</item>
+      <item quantity="other">Սքրինշոթը կարվի <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:</item>
     </plurals>
     <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Անձայն ռեժիմ"</string>
     <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Ձայնը անջատված է"</string>
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Ինքնաթիռի ռեժիմ"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Ինքնաթիռի ռեժիմը միացված է"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Ինքնաթիռի ռեժիմը անջատված է"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Մարտկոցի տնտեսում"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Անջատել"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Միացնել"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Կարգավորումներ"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Օգնական"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ձայնային օգնութ"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Տեղորոշում"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"տեղորոշել այս սարքը"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի տեղադրության տվյալները:"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Տեղադրության տվյալները հավելվածին հասանելի կլինեն միայն, երբ այն օգտագործելիս լինեք:"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Միշտ թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-ին օգտագործել սարքի տեղադրությունը:"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Տեղադրության տվյալները հավելվածին միշտ հասանելի կլինեն, նույնիսկ եթե այն չեք օգտագործում:"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Օրացույց"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"օգտագործել օրացույցը"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել ձեր օրացույցը:"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Այս հավելվածը կարող է ավելացնել, հեռացնել կամ փոխել օրացույցի միջոցառումները ձեր հեռախոսում: Այս հավելվածը կարող է ուղարկել հաղորդագրություններ օրացույցի սեփականատերերի անունից կամ փոխել միջոցառումները առանց դրանց սեփականատերերին ծանուցելու:"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"օգտագործել տեղադրություն տրամադրող հավելվյալ հրամաններ"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ծրագրին թույլ է տալիս օգտագործել տեղադրության մասին տվյալների աղբյուրների կառավարման լրացուցիչ հրահանգներ: Սա կարող է ծրագրին թույլ տալ միջամտել GPS-ի կամ տեղադրության մասին տվյալների այլ աղբյուրների գործառույթներին:"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"օգտագործել ճշգրիտ տեղադրությունը (GPS և ցանցային)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Այս հավելվածը կարող է ստանալ ձեր տեղադրության տվյալները GPS-ի միջոցով կամ տեղորոշման ցանցային աղբյուրներից, օրինակ՝ բջջային աշտարակներից և Wi-Fi ցանցերից: Այս տեղորոշման ծառայությունները պետք է միացված և հասանելի լինեն ձեր հեռախոսում, որպեսզի հավելվածը կարողանա օգտագործել դրանք: Դա կարող է արագացնել մարտկոցի լիցքի սպառումը:"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"Տեղադրության ճշգրիտ տվյալների հասանելիություն միայն ֆոնային ռեժիմում"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Հավելվածը կարող է ստանալ ձեր տեղադրության տվյալները ֆոնային ռեժիմում։ Այս տեղորոշման ծառայությունները պետք է միացված և հասանելի լինեն ձեր հեռախոսում, որպեսզի հավելվածը կարողանա օգտագործել դրանք: Սա կարող է արագացնել մարտկոցի լիցքի սպառումը:"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"օգտագործել մոտավոր տեղադրությունը (ցանցային)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Այս հավելվածը կարող է ստանալ ձեր տեղադրության տվյալները ցանցային տարբեր աղբյուրներից, օրինակ՝ բջջային աշտարակներից և Wi-Fi ցանցերից: Այս տեղորոշման ծառայությունները պետք է միացված և հասանելի լինեն ձեր պլանշետում, որպեսզի հավելվածը կարողանա օգտագործել դրանք:"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Այս հավելվածը կարող է ստանալ ձեր տեղադրության տվյալները ցանցային տարբեր աղբյուրներից, օրինակ՝ բջջային աշտարակներից և Wi-Fi ցանցերից: Այս տեղորոշման ծառայությունները պետք է միացված և հասանելի լինեն ձեր հեռուստացույցում, որպեսզի հավելվածը կարողանա օգտագործել դրանք:"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Այս հավելվածը կարող է ստանալ ձեր տեղադրության տվյալները ցանցային տարբեր աղբյուրներից, օրինակ՝ բջջային աշտարակներից և Wi-Fi ցանցերից: Այս տեղորոշման ծառայությունները պետք է միացված և հասանելի լինեն ձեր հեռախոսում, որպեսզի հավելվածը կարողանա օգտագործել դրանք:"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"Ֆոնային ռեժիմում տեղադրության տվյալների հասանելիություն"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Հավելվածը կարող է ստանալ ձեր տեղադրության տվյալները ֆոնային ռեժիմում։ Այս տեղորոշման ծառայությունները պետք է միացված և հասանելի լինեն ձեր հեռախոսում, որպեսզի հավելվածը կարողանա օգտագործել դրանք: Սա կարող է արագացնել մարտկոցի լիցքի սպառումը:"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"փոխել ձեր աուդիո կարգավորումները"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Թույլ է տալիս հավելվածին փոփոխել ձայնանյութի գլոբալ կարգավորումները, ինչպես օրինակ` ձայնը և թե որ խոսափողն է օգտագործված արտածման համար:"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ձայնագրել աուդիո ֆայլ"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Մատնահետքի պատկերակ"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"կառավարել դեմքի ճանաչման սարքը"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Հավելվածին թույլ է տալիս ավելացնել և հեռացնել դեմքի նմուշներ:"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"օգտագործել դեմքի ճանաչման սարքը"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Հավելվածին թույլ է տալիս օգտագործել նույնականացման համար նախատեսված սարքը"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Չհաջողվեց ճանաչել ձեր դեմքը: Նորից փորձեք:"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Դեմքը չափազանց վառ է երևում։ Թուլացրեք լույսը։"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Դեմքը չափազանց մուգ է երևում։ Ուժեղացրեք լույսը։"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Սարքը մի փոքր հեռացրեք դեմքից։"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Սարքը մի փոքր մոտեցրեք դեմքին։"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Սարքը մի փոքր վերև պահեք։"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Սարքը մի փոքր ներքև պահեք։"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Սարքը մի փոքր ձախ պահեք։"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Սարքը մի փոքր ձախ պահեք։"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Նայեք սարքի տեսախցիկին։"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Դեմք չի հայտնաբերվել։"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Պահեք սարքը դեմքի դիմաց։"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Դեմքի ճանաչման սարքն անհասանելի է։"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Ժամանակը սպառվել է: Նորից փորձեք:"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Դեմքը հնարավոր չէ պահել։"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Դեմքի ճանաչումը չեղարկվել է։"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Չափից շատ փորձեր եք կատարել: Փորձեք ավելի ուշ:"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Չափից շատ փորձեր եք կատարել: Դեմքի ճանաչման գործառույթն անջատվել է։"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Նորից փորձեք:"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Գրանցված դեմք չկա։"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Սարքը չունի դեմքի ճանաչման սկաներ"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Դեմք <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Դեմքի պատկերակ"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"կարդալ համաժամեցման կարգավորումները"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Թույլ է տալիս հավելվածին կարդալ համաժամեցման կարգավորումները հաշվի համար: Օրինակ` այն կարող է որոշել, արդյոք Մարդիկ հավելվածը համաժամեցված է հաշվի հետ:"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"համաժամեցումը փոխարկել միացվածի և անջատվածի"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi ցանցում ինտերնետ կապ չկա"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Հպեք՝ ընտրանքները տեսնելու համար"</string>
+    <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">"Սարքը չի կարող աշխատել միայն 5 ԳՀց հաճախականությամբ։ Այդ հաճախականությունը կօգտագործվի հնարավորության դեպքում։"</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>
@@ -1814,7 +1850,7 @@
     <string name="app_category_social" msgid="5842783057834965912">"Սոցցանցեր և հաղորդակցություն"</string>
     <string name="app_category_news" msgid="7496506240743986873">"Նորություններ և ամսագրեր"</string>
     <string name="app_category_maps" msgid="5878491404538024367">"Քարտեզներ և նավարկում"</string>
-    <string name="app_category_productivity" msgid="3742083261781538852">"Աշխատանք"</string>
+    <string name="app_category_productivity" msgid="3742083261781538852">"Արդյունավետություն"</string>
     <string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"Սարքի հիշողություն"</string>
     <string name="adb_debugging_notification_channel_tv" msgid="5537766997350092316">"USB վրիպազերծում"</string>
     <string name="time_picker_hour_label" msgid="2979075098868106450">"ժամ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 4336f80..05f6ba1 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mode pesawat"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Mode pesawat AKTIF"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Mode pesawat MATI"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Penghemat baterai"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Penghemat baterai NONAKTIF"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Penghemat baterai AKTIF"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Setelan"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Bantuan"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Bantuan Suara"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Lokasi"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"mengakses lokasi perangkat ini"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi perangkat ini?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikasi ini hanya akan memiliki akses ke lokasi saat Anda sedang menggunakan aplikasi."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Selalu izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi perangkat ini?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikasi ini akan selalu memiliki akses ke lokasi, bahkan saat Anda sedang tidak menggunakan aplikasi."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"mengakses kalender"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses kalender?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Aplikasi ini dapat menambahkan, menghapus, atau mengubah acara kalender di ponsel. Aplikasi ini dapat mengirim pesan yang kelihatannya berasal dari pemilik kalender, atau mengubah acara tanpa memberi tahu pemilik."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"akses perintah penyedia lokasi ekstra"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Memungkinkan aplikasi mengakses perintah penyedia lokasi ekstra. Tindakan ini memungkinkan aplikasi mengganggu pengoperasian GPS atau sumber lokasi lain."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"akses lokasi akurat (berbasis jaringan dan GPS)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Aplikasi ini dapat memperoleh lokasi Anda berdasarkan sumber jaringan seperti menara seluler dan jaringan Wi-Fi. Layanan lokasi ini harus diaktifkan dan tersedia di ponsel agar aplikasi dapat menggunakannya. Dapat meningkatkan penggunaan baterai."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"akses lokasi pasti hanya saat di latar depan"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Aplikasi ini bisa mendapatkan lokasi pasti Anda ketika aplikasi berada di latar depan. Fitur layanan lokasi ini harus diaktifkan dan tersedia di ponsel agar dapat digunakan oleh aplikasi. Fitur ini dapat meningkatkan konsumsi baterai."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"akses perkiraan lokasi (berbasis jaringan)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Aplikasi ini dapat memperoleh lokasi Anda berdasarkan sumber jaringan seperti menara seluler dan jaringan Wi-Fi. Layanan lokasi ini harus diaktifkan dan tersedia di tablet agar aplikasi dapat menggunakannya."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Aplikasi ini dapat memperoleh lokasi Anda berdasarkan sumber jaringan seperti menara seluler dan jaringan Wi-Fi. Layanan lokasi ini harus diaktifkan dan tersedia di TV agar aplikasi dapat menggunakannya."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Aplikasi ini dapat memperoleh lokasi Anda berdasarkan sumber jaringan seperti menara seluler dan jaringan Wi-Fi. Layanan lokasi ini harus diaktifkan dan tersedia di ponsel agar aplikasi dapat menggunakannya."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"akses lokasi pasti saat di latar belakang"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Aplikasi ini bisa mendapatkan lokasi pasti Anda kapan pun aplikasi ada di latar belakang. Fitur layanan lokasi ini harus diaktifkan dan tersedia di ponsel agar dapat digunakan oleh aplikasi. Fitur ini dapat meningkatkan konsumsi baterai."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ubah setelan audio Anda"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Memungkinkan aplikasi mengubah setelan audio global, misalnya volume dan pengeras suara mana yang digunakan untuk keluaran."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"rekam audio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon sidik jari"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"kelola hardware autentikasi wajah"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Mengizinkan apl memicu metode untuk menambah &amp; menghapus template wajah untuk digunakan."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"gunakan hardware autentikasi wajah"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Mengizinkan aplikasi untuk menggunakan hardware autentikasi wajah untuk autentikasi"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Tidak dapat memproses wajah. Harap coba lagi."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Wajah terlalu cerah. Coba dengan cahaya lebih redup."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Wajah terlalu gelap. Buka sumber cahaya."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Jauhkan sensor dari wajah."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Dekatkan sensor ke wajah."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Naikkan sensor."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Turunkan sensor."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Gerakkan sensor ke kanan."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Gerakkan sensor ke kiri."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Lihat sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Tidak ada wajah yang terdeteksi"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Pastikan wajah tetap diam di depan perangkat."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware pemrosesan wajah tidak tersedia."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Waktu tunggu wajah habis. Harap coba lagi."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Wajah tidak dapat disimpan."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Pemrosesan wajah dibatalkan."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Terlalu banyak percobaan. Coba lagi nanti."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Terlalu sering dicoba. Autentikasi wajah dinonaktifkan."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Coba lagi."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Tidak ada wajah yang didaftarkan."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Perangkat ini tidak memiliki sensor autentikasi wajah"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> wajah"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ikon wajah"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"baca setelan sinkron"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Memungkinkan aplikasi membaca setelan sinkronisasi untuk sebuah akun. Misalnya, izin ini dapat menentukan apakah aplikasi Orang disinkronkan dengan sebuah akun."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"nyalakan dan matikan sinkronisasi"</string>
@@ -584,14 +617,14 @@
     <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"Memantau banyaknya sandi salah yang diketikkan saat membuka kunci layar, dan mengunci tablet atau menghapus semua data pengguna ini jika terlalu banyak sandi salah diketikkan."</string>
     <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Memantau banyaknya sandi salah yang diketikkan saat membuka kunci layar, dan mengunci TV atau menghapus semua data pengguna ini jika terlalu banyak sandi salah diketikkan."</string>
     <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Memantau banyaknya sandi salah yang diketikkan saat membuka kunci layar, dan mengunci ponsel atau menghapus semua data pengguna ini jika terlalu banyak sandi salah diketikkan."</string>
-    <string name="policylab_resetPassword" msgid="4934707632423915395">"Ubah kunci layar"</string>
+    <string name="policylab_resetPassword" msgid="4934707632423915395">"Mengubah kunci layar"</string>
     <string name="policydesc_resetPassword" msgid="1278323891710619128">"Mengubah kunci layar."</string>
-    <string name="policylab_forceLock" msgid="2274085384704248431">"Kunci layar"</string>
-    <string name="policydesc_forceLock" msgid="1141797588403827138">"Kontrol cara dan kapan layar mengunci."</string>
-    <string name="policylab_wipeData" msgid="3910545446758639713">"Hapus semua data"</string>
+    <string name="policylab_forceLock" msgid="2274085384704248431">"Mengunci layar"</string>
+    <string name="policydesc_forceLock" msgid="1141797588403827138">"Mengontrol cara dan kapan layar mengunci."</string>
+    <string name="policylab_wipeData" msgid="3910545446758639713">"Menghapus semua data"</string>
     <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Hapus data tablet tanpa peringatan dengan menyetel ulang data pabrik."</string>
     <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Menghapus data TV tanpa peringatan dengan mengembalikan ke setelan pabrik."</string>
-    <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Hapus data ponsel tanpa peringatan dengan menyetel ulang data pabrik."</string>
+    <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Menghapus data ponsel tanpa peringatan dengan melakukan reset ke setelan pabrik."</string>
     <string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Menghapus data pengguna"</string>
     <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"Menghapus data pengguna ini di tablet ini tanpa peringatan."</string>
     <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Menghapus data pengguna ini di TV ini tanpa peringatan."</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi tidak memiliki akses internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap untuk melihat opsi"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Perubahan pada setelan hotspot Anda"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Pita hotspot Anda telah berubah."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Perangkat ini tidak mendukung preferensi Anda, yaitu hanya 5GHz. Sebagai gantinya, perangkat ini akan menggunakan pita frekuensi 5GHz jika tersedia."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Dialihkan ke <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Perangkat menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> jika <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tidak memiliki akses internet. Tarif mungkin berlaku."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Dialihkan dari <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ke <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 4913963..a915202 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Flugstilling"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"KVEIKT er á flugstillingu"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"SLÖKKT er á flugstillingu"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Rafhlöðusparnaður"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Slökkt er á rafhlöðusparnaði"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Kveikt er á rafhlöðusparnaði"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Stillingar"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Aðstoð"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Raddaðstoð"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Staðsetning"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"fá aðgang að staðsetningu þessa tækis"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Viltu veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að staðsetningu þessa tækis?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Forritið hefur aðeins aðgang að staðsetningunni á meðan þú notar forritið."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Viltu alltaf veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að staðsetningu þessa tækis?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Forritið hefur alltaf aðgang að staðsetningunni, jafnvel þegar þú ert ekki að nota forritið."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Dagatal"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"fá aðgang að dagatalinu þínu"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Viltu veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að dagatalinu þínu?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Þetta forrit getur bætt við, fjarlægt eða breytt dagatalsviðburðum í símanum. Þetta forrit getur sent skilaboð sem geta virst koma ffrá eigendum dagatala eða breytt viðburðum án þess að láta eigendurna vita."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"aðgangur að viðbótarskipunum staðsetningarveitu"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Leyfir forriti að fá aðgang að fleiri skipunum staðsetningarveitu. Þetta getur gert forritinu kleift að hafa áhrif á virkni GPS og annars staðsetningarbúnaðar."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"fá aðgang að nákvæmri staðsetningu (frá GPS og símkerfi)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Þetta forrit getur séð staðsetningu þína út frá netkerfum á borð við farsímasenda og Wi-Fi net. Það verður að vera kveikt á slíkri staðsetningarþjónustu og hún þarf að vera aðgengileg símanum til að forritið geti notað hana. Þetta getur aukið rafhlöðunotkun."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"aðgangur að nákvæmri staðsetningu aðeins í forgrunni"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Þetta forrit getur aðeins séð staðsetningu þína þegar það er í forgrunni. Það verður að vera kveikt á þessari staðsetningarþjónustu og hún þarf að vera aðgengileg í símanum til að forritið geti notað hana. Þetta getur aukið rafhlöðunotkun."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"fá aðgang að áætlaðri staðsetningu (frá símkerfi)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Þetta forrit getur séð staðsetningu þína út frá netkerfum á borð við farsímasenda og Wi-Fi net. Það verður að vera kveikt á slíkri staðsetningarþjónustu og hún þarf að vera aðgengileg spjaldtölvunni til að forritið geti notað hana."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Þetta forrit getur séð staðsetningu þína út frá netkerfum á borð við farsímasenda og Wi-Fi net. Það verður að vera kveikt á slíkri staðsetningarþjónustu og hún þarf að vera aðgengileg sjónvarpinu til að forritið geti notað hana."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Þetta forrit getur séð staðsetningu þína út frá netkerfum á borð við farsímasenda og Wi-Fi net. Það verður að vera kveikt á slíkri staðsetningarþjónustu og hún þarf að vera aðgengileg símanum til að forritið geti notað hana."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"aðgangur að nákvæmri staðsetningu í bakgrunni"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Þetta forrit getur séð nákvæma staðsetningu þína hvenær sem það er í bakgrunninum. Það verður að vera kveikt á þessari staðsetningarþjónustu og hún þarf að vera aðgengileg í símanum til að forritið geti notað hana. Þetta getur aukið rafhlöðunotkun."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"breyta hljóðstillingum"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Leyfir forriti að breyta altækum hljóðstillingum, s.s. hljóðstyrk og hvaða hátalari er notaður sem úttak."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"taka upp hljóð"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingrafaratákn"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"stjórna vélbúnaði andlitsgreiningar"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Leyfir forritinu að beita aðferðum til að bæta við og eyða andlitssniðmátum til notkunar."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"nota vélbúnað andlitsgreiningar"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Leyfir forritinu að nota andlitsgreiningarvélbúnað til auðkenningar"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Ekki tókst að vinna úr andlitinu. Reyndu aftur."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Andlitið er of bjart. Prófaðu í minni birtu."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Andlitið er of dökkt. Finndu betri lýsingu."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Færðu skynjarann lengra frá andlitinu."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Færðu skynjarann nær andlitinu."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Færðu skynjarann ofar."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Færðu skynjarann neðar."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Færðu skynjarann til hægri."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Færðu skynjarann til vinstri."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Horfðu á skynjarann."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Ekkert andlit fannst."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Haltu andlitinu kyrru fyrir framan tækið."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Andlitsvélbúnaður ekki til staðar."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Tímamörk runnu út fyrir andlit. Reyndu aftur."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Ekki tókst að geyma andlit."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Hætt við andlitsgreiningu."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Of margar tilraunir. Reyndu aftur síðar."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Of margar tilraunir. Slökkt á andlitsgreiningu."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Reyndu aftur."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Ekkert andlit skráð."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Þetta tæki er ekki með auðkenningarskynjara fyrir andlit"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Andlit <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Andlitstákn"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lesa samstillingar"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Leyfir forriti að lesa kosti samstillingar fyrir reikning. Þetta er til dæmis hægt að nota til að komast að því hvort forritið Fólk er samstillt við reikning."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"kveikja og slökkva á samstillingu"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi er ekki með tengingu við internetið"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Ýttu til að sjá valkosti"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Breytingar á stillingum heits reits"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Tíðnisvið heita reitsins hefur breyst."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Þetta tæki styður ekki val þitt fyrir aðeins 5 GHz. Í staðinn mun þetta tæki nota 5 GHz tíðnisvið þegar það er í boði."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Skipt yfir á <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Tækið notar <xliff:g id="NEW_NETWORK">%1$s</xliff:g> þegar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> er ekki með internetaðgang. Gjöld kunna að eiga við."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Skipt úr <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> yfir í <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index f8d6f13..de0b528 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -134,7 +134,7 @@
   </string-array>
     <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Non attiva"</string>
     <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Rete preferita: Wi-Fi"</string>
-    <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Modalità preferita: dati mobili"</string>
+    <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Rete preferita: dati mobili"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Solo Wi-Fi"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: inoltro non effettuato"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modalità aereo"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modalità aereo attiva"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modalità aereo non attiva"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Risparmio energetico"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Risparmio energetico disattivato"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Risparmio energetico attivo"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Impostazioni"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assistenza"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -272,31 +269,34 @@
     <string name="managed_profile_label" msgid="8947929265267690522">"Passa a profilo di lavoro"</string>
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Contatti"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"accedere ai contatti"</string>
-    <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai tuoi contatti?"</string>
+    <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai tuoi contatti?"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Geolocalizzazione"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"accedere alla posizione di questo dispositivo"</string>
-    <string name="permgrouprequest_location" msgid="3788275734953323491">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alla posizione di questo dispositivo?"</string>
+    <string name="permgrouprequest_location" msgid="3788275734953323491">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alla posizione di questo dispositivo?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"L\'app avrà accesso alla posizione soltanto quando la usi."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Vuoi consentire sempre a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alla posizione di questo dispositivo?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"L\'app avrà sempre accesso alla posizione, anche quando non la usi."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendario"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"accedere al calendario"</string>
-    <string name="permgrouprequest_calendar" msgid="289900767793189421">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere al tuo calendario?"</string>
+    <string name="permgrouprequest_calendar" msgid="289900767793189421">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere al tuo calendario?"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="4656988620100940350">"inviare e visualizzare SMS"</string>
-    <string name="permgrouprequest_sms" msgid="7168124215838204719">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di inviare e visualizzare SMS?"</string>
+    <string name="permgrouprequest_sms" msgid="7168124215838204719">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di inviare e visualizzare SMS?"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"Archiviazione"</string>
     <string name="permgroupdesc_storage" msgid="637758554581589203">"accedere a foto, contenuti multimediali e file sul dispositivo"</string>
     <string name="permgrouprequest_storage" msgid="7885942926944299560">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere a foto, contenuti multimediali e file memorizzati sul dispositivo?"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microfono"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"registrare audio"</string>
-    <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di registrare audio?"</string>
+    <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di registrare audio?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotocamera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"scattare foto e registrare video"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di scattare foto e registrare video?"</string>
     <string name="permgrouplab_calllog" msgid="8798646184930388160">"Registri chiamate"</string>
     <string name="permgroupdesc_calllog" msgid="3006237336748283775">"leggere e scrivere il registro chiamate del telefono"</string>
-    <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Vuoi consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai registri chiamate del tuo telefono?"</string>
+    <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai registri chiamate del tuo telefono?"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Telefono"</string>
     <string name="permgroupdesc_phone" msgid="6234224354060641055">"eseguire e gestire le telefonate"</string>
-    <string name="permgrouprequest_phone" msgid="9166979577750581037">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di effettuare e gestire telefonate?"</string>
+    <string name="permgrouprequest_phone" msgid="9166979577750581037">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di effettuare e gestire telefonate?"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Sensori per il corpo"</string>
     <string name="permgroupdesc_sensors" msgid="7147968539346634043">"accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string>
     <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai dati dei sensori relativi ai parametri vitali?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Questa app può aggiungere, rimuovere o modificare eventi di calendario sul telefono. Può inviare messaggi che possono sembrare inviati dai proprietari del calendario o modificare eventi senza notificare i proprietari."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accesso a comandi aggiuntivi provider di geolocalizz."</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Consente all\'app di accedere a ulteriori comandi del fornitore di posizione. Ciò potrebbe consentire all\'app di interferire con il funzionamento del GPS o di altre fonti di geolocalizzazione."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"accesso alla posizione esatta (basata su GPS e rete)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Questa app può accedere alla tua posizione tramite fonti di rete come antenne di telefonia mobile e reti Wi-Fi. Tali servizi di geolocalizzazione devono essere attivati e disponibili sul telefono per consentire all\'app di utilizzarli. Ciò può aumentare il consumo di batteria."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"Accesso alla posizione esatta solo in primo piano"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Questa app può recuperare la tua posizione esatta solo quando è in primo piano. Questi servizi di geolocalizzazione devono essere attivi e disponibili sul telefono affinché l\'app possa usarli. Potrebbe aumentare il consumo della batteria."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"accesso alla posizione approssimativa (basata sulla rete)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Questa app può accedere alla tua posizione tramite fonti di rete come antenne di telefonia mobile e reti Wi-Fi. Tali servizi di geolocalizzazione devono essere attivati e disponibili sul tablet per consentire all\'app di utilizzarli."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Questa app può accedere alla tua posizione tramite fonti di rete come antenne di telefonia mobile e reti Wi-Fi. Tali servizi di geolocalizzazione devono essere attivati e disponibili sulla TV per consentire all\'app di utilizzarli."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Questa app può accedere alla tua posizione tramite fonti di rete come antenne di telefonia mobile e reti Wi-Fi. Tali servizi di geolocalizzazione devono essere attivati e disponibili sul telefono per consentire all\'app di utilizzarli."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"Accesso alla posizione esatta in background"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Questa app può recuperare la tua posizione esatta quando è in background. Questi servizi di geolocalizzazione devono essere attivi e disponibili sul telefono affinché l\'app possa usarli. Potrebbe aumentare il consumo della batteria."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modifica impostazioni audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Consente all\'applicazione di modificare le impostazioni audio globali, come il volume e quale altoparlante viene utilizzato per l\'uscita."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"registrare audio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona dell\'impronta digitale"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"gestisci l\'hardware per l\'autenticazione dei volti"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Consente all\'app di richiamare i metodi per aggiungere e rimuovere i modelli di volti."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"utilizza l\'hardware per l\'autenticazione dei volti"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Consente all\'app di utilizzare hardware per l\'autenticazione dei volti"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Impossibile elaborare il volto. Riprova."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Volto troppo luminoso. Prova dove c\'è meno luce."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Volto troppo scuro. Non coprire la fonte di luce."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Allontana il sensore dal volto."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Avvicina il sensore al volto."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Sposta il sensore verso l\'alto."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Sposta il sensore verso il basso."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Sposta il sensore verso destra."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Sposta il sensore verso sinistra."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Guarda il sensore."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nessun volto rilevato."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Tieni il volto fermo davanti al dispositivo."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware per il volto non disponibile."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Timeout operazione associata al volto. Riprova."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Il volto non può essere memorizzato."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Operazione associata al volto annullata."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Troppi tentativi. Riprova più tardi."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Troppi tentativi. Autenticazione disattivata."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Riprova."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nessun volto registrato."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Nessun sensore di autenticazione del volto sul dispositivo."</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Volto <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Icona volto"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lettura impostazioni di sincronizz."</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Consente all\'applicazione di leggere le impostazioni di sincronizzazione per un account. Ad esempio, questa autorizzazione può determinare se l\'applicazione Persone è sincronizzata con un account."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"attivazione e disattivazione della sincronizzazione"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"La rete Wi-Fi non ha accesso a Internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tocca per le opzioni"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Modifiche alle tue impostazioni dell\'hotspot"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"La tua banda di hotspot è cambiata."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Questo dispositivo non supporta la tua preferenza esclusiva per 5 GHz. Utilizzerà invece la banda a 5 GHz solo quando è disponibile."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Passato a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Il dispositivo utilizza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ha accesso a Internet. Potrebbero essere applicati costi."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Passato da <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 0a95312..0c0e0ab 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -242,9 +242,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"מצב טיסה"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"מצב טיסה מופעל"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"מצב טיסה כבוי"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"חיסכון בסוללה"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"תכונת החיסכון בסוללה כבויה"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"תכונת החיסכון בסוללה פועלת"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"הגדרות"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"סיוע"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -282,6 +279,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"מיקום"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"גישה אל מיקום המכשיר הזה"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה למיקום המכשיר?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"לאפליקציה תהיה גישה אל נתוני המיקום רק במהלך השימוש באפליקציה."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"‏תמיד לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה למיקום המכשיר?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"לאפליקציה תמיד תהיה גישה לנתוני המיקום, גם כשהאפליקציה אינה בשימוש."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"יומן"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"גישה אל היומן"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה ליומן?"</string>
@@ -408,12 +408,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"אפליקציה זו יכולה להוסיף, להסיר ולשנות אירועי יומן בטלפון. האפליקציה יכולה לשנות אירועים בלי להודיע לבעליהם ולשלוח הודעות שעשויות להיראות כאילו נשלחו מבעלי יומנים."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"גישה לפקודות ספק מיקום נוספות"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"‏מאפשרת לאפליקציה לגשת לפקודות נוספות של ספק המיקום. הרשאה זו עשויה לאפשר לאפליקציה לשבש את פעולת ה-GPS או מקורות מיקום אחרים."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"‏גישה אל מיקום מדויק (מבוסס GPS ורשת)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"‏אפליקציה זו יכולה לזהות את המיקום שלך על סמך GPS או מקורות מיקום ברשת, כגון אנטנות סלולריות ורשתות Wi-Fi. שירותי מיקום אלה חייבים להיות מופעלים וזמינים בטלפון שלך כדי שהאפליקציה תוכל להשתמש בהם. ייתכן שהדבר יגביר את צריכת הסוללה."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"קבלת גישה למיקום מדויק בחזית בלבד"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"אפליקציה זו יכולה לזהות את המיקום המדויק שלך רק כאשר היא פועלת בחזית. כדי שהאפליקציה תוכל להשתמש בשירותי המיקום, עליהם להיות מופעלים וזמינים בטלפון. ייתכן שפעולה זו תגביר את צריכת הסוללה."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"גישה אל מיקום משוער (מבוסס רשת)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"‏אפליקציה זו יכולה לזהות את המיקום שלך על סמך מקורות מיקום ברשת, כגון אנטנות סלולריות ורשתות Wi-Fi. שירותי מיקום אלה חייבים להיות מופעלים וזמינים בטאבלט שלך כדי שהאפליקציה תוכל להשתמש בהם."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"‏אפליקציה זו יכולה לזהות את המיקום שלך על סמך מקורות מיקום ברשת, כגון אנטנות סלולריות ורשתות Wi-Fi. שירותי מיקום אלה חייבים להיות מופעלים וזמינים בטלוויזיה שלך כדי שהאפליקציה תוכל להשתמש בהם."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"‏אפליקציה זו יכולה לזהות את המיקום שלך על סמך מקורות מיקום ברשת, כגון אנטנות סלולריות ורשתות Wi-Fi. שירותי מיקום אלה חייבים להיות מופעלים וזמינים בטלפון שלך כדי שהאפליקציה תוכל להשתמש בהם."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"קבלת גישה למיקום מדויק ברקע"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"אפליקציה זו יכולה לזהות את המיקום המדויק שלך בכל זמן שהיא פועלת ברקע. כדי שהאפליקציה תוכל להשתמש בשירותי המיקום, עליהם להיות מופעלים וזמינים בטלפון. ייתכן שפעולה זו תגביר את צריכת הסוללה."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"שנה את הגדרות האודיו שלך"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"מאפשר לאפליקציה לשנות הגדרות אודיו גלובליות כמו עוצמת קול ובחירת הרמקול המשמש לפלט."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"הקלט אודיו"</string>
@@ -521,6 +523,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"סמל טביעת אצבע"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"ניהול של חומרה של זיהוי פנים לצורך אימות"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"מאפשרת לאפליקציה להפעיל שיטות להוספה ומחיקה של תבניות פנים שבהן ייעשה שימוש."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"שימוש בחומרה של זיהוי פנים לצורך אימות"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"מאפשרת לאפליקציה להשתמש בחומרה של זיהוי פנים לצורך אימות"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"לא ניתן היה לעבד את הפנים. יש לנסות שוב."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"הפנים בהירים מדי. יש לנסות באור עמום יותר."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"הפנים כהים מדי. יש להוסיף מקור אור."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"יש להרחיק את החיישן מהפנים."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"יש לקרב את החיישן לפנים."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"יש להזיז את החיישן גבוה יותר."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"יש להזיז את החיישן נמוך יותר."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"יש להזיז את החיישן ימינה."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"יש להזיז את החיישן שמאלה."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"יש להסתכל על החיישן."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"לא זוהו פנים."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"יש להחזיק את הפנים באופן יציב מול המכשיר."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"החומרה לזיהוי הפנים לא זמינה."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"חלף הזמן הקצוב לזיהוי הפנים. יש לנסות שוב."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"לא ניתן לשמור את הפנים."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"פעולת הפנים בוטלה."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"יותר מדי ניסיונות. יש לנסות שוב מאוחר יותר."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"יותר מדי ניסיונות. אימות הפנים הושבת."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"יש לנסות שוב."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"לא נרשמו פנים."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"במכשיר זה אין חיישן לאימות פנים"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"פנים <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"סמל הפנים"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"קרא את הגדרות הסינכרון"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"מאפשר לאפליקציה לקרוא את הגדרות הסנכרון של חשבון. לדוגמה, ניתן לגלות כך האם האפליקציה \'אנשים\' מסונכרן עם חשבון כלשהו."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"הפעלת וכיבוי סנכרון"</string>
@@ -1084,7 +1117,7 @@
     <string name="capital_on" msgid="1544682755514494298">"מופעל"</string>
     <string name="capital_off" msgid="6815870386972805832">"כבוי"</string>
     <string name="whichApplication" msgid="4533185947064773386">"השלמת פעולה באמצעות"</string>
-    <string name="whichApplicationNamed" msgid="8260158865936942783">"‏השלם את הפעולה באמצעות %1$s"</string>
+    <string name="whichApplicationNamed" msgid="8260158865936942783">"‏להשלמת הפעולה באמצעות %1$s"</string>
     <string name="whichApplicationLabel" msgid="7425855495383818784">"השלם פעולה"</string>
     <string name="whichViewApplication" msgid="3272778576700572102">"פתח באמצעות"</string>
     <string name="whichViewApplicationNamed" msgid="2286418824011249620">"‏פתח באמצעות %1$s"</string>
@@ -1219,6 +1252,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"‏לרשת ה-Wi-Fi אין גישה לאינטרנט"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"הקש לקבלת אפשרויות"</string>
+    <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">"‏מכשיר זה לא תומך בהעדפות שלך ל-5GHz בלבד. במקום זאת, מכשיר זה ישתמש בתדר 5GHz כשיהיה זמין."</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="NEW_NETWORK">%1$s</xliff:g> כשלרשת <xliff:g id="PREVIOUS_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>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 9fc4389..00d1b5d 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"機内モード"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"機内モードON"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"機内モードOFF"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"バッテリー セーバー"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"バッテリー セーバー OFF"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"バッテリー セーバー ON"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"設定"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"サポート"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"音声アシスト"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"位置情報"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"この端末の位置情報へのアクセス"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"この端末の位置情報へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"このアプリは、使用時のみ、位置情報にアクセスできるようになります。"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"この端末の位置情報へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に常に許可しますか?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"このアプリは、未使用時も含め、常に位置情報にアクセスできるようになります。"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"カレンダー"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"カレンダーへのアクセス"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"カレンダーへのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"このアプリは、お使いのスマートフォンでカレンダーの予定を追加、削除、変更できます。また、カレンダーの所有者から発信されたかのように表示されるメッセージを送信したり、所有者に通知することなく予定を変更したりできます。"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"位置情報提供者の追加コマンドアクセス"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"位置情報提供元の追加のコマンドにアクセスすることをアプリに許可します。許可すると、アプリがGPSなどの位置情報源の動作を妨害する恐れがあります。"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"正確な位置情報(GPSとネットワーク基地局)へのアクセス"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"このアプリは、ネットワーク位置情報源(携帯基地局や Wi-Fi ネットワークなど)に基づいて、ユーザーの位置情報を取得します。これらの位置情報サービスは ON の状態にして、スマートフォンでアプリがサービスを利用できるようにする必要があります。これにより電池の消費量が増える可能性があります。"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"フォアグラウンドでのみ正確な位置情報にアクセス"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"このアプリは、フォアグラウンド状態でのみユーザーの正確な位置情報を取得できます。この位置情報サービスは ON の状態にして、スマートフォンでアプリがサービスを利用できるようにする必要があります。これにより、電池の消費量が増える可能性があります。"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"おおよその位置情報(ネットワーク基地局)へのアクセス"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"このアプリは、ネットワーク位置情報源(携帯基地局や Wi-Fi ネットワークなど)に基づいて、ユーザーの位置情報を取得します。これらの位置情報サービスは ON の状態にして、タブレットでアプリがサービスを利用できるようにする必要があります。"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"このアプリは、ネットワーク位置情報源(携帯基地局や Wi-Fi ネットワークなど)に基づいて、ユーザーの位置情報を取得します。これらの位置情報サービスは ON の状態にして、テレビでアプリがサービスを利用できるようにする必要があります。"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"このアプリは、ネットワーク位置情報源(携帯基地局や Wi-Fi ネットワークなど)に基づいて、ユーザーの位置情報を取得します。これらの位置情報サービスは ON の状態にして、スマートフォンでアプリがサービスを利用できるようにする必要があります。"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"バックグラウンドで正確な位置情報にアクセス"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"このアプリは、バックグラウンド状態でも常にユーザーの正確な位置情報を取得できます。この位置情報サービスは ON の状態にして、スマートフォンでアプリがサービスを利用できるようにする必要があります。これにより、電池の消費量が増える可能性があります。"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"音声設定の変更"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"音声全般の設定(音量、出力に使用するスピーカーなど)の変更をアプリに許可します。"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"録音"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指紋アイコン"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"顔認証ハードウェアの管理"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"使用する顔テンプレートの追加や削除を行うメソッドの呼び出しをアプリに許可します。"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"顔認証ハードウェアの使用"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"顔認証ハードウェアを認証に使用することをアプリに許可します"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"顔を認識できませんでした。もう一度お試しください。"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"顔が明るすぎます。照明を暗くしてみてください。"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"顔が暗すぎます。光源のカバーを外してください。"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"センサーを顔から遠ざけてください。"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"センサーを顔に近づけてください。"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"センサーを上に動かしてください。"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"センサーを下に動かしてください。"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"センサーを右に動かしてください。"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"センサーを左に動かしてください。"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"センサーを見てください。"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"顔を検出できません。"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"端末の前で顔を静止してください。"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"顔認証ハードウェアが使用できません。"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"読み取りのタイムアウトです。もう一度お試しください。"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"顔の情報を保存できません。"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"顔の操作をキャンセルしました。"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"試行回数の上限です。後でもう一度お試しください。"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"試行回数の上限です。顔認証は無効になりました。"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"もう一度お試しください。"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"顔の情報が登録されていません。"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"この端末には顔認証センサーがありません"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"顔 <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"顔アイコン"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"同期設定の読み取り"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"アカウントの同期設定の読み取りをアプリに許可します。たとえば、連絡帳アプリがアカウントと同期しているかどうかをアプリから特定できるようになります。"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"同期のON/OFFの切り替え"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi‑Fi はインターネットに接続していません"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"タップしてその他のオプションを表示"</string>
+    <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">"この端末は 5 GHz のみという設定に対応していません。ただし、5 GHz 周波数帯が利用できるときには利用します。"</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>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 8112cc2..ab65348 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"თვითმფრინავის რეჟიმი"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"თვითმფრინავის რეჟიმი ჩართულია."</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"თვითმფრინავის რეჟიმი გამორთულია."</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ბატარეის დამზოგი"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ბატარეის დამზოგი გამორთულია"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ბატარეის დამზოგი ჩართულია"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"პარამეტრები"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"დახმარება"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ხმოვანი ასისტ."</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"მდებარეობა"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"მოწყობილობის მდებარეობაზე წვდომა"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"გსურთ, მიანიჭოთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ს&lt;/b&gt; ამ მოწყობილობის მდებარეობაზე წვდომის ნებართვა?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ამ აპს მდებარეობაზე წვდომა მხოლოდ მაშინ ექნება, როცა თქვენ მას გამოიყენებთ."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"გსურთ, მიანიჭოთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ს&lt;/b&gt; ამ მოწყობილობის მდებარეობაზე წვდომის ნებართვა?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ამ აპს ყოველთვის ექნება მდებარეობაზე წვდომა, მაშინაც კი, როცა თქვენ მას არ იყენებთ."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"კალენდარი"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"თქვენს კალენდარზე წვდომა"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"გსურთ, მიანიჭოთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ს&lt;/b&gt; თქვენს კალენდარზე წვდომის ნებართვა?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ამ აპს შეუძლია თქვენს ტელეფონში კალენდრის მოვლენების დამატება, ამოშლა ან შეცვლა. ამ აპს შეუძლია კალენდრების მფლობელების სახელით შეტყობინებების გაგზავნა ან მოვლენების მათი მფლობელების შეტყობინების გარეშე შეცვლა."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"მდებარეობის პროვაიდერის დამატებით ბრძანებებზე წვდომა"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"აპს შეეძლება წვდომა ჰქონდეს მდებარეობის სერვისის დამატებით ბრძანებებზე. შესაძლოა აპმა ეს გამოიყენოს GPS-ისა და მდებარეობის სხვა წყაროების მუშაობის პროცესში ჩარევისთვის."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"ზუსტ მდებარეობაზე წვდომა (GPS-ისა და ქსელის მეშვეობით)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"ამ აპს თქვენი მდებარეობის შესახებ ინფორმაციის მიღება GPS-ის ან ქსელის მდებარეობის ისეთი წყაროების მიხედვით შეუძლია, როგორიცაა მობილური კავშირგაბმულობის ანძები და Wi-Fi ქსელები. მდებარეობის აღნიშნული სერვისები თქვენს ტელეფონზე ჩართული და ხელმისაწვდომი უნდა იყოს, რათა აპმა მათი გამოყენება შეძლოს. ამან შეიძლება ბატარეის მოხმარება გაზარდოს."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ზუსტ მდებარეობაზე წვდომა მხოლოდ წინა პლანზე"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ამ აპს შეუძლია თქვენი ზუსტი მდებარეობის შესახებ ინფორმაციის მიღება მხოლოდ მაშინ, როცა გაშვებულია წინა პლანზე. თქვენს ტელეფონზე ჩართული და ხელმისაწვდომი უნდა იყოს მდებარეობის სერვისები, აპმა მათი გამოყენება რომ შეძლოს. ამან შეიძლება გაზარდოს ბატარეის მოხმარება."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"მიახლოებით მდებარეობაზე წვდომა (ქსელის მეშვეობით)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ამ აპს თქვენი მდებარეობის შესახებ ინფორმაციის მიღება ისეთი წყაროების მეშვეობით შეუძლია, როგორიცაა მობილური კავშირგაბმულობის ანძები და Wi-Fi ქსელები. მდებარეობის აღნიშნული სერვისები თქვენს ტაბლეტზე ჩართული და ხელმისაწვდომი უნდა იყოს, რათა აპმა მათი გამოყენება შეძლოს."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ამ აპს თქვენი მდებარეობის შესახებ ინფორმაციის მიღება ისეთი წყაროების მეშვეობით შეუძლია, როგორიცაა მობილური კავშირგაბმულობის ანძები და Wi-Fi ქსელები. მდებარეობის აღნიშნული სერვისები თქვენს ტელევიზორზე ჩართული და ხელმისაწვდომი უნდა იყოს, რათა აპმა მათი გამოყენება შეძლოს."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ამ აპს თქვენი მდებარეობის შესახებ ინფორმაციის მიღება ისეთი წყაროების მეშვეობით შეუძლია, როგორიცაა მობილური კავშირგაბმულობის ანძები და Wi-Fi ქსელები. მდებარეობის აღნიშნული სერვისები თქვენს ტელეფონზე ჩართული და ხელმისაწვდომი უნდა იყოს, რათა აპმა მათი გამოყენება შეძლოს."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ზუსტ მდებარეობაზე წვდომა ფონურ რეჟიმში"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ამ აპს შეუძლია თქვენი ზუსტი მდებარეობის შესახებ ინფორმაციის მიღება ყოველთვის, როცა გაშვებულია ფონურ რეჟიმში. თქვენს ტელეფონზე ჩართული და ხელმისაწვდომი უნდა იყოს მდებარეობის სერვისები, აპმა მათი გამოყენება რომ შეძლოს. ამან შეიძლება გაზარდოს ბატარეის მოხმარება."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"თქვენი აუდიო პარამეტრების შეცვლა"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"აპს შეეძლება აუდიოს გლობალური პარამეტრების შეცვლა. მაგ.: ხმის სიმაღლე და რომელი დინამიკი გამოიყენება სიგნალის გამოსტანად."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"აუდიოს ჩაწერა"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"თითის ანაბეჭდის ხატულა"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"სახის ამოცნობის აპარატურის მართვა"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"საშუალებას აძლევს აპს, დაამატოს და წაშალოს სახეების შაბლონები."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"სახის ამოცნობის აპარატურის გამოყენება"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"საშუალებას აძლევს აპს, ავტორიზაციისთვის გამოიყენოს სახის ამოცნობის აპარატურა"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"სახე ვერ მუშავდება. გთხოვთ, ცადოთ ხელახლა."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"სახე გადანათებულია. დაუკელით განათებას."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"სახე ჩაბნელებულია. მოუმატეთ განათებას."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"დააშორეთ მოწყობილობის სენსორი სახეს."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"მიუახლოვეთ მოწყობილობის სენსორი სახეს."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"ასწიეთ მოწყობილობის სენსორი ოდნავ ზემოთ."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"ჩასწიეთ მოწყობილობის სენსორი ოდნავ ქვემოთ."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"გასწიეთ მოწყობილობის სენსორი ოდნავ მარჯვნივ."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"გასწიეთ მოწყობილობის სენსორი ოდნავ მარცხნივ."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"შეხედეთ სენსორს."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"სახის ამოცნობა ვერ მოხერხდა."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"მოათავსეთ სახე მოწყობილობის წინ უმოძრაოდ."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"სახის ამოცნობის აპარატურა მიუწვდომელია."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"სახის ამოცნობის დრო ამოიწურა. ცადეთ ხელახლა."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"სახის შენახვა ვერ მოხერხდა."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"სახის ამოცნობა გაუქმდა."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"დაფიქსირდა ბევრი მცდელობა. ცადეთ მოგვიანებით."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"დაფიქსირდა ბევრი მცდელობა. სახის ამოცნობა გაითიშა."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"ცადეთ ხელახლა."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"სახე რეგისტრირებული არ არის."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"ამ მოწყობილობას არ აქვს სახის ამოცნობის სენსორი"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"სახე <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"სახის ხატულა"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"სინქრონიზაციის პარამეტრების წაკითხვა"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"აპს შეეძლება, წაიკითხოს ანგარიშის სინქრონიზაციის პარამეტრები. მაგალითად, მას შეეძლება განსაზღვროს, არის თუ არა People აპი სინქრონიზებული ანგარიშთან."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"სინქრონიზაციის ჩართვა და გამორთვა"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi‑Fi ქსელს ინტერნეტზე წვდომა არ აქვს"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"შეეხეთ ვარიანტების სანახავად"</string>
+    <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">"ამ მოწყობილობას არ შეუძლია მხოლოდ 5 გჰც სიხშირეზე მუშაობა. აღნიშნული სიხშირის გამოყენება მოხდება მაშინ, როცა ეს შესაძლებელია."</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>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index cd56d4c..3d4749f 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Ұшақ режимі"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Ұшақ режимі ҚОСУЛЫ"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Ұшақ режимі ӨШІРУЛІ"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Battery saver"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Battery saver ӨШІРУЛІ"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Battery saver ҚОСУЛЫ"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Параметрлер"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Көмек"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Дауыс көмекшісі"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Орналасу"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"бұл құрылғының орналасқан жерін көру"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына құрылғының орналасқан жері туралы мәліметтерді пайдалануға рұқсат берілсін бе?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Қолданбаны пайдалану кезінде ғана оған геодеректеріңізді көруге рұқсат етіледі."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" кез келген уақытта құрылғы геодеректерін пайдалансын ба?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Қолданба пайдаланылмаса да, оған геодеректеріңізді көруге рұқсат етіледі."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Күнтізбе"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"күнтізбеге кіру"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына күнтізбеге кіруге рұқсат берілсін бе?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Бұл қолданба телефондағы күнтізбе оқиғаларын енгізе, өшіре не өзгерте алады. Ол күнтізбе иелерінен келгендей болып көрінетін хабарларды жіберуі немесе иелеріне хабарламай, оқиғаларды өзгертуі мүмкін."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"қосымша аймақ жабдықтаушы пәрмендеріне қол жетімділік"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Қолданбаға орын жеткізушісінің қосымша пәрмендеріне қатынасуға рұқсат береді. Бұл қолданбаға GPS немесе басқа орын көздерінің жұмысына кедергі келтіруге рұқсат беруі мүмкін."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"дәл орынға қатынасу (GPS және желіге негізделген)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Бұл қолданба орналасқан жеріңізді GPS немесе желіні анықтау көздерінің (мысалы, ұялы байланыс мұнаралары мен Wi-Fi желілері) негізінде анықтайды. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін олар қосулы әрі телефонда қолжетімді болуы керек. Батарея көбірек тұтынылуы мүмкін."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"нақты орналасқан жер туралы ақпаратқа тек ашық экранда кіру"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Бұл қолданба нақты орналасқан жеріңіз туралы ақпаратты экранда ашық тұрғанда ғана анықтай алады. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін, олар қосулы әрі телефонда қолжетімді болуы керек. Батарея көбірек тұтынылуы мүмкін."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"шамамен алған орынға қатынасу (желі негізінде)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Бұл қолданба орналасқан жеріңізді ұялы байланыс мұнаралары мен Wi-Fi желілері сияқты желі көздерінің негізінде анықтайды. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін олар қосулы әрі планшетте қолжетімді болуы керек."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Бұл қолданба орналасқан жеріңізді ұялы байланыс мұнаралары мен Wi-Fi желілері сияқты желі көздерінің негізінде анықтайды. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін олар қосулы әрі теледидарда қолжетімді болуы керек."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Бұл қолданба орналасқан жеріңізді ұялы байланыс мұнаралары мен Wi-Fi желілері сияқты желі көздерінің негізінде анықтайды. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін олар қосулы әрі телефонда қолжетімді болуы керек."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"геодеректерге фондық режимде кіру"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Бұл қолданба нақты орналасқан жеріңіз туралы ақпаратты фондық режимде де анықтай алады. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін, олар қосулы әрі телефонда қолжетімді болуы керек. Батарея көбірек тұтынылуы мүмкін."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"аудио параметрлерін өзгерту"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Қолданбаға дыбыс қаттылығы және аудио шығыс үндеткішін таңдау сияқты жаһандық аудио параметрлерін өзгерту мүмкіндігін береді."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"аудио жазу"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Саусақ ізі белгішесі"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"бетті тану жабдығын басқару"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Қолданбаға пайдаланатын бет үлгілерін енгізу және жою әдістерін шақыруға мүмкіндік береді."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"бетті тану жабдығын пайдалану"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Қолданбаға бетті тану жабдығын қолдануға рұқсат етеді"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Бет өңделмеді. Әрекетті қайталаңыз."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Бет тым ашық түсті болып шықты. Жарықты азайтыңыз."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Бет тым күңгірт түсті. Жарық көзін бөгемеңіз."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Датчикті беттен алыстатыңыз."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Датчикті бетке жақындатыңыз."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Датчикті жоғары көтеріңіз."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Датчикті төмен қарай жылжытыңыз."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Датчикті оңға қарай жылжытыңыз."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Датчикті солға қарай жылжытыңыз."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Датчикке қараңыз."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Бет анықталмады."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Құрылғыға бетіңізді қозғалтпай қарап тұрыңыз."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Бетті тану жабдығы қолжетімді емес."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Күту уақыты бітті. Әрекетті қайталаңыз."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Бетті сақтау мүмкін емес."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Бетті танудан бас тартылды."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Тым көп әрекет жасалды. Кейінірек қайталаңыз."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Тым көп әрекет жасалды. Бетті тану функциясы өшірілді."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Қайталап көріңіз."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Ешқандай бет тіркелмеген."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Бұл құрылғыда бетті тану датчигі жоқ"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> беті"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Бет белгішесі"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"синх параметрлерін оқу"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Қолданбаға есептік жазба синхрондау параметрлерін оқу мүмкіндігін береді. Мысалы, бұл арқылы People қолданбасының есептік жазбамен сихрондалғаны анықталуы мүмкін."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"синх қосу және өшіру арасында ауысу"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi желісінде интернет байланысы жоқ"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Опциялар үшін түртіңіз"</string>
+    <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">"Бұл құрылғы тек 5 ГГц жиілікте жұмыс істей алмайды. Бұл жиілік мүмкін болған жағдайда ғана қолданылады."</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>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 559d567..22bd6c3 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ពេល​ជិះ​យន្តហោះ"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"បាន​បើក​របៀប​ពេល​ជិះ​យន្ត​ហោះ"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បាន​បិទ​របៀបពេលជិះ​យន្តហោះ​"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"កម្មវិធី​សន្សំ​ថ្ម"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"កម្មវិធី​សន្សំ​ថ្ម​បាន​បិទ"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"កម្មវិធី​សន្សំថ្មបាន​បើក"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"ការ​កំណត់"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"ជំនួយ"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ជំនួយសម្លេង"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"ទីតាំង"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"ចូលដំណើរការទីតាំងរបស់ឧបករណ៍នេះ"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើ​ទីតាំងរបស់ឧបករណ៍នេះ?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"កម្មវិធីនេះ​នឹងមាន​សិទ្ធិ​ចូលប្រើ​ទីតាំង នៅពេល​អ្នកប្រើ​កម្មវិធីនេះ​តែ​ប៉ុណ្ណោះ។"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"អនុញ្ញាត​ឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើ​ទីតាំង​របស់ឧបករណ៍​នេះ​ជានិច្ច​មែនទេ?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"កម្មវិធីនេះ​នឹងមាន​សិទ្ធិ​ចូលប្រើ​ទីតាំង​ជានិច្ច ទោះបីជា​នៅពេល​អ្នក​មិនប្រើ​កម្មវិធីនេះ​ក៏​ដោយ។"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"ប្រតិទិន"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"ចូលប្រើប្រិតិទិនរបស់អ្នក"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើ​ប្រតិទិនរបស់អ្នក?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"កម្មវិធី​នេះ​អាច​បញ្ចូល​ លុប​ ឬ​ប្តូរ​ព្រឹត្តិការណ៍​ប្រតិទិន​នៅលើ​ទូរសព្ទ​របស់​អ្នក​។ កម្មវិធី​នេះ​អាច​ផ្ញើ​សារ​ ដែល​អាច​បង្ហាញ​ថា​សារ​នោះ​ចេញ​មក​ពី​ម្ចាស់​ប្រតិទិន​ ឬ​ផ្លាស់ប្តូរ​ព្រឹត្តិការណ៍​ដោយ​គ្មាន​ការ​ជូន​ដំណឹង​ដល់​ម្ចាស់​របស់​ពួកវា​ទេ​។"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ចូល​ដំណើរការ​ពាក្យ​បញ្ជា​ក្រុមហ៊ុន​ផ្ដល់​ទីតាំង"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ឲ្យ​កម្មវិធី​ចូល​ដំណើរការ​ពាក្យ​បញ្ជា​កម្មវិធី​ផ្ដល់​​ទីតាំង​បន្ថែម។ វា​អាច​អនុញ្ញាត​ឲ្យ​កម្មវិធី​ទាក់ទង​ជា​មួយ​ប្រតិបត្តិការ​ជីភីអេស ឬ​ប្រភព​ទីតាំង​ផ្សេង។"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"ចូលដំណើរការទីតាំងច្បាស់លាស់ (ផ្អែកលើបណ្តាញ និង GPS)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"កម្មវិធី​នេះ​អាច​ទទួល​បាន​ទីតាំង​របស់​អ្នក​ ដោយ​ផ្អែក​លើ​ GPS​ ឬ​ប្រភព​ទីតាំង​បណ្តាញ​ ដូចជា​៖​ អង់តែន​ និង​បណ្តាញ​ Wi-Fi​ ។​ សេវាកម្ម​ទីតាំង​ទាំងនេះ​ត្រូវតែ​បើក​ និង​ត្រូវតែ​មាន​នៅ​លើ​ទូរសព្ទ​របស់​អ្នក ដើម្បី​ឲ្យ​កម្មវិធី​នេះ​អាច​ប្រើ​វា​បាន​។ វា​អាច​ប្រើ​ថាមពល​ច្រើន​ជាងមុន​។"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ចូល​ប្រើ​ទីតាំង​ជាក់លាក់​តែ​នៅផ្ទៃ​ខាងមុខ​ប៉ុណ្ណោះ"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"កម្មវិធីនេះ​អាចទទួល​បានទីតាំង​ពិតប្រាកដ​របស់អ្នក​តែនៅពេល​វាស្ថិតនៅ​ផ្ទៃខាងមុខប៉ុណ្ណោះ។ សេវាកម្ម​ទីតាំង​ទាំងនេះ​ត្រូវតែ​បើក និងមាន​នៅលើ​ទូរសព្ទ​របស់អ្នក ដើម្បីឱ្យ​កម្មវិធី​នេះអាចប្រើ​ពួកវាបាន។ វាអាចប្រើ​ថាមពល​ច្រើន​ជាងមុន។"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ចូលដំណើរការទីតាំងប្រហាក់ប្រហែល (ផ្អែកលើបណ្តាញ)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"កម្មវិធី​នេះ​អាច​ទទួល​បាន​ទីតាំង​របស់​អ្នក​ ដោយ​ផ្អែក​លើ​ប្រភព​បណ្តាញ​ ដូចជា​៖​ អង់តែន​ និង​បណ្តាញ​ Wi-Fi​ ។​ សេវាកម្ម​ទីតាំង​ទាំងនេះ​ត្រូវតែ​បើក​ និង​ត្រូវតែ​មាន​នៅ​លើ​ថេប្លេត​របស់​អ្នក ដើម្បី​ឲ្យ​កម្មវិធី​នេះ​អាច​ប្រើ​វា​បាន​។"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"កម្មវិធី​នេះ​អាច​ទទួល​បាន​ទីតាំង​របស់​អ្នក​ ដោយ​ផ្អែក​លើ​ប្រភព​បណ្តាញ​ ដូចជា​៖​ អង់តែន​ និង​បណ្តាញ​ Wi-Fi​ ។​ សេវាកម្ម​ទីតាំង​ទាំងនេះ​ត្រូវតែ​បើក​ និង​ត្រូវតែ​មាន​នៅ​លើ​ទូរទស្សន៍​របស់​អ្នក ដើម្បី​ឲ្យ​កម្មវិធី​នេះ​អាច​ប្រើ​វា​បាន​។"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"កម្មវិធី​នេះ​អាច​ទទួល​បាន​ទីតាំង​របស់​អ្នក​ ដោយ​ផ្អែក​លើ​ប្រភព​បណ្តាញ​ ដូចជា​៖​ អង់តែន​ និង​បណ្តាញ​ Wi-Fi​ ។​ សេវាកម្ម​ទីតាំង​ទាំងនេះ​ត្រូវតែ​បើក​ និង​ត្រូវតែ​មាន​នៅ​លើ​ទូរសព្ទ​របស់​អ្នក ដើម្បី​ឲ្យ​កម្មវិធី​នេះ​អាច​ប្រើ​វា​បាន​។"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ចូលប្រើ​ទីតាំង​ជាក់លាក់​នៅផ្ទៃ​ខាងក្រោយ"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"កម្មវិធីនេះ​អាចទទួល​បានទីតាំង​ពិតប្រាកដ​របស់អ្នក​គ្រប់ពេល​ដែលវា​ស្ថិតនៅ​ផ្ទៃខាងក្រោយ។ សេវាកម្ម​ទីតាំង​ទាំងនេះ​ត្រូវតែ​បើក និងមាន​នៅលើ​ទូរសព្ទ​របស់អ្នក ដើម្បីឱ្យ​កម្មវិធី​នេះអាចប្រើ​ពួកវាបាន។ វាអាចប្រើ​ថាមពល​ច្រើន​ជាងមុន។"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ប្ដូរ​ការ​កំណត់​អូឌីយូ​របស់​អ្នក"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ឲ្យ​កម្មវិធី​កែ​ការ​កំណត់​សំឡេង​សកល ដូច​ជា​កម្រិត​សំឡេង និង​អូប៉ាល័រ​ដែល​បាន​ប្រើ​សម្រាប់​លទ្ធផល។"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ថត​សំឡេង"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"រូបតំណាងស្នាមម្រាមដៃ"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"គ្រប់គ្រង​ផ្នែករឹង​ផ្ទៀងផ្ទាត់​ផ្ទៃ​មុខ"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"អនុញ្ញាតឱ្យកម្មវិធីប្រើវិធីសាស្ត្រដើម្បី​បញ្ចូល និងលុបទម្រង់​គំរូ​ផ្ទៃមុខសម្រាប់ប្រើប្រាស់។"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ប្រើ​ផ្នែករឹង​ផ្ទៀងផ្ទាត់​ផ្ទៃ​មុខ"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"អនុញ្ញាត​ឱ្យ​កម្មវិធី​ប្រើ​ផ្នែករឹង​ផ្ទៀងផ្ទាត់​ផ្ទៃមុខ​សម្រាប់​ការផ្ទៀងផ្ទាត់"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"មិនអាចដំណើរការ​ផ្ទៃមុខបានទេ។ សូមព្យាយាមម្តងទៀត។"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"ផ្ទៃ​មុខ​ចាំង​ពេក។ សូម​សាកល្បង​នៅកន្លែងដែលមាន​ពន្លឺ​ទាប​ជាងនេះ។"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"ផ្ទៃ​មុខ​ងងឹត​ពេក។ សូមរកកន្លែង​ដែលមាន​ប្រភពពន្លឺ។"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"សូម​ផ្លាស់ទីឧបករណ៍​ចាប់សញ្ញា​ឱ្យ​ឆ្ងាយ​ពី​មុខ​។"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"សូម​ដាក់​ឧបករណ៍​ចាប់សញ្ញា​ឱ្យនៅជិតមុខ​ជាងនេះ។"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"សូម​ផ្លាស់ទី​ឧបករណ៍​ចាប់សញ្ញា​ឱ្យខ្ពស់​ជាងនេះ។"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"សូម​ផ្លាស់ទី​ឧបករណ៍ចាប់សញ្ញា​ឱ្យ​ទាបជាងនេះ។"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"សូម​ផ្លាស់ទី​ឧបករណ៍ចាប់សញ្ញា​ទៅ​ស្ដាំ។"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"សូម​ផ្លាស់ទី​ឧបករណ៍ចាប់សញ្ញា​ទៅឆ្វេង។"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"សូម​មើល​ទៅ​ឧបករណ៍​ចាប់សញ្ញា។"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"រកមិន​ឃើញ​មុខទេ។"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"រក្សាមុខ​របស់អ្នក​ឱ្យ​នឹង​នៅមុខ​ឧបករណ៍​។"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"មិន​អាច​ប្រើ​ផ្នែករឹង​ចាប់ផ្ទៃ​មុខ​បានទេ។"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"ការសម្គាល់​ផ្ទៃមុខ​បាន​អស់ម៉ោង។ សូមព្យាយាមម្ដងទៀត។"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"មិន​អាច​រក្សាទុក​ផ្ទៃ​មុខ​បានទេ។"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"បាន​បោះបង់​ប្រតិបត្តិការចាប់​ផ្ទៃមុខ។"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"ព្យាយាមចូលច្រើនពេកហើយ។ សូមព្យាយាមម្តងទៀតពេលក្រោយ។"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ព្យាយាមចូលច្រើនពេកហើយ។ បាន​បិទការផ្ទៀងផ្ទាត់​ផ្ទៃ​មុខ។"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"សូមព្យាយាម​ម្ដងទៀត។"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"មិន​អាច​ថត​បញ្ចូលផ្ទៃ​មុខ​បានទេ។"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"ឧបករណ៍​នេះ​មិន​មាន​ឧបករណ៍​ផ្ទៀងផ្ទាត់​ផ្ទៃមុខ​នោះទេ"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"ផ្ទៃមុខទី <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"រូប​ផ្ទៃមុខ"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"អាន​ការ​កំណត់​ធ្វើ​សម​កាល​កម្ម"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ឲ្យ​កម្មវិធី​អាន​ការ​កំណត់​ធ្វើ​សម​កាល​កម្ម​សម្រាប់​គណនី។ ឧទាហរណ៍ វា​អាច​កំណត់​ថា​តើ​​​កម្មវិធី​ត្រូវ​បាន​បើក​ជា​មួយ​គណនី​ដែរ​ឬទេ។"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"បិទ/បើក​ការ​ធ្វើ​សម​កាល​កម្ម"</string>
@@ -1177,6 +1210,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi មិនមាន​ការតភ្ជាប់​អ៊ីនធឺណិតទេ"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ប៉ះសម្រាប់ជម្រើស"</string>
+    <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">"ឧបករណ៍​នេះ​មិន​អាច​ប្រើចំណូល​ចិត្ត​របស់អ្នកសម្រាប់តែ 5GHz ទេ។ ផ្ទុយ​មកវិញ ឧបករណ៍​នេះ​នឹង​ប្រើរលកសញ្ញា​ 5GHz នៅពេល​ដែលអាច​ប្រើបាន។"</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="NEW_NETWORK">%1$s</xliff:g> នៅ​ពេល​ដែល <xliff:g id="PREVIOUS_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>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 845cca2..58f8d56 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ಎರ್‌ಪ್ಲೇನ್ ಮೋಡ್ ಆನ್ ಆಗಿದೆ"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ಎರ್‌ಪ್ಲೇನ್ ಮೋಡ್ ಆಫ್ ಆಗಿದೆ"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ಬ್ಯಾಟರಿ ಸೇವರ್‌‌"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಆಫ್ ಆಗಿದೆ"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ಬ್ಯಾಟರಿ ಸೇವರ್‌ ಆನ್ ಆಗಿದೆ"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"ಸಹಾಯ ಮಾಡು"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ಧ್ವನಿ ಸಹಾಯಕ"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"ಸ್ಥಳ"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"ಈ ಸಾಧನದ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"ಈ ಸಾಧನದ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ನೀವು ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಬಳಸುವಾಗ ಈ ಅಪ್ಲಿಕೇಶನ್‌ ಸ್ಥಳಕ್ಕೆ ಮಾತ್ರ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರುತ್ತದೆ."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ಈ ಸಾಧನದ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಲು ಯಾವಾಗಲೂ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ನೀವು ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಬಳಸದಿರುವಾಗಲೂ ಅಪ್ಲಿಕೇಶನ್ ಯಾವಾಗಲೂ ಸ್ಥಳಕ್ಕೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರುತ್ತದೆ."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"ಕ್ಯಾಲೆಂಡರ್"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"ನಿಮ್ಮ ಕ್ಯಾಲೆಂಡರ್ ಪ್ರವೇಶಿಸಲು"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"ನಿಮ್ಮ ಕ್ಯಾಲೆಂಡರ್ ಪ್ರವೇಶಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿ ಕ್ಯಾಲೆಂಡರ್ ಈವೆಂಟ್‌ಗಳನ್ನು ಸೇರಿಸಬಹುದು, ತೆಗೆದುಹಾಕಬಹುದು ಅಥವಾ ಬದಲಾಯಿಸಬಹುದು. ಈ ಅಪ್ಲಿಕೇಶನ್ ಕ್ಯಾಲೆಂಡರ್‌ನ ಮಾಲೀಕರಿಂದ ಬಂದಿರಬಹುದಾದ ಕಾಣಿಸಿಕೊಳ್ಳುವ ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಬಹುದು ಅಥವಾ ಅವರ ಮಾಲೀಕರಿಗೆ ತಿಳಿಸದಂತೆ ಘಟನೆಗಳು ಬದಲಾಯಿಸುವುದು."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ಹೆಚ್ಚುವರಿ ಸ್ಥಳ ಪೂರೈಕೆದಾರರ ಆದೇಶಗಳನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ಹೆಚ್ಚಿನ ಸ್ಥಳ ಪೂರೈಕೆದಾರ ಆದೇಶಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ. ಇದು GPS ಅಥವಾ ಇತರ ಸ್ಥಳ ಮೂಲಗಳ ಕಾರ್ಯಾಚರಣೆಯಲ್ಲಿ ಮಧ್ಯ ಪ್ರವೇಶಿಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅನುಮತಿಸಬಹುದು."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"ನಿಖರ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ (GPS ಮತ್ತು ನೆಟ್‍ವರ್ಕ್-ಆಧಾರಿತ)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಸೆಲ್ ಟವರ್‌ಗಳು ಮತ್ತು ವೈ-ಫೈ ನೆಟ್‍‍ವರ್ಕ್‌ನಂತಹ GPS ಅಥವಾ ನೆಟ್‍‍ವರ್ಕ್ ಮೂಲಗಳ ಆಧಾರದ ಮೇಲೆ ಪಡೆಯಬಹುದು. ಈ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಆನ್ ಮಾಡಿರಬೇಕು ಮತ್ತು ಅವುಗಳನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಸಾಧ್ಯವಾಗುವಂತೆ ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್‌ನಲ್ಲಿ ಅವುಗಳು ಲಭ್ಯವಿರಬೇಕು. ಇದು ಬ್ಯಾಟರಿ ಬಳಕೆಯನ್ನು ಹೆಚ್ಚಿಸಬಹುದು."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ಮುನ್ನೆಲೆಯಲ್ಲಿ ಮಾತ್ರ ನಿಖರವಾದ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ಈ ಅಪ್ಲಿಕೇಶನ್‌ ಮುಂಭಾಗದಲ್ಲಿರುವಾಗ ಮಾತ್ರ ನಿಮ್ಮ ನಿಖರ ಸ್ಥಳವನ್ನು ಪಡೆಯಬಹುದು. ಈ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಆನ್ ಮಾಡಿರಬೇಕು ಮತ್ತು ಅವುಗಳನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಸಾಧ್ಯವಾಗುವಂತೆ ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿ ಅವುಗಳು ಲಭ್ಯವಿರಬೇಕು."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ಅಂದಾಜು ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ (ನೆಟ್‌ವರ್ಕ್-ಆಧಾರಿತ)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಸೆಲ್ ಟವರ್‌ಗಳು ಮತ್ತು ವೈ-ಫೈ ನೆಟ್‍‍ವರ್ಕ್‌ನಂತಹ ನೆಟ್‍‍ವರ್ಕ್ ಮೂಲಗಳ ಆಧಾರದ ಮೇಲೆ ಪಡೆಯಬಹುದು. ಈ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಆನ್ ಮಾಡಿರಬೇಕು ಮತ್ತು ಅವುಗಳನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಸಾಧ್ಯವಾಗುವಂತೆ ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್‌ನಲ್ಲಿ ಅವುಗಳು ಲಭ್ಯವಿರಬೇಕು."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಸೆಲ್ ಟವರ್‌ಗಳು ಮತ್ತು ವೈ-ಫೈ ನೆಟ್‍‍ವರ್ಕ್‌ನಂತಹ ನೆಟ್‍‍ವರ್ಕ್ ಮೂಲಗಳ ಆಧಾರದ ಮೇಲೆ ಪಡೆಯಬಹುದು. ಈ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಆನ್ ಮಾಡಿರಬೇಕು ಮತ್ತು ಅವುಗಳನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಸಾಧ್ಯವಾಗುವಂತೆ ನಿಮ್ಮ ಟಿವಿಯಲ್ಲಿ ಅವುಗಳು ಲಭ್ಯವಿರಬೇಕು."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಸೆಲ್ ಟವರ್‌ಗಳು ಮತ್ತು ವೈ-ಫೈ ನೆಟ್‍‍ವರ್ಕ್‌ನಂತಹ ನೆಟ್‍‍ವರ್ಕ್ ಮೂಲಗಳ ಆಧಾರದ ಮೇಲೆ ಪಡೆಯಬಹುದು. ಈ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಆನ್ ಮಾಡಿರಬೇಕು ಮತ್ತು ಅವುಗಳನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಸಾಧ್ಯವಾಗುವಂತೆ ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿ ಅವುಗಳು ಲಭ್ಯವಿರಬೇಕು."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ಹಿನ್ನೆಲೆಯಲ್ಲಿ ನಿಖರ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಹಿನ್ನೆಲೆಯಲ್ಲಿದೆಯಾದರೂ ನಿಮ್ಮ ಸರಿಯಾದ ಸ್ಥಳವನ್ನು ಪಡೆಯಬಹುದು. ಈ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಆನ್ ಮಾಡಿರಬೇಕು ಮತ್ತು ಅವುಗಳನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಸಾಧ್ಯವಾಗುವಂತೆ ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿ ಅವುಗಳು ಲಭ್ಯವಿರಬೇಕು."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ನಿಮ್ಮ ಆಡಿಯೊ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಿ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ವಾಲ್ಯೂಮ್ ರೀತಿಯ ಮತ್ತು ಔಟ್‍‍ಪುಟ್‍‍ಗಾಗಿ ಯಾವ ಸ್ಪೀಕರ್ ಬಳಸಬೇಕು ಎಂಬ ರೀತಿಯ ಜಾಗತಿಕ ಆಡಿಯೊ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ಆಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ಬೆರಳಚ್ಚು ಐಕಾನ್"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"ಮುಖ ದೃಢೀಕರಣ ಹಾರ್ಡ್‌ವೇರ್‌ ಅನ್ನು ನಿರ್ವಹಿಸಿ"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"ಬಳಕೆಗೆ ಮುಖದ ಟೆಂಪ್ಲೇಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಮತ್ತು ಅಳಿಸಲು ವಿಧಾನಗಳನ್ನು ಮನವಿ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ಮುಖ ದೃಢೀಕರಣ ಹಾರ್ಡ್‌ವೇರ್‌ ಅನ್ನು ಬಳಸಿ"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ಧೃಡೀಕರಣಕ್ಕಾಗಿ ಮುಖದ ಹಾರ್ಡ್‌ವೇರ್ ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"ಮುಖವನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"ಮುಖವು ತುಂಬಾ ಪ್ರಕಾಶಮಾನವಾಗಿದೆ. ಕಡಿಮೆ ಲೈಟ್‌ನಲ್ಲಿ ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"ಮುಖ ತುಂಬಾ ಕಪ್ಪಾಗಿದೆ. ನೇರವಾಗಿ ಬೆಳಕು ಬಿಳುವಂತೆ ಮಾಡಿ."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"ಮುಖದಿಂದ ಸೆನ್ಸರ್ ಅನ್ನು ದೂರ ಸರಿಸಿ."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"ಸೆನ್ಸರ್ ಅನ್ನು ಮುಖದ ಹತ್ತಿರಕ್ಕೆ ತನ್ನಿ."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"ಸೆನ್ಸರ್ ಅನ್ನು ಮೇಲಕ್ಕೆ ಸರಿಸಿ."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"ಸೆನ್ಸರ್ ಅನ್ನು ಕೆಳಕ್ಕೆ ಸರಿಸಿ."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"ಸೆನ್ಸರ್ ಅನ್ನು ಬಲಕ್ಕೆ ಸರಿಸಿ."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"ಸೆನ್ಸರ್ ಅನ್ನು ಎಡಕ್ಕೆ ಸರಿಸಿ."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"ಸೆನ್ಸರ್ ಅನ್ನು ನೋಡಿ."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"ಯಾವುದೇ ಮುಖ ಪತ್ತೆಯಾಗಿಲ್ಲ."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"ಸಾಧನದ ಮುಂದೆ ಮುಖವನ್ನು ಸ್ಥಿರವಾಗಿ ಇರಿಸಿ."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"ಮುಖದ ಹಾರ್ಡ್‌ವೇರ್‌ ಲಭ್ಯವಿಲ್ಲ."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"ಮುಖ ಸಮಯದ ಅವಧಿಯನ್ನು ತಲುಪಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"ಮುಖವನ್ನು ಸಂಗ್ರಹಿಸಲಾಗುವುದಿಲ್ಲ."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"ಮುಖದ ಕಾರ್ಯಚರಣೆಯನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ಹಲವು ಪ್ರಯತ್ನ. ಮುಖದ ದೃಢೀಕರಣ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"ಯಾವುದೇ ಮುಖವನ್ನು ನೋಂದಣಿ ಮಾಡಿಲ್ಲ."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"ಈ ಸಾಧನವು ಮುಖ ದೃಢೀಕರಣ ಸೆನ್ಸರ್‌ ಅನ್ನು ಹೊಂದಿಲ್ಲ"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"ಮುಖದ <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"ಮುಖದ ಐಕಾನ್‌"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ಸಿಂಕ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ರೀಡ್‌ ಮಾಡು"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ಒಂದು ಖಾತೆಯ ಸಿಂಕ್ ಸೆಟ್ಟಿಂಗ್‍‍ಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ. ಉದಾಹರಣೆಗೆ, ಖಾತೆಯೊಂದಿಗೆ ಜನರ ಅಪ್ಲಿಕೇಶನ್ ಸಿಂಕ್ ಮಾಡಲಾಗಿದೆಯೇ ಎಂಬುದನ್ನು ಇದು ನಿರ್ಧರಿಸಬಹುದು."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ಸಿಂಕ್ ಆನ್ ಮತ್ತು ಸಿಂಕ್ ಆಫ್ ಟಾಗಲ್ ಮಾಡಿ"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"ವೈ ಫೈ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+    <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">"ಈ ಸಾಧನವು 5GHz ಗೆ ಮಾತ್ರ ನಿಮ್ಮ ಆದ್ಯತೆಯನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ. ಬದಲಿಗೆ, ಈ ಸಾಧನವು 5GHz ಬ್ಯಾಂಡ್ ಅನ್ನು ಲಭ್ಯವಿರುವಾಗ ಬಳಸುತ್ತದೆ."</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>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 4aa6698..076b65f 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"비행기 모드"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"비행기 모드 사용중"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"비행기 모드 사용중이 아님"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"배터리 세이버"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"배터리 세이버 사용 안함"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"배터리 세이버 사용 중"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"설정"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"지원"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"음성 지원"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"위치"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"이 기기의 위치정보에 액세스"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 내 기기 위치에 액세스하도록 허용하시겠습니까?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"앱을 사용할 때만 앱에서 위치에 액세스합니다."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 내 기기 위치에 액세스하도록 허용하시겠습니까?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"앱을 사용하지 않을 때에도 앱에서 항상 위치에 액세스합니다."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"캘린더"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"캘린더에 액세스"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 내 캘린더에 액세스하도록 허용하시겠습니까?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"앱이 일정을 추가, 삭제, 변경할 수 있도록 허용합니다. 앱이 메시지를 전송하거나 사용자에게 별도 표시 없이 일정을 수정할 수도 있습니다."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"추가 위치 제공업체 명령에 접근"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"앱이 추가 위치 정보 제공 기능의 명령에 접근하도록 허용합니다. 이 경우 앱이 GPS 또는 기타 위치 소스의 작동을 방해할 수 있습니다."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"정확한 위치(GPS 및 네트워크 기반)에 액세스"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"이 앱은 GPS나 기지국 및 Wi-Fi 네트워크와 같은 네트워크 소스를 통해 내 위치를 알 수 있습니다. 앱에서 위치 서비스를 사용하려면 휴대전화에서 위치 서비스가 사용 설정되어 있으며 사용 가능해야 합니다. 이로 인해 배터리 사용량이 늘어날 수 있습니다."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"포그라운드에서만 정확한 위치에 액세스"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"이 앱은 포그라운드에 있을 때만 나의 정확한 위치를 알 수 있습니다. 앱에서 위치 서비스를 사용하려면 휴대전화에서 위치 서비스가 사용 설정되어 있으며 사용할 수 있어야 합니다. 배터리 사용량이 늘어날 수 있습니다."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"대략적인 위치(네트워크 기반)에 액세스"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"이 앱은 기지국 및 Wi-Fi 네트워크와 같은 네트워크 소스를 통해 내 위치를 알 수 있습니다. 앱에서 위치 서비스를 사용하려면 태블릿에서 위치 서비스가 사용 설정되어 있으며 사용 가능해야 합니다."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"이 앱은 기지국 및 Wi-Fi 네트워크와 같은 네트워크 소스를 통해 내 위치를 알 수 있습니다. 앱에서 위치 서비스를 사용하려면 TV에서 위치 서비스가 사용 설정되어 있으며 사용 가능해야 합니다."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"이 앱은 기지국 및 Wi-Fi 네트워크와 같은 네트워크 소스를 통해 내 위치를 알 수 있습니다. 앱에서 위치 서비스를 사용하려면 휴대전화에서 위치 서비스가 사용 설정되어 있으며 사용 가능해야 합니다."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"백그라운드에서 정확한 위치에 액세스"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"이 앱은 백그라운드에서 언제든지 나의 정확한 위치를 알 수 있습니다. 앱에서 위치 서비스를 사용하려면 휴대전화에서 위치 서비스가 사용 설정되어 있으며 사용할 수 있어야 합니다. 배터리 사용량이 늘어날 수 있습니다."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"오디오 설정 변경"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"앱이 음량이나 출력을 위해 사용하는 스피커 등 전체 오디오 설정을 변경할 수 있도록 허용합니다."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"오디오 녹음"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"지문 아이콘"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"얼굴 인증 하드웨어 관리"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"사용할 얼굴 템플릿의 추가 및 삭제 메서드를 앱에서 호출하도록 허용합니다."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"얼굴 인증 하드웨어 사용"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"앱에서 얼굴 인증 하드웨어를 인증에 사용하도록 허용합니다."</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"얼굴을 인식할 수 없습니다. 다시 시도해 주세요."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"얼굴이 너무 밝습니다. 조명을 더 어둡게 해 보세요."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"얼굴이 너무 어둡습니다. 조명을 더 밝게 해 보세요."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"센서를 얼굴에서 더 멀리 떨어뜨려 주세요."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"센서를 얼굴에 더 가까이 대 주세요."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"센서를 위쪽으로 옮겨 주세요."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"센서를 아래쪽으로 옮겨 주세요."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"센서를 오른쪽으로 옮겨 주세요."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"센서를 왼쪽으로 옮겨 주세요."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"센서를 바라보세요."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"얼굴을 감지할 수 없습니다."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"얼굴을 움직이지 말고 기기를 마주 보세요."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"얼굴 인식 하드웨어를 사용할 수 없습니다."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"얼굴 인식 시간이 초과되었습니다. 다시 시도하세요."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"얼굴을 저장할 수 없습니다."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"얼굴 인식 작업이 취소되었습니다."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"시도 횟수가 너무 많습니다. 나중에 다시 시도하세요."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"시도 횟수가 너무 많아 얼굴 인증이 사용 중지되었습니다."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"다시 시도해 보세요."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"등록된 얼굴이 없습니다."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"이 기기에는 얼굴 인증 센서가 없습니다."</string>
+    <string name="face_name_template" msgid="7004562145809595384">"얼굴 <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"얼굴 아이콘"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"동기화 설정 읽기"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"앱이 계정의 동기화 설정을 읽을 수 있도록 허용합니다. 예를 들어, 계정에서 주소록 앱을 동기화할지 여부를 확인할 수 있습니다."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"동기화 사용 및 사용 중지 전환"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi가 인터넷에 연결되어 있지 않습니다"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"탭하여 옵션 보기"</string>
+    <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">"이 기기에서는 5GHz 전용 환경설정이 지원되지 않습니다. 대신 가능할 때만 기기에서 5GHz 대역이 사용됩니다."</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>
@@ -1673,7 +1709,7 @@
       <item quantity="one">1초 후에 다시 시도하세요.</item>
     </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"나중에 다시 시도"</string>
-    <string name="immersive_cling_title" msgid="8394201622932303336">"전체 화면 보기 중"</string>
+    <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>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index af2f9fe..a75a00c 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -60,7 +60,7 @@
     <string name="ClirMmi" msgid="7784673673446833091">"Чыгуучу номурду аныктоо"</string>
     <string name="ColpMmi" msgid="3065121483740183974">"Туташкан линия ID-си"</string>
     <string name="ColrMmi" msgid="4996540314421889589">"Туташкан линия ID-син Чектөө"</string>
-    <string name="CfMmi" msgid="5123218989141573515">"Чалууну багыттоо"</string>
+    <string name="CfMmi" msgid="5123218989141573515">"Башка номерге багыттоо"</string>
     <string name="CwMmi" msgid="9129678056795016867">"Чалууну кармап туруу"</string>
     <string name="BaMmi" msgid="455193067926770581">"Чалууга тыюу салуу"</string>
     <string name="PwdMmi" msgid="7043715687905254199">"Сырсөздү өзгөртүү"</string>
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Учак режими"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Учак режими КҮЙҮК"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Учак режими ӨЧҮК"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Батареяны үнөмдөгүч"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Батареяны үнөмдөгүч ӨЧҮК"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Батареяны үнөмдөгүч КҮЙҮК"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Жөндөөлөр"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Жардам"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Үн жардамчысы"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Жайгашкан жер"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"түзмөктүн жайгашкан жерин аныктоого"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосу бул түзмөктүн кайда жүргөнүн көрүп турганга уруксат бересизби?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Сиз бул колдонмону пайдаланып жатканда гана ал жайгашкан жериңизди көрө алат."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна бул түзмөктүн жайгашкан жерин пайдаланууга дайыма уруксат берилсинби?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Сиз бул колдонмону пайдаланбай турганда да ал жайгашкан жериңизди көрүп турат."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Жылнаама"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"жылнаамаңызды пайдалануу"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна жылнаамаңызды пайдаланууга уруксат берилсинби?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Бул колдонмо телефонуңузга жылнаама иш-чараларын кошуп, алып салып же өзгөртүшү мүмкүн. Бул колдонмо жылнаама ээсинин атынан билдирүүлөрдү жөнөтүп же ээсине эскертпестен иш-чараларды өзгөртүшү мүмкүн."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"жайгашкан жерди аныктагычтын кошумча буйруктарын пайдалануу"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Колдонмого жайгашкан жерди табуучу кошумча жабдуучулардын буйруктарын колдонуу мүмкүнчүлүгүн берет. Ушуну менен колдонмо GPS\'тин ишине жана башка жайгашкан жерлерди аныктоо кызматтарына кийлигише алат."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"так аныкталган жайгашкан жерге (GPS жана тармактын негизинде) уруксат"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Бул колдонмо GPS же байланыш мунаралары жана Wi-Fi сыяктуу тармактык булактар аркылуу жайгашкан жериңизди аныктай алат. Колдонмо бул кызматтарды пайдаланышы үчүн, аларды күйгүзүп, телефонуңузга туташтырып коюшуңуз керек. Ушуну менен батареянын кубаты көбүрөөк сарпталышы мүмкүн."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"так аныкталган жайгашкан жерге активдүү режимде гана кирүүгө уруксат берүү"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Бул колдонмонун активдүү режимде гана жайгашкан жериңиздин дайындарын  алууга мүмкүнчүлүгү бар. Колдонмо бул кызматтарды пайдаланышы үчүн аларды күйгүзүп, телефонуңузга туташтырып коюшуңуз керек. Ушуну менен батареянын кубаты көбүрөөк сарпталышы мүмкүн."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"болжолдуу жайгашкан жерге (тармактын негизинде) уруксат"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Бул колдонмо байланыш мунаралары жана Wi-Fi сыяктуу тармактык булактар аркылуу жайгашкан жериңизди аныктай алат. Колдонмо бул кызматтарды пайдаланышы үчүн, аларды күйгүзүп, планшетиңизге туташтырып коюшуңуз керек."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Бул колдонмо байланыш мунаралары жана Wi-Fi сыяктуу тармактык булактар аркылуу жайгашкан жериңизди аныктай алат. Колдонмо бул кызматтарды пайдаланышы үчүн, аларды күйгүзүп, сыналгыңызга туташтырып коюшуңуз керек."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Бул колдонмо байланыш мунаралары жана Wi-Fi сыяктуу тармактык булактар аркылуу жайгашкан жериңизди аныктай алат. Колдонмо бул кызматтарды пайдаланышы үчүн, аларды күйгүзүп, телефонуңузга туташтырып коюшуңуз керек."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"так аныкталган жайгашкан жерге фондук режимде кирүүгө уруксат берүү"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Бул колдонмо фондук режимде сиздин кайда жүргөнүңүздү көрүп турат. Ал үчүн жайгашкан жерди аныктоо функциясын иштетишиңиз керек. Эскерте кетүүчү нерсе, батареяңыз тез отуруп калышы мүмкүн."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"аудио жөндөөлөрүңүздү өзгөртүңүз"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Колдонмого үн деңгээли жана кайсы динамик аркылуу үн чыгарылышы керек сыяктуу түзмөктүн аудио тууралоолорун өзгөртүүгө уруксат берет."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"аудио жаздыруу"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Манжа изинин сөлөкөтү"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"жүздүн аныктыгын текшерүүчү аппараттык камсыздоону башкаруу"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Колдонмого пайдалануу үчүн жүздүн үлгүлөрүн кошуу жана жок кылуу мүмкүндүгүн берет."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"жүздүн аныктыгын текшерүүчү аппараттык камсыздоону колдонуу"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Колдонмого аныктыгын текшерүү үчүн жүздүн аныктыгын текшерүүчү аппараттык камсыздоону пайдалануу мүмкүндүгүн берет"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Жүзүңүз таанылбай койду. Кайра аракет кылыңыз."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Өтө жарык болуп жатат. Жаракты азайтып көрүңүз."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Өтө караңгы болуп жатат. Жарыкты ачып коюңуз."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Сенсорду бетиңизден алысыраак жылдырыңыз."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Сенсорду бетиңизге жакыныраак жылдырыңыз."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Сенсорду жогору жакка жылдырыңыз."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Сенсорду төмөн жакка жылдырыңыз."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Сенсорду оң жакка жылдырыңыз."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Сенсорду сол жакка жылдырыңыз."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Сенсорду караңыз."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Жүзүңүз табылган жок."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Түзмөктүн алдында жүзүңүздү бир калыпта кармаңыз."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Жүздү аныктоочу аппараттык камсыздоо жеткиликсиз."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Жүздүн аныктыгын текшерүүнү күтүү мөөнөтү бүттү. Кайра аракет кылыңыз."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Жүздү сактоо мүмкүн эмес."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Жүздүн аныктыгын текшерүү жокко чыгарылды."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Өтө көп жолу аракет жасадыңыз. Кийинчерээк кайра аракет кылыңыз."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Өтө көп жолу аракет жасадыңыз. Жүздүн аныктыгын текшерүү сенсору өчүрүлдү."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Кайра аракет кылыңыз."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Бир да жүз катталган жок."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Бул түзмөктө жүздүн аныктыгын текшерүү сенсору жок"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Жүз <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Жүздүн сүрөтчөсү"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"шайкештирүү жөндөөлөрүн окуу"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Колдонмого эсеп менен синхрондошуу тууралоолорун окуганга уруксат берет. Мисалы, Кишилер колдонмосу эсеп менен синхрондошкондугун аныктай алат."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"синхрондоштурууну өчүрүү/жандыруу"</string>
@@ -757,7 +790,7 @@
     <string name="lockscreen_transport_stop_description" msgid="5907083260651210034">"Токтотуу"</string>
     <string name="lockscreen_transport_rew_description" msgid="6944412838651990410">"Артка түрүү"</string>
     <string name="lockscreen_transport_ffw_description" msgid="42987149870928985">"Алдыга түрүү"</string>
-    <string name="emergency_calls_only" msgid="6733978304386365407">"Шашылыш чалуу гана"</string>
+    <string name="emergency_calls_only" msgid="6733978304386365407">"Өзгөчө кырдаалда гана чалууга болот"</string>
     <string name="lockscreen_network_locked_message" msgid="143389224986028501">"Тармак кулпуланган"</string>
     <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"SIM-карта PUK-бөгөттө."</string>
     <string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Колдонуучунун нускамасын караңыз же Кардарларды тейлөө борборуна кайрылыңыз."</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi тармагы Интернетке туташпай турат"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Параметрлерди ачуу үчүн таптап коюңуз"</string>
+    <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">"Бул түзмөк 5ГГцти гана колдонуу жөндөөсүн колдоого албайт. Анын ордуна, бул түзмөк 5ГГц жыштыгын ал жеткиликтүү болгондо колдонот."</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>
diff --git a/core/res/res/values-land/dimens_package_installer.xml b/core/res/res/values-land/dimens_package_installer.xml
new file mode 100644
index 0000000..2146241
--- /dev/null
+++ b/core/res/res/values-land/dimens_package_installer.xml
@@ -0,0 +1,24 @@
+<?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 370b65b..ef2cede 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ໂໝດໃນຍົນ"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ເປີດໂໝດຢູ່ໃນຍົນແລ້ວ"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ປິດໂໝດໃນຍົນແລ້ວ"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ຕົວປະຢັດແບັດເຕີຣີ"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ຕົວປະຢັດແບັດເຕີຣີປິດຢູ່"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ຕົວປະຢັດແບັດເຕີຣີເປີດຢູ່"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"​ການ​ຕັ້ງ​ຄ່າ"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"ຕົວຊ່ວຍ"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ຊ່ວຍ​ເຫຼືອ​ທາງ​ສຽງ"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"ສະ​ຖານ​ທີ່"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"ເຂົ້າເຖິງຂໍ້ມູນສະຖານທີ່ຂອງອຸປະກອນນີ້"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"ອະນຸຍາດ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ໃຫ້ເຂົ້າເຖິງສະຖານທີ່ຂອງອຸປະກອນບໍ?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ແອັບຈະມີສິດເຂົ້າເຖິງສະຖານທີ່ໃນເວລາທີ່ທ່ານກຳລັງໃຊ້ແອັບຢູ່ເທົ່ານັ້ນ."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງສະຖານທີ່ຂອງອຸປະກອນນີ້ບໍ?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ແອັບຈະມີສິດເຂົ້າເຖິງສະຖານທີ່ທຸກເທື່ອ ເຖິງແມ່ນໃນເວລາທ່ານບໍ່ໃຊ້ແອັບຢູ່ກໍຕາມ."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"ປະຕິທິນ"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"ເຂົ້າ​ຫາ​ປະ​ຕິ​ທິນ​ຂອງ​ທ່ານ"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"ອະນຸຍາດ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ໃຫ້ເຂົ້າເຖິງປະຕິທິນຂອງທ່ານບໍ?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"This app can add, remove, or change calendar events on your phone. This app can send messages that may appear to come from calendar owners, or change events without notifying their owners."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ເຂົ້າເຖິງຄຳສັ່ງຜູ່ໃຫ້ບໍລິການພິກັດສະຖານທີ່"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ອະນຸຍາດ​ໃຫ້​ແອັບຯ​ເຂົ້າເຖິງ​ຄຳສັ່ງ​ເພີ່ມເຕີມ​ຂອງ​ຜູ່​ໃຫ້​ບໍລິການ​ສະຖານທີ່. ນີ້​ອາດ​ຈະ​ເປັນ​ການ​ເຮັດ​ໃຫ້​ແອັບຯ ລົບກວນ​ການ​ເຮັດ​ວຽກ​ຂອງ GPS ຫຼື​ແຫລ່ງ​ຂໍ້ມູນ​ສະຖານທີ່​ອື່ນໆ​ໄດ້."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"ເຂົ້າ​ຫາທີ່ຕັ້ງທີ່ແນ່ນອນ (ອີງໃສ່ GPS ແລະ ເຄືອຂ່າຍ)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"This app can get your location based on GPS or network location sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ເຂົ້າເຖິງສະຖານທີ່ແນ່ນອນໃນພື້ນໜ້າເທົ່ານັ້ນ"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ແອັບນີ້ສາມາດຮັບເອົາສະຖານທີ່ແນ່ນອນຂອງທ່ານໄດ້ທຸກເວລາທີ່ມັນຢູ່ໃນພື້ນໜ້າ. ການບໍລິການສະຖານທີ່ເຫຼົ່ານີ້ຕ້ອງເປີດຢູ່ ແລະ ສາມາດໃຊ້ໄດ້ໃນໂທລະສັບຂອງທ່ານເພື່ອໃຫ້ແອັບສາມາດໃຊ້ພວກມັນໄດ້. ນີ້ອາດຈະເຮັດໃຫ້ການໃຊ້ແບັດເຕີຣີເພີ່ມຂຶ້ນ."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ເຂົ້າ​ຫາທີ່ຕັ້ງໂດຍປະມານ (ອີງໃສ່ເຄືອຂ່າຍ)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ເຂົ້າເຖິງສະຖານທີ່ແນ່ນອນໃນພື້ນຫຼັງ"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ແອັບນີ້ສາມາດຮັບເອົາສະຖານທີ່ແນ່ນອນຂອງທ່ານໄດ້ທຸກເວລາທີ່ມັນຢູ່ໃນພື້ນຫຼັງ. ການບໍລິການສະຖານທີ່ເຫຼົ່ານີ້ຕ້ອງເປີດຢູ່ ແລະ ສາມາດໃຊ້ໄດ້ໃນໂທລະສັບຂອງທ່ານເພື່ອໃຫ້ແອັບສາມາດໃຊ້ພວກມັນໄດ້. ນີ້ອາດຈະເຮັດໃຫ້ການໃຊ້ແບັດເຕີຣີເພີ່ມຂຶ້ນ."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ປ່ຽນການຕັ້ງຄ່າສຽງຂອງທ່ານ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ອະນຸຍາດໃຫ້ແອັບຯແກ້ໄຂການຕັ້ງຄ່າສຽງສ່ວນກາງ ເຊັ່ນ: ລະດັບສຽງ ແລະລຳໂພງໃດທີ່ຖືກໃຊ້ສົ່ງສຽງອອກ."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ບັນທຶກສຽງ"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ໄອຄອນລາຍນິ້ວມື"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"ຈັດການຮາດແວການກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າ"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"ອະນຸຍາດໃຫ້ແອັບເປີດວິທີການຕ່າງໆເພື່ອເພີ່ມ ແລະ ລຶບແມ່ແບບໃບໜ້າສຳລັບການນຳໃຊ້."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ໃຊ້ຮາດແວການກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າ"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ອະນຸຍາດໃຫ້ແອັບໃຊ້ຮາດແວການກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າສຳລັບການກວດສອບຄວາມຖືກຕ້ອງ"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"ບໍ່ສາມາດປະມວນຜົນຂໍ້ມູນໃບໜ້າໄດ້. ກະລຸນາລອງອີກຄັ້ງ."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"ໃບໜ້າສະຫວ່າງເກີນໄປ. ກະລຸນາລອງໃນບ່ອນທີ່ແສງໜ້ອຍກວ່າ."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"ໃບໜ້າມືດເກີນໄປ. ກະລຸນາເປີດແຫຼ່ງກຳເນີດແສງ."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"ກະລຸນາຍ້າຍເຊັນເຊີອອກຫ່າງຈາກໃບໜ້າຕື່ມອີກ."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"ກະລຸນາຍ້າຍເຊັນເຊີເຂົ້າໃກ້ໃບໜ້າຕື່ມອີກ."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"ກະລຸນາຍ້າຍເຊັນເຊີໃຫ້ສູງຂຶ້ນອີກ."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"ກະລຸນາຍ້າຍເຊັນເຊີໃຫ້ຕໍ່າລົງອີກ."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"ກະລຸນາຍ້າຍເຊັນເຊີໄປເບື້ອງຂວາ."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"ກະລຸນາຍ້າຍເຊັນເຊີໄປເບື້ອງຊ້າຍ."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"ກະລຸນາແນມເບິ່ງເຊັນເຊີ."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"ກວດບໍ່ພົບໃບໜ້າ."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"ຮັກສາໃບໜ້າໃຫ້ຢູ່ນິ້ງຕໍ່ໜ້າອຸປະກອນ"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"ຮາດແວກວດໃບໜ້າບໍ່ສາມາດໃຊ້ໄດ້."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"ໝົດເວລາກວດໃບໜ້າແລ້ວ. ກະລຸນາລອງອີກຄັ້ງ."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"ບໍ່ສາມາດເກັບຮັກສາໃບໜ້າໄວ້ໄດ້."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"ຍົກເລີກການດຳເນີນການກັບໃບໜ້າແລ້ວ."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"ມີຄວາມພະຍາຍາມຫຼາຍຄັ້ງເກີນໄປ. ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ມີຄວາມພະຍາຍາມຫຼາຍຄັ້ງເກີນໄປ. ປິດນຳໃຊ້ການກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າແລ້ວ."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"ລອງອີກຄັ້ງ."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"ບໍ່ໄດ້ລົງທະບຽນໃບໜ້າໃດ."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າ"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"ໃບໜ້າ <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"ໄອຄອນໃບໜ້າ"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ອ່ານການຕັ້ງຄ່າຊິ້ງຂໍ້ມູນ"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ອະນຸຍາດໃຫ້ແອັບຯ ອ່ານການຕັ້ງຄ່າການຊິ້ງຂໍ້ມູນຂອງບັນຊີໄດ້. ຕົວຢ່າງເຊັ່ນ: ມັນຈະສາມາດກວດສອບໄດ້ແອັບຯ People ຖືກຊິ້ງຂໍ້ມູນກັບບັນຊີໃດນຶ່ງແລ້ວຫຼືຍັງ."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ສະລັບການເປີດ ແລະປິດການຊິ້ງຂໍ້ມູນ"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi ບໍ່ມີສັນຍານອິນເຕີເນັດ"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ແຕະເພື່ອເບິ່ງຕົວເລືອກ"</string>
+    <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">"ອຸປະກອນນີ້ບໍ່ຮອງຮັບການຕັ້ງຄ່າຂອງທ່ານສຳລັບ 5GHz ເທົ່ານັ້ນ. ແຕ່ວ່າອຸປະກອນນີ້ຈະໃຊ້ຄື້ນຄວາມຖີ່ 5GHz ເມື່ອສາມາດໃຊ້ໄດ້."</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="NEW_NETWORK">%1$s</xliff:g> ເມື່ອ <xliff:g id="PREVIOUS_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>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index e23a01b..e72b1a9 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -242,9 +242,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Lėktuvo režimas"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ĮJUNGTAS lėktuvo režimas"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"lėktuvo režimas IŠJUNGTAS"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Akumuliatoriaus tausojimo priemonė"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Akumuliatoriaus tausojimo priemonė IŠJUNGTA"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Akumuliatoriaus tausojimo priemonė ĮJUNGTA"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Nustatymai"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Pagalba"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -282,6 +279,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Vietovė"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"pasiekti įrenginio vietovės informaciją"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Suteikti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; galimybę pasiekti įrenginio vietovę?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Programa galės pasiekti vietovę, tik kai ją naudosite."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Visada leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti šio įrenginio vietovę?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Programa visada galės pasiekti vietovę, net kai jos nenaudosite."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendorius"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"pasiekti kalendorių"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Suteikti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; galimybę pasiekti kalendorių?"</string>
@@ -408,12 +408,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ši programa gali pridėti, pašalinti arba pakeisti telefone esančius kalendoriaus įvykius. Ši programa gali siųsti pranešimus, kurie atrodys atsiųsti kalendoriaus savininkų, arba pakeisti įvykius nepranešusi jų savininkams."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pasiekti papildomas vietos teikimo įrankio komandas"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Programai leidžiama pasiekti papildomas vietovės nustatymo paslaugų teikėjų komandas. Dėl to programa gali trukdyti veikti GPS ar kitiems vietovės nustatymo šaltiniams."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"pasiekti tikslią vietą (nustatytą atsižvelgiant į GPS ir tinklą)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Ši programa gali gauti jūsų vietos informaciją naudodamasi tinklo šaltinių, pvz., mobiliojo ryšio bokštų ir „Wi-Fi“ tinklų, duomenimis. Šios vietovės paslaugos turi būti įjungtos ir pasiekiamos telefone, kad programa galėtų jas naudoti. Tai gali padidinti akumuliatoriaus energijos naudojimą."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"pasiekti tikslią vietovę, tik kai programa veikia priekiniame plane"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ši programa gali gauti tikslius jūsų vietovės duomenis bet kuriuo metu, kai veikia priekiniame plane. Šios vietovės paslaugos turi būti įjungtos ir pasiekiamos telefone, kad programa galėtų jas naudoti. Tai gali padidinti akumuliatoriaus energijos suvartojimą."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"pasiekti apytikslę vietą (nustatytą atsižvelgiant į tinklą)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ši programa gali gauti jūsų vietos informaciją naudodamasi tinklo šaltinių, pvz., mobiliojo ryšio bokštų ir „Wi-Fi“ tinklų, duomenimis. Šios vietovės paslaugos turi būti įjungtos ir pasiekiamos planšetiniame kompiuteryje, kad programa galėtų jas naudoti."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ši programa gali gauti jūsų vietos informaciją naudodamasi tinklo šaltinių, pvz., mobiliojo ryšio bokštų ir „Wi-Fi“ tinklų, duomenimis. Šios vietovės paslaugos turi būti įjungtos ir pasiekiamos TV, kad programa galėtų jas naudoti."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ši programa gali gauti jūsų vietos informaciją naudodamasi tinklo šaltinių, pvz., mobiliojo ryšio bokštų ir „Wi-Fi“ tinklų, duomenimis. Šios vietovės paslaugos turi būti įjungtos ir pasiekiamos telefone, kad programa galėtų jas naudoti."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"pasiekti tikslią vietovę, kai programa veikia fone"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ši programa gali gauti tikslius jūsų vietovės duomenis bet kuriuo metu, kai veikia fone. Šios vietovės paslaugos turi būti įjungtos ir pasiekiamos telefone, kad programa galėtų jas naudoti. Tai gali padidinti akumuliatoriaus energijos suvartojimą."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"keisti garso nustatymus"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Leidžiama programai keisti visuotinius garso nustatymus, pvz., garsumą ir tai, kuris garsiakalbis naudojamas išvesčiai."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"įrašyti garsą"</string>
@@ -521,6 +523,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Kontrolinio kodo piktograma"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"tvarkyti veido autentifikavimo aparatinę įrangą"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Programai leidžiama aktyv. metodus, norint pridėti ir ištrinti naudojamus veidų šablonus."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"naudoti veido autentifikavimo aparatinę įrangą"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Programai leidžiama naudoti veido autentifikavimo aparatinę įrangą tapatybei nustatyti"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Nepavyko apdoroti veido. Bandykite dar kartą."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Veidas per šviesus. Band. sumažinti apšvietimą."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Veidas per tamsus. Atidenkite apšvietimo šaltinį."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Patraukite jutiklį toliau nuo veido."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Laikykite jutiklį arčiau veido."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Pakelkite jutiklį aukščiau."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Nuleiskite jutiklį žemiau."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Patraukite jutiklį dešinėn."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Patraukite jutiklį kairėn."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Žiūrėkite į jutiklį."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Neaptikta jokių veidų."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Stabiliai laikykite veidą prieš įrenginį."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Veido atpažinimo aparatinė įranga nepasiekiama."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Baigėsi veido atpaž. skirt. laik. Band. dar kartą."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Nepavyko išsaugoti veido duomenų."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Veido atpažinimo operacija atšaukta."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Per daug bandymų. Vėliau bandykite dar kartą."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Per daug bandymų. Veido autentifik. išjungtas."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Bandykite dar kartą."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Neužregistruota jokių veidų."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Šiame įrenginyje nėra veido autentifikavimo jutiklio"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> veidas"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Veido pkt."</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"skaityti sinchronizavimo nustatymus"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Leidžiama programai skaityti ir sinchronizuoti paskyros nustatymus. Pvz., taip gali būti nustatoma, ar su paskyra sinchronizuota Žmonių programa."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"įjungti arba išjungti sinchronizavimą"</string>
@@ -1219,6 +1252,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"„Wi‑Fi“ tinkle nėra interneto ryšio"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Palieskite, kad būtų rodomos parinktys."</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Viešosios interneto prieigos taško nustatymų pakeitimai"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Viešosios prieigos taško dažnio juosta pasikeitė."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Šiame įrenginyje nepalaikoma tik 5 GHz nuostata. Vietoj to šiame įrenginyje bus naudojama 5 GHz dažnio juosta, kai bus pasiekiama."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Perjungta į tinklą <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Įrenginyje naudojamas kitas tinklas (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>), kai dabartiniame tinkle (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nėra interneto ryšio. Gali būti taikomi mokesčiai."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Perjungta iš tinklo <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> į tinklą <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index f033292..a5116e4 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -239,9 +239,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Lidojuma režīms"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Lidojuma režīms ir IESLĒGTS."</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Lidojuma režīms ir IZSLĒGTS."</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Akumulatora jaudas taupīšanas režīms"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Akumulatora jaudas taupīšanas režīms ir IZSLĒGTS"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Akumulatora jaudas taupīšanas režīms ir IESLĒGTS"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Iestatījumi"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Palīdzība"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Balss palīgs"</string>
@@ -279,6 +276,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Atrašanās vieta"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"piekļūt ierīces atrašanās vietas informācijai"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt šīs ierīces atrašanās vietai?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Lietotne varēs piekļūt atrašanās vietai tikai tad, kad izmantosiet šo lietotni."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Vienmēr atļaut &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt atrašanās vietai?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Lietotne vienmēr varēs piekļūt atrašanās vietai, pat ja neizmantosiet šo lietotni."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendārs"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"piekļūt jūsu kalendāram"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt jūsu kalendāram?"</string>
@@ -405,12 +405,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Šī lietotne var pievienot, noņemt vai mainīt kalendāra pasākumus jūsu tālrunī. Šī lietotne var sūtīt ziņojumus, ko šķietami sūtījuši kalendāru īpašnieki, vai mainīt pasākumus, neinformējot to īpašniekus."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"piekļūt atrašanās vietas nodrošinātāja papildu komandām"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ļauj lietotnei piekļūt papildu atrašanās vietas noteikšanas nodrošinātāju komandām. Tas var ļaut lietotnei traucēt GPS vai citu atrašanās vietas noteikšanas avotu darbību."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"Piekļūt precīzai atrašanās vietai (izmantojot GPS un tīklu)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Šī lietotne var iegūt jūsu atrašanās vietu, pamatojoties uz GPS vai tīkla atrašanās vietas avotiem, piemēram, mobilo sakaru torņiem un Wi-Fi tīkliem. Šiem atrašanās vietu pakalpojumiem ir jābūt ieslēgtiem un pieejamiem jūsu tālrunī, lai lietotne tos varētu izmantot. Tādējādi var palielināties akumulatora patēriņš."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"piekļuve precīzai atrašanās vietai, tikai darbojoties priekšplānā"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Šī lietotne var iegūt precīzu jūsu atrašanās vietu, tikai darbojoties priekšplānā. Šiem atrašanās vietu pakalpojumiem ir jābūt ieslēgtiem un pieejamiem jūsu tālrunī, lai lietotne varētu tos izmantot. Tādējādi var palielināties akumulatora jaudas patēriņš."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"Piekļūt aptuvenai atrašanās vietai (izmantojot tīklu)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Šī lietotne var iegūt jūsu atrašanās vietu, pamatojoties uz tīkla avotiem, piemēram, mobilo sakaru torņiem un Wi-Fi tīkliem. Šiem atrašanās vietu pakalpojumiem ir jābūt ieslēgtiem un pieejamiem jūsu planšetdatorā, lai lietotne tos varētu izmantot."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Šī lietotne var iegūt jūsu atrašanās vietu, pamatojoties uz tīkla avotiem, piemēram, mobilo sakaru torņiem un Wi-Fi tīkliem. Šiem atrašanās vietu pakalpojumiem ir jābūt ieslēgtiem un pieejamiem jūsu televizorā, lai lietotne tos varētu izmantot."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Šī lietotne var iegūt jūsu atrašanās vietu, pamatojoties uz tīkla avotiem, piemēram, mobilo sakaru torņiem un Wi-Fi tīkliem. Šiem atrašanās vietu pakalpojumiem ir jābūt ieslēgtiem un pieejamiem jūsu tālrunī, lai lietotne tos varētu izmantot."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"Piekļuve precīzai atrašanās vietai, darbojoties fonā"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Šī lietotne var iegūt precīzu jūsu atrašanās vietu jebkurā laikā, darbojoties fonā. Šiem atrašanās vietu pakalpojumiem ir jābūt ieslēgtiem un pieejamiem jūsu tālrunī, lai lietotne varētu tos izmantot. Tādējādi var palielināties akumulatora jaudas patēriņš."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"mainīt audio iestatījumus"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ļauj lietotnei mainīt globālos audio iestatījumus, piemēram, skaļumu un izejai izmantoto skaļruni."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ierakstīt audio"</string>
@@ -518,6 +520,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Pirksta nospieduma ikona"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"pārvaldīt sejas autentifikācijas aparatūru"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Atļauj lietotnei izsaukt metodes izmantojamo sejas veidņu pievienošanai un dzēšanai."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"izmantot sejas autentifikācijas aparatūru"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Atļauj lietotnei izmantot sejas autentifikācijas aparatūru autentificēšanai"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Nevarēja apstrādāt sejas datus. Mēģiniet vēlreiz."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Seja ir pārāk izgaismota. Mēģiniet tumšākā vidē."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Pārāk tumšs sejas attēls. Mēģiniet gaišākā vidē."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Pavirziet sensoru tālāk no sejas."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Pavirziet sensoru tuvāk sejai."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Pavirziet sensoru augstāk."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Pavirziet sensoru zemāk."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Pavirziet sensoru pa labi."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Pavirziet sensoru pa kreisi."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Lūdzu, paskatieties uz sensoru."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nav atrasta neviena seja."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Turiet ierīci vērstu pret nekustīgu seju."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Sejas autentifikācijas aparatūra nav pieejama."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Sejas datu nolasīšanas noildze. Mēģiniet vēlreiz."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Sejas datus nevar saglabāt."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Darbība ar sejas datiem atcelta."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Pārāk daudz mēģinājumu. Vēlāk mēģiniet vēlreiz."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Par daudz mēģinājumu. Sejas atpazīšana atspējota."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Mēģiniet vēlreiz."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nav reģistrēti sejas dati."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Šai ierīcei nav sejas autentifikācijas sensora."</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Seja <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Sejas ikona"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lasīt sinhronizācijas iestatījumus"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ļauj lietotnei lasīt konta sinhronizācijas iestatījumus. Piemēram, šādi var noteikt, vai lietotne Personas ir sinhronizēta ar kontu."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ieslēgt un izslēgt sinhronizāciju"</string>
@@ -1197,6 +1230,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi tīklā nav piekļuves internetam."</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Pieskarieties, lai skatītu iespējas."</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Izmaiņas tīklāja iestatījumos"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Ir mainīts tīklāja joslas platums."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Šajā ierīcē netiek atbalstīta jūsu preference par tikai 5 GHz joslu. 5 GHz josla ierīcē tiks izmantota, kad tā būs pieejama."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Pārslēdzās uz tīklu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Kad vienā tīklā (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nav piekļuves internetam, ierīcē tiek izmantots cits tīkls (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>). Var tikt piemērota maksa."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Pārslēdzās no tīkla <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> uz tīklu <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-mcc214/config.xml b/core/res/res/values-mcc214/config.xml
new file mode 100644
index 0000000..9410848
--- /dev/null
+++ b/core/res/res/values-mcc214/config.xml
@@ -0,0 +1,28 @@
+<?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 my obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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">
+  <!-- String array containing numbers that shouldn't be logged
+       016 present here for Spain's gender violence number -->
+  <string-array translatable="false" name="unloggable_phone_numbers">
+    <item>016</item>
+  </string-array>
+</resources>
diff --git a/core/res/res/values-mcc302-mnc220/config.xml b/core/res/res/values-mcc302-mnc220/config.xml
index 8774334..3308128 100644
--- a/core/res/res/values-mcc302-mnc220/config.xml
+++ b/core/res/res/values-mcc302-mnc220/config.xml
@@ -29,8 +29,8 @@
          Or string format of ApnSettingV3.
          note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
     <string-array translatable="false" name="config_tether_apndata">
-        <item>[ApnSettingV3]TELUS ISP,isp.telus.com,,,,,,,,,302,220,,DUN,IP,IP,true,0,,,,,,,gid,54</item>
-        <item>[ApnSettingV3]Tethered Mobile Internet,isp.mb.com,,,,,,,,,302,220,,DUN,,,true,0,,,,,,,gid,50</item>
+        <item>[ApnSettingV3]TELUS ISP,isp.telus.com,,,,,,,,,302,220,,DUN,IP,IP,true,0,,,,,,,gid,5455</item>
+        <item>[ApnSettingV3]Tethered Mobile Internet,isp.mb.com,,,,,,,,,302,220,,DUN,,,true,0,,,,,,,gid,5043</item>
         <item>[ApnSettingV3]Tethered Public Mobile,isp.mb.com,,,,,,,,,302,220,,DUN,,,true,0,,,,,,,gid,4D4F</item>
     </string-array>
 
diff --git a/core/res/res/values-mcc302-mnc221/config.xml b/core/res/res/values-mcc302-mnc221/config.xml
index 05896b1..bb0d4d5 100644
--- a/core/res/res/values-mcc302-mnc221/config.xml
+++ b/core/res/res/values-mcc302-mnc221/config.xml
@@ -27,9 +27,9 @@
          Or string format of ApnSettingV3.
          note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
     <string-array translatable="false" name="config_tether_apndata">
-        <item>[ApnSettingV3]TELUS ISP,isp.telus.com,,,,,,,,,302,221,,DUN,,,true,0,,,,,,,gid,54</item>
-        <item>[ApnSettingV3]Tethered PC Mobile,isp.mb.com,,,,,,,,,302,221,,DUN,,,true,0,,,,,,,gid,50</item>
-        <item>[ApnSettingV3]Koodo,sp.koodo.com,,,,,,,,,302,221,,DUN,,,true,0,,,,,,,gid,4B</item>
+        <item>[ApnSettingV3]TELUS ISP,isp.telus.com,,,,,,,,,302,221,,DUN,,,true,0,,,,,,,gid,5455</item>
+        <item>[ApnSettingV3]Tethered PC Mobile,isp.mb.com,,,,,,,,,302,221,,DUN,,,true,0,,,,,,,gid,5043</item>
+        <item>[ApnSettingV3]Koodo,sp.koodo.com,,,,,,,,,302,221,,DUN,,,true,0,,,,,,,gid,4B4F</item>
     </string-array>
 
     <!-- Values for GPS configuration (Telus) -->
diff --git a/core/res/res/values-mcc313-mnc100-as/strings.xml b/core/res/res/values-mcc313-mnc100-as/strings.xml
deleted file mode 100644
index 350757c..0000000
--- a/core/res/res/values-mcc313-mnc100-as/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"ফ\'নৰ অনুমতি নাই MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-az/strings.xml b/core/res/res/values-mcc313-mnc100-az/strings.xml
deleted file mode 100644
index 44796df..0000000
--- a/core/res/res/values-mcc313-mnc100-az/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"MM#6 telefonu dəstəklənmir"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-b+sr+Latn/strings.xml b/core/res/res/values-mcc313-mnc100-b+sr+Latn/strings.xml
deleted file mode 100644
index d5bf39e..0000000
--- a/core/res/res/values-mcc313-mnc100-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Telefon nije dozvoljen MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-be/strings.xml b/core/res/res/values-mcc313-mnc100-be/strings.xml
deleted file mode 100644
index c9f4633..0000000
--- a/core/res/res/values-mcc313-mnc100-be/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Тэлефон не дапускаецца MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-bn/strings.xml b/core/res/res/values-mcc313-mnc100-bn/strings.xml
deleted file mode 100644
index 5292241..0000000
--- a/core/res/res/values-mcc313-mnc100-bn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"ফোন অনুমোদিত নয় MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-bs/strings.xml b/core/res/res/values-mcc313-mnc100-bs/strings.xml
deleted file mode 100644
index d5bf39e..0000000
--- a/core/res/res/values-mcc313-mnc100-bs/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Telefon nije dozvoljen MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-en-rAU/strings.xml b/core/res/res/values-mcc313-mnc100-en-rAU/strings.xml
deleted file mode 100644
index f1a3611..0000000
--- a/core/res/res/values-mcc313-mnc100-en-rAU/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Phone not allowed MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-en-rCA/strings.xml b/core/res/res/values-mcc313-mnc100-en-rCA/strings.xml
deleted file mode 100644
index f1a3611..0000000
--- a/core/res/res/values-mcc313-mnc100-en-rCA/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Phone not allowed MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-en-rIN/strings.xml b/core/res/res/values-mcc313-mnc100-en-rIN/strings.xml
deleted file mode 100644
index f1a3611..0000000
--- a/core/res/res/values-mcc313-mnc100-en-rIN/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Phone not allowed MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-en-rXC/strings.xml b/core/res/res/values-mcc313-mnc100-en-rXC/strings.xml
deleted file mode 100644
index 8a8bf7e..0000000
--- a/core/res/res/values-mcc313-mnc100-en-rXC/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‎‎‏‎‏‎‎‎‎‏‎‎‏‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎‏‎‎‎Phone not allowed MM#6‎‏‎‎‏‎"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-et/strings.xml b/core/res/res/values-mcc313-mnc100-et/strings.xml
deleted file mode 100644
index 83cfbaf..0000000
--- a/core/res/res/values-mcc313-mnc100-et/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Telefon pole lubatud MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-eu/strings.xml b/core/res/res/values-mcc313-mnc100-eu/strings.xml
deleted file mode 100644
index 028ca37..0000000
--- a/core/res/res/values-mcc313-mnc100-eu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Telefonoa ez da onartzen MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-fr-rCA/strings.xml b/core/res/res/values-mcc313-mnc100-fr-rCA/strings.xml
deleted file mode 100644
index 89c50ea..0000000
--- a/core/res/res/values-mcc313-mnc100-fr-rCA/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Téléphone non autorisé MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-gl/strings.xml b/core/res/res/values-mcc313-mnc100-gl/strings.xml
deleted file mode 100644
index 04390a0..0000000
--- a/core/res/res/values-mcc313-mnc100-gl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Non se admite o teléfono MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-gu/strings.xml b/core/res/res/values-mcc313-mnc100-gu/strings.xml
deleted file mode 100644
index 6291d57..0000000
--- a/core/res/res/values-mcc313-mnc100-gu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"MM#6 ફોનની મંજૂરી નથી"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-hy/strings.xml b/core/res/res/values-mcc313-mnc100-hy/strings.xml
deleted file mode 100644
index 62acde3..0000000
--- a/core/res/res/values-mcc313-mnc100-hy/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Հեռախոսի օգտագործումն արգելված է (MM#6)"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-is/strings.xml b/core/res/res/values-mcc313-mnc100-is/strings.xml
deleted file mode 100644
index 3ad7b3c..0000000
--- a/core/res/res/values-mcc313-mnc100-is/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Sími ekki leyfður MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-ka/strings.xml b/core/res/res/values-mcc313-mnc100-ka/strings.xml
deleted file mode 100644
index a063fc4..0000000
--- a/core/res/res/values-mcc313-mnc100-ka/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"ტელეფონი დაუშვებელია MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-kk/strings.xml b/core/res/res/values-mcc313-mnc100-kk/strings.xml
deleted file mode 100644
index 0562a2f..0000000
--- a/core/res/res/values-mcc313-mnc100-kk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Телефон пайдалануға болмайды MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-km/strings.xml b/core/res/res/values-mcc313-mnc100-km/strings.xml
deleted file mode 100644
index 74e607b..0000000
--- a/core/res/res/values-mcc313-mnc100-km/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"មិន​អនុញ្ញាត​ចំពោះ​ទូរសព្ទ​ទេ MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-kn/strings.xml b/core/res/res/values-mcc313-mnc100-kn/strings.xml
deleted file mode 100644
index e287270..0000000
--- a/core/res/res/values-mcc313-mnc100-kn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"ಫೋನ್ MM#6 ಅನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-ky/strings.xml b/core/res/res/values-mcc313-mnc100-ky/strings.xml
deleted file mode 100644
index 8c08c4f..0000000
--- a/core/res/res/values-mcc313-mnc100-ky/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Телефонду колдонууга тыюу салынган MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-lo/strings.xml b/core/res/res/values-mcc313-mnc100-lo/strings.xml
deleted file mode 100644
index 793b87b..0000000
--- a/core/res/res/values-mcc313-mnc100-lo/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"ບໍ່ອະນຸຍາດໃຫ້ໃຊ້ໂທລະສັບ MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-mk/strings.xml b/core/res/res/values-mcc313-mnc100-mk/strings.xml
deleted file mode 100644
index 0b403e9..0000000
--- a/core/res/res/values-mcc313-mnc100-mk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Телефонот не е дозволен MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-ml/strings.xml b/core/res/res/values-mcc313-mnc100-ml/strings.xml
deleted file mode 100644
index 1adc455..0000000
--- a/core/res/res/values-mcc313-mnc100-ml/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"ഫോൺ അനുവദനീയമല്ല MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-mn/strings.xml b/core/res/res/values-mcc313-mnc100-mn/strings.xml
deleted file mode 100644
index 5d5fbff..0000000
--- a/core/res/res/values-mcc313-mnc100-mn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Утсыг зөвшөөрөөгүй MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-mr/strings.xml b/core/res/res/values-mcc313-mnc100-mr/strings.xml
deleted file mode 100644
index 32c6946..0000000
--- a/core/res/res/values-mcc313-mnc100-mr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"फोन MM#6 ला अनुमती देत नाही"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-my/strings.xml b/core/res/res/values-mcc313-mnc100-my/strings.xml
deleted file mode 100644
index 7de66f7..0000000
--- a/core/res/res/values-mcc313-mnc100-my/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"ဖုန်းကို ခွင့်မပြုပါ MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-ne/strings.xml b/core/res/res/values-mcc313-mnc100-ne/strings.xml
deleted file mode 100644
index 0fb9d64..0000000
--- a/core/res/res/values-mcc313-mnc100-ne/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"फोनलाई अनुमति छैन MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-or/strings.xml b/core/res/res/values-mcc313-mnc100-or/strings.xml
deleted file mode 100644
index a64ee94..0000000
--- a/core/res/res/values-mcc313-mnc100-or/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"ଫୋନ୍‌ର ଅନୁମତି ନାହିଁ MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-pa/strings.xml b/core/res/res/values-mcc313-mnc100-pa/strings.xml
deleted file mode 100644
index 87b2e47..0000000
--- a/core/res/res/values-mcc313-mnc100-pa/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"ਫ਼ੋਨ ਨੂੰ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-pt-rBR/strings.xml b/core/res/res/values-mcc313-mnc100-pt-rBR/strings.xml
deleted file mode 100644
index f80f618..0000000
--- a/core/res/res/values-mcc313-mnc100-pt-rBR/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Smartphone não permitido MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-si/strings.xml b/core/res/res/values-mcc313-mnc100-si/strings.xml
deleted file mode 100644
index 9493af0b..0000000
--- a/core/res/res/values-mcc313-mnc100-si/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"දුරකථනය MM#6 ඉඩ නොදේ"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-sq/strings.xml b/core/res/res/values-mcc313-mnc100-sq/strings.xml
deleted file mode 100644
index 237a4a4..0000000
--- a/core/res/res/values-mcc313-mnc100-sq/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Telefoni nuk lejohet MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-sw/strings.xml b/core/res/res/values-mcc313-mnc100-sw/strings.xml
deleted file mode 100644
index a7574fb..0000000
--- a/core/res/res/values-mcc313-mnc100-sw/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Simu hairuhusiwi MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-ta/strings.xml b/core/res/res/values-mcc313-mnc100-ta/strings.xml
deleted file mode 100644
index 7ef5ea9..0000000
--- a/core/res/res/values-mcc313-mnc100-ta/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"ஃபோன் அனுமதிக்கப்படவில்லை MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-te/strings.xml b/core/res/res/values-mcc313-mnc100-te/strings.xml
deleted file mode 100644
index 8908fb7..0000000
--- a/core/res/res/values-mcc313-mnc100-te/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"ఫోన్ అనుమతించబడదు MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-ur/strings.xml b/core/res/res/values-mcc313-mnc100-ur/strings.xml
deleted file mode 100644
index d670d0e..0000000
--- a/core/res/res/values-mcc313-mnc100-ur/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"‏فون کی اجازت نہیں ہے MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-uz/strings.xml b/core/res/res/values-mcc313-mnc100-uz/strings.xml
deleted file mode 100644
index 202a30c..0000000
--- a/core/res/res/values-mcc313-mnc100-uz/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"Chaqiruvlar taqiqlangan (MM#6)"</string>
-</resources>
diff --git a/core/res/res/values-mcc313-mnc100-zh-rHK/strings.xml b/core/res/res/values-mcc313-mnc100-zh-rHK/strings.xml
deleted file mode 100644
index db85730..0000000
--- a/core/res/res/values-mcc313-mnc100-zh-rHK/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mmcc_illegal_me" msgid="7320955531336937252">"不允許手機 MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mcc334-mnc03/config.xml b/core/res/res/values-mcc334-mnc03/config.xml
new file mode 100644
index 0000000..c0d2b35
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc03/config.xml
@@ -0,0 +1,20 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc030/config.xml b/core/res/res/values-mcc334-mnc030/config.xml
new file mode 100644
index 0000000..c0d2b35
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc030/config.xml
@@ -0,0 +1,20 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc704-mnc03/config.xml b/core/res/res/values-mcc704-mnc03/config.xml
new file mode 100644
index 0000000..c0d2b35
--- /dev/null
+++ b/core/res/res/values-mcc704-mnc03/config.xml
@@ -0,0 +1,20 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc706-mnc04/config.xml b/core/res/res/values-mcc706-mnc04/config.xml
new file mode 100644
index 0000000..c0d2b35
--- /dev/null
+++ b/core/res/res/values-mcc706-mnc04/config.xml
@@ -0,0 +1,20 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc712-mnc04/config.xml b/core/res/res/values-mcc712-mnc04/config.xml
new file mode 100644
index 0000000..c0d2b35
--- /dev/null
+++ b/core/res/res/values-mcc712-mnc04/config.xml
@@ -0,0 +1,20 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc716-mnc06/config.xml b/core/res/res/values-mcc716-mnc06/config.xml
new file mode 100644
index 0000000..c0d2b35
--- /dev/null
+++ b/core/res/res/values-mcc716-mnc06/config.xml
@@ -0,0 +1,20 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc716-mnc10/config.xml b/core/res/res/values-mcc716-mnc10/config.xml
new file mode 100644
index 0000000..c0d2b35
--- /dev/null
+++ b/core/res/res/values-mcc716-mnc10/config.xml
@@ -0,0 +1,20 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc716-mnc17/config.xml b/core/res/res/values-mcc716-mnc17/config.xml
new file mode 100644
index 0000000..c0d2b35
--- /dev/null
+++ b/core/res/res/values-mcc716-mnc17/config.xml
@@ -0,0 +1,20 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc722-mnc07/config.xml b/core/res/res/values-mcc722-mnc07/config.xml
new file mode 100644
index 0000000..c0d2b35
--- /dev/null
+++ b/core/res/res/values-mcc722-mnc07/config.xml
@@ -0,0 +1,20 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc732-mnc123/config.xml b/core/res/res/values-mcc732-mnc123/config.xml
new file mode 100644
index 0000000..c0d2b35
--- /dev/null
+++ b/core/res/res/values-mcc732-mnc123/config.xml
@@ -0,0 +1,20 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc740-mnc00/config.xml b/core/res/res/values-mcc740-mnc00/config.xml
new file mode 100644
index 0000000..c0d2b35
--- /dev/null
+++ b/core/res/res/values-mcc740-mnc00/config.xml
@@ -0,0 +1,20 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+    <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index c16de4c..47f388d 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Режим на работа во авион"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Режимот на работа во авион е вклучен"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Режимот на работа во авион е исклучен"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Штедач на батерија"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Штедачот на батерија е ИСКЛУЧЕН"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Штедачот на батерија е ВКЛУЧЕН"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Поставки"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Асистенција"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Гласовна помош"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Локација"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"пристапува до локацијата на овој уред"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Дали да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до локацијата на уредот?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Апликацијата ќе има пристап до локацијата само додека ја користите."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Дозволете &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; секогаш да пристапува до локацијата?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Апликацијата секогаш ќе има пристап до локацијата, дури и кога не ја користите."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"пристапува до календарот"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Дали да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до календарот?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Апликацијава може да додава, отстранува или менува настани во календарот на телефонот. Таа може да испраќа пораки што ќе изгледаат како да се испратени од сопствениците на календарот или да менува настани без да ги извести нивните сопственици."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"пристапи кон наредби на давателот на дополнителна локација"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Овозможува апликацијата да пристапи кон дополнителни наредби на давател на локација. Ова може да овозможи апликацијата да го попечи функционирањето на GPS или други извори на локација."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"пристап до прецизната локација (GPS и врз база на мрежа)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Апликацијава може да ја добие вашата локација од GPS или мрежните извори за локација како што се репетиторите за мобилни мрежи и Wi-Fi мрежите. Овие услуги за локација мора да се вклучени и достапни на телфонот за да може да ги користи апликацијата. Ова може да ја зголеми потрошувачката на батеријата."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"пристап до прецизната локација само во преден план"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Апликацијава може да ја добие вашата точна локација само кога е во преден план. Услугиве според локација мора да се вклучени и достапни на телефонот за да може да ги користи апликацијата. Ова може да го зголеми трошењето на батеријата."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"пристап до приближната локација (врз база на мрежа)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Апликацијава може да ја добие вашата локација од мрежните извори како што се репетиторите за мобилни мрежи и Wi-Fi мрежите. Овие услуги за локација мора да се вклучени и достапни на таблетот за да може да ги користи апликацијата."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Апликацијава може да ја добие вашата локација од мрежните извори како што се репетиторите за мобилни мрежи и Wi-Fi мрежите. Овие услуги за локација мора да се вклучени и достапни на телевизорот за да може да ги користи апликацијата."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Апликацијава може да ја добие вашата локација од мрежните извори како што се репетиторите за мобилни мрежи и Wi-Fi мрежите. Овие услуги за локација мора да се вклучени и достапни на телефонот за да може да ги користи апликацијата."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"пристап до прецизната локација во заднина"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Апликацијава може да ја добие вашата точна локација секогаш кога е во заднина. Услугиве според локација мора да се вклучени и достапни на телефонот за да може да ги користи апликацијата. Ова може да го зголеми трошењето на батеријата."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"менува аудио поставки"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Овозможува апликацијата да ги менува глобалните аудио поставки, како што се јачината на звукот и кој звучник се користи за излез."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"снимај аудио"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Икона за отпечатоци"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"управува со хардвер за проверка на лице"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Дозволува апликац. да повика начини за додавање и бришење шаблони на лице за користење."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"користи хардвер за проверка на лице"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Дозволува апликацијата да користи хардвер за лице за проверка"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Лицето не можеше да се обработи. Обидете се пак."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Лицето е пресветло. Пробајте на послаба светлина."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Лицето е претемно. Пробајте со поголема светлина."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Оддалечете го сензорот од лицето."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Доближете го сензорот до лицето."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Подигнете го сензорот."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Спуштете го сензорот."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Поместете го сензорот надесно."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Поместете го сензорот налево."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Погледнете во сензорот."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Не е откриено лице."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Држете го лицето стабилно пред уредот."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Хардверот за лице не е достапен."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Истече времето за проверка на лице. Повторен обид."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Лицето не може да се чува."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Операцијата со лице се откажа."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Премногу обиди. Обидете се повторно подоцна."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Премногу обиди. Проверката на лице е оневозможена."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Обидете се повторно."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Нема регистрирано лице."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Уредов нема сензор за проверка на лице"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Лице <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Икона"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"чита поставки за синхронизација"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Овозможува апликацијата да ги чита поставките за синхронизирање на сметка. На пример, така може да се утврди дали апликацијата „Луѓе“ е синхронизирана со сметка."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"вклучи и исклучи синхронизација"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi нема пристап до интернет"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Допрете за опции"</string>
+    <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">"Уредов не ги поддржува вашите поставки за само 5 GHz. Наместо тоа, ќе го користи појасот од 5 GHz кога е достапен."</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="NEW_NETWORK">%1$s</xliff:g> кога <xliff:g id="PREVIOUS_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>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 5c78594..1a3556c 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ഫ്ലൈറ്റ് മോഡ്"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ഫ്ലൈറ്റ് മോഡ് ഓണാണ്"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ഫ്ലൈറ്റ് മോഡ് ഓഫാണ്"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ബാറ്ററി ലാഭിക്കൽ"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ബാറ്ററി സേവർ ഓഫാണ്"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ബാറ്ററി ലാഭിക്കൽ ഓണാണ്"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"ക്രമീകരണം"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"അസിസ്റ്റ്"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"വോയ്‌സ് സഹായം"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"ലൊക്കേഷൻ"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്സസ് ചെയ്യാൻ"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കണോ?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"നിങ്ങൾ ഉപയോഗിക്കുമ്പോൾ മാത്രം ആപ്പ് ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യും."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എപ്പോഴും ആപ്പിനെ അനുവദിക്കണോ?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"നിങ്ങൾ ആപ്പ് ഉപയോഗിക്കാത്തപ്പോൾ പോലും, ഏതുസമയത്തും ആപ്പ് ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യും."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"കലണ്ടർ"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"നിങ്ങളുടെ കലണ്ടർ ആക്‌സസ്സ് ചെയ്യുക"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"നിങ്ങളുടെ കലണ്ടർ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കണോ?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ഈ ആപ്പിന്, നിങ്ങളുടെ ഫോണിൽ കലണ്ടർ ഇവന്റുകൾ ചേർക്കാനോ നീക്കംചെയ്യാനോ മാറ്റാനോ കഴിയും. കലണ്ടർ ഉടമകളിൽ നിന്നാണ് വരുന്നതെന്ന് തോന്നിപ്പിക്കാവുന്ന സന്ദേശങ്ങൾ അയയ്ക്കാനോ ഉടമകളെ അറിയിക്കാതെ അവരുടെ ഇവന്റുകളെ മാറ്റാനോ ഈ ആപ്പിന് കഴിയും."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ലൊക്കേഷൻ ദാതാവിന്റെ അധിക കമാൻഡുകൾ ആക്‌സസ്സുചെയ്യുക"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ലൊക്കേഷൻ ദാതാവിന്റെ അധിക കമാൻഡുകൾ ആക്‌സസ്സുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് GPS-ന്റെയോ മറ്റ് ലൊക്കേഷൻ ഉറവിടങ്ങളുടെയോ പ്രവർത്തനത്തിൽ ഇടപെടാൻ അപ്ലിക്കേഷനെ അനുവദിക്കാനിടയുണ്ട്."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"കൃത്യമായ ലൊക്കേഷൻ (GPS - നെറ്റ്‌വർക്ക് അധിഷ്ഠിതം) ആക്സസ് ചെയ്യുക"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"GPS-നെയോ സെൽ ടവറുകളും വൈഫൈ നെറ്റ്‌വർക്കുകളും പോലുള്ള നെറ്റ്‌വർക്ക് ലൊക്കേഷൻ ഉറവിടങ്ങളെയോ അടിസ്ഥാനമാക്കിക്കൊണ്ട് ഈ ആപ്പിന് നിങ്ങളുടെ ലൊക്കേഷൻ അനുമാനിക്കാൻ കഴിയും. നിങ്ങളുടെ ഫോണിൽ ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓൺ ചെയ്തിട്ടുണ്ടെങ്കിൽ മാത്രമാണ് ആപ്പിന് അവ ഉപയോഗിക്കാൻ കഴിയുക. ഇത് ബാറ്ററി ഉപയോഗം വർദ്ധിപ്പിച്ചേക്കാം."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ഫോർഗ്രൗണ്ടിൽ മാത്രം കൃത്യമായ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യുക"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ഫോർഗ്രൗണ്ടിൽ ഉള്ളപ്പോൾ മാത്രമേ ഈ ആപ്പിന് നിങ്ങളുടെ കൃത്യമായ ലൊക്കേഷൻ നേടാനാവൂ. ആപ്പിന് ഉപയോഗിക്കാനായി, ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓണായിരിക്കുകയും നിങ്ങളുടെ ഫോണിൽ ലഭ്യമാവുകയും വേണം. ഇതിലൂടെ ബാറ്ററി ഉപഭോഗം വർദ്ധിച്ചേക്കാം."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ഏകദേശ ലൊക്കേഷൻ (നെറ്റ്‌വർക്ക് അധിഷ്ഠിതം) ആക്സസ് ചെയ്യുക"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"സെൽ ടവറുകളും വൈഫൈ നെറ്റ്‌വർക്കുകളും പോലുള്ള നെറ്റ്‌വർക്ക് ഉറവിടങ്ങളെ അടിസ്ഥാനമാക്കിക്കൊണ്ട് ഈ ആപ്പിന് നിങ്ങളുടെ ലൊക്കേഷൻ അനുമാനിക്കാൻ കഴിയും. നിങ്ങളുടെ ടാബ്‌ലെറ്റിൽ ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓൺ ചെയ്തിട്ടുണ്ടെങ്കിൽ മാത്രമാണ് ആപ്പിന് അവ ഉപയോഗിക്കാൻ കഴിയുക."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"സെൽ ടവറുകളും വൈഫൈ നെറ്റ്‌വർക്കുകളും പോലുള്ള നെറ്റ്‌വർക്ക് ഉറവിടങ്ങളെ അടിസ്ഥാനമാക്കിക്കൊണ്ട് ഈ ആപ്പിന് നിങ്ങളുടെ ലൊക്കേഷൻ അനുമാനിക്കാൻ കഴിയും. നിങ്ങളുടെ ടിവിയിൽ ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓൺ ചെയ്തിട്ടുണ്ടെങ്കിൽ മാത്രമാണ് ആപ്പിന് അവ ഉപയോഗിക്കാൻ കഴിയുക."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"സെൽ ടവറുകളും വൈഫൈ നെറ്റ്‌വർക്കുകളും പോലുള്ള നെറ്റ്‌വർക്ക് ഉറവിടങ്ങളെ അടിസ്ഥാനമാക്കിക്കൊണ്ട് ഈ ആപ്പിന് നിങ്ങളുടെ ലൊക്കേഷൻ അനുമാനിക്കാൻ കഴിയും. നിങ്ങളുടെ ഫോണിൽ ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓൺ ചെയ്തിട്ടുണ്ടെങ്കിൽ മാത്രമാണ് ആപ്പിന് അവ ഉപയോഗിക്കാൻ കഴിയുക."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"പശ്ചാത്തലത്തിൽ കൃത്യമായ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യുക"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ഈ ആപ്പ് പശ്ചാത്തലത്തിൽ ഉള്ളപ്പോൾ എപ്പോൾ വേണമെങ്കിലും ഇതിന് നിങ്ങളുടെ കൃത്യമായ ലൊക്കേഷൻ നേടാനാവും. ആപ്പിന് ഉപയോഗിക്കാനായി, ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓണായിരിക്കുകയും നിങ്ങളുടെ ഫോണിൽ ലഭ്യമാവുകയും വേണം. ഇതിലൂടെ ബാറ്ററി ഉപഭോഗം വർദ്ധിച്ചേക്കാം."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"നിങ്ങളുടെ ഓഡിയോ ക്രമീകരണങ്ങൾ മാറ്റുക"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"വോളിയവും ഔട്ട്പുട്ടിനായി ഉപയോഗിച്ച സ്‌പീക്കറും പോലുള്ള ആഗോള ഓഡിയോ ക്രമീകരണങ്ങൾ പരിഷ്‌ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ഓഡിയോ റെക്കോർഡ് ചെയ്യുക"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"വിരലടയാള ഐക്കൺ"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"മുഖം തിരിച്ചറിയൽ ഹാർഡ്‌വെയർ മാനേജ് ചെയ്യുക"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"ഉപയോഗിക്കാനായി, മുഖത്തിന്റെ ടെംപ്ലേറ്റുകൾ ചേർക്കാനും ഇല്ലാതാക്കാനുമുള്ള രീതികൾ അഭ്യർത്ഥിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"മുഖം തിരിച്ചറിയൽ ഹാർഡ്‌വെയർ ഉപയോഗിക്കുക"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"പരിശോധിച്ചുറപ്പിക്കലിനായി മുഖം തിരിച്ചറിയൽ ഹാർഡ്‌വെയർ  ഉപയോഗിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"മുഖം പ്രോസസ്സ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"മുഖം വളരെ തെളിച്ചമുള്ളതാണ്. കുറഞ്ഞ വെളിച്ചത്തിൽ ശ്രമിക്കുക."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"മുഖം വളരെ ഇരുണ്ടതാണ്. പ്രകാശ ലഭ്യത ഉറപ്പാക്കി ശ്രമിക്കുക."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"മുഖത്തിനടുത്തുനിന്ന് സെൻസർ അകലേയ്ക്ക് നീക്കുക."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"സെൻസർ മുഖത്തിനടുത്തേയ്ക്ക് കൊണ്ടുവരിക."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"സെൻസർ മുകളിലേക്ക് നീക്കുക."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"സെൻസർ താഴേയ്ക്ക് നീക്കുക."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"സെൻസർ വലത്തേയ്ക്ക് നീക്കുക."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"സെൻസർ ഇടത്തേയ്ക്ക് നീക്കുക."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"സെൻസറിലേക്ക് നോക്കുക."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"മുഖം കണ്ടെത്താനായില്ല."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"ഉപകരണത്തിന് മുന്നിൽ മുഖം ഇളകാതെ നേരെ നിറുത്തുക."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"മുഖത്തിന്റെ ഹാർഡ്‌വെയർ ലഭ്യമല്ല."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"മുഖം നൽകേണ്ട സമയം കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"മുഖം സൂക്ഷിക്കാനാവില്ല."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"മുഖത്തിന്റെ പ്രവർത്തനം റദ്ദാക്കി."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"നിരവധി തവണ ശ്രമിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"നിരവധി തവണ ശ്രമിച്ചു. മുഖം തിരിച്ചറിയൽ പ്രവർത്തനരഹിതമാക്കി."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"വീണ്ടും ശ്രമിക്കുക."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"ഒരു മുഖവും എൻറോൾ ചെയ്‌തിട്ടില്ല."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"ഈ ഉപകരണത്തിന് മുഖം തിരിച്ചറിയാനാവുന്ന സെൻസർ ഇല്ല"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"മുഖം <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"മുഖത്തിന്റെ ഐക്കൺ"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"സമന്വയ ക്രമീകരണങ്ങൾ റീഡുചെയ്യുക"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ഒരു അക്കൗണ്ടിനായി സമന്വയ ക്രമീകരണങ്ങൾ റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഉദാഹരണത്തിന്, ആളുകൾ അപ്ലിക്കേഷൻ ഒരു അക്കൗണ്ടിൽ സമന്വയിപ്പിച്ചിട്ടുണ്ടോയെന്നത് നിർണ്ണയിക്കാൻ ഇതിനാകും."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"സമന്വയം ഓണാക്കുക, ഓഫാക്കുക ടോഗിൾചെയ്യുക"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"വൈഫൈയ്ക്ക് ഇന്റർനെറ്റ് ആക്‌സസ് ഇല്ല"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക"</string>
+    <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">"നിങ്ങളുടെ മുൻഗണനയനുസരിച്ചുള്ള, 5GHz മാത്രം എന്നത് ഈ ഉപകരണം പിന്തുണയ്ക്കുന്നില്ല. പകരം, 5GHz ബാൻഡ് ലഭ്യമാകുമ്പോൾ അത് ഉപയോഗിക്കും."</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>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 0e724bc..e1b9670 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -133,8 +133,8 @@
     <item msgid="4397097370387921767">"%s Wi-Fi Дуудлага"</item>
   </string-array>
     <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Идэвхгүй"</string>
-    <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi илүү эрхэмлэдэг"</string>
-    <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Давуу эрхтэй мобайл"</string>
+    <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Wi-Fi давуу эрхтэй"</string>
+    <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Мобайл давуу эрхтэй"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Зөвхөн Wi-Fi"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: дамжуулагдаагүй"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Нислэгийн горим"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Нислэгийн горим асав"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Нислэгийн горим унтарсан"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Батарей хэмнэгч"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Тэжээл хэмнэгч УНТРААЛТТАЙ байна"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Тэжээл хэмнэгч АСААЛТТАЙ байна"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Тохиргоо"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Туслах"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Дуут туслах"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Байршил"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"энэ төхөөрөмжийн байршилд хандалт хийх"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д энэ төхөөрөмжийн байршилд хандахыг зөвшөөрөх үү?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Та аппыг зөвхөн хэрэглэж байгаа үед апп нь байршилд хандах болно."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д энэ төхөөрөмжийн байршилд хандахыг байнга зөвшөөрөх үү?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Та энэ аппыг хэрэглээгүй байсан ч апп нь байршилд үргэлж хандах болно."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Хуанли"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"Хуанли руу хандах"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д таны календарьт хандахыг зөвшөөрөх үү?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Энэ апп таны утсанд хуанлийн арга хэмжээг нэмэх, устгах, эсвэл өөрчлөх боломжтой. Энэ апп нь хуанли эзэмшигчээс зурвас илгээсэн мэт харагдах, эсвэл эзэмшигчид мэдэгдэлгүйгээр арга хэмжээг өөрчлөх боломжтой."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"байршил нийлүүлэгчийн нэмэлт тушаалд хандах"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Апп нь байршил нийлүүлэгчийн нэмэлт тушаалд хандах боломжтой. Энэ нь апп-д GPS эсвэл бусад байршлын үйлчилгээний ажиллагаанд нөлөөлөх боломжийг олгоно."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"тодорхой байршилд хандах (GPS, сүлжээнд суурилсан)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Энэ апп GPS, эсвэл үүрэн цамхаг, Wi-Fi сүлжээ зэрэг сүлжээний байршлын эх сурвалжийг ашиглан таны байршлыг мэдэх боломжтой. Эдгээр байршлын үйлчилгээ нь таны утсанд асаалттай байх ёстой ба апп ашиглах боломжтой байх ёстой. Энэ нь батерей зарцуулалтыг нэмэгдүүлнэ."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"нарийвчилсан байршилд зөвхөн нүүр хэсэгт хандах"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Энэ апп нь зөвхөн нүүр хэсэгт байх үедээ л таны байршлыг нарийн тогтоох боломжтой. Апп эдгээр байршлын үйлчилгээг ашиглахын тулд эдгээрийг таны утсан дээр асааж идэвхтэй байлгах шаардлагатай. Энэ нь батарейны хэрэглээг ихэсгэж болзошгүй."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ойролцоох байршилд хандах (сүлжээнд суурилсан)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Энэ апп үүрэн цамхаг, Wi-Fi сүлжээ зэрэг сүлжээний байршлын эх сурвалжийг ашиглан таны байршлыг мэдэх боломжтой. Эдгээр байршлын үйлчилгээ нь таны таблетад асаалттай байх ёстой ба апп ашиглах боломжтой байх ёстой."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Энэ апп үүрэн цамхаг, Wi-Fi сүлжээ зэрэг сүлжээний байршлын эх сурвалжийг ашиглан таны байршлыг мэдэх боломжтой. Эдгээр байршлын үйлчилгээ нь таны ТВ-д асаалттай байх ёстой ба апп ашиглах боломжтой байх ёстой."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Энэ апп үүрэн цамхаг, Wi-Fi сүлжээ зэрэг сүлжээний байршлын эх сурвалжийг ашиглан таны байршлыг мэдэх боломжтой. Эдгээр байршлын үйлчилгээ нь таны утсанд асаалттай байх ёстой ба апп ашиглах боломжтой байх ёстой."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"нарийвчилсан байршилд арын дэвсгэрт хандах"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Энэ апп нь ард ажиллах үедээ таны байршлыг ямар ч үед нарийн тогтоох боломжтой. Апп эдгээр байршлын үйлчилгээг ашиглахын тулд эдгээрийг таны утсан дээр асааж идэвхтэй байлгах шаардлагатай. Энэ нь батарейны хэрэглээг ихэсгэж болзошгүй."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"Аудио тохиргоо солих"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Апп нь дууны хэмжээ, спикерын гаралтад ашиглагдах глобал аудио тохиргоог өөрчлөх боломжтой."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"аудио бичих"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Хурууны хээний дүрс"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"царай танилтын техник хангамжийг удирдах"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Аппад царайны загварыг ашиглахын тулд нэмэх эсвэл устгах аргыг идэвхжүүлэхийг зөвшөөрдөг."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"царай танилтын техник хангамжийг ашиглах"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Аппад царай танилтын техник хангамжийг баталгаажуулалтад ашиглахыг зөвшөөрдөг"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Царайг таньж чадсангүй. Дахин оролдоно уу."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Царай хэт цайвар байна. Гэрэл багатай газар оролдож үзнэ үү."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Царай хэт бараан байна. Гэрэлтэй газар туршиж үзнэ үү."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Мэдрэгчийг царайнаас холдуулна уу."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Mэдрэгчийг царайтай ойртуулна уу."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Мэдрэгчийг дээшлүүлнэ үү."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Mэдрэгчийг доошлуулна уу."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Мэдрэгчийг баруун тийш болгоно уу."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Мэдрэгчийг зүүн тийш болгоно уу."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Мэдрэгч рүү харна уу."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Царай олдсонгүй."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Царайг төхөөрөмжийн урд талд хөдөлгөөнгүй байлгана уу."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Царайны техник хангамж боломжгүй байна."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Царай таниулах хугацаа дууслаа. Дахин оролдоно уу."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Царайг хадгалах боломжгүй байна."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Царайны үйл ажиллагааг цуцаллаа."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Хэт олон удаа оролдлоо. Дараа дахин оролдоно уу."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Хэт олон удаа оролдлоо. Царай танилтыг идэвхгүй болголоо."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Дахин оролдоно уу."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Бүртгүүлсэн царай алга."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Энэ төхөөрөмжид царай таних мэдрэгч алга"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Царай <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Царайны дүрс тэмдэг"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"синк тохиргоог унших"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Апп нь бүртгэлийн синк тохиргоог унших боломжтой. Жишээ нь энэ нь Хүмүүс апп бүртгэлтэй синк хийгдсэн эсэхийг тодорхойлох боломжтой."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"синкийг унтрааж асаах тохиргоо"</string>
@@ -729,7 +762,7 @@
     <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Тайлах PIN-г оруулна уу"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Буруу PIN код."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Тайлах бол Цэсийг дараад 0."</string>
-    <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Яаралтай дугаар"</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_instructions_when_pattern_enabled" msgid="46154051614126049">"Яаралтай дуудлага хийх буюу эсвэл түгжээг тайлах бол цэсийг дарна уу."</string>
@@ -1128,7 +1161,7 @@
     <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>
     <string name="volume_ringtone" msgid="6885421406845734650">"Хонхны аяны хэмжээ"</string>
-    <string name="volume_music" msgid="5421651157138628171">"Медиа дууны хэмжээ"</string>
+    <string name="volume_music" msgid="5421651157138628171">"Медиа дууны түвшин"</string>
     <string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Блютүүтээр тоглож байна"</string>
     <string name="volume_music_hint_silent_ringtone_selected" msgid="8310739960973156272">"Хонхны дууг чимээгүй болгов"</string>
     <string name="volume_call" msgid="3941680041282788711">"Ирсэн дуудлагын дууны хэмжээ"</string>
@@ -1139,7 +1172,7 @@
     <string name="volume_icon_description_bluetooth" msgid="6538894177255964340">"Блютүүтын хэмжээ"</string>
     <string name="volume_icon_description_ringer" msgid="3326003847006162496">"Хонхны дууны хэмжээ"</string>
     <string name="volume_icon_description_incall" msgid="8890073218154543397">"Дуудлагын дууны хэмжээ"</string>
-    <string name="volume_icon_description_media" msgid="4217311719665194215">"Медиа дууны хэмжээ"</string>
+    <string name="volume_icon_description_media" msgid="4217311719665194215">"Медиа дууны түвшин"</string>
     <string name="volume_icon_description_notification" msgid="7044986546477282274">"Мэдэгдлийн дууны хэмжээ"</string>
     <string name="ringtone_default" msgid="3789758980357696936">"Үндсэн хонхны ая"</string>
     <string name="ringtone_default_with_actual" msgid="1767304850491060581">"Үндсэн (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi-д интернет хандалт алга"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Сонголт хийхийн тулд товшино уу"</string>
+    <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">"Энэ төхөөрөмж таны \"зөвхөн 5Гц\" гэсэн давуу сонголтыг дэмждэггүй. Үүний оронд энэ төхөөрөмж 5Гц зурвасыг боломжтой үед нь ашиглах болно."</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>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 8c38897..65c8828 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"विमान मोड"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"विमान मोड चालू आहे"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"विमान मोड बंद आहे"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"बॅटरी बचतकर्ता"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"बॅटरी बचतकर्ता बंद आहे"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"बॅटरी बचतकर्ता सुरू आहे"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"सेटिंग्ज"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"सहाय्यता"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"व्हॉइस सहाय्य"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"स्थान"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"या डिव्हाइसच्या स्थानावर प्रवेश"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला या डिव्हाइसचे स्थान अॅक्सेस करू द्यायचे?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"तुम्ही अॅप वापरत असताना, अॅपला फक्त स्थानाचा अॅक्सेस असेल."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"या डिव्हाइसचे स्थान अॅक्सेस करण्याची &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला नेहेमी अनुमती द्यायची का?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"तुम्ही अॅप वापरत नसलात तरीही, अॅपला स्थानाचा नेहेमी अॅक्सेस असेल."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"कॅलेंडर"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"आपल्या कॅलेंडरवर प्रवेश"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला तुमचे कॅलेंडर अॅक्सेस करू द्यायचे?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"हा अॅप आपल्या फोनवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"अतिरिक्त स्थान प्रदाता आदेश अॅक्सेस करा"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"अ‍ॅपला अतिरिक्त स्‍थान प्रदाता आदेशावर प्रवेश करण्‍याची अनुमती देते. हे कदाचित अ‍ॅपला GPS किंवा इतर स्‍थान स्त्रोत च्या ऑपरेशनमध्‍ये हस्तक्षेप करण्‍याची अनुमती देऊ शकते."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"अचूक स्थानामध्ये (GPS आणि नेटवर्क-आधारित) अॅक्सेस करा"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"हा अॅप सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या GPS किंवा नेटवर्क स्रोतांच्या आधारावर तुमचे स्थान मिळवू शकतो. या स्थान सेवा वापरण्यास सक्षम असण्यासाठी त्या तुमच्या फोनवर चालू केलेल्या आणि उपलब्ध असणे आवश्यक आहे. हे बॅटरी वापर वाढवू शकते."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"फक्त फोरग्राउंडमध्ये अचूकपणे अ‍ॅक्सेस करा"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"हे अ‍ॅप फक्त फोरग्राउंडमध्ये असतानाच तुमचे अचूक स्थान मिळवू शकते. या स्थान सेवा सुरू करणे आणि त्या वापरण्यासाठी अ‍ॅपसाठी तुमच्या फोनवर उपलब्ध करणे आवश्यक आहे, यामुळे बॅटरी वापर वाढू शकतो."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"अंदाजे स्‍थानामध्ये (नेटवर्क-आधारित) अॅक्सेस करा"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"हा अॅप सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतांच्या आधारावर तुमचे स्थान मिळवू शकतो. अॅपला या स्थान सेवा वापरण्यास सक्षम असण्यासाठी तुमच्या टॅब्लेटवर त्या चालू केलेल्या आणि उपलब्ध असणे आवश्यक आहे."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"हा अॅप सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतांच्या आधारावर तुमचे स्थान मिळवू शकतो. अॅपला या स्थान सेवा वापरण्यास सक्षम असण्यासाठी तुमच्या टीव्हीवर त्या चालू केलेल्या आणि उपलब्ध असणे आवश्यक आहे."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"हा अॅप सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतांच्या आधारावर तुमचे स्थान मिळवू शकतो. अॅपला या स्थान सेवा वापरण्यास सक्षम असण्यासाठी तुमच्या फोनवर त्या चालू केलेल्या आणि उपलब्ध असणे आवश्यक आहे."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"बॅकग्राउंडमध्ये अचूक स्थान अ‍ॅक्सेस करा"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"हे अ‍ॅप बॅकग्राउंडमध्ये असताना अचूक स्थान कधीही मिळवू शकते. या स्थान सेवा सुरू करणे आणि त्या वापरण्यासाठी अ‍ॅपसाठी तुमच्या फोनवर उपलब्ध करणे आवश्यक आहे, यामुळे बॅटरी वापर वाढू शकतो."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"आपल्या ऑडिओ सेटिंग्ज बदला"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"व्हॉल्यूम आणि आउटपुटसाठी कोणता स्पीकर वापरला आहे यासारख्या समग्र ऑडिओ सेटिंग्ज सुधारित करण्यासाठी अॅप ला अनुमती देते."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ऑडिओ रेकॉर्ड"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"फिंगरप्रिंट आयकन"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"चेहरा ऑथेंटिकेशन हार्डवेअर व्यवस्थापित करा"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"अॅपला वापरासाठी चेहरा टेम्पलेट जोडण्याच्या आणि हटवण्याच्या पद्धती जारी करू देते."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"चेहरा ऑथेंटिकेशन हार्डवेअर वापरा"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"अॅपला चेहरा ऑथेंटिकेशनसाठी ऑथेंटिकेशन हार्डवेअर वापरू देते"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"चेहऱ्यावर प्रक्रिया झाली नाही. पुन्हा प्रयत्न करा."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"चेहऱ्यावर खूप प्रकाश आहे. कृपया कमी प्रकाशात प्रयत्न करा."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"चेहऱ्यावर खूप अंधार आहे. कृपया प्रकाश स्रोत खुला करा."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"कृपया सेन्सर चेहऱ्यापासून आणखी दूर हलवा."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"कृपया सेन्सर चेहऱ्याच्या आणखी जवळ आणा."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"कृपया सेन्सर आणखी वर हलवा."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"कृपया सेन्सर आणखी खाली हलवा."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"कृपया सेन्सर उजवीकडे हलवा."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"कृपया सेन्सर डावीकडे हलवा."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"कृपया सेन्सरकडे पहा."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"चेहरा आढळला नाही."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"डिव्हाइसच्या समोर चेहरा स्थिर ठेवा."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"चेहरा हार्डवेअर उपलब्ध नाही."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"चेहरा टाइमआउट झाला. पुन्हा प्रयत्न करा."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"चेहरा स्टोअर केला जाऊ शकत नाही."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"चेहरा ऑपरेशन रद्द केले गेले."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"खूप जास्त प्रयत्न केले. नंतर पुन्हा प्रयत्न करा."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"खूप जास्त प्रयत्न केले. चेहरा ऑथेंटिकेशन बंद केले गेले."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"पुन्हा प्रयत्न करा."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"चेहरा नोंदवलेला नाही."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"या डिव्हाइसमध्ये चेहरा ऑथेंटिकेशन सेन्सर नाही"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"चेहरा <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"चेहरा आयकन"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"सिंक सेटिंग्‍ज वाचा"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"खात्याच्या सिंक सेटिंग्ज वाचण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांचा अॅप संकालित केला आहे किंवा नाही हे निर्धारित करू शकते."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"सिंक चालू आणि बंद करा टॉगल करा"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"वाय-फाय ला इंटरनेटचा अॅक्सेस नाही"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"पर्यायांसाठी टॅप करा"</string>
+    <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">"हे डिव्हाइस तुमच्या फक्त ५GHz साठी प्राधान्याला सपोर्ट करत नाही. त्याऐवजी, हे डिव्हाइस ५GHz बँड उपलब्ध असताना वापरेल."</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>
@@ -1806,7 +1842,7 @@
     <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="conference_call" msgid="3751093130790472426">"परिषद कॉल"</string>
+    <string name="conference_call" msgid="3751093130790472426">"कॉन्फरन्स कॉल"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"टूलटिप"</string>
     <string name="app_category_game" msgid="5431836943981492993">"गेम"</string>
     <string name="app_category_audio" msgid="1659853108734301647">"संगीत आणि ऑडिओ"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index f9bdce7..b2f10c1 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mod pesawat"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Mod Pesawat DIHIDUPKAN"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Mod Pesawat DIMATIKAN"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Penjimat bateri"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Penjimat bateri DIMATIKAN"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Penjimat Bateri DIHIDUPKAN"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Tetapan"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Bantu"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Bantuan Suara"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Lokasi"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"mengakses lokasi peranti ini"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi peranti ini?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Apl ini hanya dapat mengakses lokasi semasa anda menggunakan apl tersebut."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Sentiasa benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi peranti ini?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Apl ini akan dapat mengakses lokasi pada sepanjang masa, meskipun apabila anda tidak menggunakan apl tersebut."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendar"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"mengakses kalendar"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses kalendar anda?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Apl ini boleh menambah, mengalih keluar atau mengubah acara kalendar pada telefon anda. Apl ini boleh menghantar mesej yang mungkin kelihatan seperti dihantar oleh pemilik kalendar atau mengubah acara tanpa memaklumi pemilik acara itu."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"akses perintah tambahan pembekal lokasi"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Membenarkan apl mengakses arahan pembekal lokasi tambahan. Ini boleh membenarkan apl untuk campur tangan dengan operasi GPS atau sumber lokasi lain."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"akses lokasi tepat (GPS dan berasaskan rangkaian)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Apl ini boleh mendapatkan lokasi anda berdasarkan sumber rangkaian seperti menara selular dan rangkaian Wi-Fi. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada telefon anda untuk membolehkan apl menggunakan perkhidmatan tersebut. Hal ini mungkin meningkatkan penggunaan bateri."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"akses lokasi tepat hanya di latar depan"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Apl ini boleh mendapatkan lokasi tepat anda hanya apabila apl tersebut berada di latar depan. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada telefon anda untuk membolehkan apl menggunakan perkhidmatan tersebut. Tindakan ini mungkin meningkatkan penggunaan bateri."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"akses lokasi anggaran (berasaskan rangkaian)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Apl ini boleh mendapatkan lokasi anda berdasarkan sumber rangkaian seperti menara selular dan rangkaian Wi-Fi. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada tablet anda untuk membolehkan apl menggunakan perkhidmatan tersebut."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Apl ini boleh mendapatkan lokasi anda berdasarkan sumber rangkaian seperti menara selular dan rangkaian Wi-Fi. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada TV anda untuk membolehkan apl menggunakan perkhidmatan tersebut."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Apl ini boleh mendapatkan lokasi anda berdasarkan sumber rangkaian seperti menara selular dan rangkaian Wi-Fi. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada telefon anda untuk membolehkan apl menggunakan perkhidmatan tersebut."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"akses lokasi tepat di latar belakang"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Apl ini boleh mendapatkan lokasi tepat anda setiap kali apl tersebut berada di latar belakang. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada telefon anda untuk membolehkan apl menggunakan perkhidmatan tersebut. Tindakan ini mungkin meningkatkan penggunaan bateri."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"tukar tetapan audio anda"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Membenarkan apl untuk mengubah suai tetapan audio global seperti kelantangan dan pembesar suara mana digunakan untuk output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"rakam audio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon cap jari"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"urus perkakasan pengesahan wajah"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Membenarkan apl menggunakan kaedah untuk menambahkan dan memadamkan templat wajah untuk digunakan."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"gunakan perkakasan pengesahan wajah"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Membenarkan apl menggunakan perkakasan pengesahan wajah untuk pengesahan"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Tidak dapat memproses wajah. Sila cuba lagi."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Wajah terlalu cerah. Cuba dlm cahaya lebih rendah."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Wajah terlalu gelap. Sila buka sumber cahaya."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Sila alihkan penderia lebih jauh dari wajah."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Sila dekatkan penderia ke wajah."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Sila alihkan penderia ke kedudukan lebih tinggi."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Sila alihkan penderia ke kedudukan lebih rendah."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Sila alihkan penderia ke kanan."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Sila alihkan penderia ke kiri."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Sila lihat penderia."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Tiada wajah dikesan."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Pastikan wajah tidak bergerak di hadapan peranti."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Perkakasan wajah tidak tersedia."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Tamat masa wajah dicapai. Cuba lagi."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Wajah tidak dapat disimpan."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Pengendalian wajah dibatalkan."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Terlalu banyak percubaan. Cuba sebentar lagi."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Terlalu banyak percubaan. Pengesahan wajah dilumpuhkan."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Cuba lagi."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Tiada wajah didaftarkan."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Peranti ini tiada penderia pengesahan wajah"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Wajah <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ikon wajah"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"membaca tetapan penyegerakan"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Membenarkan apl membaca tetapan segerak untuk akaun. Sebagai contoh, ini boleh menentukan sama ada apl Orang disegerakkan dengan akaun."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"togol segerak hidup dan mati"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi tiada akses Internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Ketik untuk mendapatkan pilihan"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Perubahan kepada tetapan tempat liputan anda"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Jalur tempat liputan anda telah berubah."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Peranti ini tidak menyokong pilihan anda untuk 5GHz sahaja. Sebaliknya, peranti ini akan menggunakan jalur 5GHz apabila tersedia."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Beralih kepada <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Peranti menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> apabila <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tiada akses Internet. Bayaran mungkin dikenakan."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Beralih daripada <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kepada <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 2b8db67..f311ab3 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"လေယာဥ်ပျံပေါ်အသုံးပြုသောစနစ်"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"လေယဥ်ပျံပေါ်၌အသုံးပြုသောစနစ်ဖွင့်ထားသည်"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"လေယဥ်ပျံပေါ်၌အသုံးပြုသောစနစ်ပိတ်ထားသည်"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ဘက်ထရီ ချွေတာမှုစနစ်"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ဘက်ထရီချွေတာခြင်းမုဒ်ကို ပိတ်ထားသည်"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ဘက်ထရီချွေတာခြင်းမုဒ်ကို ဖွင့်ထားသည်"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"ဆက်တင်များ"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"အကူအညီ"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"အသံ အကူအညီ"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"တည်နေရာ"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"ဤစက်ပစ္စည်း၏ တည်နေရာကို ရယူရန်"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား ဤစက်ပစ္စည်း၏တည်နေရာကို သုံးခွင့်ပေးလိုပါသလား။"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"အက်ပ်ကိုအသုံးပြုသည့် အချိန်တွင်သာ ၎င်းကတည်နေရာကို အသုံးပြုခွင့်ရပါမည်။"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား ဤစက်ပစ္စည်း၏တည်နေရာကို အမြဲ အသုံးပြုခွင့်ပေးလိုပါသလား။"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"သင် အက်ပ်ကိုအသုံးပြုမနေသော်လည်း ၎င်းကတည်နေရာကို အမြဲ အသုံးပြုခွင့်ရနေပါမည်။"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"ပြက္ခဒိန်"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"သင့်ပြက္ခဒိန်အား ဝင်ရောက်သုံးရန်"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား သင်၏ပြက္ခဒိန်ကို သုံးခွင့်ပေးလိုပါသလား။"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ဤအက်ပ်သည် သင့်ဖုန်းပေါ်ရှိ ပြက္ခဒိန်ဖြစ်ရပ်များကို ထည့်ခြင်း၊ ဖယ်ရှားခြင်း သို့မဟုတ် ပြောင်းလဲခြင်းတို့ ပြုလုပ်နိုင်ပါသည်။ ဤအက်ပ်သည် ပြက္ခဒိန်ပိုင်ရှင်များမှ လာခြင်းဖြစ်နိုင်သည့် မက်ဆေ့ဂျ်များကို ပို့နိုင်ပြီး ပိုင်ရှင်များကို အသိမပေးဘဲ ဖြစ်ရပ်များကို ပြောင်းလဲနိုင်ပါသည်။"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"တည်နေရာပံ့ပိုးမှုညွှန်ကြားချက်အပိုအား ဝင်ရောက်ကြည့်ခြင်း"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"အက်ပ်အား တည်နေရာ စီမံပေးရေး ညွှန်ကြားချက် အပိုများကို ရယူခွင့်ပြုသည်။ သို့ဖြစ်၍ အက်ပ်သည် GPS သို့မဟုတ် အခြား တည်နေရာ ရင်းမြစ်ကို သုံးကြသူတို့၏ လုပ်ငန်းများကို ဝင်စွက်ခွင့် ပြုနိုင်သည်။"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"တိကျတဲ့ တည်နေရာ (GPS နှင့် ကွန်ရက် အခြေခံ)ကို ရယူသုံးရန်"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"ဤအက်ပ်သည် ဆဲလ်တာဝါများနှင့် Wi-Fi ကွန်ရက်များကဲ့သို့ GPS သို့မဟုတ် ကွန်ရက်တည်နေရာ အရင်းအမြစ်ပေါ် အခြေခံပြီး သင့်တည်နေရာအချက်အလက်ကို ရယူနိုင်ပါသည်။ အက်ပ်က အသုံးပြုနိုင်ရန်အတွက် ဤတည်နေရာ ဝန်ဆောင်မှုများကို ဖွင့်ထားရမည် ဖြစ်ပြီး သင့်ဖုန်းပေါ်တွင် ရရှိနိုင်ရပါမည်။ ၎င်းသည် ဘက်ထရီကို ပိုမိုအသုံးပြုနိုင်ပါသည်။"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"မျက်နှာစာတွင်သာ တည်နေရာအတိအကျ အသုံးပြုခြင်း"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"မျက်နှာစာတွင် ဖွင့်ထားမှသာ ဤအက်ပ်က သင်၏တည်နေရာအတိအကျကို ရယူနိုင်ပါသည်။ သင်၏ဖုန်းတွင် အက်ပ်ကအသုံးပြုရန်အတွက် ဤတည်နေရာဝန်ဆောင်မှုများကို ဖွင့်ထားပြီး အသုံးပြု၍ ရပါမည်။ ၎င်းက ဘက်ထရီ ပိုကုန်နိုင်ပါသည်။"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"အနီးစပ်ဆုံး တည်နေရာ (ကွန်ရက် အခြေခံ)ကို ရယူသုံးရန်"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ဤအက်ပ်သည် ဆဲလ်တာဝါများနှင့် Wi-Fi ကွန်ရက်များကဲ့သို့ ကွန်ရက်အရင်းအမြစ်ပေါ် အခြေခံပြီး သင့်တည်နေရာအချက်အလက်ကို ရယူနိုင်ပါသည်။ အက်ပ်က အသုံးပြုနိုင်ရန်အတွက် ဤတည်နေရာ ဝန်ဆောင်မှုများကို ဖွင့်ထားရမည် ဖြစ်ပြီး သင့်တက်ဘလက်ပေါ်တွင် ရရှိနိုင်ရပါမည်။"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ဤအက်ပ်သည် ဆဲလ်တာဝါများနှင့် Wi-Fi ကွန်ရက်များကဲ့သို့ ကွန်ရက်အရင်းအမြစ်ပေါ် အခြေခံပြီး သင့်တည်နေရာအချက်အလက်ကို ရယူနိုင်ပါသည်။ အက်ပ်က အသုံးပြုနိုင်ရန်အတွက် ဤတည်နေရာ ဝန်ဆောင်မှုများကို ဖွင့်ထားရမည် ဖြစ်ပြီး သင့်တီဗီပေါ်တွင် ရရှိနိုင်ရပါမည်။"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ဤအက်ပ်သည် ဆဲလ်တာဝါများနှင့် Wi-Fi ကွန်ရက်များကဲ့သို့ ကွန်ရက်အရင်းအမြစ်ပေါ် အခြေခံပြီး သင့်တည်နေရာအချက်အလက်ကို ရယူနိုင်ပါသည်။ အက်ပ်က အသုံးပြုနိုင်ရန်အတွက် ဤတည်နေရာ ဝန်ဆောင်မှုများကို ဖွင့်ထားရမည် ဖြစ်ပြီး သင့်ဖုန်းပေါ်တွင် ရရှိနိုင်ရပါမည်။"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"နောက်ခံတွင် တည်နေရာအတိအကျ အသုံးပြုခြင်း"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"နောက်ခံတွင် ဖွင့်ထားသည့်အခါတိုင်း ဤအက်ပ်က သင်၏တည်နေရာအတိအကျကို ရယူနိုင်ပါသည်။ သင်၏ဖုန်းတွင် အက်ပ်ကအသုံးပြုရန်အတွက် ဤတည်နေရာဝန်ဆောင်မှုများကို ဖွင့်ထားပြီး အသုံးပြု၍ ရပါမည်။ ၎င်းက ဘက်ထရီ ပိုကုန်နိုင်ပါသည်။"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"သင့်အသံအပြင်အဆင်အားပြောင်းခြင်း"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"အပလီကေးရှင်းအား အသံအတိုးအကျယ်နှင့် အထွက်ကို မည်သည့်စပီကာကို သုံးရန်စသည်ဖြင့် စက်တစ်ခုလုံးနှင့်ဆိုင်သော အသံဆိုင်ရာ ဆက်တင်များ ပြင်ဆင်ခွင့် ပြုရန်"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"အသံဖမ်းခြင်း"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"လက်ဗွေ အိုင်ကွန်"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"မျက်နှာအထောက်အထားစိစစ်ခြင်း စက်ပစ္စည်းကို စီမံပါ"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"အသုံးပြုရန်အတွက် မျက်နှာပုံစံထည့်ရန် (သို့) ဖျက်ရန်နည်းလမ်းကို အက်ပ်အား သုံးခွင့်ပြုသည်။"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"မျက်နှာအထောက်အထားစိစစ်ခြင်း စက်ပစ္စည်းကို သုံးပါ"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"အထောက်အထားစိစစ်ရန်အတွက် ဤအက်ပ်အား မျက်နှာအထောက်အထားစိစစ်ခြင်း စက်ပစ္စည်းကိုသုံးခွင့်ပြုသည်"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"မျက်နှာကို မမှတ်မိပါ။ ထပ်လုပ်ကြည့်ပါ။"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"မျက်နှာအလွန်လင်းနေသည်။ အလင်းလျှော့ပြီး စမ်းကြည့်ပါ။"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"မျက်နှာအလွန်မှောင်နေသည်။ ပြတင်းပေါက်ဖွင့်လိုက်ပါ။"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"အာရုံခံကိရိယာကို မျက်နှာနှင့် ခွာလိုက်ပါ။"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"အာရုံခံကိရိယာကို မျက်နှာအနီး ရွှေ့လိုက်ပါ။"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"အာရုံခံကိရိယာကို ပိုမြင့်အောင်ထားပါ။"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"အာရုံခံကိရိယာကို ပိုနိမ့်အောင်ထားပါ။"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"အာရုံခံကိရိယာကို ညာဘက်သို့ ရွှေ့ပါ။"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"အာရုံခံကိရိယာကို ဘယ်ဘက်သို့ ရွှေ့ပါ။"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"အာရုံခံကိရိယာကို ကြည့်ပါ။"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"မျက်နှာတစ်ခုမျှ မတွေ့ပါ။"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"မျက်နှာကို စက်၏ရှေ့တွင် အဆင်သင့်ထားပါ။"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"မျက်နှာ စက်ပစ္စည်း မရနိုင်ပါ။"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"မျက်နှာ သက်တမ်းကုန်သွားပါပြီ။ ထပ်စမ်းကြည့်ပါ။"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"မျက်နှာကို သိမ်း၍မရပါ။"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"မျက်နှာ ဆောင်ရွက်ခြင်းကို ပယ်ဖျက်လိုက်ပါပြီ။"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"အကြိမ်များစွာ စမ်းပြီးပါပြီ။ နောက်မှထပ်စမ်းပါ။"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"အကြိမ်များစွာ စမ်းပြီးပါပြီ။ ပိတ်လိုက်ပါပြီ။"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"ထပ်စမ်းကြည့်ပါ။"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"စာရင်းသွင်းထားသည့် မျက်နှာတစ်ခုမျှ မရှိပါ။"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"ဤစက်တွင် မျက်နှာအထောက်အထားစိစစ်ခြင်း အာရုံခံကိရိယာမရှိပါ"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"မျက်နှာ <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"မျက်နှာသင်္ကေတ"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ထပ်တူပြုအဆင်အပြင်အားဖတ်ခြင်း"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"အပလီကေးရှင်းအား အကောင့်တစ်ခုအတွက် ထပ်တူညီအောင် လုပ်ဆောင်မှု ဆက်တင်အား ကြည့်ခွင့် ပြုပါ။ ဥပမာ People အက်ပ်က အကောင့်တစ်ခုနဲ့ ထပ်တူညီအောင် လုပ်ရန် ဆက်သွယ်ထားမှု ရှိမရှိ သိရှိနိုင်ခြင်း"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ထပ်တူညီအောင် လုပ်ခြင်းအား ပြုနိုင်၊ မပြုနိုင် အပြောင်းအလဲလုပ်ခြင်း"</string>
@@ -566,7 +599,7 @@
     <string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM လက်မှတ်များကို ရယူသုံးခြင်း"</string>
     <string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"အပလီကေးရှင်းတစ်ခုအား စီမံလုပ်ကိုင်ခွင့် DRM လက်မှတ်များ သုံးခွင့် ပြုသည်။ သာမန် အက်ပ်များ အတွက် ဘယ်တော့မှ မလိုအပ်နိုင်ပါ။"</string>
     <string name="permlab_handoverStatus" msgid="7820353257219300883">"Android ရဲ့ အလင်းတန်းထိုး လွှဲပြောင်းမှု အခြေအနေကို ရယူရန်"</string>
-    <string name="permdesc_handoverStatus" msgid="4788144087245714948">"ဒီအပလီကေးရှင်းအား အန်ဒရွိုက်၏ လက်ရှိ အလင်းတန်းထိုး လွှဲပြောင်းမှု အကြောင်း အချက်အလက်ကို ရယူခွင့် ပြုသည်"</string>
+    <string name="permdesc_handoverStatus" msgid="4788144087245714948">"ဒီအပလီကေးရှင်းအား Android၏ လက်ရှိ အလင်းတန်းထိုး လွှဲပြောင်းမှု အကြောင်း အချက်အလက်ကို ရယူခွင့် ပြုသည်"</string>
     <string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM လက်မှတ်များ ဖယ်ရှားရန်"</string>
     <string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"အပလီကေးရှင်းအား DRM လက်မှတ်များကို ဖယ်ရှားခွင့် ပြုသည်။  သာမန် အက်ပ်များ အတွက် ဘယ်တော့မှ မလိုအပ်နိုင်ပါ။"</string>
     <string name="permlab_bindCarrierMessagingService" msgid="1490229371796969158">"စာပို့စာယူ ဆက်သွယ်ရေးဝန်ဆောင်မှုတစ်ခုအား ပူးပေါင်းခွင့်ပြုရန်"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi တွင် အင်တာနက်ချိတ်ဆက်မှု မရှိပါ"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"အခြားရွေးချယ်စရာများကိုကြည့်ရန် တို့ပါ"</string>
+    <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">"ဤစက်ပစ္စည်းသည် သင်၏ 5GHz သီးသန့်ရွေးချယ်မှုအတွက် ပံ့ပိုးမထားပါ။ ၎င်းအစား ဤစက်ပစ္စည်းသည် ရနိုင်သည့်အခါ 5GHz လိုင်းကို သုံးသွားပါမည်။"</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>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 635151c..a5bd899 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Flymodus"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Flymodus er på"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flymodus er av"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Batterisparing"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Batterisparing er AV"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Batterisparing er PÅ"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Innstillinger"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Hjelp"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Talehjelp"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Posisjon"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"få tilgang til enhetens plassering"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til denne enhetens posisjon?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Appen får bare tilgang til posisjonen når du bruker appen."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Vil du alltid gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til denne enhetens posisjon?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Appen får alltid tilgang til posisjonen, selv når du ikke bruker appen."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"åpne kalenderen din"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til kalenderen din?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Denne appen kan legge til, fjerne eller endre kalenderaktiviteter på telefonen din. Denne appen kan sende meldinger som kan virke som om kommer fra kalendereiere, eller endre aktiviteter uten at eierne blir varslet."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"bruke ekstra posisjonskommandoer"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Appen gis tillatelse til å bruke ekstra kommandoer fra posisjonsleverandører. Dette kan gi appen tillatelse til å påvirke bruken av GPS eller andre posisjonskilder."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"få tilgang til nøyaktig posisjon (GPS- og nettverksbasert)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Denne appen kan få posisjonen din fra GPS eller nettverksplasseringskilder som mobilmaster og Wi-Fi-nettverk. Disse posisjonstjenestene må være slått på og tilgjengelige på telefonen din for at appen skal kunne bruke dem. Dette kan øke batteriforbruket."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"bare tilgang til nøyaktig posisjon i forgrunnen"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Denne appen kan bare få den nøyaktige posisjonen din når den er på i forgrunnen. Disse posisjonstjenestene må være slått på og tilgjengelige på telefonen din for at appen skal kunne bruke dem. Dette kan øke batteriforbruket."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"få tilgang til omtrentlig posisjon (nettverksbasert)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Denne appen kan få posisjonen din fra nettverkskilder som mobilmaster og Wi-Fi-nettverk. Disse posisjonstjenestene må være slått på og tilgjengelige på nettbrettet ditt for at appen skal kunne bruke dem."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Denne appen kan få posisjonen din fra nettverkskilder som mobilmaster og Wi-Fi-nettverk. Disse posisjonstjenestene må være slått på og tilgjengelige på TV-en din for at appen skal kunne bruke dem."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Denne appen kan få posisjonen din fra nettverkskilder som mobilmaster og Wi-Fi-nettverk. Disse posisjonstjenestene må være slått på og tilgjengelige på telefonen din for at appen skal kunne bruke dem."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"tilgang til nøyaktig posisjon i bakgrunnen"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Denne appen kan få den nøyaktige posisjonen din når den er på i bakgrunnen. Disse posisjonstjenestene må være slått på og tilgjengelige på telefonen din for at appen skal kunne bruke dem. Dette kan øke batteriforbruket."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"endre lydinnstillinger"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Lar appen endre globale lydinnstillinger slik som volum og hvilken høyttaler som brukes for lydavspilling."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ta opp lyd"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon for fingeravtrykk"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"administrere maskinvare for ansiktsautentisering"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Lar appen bruke metoder for å legge til og slette ansiktmaler for bruk."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"bruke maskinvare for ansiktsautentisering"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Lar appen bruke maskinvare for ansiktsautentisering til autentisering"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Kunne ikke behandle ansiktet. Prøv igjen."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Ansiktet er for lyst. Prøv med svakere lys."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Ansiktet er for mørkt. Bruk en lyskilde."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Flytt sensoren lengre vekk fra ansiktet."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Hold sensoren nærmere ansiktet."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Flytt sensoren opp."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Flytt sensoren ned."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Flytt sensoren til høyre."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Flytt sensoren til venstre."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Se på sensoren."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Ingen ansikter er oppdaget."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Hold ansiktet stødig foran enheten."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Maskinvare for ansikt er ikke tilgjengelig."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Tidsavbrudd for ansikt er nådd. Prøv igjen."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Ansiktet kan ikke lagres."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Ansikt-operasjonen ble avbrutt."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"For mange forsøk. Prøv igjen senere."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"For mange forsøk. Ansiktsautentisering er slått av."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Prøv igjen."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Ingen ansikt er registrert."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Denne enheten har ikke sensor for ansiktsautentisering"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Ansikt <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ansiktikon"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lese synkroniseringsinnstillinger"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Lar appen lese synkroniseringsinnstillingene for en konto. For eksempel kan den finne ut om Personer-appen er synkronisert med en konto."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"slå synkronisering av og på"</string>
@@ -584,14 +617,14 @@
     <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"Overvåker antallet feil passord som er skrevet inn når skjermen låses opp, og låser nettbrettet eller sletter denne brukerens data når for mange feil passord er skrevet inn."</string>
     <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Overvåker antallet feil passord som er skrevet inn når skjermen låses opp, og låser TV-en eller sletter denne brukerens data når for mange feil passord er skrevet inn."</string>
     <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Overvåker antallet feil passord som er skrevet inn når skjermen låses opp, og låser telefonen eller sletter denne brukerens data når for mange feil passord er skrevet inn."</string>
-    <string name="policylab_resetPassword" msgid="4934707632423915395">"Endre skjermlåsen"</string>
-    <string name="policydesc_resetPassword" msgid="1278323891710619128">"Endre skjermlåsen."</string>
-    <string name="policylab_forceLock" msgid="2274085384704248431">"Lås skjermen"</string>
-    <string name="policydesc_forceLock" msgid="1141797588403827138">"Kontrollér hvordan og når skjermen låses."</string>
-    <string name="policylab_wipeData" msgid="3910545446758639713">"Slett alle data"</string>
+    <string name="policylab_resetPassword" msgid="4934707632423915395">"Endring av skjermlåsen"</string>
+    <string name="policydesc_resetPassword" msgid="1278323891710619128">"Kan endre skjermlåsen."</string>
+    <string name="policylab_forceLock" msgid="2274085384704248431">"Låsing av skjermen"</string>
+    <string name="policydesc_forceLock" msgid="1141797588403827138">"Kan kontrollere hvordan og når skjermen låses."</string>
+    <string name="policylab_wipeData" msgid="3910545446758639713">"Sletting av alle data"</string>
     <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Tilbakestill nettbrettets data uten advarsel ved å tilbakestille til fabrikkstandard."</string>
     <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Slett TV-ens data uten advarsel ved å tilbakestille til fabrikkstandard."</string>
-    <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Tilbakestill telefonens data uten advarsel, ved å tilbakestille til fabrikkstandard."</string>
+    <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Telefonens data kan slettes uten advarsel ved å tilbakestille til fabrikkstandard."</string>
     <string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Slett brukerdataene"</string>
     <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"Sletter denne brukerens data på dette nettbrettet uten advarsel."</string>
     <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Sletter denne brukerens data på denne TV-en uten advarsel."</string>
@@ -604,7 +637,7 @@
     <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Krev at lagrede appdata krypteres."</string>
     <string name="policylab_disableCamera" msgid="6395301023152297826">"Deaktiver kameraer"</string>
     <string name="policydesc_disableCamera" msgid="2306349042834754597">"Hindre bruk av alle kameraer på enheten."</string>
-    <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Deaktiver enkelte skjermlåsfunksjoner"</string>
+    <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Deaktivering av skjermlåsfunksjoner"</string>
     <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Forhindrer bruk av enkelte skjermlåsfunksjoner."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Hjemmenummer"</item>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi har ikke Internett-tilgang"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Trykk for å få alternativer"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Endres til innstillingene dine for Wi-Fi-soner"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Båndet ditt for Wi-Fi-sone er endret."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Denne enheten støtter ikke innstillingen din for bare 5 GHz. I stedet bruker enheten 5 GHz-båndet når det er tilgjengelig."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Byttet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Enheten bruker <xliff:g id="NEW_NETWORK">%1$s</xliff:g> når <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ikke har Internett-tilgang. Avgifter kan påløpe."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Byttet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 1f0dc48..eac241b 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"हवाइजहाज मोड"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"हवाइजहाज मोड खुला छ"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"हवाइजहाज मोड बन्द छ"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ब्याट्री सेभर"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ब्याट्री सेभर निष्क्रिय छ"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ब्याट्री सेभर सक्रिय छ"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"सेटिङहरू"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"सहायता दिनुहोस्"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"आवाज सहायता"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"स्थान"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"यस यन्त्रको स्थानमाथि पहुँच"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यो यन्त्रको स्थानमाथि पहुँच राख्न दिने हो?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"तपाईं उक्त अनुप्रयोग प्रयोग गरिरहेका बेलामा त्यससँग स्थानमाथिको पहुँच रहने छ।"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यो यन्त्रको स्थानमाथि पहुँच राख्न दिने हो?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"तपाईं उक्त अनुप्रयोग प्रयोग नगरिरहेका बेलामा पनि त्यससँग स्थानमाथिको पहुँच रहने छ।"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"पात्रो"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"तपाईंको पात्रोमाथि पहुँच गर्नुहोस्"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई आफ्नो पात्रोमाथि पहुँच राख्न दिने हो?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"यस अनुप्रयोगले तपाईंको फोनमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस अनुप्रयोगले पात्रोका मालिकहरू मार्फत आएको जस्तो लाग्ने सन्देशहरू पठाउन वा तिनीहरूका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"अधिक स्थान प्रदायक आदेशहरू पहुँच गर्नुहोस्"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"अनुप्रयोगलाई अतिरिक्त स्थान प्रदायक आदेशहरू पहुँच गर्न अनुमति दिन्छ। यो अनुप्रयोगलाई GPS वा अन्य स्थान स्रोतहरूको संचालन साथै हस्तक्षेप गर्न अनुमति दिन सक्छ।"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"सटीक स्थान पहुँच गराउनुहोस् (GPS तथा नेटवर्कमा आधारित)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"यस अनुप्रयोगले सेलका टावर र Wi-Fi नेटवर्कहरू जस्ता GPS वा नेटवर्कको स्थानका स्रोतहरूको आधारमा तपाईंको स्थान बारे जानकारी प्राप्त गर्न सक्छ। यो अनुप्रयोग ती स्रोतहरूको प्रयोग गर्न सक्षम होस् भन्नका खातिर यी स्थान सम्बन्धी सेवाहरूलाई अनिवार्य रूपमा सक्रिय पार्नुपर्छ र यी तपाईंको फोनमा उपलब्ध हुनु पर्छ। यसले ब्याट्रीको खपत बढाउन सक्छ।"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"अग्रभूमिमा मात्र सटीक स्थानमाथि पहुँच राख्नुहोस्"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"यो अनुप्रयोगले अग्रभागमा चलिरहेको अवस्थामा मात्र तपाईंलाई स्थानको सटिक जानकारी दिन सक्छ। यी स्थानसम्बन्धी सेवाहरू अनिवार्य रूपमा सक्रिय गरिएका हुनु पर्छ र अनुप्रयोगले यिनीहरूको प्रयोग गर्न सकोस् भन्नाका खातिर तपाईंको फोनमै उपलब्ध हुन्छन्। यस कार्यले गर्दा ब्याट्री बढी खर्च हुन सक्छ।"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"अनुमानित स्थान पहुँच गराउनुहोस् (नेटवर्कमा आधारित)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"यस अनुप्रयोगले सेलका टावर र Wi-Fi नेटवर्कहरू जस्ता नेटवर्कका स्रोतहरूको आधारमा तपाईंको स्थान बारे जानकारी प्राप्त गर्न सक्छ। यो अनुप्रयोग ती स्रोतहरूको प्रयोग गर्न सक्षम होस् भन्नका खातिर यी स्थान सम्बन्धी सेवाहरूलाई अनिवार्य रूपमा सक्रिय पार्नुपर्छ र यी तपाईंको ट्याब्लेटमा उपलब्ध हुनु पर्छ।"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"यस अनुप्रयोगले सेलका टावर र Wi-Fi नेटवर्कहरू जस्ता नेटवर्कका स्रोतहरूको आधारमा तपाईंको स्थान बारे जानकारी प्राप्त गर्न सक्छ। यो अनुप्रयोग ती स्रोतहरूको प्रयोग गर्न सक्षम होस् भन्नका खातिर यी स्थान सम्बन्धी सेवाहरूलाई अनिवार्य रूपमा सक्रिय पार्नुपर्छ र यी तपाईंको TV मा उपलब्ध हुनु पर्छ।"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"यस अनुप्रयोगले सेलका टावर र Wi-Fi नेटवर्कहरू जस्ता नेटवर्कका स्रोतहरूको आधारमा तपाईंको स्थान बारे जानकारी प्राप्त गर्न सक्छ। यो अनुप्रयोग ती स्रोतहरूको प्रयोग गर्न सक्षम होस् भन्नका खातिर यी स्थान सम्बन्धी सेवाहरूलाई अनिवार्य रूपमा सक्रिय पार्नुपर्छ र यी तपाईंको फोनमा उपलब्ध हुनु पर्छ।"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"पृष्ठभूमिमा सटीक स्थानमाथि पहुँच राख्नुहोस्"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"यो अनुप्रयोगले पृष्ठभूमिमा चलिरहेको अवस्थामा तपाईंलाई जुनसुकै बेला स्थानको सटिक जानकारी दिन सक्छ। यी स्थानसम्बन्धी सेवाहरू अनिवार्य रूपमा सक्रिय गरिएका हुनु पर्छ र अनुप्रयोगले यिनीहरूको प्रयोग गर्न सकोस् भन्नाका खातिर तपाईंको फोनमै उपलब्ध हुन्छन्। यस कार्यले गर्दा ब्याट्री बढी खर्च हुन सक्छ।"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"तपाईँका अडियो सेटिङहरू परिवर्तन गर्नुहोस्"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"अनुप्रयोगलाई ग्लोबल अडियो सेटिङहरू परिमार्जन गर्न अनुमति दिन्छ, जस्तै आवाजको मात्रा र आउटपुटको लागि कुन स्पिकर प्रयोग गर्ने।"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"अडियो रेकर्ड गर्नुहोस्"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"फिंगरप्रिन्ट आइकन"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"अनुहार प्रमाणिकरण हार्डवेयर व्यवस्थापन गर्नुहोस्"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"अनुप्रयोगलाई प्रयोगका लागि अनुहार टेम्प्लेट थप्न र मेटाउने तरिका आह्वान गर्न अनुमति दिन्छ।"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"अनुहार प्रमाणिकरण हार्डवेयर प्रयोग गर्नुहोस्"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"अनुप्रयोगलाई प्रमाणीकरणका लागि अनुहार प्रमाणीकरण हार्डवेयर प्रयोग गर्न अनुमति दिन्छ"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"अनुहार प्रशोधन गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"अनुहारमा धेरै उज्यालो छ। कृपया कम प्रकाशमा प्रयास गर्नुहोस्।"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"अनुहार धेरै गाढा छ। कृपया प्रकाशको स्रोतको थप्नुहोस्‌।"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"कृपया अनुहारबाट सेन्सरलाई अलिक टाढा सार्नुहोस्।"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"कृपया अनुहारलाई सेन्सरको नजिक ल्याउनुहोस्।"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"कृपया सेन्सरलाई माथि सार्नुहोस्।"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"कृपया सेन्सरलाई तल सार्नुहोस्।"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"कृपया सेन्सरलाई दायाँतिर सार्नुहोस्।"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"कृपया सेन्सरलाई बायाँतिर सार्नुहोस्।"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"कृपया सेन्सरमा हेर्नुहोस्।"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"अनुहार पत्ता लागेन।"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"यन्त्रको अगाडि अनुहार स्थिर राख्नुहोस्।"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"अनुहार पहिचान गर्ने हार्डवेयर उपलब्ध छैन।"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"अनुहारको समय सकिएको छ। फेरि प्रयास गर्नुहोस्‌।"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"अनुहार भण्डारण गर्न सकिँदैन।"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"अनुहार पहिचान रद्द गरियो।"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"धेरैपटक प्रयासहरू भए। पछि फेरि प्रयास गर्नुहोस्‌।"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"अत्यधिक धेरैपटक गलत प्रयासहरू भए। अनुहार प्रमाणिकरणलाई असक्षम पारियो।"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"फेरि प्रयास गर्नुहोस्।"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"कुनै पनि अनुहार दर्ता गरिएन।"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"यो यन्त्रमा कुनै अनुहार प्रमाणिकरण सेन्सर छैन"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"अनुहार <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"अनुहारको आइकन"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"समीकरण सेटिङहरू पढ्नुहोस्"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"अनुप्रयोगलाई खाताको लागि सिंक सेटिङहरू पढ्न अनुमति दिन्छ। उदाहरणको लागि यसले व्यक्तिहरको अनुप्रयोग खातासँग सिंक भएको नभएको निर्धारण गर्न सक्दछ।"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"टगल सिंक खुला र बन्द"</string>
@@ -1181,6 +1214,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi मार्फत इन्टरनेटमाथि पहुँच राख्न सकिँदैन"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"विकल्पहरूका लागि ट्याप गर्नुहोस्"</string>
+    <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">"यो यन्त्रले तपाईंको 5GHz मात्रको प्राथमिकतालाई समर्थन गर्दैन। बरु, उपलब्ध भएको खण्डमा यो यन्त्रले 5GHz ब्यान्ड प्रयोग गर्छ।"</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>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
new file mode 100644
index 0000000..688040b
--- /dev/null
+++ b/core/res/res/values-night/colors.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
+
+  NOTE: You might also want to edit: packages/SystemUI/res/values-night/colors.xml
+  -->
+<resources>
+    <!-- The primary text color if the text is on top of a dark background.
+    This is also affects colorized notifications with dark backgrounds. -->
+    <color name="notification_primary_text_color_dark">#dadada</color>
+
+    <!-- The secondary text color if the text is on top of a dark background. -->
+    <color name="notification_secondary_text_color_dark">#dadada</color>
+
+    <color name="notification_default_color_dark">#dadada</color>
+
+    <!-- The background color of a notification card. -->
+    <color name="notification_material_background_color">@*android:color/material_grey_900</color>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
new file mode 100644
index 0000000..5e3675a
--- /dev/null
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -0,0 +1,54 @@
+<?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.
+-->
+
+<!--
+===============================================================
+                        PLEASE READ
+===============================================================
+This file contains the themes that are the Device Defaults.
+If you want to edit themes to skin your device, do it here.
+We recommend that you do not edit themes.xml and instead edit
+this file.
+
+Editing this file instead of themes.xml will greatly simplify
+merges for future platform versions and CTS compliance will be
+easier.
+===============================================================
+                        PLEASE READ
+===============================================================
+ -->
+<resources>
+
+    <!-- The dark default theme for apps that target API level XX and higher.
+         <p>The DeviceDefault themes are aliases for a specific device’s native look and feel. The
+         DeviceDefault theme family and widget style family offer ways for you to target your app
+         to a device’s native theme with all device customizations intact.</p>
+         <p>For example, when you set your app's {@code targetSdkVersion} to XX or higher, this
+         theme is applied to your application by default. As such, your app might appear with the
+         {@link #Theme_Material Material} styles on one device, but with a different set of styles on
+         another device. This is great if you want your app to fit with the device's native look and
+         feel. If, however, you prefer to keep your UI style the same across all devices, you should
+         apply a specific theme such as {@link #Theme_Material Material} or one of your own design.
+         For more information, read <a
+         href="http://android-developers.blogspot.com/20XX/XX/material-everywhere.html">Material
+         Everywhere</a>.</p>
+         <p>Styles used by the DeviceDefault theme are named using the convention
+         Type.DeviceDefault.Etc (for example, {@code Widget.DeviceDefault.Button} and
+         {@code TextAppearance.DeviceDefault.Widget.PopupMenu.Large}).</p>
+          -->
+    <!-- DeviceDefault theme for a window that should look like the Settings app.  -->
+    <style name="Theme.DeviceDefault.Settings" parent="Theme.DeviceDefault"/>
+</resources>
diff --git a/core/res/res/values-night/themes_package_installer.xml b/core/res/res/values-night/themes_package_installer.xml
new file mode 100644
index 0000000..0ad2bdc
--- /dev/null
+++ b/core/res/res/values-night/themes_package_installer.xml
@@ -0,0 +1,35 @@
+<?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-night/values.xml b/core/res/res/values-night/values.xml
new file mode 100644
index 0000000..4eb2ff3
--- /dev/null
+++ b/core/res/res/values-night/values.xml
@@ -0,0 +1,39 @@
+<?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="Theme.DeviceDefault.QuickSettings" parent="android:Theme.DeviceDefault">
+        <!-- Color palette -->
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
+        <item name="colorSecondary">@color/secondary_device_default_settings</item>
+        <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="colorControlNormal">?attr/textColorPrimary</item>
+        <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+        <!-- QS panel background -->
+        <item name="colorBackgroundFloating">@color/material_grey_900</item>
+
+        <!-- volume background -->
+        <item name="panelColorBackground">@color/material_grey_800</item>
+    </style>
+
+    <style name="TextAppearance.Material.Notification">
+        <item name="textColor">@color/notification_secondary_text_color_dark</item>
+        <item name="textSize">@dimen/notification_text_size</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c9cfc7a..99ee6d4 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -34,7 +34,7 @@
     <string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
     <string name="mmiError" msgid="5154499457739052907">"Verbindingsprobleem of ongeldige MMI-code."</string>
     <string name="mmiFdnError" msgid="5224398216385316471">"Bewerking is beperkt tot vaste nummers."</string>
-    <string name="mmiErrorWhileRoaming" msgid="762488890299284230">"Kan instellingen voor doorschakelen van oproepen niet wijzigen vanaf je telefoon tijdens roaming."</string>
+    <string name="mmiErrorWhileRoaming" msgid="762488890299284230">"Kan instellingen voor doorschakelen van gesprekken niet wijzigen vanaf je telefoon tijdens roaming."</string>
     <string name="serviceEnabled" msgid="8147278346414714315">"Service is ingeschakeld."</string>
     <string name="serviceEnabledFor" msgid="6856228140453471041">"Service is ingeschakeld voor:"</string>
     <string name="serviceDisabled" msgid="1937553226592516411">"Service is uitgeschakeld."</string>
@@ -60,21 +60,21 @@
     <string name="ClirMmi" msgid="7784673673446833091">"Uitgaande beller-ID"</string>
     <string name="ColpMmi" msgid="3065121483740183974">"ID van verbonden lijn"</string>
     <string name="ColrMmi" msgid="4996540314421889589">"Beperking voor ID van verbonden lijn"</string>
-    <string name="CfMmi" msgid="5123218989141573515">"Oproep doorschakelen"</string>
+    <string name="CfMmi" msgid="5123218989141573515">"Gesprek doorschakelen"</string>
     <string name="CwMmi" msgid="9129678056795016867">"Wisselgesprek"</string>
-    <string name="BaMmi" msgid="455193067926770581">"Oproep blokkeren"</string>
+    <string name="BaMmi" msgid="455193067926770581">"Gesprek blokkeren"</string>
     <string name="PwdMmi" msgid="7043715687905254199">"Wachtwoordwijziging"</string>
     <string name="PinMmi" msgid="3113117780361190304">"Pin-wijziging"</string>
     <string name="CnipMmi" msgid="3110534680557857162">"Nummer van beller beschikbaar"</string>
     <string name="CnirMmi" msgid="3062102121430548731">"Nummer van beller beperkt"</string>
     <string name="ThreeWCMmi" msgid="9051047170321190368">"Driewegs bellen"</string>
-    <string name="RuacMmi" msgid="7827887459138308886">"Ongewenste, vervelende oproepen weigeren"</string>
+    <string name="RuacMmi" msgid="7827887459138308886">"Ongewenste, vervelende gesprekken weigeren"</string>
     <string name="CndMmi" msgid="3116446237081575808">"Weergave van nummer van beller"</string>
     <string name="DndMmi" msgid="1265478932418334331">"Niet storen"</string>
-    <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Beller-ID standaard ingesteld op \'beperkt\'. Volgende oproep: beperkt."</string>
-    <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Beller-ID standaard ingesteld op \'beperkt\'. Volgende oproep: onbeperkt."</string>
-    <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Beller-ID standaard ingesteld op \'onbeperkt\'. Volgende oproep: beperkt."</string>
-    <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Beller-ID standaard ingesteld op \'onbeperkt\'. Volgende oproep: onbeperkt."</string>
+    <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Beller-ID standaard ingesteld op \'beperkt\'. Volgend gesprek: beperkt."</string>
+    <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Beller-ID standaard ingesteld op \'beperkt\'. Volgend gesprek: onbeperkt."</string>
+    <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Beller-ID standaard ingesteld op \'onbeperkt\'. Volgend gesprek: beperkt."</string>
+    <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Beller-ID standaard ingesteld op \'onbeperkt\'. Volgend gesprek: onbeperkt."</string>
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"Service niet voorzien."</string>
     <string name="CLIRPermanent" msgid="3377371145926835671">"U kunt de instelling voor de beller-ID niet wijzigen."</string>
     <string name="RestrictedOnDataTitle" msgid="5221736429761078014">"Geen service voor mobiele data"</string>
@@ -88,7 +88,7 @@
     <string name="EmergencyCallWarningTitle" msgid="813380189532491336">"Noodoproepen niet beschikbaar"</string>
     <string name="EmergencyCallWarningSummary" msgid="1899692069750260619">"Er kunnen geen noodoproepen worden gemaakt via wifi"</string>
     <string name="notification_channel_network_alert" msgid="4427736684338074967">"Meldingen"</string>
-    <string name="notification_channel_call_forward" msgid="2419697808481833249">"Oproep doorschakelen"</string>
+    <string name="notification_channel_call_forward" msgid="2419697808481833249">"Gesprek doorschakelen"</string>
     <string name="notification_channel_emergency_callback" msgid="6686166232265733921">"Modus voor noodoproepen"</string>
     <string name="notification_channel_mobile_data_status" msgid="4575131690860945836">"Status van mobiele data"</string>
     <string name="notification_channel_sms" msgid="3441746047346135073">"Sms\'jes"</string>
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Vliegtuigmodus"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Vliegtuigmodus is AAN"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Vliegtuigmodus is UIT"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Batterijbesparing"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Batterijbesparing is UIT"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Batterijbesparing is AAN"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Instellingen"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Helpen"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Spraakassistent"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Locatie"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"de locatie van dit apparaat openen"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot de locatie van dit apparaat?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"De app heeft alleen toegang tot de locatie wanneer je de app gebruikt."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; altijd toegang geven tot de locatie van dit apparaat?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"De app heeft altijd toegang tot de locatie, ook wanneer je de app niet gebruikt."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"toegang krijgen tot je agenda"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot je agenda?"</string>
@@ -287,15 +287,15 @@
     <string name="permgrouprequest_storage" msgid="7885942926944299560">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot foto\'s, media en bestanden op je apparaat?"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Microfoon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"audio opnemen"</string>
-    <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; het volgende toestaan: audio opnemen."</string>
+    <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om audio op te nemen?"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"foto\'s maken en video opnemen"</string>
     <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om foto\'s te maken en video op te nemen?"</string>
     <string name="permgrouplab_calllog" msgid="8798646184930388160">"Gesprekkenlijsten"</string>
     <string name="permgroupdesc_calllog" msgid="3006237336748283775">"gesprekkenlijst lezen en schrijven"</string>
-    <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Wil je &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot je gesprekkenlijsten geven?"</string>
+    <string name="permgrouprequest_calllog" msgid="8487355309583773267">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot je gesprekkenlijsten?"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Telefoon"</string>
-    <string name="permgroupdesc_phone" msgid="6234224354060641055">"telefoneren en oproepen beheren"</string>
+    <string name="permgroupdesc_phone" msgid="6234224354060641055">"telefoneren en gesprekken beheren"</string>
     <string name="permgrouprequest_phone" msgid="9166979577750581037">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om telefoongesprekken te starten en te beheren?"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Lichaamssensoren"</string>
     <string name="permgroupdesc_sensors" msgid="7147968539346634043">"toegang krijgen tot sensorgegevens over je vitale functies"</string>
@@ -322,8 +322,8 @@
     <string name="permdesc_install_shortcut" msgid="8341295916286736996">"Een app toestaan snelkoppelingen aan het startscherm toe te voegen zonder tussenkomst van de gebruiker."</string>
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"snelkoppelingen verwijderen"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"De app toestaan snelkoppelingen van het startscherm te verwijderen zonder tussenkomst van de gebruiker."</string>
-    <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"uitgaande oproepen doorschakelen"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"De app toestaan het nummer te bekijken dat wordt gekozen voor een uitgaande oproep, met de mogelijkheid de oproep om te leiden naar een ander nummer of de oproep helemaal af te breken."</string>
+    <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"uitgaande gesprekken doorschakelen"</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"De app toestaan het nummer te bekijken dat wordt gekozen voor een uitgaand gesprek, met de mogelijkheid het gesprek om te leiden naar een ander nummer of het gesprek helemaal af te breken."</string>
     <string name="permlab_answerPhoneCalls" msgid="4077162841226223337">"telefoonoproepen beantwoorden"</string>
     <string name="permdesc_answerPhoneCalls" msgid="2901889867993572266">"Hiermee kan de app een inkomende telefoonoproep beantwoorden."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"tekstberichten (SMS) ontvangen"</string>
@@ -385,11 +385,11 @@
     <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Hiermee kan de app gegevens wijzigen over de contacten die zijn opgeslagen op je tv, inclusief de frequentie waarmee je hebt gebeld, gemaild of op andere manieren hebt gecommuniceerd met specifieke contacten. Met deze toestemming kunnen apps contactgegevens verwijderen."</string>
     <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Hiermee kan de app gegevens wijzigen over de contacten die zijn opgeslagen op je telefoon, inclusief de frequentie waarmee je hebt gebeld, gemaild of op andere manieren hebt gecommuniceerd met specifieke contacten. Met deze toestemming kunnen apps contactgegevens verwijderen."</string>
     <string name="permlab_readCallLog" msgid="3478133184624102739">"gesprekkenlijst lezen"</string>
-    <string name="permdesc_readCallLog" msgid="3204122446463552146">"Deze app kan je oproepgeschiedenis lezen."</string>
+    <string name="permdesc_readCallLog" msgid="3204122446463552146">"Deze app kan je gespreksgeschiedenis lezen."</string>
     <string name="permlab_writeCallLog" msgid="8552045664743499354">"gesprekkenlijst schrijven"</string>
-    <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Toestaan dat de app het gesprekkenlijst van je tablet aanpast, waaronder gegevens over inkomende en uitgaande oproepen. Schadelijke apps kunnen hiermee je gesprekkenlijst wissen of aanpassen."</string>
-    <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Toestaan dat de app het gesprekkenlijst van je tv aanpast, waaronder gegevens over inkomende en uitgaande oproepen. Schadelijke apps kunnen hiermee je gesprekkenlijst wissen of aanpassen."</string>
-    <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Toestaan dat de app het gesprekkenlijst van je telefoon aanpast, waaronder gegevens over inkomende en uitgaande oproepen. Schadelijke apps kunnen hiermee je gesprekkenlijst wissen of aanpassen."</string>
+    <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Toestaan dat de app de gesprekkenlijst van je tablet aanpast, waaronder gegevens over inkomende en uitgaande gesprekken. Schadelijke apps kunnen hiermee je gesprekkenlijst wissen of aanpassen."</string>
+    <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Toestaan dat de app de gesprekkenlijst van je tv aanpast, waaronder gegevens over inkomende en uitgaande gesprekken. Schadelijke apps kunnen hiermee je gesprekkenlijst wissen of aanpassen."</string>
+    <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Toestaan dat de app de gesprekkenlijst van je telefoon aanpast, waaronder gegevens over inkomende en uitgaande gesprekken. Schadelijke apps kunnen hiermee je gesprekkenlijst wissen of aanpassen."</string>
     <string name="permlab_bodySensors" msgid="4683341291818520277">"toegang tot lichaamssensoren (zoals hartslagmeters)"</string>
     <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Hiermee kan de app toegang krijgen tot gegevens van sensoren die je lichamelijke conditie controleren, zoals je hartslag."</string>
     <string name="permlab_readCalendar" msgid="6716116972752441641">"Agenda-afspraken en -gegevens lezen"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Deze app kan agenda-afspraken toevoegen, verwijderen of wijzigen op je telefoon. Deze app kan berichten verzenden die afkomstig lijken te zijn van agenda-eigenaren of afspraken aanpassen zonder dit aan de eigenaar te melden."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"toegang tot extra opdrachten van locatieaanbieder"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Hiermee kan de app toegang krijgen tot extra opdrachten voor de locatieprovider. De app kan hiermee de werking van gps of andere locatiebronnen te verstoren."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"toegang tot precieze locatie (gps- en netwerkgebaseerd)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Deze app kan je locatie ophalen op basis van gps- of netwerklocatiebronnen zoals zendmasten en wifi-netwerken. De app kan alleen gebruikmaken van deze locatieservices als ze zijn ingeschakeld en beschikbaar zijn op je telefoon. Het batterijverbruik kan hierdoor toenemen."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"alleen toegang tot precieze locatie op de voorgrond"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Deze app kan je exacte locatie ophalen wanneer de app op de voorgrond wordt uitgevoerd. De app kan alleen gebruikmaken van deze locatieservices als ze zijn ingeschakeld en beschikbaar zijn op je telefoon. Hierdoor kan het batterijverbruik toenemen."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"toegang tot geschatte locatie (netwerkgebaseerd)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Deze app kan je locatie ophalen op basis van netwerkbronnen zoals zendmasten en wifi-netwerken. De app kan alleen gebruikmaken van deze locatieservices als ze zijn ingeschakeld en beschikbaar zijn op je tablet."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Deze app kan je locatie ophalen op basis van netwerkbronnen zoals zendmasten en wifi-netwerken. De app kan alleen gebruikmaken van deze locatieservices als ze zijn ingeschakeld en beschikbaar zijn op je tv."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Deze app kan je locatie ophalen op basis van netwerkbronnen zoals zendmasten en wifi-netwerken. De app kan alleen gebruikmaken van deze locatieservices als ze zijn ingeschakeld en beschikbaar zijn op je telefoon."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"toegang tot precieze locatie op de achtergrond"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Deze app kan je exacte locatie ophalen wanneer de app op de achtergrond wordt uitgevoerd. De app kan alleen gebruikmaken van deze locatieservices als ze zijn ingeschakeld en beschikbaar zijn op je telefoon. Hierdoor kan het batterijverbruik toenemen."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"je audio-instellingen wijzigen"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Hiermee kan de app algemene audio-instellingen wijzigen zoals het volume en welke luidspreker wordt gebruikt voor de uitvoer."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"audio opnemen"</string>
@@ -419,13 +421,13 @@
     <string name="permlab_vibrate" msgid="7696427026057705834">"trilling beheren"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Hiermee kan de app de trilstand beheren."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"telefoonnummers rechtstreeks bellen"</string>
-    <string name="permdesc_callPhone" msgid="3740797576113760827">"Hiermee kan de app zonder je tussenkomst telefoonnummers bellen. Dit kan tot onverwachte kosten of oproepen leiden. De app kan hiermee geen noodnummers bellen. Schadelijke apps kunnen u geld kosten door nummers te bellen zonder om je bevestiging te vragen."</string>
+    <string name="permdesc_callPhone" msgid="3740797576113760827">"Hiermee kan de app zonder je tussenkomst telefoonnummers bellen. Dit kan tot onverwachte kosten of gesprekken leiden. De app kan hiermee geen noodnummers bellen. Schadelijke apps kunnen u geld kosten door nummers te bellen zonder om je bevestiging te vragen."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"toegang tot IMS-service voor bellen"</string>
     <string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Hiermee kan de app de IMS-service gebruiken om te bellen zonder je tussenkomst."</string>
     <string name="permlab_readPhoneState" msgid="9178228524507610486">"telefoonstatus en -identiteit lezen"</string>
-    <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 oproep actief is, en het andere telefoonnummer waarmee wordt gebeld."</string>
-    <string name="permlab_manageOwnCalls" msgid="1503034913274622244">"oproepen doorschakelen via het systeem"</string>
-    <string name="permdesc_manageOwnCalls" msgid="6552974537554717418">"Hiermee kan de app de bijbehorende oproepen doorschakelen via het systeem om de belfunctionaliteit te verbeteren."</string>
+    <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_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>
@@ -485,7 +487,7 @@
     <string name="permlab_nfc" msgid="4423351274757876953">"Near Field Communication regelen"</string>
     <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 er een oproep binnenkomt en de toetsenblokkering weer inschakelen als de oproep is beëindigd."</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_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>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Vingerafdruk-pictogram"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"hardware voor gezichtsherkenning beheren"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Hiermee kan de app methoden aanroepen om gezichtstemplates toe te voegen en te verwijderen voor gebruik."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"hardware voor gezichtsherkenning gebruiken"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Hiermee kan de app hardware voor gezichtsherkenning gebruiken voor verificatie"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Kan gezicht niet verwerken. Probeer het opnieuw."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Gezicht is te helder. Probeer bij minder licht."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Gezicht is te donker. Laat meer licht toe."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Houd de sensor verder weg van je gezicht."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Houd de sensor dichter bij je gezicht."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Beweeg de sensor omhoog."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Beweeg de sensor omlaag."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Beweeg de sensor naar rechts."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Beweeg de sensor naar links."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Kijk naar de sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Geen gezicht gedetecteerd."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Houd het gezicht stil voor het apparaat."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware voor gezichtsherkenning niet beschikbaar."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Time-out voor gezicht bereikt. Probeer opnieuw."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Gezicht kan niet worden opgeslagen."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Bewerking voor gezichtsherkenning geannuleerd."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Te veel pogingen. Probeer het later opnieuw."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Te veel pogingen. Gezichtsherkenning inactief."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Probeer het opnieuw."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Geen gezicht geregistreerd."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Dit apparaat heeft geen sensor voor gezichtsherkenning"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Gezicht <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Gezichtspictogram"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"synchronisatie-instellingen lezen"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Hiermee kan de app de synchronisatie-instellingen voor een account lezen. Dit kan bijvoorbeeld bepalen of de app Personen wordt gesynchroniseerd met een account."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"synchronisatie in- en uitschakelen"</string>
@@ -529,18 +562,18 @@
     <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"de content van je SD-kaart aanpassen of verwijderen"</string>
     <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Hiermee kan de app schrijven naar de USB-opslag."</string>
     <string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Hiermee kan de app schrijven naar de SD-kaart."</string>
-    <string name="permlab_use_sip" msgid="2052499390128979920">"SIP-oproepen plaatsen/ontvangen"</string>
-    <string name="permdesc_use_sip" msgid="2297804849860225257">"Toestaan dat de app SIP-oproepen plaatst en ontvangt."</string>
+    <string name="permlab_use_sip" msgid="2052499390128979920">"Bellen of gebeld worden via SIP"</string>
+    <string name="permdesc_use_sip" msgid="2297804849860225257">"Toestaan dat de app belt en gebeld wordt via SIP"</string>
     <string name="permlab_register_sim_subscription" msgid="3166535485877549177">"nieuwe telecom-sim-verbindingen registreren"</string>
     <string name="permdesc_register_sim_subscription" msgid="2138909035926222911">"Hiermee kan de app nieuwe telecom-sim-verbindingen registreren."</string>
     <string name="permlab_register_call_provider" msgid="108102120289029841">"nieuwe telecomverbindingen registreren"</string>
     <string name="permdesc_register_call_provider" msgid="7034310263521081388">"Hiermee kan de app nieuwe telecomverbindingen registreren."</string>
     <string name="permlab_connection_manager" msgid="1116193254522105375">"telecomverbindingen beheren"</string>
     <string name="permdesc_connection_manager" msgid="5925480810356483565">"Hiermee kan de app telecomverbindingen beheren."</string>
-    <string name="permlab_bind_incall_service" msgid="6773648341975287125">"interactie met scherm in actieve oproep"</string>
-    <string name="permdesc_bind_incall_service" msgid="8343471381323215005">"Hiermee kan de app bepalen wanneer en hoe de gebruiker het scherm in een actieve oproep te zien krijgt."</string>
+    <string name="permlab_bind_incall_service" msgid="6773648341975287125">"interactie met scherm in actief gesprek"</string>
+    <string name="permdesc_bind_incall_service" msgid="8343471381323215005">"Hiermee kan de app bepalen wanneer en hoe de gebruiker het scherm in een actief gesprek te zien krijgt."</string>
     <string name="permlab_bind_connection_service" msgid="3557341439297014940">"communicatie met telefonische diensten"</string>
-    <string name="permdesc_bind_connection_service" msgid="4008754499822478114">"Hiermee kan de app met telefonische diensten communiceren om oproepen te plaatsen/ontvangen."</string>
+    <string name="permdesc_bind_connection_service" msgid="4008754499822478114">"Hiermee kan de app met telefonische diensten communiceren zodat je kunt bellen of gebeld worden."</string>
     <string name="permlab_control_incall_experience" msgid="9061024437607777619">"een gebruikerservaring bieden tijdens een gesprek"</string>
     <string name="permdesc_control_incall_experience" msgid="915159066039828124">"Hiermee kan de app een gebruikerservaring bieden tijdens een gesprek."</string>
     <string name="permlab_readNetworkUsageHistory" msgid="7862593283611493232">"historisch netwerkgebruik lezen"</string>
@@ -585,13 +618,13 @@
     <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de tv vergrendelen of alle gegevens van deze gebruiker wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
     <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de telefoon vergrendelen of alle gegevens van deze gebruiker wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
     <string name="policylab_resetPassword" msgid="4934707632423915395">"De schermvergrendeling wijzigen"</string>
-    <string name="policydesc_resetPassword" msgid="1278323891710619128">"De schermvergrendeling wijzigen."</string>
+    <string name="policydesc_resetPassword" msgid="1278323891710619128">"Wijzig de schermvergrendeling."</string>
     <string name="policylab_forceLock" msgid="2274085384704248431">"Het scherm vergrendelen"</string>
-    <string name="policydesc_forceLock" msgid="1141797588403827138">"Beheren hoe en wanneer het scherm wordt vergrendeld."</string>
+    <string name="policydesc_forceLock" msgid="1141797588403827138">"Beheer hoe en wanneer het scherm wordt vergrendeld."</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Alle gegevens wissen"</string>
     <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"De gegevens van de tablet zonder waarschuwing wissen door de fabrieksinstellingen te herstellen."</string>
     <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"De gegevens van de tv zonder waarschuwing wissen door de fabrieksinstellingen te herstellen."</string>
-    <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"De gegevens van de telefoon zonder waarschuwing wissen door de fabrieksinstellingen te herstellen."</string>
+    <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Wis de gegevens van de telefoon zonder waarschuwing door de fabrieksinstellingen te herstellen."</string>
     <string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Gebruikersgegevens wissen"</string>
     <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"De gegevens van deze gebruiker op deze tablet zonder waarschuwing wissen."</string>
     <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"De gegevens van deze gebruiker op deze tv zonder waarschuwing wissen."</string>
@@ -605,7 +638,7 @@
     <string name="policylab_disableCamera" msgid="6395301023152297826">"Camera\'s uitschakelen"</string>
     <string name="policydesc_disableCamera" msgid="2306349042834754597">"Het gebruik van alle apparaatcamera\'s voorkomen."</string>
     <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Bepaalde functies voor schermvergrendeling uitschakelen"</string>
-    <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Gebruik van bepaalde functies voor schermvergrendeling voorkomen."</string>
+    <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Voorkom het gebruik van bepaalde functies voor schermvergrendeling."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Thuis"</item>
     <item msgid="869923650527136615">"Mobiel"</item>
@@ -1131,7 +1164,7 @@
     <string name="volume_music" msgid="5421651157138628171">"Mediavolume"</string>
     <string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Afspelen via Bluetooth"</string>
     <string name="volume_music_hint_silent_ringtone_selected" msgid="8310739960973156272">"Stille beltoon ingesteld"</string>
-    <string name="volume_call" msgid="3941680041282788711">"Volume inkomende oproep"</string>
+    <string name="volume_call" msgid="3941680041282788711">"Volume inkomend gesprek"</string>
     <string name="volume_bluetooth_call" msgid="2002891926351151534">"Volume tijdens gesprek in Bluetooth-modus"</string>
     <string name="volume_alarm" msgid="1985191616042689100">"Alarmvolume"</string>
     <string name="volume_notification" msgid="2422265656744276715">"Meldingsvolume"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wifi-netwerk heeft geen internettoegang"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tik voor opties"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Wijzigingen in je hotspot-instellingen"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Je hotspot-band is gewijzigd."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Dit apparaat biedt geen ondersteuning voor je voorkeur voor alleen 5 GHz. In plaats daarvan gebruikt dit apparaat de 5-GHz-band wanneer deze beschikbaar is."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Overgeschakeld naar <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Apparaat gebruikt <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internetverbinding heeft. Er kunnen kosten in rekening worden gebracht."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Overgeschakeld van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> naar <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1477,7 +1513,7 @@
     <string name="share_action_provider_share_with" msgid="5247684435979149216">"Delen met"</string>
     <string name="sending" msgid="3245653681008218030">"Verzenden..."</string>
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Browser starten?"</string>
-    <string name="SetupCallDefault" msgid="5834948469253758575">"Oproep accepteren?"</string>
+    <string name="SetupCallDefault" msgid="5834948469253758575">"Gesprek accepteren?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Altijd"</string>
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Één keer"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s ondersteunt werkprofielen niet"</string>
@@ -1550,7 +1586,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%2$d</xliff:g> mislukte pogingen wordt u gevraagd je telefoon te ontgrendelen via een e-mailaccount.\n\n Probeer het over <xliff:g id="NUMBER_2">%3$d</xliff:g> seconden opnieuw."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Verwijderen"</string>
-    <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls u langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."</string>
+    <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls je langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."</string>
     <string name="accessibility_shortcut_warning_dialog_title" msgid="8404780875025725199">"Sneltoets voor toegankelijkheid gebruiken?"</string>
     <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Wanneer de snelkoppeling is ingeschakeld, kun je drie seconden op beide volumeknoppen drukken om een toegankelijkheidsfunctie te starten.\n\n Huidige toegankelijkheidsfunctie:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Je kunt de functie wijzigen in Instellingen &gt; Toegankelijkheid."</string>
     <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Sneltoets uitschakelen"</string>
@@ -1745,11 +1781,11 @@
     <string name="muted_by" msgid="5942954724562097128">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> dempt sommige geluiden"</string>
     <string name="system_error_wipe_data" msgid="6608165524785354962">"Er is een intern probleem met je apparaat. Het apparaat kan instabiel zijn totdat u het apparaat terugzet naar de fabrieksinstellingen."</string>
     <string name="system_error_manufacturer" msgid="8086872414744210668">"Er is een intern probleem met je apparaat. Neem contact op met de fabrikant voor meer informatie."</string>
-    <string name="stk_cc_ussd_to_dial" msgid="5214333646366591205">"USSD-verzoek gewijzigd in normale oproep"</string>
+    <string name="stk_cc_ussd_to_dial" msgid="5214333646366591205">"USSD-verzoek gewijzigd in normaal gesprek"</string>
     <string name="stk_cc_ussd_to_ss" msgid="4884994189414782605">"USSD-verzoek gewijzigd in SS-verzoek"</string>
     <string name="stk_cc_ussd_to_ussd" msgid="5728637484565449312">"Gewijzigd in nieuw USSD-verzoek"</string>
     <string name="stk_cc_ussd_to_dial_video" msgid="4134455726513175559">"USSD-verzoek gewijzigd in videogesprek"</string>
-    <string name="stk_cc_ss_to_dial" msgid="1360775164651754978">"SS-verzoek gewijzigd in normale oproep"</string>
+    <string name="stk_cc_ss_to_dial" msgid="1360775164651754978">"SS-verzoek gewijzigd in normaal gesprek"</string>
     <string name="stk_cc_ss_to_dial_video" msgid="6577956662913194947">"SS-verzoek gewijzigd in videogesprek"</string>
     <string name="stk_cc_ss_to_ussd" msgid="5614626512855868785">"SS-verzoek gewijzigd in USSD-verzoek"</string>
     <string name="stk_cc_ss_to_ss" msgid="7716729801537709054">"Gewijzigd in nieuw SS-verzoek"</string>
@@ -1869,8 +1905,8 @@
     <string name="harmful_app_warning_title" msgid="8982527462829423432">"Schadelijke app gevonden"</string>
     <string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> wil segmenten van <xliff:g id="APP_2">%2$s</xliff:g> weergeven"</string>
     <string name="screenshot_edit" msgid="7867478911006447565">"Bewerken"</string>
-    <string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"Trillen bij oproepen en meldingen"</string>
-    <string name="volume_dialog_ringer_guidance_silent" msgid="2128975224280276122">"Oproepen en meldingen zijn gedempt"</string>
+    <string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"Trillen bij gesprekken en meldingen"</string>
+    <string name="volume_dialog_ringer_guidance_silent" msgid="2128975224280276122">"Gesprekken en meldingen zijn gedempt"</string>
     <string name="notification_channel_system_changes" msgid="5072715579030948646">"Systeemwijzigingen"</string>
     <string name="notification_channel_do_not_disturb" msgid="6766940333105743037">"Niet storen"</string>
     <string name="zen_upgrade_notification_visd_title" msgid="3288313883409759733">"Nieuw: \'Niet storen\' verbergt meldingen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 96677fa..c86fab8 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ଏରୋପ୍ଲେନ୍‍ ମୋଡ୍"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ଏରୋପ୍ଲେନ୍‍ ମୋଡ୍ ଅନ୍ ଅଛି"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ଏରୋପ୍ଲେନ୍‍ ମୋଡ୍ ଅଫ୍ ଅଛି"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ବ୍ୟାଟେରୀ ସେଭର୍"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ବ୍ୟାଟେରୀ ସେଭର୍‌ ଅଫ୍ ଅଛି"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ବ୍ୟାଟେରୀ ସେଭର୍‌ ଅନ୍‌ ଅଛି"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"ସେଟିଙ୍ଗ"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"ସହାୟକ"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ଭଏସ୍‌ ସହାୟକ"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"ଲୋକେଶନ୍‌"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"ଏହି ଡିଭାଇସ୍‌ର ଲୋକେଶନ୍‍ ଆକ୍ସେସ୍‍ କରେ"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଏହି ଡିଭାଇସ୍‌ର ଲୋକେଶନ୍‍ ଆକ୍ସେସ୍‍ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ଆପଣ ଆପ୍‍ ବ୍ୟବହାର କରୁଥିବା ବେଳେ କେବଳ ଲୋକେସନ୍‍କୁ ଆପ୍‍‍ର ଆକ୍ସେସ୍‍ ରହିବ।"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ସଦାବେଳେ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଏହି ଡିଭାଇସ୍‌ର ଲୋକେସନ୍‍ ଆକ୍ସେସ୍‍ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ଆପଣ ଆପ୍‍ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ, ଲୋକେସନ୍‍କୁ ସଦାବେଳେ ଆପ୍‍‍ର ଆକ୍ସେସ୍‍ ରହିବ।"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"କ୍ୟାଲେଣ୍ଡର୍"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"ଆପଣଙ୍କ କ୍ୟାଲେଣ୍ଡର୍‍ ଆକ୍ସେସ୍‍ କରେ"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଆପଣଙ୍କ କ୍ୟାଲେଣ୍ଡର୍‌କୁ ଆକ୍ସେସ୍‍ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string>
@@ -291,12 +291,9 @@
     <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>
-    <!-- no translation found for permgrouplab_calllog (8798646184930388160) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_calllog (3006237336748283775) -->
-    <skip />
-    <!-- no translation found for permgrouprequest_calllog (8487355309583773267) -->
-    <skip />
+    <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>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"ଫୋନ୍‍"</string>
     <string name="permgroupdesc_phone" msgid="6234224354060641055">"ଫୋନ୍‍ କଲ୍‍ କରେ ଏବଂ ପରିଚାଳନା କରେ"</string>
     <string name="permgrouprequest_phone" msgid="9166979577750581037">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଫୋନ୍‍ କଲ୍‍ କରିବାକୁ ତଥା ପରିଚାଳନା କରିବାକୁ ଅନୁମତି ଦେବେ କି?"</string>
@@ -405,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ଏହି ଆପ୍‍, ଆପଣଙ୍କ ଫୋନ୍‌ରେ କ୍ୟାଲେଣ୍ଡର୍‌ ଇଭେଣ୍ଟଗୁଡ଼ିକୁ ଯୋଡ଼ିପାରେ, ବାହାର କରିପାରେ କିମ୍ବା ବଦଳାଇପାରେ। କ୍ୟାଲେଣ୍ଡର୍‌ ମାଲିକଙ୍କ ପାଖରୁ ଆସିଥିବା ପରି ଜଣା‍ପଡ଼ିବା ମେସେଜ୍‍କୁ ଏହି ଆପ୍‍ ପଠାଇପାରେ କିମ୍ବା ମାଲିକଙ୍କୁ ନଜଣାଇ ଇଭେଣ୍ଟ ବଦଳାଇପାରେ।"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ଅତିରିକ୍ତ ଲୋକେଶନ୍ ପ୍ରଦାନକାରୀ କମାଣ୍ଡକୁ ଆକ୍ସେସ୍‍ କରନ୍ତୁ"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ଅତିରିକ୍ତ ଲୋକେଶନ୍‍ ପ୍ରଦାନକାରୀ କମାଣ୍ଡ ଆକ୍ସେସ୍‌ କରିବା ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। GPS କିମ୍ବା ଅନ୍ୟ ଲୋକେଶନ୍‍ ସୋର୍ସଗୁଡିକରେ ଆପ୍‍ଟି ପ୍ରଭାବ ପକାଇପାରେ।"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"ନିର୍ଦ୍ଦିଷ୍ଟ ଲୋକେଶନ୍‍ ଆକ୍ସେସ୍ କରେ (GPS ଏବଂ ନେଟ୍‌ୱର୍କ-ଆଧାରିତ)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"ଏହି ଆପ୍‍, GPS କିମ୍ୱା ନେଟ୍‌ୱର୍କ ସୋର୍ସ ଉପରେ ଆଧାର କରି ଆପଣଙ୍କ ଲୋକେଶନ୍‍ ପ୍ରାପ୍ତ କରିପାରେ, ଯେପରିକି ସେଲ୍‍ ଟାୱାର୍‍ ଓ ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କ। ଏହି ଲୋକେଶନ୍‌ ସେବା, ଆପଣଙ୍କ ଫୋନ୍‌ରେ ଅନ୍‍ ରହିଥିବା ଓ ଉପଲବ୍ଧ ଥିବା ଦରକାର, ଯେଉଁଥିରୁ ଆପ୍‌ ସେଗୁଡ଼ିକର ବ୍ୟବହାର କରିପାରିବ। ଏହାଦ୍ୱାରା ବ୍ୟାଟେରୀ ଖର୍ଚ୍ଚ ବଢ଼ିପାରେ।"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"କେବଳ ସମ୍ମୁଖଭାଗରେ ସଠିକ୍‍ ଲୋକେଶନ୍‍ର ଆକ୍ସେସ୍‍ କରନ୍ତୁ"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ଏହି ଆପ୍‍ ଯେତେବେଳେ ସମ୍ମୁଖଭାଗରେ ଥିବାବେଳେ ଆପଣଙ୍କର ସଠିକ୍‍ ଲୋକେଶନ୍‍ ପ୍ରାପ୍ତ କରିପାରିବ। ଏହି ଲୋକେଶନ୍‍ ସେବାଗୁଡ଼ିକ ନିଶ୍ଚିତରୂପେ ଅନ୍‍ ରହିବା ଦରକାର ଏବଂ ଆପ୍‍ର ବ୍ୟବହାର ପାଇଁ ଫୋନ୍‍ରେ ଉପଲବ୍ଧ ଥିବା ଦରକାର। ଏହା ବ୍ୟାଟେରୀ ଅଧିକା ଖର୍ଚ୍ଚ କରିପାରେ।"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ପାଖାପାଖି ଲୋକେଶନ୍‍ ଆକ୍ସେସ୍‍ କରେ (ନେଟ୍‌ୱର୍କ-ଆଧାରିତ)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ଏହି ଆପ୍‍, ନେଟ୍‌ୱର୍କ ସୋର୍ସ ଉପରେ ଆଧାର କରି ଆପଣଙ୍କ ଲୋକେଶନ୍‍ ପ୍ରାପ୍ତ କରିପାରେ, ଯେପରିକି ସେଲ୍‍ ଟାୱାର୍‍ ଓ ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କ। ଏହି ଲୋକେଶନ୍‌ ସେବା, ଆପଣଙ୍କ ଟାବଲେଟ୍‌ରେ ଅନ୍‍ ରହିଥିବା ଓ ଉପଲବ୍ଧ ଥିବା ଦରକାର, ଯେଉଁଥିରୁ ଆପ୍‌ ସେଗୁଡ଼ିକର ବ୍ୟବହାର କରିପାରିବ।"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ଏହି ଆପ୍‍, ନେଟ୍‌ୱର୍କ ସୋର୍ସ ଉପରେ ଆଧାର କରି ଆପଣଙ୍କ ଲୋକେଶନ୍‍ ପ୍ରାପ୍ତ କରିପାରେ, ଯେପରିକି ସେଲ୍‍ ଟାୱାର୍‍ ଓ ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କ। ଏହି ଲୋକେଶନ୍‌ ସେବା, ଆପଣଙ୍କ ଟିଭିରେ ଅନ୍‍ ରହିଥିବା ଓ ଉପଲବ୍ଧ ଥିବା ଦରକାର, ଯେଉଁଥିରୁ ଆପ୍‌ ସେଗୁଡ଼ିକର ବ୍ୟବହାର କରିପାରିବ।"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ସେଲ୍‍ ଟାୱାର ଓ ୱାଇ-ଫାଇ ନେଟ୍‌ୱର୍କ ପରି ସୋର୍ସକୁ ଆଧାର କରି ଏହି ଆପ୍‍ ଆପଣଙ୍କ ଲୋକେଶନ୍‍ ପ୍ରାପ୍ତ କରିପାରିବ। ଏହି ଲୋକେଶନ୍‍ ସେବାଗୁଡ଼ିକର ବ୍ୟବହାର କରିବାକୁ ସେଗୁଡ଼ିକ ଅନ୍‍ କରାଯିବା ଏବଂ ଆପଣଙ୍କ ଫୋନ୍‌ରେ ଉପଲବ୍ଧ ଥିବା ଜରୁରୀ"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ପୃଷ୍ଠପଟରେ ସଠିକ୍‍ ଲୋକେଶନ୍‍ର ଆକ୍ସେସ୍‍"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ଏହି ଆପ୍‍ ପୃଷ୍ଠପଟରେ ରହିଥିବାବେଳେ ଯେକୌଣସି ସମୟରେ ଆପଣଙ୍କର ଲୋକେଶନ୍‍ ପ୍ରାପ୍ତ କରିପାରିବ। ଏହି ଲୋକେଶନ୍‍ ସେବାଗୁଡ଼ିକ ନିଶ୍ଚିତରୂପେ ଅନ୍‍ ରହିବା ଦରକାର ଏବଂ ଆପ୍‍ର ବ୍ୟବହାର ପାଇଁ ଫୋନ୍‍ରେ ଉପଲବ୍ଧ ଥିବା ଦରକାର। ଏହା ବ୍ୟାଟେରୀ ଅଧିକା ଖର୍ଚ୍ଚ କରିପାରେ।"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ଆପଣଙ୍କ ଅଡିଓ ସେଟିଙ୍ଗକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ଆପ୍‌କୁ ଗ୍ଲୋବାଲ୍ ଅଡିଓ ସେଟିଙ୍ଗ, ଯେପରିକି ଭଲ୍ୟୁମ୍‌କୁ ସଂଶୋଧିତ କରିବାକୁ ଏବଂ ଆଉଟପୁଟ୍ ପାଇଁ ସ୍ପିକର୍‌ ବ୍ୟବହାର କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ଅଡିଓ ରେକର୍ଡ କରନ୍ତୁ"</string>
@@ -518,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଇକନ୍"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"ଫେସ୍‍ ପ୍ରମାଣୀକରଣ ହାର୍ଡୱେର୍‌ର ପରିଚାଳନା କରନ୍ତୁ"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"ବ୍ୟବହାର ପାଇଁ ଆପ୍‍କୁ ଫେସିଆଲ୍‍ ଟେମ୍ପଲେଟ୍‍ ଯୋଡିବା ଓ ଡିଲିଟ୍‍ ର ପଦ୍ଧତି ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ଫେସ୍‍ ପ୍ରମାଣୀକରଣ ହାର୍ଡୱେର୍‌ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ଫେସ୍‍ ପ୍ରମାଣୀକରଣ ହାର୍ଡୱେର୍‌ର ପ୍ରମାଣ ପାଇଁ ଆପ୍‍କୁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"ଫେସ୍‍ ଚିହ୍ନଟ ହେଲାନାହିଁ ।ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"ଫେସ୍‍ଟି ବହୁତ ଉଜ୍ଵଳ ଦେଖାଯାଉଛି। ଦୟାକରି, କମ ଆଲୋକରେ ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"ଫେସ୍‍ଟି ବହୁତ କଳା ଦେଖାଯାଉଛି। ଦୟାକରି ଆଲୋକକୁ ଅନାବୃତ କରନ୍ତୁ।"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"ଦୟାକରି ସେନ୍‍ସର୍‍କୁ ଫେସ୍‍ ପାଖରୁ ଦୂରେଇ ରଖନ୍ତୁ"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"ଦୟାକରି ସେନ୍‍ସର୍‍କୁ ଫେସ୍‍ର ଅତି ନିକଟକୁ ଆଣନ୍ତୁ।"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"ଦୟାକରି ସେନ୍‍ସର୍‍କୁ ଆହୁରି ଉପରକୁ ଘୁଞ୍ଚାନ୍ତୁ।"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"ଦୟାକରି ସେନ୍‍ସର୍‍କୁ ତଳକୁ ଘୁଞ୍ଚାନ୍ତୁ।"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"ଦୟାକରି ସେନ୍‍ସର୍‍କୁ ଦକ୍ଷିଣକୁ ଘୁଞ୍ଚାନ୍ତୁ।"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"ଦୟାକରି ସେନ୍‍ସର୍‍କୁ ବାମକୁ ଘୁଞ୍ଚାନ୍ତୁ।"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"ଦୟାକରି ସେନ୍‍ସର୍‍କୁ ଦେଖନ୍ତୁ"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"କୌଣସି ଫେସ୍‍ ଚିହ୍ନଟ ହେଲା ନାହିଁ"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"ଡିଭାଇସ୍‍ର ସାମ୍ନାରେ ଫେସ୍‍ ସିଧା ରଖନ୍ତୁ।"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"ଫେସ୍‍ର ହାର୍ଡୱେୟାର୍‍ ଉପଲବ୍ଧ ନାହିଁ।"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"ଫେସ୍‍ର ସମୟସୀମା ସରିଗଲା। ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"ଫେସ୍‍ ମେମୋରୀରେ ଷ୍ଟୋର୍‍ କରାଯାଇପାରିବ ନାହିଁ।"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"ଫେସ୍‍ର ଅପରେଶନ୍‍ କ୍ୟାନ୍ସଲ୍‍ ହୋ‍ଇଗଲା"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"ବାରମ୍ବାର ଚେଷ୍ଟା। ପରେ ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ବାରମ୍ବାର ଚେଷ୍ଟା। ଫେସ୍‍ ପ୍ରମାଣୀକରଣ ଅକ୍ଷମ କରାଗଲା।"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"କୌଣସି ଫେସ୍‍ ପଞ୍ଜୀକୃତ ହୋ‍ଇନଥିଲା।"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"ଡିଭାଇସ୍‍ର ଗୋଟିଏ ଫେସ୍‍ ପ୍ରମାଣୀକରଣ ସେନ୍‍ସର୍‍ ନାହିଁ"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g>ଙ୍କ ଫେସ୍‍"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"ଫେସ୍ ଆଇକନ୍"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ସିଙ୍କ ସେଟିଙ୍ଗକୁ ପଢ଼ନ୍ତୁ"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ଏକ ଆକାଉଣ୍ଟ ପାଇଁ ସିଙ୍କ ସେଟିଙ୍ଗ ପଢ଼ିବାକୁ ଆପ୍‍ଟିକୁ ଅନୁମତି ଦିଏ। ଉଦାହରଣସ୍ୱରୂପ, ଲୋକଙ୍କ ଆପ୍‍ ଏକ ଆକାଉଣ୍ଟରେ ସିଙ୍କ ହୋଇଛି କି ନାହିଁ ଏହା ଜାଣିପାରେ।"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ସିଙ୍କ ଅନ୍‍ ଓ ଅଫ୍‍ ଟୋଗଲ୍‌ କରନ୍ତୁ"</string>
@@ -1178,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"ୱାଇ-ଫାଇର କୌଣସି ଇଣ୍ଟରନେଟ୍‍ ଆକ୍ସେସ୍‍ ନାହିଁ"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ବିକଳ୍ପ ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ"</string>
+    <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">"କେବଳ 5GHz ପାଇଁ, ଏହି ଡିଭାଇସ୍‍ ଆପଣଙ୍କର ପସନ୍ଦକୁ ସପୋର୍ଟ କରେନାହିଁ। ଏହା ପରିବର୍ତ୍ତେ, ଏହି ଡିଭାଇସ୍‍ 5GHz ବ୍ୟାଣ୍ଡ ବ୍ୟବହାର କରିବ।"</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>
@@ -1698,8 +1731,7 @@
     <string name="package_updated_device_owner" msgid="1847154566357862089">"ଆପଣଙ୍କ ଆଡମିନ୍‌‌ ଅପଡେଟ୍‍ କରିଛନ୍ତି"</string>
     <string name="package_deleted_device_owner" msgid="2307122077550236438">"ଆପଣଙ୍କ ଆଡମିନ୍‌‌ ଡିଲିଟ୍‍ କରିଛନ୍ତି"</string>
     <string name="battery_saver_description_with_learn_more" msgid="6323937147992667707">"ବ୍ୟାଟେରୀର କାର୍ଯ୍ୟକାଳକୁ ବଢ଼ାଇବା ପାଇଁ, ବ୍ୟାଟେରୀ ସେଭର୍ କିଛି ଡିଭାଇସ୍‍ ଫିଚର୍‌କୁ ବନ୍ଦ କରିବା ସହ କେତେକ ଆପ୍‌କୁ ଚାଲିବାରୁ ରୋକିଥାଏ। "<annotation id="url">"ଅଧିକ ଜାଣନ୍ତୁ"</annotation></string>
-    <!-- no translation found for battery_saver_description (769989536172631582) -->
-    <skip />
+    <string name="battery_saver_description" msgid="769989536172631582">"ଆପଣଙ୍କ ବ୍ୟାଟେରୀ ଆୟୁଶ ବଢାଇବାକୁ, ବ୍ୟାଟେରୀ ସେଭର୍‌ କିଛି ଡିଭାଇସ୍ ‌ଫିଚର୍‌ଗୁଡିକୁ ବନ୍ଦ କରେ ଏବଂ ଆପ୍‌ଗୁଡିକୁ ପ୍ରତିବନ୍ଧିତ କରିଥାଏ।"</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>
@@ -1878,10 +1910,8 @@
     <string name="volume_dialog_ringer_guidance_silent" msgid="2128975224280276122">"କଲ୍ ଓ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ନିଃଶବ୍ଦ କରିଦିଆଯିବ"</string>
     <string name="notification_channel_system_changes" msgid="5072715579030948646">"ସିଷ୍ଟମ୍‌ରେ ପରିବର୍ତ୍ତନ"</string>
     <string name="notification_channel_do_not_disturb" msgid="6766940333105743037">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ"</string>
-    <!-- no translation found for zen_upgrade_notification_visd_title (3288313883409759733) -->
-    <skip />
-    <!-- no translation found for zen_upgrade_notification_visd_content (5533674060311631165) -->
-    <skip />
+    <string name="zen_upgrade_notification_visd_title" msgid="3288313883409759733">"ନୂଆ: \"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ମୋଡ୍‌ ଅନ୍‌ ଥିବା ଯୋଗୁଁ ବିଜ୍ଞପ୍ତି ଲୁଚାଇ ଦିଆଯାଉଛି"</string>
+    <string name="zen_upgrade_notification_visd_content" msgid="5533674060311631165">"ଅଧିକ ଜାଣିବାକୁ ଟ୍ୟାପ୍‌ କରନ୍ତୁ ଏବଂ ବଦଳାନ୍ତୁ।"</string>
     <string name="zen_upgrade_notification_title" msgid="3799603322910377294">"’ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ’ ବଦଳିଯାଇଛି"</string>
     <string name="zen_upgrade_notification_content" msgid="1794994264692424562">"କ’ଣ ଅବରୋଧ ହୋଇଛି ଯାଞ୍ଚ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string>
     <string name="notification_app_name_system" msgid="4205032194610042794">"ସିଷ୍ଟମ୍‌"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index e0701b44..48c1a38 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ਏਅਰਪਲੇਨ ਮੋਡ"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ਏਅਰਪਲੇਨ ਮੋਡ ਚਾਲੂ ਹੈ"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ਏਅਰਪਲੇਨ ਮੋਡ ਬੰਦ ਹੈ"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ਬੈਟਰੀ ਸੇਵਰ"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ਬੈਟਰੀ ਸੇਵਰ ਬੰਦ ਹੈ"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ਬੈਟਰੀ ਸੇਵਰ ਚਾਲੂ ਹੈ"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"ਸੈਟਿੰਗਾਂ"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"ਸਹਾਇਤਾ ਕਰੋ"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ਅਵਾਜ਼ੀ ਸਹਾਇਕ"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"ਟਿਕਾਣਾ"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"ਇਸ ਡੀਵਾਈਸ ਦੇ ਨਿਰਧਾਰਤ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚੋ"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਇਸ ਡੀਵਾਈਸ ਦੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਐਪ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਹੀ ਐਪ ਕੋਲ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ।"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਹਮੇਸ਼ਾਂ ਇਸ ਡੀਵਾਈਸ ਦੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ਐਪ ਕੋਲ ਹਮੇਸ਼ਾਂ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ, ਭਾਵੇਂ ਤੁਸੀਂ ਐਪ ਦੀ ਵਰਤੋਂ ਨਾ ਕਰ ਰਹੇ ਹੋਵੋ।"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"ਕੈਲੰਡਰ"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"ਤੁਹਾਡੇ ਕੈਲੰਡਰ ਤੱਕ ਪਹੁੰਚ ਕਰਨ"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਤੁਹਾਡੇ ਕੈਲੰਡਰ ਤੱਕ ਪਹੁੰਚ ਕਰਨੀ ਦੇਣੀ ਹੈ?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ਇਹ ਐਪ ਤੁਹਾਡੇ ਫ਼ੋਨ \'ਤੇ ਕੈਲੰਡਰ ਇਵੈਂਟਾਂ ਨੂੰ ਸ਼ਾਮਲ ਕਰ ਸਕਦੀ ਹੈ, ਹਟਾ ਸਕਦੀ ਹੈ, ਜਾਂ ਬਦਲ ਸਕਦੀ ਹੈ। ਇਹ ਐਪ ਉਹਨਾਂ ਸੁਨੇਹਿਆਂ ਨੂੰ ਭੇਜ ਸਕਦੀ ਹੈ ਜੋ ਕੈਲੰਡਰ ਮਾਲਕਾਂ ਤੋਂ ਆਉਂਦੇ ਜਾਪ ਸਕਦੇ ਹਨ, ਜਾਂ ਉਹਨਾਂ ਦੇ ਮਾਲਕਾਂ ਨੂੰ ਸੂਚਿਤ ਕੀਤੇ ਬਿਨਾਂ ਇਵੈਂਟਾਂ ਨੂੰ ਬਦਲ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ਵਾਧੂ ਟਿਕਾਣਾ ਪ੍ਰਦਾਤਾ ਕਮਾਂਡਾਂ ਤੱਕ ਪਹੁੰਚ"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ਐਪ ਨੂੰ ਵਾਧੂ ਟਿਕਾਣਾ ਪ੍ਰਦਾਤਾ ਕਮਾਂਡਾਂ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਐਪ ਨੂੰ GPS ਜਾਂ ਹੋਰ ਟਿਕਾਣਾ ਸਰੋਤਾਂ ਦੇ ਓਪਰੇਸ਼ਨ ਵਿੱਚ ਵਿਘਨ ਪਾਉਣ ਦੀ ਆਗਿਆ ਦੇ ਸਕਦਾ ਹੈ।"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"ਸਟੀਕ ਟਿਕਾਣੇ \'ਤੇ ਪਹੁੰਚ ਕਰੋ (GPS ਅਤੇ ਨੈੱਟਵਰਕ-ਆਧਾਰਿਤ)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"ਇਹ ਐਪ GPS ਜਾਂ ਨੈੱਟਵਰਕ ਸਰੋਤਾਂ ਜਿਵੇਂ ਕਿ ਸੈੱਲ ਟਾਵਰਾਂ ਅਤੇ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕਾਂ \'ਤੇ ਆਧਾਰਿਤ ਤੁਹਾਡਾ ਟਿਕਾਣਾ ਪਤਾ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਦੁਆਰਾ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕੀਤੇ ਜਾਣ ਦੇ ਯੋਗ ਹੋਣ ਲਈ ਇਹ ਸੇਵਾਵਾਂ ਤੁਹਾਡੇ ਫ਼ੋਨ \'ਤੇ ਉਪਲਬਧ ਹੋਣੀਆਂ ਅਤੇ ਚਾਲੂ ਕੀਤੀਆਂ ਹੋਣੀਆਂ ਲਾਜ਼ਮੀ ਹਨ। ਇਸ ਨਾਲ ਬੈਟਰੀ ਦੀ ਖਪਤ ਵਧ ਸਕਦੀ ਹੈ।"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ਸਿਰਫ਼ ਫੋਰਗ੍ਰਾਊਂਡ ਵਿੱਚ ਸਟੀਕ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰੋ"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ਇਹ ਐਪ ਫੋਰਗ੍ਰਾਉਂਡ ਵਿੱਚ ਹੋਣ \'ਤੇ ਹੀ ਤੁਹਾਡਾ ਸਟੀਕ ਟਿਕਾਣਾ ਪਤਾ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਵੱਲੋਂ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਇਹ ਸੇਵਾਵਾਂ ਤੁਹਾਡੇ ਫ਼ੋਨ \'ਤੇ ਉਪਲਬਧ ਹੋਣੀਆਂ ਅਤੇ ਚਾਲੂ ਕੀਤੀਆਂ ਹੋਣੀਆਂ ਲਾਜ਼ਮੀ ਹਨ। ਇਸ ਨਾਲ ਬੈਟਰੀ ਦੀ ਖਪਤ ਵਧ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ਅੰਦਾਜ਼ਨ ਟਿਕਾਣੇ \'ਤੇ ਪਹੁੰਚ ਕਰੋ (ਨੈੱਟਵਰਕ-ਆਧਾਰਿਤ)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ਇਹ ਐਪ ਨੈੱਟਵਰਕ ਸਰੋਤਾਂ ਜਿਵੇਂ ਕਿ ਸੈੱਲ ਟਾਵਰਾਂ ਅਤੇ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕਾਂ \'ਤੇ ਆਧਾਰਿਤ ਤੁਹਾਡਾ ਟਿਕਾਣਾ ਪਤਾ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਦੁਆਰਾ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕੀਤੇ ਜਾਣ ਦੇ ਯੋਗ ਹੋਣ ਲਈ ਇਹ ਸੇਵਾਵਾਂ ਤੁਹਾਡੇ ਟੈਬਲੈੱਟ \'ਤੇ ਉਪਲਬਧ ਹੋਣੀਆਂ ਅਤੇ ਚਾਲੂ ਕੀਤੀਆਂ ਹੋਣੀਆਂ ਲਾਜ਼ਮੀ ਹਨ।"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ਇਹ ਐਪ ਨੈੱਟਵਰਕ ਸਰੋਤਾਂ ਜਿਵੇਂ ਕਿ ਸੈੱਲ ਟਾਵਰਾਂ ਅਤੇ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕਾਂ \'ਤੇ ਆਧਾਰਿਤ ਤੁਹਾਡਾ ਟਿਕਾਣਾ ਪਤਾ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਵੱਲੋਂ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕੀਤੇ ਜਾਣ ਦੇ ਯੋਗ ਹੋਣ ਲਈ ਇਹ ਸੇਵਾਵਾਂ ਤੁਹਾਡੇ ਟੀਵੀ \'ਤੇ ਉਪਲਬਧ ਹੋਣੀਆਂ ਅਤੇ ਚਾਲੂ ਕੀਤੀਆਂ ਹੋਣੀਆਂ ਲਾਜ਼ਮੀ ਹਨ।"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ਇਹ ਐਪ ਨੈੱਟਵਰਕ ਸਰੋਤਾਂ ਜਿਵੇਂ ਕਿ ਸੈੱਲ ਟਾਵਰਾਂ ਅਤੇ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕਾਂ \'ਤੇ ਆਧਾਰਿਤ ਤੁਹਾਡਾ ਟਿਕਾਣਾ ਪਤਾ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਵੱਲੋਂ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕੀਤੇ ਜਾਣ ਦੇ ਯੋਗ ਹੋਣ ਲਈ ਇਹ ਸੇਵਾਵਾਂ ਤੁਹਾਡੇ ਫ਼ੋਨ \'ਤੇ ਉਪਲਬਧ ਹੋਣੀਆਂ ਅਤੇ ਚਾਲੂ ਕੀਤੀਆਂ ਹੋਣੀਆਂ ਲਾਜ਼ਮੀ ਹਨ।"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"ਬੈਕਗ੍ਰਾਉਂਡ ਵਿੱਚ ਸਟੀਕ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰੋ"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ਇਹ ਐਪ ਬੈਕਗ੍ਰਾਉਂਡ ਵਿੱਚ ਹੋਣ \'ਤੇ ਕਿਸੇ ਵੇਲੇ ਵੀ ਤੁਹਾਡਾ ਸਟੀਕ ਟਿਕਾਣਾ ਪਤਾ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਵੱਲੋਂ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਇਹ ਸੇਵਾਵਾਂ ਤੁਹਾਡੇ ਫ਼ੋਨ \'ਤੇ ਉਪਲਬਧ ਹੋਣੀਆਂ ਅਤੇ ਚਾਲੂ ਕੀਤੀਆਂ ਹੋਣੀਆਂ ਲਾਜ਼ਮੀ ਹਨ। ਇਸ ਨਾਲ ਬੈਟਰੀ ਦੀ ਖਪਤ ਵਧ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ਆਪਣੀਆਂ ਆਡੀਓ ਸੈਟਿੰਗਾਂ ਬਦਲੋ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ਐਪ ਨੂੰ ਗਲੋਬਲ ਆਡੀਓ ਸੈਟਿੰਗਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਅਵਾਜ਼ ਅਤੇ ਆਊਟਪੁਟ ਲਈ ਕਿਹੜਾ ਸਪੀਕਰ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ।"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">" ਆਡੀਓ  ਰਿਕਾਰਡ ਕਰਨ"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਤੀਕ"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਰਨ ਹਾਰਡਵੇਅਰ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"ਐਪ ਨੂੰ ਵਰਤਣ ਲਈ ਚਿਹਰਾ ਟੈਮਪਲੇਟ ਸ਼ਾਮਲ ਕਰਨ ਜਾਂ ਮਿਟਾਉਣ ਦੀਆਂ ਵਿਧੀਆਂ ਦੀ ਬੇਨਤੀ ਕਰਨ ਦਿੰਦੀ ਹੈ।"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਰਨ ਹਾਰਡਵੇਅਰ ਵਰਤੋ"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ਐਪ ਨੂੰ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਰਨ ਹਾਰਡਵੇਅਰ ਵਰਤਣ ਦਿੰਦੀ ਹੈ"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"ਚਿਹਰੇ ਦੀ ਪਛਾਣ ਨਹੀਂ ਹੋਈ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"ਚਿਹਰਾ ਬਹੁਤ ਚਮਕਦਾਰ ਹੈ। ਘੱਟ ਰੋਸ਼ਨੀ ਵਿੱਚ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"ਚਿਹਰਾ ਹਨੇਰੇ ਵਿੱਚ ਹੈ। ਚਾਨਣ ਕਰੋ।"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"ਚਿਹਰੇ ਨੂੰ ਸੈਂਸਰ ਤੋਂ ਦੂਰ ਲਿਜਾਓ।"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"ਚਿਹਰੇ ਨੂੰ ਸੈਂਸਰ ਦੇ ਨੇੜੇ ਲਿਆਓ।"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"ਸੈਂਸਰ ਨੂੰ ਉੱਪਰ ਕਰੋ।"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"ਸੈਂਸਰ ਨੂੰ ਥੱਲੇ ਕਰੋ।"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"ਸੈਂਸਰ ਨੂੰ ਸੱਜੇ ਪਾਸੇ ਲਿਜਾਓ।"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"ਸੈਂਸਰ ਨੂੰ ਖੱਬੇ ਪਾਸੇ ਲਿਜਾਓ।"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"ਕਿਰਪਾ ਕਰਕੇ ਸੈਂਸਰ ਵੱਲ ਦੇਖੋ।"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"ਕੋਈ ਚਿਹਰਾ ਨਹੀਂ ਦਿਸਿਆ।"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"ਚਿਹਰੇ ਨੂੰ ਡੀਵਾਈਸ ਦੇ ਸਾਹਮਣੇ ਸਥਿਰ ਰੱਖੋ।"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"ਚਿਹਰਾ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"ਚਿਹਰਾ ਪਛਾਣਨ ਦਾ ਸਮਾਂ ਸਮਾਪਤ ਹੋਇਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"ਚਿਹਰੇ ਦੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"ਚਿਹਰਾ ਪਛਾਣਨ ਦੀ ਪ੍ਰਕਿਰਿਆ ਰੱਦ ਕੀਤੀ ਗਈ।"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"ਹੱਦੋਂ ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ। ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ਹੱਦੋਂ ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ। ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਰਨ ਬੰਦ ਹੈ।"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"ਕੋਈ ਚਿਹਰਾ ਜਾਣਕਾਰੀ ਦਰਜ ਨਹੀਂ ਕੀਤੀ ਗਈ।"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਕੋਈ ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਰਨ ਸੈਂਸਰ ਨਹੀਂ ਹੈ"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"ਚਿਹਰਾ <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"ਚਿਹਰਾ ਪ੍ਰਤੀਕ"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ਸਿੰਕ ਸੈਟਿੰਗਾਂ ਪੜ੍ਹੋ"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ਐਪ ਨੂੰ ਕਿਸੇ ਖਾਤੇ ਲਈ ਸਮਕਾਲੀਕਰਨ ਸੈਟਿੰਗਾਂ ਪੜ੍ਹਨ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਇਹ ਪਤਾ ਕਰ ਸਕਦਾ ਹੈ ਕਿ People ਐਪ ਦਾ ਕਿਸੇ ਖਾਤੇ ਨਾਲ ਸਮਕਾਲੀਕਿਰਤ ਕੀਤਾ ਗਿਆ ਹੈ ਜਾਂ ਨਹੀਂ।"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ਸਮਕਾਲੀਕਰਨ ਚਾਲੂ ਅਤੇ ਬੰਦ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"ਵਾਈ-ਫਾਈ ਦੀ ਕੋਈ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ"</string>
+    <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">"ਇਹ ਡੀਵਾਈਸ ਸਿਰਫ਼ 5GHz ਦੀ ਤੁਹਾਡੀ ਤਰਜੀਹ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦਾ ਹੈ। ਇਸਦੀ ਬਜਾਏ, ਇਹ ਡੀਵਾਈਸ ਉਪਲਬਧ ਹੋਣ \'ਤੇ 5GHz ਬੈਂਡ ਵਰਤੇਗਾ।"</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>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 4f015e2..188e3e4 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -242,9 +242,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Tryb samolotowy"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Tryb samolotowy jest włączony"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Tryb samolotowy jest wyłączony"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Oszczędzanie baterii"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Oszczędzanie baterii WYŁ."</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Oszczędzanie baterii WŁ."</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Ustawienia"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Pomoc"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Asystent głosowy"</string>
@@ -282,6 +279,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Lokalizacja"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"dostęp do informacji o lokalizacji tego urządzenia"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do lokalizacji urządzenia?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikacja będzie mieć dostęp do lokalizacji tylko wtedy, gdy jest używana."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Zawsze zezwalać aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> na dostęp do lokalizacji urządzenia?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikacja będzie zawsze mieć dostęp do lokalizacji, nawet wtedy, gdy nie jest używana."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendarz"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"dostęp do kalendarza"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do kalendarza?"</string>
@@ -408,12 +408,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ta aplikacja może dodawać, usuwać i zmieniać wydarzenia z kalendarza na telefonie. Ta aplikacja może wysyłać wiadomości wyglądające jak utworzone przez właścicieli kalendarza lub zmieniać wydarzenia bez wiedzy ich właścicieli."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"dostęp do dodatkowych poleceń dostawcy informacji o lokalizacji"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Pozwala aplikacji na dostęp do dodatkowych poleceń dostawcy informacji o lokalizacji. Aplikacje z tym uprawnieniem mogą wpływać na działanie GPS-a lub innych źródeł lokalizacji."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"dostęp do dokładnej lokalizacji (na podstawie GPS-a i sieci)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Ta aplikacja może określać Twoją lokalizację na podstawie GPS-a lub sieciowych źródeł lokalizacji, takich jak stacje bazowe i sieci Wi-Fi. Te usługi lokalizacyjne muszą być włączone i dostępne na telefonie, by aplikacja mogła z nich korzystać. Może to zwiększyć zużycie baterii."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"dostęp do dokładnej lokalizacji tylko na pierwszym planie"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ta aplikacja może określić Twoją dokładną lokalizację tylko wtedy, gdy działa na pierwszym planie. Te usługi lokalizacyjne muszą być włączone i dostępne na telefonie, by aplikacja mogła z nich korzystać. Może to zwiększyć zużycie baterii."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"dostęp do przybliżonej lokalizacji (na podstawie sieci)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ta aplikacja może określać Twoją lokalizację na podstawie źródeł sieciowych, takich jak stacje bazowe i sieci Wi-Fi. Te usługi lokalizacyjne muszą być włączone i dostępne na tablecie, by aplikacja mogła z nich korzystać."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ta aplikacja może określać Twoją lokalizację na podstawie źródeł sieciowych, takich jak stacje bazowe i sieci Wi-Fi. Te usługi lokalizacyjne muszą być włączone i dostępne na telewizorze, by aplikacja mogła z nich korzystać."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ta aplikacja może określać Twoją lokalizację na podstawie źródeł sieciowych, takich jak stacje bazowe i sieci Wi-Fi. Te usługi lokalizacyjne muszą być włączone i dostępne na telefonie, by aplikacja mogła z nich korzystać."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"dostęp do dokładnej lokalizacji w tle"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ta aplikacja może określić Twoją dokładną lokalizację w dowolnym momencie, działając w tle. Te usługi lokalizacyjne muszą być włączone i dostępne na telefonie, by aplikacja mogła z nich korzystać. Może to zwiększyć zużycie baterii."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"zmienianie ustawień audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Pozwala aplikacji na modyfikowanie globalnych ustawień dźwięku, takich jak głośność oraz urządzenie wyjściowe."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"nagrywanie dźwięku"</string>
@@ -521,6 +523,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona odcisku palca"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"zarządzanie sprzętem do uwierzytelniania za pomocą twarzy"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Zezwala na aktywowanie przez aplikację metody dodawania i usuwania szablonów twarzy."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"używanie sprzętu do uwierzytelniania za pomocą twarzy"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Zezwala na używanie przez aplikację sprzętu do analizy twarzy na potrzeby uwierzytelniania"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Nie można przeanalizować twarzy. Spróbuj ponownie."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Zbyt jasna twarz. Spróbuj w ciemniejszym miejscu."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Zbyt ciemna twarz. Spróbuj w jaśniejszym miejscu."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Oddal czujnik od twarzy."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Przybliż czujnik do twarzy."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Przesuń czujnik wyżej."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Przesuń czujnik niżej."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Przesuń czujnik w prawo."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Przesuń czujnik w lewo."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Spójrz na czujnik."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nie wykryto twarzy."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Nie ruszaj twarzą, patrząc na urządzenie."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Czujnik twarzy nie jest dostępny."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Upłynął limit czasu analizy twarzy. Spróbuj ponownie."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Nie można zapisać informacji o twarzy."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Analiza twarzy została anulowana."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Zbyt wiele prób. Spróbuj ponownie później."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Zbyt wiele prób. Wyłączono uwierzytelnianie za pomocą twarzy."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Spróbuj ponownie."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nie zarejestrowano twarzy."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"To urządzenie nie jest wyposażone w czujnik twarzy"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Twarz <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona twarzy"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"czytanie ustawień synchronizacji"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Zezwala aplikacji na odczyt ustawień synchronizacji konta. Pozwala to na przykład określić, czy aplikacja Ludzie jest zsynchronizowana z kontem."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"włączanie i wyłączanie synchronizacji"</string>
@@ -610,7 +643,7 @@
     <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Wymaganie szyfrowania przechowywanych danych aplikacji"</string>
     <string name="policylab_disableCamera" msgid="6395301023152297826">"Wyłącz aparaty"</string>
     <string name="policydesc_disableCamera" msgid="2306349042834754597">"Zapobieganie używaniu wszystkich aparatów w urządzeniu"</string>
-    <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Wył. niektórych funkcji bl. ekr."</string>
+    <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Wył. funkcji blokady ekranu"</string>
     <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Zapobieganie użyciu niektórych funkcji blokady ekranu."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Dom"</item>
@@ -1219,6 +1252,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Sieć Wi-Fi nie ma dostępu do internetu"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Kliknij, by wyświetlić opcje"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Zmieniono ustawienia hotspotu"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Zmieniono pasmo hotspotu."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"To urządzenie nie może korzystać tylko z częstotliwości 5 GHz. Będzie korzystać z tego pasma, jeśli będzie ono dostępne."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Zmieniono na połączenie typu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Urządzenie korzysta z połączenia typu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, gdy <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nie dostępu do internetu. Mogą zostać naliczone opłaty."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Przełączono z połączenia typu <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>."</string>
@@ -1450,7 +1486,7 @@
     <string name="add_account_button_label" msgid="3611982894853435874">"Dodaj konto"</string>
     <string name="number_picker_increment_button" msgid="2412072272832284313">"Zwiększ"</string>
     <string name="number_picker_decrement_button" msgid="476050778386779067">"Zmniejsz"</string>
-    <string name="number_picker_increment_scroll_mode" msgid="5259126567490114216">"<xliff:g id="VALUE">%s</xliff:g> dotknij i przytrzymaj."</string>
+    <string name="number_picker_increment_scroll_mode" msgid="5259126567490114216">"<xliff:g id="VALUE">%s</xliff:g> naciśnij i przytrzymaj."</string>
     <string name="number_picker_increment_scroll_action" msgid="9101473045891835490">"Przesuń w górę, by zwiększyć, i w dół, by zmniejszyć."</string>
     <string name="time_picker_increment_minute_button" msgid="8865885114028614321">"Zmień minutę na późniejszą"</string>
     <string name="time_picker_decrement_minute_button" msgid="6246834937080684791">"Zmień minutę na wcześniejszą"</string>
@@ -1477,7 +1513,7 @@
     <string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"Nie udało się uruchomić aplikacji <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="shareactionprovider_share_with" msgid="806688056141131819">"Udostępnij przez:"</string>
     <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Udostępnij przez <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="content_description_sliding_handle" msgid="415975056159262248">"Uchwyt przesuwny. Dotknij i przytrzymaj."</string>
+    <string name="content_description_sliding_handle" msgid="415975056159262248">"Uchwyt przesuwny. Naciśnij i przytrzymaj."</string>
     <string name="description_target_unlock_tablet" msgid="3833195335629795055">"Przesuń, aby odblokować."</string>
     <string name="action_bar_home_description" msgid="5293600496601490216">"Przejdź do strony głównej"</string>
     <string name="action_bar_up_description" msgid="2237496562952152589">"Przejdź wyżej"</string>
diff --git a/core/res/res/values-port/dimens_package_installer.xml b/core/res/res/values-port/dimens_package_installer.xml
new file mode 100644
index 0000000..af28713
--- /dev/null
+++ b/core/res/res/values-port/dimens_package_installer.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+
+<!-- portrait dimensions for the permission grant dialog. -->
+<resources>
+    <!-- This yields 95% width -->
+    <dimen name="permissionGrantDialogWeight">380</dimen>
+    <dimen name="permissionGrantDialogWidth">0dp</dimen>
+</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index cb19ed3..6c0391c 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo avião"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modo avião ATIVADO"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modo avião DESATIVADO"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Economia de bateria"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"A Economia de bateria está DESATIVADA"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"A Economia de bateria está ATIVADA"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Configurações"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assistência"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ajuda de voz"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Local"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"acesse o local do dispositivo"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse a localização deste dispositivo?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"O app só terá acesso ao local enquanto estiver sendo usado."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Sempre permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse o local deste disp.?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"O app sempre terá acesso ao local, mesmo quando não estiver sendo usado."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acesse sua agenda"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse sua agenda?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Este app pode adicionar, remover ou alterar eventos da agenda no seu smartphone. Ele também pode enviar mensagens que aparentem ser de autoria do proprietário da agenda ou alterar eventos sem notificar o proprietário."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acessar comandos extras do provedor de localização"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que o app acesse comandos do provedor não relacionados à localização. Isso pode permitir que o app interfira no funcionamento do GPS ou de outras fontes de localização."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"acessar localização precisa (GPS e com base na rede)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Este app pode ver seu local com base no GPS ou nas fontes de localização da rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los. Isso pode aumentar o consumo de bateria."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"acessar localização precisa apenas em primeiro plano"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Este app pode ver sua localização exata a qualquer momento apenas quando está em primeiro plano. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los. Isso pode aumentar o consumo de bateria."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acessar localização aproximada (com base na rede)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis no seu tablet para que o app possa usá-los."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis na sua TV para que o app possa usá-los."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"acessar localização precisa em segundo plano"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Este app pode ver sua localização exata a qualquer momento quando está em segundo plano. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los. Isso pode aumentar o consumo de bateria."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"alterar as suas configurações de áudio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que o app modifique configurações de áudio globais como volume e alto-falantes de saída."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
@@ -433,9 +435,9 @@
     <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"impedir modo de inatividade do tablet"</string>
     <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"impedir a suspensão da TV"</string>
     <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"impedir modo de inatividade do telefone"</string>
-    <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Permite que o app impeça o tablet de entrar no modo de inatividade."</string>
+    <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Permite que o app impeça a suspensão do tablet."</string>
     <string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"Permite que o app impeça a suspensão da TV."</string>
-    <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"Permite que o app impeça o telefone de entrar no modo de inatividade."</string>
+    <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"Permite que o app impeça a suspensão do telefone."</string>
     <string name="permlab_transmitIr" msgid="7545858504238530105">"transmitir infravermelhos"</string>
     <string name="permdesc_transmitIr" product="tablet" msgid="5358308854306529170">"Permite que o app use o transmissor infravermelho do tablet."</string>
     <string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"Permite que o app use o transmissor de infravermelho da TV."</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ícone de impressão digital"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"gerenciar hardware de autenticação facial"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que o app execute métodos para adicionar e excluir modelos de rosto para uso."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"usar hardware de autenticação facial"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite que o app use o hardware de autenticação facial para autenticação"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Falha ao processar o rosto. Tente novamente."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Rosto muito iluminado. Tente com menos iluminação."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Rosto muito escuro. Acrescente uma fonte de luz."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Afaste o sensor do rosto."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Aproxime o sensor do rosto."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Mova o sensor para cima."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Mova o sensor para baixo."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Mova o sensor para a direita."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Mova o sensor para a esquerda."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Olhe para o sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nenhum rosto detectado."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantenha o rosto parado na frente do dispositivo."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware de rosto não disponível."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Tempo máx. p/ captura facial atingido. Tente novamente."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Não é possível armazenar um rosto."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Operação facial cancelada."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Excesso de tentativas. Tente novamente mais tarde."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Excesso de tentativas. Autenticação facial desat."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Tente novamente."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nenhum rosto registrado."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Este dispositivo não tem um sensor de autenticação facial"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Rosto <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ícone facial"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler as configurações de sincronização"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que o app leia as configurações de sincronização de uma conta. Por exemplo, pode determinar se o app People está sincronizado com uma conta."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ativar e desativar sincronização"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"O Wi‑Fi não tem acesso à Internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toque para ver opções"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Mudanças nas suas configurações de ponto de acesso"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Sua banda de ponto de acesso foi alterada."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Este dispositivo não é compatível com sua preferência apenas por 5 GHz. Em vez disso, o dispositivo usará a banda de 5 GHz quando ela estiver disponível."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1395,7 +1431,7 @@
     <string name="gpsVerifYes" msgid="2346566072867213563">"Sim"</string>
     <string name="gpsVerifNo" msgid="1146564937346454865">"Não"</string>
     <string name="sync_too_many_deletes" msgid="5296321850662746890">"Limite de exclusão excedido"</string>
-    <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"Há <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> itens excluídos para <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, conta <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. O que você deseja fazer?"</string>
+    <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"Há <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> itens excluídos para <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, conta <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. O que você quer fazer?"</string>
     <string name="sync_really_delete" msgid="2572600103122596243">"Excluir os itens"</string>
     <string name="sync_undo_deletes" msgid="2941317360600338602">"Desfazer as exclusões"</string>
     <string name="sync_do_nothing" msgid="3743764740430821845">"Não fazer nada por enquanto"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index d161986..597be12 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo de avião"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"O modo de voo está ativado"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"O modo de voo está desativado"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Poupança de bateria"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Poupança de bateria DeSATIVADA"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Poupança de bateria ATIVADA"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Definições"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assistência"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Assist. de voz"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Localização"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"aceder à localização do seu dispositivo"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Pretende permitir que a aplicação &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda à localização deste dispositivo?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"A aplicação tem acesso à localização apenas quando a estiver a utilizar."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Pretende permitir que a aplicação &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda sempre à localização deste dispositivo?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"A aplicação terá sempre acesso à localização, mesmo quando não está a utilizá-la."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendário"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"aceder ao calendário"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Pretende permitir que a aplicação &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda ao calendário?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Esta aplicação pode adicionar, remover ou alterar eventos do calendário no seu telemóvel. Esta aplicação pode enviar mensagens que parecem vir de proprietários do calendário ou alterar eventos sem notificar os respetivos proprietários."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"aceder a comandos adicionais do fornecedor de localização"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que a aplicação aceda a comandos adicionais do fornecedor de localização. Esta opção pode permitir que a aplicação interfira com o funcionamento do GPS ou de outras fontes de localização."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"aceder à localização exata (baseada no GPS e na rede)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Esta aplicação pode obter a sua localização com base em fontes de localização da rede e de GPS, tais como torres de redes móveis e redes Wi-Fi. É necessário que estes serviços de localização estejam ativados e disponíveis no seu telemóvel para que a aplicação os possa utilizar. Esta ação pode aumentar o consumo da bateria."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"apenas aceder à localização exata em primeiro plano"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Esta aplicação apenas pode obter a sua localização exata quando estiver em primeiro plano. É necessário que estes Serviços de localização estejam ativados e disponíveis no seu telemóvel para que a aplicação os possa utilizar. Esta ação pode aumentar o consumo da bateria."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"aceder à localização aproximada (baseada na rede)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Esta aplicação pode obter a sua localização com base em fontes de rede, tais como torres de redes móveis e redes Wi-Fi. É necessário que estes serviços de localização estejam ativados e disponíveis no seu tablet para que a aplicação os possa utilizar."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Esta aplicação pode obter a sua localização com base em fontes de rede, tais como torres de redes móveis e redes Wi-Fi. É necessário que estes serviços de localização estejam ativados e disponíveis na sua TV para que a aplicação os possa utilizar."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Esta aplicação pode obter a sua localização com base em fontes de rede, tais como torres de redes móveis e redes Wi-Fi. É necessário que estes serviços de localização estejam ativados e disponíveis no seu telemóvel para que a aplicação os possa utilizar."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"aceder à localização exata em segundo plano"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Esta aplicação pode obter a sua localização exata sempre que estiver em segundo plano. É necessário que estes Serviços de localização estejam ativados e disponíveis no seu telemóvel para que a aplicação os possa utilizar. Esta ação pode aumentar o consumo da bateria."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"alterar as suas definições de áudio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que a aplicação modifique definições de áudio globais, tais como o volume e qual o altifalante utilizado para a saída de som."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ícone de impressão digital"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"gerir hardware de autenticação facial"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite à aplicação invocar métodos para adicionar e eliminar modelos faciais para uso."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"utilizar hardware de autenticação facial"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite que a aplicação utilize hardware de autenticação facial para autenticação."</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Não foi possível processar o rosto. Tente de novo."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Rosto demasiado claro. Experimente com menos luz."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Rosto demasiado escuro. Destape a fonte de luz."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Afaste o sensor do rosto."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Aproxime o sensor do rosto."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Mova o sensor para cima."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Mova o sensor para baixo."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Mova o sensor para a direita."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Mova o sensor para a esquerda."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Olhe para o sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nenhum rosto detetado."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantenha o rosto parado em frente ao dispositivo."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"O hardware de rosto não está disponível."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Limite de tempo de rosto atingido. Tente novamente."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Não é possível armazenar o rosto."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Operação de rosto cancelada."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Demasiadas tentativas. Tente novamente mais tarde."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Demasiadas tentativas. Autenticação facial desativada."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Tente novamente."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nenhum rosto inscrito."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Este dispositivo não tem sensor de autenticação facial."</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Rosto <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ícone de rosto"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler definições de sincronização"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que a aplicação leia as definições de sincronização de uma conta. Por exemplo, pode determinar se a aplicação Pessoas está sincronizada com uma conta."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ativar e desativar a sincronização"</string>
@@ -585,13 +618,13 @@
     <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear a TV ou apagar todos os dados deste utilizador se forem introduzidas demasiadas palavras-passe incorretas."</string>
     <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear o telemóvel ou apagar todos os dados deste utilizador se forem introduzidas demasiadas palavras-passe incorretas."</string>
     <string name="policylab_resetPassword" msgid="4934707632423915395">"Alterar o bloqueio de ecrã"</string>
-    <string name="policydesc_resetPassword" msgid="1278323891710619128">"Alterar o bloqueio de ecrã."</string>
+    <string name="policydesc_resetPassword" msgid="1278323891710619128">"Altera o bloqueio de ecrã."</string>
     <string name="policylab_forceLock" msgid="2274085384704248431">"Bloquear o ecrã"</string>
-    <string name="policydesc_forceLock" msgid="1141797588403827138">"Controlar como e quando ocorre o bloqueio do ecrã."</string>
+    <string name="policydesc_forceLock" msgid="1141797588403827138">"Controla como e quando ocorre o bloqueio do ecrã."</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Apagar todos os dados"</string>
     <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Apagar os dados do tablet sem avisar através de uma reposição de dados de fábrica."</string>
     <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Apagar os dados da TV sem aviso prévio ao executar uma reposição de dados de fábrica."</string>
-    <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Apagar os dados do telemóvel sem avisar através de uma reposição de dados de fábrica."</string>
+    <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Apaga os dados do telemóvel sem avisar ao efetuar uma reposição de dados de fábrica."</string>
     <string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Apagar os dados do utilizador"</string>
     <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"Apagar os dados deste utilizador neste tablet sem aviso."</string>
     <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Apagar os dados deste utilizador nesta TV sem aviso."</string>
@@ -605,7 +638,7 @@
     <string name="policylab_disableCamera" msgid="6395301023152297826">"Desativar câmaras"</string>
     <string name="policydesc_disableCamera" msgid="2306349042834754597">"Evitar a utilização de todas as câmaras do dispositivo."</string>
     <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Desat. funcionalid. bloq. ecrã"</string>
-    <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Impeça a utilização de algumas funcionalidades de bloqueio de ecrã."</string>
+    <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Impede a utilização de algumas funcionalidades de bloqueio de ecrã."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Casa"</item>
     <item msgid="869923650527136615">"Móvel"</item>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"O Wi-Fi não tem acesso à Internet."</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toque para obter mais opções"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Alterações às definições de zona Wi-Fi"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"A banda da sua zona Wi-Fi foi alterada."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Este dispositivo não suporta a sua preferência apenas para 5 GHz. Em alternativa, este dispositivo vai utilizar a banda de 5 GHz quando estiver disponível."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Mudou para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Podem aplicar-se custos."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Mudou de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index cb19ed3..6c0391c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo avião"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modo avião ATIVADO"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modo avião DESATIVADO"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Economia de bateria"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"A Economia de bateria está DESATIVADA"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"A Economia de bateria está ATIVADA"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Configurações"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assistência"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ajuda de voz"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Local"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"acesse o local do dispositivo"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse a localização deste dispositivo?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"O app só terá acesso ao local enquanto estiver sendo usado."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Sempre permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse o local deste disp.?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"O app sempre terá acesso ao local, mesmo quando não estiver sendo usado."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acesse sua agenda"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse sua agenda?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Este app pode adicionar, remover ou alterar eventos da agenda no seu smartphone. Ele também pode enviar mensagens que aparentem ser de autoria do proprietário da agenda ou alterar eventos sem notificar o proprietário."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acessar comandos extras do provedor de localização"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que o app acesse comandos do provedor não relacionados à localização. Isso pode permitir que o app interfira no funcionamento do GPS ou de outras fontes de localização."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"acessar localização precisa (GPS e com base na rede)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Este app pode ver seu local com base no GPS ou nas fontes de localização da rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los. Isso pode aumentar o consumo de bateria."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"acessar localização precisa apenas em primeiro plano"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Este app pode ver sua localização exata a qualquer momento apenas quando está em primeiro plano. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los. Isso pode aumentar o consumo de bateria."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acessar localização aproximada (com base na rede)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis no seu tablet para que o app possa usá-los."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis na sua TV para que o app possa usá-los."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"acessar localização precisa em segundo plano"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Este app pode ver sua localização exata a qualquer momento quando está em segundo plano. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los. Isso pode aumentar o consumo de bateria."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"alterar as suas configurações de áudio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que o app modifique configurações de áudio globais como volume e alto-falantes de saída."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
@@ -433,9 +435,9 @@
     <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"impedir modo de inatividade do tablet"</string>
     <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"impedir a suspensão da TV"</string>
     <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"impedir modo de inatividade do telefone"</string>
-    <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Permite que o app impeça o tablet de entrar no modo de inatividade."</string>
+    <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Permite que o app impeça a suspensão do tablet."</string>
     <string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"Permite que o app impeça a suspensão da TV."</string>
-    <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"Permite que o app impeça o telefone de entrar no modo de inatividade."</string>
+    <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"Permite que o app impeça a suspensão do telefone."</string>
     <string name="permlab_transmitIr" msgid="7545858504238530105">"transmitir infravermelhos"</string>
     <string name="permdesc_transmitIr" product="tablet" msgid="5358308854306529170">"Permite que o app use o transmissor infravermelho do tablet."</string>
     <string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"Permite que o app use o transmissor de infravermelho da TV."</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ícone de impressão digital"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"gerenciar hardware de autenticação facial"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que o app execute métodos para adicionar e excluir modelos de rosto para uso."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"usar hardware de autenticação facial"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite que o app use o hardware de autenticação facial para autenticação"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Falha ao processar o rosto. Tente novamente."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Rosto muito iluminado. Tente com menos iluminação."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Rosto muito escuro. Acrescente uma fonte de luz."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Afaste o sensor do rosto."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Aproxime o sensor do rosto."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Mova o sensor para cima."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Mova o sensor para baixo."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Mova o sensor para a direita."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Mova o sensor para a esquerda."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Olhe para o sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nenhum rosto detectado."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantenha o rosto parado na frente do dispositivo."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware de rosto não disponível."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Tempo máx. p/ captura facial atingido. Tente novamente."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Não é possível armazenar um rosto."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Operação facial cancelada."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Excesso de tentativas. Tente novamente mais tarde."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Excesso de tentativas. Autenticação facial desat."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Tente novamente."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nenhum rosto registrado."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Este dispositivo não tem um sensor de autenticação facial"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Rosto <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ícone facial"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler as configurações de sincronização"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que o app leia as configurações de sincronização de uma conta. Por exemplo, pode determinar se o app People está sincronizado com uma conta."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ativar e desativar sincronização"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"O Wi‑Fi não tem acesso à Internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toque para ver opções"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Mudanças nas suas configurações de ponto de acesso"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Sua banda de ponto de acesso foi alterada."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Este dispositivo não é compatível com sua preferência apenas por 5 GHz. Em vez disso, o dispositivo usará a banda de 5 GHz quando ela estiver disponível."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1395,7 +1431,7 @@
     <string name="gpsVerifYes" msgid="2346566072867213563">"Sim"</string>
     <string name="gpsVerifNo" msgid="1146564937346454865">"Não"</string>
     <string name="sync_too_many_deletes" msgid="5296321850662746890">"Limite de exclusão excedido"</string>
-    <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"Há <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> itens excluídos para <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, conta <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. O que você deseja fazer?"</string>
+    <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"Há <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> itens excluídos para <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, conta <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. O que você quer fazer?"</string>
     <string name="sync_really_delete" msgid="2572600103122596243">"Excluir os itens"</string>
     <string name="sync_undo_deletes" msgid="2941317360600338602">"Desfazer as exclusões"</string>
     <string name="sync_do_nothing" msgid="3743764740430821845">"Não fazer nada por enquanto"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 4b598e2..de01af6 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -239,13 +239,10 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mod Avion"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modul Avion este ACTIVAT"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modul avion este DEZACTIVAT"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Economisirea energiei"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Economisirea bateriei este dezactivată"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Economisirea bateriei este activată"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Setări"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Asistență"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Asistent vocal"</string>
-    <string name="global_action_lockdown" msgid="1099326950891078929">"Blocați"</string>
+    <string name="global_action_lockdown" msgid="1099326950891078929">"Blocare strictă"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"˃999"</string>
     <string name="notification_hidden_text" msgid="6351207030447943784">"Notificare nouă"</string>
     <string name="notification_channel_virtual_keyboard" msgid="6969925135507955575">"Tastatură virtuală"</string>
@@ -279,6 +276,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Locație"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"acceseze locația acestui dispozitiv"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Permiteți &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze locația acestui dispozitiv?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplicația va avea acces la locație doar atunci când o folosiți."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Permiteți &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze locația dispozitivului?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplicația va avea în permanență acces la locație, chiar și atunci când nu o folosiți."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceseze calendarul"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Permiteți &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să vă acceseze calendarul?"</string>
@@ -405,12 +405,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Această aplicație poate să adauge, să elimine sau să modifice evenimente din calendarul de pe telefon. Această aplicație poate să trimită mesaje care par trimise de proprietarii calendarului sau să modifice evenimentele fără notificarea acestora."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accesare comenzi suplimentare ale furnizorului locației"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite aplicației să acceseze comenzi suplimentare pentru furnizorul locației. Aplicația ar putea să utilizeze această permisiune pentru a influența operațiile GPS sau ale altor surse de locații."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"să acceseze locația exactă (bazată pe GPS și pe rețea)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Această aplicație vă poate obține locația cu ajutorul sistemului GPS sau al surselor de localizare ale rețelei, cum ar fi turnurile de telefonie mobilă și rețelele Wi-Fi. Aceste servicii de localizare trebuie să fie activate și disponibile pe telefon pentru ca aplicația să le poată folosi. Acest lucru ar putea accelera descărcarea bateriei."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"să acceseze locația exactă în prim-plan"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Aplicația vă poate obține locația exactă numai când rulează în prim-plan. Serviciile de localizare trebuie să fie activate și disponibile pe telefon pentru ca aplicația să le poată folosi. Acest lucru poate accelera descărcarea bateriei."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"să acceseze locația aproximativă (bazată pe rețea)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Această aplicație vă poate obține locația cu ajutorul surselor rețelei, cum ar fi turnurile de telefonie mobilă și rețelele Wi-Fi. Aceste servicii de localizare trebuie să fie activate și disponibile pe tabletă pentru ca aplicația să le poată folosi."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Această aplicație vă poate obține locația cu ajutorul surselor rețelei, cum ar fi turnurile de telefonie mobilă și rețelele Wi-Fi. Aceste servicii de localizare trebuie să fie activate și disponibile pe televizor pentru ca aplicația să le poată folosi."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Această aplicație vă poate obține locația cu ajutorul surselor rețelei, cum ar fi turnurile de telefonie mobilă și rețelele Wi-Fi. Aceste servicii de localizare trebuie să fie activate și disponibile pe telefon pentru ca aplicația să le poată folosi."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"să acceseze locația exactă în fundal"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Aplicația vă poate obține locația exactă ori de câte ori rulează în fundal. Serviciile de localizare trebuie să fie activate și disponibile pe telefon pentru ca aplicația să le poată folosi. Acest lucru poate accelera descărcarea bateriei."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modificare setări audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite aplicației să modifice setările audio globale, cum ar fi volumul și difuzorul care este utilizat pentru ieșire."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"înregistreze sunet"</string>
@@ -518,6 +520,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Pictograma amprentă"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"să gestioneze hardware-ul de autentificare facială"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite aplicației să invoce metode pentru a adăuga și a șterge șabloane faciale pentru utilizare."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"să folosească hardware de autentificare facială"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite aplicației să folosească hardware de autentificare facială pentru autentificare"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Chipul nu a putut fi procesat. Încercați din nou."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Chip prea luminat. Încercați cu mai puțină lumină."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Chip prea întunecat. Măriți sursa de lumină."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Deplasați senzorul mai departe de chip."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Aduceți senzorul mai aproape de chip."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Deplasați senzorul mai sus."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Deplasați senzorul mai jos."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Deplasați senzorul spre dreapta."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Deplasați senzorul spre stânga."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Priviți spre senzor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nu s-a detectat niciun chip."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Țineți chipul nemișcat în fața dispozitivului."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware-ul pentru chip nu este disponibil."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Timpul pentru reunoaștere facială a expirat. Încercați din nou."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Chipul nu poate fi stocat."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Operațiunea privind chipul a fost anulată."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Prea multe încercări. Reîncercați mai târziu."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Prea multe încercări. Autentificarea facială este dezactivată."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Încercați din nou."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nu au fost înregistrate chipuri."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Dispozitivul nu are un senzor de autentificare a chipului"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Chip <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Pictograma chip"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"citire setări sincronizare"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite aplicației să citească setările de sincronizare ale unui cont. De exemplu, cu această permisiune aplicația poate determina dacă aplicația Persoane este sincronizată cu un anumit cont."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activează/dezactivează sincronizarea"</string>
@@ -1197,6 +1230,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Rețeaua Wi-Fi nu are acces la internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Atingeți pentru opțiuni"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Modificări aduse setărilor pentru hotspot"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"S-a schimbat banda de frecvență a hotspotului."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Dispozitivul nu acceptă doar preferința pentru 5 GHz. Dispozitivul va folosi banda de 5 GHz când este disponibilă."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"S-a comutat la <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Dispozitivul folosește <xliff:g id="NEW_NETWORK">%1$s</xliff:g> când <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nu are acces la internet. Se pot aplica taxe."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"S-a comutat de la <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> la <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 893018a..63a8574 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -242,9 +242,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Режим полета"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Выключить"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Включить"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Режим энергосбережения"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Выключить"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Включить"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Настройки"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Помощник"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Аудиоподсказки"</string>
@@ -278,22 +275,25 @@
     <string name="managed_profile_label" msgid="8947929265267690522">"Переключиться на рабочий профиль"</string>
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакты"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"доступ к контактам"</string>
-    <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Разрешить приложению &lt;b&gt;\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"&lt;/b&gt; доступ к контактам?"</string>
+    <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к контактам?"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Местоположение"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"доступ к данным о местоположении устройства"</string>
-    <string name="permgrouprequest_location" msgid="3788275734953323491">"Разрешить приложению &lt;b&gt;\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"&lt;/b&gt; доступ к данным о местоположении устройства?"</string>
+    <string name="permgrouprequest_location" msgid="3788275734953323491">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к данным о местоположении устройства?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Доступ будет открыт, только когда вы пользуетесь приложением."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Всегда разрешать приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к геоданным устройства?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Доступ будет открыт, даже когда вы не пользуетесь приложением."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Календарь"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"доступ к календарю"</string>
-    <string name="permgrouprequest_calendar" msgid="289900767793189421">"Разрешить приложению &lt;b&gt;\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"&lt;/b&gt; доступ к календарю?"</string>
+    <string name="permgrouprequest_calendar" msgid="289900767793189421">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к календарю?"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="4656988620100940350">"отправлять и просматривать SMS-сообщения"</string>
-    <string name="permgrouprequest_sms" msgid="7168124215838204719">"Разрешить приложению &lt;b&gt;\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"&lt;/b&gt; отправлять и просматривать SMS?"</string>
+    <string name="permgrouprequest_sms" msgid="7168124215838204719">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; отправлять и просматривать SMS?"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"Хранилище"</string>
     <string name="permgroupdesc_storage" msgid="637758554581589203">"доступ к фото, мультимедиа и файлам на вашем устройстве"</string>
     <string name="permgrouprequest_storage" msgid="7885942926944299560">"Разрешить приложению &lt;b&gt;\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"&lt;/b&gt; доступ к фото, мультимедиа и файлам на устройстве?"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"записывать аудио"</string>
-    <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Разрешить приложению &lt;b&gt;\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"&lt;/b&gt; записывать аудио?"</string>
+    <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Разрешить приложению &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>
@@ -302,7 +302,7 @@
     <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к списку вызовов?"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Телефон"</string>
     <string name="permgroupdesc_phone" msgid="6234224354060641055">"осуществлять вызовы и управлять ими"</string>
-    <string name="permgrouprequest_phone" msgid="9166979577750581037">"Разрешить приложению &lt;b&gt;\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"&lt;/b&gt; совершать звонки и управлять ими?"</string>
+    <string name="permgrouprequest_phone" msgid="9166979577750581037">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; совершать звонки и управлять ими?"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Нательные датчики"</string>
     <string name="permgroupdesc_sensors" msgid="7147968539346634043">"доступ к данным датчиков о состоянии организма"</string>
     <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Разрешить приложению &lt;b&gt;\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"&lt;/b&gt; доступ к данным датчиков о состоянии организма?"</string>
@@ -408,12 +408,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Приложение может добавлять, удалять или изменять мероприятия в календаре на телефоне. Оно также может отправлять сообщения от имени владельцев календаря, а также изменять мероприятия без ведома владельца."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Доступ к дополнительным командам управления источниками геоданных"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Доступ к дополнительным командам управления источниками геоданных и вмешательство в работу системы GPS или других источников геоданных."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"Доступ к точному местоположению (по координатам сети и спутникам GPS)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Приложение может получать сведения о вашем местоположении от GPS-датчика или сетевых источников, таких как вышки сотовой связи и точки доступа Wi-Fi. Необходимо включить соответствующие параметры на телефоне и разрешить приложению использовать геоданные. Может увеличиться расход заряда батареи."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"Доступ к точному местоположению только в фоновом режиме"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Приложение может получать сведения о вашем точном местоположении только в фоновом режиме. Для этого необходимо включить соответствующие параметры на телефоне и разрешить использовать геоданные. Может увеличиться расход заряда батареи."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"Доступ к примерному местоположению (по координатам сети)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Приложение может получать сведения о вашем местоположении от сетевых источников, таких как вышки сотовой связи и точки доступа Wi-Fi. Необходимо включить соответствующие параметры на планшете и разрешить приложению использовать геоданные."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Приложение может получать сведения о вашем местоположении от сетевых источников, таких как вышки сотовой связи и точки доступа Wi-Fi. Необходимо включить соответствующие параметры на телевизоре и разрешить приложению использовать геоданные."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Приложение может получать сведения о вашем местоположении от сетевых источников, таких как вышки сотовой связи и точки доступа Wi-Fi. Необходимо включить соответствующие параметры на телефоне и разрешить приложению использовать геоданные."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"Доступ к геоданным в фоновом режиме"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Приложение может получать сведения о вашем точном местоположении в фоновом режиме. Для этого необходимо включить соответствующие параметры на телефоне и разрешить использовать геоданные. Может увеличиться расход заряда батареи."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"Изменение настроек аудио"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Приложение сможет изменять системные настройки звука, например уровень громкости и активный динамик."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"Запись аудио"</string>
@@ -475,16 +477,16 @@
     <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Получение пакетов, отправляемых на все устройства в сети Wi-Fi, а не только на телевизор. При этом используется больше электроэнергии, чем в обычном режиме."</string>
     <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Приложение сможет получать пакеты, отправленные всем устройствам в сети Wi-Fi (не только вашему телефону) в многоадресной рассылке. При этом расходуется больше энергии, чем в одноадресном режиме."</string>
     <string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"Доступ к настройкам Bluetooth"</string>
-    <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Приложение сможет настраивать параметры локального планшетного ПК с поддержкой Bluetooth, а также обнаруживать удаленные устройства и выполнять сопряжение с ними."</string>
-    <string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Изменение параметров Bluetooth на телевизоре, обнаружение устройств и подключение к ним."</string>
-    <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Приложение сможет настраивать параметры локального телефона с поддержкой Bluetooth, а также обнаруживать удаленные устройства и выполнять сопряжение с ними."</string>
+    <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Приложение сможет настраивать параметры локального планшетного ПК с поддержкой Bluetooth, а также обнаруживать удаленные устройства и устанавливать соединение с ними."</string>
+    <string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Изменение параметров Bluetooth на телевизоре, обнаружение устройств и установление соединения с ними."</string>
+    <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Приложение сможет настраивать параметры локального телефона с поддержкой Bluetooth, а также обнаруживать удаленные устройства и устанавливать соединение с ними."</string>
     <string name="permlab_accessWimaxState" msgid="4195907010610205703">"подключать/отключать сеть WiMAX"</string>
     <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Приложение сможет определять, активирован ли WiMAX, а также получать информацию о подключенных сетях WiMAX."</string>
     <string name="permlab_changeWimaxState" msgid="340465839241528618">"Изменение статуса WiMAX"</string>
     <string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Приложение сможет подключать устройство к сетям WiMAX и отключать его от них."</string>
     <string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Подключение телевизора к сетям WiMAX и его отключение от них."</string>
     <string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Приложение сможет подключать устройство к сетям WiMAX и отключать его от них."</string>
-    <string name="permlab_bluetooth" msgid="6127769336339276828">"Установление связи с устройствами Bluetooth"</string>
+    <string name="permlab_bluetooth" msgid="6127769336339276828">"Установление соединения с устройствами Bluetooth"</string>
     <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Приложение сможет просматривать конфигурацию Bluetooth на планшетном ПК, а также запрашивать и подтверждать соединение с другими устройствами."</string>
     <string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"Доступ к настройкам Bluetooth на телевизоре, установка соединений с сопряженными устройствами и принятие запросов на подключение."</string>
     <string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Приложение сможет просматривать конфигурацию Bluetooth на телефоне, а также запрашивать и подтверждать соединение с другими устройствами."</string>
@@ -521,6 +523,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Значок отпечатка пальца"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"Управлять оборудованием для распознавания лиц"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Приложение сможет добавлять и удалять шаблоны лиц."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"Использовать оборудование для распознавания лиц"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Приложение сможет использовать распознающее оборудование для аутентификации."</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Не удалось распознать лицо. Повторите попытку."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Лицо слишком яркое. Притушите свет."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Лицо слишком темное. Сделайте свет ярче."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Уберите устройство дальше от лица"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Поднесите устройство ближе к лицу"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Поднимите устройство выше"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Опустите устройство ниже"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Сдвиньте устройство правее"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Сдвиньте устройство левее"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Посмотрите в камеру устройства"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Лица не обнаружены"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Расположите лицо напротив устройства"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Оборудование для распознавания лица недоступно"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Превышено время ожидания. Повторите попытку."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Невозможно сохранить распознанное лицо"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Распознавание отменено"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Слишком много попыток. Повторите позже."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Слишком много попыток. Сканер отключен."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Попробуйте ещё раз"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Нет зарегистрированных лиц"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"На этом устройстве нет сканера для распознавания лица"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Лицо <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Значок лица"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"Просмотр настроек синхронизации"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Приложение сможет просматривать настройки синхронизации аккаунта, например определять, включена ли синхронизация для приложения \"Контакты\"."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"Включение/выключение синхронизации"</string>
@@ -1219,6 +1252,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Сеть Wi-Fi не подключена к Интернету"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Нажмите, чтобы показать варианты."</string>
+    <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">"Устройство не может работать только на частоте 5 ГГц. Эта частота будет использоваться, когда это возможно."</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="NEW_NETWORK">%1$s</xliff:g>, если подключение к сети <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> недоступно. Может взиматься плата за передачу данных."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Устройство отключено от сети <xliff:g id="NEW_NETWORK">%2$s</xliff:g> и теперь использует <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index a24d0fc..a3e3ea0 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"අහස්යානා ආකාරය"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"අහස්යානා ආකාරය සක්‍රීයයි."</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"අහස්යානා අකාරය අක්‍රියයි"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"බැටරි සුරැකුම"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"බැටරි සුරැකුම ක්‍රියාවිරහිතයි"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"බැටරි සුරැකුම ක්‍රියාත්මකයි"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"සැකසීම්"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"සහාය දීම"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"හඬ සහායක"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"ස්ථානය"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"මෙම උපාංගයේ ස්ථානයට ප්‍රවේශ කරන්න"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; වෙත මෙම උපාංගයේ ස්ථානය ලබා ගැනීමට ඉඩ දෙන්නද?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ඔබ යෙදුම භාවිතා විට පමණක් යෙදුමට ස්ථානයට ප්‍රවේශය ඇත."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; වෙත මෙම උපාංගයේ ස්ථානය ලබා ගැනීමට සැම විට ඉඩ දෙන්නද?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ඔබ යෙදුම භාවිතා නොකරන විට පවා යෙදුමට සැම විටම ස්ථානයට ප්‍රවේශය ඇත."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"දින දර්ශනය"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"ඔබේ දින දර්ශනයට පිවිසෙන්න"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; වෙත ඔබගේ දින දර්ශනය ප්‍රවේශ කිරීමට ඉඩ දෙන්නද?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"මෙම යෙදුමට ඔබේ දුරකථනය මත දින දර්ශන සිදුවීම් එක් කිරීමට, ඉවත් කිරීමට, හෝ වෙනස් කිරීමට හැකිය. මෙම යෙදුමට දින දර්ශන හිමිකරුවන් වෙතින් වන ඒවා ලෙස දිස් විය හැකි පණිවිඩ එවීමට, හෝ සිදුවීම්වල හිමිකරුවන්ට දැනුම් දීමෙන් තොරව ඒවා වෙනස් කිරීමට හැකිය."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"අමතර ස්ථාන සැපයුම්කරු විධාන වෙත ප්‍රවේශ වීම"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ස්ථානය සපයන අමතර අණ වලට ප්‍රවේශය කිරීමට යෙදුමට අවසර දෙන්න. GPS ක්‍රියාවන් හෝ වෙනත් ස්ථාන මූලාශ්‍ර සමඟ මැදිහත් වීමට මෙයින් යෙදුමට ඉඩ ලැබේ."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"නිවැරදි ස්ථානයට (GPS සහ ජාලය පදනම් කරගත්) පිවිසීම"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"මෙම යෙදුමට GPS හෝ ජංගම දුරකථන කුළුණු සහ Wi-Fi ජාල වැනි ජාල ස්ථාන මූලාශ්‍ර පදනම්ව ඔබගේ ස්ථානය ලබා ගත හැකිය. යෙදුමට ඒවා භාවිත කිරීමට හැකි වීමට මෙම ස්ථාන සේවා ක්‍රියාත්මක කර සහ ඔබේ දුරකථනය මත ලබා ගත හැකිව තිබිය යුතුය. මෙය බැටරි පාරිභෝජනය වැඩි කළ හැකිය."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"පෙරබිම තුළ පමණක් නිශ්චිත ස්ථානය වෙත පිවිසෙන්න"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"මෙම යෙදුම පෙරබිම තුළ ඇති විට පමණක් එයට ඔබේ නිශ්චිත ස්ථානය ලබා ගත හැකිය. යෙදුමට ඒවා භාවිත කිරීමට හැකි වීමට මෙම ස්ථාන සේවා ක්‍රියාත්මක කර සහ ඔබේ දුරකථනය මත ලබා ගත හැකිව තිබිය යුතුය. මෙය බැටරි පරිභෝජනය වැඩි කළ හැකිය."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ආසන්නතම ස්ථානයට (ජාලය-පාදක වූ) පිවිසීම"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"මෙම යෙදුමට ජංගම දුරකථන කුළුණු සහ Wi-Fi ජාල වැනි ජාල මූලාශ්‍ර පදනම්ව ඔබගේ ස්ථානය ලබා ගත හැකිය. යෙදුමට ඒවා භාවිත කිරීමට හැකි වීමට මෙම ස්ථාන සේවා ක්‍රියාත්මක කර සහ ඔබේ ටැබ්ලට් පරිගණකය මත ලබා ගත හැකිව තිබිය යුතුය."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"මෙම යෙදුමට ජංගම දුරකථන කුළුණු සහ Wi-Fi ජාල වැනි ජාල මූලාශ්‍ර පදනම්ව ඔබගේ ස්ථානය ලබා ගත හැකිය. යෙදුමට ඒවා භාවිත කිරීමට හැකි වීමට මෙම ස්ථාන සේවා ක්‍රියාත්මක කර සහ ඔබේ TV මත ලබා ගත හැකිව තිබිය යුතුය."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"මෙම යෙදුමට ජංගම දුරකථන කුළුණු සහ Wi-Fi ජාල වැනි ජාල මූලාශ්‍ර පදනම්ව ඔබගේ ස්ථානය ලබා ගත හැකිය. යෙදුමට ඒවා භාවිත කිරීමට හැකි වීමට මෙම ස්ථාන සේවා ක්‍රියාත්මක කර සහ ඔබේ දුරකථනය මත ලබා ගත හැකිව තිබිය යුතුය."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"පසුබිම තුළ නිශ්චිත ස්ථානය වෙත පිවිසෙන්න"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"මෙම යෙදුම පසුබිම තුළ ඇති ඕනෑම අවස්ථාවක එයට ඔබේ නිශ්චිත ස්ථානය ලබා ගත හැකිය. යෙදුමට ඒවා භාවිත කිරීමට හැකි වීමට මෙම ස්ථාන සේවා ක්‍රියාත්මක කර සහ ඔබේ දුරකථනය මත ලබා ගත හැකිව තිබිය යුතුය. මෙය බැටරි පරිභෝජනය වැඩි කළ හැකිය."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ඔබගේ ශ්‍රව්‍ය සැකසීම් වෙනස් කරන්න"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ශබ්දය ආදී ගෝලීය ශබ්ද සැකසීම් වෙනස් කිරීමට සහ ප්‍රතිදානය සඳහා භාවිත කරන්නේ කුමන නාදකය දැයි තේරීමට යෙදුමට අවසර දෙන්න."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ශබ්ද පටිගත කරන්න"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ඇඟිලි සලකුණු නිරූපකය"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"මුහුණු සත්‍යාපක දෘඪාංග කළමනාකරණය කරන්න"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"මුහුණු අච්චු එකතු කිරීමට සහ ඉවත් කිරීමට අදාළ ක්‍රම භාවිතය සඳහා මෙම යෙදුමට ඉඩ දෙයි."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"මුහුණු සත්‍යාපක දෘඪාංග භාවිතා කරන්න"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"සත්‍යාපනය සඳහා සත්‍යාපක දෘඪාංග භාවිත කිරීමට යෙදුමට ඉඩ දෙයි"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"මුහුණ සැකසීමට නොහැකි විය. කරුණාකර නැවත උත්සාහ කර."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"මුහුණ දීප්තිමත් වැඩිය. කරුණාකර අඩු ආලෝකය උත්සාහ කරන්න."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"මුහුණ අඳුරු වැඩිය. කරුණාර ආලෝක ප්‍රභවය නිරාවරණය කරන්න."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"කරුණාකර සංවේදකය මුහුණෙන් වඩා ඈත් කරන්න."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"කරුණාකර සංවේදකය මුහුණ සමීපයට ගෙන එන්න."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"කරුණාකර සංවේදකය ඉහළට ගෙන යන්න."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"කරුණාකර සංවේදකය පහළට ගෙන යන්න."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"කරුණාකර සංවේදකය දකුණට ගෙන යන්න."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"කරුණාකර සංවේදකය වමට ගෙන යන්න."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"කරුණාකර සංවේදකය දෙස බලන්න."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"මුහුණ අනාවරණය කර නොගන්නා ලදී."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"උපාංගය ඉදිරියේ මුහුණ ස්ථාවරව තබන්න."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"මුහුණු දෘඪාංගය ලද නොහැකිය."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"මුහුණු කාල නිමාව ළඟා විය. නැවත උත්සාහ කරන්න."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"මුහුණ ගබඩා කළ නොහැක."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"මුහුණු මෙහෙයුම අවලංගු කරන ලදී."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"උත්සාහයන් ඉතා වැඩි ගණනකි. පසුව නැවත උත්සාහ කරන්න."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"උත්සාහයන් ඉතා වැඩි ගණනකි. මුහුණු සත්‍යාපනය අබල කරන ලදී."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"නැවත උත්සාහ කරන්න."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"මුහුණක් ඇතුළත් කර නොමැත."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"මෙම උපාංගයේ මුහුණු සත්‍යාපක සංවේදකයක් නොමැත"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"මුහුණු <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"මුහුණ නිරූපකය"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"සමමුහුර්ත සැකසීම් කියවන්න"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ගිණුම සඳහා සමමුහුර්ත සැකසීම් කියවීමට යෙදුමට අවසර දෙන්න. උදාහරණයක් ලෙස, ගිණුමක් සමඟ පුද්ගල යෙදුම සමමුහුර්ත දැයි මෙයට හඳුනා ගත හැක."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"සමමුහුර්ත කිරීම සක්‍රිය කරන්න සහ අක්‍රිය කරන්න"</string>
@@ -1177,6 +1210,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi සඳහා අන්තර්ජාල ප්‍රවේශය නැත"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"විකල්ප සඳහා තට්ටු කරන්න"</string>
+    <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">"මෙම උපාංගය 5GHz සඳහා ඔබේ මනාපවලට සහාය නොදක්වයි. ඒ වෙනුවට, මෙම උපාංගය ලබා ගත හැකි විට 5GHz කලාපය භාවිතා කරනු ඇත."</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="NEW_NETWORK">%1$s</xliff:g> <xliff:g id="PREVIOUS_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>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index ad9f7e1..3f2f46d 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -135,8 +135,8 @@
     <item msgid="4397097370387921767">"Volanie siete Wi‑Fi %s"</item>
   </string-array>
     <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Vypnuté"</string>
-    <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Uprednostniť Wi‑Fi"</string>
-    <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferujem mobilné dáta"</string>
+    <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferovať Wi‑Fi"</string>
+    <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferovať mobilné spojenie"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Len Wi‑Fi"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nepresmerované"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -242,9 +242,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Režim v lietadle"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Režim v lietadle je ZAPNUTÝ"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Režim v lietadle je VYPNUTÝ"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Šetrič batérie"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Šetrič batérie je VYPNUTÝ"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Šetrič batérie je ZAPNUTÝ"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Nastavenia"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Pomôcť"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Hlasový asistent"</string>
@@ -282,6 +279,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Poloha"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"prístup k polohe tohto zariadenia"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k polohe tohto zariadenia?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikácia bude mať prístup k polohe iba vtedy, keď ju budete používať."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Chcete vždy povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k polohe tohto zariadenia?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikácia bude mať vždy prístup k polohe, aj keď ju nebudete používať."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendár"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"prístup ku kalendáru"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup ku kalendáru?"</string>
@@ -408,12 +408,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Táto aplikácia môže pridávať, odstraňovať alebo meniť udalosti kalendára vo vašom telefóne. Táto aplikácia môže odosielať správy od vlastníkov kalendára alebo meniť udalosti bez upozornenia ich vlastníkov."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"prístup k ďalším príkazom poskytovateľa polohy"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Umožňuje aplikácii pristupovať k ďalším príkazom poskytovateľa informácií o polohe. Aplikácii to môže umožniť zasahovať do činnosti systému GPS alebo iných zdrojov informácií o polohe."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"prístup k presnej polohe (pomocou GPS a siete)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Táto aplikácia môže získať údaje o vašej polohe na základe systému GPS alebo sieťových zdrojov, ako sú mobilné veže a siete Wi‑Fi. Na to, aby aplikácia mohla používať služby určovania polohy, musia byť tieto služby zapnuté a k dispozícii na telefóne. Môže to viesť k zvýšeniu spotreby batérie."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"prístup k presnej polohe iba v popredí"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Táto aplikácia dokáže získať vašu presnú polohu iba vtedy, keď je spustená v popredí. Na to, aby mohla používať služby určovania polohy, musia byť tieto služby zapnuté a k dispozícii v telefóne. Môže to zvýšiť spotebu batérie."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"prístup k približnej polohe (pomocou siete)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Táto aplikácia môže získať údaje o vašej polohe na základe sieťových zdrojov, ako sú mobilné veže a siete Wi‑Fi. Na to, aby aplikácia mohla používať služby určovania polohy, musia byť tieto služby zapnuté a k dispozícii na tablete."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Táto aplikácia môže získať údaje o vašej polohe na základe sieťových zdrojov, ako sú mobilné veže a siete Wi‑Fi. Na to, aby aplikácia mohla používať služby určovania polohy, musia byť tieto služby zapnuté a k dispozícii na televízore."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Táto aplikácia môže získať údaje o vašej polohe na základe sieťových zdrojov, ako sú mobilné veže a siete Wi‑Fi. Na to, aby aplikácia mohla používať služby určovania polohy, musia byť tieto služby zapnuté a k dispozícii na telefóne."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"prístup k presnej polohe na pozadí"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Táto aplikácia dokáže kedykoľvek získať vašu presnú polohu, keď je spustená na pozadí. Na to, aby mohla používať služby určovania polohy, musia byť tieto služby zapnuté a k dispozícii v telefóne. Môže to zvýšiť spotebu batérie."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"meniť nastavenia zvuku"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Umožňuje aplikácii upraviť globálne nastavenia zvuku, ako je hlasitosť, alebo určiť, z ktorého reproduktora bude zvuk vychádzať."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"nahrávať zvuk"</string>
@@ -425,7 +427,7 @@
     <string name="permlab_vibrate" msgid="7696427026057705834">"ovládať vibrovanie"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Umožňuje aplikácii ovládať vibrácie."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"priamo volať na telefónne čísla"</string>
-    <string name="permdesc_callPhone" msgid="3740797576113760827">"Umožňuje aplikácii volať telefónne čísla bez vášho zásahu. V dôsledku toho sa môžu účtovať neočakávané poplatky alebo sa môžu uskutočniť neočakávané hovory. Toto povolenie neumožňuje aplikácii volať na čísla tiesňového volania."</string>
+    <string name="permdesc_callPhone" msgid="3740797576113760827">"Umožňuje aplikácii volať telefónne čísla bez vášho zásahu. V dôsledku toho sa môžu účtovať neočakávané poplatky alebo sa môžu uskutočniť neočakávané hovory. Toto povolenie neumožňuje aplikácii volať na tiesňovú linku."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"prístup k službe volania IMS"</string>
     <string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Umožňuje aplikácii používať službu okamžitých správ (IMS) na volanie bez intervencie používateľa."</string>
     <string name="permlab_readPhoneState" msgid="9178228524507610486">"čítať stav a identitu telefónu"</string>
@@ -521,6 +523,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona odtlačku prsta"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"správa hardvéru na overenie tváre"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Umožňuje aplikácii vyvolať metódy, ktoré pridávajú a odstraňujú šablóny tvárí."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"používanie hardvéru na overenie tváre"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Umožňuje aplikácii používať na overenie totožnosti hardvér na overenie tváre"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Tvár sa nepodarilo spracovať. Skúste to znova."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Tvár je príliš svetlá. Skúste to pri nižšom osvetlení."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Tvár je príliš tmavá. Zvýšte osvetlenie."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Oddiaľte senzor od tváre."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Priblížte senzor k tvári."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Pohnite senzor vyššie."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Pohnite senzor nižšie."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Pohnite senzor doprava."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Pohnite senzor doľava."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Pozerajte sa na senzor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nebola rozpoznaná žiadna tvár."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Udržte tvár nehybne oproti zariadeniu."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardvér na snímanie tváre nie je k dispozícii"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Limit rozpoznania tváre vypršal. Skúste to znova."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Tvár sa nedá uchovať."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Operácia týkajúca sa tváre bola zrušená"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Príliš veľa pokusov. Skúste to znova neskôr."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Príliš veľa pokusov. Overenie tváre je zakázané."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Skúste to znova."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nemáte zaregistrovanú žiadnu tvár."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Toto zariadenie nemá senzor na overenie tváre."</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Tvár <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona tváre"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"čítať nastavenia synchronizácie"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Umožňuje aplikácii čítať nastavenia synchronizácie v účte. Môže napríklad určiť, či je s účtom synchronizovaná aplikácia Ľudia."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"zapnúť alebo vypnúť synchronizáciu"</string>
@@ -735,7 +768,7 @@
     <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Zadajte kód PIN na odomknutie"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Nesprávny kód PIN."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Ak chcete telefón odomknúť, stlačte Menu a následne 0."</string>
-    <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Číslo tiesňovej linky"</string>
+    <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Tiesňová linka"</string>
     <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Žiadny signál"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Obrazovka je uzamknutá."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Ak chcete odomknúť telefón alebo uskutočniť tiesňové volanie, stlačte Menu."</string>
@@ -1219,6 +1252,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Sieť Wi‑Fi nemá prístup k internetu"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Klepnutím získate možnosti"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Zmeny nastavení hotspotu"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Pásmo vášho hotspotu sa zmenilo."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Toto zariadenie nepodporuje vašu predvoľbu používať iba 5 GHz. Namiesto toho bude pásmo 5 GHz používať vtedy, keď bude k dispozícii."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Prepnuté na sieť: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Keď <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nemá prístup k internetu, zariadenie používa <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Môžu sa účtovať poplatky."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Prepnuté zo siete <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sieť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 3223eda..90ab23b 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -135,7 +135,7 @@
     <item msgid="4397097370387921767">"Klicanje prek Wi-Fi-ja (%s)"</item>
   </string-array>
     <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Izklopljeno"</string>
-    <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Prednostno – Wi-Fi"</string>
+    <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Prednostno Wi-Fi"</string>
     <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Prednostno mobilno"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Samo Wi-Fi"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ni posredovano"</string>
@@ -242,9 +242,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Način za letalo"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Način za letalo je VKLOPLJEN"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Način za letalo je IZKLOPLJEN"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Varčevanje z energijo akumulatorja"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Varčevanje z energijo akumulatorja je izklopljeno"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Varčevanje z energijo akumulatorja je vklopljeno"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Nastavitve"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Pomoč"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Glas. pomočnik"</string>
@@ -282,6 +279,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Lokacija"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"dostop do lokacije te naprave"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; omogočiti dostop do lokacije te naprave?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikacija bo imela dostop do lokacije samo, ko aplikacijo uporabljate."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; vedno dovoliti dostop do lokacije te naprave?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikacija bo vedno imela dostop do lokacije, tudi ko aplikacije ne uporabljate."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Koledar"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"dostop do koledarja"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; omogočiti dostop do koledarja?"</string>
@@ -408,12 +408,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ta aplikacija lahko dodaja, odstranjuje in spreminja dogodke v koledarju, ki so shranjeni v telefonu. Ta aplikacija lahko pošilja sporočila, ki bodo morda videti, kot da prihajajo od lastnikov koledarjev, ali spreminja dogodke brez vednosti lastnikov."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"dostopanje do ukazov ponudnika dodatnih lokacij"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Aplikaciji omogoča dostop do dodatnih ukazov ponudnika lokacij. S tem lahko aplikacija moti delovanje sistema GPS ali drugih virov lokacije."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"dostop do natančne lokacije (na podlagi podatkov GPS in omrežja)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Ta aplikacija lahko pridobi vašo lokacijo na podlagi sistema GPS ali omrežnih lokacijskih virov, kot so bazne postaje in omrežja Wi-Fi. Če želite aplikaciji omogočiti uporabo teh lokacijskih storitev, morajo biti te vklopljene in na voljo v telefonu. Poraba energije akumulatorja bo morda večja."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"dostop do točne lokacije samo, ko deluje v ospredju"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ta aplikacija lahko pridobi vašo točno lokacijo samo, ko deluje v ospredju. Če želite aplikaciji omogočiti uporabo teh lokacijskih storitev, morajo biti te vklopljene in na voljo v telefonu. Poraba energije akumulatorja bo morda večja."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"dostop do približne lokacije (na podlagi podatkov omrežja)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ta aplikacija lahko pridobi vašo lokacijo na podlagi omrežnih virov, kot so bazne postaje in omrežja Wi-Fi. Če želite aplikaciji omogočiti uporabo teh lokacijskih storitev, morajo biti te vklopljene in na voljo v tabličnem računalniku."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ta aplikacija lahko pridobi vašo lokacijo na podlagi omrežnih virov, kot so bazne postaje in omrežja Wi-Fi. Če želite aplikaciji omogočiti uporabo teh lokacijskih storitev, morajo biti te vklopljene in na voljo v televizorju."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ta aplikacija lahko pridobi vašo lokacijo na podlagi omrežnih virov, kot so bazne postaje in omrežja Wi-Fi. Če želite aplikaciji omogočiti uporabo teh lokacijskih storitev, morajo biti te vklopljene in na voljo v telefonu."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"dostop do točne lokacije, ko deluje v ozadju"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ta aplikacija lahko pridobi vašo točno lokacijo, ko deluje v ozadju. Če želite aplikaciji omogočiti uporabo teh lokacijskih storitev, morajo biti te vklopljene in na voljo v telefonu. Poraba energije akumulatorja bo morda večja."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"spreminjanje nastavitev zvoka"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Aplikaciji omogoča spreminjanje splošnih zvočnih nastavitev, na primer glasnost in kateri zvočnik se uporablja."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"snemanje zvoka"</string>
@@ -521,6 +523,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona prstnih odtisov"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"upravljanje strojne opreme za preverjanje pristnosti obraza"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Aplikaciji omogoča sprožanje načinov za dodajanje in brisanje predlog z obrazi za uporabo."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"uporaba strojne opreme za preverjanje pristnosti obraza"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Aplikaciji omogoča uporabo strojne opreme za preverjanje pristnosti obraza"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Obraza ni bilo mogoče obdelati. Poskusite znova."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Obraz je presvetel. Poskusite pri manj svetlobe."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Obraz je pretemen. Odkrijte vir svetlobe."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Tipalo premaknite dlje od obraza."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Tipalo premaknite bliže obrazu."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Tipalo premaknite višje."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Tipalo premaknite nižje."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Tipalo premaknite v desno."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Tipalo premaknite v levo."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Poglejte v tipalo."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Ni zaznanih obrazov."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Obraz naj bo pri miru in pred napravo."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Strojna oprema za prepoznavo obraza ni na voljo."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Dosežena časovna omejitev za obraz. Poskusite znova."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Obraza ni mogoče shraniti."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Dejanje z obrazom je bilo preklicano."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Preveč poskusov. Poskusite znova pozneje."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Preveč poskusov. Preverjanje pristnosti obraza je onemogočeno."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Poskusite znova."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Ni prijavljenih obrazov."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Ta naprava nima tipala za preverjanje pristnosti obraza"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Obraz <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona obraza"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"branje nastavitev sinhronizacije"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Aplikaciji omogoča branje nastavitev sinhronizacije za račun. S tem lahko aplikacija na primer ugotovi, ali je aplikacija Ljudje sinhronizirana z računom."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"vklop in izklop sinhronizacije"</string>
@@ -1219,6 +1252,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Omrežje Wi-Fi nima dostopa do interneta"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Dotaknite se za možnosti"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Spremembe nastavitev dostopne točke"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Pas dostopne točke je spremenjen."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Ta naprava ne podpira prednostne nastavitve samo za 5-GHz pas. Namesto tega bo ta naprava uporabljala 5-GHz pas, ko bo na voljo."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Preklopljeno na omrežje vrste <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Naprava uporabi omrežje vrste <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, ko omrežje vrste <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nima dostopa do interneta. Prenos podatkov se lahko zaračuna."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Preklopljeno z omrežja vrste <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na omrežje vrste <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 1323c07..bb42da4 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"modaliteti i aeroplanit"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modaliteti i aeroplanit është i AKTIVIZUAR"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modaliteti i aeroplanit është i ÇAKTIVIZUAR"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Kursyesi i baterisë"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"\"Kursyesi i baterisë\" është JOAKTIV."</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"\"Kursyesi i baterisë\" është AKTIV"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Cilësimet"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Ndihma"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ndihma zanore"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Vendndodhja"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"qaset te vendndodhja e kësaj pajisjeje"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te vendndodhja e kësaj pajisjeje?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikacioni do të ketë qasje te vendndodhja vetëm kur po e përdor aplikacionin."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Të lejohet gjithmonë që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te vendndodhja?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikacioni do të ketë gjithmonë qasje te vendndodhja, edhe kur nuk po e përdor aplikacionin."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendari"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"qasje te kalendari yt"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te kalendari yt?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ky aplikacion mund të shtojë, të heqë ose të ndryshojë ngjarjet e kalendarit në telefonin tënd. Ky aplikacion mund të dërgojë mesazhe që mund të duket se vijnë nga zotëruesit e kalendarit ose të ndryshojë ngjarjet pa i njoftuar zotëruesit e tyre."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"qasje në komandat shtesë të ofruesit të vendndodhjes"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Lejon aplikacionin të ketë qasje në komandat shtesë të ofruesit për vendndodhjen. Kjo mund ta lejojë aplikacionin të ndërhyjë në operacionin e GPS-së apo të burimeve të tjera për vendndodhjen."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"qasu te vendndodhja e përpiktë (në bazë të GPS-së dhe rrjetit)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Ky aplikacion mund të marrë vendndodhjen tënde bazuar në burimet e rrjetit si antenat e operatorëve celulare dhe rrjetet Wi-Fi. Këto shërbime të vendndodhjes duhet të jenë të aktivizuara dhe në dispozicion në telefonin tënd që aplikacioni të mund t\'i përdorë. Kjo mund të rritë konsumin e baterisë."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"qasu në vendndodhjen e saktë vetëm në plan të parë"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ky aplikacion mund të marrë vendndodhjen tënde të saktë në çdo kohë kur është në plan të parë. Këto shërbime të vendndodhjes duhet të jenë të aktivizuara dhe në dispozicion në telefonin tënd që aplikacioni të mund t\'i përdorë. Kjo gjë mund të rritë konsumin e baterisë."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"qasu te vendndodhja e përafërt (bazuar në rrjet)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ky aplikacion mund të marrë vendndodhjen tënde bazuar në burimet e rrjetit si antenat e operatorëve celulare dhe rrjetet Wi-Fi. Këto shërbime të vendndodhjes duhet të jenë të aktivizuara dhe në dispozicion në tabletin tënd që aplikacioni të mund t\'i përdorë."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ky aplikacion mund të marrë vendndodhjen tënde bazuar në burimet e rrjetit si antenat e operatorëve celulare dhe rrjetet Wi-Fi. Këto shërbime të vendndodhjes duhet të jenë të aktivizuara dhe në dispozicion në televizorin tënd që aplikacioni të mund t\'i përdorë."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ky aplikacion mund të marrë vendndodhjen tënde bazuar në burimet e rrjetit si antenat e operatorëve celulare dhe rrjetet Wi-Fi. Këto shërbime të vendndodhjes duhet të jenë të aktivizuara dhe në dispozicion në telefonin tënd që aplikacioni të mund t\'i përdorë."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"qasu në vendndodhjen e saktë në sfond"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ky aplikacion mund të marrë vendndodhjen tënde të saktë në çdo kohë kur është në sfond. Këto shërbime të vendndodhjes duhet të jenë të aktivizuara dhe në dispozicion në telefonin tënd që aplikacioni të mund t\'i përdorë. Kjo gjë mund të rritë konsumin e baterisë."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ndrysho cilësimet e audios"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Lejon aplikacionin të modifikojë cilësimet globale të audios siç është volumi dhe se cili altoparlant përdoret për daljen."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"regjistro audio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona e gjurmës së gishtit"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"menaxho harduerin për vërtetimin e fytyrës"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Lejon aplikacionin të aktivizojë mënyra për shtim e fshirje të shablloneve të përdorura."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"përdor harduerin për vërtetimin e fytyrës"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Lejon aplikacionin të përdorë harduer vërtetimi të fytyrës për procesin e vërtetimit"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Fytyra nuk mund të përpunohej. Provo sërish."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Fytyra ka shumë ndriçim. Provo me më pak ndriçim."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Fytyra shumë e errët. Zbulo burimin e dritës."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Lëvize sensorin pak më larg fytyrës."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Sille sensorin më pranë fytyrës."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Lëvize sensorin më lart."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Lëvize sensorin më poshtë."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Lëvize sensorin djathtas."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Lëvize sensorin majtas."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Shiko nga sensori"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nuk u diktua fytyrë"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mbaje fytyrën pa lëvizur përpara pajisjes."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Nuk ka harduer për fytyrën."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Mbaroi afati për fytyrën. Provo sërish."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Fytyra nuk mund të ruhet."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Veprimi me fytyrën u anulua."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Shumë përpjekje. Provo sërish më vonë."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Shumë përpjekje. Vërtetimi për fytyrën joaktiv."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Provo sërish."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nuk ka fytyrë të regjistruar."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Kjo pajisje nuk ka sensor vërtetimi për fytyrën"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Fytyra <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona e fytyrës"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lexo cilësimet e sinkronizimit"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Lejon aplikacionin të lexojë cilësimet e sinkronizimit për një llogari. Për shembull, kjo mund të përcaktojë nëse aplikacioni \"Kontaktet\" është i sinkronizuar me një llogari."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ndiz ose fik sinkronizimin"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi nuk ka qasje në internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Trokit për opsionet"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Ndryshimet në cilësimet e zonës së qasjes për internet"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Brezi yt i zonës së qasjes për internet ka ndryshuar."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Kjo pajisje nuk e mbështet preferencën për vetëm 5 GHz. Përkundrazi, pajisja do të përdorë brezin 5 GHz nëse ka."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Kaloi te <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Pajisja përdor <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kur <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nuk ka qasje në internet. Mund të zbatohen tarifa."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Kaloi nga <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> te <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 7206f57..fa1dc59 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -239,9 +239,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Режим рада у авиону"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Режим рада у авиону је УКЉУЧЕН"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Режим рада у авиону је ИСКЉУЧЕН"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Уштеда батерије"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Уштеда батерије је ИСКЉУЧЕНА"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Уштеда батерије је УКЉУЧЕНА"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Подешавања"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Помоћ"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Гласовна помоћ"</string>
@@ -279,6 +276,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Локација"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"приступи локацији овог уређаја"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Желите ли да омогућите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа локацији овог уређаја?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Апликација ће имати приступ локацији само док користите апликацију."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Желите ли да омогућите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа локацији овог уређаја?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Апликација ће увек имати приступ локацији, чак и када не користите апликацију."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"приступи календару"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Желите ли да омогућите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа календару?"</string>
@@ -405,12 +405,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ова апликацији може да додаје, уклања или мења догађаје из календара на телефону. Ова апликација може да шаље поруке које изгледају као да их шаљу власници календара или да мења догађаје без знања власника."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"приступ додатним командама добављача локације"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Омогућава апликацији да приступа додатним командама даваоца услуга локације. То може да омогући апликацији да утиче на рад GPS-а или других извора локације."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"приступ прецизној локацији (утврђена преко мреже и GPS-а)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Ова апликација може да приступи вашој локацији помоћу GPS-а или мрежних извора локација, као што су мобилни предајници и Wi-Fi мреже. Ове услуге локације морају да буду укључене и доступне на телефону да би апликација могла да их користи. То може да повећа потрошњу батерије."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"приступ прецизној локацији само у првом плану"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ова апликација може да одреди вашу тачну локацију само када ради у првом плану. Ове услуге локације морају да буду укључене и доступне на телефону да би апликација могла да их користи. То може да повећа потрошњу батерије."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"приступ приближној локацији (утврђена преко мреже)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ова апликација може да приступи вашој локацији помоћу извора мреже, као што су мобилни предајници и Wi-Fi мреже. Ове услуге локације морају да буду укључене и доступне на таблету да би апликација могла да их користи."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ова апликација може да приступи вашој локацији помоћу извора мреже, као што су мобилни предајници и Wi-Fi мреже. Ове услуге локације морају да буду укључене и доступне на ТВ-у да би апликација могла да их користи."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ова апликација може да приступи вашој локацији помоћу извора мреже, као што су мобилни предајници и Wi-Fi мреже. Ове услуге локације морају да буду укључене и доступне на телефону да би апликација могла да их користи."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"приступ прецизној локацији у позадини"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Ова апликација може да одреди вашу тачну локацију сваки пут када ради у позадини. Ове услуге локације морају да буду укључене и доступне на телефону да би апликација могла да их користи. То може да повећа потрошњу батерије."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"промена аудио подешавања"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Дозвољава апликацији да мења глобална аудио подешавања као што су јачина звука и избор звучника који се користи као излаз."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"снимање аудио записа"</string>
@@ -518,6 +520,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Икона отиска прста"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"управљање хардв. за потврду идентитета помоћу лица"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Дозвољава да апликација активира методе за додавање и брисање шаблона лица ради коришћења."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"коришћење хардв. за потврду идентитета помоћу лица"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Дозвољава да апликација користи хардвер за потврду идентитета помоћу лица"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Обрада лица није успела. Пробајте поново."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Лице је пресветло. Пробајте са слабијим осветљењем."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Лице је исувише тамно. Откријте извор светла."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Удаљите сензор од лица."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Приближите сензор лицу."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Померите сензор навише."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Померите сензор наниже."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Померите сензор удесно."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Померите сензор улево."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Гледајте у сензор."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Није откривено ниједно лице."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Задржите лице испред уређаја без померања."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Харвдер за лице није доступан."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Истекло је време за проверу лица. Пробајте поново."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Није могуће сачувати лице."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Обрада лица је отказана."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Превише покушаја. Пробајте поново касније."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Више покушаја. Потврда идентитета је онемогућена."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Пробајте поново."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Није регистровано ниједно лице."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Овај уређај нема сензор за потврду идентитета помоћу лица"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Лице <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Икона лица"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"читање подешавања синхронизације"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Дозвољава апликацији да чита подешавања синхронизације за налог. На пример, овако може да се утврди да ли је апликација Људи синхронизована са налогом."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"укључивање и искључивање синхронизације"</string>
@@ -1197,6 +1230,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi нема приступ интернету"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Додирните за опције"</string>
+    <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">"Овај уређај не подржава подешавање само за 5 GHz. Уређај ће користити опсег од 5 GHz када буде доступан."</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="NEW_NETWORK">%1$s</xliff:g> када тип мреже <xliff:g id="PREVIOUS_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>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 4351664..7b9005c50 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Flygplansläge"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Flygplansläge är AKTIVERAT"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flygplansläge är INAKTIVERAT"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Batterisparläge"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Batterisparläget har inaktiverats"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Batterisparläget har aktiverats"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Inställningar"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Hjälp"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -272,19 +269,22 @@
     <string name="managed_profile_label" msgid="8947929265267690522">"Byt till jobbprofilen"</string>
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakter"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"få tillgång till dina kontakter"</string>
-    <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomstbehörighet till dina kontakter?"</string>
+    <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till dina kontakter?"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Plats"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"komma åt enhetens platsuppgifter"</string>
-    <string name="permgrouprequest_location" msgid="3788275734953323491">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomstbehörighet till enhetens plats?"</string>
+    <string name="permgrouprequest_location" msgid="3788275734953323491">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till enhetens plats?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Appen har endast åtkomst till platsen när du använder den."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till enhetens plats?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Appen har alltid åtkomst till platsen, även om du inte använder den."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"få tillgång till din kalender"</string>
-    <string name="permgrouprequest_calendar" msgid="289900767793189421">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomstbehörighet till din kalender?"</string>
+    <string name="permgrouprequest_calendar" msgid="289900767793189421">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till din kalender?"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"Sms"</string>
     <string name="permgroupdesc_sms" msgid="4656988620100940350">"skicka och visa sms"</string>
     <string name="permgrouprequest_sms" msgid="7168124215838204719">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att skicka och visa sms?"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"Lagring"</string>
     <string name="permgroupdesc_storage" msgid="637758554581589203">"få åtkomst till foton, media och filer på din enhet"</string>
-    <string name="permgrouprequest_storage" msgid="7885942926944299560">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomstbehörighet till foton, mediafiler och andra filer på enheten?"</string>
+    <string name="permgrouprequest_storage" msgid="7885942926944299560">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till foton, mediefiler och andra filer på enheten?"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"spela in ljud"</string>
     <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att spela in ljud?"</string>
@@ -299,7 +299,7 @@
     <string name="permgrouprequest_phone" msgid="9166979577750581037">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att ringa och hantera telefonsamtal?"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Kroppssensorer"</string>
     <string name="permgroupdesc_sensors" msgid="7147968539346634043">"få åtkomst till sensordata om dina vitalparametrar"</string>
-    <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomstbehörighet till sensordata om vitalparametrar?"</string>
+    <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till sensordata om vitalparametrar?"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Hämta fönsterinnehåll"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Granska innehållet i ett fönster som du interagerar med."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Aktivera Explore by touch"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Appen kan lägga till, ta bort eller ändra kalenderhändelser på mobilen. Appen kan skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra uppgifter utan ägarens vetskap."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få åtkomst till extra kommandon för platsleverantör"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Tillåter att appen får åtkomst till extra kommandon för platsleverantör. Detta kan innebära att appen tillåts störa funktionen för GPS eller andra platskällor."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"få åtkomst till din exakta position (GPS- och nätverksbaserad)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Appen kan få information om din plats från GPS eller platskällor i nätverket, som mobilmaster och Wi-Fi-nätverk. De platstjänsterna måste ha aktiverats och finnas på mobilen om appen ska kunna använda dem. Detta kan leda till ökad batteriförbrukning."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"endast åtkomst till exakt plats i förgrunden"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Den här appen kan endast få information om din exakta plats när den körs i förgrunden. Platstjänsterna måste ha aktiverats och finnas på mobilen om appen ska kunna använda dem. Detta kan leda till ökad batteriförbrukning."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"få åtkomst till din ungefärliga position (nätverksbaserad)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Appen kan få information om din plats från källor i nätverket, som mobilmaster och Wi-Fi-nätverk. De platstjänsterna måste ha aktiverats och finnas på surfplattan om appen ska kunna använda dem."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Appen kan få information om din plats från källor i nätverket, som mobilmaster och Wi-Fi-nätverk. De platstjänsterna måste ha aktiverats och fungera på TV:n om appen ska kunna använda dem."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Appen kan få information om din plats från källor i nätverket, som mobilmaster och Wi-Fi-nätverk. De platstjänsterna måste ha aktiverats och finnas på mobilen om appen ska kunna använda dem."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"åtkomst till exakt plats i bakgrunden"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Den här appen kan när som helst få information om din exakta plats när den körs i bakgrunden. Platstjänsterna måste ha aktiverats och finnas på mobilen om appen ska kunna använda dem. Detta kan leda till ökad batteriförbrukning."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ändra dina ljudinställningar"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Tillåter att appen ändrar globala ljudinställningar som volym och vilken högtalarutgång som används."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"spela in ljud"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon för fingeravtryck"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"hantera maskinvara för ansiktsautentisering"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Tillåter att appen anropar metoder för att lägga till och radera ansiktsmallar."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"använda maskinvara för ansiktsautentisering"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Tillåter att appen använder maskinvara för ansiktsigenkänning vid autentisering"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Det gick inte att läsa av ansiktet. Försök igen."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Ansiktet är för ljust. Testa i svagare belysning."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Ansiktet är för mörkt. Testa i starkare belysning."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Flytta sensorn längre ifrån ansiktet."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Ha sensorn närmare ansiktet."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Flytta sensorn uppåt."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Flytta sensorn nedåt."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Flytta sensorn åt höger."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Flytta sensorn åt vänster."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Titta på sensorn."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Inget ansikte hittades."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Håll ansiktet stilla framför enheten."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Maskinvara för ansiktsigenkänning saknas."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Tidsgränsen för ansikte har nåtts. Försök igen."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Det gick inte att lagra ansiktet."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Ansiktsåtgärden har avbrutits."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Du har gjort för många försök. Försök igen senare."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"För många försök. Ansiktsautentisering inaktiverad"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Försök igen."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Inget ansikte har registrerats."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Enheten har ingen sensor för ansiktsautentisering"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Ansikte <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Ansikte"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"läsa synkroniseringsinställningar"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Tillåter att appen läser synkroniseringsinställningarna för ett konto. Detta kan användas till exempel för att avgöra om appen Personer är synkroniserad med ett konto."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"aktivera/inaktivera synkronisering"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi-nätverket är inte anslutet till internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tryck för alternativ"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Ändringar i inställningarna för surfzon"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Frekvensbandet för surfzonen har ändrats."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Den här enheten har inte stöd för inställningen för att endast använda 5 GHz. I stället används 5 GHz när det är möjligt."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Byte av nätverk till <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> används på enheten när det inte finns internetåtkomst via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Avgifter kan tillkomma."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Byte av nätverk från <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> till <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index f8af25e..9d8790d 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -71,10 +71,10 @@
     <string name="RuacMmi" msgid="7827887459138308886">"Ukataaji wa simu zinazokera zisizohitajika"</string>
     <string name="CndMmi" msgid="3116446237081575808">"Kuonyeshwa kwa nambari inayopiga"</string>
     <string name="DndMmi" msgid="1265478932418334331">"Usinisumbue"</string>
-    <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Chaguo msingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo: Imezuiliwa"</string>
-    <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Chaguo msingi za kitambulisho cha mpigaji simu huwa kuzuiwa. Simu ifuatayo: Haijazuiliwa"</string>
-    <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Chaguo msingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo:Imezuiliwa"</string>
-    <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Chaguo msingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo: Haijazuiliwa"</string>
+    <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Chaguomsingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo: Imezuiliwa"</string>
+    <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Chaguomsingi za kitambulisho cha mpigaji simu huwa kuzuiwa. Simu ifuatayo: Haijazuiliwa"</string>
+    <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Chaguomsingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo:Imezuiliwa"</string>
+    <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Chaguomsingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo: Haijazuiliwa"</string>
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"Huduma haitathminiwi."</string>
     <string name="CLIRPermanent" msgid="3377371145926835671">"Hauwezi kubadilisha mpangilio wa kitambulisho cha anayepiga."</string>
     <string name="RestrictedOnDataTitle" msgid="5221736429761078014">"Hakuna huduma ya data kwa vifaa vya mkononi"</string>
@@ -234,9 +234,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Hali ya ndegeni"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Hali ya ndegeni IMEWASHWA"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Hali ya ndegeni IMEZIMWA"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Kiokoa betri"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Kiokoa betri KIMEZIMWA"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Kiokoa betri KIMEWASHWA"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Mipangilio"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Mapendekezo"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Usaidizi wa Sauti"</string>
@@ -274,6 +271,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Mahali"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"itambue mahali kifaa hiki kilipo"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; itambue mahali kifaa kilipo?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Programu itafikia tu data ya mahali ulipo unaipotumia."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie data ya mahali kifaa hiki kilipo kila wakati?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Programu itaweza kufikia data ya mahali kifaa kilipo hata wakati huitumii."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalenda"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"ifikie kalenda yako"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie kalenda yako?"</string>
@@ -400,12 +400,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Programu hii inaweza kuongeza, kuondoa au kubadilisha matukio kwenye simu yako. Pogramu hii inaweza kutuma ujumbe unaoonekana kuwa umetoka kwa wamiliki wa kalenda au kurekebisha matukio bila kuwataarifu wamiliki."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"fikia amri za ziada za mtoa huduma ya mahali"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ruhusu programu kufikia amri za ziada za mtoa huduma za mahali. Hii huenda ikaruhusu programu ikatize matumizi ya GPS au vyanzo vingine vya eneo."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"fikia mahali halisi (inategemea mtandao na GPS)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Programu hii inaweza kupata eneo lako kulingana na vyanzo vya eneo vya GPS au mtandao kama vile minara ya simu na mitandao ya Wi-Fi. Huduma hizi za mahali lazima ziwashwe na zipatikane kwenye simu yako ili programu iweze kuzitumia.Hii huenda ikaongeza matumizi ya betri."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"kufikia mahali mahususi ikiwa tu programu imefunguliwa kwenye skrini"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Programu hii inaweza kupata mahali halisi ikiwa tu umeifungua kwenye skrini. Ni lazima uwashe huduma hizi za mahali na zipatikane kwenye simu yako ili programu iweze kuzitumia. Hatua hii inaweza kuongeza utumiaji wa betri."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"fikia mahali karibu na hapo (inategemea mtandao)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Programu hii inaweza kupata eneo lako kulingana na vyanzo vya mtandao kama vile minara ya simu na mitandao ya Wi-Fi. Huduma hizi za mahali lazima ziwashwe na zipatikane kwenye kompyuta yako kibao ili programu iweze kuzitumia."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Programu hii inaweza kupata eneo lako kulingana na vyanzo vya mtandao kama vile minara ya simu na mitandao ya Wi-Fi. Huduma hizi za mahali lazima ziwashwe na zipatikane kwenye runinga yako ili programu iweze kuzitumia."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Programu hii inaweza kupata eneo lako kulingana na vyanzo vya mtandao kama vile minara ya simu na mitandao ya Wi-Fi. Huduma hizi za mahali lazima ziwashwe na zipatikane kwenye simu yako ili programu iweze kuzitumia."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"kufikia mahali halisi ikiwa inatumika chinichini"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Programu hii inaweza kupata mahali halisi ulipo wakati wowote ikiwa inatumika chinichini. Ni lazima uwashe huduma hizi za mahali na zipatikane kwenye simu yako ili programu iweze kuzitumia. Hatua hii inaweza kuongeza utumiaji wa betri."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"badilisha mipangilio yako ya sauti"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Inaruhusu programu kurekebisha mipangilio ya sauti kila mahali kama vile sauti na ni kipaza sauti kipi ambacho kinatumika kwa kutoa."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"kurekodi sauti"</string>
@@ -513,6 +515,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Aikoni ya kitambulisho"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"dhibiti maunzi ya kuthibitisha uso"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Huruhusu programu iombe njia za kuongeza na kufuta violezo vya uso vitakavyotumiwa."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"tumia maunzi ya kuthibistiha uso"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Huruhusu programu ithibitishe uso kwa kutumia maunzi ya kuthibitisha"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Imeshindwa kuchakata uso. Tafadhali jaribu tena."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Uso unang\'aa sana. Tafadhalia punguza mwangaza."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Uso hauna mwangaza wa kutosha. Tafadhali ongeza mwangaza."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Tafadhali sogeza kitambuzi mbali na uso."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Tafadhali sogeza kitambuzi karibu na uso."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Tafadhali sogeza kitambuzi juu."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Tafadhali sogeza kitambuzi chini."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Tafadhali sogeza kitambuzi kulia."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Tafadhali sogeza kitambuzi kushoto."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Tafadhali angalia kitambuzi."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Haikutambua uso wowote"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Weka uso wima mbele ya kifaa."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Maunzi ya uso hayapatikani."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Muda wa kutambua uso umeisha. Jaribu tena."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Huwezi kuhifadhi uso."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Utendaji wa kitambulisho umeghairiwa."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Umejaribu mara nyingi mno. Jaribu tena baadaye."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Umejaribu mara nyingi mno. Kitambuzi cha uso kimezimwa."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Jaribu tena."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Hujasajili uso wowote."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Kifaa hiki hakina kitambuzi kinachothibitisha uso"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Uso wa <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Aikoni ya uso"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"kusoma mipangilio ya usawazishaji"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Inaruhusu programu kusoma mipangilio ya upatanishi wa akaunti. Kwa mfano, huku kunaweza kuamua kama programu ya Watu imepatanishwa na akaunti."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"kuwasha na kuzima usawazishaji"</string>
@@ -770,9 +803,9 @@
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Umejaribu kufungua kompyuta kibao kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukijaribu tena mara <xliff:g id="NUMBER_1">%2$d</xliff:g> bila mafanikio, kompyuta ndogo itarejeshwa kwenye mipangilio iliyotoka nayo kiwandani na data yote iliyomo itafutwa."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukikosea majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi, runinga itarejeshwa katika hali iliyotoka nayo kiwandani na data yote ya watumiaji itafutwa."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukijaribu tena mara <xliff:g id="NUMBER_1">%2$d</xliff:g> bila mafanikio, simu itarejeshwa kwenye mipangilio iliyotoka nayo kiwandani na data yote iliyomo itafutwa."</string>
-    <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Kompyuta ndogo haitaweza kuwekwa upya kwa kiwanda chaguo msingi."</string>
+    <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Kompyuta ndogo haitaweza kuwekwa upya kwa kiwanda chaguomsingi."</string>
     <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa runinga itarejeshwa katika mipangilio iliyotoka nayo kiwandani."</string>
-    <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Kompyuta ndogo haitaweza kuwekwa upya kwa kiwanda chaguo msingi."</string>
+    <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Kompyuta ndogo haitaweza kuwekwa upya kwa kiwanda chaguomsingi."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Jaribu tena kwa sekunde <xliff:g id="NUMBER">%d</xliff:g>."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Umesahau mchoro?"</string>
     <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Fungua akaunti"</string>
@@ -1062,9 +1095,9 @@
     <string name="whichImageCaptureApplication" msgid="3680261417470652882">"Piga picha ukitumia"</string>
     <string name="whichImageCaptureApplicationNamed" msgid="8619384150737825003">"Piga picha ukitumia %1$s"</string>
     <string name="whichImageCaptureApplicationLabel" msgid="6390303445371527066">"Piga picha"</string>
-    <string name="alwaysUse" msgid="4583018368000610438">"Tumia kama chaguo msingi la kitendo hiki."</string>
+    <string name="alwaysUse" msgid="4583018368000610438">"Tumia kama chaguomsingi la kitendo hiki."</string>
     <string name="use_a_different_app" msgid="8134926230585710243">"Tumia programu tofauti"</string>
-    <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Futa chaguo msingi katika mipangilio ya Mfumo &gt; Apps &gt; iliyopakuliwa."</string>
+    <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Futa chaguomsingi katika mipangilio ya Mfumo &gt; Apps &gt; iliyopakuliwa."</string>
     <string name="chooseActivity" msgid="7486876147751803333">"Chagua kitendo"</string>
     <string name="chooseUsbActivity" msgid="6894748416073583509">"Chagua programu ya kifaa cha USB"</string>
     <string name="noApplications" msgid="2991814273936504689">"Hakuna programu zinazoweza kufanya tendo hili."</string>
@@ -1139,8 +1172,8 @@
     <string name="volume_icon_description_incall" msgid="8890073218154543397">"Sauti ya simu"</string>
     <string name="volume_icon_description_media" msgid="4217311719665194215">"Sauti ya faili"</string>
     <string name="volume_icon_description_notification" msgid="7044986546477282274">"Sauti ya arifa"</string>
-    <string name="ringtone_default" msgid="3789758980357696936">"Mlio chaguo msingi"</string>
-    <string name="ringtone_default_with_actual" msgid="1767304850491060581">"Chaguo msingi (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
+    <string name="ringtone_default" msgid="3789758980357696936">"Mlio chaguomsingi"</string>
+    <string name="ringtone_default_with_actual" msgid="1767304850491060581">"Chaguomsingi (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
     <string name="ringtone_silent" msgid="7937634392408977062">"Hamna"</string>
     <string name="ringtone_picker_title" msgid="3515143939175119094">"Toni za mlio"</string>
     <string name="ringtone_picker_title_alarm" msgid="6473325356070549702">"Sauti za kengele"</string>
@@ -1173,6 +1206,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi haina muunganisho wa intaneti"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Gusa ili upate chaguo"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Mabadiliko kwenye mipangilio ya mtandao pepe"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Bendi ya mtandao pepe wako imebadilika."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Kifaa hiki hakitumii mapendeleo yako ya GHz 5 pekee. Badala yake, kifaa hiki kitatumia bendi ya GHz 5 itakapopatikana."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Sasa inatumia <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Kifaa hutumia <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wakati <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> haina intaneti. Huenda ukalipishwa."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Imebadilisha mtandao kutoka <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sasa inatumia <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1537,12 +1573,12 @@
     <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Umeingiza nenosiri lako kwa makosa mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
     <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Umeingiza nenosiri lako kwa makosa mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
     <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Umechora ruwaza yako ya kufunga kwa makosa mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi yasiyofaulu, kompyuta ndogo itarejeshwa katika mfumo chaguo msingi ilivyotoka kiwandani data yote ya mtumiaji itapotea."</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi yasiyofaulu, kompyuta ndogo itarejeshwa katika mfumo chaguomsingi ilivyotoka kiwandani data yote ya mtumiaji itapotea."</string>
     <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukikosea majaribio mengine <xliff:g id="NUMBER_1">%2$d</xliff:g>, runinga itarejeshwa katika hali iliyotoka nayo kiwandani na data yote ya watumiaji itafutwa."</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi yasiyofaulu, simu itarejeshwa katika mfumo chaguo msingi ilivyotoka kiwandani na data yote ya mtumiaji itapotea."</string>
-    <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa kompyuta ndogo itarejeshwa katika mfumo chaguo msingi ilivyotoka kiwandani."</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi yasiyofaulu, simu itarejeshwa katika mfumo chaguomsingi ilivyotoka kiwandani na data yote ya mtumiaji itapotea."</string>
+    <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa kompyuta ndogo itarejeshwa katika mfumo chaguomsingi ilivyotoka kiwandani."</string>
     <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa runinga itarejeshwa katika hali iliyotoka nayo kiwandani."</string>
-    <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa simu  itarejeshwa katika mfumo chaguo msingi ilivyotoka kiwandani."</string>
+    <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa simu  itarejeshwa katika mfumo chaguomsingi ilivyotoka kiwandani."</string>
     <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Umekosea katika kuweka mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> bila kufaulu, utaombwa kufungua kompyuta yako ndogo kwa kutumia akaunti yako ya barua pepe.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
     <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Umekosea kuchora mchoro wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukikosea majaribio mengine <xliff:g id="NUMBER_1">%2$d</xliff:g>, utaombwa ufungue runinga yako ukitumia akaunti ya barua pepe.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Umekosea kuchora mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> yasiyofaulu, utaombwa kufungua simu yako kwa kutumia akaunti ya barua pepe.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 2c7485a..0edf58c 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"விமானப் பயன்முறை"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"விமானப் பயன்முறை இயக்கத்தில் உள்ளது"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"விமானப் பயன்முறை முடக்கத்தில் உள்ளது"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"பேட்டரி சேமிப்பான்"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"பேட்டரி சேமிப்பான் ஆஃப் செய்யப்பட்டுள்ளது"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"பேட்டரி சேமிப்பான் ஆன் செய்யப்பட்டுள்ளது"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"அமைப்பு"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"உதவி"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"குரல் உதவி"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"இருப்பிடம்"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"இந்தச் சாதனத்தின் இருப்பிடத்தை அறிந்து கொள்ள"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"இந்தச் சாதனத்தின் இருப்பிடத்தை அணுகுவதற்கு &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; பயன்பாட்டை அனுமதிக்கவா?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"இந்த ஆப்ஸைப் பயன்படுத்தும் சமயத்தில் மட்டுமே, இது உங்கள் இருப்பிடத்தை அணுகும்."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"இதன் இருப்பிடத்தை எப்போதுமே &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; அணுக அனுமதிக்கவா?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"இந்த ஆப்ஸைப் பயன்படுத்தாத சமயங்களில் கூட, இது உங்கள் இருப்பிடத்தை அணுகும்."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"கேலெண்டர்"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"கேலெண்டரை அணுகலாம்"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"கேலெண்டரை அணுகுவதற்கு &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; பயன்பாட்டை அனுமதிக்கவா?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"இந்தப் பயன்பாடு உங்கள் மொபைலில் கேலெண்டர் நிகழ்வுகளைச் சேர்க்கலாம், அகற்றலாம் அல்லது மாற்றலாம். இந்தப் பயன்பாடு கேலெண்டர் உரிமையாளர்கள் அனுப்பியது போல் தோன்றும் செய்திகளை அனுப்பலாம் அல்லது உரிமையாளர்களிடம் தெரிவிக்காமலே கேலெண்டரில் நிகழ்வுகளை மாற்றலாம்."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"கூடுதல் இட வழங்குநரின் கட்டளைகளின் அணுகல்"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"கூடுதல் இட வழங்குநர் கட்டளைகளை அணுகப் பயன்பாட்டை அனுமதிக்கிறது. இது, GPS அல்லது பிற இருப்பிட மூலங்களின் செயல்பாட்டை இடைமறிக்க பயன்பாட்டை அனுமதிக்கலாம்."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"துல்லியமான இருப்பிடத்தை அணுகுதல் (GPS மற்றும் நெட்வொர்க் அடிப்படையில்)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"இந்தப் பயன்பாடு GPS அல்லது நெட்வொர்க் மூலங்களின் (செல் கோபுரங்கள், வைஃபை நெட்வொர்க்குகள் போன்றவை) அடிப்படையில் உங்கள் இருப்பிடத்தைப் பெறலாம். பயன்பாடு பயன்படுத்தும் வகையில், உங்கள் மொபைலில் இந்த இருப்பிடச் சேவைகள் இயக்கப்பட்டு, கிடைக்கும்படி இருக்க வேண்டும். இதனால் பேட்டரி அதிகம் பயன்படுத்தப்படலாம்."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"முன்புலத்தில் இயங்கும்போது மட்டும் துல்லியமான இருப்பிடத்தைக் கண்டறிதல்"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"இந்த ஆப்ஸ் முன்புலத்தில் இயங்கும்போது மட்டுமே நீங்கள் இருக்கும் இடத்தைத் துல்லியமாகக் கண்டறியும். உங்கள் மொபைலில், இருப்பிடச் சேவைகளை ஆப்ஸ் பயன்படுத்துவதற்கு வசதியாக, அவை ஆன் செய்யப்பட்டிருக்க வேண்டும். இதனால் பேட்டரி அதிகம் பயன்படுத்தப்படலாம்."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"தோராயமான இருப்பிடத்தை அணுகுதல் (நெட்வொர்க் அடிப்படையில்)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"இந்தப் பயன்பாடு நெட்வொர்க் மூலங்களின் (செல் கோபுரங்கள், வைஃபை நெட்வொர்க்குகள் போன்றவை) அடிப்படையில் உங்கள் இருப்பிடத்தைப் பெறலாம். பயன்பாடு பயன்படுத்தும் வகையில், உங்கள் டேப்லெட்டில் இந்த இருப்பிடச் சேவைகள் இயக்கப்பட்டு, கிடைக்கும்படி இருக்க வேண்டும்."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"இந்தப் பயன்பாடு நெட்வொர்க் மூலங்களின் (செல் கோபுரங்கள், வைஃபை நெட்வொர்க்குகள் போன்றவை) அடிப்படையில் உங்கள் இருப்பிடத்தைப் பெறலாம். பயன்பாடு பயன்படுத்தும் வகையில், உங்கள் டிவியில் இந்த இருப்பிடச் சேவைகள் இயக்கப்பட்டு, கிடைக்கும்படி இருக்க வேண்டும்."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"இந்தப் பயன்பாடு நெட்வொர்க் மூலங்களின் (செல் கோபுரங்கள், வைஃபை நெட்வொர்க்குகள் போன்றவை) அடிப்படையில் உங்கள் இருப்பிடத்தைப் பெறலாம். பயன்பாடு பயன்படுத்தும் வகையில், உங்கள் மொபைலில் இந்த இருப்பிடச் சேவைகள் இயக்கப்பட்டு, கிடைக்கும்படி இருக்க வேண்டும்."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"பின்புலத்தில் இயங்கும்போது துல்லியமான இடத்தைக் கண்டறிதல்"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"இந்த ஆப்ஸ் பின்புலத்தில் இயங்கும்போது நீங்கள் இருக்கும் இடத்தைத் துல்லியமாகக் கண்டறியும். உங்கள் மொபைலில், இருப்பிடச் சேவைகளை ஆப்ஸ் பயன்படுத்துவதற்கு வசதியாக, அவை ஆன் செய்யப்பட்டிருக்க வேண்டும். இதனால் பேட்டரி அதிகம் பயன்படுத்தப்படலாம்."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"எனது ஆடியோ அமைப்புகளை மாற்றுதல்"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ஒலியளவு மற்றும் வெளியீட்டிற்கு ஸ்பீக்கர்கள் பயன்படுத்தப்படுவது போன்ற ஒட்டுமொத்த ஆடியோ அமைப்புகளைக் கட்டுப்படுத்தப் பயன்பாட்டை அனுமதிக்கிறது."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ஆடியோவைப் பதிவுசெய்தல்"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"கைரேகை ஐகான்"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"முக அங்கீகாரத்திற்கான வன்பொருளை நிர்வகித்தல்"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"உபயோகிப்பதற்காக முக டெம்ப்ளேட்டுகளை சேர்க்கும்/நீக்கும் முறைகளை இயக்க, ஆப்ஸை அனுமதிக்கும்."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"முக அங்கீகாரத்திற்கான வன்பொருளைப் பயன்படுத்துதல்"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"அடையாளம் காண்பதற்கு, முக அங்கீகார வன்பொருளைப் பயன்படுத்த ஆப்ஸை அனுமதிக்கிறது"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"அடையாளம் காண முடியவில்லை. மீண்டும் முயலவும்."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"முகம் பிரகாசமாக உள்ளது. குறைந்த ஒளியில் முயலவும்."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"முகம் தெரியவில்லை, வெளிச்சமான இடத்தில் முயலவும்."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"சென்சாரை முகத்திலிருந்து சற்று தொலைவில் நகர்த்துக."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"சென்சாரை முகத்திற்கு அருகில் கொண்டு வரவும்."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"சென்சாரை மேலே உயர்த்தவும்."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"சென்சாரைக் கீழே நகர்த்தவும்."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"சென்சாரை வலது புறமாக நகர்த்தவும்."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"சென்சாரை இடது புறமாக நகர்த்தவும்."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"சென்சாரைப் பார்க்கவும்."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"முகம் தெரியவில்லை."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"சாதனத்தின் முன், முகத்தை அசையாமல் காண்பிக்கவும்."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"முக அங்கீகாரத்திற்கான வன்பொருள் இல்லை."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"முகப் பதிவிற்கான நேரம் முடிந்தது. மீண்டும் முயல்க."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"முகத்தைச் சேமிக்க இயலாது."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"முக அங்கீகாரச் செயல்பாடு ரத்துசெய்யப்பட்டது."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"பலமுறை முயன்றுவிட்டீர்கள். பிறகு முயலவும்."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"பலமுறை தோல்வி. முக அங்கீகாரம் முடக்கப்பட்டது."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"மீண்டும் முயலவும்."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"முகம் எதுவும் பதிவு செய்யப்படவில்லை."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"இந்தச் சாதனத்தில், முக அங்கீகாரத்திற்கான சென்சார் இல்லை"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"முகம் <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"முக ஐகான்"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ஒத்திசைவு அமைப்புகளைப் படித்தல்"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"கணக்கிற்கான ஒத்திசைவு அமைப்புகளைப் படிக்க பயன்பாட்டை அனுமதிக்கிறது. எடுத்துக்காட்டாக, பீப்பிள் பயன்பாடு கணக்குடன் ஒத்திசைக்கப்பட்டுள்ளதா என்பதை இது தீர்மானிக்கலாம்."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ஒத்திசைவை இயக்குவதையும், முடக்குவதையும் மாற்றுதல்"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"வைஃபையில் இண்டர்நெட் அணுகல் இல்லை"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"விருப்பங்களுக்கு, தட்டவும்"</string>
+    <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">"இந்தச் சாதனத்தில், ’5GHz மட்டும்’ எனும் முன்னுரிமைத் தேர்வு ஆதரிக்கப்படவில்லை. எனினும் 5GHz அலைவரிசை கிடைக்கும்போது, சாதனம் அதைப் பயன்படுத்திக்கொள்ளும்."</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>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 7c31e0a..98b737f 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ఎయిర్‌ప్లేన్ మోడ్"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ఎయిర్‌ప్లేన్ మోడ్ ఆన్‌లో ఉంది"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ఎయిర్‌ప్లేన్ మోడ్ ఆఫ్‌లో ఉంది"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"బ్యాటరీ సేవర్"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"బ్యాటరీ సేవర్ ఆఫ్‌లో ఉంది"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"బ్యాటరీ సేవర్ ఆన్‌లో ఉంది"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"సెట్టింగ్‌లు"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"సహాయం"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"వాయిస్ అసిస్టెంట్"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"స్థానం"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"ఈ పరికర స్థానాన్ని యాక్సెస్ చేయడానికి"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"ఈ పరికర స్థానాన్ని యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని అనుమతించాలా?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"మీరు యాప్‌ని ఉపయోగిస్తున్నప్పుడు మాత్రమే యాప్ స్థానానికి యాక్సెస్ కలిగి ఉంటుంది."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ఈ పరికర స్థానాన్ని యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని ఎల్లప్పుడూ అనుమతించాలా?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ఈ యాప్ ఎల్లప్పుడూ స్థానానికి యాక్సెస్ కలిగి ఉంటుంది, మీరు యాప్‌ని ఉపయోగించనప్పుడు కూడా."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"క్యాలెండర్"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"మీ క్యాలెండర్‌ను యాక్సెస్ చేయడానికి"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"మీ క్యాలెండర్‌ని యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని అనుమతించాలా?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ఈ యాప్ మీ ఫోన్‌లో క్యాలెండర్ ఈవెంట్‌లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా సందేశాలను పంపగలదు లేదా ఈవెంట్‌లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"అదనపు స్థాన ప్రదాత ఆదేశాలను యాక్సెస్ చేయడం"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"అదనపు స్థాన ప్రదాత ఆదేశాలను యాక్సెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఇది GPS లేదా ఇతర స్థాన మూలాల నిర్వహణలో యాప్‌ ప్రమేయం ఉండేలా అనుమతించవచ్చు."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"ఖచ్చితమైన స్థానాన్ని (GPS మరియు నెట్‌వర్క్-ఆధారితం) యాక్సెస్ చేయడం"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"ఈ యాప్‌ GPS ఆధారంగా లేదా సెల్ టవర్‌లు, Wi-Fi నెట్‌వర్క్‌ల వంటి నెట్‌వర్క్ స్థాన మూలాధారాల ఆధారంగా మీ స్థానాన్ని తెలుసుకోగలదు. యాప్‌ ఉపయోగించడానికి మీ ఫోన్‌లో ఈ స్థాన సేవలను తప్పనిసరిగా ఆన్ చేయాలి మరియు అందుబాటులో ఉండాలి. ఇది బ్యాటరీ వినియోగాన్ని పెంచవచ్చు."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"స్క్రీన్‌పై ఉన్నప్పుడు మాత్రమే ఖచ్చితమైన స్థానాన్ని యాక్సెస్ చేయండి"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ఈ యాప్ స్క్రీన్‌పై ఉన్నప్పుడు మాత్రమే అది మీ ఖచ్చితమైన స్థానాన్ని తెలుసుకోగలదు. యాప్‌ ఉపయోగించడానికి మీ ఫోన్‌లో ఈ స్థాన సేవలను తప్పనిసరిగా ఆన్ చేయాలి మరియు అందుబాటులో ఉండాలి. ఇది బ్యాటరీ వినియోగాన్ని పెంచవచ్చు."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ఇంచుమించు స్థానాన్ని (నెట్‌వర్క్-ఆధారితం) యాక్సెస్ చేయడం"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ఈ యాప్‌ సెల్ టవర్‌లు మరియు Wi-Fi నెట్‌వర్క్‌ల వంటి నెట్‌వర్క్ మూలాధారాల ఆధారంగా మీ స్థానాన్ని తెలుసుకోగలదు. యాప్‌ ఉపయోగించడానికి మీ టాబ్లెట్‌లో ఈ స్థాన సేవలను తప్పనిసరిగా ఆన్ చేయాలి మరియు అందుబాటులో ఉండాలి."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"ఈ యాప్‌ సెల్ టవర్‌లు మరియు Wi-Fi నెట్‌వర్క్‌ల వంటి నెట్‌వర్క్ మూలాధారాల ఆధారంగా మీ స్థానాన్ని తెలుసుకోగలదు. యాప్‌ ఉపయోగించడానికి మీ టీవీలో ఈ స్థాన సేవలను తప్పనిసరిగా ఆన్ చేయాలి మరియు అందుబాటులో ఉండాలి."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"ఈ యాప్‌ సెల్ టవర్‌లు మరియు Wi-Fi నెట్‌వర్క్‌ల వంటి నెట్‌వర్క్ మూలాధారాల ఆధారంగా మీ స్థానాన్ని తెలుసుకోగలదు. యాప్‌ ఉపయోగించడానికి మీ ఫోన్‌లో ఈ స్థాన సేవలను తప్పనిసరిగా ఆన్ చేయాలి మరియు అందుబాటులో ఉండాలి."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"నేపథ్యంలో ఖచ్చితమైన స్థానాన్ని యాక్సెస్ చేయి"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"ఈ యాప్ నేపథ్యంలో ఉన్నప్పుడు ఎప్పుడైనా అది మీ ఖచ్చితమైన స్థానాన్ని తెలుసుకోగలదు. యాప్‌ ఉపయోగించడానికి మీ ఫోన్‌లో ఈ స్థాన సేవలను తప్పనిసరిగా ఆన్ చేయాలి మరియు అందుబాటులో ఉండాలి. ఇది బ్యాటరీ వినియోగాన్ని పెంచవచ్చు."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"మీ ఆడియో సెట్టింగ్‌లను మార్చడం"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"వాల్యూమ్ మరియు అవుట్‌పుట్ కోసం ఉపయోగించాల్సిన స్పీకర్ వంటి సార్వజనీన ఆడియో సెట్టింగ్‌లను సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ఆడియోను రికార్డ్ చేయడం"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"వేలిముద్ర చిహ్నం"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"ముఖ ప్రమాణీకరణ హార్డ్‌వేర్‌ను నిర్వహించండి"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"వినియోగం కోసం ముఖ టెంప్లేట్‌లను జోడించే మరియు తొలగించే పద్ధతులను అమలు చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ముఖ ప్రమాణీకరణ హార్డ్‌వేర్‌ను వాడండి"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ప్రమాణీకరణ కోసం ముఖ ప్రామాణీకరణ హార్డ్‌వేర్‌ను ఉపయోగించడానికి యాప్‌ని అనుమతిస్తుంది"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"ముఖాన్ని ప్రాసెస్ చేయడం సాధ్యపడలేదు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"ముఖం చాలా ప్రకాశవంతంగా ఉంటుంది. దయచేసి తక్కువ కాంతిలో ప్రయత్నించండి."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"ముఖం చాలా చీకటిగా ఉంది. దయచేసి కాంతి మూలాన్ని వెతకండి."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"దయచేసి ముఖం నుండి దూరంగా సెన్సార్‌ను జరపండి."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"దయచేసి సెన్సార్‌ను ముఖానికి మరింత దగ్గరగా తీసుకురండి."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"దయచేసి సెన్సార్‌ను ఎక్కువకు జరపండి."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"దయచేసి సెన్సార్‌ను తక్కువకు జరపండి."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"దయచేసి సెన్సార్‌ను కుడికి జరపండి."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"దయచేసి సెన్సార్‌ను ఎడమవైపుకు జరపండి."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"దయచేసి సెన్సార్‌ను చూడండి."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"ఎలాంటి ముఖం గుర్తించబడలేదు."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"పరికరానికి నేరుగా ముఖాన్ని స్థిరంగా ఉంచండి."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"ముఖ హార్డ్‌వేర్ అందుబాటులో లేదు."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"ముఖ గడువు సమయం చేరుకుంది. మళ్లీ ప్రయత్నించండి."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"ముఖం నిల్వ చేయబడదు."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"ముఖ కార్యకలాపం రద్దయింది."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"చాలా ఎక్కువ ప్రయత్నాలు చేసారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"చాలా ఎక్కువ ప్రయత్నాలు చేసారు. ముఖ ప్రమాణీకరణ నిలిపివేయబడింది."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"మళ్లీ ప్రయత్నించండి."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"ముఖం నమోదు చేయబడలేదు."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"ఈ పరికరంలో ముఖ ప్రామాణీకరణ సెన్సార్ లేదు"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"ముఖ <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"ముఖ చిహ్నం"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"సమకాలీకరణ సెట్టింగ్‌లను చదవడం"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ఖాతా యొక్క సమకాలీకరణ సెట్టింగ్‌లను చదవడానికి యాప్‌ను అనుమతిస్తుంది. ఉదాహరణకు, వ్యక్తుల యాప్‌ ఖాతాతో సమకాలీకరించబడాలా లేదా అనే విషయాన్ని ఇది నిశ్చయించవచ్చు."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"సమకాలీకరణను ఆన్ మరియు ఆఫ్‌కు టోగుల్ చేయడం"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fiకి ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ఎంపికల కోసం నొక్కండి"</string>
+    <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">"ఈ పరికరం 5GHz కోసం మాత్రమే మీ ప్రాధాన్యతకు మద్దతు ఇవ్వదు. బదులుగా, ఈ పరికరం అందుబాటులో ఉన్నప్పుడు 5GHz బ్యాండ్‌ను ఉపయోగిస్తుంది."</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>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 781c716..89c2e44 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"โหมดใช้งานบนเครื่องบิน"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"เปิดโหมดใช้งานบนเครื่องบิน"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"โหมดใช้งานบนเครื่องบินปิดทำงานอยู่"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"โหมดประหยัดแบตเตอรี่"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ปิดโหมดประหยัดแบตเตอรี่อยู่"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"เปิดโหมดประหยัดแบตเตอรี่อยู่"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"การตั้งค่า"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"ผู้ช่วย"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ตัวช่วยเสียง"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"ตำแหน่ง"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"เข้าถึงตำแหน่งของอุปกรณ์นี้"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงตำแหน่งของอุปกรณ์นี้ไหม"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"แอปจะมีสิทธิ์เข้าถึงตำแหน่งได้ในขณะที่คุณกำลังใช้แอปเท่านั้น"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงตำแหน่งของอุปกรณ์นี้ทุกครั้งไหม"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"แอปจะมีสิทธิ์เข้าถึงตำแหน่งเสมอแม้ว่าคุณจะไม่ได้ใช้แอป"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"ปฏิทิน"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"เข้าถึงปฏิทิน"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงปฏิทินไหม"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"แอปนี้สามารถเพิ่ม นำออก หรือเปลี่ยนกิจกรรมในปฏิทินบนโทรศัพท์ของคุณ แอปนี้สามารถส่งข้อความที่ดูเหมือนว่ามาจากเจ้าของปฏิทิน หรือเปลี่ยนแปลงกิจกรรมโดยไม่แจ้งให้เจ้าของทราบ"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"เข้าถึงคำสั่งของโปรแกรมแจ้งตำแหน่งพิเศษ"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"อนุญาตให้แอปเข้าถึงคำสั่งของผู้ให้บริการตำแหน่งเพิ่มเติม ซึ่งอาจทำให้แอปสามารถแทรกแซงการทำงานของ GPS หรือต้นทางของตำแหน่งอื่นๆ ได้"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"เข้าถึงตำแหน่งที่แม่นยำ (อิงจาก GPS และเครือข่าย)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"แอปนี้สามารถรับตำแหน่งของคุณโดยอิงจากแหล่งข้อมูลเครือข่าย เช่น เสาสัญญาณมือถือและเครือข่าย Wi-Fi แอปจะใช้บริการตำแหน่งเหล่านี้ได้ต่อเมื่อคุณเปิดบริการและบริการพร้อมใช้งานในโทรศัพท์ของคุณ การเปิดบริการนี้อาจเพิ่มการใช้แบตเตอรี่"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"เข้าถึงตำแหน่งที่แม่นยำเมื่ออยู่เบื้องหน้าเท่านั้น"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"แอปนี้รับตำแหน่งที่แม่นยำของคุณได้เมื่อทำงานอยู่เบื้องหน้าเท่านั้น แอปจะใช้บริการตำแหน่งเหล่านี้ได้ต่อเมื่อคุณเปิดบริการและบริการพร้อมใช้งานในโทรศัพท์ของคุณ ซึ่งอาจทำให้มีการใช้แบตเตอรี่มากขึ้น"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"เข้าถึงตำแหน่งโดยประมาณ (อิงจากเครือข่าย)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"แอปนี้สามารถรับตำแหน่งของคุณโดยอิงจากแหล่งข้อมูลเครือข่าย เช่น เสาสัญญาณมือถือและเครือข่าย Wi-Fi แอปจะใช้บริการตำแหน่งเหล่านี้ได้ต่อเมื่อคุณเปิดบริการและบริการพร้อมใช้งานในแท็บเล็ตของคุณ"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"แอปนี้สามารถรับตำแหน่งของคุณโดยอิงจากแหล่งข้อมูลเครือข่าย เช่น เสาสัญญาณมือถือและเครือข่าย Wi-Fi แอปจะใช้บริการตำแหน่งเหล่านี้ได้ต่อเมื่อคุณเปิดบริการและบริการพร้อมใช้งานในทีวีของคุณ"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"แอปนี้สามารถรับตำแหน่งของคุณโดยอิงจากแหล่งข้อมูลเครือข่าย เช่น เสาสัญญาณมือถือและเครือข่าย Wi-Fi แอปจะใช้บริการตำแหน่งเหล่านี้ได้ต่อเมื่อคุณเปิดบริการและบริการพร้อมใช้งานในโทรศัพท์ของคุณ"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"เข้าถึงตำแหน่งที่แม่นยำเมื่ออยู่เบื้องหลัง"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"แอปนี้รับตำแหน่งที่แม่นยำของคุณได้ทุกเมื่อที่ทำงานอยู่เบื้องหลัง แอปจะใช้บริการตำแหน่งเหล่านี้ได้ต่อเมื่อคุณเปิดบริการและบริการพร้อมใช้งานในโทรศัพท์ของคุณ ซึ่งอาจทำให้มีการใช้แบตเตอรี่มากขึ้น"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"เปลี่ยนการตั้งค่าเสียงของคุณ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"อนุญาตให้แอปพลิเคชันปรับเปลี่ยนการตั้งค่าเสียงทั้งหมดได้ เช่น ระดับเสียงและลำโพงที่จะใช้งาน"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"บันทึกเสียง"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ไอคอนลายนิ้วมือ"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"จัดการฮาร์ดแวร์ตรวจสอบสิทธิ์ด้วยใบหน้า"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"อนุญาตให้แอปเรียกใช้วิธีเพิ่มและลบเทมเพลตใบหน้าสำหรับการใช้งาน"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ใช้ฮาร์ดแวร์ตรวจสอบสิทธิ์ด้วยใบหน้า"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"อนุญาตให้แอปใช้ฮาร์ดแวร์ตรวจสอบสิทธิ์ด้วยใบหน้าเพื่อตรวจสอบสิทธิ์"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"ประมวลผลใบหน้าไม่ได้ โปรดลองอีกครั้ง"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"ใบหน้าสว่างเกินไป โปรดลดแสง"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"ใบหน้ามืดเกินไป โปรดเปิดแหล่งกำเนิดแสง"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"โปรดขยับเซ็นเซอร์ให้ห่างจากใบหน้ามากขึ้น"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"โปรดขยับเซ็นเซอร์ให้ใกล้ใบหน้ามากขึ้น"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"โปรดขยับเซ็นเซอร์ให้สูงขึ้น"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"โปรดขยับเซ็นเซอร์ให้ต่ำลง"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"โปรดขยับเซ็นเซอร์ไปทางขวา"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"โปรดขยับเซ็นเซอร์ไปทางซ้าย"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"โปรดมองที่เซ็นเซอร์"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"ไม่พบใบหน้า"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"ให้ใบหน้าอยู่นิ่งๆ หน้าอุปกรณ์"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"ฮาร์ดแวร์รู้จำใบหน้าไม่พร้อมใช้งาน"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"หมดเวลาใช้ใบหน้าแล้ว โปรดลองอีกครั้ง"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"จัดเก็บข้อมูลใบหน้าไม่ได้"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"ยกเลิกการดำเนินการกับใบหน้าแล้ว"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"ดำเนินการหลายครั้งเกินไป ลองอีกครั้งในภายหลัง"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ดำเนินการหลายครั้งเกินไป ปิดใช้การตรวจสอบสิทธิ์ด้วยใบหน้าแล้ว"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"ลองอีกครั้ง"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"ไม่ได้ลงทะเบียนใบหน้าไว้"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ตรวจสอบสิทธิ์ด้วยใบหน้า"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"ใบหน้า <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"ไอคอนใบหน้า"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"อ่านการตั้งค่าการซิงค์แล้ว"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"อนุญาตให้แอปพลิเคชันอ่านการตั้งค่าการซิงค์ของบัญชี ตัวอย่างเช่น การอนุญาตนี้สามารถระบุได้ว่าแอปพลิเคชัน People ซิงค์กับบัญชีหรือไม่"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"สลับระหว่างเปิดและปิดการซิงค์"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi เชื่อมต่ออินเทอร์เน็ตไม่ได้"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"แตะเพื่อดูตัวเลือก"</string>
+    <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">"อุปกรณ์นี้ไม่รองรับค่ากำหนดของคุณเฉพาะสำหรับ 5 GHz เท่านั้น และจะใช้ย่านความถี่ 5 GHz แทน เมื่อใช้ได้"</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="NEW_NETWORK">%1$s</xliff:g> เมื่อ <xliff:g id="PREVIOUS_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>
@@ -1252,7 +1288,7 @@
     <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="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"อุปกรณ์ที่พ่วงไม่สามารถใช้งานร่วมกับโทรศัพท์นี้ แตะเพื่อดูข้อมูลเพิ่มเติม"</string>
     <string name="adb_active_notification_title" msgid="6729044778949189918">"เชื่อมต่อการแก้ไขข้อบกพร่องผ่าน USB แล้ว"</string>
     <string name="adb_active_notification_message" msgid="7463062450474107752">"แตะเพื่อปิดการแก้ไขข้อบกพร่อง USB"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"เลือกเพื่อปิดใช้งานการแก้ไขข้อบกพร่อง USB"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index fd43538..4453309 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Airplane mode"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Naka-ON ang airplane mode"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Naka-OFF ang airplane mode"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Pangtipid sa baterya"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"NAKA-OFF ang Pangtipid sa baterya"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"NAKA-ON ang Pangtipid sa baterya"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Mga Setting"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Tulong"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Lokasyon"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"i-access ang lokasyon ng device na ito"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang lokasyon ng device na ito?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Maa-access lang ng app ang lokasyon habang ginagamit mo ang app."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Laging payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang lokasyon?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Palaging maa-access ng app ang lokasyon, kahit na hindi mo ginagamit ang app."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendaryo"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"i-access ang iyong kalendaryo"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang iyong kalendaryo?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Makakapagdagdag, makakapag-alis o makakapagbago ang app na ito ng mga event sa kalendaryo sa iyong telepono. Magagawa ng app na ito na magpadala ng mga mensahe na maaaring mukhang mula sa mga may-ari ng kalendaryo o magbago ng mga event nang hindi inaabisuhan ang mga may-ari nito."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"i-access ang mga dagdag na command ng provider ng lokasyon"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Nagbibigay-daan sa app na mag-access ng mga karagdagang command ng provider ng lokasyon. Maaari nitong bigyang-daan ang app na gambalain ang pagpapatakbo ng GPS o ng iba pang mga pinagmulan ng lokasyon."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"i-access ang tumpak na lokasyon (batay sa GPS at network)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Makukuha ng app na ito ang iyong lokasyon batay sa GPS o mga pinagmulan ng lokasyon ng network gaya ng mga cell tower at Wi-Fi network. Ang mga serbisyo ng lokasyon na ito ay dapat naka-on at available sa iyong telepono para sa app upang magamit ang mga ito. Maaaring tumaas ang pagkonsumo sa baterya dahil dito."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"i-access lang ang tumpak na lokasyon sa foreground"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Makukuha lang ng app na ito ang iyong eksaktong lokasyon kapag nasa foreground ito. Ang mga serbisyo ng lokasyon na ito ay dapat naka-on at available sa iyong telepono para magamit ng app ang mga ito. Maaaring lumakas ang pagkonsumo ng baterya dahil dito."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"i-access ang tinatantyang lokasyon (batay sa network)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Makukuha ng app na ito ang iyong lokasyon batay sa mga pinagmulan ng network gaya ng mga cell tower at Wi-Fi network. Ang mga serbisyo ng lokasyon na ito ay dapat naka-on at available sa iyong tablet para sa app upang magamit ang mga ito."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Makukuha ng app na ito ang iyong lokasyon batay sa mga pinagmulan ng network gaya ng mga cell tower at Wi-Fi network. Ang mga serbisyo ng lokasyon na ito ay dapat naka-on at available sa iyong TV para sa app upang magamit ang mga ito."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Makukuha ng app na ito ang iyong lokasyon batay sa mga pinagmulan ng network gaya ng mga cell tower at Wi-Fi network. Ang mga serbisyo ng lokasyon na ito ay dapat naka-on at available sa iyong telepono para sa app upang magamit ang mga ito."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"i-access ang tumpak na lokasyon sa background"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Makukuha ng app na ito ang iyong eksaktong lokasyon anumang oras na nasa background ito. Ang mga serbisyo ng lokasyon na ito ay dapat naka-on at available sa iyong telepono para magamit ng app ang mga ito. Maaaring lumakas ang pagkonsumo ng baterya dahil dito."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"baguhin ang mga setting ng iyong audio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Pinapayagan ang app na baguhin ang mga pandaigdigang setting ng audio gaya ng volume at kung aling speaker ang ginagamit para sa output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"mag-record ng audio"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icon ng fingerprint"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"pamahalaan ang hardware sa authentication ng mukha"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Pumapayag na mag-invoke ang app ng paraang magdagdag at mag-delete ng template ng mukha."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"gumamit ng hardware sa pag-authenticate ng mukha"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Pumapayag na gumamit ng face authentication hardware ang app para sa pag-authenticate"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Hindi maiproseso ang mukha. Pakisubukang muli."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Masyadong maliwanag. Pakisubukan sa mas madilim."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Masyadong madilim. Huwag takpan ang ilaw."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Pakilayo sa mukha ang sensor."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Pakilapit sa mukha ang sensor."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Pakitaas ang sensor."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Pakibaba ang sensor."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Pakiusog pakanan ang sensor."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Pakiusog pakaliwa ang sensor."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Tumingin sa sensor."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Walang natukoy na mukha."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Panatilihin sa harap ng device ang mukha."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hindi available ang hardware para sa mukha."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Nag-time out ang mukha. Subukang muli."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Hindi ma-store ang mukha."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Nakansela ang operation kaugnay ng mukha."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Masyadong maraming pagsubok. Subukang muli mamaya."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Sobrang pagsubok. Bawal na: facial authentication."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Subukang muli."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Walang naka-enroll na mukha."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Walang sensor ng pag-authenticate ng mukha ang device na ito"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Mukha <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Face icon"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"basahin ang mga setting ng sync"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Pinapayagan ang app na basahin ang mga setting ng pag-sync para sa isang account. Halimbawa, matutukoy nito kung naka-sync ang app na Mga Tao sa isang account."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"I-toggle on at off ang pag-sync"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Walang access sa internet ang Wi-Fi"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"I-tap para sa mga opsyon"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Mga pagbabago sa mga setting ng iyong hotspot"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Nagbago ang band ng iyong hotspot."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Hindi sinusuportahan ng device na ito ang kagustuhan mong gumamit lang ng 5GHz. Sa halip, gagamitin ng device na ito ang 5GHz na band kapag available."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Lumipat sa <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Ginagamit ng device ang <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kapag walang access sa internet ang <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Maaaring may mga malapat na singilin."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Lumipat sa <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mula sa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index b9461b2..a71bc4a 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Uçak modu"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Uçak modu AÇIK"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Uçak modu KAPALI"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Pil tasarrufu"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Pil tasarrufu KAPALI"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Pil tasarrufu AÇIK"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Ayarlar"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Asist"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Sesli Yardım"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Konum"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"bu cihazın konumuna erişme"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının bu cihazın konumuna erişmesine izin verilsin mi?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Bu uygulama konum bilgisine yalnızca kullanıldığı sırada erişebilecektir."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının bu cihazın konumuna erişmesine her zaman izin verilsin mi?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Bu uygulama, kullanılmadığında bile konum bilgisine her zaman erişebilecektir."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Takvim"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"takviminize erişme"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının takviminize erişmesine izin verilsin mi?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Bu uygulama, telefonunuza takvim etkinlikleri ekleyebilir, mevcut etkinlikleri kaldırabilir ve değiştirebilir. Bu uygulama, takvim sahiplerinden gelmiş gibi görünen iletiler gönderebilir veya takvim sahiplerinin bilgisi olmadan etkinlikleri değiştirebilir."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ek konum sağlayıcı komutlarına eriş"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Uygulamanın, ekstra konum sağlayıcı komutlarına erişmesine izin verir. Bu izin, uygulamanın GPS veya diğer konum kaynaklarının çalışmasını kesmesine olanak sağlayabilir."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"konum bilgilerine hassas olarak erişme (GPS ve ağ tabanlı)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Bu uygulama baz istasyonu ve kablosuz ağ gibi ağ kaynaklarını kullanarak konumunuzu belirleyebilir. Uygulamanın bu hizmetleri kullanabilmesi için telefonunuzda bu konum hizmetleri açık ve kullanılabilir olmalıdır. Bu hizmet pil tüketimini artırabilir."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"yalnızca ön planda kesin konuma erişme"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Bu uygulama yalnızca ön plandayken kesin konumunuzu alabilir. Uygulamanın bu hizmetleri kullanabilmesi için telefonunuzda bu konum hizmetleri açık ve kullanılabilir olmalıdır. Bu, pil tüketimini artırabilir."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"konum bilgilerine yaklaşık olarak erişme (ağ tabanlı)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Bu uygulama baz istasyonu ve kablosuz ağ gibi ağ kaynaklarını kullanarak konumunuzu belirleyebilir. Uygulamanın bu hizmetleri kullanabilmesi için tabletinizde bu konum hizmetleri açık ve kullanılabilir olmalıdır."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Bu uygulama baz istasyonu ve kablosuz ağ gibi ağ kaynaklarını kullanarak konumunuzu belirleyebilir. Uygulamanın bu hizmetleri kullanabilmesi için TV\'nizde bu konum hizmetleri açık ve kullanılabilir olmalıdır."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Bu uygulama baz istasyonu ve kablosuz ağ gibi ağ kaynaklarını kullanarak konumunuzu belirleyebilir. Uygulamanın bu hizmetleri kullanabilmesi için telefonunuzda bu konum hizmetleri açık ve kullanılabilir olmalıdır."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"arka planda kesin konuma erişme"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Bu uygulama arka plandayken istediği zaman kesin konumunuzu alabilir. Uygulamanın bu hizmetleri kullanabilmesi için telefonunuzda bu konum hizmetleri açık ve kullanılabilir olmalıdır. Bu, pil tüketimini artırabilir."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ses ayarlarınızı değiştirin"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Uygulamaya ses düzeyi ve ses çıkışı için kullanılan hoparlör gibi genel ses ayarlarını değiştirme izni verir."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ses kaydet"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Parmak izi simgesi"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"yüz kimlik doğrulaması donanımını yönetme"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Uygulamanın, kullanılacak yüz şablonlarını ekleme ve silme yöntemlerini başlatmasına izin verir."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"yüz kimlik doğrulaması donanımını kullanma"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Uygulamanın yüz kimlik doğrulaması donanımı kullanmasına izin verir"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Yüz işlenemedi. Lütfen tekrar deneyin."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Yüzünüz çok parlak. Lütfen ışığı kısmayı deneyin."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Yüzünüz çok karanlık. Lütfen ışığı artırın."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Lütfen sensörü yüzünüzden biraz uzaklaştırın."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Lütfen sensörü yüzünüze yaklaştırın."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Lütfen sensörü daha yukarı kaldırın."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Lütfen sensörü daha aşağı indirin."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Lütfen sensörü sağa kaydırın."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Lütfen sensörü sola kaydırın."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Lütfen sensöre bakın."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Yüz algılanmadı."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Yüzünüzü cihazın önünde sabit bir şekilde tutun."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Yüz donanımı kullanılamıyor."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Yüz için zaman aşımı oluştu. Tekrar deneyin."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Yüz kaydedilemiyor."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Yüz işlemi iptal edildi."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Çok fazla deneme yapıldı. Daha sonra tekrar deneyin."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Çok fazla deneme yapıldı. Yüz kimlik doğrulaması devre dışı bırakıldı."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Tekrar deneyin."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Herhangi bir yüz kaydedilmedi."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Bu cihazda yüz kimlik doğrulaması sensörü yok"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Yüz <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Yüz simgesi"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"senk. ayarlarını okuma"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Uygulamaya bir hesaba ait senkronizasyon ayarlarını okuma izni verir. Örneğin, bu izne sahip bir uygulama Kişiler uygulamasının bir hesapla senkronize olup olmadığını belirleyebilir."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"senkronizasyonu açma/kapatma"</string>
@@ -605,7 +638,7 @@
     <string name="policylab_disableCamera" msgid="6395301023152297826">"Kameraları devre dışı bırak"</string>
     <string name="policydesc_disableCamera" msgid="2306349042834754597">"Tüm cihaz kameralarının kullanımını engelleme."</string>
     <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Ekran kilidinin bazı özelliklerini devre dışı bırakma"</string>
-    <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Ekran kilidinin bazı özelliklerinin kullanılmasını önleyin."</string>
+    <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Ekran kilidinin bazı özelliklerinin kullanılmasını önleme."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Ev"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Kablosuz bağlantının internet erişimi yok"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Seçenekler için dokunun"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Hotspot ayarlarınız değişti"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Hotspot bandı değişti."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Bu cihaz yalnızca 5 GHz bandının kullanılmasına yönelik tercihinizi desteklemiyor. Bunun yerine, bu cihaz 5 GHz bandını mevcut olduğunda kullanacak."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ağına geçildi"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ağının internet erişimi olmadığında cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ağını kullanır. Bunun için ödeme alınabilir."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ağından <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ağına geçildi"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 8c6fd0a..6476134 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -242,9 +242,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Режим польоту"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Режим польоту ВВІМК."</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Режим польоту ВИМК."</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Режим енергозбереження"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Режим економії заряду акумулятора ВИМКНЕНО"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Режим економії заряду акумулятора ВВІМКНЕНО"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Налаштування"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Підказки"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Голос. підказки"</string>
@@ -282,6 +279,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Геодані"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"доступ до геоданих пристрою"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Надати додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до геоданих пристрою?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Додаток матиме доступ до геоданих, лише коли ви ним користуєтеся."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Завжди надавати додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до геоданих цього пристрою?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Додаток матиме доступ до геоданих, навіть коли ви ним не користуєтеся."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"отримувати доступ до календаря"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Надати додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до календаря?"</string>
@@ -408,12 +408,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Цей додаток може додавати, вилучати та змінювати події календаря на вашому телефоні. Він також може надсилати повідомлення від імені власників календаря або редагувати події без відома власників."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"отр. дост. до додат. команд пров. місцезн."</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Додаток отримуватиме доступ до додаткових команд постачальника геоданих. Можливе втручання додатка в роботу GPS чи інших джерел геоданих."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"отримувати дані про точне місцезнаходження (на основі GPS і мережі)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Цей додаток може отримувати дані про ваше місцезнаходження на основі даних GPS або джерел мережі, як-от антен мобільного зв’язку та мереж Wi-Fi. Щоб додаток міг користуватися цими службами локації, вони мають бути доступними й увімкненими на вашому телефоні. Це може пришвидшити розряджання акумулятора."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"отримувати доступ до даних про точне місцезнаходження лише в активному режимі"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Цей додаток може отримувати дані про ваше точне місцезнаходження лише в активному режимі. Щоб додаток користувався службами локації, вони мають бути наявні й увімкнені на вашому телефоні. Через це може швидше розряджатись акумулятор."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"отримувати дані про приблизне місцезнаходження (на основі мережі)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Цей додаток може отримувати дані про ваше місцезнаходження на основі джерел мережі, як-от антен мобільного зв’язку та мереж Wi-Fi. Щоб додаток міг користуватися цими службами локації, вони мають бути доступними й увімкненими на вашому планшеті."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Цей додаток може отримувати дані про ваше місцезнаходження на основі джерел мережі, як-от антен мобільного зв’язку та мереж Wi-Fi. Щоб додаток міг користуватися цими службами локації, вони мають бути доступними й увімкненими на вашому телевізорі."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Цей додаток може отримувати дані про ваше місцезнаходження на основі джерел мережі, як-от антен мобільного зв’язку та мереж Wi-Fi. Щоб додаток міг користуватися цими службами локації, вони мають бути доступними й увімкненими на вашому телефоні."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"отримувати доступ до даних про точне місцезнаходження у фоновому режимі"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Цей додаток може будь-коли отримувати дані про ваше точне місцезнаходження у фоновому режимі. Щоб додаток користувався службами локації, вони мають бути наявні й увімкнені на вашому телефоні. Через це може швидше розряджатись акумулятор."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"змінювати налаштув-ня звуку"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Дозволяє програмі змінювати загальні налаштування звуку, як-от гучність і динамік, який використовується для виводу сигналу."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"запис-ти аудіо"</string>
@@ -521,6 +523,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Значок відбитка пальця"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"керувати обладнанням для автентифікації облич"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Додаток може активувати способи додавання й видалення шаблонів облич."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"застосовувати обладнання для автентифікації облич"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Додаток може застосовувати обладнання для автентифікації облич"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Не вдалося розпізнати обличчя. Повторіть спробу."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Обличчя засвітле. Спробуйте знизити яскравість."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Обличчя затемне. Не заступайте джерело світла."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Перемістіть сканер далі від обличчя."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Перемістіть сканер ближче до обличчя."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Перемістіть сканер вище."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Перемістіть сканер нижче."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Перемістіть сканер праворуч."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Перемістіть сканер ліворуч."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Дивіться на сканер."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Обличчя не виявлено."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Тримайте обличчя нерухомо перед пристроєм."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Обладнання для сканування облич недоступне."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Час очікування обличчя минув. Повторіть спробу."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Не вдається зберегти обличчя."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Дію з обличчям скасовано."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Забагато спроб. Повторіть пізніше."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Забагато спроб. Автентифікацію обличчя вимкнено."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Повторіть спробу."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Немає зареєстрованих облич."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"На цьому пристрої немає сканера для автентифікації облич"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Обличчя <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Значок обличчя"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"читати налаштування синхронізації"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Дозволяє програмі читати налаштування синхронізації для облікового запису, наприклад, визначати, чи програма Люди синхронізується з обліковим записом."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"вмикати й вимикати синхронізацію"</string>
@@ -1219,6 +1252,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Мережа Wi-Fi не має доступу до Інтернету"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Торкніться, щоб відкрити опції"</string>
+    <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">"На цьому пристрої не підтримується налаштування лише 5 ГГц. Замість цього буде використовуватися діапазон 5 ГГц, якщо доступно."</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>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index aaaa13b..2ed0e6c 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ہوائی جہاز وضع"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ہوائی جہاز وضع آن ہے"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ہوائی جہاز وضع آف ہے"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"بیٹری سیور"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"بیٹری سیور آف ہے"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"بیٹری سیور آن ہے"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"ترتیبات"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"اسسٹ"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"مقام"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"اس آلہ کے مقام تک رسائی حاصل کریں"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اس آلہ کے مقام تک رسائی کی اجازت دیں؟"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"جب آپ ایپ استعمال کریں گے تبھی ایپ کو مقام تک رسائی حاصل ہوگی۔"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"‏‎&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‎ کو ہمیشہ اس آلہ کے مقام تک رسائی کرنے کی اجازت دیں؟"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"اگرچہ آپ ایپ استعمال نہ کر رہے ہوں تب بھی ایپ کو ہمیشہ مقام تک رسائی حاصل ہوگی۔"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"کیلنڈر"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"اپنے کیلنڈر تک رسائی حاصل کریں"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آپ کے کیلنڈر تک رسائی کی اجازت دیں؟"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"یہ اپپ آپ کے فون پر کیلنڈر ایونٹس کو شامل، ہٹا یا ترمیم کر سکتی ہے۔ یہ ایپ وہ پیغامات بھیج سکتی ہے جو کیلنڈر کے مالکان کی جانب سے آنے والے معلوم ہو سکتے ہیں یا ان مالکان کو اطلاع دیے بغیر ایونٹس تبدیل کر سکتی ہے۔"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"اضافی مقام فراہم کنندہ کی کمانڈز تک رسائی حاصل کریں"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"‏ایپ کو اضافی مقام فراہم کنندہ کی کمانڈز تک رسائی حاصل کرنے کی اجازت دیتی ہے۔ یہ ایپ کو GPS یا دوسرے مقام کے مآخذ کے عمل کے ساتھ مداخلت کرنے کی اجازت دے سکتی ہے۔"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"‏قطعی مقام تک رسائی حاصل کریں (GPS اور نیٹ ورک پر مبنی)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"‏GPS یا نیٹ ورک مقام کے مآخذات جیسے کہ سیل ٹاورز اور Wi-Fi نیٹ ورکس کی بنیاد پر یہ ایپ آپ کا مقام حاصل کر سکتی ہے۔ ایپ کو ان مقام کی سروسز کو استعمال کرنے کیلئے ان کا آن ہونا اور آپ کے فون پر دستیاب ہونا ضروری ہے۔ اس سے بیٹری کے استعمال میں اضافہ ہوسکتا ہے۔"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"صرف پیش منظر میں درست مقام تک رسائی حاصل کریں"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"یہ ایپ جب پس منظر میں ہوتی ہے تبھی یہ آپ کا صحیح مقام حاصل کر سکتی ہے۔ ایپ کو ان مقام کی سروسز کو استعمال کر سکنے کیلئے ان کا آن ہونا اور آپ کے فون پر دستیاب ہونا ضروری ہے۔"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"تخمینی مقام تک رسائی حاصل کریں (نیٹ ورک پر مبنی)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"‏نیٹ ورک مآخذات جیسے کہ سیل ٹاورز اور Wi-Fi نیٹ ورکس کی بنیاد پر یہ ایپ آپ کا مقام حاصل کر سکتی ہے۔ ایپ کو ان مقام کی سروسز کو استعمال کرنے کیلئے ان کا آن ہونا اور آپ کے ٹیبلیٹ پر دستیاب ہونا ضروری ہے۔"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"‏نیٹ ورک مآخذات جیسے کہ سیل ٹاورز اور Wi-Fi نیٹ ورکس کی بنیاد پر یہ ایپ آپ کا مقام حاصل کر سکتی ہے۔ ایپ کو ان مقام کی سروسز کو استعمال کرنے کیلئے ان کا آن ہونا اور آپ کے TV پر دستیاب ہونا ضروری ہے۔"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"‏نیٹ ورک مآخذات جیسے کہ سیل ٹاورز اور Wi-Fi نیٹ ورکس کی بنیاد پر یہ ایپ آپ کا مقام حاصل کر سکتی ہے۔ ایپ کو ان مقام کی سروسز کو استعمال کرنے کیلئے ان کا آن ہونا اور آپ کے فون پر دستیاب ہونا ضروری ہے۔"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"پس منظر میں درست مقام تک رسائی حاصل کریں"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"یہ ایپ جب بھی پس منظر میں ہو آپ کا صحیح مقام حاصل کر سکتی ہے۔ ایپ کو ان مقام کی سروسز کو استعمال کر سکنے کیلئے ان کا آن ہونا اور آپ کے فون پر دستیاب ہونا ضروری ہے۔"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"اپنے آڈیو کی ترتیبات کو تبدیل کریں"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ایپ کو مجموعی آڈیو ترتیبات جیسے والیوم اور آؤٹ پٹ کیلئے جو اسپیکر استعمال ہوتا ہے اس میں ترمیم کرنے کی اجازت دیتا ہے۔"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"آڈیو ریکارڈ کریں"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"فنگر پرنٹ آئیکن"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"چہرے کی توثیق کے ہارڈویئر کا نظم کریں"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"ایپ کو چہرے کی تمثیلات شامل اور حذف کرنے کے طریقوں کو کالعدم قرار دینے کی اجازت دیتا ہے۔"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"چہرے کی توثیق کا ہارڈویئر استعمال کریں"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ایپ کو توثیق کیلئے چہرے کا ہارڈ ویئر استعمال کرنے کی اجازت دیتا ہے"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"چہرے پر کارروائی نہیں ہو سکی۔ دوبارہ کوشش کریں۔"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"چہرے پر بہت زیادہ روشنی ہے۔ براہ کرم کم روشنی میں کوشش کریں۔"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"چہرے پر روشنی بہت کم ہے۔ براہ کرم روشنی بڑھائیں۔"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"براہ کرم سینسر کو چہرے سے تھوڑا دور رکھیں۔"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"براہ کرم سینسر کو چہرے سے تھوڑا قریب رکھیں۔"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"براہ کرم سینسر کو تھوڑا اوپر کریں۔"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"براہ کرم سینسر کو تھوڑا نیچے کریں۔"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"براہ کرم سینسر کو دائیں طرف کریں۔"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"براہ کرم سینسر کو بائیں طرف کریں۔"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"براہ کرم سینسر کی طرف دیکھیں۔"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"کسی چہرے کا پتہ نہیں چلا۔"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"آلہ کے سامنے چہرہ سیدھا رکھیں۔"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"چہرے کا ہارڈویئر دستیاب نہیں ہے۔"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"چہرہ پہچانے کی میعاد ختم ہو گئی۔ دوبارہ کوشش کریں۔"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"چہرے کو اسٹور نہیں کیا جا سکتا۔"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"چہرے پر ہونے والی کارروائی منسوخ ہو گئی۔"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"کافی زیادہ کوششیں کی گئیں۔ دوبارہ کوشش کریں۔"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"کافی زیادہ کوششیں کی گئیں۔ چہرے کی توثیق منسوخ ہو گئی۔"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"دوبارہ کوشش کریں۔"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"کوئی بھی چہرہ مندرج نہیں ہے۔"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"اس آلہ میں چہرے کی تصدیق کرنے والا سینسر نہیں ہے۔"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"چہرہ <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"چہرے کا آئیکن"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"مطابقت پذیری کی ترتیبات پڑھیں"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"‏ایپ کو کسی اکاؤنٹ کیلئے مطابقت پذیری کی ترتیبات پڑھنے کی اجازت دیتا ہے۔ مثلا، یہ تعین کرسکتا ہے کہ آیا People ایپ کسی اکاؤنٹ کے ساتھ مطابقت پذیر ہے۔"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"مطابقت پذیری آن اور آف ٹوگل کریں"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"‏Wi-Fi کو انٹرنیٹ تک رسائی نہیں ہے"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"اختیارات کیلئے تھپتھپائیں"</string>
+    <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">"‏یہ آلہ صرف 5GHz کے لئے آپ کی ترجیح کا تعاون نہیں کرے گا۔ بلکہ 5GHz بینڈ کے دستیاب ہونے پر اس کا استعمال کرے گا۔"</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>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 32dc0263..577e6b9 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Parvoz rejimi"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Parvoz usuli yoqilgan"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Parvoz rejimi o‘chirilgan"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Quvvat tejash rejimi"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Quvvat tejash rejimi YOQILMAGAN"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Quvvat tejash rejimi YONIQ"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Sozlamalar"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Yordam"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ovozli yordam"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Joylashuv"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"shu qurilmaning joylashuvi haqidagi axborotga kirish"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun bu qurilmaning joylashuvi haqidagi axborotdan foydalanishiga ruxsat berilsinmi?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Bu ilovadan foydalanilayotdangina u joylashuv axborotidan foydalana oladi."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun bu qurilmaning joylashuvi haqidagi axborotdan foydalanishiga ruxsat berilsinmi?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Bu ilovadan foydalanilmaganda ham u doim joylashuv axborotidan foydalana oladi."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Taqvim"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"taqvimingizga kirish"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun taqvimingizga ruxsat berilsinmi?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Bu ilova telefonga taqvim tadbirlarini qo‘shishi, olib tashlashi yoki o‘zgartirishi mumkin. Bu ilova taqvim egalari nomidan ko‘rinadigan SMS yuborishi yoki egalarini ogohlantirmasdan tadbirlarni o‘zgartirishi mumkin."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"qo‘shimcha manzillarga kirish buyruqlari"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ilovaga qo‘shimcha joylashuv xizmati buyruqlaridan foydalanishga ruxsat beradi. Uning yordamida ilova GPS yoki boshqa joylashuv ma’lumoti manbalarining ishlashiga xalaqit qilishi mumkin."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"aniq joylashuv ma’lumotiga kirish (GPS va tarmoq asosida)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Bu ilova Wi-Fi va uyali tarmoq antennalari kabi tarmoq manbalari yoki GPS asosida joylashuvingiz axborotini olishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular telefonda yoniq bo‘lishi va ishlashi kerak."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"aniq joylashuv axborotini olishga faqat old fonda ruxsat"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Bu ilova faqat fon rejimida aniq joylashuv axborotingizdan foydalanishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular telefoningizda yoniq turishi va ishlashi kerak. Bunda batareya sarfi oshishi mumkin."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"taxminiy joylashuv (tarmoq asosida) ma’lumotlaridan foydalanishga ruxsat"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Bu ilova Wi-Fi va uyali tarmoq antennalari kabi tarmoq manbalari asosida joylashuvingiz axborotini olishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular planshetda yoniq bo‘lishi va ishlashi kerak."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Bu ilova Wi-Fi va uyali tarmoq antennalari kabi tarmoq manbalari asosida joylashuvingiz axborotini olishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular televizorda yoniq bo‘lishi va ishlashi kerak."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Bu ilova Wi-Fi va uyali tarmoq antennalari kabi tarmoq manbalari asosida joylashuvingiz axborotini olishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular telefoningizda yoniq bo‘lishi va ishlashi kerak."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"fonda joylashuv axborotini olishga ruxsat"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Bu ilova fon rejimida aniq joylashuv axborotingizdan foydalanishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular telefoningizda yoniq turishi va ishlashi kerak. Bunda batareya sarfi oshishi mumkin."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"audio sozlamalaringizni o‘zgartirish"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ilovalarga tovush va ovoz chiqarish uchun foydalaniladigan karnay kabi global audio sozlamalarini o‘zgartirish uchun ruxsat beradi."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ovoz yozib olish"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Barmoq izi belgisi"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"yuzni aniqlash qurilmasini boshqarish"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Ilova foydalanish uchun yuz namunalarini qo‘shish va o‘chirish usullarini tatbiq qilishi mumkin."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"yuzni aniqlash qurilmasidan foydalanish"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Haqiqiylikni tekshirish uchun skanerdan foydalanish imkonini beradi"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Yuz aniqlanmadi. Qaytadan urining."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Yuz juda yorqin. Yorug‘likni kamaytiring."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Yuz juda xira. Yorug‘likni oshiring."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Qurilmani yuzingizdan uzoqroq tuting."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Qurilmani yuzga yaqin tuting."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Qurilmani teparoq ko‘taring."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Qurilmani pastroq tushiring."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Qurilmani o‘ngroq suring."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Qurilmani chaproq suring."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Qurilma kamerasiga qarang."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Yuz aniqlanmadi."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Yuzingizni qurilmaga qarating."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Yuzni aniqlash uchun qurilma mavjud emas."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Yuzni aniqlash vaqti tugadi. Qaytadan urining."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Aniqlangan yuz saqlanmadi."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Yuzni aniqlash bekor qilindi."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Juda ko‘p urinildi. Keyinroq qaytadan urining."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Juda ko‘p urinildi. Skaner faolsizlantirildi."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Qaytadan urining."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Hech qanday yuz qayd qilinmagan."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Bu qurilmada yuzni aniqlash uchun skaner mavjud emas"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Yuz <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Yuz belgisi"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"sinx-sh sozlamalarini o‘qish"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ilovaga hisobning sinxronlash sozlamalarini o‘qish uchun ruxsat beradi. Masalan, bu \"Odamlar\" ilovasi hisob bilan sinxronlangan yoki aksini aniqlay oladi."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"sinx.ni yoqish/o‘chirish"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi tarmoqda internet aloqasi yo‘q"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Variantlarni ko‘rsatish uchun bosing"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Hotspot sozlamalari o‘zgartirildi"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Hotspot chastotasi o‘zgartirildi."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Qurilma faqat 5 GGs chastotada ishlay olmaydi. Bu chastotadan imkoniyatga qarab foydalaniladi."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> tarmog‘iga ulanildi"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Agar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tarmoqda internet uzilsa, qurilma <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ga ulanadi. Sarflangan trafik uchun haq olinishi mumkin."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> tarmog‘idan <xliff:g id="NEW_NETWORK">%2$s</xliff:g> tarmog‘iga o‘tildi"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index c833da8..7b603fb 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Chế độ trên máy bay"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Chế độ trên máy bay BẬT"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Chế độ trên máy bay TẮT"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Trình tiết kiệm pin"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Trình tiết kiệm pin đang TẮT"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Trình tiết kiệm pin đang BẬT"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Cài đặt"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Hỗ trợ"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Trợ lý thoại"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Vị trí"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"truy cập vị trí của thiết bị này"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào vị trí của thiết bị này?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Ứng dụng sẽ chỉ có quyền truy cập vào vị trí khi bạn sử dụng."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Luôn cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vị trí thiết bị?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Ứng dụng sẽ luôn có quyền truy cập vào vị trí, ngay cả khi bạn không sử dụng."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Lịch"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"truy cập lịch của bạn"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào lịch của bạn?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ứng dụng này có thể thêm, xóa hoặc thay đổi sự kiện lịch trên điện thoại của bạn. Ứng dụng này có thể gửi các tin nhắn trông có vẻ như được gửi từ các chủ sở hữu lịch hoặc thay đổi sự kiện mà không thông báo cho chủ sở hữu."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"truy cập vào các lệnh của nhà cung cấp vị trí bổ sung"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Cho phép ứng dụng truy cập vào các lệnh của nhà cung cấp vị trí bổ sung. Điều này có thể cho phép ứng dụng can thiệp vào hoạt động của Hệ thống định vị toàn cầu (GPS) hoặc các nguồn vị trí khác."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"truy cập vị trí chính xác (dựa vào mạng và GPS)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Ứng dụng này có thể nhận thông tin vị trí của bạn dựa trên GPS hoặc các nguồn vị trí mạng như tháp phát sóng di động và mạng Wi-Fi. Các dịch vụ vị trí này phải được bật và khả dụng trên điện thoại của bạn để ứng dụng có thể sử dụng chúng. Điều này có thể làm tăng mức tiêu thụ pin."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"chỉ truy cập vị trí chính xác trong nền trước"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Bất cứ khi nào chạy trong nền trước, ứng dụng này có thể nhận thông tin vị trí chính xác của bạn. Để ứng dụng có thể sử các dụng dịch vụ vị trí, điện thoại của bạn phải có các dịch vụ này và dịch vụ ở trạng thái bật. Hoạt động này có thể tăng mức tiêu thụ pin."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"truy cập vị trí gần đúng (dựa vào mạng)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ứng dụng này có thể nhận thông tin vị trí của bạn dựa trên các nguồn mạng như tháp phát sóng di động và mạng Wi-Fi. Các dịch vụ vị trí này phải được bật và khả dụng trên máy tính bảng của bạn để ứng dụng có thể sử dụng chúng."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Ứng dụng này có thể nhận thông tin vị trí của bạn dựa trên các nguồn mạng như tháp phát sóng di động và mạng Wi-Fi. Các dịch vụ vị trí này phải được bật và khả dụng trên TV của bạn để ứng dụng có thể sử dụng chúng."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Ứng dụng này có thể nhận thông tin vị trí của bạn dựa trên các nguồn mạng như tháp phát sóng di động và mạng Wi-Fi. Các dịch vụ vị trí này phải được bật và khả dụng trên điện thoại của bạn để ứng dụng có thể sử dụng chúng."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"truy cập vị trí chính xác trong nền"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Bất cứ khi nào chạy trong nền, ứng dụng này có thể nhận thông tin vị trí chính xác của bạn. Để ứng dụng có thể sử dụng các dịch vụ vị trí, điện thoại của bạn phải có các dịch vụ này và dịch vụ ở trạng thái bật. Hoạt động này có thể tăng mức tiêu thụ pin."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"thay đổi cài đặt âm thanh của bạn"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Cho phép ứng dụng sửa đổi cài đặt âm thanh chung chẳng hạn như âm lượng và loa nào được sử dụng cho thiết bị ra."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"ghi âm"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Biểu tượng vân tay"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"quản lý phần cứng xác thực khuôn mặt"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Cho phép ứng dụng gọi ra các phương pháp để thêm và xóa mẫu khuôn mặt sử dụng."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"sử dụng phần cứng xác thực khuôn mặt"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Cho phép ứng dụng sử dụng phần cứng xác thực khuôn mặt để tiến hành xác thực"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Không thể xử lý khuôn mặt. Vui lòng thử lại."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Khuôn mặt quá sáng. Hãy thử dưới ánh sáng yếu hơn."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Khuôn mặt quá tối. Hãy tìm nguồn sáng tốt hơn."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Hãy đưa cảm biến ra xa khuôn mặt hơn."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Hãy đưa cảm biến lại gần khuôn mặt hơn."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Hãy đưa cảm biến lên cao hơn."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Hãy đưa cảm biến xuống thấp hơn."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Hãy đưa cảm biến sang bên phải."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Hay đưa cảm biến sang bên trái."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Hãy nhìn vào cảm biến."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Không phát hiện được khuôn mặt nào."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Hãy giữ yên khuôn mặt trước thiết bị."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Không truy cập được phần cứng nhận dạng khuôn mặt."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Đã hết thời gian chờ khuôn mặt. Hãy thử lại."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Không thể lưu khuôn mặt."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Đã hủy thao tác dùng khuôn mặt."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Bạn đã thử quá nhiều lần. Hãy thử lại sau."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Đã thử quá nhiều lần. Đã tắt xác thực khuôn mặt."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Hãy thử lại."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Bạn chưa đăng ký khuôn mặt."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Thiết bị này không có cảm biến xác thực khuôn mặt"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Khuôn mặt <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Biểu tượng khuôn mặt"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"đọc cài đặt đồng bộ hóa"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Cho phép ứng dụng đọc cài đặt đồng bộ hóa cho tài khoản. Ví dụ: việc này có thể xác định liệu ứng dụng Mọi người đã được đồng bộ hóa với tài khoản chưa."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"chuyển đổi bật và tắt đồng bộ hóa"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi không có quyền truy cập Internet"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Nhấn để biết tùy chọn"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Những thay đổi trong mục cài đặt điểm phát sóng của bạn"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Bằng tần của điểm phát sóng đã thay đổi."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Thiết bị này không hỗ trợ tùy chọn chỉ sử dụng băng tần 5 GHz. Thay vào đó, thiết bị này sẽ sử dụng băng tần 5 GHz khi có thể."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Đã chuyển sang <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Thiết bị sử dụng <xliff:g id="NEW_NETWORK">%1$s</xliff:g> khi <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> không có quyền truy cập Internet. Bạn có thể phải trả phí."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Đã chuyển từ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> sang <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 880d140..8a55d01 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"飞行模式"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"已开启飞行模式"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"未开启飞行模式"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"省电模式"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"省电模式已关闭"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"省电模式已开启"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"设置"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"助手应用"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"语音助理"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"位置信息"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"获取此设备的位置信息"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;获取此设备的位置信息吗?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"只有当您使用该应用时,该应用才有权获取位置信息。"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"一律允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;获取此设备的位置信息吗?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"即使您并未使用该应用,该应用也将始终有权获取位置信息。"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"日历"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"访问您的日历"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;访问您的日历吗?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"此应用可在手机上添加、移除或更改日历活动。此应用可能会以日历所有者的身份发送消息,或在不通知所有者的情况下更改活动。"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"获取额外的位置信息提供程序命令"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"允许该应用使用其他的位置信息提供程序命令。此权限使该应用可以干扰GPS或其他位置信息源的运作。"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"访问确切位置信息(以 GPS 和网络为依据)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"此应用可根据 GPS 或网络来源(例如基站和 WLAN 网络)获取您的位置信息。您的手机必须支持并开启这些位置信息服务,此应用才能使用这些服务。这可能会增加耗电量。"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"只能在前台获取精确的位置信息"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"此应用只有在前台运行时才能获取您的精确位置信息。您的手机必须支持并开启这些位置信息服务,此应用才能使用这些服务。这可能会增加耗电量。"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"访问大致位置信息(以网络为依据)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"此应用可根据网络来源(例如基站和 WLAN 网络)获取您的位置信息。您的平板电脑必须支持并开启这些位置信息服务,此应用才能使用这些服务。"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"此应用可根据网络来源(例如基站和 WLAN 网络)获取您的位置信息。您的电视必须支持并开启这些位置信息服务,此应用才能使用这些服务。"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"此应用可根据网络来源(例如基站和 WLAN 网络)获取您的位置信息。您的手机必须支持并开启这些位置信息服务,此应用才能使用这些服务。"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"在后台获取精确的位置信息"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"此应用在后台运行时可随时获取您的精确位置信息。您的手机必须支持并开启这些位置信息服务,此应用才能使用这些服务。这可能会增加耗电量。"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"更改您的音频设置"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"允许该应用修改全局音频设置,例如音量和用于输出的扬声器。"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"录音"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指纹图标"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"管理人脸身份验证硬件"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"允许该应用调用方法来添加和删除可用的人脸模板。"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"使用人脸身份验证硬件"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"允许该应用使用人脸身份验证硬件进行身份验证"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"无法识别此面孔,请重试。"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"面部太亮,请在光线适中的环境下重试。"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"面部太暗,请不要挡住光线。"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"请将传感器移到距离面孔较远处。"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"请将传感器靠近面孔。"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"请上移传感器。"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"请下移传感器。"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"请右移传感器。"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"请左移传感器。"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"请目视传感器。"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"未检测到任何面孔。"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"请将面孔保持在设备前不动。"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"无法使用面孔识别硬件"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"面孔处理操作超时,请重试。"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"无法存储面孔。"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"面孔处理操作已取消。"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"尝试次数过多,请稍后重试。"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"尝试次数过多,系统已停用人脸身份验证功能。"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"请重试。"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"未注册任何面孔。"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"此设备没有人脸身份验证传感器"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"面孔 <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"面孔图标"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"读取同步设置"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允许该应用读取某个帐号的同步设置。例如,此权限可确定“联系人”应用是否与某个帐号同步。"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"启用和停用同步"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"此 WLAN 网络无法访问互联网"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"点按即可查看相关选项"</string>
+    <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">"此设备不支持您的偏好设置(仅限 5GHz),而且会在 5GHz 频段可用时使用该频段。"</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>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index de262f3..0b75202 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"飛行模式"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"飛航模式為 [開啟]"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"飛行模式為 [關閉]"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"省電模式"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"省電模式已關閉"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"省電模式已開啟"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"設定"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"小幫手"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"語音助手"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"位置資訊"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"存取此裝置的位置"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取此裝置的位置資訊嗎?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"只有在您使用此應用程式時,應用程式才能存取位置資訊。"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"要一律允許「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」存取此裝置的位置資訊嗎?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"即使您沒有使用此應用程式,應用程式亦一直能夠存取位置資訊。"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"日曆"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"存取您的日曆"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取您的日曆嗎?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"此應用程式可以加入、移除或變更您的手機中的日曆活動。此應用程式可以傳送看似來自日曆擁有者的訊息,或變更活動而不通知其擁有者。"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"接收額外的位置提供者指令"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"允許應用程式存取額外的位置提供者指令。這項設定可能會使應用程式干擾 GPS 或其他位置來源的運作。"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"存取精確位置 (根據 GPS 和網絡)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"此應用程式可以根據 GPS 或網絡位置來源 (例如手機訊號塔和 Wi-Fi 網絡) 取得您的位置。您的手機必須支援並啟用這些位置資訊服務,應用程式方可使用這項功能。這可能會增加耗電量。"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"只在前景存取精確位置"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"此應用程式只可在前台運行時獲取您的確實位置資訊。您的手機必須支援並啟用這些位置資訊服務,應用程式方可使用這項功能,但這樣做可能會增加耗電量。"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"存取約略位置 (根據網絡)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"此應用程式可以根據您的網絡來源 (例如手機訊號塔和 Wi-Fi 網絡) 取得您的位置。您的平板電腦必須支援並啟用這些位置資訊服務,應用程式方可使用這項功能。"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"此應用程式可以根據您的網絡來源 (例如手機訊號塔和 Wi-Fi 網絡) 取得您的位置。您的電視必須支援並啟用這些位置資訊服務,應用程式方可使用這項功能。"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"此應用程式可以根據您的網絡來源 (例如手機訊號塔和 Wi-Fi 網絡) 取得您的位置。您的手機必須支援並啟用這些位置資訊服務,應用程式方可使用這項功能。"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"在背景中存取精確位置"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"此應用程式可在背景運行時隨時獲取您的確實位置資訊。您的手機必須支援並啟用這些位置資訊服務,應用程式方可使用這項功能,但這樣做可能會增加耗電量。"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"更改音效設定"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"允許應用程式修改全域音頻設定,例如音量和用於輸出的喇叭。"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"錄製音效"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指紋圖示"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"管理臉孔驗證硬件"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"允許應用程式調用方法,以加入和刪除可用的臉孔範本。"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"使用臉孔驗證硬件"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"允許應用程式使用臉孔驗證硬件來驗證"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"無法識別臉孔,請再試一次。"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"臉孔太光亮,請在較暗的光線下嘗試。"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"臉孔太暗,請尋找更佳光源。"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"請將感應器移離臉孔。"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"請將感應器靠近臉孔。"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"請將感應器向上移。"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"請將感應器向下移。"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"請將感應器向右移。"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"請將感應器向左移。"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"請看著感應器。"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"未偵測到任何臉孔。"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"請在裝置前保持臉孔不動。"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"無法使用臉孔識別硬件。"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"臉孔操作已逾時,請再試一次。"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"無法儲存臉孔。"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"臉孔操作已取消。"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"嘗試次數過多,請稍後再試。"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"嘗試次數過多,臉孔驗證已停用。"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"請再試一次。"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"未註冊任何臉孔。"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"此裝置沒有臉孔驗證感應器"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"臉孔 <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"臉孔圖示"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"讀取同步處理設定"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允許應用程式讀取帳戶的同步設定,例如確定「通訊錄」應用程式是否和某個帳戶保持同步。"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"開啟和關閉同步功能"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi 並未連接互聯網"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"輕按即可查看選項"</string>
+    <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">"此裝置不支援只限 5 GHz 的偏好設定,但會在 5 GHz 頻段可用時採用。"</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>
@@ -1736,7 +1772,7 @@
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"直至您關閉「請勿騷擾」功能"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"收合"</string>
-    <string name="zen_mode_feature_name" msgid="5254089399895895004">"請勿干擾"</string>
+    <string name="zen_mode_feature_name" msgid="5254089399895895004">"請勿騷擾"</string>
     <string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"休息時間"</string>
     <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"平日夜間"</string>
     <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"週末"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 2548a14..d130fba 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"飛航模式"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"飛航模式為 [開啟]"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"飛航模式為 [關閉]"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"節約耗電量"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"節約耗電量模式已關閉"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"節約耗電量模式已開啟"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"設定"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"協助"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"語音小幫手"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"位置"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"存取這台裝置的位置資訊"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取這個裝置的位置資訊嗎?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"該應用程式只有在你使用時,才能存取位置資訊。"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"要一律允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取這個裝置的位置資訊嗎?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"該應用程式即使在你未使用時,也隨時可以存取位置資訊。"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"日曆"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"存取你的日曆"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取你的日曆嗎?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"這個應用程式可在手機上新增、移除或變更日曆活動。提醒你,這個應用程式可能會以日曆擁有者的名義傳送訊息,或是在不通知日曆擁有者的情況下變更活動內容。"</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"接收額外的位置提供者指令"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"允許應用程式存取額外位置資訊提供者指令。這項設定可能會造成應用程式干擾 GPS 或其他位置資訊來源的運作。"</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"存取精確位置 (以 GPS 和網路為依據)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"這個應用程式可根據 GPS 或網路位置來源 (例如基地台和 Wi-Fi 網路) 取得你的位置資訊。你必須在手機上開啟這類定位服務,才能讓這個應用程式取得位置資訊。不過這可能會增加耗電量。"</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"僅可在前景中取得精確位置"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"這個應用程式只能在前景中取得你的確切位置。你必須在手機上開啟這些定位服務,才能讓這個應用程式取得確切位置。請注意,這麼做可能會增加耗電量。"</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"存取概略位置 (以網路為依據)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"這個應用程式可根據網路來源 (例如基地台和 Wi-Fi 網路) 取得你的位置資訊。你必須在平板電腦上開啟這類定位服務,才能讓這個應用程式取得位置資訊。"</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"這個應用程式可根據網路來源 (例如基地台和 Wi-Fi 網路) 取得你的位置資訊。你必須在電視上開啟這類定位服務,才能讓這個應用程式取得位置資訊。"</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"這個應用程式可根據網路來源 (例如基地台和 Wi-Fi 網路) 取得你的位置資訊。你必須在手機上開啟這類定位服務,才能讓這個應用程式取得位置資訊。"</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"在背景中取得精確位置"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"這個應用程式可在背景中隨時取得你的確切位置。你必須在手機上開啟這些定位服務,才能讓這個應用程式取得確切位置。請注意,這麼做可能會增加耗電量。"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"變更音訊設定"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"允許應用程式修改全域音訊設定,例如音量和用來輸出的喇叭。"</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"錄製音訊"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指紋圖示"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"管理臉孔驗證硬體"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"允許應用程式呼叫方法來新增及移除可用的臉孔範本。"</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"使用臉孔驗證硬體"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"允許應用程式使用臉孔驗證硬體進行驗證"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"無法識別臉孔,請再試一次。"</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"臉孔過亮。請移至光線適中的場所,然後再試一次。"</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"臉孔過暗。請增添光源,然後再試一次。"</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"請將感應器移到距離臉孔較遠處。"</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"請將感應器靠近臉孔。"</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"請將感應器往上移動。"</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"請將感應器往下移動。"</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"請將感應器往右移動。"</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"請將感應器往左移動。"</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"請直視感應器。"</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"未偵測到任何臉孔。"</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"請將臉孔保持在裝置前方。"</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"無法使用臉部辨識硬體。"</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"臉孔處理作業逾時,請再試一次。"</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"無法儲存臉孔。"</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"臉孔處理作業已取消。"</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"嘗試次數過多,請稍後再試。"</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"嘗試次數過多,臉孔驗證功能已停用。"</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"請再試一次。"</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"未登錄任何臉孔。"</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"這個裝置沒有臉孔驗證感應器"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"臉孔 <xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"臉孔圖示"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"讀取同步處理設定"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允許應用程式讀取帳戶的同步處理設定,例如判斷「使用者」應用程式是否和某個帳戶進行同步處理。"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"開啟及關閉同步功能"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi 沒有網際網路連線"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"輕觸即可查看選項"</string>
+    <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">"這個裝置不支援你的偏好設定 (僅限 5GHz),而是會在 5GHz 可用時使用該頻帶。"</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>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index d252989..fed4824 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -236,9 +236,6 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Imodi yendiza"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Imodi yendiza IVULIWE"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Imodi yendiza IVALIWE"</string>
-    <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Isilondolozi sebhethri"</string>
-    <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Iseva yebhethri SIVALIWE"</string>
-    <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Isilondolozi sebhethri SIVULIWE"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Izilungiselelo"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Siza"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Isisekeli sezwi"</string>
@@ -276,6 +273,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"Indawo"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"finyelela kundawo yale divayisi"</string>
     <string name="permgrouprequest_location" msgid="3788275734953323491">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ingene kundawo yale divayisi?"</string>
+    <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Uhlelo lokusebenza luzoba nokufinyelela kundawo kuphela uma usebenzisa uhlelo lokusebenza."</string>
+    <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Hlala uvumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele indawo yale divayisi?"</string>
+    <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Uhlelo lokusebenza luzohlala lunokufinyelela kundawo, nanoma ungasebenzisi uhlelo lokusebenza."</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Ikhalenda"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"finyelela kukhalenda yakho"</string>
     <string name="permgrouprequest_calendar" msgid="289900767793189421">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele kukhalenda yakho?"</string>
@@ -402,12 +402,14 @@
     <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Lolu hlelo lokusebenza lungangeza, lisuse, noma lishintshe imicimbi yekhalenda efonini yakho. Lolu hlelo lokusebenza lingathumela imilayezo engabonakala ivela kusuka kubanikazi bekhalenda, noma lishintshe imicimbi ngaphandle kokwazisa abanikazi."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"finyelela kweminye imiyalo yokunikeza indawo"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ivumela uhlelo lokusebenza ukufinyelela imiyalo eyengeziwe yabahlinzeki bendawo. Lokhu kungase kuvumele uhlelo lokusebenza ukuthi liphazamisane nomsebenzi we-GPS noma eminye imithombo yendawo."</string>
-    <string name="permlab_accessFineLocation" msgid="251034415460950944">"finyelela indawo enembile (i-GPS nesuselwa kunethiwekhi)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5821994817969957884">"Lolu hlelo lokusebenza lungathola indawo yakho ngokususelwe ku-GPS noma imithombo yenethiwekhi njengamathawa eseli namanethiwekhi e-Wi-Fi. Lawa masevisi endawo kufanele avulwe futhi atholakale efonini yakho ukuze uhlelo lokusebenza lukwazi ukuwasebenzisa. Lokhu kungakhulisa ukusetshenziswa kwebhethri."</string>
+    <string name="permlab_accessFineLocation" msgid="6265109654698562427">"finyelela indawo eqondile kuphela phambili"</string>
+    <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Lolu hlelo lokusebenza lungakutholela indawo eqondile kuphela uma liphambili. Lawa masevisi endawo kufanele avulwe futhi atholakale efonini yakho ukuze uhlelo lokusebenza lukwazi ukuwasebenzisa. Lokhu kungakhulisa ukusebenza kwebhethri."</string>
     <string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"finyelela kundawo elinganiselwe (esuselwa kunethiwekhi)"</string>
     <string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Lolu hlelo lokusebenza lungathola indawo yakho ngokususelwe kumithombo yenethiwekhi njengamathawa eseli namanethiwekhi e-Wi-Fi. Lawa masevisi endawo kufanele avulwe futhi atholakale kuthebhulethi yakho ukuze uhlelo lokusebenza lukwazi ukuwasebenzisa."</string>
     <string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Lolu hlelo lokusebenza lingathola indawo yakho ngokususelwe kumithombo yenethiwekhi njengamathawa eseli namanethiwekhi e-Wi-Fi. Lawa masevisi endawo kufanele avulwe futhi atholakale ku-TV yakho ukuze uhlelo lokusebenza lukwazi ukuwasebenzisa."</string>
     <string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Lolu hlelo lokusebenza lungathola indawo yakho ngokususelwe kumithombo yenethiwekhi njengamathawa eseli kanye namanethiwekhi e-Wi-Fi. Lawa masevisi endawo kufanele avulwe futhi atholakale efonini yakho ukuze uhlelo lokusebenza likwazi ukuwasebenzisa."</string>
+    <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"finyelela indawo eqondile emuva"</string>
+    <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Lolu hlelo lokusebenza lungakutholela indawo eqondile noma kunini lusemuva. Lawa masevisi endawo kufanele avulwe futhi atholakale efonini yakho ukuze uhlelo lokusebenza lukwazi ukuwasebenzisa. Lokhu kungakhulisa ukusebenza kwebhethri."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"shintsha izilungiselelo zakho zomsindo"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ivumela uhlelo lokusebenza ukushintsha izilungiselelo zomsindo we-global njengevolomu nokuthi isiphi isipika esisetshenziselwa okukhiphayo."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"qopha umsindo"</string>
@@ -515,6 +517,37 @@
   <string-array name="fingerprint_error_vendor">
   </string-array>
     <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Isithonjana sezigxivizo zeminwe"</string>
+    <string name="permlab_manageFace" msgid="2137540986007309781">"phatha izingxenyekazi zekhompuyutha zokufakazela ubuqiniso zobuso"</string>
+    <string name="permdesc_manageFace" msgid="8919637120670185330">"Ivumela uhlelo lokusebenza ukuthi luhoxise izindlela zokungeza nokususa amathempulethi obuso azosetshenziswa."</string>
+    <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"sebenzisa izingxenyekazi zekhompuyutha zokufakazela ubuqiniso kobuso"</string>
+    <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Ivumela uhlelo lokusebenza ukuthi lusebenzise ukufakazela ubuqiniso bobuso bezingxenyekazi ukuze kufakazelwe ubuqiniso"</string>
+    <string name="face_acquired_insufficient" msgid="5901287247766106330">"Ayikwazanga ukucubungula ubuso. Sicela uzame futhi."</string>
+    <string name="face_acquired_too_bright" msgid="610606792381297174">"Ubuso bukhanya kakhulu. Sicela uzame ekukhanyeni okuncane."</string>
+    <string name="face_acquired_too_dark" msgid="7229162716976778371">"Ubuso bumnyama kakhulu. Sicela uvule umthombo wokukhanya."</string>
+    <string name="face_acquired_too_close" msgid="1980310037427755293">"Sicela uhambise inzwa kude kunobuso."</string>
+    <string name="face_acquired_too_far" msgid="4494571381828850007">"Sicela ulethe inzwa eduze kobuso."</string>
+    <string name="face_acquired_too_high" msgid="228411096134808372">"Sicela uhambise inzwa phezulu."</string>
+    <string name="face_acquired_too_low" msgid="4539774649296349109">"Sicela uhambise inzwa ngezansi."</string>
+    <string name="face_acquired_too_right" msgid="1650292067226118760">"Sicela uhambise inzwa ngakwesokudla."</string>
+    <string name="face_acquired_too_left" msgid="2712489669456176505">"Sicela uhambise inzwa ngakwesokunxele."</string>
+    <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Sicela ubheke kunzwa."</string>
+    <string name="face_acquired_not_detected" msgid="5707782294589511391">"Abukho ubuso obutholiwe."</string>
+    <string name="face_acquired_not_steady" msgid="3722829465011040042">"Gcina ubuso bumile ngaphambi kwedivayisi."</string>
+  <string-array name="face_acquired_vendor">
+  </string-array>
+    <string name="face_error_hw_not_available" msgid="6255891785768984615">"Izingxenyekazi zekhompuyutha zobuso azitholakali."</string>
+    <string name="face_error_timeout" msgid="4014326147867150054">"Kufinyelelwe kusikhathi sokuvala sobuso. Zama futhi."</string>
+    <string name="face_error_no_space" msgid="8224993703466381314">"Ubuso abukwazi ukugcinwa."</string>
+    <string name="face_error_canceled" msgid="283945501061931023">"Umsebenzi wobuso ukhanselwe."</string>
+    <string name="face_error_lockout" msgid="3407426963155388504">"Imizamo eminingi kakhulu. Zama futhi emuva kwesikhathi."</string>
+    <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Imizamo eminingi kakhulu. Ukufakazela ubuqiniso kobuso kukhutshaziwe."</string>
+    <string name="face_error_unable_to_process" msgid="238761109287767270">"Zama futhi."</string>
+    <string name="face_error_not_enrolled" msgid="9166792142679691323">"Abukho ubuso obubhalisiwe."</string>
+    <string name="face_error_hw_not_present" msgid="4737289254517095671">"Le divayisi ayinayo inzwa yokufakazela ubuqiniso yobuso"</string>
+    <string name="face_name_template" msgid="7004562145809595384">"Ubuso be-<xliff:g id="FACEID">%d</xliff:g>"</string>
+  <string-array name="face_error_vendor">
+  </string-array>
+    <string name="face_icon_content_description" msgid="4024817159806482191">"Isithonjana sobuso"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"funda izilungiselelo zokuvumelanisa"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ivumela uhlelo lokusebenza ukufunda izilungiselelo zokuvumelanisa ze-akhawunti. Isibonelo, lokhu kungacacisa ukuthi noma ngabe uhlelo lokusebenza le-People livumelanisiwe ne-akhawunti."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"shintsha phakathi kokuvula kanye nokucisha ukuvumelanisa"</string>
@@ -1175,6 +1208,9 @@
     <skip />
     <string name="wifi_no_internet" msgid="8938267198124654938">"I-Wi-Fi ayinakho ukufinyelela kwe-inthanethi"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Thepha ukuze uthole izinketho"</string>
+    <string name="wifi_softap_config_change" msgid="8475911871165857607">"Ushintsho kuzilungiselelo zakho ze-hotspot"</string>
+    <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Ibhendi yakho ye-hotspot ishintshile."</string>
+    <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Le divayisi ayisekeli okuncamelayo kwe-5GHz kuphela. Kunalokho, le divayisi izosebenzisa ibhendi ye-5GHz uma itholakala."</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"Kushintshelwe ku-<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="775163331794506615">"Idivayisi isebenzisa i-<xliff:g id="NEW_NETWORK">%1$s</xliff:g> uma i-<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> inganakho ukufinyelela kwe-inthanethi. Kungasebenza izindleko."</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"Kushintshelewe kusuka ku-<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kuya ku-<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d2684ce..2f710bf 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1477,7 +1477,7 @@
              used for, typically, account ID or password input.  It is expected that IMEs
              normally are able to input ASCII even without being told so (such IMEs
              already respect this flag in a sense), but there could be some cases they
-             aren't when, for instance, only non-ASCII input languagaes like Arabic,
+             aren't when, for instance, only non-ASCII input languages like Arabic,
              Greek, Hebrew, Russian are enabled in the IME.  Applications need to be
              aware that the flag is not a guarantee, and not all IMEs will respect it.
              However, it is strongly recommended for IME authors to respect this flag
@@ -3113,6 +3113,9 @@
              the alpha channel of the outlineAmbientShadowColor (typically opaque), and the
              {@link android.R.attr#ambientShadowAlpha} theme attribute. -->
         <attr name="outlineAmbientShadowColor" format="color" />
+
+        <!-- Whether to allow the rendering system to force this View to render as light-on-dark. -->
+        <attr name="allowForceDark" format="boolean" />
     </declare-styleable>
 
     <!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -3161,7 +3164,7 @@
              enabled by a ViewGroup for all its children in specific situations (for
              instance during a scrolling.) This property lets you persist the cache
              in memory after its initial usage. Persisting the cache consumes more
-             memory but may prevent frequent garbage collection is the cache is created
+             memory but may prevent frequent garbage collection if the cache is created
              over and over again. By default the persistence is set to scrolling.
              Deprecated: The view drawing cache was largely made obsolete with the introduction of
              hardware-accelerated rendering in API 11. -->
@@ -7863,8 +7866,7 @@
              wallpaper. -->
         <attr name="showMetadataInPreview" format="boolean" />
 
-        <!-- Wallpapers optimized and capable of drawing in ambient mode will return true.
-            @hide -->
+        <!-- Wallpapers optimized and capable of drawing in ambient mode will return true. -->
         <attr name="supportsAmbientMode" format="boolean" />
 
     </declare-styleable>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c4fa190..74663c9 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1358,7 +1358,10 @@
     <!-- Specifies the target sandbox this app wants to use. Higher sandbox versions
          will have increasing levels of security.
 
-         <p>The default value of this attribute is <code>1</code>. -->
+         <p>The default value of this attribute is <code>1</code>.
+         <p>
+         @deprecated The security properties have been moved to
+         {@link android.os.Build.VERSION Build.VERSION} 27 and 28. -->
     <attr name="targetSandboxVersion" format="integer" />
 
     <!-- The user-visible SDK version (ex. 26) of the framework against which the application was
@@ -1580,6 +1583,7 @@
         <attr name="banner" />
         <attr name="logo" />
         <attr name="permissionGroup" />
+        <attr name="backgroundPermission" />
         <attr name="description" />
         <attr name="request" />
         <attr name="protectionLevel" />
@@ -1610,6 +1614,9 @@
         <attr name="logo" />
         <attr name="description" />
         <attr name="request" />
+        <attr name="requestDetail" />
+        <attr name="backgroundRequest" />
+        <attr name="backgroundRequestDetail" />
         <attr name="permissionGroupFlags" />
         <attr name="priority" />
     </declare-styleable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0b5dd7e..fddb904 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -42,13 +42,13 @@
         <item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item>
@@ -491,13 +491,21 @@
 
          Note also: the order of this is important. The first upstream type
          for which a satisfying network exists is used.
-      -->
+    -->
     <integer-array translatable="false" name="config_tether_upstream_types">
         <item>1</item>
         <item>7</item>
         <item>0</item>
     </integer-array>
 
+    <!-- When true, the tethering upstream network follows the current default
+         Internet network (except when the current default network is mobile,
+         in which case a DUN network will be used if required).
+
+         When true, overrides the config_tether_upstream_types setting above.
+    -->
+    <bool translatable="false" name="config_tether_upstream_automatic">true</bool>
+
     <!-- If the DUN connection for this CDMA device supports more than just DUN -->
     <!-- traffic you should list them here. -->
     <!-- If this device is not CDMA this is ignored.  If this list is empty on -->
@@ -543,9 +551,6 @@
          radio is unable to find any MCC information to infer wifi country code from -->
     <bool translatable="false" name="config_wifi_revert_country_code_on_cellular_loss">false</bool>
 
-    <!-- Boolean indicating whether or not wifi firmware debugging is enabled -->
-    <bool translatable="false" name="config_wifi_enable_wifi_firmware_debugging">true</bool>
-
     <!-- Integer size limit, in KB, for a single WifiLogger ringbuffer, in default logging mode -->
     <integer translatable="false" name="config_wifi_logger_ring_buffer_default_size_limit_kb">32</integer>
 
@@ -575,18 +580,16 @@
     <integer translatable="false" name="config_wifi_framework_min_rx_rate_for_staying_on_network">16</integer>
     <!-- Integer parameters of the wifi to cellular handover feature
          wifi should not stick to bad networks -->
-    <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz">-82</integer>
-    <integer translatable="false" name="config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz">-82</integer>
+    <!-- Integer threshold for low network score, should be somewhat less than the entry threshhold -->
+    <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz">-80</integer>
+    <!-- Integer threshold, do not connect to APs with RSSI lower than the entry threshold -->
+    <integer translatable="false" name="config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz">-77</integer>
     <integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_5GHz">-70</integer>
     <integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_5GHz">-57</integer>
-    <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz">-85</integer>
-    <integer translatable="false" name="config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz">-85</integer>
+    <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz">-83</integer>
+    <integer translatable="false" name="config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz">-80</integer>
     <integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_24GHz">-73</integer>
     <integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_24GHz">-60</integer>
-    <integer translatable="false" name="config_wifi_framework_wifi_score_bad_link_speed_24">6</integer>
-    <integer translatable="false" name="config_wifi_framework_wifi_score_bad_link_speed_5">12</integer>
-    <integer translatable="false" name="config_wifi_framework_wifi_score_good_link_speed_24">24</integer>
-    <integer translatable="false" name="config_wifi_framework_wifi_score_good_link_speed_5">36</integer>
 
     <!-- Integer delay in milliseconds before shutting down soft AP when there
          are no connected devices. Framework will enforce a minimum limit on
@@ -659,9 +662,31 @@
     <!-- Boolean indicating that wifi only link configuratios that have exact same credentials (i.e PSK) -->
     <bool translatable="false" name="config_wifi_only_link_same_credential_configurations">true</bool>
 
-    <!-- Boolean indicating whether framework needs to set the tx power limit for meeting SAR requirements
-         during voice calls -->
-    <bool translatable="false" name="config_wifi_framework_enable_voice_call_sar_tx_power_limit">false</bool>
+    <!-- Boolean indicating whether framework needs to set the tx power limit for meeting SAR requirements -->
+    <bool translatable="false" name="config_wifi_framework_enable_sar_tx_power_limit">false</bool>
+
+    <!-- Boolean indicating whether framework should use detection of softAP mode to set the tx
+         power limit for meeting SAR requirements -->
+    <bool translatable="false" name="config_wifi_framework_enable_soft_ap_sar_tx_power_limit">false</bool>
+
+    <!-- Boolean indicating whether framework needs to use body proximity to set the tx power limit
+         for meeting SAR requirements -->
+    <bool translatable="false" name="config_wifi_framework_enable_body_proximity_sar_tx_power_limit">false</bool>
+
+    <!-- String for the sensor type for body/head proximity for SAR -->
+    <string translatable="false" name="config_wifi_sar_sensor_type"></string>
+
+    <!-- Integer indicating event id by sar sensor for free space -->
+    <integer translatable="false" name="config_wifi_framework_sar_free_space_event_id">1</integer>
+
+    <!-- Integer indicating event id by sar sensor for near hand -->
+    <integer translatable="false" name="config_wifi_framework_sar_near_hand_event_id">2</integer>
+
+    <!-- Integer indicating event id by sar sensor for near head -->
+    <integer translatable="false" name="config_wifi_framework_sar_near_head_event_id">3</integer>
+
+    <!-- Integer indicating event id by sar sensor for near body -->
+    <integer translatable="false" name="config_wifi_framework_sar_near_body_event_id">4</integer>
 
     <!-- Wifi driver supports batched scan -->
     <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool>
@@ -2085,6 +2110,10 @@
          states. -->
     <bool name="config_dozeAlwaysOnDisplayAvailable">false</bool>
 
+    <!-- Control whether the always on display mode is enabled by default. This value will be used
+         during initialization when the setting is still null. -->
+    <bool name="config_dozeAlwaysOnEnabled">true</bool>
+
     <!-- Whether the display blanks itself when transitioning from a doze to a non-doze state -->
     <bool name="config_displayBlanksAfterDoze">false</bool>
 
@@ -2572,6 +2601,7 @@
          "silent" = silent mode
          "users" = list of users
          "restart" = restart device
+         "emergency" = Launch emergency dialer
          "lockdown" = Lock down device until the user authenticates
          "logout" =  Logout the current user
          -->
@@ -2582,6 +2612,7 @@
         <item>logout</item>
         <item>bugreport</item>
         <item>screenshot</item>
+        <item>emergency</item>
     </string-array>
 
     <!-- Number of milliseconds to hold a wake lock to ensure that drawing is fully
@@ -2698,6 +2729,9 @@
          empty string is passed in -->
     <string name="config_ims_package"/>
 
+    <!-- String array containing numbers that shouldn't be logged. Country-specific. -->
+    <string-array name="unloggable_phone_numbers" />
+
     <!-- Flag specifying whether or not IMS will use the dynamic ImsResolver -->
     <bool name="config_dynamic_bind_ims">false</bool>
 
@@ -2881,6 +2915,9 @@
     <!-- Keyguard component -->
     <string name="config_keyguardComponent" translatable="false">com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>
 
+    <!-- Limit for the number of face templates per user -->
+    <integer name="config_faceMaxTemplatesPerUser">1</integer>
+
     <!-- For performance and storage reasons, limit the number of fingerprints per user -->
     <integer name="config_fingerprintMaxTemplatesPerUser">5</integer>
 
@@ -2931,6 +2968,14 @@
          -->
     <string translatable="false" name="config_mainBuiltInDisplayCutout"></string>
 
+    <!-- Like config_mainBuiltInDisplayCutout, but this path is used to report the
+         one single bounding rect per device edge to the app via
+         {@link DisplayCutout#getBoundingRect}. Note that this path should try to match the visual
+         appearance of the cutout as much as possible, and may be smaller than
+         config_mainBuiltInDisplayCutout
+         -->
+    <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@string/config_mainBuiltInDisplayCutout</string>
+
     <!-- Whether the display cutout region of the main built-in display should be forced to
          black in software (to avoid aliasing or emulate a cutout that is not physically existent).
          -->
@@ -3137,6 +3182,9 @@
     <!-- True if camera app should be pinned via Pinner Service -->
     <bool name="config_pinnerCameraApp">false</bool>
 
+    <!-- True if home app should be pinned via Pinner Service -->
+    <bool name="config_pinnerHomeApp">false</bool>
+
     <!-- Number of days preloaded file cache should be preserved on a device before it can be
          deleted -->
     <integer name="config_keepPreloadsMinDays">7</integer>
@@ -3172,12 +3220,11 @@
     <string-array translatable="false" name="config_defaultFirstUserRestrictions">
     </string-array>
 
-    <!-- Specifies whether the permissions needed by a legacy app should be
-         reviewed before any of its components can run. A legacy app is one
-         with targetSdkVersion < 23, i.e apps using the old permission model.
-         If review is not required, permissions are reviewed before the app
-         is installed. -->
-    <bool name="config_permissionReviewRequired">false</bool>
+    <!-- Specifies whether certain permissions should be individually controlled. -->
+    <bool name="config_permissionsIndividuallyControlled">false</bool>
+
+    <!-- Specifies whether the user has to give consent to manage wireless (wifi + bluetooth). -->
+    <bool name="config_wirelessConsentRequired">false</bool>
 
     <!-- Default value for android:focusableInTouchMode for some framework scrolling containers.
          ListView/GridView are notably absent since this is their default anyway.
@@ -3339,6 +3386,9 @@
     <!-- URI for in call notification sound -->
     <string translatable="false" name="config_inCallNotificationSound">/system/media/audio/ui/InCallNotification.ogg</string>
 
+    <!-- Default number of notifications from the same app before they are automatically grouped by the OS -->
+    <integer translatable="false" name="config_autoGroupAtCount">4</integer>
+
     <!-- The OEM specified sensor type for the lift trigger to launch the camera app. -->
     <integer name="config_cameraLiftTriggerSensorType">-1</integer>
     <!-- The OEM specified sensor string type for the gesture to launch camera app, this value
@@ -3366,7 +3416,7 @@
     <string-array translatable="false" name="config_batteryPackageTypeService"/>
 
     <!-- Flag indicating whether or not to enable night mode detection. -->
-    <bool name="config_enableNightMode">false</bool>
+    <bool name="config_enableNightMode">true</bool>
 
     <!-- Flag indicating that the actions buttons for a notification should be tinted with by the
          color supplied by the Notification.Builder if present. -->
@@ -3447,4 +3497,13 @@
     <!-- Whether or not swipe up gesture's opt-in setting is available on this device -->
     <bool name="config_swipe_up_gesture_setting_available">false</bool>
 
+    <!-- Applications which are disabled unless matching a particular sku -->
+    <string-array name="config_disableApksUnlessMatchedSku_apk_list" translatable="false" />
+    <string-array name="config_disableApkUnlessMatchedSku_skus_list" translatable="false" />
+
+    <!-- Whether or not we should show the option to show battery percentage -->
+    <bool name="config_battery_percentage_setting_available">true</bool>
+
+    <!-- Whether or not battery saver should be "sticky" when manually enabled. -->
+    <bool name="config_batterySaverStickyBehaviourDisabled">false</bool>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6189971..e25bb79 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2907,6 +2907,8 @@
         <public name="opticalInsetTop" />
         <public name="opticalInsetRight" />
         <public name="opticalInsetBottom" />
+        <public name="allowForceDark" />
+        <public name="supportsAmbientMode" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e2">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ac9c9f6..15331d64 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -147,19 +147,19 @@
     <!-- Displayed to tell the user that they cannot change the caller ID setting. -->
     <string name="CLIRPermanent">You can\'t change the caller ID setting.</string>
 
-    <!-- Notification title to tell the user that data service is blocked by access control. -->
+    <!-- Notification title to tell the user that data service is blocked by access control. [CHAR LIMIT=NOTIF_TITLE] -->
     <string name="RestrictedOnDataTitle">No mobile data service</string>
-    <!-- Notification title to tell the user that emergency calling is blocked by access control. -->
+    <!-- Notification title to tell the user that emergency calling is blocked by access control. [CHAR LIMIT=NOTIF_TITLE] -->
     <string name="RestrictedOnEmergencyTitle">Emergency calling unavailable</string>
-    <!-- Notification title to tell the user that normal service is blocked by access control. -->
+    <!-- Notification title to tell the user that normal service is blocked by access control. [CHAR LIMIT=NOTIF_TITLE] -->
     <string name="RestrictedOnNormalTitle">No voice service</string>
-    <!-- Notification title to tell the user that all emergency and normal voice services are blocked by access control. -->
+    <!-- Notification title to tell the user that all emergency and normal voice services are blocked by access control. [CHAR LIMIT=NOTIF_TITLE] -->
     <string name="RestrictedOnAllVoiceTitle">No voice service or emergency calling</string>
 
-    <!-- Notification content to tell the user that voice/data/emergency service is blocked by access control. -->
+    <!-- Notification content to tell the user that voice/data/emergency service is blocked by access control. [CHAR LIMIT=NOTIF_BODY] -->
     <string name="RestrictedStateContent">Temporarily turned off by your carrier</string>
 
-    <!-- Notification content to tell the user that voice/data/emergency service is blocked by access control when multiple SIMs are active. -->
+    <!-- Notification content to tell the user that voice/data/emergency service is blocked by access control when multiple SIMs are active. [CHAR LIMIT=NOTIF_BODY] -->
     <string name="RestrictedStateContentMsimTemplate">Temporarily turned off by your carrier for SIM <xliff:g id="simNumber" example="1">%d</xliff:g></string>
 
     <!-- Displayed to tell the user that they should switch their network preference. -->
@@ -321,18 +321,18 @@
     <!-- Sync notifications --> <skip />
     <!-- A notification is shown when there is a sync error.  This is the text that will scroll through the notification bar (will be seen by the user as he uses another application). -->
     <string name="contentServiceSync">Sync</string>
-    <!-- A notification is shown when there is a sync error.  This is the title of the notification.  It will be seen in the pull-down notification tray. -->
+    <!-- A notification is shown when there is a sync error.  This is the title of the notification.  It will be seen in the pull-down notification tray. [CHAR LIMIT=NOTIF_TITLE] -->
     <string name="contentServiceSyncNotificationTitle">Can\'t sync</string>
-    <!-- A notification is shown when there is a sync error.  This is the message of the notification.  It describes the error, in this case is there were too many deletes. The argument is the type of content, for example Gmail or Calendar. It will be seen in the pull-down notification tray. -->
+    <!-- A notification is shown when there is a sync error.  This is the message of the notification.  It describes the error, in this case is there were too many deletes. The argument is the type of content, for example Gmail or Calendar. It will be seen in the pull-down notification tray. [CHAR LIMIT=NOTIF_BODY] -->
     <string name="contentServiceTooManyDeletesNotificationDesc">Attempted to delete too many <xliff:g id="content_type">%s</xliff:g>.</string>
 
-    <!-- If MMS discovers there isn't much space left on the device, it will show a toast with this message. -->
+    <!-- If MMS discovers there isn't much space left on the device, it will show a toast with this message. [CHAR LIMIT=TOAST] -->
     <string name="low_memory" product="tablet">Tablet storage is full. Delete some files to free space.</string>
-    <!-- If MMS discovers there isn't much space left on the device, it will show a toast with this message. -->
+    <!-- If MMS discovers there isn't much space left on the device, it will show a toast with this message. [CHAR LIMIT=TOAST] -->
     <string name="low_memory" product="watch">Watch storage is full. Delete some files to free space.</string>
-    <!-- If MMS discovers there isn't much space left on the device, it will show a toast with this message. -->
+    <!-- If MMS discovers there isn't much space left on the device, it will show a toast with this message. [CHAR LIMIT=TOAST] -->
     <string name="low_memory" product="tv">TV storage is full. Delete some files to free space.</string>
-    <!-- If MMS discovers there isn't much space left on the device, it will show a toast with this message. -->
+    <!-- If MMS discovers there isn't much space left on the device, it will show a toast with this message. [CHAR LIMIT=TOAST] -->
     <string name="low_memory" product="default">Phone storage is full. Delete some files to free space.</string>
 
     <!-- SSL CA cert notification --> <skip />
@@ -539,15 +539,6 @@
     <!-- status message in phone options dialog for when airplane mode is off -->
     <string name="global_actions_airplane_mode_off_status">Airplane mode is OFF</string>
 
-    <!-- label for item that enables battery saver in phone options dialog -->
-    <string name="global_action_toggle_battery_saver">Battery saver</string>
-
-    <!-- status message in phone options dialog for when battery saver is enabled -->
-    <string name="global_action_battery_saver_on_status">Battery saver is OFF</string>
-
-    <!-- status message in phone options dialog for when battery saver is disabled -->
-    <string name="global_action_battery_saver_off_status">Battery saver is ON</string>
-
     <!-- label for item that launches settings in phone options dialog [CHAR LIMIT=15]-->
     <string name="global_action_settings">Settings</string>
 
@@ -671,7 +662,7 @@
     <string name="permgrouplab_contacts">Contacts</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgroupdesc_contacts">access your contacts</string>
-    <!-- Message shown to the user when the apps requests permission from this group -->
+    <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
     <string name="permgrouprequest_contacts">Allow
         &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your contacts?</string>
 
@@ -679,15 +670,22 @@
     <string name="permgrouplab_location">Location</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgroupdesc_location">access this device\'s location</string>
-    <!-- Message shown to the user when the apps requests permission from this group -->
+    <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
     <string name="permgrouprequest_location">Allow
         &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access this device\'s location?</string>
+    <!-- Subtitle of the message shown to the user when the apps requests permission to use the location only while app is in foreground [CHAR LIMIT=150]-->
+    <string name="permgrouprequestdetail_location">The app will only have access to the location while you\u2019re using the app.</string>
+    <!-- Message shown to the user when the apps requests permission to use the location while app is in foreground and background. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
+    <string name="permgroupbackgroundrequest_location">Always allow
+        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access this device\u2019s location?</string>
+    <!-- Subtitle of the message shown to the user when the apps requests permission to use the location while app is in foreground and background [CHAR LIMIT=150] -->
+    <string name="permgroupbackgroundrequestdetail_location">The app will always have access to the location, even when you\u2019re not using the app.</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_calendar">Calendar</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgroupdesc_calendar">access your calendar</string>
-    <!-- Message shown to the user when the apps requests permission from this group -->
+    <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
     <string name="permgrouprequest_calendar">Allow
         &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your calendar?</string>
 
@@ -695,7 +693,7 @@
     <string name="permgrouplab_sms">SMS</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgroupdesc_sms">send and view SMS messages</string>
-    <!-- Message shown to the user when the apps requests permission from this group -->
+    <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
     <string name="permgrouprequest_sms">Allow
         &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to send and view SMS messages?</string>
 
@@ -703,7 +701,7 @@
     <string name="permgrouplab_storage">Storage</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgroupdesc_storage">access photos, media, and files on your device</string>
-    <!-- Message shown to the user when the apps requests permission from this group -->
+    <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
     <string name="permgrouprequest_storage">Allow
         &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access photos, media, and files on your device?</string>
 
@@ -711,7 +709,7 @@
     <string name="permgrouplab_microphone">Microphone</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgroupdesc_microphone">record audio</string>
-    <!-- Message shown to the user when the apps requests permission from this group -->
+    <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
     <string name="permgrouprequest_microphone">Allow
         &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to record audio?</string>
 
@@ -719,7 +717,7 @@
     <string name="permgrouplab_camera">Camera</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgroupdesc_camera">take pictures and record video</string>
-    <!-- Message shown to the user when the apps requests permission from this group -->
+    <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
     <string name="permgrouprequest_camera">Allow
         &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to take pictures and record video?</string>
 
@@ -727,7 +725,7 @@
     <string name="permgrouplab_calllog">Call logs</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgroupdesc_calllog">read and write phone call log</string>
-    <!-- Message shown to the user when the apps requests permission from this group -->
+    <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
     <string name="permgrouprequest_calllog">Allow
         &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your phone call logs?</string>
 
@@ -735,7 +733,7 @@
     <string name="permgrouplab_phone">Phone</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgroupdesc_phone">make and manage phone calls</string>
-    <!-- Message shown to the user when the apps requests permission from this group -->
+    <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
     <string name="permgrouprequest_phone">Allow
         &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to make and manage phone calls?</string>
 
@@ -743,7 +741,7 @@
     <string name="permgrouplab_sensors">Body Sensors</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgroupdesc_sensors">access sensor data about your vital signs</string>
-    <!-- Message shown to the user when the apps requests permission from this group -->
+    <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
     <string name="permgrouprequest_sensors">Allow
         &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access sensor data about your vital signs?</string>
 
@@ -1082,7 +1080,7 @@
         with the operation of the GPS or other location sources.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_accessFineLocation">access precise only in the foreground</string>
+    <string name="permlab_accessFineLocation">access precise location only in the foreground</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_accessFineLocation">This app can get your exact location only when it is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption.</string>
 
@@ -1401,6 +1399,72 @@
     <!-- Content description which should be used for the fingerprint icon. -->
     <string name="fingerprint_icon_content_description">Fingerprint icon</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=70] -->
+    <string name="permlab_manageFace">manage face authentication hardware</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=90] -->
+    <string name="permdesc_manageFace">Allows the app to invoke methods to add and delete facial templates for use.</string>
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=70] -->
+    <string name="permlab_useFaceAuthentication">use face authentication hardware</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=90] -->
+    <string name="permdesc_useFaceAuthentication">Allows the app to use face authentication hardware for authentication</string>
+
+    <!-- Message shown during face acquisition when the face cannot be recognized [CHAR LIMIT=50] -->
+    <string name="face_acquired_insufficient">Couldn\u2019t process face. Please try again.</string>
+    <!-- Message shown during face acquisition when the image is too bright [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_bright">Face is too bright. Please try in lower light.</string>
+    <!-- Message shown during face acquisition when the image is too dark [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_dark">Face is too dark. Please uncover light source.</string>
+    <!-- Message shown during face acquisition when the user is too close to sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_close">Please move sensor farther from face.</string>
+    <!-- Message shown during face acquisition when the user is too far from sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_far">Please bring sensor closer to face.</string>
+    <!-- Message shown during face acquisition when the user is too high relatively to sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_high">Please move sensor higher.</string>
+    <!-- Message shown during face acquisition when the user is too low relatively to sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_low">Please move sensor lower.</string>
+    <!-- Message shown during face acquisition when the user is too right relatively to sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_right">Please move sensor to the right.</string>
+    <!-- Message shown during face acquisition when the user is too left relatively to sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_left">Please move sensor to the left.</string>
+    <!-- Message shown during face acquisition when the user is not front facing the sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_poor_gaze">Please look at the sensor.</string>
+    <!-- Message shown during face acquisition when the user is not detected [CHAR LIMIT=50] -->
+    <string name="face_acquired_not_detected">No face detected.</string>
+    <!-- Message shown during face acquisition when the face is not kept steady infront of device [CHAR LIMIT=50] -->
+    <string name="face_acquired_not_steady">Keep face steady infront of device.</string>
+    <!-- Array containing custom messages shown during face acquisition from vendor.  Vendor is expected to add and translate these strings -->
+    <string-array name="face_acquired_vendor">
+    </string-array>
+
+    <!-- Error message shown when the face hardware can't be accessed. [CHAR LIMIT=50] -->
+    <string name="face_error_hw_not_available">Face hardware not available.</string>
+    <!-- Error message shown when the face hardware timer has expired and the user needs to restart the operation. [CHAR LIMIT=50] -->
+    <string name="face_error_timeout">Face time out reached. Try again.</string>
+    <!-- Error message shown when the face hardware has run out of room for storing faces. [CHAR LIMIT=50] -->
+    <string name="face_error_no_space">Face can\u2019t be stored.</string>
+    <!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] -->
+    <string name="face_error_canceled">Face operation canceled.</string>
+    <!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] -->
+    <string name="face_error_lockout">Too many attempts. Try again later.</string>
+    <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=50] -->
+    <string name="face_error_lockout_permanent">Too many attempts. Facial authentication disabled.</string>
+    <!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] -->
+    <string name="face_error_unable_to_process">Try again.</string>
+    <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=50] -->
+    <string name="face_error_not_enrolled">No face enrolled.</string>
+    <!-- Generic error message shown when the app requests face authentication on a device without a sensor. [CHAR LIMIT=60] -->
+    <string name="face_error_hw_not_present">This device does not have a face authentication sensor</string>
+
+    <!-- Template to be used to name enrolled faces by default. [CHAR LIMIT=10] -->
+    <string name="face_name_template">Face <xliff:g id="faceId" example="1">%d</xliff:g></string>
+
+    <!-- Array containing custom error messages from vendor.  Vendor is expected to add and translate these strings -->
+    <string-array name="face_error_vendor">
+    </string-array>
+
+    <!-- Content description which should be used for the face icon. [CHAR LIMIT=10] -->
+    <string name="face_icon_content_description">Face icon</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readSyncSettings">read sync settings</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -3173,6 +3237,21 @@
     <!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
     <string name="wifi_no_internet_detailed">Tap for options</string>
 
+    <!-- A notification is shown when the user's softap config has been changed due to underlying
+         hardware restrictions. This is the notifications's title.
+         [CHAR_LIMIT=NONE] -->
+    <string name="wifi_softap_config_change">Changes to your hotspot settings</string>
+
+    <!-- A notification is shown when the user's softap config has been changed due to underlying
+         hardware restrictions. This is the notification's summary message.
+         [CHAR_LIMIT=NONE] -->
+    <string name="wifi_softap_config_change_summary">Your hotspot band has changed.</string>
+
+    <!-- A notification is shown when the user's softap config has been changed due to underlying
+         hardware restrictions. This is the notification's full message.
+         [CHAR_LIMIT=NONE] -->
+    <string name="wifi_softap_config_change_detailed">This device doesn\u2019t support your preference for 5GHz only. Instead, this device will use the 5GHz band when available.</string>
+
     <!-- A notification might be shown if the device switches to another network type (e.g., mobile data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the notification's title. %1$s is the network type that the device switched to, e.g., cellular data. It is one of the strings in the network_switch_type_name array. -->
     <string name="network_switch_metered">Switched to <xliff:g id="network_type">%1$s</xliff:g></string>
 
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 50a6ff3..e1db71f 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -506,13 +506,13 @@
         <item name="textEditSuggestionHighlightStyle">?attr/textEditSuggestionHighlightStyle</item>
         <item name="textCursorDrawable">?attr/textCursorDrawable</item>
         <item name="breakStrategy">high_quality</item>
-        <item name="hyphenationFrequency">normal</item>
+        <item name="hyphenationFrequency">none</item>
     </style>
 
     <style name="Widget.CheckedTextView">
         <item name="textAlignment">viewStart</item>
         <item name="breakStrategy">high_quality</item>
-        <item name="hyphenationFrequency">normal</item>
+        <item name="hyphenationFrequency">none</item>
     </style>
 
     <style name="Widget.TextView.ListSeparator">
@@ -540,7 +540,7 @@
         <item name="textColor">?attr/editTextColor</item>
         <item name="gravity">center_vertical</item>
         <item name="breakStrategy">simple</item>
-        <item name="hyphenationFrequency">normal</item>
+        <item name="hyphenationFrequency">none</item>
         <item name="defaultFocusHighlightEnabled">false</item>
     </style>
 
diff --git a/core/res/res/values/styles_package_installer.xml b/core/res/res/values/styles_package_installer.xml
new file mode 100644
index 0000000..339f9c7
--- /dev/null
+++ b/core/res/res/values/styles_package_installer.xml
@@ -0,0 +1,98 @@
+<?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.
+  -->
+
+<!-- styles for the permission grant dialog. -->
+<resources>
+    <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>
+</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 37c08df..2f45cce 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -336,10 +336,16 @@
   <java-symbol type="bool" name="config_wifi_framework_enable_associated_network_selection" />
   <java-symbol type="bool" name="config_wifi_framework_use_single_radio_chain_scan_results_network_selection" />
   <java-symbol type="bool" name="config_wifi_only_link_same_credential_configurations" />
-  <java-symbol type="bool" name="config_wifi_framework_enable_voice_call_sar_tx_power_limit" />
+  <java-symbol type="bool" name="config_wifi_framework_enable_sar_tx_power_limit" />
+  <java-symbol type="bool" name="config_wifi_framework_enable_soft_ap_sar_tx_power_limit" />
+  <java-symbol type="bool" name="config_wifi_framework_enable_body_proximity_sar_tx_power_limit" />
+  <java-symbol type="string" name="config_wifi_sar_sensor_type" />
+  <java-symbol type="integer" name="config_wifi_framework_sar_free_space_event_id" />
+  <java-symbol type="integer" name="config_wifi_framework_sar_near_hand_event_id" />
+  <java-symbol type="integer" name="config_wifi_framework_sar_near_head_event_id" />
+  <java-symbol type="integer" name="config_wifi_framework_sar_near_body_event_id" />
   <java-symbol type="bool" name="config_wifi_enable_disconnection_debounce" />
   <java-symbol type="bool" name="config_wifi_revert_country_code_on_cellular_loss" />
-  <java-symbol type="bool" name="config_wifi_enable_wifi_firmware_debugging" />
   <java-symbol type="integer" name="config_wifi_logger_ring_buffer_default_size_limit_kb" />
   <java-symbol type="integer" name="config_wifi_logger_ring_buffer_verbose_size_limit_kb" />
   <java-symbol type="bool" name="config_wifi_turn_off_during_emergency_call" />
@@ -387,10 +393,6 @@
   <java-symbol type="integer" name="config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz" />
   <java-symbol type="integer" name="config_wifi_framework_wifi_score_low_rssi_threshold_5GHz" />
   <java-symbol type="integer" name="config_wifi_framework_wifi_score_good_rssi_threshold_5GHz" />
-  <java-symbol type="integer" name="config_wifi_framework_wifi_score_bad_link_speed_24" />
-  <java-symbol type="integer" name="config_wifi_framework_wifi_score_bad_link_speed_5" />
-  <java-symbol type="integer" name="config_wifi_framework_wifi_score_good_link_speed_24" />
-  <java-symbol type="integer" name="config_wifi_framework_wifi_score_good_link_speed_5" />
   <java-symbol type="integer" name="config_wifi_framework_scan_result_rssi_level_patchup_value" />
   <java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
   <java-symbol type="string"  name="config_wifi_random_mac_oui" />
@@ -1055,6 +1057,9 @@
   <java-symbol type="string" name="network_switch_type_name_unknown" />
   <java-symbol type="string" name="wifi_no_internet" />
   <java-symbol type="string" name="wifi_no_internet_detailed" />
+  <java-symbol type="string" name="wifi_softap_config_change" />
+  <java-symbol type="string" name="wifi_softap_config_change_summary" />
+  <java-symbol type="string" name="wifi_softap_config_change_detailed" />
   <java-symbol type="string" name="wifi_connect_alert_title" />
   <java-symbol type="string" name="wifi_connect_alert_message" />
   <java-symbol type="string" name="wifi_connect_default_application" />
@@ -1330,7 +1335,6 @@
   <java-symbol type="drawable" name="ic_text_dot" />
   <java-symbol type="drawable" name="ic_print" />
   <java-symbol type="drawable" name="ic_print_error" />
-  <java-symbol type="drawable" name="ic_grayedout_printer" />
   <java-symbol type="drawable" name="jog_dial_arrow_long_left_green" />
   <java-symbol type="drawable" name="jog_dial_arrow_long_right_red" />
   <java-symbol type="drawable" name="jog_dial_arrow_short_left_and_right" />
@@ -1767,9 +1771,6 @@
   <java-symbol type="string" name="global_action_silent_mode_off_status" />
   <java-symbol type="string" name="global_action_silent_mode_on_status" />
   <java-symbol type="string" name="global_action_toggle_silent_mode" />
-  <java-symbol type="string" name="global_action_battery_saver_off_status" />
-  <java-symbol type="string" name="global_action_battery_saver_on_status" />
-  <java-symbol type="string" name="global_action_toggle_battery_saver" />
   <java-symbol type="string" name="global_action_lockdown" />
   <java-symbol type="string" name="global_action_voice_assist" />
   <java-symbol type="string" name="global_action_assist" />
@@ -1822,6 +1823,7 @@
   <java-symbol type="array" name="config_tether_bluetooth_regexs" />
   <java-symbol type="array" name="config_tether_dhcp_range" />
   <java-symbol type="array" name="config_tether_upstream_types" />
+  <java-symbol type="bool" name="config_tether_upstream_automatic" />
   <java-symbol type="array" name="config_tether_apndata" />
   <java-symbol type="array" name="config_tether_usb_regexs" />
   <java-symbol type="array" name="config_tether_wifi_regexs" />
@@ -2193,6 +2195,7 @@
   <java-symbol type="string" name="ext_media_move_failure_message" />
   <java-symbol type="style" name="Animation.RecentApplications" />
   <java-symbol type="integer" name="dock_enter_exit_duration" />
+  <java-symbol type="bool" name="config_battery_percentage_setting_available" />
 
   <!-- ImfTest -->
   <java-symbol type="layout" name="auto_complete_list" />
@@ -2232,6 +2235,7 @@
   <java-symbol type="drawable" name="decor_maximize_button_light" />
   <java-symbol type="color" name="decor_button_dark_color" />
   <java-symbol type="color" name="decor_button_light_color" />
+  <java-symbol type="array" name="unloggable_phone_numbers" />
 
   <!-- From TelephonyProvider -->
   <java-symbol type="xml" name="apns" />
@@ -2412,6 +2416,34 @@
   <java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/>
   <java-symbol type="bool" name="config_fingerprintSupportsGestures"/>
 
+  <!-- Face authentication messages -->
+  <java-symbol type="string" name="face_error_unable_to_process" />
+  <java-symbol type="string" name="face_error_hw_not_available" />
+  <java-symbol type="string" name="face_error_no_space" />
+  <java-symbol type="string" name="face_error_timeout" />
+  <java-symbol type="array" name="face_error_vendor" />
+  <java-symbol type="string" name="face_error_canceled" />
+  <java-symbol type="string" name="face_error_lockout" />
+  <java-symbol type="string" name="face_error_lockout_permanent" />
+  <java-symbol type="string" name="face_error_not_enrolled" />
+  <java-symbol type="string" name="face_error_hw_not_present" />
+  <java-symbol type="string" name="face_acquired_insufficient" />
+  <java-symbol type="string" name="face_acquired_too_bright" />
+  <java-symbol type="string" name="face_acquired_too_dark" />
+  <java-symbol type="string" name="face_acquired_too_close" />
+  <java-symbol type="string" name="face_acquired_too_far" />
+  <java-symbol type="string" name="face_acquired_too_high" />
+  <java-symbol type="string" name="face_acquired_too_low" />
+  <java-symbol type="string" name="face_acquired_too_right" />
+  <java-symbol type="string" name="face_acquired_too_left" />
+  <java-symbol type="string" name="face_acquired_poor_gaze" />
+  <java-symbol type="string" name="face_acquired_not_detected" />
+  <java-symbol type="array" name="face_acquired_vendor" />
+  <java-symbol type="string" name="face_name_template" />
+
+  <!-- Face config -->
+  <java-symbol type="integer" name="config_faceMaxTemplatesPerUser" />
+
   <!-- From various Material changes -->
   <java-symbol type="attr" name="titleTextAppearance" />
   <java-symbol type="attr" name="subtitleTextAppearance" />
@@ -2887,6 +2919,7 @@
   <!-- Pinner Service -->
   <java-symbol type="array" name="config_defaultPinnerServiceFiles" />
   <java-symbol type="bool" name="config_pinnerCameraApp" />
+  <java-symbol type="bool" name="config_pinnerHomeApp" />
 
   <java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
 
@@ -2969,12 +3002,13 @@
   <!-- Default first user restrictions -->
   <java-symbol type="array" name="config_defaultFirstUserRestrictions" />
 
-  <java-symbol type="bool" name="config_permissionReviewRequired" />
+  <java-symbol type="bool" name="config_permissionsIndividuallyControlled" />
+  <java-symbol type="bool" name="config_wirelessConsentRequired" />
 
   <!-- Global actions icons -->
   <java-symbol type="drawable" name="ic_restart" />
   <java-symbol type="drawable" name="ic_screenshot" />
-
+  <java-symbol type="drawable" name="ic_faster_emergency" />
   <java-symbol type="drawable" name="emergency_icon" />
 
   <java-symbol type="array" name="config_convert_to_emergency_number_map" />
@@ -3230,7 +3264,9 @@
   <java-symbol type="bool" name="config_handleVolumeKeysInWindowManager" />
   <java-symbol type="dimen" name="config_inCallNotificationVolume" />
   <java-symbol type="string" name="config_inCallNotificationSound" />
+  <java-symbol type="integer" name="config_autoGroupAtCount" />
   <java-symbol type="bool" name="config_dozeAlwaysOnDisplayAvailable" />
+  <java-symbol type="bool" name="config_dozeAlwaysOnEnabled" />
   <java-symbol type="bool" name="config_displayBlanksAfterDoze" />
   <java-symbol type="bool" name="config_displayBrightnessBucketsInDoze" />
   <java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
@@ -3326,6 +3362,7 @@
 
   <java-symbol type="string" name="global_action_logout" />
   <java-symbol type="string" name="config_mainBuiltInDisplayCutout" />
+  <java-symbol type="string" name="config_mainBuiltInDisplayCutoutRectApproximation" />
   <java-symbol type="drawable" name="messaging_user" />
   <java-symbol type="bool" name="config_fillMainBuiltInDisplayCutout" />
   <java-symbol type="drawable" name="ic_logout" />
@@ -3367,6 +3404,7 @@
   <java-symbol type="string" name="notification_app_name_settings" />
 
   <java-symbol type="integer" name="config_lowBatteryAutoTriggerDefaultLevel" />
+  <java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" />
 
   <!-- For car devices -->
   <java-symbol type="string" name="car_loading_profile" />
@@ -3396,4 +3434,7 @@
 
   <java-symbol type="integer" name="config_defaultHapticFeedbackIntensity" />
   <java-symbol type="integer" name="config_defaultNotificationVibrationIntensity" />
+
+  <java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
+  <java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" />
 </resources>
diff --git a/core/res/res/values/themes_package_installer.xml b/core/res/res/values/themes_package_installer.xml
new file mode 100644
index 0000000..3bc93cd
--- /dev/null
+++ b/core/res/res/values/themes_package_installer.xml
@@ -0,0 +1,35 @@
+<?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.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>
+</resources>
diff --git a/core/tests/HdmiCec/Android.mk b/core/tests/HdmiCec/Android.mk
deleted file mode 100644
index 450068b..0000000
--- a/core/tests/HdmiCec/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Include all test java files
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := HdmiCecTests
-
-LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
-LOCAL_CERTIFICATE := platform
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/HdmiCec/AndroidManifest.xml b/core/tests/HdmiCec/AndroidManifest.xml
deleted file mode 100644
index 80129d0d..0000000
--- a/core/tests/HdmiCec/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.test.example.helloworld"
-    android:sharedUserId="android.uid.system" >
-    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
-        android:targetPackage="android.test.example.helloworld"
-        android:label="Hello World Test"/>
-</manifest>
diff --git a/core/tests/HdmiCec/AndroidTest.xml b/core/tests/HdmiCec/AndroidTest.xml
deleted file mode 100644
index 5af30fb..0000000
--- a/core/tests/HdmiCec/AndroidTest.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<configuration description="Runs sample instrumentation test.">
-    <option name="test-suite-tag" value="apct"/>
-    <option name="test-suite-tag" value="apct-instrumentation"/>
-    <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
-        <option name="test-file-name" value="HelloWorldTests.apk"/>
-    </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
-    <option name="test-suite-tag" value="apct"/>
-    <option name="test-tag" value="SampleInstrumentationTest"/>
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="package" value="android.test.example.helloworld"/>
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
-    </test>
-</configuration>
diff --git a/core/tests/HdmiCec/HelloWorldTests_HalloWelt.config b/core/tests/HdmiCec/HelloWorldTests_HalloWelt.config
deleted file mode 100644
index 902699b..0000000
--- a/core/tests/HdmiCec/HelloWorldTests_HalloWelt.config
+++ /dev/null
@@ -1,26 +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.
--->
-<configuration description="Runs only the HalloWelt test.">
-    <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup" />
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
-        <option name="test-file-name" value="HelloWorldTests.apk" />
-    </target_preparer>
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-tag" value="SampleInstrumentationTest" />
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="android.test.example.helloworld" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
-        <option name="class" value="android.test.example.helloworld.HelloWorldTest" />
-        <option name="method" value="testHalloWelt" />
-    </test>
-</configuration>
diff --git a/core/tests/HdmiCec/OWNERS b/core/tests/HdmiCec/OWNERS
deleted file mode 100644
index cc016f1..0000000
--- a/core/tests/HdmiCec/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-amyjojo@google.com
-nchalko@google.com
-shubang@google.com
\ No newline at end of file
diff --git a/core/tests/HdmiCec/src/android/test/example/helloworld/HelloWorldTest.java b/core/tests/HdmiCec/src/android/test/example/helloworld/HelloWorldTest.java
deleted file mode 100644
index 09321ad..0000000
--- a/core/tests/HdmiCec/src/android/test/example/helloworld/HelloWorldTest.java
+++ /dev/null
@@ -1,66 +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.test.example.helloworld;
-
-import android.support.test.filters.SmallTest;
-import android.util.Log;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class HelloWorldTest {
-    private static final String TAG = HelloWorldTest.class.getSimpleName();
-
-    @BeforeClass
-    public static void beforeClass() {
-        Log.d(TAG, "beforeClass()");
-    }
-
-    @AfterClass
-    public static void afterClass() {
-        Log.d(TAG, "afterClass()");
-    }
-
-    @Before
-    public void before() {
-        Log.d(TAG, "before()");
-    }
-
-    @After
-    public void after() {
-        Log.d(TAG, "after()");
-    }
-
-    @Test
-    @SmallTest
-    public void testHelloWorld() {
-        Log.d(TAG, "testHelloWorld()");
-        Assert.assertNotEquals("Hello", "world");
-    }
-
-    @Test
-    @SmallTest
-    public void testHalloWelt() {
-        Log.d(TAG, "testHalloWelt()");
-        Assert.assertNotEquals("Hallo", "Welt");
-    }
-}
diff --git a/core/tests/benchmarks/src/android/content/res/ResourcesBenchmark.java b/core/tests/benchmarks/src/android/content/res/ResourcesBenchmark.java
deleted file mode 100644
index 426b0dc..0000000
--- a/core/tests/benchmarks/src/android/content/res/ResourcesBenchmark.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.res;
-
-import android.util.AttributeSet;
-import android.util.Xml;
-
-import com.android.internal.R;
-
-import org.xmlpull.v1.XmlPullParser;
-
-import com.google.caliper.AfterExperiment;
-import com.google.caliper.BeforeExperiment;
-
-public class ResourcesBenchmark {
-
-    private AssetManager mAsset;
-    private Resources mRes;
-
-    private int mTextId;
-    private int mColorId;
-    private int mIntegerId;
-    private int mLayoutId;
-
-    @BeforeExperiment
-    protected void setUp() {
-        mAsset = new AssetManager();
-        mAsset.addAssetPath("/system/framework/framework-res.apk");
-        mRes = new Resources(mAsset, null, null);
-
-        mTextId = mRes.getIdentifier("cancel", "string", "android");
-        mColorId = mRes.getIdentifier("transparent", "color", "android");
-        mIntegerId = mRes.getIdentifier("config_shortAnimTime", "integer", "android");
-        mLayoutId = mRes.getIdentifier("two_line_list_item", "layout", "android");
-    }
-
-    @AfterExperiment
-    protected void tearDown() {
-        mAsset.close();
-    }
-
-    public void timeGetString(int reps) {
-        for (int i = 0; i < reps; i++) {
-            mRes.getText(mTextId);
-        }
-    }
-
-    public void timeGetColor(int reps) {
-        for (int i = 0; i < reps; i++) {
-            mRes.getColor(mColorId, null);
-        }
-    }
-
-    public void timeGetInteger(int reps) {
-        for (int i = 0; i < reps; i++) {
-            mRes.getInteger(mIntegerId);
-        }
-    }
-
-    public void timeGetLayoutAndTraverse(int reps) throws Exception {
-        for (int i = 0; i < reps; i++) {
-            final XmlResourceParser parser = mRes.getLayout(mLayoutId);
-            try {
-                while (parser.next() != XmlPullParser.END_DOCUMENT) {
-                    // Walk the entire tree
-                }
-            } finally {
-                parser.close();
-            }
-        }
-    }
-}
diff --git a/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
index 5989da7..c70fc3c 100644
--- a/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
+++ b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
@@ -54,7 +54,7 @@
         for (int i = 0; i < reps; i++) {
             try (FileInputStream in = new FileInputStream(mSrc);
                     FileOutputStream out = new FileOutputStream(mDest)) {
-                copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+                copyInternalUserspace(in.getFD(), out.getFD(), Long.MAX_VALUE, null, null, null);
             }
         }
     }
@@ -63,7 +63,7 @@
         for (int i = 0; i < reps; i++) {
             try (FileInputStream in = new FileInputStream(mSrc);
                     FileOutputStream out = new FileOutputStream(mDest)) {
-                copyInternalSendfile(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+                copyInternalSendfile(in.getFD(), out.getFD(), Long.MAX_VALUE, null, null, null);
             }
         }
     }
@@ -72,7 +72,7 @@
         for (int i = 0; i < reps; i++) {
             try (MemoryPipe in = MemoryPipe.createSource(mData);
                     FileOutputStream out = new FileOutputStream(mDest)) {
-                copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+                copyInternalUserspace(in.getFD(), out.getFD(), Long.MAX_VALUE, null, null, null);
             }
         }
     }
@@ -81,7 +81,7 @@
         for (int i = 0; i < reps; i++) {
             try (MemoryPipe in = MemoryPipe.createSource(mData);
                     FileOutputStream out = new FileOutputStream(mDest)) {
-                copyInternalSplice(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+                copyInternalSplice(in.getFD(), out.getFD(), Long.MAX_VALUE, null, null, null);
             }
         }
     }
@@ -90,7 +90,7 @@
         for (int i = 0; i < reps; i++) {
             try (FileInputStream in = new FileInputStream(mSrc);
                     MemoryPipe out = MemoryPipe.createSink(mData)) {
-                copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+                copyInternalUserspace(in.getFD(), out.getFD(), Long.MAX_VALUE, null, null, null);
             }
         }
     }
@@ -99,7 +99,7 @@
         for (int i = 0; i < reps; i++) {
             try (FileInputStream in = new FileInputStream(mSrc);
                     MemoryPipe out = MemoryPipe.createSink(mData)) {
-                copyInternalSplice(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+                copyInternalSplice(in.getFD(), out.getFD(), Long.MAX_VALUE, null, null, null);
             }
         }
     }
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index ada0366..b906d84 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -446,7 +446,10 @@
 
         final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
         mContext.registerReceiver(receiver, filter);
-        assertTrue(adapter.enable());
+        // Note: for Wear Local Edition builds, which have Permission Review Mode enabled to
+        // obey China CMIIT, BluetoothAdapter may not startup immediately on methods enable/disable.
+        // So no assertion applied here.
+        adapter.enable();
         boolean success = false;
         try {
             success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS);
@@ -489,7 +492,10 @@
 
         final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
         mContext.registerReceiver(receiver, filter);
-        assertTrue(adapter.disable());
+        // Note: for Wear Local Edition builds, which have Permission Review Mode enabled to
+        // obey China CMIIT, BluetoothAdapter may not startup immediately on methods enable/disable.
+        // So no assertion applied here.
+        adapter.disable();
         boolean success = false;
         try {
             success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS);
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 0f019e71..2a906ae 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1401,6 +1401,14 @@
         <service android:name="android.content.CrossUserContentService"
                 android:exported="true" />
 
+        <activity android:name="android.app.assist.EmptyLayoutActivity"
+                  android:label="My Title">
+           <intent-filter>
+               <action android:name="android.intent.action.MAIN" />
+               <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+           </intent-filter>
+       </activity>
+
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/ascii.ttc b/core/tests/coretests/assets/fonts_for_family_selection/ascii.ttc
new file mode 100644
index 0000000..e404f2b
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/ascii.ttc
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttf b/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttf
new file mode 100644
index 0000000..792b7d7
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttx b/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttx
new file mode 100644
index 0000000..22b2941
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttx
@@ -0,0 +1,1942 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+    <GlyphID id="2" name="b"/>
+    <GlyphID id="3" name="c"/>
+    <GlyphID id="4" name="d"/>
+    <GlyphID id="5" name="e"/>
+    <GlyphID id="6" name="f"/>
+    <GlyphID id="7" name="g"/>
+    <GlyphID id="8" name="h"/>
+    <GlyphID id="9" name="i"/>
+    <GlyphID id="10" name="j"/>
+    <GlyphID id="11" name="k"/>
+    <GlyphID id="12" name="l"/>
+    <GlyphID id="13" name="m"/>
+    <GlyphID id="14" name="n"/>
+    <GlyphID id="15" name="o"/>
+    <GlyphID id="16" name="p"/>
+    <GlyphID id="17" name="q"/>
+    <GlyphID id="18" name="r"/>
+    <GlyphID id="19" name="s"/>
+    <GlyphID id="20" name="t"/>
+    <GlyphID id="21" name="u"/>
+    <GlyphID id="22" name="v"/>
+    <GlyphID id="23" name="w"/>
+    <GlyphID id="24" name="x"/>
+    <GlyphID id="25" name="y"/>
+    <GlyphID id="26" name="z"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="50"/>
+    <created value="Thu Feb 22 10:04:28 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="100"/>
+    <descent value="0"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="100"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="50" lsb="93"/>
+    <mtx name="a" width="50" lsb="0"/>
+    <mtx name="b" width="50" lsb="0"/>
+    <mtx name="c" width="50" lsb="0"/>
+    <mtx name="d" width="50" lsb="0"/>
+    <mtx name="e" width="50" lsb="0"/>
+    <mtx name="f" width="50" lsb="0"/>
+    <mtx name="g" width="50" lsb="0"/>
+    <mtx name="h" width="50" lsb="0"/>
+    <mtx name="i" width="50" lsb="0"/>
+    <mtx name="j" width="50" lsb="0"/>
+    <mtx name="k" width="50" lsb="0"/>
+    <mtx name="l" width="50" lsb="0"/>
+    <mtx name="m" width="50" lsb="0"/>
+    <mtx name="n" width="50" lsb="0"/>
+    <mtx name="o" width="50" lsb="0"/>
+    <mtx name="p" width="50" lsb="0"/>
+    <mtx name="q" width="50" lsb="0"/>
+    <mtx name="r" width="50" lsb="0"/>
+    <mtx name="s" width="50" lsb="0"/>
+    <mtx name="t" width="50" lsb="0"/>
+    <mtx name="u" width="50" lsb="0"/>
+    <mtx name="v" width="50" lsb="0"/>
+    <mtx name="w" width="50" lsb="0"/>
+    <mtx name="x" width="50" lsb="0"/>
+    <mtx name="y" width="50" lsb="0"/>
+    <mtx name="z" width="50" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+        <map code="0x0061" name="a" /> <!-- a -->
+        <map code="0x0062" name="b" /> <!-- b -->
+        <map code="0x0063" name="c" /> <!-- c -->
+        <map code="0x0064" name="d" /> <!-- d -->
+        <map code="0x0065" name="e" /> <!-- e -->
+        <map code="0x0066" name="f" /> <!-- f -->
+        <map code="0x0067" name="g" /> <!-- g -->
+        <map code="0x0068" name="h" /> <!-- h -->
+        <map code="0x0069" name="i" /> <!-- i -->
+        <map code="0x006A" name="j" /> <!-- j -->
+        <map code="0x006B" name="k" /> <!-- k -->
+        <map code="0x006C" name="l" /> <!-- l -->
+        <map code="0x006D" name="m" /> <!-- m -->
+        <map code="0x006E" name="n" /> <!-- n -->
+        <map code="0x006F" name="o" /> <!-- o -->
+        <map code="0x0070" name="p" /> <!-- p -->
+        <map code="0x0071" name="q" /> <!-- q -->
+        <map code="0x0072" name="r" /> <!-- r -->
+        <map code="0x0073" name="s" /> <!-- s -->
+        <map code="0x0074" name="t" /> <!-- t -->
+        <map code="0x0075" name="u" /> <!-- u -->
+        <map code="0x0076" name="v" /> <!-- v -->
+        <map code="0x0077" name="w" /> <!-- w -->
+        <map code="0x0078" name="x" /> <!-- x -->
+        <map code="0x0079" name="y" /> <!-- y -->
+        <map code="0x007A" name="z" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="a" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="b" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="c" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="d" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="e" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="f" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="g" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="h" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="i" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="j" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="k" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="l" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="m" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="n" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="o" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="p" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="q" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="r" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="s" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="t" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="u" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="v" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="w" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="x" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="y" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="z" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+  </glyf>
+
+  <fvar>
+    <Axis>
+      <!-- Weight axis but no effects to the glyph. -->
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>1000.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <!-- Italic axis but no effects to the glyph. -->
+      <AxisTag>ital</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asca</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascb</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascc</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascd</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asce</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascf</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascg</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asch</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asci</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascj</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asck</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascl</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascm</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascn</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asco</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascp</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascq</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascr</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascs</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asct</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascu</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascv</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascw</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascx</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascy</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascz</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+  </fvar>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1" />
+      <VarRegionList>
+        <Region index="0">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="3">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="4">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="5">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="6">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="7">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="8">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="9">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="10">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="11">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="12">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="13">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="14">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="15">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="16">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="17">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="18">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="19">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="20">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="21">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="22">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="23">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="24">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="25">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="26">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <VarData index="0">
+        <NumShorts value="0" />
+        <VarRegionIndex index="0" value="0" />
+        <VarRegionIndex index="1" value="1" />
+        <VarRegionIndex index="2" value="2" />
+        <VarRegionIndex index="3" value="3" />
+        <VarRegionIndex index="4" value="4" />
+        <VarRegionIndex index="5" value="5" />
+        <VarRegionIndex index="6" value="6" />
+        <VarRegionIndex index="7" value="7" />
+        <VarRegionIndex index="8" value="8" />
+        <VarRegionIndex index="9" value="9" />
+        <VarRegionIndex index="10" value="10" />
+        <VarRegionIndex index="11" value="11" />
+        <VarRegionIndex index="12" value="12" />
+        <VarRegionIndex index="13" value="13" />
+        <VarRegionIndex index="14" value="14" />
+        <VarRegionIndex index="15" value="15" />
+        <VarRegionIndex index="16" value="16" />
+        <VarRegionIndex index="17" value="17" />
+        <VarRegionIndex index="18" value="18" />
+        <VarRegionIndex index="19" value="19" />
+        <VarRegionIndex index="20" value="20" />
+        <VarRegionIndex index="21" value="21" />
+        <VarRegionIndex index="22" value="22" />
+        <VarRegionIndex index="23" value="23" />
+        <VarRegionIndex index="24" value="24" />
+        <VarRegionIndex index="25" value="25" />
+        <VarRegionIndex index="26" value="26" />
+        <Item index="0" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="1" value="[0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="2" value="[0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="3" value="[0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="4" value="[0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="5" value="[0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="6" value="[0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="7" value="[0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="8" value="[0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="9" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="10" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="11" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="12" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="13" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="14" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="15" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="16" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="17" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="18" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="19" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="20" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0]" />
+        <Item index="21" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0]" />
+        <Item index="22" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0]" />
+        <Item index="23" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0]" />
+        <Item index="24" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0]" />
+        <Item index="25" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0]" />
+        <Item index="26" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100]" />
+      </VarData>
+    </VarStore>
+    <AdvWidthMap>
+      <Map index="0" outer="0" inner="0" />
+      <Map index="1" outer="0" inner="1" />
+      <Map index="2" outer="0" inner="2" />
+      <Map index="3" outer="0" inner="3" />
+      <Map index="4" outer="0" inner="4" />
+      <Map index="5" outer="0" inner="5" />
+      <Map index="6" outer="0" inner="6" />
+      <Map index="7" outer="0" inner="7" />
+      <Map index="8" outer="0" inner="8" />
+      <Map index="9" outer="0" inner="9" />
+      <Map index="10" outer="0" inner="10" />
+      <Map index="11" outer="0" inner="11" />
+      <Map index="12" outer="0" inner="12" />
+      <Map index="13" outer="0" inner="13" />
+      <Map index="14" outer="0" inner="14" />
+      <Map index="15" outer="0" inner="15" />
+      <Map index="16" outer="0" inner="16" />
+      <Map index="17" outer="0" inner="17" />
+      <Map index="18" outer="0" inner="18" />
+      <Map index="19" outer="0" inner="19" />
+      <Map index="20" outer="0" inner="20" />
+      <Map index="21" outer="0" inner="21" />
+      <Map index="22" outer="0" inner="22" />
+      <Map index="23" outer="0" inner="23" />
+      <Map index="24" outer="0" inner="24" />
+      <Map index="25" outer="0" inner="25" />
+      <Map index="26" outer="0" inner="26" />
+    </AdvWidthMap>
+  </HVAR>
+
+  <gvar>
+    <version value="1" />
+    <reserved value="0" />
+    <glyphVariations glyph="a">
+      <tuple>
+        <coord axis="Asca" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="b">
+      <tuple>
+        <coord axis="Ascb" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="c">
+      <tuple>
+        <coord axis="Ascc" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="d">
+      <tuple>
+        <coord axis="Ascd" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="e">
+      <tuple>
+        <coord axis="Asce" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="f">
+      <tuple>
+        <coord axis="Ascf" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="g">
+      <tuple>
+        <coord axis="Ascg" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="h">
+      <tuple>
+        <coord axis="Asch" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="i">
+      <tuple>
+        <coord axis="Asci" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="j">
+      <tuple>
+        <coord axis="Ascj" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="k">
+      <tuple>
+        <coord axis="Asck" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="l">
+      <tuple>
+        <coord axis="Ascl" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="m">
+      <tuple>
+        <coord axis="Ascm" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="n">
+      <tuple>
+        <coord axis="Ascn" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="o">
+      <tuple>
+        <coord axis="Asco" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="p">
+      <tuple>
+        <coord axis="Ascp" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="q">
+      <tuple>
+        <coord axis="Ascq" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="r">
+      <tuple>
+        <coord axis="Ascr" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="s">
+      <tuple>
+        <coord axis="Ascs" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="t">
+      <tuple>
+        <coord axis="Asct" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="u">
+      <tuple>
+        <coord axis="Ascu" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="v">
+      <tuple>
+        <coord axis="Ascv" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="w">
+      <tuple>
+        <coord axis="Ascw" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="x">
+      <tuple>
+        <coord axis="Ascx" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="y">
+      <tuple>
+        <coord axis="Ascy" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="z">
+      <tuple>
+        <coord axis="Ascz" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      3 em signal
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttf
new file mode 100644
index 0000000..f220eb3
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttx
new file mode 100644
index 0000000..ebedcb6
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:10 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="100"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="3em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttf
new file mode 100644
index 0000000..b9ffb84
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttx
new file mode 100644
index 0000000..def6a29
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:11 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="100"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="3em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttf
new file mode 100644
index 0000000..075b068
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttx
new file mode 100644
index 0000000..d201183
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:11 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="200"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="3em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttf
new file mode 100644
index 0000000..5b47f0d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttx
new file mode 100644
index 0000000..7c801a0
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:11 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="200"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="3em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttf
new file mode 100644
index 0000000..3e9133b
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttx
new file mode 100644
index 0000000..acb2006
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:11 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="300"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="3em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttf
new file mode 100644
index 0000000..3ba3feb
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttx
new file mode 100644
index 0000000..452ff66
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:11 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="300"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="3em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttf
new file mode 100644
index 0000000..b3175a1
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttx
new file mode 100644
index 0000000..34a638f
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:11 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="3em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttf
new file mode 100644
index 0000000..099c4f1
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttx
new file mode 100644
index 0000000..b18af66
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:12 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="3em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttf
new file mode 100644
index 0000000..b8edcb6
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttx
new file mode 100644
index 0000000..6daf8da
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:12 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="500"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="3em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttf
new file mode 100644
index 0000000..6d7dde9
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttx
new file mode 100644
index 0000000..c5843f0
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:12 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="500"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="3em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttf
new file mode 100644
index 0000000..eb6d7d1
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttx
new file mode 100644
index 0000000..d46059f
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:12 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="600"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="3em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttf
new file mode 100644
index 0000000..f509e94
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttx
new file mode 100644
index 0000000..03073d33d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:12 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="600"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="3em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttf
new file mode 100644
index 0000000..062f299
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttx
new file mode 100644
index 0000000..ca24fde
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:12 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="700"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="3em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttf
new file mode 100644
index 0000000..fdd0239
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttx
new file mode 100644
index 0000000..468d591
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:13 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="700"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="3em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_names.txt b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_names.txt
new file mode 100644
index 0000000..986d712
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_names.txt
@@ -0,0 +1,18 @@
+ascii_a3em_weight100_upright.ttf
+ascii_b3em_weight100_italic.ttf
+ascii_c3em_weight200_upright.ttf
+ascii_d3em_weight200_italic.ttf
+ascii_e3em_weight300_upright.ttf
+ascii_f3em_weight300_italic.ttf
+ascii_g3em_weight400_upright.ttf
+ascii_h3em_weight400_italic.ttf
+ascii_i3em_weight500_upright.ttf
+ascii_j3em_weight500_italic.ttf
+ascii_k3em_weight600_upright.ttf
+ascii_l3em_weight600_italic.ttf
+ascii_m3em_weight700_upright.ttf
+ascii_n3em_weight700_italic.ttf
+ascii_o3em_weight800_upright.ttf
+ascii_p3em_weight800_italic.ttf
+ascii_q3em_weight900_upright.ttf
+ascii_r3em_weight900_italic.ttf
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttf
new file mode 100644
index 0000000..993e7fa
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttx
new file mode 100644
index 0000000..b8c712f
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:13 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="800"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="3em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttf
new file mode 100644
index 0000000..f0d54f0
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttx
new file mode 100644
index 0000000..5d8ee18
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:13 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="800"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="3em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttf
new file mode 100644
index 0000000..c776c783
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttx
new file mode 100644
index 0000000..169fd73
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:13 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="900"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="3em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttf
new file mode 100644
index 0000000..02f6246
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttx
new file mode 100644
index 0000000..131d7b1
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:13 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="900"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="3em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/res/layout/empty_layout.xml b/core/tests/coretests/res/layout/empty_layout.xml
new file mode 100644
index 0000000..c208b95
--- /dev/null
+++ b/core/tests/coretests/res/layout/empty_layout.xml
@@ -0,0 +1,26 @@
+<?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:tools="http://schemas.android.com/tools"
+    android:id="@+id/parent"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
+    android:orientation="vertical">
+</LinearLayout>
diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
new file mode 100644
index 0000000..bdb3e08
--- /dev/null
+++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
@@ -0,0 +1,285 @@
+/*
+ * 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.app.assist;
+
+import static android.view.View.IMPORTANT_FOR_AUTOFILL_AUTO;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.assist.AssistStructure.ViewNode;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit test for {@link AssistStructure}.
+ *
+ * <p>To run it: {@code atest app.assist.AssistStructureTest}
+ *
+ * <p>TODO: right now this test is focused in the parcelization of a big object, due to an
+ * upcoming refactoring on the Autofill parcelization that does not use
+ * {@link AssistStructure#ensureData()} to load the structure (which in turn requires calls from
+ * system server to the app). Ideally it should be emprove to:
+ *
+ * <ol>
+ *    <li>Add tests for Assist (to make sure autofill properties are not parcelized).
+ *    <li>Assert all properties and make sure just the relevant properties (for Autofill or Assist)
+ *    are parcelized.
+ *    <li>Add tests for Autofill requests with the {@code FLAG_MANUAL_REQUEST} flag.
+ * </ol>
+ */
+@RunWith(AndroidJUnit4.class)
+public class AssistStructureTest {
+
+    private static final String TAG = "AssistStructureTest";
+
+    private static final boolean FOR_AUTOFILL = true;
+    private static final int NO_FLAGS = 0;
+
+    private static final int BIG_VIEW_SIZE = 10_000_000;
+    private static final char BIG_VIEW_CHAR = '6';
+    private static final String BIG_STRING = repeat(BIG_VIEW_CHAR, BIG_VIEW_SIZE);
+    // Cannot be much big because it could hang test due to blocking GC
+    private static final int NUMBER_SMALL_VIEWS = 10_000;
+
+    private EmptyLayoutActivity mActivity;
+
+    private final ActivityTestRule<EmptyLayoutActivity> mActivityTestRule =
+            new ActivityTestRule<>(EmptyLayoutActivity.class);
+
+    private final Context mContext = InstrumentationRegistry.getTargetContext();
+
+    @Before
+    public void setup() {
+        mActivity = mActivityTestRule.launchActivity(null);
+    }
+
+    @Test
+    public void testParcelizationForAutofill_oneSmallView() {
+        mActivity.addView(newSmallView());
+
+        waitUntilViewsAreLaidOff();
+
+        AssistStructure structure = new AssistStructure(mActivity, FOR_AUTOFILL, NO_FLAGS);
+
+        // Check properties on "original" structure
+        assertStructureWithManySmallViews(structure, 1);
+
+        // Check properties on "cloned" structure
+        AssistStructure clone = cloneThroughParcel(structure);
+        assertStructureWithManySmallViews(clone, 1);
+    }
+
+    @Test
+    public void testParcelizationForAutofill_manySmallViews() {
+        Log.d(TAG, "Adding " + NUMBER_SMALL_VIEWS + " small views");
+
+        for (int i = 1; i <= NUMBER_SMALL_VIEWS; i++) {
+            mActivity.addView(newSmallView());
+        }
+
+        waitUntilViewsAreLaidOff();
+
+        AssistStructure structure = new AssistStructure(mActivity, FOR_AUTOFILL, NO_FLAGS);
+
+        // Check properties on "original" structure
+        assertStructureWithManySmallViews(structure, NUMBER_SMALL_VIEWS);
+
+        // Check properties on "cloned" structure
+        AssistStructure clone = cloneThroughParcel(structure);
+        assertStructureWithManySmallViews(clone, NUMBER_SMALL_VIEWS);
+    }
+
+    private void assertStructureWithManySmallViews(AssistStructure structure, int expectedSize) {
+        int i = 0;
+        try {
+            assertPackageName(structure);
+
+            assertThat(structure.getWindowNodeCount()).isEqualTo(1);
+
+            ViewNode rootView = structure.getWindowNodeAt(0).getRootViewNode();
+            assertThat(rootView.getClassName()).isEqualTo(FrameLayout.class.getName());
+            assertThat(rootView.getChildCount()).isEqualTo(2); // title and parent
+            assertThat(rootView.getAutofillId()).isNotNull();
+            assertThat(rootView.getImportantForAutofill()).isEqualTo(IMPORTANT_FOR_AUTOFILL_AUTO);
+
+            // Title
+            ViewNode title = rootView.getChildAt(0);
+            assertThat(title.getClassName()).isEqualTo(TextView.class.getName());
+            assertThat(title.getChildCount()).isEqualTo(0);
+            assertThat(title.getText().toString()).isEqualTo("My Title");
+            assertThat(title.getAutofillId()).isNotNull();
+
+            // Parent
+            ViewNode parent = rootView.getChildAt(1);
+            assertThat(parent.getClassName()).isEqualTo(LinearLayout.class.getName());
+            assertThat(parent.getChildCount()).isEqualTo(expectedSize);
+            assertThat(parent.getIdEntry()).isEqualTo("parent");
+            assertThat(parent.getAutofillId()).isNotNull();
+
+            // Children
+            for (i = 0; i < expectedSize; i++) {
+                ViewNode smallView = parent.getChildAt(i);
+                assertSmallView(smallView);
+            }
+        } catch (RuntimeException | Error e) {
+            Log.e(TAG, "dumping structure because of error at index #" + i + ": " + e);
+            structure.dump(true);
+            throw e;
+        }
+    }
+
+    @Test
+    public void testParcelizationForAutofill_oneBigView() {
+        Log.d(TAG, "Adding view with " + BIG_VIEW_SIZE + " chars");
+
+        mActivity.addView(newBigView());
+        waitUntilViewsAreLaidOff();
+
+        AssistStructure structure = new AssistStructure(mActivity, FOR_AUTOFILL, NO_FLAGS);
+
+        // Check properties on "original" structure
+        assertStructureWithOneBigView(structure);
+
+        // Check properties on "cloned" structure
+        AssistStructure clone = cloneThroughParcel(structure);
+        assertStructureWithOneBigView(clone);
+    }
+
+    private void assertStructureWithOneBigView(AssistStructure structure) {
+        try {
+            assertPackageName(structure);
+
+            assertThat(structure.getWindowNodeCount()).isEqualTo(1);
+
+            ViewNode rootView = structure.getWindowNodeAt(0).getRootViewNode();
+            assertThat(rootView.getClassName()).isEqualTo(FrameLayout.class.getName());
+            assertThat(rootView.getChildCount()).isEqualTo(2); // title and parent
+            assertThat(rootView.getAutofillId()).isNotNull();
+            assertThat(rootView.getImportantForAutofill()).isEqualTo(IMPORTANT_FOR_AUTOFILL_AUTO);
+
+            // Title
+            ViewNode title = rootView.getChildAt(0);
+            assertThat(title.getClassName()).isEqualTo(TextView.class.getName());
+            assertThat(title.getChildCount()).isEqualTo(0);
+            assertThat(title.getText().toString()).isEqualTo("My Title");
+            assertThat(title.getAutofillId()).isNotNull();
+
+            // Parent
+            ViewNode parent = rootView.getChildAt(1);
+            assertThat(parent.getClassName()).isEqualTo(LinearLayout.class.getName());
+            assertThat(parent.getChildCount()).isEqualTo(1);
+            assertThat(parent.getIdEntry()).isEqualTo("parent");
+            assertThat(parent.getAutofillId()).isNotNull();
+
+            // Children
+            ViewNode bigView = parent.getChildAt(0);
+            assertBigView(bigView);
+        } catch (RuntimeException | Error e) {
+            Log.e(TAG, "dumping structure because of error: " + e);
+            structure.dump(true);
+            throw e;
+        }
+    }
+
+    private EditText newSmallView() {
+        EditText view = new EditText(mContext);
+        view.setText("I AM GROOT");
+        return view;
+    }
+
+    private void assertSmallView(ViewNode view) {
+        assertThat(view.getClassName()).isEqualTo(EditText.class.getName());
+        assertThat(view.getChildCount()).isEqualTo(0);
+        assertThat(view.getIdEntry()).isNull();
+        assertThat(view.getAutofillId()).isNotNull();
+        assertThat(view.getText().toString()).isEqualTo("I AM GROOT");
+    }
+
+    private EditText newBigView() {
+        EditText view = new EditText(mContext);
+        view.setText("Big Hint in Little View");
+        view.setAutofillHints(BIG_STRING);
+        return view;
+    }
+
+    private void assertBigView(ViewNode view) {
+        assertThat(view.getClassName()).isEqualTo(EditText.class.getName());
+        assertThat(view.getChildCount()).isEqualTo(0);
+        assertThat(view.getIdEntry()).isNull();
+        assertThat(view.getAutofillId()).isNotNull();
+        assertThat(view.getText().toString()).isEqualTo("Big Hint in Little View");
+
+        String[] hints = view.getAutofillHints();
+        assertThat(hints.length).isEqualTo(1);
+        String hint = hints[0];
+        // Cannot assert the whole string because it takes too long and crashes the test by ANR
+        assertThat(hint.length()).isEqualTo(BIG_VIEW_SIZE);
+        assertThat(hint.charAt(0)).isEqualTo(BIG_VIEW_CHAR);
+        assertThat(hint.charAt(BIG_VIEW_SIZE - 1)).isEqualTo(BIG_VIEW_CHAR);
+    }
+
+    private void assertPackageName(AssistStructure structure) {
+        assertThat(structure.getActivityComponent()).isEqualTo(
+                new ComponentName("com.android.frameworks.coretests",
+                        "android.app.assist.EmptyLayoutActivity"));
+    }
+
+    private AssistStructure cloneThroughParcel(AssistStructure structure) {
+        Parcel parcel = Parcel.obtain();
+
+        try {
+            // Write to parcel
+            parcel.setDataPosition(0); // Sanity / paranoid check
+            structure.writeToParcel(parcel, NO_FLAGS);
+
+            // Read from parcel
+            parcel.setDataPosition(0);
+            AssistStructure clone = AssistStructure.CREATOR.createFromParcel(parcel);
+            assertThat(clone).isNotNull();
+            return clone;
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    private void waitUntilViewsAreLaidOff() {
+        // TODO: use a more robust mechanism than just sleeping
+        SystemClock.sleep(3000);
+    }
+
+    // TODO: use some common helper
+    private static String repeat(char c, int size) {
+        StringBuilder builder = new StringBuilder(size);
+        for (int i = 1; i <= size; i++) {
+            builder.append(c);
+        }
+        return builder.toString();
+    }
+}
diff --git a/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java b/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java
new file mode 100644
index 0000000..f4b6bed
--- /dev/null
+++ b/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java
@@ -0,0 +1,44 @@
+/*
+ * 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.app.assist;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import android.widget.LinearLayout;
+
+import com.android.frameworks.coretests.R;
+
+public class EmptyLayoutActivity extends Activity {
+
+    private LinearLayout mParent;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.empty_layout);
+
+        mParent = findViewById(R.id.parent);
+    }
+
+    public void addView(View view) {
+        view.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+                LayoutParams.WRAP_CONTENT));
+        runOnUiThread(() -> mParent.addView(view));
+    }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index fe58116..a788a93 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -47,6 +47,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.ArrayMap;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -56,6 +57,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 /** Test {@link TransactionExecutor} logic. */
@@ -232,6 +234,44 @@
     }
 
     @Test
+    public void testDoNotLaunchDestroyedActivity() {
+        final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed = new ArrayMap<>();
+        when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
+        // Assume launch transaction is still in queue, so there is no client record.
+        when(mTransactionHandler.getActivityClient(any())).thenReturn(null);
+
+        // An incoming destroy transaction enters binder thread (preExecute).
+        final IBinder token = mock(IBinder.class);
+        final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */,
+                token /* activityToken */);
+        destroyTransaction.setLifecycleStateRequest(
+                DestroyActivityItem.obtain(false /* finished */, 0 /* configChanges */));
+        destroyTransaction.preExecute(mTransactionHandler);
+        // The activity should be added to to-be-destroyed container.
+        assertEquals(1, mTransactionHandler.getActivitiesToBeDestroyed().size());
+
+        // A previous queued launch transaction runs on main thread (execute).
+        final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */,
+                token /* activityToken */);
+        final LaunchActivityItem launchItem = spy(LaunchActivityItem.obtain(
+                null /* intent */, 0 /* ident */, null /* info */, null /* curConfig */,
+                null, /* overrideConfig */ null /* compatInfo */, null /* referrer */ ,
+                null /* voiceInteractor */, 0 /* procState */, null /* state */,
+                null /* persistentState */, null /* pendingResults */,
+                null /* pendingNewIntents */, false /* isForward */, null /* profilerInfo */));
+        launchTransaction.addCallback(launchItem);
+        mExecutor.execute(launchTransaction);
+
+        // The launch transaction should not be executed because its token is in the
+        // to-be-destroyed container.
+        verify(launchItem, times(0)).execute(any(), any(), any());
+
+        // After the destroy transaction has been executed, the token should be removed.
+        mExecutor.execute(destroyTransaction);
+        assertEquals(0, mTransactionHandler.getActivitiesToBeDestroyed().size());
+    }
+
+    @Test
     public void testActivityResultRequiredStateResolution() {
         PostExecItem postExecItem = new PostExecItem(ON_RESUME);
 
@@ -243,12 +283,12 @@
         // Verify resolution that should get to onPause
         mClientRecord.setState(ON_RESUME);
         mExecutor.executeCallbacks(transaction);
-        verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_PAUSE));
+        verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction));
 
         // Verify resolution that should get to onStart
         mClientRecord.setState(ON_STOP);
         mExecutor.executeCallbacks(transaction);
-        verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_START));
+        verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction));
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/database/DatabaseUtilsTest.java b/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
new file mode 100644
index 0000000..7c206d7
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseUtilsTest.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 android.database;
+
+import static android.database.DatabaseUtils.bindSelection;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DatabaseUtilsTest {
+    private static final Object[] ARGS = { "baz", 4, null };
+
+    @Test
+    public void testBindSelection_none() throws Exception {
+        assertEquals(null,
+                bindSelection(null, ARGS));
+        assertEquals("",
+                bindSelection("", ARGS));
+        assertEquals("foo=bar",
+                bindSelection("foo=bar", ARGS));
+    }
+
+    @Test
+    public void testBindSelection_normal() throws Exception {
+        assertEquals("foo='baz'",
+                bindSelection("foo=?", ARGS));
+        assertEquals("foo='baz' AND bar=4",
+                bindSelection("foo=? AND bar=?", ARGS));
+        assertEquals("foo='baz' AND bar=4 AND meow=NULL",
+                bindSelection("foo=? AND bar=? AND meow=?", ARGS));
+    }
+
+    @Test
+    public void testBindSelection_whitespace() throws Exception {
+        assertEquals("BETWEEN 5 AND 10",
+                bindSelection("BETWEEN? AND ?", 5, 10));
+        assertEquals("IN 'foo'",
+                bindSelection("IN?", "foo"));
+    }
+
+    @Test
+    public void testBindSelection_indexed() throws Exception {
+        assertEquals("foo=10 AND bar=11 AND meow=1",
+                bindSelection("foo=?10 AND bar=? AND meow=?1",
+                        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12));
+    }
+}
diff --git a/core/tests/coretests/src/android/graphics/FontFileUtilTest.java b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
new file mode 100644
index 0000000..76267b2
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.graphics;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.graphics.fonts.FontFileUtil;
+import android.graphics.fonts.FontVariationAxis;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.util.Log;
+import android.util.Pair;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+@SmallTest
+public class FontFileUtilTest {
+    private static final String TAG = "FontFileUtilTest";
+    private static final String CACHE_FILE_PREFIX = ".font";
+
+    private static File getTempFile() {
+        Context ctx = InstrumentationRegistry.getTargetContext();
+        final String prefix = CACHE_FILE_PREFIX;
+        for (int i = 0; i < 100; ++i) {
+            final File file = new File(ctx.getCacheDir(), prefix + i);
+            try {
+                if (file.createNewFile()) {
+                    return file;
+                }
+            } catch (IOException e) {
+                // ignore. Try next file.
+            }
+        }
+        return null;
+    }
+
+    private static ByteBuffer mmap(AssetManager am, String path) {
+        File file = getTempFile();
+        try (InputStream is = am.open(path)) {
+            if (!copyToFile(file, is)) {
+                return null;
+            }
+            return mmap(file);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to open assets");
+            return null;
+        } finally {
+            file.delete();
+        }
+    }
+
+    private static ByteBuffer mmap(File file) {
+        try (FileInputStream fis = new FileInputStream(file)) {
+            FileChannel channel = fis.getChannel();
+            return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    private static boolean copyToFile(File file, InputStream is) {
+        try (FileOutputStream os = new FileOutputStream(file, false)) {
+            byte[] buffer = new byte[1024];
+            int readLen;
+            while ((readLen = is.read(buffer)) != -1) {
+                os.write(buffer, 0, readLen);
+            }
+            return true;
+        } catch (IOException e) {
+            Log.e(TAG, "Error copying resource contents to temp file: " + e.getMessage());
+            return false;
+        }
+    }
+
+    @Test
+    public void testRegularFonts() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            ByteBuffer buffer = mmap(am, path);
+            int packed = FontFileUtil.analyzeStyle(buffer, 0, null);
+            assertEquals(path, weight, FontFileUtil.unpackWeight(packed));
+            assertEquals(path, italic, FontFileUtil.unpackItalic(packed));
+        }
+    }
+
+    @Test
+    public void testTtcFont() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        String path = FontTestUtil.getTtcFontFileInAsset();
+        ByteBuffer buffer = mmap(am, path);
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
+
+            int packed = FontFileUtil.analyzeStyle(buffer, ttcIndex, null);
+            assertEquals(path + "#" + ttcIndex, weight, FontFileUtil.unpackWeight(packed));
+            assertEquals(path + "#" + ttcIndex, italic, FontFileUtil.unpackItalic(packed));
+        }
+    }
+
+    @Test
+    public void testVariationFont() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        String path = FontTestUtil.getVFFontInAsset();
+        ByteBuffer buffer = mmap(am, path);
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String axes = FontTestUtil.getVarSettingsFromStyle(weight, italic);
+
+            int packed = FontFileUtil.analyzeStyle(buffer, 0,
+                    FontVariationAxis.fromFontVariationSettings(axes));
+            assertEquals(path + "#" + axes, weight, FontFileUtil.unpackWeight(packed));
+            assertEquals(path + "#" + axes, italic, FontFileUtil.unpackItalic(packed));
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/graphics/FontTestUtil.java b/core/tests/coretests/src/android/graphics/FontTestUtil.java
new file mode 100644
index 0000000..2becb4b
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/FontTestUtil.java
@@ -0,0 +1,208 @@
+/*
+ * 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.graphics;
+
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides a utility for testing fonts
+ *
+ * For the purpose of testing font selection of families or fallbacks, this class provies following
+ * regular font files.
+ *
+ * - ascii_a3em_weight100_upright.ttf
+ *   'a' has 3em width and others have 1em width. The metadata has weight=100, non-italic value.
+ * - ascii_b3em_weight100_italic.ttf
+ *   'b' has 3em width and others have 1em width. The metadata has weight=100, italic value.
+ * - ascii_c3em_weight200_upright.ttf
+ *   'c' has 3em width and others have 1em width. The metadata has weight=200, non-italic value.
+ * - ascii_d3em_weight200_italic.ttf
+ *   'd' has 3em width and others have 1em width. The metadata has weight=200, italic value.
+ * - ascii_e3em_weight300_upright.ttf
+ *   'e' has 3em width and others have 1em width. The metadata has weight=300, non-italic value.
+ * - ascii_f3em_weight300_italic.ttf
+ *   'f' has 3em width and others have 1em width. The metadata has weight=300, italic value.
+ * - ascii_g3em_weight400_upright.ttf
+ *   'g' has 3em width and others have 1em width. The metadata has weight=400, non-italic value.
+ * - ascii_h3em_weight400_italic.ttf
+ *   'h' has 3em width and others have 1em width. The metadata has weight=400, italic value.
+ * - ascii_i3em_weight500_upright.ttf
+ *   'i' has 3em width and others have 1em width. The metadata has weight=500, non-italic value.
+ * - ascii_j3em_weight500_italic.ttf
+ *   'j' has 3em width and others have 1em width. The metadata has weight=500, italic value.
+ * - ascii_k3em_weight600_upright.ttf
+ *   'k' has 3em width and others have 1em width. The metadata has weight=600, non-italic value.
+ * - ascii_l3em_weight600_italic.ttf
+ *   'l' has 3em width and others have 1em width. The metadata has weight=600, italic value.
+ * - ascii_m3em_weight700_upright.ttf
+ *   'm' has 3em width and others have 1em width. The metadata has weight=700, non-italic value.
+ * - ascii_n3em_weight700_italic.ttf
+ *   'n' has 3em width and others have 1em width. The metadata has weight=700, italic value.
+ * - ascii_o3em_weight800_upright.ttf
+ *   'o' has 3em width and others have 1em width. The metadata has weight=800, non-italic value.
+ * - ascii_p3em_weight800_italic.ttf
+ *   'p' has 3em width and others have 1em width. The metadata has weight=800, italic value.
+ * - ascii_q3em_weight900_upright.ttf
+ *   'q' has 3em width and others have 1em width. The metadata has weight=900, non-italic value.
+ * - ascii_r3em_weight900_italic.ttf
+ *   'r' has 3em width and others have 1em width. The metadata has weight=900, italic value.
+ *
+ * In addition to above font files, this class provides a font collection file and a variable font
+ * file.
+ * - ascii.ttc
+ *   The collection of above 18 fonts with above order.
+ * - ascii_vf.ttf
+ *   This font supports a-z characters and all characters has 1em width. This font supports 'wght',
+ *   'ital' axes but no effect for the glyph width. This font also supports 'Asc[a-z]' 26 axes which
+ *   makes glyph width 3em. For example, 'Asca 1.0' makes a glyph width of 'a' 3em, 'Ascb 1.0' makes
+ *   a glyph width of 'b' 3em. With these axes, above font can be replicated like
+ *   - 'Asca' 1.0, 'wght' 100.0' is equivalent with ascii_a3em_width100_upright.ttf
+ *   - 'Ascb' 1.0, 'wght' 100.0, 'ital' 1.0' is equivalent with ascii_b3em_width100_italic.ttf
+ */
+public class FontTestUtil {
+    private static final String FAMILY_SELECTION_FONT_PATH_IN_ASSET = "fonts_for_family_selection";
+    private static final List<Pair<Integer, Boolean>> sStyleList;
+    private static final Map<Pair<Integer, Boolean>, String> sFontMap;
+    private static final Map<Pair<Integer, Boolean>, Integer> sTtcMap;
+    private static final Map<Pair<Integer, Boolean>, String> sVariationSettingsMap;
+    private static final String[] sFontList = {  // Same order of ascii.ttc
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_a3em_weight100_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_b3em_weight100_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_c3em_weight200_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_d3em_weight200_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_e3em_weight300_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_f3em_weight300_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_g3em_weight400_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_h3em_weight400_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_i3em_weight500_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_j3em_weight500_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_k3em_weight600_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_l3em_weight600_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_m3em_weight700_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_n3em_weight700_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_o3em_weight800_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_p3em_weight800_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_q3em_weight900_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_r3em_weight900_italic.ttf",
+    };
+
+    private static final String[] FONT_VARIATION_SETTING_LIST = {
+            "'Asca' 1.0, 'wght' 100.0",
+            "'Ascb' 1.0, 'wght' 100.0, 'ital' 1.0",
+            "'Ascc' 1.0, 'wght' 200.0",
+            "'Ascd' 1.0, 'wght' 200.0, 'ital' 1.0",
+            "'Asce' 1.0, 'wght' 300.0",
+            "'Ascf' 1.0, 'wght' 300.0, 'ital' 1.0",
+            "'Ascg' 1.0, 'wght' 400.0",
+            "'Asch' 1.0, 'wght' 400.0, 'ital' 1.0",
+            "'Asci' 1.0, 'wght' 500.0",
+            "'Ascj' 1.0, 'wght' 500.0, 'ital' 1.0",
+            "'Asck' 1.0, 'wght' 600.0",
+            "'Ascl' 1.0, 'wght' 600.0, 'ital' 1.0",
+            "'Ascm' 1.0, 'wght' 700.0",
+            "'Ascn' 1.0, 'wght' 700.0, 'ital' 1.0",
+            "'Asco' 1.0, 'wght' 800.0",
+            "'Ascp' 1.0, 'wght' 800.0, 'ital' 1.0",
+            "'Ascq' 1.0, 'wght' 900.0",
+            "'Ascr' 1.0, 'wght' 900.0, 'ital' 1.0",
+    };
+
+    static {
+        // Style list with the same order of sFontList.
+        ArrayList<Pair<Integer, Boolean>> styles = new ArrayList<>();
+        styles.add(new Pair<>(100, false));
+        styles.add(new Pair<>(100, true));
+        styles.add(new Pair<>(200, false));
+        styles.add(new Pair<>(200, true));
+        styles.add(new Pair<>(300, false));
+        styles.add(new Pair<>(300, true));
+        styles.add(new Pair<>(400, false));
+        styles.add(new Pair<>(400, true));
+        styles.add(new Pair<>(500, false));
+        styles.add(new Pair<>(500, true));
+        styles.add(new Pair<>(600, false));
+        styles.add(new Pair<>(600, true));
+        styles.add(new Pair<>(700, false));
+        styles.add(new Pair<>(700, true));
+        styles.add(new Pair<>(800, false));
+        styles.add(new Pair<>(800, true));
+        styles.add(new Pair<>(900, false));
+        styles.add(new Pair<>(900, true));
+        sStyleList = Collections.unmodifiableList(styles);
+
+        HashMap<Pair<Integer, Boolean>, String> map = new HashMap<>();
+        HashMap<Pair<Integer, Boolean>, Integer> ttcMap = new HashMap<>();
+        HashMap<Pair<Integer, Boolean>, String> variationMap = new HashMap<>();
+        HashMap<Character, Pair<Integer, Boolean>> reverseMap = new HashMap<>();
+        for (int i = 0; i < sFontList.length; ++i) {
+            map.put(sStyleList.get(i), sFontList[i]);
+            ttcMap.put(sStyleList.get(i), i);
+            variationMap.put(sStyleList.get(i), FONT_VARIATION_SETTING_LIST[i]);
+        }
+        sFontMap = Collections.unmodifiableMap(map);
+        sTtcMap = Collections.unmodifiableMap(ttcMap);
+        sVariationSettingsMap = Collections.unmodifiableMap(variationMap);
+    }
+
+    /**
+     * Returns a path to the font collection file in asset directory.
+     */
+    public static String getTtcFontFileInAsset() {
+        return FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ascii.ttc";
+    }
+
+    /**
+     * Returns a path to the variable font file in asset directory.
+     */
+    public static String getVFFontInAsset() {
+        return FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ascii_vf.ttf";
+    }
+
+    /**
+     * Returns a ttc index of the specified style.
+     */
+    public static int getTtcIndexFromStyle(int weight, boolean italic) {
+        return sTtcMap.get(new Pair<>(weight, italic)).intValue();
+    }
+
+    /**
+     * Returns a variation settings string of the specified style.
+     */
+    public static String getVarSettingsFromStyle(int weight, boolean italic) {
+        return sVariationSettingsMap.get(new Pair<>(weight, italic));
+    }
+
+    /**
+     * Returns a font path from the specified style.
+     */
+    public static String getFontPathFromStyle(int weight, boolean italic) {
+        return sFontMap.get(new Pair<>(weight, italic));
+    }
+
+    /**
+     * Returns all supported styles.
+     */
+    public static List<Pair<Integer, Boolean>> getAllStyles() {
+        return sStyleList;
+    }
+}
diff --git a/core/tests/coretests/src/android/net/UriCodecTest.java b/core/tests/coretests/src/android/net/UriCodecTest.java
new file mode 100644
index 0000000..7fe9402
--- /dev/null
+++ b/core/tests/coretests/src/android/net/UriCodecTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import junit.framework.TestCase;
+
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Tests for {@link UriCodec}
+ */
+public class UriCodecTest extends TestCase {
+
+    public void testDecode_emptyString_returnsEmptyString() {
+        assertEquals("", UriCodec.decode("",
+                false /* convertPlus */,
+                StandardCharsets.UTF_8,
+                true /* throwOnFailure */));
+    }
+
+    public void testDecode_wrongHexDigit_fails() {
+        try {
+            // %p in the end.
+            UriCodec.decode("ab%2f$%C4%82%25%e0%a1%80%p",
+                    false /* convertPlus */,
+                    StandardCharsets.UTF_8,
+                    true /* throwOnFailure */);
+            fail("Expected URISyntaxException");
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        }
+    }
+
+    public void testDecode_secondHexDigitWrong_fails() {
+        try {
+            // %1p in the end.
+            UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80%1p",
+                    false /* convertPlus */,
+                    StandardCharsets.UTF_8,
+                    true /* throwOnFailure */);
+            fail("Expected URISyntaxException");
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        }
+    }
+
+    public void testDecode_endsWithPercent_fails() {
+        try {
+            // % in the end.
+            UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80%",
+                    false /* convertPlus */,
+                    StandardCharsets.UTF_8,
+                    true /* throwOnFailure */);
+            fail("Expected URISyntaxException");
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        }
+    }
+
+    public void testDecode_dontThrowException_appendsUnknownCharacter() {
+        assertEquals("ab/$\u0102%\u0840\ufffd",
+                UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80%",
+                        false /* convertPlus */,
+                        StandardCharsets.UTF_8,
+                        false /* throwOnFailure */));
+    }
+
+    public void testDecode_convertPlus() {
+        assertEquals("ab/$\u0102% \u0840",
+                UriCodec.decode("ab%2f$%c4%82%25+%e0%a1%80",
+                        true /* convertPlus */,
+                        StandardCharsets.UTF_8,
+                        false /* throwOnFailure */));
+    }
+
+    // Last character needs decoding (make sure we are flushing the buffer with chars to decode).
+    public void testDecode_lastCharacter() {
+        assertEquals("ab/$\u0102%\u0840",
+                UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80",
+                        false /* convertPlus */,
+                        StandardCharsets.UTF_8,
+                        true /* throwOnFailure */));
+    }
+
+    // Check that a second row of encoded characters is decoded properly (internal buffers are
+    // reset properly).
+    public void testDecode_secondRowOfEncoded() {
+        assertEquals("ab/$\u0102%\u0840aa\u0840",
+                UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80aa%e0%a1%80",
+                        false /* convertPlus */,
+                        StandardCharsets.UTF_8,
+                        true /* throwOnFailure */));
+    }
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 0bc3a2d..9c9f11b 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -193,7 +193,7 @@
 
             try (MemoryPipe in = MemoryPipe.createSource(source);
                     FileOutputStream out = new FileOutputStream(dest)) {
-                FileUtils.copy(in.getFD(), out.getFD(), null, null, size);
+                FileUtils.copy(in.getFD(), out.getFD(), size, null, null, null);
             }
 
             actual = readFile(dest);
diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
new file mode 100644
index 0000000..c8bc35c
--- /dev/null
+++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.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.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.system.Os;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class RedactingFileDescriptorTest {
+    private Context mContext;
+    private File mFile;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mFile = File.createTempFile("redacting", "dat");
+        try (FileOutputStream out = new FileOutputStream(mFile)) {
+            final byte[] buf = new byte[1_000_000];
+            Arrays.fill(buf, (byte) 64);
+            out.write(buf);
+        }
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mFile.delete();
+    }
+
+    @Test
+    public void testSingleByte() throws Exception {
+        final FileDescriptor fd = RedactingFileDescriptor
+                .open(mContext, mFile, new long[] { 10, 11 }).getFileDescriptor();
+
+        final byte[] buf = new byte[1_000];
+        assertEquals(buf.length, Os.read(fd, buf, 0, buf.length));
+        for (int i = 0; i < buf.length; i++) {
+            if (i == 10) {
+                assertEquals(0, buf[i]);
+            } else {
+                assertEquals(64, buf[i]);
+            }
+        }
+    }
+
+    @Test
+    public void testRanges() throws Exception {
+        final FileDescriptor fd = RedactingFileDescriptor
+                .open(mContext, mFile, new long[] { 100, 200, 300, 400 }).getFileDescriptor();
+
+        final byte[] buf = new byte[10];
+        assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 90));
+        assertArrayEquals(new byte[] { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 }, buf);
+
+        assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 95));
+        assertArrayEquals(new byte[] { 64, 64, 64, 64, 64, 0, 0, 0, 0, 0 }, buf);
+
+        assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 100));
+        assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, buf);
+
+        assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 195));
+        assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 64, 64, 64, 64, 64 }, buf);
+
+        assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 395));
+        assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 64, 64, 64, 64, 64 }, buf);
+    }
+
+    @Test
+    public void testEntireFile() throws Exception {
+        final FileDescriptor fd = RedactingFileDescriptor
+                .open(mContext, mFile, new long[] { 0, 5_000_000 }).getFileDescriptor();
+
+        try (FileInputStream in = new FileInputStream(fd)) {
+            int val;
+            while ((val = in.read()) != -1) {
+                assertEquals(0, val);
+            }
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/os/WorkSourceTest.java b/core/tests/coretests/src/android/os/WorkSourceTest.java
index 952a64d..425ab89 100644
--- a/core/tests/coretests/src/android/os/WorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/WorkSourceTest.java
@@ -378,6 +378,17 @@
         assertEquals(75, ws1.getWorkChains().get(0).getAttributionUid());
     }
 
+    public void testRemove_fromSameWorkSource() {
+        WorkSource ws1 = new WorkSource(50, "foo");
+        WorkSource ws2 = ws1;
+        ws2.add(ws1);
+        assertTrue(ws2.remove(ws1));
+
+        assertEquals(0, ws1.size());
+        assertEquals(50, ws1.get(0));
+        assertEquals("foo", ws1.getName(0));
+    }
+
     public void testTransferWorkChains() {
         WorkSource ws1 = new WorkSource();
         WorkChain wc1 = ws1.createWorkChain().addNode(100, "tag");
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 7428b12..71627ab 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -66,7 +66,6 @@
                     Settings.System.LOCKSCREEN_DISABLED, // ?
                     Settings.System.MEDIA_BUTTON_RECEIVER, // candidate for backup?
                     Settings.System.MUTE_STREAMS_AFFECTED, //  candidate for backup?
-                    Settings.System.NOTIFICATION_LIGHT_PULSE, // candidate for backup?
                     Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache
                     Settings.System.POINTER_LOCATION, // backup candidate?
                     Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING, // used for testing only
@@ -98,6 +97,7 @@
     private static final Set<String> BACKUP_BLACKLISTED_GLOBAL_SETTINGS =
             newHashSet(
                     Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
+                    Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED,
                     Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED,
                     Settings.Global.ADB_ENABLED,
                     Settings.Global.ADD_USERS_WHEN_LOCKED,
@@ -120,10 +120,14 @@
                     Settings.Global.ASSISTED_GPS_ENABLED,
                     Settings.Global.AUDIO_SAFE_VOLUME_STATE,
                     Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
+                    Settings.Global.AUTOFILL_LOGGING_LEVEL,
+                    Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
+                    Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
                     Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
                     Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
                     Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
                     Settings.Global.BATTERY_STATS_CONSTANTS,
+                    Settings.Global.BINDER_CALLS_STATS,
                     Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE,
                     Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS,
                     Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS,
@@ -236,6 +240,7 @@
                     Settings.Global.EUICC_SUPPORTED_COUNTRIES,
                     Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
                     Settings.Global.FANCY_IME_ANIMATIONS,
+                    Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED,
                     Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
                     Settings.Global.FORCED_APP_STANDBY_ENABLED,
                     Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
@@ -271,6 +276,7 @@
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+                    Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
                     Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
                     Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
                     Settings.Global.LOCK_SOUND,
@@ -378,16 +384,14 @@
                     Settings.Global.SETUP_PREPAID_DATA_SERVICE_URL,
                     Settings.Global.SETUP_PREPAID_DETECTION_REDIR_HOST,
                     Settings.Global.SETUP_PREPAID_DETECTION_TARGET_URL,
+                    Settings.Global.SETTINGS_USE_EXTERNAL_PROVIDER_API,
+                    Settings.Global.SETTINGS_USE_PSD_API,
                     Settings.Global.SHORTCUT_MANAGER_CONSTANTS,
                     Settings.Global.SHOW_FIRST_CRASH_DIALOG,
                     Settings.Global.SHOW_MUTE_IN_CRASH_DIALOG,
                     Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
                     Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
                     Settings.Global.SHOW_TEMPERATURE_WARNING,
-                    Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION,
-                    Settings.Global.SHOW_ZEN_SETTINGS_SUGGESTION,
-                    Settings.Global.ZEN_SETTINGS_UPDATED,
-                    Settings.Global.ZEN_SETTINGS_SUGGESTION_VIEWED,
                     Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
                     Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
                     Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
@@ -458,6 +462,8 @@
                     Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS,
                     Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
                     Settings.Global.WIFI_COUNTRY_CODE,
+                    Settings.Global.WIFI_DATA_STALL_MIN_TX_BAD,
+                    Settings.Global.WIFI_DATA_STALL_MIN_TX_SUCCESS_WITHOUT_RX,
                     Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN,
                     Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON,
                     Settings.Global.WIFI_DISPLAY_ON,
@@ -467,6 +473,8 @@
                     Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
                     Settings.Global.WIFI_FREQUENCY_BAND,
                     Settings.Global.WIFI_IDLE_MS,
+                    Settings.Global.WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED,
+                    Settings.Global.WIFI_LINK_SPEED_METRICS_ENABLED,
                     Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
                     Settings.Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
                     Settings.Global.WIFI_NETWORK_SHOW_RSSI,
@@ -479,6 +487,7 @@
                     Settings.Global.WIFI_SAVED_STATE,
                     Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
                     Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
+                    Settings.Global.WIFI_SCAN_THROTTLE_ENABLED,
                     Settings.Global.WIFI_SCORE_PARAMS,
                     Settings.Global.WIFI_SLEEP_POLICY,
                     Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
@@ -486,8 +495,8 @@
                     Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED,
                     Settings.Global.WIFI_WATCHDOG_ON,
                     Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON,
-                    Settings.Global.WINDOW_ANIMATION_SCALE,
                     Settings.Global.CHARGING_STARTED_SOUND,
+                    Settings.Global.WINDOW_ANIMATION_SCALE,
                     Settings.Global.WTF_IS_FATAL,
                     Settings.Global.ZEN_MODE,
                     Settings.Global.ZEN_MODE_CONFIG_ETAG,
@@ -508,6 +517,8 @@
                  Settings.Secure.ANR_SHOW_BACKGROUND,
                  Settings.Secure.ASSISTANT,
                  Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
+                 Settings.Secure.ASSIST_GESTURE_SENSITIVITY,
+                 Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
                  Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
                  Settings.Secure.ASSIST_STRUCTURE_ENABLED,
                  Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
@@ -551,10 +562,8 @@
                  Settings.Secure.LAST_SETUP_SHOWN,
                  Settings.Secure.LOCATION_CHANGER,
                  Settings.Secure.LOCATION_MODE,
-                 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, // Candidate?
                  Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, // Candidate?
                  Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
-                 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, // Candidate?
                  Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
                  Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH,
                  Settings.Secure.MULTI_PRESS_TIMEOUT,
@@ -588,12 +597,15 @@
                  Settings.Secure.SEARCH_THREAD_KEEPALIVE_SECONDS,
                  Settings.Secure.SEARCH_WEB_RESULTS_OVERRIDE_LIMIT,
                  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.SETTINGS_CLASSNAME,
                  Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, // candidate?
                  Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
                  Settings.Secure.SKIP_FIRST_USE_HINTS, // candidate?
+                 Settings.Secure.SLEEP_TIMEOUT,
                  Settings.Secure.SMS_DEFAULT_APPLICATION,
-                 Settings.Secure.THEME_MODE,
+                 Settings.Secure.SPELL_CHECKER_ENABLED,  // Intentionally removed in Q
                  Settings.Secure.TRUST_AGENTS_INITIALIZED,
                  Settings.Secure.TV_INPUT_CUSTOM_LABELS,
                  Settings.Secure.TV_INPUT_HIDDEN_INPUTS,
@@ -696,3 +708,4 @@
     }
 
 }
+
diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index 6f1d47d..f3d6013 100644
--- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 
 import android.content.Context;
 import android.graphics.Typeface;
@@ -72,7 +73,7 @@
         assertEquals(0, mt.getWidths().size());
         assertEquals(0, mt.getSpanEndCache().size());
         assertEquals(0, mt.getFontMetrics().size());
-        assertEquals(0, mt.getNativePtr());
+        assertNull(mt.getNativeMeasuredParagraph());
 
         // Recycle it
         MeasuredParagraph mt2 = MeasuredParagraph.buildForBidi("_VVV_", 1, 4, RTL, mt);
@@ -84,7 +85,7 @@
         assertEquals(0, mt2.getWidths().size());
         assertEquals(0, mt2.getSpanEndCache().size());
         assertEquals(0, mt2.getFontMetrics().size());
-        assertEquals(0, mt2.getNativePtr());
+        assertNull(mt.getNativeMeasuredParagraph());
 
         mt2.recycle();
     }
@@ -106,7 +107,7 @@
         assertEquals(10, mt.getWidths().get(2), 0);
         assertEquals(0, mt.getSpanEndCache().size());
         assertEquals(0, mt.getFontMetrics().size());
-        assertEquals(0, mt.getNativePtr());
+        assertNull(mt.getNativeMeasuredParagraph());
 
         // Recycle it
         MeasuredParagraph mt2 =
@@ -123,7 +124,7 @@
         assertEquals(5, mt2.getWidths().get(2), 0);
         assertEquals(0, mt2.getSpanEndCache().size());
         assertEquals(0, mt2.getFontMetrics().size());
-        assertEquals(0, mt2.getNativePtr());
+        assertNull(mt.getNativeMeasuredParagraph());
 
         mt2.recycle();
     }
@@ -143,7 +144,7 @@
         assertEquals(1, mt.getSpanEndCache().size());
         assertEquals(3, mt.getSpanEndCache().get(0));
         assertNotEquals(0, mt.getFontMetrics().size());
-        assertNotEquals(0, mt.getNativePtr());
+        assertNotNull(mt.getNativeMeasuredParagraph());
 
         // Recycle it
         MeasuredParagraph mt2 =
@@ -158,7 +159,7 @@
         assertEquals(1, mt2.getSpanEndCache().size());
         assertEquals(4, mt2.getSpanEndCache().get(0));
         assertNotEquals(0, mt2.getFontMetrics().size());
-        assertNotEquals(0, mt2.getNativePtr());
+        assertNotNull(mt.getNativeMeasuredParagraph());
 
         mt2.recycle();
     }
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index 870d6b2..72290bf 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -785,4 +785,11 @@
         assertEquals(2, TextUtils.length("  "));
         assertEquals(6, TextUtils.length("Hello!"));
     }
+
+    @Test
+    public void testTrimToLengthWithEllipsis() {
+        assertEquals("ABC...", TextUtils.trimToLengthWithEllipsis("ABCDEF", 3));
+        assertEquals("ABC", TextUtils.trimToLengthWithEllipsis("ABC", 3));
+        assertEquals("", TextUtils.trimToLengthWithEllipsis("", 3));
+    }
 }
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index 15dbbc8..c98e646 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -20,6 +20,7 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.Suppress;
 import android.text.InputType;
 import android.util.KeyUtils;
 import android.view.KeyEvent;
@@ -238,6 +239,7 @@
     }
 
     @Test
+    @Suppress
     public void testEmojiModifier() {
         EditorState state = new EditorState();
 
@@ -267,6 +269,7 @@
     }
 
     @Test
+    @Suppress
     public void testMixedEdgeCases() {
         EditorState state = new EditorState();
 
diff --git a/core/tests/coretests/src/android/util/Base64Test.java b/core/tests/coretests/src/android/util/Base64Test.java
index 3aee583..af608c3 100644
--- a/core/tests/coretests/src/android/util/Base64Test.java
+++ b/core/tests/coretests/src/android/util/Base64Test.java
@@ -18,13 +18,18 @@
 
 import android.support.test.filters.LargeTest;
 
+import junit.framework.TestCase;
+
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Random;
-import junit.framework.TestCase;
+import java.util.stream.Collectors;
 
 @LargeTest
 public class Base64Test extends TestCase {
@@ -530,4 +535,74 @@
             assertEquals(plain, actual);
         }
     }
+
+    public void testOutputStream_ioExceptionDuringClose() {
+        OutputStream out = new OutputStream() {
+            @Override public void write(int b) throws IOException { }
+            @Override public void close() throws IOException {
+                throw new IOException("close()");
+            }
+        };
+        OutputStream out2 = new Base64OutputStream(out, Base64.DEFAULT);
+        try {
+            out2.close();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testOutputStream_ioExceptionDuringCloseAndWrite() {
+        OutputStream out = new OutputStream() {
+            @Override public void write(int b) throws IOException {
+                throw new IOException("write()");
+            }
+            @Override public void write(byte[] b) throws IOException {
+                throw new IOException("write()");
+            }
+            @Override public void write(byte[] b, int off, int len) throws IOException {
+                throw new IOException("write()");
+            }
+            @Override public void close() throws IOException {
+                throw new IOException("close()");
+            }
+        };
+        OutputStream out2 = new Base64OutputStream(out, Base64.DEFAULT);
+        try {
+            out2.close();
+            fail();
+        } catch (IOException expected) {
+            // Base64OutputStream write()s pending (possibly empty) data
+            // before close(), so the IOE from write() should be thrown and
+            // any later exception suppressed.
+            assertEquals("write()", expected.getMessage());
+            Throwable[] suppressed = expected.getSuppressed();
+            List<String> suppressedMessages = Arrays.asList(suppressed).stream()
+                    .map((e) -> e.getMessage())
+                    .collect(Collectors.toList());
+            assertEquals(Collections.singletonList("close()"), suppressedMessages);
+        }
+    }
+
+    public void testOutputStream_ioExceptionDuringWrite() {
+        OutputStream out = new OutputStream() {
+            @Override public void write(int b) throws IOException {
+                throw new IOException("write()");
+            }
+            @Override public void write(byte[] b) throws IOException {
+                throw new IOException("write()");
+            }
+            @Override public void write(byte[] b, int off, int len) throws IOException {
+                throw new IOException("write()");
+            }
+        };
+        // Base64OutputStream write()s pending (possibly empty) data
+        // before close(), so the IOE from write() should be thrown.
+        OutputStream out2 = new Base64OutputStream(out, Base64.DEFAULT);
+        try {
+            out2.close();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
 }
diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/util/TimestampedValueTest.java
new file mode 100644
index 0000000..03b4abd
--- /dev/null
+++ b/core/tests/coretests/src/android/util/TimestampedValueTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class TimestampedValueTest {
+
+    @Test
+    public void testEqualsAndHashcode() {
+        TimestampedValue<String> one1000one = new TimestampedValue<>(1000, "one");
+        assertEqualsAndHashCode(one1000one, one1000one);
+
+        TimestampedValue<String> one1000two = new TimestampedValue<>(1000, "one");
+        assertEqualsAndHashCode(one1000one, one1000two);
+
+        TimestampedValue<String> two1000 = new TimestampedValue<>(1000, "two");
+        assertNotEquals(one1000one, two1000);
+
+        TimestampedValue<String> one2000 = new TimestampedValue<>(2000, "one");
+        assertNotEquals(one1000one, one2000);
+    }
+
+    private static void assertEqualsAndHashCode(Object one, Object two) {
+        assertEquals(one, two);
+        assertEquals(one.hashCode(), two.hashCode());
+    }
+
+    @Test
+    public void testParceling() {
+        TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
+        Parcel parcel = Parcel.obtain();
+        try {
+            TimestampedValue.writeToParcel(parcel, stringValue);
+
+            parcel.setDataPosition(0);
+
+            TimestampedValue<String> stringValueCopy =
+                    TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
+            assertEquals(stringValue, stringValueCopy);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    @Test
+    public void testParceling_valueClassOk() {
+        TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
+        Parcel parcel = Parcel.obtain();
+        try {
+            TimestampedValue.writeToParcel(parcel, stringValue);
+
+            parcel.setDataPosition(0);
+
+            TimestampedValue<Object> stringValueCopy =
+                    TimestampedValue.readFromParcel(parcel, null /* classLoader */, Object.class);
+            assertEquals(stringValue, stringValueCopy);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    @Test
+    public void testParceling_valueClassIncompatible() {
+        TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
+        Parcel parcel = Parcel.obtain();
+        try {
+            TimestampedValue.writeToParcel(parcel, stringValue);
+
+            parcel.setDataPosition(0);
+
+            TimestampedValue.readFromParcel(parcel, null /* classLoader */, Double.class);
+            fail();
+        } catch (RuntimeException expected) {
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    @Test
+    public void testParceling_nullValue() {
+        TimestampedValue<String> nullValue = new TimestampedValue<>(1000, null);
+        Parcel parcel = Parcel.obtain();
+        try {
+            TimestampedValue.writeToParcel(parcel, nullValue);
+
+            parcel.setDataPosition(0);
+
+            TimestampedValue<Object> nullValueCopy =
+                    TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
+            assertEquals(nullValue, nullValueCopy);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    @Test
+    public void testReferenceTimeDifference() {
+        TimestampedValue<Long> value1 = new TimestampedValue<>(1000, 123L);
+        assertEquals(0, TimestampedValue.referenceTimeDifference(value1, value1));
+
+        TimestampedValue<Long> value2 = new TimestampedValue<>(1, 321L);
+        assertEquals(999, TimestampedValue.referenceTimeDifference(value1, value2));
+        assertEquals(-999, TimestampedValue.referenceTimeDifference(value2, value1));
+    }
+}
diff --git a/core/tests/coretests/src/android/view/KeyEventTest.java b/core/tests/coretests/src/android/view/KeyEventTest.java
index aabf816..b9d95e5 100644
--- a/core/tests/coretests/src/android/view/KeyEventTest.java
+++ b/core/tests/coretests/src/android/view/KeyEventTest.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import static org.junit.Assert.assertEquals;
 
 import android.support.test.filters.SmallTest;
@@ -28,12 +30,71 @@
 @RunWith(AndroidJUnit4.class)
 public class KeyEventTest {
 
+    private static final int DOWN_TIME = 50;
+    private static final long EVENT_TIME = 100;
+    private static final int ACTION = KeyEvent.ACTION_DOWN;
+    private static final int KEYCODE = KeyEvent.KEYCODE_0;
+    private static final int REPEAT = 0;
+    private static final int METASTATE = 0;
+    private static final int DEVICE_ID = 0;
+    private static final int SCAN_CODE = 0;
+    private static final int FLAGS = 0;
+    private static final int SOURCE = InputDevice.SOURCE_KEYBOARD;
+    private static final String CHARACTERS = null;
+
     @Test
     public void testObtain() {
-        KeyEvent keyEvent = KeyEvent.obtain(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0,
-                0, 0, 0, 0, 0, InputDevice.SOURCE_KEYBOARD, null);
-        assertEquals(KeyEvent.ACTION_DOWN, keyEvent.getAction());
-        assertEquals(KeyEvent.KEYCODE_0, keyEvent.getKeyCode());
-        assertEquals(InputDevice.SOURCE_KEYBOARD, keyEvent.getSource());
+        KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
+                METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, CHARACTERS);
+        assertEquals(DOWN_TIME, keyEvent.getDownTime());
+        assertEquals(EVENT_TIME, keyEvent.getEventTime());
+        assertEquals(ACTION, keyEvent.getAction());
+        assertEquals(KEYCODE, keyEvent.getKeyCode());
+        assertEquals(REPEAT, keyEvent.getRepeatCount());
+        assertEquals(METASTATE, keyEvent.getMetaState());
+        assertEquals(DEVICE_ID, keyEvent.getDeviceId());
+        assertEquals(SCAN_CODE, keyEvent.getScanCode());
+        assertEquals(FLAGS, keyEvent.getFlags());
+        assertEquals(SOURCE, keyEvent.getSource());
+        assertEquals(INVALID_DISPLAY, keyEvent.getDisplayId());
+        assertEquals(CHARACTERS, keyEvent.getCharacters());
+    }
+
+    @Test
+    public void testObtainFromKeyEvent() {
+        KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
+                METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, CHARACTERS);
+        KeyEvent keyEvent2 = KeyEvent.obtain(keyEvent);
+        assertEquals(keyEvent.getDownTime(), keyEvent2.getDownTime());
+        assertEquals(keyEvent.getEventTime(), keyEvent2.getEventTime());
+        assertEquals(keyEvent.getAction(), keyEvent2.getAction());
+        assertEquals(keyEvent.getKeyCode(), keyEvent2.getKeyCode());
+        assertEquals(keyEvent.getRepeatCount(), keyEvent2.getRepeatCount());
+        assertEquals(keyEvent.getMetaState(), keyEvent2.getMetaState());
+        assertEquals(keyEvent.getDeviceId(), keyEvent2.getDeviceId());
+        assertEquals(keyEvent.getScanCode(), keyEvent2.getScanCode());
+        assertEquals(keyEvent.getFlags(), keyEvent2.getFlags());
+        assertEquals(keyEvent.getSource(), keyEvent2.getSource());
+        assertEquals(keyEvent.getDisplayId(), keyEvent2.getDisplayId());
+        assertEquals(keyEvent.getCharacters(), keyEvent2.getCharacters());
+    }
+
+    @Test
+    public void testObtainWithDisplayId() {
+        final int displayId = 5;
+        KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
+                METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, displayId, CHARACTERS);
+        assertEquals(DOWN_TIME, keyEvent.getDownTime());
+        assertEquals(EVENT_TIME, keyEvent.getEventTime());
+        assertEquals(ACTION, keyEvent.getAction());
+        assertEquals(KEYCODE, keyEvent.getKeyCode());
+        assertEquals(REPEAT, keyEvent.getRepeatCount());
+        assertEquals(METASTATE, keyEvent.getMetaState());
+        assertEquals(DEVICE_ID, keyEvent.getDeviceId());
+        assertEquals(SCAN_CODE, keyEvent.getScanCode());
+        assertEquals(FLAGS, keyEvent.getFlags());
+        assertEquals(SOURCE, keyEvent.getSource());
+        assertEquals(displayId, keyEvent.getDisplayId());
+        assertEquals(CHARACTERS, keyEvent.getCharacters());
     }
 }
diff --git a/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
new file mode 100644
index 0000000..93ad41f0
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
@@ -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.
+ */
+
+package android.view;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.widget.FrameLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ViewGroupTransientViewTest {
+
+    @Rule
+    public ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class);
+
+    private FrameLayout mBasePanel;
+    private ViewGroup mTestViewGroup;
+    private TestView mTestView;
+
+    @Before
+    public void setUp() {
+        final Activity activity = mActivityRule.getActivity();
+        mBasePanel = new FrameLayout(activity);
+        mTestViewGroup = new FrameLayout(activity);
+        mTestView = new TestView(activity);
+        activity.runOnUiThread(() -> activity.setContentView(mBasePanel));
+    }
+
+    @UiThreadTest
+    @Test
+    public void addAndRemove_inNonAttachedViewGroup_shouldNotAttachAndDetach() {
+        mTestViewGroup.addTransientView(mTestView, 0);
+        assertEquals(0, mTestView.mAttachedCount);
+
+        mTestViewGroup.removeTransientView(mTestView);
+        assertEquals(0, mTestView.mDetachedCount);
+    }
+
+    @UiThreadTest
+    @Test
+    public void addAndRemove_inAttachedViewGroup_shouldAttachAndDetachOnce() {
+        mBasePanel.addView(mTestViewGroup);
+        mTestViewGroup.addTransientView(mTestView, 0);
+        assertEquals(mTestView, mTestViewGroup.getTransientView(0));
+        assertEquals(1, mTestViewGroup.getTransientViewCount());
+        assertEquals(1, mTestView.mAttachedCount);
+
+        mBasePanel.removeView(mTestViewGroup);
+        mTestViewGroup.removeTransientView(mTestView);
+        assertEquals(null, mTestViewGroup.getTransientView(0));
+        assertEquals(0, mTestViewGroup.getTransientViewCount());
+        assertEquals(1, mTestView.mDetachedCount);
+    }
+
+    @UiThreadTest
+    @Test
+    public void addRemoveAdd_noException() {
+        mBasePanel.addView(mTestViewGroup);
+        mTestViewGroup.addTransientView(mTestView, 1);
+        mTestViewGroup.removeTransientView(mTestView);
+        mTestViewGroup.addTransientView(mTestView, 2);
+    }
+
+    @UiThreadTest
+    @Test
+    public void reAddBeforeRemove_shouldThrowException() {
+        mTestViewGroup.addView(mTestView);
+
+        try {
+            mTestViewGroup.addTransientView(mTestView, 0);
+            fail("Not allow to add as transient view before removing it");
+        } catch (IllegalStateException e) {
+            // Expected
+        }
+
+        mTestViewGroup.removeView(mTestView);
+        mTestViewGroup.addTransientView(mTestView, 0);
+        try {
+            mTestViewGroup.addTransientView(mTestView, 1);
+            fail("Not allow to add the same transient view again");
+        } catch (IllegalStateException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void drawTransientView() throws Exception {
+        // For view can be drawn if keyguard is active.
+        mActivityRule.getActivity().setShowWhenLocked(true);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        mTestView.mOnDraw = () -> latch.countDown();
+
+        mActivityRule.getActivity().runOnUiThread(() -> {
+            mBasePanel.addView(mTestViewGroup);
+            mTestViewGroup.addTransientView(mTestView, 0);
+        });
+
+        if (!latch.await(3, TimeUnit.SECONDS)) {
+            fail("Transient view does not draw");
+        }
+    }
+
+    private static class TestView extends View {
+        int mAttachedCount;
+        int mDetachedCount;
+        Runnable mOnDraw;
+
+        TestView(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected void onAttachedToWindow() {
+            super.onAttachedToWindow();
+            mAttachedCount++;
+        }
+
+        @Override
+        protected void onDetachedFromWindow() {
+            super.onDetachedFromWindow();
+            mDetachedCount++;
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            if (mOnDraw != null) {
+                mOnDraw.run();
+                mOnDraw = null;
+            }
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index 4de8155..ec80d20 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -300,6 +300,26 @@
     }
 
     @Test
+    public void subTreeChangeEventFromUncachedNode_clearsNodeInCache() {
+        AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
+        long id = nodeInfo.getSourceNodeId();
+        mAccessibilityCache.add(nodeInfo);
+        nodeInfo.recycle();
+
+        AccessibilityEvent event = AccessibilityEvent
+                .obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+        event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
+
+        mAccessibilityCache.onAccessibilityEvent(event);
+        AccessibilityNodeInfo shouldBeNull = mAccessibilityCache.getNode(WINDOW_ID_1, id);
+        if (shouldBeNull != null) {
+            shouldBeNull.recycle();
+        }
+        assertNull(shouldBeNull);
+    }
+
+    @Test
     public void scrollEvent_clearsNodeAndChild() {
         AccessibilityEvent event = AccessibilityEvent
                 .obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
@@ -523,6 +543,27 @@
         }
     }
 
+    @Test
+    public void addA11yFocusNodeBeforeFocusClearedEvent_previousA11yFocusNodeGetsRefreshed() {
+        AccessibilityNodeInfo nodeInfo1 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
+        nodeInfo1.setAccessibilityFocused(true);
+        mAccessibilityCache.add(nodeInfo1);
+        AccessibilityNodeInfo nodeInfo2 = getNodeWithA11yAndWindowId(OTHER_VIEW_ID, WINDOW_ID_1);
+        nodeInfo2.setAccessibilityFocused(true);
+        mAccessibilityCache.add(nodeInfo2);
+        AccessibilityEvent event = AccessibilityEvent.obtain(
+                AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+        event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1));
+        mAccessibilityCache.onAccessibilityEvent(event);
+        event.recycle();
+        try {
+            verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo1, true);
+        } finally {
+            nodeInfo1.recycle();
+            nodeInfo2.recycle();
+        }
+    }
+
     private void assertNodeIsRefreshedWithEventType(int eventType, int contentChangeTypes) {
         AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
         mAccessibilityCache.add(nodeInfo);
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 44b1f08..0dd7685 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -119,6 +119,10 @@
         return false;
     }
 
+    public int getSoftKeyboardShowMode() {
+        return 0;
+    }
+
     public void setSoftKeyboardCallbackEnabled(boolean enabled) {}
 
     public boolean isAccessibilityButtonAvailable() {
diff --git a/core/tests/coretests/src/android/widget/DateTimeViewTest.java b/core/tests/coretests/src/android/widget/DateTimeViewTest.java
index fbf0d36..40a6b7a 100644
--- a/core/tests/coretests/src/android/widget/DateTimeViewTest.java
+++ b/core/tests/coretests/src/android/widget/DateTimeViewTest.java
@@ -31,11 +31,25 @@
     @UiThreadTest
     @Test
     public void additionalOnDetachedFromWindow_noException() {
-        final DateTimeView dateTimeView = new DateTimeView(InstrumentationRegistry.getContext());
-        dateTimeView.onAttachedToWindow();
-        dateTimeView.onAttachedToWindow();
+        final TestDateTimeView dateTimeView = new TestDateTimeView();
+        dateTimeView.attachedToWindow();
+        dateTimeView.detachedFromWindow();
         // Even there is an additional detach (abnormal), DateTimeView should not unregister
         // receiver again that raises "java.lang.IllegalArgumentException: Receiver not registered".
-        dateTimeView.onDetachedFromWindow();
+        dateTimeView.detachedFromWindow();
+    }
+
+    private static class TestDateTimeView extends DateTimeView {
+        TestDateTimeView() {
+            super(InstrumentationRegistry.getContext());
+        }
+
+        void attachedToWindow() {
+            super.onAttachedToWindow();
+        }
+
+        void detachedFromWindow() {
+            super.onDetachedFromWindow();
+        }
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index b18fa74..c165b6b 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -16,33 +16,6 @@
 
 package com.android.internal.app;
 
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
-
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
@@ -51,11 +24,42 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
+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.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
 @RunWith(AndroidJUnit4.class)
 public class IntentForwarderActivityTest {
+
     private static final ComponentName FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME =
             new ComponentName(
                     "android",
@@ -77,22 +81,26 @@
 
     private static IntentForwarderActivity.Injector sInjector;
     private static ComponentName sComponentName;
+    private static String sActivityName;
+    private static String sPackageName;
 
     @Mock private IPackageManager mIPm;
     @Mock private PackageManager mPm;
     @Mock private UserManager mUserManager;
+    @Mock private ApplicationInfo mApplicationInfo;
 
     @Rule
     public ActivityTestRule<IntentForwarderWrapperActivity> mActivityRule =
             new ActivityTestRule<>(IntentForwarderWrapperActivity.class, true, false);
 
     private Context mContext;
+    public static final String PHONE_NUMBER = "123-456-789";
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mContext = InstrumentationRegistry.getTargetContext();
-        sInjector = new TestInjector();
+        sInjector = spy(new TestInjector());
     }
 
     @Test
@@ -252,6 +260,149 @@
         assertEquals(MANAGED_PROFILE_INFO.id, activity.mUserIdActivityLaunchedIn);
     }
 
+    @Test
+    public void shouldSkipDisclosure_notWhitelisted() throws RemoteException {
+        setupShouldSkipDisclosureTest();
+        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
+            .setAction(Intent.ACTION_SEND)
+            .setType(TYPE_PLAIN_TEXT);
+
+        mActivityRule.launchActivity(intent);
+
+        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+        verify(sInjector).showToast(anyInt(), anyInt());
+    }
+
+    @Test
+    public void shouldSkipDisclosure_withResolverActivity() throws RemoteException {
+        setupShouldSkipDisclosureTest();
+        sActivityName = ResolverActivity.class.getName();
+        sPackageName = "android";
+        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
+            .setAction(Intent.ACTION_SEND)
+            .setType(TYPE_PLAIN_TEXT);
+
+        mActivityRule.launchActivity(intent);
+
+        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyInt(), anyInt());
+    }
+
+    @Test
+    public void shouldSkipDisclosure_callIntent_call() throws RemoteException {
+        setupShouldSkipDisclosureTest();
+        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
+            .setAction(Intent.ACTION_CALL);
+
+        mActivityRule.launchActivity(intent);
+
+        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyInt(), anyInt());
+    }
+
+    @Test
+    public void shouldSkipDisclosure_callIntent_dial() throws RemoteException {
+        setupShouldSkipDisclosureTest();
+        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
+            .setAction(Intent.ACTION_DIAL);
+
+        mActivityRule.launchActivity(intent);
+
+        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyInt(), anyInt());
+    }
+
+    @Test
+    public void shouldSkipDisclosure_callIntent_notCallOrDial() throws RemoteException {
+        setupShouldSkipDisclosureTest();
+        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
+            .setAction(Intent.ACTION_ALARM_CHANGED);
+
+        mActivityRule.launchActivity(intent);
+
+        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+        verify(sInjector).showToast(anyInt(), anyInt());
+    }
+
+    @Test
+    public void shouldSkipDisclosure_textMessageIntent_sms() throws RemoteException {
+        setupShouldSkipDisclosureTest();
+        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
+            .setAction(Intent.ACTION_SENDTO)
+            .setData(Uri.fromParts("sms", PHONE_NUMBER, null));
+
+        mActivityRule.launchActivity(intent);
+
+        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyInt(), anyInt());
+    }
+
+    @Test
+    public void shouldSkipDisclosure_textMessageIntent_smsto() throws RemoteException {
+        setupShouldSkipDisclosureTest();
+        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
+            .setAction(Intent.ACTION_SENDTO)
+            .setData(Uri.fromParts("smsto", PHONE_NUMBER, null));
+
+        mActivityRule.launchActivity(intent);
+
+        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyInt(), anyInt());
+    }
+
+    @Test
+    public void shouldSkipDisclosure_textMessageIntent_mms() throws RemoteException {
+        setupShouldSkipDisclosureTest();
+        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
+            .setAction(Intent.ACTION_SENDTO)
+            .setData(Uri.fromParts("mms", PHONE_NUMBER, null));
+
+        mActivityRule.launchActivity(intent);
+
+        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyInt(), anyInt());
+    }
+
+    @Test
+    public void shouldSkipDisclosure_textMessageIntent_mmsto() throws RemoteException {
+        setupShouldSkipDisclosureTest();
+        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
+            .setAction(Intent.ACTION_SENDTO)
+            .setData(Uri.fromParts("mmsto", PHONE_NUMBER, null));
+
+        mActivityRule.launchActivity(intent);
+
+        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyInt(), anyInt());
+    }
+
+    @Test
+    public void shouldSkipDisclosure_textMessageIntent_invalidUri() throws RemoteException {
+        setupShouldSkipDisclosureTest();
+        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
+            .setAction(Intent.ACTION_SENDTO)
+            .setData(Uri.fromParts("invalid", PHONE_NUMBER, null));
+
+        mActivityRule.launchActivity(intent);
+
+        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+        verify(sInjector).showToast(anyInt(), anyInt());
+    }
+
+    private void setupShouldSkipDisclosureTest() throws RemoteException {
+        sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
+        sActivityName = "MyTestActivity";
+        sPackageName = "test.package.name";
+        when(mApplicationInfo.isSystemApp()).thenReturn(true);
+        // Managed profile exists.
+        List<UserInfo> profiles = new ArrayList<>();
+        profiles.add(CURRENT_USER_INFO);
+        profiles.add(MANAGED_PROFILE_INFO);
+        when(mUserManager.getProfiles(anyInt())).thenReturn(profiles);
+        // Intent can be forwarded.
+        when(mIPm.canForwardTo(
+            any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true);
+    }
 
     public static class IntentForwarderWrapperActivity extends IntentForwarderActivity {
         private Intent mStartActivityIntent;
@@ -276,7 +427,7 @@
         }
     }
 
-    class TestInjector implements IntentForwarderActivity.Injector {
+    public class TestInjector implements IntentForwarderActivity.Injector {
 
         @Override
         public IPackageManager getIPackageManager() {
@@ -292,5 +443,21 @@
         public PackageManager getPackageManager() {
             return mPm;
         }
+
+        @Override
+        public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
+            ActivityInfo activityInfo = new ActivityInfo();
+            activityInfo.packageName = sPackageName;
+            activityInfo.name = sActivityName;
+            activityInfo.applicationInfo = mApplicationInfo;
+
+            ResolveInfo resolveInfo = new ResolveInfo();
+            resolveInfo.activityInfo = activityInfo;
+
+            return resolveInfo;
+        }
+
+        @Override
+        public void showToast(int messageId, int duration) {}
     }
 }
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
new file mode 100644
index 0000000..10a3189
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.os.Parcel;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Test BatteryStatsHistory.
+ */
+@RunWith(AndroidJUnit4.class)
+public class BatteryStatsHistoryTest {
+    private static final int MAX_HISTORY_FILES = 32;
+    private final BatteryStatsImpl mBatteryStatsImpl = new MockBatteryStatsImpl();
+    private final Parcel mHistoryBuffer = Parcel.obtain();
+    private File mSystemDir;
+    private File mHistoryDir;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        Context context = InstrumentationRegistry.getContext();
+        mSystemDir = context.getDataDir();
+        mHistoryDir = new File(mSystemDir, BatteryStatsHistory.HISTORY_DIR);
+        mHistoryDir.delete();
+    }
+
+    @Test
+    public void testConstruct() {
+        BatteryStatsHistory history =
+                new BatteryStatsHistory(mBatteryStatsImpl, mSystemDir, mHistoryBuffer);
+        verifyFileNumbers(history, Arrays.asList(0));
+        verifyActiveFile(history, "0.bin");
+    }
+
+    @Test
+    public void testCreateNextFile() {
+        BatteryStatsHistory history =
+                new BatteryStatsHistory(mBatteryStatsImpl, mSystemDir, mHistoryBuffer);
+
+        List<Integer> fileList = new ArrayList<>();
+        fileList.add(0);
+
+        // create file 1 to 31.
+        for (int i = 1; i < MAX_HISTORY_FILES; i++) {
+            fileList.add(i);
+            history.createNextFile();
+            verifyFileNumbers(history, fileList);
+            verifyActiveFile(history, i + ".bin");
+        }
+
+        // create file 32
+        history.createNextFile();
+        fileList.add(32);
+        fileList.remove(0);
+        // verify file 0 is deleted.
+        verifyFileDeleted("0.bin");
+        verifyFileNumbers(history, fileList);
+        verifyActiveFile(history, "32.bin");
+
+        // create file 33
+        history.createNextFile();
+        // verify file 1 is deleted
+        fileList.add(33);
+        fileList.remove(0);
+        verifyFileDeleted("1.bin");
+        verifyFileNumbers(history, fileList);
+        verifyActiveFile(history, "33.bin");
+
+        assertEquals(0, history.getHistoryUsedSize());
+
+        // create a new BatteryStatsHistory object, it will pick up existing history files.
+        BatteryStatsHistory history2 =
+                new BatteryStatsHistory(mBatteryStatsImpl, mSystemDir, mHistoryBuffer);
+        // verify construct can pick up all files from file system.
+        verifyFileNumbers(history2, fileList);
+        verifyActiveFile(history2, "33.bin");
+
+        history2.resetAllFiles();
+        // verify all existing files are deleted.
+        for (int i = 2; i < 33; ++i) {
+            verifyFileDeleted(i + ".bin");
+        }
+
+        // verify file 0 is created
+        verifyFileNumbers(history2, Arrays.asList(0));
+        verifyActiveFile(history2, "0.bin");
+
+        // create file 1.
+        history2.createNextFile();
+        verifyFileNumbers(history2, Arrays.asList(0, 1));
+        verifyActiveFile(history2, "1.bin");
+    }
+
+    private void verifyActiveFile(BatteryStatsHistory history, String file) {
+        final File expectedFile = new File(mHistoryDir, file);
+        assertEquals(expectedFile.getPath(), history.getActiveFile().getBaseFile().getPath());
+        assertTrue(expectedFile.exists());
+    }
+
+    private void verifyFileNumbers(BatteryStatsHistory history, List<Integer> fileList) {
+        assertEquals(fileList.size(), history.getFilesNumbers().size());
+        for (int i = 0; i < fileList.size(); i++) {
+            assertEquals(fileList.get(i), history.getFilesNumbers().get(i));
+            final File expectedFile =
+                    new File(mHistoryDir, fileList.get(i) + ".bin");
+            assertTrue(expectedFile.exists());
+        }
+    }
+
+    private void verifyFileDeleted(String file) {
+        assertFalse(new File(mHistoryDir, file).exists());
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 94492ba..b798042 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -26,6 +26,7 @@
         BatteryStatsDualTimerTest.class,
         BatteryStatsDurationTimerTest.class,
         BatteryStatsHelperTest.class,
+        BatteryStatsHistoryTest.class,
         BatteryStatsImplTest.class,
         BatteryStatsNoteTest.class,
         BatteryStatsSamplingTimerTest.class,
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 b7fef13..ace6b2d 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -16,20 +16,32 @@
 
 package com.android.internal.os;
 
+import static org.junit.Assert.assertEquals;
+
+import android.content.Intent;
+import android.os.BatteryManager;
 import android.os.Binder;
+import android.os.OsProtoEnums;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.ArrayMap;
 import android.util.SparseArray;
 
+import com.android.internal.os.BinderInternal.CallSession;
+
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Arrays;
-import java.util.List;
 
-import static org.junit.Assert.assertEquals;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Random;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -41,9 +53,12 @@
 
     @Test
     public void testDetailedOff() {
-        TestBinderCallsStats bcs = new TestBinderCallsStats(false);
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(false);
+        bcs.setSamplingInterval(5);
+
         Binder binder = new Binder();
-        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+        CallSession callSession = bcs.callStarted(binder, 1);
         bcs.time += 10;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
 
@@ -51,51 +66,40 @@
         assertEquals(1, uidEntries.size());
         BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
         Assert.assertNotNull(uidEntry);
+        List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
         assertEquals(1, uidEntry.callCount);
+        assertEquals(1, uidEntry.recordedCallCount);
         assertEquals(10, uidEntry.cpuTimeMicros);
-        assertEquals("Detailed tracking off - no entries should be returned",
-                0, uidEntry.getCallStatsList().size());
+        assertEquals(binder.getClass(), callStatsList.get(0).binderClass);
+        assertEquals(1, callStatsList.get(0).transactionCode);
 
-        BinderCallsStats.UidEntry sampledEntries = bcs.getSampledEntries();
-        List<BinderCallsStats.CallStat> sampledCallStatsList = sampledEntries.getCallStatsList();
-        assertEquals(1, sampledCallStatsList.size());
-
-
-        assertEquals(1, sampledCallStatsList.get(0).callCount);
-        assertEquals(10, sampledCallStatsList.get(0).cpuTimeMicros);
-        assertEquals(binder.getClass().getName(), sampledCallStatsList.get(0).className);
-        assertEquals(1, sampledCallStatsList.get(0).msg);
-
+        // CPU usage is sampled, should not be tracked here.
         callSession = bcs.callStarted(binder, 1);
         bcs.time += 20;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
-
-        uidEntry = bcs.getUidEntries().get(TEST_UID);
         assertEquals(2, uidEntry.callCount);
-        // When sampling is enabled, cpu time is only measured for the first transaction in the
-        // sampling interval, for others an average duration of previous transactions is used as
-        // approximation
-        assertEquals(20, uidEntry.cpuTimeMicros);
-        sampledCallStatsList = sampledEntries.getCallStatsList();
-        assertEquals(1, sampledCallStatsList.size());
+        assertEquals(1, uidEntry.recordedCallCount);
+        assertEquals(10, uidEntry.cpuTimeMicros);
+        assertEquals(1, callStatsList.size());
 
         callSession = bcs.callStarted(binder, 2);
         bcs.time += 50;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
         uidEntry = bcs.getUidEntries().get(TEST_UID);
         assertEquals(3, uidEntry.callCount);
-
-        // This is the first transaction of a new type, so the real CPU time will be measured
-        assertEquals(70, uidEntry.cpuTimeMicros);
-        sampledCallStatsList = sampledEntries.getCallStatsList();
-        assertEquals(2, sampledCallStatsList.size());
+        assertEquals(1, uidEntry.recordedCallCount);
+        // Still sampled even for another API.
+        callStatsList = new ArrayList(uidEntry.getCallStatsList());
+        assertEquals(1, callStatsList.size());
     }
 
     @Test
     public void testDetailedOn() {
-        TestBinderCallsStats bcs = new TestBinderCallsStats(true);
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+
         Binder binder = new Binder();
-        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+        CallSession callSession = bcs.callStarted(binder, 1);
         bcs.time += 10;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
 
@@ -105,17 +109,13 @@
         Assert.assertNotNull(uidEntry);
         assertEquals(1, uidEntry.callCount);
         assertEquals(10, uidEntry.cpuTimeMicros);
-        assertEquals(1, uidEntry.getCallStatsList().size());
+        assertEquals(1, new ArrayList(uidEntry.getCallStatsList()).size());
 
-        BinderCallsStats.UidEntry sampledEntries = bcs.getSampledEntries();
-        assertEquals("Sampling is not used when detailed tracking on",
-                0, bcs.getSampledEntries().getCallStatsList().size());
-
-        List<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
+        List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
         assertEquals(1, callStatsList.get(0).callCount);
         assertEquals(10, callStatsList.get(0).cpuTimeMicros);
-        assertEquals(binder.getClass().getName(), callStatsList.get(0).className);
-        assertEquals(1, callStatsList.get(0).msg);
+        assertEquals(binder.getClass(), callStatsList.get(0).binderClass);
+        assertEquals(1, callStatsList.get(0).transactionCode);
 
         callSession = bcs.callStarted(binder, 1);
         bcs.time += 20;
@@ -124,7 +124,7 @@
         uidEntry = bcs.getUidEntries().get(TEST_UID);
         assertEquals(2, uidEntry.callCount);
         assertEquals(30, uidEntry.cpuTimeMicros);
-        callStatsList = uidEntry.getCallStatsList();
+        callStatsList = new ArrayList(uidEntry.getCallStatsList());
         assertEquals(1, callStatsList.size());
 
         callSession = bcs.callStarted(binder, 2);
@@ -135,26 +135,228 @@
 
         // This is the first transaction of a new type, so the real CPU time will be measured
         assertEquals(80, uidEntry.cpuTimeMicros);
-        callStatsList = uidEntry.getCallStatsList();
+        callStatsList = new ArrayList(uidEntry.getCallStatsList());
         assertEquals(2, callStatsList.size());
     }
 
     @Test
-    public void testParcelSize() {
-        TestBinderCallsStats bcs = new TestBinderCallsStats(true);
+    public void testEnableInBetweenCall() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
         Binder binder = new Binder();
-        BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(null, REQUEST_SIZE, REPLY_SIZE);
+
+        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+        assertEquals(0, uidEntries.size());
+    }
+
+    @Test
+    public void testInBetweenCallWhenExceptionThrown() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        Binder binder = new Binder();
+        bcs.callThrewException(null, new IllegalStateException());
+        bcs.callEnded(null, REQUEST_SIZE, REPLY_SIZE);
+
+        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+        assertEquals(0, uidEntries.size());
+    }
+
+    @Test
+    public void testSampling() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(false);
+        bcs.setSamplingInterval(2);
+
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        callSession = bcs.callStarted(binder, 1);
+        bcs.time += 1000;  // shoud be ignored.
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        callSession = bcs.callStarted(binder, 1);
+        bcs.time += 50;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+        assertEquals(1, uidEntries.size());
+        BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+        Assert.assertNotNull(uidEntry);
+        assertEquals(3, uidEntry.callCount);
+        assertEquals(60 /* 10 + 50 */, uidEntry.cpuTimeMicros);
+
+        List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
+        assertEquals(1, callStatsList.size());
+        BinderCallsStats.CallStat callStats = callStatsList.get(0);
+        assertEquals(3, callStats.callCount);
+        assertEquals(2, callStats.recordedCallCount);
+        assertEquals(60, callStats.cpuTimeMicros);
+        assertEquals(50, callStats.maxCpuTimeMicros);
+        assertEquals(0, callStats.maxRequestSizeBytes);
+        assertEquals(0, callStats.maxReplySizeBytes);
+    }
+
+    @Test
+    public void testSamplingWithDifferentApis() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(false);
+        bcs.setSamplingInterval(2);
+
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        callSession = bcs.callStarted(binder, 2 /* another method */);
+        bcs.time += 1000;  // shoud be ignored.
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+
+        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+        assertEquals(1, uidEntries.size());
+        BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+        assertEquals(2, uidEntry.callCount);
+        assertEquals(1, uidEntry.recordedCallCount);
+        assertEquals(10, uidEntry.cpuTimeMicros);
+
+        List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
+        assertEquals(1, callStatsList.size());
+
+        BinderCallsStats.CallStat callStats = callStatsList.get(0);
+        assertEquals(1, callStats.callCount);
+        assertEquals(1, callStats.recordedCallCount);
+        assertEquals(10, callStats.cpuTimeMicros);
+        assertEquals(10, callStats.maxCpuTimeMicros);
+    }
+
+    private static class BinderWithGetTransactionName extends Binder {
+        public static String getDefaultTransactionName(int code) {
+            return "resolved";
+        }
+    }
+
+    private static class AnotherBinderWithGetTransactionName extends Binder {
+        public static String getDefaultTransactionName(int code) {
+            return "foo" + code;
+        }
+    }
+
+    @Test
+    public void testTransactionCodeResolved() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Binder binder = new BinderWithGetTransactionName();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        List<BinderCallsStats.ExportedCallStat> callStatsList =
+                bcs.getExportedCallStats();
+        assertEquals("resolved", callStatsList.get(0).methodName);
+    }
+
+    @Test
+    public void testMultipleTransactionCodeResolved() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+
+        Binder binder = new AnotherBinderWithGetTransactionName();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        Binder binder2 = new BinderWithGetTransactionName();
+        callSession = bcs.callStarted(binder2, 1);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        callSession = bcs.callStarted(binder, 2);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        List<BinderCallsStats.ExportedCallStat> callStatsList =
+                bcs.getExportedCallStats();
+        assertEquals("foo1", callStatsList.get(0).methodName);
+        assertEquals(AnotherBinderWithGetTransactionName.class.getName(),
+                callStatsList.get(0).className);
+        assertEquals("foo2", callStatsList.get(1).methodName);
+        assertEquals(AnotherBinderWithGetTransactionName.class.getName(),
+                callStatsList.get(1).className);
+        assertEquals("resolved", callStatsList.get(2).methodName);
+        assertEquals(BinderWithGetTransactionName.class.getName(),
+                callStatsList.get(2).className);
+    }
+
+    @Test
+    public void testResolvingCodeDoesNotThrowWhenMethodNotPresent() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        List<BinderCallsStats.ExportedCallStat> callStatsList =
+                bcs.getExportedCallStats();
+        assertEquals("1", callStatsList.get(0).methodName);
+    }
+
+    @Test
+    public void testParcelSize() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
         bcs.time += 10;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
 
         List<BinderCallsStats.CallStat> callStatsList =
-                bcs.getUidEntries().get(TEST_UID).getCallStatsList();
+                new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
 
         assertEquals(REQUEST_SIZE, callStatsList.get(0).maxRequestSizeBytes);
         assertEquals(REPLY_SIZE, callStatsList.get(0).maxReplySizeBytes);
     }
 
     @Test
+    public void testMaxCpu() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.time += 50;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        callSession = bcs.callStarted(binder, 1);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        List<BinderCallsStats.CallStat> callStatsList =
+                new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
+
+        assertEquals(50, callStatsList.get(0).maxCpuTimeMicros);
+    }
+
+    @Test
+    public void testMaxLatency() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.elapsedTime += 5;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        callSession = bcs.callStarted(binder, 1);
+        bcs.elapsedTime += 1;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        List<BinderCallsStats.CallStat> callStatsList =
+                new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
+
+        assertEquals(5, callStatsList.get(0).maxLatencyMicros);
+    }
+
+    @Test
     public void testGetHighestValues() {
         List<Integer> list = Arrays.asList(1, 2, 3, 4);
         List<Integer> highestValues = BinderCallsStats
@@ -162,12 +364,222 @@
         assertEquals(Arrays.asList(4, 3, 2), highestValues);
     }
 
+    @Test
+    public void testExceptionCount() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callThrewException(callSession, new IllegalStateException());
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        callSession = bcs.callStarted(binder, 1);
+        bcs.callThrewException(callSession, new IllegalStateException());
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        callSession = bcs.callStarted(binder, 1);
+        bcs.callThrewException(callSession, new RuntimeException());
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        ArrayMap<String, Integer> expected = new ArrayMap<>();
+        expected.put("java.lang.IllegalStateException", 2);
+        expected.put("java.lang.RuntimeException", 1);
+        assertEquals(expected, bcs.getExceptionCounts());
+    }
+
+    @Test
+    public void testDataResetWhenInitialStateSet() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        bcs.setInitialState(true, true);
+
+        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+        assertEquals(0, uidEntries.size());
+    }
+
+    @Test
+    public void testScreenAndChargerInitialStates() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Binder binder = new Binder();
+        bcs.setInitialState(true /** screen iteractive */, false);
+
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        List<BinderCallsStats.CallStat> callStatsList =
+                new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
+        assertEquals(true, callStatsList.get(0).screenInteractive);
+    }
+
+    @Test
+    public void testNoDataCollectedOnCharger() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
+                .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
+        bcs.getBroadcastReceiver().onReceive(null, intent);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        assertEquals(0, bcs.getUidEntries().size());
+    }
+
+    @Test
+    public void testScreenOff() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_OFF));
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+        assertEquals(1, uidEntries.size());
+        BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+        Assert.assertNotNull(uidEntry);
+        List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
+        assertEquals(false, callStatsList.get(0).screenInteractive);
+    }
+
+    @Test
+    public void testScreenOn() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_ON));
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+        assertEquals(1, uidEntries.size());
+        BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+        Assert.assertNotNull(uidEntry);
+        List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
+        assertEquals(true, callStatsList.get(0).screenInteractive);
+    }
+
+    @Test
+    public void testOnCharger() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
+                .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
+        bcs.getBroadcastReceiver().onReceive(null, intent);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        assertEquals(0, bcs.getExportedCallStats().size());
+    }
+
+    @Test
+    public void testOnBattery() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
+                .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_NONE);
+        bcs.getBroadcastReceiver().onReceive(null, intent);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        assertEquals(1, bcs.getExportedCallStats().size());
+    }
+
+    @Test
+    public void testDumpDoesNotThrowException() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callThrewException(callSession, new IllegalStateException());
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        PrintWriter pw = new PrintWriter(new StringWriter());
+        bcs.dump(pw, new HashMap<>(), true);
+    }
+
+    @Test
+    public void testGetExportedStatsWhenDetailedTrackingDisabled() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(false);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        assertEquals(0, bcs.getExportedCallStats().size());
+    }
+
+    @Test
+    public void testGetExportedStatsWhenDetailedTrackingEnabled() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_ON));
+
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.time += 10;
+        bcs.elapsedTime += 20;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        assertEquals(1, bcs.getExportedCallStats().size());
+        BinderCallsStats.ExportedCallStat stat = bcs.getExportedCallStats().get(0);
+        assertEquals(TEST_UID, stat.uid);
+        assertEquals("android.os.Binder", stat.className);
+        assertEquals("1", stat.methodName);
+        assertEquals(true, stat.screenInteractive);
+        assertEquals(10, stat.cpuTimeMicros);
+        assertEquals(10, stat.maxCpuTimeMicros);
+        assertEquals(20, stat.latencyMicros);
+        assertEquals(20, stat.maxLatencyMicros);
+        assertEquals(1, stat.callCount);
+        assertEquals(1, stat.recordedCallCount);
+        assertEquals(REQUEST_SIZE, stat.maxRequestSizeBytes);
+        assertEquals(REPLY_SIZE, stat.maxReplySizeBytes);
+        assertEquals(0, stat.exceptionCount);
+    }
+
+    @Test
+    public void testGetExportedStatsWithoutCalls() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        Binder binder = new Binder();
+        assertEquals(0, bcs.getExportedCallStats().size());
+    }
+
+    @Test
+    public void testGetExportedExceptionsWithoutCalls() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        Binder binder = new Binder();
+        assertEquals(0, bcs.getExceptionCounts().size());
+    }
+
     static class TestBinderCallsStats extends BinderCallsStats {
         int callingUid = TEST_UID;
         long time = 1234;
+        long elapsedTime = 0;
 
-        TestBinderCallsStats(boolean detailedTracking) {
-            super(detailedTracking);
+        TestBinderCallsStats() {
+            // Make random generator not random.
+            super(new Injector() {
+                public Random getRandomGenerator() {
+                    return new Random() {
+                        int mCallCount = 0;
+
+                        public int nextInt() {
+                            return mCallCount++;
+                        }
+                    };
+                }
+            });
+            setSamplingInterval(1);
         }
 
         @Override
@@ -176,6 +588,11 @@
         }
 
         @Override
+        protected long getElapsedRealtimeMicro() {
+            return elapsedTime;
+        }
+
+        @Override
         protected int getCallingUid() {
             return callingUid;
         }
diff --git a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
index 45b19bc..a44b860 100644
--- a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
@@ -15,8 +15,11 @@
  */
 package com.android.internal.util;
 
+import static com.android.internal.util.DumpUtils.CRITICAL_SECTION_COMPONENTS;
 import static com.android.internal.util.DumpUtils.filterRecord;
 import static com.android.internal.util.DumpUtils.isNonPlatformPackage;
+import static com.android.internal.util.DumpUtils.isPlatformCriticalPackage;
+import static com.android.internal.util.DumpUtils.isPlatformNonCriticalPackage;
 import static com.android.internal.util.DumpUtils.isPlatformPackage;
 
 import android.content.ComponentName;
@@ -25,7 +28,7 @@
 
 /**
  * Run with:
- atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/DumpTest.java
+ atest FrameworksCoreTests:DumpUtilsTest
  */
 public class DumpUtilsTest extends TestCase {
 
@@ -89,6 +92,32 @@
         assertTrue(isNonPlatformPackage(wcn("com.google.def/abc")));
     }
 
+    public void testIsPlatformCriticalPackage() {
+        for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) {
+            assertTrue(isPlatformCriticalPackage(() -> componentName));
+            assertTrue(isPlatformPackage(componentName));
+        }
+        assertFalse(isPlatformCriticalPackage(wcn("com.google.p/abc")));
+        assertFalse(isPlatformCriticalPackage(wcn("com.android.def/abc")));
+        assertFalse(isPlatformCriticalPackage(wcn("com.android.abc")));
+        assertFalse(isPlatformCriticalPackage(wcn("com.android")));
+        assertFalse(isPlatformCriticalPackage(wcn(null)));
+        assertFalse(isPlatformCriticalPackage(null));
+    }
+
+    public void testIsPlatformNonCriticalPackage() {
+        for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) {
+            assertFalse(isPlatformNonCriticalPackage(() -> componentName));
+        }
+        assertTrue(isPlatformNonCriticalPackage(wcn("android/abc")));
+        assertTrue(isPlatformNonCriticalPackage(wcn("android.abc/abc")));
+        assertTrue(isPlatformNonCriticalPackage(wcn("com.android.def/abc")));
+
+        assertFalse(isPlatformNonCriticalPackage(wcn("com.google.def/abc")));
+        assertFalse(isPlatformNonCriticalPackage(wcn(null)));
+        assertFalse(isPlatformNonCriticalPackage(null));
+    }
+
     public void testFilterRecord() {
         assertFalse(filterRecord(null).test(wcn("com.google.p/abc")));
         assertFalse(filterRecord(null).test(wcn("com.android.p/abc")));
@@ -105,6 +134,19 @@
         assertFalse(filterRecord("all-non-platform").test(wcn("com.android.p/abc")));
         assertFalse(filterRecord("all-non-platform").test(wcn(null)));
 
+        for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) {
+            assertTrue(filterRecord("all-platform-critical").test((() -> componentName)));
+            assertFalse(filterRecord("all-platform-non-critical").test((() -> componentName)));
+            assertTrue(filterRecord("all-platform").test((() -> componentName)));
+        }
+        assertFalse(filterRecord("all-platform-critical").test(wcn("com.google.p/abc")));
+        assertFalse(filterRecord("all-platform-critical").test(wcn("com.android.p/abc")));
+        assertFalse(filterRecord("all-platform-critical").test(wcn(null)));
+
+        assertTrue(filterRecord("all-platform-non-critical").test(wcn("com.android.p/abc")));
+        assertFalse(filterRecord("all-platform-non-critical").test(wcn("com.google.p/abc")));
+        assertFalse(filterRecord("all-platform-non-critical").test(wcn(null)));
+
         // Partial string match.
         assertTrue(filterRecord("abc").test(wcn("com.google.p/.abc")));
         assertFalse(filterRecord("abc").test(wcn("com.google.p/.def")));
diff --git a/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
index 951e87a..359bd5e 100644
--- a/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
@@ -25,4 +25,7 @@
         assertEquals("ABCDEF", HexDump.toHexString(
                 new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef }, true));
     }
+    public void testNullArray() {
+        assertEquals("(null)", HexDump.toHexString(null));
+    }
 }
diff --git a/core/tests/hdmitests/Android.mk b/core/tests/hdmitests/Android.mk
new file mode 100644
index 0000000..e0d2c09
--- /dev/null
+++ b/core/tests/hdmitests/Android.mk
@@ -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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# Include all test java files
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test frameworks-base-testutils
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_PACKAGE_NAME := HdmiCecTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hdmitests/AndroidManifest.xml b/core/tests/hdmitests/AndroidManifest.xml
new file mode 100644
index 0000000..1460b41
--- /dev/null
+++ b/core/tests/hdmitests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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="android.hardware.hdmi"
+    android:sharedUserId="android.uid.system" >
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.hardware.hdmi"
+        android:label="HDMI CEC Tests"/>
+
+</manifest>
\ No newline at end of file
diff --git a/core/tests/hdmitests/AndroidTest.xml b/core/tests/hdmitests/AndroidTest.xml
new file mode 100644
index 0000000..7ef672d
--- /dev/null
+++ b/core/tests/hdmitests/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?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 HDMI CEC Tests.">
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-suite-tag" value="apct-instrumentation"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="HdmiCecTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="HdmiTests"/>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.hardware.hdmi" />
+        <option name="hidden-api-checks" value="false"/>
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
new file mode 100644
index 0000000..7b76a08
--- /dev/null
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.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 android.hardware.hdmi;
+
+import android.os.Handler;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import android.util.Log;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link HdmiAudioSystemClient}
+ */
+@RunWith(JUnit4.class)
+@SmallTest
+public class HdmiAudioSystemClientTest {
+    private static final String TAG = "HdmiAudioSystemClientTe";
+
+    private HdmiAudioSystemClient mHdmiAudioSystemClient;
+    private TestLooper mTestLooper;
+    private int mVolume;
+    private int mMaxVolume;
+    private boolean mIsMute;
+
+    @Before
+    public void before() {
+        Log.d(TAG, "before()");
+        IHdmiControlService mService = new TestHdmiControlService();
+        mTestLooper = new TestLooper();
+        mHdmiAudioSystemClient =
+                new HdmiAudioSystemClient(mService, new Handler(mTestLooper.getLooper()));
+        resetVariables();
+    }
+
+    @Test
+    public void testSingleCommand() {
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 50, 100, false);
+        assertAudioStatus(50, 100, false);
+    }
+
+    @Test
+    public void testMultipleCommands_longTimeBetweenCalls() {
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 50, 100, false);
+        assertAudioStatus(50, 100, false);
+        mTestLooper.moveTimeForward(500);
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 60, 100, false);
+        assertAudioStatus(60, 100, false);
+    }
+
+    @Test
+    public void testMultipleCommands_shortTimeBetweenCalls() {
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 1, 100, false);
+        assertAudioStatus(1, 100, false);
+
+        mTestLooper.moveTimeForward(100); // current time: 100ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 10, 100, false);
+        assertAudioStatus(1, 100, false); // command not sent, no change
+
+        mTestLooper.moveTimeForward(100); // current time: 200ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 20, 100, false);
+        assertAudioStatus(1, 100, false); // command not sent, no change
+
+        mTestLooper.moveTimeForward(300); // current time: 500ms
+        mTestLooper.dispatchAll();
+        assertAudioStatus(20, 100, false); // pending command sent, changed to 20
+
+        mTestLooper.moveTimeForward(100); // current time: 600ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 60, 100, false);
+        assertAudioStatus(20, 100, false); // command not sent, no change
+
+        mTestLooper.moveTimeForward(200); // current time: 800ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 80, 100, false);
+        assertAudioStatus(20, 100, false); // command not sent, no change
+
+        mTestLooper.moveTimeForward(200); // current time: 1000ms
+        mTestLooper.dispatchAll();
+        assertAudioStatus(80, 100, false); // command sent, changed to 80
+    }
+
+    @Test
+    public void testMultipleCommands_shortTimeAndReturn() {
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 1, 100, false);
+        assertAudioStatus(1, 100, false);
+
+        mTestLooper.moveTimeForward(100); // current time: 100ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 20, 100, false);
+        assertAudioStatus(1, 100, false); // command not sent, no change
+
+        mTestLooper.moveTimeForward(100); // current time: 200ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 1, 100, false);
+        assertAudioStatus(1, 100, false); // command not sent, no change
+
+        mTestLooper.moveTimeForward(300); // current time: 500ms
+        mTestLooper.dispatchAll();
+        assertAudioStatus(1, 100, false); // pending command sent
+    }
+
+    @Test
+    public void testMultipleCommands_muteAdjust() {
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 1, 100, false);
+        assertAudioStatus(1, 100, false);
+
+        mTestLooper.moveTimeForward(100); // current time: 100ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(true, 10, 100, true);
+        assertAudioStatus(10, 100, true); // mute adjust, command sent, changed to 10
+
+        mTestLooper.moveTimeForward(100); // current time: 200ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 20, 100, false);
+        assertAudioStatus(10, 100, true); // command not sent, no change
+
+        mTestLooper.moveTimeForward(300); // current time: 500ms
+        mTestLooper.dispatchAll();
+        assertAudioStatus(20, 100, false); // pending command sent, changed to 20, unmuted
+    }
+
+    private void assertAudioStatus(int volume, int maxVolume, boolean isMute) {
+        Assert.assertEquals(volume, mVolume);
+        Assert.assertEquals(maxVolume, mMaxVolume);
+        Assert.assertEquals(isMute, mIsMute);
+    }
+
+    private void resetVariables() {
+        mVolume = -1;
+        mMaxVolume = -1;
+        mIsMute = true;
+    }
+
+    private final class TestHdmiControlService extends IHdmiControlService.Stub {
+
+        @Override
+        public int[] getSupportedTypes() {
+            return null;
+        }
+
+        @Override
+        public HdmiDeviceInfo getActiveSource() {
+            return null;
+        }
+
+        @Override
+        public void deviceSelect(final int deviceId, final IHdmiControlCallback callback) {
+        }
+
+        @Override
+        public void portSelect(final int portId, final IHdmiControlCallback callback) {
+        }
+
+        @Override
+        public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
+        }
+
+        @Override
+        public void oneTouchPlay(final IHdmiControlCallback callback) {
+        }
+
+        @Override
+        public void queryDisplayStatus(final IHdmiControlCallback callback) {
+        }
+
+        @Override
+        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
+        }
+
+        @Override
+        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
+        }
+
+        @Override
+        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
+        }
+
+        @Override
+        public List<HdmiPortInfo> getPortInfo() {
+            return null;
+        }
+
+        @Override
+        public boolean canChangeSystemAudioMode() {
+            return false;
+        }
+
+        @Override
+        public boolean getSystemAudioMode() {
+            return false;
+        }
+
+        @Override
+        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
+        }
+
+        @Override
+        public void addSystemAudioModeChangeListener(
+                final IHdmiSystemAudioModeChangeListener listener) {
+        }
+
+        @Override
+        public void removeSystemAudioModeChangeListener(
+                final IHdmiSystemAudioModeChangeListener listener) {
+        }
+
+        @Override
+        public void setInputChangeListener(final IHdmiInputChangeListener listener) {
+        }
+
+        @Override
+        public List<HdmiDeviceInfo> getInputDevices() {
+            return null;
+        }
+
+        // Returns all the CEC devices on the bus including system audio, switch,
+        // even those of reserved type.
+        @Override
+        public List<HdmiDeviceInfo> getDeviceList() {
+            return null;
+        }
+
+        @Override
+        public void setSystemAudioVolume(final int oldIndex, final int newIndex,
+                final int maxIndex) {
+        }
+
+        @Override
+        public void setSystemAudioMute(final boolean mute) {
+        }
+
+        @Override
+        public void setArcMode(final boolean enabled) {
+        }
+
+        @Override
+        public void setProhibitMode(final boolean enabled) {
+        }
+
+        @Override
+        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
+                final int deviceType) {
+        }
+
+        @Override
+        public void sendVendorCommand(final int deviceType, final int targetAddress,
+                final byte[] params, final boolean hasVendorId) {
+        }
+
+        @Override
+        public void sendStandby(final int deviceType, final int deviceId) {
+        }
+
+        @Override
+        public void setHdmiRecordListener(IHdmiRecordListener listener) {
+        }
+
+        @Override
+        public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
+        }
+
+        @Override
+        public void stopOneTouchRecord(final int recorderAddress) {
+        }
+
+        @Override
+        public void startTimerRecording(final int recorderAddress, final int sourceType,
+                final byte[] recordSource) {
+        }
+
+        @Override
+        public void clearTimerRecording(final int recorderAddress, final int sourceType,
+                final byte[] recordSource) {
+        }
+
+        @Override
+        public void sendMhlVendorCommand(final int portId, final int offset, final int length,
+                final byte[] data) {
+        }
+
+        @Override
+        public void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener) {
+        }
+
+        @Override
+        public void setStandbyMode(final boolean isStandbyModeOn) {
+        }
+
+        @Override
+        public void reportAudioStatus(final int deviceType, final int volume, final int maxVolume,
+                final boolean isMute) {
+            mVolume = volume;
+            mMaxVolume = maxVolume;
+            mIsMute = isMute;
+        }
+    }
+
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
index c66a10c..80ab4ea 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
@@ -44,7 +44,7 @@
 include $(BUILD_PACKAGE)
 
 ifndef LOCAL_JACK_ENABLED
-$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
 	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacyandexception/Test.class" >> $@
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
index c5e112b..cf8fc92 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
@@ -38,7 +38,7 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
 	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacytestapp/Test.class" >> $@
@@ -69,7 +69,7 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList2): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList2): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
 	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacytestapp/Test.class" >> $@
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
index a6c5373..2ce50b3 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
@@ -36,7 +36,7 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
 	$(MAINDEXCLASSES) $< 1>$@
 
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
index da48df9..8b0c750 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
@@ -35,7 +35,7 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
 	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
index 02b3f53..a36c993 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
@@ -35,7 +35,7 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
 	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
index 4808684..6b7418c 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
@@ -35,7 +35,7 @@
 
 include $(BUILD_PACKAGE)
 
-$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+$(mainDexList): $(full_classes_pre_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
 	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
diff --git a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
index 433d4d2..39bb84a 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -16,9 +16,10 @@
 
 package com.android.internal.util;
 
-import android.test.MoreAsserts;
+import static com.android.internal.util.ArrayUtils.concatElements;
 
-import java.util.Arrays;
+import static org.junit.Assert.assertArrayEquals;
+
 import junit.framework.TestCase;
 
 /**
@@ -92,29 +93,29 @@
     }
 
     public void testAppendInt() throws Exception {
-        MoreAsserts.assertEquals(new int[] { 1 },
+        assertArrayEquals(new int[] { 1 },
                 ArrayUtils.appendInt(null, 1));
-        MoreAsserts.assertEquals(new int[] { 1 },
+        assertArrayEquals(new int[] { 1 },
                 ArrayUtils.appendInt(new int[] { }, 1));
-        MoreAsserts.assertEquals(new int[] { 1, 2 },
+        assertArrayEquals(new int[] { 1, 2 },
                 ArrayUtils.appendInt(new int[] { 1 }, 2));
-        MoreAsserts.assertEquals(new int[] { 1, 2 },
+        assertArrayEquals(new int[] { 1, 2 },
                 ArrayUtils.appendInt(new int[] { 1, 2 }, 1));
     }
 
     public void testRemoveInt() throws Exception {
         assertNull(ArrayUtils.removeInt(null, 1));
-        MoreAsserts.assertEquals(new int[] { },
+        assertArrayEquals(new int[] { },
                 ArrayUtils.removeInt(new int[] { }, 1));
-        MoreAsserts.assertEquals(new int[] { 1, 2, 3, },
+        assertArrayEquals(new int[] { 1, 2, 3, },
                 ArrayUtils.removeInt(new int[] { 1, 2, 3}, 4));
-        MoreAsserts.assertEquals(new int[] { 2, 3, },
+        assertArrayEquals(new int[] { 2, 3, },
                 ArrayUtils.removeInt(new int[] { 1, 2, 3}, 1));
-        MoreAsserts.assertEquals(new int[] { 1, 3, },
+        assertArrayEquals(new int[] { 1, 3, },
                 ArrayUtils.removeInt(new int[] { 1, 2, 3}, 2));
-        MoreAsserts.assertEquals(new int[] { 1, 2, },
+        assertArrayEquals(new int[] { 1, 2, },
                 ArrayUtils.removeInt(new int[] { 1, 2, 3}, 3));
-        MoreAsserts.assertEquals(new int[] { 2, 3, 1 },
+        assertArrayEquals(new int[] { 2, 3, 1 },
                 ArrayUtils.removeInt(new int[] { 1, 2, 3, 1 }, 1));
     }
 
@@ -129,30 +130,51 @@
     }
 
     public void testAppendLong() throws Exception {
-        MoreAsserts.assertEquals(new long[] { 1 },
+        assertArrayEquals(new long[] { 1 },
                 ArrayUtils.appendLong(null, 1));
-        MoreAsserts.assertEquals(new long[] { 1 },
+        assertArrayEquals(new long[] { 1 },
                 ArrayUtils.appendLong(new long[] { }, 1));
-        MoreAsserts.assertEquals(new long[] { 1, 2 },
+        assertArrayEquals(new long[] { 1, 2 },
                 ArrayUtils.appendLong(new long[] { 1 }, 2));
-        MoreAsserts.assertEquals(new long[] { 1, 2 },
+        assertArrayEquals(new long[] { 1, 2 },
                 ArrayUtils.appendLong(new long[] { 1, 2 }, 1));
     }
 
     public void testRemoveLong() throws Exception {
         assertNull(ArrayUtils.removeLong(null, 1));
-        MoreAsserts.assertEquals(new long[] { },
+        assertArrayEquals(new long[] { },
                 ArrayUtils.removeLong(new long[] { }, 1));
-        MoreAsserts.assertEquals(new long[] { 1, 2, 3, },
+        assertArrayEquals(new long[] { 1, 2, 3, },
                 ArrayUtils.removeLong(new long[] { 1, 2, 3}, 4));
-        MoreAsserts.assertEquals(new long[] { 2, 3, },
+        assertArrayEquals(new long[] { 2, 3, },
                 ArrayUtils.removeLong(new long[] { 1, 2, 3}, 1));
-        MoreAsserts.assertEquals(new long[] { 1, 3, },
+        assertArrayEquals(new long[] { 1, 3, },
                 ArrayUtils.removeLong(new long[] { 1, 2, 3}, 2));
-        MoreAsserts.assertEquals(new long[] { 1, 2, },
+        assertArrayEquals(new long[] { 1, 2, },
                 ArrayUtils.removeLong(new long[] { 1, 2, 3}, 3));
-        MoreAsserts.assertEquals(new long[] { 2, 3, 1 },
+        assertArrayEquals(new long[] { 2, 3, 1 },
                 ArrayUtils.removeLong(new long[] { 1, 2, 3, 1 }, 1));
     }
 
+    public void testConcatEmpty() throws Exception {
+        assertArrayEquals(new Long[] {},
+                concatElements(Long.class, null, null));
+        assertArrayEquals(new Long[] {},
+                concatElements(Long.class, new Long[] {}, null));
+        assertArrayEquals(new Long[] {},
+                concatElements(Long.class, null, new Long[] {}));
+        assertArrayEquals(new Long[] {},
+                concatElements(Long.class, new Long[] {}, new Long[] {}));
+    }
+
+    public void testconcatElements() throws Exception {
+        assertArrayEquals(new Long[] { 1L },
+                concatElements(Long.class, new Long[] { 1L }, new Long[] {}));
+        assertArrayEquals(new Long[] { 1L },
+                concatElements(Long.class, new Long[] {}, new Long[] { 1L }));
+        assertArrayEquals(new Long[] { 1L, 2L },
+                concatElements(Long.class, new Long[] { 1L }, new Long[] { 2L }));
+        assertArrayEquals(new Long[] { 1L, 2L, 3L, 4L },
+                concatElements(Long.class, new Long[] { 1L, 2L }, new Long[] { 3L, 4L }));
+    }
 }
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index 4e09c69..5cfae11 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -38,7 +38,7 @@
   <hidden-api-whitelisted-app package="com.android.launcher3" />
   <hidden-api-whitelisted-app package="com.android.mtp" />
   <hidden-api-whitelisted-app package="com.android.musicfx" />
-  <hidden-api-whitelisted-app package="com.android.packageinstaller" />
+  <hidden-api-whitelisted-app package="com.android.permissioncontroller" />
   <hidden-api-whitelisted-app package="com.android.printservice.recommendation" />
   <hidden-api-whitelisted-app package="com.android.printspooler" />
   <hidden-api-whitelisted-app package="com.android.providers.blockednumber" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 82b6a22..f6587d3 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -133,13 +133,18 @@
     </privapp-permissions>
 
     <privapp-permissions package="com.android.packageinstaller">
-        <permission name="android.permission.CLEAR_APP_CACHE"/>
         <permission name="android.permission.DELETE_PACKAGES"/>
         <permission name="android.permission.INSTALL_PACKAGES"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+    </privapp-permissions>
+
+    <privapp-permissions package="com.android.permissioncontroller">
+        <permission name="android.permission.CLEAR_APP_CACHE"/>
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
-        <permission name="android.permission.USE_RESERVED_DISK"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.phone">
@@ -330,6 +335,7 @@
         <permission name="android.permission.START_TASKS_FROM_RECENTS" />
         <permission name="android.permission.STOP_APP_SWITCHES"/>
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
+        <permission name="android.permission.SUSPEND_APPS" />
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
         <permission name="android.permission.USE_RESERVED_DISK"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
diff --git a/data/keyboards/Vendor_054c_Product_0268.kl b/data/keyboards/Vendor_054c_Product_0268.kl
index 7c60137..522db3c 100644
--- a/data/keyboards/Vendor_054c_Product_0268.kl
+++ b/data/keyboards/Vendor_054c_Product_0268.kl
@@ -35,7 +35,7 @@
 key 0x122    BUTTON_THUMBR
 
 # PS key
-key 0x2d0    HOME
+key 0x2d0    BUTTON_MODE
 
 # Left Analog Stick
 axis 0x00    X
diff --git a/data/sounds/AllAudio.mk b/data/sounds/AllAudio.mk
index bf8067c..bb8add1 100644
--- a/data/sounds/AllAudio.mk
+++ b/data/sounds/AllAudio.mk
@@ -234,3 +234,8 @@
     $(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 \
diff --git a/data/sounds/AudioPackage14.mk b/data/sounds/AudioPackage14.mk
new file mode 100644
index 0000000..c903a2b
--- /dev/null
+++ b/data/sounds/AudioPackage14.mk
@@ -0,0 +1,32 @@
+#
+# Audio Package 14 - P
+#
+# Include this file in a product makefile to include these audio files
+#
+#
+
+LOCAL_PATH := frameworks/base/data/sounds
+
+# Simple files that do not require renaming
+ALARM_FILES := Argon Carbon Helium Krypton Neon Oxygen Osmium Platinum Timer
+NOTIFICATION_FILES := Ariel Ceres Carme Elara Europa Iapetus Io Rhea Salacia Titan Tethys
+RINGTONE_FILES := Atria Callisto Dione Ganymede Luna Oberon Phobos Pyxis Sedna Titania Triton \
+	Umbriel
+EFFECT_FILES := Effect_Tick KeypressReturn KeypressInvalid KeypressDelete KeypressSpacebar KeypressStandard \
+	camera_focus Dock Undock Lock Unlock Trusted ChargingStarted InCallNotification \
+	NFCFailure NFCInitiated NFCSuccess NFCTransferComplete NFCTransferInitiated
+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)
+
+PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
+	$(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:system/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)
+
+PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
+	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:system/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)
diff --git a/data/sounds/README.txt b/data/sounds/README.txt
index 193fd71..db20319 100644
--- a/data/sounds/README.txt
+++ b/data/sounds/README.txt
@@ -31,3 +31,13 @@
 ./effects/ogg/VideoStop_48k.ogg
   unused
 
+NFC
+---
+
+./effects/ogg/NFCFailure.ogg
+./effects/ogg/NFCInitiated.ogg
+./effects/ogg/NFCSuccess.ogg
+./effects/ogg/NFCTransferComplete.ogg
+./effects/ogg/NFCTransferInitiated.ogg
+
+referenced in AudioPackage14.mk (= AudioPackage13.mk + NFC sounds).
diff --git a/data/sounds/effects/ogg/NFCFailure.ogg b/data/sounds/effects/ogg/NFCFailure.ogg
new file mode 100644
index 0000000..e9ee662
--- /dev/null
+++ b/data/sounds/effects/ogg/NFCFailure.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/NFCInitiated.ogg b/data/sounds/effects/ogg/NFCInitiated.ogg
new file mode 100644
index 0000000..a86319f
--- /dev/null
+++ b/data/sounds/effects/ogg/NFCInitiated.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/NFCSuccess.ogg b/data/sounds/effects/ogg/NFCSuccess.ogg
new file mode 100644
index 0000000..39dfd1f
--- /dev/null
+++ b/data/sounds/effects/ogg/NFCSuccess.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/NFCTransferComplete.ogg b/data/sounds/effects/ogg/NFCTransferComplete.ogg
new file mode 100644
index 0000000..f00cd98
--- /dev/null
+++ b/data/sounds/effects/ogg/NFCTransferComplete.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/NFCTransferInitiated.ogg b/data/sounds/effects/ogg/NFCTransferInitiated.ogg
new file mode 100644
index 0000000..7be1bcb
--- /dev/null
+++ b/data/sounds/effects/ogg/NFCTransferInitiated.ogg
Binary files differ
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 97130f1..53e9826 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas.VertexMode;
 import android.text.GraphicsOperations;
 import android.text.MeasuredParagraph;
@@ -44,6 +45,7 @@
      * freed by NativeAllocation.
      * @hide
      */
+    @UnsupportedAppUsage
     protected long mNativeCanvasWrapper;
 
     /**
@@ -501,7 +503,7 @@
                             contextStart - paraStart,
                             contextEnd - contextStart,
                             x, y, isRtl, paint.getNativeInstance(),
-                            mp.getNativePtr());
+                            mp.getNativeMeasuredParagraph().getNativePtr());
                     return;
                 }
             }
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index c6f8415..cea6c1c 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
 import android.content.res.ResourcesImpl;
 import android.os.Parcel;
@@ -59,6 +60,7 @@
     private static final long NATIVE_ALLOCATION_SIZE = 32;
 
     // Convenience for JNI access
+    @UnsupportedAppUsage
     private final long mNativePtr;
 
     /**
@@ -75,9 +77,13 @@
      */
     private boolean mRequestPremultiplied;
 
+    @UnsupportedAppUsage
     private byte[] mNinePatchChunk; // may be null
+    @UnsupportedAppUsage
     private NinePatch.InsetStruct mNinePatchInsets; // may be null
+    @UnsupportedAppUsage
     private int mWidth;
+    @UnsupportedAppUsage
     private int mHeight;
     private boolean mRecycled;
 
@@ -99,11 +105,13 @@
      * density when running old apps.
      * @hide
      */
+    @UnsupportedAppUsage
     public static void setDefaultDensity(int density) {
         sDefaultDensity = density;
     }
 
     @SuppressWarnings("deprecation")
+    @UnsupportedAppUsage
     static int getDefaultDensity() {
         if (sDefaultDensity >= 0) {
             return sDefaultDensity;
@@ -117,6 +125,7 @@
      * int (pointer).
      */
     // called from JNI
+    @UnsupportedAppUsage
     Bitmap(long nativeBitmap, int width, int height, int density, boolean requestPremultiplied,
             byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets) {
         if (nativeBitmap == 0) {
@@ -158,6 +167,7 @@
      * width/height values
      */
     @SuppressWarnings("unused") // called from JNI
+    @UnsupportedAppUsage
     void reinit(int width, int height, boolean requestPremultiplied) {
         mWidth = width;
         mHeight = height;
@@ -328,6 +338,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setNinePatchChunk(byte[] chunk) {
         mNinePatchChunk = chunk;
     }
@@ -523,6 +534,7 @@
          */
         HARDWARE    (7);
 
+        @UnsupportedAppUsage
         final int nativeInt;
 
         private static Config sConfigs[] = {
@@ -533,6 +545,7 @@
             this.nativeInt = ni;
         }
 
+        @UnsupportedAppUsage
         static Config nativeToConfig(int ni) {
             return sConfigs[ni];
         }
@@ -667,6 +680,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public Bitmap createAshmemBitmap() {
         checkRecycled("Can't copy a recycled bitmap");
         noteHardwareBitmapSlowCall();
@@ -685,6 +699,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public Bitmap createAshmemBitmap(Config config) {
         checkRecycled("Can't copy a recycled bitmap");
         noteHardwareBitmapSlowCall();
@@ -703,6 +718,7 @@
      *         currently PIXEL_FORMAT_RGBA_8888 is the only supported format
      * @hide
      */
+    @UnsupportedAppUsage
     public static Bitmap createHardwareBitmap(@NonNull GraphicBuffer graphicBuffer) {
         return nativeCreateHardwareBitmap(graphicBuffer);
     }
@@ -1500,6 +1516,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     static public int scaleFromDensity(int size, int sdensity, int tdensity) {
         if (sdensity == DENSITY_NONE || tdensity == DENSITY_NONE || sdensity == tdensity) {
             return size;
@@ -2032,6 +2049,7 @@
      * @return {@link GraphicBuffer} which is internally used by hardware bitmap
      * @hide
      */
+    @UnsupportedAppUsage
     public GraphicBuffer createGraphicBufferHandle() {
         return nativeCreateGraphicBufferHandle(mNativePtr);
     }
@@ -2049,6 +2067,7 @@
     private static native Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig);
     private static native long nativeGetNativeFinalizer();
     private static native void nativeRecycle(long nativeBitmap);
+    @UnsupportedAppUsage
     private static native void nativeReconfigure(long nativeBitmap, int width, int height,
                                                  int config, boolean isPremultiplied);
 
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 7ea35e7..adab1a9c 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.os.Trace;
@@ -831,11 +832,15 @@
         return decodeFileDescriptor(fd, null, null);
     }
 
+    @UnsupportedAppUsage
     private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
             Rect padding, Options opts);
+    @UnsupportedAppUsage
     private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
             Rect padding, Options opts);
+    @UnsupportedAppUsage
     private static native Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts);
+    @UnsupportedAppUsage
     private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
             int length, Options opts);
     private static native boolean nativeIsSeekable(FileDescriptor fd);
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 2da27c7..9b5027d 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -15,6 +15,7 @@
 
 package android.graphics;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 
 import java.io.FileDescriptor;
@@ -165,6 +166,7 @@
 
         This can be called from JNI code.
     */
+    @UnsupportedAppUsage
     private BitmapRegionDecoder(long decoder) {
         mNativeBitmapRegionDecoder = decoder;
         mRecycled = false;
@@ -267,6 +269,7 @@
     private static native int nativeGetHeight(long lbm);
     private static native void nativeClean(long lbm);
 
+    @UnsupportedAppUsage
     private static native BitmapRegionDecoder nativeNewInstance(
             byte[] data, int offset, int length, boolean isShareable);
     private static native BitmapRegionDecoder nativeNewInstance(
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index 5577f53..bcf7229 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
 
 /**
  * Shader used to draw a bitmap as a texture. The bitmap can be repeated or
@@ -28,9 +29,12 @@
      * @hide
      */
     @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
+    @UnsupportedAppUsage
     public Bitmap mBitmap;
 
+    @UnsupportedAppUsage
     private int mTileX;
+    @UnsupportedAppUsage
     private int mTileY;
 
     /**
diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java
index 60588d0..cbd4ead 100644
--- a/graphics/java/android/graphics/Camera.java
+++ b/graphics/java/android/graphics/Camera.java
@@ -16,14 +16,14 @@
 
 package android.graphics;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * A camera instance can be used to compute 3D transformations and
  * generate a matrix that can be applied, for instance, on a
  * {@link Canvas}.
  */
 public class Camera {
-    private Matrix mMatrix;
-
     /**
      * Creates a new camera, with empty transformations.
      */
@@ -149,13 +149,7 @@
      * @param canvas The Canvas to set the transform matrix onto
      */
     public void applyToCanvas(Canvas canvas) {
-        if (canvas.isHardwareAccelerated()) {
-            if (mMatrix == null) mMatrix = new Matrix();
-            getMatrix(mMatrix);
-            canvas.concat(mMatrix);
-        } else {
-            nativeApplyToCanvas(canvas.getNativeCanvasWrapper());
-        }
+        nativeApplyToCanvas(canvas.getNativeCanvasWrapper());
     }
 
     public native float dotWithNormal(float dx, float dy, float dz);
@@ -174,5 +168,6 @@
     private native void nativeGetMatrix(long native_matrix);
     private native void nativeApplyToCanvas(long native_canvas);
 
+    @UnsupportedAppUsage
     long native_instance;
 }
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index a465eea..ef58c8f 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
 
 import dalvik.annotation.optimization.CriticalNative;
@@ -54,6 +55,7 @@
     public static boolean sCompatibilitySetBitmap = false;
 
     /** @hide */
+    @UnsupportedAppUsage
     public long getNativeCanvasWrapper() {
         return mNativeCanvasWrapper;
     }
@@ -62,6 +64,7 @@
     public boolean isRecordingFor(Object o) { return false; }
 
     // may be null
+    @UnsupportedAppUsage
     private Bitmap mBitmap;
 
     // optional field set by the caller
@@ -123,6 +126,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public Canvas(long nativeCanvas) {
         if (nativeCanvas == 0) {
             throw new IllegalStateException();
@@ -141,6 +145,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     protected GL getGL() {
         return null;
     }
@@ -269,6 +274,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public void setScreenDensity(int density) {
         mScreenDensity = density;
     }
@@ -1263,6 +1269,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void release() {
         mNativeCanvasWrapper = 0;
         if (mFinalizer != null) {
@@ -1276,6 +1283,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static void freeCaches() {
         nFreeCaches();
     }
@@ -1285,6 +1293,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static void freeTextLayoutCaches() {
         nFreeTextLayoutCaches();
     }
@@ -1591,9 +1600,9 @@
      * Draw the specified circle using the specified paint. If radius is <= 0, then nothing will be
      * drawn. The circle will be filled or framed based on the Style in the paint.
      *
-     * @param cx The x-coordinate of the center of the cirle to be drawn
-     * @param cy The y-coordinate of the center of the cirle to be drawn
-     * @param radius The radius of the cirle to be drawn
+     * @param cx The x-coordinate of the center of the circle to be drawn
+     * @param cy The y-coordinate of the center of the circle to be drawn
+     * @param radius The radius of the circle to be drawn
      * @param paint The paint used to draw the circle
      */
     public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
diff --git a/graphics/java/android/graphics/CanvasProperty.java b/graphics/java/android/graphics/CanvasProperty.java
index ea3886c..1275e08 100644
--- a/graphics/java/android/graphics/CanvasProperty.java
+++ b/graphics/java/android/graphics/CanvasProperty.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import android.annotation.UnsupportedAppUsage;
 import com.android.internal.util.VirtualRefBasePtr;
 
 /**
@@ -26,10 +27,12 @@
 
     private VirtualRefBasePtr mProperty;
 
+    @UnsupportedAppUsage
     public static CanvasProperty<Float> createFloat(float initialValue) {
         return new CanvasProperty<Float>(nCreateFloat(initialValue));
     }
 
+    @UnsupportedAppUsage
     public static CanvasProperty<Paint> createPaint(Paint initialValue) {
         return new CanvasProperty<Paint>(nCreatePaint(initialValue.getNativeInstance()));
     }
diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java
index 9201a2e..0f7980c 100644
--- a/graphics/java/android/graphics/ColorMatrixColorFilter.java
+++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 
 /**
  * A color filter that transforms colors through a 4x5 color matrix. This filter
@@ -26,6 +27,7 @@
  * @see ColorMatrix
  */
 public class ColorMatrixColorFilter extends ColorFilter {
+    @UnsupportedAppUsage
     private final ColorMatrix mMatrix = new ColorMatrix();
 
     /**
@@ -76,6 +78,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setColorMatrix(@Nullable ColorMatrix matrix) {
         discardNativeInstance();
         if (matrix == null) {
@@ -104,6 +107,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setColorMatrixArray(@Nullable float[] array) {
         // called '...Array' so that passing null isn't ambiguous
         discardNativeInstance();
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index c69eb32..229923c 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.graphics.fonts.FontVariationAxis;
 import android.text.TextUtils;
@@ -51,16 +52,19 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public long mNativePtr;
 
     // Points native font family builder. Must be zero after freezing this family.
     private long mBuilderPtr;
 
+    @UnsupportedAppUsage
     public FontFamily() {
         mBuilderPtr = nInitBuilder(null, 0);
         mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr);
     }
 
+    @UnsupportedAppUsage
     public FontFamily(@Nullable String[] langs, int variant) {
         final String langsString;
         if (langs == null || langs.length == 0) {
@@ -80,6 +84,7 @@
      * @return boolean returns false if some error happens in native code, e.g. broken font file is
      *                 passed, etc.
      */
+    @UnsupportedAppUsage
     public boolean freeze() {
         if (mBuilderPtr == 0) {
             throw new IllegalStateException("This FontFamily is already frozen");
@@ -93,6 +98,7 @@
         return mNativePtr != 0;
     }
 
+    @UnsupportedAppUsage
     public void abortCreation() {
         if (mBuilderPtr == 0) {
             throw new IllegalStateException("This FontFamily is already frozen or abandoned");
@@ -122,6 +128,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public boolean addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes,
             int weight, int italic) {
         if (mBuilderPtr == 0) {
@@ -147,6 +154,7 @@
      *            using the OS/2 table in the font.
      * @return
      */
+    @UnsupportedAppUsage
     public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie,
             boolean isAsset, int ttcIndex, int weight, int isItalic,
             FontVariationAxis[] axes) {
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 431d0e0..e3e8380 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import android.annotation.UnsupportedAppUsage;
 import android.graphics.fonts.FontVariationAxis;
 import android.text.FontConfig;
 import android.util.Xml;
@@ -37,6 +38,7 @@
 public class FontListParser {
 
     /* Parse fallback list (no names) */
+    @UnsupportedAppUsage
     public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException {
         try {
             XmlPullParser parser = Xml.newPullParser();
diff --git a/graphics/java/android/graphics/GraphicBuffer.java b/graphics/java/android/graphics/GraphicBuffer.java
index 53d2177..7408683 100644
--- a/graphics/java/android/graphics/GraphicBuffer.java
+++ b/graphics/java/android/graphics/GraphicBuffer.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -53,6 +54,7 @@
     private final int mFormat;
     private final int mUsage;
     // Note: do not rename, this field is used by native code
+    @UnsupportedAppUsage
     private final long mNativeObject;
 
     // These two fields are only used by lock/unlockCanvas()
@@ -84,6 +86,7 @@
     /**
      * Private use only. See {@link #create(int, int, int, int)}.
      */
+    @UnsupportedAppUsage
     private GraphicBuffer(int width, int height, int format, int usage, long nativeObject) {
         mWidth = width;
         mHeight = height;
@@ -96,6 +99,7 @@
      * For SurfaceControl JNI.
      * @hide
      */
+    @UnsupportedAppUsage
     public static GraphicBuffer createFromExisting(int width, int height,
             int format, int usage, long unwrappedNativeObject) {
         long nativeObject = nWrapGraphicBuffer(unwrappedNativeObject);
@@ -274,6 +278,7 @@
         nWriteGraphicBufferToParcel(mNativeObject, dest);
     }
 
+    @UnsupportedAppUsage
     public static final Parcelable.Creator<GraphicBuffer> CREATOR =
             new Parcelable.Creator<GraphicBuffer>() {
         public GraphicBuffer createFromParcel(Parcel in) {
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 098f100..7bed1ac 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -28,6 +28,7 @@
 import android.annotation.Nullable;
 import android.annotation.Px;
 import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
 import android.content.ContentResolver;
 import android.content.res.AssetFileDescriptor;
@@ -1856,6 +1857,7 @@
      * Private method called by JNI.
      */
     @SuppressWarnings("unused")
+    @UnsupportedAppUsage
     private int postProcessAndRelease(@NonNull Canvas canvas) {
         try {
             return mPostProcessor.onPostProcess(canvas);
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 43fd270..9546a4a 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import android.annotation.UnsupportedAppUsage;
+
 public class ImageFormat {
     /*
      * these constants are chosen to be binary compatible with their previous
@@ -103,6 +105,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int Y8 = 0x20203859;
 
     /**
diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java
index 1578ffb..62a890f 100644
--- a/graphics/java/android/graphics/LightingColorFilter.java
+++ b/graphics/java/android/graphics/LightingColorFilter.java
@@ -22,6 +22,7 @@
 package android.graphics;
 
 import android.annotation.ColorInt;
+import android.annotation.UnsupportedAppUsage;
 
 /**
  * A color filter that can be used to simulate simple lighting effects.
@@ -72,6 +73,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setColorMultiply(@ColorInt int mul) {
         if (mMul != mul) {
             mMul = mul;
@@ -97,6 +99,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setColorAdd(@ColorInt int add) {
         if (mAdd != add) {
             mAdd = add;
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 7139efe..7e6fc35 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -19,6 +19,7 @@
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 
 public class LinearGradient extends Shader {
 
@@ -31,15 +32,24 @@
      */
     private int mType;
 
+    @UnsupportedAppUsage
     private float mX0;
+    @UnsupportedAppUsage
     private float mY0;
+    @UnsupportedAppUsage
     private float mX1;
+    @UnsupportedAppUsage
     private float mY1;
+    @UnsupportedAppUsage
     private int[] mColors;
+    @UnsupportedAppUsage
     private float[] mPositions;
+    @UnsupportedAppUsage
     private int mColor0;
+    @UnsupportedAppUsage
     private int mColor1;
 
+    @UnsupportedAppUsage
     private TileMode mTileMode;
 
     /**
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index 486070c..f8cb366 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -21,6 +21,7 @@
 
 import libcore.util.NativeAllocationRegistry;
 
+import android.annotation.UnsupportedAppUsage;
 import java.io.PrintWriter;
 
 /**
@@ -39,6 +40,7 @@
     public static final int MPERSP_2 = 8;   //!< use with getValues/setValues
 
     /** @hide */
+    @UnsupportedAppUsage
     public final static Matrix IDENTITY_MATRIX = new Matrix() {
         void oops() {
             throw new IllegalStateException("Matrix can not be modified");
@@ -231,6 +233,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public final long native_instance;
 
     /**
diff --git a/graphics/java/android/graphics/Movie.java b/graphics/java/android/graphics/Movie.java
index 83857be..4c953b5 100644
--- a/graphics/java/android/graphics/Movie.java
+++ b/graphics/java/android/graphics/Movie.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 
 import java.io.FileInputStream;
@@ -25,8 +26,10 @@
  * @deprecated Prefer {@link android.graphics.drawable.AnimatedImageDrawable}.
  */
 public class Movie {
+    @UnsupportedAppUsage
     private long mNativeMovie;
 
+    @UnsupportedAppUsage
     private Movie(long nativeMovie) {
         if (nativeMovie == 0) {
             throw new RuntimeException("native movie creation failed");
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index b6a209f..800247a 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * The NinePatch class permits drawing a bitmap in nine or more sections.
  * Essentially, it allows the creation of custom graphics that will scale the
@@ -41,6 +43,7 @@
      */
     public static class InsetStruct {
         @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
+        @UnsupportedAppUsage
         InsetStruct(int opticalLeft, int opticalTop, int opticalRight, int opticalBottom,
                 int outlineLeft, int outlineTop, int outlineRight, int outlineBottom,
                 float outlineRadius, int outlineAlpha, float decodeScale) {
@@ -77,6 +80,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     private final Bitmap mBitmap;
 
     /**
@@ -84,6 +88,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public long mNativeChunk;
 
     private Paint mPaint;
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 1c85df0..98c990a 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -19,6 +19,7 @@
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
 import android.graphics.drawable.Drawable;
 
 import java.lang.annotation.Retention;
@@ -66,6 +67,7 @@
     public Path mPath;
 
     /** @hide */
+    @UnsupportedAppUsage
     public final Rect mRect = new Rect();
     /** @hide */
     public float mRadius = RADIUS_UNDEFINED;
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 42dac38..9dab536 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -19,6 +19,7 @@
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Size;
+import android.annotation.UnsupportedAppUsage;
 import android.graphics.fonts.FontVariationAxis;
 import android.os.LocaleList;
 import android.text.GraphicsOperations;
@@ -44,6 +45,7 @@
  */
 public class Paint {
 
+    @UnsupportedAppUsage
     private long mNativePaint;
     private long mNativeShader;
     private long mNativeColorFilter;
@@ -61,6 +63,7 @@
     private MaskFilter  mMaskFilter;
     private PathEffect  mPathEffect;
     private Shader      mShader;
+    @UnsupportedAppUsage
     private Typeface    mTypeface;
     private Xfermode    mXfermode;
 
@@ -618,6 +621,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public void setCompatibilityScaling(float factor) {
         if (factor == 1.0) {
             mHasCompatScaling = false;
@@ -635,6 +639,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public long getNativeInstance() {
         long newNativeShader = mShader == null ? 0 : mShader.getNativeInstance();
         if (newNativeShader != mNativeShader) {
@@ -1718,6 +1723,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setHyphenEdit(int hyphen) {
         nSetHyphenEdit(mNativePaint, hyphen);
     }
@@ -2261,6 +2267,7 @@
      * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
      * @hide
      */
+    @UnsupportedAppUsage
     public float getTextRunAdvances(char[] chars, int index, int count,
             int contextIndex, int contextCount, boolean isRtl, float[] advances,
             int advancesIndex) {
@@ -2451,6 +2458,7 @@
      * @return the offset of the next position, or -1
      * @hide
      */
+    @UnsupportedAppUsage
     public int getTextRunCursor(char[] text, int contextStart, int contextLength,
             int dir, int offset, int cursorOpt) {
         int contextEnd = contextStart + contextLength;
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 1652f64..405ab0b 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.annotation.UnsupportedAppUsage;
 
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
@@ -46,10 +47,12 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isSimplePath = true;
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public Region rects;
     private Direction mLastDirection = null;
 
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index f2d0227..f7acb11 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import android.annotation.UnsupportedAppUsage;
 import java.io.InputStream;
 import java.io.OutputStream;
 
@@ -32,6 +33,7 @@
  */
 public class Picture {
     private PictureCanvas mRecordingCanvas;
+    @UnsupportedAppUsage
     private long mNativePicture;
     private boolean mRequiresHwAcceleration;
 
diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java
index c6b6c66..f291e27 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.proto.ProtoOutputStream;
@@ -37,7 +38,7 @@
         this.y = y;
     }
 
-    public Point(Point src) {
+    public Point(@NonNull Point src) {
         this.x = src.x;
         this.y = src.y;
     }
@@ -99,7 +100,7 @@
     }
 
     /** @hide */
-    public void printShortString(PrintWriter pw) {
+    public void printShortString(@NonNull PrintWriter pw) {
         pw.print("["); pw.print(x); pw.print(","); pw.print(y); pw.print("]");
     }
 
@@ -130,7 +131,7 @@
      * @param fieldId           Field Id of the Rect as defined in the parent message
      * @hide
      */
-    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+    public void writeToProto(@NonNull ProtoOutputStream protoOutputStream, long fieldId) {
         final long token = protoOutputStream.start(fieldId);
         protoOutputStream.write(PointProto.X, x);
         protoOutputStream.write(PointProto.Y, y);
@@ -141,6 +142,7 @@
         /**
          * Return a new point from the data in the specified parcel.
          */
+        @Override
         public Point createFromParcel(Parcel in) {
             Point r = new Point();
             r.readFromParcel(in);
@@ -150,6 +152,7 @@
         /**
          * Return an array of rectangles of the specified size.
          */
+        @Override
         public Point[] newArray(int size) {
             return new Point[size];
         }
@@ -161,7 +164,7 @@
      *
      * @param in The parcel to read the point's coordinates from
      */
-    public void readFromParcel(Parcel in) {
+    public void readFromParcel(@NonNull Parcel in) {
         x = in.readInt();
         y = in.readInt();
     }
diff --git a/graphics/java/android/graphics/PointF.java b/graphics/java/android/graphics/PointF.java
index 8e4288e..a3b4194 100644
--- a/graphics/java/android/graphics/PointF.java
+++ b/graphics/java/android/graphics/PointF.java
@@ -16,10 +16,10 @@
 
 package android.graphics;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-
 /**
  * PointF holds two float coordinates
  */
@@ -34,7 +34,7 @@
         this.y = y; 
     }
     
-    public PointF(Point p) { 
+    public PointF(@NonNull Point p) {
         this.x = p.x;
         this.y = p.y;
     }
@@ -50,7 +50,7 @@
     /**
      * Set the point's x and y coordinates to the coordinates of p
      */
-    public final void set(PointF p) { 
+    public final void set(@NonNull PointF p) {
         this.x = p.x;
         this.y = p.y;
     }
@@ -134,6 +134,7 @@
         /**
          * Return a new point from the data in the specified parcel.
          */
+        @Override
         public PointF createFromParcel(Parcel in) {
             PointF r = new PointF();
             r.readFromParcel(in);
@@ -143,6 +144,7 @@
         /**
          * Return an array of rectangles of the specified size.
          */
+        @Override
         public PointF[] newArray(int size) {
             return new PointF[size];
         }
@@ -154,7 +156,7 @@
      *
      * @param in The parcel to read the point's coordinates from
      */
-    public void readFromParcel(Parcel in) {
+    public void readFromParcel(@NonNull Parcel in) {
         x = in.readFloat();
         y = in.readFloat();
     }
diff --git a/graphics/java/android/graphics/PorterDuff.java b/graphics/java/android/graphics/PorterDuff.java
index d7d3049..fba5043 100644
--- a/graphics/java/android/graphics/PorterDuff.java
+++ b/graphics/java/android/graphics/PorterDuff.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * <p>This class contains the list of alpha compositing and blending modes
  * that can be passed to {@link PorterDuffXfermode}, a specialized implementation
@@ -364,6 +366,7 @@
         /**
          * @hide
          */
+        @UnsupportedAppUsage
         public final int nativeInt;
     }
 
diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java
index 88a6322..6665220 100644
--- a/graphics/java/android/graphics/PorterDuffColorFilter.java
+++ b/graphics/java/android/graphics/PorterDuffColorFilter.java
@@ -18,6 +18,7 @@
 
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
 
 /**
  * A color filter that can be used to tint the source pixels using a single
@@ -50,6 +51,7 @@
      * @hide
      */
     @ColorInt
+    @UnsupportedAppUsage
     public int getColor() {
         return mColor;
     }
@@ -62,6 +64,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public PorterDuff.Mode getMode() {
         return mMode;
     }
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index f4b1191..41d2628 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.ColorInt;
+import android.annotation.UnsupportedAppUsage;
 
 public class RadialGradient extends Shader {
 
@@ -31,14 +32,22 @@
      */
     private int mType;
 
+    @UnsupportedAppUsage
     private float mX;
+    @UnsupportedAppUsage
     private float mY;
+    @UnsupportedAppUsage
     private float mRadius;
+    @UnsupportedAppUsage
     private int[] mColors;
+    @UnsupportedAppUsage
     private float[] mPositions;
+    @UnsupportedAppUsage
     private int mCenterColor;
+    @UnsupportedAppUsage
     private int mEdgeColor;
 
+    @UnsupportedAppUsage
     private TileMode mTileMode;
 
     /**
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 56a6820..4fec33f 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -19,6 +19,7 @@
 import android.annotation.CheckResult;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -206,6 +207,7 @@
      * Print short representation to given writer.
      * @hide
      */
+    @UnsupportedAppUsage
     public void printShortString(@NonNull PrintWriter pw) {
         pw.print('['); pw.print(left); pw.print(',');
         pw.print(top); pw.print("]["); pw.print(right);
@@ -694,6 +696,7 @@
      * Scales up the rect by the given scale.
      * @hide
      */
+    @UnsupportedAppUsage
     public void scale(float scale) {
         if (scale != 1.0f) {
             left = (int) (left * scale + 0.5f);
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index dca6d9e..29d9367 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Pools.SynchronizedPool;
@@ -30,6 +32,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public long mNativeRegion;
 
     // the native values for these must match up with the enum in SkRegion.h
@@ -48,6 +51,7 @@
         /**
          * @hide
          */
+        @UnsupportedAppUsage
         public final int nativeInt;
     }
 
@@ -59,14 +63,14 @@
 
     /** Return a copy of the specified region
     */
-    public Region(Region region) {
+    public Region(@NonNull Region region) {
         this(nativeConstructor());
         nativeSetRegion(mNativeRegion, region.mNativeRegion);
     }
 
     /** Return a region set to the specified rectangle
     */
-    public Region(Rect r) {
+    public Region(@NonNull Rect r) {
         mNativeRegion = nativeConstructor();
         nativeSetRect(mNativeRegion, r.left, r.top, r.right, r.bottom);
     }
@@ -86,14 +90,14 @@
 
     /** Set the region to the specified region.
     */
-    public boolean set(Region region) {
+    public boolean set(@NonNull Region region) {
         nativeSetRegion(mNativeRegion, region.mNativeRegion);
         return true;
     }
 
     /** Set the region to the specified rectangle
     */
-    public boolean set(Rect r) {
+    public boolean set(@NonNull Rect r) {
         return nativeSetRect(mNativeRegion, r.left, r.top, r.right, r.bottom);
     }
     
@@ -109,7 +113,7 @@
      * that is identical to the pixels that would be drawn by the path
      * (with no antialiasing).
      */
-    public boolean setPath(Path path, Region clip) {
+    public boolean setPath(@NonNull Path path, @NonNull Region clip) {
         return nativeSetPath(mNativeRegion, path.readOnlyNI(), clip.mNativeRegion);
     }
 
@@ -132,6 +136,7 @@
      * Return a new Rect set to the bounds of the region. If the region is
      * empty, the Rect will be set to [0, 0, 0, 0]
      */
+    @NonNull
     public Rect getBounds() {
         Rect r = new Rect();
         nativeGetBounds(mNativeRegion, r);
@@ -142,7 +147,7 @@
      * Set the Rect to the bounds of the region. If the region is empty, the
      * Rect will be set to [0, 0, 0, 0]
      */
-    public boolean getBounds(Rect r) {
+    public boolean getBounds(@NonNull Rect r) {
         if (r == null) {
             throw new NullPointerException();
         }
@@ -153,6 +158,7 @@
      * Return the boundary of the region as a new Path. If the region is empty,
      * the path will also be empty.
      */
+    @NonNull
     public Path getBoundaryPath() {
         Path path = new Path();
         nativeGetBoundaryPath(mNativeRegion, path.mutateNI());
@@ -163,7 +169,7 @@
      * Set the path to the boundary of the region. If the region is empty, the
      * path will also be empty.
      */
-    public boolean getBoundaryPath(Path path) {
+    public boolean getBoundaryPath(@NonNull Path path) {
         return nativeGetBoundaryPath(mNativeRegion, path.mutateNI());
     }
         
@@ -178,7 +184,7 @@
      * that the rectangle is not contained by this region, but return true is a
      * guarantee that the rectangle is contained by this region.
      */
-    public boolean quickContains(Rect r) {
+    public boolean quickContains(@NonNull Rect r) {
         return quickContains(r.left, r.top, r.right, r.bottom);
     }
 
@@ -196,7 +202,7 @@
      * not intersect the region. Returning false is not a guarantee that they
      * intersect, but returning true is a guarantee that they do not.
      */
-    public boolean quickReject(Rect r) {
+    public boolean quickReject(@NonNull Rect r) {
         return quickReject(r.left, r.top, r.right, r.bottom);
     }
 
@@ -236,6 +242,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void scale(float scale) {
         scale(scale, null);
     }
@@ -247,7 +254,7 @@
      */
     public native void scale(float scale, Region dst);
 
-    public final boolean union(Rect r) {
+    public final boolean union(@NonNull Rect r) {
         return op(r, Op.UNION);
     }
 
@@ -255,7 +262,7 @@
      * Perform the specified Op on this region and the specified rect. Return
      * true if the result of the op is not empty.
      */
-    public boolean op(Rect r, Op op) {
+    public boolean op(@NonNull Rect r, @NonNull Op op) {
         return nativeOp(mNativeRegion, r.left, r.top, r.right, r.bottom,
                         op.nativeInt);
     }
@@ -264,7 +271,7 @@
      * Perform the specified Op on this region and the specified rect. Return
      * true if the result of the op is not empty.
      */
-    public boolean op(int left, int top, int right, int bottom, Op op) {
+    public boolean op(int left, int top, int right, int bottom, @NonNull Op op) {
         return nativeOp(mNativeRegion, left, top, right, bottom,
                         op.nativeInt);
     }
@@ -273,7 +280,7 @@
      * Perform the specified Op on this region and the specified region. Return
      * true if the result of the op is not empty.
      */
-    public boolean op(Region region, Op op) {
+    public boolean op(@NonNull Region region, @NonNull Op op) {
         return op(this, region, op);
     }
 
@@ -281,7 +288,7 @@
      * Set this region to the result of performing the Op on the specified rect
      * and region. Return true if the result is not empty.
      */
-    public boolean op(Rect rect, Region region, Op op) {
+    public boolean op(@NonNull Rect rect, @NonNull Region region, @NonNull Op op) {
         return nativeOp(mNativeRegion, rect, region.mNativeRegion,
                         op.nativeInt);
     }
@@ -290,11 +297,12 @@
      * Set this region to the result of performing the Op on the specified
      * regions. Return true if the result is not empty.
      */
-    public boolean op(Region region1, Region region2, Op op) {
+    public boolean op(@NonNull Region region1, @NonNull Region region2, @NonNull Op op) {
         return nativeOp(mNativeRegion, region1.mNativeRegion,
                         region2.mNativeRegion, op.nativeInt);
     }
 
+    @Override
     public String toString() {
         return nativeToString(mNativeRegion);
     }
@@ -304,6 +312,7 @@
      *
      * @hide
      */
+    @NonNull
     public static Region obtain() {
         Region region = sPool.acquire();
         return (region != null) ? region : new Region();
@@ -316,7 +325,8 @@
      *
      * @hide
      */
-    public static Region obtain(Region other) {
+    @NonNull
+    public static Region obtain(@NonNull Region other) {
         Region region = obtain();
         region.set(other);
         return region;
@@ -327,6 +337,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void recycle() {
         setEmpty();
         sPool.release(this);
@@ -341,6 +352,7 @@
              * @param p    Parcel object to read the region from
              * @return a new region created from the data in the parcel
              */
+            @Override
             public Region createFromParcel(Parcel p) {
                 long ni = nativeCreateFromParcel(p);
                 if (ni == 0) {
@@ -348,11 +360,13 @@
                 }
                 return new Region(ni);
             }
+            @Override
             public Region[] newArray(int size) {
                 return new Region[size];
             }
     };
     
+    @Override
     public int describeContents() {
         return 0;
     }
@@ -362,6 +376,7 @@
      * rebuilt from the parcel by calling CREATOR.createFromParcel().
      * @param p    Parcel object to write the region data into
      */
+    @Override
     public void writeToParcel(Parcel p, int flags) {
         if (!nativeWriteToParcel(mNativeRegion, p)) {
             throw new RuntimeException();
@@ -377,6 +392,7 @@
         return nativeEquals(mNativeRegion, peer.mNativeRegion);
     }
 
+    @Override
     protected void finalize() throws Throwable {
         try {
             nativeDestructor(mNativeRegion);
@@ -395,6 +411,7 @@
 
     /* add dummy parameter so constructor can be called from jni without
        triggering 'not cloneable' exception */
+    @UnsupportedAppUsage
     private Region(long ni, int dummy) {
         this(ni);
     }
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 40288f5..40bcc9e 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 
 import libcore.util.NativeAllocationRegistry;
 
@@ -72,6 +73,7 @@
         TileMode(int nativeInt) {
             this.nativeInt = nativeInt;
         }
+        @UnsupportedAppUsage
         final int nativeInt;
     }
 
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 1eebd26..99f440d 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -68,13 +69,17 @@
  */
 public class SurfaceTexture {
     private final Looper mCreatorLooper;
+    @UnsupportedAppUsage
     private Handler mOnFrameAvailableHandler;
 
     /**
      * These fields are used by native code, do not access or modify.
      */
+    @UnsupportedAppUsage
     private long mSurfaceTexture;
+    @UnsupportedAppUsage
     private long mProducer;
+    @UnsupportedAppUsage
     private long mFrameAvailableListener;
 
     private boolean mIsSingleBuffered;
@@ -378,6 +383,7 @@
      * This method is invoked from native code only.
      */
     @SuppressWarnings({"UnusedDeclaration"})
+    @UnsupportedAppUsage
     private static void postEventFromNative(WeakReference<SurfaceTexture> weakSelf) {
         SurfaceTexture st = weakSelf.get();
         if (st != null) {
@@ -405,6 +411,7 @@
     private native void nativeSetDefaultBufferSize(int width, int height);
     private native void nativeUpdateTexImage();
     private native void nativeReleaseTexImage();
+    @UnsupportedAppUsage
     private native int nativeDetachFromGLContext();
     private native int nativeAttachToGLContext(int texName);
     private native void nativeRelease();
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index b6b80b4..f944d85 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -19,6 +19,7 @@
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 
 public class SweepGradient extends Shader {
 
@@ -31,11 +32,17 @@
      */
     private int mType;
 
+    @UnsupportedAppUsage
     private float mCx;
+    @UnsupportedAppUsage
     private float mCy;
+    @UnsupportedAppUsage
     private int[] mColors;
+    @UnsupportedAppUsage
     private float[] mPositions;
+    @UnsupportedAppUsage
     private int mColor0;
+    @UnsupportedAppUsage
     private int mColor1;
 
     /**
diff --git a/graphics/java/android/graphics/TableMaskFilter.java b/graphics/java/android/graphics/TableMaskFilter.java
index d0c1438..d81c491 100644
--- a/graphics/java/android/graphics/TableMaskFilter.java
+++ b/graphics/java/android/graphics/TableMaskFilter.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * @hide
  */
@@ -32,6 +34,7 @@
         native_instance = ni;
     }
     
+    @UnsupportedAppUsage
     public static TableMaskFilter CreateClipTable(int min, int max) {
         return new TableMaskFilter(nativeNewClip(min, max));
     }
diff --git a/graphics/java/android/graphics/TemporaryBuffer.java b/graphics/java/android/graphics/TemporaryBuffer.java
index 36a2275..0ae2c70 100644
--- a/graphics/java/android/graphics/TemporaryBuffer.java
+++ b/graphics/java/android/graphics/TemporaryBuffer.java
@@ -16,12 +16,14 @@
 
 package android.graphics;
 
+import android.annotation.UnsupportedAppUsage;
 import com.android.internal.util.ArrayUtils;
 
 /**
  * @hide
  */
 public class TemporaryBuffer {
+    @UnsupportedAppUsage
     public static char[] obtain(int len) {
         char[] buf;
 
@@ -37,6 +39,7 @@
         return buf;
     }
 
+    @UnsupportedAppUsage
     public static void recycle(char[] temp) {
         if (temp.length > 1000) return;
 
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 18dd97f..522d7a5 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -25,6 +25,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.graphics.fonts.FontVariationAxis;
 import android.net.Uri;
@@ -93,6 +94,7 @@
     /** The NORMAL style of the default monospace typeface. */
     public static final Typeface MONOSPACE;
 
+    @UnsupportedAppUsage
     static Typeface[] sDefaults;
 
     /**
@@ -119,12 +121,15 @@
     private static final Object sDynamicCacheLock = new Object();
 
     static Typeface sDefaultTypeface;
+    @UnsupportedAppUsage
     static final Map<String, Typeface> sSystemFontMap;
+    @UnsupportedAppUsage
     static final Map<String, FontFamily[]> sSystemFallbackMap;
 
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public long native_instance;
 
     /** @hide */
@@ -139,6 +144,7 @@
     public static final int BOLD_ITALIC = 3;
     /** @hide */ public static final int STYLE_MASK = 0x03;
 
+    @UnsupportedAppUsage
     private @Style int mStyle = 0;
 
     /**
@@ -162,6 +168,7 @@
     private int[] mSupportedAxes;
     private static final int[] EMPTY_AXES = {};
 
+    @UnsupportedAppUsage
     private static void setDefault(Typeface t) {
         sDefaultTypeface = t;
         nativeSetDefault(t.native_instance);
@@ -891,6 +898,7 @@
      *
      * @param families array of font families
      */
+    @UnsupportedAppUsage
     private static Typeface createFromFamilies(FontFamily[] families) {
         long[] ptrArray = new long[families.length];
         for (int i = 0; i < families.length; i++) {
@@ -904,6 +912,7 @@
      * This method is used by supportlib-v27.
      * TODO: Remove private API use in supportlib: http://b/72665240
      */
+    @UnsupportedAppUsage
     private static Typeface createFromFamiliesWithDefault(FontFamily[] families, int weight,
                 int italic) {
         return createFromFamiliesWithDefault(families, DEFAULT_FAMILY, weight, italic);
@@ -922,6 +931,7 @@
      *               closest to the regular weight and upright font is used.
      * @param families array of font families
      */
+    @UnsupportedAppUsage
     private static Typeface createFromFamiliesWithDefault(FontFamily[] families,
                 String fallbackName, int weight, int italic) {
         FontFamily[] fallback = sSystemFallbackMap.get(fallbackName);
@@ -939,6 +949,7 @@
     }
 
     // don't allow clients to call this directly
+    @UnsupportedAppUsage
     private Typeface(long ni) {
         if (ni == 0) {
             throw new RuntimeException("native typeface cannot be made");
@@ -1197,7 +1208,9 @@
     // TODO: clean up: change List<FontVariationAxis> to FontVariationAxis[]
     private static native long nativeCreateFromTypefaceWithVariation(
             long native_instance, List<FontVariationAxis> axes);
+    @UnsupportedAppUsage
     private static native long nativeCreateWeightAlias(long native_instance, int weight);
+    @UnsupportedAppUsage
     private static native long nativeCreateFromArray(long[] familyArray, int weight, int italic);
     private static native int[] nativeGetSupportedAxes(long native_instance);
 
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index a5da5d0..6f4adfd 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -21,6 +21,8 @@
 
 package android.graphics;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * Xfermode is the base class for objects that are called to implement custom
  * "transfer-modes" in the drawing pipeline. The static function Create(Modes)
@@ -30,5 +32,6 @@
  */
 public class Xfermode {
     static final int DEFAULT = PorterDuff.Mode.SRC_OVER.nativeInt;
+    @UnsupportedAppUsage
     int porterDuffMode = DEFAULT;
 }
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 4f467d9..3aaec31 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -19,6 +19,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
@@ -562,6 +563,7 @@
      *  callback, so no need to post.
      */
     @SuppressWarnings("unused")
+    @UnsupportedAppUsage
     private void onAnimationEnd() {
         if (mAnimationCallbacks != null) {
             for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index d714ca8..b29fd4d 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.content.res.Resources;
@@ -202,11 +203,13 @@
                 R.styleable.AnimatedRotateDrawable_frameDuration, state.mFrameDuration));
     }
 
+    @UnsupportedAppUsage
     public void setFramesCount(int framesCount) {
         mState.mFramesCount = framesCount;
         mIncrement = 360.0f / mState.mFramesCount;
     }
 
+    @UnsupportedAppUsage
     public void setFramesDuration(int framesDuration) {
         mState.mFrameDuration = framesDuration;
     }
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 3ed6a78..00380c5 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -20,6 +20,7 @@
 import android.animation.TimeInterpolator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
@@ -66,6 +67,7 @@
     private static final String ELEMENT_TRANSITION = "transition";
     private static final String ELEMENT_ITEM = "item";
 
+    @UnsupportedAppUsage
     private AnimatedStateListState mState;
 
     /** The currently running transition, if any. */
@@ -558,7 +560,9 @@
 
         int[] mAnimThemeAttrs;
 
+        @UnsupportedAppUsage
         LongSparseLongArray mTransitions;
+        @UnsupportedAppUsage
         SparseIntArray mStateIds;
 
         AnimatedStateListState(@Nullable AnimatedStateListState orig,
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 34eac56..76f2cfb 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -25,6 +25,7 @@
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
 import android.app.Application;
 import android.content.pm.ActivityInfo.Config;
@@ -168,7 +169,7 @@
  * </p>
  * Below is an example of a VectorDrawable defined in vectordrawable.xml. This VectorDrawable is
  * referred to by its file name (not including file suffix) in the
- * <a href="AVDExample">AnimatedVectorDrawable XML example</a>.
+ * <a href="#AVDExample">AnimatedVectorDrawable XML example</a>.
  * <pre>
  * &lt;vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
  *     android:height=&quot;64dp&quot;
@@ -305,6 +306,7 @@
     private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
 
     /** Local, mutable animator set. */
+    @UnsupportedAppUsage
     private VectorDrawableAnimator mAnimatorSet;
 
     /**
@@ -313,6 +315,7 @@
      */
     private Resources mRes;
 
+    @UnsupportedAppUsage
     private AnimatedVectorDrawableState mAnimatedVectorState;
 
     /** The animator set that is parsed from the xml. */
@@ -641,6 +644,7 @@
      * Force to animate on UI thread.
      * @hide
      */
+    @UnsupportedAppUsage
     public void forceAnimationOnUI() {
         if (mAnimatorSet instanceof VectorDrawableAnimatorRT) {
             VectorDrawableAnimatorRT animator = (VectorDrawableAnimatorRT) mAnimatorSet;
@@ -1771,6 +1775,7 @@
         }
 
         // onFinished: should be called from native
+        @UnsupportedAppUsage
         private static void callOnFinished(VectorDrawableAnimatorRT set, int id) {
             set.onAnimationEnd(id);
         }
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 0fd1741..57764c2 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -24,6 +24,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.Resources.Theme;
@@ -88,6 +89,7 @@
     private AnimationState mAnimationState;
 
     /** The current frame, ranging from 0 to {@link #mAnimationState#getChildCount() - 1} */
+    @UnsupportedAppUsage
     private int mCurFrame = 0;
 
     /** Whether the drawable has an animation callback posted. */
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 99bed60..9761901 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -17,6 +17,7 @@
 package android.graphics.drawable;
 
 import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -87,9 +88,11 @@
 
     private final Rect mDstRect = new Rect();   // #updateDstRectAndInsetsIfDirty() sets this
 
+    @UnsupportedAppUsage
     private BitmapState mBitmapState;
     private PorterDuffColorFilter mTintFilter;
 
+    @UnsupportedAppUsage
     private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
 
     private boolean mDstRectAndInsetsDirty = true;
@@ -237,6 +240,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public void setBitmap(Bitmap bitmap) {
         if (mBitmapState.mBitmap != bitmap) {
             mBitmapState.mBitmap = bitmap;
@@ -692,6 +696,7 @@
     /**
      * @hide only needed by a hack within ProgressBar
      */
+    @UnsupportedAppUsage
     public ColorStateList getTint() {
         return mBitmapState.mTint;
     }
@@ -699,6 +704,7 @@
     /**
      * @hide only needed by a hack within ProgressBar
      */
+    @UnsupportedAppUsage
     public Mode getTintMode() {
         return mBitmapState.mTintMode;
     }
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index d925b6b..31fdb02 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -23,6 +23,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.Resources.Theme;
@@ -58,6 +59,7 @@
 
     private final Rect mTmpRect = new Rect();
 
+    @UnsupportedAppUsage
     private ClipState mState;
 
     ClipDrawable() {
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index a601d6d..18b41fa 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -52,6 +53,7 @@
  * @attr ref android.R.styleable#ColorDrawable_color
  */
 public class ColorDrawable extends Drawable {
+    @UnsupportedAppUsage
     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
     @ViewDebug.ExportedProperty(deepExport = true, prefix = "state_")
@@ -336,6 +338,7 @@
         int[] mThemeAttrs;
         int mBaseColor; // base color, independent of setAlpha()
         @ViewDebug.ExportedProperty
+        @UnsupportedAppUsage
         int mUseColor;  // basecolor modulated by setAlpha()
         @Config int mChangingConfigurations;
         ColorStateList mTint = null;
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 986d0c1..e1f7263 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -186,6 +187,7 @@
     private int mLevel = 0;
     private @Config int mChangingConfigurations = 0;
     private Rect mBounds = ZERO_BOUNDS_RECT;  // lazily becomes a new Rect()
+    @UnsupportedAppUsage
     private WeakReference<Callback> mCallback = null;
     private boolean mVisible = true;
 
@@ -204,6 +206,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     protected int mSrcDensityOverride = 0;
 
     /**
@@ -714,6 +717,7 @@
      *
      * @hide magic!
      */
+    @UnsupportedAppUsage
     public boolean isProjected() {
         return false;
     }
@@ -1389,6 +1393,7 @@
      * @throws XmlPullParserException
      * @throws IOException
      */
+    @UnsupportedAppUsage
     void inflateWithAttributes(@NonNull @SuppressWarnings("unused") Resources r,
             @NonNull @SuppressWarnings("unused") XmlPullParser parser, @NonNull TypedArray attrs,
             @AttrRes int visibleAttr) throws XmlPullParserException, IOException {
@@ -1508,6 +1513,7 @@
      * Ensures the tint filter is consistent with the current tint color and
      * mode.
      */
+    @UnsupportedAppUsage
     @Nullable PorterDuffColorFilter updateTintFilter(@Nullable PorterDuffColorFilter tintFilter,
             @Nullable ColorStateList tint, @Nullable PorterDuff.Mode tintMode) {
         if (tint == null || tintMode == null) {
@@ -1613,6 +1619,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) {
         switch (value) {
             case 3: return Mode.SRC_OVER;
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index e7b383a..10f6fae 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -17,6 +17,7 @@
 package android.graphics.drawable;
 
 import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -54,9 +55,11 @@
      * to improve the quality at negligible cost.
      */
     private static final boolean DEFAULT_DITHER = true;
+    @UnsupportedAppUsage
     private DrawableContainerState mDrawableContainerState;
     private Rect mHotspotBounds;
     private Drawable mCurrDrawable;
+    @UnsupportedAppUsage
     private Drawable mLastDrawable;
     private int mAlpha = 0xFF;
 
@@ -686,11 +689,13 @@
         @Config int mChildrenChangingConfigurations;
 
         SparseArray<ConstantState> mDrawableFutures;
+        @UnsupportedAppUsage
         Drawable[] mDrawables;
         int mNumChildren;
 
         boolean mVariablePadding = false;
         boolean mCheckedPadding;
+        @UnsupportedAppUsage
         Rect mConstantPadding;
 
         boolean mConstantSize = false;
@@ -720,6 +725,7 @@
         boolean mAutoMirrored;
 
         ColorFilter mColorFilter;
+        @UnsupportedAppUsage
         boolean mHasColorFilter;
 
         ColorStateList mTintList;
@@ -730,6 +736,7 @@
         /**
          * @hide
          */
+        @UnsupportedAppUsage
         protected DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
                 Resources res) {
             mOwner = owner;
diff --git a/graphics/java/android/graphics/drawable/DrawableInflater.java b/graphics/java/android/graphics/drawable/DrawableInflater.java
index 0ee9071..bad3791 100644
--- a/graphics/java/android/graphics/drawable/DrawableInflater.java
+++ b/graphics/java/android/graphics/drawable/DrawableInflater.java
@@ -22,6 +22,7 @@
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
@@ -49,6 +50,7 @@
             new HashMap<>();
 
     private final Resources mRes;
+    @UnsupportedAppUsage
     private final ClassLoader mClassLoader;
 
     /**
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index a907ca5..4ee45bf 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -46,6 +47,7 @@
  * Drawable container with only one child element.
  */
 public abstract class DrawableWrapper extends Drawable implements Drawable.Callback {
+    @UnsupportedAppUsage
     private DrawableWrapperState mState;
     private Drawable mDrawable;
     private boolean mMutated;
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 5629389..8740234 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -20,6 +20,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -155,10 +156,14 @@
     private static final float DEFAULT_INNER_RADIUS_RATIO = 3.0f;
     private static final float DEFAULT_THICKNESS_RATIO = 9.0f;
 
+    @UnsupportedAppUsage
     private GradientState mGradientState;
 
+    @UnsupportedAppUsage
     private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    @UnsupportedAppUsage
     private Rect mPadding;
+    @UnsupportedAppUsage
     private Paint mStrokePaint;   // optional, set by the caller
     private ColorFilter mColorFilter;   // optional, set by the caller
     private PorterDuffColorFilter mTintFilter;
@@ -1792,27 +1797,46 @@
 
     final static class GradientState extends ConstantState {
         public @Config int mChangingConfigurations;
+        @UnsupportedAppUsage
         public @Shape int mShape = RECTANGLE;
+        @UnsupportedAppUsage
         public @GradientType int mGradient = LINEAR_GRADIENT;
+        @UnsupportedAppUsage
         public int mAngle = 0;
+        @UnsupportedAppUsage
         public Orientation mOrientation;
+        @UnsupportedAppUsage
         public ColorStateList mSolidColors;
         public ColorStateList mStrokeColors;
+        @UnsupportedAppUsage
         public @ColorInt int[] mGradientColors;
         public @ColorInt int[] mTempColors; // no need to copy
         public float[] mTempPositions; // no need to copy
+        @UnsupportedAppUsage
         public float[] mPositions;
+        @UnsupportedAppUsage
         public int mStrokeWidth = -1; // if >= 0 use stroking.
+        @UnsupportedAppUsage
         public float mStrokeDashWidth = 0.0f;
+        @UnsupportedAppUsage
         public float mStrokeDashGap = 0.0f;
+        @UnsupportedAppUsage
         public float mRadius = 0.0f; // use this if mRadiusArray is null
+        @UnsupportedAppUsage
         public float[] mRadiusArray = null;
+        @UnsupportedAppUsage
         public Rect mPadding = null;
+        @UnsupportedAppUsage
         public int mWidth = -1;
+        @UnsupportedAppUsage
         public int mHeight = -1;
+        @UnsupportedAppUsage
         public float mInnerRadiusRatio = DEFAULT_INNER_RADIUS_RATIO;
+        @UnsupportedAppUsage
         public float mThicknessRatio = DEFAULT_THICKNESS_RATIO;
+        @UnsupportedAppUsage
         public int mInnerRadius = -1;
+        @UnsupportedAppUsage
         public int mThickness = -1;
         public boolean mDither = false;
         public Insets mOpticalInsets = Insets.NONE;
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 361fe0b..accc081 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -21,6 +21,7 @@
 import android.annotation.IdRes;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -99,6 +100,7 @@
 
     private static final int VERSION_STREAM_SERIALIZER = 1;
 
+    @UnsupportedAppUsage
     private final int mType;
 
     private ColorStateList mTintList;
@@ -115,6 +117,7 @@
 
     // TYPE_RESOURCE: package name
     // TYPE_URI: uri string
+    @UnsupportedAppUsage
     private String          mString1;
 
     // TYPE_RESOURCE: resId
@@ -139,6 +142,7 @@
      * @return The {@link android.graphics.Bitmap} held by this {@link #TYPE_BITMAP} Icon.
      * @hide
      */
+    @UnsupportedAppUsage
     public Bitmap getBitmap() {
         if (mType != TYPE_BITMAP && mType != TYPE_ADAPTIVE_BITMAP) {
             throw new IllegalStateException("called getBitmap() on " + this);
@@ -154,6 +158,7 @@
      * @return The length of the compressed bitmap byte array held by this {@link #TYPE_DATA} Icon.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getDataLength() {
         if (mType != TYPE_DATA) {
             throw new IllegalStateException("called getDataLength() on " + this);
@@ -168,6 +173,7 @@
      * valid compressed bitmap data is found.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getDataOffset() {
         if (mType != TYPE_DATA) {
             throw new IllegalStateException("called getDataOffset() on " + this);
@@ -182,6 +188,7 @@
      * bitmap data.
      * @hide
      */
+    @UnsupportedAppUsage
     public byte[] getDataBytes() {
         if (mType != TYPE_DATA) {
             throw new IllegalStateException("called getDataBytes() on " + this);
@@ -195,6 +202,7 @@
      * @return The {@link android.content.res.Resources} for this {@link #TYPE_RESOURCE} Icon.
      * @hide
      */
+    @UnsupportedAppUsage
     public Resources getResources() {
         if (mType != TYPE_RESOURCE) {
             throw new IllegalStateException("called getResources() on " + this);
@@ -560,6 +568,7 @@
      * Version of createWithResource that takes Resources. Do not use.
      * @hide
      */
+    @UnsupportedAppUsage
     public static Icon createWithResource(Resources res, @DrawableRes int resId) {
         if (res == null) {
             throw new IllegalArgumentException("Resource must not be null.");
@@ -692,6 +701,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public boolean hasTint() {
         return (mTintList != null) || (mTintMode != DEFAULT_TINT_MODE);
     }
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index ade4294..bc8a4cb 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
@@ -57,6 +58,7 @@
     private final Rect mTmpRect = new Rect();
     private final Rect mTmpInsetRect = new Rect();
 
+    @UnsupportedAppUsage
     private InsetState mState;
 
     /**
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 4725c2c..b4392c8 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -93,6 +94,7 @@
     public static final int INSET_UNDEFINED = Integer.MIN_VALUE;
 
     @NonNull
+    @UnsupportedAppUsage
     LayerState mLayerState;
 
     private int[] mPaddingL;
@@ -428,6 +430,7 @@
      * @param layer The layer to add.
      * @return The index of the layer.
      */
+    @UnsupportedAppUsage
     int addLayer(@NonNull ChildDrawable layer) {
         final LayerState st = mLayerState;
         final int N = st.mChildren != null ? st.mChildren.length : 0;
@@ -1739,6 +1742,7 @@
     /**
      * Ensures the child padding caches are large enough.
      */
+    @UnsupportedAppUsage
     void ensurePadding() {
         final int N = mLayerState.mNumChildren;
         if (mPaddingL != null && mPaddingL.length >= N) {
@@ -1820,6 +1824,7 @@
     }
 
     static class ChildDrawable {
+        @UnsupportedAppUsage
         public Drawable mDrawable;
         public int[] mThemeAttrs;
         public int mDensity = DisplayMetrics.DENSITY_DEFAULT;
@@ -1922,6 +1927,7 @@
         private int[] mThemeAttrs;
 
         int mNumChildren;
+        @UnsupportedAppUsage
         ChildDrawable[] mChildren;
 
         int mDensity;
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 7f23cea..b534771 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -70,6 +71,7 @@
     /** Temporary rect used for density scaling. */
     private Rect mTempRect;
 
+    @UnsupportedAppUsage
     private NinePatchState mNinePatchState;
     private PorterDuffColorFilter mTintFilter;
     private Rect mPadding;
@@ -588,6 +590,7 @@
         @Config int mChangingConfigurations;
 
         // Values loaded during inflation.
+        @UnsupportedAppUsage
         NinePatch mNinePatch = null;
         ColorStateList mTint = null;
         Mode mTintMode = DEFAULT_TINT_MODE;
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 266a6ac..1540cc2 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -120,6 +121,7 @@
     private final Rect mDirtyBounds = new Rect();
 
     /** Mirrors mLayerState with some extra information. */
+    @UnsupportedAppUsage
     private RippleState mState;
 
     /** The masking layer, e.g. the layer with id R.id.mask. */
@@ -157,6 +159,7 @@
     private Paint mRipplePaint;
 
     /** Target density of the display into which ripples are drawn. */
+    @UnsupportedAppUsage
     private int mDensity;
 
     /** Whether bounds are being overridden. */
@@ -857,6 +860,7 @@
         mMask.draw(canvas);
     }
 
+    @UnsupportedAppUsage
     Paint getRipplePaint() {
         if (mRipplePaint == null) {
             mRipplePaint = new Paint();
@@ -945,6 +949,7 @@
      * @param forceSoftware true if RenderThread animations should be disabled, false otherwise
      * @hide
      */
+    @UnsupportedAppUsage
     public void setForceSoftware(boolean forceSoftware) {
         mForceSoftware = forceSoftware;
     }
@@ -975,6 +980,7 @@
 
     static class RippleState extends LayerState {
         int[] mTouchThemeAttrs;
+        @UnsupportedAppUsage
         ColorStateList mColor = ColorStateList.valueOf(Color.MAGENTA);
         int mMaxRadius = RADIUS_AUTO;
 
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index c0dfe77..db5f082 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -23,6 +23,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.content.res.Resources;
@@ -54,6 +55,7 @@
 public class RotateDrawable extends DrawableWrapper {
     private static final int MAX_LEVEL = 10000;
 
+    @UnsupportedAppUsage
     private RotateState mState;
 
     /**
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index 51e143b..91ed061 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -23,6 +23,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
@@ -66,6 +67,7 @@
 
     private final Rect mTmpRect = new Rect();
 
+    @UnsupportedAppUsage
     private ScaleState mState;
 
     ScaleDrawable() {
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 67c3412..8de8f81 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
@@ -63,6 +64,7 @@
 
     private static final boolean DEBUG = false;
 
+    @UnsupportedAppUsage
     private StateListState mStateListState;
     private boolean mMutated;
 
@@ -129,6 +131,7 @@
     /**
      * Updates the constant state from the values in the typed array.
      */
+    @UnsupportedAppUsage
     private void updateStateFromTypedArray(TypedArray a) {
         final StateListState state = mStateListState;
 
@@ -206,6 +209,7 @@
      * @param attrs The attribute set.
      * @return An array of state_ attributes.
      */
+    @UnsupportedAppUsage
     int[] extractStateSet(AttributeSet attrs) {
         int j = 0;
         final int numAttrs = attrs.getAttributeCount();
@@ -329,6 +333,7 @@
             mStateSets = stateSets;
         }
 
+        @UnsupportedAppUsage
         int addStateSet(int[] stateSet, Drawable drawable) {
             final int pos = addChild(drawable);
             mStateSets[pos] = stateSet;
diff --git a/graphics/java/android/graphics/drawable/TransitionDrawable.java b/graphics/java/android/graphics/drawable/TransitionDrawable.java
index 3dfd680..276f366 100644
--- a/graphics/java/android/graphics/drawable/TransitionDrawable.java
+++ b/graphics/java/android/graphics/drawable/TransitionDrawable.java
@@ -16,6 +16,7 @@
 
 package android.graphics.drawable;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -65,10 +66,13 @@
     private boolean mReverse;
     private long mStartTimeMillis;
     private int mFrom;
+    @UnsupportedAppUsage
     private int mTo;
     private int mDuration;
     private int mOriginalDuration;
+    @UnsupportedAppUsage
     private int mAlpha = 0;
+    @UnsupportedAppUsage
     private boolean mCrossFade;
 
     /**
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index b5bd97f..7325b85 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -16,6 +16,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.ComplexColor;
@@ -321,6 +322,7 @@
 
     private VectorDrawableState mVectorState;
 
+    @UnsupportedAppUsage
     private PorterDuffColorFilter mTintFilter;
     private ColorFilter mColorFilter;
 
@@ -389,6 +391,7 @@
         mMutated = false;
     }
 
+    @UnsupportedAppUsage
     Object getTargetByName(String name) {
         return mVectorState.mVGTargetsMap.get(name);
     }
@@ -867,6 +870,7 @@
         return super.getChangingConfigurations() | mVectorState.getChangingConfigurations();
     }
 
+    @UnsupportedAppUsage
     void setAllowCaching(boolean allowCaching) {
         nSetAllowCaching(mVectorState.getNativeRenderer(), allowCaching);
     }
@@ -1498,6 +1502,7 @@
         }
 
         @SuppressWarnings("unused")
+        @UnsupportedAppUsage
         public void setRotation(float rotation) {
             if (isTreeValid()) {
                 nSetRotation(mNativePtr, rotation);
@@ -1510,6 +1515,7 @@
         }
 
         @SuppressWarnings("unused")
+        @UnsupportedAppUsage
         public void setPivotX(float pivotX) {
             if (isTreeValid()) {
                 nSetPivotX(mNativePtr, pivotX);
@@ -1522,6 +1528,7 @@
         }
 
         @SuppressWarnings("unused")
+        @UnsupportedAppUsage
         public void setPivotY(float pivotY) {
             if (isTreeValid()) {
                 nSetPivotY(mNativePtr, pivotY);
@@ -1558,6 +1565,7 @@
         }
 
         @SuppressWarnings("unused")
+        @UnsupportedAppUsage
         public void setTranslateX(float translateX) {
             if (isTreeValid()) {
                 nSetTranslateX(mNativePtr, translateX);
@@ -1570,6 +1578,7 @@
         }
 
         @SuppressWarnings("unused")
+        @UnsupportedAppUsage
         public void setTranslateY(float translateY) {
             if (isTreeValid()) {
                 nSetTranslateY(mNativePtr, translateY);
@@ -2024,7 +2033,7 @@
                 if (fillColors instanceof  GradientColor) {
                     mFillColors = fillColors;
                     fillGradient = ((GradientColor) fillColors).getShader();
-                } else if (fillColors.isStateful()) {
+                } else if (fillColors.isStateful() || fillColors.canApplyTheme()) {
                     mFillColors = fillColors;
                 } else {
                     mFillColors = null;
@@ -2040,7 +2049,7 @@
                 if (strokeColors instanceof GradientColor) {
                     mStrokeColors = strokeColors;
                     strokeGradient = ((GradientColor) strokeColors).getShader();
-                } else if (strokeColors.isStateful()) {
+                } else if (strokeColors.isStateful() || strokeColors.canApplyTheme()) {
                     mStrokeColors = strokeColors;
                 } else {
                     mStrokeColors = null;
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
new file mode 100644
index 0000000..9d94a64
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -0,0 +1,477 @@
+/*
+ * 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.graphics.fonts;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.util.TypedValue;
+
+import com.android.internal.util.Preconditions;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+/**
+ * A font class can be used for creating FontFamily.
+ */
+public class Font {
+    private static final String TAG = "Font";
+
+    private static final int NOT_SPECIFIED = -1;
+    private static final int STYLE_ITALIC = 1;
+    private static final int STYLE_NORMAL = 0;
+
+    /**
+     * A font weight value for the thin weight
+     */
+    public static final int FONT_WEIGHT_THIN = 100;
+
+    /**
+     * A font weight value for the extra-light weight
+     */
+    public static final int FONT_WEIGHT_EXTRA_LIGHT = 200;
+
+    /**
+     * A font weight value for the light weight
+     */
+    public static final int FONT_WEIGHT_LIGHT = 300;
+
+    /**
+     * A font weight value for the normal weight
+     */
+    public static final int FONT_WEIGHT_NORMAL = 400;
+
+    /**
+     * A font weight value for the medium weight
+     */
+    public static final int FONT_WEIGHT_MEDIUM = 500;
+
+    /**
+     * A font weight value for the semi-bold weight
+     */
+    public static final int FONT_WEIGHT_SEMI_BOLD = 600;
+
+    /**
+     * A font weight value for the bold weight.
+     */
+    public static final int FONT_WEIGHT_BOLD = 700;
+
+    /**
+     * A font weight value for the extra-bold weight
+     */
+    public static final int FONT_WEIGHT_EXTRA_BOLD = 800;
+
+    /**
+     * A font weight value for the black weight
+     */
+    public static final int FONT_WEIGHT_BLACK = 900;
+
+    /**
+     * A builder class for creating new Font.
+     */
+    public static class Builder {
+        private static final NativeAllocationRegistry sAssetByteBufferRegistroy =
+                new NativeAllocationRegistry(ByteBuffer.class.getClassLoader(),
+                    nGetReleaseNativeAssetFunc(), 64);
+
+        private static final NativeAllocationRegistry sFontRegistory =
+                new NativeAllocationRegistry(Font.class.getClassLoader(),
+                    nGetReleaseNativeFont(), 64);
+
+        private @Nullable ByteBuffer mBuffer;
+        private @IntRange(from = -1, to = 1000) int mWeight = NOT_SPECIFIED;
+        private @IntRange(from = -1, to = 1) int mItalic = NOT_SPECIFIED;
+        private @IntRange(from = 0) int mTtcIndex = 0;
+        private @Nullable FontVariationAxis[] mAxes = null;
+
+        /**
+         * Constructs a builder with a byte buffer.
+         *
+         * Note that only direct buffer can be used as the source of font data.
+         *
+         * @see ByteBuffer#allocateDirect(int)
+         * @param buffer a byte buffer of a font data
+         */
+        public Builder(@NonNull ByteBuffer buffer) {
+            Preconditions.checkNotNull(buffer, "buffer can not be null");
+            if (!buffer.isDirect()) {
+                throw new IllegalArgumentException(
+                        "Only direct buffer can be used as the source of font data.");
+            }
+            mBuffer = buffer;
+        }
+
+        /**
+         * Constructs a builder with a file path.
+         *
+         * @param path a file path to the font file
+         */
+        public Builder(@NonNull File path) throws IOException {
+            Preconditions.checkNotNull(path, "path can not be null");
+            try (FileInputStream fis = new FileInputStream(path)) {
+                final FileChannel fc = fis.getChannel();
+                mBuffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
+            }
+        }
+
+        /**
+         * Constructs a builder with a file descriptor.
+         *
+         * @param fd a file descriptor
+         */
+        public Builder(@NonNull FileDescriptor fd) throws IOException {
+            this(fd, 0, -1);
+        }
+
+        /**
+         * Constructs a builder with a file descriptor.
+         *
+         * @param fd a file descriptor
+         * @param offset an offset to of the font data in the file
+         * @param size a size of the font data. If -1 is passed, use until end of the file.
+         */
+        public Builder(@NonNull FileDescriptor fd, @IntRange(from = 0) long offset,
+                @IntRange(from = -1) long size) throws IOException {
+            try (FileInputStream fis = new FileInputStream(fd)) {
+                final FileChannel fc = fis.getChannel();
+                size = (size == -1) ? fc.size() - offset : size;
+                mBuffer = fc.map(FileChannel.MapMode.READ_ONLY, offset, size);
+            }
+        }
+
+        /**
+         * Constructs a builder from an asset manager and a file path in an asset directory.
+         *
+         * @param am the application's asset manager
+         * @param path the file name of the font data in the asset directory
+         */
+        public Builder(@NonNull AssetManager am, @NonNull String path) throws IOException {
+            final long nativeAsset = nGetNativeAsset(am, path, true /* is asset */, 0 /* cookie */);
+            if (nativeAsset == 0) {
+                throw new FileNotFoundException("Unable to open " + path);
+            }
+            final ByteBuffer b = nGetAssetBuffer(nativeAsset);
+            sAssetByteBufferRegistroy.registerNativeAllocation(b, nativeAsset);
+            if (b == null) {
+                throw new FileNotFoundException(path + " not found");
+            }
+            mBuffer = b;
+        }
+
+        /**
+         * Constructs a builder from resources.
+         *
+         * Resource ID must points the font file. XML font can not be used here.
+         *
+         * @param res the resource of this application.
+         * @param resId the resource ID of font file.
+         */
+        public Builder(@NonNull Resources res, int resId) throws IOException {
+            final TypedValue value = new TypedValue();
+            res.getValue(resId, value, true);
+            if (value.string == null) {
+                throw new FileNotFoundException(resId + " not found");
+            }
+            final String str = value.string.toString();
+            if (str.toLowerCase().endsWith(".xml")) {
+                throw new FileNotFoundException(resId + " must be font file.");
+            }
+            final long nativeAsset = nGetNativeAsset(res.getAssets(), str, false /* is asset */,
+                    value.assetCookie);
+            if (nativeAsset == 0) {
+                throw new FileNotFoundException("Unable to open " + str);
+            }
+            final ByteBuffer b = nGetAssetBuffer(nativeAsset);
+            sAssetByteBufferRegistroy.registerNativeAllocation(b, nativeAsset);
+            if (b == null) {
+                throw new FileNotFoundException(str + " not found");
+            }
+            mBuffer = b;
+        }
+
+        /**
+         * Sets weight of the font.
+         *
+         * Tells the system the weight of the given font. If this function is not called, the system
+         * will resolve the weight value by reading font tables.
+         *
+         * Here are pairs of the common names and their values.
+         * <p>
+         *  <table>
+         *  <thead>
+         *  <tr>
+         *  <th align="center">Value</th>
+         *  <th align="center">Name</th>
+         *  <th align="center">Android Definition</th>
+         *  </tr>
+         *  </thead>
+         *  <tbody>
+         *  <tr>
+         *  <td align="center">100</td>
+         *  <td align="center">Thin</td>
+         *  <td align="center">{@link Font#FONT_WEIGHT_THIN}</td>
+         *  </tr>
+         *  <tr>
+         *  <td align="center">200</td>
+         *  <td align="center">Extra Light (Ultra Light)</td>
+         *  <td align="center">{@link Font#FONT_WEIGHT_EXTRA_LIGHT}</td>
+         *  </tr>
+         *  <tr>
+         *  <td align="center">300</td>
+         *  <td align="center">Light</td>
+         *  <td align="center">{@link Font#FONT_WEIGHT_LIGHT}</td>
+         *  </tr>
+         *  <tr>
+         *  <td align="center">400</td>
+         *  <td align="center">Normal (Regular)</td>
+         *  <td align="center">{@link Font#FONT_WEIGHT_NORMAL}</td>
+         *  </tr>
+         *  <tr>
+         *  <td align="center">500</td>
+         *  <td align="center">Medium</td>
+         *  <td align="center">{@link Font#FONT_WEIGHT_MEDIUM}</td>
+         *  </tr>
+         *  <tr>
+         *  <td align="center">600</td>
+         *  <td align="center">Semi Bold (Demi Bold)</td>
+         *  <td align="center">{@link Font#FONT_WEIGHT_SEMI_BOLD}</td>
+         *  </tr>
+         *  <tr>
+         *  <td align="center">700</td>
+         *  <td align="center">Bold</td>
+         *  <td align="center">{@link Font#FONT_WEIGHT_BOLD}</td>
+         *  </tr>
+         *  <tr>
+         *  <td align="center">800</td>
+         *  <td align="center">Extra Bold (Ultra Bold)</td>
+         *  <td align="center">{@link Font#FONT_WEIGHT_EXTRA_BOLD}</td>
+         *  </tr>
+         *  <tr>
+         *  <td align="center">900</td>
+         *  <td align="center">Black (Heavy)</td>
+         *  <td align="center">{@link Font#FONT_WEIGHT_BLACK}</td>
+         *  </tr>
+         *  </tbody>
+         * </p>
+         *
+         * @see Font#FONT_WEIGHT_THIN
+         * @see Font#FONT_WEIGHT_EXTRA_LIGHT
+         * @see Font#FONT_WEIGHT_LIGHT
+         * @see Font#FONT_WEIGHT_NORMAL
+         * @see Font#FONT_WEIGHT_MEDIUM
+         * @see Font#FONT_WEIGHT_SEMI_BOLD
+         * @see Font#FONT_WEIGHT_BOLD
+         * @see Font#FONT_WEIGHT_EXTRA_BOLD
+         * @see Font#FONT_WEIGHT_BLACK
+         * @param weight a weight value
+         * @return this builder
+         */
+        public @NonNull Builder setWeight(@IntRange(from = 1, to = 1000) int weight) {
+            Preconditions.checkArgument(1 <= weight && weight <= 1000);
+            mWeight = weight;
+            return this;
+        }
+
+        /**
+         * Sets italic information of the font.
+         *
+         * Tells the system the style of the given font. If this function is not called, the system
+         * will resolve the style by reading font tables.
+         *
+         * For example, if you want to use italic font as upright font, call {@code
+         * setItalic(false)} explicitly.
+         *
+         * @param italic {@code true} if the font is italic. Otherwise {@code false}.
+         * @return this builder
+         */
+        public @NonNull Builder setItalic(boolean italic) {
+            mItalic = italic ? STYLE_ITALIC : STYLE_NORMAL;
+            return this;
+        }
+
+        /**
+         * Sets an index of the font collection. See {@link android.R.attr#ttcIndex}.
+         *
+         * @param ttcIndex An index of the font collection. If the font source is not font
+         *                 collection, do not call this method or specify 0.
+         * @return this builder
+         */
+        public @NonNull Builder setTtcIndex(@IntRange(from = 0) int ttcIndex) {
+            mTtcIndex = ttcIndex;
+            return this;
+        }
+
+        /**
+         * Sets the font variation settings.
+         *
+         * @param variationSettings see {@link FontVariationAxis#fromFontVariationSettings(String)}
+         * @return this builder
+         * @throws IllegalArgumentException If given string is not a valid font variation settings
+         *                                  format.
+         */
+        public @NonNull Builder setFontVariationSettings(@Nullable String variationSettings) {
+            mAxes = FontVariationAxis.fromFontVariationSettings(variationSettings);
+            return this;
+        }
+
+        /**
+         * Sets the font variation settings.
+         *
+         * @param axes an array of font variation axis tag-value pairs
+         * @return this builder
+         */
+        public @NonNull Builder setFontVariationSettings(@Nullable FontVariationAxis[] axes) {
+            mAxes = axes;
+            return this;
+        }
+
+        /**
+         * Creates the font based on the configured values.
+         * @return the Font object
+         */
+        public @Nullable Font build() {
+            if (mWeight == NOT_SPECIFIED || mItalic == NOT_SPECIFIED) {
+                final int packed = FontFileUtil.analyzeStyle(mBuffer, mTtcIndex, mAxes);
+                if (FontFileUtil.isSuccess(packed)) {
+                    if (mWeight == NOT_SPECIFIED) {
+                        mWeight = FontFileUtil.unpackWeight(packed);
+                    }
+                    if (mItalic == NOT_SPECIFIED) {
+                        mItalic = FontFileUtil.unpackItalic(packed) ? STYLE_ITALIC : STYLE_NORMAL;
+                    }
+                } else {
+                    mWeight = 400;
+                    mItalic = STYLE_NORMAL;
+                }
+            }
+            final boolean italic = (mItalic == STYLE_ITALIC);
+            final long builderPtr = nInitBuilder();
+            if (mAxes != null) {
+                for (FontVariationAxis axis : mAxes) {
+                    nAddAxis(builderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue());
+                }
+            }
+            final long ptr = nBuild(builderPtr, mBuffer, mWeight, italic, mTtcIndex);
+            final Font font = new Font(ptr, mWeight, italic, mTtcIndex, mAxes);
+            sFontRegistory.registerNativeAllocation(font, ptr);
+            return font;
+        }
+
+        /**
+         * Native methods for accessing underlying buffer in Asset
+         */
+        private static native long nGetNativeAsset(
+                @NonNull AssetManager am, @NonNull String path, boolean isAsset, int cookie);
+        private static native ByteBuffer nGetAssetBuffer(long nativeAsset);
+        @CriticalNative
+        private static native long nGetReleaseNativeAssetFunc();
+
+        /**
+         * Native methods for creating Font
+         */
+        private static native long nInitBuilder();
+        @CriticalNative
+        private static native void nAddAxis(long builderPtr, int tag, float value);
+        private static native long nBuild(
+                long builderPtr, ByteBuffer buffer, int weight, boolean italic, int ttcIndex);
+        @CriticalNative
+        private static native long nGetReleaseNativeFont();
+    }
+
+    private final long mNativePtr;  // address of the shared ptr of minikin::Font
+    private final @IntRange(from = 0, to = 1000) int mWeight;
+    private final boolean mItalic;
+    private final @IntRange(from = 0) int mTtcIndex;
+    private final @Nullable FontVariationAxis[] mAxes;
+
+    /**
+     * Use Builder instead
+     */
+    private Font(long nativePtr, @IntRange(from = 0, to = 1000) int weight, boolean italic,
+            @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] axes) {
+        mWeight = weight;
+        mItalic = italic;
+        mNativePtr = nativePtr;
+        mTtcIndex = ttcIndex;
+        mAxes = axes;
+    }
+
+    /**
+     * Get a weight value associated with this font.
+     *
+     * @see Builder#setWeight(int)
+     * @return a weight value
+     */
+    public @IntRange(from = 0, to = 1000)int getWeight() {
+        return mWeight;
+    }
+
+    /**
+     * Returns true if this font is marked as italic, otherwise returns false.
+     *
+     * @see Builder#setItalic(boolean)
+     * @return true if italic, otherwise false
+     */
+    public boolean isItalic() {
+        return mItalic;
+    }
+
+    /**
+     * Get a TTC index value associated with this font.
+     *
+     * If TTF/OTF file is provided, this value is always 0.
+     *
+     * @see Builder#setTtcIndex(int)
+     * @return a TTC index value
+     */
+    public @IntRange(from = 0) int getTtcIndex() {
+        return mTtcIndex;
+    }
+
+    /**
+     * Get a font variation settings associated with this font
+     *
+     * @see Builder#setFontVariationSettings(String)
+     * @see Builder#setFontVariationSettings(FontVariationAxis[])
+     * @return font variation settings
+     */
+    public @Nullable FontVariationAxis[] getAxes() {
+        return mAxes;
+    }
+
+    /** @hide */
+    public long getNativePtr() {
+        return mNativePtr;
+    }
+
+    @Override
+    public String toString() {
+        return "Font {weight=" + mWeight + ", italic=" + mItalic + "}";
+    }
+}
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
new file mode 100644
index 0000000..74b58ea
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/FontFamily.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 android.graphics.fonts;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * A font family class can be used for creating Typeface.
+ *
+ * <p>
+ * A font family is a bundle of fonts for drawing text in various styles.
+ * For example, you can bundle regular style font and bold style font into a single font family,
+ * then system will select the correct style font from family for drawing.
+ *
+ * <pre>
+ *  FontFamily family = new FontFamily.Builder(new Font.Builder("regular.ttf").build())
+ *      .addFont(new Font.Builder("bold.ttf").build()).build();
+ *  Typeface typeface = new Typeface.Builder2(family).build();
+ *
+ *  SpannableStringBuilder ssb = new SpannableStringBuilder("Hello, World.");
+ *  ssb.setSpan(new StyleSpan(Typeface.Bold), 6, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ *
+ *  textView.setTypeface(typeface);
+ *  textView.setText(ssb);
+ * </pre>
+ *
+ * In this example, "Hello, " is drawn with "regular.ttf", and "World." is drawn with "bold.ttf".
+ *
+ * If there is no font exactly matches with the text style, the system will select the closest font.
+ * </p>
+ *
+ */
+public class FontFamily {
+    private static final String TAG = "FontFamily";
+
+    /**
+     * A builder class for creating new FontFamily.
+     */
+    public static class Builder {
+        private static final NativeAllocationRegistry sFamilyRegistory =
+                new NativeAllocationRegistry(FontFamily.class.getClassLoader(),
+                    nGetReleaseNativeFamily(), 64);
+
+        private final ArrayList<Font> mFonts = new ArrayList<>();
+        private final HashSet<Integer> mStyleHashSet = new HashSet<>();
+
+        /**
+         * Constructs a builder.
+         *
+         * @param font a font
+         */
+        public Builder(@NonNull Font font) {
+            Preconditions.checkNotNull(font, "font can not be null");
+            mStyleHashSet.add(makeStyleIdentifier(font));
+            mFonts.add(font);
+        }
+
+        /**
+         * Adds different style font to the builder.
+         *
+         * System will select the font if the text style is closest to the font.
+         * If the same style font is already added to the builder, this method will fail with
+         * {@link IllegalArgumentException}.
+         *
+         * Note that system assumes all fonts bundled in FontFamily have the same coverage for the
+         * code points. For example, regular style font and bold style font must have the same code
+         * point coverage, otherwise some character may be shown as tofu.
+         *
+         * @param font a font
+         * @return this builder
+         */
+        public @NonNull Builder addFont(@NonNull Font font) {
+            Preconditions.checkNotNull(font, "font can not be null");
+            if (!mStyleHashSet.add(makeStyleIdentifier(font))) {
+                throw new IllegalArgumentException(font + " has already been added");
+            }
+            mFonts.add(font);
+            return this;
+        }
+
+        /**
+         * Build the font family
+         * @return a font family
+         */
+        public @NonNull FontFamily build() {
+            final long builderPtr = nInitBuilder();
+            for (int i = 0; i < mFonts.size(); ++i) {
+                nAddFont(builderPtr, mFonts.get(i).getNativePtr());
+            }
+            final long ptr = nBuild(builderPtr);
+            final FontFamily family = new FontFamily(mFonts, ptr);
+            sFamilyRegistory.registerNativeAllocation(family, ptr);
+            return family;
+        }
+
+        private static int makeStyleIdentifier(@NonNull Font font) {
+            return font.getWeight() | (font.isItalic() ? (1 << 16) : 0);
+        }
+
+        private static native long nInitBuilder();
+        @CriticalNative
+        private static native void nAddFont(long builderPtr, long fontPtr);
+        private static native long nBuild(long builderPtr);
+        @CriticalNative
+        private static native long nGetReleaseNativeFamily();
+    }
+
+    private final ArrayList<Font> mFonts;
+    private final long mNativePtr;
+
+    // Use Builder instead.
+    private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
+        mFonts = fonts;
+        mNativePtr = ptr;
+    }
+
+    /**
+     * Returns a font
+     *
+     * @param index an index of the font
+     * @return a registered font
+     */
+    public Font getFont(@IntRange(from = 0) int index) {
+        return mFonts.get(index);
+    }
+
+    /**
+     * Returns the number of fonts in this FontFamily.
+     *
+     * @return the number of fonts registered in this family.
+     */
+    public int getFontCount() {
+        return mFonts.size();
+    }
+
+    /** @hide */
+    public long getNativePtr() {
+        return mNativePtr;
+    }
+}
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
new file mode 100644
index 0000000..f8b456b
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -0,0 +1,134 @@
+/*
+ * 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.graphics.fonts;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Provides a utility for font file operations.
+ * @hide
+ */
+public class FontFileUtil {
+
+    private FontFileUtil() {}  // Do not instanciate
+
+    /**
+     * Unpack the weight value from packed integer.
+     */
+    public static int unpackWeight(int packed) {
+        return packed & 0xFFFF;
+    }
+
+    /**
+     * Unpack the italic value from packed integer.
+     */
+    public static boolean unpackItalic(int packed) {
+        return (packed & 0x10000) != 0;
+    }
+
+    /**
+     * Returns true if the analyzeStyle succeeded
+     */
+    public static boolean isSuccess(int packed) {
+        return packed != ANALYZE_ERROR;
+    }
+
+    private static int pack(@IntRange(from = 0, to = 1000) int weight, boolean italic) {
+        return weight | (italic ? 0x10000 : 0);
+    }
+
+    private static final int SFNT_VERSION_1 = 0x00010000;
+    private static final int SFNT_VERSION_OTTO = 0x4F54544F;
+    private static final int TTC_TAG = 0x74746366;
+    private static final int OS2_TABLE_TAG = 0x4F532F32;
+
+    private static final int ANALYZE_ERROR = 0xFFFFFFFF;
+
+    /**
+     * Analyze the font file returns packed style info
+     */
+    public static final int analyzeStyle(@NonNull ByteBuffer buffer,
+            @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] varSettings) {
+        int weight = -1;
+        int italic = -1;
+        if (varSettings != null) {
+            for (FontVariationAxis axis :varSettings) {
+                if ("wght".equals(axis.getTag())) {
+                    weight = (int) axis.getStyleValue();
+                } else if ("ital".equals(axis.getTag())) {
+                    italic = (axis.getStyleValue() == 1.0f) ? 1 : 0;
+                }
+            }
+        }
+
+        if (weight != -1 && italic != -1) {
+            // Both weight/italic style are specifeid by variation settings.
+            // No need to look into OS/2 table.
+            // TODO: Good to look HVAR table to check if this font supports wght/ital axes.
+            return pack(weight, italic == 1);
+        }
+
+        ByteOrder originalOrder = buffer.order();
+        buffer.order(ByteOrder.BIG_ENDIAN);
+        try {
+            int fontFileOffset = 0;
+            int magicNumber = buffer.getInt(0);
+            if (magicNumber == TTC_TAG) {
+                // TTC file.
+                if (ttcIndex >= buffer.getInt(8 /* offset to number of fonts in TTC */)) {
+                    return ANALYZE_ERROR;
+                }
+                fontFileOffset = buffer.getInt(
+                    12 /* offset to array of offsets of font files */ + 4 * ttcIndex);
+            }
+            int sfntVersion = buffer.getInt(fontFileOffset);
+
+            if (sfntVersion != SFNT_VERSION_1 && sfntVersion != SFNT_VERSION_OTTO) {
+                return ANALYZE_ERROR;
+            }
+
+            int numTables = buffer.getShort(fontFileOffset + 4 /* offset to number of tables */);
+            int os2TableOffset = -1;
+            for (int i = 0; i < numTables; ++i) {
+                int tableOffset = fontFileOffset + 12 /* size of offset table */
+                        + i * 16 /* size of table record */;
+                if (buffer.getInt(tableOffset) == OS2_TABLE_TAG) {
+                    os2TableOffset = buffer.getInt(tableOffset + 8 /* offset to the table */);
+                    break;
+                }
+            }
+
+            if (os2TableOffset == -1) {
+                // Couldn't find OS/2 table. use regular style
+                return pack(400, false);
+            }
+
+            int weightFromOS2 = buffer.getShort(os2TableOffset + 4 /* offset to weight class */);
+            boolean italicFromOS2 =
+                    (buffer.getShort(os2TableOffset + 62 /* offset to fsSelection */) & 1) != 0;
+            return pack(weight == -1 ? weightFromOS2 : weight,
+                    italic == -1 ? italicFromOS2 : italic == 1);
+        } finally {
+            buffer.order(originalOrder);
+        }
+    }
+}
diff --git a/graphics/java/android/graphics/fonts/FontVariationAxis.java b/graphics/java/android/graphics/fonts/FontVariationAxis.java
index 1b7408a..2a902c5 100644
--- a/graphics/java/android/graphics/fonts/FontVariationAxis.java
+++ b/graphics/java/android/graphics/fonts/FontVariationAxis.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.text.TextUtils;
 
 import java.util.ArrayList;
@@ -27,8 +28,10 @@
  * Class that holds information about single font variation axis.
  */
 public final class FontVariationAxis {
+    @UnsupportedAppUsage
     private final int mTag;
     private final String mTagString;
+    @UnsupportedAppUsage
     private final float mStyleValue;
 
     /**
diff --git a/graphics/java/android/graphics/pdf/PdfRenderer.java b/graphics/java/android/graphics/pdf/PdfRenderer.java
index 4a91705..1836f00 100644
--- a/graphics/java/android/graphics/pdf/PdfRenderer.java
+++ b/graphics/java/android/graphics/pdf/PdfRenderer.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
 import android.graphics.Matrix;
@@ -118,6 +119,7 @@
 
     private ParcelFileDescriptor mInput;
 
+    @UnsupportedAppUsage
     private Page mCurrentPage;
 
     /** @hide */
@@ -242,6 +244,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     private void doClose() {
         if (mCurrentPage != null) {
             mCurrentPage.close();
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 030fa60..78dbb6a 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -283,10 +283,16 @@
     public static final int KEY_GEN_NO_KEYSTORE_PROVIDER = 5;
 
     /**
+     * StrongBox unavailable when calling {@link #generateKeyPair}
+     * @hide
+     */
+    public static final int KEY_GEN_STRONGBOX_UNAVAILABLE = 6;
+
+    /**
      * General failure while calling {@link #generateKeyPair}
      * @hide
      */
-    public static final int KEY_GEN_FAILURE = 6;
+    public static final int KEY_GEN_FAILURE = 7;
 
     /**
      * Successful call to {@link #attestKey}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index fe05c13..4f4ca3f 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -128,7 +128,11 @@
     public static final int FLAG_STRONGBOX = 1 << 4;
 
     // States
-    public enum State { UNLOCKED, LOCKED, UNINITIALIZED };
+    public enum State {
+        UNLOCKED,
+        LOCKED,
+        UNINITIALIZED
+    };
 
     private int mError = NO_ERROR;
 
@@ -190,22 +194,33 @@
     }
 
     public byte[] get(String key, int uid) {
-        try {
-            key = key != null ? key : "";
-            return mBinder.get(key, uid);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Cannot connect to keystore", e);
-            return null;
-        } catch (android.os.ServiceSpecificException e) {
-            Log.w(TAG, "KeyStore exception", e);
-            return null;
-        }
+        return get(key, uid, false);
     }
 
     public byte[] get(String key) {
         return get(key, UID_SELF);
     }
 
+    public byte[] get(String key, int uid, boolean suppressKeyNotFoundWarning) {
+        try {
+            key = key != null ? key : "";
+            return mBinder.get(key, uid);
+        } catch (RemoteException e) {
+             Log.w(TAG, "Cannot connect to keystore", e);
+            return null;
+        } catch (android.os.ServiceSpecificException e) {
+            if (!suppressKeyNotFoundWarning || e.errorCode != KEY_NOT_FOUND) {
+                Log.w(TAG, "KeyStore exception", e);
+            }
+            return null;
+        }
+    }
+
+    public byte[] get(String key, boolean suppressKeyNotFoundWarning) {
+        return get(key, UID_SELF, suppressKeyNotFoundWarning);
+    }
+
+
     public boolean put(String key, byte[] value, int uid, int flags) {
         return insert(key, value, uid, flags) == NO_ERROR;
     }
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 2b5a37b..4c007cb 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -811,7 +811,7 @@
         }
 
         int errorCode = mKeyStore.importWrappedKey(
-            Credentials.USER_SECRET_KEY + alias,
+            Credentials.USER_PRIVATE_KEY + alias,
             entry.getWrappedKeyBytes(),
             Credentials.USER_PRIVATE_KEY + entry.getWrappingKeyAlias(),
             maskingKey,
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 452024c..89d370f 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -268,6 +268,11 @@
     private final boolean mIsStrongBoxBacked;
     private final boolean mUserConfirmationRequired;
     private final boolean mUnlockedDeviceRequired;
+    /*
+     * ***NOTE***: All new fields MUST also be added to the following:
+     * ParcelableKeyGenParameterSpec class.
+     * The KeyGenParameterSpec.Builder constructor that takes a KeyGenParameterSpec
+     */
 
     /**
      * @hide should be built with Builder
@@ -791,6 +796,9 @@
             mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded();
             mUserAuthenticationValidWhileOnBody = sourceSpec.isUserAuthenticationValidWhileOnBody();
             mInvalidatedByBiometricEnrollment = sourceSpec.isInvalidatedByBiometricEnrollment();
+            mIsStrongBoxBacked = sourceSpec.isStrongBoxBacked();
+            mUserConfirmationRequired = sourceSpec.isUserConfirmationRequired();
+            mUnlockedDeviceRequired = sourceSpec.isUnlockedDeviceRequired();
         }
 
         /**
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 911bbf8..8231dc9 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -97,11 +97,14 @@
         out.writeBoolean(mSpec.isRandomizedEncryptionRequired());
         out.writeBoolean(mSpec.isUserAuthenticationRequired());
         out.writeInt(mSpec.getUserAuthenticationValidityDurationSeconds());
+        out.writeBoolean(mSpec.isUserPresenceRequired());
         out.writeByteArray(mSpec.getAttestationChallenge());
         out.writeBoolean(mSpec.isUniqueIdIncluded());
         out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody());
         out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment());
-        out.writeBoolean(mSpec.isUserPresenceRequired());
+        out.writeBoolean(mSpec.isStrongBoxBacked());
+        out.writeBoolean(mSpec.isUserConfirmationRequired());
+        out.writeBoolean(mSpec.isUnlockedDeviceRequired());
     }
 
     private static Date readDateOrNull(Parcel in) {
@@ -114,19 +117,12 @@
     }
 
     private ParcelableKeyGenParameterSpec(Parcel in) {
-        String keystoreAlias = in.readString();
-        int purposes = in.readInt();
-        KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
-                keystoreAlias, purposes);
-        builder.setUid(in.readInt());
-        // KeySize is -1 by default, if the KeyGenParameterSpec previously parcelled had the default
-        // value, do not set it as this will cause setKeySize to throw.
-        int keySize = in.readInt();
-        if (keySize >= 0) {
-            builder.setKeySize(keySize);
-        }
+        final String keystoreAlias = in.readString();
+        final int purposes = in.readInt();
+        final int uid = in.readInt();
+        final int keySize = in.readInt();
 
-        int keySpecType = in.readInt();
+        final int keySpecType = in.readInt();
         AlgorithmParameterSpec algorithmSpec = null;
         if (keySpecType == ALGORITHM_PARAMETER_SPEC_NONE) {
             algorithmSpec = null;
@@ -141,32 +137,60 @@
             throw new IllegalArgumentException(
                     String.format("Unknown algorithm parameter spec: %d", keySpecType));
         }
-        if (algorithmSpec != null) {
-            builder.setAlgorithmParameterSpec(algorithmSpec);
-        }
-        builder.setCertificateSubject(new X500Principal(in.createByteArray()));
-        builder.setCertificateSerialNumber(new BigInteger(in.createByteArray()));
-        builder.setCertificateNotBefore(new Date(in.readLong()));
-        builder.setCertificateNotAfter(new Date(in.readLong()));
-        builder.setKeyValidityStart(readDateOrNull(in));
-        builder.setKeyValidityForOriginationEnd(readDateOrNull(in));
-        builder.setKeyValidityForConsumptionEnd(readDateOrNull(in));
-        String[] digests = in.createStringArray();
-        if (digests != null) {
-            builder.setDigests(digests);
-        }
-        builder.setEncryptionPaddings(in.createStringArray());
-        builder.setSignaturePaddings(in.createStringArray());
-        builder.setBlockModes(in.createStringArray());
-        builder.setRandomizedEncryptionRequired(in.readBoolean());
-        builder.setUserAuthenticationRequired(in.readBoolean());
-        builder.setUserAuthenticationValidityDurationSeconds(in.readInt());
-        builder.setAttestationChallenge(in.createByteArray());
-        builder.setUniqueIdIncluded(in.readBoolean());
-        builder.setUserAuthenticationValidWhileOnBody(in.readBoolean());
-        builder.setInvalidatedByBiometricEnrollment(in.readBoolean());
-        builder.setUserPresenceRequired(in.readBoolean());
-        mSpec = builder.build();
+
+        final X500Principal certificateSubject = new X500Principal(in.createByteArray());
+        final BigInteger certificateSerialNumber = new BigInteger(in.createByteArray());
+        final Date certificateNotBefore = new Date(in.readLong());
+        final Date certificateNotAfter = new Date(in.readLong());
+        final Date keyValidityStartDate = readDateOrNull(in);
+        final Date keyValidityForOriginationEnd = readDateOrNull(in);
+        final Date keyValidityForConsumptionEnd = readDateOrNull(in);
+        final String[] digests = in.createStringArray();
+        final String[] encryptionPaddings = in.createStringArray();
+        final String[] signaturePaddings = in.createStringArray();
+        final String[] blockModes = in.createStringArray();
+        final boolean randomizedEncryptionRequired = in.readBoolean();
+        final boolean userAuthenticationRequired = in.readBoolean();
+        final int userAuthenticationValidityDurationSeconds = in.readInt();
+        final boolean userPresenceRequired = in.readBoolean();
+        final byte[] attestationChallenge = in.createByteArray();
+        final boolean uniqueIdIncluded = in.readBoolean();
+        final boolean userAuthenticationValidWhileOnBody = in.readBoolean();
+        final boolean invalidatedByBiometricEnrollment = in.readBoolean();
+        final boolean isStrongBoxBacked = in.readBoolean();
+        final boolean userConfirmationRequired = in.readBoolean();
+        final boolean unlockedDeviceRequired = in.readBoolean();
+        // The KeyGenParameterSpec is intentionally not constructed using a Builder here:
+        // The intention is for this class to break if new parameters are added to the
+        // KeyGenParameterSpec constructor (whereas using a builder would silently drop them).
+        mSpec = new KeyGenParameterSpec(
+                keystoreAlias,
+                uid,
+                keySize,
+                algorithmSpec,
+                certificateSubject,
+                certificateSerialNumber,
+                certificateNotBefore,
+                certificateNotAfter,
+                keyValidityStartDate,
+                keyValidityForOriginationEnd,
+                keyValidityForConsumptionEnd,
+                purposes,
+                digests,
+                encryptionPaddings,
+                signaturePaddings,
+                blockModes,
+                randomizedEncryptionRequired,
+                userAuthenticationRequired,
+                userAuthenticationValidityDurationSeconds,
+                userPresenceRequired,
+                attestationChallenge,
+                uniqueIdIncluded,
+                userAuthenticationValidWhileOnBody,
+                invalidatedByBiometricEnrollment,
+                isStrongBoxBacked,
+                userConfirmationRequired,
+                unlockedDeviceRequired);
     }
 
     public static final Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
index 254b6be..32f8ec4 100644
--- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -77,6 +77,9 @@
                 .setUniqueIdIncluded(true)
                 .setUserAuthenticationValidWhileOnBody(true)
                 .setInvalidatedByBiometricEnrollment(true)
+                .setIsStrongBoxBacked(true)
+                .setUserConfirmationRequired(true)
+                .setUnlockedDeviceRequired(true)
                 .build();
     }
 
@@ -105,6 +108,9 @@
         assertThat(spec.isUniqueIdIncluded(), is(true));
         assertThat(spec.isUserAuthenticationValidWhileOnBody(), is(true));
         assertThat(spec.isInvalidatedByBiometricEnrollment(), is(true));
+        assertThat(spec.isStrongBoxBacked(), is(true));
+        assertThat(spec.isUserConfirmationRequired(), is(true));
+        assertThat(spec.isUnlockedDeviceRequired(), is(true));
     }
 
     private Parcel parcelForReading(ParcelableKeyGenParameterSpec spec) {
diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp
index d8adbe5..8fc3219 100644
--- a/libs/androidfw/ChunkIterator.cpp
+++ b/libs/androidfw/ChunkIterator.cpp
@@ -32,11 +32,30 @@
 
   if (len_ != 0) {
     // Prepare the next chunk.
-    VerifyNextChunk();
+    if (VerifyNextChunkNonFatal()) {
+      VerifyNextChunk();
+    }
   }
   return Chunk(this_chunk);
 }
 
+// TODO(b/111401637) remove this and have full resource file verification
+// Returns false if there was an error.
+bool ChunkIterator::VerifyNextChunkNonFatal() {
+  if (len_ < sizeof(ResChunk_header)) {
+    last_error_ = "not enough space for header";
+    last_error_was_fatal_ = false;
+    return false;
+  }
+  const size_t size = dtohl(next_chunk_->size);
+  if (size > len_) {
+    last_error_ = "chunk size is bigger than given data";
+    last_error_was_fatal_ = false;
+    return false;
+  }
+  return true;
+}
+
 // Returns false if there was an error.
 bool ChunkIterator::VerifyNextChunk() {
   const uintptr_t header_start = reinterpret_cast<uintptr_t>(next_chunk_);
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 04d506a..c2740c9 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -560,7 +560,9 @@
 
   if (iter.HadError()) {
     LOG(ERROR) << iter.GetLastError();
-    return {};
+    if (iter.HadFatalError()) {
+      return {};
+    }
   }
 
   // Flatten and construct the TypeSpecs.
@@ -641,7 +643,9 @@
 
   if (iter.HadError()) {
     LOG(ERROR) << iter.GetLastError();
-    return false;
+    if (iter.HadFatalError()) {
+      return false;
+    }
   }
   return true;
 }
@@ -673,7 +677,9 @@
 
   if (iter.HadError()) {
     LOG(ERROR) << iter.GetLastError();
-    return {};
+    if (iter.HadFatalError()) {
+      return {};
+    }
   }
 
   // Need to force a move for mingw32.
diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h
index 89b588e..99a52dc 100644
--- a/libs/androidfw/include/androidfw/Chunk.h
+++ b/libs/androidfw/include/androidfw/Chunk.h
@@ -94,18 +94,27 @@
 
   Chunk Next();
   inline bool HasNext() const { return !HadError() && len_ != 0; };
+  // Returns whether there was an error and processing should stop
   inline bool HadError() const { return last_error_ != nullptr; }
   inline std::string GetLastError() const { return last_error_; }
+  // Returns whether there was an error and processing should stop. For legacy purposes,
+  // some errors are considered "non fatal". Fatal errors stop processing new chunks and
+  // throw away any chunks already processed. Non fatal errors also stop processing new
+  // chunks, but, will retain and use any valid chunks already processed.
+  inline bool HadFatalError() const { return HadError() && last_error_was_fatal_; }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ChunkIterator);
 
   // Returns false if there was an error.
   bool VerifyNextChunk();
+  // Returns false if there was an error. For legacy purposes.
+  bool VerifyNextChunkNonFatal();
 
   const ResChunk_header* next_chunk_;
   size_t len_;
   const char* last_error_;
+  bool last_error_was_fatal_ = true;
 };
 
 }  // namespace android
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0db7799..83e90b6 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -48,6 +48,11 @@
         device_uses_hwc2: {
             cflags: ["-DUSE_HWC2"],
         },
+        eng: {
+            lto: {
+                never: true,
+            },
+        },
     },
 }
 
@@ -67,7 +72,6 @@
         "libft2",
         "libminikin",
         "libandroidfw",
-        "libRScpp",
     ],
     static_libs: [
         "libEGL_blobCache",
@@ -198,6 +202,7 @@
         "AnimatorManager.cpp",
         "Caches.cpp",
         "CanvasState.cpp",
+        "CanvasTransform.cpp",
         "ClipArea.cpp",
         "DamageAccumulator.cpp",
         "DeferredLayerUpdater.cpp",
@@ -224,7 +229,6 @@
         "RenderProperties.cpp",
         "ResourceCache.cpp",
         "SkiaCanvas.cpp",
-        "SkiaCanvasProxy.cpp",
         "Snapshot.cpp",
         "Texture.cpp",
         "VectorDrawable.cpp",
@@ -237,7 +241,6 @@
     },
 
     export_include_dirs: ["."],
-    export_shared_lib_headers: ["libRScpp"],
 }
 
 cc_library {
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
new file mode 100644
index 0000000..1b15dbd
--- /dev/null
+++ b/libs/hwui/CanvasTransform.cpp
@@ -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.
+ */
+
+#include "CanvasTransform.h"
+#include "utils/Color.h"
+#include "Properties.h"
+
+#include <ui/ColorSpace.h>
+#include <SkColorFilter.h>
+#include <SkPaint.h>
+
+#include <algorithm>
+#include <cmath>
+
+namespace android::uirenderer {
+
+static SkColor makeLight(SkColor color) {
+    Lab lab = sRGBToLab(color);
+    float invertedL = std::min(110 - lab.L, 100.0f);
+    if (invertedL > lab.L) {
+        lab.L = invertedL;
+        return LabToSRGB(lab, SkColorGetA(color));
+    } else {
+        return color;
+    }
+}
+
+static SkColor makeDark(SkColor color) {
+    Lab lab = sRGBToLab(color);
+    float invertedL = std::min(110 - lab.L, 100.0f);
+    if (invertedL < lab.L) {
+        lab.L = invertedL;
+        return LabToSRGB(lab, SkColorGetA(color));
+    } else {
+        return color;
+    }
+}
+
+static SkColor transformColor(ColorTransform transform, SkColor color) {
+    switch (transform) {
+        case ColorTransform::Light:
+            return makeLight(color);
+        case ColorTransform::Dark:
+            return makeDark(color);
+        default:
+            return color;
+    }
+}
+
+static void applyColorTransform(ColorTransform transform, SkPaint& paint) {
+    if (transform == ColorTransform::None) return;
+
+    SkColor newColor = transformColor(transform, paint.getColor());
+    paint.setColor(newColor);
+
+    if (paint.getColorFilter()) {
+        SkBlendMode mode;
+        SkColor color;
+        // TODO: LRU this or something to avoid spamming new color mode filters
+        if (paint.getColorFilter()->asColorMode(&color, &mode)) {
+            color = transformColor(transform, color);
+            paint.setColorFilter(SkColorFilter::MakeModeFilter(color, mode));
+        }
+    }
+}
+
+class ColorFilterCanvas : public SkPaintFilterCanvas {
+public:
+    ColorFilterCanvas(ColorTransform transform, SkCanvas* canvas)
+            : SkPaintFilterCanvas(canvas), mTransform(transform) {}
+
+    bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type type) const override {
+        if (*paint) {
+            applyColorTransform(mTransform, *(paint->writable()));
+        }
+        return true;
+    }
+
+private:
+    ColorTransform mTransform;
+};
+
+std::unique_ptr<SkCanvas> makeTransformCanvas(SkCanvas* inCanvas, ColorTransform transform) {
+    switch (transform) {
+        case ColorTransform::Light:
+            return std::make_unique<ColorFilterCanvas>(ColorTransform::Light, inCanvas);
+        case ColorTransform::Dark:
+            return std::make_unique<ColorFilterCanvas>(ColorTransform::Dark, inCanvas);
+        default:
+            return nullptr;
+    }
+}
+
+std::unique_ptr<SkCanvas> makeTransformCanvas(SkCanvas* inCanvas, UsageHint usageHint) {
+    if (Properties::forceDarkMode) {
+        switch (usageHint) {
+            case UsageHint::Unknown:
+                return makeTransformCanvas(inCanvas, ColorTransform::Light);
+            case UsageHint::Background:
+                return makeTransformCanvas(inCanvas, ColorTransform::Dark);
+        }
+    }
+    return nullptr;
+}
+
+};  // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/CanvasTransform.h b/libs/hwui/CanvasTransform.h
new file mode 100644
index 0000000..f71fdfa
--- /dev/null
+++ b/libs/hwui/CanvasTransform.h
@@ -0,0 +1,40 @@
+/*
+ * 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 <SkCanvas.h>
+#include <SkPaintFilterCanvas.h>
+
+#include <memory>
+
+namespace android::uirenderer {
+
+enum class UsageHint {
+    Unknown = 0,
+    Background = 1,
+};
+
+enum class ColorTransform {
+    None,
+    Light,
+    Dark,
+};
+
+std::unique_ptr<SkCanvas> makeTransformCanvas(SkCanvas* inCanvas, ColorTransform transform);
+std::unique_ptr<SkCanvas> makeTransformCanvas(SkCanvas* inCanvas, UsageHint usageHint);
+
+}  // namespace android::uirenderer;
\ No newline at end of file
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 6408ce6..ab80d3d 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -286,7 +286,7 @@
         eglDestroySyncKHR(display, fence);
     }
 
-    return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
+    return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap)));
 }
 
 };  // namespace android::uirenderer
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index d284269..17bec19 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -17,6 +17,7 @@
 #include "Properties.h"
 #include "Debug.h"
 #include "DeviceInfo.h"
+#include "SkTraceEventCommon.h"
 
 #include <algorithm>
 #include <cstdlib>
@@ -59,6 +60,7 @@
 bool Properties::filterOutTestOverhead = false;
 bool Properties::disableVsync = false;
 bool Properties::skpCaptureEnabled = false;
+bool Properties::forceDarkMode = false;
 bool Properties::enableRTAnimations = true;
 
 bool Properties::runningInEmulator = false;
@@ -140,8 +142,13 @@
 
     skpCaptureEnabled = debuggingEnabled && property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false);
 
+    SkAndroidFrameworkTraceUtil::setEnableTracing(
+            property_get_bool(PROPERTY_SKIA_ATRACE_ENABLED, false));
+
     runningInEmulator = property_get_bool(PROPERTY_QEMU_KERNEL, false);
 
+    forceDarkMode = property_get_bool(PROPERTY_FORCE_DARK, false);
+
     return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw) ||
            (prevDebugStencilClip != debugStencilClip);
 }
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 657f9aa..ea017a7 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -171,6 +171,11 @@
 #define PROPERTY_CAPTURE_SKP_ENABLED "debug.hwui.capture_skp_enabled"
 
 /**
+ * Allows to record Skia drawing commands with systrace.
+ */
+#define PROPERTY_SKIA_ATRACE_ENABLED "debug.hwui.skia_atrace_enabled"
+
+/**
  * Defines how many frames in a sequence to capture.
  */
 #define PROPERTY_CAPTURE_SKP_FRAMES "debug.hwui.capture_skp_frames"
@@ -185,6 +190,8 @@
  */
 #define PROPERTY_QEMU_KERNEL "ro.kernel.qemu"
 
+#define PROPERTY_FORCE_DARK "debug.hwui.force_dark"
+
 ///////////////////////////////////////////////////////////////////////////////
 // Misc
 ///////////////////////////////////////////////////////////////////////////////
@@ -258,6 +265,7 @@
     static bool disableVsync;
 
     static bool skpCaptureEnabled;
+    static bool forceDarkMode;
 
     // For experimentation b/68769804
     ANDROID_API static bool enableRTAnimations;
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index dc962f3..8393288 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -28,6 +28,7 @@
 #include <androidfw/ResourceTypes.h>
 
 #include "AnimatorManager.h"
+#include "CanvasTransform.h"
 #include "Debug.h"
 #include "DisplayList.h"
 #include "Matrix.h"
@@ -208,6 +209,14 @@
 
     void output(std::ostream& output, uint32_t level);
 
+    void setUsageHint(UsageHint usageHint) {
+        mUsageHint = usageHint;
+    }
+
+    UsageHint usageHint() const {
+        return mUsageHint;
+    }
+
 private:
     void computeOrderingImpl(RenderNodeOp* opState,
                              std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
@@ -263,6 +272,8 @@
 
     sp<PositionListener> mPositionListener;
 
+    UsageHint mUsageHint = UsageHint::Unknown;
+
     // METHODS & FIELDS ONLY USED BY THE SKIA RENDERER
 public:
     /**
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index e495744..ff9cf45 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -44,8 +44,8 @@
 }
 
 bool LayerProperties::setColorFilter(SkColorFilter* filter) {
-    if (mColorFilter == filter) return false;
-    SkRefCnt_SafeAssign(mColorFilter, filter);
+    if (mColorFilter.get() == filter) return false;
+    mColorFilter = sk_ref_sp(filter);
     return true;
 }
 
@@ -62,7 +62,7 @@
     setOpaque(other.opaque());
     setAlpha(other.alpha());
     setXferMode(other.xferMode());
-    setColorFilter(other.colorFilter());
+    setColorFilter(other.getColorFilter());
     return *this;
 }
 
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index c024373..0766e3b 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -89,9 +89,7 @@
 
     SkBlendMode xferMode() const { return mMode; }
 
-    bool setColorFilter(SkColorFilter* filter);
-
-    SkColorFilter* colorFilter() const { return mColorFilter; }
+    SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
 
     // Sets alpha, xfermode, and colorfilter from an SkPaint
     // paint may be NULL, in which case defaults will be set
@@ -105,6 +103,7 @@
     LayerProperties();
     ~LayerProperties();
     void reset();
+    bool setColorFilter(SkColorFilter* filter);
 
     // Private since external users should go through properties().effectiveLayerType()
     LayerType type() const { return mType; }
@@ -116,7 +115,7 @@
     bool mOpaque;
     uint8_t mAlpha;
     SkBlendMode mMode;
-    SkColorFilter* mColorFilter = nullptr;
+    sk_sp<SkColorFilter> mColorFilter;
 };
 
 /*
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 7b41f89..17f1a3b 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -21,6 +21,7 @@
 #include "VectorDrawable.h"
 #include "hwui/Bitmap.h"
 #include "hwui/MinikinUtils.h"
+#include "hwui/PaintFilter.h"
 #include "pipeline/skia/AnimatedDrawables.h"
 
 #include <SkAnimatedImage.h>
@@ -28,7 +29,6 @@
 #include <SkColorFilter.h>
 #include <SkColorSpaceXformCanvas.h>
 #include <SkDeque.h>
-#include <SkDrawFilter.h>
 #include <SkDrawable.h>
 #include <SkGraphics.h>
 #include <SkImage.h>
@@ -40,6 +40,8 @@
 #include <SkTextBlob.h>
 
 #include <memory>
+#include <optional>
+#include <utility>
 
 namespace android {
 
@@ -211,7 +213,7 @@
     Clip(const SkRRect& rrect, SkClipOp op, const SkMatrix& m)
             : mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {}
     Clip(const SkPath& path, SkClipOp op, const SkMatrix& m)
-            : mType(Type::Path), mOp(op), mMatrix(m), mPath(&path) {}
+            : mType(Type::Path), mOp(op), mMatrix(m), mPath(std::in_place, path) {}
 
     void apply(SkCanvas* canvas) const {
         canvas->setMatrix(mMatrix);
@@ -223,7 +225,7 @@
                 canvas->clipRRect(mRRect, mOp);
                 break;
             case Type::Path:
-                canvas->clipPath(*mPath.get(), mOp);
+                canvas->clipPath(mPath.value(), mOp);
                 break;
         }
     }
@@ -240,7 +242,7 @@
     SkMatrix mMatrix;
 
     // These are logically a union (tracked separately due to non-POD path).
-    SkTLazy<SkPath> mPath;
+    std::optional<SkPath> mPath;
     SkRRect mRRect;
 };
 
@@ -400,12 +402,12 @@
 // Canvas state operations: Filters
 // ----------------------------------------------------------------------------
 
-SkDrawFilter* SkiaCanvas::getDrawFilter() {
-    return mCanvas->getDrawFilter();
+PaintFilter* SkiaCanvas::getPaintFilter() {
+    return mPaintFilter.get();
 }
 
-void SkiaCanvas::setDrawFilter(SkDrawFilter* drawFilter) {
-    mCanvas->setDrawFilter(drawFilter);
+void SkiaCanvas::setPaintFilter(sk_sp<PaintFilter> paintFilter) {
+    mPaintFilter = std::move(paintFilter);
 }
 
 // ----------------------------------------------------------------------------
@@ -439,8 +441,15 @@
     mCanvas->drawColor(color, mode);
 }
 
+SkiaCanvas::PaintCoW&& SkiaCanvas::filterPaint(PaintCoW&& paint) const {
+    if (mPaintFilter) {
+        mPaintFilter->filter(&paint.writeable());
+    }
+    return std::move(paint);
+}
+
 void SkiaCanvas::drawPaint(const SkPaint& paint) {
-    mCanvas->drawPaint(paint);
+    mCanvas->drawPaint(*filterPaint(paint));
 }
 
 // ----------------------------------------------------------------------------
@@ -457,53 +466,53 @@
         pts[i].set(points[0], points[1]);
         points += 2;
     }
-    mCanvas->drawPoints(mode, count, pts.get(), paint);
+    mCanvas->drawPoints(mode, count, pts.get(), *filterPaint(paint));
 }
 
 void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) {
-    mCanvas->drawPoint(x, y, paint);
+    mCanvas->drawPoint(x, y, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
-    this->drawPoints(points, count, paint, SkCanvas::kPoints_PointMode);
+    this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kPoints_PointMode);
 }
 
 void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY,
                           const SkPaint& paint) {
-    mCanvas->drawLine(startX, startY, stopX, stopY, paint);
+    mCanvas->drawLine(startX, startY, stopX, stopY, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
     if (CC_UNLIKELY(count < 4 || paint.nothingToDraw())) return;
-    this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
+    this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kLines_PointMode);
 }
 
 void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
-    mCanvas->drawRect({left, top, right, bottom}, paint);
+    mCanvas->drawRect({left, top, right, bottom}, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
-    mCanvas->drawRegion(region, paint);
+    mCanvas->drawRegion(region, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
                                const SkPaint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
-    mCanvas->drawRoundRect(rect, rx, ry, paint);
+    mCanvas->drawRoundRect(rect, rx, ry, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
     if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
-    mCanvas->drawCircle(x, y, radius, paint);
+    mCanvas->drawCircle(x, y, radius, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
-    mCanvas->drawOval(oval, paint);
+    mCanvas->drawOval(oval, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle,
@@ -511,9 +520,9 @@
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
     if (fabs(sweepAngle) >= 360.0f) {
-        mCanvas->drawOval(arc, paint);
+        mCanvas->drawOval(arc, *filterPaint(paint));
     } else {
-        mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint);
+        mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, *filterPaint(paint));
     }
 }
 
@@ -522,19 +531,19 @@
     if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) {
         return;
     }
-    mCanvas->drawPath(path, paint);
+    mCanvas->drawPath(path, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
-    mCanvas->drawVertices(vertices, mode, paint);
+    mCanvas->drawVertices(vertices, mode, *filterPaint(paint));
 }
 
 // ----------------------------------------------------------------------------
 // Canvas draw operations: Bitmaps
 // ----------------------------------------------------------------------------
 
-const SkPaint* SkiaCanvas::addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
-                                     sk_sp<SkColorFilter> colorSpaceFilter) {
+SkiaCanvas::PaintCoW&& SkiaCanvas::filterBitmap(PaintCoW&& paint,
+                                                sk_sp<SkColorFilter> colorSpaceFilter) const {
     /* We don't apply the colorSpace filter if this canvas is already wrapped with
      * a SkColorSpaceXformCanvas since it already takes care of converting the
      * contents of the bitmap into the appropriate colorspace.  The mCanvasWrapper
@@ -542,39 +551,31 @@
      * to have a non-sRGB colorspace.
      */
     if (!mCanvasWrapper && colorSpaceFilter) {
-        if (origPaint) {
-            *tmpPaint = *origPaint;
-        }
-
-        if (tmpPaint->getColorFilter()) {
-            tmpPaint->setColorFilter(
-                    SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorSpaceFilter));
-            LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
+        SkPaint& tmpPaint = paint.writeable();
+        if (tmpPaint.getColorFilter()) {
+            tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(tmpPaint.refColorFilter(),
+                                                                     std::move(colorSpaceFilter)));
+            LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter());
         } else {
-            tmpPaint->setColorFilter(colorSpaceFilter);
+            tmpPaint.setColorFilter(std::move(colorSpaceFilter));
         }
-
-        return tmpPaint;
-    } else {
-        return origPaint;
     }
+    return filterPaint(std::move(paint));
 }
 
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImage(image, left, top, addFilter(paint, &tmpPaint, colorFilter));
+    mCanvas->drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)));
 }
 
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
     SkAutoCanvasRestore acr(mCanvas, true);
     mCanvas->concat(matrix);
 
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImage(image, 0, 0, addFilter(paint, &tmpPaint, colorFilter));
+    mCanvas->drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)));
 }
 
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
@@ -583,10 +584,9 @@
     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImageRect(image, srcRect, dstRect, addFilter(paint, &tmpPaint, colorFilter),
+    mCanvas->drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
                            SkCanvas::kFast_SrcRectConstraint);
 }
 
@@ -665,21 +665,20 @@
 #endif
 
     // cons-up a shader for the bitmap
-    SkPaint tmpPaint;
-    if (paint) {
-        tmpPaint = *paint;
-    }
+    PaintCoW paintCoW(paint);
+    SkPaint& tmpPaint = paintCoW.writeable();
 
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
     sk_sp<SkShader> shader =
             image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     if (colorFilter) {
-        shader = shader->makeWithColorFilter(colorFilter);
+        shader = shader->makeWithColorFilter(std::move(colorFilter));
     }
-    tmpPaint.setShader(shader);
+    tmpPaint.setShader(std::move(shader));
 
-    mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate, tmpPaint);
+    mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate,
+                          *filterPaint(std::move(paintCoW)));
 }
 
 void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft,
@@ -706,10 +705,10 @@
     lattice.fBounds = nullptr;
     SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImageLattice(image.get(), lattice, dst, addFilter(paint, &tmpPaint, colorFilter));
+    mCanvas->drawImageLattice(image.get(), lattice, dst,
+                              filterBitmap(paint, std::move(colorFilter)));
 }
 
 double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) {
@@ -732,6 +731,9 @@
     // glyphs centered or right-aligned; the offset above takes
     // care of all alignment.
     SkPaint paintCopy(paint);
+    if (mPaintFilter) {
+        mPaintFilter->filter(&paintCopy);
+    }
     paintCopy.setTextAlign(SkPaint::kLeft_Align);
     SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
     // Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
@@ -760,6 +762,9 @@
     // glyphs centered or right-aligned; the offsets take care of
     // that portion of the alignment.
     SkPaint paintCopy(paint);
+    if (mPaintFilter) {
+        mPaintFilter->filter(&paintCopy);
+    }
     paintCopy.setTextAlign(SkPaint::kLeft_Align);
     SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
 
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 3efc22a..24b7ec6 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -22,7 +22,9 @@
 #include "hwui/Canvas.h"
 
 #include <SkCanvas.h>
-#include <SkTLazy.h>
+
+#include <cassert>
+#include <optional>
 
 namespace android {
 
@@ -87,8 +89,8 @@
     virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) override;
     virtual bool clipPath(const SkPath* path, SkClipOp op) override;
 
-    virtual SkDrawFilter* getDrawFilter() override;
-    virtual void setDrawFilter(SkDrawFilter* drawFilter) override;
+    virtual PaintFilter* getPaintFilter() override;
+    virtual void setPaintFilter(sk_sp<PaintFilter> paintFilter) override;
 
     virtual SkCanvasState* captureCanvasState() const override;
 
@@ -158,6 +160,46 @@
                                   const SkPaint& paint, const SkPath& path, size_t start,
                                   size_t end) override;
 
+    /** This class acts as a copy on write SkPaint.
+     *
+     *  Initially this will be the SkPaint passed to the contructor.
+     *  The first time writable() is called this will become a copy of the
+     *  initial SkPaint (or a default SkPaint if nullptr).
+     */
+    struct PaintCoW {
+        PaintCoW(const SkPaint& that) : mPtr(&that) {}
+        PaintCoW(const SkPaint* ptr) : mPtr(ptr) {}
+        PaintCoW(const PaintCoW&) = delete;
+        PaintCoW(PaintCoW&&) = delete;
+        PaintCoW& operator=(const PaintCoW&) = delete;
+        PaintCoW& operator=(PaintCoW&&) = delete;
+        SkPaint& writeable() {
+            if (!mStorage) {
+                if (!mPtr) {
+                    mStorage.emplace();
+                } else {
+                    mStorage.emplace(*mPtr);
+                }
+                mPtr = &*mStorage;
+            }
+            return *mStorage;
+        }
+        operator const SkPaint*() const { return mPtr; }
+        const SkPaint* operator->() const { assert(mPtr); return mPtr; }
+        const SkPaint& operator*() const { assert(mPtr); return *mPtr; }
+        explicit operator bool() { return mPtr != nullptr; }
+    private:
+        const SkPaint* mPtr;
+        std::optional<SkPaint> mStorage;
+    };
+
+    /** Filters the paint using the current paint filter.
+     *
+     *  @param paint the paint to filter. Will be initialized with the default
+     *      SkPaint before filtering if filtering is required.
+     */
+    PaintCoW&& filterPaint(PaintCoW&& paint) const;
+
 private:
     struct SaveRec {
         int saveCount;
@@ -174,8 +216,15 @@
 
     void drawPoints(const float* points, int count, const SkPaint& paint, SkCanvas::PointMode mode);
 
-    const SkPaint* addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
-                             sk_sp<SkColorFilter> colorSpaceFilter);
+    /** Filters the paint for bitmap drawing.
+     *
+     *  After filtering the paint for bitmap drawing,
+     *  also calls filterPaint on the paint.
+     *
+     *  @param paint the paint to filter. Will be initialized with the default
+     *      SkPaint before filtering if filtering is required.
+     */
+    PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp<SkColorFilter> colorSpaceFilter) const;
 
     class Clip;
 
@@ -185,6 +234,7 @@
                                                // unless it is the same as mCanvasOwned.get()
     std::unique_ptr<SkDeque> mSaveStack;       // lazily allocated, tracks partial saves.
     std::vector<Clip> mClipStack;              // tracks persistent clips.
+    sk_sp<PaintFilter> mPaintFilter;
 };
 
 }  // namespace android
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
deleted file mode 100644
index fc009d8..0000000
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ /dev/null
@@ -1,486 +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.
- */
-
-#include "SkiaCanvasProxy.h"
-
-#include <memory>
-
-#include <log/log.h>
-
-#include <SkLatticeIter.h>
-#include <SkPaint.h>
-#include <SkPatchUtils.h>
-#include <SkPath.h>
-#include <SkPixelRef.h>
-#include <SkRRect.h>
-#include <SkRSXform.h>
-#include <SkRect.h>
-#include <SkSurface.h>
-#include <SkTextBlobRunIterator.h>
-#include <SkVertices.h>
-#include "hwui/Bitmap.h"
-
-namespace android {
-namespace uirenderer {
-
-SkiaCanvasProxy::SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls)
-        : INHERITED(canvas->width(), canvas->height())
-        , mCanvas(canvas)
-        , mFilterHwuiCalls(filterHwuiCalls) {}
-
-void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) {
-    mCanvas->drawPaint(paint);
-}
-
-void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPoint pts[],
-                                   const SkPaint& paint) {
-    if (!pts || count == 0) {
-        return;
-    }
-
-    // convert the SkPoints into floats
-    static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats");
-    const size_t floatCount = count << 1;
-    const float* floatArray = &pts[0].fX;
-
-    switch (pointMode) {
-        case kPoints_PointMode: {
-            mCanvas->drawPoints(floatArray, floatCount, paint);
-            break;
-        }
-        case kLines_PointMode: {
-            mCanvas->drawLines(floatArray, floatCount, paint);
-            break;
-        }
-        case kPolygon_PointMode: {
-            SkPaint strokedPaint(paint);
-            strokedPaint.setStyle(SkPaint::kStroke_Style);
-
-            SkPath path;
-            for (size_t i = 0; i < count - 1; i++) {
-                path.moveTo(pts[i]);
-                path.lineTo(pts[i + 1]);
-                this->drawPath(path, strokedPaint);
-                path.rewind();
-            }
-            break;
-        }
-        default:
-            LOG_ALWAYS_FATAL("Unknown point type");
-    }
-}
-
-void SkiaCanvasProxy::onDrawOval(const SkRect& rect, const SkPaint& paint) {
-    mCanvas->drawOval(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
-}
-
-void SkiaCanvasProxy::onDrawRect(const SkRect& rect, const SkPaint& paint) {
-    mCanvas->drawRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
-}
-
-void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint) {
-    if (!roundRect.isComplex()) {
-        const SkRect& rect = roundRect.rect();
-        SkVector radii = roundRect.getSimpleRadii();
-        mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, radii.fX, radii.fY,
-                               paint);
-    } else {
-        SkPath path;
-        path.addRRect(roundRect);
-        mCanvas->drawPath(path, paint);
-    }
-}
-
-void SkiaCanvasProxy::onDrawArc(const SkRect& rect, SkScalar startAngle, SkScalar sweepAngle,
-                                bool useCenter, const SkPaint& paint) {
-    mCanvas->drawArc(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, startAngle, sweepAngle,
-                     useCenter, paint);
-}
-
-void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) {
-    mCanvas->drawPath(path, paint);
-}
-
-void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
-                                   const SkPaint* paint) {
-    sk_sp<Bitmap> hwuiBitmap = Bitmap::createFrom(bitmap.info(), *bitmap.pixelRef());
-    // HWUI doesn't support extractSubset(), so convert any subsetted bitmap into
-    // a drawBitmapRect(); pass through an un-subsetted bitmap.
-    if (hwuiBitmap && bitmap.dimensions() != hwuiBitmap->info().dimensions()) {
-        SkIPoint origin = bitmap.pixelRefOrigin();
-        mCanvas->drawBitmap(
-                *hwuiBitmap, origin.fX, origin.fY, origin.fX + bitmap.dimensions().width(),
-                origin.fY + bitmap.dimensions().height(), left, top,
-                left + bitmap.dimensions().width(), top + bitmap.dimensions().height(), paint);
-    } else {
-        mCanvas->drawBitmap(*hwuiBitmap, left, top, paint);
-    }
-}
-
-void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& skBitmap, const SkRect* srcPtr,
-                                       const SkRect& dst, const SkPaint* paint, SrcRectConstraint) {
-    SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(skBitmap.width(), skBitmap.height());
-    // TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src?
-    Bitmap* bitmap = reinterpret_cast<Bitmap*>(skBitmap.pixelRef());
-    mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft, dst.fTop,
-                        dst.fRight, dst.fBottom, paint);
-}
-
-void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
-                                       const SkRect& dst, const SkPaint*) {
-    // TODO make nine-patch drawing a method on Canvas.h
-    SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported");
-}
-
-void SkiaCanvasProxy::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
-                                  const SkPaint* paint) {
-    SkBitmap skiaBitmap;
-    SkPixmap pixmap;
-    if (image->peekPixels(&pixmap) && skiaBitmap.installPixels(pixmap)) {
-        onDrawBitmap(skiaBitmap, left, top, paint);
-    }
-}
-
-void SkiaCanvasProxy::onDrawImageRect(const SkImage* image, const SkRect* srcPtr, const SkRect& dst,
-                                      const SkPaint* paint, SrcRectConstraint constraint) {
-    SkBitmap skiaBitmap;
-    SkPixmap pixmap;
-    if (image->peekPixels(&pixmap) && skiaBitmap.installPixels(pixmap)) {
-        sk_sp<Bitmap> bitmap = Bitmap::createFrom(skiaBitmap.info(), *skiaBitmap.pixelRef());
-        SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(image->width(), image->height());
-        mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft,
-                            dst.fTop, dst.fRight, dst.fBottom, paint);
-    }
-}
-
-void SkiaCanvasProxy::onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
-                                      const SkPaint*) {
-    SkDEBUGFAIL("SkiaCanvasProxy::onDrawImageNine is not yet supported");
-}
-
-void SkiaCanvasProxy::onDrawImageLattice(const SkImage* image, const Lattice& lattice,
-                                         const SkRect& dst, const SkPaint* paint) {
-    SkLatticeIter iter(lattice, dst);
-    SkRect srcR, dstR;
-    while (iter.next(&srcR, &dstR)) {
-        onDrawImageRect(image, &srcR, dstR, paint, SkCanvas::kFast_SrcRectConstraint);
-    }
-}
-
-void SkiaCanvasProxy::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
-                                           const SkPaint& paint) {
-    if (mFilterHwuiCalls) {
-        return;
-    }
-    mCanvas->drawVertices(vertices, bmode, paint);
-}
-
-sk_sp<SkSurface> SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
-    SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported");
-    return NULL;
-}
-
-void SkiaCanvasProxy::willSave() {
-    mCanvas->save(android::SaveFlags::MatrixClip);
-}
-
-static inline SaveFlags::Flags saveFlags(SkCanvas::SaveLayerFlags layerFlags) {
-    SaveFlags::Flags saveFlags = 0;
-
-    if (!(layerFlags & SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag)) {
-        saveFlags |= SaveFlags::ClipToLayer;
-    }
-
-    return saveFlags;
-}
-
-SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(
-        const SaveLayerRec& saveLayerRec) {
-    SkRect rect;
-    if (saveLayerRec.fBounds) {
-        rect = *saveLayerRec.fBounds;
-    } else if (!mCanvas->getClipBounds(&rect)) {
-        rect = SkRect::MakeEmpty();
-    }
-    mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, saveLayerRec.fPaint,
-                       saveFlags(saveLayerRec.fSaveLayerFlags));
-    return SkCanvas::kNoLayer_SaveLayerStrategy;
-}
-
-void SkiaCanvasProxy::willRestore() {
-    mCanvas->restore();
-}
-
-void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) {
-    mCanvas->concat(matrix);
-}
-
-void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
-    mCanvas->setMatrix(matrix);
-}
-
-void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
-                                   const SkPaint& paint) {
-    SkPath path;
-    path.addRRect(outer);
-    path.addRRect(inner);
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    this->drawPath(path, paint);
-}
-
-/**
- * Utility class that converts the incoming text & paint from the given encoding
- * into glyphIDs.
- */
-class GlyphIDConverter {
-public:
-    GlyphIDConverter(const void* text, size_t byteLength, const SkPaint& origPaint) {
-        paint = origPaint;
-        if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
-            glyphIDs = (uint16_t*)text;
-            count = byteLength >> 1;
-        } else {
-            // ensure space for one glyph per ID given UTF8 encoding.
-            storage.reset(new uint16_t[byteLength]);
-            glyphIDs = storage.get();
-            count = paint.textToGlyphs(text, byteLength, storage.get());
-            paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-        }
-    }
-
-    SkPaint paint;
-    uint16_t* glyphIDs;
-    int count;
-
-private:
-    std::unique_ptr<uint16_t[]> storage;
-};
-
-void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
-                                 const SkPaint& origPaint) {
-    // convert to glyphIDs if necessary
-    GlyphIDConverter glyphs(text, byteLength, origPaint);
-
-    // compute the glyph positions
-    std::unique_ptr<SkScalar[]> glyphWidths(new SkScalar[glyphs.count]);
-    glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get());
-
-    // compute conservative bounds
-    // NOTE: We could call the faster paint.getFontBounds for a less accurate,
-    //       but even more conservative bounds if this  is too slow.
-    SkRect bounds;
-    glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
-
-    // adjust for non-left alignment
-    if (glyphs.paint.getTextAlign() != SkPaint::kLeft_Align) {
-        SkScalar stop = 0;
-        for (int i = 0; i < glyphs.count; i++) {
-            stop += glyphWidths[i];
-        }
-        if (glyphs.paint.getTextAlign() == SkPaint::kCenter_Align) {
-            stop = SkScalarHalf(stop);
-        }
-        if (glyphs.paint.isVerticalText()) {
-            y -= stop;
-        } else {
-            x -= stop;
-        }
-    }
-
-    // setup the first glyph position and adjust bounds if needed
-    int xBaseline = 0;
-    int yBaseline = 0;
-    if (mCanvas->drawTextAbsolutePos()) {
-        bounds.offset(x, y);
-        xBaseline = x;
-        yBaseline = y;
-    }
-
-    static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats");
-    auto glyphFunc = [&](uint16_t* text, float* positions) {
-        memcpy(text, glyphs.glyphIDs, glyphs.count * sizeof(uint16_t));
-        size_t posIndex = 0;
-        // setup the first glyph position
-        positions[posIndex++] = xBaseline;
-        positions[posIndex++] = yBaseline;
-        // setup the remaining glyph positions
-        if (glyphs.paint.isVerticalText()) {
-            float yPosition = yBaseline;
-            for (int i = 1; i < glyphs.count; i++) {
-                positions[posIndex++] = xBaseline;
-                yPosition += glyphWidths[i - 1];
-                positions[posIndex++] = yPosition;
-            }
-        } else {
-            float xPosition = xBaseline;
-            for (int i = 1; i < glyphs.count; i++) {
-                xPosition += glyphWidths[i - 1];
-                positions[posIndex++] = xPosition;
-                positions[posIndex++] = yBaseline;
-            }
-        }
-    };
-    mCanvas->drawGlyphs(glyphFunc, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop,
-                        bounds.fRight, bounds.fBottom, 0);
-}
-
-void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
-                                    const SkPaint& origPaint) {
-    // convert to glyphIDs if necessary
-    GlyphIDConverter glyphs(text, byteLength, origPaint);
-
-    // convert to relative positions if necessary
-    int x, y;
-    if (mCanvas->drawTextAbsolutePos()) {
-        x = 0;
-        y = 0;
-    } else {
-        x = pos[0].fX;
-        y = pos[0].fY;
-    }
-
-    // Compute conservative bounds.  If the content has already been processed
-    // by Minikin then it had already computed these bounds.  Unfortunately,
-    // there is no way to capture those bounds as part of the Skia drawPosText
-    // API so we need to do that computation again here.
-    SkRect bounds = SkRect::MakeEmpty();
-    for (int i = 0; i < glyphs.count; i++) {
-        SkRect glyphBounds = SkRect::MakeEmpty();
-        glyphs.paint.measureText(&glyphs.glyphIDs[i], sizeof(uint16_t), &glyphBounds);
-        glyphBounds.offset(pos[i].fX, pos[i].fY);
-        bounds.join(glyphBounds);
-    }
-
-    static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats");
-    auto glyphFunc = [&](uint16_t* text, float* positions) {
-        memcpy(text, glyphs.glyphIDs, glyphs.count * sizeof(uint16_t));
-        if (mCanvas->drawTextAbsolutePos()) {
-            memcpy(positions, pos, 2 * glyphs.count * sizeof(float));
-        } else {
-            for (int i = 0, posIndex = 0; i < glyphs.count; i++) {
-                positions[posIndex++] = pos[i].fX - x;
-                positions[posIndex++] = pos[i].fY - y;
-            }
-        }
-    };
-    mCanvas->drawGlyphs(glyphFunc, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop,
-                        bounds.fRight, bounds.fBottom, 0);
-}
-
-void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
-                                     SkScalar constY, const SkPaint& paint) {
-    const size_t pointCount = byteLength >> 1;
-    std::unique_ptr<SkPoint[]> pts(new SkPoint[pointCount]);
-    for (size_t i = 0; i < pointCount; i++) {
-        pts[i].set(xpos[i], constY);
-    }
-    this->onDrawPosText(text, byteLength, pts.get(), paint);
-}
-
-void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
-                                       const SkMatrix* matrix, const SkPaint& origPaint) {
-    SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextOnPath is not supported");
-}
-
-void SkiaCanvasProxy::onDrawTextRSXform(const void* text, size_t byteLength,
-                                        const SkRSXform xform[], const SkRect* cullRect,
-                                        const SkPaint& paint) {
-    GlyphIDConverter glyphs(text, byteLength, paint);  // Just get count
-    SkMatrix localM, currM, origM;
-    mCanvas->getMatrix(&currM);
-    origM = currM;
-    for (int i = 0; i < glyphs.count; i++) {
-        localM.setRSXform(*xform++);
-        currM.setConcat(origM, localM);
-        mCanvas->setMatrix(currM);
-        this->onDrawText((char*)text + (byteLength / glyphs.count * i), byteLength / glyphs.count,
-                         0, 0, paint);
-    }
-    mCanvas->setMatrix(origM);
-}
-
-void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                                     const SkPaint& paint) {
-    SkPaint runPaint = paint;
-
-    SkTextBlobRunIterator it(blob);
-    for (; !it.done(); it.next()) {
-        size_t textLen = it.glyphCount() * sizeof(uint16_t);
-        const SkPoint& offset = it.offset();
-        // applyFontToPaint() always overwrites the exact same attributes,
-        // so it is safe to not re-seed the paint for this reason.
-        it.applyFontToPaint(&runPaint);
-
-        switch (it.positioning()) {
-            case SkTextBlob::kDefault_Positioning:
-                this->drawText(it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
-                break;
-            case SkTextBlob::kHorizontal_Positioning: {
-                std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
-                for (size_t i = 0; i < it.glyphCount(); i++) {
-                    pts[i].set(x + offset.x() + it.pos()[i], y + offset.y());
-                }
-                this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
-                break;
-            }
-            case SkTextBlob::kFull_Positioning: {
-                std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
-                for (size_t i = 0; i < it.glyphCount(); i++) {
-                    const size_t xIndex = i * 2;
-                    const size_t yIndex = xIndex + 1;
-                    pts[i].set(x + offset.x() + it.pos()[xIndex],
-                               y + offset.y() + it.pos()[yIndex]);
-                }
-                this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
-                break;
-            }
-            default:
-                SK_ABORT("unhandled positioning mode");
-        }
-    }
-}
-
-void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
-                                  const SkPoint texCoords[4], SkBlendMode bmode,
-                                  const SkPaint& paint) {
-    if (mFilterHwuiCalls) {
-        return;
-    }
-    SkMatrix matrix;
-    mCanvas->getMatrix(&matrix);
-    SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix);
-
-    mCanvas->drawVertices(
-            SkPatchUtils::MakeVertices(cubics, colors, texCoords, lod.width(), lod.height()).get(),
-            bmode, paint);
-}
-
-void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle) {
-    mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op);
-}
-
-void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkClipOp op, ClipEdgeStyle) {
-    SkPath path;
-    path.addRRect(roundRect);
-    mCanvas->clipPath(&path, op);
-}
-
-void SkiaCanvasProxy::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle) {
-    mCanvas->clipPath(&path, op);
-}
-
-};  // namespace uirenderer
-};  // namespace android
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
deleted file mode 100644
index 360d5a0..0000000
--- a/libs/hwui/SkiaCanvasProxy.h
+++ /dev/null
@@ -1,112 +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.
- */
-
-#ifndef SkiaCanvasProxy_DEFINED
-#define SkiaCanvasProxy_DEFINED
-
-#include <SkCanvas.h>
-#include <cutils/compiler.h>
-
-#include "hwui/Canvas.h"
-
-namespace android {
-namespace uirenderer {
-
-/**
- * This class serves as a proxy between Skia's SkCanvas and Android Framework's
- * Canvas.  The class does not maintain any draw-related state and will pass
- * through most requests directly to the Canvas provided in the constructor.
- *
- * Upon construction it is expected that the provided Canvas has already been
- * prepared for recording and will continue to be in the recording state while
- * this proxy class is being used.
- *
- * If filterHwuiCalls is true, the proxy silently ignores away draw calls that
- * aren't supported by HWUI.
- */
-class ANDROID_API SkiaCanvasProxy : public SkCanvas {
-public:
-    explicit SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls = false);
-    virtual ~SkiaCanvasProxy() {}
-
-protected:
-    virtual sk_sp<SkSurface> onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
-
-    virtual void willSave() override;
-    virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
-    virtual void willRestore() override;
-
-    virtual void didConcat(const SkMatrix&) override;
-    virtual void didSetMatrix(const SkMatrix&) override;
-
-    virtual void onDrawPaint(const SkPaint& paint) override;
-    virtual void onDrawPoints(PointMode, size_t count, const SkPoint pts[],
-                              const SkPaint&) override;
-    virtual void onDrawOval(const SkRect&, const SkPaint&) override;
-    virtual void onDrawRect(const SkRect&, const SkPaint&) override;
-    virtual void onDrawRRect(const SkRRect&, const SkPaint&) override;
-    virtual void onDrawPath(const SkPath& path, const SkPaint&) override;
-    virtual void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
-                           const SkPaint&) override;
-    virtual void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
-                              const SkPaint*) override;
-    virtual void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst,
-                                  const SkPaint* paint, SrcRectConstraint) override;
-    virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
-                                  const SkPaint*) override;
-    virtual void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*);
-    virtual void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
-                                 SrcRectConstraint);
-    virtual void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
-                                 const SkPaint*);
-    virtual void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
-                                    const SkPaint*);
-    virtual void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override;
-
-    virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
-
-    virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
-                            const SkPaint&) override;
-    virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
-                               const SkPaint&) override;
-    virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
-                                SkScalar constY, const SkPaint&) override;
-    virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
-                                  const SkMatrix* matrix, const SkPaint&) override;
-    virtual void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
-                                   const SkRect* cullRect, const SkPaint& paint);
-    virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                                const SkPaint& paint) override;
-
-    virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
-                             const SkPoint texCoords[4], SkBlendMode,
-                             const SkPaint& paint) override;
-
-    virtual void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
-    virtual void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
-    virtual void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
-
-private:
-    Canvas* mCanvas;
-    bool mFilterHwuiCalls;
-
-    typedef SkCanvas INHERITED;
-};
-
-};  // namespace uirenderer
-};  // namespace android
-
-#endif  // SkiaCanvasProxy_DEFINED
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 402fbad..9f82d0f 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -474,14 +474,13 @@
 // Update the given paint with alpha and color filter. Return nullptr if no color filter is
 // specified and root alpha is 1. Otherwise, return updated paint.
 SkPaint* Tree::updatePaint(SkPaint* outPaint, TreeProperties* prop) {
-    if (prop->getRootAlpha() == 1.0f && prop->getColorFilter() == nullptr) {
-        return nullptr;
-    } else {
+    // HWUI always draws VD with bilinear filtering.
+    outPaint->setFilterQuality(kLow_SkFilterQuality);
+    if (prop->getRootAlpha() < 1.0f || prop->getColorFilter() != nullptr) {
         outPaint->setColorFilter(sk_ref_sp(prop->getColorFilter()));
-        outPaint->setFilterQuality(kLow_SkFilterQuality);
         outPaint->setAlpha(prop->getRootAlpha() * 255);
-        return outPaint;
     }
+    return outPaint;
 }
 
 Bitmap& Tree::getBitmapUpdateIfDirty() {
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index e84b9ac..af0ae05 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -59,12 +59,6 @@
         onPropertyChanged();                                          \
         retVal;                                                       \
     })
-#define UPDATE_SKPROP(field, value)                                    \
-    ({                                                                 \
-        bool retVal = ((field) != (value));                            \
-        if ((field) != (value)) SkRefCnt_SafeAssign((field), (value)); \
-        retVal;                                                        \
-    })
 
 /* A VectorDrawable is composed of a tree of nodes.
  * Each node can be a group node, or a path.
@@ -223,29 +217,28 @@
             int fillType = 0; /* non-zero or kWinding_FillType in Skia */
         };
         explicit FullPathProperties(Node* mNode) : Properties(mNode), mTrimDirty(false) {}
-        ~FullPathProperties() {
-            SkSafeUnref(fillGradient);
-            SkSafeUnref(strokeGradient);
-        }
+        ~FullPathProperties() {}
         void syncProperties(const FullPathProperties& prop) {
             mPrimitiveFields = prop.mPrimitiveFields;
             mTrimDirty = true;
-            UPDATE_SKPROP(fillGradient, prop.fillGradient);
-            UPDATE_SKPROP(strokeGradient, prop.strokeGradient);
+            fillGradient = prop.fillGradient;
+            strokeGradient = prop.strokeGradient;
             onPropertyChanged();
         }
         void setFillGradient(SkShader* gradient) {
-            if (UPDATE_SKPROP(fillGradient, gradient)) {
+            if (fillGradient.get() != gradient) {
+                fillGradient = sk_ref_sp(gradient);
                 onPropertyChanged();
             }
         }
         void setStrokeGradient(SkShader* gradient) {
-            if (UPDATE_SKPROP(strokeGradient, gradient)) {
+            if (strokeGradient.get() != gradient) {
+                strokeGradient = sk_ref_sp(gradient);
                 onPropertyChanged();
             }
         }
-        SkShader* getFillGradient() const { return fillGradient; }
-        SkShader* getStrokeGradient() const { return strokeGradient; }
+        SkShader* getFillGradient() const { return fillGradient.get(); }
+        SkShader* getStrokeGradient() const { return strokeGradient.get(); }
         float getStrokeWidth() const { return mPrimitiveFields.strokeWidth; }
         void setStrokeWidth(float strokeWidth) {
             VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth);
@@ -325,8 +318,8 @@
             count,
         };
         PrimitiveFields mPrimitiveFields;
-        SkShader* fillGradient = nullptr;
-        SkShader* strokeGradient = nullptr;
+        sk_sp<SkShader> fillGradient;
+        sk_sp<SkShader> strokeGradient;
     };
 
     // Called from UI thread
@@ -550,8 +543,7 @@
             SkRect bounds;
             int scaledWidth = 0;
             int scaledHeight = 0;
-            SkColorFilter* colorFilter = nullptr;
-            ~NonAnimatableProperties() { SkSafeUnref(colorFilter); }
+            sk_sp<SkColorFilter> colorFilter;
         } mNonAnimatableProperties;
         bool mNonAnimatablePropertiesDirty = true;
 
@@ -561,8 +553,7 @@
         void syncNonAnimatableProperties(const TreeProperties& prop) {
             // Copy over the data that can only be changed in UI thread
             if (mNonAnimatableProperties.colorFilter != prop.mNonAnimatableProperties.colorFilter) {
-                SkRefCnt_SafeAssign(mNonAnimatableProperties.colorFilter,
-                                    prop.mNonAnimatableProperties.colorFilter);
+                mNonAnimatableProperties.colorFilter = prop.mNonAnimatableProperties.colorFilter;
             }
             mNonAnimatableProperties = prop.mNonAnimatableProperties;
         }
@@ -599,12 +590,13 @@
             }
         }
         void setColorFilter(SkColorFilter* filter) {
-            if (UPDATE_SKPROP(mNonAnimatableProperties.colorFilter, filter)) {
+            if (mNonAnimatableProperties.colorFilter.get() != filter) {
+                mNonAnimatableProperties.colorFilter = sk_ref_sp(filter);
                 mNonAnimatablePropertiesDirty = true;
                 mTree->onPropertyChanged(this);
             }
         }
-        SkColorFilter* getColorFilter() const { return mNonAnimatableProperties.colorFilter; }
+        SkColorFilter* getColorFilter() const { return mNonAnimatableProperties.colorFilter.get(); }
 
         float getViewportWidth() const { return mNonAnimatableProperties.viewportWidth; }
         float getViewportHeight() const { return mNonAnimatableProperties.viewportHeight; }
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index a401b3f..7a8d026 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -17,6 +17,7 @@
 
 #include "Caches.h"
 #include "HardwareBitmapUploader.h"
+#include "Properties.h"
 #include "renderthread/RenderProxy.h"
 #include "utils/Color.h"
 
@@ -34,6 +35,7 @@
 #include <SkToSRGBColorFilter.h>
 
 #include <limits>
+#include <SkHighContrastFilter.h>
 
 namespace android {
 
@@ -195,11 +197,13 @@
     mPixelStorage.ashmem.size = mappedSize;
 }
 
-Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info)
+Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette)
         : SkPixelRef(info.width(), info.height(), nullptr,
                      bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride())
         , mInfo(validateAlpha(info))
-        , mPixelStorageType(PixelStorageType::Hardware) {
+        , mPixelStorageType(PixelStorageType::Hardware)
+        , mPalette(palette)
+        , mPaletteGenerationId(getGenerationID()) {
     mPixelStorage.hardware.buffer = buffer;
     buffer->incStrong(buffer);
     setImmutable();  // HW bitmaps are always immutable
@@ -326,7 +330,106 @@
     if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) {
         *outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace());
     }
+
+    // TODO: Move this to the canvas (or other?) layer where we have the target lightness
+    // mode and can selectively do the right thing.
+    if (palette() != BitmapPalette::Unknown && uirenderer::Properties::forceDarkMode) {
+        SkHighContrastConfig config;
+        config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
+        *outputColorFilter = SkHighContrastFilter::Make(config)->makeComposed(*outputColorFilter);
+    }
     return image;
 }
 
+class MinMaxAverage {
+public:
+
+    void add(float sample) {
+        if (mCount == 0) {
+            mMin = sample;
+            mMax = sample;
+        } else {
+            mMin = std::min(mMin, sample);
+            mMax = std::max(mMax, sample);
+        }
+        mTotal += sample;
+        mCount++;
+    }
+
+    float average() {
+        return mTotal / mCount;
+    }
+
+    float min() {
+        return mMin;
+    }
+
+    float max() {
+        return mMax;
+    }
+
+    float delta() {
+        return mMax - mMin;
+    }
+
+private:
+    float mMin = 0.0f;
+    float mMax = 0.0f;
+    float mTotal = 0.0f;
+    int mCount = 0;
+};
+
+BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes) {
+    ATRACE_CALL();
+
+    SkPixmap pixmap{info, addr, rowBytes};
+
+    // TODO: This calculation of converting to HSV & tracking min/max is probably overkill
+    // Experiment with something simpler since we just want to figure out if it's "color-ful"
+    // and then the average perceptual lightness.
+
+    MinMaxAverage hue, saturation, value;
+    int sampledCount = 0;
+
+    // Sample a grid of 100 pixels to get an overall estimation of the colors in play
+    const int x_step = std::max(1, pixmap.width() / 10);
+    const int y_step = std::max(1, pixmap.height() / 10);
+    for (int x = 0; x < pixmap.width(); x += x_step) {
+        for (int y = 0; y < pixmap.height(); y += y_step) {
+            SkColor color = pixmap.getColor(x, y);
+            if (!info.isOpaque() && SkColorGetA(color) < 75) {
+                continue;
+            }
+
+            sampledCount++;
+            float hsv[3];
+            SkColorToHSV(color, hsv);
+            hue.add(hsv[0]);
+            saturation.add(hsv[1]);
+            value.add(hsv[2]);
+        }
+    }
+
+    // TODO: Tune the coverage threshold
+    if (sampledCount < 5) {
+        ALOGV("Not enough samples, only found %d for image sized %dx%d, format = %d, alpha = %d",
+              sampledCount, info.width(), info.height(), (int) info.colorType(), (int) info.alphaType());
+        return BitmapPalette::Unknown;
+    }
+
+    ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = %f]",
+          sampledCount,
+          hue.min(), hue.max(), hue.average(),
+          saturation.min(), saturation.max(), saturation.average());
+
+    if (hue.delta() <= 20 && saturation.delta() <= .1f) {
+        if (value.average() >= .5f) {
+            return BitmapPalette::Light;
+        } else {
+            return BitmapPalette::Dark;
+        }
+    }
+    return BitmapPalette::Unknown;
+}
+
 }  // namespace android
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index dbd4456..d268042 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -34,6 +34,12 @@
     Hardware,
 };
 
+enum class BitmapPalette {
+    Unknown,
+    Light,
+    Dark,
+};
+
 namespace uirenderer {
 namespace renderthread {
 class RenderThread;
@@ -63,7 +69,7 @@
     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);
+    Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette = BitmapPalette::Unknown);
 
     int rowBytesAsPixels() const { return rowBytes() >> mInfo.shiftPerPixel(); }
 
@@ -103,6 +109,20 @@
      */
     sk_sp<SkImage> makeImage(sk_sp<SkColorFilter>* outputColorFilter);
 
+    static BitmapPalette computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes);
+
+    static BitmapPalette computePalette(const SkBitmap& bitmap) {
+        return computePalette(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes());
+    }
+
+    BitmapPalette palette() {
+        if (!isHardware() && mPaletteGenerationId != getGenerationID()) {
+            mPalette = computePalette(info(), pixels(), rowBytes());
+            mPaletteGenerationId = getGenerationID();
+        }
+        return mPalette;
+    }
+
 private:
     virtual ~Bitmap();
     void* getStorage() const;
@@ -111,6 +131,9 @@
 
     const PixelStorageType mPixelStorageType;
 
+    BitmapPalette mPalette = BitmapPalette::Unknown;
+    uint32_t mPaletteGenerationId = -1;
+
     bool mHasHardwareMipMap = false;
 
     union {
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index fb6bd6f..af7f013 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -23,7 +23,7 @@
 #include "Typeface.h"
 #include "pipeline/skia/SkiaRecordingCanvas.h"
 
-#include <SkDrawFilter.h>
+#include "hwui/PaintFilter.h"
 
 namespace android {
 
@@ -40,10 +40,10 @@
 
 void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
     uint32_t flags;
-    SkDrawFilter* drawFilter = getDrawFilter();
-    if (drawFilter) {
+    PaintFilter* paintFilter = getPaintFilter();
+    if (paintFilter) {
         SkPaint paintCopy(paint);
-        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+        paintFilter->filter(&paintCopy);
         flags = paintCopy.getFlags();
     } else {
         flags = paint.getFlags();
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 88b12b2..b9af7de2 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -39,6 +39,7 @@
 }
 
 namespace android {
+class PaintFilter;
 
 namespace uirenderer {
 class CanvasPropertyPaint;
@@ -73,7 +74,6 @@
 }  // namespace SaveFlags
 
 namespace uirenderer {
-class SkiaCanvasProxy;
 namespace VectorDrawable {
 class Tree;
 };
@@ -213,8 +213,8 @@
     virtual bool clipPath(const SkPath* path, SkClipOp op) = 0;
 
     // filters
-    virtual SkDrawFilter* getDrawFilter() = 0;
-    virtual void setDrawFilter(SkDrawFilter* drawFilter) = 0;
+    virtual PaintFilter* getPaintFilter() = 0;
+    virtual void setPaintFilter(sk_sp<PaintFilter> paintFilter) = 0;
 
     // WebView only
     virtual SkCanvasState* captureCanvasState() const { return nullptr; }
@@ -304,7 +304,6 @@
 
     friend class DrawTextFunctor;
     friend class DrawTextOnPathFunctor;
-    friend class uirenderer::SkiaCanvasProxy;
 };
 
 };  // namespace android
diff --git a/libs/hwui/hwui/PaintFilter.h b/libs/hwui/hwui/PaintFilter.h
new file mode 100644
index 0000000..bf5627e
--- /dev/null
+++ b/libs/hwui/hwui/PaintFilter.h
@@ -0,0 +1,19 @@
+#ifndef ANDROID_GRAPHICS_PAINT_FILTER_H_
+#define ANDROID_GRAPHICS_PAINT_FILTER_H_
+
+class SkPaint;
+
+namespace android {
+
+class PaintFilter : public SkRefCnt {
+public:
+    /**
+     *  Called with the paint that will be used to draw.
+     *  The implementation may modify the paint as they wish.
+     */
+    virtual void filter(SkPaint*) = 0;
+};
+
+} // namespace android
+
+#endif
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 14d31b2..c41f6a6 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -82,7 +82,14 @@
             textureMatrix = textureMatrixInv;
         }
 
-        SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrix);
+        SkMatrix matrix;
+        if (dstRect) {
+            // Destination rectangle is set only when we are trying to read back the content
+            // of the layer. In this case we don't want to apply layer transform.
+            matrix = textureMatrix;
+        } else {
+            matrix = SkMatrix::Concat(layerTransform, textureMatrix);
+        }
 
         SkPaint paint;
         paint.setAlpha(layer->getAlpha());
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 1b816fe..85fdc103 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -142,7 +142,7 @@
             LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline);
             const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath();
             SkAutoCanvasRestore acr2(canvas, shouldClip);
-            canvas->setMatrix(mProjectedDisplayList->mProjectedReceiverParentMatrix);
+            canvas->setMatrix(mProjectedDisplayList->mParentMatrix);
             if (shouldClip) {
                 clipOutline(*mProjectedDisplayList->mProjectedOutline, canvas, nullptr);
             }
@@ -156,10 +156,10 @@
                             SkPaint* paint) {
     paint->setFilterQuality(kLow_SkFilterQuality);
     if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
-        properties.xferMode() != SkBlendMode::kSrcOver || properties.colorFilter() != nullptr) {
+        properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr) {
         paint->setAlpha(properties.alpha() * alphaMultiplier);
         paint->setBlendMode(properties.xferMode());
-        paint->setColorFilter(sk_ref_sp(properties.colorFilter()));
+        paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
         return true;
     }
     return false;
@@ -200,9 +200,7 @@
         setViewProperties(properties, canvas, &alphaMultiplier);
     }
     SkiaDisplayList* displayList = (SkiaDisplayList*)mRenderNode->getDisplayList();
-    if (displayList->containsProjectionReceiver()) {
-        displayList->mProjectedReceiverParentMatrix = canvas->getTotalMatrix();
-    }
+    displayList->mParentMatrix = canvas->getTotalMatrix();
 
     // TODO should we let the bound of the drawable do this for us?
     const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 6292a6c..dba97fe 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -55,6 +55,11 @@
         if (casterZ >= -NON_ZERO_EPSILON) {  // draw only children with negative Z
             return;
         }
+        SkAutoCanvasRestore acr(canvas, true);
+        // Since we're drawing out of recording order, the child's matrix needs to be applied to the
+        // canvas. In in-order drawing, the canvas already has the child's matrix applied.
+        canvas->setMatrix(mDisplayList->mParentMatrix);
+        canvas->concat(childNode->getRecordedMatrix());
         childNode->forceDraw(canvas);
         drawIndex++;
     }
@@ -102,6 +107,11 @@
 
         RenderNodeDrawable* childNode = zChildren[drawIndex];
         SkASSERT(childNode);
+        SkAutoCanvasRestore acr(canvas, true);
+        // Since we're drawing out of recording order, the child's matrix needs to be applied to the
+        // canvas. In in-order drawing, the canvas already has the child's matrix applied.
+        canvas->setMatrix(mStartBarrier->mDisplayList->mParentMatrix);
+        canvas->concat(childNode->getRecordedMatrix());
         childNode->forceDraw(canvas);
 
         drawIndex++;
@@ -153,10 +163,15 @@
     }
 
     SkAutoCanvasRestore acr(canvas, true);
+    // Since we're drawing out of recording order, the child's matrix needs to be applied to the
+    // canvas. In in-order drawing, the canvas already has the child's matrix applied.
+    canvas->setMatrix(mStartBarrier->mDisplayList->mParentMatrix);
 
     SkMatrix shadowMatrix;
-    mat4 hwuiMatrix;
+    mat4 hwuiMatrix(caster->getRecordedMatrix());
     // TODO we don't pass the optional boolean to treat it as a 4x4 matrix
+    // applyViewPropertyTransforms gets the same matrix, which render nodes apply with
+    // RenderNodeDrawable::setViewProperties as a part if their draw.
     caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix);
     hwuiMatrix.copyTo(shadowMatrix);
     canvas->concat(shadowMatrix);
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 58b9242..6eff589 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -173,12 +173,12 @@
     // node is drawn.
     const Outline* mProjectedOutline = nullptr;
 
-    // mProjectedReceiverParentMatrix is valid when render node tree is traversed during the draw
-    // pass. Render nodes that have a child receiver node, will store their matrix in
-    // mProjectedReceiverParentMatrix. Child receiver node will set the matrix and then clip with
-    // the
-    // outline of their parent.
-    SkMatrix mProjectedReceiverParentMatrix;
+    // mParentMatrix is set and valid when render node tree is traversed during the draw
+    // pass. Render nodes, which draw in a order different than recording order (e.g. nodes with a
+    // child receiver node or Z elevation), can use mParentMatrix to calculate the final transform
+    // without replaying the matrix transform OPs from the display list.
+    // Child receiver node will set the matrix and then clip with the outline of their parent.
+    SkMatrix mParentMatrix;
 };
 
 };  // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
index 208910a..f2f5056 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -26,6 +26,7 @@
 #include "DeviceInfo.h"
 #include "Matrix.h"
 #include "Properties.h"
+#include "utils/MathUtils.h"
 
 using namespace android::uirenderer::renderthread;
 
@@ -115,9 +116,9 @@
             paint.setBlendMode(SkBlendMode::kSrc);
             // Apply a filter, which is matching OpenGL pipeline readback behaviour. Filter usage
             // is codified by tests using golden images like DecodeAccuracyTest.
-            if (skiaSrcRect.width() != bitmap->width() ||
-                skiaSrcRect.height() != bitmap->height()) {
-                // TODO: apply filter always, but check if tests will be fine
+            bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width())
+                    && MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
+            if (!disableFilter) {
                 paint.setFilterQuality(kLow_SkFilterQuality);
             }
             scaledSurface->getCanvas()->concat(textureMatrix);
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index f0da660..b9748af 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -17,6 +17,7 @@
 #include "SkiaRecordingCanvas.h"
 
 #include <SkImagePriv.h>
+#include "CanvasTransform.h"
 #include "Layer.h"
 #include "LayerDrawable.h"
 #include "NinePatchUtils.h"
@@ -44,13 +45,21 @@
     }
 
     mDisplayList->attachRecorder(&mRecorder, SkIRect::MakeWH(width, height));
-    SkiaCanvas::reset(&mRecorder);
+    SkCanvas* canvas = &mRecorder;
+    if (renderNode) {
+        mWrappedCanvas = makeTransformCanvas(&mRecorder, renderNode->usageHint());
+        if (mWrappedCanvas) {
+            canvas = mWrappedCanvas.get();
+        }
+    }
+    SkiaCanvas::reset(canvas);
 }
 
 uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() {
     // close any existing chunks if necessary
     insertReorderBarrier(false);
     mRecorder.restoreToCount(1);
+    mWrappedCanvas = nullptr;
     return mDisplayList.release();
 }
 
@@ -148,12 +157,45 @@
 // Recording Canvas draw operations: Bitmaps
 // ----------------------------------------------------------------------------
 
+SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint,
+                                                         sk_sp<SkColorFilter> colorSpaceFilter) {
+    bool fixBlending = false;
+    bool fixAA = false;
+    if (paint) {
+        // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
+        // older.
+        fixBlending = sApiLevel <= 27 && paint->getBlendMode() == SkBlendMode::kClear;
+        fixAA = paint->isAntiAlias();
+    }
+
+    if (fixBlending || fixAA || colorSpaceFilter) {
+        SkPaint& tmpPaint = paint.writeable();
+
+        if (fixBlending) {
+            tmpPaint.setBlendMode(SkBlendMode::kDstOut);
+        }
+
+        if (colorSpaceFilter) {
+            if (tmpPaint.getColorFilter()) {
+                tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(
+                       tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
+            } else {
+                tmpPaint.setColorFilter(std::move(colorSpaceFilter));
+            }
+            LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter());
+        }
+
+        // disabling AA on bitmap draws matches legacy HWUI behavior
+        tmpPaint.setAntiAlias(false);
+    }
+
+    return filterPaint(std::move(paint));
+}
 
 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImage(image, left, top, bitmapPaint(paint, &tmpPaint, colorFilter));
+    mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)));
     // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
     // it is not safe to store a raw SkImage pointer, because the image object will be destroyed
     // when this function ends.
@@ -166,10 +208,9 @@
     SkAutoCanvasRestore acr(&mRecorder, true);
     concat(matrix);
 
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImage(image, 0, 0, bitmapPaint(paint, &tmpPaint, colorFilter));
+    mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)));
     if (!bitmap.isImmutable() && image.get() && !image->unique()) {
         mDisplayList->mMutableImages.push_back(image.get());
     }
@@ -181,10 +222,9 @@
     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImageRect(image, srcRect, dstRect, bitmapPaint(paint, &tmpPaint, colorFilter),
+    mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
                             SkCanvas::kFast_SrcRectConstraint);
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
         !dstRect.isEmpty()) {
@@ -216,23 +256,15 @@
     lattice.fBounds = nullptr;
     SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    SkPaint tmpPaint;
+    PaintCoW filteredPaint(paint);
+    // HWUI always draws 9-patches with bilinear filtering, regardless of what is set in the Paint.
+    if (!filteredPaint || filteredPaint->getFilterQuality() != kLow_SkFilterQuality) {
+        filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
+    }
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    const SkPaint* filteredPaint = bitmapPaint(paint, &tmpPaint, colorFilter);
-    // Besides kNone, the other three SkFilterQualities are treated the same. And Android's
-    // Java API only supports kLow and kNone anyway.
-    if (!filteredPaint || filteredPaint->getFilterQuality() == kNone_SkFilterQuality) {
-        if (filteredPaint != &tmpPaint) {
-            if (paint) {
-                tmpPaint = *paint;
-            }
-            filteredPaint = &tmpPaint;
-        }
-        tmpPaint.setFilterQuality(kLow_SkFilterQuality);
-    }
-
-    mRecorder.drawImageLattice(image.get(), lattice, dst, filteredPaint);
+    mRecorder.drawImageLattice(image.get(), lattice, dst,
+                               filterBitmap(std::move(filteredPaint), std::move(colorFilter)));
     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 93807a5..50d84d6 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -78,6 +78,7 @@
 private:
     SkLiteRecorder mRecorder;
     std::unique_ptr<SkiaDisplayList> mDisplayList;
+    std::unique_ptr<SkCanvas> mWrappedCanvas;
     StartReorderBarrierDrawable* mCurrentBarrier;
 
     /**
@@ -89,44 +90,7 @@
      */
     void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height);
 
-    inline static const SkPaint* bitmapPaint(const SkPaint* origPaint, SkPaint* tmpPaint,
-                                             sk_sp<SkColorFilter> colorSpaceFilter) {
-        bool fixBlending = false;
-        bool fixAA = false;
-        if (origPaint) {
-            // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
-            // older.
-            fixBlending = sApiLevel <= 27 && origPaint->getBlendMode() == SkBlendMode::kClear;
-            fixAA = origPaint->isAntiAlias();
-        }
-
-        if (fixBlending || fixAA || colorSpaceFilter) {
-            if (origPaint) {
-                *tmpPaint = *origPaint;
-            }
-
-            if (fixBlending) {
-                tmpPaint->setBlendMode(SkBlendMode::kDstOut);
-            }
-
-            if (colorSpaceFilter) {
-                if (tmpPaint->getColorFilter()) {
-                    tmpPaint->setColorFilter(SkColorFilter::MakeComposeFilter(
-                            tmpPaint->refColorFilter(), colorSpaceFilter));
-                } else {
-                    tmpPaint->setColorFilter(colorSpaceFilter);
-                }
-                LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
-            }
-
-            // disabling AA on bitmap draws matches legacy HWUI behavior
-            tmpPaint->setAntiAlias(false);
-            return tmpPaint;
-        } else {
-            return origPaint;
-        }
-    }
-
+    PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp<SkColorFilter> colorSpaceFilter);
 };
 
 };  // namespace skiapipeline
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index b07fb2d..bec80b1e 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -134,6 +134,7 @@
     }
 
     contextOptions->fPersistentCache = &skiapipeline::ShaderCache::get();
+    contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting;
 }
 
 void CacheManager::trimMemory(TrimMemoryMode mode) {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 6eca8d2..e3807e6 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -85,6 +85,11 @@
             [ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); });
 }
 
+void RenderProxy::allocateBuffers(const sp<Surface>& surface) {
+    mRenderThread.queue().post(
+            [ surf = surface ]() mutable { surf->allocateBuffers(); });
+}
+
 void RenderProxy::updateSurface(const sp<Surface>& surface) {
     mRenderThread.queue().post(
             [ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); });
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 5668484..c2964a4 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -70,6 +70,7 @@
     ANDROID_API void setName(const char* name);
 
     ANDROID_API void initialize(const sp<Surface>& surface);
+    ANDROID_API void allocateBuffers(const sp<Surface>& surface);
     ANDROID_API void updateSurface(const sp<Surface>& surface);
     ANDROID_API bool pauseSurface(const sp<Surface>& surface);
     ANDROID_API void setStopped(bool stopped);
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 62f820a..a19edae 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -31,44 +31,242 @@
 namespace uirenderer {
 namespace renderthread {
 
-#define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F)
-#define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F)
+#define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F)
+#define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F)
+#define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F)
 
 VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {}
 
 void VulkanManager::destroy() {
-    if (!hasVkContext()) return;
-
     mRenderThread.renderState().onVkContextDestroyed();
     mRenderThread.setGrContext(nullptr);
 
     if (VK_NULL_HANDLE != mCommandPool) {
-        mDestroyCommandPool(mBackendContext->fDevice, mCommandPool, nullptr);
+        mDestroyCommandPool(mDevice, mCommandPool, nullptr);
         mCommandPool = VK_NULL_HANDLE;
     }
-    mBackendContext.reset();
-}
 
-void VulkanManager::initialize() {
-    if (hasVkContext()) {
-        return;
+    if (mDevice != VK_NULL_HANDLE) {
+        mDeviceWaitIdle(mDevice);
+        mDestroyDevice(mDevice, nullptr);
     }
 
-    auto canPresent = [](VkInstance, VkPhysicalDevice, uint32_t) { return true; };
+    if (mInstance != VK_NULL_HANDLE) {
+        mDestroyInstance(mInstance, nullptr);
+    }
 
-    mBackendContext.reset(GrVkBackendContext::Create(vkGetInstanceProcAddr, vkGetDeviceProcAddr,
-                                                     &mPresentQueueIndex, canPresent));
-    LOG_ALWAYS_FATAL_IF(!mBackendContext.get());
+    mGraphicsQueue = VK_NULL_HANDLE;
+    mPresentQueue = VK_NULL_HANDLE;
+    mDevice = VK_NULL_HANDLE;
+    mPhysicalDevice = VK_NULL_HANDLE;
+    mInstance = VK_NULL_HANDLE;
+}
 
-    // Get all the addresses of needed vulkan functions
-    VkInstance instance = mBackendContext->fInstance;
-    VkDevice device = mBackendContext->fDevice;
-    GET_PROC(CreateAndroidSurfaceKHR);
-    GET_PROC(DestroySurfaceKHR);
-    GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
-    GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
-    GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
-    GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
+bool VulkanManager::setupDevice(VkPhysicalDeviceFeatures& deviceFeatures) {
+    VkResult err;
+
+    constexpr VkApplicationInfo app_info = {
+        VK_STRUCTURE_TYPE_APPLICATION_INFO, // sType
+        nullptr,                            // pNext
+        "android framework",                // pApplicationName
+        0,                                  // applicationVersion
+        "android framework",                // pEngineName
+        0,                                  // engineVerison
+        VK_MAKE_VERSION(1, 0, 0),           // apiVersion
+    };
+
+    std::vector<const char*> instanceExtensions;
+    {
+        GET_PROC(EnumerateInstanceExtensionProperties);
+
+        uint32_t extensionCount = 0;
+        err = mEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
+        if (VK_SUCCESS != err) {
+            return false;
+        }
+        std::unique_ptr<VkExtensionProperties[]> extensions(
+                new VkExtensionProperties[extensionCount]);
+        err = mEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.get());
+        if (VK_SUCCESS != err) {
+            return false;
+        }
+        bool hasKHRSurfaceExtension = false;
+        bool hasKHRAndroidSurfaceExtension = false;
+        for (uint32_t i = 0; i < extensionCount; ++i) {
+            instanceExtensions.push_back(extensions[i].extensionName);
+            if (!strcmp(extensions[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) {
+                hasKHRSurfaceExtension = true;
+            }
+            if (!strcmp(extensions[i].extensionName,VK_KHR_ANDROID_SURFACE_EXTENSION_NAME)) {
+                hasKHRAndroidSurfaceExtension = true;
+            }
+        }
+        if (!hasKHRSurfaceExtension || !hasKHRAndroidSurfaceExtension) {
+            this->destroy();
+            return false;
+        }
+    }
+
+    const VkInstanceCreateInfo instance_create = {
+        VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,    // sType
+        nullptr,                                   // pNext
+        0,                                         // flags
+        &app_info,                                 // pApplicationInfo
+        0,                                         // enabledLayerNameCount
+        nullptr,                                   // ppEnabledLayerNames
+        (uint32_t) instanceExtensions.size(),      // enabledExtensionNameCount
+        instanceExtensions.data(),                 // ppEnabledExtensionNames
+    };
+
+    GET_PROC(CreateInstance);
+    err = mCreateInstance(&instance_create, nullptr, &mInstance);
+    if (err < 0) {
+        this->destroy();
+        return false;
+    }
+
+    GET_INST_PROC(DestroyInstance);
+    GET_INST_PROC(EnumeratePhysicalDevices);
+    GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
+    GET_INST_PROC(GetPhysicalDeviceFeatures);
+    GET_INST_PROC(CreateDevice);
+    GET_INST_PROC(EnumerateDeviceExtensionProperties);
+    GET_INST_PROC(CreateAndroidSurfaceKHR);
+    GET_INST_PROC(DestroySurfaceKHR);
+    GET_INST_PROC(GetPhysicalDeviceSurfaceSupportKHR);
+    GET_INST_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
+    GET_INST_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
+    GET_INST_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
+
+    uint32_t gpuCount;
+    err = mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr);
+    if (err) {
+        this->destroy();
+        return false;
+    }
+    if (!gpuCount) {
+        this->destroy();
+        return false;
+    }
+    // Just returning the first physical device instead of getting the whole array. Since there
+    // should only be one device on android.
+    gpuCount = 1;
+    err = mEnumeratePhysicalDevices(mInstance, &gpuCount, &mPhysicalDevice);
+    // VK_INCOMPLETE is returned when the count we provide is less than the total device count.
+    if (err && VK_INCOMPLETE != err) {
+        this->destroy();
+        return false;
+    }
+
+    // query to get the initial queue props size
+    uint32_t queueCount;
+    mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
+    if (!queueCount) {
+        this->destroy();
+        return false;
+    }
+
+    // now get the actual queue props
+    std::unique_ptr<VkQueueFamilyProperties[]> queueProps(new VkQueueFamilyProperties[queueCount]);
+    mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, queueProps.get());
+
+    // iterate to find the graphics queue
+    mGraphicsQueueIndex = queueCount;
+    for (uint32_t i = 0; i < queueCount; i++) {
+        if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+            mGraphicsQueueIndex = i;
+            break;
+        }
+    }
+    if (mGraphicsQueueIndex == queueCount) {
+        this->destroy();
+        return false;
+    }
+
+    // All physical devices and queue families on Android must be capable of
+    // presentation with any native window. So just use the first one.
+    mPresentQueueIndex = 0;
+
+    std::vector<const char*> deviceExtensions;
+    {
+        uint32_t extensionCount = 0;
+        err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount,
+                nullptr);
+        if (VK_SUCCESS != err) {
+            this->destroy();
+            return false;
+        }
+        std::unique_ptr<VkExtensionProperties[]> extensions(
+                new VkExtensionProperties[extensionCount]);
+        err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount,
+                extensions.get());
+        if (VK_SUCCESS != err) {
+            this->destroy();
+            return false;
+        }
+        bool hasKHRSwapchainExtension = false;
+        for (uint32_t i = 0; i < extensionCount; ++i) {
+            deviceExtensions.push_back(extensions[i].extensionName);
+            if (!strcmp(extensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
+                hasKHRSwapchainExtension = true;
+            }
+        }
+        if (!hasKHRSwapchainExtension) {
+            this->destroy();
+            return false;
+        }
+    }
+
+    // query to get the physical device properties
+    mGetPhysicalDeviceFeatures(mPhysicalDevice, &deviceFeatures);
+    // this looks like it would slow things down,
+    // and we can't depend on it on all platforms
+    deviceFeatures.robustBufferAccess = VK_FALSE;
+
+    float queuePriorities[1] = { 0.0 };
+
+    const VkDeviceQueueCreateInfo queueInfo[2] = {
+        {
+            VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
+            nullptr,                                    // pNext
+            0,                                          // VkDeviceQueueCreateFlags
+            mGraphicsQueueIndex,                        // queueFamilyIndex
+            1,                                          // queueCount
+            queuePriorities,                            // pQueuePriorities
+        },
+        {
+            VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
+            nullptr,                                    // pNext
+            0,                                          // VkDeviceQueueCreateFlags
+            mPresentQueueIndex,                         // queueFamilyIndex
+            1,                                          // queueCount
+            queuePriorities,                            // pQueuePriorities
+        }
+    };
+    uint32_t queueInfoCount = (mPresentQueueIndex != mGraphicsQueueIndex) ? 2 : 1;
+
+    const VkDeviceCreateInfo deviceInfo = {
+        VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,    // sType
+        nullptr,                                 // pNext
+        0,                                       // VkDeviceCreateFlags
+        queueInfoCount,                          // queueCreateInfoCount
+        queueInfo,                               // pQueueCreateInfos
+        0,                                       // layerCount
+        nullptr,                                 // ppEnabledLayerNames
+        (uint32_t) deviceExtensions.size(),      // extensionCount
+        deviceExtensions.data(),                 // ppEnabledExtensionNames
+        &deviceFeatures                          // ppEnabledFeatures
+    };
+
+    err = mCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice);
+    if (err) {
+        this->destroy();
+        return false;
+    }
+
+    GET_DEV_PROC(GetDeviceQueue);
+    GET_DEV_PROC(DeviceWaitIdle);
+    GET_DEV_PROC(DestroyDevice);
     GET_DEV_PROC(CreateSwapchainKHR);
     GET_DEV_PROC(DestroySwapchainKHR);
     GET_DEV_PROC(GetSwapchainImagesKHR);
@@ -93,25 +291,74 @@
     GET_DEV_PROC(WaitForFences);
     GET_DEV_PROC(ResetFences);
 
+    return true;
+}
+
+void VulkanManager::initialize() {
+    if (mDevice != VK_NULL_HANDLE) {
+        return;
+    }
+
+    std::vector<const char*> instanceExtensions;
+    std::vector<const char*> deviceExtensions;
+    VkPhysicalDeviceFeatures deviceFeatures;
+    LOG_ALWAYS_FATAL_IF(!this->setupDevice(deviceFeatures));
+
+    mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
+
+    uint32_t extensionFlags = kKHR_surface_GrVkExtensionFlag |
+                              kKHR_android_surface_GrVkExtensionFlag |
+                              kKHR_swapchain_GrVkExtensionFlag;
+
+    uint32_t featureFlags = 0;
+    if (deviceFeatures.geometryShader) {
+        featureFlags |= kGeometryShader_GrVkFeatureFlag;
+    }
+    if (deviceFeatures.dualSrcBlend) {
+        featureFlags |= kDualSrcBlend_GrVkFeatureFlag;
+    }
+    if (deviceFeatures.sampleRateShading) {
+        featureFlags |= kSampleRateShading_GrVkFeatureFlag;
+    }
+
+    auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
+        if (device != VK_NULL_HANDLE) {
+            return vkGetDeviceProcAddr(device, proc_name);
+        }
+        return vkGetInstanceProcAddr(instance, proc_name);
+    };
+
+    GrVkBackendContext backendContext;
+    backendContext.fInstance = mInstance;
+    backendContext.fPhysicalDevice = mPhysicalDevice;
+    backendContext.fDevice = mDevice;
+    backendContext.fQueue = mGraphicsQueue;
+    backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
+    backendContext.fMinAPIVersion = VK_MAKE_VERSION(1, 0, 0);
+    backendContext.fExtensions = extensionFlags;
+    backendContext.fFeatures = featureFlags;
+    backendContext.fGetProc = std::move(getProc);
+    backendContext.fOwnsInstanceAndDevice = false;
+
     // create the command pool for the command buffers
     if (VK_NULL_HANDLE == mCommandPool) {
         VkCommandPoolCreateInfo commandPoolInfo;
         memset(&commandPoolInfo, 0, sizeof(VkCommandPoolCreateInfo));
         commandPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
         // this needs to be on the render queue
-        commandPoolInfo.queueFamilyIndex = mBackendContext->fGraphicsQueueIndex;
+        commandPoolInfo.queueFamilyIndex = mGraphicsQueueIndex;
         commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
-        SkDEBUGCODE(VkResult res =) mCreateCommandPool(mBackendContext->fDevice, &commandPoolInfo,
-                                                       nullptr, &mCommandPool);
+        SkDEBUGCODE(VkResult res =) mCreateCommandPool(mDevice, &commandPoolInfo, nullptr,
+                &mCommandPool);
         SkASSERT(VK_SUCCESS == res);
     }
 
-    mGetDeviceQueue(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue);
+    mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue);
 
     GrContextOptions options;
     options.fDisableDistanceFieldPaths = true;
     mRenderThread.cacheManager().configureContext(&options);
-    sk_sp<GrContext> grContext(GrContext::MakeVulkan(mBackendContext, options));
+    sk_sp<GrContext> grContext(GrContext::MakeVulkan(backendContext, options));
     LOG_ALWAYS_FATAL_IF(!grContext.get());
     mRenderThread.setGrContext(grContext);
     DeviceInfo::initialize(mRenderThread.getGrContext()->maxRenderTargetSize());
@@ -138,8 +385,7 @@
 
     // Before we reuse a backbuffer, make sure its fences have all signaled so that we can safely
     // reuse its commands buffers.
-    VkResult res =
-            mWaitForFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX);
+    VkResult res = mWaitForFences(mDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX);
     if (res != VK_SUCCESS) {
         return nullptr;
     }
@@ -153,12 +399,12 @@
 
     VkResult res;
 
-    res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences);
+    res = mResetFences(mDevice, 2, backbuffer->mUsageFences);
     SkASSERT(VK_SUCCESS == res);
 
     // The acquire will signal the attached mAcquireSemaphore. We use this to know the image has
     // finished presenting and that it is safe to begin sending new commands to the returned image.
-    res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX,
+    res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX,
                                backbuffer->mAcquireSemaphore, VK_NULL_HANDLE,
                                &backbuffer->mImageIndex);
 
@@ -173,11 +419,11 @@
             return nullptr;
         }
         backbuffer = getAvailableBackbuffer(surface);
-        res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences);
+        res = mResetFences(mDevice, 2, backbuffer->mUsageFences);
         SkASSERT(VK_SUCCESS == res);
 
         // acquire the image
-        res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX,
+        res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX,
                                    backbuffer->mAcquireSemaphore, VK_NULL_HANDLE,
                                    &backbuffer->mImageIndex);
 
@@ -205,7 +451,7 @@
             layout,                                     // oldLayout
             VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,   // newLayout
             mPresentQueueIndex,                         // srcQueueFamilyIndex
-            mBackendContext->fGraphicsQueueIndex,       // dstQueueFamilyIndex
+            mGraphicsQueueIndex,       // dstQueueFamilyIndex
             surface->mImages[backbuffer->mImageIndex],  // image
             {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}     // subresourceRange
     };
@@ -236,7 +482,7 @@
     submitInfo.signalSemaphoreCount = 0;
 
     // Attach first fence to submission here so we can track when the command buffer finishes.
-    mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[0]);
+    mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[0]);
 
     // We need to notify Skia that we changed the layout of the wrapped VkImage
     sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface;
@@ -255,17 +501,14 @@
 void VulkanManager::destroyBuffers(VulkanSurface* surface) {
     if (surface->mBackbuffers) {
         for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
-            mWaitForFences(mBackendContext->fDevice, 2, surface->mBackbuffers[i].mUsageFences, true,
-                           UINT64_MAX);
+            mWaitForFences(mDevice, 2, surface->mBackbuffers[i].mUsageFences, true, UINT64_MAX);
             surface->mBackbuffers[i].mImageIndex = -1;
-            mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mAcquireSemaphore,
-                              nullptr);
-            mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mRenderSemaphore,
-                              nullptr);
-            mFreeCommandBuffers(mBackendContext->fDevice, mCommandPool, 2,
-                                surface->mBackbuffers[i].mTransitionCmdBuffers);
-            mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[0], 0);
-            mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[1], 0);
+            mDestroySemaphore(mDevice, surface->mBackbuffers[i].mAcquireSemaphore, nullptr);
+            mDestroySemaphore(mDevice, surface->mBackbuffers[i].mRenderSemaphore, nullptr);
+            mFreeCommandBuffers(mDevice, mCommandPool, 2,
+                    surface->mBackbuffers[i].mTransitionCmdBuffers);
+            mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[0], 0);
+            mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[1], 0);
         }
     }
 
@@ -282,29 +525,27 @@
     if (VK_NULL_HANDLE != mPresentQueue) {
         mQueueWaitIdle(mPresentQueue);
     }
-    mDeviceWaitIdle(mBackendContext->fDevice);
+    mDeviceWaitIdle(mDevice);
 
     destroyBuffers(surface);
 
     if (VK_NULL_HANDLE != surface->mSwapchain) {
-        mDestroySwapchainKHR(mBackendContext->fDevice, surface->mSwapchain, nullptr);
+        mDestroySwapchainKHR(mDevice, surface->mSwapchain, nullptr);
         surface->mSwapchain = VK_NULL_HANDLE;
     }
 
     if (VK_NULL_HANDLE != surface->mVkSurface) {
-        mDestroySurfaceKHR(mBackendContext->fInstance, surface->mVkSurface, nullptr);
+        mDestroySurfaceKHR(mInstance, surface->mVkSurface, nullptr);
         surface->mVkSurface = VK_NULL_HANDLE;
     }
     delete surface;
 }
 
 void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent) {
-    mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount,
-                           nullptr);
+    mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, nullptr);
     SkASSERT(surface->mImageCount);
     surface->mImages = new VkImage[surface->mImageCount];
-    mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount,
-                           surface->mImages);
+    mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, surface->mImages);
 
     SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
 
@@ -354,15 +595,15 @@
     for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
         SkDEBUGCODE(VkResult res);
         surface->mBackbuffers[i].mImageIndex = -1;
-        SkDEBUGCODE(res =) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr,
+        SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr,
                                             &surface->mBackbuffers[i].mAcquireSemaphore);
-        SkDEBUGCODE(res =) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr,
+        SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr,
                                             &surface->mBackbuffers[i].mRenderSemaphore);
-        SkDEBUGCODE(res =) mAllocateCommandBuffers(mBackendContext->fDevice, &commandBuffersInfo,
+        SkDEBUGCODE(res =) mAllocateCommandBuffers(mDevice, &commandBuffersInfo,
                                                    surface->mBackbuffers[i].mTransitionCmdBuffers);
-        SkDEBUGCODE(res =) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr,
+        SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr,
                                         &surface->mBackbuffers[i].mUsageFences[0]);
-        SkDEBUGCODE(res =) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr,
+        SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr,
                                         &surface->mBackbuffers[i].mUsageFences[1]);
         SkASSERT(VK_SUCCESS == res);
     }
@@ -372,35 +613,35 @@
 bool VulkanManager::createSwapchain(VulkanSurface* surface) {
     // check for capabilities
     VkSurfaceCapabilitiesKHR caps;
-    VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mBackendContext->fPhysicalDevice,
+    VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice,
                                                             surface->mVkSurface, &caps);
     if (VK_SUCCESS != res) {
         return false;
     }
 
     uint32_t surfaceFormatCount;
-    res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
+    res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface,
                                               &surfaceFormatCount, nullptr);
     if (VK_SUCCESS != res) {
         return false;
     }
 
     FatVector<VkSurfaceFormatKHR, 4> surfaceFormats(surfaceFormatCount);
-    res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
+    res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface,
                                               &surfaceFormatCount, surfaceFormats.data());
     if (VK_SUCCESS != res) {
         return false;
     }
 
     uint32_t presentModeCount;
-    res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
+    res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
                                                    surface->mVkSurface, &presentModeCount, nullptr);
     if (VK_SUCCESS != res) {
         return false;
     }
 
     FatVector<VkPresentModeKHR, VK_PRESENT_MODE_RANGE_SIZE_KHR> presentModes(presentModeCount);
-    res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
+    res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
                                                    surface->mVkSurface, &presentModeCount,
                                                    presentModes.data());
     if (VK_SUCCESS != res) {
@@ -482,8 +723,8 @@
     swapchainCreateInfo.imageArrayLayers = 1;
     swapchainCreateInfo.imageUsage = usageFlags;
 
-    uint32_t queueFamilies[] = {mBackendContext->fGraphicsQueueIndex, mPresentQueueIndex};
-    if (mBackendContext->fGraphicsQueueIndex != mPresentQueueIndex) {
+    uint32_t queueFamilies[] = {mGraphicsQueueIndex, mPresentQueueIndex};
+    if (mGraphicsQueueIndex != mPresentQueueIndex) {
         swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
         swapchainCreateInfo.queueFamilyIndexCount = 2;
         swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
@@ -499,19 +740,18 @@
     swapchainCreateInfo.clipped = true;
     swapchainCreateInfo.oldSwapchain = surface->mSwapchain;
 
-    res = mCreateSwapchainKHR(mBackendContext->fDevice, &swapchainCreateInfo, nullptr,
-                              &surface->mSwapchain);
+    res = mCreateSwapchainKHR(mDevice, &swapchainCreateInfo, nullptr, &surface->mSwapchain);
     if (VK_SUCCESS != res) {
         return false;
     }
 
     // destroy the old swapchain
     if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
-        mDeviceWaitIdle(mBackendContext->fDevice);
+        mDeviceWaitIdle(mDevice);
 
         destroyBuffers(surface);
 
-        mDestroySwapchainKHR(mBackendContext->fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
+        mDestroySwapchainKHR(mDevice, swapchainCreateInfo.oldSwapchain, nullptr);
     }
 
     createBuffers(surface, surfaceFormat, extent);
@@ -535,20 +775,18 @@
     surfaceCreateInfo.flags = 0;
     surfaceCreateInfo.window = window;
 
-    VkResult res = mCreateAndroidSurfaceKHR(mBackendContext->fInstance, &surfaceCreateInfo, nullptr,
-                                            &surface->mVkSurface);
+    VkResult res = mCreateAndroidSurfaceKHR(mInstance, &surfaceCreateInfo, nullptr,
+            &surface->mVkSurface);
     if (VK_SUCCESS != res) {
         delete surface;
         return nullptr;
     }
 
     SkDEBUGCODE(VkBool32 supported; res = mGetPhysicalDeviceSurfaceSupportKHR(
-                                            mBackendContext->fPhysicalDevice, mPresentQueueIndex,
-                                            surface->mVkSurface, &supported);
-                // All physical devices and queue families on Android must be capable of
-                // presentation with any
-                // native window.
-                SkASSERT(VK_SUCCESS == res && supported););
+            mPhysicalDevice, mPresentQueueIndex, surface->mVkSurface, &supported);
+    // All physical devices and queue families on Android must be capable of
+    // presentation with any native window.
+    SkASSERT(VK_SUCCESS == res && supported););
 
     if (!createSwapchain(surface)) {
         destroySurface(surface);
@@ -605,7 +843,7 @@
 void VulkanManager::swapBuffers(VulkanSurface* surface) {
     if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
         ATRACE_NAME("Finishing GPU work");
-        mDeviceWaitIdle(mBackendContext->fDevice);
+        mDeviceWaitIdle(mDevice);
     }
 
     SkASSERT(surface->mBackbuffers);
@@ -638,7 +876,7 @@
             dstAccessMask,                              // inputMask
             layout,                                     // oldLayout
             VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,            // newLayout
-            mBackendContext->fGraphicsQueueIndex,       // srcQueueFamilyIndex
+            mGraphicsQueueIndex,                        // srcQueueFamilyIndex
             mPresentQueueIndex,                         // dstQueueFamilyIndex
             surface->mImages[backbuffer->mImageIndex],  // image
             {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}     // subresourceRange
@@ -670,7 +908,7 @@
     submitInfo.pSignalSemaphores = &backbuffer->mRenderSemaphore;
 
     // Attach second fence to submission here so we can track when the command buffer finishes.
-    mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[1]);
+    mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[1]);
 
     // Submit present operation to present queue. We use a semaphore here to make sure all rendering
     // to the image is complete and that the layout has been change to present on the graphics
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index c319c9e..69641d5 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -17,11 +17,14 @@
 #ifndef VULKANMANAGER_H
 #define VULKANMANAGER_H
 
+#if !defined(VK_USE_PLATFORM_ANDROID_KHR)
+#  define VK_USE_PLATFORM_ANDROID_KHR
+#endif
+#include <vulkan/vulkan.h>
+
 #include <SkSurface.h>
 #include <vk/GrVkBackendContext.h>
 
-#include <vulkan/vulkan.h>
-
 namespace android {
 namespace uirenderer {
 namespace renderthread {
@@ -79,7 +82,7 @@
     void initialize();
 
     // Quick check to see if the VulkanManager has been initialized.
-    bool hasVkContext() { return mBackendContext.get() != nullptr; }
+    bool hasVkContext() { return mDevice != VK_NULL_HANDLE; }
 
     // Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new
     // VulkanSurface object which is returned.
@@ -111,6 +114,10 @@
     explicit VulkanManager(RenderThread& thread);
     ~VulkanManager() { destroy(); }
 
+    // Sets up the VkInstance and VkDevice objects. Also fills out the passed in
+    // VkPhysicalDeviceFeatures struct.
+    bool setupDevice(VkPhysicalDeviceFeatures& deviceFeatures);
+
     void destroyBuffers(VulkanSurface* surface);
 
     bool createSwapchain(VulkanSurface* surface);
@@ -148,7 +155,21 @@
     VkPtr<PFN_vkQueuePresentKHR> mQueuePresentKHR;
     VkPtr<PFN_vkCreateSharedSwapchainsKHR> mCreateSharedSwapchainsKHR;
 
-    // Additional vulkan functions
+    // Instance Functions
+    VkPtr<PFN_vkEnumerateInstanceExtensionProperties> mEnumerateInstanceExtensionProperties;
+    VkPtr<PFN_vkCreateInstance> mCreateInstance;
+
+    VkPtr<PFN_vkDestroyInstance> mDestroyInstance;
+    VkPtr<PFN_vkEnumeratePhysicalDevices> mEnumeratePhysicalDevices;
+    VkPtr<PFN_vkGetPhysicalDeviceQueueFamilyProperties> mGetPhysicalDeviceQueueFamilyProperties;
+    VkPtr<PFN_vkGetPhysicalDeviceFeatures> mGetPhysicalDeviceFeatures;
+    VkPtr<PFN_vkCreateDevice> mCreateDevice;
+    VkPtr<PFN_vkEnumerateDeviceExtensionProperties> mEnumerateDeviceExtensionProperties;
+
+    // Device Functions
+    VkPtr<PFN_vkGetDeviceQueue> mGetDeviceQueue;
+    VkPtr<PFN_vkDeviceWaitIdle> mDeviceWaitIdle;
+    VkPtr<PFN_vkDestroyDevice> mDestroyDevice;
     VkPtr<PFN_vkCreateCommandPool> mCreateCommandPool;
     VkPtr<PFN_vkDestroyCommandPool> mDestroyCommandPool;
     VkPtr<PFN_vkAllocateCommandBuffers> mAllocateCommandBuffers;
@@ -158,10 +179,8 @@
     VkPtr<PFN_vkEndCommandBuffer> mEndCommandBuffer;
     VkPtr<PFN_vkCmdPipelineBarrier> mCmdPipelineBarrier;
 
-    VkPtr<PFN_vkGetDeviceQueue> mGetDeviceQueue;
     VkPtr<PFN_vkQueueSubmit> mQueueSubmit;
     VkPtr<PFN_vkQueueWaitIdle> mQueueWaitIdle;
-    VkPtr<PFN_vkDeviceWaitIdle> mDeviceWaitIdle;
 
     VkPtr<PFN_vkCreateSemaphore> mCreateSemaphore;
     VkPtr<PFN_vkDestroySemaphore> mDestroySemaphore;
@@ -172,7 +191,12 @@
 
     RenderThread& mRenderThread;
 
-    sk_sp<const GrVkBackendContext> mBackendContext;
+    VkInstance mInstance = VK_NULL_HANDLE;
+    VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
+    VkDevice mDevice = VK_NULL_HANDLE;
+
+    uint32_t mGraphicsQueueIndex;
+    VkQueue mGraphicsQueue = VK_NULL_HANDLE;
     uint32_t mPresentQueueIndex;
     VkQueue mPresentQueue = VK_NULL_HANDLE;
     VkCommandPool mCommandPool = VK_NULL_HANDLE;
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 7f8cb2d..3d50d2d 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -127,7 +127,7 @@
         return false;
     }
     void* addr = mmap(nullptr, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
-    if (!addr) {
+    if (addr == MAP_FAILED) {
         int err = errno;
         // The file not existing is normal for addToDump(), so only log if
         // we get an unexpected error
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index b74d359..e3c97ce 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -136,8 +136,6 @@
         gBenchmarkReporter.reset(new benchmark::ConsoleReporter());
     } else if (!strcmp(format, "json")) {
         gBenchmarkReporter.reset(new benchmark::JSONReporter());
-    } else if (!strcmp(format, "csv")) {
-        gBenchmarkReporter.reset(new benchmark::CSVReporter());
     } else {
         fprintf(stderr, "Unknown format '%s'", format);
         return false;
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 15c0ab1..eb67e6c 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -1094,7 +1094,7 @@
     class ShadowTestCanvas : public SkCanvas {
     public:
         ShadowTestCanvas(int width, int height) : SkCanvas(width, height) {}
-        int getIndex() { return mDrawCounter; }
+        int getDrawCounter() { return mDrawCounter; }
 
         virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
             // expect to draw 2 RenderNodeDrawable, 1 StartReorderBarrierDrawable,
@@ -1109,17 +1109,36 @@
             EXPECT_EQ(dy, TRANSLATE_Y);
         }
 
-        virtual void didConcat(const SkMatrix& matrix) override {
-            // This function is invoked by EndReorderBarrierDrawable::drawShadow to apply shadow
-            // matrix.
+        virtual void didSetMatrix(const SkMatrix& matrix) override {
             mDrawCounter++;
-            EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X, CASTER_Y), matrix);
-            EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
-                      getTotalMatrix());
+            // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
+            // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
+            EXPECT_TRUE(matrix.isIdentity());
+            EXPECT_TRUE(getTotalMatrix().isIdentity());
+        }
+
+        virtual void didConcat(const SkMatrix& matrix) override {
+            mDrawCounter++;
+            if (mFirstDidConcat) {
+                // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
+                mFirstDidConcat = false;
+                EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
+                          matrix);
+                EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
+                          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());
+            }
         }
 
     protected:
         int mDrawCounter = 0;
+    private:
+        bool mFirstDidConcat = true;
     };
 
     auto parent = TestUtils::createSkiaNode(
@@ -1143,7 +1162,7 @@
     ShadowTestCanvas canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
     RenderNodeDrawable drawable(parent.get(), &canvas, false);
     canvas.drawDrawable(&drawable);
-    EXPECT_EQ(6, canvas.getIndex());
+    EXPECT_EQ(9, canvas.getDrawCounter());
 }
 
 // Draw a vector drawable twice but with different bounds and verify correct bounds are used.
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 75740e8..a3e7859 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -16,8 +16,10 @@
 
 #include "Color.h"
 
-
 #include <utils/Log.h>
+#include <ui/ColorSpace.h>
+
+#include <algorithm>
 #include <cmath>
 
 namespace android {
@@ -107,5 +109,97 @@
     }
 }
 
+template<typename T>
+static constexpr T clamp(T x, T min, T max) {
+    return x < min ? min : x > max ? max : x;
+}
+
+//static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
+static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
+static const mat3 BRADFORD = mat3{
+        float3{ 0.8951f, -0.7502f,  0.0389f},
+        float3{ 0.2664f,  1.7135f, -0.0685f},
+        float3{-0.1614f,  0.0367f,  1.0296f}
+};
+
+static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
+    float3 srcLMS = matrix * srcWhitePoint;
+    float3 dstLMS = matrix * dstWhitePoint;
+    return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
+}
+
+namespace LabColorSpace {
+
+static constexpr float A = 216.0f / 24389.0f;
+static constexpr float B = 841.0f / 108.0f;
+static constexpr float C = 4.0f / 29.0f;
+static constexpr float D = 6.0f / 29.0f;
+
+float3 toXyz(const Lab& lab) {
+    float3 v { lab.L, lab.a, lab.b };
+    v[0] = clamp(v[0], 0.0f, 100.0f);
+    v[1] = clamp(v[1], -128.0f, 128.0f);
+    v[2] = clamp(v[2], -128.0f, 128.0f);
+
+    float fy = (v[0] + 16.0f) / 116.0f;
+    float fx = fy + (v[1] * 0.002f);
+    float fz = fy - (v[2] * 0.005f);
+    float X = fx > D ? fx * fx * fx : (1.0f / B) * (fx - C);
+    float Y = fy > D ? fy * fy * fy : (1.0f / B) * (fy - C);
+    float Z = fz > D ? fz * fz * fz : (1.0f / B) * (fz - C);
+
+    v[0] = X * ILLUMINANT_D50_XYZ[0];
+    v[1] = Y * ILLUMINANT_D50_XYZ[1];
+    v[2] = Z * ILLUMINANT_D50_XYZ[2];
+
+    return v;
+}
+
+Lab fromXyz(const float3& v) {
+    float X = v[0] / ILLUMINANT_D50_XYZ[0];
+    float Y = v[1] / ILLUMINANT_D50_XYZ[1];
+    float Z = v[2] / ILLUMINANT_D50_XYZ[2];
+
+    float fx = X > A ? pow(X, 1.0f / 3.0f) : B * X + C;
+    float fy = Y > A ? pow(Y, 1.0f / 3.0f) : B * Y + C;
+    float fz = Z > A ? pow(Z, 1.0f / 3.0f) : B * Z + C;
+
+    float L = 116.0f * fy - 16.0f;
+    float a = 500.0f * (fx - fy);
+    float b = 200.0f * (fy - fz);
+
+    return Lab {
+            clamp(L, 0.0f, 100.0f),
+            clamp(a, -128.0f, 128.0f),
+            clamp(b, -128.0f, 128.0f)
+    };
+}
+
+};
+
+Lab sRGBToLab(SkColor color) {
+    auto colorSpace = ColorSpace::sRGB();
+    float3 rgb;
+    rgb.r = SkColorGetR(color) / 255.0f;
+    rgb.g = SkColorGetG(color) / 255.0f;
+    rgb.b = SkColorGetB(color) / 255.0f;
+    float3 xyz = colorSpace.rgbToXYZ(rgb);
+    float3 srcXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
+    xyz = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * xyz;
+    return LabColorSpace::fromXyz(xyz);
+}
+
+SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) {
+    auto colorSpace = ColorSpace::sRGB();
+    float3 xyz = LabColorSpace::toXyz(lab);
+    float3 dstXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
+    xyz = adaptation(BRADFORD, ILLUMINANT_D50_XYZ, dstXYZ) * xyz;
+    float3 rgb = colorSpace.xyzToRGB(xyz);
+    return SkColorSetARGB(alpha,
+            static_cast<uint8_t>(rgb.r * 255),
+            static_cast<uint8_t>(rgb.g * 255),
+            static_cast<uint8_t>(rgb.b * 255));
+}
+
 };  // namespace uirenderer
 };  // namespace android
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 2bec1f5..3c13a54 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -114,6 +114,16 @@
 bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
 
 sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
+
+struct Lab {
+    float L;
+    float a;
+    float b;
+};
+
+Lab sRGBToLab(SkColor color);
+SkColor LabToSRGB(const Lab& lab, SkAlpha alpha);
+
 } /* namespace uirenderer */
 } /* namespace android */
 
diff --git a/libs/incident/proto/android/section.proto b/libs/incident/proto/android/section.proto
index 45f3c91..5afe22a 100644
--- a/libs/incident/proto/android/section.proto
+++ b/libs/incident/proto/android/section.proto
@@ -51,10 +51,9 @@
 message SectionFlags {
   optional SectionType type = 1 [default = SECTION_NONE];
   optional string args = 2;
-  optional bool device_specific = 3 [default = false];
   // If true, then the section will only be generated for userdebug and eng
   // builds.
-  optional bool userdebug_and_eng_only = 4 [default = false];
+  optional bool userdebug_and_eng_only = 3 [default = false];
 }
 
 extend google.protobuf.FieldOptions {
diff --git a/libs/usb/Android.bp b/libs/usb/Android.bp
index b8f2904..027a748 100644
--- a/libs/usb/Android.bp
+++ b/libs/usb/Android.bp
@@ -1 +1,23 @@
+//
+// 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.
+//
+
+java_sdk_library {
+    name: "com.android.future.usb.accessory",
+    srcs: ["src/**/*.java"],
+    api_packages: ["com.android.future.usb"],
+}
+
 subdirs = ["tests/*"]
diff --git a/libs/usb/Android.mk b/libs/usb/Android.mk
deleted file mode 100644
index 129828f..0000000
--- a/libs/usb/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Copyright (C) 2011 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT 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_SRC_FILES := $(call all-java-files-under,src)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE:= com.android.future.usb.accessory
-
-include $(BUILD_JAVA_LIBRARY)
diff --git a/libs/usb/api/current.txt b/libs/usb/api/current.txt
new file mode 100644
index 0000000..8488db5
--- /dev/null
+++ b/libs/usb/api/current.txt
@@ -0,0 +1,25 @@
+package com.android.future.usb {
+
+  public class UsbAccessory {
+    method public java.lang.String getDescription();
+    method public java.lang.String getManufacturer();
+    method public java.lang.String getModel();
+    method public java.lang.String getSerial();
+    method public java.lang.String getUri();
+    method public java.lang.String getVersion();
+  }
+
+  public class UsbManager {
+    method public static com.android.future.usb.UsbAccessory getAccessory(android.content.Intent);
+    method public com.android.future.usb.UsbAccessory[] getAccessoryList();
+    method public static com.android.future.usb.UsbManager getInstance(android.content.Context);
+    method public boolean hasPermission(com.android.future.usb.UsbAccessory);
+    method public android.os.ParcelFileDescriptor openAccessory(com.android.future.usb.UsbAccessory);
+    method public void requestPermission(com.android.future.usb.UsbAccessory, android.app.PendingIntent);
+    field public static final java.lang.String ACTION_USB_ACCESSORY_ATTACHED = "android.hardware.usb.action.USB_ACCESSORY_ATTACHED";
+    field public static final java.lang.String ACTION_USB_ACCESSORY_DETACHED = "android.hardware.usb.action.USB_ACCESSORY_DETACHED";
+    field public static final java.lang.String EXTRA_PERMISSION_GRANTED = "permission";
+  }
+
+}
+
diff --git a/libs/usb/api/removed.txt b/libs/usb/api/removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/usb/api/removed.txt
diff --git a/libs/usb/api/system-current.txt b/libs/usb/api/system-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/usb/api/system-current.txt
diff --git a/libs/usb/api/system-removed.txt b/libs/usb/api/system-removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/usb/api/system-removed.txt
diff --git a/libs/usb/api/test-current.txt b/libs/usb/api/test-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/usb/api/test-current.txt
diff --git a/libs/usb/api/test-removed.txt b/libs/usb/api/test-removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/usb/api/test-removed.txt
diff --git a/libs/usb/tests/AccessoryChat/Android.mk b/libs/usb/tests/AccessoryChat/Android.mk
index ecb455a..cfe2da1 100644
--- a/libs/usb/tests/AccessoryChat/Android.mk
+++ b/libs/usb/tests/AccessoryChat/Android.mk
@@ -23,4 +23,6 @@
 
 LOCAL_PACKAGE_NAME := AccessoryChat
 
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
 include $(BUILD_PACKAGE)
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 6eb3d8d..30a07ef 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -22,7 +22,6 @@
 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;
@@ -42,16 +41,11 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.ArraySet;
 import android.util.Log;
 import com.android.internal.location.ProviderProperties;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Set;
 
 /**
  * This class provides access to the system location services.  These
@@ -331,7 +325,7 @@
             Message msg = Message.obtain();
             msg.what = TYPE_LOCATION_CHANGED;
             msg.obj = location;
-            mListenerHandler.sendMessage(msg);
+            sendCallbackMessage(msg);
         }
 
         @Override
@@ -345,7 +339,7 @@
                 b.putBundle("extras", extras);
             }
             msg.obj = b;
-            mListenerHandler.sendMessage(msg);
+            sendCallbackMessage(msg);
         }
 
         @Override
@@ -353,7 +347,7 @@
             Message msg = Message.obtain();
             msg.what = TYPE_PROVIDER_ENABLED;
             msg.obj = provider;
-            mListenerHandler.sendMessage(msg);
+            sendCallbackMessage(msg);
         }
 
         @Override
@@ -361,7 +355,13 @@
             Message msg = Message.obtain();
             msg.what = TYPE_PROVIDER_DISABLED;
             msg.obj = provider;
-            mListenerHandler.sendMessage(msg);
+            sendCallbackMessage(msg);
+        }
+
+        private void sendCallbackMessage(Message msg) {
+            if (!mListenerHandler.sendMessage(msg)) {
+                locationCallbackFinished();
+            }
         }
 
         private void _handleMessage(Message msg) {
@@ -384,6 +384,10 @@
                     mListener.onProviderDisabled((String) msg.obj);
                     break;
             }
+            locationCallbackFinished();
+        }
+
+        private void locationCallbackFinished() {
             try {
                 mService.locationCallbackFinished(this);
             } catch (RemoteException e) {
@@ -730,6 +734,11 @@
      * calling Activity. If a Looper is specified with a {@link LocationListener}
      * then callbacks are made on the supplied Looper thread.
      *
+     * <p> When location callbacks are invoked, the system will hold a wakelock
+     * on your application's behalf for some period of time, but not
+     * indefinitely. If your application requires a long running wakelock
+     * within the location callback, you should acquire it yourself.
+     *
      * <p class="note"> Prior to Jellybean, the minTime parameter was
      * only a hint, and some location provider implementations ignored it.
      * From Jellybean and onwards it is mandatory for Android compatible
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
new file mode 100644
index 0000000..447195d
--- /dev/null
+++ b/location/lib/Android.bp
@@ -0,0 +1,21 @@
+//
+// 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.
+//
+
+java_sdk_library {
+    name: "com.android.location.provider",
+    srcs: ["java/**/*.java"],
+    api_packages: ["com.android.location.provider"],
+}
diff --git a/location/lib/Android.mk b/location/lib/Android.mk
deleted file mode 100644
index 6642134..0000000
--- a/location/lib/Android.mk
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH := $(call my-dir)
-
-# the library
-# ============================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= com.android.location.provider
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-include $(BUILD_JAVA_LIBRARY)
-
-
-# ====  com.google.location.xml lib def  ========================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := com.android.location.provider.xml
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_CLASS := ETC
-
-# This will install the file in /system/etc/permissions
-#
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
-
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-
-include $(BUILD_PREBUILT)
-
-# ==== Stub library  ===========================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.location.provider-stubs-gen
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_SRC_FILES := $(call all-java-files-under,java)
-LOCAL_DROIDDOC_STUB_OUT_DIR := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/com.android.location.provider.stubs_intermediates/src
-LOCAL_DROIDDOC_OPTIONS:= \
-    -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
-    -stubpackages com.android.location.provider \
-    -nodocs
-LOCAL_UNINSTALLABLE_MODULE := true
-include $(BUILD_DROIDDOC)
-com_android_nfc_extras_gen_stamp := $(full_target)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.location.provider.stubs
-LOCAL_SOURCE_FILES_ALL_GENERATED := true
-LOCAL_SDK_VERSION := current
-LOCAL_ADDITIONAL_DEPENDENCIES := $(com_android_nfc_extras_gen_stamp)
-com_android_nfc_extras_gen_stamp :=
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
new file mode 100644
index 0000000..1e69f16
--- /dev/null
+++ b/location/lib/api/current.txt
@@ -0,0 +1,47 @@
+package com.android.location.provider {
+
+  public abstract deprecated class FusedProvider {
+    ctor public FusedProvider();
+    method public android.os.IBinder getBinder();
+  }
+
+  public abstract class LocationProviderBase {
+    ctor public LocationProviderBase(java.lang.String, com.android.location.provider.ProviderPropertiesUnbundled);
+    method public android.os.IBinder getBinder();
+    method public abstract void onDisable();
+    method public void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public abstract void onEnable();
+    method public abstract int onGetStatus(android.os.Bundle);
+    method public abstract long onGetStatusUpdateTime();
+    method public boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
+    method public abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
+    method public final void reportLocation(android.location.Location);
+    field public static final java.lang.String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
+    field public static final java.lang.String FUSED_PROVIDER = "fused";
+  }
+
+  public final class LocationRequestUnbundled {
+    method public long getFastestInterval();
+    method public long getInterval();
+    method public int getQuality();
+    method public float getSmallestDisplacement();
+    field public static final int ACCURACY_BLOCK = 102; // 0x66
+    field public static final int ACCURACY_CITY = 104; // 0x68
+    field public static final int ACCURACY_FINE = 100; // 0x64
+    field public static final int POWER_HIGH = 203; // 0xcb
+    field public static final int POWER_LOW = 201; // 0xc9
+    field public static final int POWER_NONE = 200; // 0xc8
+  }
+
+  public final class ProviderPropertiesUnbundled {
+    method public static com.android.location.provider.ProviderPropertiesUnbundled create(boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
+  }
+
+  public final class ProviderRequestUnbundled {
+    method public long getInterval();
+    method public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
+    method public boolean getReportLocation();
+  }
+
+}
+
diff --git a/location/lib/api/removed.txt b/location/lib/api/removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/location/lib/api/removed.txt
diff --git a/location/lib/api/system-current.txt b/location/lib/api/system-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/location/lib/api/system-current.txt
diff --git a/location/lib/api/system-removed.txt b/location/lib/api/system-removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/location/lib/api/system-removed.txt
diff --git a/location/lib/api/test-current.txt b/location/lib/api/test-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/location/lib/api/test-current.txt
diff --git a/location/lib/api/test-removed.txt b/location/lib/api/test-removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/location/lib/api/test-removed.txt
diff --git a/location/lib/com.android.location.provider.xml b/location/lib/com.android.location.provider.xml
deleted file mode 100644
index 000e68f..0000000
--- a/location/lib/com.android.location.provider.xml
+++ /dev/null
@@ -1,20 +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.
--->
-
-<permissions>
-    <library name="com.android.location.provider"
-            file="/system/framework/com.android.location.provider.jar" />
-</permissions>
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index e245425..1030d9d 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -1083,6 +1083,7 @@
             ENCODING_AAC_LC,
             ENCODING_DOLBY_TRUEHD,
             ENCODING_E_AC3_JOC,
+            ENCODING_AC4,
     };
 
     /** @hide */
@@ -1093,7 +1094,8 @@
             ENCODING_DTS_HD,
             ENCODING_AAC_LC,
             ENCODING_DOLBY_TRUEHD,
-            ENCODING_E_AC3_JOC }
+            ENCODING_E_AC3_JOC,
+            ENCODING_AC4 }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface SurroundSoundEncoding {}
@@ -1105,14 +1107,14 @@
      * It is just a default to use if an international name is not available.
      *
      * @param audioFormat a surround format
-     * @return short default name for the format, eg. “AC3” for ENCODING_AC3.
+     * @return short default name for the format.
      */
     public static String toDisplayName(@SurroundSoundEncoding int audioFormat) {
         switch (audioFormat) {
             case ENCODING_AC3:
-                return "Dolby Digital (AC3)";
+                return "Dolby Digital";
             case ENCODING_E_AC3:
-                return "Dolby Digital Plus (E_AC3)";
+                return "Dolby Digital Plus";
             case ENCODING_DTS:
                 return "DTS";
             case ENCODING_DTS_HD:
@@ -1122,7 +1124,9 @@
             case ENCODING_DOLBY_TRUEHD:
                 return "Dolby TrueHD";
             case ENCODING_E_AC3_JOC:
-                return "Dolby Atmos";
+                return "Dolby Atmos in Dolby Digital Plus";
+            case ENCODING_AC4:
+                return "Dolby AC-4";
             default:
                 return "Unknown surround sound format";
         }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 196b886..1a282b2 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -794,6 +794,11 @@
      * <p>
      * This method should only be used by applications that replace the platform-wide
      * management of audio settings or the main telephony application.
+     * <p>This method has no effect if the device implements a fixed volume policy
+     * as indicated by {@link #isVolumeFixed()}.
+     * <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
+     * unless the app has been granted Do Not Disturb Access.
+     * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
      *
      * @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL},
      * {@link #STREAM_SYSTEM}, {@link #STREAM_RING}, {@link #STREAM_MUSIC},
@@ -804,6 +809,8 @@
      * @param flags One or more flags.
      * @see #adjustVolume(int, int)
      * @see #setStreamVolume(int, int, int)
+     * @throws SecurityException if the adjustment triggers a Do Not Disturb change
+     *   and the caller is not granted notification policy access.
      */
     public void adjustStreamVolume(int streamType, int direction, int flags) {
         final IAudioService service = getService();
@@ -1121,6 +1128,8 @@
      * @see #getStreamMaxVolume(int)
      * @see #getStreamVolume(int)
      * @see #isVolumeFixed()
+     * @throws SecurityException if the volume change triggers a Do Not Disturb change
+     *   and the caller is not granted notification policy access.
      */
     public void setStreamVolume(int streamType, int index, int flags) {
         final IAudioService service = getService();
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index aa45709..450a656 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -2537,7 +2537,9 @@
                     if (size == 0) {
                         return 0;
                     }
-                    if (position < 0) {
+                    // We don't allow read positions after the available bytes,
+                    // the input stream won't be able to seek back then.
+                    if (position < 0 || position >= in.available()) {
                         return -1;
                     }
                     try {
@@ -2546,6 +2548,13 @@
                             mPosition = position;
                         }
 
+                        // If the read will cause us to go over the available bytes,
+                        // reduce the size so that we stay in the available range.
+                        // Otherwise the input stream may not be able to seek back.
+                        if (mPosition + size > in.available()) {
+                            size = in.available() - (int)mPosition;
+                        }
+
                         int bytesRead = in.read(buffer, offset, size);
                         if (bytesRead >= 0) {
                             mPosition += bytesRead;
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 3a0a58e..60cad90 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -1523,7 +1523,7 @@
                 // this should never happen as mWrappedKey is initialized in
                 // JNI after construction of the KeyRequest object. The check
                 // is needed here to guarantee @NonNull annotation.
-                throw new RuntimeException("Cerfificate is not initialized");
+                throw new RuntimeException("Certificate is not initialized");
             }
             return mWrappedKey;
         }
@@ -1537,7 +1537,7 @@
                 // this should never happen as mCertificateData is initialized in
                 // JNI after construction of the KeyRequest object. The check
                 // is needed here to guarantee @NonNull annotation.
-                throw new RuntimeException("Cerfificate is not initialized");
+                throw new RuntimeException("Certificate is not initialized");
             }
             return mCertificateData;
         }
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 7c68b55..2a2f4fe 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -143,7 +143,7 @@
      * MIME type for HEIF still image data encoded in HEVC.
      *
      * To decode such an image, {@link MediaCodec} decoder for
-     * {@ #MIMETYPE_VIDEO_HEVC} shall be used. The client needs to form
+     * {@link #MIMETYPE_VIDEO_HEVC} shall be used. The client needs to form
      * the correct {@link #MediaFormat} based on additional information in
      * the track format, and send it to {@link MediaCodec#configure}.
      *
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index aae1f51..6bf52bd 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -323,8 +323,10 @@
         StrictMode.setThreadPolicy(policy);
 
         try {
-            if (offset != mCurrentOffset) {
-                seekTo(offset);
+            synchronized(this) {
+                if (offset != mCurrentOffset) {
+                    seekTo(offset);
+                }
             }
 
             int n = mInputStream.read(data, 0, size);
@@ -366,7 +368,7 @@
     }
 
     @Override
-    public long getSize() {
+    public synchronized long getSize() {
         if (mConnection == null) {
             try {
                 seekTo(0);
@@ -379,7 +381,7 @@
     }
 
     @Override
-    public String getMIMEType() {
+    public synchronized String getMIMEType() {
         if (mConnection == null) {
             try {
                 seekTo(0);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index ada91be..d532e52 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2510,10 +2510,10 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(mTrackType);
+            dest.writeString(mFormat.getString(MediaFormat.KEY_MIME));
             dest.writeString(getLanguage());
 
             if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
-                dest.writeString(mFormat.getString(MediaFormat.KEY_MIME));
                 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT));
                 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT));
                 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE));
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index dcc872c..bcff5bf 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -116,9 +116,9 @@
  *         these circumstances. Sometimes, due to programming errors, invoking a playback
  *         control operation in an invalid state may also occur. Under all these
  *         error conditions, the internal player engine invokes a user supplied
- *         MediaPlayer2EventCallback.onError() method if an MediaPlayer2EventCallback has been
+ *         EventCallback.onError() method if an EventCallback has been
  *         registered beforehand via
- *         {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
+ *         {@link #setEventCallback(Executor, EventCallback)}.
  *         <ul>
  *         <li>It is important to note that once an error occurs, the
  *         MediaPlayer2 object enters the <em>Error</em> state (except as noted
@@ -159,9 +159,9 @@
  *         player engine continues working on the rest of preparation work
  *         until the preparation work completes. When the preparation completes,
  *         the internal player engine then calls a user supplied callback method,
- *         onInfo() of the MediaPlayer2EventCallback interface with {@link #MEDIA_INFO_PREPARED},
- *         if an MediaPlayer2EventCallback is registered beforehand via
- *         {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.</li>
+ *         onInfo() of the EventCallback interface with {@link #MEDIA_INFO_PREPARED},
+ *         if an EventCallback is registered beforehand via
+ *         {@link #setEventCallback(Executor, EventCallback)}.</li>
  *         <li>It is important to note that
  *         the <em>Preparing</em> state is a transient state, and the behavior
  *         of calling any method with side effect while a MediaPlayer2 object is
@@ -180,10 +180,10 @@
  *         whether the MediaPlayer2 object is in the <em>Started</em> state.
  *         <ul>
  *         <li>While in the <em>Started</em> state, the internal player engine calls
- *         a user supplied callback method MediaPlayer2EventCallback.onInfo() with
- *         {@link #MEDIA_INFO_BUFFERING_UPDATE} if an MediaPlayer2EventCallback has been
+ *         a user supplied callback method EventCallback.onInfo() with
+ *         {@link #MEDIA_INFO_BUFFERING_UPDATE} if an EventCallback has been
  *         registered beforehand via
- *         {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
+ *         {@link #setEventCallback(Executor, EventCallback)}.
  *         This callback allows applications to keep track of the buffering status
  *         while streaming audio/video.</li>
  *         <li>Calling {@link #play()} has not effect
@@ -215,10 +215,10 @@
  *         call returns right away, the actual seek operation may take a while to
  *         finish, especially for audio/video being streamed. When the actual
  *         seek operation completes, the internal player engine calls a user
- *         supplied MediaPlayer2EventCallback.onCallCompleted() with
+ *         supplied EventCallback.onCallCompleted() with
  *         {@link #CALL_COMPLETED_SEEK_TO}
- *         if an MediaPlayer2EventCallback has been registered beforehand via
- *         {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.</li>
+ *         if an EventCallback has been registered beforehand via
+ *         {@link #setEventCallback(Executor, EventCallback)}.</li>
  *         <li>Please
  *         note that {@link #seekTo(long, int)} can also be called in the other states,
  *         such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted
@@ -238,9 +238,9 @@
  *         the MediaPlayer2 object shall remain in the <em>Started</em> state.</li>
  *         <li>If the looping mode was set to <var>false
  *         </var>, the player engine calls a user supplied callback method,
- *         MediaPlayer2EventCallback.onCompletion(), if an MediaPlayer2EventCallback is
+ *         EventCallback.onCompletion(), if an EventCallback is
  *         registered beforehand via
- *         {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
+ *         {@link #setEventCallback(Executor, EventCallback)}.
  *         The invoke of the callback signals that the object is now in the <em>
  *         PlaybackCompleted</em> state.</li>
  *         <li>While in the <em>PlaybackCompleted</em>
@@ -387,7 +387,7 @@
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
  *         the object state. </p></td></tr>
- * <tr><td>setMediaPlayer2EventCallback </p></td>
+ * <tr><td>setEventCallback </p></td>
  *     <td>any </p></td>
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
@@ -445,7 +445,7 @@
  * possible runtime errors during playback or streaming. Registration for
  * these events is done by properly setting the appropriate listeners (via calls
  * to
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)},
+ * {@link #setEventCallback(Executor, EventCallback)},
  * {@link #setDrmEventCallback(Executor, DrmEventCallback)}).
  * In order to receive the respective callback
  * associated with these listeners, applications are required to create
@@ -453,8 +453,8 @@
  * thread by default has a Looper running).
  *
  */
-public abstract class MediaPlayer2 extends MediaPlayerBase
-                                   implements SubtitleController.Listener
+public abstract class MediaPlayer2 implements SubtitleController.Listener
+                                            , AutoCloseable
                                             , AudioRouting {
     /**
      * Create a MediaPlayer2 object.
@@ -514,6 +514,12 @@
     public MediaPlayer2() { }
 
     /**
+     * Returns a {@link MediaPlayerBase} implementation which runs based on
+     * this MediaPlayer2 instance.
+     */
+    public abstract MediaPlayerBase getMediaPlayerBase();
+
+    /**
      * Releases the resources held by this {@code MediaPlayer2} object.
      *
      * It is considered good practice to call this method when you're
@@ -549,7 +555,6 @@
      *
      */
     // This is an asynchronous call.
-    @Override
     public abstract void play();
 
     /**
@@ -560,21 +565,18 @@
      *
      */
     // This is an asynchronous call.
-    @Override
     public abstract void prepare();
 
     /**
      * Pauses playback. Call play() to resume.
      */
     // This is an asynchronous call.
-    @Override
     public abstract void pause();
 
     /**
      * Tries to play next data source if applicable.
      */
     // This is an asynchronous call.
-    @Override
     public abstract void skipToNext();
 
     /**
@@ -584,7 +586,6 @@
      * @param msec the offset in milliseconds from the start to seek to
      */
     // This is an asynchronous call.
-    @Override
     public void seekTo(long msec) {
         seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */);
     }
@@ -594,7 +595,6 @@
      *
      * @return the current position in milliseconds
      */
-    @Override
     public abstract long getCurrentPosition();
 
     /**
@@ -603,7 +603,6 @@
      * @return the duration in milliseconds, if no duration is available
      *         (for example, if streaming live content), -1 is returned.
      */
-    @Override
     public abstract long getDuration();
 
     /**
@@ -615,25 +614,60 @@
      *
      * @return the current buffered media source position in milliseconds
      */
-    @Override
     public abstract long getBufferedPosition();
 
     /**
+     * MediaPlayer2 has not been prepared or just has been reset.
+     * In this state, MediaPlayer2 doesn't fetch data.
+     * @hide
+     */
+    public static final int PLAYER_STATE_IDLE = 1001;
+
+    /**
+     * MediaPlayer2 has been just prepared.
+     * In this state, MediaPlayer2 just fetches data from media source,
+     * but doesn't actively render data.
+     * @hide
+     */
+    public static final int PLAYER_STATE_PREPARED = 1002;
+
+    /**
+     * MediaPlayer2 is paused.
+     * In this state, MediaPlayer2 doesn't actively render data.
+     * @hide
+     */
+    public static final int PLAYER_STATE_PAUSED = 1003;
+
+    /**
+     * MediaPlayer2 is actively playing back data.
+     * @hide
+     */
+    public static final int PLAYER_STATE_PLAYING = 1004;
+
+    /**
+     * MediaPlayer2 has hit some fatal error and cannot continue playback.
+     * @hide
+     */
+    public static final int PLAYER_STATE_ERROR = 1005;
+
+    /**
+     * @hide
+     */
+    @IntDef(flag = false, prefix = "MEDIAPLAYER2_STATE", value = {
+        PLAYER_STATE_IDLE,
+        PLAYER_STATE_PREPARED,
+        PLAYER_STATE_PAUSED,
+        PLAYER_STATE_PLAYING,
+        PLAYER_STATE_ERROR })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaPlayer2State {}
+
+    /**
      * Gets the current player state.
      *
      * @return the current player state.
      */
-    @Override
-    public abstract @PlayerState int getPlayerState();
-
-    /**
-     * Gets the current buffering state of the player.
-     * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
-     * buffered.
-     * @return the buffering state, one of the following:
-     */
-    @Override
-    public abstract @BuffState int getBufferingState();
+    public abstract @MediaPlayer2State int getState();
 
     /**
      * Sets the audio attributes for this MediaPlayer2.
@@ -643,14 +677,12 @@
      * @param attributes a non-null set of audio attributes
      */
     // This is an asynchronous call.
-    @Override
     public abstract void setAudioAttributes(@NonNull AudioAttributes attributes);
 
     /**
      * Gets the audio attributes for this MediaPlayer2.
      * @return attributes a set of audio attributes
      */
-    @Override
     public abstract @Nullable AudioAttributes getAudioAttributes();
 
     /**
@@ -659,7 +691,6 @@
      * @param dsd the descriptor of data source you want to play
      */
     // This is an asynchronous call.
-    @Override
     public abstract void setDataSource(@NonNull DataSourceDesc dsd);
 
     /**
@@ -669,7 +700,6 @@
      * @param dsd the descriptor of data source you want to play after current one
      */
     // This is an asynchronous call.
-    @Override
     public abstract void setNextDataSource(@NonNull DataSourceDesc dsd);
 
     /**
@@ -678,7 +708,6 @@
      * @param dsds the list of data sources you want to play after current one
      */
     // This is an asynchronous call.
-    @Override
     public abstract void setNextDataSources(@NonNull List<DataSourceDesc> dsds);
 
     /**
@@ -686,7 +715,6 @@
      *
      * @return the current DataSourceDesc
      */
-    @Override
     public abstract @NonNull DataSourceDesc getCurrentDataSource();
 
     /**
@@ -694,44 +722,9 @@
      * @param loop true if the current data source is meant to loop.
      */
     // This is an asynchronous call.
-    @Override
     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 the desired playback speed
-     */
-    // This is an asynchronous call.
-    @Override
-    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
-     */
-    @Override
-    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.
-     */
-    @Override
-    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
@@ -741,7 +734,6 @@
      * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
      */
     // This is an asynchronous call.
-    @Override
     public abstract void setPlayerVolume(float volume);
 
     /**
@@ -749,36 +741,16 @@
      * Note that it does not take into account the associated stream volume.
      * @return the player volume.
      */
-    @Override
     public abstract float getPlayerVolume();
 
     /**
      * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
      */
-    @Override
     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.
-     */
-    // This is a synchronous call.
-    @Override
-    public abstract void registerPlayerEventCallback(@NonNull Executor e,
-            @NonNull PlayerEventCallback cb);
-
-    /**
-     * Removes a previously registered callback for player events
-     * @param cb the callback to remove
-     */
-    // This is a synchronous call.
-    @Override
-    public abstract void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb);
-
-    /**
      * Create a request parcel which can be routed to the native media
      * player using {@link #invoke(Parcel, Parcel)}. The Parcel
      * returned has the proper InterfaceToken set. The caller should
@@ -812,10 +784,10 @@
     /**
      * Insert a task in the command queue to help the client to identify whether a batch
      * of commands has been finished. When this command is processed, a notification
-     * {@code MediaPlayer2EventCallback.onCommandLabelReached} will be fired with the
+     * {@code EventCallback.onCommandLabelReached} will be fired with the
      * given {@code label}.
      *
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCommandLabelReached
+     * @see android.media.MediaPlayer2.EventCallback#onCommandLabelReached
      *
      * @param label An application specific Object used to help to identify the completeness
      * of a batch of commands.
@@ -911,15 +883,6 @@
     // This is a synchronous call.
     public abstract void clearPendingCommands();
 
-    /**
-     * Stops playback after playback has been started or paused.
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized.
-     * @hide
-     */
-    public void stop() { }
-
     //--------------------------------------------------------------------------
     // Explicit Routing
     //--------------------
@@ -973,7 +936,8 @@
      */
     // This is a synchronous call.
     @Override
-    public abstract void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener);
+    public abstract void removeOnRoutingChangedListener(
+            AudioRouting.OnRoutingChangedListener listener);
 
     /**
      * Set the low-level power management behavior for this MediaPlayer2.
@@ -1010,9 +974,9 @@
      *
      * @return the width of the video, or 0 if there is no video,
      * no display surface was set, or the width has not been determined
-     * yet. The {@code MediaPlayer2EventCallback} can be registered via
-     * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
-     * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the width
+     * yet. The {@code EventCallback} can be registered via
+     * {@link #setEventCallback(Executor, EventCallback)} to provide a
+     * notification {@code EventCallback.onVideoSizeChanged} when the width
      * is available.
      */
     public abstract int getVideoWidth();
@@ -1022,9 +986,10 @@
      *
      * @return the height of the video, or 0 if there is no video,
      * no display surface was set, or the height has not been determined
-     * yet. The {@code MediaPlayer2EventCallback} can be registered via
-     * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
-     * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the height is available.
+     * yet. The {@code EventCallback} can be registered via
+     * {@link #setEventCallback(Executor, EventCallback)} to provide a
+     * notification {@code EventCallback.onVideoSizeChanged} when the height is
+     * available.
      */
     public abstract int getVideoHeight();
 
@@ -1051,60 +1016,6 @@
     public abstract boolean isPlaying();
 
     /**
-     * MediaPlayer2 has not been prepared or just has been reset.
-     * In this state, MediaPlayer2 doesn't fetch data.
-     * @hide
-     */
-    public static final int MEDIAPLAYER2_STATE_IDLE = 1;
-
-    /**
-     * MediaPlayer2 has been just prepared.
-     * In this state, MediaPlayer2 just fetches data from media source,
-     * but doesn't actively render data.
-     * @hide
-     */
-    public static final int MEDIAPLAYER2_STATE_PREPARED = 2;
-
-    /**
-     * MediaPlayer2 is paused.
-     * In this state, MediaPlayer2 doesn't actively render data.
-     * @hide
-     */
-    public static final int MEDIAPLAYER2_STATE_PAUSED = 3;
-
-    /**
-     * MediaPlayer2 is actively playing back data.
-     * @hide
-     */
-    public static final int MEDIAPLAYER2_STATE_PLAYING = 4;
-
-    /**
-     * MediaPlayer2 has hit some fatal error and cannot continue playback.
-     * @hide
-     */
-    public static final int MEDIAPLAYER2_STATE_ERROR = 5;
-
-    /**
-     * @hide
-     */
-    @IntDef(flag = false, prefix = "MEDIAPLAYER2_STATE", value = {
-        MEDIAPLAYER2_STATE_IDLE,
-        MEDIAPLAYER2_STATE_PREPARED,
-        MEDIAPLAYER2_STATE_PAUSED,
-        MEDIAPLAYER2_STATE_PLAYING,
-        MEDIAPLAYER2_STATE_ERROR })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface MediaPlayer2State {}
-
-    /**
-     * Gets the current MediaPlayer2 state.
-     *
-     * @return the current MediaPlayer2 state.
-     * @hide
-     */
-    public abstract @MediaPlayer2State int getMediaPlayer2State();
-
-    /**
      * 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.
@@ -1383,7 +1294,6 @@
      * data source and calling prepare().
      */
     // This is a synchronous call.
-    @Override
     public abstract void reset();
 
     /**
@@ -1706,7 +1616,7 @@
      * Interface definition for callbacks to be invoked when the player has the corresponding
      * events.
      */
-    public abstract static class MediaPlayer2EventCallback {
+    public abstract static class EventCallback {
         /**
          * Called to indicate the video size
          *
@@ -1718,7 +1628,8 @@
          * @param width the width of the video
          * @param height the height of the video
          */
-        public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int width, int height) { }
+        public void onVideoSizeChanged(
+                MediaPlayer2 mp, DataSourceDesc dsd, int width, int height) { }
 
         /**
          * Called to indicate an avaliable timed text
@@ -1794,7 +1705,7 @@
          * @param dsd the DataSourceDesc of this data source
          * @param timestamp the new media clock.
          */
-        public void onMediaTimeChanged(
+        public void onMediaTimeDiscontinuity(
                 MediaPlayer2 mp, DataSourceDesc dsd, MediaTimestamp timestamp) { }
 
         /**
@@ -1805,58 +1716,48 @@
          *        {@link #notifyWhenCommandLabelReached(Object)}.
          */
         public void onCommandLabelReached(MediaPlayer2 mp, @NonNull Object label) { }
+
+        /**
+         * Called when when a player subtitle track has new subtitle data available.
+         * @param mp the player that reports the new subtitle data
+         * @param dsd the DataSourceDesc of this data source
+         * @param data the subtitle data
+         */
+        public void onSubtitleData(
+                MediaPlayer2 mp, DataSourceDesc dsd, @NonNull SubtitleData data) { }
     }
 
     /**
      * Sets the callback to be invoked when the media source is ready for playback.
      *
-     * @param eventCallback the callback that will be run
      * @param executor the executor through which the callback should be invoked
+     * @param eventCallback the callback that will be run
      */
     // This is a synchronous call.
-    public abstract void setMediaPlayer2EventCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull MediaPlayer2EventCallback eventCallback);
+    public abstract void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull EventCallback eventCallback);
 
     /**
-     * Clears the {@link MediaPlayer2EventCallback}.
+     * Unregisters the {@link EventCallback}.
+     *
+     * @param eventCallback the callback to be unregistered
      */
     // This is a synchronous call.
-    public abstract void clearMediaPlayer2EventCallback();
-
-    /**
-     * Interface definition of a callback to be invoked when a
-     * track has data available.
-     *
-     * @hide
-     */
-    public interface OnSubtitleDataListener
-    {
-        public void onSubtitleData(MediaPlayer2 mp, SubtitleData data);
-    }
-
-    /**
-     * Register a callback to be invoked when a track has data available.
-     *
-     * @param listener the callback that will be run
-     *
-     * @hide
-     */
-    // This is a synchronous call.
-    public void setOnSubtitleDataListener(OnSubtitleDataListener listener) { }
+    public abstract void unregisterEventCallback(EventCallback eventCallback);
 
 
     /* Do not change these values without updating their counterparts
      * in include/media/mediaplayer2.h!
      */
     /** Unspecified media player error.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
+     * @see android.media.MediaPlayer2.EventCallback#onError
      */
     public static final int MEDIA_ERROR_UNKNOWN = 1;
 
     /** The video is streamed and its container is not valid for progressive
      * playback i.e the video's index (e.g moov atom) is not at the start of the
      * file.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
+     * @see android.media.MediaPlayer2.EventCallback#onError
      */
     public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
 
@@ -1872,7 +1773,7 @@
 
     /** Unspecified low-level system error. This value originated from UNKNOWN_ERROR in
      * system/core/include/utils/Errors.h
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
+     * @see android.media.MediaPlayer2.EventCallback#onError
      * @hide
      */
     public static final int MEDIA_ERROR_SYSTEM = -2147483648;
@@ -1893,65 +1794,65 @@
     public @interface MediaError {}
 
     /* Do not change these values without updating their counterparts
-     * in include/media/mediaplayer2.h!
+     * in include/media/MediaPlayer2Types.h!
      */
     /** Unspecified media player info.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_UNKNOWN = 1;
 
-    /** The player switched to this datas source because it is the
-     * next-to-be-played in the playlist.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+    /** The player just started the playback of this datas source.
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
-    public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
+    public static final int MEDIA_INFO_DATA_SOURCE_START = 2;
 
     /** The player just pushed the very first video frame for rendering.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3;
 
     /** The player just rendered the very first audio sample.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_AUDIO_RENDERING_START = 4;
 
     /** The player just completed the playback of this data source.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
-    public static final int MEDIA_INFO_PLAYBACK_COMPLETE = 5;
+    public static final int MEDIA_INFO_DATA_SOURCE_END = 5;
 
-    /** The player just completed the playback of the full playlist.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+    /** The player just completed the playback of all data sources set by {@link #setDataSource},
+     * {@link #setNextDataSource} and {@link #setNextDataSources}.
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
-    public static final int MEDIA_INFO_PLAYLIST_END = 6;
+    public static final int MEDIA_INFO_DATA_SOURCE_LIST_END = 6;
 
     /** The player just prepared a data source.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_PREPARED = 100;
 
     /** The video is too complex for the decoder: it can't decode frames fast
      *  enough. Possibly only the audio plays fine at this stage.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;
 
     /** MediaPlayer2 is temporarily pausing playback internally in order to
      * buffer more data.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_BUFFERING_START = 701;
 
     /** MediaPlayer2 is resuming playback after filling buffers.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_BUFFERING_END = 702;
 
     /** Estimated network bandwidth information (kbps) is available; currently this event fires
      * simultaneously as {@link #MEDIA_INFO_BUFFERING_START} and {@link #MEDIA_INFO_BUFFERING_END}
      * when playing network files.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      * @hide
      */
     public static final int MEDIA_INFO_NETWORK_BANDWIDTH = 703;
@@ -1963,26 +1864,26 @@
      * has already been played indicates that the next 30 percent of the
      * content to play has been buffered.
      *
-     * The {@code extra} parameter in {@code MediaPlayer2EventCallback.onInfo} is the
+     * The {@code extra} parameter in {@code EventCallback.onInfo} is the
      * percentage (0-100) of the content that has been buffered or played thus far.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_BUFFERING_UPDATE = 704;
 
     /** Bad interleaving means that a media has been improperly interleaved or
      * not interleaved at all, e.g has all the video samples first then all the
      * audio ones. Video is playing but a lot of disk seeks may be happening.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_BAD_INTERLEAVING = 800;
 
     /** The media cannot be seeked (e.g live stream)
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_NOT_SEEKABLE = 801;
 
     /** A new set of metadata is available.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_METADATA_UPDATE = 802;
 
@@ -1994,30 +1895,30 @@
 
     /** Informs that audio is not playing. Note that playback of the video
      * is not interrupted.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804;
 
     /** Informs that video is not playing. Note that playback of the audio
      * is not interrupted.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805;
 
     /** Failed to handle timed text track properly.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      *
      * {@hide}
      */
     public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
 
     /** Subtitle track was not supported by the media framework.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;
 
     /** Reading the subtitle track takes too long.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     * @see android.media.MediaPlayer2.EventCallback#onInfo
      */
     public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;
 
@@ -2026,11 +1927,11 @@
      */
     @IntDef(flag = false, prefix = "MEDIA_INFO", value = {
             MEDIA_INFO_UNKNOWN,
-            MEDIA_INFO_STARTED_AS_NEXT,
+            MEDIA_INFO_DATA_SOURCE_START,
             MEDIA_INFO_VIDEO_RENDERING_START,
             MEDIA_INFO_AUDIO_RENDERING_START,
-            MEDIA_INFO_PLAYBACK_COMPLETE,
-            MEDIA_INFO_PLAYLIST_END,
+            MEDIA_INFO_DATA_SOURCE_END,
+            MEDIA_INFO_DATA_SOURCE_LIST_END,
             MEDIA_INFO_PREPARED,
             MEDIA_INFO_VIDEO_TRACK_LAGGING,
             MEDIA_INFO_BUFFERING_START,
@@ -2052,129 +1953,124 @@
 
     //--------------------------------------------------------------------------
     /** The player just completed a call {@link #attachAuxEffect}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1;
 
     /** The player just completed a call {@link #deselectTrack}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_DESELECT_TRACK = 2;
 
     /** The player just completed a call {@link #loopCurrent}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_LOOP_CURRENT = 3;
 
     /** The player just completed a call {@link #pause}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_PAUSE = 4;
 
     /** The player just completed a call {@link #play}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_PLAY = 5;
 
     /** The player just completed a call {@link #prepare}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_PREPARE = 6;
 
     /** The player just completed a call {@link #releaseDrm}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_RELEASE_DRM = 12;
 
     /** The player just completed a call {@link #restoreDrmKeys}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_RESTORE_DRM_KEYS = 13;
 
     /** The player just completed a call {@link #seekTo}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_SEEK_TO = 14;
 
     /** The player just completed a call {@link #selectTrack}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_SELECT_TRACK = 15;
 
     /** The player just completed a call {@link #setAudioAttributes}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_SET_AUDIO_ATTRIBUTES = 16;
 
     /** The player just completed a call {@link #setAudioSessionId}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_SET_AUDIO_SESSION_ID = 17;
 
     /** The player just completed a call {@link #setAuxEffectSendLevel}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL = 18;
 
     /** The player just completed a call {@link #setDataSource}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_SET_DATA_SOURCE = 19;
 
     /** The player just completed a call {@link #setNextDataSource}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCE = 22;
 
     /** The player just completed a call {@link #setNextDataSources}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCES = 23;
 
     /** The player just completed a call {@link #setPlaybackParams}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_SET_PLAYBACK_PARAMS = 24;
 
-    /** The player just completed a call {@link #setPlaybackSpeed}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
-     */
-    public static final int CALL_COMPLETED_SET_PLAYBACK_SPEED = 25;
-
     /** The player just completed a call {@link #setPlayerVolume}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_SET_PLAYER_VOLUME = 26;
 
     /** The player just completed a call {@link #setSurface}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_SET_SURFACE = 27;
 
     /** The player just completed a call {@link #setSyncParams}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_SET_SYNC_PARAMS = 28;
 
     /** The player just completed a call {@link #skipToNext}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_COMPLETED_SKIP_TO_NEXT = 29;
 
     /** The player just completed a call {@link #setBufferingParams}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      * @hide
      */
     public static final int CALL_COMPLETED_SET_BUFFERING_PARAMS = 1001;
 
     /** The player just completed a call {@code setVideoScalingMode}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      * @hide
      */
     public static final int CALL_COMPLETED_SET_VIDEO_SCALING_MODE = 1002;
 
     /** The player just completed a call {@code notifyWhenCommandLabelReached}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCommandLabelReached
+     * @see android.media.MediaPlayer2.EventCallback#onCommandLabelReached
      * @hide
      */
     public static final int CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED = 1003;
@@ -2200,7 +2096,6 @@
             CALL_COMPLETED_SET_NEXT_DATA_SOURCE,
             CALL_COMPLETED_SET_NEXT_DATA_SOURCES,
             CALL_COMPLETED_SET_PLAYBACK_PARAMS,
-            CALL_COMPLETED_SET_PLAYBACK_SPEED,
             CALL_COMPLETED_SET_PLAYER_VOLUME,
             CALL_COMPLETED_SET_SURFACE,
             CALL_COMPLETED_SET_SYNC_PARAMS,
@@ -2213,40 +2108,46 @@
     public @interface CallCompleted {}
 
     /** Status code represents that call is completed without an error.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_STATUS_NO_ERROR = 0;
 
     /** Status code represents that call is ended with an unknown error.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_STATUS_ERROR_UNKNOWN = Integer.MIN_VALUE;
 
     /** Status code represents that the player is not in valid state for the operation.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_STATUS_INVALID_OPERATION = 1;
 
     /** Status code represents that the argument is illegal.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_STATUS_BAD_VALUE = 2;
 
     /** Status code represents that the operation is not allowed.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_STATUS_PERMISSION_DENIED = 3;
 
     /** Status code represents a file or network related operation error.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
     public static final int CALL_STATUS_ERROR_IO = 4;
 
+    /** Status code represents that the call has been skipped. For example, a {@link #seekTo}
+     * request may be skipped if it is followed by another {@link #seekTo} request.
+     * @see EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_SKIPPED = 5;
+
     /** Status code represents that DRM operation is called before preparing a DRM scheme through
      *  {@link #prepareDrm}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
-    public static final int CALL_STATUS_NO_DRM_SCHEME = 5;
+    public static final int CALL_STATUS_NO_DRM_SCHEME = 6;
 
     /**
      * @hide
@@ -2258,6 +2159,7 @@
             CALL_STATUS_BAD_VALUE,
             CALL_STATUS_PERMISSION_DENIED,
             CALL_STATUS_ERROR_IO,
+            CALL_STATUS_SKIPPED,
             CALL_STATUS_NO_DRM_SCHEME})
     @Retention(RetentionPolicy.SOURCE)
     public @interface CallStatus {}
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 56423fd..95ba00f 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -97,7 +97,6 @@
     private long mNativeSurfaceTexture;  // accessed by native methods
     private int mListenerContext; // accessed by native methods
     private SurfaceHolder mSurfaceHolder;
-    private EventHandler mEventHandler;
     private PowerManager.WakeLock mWakeLock = null;
     private boolean mScreenOnWhilePlaying;
     private boolean mStayAwake;
@@ -135,7 +134,7 @@
     //--- guarded by |mDrmLock| end
 
     private HandlerThread mHandlerThread;
-    private final Handler mTaskHandler;
+    private final TaskHandler mTaskHandler;
     private final Object mTaskLock = new Object();
     @GuardedBy("mTaskLock")
     private final List<Task> mPendingTasks = new LinkedList<>();
@@ -149,19 +148,10 @@
      * result in an exception.</p>
      */
     public MediaPlayer2Impl() {
-        Looper looper;
-        if ((looper = Looper.myLooper()) != null) {
-            mEventHandler = new EventHandler(this, looper);
-        } else if ((looper = Looper.getMainLooper()) != null) {
-            mEventHandler = new EventHandler(this, looper);
-        } else {
-            mEventHandler = null;
-        }
-
         mHandlerThread = new HandlerThread("MediaPlayer2TaskThread");
         mHandlerThread.start();
-        looper = mHandlerThread.getLooper();
-        mTaskHandler = new Handler(looper);
+        Looper looper = mHandlerThread.getLooper();
+        mTaskHandler = new TaskHandler(this, looper);
 
         mTimeProvider = new TimeProvider(this);
         mOpenSubtitleSources = new Vector<InputStream>();
@@ -173,6 +163,11 @@
         native_setup(new WeakReference<MediaPlayer2Impl>(this));
     }
 
+    @Override
+    public MediaPlayerBase getMediaPlayerBase() {
+        return null;
+    }
+
     /**
      * Releases the resources held by this {@code MediaPlayer2} object.
      *
@@ -208,8 +203,6 @@
      * playback will continue from where it was paused. If playback had
      * been stopped, or never started before, playback will start at the
      * beginning.
-     *
-     * @throws IllegalStateException if it is called in an invalid state
      */
     @Override
     public void play() {
@@ -217,6 +210,22 @@
             @Override
             void process() {
                 stayAwake(true);
+
+                // TODO: remove this block when native code sends MEDIA_INFO_DATA_SOURCE_START
+                // when pipeline is created.
+                if (getState() == PLAYER_STATE_PREPARED) {
+                    final DataSourceDesc dsd;
+                    synchronized (mSrcLock) {
+                        dsd = mCurrentDSD;
+                    }
+                    synchronized (mEventCbLock) {
+                        for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                            cb.first.execute(() -> cb.second.onInfo(
+                                    MediaPlayer2Impl.this, dsd, MEDIA_INFO_DATA_SOURCE_START, 0));
+                        }
+                    }
+                }
+
                 _start();
             }
         });
@@ -231,8 +240,6 @@
      * call prepare(). For streams, you should call prepare(),
      * which returns immediately, rather than blocking until enough data has been
      * buffered.
-     *
-     * @throws IllegalStateException if it is called in an invalid state
      */
     @Override
     public void prepare() {
@@ -248,9 +255,6 @@
 
     /**
      * Pauses playback. Call play() to resume.
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized.
      */
     @Override
     public void pause() {
@@ -258,6 +262,22 @@
             @Override
             void process() {
                 stayAwake(false);
+
+                // TODO: remove this block when native code allows prepared -> pause
+                // and sends MEDIA_INFO_DATA_SOURCE_START when pipeline is created.
+                if (getState() == PLAYER_STATE_PREPARED) {
+                    final DataSourceDesc dsd;
+                    synchronized (mSrcLock) {
+                        dsd = mCurrentDSD;
+                    }
+                    synchronized (mEventCbLock) {
+                        for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                            cb.first.execute(() -> cb.second.onInfo(
+                                    MediaPlayer2Impl.this, dsd, MEDIA_INFO_DATA_SOURCE_START, 0));
+                        }
+                    }
+                }
+
                 _pause();
             }
         });
@@ -313,39 +333,11 @@
     }
 
     @Override
-    public @PlayerState int getPlayerState() {
-        int mediaplayer2State = getMediaPlayer2State();
-        int playerState;
-        switch (mediaplayer2State) {
-            case MEDIAPLAYER2_STATE_IDLE:
-                playerState = PLAYER_STATE_IDLE;
-                break;
-            case MEDIAPLAYER2_STATE_PREPARED:
-            case MEDIAPLAYER2_STATE_PAUSED:
-                playerState = PLAYER_STATE_PAUSED;
-                break;
-            case MEDIAPLAYER2_STATE_PLAYING:
-                playerState = PLAYER_STATE_PLAYING;
-                break;
-            case MEDIAPLAYER2_STATE_ERROR:
-            default:
-                playerState = PLAYER_STATE_ERROR;
-                break;
-        }
-
-        return playerState;
+    public @MediaPlayer2State int getState() {
+        return native_getState();
     }
 
-    /**
-     * Gets the current buffering state of the player.
-     * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
-     * buffered.
-     */
-    @Override
-    public @BuffState int getBufferingState() {
-        // TODO: use cached state or call native function.
-        return BUFFERING_STATE_UNKNOWN;
-    }
+    private native int native_getState();
 
     /**
      * Sets the audio attributes for this MediaPlayer2.
@@ -384,23 +376,22 @@
      * Sets the data source as described by a DataSourceDesc.
      *
      * @param dsd the descriptor of data source you want to play
-     * @throws IllegalStateException if it is called in an invalid state
-     * @throws NullPointerException if dsd is null
      */
     @Override
     public void setDataSource(@NonNull DataSourceDesc dsd) {
         addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
             @Override
-            void process() {
-                Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
-                // TODO: setDataSource could update exist data source
+            void process() throws IOException {
+                Preconditions.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+                int state = getState();
+                if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
+                    throw new IllegalStateException("called in wrong state " + state);
+                }
+
                 synchronized (mSrcLock) {
                     mCurrentDSD = dsd;
                     mCurrentSrcId = mSrcIdGenerator++;
-                    try {
-                        handleDataSource(true /* isCurrent */, dsd, mCurrentSrcId);
-                    } catch (IOException e) {
-                    }
+                    handleDataSource(true /* isCurrent */, dsd, mCurrentSrcId);
                 }
             }
         });
@@ -411,28 +402,20 @@
      * after current data source is finished.
      *
      * @param dsd the descriptor of data source you want to play after current one
-     * @throws IllegalStateException if it is called in an invalid state
-     * @throws NullPointerException if dsd is null
      */
     @Override
     public void setNextDataSource(@NonNull DataSourceDesc dsd) {
         addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
             @Override
             void process() {
-                Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
+                Preconditions.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
                 synchronized (mSrcLock) {
                     mNextDSDs = new ArrayList<DataSourceDesc>(1);
                     mNextDSDs.add(dsd);
                     mNextSrcId = mSrcIdGenerator++;
                     mNextSourceState = NEXT_SOURCE_STATE_INIT;
-                    mNextSourcePlayPending = false;
                 }
-                int state = getMediaPlayer2State();
-                if (state != MEDIAPLAYER2_STATE_IDLE) {
-                    synchronized (mSrcLock) {
-                        prepareNextDataSource_l();
-                    }
-                }
+                prepareNextDataSource();
             }
         });
     }
@@ -441,8 +424,6 @@
      * Sets a list of data sources to be played sequentially after current data source is done.
      *
      * @param dsds the list of data sources you want to play after current one
-     * @throws IllegalStateException if it is called in an invalid state
-     * @throws IllegalArgumentException if dsds is null or empty, or contains null DataSourceDesc
      */
     @Override
     public void setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
@@ -463,14 +444,8 @@
                     mNextDSDs = new ArrayList(dsds);
                     mNextSrcId = mSrcIdGenerator++;
                     mNextSourceState = NEXT_SOURCE_STATE_INIT;
-                    mNextSourcePlayPending = false;
                 }
-                int state = getMediaPlayer2State();
-                if (state != MEDIAPLAYER2_STATE_IDLE) {
-                    synchronized (mSrcLock) {
-                        prepareNextDataSource_l();
-                    }
-                }
+                prepareNextDataSource();
             }
         });
     }
@@ -500,46 +475,6 @@
     private native void setLooping(boolean looping);
 
     /**
-     * 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 the desired playback speed
-     */
-    @Override
-    public void setPlaybackSpeed(float speed) {
-        addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_SPEED, false) {
-            @Override
-            void process() {
-                _setPlaybackParams(getPlaybackParams().setSpeed(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
-     */
-    @Override
-    public float getPlaybackSpeed() {
-        return getPlaybackParams().getSpeed();
-    }
-
-    /**
-     * 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.
-     */
-    @Override
-    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
@@ -579,25 +514,6 @@
         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.
-     */
-    @Override
-    public void registerPlayerEventCallback(@NonNull Executor e,
-            @NonNull PlayerEventCallback cb) {
-    }
-
-    /**
-     * Removes a previously registered callback for player events
-     * @param cb the callback to remove
-     */
-    @Override
-    public void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb) {
-    }
-
-
     private static final int NEXT_SOURCE_STATE_ERROR = -1;
     private static final int NEXT_SOURCE_STATE_INIT = 0;
     private static final int NEXT_SOURCE_STATE_PREPARING = 1;
@@ -666,7 +582,7 @@
             @Override
             void process() {
                 synchronized (mEventCbLock) {
-                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
                         cb.first.execute(() -> cb.second.onCommandLabelReached(
                                 MediaPlayer2Impl.this, label));
                     }
@@ -1004,67 +920,100 @@
     private native void nativeHandleDataSourceCallback(
             boolean isCurrent, long srcId, Media2DataSource dataSource);
 
-    // This function shall be called with |mSrcLock| acquired.
-    private void prepareNextDataSource_l() {
-        if (mNextDSDs == null || mNextDSDs.isEmpty()
-                || mNextSourceState != NEXT_SOURCE_STATE_INIT) {
-            // There is no next source or it's in preparing or prepared state.
-            return;
+    /**
+     * @return true if there is a next data source, false otherwise.
+     */
+    // This function should be always called on |mHandlerThread|.
+    private boolean prepareNextDataSource() {
+        if (Looper.myLooper() != mHandlerThread.getLooper()) {
+            Log.e(TAG, "prepareNextDataSource: called on wrong looper");
         }
 
-        try {
-            mNextSourceState = NEXT_SOURCE_STATE_PREPARING;
-            handleDataSource(false /* isCurrent */, mNextDSDs.get(0), mNextSrcId);
-        } catch (Exception e) {
-            Message msg2 = mEventHandler.obtainMessage(
-                    MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
-            final long nextSrcId = mNextSrcId;
-            mEventHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mEventHandler.handleMessage(msg2, nextSrcId);
-                }
-            });
+        boolean hasNextDSD;
+        synchronized (mSrcLock) {
+            hasNextDSD = (mNextDSDs != null && !mNextDSDs.isEmpty());
         }
+
+        int state = getState();
+        if (state == PLAYER_STATE_ERROR || state == PLAYER_STATE_IDLE) {
+            // Current source has not been prepared yet.
+            return hasNextDSD;
+        }
+
+        synchronized (mSrcLock) {
+            if (!hasNextDSD || mNextSourceState != NEXT_SOURCE_STATE_INIT) {
+                // There is no next source or it's in preparing or prepared state.
+                return hasNextDSD;
+            }
+
+            try {
+                mNextSourceState = NEXT_SOURCE_STATE_PREPARING;
+                handleDataSource(false /* isCurrent */, mNextDSDs.get(0), mNextSrcId);
+            } catch (Exception e) {
+                Message msg = mTaskHandler.obtainMessage(
+                        MEDIA_ERROR, MEDIA_ERROR_IO, MEDIA_ERROR_UNKNOWN, null);
+                mTaskHandler.handleMessage(msg, mNextSrcId);
+
+                mNextDSDs.remove(0);
+                // make a new SrcId to obsolete notification for previous one.
+                mNextSrcId = mSrcIdGenerator++;
+                mNextSourceState = NEXT_SOURCE_STATE_INIT;
+                return prepareNextDataSource();
+            }
+        }
+        return hasNextDSD;
     }
 
-    // This function shall be called with |mSrcLock| acquired.
-    private void playNextDataSource_l() {
-        if (mNextDSDs == null || mNextDSDs.isEmpty()) {
-            return;
+    // This function should be always called on |mHandlerThread|.
+    private void playNextDataSource() {
+        if (Looper.myLooper() != mHandlerThread.getLooper()) {
+            Log.e(TAG, "playNextDataSource: called on wrong looper");
         }
 
-        if (mNextSourceState == NEXT_SOURCE_STATE_PREPARED) {
-            // Switch to next source only when it's in prepared state.
-            mCurrentDSD = mNextDSDs.get(0);
-            mCurrentSrcId = mNextSrcId;
-            mBufferedPercentageCurrent.set(mBufferedPercentageNext.get());
-            mNextDSDs.remove(0);
-            mNextSrcId = mSrcIdGenerator++;  // make it different from mCurrentSrcId
-            mBufferedPercentageNext.set(0);
-            mNextSourceState = NEXT_SOURCE_STATE_INIT;
-            mNextSourcePlayPending = false;
+        boolean hasNextDSD = false;
+        synchronized (mSrcLock) {
+            if (mNextDSDs != null && !mNextDSDs.isEmpty()) {
+                hasNextDSD = true;
+                if (mNextSourceState == NEXT_SOURCE_STATE_PREPARED) {
+                    // Switch to next source only when it has been prepared.
+                    mCurrentDSD = mNextDSDs.get(0);
+                    mCurrentSrcId = mNextSrcId;
+                    mBufferedPercentageCurrent.set(mBufferedPercentageNext.get());
+                    mNextDSDs.remove(0);
+                    mNextSrcId = mSrcIdGenerator++;  // make it different from |mCurrentSrcId|
+                    mBufferedPercentageNext.set(0);
+                    mNextSourceState = NEXT_SOURCE_STATE_INIT;
 
-            long srcId = mCurrentSrcId;
-            try {
-                nativePlayNextDataSource(srcId);
-            } catch (Exception e) {
-                Message msg2 = mEventHandler.obtainMessage(
-                        MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
-                mEventHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mEventHandler.handleMessage(msg2, srcId);
+                    long srcId = mCurrentSrcId;
+                    try {
+                        nativePlayNextDataSource(srcId);
+                    } catch (Exception e) {
+                        Message msg2 = mTaskHandler.obtainMessage(
+                                MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
+                        mTaskHandler.handleMessage(msg2, srcId);
+                        // Keep |mNextSourcePlayPending|
+                        hasNextDSD = prepareNextDataSource();
                     }
-                });
-            }
+                    if (hasNextDSD) {
+                        stayAwake(true);
 
-            // Wait for MEDIA2_INFO_STARTED_AS_NEXT to prepare next source.
-        } else {
-            if (mNextSourceState == NEXT_SOURCE_STATE_INIT) {
-                prepareNextDataSource_l();
+                        // Now a new current src is playing.
+                        // Wait for MEDIA_INFO_DATA_SOURCE_START to prepare next source.
+                        mNextSourcePlayPending = false;
+                    }
+                } else if (mNextSourceState == NEXT_SOURCE_STATE_INIT) {
+                    hasNextDSD = prepareNextDataSource();
+                }
             }
-            mNextSourcePlayPending = true;
+        }
+
+        if (!hasNextDSD) {
+            synchronized (mEventCbLock) {
+                for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                    cb.first.execute(() -> cb.second.onInfo(
+                            MediaPlayer2Impl.this, null, MEDIA_INFO_DATA_SOURCE_LIST_END, 0));
+                }
+            }
         }
     }
 
@@ -1080,20 +1029,6 @@
 
     private native int _getAudioStreamType() throws IllegalStateException;
 
-    /**
-     * Stops playback after playback has been started or paused.
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized.
-     * #hide
-     */
-    @Override
-    public void stop() {
-        stayAwake(false);
-        _stop();
-    }
-
-    private native void _stop() throws IllegalStateException;
 
     //--------------------------------------------------------------------------
     // Explicit Routing
@@ -1191,7 +1126,7 @@
                 enableNativeRoutingCallbacksLocked(true);
                 mRoutingChangeListeners.put(
                         listener, new NativeRoutingEventHandlerDelegate(this, listener,
-                                handler != null ? handler : mEventHandler));
+                                handler != null ? handler : mTaskHandler));
             }
         }
     }
@@ -1305,9 +1240,9 @@
      *
      * @return the width of the video, or 0 if there is no video,
      * no display surface was set, or the width has not been determined
-     * yet. The {@code MediaPlayer2EventCallback} can be registered via
-     * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
-     * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the width
+     * yet. The {@code EventCallback} can be registered via
+     * {@link #setEventCallback(Executor, EventCallback)} to provide a
+     * notification {@code EventCallback.onVideoSizeChanged} when the width
      * is available.
      */
     @Override
@@ -1318,9 +1253,9 @@
      *
      * @return the height of the video, or 0 if there is no video,
      * no display surface was set, or the height has not been determined
-     * yet. The {@code MediaPlayer2EventCallback} can be registered via
-     * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
-     * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the height
+     * yet. The {@code EventCallback} can be registered via
+     * {@link #setEventCallback(Executor, EventCallback)} to provide a
+     * notification {@code EventCallback.onVideoSizeChanged} when the height
      * is available.
      */
     @Override
@@ -1355,13 +1290,6 @@
     @Override
     public native boolean isPlaying();
 
-    @Override
-    public @MediaPlayer2State int getMediaPlayer2State() {
-        return native_getMediaPlayer2State();
-    }
-
-    private native int native_getMediaPlayer2State();
-
     /**
      * Gets the current buffering management params used by the source component.
      * Calling it only after {@code setDataSource} has been called.
@@ -1395,7 +1323,7 @@
         addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
             @Override
             void process() {
-                Preconditions.checkNotNull(params, "the BufferingParams cannot be null");
+                Preconditions.checkArgument(params != null, "the BufferingParams cannot be null");
                 _setBufferingParams(params);
             }
         });
@@ -1459,7 +1387,7 @@
         addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
             @Override
             void process() {
-                Preconditions.checkNotNull(params, "the PlaybackParams cannot be null");
+                Preconditions.checkArgument(params != null, "the PlaybackParams cannot be null");
                 _setPlaybackParams(params);
             }
         });
@@ -1492,7 +1420,7 @@
         addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
             @Override
             void process() {
-                Preconditions.checkNotNull(params, "the SyncParams cannot be null");
+                Preconditions.checkArgument(params != null, "the SyncParams cannot be null");
                 _setSyncParams(params);
             }
         });
@@ -1722,8 +1650,8 @@
         stayAwake(false);
         _reset();
         // make sure none of the listeners get called anymore
-        if (mEventHandler != null) {
-            mEventHandler.removeCallbacksAndMessages(null);
+        if (mTaskHandler != null) {
+            mTaskHandler.removeCallbacksAndMessages(null);
         }
 
         synchronized (mIndexTrackPairs) {
@@ -2148,9 +2076,9 @@
     private int mSelectedSubtitleTrackIndex = -1;
     private Vector<InputStream> mOpenSubtitleSources;
 
-    private OnSubtitleDataListener mSubtitleDataListener = new OnSubtitleDataListener() {
+    private EventCallback mSubtitleDataCallback = new EventCallback() {
         @Override
-        public void onSubtitleData(MediaPlayer2 mp, SubtitleData data) {
+        public void onSubtitleData(MediaPlayer2 mp, DataSourceDesc dsd, SubtitleData data) {
             int index = data.getTrackIndex();
             synchronized (mIndexTrackPairs) {
                 for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) {
@@ -2174,7 +2102,7 @@
             }
             mSelectedSubtitleTrackIndex = -1;
         }
-        setOnSubtitleDataListener(null);
+        unregisterEventCallback(mSubtitleDataCallback);
         if (track == null) {
             return;
         }
@@ -2194,7 +2122,8 @@
                 selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true);
             } catch (IllegalStateException e) {
             }
-            setOnSubtitleDataListener(mSubtitleDataListener);
+            final Executor executor = (runnable) -> mTaskHandler.post(runnable);
+            registerEventCallback(executor, mSubtitleDataCallback);
         }
         // no need to select out-of-band tracks
     }
@@ -2256,9 +2185,9 @@
 
             public void run() {
                 int res = addTrack();
-                if (mEventHandler != null) {
-                    Message m = mEventHandler.obtainMessage(MEDIA_INFO, res, 0, null);
-                    mEventHandler.sendMessage(m);
+                if (mTaskHandler != null) {
+                    Message m = mTaskHandler.obtainMessage(MEDIA_INFO, res, 0, null);
+                    mTaskHandler.sendMessage(m);
                 }
                 thread.getLooper().quitSafely();
             }
@@ -2446,7 +2375,7 @@
         if (!mSubtitleController.hasRendererFor(fFormat)) {
             // test and add not atomic
             Context context = ActivityThread.currentApplication();
-            mSubtitleController.registerRenderer(new SRTRenderer(context, mEventHandler));
+            mSubtitleController.registerRenderer(new SRTRenderer(context, mTaskHandler));
         }
         final SubtitleTrack track = mSubtitleController.addTrack(fFormat);
         synchronized (mIndexTrackPairs) {
@@ -2499,9 +2428,9 @@
 
             public void run() {
                 int res = addTrack();
-                if (mEventHandler != null) {
-                    Message m = mEventHandler.obtainMessage(MEDIA_INFO, res, 0, null);
-                    mEventHandler.sendMessage(m);
+                if (mTaskHandler != null) {
+                    Message m = mTaskHandler.obtainMessage(MEDIA_INFO, res, 0, null);
+                    mTaskHandler.sendMessage(m);
                 }
                 thread.getLooper().quitSafely();
             }
@@ -2717,7 +2646,6 @@
             mTimeProvider.close();
             mTimeProvider = null;
         }
-        mOnSubtitleDataListener = null;
 
         // Modular DRM clean up
         mOnDrmConfigHelper = null;
@@ -2764,10 +2692,10 @@
         return mTimeProvider;
     }
 
-    private class EventHandler extends Handler {
+    private class TaskHandler extends Handler {
         private MediaPlayer2Impl mMediaPlayer;
 
-        public EventHandler(MediaPlayer2Impl mp, Looper looper) {
+        public TaskHandler(MediaPlayer2Impl mp, Looper looper) {
             super(looper);
             mMediaPlayer = mp;
         }
@@ -2785,6 +2713,21 @@
             final int what = msg.arg1;
             final int extra = msg.arg2;
 
+            final DataSourceDesc dsd;
+            boolean isCurrentSrcId = false;
+            boolean isNextSrcId = false;
+            synchronized (mSrcLock) {
+                if (srcId == mCurrentSrcId) {
+                    dsd = mCurrentDSD;
+                    isCurrentSrcId = true;
+                } else if (mNextDSDs != null && !mNextDSDs.isEmpty() && srcId == mNextSrcId) {
+                    dsd = mNextDSDs.get(0);
+                    isNextSrcId = true;
+                } else {
+                    return;
+                }
+            }
+
             switch(msg.what) {
             case MEDIA_PREPARED:
             {
@@ -2799,33 +2742,29 @@
                     sendMessage(msg2);
                 }
 
-                final DataSourceDesc dsd;
-                synchronized (mSrcLock) {
-                    Log.i(TAG, "MEDIA_PREPARED: srcId=" + srcId
-                            + ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
-                    if (srcId == mCurrentSrcId) {
-                        dsd = mCurrentDSD;
-                        prepareNextDataSource_l();
-                    } else if (mNextDSDs != null && !mNextDSDs.isEmpty()
-                            && srcId == mNextSrcId) {
-                        dsd = mNextDSDs.get(0);
-                        mNextSourceState = NEXT_SOURCE_STATE_PREPARED;
-                        if (mNextSourcePlayPending) {
-                            playNextDataSource_l();
-                        }
-                    } else {
-                        dsd = null;
-                    }
-                }
-
                 if (dsd != null) {
                     synchronized (mEventCbLock) {
-                        for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                        for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
                             cb.first.execute(() -> cb.second.onInfo(
                                     mMediaPlayer, dsd, MEDIA_INFO_PREPARED, 0));
                         }
                     }
                 }
+
+                synchronized (mSrcLock) {
+                    Log.i(TAG, "MEDIA_PREPARED: srcId=" + srcId
+                            + ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
+
+                    if (isCurrentSrcId) {
+                        prepareNextDataSource();
+                    } else if (isNextSrcId) {
+                        mNextSourceState = NEXT_SOURCE_STATE_PREPARED;
+                        if (mNextSourcePlayPending) {
+                            playNextDataSource();
+                        }
+                    }
+                }
+
                 synchronized (mTaskLock) {
                     if (mCurrentTask != null
                             && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE
@@ -2860,7 +2799,7 @@
                         synchronized (mEventCbLock) {
                             for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
                                 cb.first.execute(() -> cb.second.onDrmInfo(
-                                        mMediaPlayer, mCurrentDSD, drmInfo));
+                                        mMediaPlayer, dsd, drmInfo));
                             }
                         }
                     }
@@ -2872,22 +2811,25 @@
 
             case MEDIA_PLAYBACK_COMPLETE:
             {
-                final DataSourceDesc dsd = mCurrentDSD;
-                synchronized (mSrcLock) {
-                    if (srcId == mCurrentSrcId) {
+                if (isCurrentSrcId) {
+                    synchronized (mEventCbLock) {
+                        for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                            cb.first.execute(() -> cb.second.onInfo(
+                                    mMediaPlayer, dsd, MEDIA_INFO_DATA_SOURCE_END, 0));
+                        }
+                    }
+                    stayAwake(false);
+
+                    synchronized (mSrcLock) {
+                        mNextSourcePlayPending = true;
+
                         Log.i(TAG, "MEDIA_PLAYBACK_COMPLETE: srcId=" + srcId
                                 + ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
-                        playNextDataSource_l();
                     }
+
+                    playNextDataSource();
                 }
 
-                synchronized (mEventCbLock) {
-                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
-                        cb.first.execute(() -> cb.second.onInfo(
-                                mMediaPlayer, dsd, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
-                    }
-                }
-                stayAwake(false);
                 return;
             }
 
@@ -2914,21 +2856,18 @@
             {
                 final int percent = msg.arg1;
                 synchronized (mEventCbLock) {
-                    if (srcId == mCurrentSrcId) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onInfo(
+                                mMediaPlayer, dsd, MEDIA_INFO_BUFFERING_UPDATE,
+                                percent));
+                    }
+                }
+
+                synchronized (mSrcLock) {
+                    if (isCurrentSrcId) {
                         mBufferedPercentageCurrent.set(percent);
-                        for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
-                            cb.first.execute(() -> cb.second.onInfo(
-                                    mMediaPlayer, mCurrentDSD, MEDIA_INFO_BUFFERING_UPDATE,
-                                    percent));
-                        }
-                    } else if (srcId == mNextSrcId && !mNextDSDs.isEmpty()) {
+                    } else if (isNextSrcId) {
                         mBufferedPercentageNext.set(percent);
-                        DataSourceDesc nextDSD = mNextDSDs.get(0);
-                        for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
-                            cb.first.execute(() -> cb.second.onInfo(
-                                    mMediaPlayer, nextDSD, MEDIA_INFO_BUFFERING_UPDATE,
-                                    percent));
-                        }
                     }
                 }
                 return;
@@ -2962,9 +2901,9 @@
                 final int width = msg.arg1;
                 final int height = msg.arg2;
                 synchronized (mEventCbLock) {
-                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
                         cb.first.execute(() -> cb.second.onVideoSizeChanged(
-                                mMediaPlayer, mCurrentDSD, width, height));
+                                mMediaPlayer, dsd, width, height));
                     }
                 }
                 return;
@@ -2974,11 +2913,11 @@
             {
                 Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
                 synchronized (mEventCbLock) {
-                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
                         cb.first.execute(() -> cb.second.onError(
-                                mMediaPlayer, mCurrentDSD, what, extra));
+                                mMediaPlayer, dsd, what, extra));
                         cb.first.execute(() -> cb.second.onInfo(
-                                mMediaPlayer, mCurrentDSD, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+                                mMediaPlayer, dsd, MEDIA_INFO_DATA_SOURCE_END, 0));
                     }
                 }
                 stayAwake(false);
@@ -2987,10 +2926,17 @@
 
             case MEDIA_INFO:
             {
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onInfo(
+                                mMediaPlayer, dsd, what, extra));
+                    }
+                }
+
                 switch (msg.arg1) {
-                    case MEDIA_INFO_STARTED_AS_NEXT:
-                        if (srcId == mCurrentSrcId) {
-                            prepareNextDataSource_l();
+                    case MEDIA_INFO_DATA_SOURCE_START:
+                        if (isCurrentSrcId) {
+                            prepareNextDataSource();
                         }
                         break;
 
@@ -3025,13 +2971,6 @@
                         }
                         break;
                 }
-
-                synchronized (mEventCbLock) {
-                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
-                        cb.first.execute(() -> cb.second.onInfo(
-                                mMediaPlayer, mCurrentDSD, what, extra));
-                    }
-                }
                 // No real default action so far.
                 return;
             }
@@ -3057,8 +2996,9 @@
                 }
 
                 synchronized (mEventCbLock) {
-                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
-                        cb.first.execute(() -> cb.second.onTimedText(mMediaPlayer, mCurrentDSD, text));
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onTimedText(
+                                mMediaPlayer, dsd, text));
                     }
                 }
                 return;
@@ -3066,15 +3006,16 @@
 
             case MEDIA_SUBTITLE_DATA:
             {
-                OnSubtitleDataListener onSubtitleDataListener = mOnSubtitleDataListener;
-                if (onSubtitleDataListener == null) {
-                    return;
-                }
                 if (msg.obj instanceof Parcel) {
                     Parcel parcel = (Parcel) msg.obj;
                     SubtitleData data = new SubtitleData(parcel);
                     parcel.recycle();
-                    onSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
+                    synchronized (mEventCbLock) {
+                        for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                            cb.first.execute(() -> cb.second.onSubtitleData(
+                                    mMediaPlayer, dsd, data));
+                        }
+                    }
                 }
                 return;
             }
@@ -3091,9 +3032,9 @@
                 }
 
                 synchronized (mEventCbLock) {
-                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
                         cb.first.execute(() -> cb.second.onTimedMetaDataAvailable(
-                                mMediaPlayer, mCurrentDSD, data));
+                                mMediaPlayer, dsd, data));
                     }
                 }
                 return;
@@ -3127,7 +3068,7 @@
 
     /*
      * 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.
+     * just uses the TaskHandler system to post the event back to the main app thread.
      * We use a weak reference to the original MediaPlayer2 object so that the native
      * code is safe from the object disappearing from underneath it.  (This is
      * the cookie passed to native_setup().)
@@ -3141,22 +3082,9 @@
         }
 
         switch (what) {
-        case MEDIA_INFO:
-            if (arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
-                new Thread(new Runnable() {
-                    @Override
-                    public void run() {
-                        // this acquires the wakelock if needed, and sets the client side state
-                        mp.play();
-                    }
-                }).start();
-                Thread.yield();
-            }
-            break;
-
         case MEDIA_DRM_INFO:
             // We need to derive mDrmInfoImpl before prepare() returns so processing it here
-            // before the notification is sent to EventHandler below. EventHandler runs in the
+            // before the notification is sent to TaskHandler below. TaskHandler runs in the
             // notification looper so its handleMessage might process the event after prepare()
             // has returned.
             Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO");
@@ -3183,21 +3111,21 @@
 
         }
 
-        if (mp.mEventHandler != null) {
-            Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
+        if (mp.mTaskHandler != null) {
+            Message m = mp.mTaskHandler.obtainMessage(what, arg1, arg2, obj);
 
-            mp.mEventHandler.post(new Runnable() {
+            mp.mTaskHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mp.mEventHandler.handleMessage(m, srcId);
+                    mp.mTaskHandler.handleMessage(m, srcId);
                 }
             });
         }
     }
 
     private final Object mEventCbLock = new Object();
-    private ArrayList<Pair<Executor, MediaPlayer2EventCallback> > mEventCallbackRecords
-        = new ArrayList<Pair<Executor, MediaPlayer2EventCallback> >();
+    private ArrayList<Pair<Executor, EventCallback> > mEventCallbackRecords
+        = new ArrayList<Pair<Executor, EventCallback> >();
 
     /**
      * Register a callback to be invoked when the media source is ready
@@ -3207,14 +3135,14 @@
      * @param executor the executor through which the callback should be invoked
      */
     @Override
-    public void setMediaPlayer2EventCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull MediaPlayer2EventCallback eventCallback) {
+    public void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull EventCallback eventCallback) {
         if (eventCallback == null) {
-            throw new IllegalArgumentException("Illegal null MediaPlayer2EventCallback");
+            throw new IllegalArgumentException("Illegal null EventCallback");
         }
         if (executor == null) {
             throw new IllegalArgumentException(
-                    "Illegal null Executor for the MediaPlayer2EventCallback");
+                    "Illegal null Executor for the EventCallback");
         }
         synchronized (mEventCbLock) {
             mEventCallbackRecords.add(new Pair(executor, eventCallback));
@@ -3222,30 +3150,19 @@
     }
 
     /**
-     * Clears the {@link MediaPlayer2EventCallback}.
+     * Clears the {@link EventCallback}.
      */
     @Override
-    public void clearMediaPlayer2EventCallback() {
+    public void unregisterEventCallback(EventCallback eventCallback) {
         synchronized (mEventCbLock) {
-            mEventCallbackRecords.clear();
+            for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                if (cb.second == eventCallback) {
+                    mEventCallbackRecords.remove(cb);
+                }
+            }
         }
     }
 
-    /**
-     * Register a callback to be invoked when a track has data available.
-     *
-     * @param listener the callback that will be run
-     *
-     * @hide
-     */
-    @Override
-    public void setOnSubtitleDataListener(OnSubtitleDataListener listener) {
-        mOnSubtitleDataListener = listener;
-    }
-
-    private OnSubtitleDataListener mOnSubtitleDataListener;
-
-
     // Modular DRM begin
 
     /**
@@ -3281,11 +3198,11 @@
     public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull DrmEventCallback eventCallback) {
         if (eventCallback == null) {
-            throw new IllegalArgumentException("Illegal null MediaPlayer2EventCallback");
+            throw new IllegalArgumentException("Illegal null EventCallback");
         }
         if (executor == null) {
             throw new IllegalArgumentException(
-                    "Illegal null Executor for the MediaPlayer2EventCallback");
+                    "Illegal null Executor for the EventCallback");
         }
         synchronized (mDrmEventCbLock) {
             mDrmEventCallbackRecords.add(new Pair(executor, eventCallback));
@@ -4749,7 +4666,12 @@
         public void run() {
             int status = CALL_STATUS_NO_ERROR;
             try {
-                process();
+                if (mMediaCallType != CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED
+                        && getState() == PLAYER_STATE_ERROR) {
+                    status = CALL_STATUS_INVALID_OPERATION;
+                } else {
+                    process();
+                }
             } catch (IllegalStateException e) {
                 status = CALL_STATUS_INVALID_OPERATION;
             } catch (IllegalArgumentException e) {
@@ -4760,6 +4682,8 @@
                 status = CALL_STATUS_ERROR_IO;
             } catch (NoDrmSchemeException e) {
                 status = CALL_STATUS_NO_DRM_SCHEME;
+            } catch (CommandSkippedException e) {
+                status = CALL_STATUS_SKIPPED;
             } catch (Exception e) {
                 status = CALL_STATUS_ERROR_UNKNOWN;
             }
@@ -4786,11 +4710,17 @@
                 return;
             }
             synchronized (mEventCbLock) {
-                for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
                     cb.first.execute(() -> cb.second.onCallCompleted(
                             MediaPlayer2Impl.this, mDSD, mMediaCallType, status));
                 }
             }
         }
     };
+
+    private final class CommandSkippedException extends RuntimeException {
+        public CommandSkippedException(String detailMessage) {
+            super(detailMessage);
+        }
+    };
 }
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 70ab863..d5afd9a 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -610,7 +610,9 @@
                                         && mSelectedRoute != mBluetoothA2dpRoute)) {
                             return;
                         }
-                        Log.v(TAG, "onRestoreRoute() : route=" + mSelectedRoute);
+                        if (DEBUG) {
+                            Log.d(TAG, "onRestoreRoute() : route=" + mSelectedRoute);
+                        }
                         mSelectedRoute.select();
                     }
                 });
@@ -965,19 +967,6 @@
                 && (route == btRoute || route == sStatic.mDefaultAudioVideo)) {
             try {
                 sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute);
-                // TODO: Remove the following logging when no longer needed.
-                if (route != btRoute) {
-                    StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
-                    StringBuffer sb = new StringBuffer();
-                    // callStack[3] is the caller of this method.
-                    for (int i = 3; i < callStack.length; i++) {
-                        StackTraceElement caller = callStack[i];
-                        sb.append(caller.getClassName() + "." + caller.getMethodName()
-                                + ":" + caller.getLineNumber()).append("  ");
-                    }
-                    Log.w(TAG, "Default route is selected while a BT route is available: pkgName="
-                            + sStatic.mPackageName + ", callers=" + sb.toString());
-                }
             } catch (RemoteException e) {
                 Log.e(TAG, "Error changing Bluetooth A2DP state", e);
             }
@@ -1064,7 +1053,9 @@
     }
 
     static void addRouteStatic(RouteInfo info) {
-        Log.v(TAG, "Adding route: " + info);
+        if (DEBUG) {
+            Log.d(TAG, "Adding route: " + info);
+        }
         final RouteCategory cat = info.getCategory();
         if (!sStatic.mCategories.contains(cat)) {
             sStatic.mCategories.add(cat);
@@ -1119,7 +1110,9 @@
     }
 
     static void removeRouteStatic(RouteInfo info) {
-        Log.v(TAG, "Removing route: " + info);
+        if (DEBUG) {
+            Log.d(TAG, "Removing route: " + info);
+        }
         if (sStatic.mRoutes.remove(info)) {
             final RouteCategory removingCat = info.getCategory();
             final int count = sStatic.mRoutes.size();
diff --git a/media/java/android/media/audiofx/DefaultEffect.java b/media/java/android/media/audiofx/DefaultEffect.java
new file mode 100644
index 0000000..a919868
--- /dev/null
+++ b/media/java/android/media/audiofx/DefaultEffect.java
@@ -0,0 +1,40 @@
+/*
+ * 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.audiofx;
+
+/**
+ * DefaultEffect is the base class for controlling default audio effects linked into the
+ * Android audio framework.
+ * <p>DefaultEffects are effects that get attached automatically to all AudioTracks,
+ * AudioRecords, and MediaPlayer instances meeting some criteria.
+ * <p>Applications should not use the DefaultEffect class directly but one of its derived classes
+ * to control specific types of defaults:
+ * <ul>
+ *   <li> {@link android.media.audiofx.StreamDefaultEffect}</li>
+ * </ul>
+ * <p>Creating a DefaultEffect object will register the corresponding effect engine as a default
+ * for the specified criteria. Whenever an audio session meets the criteria, an AudioEffect will
+ * be created and attached to it using the specified priority.
+ * @hide
+ */
+
+public abstract class DefaultEffect {
+    /**
+     * System wide unique default effect ID.
+     */
+    int mId;
+}
diff --git a/media/java/android/media/audiofx/StreamDefaultEffect.java b/media/java/android/media/audiofx/StreamDefaultEffect.java
new file mode 100644
index 0000000..9b1a21a
--- /dev/null
+++ b/media/java/android/media/audiofx/StreamDefaultEffect.java
@@ -0,0 +1,117 @@
+/*
+ * 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.audiofx;
+
+import android.annotation.RequiresPermission;
+import android.app.ActivityThread;
+import android.util.Log;
+import java.util.UUID;
+
+/**
+ * StreamDefaultEffect is a default effect that attaches automatically to all AudioTracks and
+ * MediaPlayer instances of a given stream type.
+ * <p>see {@link android.media.audiofx.DefaultEffect} class for more details on default effects.
+ * @hide
+ */
+
+public class StreamDefaultEffect extends DefaultEffect {
+    static {
+        System.loadLibrary("audioeffect_jni");
+    }
+
+    private final static String TAG = "StreamDefaultEffect-JAVA";
+
+    /**
+     * Class constructor.
+     *
+     * @param type type of effect engine to be default. This parameter is ignored if uuid is set,
+     *             and can be set to {@link android.media.audiofx.AudioEffect#EFFECT_TYPE_NULL}
+     *             in that case.
+     * @param uuid unique identifier of a particular effect implementation to be default. This
+     *             parameter can be set to
+     *             {@link android.media.audiofx.AudioEffect#EFFECT_TYPE_NULL}, in which case only
+     *             the type will be used to select the effect.
+     * @param priority the priority level requested by the application for controlling the effect
+     *             engine. As the same engine can be shared by several applications, this parameter
+     *             indicates how much the requesting application needs control of effect parameters.
+     *             The normal priority is 0, above normal is a positive number, below normal a
+     *             negative number.
+     * @param streamUsage a USAGE_* constant from {@link android.media.AudioAttributes} indicating
+     *             what streams the given effect should attach to by default. Note that similar
+     *             usages may share defaults.
+     *
+     * @throws java.lang.IllegalArgumentException
+     * @throws java.lang.UnsupportedOperationException
+     * @throws java.lang.RuntimeException
+     */
+    @RequiresPermission(value = android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS,
+                        conditional = true)  // Android Things uses an alternate permission.
+    public StreamDefaultEffect(UUID type, UUID uuid, int priority, int streamUsage) {
+        int[] id = new int[1];
+        int initResult = native_setup(type.toString(),
+                                      uuid.toString(),
+                                      priority,
+                                      streamUsage,
+                                      ActivityThread.currentOpPackageName(),
+                                      id);
+        if (initResult != AudioEffect.SUCCESS) {
+            Log.e(TAG, "Error code " + initResult + " when initializing StreamDefaultEffect");
+            switch (initResult) {
+                case AudioEffect.ERROR_BAD_VALUE:
+                    throw (new IllegalArgumentException(
+                            "Stream usage, type uuid, or implementation uuid not supported."));
+                case AudioEffect.ERROR_INVALID_OPERATION:
+                    throw (new UnsupportedOperationException(
+                            "Effect library not loaded"));
+                default:
+                    throw (new RuntimeException(
+                            "Cannot initialize effect engine for type: " + type
+                            + " Error: " + initResult));
+            }
+        }
+
+        mId = id[0];
+    }
+
+
+    /**
+     * Releases the native StreamDefaultEffect resources. It is a good practice to
+     * release the default effect when done with use as control can be returned to
+     * other applications or the native resources released.
+     */
+    public void release() {
+        native_release(mId);
+    }
+
+    @Override
+    protected void finalize() {
+        release();
+    }
+
+    // ---------------------------------------------------------
+    // Native methods called from the Java side
+    // --------------------
+
+    private native final int native_setup(String type,
+                                          String uuid,
+                                          int priority,
+                                          int streamUsage,
+                                          String opPackageName,
+                                          int[] id);
+
+    private native final void native_release(int id);
+}
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index de22fa3..3f8bab5 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -130,9 +130,7 @@
     }
 
     /**
-     * Dispatches the media button event as system service to the session. This only effects the
-     * {@link MediaSession.Callback#getCurrentControllerInfo()} and doesn't bypass any permission
-     * check done by the system service.
+     * Dispatches the media button event as system service to the session.
      * <p>
      * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the
      * foreground activity didn't consume the key from the hardware devices.
@@ -154,7 +152,7 @@
             return false;
         }
         try {
-            return mSessionBinder.sendMediaButton(mContext.getPackageName(), mCbStub,
+            return mSessionBinder.sendMediaButton(mContext.getOpPackageName(), mCbStub,
                     asSystemService, keyEvent);
         } catch (RemoteException e) {
             // System is dead. =(
@@ -163,9 +161,7 @@
     }
 
     /**
-     * Dispatches the volume button event as system service to the session. This only effects the
-     * {@link MediaSession.Callback#getCurrentControllerInfo()} and doesn't bypass any permission
-     * check done by the system service.
+     * Dispatches the volume button event as system service to the session.
      * <p>
      * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the
      * foreground activity didn't consume the key from the hardware devices.
@@ -189,8 +185,8 @@
                         break;
                 }
                 try {
-                    mSessionBinder.adjustVolume(mContext.getPackageName(), mCbStub, true, direction,
-                            AudioManager.FLAG_SHOW_UI);
+                    mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true,
+                            direction, AudioManager.FLAG_SHOW_UI);
                 } catch (RemoteException e) {
                     Log.wtf(TAG, "Error calling adjustVolumeBy", e);
                 }
@@ -200,7 +196,8 @@
                 final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE
                         | AudioManager.FLAG_FROM_KEY;
                 try {
-                    mSessionBinder.adjustVolume(mContext.getPackageName(), mCbStub, true, 0, flags);
+                    mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, true, 0,
+                            flags);
                 } catch (RemoteException e) {
                     Log.wtf(TAG, "Error calling adjustVolumeBy", e);
                 }
@@ -369,7 +366,7 @@
      */
     public void setVolumeTo(int value, int flags) {
         try {
-            mSessionBinder.setVolumeTo(mContext.getPackageName(), mCbStub, value, flags);
+            mSessionBinder.setVolumeTo(mContext.getOpPackageName(), mCbStub, value, flags);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error calling setVolumeTo.", e);
         }
@@ -390,7 +387,7 @@
      */
     public void adjustVolume(int direction, int flags) {
         try {
-            mSessionBinder.adjustVolume(mContext.getPackageName(), mCbStub, false, direction,
+            mSessionBinder.adjustVolume(mContext.getOpPackageName(), mCbStub, false, direction,
                     flags);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
@@ -457,7 +454,7 @@
             throw new IllegalArgumentException("command cannot be null or empty");
         }
         try {
-            mSessionBinder.sendCommand(mContext.getPackageName(), mCbStub, command, args, cb);
+            mSessionBinder.sendCommand(mContext.getOpPackageName(), mCbStub, command, args, cb);
         } catch (RemoteException e) {
             Log.d(TAG, "Dead object in sendCommand.", e);
         }
@@ -522,7 +519,7 @@
 
         if (!mCbRegistered) {
             try {
-                mSessionBinder.registerCallbackListener(mContext.getPackageName(), mCbStub);
+                mSessionBinder.registerCallbackListener(mContext.getOpPackageName(), mCbStub);
                 mCbRegistered = true;
             } catch (RemoteException e) {
                 Log.e(TAG, "Dead object in registerCallback", e);
@@ -669,7 +666,7 @@
          */
         public void prepare() {
             try {
-                mSessionBinder.prepare(mContext.getPackageName(), mCbStub);
+                mSessionBinder.prepare(mContext.getOpPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare.", e);
             }
@@ -693,7 +690,7 @@
                         "You must specify a non-empty String for prepareFromMediaId.");
             }
             try {
-                mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mCbStub, mediaId,
+                mSessionBinder.prepareFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId,
                         extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e);
@@ -720,7 +717,8 @@
                 query = "";
             }
             try {
-                mSessionBinder.prepareFromSearch(mContext.getPackageName(), mCbStub, query, extras);
+                mSessionBinder.prepareFromSearch(mContext.getOpPackageName(), mCbStub, query,
+                        extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare(" + query + ").", e);
             }
@@ -744,7 +742,7 @@
                         "You must specify a non-empty Uri for prepareFromUri.");
             }
             try {
-                mSessionBinder.prepareFromUri(mContext.getPackageName(), mCbStub, uri, extras);
+                mSessionBinder.prepareFromUri(mContext.getOpPackageName(), mCbStub, uri, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare(" + uri + ").", e);
             }
@@ -755,7 +753,7 @@
          */
         public void play() {
             try {
-                mSessionBinder.play(mContext.getPackageName(), mCbStub);
+                mSessionBinder.play(mContext.getOpPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play.", e);
             }
@@ -774,7 +772,8 @@
                         "You must specify a non-empty String for playFromMediaId.");
             }
             try {
-                mSessionBinder.playFromMediaId(mContext.getPackageName(), mCbStub, mediaId, extras);
+                mSessionBinder.playFromMediaId(mContext.getOpPackageName(), mCbStub, mediaId,
+                        extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
             }
@@ -796,7 +795,7 @@
                 query = "";
             }
             try {
-                mSessionBinder.playFromSearch(mContext.getPackageName(), mCbStub, query, extras);
+                mSessionBinder.playFromSearch(mContext.getOpPackageName(), mCbStub, query, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play(" + query + ").", e);
             }
@@ -815,7 +814,7 @@
                         "You must specify a non-empty Uri for playFromUri.");
             }
             try {
-                mSessionBinder.playFromUri(mContext.getPackageName(), mCbStub, uri, extras);
+                mSessionBinder.playFromUri(mContext.getOpPackageName(), mCbStub, uri, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play(" + uri + ").", e);
             }
@@ -827,7 +826,7 @@
          */
         public void skipToQueueItem(long id) {
             try {
-                mSessionBinder.skipToQueueItem(mContext.getPackageName(), mCbStub, id);
+                mSessionBinder.skipToQueueItem(mContext.getOpPackageName(), mCbStub, id);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
             }
@@ -839,7 +838,7 @@
          */
         public void pause() {
             try {
-                mSessionBinder.pause(mContext.getPackageName(), mCbStub);
+                mSessionBinder.pause(mContext.getOpPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling pause.", e);
             }
@@ -851,7 +850,7 @@
          */
         public void stop() {
             try {
-                mSessionBinder.stop(mContext.getPackageName(), mCbStub);
+                mSessionBinder.stop(mContext.getOpPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling stop.", e);
             }
@@ -864,7 +863,7 @@
          */
         public void seekTo(long pos) {
             try {
-                mSessionBinder.seekTo(mContext.getPackageName(), mCbStub, pos);
+                mSessionBinder.seekTo(mContext.getOpPackageName(), mCbStub, pos);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling seekTo.", e);
             }
@@ -876,7 +875,7 @@
          */
         public void fastForward() {
             try {
-                mSessionBinder.fastForward(mContext.getPackageName(), mCbStub);
+                mSessionBinder.fastForward(mContext.getOpPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling fastForward.", e);
             }
@@ -887,7 +886,7 @@
          */
         public void skipToNext() {
             try {
-                mSessionBinder.next(mContext.getPackageName(), mCbStub);
+                mSessionBinder.next(mContext.getOpPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling next.", e);
             }
@@ -899,7 +898,7 @@
          */
         public void rewind() {
             try {
-                mSessionBinder.rewind(mContext.getPackageName(), mCbStub);
+                mSessionBinder.rewind(mContext.getOpPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling rewind.", e);
             }
@@ -910,7 +909,7 @@
          */
         public void skipToPrevious() {
             try {
-                mSessionBinder.previous(mContext.getPackageName(), mCbStub);
+                mSessionBinder.previous(mContext.getOpPackageName(), mCbStub);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling previous.", e);
             }
@@ -925,7 +924,7 @@
          */
         public void setRating(Rating rating) {
             try {
-                mSessionBinder.rate(mContext.getPackageName(), mCbStub, rating);
+                mSessionBinder.rate(mContext.getOpPackageName(), mCbStub, rating);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling rate.", e);
             }
@@ -960,7 +959,7 @@
                 throw new IllegalArgumentException("CustomAction cannot be null.");
             }
             try {
-                mSessionBinder.sendCustomAction(mContext.getPackageName(), mCbStub, action, args);
+                mSessionBinder.sendCustomAction(mContext.getOpPackageName(), mCbStub, action, args);
             } catch (RemoteException e) {
                 Log.d(TAG, "Dead object in sendCustomAction.", e);
             }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 3f0b6c5..98fb573 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -321,7 +321,7 @@
     private void dispatchMediaKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
             boolean needWakeLock) {
         try {
-            mService.dispatchMediaKeyEvent(mContext.getPackageName(), asSystemService, keyEvent,
+            mService.dispatchMediaKeyEvent(mContext.getOpPackageName(), asSystemService, keyEvent,
                     needWakeLock);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to send key event.", e);
@@ -357,7 +357,7 @@
     private void dispatchVolumeKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent,
             int stream, boolean musicOnly) {
         try {
-            mService.dispatchVolumeKeyEvent(mContext.getPackageName(), asSystemService, keyEvent,
+            mService.dispatchVolumeKeyEvent(mContext.getOpPackageName(), asSystemService, keyEvent,
                     stream, musicOnly);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to send volume key event.", e);
@@ -378,7 +378,7 @@
      */
     public void dispatchAdjustVolume(int suggestedStream, int direction, int flags) {
         try {
-            mService.dispatchAdjustVolume(mContext.getPackageName(), suggestedStream, direction,
+            mService.dispatchAdjustVolume(mContext.getOpPackageName(), suggestedStream, direction,
                     flags);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to send adjust volume.", e);
@@ -460,7 +460,7 @@
         try {
             List<Bundle> bundles = mService.getSessionTokens(
                     /* activeSessionOnly */ true, /* sessionServiceOnly */ false,
-                    mContext.getPackageName());
+                    mContext.getOpPackageName());
             return toTokenList(bundles);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -483,7 +483,7 @@
         try {
             List<Bundle> bundles = mService.getSessionTokens(
                     /* activeSessionOnly */ false, /* sessionServiceOnly */ true,
-                    mContext.getPackageName());
+                    mContext.getOpPackageName());
             return toTokenList(bundles);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -508,7 +508,7 @@
         try {
             List<Bundle> bundles = mService.getSessionTokens(
                     /* activeSessionOnly */ false, /* sessionServiceOnly */ false,
-                    mContext.getPackageName());
+                    mContext.getOpPackageName());
             return toTokenList(bundles);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -561,7 +561,8 @@
             SessionTokensChangedWrapper wrapper = new SessionTokensChangedWrapper(
                     mContext, executor, listener);
             try {
-                mService.addSessionTokensListener(wrapper.mStub, userId, mContext.getPackageName());
+                mService.addSessionTokensListener(wrapper.mStub, userId,
+                        mContext.getOpPackageName());
                 mSessionTokensListener.put(listener, wrapper);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error in addSessionTokensListener.", e);
@@ -584,7 +585,8 @@
             SessionTokensChangedWrapper wrapper = mSessionTokensListener.remove(listener);
             if (wrapper != null) {
                 try {
-                    mService.removeSessionTokensListener(wrapper.mStub, mContext.getPackageName());
+                    mService.removeSessionTokensListener(wrapper.mStub,
+                            mContext.getOpPackageName());
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error in removeSessionTokensListener.", e);
                 } finally {
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 143182f..ec2d9ba 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -888,6 +888,8 @@
                     if (token != null) {
                         session = new Session(token, channel, mService, mUserId, seq,
                                 mSessionCallbackRecordMap);
+                    } else {
+                        mSessionCallbackRecordMap.delete(seq);
                     }
                     record.postSessionCreated(session);
                 }
@@ -2487,7 +2489,7 @@
                 }
             }
             synchronized (mSessionCallbackRecordMap) {
-                mSessionCallbackRecordMap.remove(mSeq);
+                mSessionCallbackRecordMap.delete(mSeq);
             }
         }
 
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index e568ef7..9c58135 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -52,11 +52,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.Iterator;
+import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.IntStream;
-import java.util.stream.Stream;
 
 /**
  * MtpDatabase provides an interface for MTP operations that MtpServer can use. To do this, it uses
@@ -453,21 +452,25 @@
     }
 
     private int[] getObjectList(int storageID, int format, int parent) {
-        Stream<MtpStorageManager.MtpObject> objectStream = mManager.getObjects(parent,
+        List<MtpStorageManager.MtpObject> objs = mManager.getObjects(parent,
                 format, storageID);
-        if (objectStream == null) {
+        if (objs == null) {
             return null;
         }
-        return objectStream.mapToInt(MtpStorageManager.MtpObject::getId).toArray();
+        int[] ret = new int[objs.size()];
+        for (int i = 0; i < objs.size(); i++) {
+            ret[i] = objs.get(i).getId();
+        }
+        return ret;
     }
 
     private int getNumObjects(int storageID, int format, int parent) {
-        Stream<MtpStorageManager.MtpObject> objectStream = mManager.getObjects(parent,
+        List<MtpStorageManager.MtpObject> objs = mManager.getObjects(parent,
                 format, storageID);
-        if (objectStream == null) {
+        if (objs == null) {
             return -1;
         }
-        return (int) objectStream.count();
+        return objs.size();
     }
 
     private MtpPropertyList getObjectPropertyList(int handle, int format, int property,
@@ -489,11 +492,12 @@
             // depth 0: single object, depth 1: immediate children
             return new MtpPropertyList(MtpConstants.RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED);
         }
-        Stream<MtpStorageManager.MtpObject> objectStream = Stream.of();
+        List<MtpStorageManager.MtpObject> objs = null;
+        MtpStorageManager.MtpObject thisObj = null;
         if (handle == 0xFFFFFFFF) {
             // All objects are requested
-            objectStream = mManager.getObjects(0, format, 0xFFFFFFFF);
-            if (objectStream == null) {
+            objs = mManager.getObjects(0, format, 0xFFFFFFFF);
+            if (objs == null) {
                 return new MtpPropertyList(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
             }
         } else if (handle != 0) {
@@ -503,7 +507,7 @@
                 return new MtpPropertyList(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
             }
             if (obj.getFormat() == format || format == 0) {
-                objectStream = Stream.of(obj);
+                thisObj = obj;
             }
         }
         if (handle == 0 || depth == 1) {
@@ -511,19 +515,22 @@
                 handle = 0xFFFFFFFF;
             }
             // Get the direct children of root or this object.
-            Stream<MtpStorageManager.MtpObject> childStream = mManager.getObjects(handle, format,
+            objs = mManager.getObjects(handle, format,
                     0xFFFFFFFF);
-            if (childStream == null) {
+            if (objs == null) {
                 return new MtpPropertyList(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
             }
-            objectStream = Stream.concat(objectStream, childStream);
+        }
+        if (objs == null) {
+            objs = new ArrayList<>();
+        }
+        if (thisObj != null) {
+            objs.add(thisObj);
         }
 
         MtpPropertyList ret = new MtpPropertyList(MtpConstants.RESPONSE_OK);
         MtpPropertyGroup propertyGroup;
-        Iterator<MtpStorageManager.MtpObject> iter = objectStream.iterator();
-        while (iter.hasNext()) {
-            MtpStorageManager.MtpObject obj = iter.next();
+        for (MtpStorageManager.MtpObject obj : objs) {
             if (property == 0xffffffff) {
                 // Get all properties supported by this object
                 propertyGroup = mPropertyGroupsByFormat.get(obj.getFormat());
diff --git a/media/java/android/mtp/MtpStorageManager.java b/media/java/android/mtp/MtpStorageManager.java
index a36d88d..756e942 100644
--- a/media/java/android/mtp/MtpStorageManager.java
+++ b/media/java/android/mtp/MtpStorageManager.java
@@ -31,10 +31,8 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Objects;
+import java.util.List;
 import java.util.Set;
-import java.util.stream.Stream;
 
 /**
  * MtpStorageManager provides functionality for listing, tracking, and notifying MtpServer of
@@ -358,13 +356,13 @@
      * Clean up resources used by the storage manager.
      */
     public synchronized void close() {
-        Stream<MtpObject> objs = Stream.concat(mRoots.values().stream(),
-                mObjects.values().stream());
-
-        Iterator<MtpObject> iter = objs.iterator();
-        while (iter.hasNext()) {
-            // Close all FileObservers.
-            MtpObject obj = iter.next();
+        for (MtpObject obj : mObjects.values()) {
+            if (obj.getObserver() != null) {
+                obj.getObserver().stopWatching();
+                obj.setObserver(null);
+            }
+        }
+        for (MtpObject obj : mRoots.values()) {
             if (obj.getObserver() != null) {
                 obj.getObserver().stopWatching();
                 obj.setObserver(null);
@@ -495,49 +493,48 @@
      * @param parent object id of the parent. 0 for all objects, 0xFFFFFFFF for all object in root
      * @param format format of returned objects. 0 for any format
      * @param storageId storage id to look in. 0xFFFFFFFF for all storages
-     * @return A stream of matched objects, or null if error
+     * @return A list of matched objects, or null if error
      */
-    public synchronized Stream<MtpObject> getObjects(int parent, int format, int storageId) {
+    public synchronized List<MtpObject> getObjects(int parent, int format, int storageId) {
         boolean recursive = parent == 0;
+        ArrayList<MtpObject> objs = new ArrayList<>();
+        boolean ret = true;
         if (parent == 0xFFFFFFFF)
             parent = 0;
         if (storageId == 0xFFFFFFFF) {
             // query all stores
             if (parent == 0) {
                 // Get the objects of this format and parent in each store.
-                ArrayList<Stream<MtpObject>> streamList = new ArrayList<>();
                 for (MtpObject root : mRoots.values()) {
-                    streamList.add(getObjects(root, format, recursive));
+                    ret &= getObjects(objs, root, format, recursive);
                 }
-                return Stream.of(streamList).flatMap(Collection::stream).reduce(Stream::concat)
-                        .orElseGet(Stream::empty);
+                return ret ? objs : null;
             }
         }
         MtpObject obj = parent == 0 ? getStorageRoot(storageId) : getObject(parent);
         if (obj == null)
             return null;
-        return getObjects(obj, format, recursive);
+        ret = getObjects(objs, obj, format, recursive);
+        return ret ? objs : null;
     }
 
-    private synchronized Stream<MtpObject> getObjects(MtpObject parent, int format, boolean rec) {
+    private synchronized boolean getObjects(List<MtpObject> toAdd, MtpObject parent, int format, boolean rec) {
         Collection<MtpObject> children = getChildren(parent);
         if (children == null)
-            return null;
-        Stream<MtpObject> ret = Stream.of(children).flatMap(Collection::stream);
+            return false;
 
-        if (format != 0) {
-            ret = ret.filter(o -> o.getFormat() == format);
+        for (MtpObject o : children) {
+            if (format == 0 || o.getFormat() == format) {
+                toAdd.add(o);
+            }
         }
+        boolean ret = true;
         if (rec) {
             // Get all objects recursively.
-            ArrayList<Stream<MtpObject>> streamList = new ArrayList<>();
-            streamList.add(ret);
             for (MtpObject o : children) {
                 if (o.isDir())
-                    streamList.add(getObjects(o, format, true));
+                    ret &= getObjects(toAdd, o, format, true);
             }
-            ret = Stream.of(streamList).filter(Objects::nonNull).flatMap(Collection::stream)
-                    .reduce(Stream::concat).orElseGet(Stream::empty);
         }
         return ret;
     }
@@ -767,12 +764,11 @@
      * @return true iff cache is consistent
      */
     public synchronized boolean checkConsistency() {
-        Stream<MtpObject> objs = Stream.concat(mRoots.values().stream(),
-                mObjects.values().stream());
-        Iterator<MtpObject> iter = objs.iterator();
+        List<MtpObject> objs = new ArrayList<>();
+        objs.addAll(mRoots.values());
+        objs.addAll(mObjects.values());
         boolean ret = true;
-        while (iter.hasNext()) {
-            MtpObject obj = iter.next();
+        for (MtpObject obj : objs) {
             if (!obj.exists()) {
                 Log.w(TAG, "Object doesn't exist " + obj.getPath() + " " + obj.getId());
                 ret = false;
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index ddbbaa4..bf80c57 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -90,9 +90,7 @@
     srcs: [
         "android_media_Media2HTTPConnection.cpp",
         "android_media_Media2HTTPService.cpp",
-        "android_media_MediaCrypto.cpp",
         "android_media_Media2DataSource.cpp",
-        "android_media_MediaDrm.cpp",
         "android_media_MediaPlayer2.cpp",
         "android_media_SyncParams.cpp",
     ],
@@ -100,19 +98,24 @@
     shared_libs: [
         "android.hardware.cas@1.0",  // for CasManager. VNDK???
         "android.hardware.cas.native@1.0",  // CasManager. VNDK???
-        "libandroid",  // NDK
         "libandroid_runtime",  // ???
         "libaudioclient",  // for use of AudioTrack, AudioSystem. to be removed
-        "liblog",  // NDK
+        "libbinder",
         "libdrmframework",  // for FileSource, MediaHTTP
         "libgui",  // for VideoFrameScheduler
         "libhidlallocatorutils",
         "libhidlbase",  // VNDK???
-        "libmediandk",  // NDK
         "libpowermanager",  // for JWakeLock. to be removed
 
         "libutils",  // Have to use shared lib to make libandroid_runtime behave correctly.
                      // Otherwise, AndroidRuntime::getJNIEnv() will return NULL.
+
+        // NDK or NDK-compliant
+        "libandroid",
+        "libmediandk",
+        "libnativehelper_compat_libc++",
+        "liblog",
+        "libz",
     ],
 
     header_libs: ["libhardware_headers"],
@@ -120,7 +123,6 @@
     static_libs: [
         "libbacktrace",
         "libbase",
-        "libbinder",
         "libc_malloc_debug_backtrace",
         "libcrypto",
         "libcutils",
@@ -133,7 +135,6 @@
         "libmediametrics",
         "libmediaplayer2",
         "libmediautils",
-        "libnativehelper",
         "libnetd_client",
         "libstagefright_esds",
         "libstagefright_foundation",
@@ -146,7 +147,6 @@
         "libstagefright_timedtext",
         "libunwindstack",
         "libutilscallstack",
-        "libz",
         "libziparchive",
     ],
 
@@ -168,7 +168,7 @@
         "-Wunreachable-code",
     ],
 
-    ldflags: ["-Wl,--exclude-libs=ALL"],
+    ldflags: ["-Wl,--exclude-libs=ALL,-error-limit=0"],
 }
 
 subdirs = [
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index c36858a..a45aa90 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -612,8 +612,12 @@
     Image_setBufferItem(env, image, buffer);
     env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp,
             static_cast<jlong>(buffer->mTimestamp));
+    auto transform = buffer->mTransform;
+    if (buffer->mTransformToDisplayInverse) {
+        transform |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+    }
     env->SetIntField(image, gSurfaceImageClassInfo.mTransform,
-            static_cast<jint>(buffer->mTransform));
+            static_cast<jint>(transform));
     env->SetIntField(image, gSurfaceImageClassInfo.mScalingMode,
             static_cast<jint>(buffer->mScalingMode));
 
diff --git a/media/jni/android_media_Media2DataSource.cpp b/media/jni/android_media_Media2DataSource.cpp
index bc3f6bd..b3434e9 100644
--- a/media/jni/android_media_Media2DataSource.cpp
+++ b/media/jni/android_media_Media2DataSource.cpp
@@ -20,12 +20,12 @@
 
 #include "android_media_Media2DataSource.h"
 
-#include "android_runtime/AndroidRuntime.h"
-#include "android_runtime/Log.h"
+#include "log/log.h"
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 
 #include <drm/drm_framework_common.h>
+#include <mediaplayer2/JavaVMHelper.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <nativehelper/ScopedLocalRef.h>
 
@@ -56,7 +56,7 @@
 }
 
 JMedia2DataSource::~JMedia2DataSource() {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = JavaVMHelper::getJNIEnv();
     env->DeleteGlobalRef(mMedia2DataSourceObj);
     env->DeleteGlobalRef(mByteArrayObj);
 }
@@ -75,12 +75,12 @@
         size = kBufferSize;
     }
 
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = JavaVMHelper::getJNIEnv();
     jint numread = env->CallIntMethod(mMedia2DataSourceObj, mReadAtMethod,
             (jlong)offset, mByteArrayObj, (jint)0, (jint)size);
     if (env->ExceptionCheck()) {
         ALOGW("An exception occurred in readAt()");
-        LOGW_EX(env);
+        jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
         env->ExceptionClear();
         mJavaObjStatus = UNKNOWN_ERROR;
         return -1;
@@ -117,11 +117,11 @@
         return OK;
     }
 
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = JavaVMHelper::getJNIEnv();
     *size = env->CallLongMethod(mMedia2DataSourceObj, mGetSizeMethod);
     if (env->ExceptionCheck()) {
         ALOGW("An exception occurred in getSize()");
-        LOGW_EX(env);
+        jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
         env->ExceptionClear();
         // After returning an error, size shouldn't be used by callers.
         *size = UNKNOWN_ERROR;
@@ -142,7 +142,7 @@
 void JMedia2DataSource::close() {
     Mutex::Autolock lock(mLock);
 
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = JavaVMHelper::getJNIEnv();
     env->CallVoidMethod(mMedia2DataSourceObj, mCloseMethod);
     // The closed state is effectively the same as an error state.
     mJavaObjStatus = UNKNOWN_ERROR;
diff --git a/media/jni/android_media_Media2HTTPConnection.cpp b/media/jni/android_media_Media2HTTPConnection.cpp
index 60176e3..d02ee06 100644
--- a/media/jni/android_media_Media2HTTPConnection.cpp
+++ b/media/jni/android_media_Media2HTTPConnection.cpp
@@ -18,14 +18,14 @@
 #define LOG_TAG "Media2HTTPConnection-JNI"
 #include <utils/Log.h>
 
+#include <mediaplayer2/JavaVMHelper.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <nativehelper/ScopedLocalRef.h>
 
 #include "android_media_Media2HTTPConnection.h"
 #include "android_util_Binder.h"
 
-#include "android_runtime/AndroidRuntime.h"
-#include "android_runtime/Log.h"
+#include "log/log.h"
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 
@@ -84,7 +84,7 @@
 }
 
 JMedia2HTTPConnection::~JMedia2HTTPConnection() {
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
     env->DeleteGlobalRef(mMedia2HTTPConnectionObj);
     env->DeleteGlobalRef(mByteArrayObj);
 }
@@ -101,7 +101,7 @@
         }
     }
 
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = JavaVMHelper::getJNIEnv();
     jstring juri = env->NewStringUTF(uri);
     jstring jheaders = env->NewStringUTF(tmp.string());
 
@@ -115,12 +115,12 @@
 }
 
 void JMedia2HTTPConnection::disconnect() {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = JavaVMHelper::getJNIEnv();
     env->CallVoidMethod(mMedia2HTTPConnectionObj, mDisconnectMethod);
 }
 
 ssize_t JMedia2HTTPConnection::readAt(off64_t offset, void *data, size_t size) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = JavaVMHelper::getJNIEnv();
 
     if (size > kBufferSize) {
         size = kBufferSize;
@@ -141,12 +141,12 @@
 }
 
 off64_t JMedia2HTTPConnection::getSize() {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = JavaVMHelper::getJNIEnv();
     return (off64_t)(env->CallLongMethod(mMedia2HTTPConnectionObj, mGetSizeMethod));
 }
 
 status_t JMedia2HTTPConnection::getMIMEType(String8 *mimeType) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = JavaVMHelper::getJNIEnv();
     jstring jmime = (jstring)env->CallObjectMethod(mMedia2HTTPConnectionObj, mGetMIMETypeMethod);
     jboolean flag = env->ExceptionCheck();
     if (flag) {
@@ -165,7 +165,7 @@
 }
 
 status_t JMedia2HTTPConnection::getUri(String8 *uri) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = JavaVMHelper::getJNIEnv();
     jstring juri = (jstring)env->CallObjectMethod(mMedia2HTTPConnectionObj, mGetUriMethod);
     jboolean flag = env->ExceptionCheck();
     if (flag) {
diff --git a/media/jni/android_media_Media2HTTPService.cpp b/media/jni/android_media_Media2HTTPService.cpp
index 382f099..1c63889 100644
--- a/media/jni/android_media_Media2HTTPService.cpp
+++ b/media/jni/android_media_Media2HTTPService.cpp
@@ -21,11 +21,11 @@
 #include "android_media_Media2HTTPConnection.h"
 #include "android_media_Media2HTTPService.h"
 
-#include "android_runtime/AndroidRuntime.h"
-#include "android_runtime/Log.h"
+#include "log/log.h"
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 
+#include <mediaplayer2/JavaVMHelper.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <nativehelper/ScopedLocalRef.h>
 
@@ -46,12 +46,12 @@
 }
 
 JMedia2HTTPService::~JMedia2HTTPService() {
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
     env->DeleteGlobalRef(mMedia2HTTPServiceObj);
 }
 
 sp<MediaHTTPConnection> JMedia2HTTPService::makeHTTPConnection() {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    JNIEnv* env = JavaVMHelper::getJNIEnv();
     jobject media2HTTPConnectionObj =
         env->CallObjectMethod(mMedia2HTTPServiceObj, mMakeHTTPConnectionMethod);
 
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 54541f0..7cf8828 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -43,27 +43,27 @@
 
 #define FIND_CLASS(var, className) \
     var = env->FindClass(className); \
-    LOG_FATAL_IF(! (var), "Unable to find class " className);
+    LOG_FATAL_IF(! (var), "Unable to find class %s", className);
 
 #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
     var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
-    LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
+    LOG_FATAL_IF(! (var), "Unable to find field %s", fieldName);
 
 #define GET_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
     var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \
-    LOG_FATAL_IF(! (var), "Unable to find method " fieldName);
+    LOG_FATAL_IF(! (var), "Unable to find method %s", fieldName);
 
 #define GET_STATIC_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
     var = env->GetStaticFieldID(clazz, fieldName, fieldDescriptor); \
-    LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
+    LOG_FATAL_IF(! (var), "Unable to find field %s", fieldName);
 
 #define GET_STATIC_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
     var = env->GetStaticMethodID(clazz, fieldName, fieldDescriptor); \
-    LOG_FATAL_IF(! (var), "Unable to find static method " fieldName);
+    LOG_FATAL_IF(! (var), "Unable to find static method %s", fieldName);
 
-#define GET_STATIC_OBJECT_FIELD(var, clazz, fieldName) \
-    var = env->GetStaticObjectField(clazz, fieldName); \
-    LOG_FATAL_IF(! (var), "Unable to find static object field " fieldName);
+#define GET_STATIC_OBJECT_FIELD(var, clazz, fieldId) \
+    var = env->GetStaticObjectField(clazz, fieldId); \
+    LOG_FATAL_IF(! (var), "Unable to find static object field %p", fieldId);
 
 
 struct RequestFields {
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 6546cf0..4265987 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -29,6 +29,7 @@
 #include <media/stagefright/Utils.h>
 #include <media/stagefright/foundation/ByteUtils.h>  // for FOURCC definition
 #include <mediaplayer2/JAudioTrack.h>
+#include <mediaplayer2/JavaVMHelper.h>
 #include <mediaplayer2/mediaplayer2.h>
 #include <stdio.h>
 #include <assert.h>
@@ -39,7 +40,7 @@
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 #include "android/native_window_jni.h"
-#include "android_runtime/Log.h"
+#include "log/log.h"
 #include "utils/Errors.h"  // for status_t
 #include "utils/KeyedVector.h"
 #include "utils/String8.h"
@@ -184,14 +185,14 @@
 JNIMediaPlayer2Listener::~JNIMediaPlayer2Listener()
 {
     // remove global references
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
     env->DeleteGlobalRef(mObject);
     env->DeleteGlobalRef(mClass);
 }
 
 void JNIMediaPlayer2Listener::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj)
 {
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
     if (obj && obj->dataSize() > 0) {
         jobject jParcel = createJavaParcelObject(env);
         if (jParcel != NULL) {
@@ -207,7 +208,7 @@
     }
     if (env->ExceptionCheck()) {
         ALOGW("An exception occurred while notifying an event.");
-        LOGW_EX(env);
+        jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
         env->ExceptionClear();
     }
 }
@@ -568,18 +569,6 @@
 }
 
 static void
-android_media_MediaPlayer2_stop(JNIEnv *env, jobject thiz)
-{
-    ALOGV("stop");
-    sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
-    if (mp == NULL ) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
-        return;
-    }
-    process_media_player_call( env, thiz, mp->stop(), NULL, NULL );
-}
-
-static void
 android_media_MediaPlayer2_pause(JNIEnv *env, jobject thiz)
 {
     ALOGV("pause");
@@ -786,13 +775,13 @@
 }
 
 static jint
-android_media_MediaPlayer2_getMediaPlayer2State(JNIEnv *env, jobject thiz)
+android_media_MediaPlayer2_getState(JNIEnv *env, jobject thiz)
 {
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL) {
         return MEDIAPLAYER2_STATE_IDLE;
     }
-    return (jint)mp->getMediaPlayer2State();
+    return (jint)mp->getState();
 }
 
 static jint
@@ -1501,8 +1490,7 @@
     {"_setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer2_setBufferingParams},
     {"_prepare",            "()V",                              (void *)android_media_MediaPlayer2_prepare},
     {"_start",              "()V",                              (void *)android_media_MediaPlayer2_start},
-    {"_stop",               "()V",                              (void *)android_media_MediaPlayer2_stop},
-    {"native_getMediaPlayer2State", "()I",                      (void *)android_media_MediaPlayer2_getMediaPlayer2State},
+    {"native_getState",     "()I",                              (void *)android_media_MediaPlayer2_getState},
     {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer2_getVideoWidth},
     {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer2_getVideoHeight},
     {"native_getMetrics",   "()Landroid/os/PersistableBundle;", (void *)android_media_MediaPlayer2_native_getMetrics},
@@ -1552,8 +1540,7 @@
 // This function only registers the native methods
 static int register_android_media_MediaPlayer2Impl(JNIEnv *env)
 {
-    return AndroidRuntime::registerNativeMethods(env,
-                "android/media/MediaPlayer2Impl", gMethods, NELEM(gMethods));
+    return jniRegisterNativeMethods(env, "android/media/MediaPlayer2Impl", gMethods, NELEM(gMethods));
 }
 
 jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
@@ -1572,6 +1559,8 @@
         goto bail;
     }
 
+    JavaVMHelper::setJavaVM(vm);
+
     /* success -- return valid version number */
     result = JNI_VERSION_1_4;
 
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index 2aca0c1..0063c11 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -3,6 +3,7 @@
 
     srcs: [
         "android_media_AudioEffect.cpp",
+        "android_media_StreamDefaultEffect.cpp",
         "android_media_Visualizer.cpp",
     ],
 
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 51b080a3..d3ba9f2 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "android_media_AudioEffect.h"
+
 #include <stdio.h>
 
 //#define LOG_NDEBUG 0
@@ -71,7 +73,7 @@
 };
 
 
-static jint translateError(int code) {
+jint AudioEffectJni::translateNativeErrorToJava(int code) {
     switch(code) {
     case NO_ERROR:
         return AUDIOEFFECT_SUCCESS;
@@ -81,6 +83,10 @@
         return AUDIOEFFECT_ERROR_NO_INIT;
     case BAD_VALUE:
         return AUDIOEFFECT_ERROR_BAD_VALUE;
+    case NAME_NOT_FOUND:
+        // Name not found means the client tried to create an effect not found on the system,
+        // which is a form of bad value.
+        return AUDIOEFFECT_ERROR_BAD_VALUE;
     case INVALID_OPERATION:
         return AUDIOEFFECT_ERROR_INVALID_OPERATION;
     case NO_MEMORY:
@@ -359,7 +365,7 @@
         goto setup_failure;
     }
 
-    lStatus = translateError(lpAudioEffect->initCheck());
+    lStatus = AudioEffectJni::translateNativeErrorToJava(lpAudioEffect->initCheck());
     if (lStatus != AUDIOEFFECT_SUCCESS && lStatus != AUDIOEFFECT_ERROR_ALREADY_EXISTS) {
         ALOGE("AudioEffect initCheck failed %d", lStatus);
         goto setup_failure;
@@ -495,7 +501,7 @@
         return AUDIOEFFECT_ERROR_NO_INIT;
     }
 
-    return (jint) translateError(lpAudioEffect->setEnabled(enabled));
+    return AudioEffectJni::translateNativeErrorToJava(lpAudioEffect->setEnabled(enabled));
 }
 
 static jboolean
@@ -590,7 +596,7 @@
     if (lpValue != NULL) {
         env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0);
     }
-    return (jint) translateError(lStatus);
+    return AudioEffectJni::translateNativeErrorToJava(lStatus);
 }
 
 static jint
@@ -658,7 +664,7 @@
     if (lStatus == NO_ERROR) {
         return vsize;
     }
-    return (jint) translateError(lStatus);
+    return AudioEffectJni::translateNativeErrorToJava(lStatus);
 }
 
 static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz,
@@ -697,11 +703,12 @@
         }
     }
 
-    lStatus = translateError(lpAudioEffect->command((uint32_t)cmdCode,
-                                                    (uint32_t)cmdSize,
-                                                    pCmdData,
-                                                    (uint32_t *)&replySize,
-                                                    pReplyData));
+    lStatus = AudioEffectJni::translateNativeErrorToJava(
+            lpAudioEffect->command((uint32_t)cmdCode,
+                                   (uint32_t)cmdSize,
+                                   pCmdData,
+                                   (uint32_t *)&replySize,
+                                   pReplyData));
 
 command_Exit:
 
@@ -900,6 +907,7 @@
 
 // ----------------------------------------------------------------------------
 
+extern int register_android_media_StreamDefaultEffect(JNIEnv *env);
 extern int register_android_media_visualizer(JNIEnv *env);
 
 int register_android_media_AudioEffect(JNIEnv *env)
@@ -924,6 +932,11 @@
         goto bail;
     }
 
+    if (register_android_media_StreamDefaultEffect(env) < 0) {
+        ALOGE("ERROR: StreamDefaultEffect native registration failed\n");
+        goto bail;
+    }
+
     if (register_android_media_visualizer(env) < 0) {
         ALOGE("ERROR: Visualizer native registration failed\n");
         goto bail;
@@ -935,4 +948,3 @@
 bail:
     return result;
 }
-
diff --git a/media/jni/audioeffect/android_media_AudioEffect.h b/media/jni/audioeffect/android_media_AudioEffect.h
new file mode 100644
index 0000000..1d7d75d
--- /dev/null
+++ b/media/jni/audioeffect/android_media_AudioEffect.h
@@ -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.
+ */
+
+#ifndef _ANDROID_MEDIA_AUDIO_EFFECT_H_
+#define _ANDROID_MEDIA_AUDIO_EFFECT_H_
+
+#include <jni.h>
+
+namespace android {
+
+class AudioEffectJni {
+public:
+    // Convert from native error code to AudioEffect.java error code.
+    static jint translateNativeErrorToJava(int code);
+private:
+    // Static methods only, so private constructor.
+    AudioEffectJni() = default;
+};
+
+} // namespace android
+
+#endif // _ANDROID_MEDIA_AUDIO_EFFECT_H_
diff --git a/media/jni/audioeffect/android_media_StreamDefaultEffect.cpp b/media/jni/audioeffect/android_media_StreamDefaultEffect.cpp
new file mode 100644
index 0000000..accdddb
--- /dev/null
+++ b/media/jni/audioeffect/android_media_StreamDefaultEffect.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "StreamDefaultEffect-JNI"
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include "media/AudioEffect.h"
+
+#include <nativehelper/ScopedUtfChars.h>
+
+#include "android_media_AudioEffect.h"
+
+using namespace android;
+
+static const char* const kClassPathName = "android/media/audiofx/StreamDefaultEffect";
+
+static jint android_media_StreamDefaultEffect_native_setup(JNIEnv *env,
+                                                           jobject /*thiz*/,
+                                                           jstring type,
+                                                           jstring uuid,
+                                                           jint priority,
+                                                           jint streamUsage,
+                                                           jstring opPackageName,
+                                                           jintArray jId)
+{
+    ALOGV("android_media_StreamDefaultEffect_native_setup");
+    status_t lStatus = NO_ERROR;
+    jint* nId = NULL;
+    const char *typeStr = NULL;
+    const char *uuidStr = NULL;
+
+    ScopedUtfChars opPackageNameStr(env, opPackageName);
+
+    if (type != NULL) {
+        typeStr = env->GetStringUTFChars(type, NULL);
+        if (typeStr == NULL) {  // Out of memory
+            lStatus = NO_MEMORY;
+            jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
+            goto setup_exit;
+        }
+    }
+
+    if (uuid != NULL) {
+        uuidStr = env->GetStringUTFChars(uuid, NULL);
+        if (uuidStr == NULL) {  // Out of memory
+            lStatus = NO_MEMORY;
+            jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
+            goto setup_exit;
+        }
+    }
+
+    if (typeStr == NULL && uuidStr == NULL) {
+        lStatus = BAD_VALUE;
+        goto setup_exit;
+    }
+
+    nId = reinterpret_cast<jint *>(env->GetPrimitiveArrayCritical(jId, NULL));
+    if (nId == NULL) {
+        ALOGE("setup: Error retrieving id pointer");
+        lStatus = BAD_VALUE;
+        goto setup_exit;
+    }
+
+    // create the native StreamDefaultEffect.
+    audio_unique_id_t id;
+    lStatus = AudioEffect::addStreamDefaultEffect(typeStr,
+                                                  String16(opPackageNameStr.c_str()),
+                                                  uuidStr,
+                                                  priority,
+                                                  static_cast<audio_usage_t>(streamUsage),
+                                                  &id);
+    if (lStatus != NO_ERROR) {
+        ALOGE("setup: Error adding StreamDefaultEffect");
+        goto setup_exit;
+    }
+
+    nId[0] = static_cast<jint>(id);
+
+setup_exit:
+    // Final cleanup and return.
+
+    if (nId != NULL) {
+        env->ReleasePrimitiveArrayCritical(jId, nId, 0);
+        nId = NULL;
+    }
+
+    if (uuidStr != NULL) {
+        env->ReleaseStringUTFChars(uuid, uuidStr);
+        uuidStr = NULL;
+    }
+
+    if (typeStr != NULL) {
+        env->ReleaseStringUTFChars(type, typeStr);
+        typeStr = NULL;
+    }
+
+    return AudioEffectJni::translateNativeErrorToJava(lStatus);
+}
+
+static void android_media_StreamDefaultEffect_native_release(JNIEnv */*env*/,
+                                                             jobject /*thiz*/,
+                                                             jint id) {
+    status_t lStatus = AudioEffect::removeStreamDefaultEffect(id);
+    if (lStatus != NO_ERROR) {
+        ALOGW("Error releasing StreamDefaultEffect: %d", lStatus);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+// Dalvik VM type signatures
+static const JNINativeMethod gMethods[] = {
+    {"native_setup",         "(Ljava/lang/String;Ljava/lang/String;IILjava/lang/String;[I)I",
+                                         (void *)android_media_StreamDefaultEffect_native_setup},
+    {"native_release",       "(I)V",      (void *)android_media_StreamDefaultEffect_native_release},
+};
+
+
+// ----------------------------------------------------------------------------
+
+int register_android_media_StreamDefaultEffect(JNIEnv *env)
+{
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
diff --git a/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java b/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java
index 5f6d45c..7c90b27 100644
--- a/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java
+++ b/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java
@@ -17,6 +17,7 @@
 
 package android.filterfw;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.filterfw.core.AsyncRunner;
 import android.filterfw.core.FilterGraph;
@@ -83,6 +84,7 @@
     /**
      * Create a new GraphEnvironment with default components.
      */
+    @UnsupportedAppUsage
     public GraphEnvironment() {
         super(null);
     }
@@ -128,6 +130,7 @@
      * @param resourceId    The ID of the graph resource to load.
      * @return              A unique ID for the graph.
      */
+    @UnsupportedAppUsage
     public int loadGraph(Context context, int resourceId) {
         // Read the file into a graph
         FilterGraph graph = null;
@@ -180,6 +183,7 @@
                             MODE_SYNCHRONOUS or MODE_ASYNCHRONOUS.
      * @return              A GraphRunner instance for this graph.
      */
+    @UnsupportedAppUsage
     public GraphRunner getRunner(int graphId, int executionMode) {
         switch (executionMode) {
             case MODE_ASYNCHRONOUS:
diff --git a/media/mca/filterfw/java/android/filterfw/core/Filter.java b/media/mca/filterfw/java/android/filterfw/core/Filter.java
index 062b6ba..4f56b92 100644
--- a/media/mca/filterfw/java/android/filterfw/core/Filter.java
+++ b/media/mca/filterfw/java/android/filterfw/core/Filter.java
@@ -17,6 +17,7 @@
 
 package android.filterfw.core;
 
+import android.annotation.UnsupportedAppUsage;
 import android.filterfw.core.FilterContext;
 import android.filterfw.core.FilterPort;
 import android.filterfw.core.KeyValueMap;
@@ -69,6 +70,7 @@
     private boolean mLogVerbose;
     private static final String TAG = "Filter";
 
+    @UnsupportedAppUsage
     public Filter(String name) {
         mName = name;
         mFramesToRelease = new HashSet<Frame>();
@@ -81,6 +83,7 @@
     /** Tests to see if a given filter is installed on the system. Requires
      * full filter package name, including filterpack.
      */
+    @UnsupportedAppUsage
     public static final boolean isAvailable(String filterName) {
         ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
         Class filterClass;
@@ -150,6 +153,7 @@
         port.setFrame(frame);
     }
 
+    @UnsupportedAppUsage
     public final void setInputValue(String inputName, Object value) {
         setInputFrame(inputName, wrapInputValue(inputName, value));
     }
diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterContext.java b/media/mca/filterfw/java/android/filterfw/core/FilterContext.java
index 3c79d1b..a19220e 100644
--- a/media/mca/filterfw/java/android/filterfw/core/FilterContext.java
+++ b/media/mca/filterfw/java/android/filterfw/core/FilterContext.java
@@ -17,6 +17,7 @@
 
 package android.filterfw.core;
 
+import android.annotation.UnsupportedAppUsage;
 import android.filterfw.core.Filter;
 import android.filterfw.core.Frame;
 import android.filterfw.core.FrameManager;
@@ -36,6 +37,7 @@
     private HashMap<String, Frame> mStoredFrames = new HashMap<String, Frame>();
     private Set<FilterGraph> mGraphs = new HashSet<FilterGraph>();
 
+    @UnsupportedAppUsage
     public FrameManager getFrameManager() {
         return mFrameManager;
     }
@@ -52,6 +54,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public GLEnvironment getGLEnvironment() {
         return mGLEnvironment;
     }
diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java b/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java
index 12f7892..e6ca11f 100644
--- a/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java
+++ b/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java
@@ -30,6 +30,7 @@
 import android.filterpacks.base.FrameBranch;
 import android.filterpacks.base.NullFilter;
 
+import android.annotation.UnsupportedAppUsage;
 import android.util.Log;
 
 /**
@@ -75,6 +76,7 @@
         return mFilters.contains(filter);
     }
 
+    @UnsupportedAppUsage
     public Filter getFilter(String name) {
         return mNameMap.get(name);
     }
@@ -160,6 +162,7 @@
         mTypeCheckMode = typeCheckMode;
     }
 
+    @UnsupportedAppUsage
     public void tearDown(FilterContext context) {
         if (!mFilters.isEmpty()) {
             flushFrames();
diff --git a/media/mca/filterfw/java/android/filterfw/core/Frame.java b/media/mca/filterfw/java/android/filterfw/core/Frame.java
index 7dd0783..e880783 100644
--- a/media/mca/filterfw/java/android/filterfw/core/Frame.java
+++ b/media/mca/filterfw/java/android/filterfw/core/Frame.java
@@ -17,6 +17,7 @@
 
 package android.filterfw.core;
 
+import android.annotation.UnsupportedAppUsage;
 import android.filterfw.core.FrameFormat;
 import android.filterfw.core.FrameManager;
 import android.graphics.Bitmap;
@@ -54,6 +55,7 @@
         mBindingId = bindingId;
     }
 
+    @UnsupportedAppUsage
     public FrameFormat getFormat() {
         return mFormat;
     }
@@ -94,6 +96,7 @@
 
     public abstract Object getObjectValue();
 
+    @UnsupportedAppUsage
     public abstract void setInts(int[] ints);
 
     public abstract int[] getInts();
@@ -116,12 +119,15 @@
 
     public abstract void setBitmap(Bitmap bitmap);
 
+    @UnsupportedAppUsage
     public abstract Bitmap getBitmap();
 
+    @UnsupportedAppUsage
     public void setTimestamp(long timestamp) {
         mTimestamp = timestamp;
     }
 
+    @UnsupportedAppUsage
     public long getTimestamp() {
         return mTimestamp;
     }
@@ -138,6 +144,7 @@
         return mRefCount;
     }
 
+    @UnsupportedAppUsage
     public Frame release() {
         if (mFrameManager != null) {
             return mFrameManager.releaseFrame(this);
diff --git a/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java b/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java
index 8f619be..eb0ff0a 100644
--- a/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java
+++ b/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java
@@ -17,6 +17,7 @@
 
 package android.filterfw.core;
 
+import android.annotation.UnsupportedAppUsage;
 import android.filterfw.core.KeyValueMap;
 import android.filterfw.core.MutableFrameFormat;
 
@@ -90,6 +91,7 @@
         return mBytesPerSample / bytesPerSampleOf(mBaseType);
     }
 
+    @UnsupportedAppUsage
     public int getTarget() {
         return mTarget;
     }
@@ -135,10 +137,12 @@
         return (mDimensions != null && mDimensions.length >= 1) ? mDimensions[0] : -1;
     }
 
+    @UnsupportedAppUsage
     public int getWidth() {
         return getLength();
     }
 
+    @UnsupportedAppUsage
     public int getHeight() {
         return (mDimensions != null && mDimensions.length >= 2) ? mDimensions[1] : -1;
     }
@@ -156,6 +160,7 @@
         return mObjectClass;
     }
 
+    @UnsupportedAppUsage
     public MutableFrameFormat mutableCopy() {
         MutableFrameFormat result = new MutableFrameFormat();
         result.setBaseType(getBaseType());
diff --git a/media/mca/filterfw/java/android/filterfw/core/FrameManager.java b/media/mca/filterfw/java/android/filterfw/core/FrameManager.java
index 8d6c483..85c8fcd 100644
--- a/media/mca/filterfw/java/android/filterfw/core/FrameManager.java
+++ b/media/mca/filterfw/java/android/filterfw/core/FrameManager.java
@@ -17,6 +17,7 @@
 
 package android.filterfw.core;
 
+import android.annotation.UnsupportedAppUsage;
 import android.filterfw.core.Frame;
 import android.filterfw.core.FrameFormat;
 import android.filterfw.core.MutableFrameFormat;
@@ -28,10 +29,13 @@
 
     private FilterContext mContext;
 
+    @UnsupportedAppUsage
     public abstract Frame newFrame(FrameFormat format);
 
+    @UnsupportedAppUsage
     public abstract Frame newBoundFrame(FrameFormat format, int bindingType, long bindingId);
 
+    @UnsupportedAppUsage
     public Frame duplicateFrame(Frame frame) {
         Frame result = newFrame(frame.getFormat());
         result.setDataFromFrame(frame);
diff --git a/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java b/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java
index 19d564c..e25d6a7 100644
--- a/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java
+++ b/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java
@@ -17,6 +17,7 @@
 
 package android.filterfw.core;
 
+import android.annotation.UnsupportedAppUsage;
 import android.filterfw.core.NativeAllocatorTag;
 import android.graphics.SurfaceTexture;
 import android.os.Looper;
@@ -66,6 +67,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public boolean isActive() {
         return nativeIsActive();
     }
@@ -78,6 +80,7 @@
         return nativeIsAnyContextActive();
     }
 
+    @UnsupportedAppUsage
     public void activate() {
         if (Looper.myLooper() != null && Looper.myLooper().equals(Looper.getMainLooper())) {
             Log.e("FilterFramework", "Activating GL context in UI thread!");
@@ -87,12 +90,14 @@
         }
     }
 
+    @UnsupportedAppUsage
     public void deactivate() {
         if (mManageContext && !nativeDeactivate()) {
             throw new RuntimeException("Could not deactivate GLEnvironment!");
         }
     }
 
+    @UnsupportedAppUsage
     public void swapBuffers() {
         if (!nativeSwapBuffers()) {
             throw new RuntimeException("Error swapping EGL buffers!");
@@ -117,6 +122,7 @@
         return result;
     }
 
+    @UnsupportedAppUsage
     public int registerSurfaceFromMediaRecorder(MediaRecorder mediaRecorder) {
         int result = nativeAddSurfaceFromMediaRecorder(mediaRecorder);
         if (result < 0) {
@@ -126,18 +132,21 @@
         return result;
     }
 
+    @UnsupportedAppUsage
     public void activateSurfaceWithId(int surfaceId) {
         if (!nativeActivateSurfaceId(surfaceId)) {
             throw new RuntimeException("Could not activate surface " + surfaceId + "!");
         }
     }
 
+    @UnsupportedAppUsage
     public void unregisterSurfaceId(int surfaceId) {
         if (!nativeRemoveSurfaceId(surfaceId)) {
             throw new RuntimeException("Could not unregister surface " + surfaceId + "!");
         }
     }
 
+    @UnsupportedAppUsage
     public void setSurfaceTimestamp(long timestamp) {
         if (!nativeSetSurfaceTimestamp(timestamp)) {
             throw new RuntimeException("Could not set timestamp for current surface!");
diff --git a/media/mca/filterfw/java/android/filterfw/core/GLFrame.java b/media/mca/filterfw/java/android/filterfw/core/GLFrame.java
index 1558cc6..9e3025f 100644
--- a/media/mca/filterfw/java/android/filterfw/core/GLFrame.java
+++ b/media/mca/filterfw/java/android/filterfw/core/GLFrame.java
@@ -17,6 +17,7 @@
 
 package android.filterfw.core;
 
+import android.annotation.UnsupportedAppUsage;
 import android.filterfw.core.Frame;
 import android.filterfw.core.FrameFormat;
 import android.filterfw.core.FrameManager;
@@ -223,6 +224,7 @@
     }
 
     @Override
+    @UnsupportedAppUsage
     public void setBitmap(Bitmap bitmap) {
         assertFrameMutable();
         assertGLEnvValid();
@@ -283,6 +285,7 @@
         setNativeViewport(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
     }
 
+    @UnsupportedAppUsage
     public void generateMipMap() {
         assertFrameMutable();
         assertGLEnvValid();
@@ -291,6 +294,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public void setTextureParameter(int param, int value) {
         assertFrameMutable();
         assertGLEnvValid();
@@ -300,6 +304,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public int getTextureId() {
         return getNativeTextureId();
     }
diff --git a/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java b/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java
index b496c54..250cfaa 100644
--- a/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java
+++ b/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java
@@ -17,6 +17,8 @@
 
 package android.filterfw.core;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * @hide
  */
@@ -50,6 +52,7 @@
         mFilterContext = context;
     }
 
+    @UnsupportedAppUsage
     public abstract FilterGraph getGraph();
 
     public FilterContext getContext() {
@@ -81,12 +84,15 @@
     }
 
     /** Starts running the graph. Will open the filters in the graph if they are not already open. */
+    @UnsupportedAppUsage
     public abstract void run();
 
+    @UnsupportedAppUsage
     public abstract void setDoneCallback(OnRunnerDoneListener listener);
     public abstract boolean isRunning();
 
     /** Stops graph execution. As part of stopping, also closes the graph nodes. */
+    @UnsupportedAppUsage
     public abstract void stop();
 
     /** Closes the filters in a graph. Can only be called if the graph is not running. */
@@ -96,5 +102,6 @@
      * Returns the last exception that happened during an asynchronous run. Returns null if
      * there is nothing to report.
      */
+    @UnsupportedAppUsage
     public abstract Exception getError();
 }
diff --git a/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java b/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java
index 8c78975..ae2ad99 100644
--- a/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java
+++ b/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java
@@ -17,6 +17,7 @@
 
 package android.filterfw.core;
 
+import android.annotation.UnsupportedAppUsage;
 import android.filterfw.core.FrameFormat;
 import android.filterfw.core.KeyValueMap;
 
@@ -31,6 +32,7 @@
         super();
     }
 
+    @UnsupportedAppUsage
     public MutableFrameFormat(int baseType, int target) {
         super(baseType, target);
     }
@@ -44,6 +46,7 @@
         mTarget = target;
     }
 
+    @UnsupportedAppUsage
     public void setBytesPerSample(int bytesPerSample) {
         mBytesPerSample = bytesPerSample;
         mSize = SIZE_UNKNOWN;
@@ -61,6 +64,7 @@
         mSize = SIZE_UNKNOWN;
     }
 
+    @UnsupportedAppUsage
     public void setDimensions(int width, int height) {
         int[] dimensions = new int[2];
         dimensions[0] = width;
diff --git a/media/mca/filterfw/java/android/filterfw/core/Program.java b/media/mca/filterfw/java/android/filterfw/core/Program.java
index 1930648..376c085 100644
--- a/media/mca/filterfw/java/android/filterfw/core/Program.java
+++ b/media/mca/filterfw/java/android/filterfw/core/Program.java
@@ -17,6 +17,7 @@
 
 package android.filterfw.core;
 
+import android.annotation.UnsupportedAppUsage;
 import android.filterfw.core.Frame;
 
 /**
@@ -24,14 +25,17 @@
  */
 public abstract class Program {
 
+    @UnsupportedAppUsage
     public abstract void process(Frame[] inputs, Frame output);
 
+    @UnsupportedAppUsage
     public void process(Frame input, Frame output) {
         Frame[] inputs = new Frame[1];
         inputs[0] = input;
         process(inputs, output);
     }
 
+    @UnsupportedAppUsage
     public abstract void setHostValue(String variableName, Object value);
 
     public abstract Object getHostValue(String variableName);
diff --git a/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java b/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java
index a971cb6..f41636e 100644
--- a/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java
+++ b/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java
@@ -17,6 +17,7 @@
 
 package android.filterfw.core;
 
+import android.annotation.UnsupportedAppUsage;
 import android.filterfw.core.Frame;
 import android.filterfw.core.NativeAllocatorTag;
 import android.filterfw.core.Program;
@@ -51,6 +52,7 @@
     private ShaderProgram(NativeAllocatorTag tag) {
     }
 
+    @UnsupportedAppUsage
     public ShaderProgram(FilterContext context, String fragmentShader) {
         mGLEnvironment = getGLEnvironment(context);
         allocate(mGLEnvironment, null, fragmentShader);
@@ -69,6 +71,7 @@
         this.setTimer();
     }
 
+    @UnsupportedAppUsage
     public static ShaderProgram createIdentity(FilterContext context) {
         ShaderProgram program = nativeCreateIdentity(getGLEnvironment(context));
         program.setTimer();
@@ -85,6 +88,7 @@
     }
 
     @Override
+    @UnsupportedAppUsage
     public void process(Frame[] inputs, Frame output) {
         if (mTimer.LOG_MFF_RUNNING_TIMES) {
           mTimer.start("glFinish");
@@ -129,6 +133,7 @@
     }
 
     @Override
+    @UnsupportedAppUsage
     public void setHostValue(String variableName, Object value) {
         if (!setUniformValue(variableName, value)) {
             throw new RuntimeException("Error setting uniform value for variable '" +
@@ -167,6 +172,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public void setSourceRegion(Quad region) {
         setSourceRegion(region.p0.x, region.p0.y,
                         region.p1.x, region.p1.y,
@@ -181,6 +187,7 @@
                         region.p3.x, region.p3.y);
     }
 
+    @UnsupportedAppUsage
     public void setSourceRect(float x, float y, float width, float height) {
         setSourceRegion(x, y, x + width, y, x, y + height, x + width, y + height);
     }
@@ -225,6 +232,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public void setMaximumTileSize(int size) {
         mMaxTileSize = size;
     }
diff --git a/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java b/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java
index d57f47c..ac08730 100644
--- a/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java
+++ b/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java
@@ -17,6 +17,7 @@
 
 package android.filterfw.format;
 
+import android.annotation.UnsupportedAppUsage;
 import android.filterfw.core.FrameFormat;
 import android.filterfw.core.MutableFrameFormat;
 import android.graphics.Bitmap;
@@ -48,6 +49,7 @@
         return result;
     }
 
+    @UnsupportedAppUsage
     public static MutableFrameFormat create(int width,
                                             int height,
                                             int colorspace,
@@ -59,6 +61,7 @@
                       target);
     }
 
+    @UnsupportedAppUsage
     public static MutableFrameFormat create(int colorspace, int target) {
         return create(FrameFormat.SIZE_UNSPECIFIED,
                       FrameFormat.SIZE_UNSPECIFIED,
@@ -67,6 +70,7 @@
                       target);
     }
 
+    @UnsupportedAppUsage
     public static MutableFrameFormat create(int colorspace) {
         return create(FrameFormat.SIZE_UNSPECIFIED,
                       FrameFormat.SIZE_UNSPECIFIED,
diff --git a/media/mca/filterfw/java/android/filterfw/geometry/Point.java b/media/mca/filterfw/java/android/filterfw/geometry/Point.java
index 4682a0d..d7acf12 100644
--- a/media/mca/filterfw/java/android/filterfw/geometry/Point.java
+++ b/media/mca/filterfw/java/android/filterfw/geometry/Point.java
@@ -17,6 +17,7 @@
 
 package android.filterfw.geometry;
 
+import android.annotation.UnsupportedAppUsage;
 import java.lang.Math;
 
 /**
@@ -24,12 +25,16 @@
  */
 public class Point {
 
+    @UnsupportedAppUsage
     public float x;
+    @UnsupportedAppUsage
     public float y;
 
+    @UnsupportedAppUsage
     public Point() {
     }
 
+    @UnsupportedAppUsage
     public Point(float x, float y) {
         this.x = x;
         this.y = y;
diff --git a/media/mca/filterfw/java/android/filterfw/geometry/Quad.java b/media/mca/filterfw/java/android/filterfw/geometry/Quad.java
index ee092fd..610e5b8 100644
--- a/media/mca/filterfw/java/android/filterfw/geometry/Quad.java
+++ b/media/mca/filterfw/java/android/filterfw/geometry/Quad.java
@@ -17,6 +17,7 @@
 
 package android.filterfw.geometry;
 
+import android.annotation.UnsupportedAppUsage;
 import android.filterfw.geometry.Point;
 
 import java.lang.Float;
@@ -29,14 +30,20 @@
  */
 public class Quad {
 
+    @UnsupportedAppUsage
     public Point p0;
+    @UnsupportedAppUsage
     public Point p1;
+    @UnsupportedAppUsage
     public Point p2;
+    @UnsupportedAppUsage
     public Point p3;
 
+    @UnsupportedAppUsage
     public Quad() {
     }
 
+    @UnsupportedAppUsage
     public Quad(Point p0, Point p1, Point p2, Point p3) {
         this.p0 = p0;
         this.p1 = p1;
diff --git a/media/native/midi/include/midi.h b/media/native/midi/include/midi.h
index 780d8a7..755d09f 100644
--- a/media/native/midi/include/midi.h
+++ b/media/native/midi/include/midi.h
@@ -71,7 +71,7 @@
   *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - an unknown error occurred.
  */
 media_status_t AMIDI_API AMidiDevice_fromJava(
-        JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr);
+        JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr) __INTRODUCED_IN(29);
 
 /**
  * Disconnects the native Midi Device Object from the associated Java MidiDevice object.
@@ -88,7 +88,7 @@
  *  - the JNI interface initialization to the associated java MidiDevice failed.
  *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info.
  */
-media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice);
+media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice) __INTRODUCED_IN(29);
 
 /**
  * Gets the MIDI device type.
@@ -104,7 +104,7 @@
  *  parameter is NULL.
  *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown error.
  */
-int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device);
+int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) __INTRODUCED_IN(29);
 
 /**
  * Gets the number of input (sending) ports available on the specified MIDI device.
@@ -117,7 +117,7 @@
  *  parameter is NULL.
  *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info.
  */
-ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device);
+ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
 
 /**
  * Gets the number of output (receiving) ports available on the specified MIDI device.
@@ -130,7 +130,7 @@
  *  parameter is NULL.
  *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN}- couldn't retrieve the device info.
  */
-ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device);
+ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
 
 /*
  * API for receiving data from the Output port of a device.
@@ -149,14 +149,14 @@
  *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
  */
 media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
-                             AMidiOutputPort **outOutputPortPtr);
+                             AMidiOutputPort **outOutputPortPtr) __INTRODUCED_IN(29);
 
 /**
  * Closes the output port.
  *
  * @param outputPort    The native API port identifier of the port.
  */
-void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort);
+void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) __INTRODUCED_IN(29);
 
 /**
  * Receives the next pending MIDI message. To retrieve all pending messages, the client should
@@ -177,7 +177,7 @@
  *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
  */
 ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
-         uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *outTimestampPtr);
+         uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *outTimestampPtr) __INTRODUCED_IN(29);
 
 /*
  * API for sending data to the Input port of a device.
@@ -196,7 +196,7 @@
  *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
  */
 media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
-                            AMidiInputPort **outInputPortPtr);
+                            AMidiInputPort **outInputPortPtr) __INTRODUCED_IN(29);
 
 /**
  * Sends data to the specified input port.
@@ -210,7 +210,7 @@
  *   was NULL, the specified buffer was NULL.
  */
 ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
-                   size_t numBytes);
+                   size_t numBytes) __INTRODUCED_IN(29);
 
 /**
  * Sends data to the specified input port with a timestamp.
@@ -225,7 +225,7 @@
  *   was NULL, the specified buffer was NULL.
  */
 ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
-        const uint8_t *buffer, size_t numBytes, int64_t timestamp);
+        const uint8_t *buffer, size_t numBytes, int64_t timestamp) __INTRODUCED_IN(29);
 
 /**
  * Sends a message with a 'MIDI flush command code' to the specified port. This should cause
@@ -239,14 +239,14 @@
  * @see AMEDIA_ERROR_UNSUPPORTED {@link AMEDIA_ERROR_UNSUPPORTED} The FLUSH command couldn't
  * be sent.
  */
-media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort);
+media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
 
 /**
  * Closes the input port.
  *
  * @param inputPort Identifies the input (sending) port to close.
  */
-void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort);
+void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
 
 #ifdef __cplusplus
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
index b2be464..5ab5092 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
@@ -43,6 +43,8 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.MeteringRectangle;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.location.Location;
 import android.location.LocationManager;
@@ -75,6 +77,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Executor;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
@@ -694,6 +697,38 @@
     }
 
     /**
+     * Configure a new camera session with output surfaces and initial session parameters.
+     *
+     * @param camera The CameraDevice to be configured.
+     * @param outputSurfaces The surface list that used for camera output.
+     * @param listener The callback CameraDevice will notify when session is available.
+     * @param handler The handler used to notify callbacks.
+     * @param initialRequest Initial request settings to use as session parameters.
+     */
+    public static CameraCaptureSession configureCameraSessionWithParameters(CameraDevice camera,
+            List<Surface> outputSurfaces, BlockingSessionCallback listener,
+            Handler handler, CaptureRequest initialRequest) throws CameraAccessException {
+        List<OutputConfiguration> outConfigurations = new ArrayList<>(outputSurfaces.size());
+        for (Surface surface : outputSurfaces) {
+            outConfigurations.add(new OutputConfiguration(surface));
+        }
+        SessionConfiguration sessionConfig = new SessionConfiguration(
+                SessionConfiguration.SESSION_REGULAR, outConfigurations,
+                new HandlerExecutor(handler), listener);
+        sessionConfig.setSessionParameters(initialRequest);
+        camera.createCaptureSession(sessionConfig);
+
+        CameraCaptureSession session = listener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+        assertFalse("Camera session should not be a reprocessable session",
+                session.isReprocessable());
+        assertFalse("Capture session type must be regular",
+                CameraConstrainedHighSpeedCaptureSession.class.isAssignableFrom(
+                        session.getClass()));
+
+        return session;
+    }
+
+    /**
      * Configure a new camera session with output surfaces and type.
      *
      * @param camera The CameraDevice to be configured.
@@ -1334,6 +1369,20 @@
         }
     }
 
+    public static class HandlerExecutor implements Executor {
+        private final Handler mHandler;
+
+        public HandlerExecutor(Handler handler) {
+            assertNotNull("handler must be valid", handler);
+            mHandler = handler;
+        }
+
+        @Override
+        public void execute(Runnable runCmd) {
+            mHandler.post(runCmd);
+        }
+    }
+
     /**
      * Provide a mock for {@link CameraDevice.StateCallback}.
      *
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
index ddb05f0..8f27fef 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
@@ -58,6 +58,7 @@
 import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
 import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageReaderListener;
 import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSessionWithParameters;
 import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes;
 import static com.android.mediaframeworktest.helpers.CameraTestUtils.getValueNotNull;
 
@@ -872,7 +873,6 @@
             outputSurfaces.add(mReaderSurface);
         }
         mSessionListener = new BlockingSessionCallback();
-        mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
 
         CaptureRequest.Builder recordingRequestBuilder =
                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
@@ -885,7 +885,10 @@
         }
         recordingRequestBuilder.addTarget(mRecordingSurface);
         recordingRequestBuilder.addTarget(mPreviewSurface);
-        mSession.setRepeatingRequest(recordingRequestBuilder.build(), listener, mHandler);
+        CaptureRequest recordingRequest = recordingRequestBuilder.build();
+        mSession = configureCameraSessionWithParameters(mCamera, outputSurfaces, mSessionListener,
+                mHandler, recordingRequest);
+        mSession.setRepeatingRequest(recordingRequest, listener, mHandler);
 
         if (useMediaRecorder) {
             mMediaRecorder.start();
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java
index f6cd990..2cb5704 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java
@@ -41,6 +41,7 @@
     private Image mImage3;
 
     @Override
+    @SuppressWarnings("CheckReturnValue")
     protected void setUp() throws Exception {
         super.setUp();
 
diff --git a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
index b05242d..d7833cc 100644
--- a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
+++ b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
@@ -37,6 +37,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.UUID;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
@@ -149,9 +150,9 @@
     public void testMtpObjectGetNameNonRoot() {
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.findFirst().get().getName(), newFile.getName());
+        Assert.assertEquals(stream.get(0).getName(), newFile.getName());
     }
 
     @Test
@@ -167,9 +168,9 @@
     public void testMtpObjectGetIdNonRoot() {
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.findFirst().get().getId(), 1);
+        Assert.assertEquals(stream.get(0).getId(), 1);
     }
 
     @Test
@@ -185,9 +186,9 @@
     public void testMtpObjectIsDirFalse() {
         logMethodName();
         File newFile = createNewFile(mainStorageDir, "TEST123.mp3");
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertFalse(stream.findFirst().get().isDir());
+        Assert.assertFalse(stream.get(0).isDir());
     }
 
     @Test
@@ -195,9 +196,9 @@
     public void testMtpObjectGetFormatDir() {
         logMethodName();
         File newFile = createNewDir(mainStorageDir);
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.findFirst().get().getFormat(), MtpConstants.FORMAT_ASSOCIATION);
+        Assert.assertEquals(stream.get(0).getFormat(), MtpConstants.FORMAT_ASSOCIATION);
     }
 
     @Test
@@ -205,9 +206,9 @@
     public void testMtpObjectGetFormatNonDir() {
         logMethodName();
         File newFile = createNewFile(mainStorageDir, "TEST123.mp3");
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.findFirst().get().getFormat(), MtpConstants.FORMAT_MP3);
+        Assert.assertEquals(stream.get(0).getFormat(), MtpConstants.FORMAT_MP3);
     }
 
     @Test
@@ -215,9 +216,9 @@
     public void testMtpObjectGetStorageId() {
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.findFirst().get().getStorageId(), mainMtpStorage.getStorageId());
+        Assert.assertEquals(stream.get(0).getStorageId(), mainMtpStorage.getStorageId());
     }
 
     @Test
@@ -225,10 +226,9 @@
     public void testMtpObjectGetLastModified() {
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.findFirst().get().getModifiedTime(),
-                newFile.lastModified() / 1000);
+        Assert.assertEquals(stream.get(0).getModifiedTime(), newFile.lastModified() / 1000);
     }
 
     @Test
@@ -236,9 +236,9 @@
     public void testMtpObjectGetParent() {
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.findFirst().get().getParent(),
+        Assert.assertEquals(stream.get(0).getParent(),
                 manager.getStorageRoot(mainMtpStorage.getStorageId()));
     }
 
@@ -247,9 +247,9 @@
     public void testMtpObjectGetRoot() {
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.findFirst().get().getRoot(),
+        Assert.assertEquals(stream.get(0).getRoot(),
                 manager.getStorageRoot(mainMtpStorage.getStorageId()));
     }
 
@@ -258,9 +258,9 @@
     public void testMtpObjectGetPath() {
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.findFirst().get().getPath().toString(), newFile.getPath());
+        Assert.assertEquals(stream.get(0).getPath().toString(), newFile.getPath());
     }
 
     @Test
@@ -273,9 +273,9 @@
         } catch (IOException e) {
             Assert.fail();
         }
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.findFirst().get().getSize(), 8);
+        Assert.assertEquals(stream.get(0).getSize(), 8);
     }
 
     @Test
@@ -283,9 +283,9 @@
     public void testMtpObjectGetSizeDir() {
         logMethodName();
         File newDir = createNewDir(mainStorageDir);
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.findFirst().get().getSize(), 0);
+        Assert.assertEquals(stream.get(0).getSize(), 0);
     }
 
     /** MtpStorageManager cache access tests. **/
@@ -304,9 +304,9 @@
     public void testRemoveMtpStorage() {
         logMethodName();
         File newFile = createNewFile(secondaryStorageDir);
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 secondaryMtpStorage.getStorageId());
-        Assert.assertEquals(stream.count(), 1);
+        Assert.assertEquals(stream.size(), 1);
 
         manager.removeMtpStorage(secondaryMtpStorage);
         Assert.assertNull(manager.getStorageRoot(secondaryMtpStorage.getStorageId()));
@@ -377,9 +377,9 @@
         MtpStorageManager.MtpObject parent = manager.getByPath(newDir.getPath());
         Assert.assertNotNull(parent);
 
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(parent.getId(), 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(parent.getId(), 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.count(), 2);
+        Assert.assertEquals(stream.size(), 2);
         Assert.assertTrue(manager.checkConsistency());
     }
 
@@ -393,9 +393,9 @@
         MtpStorageManager.MtpObject parent = manager.getByPath(newDir.getPath());
         Assert.assertNotNull(parent);
 
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(parent.getId(),
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(parent.getId(),
                 MtpConstants.FORMAT_MP3, mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.findFirst().get().getPath().toString(), newMP3File.toString());
+        Assert.assertEquals(stream.get(0).getPath().toString(), newMP3File.toString());
         Assert.assertTrue(manager.checkConsistency());
     }
 
@@ -407,9 +407,9 @@
         File newFile = createNewFile(mainStorageDir);
         File newMP3File = createNewFile(newDir, "lalala.mp3");
 
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.count(), 2);
+        Assert.assertEquals(stream.size(), 2);
         Assert.assertTrue(manager.checkConsistency());
     }
 
@@ -421,9 +421,9 @@
         File newFile = createNewFile(mainStorageDir);
         File newMP3File = createNewFile(newDir, "lalala.mp3");
 
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.count(), 3);
+        Assert.assertEquals(stream.size(), 3);
         Assert.assertTrue(manager.checkConsistency());
     }
 
@@ -438,8 +438,8 @@
         createNewFile(secondaryStorageDir);
         createNewFile(newDir2, "lalala.mp3");
 
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0, 0, 0xFFFFFFFF);
-        Assert.assertEquals(stream.count(), 6);
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0, 0, 0xFFFFFFFF);
+        Assert.assertEquals(stream.size(), 6);
         Assert.assertTrue(manager.checkConsistency());
     }
 
@@ -454,8 +454,8 @@
         createNewFile(secondaryStorageDir);
         createNewFile(newDir2, "lalala.mp3");
 
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0, 0xFFFFFFFF);
-        Assert.assertEquals(stream.count(), 4);
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0, 0xFFFFFFFF);
+        Assert.assertEquals(stream.size(), 4);
         Assert.assertTrue(manager.checkConsistency());
     }
 
@@ -465,9 +465,9 @@
     @SmallTest
     public void testObjectAdded() {
         logMethodName();
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.count(), 0);
+        Assert.assertEquals(stream.size(), 0);
 
         File newFile = createNewFile(mainStorageDir);
         manager.flushEvents();
@@ -481,9 +481,9 @@
     @SmallTest
     public void testObjectAddedDir() {
         logMethodName();
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.count(), 0);
+        Assert.assertEquals(stream.size(), 0);
 
         File newDir = createNewDir(mainStorageDir);
         manager.flushEvents();
@@ -498,9 +498,9 @@
     @SmallTest
     public void testObjectAddedRecursiveDir() {
         logMethodName();
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.count(), 0);
+        Assert.assertEquals(stream.size(), 0);
 
         File newDir = createNewDir(createNewDir(createNewDir(mainStorageDir)));
         manager.flushEvents();
@@ -516,9 +516,9 @@
     public void testObjectRemoved() {
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.count(), 1);
+        Assert.assertEquals(stream.size(), 1);
 
         Assert.assertTrue(newFile.delete());
         manager.flushEvents();
@@ -532,9 +532,9 @@
     public void testObjectMoved() {
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
-        Assert.assertEquals(stream.count(), 1);
+        Assert.assertEquals(stream.size(), 1);
         File toFile = new File(mainStorageDir, "to" + newFile.getName());
 
         Assert.assertTrue(newFile.renameTo(toFile));
@@ -555,7 +555,7 @@
     @SmallTest
     public void testSendObjectSuccess() {
         logMethodName();
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
         int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
                 "newFile", MtpConstants.FORMAT_UNDEFINED);
@@ -574,7 +574,7 @@
     @SmallTest
     public void testSendObjectSuccessDir() {
         logMethodName();
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
         int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
                 "newDir", MtpConstants.FORMAT_ASSOCIATION);
@@ -601,7 +601,7 @@
     @SmallTest
     public void testSendObjectSuccessDelayed() {
         logMethodName();
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
         int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
                 "newFile", MtpConstants.FORMAT_UNDEFINED);
@@ -620,7 +620,7 @@
     @SmallTest
     public void testSendObjectSuccessDirDelayed() {
         logMethodName();
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
         int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
                 "newDir", MtpConstants.FORMAT_ASSOCIATION);
@@ -647,7 +647,7 @@
     @SmallTest
     public void testSendObjectSuccessDeleted() {
         logMethodName();
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
         int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
                 "newFile", MtpConstants.FORMAT_UNDEFINED);
@@ -667,7 +667,7 @@
     @SmallTest
     public void testSendObjectFailed() {
         logMethodName();
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
         int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
                 "newFile", MtpConstants.FORMAT_UNDEFINED);
@@ -682,7 +682,7 @@
     @SmallTest
     public void testSendObjectFailedDeleted() {
         logMethodName();
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
         int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
                 "newFile", MtpConstants.FORMAT_UNDEFINED);
@@ -702,7 +702,7 @@
     @SmallTest
     public void testSendObjectFailedAdded() {
         logMethodName();
-        Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+        List<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId());
         int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
                 "newFile", MtpConstants.FORMAT_UNDEFINED);
@@ -731,7 +731,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginRemoveObject(obj));
 
         Assert.assertTrue(newFile.delete());
@@ -748,7 +748,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginRemoveObject(obj));
 
         Assert.assertTrue(manager.endRemoveObject(obj, true));
@@ -766,7 +766,7 @@
         File newDir = createNewDir(mainStorageDir);
         createNewFile(createNewDir(newDir));
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         manager.getObjects(obj.getId(), 0, mainMtpStorage.getStorageId());
         Assert.assertTrue(manager.beginRemoveObject(obj));
 
@@ -776,7 +776,7 @@
         Assert.assertTrue(manager.endRemoveObject(obj, true));
         Assert.assertEquals(objectsAdded.size(), 1);
         Assert.assertEquals(objectsRemoved.size(), 1);
-        Assert.assertEquals(manager.getObjects(0, 0, mainMtpStorage.getStorageId()).count(), 0);
+        Assert.assertEquals(manager.getObjects(0, 0, mainMtpStorage.getStorageId()).size(), 0);
         Assert.assertNull(manager.getObject(obj.getId()));
         Assert.assertTrue(manager.checkConsistency());
     }
@@ -788,14 +788,14 @@
         File newDir = createNewDir(mainStorageDir);
         createNewFile(createNewDir(newDir));
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginRemoveObject(obj));
 
         Assert.assertTrue(manager.endRemoveObject(obj, true));
         Assert.assertTrue(FileUtils.deleteContentsAndDir(newDir));
         manager.flushEvents();
         Assert.assertEquals(objectsRemoved.size(), 0);
-        Assert.assertEquals(manager.getObjects(0, 0, mainMtpStorage.getStorageId()).count(), 0);
+        Assert.assertEquals(manager.getObjects(0, 0, mainMtpStorage.getStorageId()).size(), 0);
         Assert.assertNull(manager.getObject(obj.getId()));
         Assert.assertTrue(manager.checkConsistency());
     }
@@ -806,7 +806,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         int id = obj.getId();
         Assert.assertTrue(manager.beginRemoveObject(obj));
 
@@ -827,7 +827,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginRemoveObject(obj));
 
         Assert.assertTrue(manager.endRemoveObject(obj, false));
@@ -841,7 +841,7 @@
         logMethodName();
         File newDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         manager.getObjects(obj.getId(), 0, mainMtpStorage.getStorageId());
         Assert.assertTrue(manager.beginRemoveObject(obj));
 
@@ -859,7 +859,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginRemoveObject(obj));
 
         Assert.assertTrue(newFile.delete());
@@ -879,9 +879,9 @@
         File newDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
                 mainMtpStorage.getStorageId())
-                .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
+                .stream().filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
         MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> !o.isDir()).findFirst().get();
 
         int id = manager.beginCopyObject(fileObj, dirObj);
@@ -905,10 +905,10 @@
         File deletedFile = createNewFile(newDirFrom);
         File newDirTo = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject toObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> o.getName().equals(newDirTo.getName())).findFirst().get();
         MtpStorageManager.MtpObject fromObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> o.getName().equals(newDirFrom.getName())).findFirst().get();
 
         manager.getObjects(fromObj.getId(), 0, mainMtpStorage.getStorageId());
@@ -939,7 +939,7 @@
         Assert.assertEquals(objectsAdded.size(), 2);
 
         // Number of files/dirs created, minus the one that was deleted.
-        Assert.assertEquals(manager.getObjects(0, 0, mainMtpStorage.getStorageId()).count(), 13);
+        Assert.assertEquals(manager.getObjects(0, 0, mainMtpStorage.getStorageId()).size(), 13);
         Assert.assertTrue(manager.checkConsistency());
     }
 
@@ -950,10 +950,10 @@
         File newFile = createNewFile(mainStorageDir);
         File newDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
         MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> !o.isDir()).findFirst().get();
 
         int id = manager.beginCopyObject(fileObj, dirObj);
@@ -972,10 +972,10 @@
         File newFile = createNewFile(mainStorageDir);
         File newDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
         MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> !o.isDir()).findFirst().get();
 
         int id = manager.beginCopyObject(fileObj, dirObj);
@@ -1002,10 +1002,10 @@
         File newFile = createNewFile(mainStorageDir);
         File newDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
         MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> !o.isDir()).findFirst().get();
 
         int id = manager.beginCopyObject(fileObj, dirObj);
@@ -1024,7 +1024,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
 
         File renamed = new File(mainStorageDir, "renamed");
@@ -1045,7 +1045,7 @@
         logMethodName();
         File newDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
 
         File renamed = new File(mainStorageDir, "renamed");
@@ -1072,7 +1072,7 @@
         logMethodName();
         File newDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         manager.getObjects(obj.getId(), 0, mainMtpStorage.getStorageId());
         Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
 
@@ -1100,7 +1100,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
 
         Assert.assertTrue(manager.endRenameObject(obj, newFile.getName(), true));
@@ -1121,7 +1121,7 @@
         logMethodName();
         File newDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         manager.getObjects(obj.getId(), 0, mainMtpStorage.getStorageId());
         Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
 
@@ -1149,7 +1149,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
 
         Assert.assertTrue(manager.endRenameObject(obj, newFile.getName(), false));
@@ -1166,7 +1166,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
 
         Assert.assertTrue(newFile.delete());
@@ -1185,7 +1185,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
 
         createNewFile(mainStorageDir, "renamed");
@@ -1205,10 +1205,10 @@
         File newFile = createNewFile(mainStorageDir);
         File dir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
         MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> !o.isDir()).findFirst().get();
         Assert.assertTrue(manager.beginMoveObject(fileObj, dirObj));
 
@@ -1233,10 +1233,10 @@
         File newDir = createNewDir(mainStorageDir);
         File movedDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> o.getName().equals(newDir.getName())).findFirst().get();
         MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> o.getName().equals(movedDir.getName())).findFirst().get();
         Assert.assertTrue(manager.beginMoveObject(movedObj, dirObj));
 
@@ -1267,10 +1267,10 @@
         File newDir = createNewDir(mainStorageDir);
         File movedDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> o.getName().equals(newDir.getName())).findFirst().get();
         MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> o.getName().equals(movedDir.getName())).findFirst().get();
         manager.getObjects(movedObj.getId(), 0, mainMtpStorage.getStorageId());
         Assert.assertTrue(manager.beginMoveObject(movedObj, dirObj));
@@ -1302,10 +1302,10 @@
         File newFile = createNewFile(mainStorageDir);
         File dir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
         MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> !o.isDir()).findFirst().get();
         Assert.assertTrue(manager.beginMoveObject(fileObj, dirObj));
 
@@ -1331,10 +1331,10 @@
         File newDir = createNewDir(mainStorageDir);
         File movedDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> o.getName().equals(newDir.getName())).findFirst().get();
         MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> o.getName().equals(movedDir.getName())).findFirst().get();
         manager.getObjects(movedObj.getId(), 0, mainMtpStorage.getStorageId());
         Assert.assertTrue(manager.beginMoveObject(movedObj, dirObj));
@@ -1367,10 +1367,10 @@
         File newFile = createNewFile(mainStorageDir);
         File dir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
         MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> !o.isDir()).findFirst().get();
         Assert.assertTrue(manager.beginMoveObject(fileObj, dirObj));
 
@@ -1391,10 +1391,10 @@
         File newFile = createNewFile(mainStorageDir);
         File dir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
         MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> !o.isDir()).findFirst().get();
         Assert.assertTrue(manager.beginMoveObject(fileObj, dirObj));
 
@@ -1417,10 +1417,10 @@
         File newFile = createNewFile(mainStorageDir);
         File dir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
         MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId())
+                mainMtpStorage.getStorageId()).stream()
                 .filter(o -> !o.isDir()).findFirst().get();
         Assert.assertTrue(manager.beginMoveObject(fileObj, dirObj));
 
@@ -1442,7 +1442,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginMoveObject(fileObj,
                 manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
 
@@ -1468,7 +1468,7 @@
         logMethodName();
         File movedDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginMoveObject(movedObj,
                 manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
 
@@ -1500,7 +1500,7 @@
         logMethodName();
         File movedDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         manager.getObjects(movedObj.getId(), 0, mainMtpStorage.getStorageId());
         Assert.assertTrue(manager.beginMoveObject(movedObj,
                 manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
@@ -1533,7 +1533,7 @@
         logMethodName();
         File movedFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginMoveObject(movedObj,
                 manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
 
@@ -1560,7 +1560,7 @@
         logMethodName();
         File movedDir = createNewDir(mainStorageDir);
         MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         manager.getObjects(movedObj.getId(), 0, mainMtpStorage.getStorageId());
         Assert.assertTrue(manager.beginMoveObject(movedObj,
                 manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
@@ -1594,7 +1594,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginMoveObject(fileObj,
                 manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
 
@@ -1615,7 +1615,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginMoveObject(fileObj,
                 manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
 
@@ -1638,7 +1638,7 @@
         logMethodName();
         File newFile = createNewFile(mainStorageDir);
         MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
-                mainMtpStorage.getStorageId()).findFirst().get();
+                mainMtpStorage.getStorageId()).get(0);
         Assert.assertTrue(manager.beginMoveObject(fileObj,
                 manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
 
diff --git a/native/android/OWNERS b/native/android/OWNERS
index 11d4be4..067cdf8 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -1,11 +1,15 @@
 set noparent
 
+per-file libandroid_net.map.txt=codewiz@google.com
 per-file libandroid_net.map.txt=ek@google.com
 per-file libandroid_net.map.txt=jchalard@google.com
 per-file libandroid_net.map.txt=lorenzo@google.com
+per-file libandroid_net.map.txt=reminv@google.com
 per-file libandroid_net.map.txt=satk@google.com
 
+per-file net.c=codewiz@google.com
 per-file net.c=ek@google.com
 per-file net.c=jchalard@google.com
 per-file net.c=lorenzo@google.com
+per-file net.c=reminv@google.com
 per-file net.c=satk@google.com
diff --git a/packages/BackupRestoreConfirmation/res/values-pt-rBR/strings.xml b/packages/BackupRestoreConfirmation/res/values-pt-rBR/strings.xml
index baa4867..d7c56b8 100644
--- a/packages/BackupRestoreConfirmation/res/values-pt-rBR/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-pt-rBR/strings.xml
@@ -28,7 +28,7 @@
     <string name="device_encryption_restore_text" msgid="1570864916855208992">"Insira sua senha de criptografia do dispositivo abaixo."</string>
     <string name="device_encryption_backup_text" msgid="5866590762672844664">"Insira sua senha de criptografia do dispositivo abaixo. Ela também será usada para criptografar o arquivo de backup."</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"Digite uma senha para usar para criptografar os dados de backup por completo. Se isso for deixado em branco, sua senha atual de backup será usada:"</string>
-    <string name="backup_enc_password_optional" msgid="1350137345907579306">"Se você deseja criptografar os dados de backup por completo, digite uma senha abaixo:"</string>
+    <string name="backup_enc_password_optional" msgid="1350137345907579306">"Se você quer criptografar os dados de backup por completo, digite uma senha abaixo:"</string>
     <string name="backup_enc_password_required" msgid="7889652203371654149">"Como seu dispositivo está criptografado, é necessário criptografar seu backup. Insira uma senha abaixo:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"Se os dados restaurados forem criptografada, digite a senha abaixo:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"Iniciando backup..."</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-pt/strings.xml b/packages/BackupRestoreConfirmation/res/values-pt/strings.xml
index baa4867..d7c56b8 100644
--- a/packages/BackupRestoreConfirmation/res/values-pt/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-pt/strings.xml
@@ -28,7 +28,7 @@
     <string name="device_encryption_restore_text" msgid="1570864916855208992">"Insira sua senha de criptografia do dispositivo abaixo."</string>
     <string name="device_encryption_backup_text" msgid="5866590762672844664">"Insira sua senha de criptografia do dispositivo abaixo. Ela também será usada para criptografar o arquivo de backup."</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"Digite uma senha para usar para criptografar os dados de backup por completo. Se isso for deixado em branco, sua senha atual de backup será usada:"</string>
-    <string name="backup_enc_password_optional" msgid="1350137345907579306">"Se você deseja criptografar os dados de backup por completo, digite uma senha abaixo:"</string>
+    <string name="backup_enc_password_optional" msgid="1350137345907579306">"Se você quer criptografar os dados de backup por completo, digite uma senha abaixo:"</string>
     <string name="backup_enc_password_required" msgid="7889652203371654149">"Como seu dispositivo está criptografado, é necessário criptografar seu backup. Insira uma senha abaixo:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"Se os dados restaurados forem criptografada, digite a senha abaixo:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"Iniciando backup..."</string>
diff --git a/packages/CaptivePortalLogin/OWNERS b/packages/CaptivePortalLogin/OWNERS
index ce50558..7311eee 100644
--- a/packages/CaptivePortalLogin/OWNERS
+++ b/packages/CaptivePortalLogin/OWNERS
@@ -1,6 +1,8 @@
 set noparent
 
+codewiz@google.com
 ek@google.com
 jchalard@google.com
 lorenzo@google.com
+reminv@google.com
 satk@google.com
diff --git a/packages/CarSystemUI/Android.mk b/packages/CarSystemUI/Android.mk
new file mode 100644
index 0000000..0d40b7f
--- /dev/null
+++ b/packages/CarSystemUI/Android.mk
@@ -0,0 +1,86 @@
+# LOCAL_PATH is not the current directory of this file.
+# If you need the local path, use CAR_SYSUI_PATH.
+# This tweak ensures that the resource overlay that is device-specific still works
+# which requires that LOCAL_PATH match the original path (which must be frameworks/base/packages/SystemUI).
+#
+# For clarity, we also define SYSTEM_UI_AOSP_PATH to frameworks/base/packages/SystemUI and refer to that
+SYSTEM_UI_AOSP_PATH := frameworks/base/packages/SystemUI
+SYSTEM_UI_CAR_PATH := frameworks/base/packages/CarSystemUI
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_USE_AAPT2 := true
+
+LOCAL_MODULE_TAGS := optional
+
+# The same as SYSTEM_UI_AOSP_PATH but based on the value of LOCAL_PATH which is
+# frameworks/base/packages/CarSystemUI.
+RELATIVE_SYSTEM_UI_AOSP_PATH := ../../../../$(SYSTEM_UI_AOSP_PATH)
+
+
+LOCAL_SRC_FILES :=  \
+    $(call all-java-files-under, src) \
+    $(call all-Iaidl-files-under, src) \
+    $(call all-java-files-under, $(RELATIVE_SYSTEM_UI_AOSP_PATH)/src) \
+    $(call all-Iaidl-files-under, $(RELATIVE_SYSTEM_UI_AOSP_PATH)/src)
+
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+    SystemUIPluginLib \
+    SystemUISharedLib \
+    androidx.car_car \
+    androidx.legacy_legacy-support-v4 \
+    androidx.recyclerview_recyclerview \
+    androidx.preference_preference \
+    androidx.appcompat_appcompat \
+    androidx.mediarouter_mediarouter \
+    androidx.palette_palette \
+    androidx.legacy_legacy-preference-v14 \
+    androidx.leanback_leanback \
+    androidx.slice_slice-core \
+    androidx.slice_slice-view \
+    androidx.slice_slice-builders \
+    androidx.arch.core_core-runtime \
+    androidx.lifecycle_lifecycle-extensions \
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    SystemUI-tags \
+    SystemUI-proto
+
+LOCAL_JAVA_LIBRARIES := telephony-common \
+    android.car
+
+LOCAL_FULL_LIBS_MANIFEST_FILES := $(SYSTEM_UI_AOSP_PATH)/AndroidManifest.xml
+LOCAL_MANIFEST_FILE := AndroidManifest.xml
+
+LOCAL_MODULE_OWNER := google
+LOCAL_PACKAGE_NAME := CarSystemUI
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_PROGUARD_FLAG_FILES := $(RELATIVE_SYSTEM_UI_AOSP_PATH)/proguard.flags \
+    proguard.flags
+
+LOCAL_RESOURCE_DIR := \
+    $(LOCAL_PATH)/res \
+    $(SYSTEM_UI_AOSP_PATH)/res-keyguard \
+    $(SYSTEM_UI_AOSP_PATH)/res
+
+ifneq ($(INCREMENTAL_BUILDS),)
+    LOCAL_PROGUARD_ENABLED := disabled
+    LOCAL_JACK_ENABLED := incremental
+endif
+
+LOCAL_DX_FLAGS := --multi-dex
+LOCAL_JACK_FLAGS := --multi-dex native
+
+include frameworks/base/packages/SettingsLib/common.mk
+
+LOCAL_OVERRIDES_PACKAGES := SystemUI
+
+LOCAL_AAPT_FLAGS := --extra-packages com.android.keyguard
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under, $(SYSTEM_UI_CAR_PATH))
diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml
new file mode 100644
index 0000000..4e8a3a3
--- /dev/null
+++ b/packages/CarSystemUI/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?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"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        package="com.android.systemui"
+        android:sharedUserId="android.uid.systemui"
+        coreApp="true">
+
+
+</manifest>
diff --git a/packages/CarSystemUI/proguard.flags b/packages/CarSystemUI/proguard.flags
new file mode 100644
index 0000000..ceb037c
--- /dev/null
+++ b/packages/CarSystemUI/proguard.flags
@@ -0,0 +1 @@
+-keep class com.android.systemui.CarSystemUIFactory
diff --git a/packages/CarSystemUI/res/drawable/car_ic_apps.xml b/packages/CarSystemUI/res/drawable/car_ic_apps.xml
new file mode 100644
index 0000000..13b543b
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_apps.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="33dp"
+    android:height="33dp"
+    android:viewportHeight="33.0"
+    android:viewportWidth="33.0">
+    <path
+        android:fillColor="@color/car_nav_icon_fill_color"
+        android:fillType="evenOdd"
+        android:pathData="M1,20L12,20C12.55,20 13,20.45 13,21L13,32C13,32.55 12.55,33 12,33L1,33C0.45,33 0,32.55 0,32L0,21C0,20.45 0.45,20 1,20ZM21,20L32,20C32.55,20 33,20.45 33,21L33,32C33,32.55 32.55,33 32,33L21,33C20.45,33 20,32.55 20,32L20,21C20,20.45 20.45,20 21,20ZM1,0L12,0C12.55,0 13,0.45 13,1L13,12C13,12.55 12.55,13 12,13L1,13C0.45,13 0,12.55 0,12L0,1C0,0.45 0.45,0 1,0ZM21,0L32,0C32.55,0 33,0.45 33,1L33,12C33,12.55 32.55,13 32,13L21,13C20.45,13 20,12.55 20,12L20,1C20,0.45 20.45,0 21,0Z"
+        android:strokeColor="@color/car_nav_icon_fill_color"
+        android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_apps_black.xml b/packages/CarSystemUI/res/drawable/car_ic_apps_black.xml
new file mode 100644
index 0000000..498d366
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_apps_black.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="33dp"
+    android:height="33dp"
+    android:viewportHeight="33.0"
+    android:viewportWidth="33.0">
+    <path
+        android:fillColor="#FF000000"
+        android:fillType="evenOdd"
+        android:pathData="M1,20L12,20C12.55,20 13,20.45 13,21L13,32C13,32.55 12.55,33 12,33L1,33C0.45,33 0,32.55 0,32L0,21C0,20.45 0.45,20 1,20ZM21,20L32,20C32.55,20 33,20.45 33,21L33,32C33,32.55 32.55,33 32,33L21,33C20.45,33 20,32.55 20,32L20,21C20,20.45 20.45,20 21,20ZM1,0L12,0C12.55,0 13,0.45 13,1L13,12C13,12.55 12.55,13 12,13L1,13C0.45,13 0,12.55 0,12L0,1C0,0.45 0.45,0 1,0ZM21,0L32,0C32.55,0 33,0.45 33,1L33,12C33,12.55 32.55,13 32,13L21,13C20.45,13 20,12.55 20,12L20,1C20,0.45 20.45,0 21,0Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml
new file mode 100644
index 0000000..250bfe8
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml
@@ -0,0 +1,20 @@
+<?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
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/car_ic_selection_bg" android:gravity="center"/>
+    <item android:drawable="@drawable/car_ic_apps_black" android:gravity="center"/>
+</layer-list>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_music.xml b/packages/CarSystemUI/res/drawable/car_ic_music.xml
new file mode 100644
index 0000000..8c15494
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_music.xml
@@ -0,0 +1,34 @@
+<?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="34dp"
+    android:height="39dp"
+    android:viewportHeight="39.0"
+    android:viewportWidth="34.0">
+    <path
+        android:fillColor="@color/car_nav_icon_fill_color"
+        android:fillType="evenOdd"
+        android:pathData="M23,25L31,23L31,9L12,12L12,38C12,38.45 11.45,39 11,39L1,39C0.55,39 0.01,38.87 0,38.3C0,38.3 0,35.87 0,31C0,30.42 0.62,30.08 1,30L9,28L9,4.85C9,4.36 9.36,3.94 9.84,3.87L32.84,0.19C33.39,0.1 33.9,0.47 33.99,1.01C34,1.07 34,1.12 34,1.17L34,33.06C34,33.51 33.45,34 33,34L23,34C22.55,34 22,33.51 22,33.06L22,26C22,25.61 22.16,25.18 23,25Z"
+        android:strokeColor="@color/car_nav_icon_fill_color"
+        android:strokeWidth="1"/>
+    <path
+        android:fillColor="@color/car_nav_icon_fill_color"
+        android:fillType="evenOdd"
+        android:pathData="M23.57,26.48L23.57,32.42L32.42,32.42L32.42,24.27L23.57,26.48ZM32.42,7.18L32.42,1.85L10.57,5.34L10.57,10.63L32.42,7.18ZM10.42,29.27L9.38,29.53L1.58,31.48C1.58,34.35 1.58,34.45 1.57,36.48C1.58,36.89 1.58,37.2 1.58,37.43L10.42,37.43L10.42,29.27Z"
+        android:strokeColor="@color/car_nav_icon_fill_color"
+        android:strokeWidth="3.15"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_music_black.xml b/packages/CarSystemUI/res/drawable/car_ic_music_black.xml
new file mode 100644
index 0000000..64287a5
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_music_black.xml
@@ -0,0 +1,34 @@
+<?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="34dp"
+    android:height="39dp"
+    android:viewportHeight="39.0"
+    android:viewportWidth="34.0">
+    <path
+        android:fillColor="#FF000000"
+        android:fillType="evenOdd"
+        android:pathData="M23,25L31,23L31,9L12,12L12,38C12,38.45 11.45,39 11,39L1,39C0.55,39 0.01,38.87 0,38.3C0,38.3 0,35.87 0,31C0,30.42 0.62,30.08 1,30L9,28L9,4.85C9,4.36 9.36,3.94 9.84,3.87L32.84,0.19C33.39,0.1 33.9,0.47 33.99,1.01C34,1.07 34,1.12 34,1.17L34,33.06C34,33.51 33.45,34 33,34L23,34C22.55,34 22,33.51 22,33.06L22,26C22,25.61 22.16,25.18 23,25Z"
+        android:strokeColor="#FF000000"
+        android:strokeWidth="1"/>
+    <path
+        android:fillColor="#FF000000"
+        android:fillType="evenOdd"
+        android:pathData="M23.57,26.48L23.57,32.42L32.42,32.42L32.42,24.27L23.57,26.48ZM32.42,7.18L32.42,1.85L10.57,5.34L10.57,10.63L32.42,7.18ZM10.42,29.27L9.38,29.53L1.58,31.48C1.58,34.35 1.58,34.45 1.57,36.48C1.58,36.89 1.58,37.2 1.58,37.43L10.42,37.43L10.42,29.27Z"
+        android:strokeColor="#FF000000"
+        android:strokeWidth="3.15"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_music_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_music_selected.xml
new file mode 100644
index 0000000..9703a8c
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_music_selected.xml
@@ -0,0 +1,20 @@
+<?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
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/car_ic_selection_bg" android:gravity="center"/>
+    <item android:drawable="@drawable/car_ic_music_black" android:gravity="center"/>
+</layer-list>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_navigation.xml b/packages/CarSystemUI/res/drawable/car_ic_navigation.xml
new file mode 100644
index 0000000..58667be
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_navigation.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="32dp"
+    android:height="37dp"
+    android:viewportHeight="37.0"
+    android:viewportWidth="32.0">
+    <path
+        android:fillColor="@color/car_nav_icon_fill_color"
+        android:fillType="evenOdd"
+        android:pathData="M16.62,0.61L31.33,35.21C31.55,35.72 31.31,36.3 30.8,36.52C30.48,36.66 30.12,36.62 29.83,36.42L15.7,26.44L1.58,36.42C1.13,36.73 0.5,36.63 0.18,36.18C-0.02,35.89 -0.06,35.53 0.08,35.21L14.78,0.61C15,0.1 15.59,-0.14 16.1,0.08C16.33,0.18 16.52,0.37 16.62,0.61Z"
+        android:strokeColor="@color/car_nav_icon_fill_color"
+        android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_navigation_black.xml b/packages/CarSystemUI/res/drawable/car_ic_navigation_black.xml
new file mode 100644
index 0000000..145d4c9
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_navigation_black.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="32dp"
+    android:height="37dp"
+    android:viewportHeight="37.0"
+    android:viewportWidth="32.0">
+    <path
+        android:fillColor="#FF000000"
+        android:fillType="evenOdd"
+        android:pathData="M16.62,0.61L31.33,35.21C31.55,35.72 31.31,36.3 30.8,36.52C30.48,36.66 30.12,36.62 29.83,36.42L15.7,26.44L1.58,36.42C1.13,36.73 0.5,36.63 0.18,36.18C-0.02,35.89 -0.06,35.53 0.08,35.21L14.78,0.61C15,0.1 15.59,-0.14 16.1,0.08C16.33,0.18 16.52,0.37 16.62,0.61Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_navigation_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_navigation_selected.xml
new file mode 100644
index 0000000..f0174db
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_navigation_selected.xml
@@ -0,0 +1,21 @@
+<?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
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/car_ic_selection_bg" android:gravity="center"/>
+    <item android:drawable="@drawable/car_ic_navigation_black" android:gravity="center"/>
+</layer-list>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification.xml b/packages/CarSystemUI/res/drawable/car_ic_notification.xml
new file mode 100644
index 0000000..f96b050
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_notification.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="32dp"
+    android:height="38dp"
+    android:viewportHeight="38.0"
+    android:viewportWidth="32.0">
+    <path
+        android:fillColor="@color/car_nav_icon_fill_color"
+        android:fillType="evenOdd"
+        android:pathData="M20.62,3.98C25.51,5.85 28.98,10.61 28.98,16.18L28.98,24.95L32,24.95L32,31.22L0,31.22L0,24.95L3.02,24.95L3.02,16.18C3.02,10.61 6.49,5.85 11.38,3.98C11.72,1.73 13.66,0 16,0C18.34,0 20.28,1.73 20.62,3.98ZM11.33,33.3L20.67,33.3C20.67,35.9 18.58,38 16,38C13.42,38 11.33,35.9 11.33,33.3Z"
+        android:strokeColor="@color/car_nav_icon_fill_color"
+        android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification_black.xml b/packages/CarSystemUI/res/drawable/car_ic_notification_black.xml
new file mode 100644
index 0000000..5f8df88
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_notification_black.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="32dp"
+    android:height="38dp"
+    android:viewportHeight="38.0"
+    android:viewportWidth="32.0">
+    <path
+        android:fillColor="#000000"
+        android:fillType="evenOdd"
+        android:pathData="M20.62,3.98C25.51,5.85 28.98,10.61 28.98,16.18L28.98,24.95L32,24.95L32,31.22L0,31.22L0,24.95L3.02,24.95L3.02,16.18C3.02,10.61 6.49,5.85 11.38,3.98C11.72,1.73 13.66,0 16,0C18.34,0 20.28,1.73 20.62,3.98ZM11.33,33.3L20.67,33.3C20.67,35.9 18.58,38 16,38C13.42,38 11.33,35.9 11.33,33.3Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_notification_selected.xml
new file mode 100644
index 0000000..02eec93
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_notification_selected.xml
@@ -0,0 +1,21 @@
+<?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
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/car_ic_selection_bg" android:gravity="center"/>
+    <item android:drawable="@drawable/car_ic_notification_black" android:gravity="center"/>
+</layer-list>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/drawable/car_ic_overview.xml b/packages/CarSystemUI/res/drawable/car_ic_overview.xml
new file mode 100644
index 0000000..590109a
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_overview.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="39dp"
+    android:height="40dp"
+    android:viewportHeight="40.0"
+    android:viewportWidth="39.0">
+    <path
+        android:fillColor="@color/car_nav_icon_fill_color"
+        android:fillType="evenOdd"
+        android:pathData="M33.4,22.74L33.4,37.74C33.4,38.84 32.51,39.74 31.4,39.74L24.4,39.74L24.4,24.74L14.4,24.74L14.4,39.74L7.4,39.74C6.3,39.74 5.4,38.84 5.4,37.74L5.4,22.74L0.5,22.74C0.22,22.74 0,22.51 0,22.24C0,22.12 0.04,22 0.12,21.91L19.03,0.17C19.21,-0.04 19.52,-0.06 19.73,0.12C19.75,0.14 19.76,0.15 19.78,0.17L38.68,21.91C38.86,22.12 38.84,22.43 38.63,22.62C38.54,22.69 38.43,22.74 38.31,22.74L33.4,22.74Z"
+        android:strokeColor="@color/car_nav_icon_fill_color"
+        android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_overview_black.xml b/packages/CarSystemUI/res/drawable/car_ic_overview_black.xml
new file mode 100644
index 0000000..48cff97
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_overview_black.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="39dp"
+    android:height="40dp"
+    android:viewportHeight="40.0"
+    android:viewportWidth="39.0">
+    <path
+        android:fillColor="#FF000000"
+        android:fillType="evenOdd"
+        android:pathData="M33.4,22.74L33.4,37.74C33.4,38.84 32.51,39.74 31.4,39.74L24.4,39.74L24.4,24.74L14.4,24.74L14.4,39.74L7.4,39.74C6.3,39.74 5.4,38.84 5.4,37.74L5.4,22.74L0.5,22.74C0.22,22.74 0,22.51 0,22.24C0,22.12 0.04,22 0.12,21.91L19.03,0.17C19.21,-0.04 19.52,-0.06 19.73,0.12C19.75,0.14 19.76,0.15 19.78,0.17L38.68,21.91C38.86,22.12 38.84,22.43 38.63,22.62C38.54,22.69 38.43,22.74 38.31,22.74L33.4,22.74Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_overview_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_overview_selected.xml
new file mode 100644
index 0000000..18ebf0c
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_overview_selected.xml
@@ -0,0 +1,20 @@
+<?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
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/car_ic_selection_bg" android:gravity="center"/>
+    <item android:drawable="@drawable/car_ic_overview_black" android:gravity="center"/>
+</layer-list>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_phone.xml b/packages/CarSystemUI/res/drawable/car_ic_phone.xml
new file mode 100644
index 0000000..7091670
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_phone.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="40dp"
+    android:height="40dp"
+    android:viewportHeight="40.0"
+    android:viewportWidth="40.0">
+    <path
+        android:fillColor="@color/car_nav_icon_fill_color"
+        android:fillType="nonZero"
+        android:pathData="M21.31,35.75L21.3,35.76L3.46,17.92L0.03,6.25C-0.16,5.32 0.53,3.88 1.41,3.51C6.36,1.25 8.88,0.1 8.95,0.08C10.02,-0.23 11.39,0.39 11.7,1.45L13.76,10.37C13.97,11.1 13.62,11.91 13.07,12.43L8.24,17.25L22.31,31.32L27.13,26.49C27.65,25.94 28.47,25.6 29.19,25.81L38.11,27.87C39.18,28.17 39.79,29.55 39.49,30.61C39.46,30.69 38.32,33.2 36.05,38.16C35.68,39.04 34.25,39.73 33.31,39.53L21.65,36.1C21.51,35.96 21.4,35.84 21.31,35.75Z"
+        android:strokeColor="@color/car_nav_icon_fill_color"
+        android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_phone_black.xml b/packages/CarSystemUI/res/drawable/car_ic_phone_black.xml
new file mode 100644
index 0000000..087eebb
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_phone_black.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="40dp"
+    android:height="40dp"
+    android:viewportHeight="40.0"
+    android:viewportWidth="40.0">
+    <path
+        android:fillColor="#FF000000"
+        android:fillType="nonZero"
+        android:pathData="M21.31,35.75L21.3,35.76L3.46,17.92L0.03,6.25C-0.16,5.32 0.53,3.88 1.41,3.51C6.36,1.25 8.88,0.1 8.95,0.08C10.02,-0.23 11.39,0.39 11.7,1.45L13.76,10.37C13.97,11.1 13.62,11.91 13.07,12.43L8.24,17.25L22.31,31.32L27.13,26.49C27.65,25.94 28.47,25.6 29.19,25.81L38.11,27.87C39.18,28.17 39.79,29.55 39.49,30.61C39.46,30.69 38.32,33.2 36.05,38.16C35.68,39.04 34.25,39.73 33.31,39.53L21.65,36.1C21.51,35.96 21.4,35.84 21.31,35.75Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_phone_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_phone_selected.xml
new file mode 100644
index 0000000..197eac0
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_phone_selected.xml
@@ -0,0 +1,20 @@
+<?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
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/car_ic_selection_bg" android:gravity="center"/>
+    <item android:drawable="@drawable/car_ic_phone_black" android:gravity="center"/>
+</layer-list>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_selection_bg.xml b/packages/CarSystemUI/res/drawable/car_ic_selection_bg.xml
new file mode 100644
index 0000000..781beaf
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_selection_bg.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="70dp"
+    android:height="70dp"
+    android:viewportHeight="70.0"
+    android:viewportWidth="70.0">
+    <path
+        android:fillColor="@color/car_accent"
+        android:fillType="evenOdd"
+        android:pathData="M4,0L66,0A4,4 0,0 1,70 4L70,66A4,4 0,0 1,66 70L4,70A4,4 0,0 1,0 66L0,4A4,4 0,0 1,4 0z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_seekbar_thumb.xml b/packages/CarSystemUI/res/drawable/car_seekbar_thumb.xml
new file mode 100644
index 0000000..1a9b8a5
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_seekbar_thumb.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
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="oval">
+            <padding
+                android:bottom="@dimen/car_padding_1"
+                android:left="@dimen/car_padding_1"
+                android:right="@dimen/car_padding_1"
+                android:top="@dimen/car_padding_1"/>
+            <solid android:color="@android:color/black"/>
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="oval">
+            <solid android:color="@color/car_accent"/>
+            <size
+                android:width="@dimen/car_seekbar_thumb_size"
+                android:height="@dimen/car_seekbar_thumb_size"/>
+        </shape>
+    </item>
+</layer-list>
diff --git a/packages/CarSystemUI/res/drawable/ic_mic_white.xml b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
new file mode 100644
index 0000000..f5a91b5
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:pathData="M24,28c3.31,0 5.98,-2.69 5.98,-6L30,10c0,-3.32 -2.68,-6 -6,-6 -3.31,0 -6,2.68 -6,6v12c0,3.31 2.69,6 6,6zM34.6,22c0,6 -5.07,10.2 -10.6,10.2 -5.52,0 -10.6,-4.2 -10.6,-10.2L10,22c0,6.83 5.44,12.47 12,13.44L22,42h4v-6.56c6.56,-0.97 12,-6.61 12,-13.44h-3.4z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/nav_button_background.xml b/packages/CarSystemUI/res/drawable/nav_button_background.xml
new file mode 100644
index 0000000..376347c
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/nav_button_background.xml
@@ -0,0 +1,26 @@
+<?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
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/nav_bar_ripple_background_color">
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="?android:colorAccent"/>
+            <corners android:radius="6dp"/>
+        </shape>
+    </item>
+</ripple>
diff --git a/packages/CarSystemUI/res/drawable/notification_material_bg.xml b/packages/CarSystemUI/res/drawable/notification_material_bg.xml
new file mode 100644
index 0000000..03746c8
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/notification_material_bg.xml
@@ -0,0 +1,26 @@
+<?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
+  -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/notification_ripple_untinted_color">
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android">
+            <solid android:color="@color/notification_material_background_color"/>
+            <corners
+                android:radius="@dimen/notification_shadow_radius"/>
+        </shape>
+    </item>
+</ripple>
diff --git a/packages/CarSystemUI/res/drawable/notification_material_bg_dim.xml b/packages/CarSystemUI/res/drawable/notification_material_bg_dim.xml
new file mode 100644
index 0000000..03746c8
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/notification_material_bg_dim.xml
@@ -0,0 +1,26 @@
+<?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
+  -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/notification_ripple_untinted_color">
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android">
+            <solid android:color="@color/notification_material_background_color"/>
+            <corners
+                android:radius="@dimen/notification_shadow_radius"/>
+        </shape>
+    </item>
+</ripple>
diff --git a/packages/CarSystemUI/res/drawable/volume_dialog_background.xml b/packages/CarSystemUI/res/drawable/volume_dialog_background.xml
new file mode 100644
index 0000000..fa3ca8f
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/volume_dialog_background.xml
@@ -0,0 +1,26 @@
+<?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
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="?android:attr/colorBackgroundFloating"/>
+    <padding
+        android:bottom="5dp"
+        android:left="5dp"
+        android:right="5dp"
+        android:top="5dp"/>
+    <corners android:bottomLeftRadius="20dp"
+        android:bottomRightRadius="20dp"/>
+</shape>
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
new file mode 100644
index 0000000..b67ce15
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -0,0 +1,145 @@
+<?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
+  -->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    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:orientation="vertical">
+    <LinearLayout
+        android:id="@id/nav_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingStart="20dp"
+        android:paddingEnd="20dp"
+        android:gravity="center">
+
+        <com.android.systemui.statusbar.car.CarFacetButton
+            android:id="@+id/home"
+            style="@style/NavigationBarButton"
+            systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+            systemui:icon="@drawable/car_ic_overview"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+            systemui:longIntent="intent:#Intent;action=com.google.android.demandspace.START;end"
+            systemui:selectedIcon="@drawable/car_ic_overview_selected"
+            systemui:useMoreIcon="false"
+        />
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"/>
+
+        <com.android.systemui.statusbar.car.CarFacetButton
+            android:id="@+id/maps_nav"
+            style="@style/NavigationBarButton"
+            systemui:categories="android.intent.category.APP_MAPS"
+            systemui:icon="@drawable/car_ic_navigation"
+            systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.CarLauncher;category=android.intent.category.APP_MAPS;launchFlags=0x24000000;end"
+            systemui:selectedIcon="@drawable/car_ic_navigation_selected"
+            systemui:useMoreIcon="false"
+        />
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"/>
+
+        <com.android.systemui.statusbar.car.CarFacetButton
+            android:id="@+id/music_nav"
+            style="@style/NavigationBarButton"
+            systemui:icon="@drawable/car_ic_music"
+            systemui:intent="intent:#Intent;component=com.android.car.media/.MediaActivity;launchFlags=0x14000000;end"
+            systemui:packages="com.android.car.media"
+            systemui:selectedIcon="@drawable/car_ic_music_selected"
+            systemui:useMoreIcon="false"
+        />
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"/>
+
+        <com.android.systemui.statusbar.car.CarFacetButton
+            android:id="@+id/phone_nav"
+            style="@style/NavigationBarButton"
+            systemui:componentNames="com.android.car.dialer/.TelecomActivity"
+            systemui:icon="@drawable/car_ic_phone"
+            systemui:intent="intent:#Intent;component=com.android.car.dialer/.TelecomActivity;launchFlags=0x14000000;end"
+            systemui:selectedIcon="@drawable/car_ic_phone_selected"
+            systemui:useMoreIcon="false"
+        />
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"/>
+
+        <com.android.systemui.statusbar.car.CarFacetButton
+            android:id="@+id/grid_nav"
+            style="@style/NavigationBarButton"
+            systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
+            systemui:icon="@drawable/car_ic_apps"
+            systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
+            systemui:selectedIcon="@drawable/car_ic_apps_selected"
+            systemui:useMoreIcon="false"
+        />
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"/>
+
+        <com.android.systemui.statusbar.car.CarFacetButton
+            android:id="@+id/note"
+            style="@style/NavigationBarButton"
+            systemui:icon="@drawable/car_ic_notification"
+            systemui:intent="intent:#Intent;component=com.android.car.notification/.CarNotificationCenterActivity;launchFlags=0x14000000;end"
+            systemui:packages="com.android.car.notification"
+            systemui:selectedIcon="@drawable/car_ic_notification_selected"
+            systemui:useMoreIcon="false"
+        />
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"/>
+
+        <com.android.systemui.statusbar.car.CarFacetButton
+            android:id="@+id/assist"
+            style="@style/NavigationBarButton"
+            systemui:icon="@drawable/ic_mic_white"
+            systemui:intent="intent:#Intent;action=com.google.android.demandspace.START;end"
+            systemui:useMoreIcon="false"
+        />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/lock_screen_nav_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingStart="@dimen/car_keyline_1"
+        android:paddingEnd="@dimen/car_keyline_1"
+        android:gravity="center"
+        android:visibility="gone">
+    </LinearLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
new file mode 100644
index 0000000..46e60db
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -0,0 +1,44 @@
+<?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
+  -->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    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:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/nav_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingStart="@dimen/car_padding_5"
+        android:paddingEnd="@dimen/car_padding_5">
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/home"
+            android:layout_width="@dimen/car_touch_target_size"
+            android:layout_height="match_parent"
+            android:background="?android:attr/selectableItemBackground"
+            android:src="@drawable/car_ic_overview"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+        />
+    </LinearLayout>
+</com.android.systemui.statusbar.car.CarNavigationBarView>
+
diff --git a/packages/CarSystemUI/res/layout/car_status_bar_header.xml b/packages/CarSystemUI/res/layout/car_status_bar_header.xml
new file mode 100644
index 0000000..e446072
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_status_bar_header.xml
@@ -0,0 +1,29 @@
+<?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
+  -->
+<!-- Extends LinearLayout -->
+<com.android.systemui.qs.car.CarStatusBarHeader
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/header"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/status_bar_height">
+
+    <include layout="@layout/car_top_navigation_bar"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+    />
+</com.android.systemui.qs.car.CarStatusBarHeader>
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
new file mode 100644
index 0000000..7b3333e
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -0,0 +1,151 @@
+<?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
+  -->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/car_top_bar"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/black"
+    android:orientation="vertical">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1">
+
+        <FrameLayout
+            android:id="@+id/left_hvac_container"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_alignParentStart="true"
+        >
+
+            <com.android.systemui.statusbar.car.CarNavigationButton
+                android:id="@+id/hvacleft"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:background="@null"
+                systemui:broadcast="true"
+                systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+            />
+
+            <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+                android:id="@+id/lefttext"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:paddingStart="@dimen/car_padding_4"
+                android:paddingEnd="16dp"
+                android:gravity="center_vertical|start"
+                android:minEms="4"
+                android:textAppearance="@style/TextAppearance.Car.Status"
+                systemui:hvacAreaId="49"
+                systemui:hvacMaxText="@string/hvac_max_text"
+                systemui:hvacMaxValue="@dimen/hvac_max_value"
+                systemui:hvacMinText="@string/hvac_min_text"
+                systemui:hvacMinValue="@dimen/hvac_min_value"
+                systemui:hvacPivotOffset="60dp"
+                systemui:hvacPropertyId="358614275"
+                systemui:hvacTempFormat="%.0f\u00B0"
+            />
+        </FrameLayout>
+
+        <FrameLayout
+            android:id="@+id/clock_container"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_centerInParent="true"
+        >
+            <com.android.systemui.statusbar.car.CarNavigationButton
+                android:id="@+id/qs"
+                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"
+            />
+            <com.android.systemui.statusbar.policy.Clock
+                android:id="@+id/clock"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:elevation="5dp"
+                android:singleLine="true"
+                android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+            />
+        </FrameLayout>
+
+        <LinearLayout
+            android:id="@+id/system_icon_area"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_centerHorizontal="true"
+            android:layout_centerVertical="true"
+            android:layout_toEndOf="@+id/clock_container"
+            android:paddingStart="@dimen/car_padding_1"
+            android:gravity="center_vertical"
+            android:orientation="horizontal"
+        >
+
+            <include
+                layout="@layout/system_icons"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:paddingStart="4dp"
+                android:gravity="center_vertical"
+            />
+        </LinearLayout>
+
+        <FrameLayout
+            android:id="@+id/right_hvac_container"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_alignParentEnd="true"
+        >
+
+            <com.android.systemui.statusbar.car.CarNavigationButton
+                android:id="@+id/hvacright"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:background="@null"
+                systemui:broadcast="true"
+                systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+            />
+
+            <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+                android:id="@+id/righttext"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:paddingStart="16dp"
+                android:paddingEnd="@dimen/car_padding_4"
+                android:gravity="center_vertical|end"
+                android:minEms="4"
+                android:textAppearance="@style/TextAppearance.Car.Status"
+                systemui:hvacAreaId="68"
+                systemui:hvacMaxText="@string/hvac_max_text"
+                systemui:hvacMaxValue="@dimen/hvac_max_value"
+                systemui:hvacMinText="@string/hvac_min_text"
+                systemui:hvacMinValue="@dimen/hvac_min_value"
+                systemui:hvacPivotOffset="60dp"
+                systemui:hvacPropertyId="358614275"
+                systemui:hvacTempFormat="%.0f\u00B0"
+            />
+        </FrameLayout>
+    </RelativeLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/car_volume_dialog.xml b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
new file mode 100644
index 0000000..c98740e
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
@@ -0,0 +1,30 @@
+<?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
+  -->
+<androidx.car.widget.PagedListView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/volume_list"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@android:color/black"
+    android:minWidth="@dimen/volume_dialog_panel_width"
+    android:theme="@style/Theme.Car.DialogListView"
+    app:dividerEndMargin="@dimen/car_keyline_1"
+    app:dividerStartMargin="@dimen/car_keyline_1"
+    app:gutter="none"
+    app:scrollBarEnabled="false"
+    app:showPagedListViewDivider="true"/>
diff --git a/packages/CarSystemUI/res/layout/status_bar_wifi_group.xml b/packages/CarSystemUI/res/layout/status_bar_wifi_group.xml
new file mode 100644
index 0000000..2793e56
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/status_bar_wifi_group.xml
@@ -0,0 +1,85 @@
+<?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
+  -->
+<com.android.systemui.statusbar.StatusBarWifiView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/wifi_combo"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:gravity="center_vertical">
+
+    <com.android.keyguard.AlphaOptimizedLinearLayout
+        android:id="@+id/wifi_group"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:paddingStart="2dp"
+        android:gravity="center_vertical"
+    >
+        <FrameLayout
+            android:id="@+id/inout_container"
+            android:layout_width="wrap_content"
+            android:layout_height="17dp"
+            android:gravity="center_vertical">
+            <ImageView
+                android:id="@+id/wifi_in"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingEnd="2dp"
+                android:src="@drawable/ic_activity_down"
+                android:visibility="gone"
+            />
+            <ImageView
+                android:id="@+id/wifi_out"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingEnd="2dp"
+                android:src="@drawable/ic_activity_up"
+                android:visibility="gone"
+            />
+        </FrameLayout>
+        <FrameLayout
+            android:id="@+id/wifi_combo"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical">
+            <com.android.systemui.statusbar.AlphaOptimizedImageView
+                android:id="@+id/wifi_signal"
+                android:layout_width="@dimen/status_bar_icon_size"
+                android:layout_height="@dimen/status_bar_icon_size"
+                android:theme="?attr/lightIconTheme"/>
+        </FrameLayout>
+
+        <View
+            android:id="@+id/wifi_signal_spacer"
+            android:layout_width="@dimen/status_bar_wifi_signal_spacer_width"
+            android:layout_height="4dp"
+            android:visibility="gone"/>
+
+        <ViewStub
+            android:id="@+id/connected_device_signals_stub"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout="@layout/connected_device_signal"/>
+
+        <View
+            android:id="@+id/wifi_airplane_spacer"
+            android:layout_width="@dimen/status_bar_airplane_spacer_width"
+            android:layout_height="4dp"
+            android:visibility="gone"
+        />
+    </com.android.keyguard.AlphaOptimizedLinearLayout>
+</com.android.systemui.statusbar.StatusBarWifiView>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
new file mode 100644
index 0000000..0594dce
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -0,0 +1,91 @@
+<?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
+  -->
+
+<!-- This is the combined status bar / notification panel window. -->
+<com.android.systemui.statusbar.phone.StatusBarWindowView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:sysui="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true">
+
+    <com.android.systemui.statusbar.BackDropView
+        android:id="@+id/backdrop"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone"
+        sysui:ignoreRightInset="true"
+    >
+        <ImageView android:id="@+id/backdrop_back"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:scaleType="centerCrop"/>
+        <ImageView android:id="@+id/backdrop_front"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:scaleType="centerCrop"
+            android:visibility="invisible"/>
+    </com.android.systemui.statusbar.BackDropView>
+
+    <com.android.systemui.statusbar.ScrimView
+        android:id="@+id/scrim_behind"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="no"
+        sysui:ignoreRightInset="true"
+    />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/status_bar_height"
+        android:orientation="vertical"
+    >
+        <FrameLayout
+            android:id="@+id/status_bar_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+        />
+
+        <include layout="@layout/car_top_navigation_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+        />
+    </LinearLayout>
+
+    <include layout="@layout/brightness_mirror"/>
+
+    <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout="@layout/car_fullscreen_user_switcher"/>
+
+    <include layout="@layout/status_bar_expanded"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="invisible"/>
+
+    <com.android.systemui.statusbar.ScrimView
+        android:id="@+id/scrim_in_front"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="no"
+        sysui:ignoreRightInset="true"
+    />
+
+</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/CarSystemUI/res/layout/system_icons.xml b/packages/CarSystemUI/res/layout/system_icons.xml
new file mode 100644
index 0000000..a7dd65e
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/system_icons.xml
@@ -0,0 +1,40 @@
+<?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:id="@+id/system_icons"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_vertical">
+
+    <com.android.systemui.statusbar.phone.StatusIconContainer
+        android:id="@+id/statusIcons"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:paddingEnd="4dp"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"
+    />
+
+    <com.android.systemui.BatteryMeterView
+        android:id="@+id/battery"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+    />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/values-night/colors.xml b/packages/CarSystemUI/res/values-night/colors.xml
new file mode 100644
index 0000000..dad94a8
--- /dev/null
+++ b/packages/CarSystemUI/res/values-night/colors.xml
@@ -0,0 +1,24 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android">
+    <color name="car_accent">#356FE5</color>
+    <color name="status_bar_background_color">#ff000000</color>
+    <color name="system_bar_background_opaque">#ff0c1013</color>
+
+    <!-- The color of the ripples on the untinted notifications -->
+    <color name="notification_ripple_untinted_color">@color/ripple_material_dark</color>
+</resources>
diff --git a/packages/CarSystemUI/res/values/attrs.xml b/packages/CarSystemUI/res/values/attrs.xml
new file mode 100644
index 0000000..6178738
--- /dev/null
+++ b/packages/CarSystemUI/res/values/attrs.xml
@@ -0,0 +1,35 @@
+<?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.
+  -->
+
+<resources>
+
+    <!-- Custom attributes to configure hvac values -->
+    <declare-styleable name="AnimatedTemperatureView">
+        <attr name="hvacAreaId" format="integer"/>
+        <attr name="hvacPropertyId" format="integer"/>
+        <attr name="hvacTempFormat" format="string"/>
+        <!-- how far away the animations should center around -->
+        <attr name="hvacPivotOffset" format="dimension"/>
+        <attr name="hvacMinValue" format="float"/>
+        <attr name="hvacMaxValue" format="float"/>
+        <attr name="hvacMinText" format="string|reference"/>
+        <attr name="hvacMaxText" format="string|reference"/>
+        <attr name="android:gravity"/>
+        <attr name="android:minEms"/>
+        <attr name="android:textAppearance"/>
+    </declare-styleable>
+</resources>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
new file mode 100644
index 0000000..e9ddbaf
--- /dev/null
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -0,0 +1,54 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android">
+    <color name="nav_bar_ripple_background_color">#40ffffff</color>
+    <color name="car_accent">#356FE5</color>
+    <!-- colors for user switcher -->
+    <color name="car_user_switcher_background_color">#000000</color>
+    <color name="car_user_switcher_name_text_color">@color/car_title2_light</color>
+    <color name="car_user_switcher_add_user_background_color">#131313</color>
+    <color name="car_nav_icon_fill_color">@color/car_grey_50</color>
+    <!-- colors for seekbar -->
+    <color name="car_seekbar_track_background">#131315</color>
+    <color name="car_seekbar_track_secondary_progress">@color/car_accent</color>
+    <!-- colors for volume dialog tint -->
+    <color name="car_volume_dialog_tint">@color/car_tint_light</color>
+
+    <!-- System ui can't depend on car libs so redefine. -->
+    <color name="car_grey_50">#fffafafa</color>
+
+    <color name="docked_divider_background">@color/car_grey_50</color>
+    <color name="system_bar_background_opaque">#ff172026</color>
+
+    <color name="status_bar_background_color">#33000000</color>
+    <drawable name="system_bar_background">@color/status_bar_background_color</drawable>
+
+    <!-- The scrim color for the background of the notifications shade. -->
+    <color name="scrim_behind_color">#172026</color>
+
+    <!-- The color of the dividing line between grouped notifications. -->
+    <color name="notification_divider_color">@*android:color/notification_action_list</color>
+
+    <!-- The color of the ripples on the untinted notifications -->
+    <color name="notification_ripple_untinted_color">@color/ripple_material_light</color>
+
+    <color name="car_teal_700">#ff00796b</color>
+    <color name="car_grey_300">#ffe0e0e0</color>
+    <color name="car_grey_900">#ff212121</color>
+
+    <color name="keyguard_button_text_color">@android:color/black</color>
+</resources>
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
new file mode 100644
index 0000000..452d61d
--- /dev/null
+++ b/packages/CarSystemUI/res/values/config.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>
+    <string name="config_statusBarComponent" translatable="false">
+        com.android.systemui.statusbar.car.CarStatusBar
+    </string>
+    <string name="config_systemUIFactoryComponent" translatable="false">
+        com.android.systemui.CarSystemUIFactory
+    </string>
+    <bool name="config_enableFullscreenUserSwitcher">true</bool>
+
+    <!-- configure which system ui bars should be displayed -->
+    <bool name="config_enableLeftNavigationBar">false</bool>
+    <bool name="config_enableRightNavigationBar">false</bool>
+    <bool name="config_enableBottomNavigationBar">true</bool>
+
+</resources>
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
new file mode 100644
index 0000000..3829aa3
--- /dev/null
+++ b/packages/CarSystemUI/res/values/dimens.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
+  -->
+<resources>
+    <!--
+       Note: status bar height and navigation bar heights are defined
+       in frameworks/base/core package and thus will have no effect if
+       set here. See car_product overlay for car specific defaults-->
+
+    <dimen name="status_bar_icon_drawing_size_dark">36dp</dimen>
+    <dimen name="status_bar_icon_drawing_size">36dp</dimen>
+    <dimen name="car_qs_header_system_icons_area_height">96dp</dimen>
+    <!-- The amount by which to scale up the status bar icons. -->
+    <item name="status_bar_icon_scale_factor" format="float" type="dimen">1.75</item>
+
+    <dimen name="car_primary_icon_size">36dp</dimen>
+
+    <!-- dimensions for the car user switcher -->
+    <dimen name="car_user_switcher_name_text_size">@dimen/car_title2_size</dimen>
+    <dimen name="car_user_switcher_vertical_spacing_between_users">124dp</dimen>
+
+    <!--These values represent MIN and MAX for hvac-->
+    <item name="hvac_min_value" format="float" type="dimen">0</item>
+    <item name="hvac_max_value" format="float" type="dimen">126</item>
+
+    <!-- Largest size an avatar might need to be drawn in the user picker, status bar, or
+         quick settings header -->
+    <dimen name="max_avatar_size">128dp</dimen>
+
+    <!-- Standard image button size for volume dialog buttons -->
+    <dimen name="volume_button_size">84dp</dimen>
+    <!-- The maximum width allowed for the volume dialog. For auto, we allow this to span a good
+         deal of the screen. This value accounts for the side margins. -->
+    <dimen name="volume_dialog_panel_width">1920dp</dimen>
+    <dimen name="volume_dialog_side_margin">@dimen/side_margin</dimen>
+
+    <dimen name="volume_dialog_elevation">6dp</dimen>
+
+    <dimen name="volume_dialog_row_margin_end">@dimen/car_keyline_3</dimen>
+
+    <dimen name="volume_dialog_row_padding_end">0dp</dimen>
+
+    <dimen name="line_item_height">128dp</dimen>
+    <dimen name="volume_icon_size">96dp</dimen>
+    <dimen name="side_margin">148dp</dimen>
+    <dimen name="car_keyline_1">24dp</dimen>
+    <dimen name="car_keyline_2">96dp</dimen>
+    <dimen name="car_keyline_3">128dp</dimen>
+</resources>
diff --git a/packages/CarSystemUI/res/values/integers.xml b/packages/CarSystemUI/res/values/integers.xml
new file mode 100644
index 0000000..8b87c74
--- /dev/null
+++ b/packages/CarSystemUI/res/values/integers.xml
@@ -0,0 +1,20 @@
+<?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>
+    <integer name="user_fullscreen_switcher_num_col">2</integer>
+</resources>
diff --git a/packages/CarSystemUI/res/values/strings.xml b/packages/CarSystemUI/res/values/strings.xml
new file mode 100644
index 0000000..0368e61
--- /dev/null
+++ b/packages/CarSystemUI/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?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>
+    <!-- String to represent lowest setting of an HVAC system [CHAR LIMIT=5]-->
+    <string name="hvac_min_text">Min</string>
+    <!-- String to represent largest setting of an HVAC system [CHAR LIMIT=5]-->
+    <string name="hvac_max_text">Max</string>
+</resources>
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
new file mode 100644
index 0000000..7f4544a
--- /dev/null
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -0,0 +1,55 @@
+<?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 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="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless">
+        <item name="android:background">@drawable/btn_borderless_rect</item>
+        <item name="android:scaleType">fitCenter</item>
+        <item name="android:padding">22dp</item>
+    </style>
+
+    <style name="TextAppearance.StatusBar.Clock"
+        parent="@*android:style/TextAppearance.StatusBar.Icon">
+        <item name="android:textSize">42sp</item>
+        <item name="android:fontFamily">sans-serif-regular</item>
+        <item name="android:textColor">@color/car_grey_50</item>
+    </style>
+
+    <style name="TextAppearance.Car.Status">
+        <item name="android:textSize">@dimen/car_body2_size</item>
+        <item name="android:textColor">@color/car_grey_50</item>
+    </style>
+
+    <style name="CarNavigationBarButtonTheme">
+        <item name="android:colorControlHighlight">@color/nav_bar_ripple_background_color</item>
+    </style>
+
+    <style name="Theme.Car.DialogListView" parent="@style/Theme.Car.NoActionBar">
+        <item name="android:colorControlActivated">@color/car_accent</item>
+        <item name="listItemBackgroundColor">@android:color/black</item>
+    </style>
+
+    <style name="NavigationBarButton">
+        <item name="android:layout_height">96dp</item>
+        <item name="android:layout_width">96dp</item>
+        <item name="android:background">@drawable/nav_button_background</item>
+    </style>
+
+</resources>
diff --git a/packages/CarSystemUI/res/xml/car_volume_items.xml b/packages/CarSystemUI/res/xml/car_volume_items.xml
new file mode 100644
index 0000000..8715946
--- /dev/null
+++ b/packages/CarSystemUI/res/xml/car_volume_items.xml
@@ -0,0 +1,55 @@
+<?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.
+  -->
+
+<!--
+  Defines all possible items on car volume settings UI, keyed by usage.
+-->
+<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto">
+  <item car:usage="unknown"
+        car:icon="@drawable/car_ic_music"/>
+  <item car:usage="media"
+        car:icon="@drawable/car_ic_music"/>
+  <item car:usage="voice_communication"
+        car:icon="@*android:drawable/ic_audio_ring_notif"/>
+  <item car:usage="voice_communication_signalling"
+        car:icon="@*android:drawable/ic_audio_ring_notif"/>
+  <item car:usage="alarm"
+        car:icon="@drawable/ic_volume_alarm"/>
+  <item car:usage="notification"
+        car:icon="@drawable/car_ic_notification"/>
+  <item car:usage="notification_ringtone"
+        car:icon="@drawable/car_ic_notification"/>
+  <item car:usage="notification_communication_request"
+        car:icon="@drawable/car_ic_notification"/>
+  <item car:usage="notification_communication_instant"
+        car:icon="@drawable/car_ic_notification"/>
+  <item car:usage="notification_communication_delayed"
+        car:icon="@drawable/car_ic_notification"/>
+  <item car:usage="notification_event"
+        car:icon="@drawable/car_ic_notification"/>
+  <item car:usage="assistance_accessibility"
+        car:icon="@drawable/car_ic_notification"/>
+  <item car:usage="assistance_navigation_guidance"
+        car:icon="@drawable/car_ic_navigation"/>
+  <item car:usage="assistance_sonification"
+        car:icon="@drawable/car_ic_notification"/>
+  <item car:usage="game"
+        car:icon="@drawable/car_ic_music"/>
+  <item car:usage="assistant"
+        car:icon="@drawable/car_ic_music"/>
+</carVolumeItems>
+
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
new file mode 100644
index 0000000..dfe5704
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.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.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.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.phone.StatusBarKeyguardViewManager;
+
+/**
+ * Class factory to provide car specific SystemUI components.
+ */
+public class CarSystemUIFactory extends SystemUIFactory {
+
+    public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
+        ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
+        return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
+    }
+
+    @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));
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
new file mode 100644
index 0000000..27d3106
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
@@ -0,0 +1,276 @@
+/*
+ * 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.hvac;
+
+import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import com.android.systemui.statusbar.car.hvac.TemperatureView;
+import com.android.systemui.statusbar.car.hvac.HvacController;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextSwitcher;
+import android.widget.TextView;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+
+/**
+ * Simple text display of HVAC properties, It is designed to show mTemperature and is configured in
+ * the XML.
+ * XML properties:
+ * hvacPropertyId - Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
+ * hvacAreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
+ * hvacTempFormat - Example: "%.1f\u00B0" (1 decimal and the degree symbol)
+ * hvacOrientaion = Example: left
+ * <p>
+ * Note: It registers itself with {@link HvacController}
+ */
+public class AnimatedTemperatureView extends FrameLayout implements TemperatureView {
+
+    private static final float TEMPERATURE_EQUIVALENT_DELTA = .01f;
+    private static final Property<ColorDrawable, Integer> COLOR_PROPERTY =
+            new Property<ColorDrawable, Integer>(Integer.class, "color") {
+
+                @Override
+                public Integer get(ColorDrawable object) {
+                    return object.getColor();
+                }
+
+                @Override
+                public void set(ColorDrawable object, Integer value) {
+                    object.setColor(value);
+                }
+            };
+
+    static boolean isHorizontal(int gravity) {
+        return Gravity.isHorizontal(gravity)
+                && (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.CENTER_HORIZONTAL;
+    }
+
+    @SuppressLint("RtlHardcoded")
+    static boolean isLeft(int gravity, int layoutDirection) {
+        return Gravity
+                .getAbsoluteGravity(gravity & Gravity.HORIZONTAL_GRAVITY_MASK, layoutDirection)
+                == Gravity.LEFT;
+    }
+
+    static boolean isVertical(int gravity) {
+        return Gravity.isVertical(gravity)
+                && (gravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.CENTER_VERTICAL;
+    }
+
+    static boolean isTop(int gravity) {
+        return (gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.TOP;
+    }
+
+    private final int mAreaId;
+    private final int mPropertyId;
+    private final int mPivotOffset;
+    private final int mGravity;
+    private final int mTextAppearanceRes;
+    private final int mMinEms;
+    private final Rect mPaddingRect;
+    private final float mMinValue;
+    private final float mMaxValue;
+
+    private final ColorDrawable mBackgroundColor;
+
+    private final TemperatureColorStore mColorStore = new TemperatureColorStore();
+    private final TemperatureBackgroundAnimator mBackgroundAnimator;
+    private final TemperatureTextAnimator mTextAnimator;
+
+    public AnimatedTemperatureView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray typedArray = context.obtainStyledAttributes(attrs,
+                R.styleable.AnimatedTemperatureView);
+        mAreaId = typedArray.getInt(R.styleable.AnimatedTemperatureView_hvacAreaId, -1);
+        mPropertyId = typedArray.getInt(R.styleable.AnimatedTemperatureView_hvacPropertyId, -1);
+        mPivotOffset =
+                typedArray.getDimensionPixelOffset(
+                        R.styleable.AnimatedTemperatureView_hvacPivotOffset, 0);
+        mGravity = typedArray.getInt(R.styleable.AnimatedTemperatureView_android_gravity,
+                Gravity.START);
+        mTextAppearanceRes =
+                typedArray.getResourceId(R.styleable.AnimatedTemperatureView_android_textAppearance,
+                        0);
+        mMinEms = typedArray.getInteger(R.styleable.AnimatedTemperatureView_android_minEms, 0);
+        mMinValue = typedArray.getFloat(R.styleable.AnimatedTemperatureView_hvacMinValue,
+                Float.NaN);
+        mMaxValue = typedArray.getFloat(R.styleable.AnimatedTemperatureView_hvacMaxValue,
+                Float.NaN);
+
+
+        mPaddingRect =
+                new Rect(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom());
+        setPadding(0, 0, 0, 0);
+
+        setClipChildren(false);
+        setClipToPadding(false);
+
+        // init Views
+        TextSwitcher textSwitcher = new TextSwitcher(context);
+        textSwitcher.setFactory(this::generateTextView);
+        ImageView background = new ImageView(context);
+        mBackgroundColor = new ColorDrawable(Color.TRANSPARENT);
+        background.setImageDrawable(mBackgroundColor);
+        background.setVisibility(View.GONE);
+
+        mBackgroundAnimator = new TemperatureBackgroundAnimator(this, background);
+
+
+        String format = typedArray.getString(R.styleable.AnimatedTemperatureView_hvacTempFormat);
+        format = (format == null) ? "%.1f\u00B0" : format;
+        CharSequence minText = typedArray.getString(
+                R.styleable.AnimatedTemperatureView_hvacMinText);
+        CharSequence maxText = typedArray.getString(
+                R.styleable.AnimatedTemperatureView_hvacMaxText);
+        mTextAnimator = new TemperatureTextAnimator(this, textSwitcher, format, mPivotOffset,
+                minText, maxText);
+
+        addView(background, ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT);
+        addView(textSwitcher, ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT);
+
+        typedArray.recycle();
+
+        // register with controller
+        HvacController hvacController = Dependency.get(HvacController.class);
+        hvacController.addHvacTextView(this);
+    }
+
+    private TextView generateTextView() {
+        TextView textView = new TextView(getContext());
+        textView.setTextAppearance(mTextAppearanceRes);
+        textView.setAllCaps(true);
+        textView.setMinEms(mMinEms);
+        textView.setGravity(mGravity);
+        textView.setPadding(mPaddingRect.left, mPaddingRect.top, mPaddingRect.right,
+                mPaddingRect.bottom);
+        textView.getViewTreeObserver()
+                .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        if (isHorizontal(mGravity)) {
+                            if (isLeft(mGravity, getLayoutDirection())) {
+                                textView.setPivotX(-mPivotOffset);
+                            } else {
+                                textView.setPivotX(textView.getWidth() + mPivotOffset);
+                            }
+                        }
+                        textView.getViewTreeObserver().removeOnPreDrawListener(this);
+                        return false;
+                    }
+                });
+        textView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+
+        return textView;
+    }
+
+    /**
+     * Formats the float for display
+     *
+     * @param temp - The current temp or NaN
+     */
+    @Override
+    public void setTemp(float temp) {
+        mTextAnimator.setTemp(temp);
+        if (Float.isNaN(temp)) {
+            mBackgroundAnimator.hideCircle();
+            return;
+        }
+        int color;
+        if (isMinValue(temp)) {
+            color = mColorStore.getMinColor();
+        } else if (isMaxValue(temp)) {
+            color = mColorStore.getMaxColor();
+        } else {
+            color = mColorStore.getColorForTemperature(temp);
+        }
+        if (mBackgroundAnimator.isOpen()) {
+            ObjectAnimator colorAnimator =
+                    ObjectAnimator.ofInt(mBackgroundColor, COLOR_PROPERTY, color);
+            colorAnimator.setEvaluator((fraction, startValue, endValue) -> mColorStore
+                    .lerpColor(fraction, (int) startValue, (int) endValue));
+            colorAnimator.start();
+        } else {
+            mBackgroundColor.setColor(color);
+        }
+
+        mBackgroundAnimator.animateOpen();
+    }
+
+    boolean isMinValue(float temp) {
+        return !Float.isNaN(mMinValue) && isApproxEqual(temp, mMinValue);
+    }
+
+    boolean isMaxValue(float temp) {
+        return !Float.isNaN(mMaxValue) && isApproxEqual(temp, mMaxValue);
+    }
+
+    private boolean isApproxEqual(float left, float right) {
+        return Math.abs(left - right) <= TEMPERATURE_EQUIVALENT_DELTA;
+    }
+
+    int getGravity() {
+        return mGravity;
+    }
+
+    int getPivotOffset() {
+        return mPivotOffset;
+    }
+
+    Rect getPaddingRect() {
+        return mPaddingRect;
+    }
+
+    /**
+     * @return propertiyId  Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (358614275)
+     */
+    @Override
+    public int getPropertyId() {
+        return mPropertyId;
+    }
+
+    /**
+     * @return hvac AreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
+     */
+    @Override
+    public int getAreaId() {
+        return mAreaId;
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mBackgroundAnimator.stopAnimations();
+    }
+
+}
+
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureBackgroundAnimator.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureBackgroundAnimator.java
new file mode 100644
index 0000000..0bc94b5
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureBackgroundAnimator.java
@@ -0,0 +1,338 @@
+/*
+ * 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.hvac;
+
+import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isHorizontal;
+import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isLeft;
+import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isTop;
+import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isVertical;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.annotation.IntDef;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.animation.AnticipateInterpolator;
+import android.widget.ImageView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Controls circular reveal animation of temperature background
+ */
+class TemperatureBackgroundAnimator {
+
+    private static final AnticipateInterpolator ANTICIPATE_INTERPOLATOR =
+            new AnticipateInterpolator();
+    private static final float MAX_OPACITY = .6f;
+
+    private final View mAnimatedView;
+
+    private int mPivotX;
+    private int mPivotY;
+    private int mGoneRadius;
+    private int mOvershootRadius;
+    private int mRestingRadius;
+    private int mBumpRadius;
+
+    @CircleState
+    private int mCircleState;
+
+    private Animator mCircularReveal;
+    private boolean mAnimationsReady;
+
+    @IntDef({CircleState.GONE, CircleState.ENTERING, CircleState.OVERSHOT, CircleState.RESTING,
+            CircleState.RESTED, CircleState.BUMPING, CircleState.BUMPED, CircleState.EXITING})
+    private @interface CircleState {
+        int GONE = 0;
+        int ENTERING = 1;
+        int OVERSHOT = 2;
+        int RESTING = 3;
+        int RESTED = 4;
+        int BUMPING = 5;
+        int BUMPED = 6;
+        int EXITING = 7;
+    }
+
+    TemperatureBackgroundAnimator(
+            AnimatedTemperatureView parent,
+            ImageView animatedView) {
+        mAnimatedView = animatedView;
+        mAnimatedView.setAlpha(0);
+
+        parent.addOnLayoutChangeListener(
+                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+                        setupAnimations(parent.getGravity(), parent.getPivotOffset(),
+                                parent.getPaddingRect(), parent.getWidth(), parent.getHeight()));
+    }
+
+    private void setupAnimations(int gravity, int pivotOffset, Rect paddingRect,
+            int width, int height) {
+        int padding;
+        if (isHorizontal(gravity)) {
+            mGoneRadius = pivotOffset;
+            if (isLeft(gravity, mAnimatedView.getLayoutDirection())) {
+                mPivotX = -pivotOffset;
+                padding = paddingRect.right;
+            } else {
+                mPivotX = width + pivotOffset;
+                padding = paddingRect.left;
+            }
+            mPivotY = height / 2;
+            mOvershootRadius = pivotOffset + width;
+        } else if (isVertical(gravity)) {
+            mGoneRadius = pivotOffset;
+            if (isTop(gravity)) {
+                mPivotY = -pivotOffset;
+                padding = paddingRect.bottom;
+            } else {
+                mPivotY = height + pivotOffset;
+                padding = paddingRect.top;
+            }
+            mPivotX = width / 2;
+            mOvershootRadius = pivotOffset + height;
+        } else {
+            mPivotX = width / 2;
+            mPivotY = height / 2;
+            mGoneRadius = 0;
+            if (width > height) {
+                mOvershootRadius = height;
+                padding = Math.max(paddingRect.top, paddingRect.bottom);
+            } else {
+                mOvershootRadius = width;
+                padding = Math.max(paddingRect.left, paddingRect.right);
+            }
+        }
+        mRestingRadius = mOvershootRadius - padding;
+        mBumpRadius = mOvershootRadius - padding / 3;
+        mAnimationsReady = true;
+    }
+
+    boolean isOpen() {
+        return mCircleState != CircleState.GONE;
+    }
+
+    void animateOpen() {
+        if (!mAnimationsReady || mCircleState == CircleState.ENTERING) {
+            return;
+        }
+
+        AnimatorSet set = new AnimatorSet();
+        List<Animator> animators = new ArrayList<>();
+        switch (mCircleState) {
+            case CircleState.ENTERING:
+                throw new AssertionError("Should not be able to reach this statement");
+            case CircleState.GONE: {
+                Animator startCircle = createEnterAnimator();
+                markState(startCircle, CircleState.ENTERING);
+                animators.add(startCircle);
+                Animator holdOvershoot = ViewAnimationUtils
+                        .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mOvershootRadius,
+                                mOvershootRadius);
+                holdOvershoot.setDuration(50);
+                markState(holdOvershoot, CircleState.OVERSHOT);
+                animators.add(holdOvershoot);
+                Animator rest = ViewAnimationUtils
+                        .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mOvershootRadius,
+                                mRestingRadius);
+                markState(rest, CircleState.RESTING);
+                animators.add(rest);
+                Animator holdRest = ViewAnimationUtils
+                        .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mRestingRadius,
+                                mRestingRadius);
+                markState(holdRest, CircleState.RESTED);
+                holdRest.setDuration(1000);
+                animators.add(holdRest);
+                Animator exit = createExitAnimator(mRestingRadius);
+                markState(exit, CircleState.EXITING);
+                animators.add(exit);
+            }
+            break;
+            case CircleState.RESTED:
+            case CircleState.RESTING:
+            case CircleState.EXITING:
+            case CircleState.OVERSHOT:
+                int startRadius =
+                        mCircleState == CircleState.OVERSHOT ? mOvershootRadius : mRestingRadius;
+                Animator bump = ViewAnimationUtils
+                        .createCircularReveal(mAnimatedView, mPivotX, mPivotY, startRadius,
+                                mBumpRadius);
+                bump.setDuration(50);
+                markState(bump, CircleState.BUMPING);
+                animators.add(bump);
+                // fallthrough intentional
+            case CircleState.BUMPED:
+            case CircleState.BUMPING:
+                Animator holdBump = ViewAnimationUtils
+                        .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mBumpRadius,
+                                mBumpRadius);
+                holdBump.setDuration(100);
+                markState(holdBump, CircleState.BUMPED);
+                animators.add(holdBump);
+                Animator rest = ViewAnimationUtils
+                        .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mBumpRadius,
+                                mRestingRadius);
+                markState(rest, CircleState.RESTING);
+                animators.add(rest);
+                Animator holdRest = ViewAnimationUtils
+                        .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mRestingRadius,
+                                mRestingRadius);
+                holdRest.setDuration(1000);
+                markState(holdRest, CircleState.RESTED);
+                animators.add(holdRest);
+                Animator exit = createExitAnimator(mRestingRadius);
+                markState(exit, CircleState.EXITING);
+                animators.add(exit);
+                break;
+        }
+        set.playSequentially(animators);
+        set.addListener(new AnimatorListenerAdapter() {
+            private boolean mCanceled = false;
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                if (mCircularReveal != null) {
+                    mCircularReveal.cancel();
+                }
+                mCircularReveal = animation;
+                mAnimatedView.setVisibility(View.VISIBLE);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mCanceled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mCanceled) {
+                    return;
+                }
+                mCircularReveal = null;
+                mCircleState = CircleState.GONE;
+                mAnimatedView.setVisibility(View.GONE);
+            }
+        });
+
+        set.start();
+    }
+
+    private Animator createEnterAnimator() {
+        AnimatorSet animatorSet = new AnimatorSet();
+        Animator circularReveal = ViewAnimationUtils
+                .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mGoneRadius,
+                        mOvershootRadius);
+        Animator fade = ObjectAnimator.ofFloat(mAnimatedView, View.ALPHA, MAX_OPACITY);
+        animatorSet.playTogether(circularReveal, fade);
+        return animatorSet;
+    }
+
+    private Animator createExitAnimator(int startRadius) {
+        AnimatorSet animatorSet = new AnimatorSet();
+        Animator circularHide = ViewAnimationUtils
+                .createCircularReveal(mAnimatedView, mPivotX, mPivotY, startRadius,
+                        (mGoneRadius + startRadius) / 2);
+        circularHide.setInterpolator(ANTICIPATE_INTERPOLATOR);
+        Animator fade = ObjectAnimator.ofFloat(mAnimatedView, View.ALPHA, 0);
+        fade.setStartDelay(50);
+        animatorSet.playTogether(circularHide, fade);
+        return animatorSet;
+    }
+
+    void hideCircle() {
+        if (!mAnimationsReady || mCircleState == CircleState.GONE
+                || mCircleState == CircleState.EXITING) {
+            return;
+        }
+
+        int startRadius;
+        switch (mCircleState) {
+            // Unreachable, but here to exhaust switch cases
+            //noinspection ConstantConditions
+            case CircleState.EXITING:
+                //noinspection ConstantConditions
+            case CircleState.GONE:
+                throw new AssertionError("Should not be able to reach this statement");
+            case CircleState.BUMPED:
+            case CircleState.BUMPING:
+                startRadius = mBumpRadius;
+                break;
+            case CircleState.OVERSHOT:
+                startRadius = mOvershootRadius;
+                break;
+            case CircleState.ENTERING:
+            case CircleState.RESTED:
+            case CircleState.RESTING:
+                startRadius = mRestingRadius;
+                break;
+            default:
+                throw new IllegalStateException("Unknown CircleState: " + mCircleState);
+        }
+
+        Animator hideAnimation = createExitAnimator(startRadius);
+        if (startRadius == mRestingRadius) {
+            hideAnimation.setInterpolator(ANTICIPATE_INTERPOLATOR);
+        }
+        hideAnimation.addListener(new AnimatorListenerAdapter() {
+            private boolean mCanceled = false;
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mCircleState = CircleState.EXITING;
+                if (mCircularReveal != null) {
+                    mCircularReveal.cancel();
+                }
+                mCircularReveal = animation;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mCanceled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mCanceled) {
+                    return;
+                }
+                mCircularReveal = null;
+                mCircleState = CircleState.GONE;
+                mAnimatedView.setVisibility(View.GONE);
+            }
+        });
+        hideAnimation.start();
+    }
+
+    void stopAnimations() {
+        if (mCircularReveal != null) {
+            mCircularReveal.end();
+        }
+    }
+
+    private void markState(Animator animator, @CircleState int startState) {
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mCircleState = startState;
+            }
+        });
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureColorStore.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureColorStore.java
new file mode 100644
index 0000000..a40ffaf
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureColorStore.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.systemui.statusbar.hvac;
+
+import android.graphics.Color;
+
+/**
+ * Contains the logic for mapping colors to temperatures
+ */
+class TemperatureColorStore {
+
+    private static class TemperatureColorValue {
+        final float mTemperature;
+        final int mColor;
+
+        private TemperatureColorValue(float temperature, int color) {
+            this.mTemperature = temperature;
+            this.mColor = color;
+        }
+
+        float getTemperature() {
+            return mTemperature;
+        }
+
+        int getColor() {
+            return mColor;
+        }
+    }
+
+    private static TemperatureColorValue tempToColor(float temperature, int color) {
+        return new TemperatureColorValue(temperature, color);
+    }
+
+    private static final int COLOR_COLDEST = 0xFF406DFF;
+    private static final int COLOR_COLD = 0xFF4094FF;
+    private static final int COLOR_NEUTRAL = 0xFFF4F4F4;
+    private static final int COLOR_WARM = 0xFFFF550F;
+    private static final int COLOR_WARMEST = 0xFFFF0000;
+    // must be sorted by temperature
+    private static final TemperatureColorValue[] sTemperatureColorValues =
+            {
+                    // Celsius
+                    tempToColor(19, COLOR_COLDEST),
+                    tempToColor(21, COLOR_COLD),
+                    tempToColor(23, COLOR_NEUTRAL),
+                    tempToColor(25, COLOR_WARM),
+                    tempToColor(27, COLOR_WARMEST),
+
+                    // Switch over
+                    tempToColor(45, COLOR_WARMEST),
+                    tempToColor(45.00001f, COLOR_COLDEST),
+
+                    // Farenheight
+                    tempToColor(66, COLOR_COLDEST),
+                    tempToColor(70, COLOR_COLD),
+                    tempToColor(74, COLOR_NEUTRAL),
+                    tempToColor(76, COLOR_WARM),
+                    tempToColor(80, COLOR_WARMEST)
+            };
+
+    private static final int COLOR_UNSET = Color.BLACK;
+
+    private final float[] mTempHsv1 = new float[3];
+    private final float[] mTempHsv2 = new float[3];
+    private final float[] mTempHsv3 = new float[3];
+
+    int getMinColor() {
+        return COLOR_COLDEST;
+    }
+
+    int getMaxColor() {
+        return COLOR_WARMEST;
+    }
+
+    int getColorForTemperature(float temperature) {
+        if (Float.isNaN(temperature)) {
+            return COLOR_UNSET;
+        }
+        TemperatureColorValue bottomValue = sTemperatureColorValues[0];
+        if (temperature <= bottomValue.getTemperature()) {
+            return bottomValue.getColor();
+        }
+        TemperatureColorValue topValue =
+                sTemperatureColorValues[sTemperatureColorValues.length - 1];
+        if (temperature >= topValue.getTemperature()) {
+            return topValue.getColor();
+        }
+
+        int index = binarySearch(temperature);
+        if (index >= 0) {
+            return sTemperatureColorValues[index].getColor();
+        }
+
+        index = -index - 1; // move to the insertion point
+
+        TemperatureColorValue startValue = sTemperatureColorValues[index - 1];
+        TemperatureColorValue endValue = sTemperatureColorValues[index];
+        float fraction = (temperature - startValue.getTemperature()) / (endValue.getTemperature()
+                - startValue.getTemperature());
+        return lerpColor(fraction, startValue.getColor(), endValue.getColor());
+    }
+
+    int lerpColor(float fraction, int startColor, int endColor) {
+        float[] startHsv = mTempHsv1;
+        Color.colorToHSV(startColor, startHsv);
+        float[] endHsv = mTempHsv2;
+        Color.colorToHSV(endColor, endHsv);
+
+        // If a target color is white/gray, it should use the same hue as the other target
+        if (startHsv[1] == 0) {
+            startHsv[0] = endHsv[0];
+        }
+        if (endHsv[1] == 0) {
+            endHsv[0] = startHsv[0];
+        }
+
+        float[] outColor = mTempHsv3;
+        outColor[0] = hueLerp(fraction, startHsv[0], endHsv[0]);
+        outColor[1] = lerp(fraction, startHsv[1], endHsv[1]);
+        outColor[2] = lerp(fraction, startHsv[2], endHsv[2]);
+
+        return Color.HSVToColor(outColor);
+    }
+
+    private float hueLerp(float fraction, float start, float end) {
+        // If in flat part of curve, no interpolation necessary
+        if (start == end) {
+            return start;
+        }
+
+        // If the hues are more than 180 degrees apart, go the other way around the color wheel
+        // by moving the smaller value above 360
+        if (Math.abs(start - end) > 180f) {
+            if (start < end) {
+                start += 360f;
+            } else {
+                end += 360f;
+            }
+        }
+        // Lerp and ensure the final output is within [0, 360)
+        return lerp(fraction, start, end) % 360f;
+
+    }
+
+    private float lerp(float fraction, float start, float end) {
+        // If in flat part of curve, no interpolation necessary
+        if (start == end) {
+            return start;
+        }
+
+        // If outside bounds, use boundary value
+        if (fraction >= 1) {
+            return end;
+        }
+        if (fraction <= 0) {
+            return start;
+        }
+
+        return (end - start) * fraction + start;
+    }
+
+    private int binarySearch(float temperature) {
+        int low = 0;
+        int high = sTemperatureColorValues.length;
+
+        while (low <= high) {
+            int mid = (low + high) >>> 1;
+            float midVal = sTemperatureColorValues[mid].getTemperature();
+
+            if (midVal < temperature) {
+                low = mid + 1;  // Neither val is NaN, thisVal is smaller
+            } else if (midVal > temperature) {
+                high = mid - 1; // Neither val is NaN, thisVal is larger
+            } else {
+                int midBits = Float.floatToIntBits(midVal);
+                int keyBits = Float.floatToIntBits(temperature);
+                if (midBits == keyBits) {    // Values are equal
+                    return mid;             // Key found
+                } else if (midBits < keyBits) { // (-0.0, 0.0) or (!NaN, NaN)
+                    low = mid + 1;
+                } else {                        /* (0.0, -0.0) or (NaN, !NaN)*/
+                    high = mid - 1;
+                }
+            }
+        }
+        return -(low + 1);  // key not found.
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureTextAnimator.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureTextAnimator.java
new file mode 100644
index 0000000..8ee5ef6
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureTextAnimator.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.hvac;
+
+import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isHorizontal;
+import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isLeft;
+
+import android.annotation.NonNull;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.RotateAnimation;
+import android.view.animation.TranslateAnimation;
+import android.widget.TextSwitcher;
+
+/**
+ * Controls animating TemperatureView's text
+ */
+class TemperatureTextAnimator {
+
+    private static final DecelerateInterpolator DECELERATE_INTERPOLATOR =
+            new DecelerateInterpolator();
+    private static final AccelerateDecelerateInterpolator ACCELERATE_DECELERATE_INTERPOLATOR =
+            new AccelerateDecelerateInterpolator();
+
+    private static final int ROTATION_DEGREES = 15;
+    private static final int DURATION_MILLIS = 200;
+
+    private AnimatedTemperatureView mParent;
+    private final TextSwitcher mTextSwitcher;
+    private final String mTempFormat;
+    private final int mPivotOffset;
+    private final CharSequence mMinText;
+    private final CharSequence mMaxText;
+
+    private Animation mTextInAnimationUp;
+    private Animation mTextOutAnimationUp;
+    private Animation mTextInAnimationDown;
+    private Animation mTextOutAnimationDown;
+    private Animation mTextFadeInAnimation;
+    private Animation mTextFadeOutAnimation;
+
+    private float mLastTemp = Float.NaN;
+
+    TemperatureTextAnimator(AnimatedTemperatureView parent, TextSwitcher textSwitcher,
+            String tempFormat, int pivotOffset,
+            CharSequence minText, CharSequence maxText) {
+        mParent = parent;
+        mTextSwitcher = textSwitcher;
+        mTempFormat = tempFormat;
+        mPivotOffset = pivotOffset;
+        mMinText = minText;
+        mMaxText = maxText;
+
+        mParent.addOnLayoutChangeListener(
+                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+                        setupAnimations(mParent.getGravity()));
+    }
+
+    void setTemp(float temp) {
+        if (Float.isNaN(temp)) {
+            mTextSwitcher.setInAnimation(mTextFadeInAnimation);
+            mTextSwitcher.setOutAnimation(mTextFadeOutAnimation);
+            mTextSwitcher.setText("--");
+            mLastTemp = temp;
+            return;
+        }
+        boolean isMinValue = mParent.isMinValue(temp);
+        boolean isMaxValue = mParent.isMaxValue(temp);
+        if (Float.isNaN(mLastTemp)) {
+            mTextSwitcher.setInAnimation(mTextFadeInAnimation);
+            mTextSwitcher.setOutAnimation(mTextFadeOutAnimation);
+        } else if (!isMinValue && (isMaxValue || temp > mLastTemp)) {
+            mTextSwitcher.setInAnimation(mTextInAnimationUp);
+            mTextSwitcher.setOutAnimation(mTextOutAnimationUp);
+        } else {
+            mTextSwitcher.setInAnimation(mTextInAnimationDown);
+            mTextSwitcher.setOutAnimation(mTextOutAnimationDown);
+        }
+        CharSequence text;
+        if (isMinValue) {
+            text = mMinText;
+        } else if (isMaxValue) {
+            text = mMaxText;
+        } else {
+            text = String.format(mTempFormat, temp);
+        }
+        mTextSwitcher.setText(text);
+        mLastTemp = temp;
+    }
+
+    private void setupAnimations(int gravity) {
+        mTextFadeInAnimation = createFadeAnimation(true);
+        mTextFadeOutAnimation = createFadeAnimation(false);
+        if (!isHorizontal(gravity)) {
+            mTextInAnimationUp = createTranslateFadeAnimation(true, true);
+            mTextOutAnimationUp = createTranslateFadeAnimation(false, true);
+            mTextInAnimationDown = createTranslateFadeAnimation(true, false);
+            mTextOutAnimationDown = createTranslateFadeAnimation(false, false);
+        } else {
+            boolean isLeft = isLeft(gravity, mTextSwitcher.getLayoutDirection());
+            mTextInAnimationUp = createRotateFadeAnimation(true, isLeft, true);
+            mTextOutAnimationUp = createRotateFadeAnimation(false, isLeft, true);
+            mTextInAnimationDown = createRotateFadeAnimation(true, isLeft, false);
+            mTextOutAnimationDown = createRotateFadeAnimation(false, isLeft, false);
+        }
+    }
+
+    @NonNull
+    private Animation createFadeAnimation(boolean in) {
+        AnimationSet set = new AnimationSet(true);
+        AlphaAnimation alphaAnimation = new AlphaAnimation(in ? 0 : 1, in ? 1 : 0);
+        alphaAnimation.setDuration(DURATION_MILLIS);
+        set.addAnimation(new RotateAnimation(0, 0)); // Undo any previous rotation
+        set.addAnimation(alphaAnimation);
+        return set;
+    }
+
+    @NonNull
+    private Animation createTranslateFadeAnimation(boolean in, boolean up) {
+        AnimationSet set = new AnimationSet(true);
+        set.setInterpolator(ACCELERATE_DECELERATE_INTERPOLATOR);
+        set.setDuration(DURATION_MILLIS);
+        int fromYDelta = in ? (up ? 1 : -1) : 0;
+        int toYDelta = in ? 0 : (up ? -1 : 1);
+        set.addAnimation(
+                new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0,
+                        Animation.RELATIVE_TO_SELF, fromYDelta, Animation.RELATIVE_TO_SELF,
+                        toYDelta));
+        set.addAnimation(new AlphaAnimation(in ? 0 : 1, in ? 1 : 0));
+        return set;
+    }
+
+    @NonNull
+    private Animation createRotateFadeAnimation(boolean in, boolean isLeft, boolean up) {
+        AnimationSet set = new AnimationSet(true);
+        set.setInterpolator(DECELERATE_INTERPOLATOR);
+        set.setDuration(DURATION_MILLIS);
+
+        float degrees = isLeft == up ? -ROTATION_DEGREES : ROTATION_DEGREES;
+        int pivotX = isLeft ? -mPivotOffset : mParent.getWidth() + mPivotOffset;
+        set.addAnimation(
+                new RotateAnimation(in ? -degrees : 0f, in ? 0f : degrees, Animation.ABSOLUTE,
+                        pivotX, Animation.ABSOLUTE, 0f));
+        set.addAnimation(new AlphaAnimation(in ? 0 : 1, in ? 1 : 0));
+        return set;
+    }
+}
diff --git a/packages/CarrierDefaultApp/res/values-el/strings.xml b/packages/CarrierDefaultApp/res/values-el/strings.xml
index a13f634..016e68f 100644
--- a/packages/CarrierDefaultApp/res/values-el/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-el/strings.xml
@@ -6,7 +6,7 @@
     <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_data_notification_detail" msgid="3112125343857014825">"Επικοινωνήστε με τον πάροχο υπηρεσιών σας %s"</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>
diff --git a/packages/EasterEgg/Android.bp b/packages/EasterEgg/Android.bp
new file mode 100644
index 0000000..43ed810
--- /dev/null
+++ b/packages/EasterEgg/Android.bp
@@ -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.
+//
+
+android_app {
+    // the build system in pi-dev can't quite handle R.java in kt
+    // so we will have a mix of java and kotlin files
+    srcs: ["src/**/*.java", "src/**/*.kt"],
+
+    resource_dirs: ["res"],
+
+    name: "EasterEgg",
+
+    certificate: "platform",
+
+	sdk_version: "current",
+
+    optimize: {
+        enabled: false,
+    }
+}
diff --git a/packages/EasterEgg/Android.mk b/packages/EasterEgg/Android.mk
deleted file mode 100644
index 8fa4d6a..0000000
--- a/packages/EasterEgg/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    jsr305
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    androidx.legacy_legacy-support-v4 \
-    androidx.legacy_legacy-support-v13 \
-    androidx.dynamicanimation_dynamicanimation \
-    androidx.recyclerview_recyclerview \
-    androidx.preference_preference \
-    androidx.appcompat_appcompat \
-    androidx.legacy_legacy-preference-v14
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_PACKAGE_NAME := EasterEgg
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml
index e8ea32b..c7dd40d 100644
--- a/packages/EasterEgg/AndroidManifest.xml
+++ b/packages/EasterEgg/AndroidManifest.xml
@@ -1,8 +1,8 @@
 <?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");
+    Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
@@ -15,85 +15,28 @@
     limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.egg"
-          android:versionCode="1"
-          android:versionName="1.0">
+    package="com.android.egg"
+    android:versionCode="1"
+    android:versionName="1.0">
 
-    <uses-sdk android:minSdkVersion="26" />
+    <uses-sdk android:minSdkVersion="28" />
 
-    <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <application
+        android:icon="@drawable/icon"
+        android:label="@string/app_name">
 
-    <application android:label="@string/app_name" android:icon="@drawable/icon">
-
-        <activity android:name=".octo.Ocquarium"
-            android:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"
-            android:label="@string/app_name">
+        <activity
+            android:name=".paint.PaintActivity"
+            android:configChanges="orientation|keyboardHidden|screenSize|uiMode"
+            android:label="@string/app_name"
+            android:theme="@style/AppTheme">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
+                <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
+                <!--<category android:name="android.intent.category.LAUNCHER" />-->
                 <category android:name="com.android.internal.category.PLATLOGO" />
             </intent-filter>
         </activity>
-
-        <!-- Android N lives on inside Android O... -->
-
-        <!-- Long press the QS tile to get here -->
-        <activity android:name=".neko.NekoLand"
-                  android:theme="@android:style/Theme.Material.NoActionBar"
-                  android:label="@string/app_name">
-            <intent-filter>
-                <action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
-                <action android:name="android.intent.action.MAIN" />
-            </intent-filter>
-        </activity>
-
-        <!-- This is where the magic happens -->
-        <service
-            android:name=".neko.NekoService"
-            android:enabled="true"
-            android:permission="android.permission.BIND_JOB_SERVICE"
-            android:exported="true" >
-        </service>
-
-        <!-- Used to show over lock screen -->
-        <activity android:name=".neko.NekoLockedActivity"
-                  android:excludeFromRecents="true"
-                  android:theme="@android:style/Theme.Material.Light.Dialog.NoActionBar"
-                  android:showOnLockScreen="true" />
-
-        <!-- Used to enable easter egg -->
-        <activity android:name=".neko.NekoActivationActivity"
-            android:excludeFromRecents="true"
-            android:theme="@android:style/Theme.NoDisplay"
-            >
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-
-        <!-- The quick settings tile, disabled by default -->
-        <service
-            android:name=".neko.NekoTile"
-            android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
-            android:icon="@drawable/stat_icon"
-            android:enabled="false"
-            android:label="@string/default_tile_name">
-            <intent-filter>
-                <action android:name="android.service.quicksettings.action.QS_TILE" />
-            </intent-filter>
-        </service>
-
-        <!-- FileProvider for sending pictures -->
-        <provider
-                android:name="androidx.core.content.FileProvider"
-                android:authorities="com.android.egg.fileprovider"
-                android:grantUriPermissions="true"
-                android:exported="false">
-            <meta-data
-                    android:name="android.support.FILE_PROVIDER_PATHS"
-                    android:resource="@xml/filepaths" />
-        </provider>
     </application>
+
 </manifest>
diff --git a/packages/EasterEgg/res/color-night/toolbar_icon_color.xml b/packages/EasterEgg/res/color-night/toolbar_icon_color.xml
new file mode 100644
index 0000000..c0a8152
--- /dev/null
+++ b/packages/EasterEgg/res/color-night/toolbar_icon_color.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#FFFF3333" android:state_selected="true" />
+    <item android:color="#FFFFFFFF" />
+</selector>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/color/toolbar_icon_color.xml b/packages/EasterEgg/res/color/toolbar_icon_color.xml
new file mode 100644
index 0000000..d3247e4
--- /dev/null
+++ b/packages/EasterEgg/res/color/toolbar_icon_color.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#FFCC0000" android:state_selected="true" />
+    <item android:color="#FF000000" />
+</selector>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/drawable/back.xml b/packages/EasterEgg/res/drawable/back.xml
deleted file mode 100644
index b55d65c..0000000
--- a/packages/EasterEgg/res/drawable/back.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="back" android:fillColor="#FF000000" android:pathData="M37.1,22c-1.1,0 -1.9,0.8 -1.9,1.9v5.6c0,1.1 0.8,1.9 1.9,1.9H39v-1.9v-5.6V22H37.1z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/belly.xml b/packages/EasterEgg/res/drawable/belly.xml
deleted file mode 100644
index 8b0e9af..0000000
--- a/packages/EasterEgg/res/drawable/belly.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="belly" android:fillColor="#FF000000" android:pathData="M20.5,25c-3.6,0 -6.5,2.9 -6.5,6.5V38h13v-6.5C27,27.9 24.1,25 20.5,25z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/body.xml b/packages/EasterEgg/res/drawable/body.xml
deleted file mode 100644
index 8608720..0000000
--- a/packages/EasterEgg/res/drawable/body.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="body" android:fillColor="#FF000000" android:pathData="M9,20h30v18h-30z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/bowtie.xml b/packages/EasterEgg/res/drawable/bowtie.xml
deleted file mode 100644
index 33fa921..0000000
--- a/packages/EasterEgg/res/drawable/bowtie.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="bowtie" android:fillColor="#FF000000" android:pathData="M29,16.8l-10,5l0,-5l10,5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/cap.xml b/packages/EasterEgg/res/drawable/cap.xml
deleted file mode 100644
index d8b4cc5..0000000
--- a/packages/EasterEgg/res/drawable/cap.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="cap" android:fillColor="#FF000000" android:pathData="M27.2,3.8c-1,-0.2 -2.1,-0.3 -3.2,-0.3s-2.1,0.1 -3.2,0.3c0.2,1.3 1.5,2.2 3.2,2.2C25.6,6.1 26.9,5.1 27.2,3.8z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/collar.xml b/packages/EasterEgg/res/drawable/collar.xml
deleted file mode 100644
index 5e4d0fd..0000000
--- a/packages/EasterEgg/res/drawable/collar.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="collar" android:fillColor="#FF000000" android:pathData="M9,18.4h30v1.7h-30z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/face_spot.xml b/packages/EasterEgg/res/drawable/face_spot.xml
deleted file mode 100644
index a89fb4f..0000000
--- a/packages/EasterEgg/res/drawable/face_spot.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="face_spot" android:fillColor="#FF000000" android:pathData="M19.5,15.2a4.5,3.2 0,1 0,9 0a4.5,3.2 0,1 0,-9 0z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/food_bits.xml b/packages/EasterEgg/res/drawable/food_bits.xml
deleted file mode 100644
index 1b2bb6f..0000000
--- a/packages/EasterEgg/res/drawable/food_bits.xml
+++ /dev/null
@@ -1,33 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M19.1,34l-3.5,1.3c-1,0.4,-2.2,-0.1,-2.6,-1.1l-1.2,-3c-0.4,-1,0.1,-2.2,1.1,-2.6l3.5,-1.3c1,-0.4,2.2,0.1,2.6,1.1l1.2,3   C20.6,32.4,20.1,33.6,19.1,34z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M25.2,28.1L22.9,28c-0.8,0,-1.5,-0.7,-1.4,-1.6l0.1,-2c0,-0.8,0.7,-1.5,1.6,-1.4l2.4,0.1c0.8,0,1.5,0.7,1.4,1.6l-0.1,2   C26.8,27.5,26.1,28.1,25.2,28.1z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M18.7,23.1L16.5,23c-0.5,0,-0.9,-0.4,-0.8,-0.9l0.1,-2.2c0,-0.5,0.4,-0.9,0.9,-0.8l2.2,0.1c0.5,0,0.9,0.4,0.8,0.9   l-0.1,2.2C19.6,22.8,19.2,23.1,18.7,23.1z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M32.2,35.3l-3.6,-1.8c-1,-0.5,-1.4,-1.7,-0.9,-2.7l1.6,-3.1c0.5,-1,1.7,-1.4,2.7,-0.9l3.6,1.8c1,0.5,1.4,1.7,0.9,2.7   l-1.6,3.1C34.4,35.4,33.2,35.7,32.2,35.3z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/food_chicken.xml b/packages/EasterEgg/res/drawable/food_chicken.xml
deleted file mode 100644
index 95b2fb5..0000000
--- a/packages/EasterEgg/res/drawable/food_chicken.xml
+++ /dev/null
@@ -1,39 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M9,12v14h10V11H9z M11.7,16.3c-0.7,0,-1.3,-0.6,-1.3,-1.3s0.6,-1.3,1.3,-1.3S13,14.3,13,15S12.4,16.3,11.7,16.3z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M5.7,20.1l1.6,-3.0l-1.6,-3.0l4.4,3.0z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M19.0,6.0l-2.3,2.3l-2.7,-2.6l-2.7,2.6l-2.3,-2.3l0.0,4.0l10.0,0.0z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M9,25c0,8.3,6.7,15,15,15s15,-6.7,15,-15H9z M29.9,31.5h-11v-1h12L29.9,31.5z M31.9,29.5h-13v-1h14L31.9,29.5z M33.9,27.5   h-15v-1h16L33.9,27.5z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M27.0,38.6h2.0v6.0h-2.0z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M17.4,44.6l-2.1999998,0.0l4.4000006,-6.0l2.1999989,0.0z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/food_cookie.xml b/packages/EasterEgg/res/drawable/food_cookie.xml
deleted file mode 100644
index 74dd134..0000000
--- a/packages/EasterEgg/res/drawable/food_cookie.xml
+++ /dev/null
@@ -1,35 +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"
-    android:viewportHeight="24">
-    <group>
-        <path
-            android:fillColor="#55FFFFFF"
-            android:fillType="evenOdd"
-            android:pathData="M5.71 18.29A8.99 8.99 0 0 0 22 13c0-3-1.46-5.65-3.71-7.29A8.99 8.99 0 0 0 2 11c0 3 1.46 5.65 3.71 7.29z"/>
-        <path
-            android:fillColor="#FFFFFFFF"
-            android:fillType="evenOdd"
-            android:pathData="M7.25 19.18A8.5 8.5 0 0 0 19.19 7.24 9 9 0 0 1 7.24 19.19z"/>
-        <path
-            android:fillColor="#55FFFFFF"
-            android:pathData="M10.5 3a0.5 0.5 0 1 1 1 0v2.05a0.5 0.5 0 1 1-1 0V3zm3.1 0.42a0.5 0.5 0 0 1 0.93 0.39l-0.8 1.88A0.5 0.5 0 1 1 12.8 5.3l0.8-1.88zm2.7 1.57a0.5 0.5 0 1 1 0.71 0.7l-1.45 1.46a0.5 0.5 0 0 1-0.7-0.71l1.44-1.45zm1.9 2.5a0.5 0.5 0 0 1 0.38 0.92l-1.9 0.77a0.5 0.5 0 0 1-0.37-0.93l1.9-0.77zM19 10.5a0.5 0.5 0 1 1 0 1h-2.05a0.5 0.5 0 0 1 0-1H19zm-0.42 3.1a0.5 0.5 0 0 1-0.39 0.93l-1.88-0.8a0.5 0.5 0 1 1 0.39-0.92l1.88 0.8zm-1.57 2.7a0.5 0.5 0 1 1-0.7 0.71l-1.46-1.45a0.5 0.5 0 0 1 0.71-0.7l1.45 1.44zm-2.5 1.9a0.5 0.5 0 1 1-0.92 0.38l-0.77-1.9a0.5 0.5 0 0 1 0.93-0.37l0.77 1.9zM11.5 19a0.5 0.5 0 1 1-1 0v-2.05a0.5 0.5 0 0 1 1 0V19zm-3.1-0.42a0.5 0.5 0 0 1-0.93-0.39l0.8-1.88A0.5 0.5 0 0 1 9.2 16.7l-0.8 1.88zm-2.7-1.57a0.5 0.5 0 1 1-0.71-0.7l1.45-1.46a0.5 0.5 0 0 1 0.7 0.71L5.7 17.01zm-1.9-2.48a0.5 0.5 0 0 1-0.38-0.92l1.88-0.8a0.5 0.5 0 0 1 0.4 0.92l-1.9 0.8zM3 11.5a0.5 0.5 0 1 1 0-1h2.05a0.5 0.5 0 1 1 0 1H3zm0.42-3.1A0.5 0.5 0 0 1 3.8 7.46l1.88 0.8A0.5 0.5 0 1 1 5.3 9.2L3.42 8.4zm1.57-2.7a0.5 0.5 0 1 1 0.7-0.71l1.46 1.45a0.5 0.5 0 0 1-0.71 0.7L4.99 5.7zm2.5-1.9A0.5 0.5 0 0 1 8.4 3.41l0.77 1.9a0.5 0.5 0 0 1-0.93 0.37L7.48 3.8z"/>
-    </group>
-</vector>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/drawable/food_dish.xml b/packages/EasterEgg/res/drawable/food_dish.xml
deleted file mode 100644
index 3fff6a9..0000000
--- a/packages/EasterEgg/res/drawable/food_dish.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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M24,13.8C11.3,13.8,1,18.3,1,24c0,5.7,10.3,10.2,23,10.2S47,29.7,47,24C47,18.3,36.7,13.8,24,13.8z M33.7,26.6   c1.1,-0.6,1.8,-1.3,1.8,-2c0,-2.1,-5.2,-3.8,-11.7,-3.8s-11.7,1.7,-11.7,3.8c0,0.6,0.4,1.2,1.2,1.7c-1.7,-0.8,-2.8,-1.7,-2.8,-2.8   c0,-2.5,6,-4.5,13.4,-4.5s13.4,2,13.4,4.5C37.4,24.7,36,25.8,33.7,26.6z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/food_donut.xml b/packages/EasterEgg/res/drawable/food_donut.xml
deleted file mode 100644
index eaf831e..0000000
--- a/packages/EasterEgg/res/drawable/food_donut.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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M24,4.5c-10.5,0,-19,8.5,-19,19s8.5,19,19,19s19,-8.5,19,-19S34.5,4.5,24,4.5z M35.2,15.5l1.6,-1.1   c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1c0.2,0.3,0.1,0.6,-0.1,0.8l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1   C34.9,16.1,35,15.7,35.2,15.5z M32.7,10.7c0,-0.3,0.3,-0.5,0.6,-0.5l0.1,0c0.3,0,0.5,0.3,0.5,0.6l-0.2,2c0,0.3,-0.3,0.5,-0.6,0.5l-0.1,0   c-0.3,0,-0.5,-0.3,-0.5,-0.6L32.7,10.7z M31.7,15.1l1.5,-0.2c0.2,0,0.5,0.1,0.5,0.4l0,0.1c0,0.2,-0.1,0.5,-0.4,0.5l-1.5,0.2   c-0.2,0,-0.5,-0.1,-0.5,-0.4l0,-0.1C31.3,15.4,31.5,15.2,31.7,15.1z M28.8,10.6l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1   c0.2,0.3,0.1,0.6,-0.1,0.8l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1C28.4,11.1,28.5,10.8,28.8,10.6z M25.8,6   c0,-0.3,0.3,-0.5,0.6,-0.5l0.1,0c0.3,0,0.5,0.3,0.5,0.6l-0.2,2c0,0.3,-0.3,0.5,-0.6,0.5l-0.1,0c-0.3,0,-0.5,-0.3,-0.5,-0.6L25.8,6z    M20.7,6.5l1.9,-0.7c0.3,-0.1,0.6,0,0.7,0.3l0,0.1c0.1,0.3,0,0.6,-0.3,0.7l-1.9,0.7c-0.3,0.1,-0.6,0,-0.7,-0.3l0,-0.1   C20.3,6.9,20.4,6.6,20.7,6.5z M19.9,10.9l1.5,-0.2c0.2,0,0.5,0.1,0.5,0.4l0,0.1c0,0.2,-0.1,0.5,-0.4,0.5l-1.5,0.2   c-0.2,0,-0.5,-0.1,-0.5,-0.4l0,-0.1C19.5,11.1,19.7,10.9,19.9,10.9z M16,10.9L16,10.9c0.2,-0.3,0.4,-0.4,0.6,-0.3l1.3,0.7   c0.2,0.1,0.3,0.4,0.2,0.6L18,12c-0.1,0.2,-0.4,0.3,-0.6,0.2l-1.3,-0.7C15.9,11.4,15.8,11.1,16,10.9z M15.8,18.5c0.2,0,0.4,0.1,0.5,0.4   l0,0.1c0,0.2,-0.1,0.4,-0.4,0.5l-1.5,0.2c-0.2,0,-0.4,-0.1,-0.5,-0.4l0,-0.1c0,-0.2,0.1,-0.4,0.4,-0.5L15.8,18.5z M14,21.8l-1.6,1.1   c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1   C14.3,21.3,14.3,21.6,14,21.8z M12.4,12L12.4,12c0.3,-0.2,0.5,-0.2,0.7,-0.1l1,1.1c0.2,0.2,0.2,0.4,0,0.6L14,13.7   c-0.2,0.2,-0.4,0.2,-0.6,0l-1,-1.1C12.2,12.4,12.2,12.1,12.4,12z M8.3,24.5c0,0.3,-0.3,0.5,-0.6,0.5l-0.1,0c-0.3,0,-0.5,-0.3,-0.5,-0.6   l0.2,-2c0,-0.3,0.3,-0.5,0.6,-0.5l0.1,0c0.3,0,0.5,0.3,0.5,0.6L8.3,24.5z M8.5,16.2v-0.1c0,-0.3,0.2,-0.6,0.6,-0.6h2   c0.3,0,0.6,0.2,0.6,0.6v0.1c0,0.3,-0.2,0.6,-0.6,0.6H9C8.7,16.7,8.5,16.5,8.5,16.2z M10.3,20.7c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1   c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1c0.2,0.3,0.1,0.6,-0.1,0.8L10.3,20.7z M11.3,28.3l0,-0.1   c-0.1,-0.3,0,-0.6,0.3,-0.7l1.9,-0.7c0.3,-0.1,0.6,0,0.7,0.3l0,0.1c0.1,0.3,0,0.6,-0.3,0.7L12,28.6C11.7,28.7,11.4,28.6,11.3,28.3z    M14.4,33c0,0.2,-0.2,0.4,-0.4,0.4h-1.5c-0.2,0,-0.4,-0.2,-0.4,-0.4v-0.1c0,-0.2,0.2,-0.4,0.4,-0.4H14c0.2,0,0.4,0.2,0.4,0.4V33z M17.9,35.2   l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1   C18.2,34.7,18.2,35.1,17.9,35.2z M20.7,33.8l-0.1,0.1c-0.1,0.3,-0.5,0.4,-0.8,0.2l-1.7,-1c-0.3,-0.1,-0.4,-0.5,-0.2,-0.8l0.1,-0.1   c0.1,-0.3,0.5,-0.4,0.8,-0.2l1.7,1C20.7,33.2,20.8,33.5,20.7,33.8z M17.5,23.5c0,-3.6,2.9,-6.5,6.5,-6.5s6.5,2.9,6.5,6.5   c0,3.6,-2.9,6.5,-6.5,6.5S17.5,27.1,17.5,23.5z M27.4,35.7l-1.9,0.7c-0.3,0.1,-0.6,0,-0.7,-0.3l0,-0.1c-0.1,-0.3,0,-0.6,0.3,-0.7l1.9,-0.7   c0.3,-0.1,0.6,0,0.7,0.3l0,0.1C27.9,35.3,27.7,35.6,27.4,35.7z M29.7,32.7l-1.4,0.5c-0.2,0.1,-0.5,0,-0.5,-0.3l0,-0.1   c-0.1,-0.2,0,-0.5,0.3,-0.5l1.4,-0.5c0.2,-0.1,0.5,0,0.5,0.3l0,0.1C30,32.3,29.9,32.6,29.7,32.7z M32.8,35.5l-0.1,0.1   c-0.1,0.3,-0.5,0.4,-0.8,0.2l-1.7,-1c-0.3,-0.1,-0.4,-0.5,-0.2,-0.8l0.1,-0.1c0.1,-0.3,0.5,-0.4,0.8,-0.2l1.7,1C32.8,34.9,32.9,35.2,32.8,35.5z    M33.7,30.9c0,0.2,-0.2,0.4,-0.5,0.4l-0.1,0c-0.2,0,-0.4,-0.2,-0.4,-0.5l0.1,-1.5c0,-0.2,0.2,-0.4,0.5,-0.4l0.1,0c0.2,0,0.4,0.2,0.4,0.5   L33.7,30.9z M34.5,26.5l-1.3,0.9c-0.2,0.1,-0.5,0.1,-0.6,-0.1l-0.1,-0.1c-0.1,-0.2,-0.1,-0.5,0.1,-0.6l1.3,-0.9c0.2,-0.1,0.5,-0.1,0.6,0.1   l0.1,0.1C34.8,26.1,34.7,26.3,34.5,26.5z M35.6,20.6l-1.7,-1c-0.3,-0.1,-0.4,-0.5,-0.2,-0.8l0.1,-0.1c0.1,-0.3,0.5,-0.4,0.8,-0.2l1.7,1   c0.3,0.1,0.4,0.5,0.2,0.8l-0.1,0.1C36.2,20.6,35.8,20.7,35.6,20.6z M38.6,27.1l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1L36.1,28   c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1C38.9,26.6,38.8,27,38.6,27.1z M39,19.4l-1.5,0.2   c-0.2,0,-0.5,-0.1,-0.5,-0.4l0,-0.1c0,-0.2,0.1,-0.5,0.4,-0.5l1.5,-0.2c0.2,0,0.5,0.1,0.5,0.4l0,0.1C39.4,19.1,39.2,19.3,39,19.4z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/food_sysuituna.xml b/packages/EasterEgg/res/drawable/food_sysuituna.xml
deleted file mode 100644
index 28cf4a2..0000000
--- a/packages/EasterEgg/res/drawable/food_sysuituna.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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M46,18.4l-5.8,4.6c-3.9,-3.2,-8.9,-5.6,-14.6,-6.3l1.2,-6l-7.3,5.9C12.5,17.2,6.4,20,2,24.3l7.2,1.4L2,27   c4.3,4.2,10.4,7.1,17.3,7.6l3.1,2.5L22,34.8c7.1,0,13.5,-2.5,18.2,-6.5l5.8,4.6l-1.4,-7.2L46,18.4z M14.3,24.8l-0.6,0.6l-1.1,-1.1   l-1.1,1.1l-0.6,-0.6l1.1,-1.1l-1.1,-1.1l0.6,-0.6l1.1,1.1l1.1,-1.1l0.6,0.6l-1.1,1.1L14.3,24.8z M18.8,29.1c0.7,-0.8,1.1,-2.2,1.1,-3.8   c0,-1.6,-0.4,-3,-1.1,-3.8c1.1,0.5,1.9,2,1.9,3.8S19.9,28.5,18.8,29.1z M20.7,29.1c0.7,-0.8,1.1,-2.2,1.1,-3.8c0,-1.6,-0.4,-3,-1.1,-3.8   c1.1,0.5,1.9,2,1.9,3.8S21.8,28.5,20.7,29.1z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/foot1.xml b/packages/EasterEgg/res/drawable/foot1.xml
deleted file mode 100644
index 0d90859..0000000
--- a/packages/EasterEgg/res/drawable/foot1.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="foot1" android:fillColor="#FF000000" android:pathData="M11.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/foot2.xml b/packages/EasterEgg/res/drawable/foot2.xml
deleted file mode 100644
index 364ba0c..0000000
--- a/packages/EasterEgg/res/drawable/foot2.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="foot2" android:fillColor="#FF000000" android:pathData="M18.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/foot3.xml b/packages/EasterEgg/res/drawable/foot3.xml
deleted file mode 100644
index e3a512a..0000000
--- a/packages/EasterEgg/res/drawable/foot3.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="foot3" android:fillColor="#FF000000" android:pathData="M29.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/foot4.xml b/packages/EasterEgg/res/drawable/foot4.xml
deleted file mode 100644
index 66b78fa..0000000
--- a/packages/EasterEgg/res/drawable/foot4.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="foot4" android:fillColor="#FF000000" android:pathData="M36.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/head.xml b/packages/EasterEgg/res/drawable/head.xml
deleted file mode 100644
index df600a8..0000000
--- a/packages/EasterEgg/res/drawable/head.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="head" android:fillColor="#FF000000" android:pathData="M9,18.5c0,-8.3 6.8,-15 15,-15s15,6.7 15,15H9z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_clear.xml b/packages/EasterEgg/res/drawable/ic_clear.xml
new file mode 100644
index 0000000..489dcd2
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_clear.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="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M19,6.41l-1.41,-1.41l-5.59,5.59l-5.59,-5.59l-1.41,1.41l5.59,5.59l-5.59,5.59l1.41,1.41l5.59,-5.59l5.59,5.59l1.41,-1.41l-5.59,-5.59z"
+      android:strokeWidth="1"
+      android:fillColor="#000000"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_close.xml b/packages/EasterEgg/res/drawable/ic_close.xml
deleted file mode 100644
index 60ea36b..0000000
--- a/packages/EasterEgg/res/drawable/ic_close.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="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.0,6.41L17.59,5.0 12.0,10.59 6.41,5.0 5.0,6.41 10.59,12.0 5.0,17.59 6.41,19.0 12.0,13.41 17.59,19.0 19.0,17.59 13.41,12.0z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_dropper.xml b/packages/EasterEgg/res/drawable/ic_dropper.xml
new file mode 100644
index 0000000..2307309
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_dropper.xml
@@ -0,0 +1,39 @@
+<!--
+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="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M13.6789,5.6997L3,16.3784L3,20L4,21L7.6216,21L18.3004,10.3212L13.6789,5.6997ZM7,19L5,19L5,17L13.788,8.344L15.6561,10.212L7,19Z"
+      android:strokeWidth="1"
+      android:fillColor="#000000"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M20.9983,2.4982L21.5018,3.0017C22.0876,3.5875 22.0876,4.5373 21.5018,5.1231L18.1231,8.5018C17.5373,9.0876 16.5875,9.0876 16.0017,8.5018L15.4982,7.9983C14.9124,7.4125 14.9124,6.4627 15.4982,5.8769L18.8769,2.4982C19.4627,1.9124 20.4125,1.9124 20.9983,2.4982Z"
+      android:strokeWidth="1"
+      android:fillColor="#000000"
+      android:fillType="evenOdd"
+      android:strokeColor="#00000000"/>
+  <path
+      android:pathData="M13.8284,3l7.0711,7.0711l-2.8284,2.8284l-7.0711,-7.0711z"
+      android:strokeWidth="1"
+      android:fillColor="#000000"
+      android:fillType="evenOdd"
+      android:strokeColor="#00000000"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_hourglass.xml b/packages/EasterEgg/res/drawable/ic_hourglass.xml
new file mode 100644
index 0000000..fe4b9c4
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_hourglass.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="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12.5,11L16,7.5L16,4L8,4L8,7.5L11.5,11L11.5,13L8,16.5L8,20L16,20L16,16.5L12.5,13L12.5,11ZM6,2L18,2L18,8L17.99,8L18,8.01L14,12L18,16L17.99,16.01L18,16.01L18,22L6,22L6,16.01L6.01,16.01L6,16L10,12L6,8.01L6.01,8L6,8L6,2Z"
+      android:strokeWidth="1"
+      android:fillColor="#000000"
+      android:fillType="nonZero"
+      android:strokeColor="#00000000"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_share.xml b/packages/EasterEgg/res/drawable/ic_share.xml
deleted file mode 100644
index 8cebc7e..0000000
--- a/packages/EasterEgg/res/drawable/ic_share.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="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M18.0,16.08c-0.76,0.0 -1.4,0.3 -1.9,0.77L8.91,12.7c0.05,-0.2 0.09,-0.4 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.5,0.5 1.2,0.81 2.0,0.81 1.66,0.0 3.0,-1.34 3.0,-3.0s-1.34,-3.0 -3.0,-3.0 -3.0,1.34 -3.0,3.0c0.0,0.2 0.0,0.4 0.0,0.7L8.04,9.81C7.5,9.31 6.79,9.0 6.0,9.0c-1.66,0.0 -3.0,1.34 -3.0,3.0s1.34,3.0 3.0,3.0c0.79,0.0 1.5,-0.31 2.04,-0.81l7.12,4.16c0.0,0.21 0.0,0.43 0.0,0.65 0.0,1.61 1.31,2.92 2.92,2.92 1.61,0.0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/icon.xml b/packages/EasterEgg/res/drawable/icon.xml
index 5ce9e51..2306b7b 100644
--- a/packages/EasterEgg/res/drawable/icon.xml
+++ b/packages/EasterEgg/res/drawable/icon.xml
@@ -1,7 +1,7 @@
 <!--
-Copyright (C) 2017 The Android Open Source Project
+    Copyright (C) 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
 
@@ -13,28 +13,7 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path
-        android:pathData="M25.0,25.0m-20.5,0.0a20.5,20.5,0,1,1,41.0,0.0a20.5,20.5,0,1,1,-41.0,0.0"
-        android:fillAlpha="0.066"
-        android:fillColor="#000000"/>
-    <path
-        android:pathData="M24.0,24.0m-20.0,0.0a20.0,20.0,0,1,1,40.0,0.0a20.0,20.0,0,1,1,-40.0,0.0"
-        android:fillColor="#283593"/>
-    <path
-        android:pathData="M44,24.2010101 L33.9004889,14.101499 L14.101499,33.9004889 L24.2010101,44 C29.2525804,43.9497929 34.2887564,41.9975027 38.1431296,38.1431296 C41.9975027,34.2887564 43.9497929,29.2525804 44,24.2010101 Z"
-        android:fillColor="#1a237e"/>
-    <path
-        android:pathData="M24.0,24.0m-14.0,0.0a14.0,14.0,0,1,1,28.0,0.0a14.0,14.0,0,1,1,-28.0,0.0"
-        android:fillColor="#5c6bc0"/>
-    <path
-        android:pathData="M37.7829445,26.469236 L29.6578482,18.3441397 L18.3441397,29.6578482 L26.469236,37.7829445 C29.1911841,37.2979273 31.7972024,36.0037754 33.9004889,33.9004889 C36.0037754,31.7972024 37.2979273,29.1911841 37.7829445,26.469236 Z"
-        android:fillColor="#3f51b5"/>
-    <path
-        android:pathData="M24.0,24.0m-8.0,0.0a8.0,8.0,0,1,1,16.0,0.0a8.0,8.0,0,1,1,-16.0,0.0"
-        android:fillColor="#FFFFFF"/>
-</vector>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/icon_bg"/>
+    <foreground android:drawable="@drawable/p"/>
+</adaptive-icon>
diff --git a/packages/EasterEgg/res/drawable/icon_bg.xml b/packages/EasterEgg/res/drawable/icon_bg.xml
new file mode 100644
index 0000000..c1553ce
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/icon_bg.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="#C5E1A5" />
\ No newline at end of file
diff --git a/packages/EasterEgg/res/drawable/left_ear.xml b/packages/EasterEgg/res/drawable/left_ear.xml
deleted file mode 100644
index 2b98736..0000000
--- a/packages/EasterEgg/res/drawable/left_ear.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="left_ear" android:fillColor="#FF000000" android:pathData="M15.4,1l5.1000004,5.3l-6.3,2.8000002z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/left_ear_inside.xml b/packages/EasterEgg/res/drawable/left_ear_inside.xml
deleted file mode 100644
index 1d947ed..0000000
--- a/packages/EasterEgg/res/drawable/left_ear_inside.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="left_ear_inside" android:fillColor="#FF000000" android:pathData="M15.4,1l3.5,6.2l-4.7,1.9z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/left_eye.xml b/packages/EasterEgg/res/drawable/left_eye.xml
deleted file mode 100644
index 4dde1b6..0000000
--- a/packages/EasterEgg/res/drawable/left_eye.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="left_eye" android:fillColor="#FF000000" android:pathData="M20.5,11c0,1.7 -3,1.7 -3,0C17.5,9.3 20.5,9.3 20.5,11z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/leg1.xml b/packages/EasterEgg/res/drawable/leg1.xml
deleted file mode 100644
index d72c746..0000000
--- a/packages/EasterEgg/res/drawable/leg1.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="leg1" android:fillColor="#FF000000" android:pathData="M9,37h5v6h-5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/leg2.xml b/packages/EasterEgg/res/drawable/leg2.xml
deleted file mode 100644
index a772a87..0000000
--- a/packages/EasterEgg/res/drawable/leg2.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="leg2" android:fillColor="#FF000000" android:pathData="M16,37h5v6h-5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/leg2_shadow.xml b/packages/EasterEgg/res/drawable/leg2_shadow.xml
deleted file mode 100644
index b01bd69..0000000
--- a/packages/EasterEgg/res/drawable/leg2_shadow.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="leg2_shadow" android:fillColor="#FF000000" android:pathData="M16,37h5v3h-5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/leg3.xml b/packages/EasterEgg/res/drawable/leg3.xml
deleted file mode 100644
index d471236..0000000
--- a/packages/EasterEgg/res/drawable/leg3.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="leg3" android:fillColor="#FF000000" android:pathData="M27,37h5v6h-5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/leg4.xml b/packages/EasterEgg/res/drawable/leg4.xml
deleted file mode 100644
index e5868eb..0000000
--- a/packages/EasterEgg/res/drawable/leg4.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="leg4" android:fillColor="#FF000000" android:pathData="M34,37h5v6h-5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/mouth.xml b/packages/EasterEgg/res/drawable/mouth.xml
deleted file mode 100644
index ddcf2e8..0000000
--- a/packages/EasterEgg/res/drawable/mouth.xml
+++ /dev/null
@@ -1,27 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="mouth"
-        android:strokeColor="#FF000000"
-        android:strokeWidth="1.2"
-        android:strokeLineCap="round"
-        android:pathData="M29,14.3c-0.4,0.8 -1.3,1.4 -2.3,1.4c-1.4,0 -2.7,-1.3 -2.7,-2.7
-                          M24,13c0,1.5 -1.2,2.7 -2.7,2.7c-1,0 -1.9,-0.5 -2.3,-1.4"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/nose.xml b/packages/EasterEgg/res/drawable/nose.xml
deleted file mode 100644
index d403cd1..0000000
--- a/packages/EasterEgg/res/drawable/nose.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="nose" android:fillColor="#FF000000" android:pathData="M25.2,13c0,1.3 -2.3,1.3 -2.3,0S25.2,11.7 25.2,13z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/octo_bg.xml b/packages/EasterEgg/res/drawable/octo_bg.xml
deleted file mode 100644
index 1e46cf4..0000000
--- a/packages/EasterEgg/res/drawable/octo_bg.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <gradient android:angle="-90"
-        android:startColor="#FF205090"
-        android:endColor="#FF001040"
-        android:type="linear"
-        />
-</shape>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/drawable/p.xml b/packages/EasterEgg/res/drawable/p.xml
new file mode 100644
index 0000000..596b782
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/p.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:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+  <path
+      android:pathData="M49,65L54,65C60.075,65 65,60.075 65,54C65,47.925 60.075,43 54,43C47.925,43 43,47.925 43,54L43,108"
+      android:strokeWidth="16"
+      android:fillColor="#00000000"
+      android:strokeColor="#7CB342"
+      android:fillType="evenOdd"/>
+  <path
+      android:pathData="M51,65L54,65C60.075,65 65,60.075 65,54C65,47.925 60.075,43 54,43C47.925,43 43,47.925 43,54L43,108"
+      android:strokeWidth="8"
+      android:fillColor="#00000000"
+      android:strokeColor="#FFFFFF"
+      android:fillType="evenOdd"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/right_ear.xml b/packages/EasterEgg/res/drawable/right_ear.xml
deleted file mode 100644
index b9fb4d1..0000000
--- a/packages/EasterEgg/res/drawable/right_ear.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="right_ear" android:fillColor="#FF000000" android:pathData="M32.6,1l-5.0999985,5.3l6.299999,2.8000002z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/right_ear_inside.xml b/packages/EasterEgg/res/drawable/right_ear_inside.xml
deleted file mode 100644
index 86b6e34..0000000
--- a/packages/EasterEgg/res/drawable/right_ear_inside.xml
+++ /dev/null
@@ -1,23 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-
-    <path android:name="right_ear_inside" android:fillColor="#FF000000" android:pathData="M33.8,9.1l-4.7,-1.9l3.5,-6.2z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/right_eye.xml b/packages/EasterEgg/res/drawable/right_eye.xml
deleted file mode 100644
index a1871a6..0000000
--- a/packages/EasterEgg/res/drawable/right_eye.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="right_eye" android:fillColor="#FF000000" android:pathData="M30.5,11c0,1.7 -3,1.7 -3,0C27.5,9.3 30.5,9.3 30.5,11z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/stat_icon.xml b/packages/EasterEgg/res/drawable/stat_icon.xml
deleted file mode 100644
index 608cb20..0000000
--- a/packages/EasterEgg/res/drawable/stat_icon.xml
+++ /dev/null
@@ -1,30 +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.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M12,2C6.5,2 2,6.5 2,12c0,5.5 4.5,10 10,10s10,-4.5 10,-10C22,6.5 17.5,2 12,2zM5.5,11c0,-1.6 3,-1.6 3,0C8.5,12.7 5.5,12.7 5.5,11zM17.5,14.6c-0.6,1 -1.7,1.7 -2.9,1.7c-1.1,0 -2,-0.6 -2.6,-1.4c-0.6,0.9 -1.6,1.4 -2.7,1.4c-1.3,0 -2.3,-0.7 -2.9,-1.8c-0.2,-0.3 0,-0.7 0.3,-0.8c0.3,-0.2 0.7,0 0.8,0.3c0.3,0.7 1,1.1 1.8,1.1c0.9,0 1.6,-0.5 1.9,-1.3c-0.2,-0.2 -0.4,-0.4 -0.4,-0.7c0,-1.3 2.3,-1.3 2.3,0c0,0.3 -0.2,0.6 -0.4,0.7c0.3,0.8 1.1,1.3 1.9,1.3c0.8,0 1.5,-0.6 1.8,-1.1c0.2,-0.3 0.6,-0.4 0.9,-0.2C17.6,13.9 17.7,14.3 17.5,14.6zM15.5,11c0,-1.6 3,-1.6 3,0C18.5,12.7 15.5,12.7 15.5,11z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M5.2,1.0l4.1000004,4.2l-5.0,2.1000004z"/>
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M18.8,1.0l-4.0999994,4.2l5.000001,2.1000004z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/tail.xml b/packages/EasterEgg/res/drawable/tail.xml
deleted file mode 100644
index 0cca23c..0000000
--- a/packages/EasterEgg/res/drawable/tail.xml
+++ /dev/null
@@ -1,26 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="tail"
-        android:strokeColor="#FF000000"
-        android:strokeWidth="5"
-        android:strokeLineCap="round"
-        android:pathData="M35,35.5h5.9c2.1,0 3.8,-1.7 3.8,-3.8v-6.2"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/tail_cap.xml b/packages/EasterEgg/res/drawable/tail_cap.xml
deleted file mode 100644
index b82f6f9..0000000
--- a/packages/EasterEgg/res/drawable/tail_cap.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="tail_cap" android:fillColor="#FF000000" android:pathData="M42.2,25.5c0,-1.4 1.1,-2.5 2.5,-2.5s2.5,1.1 2.5,2.5H42.2z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/tail_shadow.xml b/packages/EasterEgg/res/drawable/tail_shadow.xml
deleted file mode 100644
index bb1ff12..0000000
--- a/packages/EasterEgg/res/drawable/tail_shadow.xml
+++ /dev/null
@@ -1,22 +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="48dp"
-        android:height="48dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path android:name="tail_shadow" android:fillColor="#FF000000" android:pathData="M40,38l0,-5l-1,0l0,5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/toolbar_bg.xml b/packages/EasterEgg/res/drawable/toolbar_bg.xml
new file mode 100644
index 0000000..0f0e702
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/toolbar_bg.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <solid android:color="@color/toolbar_bg_color" />
+</shape>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/drawable/toolbar_button_bg.xml b/packages/EasterEgg/res/drawable/toolbar_button_bg.xml
new file mode 100644
index 0000000..1b6a53e
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/toolbar_button_bg.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.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="@android:color/black" />
+            <corners android:radius="4dp" />
+        </shape>
+    </item>
+</ripple>
diff --git a/packages/EasterEgg/res/layout/activity_paint.xml b/packages/EasterEgg/res/layout/activity_paint.xml
new file mode 100644
index 0000000..a4c17af
--- /dev/null
+++ b/packages/EasterEgg/res/layout/activity_paint.xml
@@ -0,0 +1,48 @@
+<?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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:app="http://schemas.android.com/apk/res/com.android.egg"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#666"
+    tools:context=".paint.PaintActivity"
+    android:id="@+id/contentView" >
+
+    <include layout="@layout/toolbar"
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:layout_gravity="top"
+        />
+    <include layout="@layout/colors"
+        android:id="@+id/colors"
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:layout_gravity="top"
+        android:visibility="gone"
+        />
+    <include layout="@layout/brushes"
+        android:id="@+id/brushes"
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:layout_gravity="top"
+        android:visibility="gone"
+        />
+
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/layout/brushes.xml b/packages/EasterEgg/res/layout/brushes.xml
new file mode 100644
index 0000000..0c4b849
--- /dev/null
+++ b/packages/EasterEgg/res/layout/brushes.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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:gravity="left"
+    android:background="@drawable/toolbar_bg"
+    android:elevation="10dp"
+    >
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/layout/cat_view.xml b/packages/EasterEgg/res/layout/cat_view.xml
deleted file mode 100644
index 85b494d..0000000
--- a/packages/EasterEgg/res/layout/cat_view.xml
+++ /dev/null
@@ -1,82 +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.
-  -->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeightSmall"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingTop="8dp"
-    android:paddingBottom="8dp"
-    android:background="?android:attr/selectableItemBackgroundBorderless"
-    android:gravity="center_horizontal"
-    android:clipToPadding="false">
-
-    <FrameLayout
-        android:layout_width="96dp"
-        android:layout_height="wrap_content">
-
-        <ImageView
-            android:id="@android:id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:padding="10dp"
-            android:layout_gravity="center"
-            android:scaleType="fitCenter" />
-
-        <LinearLayout
-            android:id="@+id/contextGroup"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:visibility="invisible"
-            android:layout_gravity="bottom">
-
-            <ImageView
-                android:id="@android:id/shareText"
-                android:layout_width="40dp"
-                android:layout_height="40dp"
-                android:padding="8dp"
-                android:src="@drawable/ic_share"
-                android:scaleType="fitCenter"
-                android:background="#40000000"/>
-
-            <Space
-                android:layout_width="0dp"
-                android:layout_height="0dp"
-                android:layout_weight="1" />
-
-            <ImageView
-                android:id="@android:id/closeButton"
-                android:layout_width="40dp"
-                android:layout_height="40dp"
-                android:padding="4dp"
-                android:src="@drawable/ic_close"
-                android:scaleType="fitCenter"
-                android:background="#40000000"/>
-
-        </LinearLayout>
-
-    </FrameLayout>
-
-    <TextView
-        android:id="@android:id/title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceListItem"
-        android:gravity="center"/>
-</LinearLayout>
-
diff --git a/packages/EasterEgg/res/layout/colors.xml b/packages/EasterEgg/res/layout/colors.xml
new file mode 100644
index 0000000..b90f4d7
--- /dev/null
+++ b/packages/EasterEgg/res/layout/colors.xml
@@ -0,0 +1,26 @@
+<?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="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:gravity="left"
+    android:background="@drawable/toolbar_bg"
+    android:elevation="10dp"
+    >
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/layout/edit_text.xml b/packages/EasterEgg/res/layout/edit_text.xml
deleted file mode 100644
index 9f7ac802..0000000
--- a/packages/EasterEgg/res/layout/edit_text.xml
+++ /dev/null
@@ -1,30 +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.
-  -->
-
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:paddingStart="20dp"
-    android:paddingEnd="20dp">
-
-    <EditText
-        android:id="@android:id/edit"
-        android:maxLines="1"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"/>
-
-</FrameLayout>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/layout/food_layout.xml b/packages/EasterEgg/res/layout/food_layout.xml
deleted file mode 100644
index d0ca0c8..0000000
--- a/packages/EasterEgg/res/layout/food_layout.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.
-  -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="vertical"
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:background="?android:attr/selectableItemBackgroundBorderless"
-              android:paddingLeft="4dp" android:paddingRight="4dp"
-              android:paddingBottom="6dp" android:paddingTop="6dp">
-    <ImageView
-        android:layout_width="64dp"
-        android:layout_height="64dp"
-        android:id="@+id/icon"
-        android:tint="?android:attr/colorControlNormal"/>
-    <TextView android:layout_width="64dp" android:layout_height="wrap_content"
-        android:gravity="top|center_horizontal"
-        android:id="@+id/text" />
-</LinearLayout>
diff --git a/packages/EasterEgg/res/layout/neko_activity.xml b/packages/EasterEgg/res/layout/neko_activity.xml
deleted file mode 100644
index c258137..0000000
--- a/packages/EasterEgg/res/layout/neko_activity.xml
+++ /dev/null
@@ -1,25 +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.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-             android:layout_width="match_parent"
-             android:layout_height="match_parent">
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/holder"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"/>
-</FrameLayout>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/layout/toolbar.xml b/packages/EasterEgg/res/layout/toolbar.xml
new file mode 100644
index 0000000..9a5a9c5
--- /dev/null
+++ b/packages/EasterEgg/res/layout/toolbar.xml
@@ -0,0 +1,111 @@
+<?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.
+-->
+<com.android.egg.paint.CutoutAvoidingToolbar
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="50dp"
+    android:orientation="horizontal"
+    android:gravity="left"
+    android:background="@drawable/toolbar_bg"
+    android:elevation="20dp"
+    >
+
+    <Space
+        android:tag="cutoutLeft"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        />
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:tag="beforeCutout"
+        android:layout_height="match_parent"
+        android:orientation="horizontal"
+        android:layout_weight="1"
+        tools:ignore="UselessParent">
+
+        <ImageButton
+            android:id="@+id/btnBrush"
+            android:layout_width="48dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:tint="@color/toolbar_icon_color"
+            android:background="@drawable/toolbar_button_bg"
+            />
+
+        <ImageButton
+            android:id="@+id/btnColor"
+            android:layout_width="48dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:tint="@color/toolbar_icon_color"
+            android:background="@drawable/toolbar_button_bg"
+            />
+
+        <ImageButton
+            android:id="@+id/btnSample"
+            android:layout_width="48dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:background="@drawable/toolbar_button_bg"
+            android:tint="@color/toolbar_icon_color"
+            android:src="@drawable/ic_dropper" />
+
+    </LinearLayout>
+
+    <Space
+        android:tag="cutoutCenter"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        />
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:tag="afterCutout"
+        android:layout_height="match_parent"
+        android:orientation="horizontal"
+        android:layout_weight="1"
+        tools:ignore="UselessParent">
+
+        <ImageButton
+            android:id="@+id/btnZen"
+            android:layout_width="48dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:background="@drawable/toolbar_button_bg"
+            android:tint="@color/toolbar_icon_color"
+            android:src="@drawable/ic_hourglass" />
+
+        <ImageButton
+            android:id="@+id/btnClear"
+            android:layout_width="48dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:background="@drawable/toolbar_button_bg"
+            android:tint="@color/toolbar_icon_color"
+            android:src="@drawable/ic_clear" />
+
+    </LinearLayout>
+
+    <Space
+        android:tag="cutoutRight"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        />
+
+</com.android.egg.paint.CutoutAvoidingToolbar>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/values-night/colors.xml b/packages/EasterEgg/res/values-night/colors.xml
new file mode 100644
index 0000000..7c188f7
--- /dev/null
+++ b/packages/EasterEgg/res/values-night/colors.xml
@@ -0,0 +1,21 @@
+<?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>
+    <color name="toolbar_bg_color">#FF333333</color>
+    <color name="paper_color">#FF000000</color>
+    <color name="paint_color">#FFFFFFFF</color>
+</resources>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/values-night/styles.xml b/packages/EasterEgg/res/values-night/styles.xml
new file mode 100644
index 0000000..4edf692
--- /dev/null
+++ b/packages/EasterEgg/res/values-night/styles.xml
@@ -0,0 +1,21 @@
+<!--
+    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="AppTheme" parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
+        <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+        <item name="android:windowLightNavigationBar">false</item>
+    </style>
+</resources>
diff --git a/packages/EasterEgg/res/values/attrs_toolbar_view.xml b/packages/EasterEgg/res/values/attrs_toolbar_view.xml
new file mode 100644
index 0000000..ed1360f
--- /dev/null
+++ b/packages/EasterEgg/res/values/attrs_toolbar_view.xml
@@ -0,0 +1,19 @@
+<!--
+    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>
+    <declare-styleable name="ToolbarView">
+    </declare-styleable>
+</resources>
diff --git a/packages/EasterEgg/res/values/colors.xml b/packages/EasterEgg/res/values/colors.xml
new file mode 100644
index 0000000..1a5388b
--- /dev/null
+++ b/packages/EasterEgg/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?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>
+    <color name="toolbar_bg_color">#FFDDDDDD</color>
+    <color name="paper_color">#FFFFFFFF</color>
+    <color name="paint_color">#FF000000</color>
+</resources>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/values/dimens.xml b/packages/EasterEgg/res/values/dimens.xml
deleted file mode 100644
index e9dcebd..0000000
--- a/packages/EasterEgg/res/values/dimens.xml
+++ /dev/null
@@ -1,19 +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.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <dimen name="neko_display_size">64dp</dimen>
-</resources>
diff --git a/packages/EasterEgg/res/values/strings.xml b/packages/EasterEgg/res/values/strings.xml
index 61e3834..32dbc97 100644
--- a/packages/EasterEgg/res/values/strings.xml
+++ b/packages/EasterEgg/res/values/strings.xml
@@ -1,6 +1,6 @@
 <?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.
@@ -15,40 +15,5 @@
     limitations under the License.
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <string name="app_name" translatable="false">Android Easter Egg</string>
-    <string name="notification_name" translatable="false">Android Neko</string>
-    <string name="notification_channel_name" translatable="false">New cats</string>
-    <string name="default_tile_name" translatable="false">\????</string>
-    <string name="notification_title" translatable="false">A cat is here.</string>
-    <string name="default_cat_name" translatable="false">Cat #%s</string>
-    <string name="directory_name" translatable="false">Cats</string>
-    <string name="confirm_delete" translatable="false">Forget %s?</string>
-    <string-array name="food_names" translatable="false">
-        <item>Empty dish</item>
-        <item>Bits</item>
-        <item>Fish</item>
-        <item>Chicken</item>
-        <item>Treat</item>
-    </string-array>
-    <array name="food_icons">
-        <item>@drawable/food_dish</item>
-        <item>@drawable/food_bits</item>
-        <item>@drawable/food_sysuituna</item>
-        <item>@drawable/food_chicken</item>
-        <item>@drawable/food_cookie</item>
-    </array>
-    <integer-array name="food_intervals">
-        <item>0</item>
-        <item>15</item>
-        <item>30</item>
-        <item>60</item>
-        <item>120</item>
-    </integer-array>
-    <integer-array name="food_new_cat_prob">
-        <item>0</item>
-        <item>5</item>
-        <item>35</item>
-        <item>65</item>
-        <item>90</item>
-    </integer-array>
+    <string name="app_name" translatable="false">PAINT.APK</string>
 </resources>
diff --git a/packages/EasterEgg/res/values/styles.xml b/packages/EasterEgg/res/values/styles.xml
new file mode 100644
index 0000000..44e2ce5
--- /dev/null
+++ b/packages/EasterEgg/res/values/styles.xml
@@ -0,0 +1,23 @@
+<!--
+    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="AppTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen">
+        <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+        <item name="android:windowLightNavigationBar">true</item>
+    </style>
+
+</resources>
diff --git a/packages/EasterEgg/res/xml/filepaths.xml b/packages/EasterEgg/res/xml/filepaths.xml
deleted file mode 100644
index 2130025..0000000
--- a/packages/EasterEgg/res/xml/filepaths.xml
+++ /dev/null
@@ -1,19 +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.
--->
-<paths>
-    <external-path name="cats" path="Pictures/Cats" />
-</paths>
\ No newline at end of file
diff --git a/packages/EasterEgg/src/com/android/egg/neko/Cat.java b/packages/EasterEgg/src/com/android/egg/neko/Cat.java
deleted file mode 100644
index dd1bd07..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/Cat.java
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.graphics.*;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-
-import java.io.ByteArrayOutputStream;
-import java.util.Random;
-import java.util.concurrent.ThreadLocalRandom;
-
-import com.android.egg.R;
-import com.android.internal.logging.MetricsLogger;
-
-import static com.android.egg.neko.NekoLand.CHAN_ID;
-
-public class Cat extends Drawable {
-    public static final long[] PURR = {0, 40, 20, 40, 20, 40, 20, 40, 20, 40, 20, 40};
-
-    private Random mNotSoRandom;
-    private Bitmap mBitmap;
-    private long mSeed;
-    private String mName;
-    private int mBodyColor;
-    private int mFootType;
-    private boolean mBowTie;
-
-    private synchronized Random notSoRandom(long seed) {
-        if (mNotSoRandom == null) {
-            mNotSoRandom = new Random();
-            mNotSoRandom.setSeed(seed);
-        }
-        return mNotSoRandom;
-    }
-
-    public static final float frandrange(Random r, float a, float b) {
-        return (b-a)*r.nextFloat() + a;
-    }
-
-    public static final Object choose(Random r, Object...l) {
-        return l[r.nextInt(l.length)];
-    }
-
-    public static final int chooseP(Random r, int[] a) {
-        int pct = r.nextInt(1000);
-        final int stop = a.length-2;
-        int i=0;
-        while (i<stop) {
-            pct -= a[i];
-            if (pct < 0) break;
-            i+=2;
-        }
-        return a[i+1];
-    }
-
-    public static final int getColorIndex(int q, int[] a) {
-        for(int i = 1; i < a.length; i+=2) {
-            if (a[i] == q) {
-                return i/2;
-            }
-        }
-        return -1;
-    }
-
-    public static final int[] P_BODY_COLORS = {
-            180, 0xFF212121, // black
-            180, 0xFFFFFFFF, // white
-            140, 0xFF616161, // gray
-            140, 0xFF795548, // brown
-            100, 0xFF90A4AE, // steel
-            100, 0xFFFFF9C4, // buff
-            100, 0xFFFF8F00, // orange
-              5, 0xFF29B6F6, // blue..?
-              5, 0xFFFFCDD2, // pink!?
-              5, 0xFFCE93D8, // purple?!?!?
-              4, 0xFF43A047, // yeah, why not green
-              1, 0,          // ?!?!?!
-    };
-
-    public static final int[] P_COLLAR_COLORS = {
-            250, 0xFFFFFFFF,
-            250, 0xFF000000,
-            250, 0xFFF44336,
-             50, 0xFF1976D2,
-             50, 0xFFFDD835,
-             50, 0xFFFB8C00,
-             50, 0xFFF48FB1,
-             50, 0xFF4CAF50,
-    };
-
-    public static final int[] P_BELLY_COLORS = {
-            750, 0,
-            250, 0xFFFFFFFF,
-    };
-
-    public static final int[] P_DARK_SPOT_COLORS = {
-            700, 0,
-            250, 0xFF212121,
-             50, 0xFF6D4C41,
-    };
-
-    public static final int[] P_LIGHT_SPOT_COLORS = {
-            700, 0,
-            300, 0xFFFFFFFF,
-    };
-
-    private CatParts D;
-
-    public static void tint(int color, Drawable ... ds) {
-        for (Drawable d : ds) {
-            if (d != null) {
-                d.mutate().setTint(color);
-            }
-        }
-    }
-
-    public static boolean isDark(int color) {
-        final int r = (color & 0xFF0000) >> 16;
-        final int g = (color & 0x00FF00) >> 8;
-        final int b = color & 0x0000FF;
-        return (r + g + b) < 0x80;
-    }
-
-    public Cat(Context context, long seed) {
-        D = new CatParts(context);
-        mSeed = seed;
-
-        setName(context.getString(R.string.default_cat_name,
-                String.valueOf(mSeed % 1000)));
-
-        final Random nsr = notSoRandom(seed);
-
-        // body color
-        mBodyColor = chooseP(nsr, P_BODY_COLORS);
-        if (mBodyColor == 0) mBodyColor = Color.HSVToColor(new float[] {
-                nsr.nextFloat()*360f, frandrange(nsr,0.5f,1f), frandrange(nsr,0.5f, 1f)});
-
-        tint(mBodyColor, D.body, D.head, D.leg1, D.leg2, D.leg3, D.leg4, D.tail,
-                D.leftEar, D.rightEar, D.foot1, D.foot2, D.foot3, D.foot4, D.tailCap);
-        tint(0x20000000, D.leg2Shadow, D.tailShadow);
-        if (isDark(mBodyColor)) {
-            tint(0xFFFFFFFF, D.leftEye, D.rightEye, D.mouth, D.nose);
-        }
-        tint(isDark(mBodyColor) ? 0xFFEF9A9A : 0x20D50000, D.leftEarInside, D.rightEarInside);
-
-        tint(chooseP(nsr, P_BELLY_COLORS), D.belly);
-        tint(chooseP(nsr, P_BELLY_COLORS), D.back);
-        final int faceColor = chooseP(nsr, P_BELLY_COLORS);
-        tint(faceColor, D.faceSpot);
-        if (!isDark(faceColor)) {
-            tint(0xFF000000, D.mouth, D.nose);
-        }
-
-        mFootType = 0;
-        if (nsr.nextFloat() < 0.25f) {
-            mFootType = 4;
-            tint(0xFFFFFFFF, D.foot1, D.foot2, D.foot3, D.foot4);
-        } else {
-            if (nsr.nextFloat() < 0.25f) {
-                mFootType = 2;
-                tint(0xFFFFFFFF, D.foot1, D.foot3);
-            } else if (nsr.nextFloat() < 0.25f) {
-                mFootType = 3; // maybe -2 would be better? meh.
-                tint(0xFFFFFFFF, D.foot2, D.foot4);
-            } else if (nsr.nextFloat() < 0.1f) {
-                mFootType = 1;
-                tint(0xFFFFFFFF, (Drawable) choose(nsr, D.foot1, D.foot2, D.foot3, D.foot4));
-            }
-        }
-
-        tint(nsr.nextFloat() < 0.333f ? 0xFFFFFFFF : mBodyColor, D.tailCap);
-
-        final int capColor = chooseP(nsr, isDark(mBodyColor) ? P_LIGHT_SPOT_COLORS : P_DARK_SPOT_COLORS);
-        tint(capColor, D.cap);
-        //tint(chooseP(nsr, isDark(bodyColor) ? P_LIGHT_SPOT_COLORS : P_DARK_SPOT_COLORS), D.nose);
-
-        final int collarColor = chooseP(nsr, P_COLLAR_COLORS);
-        tint(collarColor, D.collar);
-        mBowTie = nsr.nextFloat() < 0.1f;
-        tint(mBowTie ? collarColor : 0, D.bowtie);
-    }
-
-    public static Cat create(Context context) {
-        return new Cat(context, Math.abs(ThreadLocalRandom.current().nextInt()));
-    }
-
-    public Notification.Builder buildNotification(Context context) {
-        final Bundle extras = new Bundle();
-        extras.putString("android.substName", context.getString(R.string.notification_name));
-        final Intent intent = new Intent(Intent.ACTION_MAIN)
-                .setClass(context, NekoLand.class)
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        return new Notification.Builder(context)
-                .setSmallIcon(Icon.createWithResource(context, R.drawable.stat_icon))
-                .setLargeIcon(createNotificationLargeIcon(context))
-                .setColor(getBodyColor())
-                .setPriority(Notification.PRIORITY_LOW)
-                .setContentTitle(context.getString(R.string.notification_title))
-                .setShowWhen(true)
-                .setCategory(Notification.CATEGORY_STATUS)
-                .setContentText(getName())
-                .setContentIntent(PendingIntent.getActivity(context, 0, intent, 0))
-                .setAutoCancel(true)
-                .setChannel(CHAN_ID)
-                .setVibrate(PURR)
-                .addExtras(extras);
-    }
-
-    public long getSeed() {
-        return mSeed;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        final int w = Math.min(canvas.getWidth(), canvas.getHeight());
-        final int h = w;
-
-        if (mBitmap == null || mBitmap.getWidth() != w || mBitmap.getHeight() != h) {
-            mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
-            final Canvas bitCanvas = new Canvas(mBitmap);
-            slowDraw(bitCanvas, 0, 0, w, h);
-        }
-        canvas.drawBitmap(mBitmap, 0, 0, null);
-    }
-
-    private void slowDraw(Canvas canvas, int x, int y, int w, int h) {
-        for (int i = 0; i < D.drawingOrder.length; i++) {
-            final Drawable d = D.drawingOrder[i];
-            if (d != null) {
-                d.setBounds(x, y, x+w, y+h);
-                d.draw(canvas);
-            }
-        }
-
-    }
-
-    public Bitmap createBitmap(int w, int h) {
-        if (mBitmap != null && mBitmap.getWidth() == w && mBitmap.getHeight() == h) {
-            return mBitmap.copy(mBitmap.getConfig(), true);
-        }
-        Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
-        slowDraw(new Canvas(result), 0, 0, w, h);
-        return result;
-    }
-
-    public static Icon recompressIcon(Icon bitmapIcon) {
-        if (bitmapIcon.getType() != Icon.TYPE_BITMAP) return bitmapIcon;
-        final Bitmap bits = bitmapIcon.getBitmap();
-        final ByteArrayOutputStream ostream = new ByteArrayOutputStream(
-                bits.getWidth() * bits.getHeight() * 2); // guess 50% compression
-        final boolean ok = bits.compress(Bitmap.CompressFormat.PNG, 100, ostream);
-        if (!ok) return null;
-        return Icon.createWithData(ostream.toByteArray(), 0, ostream.size());
-    }
-
-    public Icon createNotificationLargeIcon(Context context) {
-        final Resources res = context.getResources();
-        final int w = 2*res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
-        final int h = 2*res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
-        return recompressIcon(createIcon(context, w, h));
-    }
-
-    public Icon createIcon(Context context, int w, int h) {
-        Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
-        final Canvas canvas = new Canvas(result);
-        final Paint pt = new Paint();
-        float[] hsv = new float[3];
-        Color.colorToHSV(mBodyColor, hsv);
-        hsv[2] = (hsv[2]>0.5f)
-                ? (hsv[2] - 0.25f)
-                : (hsv[2] + 0.25f);
-        pt.setColor(Color.HSVToColor(hsv));
-        float r = w/2;
-        canvas.drawCircle(r, r, r, pt);
-        int m = w/10;
-
-        slowDraw(canvas, m, m, w-m-m, h-m-m);
-
-        return Icon.createWithBitmap(result);
-    }
-
-    @Override
-    public void setAlpha(int i) {
-
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) {
-
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    public String getName() {
-        return mName;
-    }
-
-    public void setName(String name) {
-        this.mName = name;
-    }
-
-    public int getBodyColor() {
-        return mBodyColor;
-    }
-
-    public void logAdd(Context context) {
-        logCatAction(context, "egg_neko_add");
-    }
-
-    public void logRename(Context context) {
-        logCatAction(context, "egg_neko_rename");
-    }
-
-    public void logRemove(Context context) {
-        logCatAction(context, "egg_neko_remove");
-    }
-
-    public void logShare(Context context) {
-        logCatAction(context, "egg_neko_share");
-    }
-
-    private void logCatAction(Context context, String prefix) {
-        MetricsLogger.count(context, prefix, 1);
-        MetricsLogger.histogram(context, prefix +"_color",
-                getColorIndex(mBodyColor, P_BODY_COLORS));
-        MetricsLogger.histogram(context, prefix + "_bowtie", mBowTie ? 1 : 0);
-        MetricsLogger.histogram(context, prefix + "_feet", mFootType);
-    }
-
-    public static class CatParts {
-        public Drawable leftEar;
-        public Drawable rightEar;
-        public Drawable rightEarInside;
-        public Drawable leftEarInside;
-        public Drawable head;
-        public Drawable faceSpot;
-        public Drawable cap;
-        public Drawable mouth;
-        public Drawable body;
-        public Drawable foot1;
-        public Drawable leg1;
-        public Drawable foot2;
-        public Drawable leg2;
-        public Drawable foot3;
-        public Drawable leg3;
-        public Drawable foot4;
-        public Drawable leg4;
-        public Drawable tail;
-        public Drawable leg2Shadow;
-        public Drawable tailShadow;
-        public Drawable tailCap;
-        public Drawable belly;
-        public Drawable back;
-        public Drawable rightEye;
-        public Drawable leftEye;
-        public Drawable nose;
-        public Drawable bowtie;
-        public Drawable collar;
-        public Drawable[] drawingOrder;
-
-        public CatParts(Context context) {
-            body = context.getDrawable(R.drawable.body);
-            head = context.getDrawable(R.drawable.head);
-            leg1 = context.getDrawable(R.drawable.leg1);
-            leg2 = context.getDrawable(R.drawable.leg2);
-            leg3 = context.getDrawable(R.drawable.leg3);
-            leg4 = context.getDrawable(R.drawable.leg4);
-            tail = context.getDrawable(R.drawable.tail);
-            leftEar = context.getDrawable(R.drawable.left_ear);
-            rightEar = context.getDrawable(R.drawable.right_ear);
-            rightEarInside = context.getDrawable(R.drawable.right_ear_inside);
-            leftEarInside = context.getDrawable(R.drawable.left_ear_inside);
-            faceSpot = context.getDrawable(R.drawable.face_spot);
-            cap = context.getDrawable(R.drawable.cap);
-            mouth = context.getDrawable(R.drawable.mouth);
-            foot4 = context.getDrawable(R.drawable.foot4);
-            foot3 = context.getDrawable(R.drawable.foot3);
-            foot1 = context.getDrawable(R.drawable.foot1);
-            foot2 = context.getDrawable(R.drawable.foot2);
-            leg2Shadow = context.getDrawable(R.drawable.leg2_shadow);
-            tailShadow = context.getDrawable(R.drawable.tail_shadow);
-            tailCap = context.getDrawable(R.drawable.tail_cap);
-            belly = context.getDrawable(R.drawable.belly);
-            back = context.getDrawable(R.drawable.back);
-            rightEye = context.getDrawable(R.drawable.right_eye);
-            leftEye = context.getDrawable(R.drawable.left_eye);
-            nose = context.getDrawable(R.drawable.nose);
-            collar = context.getDrawable(R.drawable.collar);
-            bowtie = context.getDrawable(R.drawable.bowtie);
-            drawingOrder = getDrawingOrder();
-        }
-        private Drawable[] getDrawingOrder() {
-            return new Drawable[] {
-                    collar,
-                    leftEar, leftEarInside, rightEar, rightEarInside,
-                    head,
-                    faceSpot,
-                    cap,
-                    leftEye, rightEye,
-                    nose, mouth,
-                    tail, tailCap, tailShadow,
-                    foot1, leg1,
-                    foot2, leg2,
-                    foot3, leg3,
-                    foot4, leg4,
-                    leg2Shadow,
-                    body, belly,
-                    bowtie
-            };
-        }
-    }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/Food.java b/packages/EasterEgg/src/com/android/egg/neko/Food.java
deleted file mode 100644
index 5c0f12e..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/Food.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-
-import com.android.egg.R;
-
-public class Food {
-    private final int mType;
-
-    private static int[] sIcons;
-    private static String[] sNames;
-
-    public Food(int type) {
-        mType = type;
-    }
-
-    public Icon getIcon(Context context) {
-        if (sIcons == null) {
-            TypedArray icons = context.getResources().obtainTypedArray(R.array.food_icons);
-            sIcons = new int[icons.length()];
-            for (int i = 0; i < sIcons.length; i++) {
-                sIcons[i] = icons.getResourceId(i, 0);
-            }
-            icons.recycle();
-        }
-        return Icon.createWithResource(context, sIcons[mType]);
-    }
-
-    public String getName(Context context) {
-        if (sNames == null) {
-            sNames = context.getResources().getStringArray(R.array.food_names);
-        }
-        return sNames[mType];
-    }
-
-    public long getInterval(Context context) {
-        return context.getResources().getIntArray(R.array.food_intervals)[mType];
-    }
-
-    public int getType() {
-        return mType;
-    }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
deleted file mode 100644
index c0b725c..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.pm.PackageManager;
-import android.util.Log;
-import android.widget.Toast;
-
-import com.android.internal.logging.MetricsLogger;
-
-public class NekoActivationActivity extends Activity {
-    private void toastUp(String s) {
-        Toast toast = Toast.makeText(this, s, Toast.LENGTH_SHORT);
-        toast.getView().setBackgroundDrawable(null);
-        toast.show();
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        final PackageManager pm = getPackageManager();
-        final ComponentName cn = new ComponentName(this, NekoTile.class);
-        if (pm.getComponentEnabledSetting(cn) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
-            if (NekoLand.DEBUG) {
-                Log.v("Neko", "Disabling tile.");
-            }
-            pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                    PackageManager.DONT_KILL_APP);
-            MetricsLogger.histogram(this, "egg_neko_enable", 0);
-            toastUp("\uD83D\uDEAB");
-        } else {
-            if (NekoLand.DEBUG) {
-                Log.v("Neko", "Enabling tile.");
-            }
-            pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
-                    PackageManager.DONT_KILL_APP);
-            MetricsLogger.histogram(this, "egg_neko_enable", 1);
-            toastUp("\uD83D\uDC31");
-        }
-        finish();
-    }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java b/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java
deleted file mode 100644
index ee89041..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import androidx.annotation.NonNull;
-import android.app.Dialog;
-import android.content.Context;
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.egg.R;
-import com.android.internal.logging.MetricsLogger;
-
-import java.util.ArrayList;
-
-public class NekoDialog extends Dialog {
-
-    private final Adapter mAdapter;
-
-    public NekoDialog(@NonNull Context context) {
-        super(context, android.R.style.Theme_Material_Dialog_NoActionBar);
-        RecyclerView view = new RecyclerView(getContext());
-        mAdapter = new Adapter(getContext());
-        view.setLayoutManager(new GridLayoutManager(getContext(), 2));
-        view.setAdapter(mAdapter);
-        final float dp = context.getResources().getDisplayMetrics().density;
-        final int pad = (int)(16*dp);
-        view.setPadding(pad, pad, pad, pad);
-        setContentView(view);
-    }
-
-    private void onFoodSelected(Food food) {
-        PrefState prefs = new PrefState(getContext());
-        int currentState = prefs.getFoodState();
-        if (currentState == 0 && food.getType() != 0) {
-            NekoService.registerJob(getContext(), food.getInterval(getContext()));
-        }
-        MetricsLogger.histogram(getContext(), "egg_neko_offered_food", food.getType());
-        prefs.setFoodState(food.getType());
-        dismiss();
-    }
-
-    private class Adapter extends RecyclerView.Adapter<Holder> {
-
-        private final Context mContext;
-        private final ArrayList<Food> mFoods = new ArrayList<>();
-
-        public Adapter(Context context) {
-            mContext = context;
-            int[] foods = context.getResources().getIntArray(R.array.food_names);
-            // skip food 0, you can't choose it
-            for (int i=1; i<foods.length; i++) {
-                mFoods.add(new Food(i));
-            }
-        }
-
-        @Override
-        public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
-            return new Holder(LayoutInflater.from(parent.getContext())
-                    .inflate(R.layout.food_layout, parent, false));
-        }
-
-        @Override
-        public void onBindViewHolder(final Holder holder, int position) {
-            final Food food = mFoods.get(position);
-            ((ImageView) holder.itemView.findViewById(R.id.icon))
-                    .setImageIcon(food.getIcon(mContext));
-            ((TextView) holder.itemView.findViewById(R.id.text))
-                    .setText(food.getName(mContext));
-            holder.itemView.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    onFoodSelected(mFoods.get(holder.getAdapterPosition()));
-                }
-            });
-        }
-
-        @Override
-        public int getItemCount() {
-            return mFoods.size();
-        }
-    }
-
-    public static class Holder extends RecyclerView.ViewHolder {
-
-        public Holder(View itemView) {
-            super(itemView);
-        }
-    }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
deleted file mode 100644
index ceb2170..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.Manifest;
-import android.app.ActionBar;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-import android.media.MediaScannerConnection;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Environment;
-import android.provider.MediaStore.Images;
-import androidx.core.content.FileProvider;
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnLongClickListener;
-import android.view.ViewGroup;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.egg.R;
-import com.android.egg.neko.PrefState.PrefsListener;
-import com.android.internal.logging.MetricsLogger;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-public class NekoLand extends Activity implements PrefsListener {
-    public static String CHAN_ID = "EGG";
-
-    public static boolean DEBUG = false;
-    public static boolean DEBUG_NOTIFICATIONS = false;
-
-    private static final int EXPORT_BITMAP_SIZE = 600;
-
-    private static final int STORAGE_PERM_REQUEST = 123;
-
-    private static boolean CAT_GEN = false;
-    private PrefState mPrefs;
-    private CatAdapter mAdapter;
-    private Cat mPendingShareCat;
-
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.neko_activity);
-        final ActionBar actionBar = getActionBar();
-        if (actionBar != null) {
-            actionBar.setLogo(Cat.create(this));
-            actionBar.setDisplayUseLogoEnabled(false);
-            actionBar.setDisplayShowHomeEnabled(true);
-        }
-
-        mPrefs = new PrefState(this);
-        mPrefs.setListener(this);
-        final RecyclerView recyclerView = findViewById(R.id.holder);
-        mAdapter = new CatAdapter();
-        recyclerView.setAdapter(mAdapter);
-        recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
-        int numCats = updateCats();
-        MetricsLogger.histogram(this, "egg_neko_visit_gallery", numCats);
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        mPrefs.setListener(null);
-    }
-
-    private int updateCats() {
-        Cat[] cats;
-        if (CAT_GEN) {
-            cats = new Cat[50];
-            for (int i = 0; i < cats.length; i++) {
-                cats[i] = Cat.create(this);
-            }
-        } else {
-            final float[] hsv = new float[3];
-            List<Cat> list = mPrefs.getCats();
-            Collections.sort(list, new Comparator<Cat>() {
-                @Override
-                public int compare(Cat cat, Cat cat2) {
-                    Color.colorToHSV(cat.getBodyColor(), hsv);
-                    float bodyH1 = hsv[0];
-                    Color.colorToHSV(cat2.getBodyColor(), hsv);
-                    float bodyH2 = hsv[0];
-                    return Float.compare(bodyH1, bodyH2);
-                }
-            });
-            cats = list.toArray(new Cat[0]);
-        }
-        mAdapter.setCats(cats);
-        return cats.length;
-    }
-
-    private void onCatClick(Cat cat) {
-        if (CAT_GEN) {
-            mPrefs.addCat(cat);
-            new AlertDialog.Builder(NekoLand.this)
-                    .setTitle("Cat added")
-                    .setPositiveButton(android.R.string.ok, null)
-                    .show();
-        } else {
-            showNameDialog(cat);
-        }
-//      noman.notify(1, cat.buildNotification(NekoLand.this).build());
-    }
-
-    private void onCatRemove(Cat cat) {
-        cat.logRemove(this);
-        mPrefs.removeCat(cat);
-    }
-
-    private void showNameDialog(final Cat cat) {
-        final Context context = new ContextThemeWrapper(this,
-                android.R.style.Theme_Material_Light_Dialog_NoActionBar);
-        // TODO: Move to XML, add correct margins.
-        View view = LayoutInflater.from(context).inflate(R.layout.edit_text, null);
-        final EditText text = (EditText) view.findViewById(android.R.id.edit);
-        text.setText(cat.getName());
-        text.setSelection(cat.getName().length());
-        final int size = context.getResources()
-                .getDimensionPixelSize(android.R.dimen.app_icon_size);
-        Drawable catIcon = cat.createIcon(this, size, size).loadDrawable(this);
-        new AlertDialog.Builder(context)
-                .setTitle(" ")
-                .setIcon(catIcon)
-                .setView(view)
-                .setPositiveButton(android.R.string.ok, new OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        cat.logRename(context);
-                        cat.setName(text.getText().toString().trim());
-                        mPrefs.addCat(cat);
-                    }
-                }).show();
-    }
-
-    @Override
-    public void onPrefsChanged() {
-        updateCats();
-    }
-
-    private class CatAdapter extends RecyclerView.Adapter<CatHolder> {
-
-        private Cat[] mCats;
-
-        public void setCats(Cat[] cats) {
-            mCats = cats;
-            notifyDataSetChanged();
-        }
-
-        @Override
-        public CatHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-            return new CatHolder(LayoutInflater.from(parent.getContext())
-                    .inflate(R.layout.cat_view, parent, false));
-        }
-
-        private void setContextGroupVisible(final CatHolder holder, boolean vis) {
-            final View group = holder.contextGroup;
-            if (vis && group.getVisibility() != View.VISIBLE) {
-                group.setAlpha(0);
-                group.setVisibility(View.VISIBLE);
-                group.animate().alpha(1.0f).setDuration(333);
-                Runnable hideAction = new Runnable() {
-                    @Override
-                    public void run() {
-                        setContextGroupVisible(holder, false);
-                    }
-                };
-                group.setTag(hideAction);
-                group.postDelayed(hideAction, 5000);
-            } else if (!vis && group.getVisibility() == View.VISIBLE) {
-                group.removeCallbacks((Runnable) group.getTag());
-                group.animate().alpha(0f).setDuration(250).withEndAction(new Runnable() {
-                    @Override
-                    public void run() {
-                        group.setVisibility(View.INVISIBLE);
-                    }
-                });
-            }
-        }
-
-        @Override
-        public void onBindViewHolder(final CatHolder holder, int position) {
-            Context context = holder.itemView.getContext();
-            final int size = context.getResources().getDimensionPixelSize(R.dimen.neko_display_size);
-            holder.imageView.setImageIcon(mCats[position].createIcon(context, size, size));
-            holder.textView.setText(mCats[position].getName());
-            holder.itemView.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    onCatClick(mCats[holder.getAdapterPosition()]);
-                }
-            });
-            holder.itemView.setOnLongClickListener(new OnLongClickListener() {
-                @Override
-                public boolean onLongClick(View v) {
-                    setContextGroupVisible(holder, true);
-                    return true;
-                }
-            });
-            holder.delete.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    setContextGroupVisible(holder, false);
-                    new AlertDialog.Builder(NekoLand.this)
-                        .setTitle(getString(R.string.confirm_delete, mCats[position].getName()))
-                        .setNegativeButton(android.R.string.cancel, null)
-                        .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog, int which) {
-                                onCatRemove(mCats[holder.getAdapterPosition()]);
-                            }
-                        })
-                        .show();
-                }
-            });
-            holder.share.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    setContextGroupVisible(holder, false);
-                    Cat cat = mCats[holder.getAdapterPosition()];
-                    if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
-                            != PackageManager.PERMISSION_GRANTED) {
-                        mPendingShareCat = cat; 
-                        requestPermissions(
-                                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
-                                STORAGE_PERM_REQUEST);
-                        return;
-                    }
-                    shareCat(cat);
-                }
-            });
-        }
-
-        @Override
-        public int getItemCount() {
-            return mCats.length;
-        }
-    }
-
-    private void shareCat(Cat cat) {
-        final File dir = new File(
-                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
-                getString(R.string.directory_name));
-        if (!dir.exists() && !dir.mkdirs()) {
-            Log.e("NekoLand", "save: error: can't create Pictures directory");
-            return;
-        }
-        final File png = new File(dir, cat.getName().replaceAll("[/ #:]+", "_") + ".png");
-        Bitmap bitmap = cat.createBitmap(EXPORT_BITMAP_SIZE, EXPORT_BITMAP_SIZE);
-        if (bitmap != null) {
-            try {
-                OutputStream os = new FileOutputStream(png);
-                bitmap.compress(Bitmap.CompressFormat.PNG, 0, os);
-                os.close();
-                MediaScannerConnection.scanFile(
-                        this,
-                        new String[] {png.toString()},
-                        new String[] {"image/png"},
-                        null);
-                Log.v("Neko", "cat file: " + png);
-                Uri uri = FileProvider.getUriForFile(this, "com.android.egg.fileprovider", png);
-                Log.v("Neko", "cat uri: " + uri);
-                Intent intent = new Intent(Intent.ACTION_SEND);
-                intent.putExtra(Intent.EXTRA_STREAM, uri);
-                intent.putExtra(Intent.EXTRA_SUBJECT, cat.getName());
-                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-                intent.setType("image/png");
-                startActivity(Intent.createChooser(intent, null));
-                cat.logShare(this);
-            } catch (IOException e) {
-                Log.e("NekoLand", "save: error: " + e);
-            }
-        }
-    }
-
-    @Override
-    public void onRequestPermissionsResult(int requestCode,
-                                           String permissions[], int[] grantResults) {
-        if (requestCode == STORAGE_PERM_REQUEST) {
-            if (mPendingShareCat != null) {
-                shareCat(mPendingShareCat);
-                mPendingShareCat = null;
-            }
-        }
-    }
-
-    private static class CatHolder extends RecyclerView.ViewHolder {
-        private final ImageView imageView;
-        private final TextView textView;
-        private final View contextGroup;
-        private final View delete;
-        private final View share;
-
-        public CatHolder(View itemView) {
-            super(itemView);
-            imageView = (ImageView) itemView.findViewById(android.R.id.icon);
-            textView = (TextView) itemView.findViewById(android.R.id.title);
-            contextGroup = itemView.findViewById(R.id.contextGroup);
-            delete = itemView.findViewById(android.R.id.closeButton);
-            share = itemView.findViewById(android.R.id.shareText);
-        }
-    }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java b/packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java
deleted file mode 100644
index e0876a4..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import androidx.annotation.Nullable;
-import android.app.Activity;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnDismissListener;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class NekoLockedActivity extends Activity implements OnDismissListener {
-
-    private NekoDialog mDialog;
-
-    @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
-
-        mDialog = new NekoDialog(this);
-        mDialog.setOnDismissListener(this);
-        mDialog.show();
-    }
-
-    @Override
-    public void onDismiss(DialogInterface dialog) {
-        finish();
-    }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
deleted file mode 100644
index 42506e6..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.app.job.JobService;
-import android.content.ComponentName;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Bundle;
-
-import java.util.List;
-import android.util.Log;
-
-import com.android.egg.R;
-
-import java.util.Random;
-
-import static com.android.egg.neko.Cat.PURR;
-import static com.android.egg.neko.NekoLand.CHAN_ID;
-
-public class NekoService extends JobService {
-
-    private static final String TAG = "NekoService";
-
-    public static int JOB_ID = 42;
-
-    public static int CAT_NOTIFICATION = 1;
-    public static int DEBUG_NOTIFICATION = 1234;
-
-    public static float CAT_CAPTURE_PROB = 1.0f; // generous
-
-    public static long SECONDS = 1000;
-    public static long MINUTES = 60 * SECONDS;
-
-    public static long INTERVAL_FLEX = 5 * MINUTES;
-
-    public static float INTERVAL_JITTER_FRAC = 0.25f;
-
-    private static void setupNotificationChannels(Context context) {
-        NotificationManager noman = context.getSystemService(NotificationManager.class);
-        NotificationChannel eggChan = new NotificationChannel(CHAN_ID,
-                context.getString(R.string.notification_channel_name),
-                NotificationManager.IMPORTANCE_DEFAULT);
-        eggChan.setSound(Uri.EMPTY, Notification.AUDIO_ATTRIBUTES_DEFAULT); // cats are quiet
-        eggChan.setVibrationPattern(PURR); // not totally quiet though
-        eggChan.setBlockableSystem(true); // unlike a real cat, you can push this one off your lap
-        eggChan.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); // cats sit in the window
-        noman.createNotificationChannel(eggChan);
-    }
-
-    @Override
-    public boolean onStartJob(JobParameters params) {
-        Log.v(TAG, "Starting job: " + String.valueOf(params));
-
-        NotificationManager noman = getSystemService(NotificationManager.class);
-        if (NekoLand.DEBUG_NOTIFICATIONS) {
-            final Bundle extras = new Bundle();
-            extras.putString("android.substName", getString(R.string.notification_name));
-            final int size = getResources()
-                    .getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
-            final Cat cat = Cat.create(this);
-            final Notification.Builder builder
-                    = cat.buildNotification(this)
-                        .setContentTitle("DEBUG")
-                        .setChannel(NekoLand.CHAN_ID)
-                        .setContentText("Ran job: " + params);
-            noman.notify(DEBUG_NOTIFICATION, builder.build());
-        }
-
-        final PrefState prefs = new PrefState(this);
-        int food = prefs.getFoodState();
-        if (food != 0) {
-            prefs.setFoodState(0); // nom
-            final Random rng = new Random();
-            if (rng.nextFloat() <= CAT_CAPTURE_PROB) {
-                Cat cat;
-                List<Cat> cats = prefs.getCats();
-                final int[] probs = getResources().getIntArray(R.array.food_new_cat_prob);
-                final float new_cat_prob = (float)((food < probs.length) ? probs[food] : 50) / 100f;
-
-                if (cats.size() == 0 || rng.nextFloat() <= new_cat_prob) {
-                    cat = Cat.create(this);
-                    prefs.addCat(cat);
-                    cat.logAdd(this);
-                    Log.v(TAG, "A new cat is here: " + cat.getName());
-                } else {
-                    cat = cats.get(rng.nextInt(cats.size()));
-                    Log.v(TAG, "A cat has returned: " + cat.getName());
-                }
-
-                final Notification.Builder builder = cat.buildNotification(this);
-                noman.notify(CAT_NOTIFICATION, builder.build());
-            }
-        }
-        cancelJob(this);
-        return false;
-    }
-
-    @Override
-    public boolean onStopJob(JobParameters jobParameters) {
-        return false;
-    }
-
-    public static void registerJobIfNeeded(Context context, long intervalMinutes) {
-        JobScheduler jss = context.getSystemService(JobScheduler.class);
-        JobInfo info = jss.getPendingJob(JOB_ID);
-        if (info == null) {
-            registerJob(context, intervalMinutes);
-        }
-    }
-
-    public static void registerJob(Context context, long intervalMinutes) {
-        setupNotificationChannels(context);
-
-        JobScheduler jss = context.getSystemService(JobScheduler.class);
-        jss.cancel(JOB_ID);
-        long interval = intervalMinutes * MINUTES;
-        long jitter = (long)(INTERVAL_JITTER_FRAC * interval);
-        interval += (long)(Math.random() * (2 * jitter)) - jitter;
-        final JobInfo jobInfo = new JobInfo.Builder(JOB_ID,
-                new ComponentName(context, NekoService.class))
-                .setPeriodic(interval, INTERVAL_FLEX)
-                .build();
-
-        Log.v(TAG, "A cat will visit in " + interval + "ms: " + String.valueOf(jobInfo));
-        jss.schedule(jobInfo);
-
-        if (NekoLand.DEBUG_NOTIFICATIONS) {
-            NotificationManager noman = context.getSystemService(NotificationManager.class);
-            noman.notify(DEBUG_NOTIFICATION, new Notification.Builder(context)
-                    .setSmallIcon(R.drawable.stat_icon)
-                    .setContentTitle(String.format("Job scheduled in %d min", (interval / MINUTES)))
-                    .setContentText(String.valueOf(jobInfo))
-                    .setPriority(Notification.PRIORITY_MIN)
-                    .setCategory(Notification.CATEGORY_SERVICE)
-                    .setChannel(NekoLand.CHAN_ID)
-                    .setShowWhen(true)
-                    .build());
-        }
-    }
-
-    public static void cancelJob(Context context) {
-        JobScheduler jss = context.getSystemService(JobScheduler.class);
-        Log.v(TAG, "Canceling job");
-        jss.cancel(JOB_ID);
-    }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java b/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
deleted file mode 100644
index 159b40a..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.content.Intent;
-import android.service.quicksettings.Tile;
-import android.service.quicksettings.TileService;
-import android.util.Log;
-
-import com.android.egg.neko.PrefState.PrefsListener;
-import com.android.internal.logging.MetricsLogger;
-
-public class NekoTile extends TileService implements PrefsListener {
-
-    private static final String TAG = "NekoTile";
-
-    private PrefState mPrefs;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mPrefs = new PrefState(this);
-    }
-
-    @Override
-    public void onStartListening() {
-        super.onStartListening();
-        mPrefs.setListener(this);
-        updateState();
-    }
-
-    @Override
-    public void onStopListening() {
-        super.onStopListening();
-        mPrefs.setListener(null);
-    }
-
-    @Override
-    public void onTileAdded() {
-        super.onTileAdded();
-        MetricsLogger.count(this, "egg_neko_tile_added", 1);
-    }
-
-    @Override
-    public void onTileRemoved() {
-        super.onTileRemoved();
-        MetricsLogger.count(this, "egg_neko_tile_removed", 1);
-    }
-
-    @Override
-    public void onPrefsChanged() {
-        updateState();
-    }
-
-    private void updateState() {
-        Tile tile = getQsTile();
-        int foodState = mPrefs.getFoodState();
-        Food food = new Food(foodState);
-        if (foodState != 0) {
-            NekoService.registerJobIfNeeded(this, food.getInterval(this));
-        }
-        tile.setIcon(food.getIcon(this));
-        tile.setLabel(food.getName(this));
-        tile.setState(foodState != 0 ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
-        tile.updateTile();
-    }
-
-    @Override
-    public void onClick() {
-        if (mPrefs.getFoodState() != 0) {
-            // there's already food loaded, let's empty it
-            MetricsLogger.count(this, "egg_neko_empty_food", 1);
-            mPrefs.setFoodState(0);
-            NekoService.cancelJob(this);
-        } else {
-            // time to feed the cats
-            if (isLocked()) {
-                if (isSecure()) {
-                    Log.d(TAG, "startActivityAndCollapse");
-                    Intent intent = new Intent(this, NekoLockedActivity.class);
-                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    startActivityAndCollapse(intent);
-                } else {
-                    unlockAndRun(new Runnable() {
-                        @Override
-                        public void run() {
-                            showNekoDialog();
-                        }
-                    });
-                }
-            } else {
-                showNekoDialog();
-            }
-        }
-    }
-
-    private void showNekoDialog() {
-        Log.d(TAG, "showNekoDialog");
-        MetricsLogger.count(this, "egg_neko_select_food", 1);
-        showDialog(new NekoDialog(this));
-    }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/PrefState.java b/packages/EasterEgg/src/com/android/egg/neko/PrefState.java
deleted file mode 100644
index bf71b19..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/PrefState.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-public class PrefState implements OnSharedPreferenceChangeListener {
-
-    private static final String FILE_NAME = "mPrefs";
-
-    private static final String FOOD_STATE = "food";
-
-    private static final String CAT_KEY_PREFIX = "cat:";
-
-    private final Context mContext;
-    private final SharedPreferences mPrefs;
-    private PrefsListener mListener;
-
-    public PrefState(Context context) {
-        mContext = context;
-        mPrefs = mContext.getSharedPreferences(FILE_NAME, 0);
-    }
-
-    // Can also be used for renaming.
-    public void addCat(Cat cat) {
-        mPrefs.edit()
-              .putString(CAT_KEY_PREFIX + String.valueOf(cat.getSeed()), cat.getName())
-              .apply();
-    }
-
-    public void removeCat(Cat cat) {
-        mPrefs.edit().remove(CAT_KEY_PREFIX + String.valueOf(cat.getSeed())).apply();
-    }
-
-    public List<Cat> getCats() {
-        ArrayList<Cat> cats = new ArrayList<>();
-        Map<String, ?> map = mPrefs.getAll();
-        for (String key : map.keySet()) {
-            if (key.startsWith(CAT_KEY_PREFIX)) {
-                long seed = Long.parseLong(key.substring(CAT_KEY_PREFIX.length()));
-                Cat cat = new Cat(mContext, seed);
-                cat.setName(String.valueOf(map.get(key)));
-                cats.add(cat);
-            }
-        }
-        return cats;
-    }
-
-    public int getFoodState() {
-        return mPrefs.getInt(FOOD_STATE, 0);
-    }
-
-    public void setFoodState(int foodState) {
-        mPrefs.edit().putInt(FOOD_STATE, foodState).apply();
-    }
-
-    public void setListener(PrefsListener listener) {
-        mListener = listener;
-        if (mListener != null) {
-            mPrefs.registerOnSharedPreferenceChangeListener(this);
-        } else {
-            mPrefs.unregisterOnSharedPreferenceChangeListener(this);
-        }
-    }
-
-    @Override
-    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-        mListener.onPrefsChanged();
-    }
-
-    public interface PrefsListener {
-        void onPrefsChanged();
-    }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java b/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java
deleted file mode 100644
index 8a06cc6..0000000
--- a/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java
+++ /dev/null
@@ -1,89 +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.egg.octo;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import com.android.egg.R;
-
-public class Ocquarium extends Activity {
-    ImageView mImageView;
-    private OctopusDrawable mOcto;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        final float dp = getResources().getDisplayMetrics().density;
-
-        getWindow().setBackgroundDrawableResource(R.drawable.octo_bg);
-
-        FrameLayout bg = new FrameLayout(this);
-        setContentView(bg);
-        bg.setAlpha(0f);
-        bg.animate().setStartDelay(500).setDuration(5000).alpha(1f).start();
-
-        mImageView = new ImageView(this);
-        bg.addView(mImageView, new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-
-        mOcto = new OctopusDrawable(getApplicationContext());
-        mOcto.setSizePx((int) (OctopusDrawable.randfrange(40f,180f) * dp));
-        mImageView.setImageDrawable(mOcto);
-
-        mImageView.setOnTouchListener(new View.OnTouchListener() {
-            boolean touching;
-            @Override
-            public boolean onTouch(View view, MotionEvent motionEvent) {
-                switch (motionEvent.getActionMasked()) {
-                    case MotionEvent.ACTION_DOWN:
-                        if (mOcto.hitTest(motionEvent.getX(), motionEvent.getY())) {
-                            touching = true;
-                            mOcto.stopDrift();
-                        }
-                        break;
-                    case MotionEvent.ACTION_MOVE:
-                        if (touching) {
-                            mOcto.moveTo(motionEvent.getX(), motionEvent.getY());
-                        }
-                        break;
-                    case MotionEvent.ACTION_UP:
-                    case MotionEvent.ACTION_CANCEL:
-                        touching = false;
-                        mOcto.startDrift();
-                        break;
-                }
-                return true;
-            }
-        });
-    }
-
-    @Override
-    protected void onPause() {
-        mOcto.stopDrift();
-        super.onPause();
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mOcto.startDrift();
-    }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/octo/OctopusDrawable.java b/packages/EasterEgg/src/com/android/egg/octo/OctopusDrawable.java
deleted file mode 100644
index 4f8c11b..0000000
--- a/packages/EasterEgg/src/com/android/egg/octo/OctopusDrawable.java
+++ /dev/null
@@ -1,436 +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.egg.octo;
-
-import android.animation.TimeAnimator;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.DashPathEffect;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.FloatValueHolder;
-
-public class OctopusDrawable extends Drawable {
-    private static float BASE_SCALE = 100f;
-    public static boolean PATH_DEBUG = false;
-
-    private static int BODY_COLOR   = 0xFF101010;
-    private static int ARM_COLOR    = 0xFF101010;
-    private static int ARM_COLOR_BACK = 0xFF000000;
-    private static int EYE_COLOR    = 0xFF808080;
-
-    private static int[] BACK_ARMS = {1, 3, 4, 6};
-    private static int[] FRONT_ARMS = {0, 2, 5, 7};
-
-    private Paint mPaint = new Paint();
-    private Arm[] mArms = new Arm[8];
-    final PointF point = new PointF();
-    private int mSizePx = 100;
-    final Matrix M = new Matrix();
-    final Matrix M_inv = new Matrix();
-    private TimeAnimator mDriftAnimation;
-    private boolean mBlinking;
-    private float[] ptmp = new float[2];
-    private float[] scaledBounds = new float[2];
-
-    public static float randfrange(float a, float b) {
-        return (float) (Math.random()*(b-a) + a);
-    }
-    public static float clamp(float v, float a, float b) {
-        return v<a?a:v>b?b:v;
-    }
-
-    public OctopusDrawable(Context context) {
-        float dp = context.getResources().getDisplayMetrics().density;
-        setSizePx((int) (100*dp));
-        mPaint.setAntiAlias(true);
-        for (int i=0; i<mArms.length; i++) {
-            final float bias = (float)i/(mArms.length-1) - 0.5f;
-            mArms[i] = new Arm(
-                    0,0, // arm will be repositioned on moveTo
-                    10f*bias + randfrange(0,20f), randfrange(20f,50f),
-                    40f*bias+randfrange(-60f,60f), randfrange(30f, 80f),
-                    randfrange(-40f,40f), randfrange(-80f,40f),
-                    14f, 2f);
-        }
-    }
-
-    public void setSizePx(int size) {
-        mSizePx = size;
-        M.setScale(mSizePx/BASE_SCALE, mSizePx/BASE_SCALE);
-        // TaperedPathStroke.setMinStep(20f*BASE_SCALE/mSizePx); // nice little floaty circles
-        TaperedPathStroke.setMinStep(8f*BASE_SCALE/mSizePx); // classic tentacles
-        M.invert(M_inv);
-    }
-
-    public void startDrift() {
-        if (mDriftAnimation == null) {
-            mDriftAnimation = new TimeAnimator();
-            mDriftAnimation.setTimeListener(new TimeAnimator.TimeListener() {
-                float MAX_VY = 35f;
-                float JUMP_VY = -100f;
-                float MAX_VX = 15f;
-                private float ax = 0f, ay = 30f;
-                private float vx, vy;
-                long nextjump = 0;
-                long unblink = 0;
-                @Override
-                public void onTimeUpdate(TimeAnimator timeAnimator, long t, long dt) {
-                    float t_sec = 0.001f * t;
-                    float dt_sec = 0.001f * dt;
-                    if (t > nextjump) {
-                        vy = JUMP_VY;
-                        nextjump = t + (long) randfrange(5000, 10000);
-                    }
-                    if (unblink > 0 && t > unblink) {
-                        setBlinking(false);
-                        unblink = 0;
-                    } else if (Math.random() < 0.001f) {
-                        setBlinking(true);
-                        unblink = t + 200;
-                    }
-
-                    ax = (float) (MAX_VX * Math.sin(t_sec*.25f));
-
-                    vx = clamp(vx + dt_sec * ax, -MAX_VX, MAX_VX);
-                    vy = clamp(vy + dt_sec * ay, -100*MAX_VY, MAX_VY);
-
-                    // oob check
-                    if (point.y - BASE_SCALE/2 > scaledBounds[1]) {
-                        vy = JUMP_VY;
-                    } else if (point.y + BASE_SCALE < 0) {
-                        vy = MAX_VY;
-                    }
-
-                    point.x = clamp(point.x + dt_sec * vx, 0, scaledBounds[0]);
-                    point.y = point.y + dt_sec * vy;
-
-                    repositionArms();
-               }
-            });
-        }
-        mDriftAnimation.start();
-    }
-
-    public void stopDrift() {
-        mDriftAnimation.cancel();
-    }
-
-    @Override
-    public void onBoundsChange(Rect bounds) {
-        final float w = bounds.width();
-        final float h = bounds.height();
-
-        lockArms(true);
-        moveTo(w/2, h/2);
-        lockArms(false);
-
-        scaledBounds[0] = w;
-        scaledBounds[1] = h;
-        M_inv.mapPoints(scaledBounds);
-    }
-
-    // real pixel coordinates
-    public void moveTo(float x, float y) {
-        point.x = x;
-        point.y = y;
-        mapPointF(M_inv, point);
-        repositionArms();
-    }
-
-    public boolean hitTest(float x, float y) {
-        ptmp[0] = x;
-        ptmp[1] = y;
-        M_inv.mapPoints(ptmp);
-        return Math.hypot(ptmp[0] - point.x, ptmp[1] - point.y) < BASE_SCALE/2;
-    }
-
-    private void lockArms(boolean l) {
-        for (Arm arm : mArms) {
-            arm.setLocked(l);
-        }
-    }
-    private void repositionArms() {
-        for (int i=0; i<mArms.length; i++) {
-            final float bias = (float)i/(mArms.length-1) - 0.5f;
-            mArms[i].setAnchor(
-                    point.x+bias*30f,point.y+26f);
-        }
-        invalidateSelf();
-    }
-
-    private void drawPupil(Canvas canvas, float x, float y, float size, boolean open,
-            Paint pt) {
-        final float r = open ? size*.33f : size * .1f;
-        canvas.drawRoundRect(x - size, y - r, x + size, y + r, r, r, pt);
-    }
-
-    @Override
-    public void draw(@NonNull Canvas canvas) {
-        canvas.save();
-        {
-            canvas.concat(M);
-
-            // arms behind
-            mPaint.setColor(ARM_COLOR_BACK);
-            for (int i : BACK_ARMS) {
-                mArms[i].draw(canvas, mPaint);
-            }
-
-            // head/body/thing
-            mPaint.setColor(EYE_COLOR);
-            canvas.drawCircle(point.x, point.y, 36f, mPaint);
-            mPaint.setColor(BODY_COLOR);
-            canvas.save();
-            {
-                canvas.clipOutRect(point.x - 61f, point.y + 8f,
-                        point.x + 61f, point.y + 12f);
-                canvas.drawOval(point.x-40f,point.y-60f,point.x+40f,point.y+40f, mPaint);
-            }
-            canvas.restore();
-
-            // eyes
-            mPaint.setColor(EYE_COLOR);
-            if (mBlinking) {
-                drawPupil(canvas, point.x - 16f, point.y - 12f, 6f, false, mPaint);
-                drawPupil(canvas, point.x + 16f, point.y - 12f, 6f, false, mPaint);
-            } else {
-                canvas.drawCircle(point.x - 16f, point.y - 12f, 6f, mPaint);
-                canvas.drawCircle(point.x + 16f, point.y - 12f, 6f, mPaint);
-            }
-
-            // too much?
-            if (false) {
-                mPaint.setColor(0xFF000000);
-                drawPupil(canvas, point.x - 16f, point.y - 12f, 5f, true, mPaint);
-                drawPupil(canvas, point.x + 16f, point.y - 12f, 5f, true, mPaint);
-            }
-
-            // arms in front
-            mPaint.setColor(ARM_COLOR);
-            for (int i : FRONT_ARMS) {
-                mArms[i].draw(canvas, mPaint);
-            }
-
-            if (PATH_DEBUG) for (Arm arm : mArms) {
-                arm.drawDebug(canvas);
-            }
-        }
-        canvas.restore();
-    }
-
-    public void setBlinking(boolean b) {
-        mBlinking = b;
-        invalidateSelf();
-    }
-
-    @Override
-    public void setAlpha(int i) {
-    }
-
-    @Override
-    public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    static Path pathMoveTo(Path p, PointF pt) {
-        p.moveTo(pt.x, pt.y);
-        return p;
-    }
-    static Path pathQuadTo(Path p, PointF p1, PointF p2) {
-        p.quadTo(p1.x, p1.y, p2.x, p2.y);
-        return p;
-    }
-
-    static void mapPointF(Matrix m, PointF point) {
-        float[] p = new float[2];
-        p[0] = point.x;
-        p[1] = point.y;
-        m.mapPoints(p);
-        point.x = p[0];
-        point.y = p[1];
-    }
-
-    private class Link  // he come to town
-            implements DynamicAnimation.OnAnimationUpdateListener {
-        final FloatValueHolder[] coords = new FloatValueHolder[2];
-        final SpringAnimation[] anims = new SpringAnimation[coords.length];
-        private float dx, dy;
-        private boolean locked = false;
-        Link next;
-
-        Link(int index, float x1, float y1, float dx, float dy) {
-            coords[0] = new FloatValueHolder(x1);
-            coords[1] = new FloatValueHolder(y1);
-            this.dx = dx;
-            this.dy = dy;
-            for (int i=0; i<coords.length; i++) {
-                anims[i] = new SpringAnimation(coords[i]);
-                anims[i].setSpring(new SpringForce()
-                        .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
-                        .setStiffness(
-                                index == 0 ? SpringForce.STIFFNESS_LOW
-                                        : index == 1 ? SpringForce.STIFFNESS_VERY_LOW
-                                                : SpringForce.STIFFNESS_VERY_LOW/2)
-                        .setFinalPosition(0f));
-                anims[i].addUpdateListener(this);
-            }
-        }
-        public void setLocked(boolean locked) {
-            this.locked = locked;
-        }
-        public PointF start() {
-            return new PointF(coords[0].getValue(), coords[1].getValue());
-        }
-        public PointF end() {
-            return new PointF(coords[0].getValue()+dx,coords[1].getValue()+dy);
-        }
-        public PointF mid() {
-            return new PointF(
-                    0.5f*dx+(coords[0].getValue()),
-                    0.5f*dy+(coords[1].getValue()));
-        }
-        public void animateTo(PointF target) {
-            if (locked) {
-                setStart(target.x, target.y);
-            } else {
-                anims[0].animateToFinalPosition(target.x);
-                anims[1].animateToFinalPosition(target.y);
-            }
-        }
-        @Override
-        public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float v, float v1) {
-            if (next != null) {
-                next.animateTo(end());
-            }
-            OctopusDrawable.this.invalidateSelf();
-        }
-
-        public void setStart(float x, float y) {
-            coords[0].setValue(x);
-            coords[1].setValue(y);
-            onAnimationUpdate(null, 0, 0);
-        }
-    }
-
-    private class Arm {
-        final Link link1, link2, link3;
-        float max, min;
-
-        public Arm(float x, float y, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3,
-                float max, float min) {
-            link1 = new Link(0, x, y, dx1, dy1);
-            link2 = new Link(1, x+dx1, y+dy1, dx2, dy2);
-            link3 = new Link(2, x+dx1+dx2, y+dy1+dy2, dx3, dy3);
-            link1.next = link2;
-            link2.next = link3;
-
-            link1.setLocked(true);
-            link2.setLocked(false);
-            link3.setLocked(false);
-
-            this.max = max;
-            this.min = min;
-        }
-
-        // when the arm is locked, it moves rigidly, without physics
-        public void setLocked(boolean locked) {
-            link2.setLocked(locked);
-            link3.setLocked(locked);
-        }
-
-        private void setAnchor(float x, float y) {
-            link1.setStart(x,y);
-        }
-
-        public Path getPath() {
-            Path p = new Path();
-            pathMoveTo(p, link1.start());
-            pathQuadTo(p, link2.start(), link2.mid());
-            pathQuadTo(p, link2.end(), link3.end());
-            return p;
-        }
-
-        public void draw(@NonNull Canvas canvas, Paint pt) {
-            final Path p = getPath();
-            TaperedPathStroke.drawPath(canvas, p, max, min, pt);
-        }
-
-        private final Paint dpt = new Paint();
-        public void drawDebug(Canvas canvas) {
-            dpt.setStyle(Paint.Style.STROKE);
-            dpt.setStrokeWidth(0.75f);
-            dpt.setStrokeCap(Paint.Cap.ROUND);
-
-            dpt.setAntiAlias(true);
-            dpt.setColor(0xFF336699);
-
-            final Path path = getPath();
-            canvas.drawPath(path, dpt);
-
-            dpt.setColor(0xFFFFFF00);
-
-            dpt.setPathEffect(new DashPathEffect(new float[] {2f, 2f}, 0f));
-
-            canvas.drawLines(new float[] {
-                    link1.end().x,   link1.end().y,
-                    link2.start().x, link2.start().y,
-
-                    link2.end().x,   link2.end().y,
-                    link3.start().x, link3.start().y,
-            }, dpt);
-            dpt.setPathEffect(null);
-
-            dpt.setColor(0xFF00CCFF);
-
-            canvas.drawLines(new float[] {
-                    link1.start().x, link1.start().y,
-                    link1.end().x,   link1.end().y,
-
-                    link2.start().x, link2.start().y,
-                    link2.end().x,   link2.end().y,
-
-                    link3.start().x, link3.start().y,
-                    link3.end().x,   link3.end().y,
-            }, dpt);
-
-            dpt.setColor(0xFFCCEEFF);
-            canvas.drawCircle(link2.start().x, link2.start().y, 2f, dpt);
-            canvas.drawCircle(link3.start().x, link3.start().y, 2f, dpt);
-
-            dpt.setStyle(Paint.Style.FILL_AND_STROKE);
-            canvas.drawCircle(link1.start().x, link1.start().y, 2f, dpt);
-            canvas.drawCircle(link2.mid().x,   link2.mid().y,   2f, dpt);
-            canvas.drawCircle(link3.end().x,   link3.end().y,   2f, dpt);
-        }
-
-    }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/octo/TaperedPathStroke.java b/packages/EasterEgg/src/com/android/egg/octo/TaperedPathStroke.java
deleted file mode 100644
index e014fbc..0000000
--- a/packages/EasterEgg/src/com/android/egg/octo/TaperedPathStroke.java
+++ /dev/null
@@ -1,55 +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.egg.octo;
-
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PathMeasure;
-import android.os.Debug;
-
-import java.util.Arrays;
-
-public class TaperedPathStroke {
-    static float sMinStepPx = 4f;
-    static PathMeasure pm = new PathMeasure();
-    static float[] pos = {0,0};
-    static float[] tan = {0,0};
-    static float lerp(float t, float a, float b) {
-        return a + t*(b-a);
-    }
-    public static void setMinStep(float px) {
-        sMinStepPx = px;
-    }
-
-    // it's the variable-width brush algorithm from the Markers app, basically
-    public static void drawPath(Canvas c, Path p, float r1, float r2, Paint pt) {
-        pm.setPath(p,false);
-        final float len = pm.getLength();
-        float t=0;
-        boolean last=false;
-        while (true) {
-            if (t>=len) {
-                t=len;
-                last=true;
-            }
-            pm.getPosTan(t, pos, tan);
-            float r = len > 0 ? lerp(t/len, r1, r2) : r1;
-            c.drawCircle(pos[0], pos[1], r, pt);
-            t += Math.max(r*0.25f, sMinStepPx); // walk forward 1/4 radius, not too small though
-            if (last) break;
-        }
-    }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt b/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt
new file mode 100644
index 0000000..4a02ee6
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt
@@ -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.
+ */
+
+package com.android.egg.paint
+
+import android.content.Context
+import android.graphics.*
+import android.graphics.PixelFormat.TRANSLUCENT
+import android.graphics.drawable.Drawable
+import android.util.DisplayMetrics
+
+class BrushPropertyDrawable : Drawable {
+    val framePaint = Paint(Paint.ANTI_ALIAS_FLAG).also {
+        it.color = Color.BLACK
+        it.style = Paint.Style.FILL
+    }
+    val wellPaint = Paint(Paint.ANTI_ALIAS_FLAG).also {
+        it.color = Color.RED
+        it.style = Paint.Style.FILL
+    }
+
+    constructor(context: Context) {
+        _size = (24 * context.resources.displayMetrics.density).toInt()
+    }
+
+    private var _size = 24
+    private var _scale = 1f
+
+    fun setFrameColor(color: Int) {
+        framePaint.color = color
+        invalidateSelf()
+    }
+
+    fun setWellColor(color: Int) {
+        wellPaint.color = color
+        invalidateSelf()
+    }
+
+    fun setWellScale(scale: Float) {
+        _scale = scale
+        invalidateSelf()
+    }
+
+    override fun getIntrinsicWidth(): Int {
+        return _size
+    }
+
+    override fun getIntrinsicHeight(): Int {
+        return _size
+    }
+
+    override fun draw(c: Canvas?) {
+        c?.let {
+            val w = bounds.width().toFloat()
+            val h = bounds.height().toFloat()
+            val inset = _size / 12 // 2dp in a 24x24 icon
+            val r = Math.min(w, h) / 2
+
+            c.drawCircle(w/2, h/2, (r - inset) * _scale + 1 , wellPaint)
+
+            val p = Path()
+            p.addCircle(w/2, h/2, r, Path.Direction.CCW)
+            p.addCircle(w/2, h/2, r - inset, Path.Direction.CW)
+            c.drawPath(p, framePaint)
+        }
+    }
+
+    override fun setAlpha(p0: Int) {
+        //
+    }
+
+    override fun getOpacity(): Int {
+        return TRANSLUCENT
+    }
+
+    override fun setColorFilter(p0: ColorFilter?) {
+        //
+    }
+
+}
\ No newline at end of file
diff --git a/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt b/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt
new file mode 100644
index 0000000..164fc5a
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.egg.paint
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.*
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.widget.LinearLayout
+
+class CutoutAvoidingToolbar : LinearLayout {
+    private var _insets: WindowInsets? = null
+
+    constructor(context: Context) : super(context) {
+        init(null, 0)
+    }
+
+    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+        init(attrs, 0)
+    }
+
+    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
+        init(attrs, defStyle)
+    }
+
+    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+        super.onSizeChanged(w, h, oldw, oldh)
+        adjustLayout()
+    }
+
+    override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets {
+        _insets = insets
+        adjustLayout()
+        return super.onApplyWindowInsets(insets)
+    }
+
+    fun adjustLayout() {
+        _insets?.displayCutout?.boundingRects?.let {
+            var cutoutCenter = 0
+            var cutoutLeft = 0
+            var cutoutRight = 0
+
+            // collect at most three cutouts
+            for (r in it) {
+                if (r.top > 0) continue
+
+                if (r.left == left) {
+                    cutoutLeft = r.width()
+                } else if (r.right == right) {
+                    cutoutRight = r.width()
+                } else {
+                    cutoutCenter = r.width()
+                }
+            }
+
+            // apply to layout
+            (findViewWithTag("cutoutLeft") as View?)?.let {
+                it.layoutParams = LayoutParams(cutoutLeft, MATCH_PARENT)
+            }
+            (findViewWithTag("cutoutCenter") as View?)?.let {
+                it.layoutParams = LayoutParams(cutoutCenter, MATCH_PARENT)
+            }
+            (findViewWithTag("cutoutRight") as View?)?.let {
+                it.layoutParams = LayoutParams(cutoutRight, MATCH_PARENT)
+            }
+
+            requestLayout()
+        }
+    }
+
+    private fun init(attrs: AttributeSet?, defStyle: Int) {
+    }
+
+}
diff --git a/packages/EasterEgg/src/com/android/egg/paint/PaintActivity.java b/packages/EasterEgg/src/com/android/egg/paint/PaintActivity.java
new file mode 100644
index 0000000..ac47fbd
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/PaintActivity.java
@@ -0,0 +1,351 @@
+/*
+ * 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.egg.paint;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.OvershootInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.Magnifier;
+
+import com.android.egg.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.stream.IntStream;
+
+public class PaintActivity extends Activity {
+    private static final float MAX_BRUSH_WIDTH_DP = 100f;
+    private static final float MIN_BRUSH_WIDTH_DP = 1f;
+
+    private static final int NUM_BRUSHES = 6;
+    private static final int NUM_COLORS = 6;
+
+    private Painting painting = null;
+    private CutoutAvoidingToolbar toolbar = null;
+    private LinearLayout brushes = null;
+    private LinearLayout colors = null;
+    private Magnifier magnifier = null;
+    private boolean sampling = false;
+
+    private View.OnClickListener buttonHandler = new View.OnClickListener() {
+        @Override
+        public void onClick(View view) {
+            switch (view.getId()) {
+                case R.id.btnBrush:
+                    view.setSelected(true);
+                    hideToolbar(colors);
+                    toggleToolbar(brushes);
+                    break;
+                case R.id.btnColor:
+                    view.setSelected(true);
+                    hideToolbar(brushes);
+                    toggleToolbar(colors);
+                    break;
+                case R.id.btnClear:
+                    painting.clear();
+                    break;
+                case R.id.btnSample:
+                    sampling = true;
+                    view.setSelected(true);
+                    break;
+                case R.id.btnZen:
+                    painting.setZenMode(!painting.getZenMode());
+                    view.animate()
+                            .setStartDelay(200)
+                            .setInterpolator(new OvershootInterpolator())
+                            .rotation(painting.getZenMode() ? 0f : 90f);
+                    break;
+            }
+        }
+    };
+
+    private void showToolbar(View bar) {
+        if (bar.getVisibility() != View.GONE) return;
+        bar.setVisibility(View.VISIBLE);
+        bar.setTranslationY(toolbar.getHeight()/2);
+        bar.animate()
+                .translationY(toolbar.getHeight())
+                .alpha(1f)
+                .setDuration(220)
+                .start();
+    }
+
+    private void hideToolbar(View bar) {
+        if (bar.getVisibility() != View.VISIBLE) return;
+        bar.animate()
+                .translationY(toolbar.getHeight()/2)
+                .alpha(0f)
+                .setDuration(150)
+                .withEndAction(new Runnable() {
+                    @Override
+                    public void run() {
+                        bar.setVisibility(View.GONE);
+                    }
+                })
+                .start();
+    }
+
+    private void toggleToolbar(View bar) {
+        if (bar.getVisibility() == View.VISIBLE) {
+            hideToolbar(bar);
+        } else {
+            showToolbar(bar);
+        }
+    }
+
+    private BrushPropertyDrawable widthButtonDrawable;
+    private BrushPropertyDrawable colorButtonDrawable;
+    private float maxBrushWidth, minBrushWidth;
+    private int nightMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
+
+    static final float lerp(float f, float a, float b) {
+        return a + (b-a) * f;
+    }
+
+    void setupViews(Painting oldPainting) {
+        setContentView(R.layout.activity_paint);
+
+        painting = oldPainting != null ? oldPainting : new Painting(this);
+        ((FrameLayout) findViewById(R.id.contentView)).addView(painting,
+                new FrameLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT));
+
+        painting.setPaperColor(getColor(R.color.paper_color));
+        painting.setPaintColor(getColor(R.color.paint_color));
+
+        toolbar = findViewById(R.id.toolbar);
+        brushes = findViewById(R.id.brushes);
+        colors = findViewById(R.id.colors);
+
+        magnifier = new Magnifier(painting);
+
+        painting.setOnTouchListener(
+                new View.OnTouchListener() {
+                    @Override
+                    public boolean onTouch(View view, MotionEvent event) {
+                        switch (event.getActionMasked()) {
+                            case ACTION_DOWN:
+                            case ACTION_MOVE:
+                                if (sampling) {
+                                    magnifier.show(event.getX(), event.getY());
+                                    colorButtonDrawable.setWellColor(
+                                            painting.sampleAt(event.getX(), event.getY()));
+                                    return true;
+                                }
+                                break;
+                            case ACTION_CANCEL:
+                                if (sampling) {
+                                    findViewById(R.id.btnSample).setSelected(false);
+                                    sampling = false;
+                                    magnifier.dismiss();
+                                }
+                                break;
+                            case ACTION_UP:
+                                if (sampling) {
+                                    findViewById(R.id.btnSample).setSelected(false);
+                                    sampling = false;
+                                    magnifier.dismiss();
+                                    painting.setPaintColor(
+                                            painting.sampleAt(event.getX(), event.getY()));
+                                    refreshBrushAndColor();
+                                }
+                                break;
+                        }
+                        return false; // allow view to continue handling
+                    }
+                });
+
+        findViewById(R.id.btnBrush).setOnClickListener(buttonHandler);
+        findViewById(R.id.btnColor).setOnClickListener(buttonHandler);
+        findViewById(R.id.btnClear).setOnClickListener(buttonHandler);
+        findViewById(R.id.btnSample).setOnClickListener(buttonHandler);
+        findViewById(R.id.btnZen).setOnClickListener(buttonHandler);
+
+        findViewById(R.id.btnColor).setOnLongClickListener(new View.OnLongClickListener() {
+            @Override
+            public boolean onLongClick(View view) {
+                colors.removeAllViews();
+                showToolbar(colors);
+                refreshBrushAndColor();
+                return true;
+            }
+        });
+
+        findViewById(R.id.btnClear).setOnLongClickListener(new View.OnLongClickListener() {
+            @Override
+            public boolean onLongClick(View view) {
+                painting.invertContents();
+                return true;
+            }
+        });
+
+        widthButtonDrawable = new BrushPropertyDrawable(this);
+        widthButtonDrawable.setFrameColor(getColor(R.color.toolbar_icon_color));
+        colorButtonDrawable = new BrushPropertyDrawable(this);
+        colorButtonDrawable.setFrameColor(getColor(R.color.toolbar_icon_color));
+
+        ((ImageButton) findViewById(R.id.btnBrush)).setImageDrawable(widthButtonDrawable);
+        ((ImageButton) findViewById(R.id.btnColor)).setImageDrawable(colorButtonDrawable);
+
+        refreshBrushAndColor();
+    }
+
+    private void refreshBrushAndColor() {
+        final LinearLayout.LayoutParams button_lp = new LinearLayout.LayoutParams(
+                0, ViewGroup.LayoutParams.WRAP_CONTENT);
+        button_lp.weight = 1f;
+        if (brushes.getChildCount() == 0) {
+            for (int i = 0; i < NUM_BRUSHES; i++) {
+                final BrushPropertyDrawable icon = new BrushPropertyDrawable(this);
+                icon.setFrameColor(getColor(R.color.toolbar_icon_color));
+                // exponentially increasing brush size
+                final float width = lerp(
+                        (float) Math.pow((float) i / NUM_BRUSHES, 2f), minBrushWidth,
+                        maxBrushWidth);
+                icon.setWellScale(width / maxBrushWidth);
+                icon.setWellColor(getColor(R.color.toolbar_icon_color));
+                final ImageButton button = new ImageButton(this);
+                button.setImageDrawable(icon);
+                button.setBackground(getDrawable(R.drawable.toolbar_button_bg));
+                button.setOnClickListener(
+                        new View.OnClickListener() {
+                            @Override
+                            public void onClick(View view) {
+                                brushes.setSelected(false);
+                                hideToolbar(brushes);
+                                painting.setBrushWidth(width);
+                                refreshBrushAndColor();
+                            }
+                        });
+                brushes.addView(button, button_lp);
+            }
+        }
+
+        if (colors.getChildCount() == 0) {
+            final Palette pal = new Palette(NUM_COLORS);
+            for (final int c : IntStream.concat(
+                    IntStream.of(Color.BLACK, Color.WHITE),
+                    Arrays.stream(pal.getColors())
+            ).toArray()) {
+                final BrushPropertyDrawable icon = new BrushPropertyDrawable(this);
+                icon.setFrameColor(getColor(R.color.toolbar_icon_color));
+                icon.setWellColor(c);
+                final ImageButton button = new ImageButton(this);
+                button.setImageDrawable(icon);
+                button.setBackground(getDrawable(R.drawable.toolbar_button_bg));
+                button.setOnClickListener(
+                    new View.OnClickListener() {
+                        @Override
+                        public void onClick(View view) {
+                            colors.setSelected(false);
+                            hideToolbar(colors);
+                            painting.setPaintColor(c);
+                            refreshBrushAndColor();
+                        }
+                    });
+                colors.addView(button, button_lp);
+            }
+        }
+
+        widthButtonDrawable.setWellScale(painting.getBrushWidth() / maxBrushWidth);
+        widthButtonDrawable.setWellColor(painting.getPaintColor());
+        colorButtonDrawable.setWellColor(painting.getPaintColor());
+    }
+
+    private void refreshNightMode(Configuration config) {
+        int newNightMode =
+                (config.uiMode & Configuration.UI_MODE_NIGHT_MASK);
+        if (nightMode != newNightMode) {
+            if (nightMode != Configuration.UI_MODE_NIGHT_UNDEFINED) {
+                painting.invertContents();
+
+                ((ViewGroup) painting.getParent()).removeView(painting);
+                setupViews(painting);
+
+                final View decorView = getWindow().getDecorView();
+                int decorSUIV = decorView.getSystemUiVisibility();
+
+                if (newNightMode == Configuration.UI_MODE_NIGHT_YES) {
+                    decorView.setSystemUiVisibility(
+                            decorSUIV & ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+                } else {
+                    decorView.setSystemUiVisibility(
+                            decorSUIV | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+                }
+            }
+            nightMode = newNightMode;
+        }
+    }
+
+    public PaintActivity() {
+
+    }
+
+    @Override
+    public void onTrimMemory(int level) {
+        super.onTrimMemory(level);
+
+        painting.onTrimMemory();
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        refreshNightMode(newConfig);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        WindowManager.LayoutParams lp = getWindow().getAttributes();
+        lp.flags = lp.flags
+                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+        getWindow().setAttributes(lp);
+
+        maxBrushWidth = MAX_BRUSH_WIDTH_DP * getResources().getDisplayMetrics().density;
+        minBrushWidth = MIN_BRUSH_WIDTH_DP * getResources().getDisplayMetrics().density;
+
+        setupViews(null);
+        refreshNightMode(getResources().getConfiguration());
+    }
+
+    @Override
+    public void onPostResume() {
+        super.onPostResume();
+    }
+
+}
diff --git a/packages/EasterEgg/src/com/android/egg/paint/Painting.kt b/packages/EasterEgg/src/com/android/egg/paint/Painting.kt
new file mode 100644
index 0000000..a4a3d3d
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/Painting.kt
@@ -0,0 +1,358 @@
+/*
+ * 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.egg.paint
+
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.*
+import android.provider.Settings
+import android.util.AttributeSet
+import android.util.DisplayMetrics
+import android.view.MotionEvent
+import android.view.View
+import android.view.WindowInsets
+import java.util.concurrent.TimeUnit
+import android.util.Log
+import android.provider.Settings.System
+
+import org.json.JSONObject
+
+fun hypot(x: Float, y: Float): Float {
+    return Math.hypot(x.toDouble(), y.toDouble()).toFloat()
+}
+
+fun invlerp(x: Float, a: Float, b: Float): Float {
+    return if (b > a) {
+        (x - a) / (b - a)
+    } else 1.0f
+}
+
+public class Painting : View, SpotFilter.Plotter {
+    companion object {
+        val FADE_MINS = TimeUnit.MINUTES.toMillis(3) // about how long a drawing should last
+        val ZEN_RATE = TimeUnit.SECONDS.toMillis(2)  // how often to apply the fade
+        val ZEN_FADE = Math.max(1f, ZEN_RATE / FADE_MINS * 255f)
+
+        val FADE_TO_WHITE_CF = ColorMatrixColorFilter(ColorMatrix(floatArrayOf(
+                1f, 0f, 0f, 0f, ZEN_FADE,
+                0f, 1f, 0f, 0f, ZEN_FADE,
+                0f, 0f, 1f, 0f, ZEN_FADE,
+                0f, 0f, 0f, 1f, 0f
+        )))
+
+        val FADE_TO_BLACK_CF = ColorMatrixColorFilter(ColorMatrix(floatArrayOf(
+                1f, 0f, 0f, 0f, -ZEN_FADE,
+                0f, 1f, 0f, 0f, -ZEN_FADE,
+                0f, 0f, 1f, 0f, -ZEN_FADE,
+                0f, 0f, 0f, 1f, 0f
+        )))
+
+        val INVERT_CF = ColorMatrixColorFilter(ColorMatrix(floatArrayOf(
+                -1f, 0f, 0f, 0f, 255f,
+                0f, -1f, 0f, 0f, 255f,
+                0f, 0f, -1f, 0f, 255f,
+                0f, 0f, 0f, 1f, 0f
+        )))
+
+        val TOUCH_STATS = "touch.stats" // Settings.System key
+    }
+
+    var devicePressureMin = 0f; // ideal value
+    var devicePressureMax = 1f; // ideal value
+
+    var zenMode = true
+        set(value) {
+            if (field != value) {
+                field = value
+                removeCallbacks(fadeRunnable)
+                if (value && isAttachedToWindow) {
+                    handler.postDelayed(fadeRunnable, ZEN_RATE)
+                }
+            }
+        }
+
+    var bitmap: Bitmap? = null
+    var paperColor : Int = 0xFFFFFFFF.toInt()
+
+    private var _paintCanvas: Canvas? = null
+    private val _bitmapLock = Object()
+    
+    private var _drawPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+    private var _lastX = 0f
+    private var _lastY = 0f
+    private var _lastR = 0f
+    private var _insets: WindowInsets? = null
+
+    private var _brushWidth = 100f
+
+    private var _filter = SpotFilter(10, 0.5f, 0.9f, this)
+
+    private val fadeRunnable = object : Runnable {
+        private val pt = Paint()
+        override fun run() {
+            val c = _paintCanvas
+            if (c != null) {
+                pt.colorFilter =
+                    if (paperColor.and(0xFF) > 0x80)
+                        FADE_TO_WHITE_CF
+                    else
+                        FADE_TO_BLACK_CF
+
+                synchronized(_bitmapLock) {
+                    c.drawBitmap(bitmap, 0f, 0f, pt)
+                }
+                invalidate()
+            }
+            postDelayed(this, ZEN_RATE)
+        }
+    }
+
+    constructor(context: Context) : super(context) {
+        init(null, 0)
+    }
+
+    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+        init(attrs, 0)
+    }
+
+    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
+        init(attrs, defStyle)
+    }
+
+    private fun init(attrs: AttributeSet?, defStyle: Int) {
+        loadDevicePressureData()
+    }
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+
+        setupBitmaps()
+
+        if (zenMode) {
+            handler.postDelayed(fadeRunnable, ZEN_RATE)
+        }
+    }
+
+    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+        super.onSizeChanged(w, h, oldw, oldh)
+        setupBitmaps()
+    }
+
+    override fun onDetachedFromWindow() {
+        if (zenMode) {
+            removeCallbacks(fadeRunnable)
+        }
+        super.onDetachedFromWindow()
+    }
+
+    fun onTrimMemory() {
+    }
+
+    override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets {
+        _insets = insets
+        if (insets != null && _paintCanvas == null) {
+            setupBitmaps()
+        }
+        return super.onApplyWindowInsets(insets)
+    }
+
+    private fun powf(a: Float, b: Float): Float {
+        return Math.pow(a.toDouble(), b.toDouble()).toFloat()
+    }
+
+    override fun plot(s: MotionEvent.PointerCoords) {
+        val c = _paintCanvas
+        if (c == null) return
+        synchronized(_bitmapLock) {
+            var x = _lastX
+            var y = _lastY
+            var r = _lastR
+            val newR = Math.max(1f, powf(adjustPressure(s.pressure), 2f).toFloat() * _brushWidth)
+
+            if (r >= 0) {
+                val d = hypot(s.x - x, s.y - y)
+                if (d > 1f && (r + newR) > 1f) {
+                    val N = (2 * d / Math.min(4f, r + newR)).toInt()
+
+                    val stepX = (s.x - x) / N
+                    val stepY = (s.y - y) / N
+                    val stepR = (newR - r) / N
+                    for (i in 0 until N - 1) { // we will draw the last circle below
+                        x += stepX
+                        y += stepY
+                        r += stepR
+                        c.drawCircle(x, y, r, _drawPaint)
+                    }
+                }
+            }
+
+            c.drawCircle(s.x, s.y, newR, _drawPaint)
+            _lastX = s.x
+            _lastY = s.y
+            _lastR = newR
+        }
+    }
+
+    private fun loadDevicePressureData() {
+        try {
+            val touchDataJson = Settings.System.getString(context.contentResolver, TOUCH_STATS)
+            val touchData = JSONObject(
+                    if (touchDataJson != null) touchDataJson else "{}")
+            if (touchData.has("min")) devicePressureMin = touchData.getDouble("min").toFloat()
+            if (touchData.has("max")) devicePressureMax = touchData.getDouble("max").toFloat()
+            if (devicePressureMin < 0) devicePressureMin = 0f
+            if (devicePressureMax < devicePressureMin) devicePressureMax = devicePressureMin + 1f
+        } catch (e: Exception) {
+        }
+    }
+
+    private fun adjustPressure(pressure: Float): Float {
+        if (pressure > devicePressureMax) devicePressureMax = pressure
+        if (pressure < devicePressureMin) devicePressureMin = pressure
+        return invlerp(pressure, devicePressureMin, devicePressureMax)
+    }
+
+    override fun onTouchEvent(event: MotionEvent?): Boolean {
+        val c = _paintCanvas
+        if (event == null || c == null) return super.onTouchEvent(event)
+
+        /*
+        val pt = Paint(Paint.ANTI_ALIAS_FLAG)
+        pt.style = Paint.Style.STROKE
+        pt.color = 0x800000FF.toInt()
+        _paintCanvas?.drawCircle(event.x, event.y, 20f, pt)
+        */
+
+        when (event.actionMasked) {
+            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
+                _filter.add(event)
+                _filter.finish()
+                invalidate()
+            }
+
+            MotionEvent.ACTION_DOWN -> {
+                _lastR = -1f
+                _filter.add(event)
+                invalidate()
+            }
+
+            MotionEvent.ACTION_MOVE -> {
+                _filter.add(event)
+
+                invalidate()
+            }
+        }
+
+        return true
+    }
+
+    override fun onDraw(canvas: Canvas) {
+        super.onDraw(canvas)
+
+        bitmap?.let {
+            canvas.drawBitmap(bitmap, 0f, 0f, _drawPaint);
+        }
+    }
+
+    // public api
+    fun clear() {
+        bitmap = null
+        setupBitmaps()
+        invalidate()
+    }
+
+    fun sampleAt(x: Float, y: Float): Int {
+        val localX = (x - left).toInt()
+        val localY = (y - top).toInt()
+        return bitmap?.getPixel(localX, localY) ?: Color.BLACK
+    }
+
+    fun setPaintColor(color: Int) {
+        _drawPaint.color = color
+    }
+
+    fun getPaintColor(): Int {
+        return _drawPaint.color
+    }
+
+    fun setBrushWidth(w: Float) {
+        _brushWidth = w
+    }
+
+    fun getBrushWidth(): Float {
+        return _brushWidth
+    }
+
+    private fun setupBitmaps() {
+        val dm = DisplayMetrics()
+        display.getRealMetrics(dm)
+        val w = dm.widthPixels
+        val h = dm.heightPixels
+        val oldBits = bitmap
+        var bits = oldBits
+        if (bits == null || bits.width != w || bits.height != h) {
+            bits = Bitmap.createBitmap(
+                    w,
+                    h,
+                    Bitmap.Config.ARGB_8888
+            )
+        }
+        if (bits == null) return
+
+        val c = Canvas(bits)
+
+        if (oldBits != null) {
+            if (oldBits.width < oldBits.height != bits.width < bits.height) {
+                // orientation change. let's rotate things so they fit better
+                val matrix = Matrix()
+                if (bits.width > bits.height) {
+                    // now landscape
+                    matrix.postRotate(-90f)
+                    matrix.postTranslate(0f, bits.height.toFloat())
+                } else {
+                    // now portrait
+                    matrix.postRotate(90f)
+                    matrix.postTranslate(bits.width.toFloat(), 0f)
+                }
+                if (bits.width != oldBits.height || bits.height != oldBits.width) {
+                    matrix.postScale(
+                            bits.width.toFloat()/oldBits.height,
+                            bits.height.toFloat()/oldBits.width)
+                }
+                c.matrix = matrix
+            }
+            // paint the old artwork atop the new
+            c.drawBitmap(oldBits, 0f, 0f, _drawPaint)
+            c.matrix = Matrix()
+        } else {
+            c.drawColor(paperColor)
+        }
+
+        bitmap = bits
+        _paintCanvas = c
+    }
+
+    fun invertContents() {
+        val invertPaint = Paint()
+        invertPaint.colorFilter = INVERT_CF
+        synchronized(_bitmapLock) {
+            _paintCanvas?.drawBitmap(bitmap, 0f, 0f, invertPaint)
+        }
+        invalidate()
+    }
+}
+
diff --git a/packages/EasterEgg/src/com/android/egg/paint/Palette.kt b/packages/EasterEgg/src/com/android/egg/paint/Palette.kt
new file mode 100644
index 0000000..7043efe
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/Palette.kt
@@ -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.
+ */
+
+package com.android.egg.paint
+
+import android.graphics.Color
+
+class Palette {
+    var colors : IntArray
+    var lightest = 0
+    var darkest = 0
+
+    /**
+     * rough luminance calculation
+     * https://www.w3.org/TR/AERT/#color-contrast
+     */
+    private fun lum(rgb: Int): Float {
+        return (Color.red(rgb) * 299f + Color.green(rgb) * 587f + Color.blue(rgb) * 114f) / 1000f
+    }
+
+    /**
+     * create a random evenly-spaced color palette
+     * guaranteed to contrast!
+     */
+    fun randomize(S: Float, V: Float) {
+        val hsv = floatArrayOf((Math.random() * 360f).toFloat(), S, V)
+        val count = colors.size
+        colors[0] = Color.HSVToColor(hsv)
+        lightest = 0
+        darkest = 0
+
+        for (i in 0 until count) {
+            hsv[0] = (hsv[0] + 360f / count).rem(360f)
+            val color = Color.HSVToColor(hsv)
+            colors[i] = color
+
+            val lum = lum(colors[i])
+            if (lum < lum(colors[darkest])) darkest = i
+            if (lum > lum(colors[lightest])) lightest = i
+        }
+    }
+
+    override fun toString() : String {
+        val str = StringBuilder("Palette{ ")
+        for (c in colors) {
+            str.append(String.format("#%08x ", c))
+        }
+        str.append("}")
+        return str.toString()
+    }
+
+    constructor(count: Int) {
+        colors = IntArray(count)
+        randomize(1f, 1f)
+    }
+
+    constructor(count: Int, S: Float, V: Float) {
+        colors = IntArray(count)
+        randomize(S, V)
+    }
+
+    constructor(_colors: IntArray) {
+        colors = _colors
+        for (i in 0 until colors.size) {
+            val lum = lum(colors[i])
+            if (lum < lum(colors[darkest])) darkest = i
+            if (lum > lum(colors[lightest])) lightest = i
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/EasterEgg/src/com/android/egg/paint/SpotFilter.kt b/packages/EasterEgg/src/com/android/egg/paint/SpotFilter.kt
new file mode 100644
index 0000000..2c15c0d
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/SpotFilter.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.egg.paint
+
+import java.util.LinkedList
+
+import android.view.MotionEvent
+
+class SpotFilter(internal var mBufSize: Int, posDecay: Float, pressureDecay: Float, internal var mPlotter: Plotter) {
+    val spots = LinkedList<MotionEvent.PointerCoords>() // newest at front
+    val tmpSpot = MotionEvent.PointerCoords()
+    var lastTool = MotionEvent.TOOL_TYPE_UNKNOWN
+
+    val posDecay: Float
+    val pressureDecay: Float
+
+    interface Plotter {
+        fun plot(s: MotionEvent.PointerCoords)
+    }
+
+    init {
+        this.posDecay = if (posDecay in 0f..1f) posDecay else 1f
+        this.pressureDecay = if (pressureDecay in 0f..1f) pressureDecay else 1f
+    }
+
+    fun filterInto(out: MotionEvent.PointerCoords, tool: Int): MotionEvent.PointerCoords {
+        lastTool = tool
+
+        var wi = 1f // weight for ith component (position)
+        var w = 0f // total weight
+        var wi_press = 1f // weight for ith component (pressure)
+        var w_press = 0f // total weight (pressure)
+
+        var x = 0f
+        var y = 0f
+        var pressure = 0f
+        var size = 0f
+        for (pi in spots) {
+            x += pi.x * wi
+            y += pi.y * wi
+
+            pressure += pi.pressure * wi_press
+            size += pi.size * wi_press
+
+            w += wi
+            wi *= posDecay // exponential backoff
+
+            w_press += wi_press
+            wi_press *= pressureDecay
+
+            if (PRECISE_STYLUS_INPUT && tool == MotionEvent.TOOL_TYPE_STYLUS) {
+                // just take the newest one, no need to average
+                break
+            }
+        }
+
+        out.x = x / w
+        out.y = y / w
+        out.pressure = pressure / w_press
+        out.size = size / w_press
+        return out
+    }
+
+    protected fun addInternal(c: MotionEvent.PointerCoords, tool: Int) {
+        val coord =
+                if (spots.size == mBufSize) {
+                    spots.removeLast()
+                } else {
+                    MotionEvent.PointerCoords()
+                }
+        coord.copyFrom(c)
+
+        spots.add(0, coord)
+
+        filterInto(tmpSpot, tool)
+        mPlotter.plot(tmpSpot)
+    }
+
+    fun add(cv: List<MotionEvent.PointerCoords>, tool: Int) {
+        for (c in cv) {
+            addInternal(c, tool)
+        }
+    }
+
+    fun add(evt: MotionEvent) {
+        val tool = evt.getToolType(0)
+        for (i in 0 until evt.historySize) {
+            evt.getHistoricalPointerCoords(0, i, tmpSpot)
+            addInternal(tmpSpot, tool)
+        }
+        evt.getPointerCoords(0, tmpSpot)
+        addInternal(tmpSpot, tool)
+    }
+
+    fun finish() {
+        while (spots.size > 0) {
+            filterInto(tmpSpot, lastTool)
+            spots.removeLast()
+            mPlotter.plot(tmpSpot)
+        }
+
+        spots.clear()
+    }
+
+    companion object {
+        var PRECISE_STYLUS_INPUT = true
+    }
+}
+
+
diff --git a/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt b/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt
new file mode 100644
index 0000000..86b11e7
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.egg.paint
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.text.TextPaint
+import android.transition.ChangeBounds
+import android.transition.Transition
+import android.transition.TransitionListenerAdapter
+import android.transition.TransitionManager
+import android.util.AttributeSet
+import android.view.*
+import android.view.animation.OvershootInterpolator
+import android.widget.FrameLayout
+
+class ToolbarView : FrameLayout {
+    var inTransition = false
+    var transitionListener: Transition.TransitionListener = object : TransitionListenerAdapter() {
+        override fun onTransitionStart(transition: Transition?) {
+            inTransition = true
+        }
+        override fun onTransitionEnd(transition: Transition?) {
+            inTransition = false
+        }
+    }
+
+    constructor(context: Context) : super(context) {
+        init(null, 0)
+    }
+
+    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+        init(attrs, 0)
+    }
+
+    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
+        init(attrs, defStyle)
+    }
+
+    override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets {
+        var lp = layoutParams as FrameLayout.LayoutParams?
+        if (lp != null && insets != null) {
+            if (insets.hasStableInsets()) {
+                lp.topMargin = insets.stableInsetTop
+                lp.bottomMargin = insets.stableInsetBottom
+            } else {
+                lp.topMargin = insets.systemWindowInsetTop
+                lp.bottomMargin = insets.systemWindowInsetBottom
+            }
+            layoutParams = lp
+        }
+
+        return super.onApplyWindowInsets(insets)
+    }
+
+    private fun init(attrs: AttributeSet?, defStyle: Int) {
+    }
+
+}
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index ff70e97..a7217ec 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -55,12 +55,6 @@
             <intent-filter>
                 <action android:name="android.service.autofill.AutofillFieldClassificationService" />
             </intent-filter>
-            <meta-data
-                android:name="android.autofill.field_classification.default_algorithm"
-                android:resource="@string/autofill_field_classification_default_algorithm" />
-            <meta-data
-                android:name="android.autofill.field_classification.available_algorithms"
-                android:resource="@array/autofill_field_classification_available_algorithms" />
         </service>
 
         <library android:name="android.ext.services"/>
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index 72647ab..617e49a 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -18,7 +18,6 @@
     <string name="app_name">Android Services Library</string>
 
     <string name="notification_assistant">Notification Assistant</string>
-    <string name="prompt_block_reason">Too many dismissals:views</string>
 
     <string name="autofill_field_classification_default_algorithm">EDIT_DISTANCE</string>
     <string-array name="autofill_field_classification_available_algorithms">
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index f878822..9d19898 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -17,13 +17,16 @@
 package android.ext.services.notification;
 
 import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking
+        .USER_SENTIMENT_NEGATIVE;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.INotificationManager;
+import android.app.Notification;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
-import android.ext.services.R;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -80,6 +83,7 @@
 
     private float mDismissToViewRatioLimit;
     private int mStreakLimit;
+    private SmartActionsHelper mSmartActionsHelper;
 
     // key : impressions tracker
     // TODO: prune deleted channels and apps
@@ -99,6 +103,7 @@
         // Contexts are correctly hooked up by the creation step, which is required for the observer
         // to be hooked up/initialized.
         new SettingsObserver(mHandler);
+        mSmartActionsHelper = new SmartActionsHelper();
     }
 
     private void loadFile() {
@@ -187,7 +192,30 @@
     @Override
     public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
         if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey());
-        return null;
+        ArrayList<Notification.Action> actions =
+                mSmartActionsHelper.suggestActions(this, sbn);
+        ArrayList<CharSequence> replies = mSmartActionsHelper.suggestReplies(this, sbn);
+        return createEnqueuedNotificationAdjustment(sbn, actions, replies);
+    }
+
+    /** A convenience helper for creating an adjustment for an SBN. */
+    @Nullable
+    private Adjustment createEnqueuedNotificationAdjustment(
+            @NonNull StatusBarNotification statusBarNotification,
+            @NonNull ArrayList<Notification.Action> smartActions,
+            @NonNull ArrayList<CharSequence> smartReplies) {
+        if (smartActions.isEmpty() && smartReplies.isEmpty()) {
+            return null;
+        }
+        Bundle signals = new Bundle();
+        signals.putParcelableArrayList(Adjustment.KEY_SMART_ACTIONS, smartActions);
+        signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
+        return new Adjustment(
+                statusBarNotification.getPackageName(),
+                statusBarNotification.getKey(),
+                signals,
+                "smart action, reply" /* explanation */,
+                statusBarNotification.getUserId());
     }
 
     @Override
@@ -292,8 +320,7 @@
         if (DEBUG) Log.d(TAG, "User probably doesn't want " + key);
         Bundle signals = new Bundle();
         signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
-        return new Adjustment(packageName, key,  signals,
-                getContext().getString(R.string.prompt_block_reason), user);
+        return new Adjustment(packageName, key,  signals, "", user);
     }
 
     // for testing
@@ -379,4 +406,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
new file mode 100644
index 0000000..9d33bd9
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -0,0 +1,255 @@
+/**
+ * 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.annotation.Nullable;
+import android.app.Notification;
+import android.app.RemoteAction;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.Process;
+import android.os.SystemProperties;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+public class SmartActionsHelper {
+    private static final ArrayList<Notification.Action> EMPTY_ACTION_LIST = new ArrayList<>();
+    private static final ArrayList<CharSequence> EMPTY_REPLY_LIST = new ArrayList<>();
+
+    // 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
+                    | Notification.FLAG_FOREGROUND_SERVICE
+                    | Notification.FLAG_GROUP_SUMMARY
+                    | Notification.FLAG_NO_CLEAR;
+    private static final int MAX_ACTION_EXTRACTION_TEXT_LENGTH = 400;
+    private static final int MAX_ACTIONS_PER_LINK = 1;
+    private static final int MAX_SMART_ACTIONS = Notification.MAX_ACTION_BUTTONS;
+    // Allow us to test out smart reply with dumb suggestions, it is disabled by default.
+    // TODO: Removed this once we have the model.
+    private static final String SYS_PROP_SMART_REPLIES_EXPERIMENT =
+            "persist.sys.smart_replies_experiment";
+
+    SmartActionsHelper() {}
+
+    /**
+     * Adds action adjustments based on the notification contents.
+     *
+     * TODO: Once we have a API in {@link TextClassificationManager} to predict smart actions
+     * from notification text / message, we can replace most of the code here by consuming that API.
+     */
+    @NonNull
+    ArrayList<Notification.Action> suggestActions(
+            @Nullable Context context, @NonNull StatusBarNotification sbn) {
+        if (!isEligibleForActionAdjustment(sbn)) {
+            return EMPTY_ACTION_LIST;
+        }
+        if (context == null) {
+            return EMPTY_ACTION_LIST;
+        }
+        TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class);
+        if (tcm == null) {
+            return EMPTY_ACTION_LIST;
+        }
+        Notification.Action[] actions = sbn.getNotification().actions;
+        int numOfExistingActions = actions == null ? 0: actions.length;
+        int maxSmartActions = MAX_SMART_ACTIONS - numOfExistingActions;
+        return suggestActionsFromText(
+                tcm,
+                getMostSalientActionText(sbn.getNotification()), maxSmartActions);
+    }
+
+    ArrayList<CharSequence> suggestReplies(
+            @Nullable Context context, @NonNull StatusBarNotification sbn) {
+        if (!isEligibleForReplyAdjustment(sbn)) {
+            return EMPTY_REPLY_LIST;
+        }
+        if (context == null) {
+            return EMPTY_REPLY_LIST;
+        }
+        // TODO: replaced this with our model when it is ready.
+        return new ArrayList<>(Arrays.asList("Yes, please", "No, thanks"));
+    }
+
+    /**
+     * Returns whether a notification is eligible for action adjustments.
+     *
+     * <p>We exclude system notifications, those that get refreshed frequently, or ones that relate
+     * to fundamental phone functionality where any error would result in a very negative user
+     * experience.
+     */
+    private boolean isEligibleForActionAdjustment(@NonNull StatusBarNotification sbn) {
+        Notification notification = sbn.getNotification();
+        String pkg = sbn.getPackageName();
+        if (!Process.myUserHandle().equals(sbn.getUser())) {
+            return false;
+        }
+        if (notification.actions != null
+                && notification.actions.length >= Notification.MAX_ACTION_BUTTONS) {
+            return false;
+        }
+        if (0 != (notification.flags & FLAG_MASK_INELGIBILE_FOR_ACTIONS)) {
+            return false;
+        }
+        if (TextUtils.isEmpty(pkg) || pkg.equals("android")) {
+            return false;
+        }
+        // For now, we are only interested in messages.
+        return Notification.CATEGORY_MESSAGE.equals(notification.category)
+                || Notification.MessagingStyle.class.equals(notification.getNotificationStyle())
+                || hasInlineReply(notification);
+    }
+
+    private boolean isEligibleForReplyAdjustment(@NonNull StatusBarNotification sbn) {
+        if (!SystemProperties.getBoolean(SYS_PROP_SMART_REPLIES_EXPERIMENT, false)) {
+            return false;
+        }
+        Notification notification = sbn.getNotification();
+        if (notification.actions == null) {
+            return false;
+        }
+        return hasInlineReply(sbn.getNotification());
+    }
+
+    private boolean hasInlineReply(Notification notification) {
+        Notification.Action[] actions = notification.actions;
+        if (actions == null) {
+            return false;
+        }
+        for (Notification.Action action : actions) {
+            RemoteInput[] remoteInputs = action.getRemoteInputs();
+            if (remoteInputs == null) {
+                continue;
+            }
+            for (RemoteInput remoteInput : remoteInputs) {
+                if (remoteInput.getAllowFreeFormInput()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /** Returns the text most salient for action extraction in a notification. */
+    @Nullable
+    private CharSequence getMostSalientActionText(@NonNull Notification notification) {
+        /* If it's messaging style, use the most recent message. */
+        Parcelable[] messages = notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
+        if (messages != null && messages.length != 0) {
+            Bundle lastMessage = (Bundle) messages[messages.length - 1];
+            CharSequence lastMessageText =
+                    lastMessage.getCharSequence(Notification.MessagingStyle.Message.KEY_TEXT);
+            if (!TextUtils.isEmpty(lastMessageText)) {
+                return lastMessageText;
+            }
+        }
+
+        // Fall back to using the normal text.
+        return notification.extras.getCharSequence(Notification.EXTRA_TEXT);
+    }
+
+    /** 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) {
+        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.
+        text = text.subSequence(0, Math.min(text.length(), MAX_ACTION_EXTRACTION_TEXT_LENGTH));
+
+        // Extract all entities.
+        TextLinks.Request textLinksRequest = new TextLinks.Request.Builder(text)
+                .setEntityConfig(
+                        TextClassifier.EntityConfig.createWithHints(
+                                Collections.singletonList(
+                                        TextClassifier.HINT_TEXT_IS_NOT_EDITABLE)))
+                .build();
+        TextLinks links = textClassifier.generateLinks(textLinksRequest);
+        ArrayMap<String, Integer> entityTypeFrequency = getEntityTypeFrequency(links);
+
+        ArrayList<Notification.Action> actions = new ArrayList<>();
+        for (TextLinks.TextLink link : links.getLinks()) {
+            // Ignore any entity type for which we have too many entities. This is to handle the
+            // 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) {
+                continue;
+            }
+
+            // Generate the actions, and add the most prominent ones to the action bar.
+            TextClassification classification =
+                    textClassifier.classifyText(
+                            new TextClassification.Request.Builder(
+                                    text, link.getStart(), link.getEnd()).build());
+            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());
+                // We have enough smart actions.
+                if (actions.size() >= maxSmartActions) {
+                    return actions;
+                }
+            }
+        }
+        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/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index 0c3bd7e..16b2010 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -31,7 +31,7 @@
     <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandia"</string>
     <string name="keyboard_layout_brazilian" msgid="5117896443147781939">"Brasil"</string>
     <string name="keyboard_layout_portuguese" msgid="2888198587329660305">"Portugis"</string>
-    <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowakia"</string>
+    <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakia"</string>
     <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenia"</string>
     <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turki"</string>
     <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraina"</string>
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
new file mode 100644
index 0000000..bc06cab
--- /dev/null
+++ b/packages/PackageInstaller/Android.bp
@@ -0,0 +1,14 @@
+android_app {
+    name: "PackageInstaller",
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+        "androidx.leanback_leanback",
+        "xz-java",
+    ],
+
+    certificate: "platform",
+    privileged: true,
+    platform_apis: true,
+}
\ No newline at end of file
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
new file mode 100644
index 0000000..513c862
--- /dev/null
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.packageinstaller">
+
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
+    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.DELETE_PACKAGES" />
+    <uses-permission android:name="android.permission.READ_INSTALL_SESSIONS" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
+    <uses-permission android:name="android.permission.USE_RESERVED_DISK" />
+    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+    <uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+
+    <uses-permission android:name="com.google.android.permission.INSTALL_WEARABLE_PACKAGES" />
+
+    <application android:name=".PackageInstallerApplication"
+            android:label="@string/app_name"
+            android:icon="@drawable/ic_app_icon"
+            android:allowBackup="false"
+            android:theme="@style/DialogWhenLarge"
+            android:supportsRtl="true"
+            android:defaultToDeviceProtectedStorage="true"
+            android:directBootAware="true">
+
+        <receiver android:name=".TemporaryFileManager"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+        </receiver>
+
+        <activity android:name=".InstallStart"
+                android:exported="true"
+                android:excludeFromRecents="true">
+            <intent-filter android:priority="1">
+                <action android:name="android.intent.action.VIEW" />
+                <action android:name="android.intent.action.INSTALL_PACKAGE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="file" />
+                <data android:scheme="content" />
+                <data android:mimeType="application/vnd.android.package-archive" />
+            </intent-filter>
+            <intent-filter android:priority="1">
+                <action android:name="android.intent.action.INSTALL_PACKAGE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="file" />
+                <data android:scheme="package" />
+                <data android:scheme="content" />
+            </intent-filter>
+            <intent-filter android:priority="1">
+                <action android:name="android.content.pm.action.CONFIRM_INSTALL" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".InstallStaging"
+                android:exported="false" />
+
+        <activity android:name=".DeleteStagedFileOnResult"
+            android:exported="false" />
+
+        <activity android:name=".PackageInstallerActivity"
+                android:exported="false" />
+
+        <activity android:name=".InstallInstalling"
+                android:theme="@style/DialogWhenLargeNoAnimation"
+                android:exported="false" />
+
+        <receiver android:name=".InstallEventReceiver"
+                android:permission="android.permission.INSTALL_PACKAGES"
+                android:exported="true">
+            <intent-filter android:priority="1">
+                <action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" />
+            </intent-filter>
+        </receiver>
+
+        <activity android:name=".InstallSuccess"
+                android:theme="@style/DialogWhenLargeNoAnimation"
+                android:exported="false" />
+
+        <activity android:name=".InstallFailed"
+                android:theme="@style/DialogWhenLargeNoAnimation"
+                android:exported="false" />
+
+        <activity android:name=".UninstallerActivity"
+                android:configChanges="orientation|keyboardHidden|screenSize"
+                android:excludeFromRecents="true"
+                android:noHistory="true"
+                android:theme="@style/AlertDialogActivity">
+            <intent-filter android:priority="1">
+                <action android:name="android.intent.action.DELETE" />
+                <action android:name="android.intent.action.UNINSTALL_PACKAGE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="package" />
+            </intent-filter>
+        </activity>
+
+        <receiver android:name=".UninstallEventReceiver"
+            android:permission="android.permission.INSTALL_PACKAGES"
+            android:exported="true">
+            <intent-filter android:priority="1">
+                <action android:name="com.android.packageinstaller.ACTION_UNINSTALL_COMMIT" />
+            </intent-filter>
+        </receiver>
+
+        <activity android:name=".UninstallUninstalling"
+            android:excludeFromRecents="true"
+            android:theme="@style/AlertDialogActivity"
+            android:exported="false" />
+
+        <receiver android:name=".UninstallFinish"
+                android:exported="false" />
+
+        <activity android:name=".television.UninstallAppProgress"
+                android:configChanges="mnc|mnc|touchscreen|navigation|screenLayout|screenSize|smallestScreenSize|orientation|locale|keyboard|keyboardHidden|fontScale|uiMode|layoutDirection|density"
+                android:exported="false" />
+
+        <!-- Wearable Components -->
+        <service android:name=".wear.WearPackageInstallerService"
+                 android:permission="com.google.android.permission.INSTALL_WEARABLE_PACKAGES"
+                 android:exported="true"/>
+
+        <provider android:name=".wear.WearPackageIconProvider"
+                  android:authorities="com.google.android.packageinstaller.wear.provider"
+                  android:grantUriPermissions="true"
+                  android:exported="true" />
+    </application>
+
+</manifest>
diff --git a/packages/PackageInstaller/CleanSpec.mk b/packages/PackageInstaller/CleanSpec.mk
new file mode 100644
index 0000000..b84e1b6
--- /dev/null
+++ b/packages/PackageInstaller/CleanSpec.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/packages/PackageInstaller/MODULE_LICENSE_APACHE2 b/packages/PackageInstaller/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/PackageInstaller/MODULE_LICENSE_APACHE2
diff --git a/packages/PackageInstaller/NOTICE b/packages/PackageInstaller/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/packages/PackageInstaller/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/packages/PackageInstaller/OWNERS b/packages/PackageInstaller/OWNERS
new file mode 100644
index 0000000..252670a
--- /dev/null
+++ b/packages/PackageInstaller/OWNERS
@@ -0,0 +1,7 @@
+svetoslavganov@google.com
+moltmann@google.com
+toddke@google.com
+suprabh@google.com
+
+# For automotive related changes
+rogerxue@google.com
diff --git a/packages/PackageInstaller/res/anim/snackbar_enter.xml b/packages/PackageInstaller/res/anim/snackbar_enter.xml
new file mode 100644
index 0000000..96bf4d2
--- /dev/null
+++ b/packages/PackageInstaller/res/anim/snackbar_enter.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:shareInterpolator="false">
+    <translate android:fromYDelta="100%" android:toYDelta="0"
+            android:interpolator="@android:interpolator/decelerate_quint"
+            android:duration="250"/>
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+            android:interpolator="@android:interpolator/decelerate_quint"
+            android:duration="250" />
+</set>
diff --git a/packages/PackageInstaller/res/anim/snackbar_exit.xml b/packages/PackageInstaller/res/anim/snackbar_exit.xml
new file mode 100644
index 0000000..9aee177
--- /dev/null
+++ b/packages/PackageInstaller/res/anim/snackbar_exit.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false">
+    <translate android:fromYDelta="0" android:toYDelta="100%"
+            android:interpolator="@android:interpolator/accelerate_quint"
+            android:duration="250"/>
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+            android:interpolator="@android:interpolator/accelerate_quint"
+            android:duration="250"/>
+</set>
diff --git a/packages/PackageInstaller/res/drawable/app_icon_foreground.xml b/packages/PackageInstaller/res/drawable/app_icon_foreground.xml
new file mode 100644
index 0000000..b1f40c1
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/app_icon_foreground.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.
+  -->
+<inset
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetTop="12dp"
+    android:insetRight="12dp"
+    android:insetBottom="12dp"
+    android:insetLeft="12dp">
+
+    <vector
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+
+        <path
+            android:fillColor="#FFFFFF"
+            android:pathData="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z" />
+        <path
+            android:pathData="M0 0h24v24H0z" />
+    </vector>
+</inset>
diff --git a/packages/PackageInstaller/res/drawable/ic_android_92.xml b/packages/PackageInstaller/res/drawable/ic_android_92.xml
new file mode 100644
index 0000000..1d3791c
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_android_92.xml
@@ -0,0 +1,25 @@
+<?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="92dp"
+        android:height="92dp"
+        android:viewportWidth="92.0"
+        android:viewportHeight="92.0">
+    <path
+        android:fillColor="#000000"
+        android:pathData="m23,69c0,2.11 1.72,3.83 3.83,3.83h3.83v13.42c0,3.18 2.57,5.75 5.75,5.75 3.18,0 5.75,-2.57 5.75,-5.75L42.17,72.83h7.67v13.42c0,3.18 2.57,5.75 5.75,5.75 3.18,0 5.75,-2.57 5.75,-5.75L61.33,72.83h3.83c2.11,0 3.83,-1.72 3.83,-3.83L69,30.67L23,30.67L23,69zM13.42,30.67c-3.18,0 -5.75,2.57 -5.75,5.75v26.83c0,3.18 2.57,5.75 5.75,5.75 3.18,0 5.75,-2.57 5.75,-5.75L19.17,36.42c0,-3.18 -2.57,-5.75 -5.75,-5.75zM78.58,30.67c-3.18,0 -5.75,2.57 -5.75,5.75v26.83c0,3.18 2.57,5.75 5.75,5.75 3.18,0 5.75,-2.57 5.75,-5.75L84.33,36.42c0,-3.18 -2.57,-5.75 -5.75,-5.75zM59.53,8.28 L64.53,3.28c0.75,-0.75 0.75,-1.95 0,-2.7 -0.75,-0.75 -1.95,-0.75 -2.7,0L56.16,6.23C53.09,4.72 49.66,3.84 46,3.84c-3.68,0 -7.13,0.88 -10.22,2.41L30.09,0.56c-0.75,-0.75 -1.95,-0.75 -2.7,0 -0.75,0.75 -0.75,1.95 0,2.7l5.02,5.02C26.72,12.48 23,19.23 23,26.84h46c0,-7.63 -3.74,-14.37 -9.47,-18.55zM38.33,19.17h-3.83v-3.83h3.83v3.83zM57.5,19.17h-3.83v-3.83h3.83v3.83z"/>
+</vector>
diff --git a/packages/PackageInstaller/res/drawable/ic_app_icon.xml b/packages/PackageInstaller/res/drawable/ic_app_icon.xml
new file mode 100644
index 0000000..82c18e0
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_app_icon.xml
@@ -0,0 +1,21 @@
+<?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.
+  -->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@*android:color/accent_device_default_light"/>
+    <foreground android:drawable="@drawable/app_icon_foreground"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/drawable/ic_done_92.xml b/packages/PackageInstaller/res/drawable/ic_done_92.xml
new file mode 100644
index 0000000..185b274
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_done_92.xml
@@ -0,0 +1,25 @@
+<?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="92dp"
+        android:height="92dp"
+        android:viewportWidth="92.0"
+        android:viewportHeight="92.0">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M34.5,61.99 L18.51,46 13.09,51.42 34.5,72.83l46,-46 -5.42,-5.42z"/>
+</vector>
diff --git a/packages/PackageInstaller/res/drawable/ic_error.xml b/packages/PackageInstaller/res/drawable/ic_error.xml
new file mode 100644
index 0000000..28612a1
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_error.xml
@@ -0,0 +1,26 @@
+<?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.0"
+        android:viewportHeight="24.0">
+    <path
+            android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"
+            android:fillColor="#000000"/>
+</vector>
diff --git a/packages/PackageInstaller/res/drawable/ic_file_download.xml b/packages/PackageInstaller/res/drawable/ic_file_download.xml
new file mode 100644
index 0000000..7ea91f5
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_file_download.xml
@@ -0,0 +1,28 @@
+<?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="#000000"
+        android:pathData="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/PackageInstaller/res/drawable/ic_lock.xml b/packages/PackageInstaller/res/drawable/ic_lock.xml
new file mode 100644
index 0000000..396bd98
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_lock.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+    <path
+        android:pathData="M0 0h48v48H0z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M36 16h-2v-4c0-5.52-4.48-10-10-10S14 6.48 14 12v4h-2c-2.21 0-4 1.79-4 4v20c0
+2.21 1.79 4 4 4h24c2.21 0 4-1.79 4-4V20c0-2.21-1.79-4-4-4zM24 34c-2.21
+0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm6.2-18H17.8v-4c0-3.42 2.78-6.2
+6.2-6.2 3.42 0 6.2 2.78 6.2 6.2v4z" />
+</vector>
diff --git a/packages/PackageInstaller/res/drawable/ic_remove.xml b/packages/PackageInstaller/res/drawable/ic_remove.xml
new file mode 100644
index 0000000..dd46eda
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_remove.xml
@@ -0,0 +1,26 @@
+<?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.0"
+        android:viewportHeight="24.0">
+    <path
+            android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13L7,13v-2h10v2z"
+            android:fillColor="#000000"/>
+</vector>
diff --git a/packages/PackageInstaller/res/drawable/ic_report_problem_92.xml b/packages/PackageInstaller/res/drawable/ic_report_problem_92.xml
new file mode 100644
index 0000000..c90a33e
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_report_problem_92.xml
@@ -0,0 +1,25 @@
+<?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="92dp"
+        android:height="92dp"
+        android:viewportWidth="92.0"
+        android:viewportHeight="92.0">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M2,84H90L46,8 2,84zM50,72h-8v-8h8v8zM50,56H42V40h8v16z"/>
+</vector>
diff --git a/packages/PackageInstaller/res/drawable/ic_settings_multiuser.xml b/packages/PackageInstaller/res/drawable/ic_settings_multiuser.xml
new file mode 100644
index 0000000..b24a5d4
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_settings_multiuser.xml
@@ -0,0 +1,25 @@
+<!--
+    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="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorAccent">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12.0,12.0c2.21,0.0 4.0,-1.79 4.0,-4.0s-1.79,-4.0 -4.0,-4.0 -4.0,1.79 -4.0,4.0 1.79,4.0 4.0,4.0zm0.0,2.0c-2.67,0.0 -8.0,1.34 -8.0,4.0l0.0,2.0l16.0,0.0l0.0,-2.0c0.0,-2.66 -5.33,-4.0 -8.0,-4.0z"/>
+</vector>
diff --git a/packages/PackageInstaller/res/layout-television/app_details.xml b/packages/PackageInstaller/res/layout-television/app_details.xml
new file mode 100644
index 0000000..86923c5
--- /dev/null
+++ b/packages/PackageInstaller/res/layout-television/app_details.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+
+<!--
+Defines the layout of the application snippet that appears on top of the
+installation screens
+-->
+<!-- The snippet about the application - title, icon -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/app_snippet"
+        android:layout_width="match_parent"
+        android:layout_height="?android:attr/actionBarSize"
+        android:orientation="horizontal">
+
+    <ImageView android:id="@+id/app_icon"
+            android:layout_marginLeft="16dp"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_gravity="center_vertical"
+            android:scaleType="fitCenter" />
+
+    <TextView android:id="@+id/app_name"
+            android:layout_gravity="center_vertical"
+            android:layout_marginLeft="32dp"
+            android:layout_marginRight="16dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/titleTextStyle"
+            android:singleLine="true"
+            android:ellipsize="end" />
+
+</LinearLayout>
+
diff --git a/packages/PackageInstaller/res/layout-television/uninstall_progress.xml b/packages/PackageInstaller/res/layout-television/uninstall_progress.xml
new file mode 100644
index 0000000..e24f63b
--- /dev/null
+++ b/packages/PackageInstaller/res/layout-television/uninstall_progress.xml
@@ -0,0 +1,109 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+    <include layout="@layout/app_details"
+            android:id="@+id/app_snippet"/>
+
+    <LinearLayout android:id="@+id/progress_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:padding="16dp">
+
+        <ImageView android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="12dp"
+                android:src="@drawable/ic_android_92"
+                android:tint="@color/bigIconColor"
+                android:contentDescription="@null" />
+
+        <ProgressBar android:id="@+id/progress_bar"
+                android:layout_width="250dp"
+                android:layout_height="wrap_content"
+                android:indeterminate="true"
+                style="?android:attr/progressBarStyleHorizontal">
+        </ProgressBar>
+
+        <TextView android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center_horizontal"
+                android:text="@string/uninstalling"
+                android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    </LinearLayout>
+
+    <!-- Status view is shown after progress view is removed -->
+    <ScrollView android:id="@+id/status_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:visibility="gone"
+            android:padding="16dp">
+
+        <TextView android:id="@+id/status_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="start"
+                android:textAppearance="?android:attr/textAppearanceMedium"/>
+    </ScrollView>
+
+    <LinearLayout android:id="@+id/ok_panel"
+            style="?android:attr/buttonBarStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:measureWithLargestChild="true"
+            android:visibility="gone"
+            android:padding="8dip">
+
+        <!-- spacer to push buttons to the right -->
+        <View android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+        <Button android:id="@+id/device_manager_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:text="@string/manage_device_administrators"
+                android:maxLines="2"
+                style="?android:attr/buttonBarButtonStyle" />
+
+        <Button android:id="@+id/users_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:text="@string/manage_users"
+                android:maxLines="2"
+                style="?android:attr/buttonBarButtonStyle" />
+
+        <Button android:id="@+id/ok_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/ok"
+                android:maxLines="2"
+                style="?android:attr/buttonBarButtonStyle" />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/PackageInstaller/res/layout/install_confirm.xml b/packages/PackageInstaller/res/layout/install_confirm.xml
new file mode 100644
index 0000000..6be46fc
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/install_confirm.xml
@@ -0,0 +1,91 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout android:id="@+id/app_snippet"
+        android:background="?android:attr/colorPrimary"
+        android:layout_width="match_parent"
+        android:layout_height="?android:attr/actionBarSize"
+        android:orientation="horizontal"
+        android:elevation="@dimen/headerElevation"
+        android:gravity="center_vertical">
+
+        <ImageView android:id="@+id/app_icon"
+            android:layout_marginStart="16dp"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:scaleType="fitCenter"
+            android:src="@drawable/ic_file_download" />
+
+        <TextView android:id="@+id/app_name"
+            android:layout_marginStart="32dp"
+            android:layout_marginEnd="16dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/titleTextStyle"
+            android:singleLine="true"
+            android:text="@string/app_name_unknown"
+            android:ellipsize="end" />
+
+    </LinearLayout>
+
+    <ScrollView android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:padding="16dip">
+
+        <TextView android:id="@+id/install_confirm_question"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    </ScrollView>
+
+    <LinearLayout style="?android:attr/buttonBarStyle"
+        android:background="?android:attr/colorBackground"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:padding="8dp"
+        android:measureWithLargestChild="true">
+
+        <!-- spacer to push buttons to the right -->
+        <View android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_weight="1" />
+
+        <Button android:id="@+id/cancel_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/cancel"
+            android:maxLines="2"
+            style="?android:attr/buttonBarButtonStyle" />
+
+        <Button android:id="@+id/ok_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/install"
+            android:maxLines="2"
+            style="?android:attr/buttonBarButtonStyle" />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/PackageInstaller/res/layout/install_failed.xml b/packages/PackageInstaller/res/layout/install_failed.xml
new file mode 100644
index 0000000..d000ee9
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/install_failed.xml
@@ -0,0 +1,96 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+    <LinearLayout android:id="@+id/app_snippet"
+            android:background="?android:attr/colorPrimary"
+            android:layout_width="match_parent"
+            android:layout_height="?android:attr/actionBarSize"
+            android:orientation="horizontal"
+            android:elevation="@dimen/headerElevation"
+            android:gravity="center_vertical">
+
+        <ImageView android:id="@+id/app_icon"
+                android:layout_marginStart="16dp"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:scaleType="fitCenter" />
+
+        <TextView android:id="@+id/app_name"
+                android:layout_marginStart="32dp"
+                android:layout_marginEnd="16dp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textAppearance="?android:attr/titleTextStyle"
+                android:singleLine="true"
+                android:ellipsize="end" />
+
+    </LinearLayout>
+
+    <LinearLayout android:id="@+id/simple_status_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:paddingLeft="16dip"
+            android:paddingRight="16dip">
+
+        <ImageView android:id="@+id/center_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="12dp"
+                android:src="@drawable/ic_report_problem_92"
+                android:tint="@color/bigIconColor"
+                android:contentDescription="@null" />
+
+        <TextView android:id="@+id/simple_status"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    </LinearLayout>
+
+    <LinearLayout android:id="@+id/buttons_panel"
+            style="?android:attr/buttonBarStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:measureWithLargestChild="true"
+            android:padding="8dip">
+
+        <!-- spacer to push button to the right -->
+        <View android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+        <Button android:id="@+id/done_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/done"
+                android:maxLines="2"
+                style="?android:attr/buttonBarButtonStyle" />
+
+    </LinearLayout>
+
+</LinearLayout>
+
+
diff --git a/packages/PackageInstaller/res/layout/install_installing.xml b/packages/PackageInstaller/res/layout/install_installing.xml
new file mode 100644
index 0000000..a043a01
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/install_installing.xml
@@ -0,0 +1,108 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+    <LinearLayout android:id="@+id/app_snippet"
+            android:background="?android:attr/colorPrimary"
+            android:layout_width="match_parent"
+            android:layout_height="?android:attr/actionBarSize"
+            android:orientation="horizontal"
+            android:elevation="@dimen/headerElevation"
+            android:gravity="center_vertical">
+
+        <ImageView
+                android:id="@+id/app_icon"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_marginStart="16dp"
+                android:scaleType="fitCenter" />
+
+        <TextView
+                android:id="@+id/app_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="32dp"
+                android:layout_marginEnd="16dp"
+                android:ellipsize="end"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/titleTextStyle" />
+
+    </LinearLayout>
+
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:paddingLeft="16dip"
+            android:paddingRight="16dip">
+
+        <ImageView
+                android:layout_width="92dp"
+                android:layout_height="92dp"
+                android:layout_marginBottom="12dp"
+                android:contentDescription="@null"
+                android:tint="@color/bigIconColor"
+                android:src="@drawable/ic_file_download" />
+
+        <ProgressBar
+                android:id="@+id/progress_bar"
+                style="?android:attr/progressBarStyleHorizontal"
+                android:layout_width="250dp"
+                android:layout_height="wrap_content"
+                android:indeterminate="false" />
+
+        <TextView
+                android:id="@+id/center_text"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center_horizontal"
+                android:text="@string/installing"
+                android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    </LinearLayout>
+
+    <LinearLayout
+            android:id="@+id/buttons_panel"
+            style="?android:attr/buttonBarStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:measureWithLargestChild="true"
+            android:orientation="horizontal"
+            android:padding="8dip">
+
+        <View
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+        <Button
+                android:id="@+id/cancel_button"
+                style="?android:attr/buttonBarButtonStyle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:maxLines="2"
+                android:text="@string/cancel" />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/PackageInstaller/res/layout/install_staging.xml b/packages/PackageInstaller/res/layout/install_staging.xml
new file mode 100644
index 0000000..e3022e7
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/install_staging.xml
@@ -0,0 +1,113 @@
+<?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.
+  -->
+
+<!--
+  Defines the layout of the splash screen that displays the security
+  settings required for an application and requests the confirmation of the
+  user before it is installed.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+    <!-- title bar -->
+    <LinearLayout android:id="@+id/app_snippet"
+            android:layout_width="match_parent"
+            android:layout_height="?android:attr/actionBarSize"
+            android:background="?android:attr/colorPrimary"
+            android:elevation="@dimen/headerElevation"
+            android:gravity="center_vertical"
+            android:orientation="horizontal">
+
+        <ImageView android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_marginLeft="16dp"
+                android:scaleType="fitCenter"
+                android:src="@drawable/ic_file_download"
+                android:tint="?android:attr/colorAccent" />
+
+        <TextView android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="32dp"
+                android:layout_marginRight="16dp"
+                android:ellipsize="end"
+                android:singleLine="true"
+                android:text="@string/app_name_unknown"
+                android:textAppearance="?android:attr/titleTextStyle" />
+
+    </LinearLayout>
+
+    <!-- content -->
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:paddingLeft="16dip"
+            android:paddingRight="16dip">
+
+        <ImageView
+                android:layout_width="92dp"
+                android:layout_height="92dp"
+                android:scaleType="fitCenter"
+                android:layout_marginBottom="12dp"
+                android:contentDescription="@null"
+                android:tint="@color/bigIconColor"
+                android:src="@drawable/ic_file_download" />
+
+        <ProgressBar
+                style="?android:attr/progressBarStyleHorizontal"
+                android:layout_width="250dp"
+                android:layout_height="wrap_content"
+                android:indeterminate="true" />
+
+        <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center_horizontal"
+                android:text="@string/message_staging"
+                android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    </LinearLayout>
+
+    <!-- Bottom buttons -->
+    <LinearLayout style="?android:attr/buttonBarStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:padding="8dp">
+
+        <!-- spacer to push button to the right -->
+        <View android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+        <Button android:id="@+id/cancel_button"
+                style="?android:attr/buttonBarButtonStyle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:maxLines="2"
+                android:text="@string/cancel" />
+
+    </LinearLayout>
+
+</LinearLayout>
+
+
diff --git a/packages/PackageInstaller/res/layout/install_success.xml b/packages/PackageInstaller/res/layout/install_success.xml
new file mode 100644
index 0000000..fee6bed
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/install_success.xml
@@ -0,0 +1,101 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+    <LinearLayout android:id="@+id/app_snippet"
+            android:background="?android:attr/colorPrimary"
+            android:layout_width="match_parent"
+            android:layout_height="?android:attr/actionBarSize"
+            android:orientation="horizontal"
+            android:elevation="@dimen/headerElevation"
+            android:gravity="center_vertical">
+
+        <ImageView android:id="@+id/app_icon"
+                android:layout_marginStart="16dp"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:scaleType="fitCenter" />
+
+        <TextView android:id="@+id/app_name"
+                android:layout_marginStart="32dp"
+                android:layout_marginEnd="16dp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textAppearance="?android:attr/titleTextStyle"
+                android:singleLine="true"
+                android:ellipsize="end" />
+
+    </LinearLayout>
+
+    <LinearLayout android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:paddingLeft="16dip"
+            android:paddingRight="16dip">
+
+        <ImageView android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="12dp"
+                android:src="@drawable/ic_done_92"
+                android:tint="@color/bigIconColor"
+                android:contentDescription="@null" />
+
+        <TextView android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center_horizontal"
+                android:text="@string/install_done"
+                android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    </LinearLayout>
+
+    <LinearLayout style="?android:attr/buttonBarStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:measureWithLargestChild="true"
+            android:padding="8dip">
+
+        <!-- spacer to push buttons to the right -->
+        <View android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+        <Button android:id="@+id/done_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/done"
+                android:maxLines="2"
+                style="?android:attr/buttonBarButtonStyle" />
+
+        <Button android:id="@+id/launch_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/launch"
+                android:maxLines="2"
+                style="?android:attr/buttonBarButtonStyle" />
+
+    </LinearLayout>
+
+</LinearLayout>
+
+
diff --git a/packages/PackageInstaller/res/values-af-television/strings.xml b/packages/PackageInstaller/res/values-af-television/strings.xml
new file mode 100644
index 0000000..32cf00f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-af-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Weier en moenie weer vra nie"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Jy kan dit later verander in Instellings &gt; Programme"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Wys stelselprogramme"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Programtoestemmings"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Programtoestemmings"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g>toestemmings"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Bykomende toestemmings"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g>toestemmings"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-af-watch/strings.xml b/packages/PackageInstaller/res/values-af-watch/strings.xml
new file mode 100644
index 0000000..a62540f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-af-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Weier; moenie weer vra nie"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Wys stelselprogramme"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Onveranderbaar"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Ja"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Kanselleer"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
new file mode 100644
index 0000000..0d41c18
--- /dev/null
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Pakketinstalleerder"</string>
+    <string name="next" msgid="3057143178373252333">"Volgende"</string>
+    <string name="install" msgid="5896438203900042068">"Installeer"</string>
+    <string name="done" msgid="3889387558374211719">"Klaar"</string>
+    <string name="cancel" msgid="8360346460165114585">"Kanselleer"</string>
+    <string name="installing" msgid="8613631001631998372">"Installeer tans…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Installeer tans <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> …"</string>
+    <string name="install_done" msgid="3682715442154357097">"Program geïnstalleer."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Wil jy hierdie program installeer? Dit sal kan ingaan by:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Wil jy hierdie program installeer? Dit vereis nie enige spesiale toegang nie."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Wil jy \'n opdatering vir die bestaande program installeer? Jou bestaande data sal nie verlore gaan nie. Die opgedateerde program sal kan ingaan by:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Wil jy \'n opdatering vir hierdie ingeboude program installeer? Jou bestaande data sal nie verlore gaan nie. Die opgedateerde program sal kan ingaan by:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Wil jy \'n opdatering na hierdie bestaande program installeer? Jou bestaande data sal nie verlore raak nie. Dit vereis nie enige spesiale toegang nie."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Wil jy \'n opdatering na hierdie ingeboude program installeer? Jou bestaande data sal nie verlore raak nie. Dit vereis nie enige spesiale toegang nie."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Program nie geïnstalleer nie."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Die installering van die pakket is geblokkeer."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Program is nie geïnstalleer nie omdat pakket met \'n bestaande pakket bots."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Program is nie geïnstalleer nie omdat dit nie met jou tablet versoenbaar is nie."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Hierdie program is nie met jou TV versoenbaar nie."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Program is nie geïnstalleer nie omdat dit nie met jou foon versoenbaar is nie."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Program is nie geïnstalleer nie omdat pakket ongeldig blyk te wees."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> kon nie op jou tablet geïnstalleer word nie."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> kon nie op jou TV geïnstalleer word nie."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> kon nie op jou foon geïnstalleer word nie."</string>
+    <string name="launch" msgid="4826921505917605463">"Open"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Jou administrateur laat nie toe dat programme wat by onbekende bronne verkry is, geïnstalleer word nie"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Hierdie gebruiker kan nie onbekende programme installeer nie"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Hierdie gebruiker word nie toegelaat om programme te installeer nie"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Bestuur programme"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Geen spasie oor nie"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> kon nie geïnstalleer word nie. Maak \'n bietjie plek en probeer weer."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Program nie gevind nie"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Die program is nie in die lys van geïnstalleerde programme gevind nie."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Nie toegelaat nie"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Die huidige gebruiker mag nie hierdie deïnstallering uitvoer nie."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Fout"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Program kon nie gedeïnstalleer word nie."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Deïnstalleer program"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Deïnstalleer opdatering"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> is deel van die volgende program:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Wil jy hierdie program deïnstalleer?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Wil jy hierdie program vir "<b>"alle"</b>" gebruikers deïnstalleer? Die program en sy data sal vir "<b>"alle"</b>" gebruikers op hierdie toestel verwyder word."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Wil jy hierdie program vir die gebruiker <xliff:g id="USERNAME">%1$s</xliff:g> deïnstalleer?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Vervang hierdie program met die fabriekweergawe? Alle data sal verwyder word."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Voer tans deïnstallerings uit"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Mislukte installerings"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Deïnstalleer tans…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Deïnstalleer tans <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> …"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Deïnstallering klaar."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Het <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> gedeïnstalleer"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Deïnstallasie onsuksesvol."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Kon nie <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> deïnstalleer nie."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Kan nie aktiewe toesteladministrasieprogram deïnstalleer nie"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Kan nie aktiewe toesteladministrasieprogram vir <xliff:g id="USERNAME">%1$s</xliff:g> deïnstalleer nie"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Dié program word vereis vir sommige gebruikers of profiele en is vir ander gedeïnstalleer"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Hierdie program is nodig vir jou profiel en kan nie gedeïnstalleer word nie."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Jou toesteladministrateur vereis die program; dit kan nie deïnstalleer word nie."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Bestuur toesteladministrasieprogramme"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Bestuur gebruikers"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> kon nie deïnstalleer word nie."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Kon nie die pakket ontleed nie."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nuut"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Alle"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privaatheid"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Toesteltoegang"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Hierdie opdatering vereis geen nuwe toestemmings nie."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Weier"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Meer inligting"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Weier in elk geval"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> van <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; altyd toe om <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Net terwyl program gebruik word"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Altyd"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Weier en moenie weer vra nie"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> is gedeaktiveer"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"alles is gedeaktiveer"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"geen is gedeaktiveer nie"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Laat toe"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Programme"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Programtoestemmings"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Moenie weer vra nie"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Geen toestemmings nie"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Bykomende toestemmings"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Maak programinligting oop"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">Nog <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Nog <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Hierdie program is vir \'n ouer weergawe van Android ontwerp. As toestemming geweier word, kan dit veroorsaak dat dit nie meer soos beplan funksioneer nie."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"voer \'n onbekende handeling uit"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> uit <xliff:g id="COUNT_1">%2$d</xliff:g> programme toegelaat"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Wys stelsel"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Versteek stelsel"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Geen programme nie"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Ligginginstellings"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> is \'n verskaffer van liggingdienste vir hierdie toestel. Liggingtoegang kan vanuit ligginginstellings verander word."</string>
+    <string name="system_warning" msgid="7103819124542305179">"As jy hierdie toestemming weier, sal basiese kenmerke van jou toestel dalk nie meer soos bedoel werk nie."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Afgedwing deur beleid"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Agtergrondtoegang is gedeaktiveer volgens beleid"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Agtergrondtoegang is geaktiveer volgens beleid"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Voorgrondtoegang is geaktiveer volgens beleid"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Beheer deur administrateur"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Altyd"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Net as program gebruik word"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nooit"</string>
+    <string name="loading" msgid="7811651799620593731">"Laai tans …"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Alle toestemmings"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Ander programvermoëns"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Toestemmingsversoek"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Skermoorlegger bespeur"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Om hierdie toestemminginstelling te verander, moet jy eers die skermoorlegger by Instellings &gt; Programme afskakel"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Maak instellings oop"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Installeer- en deïnstalleerhandelinge word nie in Wear gesteun nie."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Kies waartoe &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang mag kry"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; is opgedateer. Kies waartoe hierdie program toegang mag kry."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Kanselleer"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Gaan voort"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nuwe toestemmings"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Huidige toestemmings"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Voer tans program uit …"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Onbekend"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Jou tablet word vir jou veiligheid nie toegelaat om onbekende programme van hierdie bron af te installeer nie."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Jou TV word vir jou veiligheid nie toegelaat om onbekende programme van hierdie bron af te installeer nie."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Jou foon word vir jou veiligheid nie toegelaat om onbekende programme van hierdie bron af te installeer nie."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Jou foon en persoonlike data is meer kwesbaar vir aanvalle deur onbekende programme. Deur hierdie program te installeer, stem jy in dat jy verantwoordelik is vir enige skade aan jou foon of verlies van data wat uit sy gebruik kan spruit."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Jou tablet en persoonlike data is meer kwesbaar vir aanvalle deur onbekende programme. Deur hierdie program te installeer, stem jy in dat jy verantwoordelik is vir enige skade aan jou tablet of verlies van data wat uit sy gebruik kan spruit."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Jou TV en persoonlike data is meer kwesbaar vir aanvalle deur onbekende programme. Deur hierdie program te installeer, stem jy in dat jy verantwoordelik is vir enige skade aan jou TV of verlies van data wat uit sy gebruik kan spruit."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Gaan voort"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Instellings"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Installeer/deïnstalleer Wear-programme"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-am-television/strings.xml b/packages/PackageInstaller/res/values-am-television/strings.xml
new file mode 100644
index 0000000..e4f23c1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-am-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"አይቀበሉና እንደገና ይጠይቁ"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"ይሄንን በኋላ ላይ በቅንብሮችና መተግበሪያዎች ውስጥ ሊቀይሩት ይችላሉ"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"የስርዓት መተግበሪያዎችን አሳይ"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"የመተግበሪያ ፈቃዶች"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"የመተግበሪያ ፈቃዶች"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> ፈቃዶች"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"ተጨማሪ ፈቃዶች"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> ፈቃዶች"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-am-watch/strings.xml b/packages/PackageInstaller/res/values-am-watch/strings.xml
new file mode 100644
index 0000000..1b01ddd
--- /dev/null
+++ b/packages/PackageInstaller/res/values-am-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"ከልክል፣ ዳግም አትጠይቅ"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"የስርዓት መተግበሪያዎችን አሳይ"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"ሊለወጥ አይችልም"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"አዎ"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"ይቅር"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-am/strings.xml b/packages/PackageInstaller/res/values-am/strings.xml
new file mode 100644
index 0000000..d4c5076
--- /dev/null
+++ b/packages/PackageInstaller/res/values-am/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"ጥቅል ጫኝ"</string>
+    <string name="next" msgid="3057143178373252333">"ቀጣይ"</string>
+    <string name="install" msgid="5896438203900042068">"ጫን"</string>
+    <string name="done" msgid="3889387558374211719">"ተከናውኗል"</string>
+    <string name="cancel" msgid="8360346460165114585">"ይቅር"</string>
+    <string name="installing" msgid="8613631001631998372">"በመጫን ላይ…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ን በመጫን ላይ…"</string>
+    <string name="install_done" msgid="3682715442154357097">"መተግበሪያ ተጭኗል፡፡"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"ይህንን መተግበሪያ መጫን ይፈልጋሉ? ወደዚህ መዳረሻ ያገኛል፦"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"ይህንን መተግበሪያ መጫን ይፈልጋሉ? ምንም የተለየ መዳረሻ አይጠይቅም።"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"ለእዚህ ነባር መተግበሪያ ማዘመኛ መጫን ይፈልጋሉ? የነበረው ውሂብህ አይጠፋም። የዘመነው መተግበሪያ ወደዚህ መዳረሻ ያገኛል፦"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"ለእዚህ አብሮ ለተሰራ መተግበሪያ ማዘመኛ መጫን ይፈልጋሉ? የነበረው ውሂብዎ አይጠፋም። የዘመነው መተግበሪያ ወደዚህ መዳረሻ ያገኛል፦"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"ለዚህ ነባር መተግበሪያ ዝማኔ መጫን ይፈልጋሉ? ነባር ውሂብዎ አይጠፉም። ምንም የተለየ መዳረሻ አይፈልግም።"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"ለዚህ አብሮ ለተሰራ መተግበሪያ ዝማኔ መጫን ይፈልጋሉ? ነባር ውሂብዎ አይጠፉም። ምንም የተለየ መዳረሻ አይፈልግም።"</string>
+    <string name="install_failed" msgid="6579998651498970899">"ትግበራ አልተጫነም።"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"ጥቅሉ እንዳይጫን ታግዷል።"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"እንደ ጥቅል ያልተጫነ መተግበሪያ ከነባር ጥቅል ጋር ይጋጫል።"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"እንደ መተግበሪያ ያልተጫነ መተግበሪያ ከጡባዊዎ ጋር ተኳሃኝ አይደለም።"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"ይሄ መተግበሪያ ከእርስዎ ቴሌቪዥን ጋር ተኳሃኝ አይደለም።"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"እንደ መተግበሪያ ያልተጫነ መተግበሪያ ከስልክዎ ጋር ተኳሃኝ አይደለም።"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"እንደ ጥቅል ያልተጫነ መተግበሪያ ልክ ያልሆነ ይመስላል።"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> በዚህ ስልክ ላይ መጫን አልተቻለም።"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> በእርስዎ ቴሌቪዥን ላይ ሊጫን አልቻለም።"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g>በዚህ ስልክ ላይ መጫን አልተቻለም።"</string>
+    <string name="launch" msgid="4826921505917605463">"ክፈት"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"የእርስዎ አስተዳዳሪ ካልታወቁ ምንጮች የመጡ መተግበሪያዎች እንዲጫኑ አይፈቅድም"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"ያልታወቁ መተግበሪያዎች በዚህ ተጠቃሚ ሊጫኑ አይችሉም"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"ይህ ተጠቃሚ መተግበሪያዎችን እንዲጭን አልተፈቀደለትም"</string>
+    <string name="ok" msgid="3468756155452870475">"እሺ"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"መተግበሪያዎች አስተዳድር"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"ቦታ ሞልቷል"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g>ለመጫን አልቻለም። ትንሽ ቦታ አስለቅቅ እና እንደገና ሞክር፡፡"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"ትግበራ አልተገኘም"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"መተግበሪያው በተጫኑ መተግበሪያዎች ዝርዝር ውስጥ አልተገኘም።"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"አይፈቀድም"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"አሁን ያለው ተጠቃሚ ይህን ማራገፍ ሥራ እንዲያከናውን አይፈቀድለትም።"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"ስሕተት"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"መተግበሪያ ሊራገፍ አልተቻለም"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"ትግበራ አራግፍ"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"ማዘመን አራግፍ"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>የሚከተለው ትግበራ አካል ነው፡"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"ይሄን መተግበሪያ ማራገፍ ይፈልጋሉ?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"ይህን መተግበሪያ "<b>"ለሁሉም"</b>" ተጠቃሚዎች መጫን ይፈልጋሉ? መተግበሪያው እና ውሂቡ በመሣሪያው ላይ ካሉ "<b>"ሁሉም"</b>" ተጠቃሚዎች ይሰረዛሉ።"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"ይህን መተግበሪያ ለተጠቃሚ <xliff:g id="USERNAME">%1$s</xliff:g> ማራገፍ ይፈልጋሉ?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"ይህ መተግበሪያ በፋብሪክው ስሪት ይተካ? ሁሉም ውሂብ ይወገዳል።"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"ይህ መተግበሪያ በፋብሪክው ስሪት ይተካ? ሁሉም ውሂብ ይወገዳል። እነዚያን የሥራ መገለጫዎች ያላቸውን ጨምሮ ሁሉንም በዚህ መሣሪያ ላይ ባሉ ተጠቃሚዎች ላይ ተጽዕኖ ያሳርፍባቸዋል።"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"በማሄድ ላይ ያሉ ማራገፎች"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"ያልተሳኩ ማራገፎች"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"ባለመጫንላይ"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ን በማራገፍ ላይ…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"አራግፍ ተጠናቋል"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ተራግፏል"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"ማራገፍ አልተሳካም፡፡"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ን ማራገፍ ስኬታማ አልነበረም።"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"ገባሪ የመሣሪያ አስተዳደር መተግበሪያን ማራገፍ አይቻልም"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"ለ<xliff:g id="USERNAME">%1$s</xliff:g> ገባሪ የመሣሪያ አስተዳደር መተግበሪያን ማራገፍ አይቻልም"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"ይህ መተግበሪያ ለአንዳንድ ተጠቃሚዎች ወይም መገለጫዎች ያስፈልጋል እና ለሌሎች ተራግፏል"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"ይህ መተግበሪያ ለእርስዎ መገለጫዎ ያስፈልጋል እና ሊራገፍ አይችልም።"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"ይህ መተግበሪያ በመሣሪያዎ አስተዳዳሪ የሚፈለግ እና ሊራገፍ የማይችል ነው።"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"የመሣሪያ አስተዳደር መተግበሪያዎችን ያስተዳድሩ"</string>
+    <string name="manage_users" msgid="3125018886835668847">"ተጠቃሚዎችን ያስተዳድሩ"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g>ማራገፍ አልተቻለም"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"አካታቹን መተንተን ችግር ነበረ።"</string>
+    <string name="newPerms" msgid="6039428254474104210">"አዲስ"</string>
+    <string name="allPerms" msgid="1024385515840703981">"ሁሉም"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"ግላዊነት"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"የመሳሪያ መዳረሻ"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"ይህ ዝማኔ ምንም አዲስ ፈቃድ አያስፈልገውም።"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"ከልክል"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"ተጨማሪ መረጃ"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"ለማንኛውም ከልክል"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> ከ<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g> እንዲከናወን ይፈቀድለት?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"ሁልጊዜ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ወደ <xliff:g id="ACTION">%2$s</xliff:g> ይፈቀድ?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"መተግበሪያን በመጠቀም ላይ ሲኮን ብቻ"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"ሁልጊዜ"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"አትቀበል እና እንደገና አትጠይቅ"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> ተሰናክሏል"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"ሁሉም ተሰናክሏል"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ምንም አልተሰናከለም"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"ፍቀድ"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"መተግበሪያዎች"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"የመተግበሪያ ፈቃዶች"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"ዳግም አትጠይቅ"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"ምንም ፍቃዶች የሉም"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"ተጨማሪ ፈቃዶች"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"የመተግበሪያ መረጃን ክፈት"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <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="old_sdk_deny_warning" msgid="3872277112584842615">"ይህ መተግበሪያ ለAndroid አሮጌ ስሪት የተነደፈ ነበር። ፈቃድ መከልከል እንደሚፈለገው ከእንግዲህ እንዳይሰራ ሊያደርገው ይችላል።"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"ያልታወቀ እርምጃ ያከናውናል"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> ከ<xliff:g id="COUNT_1">%2$d</xliff:g> መተግበሪያዎች ተፈቅዶላቸዋል"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"ስርዓትን አሳይ"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"ስርዓትን ደብቅ"</string>
+    <string name="no_apps" msgid="1965493419005012569">"ምንም መተግበሪያዎች የሉም"</string>
+    <string name="location_settings" msgid="1774875730854491297">"የአካባቢ ቅንብሮች"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> የዚህ መሳሪያ አካባቢ አገልግሎቶች አቅራቢ ነው። የአካባቢ መዳረሻ ከአካባቢ ቅንብሮች ሊሻሻል ይችላል።"</string>
+    <string name="system_warning" msgid="7103819124542305179">"ይህን ፍቃድ ከከለከሉ የመሳሪያዎ መሰረታዊ ባህሪያት ከዚህ በኋላ እንደተፈለገው ላይሰሩ ይችላሉ።"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"በመመሪያ ተፈጻሚ የሆነ"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"የጀርባ መዳረሻ በመመሪያ ተሰናክሏል"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"የጀርባ መዳረሻ በመመሪያ ነቅቷል"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"የፊት መዳረሻ በመመሪያ ነቅቷል"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"በአስተዳዳሪ ቁጥጥር የሚደረግበት"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"ዘወትር"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"መተግበሪያን በስራ ላይ ሲሆን ብቻ"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"በጭራሽ"</string>
+    <string name="loading" msgid="7811651799620593731">"በመጫን ላይ…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"ሁሉም ፍቃዶች"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"ሌሎች የመተግበሪያ ችሎታዎች"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"የፍቃድ ጥያቄ"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"የማያ ገጽ ተደራቢ ተገኝቷል"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"ይህን የፍቃድ ቅንብር ለመቀየር መጀመሪያ የማያ ገጽ ተደራቢውን ከቅንብሮች &gt; መተግበሪያዎች ማጥፋት አለብዎ"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"ቅንብሮችን ክፈት"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"በWear ላይ የመጫን/ማራገፍ እርምጃዎች አይደገፉም።"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ምን መድረስ እንደሚችል ይምረጡ"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ተዘምኗል። ይህ መተግበሪያ ምን መድረስ እንደሚችል ይምረጡ።"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"ይቅር"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"ቀጥል"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"አዲስ ፍቃዶች"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"የአሁኖቹ ፍቃዶች"</string>
+    <string name="message_staging" msgid="6151794817691100003">"መተግበሪያን በማዘጋጀት ላይ…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"ያልታወቀ"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"ለእርስዎ ደህንነት ሲባል የእርስዎ ጡባዊ ከዚህ ምንጭ የመጡ ያልታወቁ መተግበሪያዎችን እንዲጭን አልተፈቀደለትም።"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"ለእርስዎ ደህንነት ሲባል የእርስዎ ቴሌቪዥን ከዚህ ምንጭ የመጡ ያልታወቁ መተግበሪያዎችን እንዲጭን አልተፈቀደለትም።"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"ለእርስዎ ደህንነት ሲባል የእርስዎ ስልክ ከዚህ ምንጭ የመጡ ያልታወቁ መተግበሪያዎችን እንዲጭን አልተፈቀደለትም።"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"የእርስዎ ስልክ እና የግል ውሂብ በማይታወቁ መተግበሪያዎች ለሚደርሱ ጥቃቶች በይልበልጥ ተጋላጭ ናቸው። ይህን መተግበሪያ በመጫንዎ በእርስዎ ስልክ ላይ ለሚደርስ ማናቸውም ጉዳት ወይም መተግበሪያውን በመጠቀም ለሚከሰት የውሂብ መጥፋት ኃላፊነቱን እንደሚወስዱ ተስማምተዋል።"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"የእርስዎ ጡባዊ እና የግል ውሂብ በማይታወቁ መተግበሪያዎች ለሚደርሱ ጥቃቶች በይበልጥ ተጋላጭ ናቸው። ይህን መተግበሪያ በመጫንዎ በእርስዎ ጡባዊ ላይ ለሚደርስ ማናቸውም ጉዳት ወይም መተግበሪያውን በመጠቀም ለሚከሰት የውሂብ መጥፋት ኃላፊነቱን እንደሚወስዱ ተስማምተዋል።"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"የእርስዎ ቴሌቪዥን እና የግል ውሂብ በማይታወቁ መተግበሪያዎች ለሚደርሱ ጥቃቶች በይበልጥ ተጋላጭ ናቸው። ይህን መተግበሪያ በመጫንዎ በእርስዎ ቴሌቪዥን ላይ ለሚደርስ ማናቸውም ጉዳት ወይም መተግበሪያውን በመጠቀም ለሚከሰት የውሂብ መጥፋት ኃላፊነቱን እንደሚወስዱ ተስማምተዋል።"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"ቀጥል"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"ቅንብሮች"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"የWear መተግበሪያዎችን መጫን/ማራገፍ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ar-television/strings.xml b/packages/PackageInstaller/res/values-ar-television/strings.xml
new file mode 100644
index 0000000..9297b88
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ar-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"رفض وعدم طرح السؤال مرة أخرى"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"‏يمكنك تغيير ذلك لاحقًا من خلال الإعدادات &gt; التطبيقات"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"عرض تطبيقات النظام"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"أذونات التطبيق"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"أذونات التطبيق"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"أذونات <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"أذونات إضافية"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"أذونات <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ar-watch/strings.xml b/packages/PackageInstaller/res/values-ar-watch/strings.xml
new file mode 100644
index 0000000..19930ea
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ar-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"الرفض وعدم السؤال مرة أخرى"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"عرض تطبيقات النظام"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"لا يمكن التغيير"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"نعم"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"إلغاء"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml
new file mode 100644
index 0000000..613481e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ar/strings.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"أداة تثبيت الحزم"</string>
+    <string name="next" msgid="3057143178373252333">"التالي"</string>
+    <string name="install" msgid="5896438203900042068">"تثبيت"</string>
+    <string name="done" msgid="3889387558374211719">"تم"</string>
+    <string name="cancel" msgid="8360346460165114585">"إلغاء"</string>
+    <string name="installing" msgid="8613631001631998372">"جارٍ التثبيت..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"جارٍ تثبيت <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"تم تثبيت التطبيق."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"هل تريد تثبيت هذا التطبيق؟ سيكون بإمكانه الدخول إلى:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"هل تريد تثبيت هذا التطبيق؟ إنه لا يتطلب أي دخول خاص."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"هل تريد تثبيت تحديث لهذا التطبيق الحالي؟ لن تفقد بياناتك الحالية. سيكون بإمكان التطبيق المحدّث الدخول إلى:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"هل تريد تثبيت تحديث لهذا التطبيق المضمن؟ لن تفقد بياناتك الحالية. سيكون بإمكان التطبيق المحدّث الدخول إلى:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"هل تريد تثبيت تحديث لهذا التطبيق الحالي؟ لن يتم فقد بياناتك الحالية. كما أنه لا يتطلب أي دخول خاص."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"هل تريد تثبيت تحديث لهذا التطبيق المضمن؟ لن يتم فقد بياناتك الحالية. كما أنه لا يتطلب أي دخول خاص."</string>
+    <string name="install_failed" msgid="6579998651498970899">"التطبيق ليس مثبتًا."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"تم حظر تثبيت الحزمة."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"لم يتم تثبيت التطبيق لأن حزمة التثبيت تتعارض مع حزمة حالية."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"لم يتم تثبيت التطبيق لأنه ليس متوافقًا مع جهازك اللوحي."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"هذا التطبيق لا يتوافق مع جهاز التلفزيون."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"لم يتم تثبيت التطبيق لأنه ليس متوافقًا مع هاتفك."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"لم يتم تثبيت التطبيق لأن الحزمة تبدو غير صالحة."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"تعذر تثبيت <xliff:g id="APP_NAME">%1$s</xliff:g> على جهازك اللوحي."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"تعذر تثبيت <xliff:g id="APP_NAME">%1$s</xliff:g> على جهاز التلفزيون."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"تعذر تثبيت <xliff:g id="APP_NAME">%1$s</xliff:g> على هاتفك."</string>
+    <string name="launch" msgid="4826921505917605463">"فتح"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"لا يسمح المشرف بتثبيت التطبيقات التي يتم الحصول عليها من مصادر غير معروفة"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"يتعذر على هذا المستخدم تثبيت التطبيقات غير المعروفة"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"غير مسموح لهذا المستخدم بتثبيت التطبيقات"</string>
+    <string name="ok" msgid="3468756155452870475">"موافق"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"إدارة التطبيقات"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"نفدت مساحة التخزين"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"تعذر تثبيت <xliff:g id="APP_NAME">%1$s</xliff:g> يُرجى تحرير بعض المساحة والمحاولة مرة أخرى."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"لم يتم العثور على التطبيق"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"لم يتم العثور على التطبيق في قائمة التطبيقات المثبتة."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"غير مسموح به"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"غير مسموح للمستخدم الحالي بتنفيذ عملية إلغاء التثبيت هذه."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"الخطأ"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"تعذر إلغاء تثبيت التطبيق."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"إلغاء تثبيت التطبيق"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"إزالة التحديث"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> هو جزء من التطبيق التالي:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"هل تريد إزالة هذا التطبيق؟"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"هل تريد إزالة هذا التطبيق "<b>"لكل"</b>" المستخدمين؟ ستتم إزالة التطبيق وبياناته من "<b>"كل"</b>" المستخدمين على هذا الجهاز."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"هل تريد إزالة هذا التطبيق للمستخدم <xliff:g id="USERNAME">%1$s</xliff:g>؟"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات. وسيؤثر هذا في جميع مستخدمي هذا الجهاز، بما في ذلك من لديهم ملفات شخصية للعمل."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"عمليات إلغاء التثبيت الجارية"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"عمليات إلغاء التثبيت غير الناجحة"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"جارٍ الإزالة..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"جارٍ إلغاء تثبيت <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"انتهت الإزالة."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"تم إلغاء تثبيت <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"تعذّر إلغاء التثبيت."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"لم يتم إلغاء تثبيت <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> بنجاح."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"تعذر إلغاء تثبيت تطبيق مشرف الأجهزة النشطة"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"تعذر إلغاء تثبيت تطبيق مشرف الأجهزة النشطة لدى <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"هذا التطبيق مطلوب لبعض المستخدمين أو الملفات الشخصية وتم إلغاء تثبيته لآخرين."</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"هذا التطبيق مطلوب لملفك الشخصي ولا يمكن إلغاء تثبيته."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"مشرف الجهاز يحتاج إلى هذا التطبيق ولا يمكن إزالته."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"إدارة تطبيقات مشرف الجهاز"</string>
+    <string name="manage_users" msgid="3125018886835668847">"إدارة حسابات المستخدمين"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"تعذرت إزالة <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"حدثت مشكلة أثناء تحليل الحزمة."</string>
+    <string name="newPerms" msgid="6039428254474104210">"جديد"</string>
+    <string name="allPerms" msgid="1024385515840703981">"الكل"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"الخصوصية"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"الدخول إلى الجهاز"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"لا يتطلب هذا التحديث أي أذونات جديدة."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"رفض"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"مزيد من المعلومات"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"الرفض على أي حال"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> من <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"‏هل توافق على منح &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إذن <xliff:g id="ACTION">%2$s</xliff:g>؟"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"‏هل تريد السماح دائمًا للتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بهذا الإجراء: <xliff:g id="ACTION">%2$s</xliff:g>؟"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"أثناء استخدام التطبيق فقط"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"دائمًا"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"رفض وعدم طرح السؤال مرة أخرى"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> إذن غير مفعّل"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"كل الأذونات غير مفعّلة"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ليس هناك أذونات غير مفعّلة"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"سماح"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"التطبيقات"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"أذونات التطبيق"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"عدم السؤال مرة أخرى"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"ما مِن أذونات"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"أذونات إضافية"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"فتح معلومات التطبيق"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="zero"><xliff:g id="COUNT_1">%1$d</xliff:g>لا أذونات أخرى</item>
+      <item quantity="two">إذنان آخران (<xliff:g id="COUNT_1">%1$d</xliff:g>)</item>
+      <item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> أذونات أخرى</item>
+      <item quantity="many"><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_0">%1$d</xliff:g>إذن واحد آخر</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"‏تم تصميم هذا التطبيق لإصدار قديم من Android. وقد يؤدي رفض الإذن إلى عدم العمل على النحو المطلوب مرة أخرى."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"تنفيذ إجراء غير معروف"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"تم السماح لـ <xliff:g id="COUNT_0">%1$d</xliff:g> من أصل <xliff:g id="COUNT_1">%2$d</xliff:g> تطبيق"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"عرض النظام"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"إخفاء النظام"</string>
+    <string name="no_apps" msgid="1965493419005012569">"ليس هناك أي تطبيقات"</string>
+    <string name="location_settings" msgid="1774875730854491297">"إعدادات الموقع"</string>
+    <string name="location_warning" msgid="8778701356292735971">"يعد <xliff:g id="APP_NAME">%1$s</xliff:g> أحد مقدمي خدمات الموقع لهذا الجهاز. يمكن تعديل إمكانية الوصول إلى الموقع من إعدادات الموقع."</string>
+    <string name="system_warning" msgid="7103819124542305179">"في حال رفض هذا الإذن، قد لا تعمل ميزات أساسية في جهازك على النحو المنشود."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"فرضته إحدى السياسات"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"تمّ إيقاف الوصول إلى الخلفية بواسطة السياسة."</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"تمّ تفعيل الوصول إلى الخلفية بواسطة السياسة."</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"تمّ تفعيل الوصول إلى المقدمة بواسطة السياسة."</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"إعدادات يتحكم فيها المشرف"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"دائمًا"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"أثناء استخدام التطبيق فقط"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"أبدًا"</string>
+    <string name="loading" msgid="7811651799620593731">"جارٍ التحميل..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"كل الأذونات"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"إمكانات التطبيق الأخرى"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"طلب الإذن"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"تم اكتشاف طبقة متراكبة للشاشة"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"لتغيير إعداد هذا الإذن، يتعين عليك أولاً إيقاف الطبقة المتراكبة للشاشة من الإعدادات &gt; التطبيقات"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"فتح الإعدادات"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"‏لا تتوافق إجراءات التثبيت/إلغاء التثبيت مع نظام Android Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"‏اختيار ما تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إليه"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"‏تم تحديث &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. عليك اختيار ما تريد السماح لهذا التطبيق بالوصول إليه."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"إلغاء"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"متابعة"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"الأذونات الجديدة"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"الأذونات الحالية"</string>
+    <string name="message_staging" msgid="6151794817691100003">"جارٍ الطرح المرحلي للتطبيق…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"غير معروف"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"لأغراض الأمان، غير مسموح لجهازك اللوحي بتثبيت تطبيقات غير معروفة من هذا المصدر."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"لأغراض الأمان، غير مسموح لجهاز التلفزيون الذي تستخدمه بتثبيت تطبيقات غير معروفة من هذا المصدر."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"لأغراض الأمان، غير مسموح لهاتفك بتثبيت تطبيقات غير معروفة من هذا المصدر."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"يعتبر الهاتف والبيانات الشخصية أكثر عرضة لهجوم التطبيقات غير المعروفة. من خلال تثبيت هذا التطبيق، توافق على تحمل مسؤولية أي ضرر يحدث لهاتفك أو فقدان البيانات الذي قد ينتج عن استخدامه."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"يعتبر الجهاز اللوحي والبيانات الشخصية أكثر عرضة لهجوم التطبيقات غير المعروفة. من خلال تثبيت هذا التطبيق، توافق على تحمل مسؤولية أي ضرر يحدث للجهاز اللوحي أو فقدان البيانات الذي قد ينتج عن استخدامه."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"يعتبر جهاز التلفزيون والبيانات الشخصية أكثر عرضة لهجوم التطبيقات غير المعروفة. من خلال تثبيت هذا التطبيق، توافق على تحمل مسؤولية أي ضرر يحدث لجهاز التلفزيون أو فقدان البيانات الذي قد ينتج عن استخدامه."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"متابعة"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"الإعدادات"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"‏تثبيت / إلغاء تثبيت تطبيقات Android Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-as-television/strings.xml b/packages/PackageInstaller/res/values-as-television/strings.xml
new file mode 100644
index 0000000..2aaae1e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-as-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"অস্বীকাৰ কৰক আৰু পুনৰাই নুসুধিব"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"আপুনি ইয়াক পিছত ছেটিংসমূহ &gt; এপসমূহ-লৈ গৈ সলনি কৰিব পাৰিব"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"ছিষ্টেম এপসমূহ দেখুৱাওক"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"এপৰ অনুমতিসমূহ"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"এপৰ অনুমতিসমূহ"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> অনুমতিসমূহ"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"অতিৰিক্ত অনুমতিসমূহ"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> অনুমতিসমূহ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-as-watch/strings.xml b/packages/PackageInstaller/res/values-as-watch/strings.xml
new file mode 100644
index 0000000..3889fc4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-as-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"অস্বীকাৰ কৰক আৰু পুনৰাই নুসুধিব"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"ছিষ্টেম এপসমূহ দেখুৱাওক"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"সলনি কৰিব নোৱাৰি"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"হয়"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"বাতিল কৰক"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-as/strings.xml b/packages/PackageInstaller/res/values-as/strings.xml
new file mode 100644
index 0000000..de2f2d6
--- /dev/null
+++ b/packages/PackageInstaller/res/values-as/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"পেকেজ ইনষ্টলাৰ"</string>
+    <string name="next" msgid="3057143178373252333">"পৰৱৰ্তী"</string>
+    <string name="install" msgid="5896438203900042068">"ইনষ্টল কৰক"</string>
+    <string name="done" msgid="3889387558374211719">"সম্পন্ন হ\'ল"</string>
+    <string name="cancel" msgid="8360346460165114585">"বাতিল কৰক"</string>
+    <string name="installing" msgid="8613631001631998372">"ইনষ্টল কৰি থকা হৈছে…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ইনষ্টল কৰি থকা হৈছে…"</string>
+    <string name="install_done" msgid="3682715442154357097">"এপ্ ইনষ্টল কৰা হ\'ল।"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"আপুনি এই এপ্লিকেশ্বন ইনষ্টল কৰিব বিচাৰেনে? ই এইবোৰ ব্যৱহাৰ কৰিব পাৰিব:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"আপুনি এই এপ্লিকেশ্বন ইনষ্টল কৰিব বিচাৰেনে? ইয়াক ব্য়ৱহাৰ সম্পৰ্কীয় কোনো অনুমতিৰ প্ৰয়োজন নাই।"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"আপুনি পূর্বৰে পৰা থকা এপ্লিকেশ্বন আপডেট কৰিব বিচাৰেনে? আপুনি কোনো পুৰণি ডেটা নেহেৰুৱাই। আপডেট হোৱা এপ্লিকেশ্বনে এইবোৰ ব্যৱহাৰ কৰিব পাৰিব:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"আপুনি এই অন্তনির্মিত এপ্লিকেশ্বন আপডেট কৰিব বিচাৰেনে? আপুনি কোনো পুৰণি ডেটা নেহেৰুৱাই। আপডেট হোৱা এপ্লিকেশ্বনে এইবোৰ ব্যৱহাৰ কৰিব পাৰিব:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"আপুনি পূর্বৰে পৰা থকা এপ্লিকেশ্বন আপডেট কৰিব বিচাৰেনে? আপুনি কোনো পুৰণি ডেটা নেহেৰুৱাব। ব্যৱহাৰৰ বাবে ইয়াক কোনো বিশেষ অনুমতিৰ প্ৰয়োজন নাই৷"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"আপুনি এই অন্তনির্মিত এপ্লিকেশ্বন আপডেট কৰিব বিচাৰেনে? আপুনি নিজৰ পুৰণি ডেটা নেহেৰুৱাব৷ ব্যৱহাৰৰ বাবে ইয়াক কোনো বিশেষ অনুমতিৰ প্ৰয়োজন নাই৷"</string>
+    <string name="install_failed" msgid="6579998651498970899">"এপ্ ইনষ্টল কৰা হোৱা নাই।"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"পেকেজটোৰ ইনষ্টল অৱৰোধ কৰা হৈছে।"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"এপটো ইনষ্টল কৰিব পৰা নহ\'ল কাৰণ ইয়াৰ পেকেজ আৰু পূর্বৰে পৰা উপলব্ধ পেকেজৰ মাজত সমস্যাৰ সৃষ্টি হৈছে।"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"আপোনাৰ টেবলেটৰ সৈতে মিল নথকাৰ বাবে এপটো ইনষ্টল নহ\'ল।"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"আপোনাৰ টিভিত এই এপ্ চলিব নোৱাৰে।"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"আপোনাৰ ফ\'নৰ সৈতে মিল নথকাৰ বাবে এপটো ইনষ্টল নহ\'ল।"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"পেকেজ মান্য নোহোৱাৰ বাবে এপটো ইনষ্টল নহ\'ল।"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক আপোনাৰ টে\'বলেটত ইনষ্টল কৰিব পৰা নগ\'ল৷"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"আপোনাৰ টিভিত <xliff:g id="APP_NAME">%1$s</xliff:g> ইনষ্টল কৰিব পৰা নগ\'ল।"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক আপোনাৰ ফ\'নত ইনষ্টল কৰিব পৰা নগ\'ল৷"</string>
+    <string name="launch" msgid="4826921505917605463">"খোলক"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"আপোনাৰ প্ৰশাসকে অজ্ঞাত উৎসৰ পৰা লাভ কৰা এপ্ ইনষ্টল কৰাৰ অনুমতি দিয়া নাই"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"এই ব্যৱহাৰকাৰীয়ে অজ্ঞাত উৎসৰপৰা লাভ কৰা এপসমূহ ইনষ্টল কৰিব নোৱাৰে"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"এই ব্যৱহাৰকাৰীক এপ্ ইনষ্টল কৰিবলৈ অনুমতি দিয়া হোৱা নাই"</string>
+    <string name="ok" msgid="3468756155452870475">"ঠিক আছে"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"এপসমূহ পৰিচালনা কৰক"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"পৰ্যাপ্ত খালী ঠাই নাই"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক ইনষ্টল কৰিব পৰা নগ\'ল। কিছু খালী ঠাই উলিয়াই পুনৰ চেষ্টা কৰক৷"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"এপ্ পোৱা নগ\'ল"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"ইনষ্টল হৈ থকা এপসমূহৰ তালিকাত এই এপটো পোৱা নগ\'ল।"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"অনুমতি দিয়া হোৱা নাই"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"বর্তমানৰ ব্যৱহাৰকাৰীক আনইনষ্টল কৰিবলৈ অনুমতি দিয়া হোৱা নাই।"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"আসোঁৱাহ"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"এপ্ আনইনষ্টল কৰিব পৰা নাযাব।"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"এপ্ আনইনষ্টল কৰক"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"আপডেট আনইনষ্টল কৰক"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> হৈছে তলৰ এপটোৰ এটা অংশ:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"আপুনি এই এপটো আনইনষ্টল কৰিব বিচাৰেনে?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"আপুনি "<b>"সকলো"</b>" ব্যৱহাৰকাৰীৰ বাবে এই এপটো আনইনষ্টল কৰিবলৈ বিচাৰেনে? এপ্লিকেশ্বন আৰু ইয়াৰ ডেটাক ডিভাইচটোত থকা "<b>"সকলো"</b>" ব্যৱহাৰকাৰীৰ পৰা আঁতৰোৱা হ\'ব৷"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"আপুনি ব্যৱহাৰকাৰী <xliff:g id="USERNAME">%1$s</xliff:g>ৰ বাবে এই এপটো আনইনষ্টল কৰিব বিচাৰেনে?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"এই এপটো ফেক্টৰী সংস্কৰণৰ সৈতে সলনি কৰিব বিচাৰেনে? সকলো তথ্য় মচা হ\'ব।"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"এই এপটো ফেক্টৰী সংস্কৰণৰ সৈতে সলনি কৰিব বিচাৰেনে? সকলো তথ্য় মচা হ\'ব। ইয়াৰ প্ৰভাৱ কার্মস্থানৰ প্ৰফাইল থকা ডিভাইচটোৰ ব্য়ৱহাৰকাৰীসকলৰ লগতে অইন সকলো ব্য়ৱহাৰকাৰীৰ ওপৰতো পৰিব।"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"আনইনষ্টল হৈ থকা বস্তুবোৰ"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"আনইনষ্টল কৰিব নোৱাৰা বস্তুবোৰ"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"আনইনষ্টল কৰি থকা হৈছে…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ক আনইনষ্টল কৰি থকা হৈছে…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"আনইনষ্টল কাৰ্যটো সমাপ্ত হ\'ল৷"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> আনইনষ্টল কৰা হ\'ল"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"আনইনষ্টল কৰিব পৰা নগ\'ল।"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ক আনইনষ্টল কৰিব পৰা নগ\'ল।"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"ডিভাইচৰ সক্ৰিয় প্ৰশাসক এপ্ আনইনষ্টল কৰিব নোৱাৰি"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g>ৰ সক্ৰিয় ডিভাইচৰ প্ৰশাসকীয় এপ্ আনইনষ্টল কৰিব নোৱাৰি"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"এই এপটো কিছুসংখ্য়ক ব্যৱহাৰকাৰী বা প্ৰ\'ফাইলৰ বাবে প্ৰয়োজনীয় আৰু বাকীসকলৰ বাবে ইয়াক আনইনষ্টল কৰা হৈছে"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"আপোনাৰ প্ৰ\'ফাইলৰ বাবে এই এপৰ প্ৰয়োজন আছে গতিকে আনইনষ্টল কৰিব পৰা নাযায়।"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"আপোনাৰ ডিভাইচৰ প্ৰশাসকে এই এপটো ৰখাটো বাধ্যতামূলক কৰি ৰাখিছে, গতিকে ইয়াক আনইনষ্টল কৰিব পৰা নাযায়।"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"ডিভাইচৰ প্ৰশাসকীয় এপসমূহ পৰিচালনা কৰক"</string>
+    <string name="manage_users" msgid="3125018886835668847">"ব্য়ৱহাৰকাৰীসকলক পৰিচালনা কৰক"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক আনইনষ্টল কৰিব নোৱাৰি৷"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"পেকেজটো পাৰ্ছ কৰোঁতে এটা সমস্যাই দেখা দিছিল।"</string>
+    <string name="newPerms" msgid="6039428254474104210">"নতুন"</string>
+    <string name="allPerms" msgid="1024385515840703981">"সকলো"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"গোপনীয়তা"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"ডিভাইচৰ ব্যৱহাৰ"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"এই আপডেটক কোনো নতুন অনুমতিৰ প্ৰয়োজন নাই।"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"প্ৰত্যাখ্যান কৰক"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"অধিক তথ্য"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"যিহ\'লেও অস্বীকাৰ কৰক"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>ৰ ভিতৰত<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>টা"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক <xliff:g id="ACTION">%2$s</xliff:g>ৰ বাবে অনুমতি দিবনে?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক সদায় <xliff:g id="ACTION">%2$s</xliff:g> কৰাৰ অনুমতি দিবনে?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"এপ্ ব্য়ৱহাৰ কৰি থাকোঁতে মাত্ৰ"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"সদায়"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"অস্বীকাৰ কৰক আৰু পুনৰাই নুসুধিব"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g>টা অক্ষম কৰা হ\'ল"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"সকলো অক্ষম কৰা হ\'ল"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"একো অক্ষম কৰা হোৱা নাই"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"অনুমতি দিয়ক"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"এপসমূহ"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"এপক দিয়া অনুমতিসমূহ"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"পুনৰাই নুসুধিব"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"কোনো অনুমতি নাই"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"অতিৰিক্ত অনুমতিসমূহ"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"এপৰ তথ্য় খোলক"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <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="old_sdk_deny_warning" msgid="3872277112584842615">"এই এপটো Androidৰ এটা পুৰণা সংস্কৰণৰ বাবে প্ৰস্তুত কৰা হৈছিল। অনুমতি নিদিলে ই বিচৰাধৰণে কাম নকৰিবও পাৰে।"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"অজ্ঞাত কাৰ্য কৰিব পাৰে"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g>ৰ ভিতৰত<xliff:g id="COUNT_0">%1$d</xliff:g>টা এপক অনুমতি দিয়া হৈছে"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"ছিষ্টেম দেখুৱাওক"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"ছিষ্টেম লুকুৱাওক"</string>
+    <string name="no_apps" msgid="1965493419005012569">"কোনো এপে এই অনুমতি বিচৰা নাই"</string>
+    <string name="location_settings" msgid="1774875730854491297">"অৱস্থান ছেটিংসমূহ"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> এই ডিভাইচৰ অৱস্থান সেৱা প্ৰদানকাৰী। অৱস্থানৰ ছেটিংসমূহত অৱস্থানৰ ব্যৱহাৰ সংশোধন কৰিব পাৰি।"</string>
+    <string name="system_warning" msgid="7103819124542305179">"আপুনি যদি এই অনুমতি প্ৰদান নকৰে, তেন্তে আপোনাৰ ডিভাইচৰ মৌলিক সুবিধাসমূহে বিচৰাধৰণে কাম নকৰিবও পাৰে।"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"নীতিৰ যোগেদি বলৱৎ কৰা"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"নীতি অনুসৰি নেপথ্য় চোৱা সুবিধা অক্ষম কৰা হ’ল"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"নীতি অনুসৰি নেপথ্য় চোৱা সুবিধা সক্ষম কৰা হ’ল"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"নীতি অনুসৰি অগ্ৰভূমি চোৱা সুবিধা সক্ষম কৰা হ’ল"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"প্ৰশাসকে নিয়ন্ত্ৰিত কৰা"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"সদায়"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"এপ্ ব্য়ৱহাৰ কৰি থাকোঁতে মাত্ৰ"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"কেতিয়াও নহয়"</string>
+    <string name="loading" msgid="7811651799620593731">"ল\'ড কৰি থকা হৈছে…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"সকলো অনুমতি"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"অন্য এপৰ কার্যক্ষমতাসমূহ"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"অনুমতি বিচাৰি কৰা অনুৰোধ"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"স্ক্ৰীণ অভাৰলে\' চিনাক্ত কৰা হৈছে"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"এই অনুমতিৰ ছেটিং সলনি কৰিবলৈ আপুনি প্ৰথমে ছেটিংসমূহ &gt; এপসমূহ-লৈ গৈ স্ক্ৰীণ অভাৰলে\' অফ কৰিব লাগিব।"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"ছেটিংসমূহ খোলক"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android ৱেৰ"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"ইনষ্টল/আনইনষ্টল কাৰ্য Wearত কৰিব নোৱাৰি।"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক কি কিত প্ৰৱেশ কৰিবলৈ অনুমতি দিব বাছনি কৰক"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; আপডেট কৰা হৈছে। এই এপক কি কিত প্ৰৱেশ কৰিবলৈ অনুমতি দিব বাছনি কৰক।"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"বাতিল কৰক"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"অব্যাহত ৰাখক"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"নতুন অনুমতিসমূহ"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"এপে বর্তমান ব্যৱহাৰ কৰি থকা অনুমতিসমূহ"</string>
+    <string name="message_staging" msgid="6151794817691100003">"এপৰ অন্তিম পর্যায়ৰ পৰীক্ষণ চলি আছে…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"অজ্ঞাত"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"আপোনাৰ টেবলেটটো যাতে সুৰক্ষিত থাকে তাৰ বাবে আপোনাৰ টেবলেটটোক এই উৎসৰ পৰা অজ্ঞাত এপসমূহ ইনষ্টল কৰিবলৈ অনুমতি দিয়া হোৱা নাই।"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"আপোনাৰ টিভিটো যাতে সুৰক্ষিত থাকে তাৰ বাবে আপোনাৰ টিভিটোক এই উৎসৰ পৰা অজ্ঞাত এপসমূহ ইনষ্টল কৰিবলৈ অনুমতি দিয়া হোৱা নাই।"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"আপোনাৰ ফ\'নটো যাতে সুৰক্ষিত থাকে তাৰ বাবে আপোনাৰ ফ\'নটোক এই উৎসৰ পৰা অজ্ঞাত এপসমূহ ইনষ্টল কৰিবলৈ অনুমতি দিয়া হোৱা নাই।"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"আপোনাৰ টিভি আৰু ব্যক্তিগত ডেটা অজ্ঞাত এপৰ আক্ৰমণৰ বলি হোৱাৰ সম্ভাৱনা অধিক। এই এপটো ইনষ্টল কৰি আপুনি ইয়াক ব্যৱহাৰ কৰাৰ ফলত আপোনাৰ ফ\'নত কোনো ক্ষতি হ\'লে বা ডেটা হেৰুৱালে আপুনিয়েই দায়ী হ\'ব বুলি সন্মত।"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"আপোনাৰ টেবলেট আৰু ব্যক্তিগত ডেটা অজ্ঞাত এপৰ আক্ৰমণৰ বলি হোৱাৰ সম্ভাৱনা অধিক। এই এপটো ইনষ্টল কৰি আপুনি ইয়াক ব্যৱহাৰ কৰাৰ ফলত আপোনাৰ টেবলেটত কোনো ক্ষতি হ\'লে বা ডেটা হেৰুৱালে আপুনিয়েই দায়ী হ\'ব বুলি সন্মত।"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"আপোনাৰ টিভি আৰু ব্যক্তিগত ডেটা অজ্ঞাত এপৰ আক্ৰমণৰ বলি হোৱাৰ সম্ভাৱনা অধিক। এই এপটো ইনষ্টল কৰি আপুনি ইয়াক ব্যৱহাৰ কৰাৰ ফলত আপোনাৰ টিভিত কোনো ক্ষতি হ\'লে বা ডেটা হেৰুৱালে আপুনিয়েই দায়ী হ\'ব বুলি সন্মত।"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"অব্যাহত ৰাখক"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"ছেটিংবোৰ"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"ৱেৰ এপসমূহ ইনষ্টল/আনইনষ্টল কৰি থকা হৈছে"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-az-television/strings.xml b/packages/PackageInstaller/res/values-az-television/strings.xml
new file mode 100644
index 0000000..92fa527
--- /dev/null
+++ b/packages/PackageInstaller/res/values-az-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Rədd edin və daha soruşmayın"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Bunu sonra Ayarlar vəTətbiqlər bölməsindən dəyişə bilərsiniz"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Sistem tətbiqlərini göstərin"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Tətbiq icazələri"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Tətbiq icazələri"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> icazələri"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Əlavə icazələr"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> icazələri"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-az-watch/strings.xml b/packages/PackageInstaller/res/values-az-watch/strings.xml
new file mode 100644
index 0000000..ef6723b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-az-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Rədd edin, bir daha soruşmayın"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Sistem tətbiqlərini göstərin"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Dəyişdirilə bilməz"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Bəli"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Ləğv edin"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-az/strings.xml b/packages/PackageInstaller/res/values-az/strings.xml
new file mode 100644
index 0000000..d870887
--- /dev/null
+++ b/packages/PackageInstaller/res/values-az/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Paket quraşdırıcı"</string>
+    <string name="next" msgid="3057143178373252333">"Növbəti"</string>
+    <string name="install" msgid="5896438203900042068">"Quraşdır"</string>
+    <string name="done" msgid="3889387558374211719">"Hazırdır"</string>
+    <string name="cancel" msgid="8360346460165114585">"Ləğv et"</string>
+    <string name="installing" msgid="8613631001631998372">"Quraşdırılır..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> quraşdırılır…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Tətbiq quraşdırılıb."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Bu tətbiqi quraşdırmaq istəyirsiniz? Tətbiq buraya giriş əldə edəcək:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Bu tətbiqi quraşdırmaq istəyirsiniz? Hər hansı bir xüsusi keçid tələb etmir."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Bu cari tətbiq güncəllənməsini quraşdırmaq istəyirsiniz? Hazırki datanız itməyəcək. Güncəllənmiş tətbiq aşağıdakılara çıxış əldə edəcək:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Daxili tətbiqdən yenilənməni quraşdırmaq istəyirsiniz? Hazırki datanız itməyəcək. Yenilənmiş tətbiq aşağıdakılara çıxış əldə edəcək:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Bu cari tətbiq güncəllənməsini quraşdırmaq istəyirsiniz? Hazırki datanız itməyəcək. O, xüsusi giriş tələb etmir."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Bu daxili tətbiq güncəllənməsini quraşdırmaq istəyirsiniz? Hazırki datanız itməyəcək. O, xüsusi giriş tələb etmir."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Tətbiq quraşdırılmayıb."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Paket yüklənməyə qarşı blok edildi."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Bu paketin mövcud paket ilə ziddiyəti səbəbiylə tətbiq quraşdırılmadı."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Bu tətbiq planşetinizə uyğun gəlmədiyi üçün tətbiq quraşdırılmadı."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Bu proqram TV-nizlə uyğun gəlmir."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Bu tətbiq telefonunuza uyğun gəlmədiyi üçün tətbiq quraşdırılmadı."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Paket yanlış kimi göründüyü üçün tətbiq quraşdırılmadı."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> planşetinizə yüklənə bilmədi."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> proqramını TV-nizdə quraşdırmaq mümkün olmadı."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> telefonunuza quraşdırıla bilmədi."</string>
+    <string name="launch" msgid="4826921505917605463">"Aç"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Naməlum mənbələrdən əldə edilmiş tətbiqlərin quraşdırılmasına admin tərəfindən icazə verilmir"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Naməlum tətbiqlər bu istifadəçi tərəfindən quraşdırıla bilməz"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Bu istifadəçinin tətbiqi quraşdırmaq üçün icazəsi yoxdur"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Tətbiqləri idarə et"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Boş yer yoxdur"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> quraşdırıla bilməz. Yaddaş üçün yer boşaldıb yenidən təkrar edin."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Tətbiq tapılmadı"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Tətbiq quraşdırılmış tətbiqlər siyahısında tapılmadı."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"İcazə verilmir"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Cari istifadəçiyə bu silinməni həyata keçirməyə icazə verilmir."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Xəta"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Tətbiq sistemdən silinmədi."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Tətbiqi qaldır"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Güncəlləməni sil"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> bu tətbiqin hissəsidir:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Bu tətbiqi aradan qaldırmaq istəyirsiniz mi?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Bu tətbiqi "<b>"bütün"</b>" istifadəçilər üçün silmək istəyirsiz? Tətbiq və onun datası cihazdakı "<b>"bütün"</b>" istifadəçilər üçün silinəcək."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"<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="1394549691152728409">"Tətbiq zavod versiyası ilə əvəz olunsun? Bütün data silinəcək."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Tətbiq zavod versiyası ilə əvəz olunsun? Bütün data silinəcək. Bu, iş profilləri olanlar da daxil olmaqla bu cihazın bütün istifadəçilərinə təsir edir."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"İşləyən sistemlər silinmələr"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Uğursuz olan sistemlər silinmələr"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Silinir..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> sistemdən silinir…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Sistemdən silmə tamamlandı."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> sistemdən silindi"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Aradan qaldırılma uğursuz oldu."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> sistemdən silinməsi uğursuz oldu."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Aktiv cihaz admin tətbiqini sistemdən silmək mümkün olmadı"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> üçün aktiv cihaz admin tətbiqini sistemdən silmək mümkün olmadı"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Bu tətbiq bəzi istifadəçi və profillər tərəfindən tələb olunur və digərləri üçün silinib"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Bu tətbiq profil üçün tələb olunur və silinə bilməz."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Bu tətbiq cihaz administratoru tərəfindən tələb olunur və sistemdən silinə bilməz."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Cihaz admin tətbiqlərini idarə edin"</string>
+    <string name="manage_users" msgid="3125018886835668847">"İstifadəçiləri idarə edin"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> sistemdən silinə bilməz."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Paketin təhlilində problem var idi."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Yeni"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Hamısı"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Məxfilik"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Qurğu icazəsi"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Bu güncəllənmə heç bir icazə istəmir"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Rədd edin"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Daha ətraflı"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Hər bir halda rədd edin"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> icazədən <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> ədəd"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə <xliff:g id="ACTION">%2$s</xliff:g> fəaliyyəti üçün icazə verilsin?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin <xliff:g id="ACTION">%2$s</xliff:g> əməliyyatına daima icazə verilsin?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Ancaq tətbiq istifadəsi zamanı"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Həmişə"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Rədd edin və daha soruşmayın"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> deaktiv edildi"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"hamısı deaktiv edildi"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"heç biri deaktiv edilmədi"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"İcazə verin"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Tətbiqlər"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Tətbiq icazələri"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Bir daha soruşmayın"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"İcazə yoxdur"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Əlavə icazələr"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Tətbiq məlumatını açın"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">daha <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">daha <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Bu tətbiq köhnə Android versiyası üçün nəzərdə tutulub. İcazəni rədd etmək onun lazımi şəkildə işləməməsinə səbəb ola bilər."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"naməlum əməliyyat etmək"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g> tətbiqdən <xliff:g id="COUNT_0">%1$d</xliff:g> ədədinə icazə var"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Sistemi göstərin"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Sistemi gizlədin"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Tətbiq yoxdur"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Məkan Ayarları"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> bu cihaz üçün məkan xidmətləri təminatçısıdır. Məkana giriş məkan ayarlarından dəyişdirilə bilər."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Bu icazəni rədd etsəniz, cihazınızın əsas funksiyaları lazımi qaydada işləməyə bilər."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Siyasət tərəfindən tətbiq olunur"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Arxa fon girişi siyasətə əsasən deaktiv edildi"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Arxa fon girişi siyasətə əsasən aktiv edildi"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Ön fon girişi siyasətə əsasən aktiv edildi"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Admin tərəfindən nəzarət olunur"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Həmişə"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Ancaq tətbiq istifadəsi zamanı"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Heç vaxt"</string>
+    <string name="loading" msgid="7811651799620593731">"Yüklənir…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Bütün icazələr"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Digər tətbiq imkanları"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"İcazə sorğusu"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Ekran örtüyü aşkarlandı"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Bu icazə ayarını dəyişdirmək üçün əvvəldə Ayarlar və Tətbiqlər bölməsindən ekran örtüyünü söndürməlisiniz"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Ayarları açın"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Yükləmə/Silmə fəaliyyətləri Wear\'də dəstəklənmir."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin giriş hüququnu seçin"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqi güncəlləndi. Bu tətbiqin giriş hüququnu seçin."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Ləğv edin"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Davam edin"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Yeni icazələr"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Cari icazələr"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Tətbiq hazırlanır..."</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Naməlum"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Təhlükəsizliyiniz üçün planşetə bu mənbədən olan naməlum tətbiqləri quraşdırmağa icazə verilmir."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Təhlükəsizliyiniz üçün TV-yə bu mənbədən olan naməlum tətbiqləri quraşdırmağa icazə verilmir."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Təhlükəsizliyiniz üçün telefona bu mənbədən olan naməlum tətbiqləri quraşdırmağa icazə verilmir."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Telefon və şəxsi data naməlum tətbiqlərin hücumuna qarşı daha həssasdır. Bu tətbiqi quraşdırmaqla telefona dəyə biləcək zərər və ya onun istifadəsi nəticəsində baş verən data itkisinə görə məsuliyyət daşıdığınızı qəbul edirsiniz."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Planşet və şəxsi data naməlum tətbiqlərin hücumuna qarşı daha həssasdır. Bu tətbiqi quraşdırmaqla planşetə dəyə biləcək zərər və ya onun istifadəsi nəticəsində baş verə biləcək data itkisinə görə məsuliyyət daşıdığınızı qəbul edirsiniz."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Tv və şəxsi data naməlum tətbiqlərin hücumuna qarşı daha həssasdır. Bu tətbiqi quraşdırmaqla Tv\'ə dəyə biləcək zərər və ya onun istifadəsi nəticəsində baş verən data itkisinə görə məsuliyyət daşıdığınızı qəbul edirsiniz."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Davam edin"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Ayarlar"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear tətbiqləri quraşdırılır/silinir"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-b+sr+Latn-television/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn-television/strings.xml
new file mode 100644
index 0000000..5dce759
--- /dev/null
+++ b/packages/PackageInstaller/res/values-b+sr+Latn-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Odbij i ne pitaj ponovo"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Ovo možete da promenite kasnije u Podešavanjima &gt; Aplikacije"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Prikaži sistemske aplikacije"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Dozvole za aplikacije"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Dozvole za aplikacije"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Dozvole za aplikaciju <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Dodatne dozvole"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Dozvole za aplikaciju <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-b+sr+Latn-watch/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn-watch/strings.xml
new file mode 100644
index 0000000..63a44db
--- /dev/null
+++ b/packages/PackageInstaller/res/values-b+sr+Latn-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Odbij i ne pitaj ponovo"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Prikaži sistemske aplikacije"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Ne može da se promeni"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Da"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Otkaži"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..4d8772f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Upakovani program za instalaciju"</string>
+    <string name="next" msgid="3057143178373252333">"Dalje"</string>
+    <string name="install" msgid="5896438203900042068">"Instaliraj"</string>
+    <string name="done" msgid="3889387558374211719">"Gotovo"</string>
+    <string name="cancel" msgid="8360346460165114585">"Otkaži"</string>
+    <string name="installing" msgid="8613631001631998372">"Instaliranje..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Instalira se <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplikacija je instalirana."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Želite li da instalirate ovu aplikaciju? Imaće pristup sledećem:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Želite li da instalirate ovu aplikaciju? Ne zahteva poseban pristup."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Želite li da instalirate ažuriranje za ovu postojeću aplikaciju? Postojeći podaci neće biti izgubljeni. Ažurirana aplikacija imaće pristup sledećem:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Želite li da instalirate ažuriranje za ovu ugrađenu aplikaciju? Postojeći podaci neće biti izgubljeni. Ažurirana aplikacija će imati pristup sledećem:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Da li želite da instalirate ažuriranje ove postojeće aplikacije? Postojeći podaci neće biti izgubljeni. Nije potreban poseban pristup."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Da li želite da instalirate ažuriranje ove ugrađene aplikacije? Postojeći podaci neće biti izgubljeni. Nije potreban poseban pristup."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Aplikacija nije instalirana."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Instaliranje paketa je blokirano."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Aplikacija nije instalirana jer je paket neusaglašen sa postojećim paketom."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Aplikacija nije instalirana jer nije kompatibilna sa tabletom."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Ova aplikacija nije kompatibilna sa TV-om."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Aplikacija nije instalirana jer nije kompatibilna sa telefonom."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Aplikacija nije instalirana jer je paket nevažeći."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Nije moguće instalirati aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> na tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Nismo uspeli da instaliramo <xliff:g id="APP_NAME">%1$s</xliff:g> na TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Nije moguće instalirati aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> na telefon."</string>
+    <string name="launch" msgid="4826921505917605463">"Otvori"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Administrator ne dozvoljava instaliranje aplikacija dobijenih iz nepoznatih izvora"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Ovaj korisnik ne može da instalira nepoznate aplikacije"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Ovom korisniku nije dozvoljeno da instalira aplikacije"</string>
+    <string name="ok" msgid="3468756155452870475">"Potvrdi"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Upravljanje aplikacijama"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Nema više mesta"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Nije moguće instalirati aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>. Oslobodite dodatni prostor i pokušajte ponovo."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Aplikacija nije pronađena"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Aplikacija nije pronađena na listi instaliranih aplikacija."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Nije dozvoljeno"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Aktuelnom korisniku nije dozvoljeno da obavi ovo deinstaliranje."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Greška"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Deinstaliranje aplikacije nije uspelo."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Deinstaliranje aplikacije"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Deinstaliranje ažuriranja"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je deo sledeće aplikacije:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Da li želite da deinstalirate ovu aplikaciju?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Da li želite da deinstalirate ovu aplikaciju za "<b>"sve"</b>" korisnike? Aplikacija i podaci koji se na nju odnose biće uklonjeni za "<b>"sve"</b>" korisnike ovog uređaja."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Želite li da deinstalirate ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Želite li da zamenite ovu aplikaciju fabričkom verzijom? Svi podaci će biti uklonjeni."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Ž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="uninstalling_notification_channel" msgid="5698369661583525583">"Aktivna deinstaliranja"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Neuspela deinstaliranja"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Deinstaliranje..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> se deinstalira…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Deinstaliranje je završeno."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Aplikacija <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> je deinstalirana"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Deinstaliranje nije uspelo."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Deinstaliranje aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> nije uspelo."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Ne možete da deinstalirate aplikaciju za aktivnog administratora uređaja"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Ne možete da deinstalirate aplikaciju za aktivnog administratora uređaja za <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Ova aplikacija je potrebna za neke korisnike ili profile, a deinstalirana je za druge"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Ova aplikacija je potrebna za vaš profil i ne može da se deinstalira."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Ova aplikacija je potrebna administratoru uređaja i ne može da se deinstalira."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Upravljaj aplikacijama za administratore uređaja"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Upravljaj korisnicima"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Nije moguće deinstalirati aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Došlo je do problema pri raščlanjivanju paketa."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Novo"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Sve"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privatnost"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Pristup uređaju"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Ovo ažuriranje ne zahteva nove dozvole."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Odbaci"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Više informacija"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Ipak odbij"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>. od <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Želite li da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Želite li uvek da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Samo dok se aplikacija koristi"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Uvek"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Odbij i ne pitaj ponovo"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"Onemogućenih: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"sve su onemogućene"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"nijedna nije onemogućena"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Dozvoli"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplikacije"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Dozvole za aplikacije"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Ne pitaj ponovo"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Nema dozvola"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Dodatne dozvole"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Otvori informacije o aplikaciji"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one">još <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="few">još <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="other">još <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Ova aplikacija je dizajnirana za stariju verziju Android-a. Ako odbijete dozvolu, ona možda više neće pravilno da funkcioniše."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"obavlja nepoznatu radnju"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> od <xliff:g id="COUNT_1">%2$d</xliff:g> aplikacija ima dozvolu"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Prikaži sistemske"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Sakrij sistemske"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Nema aplikacija"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Podešavanja lokacije"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> pruža usluge lokacije za ovaj uređaj. Pristup lokaciji možete da izmenite u podešavanjima lokacije."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Ako odbijete ovu dozvolu, osnovne funkcije uređaja možda neće više funkcionisati ispravno."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Primenjuje se u skladu sa smernicama"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Pristup u pozadini je onemogućen smernicama"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Pristup u pozadini je omogućen smernicama"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Pristup u prvom planu je omogućen smernicama"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Kontroliše administrator"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Uvek"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Samo dok se aplikacija koristi"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nikada"</string>
+    <string name="loading" msgid="7811651799620593731">"Učitava se…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Sve dozvole"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Ostale mogućnosti aplikacije"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Zahtev za dozvolu"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Otkriven je element koji prekriva sadržaj ekrana"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Da biste promenili podešavanje ove dozvole, prvo treba da isključite element koji prekriva sadržaj ekrana u odeljku Podešavanja &gt; Aplikacije"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Otvori podešavanja"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Radnje Instaliraj/Deinstaliraj nisu podržane u Wear-u."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Izaberite čemu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; može da pristupa"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; je ažurirana. Izaberite čemu ova aplikacija može da pristupa."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Otkaži"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Nastavi"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nove dozvole"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Aktuelne dozvole"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Aplikacija se priprema…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Nepoznato"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Tabletu iz bezbednosnih razloga nije dozvoljeno da instalira nepoznate aplikacije iz ovog izvora."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Televizoru iz bezbednosnih razloga nije dozvoljeno da instalira nepoznate aplikacije iz ovog izvora."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Telefonu iz bezbednosnih razloga nije dozvoljeno da instalira nepoznate aplikacije iz ovog izvora."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Telefon i lični podaci su podložniji napadu nepoznatih aplikacija. Ako instalirate ovu aplikaciju, prihvatate da ste odgovorni za eventualna oštećenja telefona ili gubitak podataka do kojih može da dođe zbog njenog korišćenja."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Tablet i lični podaci su podložniji napadu nepoznatih aplikacija. Ako instalirate ovu aplikaciju, prihvatate da ste odgovorni za eventualna oštećenja tableta ili gubitak podataka do kojih može da dođe zbog njenog korišćenja."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"TV i lični podaci su podložniji napadu nepoznatih aplikacija. Ako instalirate ovu aplikaciju, prihvatate da ste odgovorni za eventualna oštećenja TV-a ili gubitak podataka do kojih može da dođe zbog njenog korišćenja."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Nastavi"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Podešavanja"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Instaliranje/deinstaliranje Wear aplikacija"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-be-television/strings.xml b/packages/PackageInstaller/res/values-be-television/strings.xml
new file mode 100644
index 0000000..befb367
--- /dev/null
+++ b/packages/PackageInstaller/res/values-be-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Адхіліць і больш не пытацца"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Пазней гэта можна змянiць у раздзеле «Налады &gt; Праграмы»"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Паказваць сістэмныя праграмы"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Дазволы праграм"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Дазволы праграм"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Дазволы праграмы <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Дадатковыя дазволы"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Дазволы праграмы <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-be-watch/strings.xml b/packages/PackageInstaller/res/values-be-watch/strings.xml
new file mode 100644
index 0000000..99b2ce8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-be-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Адхіліць, больш не пытацца"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Паказваць сістэмныя праграмы"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Нельга змяніць"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Так"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Скасаваць"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-be/strings.xml b/packages/PackageInstaller/res/values-be/strings.xml
new file mode 100644
index 0000000..1b65e29
--- /dev/null
+++ b/packages/PackageInstaller/res/values-be/strings.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Усталёўшчык пакетаў"</string>
+    <string name="next" msgid="3057143178373252333">"Далей"</string>
+    <string name="install" msgid="5896438203900042068">"Усталяваць"</string>
+    <string name="done" msgid="3889387558374211719">"Гатова"</string>
+    <string name="cancel" msgid="8360346460165114585">"Скасаваць"</string>
+    <string name="installing" msgid="8613631001631998372">"Усталяванне..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Ідзе ўсталяванне <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Прыкладанне ўсталявана."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Усталяваць гэта прыкладанне? Яно атрымае доступ да:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Усталяваць гэта прыкладанне? Яно не патрабуе спецыяльнага доступу."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Усталяваць абнаўленне для гэтага існуючага прыкладання? Існуючыя дадзеныя не будуць страчаны. Абноўленае прыкладанне атрымае доступ да:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Усталяваць абнаўленне для гэтага ўбудаванага прыкладання? Існуючыя дадзеныя не будуць страчаны. Абноўленае прыкладанне атрымае доступ да:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Усталяваць абнаўленне для гэтага прыкладання? Вашы iснуючыя дадзеныя не будуць згублены. Спецыяльны доступ не патрабуецца."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Усталяваць абнаўленне для гэтага ўбудаванага прыкладання? Вашы iснуючыя дадзеныя не будуць згублены. Спецыяльны доступ не патрабуецца."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Прыкладанне не ўсталявана."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Для пакета заблакіравана магчымасць усталявання."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Праграма не ўсталявана, таму што пакет канфліктуе з існуючым пакетам."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Праграма не ўсталявана, таму што яна несумяшчальная з вашым планшэтам."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Гэта праграма несумяшчальная з вашым тэлевізарам."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Праграма не ўсталявана, таму што яна несумяшчальная з вашым тэлефонам."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Праграма не ўсталявана, таму што пакет, магчыма, з\'яўляецца несапраўдным."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"На гэтым планшэце немагчыма ўсталяваць прыкладанне <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"На вашым тэлевізары немагчыма ўсталяваць праграму <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"На гэтым тэлефоне немагчыма ўсталяваць прыкладанне <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="launch" msgid="4826921505917605463">"Адкрыць"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Ваш адміністратар не дазваляе ўсталёўку праграм з невядомых крыніц."</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Гэты карыстальнік не можа ўсталёўваць невядомыя праграмы"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Гэты карыстальнік не можа ўсталёўваць праграмы"</string>
+    <string name="ok" msgid="3468756155452870475">"ОК"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Кіраванне прыкладаннямі"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Не хапае месца"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Немагчыма ўсталяваць прыкладанне <xliff:g id="APP_NAME">%1$s</xliff:g>. Вызваліце месца і паўтарыце спробу."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Прыкладанне не знойдзена"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Прыкладанне не знойдзена ў спісе ўсталяваных прыкладанняў."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Забаронена"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Бягучы карыстальнік не мае дазволу на гэта выдаленне."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Памылка"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Нельга выдаліць праграму."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Выдалiць прыкладанне"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Выдаліць абнаўленні"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> з\'яўляецца часткай наступнага прыкладання:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Выдаліць гэта прыкладанне?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Выдалiць гэта прыкладанне для "<b>"ўсiх"</b>" карыстальнirfў? Прыкладанне i яго дадзеныя будуць выдалены для "<b>"ўсiх"</b>" карыстальнiкаў прылады."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Хочаце выдаліць гэту праграму для карыстальніка <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Замяніць гэту праграму заводскай версіяй? Усе даныя будуць выдалены."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Замяніць гэту праграму заводскай версіяй? Усе даныя будуць выдаленыя. Гэта паўплывае на ўсіх карыстальнікаў гэтай прылады, у тым ліку карыстальнікаў з працоўнымі профілямі."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Актыўныя выдаленні"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Збоі выдалення"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Выдаленне..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> выдаляецца…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Выдаленне завершана"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Выдалена <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Няўдалае выдаленне."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Не атрымалася выдаліць <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Немагчыма выдаліць актыўную праграму адміністратара прылады"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Немагчыма выдаліць актыўную праграму адміністратара прылады для карыстальніка <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Гэта праграма патрабуецца для некаторых карыстальнікаў або профіляў і была выдалена для іншых"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Гэта праграма неабходная для вашага профілю і не можа быць выдалена."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Гэта праграма патрабуецца адміністратару вашай прылады і не можа быць выдалена."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Праграмы адміністратара для кіравання прыладамі"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Кіраванне карыстальнікамі"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Немагчыма выдалiць прыкладанне <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Памылка аналiзу пакета."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Новыя"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Усе"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Прыватнасць"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Доступ да прылады"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Гэтае абнаўленне не патрабуе ніякіх новых дазволаў."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Адмовіць"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Дадатковая iнфармацыя"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Усё роўна адмовіць"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> з <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Дазволіць &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Заўсёды дазваляць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Толькі пры актыўнай праграме"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Заўсёды"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Адхіліць і больш не пытацца"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"Адключана: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"усе адключаны"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"няма адключаных"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Дазволіць"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Праграмы"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Дазволы праграм"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Больш не пытацца"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Няма дазволаў"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Дадатковыя дазволы"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Паказаць звесткі пра праграму"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> іншы</item>
+      <item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> іншыя</item>
+      <item quantity="many"><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="old_sdk_deny_warning" msgid="3872277112584842615">"Гэта праграма была распрацавана для больш старой версіі Android. Адхіленне дазволу можа прывесці да таго, што яна не будзе працаваць належным чынам."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"выканаць невядомае дзеянне"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> з <xliff:g id="COUNT_1">%2$d</xliff:g> праграм з дазволам"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Паказаць сістэмныя"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Схаваць сістэмныя"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Няма праграм"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Налады месцазнаходжання"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> з\'яўляецца службай вызначэння месцазнаходжання для гэтай прылады. Доступ да вызначэння месцазнаходжання можна змяніць у наладах вызначэння месцазнаходжання."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Калі вы адхіліце гэты дазвол, асноўныя функцыі прылады могуць перастаць працаваць належным чынам."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Ажыццёўлена палітыкай"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Доступ у фонавым рэжыме адключаны згодна з правіламі"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Доступ у фонавым рэжыме ўключаны згодна з правіламі"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Доступ у актыўным рэжыме ўключаны згодна з правіламі"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Кантралюецца адміністратарам"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Заўсёды"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Толькі пры актыўнай праграме"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Ніколі"</string>
+    <string name="loading" msgid="7811651799620593731">"Загрузка..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Усе дазволы"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Іншыя магчымасці праграмы"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Запыт дазволу"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Выяўлены слой экрана"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Каб змяніць гэту наладу дазволу, вы павінны спачатку выключыць слой экрана з меню Налады &gt; Праграмы"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Адкрыць налады"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Дзеянні па ўсталяванні або выдаленні не падтрымліваюцца на Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Выберыце, да чаго дазволіць доступ праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Праграма &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; была абноўлена. Выберыце, да чаго ёй дазволіць доступ."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Скасаваць"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Далей"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Новыя дазволы"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Бягучыя дазволы"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Падрыхтоўка праграмы..."</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Невядома"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"У мэтах бяспекі вашаму планшэту не дазваляецца ўсталёўваць невядомыя праграмы з гэтай крыніцы."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"У мэтах бяспекі вашаму тэлевізару не дазваляецца ўсталёўваць невядомыя праграмы з гэтай крыніцы."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"У мэтах бяспекі вашаму тэлефону не дазваляецца ўсталёўваць невядомыя праграмы з гэтай крыніцы."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Ваш тэлефон і асабістыя даныя больш уразлівыя для нападаў невядомых праграм. Пры ўсталёўцы гэтай праграмы вы згаджаецеся, што несяце адказнасць за любыя пашкоджанні тэлефона ці страту даных, якія могуць адбыцца ў выніку выкарыстання гэтай праграмы."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Ваш планшэт і асабістыя даныя больш уразлівыя для нападаў невядомых праграм. Пры ўсталёўцы гэтай праграмы вы згаджаецеся, што несяце адказнасць за любыя пашкоджанні планшэта ці страту даных, якія могуць адбыцца ў выніку выкарыстання гэтай праграмы."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Ваш тэлевізар і асабістыя даныя больш уразлівыя для нападаў невядомых праграм. Пры ўсталёўцы гэтай праграмы вы згаджаецеся, што несяце адказнасць за любыя пашкоджанні тэлевізара ці страту даных, якія могуць адбыцца ў выніку выкарыстання гэтай праграмы."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Працягнуць"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Налады"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Усталяванне/выдаленне праграм wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-bg-television/strings.xml b/packages/PackageInstaller/res/values-bg-television/strings.xml
new file mode 100644
index 0000000..7429955
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bg-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Отказване, без повторно запитване"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Можете да промените това по-късно от „Настройки“ &gt; „Приложения“"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Показване на системните приложения"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Разрешения за приложението"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Разрешения за приложението"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Разрешения за <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Допълнителни разрешения"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Разрешения за <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-bg-watch/strings.xml b/packages/PackageInstaller/res/values-bg-watch/strings.xml
new file mode 100644
index 0000000..b71606b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bg-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Отказ, без повторно запитване"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Показване на системните приложения"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Без промяна"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Да"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Отказ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-bg/strings.xml b/packages/PackageInstaller/res/values-bg/strings.xml
new file mode 100644
index 0000000..30d99eb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bg/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Инсталираща програма за пакети"</string>
+    <string name="next" msgid="3057143178373252333">"Напред"</string>
+    <string name="install" msgid="5896438203900042068">"Инсталиране"</string>
+    <string name="done" msgid="3889387558374211719">"Готово"</string>
+    <string name="cancel" msgid="8360346460165114585">"Назад"</string>
+    <string name="installing" msgid="8613631001631998372">"Инсталира се..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> се инсталира…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Приложението бе инсталирано."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Искате ли да инсталирате това приложение? То ще получи достъп до:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Искате ли да инсталирате това приложение? То не изисква никакъв специален достъп."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Искате ли да инсталирате актуализация за това съществуващо приложение? Съществуващите ви данни няма да бъдат загубени. Актуализираното приложение ще получи достъп до:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Искате ли да инсталирате актуализация за това вградено приложение? Съществуващите ви данни няма да бъдат загубени. Актуализираното приложение ще получи достъп до:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Искате ли да инсталирате актуализация за това съществуващо приложение? Съществуващите ви данни няма да бъдат загубени. Не се изисква специален достъп."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Искате ли да инсталирате актуализация за това вградено приложение? Съществуващите ви данни няма да бъдат загубени. Не се изисква специален достъп."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Приложението не бе инсталирано."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Инсталирането на пакета бе блокирано."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Приложението не бе инсталирано, тъй като пакетът е в конфликт със съществуващ пакет."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Приложението не бе инсталирано, тъй като не е съвместимо с таблета ви."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Това приложение не е съвместимо с телевизора ви."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Приложението не бе инсталирано, тъй като не е съвместимо с телефона ви."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Приложението не бе инсталирано, тъй като изглежда, че пакетът е невалиден."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> не можа да се инсталира на таблета ви."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> не можа да се инсталира на телевизора ви."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> не можа да се инсталира на телефона ви."</string>
+    <string name="launch" msgid="4826921505917605463">"Отваряне"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Администраторът ви не разрешава инсталирането на приложения, получени от неизвестни източници"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Този потребител не може да инсталира неизвестни приложения"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Този потребител няма разрешение да инсталира приложения"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Управление на приложенията"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Няма място"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> не можа да се инсталира. Освободете място и опитайте отново."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Приложението не бе намерено"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Приложението не бе намерено в списъка с инсталирани приложения."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Няма разрешение"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Текущият потребител няма разрешение да извърши това деинсталиране."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Грешка"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Приложението не можа да бъде деинсталирано."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Деинсталиране на приложението"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Деинсталиране на актуализацията"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> е част от следното приложение:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Искате ли да деинсталирате това приложение?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Искате ли да деинсталирате това приложение за "<b>"всички"</b>" потребители? Приложението и данните му ще бъдат премахнати от "<b>"всички"</b>" потребители на устройството."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Искате ли да деинсталирате това приложение за потребителя <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Това приложение да се замени ли с фабричната версия? Всички данни ще бъдат премахнати."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Това приложение да се замени ли с фабричната версия? Всички данни ще бъдат премахнати. Промяната ще засегне всеки потребител на устройството, включително тези със служебни потребителски профили."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Активни деинсталирания"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Неуспешни деинсталирания"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Деинсталира се..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> се деинсталира…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Деинсталирането завърши."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Деинсталирахте <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Деинсталирането не бе успешно."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Деинсталирането на <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> бе неуспешно."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Активното приложение за администриране на устройството не може да се деинсталира"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Активното приложение за администриране на устройството не може да се деинсталира за <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Това приложение е необходимо за някои потребители или потребителски профили и бе деинсталирано за други."</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Това приложение е необходимо за потребителския ви профил и не може да се деинсталира."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Приложението се изисква от администратора на у-вото и не може да се деинсталира."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Управление на прилож. за администриране на у-вото"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Управление на потребителите"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> не можа да се деинсталира."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"При синтактичния анализ на пакета възникна проблем."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Нови"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Всички"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Поверителност"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Достъп до у-вото"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Тази актуализация не изисква нови разрешения."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Отказване"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Още информация"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Отказване въпреки това"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> от <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Разрешаване на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Винаги ли да се разрешава на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Само при използване на приложението"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Винаги"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Отказване, без повторно запитване"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"Деактивирахте <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"всички са деактивирани"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"няма деактивирани"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Разрешаване"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Приложения"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Разрешения за приложения"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Без повторно питане"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Няма разрешения"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Допълнителни разрешения"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Отваряне на информацията за приложението"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">Още <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Още <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Това приложение е създадено за по-стара версия на Android. То може да спре да функционира нормално при отказване на разрешението."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"извършване на неизвестно действие"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> от <xliff:g id="COUNT_1">%2$d</xliff:g> приложения имат разрешение"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Системни приложения"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Скриване на системните"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Няма приложения"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Настройки за местоположението"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> е доставчик на услуги за местоположението за това устройство. Достъпът до местоположението може да бъде променен от съответните настройки."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Ако откажете това разрешение, основни функции на устройството ви може да спрат да работят както трябва."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Наложено чрез правило"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Достъпът на заден план е деактивиран от правилата"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Достъпът на заден план е активиран от правилата"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Достъпът на преден план е активиран от правилата"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Контролира се от администратор"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Винаги"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Само при използване на прилож."</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Никога"</string>
+    <string name="loading" msgid="7811651799620593731">"Зарежда се…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Всички разрешения"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Други възможности на приложението"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Заявка за разрешение"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Открито е екранно наслагване"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"За да промените настройката за това разрешение, трябва първо да изключите екранното наслагване от „Настройки“ &gt; „Приложения“"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Отваряне на настройките"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Действията инсталиране и деинсталиране не се поддържат на устройства с Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Изберете до какво да има достъп &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Приложението &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; е актуализирано. Изберете до какво да има достъп."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Отказ"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Напред"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Нови разрешения"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Текущи разрешения"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Приложението се подготвя…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Неизвестно"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"От съображения за сигурност на таблета ви не могат да се инсталират неизвестни приложения от този източник."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"От съображения за сигурност на телевизора ви не могат да се инсталират неизвестни приложения от този източник."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"От съображения за сигурност на телефона ви не могат да се инсталират неизвестни приложения от този източник."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Телефонът и личните ви данни са по-уязвими към атаки от неизвестни приложения. С инсталирането на това приложение приемате, че носите отговорност при евентуална повреда на телефона или загуба на информация вследствие на използването на приложението."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Таблетът и личните ви данни са по-уязвими към атаки от неизвестни приложения. С инсталирането на това приложение приемате, че носите отговорност при евентуална повреда на таблета или загуба на информация вследствие на използването на приложението."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Телевизорът и личните ви данни са по-уязвими към атаки от неизвестни приложения. С инсталирането на това приложение приемате, че носите отговорност при евентуална повреда на телевизора или загуба на информация вследствие на използването на приложението."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Напред"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Настройки"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Инсталир./деинсталир. на прилож. за Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-bn-television/strings.xml b/packages/PackageInstaller/res/values-bn-television/strings.xml
new file mode 100644
index 0000000..a83d7b8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bn-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"অস্বীকার করুন এবং আবার জিজ্ঞাসা করবেন না"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"আপনি সেটিংস &gt; অ্যাপ্লিকেশান এ এটি পরে পরিবর্তন করতে পারেন"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"সিস্টেম অ্যাপ্লিকেশানগুলি দেখান"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"অ্যাপ্লিকেশনের অনুমতি"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"অ্যাপ্লিকেশনের অনুমতি"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> অনুমতিগুলি"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"অতিরিক্ত অনুমতিগুলি"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> অনুমতিগুলি"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-bn-watch/strings.xml b/packages/PackageInstaller/res/values-bn-watch/strings.xml
new file mode 100644
index 0000000..79a91f4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bn-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"অস্বীকার করুন, আবার জিজ্ঞাসা করবেন না"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"সিস্টেম অ্যাপ্লিকেশানগুলি দেখান"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"পরিবর্তন করা যাবে না"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"হ্যাঁ"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"বাতিল করুন"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-bn/strings.xml b/packages/PackageInstaller/res/values-bn/strings.xml
new file mode 100644
index 0000000..c66f5bb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bn/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"প্যাকেজ ইনস্টলার"</string>
+    <string name="next" msgid="3057143178373252333">"পরবর্তী"</string>
+    <string name="install" msgid="5896438203900042068">"ইনস্টল করুন"</string>
+    <string name="done" msgid="3889387558374211719">"সম্পন্ন হয়েছে"</string>
+    <string name="cancel" msgid="8360346460165114585">"বাতিল করুন"</string>
+    <string name="installing" msgid="8613631001631998372">"ইনস্টল করা হচ্ছে…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ইন্সটল করা হচ্ছে…"</string>
+    <string name="install_done" msgid="3682715442154357097">"অ্যাপ্লিকেশান ইনস্টল করা হয়েছে৷"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"আপনি কি এই অ্যাপ্লিকেশানটি ইনস্টল করতে চান? এর মাধ্যমে যেসব জিনিস অ্যাক্সেস করার সুবিধা পাবেন সেগুলি হল:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"আপনি কি এই অ্যাপ্লিকেশানটি ইনস্টল করতে চান? এর জন্য কোনো বিশেষ অ্যাক্সেসের প্রয়োজন নেই৷"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"আপনি কি এই বিদ্যমান অ্যাপ্লিকেশানের একটি আপডেট ইনস্টল করতে চান? আপনার বিদ্যমান ডেটাগুলি একই রকম থাকবে৷ এই আপডেট হওয়া অ্যাপ্লিকেশানটির মাধ্যমে যেসব জিনিস অ্যাক্সেস করার সুবিধা পাবেন সেগুলি হল:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"আপনি কি এই ভেতরে থাকা অ্যাপ্লিকেশানের একটি আপডেট ইনস্টল করতে চান? আপনার বিদ্যমান ডেটাগুলি একই রকম থাকবে৷ এই আপডেট হওয়া অ্যাপ্লিকেশানটির মাধ্যমে যেসব জিনিস অ্যাক্সেস করার সুবিধা পাবেন সেগুলি হল:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"আপনি কি এই বিদ্যমান অ্যাপ্লিকেশানের একটি আপডেট ইনস্টল করতে চান? আপনার বিদ্যমান ডেটাগুলি একই রকম থাকবে৷ এর জন্য কোনো বিশেষ অ্যাক্সেসের প্রয়োজন নেই৷"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"আপনি কি ভেতরে থাকা অ্যাপ্লিকেশানের একটি আপডেট ইনস্টল করতে চান? আপনার বিদ্যমান ডেটাগুলি একই রকম থাকবে৷ এর জন্য কোনো বিশেষ অ্যাক্সেসের প্রয়োজন নেই৷"</string>
+    <string name="install_failed" msgid="6579998651498970899">"অ্যাপ্লিকেশান ইনস্টল করা হয়নি৷"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"ইনস্টল হওয়া থেকে প্যাকেজটিকে অবরুদ্ধ করা হয়েছে।"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"কোনো বিদ্যমান প্যাকেজের সাথে এই প্যাকেজটির বিবাদ থাকার ফলে অ্যাপ ইনস্টল করা হয়নি৷"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"অ্যাপটি আপনার ট্যাবলেটের জন্য উপযুক্ত না হওয়ার কারণে এটি ইনস্টল করা হয়নি৷"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"এই অ্যাপ্লিকেশানটি আপনার টিভির জন্য উপযুক্ত নয়৷"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"অ্যাপটি আপনার ফোনের জন্য উপযুক্ত না হওয়ার কারণে এটি ইনস্টল করা হয়নি৷"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"প্যাকেজটি অবৈধ বলে মনে হওয়ার কারণে অ্যাপ ইনস্টল করা হয়নি৷"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> আপনার ট্যাবলেটে ইনস্টল করা যায়নি৷"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g>-কে আপনার টিভিতে ইনস্টল করা যাবে না৷"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> আপনার ফোনে ইনস্টল করা যায়নি৷"</string>
+    <string name="launch" msgid="4826921505917605463">"খুলুন"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"আপনার প্রশাসক অজানা উৎস থেকে প্রাপ্ত অ্যাপ ইনস্টল করার অনুমতি দেয় না"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"এই ব্যবহারকারী অজানা অ্যাপ ইনস্টল করতে পারবেন না"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"এই ব্যবহারকারী অ্যাপ ইনস্টল করার অনুমতি পাননি"</string>
+    <string name="ok" msgid="3468756155452870475">"ঠিক আছে"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"অ্যাপ্লিকেশানগুলির পরিচালনা করুন"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"পর্যাপ্ত জায়গা খালি নেই"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> ইনস্টল করা যায়নি৷ কিছু পরিমাণ জায়গা খালি করে আবার চেষ্টা করুন৷"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"অ্যাপ্লিকেশান পাওয়া যায়নি"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"অ্যাপ্লিকেশানটিকে ইনস্টল করা অ্যাপ্লিকেশানের তালিকাতে পাওয়া যায়নি৷"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"অনুমোদিত নয়"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"বর্তমান ব্যবহারকারী এই আনইনস্টলের কাজটি করার জন্য অনুমোদিত নয়৷"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"ত্রুটি"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"অ্যাপ আনইনস্টল করা গেল না৷"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"অ্যাপ্লিকেশানটিকে আনইনস্টল করুন"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"আপডেট আনইনস্টল করুন"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> হল নিম্নলিখিত অ্যাপ্লিকেশানগুলির অংশ বিশেষ:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"আপনি কি এই অ্যাপ্লিকেশানটিকে আনইনস্টল করতে চান?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"আপনি কি "<b>"সমস্ত"</b>" ব্যবহারকারীর জন্য এই অ্যাপ্লিকেশানটিকে আনইনস্টল করতে চান? এই ডিভাইসের "<b>"সমস্ত"</b>" ব্যবহারকারীর কাছ থেকে অ্যাপ্লিকেশানটি ও এর ডেটা হারিয়ে যাবে৷"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"আপনি কি ব্যবহারকারী <xliff:g id="USERNAME">%1$s</xliff:g> এর জন্য এই অ্যাপ্লিকেশানটি আনইনস্টল করতে চান?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"ফ্যাক্টরি সংস্করণের সাথে এই অ্যাপটিকে বদলাবেন? সব ডেটা মুছে যাবে।"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"ফ্যাক্টরি সংস্করণের সাথে এই অ্যাপটিকে বদলাবেন? সমস্ত ডেটা মুছে যাবে। এটি এই ডিভাইসের সমস্ত ব্যবহারকারী সহ তাদের কার্যের প্রোফাইলের উপরেও প্রভাব ফেলবে।"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"এগুলি আনইনস্টল করা হচ্ছে"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"এগুলি আনইনস্টল করা যায়নি"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"আনইনস্টল করা হচ্ছে ..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> আনইনস্টল করা হচ্ছে…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"আনইনস্টল সমাপ্ত হয়েছে৷"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> আনইনস্টল করা হয়েছে"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"আনইনস্টল সফল হয়নি৷"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> আনইনস্টল করা গেল না৷"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"সক্রিয় থাকা ডিভাইস প্রশাসক অ্যাপটি আনইনস্টল করা যাবে না"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> এর সক্রিয় থাকা ডিভাইস প্রশাসক অ্যাপটি আনইনস্টল করা যাবে না"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"কিছু ব্যবহারকারী বা প্রোফাইলের জন্য এই অ্যাপ্লিকেশানটি আবশ্যক এবং অন্যদের জন্য আনইনস্টল করা হবে"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"আপনার প্রোফাইলের জন্য এই অ্যাপ্লিকেশানটি প্রয়োজন এবং এটিকে আনইনস্টল করা যাবে না৷"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"আপনার ডিভাইস প্রশাসকের চাহিদা অনুযায়ী এই অ্যাপ্লিকেশানটি আবশ্যক এবং এটি আনইনস্টল করা যাবে না।"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"ডিভাইস প্রশাসক অ্যাপগুলি পরিচালনা করুন"</string>
+    <string name="manage_users" msgid="3125018886835668847">"ব্যবহারকারীদের পরিচালনা করুন"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> আনইনস্টল করা যায়নি৷"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"প্যাকেজটি বিশ্লেষণ করার ক্ষেত্রে একটি সমস্যা হয়েছে৷"</string>
+    <string name="newPerms" msgid="6039428254474104210">"নতুন"</string>
+    <string name="allPerms" msgid="1024385515840703981">"সমস্ত"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"গোপনীয়তা"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"ডিভাইসের অ্যাক্সেস"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"এই আপডেটের জন্য কোনো নতুন অনুমতির প্রয়োজন নেই৷"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"প্রত্যাখ্যান করুন"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"আরও তথ্য"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"যাইহোক অস্বীকার করুন"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> এর <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;কে <xliff:g id="ACTION">%2$s</xliff:g> এর অনুমতি দেবেন?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"<xliff:g id="ACTION">%2$s</xliff:g>-এ সবসময় &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অনুমতি দেবেন?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"অ্যাপটি ব্যবহার করার সময়"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"সবসময়"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"অস্বীকার করুন এবং আবার জিজ্ঞাসা করবেন না"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g>টি অক্ষম করা হয়েছে"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"সমস্ত অক্ষম করা হয়েছে"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"কোনো কিছুই অক্ষম করা হয়নি"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"অনুমতি দিন"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"অ্যাপ"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"অ্যাপ্লিকেশনের অনুমতি"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"আর জিজ্ঞাসা করবেন না"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"কোনো অনুমতি নেই"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"অতিরিক্ত অনুমতিগুলি"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"অ্যাপের তথ্য দেখুন"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <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="old_sdk_deny_warning" msgid="3872277112584842615">"একটি পুরোনো সংস্করণের Android এর জন্য এই অ্যাপ্লিকেশানটি ডিজাইন করা হয়েছিল৷ অনুমতি অস্বীকার করলে এটিকে যে কাজের উদ্দেশ্যে তৈরি করা হয়েছিল সেটি নাও করতে পারে৷"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"কোনো অজানা কার্য সঞ্চালন করুন"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g>টির মধ্যে <xliff:g id="COUNT_0">%1$d</xliff:g>টি অ্যাপ্লিকেশান মঞ্জুরিপ্রাপ্ত"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"সিস্টেম দেখুন"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"সিস্টেম লুকান"</string>
+    <string name="no_apps" msgid="1965493419005012569">"কোনো অ্যাপ্লিকেশান নেই"</string>
+    <string name="location_settings" msgid="1774875730854491297">"লোকেশন সেটিংস"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> এই ডিভাইসের জন্য একটি লোকেশন পরিষেবাগুলি প্রদান করে। লোকেশন সেটিংস থেকে লোকেশনের অ্যাক্সেস পরিবর্তন করা যায়।"</string>
+    <string name="system_warning" msgid="7103819124542305179">"আপনি যদি এই অনুমতিটি অস্বীকার করেন, তবে আপনার ডিভাইসের প্রাথমিক বৈশিষ্ট্যগুলিকে যে কাজের উদ্দেশ্যে তৈরি করা হয়েছিল সেগুলি নাও করতে পারে৷"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"নীতি দ্বারা প্রয়োগ করা হয়েছে"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"নীতির কারণে ব্যাকগ্রাউন্ড অ্যাক্সেস বন্ধ করা আছে"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"নীতির কারণে ব্যাকগ্রাউন্ড অ্যাক্সেস চালু করা আছে"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"নীতির কারণে খুলে রাখা অ্যাপের অ্যাক্সেস চালু করা আছে"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"অ্যাডমিনের দ্বারা নিয়ন্ত্রিত"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"সবসময়"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"অ্যাপটি ব্যবহার করার সময়"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"কখনও না"</string>
+    <string name="loading" msgid="7811651799620593731">"লোড হচ্ছে..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"সমস্ত অনুমতি"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"অন্যান্য অ্যাপ্লিকেশান ক্ষমতা"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"অনুমতির অনুরোধ"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"স্ক্রিন আচ্ছাদন শনাক্ত করা হয়েছে"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"এই অনুমতি সেটিংস পরিবর্তন করতে, আপনাকে প্রথমে সেটিংস &gt; এ গিয়ে অ্যাপ্লিকেশানগুলি থেকে স্ক্রিন ওভারলে বন্ধ করতে হবে"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"সেটিংস খুলুন"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"ওয়েরে ইনস্টল/আনইনস্টল করার কাজগুলি সমর্থিত নয়।"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; কে কোন জিনিসগুলিতে অ্যাক্সেস দেবেন তা বেছে নিন"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; আপডেট করা হয়েছে৷ এই অ্যাপ্লিকেশানটিকে কোন জিনিসগুলিতে অ্যাক্সেস দেবেন তা চয়ন করুন৷"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"বাতিল করুন"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"চালিয়ে যান"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"নতুন অনুমতিগুলি"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"বর্তমান অনুমতিগুলি"</string>
+    <string name="message_staging" msgid="6151794817691100003">"অ্যাপ্লিকেশান স্টেজ করা হচ্ছে..."</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"অজানা"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"আপনার নিরাপত্তার জন্য আপনার ট্যাবলেট কে এই উৎস থেকে আসা অজানা অ্যাপ ইনস্টল করার অনুমতি দেওয়া হয় না।"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"আপনার নিরাপত্তার জন্য আপনার TV কে এই উৎস থেকে আসা অজানা অ্যাপ ইনস্টল করার অনুমতি দেওয়া হয় না।"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"আপনার নিরাপত্তার জন্য আপনার ফোন কে এই উৎস থেকে আসা অজানা অ্যাপ ইনস্টল করার অনুমতি দেওয়া হয় না।"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"অজানা অ্যাপের দ্বারা আপনার ফোন এবং ব্যক্তিগত ডেটা আক্রান্ত হওয়ার সম্ভাবনা বেশি থাকে। এই অ্যাপটি ইনস্টল করার মাধ্যমে আপনি সম্মত হলেন যে এটি ব্যবহারের ফলে আপনার ফোনের বা ডেটার কোনও ক্ষতি হলে তার জন্য আপনিই দায়ী থাকবেন।"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"অজানা অ্যাপের দ্বারা আপনার ট্যাবলেট এবং ব্যক্তিগত ডেটা আক্রান্ত হওয়ার সম্ভাবনা বেশি থাকে। এই অ্যাপটি ইনস্টল করার মাধ্যমে আপনি সম্মত হলেন যে এটি ব্যবহারের ফলে আপনার ট্যাবলেটের বা ডেটার কোনও ক্ষতি হলে তার জন্য আপনিই দায়ী থাকবেন।"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"অজানা অ্যাপের দ্বারা আপনার টিভি এবং ব্যক্তিগত ডেটা আক্রান্ত হওয়ার সম্ভাবনা বেশি থাকে। এই অ্যাপটি ইনস্টল করার মাধ্যমে আপনি সম্মত হলেন যে এটি ব্যবহারের ফলে আপনার টিভি বা ডেটার কোনও ক্ষতি হলে তার জন্য আপনিই দায়ী থাকবেন।"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"চালিয়ে যান"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"সেটিংস"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"ওয়্যার অ্যাপ ইনস্টল/আনইনস্টল করা হচ্ছে"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-bs-television/strings.xml b/packages/PackageInstaller/res/values-bs-television/strings.xml
new file mode 100644
index 0000000..564f2a6
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bs-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Odbij i ne pitaj ponovo"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Ovo možete kasnije promijeniti u odjeljku Postavke &gt; Aplikacije"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Prikaži sistemske aplikacije"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Odobrenja za aplikacije"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Odobrenja za aplikacije"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Odobrenja za: <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Dodatna odobrenja"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Odobrenja za: <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-bs-watch/strings.xml b/packages/PackageInstaller/res/values-bs-watch/strings.xml
new file mode 100644
index 0000000..dcae097
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bs-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Odbij i ne pitaj ponovo"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Prikaži sistemske aplikacije"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Ne mijenja se"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Da"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Otkaži"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-bs/strings.xml b/packages/PackageInstaller/res/values-bs/strings.xml
new file mode 100644
index 0000000..3f2c5c3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bs/strings.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Alat za instaliranje paketa"</string>
+    <string name="next" msgid="3057143178373252333">"Naprijed"</string>
+    <string name="install" msgid="5896438203900042068">"Instaliraj"</string>
+    <string name="done" msgid="3889387558374211719">"Gotovo"</string>
+    <string name="cancel" msgid="8360346460165114585">"Otkaži"</string>
+    <string name="installing" msgid="8613631001631998372">"Instalacija u toku..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Instaliranje <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplikacija je instalirana."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Želite li instalirati ovu aplikaciju? Ona će dobiti pristup:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Želite li instalirati ovu aplikaciju? Ona ne zahtijeva poseban pristup."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Želite li ažurirati ovu postojeću aplikaciju? Vaši postojeći podaci neće biti izgubljeni. Ažurirana aplikacija će dobiti pristup:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Želite li ažurirati ovu ugrađenu aplikaciju? Vaši postojeći podaci neće biti izgubljeni. Ažurirana aplikacija će dobiti pristup:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Želite li ažurirati ovu postojeću aplikaciju? Vaši postojeći podaci neće biti izgubljeni. Za ovo nije potreban poseban pristup."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Želite li ažurirati ovu ugrađenu aplikaciju? Vaš postojeći podaci neće biti izgubljeni. Nije potreban poseban pristup."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Aplikacija nije instalirana."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Instaliranje ovog paketa je blokirano."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Aplikacija nije instalirana jer paket nije usaglašen s postojećim paketom."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Aplikacija nije instalirana jer nije kompatibilna s vašim tabletom."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Ova aplikacija nije kompatibilna s vašim TV-om."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Aplikacija nije instalirana jer nije kompatibilna s vašim telefonom."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Aplikacija nije instalirana jer izgleda da paket nije važeći."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> ne možete instalirati na svoj tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Nije moguće instalirati aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> na vaš TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> ne možete instalirati na svoj telefon."</string>
+    <string name="launch" msgid="4826921505917605463">"Otvori"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Vaš administrator ne dozvoljava instaliranje aplikacija iz nepoznatih izvora."</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Ovaj korisnik ne može instalirati nepoznate aplikacije."</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Ovom korisniku nije dozvoljeno instaliranje aplikacija"</string>
+    <string name="ok" msgid="3468756155452870475">"Uredu"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Upravljaj aplikacijama"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Nedostatak prostora"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Ne možete instalirati aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>. Oslobodite prostora u pohrani i pokušajte ponovo."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Aplikacija nije pronađena"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Aplikacija nije pronađena na spisku instaliranih aplikacija."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Nije dozvoljeno"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Trenutnom korisniku nije dozvoljeno da izvrši ovu deinstalaciju."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Greška"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Nije bilo moguće deinstalirati aplikaciju."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Uklanjanje aplikacije"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Uklanjanje ažuriranja"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je dio sljedeće aplikacije:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Želite li ukloniti ovu aplikaciju?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Želite li ukloniti ovu aplikaciju za "<b>" sve "</b>" korisnike? Aplikacija i njeni podaci će biti uklonjeni iz "<b>" svih "</b>" korisničkih računa na uređaju."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Želite li ukloniti ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Želite li ovu aplikaciju zamijeniti s fabričkom verzijom? Svi podaci će biti uklonjeni."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Želite li ovu aplikaciju zamijeniti s fabričkom verzijom? Svi podaci će biti uklonjeni. To će utjecati na sve korisnike uređaja, uključujući i one s radnim profilima."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Tekuća deinstaliranja"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Neuspjela deinstaliranja"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Uklanjanje u toku..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Deinstalacija paketa <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Uklanjanje završeno."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Deinstaliran je paket <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Uklanjanje nije uspjelo."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Paket <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> nije uspješno deinstaliran."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Nije moguće deinstalirati aktivnu aplikaciju administratora uređaja"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Nije moguće deinstalirati aktivnu aplikaciju administratora uređaja za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Ova aplikacija je neophodna nekim korisnicima ili profilima, a kod ostalih je deinstalirana"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Ova aplikacija je potrebna za vaš profil i ne može se deinstalirati."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Ova aplikacija je potrebna administratoru vašeg uređaja i ne može se ukloniti."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Upravljajte aplikacijama administratora uređaja"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Upravljanje korisnicima"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> se ne može ukloniti."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Došlo je do problema prilikom raščlanjivanja paketa."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Novo"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Sve"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privatnost"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Pristup uređaju"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Za ovo ažuriranje nisu potrebne nova odobrenja."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Odbij"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Više informacija"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Odbij svakako"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> od <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Uvijek dozvoliti da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Samo dok se koristi aplikacija"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Uvijek"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Odbij i ne pitaj ponovo"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"omogućeno je <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"sve je onemogućeno"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ništa nije onemogućeno"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Dozvoli"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplikacije"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Odobrenja za aplikacije"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Ne pitaj ponovo"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Nema odobrenja"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Dodatna odobrenja"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Otvori informacije o aplikaciji"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one">još <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="few">još <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="other">još <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Ova aplikacija je kreirana za stariju verziju Androida. Odbijanje odobrenja može uzrokovati da ona više ne funkcionira onako kako je primarno zamišljeno."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"izvrši nepoznatu radnju"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Aplikacije sa odobrenjem: <xliff:g id="COUNT_0">%1$d</xliff:g> od <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Prikaži sistemske aplikacije"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Sakrij sistemske"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Nijedna aplikacija"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Postavke lokacije"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> pruža usluge lokacije za ovaj uređaj. Pristup lokaciji se može izmijeniti u postavkama lokacije."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Ukoliko odbijete ovo odobrenje, osnovne funkcije vašeg uređaja možda više neće funkcionirati onako kako je prvobitno zamišljeno."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Nametnuto je pravilima"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Pristup pozadini je onemogućen u skladu s pravilima"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Pristup pozadini je omogućen u skladu s pravilima"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Pristup u prvom planu je omogućen u skladu s pravilima"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Kontrolira administrator"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Uvijek"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Samo dok se koristi aplikacija"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nikada"</string>
+    <string name="loading" msgid="7811651799620593731">"Učitava se…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Sva odobrenja"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Ostale mogućnosti aplikacije"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Zahtjev za odobrenjem"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Otkriven je element koji prekriva sadržaj ekrana"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Da promijenite postavku ovog odobrenja, prvo morate isključiti element koji prekriva sadržaj ekrana u odjeljku Postavke &gt; Aplikacije"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Otvori postavke"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Instaliranje/deinstaliranje nije podržano na Wearu."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Odaberite čemu aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&amp;Lt;/b&gt; može pristupiti"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Aplikacija &amp;Lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&amp;Lt;/b&gt; je ažurirana. Odaberite čemu ova aplikacija može pristupiti."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Otkaži"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Nastavi"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nova odobrenja"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Postojeća odobrenja"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Aplikacija se postavlja…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Nepoznato"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Vašem tabletu iz sigurnosnih razloga nije dopušteno instaliranje nepoznatih aplikacija iz ovog izvora."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Vašem TV-u iz sigurnosnih razloga nije dopušteno instaliranje nepoznatih aplikacija iz ovog izvora."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Vašem telefonu iz sigurnosnih razloga nije dopušteno instaliranje nepoznatih aplikacija iz ovog izvora."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Vaši podaci na telefonu i vaši lični podaci izloženiji su napadima nepoznatih aplikacija. Instaliranjem ove aplikacije, saglasni ste da ste vi odgovorni za bilo kakvu štetu na telefonu ili gubitak podataka do kojih može doći korištenjem aplikacije."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Vaši podaci na tabletu i vaši lični podaci izloženiji su napadima nepoznatih aplikacija. Instaliranjem ove aplikacije, saglasni ste da ste vi odgovorni za bilo kakvu štetu na tabletu ili gubitak podataka do kojih može doći korištenjem aplikacije."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Vaši podaci na TV-u i vaši lični podaci izloženiji su napadima nepoznatih aplikacija. Instaliranjem ove aplikacije, saglasni ste da ste vi odgovorni za bilo kakvu štetu na TV-u ili gubitak podataka do kojih može doći korištenjem aplikacije."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Nastavi"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Postavke"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"(De)instaliranje wear aplikacija"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ca-television/strings.xml b/packages/PackageInstaller/res/values-ca-television/strings.xml
new file mode 100644
index 0000000..7126c41
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ca-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Denega i no m\'ho tornis a preguntar"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Pots canviar-ho més endavant a Configuració &gt; Aplicacions"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Mostra les aplicacions del sistema"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Permisos d\'aplicacions"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Permisos d\'aplicacions"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Permisos: <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Permisos addicionals"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Permisos: <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ca-watch/strings.xml b/packages/PackageInstaller/res/values-ca-watch/strings.xml
new file mode 100644
index 0000000..d289b56
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ca-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Denega i no m\'ho demanis més"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Mostra les aplicacions del sistema"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"No es pot canviar"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Sí"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Cancel·la"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ca/strings.xml b/packages/PackageInstaller/res/values-ca/strings.xml
new file mode 100644
index 0000000..62d758c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ca/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Instal·lador de paquets"</string>
+    <string name="next" msgid="3057143178373252333">"Següent"</string>
+    <string name="install" msgid="5896438203900042068">"Instal·la"</string>
+    <string name="done" msgid="3889387558374211719">"Fet"</string>
+    <string name="cancel" msgid="8360346460165114585">"Cancel·la"</string>
+    <string name="installing" msgid="8613631001631998372">"S\'està instal·lant..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"S\'està instal·lant <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplicació instal·lada."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Vols instal·lar aquesta aplicació? Tindrà els permisos següents:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Vols instal·lar aquesta aplicació? No requereix cap accés especial."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Vols instal·lar una actualització per a aquesta aplicació? No es perdran les teves dades existents. L\'aplicació actualitzada tindrà els permisos següents:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Vols instal·lar una actualització d\'aquesta aplicació integrada? No es perdran les teves dades. L\'aplicació actualitzada tindrà els permisos següents:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Vols instal·lar una actualització a aquesta aplicació existent? Les dades existents no es perdran. No cal cap tipus d\'accés especial."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Vols instal·lar una actualització a aquesta aplicació integrada? Les teves dades existents no es perdran. No cal cap tipus d\'accés especial."</string>
+    <string name="install_failed" msgid="6579998651498970899">"L\'aplicació no s\'ha instal·lat."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"El paquet s\'ha bloquejat perquè no es pugui instal·lar."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"L\'aplicació no s\'ha instal·lat perquè el paquet entra en conflicte amb un d\'existent."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"L\'aplicació no s\'ha instal·lat perquè no és compatible amb la teva tauleta."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Aquesta aplicació no és compatible amb el teu televisor."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"L\'aplicació no s\'ha instal·lat perquè no és compatible amb el teu telèfon."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"L\'aplicació no s\'ha instal·lat perquè sembla que el paquet no és vàlid."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> no s\'ha pogut instal·lar a la tauleta."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> no s\'ha pogut instal·lar al televisor."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> no s\'ha pogut instal·lar al telèfon."</string>
+    <string name="launch" msgid="4826921505917605463">"Obre"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"L\'administrador no permet instal·lar aplicacions de fonts desconegudes"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Aquest usuari no pot instal·lar aplicacions desconegudes"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Aquest usuari no té permís per instal·lar aplicacions"</string>
+    <string name="ok" msgid="3468756155452870475">"D\'acord"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Gestiona les aplicacions"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Espai esgotat"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"No s\'ha pogut instal·lar <xliff:g id="APP_NAME">%1$s</xliff:g>. Allibera espai i torna-ho a provar."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"No s\'ha trobat l\'aplicació"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"No s\'ha trobat l\'aplicació a la llista d\'aplicacions instal·lades."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Sense autorització"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"L\'usuari actual no té permís per dur a terme aquesta desinstal·lació."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Error"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"No s\'ha pogut desinstal·lar l\'aplicació."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Desinstal·la l\'aplicació"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Desinstal·la l\'actualització"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> forma part de l\'aplicació següent:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Vols desinstal·lar aquesta aplicació?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Vols desinstal·lar aquesta aplicació per a "<b>"tots"</b>" els usuaris? L\'aplicació i les seves dades se suprimiran per a "<b>"tots"</b>" els usuaris del dispositiu."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Vols desinstal·lar aquesta aplicació per a l\'usuari <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Si substitueixes aquesta aplicació per la versió de fàbrica, s\'esborraran totes les dades."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Si substitueixes aquesta aplicació per la versió de fàbrica, s\'esborraran totes les dades. Això afectarà tots els usuaris d\'aquest dispositiu, inclosos els que tinguin un perfil professional."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Desinstal·lacions en curs"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Desinstal·lacions fallides"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"S\'està desinstal·lant..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"S\'està desinstal·lant <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Desinstal·lació finalitzada."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"S\'ha desinstal·lat <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"S\'ha produït un error en la desinstal·lació."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"No s\'ha pogut desinstal·lar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> correctament."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"No es pot desinstal·lar l\'aplicació activa de l\'administrador del dispositiu"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"No es pot desinstal·lar l\'aplicació activa de l\'administrador del dispositiu per a <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"L\'aplicació cal en alguns usuaris o perfils i s\'ha desinstal·lat per a d\'altres"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Aquesta aplicació es necessita per al teu perfil i no es pot desinstal·lar."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"L\'administrador del dispositiu necessita l\'aplicació i no la pots desinstal·lar."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Gestiona aplicacions d\'administració del dispositiu"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Gestiona els usuaris"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> no s\'ha pogut desinstal·lar."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"S\'ha produït un problema en analitzar el paquet."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nous"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Tots"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privadesa"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Accés al dispositiu"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Aquesta actualització no requereix permisos nous."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Denega"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Més informació"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Denega de totes maneres"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Vols permetre a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Només mentre s\'utilitzi l\'aplicació"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Sempre"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Denega i no m\'ho tornis a preguntar"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> permisos desactivats"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"tots els permisos desactivats"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"cap permís desactivat"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Permet"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplicacions"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Permisos d\'aplicacions"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"No m\'ho tornis a preguntar"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Sense permisos"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Més permisos"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Obre la informació de l\'aplicació"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> més</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> més</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Aquesta aplicació es va dissenyar per a una versió anterior d\'Android. És possible que no funcioni com està previst si li denegues el permís."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"dur a terme una acció desconeguda"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> de <xliff:g id="COUNT_1">%2$d</xliff:g> aplicacions permeses"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Mostra aplicacions del sistema"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Amaga aplicacions del sistema"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Cap aplicació"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Configuració d\'ubicació"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> és un proveïdor de serveis d\'ubicació per a aquest dispositiu. L\'accés a la ubicació es pot modificar des de la configuració d\'ubicació."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Si rebutges aquest permís, és possible que funcions bàsiques del dispositiu deixin de funcionar correctament."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Aplicat en funció de la política"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"S\'ha desactivat l\'accés en segon pla per la política"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"S\'ha activat l\'accés en segon pla per la política"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"S\'ha activat l\'accés en primer pla per la política"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Controlat per l\'administrador"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Sempre"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Només mentre s\'utilitzi l\'app"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Mai"</string>
+    <string name="loading" msgid="7811651799620593731">"S\'està carregant..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Tots els permisos"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Altres competències de l\'aplicació"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Sol·licitud de permís"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"S\'ha detectat una superposició de pantalla"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Per canviar la configuració de permisos, cal que desactivis la superposició de pantalla des de Configuració &gt; Aplicacions"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Obre Configuració"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Les accions d\'instal·lar o de desinstal·lar no s\'admeten a Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Tria a què vols que tingui accés &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"S\'ha actualitzat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. Tria a què vols que tingui accés aquesta aplicació."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Cancel·la"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continua"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Permisos nous"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Permisos actuals"</string>
+    <string name="message_staging" msgid="6151794817691100003">"S\'està preparant la instal·lació de l\'aplicació…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Desconegut"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Per seguretat, la tauleta no pot instal·lar aplicacions desconegudes d\'aquesta font."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Per seguretat, el televisor no pot instal·lar aplicacions desconegudes d\'aquesta font."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Per seguretat, el telèfon no pot instal·lar aplicacions desconegudes d\'aquesta font."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"El telèfon i les dades personals són més vulnerables als atacs d\'aplicacions desconegudes. En instal·lar aquesta aplicació, acceptes que ets responsable de qualsevol dany que es produeixi al telèfon o de la pèrdua de dades que pugui resultar del seu ús."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"La tauleta i les dades personals són més vulnerables als atacs d\'aplicacions desconegudes. En instal·lar aquesta aplicació, acceptes que ets responsable de qualsevol dany que es produeixi a la tauleta o de la pèrdua de dades que pugui resultar del seu ús."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"El televisor i les dades personals són més vulnerables als atacs d\'aplicacions desconegudes. En instal·lar aquesta aplicació, acceptes que ets responsable de qualsevol dany que es produeixi al televisor o de la pèrdua de dades que pugui resultar del seu ús."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continua"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Configuració"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Instal·lant o desinstal·lant aplicacions de Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-cs-television/strings.xml b/packages/PackageInstaller/res/values-cs-television/strings.xml
new file mode 100644
index 0000000..ed2d8dc
--- /dev/null
+++ b/packages/PackageInstaller/res/values-cs-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Odmítnout a již se neptat"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Svoji volbu můžete později změnit v nabídce Nastavení &gt; Aplikace."</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Zobrazit systémové aplikace"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Oprávnění aplikací"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Oprávnění aplikací"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> – oprávnění"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Další oprávnění"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> – oprávnění"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-cs-watch/strings.xml b/packages/PackageInstaller/res/values-cs-watch/strings.xml
new file mode 100644
index 0000000..160d7aa
--- /dev/null
+++ b/packages/PackageInstaller/res/values-cs-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Odmítnout a již se neptat"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Zobrazit systémové aplikace"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Nelze změnit"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Ano"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Zrušit"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-cs/strings.xml b/packages/PackageInstaller/res/values-cs/strings.xml
new file mode 100644
index 0000000..a64c07a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-cs/strings.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Nástroj k instalaci balíčků"</string>
+    <string name="next" msgid="3057143178373252333">"Další"</string>
+    <string name="install" msgid="5896438203900042068">"Instalovat"</string>
+    <string name="done" msgid="3889387558374211719">"Hotovo"</string>
+    <string name="cancel" msgid="8360346460165114585">"Zrušit"</string>
+    <string name="installing" msgid="8613631001631998372">"Probíhá instalace..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Instalace balíčku <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplikace je nainstalována."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Chcete tuto aplikaci nainstalovat? Aplikace získá přístup k těmto oprávněním:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Chcete tuto aplikaci nainstalovat? Aplikace nevyžaduje žádná zvláštní oprávnění."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Chcete nainstalovat aktualizaci této existující aplikace? Stávající data nebudou ztracena. Aktualizovaná aplikace získá přístup k následujícímu:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Chcete nainstalovat aktualizaci této integrované aplikace? Stávající data nebudou ztracena. Aktualizovaná aplikace získá přístup k následujícímu:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Chcete nainstalovat aktualizaci této existující aplikace? Vaše existující data nebudou ztracena. Není vyžadován žádný zvláštní přístup."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Chcete nainstalovat aktualizaci této integrované aplikace? Vaše existující data nebudou ztracena. Není vyžadován žádný zvláštní přístup."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Aplikaci nelze nainstalovat."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Instalace balíčku byla zablokována."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Aplikaci nelze nainstalovat, protože balíček je v konfliktu se stávajícím balíčkem."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Aplikaci nelze nainstalovat, protože s tabletem není kompatibilní."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Tato aplikace s vaší televizí není kompatibilní."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Aplikaci nelze nainstalovat, protože s telefonem není kompatibilní."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Aplikaci nelze nainstalovat, protože balíček zřejmě není platný."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g> do tohoto tabletu nelze nainstalovat."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g> se do televize nepodařilo nainstalovat."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g> do tohoto telefonu nelze nainstalovat."</string>
+    <string name="launch" msgid="4826921505917605463">"Otevřít"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Váš administrátor nedovoluje instalaci aplikací z neznámých zdrojů"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Tento uživatel nemůže instalovat neznámé aplikace"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Tento uživatel nesmí instalovat aplikace"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Spravovat aplikace"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Nedostatek místa"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g> nelze nainstalovat. Uvolněte místo v paměti a zkuste to znovu."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Aplikace nebyla nalezena"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Aplikaci se nepodařilo najít na seznamu nainstalovaných aplikací."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Není povoleno"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Aktuální uživatel nemá k odinstalaci oprávnění."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Chyba"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Aplikaci nelze odinstalovat."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Odinstalovat aplikaci"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Odinstalovat aktualizaci"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"Činnost <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je součástí následující aplikace:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Chcete tuto aplikaci odinstalovat?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Chcete  tuto aplikaci odinstalovat "<b>"všem"</b>" uživatelům? Aplikace a její údaje budou odstraněny "<b>"všem"</b>" uživatelům tohoto zařízení."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Chcete tuto aplikaci pro uživatele <xliff:g id="USERNAME">%1$s</xliff:g> odinstalovat?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Chcete tuto aplikaci nahradit tovární verzí? Všechna data budou odstraněna."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Probíhající odinstalace"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Neúspěšné odinstalace"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Probíhá odinstalace..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Odinstalace balíčku <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Odinstalace byla dokončena."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Balíček <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> byl odinstalován"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Odinstalace se nezdařila."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Odinstalace balíčku <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> se nezdařila."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Aktivní aplikaci pro správu zařízení nelze odinstalovat"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Aktivní aplikaci pro správu zařízení uživatele <xliff:g id="USERNAME">%1$s</xliff:g> nelze odinstalovat"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Tato aplikace je u některých uživatelů nebo profilů požadována, u ostatních byla odinstalována."</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Tato aplikace je pro váš profil požadována a nelze ji odinstalovat."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Tato aplikace je administrátorem zařízení vyžadována a nelze ji odinstalovat."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Přejít do nastavení aplikací pro správu zařízení"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Správa uživatelů"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g> nelze odinstalovat."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Při analýze balíčku došlo k chybě."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nově"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Vše"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Ochrana soukromí"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Přístup k zařízení"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Tato aktualizace nevyžaduje žádná nová oprávnění."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Odmítnout"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Další informace"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Zamítnout"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> z <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Vždy povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Pouze při používání aplikace"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Vždy"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Odmítnout a už se neptat"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"zakázáno (<xliff:g id="COUNT">%1$d</xliff:g>)"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"vše zakázáno"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"nic nezakázáno"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Povolit"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplikace"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Oprávnění aplikací"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Příště se neptat"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Žádná oprávnění"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Další oprávnění"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Otevřít informace o aplikaci"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="few">Ještě <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="many">Ještě <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="other">Ještě <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Ještě <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Tato aplikace byla vytvořena pro starší verzi platformy Android. Pokud oprávnění neudělíte, může přestat fungovat podle původního záměru."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"provést neznámou akci"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Povoleno u <xliff:g id="COUNT_0">%1$d</xliff:g> z <xliff:g id="COUNT_1">%2$d</xliff:g> aplikací"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Zobrazit systémové aplikace"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Skrýt systémové aplikace"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Žádné aplikace"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Nastavení polohy"</string>
+    <string name="location_warning" msgid="8778701356292735971">"Služby určování polohy v tomto zařízení poskytuje aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>. Přístup k poloze lze upravit v nastavení polohy."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Pokud toto oprávnění zamítnete, základní funkce zařízení nemusejí fungovat správně."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Vynuceno zásadami"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Přístup na pozadí byl zakázán zásadami"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Přístup na pozadí byl povolen zásadami"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Přístup na popředí byl povolen zásadami"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Spravováno administrátorem"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Vždy"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Pouze při používání aplikace"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nikdy"</string>
+    <string name="loading" msgid="7811651799620593731">"Načítání…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Všechna oprávnění"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Ostatní oprávnění aplikace"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Žádost o oprávnění"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Byla zjištěna překryvná vrstva obrazovky"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Chcete-li změnit nastavení tohoto oprávnění, v Nastavení &gt; Aplikace je třeba nejprve vypnout překryvnou vrstvu obrazovky"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Otevřít nastavení"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Akce instalace/odinstalace nejsou v zařízení Wear podporovány."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Určete, k čemu aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; povolíte přístup"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Aplikace &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; byla aktualizována. Určete, k čemu jí povolíte přístup."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Zrušit"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Pokračovat"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nová oprávnění"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Aktuální oprávnění"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Příprava instalace…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Neznámá aplikace"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Z bezpečnostních důvodů do tabletu není dovoleno instalovat neznámé aplikace z tohoto zdroje."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Z bezpečnostních důvodů do televize není dovoleno instalovat neznámé aplikace z tohoto zdroje."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Z bezpečnostních důvodů do telefonu není dovoleno instalovat neznámé aplikace z tohoto zdroje."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Telefon a osobní údaje jsou zranitelnější vůči útoku ze strany neznámých aplikací. Instalací této aplikace přijímáte odpovědnost za případné škody na telefonu nebo ztrátu dat, která může být používáním aplikace způsobena."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Tablet a osobní údaje jsou zranitelnější vůči útoku ze strany neznámých aplikací. Instalací této aplikace přijímáte odpovědnost za případné škody na tabletu nebo ztrátu dat, která může být používáním aplikace způsobena."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Televize a osobní údaje jsou zranitelnější vůči útoku ze strany neznámých aplikací. Instalací této aplikace přijímáte odpovědnost za případné škody na televizi nebo ztrátu dat, která může být používáním aplikace způsobena."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Pokračovat"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Nastavení"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Instalace/odinstalace aplikací pro Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-da-television/strings.xml b/packages/PackageInstaller/res/values-da-television/strings.xml
new file mode 100644
index 0000000..f9c0da2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-da-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Afvis, og spørg ikke igen"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Du kan altid ændre dette i Indstillinger &gt; Apps"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Vis systemapps"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Apptilladelser"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Apptilladelser"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g>-tilladelser"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Flere tilladelser"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g>-tilladelser"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-da-watch/strings.xml b/packages/PackageInstaller/res/values-da-watch/strings.xml
new file mode 100644
index 0000000..616b1c2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-da-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Afvis, og spørg ikke igen"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Vis systemapps"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Kan ikke ændres"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Ja"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Annuller"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-da/strings.xml b/packages/PackageInstaller/res/values-da/strings.xml
new file mode 100644
index 0000000..73d03a5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-da/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Pakkeinstallationsprogram"</string>
+    <string name="next" msgid="3057143178373252333">"Næste"</string>
+    <string name="install" msgid="5896438203900042068">"Installer"</string>
+    <string name="done" msgid="3889387558374211719">"Afslut"</string>
+    <string name="cancel" msgid="8360346460165114585">"Annuller"</string>
+    <string name="installing" msgid="8613631001631998372">"Installerer..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Installerer <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Appen er installeret."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Vil du installere denne applikation? Den får adgang til følgende:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Vil du installere denne applikation? Den kræver ingen særlig adgang."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Vil du installere en opdatering til den eksisterende app? Du mister ikke dine eksisterende data. Den opdaterede app kan gøre følgende:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Vil du installere en opdatering til den indbyggede app? Du mister ikke dine eksisterende data. Den opdaterede app kan gøre følgende:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Vil du installere en opdatering til denne eksisterende applikation? Dine eksisterende data vil ikke gå tabt. Det kræver ikke nogen særlig adgang."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Vil du installere en opdatering til denne indbyggede applikation? Dine eksisterende data vil ikke gå tabt. Det kræver ikke nogen særlig adgang."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Appen blev ikke installeret."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Pakken blev blokeret i at blive installeret."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Appen blev ikke installeret, da pakken er i strid med en eksisterende pakke."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Appen blev ikke installeret, da den er ikke kompatibel med din tablet."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Denne app er ikke kompatibel med dit fjernsyn."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Appen blev ikke installeret, da den ikke er kompatibel med din telefon."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Appen blev ikke installeret, da pakken ser ud til at være ugyldig."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> kunne ikke installeres på din tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> kunne ikke installeres på dit tv."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> kunne ikke installeres på din telefon."</string>
+    <string name="launch" msgid="4826921505917605463">"Åbn"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Din administrator tillader ikke installation af apps, der hentes fra ukendte kilder"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Denne bruger kan ikke installere ukendte apps"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Denne bruger har ikke tilladelse til at installere apps"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Administrer apps"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Der er ikke mere plads"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> kunne ikke installeres. Frigør noget plads, og prøv igen."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Appen blev ikke fundet"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Appen blev ikke fundet på listen over installerede apps."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Ikke tilladt"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Den nuværende bruger har ikke tilladelse til at udføre denne afinstallation."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Fejl"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Appen kunne ikke afinstalleres."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Afinstaller appen"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Afinstaller opdatering"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> er en del af følgende app:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Vil du afinstallere denne app?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Vil du afinstallere denne app for "<b>"alle"</b>" brugere? Applikationen og dens data vil blive fjernet fra "<b>"alle"</b>" brugere på denne enhed."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Vil du afinstallere denne app for brugeren <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Vil du erstatte denne app med fabriksversionen? Alle data fjernes."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Vil du erstatte denne app med fabriksversionen? Alle data fjernes. Dette påvirker alle brugere af denne enhed, herunder de brugere, der har arbejdsprofiler."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Igangværende afinstallationer"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Mislykkede afinstallationer"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Afinstallerer..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Afinstallerer <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Afinstallationen er afsluttet."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> blev afinstalleret"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Afinstallationen mislykkedes."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kunne ikke afinstalleres."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Den aktive app til enhedsadministration kan ikke afinstalleres"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Den aktive app til enhedsadministration for <xliff:g id="USERNAME">%1$s</xliff:g> kan ikke afinstalleres"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Denne app kræves for nogle brugere eller profiler og afinstalleres for andre"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Denne app er nødvendig for din profil og kan ikke afinstalleres."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Denne app er påkrævet af din enhedsadministrator og kan ikke afinstalleres."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Administrer apps til enhedsadministration"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Administrer brugere"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> kunne ikke afinstalleres."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Der opstod et problem med parsing af pakken."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Ny"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Alle"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privatliv"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Adgang til enheden"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Denne opdatering kræver ingen nye tilladelser."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Afvis"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Få flere oplysninger"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Afvis alligevel"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> ud af <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Skal &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; altid have tilladelse til at <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Kun mens appen bruges"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Altid"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Afvis, og spørg ikke igen"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> er deaktiveret"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"alle er deaktiveret"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ingen er deaktiveret"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Tillad"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Apps"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Apptilladelser"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Spørg ikke igen"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Ingen tilladelser"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Flere tilladelser"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Åbn appinfo"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> mere</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> mere</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Denne app er udviklet til en ældre version af Android. Hvis du ikke giver den tilladelse, vil den muligvis ikke længere virke efter hensigten."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"udføre en ukendt handling"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> ud af <xliff:g id="COUNT_1">%2$d</xliff:g> apps har tilladelse"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Vis systemapps"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Skjul systemapps"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Ingen apps"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Placeringsindstillinger"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> udbyder placeringstjenester for denne enhed. Adgangen til din placering kan ændres i Placeringsindstillinger."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Hvis du afviser denne tilladelse, vil grundlæggende funktioner på din enhed muligvis ikke længere fungere efter hensigten."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Håndhæves af politik"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Adgang i baggrunden er deaktiveret af en politik"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Adgang i baggrunden er aktiveret af en politik"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Adgang i forgrunden er aktiveret af en politik"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Styres af administratoren"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Altid"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Kun mens appen bruges"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Aldrig"</string>
+    <string name="loading" msgid="7811651799620593731">"Indlæser…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Alle tilladelser"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Andre app-egenskaber"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Anmodning om tilladelse"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Der er registreret skærmoverlejring"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Hvis du vil ændre denne indstilling for tilladelser, skal du først slå skærmoverlejringen fra i Indstillinger &gt; Apps"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Åbn indstillingerne"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Det er ikke muligt at installere/afinstallere på Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Vælg, hvad &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; må få adgang til"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; er blevet opdateret. Vælg, hvad denne app må få adgang til."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Annuller"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Fortsæt"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nye tilladelser"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Aktuelle tilladelser"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Forbereder appen…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Ukendt"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Din tablet har af sikkerhedshensyn ikke tilladelse til at installere ukendte apps fra denne kilde."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Dit fjernsyn har af sikkerhedshensyn ikke tilladelse til at installere ukendte apps fra denne kilde."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Din telefon har af sikkerhedshensyn ikke tilladelse til at installere ukendte apps fra denne kilde."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Din telefon og dine personlige data er mere sårbare over for angreb fra ukendte apps. Når du installerer denne app, accepterer du, at du er ansvarlig for skader på din telefon eller tab af data, der kan skyldes brug af appen."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Din tablet og dine personlige data er mere sårbare over for angreb fra ukendte apps. Når du installerer denne app, accepterer du, at du er ansvarlig for skader på din tablet eller tab af data, der kan skyldes brug af appen."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Dit fjernsyn og dine personlige data er mere sårbare over for angreb fra ukendte apps. Når du installerer denne app, accepterer du, at du er ansvarlig for skader på dit fjernsyn eller tab af data, der kan skyldes brug af appen."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Fortsæt"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Indstillinger"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Installerer/afinstallerer Wear-apps"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-de-television/strings.xml b/packages/PackageInstaller/res/values-de-television/strings.xml
new file mode 100644
index 0000000..dc218e4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-de-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Ablehnen und nicht mehr fragen"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Du kannst dies später unter \"Einstellungen &gt; Apps\" ändern."</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"System-Apps anzeigen"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"App-Berechtigungen"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"App-Berechtigungen"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Berechtigungen für <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Zusätzliche Berechtigungen"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Berechtigungen für <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-de-watch/strings.xml b/packages/PackageInstaller/res/values-de-watch/strings.xml
new file mode 100644
index 0000000..5a0b8e1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-de-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Ablehnen &amp; nicht mehr fragen"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"System-Apps anzeigen"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Keine Änderung möglich"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"\"Ja\""</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Abbrechen"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-de/strings.xml b/packages/PackageInstaller/res/values-de/strings.xml
new file mode 100644
index 0000000..5a05c0c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-de/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Paket-Installer"</string>
+    <string name="next" msgid="3057143178373252333">"Weiter"</string>
+    <string name="install" msgid="5896438203900042068">"Installieren"</string>
+    <string name="done" msgid="3889387558374211719">"Fertig"</string>
+    <string name="cancel" msgid="8360346460165114585">"Abbrechen"</string>
+    <string name="installing" msgid="8613631001631998372">"Wird installiert..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> wird installiert…"</string>
+    <string name="install_done" msgid="3682715442154357097">"App wurde installiert."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Möchtest du diese App installieren? Sie erhält dann folgende Berechtigungen:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Möchtest du diese App installieren? Sie benötigt keinen besonderen Zugriff."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Möchtest du ein Update für diese vorhandene App installieren? Deine vorhandenen Daten bleiben erhalten. Die aktualisierte App erhält Zugriff auf:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Möchtest du ein Update für diese integrierte App installieren? Deine vorhandenen Daten bleiben erhalten. Die aktualisierte App erhält Zugriff auf:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Möchtest du ein Update für diese bestehende App installieren? Deine vorhandenen Daten bleiben erhalten. Die App benötigt keine besonderen Zugriffsrechte."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Möchtest du ein Update für diese integrierte App installieren? Deine vorhandenen Daten bleiben erhalten. Die App benötigt keine besonderen Zugriffsrechte."</string>
+    <string name="install_failed" msgid="6579998651498970899">"App wurde nicht installiert."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Die Installation des Pakets wurde blockiert."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Die App wurde nicht installiert, da das Paket in Konflikt mit einem bestehenden Paket steht."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Die App wurde nicht installiert, da sie nicht mit deinem Tablet kompatibel ist."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Diese App ist nicht mit deinem Fernseher kompatibel."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Die App wurde nicht installiert, da sie nicht mit deinem Smartphone kompatibel ist."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Die App wurde nicht installiert, da das Paket offenbar ungültig ist."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> konnte nicht auf deinem Tablet installiert werden."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> konnte nicht auf deinem Fernseher installiert werden."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> konnte nicht auf deinem Telefon installiert werden."</string>
+    <string name="launch" msgid="4826921505917605463">"Öffnen"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Dein Administrator lässt keine Installationen von Apps aus unbekannten Quellen zu"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Dieser Nutzer darf keine unbekannten Apps installieren"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Dieser Nutzer darf keine Apps installieren"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Apps verwalten"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Kein freier Speicher vorhanden"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> konnte nicht installiert werden. Gib Speicherplatz frei und versuche es erneut."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"App nicht gefunden"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Die App wurde nicht in der Liste der installierten Apps gefunden."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Keine Berechtigung"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Der aktuelle Nutzer ist nicht dazu berechtigt, diese Deinstallation auszuführen."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Fehler"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"App konnte nicht deinstalliert werden."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"App deinstallieren"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Update deinstallieren"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> gehört zu folgender App:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Möchtest du diese App deinstallieren?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Möchtest du diese App für "<b>"alle"</b>" Nutzer entfernen? Die App und alle zugehörigen Daten werden für "<b>"alle"</b>" Nutzer des Geräts entfernt."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"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="1394549691152728409">"Diese App durch die Werksversion ersetzen? Alle Daten werden entfernt."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Diese App durch die Werksversion ersetzen? Alle Daten werden entfernt. Dies betrifft alle Nutzer des Geräts, einschließlich Arbeitsprofilen."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Laufende Deinstallationen"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Fehlgeschlagene Deinstallationen"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Wird deinstalliert..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> wird deinstalliert…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Deinstallation abgeschlossen"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> deinstalliert"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Deinstallation fehlgeschlagen"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Deinstallation von <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> fehlgeschlagen."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Aktive Apps zur Geräteverwaltung können nicht deinstalliert werden"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Die aktive App zur Geräteverwaltung kann nicht für <xliff:g id="USERNAME">%1$s</xliff:g> deinstalliert werden"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Diese App wird für einige Nutzer oder Profile benötigt und wurde für andere deinstalliert"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Diese App wird für dein Profil benötigt und kann nicht deinstalliert werden."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Die App wurde als obligatorisch festgelegt und kann nicht deinstalliert werden."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Apps zur Geräteverwaltung"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Nutzer verwalten"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> konnte nicht deinstalliert werden."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Beim Parsen des Pakets ist ein Problem aufgetreten."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Neu"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Alle"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Datenschutz"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Gerätezugriff"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Für dieses Update sind keine neuen Berechtigungen erforderlich."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Ablehnen"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Weitere Informationen"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Trotzdem ablehnen"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> von <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Zulassen, dass die App &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g> darf?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; immer erlauben zu <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Nur während der App-Nutzung"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Immer"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Ablehnen und nicht mehr fragen"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> deaktiviert"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"Alle deaktiviert"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"Keine deaktiviert"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Zulassen"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Apps"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"App-Berechtigungen"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Nicht mehr fragen"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Keine Berechtigungen"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Zusätzliche Berechtigungen"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"App-Info öffnen"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">Noch <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Noch <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Diese App wurde für eine ältere Version von Android konzipiert. Wenn du keine Berechtigung erteilst, funktioniert die App möglicherweise nicht mehr ordnungsgemäß."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"Unbekannte Aktion durchführen"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> von <xliff:g id="COUNT_1">%2$d</xliff:g> Apps sind berechtigt."</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"System-Apps anzeigen"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"System ausblenden"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Keine Apps"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Standorteinstellungen"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist ein Anbieter von Standortdiensten für dieses Gerät. Die Berechtigungen für den Zugriff auf deinen Standort kannst du in den Standorteinstellungen ändern."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Wenn du diese Berechtigung deaktivierst, funktionieren grundlegende Funktionen deines Geräts möglicherweise nicht mehr ordnungsgemäß."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Von Richtlinien durchgesetzt"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Hintergrundzugriff aufgrund der Richtlinie deaktiviert"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Hintergrundzugriff aufgrund der Richtlinie aktiviert"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Vordergrundzugriff aufgrund der Richtlinie aktiviert"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Durch den Administrator verwaltet"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Immer"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Nur während der App-Nutzung"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nie"</string>
+    <string name="loading" msgid="7811651799620593731">"Wird geladen…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Alle Berechtigungen"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Andere App-Funktionen"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Berechtigungsanfrage"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Display-Overlay erkannt"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Um diese Berechtigungseinstellung zu ändern, musst du zunächst das Display-Overlay über \"Einstellungen\" &gt; \"Apps\" deaktivieren."</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Einstellungen öffnen"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Von Android Wear nicht unterstützte Aktionen installieren/deinstallieren."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Worauf darf die App &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; zugreifen?"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Die App &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; wurde aktualisiert. Worauf darf diese App zugreifen?"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Abbrechen"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Weiter"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Neue Berechtigungen"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Aktuelle Berechtigungen"</string>
+    <string name="message_staging" msgid="6151794817691100003">"App wird vorbereitet…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Unbekannt"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Aus Sicherheitsgründen kannst du auf dem Tablet keine unbekannten Apps aus dieser Quelle installieren."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Aus Sicherheitsgründen kannst du auf dem Fernseher keine unbekannten Apps aus dieser Quelle installieren."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Aus Sicherheitsgründen kannst du auf dem Smartphone keine unbekannten Apps aus dieser Quelle installieren."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Unbekannte Apps können gefährlich für dein Smartphone und deine personenbezogenen Daten sein. Indem du diese App installierst, erklärst du dich damit einverstanden, dass du die Verantwortung für alle Schäden an deinem Smartphone und jegliche Datenverluste trägst, die aus der Verwendung dieser App entstehen können."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Unbekannte Apps können gefährlich für dein Tablet und deine personenbezogenen Daten sein. Indem du diese App installierst, erklärst du dich damit einverstanden, dass du die Verantwortung für alle Schäden an deinem Tablet und jegliche Datenverluste trägst, die aus der Verwendung dieser App entstehen können."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Unbekannte Apps können gefährlich für deinen Fernseher und deine personenbezogenen Daten sein. Indem du diese App installierst, erklärst du dich damit einverstanden, dass du die Verantwortung für alle Schäden an deinem Fernseher und jegliche Datenverluste trägst, die aus der Verwendung dieser App entstehen können."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Weiter"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Einstellungen"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear-Apps installieren/deinstallieren"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-el-television/strings.xml b/packages/PackageInstaller/res/values-el-television/strings.xml
new file mode 100644
index 0000000..44f77a9
--- /dev/null
+++ b/packages/PackageInstaller/res/values-el-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Απόρριψη και να μην ερωτηθώ ξανά"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Μπορείτε να το αλλάξετε αυτό αργότερα από το μενού Ρυθμίσεις &gt; Εφαρμογές"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Εμφάνιση εφαρμογών συστήματος"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Άδειες εφαρμογών"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Άδειες εφαρμογών"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Άδειες - <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Πρόσθετες άδειες"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Άδειες - <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-el-watch/strings.xml b/packages/PackageInstaller/res/values-el-watch/strings.xml
new file mode 100644
index 0000000..3d923dc
--- /dev/null
+++ b/packages/PackageInstaller/res/values-el-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Απόρριψη και να μην ερωτηθώ ξανά"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Εμφάνιση εφαρμογών συστήματος"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Δεν είναι δυνατή η αλλαγή"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Ναι"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Ακύρωση"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-el/strings.xml b/packages/PackageInstaller/res/values-el/strings.xml
new file mode 100644
index 0000000..707f7b1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-el/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Πρόγραμμα εγκατάστασης πακέτου"</string>
+    <string name="next" msgid="3057143178373252333">"Επόμενο"</string>
+    <string name="install" msgid="5896438203900042068">"Εγκατάσταση"</string>
+    <string name="done" msgid="3889387558374211719">"Τέλος"</string>
+    <string name="cancel" msgid="8360346460165114585">"Ακύρωση"</string>
+    <string name="installing" msgid="8613631001631998372">"Εγκατάσταση..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Εγκατάσταση <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Εγκατάσταση εφαρμογής."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Θέλετε να εγκαταστήσετε αυτήν την εφαρμογή; Θα έχει πρόσβαση σε:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Θέλετε να εγκαταστήσετε αυτήν την εφαρμογή; Δεν απαιτείται οποιαδήποτε ειδική πρόσβαση."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Θέλετε να εγκαταστήσετε μια ενημέρωση σε αυτήν την υπάρχουσα εφαρμογή; Τα υπάρχοντα δεδομένα σας δεν θα χαθούν. Η ενημερωμένη εφαρμογή θα έχει πρόσβαση σε:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Θέλετε να εγκαταστήσετε μια ενημέρωση σε αυτήν την ενσωματωμένη εφαρμογή; Τα υπάρχοντα δεδομένα σας δεν θα χαθούν. Η ενημερωμένη εφαρμογή θα έχει πρόσβαση σε:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Θέλετε να εγκαταστήσετε μια ενημέρωση για αυτήν την υπάρχουσα εφαρμογή; Τα υπάρχοντα δεδομένα σας δεν θα χαθούν. Δεν απαιτείται ειδική πρόσβαση."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Θέλετε να εγκαταστήσετε μια ενημέρωση για αυτήν την ενσωματωμένη εφαρμογή; Τα υπάρχοντα δεδομένα σας δεν θα χαθούν. Δεν απαιτείται ειδική πρόσβαση."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Η εφαρμογή δεν έχει εγκατασταθεί."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Η εγκατάσταση του πακέτου αποκλείστηκε."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Η εφαρμογή δεν έχει εγκατασταθεί, επειδή το πακέτο έρχεται σε διένεξη με κάποιο υπάρχον πακέτο."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Η εφαρμογή δεν έχει εγκατασταθεί, επειδή δεν είναι συμβατή με το tablet που χρησιμοποιείτε."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Αυτή η εφαρμογή δεν είναι συμβατή με την τηλεόρασή σας."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Η εφαρμογή δεν έχει εγκατασταθεί, επειδή δεν είναι συμβατή με το τηλέφωνό σας."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Η εφαρμογή δεν έχει εγκατασταθεί, επειδή φαίνεται ότι το πακέτο δεν είναι έγκυρο."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Δεν ήταν δυνατή η εγκατάσταση της εφαρμογής <xliff:g id="APP_NAME">%1$s</xliff:g> στο tablet σας."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> δεν ήταν δυνατό να εγκατασταθεί στην τηλεόρασή σας."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Δεν ήταν δυνατή η εγκατάσταση της εφαρμογής <xliff:g id="APP_NAME">%1$s</xliff:g> στο τηλέφωνό σας."</string>
+    <string name="launch" msgid="4826921505917605463">"Άνοιγμα"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Ο διαχειριστής σας δεν επιτρέπει την εγκατάσταση εφαρμογών που προέρχονται από άγνωστες πηγές"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Δεν είναι δυνατή η εγκατάσταση άγνωστων εφαρμογών από αυτόν τον χρήστη"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Αυτός ο χρήστης δεν έχει δυνατότητα εγκατάστασης εφαρμογών."</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Διαχείριση εφαρμογών"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Δεν υπάρχει χώρος"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Δεν ήταν δυνατή η εγκατάσταση της εφαρμογής <xliff:g id="APP_NAME">%1$s</xliff:g>. Απελευθερώστε λίγο χώρο και προσπαθήστε ξανά."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Δεν βρέθηκε εφαρμογή"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Η εφαρμογή δεν βρέθηκε στη λίστα με τις εγκατεστημένες εφαρμογές."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Δεν επιτρέπεται"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Δεν επιτρέπεται στον τρέχοντα χρήση να εκτελέσει την απεγκατάσταση."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Σφάλμα"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Δεν ήταν δυνατή η απεγκατάσταση της εφαρμογής."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Απεγκατάσταση εφαρμογής"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Απεγκατάσταση ενημέρωσης"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"Η δραστηριότητα <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> αποτελεί τμήμα της ακόλουθης εφαρμογής:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Θέλετε να καταργήσετε την εγκατάσταση αυτής της εφαρμογής;"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Θέλετε να καταργήσετε την εγκατάσταση αυτής της εφαρμογής για "<b>"όλους"</b>" τους χρήστες; Η εφαρμογή και τα δεδομένα της θα καταργηθούν από "<b>"όλους"</b>" τους χρήστες στη συσκευή."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Θέλετε να καταργήσετε την εγκατάσταση αυτής της εφαρμογής για το χρήστη <xliff:g id="USERNAME">%1$s</xliff:g>;"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Αντικατάσταση αυτής της εφαρμογής με την εργοστασιακή έκδοση; Όλα τα δεδομένα θα καταργηθούν."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Αντικατάσταση αυτής της εφαρμογής με την εργοστασιακή έκδοση; Όλα τα δεδομένα θα καταργηθούν. Αυτό επηρεάζει όλους τους χρήστες της συσκευής, συμπεριλαμβανομένων και αυτών με προφίλ εργασίας."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Απεγκαταστάσεις σε εξέλιξη"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Αποτυχημένες απεγκαταστάσεις"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Απεγκατάσταση..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Απεγκατάσταση <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Η κατάργηση εγκατάστασης ολοκληρώθηκε."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Απεγκαταστάθηκε <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Μη επιτυχής κατάργηση εγκατάστασης."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Επιτυχής απεγκατάσταση <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Η κατάργ. εγκατάστ. της ενεργούς εφαρμογής διαχείρισης συσκευής δεν είναι δυνατή"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Η κατάργ. εγκατάστασης της ενεργούς εφαρμογής διαχείρισης συσκευής για τον χρήστη <xliff:g id="USERNAME">%1$s</xliff:g> δεν είναι δυνατή"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Η εφαρμογή απαιτείται για κάποιους χρήστες/προφίλ και απεγκαταστήθηκε για άλλους"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Αυτή η εφαρμογή απαιτείται για το προφίλ σας και δεν είναι δυνατή η απεγκατάστασή της."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Η εφαρμογή απαιτείται από το διαχειριστή και δεν είναι δυνατή η απεγκατάσταση."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Διαχείριση εφαρμογών διαχείρισης συσκευής"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Διαχείριση χρηστών"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Δεν ήταν δυνατή η κατάργηση της εγκατάστασης της εφαρμογής <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Παρουσιάστηκε ένα πρόβλημα κατά την ανάλυση του πακέτου."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Νέο"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Όλα"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Απόρρητο"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Πρόσβαση συσκευής"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Αυτή η ενημέρωση δεν απαιτεί νέες άδειες."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Απόρριψη"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Περισσότερες πληροφορίες"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Απόρριψη"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> από <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; να <xliff:g id="ACTION">%2$s</xliff:g>;"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Να επιτρέπεται πάντα στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; να <xliff:g id="ACTION">%2$s</xliff:g>;"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Μόνο κατά τη χρήση της εφαρμογής"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Πάντα"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Απόρριψη και να μην ερωτηθώ ξανά"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"έχουν απενεργοποιηθεί <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"έχουν απενεργοποιηθεί όλες"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"δεν έχει απενεργοποιηθεί καμία"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Να επιτρέπεται"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Εφαρμογές"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Δικαιώματα εφ/γών"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Να μην ερωτηθώ ξανά"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Χωρίς δικαιώματα"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Πρόσθετα δικαιώματα"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Άνοιγμα πληροφοριών εφαρμογής"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">Ακόμα <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Ακόμα <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Αυτή η εφαρμογή σχεδιάστηκε για παλαιότερη έκδοση του Android. Η άρνηση άδειας μπορεί να έχει ως αποτέλεσμα να διακοπεί η κανονική λειτουργία της."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"εκτέλεση άγνωστης ενέργειας"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Επιτρέπονται <xliff:g id="COUNT_0">%1$d</xliff:g> από <xliff:g id="COUNT_1">%2$d</xliff:g> εφαρμογές"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Εμφάνιση συστήματος"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Απόκρυψη συστήματος"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Δεν υπάρχουν εφαρμογές"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Ρυθμίσεις τοποθεσίας"</string>
+    <string name="location_warning" msgid="8778701356292735971">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> είναι ο πάροχος των υπηρεσιών τοποθεσίας για τη συγκεκριμένη συσκευή. Μπορείτε να τροποποιήσετε την πρόσβαση τοποθεσίας από τις ρυθμίσεις τοποθεσίας."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Εάν αρνηθείτε να παραχωρήσετε αυτήν την άδεια, η λειτουργία ορισμένων βασικών δυνατοτήτων ενδέχεται να μην είναι η αναμενόμενη."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Επιβάλλεται από την πολιτική"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Η πρόσβαση στο παρασκήνιο απενενεργοποιήθηκε βάσει πολιτικής"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Η πρόσβαση στο παρασκήνιο ενεργοποιήθηκε βάσει πολιτικής"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Η πρόσβαση στο προσκήνιο ενεργοποιήθηκε βάσει πολιτικής"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Ελέγχεται από τον διαχειριστή"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Πάντα"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Μόνο κατά τη χρήση της εφαρμ."</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Ποτέ"</string>
+    <string name="loading" msgid="7811651799620593731">"Γίνεται φόρτωση…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Όλα τα δικαιώματα"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Άλλες δυνατότητες εφαρμογής"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Αίτημα άδειας"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Εντοπίστηκε επικάλυψη οθόνης"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Για να αλλάξετε αυτή τη ρύθμιση άδειας, θα πρέπει πρώτα να απενεργοποιήσετε την επικάλυψη οθόνης από τις Ρυθμίσεις &gt; Εφαρμογές"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Άνοιγμα ρυθμίσεων"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Οι ενέργειες εγκατάστασης/απεγκατάστασης δεν υποστηρίζονται στο Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Επιλέξτε σε τι θα έχει πρόσβαση η εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Η εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ενημερώθηκε. Επιλέξτε σε τι θα έχει πρόσβαση αυτή η εφαρμογή."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Ακύρωση"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Συνέχεια"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Νέες άδειες"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Τρέχουσες άδειες"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Σταδιακή διάθεση εφαρμογής…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Άγνωστη"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Για λόγους ασφάλειας, δεν επιτρέπεται η εγκατάσταση άγνωστων εφαρμογών από αυτήν την πηγή στο tablet σας."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Για λόγους ασφάλειας, δεν επιτρέπεται η εγκατάσταση άγνωστων εφαρμογών από αυτήν την πηγή στην τηλεόρασή σας."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Για λόγους ασφάλειας, δεν επιτρέπεται η εγκατάσταση άγνωστων εφαρμογών από αυτήν την πηγή στο τηλέφωνό σας."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Το τηλέφωνό σας και τα προσωπικά δεδομένα σας είναι πιο ευάλωτα σε επιθέσεις από άγνωστες εφαρμογές. Με την εγκατάσταση αυτής της εφαρμογής, συμφωνείτε ότι είστε υπεύθυνοι για οποιαδήποτε ζημιά στο τηλέφωνο ή απώλεια δεδομένων που μπορεί να προκύψει από τη χρήση τους."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Το tablet σας και τα προσωπικά δεδομένα σας είναι πιο ευάλωτα σε επιθέσεις από άγνωστες εφαρμογές. Με την εγκατάσταση αυτής της εφαρμογής, συμφωνείτε ότι είστε υπεύθυνοι για οποιαδήποτε ζημιά στο tablet ή απώλεια δεδομένων που μπορεί να προκύψει από τη χρήση τους."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Η τηλεόρασή σας και τα προσωπικά δεδομένα σας είναι πιο ευάλωτα σε επιθέσεις από άγνωστες εφαρμογές. Με την εγκατάσταση αυτής της εφαρμογής, συμφωνείτε ότι είστε υπεύθυνοι για οποιαδήποτε ζημιά στην τηλεόρασή ή απώλεια δεδομένων που μπορεί να προκύψει από τη χρήση τους."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Συνέχεια"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Ρυθμίσεις"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Εγκατάσταση/απεγκατάσταση εφαρμογών Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rAU-television/strings.xml b/packages/PackageInstaller/res/values-en-rAU-television/strings.xml
new file mode 100644
index 0000000..663e1d4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rAU-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Deny and don\'t ask again"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"You can change this later in Settings &gt; Apps"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Show system apps"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"App permissions"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"App permissions"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> permissions"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Additional permissions"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> permissions"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rAU-watch/strings.xml b/packages/PackageInstaller/res/values-en-rAU-watch/strings.xml
new file mode 100644
index 0000000..e0d0edb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rAU-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Deny, don\'t ask again"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Show system apps"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Can\'t be changed"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Yes"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Cancel"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rAU/strings.xml b/packages/PackageInstaller/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..ded57bb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rAU/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Package installer"</string>
+    <string name="next" msgid="3057143178373252333">"Next"</string>
+    <string name="install" msgid="5896438203900042068">"Install"</string>
+    <string name="done" msgid="3889387558374211719">"Done"</string>
+    <string name="cancel" msgid="8360346460165114585">"Cancel"</string>
+    <string name="installing" msgid="8613631001631998372">"Installing…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Installing <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"App installed."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Do you want to install this application? It will get access to:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Do you want to install this application? It does not require any special access."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Do you want to install an update to this existing application? Your existing data will not be lost. The updated application will get access to:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Do you want to install an update to this built-in application? Your existing data will not be lost. The updated application will get access to:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Do you want to install an update to this existing application? Your existing data will not be lost. It does not require any special access."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Do you want to install an update to this built-in application? Your existing data will not be lost. It does not require any special access."</string>
+    <string name="install_failed" msgid="6579998651498970899">"App not installed."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"The package was blocked from being installed."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"App not installed as package conflicts with an existing package."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"App not installed as app isn\'t compatible with your tablet."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"This app isn\'t compatible with your TV."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"App not installed as app isn\'t compatible with your phone."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"App not installed as package appears to be invalid."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed on your tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed on your TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed on your phone."</string>
+    <string name="launch" msgid="4826921505917605463">"Open"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Your admin doesn\'t allow installation of apps obtained from unknown sources"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Unknown apps can\'t be installed by this user"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"This user is not allowed to install apps"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Manage apps"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Out of space"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed. Free up some space and try again."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"App not found"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"The app wasn\'t found in the list of installed apps."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Not allowed"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"The current user is not allowed to perform this uninstallation."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Error"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"App could not be uninstalled."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Uninstall app"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Uninstall update"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> is part of the following app:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Do you want to uninstall this app?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Do you want to uninstall this app for "<b>"all"</b>" users? The application and its data will be removed from "<b>"all"</b>" users on the device."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"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="1394549691152728409">"Replace this app with the factory version? All data will be removed."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Running uninstalls"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Failed uninstalls"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Uninstalling…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Uninstalling <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Uninstall finished"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Uninstalled <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Uninstall unsuccessful."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Uninstalling <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> unsuccessful."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Can\'t uninstall active device admin app"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Can\'t uninstall active device admin app for <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"This app is required for some users or profiles and was uninstalled for others"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"This app is needed for your profile and can\'t be uninstalled."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"This app is required by your device administrator and can\'t be uninstalled."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Manage device admin apps"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Manage users"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be uninstalled."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"There was a problem while parsing the package."</string>
+    <string name="newPerms" msgid="6039428254474104210">"New"</string>
+    <string name="allPerms" msgid="1024385515840703981">"All"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privacy"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Device Access"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"This update requires no new permissions."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Deny"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"More info"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Deny anyway"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> of <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Always allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Only while using app"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Always"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Deny and don’t ask again"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> disabled"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"all disabled"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"none disabled"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Allow"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Apps"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"App permissions"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Don\'t ask again"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"No permissions"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Additional permissions"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Open app info"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> more</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> more</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"This app was designed for an older version of Android. Denying permission may cause it to no longer function as intended."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"perform an unknown action"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> of <xliff:g id="COUNT_1">%2$d</xliff:g> apps allowed"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Show system"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Hide system"</string>
+    <string name="no_apps" msgid="1965493419005012569">"No apps"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Location Settings"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> is a provider of location services for this device. Location access can be modified from location settings."</string>
+    <string name="system_warning" msgid="7103819124542305179">"If you deny this permission, basic features of your device may no longer function as intended."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Enforced by policy"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Background access disabled by policy"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Background access enabled by policy"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Foreground access enabled by policy"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Controlled by admin"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Always"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Only while using app"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Never"</string>
+    <string name="loading" msgid="7811651799620593731">"Loading…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"All permissions"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Other app capabilities"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Permission request"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Screen overlay detected"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"To change this permission setting, you first have to turn off the screen overlay from Settings &gt; Apps"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Open settings"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Install/Uninstall actions not supported on Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Choose what to allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; has been updated. Choose what to allow this app to access."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Cancel"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continue"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"New permissions"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Current permissions"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Staging app…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Unknown"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"For your security, your tablet is not allowed to install unknown apps from this source."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"For your security, your TV is not allowed to install unknown apps from this source."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"For your security, your phone is not allowed to install unknown apps from this source."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Your phone and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your phone or loss of data that may result from its use."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Your tablet and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your tablet or loss of data that may result from its use."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Your TV and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your TV or loss of data that may result from its use."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continue"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Settings"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Installing/uninstalling Wear apps"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rCA-television/strings.xml b/packages/PackageInstaller/res/values-en-rCA-television/strings.xml
new file mode 100644
index 0000000..663e1d4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rCA-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Deny and don\'t ask again"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"You can change this later in Settings &gt; Apps"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Show system apps"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"App permissions"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"App permissions"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> permissions"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Additional permissions"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> permissions"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rCA-watch/strings.xml b/packages/PackageInstaller/res/values-en-rCA-watch/strings.xml
new file mode 100644
index 0000000..e0d0edb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rCA-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Deny, don\'t ask again"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Show system apps"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Can\'t be changed"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Yes"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Cancel"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rCA/strings.xml b/packages/PackageInstaller/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..ded57bb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rCA/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Package installer"</string>
+    <string name="next" msgid="3057143178373252333">"Next"</string>
+    <string name="install" msgid="5896438203900042068">"Install"</string>
+    <string name="done" msgid="3889387558374211719">"Done"</string>
+    <string name="cancel" msgid="8360346460165114585">"Cancel"</string>
+    <string name="installing" msgid="8613631001631998372">"Installing…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Installing <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"App installed."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Do you want to install this application? It will get access to:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Do you want to install this application? It does not require any special access."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Do you want to install an update to this existing application? Your existing data will not be lost. The updated application will get access to:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Do you want to install an update to this built-in application? Your existing data will not be lost. The updated application will get access to:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Do you want to install an update to this existing application? Your existing data will not be lost. It does not require any special access."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Do you want to install an update to this built-in application? Your existing data will not be lost. It does not require any special access."</string>
+    <string name="install_failed" msgid="6579998651498970899">"App not installed."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"The package was blocked from being installed."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"App not installed as package conflicts with an existing package."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"App not installed as app isn\'t compatible with your tablet."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"This app isn\'t compatible with your TV."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"App not installed as app isn\'t compatible with your phone."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"App not installed as package appears to be invalid."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed on your tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed on your TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed on your phone."</string>
+    <string name="launch" msgid="4826921505917605463">"Open"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Your admin doesn\'t allow installation of apps obtained from unknown sources"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Unknown apps can\'t be installed by this user"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"This user is not allowed to install apps"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Manage apps"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Out of space"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed. Free up some space and try again."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"App not found"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"The app wasn\'t found in the list of installed apps."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Not allowed"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"The current user is not allowed to perform this uninstallation."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Error"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"App could not be uninstalled."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Uninstall app"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Uninstall update"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> is part of the following app:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Do you want to uninstall this app?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Do you want to uninstall this app for "<b>"all"</b>" users? The application and its data will be removed from "<b>"all"</b>" users on the device."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"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="1394549691152728409">"Replace this app with the factory version? All data will be removed."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Running uninstalls"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Failed uninstalls"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Uninstalling…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Uninstalling <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Uninstall finished"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Uninstalled <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Uninstall unsuccessful."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Uninstalling <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> unsuccessful."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Can\'t uninstall active device admin app"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Can\'t uninstall active device admin app for <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"This app is required for some users or profiles and was uninstalled for others"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"This app is needed for your profile and can\'t be uninstalled."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"This app is required by your device administrator and can\'t be uninstalled."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Manage device admin apps"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Manage users"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be uninstalled."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"There was a problem while parsing the package."</string>
+    <string name="newPerms" msgid="6039428254474104210">"New"</string>
+    <string name="allPerms" msgid="1024385515840703981">"All"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privacy"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Device Access"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"This update requires no new permissions."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Deny"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"More info"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Deny anyway"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> of <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Always allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Only while using app"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Always"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Deny and don’t ask again"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> disabled"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"all disabled"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"none disabled"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Allow"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Apps"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"App permissions"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Don\'t ask again"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"No permissions"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Additional permissions"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Open app info"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> more</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> more</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"This app was designed for an older version of Android. Denying permission may cause it to no longer function as intended."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"perform an unknown action"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> of <xliff:g id="COUNT_1">%2$d</xliff:g> apps allowed"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Show system"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Hide system"</string>
+    <string name="no_apps" msgid="1965493419005012569">"No apps"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Location Settings"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> is a provider of location services for this device. Location access can be modified from location settings."</string>
+    <string name="system_warning" msgid="7103819124542305179">"If you deny this permission, basic features of your device may no longer function as intended."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Enforced by policy"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Background access disabled by policy"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Background access enabled by policy"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Foreground access enabled by policy"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Controlled by admin"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Always"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Only while using app"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Never"</string>
+    <string name="loading" msgid="7811651799620593731">"Loading…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"All permissions"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Other app capabilities"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Permission request"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Screen overlay detected"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"To change this permission setting, you first have to turn off the screen overlay from Settings &gt; Apps"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Open settings"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Install/Uninstall actions not supported on Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Choose what to allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; has been updated. Choose what to allow this app to access."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Cancel"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continue"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"New permissions"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Current permissions"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Staging app…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Unknown"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"For your security, your tablet is not allowed to install unknown apps from this source."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"For your security, your TV is not allowed to install unknown apps from this source."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"For your security, your phone is not allowed to install unknown apps from this source."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Your phone and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your phone or loss of data that may result from its use."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Your tablet and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your tablet or loss of data that may result from its use."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Your TV and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your TV or loss of data that may result from its use."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continue"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Settings"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Installing/uninstalling Wear apps"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rGB-television/strings.xml b/packages/PackageInstaller/res/values-en-rGB-television/strings.xml
new file mode 100644
index 0000000..663e1d4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rGB-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Deny and don\'t ask again"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"You can change this later in Settings &gt; Apps"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Show system apps"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"App permissions"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"App permissions"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> permissions"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Additional permissions"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> permissions"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rGB-watch/strings.xml b/packages/PackageInstaller/res/values-en-rGB-watch/strings.xml
new file mode 100644
index 0000000..e0d0edb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rGB-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Deny, don\'t ask again"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Show system apps"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Can\'t be changed"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Yes"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Cancel"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rGB/strings.xml b/packages/PackageInstaller/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..ded57bb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rGB/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Package installer"</string>
+    <string name="next" msgid="3057143178373252333">"Next"</string>
+    <string name="install" msgid="5896438203900042068">"Install"</string>
+    <string name="done" msgid="3889387558374211719">"Done"</string>
+    <string name="cancel" msgid="8360346460165114585">"Cancel"</string>
+    <string name="installing" msgid="8613631001631998372">"Installing…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Installing <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"App installed."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Do you want to install this application? It will get access to:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Do you want to install this application? It does not require any special access."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Do you want to install an update to this existing application? Your existing data will not be lost. The updated application will get access to:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Do you want to install an update to this built-in application? Your existing data will not be lost. The updated application will get access to:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Do you want to install an update to this existing application? Your existing data will not be lost. It does not require any special access."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Do you want to install an update to this built-in application? Your existing data will not be lost. It does not require any special access."</string>
+    <string name="install_failed" msgid="6579998651498970899">"App not installed."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"The package was blocked from being installed."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"App not installed as package conflicts with an existing package."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"App not installed as app isn\'t compatible with your tablet."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"This app isn\'t compatible with your TV."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"App not installed as app isn\'t compatible with your phone."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"App not installed as package appears to be invalid."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed on your tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed on your TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed on your phone."</string>
+    <string name="launch" msgid="4826921505917605463">"Open"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Your admin doesn\'t allow installation of apps obtained from unknown sources"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Unknown apps can\'t be installed by this user"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"This user is not allowed to install apps"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Manage apps"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Out of space"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed. Free up some space and try again."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"App not found"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"The app wasn\'t found in the list of installed apps."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Not allowed"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"The current user is not allowed to perform this uninstallation."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Error"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"App could not be uninstalled."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Uninstall app"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Uninstall update"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> is part of the following app:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Do you want to uninstall this app?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Do you want to uninstall this app for "<b>"all"</b>" users? The application and its data will be removed from "<b>"all"</b>" users on the device."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"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="1394549691152728409">"Replace this app with the factory version? All data will be removed."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Running uninstalls"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Failed uninstalls"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Uninstalling…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Uninstalling <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Uninstall finished"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Uninstalled <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Uninstall unsuccessful."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Uninstalling <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> unsuccessful."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Can\'t uninstall active device admin app"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Can\'t uninstall active device admin app for <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"This app is required for some users or profiles and was uninstalled for others"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"This app is needed for your profile and can\'t be uninstalled."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"This app is required by your device administrator and can\'t be uninstalled."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Manage device admin apps"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Manage users"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be uninstalled."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"There was a problem while parsing the package."</string>
+    <string name="newPerms" msgid="6039428254474104210">"New"</string>
+    <string name="allPerms" msgid="1024385515840703981">"All"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privacy"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Device Access"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"This update requires no new permissions."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Deny"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"More info"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Deny anyway"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> of <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Always allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Only while using app"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Always"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Deny and don’t ask again"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> disabled"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"all disabled"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"none disabled"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Allow"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Apps"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"App permissions"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Don\'t ask again"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"No permissions"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Additional permissions"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Open app info"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> more</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> more</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"This app was designed for an older version of Android. Denying permission may cause it to no longer function as intended."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"perform an unknown action"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> of <xliff:g id="COUNT_1">%2$d</xliff:g> apps allowed"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Show system"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Hide system"</string>
+    <string name="no_apps" msgid="1965493419005012569">"No apps"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Location Settings"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> is a provider of location services for this device. Location access can be modified from location settings."</string>
+    <string name="system_warning" msgid="7103819124542305179">"If you deny this permission, basic features of your device may no longer function as intended."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Enforced by policy"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Background access disabled by policy"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Background access enabled by policy"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Foreground access enabled by policy"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Controlled by admin"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Always"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Only while using app"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Never"</string>
+    <string name="loading" msgid="7811651799620593731">"Loading…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"All permissions"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Other app capabilities"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Permission request"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Screen overlay detected"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"To change this permission setting, you first have to turn off the screen overlay from Settings &gt; Apps"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Open settings"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Install/Uninstall actions not supported on Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Choose what to allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; has been updated. Choose what to allow this app to access."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Cancel"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continue"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"New permissions"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Current permissions"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Staging app…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Unknown"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"For your security, your tablet is not allowed to install unknown apps from this source."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"For your security, your TV is not allowed to install unknown apps from this source."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"For your security, your phone is not allowed to install unknown apps from this source."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Your phone and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your phone or loss of data that may result from its use."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Your tablet and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your tablet or loss of data that may result from its use."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Your TV and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your TV or loss of data that may result from its use."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continue"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Settings"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Installing/uninstalling Wear apps"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rIN-television/strings.xml b/packages/PackageInstaller/res/values-en-rIN-television/strings.xml
new file mode 100644
index 0000000..663e1d4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rIN-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Deny and don\'t ask again"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"You can change this later in Settings &gt; Apps"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Show system apps"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"App permissions"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"App permissions"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> permissions"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Additional permissions"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> permissions"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rIN-watch/strings.xml b/packages/PackageInstaller/res/values-en-rIN-watch/strings.xml
new file mode 100644
index 0000000..e0d0edb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rIN-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Deny, don\'t ask again"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Show system apps"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Can\'t be changed"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Yes"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Cancel"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rIN/strings.xml b/packages/PackageInstaller/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..ded57bb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rIN/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Package installer"</string>
+    <string name="next" msgid="3057143178373252333">"Next"</string>
+    <string name="install" msgid="5896438203900042068">"Install"</string>
+    <string name="done" msgid="3889387558374211719">"Done"</string>
+    <string name="cancel" msgid="8360346460165114585">"Cancel"</string>
+    <string name="installing" msgid="8613631001631998372">"Installing…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Installing <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"App installed."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Do you want to install this application? It will get access to:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Do you want to install this application? It does not require any special access."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Do you want to install an update to this existing application? Your existing data will not be lost. The updated application will get access to:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Do you want to install an update to this built-in application? Your existing data will not be lost. The updated application will get access to:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Do you want to install an update to this existing application? Your existing data will not be lost. It does not require any special access."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Do you want to install an update to this built-in application? Your existing data will not be lost. It does not require any special access."</string>
+    <string name="install_failed" msgid="6579998651498970899">"App not installed."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"The package was blocked from being installed."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"App not installed as package conflicts with an existing package."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"App not installed as app isn\'t compatible with your tablet."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"This app isn\'t compatible with your TV."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"App not installed as app isn\'t compatible with your phone."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"App not installed as package appears to be invalid."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed on your tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed on your TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed on your phone."</string>
+    <string name="launch" msgid="4826921505917605463">"Open"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Your admin doesn\'t allow installation of apps obtained from unknown sources"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Unknown apps can\'t be installed by this user"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"This user is not allowed to install apps"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Manage apps"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Out of space"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be installed. Free up some space and try again."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"App not found"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"The app wasn\'t found in the list of installed apps."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Not allowed"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"The current user is not allowed to perform this uninstallation."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Error"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"App could not be uninstalled."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Uninstall app"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Uninstall update"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> is part of the following app:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Do you want to uninstall this app?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Do you want to uninstall this app for "<b>"all"</b>" users? The application and its data will be removed from "<b>"all"</b>" users on the device."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"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="1394549691152728409">"Replace this app with the factory version? All data will be removed."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Running uninstalls"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Failed uninstalls"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Uninstalling…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Uninstalling <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Uninstall finished"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Uninstalled <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Uninstall unsuccessful."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Uninstalling <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> unsuccessful."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Can\'t uninstall active device admin app"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Can\'t uninstall active device admin app for <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"This app is required for some users or profiles and was uninstalled for others"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"This app is needed for your profile and can\'t be uninstalled."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"This app is required by your device administrator and can\'t be uninstalled."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Manage device admin apps"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Manage users"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> couldn\'t be uninstalled."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"There was a problem while parsing the package."</string>
+    <string name="newPerms" msgid="6039428254474104210">"New"</string>
+    <string name="allPerms" msgid="1024385515840703981">"All"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privacy"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Device Access"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"This update requires no new permissions."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Deny"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"More info"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Deny anyway"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> of <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Always allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Only while using app"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Always"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Deny and don’t ask again"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> disabled"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"all disabled"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"none disabled"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Allow"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Apps"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"App permissions"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Don\'t ask again"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"No permissions"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Additional permissions"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Open app info"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> more</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> more</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"This app was designed for an older version of Android. Denying permission may cause it to no longer function as intended."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"perform an unknown action"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> of <xliff:g id="COUNT_1">%2$d</xliff:g> apps allowed"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Show system"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Hide system"</string>
+    <string name="no_apps" msgid="1965493419005012569">"No apps"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Location Settings"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> is a provider of location services for this device. Location access can be modified from location settings."</string>
+    <string name="system_warning" msgid="7103819124542305179">"If you deny this permission, basic features of your device may no longer function as intended."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Enforced by policy"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Background access disabled by policy"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Background access enabled by policy"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Foreground access enabled by policy"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Controlled by admin"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Always"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Only while using app"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Never"</string>
+    <string name="loading" msgid="7811651799620593731">"Loading…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"All permissions"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Other app capabilities"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Permission request"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Screen overlay detected"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"To change this permission setting, you first have to turn off the screen overlay from Settings &gt; Apps"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Open settings"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Install/Uninstall actions not supported on Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Choose what to allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; has been updated. Choose what to allow this app to access."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Cancel"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continue"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"New permissions"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Current permissions"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Staging app…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Unknown"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"For your security, your tablet is not allowed to install unknown apps from this source."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"For your security, your TV is not allowed to install unknown apps from this source."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"For your security, your phone is not allowed to install unknown apps from this source."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Your phone and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your phone or loss of data that may result from its use."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Your tablet and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your tablet or loss of data that may result from its use."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Your TV and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your TV or loss of data that may result from its use."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continue"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Settings"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Installing/uninstalling Wear apps"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rXC-television/strings.xml b/packages/PackageInstaller/res/values-en-rXC-television/strings.xml
new file mode 100644
index 0000000..0f9eeb1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rXC-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‎Deny and don\'t ask again‎‏‎‎‏‎"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‏‏‏‎‎‎‎You can change this later in Settings &gt; Apps‎‏‎‎‏‎"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‎‎‏‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>‎‏‎‎‏‏‏‎ / ‎‏‎‎‏‏‎<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‎‏‎‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‏‎‏‎Show system apps‎‏‎‎‏‎"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‎‎‏‎App permissions‎‏‎‎‏‎"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‏‏‎‏‏‎‎‎‏‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‎‎‏‎‎‎‎‏‏‎‎‎App permissions‎‏‎‎‏‎"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‏‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‏‎‎‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="PERMISSION">%1$s</xliff:g>‎‏‎‎‏‏‏‎ permissions‎‏‎‎‏‎"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‎‏‎‎Additional permissions‎‏‎‎‏‎"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="PERMISSION">%1$s</xliff:g>‎‏‎‎‏‏‏‎ permissions‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rXC-watch/strings.xml b/packages/PackageInstaller/res/values-en-rXC-watch/strings.xml
new file mode 100644
index 0000000..89b2ea2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rXC-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎Deny, don\'t ask again‎‏‎‎‏‎"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>‎‏‎‎‏‏‏‎ / ‎‏‎‎‏‏‎<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‎‏‎‏‏‏‎‎‎‏‎‏‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎Show system apps‎‏‎‎‏‎"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‎‏‏‎‎‏‎‎‎‎Can\'t be changed‎‏‎‎‏‎"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‎‏‎‎‏‎Yes‎‏‎‎‏‎"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‎‎Cancel‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-en-rXC/strings.xml b/packages/PackageInstaller/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..ca392cc
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rXC/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‎‎‎‏‏‏‎‏‏‎‎‏‎‎Package installer‎‏‎‎‏‎"</string>
+    <string name="next" msgid="3057143178373252333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‏‎‏‏‎‏‎Next‎‏‎‎‏‎"</string>
+    <string name="install" msgid="5896438203900042068">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‏‎‎‎Install‎‏‎‎‏‎"</string>
+    <string name="done" msgid="3889387558374211719">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‏‎‎‎‏‎‏‎‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‏‏‏‎Done‎‏‎‎‏‎"</string>
+    <string name="cancel" msgid="8360346460165114585">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‏‎‏‏‎‎‏‎Cancel‎‏‎‎‏‎"</string>
+    <string name="installing" msgid="8613631001631998372">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‏‎‎‎Installing…‎‏‎‎‏‎"</string>
+    <string name="installing_app" msgid="4097935682329028894">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‏‎‎‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎‏‏‏‏‎‎Installing ‎‏‎‎‏‏‎<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎…‎‏‎‎‏‎"</string>
+    <string name="install_done" msgid="3682715442154357097">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎App installed.‎‏‎‎‏‎"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‎‎‎‎‏‏‎‎‎‎‎‎‎‏‏‏‎‎‎‏‏‎‎‏‎‎Do you want to install this application? It will get access to:‎‏‎‎‏‎"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‎‎‏‎‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‎‎‎‏‎‏‎‏‎‏‏‎‏‏‎‎‎‎‎Do you want to install this application? It does not require any special access.‎‏‎‎‏‎"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‏‎‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‎‏‎‎‎‏‎‏‏‏‎‎‎Do you want to install an update to this existing application? Your existing data will not be lost. The updated application will get access to:‎‏‎‎‏‎"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎‏‎‎‎‎‎Do you want to install an update to this built-in application? Your existing data will not be lost. The updated application will get access to:‎‏‎‎‏‎"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‎‎‎‎Do you want to install an update to this existing application? Your existing data will not be lost. It does not require any special access.‎‏‎‎‏‎"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎‏‏‎‎Do you want to install an update to this built-in application? Your existing data will not be lost. It does not require any special access.‎‏‎‎‏‎"</string>
+    <string name="install_failed" msgid="6579998651498970899">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎App not installed.‎‏‎‎‏‎"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎The package was blocked from being installed.‎‏‎‎‏‎"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‎‎‏‏‎‏‎‎‏‎‏‎‏‎‎App not installed as package conflicts with an existing package.‎‏‎‎‏‎"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎App not installed as app isn\'t compatible with your tablet.‎‏‎‎‏‎"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‎‎‎‎‎‎‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‏‏‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‏‏‎‎‏‏‎‎‏‎This app isn\'t compatible with your TV.‎‏‎‎‏‎"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎App not installed as app isn\'t compatible with your phone.‎‏‎‎‏‎"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎‎‏‎‏‏‎‎App not installed as package appears to be invalid.‎‏‎‎‏‎"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‎‎‏‎‎‏‏‎‏‎‏‎‏‎‎‏‏‎‏‎‎‏‏‎‏‎‏‏‏‏‎‏‏‎‎‎‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ couldn\'t be installed on your tablet.‎‏‎‎‏‎"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‏‏‎‎‎‎‎‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‎‎‎‎‏‏‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ couldn\'t be installed on your TV.‎‏‎‎‏‎"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ couldn\'t be installed on your phone.‎‏‎‎‏‎"</string>
+    <string name="launch" msgid="4826921505917605463">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‏‎‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‏‎Open‎‏‎‎‏‎"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‎‎‎‏‎‏‎‎‏‏‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‏‎‏‏‎‎Your admin doesn\'t allow installation of apps obtained from unknown sources‎‏‎‎‏‎"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‎‏‎‏‎‎‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎Unknown apps can\'t be installed by this user‎‏‎‎‏‎"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‎‎This user is not allowed to install apps‎‏‎‎‏‎"</string>
+    <string name="ok" msgid="3468756155452870475">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‎‏‏‎OK‎‏‎‎‏‎"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‎‎‎‏‏‎‏‎‎‏‏‏‏‎‏‏‎‎‏‎‎‏‏‏‎‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎Manage apps‎‏‎‎‏‎"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎Out of space‎‏‎‎‏‎"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‎‎‏‏‏‎‏‏‏‎‏‎‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ couldn\'t be installed. Free up some space and try again.‎‏‎‎‏‎"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‏‎‏‏‎‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‎App not found‎‏‎‎‏‎"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‎‎‏‏‏‎‎‎‎‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‏‏‏‎‎‏‎‎‏‏‏‏‎‏‎‎The app wasn\'t found in the list of installed apps.‎‏‎‎‏‎"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎‎‏‏‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‏‎‏‏‏‎‎Not allowed‎‏‎‎‏‎"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‎‎‎‏‎‎‏‎‏‏‎‏‏‎‎‏‎The current user is not allowed to perform this uninstallation.‎‏‎‎‏‎"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‏‏‏‎‏‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‎‎‏‎Error‎‏‎‎‏‎"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‏‎‎‎‎‏‎‎App could not be uninstalled.‎‏‎‎‏‎"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‎‏‏‎‎‏‎‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎Uninstall app‎‏‎‎‏‎"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‎‎‎‎‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‏‎‏‏‏‏‎‎Uninstall update‎‏‎‎‏‎"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‎‏‎‎‏‎‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is part of the following app:‎‏‎‎‏‎"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‏‎‏‎‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‎‏‎‏‏‎‎Do you want to uninstall this app?‎‏‎‎‏‎"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‎‏‏‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎Do you want to uninstall this app for ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎all‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎ users? The application and its data will be removed from ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎all‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎ users on the device.‎‏‎‎‏‎"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‏‏‎‎‏‎‎‎‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‎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="1394549691152728409">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‏‎‏‎‎‏‏‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‎‏‎‏‏‎‎‏‎Replace this app with the factory version? All data will be removed.‎‏‎‎‏‎"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‎‎‏‎‎‏‎‏‎‏‎‎‏‏‏‎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="uninstalling_notification_channel" msgid="5698369661583525583">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‏‎‎‏‎‏‎‏‏‎‏‎‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‎‎‏‏‏‏‎Running uninstalls‎‏‎‎‏‎"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎Failed uninstalls‎‏‎‎‏‎"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‎‎Uninstalling…‎‏‎‎‏‎"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‎‏‏‏‏‏‏‏‎‎Uninstalling ‎‏‎‎‏‏‎<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎…‎‏‎‎‏‎"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‎‎‎‎‎‎‏‎‎‎‎‎‎Uninstall finished.‎‏‎‎‏‎"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‎‎‎‏‎‏‎‏‎‎‎‏‏‎‏‏‎‎‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‎‏‏‏‏‏‎Uninstalled ‎‏‎‎‏‏‎<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‎‎‎‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‎‎‎Uninstall unsuccessful.‎‏‎‎‏‎"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‎Uninstalling ‎‏‎‎‏‏‎<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ unsuccessful.‎‏‎‎‏‎"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‏‎‎Can\'t uninstall active device admin app‎‏‎‎‏‎"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‏‎‎‏‏‏‏‎‎‎Can\'t uninstall active device admin app for ‎‏‎‎‏‏‎<xliff:g id="USERNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‎‎This app is required for some users or profiles and was uninstalled for others‎‏‎‎‏‎"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎This app is needed for your profile and can\'t be uninstalled.‎‏‎‎‏‎"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎‎‎‏‏‏‎This app is required by your device administrator and can\'t be uninstalled.‎‏‎‎‏‎"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‎Manage device admin apps‎‏‎‎‏‎"</string>
+    <string name="manage_users" msgid="3125018886835668847">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎Manage users‎‏‎‎‏‎"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‎‏‏‎‎‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ couldn\'t be uninstalled.‎‏‎‎‏‎"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‎‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‎‏‏‎There was a problem parsing the package.‎‏‎‎‏‎"</string>
+    <string name="newPerms" msgid="6039428254474104210">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‎‏‎‎New‎‏‎‎‏‎"</string>
+    <string name="allPerms" msgid="1024385515840703981">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‎‏‏‎‏‎All‎‏‎‎‏‎"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‎Privacy‎‏‎‎‏‎"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎‎‎Device Access‎‏‎‎‏‎"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‏‏‎This update requires no new permissions.‎‏‎‎‏‎"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‎‏‎‎‏‎‎‎‎‏‎‎‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‎‎Deny‎‏‎‎‏‎"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎More info‎‏‎‎‏‎"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‏‎‏‎‏‏‏‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‏‎‎‎‎‏‎‎Deny anyway‎‏‎‎‏‎"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‏‎‎‎‏‎‎‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>‎‏‎‎‏‏‏‎ of ‎‏‎‎‏‏‎<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‏‏‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to ‎‏‎‎‏‏‎<xliff:g id="ACTION">%2$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‏‎‎‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‎‎‎‏‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎Always allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to ‎‏‎‎‏‏‎<xliff:g id="ACTION">%2$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‏‎‎‏‏‏‎‏‏‏‎‎‎‎‏‏‎‏‏‎‎Only while using app‎‏‎‎‏‎"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‏‎‎‏‏‎‏‏‏‏‏‎Always‎‏‎‎‏‎"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‏‏‎‎‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎Deny and don’t ask again‎‏‎‎‏‎"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="COUNT">%1$d</xliff:g>‎‏‎‎‏‏‏‎ disabled‎‏‎‎‏‎"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‏‏‎‏‏‎‎‏‏‎‎‎‏‏‎‎‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎‎‎‏‎‏‎‏‏‏‏‏‎‎all disabled‎‏‎‎‏‎"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‎‏‎‎‏‏‎‏‏‎‏‎‏‏‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‏‏‏‎‎none disabled‎‏‎‎‏‎"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‏‏‎Allow‎‏‎‎‏‎"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‏‏‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎Apps‎‏‎‎‏‎"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‎‏‏‎‏‎‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎‏‎‎App permissions‎‏‎‎‏‎"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‏‎‎‏‏‏‏‎Don\'t ask again‎‏‎‎‏‎"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‎‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‏‎‏‏‎‎No permissions‎‏‎‎‏‎"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‎‏‏‎Additional permissions‎‏‎‎‏‎"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎Open app info‎‏‎‎‏‎"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="COUNT_1">%1$d</xliff:g>‎‏‎‎‏‏‏‎ more‎‏‎‎‏‎</item>
+      <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="COUNT_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ more‎‏‎‎‏‎</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‏‏‏‎This app was designed for an older version of Android. Denying permission may cause it to no longer function as intended.‎‏‎‎‏‎"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‎‎‎perform an unknown action‎‏‎‎‏‎"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="COUNT_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ of ‎‏‎‎‏‏‎<xliff:g id="COUNT_1">%2$d</xliff:g>‎‏‎‎‏‏‏‎ apps allowed‎‏‎‎‏‎"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‎‏‎‎‏‎‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‏‎‎‏‎‏‎‏‏‎‎‏‎‎‎‏‎‎‏‎‎‎‏‏‎‎‏‎Show system‎‏‎‎‏‎"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‎Hide system‎‏‎‎‏‎"</string>
+    <string name="no_apps" msgid="1965493419005012569">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‏‎‎‏‎No apps‎‏‎‎‏‎"</string>
+    <string name="location_settings" msgid="1774875730854491297">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‎‎‎‎‏‎‎‏‎‏‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‎‎‎‎‏‎Location Settings‎‏‎‎‏‎"</string>
+    <string name="location_warning" msgid="8778701356292735971">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‎‏‎‎‎‏‏‎‎‎‎‎‏‏‏‏‏‎‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is a provider of location services for this device. Location access can be modified from location settings.‎‏‎‎‏‎"</string>
+    <string name="system_warning" msgid="7103819124542305179">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‎‎‏‏‎‏‏‎If you deny this permission, basic features of your device may no longer function as intended.‎‏‎‎‏‎"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‎‎‎‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‏‎‏‏‏‎‏‎Enforced by policy‎‏‎‎‏‎"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‏‎‎‏‏‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎‏‎‏‎‎‏‏‎‏‎‎‏‎Background access disabled by policy‎‏‎‎‏‎"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‏‏‏‏‎‎Background access enabled by policy‎‏‎‎‏‎"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‏‏‎‎‎‏‏‎‎‏‎‎‏‎‏‎‏‎‎‏‎‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‎‎‎Foreground access enabled by policy‎‏‎‎‏‎"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‎Controlled by admin‎‏‎‎‏‎"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‎‎‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎Always‎‏‎‎‏‎"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‎‏‏‏‏‎‎‏‎‎‏‎Only while using app‎‏‎‎‏‎"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‏‏‏‎‎‎‎‏‏‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎Never‎‏‎‎‏‎"</string>
+    <string name="loading" msgid="7811651799620593731">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‏‎‎‎‎‏‏‎Loading…‎‏‎‎‏‎"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‏‎‏‏‎‎‏‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‏‎‎‏‎‎All permissions‎‏‎‎‏‎"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‏‎Other app capabilities‎‏‎‎‏‎"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‎‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‎‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‏‏‎Permission request‎‏‎‎‏‎"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‎‎‎‎‎‏‎Screen overlay detected‎‏‎‎‏‎"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‎‏‏‎To change this permission setting, you first have to turn off the screen overlay from Settings &gt; Apps‎‏‎‎‏‎"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‏‎‎‏‎‎‏‏‏‏‎Open settings‎‏‎‎‏‎"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‎‏‎Android Wear‎‏‎‎‏‎"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‏‎‎‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‏‏‎‎‎‎Install/Uninstall actions not supported on Wear.‎‏‎‎‏‎"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‏‎‏‏‏‎Choose what to allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access‎‏‎‎‏‎"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎&lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; has been updated. Choose what to allow this app to access.‎‏‎‎‏‎"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‏‏‎‎‏‏‏‎‏‏‏‎‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎Cancel‎‏‎‎‏‎"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‏‎‏‎‏‎‎Continue‎‏‎‎‏‎"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‎New permissions‎‏‎‎‏‎"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‏‏‎‏‏‎‏‎‎‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‏‎‏‎‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‎‎‏‏‏‎‎Current permissions‎‏‎‎‏‎"</string>
+    <string name="message_staging" msgid="6151794817691100003">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‏‎‏‏‎‎‎‎‎‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‏‎Staging app…‎‏‎‎‏‎"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‎‏‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‏‎Unknown‎‏‎‎‏‎"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‎‏‎‏‎‏‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‎For your security, your tablet is not allowed to install unknown apps from this source.‎‏‎‎‏‎"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‏‎‏‎‏‏‏‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎For your security, your TV is not allowed to install unknown apps from this source.‎‏‎‎‏‎"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‏‎‏‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‎For your security, your phone is not allowed to install unknown apps from this source.‎‏‎‎‏‎"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎Your phone and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your phone or loss of data that may result from its use.‎‏‎‎‏‎"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‏‎‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‎‏‎‏‏‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‎Your tablet and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your tablet or loss of data that may result from its use.‎‏‎‎‏‎"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‎Your TV and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your TV or loss of data that may result from its use.‎‏‎‎‏‎"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‏‎‏‏‏‏‎‎‏‎‎‏‎‎‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎‏‎‏‎‎‎‎‏‎‎‎Continue‎‏‎‎‏‎"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‎‎‎Settings‎‏‎‎‏‎"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎Installing/uninstalling wear apps‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-es-rUS-television/strings.xml b/packages/PackageInstaller/res/values-es-rUS-television/strings.xml
new file mode 100644
index 0000000..e664dc2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-es-rUS-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Denegar el permiso y no volver a preguntar"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Puedes cambiar esta opción más tarde en Configuración &gt; Aplicaciones"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Mostrar apps del sistema"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Permisos de apps"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Permisos de apps"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Permisos de <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Permisos adicionales"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Permisos de <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-es-rUS-watch/strings.xml b/packages/PackageInstaller/res/values-es-rUS-watch/strings.xml
new file mode 100644
index 0000000..3880892
--- /dev/null
+++ b/packages/PackageInstaller/res/values-es-rUS-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Denegar y no preguntar más"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Mostrar apps del sistema"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Inalterable"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Sí"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Cancelar"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-es-rUS/strings.xml b/packages/PackageInstaller/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..178f551
--- /dev/null
+++ b/packages/PackageInstaller/res/values-es-rUS/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Programa de instalación del paquete"</string>
+    <string name="next" msgid="3057143178373252333">"Siguiente"</string>
+    <string name="install" msgid="5896438203900042068">"Instalar"</string>
+    <string name="done" msgid="3889387558374211719">"Finalizado"</string>
+    <string name="cancel" msgid="8360346460165114585">"Cancelar"</string>
+    <string name="installing" msgid="8613631001631998372">"Instalando…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Instalando <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Se instaló la aplicación."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"¿Deseas instalar la aplicación? Esta tendrá acceso a lo siguiente:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"¿Deseas instalar esta aplicación? No requiere accesos especiales."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"¿Deseas instalar una actualización para esta aplicación? Tus datos no se perderán. La aplicación actualizada tendrá acceso a lo siguiente:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"¿Deseas instalar una actualización para esta aplicación integrada? Tus datos no se perderán. La aplicación actualizada tendrá acceso a lo siguiente:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"¿Quieres instalar una actualización de esta aplicación existente? Los datos existentes no se perderán. No se requiere ningún acceso especial."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"¿Quieres instalar una actualización de esta aplicación integrada? Los datos existentes no se perderán. No se requiere ningún acceso especial."</string>
+    <string name="install_failed" msgid="6579998651498970899">"No se instaló la aplicación."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Se bloqueó el paquete para impedir la instalación."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"No se instaló la app ya que está en conflicto con un paquete existente."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"No se instaló la app porque no es compatible con tu tablet."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Esta app no es compatible con la TV."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"No se instaló la app porque no es compatible con tu teléfono."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"No se instaló la app porque parece que el paquete no es válido."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"No se pudo instalar <xliff:g id="APP_NAME">%1$s</xliff:g> en tu tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"No se pudo instalar <xliff:g id="APP_NAME">%1$s</xliff:g> en la TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"No se pudo instalar <xliff:g id="APP_NAME">%1$s</xliff:g> en tu dispositivo."</string>
+    <string name="launch" msgid="4826921505917605463">"Abrir"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Tu administrador no permite la instalación de apps que se obtuvieron de fuentes desconocidas"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Este usuario no puede instalar apps desconocidas"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Este usuario no puede instalar apps"</string>
+    <string name="ok" msgid="3468756155452870475">"Aceptar"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Administrar aplicaciones"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Sin espacio"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"No se pudo instalar <xliff:g id="APP_NAME">%1$s</xliff:g>. Libera espacio y vuelve a intentarlo."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"No se encontró la aplicación."</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"La aplicación no se encontró en la lista de aplicaciones instaladas."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"No tiene permiso"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"El usuario actual no tiene permiso para llevar a cabo esta desinstalación."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Error"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"No se pudo desinstalar la app."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Desinstalar la aplicación"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Desinstalar la actualización"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> es parte de la siguiente aplicación:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"¿Deseas desinstalar esta aplicación?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"¿Quieres desinstalar esta aplicación para "<b>"todos"</b>" los usuarios? La aplicación y sus datos se eliminarán de "<b>"todos"</b>" los usuarios del dispositivo."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"¿Deseas desinstalar esta aplicación para el usuario <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"¿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="2083665452990861991">"¿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 poseen perfiles de trabajo."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Desinstalaciones activas"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Desinstalaciones con errores"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Desinstalando…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Desinstalando <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"La desinstalación finalizó."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Se desinstaló <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Desinstalación incorrecta"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"No se pudo desinstalar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"No se puede desinstalar la app de administración activa del dispositivo"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"No se puede desinstalar la app de administración activa del dispositivo para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"App necesaria en algunos usuarios o perfiles, y desinstalada en otros"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Esta app es necesaria en tu perfil y no la puedes desinstalar."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"El admin. del dispositivo necesita esta aplicación y no se puede desinstalar."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Administrar apps del dispositivo del administrador"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Administrar usuarios"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"No se pudo desinstalar <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Se produjo un error durante el análisis del paquete."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nuevo"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Todo"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privacidad"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Acceso al dispositivo"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Esta actualización no requiere permisos nuevos."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Rechazar"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Más información"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Denegar de todos modos"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"¿Quieres que la app de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; realice la siguiente acción: <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pueda <xliff:g id="ACTION">%2$s</xliff:g> siempre?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Solo cuando se usa la app"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Siempre"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Denegar el permiso y no volver a preguntar"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> inhabilitados"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"todos inhabilitados"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ninguno inhabilitado"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Permitir"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplicaciones"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Permisos de apps"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"No volver a preguntar"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Sin permisos"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Permisos adicionales"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Abrir información de la app"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> más</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> más</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Esta aplicación se diseñó para una versión de Android anterior. Si deniegas el permiso, es posible que deje de funcionar de la forma prevista."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"realizar una acción desconocida"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Se otorgó el permiso a <xliff:g id="COUNT_0">%1$d</xliff:g> de <xliff:g id="COUNT_1">%2$d</xliff:g> aplicaciones."</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Mostrar sistema"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Ocultar sistema"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Ninguna aplicación"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Configuración de la ubicación"</string>
+    <string name="location_warning" msgid="8778701356292735971">"La aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> provee servicios de ubicación a este dispositivo. El acceso a la ubicación puede modificarse desde la configuración de la ubicación."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Si no concedes este permiso, es posible que algunas funciones básicas del dispositivo dejen de funcionar correctamente."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Se aplica en función de la política"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Acceso en segundo plano inhabilitado por la política"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Acceso en segundo plano habilitado por la política"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Acceso en primer plano habilitado por la política"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Controlado por el administrador"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Siempre"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Solo cuando se usa la app"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nunca"</string>
+    <string name="loading" msgid="7811651799620593731">"Cargando…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Todos los permisos"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Otras funciones de la aplicación"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Solicitud de permiso"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Se detectó una superposición de pantalla"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Para cambiar esta configuración de permisos, primero debes desactivar la superposición de pantalla en Configuración &gt; Aplicaciones"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Abrir configuración"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear no admite las acciones de instalación y desinstalación"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Selecciona los permisos de acceso para &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Se actualizó &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. Selecciona los permisos de acceso para esta app."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Cancelar"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continuar"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Permisos nuevos"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Permisos actuales"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Preparando app…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Desconocido"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Por tu seguridad, tu tablet no tiene permitido instalar apps desconocidas de esta fuente."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Por tu seguridad, tu TV no tiene permitido instalar apps desconocidas de esta fuente."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Por tu seguridad, tu teléfono no tiene permitido instalar apps desconocidas de esta fuente."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Tu teléfono y tus datos personales son más vulnerables a los ataques de apps desconocidas. Si instalas esta app, serás responsable de los daños que sufra tu teléfono y la pérdida de datos que pueda ocasionar su uso."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Tu tablet y tus datos personales son más vulnerables a los ataques de apps desconocidas. Si instalas esta app, serás responsable de los daños que sufra tu tablet y la pérdida de datos que pueda ocasionar su uso."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Tu TV y tus datos personales son más vulnerables a los ataques de apps desconocidas. Si instalas esta app, serás responsable de los daños que sufra tu TV y la pérdida de datos que pueda ocasionar su uso."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continuar"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Configuración"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Instalando/desinstalando apps para Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-es-television/strings.xml b/packages/PackageInstaller/res/values-es-television/strings.xml
new file mode 100644
index 0000000..6a12063
--- /dev/null
+++ b/packages/PackageInstaller/res/values-es-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Denegar y no volver a preguntar"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Puedes cambiar esta opción más tarde en Ajustes &gt; Aplicaciones."</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Mostrar aplicaciones del sistema"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Permisos de aplicaciones"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Permisos de aplicaciones"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Permisos: <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Permisos adicionales"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Permisos: <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-es-watch/strings.xml b/packages/PackageInstaller/res/values-es-watch/strings.xml
new file mode 100644
index 0000000..b3c4ff1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-es-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Denegar y no preguntar más"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Mostrar aplicaciones del sistema"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"No se puede cambiar"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Sí"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Cancelar"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-es/strings.xml b/packages/PackageInstaller/res/values-es/strings.xml
new file mode 100644
index 0000000..bffaa0f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-es/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Instalador de paquetes"</string>
+    <string name="next" msgid="3057143178373252333">"Siguiente"</string>
+    <string name="install" msgid="5896438203900042068">"Instalar"</string>
+    <string name="done" msgid="3889387558374211719">"Listo"</string>
+    <string name="cancel" msgid="8360346460165114585">"Cancelar"</string>
+    <string name="installing" msgid="8613631001631998372">"Instalando…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Instalando <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplicación instalada"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"¿Quieres instalar esta aplicación? Tendrá los siguientes permisos:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"¿Quieres instalar esta aplicación? No requiere accesos especiales."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"¿Quieres instalar una actualización de la aplicación? Tus datos no se perderán. La aplicación actualizada podrá acceder a:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"¿Quieres instalar una actualización de esta aplicación integrada? Tus datos no se perderán. La aplicación actualizada podrá acceder a:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"¿Quieres instalar una actualización de esta aplicación? Tus datos no se perderán. No requiere ningún acceso especial."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"¿Quieres instalar una actualización de esta aplicación integrada? Tus datos no se perderán. No requiere ningún acceso especial."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Aplicación no instalada"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Se ha bloqueado la instalación del paquete."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"La aplicación no se ha instalado debido a un conflicto con un paquete actual."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"La aplicación no se ha instalado porque no es compatible con tu tablet."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Esta aplicación no es compatible con tu TV."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"La aplicación no se ha instalado porque no es compatible con tu teléfono."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"La aplicación no se ha instalado porque parece que el paquete no es válido."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"No se ha podido instalar <xliff:g id="APP_NAME">%1$s</xliff:g> en el tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> no se ha podido instalar en tu TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"No se ha podido instalar <xliff:g id="APP_NAME">%1$s</xliff:g> en el teléfono."</string>
+    <string name="launch" msgid="4826921505917605463">"Abrir"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"El administrador no permite instalar aplicaciones de fuentes desconocidas"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Este usuario no puede instalar aplicaciones desconocidas"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Este usuario no tiene permiso para instalar aplicaciones"</string>
+    <string name="ok" msgid="3468756155452870475">"Aceptar"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Administrar aplicaciones"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Sin espacio"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"No se ha podido instalar la aplicación <xliff:g id="APP_NAME">%1$s</xliff:g>. Libera espacio y vuelve a intentarlo."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Aplicación no encontrada"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"No se ha encontrado la aplicación en la lista de aplicaciones instaladas."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"No permitido"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"El usuario actual no puede iniciar el proceso de desinstalación."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Error"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"No se ha podido desinstalar la aplicación."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Desinstalar aplicación"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Desinstalar actualización"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> forma parte de esta aplicación:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"¿Quieres desinstalar esta aplicación?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"¿Quieres desinstalar esta aplicación para "<b>"todos"</b>" los usuarios? La aplicación y sus datos se eliminarán de "<b>"todos"</b>" los usuarios del dispositivo."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"¿Quieres desinstalar esta aplicación para el usuario <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"¿Quieres sustituir esta aplicación con la versión de fábrica? Ten en cuenta que se eliminarán todos los datos."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"¿Quieres sustituir esta aplicación con la versión de fábrica? Ten en cuenta que se eliminarán todos los datos. Esto afecta a todos los usuarios del dispositivo, incluidos los que tienen perfiles de trabajo."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Desinstalaciones en curso"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Desinstalaciones fallidas"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Desinstalando..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Desinstalando <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Desinstalación completada"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Se ha desinstalado <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Desinstalación correcta"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"No se ha podido desinstalar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"No se puede desinstalar la aplicación de administración de dispositivos activa"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"No se ha podido desinstalar la aplicación de administración de dispositivos activa de <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Aplicación necesaria para algunos usuarios o perfiles y desinstalada en otros casos"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Esta aplicación es necesaria para tu perfil y no se puede desinstalar."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Esta aplicación es necesaria para el administrador de tu dispositivo y no se puede desinstalar."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Gestionar aplicaciones de admón. de dispositivos"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Administrar usuarios"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"No se ha podido desinstalar <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Se ha producido un error al analizar el paquete."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nuevo"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Todos"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privacidad"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Acceso al dispositivo"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Esta actualización no requiere permisos nuevos."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Denegar"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Más información"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Denegar de todos modos"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"¿Permitir a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"¿Quieres permitir siempre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Solo mientras se usa la aplicación"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Siempre"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Denegar y no volver a preguntar"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"Inhabilitados: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"todos inhabilitados"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ninguno inhabilitado"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Permitir"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplicaciones"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Permisos de aplicaciones"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"No volver a preguntar"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Sin permisos"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Permisos adicionales"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Abrir la información de la aplicación"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> más</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> más</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Esta aplicación está diseñada para una versión anterior de Android. Si se le deniega el permiso, puede dejar de funcionar de la forma prevista."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"realizar una acción desconocida"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> de <xliff:g id="COUNT_1">%2$d</xliff:g> aplicaciones permitidas"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Mostrar aplicaciones del sistema"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Ocultar aplicaciones del sistema"</string>
+    <string name="no_apps" msgid="1965493419005012569">"No hay aplicaciones"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Ajustes de ubicación"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> es un proveedor de servicios de ubicación de este dispositivo. El acceso a la ubicación se puede modificar en los ajustes de ubicación."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Si rechazas este permiso, es posible que funciones básicas de tu dispositivo dejen de funcionar correctamente."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Aplicado por política"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Acceso en segundo plano inhabilitado por política"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Acceso en segundo plano habilitado por política"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Acceso en primer plano habilitado por política"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Controlado por el administrador"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Siempre"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Solo mientras se usa la app"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nunca"</string>
+    <string name="loading" msgid="7811651799620593731">"Cargando..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Todos los permisos"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Otras funciones de la aplicación"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Solicitud de permiso"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Superposición de pantalla detectada"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Para cambiar la configuración de este permiso, desactiva la superposición de pantalla en Ajustes &gt; Aplicaciones"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Abrir ajustes"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Las acciones de instalar y desinstalar no pueden realizarse en Wear"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Elige los permisos de acceso que quieres conceder a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; se ha actualizado. Elige los permisos de acceso que quieres conceder a esta aplicación."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Cancelar"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continuar"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Permisos nuevos"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Permisos actuales"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Preparando aplicación…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Desconocido"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Por motivos de seguridad, tu tablet no puede instalar aplicaciones desconocidas de esta fuente."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Por motivos de seguridad, tu TV no puede instalar aplicaciones desconocidas de esta fuente."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Por motivos de seguridad, tu teléfono no puede instalar aplicaciones desconocidas de esta fuente."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Tu teléfono y tus datos personales son más vulnerables a los ataques de aplicaciones desconocidas. Al instalar esta aplicación, aceptas ser responsable de cualquier daño que sufra tu teléfono o la pérdida de datos que se pueda derivar de su uso."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Tu tablet y tus datos personales son más vulnerables a los ataques de aplicaciones desconocidas. Al instalar esta aplicación, aceptas ser responsable de cualquier daño que sufra tu tablet o la pérdida de datos que se pueda derivar de su uso."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Tu TV y tus datos personales son más vulnerables a los ataques de aplicaciones desconocidas. Al instalar esta aplicación, aceptas ser responsable de cualquier daño que sufra tu TV o la pérdida de datos que se pueda derivar de su uso."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continuar"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Ajustes"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Instalando/desinstalando apps para Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-et-television/strings.xml b/packages/PackageInstaller/res/values-et-television/strings.xml
new file mode 100644
index 0000000..0abb2de
--- /dev/null
+++ b/packages/PackageInstaller/res/values-et-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Keela ja ära enam küsi"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Saate seda hiljem muuta jaotises Seaded &gt; Rakendused"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Kuva süsteemirakendused"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Rakenduste load"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Rakenduste load"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> – load"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Lisaload"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> – load"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-et-watch/strings.xml b/packages/PackageInstaller/res/values-et-watch/strings.xml
new file mode 100644
index 0000000..328e215
--- /dev/null
+++ b/packages/PackageInstaller/res/values-et-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Keela, ära enam küsi"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Kuva süsteemirakendused"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Ei saa muuta"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Jah"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Tühista"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-et/strings.xml b/packages/PackageInstaller/res/values-et/strings.xml
new file mode 100644
index 0000000..1754411
--- /dev/null
+++ b/packages/PackageInstaller/res/values-et/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Paketiinstaller"</string>
+    <string name="next" msgid="3057143178373252333">"Järgmine"</string>
+    <string name="install" msgid="5896438203900042068">"Installi"</string>
+    <string name="done" msgid="3889387558374211719">"Valmis"</string>
+    <string name="cancel" msgid="8360346460165114585">"Tühista"</string>
+    <string name="installing" msgid="8613631001631998372">"Installimine ..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Paketi <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> installimine …"</string>
+    <string name="install_done" msgid="3682715442154357097">"Rakendus on installitud."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Kas soovite rakenduse installida? See pääseb järgmiste üksuste juurde:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Kas soovite rakenduse installida? See ei nõua spetsiaalseid juurdepääsuõigusi."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Kas soovite olemasoleva rakenduse värskenduse installida? Teie olemasolevad andmed jäävad alles. Värskendatud rakendus pääseb järgmiste funktsioonide juurde:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Kas soovite sisseehitatud rakenduse värskenduse installida? Teie olemasolevad andmed jäävad alles. Värskendatud rakendus pääseb järgmiste funktsioonide juurde:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Kas soovite installida olemasoleva rakenduse värskenduse? Olemasolevad andmed ei lähe kaduma. See ei nõua erijuurdepääsu."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Kas soovite installida sisseehitatud rakenduse värskenduse? Olemasolevad andmed ei lähe kaduma. See ei nõua erijuurdepääsu."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Rakendus pole installitud."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Paketi installimine blokeeriti."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Rakendust ei installitud, kuna pakett on olemasoleva paketiga vastuolus."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Rakendust ei installitud, kuna rakendus ei ühildu teie tahvelarvutiga."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Rakendus ei ühildu teie teleriga."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Rakendust ei installitud, kuna rakendus ei ühildu teie telefoniga."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Rakendust ei installitud, kuna pakett näib olevat sobimatu."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Rakendust <xliff:g id="APP_NAME">%1$s</xliff:g> ei saa teie tahvelarvutisse installida."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Rakendust <xliff:g id="APP_NAME">%1$s</xliff:g> ei saa teie telerisse installida."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Rakendust <xliff:g id="APP_NAME">%1$s</xliff:g> ei saa teie telefoni installida."</string>
+    <string name="launch" msgid="4826921505917605463">"Ava"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Administraator ei luba installida tundmatutest allikatest pärinevaid rakendusi"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"See kasutaja ei saa installida tundmatuid rakendusi"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Kasutajal ei ole lubatud rakendusi installida"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Rakenduste haldamine"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Pole ruumi"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Rakendust <xliff:g id="APP_NAME">%1$s</xliff:g> ei saa installida. Vabastage mälu ja proovige uuesti."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Rakendust ei leitud"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Rakendust ei leitud installitud rakenduste loendist."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Ei ole lubatud"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Praegusel kasutajal ei ole lubatud seda desinstallimist teha."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Viga"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Rakendust ei saanud desinstallida."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Rakenduse desinstallimine"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Värskenduse desinstallimine"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> on osa järgmisest rakendusest:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Kas soovite selle rakenduse desinstallida?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Kas soovite desinstallida selle rakenduse "<b>"kõikidelt"</b>" kasutajatelt? Rakendus ja selle andmed eemaldatakse "<b>"kõikidelt"</b>" seadme kasutajatelt."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Kas soovite kasutaja <xliff:g id="USERNAME">%1$s</xliff:g> puhul rakenduse desinstallida?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Kas asendada see rakendus tehaseversiooniga? Kõik andmed eemaldatakse."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Käimasolevad desinstallimised"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Ebaõnnestunud desinstallimised"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Desinstallimine ..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Üksuse <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> desinstallimine …"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Desinstallimine on lõpetatud."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Üksus <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> on desinstallitud"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Desinstallimine ebaõnnestus."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Üksuse <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> desinstallimine ebaõnnestus."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Aktiivset seadme administraatori rakendust ei saa desinstallida"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Kasutaja <xliff:g id="USERNAME">%1$s</xliff:g> puhul ei saa aktiivset seadme administraatori rakendust desinstallida"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Rakendus on mõne kasutaja ja profiili puhul vajalik, teiste puhul see desinstalliti"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"See rakendus on vajalik teie profiili jaoks ja seda ei saa desinstallida."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Seadme administraator vajab seda rakendust ja seda ei saa desinstallida."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Halda seadme administraatori rakendusi"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Halda kasutajaid"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Rakendust <xliff:g id="APP_NAME">%1$s</xliff:g> ei saanud desinstallida."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Probleem paketi sõelumisel."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Uus"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Kõik"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privaatsus"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Seadme juurdepääs"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"See värskendus ei nõua uusi lube."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Keela"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Lisateave"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Keela ikkagi"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>-st"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Kas lubada rakenduse puhul &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; alati toiming <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Ainult rakenduse kasutamise ajal"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Alati"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Keela ja ära enam küsi"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> on keelatud"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"kõik on keelatud"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"mitte ükski pole keelatud"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Luba"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Rakendused"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Rakenduste load"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Ära enam küsi"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Lube ei ole"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Täiendavad load"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Ava rakenduse teave"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">Veel <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Veel <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Rakendus on mõeldud Androidi vanemale versioonile. Kui keeldute loa andmisest, ei pruugi see ootuspäraselt töötada."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"tundmatu toiming"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> rakendust <xliff:g id="COUNT_1">%2$d</xliff:g>-st on lubatud"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Kuva süsteem"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Peida süsteem"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Rakendusi pole"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Asukohaseaded"</string>
+    <string name="location_warning" msgid="8778701356292735971">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> on selle seadme asukohateenuste pakkuja. Asukoha juurdepääsu saab muuta asukohaseadetes."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Kui keelate loa, ei pruugi seadme põhifunktsioonid enam ootuspäraselt töötada."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Eeskirjadega jõustatud"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Reegli alusel on taustale juurdepääs keelatud"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Reegli alusel on taustale juurdepääs lubatud"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Reegli alusel on esiplaanile juurdepääs lubatud"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Juhib administraator"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Alati"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Ainult rakenduse kasutamisel"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Mitte kunagi"</string>
+    <string name="loading" msgid="7811651799620593731">"Laadimine ..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Kõik load"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Rakenduse muud funktsioonid"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Loa taotlus"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Tuvastati ekraani ülekate"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Selle loa seade muutmiseks peate esmalt välja lülitama ekraani ülekatte menüüs Seaded &gt; Rakendused"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Ava seaded"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear ei toeta installimist/desinstallimist."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Valige, millele lubate rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurde pääseda"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Rakendust &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; värskendati. Valige, millele lubate sellel rakendusel juurde pääseda."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Tühista"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Jätka"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Uued load"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Praegused load"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Rakenduse ettevalmistamine …"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Tundmatu"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Teie turvalisuse huvides ei ole tahvelarvutil lubatud installida sellest allikast tundmatuid rakendusi."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Teie turvalisuse huvides ei ole TV-l lubatud installida sellest allikast tundmatuid rakendusi."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Teie turvalisuse huvides ei ole telefonil lubatud installida sellest allikast tundmatuid rakendusi."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Teie telefon ja isiklikud andmed on tundmatute rakenduste rünnakute suhtes haavatavamad. Selle rakenduse installimisel nõustute, et vastutate telefoni kahjude ja andmekao eest, mis võivad tuleneda selliste rakenduste kasutamisest."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Teie tahvelarvuti ja isiklikud andmed on tundmatute rakenduste rünnakute suhtes haavatavamad. Selle rakenduse installimisel nõustute, et vastutate tahvelarvuti kahjude ja andmekao eest, mis võivad tuleneda selliste rakenduste kasutamisest."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Teie teler ja isiklikud andmed on tundmatute rakenduste rünnakute suhtes haavatavamad. Selle rakenduse installimisel nõustute, et vastutate teleri kahjude ja andmekao eest, mis võivad tuleneda selliste rakenduste kasutamisest."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Jätka"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Seaded"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Weari rak. installimine/desinstallimine"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-eu-television/strings.xml b/packages/PackageInstaller/res/values-eu-television/strings.xml
new file mode 100644
index 0000000..78fffae
--- /dev/null
+++ b/packages/PackageInstaller/res/values-eu-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Ukatu eta ez galdetu berriro"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Hori geroago alda dezakezu Ezarpenak &gt; Aplikazioak atalean"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Erakutsi sistemaren aplikazioak"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Aplikazio-baimenak"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Aplikazio-baimenak"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> erabiltzeko baimenak"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Baimen gehigarriak"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> erabiltzeko baimenak"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-eu-watch/strings.xml b/packages/PackageInstaller/res/values-eu-watch/strings.xml
new file mode 100644
index 0000000..0f08cad
--- /dev/null
+++ b/packages/PackageInstaller/res/values-eu-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Ukatu; ez galdetu berriro"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Erakutsi sistemaren aplikazioak"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Ezin da aldatu"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Bai"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Utzi"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
new file mode 100644
index 0000000..0cb7e4c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Pakete-instalatzailea"</string>
+    <string name="next" msgid="3057143178373252333">"Hurrengoa"</string>
+    <string name="install" msgid="5896438203900042068">"Instalatu"</string>
+    <string name="done" msgid="3889387558374211719">"Eginda"</string>
+    <string name="cancel" msgid="8360346460165114585">"Utzi"</string>
+    <string name="installing" msgid="8613631001631998372">"Instalatzen…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> instalatzen…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplikazioa instalatu da."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Aplikazioa instalatu nahi duzu? Elementu hauetarako sarbidea izango du:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Aplikazioa instalatu nahi duzu? Ez du sarbide berezirik behar."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Aplikazioaren eguneratzea instalatu nahi duzu? Lehendik dauden datuak ez dira galduko. Eguneratutako aplikazioak elementu hauetarako sarbidea izango du:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Aplikazio integratu honen eguneratzea instalatu nahi duzu? Lehendik dauden datuak ez dira galduko. Eguneratutako aplikazioak elementu hauetarako sarbidea izango du:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Aplikazioaren eguneratzea instalatu nahi duzu? Lehendik dauden datuak ez dira galduko. Ez du sarbide berezirik behar."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Aplikazio integratu honen eguneratzea instalatu nahi duzu? Lehendik dauden datuak ez dira galduko. Ez du sarbide berezirik behar."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Ez da aplikazioa instalatu."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Blokeatu egin da paketea instalatzeko aukera."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Ez da instalatu aplikazioa, gatazka bat sortu delako lehendik dagoen pakete batekin."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Ez da instalatu aplikazioa, ez delako tabletarekin bateragarria."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Aplikazioa ez da telebistarekin bateragarria."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Ez da instalatu aplikazioa, ez delako telefonoarekin bateragarria."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Ez da instalatu aplikazioa, paketeak ez duelako balio."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Ezin izan da <xliff:g id="APP_NAME">%1$s</xliff:g> tabletan instalatu."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Ezin izan da instalatu <xliff:g id="APP_NAME">%1$s</xliff:g> telebistan."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Ezin izan da <xliff:g id="APP_NAME">%1$s</xliff:g> telefonoan instalatu."</string>
+    <string name="launch" msgid="4826921505917605463">"Ireki"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Administratzaileak ez du onartzen iturburu ezezagunetako aplikazioak instalatzea"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Erabiltzaile honek ezin ditu instalatu aplikazio ezezagunak"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Erabiltzaile honek ez du baimenik aplikazioak instalatzeko"</string>
+    <string name="ok" msgid="3468756155452870475">"Ados"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Kudeatu aplikazioak"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Ez dago behar adina toki"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Ezin izan da <xliff:g id="APP_NAME">%1$s</xliff:g> instalatu. Egin toki pixka bat eta saiatu berriro."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Ez da aplikazioa aurkitu"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Aplikazioa ez da aurkitu instalatutako aplikazioen zerrendan."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Ez dauka baimenik"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Erabiltzaile honek ez dauka desinstalatzeko baimenik."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Errorea"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Ezin izan da desinstalatu aplikazioa."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Desinstalatu aplikazioa"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Desinstalatu eguneratzea"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> aplikazio honen zati da:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Aplikazioa desinstalatu nahi duzu?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Aplikazioa erabiltzaile "<b>"guztientzat"</b>" desinstalatu nahi duzu? Aplikazioa eta bere datu guztiak gailuko erabiltzaile "<b>"guztiei"</b>" ezabatuko zaizkie."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"<xliff:g id="USERNAME">%1$s</xliff:g> erabiltzailearen aplikazioa desinstalatu nahi duzu?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Aplikazio hau jatorrizko bertsioarekin ordeztu nahi duzu? Datu guztiak ezabatuko dira."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Aplikazio hau jatorrizko bertsioarekin ordeztu nahi duzu? Datu guztiak ezabatuko dira. Gailuaren erabiltzaile guztiengan izango du eragina, laneko profilak dituztenengan barne."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Abian diren desinstalatze-eragiketak"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Huts egin duten desinstalatze-eragiketak"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Desinstalatzen…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> desinstalatzen…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Desinstalatu da."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Desinstalatu da <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Ezin izan da desinstalatu."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Ezin izan da desinstalatu <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Ezin da desinstalatu gailua administratzeko aplikazio aktiboa"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Ezin da desinstalatu <xliff:g id="USERNAME">%1$s</xliff:g> erabiltzailearen gailua administratzeko aplikazio aktiboa"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Erabiltzaile edo profil batzuek behar dute aplikazio hau, baina desinstalatu egin da beste guztientzat."</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Zure profilak behar du aplikazio hau eta ezin da desinstalatu."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Gailuaren administratzaileak aplikazio hori behar du eta ezin da desinstalatu."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Kudeatu gailua administratzeko aplikazioak"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Kudeatu erabiltzaileak"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Ezin izan da <xliff:g id="APP_NAME">%1$s</xliff:g> desinstalatu."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Arazo bat izan da paketea analizatzean."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Berriak"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Guztiak"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Pribatutasuna"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Gailurako sarbidea"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Eguneratze honek ez du baimen berririk behar."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Ukatu"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Informazio gehiago"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Ukatu hala ere"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari \"<xliff:g id="ACTION">%2$s</xliff:g>\" izeneko baimena eman nahi diozu?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Beti eman nahi diozu \"<xliff:g id="ACTION">%2$s</xliff:g>\" baimena &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Aplikazioa erabiltzean soilik"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Beti"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Baztertu eta ez galdetu berriro"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> desgaituta"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"guztiak desgaituta"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"guztiak gaituta"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Baimendu"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplikazioak"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Aplikazio-baimenak"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Ez galdetu berriro"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Ez dago baimenik"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Baimen gehigarriak"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Ireki aplikazioaren informazioa"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> gehiago</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> gehiago</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Aplikazio hau Android-en bertsio zaharrago baterako diseinatuta dago. Baimena ukatzen baduzu, agian aurrerantzean ez du behar bezain ondo funtzionatuko."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"Gauzatu ekintza ezezagunak"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g> aplikaziok dute baimena"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Erakutsi sistema"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Ezkutatu sistema"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Ez dago aplikaziorik"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Kokapen-ezarpenak"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> gailu honen kokapen-zerbitzuen hornitzailea da. Kokapenerako sarbidea kokapen-ezarpenetatik alda daiteke."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Baimena ematen ez baduzu, baliteke gailuaren oinarrizko eginbide batzuek behar bezala ez funtzionatzea."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Gidalerroen bidez aplikatzen da"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Gidalerro batek eskatuta, atzeko planoa atzitzeko aukera desgaitu da"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Gidalerro batek eskatuta, atzeko planoa atzitzeko aukera gaitu da"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Gidalerro batek eskatuta, aurreko planoa atzitzeko aukera gaitu da"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Administratzaileak kontrolatzen du"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Beti"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Aplikazioa erabiltzean soilik"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Inoiz ez"</string>
+    <string name="loading" msgid="7811651799620593731">"Kargatzen…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Baimen guztiak"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Aplikazioaren beste gaitasun batzuk"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Baimen-eskaera"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Pantailaren gainjartzea detektatu da"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Baimen-ezarpen hau aldatzeko, pantailaren gainjartzea desaktibatu behar duzu Ezarpenak &gt; Aplikazioak atalean"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Ireki ezarpenak"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Instalatzeko eta desinstalatzeko ekintzak ezin dira gauzatu Wear gailuetan."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Aukeratu zer atzi dezakeen &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioak"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Eguneratu egin da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. Aukeratu aplikazioak zer atzi dezakeen."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Utzi"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Jarraitu"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Baimen berriak"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Uneko baimenak"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Aplikazioa prestatzen…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Ezezaguna"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Segurtasuna bermatzeko, tableta honetan ezin dira instalatu iturburu honetako aplikazio ezezagunak."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Segurtasuna bermatzeko, telebista honetan ezin dira instalatu iturburu honetako aplikazio ezezagunak."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Segurtasuna bermatzeko, telefono honetan ezin dira instalatu iturburu honetako aplikazio ezezagunak."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartzen duzu hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea zeu zarela."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartzen duzu hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea zeu zarela."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Iturburu honetako aplikazioak instalatzen badituzu, onartzen duzu haiek erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea zeu zarela."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Egin aurrera"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Ezarpenak"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear aplikazioak instalatzea/desinstalatzea"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fa-television/strings.xml b/packages/PackageInstaller/res/values-fa-television/strings.xml
new file mode 100644
index 0000000..95f2a54
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fa-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"اجازه ندارد و دیگر سؤال نشود"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"‏می‌توانید بعداً آن را در تنظیمات &gt; برنامه‌ها تغییر دهید"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"نمایش‌ برنامه‌های سیستم"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"مجوزهای برنامه"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"مجوزهای برنامه"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"مجوزهای <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"مجوزهای بیشتر"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"مجوزهای <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fa-watch/strings.xml b/packages/PackageInstaller/res/values-fa-watch/strings.xml
new file mode 100644
index 0000000..8d14954
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fa-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"رد شود، دیگر سؤال نشود"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"نمایش‌ برنامه‌های سیستم"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"نمی‌تواند تغییر کند"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"بله"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"لغو"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fa/strings.xml b/packages/PackageInstaller/res/values-fa/strings.xml
new file mode 100644
index 0000000..22f1a25
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fa/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"نصب‌کننده بسته"</string>
+    <string name="next" msgid="3057143178373252333">"بعدی"</string>
+    <string name="install" msgid="5896438203900042068">"نصب"</string>
+    <string name="done" msgid="3889387558374211719">"تمام"</string>
+    <string name="cancel" msgid="8360346460165114585">"لغو"</string>
+    <string name="installing" msgid="8613631001631998372">"در حال نصب…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"درحال نصب <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"برنامه نصب شد."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"آیا می‌خواهید این برنامه را نصب کنید؟ این برنامه به این موارد دسترسی خواهد یافت:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"آیا می‌خواهید این برنامه را نصب کنید؟ این برنامه به دسترسی خاصی نیاز ندارد."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"آیا میٰ‌خواهید بهٰ‌روزرسانی این برنامه کنونی را نصب کنید؟ داده کنونی شما از بین نمی‌رود. برنامه به‌روزرسانی شده دسترسی خواهد داشت به:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"آیا می‌خواهید به‌روزرسانی این برنامه جاسازی شده را نصب کنید؟ داده‌های کنونی شما از بین نمی‌رود. برنامه به‌روزرسانی شده دسترسی خواهد داشت به:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"آیا می‌خواهید یک به‌روزرسانی برای این برنامه کاربردی موجود نصب کنید؟ داده‌های موجود شما از دست نخواهد رفت. به دسترسی ویژه‌ای نیاز ندارد."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"آیا می‌خواهید یک به‌روزرسانی برای این برنامه کاربردی داخلی نصب کنید؟ داده‌های موجود شما از دست نخواهد رفت. به دسترسی ویژه‌ای نیاز ندارد."</string>
+    <string name="install_failed" msgid="6579998651498970899">"برنامه نصب نشد."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"از نصب شدن بسته جلوگیری شد."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"برنامه نصب نشد چون بسته با بسته موجود تداخل دارد."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"برنامه نصب نشد چون با رایانه لوحی‌تان سازگار نیست."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"این برنامه با تلویزیون شما سازگار نیست."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"برنامه نصب نشد چون با تلفنتان سازگار نیست."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"برنامه نصب نشد چون به نظر می‌رسد بسته معتبر نیست."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> در رایانهٔ لوحی شما نصب نشد."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> را نمی‌توان روی تلویزیون شما نصب کرد."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> در تلفن شما نصب نشد."</string>
+    <string name="launch" msgid="4826921505917605463">"باز کردن"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"سرپرست سیستم شما اجازه نمی‌دهد برنامه‌های دریافت‌شده از منابع ناشناس را نصب کنید"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"این کاربر نمی‌تواند برنامه‌های ناشناس نصب کند"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"این کاربر مجاز به نصب برنامه‌ نیست"</string>
+    <string name="ok" msgid="3468756155452870475">"تأیید"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"مدیریت برنامه‌ها"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"فضا کافی نیست"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> نصب نمی‌شود. مقداری از فضا را آزاد کرده و دوباره امتحان کنید."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"برنامه یافت نشد"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"برنامه در فهرست برنامه‌های نصب شده یافت نشد."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"مجاز نیست"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"کاربر کنونی مجاز به انجام این حذف نصب نیست."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"خطا"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"برنامه را نمی‌توان حذف نصب کرد."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"حذف نصب برنامه"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"حذف نصب به‌روزرسانی"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> قسمتی از برنامه زیر است:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"می‌خواهید این برنامه را حذف نصب کنید؟"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"آیا می‌خواهید این برنامه را برای "<b>"همه"</b>" کاربران حذف کنید؟ این برنامه کاربردی و داده‌های آن برای "<b>"همه"</b>" کاربران این دستگاه حذف خواهد شد."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"آیا می‌خواهید این برنامه را برای این کاربر <xliff:g id="USERNAME">%1$s</xliff:g> حذف نصب کنید؟"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"این برنامه با نسخه کارخانه جایگزین شود؟ همه داده‌ها پاک می‌شوند."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"این برنامه با نسخه کارخانه جایگزین شود؟ همه داده‌ها پاک می‌شوند. این کار همه کاربران این دستگاه (از جمله کاربرانی که نمایه کاری دارند) را تحت تأثیر قرار خواهد داد."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"حذف‌نصب‌های درحال انجام"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"حذف‌نصب‌های ناموفق"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"در حال حذف نصب..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"درحال حذف نصب <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"حذف نصب پایان یافت."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> را حذف نصب کرد"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"حذف نصب انجام نشد."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> باموفقیت حذف نصب شد."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"نمی‌توان برنامه فعال سرپرست دستگاه را حذف نصب کرد"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"نمی‌توان برنامه فعال سرپرست دستگاه را برای <xliff:g id="USERNAME">%1$s</xliff:g> حذف نصب کرد"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"این برنامه برای برخی کاربران یا نمایه‌ها ضروری است و برای بقیه حذف نصب شد"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"این برنامه برای نمایه شما لازم است و نمی‌توان آن را حذف نصب کرد."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"سرپرست دستگاه شما این برنامه را لازم کرده است و نمی‌تواند حذف نصب شود."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"مدیریت برنامه‌های سرپرست دستگاه"</string>
+    <string name="manage_users" msgid="3125018886835668847">"مدیریت کاربران"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> حذف نصب نشد."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"مشکلی در تجزیه این بسته وجود داشت."</string>
+    <string name="newPerms" msgid="6039428254474104210">"جدید"</string>
+    <string name="allPerms" msgid="1024385515840703981">"همه موارد"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"حریم خصوصی"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"دسترسی به دستگاه"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"این به‌روزرسانی به مجوز جدیدی نیاز ندارد."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"اجازه ندارد"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"اطلاعات بیشتر"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"در هر صورت نادیده گرفته شود"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> از <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"‏به&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه <xliff:g id="ACTION">%2$s</xliff:g> را می‌دهید؟"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"‏همیشه به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; برای <xliff:g id="ACTION">%2$s</xliff:g> اجازه داده شود؟"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"فقط هنگام استفاده از برنامه"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"همیشه"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"رد شود و دیگر سؤال نشود"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> مجوز غیرفعال هستند"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"همه مجوزها غیرفعال هستند"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"هیچ‌ موردی غیرفعال نیست"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"مجاز است"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"برنامه‌ها"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"مجوزهای برنامه"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"دوباره سؤال نشود"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"مجوزی موجود نیست"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"مجوزهای بیشتر"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"باز کردن اطلاعات برنامه"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <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="old_sdk_deny_warning" msgid="3872277112584842615">"‏این برنامه برای یک نسخه قدیمی‌تر از Android طراحی شده بود. نپذیرفتن اجازه ممکن است باعث شود که برنامه دیگر به صورتی که موردنظر است کار نکند."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"انجام یک اقدام ناشناس"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> برنامه از <xliff:g id="COUNT_1">%2$d</xliff:g> برنامه مجاز است"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"نمایش سیستم"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"پنهان کردن سیستم"</string>
+    <string name="no_apps" msgid="1965493419005012569">"برنامه‌ای موجود نیست"</string>
+    <string name="location_settings" msgid="1774875730854491297">"تنظیمات مکان"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> یکی از ارائه‌دهندگان سرویس‌های مکان برای این دستگاه است. با رفتن به تنظیمات مکان می‌توانید دسترسی به موقعیت مکانی را تغییر دهید."</string>
+    <string name="system_warning" msgid="7103819124542305179">"اگر این اجازه را رد کنید، ممکن است قابلیت‌های اصلی دستگاهتان دیگر عملکرد موردانتظار را نداشته باشند."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"اجرا توسط خط‌مشی"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"دسترسی به پس‌زمینه به‌موجب خط‌مشی غیرفعال شد"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"دسترسی به پس‌زمینه به‌موجب خط‌مشی فعال شد"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"دسترسی به پیش‌زمینه به‌موجب خط‌مشی فعال شد"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"توسط سرپرست سیستم کنترل می‌شود"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"همیشه"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"فقط هنگام استفاده از برنامه"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"هرگز"</string>
+    <string name="loading" msgid="7811651799620593731">"درحال بارگیری…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"همه مجوزها"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"سایر قابلیت‌های برنامه"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"درخواست مجوز"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"هم‌پوشانی صفحه شناسایی شد"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"‏برای تغییر این تنظیم مجوز، ابتدا باید هم‌پوشانی صفحه را از «تنظیمات &gt; برنامه‌ها» خاموش کنید"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"باز کردن تنظیمات"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"‏کنش‌های نصب/حذف نصب در Wear پشتیبانی نمی‌شود."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"‏انتخاب کنید &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دارد به چه چیزی دسترسی پیدا کند"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; به‌روزرسانی شده است. انتخاب کنید این برنامه اجازه دارد به چه چیزی دسترسی پیدا کند."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"لغو"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"ادامه"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"مجوزهای جدید"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"مجوزهای کنونی"</string>
+    <string name="message_staging" msgid="6151794817691100003">"مرحله‌بندی برنامه…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"نامشخص"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"برای امنیت شما، رایانه لوحی‌تان اجازه نمی‌دهد از این منبع برنامه‌های ناشناس نصب شوند."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"برای امنیت شما، تلویزیونتان اجازه نمی‌دهد از این منبع برنامه‌های ناشناس نصب شوند."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"برای امنیت شما، تلفنتان اجازه نمی‌دهد از این منبع برنامه‌های ناشناس نصب شوند."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"تلفن و داده‌های شخصی‌تان در برابر حمله برنامه‌های ناشناس آسیب‌پذیرتر هستند. با نصب این برنامه، موافقت می‌کنید که مسئول هرگونه آسیب به تلفن یا از دست رفتن داده‌ای هستید که ممکن است در نتیجه استفاده از آن به وجود آید."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"رایانه لوحی و داده‌های شخصی‌تان در برابر حمله برنامه‌های ناشناس آسیب‌پذیرتر هستند. با نصب این برنامه، موافقت می‌کنید که مسئول هرگونه آسیب به رایانه لوحی یا از دست رفتن داده‌ای هستید که ممکن است در نتیجه استفاده از آن به وجود آید."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"تلویزیون و داده‌های شخصی‌تان در برابر حمله برنامه‌های ناشناس آسیب‌پذیرتر هستند. با نصب این برنامه، موافقت می‌کنید که مسئول هرگونه آسیب به تلویزیون یا از دست رفتن داده‌ای هستید که ممکن است در نتیجه استفاده از آن به وجود آید."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"ادامه"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"تنظیمات"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"نصب/حذف نصب برنامه‌های پوشیدنی"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fi-television/strings.xml b/packages/PackageInstaller/res/values-fi-television/strings.xml
new file mode 100644
index 0000000..fbcbe4f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fi-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Hylkää äläkä kysy uudelleen"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Voit muuttaa tätä myöhemmin valitsemalla Asetukset &gt; Sovellukset."</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Näytä järjestelmäsovellukset"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Sovellusten käyttöoikeudet"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Sovellusten käyttöoikeudet"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Käyttöoikeudet – <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Lisäkäyttöoikeudet"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Käyttöoikeudet – <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fi-watch/strings.xml b/packages/PackageInstaller/res/values-fi-watch/strings.xml
new file mode 100644
index 0000000..292c417
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fi-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Hylkää, äläkä kysy uudelleen"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Näytä järjestelmäsovellukset"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Ei muutettavissa"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Kyllä"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Peruuta"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fi/strings.xml b/packages/PackageInstaller/res/values-fi/strings.xml
new file mode 100644
index 0000000..7ce75d1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fi/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Paketin asentaja"</string>
+    <string name="next" msgid="3057143178373252333">"Seuraava"</string>
+    <string name="install" msgid="5896438203900042068">"Asenna"</string>
+    <string name="done" msgid="3889387558374211719">"Valmis"</string>
+    <string name="cancel" msgid="8360346460165114585">"Peruuta"</string>
+    <string name="installing" msgid="8613631001631998372">"Asennetaan…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Asennetaan kohdetta <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Sovellus on asennettu."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Haluatko asentaa tämän sovelluksen? Se saa käyttöönsä seuraavat ominaisuudet:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Haluatko asentaa tämän sovelluksen? Se ei vaadi erityisiä käyttöoikeuksia."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Haluatko asentaa päivityksen tähän olemassa olevaan sovellukseen? Olemassa olevat tiedot eivät katoa. Päivitetty sovellus saa käyttöönsä seuraavat ominaisuudet:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Haluatko asentaa päivityksen tähän sisäiseen sovellukseen? Olemassa olevat tiedot eivät katoa. Päivitetty sovellus saa käyttöönsä seuraavat ominaisuudet:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Haluatko asentaa päivityksen tähän sovellukseen? Et menetä nykyisiä tietojasi. Päivitys ei edellytä erityisiä käyttöoikeuksia."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Haluatko asentaa päivityksen tähän laitteen mukana tulleeseen sovellukseen? Et menetä nykyisiä tietojasi. Päivitys ei edellytä erityisiä käyttöoikeuksia."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Sovellusta ei asennettu."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Paketin asennus estettiin."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Sovellusta ei asennettu, koska paketti on ristiriidassa nykyisen paketin kanssa."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Sovellusta ei asennettu, koska se ei ole yhteensopiva tabletin kanssa."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Tämä sovellus ei ole yhteensopiva televisiosi kanssa."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Sovellusta ei asennettu, koska se ei ole yhteensopiva puhelimen kanssa."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Sovellusta ei asennettu, koska paketti vaikuttaa virheelliseltä."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Sovelluksen <xliff:g id="APP_NAME">%1$s</xliff:g> asentaminen tähän tablet-laitteeseen epäonnistui."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei asennu televisioosi."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Sovelluksen <xliff:g id="APP_NAME">%1$s</xliff:g> asentaminen puhelimeesi ei onnistunut."</string>
+    <string name="launch" msgid="4826921505917605463">"Avaa"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Järjestelmänvalvoja ei salli sovellusten asentamista tuntemattomista lähteistä."</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Tämä käyttäjä ei voi asentaa tuntemattomia sovelluksia."</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Tämä käyttäjä ei voi asentaa sovelluksia."</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Hallinnoi sovelluksia"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Tallennustila loppu"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Sovelluksen <xliff:g id="APP_NAME">%1$s</xliff:g> asentaminen epäonnistui. Vapauta tallennustilaa ja yritä uudelleen."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Sovellusta ei löydy"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Sovellusta ei löydy asennettujen sovelluksien luettelosta."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Ei sallittu"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Nykyisellä käyttäjällä ei ole oikeutta suorittaa tätä poistoa."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Virhe"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Sovelluksen poistaminen epäonnistui."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Poista sovellus"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Poista päivitys"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> on osa seuraavaa sovellusta:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Haluatko poistaa tämän sovelluksen?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Haluatko poistaa tämän sovelluksen "<b>"kaikilta"</b>" käyttäjiltä? Sovellus ja sen tiedot poistetaan "<b>"kaikilta"</b>" laitteen käyttäjiltä."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"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="1394549691152728409">"Haluatko korvata tämän sovelluksen tehdasversiolla? Kaikki tiedot poistetaan."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Haluatko korvata tämän sovelluksen tehdasversiolla? Kaikki tiedot poistetaan. Tämä vaikuttaa kaikkiin laitteen käyttäjiin, myös työprofiileihin."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Käynnissä olevat poistot"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Epäonnistuneet poistot"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Poistetaan..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Poistetaan pakettia <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Poisto valmis."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> poistettu"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Poisto epäonnistui."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> on poistettu."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Aktiivista laitteenhallintasovellusta ei voi poistaa käytöstä."</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Käyttäjän <xliff:g id="USERNAME">%1$s</xliff:g> aktiivista laitteenhallintasovellusta ei voi poistaa käytöstä."</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Jotkin käyttäjät/profiilit tarvitsevat tätä sovellusta ja se poistettiin muista."</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Profiilisi käyttö edellyttää tätä sovellusta. Sovellusta ei voi poistaa."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Laitteen järjestelmänvalvoja tarvitsee tätä sovellusta eikä sitä voi poistaa."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Hallinnoi laitteenhallintasovelluksia"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Hallinnoi käyttäjiä"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Sovelluksen <xliff:g id="APP_NAME">%1$s</xliff:g> poistaminen epäonnistui"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Paketin jäsentämisessä esiintyi ongelma."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Uusi"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Kaikki"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Tietosuoja"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Laitteen käyttö"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Tämä päivitys ei vaadi uusia käyttöoikeuksia."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Estä"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Lisätietoja"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Kiellä silti"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aina <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Vain sovelluksen käytön aikana"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Aina"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Hylkää äläkä kysy uudelleen"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> pois käytöstä"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"kaikki pois käytöstä"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"kaikki käytössä"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Salli"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Sovellukset"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Sovellusten käyttöoikeudet"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Älä kysy uudestaan"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Ei käyttöoikeuksia"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Lisäkäyttöoikeudet"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Avaa sovelluksen tiedot"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> lisää</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> lisää</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Tämä sovellus on suunniteltu vanhemmalle Android-versiolle. Se ei välttämättä toimi oikein, jos käyttöoikeuksia ei sallita."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"suorita tuntematon toiminto"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Sallitut sovellukset: <xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Näytä järjestelmä"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Piilota järjestelmä"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Ei sovelluksia"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Sijaintiasetukset"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> on tämän laitteen sijaintipalveluiden tarjoaja. Sijainnin käyttöoikeutta voi muokata sijaintiasetuksissa."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Jos peruutat tämän käyttöoikeuden, laitteesi perustoiminnot eivät välttämättä enää toimi oikein."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Käytännön vahvistama"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Käytäntö estää taustakäytön"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Käytäntö sallii taustakäytön"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Käytäntö sallii käytön etualalla"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Järjestelmänvalvoja hallinnoi tätä"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Aina"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Vain sovelluksen käytön aikana"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Ei koskaan"</string>
+    <string name="loading" msgid="7811651799620593731">"Ladataan…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Kaikki käyttöoikeudet"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Muut sovellusluvat"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Lupapyyntö"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Näytön peittokuva havaittiin"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Ennen kuin voit muokata tätä käyttöoikeusasetusta, sinun täytyy poistaa näytön peittokuva käytöstä Asetukset-valikon Sovellukset-kohdasta."</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Avaa Asetukset"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear ei tue asennus- ja poistotoimintoja."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Valitse, mitä käyttöoikeuksia sovellukselle &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; myönnetään."</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on päivitetty. Valitse, mitä käyttöoikeuksia tälle sovellukselle myönnetään."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Peruuta"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Jatka"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Uudet käyttöoikeudet"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Nykyiset käyttöoikeudet"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Valmistellaan sovellusta…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Tuntematon"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Turvallisuussyistä tabletti ei voi asentaa tuntemattomia sovelluksia tästä lähteestä."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Turvallisuussyistä televisiosi ei voi asentaa tuntemattomia sovelluksia tästä lähteestä."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Turvallisuussyistä puhelin ei voi asentaa tuntemattomia sovelluksia tästä lähteestä."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Tuntemattomat sovellukset voivat helpommin kaapata puhelimesi ja henkilötietosi. Lataamalla sovelluksia tästä lähteestä hyväksyt, että olet itse vastuussa puhelimellesi aiheutuvista vahingoista tai tietojen menetyksestä, jotka voivat johtua sovellusten käytöstä."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Tuntemattomat sovellukset voivat helpommin kaapata tablettisi ja henkilötietosi. Lataamalla sovelluksia tästä lähteestä hyväksyt, että olet itse vastuussa tabletillesi aiheutuvista vahingoista tai tietojen menetyksestä, jotka voivat johtua sovellusten käytöstä."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Tuntemattomat sovellukset voivat helpommin kaapata televisiosi ja henkilötietosi. Lataamalla sovelluksen hyväksyt, että olet itse vastuussa mahdollisista televisiolle aiheutuvista vahingoista tai tietojen menetyksestä, jotka voivat johtua sovellusten käytöstä."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Jatka"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Asetukset"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear-sovellusten asennus/poistaminen"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fr-rCA-television/strings.xml b/packages/PackageInstaller/res/values-fr-rCA-television/strings.xml
new file mode 100644
index 0000000..f9b360e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr-rCA-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Refuser et ne plus demander"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Vous pourrez modifier ce choix plus tard dans le menu Paramètres &gt; Applications"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Afficher les applications système"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Autorisations de l\'application"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Autorisations de l\'application"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Autorisations pour <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Autorisations supplémentaires"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Autorisations pour <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fr-rCA-watch/strings.xml b/packages/PackageInstaller/res/values-fr-rCA-watch/strings.xml
new file mode 100644
index 0000000..ad86d016
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr-rCA-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Refuser et ne plus demander"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Afficher les applications système"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Inchangeable"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Oui"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Annuler"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fr-rCA/strings.xml b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..e44e465
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Programme installation trousse"</string>
+    <string name="next" msgid="3057143178373252333">"Suivante"</string>
+    <string name="install" msgid="5896438203900042068">"Installer"</string>
+    <string name="done" msgid="3889387558374211719">"Terminé"</string>
+    <string name="cancel" msgid="8360346460165114585">"Annuler"</string>
+    <string name="installing" msgid="8613631001631998372">"Installation..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Installation de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> en cours…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Application installée."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Voulez-vous installer cette application? Elle pourra :"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Voulez-vous installer cette application? Elle n\'exige aucun accès particulier."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Voulez-vous installer une mise à jour pour cette application? Vos données existantes seront conservées. L\'application mise à jour aura accès à :"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Voulez-vous installer une mise à jour pour cette application intégrée? Vos données existantes seront conservées. L\'application mise à jour aura accès à :"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Voulez-vous installer une mise à jour pour cette application? Vos données ne seront pas perdues. Aucun droit d\'accès spécial n\'est requis."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Voulez-vous installer une mise à jour pour cette application intégrée? Vos données existantes ne seront pas perdues. Aucun droit d\'accès spécial n\'est requis."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Application non installée."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"L\'installation du paquet a été bloquée."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"L\'application n\'a pas été installée, car le paquet entre en conflit avec un paquet existant."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"L\'application n\'a pas été installée, car elle n\'est pas compatible avec votre tablette."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Cette application n\'est pas compatible avec votre téléviseur."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"L\'application n\'a pas été installée, car elle n\'est pas compatible avec votre téléphone."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"L\'application n\'a pas été installée, car elle ne semble pas être valide."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Impossible d\'installer <xliff:g id="APP_NAME">%1$s</xliff:g> sur cette tablette."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'a pas pu être installée sur votre téléviseur."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Impossible d\'installer <xliff:g id="APP_NAME">%1$s</xliff:g> sur ce téléphone."</string>
+    <string name="launch" msgid="4826921505917605463">"Ouvrir"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Votre administrateur n\'autorise pas l\'installation d\'applications obtenues à partir de sources inconnues"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Cet utilisateur ne peut pas installer les applications inconnues"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Cet utilisateur n\'est pas autorisé à installer des applications"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Gérer les applications"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Espace insuffisant"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Impossible d\'installer <xliff:g id="APP_NAME">%1$s</xliff:g>. Veuillez libérer de l\'espace, puis réessayer."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Application non trouvée"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"L\'application ne figure pas dans la liste des applications installées."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Non autorisé"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"L\'utilisateur actuel n\'est pas autorisé à effectuer cette désinstallation."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Erreur"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"L\'application n\'a pas pu être désinstallée."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Désinstaller l\'application"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Désinstaller mise à jour"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> fait partie de l\'application suivante :"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Voulez-vous désinstaller cette application?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Voulez-vous désinstaller cette application pour "<b>"tous"</b>" les utilisateurs? L\'application et ses données seront supprimées pour "<b>"tous"</b>" les utilisateurs de l\'appareil."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Voulez-vous désinstaller cette application pour l\'utilisateur <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Remplacer cette application par la version d\'usine? Toutes les données seront supprimées."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Désinstallations en cours"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Désinstallations échouées"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Désinstallation..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Désinstallation de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> en cours…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Désinstallation terminée."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"L\'application <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> a bien été désinstallée"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Échec de la désinstallation."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"La désinstallation de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> n\'a pas réussi."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Impossible de désinstaller une application d\'administration de l\'appareil active"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Impossible de désinstaller une application d\'administration de l\'appareil active pour <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Cette application est nécessaire pour certains utilisateurs ou profils, et elle a été désinstallée pour d\'autres."</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Cette application est nécessaire pour votre profil et ne peut pas être désinstallée."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Impossible de désinstaller l\'application : requise par administrateur appareil."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Gérer les applications d\'administration d\'appareils"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Gérer les utilisateurs"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Impossible de désinstaller <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Un problème est survenu lors de l\'analyse du paquet."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nouvelles"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Toutes"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Confidentialité"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Accès à l\'appareil"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Cette mise à jour n\'exige pas de nouvelles autorisations."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Refuser"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"En savoir plus"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Refuser quand même"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Toujours autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Seulement durant l\'utilisation de l\'application"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Toujours"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Refuser et ne plus demander"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> autorisation(s) désactivée(s)"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"toutes les autorisations sont désactivées"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"aucune autorisation n\'est désactivée"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Autoriser"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Applications"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Autorisations applis"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Ne plus demander"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Aucune autorisation"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Autorisations supplémentaires"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Ouvrir l\'information sur l\'application"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> autre</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> autres</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Cette application a été conçue pour une version antérieure d\'Android. Si vous n\'accordez pas l\'autorisation, il se peut qu\'elle ne fonctionne plus correctement."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"effectuer une action inconnue"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> application(s) autorisée(s) sur <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Afficher le système"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Masquer le système"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Aucune application"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Paramètres de localisation"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> est un fournisseur de services de localisation pour cet appareil. L\'accès à la position peut être modifié dans le menu des paramètres de localisation."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Si vous refusez cette autorisation, il est possible que cela touche certaines fonctionnalités de base de votre appareil."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Activé conformément à la politique"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"L\'accès en arrière-plan est désactivé par la politique"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"L\'accès en arrière-plan est activé par la politique"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"L\'accès en avant-plan est activé par la politique"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Contrôlé par l\'administrateur"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Toujours"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Seulement durant l\'util. de l\'appli"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Jamais"</string>
+    <string name="loading" msgid="7811651799620593731">"Chargement en cours…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Toutes les autorisations"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Autres autorisations de l\'application"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Demande d\'autorisation"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"La superposition d\'écran a été détectée"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Pour modifier ce paramètre d\'autorisation, vous devez tout d\'abord désactiver la superposition d\'écran en accédant à Paramètres &gt; Applications."</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Ouvrir les paramètres"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Les actions d\'installation et de désinstallation ne sont pas prises en charge par Android Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Définissez les autorisations d\'accès de l\'application « <xliff:g id="APP_NAME">%1$s</xliff:g> »"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"L\'application « <xliff:g id="APP_NAME">%1$s</xliff:g> » a été mise à jour. Définissez ses autorisations d\'accès."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Annuler"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continuer"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nouvelles autorisations"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Autorisations actuelles"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Pré-production de l\'application en cours…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Inconnue"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"À des fins de sécurité, l\'installation d\'applications inconnues provenant de cette source n\'est pas autorisée sur cette tablette."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"À des fins de sécurité, l\'installation d\'applications inconnues provenant de cette source n\'est pas autorisée sur ce téléviseur."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"À des fins de sécurité, l\'installation d\'applications inconnues provenant de cette source n\'est pas autorisée sur ce téléphone."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Votre téléphone et vos données personnelles sont plus vulnérables aux attaques provenant d\'applications inconnues. En installant cette application vous acceptez d\'être le seul responsable de tout dommage causé à votre téléphone ou de toute perte de données pouvant découler de l\'utilisation de telles applications."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Votre tablette et vos données personnelles sont plus vulnérables aux attaques provenant d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre tablette ou de toute perte de données pouvant découler de l\'utilisation de telles applications."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Votre téléviseur et vos données personnelles sont plus vulnérables face aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléviseur ou de toute perte de données pouvant découler de son utilisation."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continuer"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Paramètres"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Installer/désinstaller applis Google Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fr-television/strings.xml b/packages/PackageInstaller/res/values-fr-television/strings.xml
new file mode 100644
index 0000000..fa16b94
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Refuser et ne plus demander"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Vous pourrez modifier ce paramètre plus tard sous Paramètres &gt; Applications."</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Afficher les applications système"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Autorisations de l\'application"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Autorisations de l\'application"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Autorisations pour <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Autorisations supplémentaires"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Autorisations pour <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fr-watch/strings.xml b/packages/PackageInstaller/res/values-fr-watch/strings.xml
new file mode 100644
index 0000000..a172e1c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Refuser et ne plus demander"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Afficher les applications système"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Non modifiable"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Oui"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Annuler"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fr/strings.xml b/packages/PackageInstaller/res/values-fr/strings.xml
new file mode 100644
index 0000000..58d9123
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Programme d\'installation du kit"</string>
+    <string name="next" msgid="3057143178373252333">"Suivant"</string>
+    <string name="install" msgid="5896438203900042068">"Installer"</string>
+    <string name="done" msgid="3889387558374211719">"OK"</string>
+    <string name="cancel" msgid="8360346460165114585">"Annuler"</string>
+    <string name="installing" msgid="8613631001631998372">"Installation..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Installation de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Application installée."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Voulez-vous installer cette application ? Elle permet les actions suivantes :"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Voulez-vous installer cette application ? Elle n\'exige aucun accès particulier."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Voulez-vous installer une mise à jour pour cette application ? Vos données existantes seront conservées. L\'application mise à jour pourra :"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Voulez-vous installer une mise à jour pour cette application intégrée ? Vos données existantes seront conservées. L\'application mise à jour pourra :"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Voulez-vous installer une mise à jour pour cette application ? Vos données ne seront pas perdues. Aucun droit d\'accès spécial n\'est requis."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Voulez-vous installer une mise à jour pour cette application intégrée ? Vos données existantes ne seront pas perdues. Aucun droit d\'accès spécial n\'est requis."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Application non installée."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"L\'installation du package a été bloquée."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"L\'application n\'a pas été installée, car le package entre en conflit avec un package existant."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"L\'application n\'a pas été installée, car elle n\'est pas compatible avec votre tablette."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Cette application n\'est pas compatible avec votre téléviseur."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"L\'application n\'a pas été installée, car elle n\'est pas compatible avec votre téléphone."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"L\'application n\'a pas été installée, car le package semble ne pas être valide."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Impossible d\'installer <xliff:g id="APP_NAME">%1$s</xliff:g> sur cette tablette."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Impossible d\'installer <xliff:g id="APP_NAME">%1$s</xliff:g> sur votre téléviseur."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Impossible d\'installer <xliff:g id="APP_NAME">%1$s</xliff:g> sur ce téléphone."</string>
+    <string name="launch" msgid="4826921505917605463">"Ouvrir"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Votre administrateur n\'autorise pas l\'installation d\'applications obtenues à partir de sources inconnues"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Cet utilisateur ne peut pas installer d\'applications inconnues"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Cet utilisateur n\'est pas autorisé à installer des applications"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Gérer les applications"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Mémoire insuffisante"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Impossible d\'installer <xliff:g id="APP_NAME">%1$s</xliff:g>. Veuillez libérer de l\'espace, puis réessayer."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Application non trouvée"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"L\'application ne figure pas dans la liste des applications installées."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Non autorisé"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"L\'utilisateur actuel n\'est pas autorisé à effectuer cette désinstallation."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Erreur"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Impossible de désinstaller l\'application."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Désinstaller l\'application"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Désinstaller la mise à jour"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> fait partie de l\'application suivante :"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Voulez-vous désinstaller cette application ?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Voulez-vous désinstaller cette application pour "<b>"tous"</b>" les utilisateurs ? L\'application et ses données seront supprimées pour "<b>"tous"</b>" les utilisateurs de l\'appareil."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Voulez-vous désinstaller cette application pour l\'utilisateur <xliff:g id="USERNAME">%1$s</xliff:g> ?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Remplacer cette application par la version d\'usine ? Toutes les données seront supprimées."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Désinstallations en cours"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Désinstallations non abouties"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Désinstallation..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Désinstallation de l\'application <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Désinstallation terminée."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"L\'application <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> a été désinstallée"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Échec de la désinstallation."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Échec de la désinstallation de l\'application <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Impossible de désinstaller une application d\'administration de l\'appareil active"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Impossible de désinstaller une application d\'administration de l\'appareil active pour <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Cette application nécessaire pour certains utilisateurs ou profils a été désinstallée pour d\'autres."</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Impossible de désinstaller l\'application, car elle est nécessaire pour votre profil."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Impossible désinstaller appli, car elle est requise par administrateur appareil."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Gérer les applis d\'administration de l\'appareil"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Gérer les utilisateurs"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Impossible de désinstaller <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Un problème est survenu lors de l\'analyse du package."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nouveautés"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Toutes"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Confidentialité"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Accès à l\'appareil"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Cette mise à jour n\'exige pas de nouvelles autorisations."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Refuser"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Plus d\'infos"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Refuser quand même"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> sur <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Autoriser l\'application &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à <xliff:g id="ACTION">%2$s</xliff:g> ?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Toujours autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à <xliff:g id="ACTION">%2$s</xliff:g> ?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Seulement lors de l\'utilisation de l\'application"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Toujours"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Refuser et ne plus demander"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> désactivées"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"toutes désactivées"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"aucune désactivée"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Autoriser"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Applications"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Autorisations applis"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Ne plus demander"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Aucune autorisation"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Autorisations supplémentaires"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Ouvrir les informations sur l\'application"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> autre</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> autres</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Cette application a été conçue pour une ancienne version d\'Android. Si vous désactivez les autorisations, l\'application risque de ne plus fonctionner comme prévu."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"effectuer une action inconnue"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> application(s) autorisée(s) sur <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Afficher les processus système"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Masquer les processus système"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Aucune application"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Paramètres de géolocalisation"</string>
+    <string name="location_warning" msgid="8778701356292735971">"Les services de localisation pour cet appareil sont fournis via <xliff:g id="APP_NAME">%1$s</xliff:g>. Vous pouvez modifier l\'accès aux données de localisation dans les paramètres de localisation."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Si vous refusez cette autorisation, il est possible que cela affecte certaines fonctionnalités de base de votre appareil."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Activé conformément aux règles"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Accès en arrière-plan désactivé conformément au règlement"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Accès en arrière-plan activé conformément au règlement"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Accès au premier plan activé conformément au règlement"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Contrôlé par l\'administrateur"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Toujours"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Seulement lors utilisation appli"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Jamais"</string>
+    <string name="loading" msgid="7811651799620593731">"Chargement en cours…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Toutes les autorisations"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Autres autorisations de l\'application"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Demande d\'autorisation"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Superposition d\'écran détectée"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Pour modifier ce paramètre d\'autorisation, vous devez tout d\'abord désactiver la superposition d\'écran en accédant à Paramètres &gt; Applications."</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Ouvrir les paramètres"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Opérations d\'installation et de désinstallation impossibles sur Android Wear"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Sélectionner les éléments auxquels &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; peut accéder"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"L\'application &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; a été mise à jour. Sélectionnez les éléments auxquels elle peut accéder."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Annuler"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continuer"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nouvelles autorisations"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Autorisations actuelles"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Pré-production de l\'application…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Inconnu"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"À des fins de sécurité, l\'installation d\'applications inconnues provenant de cette source n\'est pas autorisée sur cette tablette."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"À des fins de sécurité, l\'installation d\'applications inconnues provenant de cette source n\'est pas autorisée sur ce téléviseur."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"À des fins de sécurité, l\'installation d\'applications inconnues provenant de cette source n\'est pas autorisée sur ce téléphone."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Votre téléphone et vos données personnelles sont plus vulnérables face aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléphone ou de toute perte de données pouvant découler de son utilisation."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Votre tablette et vos données personnelles sont plus vulnérables face aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre tablette ou de toute perte de données pouvant découler de son utilisation."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Votre téléviseur et vos données personnelles sont plus vulnérables face aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléviseur ou de toute perte de données pouvant découler de son utilisation."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continuer"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Paramètres"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Installer/Désinstaller les applis Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-gl-television/strings.xml b/packages/PackageInstaller/res/values-gl-television/strings.xml
new file mode 100644
index 0000000..01e8498
--- /dev/null
+++ b/packages/PackageInstaller/res/values-gl-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Denegar e non volver preguntar"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Podes cambiar esta opción máis tarde en Configuración e aplicacións"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Mostrar aplicacións do sistema"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Permisos de aplicacións"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Permisos de aplicacións"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Permisos de <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Permisos adicionais"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Permisos de <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-gl-watch/strings.xml b/packages/PackageInstaller/res/values-gl-watch/strings.xml
new file mode 100644
index 0000000..5cbb970
--- /dev/null
+++ b/packages/PackageInstaller/res/values-gl-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Denegar, non volver preguntar"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Mostrar aplicacións do sistema"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Cambio imposible"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Si"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Cancelar"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-gl/strings.xml b/packages/PackageInstaller/res/values-gl/strings.xml
new file mode 100644
index 0000000..f7bf98d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-gl/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Instalador de paquetes"</string>
+    <string name="next" msgid="3057143178373252333">"Seguinte"</string>
+    <string name="install" msgid="5896438203900042068">"Instalar"</string>
+    <string name="done" msgid="3889387558374211719">"Feito"</string>
+    <string name="cancel" msgid="8360346460165114585">"Cancelar"</string>
+    <string name="installing" msgid="8613631001631998372">"Instalando…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Instalando <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplicación instalada"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Queres instalar esta aplicación? Poderá acceder a:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Queres instalar esta aplicación? Non require ningún acceso especial."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Queres instalar unha actualización para esta aplicación? Non se perderán os teus datos existentes. A aplicación actualizada disporá de acceso a:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Queres instalar unha actualización para esta aplicación integrada?  Non se perderán os teus datos existentes. A aplicación actualizada disporá de acceso a:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Queres instalar unha actualización para esta aplicación? Non se perderán os teus datos existentes. Non require ningún acceso especial."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Queres instalar unha actualización para esta aplicación integrada? Non se perderán os teus datos existentes. Non require ningún acceso especial."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Aplicación non instalada"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Bloqueouse a instalación do paquete."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"A aplicación non se instalou porque o paquete presenta un conflito cun paquete existente."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"A aplicación non se instalou porque a aplicación non é compatible coa tableta."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Esta aplicación non é compatible coa túa televisión."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"A aplicación non se instalou porque a aplicación non é compatible co teléfono."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"A aplicación non se instalou porque parece que o paquete non é válido."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Non se puido instalar <xliff:g id="APP_NAME">%1$s</xliff:g> na túa tableta."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> non se puido instalar na túa televisión."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Non se puido instalar <xliff:g id="APP_NAME">%1$s</xliff:g> no teu teléfono."</string>
+    <string name="launch" msgid="4826921505917605463">"Abrir"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"O teu administrador non permite a instalación de aplicacións obtidas a partir de fontes descoñecidas"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Este usuario non pode instalar aplicacións descoñecidas"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Este usuario non ten permiso para instalar aplicacións"</string>
+    <string name="ok" msgid="3468756155452870475">"Aceptar"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Xestionar aplicacións"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Espazo esgotado"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Non se puido instalar <xliff:g id="APP_NAME">%1$s</xliff:g>. Libera espazo e téntao de novo."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Non se encontrou a aplicación"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Non se atopou a aplicación na lista de aplicacións instaladas."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Non permitido"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"O usuario actual non pode realizar esta desinstalación."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Erro"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Non se puido desinstalar a aplicación."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Desinstalar aplicación"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Desinstalar actualización"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> forma parte da seguinte aplicación:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Queres desinstalar esta aplicación?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Queres desinstalar esta aplicación para "<b>"todos"</b>" os usuarios? A aplicación e os seus datos eliminaranse de "<b>"todos"</b>" os usuarios do dispositivo."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Queres desinstalar esta aplicación para o usuario <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"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="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Desintalacións en curso"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Erros nas desinstalacións"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Desinstalando…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Desinstalando <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Desinstalación finalizada"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Desinstalouse <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Desinstalación incorrecta"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"A desinstalación de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> non se realizou correctamente."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Non se puido desinstalar a aplicación de administración de dispositivos activa"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Non se puido desinstalar a aplicación de administración de dispositivos activa para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"A aplicación é necesaria para algúns usuarios ou perfís e estaba desinstalada para outros"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"O teu perfil necesita esta aplicación e non se pode desinstalar."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"O administrador do teu dispositivo necesita esta aplicación e non se pode desinstalar."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Xestionar apps de administración de dispositivos"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Administrar usuarios"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Non se puido desinstalar <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Produciuse un problema ao analizar o paquete."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Novo"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Todos"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privacidade"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Acceso dispositivo"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Esta actualización non require novos permisos."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Denegar"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Máis información"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Denegar igualmente"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Queres permitir á aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Permitir sempre á aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Só ao usar a aplicación"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Sempre"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Denegar e non volver preguntar"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> desactivados"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"todos desactivados"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ningún desactivado"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Permitir"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplicacións"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Permisos de aplicacións"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Non preguntar de novo"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Sen permisos"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Permisos adicionais"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Abrir información da aplicación"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> permisos máis</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> permiso máis</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Esta aplicación deseñouse para unha versión anterior de Android. Denegar o permiso pode provocar que non funcione como está previsto."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"realiza unha acción descoñecida"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> de <xliff:g id="COUNT_1">%2$d</xliff:g> aplicacións con permiso"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Mostrar sistema"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Ocultar sistema"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Ningunha aplicación"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Configuración da localización"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> é un fornecedor de servizos de localización para este dispositivo. O acceso de localización pode modificarse desde a configuración de localización."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Se denegas este permiso, é posible que as funcións básicas do teu dispositivo deixen de funcionar segundo o previsto."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Aplicado pola política"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"O acceso en segundo plano está desactivado pola política"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"O acceso en segundo plano está activado pola política"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"O acceso en primeiro plano está activado pola política"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Opción controlada polo administrador"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Sempre"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Só ao usar a aplicación"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nunca"</string>
+    <string name="loading" msgid="7811651799620593731">"Cargando…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Todos os permisos"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Outras funcionalidades da aplicación"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Solicitude de permiso"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Detectouse unha superposición na pantalla"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Para cambiar a configuración deste permiso, primeiro tes que desactivar a superposición na pantalla, en Configuración &gt; Aplicacións"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Abrir configuración"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"As accións de instalar e desinstalar non son compatibles con Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Seleccionar os permisos de acceso que queres dar a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Actualizouse a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. Selecciona os permisos de acceso que lle queres dar."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Cancelar"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continuar"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Novos permisos"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Permisos actuais"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Probando aplicación…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Descoñecida"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Por cuestións de seguranza, na tableta non se poden instalar aplicacións descoñecidas procedentes desta fonte."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Por cuestións de seguranza, na televisión non se poden instalar aplicacións descoñecidas procedentes desta fonte."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Por cuestións de seguranza, no teléfono non se poden instalar aplicacións descoñecidas procedentes desta fonte."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"O teléfono e os datos persoais son máis vulnerables aos ataques de aplicacións descoñecidas. Ao instalar esta aplicación, aceptas que es responsable dos danos ocasionados no teléfono ou da perda dos datos que se poidan derivar do seu uso."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"A tableta e os datos persoais son máis vulnerables aos ataques de aplicacións descoñecidas. Ao instalar esta aplicación, aceptas que es responsable dos danos ocasionados na tableta ou da perda dos datos que se poidan derivar do seu uso."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"A televisión e os datos persoais son máis vulnerables aos ataques de aplicacións descoñecidas. Ao instalar esta aplicación, aceptas que es responsable dos danos ocasionados na televisión ou da perda dos datos que se poidan derivar do seu uso."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continuar"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Configuración"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Instalando/desinstalando apps Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-gu-television/strings.xml b/packages/PackageInstaller/res/values-gu-television/strings.xml
new file mode 100644
index 0000000..b0a40b6
--- /dev/null
+++ b/packages/PackageInstaller/res/values-gu-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"નકારો અને ફરીથી પૂછશો નહીં"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"તમે પછીથી આને સેટિંગ્સ &gt; એપ્લિકેશન્સમાં બદલી શકો છો"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"સિસ્ટમ ઍપ્લિકેશનો બતાવો"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"ઍપ્લિકેશન પરવાનગીઓ"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"ઍપ્લિકેશન પરવાનગીઓ"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> પરવાનગીઓ"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"વધારાની પરવાનગીઓ"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> પરવાનગીઓ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-gu-watch/strings.xml b/packages/PackageInstaller/res/values-gu-watch/strings.xml
new file mode 100644
index 0000000..6e83cf2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-gu-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"નકારો, ફરીથી પૂછશો નહીં"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"સિસ્ટમ ઍપ્લિકેશનો બતાવો"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"બદલી શકતાં નથી"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"હા"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"રદ કરો"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-gu/strings.xml b/packages/PackageInstaller/res/values-gu/strings.xml
new file mode 100644
index 0000000..5e0a2b3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-gu/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"પૅકેજ ઇન્સ્ટોલર"</string>
+    <string name="next" msgid="3057143178373252333">"આગલું"</string>
+    <string name="install" msgid="5896438203900042068">"ઇન્સ્ટોલ કરો"</string>
+    <string name="done" msgid="3889387558374211719">"થઈ ગયું"</string>
+    <string name="cancel" msgid="8360346460165114585">"રદ કરો"</string>
+    <string name="installing" msgid="8613631001631998372">"ઇન્સ્ટોલ કરી રહ્યું છે…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ને ઇન્સ્ટૉલ કરી રહ્યાં છીએ…"</string>
+    <string name="install_done" msgid="3682715442154357097">"ઍપ્લિકેશન ઇન્સ્ટોલ કરી."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"શું તમે આ ઍપ્લિકેશન ઇન્સ્ટોલ કરવા માંગો છો? તે આની ઍક્સેસ મેળવશે:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"શું તમે આ એપ્લિકેશનને ઇન્સ્ટોલ કરવા માંગો છો? તેને કોઈપણ વિશિષ્ટ ઍક્સેસની જરૂર નથી."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"શું તમે આ અસ્તિત્વમાંની એપ્લિકેશનના અપડેટને ઇન્સ્ટોલ કરવા માગો છો? તમારો અસ્તિત્વમાંનો ડેટા ગુમ થશે નહીં. અપડેટ કરેલ એપ્લિકેશનને આની ઍક્સેસ મળશે:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"શું તમે આ બિલ્ટ-ઇન એપ્લિકેશનના અપડેટને ઇન્સ્ટોલ કરવા માગો છો? તમારો અસ્તિત્વમાંનો ડેટા ગુમ થશે નહીં. અપડેટ કરેલ એપ્લિકેશનને આની ઍક્સેસ મળશે:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"શું તમે આ અસ્તિત્વમાંની એપ્લિકેશનના અપડેટને ઇન્સ્ટોલ કરવા માગો છો? તમારો અસ્તિત્વમાંનો ડેટા ગુમ થશે નહીં. તેને કોઈ વિશિષ્ટ ઍક્સેસની જરૂર હોતી નથી."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"શું તમે આ બિલ્ટ-ઇન એપ્લિકેશનના અપડેટને ઇન્સ્ટોલ કરવા માગો છો? તમારો અસ્તિત્વમાંનો ડેટા ગુમ થશે નહીં. તેને કોઈ વિશિષ્ટ ઍક્સેસની જરૂર હોતી નથી."</string>
+    <string name="install_failed" msgid="6579998651498970899">"ઍપ્લિકેશન ઇન્સ્ટોલ કરેલ નથી."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"પૅકેજને ઇન્સ્ટૉલ થવાથી અવરોધિત કરવામાં આવ્યું હતું."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"પૅકેજનો અસ્તિત્વમાંના પૅકેજ સાથે વિરોધાભાસ હોવાને કારણે ઍપ્લિકેશન ઇન્સ્ટૉલ થઈ નથી."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"તમારા ટેબ્લેટ સાથે ઍપ્લિકેશન સુસંગત ન હોવાને કારણે ઍપ્લિકેશન ઇન્સ્ટૉલ થઈ નથી."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"આ અ‍ૅપ્લિકેશન તમારા ટીવી સાથે સુસંગત નથી."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"તમારા ફોન સાથે ઍપ્લિકેશન સુસંગત ન હોવાને કારણે ઍપ્લિકેશન ઇન્સ્ટૉલ થઈ નથી."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"પૅકેજ અમાન્ય લાગી રહ્યું હોવાને કારણે ઍપ્લિકેશન ઇન્સ્ટૉલ થઈ નથી."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"તમારા ટેબ્લેટ પર <xliff:g id="APP_NAME">%1$s</xliff:g> ઇન્સ્ટોલ કરી શકાયું નથી."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"તમારા ટીવી પર <xliff:g id="APP_NAME">%1$s</xliff:g> ઇન્સ્ટોલ કરી શકાયું નથી."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"તમારા ફોન પર <xliff:g id="APP_NAME">%1$s</xliff:g> ઇન્સ્ટોલ કરી શકાયું નથી."</string>
+    <string name="launch" msgid="4826921505917605463">"ખોલો"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"તમારા વ્યવસ્થાપક અજાણ્યા સ્રોતોથી મેળવેલ ઍપ્લિકેશનોના ઇન્સ્ટૉલેશનની મંજૂરી આપતા નથી"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"આ વપરાશકર્તા અજાણી ઍપ્લિકેશનો ઇન્સ્ટૉલ કરી શકશે નહીં"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"આ વપરાશકર્તાને ઍપ્લિકેશનો ઇન્સ્ટૉલ કરવાની મંજૂરી નથી"</string>
+    <string name="ok" msgid="3468756155452870475">"ઓકે"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"એપ્લિકેશન્સનું સંચાલન કરો"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"જગ્યાની બહાર"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> ઇન્સ્ટોલ કરી શકાઈ નથી. થોડી સ્પેસ ખાલી કરો અને ફરીથી પ્રયાસ કરો."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"ઍપ્લિકેશન મળી નથી"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"ઇન્સ્ટોલ કરેલ ઍપ્લિકેશન્સની સૂચિમાં ઍપ્લિકેશન મળી નહોતી."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"મંજૂરી નથી"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"વર્તમાન વપરાશકર્તાને આ અનઇન્સ્ટૉલેશન કરવાની મંજૂરી નથી."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"ભૂલ"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"ઍપ્લિકેશન અનઇન્સ્ટૉલ કરી શકાઈ નહીં."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"ઍપ્લિકેશન અનઇન્સ્ટોલ કરો"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"અપડેટ અનઇન્સ્ટોલ કરો"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> એ નીચેની એપ્લિકેશનનો એક ભાગ છે:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"શું તમે આ એપ્લિકેશનને અનઇન્સ્ટોલ કરવા માંગો છો?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"શું તમે "<b>"તમામ"</b>" વપરાશકર્તાઓ માટે આ ઍપ્લિકેશનને અનઇન્સ્ટોલ કરવા માગો છો? ઉપકરણ પરના "<b>"તમામ"</b>" વપરાશકર્તાઓમાંથી ઍપ્લિકેશન અને તેનો ડેટા દૂર કરવામાં આવશે."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"શું તમે <xliff:g id="USERNAME">%1$s</xliff:g> વપરાશકર્તા માટે આ એપ્લિકેશનને અનઇન્સ્ટોલ કરવા માગો છો?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"આ ઍપ્લિકેશનને ફેક્ટરી સંસ્કરણથી બદલીએ? તમામ ડેટા દૂર કરવામાં આવશે."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"આ ઍપ્લિકેશનને ફેક્ટરી સંસ્કરણથી બદલીએ? તમામ ડેટા દૂર કરવામાં આવશે. આનાથી કાર્ય પ્રોફાઇલ્સ સાથેના વપરાશકર્તાઓ સહિત આ ઉપકરણના તમામ વપરાશકર્તાઓ પ્રભાવિત થશે."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"ચાલી રહેલા અનઇન્સ્ટૉલ"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"નિષ્ફળ થયેલા અનઇન્સ્ટૉલ"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"અનઇન્સ્ટોલ કરી રહ્યું છે..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ને અનઇન્સ્ટૉલ કરી રહ્યાં છે…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"અનઇન્સ્ટોલ કરો સમાપ્ત થયું."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> અનઇન્સ્ટૉલ કર્યું"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"અનઇન્સ્ટોલ કરવું અસફળ રહ્યું."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ને અનઇન્સ્ટૉલ કરવું અસફળ રહ્યું."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"સક્રિય ઉપકરણ વ્યવસ્થાપક ઍપ્લિકેશનોને અનઇન્સ્ટૉલ કરી શકાતી નથી"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> માટે સક્રિય ઉપકરણ વ્યવસ્થાપક ઍપ્લિકેશનોને અનઇન્સ્ટૉલ કરી શકાતી નથી"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"આ અ‍ૅપ્લિકેશન અમુક વપરાશકર્તાઓ અથવા પ્રોફાઇલ્સ માટે જરૂરી છે અને તે અન્ય લોકો માટે અનઇન્સ્ટૉલ કરી હતી"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"તમારી કાર્યાલયની પ્રોફાઇલ માટે ઍપ્લિકેશન જરૂરી છે અને અનઇન્સ્ટૉલ કરી શકાતી નથી."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"આ ઍપ્લિકેશન તમારા ઉપકરણ વ્યવસ્થાપક માટે આવશ્યક છે અને તે અનઇન્સ્ટોલ કરી શકાતી નથી."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"ઉપકરણ વ્યવસ્થાપક ઍપ્લિકેશનોનું સંચાલન કરો"</string>
+    <string name="manage_users" msgid="3125018886835668847">"વપરાશકર્તાઓનું સંચાલન કરો"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> અનઇન્સ્ટોલ કરી શકાઈ નથી."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"પૅકેજનું વિશ્લેષણ કરવામાં એક સમસ્યા આવી હતી."</string>
+    <string name="newPerms" msgid="6039428254474104210">"નવું"</string>
+    <string name="allPerms" msgid="1024385515840703981">"તમામ"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"ગોપનીયતા"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"ઉપકરણ ઍક્સેસ"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"આ અપડેટને કોઈ નવી પરવાનગીઓની જરૂર નથી."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"નકારો"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"વધુ માહિતી"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"કોઇપણ રીતે નકારો"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> માંથી <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ને <xliff:g id="ACTION">%2$s</xliff:g> મંજૂરી આપીએ?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને હંમેશા <xliff:g id="ACTION">%2$s</xliff:g>ની મંજૂરી આપીએ?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"માત્ર ઍપનો ઉપયોગ કરતી વખતે જ"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"હંમેશા"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"નકારો અને ફરીથી પૂછશો નહીં"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> અક્ષમ કરી"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"તમામ અક્ષમ કરી"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"કોઈપણ અક્ષમ કરેલ નથી"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"મંજૂરી આપો"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"ઍપ્લિકેશનો"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"ઍપ્લિકેશન પરવાનગીઓ"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"ફરીથી પૂછશો નહીં"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"કોઈ પરવાનગીઓ નથી"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"વધારાની પરવાનગીઓ"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"ઍપ માહિતી ખોલો"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <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="old_sdk_deny_warning" msgid="3872277112584842615">"આ ઍપ્લિકેશન Android ના જુના સંસ્કરણ માટે તૈયાર કરવામાં આવી હતી. પરવાનગી નકારવાથી તે ધાર્યા પ્રમાણે બિલકુલ કાર્ય કરશે નહી."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"એક અજાણી ક્રિયા કરો"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g> માંથી <xliff:g id="COUNT_0">%1$d</xliff:g> એપ્લિકેશન્સને મંજૂરી છે"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"સિસ્ટમ બતાવો"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"સિસ્ટમ છુપાવો"</string>
+    <string name="no_apps" msgid="1965493419005012569">"કોઇ ઍપ્લિકેશનો નથી"</string>
+    <string name="location_settings" msgid="1774875730854491297">"સ્થાન સેટિંગ્સ"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> એ આ ઉપકરણ માટે સ્થાન સેવાઓના પ્રદાતા છે. સ્થાન સેટિંગ્સમાંથી સ્થાન ઍક્સેસ સંશોધિત કરી શકાય છે."</string>
+    <string name="system_warning" msgid="7103819124542305179">"જો તમે આ પરવાનગી નકારો છો, તો તમારા ઉપકરણની મૂળભૂત સુવિધાઓ અપેક્ષા પ્રમાણે કાર્ય કરી શકશે નહીં."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"નીતિ દ્વારા લાગુ"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"નીતિ દ્વારા બૅકગ્રાઉન્ડ ઍક્સેસને બંધ કરવામાં આવ્યો છે"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"નીતિ દ્વારા બૅકગ્રાઉન્ડ ઍક્સેસને ચાલુ કરવામાં આવ્યો છે"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"નીતિ દ્વારા ફૉરગ્રાઉન્ડ ઍક્સેસને ચાલુ કરવામાં આવ્યો છે"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"વ્યવસ્થાપક દ્વારા નિયંત્રિત"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"હંમેશા"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"માત્ર ઍપનો ઉપયોગ કરતી વખતે જ"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"ક્યારેય નહીં"</string>
+    <string name="loading" msgid="7811651799620593731">"લોડ કરી રહ્યું છે..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"બધી પરવાનગીઓ"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"અન્ય ઍપ્લિકેશન ક્ષમતાઓ"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"પરવાનગીની વિનંતી"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"સ્ક્રીન ઓવરલે મળ્યું"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"આ પરવાનગી સેટિંગ બદલવા માટે, તમારે પહેલા સેટિંગ્સ &gt; Apps માંથી સ્ક્રીન ઓવરલે બંધ કરવું પડશે"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"સેટિંગ્સ ખોલો"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear પર ઇન્સ્ટૉલ/અનઇન્સ્ટૉલ ક્રિયાઓ સમર્થિત નથી."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ને શેના ઍક્સેસ માટેની મંજૂરી આપવી તે પસંદ કરો"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; અપડેટ કરવામાં આવી છે. આ ઍપ્લિકેશનને શેના ઍક્સેસ માટેની મંજૂરી આપવી તે પસંદ કરો."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"રદ કરો"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"ચાલુ રાખો"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"નવી પરવાનગીઓ"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"વર્તમાન પરવાનગીઓ"</string>
+    <string name="message_staging" msgid="6151794817691100003">"ઍપ્લિકેશન અમલમં છે..."</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"અજાણી"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"તમારી સુરક્ષા માટે, તમારા ટૅબ્લેટને આ સ્રોત પરથી અજાણી ઍપ્લિકેશનો ઇન્સ્ટૉલ કરવાની મંજૂરી નથી."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"તમારી સુરક્ષા માટે, તમારા ટીવીને આ સ્રોત પરથી અજાણી ઍપ્લિકેશનો ઇન્સ્ટૉલ કરવાની મંજૂરી નથી."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"તમારી સુરક્ષા માટે, તમારા ફોનને આ સ્રોત પરથી અજાણી ઍપ્લિકેશનો ઇન્સ્ટૉલ કરવાની મંજૂરી નથી."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"તમારો ફોન અને વ્યક્તિગત ડેટા અજાણી ઍપ્લિકેશનો દ્વારા હુમલા માટે વધુ સંવેદનશીલ છે. આ ઍપ્લિકેશન ઇન્સ્ટૉલ કરીને તમે સંમત થાઓ છો કે આનો ઉપયોગ કરવાથી તમારા ફોનને થતી કોઈપણ હાનિ અથવા ડેટાના નુકસાન માટે તમે જવાબદાર છો."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"તમારું ટૅબ્લેટ અને વ્યક્તિગત ડેટા અજાણી ઍપ્લિકેશનો દ્વારા હુમલા માટે વધુ સંવેદનશીલ છે. આ ઍપ્લિકેશન ઇન્સ્ટૉલ કરીને તમે સંમત થાઓ છો કે આનો ઉપયોગ કરવાથી તમારા ટૅબ્લેટને થતી કોઈપણ હાનિ અથવા ડેટાના નુકસાન માટે તમે જવાબદાર છો."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"તમારું ટીવી અને વ્યક્તિગત ડેટા અજાણી ઍપ્લિકેશનો દ્વારા હુમલા માટે વધુ સંવેદનશીલ છે. આ  ઍપ્લિકેશન ઇન્સ્ટૉલ કરીને તમે સંમત થાઓ છો કે આનો ઉપયોગ કરવાથી તમારા ટીવીને થતી કોઈપણ હાનિ અથવા ડેટાના નુકસાન માટે તમે જવાબદાર છો."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"ચાલુ રાખો"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"સેટિંગ્સ"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"એમ્બેડ ઍપ્લિકેશનો ઇન્સ્ટૉલ/અનઇન્સ્ટૉલ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-hi-television/strings.xml b/packages/PackageInstaller/res/values-hi-television/strings.xml
new file mode 100644
index 0000000..2acd9c5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hi-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"मना करें और फिर से ना पूछें"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"आप इसे बाद में सेटिंग &gt; ऐप्‍स में बदल सकते हैं"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"सिस्टम ऐप्स दिखाएं"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"ऐप्लिकेशन अनुमतियां"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"ऐप्लिकेशन अनुमतियां"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> अनुमतियां"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"और अनुमतियां"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> अनुमतियां"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-hi-watch/strings.xml b/packages/PackageInstaller/res/values-hi-watch/strings.xml
new file mode 100644
index 0000000..f7adb0c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hi-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"मना करें, फिर से ना पूछें"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"सिस्टम ऐप्स दिखाएं"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"बदला नहीं जा सकता"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"हां"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"रद्द करें"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-hi/strings.xml b/packages/PackageInstaller/res/values-hi/strings.xml
new file mode 100644
index 0000000..5ddddbe
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hi/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"पैकेज इंस्‍टॉलर"</string>
+    <string name="next" msgid="3057143178373252333">"आगे"</string>
+    <string name="install" msgid="5896438203900042068">"इंस्‍टॉल करें"</string>
+    <string name="done" msgid="3889387558374211719">"हो गया"</string>
+    <string name="cancel" msgid="8360346460165114585">"रद्द करें"</string>
+    <string name="installing" msgid="8613631001631998372">"इंस्‍टॉल कर रहा है…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> इंस्टॉल हो रहा है…"</string>
+    <string name="install_done" msgid="3682715442154357097">"ऐप्स  इंस्‍टॉल हो गया."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"क्‍या आप इस ऐप्स को इंस्‍टॉल करना चाहते हैं? इससे यहां पर पहुंच प्राप्त होगी:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"क्‍या आप इस ऐप्स को इंस्‍टॉल करना चाहते हैं? इसके लिए किसी विशेष पहुंच की आवश्‍यकता नहीं है."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"क्‍या आप इस मौजूदा ऐप के बारे में नई जानकारी इंस्‍टॉल करना चाहते हैं? आपका मौजूदा डेटा गुम नहीं होगा. अपडेट किये गए ऐप से आपको इन पर पहुंच मिलेगी:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"क्‍या आप इस बिल्ट-इन ऐप के बारे में नई जानकारी इंस्‍टॉल करना चाहते हैं? आपका मौजूदा डेटा गुम नहीं होगा. नई जानकारी वाले ऐप से आपको इन पर मिलेगी:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"क्या आप इस मौजूदा ऐप में नई जानकारी इंस्टॉल करना चाहते हैं? आपका मौजूदा डेटा बना रहेगा. इसे किसी खास पहुंच की ज़रुरत नहीं होती."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"क्या आप इस मौजूदा ऐप में नई जानकारी इंस्टॉल करना चाहते हैं? आपका मौजूदा डेटा बना रहेगा. इसे किसी खास पहुंच की ज़रुरत नहीं होती."</string>
+    <string name="install_failed" msgid="6579998651498970899">"ऐप्स  इंस्‍टॉल नहीं हुआ."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"पैकेज को इंस्टॉल होने से अवरुद्ध किया हुआ है."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"ऐप्लिकेशन इंस्टॉल नहीं हुआ क्योंकि पैकेज का किसी मौजूदा पैकेज से विरोध है."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"ऐप्लिकेशन इंस्टॉल नहीं हुआ क्योंकि ऐप्लिकेशन आपके टैबलेट से संगत नहीं है."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"यह ऐप आपके टीवी के संगत नहीं है."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"ऐप्लिकेशन इंस्टॉल नहीं हुआ क्योंकि ऐप्लिकेशन आपके फ़ोन से संगत नहीं है."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"ऐप्लिकेशन इंस्टॉल नहीं हुआ क्योंकि पैकेज अमान्य लग रहा है."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> को आपके टैबलेट पर इंस्‍टॉल नहीं किया जा सका."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> को आपके टीवी पर इंस्‍टॉल नहीं किया जा सकता."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> को आपके फ़ोन पर इंस्‍टॉल नहीं किया जा सका."</string>
+    <string name="launch" msgid="4826921505917605463">"खोलें"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"आपका व्यवस्थापक अनजान स्रोतों से मिलने वाले ऐप्लिकेशन को इंस्टॉल करने की अनुमति नहीं देता है"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"यह उपयोगकर्ता अनजान ऐप्लिकेशन इंस्टॉल नहीं कर सकता"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"इस उपयोगकर्ता ऐप्लिकेशन इंस्टॉल करने की अनुमति नहीं है"</string>
+    <string name="ok" msgid="3468756155452870475">"ठीक है"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"ऐप्स  प्रबंधित करें"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"जगह नहीं है"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> को इंस्‍टॉल नहीं किया जा सका. थोड़ी जगह खाली करें और फिर से कोशिश करें."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"ऐप्स  नहीं मिला"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"ऐप्स , इंस्‍टॉल किए गए ऐप्स  की सूची में नहीं मिला था."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"अनुमति नहीं है"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"मौजूदा उपयोगकर्ता को यह अनइंस्टॉल करने की अनुमति नहीं है"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"गड़बड़ी"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"ऐप्लिकेशन अनइंस्टॉल नहीं किया जा सका."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"ऐप्स अनइंस्‍टॉल करें"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"अपडेट अनइंस्‍टॉल करें"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> निम्‍न ऐप्स  का भाग है:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"क्‍या आप इस ऐप्स  को अनइंस्‍टॉल करना चाहते हैं?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"क्या आप इस ऐप्स  को "<b>"सभी"</b>" उपयोगकर्ताओं के लिए अनइंस्टॉल करना चाहते हैं? ऐप्स  और उसके डेटा को डिवाइस पर "<b>"सभी"</b>" उपयोगकर्ताओं से निकाल दिया जाएगा."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"क्या आप उपयोगकर्ता <xliff:g id="USERNAME">%1$s</xliff:g> के लिए इस ऐप को अनइंस्टॉल करना चाहते हैं?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"इस ऐप्लिकेशन को फ़ैक्टरी वर्शन से बदलें? सभी डेटा निकाल दिया जाएगा."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"इस ऐप्लिकेशन को फ़ैक्ट्री वर्शन से बदलें? पूरा डेटा निकाल दिया जाएगा. इसका इस डिवाइस के सभी उपयोगकर्ताओं पर असर पड़ेगा, जिनमें कार्य प्रोफ़ाइल वाले उपयोगकर्ता शामिल हैं."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"वे अनइंस्टॉल जो चल रहे हैं"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"वे अनइंस्टॉल जो सफल नहीं रहे"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"अनइंस्‍टॉल कर रहा है…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> अनइंस्टॉल किया जा रहा है…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"अनइंस्‍टॉल करना पूर्ण हो गया."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> अनइंस्टॉल किया गया"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"अनइंस्‍टॉल करना विफल."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> को अनइंस्टॉल करना असफल."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"सक्रिय डिवाइस व्यवस्थापक ऐप्लिकेशन को अनइंस्टॉल नहीं किया जा सकता"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> के लिए सक्रिय डिवाइस व्यवस्थापक ऐप्लिकेशन को अनइंस्टॉल नहीं किया जा सकता"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"यह ऐप्लिकेशन कुछ उपयोगकर्ताओं या प्रोफ़ाइल हेतु आवश्यक है और अन्य हेतु अनइंस्टॉल हो गया है"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"आपकी कार्य प्रोफ़ाइल के लिए यह ऐप्लिकेशन आवश्यक है और उसे अनइंस्टॉल नहीं किया जा सकता."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"आपके डिवाइस व्यवस्थापक के लिए यह ऐप्स जरूरी है व इसे अनइंस्टॉल नहीं किया जा सकता."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"डिवाइस व्यवस्थापक ऐप्लिकेशन प्रबंधित करें"</string>
+    <string name="manage_users" msgid="3125018886835668847">"उपयोगकर्ताओं को प्रबंधित करें"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> को अनइंस्‍टॉल नहीं किया जा सका."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"पैकेज को पार्स करने में कोई समस्‍या थी."</string>
+    <string name="newPerms" msgid="6039428254474104210">"नया"</string>
+    <string name="allPerms" msgid="1024385515840703981">"सभी"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"निजता"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"डिवाइस पहुंच"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"इस अपडेट लिए अनुमति की ज़रुरत नहीं है."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"अस्वीकार करें"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"अधिक जानकारी"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"फिर भी अस्वीकार करें"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> में से <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को <xliff:g id="ACTION">%2$s</xliff:g> की अनुमति दें?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को हमेशा <xliff:g id="ACTION">%2$s</xliff:g> की अनुमति दें?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"सिर्फ़ ऐप्लिकेशन इस्तेमाल करते समय"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"हमेशा"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"अनुमति न दें और दोबारा न पूछें"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> अक्षम"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"सभी अक्षम हैं"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"कोई भी अक्षम नहीं है"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"अनुमति दें"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"ऐप्लिकेशन"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"ऐप्लिकेशन अनुमतियां"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"फिर से न पूछें"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"कोई अनुमति नहीं"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"और अनुमतियां"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"ऐप्लिकेशन से जुड़ी जानकारी देखें"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <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="old_sdk_deny_warning" msgid="3872277112584842615">"इस ऐप को Android के पुराने वर्शन के लिए डिज़ाइन किया गया था. अनुमति अस्वीकार करने पर हो सकता है कि फ़ंक्शन लक्षित रूप से काम नहीं करे."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"कोई अज्ञात कार्रवाई करें"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g> में से <xliff:g id="COUNT_0">%1$d</xliff:g> ऐप्लिकेशन को अनुमति है"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"सिस्टम दिखाएं"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"सिस्टम छिपाएं"</string>
+    <string name="no_apps" msgid="1965493419005012569">"कोई ऐप्स नहीं"</string>
+    <string name="location_settings" msgid="1774875730854491297">"जगह की सेटिंग"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> इस डिवाइस के लिए जगह की जानकारी उपलब्‍ध कराता है. जगह की पहुंच (एक्सेस) को जगह की सेटिंग से बदला जा सकता है."</string>
+    <string name="system_warning" msgid="7103819124542305179">"यदि आप इस अनुमति को अस्वीकार करते हैं, तो हो सकता है कि आपके डिवाइस की मूलभूत सुविधाएं लक्षित कार्य ना कर पाएं."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"नीति द्वारा लागू"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"नीति के मुताबिक बैकग्राउंड एक्सेस बंद किया गया"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"नीति के मुताबिक बैकग्राउंड एक्सेस चालू किया गया"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"नीति के मुताबिक फ़ोरग्राउंड एक्सेस चालू किया गया"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"इसका नियंत्रण एडमिन के पास है"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"हमेशा"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"ऐप्लिकेशन इस्तेमाल करते समय"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"कभी नहीं"</string>
+    <string name="loading" msgid="7811651799620593731">"लोड हो रहा है…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"सभी अनुमतियां"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"अन्‍य ऐप कार्यक्षमताएं"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"अनुमति का अनुरोध"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"स्क्रीन ओवरले मिला"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"इस अनुमति सेटिंग को बदलने के लिए, आपको पहले सेटिंग &gt; ऐप, से स्क्रीन ओवरले को बंद करना होगा"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"सेटिंग खोलें"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"इंस्टॉल/अनइंस्टॉल किए जाने की कार्रवाइयां Wear पर समर्थित नहीं हैं."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"यह चुनें कि &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को किस-किस चीज पर पहुंचने देना चाहते हैं"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपडेट कर दिया गया है. यह चुनें कि इस ऐप्लिकेशन को किस-किस चीज तक पहुंचने देना चाहते हैं."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"रद्द करें"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"जारी रखें"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"नई अनुमतियां"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"वर्तमान अनुमतियां"</string>
+    <string name="message_staging" msgid="6151794817691100003">"ऐप्लिकेशन चरणबद्ध किया जा रहा है…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"अज्ञात"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"आपकी सुरक्षा के लिए, आपके टैबलेट को इस स्रोत से आने वाले अनजान ऐप्लिकेशन इंस्टॉल करने की अनुमति नहीं है."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"आपकी सुरक्षा के लिए, आपके टीवी को इस स्रोत से आने वाले अनजान ऐप्लिकेशन इंस्टॉल करने की अनुमति नहीं है."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"आपकी सुरक्षा के लिए, आपके फ़ोन को इस स्रोत से आने वाले अनजान ऐप्लिकेशन इंस्टॉल करने की अनुमति नहीं है."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"आपका फ़ोन और व्यक्तिगत डेटा अज्ञात ऐप्लिकेशन के हमले के प्रति अधिक संवेदनशील हैं. इस ऐप्लिकेशन को इंस्टॉल करके आप सहमति देते हैं कि इसके उपयोग के चलते आपके फ़ोन को होने वाले किसी भी नुकसान या डेटा की हानि के लिए आप ज़िम्मेदार हैं."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"आपका टैबलेट और व्यक्तिगत डेटा अज्ञात ऐप्लिकेशन के हमले के प्रति अधिक संवेदनशील हैं. इस ऐप्लिकेशन को इंस्टॉल करके आप सहमति देते हैं कि इसके उपयोग के चलते आपके टैबलेट को होने वाले किसी भी नुकसान या डेटा की हानि के लिए आप ज़िम्मेदार हैं."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"आपका टीवी और व्यक्तिगत डेटा अज्ञात ऐप्लिकेशन के हमले के प्रति अधिक संवेदनशील हैं. इस ऐप्लिकेशन को इंस्टॉल करके आप सहमति देते हैं कि इसके उपयोग के चलते आपके टीवी को होने वाले किसी भी नुकसान या डेटा की हानि के लिए आप ज़िम्मेदार हैं."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"जारी रखें"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"सेटिंग"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"वियर ऐप इंस्टॉल/अनइंस्टॉल हो रहे हैं"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-hr-television/strings.xml b/packages/PackageInstaller/res/values-hr-television/strings.xml
new file mode 100644
index 0000000..ba363f4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hr-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Odbij i više ne pitaj"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"To možete kasnije promijenili u odjeljku Postavke &gt; Aplikacije"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Prikaži aplikacije sustava"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Dopuštenja aplikacije"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Dopuštenja aplikacije"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Dopuštenja – <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Dodatna dopuštenja"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Dopuštenja – <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-hr-watch/strings.xml b/packages/PackageInstaller/res/values-hr-watch/strings.xml
new file mode 100644
index 0000000..cd44eee
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hr-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Ne, više ne pitaj"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Prikaži aplikacije sustava"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Promjena nemoguća"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Da"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Odustani"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-hr/strings.xml b/packages/PackageInstaller/res/values-hr/strings.xml
new file mode 100644
index 0000000..3884ae5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hr/strings.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Alat za instaliranje paketa"</string>
+    <string name="next" msgid="3057143178373252333">"Sljedeća"</string>
+    <string name="install" msgid="5896438203900042068">"Instaliraj"</string>
+    <string name="done" msgid="3889387558374211719">"Gotovo"</string>
+    <string name="cancel" msgid="8360346460165114585">"Odustani"</string>
+    <string name="installing" msgid="8613631001631998372">"Instaliranje..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Instaliranje paketa <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplikacija je instalirana."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Želite li instalirati ovu aplikaciju? Aplikacija će moći sljedeće:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Želite li instalirati ovu aplikaciju? Aplikacija ne zahtijeva nikakav poseban pristup."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Želite li instalirati ažuriranje postojeće aplikacije? Vaši postojeći podaci neće biti izgubljeni. Ažurirana aplikacija dobit će pristup sljedećem:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Želite li instalirati ažuriranje za ovu ugrađenu aplikaciju? Vaši postojeći podaci neće biti izgubljeni. Ažurirana aplikacija dobit će pristup sljedećem:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Želite li instalirati ažuriranje postojeće aplikacije? Vaši postojeći podaci neće se izgubiti. Nije potreban nikakav poseban pristup."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Želite li instalirati ažuriranje te ugrađene aplikacije? Vaši postojeći podaci neće se izgubiti. Nije potreban nikakav poseban pristup."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Aplikacija nije instalirana."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Instaliranje paketa blokirano je."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Aplikacija koja nije instalirana kao paket u sukobu je s postojećim paketom."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Aplikacija koja nije instalirana kao aplikacija nije kompatibilna s vašim tabletom."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Aplikacija nije kompatibilna s vašim televizorom."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Aplikacija koja nije instalirana kao aplikacija nije kompatibilna s vašim telefonom."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Aplikacija koja nije instalirana kao paket vjerojatno nije važeća."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> nije moguće instalirati na ovo tabletno računalo."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Nije bilo moguće instalirati aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> na vaš televizor."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> nije moguće instalirati na vaš telefon."</string>
+    <string name="launch" msgid="4826921505917605463">"Otvori"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Vaš administrator ne dopušta instaliranje aplikacija iz nepoznatih izvora"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Ovaj korisnik ne može instalirati nepoznate aplikacije"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Ovaj korisnik nema dopuštenje za instaliranje aplikacija"</string>
+    <string name="ok" msgid="3468756155452870475">"U redu"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Upravljanje aplikacijama"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Nema dovoljno mjesta"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> nije moguće instalirati. Oslobodite dio prostora i pokušajte ponovo."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Aplikacija nije pronađena"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Na popisu instaliranih aplikacija ova aplikacija nije pronađena."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Nije dopušteno"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Trenutačni korisnik nema dopuštenje za to deinstaliranje."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Pogreška"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Deinstaliranje aplikacije nije uspjelo."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Deinstaliraj aplikaciju"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Deinstalacija ažuriranja"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"Aktivnost <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> dio je sljedeće aplikacije:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Želite li deinstalirati ovu aplikaciju?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Želite li deinstalirati tu aplikaciju za "<b>"sve"</b>" korisnike? Aplikacija i njezini podaci bit će uklonjeni sa "<b>"svih"</b>" korisnika na uređaju."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Želite li deinstalirati tu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Želite li tu aplikaciju zamijeniti tvorničkom verzijom? Izgubit ćete sve podatke."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Ž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="uninstalling_notification_channel" msgid="5698369661583525583">"Deinstaliranja u tijeku"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Neuspjela deinstaliranja"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Deinstaliranje..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Deinstaliranje aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Deinstalacija je završena."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Aplikacija <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> deinstalirana"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Deinstalacija nije uspjela."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Deinstaliranje aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> nije uspjelo."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Deinstaliranje aktivne aplikacije administratora uređaja nije uspjelo"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Nije uspjelo deinstaliranje aktivne aplikacije administratora uređaja za <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Ta je aplikacija obavezna za neke korisnike ili profile, deinstalirana je za ostale"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Ta je aplikacija potrebna za vaš profil i ne može se deinstalirati."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Ta je aplikacija neophodna administratoru uređaja i nije ju moguće deinstalirati."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Upravljaj aplikacijama administratora uređaja"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Upravljaj korisnicima"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> nije moguće instalirati."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Došlo je do problema pri analiziranju paketa."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Novo"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Sve"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privatnost"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Pristup uređaja"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Ovo ažuriranje ne zahtijeva nove dozvole."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Odbij"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Više informacija"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Svejedno odbij"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> od <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti da može <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Želite li uvijek dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sljedeće: <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Samo dok se aplikacija koristi"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Uvijek"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Odbij i više ne pitaj"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"Onemogućeno: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"sve onemogućeno"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ništa nije onemogućeno"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Dopusti"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplikacije"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Dopuštenja aplikacije"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Više me ne pitaj"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Nema dopuštenja"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Dodatna dopuštenja"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Otvori informacije o aplikaciji"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one">Još <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="few">Još <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="other">Još <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Ova je aplikacija napravljena za stariju verziju Androida. Ako ne dobije dopuštenje, možda više neće funkcionirati kako treba."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"izvršiti nepoznatu radnju"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Aplikacije s dopuštenjem: <xliff:g id="COUNT_0">%1$d</xliff:g> od <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Prikaži sustav"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Sakrij sustav"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Nema aplikacija"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Postavke lokacije"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> pruža usluge lokacije za ovaj uređaj. Pristup lokaciji može se izmijeniti u postavkama lokacije."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Ako ne odobrite ovo dopuštenje, osnovne značajke vašeg uređaja možda više neće funkcionirati pravilno."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Provoditi se na temelju pravila"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Pristup u pozadini onemogućen je pravilima"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Pristup u pozadini omogućen je pravilima"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Pristup u prednjem planu omogućen je pravilima"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Kontrolira administrator"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Uvijek"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Samo dok se aplikacija koristi"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nikad"</string>
+    <string name="loading" msgid="7811651799620593731">"Učitavanje…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Sva dopuštenja"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Ostale mogućnosti aplikacije"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Zahtijevanje dopuštenja"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Otkriveno je preklapanje na zaslonu"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Da biste promijenili tu postavku dopuštenja, prvo morate isključiti preklapanje na zaslonu u Postavkama &gt; Aplikacije"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Otvori postavke"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Radnje instaliranja i deinstaliranja nisu podržane na Wearu."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Odaberite čemu će &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; moći pristupiti"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ažurirana je. Odaberite čemu će moći pristupiti."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Otkaži"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Nastavi"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nova dopuštenja"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Trenutačna dopuštenja"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Postavljanje aplikacije…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Nepoznato"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Iz sigurnosnih razloga tablet nema dopuštenje za instaliranje nepoznatih aplikacija iz ovog izvora."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Iz sigurnosnih razloga televizor nema dopuštenje za instaliranje nepoznatih aplikacija iz ovog izvora."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Iz sigurnosnih razloga telefon nema dopuštenje za instaliranje nepoznatih aplikacija iz ovog izvora."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Vaš telefon i osobni podaci podložniji su napadima nepoznatih aplikacija. Instaliranjem te aplikacije prihvaćate odgovornost za oštećenje telefona ili gubitak podataka do kojih može doći uslijed njezine upotrebe."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Vaš tablet i osobni podaci podložniji su napadima nepoznatih aplikacija. Instaliranjem te aplikacije prihvaćate odgovornost za oštećenje tableta ili gubitak podataka do kojih može doći uslijed njezine upotrebe."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Vaš TV i osobni podaci podložniji su napadima nepoznatih aplikacija. Instaliranjem te aplikacije prihvaćate odgovornost za oštećenje televizora ili gubitak podataka do kojih može doći uslijed njezine upotrebe."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Nastavi"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Postavke"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Instaliranje/deinstaliranje Wear apl."</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-hu-television/strings.xml b/packages/PackageInstaller/res/values-hu-television/strings.xml
new file mode 100644
index 0000000..0557700
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hu-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Megtagadás, és ne jelenjen meg többé"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Ezt később módosíthatja a Beállítások &gt; Alkalmazások pontnál"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Rendszeralkalmazások megjelenítése"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Alkalmazásengedélyek"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Alkalmazásengedélyek"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> – jogosultságok"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"További engedélyek"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> – jogosultságok"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-hu-watch/strings.xml b/packages/PackageInstaller/res/values-hu-watch/strings.xml
new file mode 100644
index 0000000..8ae3504
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hu-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Megtagadás, ne jelenjen meg"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>/<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Rendszeralkalmazások megjelenítése"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Nem változtatható"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Igen"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Mégse"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-hu/strings.xml b/packages/PackageInstaller/res/values-hu/strings.xml
new file mode 100644
index 0000000..5e96550
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hu/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Csomagtelepítő"</string>
+    <string name="next" msgid="3057143178373252333">"Tovább"</string>
+    <string name="install" msgid="5896438203900042068">"Telepítés"</string>
+    <string name="done" msgid="3889387558374211719">"Kész"</string>
+    <string name="cancel" msgid="8360346460165114585">"Mégse"</string>
+    <string name="installing" msgid="8613631001631998372">"Telepítés..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> telepítése…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Alkalmazás telepítve."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Telepíti ezt az alkalmazást? Az a következőkhöz fog hozzáférést kapni:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Telepíti ezt az alkalmazást? Az alkalmazás nem igényel különleges hozzáférést."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Telepít egy frissítést ehhez a meglévő alkalmazáshoz? A meglévő adatai nem vesznek el. A frissített alkalmazás a következőkhöz kap hozzáférést:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Telepít egy frissítést ehhez a beépített alkalmazáshoz? A meglévő adatai nem vesznek el. A frissített alkalmazás a következőkhöz kap hozzáférést:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Telepít egy frissítést ehhez a meglévő alkalmazáshoz? A meglévő adatai nem vesznek el. A frissítés nem igényel különleges hozzáférést."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Telepít egy frissítést ehhez a beépített alkalmazáshoz? A meglévő adatai nem vesznek el. A frissítés nem igényel különleges hozzáférést."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Az alkalmazás nincs telepítve."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"A csomag telepítését letiltotta a rendszer."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"A nem csomagként telepített alkalmazás ütközik egy már létező csomaggal."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"A nem alkalmazásként telepített alkalmazás nem kompatibilis az Ön táblagépével."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Ez az alkalmazás nem kompatibilis tévéjével."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"A nem alkalmazásként telepített alkalmazás nem kompatibilis az Ön telefonjával."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"A nem csomagként telepített alkalmazás érvénytelen."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás nem telepíthető táblagépére."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás nem telepíthető a tévéjére."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás nem telepíthető telefonjára."</string>
+    <string name="launch" msgid="4826921505917605463">"Megnyitás"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"A rendszergazda nem engedélyezi az ismeretlen forrásokból származó alkalmazások telepítését"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Ez a felhasználó nem telepíthet ismeretlen alkalmazásokat"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Ez a felhasználó nem telepíthet alkalmazásokat"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Alkalmazások kezelése"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Nincs elég hely"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazást nem lehet telepíteni. Szabadítson fel egy kis helyet, és próbálja újra."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Az alkalmazás nem található"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Az alkalmazás nem található a telepített alkalmazások listájában."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Nem engedélyezett"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"A jelenlegi felhasználó számára nem engedélyezett az eltávolítás végrehajtása."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Hiba"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Az alkalmazás nem távolítható el."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Alkalmazás eltávolítása"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Frissítés eltávolítása"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"A(z) <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> a következő alkalmazás része:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Eltávolítja ezt az alkalmazást?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Szeretné eltávolítani ezt az alkalmazást "<b>"minden"</b>" felhasználónál? Az alkalmazást és adatait az eszköz "<b>"minden"</b>" felhasználójánál töröljük."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"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="1394549691152728409">"Lecseréli az alkalmazást a gyári verzióra? Minden adat törlődik."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Futó telepítések"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Sikertelen telepítések"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Eltávolítás..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"A(z) <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> eltávolítása folyamatban van…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Az eltávolítás befejeződött."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"A(z) <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> eltávolítása befejeződött"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Az eltávolítás sikertelen."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"A(z) <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> eltávolítása nem sikerült."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Nem lehet eltávolítani az aktív eszközrendszergazdai alkalmazást"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Nem lehet eltávolítani az aktív eszközrendszergazdai alkalmazást <xliff:g id="USERNAME">%1$s</xliff:g> felhasználó esetében"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Egyes felhasználóknak/profiloknak szüksége van erre, másoknál pedig eltávolították"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Ez az alkalmazás szükséges a profiljához, így nem távolítható el."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Az alkalmazásra szüksége van az eszköz adminisztrátorának, és nem távolítható el."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Eszközrendszergazdai alkalmazások kezelése"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Felhasználók kezelése"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Nem sikerült a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> eltávolítása"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Gond volt a csomag elemzésekor."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Új"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Mind"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Adatvédelem"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Eszközhozzáférés"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"A frissítés nem igényel új engedélyeket."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Elutasítás"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"További információ"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Tiltás mindenképpen"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>/<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára a következőt: <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Mindig engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&amp;gt számára a következőt: <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Az alkalmazás használatakor"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Mindig"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Megtagadás, és ne jelenjen meg többé"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> van letiltva"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"az összes le van tiltva"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"egy sincs letiltva"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Engedélyezés"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Alkalmazások"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Alkalmazásengedélyek"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Ne jelenjen meg többé"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Nincs engedély"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"További engedélyek"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Alkalmazásinformációk megnyitása"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> további</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> további</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Ez az alkalmazás az Android egy korábbi verziójához készült. Az engedély megtagadása esetén előfordulhat, hogy a továbbiakban nem fog megfelelően működni."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"végrehajt egy ismeretlen műveletet"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g>/<xliff:g id="COUNT_0">%1$d</xliff:g> alkalmazás engedélyezve"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Rendszerfolyamatok megjelenítése"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Rendszerfolyamatok elrejtése"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Nincsenek alkalmazások"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Helybeállítások"</string>
+    <string name="location_warning" msgid="8778701356292735971">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> helyszolgáltatásokat biztosít ennek az eszköznek. A helyhozzáférést a helybeállításokban lehet módosítani."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Ha ezt nem engedélyezi, akkor előfordulhat, hogy az eszköz egyes alapfunkciói nem megfelelően fognak működni."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Irányelv által kényszerítve"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Háttérhozzáférés házirend által letiltva"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Háttérhozzáférés házirend által engedélyezve"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Előtérbeli hozzáférés házirend által engedélyezve"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Rendszergazda által irányítva"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Mindig"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Az alkalmazás használatakor"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Soha"</string>
+    <string name="loading" msgid="7811651799620593731">"Betöltés…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Az összes engedély"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Egyéb alkalmazáslehetőségek"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Engedélykérés"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Képernyőfedvény észlelve"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Ennek az engedélynek a módosításához először ki kell kapcsolnia a képernyőfedvényt a Beállítások &gt; Alkalmazások menüben"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Beállítások megnyitása"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"A Wear nem támogatja a telepítés/eltávolítás műveletet."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Válassza ki, hogy a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mihez férjen hozzá"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"A(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; frissítése megtörtént. Válassza ki, hogy mihez férjen hozzá ez az alkalmazás."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Mégse"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Tovább"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Új engedélyek"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Jelenlegi engedélyek"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Alkalmazás fokozatos közzététele…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Ismeretlen"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Az Ön biztonsága érdekében táblagépe nem telepíthet ebből a forrásból származó ismeretlen alkalmazásokat."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Az Ön biztonsága érdekében tévéje nem telepíthet ebből a forrásból származó ismeretlen alkalmazásokat."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Az Ön biztonsága érdekében telefonja nem telepíthet ebből a forrásból származó ismeretlen alkalmazásokat."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Telefonja és személyes adatai fokozott kockázatnak vannak kitéve az ismeretlen alkalmazások támadásaival szemben. Az alkalmazás telepítésével elismeri, hogy Ön a felelős az alkalmazás használatából eredő esetleges adatvesztésért és a telefont ért károkért."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Táblagépe és személyes adatai fokozott kockázatnak vannak kitéve az ismeretlen alkalmazások támadásaival szemben. Az alkalmazás telepítésével elismeri, hogy Ön a felelős az alkalmazás használatából eredő esetleges adatvesztésért és a táblagépet ért károkért."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Tévéje és személyes adatai fokozott kockázatnak vannak kitéve az ismeretlen alkalmazások támadásaival szemben. Az alkalmazás telepítésével elismeri, hogy Ön a felelős az alkalmazás használatából eredő esetleges adatvesztésért és a tévét ért károkért."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Tovább"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Beállítások"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear-alkalmazások telepítése/törlése"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-hy-television/strings.xml b/packages/PackageInstaller/res/values-hy-television/strings.xml
new file mode 100644
index 0000000..f260673
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hy-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Մերժել և այլևս չհարցնել"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Կարող եք փոխել սա ավելի ուշ Կարգավորումներում և Հավելվածներում"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Ցուցադրել համակարգի հավելվածները"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Հավելվածի թույլտվությունները"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Հավելվածի թույլտվությունները"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> թույլտվությունները"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Լրացուցիչ թույլտվություններ"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> թույլտվությունները"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-hy-watch/strings.xml b/packages/PackageInstaller/res/values-hy-watch/strings.xml
new file mode 100644
index 0000000..5538858
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hy-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Մերժել, այլևս չհարցնել"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Ցուցադրել համակարգի հավելվածները"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Հնարավոր չէ փոխել"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Այո"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Չեղարկել"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-hy/strings.xml b/packages/PackageInstaller/res/values-hy/strings.xml
new file mode 100644
index 0000000..e89f1ec
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hy/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Փաթեթի տեղադրիչ"</string>
+    <string name="next" msgid="3057143178373252333">"Հաջորդը"</string>
+    <string name="install" msgid="5896438203900042068">"Տեղադրել"</string>
+    <string name="done" msgid="3889387558374211719">"Պատրաստ է"</string>
+    <string name="cancel" msgid="8360346460165114585">"Չեղարկել"</string>
+    <string name="installing" msgid="8613631001631998372">"Տեղադրվում է..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-ի տեղադրում…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Հավելվածը տեղադրված է:"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Ցանկանու՞մ եք տեղադրել այս ծրագիրը: Այն մուտքի հնարավորություն կունենա`"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Ցանկանու՞մ եք տեղադրել այս հավելվածը: Այն հատուկ մուտք չի պահանջում:"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Ցանկանու՞մ եք այս առկա հավելվածում թարմացում տեղադրել: Ձեր ընթացիկ տվյալները չեն կորի: Նորացված հավելվածը կստանա մատչում`"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Ցանկանու՞մ եք այս ներկառուցված հավելվածում թարմացում տեղադրել: Ձեր առկա տվյալները չեն կորի: Նորացված հավելվածը կստանա մատչում `"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Ցանկանու՞մ եք այս առկա հավելվածում թարմացում տեղադրել: Ձեր ընթացիկ տվյալները չեն կորի: Այն չի պահանջում որևէ հատուկ մուտք:"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Ցանկանու՞մ եք այս ներկառուցված հավելվածում թարմացում տեղադրել: Ձեր ընթացիկ տվյալները չեն կորի: Այն չի պահանջում որևէ հատուկ մուտք:"</string>
+    <string name="install_failed" msgid="6579998651498970899">"Հավելվածը տեղադրված չէ:"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Փաթեթի տեղադրումն արգելափակվել է:"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Հավելվածը չի տեղադրվել, քանի որ տեղադրման փաթեթն ունի հակասություն առկա փաթեթի հետ:"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Հավելվածը չի տեղադրվել, քանի որ այն համատեղելի չէ ձեր պլանշետի հետ:"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Այս հավելվածը համատեղելի չէ ձեր հեռուստացույցի հետ:"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Հավելվածը չի տեղադրվել, քանի որ այն համատեղելի չէ ձեր հեռախոսի հետ:"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Հավելվածը չի տեղադրվել, քանի որ տեղադրման փաթեթը, կարծես թե, վնասված է:"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ը չհաջողվեց տեղադրել ձեր պլանշետում:"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Չհաջողվեց տեղադրել <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր հեռուստացույցի վրա:"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ը չհաջողվեց տեղադրել ձեր հեռախոսում:"</string>
+    <string name="launch" msgid="4826921505917605463">"Բացել"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Ձեր ադմինիստրատորը թույլ չի տալիս տեղադրել հավելվածներ անհայտ աղբյուրներից"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Այս օգտատերը չի կարող անհայտ հավելվածներ տեղադրել"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Այս օգտատիրոջը չի թույլատրվում տեղադրել հավելվածներ"</string>
+    <string name="ok" msgid="3468756155452870475">"Հաստատել"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Կառավարել հավելվածները"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Տարածքից դուրս"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ը չհաջողվեց տեղադրել: Ազատեք որոշակի տարածք և կրկին փորձեք:"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Հավելվածը չի գտնվել"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Հավելվածը չի գտնվել տեղադրված հավելվածների ցանկում:"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Արգելված է"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Ընթացիկ օգտատերը հեռացնելու թույլտվություն չունի:"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Սխալ"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Հնարավոր չէ հեռացնել հավելվածը:"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Ապատեղադրել հավելվածը"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Ապատեղադրել թարմացումը"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>-ը հետևյալ հավելվածի մասն է`"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Ուզո՞ւմ եք ապատեղադրել այս հավելվածը։"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Ցանկանու՞մ եք ապատեղադրել այս հավելվածը "<b>"բոլոր"</b>" օգտատերերի համար:  Հավելվածը և դրա տվյալները կհեռացվեն սարքի "<b>"բոլոր"</b>" օգտատերերից:"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Ցանկանում եք ապատեղադրե՞լ այս ծրագիրը <xliff:g id="USERNAME">%1$s</xliff:g> օգտատիրոջ համար:"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Փոխարինե՞լ այս հավելվածը գործարանային տարբերակով: Բոլոր տվյալները կհեռացվեն:"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Փոխարինե՞լ այս հավելվածը գործարանային տարբերակով: Բոլոր տվյալները կհեռացվեն: Դա վերաբերում է այս սարքի բոլոր օգտատերերին, այդ թվում նաև աշխատանքային պրոֆիլներ ունեցողներին:"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Ընթացիկ հեռացումներ"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Ձախողված հեռացումներ"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Ապատեղադրում է..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> հավելվածը հեռացվում է…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Ապատեղադրումը ավարտված է:"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Հեռացված <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Ապատեղադրումն անհաջող է:"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Չհաջողվեց հեռացնել <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> հավելվածը:"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Հնարավոր չէ հեռացնել ակտիվ սարքի ադմինիստրատորի հավելվածը"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Հնարավոր չէ հեռացնել ակտիվ սարքի ադմինիստրատորի հավելվածը <xliff:g id="USERNAME">%1$s</xliff:g> օգտատիրոջ համար"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Այս հավելվածն անհրաժեշտ է որոշ օգտատերերի կամ պրոֆիլների համար և մնացածի մոտ հեռացվել է"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Այս հավելվածն անհրաժեշտ է ձեր պրոֆիլի համար: Այն հնարավոր չէ հեռացնել:"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Ծրագիրը ձեր սարքի ադմինիստրատորի կողմից նշվել է որպես պարտադիր և չի կարող հեռացվել:"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Կառավարել սարքի ադմինիստրատորի հավելվածները"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Կառավարել օգտատերերին"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ը չհաջողվեց ապատեղադրել:"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Փաթեթը վերլուծելիս խնդիր առաջացավ:"</string>
+    <string name="newPerms" msgid="6039428254474104210">"Նոր"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Բոլորը"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Գաղտնիություն"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Սարքի մատչում"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Այս թարմացումը պահանջում է, որ նոր թույլտվություններ չտրվեն:"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Մերժել"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Այլ տեղեկություններ"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Մերժել ամեն դեպքում"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>-ը <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>-ից"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին <xliff:g id="ACTION">%2$s</xliff:g>:"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Միշտ թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին <xliff:g id="ACTION">%2$s</xliff:g>:"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Միայն հավելվածն օգտագործելիս"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Միշտ"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Մերժել և այլևս չհարցնել"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"կասեցվել է <xliff:g id="COUNT">%1$d</xliff:g> թույլտվություն"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"բոլոր թույլտվությունները կասեցվել են"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ոչ մի թույլտվություն չի կասեցվել"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Թույլատրել"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Հավելվածներ"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Հավելվածների թույլտվություններ"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Այլևս չհարցնել"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Թույլտվություններ չկան"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Լրացուցիչ թույլտվություններ"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Հավելվածի մասին"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <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="old_sdk_deny_warning" msgid="3872277112584842615">"Այս հավելվածը նախատեսված է Android-ի ավելի հին տարբերակի համար: Եթե մերժեք թույլտվությունը, այն կարող է չաշխատել ինչպես հարկն է:"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"թույլատրել անհայտ գործողություն"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Թույլատրված է <xliff:g id="COUNT_0">%1$d</xliff:g> հավելվածի՝ <xliff:g id="COUNT_1">%2$d</xliff:g>-ից"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Ցուցադրել համակարգայինները"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Թաքցնել համակարգայինները"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Հավելվածներ չկան"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Տեղորոշման կարգավորումներ"</string>
+    <string name="location_warning" msgid="8778701356292735971">"Այս սարքի տեղորոշման ծառայությունները տրամադրում է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը: Տեղադրության ցուցադրման կարգավորումները կարող եք փոխել տեղադրության կարգավորումներից:"</string>
+    <string name="system_warning" msgid="7103819124542305179">"Եթե չտրամադրեք այս թույլտվությունը, ձեր սարքի հիմնական գործառույթները հնարավոր է սխալ աշխատեն:"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Սահմանված է կանոններով"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Հասանելիությունը ֆոնային ռեժիմում անջատած է կանոնի համաձայն"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Հասանելիությունը ֆոնային ռեժիմում միացված է կանոնի համաձայն"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Հասանելիությունն ակտիվ ռեժիմում միացված է կանոնի համաձայն"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Վերահսկվում է ադմինիստրատորի կողմից"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Միշտ"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Միայն հավելվածն օգտագործելիս"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Երբեք"</string>
+    <string name="loading" msgid="7811651799620593731">"Բեռնում…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Բոլոր թույլտվությունները"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Այլ հավելվածների հնարավորությունները"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Թույլտվության հարցում"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Ցուցադրում այլ պատուհանների վրա"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Այս թույլտվության կարգավորումները փոխելու համար նախ անհրաժեշտ է անջատել էկրանի վերադրումը՝ անցնելով Կարգավորումներ &gt; Հավելվածներ"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Բացել կարգավորումները"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Տեղադրման/հեռացման գործողությունները Android Wear-ում չեն աջակցվում:"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Ընտրեք, ինչ թույլտվություններ եք ցանկանում տրամադրել &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածը թարմացվել է: Ընտրեք, ինչ թույլտվություններ եք ցանկանում տրամադրել այդ հավելվածին:"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Չեղարկել"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Շարունակել"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Նոր թույլտվությունները"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Առկա թույլտվությունները"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Հավելվածի նախապատրաստում…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Անհայտ"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Անվտանգության նկատառումներից ելնելով՝ ձեր պլանշետին չի թույլատրվում այս աղբյուրից տեղադրել անհայտ հավելվածներ:"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Անվտանգության նկատառումներից ելնելով՝ ձեր հեռուստացույցին չի թույլատրվում այս աղբյուրից տեղադրել անհայտ հավելվածներ:"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Անվտանգության նկատառումներից ելնելով՝ ձեր հեռախոսին չի թույլատրվում այս աղբյուրից տեղադրել անհայտ հավելվածներ:"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Ձեր հեռախոսը և անձնական տվյալներն առավել խոցելի են անհայտ հավելվածների գրոհների նկատմամբ: Տեղադրելով այս հավելվածը՝ դուք ընդունում եք, որ պատասխանատվություն եք կրում հավելվածի օգտագործման հետևանքով ձեր հեռախոսին պատճառած ցանկացած վնասի կամ տվյալների կորստի համար:"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Ձեր պլանշետը և անձնական տվյալներն առավել խոցելի են անհայտ հավելվածների գրոհների նկատմամբ: Տեղադրելով այս հավելվածը՝ դուք ընդունում եք, որ պատասխանատվություն եք կրում հավելվածի օգտագործման հետևանքով ձեր պլանշետին պատճառած ցանկացած վնասի կամ տվյալների կորստի համար:"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Ձեր TV-ն և անձնական տվյալներն առավել խոցելի են անհայտ հավելվածների գրոհների նկատմամբ: Տեղադրելով այս հավելվածը՝ դուք ընդունում եք, որ պատասխանատվություն եք կրում հավելվածի օգտագործման հետևանքով ձեր TV-ին պատճառած ցանկացած վնասի կամ տվյալների կորստի համար:"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Շարունակել"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Կարգավորումներ"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear հավելվածների տեղադրում/հեռացում"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-in-television/strings.xml b/packages/PackageInstaller/res/values-in-television/strings.xml
new file mode 100644
index 0000000..8e6e9ab
--- /dev/null
+++ b/packages/PackageInstaller/res/values-in-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Tolak dan jangan tanya lagi"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Anda dapat mengubah ini nanti di Setelan &gt; Aplikasi"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Tampilkan aplikasi sistem"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Izin aplikasi"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Izin aplikasi"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Izin <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Izin tambahan"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Izin <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-in-watch/strings.xml b/packages/PackageInstaller/res/values-in-watch/strings.xml
new file mode 100644
index 0000000..ca2e087
--- /dev/null
+++ b/packages/PackageInstaller/res/values-in-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Tolak, jangan tanya lagi"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Tampilkan aplikasi sistem"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Tidak dapat diubah"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Ya"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Batal"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-in/strings.xml b/packages/PackageInstaller/res/values-in/strings.xml
new file mode 100644
index 0000000..afae29a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-in/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Pemasang paket"</string>
+    <string name="next" msgid="3057143178373252333">"Berikutnya"</string>
+    <string name="install" msgid="5896438203900042068">"Instal"</string>
+    <string name="done" msgid="3889387558374211719">"Selesai"</string>
+    <string name="cancel" msgid="8360346460165114585">"Batal"</string>
+    <string name="installing" msgid="8613631001631998372">"Memasang..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Menginstal <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Apl terpasang."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Apakah Anda ingin memasang aplikasi ini? Aplikasi akan memiliki akses ke:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Apakah Anda ingin memasang aplikasi ini? Aplikasi tidak memerlukan akses khusus apa pun."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Apakah Anda ingin memasang pembaruan ke aplikasi yang ada? Data Anda yang ada tidak akan hilang. Aplikasi yang diperbarui akan mendapatkan akses ke:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Apakah Anda ingin memasang pembaruan ke aplikasi yang tertanam? Data Anda yang ada tidak akan hilang. Aplikasi yang diperbarui akan mendapatkan akses ke:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Anda ingin memasang pembaruan ke aplikasi yang ada ini? Data Anda yang ada akan hilang. Tindakan ini tidak memerlukan akses khusus apa pun."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Anda ingin memasang pembaruan ke aplikasi yang ada di dalamnya? Data Anda yang ada akan hilang. Tindakan ini tidak memerlukan akses khusus apa pun."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Apl tidak terpasang."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Paket diblokir sehingga tidak dapat dipasang."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Aplikasi yang tidak dipasang sebagai paket akan bentrok dengan paket yang sudah ada."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Aplikasi yang tidak dipasang sebagai aplikasi tidak kompatibel dengan tablet Anda."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Aplikasi ini tidak kompatibel dengan TV Anda."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Aplikasi yang tidak dipasang sebagai aplikasi tidak kompatibel dengan ponsel Anda."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Aplikasi yang tidak dipasang sebagai paket tampaknya tidak valid."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak dapat dipasang pada tablet Anda."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak dapat dipasang di TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak dapat dipasang pada ponsel Anda."</string>
+    <string name="launch" msgid="4826921505917605463">"Buka"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Admin tidak mengizinkan penginstalan aplikasi yang didapatkan dari sumber tidak dikenal"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Aplikasi yang tidak dikenal tidak dapat diinstal oleh pengguna ini"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Pengguna ini tidak diizinkan menginstal aplikasi"</string>
+    <string name="ok" msgid="3468756155452870475">"Oke"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Kelola apl"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Kehabisan ruang penyimpanan"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak dapat dipasang. Kosongkan sebagian ruang dan coba lagi."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Apl tidak ditemukan"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Apl tersebut tidak ditemukan di dalam daftar apl yang terpasang."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Tidak diizinkan"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Pengguna saat ini tidak diizinkan meng-uninstal."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Kesalahan"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Aplikasi tidak dapat dipasang."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Uninstal apl"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Uninstal pembaruan"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> adalah bagian dari apl berikut:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Apakah Anda ingin meng-uninstal apl ini?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Anda ingin mencopot aplikasi ini untuk "<b>"semua"</b>" pengguna? Aplikasi dan datanya akan dihapus dari "<b>"semua"</b>" pengguna pada perangkat."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Ingin meng-uninstal aplikasi ini untuk pengguna <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Gantikan aplikasi ini dengan versi pabrik? Semua data akan dihapus."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Gantikan aplikasi ini dengan versi pabrik? Semua data akan dihapus. Tindakan ini memengaruhi semua pengguna perangkat ini, termasuk yang memiliki profil kerja."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Menjalankan uninstal"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Uninstal yang gagal"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Mencopot pemasangan..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Mencopot pemasangan <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Pencopotan pemasangan selesai."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> di-uninstal"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Pencopotan pemasangan tidak berhasil."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Gagal meng-uninstal <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Tidak dapat meng-uninstal aplikasi admin perangkat yang aktif"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Tidak dapat meng-uninstal aplikasi admin perangkat yang aktif untuk <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Aplikasi ini diperlukan untuk beberapa pengguna atau profil, dan telah dicopot pemasangannya untuk yang lainnya"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Aplikasi ini diperlukan untuk profil Anda dan tidak dapat dicopot pemasangannya."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Aplikasi diwajibkan administrator perangkat &amp; pemasangannya tidak bisa dicopot."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Kelola aplikasi admin perangkat"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Kelola pengguna"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak dapat dicopot pemasangannya."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Terjadi masalah saat mengurai paket."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Baru"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Semua"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privasi"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Akses Perangkat"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Pembaruan ini tidak memerlukan izin baru."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Tolak"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Info selengkapnya"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Tetap tolak"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> dari <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; untuk <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Selalu izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; untuk <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Hanya saat menggunakan aplikasi"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Selalu"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Tolak dan jangan tanya lagi"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> dinonaktifkan"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"semua dinonaktifkan"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"tidak ada yang dinonaktifkan"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Izinkan"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplikasi"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Izin aplikasi"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Jangan tanya lagi"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Tidak ada izin"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Izin tambahan"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Buka info aplikasi"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> lainnya</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> lainnya</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Aplikasi ini dirancang untuk versi lama Android. Menolak izin dapat menyebabkan aplikasi tidak berfungsi lagi sesuai harapan."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"melakukan tindakan yang tidak dikenal"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> dari <xliff:g id="COUNT_1">%2$d</xliff:g> aplikasi diizinkan"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Tampilkan sistem"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Sembunyikan sistem"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Tidak ada aplikasi"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Setelan Lokasi"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> adalah penyedia layanan lokasi untuk perangkat ini. Akses lokasi dapat diubah dari setelan lokasi."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Jika Anda menolak izin ini, fitur dasar perangkat mungkin tidak berfungsi lagi sesuai harapan."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Diterapkan menurut kebijakan"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Akses background dinonaktifkan oleh kebijakan"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Akses background diaktifkan oleh kebijakan"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Akses latar depan diaktifkan oleh kebijakan"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Dikontrol oleh admin"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Selalu"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Hanya saat menggunakan aplikasi"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Tidak pernah"</string>
+    <string name="loading" msgid="7811651799620593731">"Memuat…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Semua izin"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Kemampuan aplikasi lainnya"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Permintaan izin"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Hamparan layar terdeteksi"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Untuk mengubah setelan izin ini, terlebih dahulu Anda harus menonaktifkan hamparan layar dari Setelan &gt; Aplikasi"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Buka setelan"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Instal/Uninstal tidak didukung di Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Pilih item yang boleh diakses oleh &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; telah diperbarui. Pilih item yang boleh diakses oleh aplikasi ini."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Batal"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Lanjutkan"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Izin baru"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Izin saat ini"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Menyiapkan aplikasi..."</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Tidak dikenal"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Demi keamanan, TV tidak diizinkan menginstal aplikasi yang tidak dikenal dari sumber ini."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Demi keamanan, TV tidak diizinkan menginstal aplikasi yang tidak dikenal dari sumber ini."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Demi keamanan, ponsel tidak diizinkan menginstal aplikasi yang tidak dikenal dari sumber ini."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Data pribadi dan ponsel lebih rentan terhadap serangan oleh aplikasi yang tidak dikenal. Dengan menginstal aplikasi ini, Anda setuju bahwa Anda bertanggung jawab atas kerusakan ponsel atau kehilangan data yang mungkin diakibatkan oleh penggunaannya."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Data pribadi dan tablet lebih rentan terhadap serangan oleh aplikasi yang tidak dikenal. Dengan menginstal aplikasi ini, Anda setuju bahwa Anda bertanggung jawab atas kerusakan tablet atau kehilangan data yang mungkin diakibatkan oleh penggunaannya."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Data pribadi dan TV lebih rentan terhadap serangan oleh aplikasi yang tidak dikenal. Dengan menginstal aplikasi ini, Anda setuju bahwa Anda bertanggung jawab atas kerusakan TV atau kehilangan data yang mungkin diakibatkan oleh penggunaannya."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Lanjutkan"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Setelan"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Melakukan instal/uninstal aplikasi Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-is-television/strings.xml b/packages/PackageInstaller/res/values-is-television/strings.xml
new file mode 100644
index 0000000..0c1fad7
--- /dev/null
+++ b/packages/PackageInstaller/res/values-is-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Hafna og ekki spyrja aftur"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Þú getur breytt þessu seinna undir Stillingar og forrit"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Sýna kerfisforrit"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Heimildir forrita"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Heimildir forrita"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Heimildir fyrir <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Viðbótarheimildir"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Heimildir fyrir <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-is-watch/strings.xml b/packages/PackageInstaller/res/values-is-watch/strings.xml
new file mode 100644
index 0000000..b82da5e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-is-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Hafna, ekki spyrja aftur"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Sýna kerfisforrit"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Má ekki breyta"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Já"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Hætta við"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-is/strings.xml b/packages/PackageInstaller/res/values-is/strings.xml
new file mode 100644
index 0000000..ea0bdcb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-is/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Uppsetningarforrit pakka"</string>
+    <string name="next" msgid="3057143178373252333">"Áfram"</string>
+    <string name="install" msgid="5896438203900042068">"Setja upp"</string>
+    <string name="done" msgid="3889387558374211719">"Lokið"</string>
+    <string name="cancel" msgid="8360346460165114585">"Hætta við"</string>
+    <string name="installing" msgid="8613631001631998372">"Setur upp…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Setur upp <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Forritið er uppsett."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Viltu setja þetta forrit upp? Það mun fá aðgang að:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Viltu setja þetta forrit upp? Það krefst ekki neins sérstaks aðgangs."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Viltu setja upp uppfærslu á þessu uppsetta forriti? Eldri gögn glatast ekki. Uppfærða forritið mun fá aðgang að:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Viltu setja upp uppfærslu á þessu innbyggða forriti? Eldri gögn glatast ekki. Uppfærða forritið mun fá aðgang að:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Viltu setja upp uppfærslu á þessu uppsetta forriti? Eldri gögn glatast ekki. Forritið krefst ekki neins sérstaks aðgangs."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Viltu setja upp uppfærslu á þessu innbyggða forriti? Eldri gögn glatast ekki. Forritið krefst ekki neins sérstaks aðgangs."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Forritið er ekki uppsett."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Lokað var á uppsetningu pakkans."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Forritið var ekki sett upp vegna árekstra á milli pakkans og annars pakka."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Forritið var ekki sett upp því að forritið er ekki samhæft við spjaldtölvuna þína."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Þetta forrit er ekki samhæft við sjónvarpið."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Forritið var ekki sett upp því að forritið er ekki samhæft við símann þinn."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Forritið var ekki sett upp því að pakkinn virðist vera ógildur."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Ekki tókst að setja <xliff:g id="APP_NAME">%1$s</xliff:g> upp í spjaldtölvunni."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Ekki var hægt að setja <xliff:g id="APP_NAME">%1$s</xliff:g> upp í sjónvarpinu."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Ekki tókst að setja <xliff:g id="APP_NAME">%1$s</xliff:g> upp í símanum."</string>
+    <string name="launch" msgid="4826921505917605463">"Opna"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Kerfisstjórinn leyfir ekki uppsetningu forrita af óþekktum uppruna"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Þessi notandi getur ekki sett upp óþekkt forrit"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Þessi notandi hefur ekki leyfi til að setja upp forrit"</string>
+    <string name="ok" msgid="3468756155452870475">"Í lagi"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Stjórna forritum"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Ekkert pláss eftir"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Ekki tókst að setja upp <xliff:g id="APP_NAME">%1$s</xliff:g>. Losaðu um pláss og reyndu aftur."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Forritið finnst ekki"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Forritið fannst ekki á listanum yfir uppsett forrit."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Ekki heimilað"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Núverandi notandi hefur ekki heimild til að fjarlægja þetta."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Villa"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Ekki tókst að fjarlægja forritið."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Fjarlægja forrit"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Fjarlægja uppfærslu"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> er hluti af þessu forriti:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Viltu fjarlægja þetta forrit?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Viltu fjarlægja þetta forrit hjá "<b>"öllum"</b>" notendum? Forritið og gögn þess verða fjarlægð hjá "<b>"öllum"</b>" notendum tækisins."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Viltu fjarlægja þetta forrit fyrir notandann <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"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="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Fjarlægingar í gangi"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Fjarlægingar sem mistókust"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Fjarlægir…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Fjarlægir <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Forritið hefur verið fjarlægt."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Fjarlægði <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Ekki tókst að fjarlægja forritið."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Ekki tókst að fjarlægja <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Ekki er hægt að fjarlægja virkt forrit tækjastjóra"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Ekki er hægt að fjarlægja virkt forrit tækjastjóra fyrir <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Þessa forrits er krafist hjá sumum notendum eða sniðum en var fjarlægt hjá öðrum"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Sniðið þitt krefst þessa forrits og ekki er hægt að fjarlægja það."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Stjórnandi tækisins krefst þessa forrits og ekki er hægt að fjarlægja það."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Stjórna forritum tækjastjóra"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Stjórna notendum"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Ekki tókst að fjarlægja <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Vandamál kom upp við að vinna úr pakkanum."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nýjar"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Allar"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Persónuvernd"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Tækisaðgangur"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Þessi uppfærsla krefst engra nýrra heimilda."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Hafna"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Frekari upplýsingar"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Hafna samt"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> af <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Leyfa forritinu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Viltu alltaf veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; leyfi til að <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Aðeins þegar forrit er í notkun"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Alltaf"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Hafna og ekki spyrja aftur"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> óvirkar"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"allar óvirkar"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"engin óvirk"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Leyfa"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Forrit"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Heimildir forrits"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Ekki spyrja aftur"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Engar heimildir"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Viðbótarheimildir"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Opna upplýsingar um forrit"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> í viðbót</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> í viðbót</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Þetta forrit var hannað fyrir eldri útgáfu af Android. Ef því er ekki veitt heimild er hugsanlegt að það virki ekki rétt."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"framkvæma óþekkta aðgerð"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> forrit af <xliff:g id="COUNT_1">%2$d</xliff:g> leyfð"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Sýna kerfisforrit"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Fela kerfisforrit"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Engin forrit"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Staðsetningarstillingar"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> býður upp á staðsetningarþjónustu fyrir þetta tæki. Hægt er að breyta aðgangi að staðsetningu í stillingum staðsetningar."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Ef þú veitir ekki þessa heimild getur verið að grunneiginleikar tækisins virki ekki lengur sem skyldi."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Stjórnað af reglu"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Bakgrunnsaðgangur óvirkur vegna reglu"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Bakgrunnsaðgangur virkur vegna reglu"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Forgrunnsaðgangur virkur vegna reglu"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Stjórnað af kerfisstjóra"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Alltaf"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Aðeins þegar forrit er í notkun"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Aldrei"</string>
+    <string name="loading" msgid="7811651799620593731">"Hleður…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Allar heimildir"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Aðrir forritseiginleikar"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Beiðni um heimild"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Skjáyfirlögn greindist"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Til að breyta þessari heimildarstillingu þarftu fyrst að slökkva á skjáyfirlögn undir Stillingar &gt; Forrit"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Opna stillingar"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Aðgerðir til að setja upp / fjarlægja eru ekki studdar í Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Veldu hverju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fær aðgang að"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; hefur verið uppfært. Veldu hverju forritið fær aðgang að."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Hætta við"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Halda áfram"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nýjar heimildir"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Núgildandi heimildir"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Setur upp forrit…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Óþekkt"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Til að tryggja öryggi þitt er ekki heimild í spjaldtölvunni þinni fyrir uppsetningu óþekktra forrita frá þessari veitu."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Til að tryggja öryggi þitt er ekki heimild í sjónvarpinu þínu fyrir uppsetningu óþekktra forrita frá þessari veitu."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Til að tryggja öryggi þitt er ekki heimild í símanum þínum fyrir uppsetningu óþekktra forrita frá þessari veitu."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Síminn þinn og persónuleg gögn eru berskjaldaðri fyrir árásum forrita af óþekktum uppruna. Með uppsetningu þessa forrits samþykkirðu að bera fulla ábyrgð á hverju því tjóni sem verða kann á símanum eða gagnatapi sem leiða kann af notkun þess."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Spjaldtölvan þín og persónuleg gögn eru berskjaldaðri fyrir árásum forrita af óþekktum uppruna. Með uppsetningu þessa forrits samþykkirðu að bera fulla ábyrgð á hverju því tjóni sem verða kann á spjaldtölvunni eða gagnatapi sem leiða kann af notkun þess."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Sjónvarpið þitt og persónuleg gögn eru berskjaldaðri fyrir árásum forrita af óþekktum uppruna. Með uppsetningu þessa forrits samþykkirðu að bera fulla ábyrgð á hverju því tjóni sem verða kann á sjónvarpinu eða gagnatapi sem leiða kann af notkun þess."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Áfram"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Stillingar"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Uppsetning/fjarlæging Wear forrita"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-it-television/strings.xml b/packages/PackageInstaller/res/values-it-television/strings.xml
new file mode 100644
index 0000000..14c46de
--- /dev/null
+++ b/packages/PackageInstaller/res/values-it-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Nega e non chiedermelo più"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Puoi modificare questa scelta in seguito in Impostazioni &gt; App"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Mostra app di sistema"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Autorizzazioni app"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Autorizzazioni app"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Autorizzazioni <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Altre autorizzazioni"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Autorizzazioni <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-it-watch/strings.xml b/packages/PackageInstaller/res/values-it-watch/strings.xml
new file mode 100644
index 0000000..73b2ab1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-it-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Nega e non chiedermelo più"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Mostra app di sistema"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Imposs modif"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Sì"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Annulla"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml
new file mode 100644
index 0000000..5870722
--- /dev/null
+++ b/packages/PackageInstaller/res/values-it/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Installazione pacchetti"</string>
+    <string name="next" msgid="3057143178373252333">"Avanti"</string>
+    <string name="install" msgid="5896438203900042068">"Installa"</string>
+    <string name="done" msgid="3889387558374211719">"Fine"</string>
+    <string name="cancel" msgid="8360346460165114585">"Annulla"</string>
+    <string name="installing" msgid="8613631001631998372">"Installazione..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Installazione di <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"App installata."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Vuoi installare questa applicazione? Avrà accesso a:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Vuoi installare questa applicazione? Non richiede alcun accesso speciale."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Vuoi installare un aggiornamento per questa applicazione esistente? I tuoi dati esistenti non andranno persi. L\'applicazione aggiornata avrà accesso a:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Vuoi installare un aggiornamento per questa applicazione integrata? I tuoi dati esistenti non andranno persi. L\'applicazione aggiornata avrà accesso a:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Vuoi installare un aggiornamento di questa applicazione esistente? I dati correnti verranno conservati. Non occorrono accessi speciali."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Vuoi installare un aggiornamento di questa applicazione integrata? I dati correnti verranno conservati. Non occorrono accessi speciali."</string>
+    <string name="install_failed" msgid="6579998651498970899">"App non installata."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"È stata bloccata l\'installazione del pacchetto."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"App non installata poiché il pacchetto è in conflitto con un pacchetto esistente."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"App non installata poiché non compatibile con il tuo tablet."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Questa app non è compatibile con la tua TV."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"App non installata poiché non compatibile con il tuo telefono."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"App non installata poiché il pacchetto risulta non valido."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Impossibile installare <xliff:g id="APP_NAME">%1$s</xliff:g> sul tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Impossibile installare <xliff:g id="APP_NAME">%1$s</xliff:g> sulla tua TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Impossibile installare <xliff:g id="APP_NAME">%1$s</xliff:g> sul telefono."</string>
+    <string name="launch" msgid="4826921505917605463">"Apri"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"L\'amministratore non consente l\'installazione di app ottenute da fonti sconosciute"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Questo utente non può installare app sconosciute"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"L\'utente non è autorizzato a installare app"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Gestisci applicazioni"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Spazio esaurito"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Impossibile installare <xliff:g id="APP_NAME">%1$s</xliff:g>. Libera dello spazio e riprova."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Applicazione non trovata"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Impossibile trovare l\'applicazione nell\'elenco di applicazioni installate."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Non autorizzato"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"L\'utente corrente non è autorizzato a eseguire questa disinstallazione."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Errore"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Impossibile disinstallare l\'app."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Disinstalla applicazione"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Disinstalla aggiornamento"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> fa parte della seguente applicazione:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Vuoi disinstallare questa applicazione?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Vuoi disinstallare questa applicazione per "<b>"tutti"</b>" gli utenti? L\'applicazione e i relativi dati verranno rimossi da "<b>"tutti"</b>" gli utenti configurati sul dispositivo."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Disinstallare l\'app per l\'utente <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Sostituire questa app con la versione di fabbrica? Tutti i dati verranno rimossi."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Disinstallazioni attuali"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Disinstallazioni non riuscite"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Disinstallazione..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Disinstallazione di <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Disinstallazione completata."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"App <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> disinstallata"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Disinstallazione non riuscita."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Impossibile disinstallare <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Impossibile disinstallare l\'app di amministrazione del dispositivo attiva"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Impossibile disinstallare l\'app di amministrazione del dispositivo attiva per <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"L\'app è necessaria per alcuni utenti/profili ed è stata disinstallata per altri"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"L\'app è necessaria per il tuo profilo e non può essere disinstallata."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"App richiesta dall\'amministratore del dispositivo. Non può essere disinstallata."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Gestisci app di amministrazione del dispositivo"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Gestisci utenti"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Impossibile disinstallare <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Errore durante l\'analisi del pacchetto."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nuove"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Tutte"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privacy"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Accesso dispositivo"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Questo aggiornamento non richiede nuove autorizzazioni."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Nega"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Altre informazioni"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Nega comunque"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> di <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Vuoi consentire sempre all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Solo durante l\'uso dell\'app"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Sempre"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Nega e non chiedermelo più"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> disattivate"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"tutte disattivate"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"nessuna disattivata"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Consenti"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"App"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Autorizzazioni app"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Non chiedermelo più"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Nessuna autorizzazione"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Altre autorizzazioni"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Visualizza informazioni sull\'app"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">Altre <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> altra</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Questa app è stata sviluppata per una versione precedente di Android. Se l\'autorizzazione viene negata, l\'app potrebbe non funzionare più come previsto."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"esegue un\'azione sconosciuta"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Sono consentite <xliff:g id="COUNT_0">%1$d</xliff:g> app su <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Mostra app di sistema"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Nascondi app di sistema"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Nessuna app"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Geolocalizzazione"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> è un fornitore di servizi di geolocalizzazione per questo dispositivo. È possibile modificare l\'accesso alla posizione dalle impostazioni sulla posizione."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Se neghi questa autorizzazione, le funzionalità di base del dispositivo potrebbero non funzionare più come previsto."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Applicata in base alle norme"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Accesso in background disattivato in base alla norma"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Accesso in background attivato in base alla norma"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Accesso in primo piano attivato in base alla norma"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Gestita dall\'amministratore"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Sempre"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Solo durante l\'uso dell\'app"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Mai"</string>
+    <string name="loading" msgid="7811651799620593731">"Caricamento…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Tutte le autorizzazioni"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Altre funzionalità dell\'app"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Richiesta di autorizzazione"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Overlay schermo rilevato"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Per modificare questa impostazione di autorizzazione, devi innanzitutto disattivare l\'overlay schermo da Impostazioni &gt; App"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Apri impostazioni"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Le azioni di installazione/disinstallazione non sono supportate su Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Scegli i dati a cui l\'app <xliff:g id="APP_NAME">%1$s</xliff:g> può accedere"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"L\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; è stata aggiornata. Scegli i dati a cui può accedere."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Annulla"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continua"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nuove autorizzazioni"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Autorizzazioni correnti"</string>
+    <string name="message_staging" msgid="6151794817691100003">"App in preparazione…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Sconosciuto"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Per sicurezza, il tuo tablet non è autorizzato a installare app sconosciute da questa origine."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Per sicurezza, la tua TV non è autorizzata a installare app sconosciute da questa origine."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Per sicurezza, il tuo telefono non è autorizzato a installare app sconosciute da questa origine."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"I dati del telefono e i dati personali sono più vulnerabili agli attacchi di app sconosciute. Se installi questa app, accetti di essere responsabile degli eventuali danni al telefono o dell\'eventuale perdita di dati derivanti dall\'uso dell\'app."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"I dati del tablet e i dati personali sono più vulnerabili agli attacchi di app sconosciute. Se installi questa app, accetti di essere responsabile degli eventuali danni al tablet o dell\'eventuale perdita di dati derivanti dall\'uso dell\'app."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"I dati della TV e i dati personali sono più vulnerabili agli attacchi di app sconosciute. Se installi questa app, accetti di essere responsabile degli eventuali danni alla TV o dell\'eventuale perdita di dati derivanti dall\'uso dell\'app."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continua"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Impostazioni"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Installazione/disinstallazione di app Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-iw-television/strings.xml b/packages/PackageInstaller/res/values-iw-television/strings.xml
new file mode 100644
index 0000000..1ec1cd7
--- /dev/null
+++ b/packages/PackageInstaller/res/values-iw-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"דחה ואל תשאל שוב"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"‏ניתן לשנות זאת מאוחר יותר ב\'הגדרות\' &gt; \'אפליקציות\'"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"הצג אפליקציות מערכת"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"הרשאות לאפליקציות"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"הרשאות לאפליקציות"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"הרשאות <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"הרשאות נוספות"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"הרשאות <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-iw-watch/strings.xml b/packages/PackageInstaller/res/values-iw-watch/strings.xml
new file mode 100644
index 0000000..34dd0d3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-iw-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"דחה ואל תשאל שוב"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"הצג אפליקציות מערכת"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"לא ניתן לשנות"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"כן"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"ביטול"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-iw/strings.xml b/packages/PackageInstaller/res/values-iw/strings.xml
new file mode 100644
index 0000000..414fe4c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-iw/strings.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"מתקין החבילה"</string>
+    <string name="next" msgid="3057143178373252333">"הבא"</string>
+    <string name="install" msgid="5896438203900042068">"התקן"</string>
+    <string name="done" msgid="3889387558374211719">"סיום"</string>
+    <string name="cancel" msgid="8360346460165114585">"ביטול"</string>
+    <string name="installing" msgid="8613631001631998372">"מתקין..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"מתקין את <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"האפליקציה הותקנה."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"האם ברצונך להתקין את האפליקציה? היא תקבל גישה אל:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"האם ברצונך להתקין את האפליקציה? היא אינה דורשת גישה מיוחדת."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"האם ברצונך להתקין עדכון לאפליקציה קיימת זו? הנתונים הקיימים שלך לא יאבדו. האפליקציה המעודכנת תקבל גישה אל:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"האם ברצונך להתקין עדכון לאפליקציה מובנית זו? הנתונים הקיימים שלך לא יאבדו. האפליקציה המעודכנת תקבל גישה אל:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"האם ברצונך להתקין עדכון עבור אפליקציה קיימת זו? הנתונים הקיימים שלך לא יאבדו. אין צורך בגישה מיוחדת."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"האם ברצונך להתקין עדכון עבור אפליקציה מובנית זו? הנתונים הקיימים שלך לא יאבדו. אין צורך בגישה מיוחדת."</string>
+    <string name="install_failed" msgid="6579998651498970899">"האפליקציה לא הותקנה."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"החבילה נחסמה להתקנה."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"האפליקציה לא הותקנה כי החבילה מתנגשת עם חבילה קיימת."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"האפליקציה לא הותקנה כי האפליקציה אינה תואמת לטאבלט."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"האפליקציה הזו אינה תואמת לטלוויזיה שלך."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"האפליקציה לא הותקנה כי האפליקציה אינה תואמת לטלפון."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"האפליקציה לא הותקנה כי נראה שהחבילה לא תקפה."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"לא ניתן להתקין את <xliff:g id="APP_NAME">%1$s</xliff:g> בטאבלט שלך."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"לא ניתן להתקין את <xliff:g id="APP_NAME">%1$s</xliff:g> בטלוויזיה שלך."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"לא ניתן להתקין את <xliff:g id="APP_NAME">%1$s</xliff:g> בטלפון שלך."</string>
+    <string name="launch" msgid="4826921505917605463">"פתח"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"מנהל המערכת שלך לא מתיר התקנה של אפליקציות ממקורות לא ידועים"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"למשתמש זה אין הרשאה להתקין אפליקציות שאינן מוכרות"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"למשתמש הזה אין הרשאה להתקין אפליקציות"</string>
+    <string name="ok" msgid="3468756155452870475">"אישור"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"נהל אפליקציות"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"אין מספיק שטח"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"לא ניתן להתקין את <xliff:g id="APP_NAME">%1$s</xliff:g>. פנה שטח ונסה שוב."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"האפליקציה לא נמצא"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"האפליקציה לא נמצאה ברשימת האפליקציות המותקנות."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"לא מורשה"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"המשתמש הנוכחי אינו מורשה להסיר את ההתקנה הזו."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"שגיאה"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"לא ניתן היה להסיר את התקנת האפליקציה."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"הסר את התקנת האפליקציה"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"הסר את התקנת העדכון"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> הוא חלק מהאפליקציה הבאה:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"האם ברצונך להסיר את ההתקנה של אפליקציה זו?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"האם אתה רוצה להסיר את האפליקציה הזו עבור "<b>"כל"</b>" המשתמשים? האפליקציה והנתונים שלה יוסרו מ"<b>"כל"</b>" המשתמשים במכשיר."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"האם ברצונך להסיר את התקנתה של אפליקציה זו עבור המשתמש <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"האם להחליף את האפליקציה הזאת בגירסת היצרן? כל הנתונים יוסרו."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"האם להחליף את האפליקציה הזאת בגירסת היצרן? כל הנתונים יוסרו. הפעולה תשפיע על כל משתמשי המכשיר, כולל משתמשים בעלי פרופיל עבודה."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"התקנות בתהליכי הסרה"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"הסרות התקנה שנכשלו"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"מסיר התקנה..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"מסיר את ההתקנה של <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"הסרת ההתקנה הסתיימה."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"ההתקנה של <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> הוסרה"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"הסרת התקנה נכשלה."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"נכשלה הסרת ההתקנה של <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"לא ניתן להסיר את ההתקנה של אפליקציה פעילה של מנהל המכשיר"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"לא ניתן להסיר את ההתקנה של אפליקציה פעילה של מנהל המכשיר של <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"אפליקציה זו נדרשת לחלק מהמשתמשים או מהפרופילים והתקנתה הוסרה למשתמשים אחרים"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"האפליקציה הזו נחוצה לפרופיל שלך ולא ניתן להסיר את ההתקנה שלה."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"מנהל המכשיר שלך מחייב את קיומה של אפליקציה זו, ולא ניתן להסירה."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"אפליקציות למנהל המערכת של מכשיר מנוהל"</string>
+    <string name="manage_users" msgid="3125018886835668847">"ניהול משתמשים"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"לא ניתן להסיר את ההתקנה של <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"אירעה בעיה בניתוח החבילה."</string>
+    <string name="newPerms" msgid="6039428254474104210">"חדש"</string>
+    <string name="allPerms" msgid="1024385515840703981">"הכל"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"פרטיות"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"גישה למכשיר"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"עדכון זה לא דורש הרשאות חדשות."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"לא, תודה"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"למידע נוסף"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"לדחות בכל מקרה"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> מתוך <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"‏האם לאשר ל&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"‏תמיד להרשות לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"רק בזמן השימוש באפליקציה"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"תמיד"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"יש לדחות ואין לשאול שוב"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> הרשאות מושבתות"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"כל ההרשאות מושבתות"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"אין הרשאות מושבתות"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"כן, זה בסדר"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"אפליקציות"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"הרשאות לאפליקציות"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"ראיתי פעם אחת, זה מספיק"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"אין הרשאות"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"הרשאות נוספות"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"פתיחה של פרטי האפליקציה"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="two">עוד <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="many">עוד <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_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"‏האפליקציה הזו עוצבה בשביל גרסה ישנה יותר של Android. דחיית ההרשאה עשויה לגרום לה לתפקד בצורה לקויה."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"ביצוע פעולה לא ידועה"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> מתוך <xliff:g id="COUNT_1">%2$d</xliff:g> אפליקציות קיבלו הרשאה"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"הרשאות המערכת"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"הסתר מערכת"</string>
+    <string name="no_apps" msgid="1965493419005012569">"אין אפליקציות"</string>
+    <string name="location_settings" msgid="1774875730854491297">"הגדרות מיקום"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> הוא ספק של שירותי מיקום בשביל המכשיר הזה. אפשר לשנות את גישת המיקום בהגדרות המיקום."</string>
+    <string name="system_warning" msgid="7103819124542305179">"אם תדחה את ההרשאה הזו, ייתכן שתכונות בסיסיות במכשיר לא יפעלו כצפוי."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"נאכף באמצעות מדיניות"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"הגישה ברקע מושבתת על ידי מדיניות"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"הגישה ברקע מופעלת על ידי מדיניות"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"הגישה בחזית מופעלת על ידי מדיניות"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"נמצא בשליטת מנהל מערכת"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"תמיד"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"רק בזמן השימוש באפליקציה"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"אף פעם"</string>
+    <string name="loading" msgid="7811651799620593731">"טוען..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"כל ההרשאות"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"הרשאות אחרות של האפליקציה"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"בקשת הרשאה"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"זוהתה שכבת על במסך"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"‏כדי לשנות את הגדרת ההרשאה הזו, ראשית עליך לכבות את שכבת העל במסך ב\'הגדרות\' &gt; \'אפליקציות\'"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"פתח הגדרות"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"‏פעולות התקנה/הסרת התקנה אינן נתמכות ב-Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"‏בחר אילו הרשאות גישה ברצונך לתת ל &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; עודכנה. בחר אילו הרשאות גישה יהיו לאפליקציה זו."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"ביטול"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"המשך"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"הרשאות חדשות"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"הרשאות קיימות"</string>
+    <string name="message_staging" msgid="6151794817691100003">"מכין אפליקציה להתקנה…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"לא ידוע"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"לצורכי אבטחה, הטאבלט שלך חסום להתקנת אפליקציות בלתי מוכרות המגיעות ממקור זה."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"לצורכי אבטחה, מכשיר הטלוויזיה שלך חסום להתקנת אפליקציות בלתי מוכרות המגיעות ממקור זה."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"לצורכי אבטחה, הטלפון שלך חסום להתקנת אפליקציות בלתי מוכרות המגיעות ממקור זה."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"נתוני הטלפון והנתונים האישיים שלך חשופים יותר בפני התקפות על ידי אפליקציות ממקורות לא ידועים. אם תתקין אפליקציה זו, אתה מסכים לכך שאתה האחראי הבלעדי במקרה של אובדן נתונים או אם ייגרם נזק לטלפון שלך בעקבות השימוש באפליקציה."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"נתוני הטאבלט והנתונים האישיים שלך חשופים יותר בפני התקפות על ידי אפליקציות ממקורות לא ידועים. אם תתקין אפליקציה זו, אתה מסכים לכך שאתה האחראי הבלעדי במקרה של אובדן נתונים או אם ייגרם נזק לטאבלט שלך בעקבות השימוש באפליקציה."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"נתוני הטלוויזיה והנתונים האישיים שלך חשופים יותר בפני התקפות על ידי אפליקציות ממקורות לא ידועים. אם תתקין אפליקציה זו, אתה מסכים לכך שאתה האחראי הבלעדי במקרה של אובדן נתונים או אם ייגרם נזק לטלוויזיה שלך בעקבות השימוש באפליקציה."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"המשך"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"הגדרות"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"‏מתקין/מסיר התקנה של אפליקציות Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ja-television/strings.xml b/packages/PackageInstaller/res/values-ja-television/strings.xml
new file mode 100644
index 0000000..9f31829
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ja-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"許可しない(次回から表示しない)"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"これは後から[設定]&gt;[アプリ]で変更できます"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"システムアプリの表示"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"アプリの権限"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"アプリの権限"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g>の権限"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"その他の権限"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g>の権限"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ja-watch/strings.xml b/packages/PackageInstaller/res/values-ja-watch/strings.xml
new file mode 100644
index 0000000..735b908
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ja-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"許可しない(今後表示しない)"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"システムアプリの表示"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"変更不可"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"はい"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"キャンセル"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ja/strings.xml b/packages/PackageInstaller/res/values-ja/strings.xml
new file mode 100644
index 0000000..5a7dc7d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ja/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"パッケージインストーラ"</string>
+    <string name="next" msgid="3057143178373252333">"次へ"</string>
+    <string name="install" msgid="5896438203900042068">"インストール"</string>
+    <string name="done" msgid="3889387558374211719">"完了"</string>
+    <string name="cancel" msgid="8360346460165114585">"キャンセル"</string>
+    <string name="installing" msgid="8613631001631998372">"インストール中..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」をインストールしています…"</string>
+    <string name="install_done" msgid="3682715442154357097">"アプリをインストールしました。"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"このアプリケーションをインストールしてもよろしいですか?このアプリケーションは下記にアクセスする場合があります:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"このアプリケーションをインストールしてもよろしいですか?このアプリケーションは特別なアクセス許可を必要としません。"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"この既存のアプリケーションへのアップデートをインストールしてもよろしいですか?既存のデータは失われません。アップデート後のアプリケーションは下記にアクセスする場合があります:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"この内蔵アプリケーションへのアップデートをインストールしてもよろしいですか?既存のデータは失われません。アップデート後のアプリケーションは下記にアクセスする場合があります:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"この既存のアプリにアップデートをインストールしますか?既存のデータが失われることはありません。特別なアクセス権も必要ありません。"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"この内蔵アプリにアップデートをインストールしますか?既存のデータが失われることはありません。特別なアクセス権も必要ありません。"</string>
+    <string name="install_failed" msgid="6579998651498970899">"アプリはインストールされていません。"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"パッケージのインストールはブロックされています。"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"パッケージが既存のパッケージと競合するため、アプリをインストールできませんでした。"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"お使いのタブレットに対応していないため、アプリをインストールできませんでした。"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"このアプリはお使いのテレビに対応していません。"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"お使いのスマートフォンに対応していないため、アプリをインストールできませんでした。"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"パッケージが無効の可能性があるため、アプリをインストールできませんでした。"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g>をタブレットにインストールできませんでした。"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g>をテレビにインストールできませんでした。"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g>を端末にインストールできませんでした。"</string>
+    <string name="launch" msgid="4826921505917605463">"開く"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"提供元不明のアプリをインストールすることは、管理者により禁止されています"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"このユーザーは不明なアプリをインストールできません"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"このユーザーはアプリをインストールできません"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"アプリを管理"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"容量不足です"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g>をインストールできませんでした。空き容量を増やしてもう一度お試しください。"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"アプリが見つかりません"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"インストール済みアプリのリストに、このアプリはありません。"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"許可されていません"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"このアンインストール操作は現在のユーザーには許可されていません。"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"エラー"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"アプリをアンインストールできませんでした。"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"アプリをアンインストール"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"アップデートをアンインストール"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>は次のアプリの一部です:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"このアプリをアンインストールしますか?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"このアプリを"<b>"すべての"</b>"ユーザーからアンインストールしますか?このアプリとそのデータは端末の"<b>"すべての"</b>"ユーザーから削除されます。"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"<xliff:g id="USERNAME">%1$s</xliff:g>さんのアプリをアンインストールしますか?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"このアプリを出荷時の状態に戻しますか?データがすべて削除されます。"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"このアプリを出荷時の状態に戻しますか?データがすべて削除されます。これは、仕事用プロファイルを設定しているユーザーも含めて、この端末を使用するすべてのユーザーが対象となります。"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"実行中のアンインストール"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"エラーになったアンインストール"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"アンインストール中..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」をアンインストールしています…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"アンインストールが完了しました。"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」をアンインストールしました"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"アンインストールできませんでした。"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」をアンインストールできませんでした。"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"有効な端末管理アプリをアンインストールできません"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> さんの有効な端末管理アプリをアンインストールできません"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"このアプリは一部のユーザーやプロファイルに必要なため、アンインストールできませんでした"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"このアプリはプロファイルに必要なため、アンインストールできません。"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"このアプリは端末管理者が必要としているため、アンインストールできません。"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"端末管理アプリを管理"</string>
+    <string name="manage_users" msgid="3125018886835668847">"ユーザーを管理"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g>をアンインストールできませんでした。"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"パッケージの解析中に問題が発生しました。"</string>
+    <string name="newPerms" msgid="6039428254474104210">"New"</string>
+    <string name="allPerms" msgid="1024385515840703981">"すべて"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"プライバシー"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"端末アクセス"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"このアップデートでは新たな許可は必要ありません。"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"許可しない"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"詳細"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"許可しない"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に<xliff:g id="ACTION">%2$s</xliff:g>を許可しますか?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"「<xliff:g id="ACTION">%2$s</xliff:g>」を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に常に許可しますか?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"アプリの使用中のみ"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"常時"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"許可しない(次回から表示しない)"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> 件無効"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"すべて無効"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"無効な権限なし"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"許可"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"アプリ"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"アプリの権限"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"今後表示しない"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"権限がありません"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"その他の権限"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"アプリ情報を開く"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">他<xliff:g id="COUNT_1">%1$d</xliff:g>件</item>
+      <item quantity="one">他<xliff:g id="COUNT_0">%1$d</xliff:g>件</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"このアプリはAndroidの以前のバージョンを対象としています。権限を許可しないと、意図されたとおりに動作しなくなる可能性があります。"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"不明な操作の実行"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g>個のアプリを許可"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"システムを表示"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"システムを表示しない"</string>
+    <string name="no_apps" msgid="1965493419005012569">"アプリなし"</string>
+    <string name="location_settings" msgid="1774875730854491297">"位置情報の設定"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g>はこの端末の位置情報サービスのプロバイダです。位置情報アクセスは位置情報の設定から変更できます。"</string>
+    <string name="system_warning" msgid="7103819124542305179">"この権限を許可しないと、お使いの端末の基本的な機能が意図されたとおりに動作しなくなる可能性があります。"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"ポリシーにより適用"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"バックグラウンドでのアクセスはポリシーによって無効です"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"バックグラウンドでのアクセスはポリシーによって有効です"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"フォアグラウンドでのアクセスはポリシーによって有効です"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"管理者により管理されています"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"常時"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"アプリの使用中のみ"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"なし"</string>
+    <string name="loading" msgid="7811651799620593731">"読み込んでいます…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"すべての権限"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"その他のアプリ機能"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"権限のリクエスト"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"画面オーバーレイを検出"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"この権限設定を変更するには、まず[設定]&gt;[アプリ]から画面オーバーレイをOFFにします"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"設定を開く"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear ではインストールやアンインストールができません"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可する権限の選択"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」が更新されました。このアプリに許可する権限を選択してください。"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"キャンセル"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"続行"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"新しい権限"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"現在の権限"</string>
+    <string name="message_staging" msgid="6151794817691100003">"アプリを準備しています…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"不明"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"セキュリティ上の理由から、お使いのタブレットではこの提供元からの不明なアプリをインストールすることはできません。"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"セキュリティ上の理由から、お使いのテレビではこの提供元からの不明なアプリをインストールすることはできません。"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"セキュリティ上の理由から、お使いのスマートフォンではこの提供元からの不明なアプリをインストールすることはできません。"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"不明なアプリをインストールするとスマートフォンや個人データが攻撃を受ける可能性が高くなります。このアプリをインストールすることにより、アプリの使用により生じる可能性があるスマートフォンへの損害やデータの損失について、ユーザーご自身が単独で責任を負うことに同意するものとします。"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"不明なアプリをインストールするとタブレットや個人データが攻撃を受ける可能性が高くなります。このアプリをインストールすることにより、アプリの使用により生じる可能性があるタブレットへの損害やデータの損失について、ユーザーご自身が単独で責任を負うことに同意するものとします。"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"不明なアプリをインストールすると TV や個人データが攻撃を受ける可能性が高くなります。このアプリをインストールすることにより、アプリの使用により生じる可能性がある TV への損害やデータの損失について、ユーザーご自身が単独で責任を負うことに同意するものとします。"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"続行"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"設定"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear アプリのインストールとアンインストール"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ka-television/strings.xml b/packages/PackageInstaller/res/values-ka-television/strings.xml
new file mode 100644
index 0000000..b7d03b0
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ka-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"უარყავი და აღარ მკითხო"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"ამის შეცვლა მოგვიანებით შეგიძლიათ სექციაში პარამეტრები &gt; აპები"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"სისტემის აპების ჩვენება"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"აპის ნებართვები"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"აპის ნებართვები"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"ნებართვები (<xliff:g id="PERMISSION">%1$s</xliff:g>)"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"დამატებითი ნებართვები"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"ნებართვები (<xliff:g id="PERMISSION">%1$s</xliff:g>)"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ka-watch/strings.xml b/packages/PackageInstaller/res/values-ka-watch/strings.xml
new file mode 100644
index 0000000..1574912
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ka-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"უარყავი და აღარ მკითხო"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"სისტემის აპების ჩვენება"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"ვერ შეიცვლება"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"დიახ"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"გაუქმება"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ka/strings.xml b/packages/PackageInstaller/res/values-ka/strings.xml
new file mode 100644
index 0000000..997f95c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ka/strings.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"პაკეტის ინსტალერი"</string>
+    <string name="next" msgid="3057143178373252333">"შემდეგი"</string>
+    <string name="install" msgid="5896438203900042068">"დაყენება"</string>
+    <string name="done" msgid="3889387558374211719">"დასრულდა"</string>
+    <string name="cancel" msgid="8360346460165114585">"გაუქმება"</string>
+    <string name="installing" msgid="8613631001631998372">"მიმდინარეობს ინსტალაცია…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"მიმდინარეობს <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-ის ინსტალაცია…"</string>
+    <string name="install_done" msgid="3682715442154357097">"აპი დაყენებულია."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"გსურთ, ამ აპლიკაციის დაყენება? მას ექნება წვდომა:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"გსურთ ამ აპლიკაციის დაყენება? ის არ მოითხოვს რაიმე განსაკუთრებულ ნებართვას."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"გსურთ განახლების დაყენება ამ არსებული აპლიკაციისთვის? არსებული მონაცემები არ დაიკარგება. განახლებულ აპლიკაციას წვდომა ექნება:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"გსურთ განახლების დაყენება ამ ჩაშენებული აპლიკაციისთვის? არსებული მონაცემები არ დაიკარგება. განახლენულ აპლიკაციას წვდომა ექნება:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"გსურთ განახლების დაყენება ამ არსებული აპლიკაციისთვის? არსებული მონაცემები არ დაიკარგება. ის არ საჭიროებს რაიმე განსაკუთრებულ წვდომას:"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"გსურთ განახლების დაყენება ამ ჩაშენებული აპლიკაციისთვის? არსებული მონაცემები არ დაიკარგება. ის არ საჭიროებს რაიმე განსაკუთრებულ წვდომას:"</string>
+    <string name="install_failed" msgid="6579998651498970899">"აპი არ არის დაყენებული."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"ამ პაკეტის ინსტალაცია დაბლოკილია."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"აპი ვერ დაინსტალირდა, რადგან პაკეტი კონფლიქტშია არსებულ პაკეტთან."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"აპი ვერ დაინსტალირდა, რადგან ის არ არის თავსებადი თქვენს ტაბლეტთან."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"ეს აპი არ არის თავსებადი თქვენს ტელევიზორთან."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"აპი ვერ დაინსტალირდა, რადგან ის არ არის თავსებადი თქვენს ტელეფონთან."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"აპი ვერ დაინსტალირდა, რადგან პაკეტი, სავარაუდოდ, არასწორია."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის დაყენება თქვენს ტაბლეტზე ვერ მოხერხდა."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> ვერ დაინსტალირდება თქვენს ტელევიზორში."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის დაყენება თქვენს ტელეფონზე ვერ მოხერხდა."</string>
+    <string name="launch" msgid="4826921505917605463">"გახსნა"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"უცნობი წყაროებიდან ჩამოტვირთული აპების ინსტალაცია თქვენი ადმინისტრატორის მიერ ნებადართული არ არის"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"ამ მომხმარებელს უცნობი აპების ინსტალაცია არ შეუძლია"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"ამ მომხმარებელს აპების დაინსტალირების უფლება არ აქვს"</string>
+    <string name="ok" msgid="3468756155452870475">"კარგი"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"აპების მართვა"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"სივრცე შეივსო"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის დაყენება შეუძლებელია. გაათავისუფლეთ მეხსიერება და სცადეთ ხელახლა."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"აპი ვერ მოიძებნა."</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"დაყენებული აპების სიაში ეს აპი ვერ მოიძებნა."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"დაუშვებელია"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"მიმდინარე მომხმარებელს არ აქვს დეინსტალაციის განხორციელების უფლება."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"შეცდომა"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"აპის დეინსტალაცია ვერ მოხერხდა."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"აპის დეინსტალაცია"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"განახლების დეინსტალაცია"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> არის შემდეგი აპის ნაწილი:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"გსურთ, ამ აპის დეინსტალაცია?"</string>
+    <!-- syntax error in translation for uninstall_application_text_all_users (5574704453233525222) org.xmlpull.v1.XmlPullParserException: expected: /string read: b (position:END_TAG </b>@1:122 in     <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"გსურთ ამ აპის დეინსტალაცია ყველა"</b>" მომხმარებილის "<b>"-თვის? აპლიკაცია და მისი მონაცემენბი წაიშლება ყველა"</b>" მომხმარებლის "<b>"-თვის მოწყობილობაზე."</string>
+)  -->
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"გსურთ <xliff:g id="USERNAME">%1$s</xliff:g> მომხმარებლისათვის ამ აპის დეინსტალაცია?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"გსურთ ამ აპის ქარხნული ვერსიით ჩანაცვლება? მონაცემები მთლიანად ამოიშლება."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"გსურთ ამ აპის ქარხნული ვერსიით ჩანაცვლება? მონაცემები მთლიანად ამოიშლება. ეს მოქმედება გავლენას იქონიებს ამ მოწყობილობის ყველა მომხმარებელზე, მათ შორის, სამსახურის პროფილებით მოსარგებლეებზეც."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"გაშვებული დეინსტალაციები"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"შეუსრულებელი დეინსტალაციები"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"დეინსტალაცია…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"მიმდინარეობს <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-ის დეინსტალაცია…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"დეინსტალაცია დასრულდა."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> დეინსტალირებულია"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"დეინსტალაცია წარუმატებელია."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-ის დეინსტალაცია ვერ მოხერხდა."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"მოწყობილობის ადმინისტრატორის აქტიური აპის დეინსტალაცია ვერ მოხერხდება"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g>-სთვის მოწყობილობის ადმინისტრატორის აქტიური აპის დეინსტალაცია ვერ მოხერხდება"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"ამ აპს მომხმარებლების/პროფილების ნაწილი იყენებს. სხვებისთვის ის დეინსტალირებულია."</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"ეს აპი საჭიროა თქვენი პროფილისთვის. მისი დეინსტალაცია ვერ მოხერხდება."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"ეს აპი საჭიროა თქვენი მოწყ. ადმინისტრატორისათვის და დეინსტალაცია ვერ გამოვა."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"მოწყობილობის ადმინისტრატორების აპების მართვა"</string>
+    <string name="manage_users" msgid="3125018886835668847">"მომხმარებლების მართვა"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> -ის დეინსტალაცია ვერ მოხერხდა."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"პაკეტის ანალიზისას წარმოიშვა პრობლემა."</string>
+    <string name="newPerms" msgid="6039428254474104210">"ახალი"</string>
+    <string name="allPerms" msgid="1024385515840703981">"ყველა"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"კონფიდენციალურობა"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"მოწყობილობის წვდომა"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"ეს განახლება არ საჭიროებს ახალ ნებართვებს."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"უარყოფა"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"დამატებითი ინფორმაცია"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"მაინც უარყოფა"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>-დან"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"გსურთ, დაუშვათ, რომ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-მ შეასრულოს <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"გსურთ, ყოველთვის შესრულდეს <xliff:g id="ACTION">%2$s</xliff:g> &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ის&lt;/b&gt; მიერ?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"მხოლოდ აპის გამოყენებისას"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"ყოველთვის"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"უარყავი და აღარ მკითხო"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"გათიშულია <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"გათიშულია ყველა"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"არაფერია გათიშული"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"დაშვება"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"აპები"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"აპის უფლებები"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"აღარ მკითხოთ"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"ნებართვები არ არის"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"დამატებითი ნებართვები"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"აპის ინფორმაციის გახსნა"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> კიდევ</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> კიდევ</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"ეს აპი Android-ის ძველი ვერსიისთვის შეიქმნა. ნებართვის უარყოფამ შესაძლოა მისი არასათანადო ფუნქციონირება გამოიწვიოს."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"უცნობი ქმედების შესრულება"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"დაშვებულია <xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g> აპიდან"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"სისტემის ჩვენება"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"სისტემური პროცესების დამალვა"</string>
+    <string name="no_apps" msgid="1965493419005012569">"აპები არ არის"</string>
+    <string name="location_settings" msgid="1774875730854491297">"მდებარეობის პარამეტრები"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> არის მდებარეობის სერვისების მომწოდებელი ამ მოწყობილობისთვის. მდებარეობაზე წვდომის შეცვლა შესაძლებელია მდებარეობის პარამეტრებიდან."</string>
+    <string name="system_warning" msgid="7103819124542305179">"ამ ნებართვის უარყოფის შემთხვევაში, თქვენი მოწყობილობის ძირითადმა ფუნქციებმა შესაძლოა სათანადოდ აღარ იმუშაოს."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"შეესაბამება წესს"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"ფონზე წვდომა დებულებით გათიშულია"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"ფონზე წვდომა დებულებით დაშვებულია"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"წინა პლანზე წვდომა დებულებით დაშვებულია"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"იმართება ადმინისტრატორის მიერ"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"ყოველთვის"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"მხოლოდ აპის გამოყენებისას"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"არასოდეს"</string>
+    <string name="loading" msgid="7811651799620593731">"იტვირთება..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"ყველა ნებართვა"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"აპის სხვა შესაძლებლობები"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"ნებართვის მოთხოვნა"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"ეკრანის გადაფარვა გამოვლინდა"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"ამ ნებართვის პარამეტრის შესაცვლელად, ჯერ უნდა გამორთოთ ეკრანის გადაფარვა პარამეტრებიდან &gt; აპებიდან"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"პარამეტრების გახსნა"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"ინსტალაციის/დეინსტალაციის მოქმედებები არ არის მხარდაჭერილი Wear-ზე."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"აირჩიეთ, რაზე ჰქონდეს წვდომა &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-ს"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; განახლდა. აირჩიეთ, რაზე ჰქონდეს წვდომა ამ აპს."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"გაუქმება"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"გაგრძელება"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"ახალი ნებართვები"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"ამჟამინდელი ნებართვები"</string>
+    <string name="message_staging" msgid="6151794817691100003">"მიმდინარეობს აპის შუალედური შენახვა…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"უცნობი"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"თქვენივე უსაფრთხოებისთვის, ტაბლეტს ამ წყაროდან უცნობი აპების ინსტალაციის უფლება არ აქვს."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"თქვენივე უსაფრთხოებისთვის, ტელევიზორს ამ წყაროდან უცნობი აპების ინსტალაციის უფლება არ აქვს."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"თქვენივე უსაფრთხოებისთვის, ტელეფონს ამ წყაროდან უცნობი აპების ინსტალაციის უფლება არ აქვს."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"თქვენი ტელეფონი და პირადი მონაცემები უცნობი აპების შემოტევების წინაშე მეტად დაუცველია. ამ აპის ინსტალაციის შემთხვევაში, თქვენ თანახმა ხართ, პასუხისმგებელი იყოთ მისი გამოყენების შედეგად ტელეფონისთვის მიყენებულ ზიანსა და მონაცემების დაკარგვაზე."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"თქვენი ტელეფონი და პირადი მონაცემები უცნობი აპების შემოტევების წინაშე მეტად დაუცველია. ამ აპის ინსტალაციის შემთხვევაში, თქვენ თანახმა ხართ, პასუხისმგებელი იყოთ მისი გამოყენების შედეგად ტაბლეტისთვის მიყენებულ ზიანსა და მონაცემების დაკარგვაზე."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"თქვენი ტელევიზორი და პირადი მონაცემები უცნობი აპების შემოტევების წინაშე მეტად დაუცველია. ამ აპის ინსტალაციის შემთხვევაში, თქვენ თანახმა ხართ, პასუხისმგებელი იყოთ მისი გამოყენების შედეგად ტელევიზორისთვის მიყენებულ ზიანსა და მონაცემების დაკარგვაზე."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"გაგრძელება"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"პარამეტრები"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear აპების ინსტალაცია/დეინსტალაცია"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-kk-television/strings.xml b/packages/PackageInstaller/res/values-kk-television/strings.xml
new file mode 100644
index 0000000..2d0ff43
--- /dev/null
+++ b/packages/PackageInstaller/res/values-kk-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Тыйым салу және қайтадан сұрамау"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Мұны кейінірек \"Параметрлер\" және \"Қолданбалар\" ішінен өзгертуге болады"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Жүйелік қолданбаларды көрсету"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Қолданба рұқсаттары"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Қолданба рұқсаттары"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> рұқсаттары"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Қосымша рұқсаттар"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> рұқсаттары"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-kk-watch/strings.xml b/packages/PackageInstaller/res/values-kk-watch/strings.xml
new file mode 100644
index 0000000..8996bcb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-kk-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Тыйым салу, қайтадан сұрамау"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Жүйелік қолданбаларды көрсету"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Өзгерту мүмкін емес"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Иә"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Бас тарту"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-kk/strings.xml b/packages/PackageInstaller/res/values-kk/strings.xml
new file mode 100644
index 0000000..26892e1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-kk/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Бума орнатқыш"</string>
+    <string name="next" msgid="3057143178373252333">"Келесі"</string>
+    <string name="install" msgid="5896438203900042068">"Орнату"</string>
+    <string name="done" msgid="3889387558374211719">"Дайын"</string>
+    <string name="cancel" msgid="8360346460165114585">"Бас тарту"</string>
+    <string name="installing" msgid="8613631001631998372">"Орнатуда…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> орнатылуда…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Қолданба орнатылды."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Бұл қолданбаны орнатуды қалайсыз ба? Оның келесі нәрселерге қол жетімділігі болады:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Бұл қолданбаны орнатуды қалайсыз ба? Ол ерекше қол жетімділікті қажет етпейді."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Бұл қолданбаның жаңартылған нұсқасын орнатуды қалайсыз ба? Деректеріңіз жоғалмайды. Жаңартылған қолданбаның келесі нәрселерге қол жетімділігі болады:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Бұл орнатылған қолданбаның жаңартылған нұсқасын орнатуды қалайсыз ба? Деректеріңіз жоғалмайды. Жаңартылған қолданбаның келесі нәрселерге қол жетімділігі болады:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Бұл қолданбаның жаңартылған нұсқасын орнатуды қалайсыз ба? Деректеріңіз жоғалмайды. Ол ерекше қол жетімділікті қажет етпейді."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Бұл орнатылған қолданбаның жаңартылған нұсқасын орнатуды қалайсыз ба? Деректеріңіз жоғалмайды. Ол ерекше қол жетімділікті қажет етпейді."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Қолданба орнатылмады."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Буманы орнатуға тыйым салынды."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Пакет түрінде орнатылмаған қолданба мен бұрыннан бар пакеттің арасында қайшылық туындайды."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Қолданба түрінде орнатылмаған қолданба, планшетіңізбен үйлесімді емес."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Бұл қолданба теледидарыңызбен үйлесімді емес."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Қолданба түрінде орнатылмаған қолданба, телефоныңызбен үйлесімді емес."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Пакет түрінде орнатылмаған қолданба жарамсыз болып табылады."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын планшетіңізге орнату мүмкін емес"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> теледидарыңызда орнату мүмкін емес."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын телефоныңызға орнату мүмкін емес."</string>
+    <string name="launch" msgid="4826921505917605463">"Ашу"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Әкімші белгісіз көздерден алынған қолданбаларды орнатуға рұқсат етпейді"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Бұл пайдаланушы белгісіз қолданбаларды орната алмайды"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Бұл пайдаланушының қолданбаларды орнату рұқсаты жоқ"</string>
+    <string name="ok" msgid="3468756155452870475">"Жарайды"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Қолданбаларды басқару"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Орнында емес"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын орнату мүмкін болмады. Орын босатып, қайта әрекеттеніп көріңіз."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Қолданба табылмады"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Қолданба орнатылған қолданбалар тізімінен табылмады."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Рұқсат етілмеген"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Ағымдағы пайдаланушыға бұл жою әрекетіне рұқсат берілмеген."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Қате"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Қолданба жойылмады."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Қолданбаны алып тастау"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Жаңартуды алып тастау"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> келесі қолданбаның бөлігі:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Бұл қолданбаны алып тастауды қалайсыз ба?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Бұл қолданбаны "<b>"барлық"</b>" пайдаланушылар үшін алып тастауды қалайсыз ба? Қолданба және оның деректері құрылғыдағы "<b>"барлық"</b>" пайдаланушылардан алынады."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Пайдаланушы <xliff:g id="USERNAME">%1$s</xliff:g> үшін осы қолданбаны жою керек пе?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Осы қолданбаны зауыттық нұсқамен ауыстыру керек пе? Бүкіл деректер жойылады."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Осы қолданбаны зауыттық нұсқамен ауыстыру керек пе? Бүкіл деректер жойылады. Бұл осы құрылғының барлық пайдаланушыларына, соның ішінде жұмыс профильдері бар пайдаланушыларға әсер етеді."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Жұмыс істеп тұрған жою әрекеттері"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Сәтсіз жою әрекеттері"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Алып тастау орындалуда..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> жойылуда…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Алып тастау аяқталды."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> жойылды"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Алып тастау сәтсіздікке ұшырады."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> жою сәтсіз аяқталды."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Белсенді құрылғының әкімші қолданбасын жою мүмкін емес"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> үшін белсенді құрылғының әкімші қолданбасын жою мүмкін емес"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Бұл қолданба кейбір пайдаланушылар немесе профильдер үшін қажет және басқалар үшін жойылды"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Бұл қолданба профиліңіз үшін қажет және оны жою мүмкін емес."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Әкімші осы қолданбаны талап етеді және оны жою мүмкін емес."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Құрылғы әкімшісі қолданбаларын басқару"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Пайдаланушыларды басқару"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын алып тастау мүмкін емес."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Жинақты сараптау кезінде мәселе орын алды."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Жаңа"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Барлық"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Құпиялылық"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Құралға кіру"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Бұл қолданба жаңа рұқсаттарды қажет етпейді."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Тыйым салу"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Қосымша ақпарат"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Бәрібір рұқсат бермеу"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>/<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына <xliff:g id="ACTION">%2$s</xliff:g> рұқсатын беру керек пе?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасы үшін <xliff:g id="ACTION">%2$s</xliff:g> әрекетіне әрқашан рұқсат етілсін бе?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Қолданба пайдаланылғанда ғана"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Әрқашан"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Тыйым салынсын және қайта сұралмасын"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> рұқсат өшірілді"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"барлық рұқсаттар өшірілді"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"рұқсаттардың ешқайсысы өшірілмеді"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Рұқсат беру"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Қолданбалар"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Қолданба рұқсаттары"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Қайта сұралмасын"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Рұқсат жоқ"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Қосымша рұқсаттар"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Қолданба ақпаратын ашу"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">Тағы <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Тағы <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Бұл қолданба Android жүйесінің ескі нұсқасына арналған. Рұқсаттан бас тартсаңыз, бұдан былай тиісінше жұмыс істемеуі мүмкін."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"белгісіз әрекетті орындау"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g> қолданбаға рұқсат етілген"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Жүйені көрсету"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Жүйені жасыру"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Қолданбалар жоқ"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Орынды анықтау параметрлері"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> — осы құрылғыға орынды анықтау қызметтерін көрсететін қолданба. Орынды пайдалану мүмкіндігін орынды анықтау параметрлерінде өзгертуге болады."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Бұл рұқсатты бермесеңіз, құрылғының негізгі функциялары енді көзделгендей жұмыс істемеуі мүмкін."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Саясат арқылы қолданылған"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Саясат бойынша фондық режимде кіруге рұқсат етілмеген"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Саясат бойынша фондық режимде кіруге рұқсат етілген"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Саясат бойынша экрандық режимде кіруге рұқсат етілген"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Әкімші басқарады"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Әрқашан"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Қолданба пайдаланылғанда ғана"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Ешқашан"</string>
+    <string name="loading" msgid="7811651799620593731">"Жүктелуде…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Барлық рұқсаттар"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Басқа қолданба мүмкіндіктері"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Рұқсат сұрау"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Экранды қабаттастыру анықталды"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Бұл рұқсат параметрін өзгерту үшін алдымен «Параметрлер» &gt; «Қолданбалар» тармағында экранды қабаттастыруды өшіру керек"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Параметрлерді ашу"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear құрылғысында \"Орнату\"/\"Жою\" әрекеттері қолданылмайды."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы қайда кіре алатынын таңдаңыз"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы жаңартылды. Бұл қолданбаның қайда кіре алатынын таңдаңыз."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Бас тарту"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Жалғастыру"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Жаңа рұқсаттар"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Ағымдағы рұқсаттар"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Қолданба реттелуде…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Белгісіз"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Қауіпсіздікті сақтау үшін планшетке бұл дереккөзден белгісіз қолданбаларды орнатуға рұқсат берілмейді."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Қауіпсіздікті сақтау үшін теледидарға бұл дереккөзден белгісіз қолданбаларды орнатуға рұқсат берілмейді."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Қауіпсіздікті сақтау үшін телефонға бұл дереккөзден белгісіз қолданбаларды орнатуға рұқсат берілмейді."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Телефон және жеке деректер белгісіз қолданбалардың шабуылына ұшырауы мүмкін. Бұл қолданбаны орнату арқылы оны пайдалану нәтижесіндегі телефонға келетін залалға немесе деректердің жоғалуына өзіңіз ғана жауапты болатыныңызға келісесіз."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Планшет және жеке деректер белгісіз қолданбалардың шабуылына ұшырауы мүмкін. Бұл қолданбаны орнату арқылы оны пайдалану нәтижесіндегі планшетке келетін залалға немесе деректердің жоғалуына өзіңіз ғана жауапты болатыныңызға келісесіз."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Теледидар және жеке деректер белгісіз қолданбалардың шабуылына ұшырауы мүмкін. Бұл қолданбаны орнату арқылы оны пайдалану нәтижесіндегі теледидарға келетін қандай да бір залалға немесе деректердің жоғалуына өзіңіз ғана жауапты болатыныңызға келісесіз."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Жалғастыру"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Параметрлер"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear қолданбасын орнату/жою"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-km-television/strings.xml b/packages/PackageInstaller/res/values-km-television/strings.xml
new file mode 100644
index 0000000..f02744d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-km-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"បដិសេធ ហើយកុំសួរម្តងទៀត"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"អ្នកអាចប្តូរវាពេលក្រោយនៅក្នុងការកំណត់ &gt; កម្មវិធី"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"បង្ហាញកម្មវិធីប្រព័ន្ធ"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"សិទ្ធិអនុញ្ញាតកម្មវិធី"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"សិទ្ធិអនុញ្ញាតកម្មវិធី"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"សិទ្ធិអនុញ្ញាត <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"ការអនុញ្ញាតបន្ថែម"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"សិទ្ធិអនុញ្ញាត <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-km-watch/strings.xml b/packages/PackageInstaller/res/values-km-watch/strings.xml
new file mode 100644
index 0000000..c5f49c5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-km-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"បដិសេធ សូមកុំសួរម្តងទៀត"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"បង្ហាញកម្មវិធីប្រព័ន្ធ"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"មិនអាចប្តូរបានទេ"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"បាទ/ចាស"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"បោះបង់"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-km/strings.xml b/packages/PackageInstaller/res/values-km/strings.xml
new file mode 100644
index 0000000..693ea32
--- /dev/null
+++ b/packages/PackageInstaller/res/values-km/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"កម្មវិធី​ដំឡើង​កញ្ចប់"</string>
+    <string name="next" msgid="3057143178373252333">"បន្ទាប់​"</string>
+    <string name="install" msgid="5896438203900042068">"ដំឡើង"</string>
+    <string name="done" msgid="3889387558374211719">"រួចរាល់"</string>
+    <string name="cancel" msgid="8360346460165114585">"បោះ​បង់​"</string>
+    <string name="installing" msgid="8613631001631998372">"កំពុង​ដំឡើង..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"កំពុង​ដំឡើង <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"បាន​ដំឡើង​កម្មវិធី។"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"តើ​អ្នក​ចង់​ដំឡើង​កម្មវិធី​នេះ? វា​នឹង​មាន​សិទ្ធិ​ចូល៖"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"តើ​អ្នក​ចង់​ដំឡើង​កម្មវិធី​នេះ? វា​មិន​ទាមទារ​សិទ្ធិ​ចូល​ពិសេស​ទេ។"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"តើ​អ្នក​ចង់​ដំឡើង​បច្ចុប្បន្នភាព​កម្មវិធី​ដែល​មាន​ស្រាប់​នេះ? ទិន្នន័យ​ដែល​មាន​ស្រាប់​របស់​អ្នក​នឹង​មិន​បាត់បង់​ទេ។ កម្មវិធី​បាន​ធ្វើ​បច្ចុប្បន្នភាព​នឹង​ចូល​ដំណើរការ​ទៅ៖"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"តើ​អ្នក​ចង់​ដំឡើង​បច្ចុប្បន្នភាព​កម្មវិធី​ដែល​ជាប់​ជា​មួយនេះ? ទិន្នន័យ​ដែល​មាន​ស្រាប់​របស់​អ្នក​នឹង​មិនបាត់បង់ទេ។ កម្មវិធី​បាន​ធ្វើ​បច្ចុប្បន្នភាពហើយ​នឹង​មានសិទ្ធិចូល​៖"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"តើ​អ្នក​ចង់​ដំឡើង​បច្ចុប្បន្នភាព​កម្មវិធី​ដែល​មាន​ស្រាប់​នេះ? ​ទិន្នន័យ​ដែល​មាន​ស្រាប់​របស់​អ្នក​នឹង​មិន​បាត់បង់​ទេ។ វា​មិន​ទាមទារ​ការ​ចូល​ដំណើរការ​ពិសេស​ណាមួយ​ទេ។"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"តើ​អ្នក​ចង់​ដំឡើង​បច្ចុប្បន្នភាព​កម្មវិធី​ដែល​ជាប់​ជា​មួយ? ​ទិន្នន័យ​ដែល​មាន​ស្រាប់​របស់​អ្នក​នឹង​មិន​បាត់បង់​ទេ។ វា​មិន​ទាមទារ​ការ​ចូល​ដំណើរការ​ពិសេស​ណាមួយ​ទេ។"</string>
+    <string name="install_failed" msgid="6579998651498970899">"មិន​បាន​ដំឡើង​កម្មវិធី។"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"កញ្ចប់នេះត្រូវបានរារាំងមិនឲ្យដំឡើង"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"កម្មវិធីមិនបានដំឡើងទេ ដោយសារកញ្ចប់កម្មវិធីមិនត្រូវគ្នាជាមួយកញ្ចប់ដែលមានស្រាប់។"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"កម្មវិធីមិនបានដំឡើងទេ ដោយសារកម្មវិធីមិនត្រូវគ្នាជាមួយថេប្លេតរបស់អ្នក។"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"កម្មវិធីនេះមិនត្រូវគ្នាជាមួយទូរទស្សន៍របស់អ្នកទេ"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"កម្មវិធីមិនបានដំឡើងទេ ដោយសារកម្មវិធីមិនត្រូវគ្នាជាមួយទូរសព្ទរបស់អ្នក។"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"កម្មវិធីមិនបានដំឡើងទេ ដោយសារកញ្ចប់គ្មានសុពលភាព។"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"មិន​អាច​ដំឡើង <xliff:g id="APP_NAME">%1$s</xliff:g> ក្នុង​កុំព្យូទ័រ​បន្ទះ​របស់​អ្នក។"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> មិនអាចដំឡើងនៅលើទូរទស្សន៍របស់អ្នកទេ។"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"មិន​អាច​ដំឡើង <xliff:g id="APP_NAME">%1$s</xliff:g> ក្នុង​ទូរស័ព្ទ​របស់​អ្នក។"</string>
+    <string name="launch" msgid="4826921505917605463">"បើក"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"អ្នក​គ្រប់គ្រង​របស់​អ្នក​មិន​អនុញ្ញាត​ឲ្យ​ដំឡើង​កម្មវិធី​ដែល​បាន​មក​ពី​ប្រភព​ដែលមិន​ស្គាល់ទេ"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"កម្មវិធី​ដែល​មិនស្គាល់​មិនអាច​ដំឡើង​ដោយ​អ្នកប្រើប្រាស់​នេះ​បាន​ទេ"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"អ្នក​ប្រើ​ប្រាស់នេះ​មិនត្រូវបាន​អនុញ្ញាត​ឲ្យ​ដំឡើងកម្មវិធីទេ"</string>
+    <string name="ok" msgid="3468756155452870475">"យល់​ព្រម​"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"គ្រប់គ្រង​កម្មវិធី"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"អស់​ទំហំ"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"មិន​អាច​ដំឡើង <xliff:g id="APP_NAME">%1$s</xliff:g> ។ លុប​ឯកសារ​ខ្លះ ហើយ​ព្យាយាម​ម្ដង​ទៀត។"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"រក​មិន​ឃើញ​កម្មវិធី"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"រក​មិន​ឃើញ​កម្មវិធី​ក្នុង​បញ្ជី​កម្មវិធី​បាន​ដំឡើង។"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"មិន​បាន​អនុញ្ញាត"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"អ្នកប្រើបច្ចុប្បន្នមិនមានការអនុញ្ញាតឱ្យ​ធ្វើការលុបនេះទេ។"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"បញ្ហា"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"មិនអាចលុបកម្មវិធីនេះបានទេ។"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"លុប​កម្មវិធី"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"លុប​បច្ចុប្បន្នភាព"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>​ ​ជា​ផ្នែក​មួយ​នៃ​កម្មវិធី​ដូច​ខាង​ក្រោម​នេះ​៖"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"តើ​អ្នក​ចង់​លុប​កម្មវិធី​នេះ​ឬ?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"តើ​អ្នក​ចង់​លុប​កម្មវិធី​នេះ​សម្រាប់​អ្នកប្រើ "<b>"ទាំងអស់"</b>"? កម្មវិធី និង​ទិន្នន័យ​របស់​វា​នឹង​ត្រូវ​បាន​លុប​ចេញ​ពី​អ្នកប្រើ "<b>"ទាំងអស់"</b>" ក្នុង​ឧបករណ៍​នេះ។"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"តើ​អ្នក​ចង់​លុប​កម្មវិធី​នេះ​សម្រាប់​អ្នកប្រើ <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"ជំនួសកម្មវិធីនេះដោយកំណែរោងចក្រឬ? ទិន្នន័យទាំងអស់នឹងត្រូវបានលុបចេញ។"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"ជំនួយកម្មវិធីនេះដោយកំណែរោងចក្រឬ? ទិន្នន័យទាំងអស់នឹងត្រូវបានលុបចេញ។ វាប៉ះពាល់ដល់អ្នកប្រើឧបករណ៍នេះទាំងអស់ ដោយរាប់បញ្ចូលទាំងអ្នកប្រើដែលមានប្រវត្តិរូបការងារផងដែរ។"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"កំពុង​ដំណើរការ​ការលុប"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"មិន​អាច​ធ្វើការលុប​បានទេ"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"កំពុង​លុប..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"កំពុងលុប <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"បាន​បញ្ចប់​ការ​លុប។"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"បានលុប <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"ការ​លុប​បរាជ័យ។"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"មិនអាចលុប <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> បាន។"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"មិនអាច​លុប​កម្មវិធី​អ្នកគ្រប់គ្រង​ឧបករណ៍​ដែល​បាន​ដំណើរការ​បានទេ"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"មិនអាច​លុប​កម្មវិធី​អ្នកគ្រប់គ្រង​ឧបករណ៍​សម្រាប់ <xliff:g id="USERNAME">%1$s</xliff:g> ដែល​បាន​ដំណើរការ​បាន​ទេ"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"កម្មវិធីនេះតម្រូវឲ្យមានសម្រាប់អ្នកប្រើ ឬប្រវត្តិរូបមួយចំនួន និងត្រូវបានលុបសម្រាប់អ្នកប្រើផ្សេងទៀត"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"កម្មវិធីនេះចាំបាច់សម្រាប់ប្រវតិ្តការងាររបស់អ្នក ហើយវាមិនអាចលុបបានទេ។"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"កម្មវិធីនេះត្រូវបានទាមទារដោយអ្នកគ្រប់គ្រងឧបករណ៍របស់អ្នក ហើយមិនអាចលុប​ចេញបាន​ទេ។"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"គ្រប់គ្រង​កម្មវិធី​អ្នកគ្រប់គ្រង​ឧបករណ៍"</string>
+    <string name="manage_users" msgid="3125018886835668847">"គ្រប់គ្រងអ្នកប្រើ"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"មិន​អាច​លុប <xliff:g id="APP_NAME">%1$s</xliff:g> ។"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"មាន​បញ្ហា​ក្នុង​ការ​ញែក​​កញ្ចប់។"</string>
+    <string name="newPerms" msgid="6039428254474104210">"ថ្មី"</string>
+    <string name="allPerms" msgid="1024385515840703981">"ទាំងអស់"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"ភាព​​ឯកជន"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"ការ​ចូល​ដំណើរការ​ឧបករណ៍"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"បច្ចុប្បន្នភាព​នេះ​មិន​ទាមទារ​សិទ្ធិ​ថ្មី​ទេ។"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"បដិសេធ"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"ព័ត៌មានបន្ថែម"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"បដិសេធទោះយ៉ាងណាក៏ដោយ"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> នៃ <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"អនុញ្ញាតឲ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g> ជានិច្ចមែនទេ?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"ខណៈពេលប្រើ​កម្មវិធីតែប៉ុណ្ណោះ"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"ជានិច្ច"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"បដិសេធ ហើយកុំ​សួរម្តងទៀត"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"បានបិទ <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"បានបិទទាំងអស់"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"មិនបានបិទអ្វីទាំងអស់"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"អនុញ្ញាត"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"កម្មវិធី"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"ការអនុញ្ញាតកម្មវិធី"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"កុំសួរទៀត"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"គ្មានសិទ្ធិអនុញ្ញាត"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"ការអនុញ្ញាតបន្ថែម"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"បើក​ព័ត៌មាន​កម្មវិធី"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ទៀត</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ទៀត</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"កម្មវិធីនេះត្រូវបានរចនាឡើងសម្រាប់កំណែចាស់របស់ Android។ ការបដិសេធសិទ្ធិអនុញ្ញាតអាចបណ្តាលឲ្យវាបំពេញមុខងារមិនដូចអ្វីដែលគេរំពឹងទុកតទៅទៀតទេ។"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"ប្រតិបត្តិការសកម្មភាពមិនស្គាល់"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"បានអនុញ្ញាតកម្មវិធី <xliff:g id="COUNT_0">%1$d</xliff:g> នៃ <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"បង្ហាញប្រព័ន្ធ"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"លាក់ប្រព័ន្ធ"</string>
+    <string name="no_apps" msgid="1965493419005012569">"គ្មានកម្មវិធី"</string>
+    <string name="location_settings" msgid="1774875730854491297">"ការកំណត់ទីតាំង"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> គឺជាអ្នកផ្តល់សេវាកម្មទីតាំងសម្រាប់ឧបករណ៍នេះ។ ការចូលដំណើរការទីតាំងអាចកែសម្រួលបានចេញពីការកំណត់ទីតាំង។"</string>
+    <string name="system_warning" msgid="7103819124542305179">"ប្រសិនបើអ្នកបដិសេធសិទ្ធិអនុញ្ញាតនេះ លក្ខណៈពិសេសគោលនៃឧបករណ៍របស់អ្នកអាចមិនដំណើរការដូចដែលអ្នកចង់បានតទៅទៀតទេ។"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"អនុវត្តតាមគោលការណ៍"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"ការចូលប្រើផ្ទៃខាងក្រោយ​ត្រូវបានបិទដោយគោលការណ៍"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"ការចូលប្រើផ្ទៃខាងក្រោយ​ត្រូវបានបើកដោយគោលការណ៍"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"ការចូលប្រើផ្ទៃខាងមុខ​ត្រូវបានបើកដោយគោលការណ៍"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"គ្រប់គ្រងដោយអ្នកគ្រប់គ្រង"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"ជានិច្ច"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"ខណៈពេលប្រើ​កម្មវិធីតែប៉ុណ្ណោះ"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"កុំឱ្យសោះ"</string>
+    <string name="loading" msgid="7811651799620593731">"កំពុងដំណើរការ..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"សិទ្ធិអនុញ្ញាតទាំងអស់"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"សមត្ថភាពកម្មវិធីផ្សេងទៀត"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"សំណើសុំសិទ្ធិអនុញ្ញាត"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"បានរកឃើញអេក្រង់ត្រួតគ្នា"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"ដើម្បីប្តូរការកំណត់សិទ្ធិអនុញ្ញាតនេះ ជាដំបូងអ្នកត្រូវបិទអេក្រង់ត្រួតគ្នានៅក្នុង ការកំណត់ &gt; កម្មវិធី"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"បើកការកំណត់"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"សកម្មភាពដំឡើង/លុបការដំឡើងមិនគាំទ្រនៅលើ Wear ទេ"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"ជ្រើសរើសអ្វីដែលត្រូវអនុញ្ញាតឲ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលដំណើរការ"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ត្រូវបានអាប់ដេត។ ជ្រើសរើសអ្វីដែលត្រូវអនុញ្ញាតឲ្យកម្មវិធីនេះចូលដំណើរការ។"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"បោះបង់"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"បន្ត"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"សិទ្ធិអនុញ្ញាតថ្មី"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"សិទ្ធិអនុញ្ញាតបច្ចុប្បន្ន"</string>
+    <string name="message_staging" msgid="6151794817691100003">"កំពុងសាកល្បងកម្មវិធី…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"មិនស្គាល់"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"ដើម្បីការពារសុវតិ្ថភាពរបស់អ្នក ថេប្លេតរបស់អ្នកមិនត្រូវបានអនុញ្ញាតឲ្យដំឡើងកម្មវិធីដែលមិនស្គាល់ពីប្រភពនេះទេ។"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"ដើម្បីការពារសុវតិ្ថភាពរបស់អ្នក ទូរទស្សន៍របស់អ្នកមិនត្រូវបានអនុញ្ញាតឲ្យដំឡើងកម្មវិធីដែលមិនស្គាល់ពីប្រភពនេះទេ។"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"ដើម្បីការពារសុវតិ្ថភាពរបស់អ្នក ទូរសព្ទរបស់អ្នកមិនត្រូវបានអនុញ្ញាតឲ្យដំឡើងកម្មវិធីដែលមិនស្គាល់ពីប្រភពនេះទេ។"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"ទូរសព្ទ និងទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នកងាយនឹងរងគ្រោះពីការវាយប្រហារពីកម្មវិធីដែលមិនស្គាល់។ ប្រសិនបើដំឡើងកម្មវិធីនេះ មានន័យថាអ្នកទទួលខុសត្រូវលើការខូចខាតណាមួយចំពោះទូរសព្ទ ឬការបាត់បង់ទិន្នន័យរបស់អ្នក ដែលអាចបណ្តាលមកពីការប្រើប្រាស់កម្មវិធីនោះ។"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"ថេប្លេត និងទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នកងាយនឹងរងគ្រោះពីការវាយប្រហារពីកម្មវិធីដែលមិនស្គាល់។ ប្រសិនបើដំឡើងកម្មវិធីនេះ មានន័យថាអ្នកទទួលខុសត្រូវលើការខូចខាតណាមួយចំពោះថេប្លេត ឬការបាត់បង់ទិន្នន័យរបស់អ្នក ដែលអាចបណ្តាលមកពីការប្រើប្រាស់កម្មវិធីនោះ។"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"ទូរទស្សន៍ និងទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នកងាយនឹងរងគ្រោះពីការវាយប្រហារពីកម្មវិធីដែលមិនស្គាល់។ ប្រសិនបើដំឡើងកម្មវិធីនេះ មានន័យថាអ្នកទទួលខុសត្រូវលើការខូចខាតណាមួយចំពោះទូរទស្សន៍ ឬការបាត់បង់ទិន្នន័យរបស់អ្នក ដែលអាចបណ្តាលមកពីការប្រើប្រាស់កម្មវិធីនោះ។"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"បន្ត"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"ការ​កំណត់"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"ការដំឡើង/ការលុបកម្មវិធីឧបករណ៍​ពាក់​"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-kn-television/strings.xml b/packages/PackageInstaller/res/values-kn-television/strings.xml
new file mode 100644
index 0000000..2af5a4b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-kn-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"ನಿರಾಕರಿಸಿ ಹಾಗೂ ಮತ್ತೊಮ್ಮೆ ಕೇಳಬೇಡ"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"ನೀವು ಇದನ್ನು ನಂತರದಲ್ಲಿ ಸೆಟ್ಟಿಂಗ್‍‍ಗಳು &gt; ಅಪ್ಲಿಕೇಶನ್‍‍ಗಳಲ್ಲಿ ಬದಲಾಯಿಸಬಹುದು"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"ಸಿಸ್ಟಂ ಅಪ್ಲಿಕೇಶನ್‌‌ಗಳನ್ನು ತೋರಿಸು"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"ಅಪ್ಲಿಕೇಶನ್ ಅನುಮತಿಗಳು"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"ಅಪ್ಲಿಕೇಶನ್ ಅನುಮತಿಗಳು"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> ಅನುಮತಿಗಳು"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"ಹೆಚ್ಚುವರಿ ಅನುಮತಿಗಳು"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> ಅನುಮತಿಗಳು"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-kn-watch/strings.xml b/packages/PackageInstaller/res/values-kn-watch/strings.xml
new file mode 100644
index 0000000..1a9a993
--- /dev/null
+++ b/packages/PackageInstaller/res/values-kn-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"ನಿರಾಕರಿಸಿ, ಮತ್ತೆ ಕೇಳಬೇಡಿ"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"ಸಿಸ್ಟಂ ಅಪ್ಲಿಕೇಶನ್‌‌ಗಳನ್ನು ತೋರಿಸು"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"ಬದಲಾಯಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"ಹೌದು"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"ರದ್ದುಮಾಡಿ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-kn/strings.xml b/packages/PackageInstaller/res/values-kn/strings.xml
new file mode 100644
index 0000000..f42035c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-kn/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"ಪ್ಯಾಕೇಜ್ ಸ್ಥಾಪಕ"</string>
+    <string name="next" msgid="3057143178373252333">"ಮುಂದೆ"</string>
+    <string name="install" msgid="5896438203900042068">"ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
+    <string name="done" msgid="3889387558374211719">"ಮುಗಿದಿದೆ"</string>
+    <string name="cancel" msgid="8360346460165114585">"ರದ್ದುಮಾಡಿ"</string>
+    <string name="installing" msgid="8613631001631998372">"ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ…"</string>
+    <string name="install_done" msgid="3682715442154357097">"ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಲಾಗಿದೆ."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"ನೀವು ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಲು ಬಯಸುವಿರಾ? ಇದು ಇಲ್ಲಿಗೆ ಪ್ರವೇಶ ಪಡೆದುಕೊಳ್ಳುತ್ತದೆ:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"ನೀವು ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಲು ಬಯಸುವಿರಾ? ಇದಕ್ಕೆ ಯಾವುದೇ ವಿಶೇಷ ಪ್ರವೇಶದ ಅಗತ್ಯವಿಲ್ಲ."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"ನೀವು ಈ ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಅಪ್ಲಿಕೇಶನ್‍ನ ಅಪ್‌ಡೇಟ್‌‌ ಆದ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಲು ಬಯಸುವಿರಾ? ಈಗಿರುವ ನಿಮ್ಮ ಡೇಟಾ ಕಳೆದು ಹೋಗುವುದಿಲ್ಲ. ಅಪ್‌ಡೇಟ್‌‌ ಆದ ಅಪ್ಲಿಕೇಶನ್ ಇಲ್ಲಿಗೆ ಪ್ರವೇಶ ಪಡೆದುಕೊಳ್ಳುತ್ತದೆ:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"ನೀವು ಈ ಬಿಲ್ಟ್-ಇನ್-ಅಪ್ಲಿಕೇಶನ್‌ನ ಅಪ್‌ಡೇಟ್‌‌ ಆದ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಿಕೊಳ್ಳಲು ಬಯಸುವಿರಾ? ಈಗಿರುವ ನಿಮ್ಮ ಡೇಟಾ ಕಳೆದು ಹೋಗುವುದಿಲ್ಲ. ಅಪ್‌ಡೇಟ್‌‌ ಆದ ಅಪ್ಲಿಕೇಶನ್ ಇಲ್ಲಿಗೆ ಪ್ರವೇಶ ಪಡೆದುಕೊಳ್ಳುತ್ತದೆ:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"ನೀವು ಈ ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಅಪ್ಲಿಕೇಶನ್‍ನ ಅಪ್‌ಡೇಟ್‌‌ ಆದ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಲು ಬಯಸುವಿರಾ? ಈಗಿರುವ ನಿಮ್ಮ ಡೇಟಾ ಕಳೆದು ಹೋಗುವುದಿಲ್ಲ. ಇದಕ್ಕೆ ಯಾವುದೇ ವಿಶೇಷ ಪ್ರವೇಶದ ಅಗತ್ಯವಿಲ್ಲ."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"ನೀವು ಈ ಬಿಲ್ಟ್-ಇನ್-ಅಪ್ಲಿಕೇಶನ್‌ನ ಅಪ್‌ಡೇಟ್‌‌ ಆದ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಿಕೊಳ್ಳಲು ಬಯಸುವಿರಾ? ಈಗಿರುವ ನಿಮ್ಮ ಡೇಟಾ ಕಳೆದು ಹೋಗುವುದಿಲ್ಲ. ಇದಕ್ಕೆ ಯಾವುದೇ ವಿಶೇಷ ಪ್ರವೇಶದ ಅಗತ್ಯವಿಲ್ಲ."</string>
+    <string name="install_failed" msgid="6579998651498970899">"ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"ಸ್ಥಾಪಿಸುವಿಕೆಯಿಂದ ಪ್ಯಾಕೇಜ್‌ ಅನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"ಪ್ಯಾಕೇಜ್‌ನಂತೆ ಸ್ಥಾಪಿತವಾಗದಿರುವ ಅಪ್ಲಿಕೇಶನ್ ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಪ್ಯಾಕೇಜ್ ಜೊತೆಗೆ ಸಂಘರ್ಷವಾಗುತ್ತದೆ."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"ಅಪ್ಲಿಕೇಶನ್‌ನಂತೆ ಸ್ಥಾಪಿತವಾಗದಿರುವ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಜೊತೆಗೆ ಹೊಂದಾಣಿಕೆಯಾಗುವುದಿಲ್ಲ."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಟಿವಿ ಜೊತೆ ಹೊಂದಾಣಿಕೆಯಾಗುವುದಿಲ್ಲ."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"ಅಪ್ಲಿಕೇಶನ್‌ನಂತೆ ಸ್ಥಾಪಿತವಾಗದಿರುವ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಫೋನ್ ಜೊತೆಗೆ ಹೊಂದಾಣಿಕೆಯಾಗುವುದಿಲ್ಲ."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"ಪ್ಯಾಕೇಜ್‌ನಂತೆ ಸ್ಥಾಪಿತವಾಗದಿರುವ ಅಪ್ಲಿಕೇಶನ್ ಅಮಾನ್ಯವಾಗಿರುವಂತೆ ತೋರುತ್ತದೆ."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್‌ನಲ್ಲಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಅನ್ನು ನಿಮ್ಮ ಟಿವಿಗೆ ಸ್ಥಾಪಿಸಲಾಗುವುದಿಲ್ಲ."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ."</string>
+    <string name="launch" msgid="4826921505917605463">"ತೆರೆ"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ಅಪರಿಚಿತ ಮೂಲಗಳ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಸ್ಥಾಪನೆಯನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"ಈ ಬಳಕೆದಾರರು ಅಪರಿಚಿತ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ಈ ಬಳಕೆದಾರರನ್ನು ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ"</string>
+    <string name="ok" msgid="3468756155452870475">"ಸರಿ"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"ಖಾಲಿ ಇಲ್ಲ"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಅನ್ನು ಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಕೊಂಚ ಸ್ಥಳವನ್ನು ಖಾಲಿ ಮಾಡಿ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"ಅಪ್ಲಿಕೇಶನ್ ಕಂಡುಬಂದಿಲ್ಲ"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"ಸ್ಥಾಪಿಸಲಾಗಿರುವ ಅಪ್ಲಿಕೇಶನ್‍ಗಳ ಪಟ್ಟಿಯಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್ ಕಂಡುಬಂದಿಲ್ಲ."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"ಈ ಅಸ್ಥಾಪಿಸುವಿಕೆಯನ್ನು ಪ್ರಸ್ತುತ ಬಳಕೆದಾರರಿಗೆ ನಿರ್ವಹಿಸಲು ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"ದೋಷ"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"ಅಪ್ಲಿಕೇಶನ್ ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"ಅಪ್ಲಿಕೇಶನ್ ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"ನವೀಕರಣವನ್ನು ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ಎಂಬುದು ಕೆಳಗಿನ ಅಪ್ಲಿಕೇಶನ್‌ನ ಭಾಗವಾಗಿದೆ:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"ನೀವು ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಅಸ್ಥಾಪಿಸಲು ಬಯಸುವಿರಾ?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"ನೀವು "<b>"ಎಲ್ಲಾ"</b>" ಬಳಕೆದಾರರಿಗೂ ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಅಸ್ಥಾಪಿಸಲು ಬಯಸುವಿರಾ? ಸಾಧನದಲ್ಲಿನ "<b>"ಎಲ್ಲಾ"</b>" ಬಳಕೆದಾರರಿಂದ ಅಪ್ಲಿಕೇಶನ್ ಮತ್ತು ಅದರ ಡೇಟಾವನ್ನು ತೆಗೆದುಹಾಕಲಾಗುವುದು."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"<xliff:g id="USERNAME">%1$s</xliff:g> ಬಳಕೆದಾರರಿಗೆ ಈ ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಅಸ್ಥಾಪಿಸಲು ನೀವು ಬಯಸುವಿರಾ?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಬದಲಿಗೆ ಫ್ಯಾಕ್ಟರಿ ಆವೃತ್ತಿಯನ್ನು ಬದಲಾಯಿಸುವುದೇ? ಎಲ್ಲಾ ಡೇಟಾ ತೆಗೆದುಹಾಕಲಾಗುತ್ತದೆ."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಬದಲಿಗೆ ಫ್ಯಾಕ್ಟರಿ ಆವೃತ್ತಿಯನ್ನು ಬದಲಾಯಿಸುವುದೇ? ಎಲ್ಲಾ ಡೇಟಾ ತೆಗೆದುಹಾಕಲಾಗುತ್ತದೆ. ಕೆಲಸದ ಪ್ರೊಫೈಲ್‌ಗಳನ್ನು ಹೊಂದಿರುವವುಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ಈ ಸಾಧನದ ಎಲ್ಲಾ ಬಳಕೆದಾರರಿಗೆ ಇದು ಪರಿಣಾಮ ಬೀರುತ್ತದೆ."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"ಚಾಲನೆಯಲ್ಲಿರುವ ಅಸ್ಥಾಪನೆಗಳು"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"ವಿಫಲಗೊಂಡ ಅಸ್ಥಾಪನೆಗಳು"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"ಅಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ಅಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್ ಪೂರ್ಣಗೊಂಡಿದೆ."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ಅಸ್ಥಾಪಿಸಲಾಗಿದೆ"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್ ವಿಫಲವಾಗಿದೆ."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ಅಸ್ಥಾಪಿಸುವಿಕೆ ಯಶಸ್ವಿಯಾಗಲಿಲ್ಲ."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"ಸಕ್ರಿಯ ಸಾಧನ ನಿರ್ವಹಣೆ ಅಪ್ಲಿಕೇಶನ್ ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> ಗಾಗಿ ಸಕ್ರಿಯ ಸಾಧನ ನಿರ್ವಹಣೆ ಅಪ್ಲಿಕೇಶನ್ ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"ಕೆಲವು ಬಳಕೆದಾರರು ಅಥವಾ ಪ್ರೊಫೈಲ್‌ಗಳಿಗೆ ಈ ಅಪ್ಲಿಕೇಶನ್ ಅಗತ್ಯವಿರುತ್ತದೆ ಮತ್ತು ಇತರರಿಗೆ ಅಸ್ಥಾಪಿಸಲಾಗಿದೆ"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"ಈ ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ನಿಮ್ಮ ಪ್ರೊಫೈಲ್‌‌ನ ಅಗತ್ಯವಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಸಾಧನ ನಿರ್ವಾಹಕರಿಗೆ ಅಗತ್ಯವಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"ಸಾಧನದ ನಿರ್ವಹಣೆ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string>
+    <string name="manage_users" msgid="3125018886835668847">"ಬಳಕೆದಾರರನ್ನು ನಿರ್ವಹಿಸಿ"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಅನ್ನು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"ಪ್ಯಾಕೇಜ್ ಪಾರ್ಸ್ ಮಾಡುವಲ್ಲಿ ಸಮಸ್ಯೆ ಕಂಡುಬಂದಿದೆ."</string>
+    <string name="newPerms" msgid="6039428254474104210">"ಹೊಸತು"</string>
+    <string name="allPerms" msgid="1024385515840703981">"ಎಲ್ಲಾ"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"ಗೌಪ್ಯತೆ"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"ಸಾಧನ ಪ್ರವೇಶ"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"ಈ ನವೀಕರಣಕ್ಕೆ ಯಾವುದೇ ಹೊಸ ಅನುಮತಿಗಳ ಅಗತ್ಯವಿಲ್ಲ."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"ನಿರಾಕರಿಸಿ"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"ಇನ್ನಷ್ಟು ಮಾಹಿತಿ"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"ಹೇಗಾದರೂ ನಿರಾಕರಿಸಿ"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> ರಲ್ಲಿ <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"<xliff:g id="ACTION">%2$s</xliff:g> <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನ್ನು ಅನುಮತಿಸುವುದೇ?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to <xliff:g id="ACTION">%2$s</xliff:g> ಅನ್ನು ಯಾವಾಗಲೂ ಅನುಮತಿಸುವುದೇ?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"ಅಪ್ಲಿಕೇಶನ್ ಬಳಸುವಾಗ ಮಾತ್ರ"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"ಯಾವಾಗಲೂ"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"ನಿರಾಕರಿಸಿ ಹಾಗೂ ಮತ್ತೊಮ್ಮೆ ಕೇಳಬೇಡ"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"ಎಲ್ಲಾ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ಯಾವುದನ್ನೂ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿಲ್ಲ"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"ಅನುಮತಿಸಿ"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"ಅಪ್ಲಿಕೇಶನ್ ಅನುಮತಿಗಳು"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"ಮತ್ತೆ ಕೇಳಬೇಡಿ"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"ಯಾವುದೇ ಅನುಮತಿಗಳಿಲ್ಲ"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"ಹೆಚ್ಚುವರಿ ಅನುಮತಿಗಳು"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿಯನ್ನು ತೆರೆಯಿರಿ"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <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="old_sdk_deny_warning" msgid="3872277112584842615">"ಈ ಅಪ್ಲಿಕೇಶನ್ Android ನ ಹಳೆಯ ಆವೃತ್ತಿಗೆ ವಿನ್ಯಾಸಗೊಳಿಸಲಾಗಿತ್ತು. ಅನುಮತಿ ನಿರಾಕರಿಸುವಿಕೆ ಇನ್ನು ಮುಂದೆ ಉದ್ದೇಶಿಸಲ್ಪಟ್ಟಂತೆ ಕಾರ್ಯನಿರ್ವಹಿಸದೆ ಇರುವುದಕ್ಕೆ ಇದು ಕಾರಣವಾಗಬಹುದು."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"ಅಪರಿಚಿತ ಕ್ರಿಯೆಯನ್ನು ಮಾಡಿ"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g> ರಲ್ಲಿ <xliff:g id="COUNT_0">%1$d</xliff:g> ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಅನುಮತಿಸಲಾಗಿದೆ"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"ಸಿಸ್ಟಂ ತೋರಿಸು"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"ಸಿಸ್ಟಂ ಮರೆಮಾಡು"</string>
+    <string name="no_apps" msgid="1965493419005012569">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಇಲ್ಲ"</string>
+    <string name="location_settings" msgid="1774875730854491297">"ಸ್ಥಳ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಅಪ್ಲಿಕೇಶನ್ ಈ ಸಾಧನಕ್ಕೆ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಒದಗಿಸುತ್ತದೆ. ಸ್ಥಳ ಪ್ರವೇಶವನ್ನು ಸ್ಥಳ ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಂದ ಮಾರ್ಪಡಿಸಬಹುದು."</string>
+    <string name="system_warning" msgid="7103819124542305179">"ನೀವು ಈ ಅನುಮತಿಯನ್ನು ನಿರಾಕರಿಸಿದರೆ, ಇನ್ನು ಮುಂದೆ ನಿಮ್ಮ ಸಾಧನದ ಮೂಲ ವೈಶಿಷ್ಟ್ಯಗಳು ಉದ್ದೇಶಿದಂತೆ ಕಾರ್ಯನಿರ್ವಹಿಸದಿರಬಹುದು."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"ನೀತಿಯ ಮೂಲಕ ಜಾರಿಗೊಳಿಸಲಾಗಿದೆ"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"ನೀತಿಯ ಮೂಲಕ ಹಿನ್ನೆಲೆ ಪ್ರವೇಶವನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"ನೀತಿ ಮೂಲಕ ಹಿನ್ನೆಲೆ ಪ್ರವೇಶವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾದ"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"ನೀತಿ ಮೂಲಕ ಮುನ್ನೆಲೆ ಪ್ರವೇಶವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾದ"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"ನಿರ್ವಾಹಕರ ಮೂಲಕ ನಿಯಂತ್ರಿಸಲಾಗಿದೆ"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"ಯಾವಾಗಲೂ"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"ಅಪ್ಲಿಕೇಶನ್ ಬಳಸುವಾಗ ಮಾತ್ರ"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"ಎಂದೂ ಇಲ್ಲ"</string>
+    <string name="loading" msgid="7811651799620593731">"ಲೋಡ್ ಆಗುತ್ತಿದೆ..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"ಎಲ್ಲ ಅನುಮತಿಗಳು"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"ಇತರ ಅಪ್ಲಿಕೇಶನ್ ಸಾಮರ್ಥ್ಯಗಳು"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"ಅನುಮತಿ ವಿನಂತಿ"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"ಪರದೆ ಆವರಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ಪತ್ತೆಹಚ್ಚಲಾಗಿದೆ"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"ಈ ಅನುಮತಿ ಸೆಟ್ಟಿಂಗ್ ಬದಲಾಯಿಸಲು, ನೀವು ಮೊದಲು ಸೆಟ್ಟಿಂಗ್‌ಗಳು &gt; ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಂದ ಪರದೆ ಆವರಿಸಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ಆಫ್ ಮಾಡಬೇಕು"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"ಸೆಟ್ಟಿಂಗ್‍ಗಳನ್ನು ತೆರೆಯಿರಿ"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear ನಲ್ಲಿ ಸ್ಥಾಪಿಸುವಿಕೆ/ಅಸ್ಥಾಪಿಸುವಿಕೆ ಕ್ರಿಯೆಗಳು ಬೆಂಬಲಿತವಾಗಿಲ್ಲ."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಪ್ರವೇಶಿಸಲು ಯಾವುದನ್ನು ಅನುಮತಿಸಬೇಕು ಎಂಬುದನ್ನು ಆರಿಸಿಕೊಳ್ಳಿ"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲಾಗಿದೆ. ಈ ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಪ್ರವೇಶಿಸಲು ಯಾವುದನ್ನು ಅನುಮತಿಸಬೇಕು ಎಂಬುದನ್ನು ಆರಿಸಿಕೊಳ್ಳಿ."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"ರದ್ದುಮಾಡಿ"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"ಮುಂದುವರಿಸು"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"ಹೊಸ ಅನುಮತಿಗಳು"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"ಪ್ರಸ್ತುತ ಅನುಮತಿಗಳು"</string>
+    <string name="message_staging" msgid="6151794817691100003">"ಸ್ಥಾಪಿಸಲು ಸಿದ್ಧವಿರುವ ಅಪ್ಲಿಕೇಶನ್…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"ಅಪರಿಚಿತ"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"ನಿಮ್ಮ ಸುರಕ್ಷತೆಯ ದೃಷ್ಟಿಯಿಂದ, ಈ ಮೂಲದಿಂದ ಬಂದಿರುವ ಅಪರಿಚಿತ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್‌ಗೆ ಅನುಮತಿಯಿಲ್ಲ."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"ನಿಮ್ಮ ಸುರಕ್ಷತೆಯ ದೃಷ್ಟಿಯಿಂದ, ಅಪರಿಚಿತ ಮೂಲಗಳಿಂದ ಪಡೆದುಕೊಳ್ಳುವ ಅಪ್ಲಿಕೇಶನ್‍‍ಗಳನ್ನು ನಿಮ್ಮ ಟಿವಿಯು ಸ್ಥಾಪಿಸದಂತೆ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"ನಿಮ್ಮ ಸುರಕ್ಷತೆಯ ದೃಷ್ಟಿಯಿಂದ, ಈ ಮೂಲದಿಂದ ಬಂದಿರುವ ಅಪರಿಚಿತ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ನಿಮ್ಮ ಫೋನ್‌ಗೆ ಅನುಮತಿಯಿಲ್ಲ."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"ನಿಮ್ಮ ಫೋನ್ ಹಾಗೂ ವೈಯಕ್ತಿಕ ಡೇಟಾ, ಅಪರಿಚಿತ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ದಾಳಿಗೆ ತುತ್ತಾಗುವ ಸಾಧ್ಯತೆ ಹೆಚ್ಚಾಗಿದೆ. ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸ್ಥಾಪಿಸುವ ಮೂಲಕ, ನಿಮ್ಮ ಫೋನ್‌ಗೆ ಯಾವುದೇ ಹಾನಿ ಉಂಟಾದರೆ ಅಥವಾ ಅದರ ಬಳಕೆಯಿಂದ ಡೇಟಾ ನಷ್ಟವಾದರೆ, ಅದಕ್ಕೆ ನೀವೇ ಜವಾಬ್ದಾರರು ಎನ್ನುವುದನ್ನು ಒಪ್ಪಿಕೊಳ್ಳುತ್ತೀರಿ."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಹಾಗೂ ವೈಯಕ್ತಿಕ ಡೇಟಾ, ಅಪರಿಚಿತ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ದಾಳಿಗೆ ತುತ್ತಾಗುವ ಸಾಧ್ಯತೆ ಹೆಚ್ಚಾಗಿದೆ. ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸ್ಥಾಪಿಸುವ ಮೂಲಕ, ನಿಮ್ಮ ಫೋನ್‌ಗೆ ಯಾವುದೇ ಹಾನಿ ಉಂಟಾದರೆ ಅಥವಾ ಅದರ ಬಳಕೆಯಿಂದ ಡೇಟಾ ನಷ್ಟವಾದರೆ, ಅದಕ್ಕೆ ನೀವೇ ಜವಾಬ್ದಾರರು ಎನ್ನುವುದನ್ನು ಒಪ್ಪಿಕೊಳ್ಳುತ್ತೀರಿ."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"ನಿಮ್ಮ ಟಿವಿ ಹಾಗೂ ವೈಯಕ್ತಿಕ ಡೇಟಾ, ಅಪರಿಚಿತ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ದಾಳಿಗೆ ತುತ್ತಾಗುವ ಸಾಧ್ಯತೆ ಹೆಚ್ಚಾಗಿದೆ. ಈ ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಸ್ಥಾಪಿಸುವ ಮೂಲಕ, ನಿಮ್ಮ ಟಿವಿಗೆ ಯಾವುದೇ ಹಾನಿ ಉಂಟಾದರೆ ಅಥವಾ ಅದರ ಬಳಕೆಯಿಂದ ಡೇಟಾ ನಷ್ಟವಾದರೆ, ಅದಕ್ಕೆ ನೀವೇ ಜವಾಬ್ದಾರರು ಎನ್ನುವುದನ್ನು ಒಪ್ಪಿಕೊಳ್ಳುತ್ತೀರಿ."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"ಮುಂದುವರಿಸಿ"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"wear ಅಪ್ಲಿಕೇಶನ್‌ ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ/ಅಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ko-television/strings.xml b/packages/PackageInstaller/res/values-ko-television/strings.xml
new file mode 100644
index 0000000..6ad2ed6
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ko-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"거부 및 다시 묻지 않음"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"설정 &gt; 앱에서 나중에 변경할 수 있습니다."</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>개 중 <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"시스템 앱 보기"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"앱 권한"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"앱 권한"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> 권한"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"추가 권한"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> 권한"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ko-watch/strings.xml b/packages/PackageInstaller/res/values-ko-watch/strings.xml
new file mode 100644
index 0000000..6cce6f9
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ko-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"거부 및 다시 묻지 않음"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"시스템 앱 보기"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"변경할 수 없음"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"예"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"취소"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ko/strings.xml b/packages/PackageInstaller/res/values-ko/strings.xml
new file mode 100644
index 0000000..235a1d4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ko/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"패키지 설치 프로그램"</string>
+    <string name="next" msgid="3057143178373252333">"다음"</string>
+    <string name="install" msgid="5896438203900042068">"설치"</string>
+    <string name="done" msgid="3889387558374211719">"완료"</string>
+    <string name="cancel" msgid="8360346460165114585">"취소"</string>
+    <string name="installing" msgid="8613631001631998372">"설치 중..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> 설치 중…"</string>
+    <string name="install_done" msgid="3682715442154357097">"앱이 설치되었습니다."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"이 애플리케이션을 설치하시겠습니까? 애플리케이션이 다음에 액세스할 수 있습니다."</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"이 애플리케이션을 설치하시겠습니까? 특별한 액세스 권한이 필요하지 않습니다."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"기존 애플리케이션에 업데이트를 설치하시겠습니까? 기존 데이터는 손실되지 않습니다. 업데이트된 애플리케이션이 다음에 액세스할 수 있습니다."</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"내장 애플리케이션에 업데이트를 설치하시겠습니까? 기존 데이터는 손실되지 않습니다. 업데이트된 애플리케이션이 다음에 액세스할 수 있습니다."</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"기존의 애플리케이션 업데이트를 설치하시겠습니까? 기존의 데이터는 손실되지 않으며 특별한 액세스 권한이 필요하지 않습니다."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"내장 애플리케이션 업데이트를 설치하시겠습니까? 기존의 데이터는 손실되지 않으며 특별한 액세스 권한이 필요하지 않습니다."</string>
+    <string name="install_failed" msgid="6579998651498970899">"앱이 설치되지 않았습니다."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"패키지 설치가 차단되었습니다."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"패키지가 기존 패키지와 충돌하여 앱이 설치되지 않았습니다."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"앱이 태블릿과 호환되지 않아서 설치되지 않았습니다."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"앱이 사용 중인 TV와 호환되지 않습니다."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"앱이 휴대전화와 호환되지 않아서 설치되지 않았습니다."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"패키지가 잘못되어 앱이 설치되지 않았습니다."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"태블릿에 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱을 설치할 수 없습니다."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g>을(를) TV에 설치할 수 없습니다."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"휴대전화에 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱을 설치할 수 없습니다."</string>
+    <string name="launch" msgid="4826921505917605463">"열기"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"관리자가 알 수 없는 출처의 앱 설치를 허용하지 않습니다."</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"이 사용자는 알 수 없는 앱을 설치할 수 없습니다."</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"이 사용자는 앱을 설치할 권한이 없습니다."</string>
+    <string name="ok" msgid="3468756155452870475">"확인"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"앱 관리"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"여유 공간이 없음"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱을 설치할 수 없습니다. 여유 공간을 늘린 후에 다시 시도하세요."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"앱을 찾을 수 없음"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"설치된 앱 목록에 앱이 없습니다."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"허용되지 않음"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"현재 사용자는 이 제거를 수행할 수 없습니다."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"오류"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"앱을 제거할 수 없습니다."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"앱 제거"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"업데이트 제거"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>은(는) 다음 앱의 일부입니다."</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"이 앱을 제거하시겠습니까?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222"><b>"모든"</b>" 사용자에 대해 이 앱을 제거하시겠습니까? 기기를 사용하는 "<b>"모든"</b>" 사용자에 대해 애플리케이션 및 데이터가 삭제됩니다."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"<xliff:g id="USERNAME">%1$s</xliff:g>님의 기기에 설치된 앱을 제거하시겠습니까?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"이 앱을 초기 버전으로 바꾸시겠습니까? 모든 데이터가 삭제됩니다."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"이 앱을 초기 버전으로 바꾸시겠습니까? 모든 데이터가 삭제되며 직장 프로필 사용자를 포함해 이 기기의 모든 사용자에게 영향을 미칩니다."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"실행 중인 제거 작업"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"실패한 제거 작업"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"제거 중..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> 제거 중…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"제거를 완료했습니다."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>을(를) 제거했습니다."</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"제거하지 못했습니다."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>을(를) 제거하지 못했습니다."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"활성 상태의 기기 관리자 앱을 제거할 수 없습니다."</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g>의 활성 상태의 기기 관리자 앱을 제거할 수 없습니다."</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"이 앱은 일부 사용자 또는 프로필에 필요하며 다른 사용자에 대해서는 제거되었습니다."</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"이 앱은 프로필에 필요하므로 삭제할 수 없습니다."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"이 앱은 기기 관리자에게 필요하므로 제거할 수 없습니다."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"기기 관리자 앱 관리"</string>
+    <string name="manage_users" msgid="3125018886835668847">"사용자 관리"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱을 제거할 수 없습니다."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"패키지를 파싱하는 중 문제가 발생했습니다."</string>
+    <string name="newPerms" msgid="6039428254474104210">"새 권한"</string>
+    <string name="allPerms" msgid="1024385515840703981">"전체"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"개인정보 보호"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"기기 액세스"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"이 업데이트에는 새로운 권한이 필요하지 않습니다."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"거부"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"추가 정보"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"거부"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;의 다음 작업을 허용하시겠습니까? <xliff:g id="ACTION">%2$s</xliff:g>"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 <xliff:g id="ACTION">%2$s</xliff:g>하도록 허용하시겠습니까??"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"앱 사용 중에만"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"항상"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"거부 및 다시 묻지 않음"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g>개 중지됨"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"모두 중지됨"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"아무것도 중지되지 않음"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"허용"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"앱"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"앱 권한"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"다시 묻지 않음"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"권한이 없음"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"추가 권한"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"앱 정보 열기"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>개 더보기</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g>개 더보기</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"이 앱은 Android 이전 버전에 맞게 설계되었습니다. 권한을 거부하면 정상적으로 작동하지 않을 수 있습니다."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"알 수 없는 작업 수행"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g>개 앱 허용됨"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"시스템 표시"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"시스템 숨기기"</string>
+    <string name="no_apps" msgid="1965493419005012569">"앱 없음"</string>
+    <string name="location_settings" msgid="1774875730854491297">"위치 설정"</string>
+    <string name="location_warning" msgid="8778701356292735971">"이 기기의 위치 서비스 제공업체는 <xliff:g id="APP_NAME">%1$s</xliff:g>입니다. 위치 정보 액세스는 위치 설정에서 수정할 수 있습니다."</string>
+    <string name="system_warning" msgid="7103819124542305179">"이 권한을 거부하는 경우 기기의 기본 기능이 제대로 작동하지 않을 수 있습니다."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"정책에 의해 시행됨"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"정책에 따라 백그라운드 액세스 사용 중지됨"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"정책에 따라 백그라운드 액세스 사용 설정함"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"정책에 따라 포그라운드 액세스 사용 설정함"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"관리자가 제어"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"항상"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"앱 사용 중에만"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"사용 안함"</string>
+    <string name="loading" msgid="7811651799620593731">"로드 중..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"모든 권한"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"다른 앱 기능"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"권한 요청"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"화면 오버레이 감지됨"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"이 권한 설정을 변경하려면 먼저 설정 &gt; 앱에서 화면 오버레이를 사용하지 않도록 설정해야 합니다."</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"설정 열기"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear에서 지원하지 않는 설치/제거 작업입니다."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 액세스하도록 허용할 항목을 선택하세요."</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;이(가) 업데이트되었습니다. 이 앱에서 액세스하도록 허용할 항목을 선택하세요."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"취소"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"계속"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"새로운 권한"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"기존 권한"</string>
+    <string name="message_staging" msgid="6151794817691100003">"앱 준비 중…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"알 수 없음"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"보안상의 이유로 이 경로를 통한 알 수 없는 앱을 태블릿에 설치할 수 없습니다."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"보안상의 이유로 이 경로를 통한 알 수 없는 앱을 TV에 설치할 수 없습니다."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"보안상의 이유로 이 경로를 통한 알 수 없는 앱을 휴대전화에 설치할 수 없습니다."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"휴대전화와 개인 데이터는 알 수 없는 앱의 공격에 더욱 취약합니다. 이 앱을 설치하면 앱 사용으로 인해 발생할 수 있는 모든 휴대전화 손상이나 데이터 손실에 사용자가 책임을 진다는 것에 동의하게 됩니다."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"태블릿과 개인 데이터는 알 수 없는 앱의 공격에 더욱 취약합니다. 이 앱을 설치하면 앱 사용으로 인해 발생할 수 있는 모든 태블릿 손상이나 데이터 손실에 사용자가 책임을 진다는 것에 동의하게 됩니다."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"TV와 개인 데이터는 알 수 없는 앱의 공격에 더욱 취약합니다. 이 앱을 설치하면 앱 사용으로 인해 발생할 수 있는 모든 TV 손상이나 데이터 손실에 사용자가 책임을 진다는 것에 동의하게 됩니다."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"계속"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"설정"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear 앱 설치/제거"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ky-television/strings.xml b/packages/PackageInstaller/res/values-ky-television/strings.xml
new file mode 100644
index 0000000..20994ff
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ky-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Баш тартам жана экинчи суралбасын"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Муну кийин Жөндөөлөр &gt; Колдонмолордон өзгөртө аласыз"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Тутум колдонмолорун көрсөтүү"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Колдонмонун уруксаттары"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Колдонмонун уруксаттары"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> уруксаттары"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Кошумча уруксаттар"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> уруксаттары"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ky-watch/strings.xml b/packages/PackageInstaller/res/values-ky-watch/strings.xml
new file mode 100644
index 0000000..aadb7c4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ky-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Баш тарам, экинчи суралбасын"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Тутум колдонмолорун көрсөтүү"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Өзгөртүүгө болбойт"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Ооба"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Жокко чыгаруу"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ky/strings.xml b/packages/PackageInstaller/res/values-ky/strings.xml
new file mode 100644
index 0000000..9a95c54
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ky/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Топтом орноткуч"</string>
+    <string name="next" msgid="3057143178373252333">"Кийинки"</string>
+    <string name="install" msgid="5896438203900042068">"Орнотуу"</string>
+    <string name="done" msgid="3889387558374211719">"Даяр"</string>
+    <string name="cancel" msgid="8360346460165114585">"Жокко чыгаруу"</string>
+    <string name="installing" msgid="8613631001631998372">"Орнотулууда…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> орнотулууда…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Колдонмо орнотулду."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Бул колдонмону орнотоюн дегениңиз аныкпы? Ал кийинкилерге жетки алат:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Бул колдонмону орнотоюн дегениңиз аныкпы? Ал эч бир атайын жетки уруксаттарын талап кылбайт."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Учурда иштеп турган бул колдонмого жаңыртуу орнотоюн дегениңиз аныкпы? Сиздин сакталган берилиштериңиз өчүрүлбөйт. Жаңыртылган колдонмо кийинкилерге жетки алат:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Бул камтылган колдонмого жаңыртуу орнотоюн дегениңиз аныкпы? Сиздин сакталган берилиштериңиз өчүрүлбөйт. Жаңыртылган колдонмо кийинкилерге жетки алат:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Учурда иштпе турган бул колдонмого жаңыртуу орнотоюн дегениңиз аныкпы? Сиздин сакталган берилиштериңиз өчүрүлбөйт. Ал эч бир атайын жетки уруксаттарын талап кылбайт."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Бул камтылган колдонмого жаңыртуу орнотоюн дегениңиз аныкпы? Сиздин сакталган берилиштериңиз өчүрүлбөйт. Ал эч бир атайын жетки уруксаттарын  талап кылбайт."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Колдонмо орнотулган жок."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Топтом орнотуудан бөгөттөлгөн."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Башка топтом менен дал келбегендиктен колдонмо орнотулган жок."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Бул колдонмо планшетиңизге шайкеш эмес болгондуктан колдонмо орнотулган жок."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Бул колдонмо сыналгыңызга шайкеш келбейт."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Бул колдонмо телефонуңузга шайкеш эмес болгондуктан колдонмо орнотулган жок."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Топтом катары орнотулбай калган колдонмо жараксыз окшойт."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосун планшетиңизге орнотуу мүмкүн эмес."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> сыналгыңызга орнотулбай койду."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосун телефонуңузга орнотуу мүмкүн эмес."</string>
+    <string name="launch" msgid="4826921505917605463">"Ачуу"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Администраторуңуз белгисиз булактардан алынган колдонмолордун орнотулушуна жол бербейт"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Бул колдонуучу белгисиз колдонмолорду орното албайт"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Бул колдонуучу колдонмолорду орното албайт"</string>
+    <string name="ok" msgid="3468756155452870475">"Жарайт"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Колдонмолорду башкаруу"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Бош орун жок"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосун телефонуңузга орнотуу мүмкүн эмес. Орун бошотуп, кайталап орнотуп көрүңүз."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Колдонмо табылган жок"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Колдонмо орнотулган колдонмолор тизмегинен табылган жок."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Тыюу салынган"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Учурдагы колдонуучу колдонмону чыгарып сала албайт."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Ката"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Колдонмону чыгарып салуу мүмкүн болбой жатат."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Колдонмону чечип салуу"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Жаңыртууну чечип салуу"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> кийинки колдонмонун бөлүгү:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Бул колдонмону чечип салгыңыз келеби?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Бул колдонмо "<b>"бардык"</b>" колдонуучулардан алынып салынсынбы? Бул колдонмо жана анын берилиштери бул түзмөктүн "<b>"бардык"</b>" колдонуучуларынан алынат."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Бул колдонмону <xliff:g id="USERNAME">%1$s</xliff:g> колдонуучусу үчүн орнотуудан чыгаргыңыз келеби?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Бул колдонмонун баштапкы версиясы орнотулсунбу? Бардык дайындар өчүп калат."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Бул колдонмонун баштапкы версиясы орнотулсунбу? Түзмөктөгү бардык профилдердин, ошондой эле жумушчу профилдин дайындары өчүп калат."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Чыгарылып салынууда"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Чыгарылып салынбай калгандар"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Чыгарылып салынууда…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> колдонмосу чыгарылууда…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Чечилип бүттү."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> колдонмосу чыгарылды"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Чечүү ийгиликсиз болду."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> колдонмосун чыгарып салуу ишке ашкан жок."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Түзмөктү башкарган колдонмо иштеп жатканда аны чыгарып салууга болбойт"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> колдонуучусунун түзмөктү башкарган колдонмосу иштеп жатканда, аны чыгарып салууга болбойт"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Колдонмо айрым колдонуучулар же профилдерге керек."</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Бул колдонмо профилиңизге керек жана аны чыгарып салуу мүмкүн эмес."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Бул колдонмо түзмөк администраторуңузга керектелет жана орнотуудан чыгаруу мүмкүн эмес."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Түзмөктү башкарган колдонмолорду көзөмөлдөө"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Колдонуучуларды башкаруу"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосун чечип салуу мүмкүн эмес."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Таңгакты окууда маселе пайда болду."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Жаңы"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Бардыгы"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Купуялуулук"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Жетүү уруксаттары"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Бул жаңыртуу жаңы уруксаттарды талап кылбайт."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Жок"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Көбүрөөк маалымат"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Баш тартуу"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> ичинен <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна <xliff:g id="ACTION">%2$s</xliff:g> уруксат берилсинби?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна <xliff:g id="ACTION">%2$s</xliff:g> аракетине ар дайым уруксат берилсинби?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Ушул колдонмодо иштегенде гана"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Ар дайым"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Баш тартам жана экинчи суралбасын"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> өчүрүлгөн"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"баары өчүрүлгөн"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"эч бири өчүрүлгөн жок"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Уруксат берүү"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Колдонмолор"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Колдонмо уруксаттары"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Экинчи суралбасын"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Уруксаттар жок"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Кошумча уруксаттар"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Колдонмо тууралуу маалыматты ачуу"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">Дагы <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Дагы <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Бул колдонмо эски Android версиясы үчүн түзүлгөн. Уруксат берилбесе, ал туура эмес иштеп калышы мүмкүн."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"белгисиз аракеттерди жасайт"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g> колдонмонун ичинен <xliff:g id="COUNT_0">%1$d</xliff:g> уруксат берилген"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Тутумду көрсөтүү"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Тутумдагы процесстерди жашыруу"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Бир дагы колдонмо жок"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Геолокация параметрлери"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> - бул түзмөктөгү жайгашкан жерди аныктоо кызматынын камсыздоочусу. Жайгашкан жерди көрүү мүмкүнчүлүгүн жайгашкан жерди аныктоо жөндөөлөрүнөн өзгөртсө болот."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Эгер бул уруксатты четке каксаңыз, түзмөгүңүздүн негизги функциялары талаптагыдай иштебей калышы мүмкүн."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Саясат тарабынан күчүнө киргизилген"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Фондук режимде колдонуу саясат тарабынан өчүрүлгөн"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Фондук режимде колдонуу саясат тарабынан иштетилген"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Активдүү режим саясат боюнча иштетилген"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Администратор тарабынан көзөмөлдөнөт"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Ар дайым"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Ушул колдонмодо иштегенде гана"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Эч качан"</string>
+    <string name="loading" msgid="7811651799620593731">"Жүктөлүүдө…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Бардык уруксаттар"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Колдонмонун башка жөндөмдөрү"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Уруксат суроо"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Экран кабатталышы аныкталды"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Бул уруксаттын жөндөөсүн өзгөртүү үчүн, адегенде Жөндөөлөр &gt; Колдонмолордон экрандын кабатталышын өчүрүңүз"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Жөндөөлөрдү ачуу"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Тагынма"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Орнотуу/чыгарып салуу аракеттери Android Wear\'де колдоого алынбайт."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосу үчүн уруксаттарды тандаңыз"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; жаңыртылды. Ал үчүн уруксаттарды тандаңыз."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Жок"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Улантуу"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Жаңы уруксаттар"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Учурдагы уруксаттар"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Күтө туруңуз…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Белгисиз"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Коопсуздукту сактоо максатында, планшетиңизге бул булактан колдонмолорду орнотууга уруксат жок."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Коопсуздукту сактоо максатында, сыналгыңызга бул булактан колдонмолорду орнотууга уруксат жок."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Коопсуздукту сактоо максатында, телефонуңузга бул булактан колдонмолорду орнотууга уруксат жок."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Телефонуңуз жана жеке дайындарыңыз белгисиз колдонмолордон зыян тартып калышы мүмкүн. Бул колдонмону орнотуп, аны пайдалануудан улам телефонуңузга кандайдыр бир зыян келтирилсе же дайындарыңызды жоготуп алсаңыз, өзүңүз жооптуу болосуз."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Планшетиңиз жана жеке дайындарыңыз белгисиз колдонмолордон зыян тартып калышы мүмкүн. Бул колдонмону орнотуп, аны пайдалануудан улам планшетиңизге кандайдыр бир зыян келтирилсе же дайындарыңызды жоготуп алсаңыз, өзүңүз жооптуу болосуз."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"TV жана жеке дайындарыңыз белгисиз колдонмолордон зыян тартып калышы мүмкүн. Бул колдонмону орнотуп, аны пайдалануудан улам TV\'ңизге кандайдыр бир зыян келтирилсе же дайындарыңызды жоготуп алсаңыз, өзүңүз жооптуу болосуз."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Улантуу"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Жөндөөлөр"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Тагынма колдонмолорду орнотуу/чыгаруу"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-lo-television/strings.xml b/packages/PackageInstaller/res/values-lo-television/strings.xml
new file mode 100644
index 0000000..a6f4e49
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lo-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"ປະຕິເສດ ແລະຢ່າຖາມອີກ"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"ທ່ານສາມາດປ່ຽນແປງສິ່ງນີ້ໃນພາຍຫຼັງໄດ້ໃນການຕັ້ງຄ່າ &gt; ແອັບ"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"ສະ​ແດງ​ແອັບ​ລະ​ບົບ"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"ການອະນຸຍາດແອັບ"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"ການອະນຸຍາດແອັບ"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> ການອະນຸຍາດ"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"ການອະນຸຍາດເພີ່ມເຕີມ"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> ການອະນຸຍາດ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-lo-watch/strings.xml b/packages/PackageInstaller/res/values-lo-watch/strings.xml
new file mode 100644
index 0000000..4fae329
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lo-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"ປະ​ຕິ​ເສດ, ຢ່າ​ຖາມ​ອີກ"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"ສະ​ແດງ​ແອັບ​ລະ​ບົບ"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"ບໍ່​ສາ​ມາດ​ປ່ຽນ​ແປງ​ໄດ້"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"ແມ່ນແລ້ວ"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"ຍົກເລີກ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-lo/strings.xml b/packages/PackageInstaller/res/values-lo/strings.xml
new file mode 100644
index 0000000..f39f140
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lo/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"ໂຕຕິດຕັ້ງແພັກເກດ"</string>
+    <string name="next" msgid="3057143178373252333">"ຕໍ່ໄປ"</string>
+    <string name="install" msgid="5896438203900042068">"ຕິດຕັ້ງ"</string>
+    <string name="done" msgid="3889387558374211719">"ແລ້ວໆ"</string>
+    <string name="cancel" msgid="8360346460165114585">"ຍົກເລີກ"</string>
+    <string name="installing" msgid="8613631001631998372">"ກຳລັງຕິດຕັ້ງ…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"ກຳລັງຕິດຕັ້ງ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"ຕິດຕັ້ງແອັບຯແລ້ວ."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"ທ່ານຕ້ອງການຕິດຕັ້ງແອັບພລິເຄຊັນນີ້ບໍ່? ມັນຈະໄດ້ສິດການເຂົ້າເຖິງ:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"ທ່ານ​ຕ້ອງ​ການ​ຕິດ​ຕັ້ງ​ແອັບພລິເຄຊັນນີ້ບໍ່​? ມັນ​ບໍ່​ຕ້ອງໃຊ້ສິດທິການ​ເຂົ້າ​ເຖິງ​​ພິ​ເສດໃດໆ."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"ທ່ານຕ້ອງການຕິດຕັ້ງອັບເດດໃສ່ແອັບພລິເຄຊັນນີ້ບໍ່? ຂໍ້ມູນຂອງທ່ານທີ່ມີຢູ່ກ່ອນແລ້ວຈະຍັງຄົງຢູ່ຄືເກົ່າ. ແອັບພລິເຄຊັນທີ່ຜ່ານການອັບເດດຈະສາມາດເຂົ້າເຖິງ:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"ທ່ານຕ້ອງການທີ່ຈະຕິດຕັ້ງຊຸດອັບເດດສຳລັບແອັບຯນີ້ບໍ່? ຂໍ້ມູນທີ່ທ່ານມີຢູ່ຈະບໍ່ສູນຫາຍ. ການອັບເດດແອັບພລິເຄຊັນນີ້ຈະສາມາດເຂົ້າເຖິງ:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"ທ່ານຕ້ອງການຕິດຕັ້ງອັບເດດໃສ່ແອັບພລິເຄຊັນນີ້ບໍ່? ຂໍ້ມູນຂອງທ່ານທີ່ມີຢູ່ກ່ອນແລ້ວຈະຍັງຄົງຢູ່ຄືເກົ່າ. ມັນບໍ່ຕ້ອງການສິດເຂົ້າເຖິງພິເສດໃດໆ."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"ທ່ານຕ້ອງການຕິດຕັ້ງອັບເດດໃສ່ແອັບພລິເຄຊັນທີ່ມີມານຳນີ້ບໍ່? ຂໍ້ມູນຂອງທ່ານທີ່ມີຢູ່ກ່ອນແລ້ວຈະຍັງຄົງຢູ່ຄືເກົ່າ. ມັນບໍ່ຕ້ອງການສິດເຂົ້າເຖິງພິເສດໃດໆເລີຍ."</string>
+    <string name="install_failed" msgid="6579998651498970899">"ບໍ່ໄດ້ຕິດຕັ້ງແອັບຯເທື່ອ."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"ແພັກ​ເກດ​ຖືກບ​ລັອກ​ບໍ່​ໃຫ້​ໄດ້​ຮັບ​ການ​ຕິດ​ຕັ້ງ"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"App not installed as package conflicts with an existing package."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"App not installed as app isn\'t compatible with your tablet."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"ແອັບ​ນີ້​ບໍ່​ເຂົ້າ​ກັນ​ໄດ້​ກັບໂທລະພາບຂອງ​ທ່ານ."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"App not installed as app isn\'t compatible with your phone."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"App not installed as package appears to be invalid."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"ບໍ່ສາມາດຕິດຕັ້ງ <xliff:g id="APP_NAME">%1$s</xliff:g> ໃສ່ແທັບເລັດຂອງທ່ານໄດ້."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ່​ສາ​ມາດ​ຕິດ​ຕັ້ງ​ໃສ່ໂທລະພາບຂອງ​ທ່ານ​ໄດ້."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"ບໍ່​ສາ​ມາດ​ຕິດ​ຕັ້ງ​ <xliff:g id="APP_NAME">%1$s</xliff:g> ໃນໂທລະສັບຂອງທ່ານໄດ້."</string>
+    <string name="launch" msgid="4826921505917605463">"ເປີດ"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"ຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ຕິດຕັ້ງແອັບທີ່ໄດ້ມາຈາກແຫຼ່ງທີ່ບໍ່ຮູ້ຈັກ"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"ຜູ້ໃຊ້ນີ້ບໍ່ສາມາດຕິດຕັ້ງແອັບທີ່ບໍ່ຮູ້ຈັກໄດ້"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"ຜູ້ໃຊ້ນີ້ບໍ່ໄດ້ຮັບອະນຸຍາດໃຫ້ຕິດຕັ້ງແອັບໄດ້"</string>
+    <string name="ok" msgid="3468756155452870475">"ຕົກລົງ"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"ຈັດການແອັບຯ"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"ພື້ນທີ່ຫວ່າງບໍ່ພຽງພໍ"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"ບໍ່ສາມາດຕິດຕັ້ງ <xliff:g id="APP_NAME">%1$s</xliff:g> ໄດ້. ກະລຸນາລຶບຂໍ້ມູນທີ່ບໍ່ຈຳເປັນອອກ ເພື່ອໃຫ້ມີບ່ອນຈັດເກັບຂໍ້ມູນຫວ່າງເພີ່ມຂຶ້ນ ແລ້ວລອງໃໝ່ອີກຄັ້ງ."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"ບໍ່ພົບເຫັນແອັບຯ"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"ບໍ່ພົບແອັບຯໃນລາຍການຂອງແອັບຯທີ່ຕິດຕັ້ງແລ້ວ."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"ບໍ່ອະນຸຍາດແລ້ວ"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"ຜູ້ໃຊ້ປັດຈຸບັນບໍ່ໄດ້ຮັບອະນຸຍາດໃຫ້ຖອນການຕິດຕັ້ງນີ້ໄດ້."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"ຜິດພາດ"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"ບໍ່ສາມາດຖອນການຕິດຕັ້ງແອັບໄດ້."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"ຖອນ​ການ​ຕິດ​ຕັ້ງແອັບຯ"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"ຖອນ​ການ​ຕິດ​ຕັ້ງ​ອັບເດດ"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ແມ່ນ​ສ່ວນ​ນຶ່ງ​ຂອງແອັບຯຂ້າງລຸ່ມ:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"ທ່ານຕ້ອງການຖອນການຕິດຕັ້ງແອັບຯນີ້ບໍ່?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"ທ່ານຕ້ອງການທີ່ຈະຖອນການຕິດຕັ້ງແອັບຯນີ້ ສຳລັງຜູ່ໃຊ້"<b>"ທຸກຄົນ"</b>"ບໍ່? ແອັບພລິເຄຊັນ ແລະຂໍ້ມູນຂອງມັນຈະຖືກລຶບອອກ ຈາກຜູ່ໃຊ້"<b>"ທັງໝົດ"</b>"ໃນອຸປະກອນນີ້."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"ທ່ານ​ຕ້ອງ​ການ​ຖອນ​ການ​ຕິດ​ຕັ້ງ​ແອັບຯ​ນີ້​ສຳ​ລັບ​ຜູ່​ໃຊ້ <xliff:g id="USERNAME">%1$s</xliff:g> ບໍ່?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"ແທນທີ່ແອັບນີ້ດ້ວຍເວີຊັນທີ່ມາຈາກໂຮງງານບໍ? ຂໍ້ມູນທັງໝົດຈະຖືກລຶບອອກ."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"ແທນທີ່ແອັບນີ້ດ້ວຍເວີຊັນທີ່ມາຈາກໂຮງງານບໍ? ຂໍ້ມູນທັງໝົດຈະຖືກລຶບອອກ ເຊິ່ງມີຜົນກັບຜູ້ໃຊ້ອຸປະກອນນີ້ທຸກຄົນ ຮວມທັງຄົນທີ່ມີໂປຣໄຟລ໌ບ່ອນເຮັດວຽກນຳ."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"ກຳລັງຖອນການຕິດຕັ້ງ"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"ຖອນການຕິດຕັ້ງບໍ່ສຳເລັດ"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"ກຳລັງຖອນການຕິດຕັ້ງ..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"ກຳລັງຖອນການຕິດຕັ້ງ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"ຖອນການຕິດຕັ້ງສຳເລັດແລ້ວ."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"ຖອນການຕິດຕັ້ງ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ແລ້ວ"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"ການຖອນການຕິດຕັ້ງບໍ່ສຳເລັດ."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"ຖອນການຕິດຕັ້ງ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ບໍ່ສຳເລັດ."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"ບໍ່ສາມາດຖອນການຕິດຕັ້ງແອັບອຸປະກອນທີ່ເຮັດວຽກຢູ່ໄດ້"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"ບໍ່ສາມາດຖອນການຕິດຕັ້ງແອັບຜູ້ເບິ່ງແຍງລະບົບທີ່ເຮັດວຽກຢູ່ສຳລັບ <xliff:g id="USERNAME">%1$s</xliff:g> ໄດ້"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"ແອັບນີ້ຈຳເປັນສຳລັບບາງຜູ້ໃຊ້ ຫຼື ບາງໂປຣໄຟລ໌ ແລະ ຖືກຖອນການຕິດຕັ້ງສຳລັບຄົນອື່ນແລ້ວ"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"ແອັບນີ້ຈຳເປັນຕ້ອງໃຊ້ກັບໂປຣໄຟລ໌ຂອງທ່ານ ແລະ ບໍ່ສາມາດຖອນການຕິດຕັ້ງໄດ້."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"​ແອັບຯ​ນີ້​ຕ້ອງ​ໃຊ້​ໂດຍ​ຜູ່​ເບິ່ງ​ແຍງ​ລະ​ບົບ​ຂອງ​ທ່ານ ແລະ​ບໍ່​ສາ​ມາດ​ຖອນ​ການ​ຕິດ​ຕັ້ງ​ໄດ້."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"ຈັດການແອັບຜູ້ເບິ່ງແຍງອຸປະກອນ"</string>
+    <string name="manage_users" msgid="3125018886835668847">"ຈັດການຜູ້ໃຊ້"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ່ສາມາດຖອນອອກໄດ້."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"ເກີດບັນຫາໃນການວິເຄາະແພັກເກດ."</string>
+    <string name="newPerms" msgid="6039428254474104210">"ໃໝ່"</string>
+    <string name="allPerms" msgid="1024385515840703981">"ທັງໝົດ"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"ຄວາມ​ເປັນ​ສ່ວນ​ຕົວ"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"ການເຂົ້າເຖິງອຸປະກອນ"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"ອັບເດດນີ້ບໍ່ຕ້ອງການການອະນຸຍາດໃໝ່."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"ປະຕິເສດ"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"ຂໍ້ມູນເພີ່ມເຕີມ"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"ຢືນຢັນປະຕິເສດ"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> ໃນ <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ສາມາດ <xliff:g id="ACTION">%2$s</xliff:g> ບໍ?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"ອະນຸຍາດ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ໃຫ້ໃຊ້ <xliff:g id="ACTION">%2$s</xliff:g> ໄດ້ທຸກເທື່ອບໍ?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"ໃນເວລາໃຊ້ແອັບເທົ່ານັ້ນ"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"ທຸກເທື່ອ"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"ປະຕິເສດ ແລະ ຢ່າຖາມອີກ"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"ປິດໄວ້ <xliff:g id="COUNT">%1$d</xliff:g> ສິດອະນຸຍາດແລ້ວ"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"ປິດໄວ້ທັງໝົດແລ້ວ"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ບໍ່ມີອັນໃດປິດການນຳໃຊ້ໄວ້"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"ອະນຸຍາດ"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"ແອັບ"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"ການ​ອະ​ນຸ​ຍາດ​ແອັບ"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"ບໍ່ຕ້ອງຖາມອີກ"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"​ບໍ່​ມີການ​ອະ​ນຸ​ຍາດ​"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"ການ​ອະ​ນຸ​ຍາດ​​ເພີ່ມ​ເຕີມ"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"ເປີດຂໍ້ມູນແອັບ"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ເພີ່ມ​ເຕີມ</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ເພີ່ມ​ເຕີມ</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"ແອັບ​ນີ້​ຖືກ​ອອກ​ແບບ​ມາ​ສຳ​ລັບ Android ເວີ​ຊັນ​ເກົ່າ. ການ​ປະ​ຕິ​ເສດ​ການ​ອະ​ນຸ​ຍາດ​ອາດ​ຈະ​ເຮັດ​ໃຫ້​ມັນ​ບໍ່​ເຮັດ​ວຽກ​ຕາມ​ຕ້ອງ​ການ​ໄດ້​ອີກ."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"ເຮັດ​ການ​ດຳ​ເນີນ​ການ​ທີ່​ບໍ່​ຮູ້​ຈັກ"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> ໃນ <xliff:g id="COUNT_1">%2$d</xliff:g> ແອັບ​ໄດ້​ຮັບ​ອະ​ນຸ​ຍາດ​ແລ້ວ"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"ສະແດງລະບົບ"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"ເຊື່ອງ​ລະ​ບົບ"</string>
+    <string name="no_apps" msgid="1965493419005012569">"ບໍ່ມີແອັບ"</string>
+    <string name="location_settings" msgid="1774875730854491297">"ການຕັ້ງຄ່າ​ທີ່​ຕັ້ງ"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> ແມ່ນ​ຜູ້​ໃຫ້​ບໍ​ລິ​ການ​ເລື່ອງ​ການ​ບໍ​ລິ​ການ​ທີ່​ຕັ້ງ​ສຳ​ລັບ​ອຸ​ປະ​ກອນ​ນີ້. ການ​ເຂົ້າ​ເຖິງ​ທີ່​ຕັ້ງ​ແມ່ນ​ສາ​ມາດ​ດັດ​ແປງ​ໄດ້​ຈາກ​ການ​ຕັ້ງ​ຄ່າ​ທີ່​ຕັ້ງ."</string>
+    <string name="system_warning" msgid="7103819124542305179">"ຖ້າ​ທ່ານ​ປະ​ຕິ​ເສດ​ການ​ອະ​ນຸ​ຍາດ​ນີ້, ຄຸນສົມບັດໃຊ້ງານ​ພື້ນ​ຖານ​ຂອງ​ອຸ​ປະ​ກອນ​ຂອງ​ທ່ານ​ອາດ​ຈະ​ບໍ່​ເຮັດ​ໜ້າ​ທີ່​ຕາມທີ່ກຳນົດໄວ້."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"ບັງ​ຄັບ​ໃຊ້​ຕາມ​ນະ​ໂຍ​ບາຍ​ແລ້ວ"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"ປິດນຳໃຊ້ການເຂົ້າເຖິງໃນພື້ນຫຼັງຕາມນະໂຍບາຍແລ້ວ"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"ເປີດນຳໃຊ້ການເຂົ້າເຖິງໃນພື້ນຫຼັງຕາມນະໂຍບາຍແລ້ວ"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"ເປີດນຳໃຊ້ການເຂົ້າເຖິງໃນພື້ນໜ້າຕາມນະໂຍບາຍແລ້ວ"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"ຄວບຄຸມໂດຍຜູ້ເບິ່ງແຍງລະບົບ"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"ທຸກເທື່ອ"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"ໃນເວລາໃຊ້ແອັບເທົ່ານັ້ນ"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"ບໍ່ເລີຍ"</string>
+    <string name="loading" msgid="7811651799620593731">"ກຳລັງ​ໂຫລດ..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"ທຸກ​ການ​ອະ​ນຸ​ຍາດ"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"ຄວາມ​​ສາ​ມາດ​​ອື່ນຂອງແອັບ"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"ການຮ້ອງຂໍການອະນຸຍາດ"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"ກວດ​ພົບ​ການ​ວາງ​ຊ້ອນ​ໜ້າ​ຈໍ​ແລ້ວ"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"ເພື່ອ​ປ່ຽນ​ແປງ​ການ​ຕັ້ງ​ຄ່າ​ການ​ອະ​ນຸ​ຍາດ​ນີ້, ກ່ອນ​ອື່ນ​ໝົດ​ທ່ານ​ຕ້ອງ​ປິດ​ການ​ວາງ​ຊ້ອນ​ໜ້າ​ຈໍ​ຈາກ​ແອັບ​ການ​ຕັ້ງ​ຄ່າ"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"ເປີດການຕັ້ງຄ່າ"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"ຕິດຕັ້ງ/ຖອນຕິດຕັ້ງ ຄຳສັ່ງທີ່ບໍ່ຮອງຮັບຢູ່ໃນ Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"ເລືອກວ່າຈະອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງຫຍັງໄດ້ແດ່"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"ອັບເດດ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ແລ້ວ. ກະລຸນາເລືອກວ່າຈະໃຫ້ແອັບນີ້ເຂົ້າເຖິງຫຍັງໄດ້ແດ່."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"​ຍົກ​ເລີກ"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"​ສືບ​ຕໍ່"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"ການ​ອະ​ນຸ​ຍາດ​ໃໝ່"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"ການ​ອະນຸຍາດ​ປັດຈຸ​ບັນ"</string>
+    <string name="message_staging" msgid="6151794817691100003">"ກຳລັງຮຽງແອັບ…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"ບໍ່ຮູ້ຈັກ"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"ເພື່ອຄວາມປອດໄພຂອງທ່ານ, ແທັບເລັດຂອງທ່ານບໍ່ສາມາດຕິດຕັ້ງແອັບຈາກແຫລ່ງຂໍ້ມູນນີ້ໄດ້."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"ເພື່ອຄວາມປອດໄພຂອງທ່ານ, ໂທລະທັດຂອງທ່ານບໍ່ສາມາດຕິດຕັ້ງແອັບຈາກແຫລ່ງຂໍ້ມູນນີ້ໄດ້."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"ເພື່ອຄວາມປອດໄພຂອງທ່ານ, ໂທລະສັບຂອງທ່ານບໍ່ສາມາດຕິດຕັ້ງແອັບຈາກແຫລ່ງຂໍ້ມູນນີ້ໄດ້."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"ໂທລະສັບ ແລະ ຂໍ້ມູນສ່ວນຕົວຂອງທ່ານອາດຖືກໂຈມຕີໄດ້ໂດຍແອັບທີ່ບໍ່ຮູ້ຈັກ. ໂດຍການຕິດຕັ້ງແອັບນີ້, ແມ່ນທ່ານຍອມຮັບວ່າທ່ານຈະຮັບຜິດຊອບຕໍ່ຄວາມເສຍຫາຍໃດໆກໍຕາມທີ່ເກີດຂຶ້ນຕໍ່ໂທລະທັດຂອງທ່ານ ຫຼື ການສູນເສຍຂໍ້ມູນທີ່ອາດເກີດຈາກການນຳໃຊ້ມັນ."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"ແທັບເລັດ ແລະ ຂໍ້ມູນສ່ວນຕົວຂອງທ່ານອາດຖືກໂຈມຕີໄດ້ໂດຍແອັບທີ່ບໍ່ຮູ້ຈັກ. ໂດຍການຕິດຕັ້ງແອັບນີ້, ແມ່ນທ່ານຍອມຮັບວ່າທ່ານຈະຮັບຜິດຊອບຕໍ່ຄວາມເສຍຫາຍໃດໆກໍຕາມທີ່ເກີດຂຶ້ນຕໍ່ໂທລະທັດຂອງທ່ານ ຫຼື ການສູນເສຍຂໍ້ມູນທີ່ອາດເກີດຈາກການນຳໃຊ້ມັນ."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"ໂທລະທັດ ແລະ ຂໍ້ມູນສ່ວນຕົວຂອງທ່ານອາດຖືກໂຈມຕີໄດ້ໂດຍແອັບທີ່ບໍ່ຮູ້ຈັກ. ໂດຍການຕິດຕັ້ງແອັບນີ້, ແມ່ນທ່ານຍອມຮັບວ່າທ່ານຈະຮັບຜິດຊອບຕໍ່ຄວາມເສຍຫາຍໃດໆກໍຕາມທີ່ເກີດຂຶ້ນຕໍ່ໂທລະທັດຂອງທ່ານ ຫຼື ການສູນເສຍຂໍ້ມູນທີ່ອາດເກີດຈາກການນຳໃຊ້ມັນ."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"ດຳເນີນການຕໍ່"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"ການຕັ້ງຄ່າ"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"ກຳລັງຕິດຕັ້ງ/ຖອດຖອນແອັບ Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-lt-television/strings.xml b/packages/PackageInstaller/res/values-lt-television/strings.xml
new file mode 100644
index 0000000..dee6de7
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lt-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Atmesti ir daugiau neklausti"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Tai vėliau galėsite pakeisti skiltyje „Nustatymai &gt; Programos“"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Rodyti sistemos programas"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Programos leidimai"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Programos leidimai"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> leidimai"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Papildomi leidimai"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> leidimai"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-lt-watch/strings.xml b/packages/PackageInstaller/res/values-lt-watch/strings.xml
new file mode 100644
index 0000000..85eebcf
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lt-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Atmesti, daugiau neklausti"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Rodyti sistemos programas"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Negalima pakeisti"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Taip"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Atšaukti"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-lt/strings.xml b/packages/PackageInstaller/res/values-lt/strings.xml
new file mode 100644
index 0000000..79546a9
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lt/strings.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Paketo įdiegimo programa"</string>
+    <string name="next" msgid="3057143178373252333">"Kitas"</string>
+    <string name="install" msgid="5896438203900042068">"Įdiegti"</string>
+    <string name="done" msgid="3889387558374211719">"Atlikta"</string>
+    <string name="cancel" msgid="8360346460165114585">"Atšaukti"</string>
+    <string name="installing" msgid="8613631001631998372">"Diegiama..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Įdiegiama „<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>“…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Programa įdiegta."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Ar norite įdiegti šią programą? Jai bus suteikta prieiga prie:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Ar norite įdiegti šią programą? Jai nereikalinga jokia speciali prieiga."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Ar norite įdiegti šios esamos programos naujinį? Neprarasite esamų duomenų. Atnaujinus programą bus suteikta prieiga prie:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Ar norite įdiegti šios integruotos programos naujinį? Neprarasite esamų duomenų. Atnaujinus programą bus suteikta prieiga prie:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Ar norite įdiegti šios esamos programos naujinį? Neprarasite esamų duomenų. Nereikia jokios specialios prieigos."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Ar norite įdiegti šios integruotos programos naujinį? Neprarasite esamų duomenų. Nereikia jokios specialios prieigos."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Programa neįdiegta."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Paketas užblokuotas ir negali būti įdiegtas."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Programa neįdiegta, nes paketas nesuderinamas su esamu paketu."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Programa neįdiegta, nes ji nesuderinama su planšetiniu kompiuteriu."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Ši programa nesuderinama su jūsų TV."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Programa neįdiegta, nes ji nesuderinama su telefonu."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Programa neįdiegta, nes panašu, kad paketas netinkamas."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Jūsų planšetiniame kompiuteryje nepavyko įdiegti „<xliff:g id="APP_NAME">%1$s</xliff:g>“."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Nepavyko programos „<xliff:g id="APP_NAME">%1$s</xliff:g>“ įdiegti jūsų TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Jūsų telefone nepavyko įdiegti „<xliff:g id="APP_NAME">%1$s</xliff:g>“."</string>
+    <string name="launch" msgid="4826921505917605463">"Atidaryti"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Jūsų administratorius neleidžia diegti programų, gautų iš nežinomų šaltinių"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Šis naudotojas negali diegti nežinomų programų"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Šiam naudotojui neleidžiama diegti programų"</string>
+    <string name="ok" msgid="3468756155452870475">"Gerai"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Tvarkyti programas"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Nėra vietos"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Nepavyko įdiegti „<xliff:g id="APP_NAME">%1$s</xliff:g>“. Atlaisvinkite vietos ir bandykite dar kartą."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Programa nerasta"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Programa nerasta įdiegtų programų sąraše."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Neleidžiama"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Dabartiniam naudotojui neleidžiama atlikti šio pašalinimo veiksmo."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Klaida"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Nepavyko įdiegti programos."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Pašalinti programą"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Pašalinti naujinį"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"„<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>“ yra šios programos dalis:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Ar norite pašalinti šią programą?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Ar norite pašalinti šią programą "<b>"visiems"</b>" naudotojams? Programa ir jos duomenys bus pašalinti iš "<b>"visų"</b>" naudotojų įrenginyje."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Ar norite pašalinti šią naudotojo <xliff:g id="USERNAME">%1$s</xliff:g> programą?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Pakeisti šios programos versiją į gamyklinę? Visi duomenys bus pašalinti."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Pakeisti šios programos versiją į gamyklinę? Visi duomenys bus pašalinti. Tai paveiks visus šio įrenginio naudotojus, įskaitant turinčius darbo profilius."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Vykdomi įdiegimai"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Nepavykę įdiegimai"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Pašalinama..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Pašalinama „<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>“…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Pašalinimas baigtas."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Paketas „<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>“ pašalintas"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Nepavyko pašalinti."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"„<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>“ pašalinimo veiksmas nesėkmingas."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Negalima pašalinti aktyvios įrenginio administravimo programos"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Negalima pašalinti aktyvios naudotojo <xliff:g id="USERNAME">%1$s</xliff:g> įrenginio administravimo programos"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Ši programa reikalinga kai kuriems naudotojams ar kai kuriuose profiliuose ir buvo pašalinta kitur"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Ši programa reikalinga jūsų profilyje ir jos negalima pašalinti."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Ši programa reikalinga jūsų įrenginio administratoriui ir jos negalima pašalinti."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Tvarkyti įrenginio administravimo programas"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Valdyti naudotojus"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Nepavyko pašalinti „<xliff:g id="APP_NAME">%1$s</xliff:g>“."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Analizuojant paketą iškilo problema."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Naujiena"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Visi"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privatumas"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Prieiga prie įreng."</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Šiam naujiniui nereikalingi nauji leidimai."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Atmesti"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Daugiau informacijos"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Vis tiek atmesti"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> iš <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Visada leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Tik naudojant programą"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Visada"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Atmesti ir daugiau neklausti"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"Išjungta leidimų: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"visi leidimai išjungti"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"nė vienas leidimas neišjungtas"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Leisti"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Programos"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Programos leidimai"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Daugiau neklausti"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Nėra jokių leidimų"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Papildomi leidimai"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Atidaryti programos informaciją"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one">Dar <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="few">Dar <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="many">Dar <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="other">Dar <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Ši programa skirta senesnės versijos „Android“. Uždraudus leidimą ji gali nebeveikti kaip numatyta."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"atlieka nežinomą veiksmą"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Leidžiama programų: <xliff:g id="COUNT_0">%1$d</xliff:g> iš <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Rodyti sistemą"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Slėpti sistemą"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Nėra jokių programų"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Vietovės nustatymai"</string>
+    <string name="location_warning" msgid="8778701356292735971">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ yra šio įrenginio vietovės paslaugų teikėjas. Vietovės pasiekiamumą galima keisti vietovės nustatymuose."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Jei uždrausite šį leidimą, pagrindinės įrenginio funkcijos gali nebeveikti, kaip numatyta."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Reikalaujama pagal politikos nuostatas"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Prieiga fone išjungta pagal politiką"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Prieiga fone įgalinta pagal politiką"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Prieiga priekiniame plane įgalinta pagal politiką"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Valdo administratorius"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Visada"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Tik naudojant programą"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Niekada"</string>
+    <string name="loading" msgid="7811651799620593731">"Įkeliama..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Visi leidimai"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Kitos programos galimybės"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Leidimo užklausa"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Aptikta ekrano perdanga"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Jei norite pakeisti šį leidimo nustatymą, pirmiausia turite išjungti ekrano perdangą skiltyje „Nustatymai &gt; Programos“"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Atidaryti nustatymus"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Diegimo / pašalinimo veiksmai nepalaikomi sistemoje „Wear“."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Pasirinkite, ką norite leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Programa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; buvo atnaujinta. Pasirinkite, ką norite leisti šiai programai pasiekti."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Atšaukti"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Tęsti"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nauji leidimai"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Dabartiniai leidimai"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Programa pateikiama etapais…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Nežinoma"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Saugos sumetimais planšetiniame kompiuteryje neleidžiama diegti nežinomų programų iš šio šaltinio."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Saugos sumetimais TV neleidžiama diegti nežinomų programų iš šio šaltinio."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Saugos sumetimais telefone neleidžiama diegti nežinomų programų iš šio šaltinio."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Telefonas ir asmeniniai duomenys labiau pažeidžiami įdiegus nežinomų programų. Įdiegdami šią programą sutinkate, kad patys esate atsakingi už žalą telefonui arba prarastus duomenis dėl šios programos naudojimo."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Planšetinis kompiuteris ir asmeniniai duomenys labiau pažeidžiami įdiegus nežinomų programų. Įdiegdami šią programą sutinkate, kad patys esate atsakingi už žalą planšetiniam kompiuteriui arba prarastus duomenis dėl šios programos naudojimo."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"TV ir asmeniniai duomenys labiau pažeidžiami įdiegus nežinomų programų. Įdiegdami šią programą sutinkate, kad patys esate atsakingi už žalą TV arba prarastus duomenis dėl šios programos naudojimo."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Tęsti"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Nustatymai"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Įdiegiamos / pašalinamos „Wear“ program."</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-lv-television/strings.xml b/packages/PackageInstaller/res/values-lv-television/strings.xml
new file mode 100644
index 0000000..f01dfd2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lv-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Noraidīt un vairs nejautāt"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Vēlāk varat veikt izmaiņas sadaļā Iestatījumi &gt; Lietotnes."</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>. no <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Rādīt sistēmas lietotnes"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Lietotņu atļaujas"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Lietotņu atļaujas"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Lietotnes <xliff:g id="PERMISSION">%1$s</xliff:g> atļaujas"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Papildu atļaujas"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Lietotnes <xliff:g id="PERMISSION">%1$s</xliff:g> atļaujas"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-lv-watch/strings.xml b/packages/PackageInstaller/res/values-lv-watch/strings.xml
new file mode 100644
index 0000000..29aef48
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lv-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Neatļaut un vairs nejautāt"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>. no <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Rādīt sistēmas lietotnes"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Nevar mainīt"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Jā"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Atcelt"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-lv/strings.xml b/packages/PackageInstaller/res/values-lv/strings.xml
new file mode 100644
index 0000000..40ddf74
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lv/strings.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Pakotnes instalēšanas programma"</string>
+    <string name="next" msgid="3057143178373252333">"Tālāk"</string>
+    <string name="install" msgid="5896438203900042068">"Instalēt"</string>
+    <string name="done" msgid="3889387558374211719">"Gatavs"</string>
+    <string name="cancel" msgid="8360346460165114585">"Atcelt"</string>
+    <string name="installing" msgid="8613631001631998372">"Notiek instalēšana..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Notiek pakotnes <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> instalēšana…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Lietotne ir instalēta."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Vai vēlaties instalēt šo lietojumprogrammu? Tā iegūs piekļuvi:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Vai vēlaties instalēt šo lietojumprogrammu? Tai nav nepieciešamas īpašas piekļuves atļaujas."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Vai vēlaties instalēt šīs lietojumprogrammas atjauninājumu? Esošie dati netiks zaudēti. Atjauninātajai lietojumprogrammai būs piekļuve:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Vai vēlaties instalēt šīs iebūvētās lietojumprogrammas atjauninājumu? Esošie dati netiks zaudēti. Atjauninātajai lietojumprogrammai būs piekļuve:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Vai vēlaties instalēt šīs lietojumprogrammas atjauninājumu? Esošie dati netiks zaudēti. Tam nav nepieciešama īpaša piekļuves atļauja."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Vai vēlaties instalēt šīs iebūvētās lietojumprogrammas atjauninājumu? Esošie dati netiks zaudēti. Tam nav nepieciešama īpaša piekļuves atļauja."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Lietotne nav instalēta."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Pakotnes instalēšana tika bloķēta."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Lietotne netika instalēta, jo rodas pakotnes konflikts ar esošo pakotni."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Lietotne netika instalēta, jo tā nav saderīga ar jūsu planšetdatoru."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Šī lietotne nav saderīga ar jūsu televizoru."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Lietotne netika instalēta, jo tā nav saderīga ar jūsu tālruni."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Lietotne netika instalēta, jo šķiet, ka pakotne nav derīga."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Lietotni <xliff:g id="APP_NAME">%1$s</xliff:g> nevarēja instalēt planšetdatorā."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Lietotni <xliff:g id="APP_NAME">%1$s</xliff:g> nevarēja instalēt jūsu televizorā."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Lietotni <xliff:g id="APP_NAME">%1$s</xliff:g> nevarēja instalēt tālrunī."</string>
+    <string name="launch" msgid="4826921505917605463">"Atvērt"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Jūsu administrators neļauj instalēt lietotnes, kas iegūtas no nezināmiem avotiem."</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Šis lietotājs nevar instalēt lietotnes, kas iegūtas no nezināmiem avotiem."</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Šim lietotājam nav atļauts instalēt lietotnes"</string>
+    <string name="ok" msgid="3468756155452870475">"Labi"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Pārvaldīt lietotnes"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Nav brīvas vietas"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Lietotni <xliff:g id="APP_NAME">%1$s</xliff:g> nevarēja instalēt. Atbrīvojiet vietu un mēģiniet vēlreiz."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Lietotne nav atrasta"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Šī lietotne netika atrasta instalēto lietotņu sarakstā."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Nav atļauts"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Pašreizējam lietotājam nav atļauts veikt atinstalēšanu."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Kļūda"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Nevarēja atinstalēt lietotni."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Atinstalēt lietotni"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Atinstalēt atjauninājumu"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ir daļa no šādas lietotnes:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Vai vēlaties atinstalēt šo lietotni?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Vai vēlaties atinstalēt šo lietotni "<b>"visiem"</b>" lietotājiem? Šī lietojumprogramma un tās dati tiks noņemti no "<b>"visiem"</b>" ierīces lietotāju kontiem."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Vai vēlaties atinstalēt šo lietotni lietotājam <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Vai aizstāt šo lietotni ar rūpnīcas versiju? Visi dati tiks noņemti."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Vai aizstāt šo lietotni ar rūpnīcas versiju? Visi dati tiks noņemti. Tas ietekmēs visu šīs ierīces lietotāju (arī to lietotāju, kuriem ir darba profili) datus."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Pašlaik veiktie atinstalēšanas gadījumi"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Neizdevušies atinstalēšanas gadījumi"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Notiek atinstalēšana..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Notiek lietotnes <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> atinstalēšana…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Atinstalēšana ir pabeigta."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Lietotne <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ir atinstalēta"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Atinstalēšana neizdevās."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Lietotnes <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> atinstalēšana nebija sekmīga."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Neizdevās atinstalēt aktīvas ierīces administratora lietotnes."</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Neizdevās atinstalēt aktīvas ierīces administratora lietotni <xliff:g id="USERNAME">%1$s</xliff:g>."</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Šī lietotne ir nepieciešama dažiem lietotājiem vai profiliem un tika atinstalēta citiem"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Šī lietotne ir nepieciešama jūsu profilam, tāpēc to nevar atinstalēt."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Ierīces administrators ir noteicis, ka lietotne ir obligāta un to nevar atinstalēt."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Pārvaldīt ierīces administratora lietotnes"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Pārvaldīt lietotājus"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Lietotni <xliff:g id="APP_NAME">%1$s</xliff:g> nevarēja atinstalēt."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Parsējot pakotni, radās problēma."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Jauna!"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Visas"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Konfidencialitāte"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Piekļuve ierīcei"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Šim atjauninājumam nav nepieciešamas jaunas atļaujas."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Neatļaut"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Plašāka informācija"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Tomēr noraidīt"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>. no <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Vai vienmēr atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Tikai izmantojot lietotni"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Vienmēr"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Noraidīt un vairs nejautāt"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> atspējotas"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"visas atspējotas"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"neviena nav atspējota"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Atļaut"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Lietotnes"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Lietotnes atļaujas"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Turpmāk vairs nejautāt"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Nav atļauju"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Papildu atļaujas"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Atvērt lietotnes informāciju"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="zero">Vēl <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Vēl <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="other">Vēl <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Šī lietotne ir paredzēta vecākai Android versijai. Ja noraidīsiet atļauju, iespējams, netiks nodrošināta paredzētā lietotnes darbība."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"veikt nezināmu darbību"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Atļautas <xliff:g id="COUNT_0">%1$d</xliff:g> lietotnes no <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Rādīt sistēmas lietotnes"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Slēpt sistēmas lietotnes"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Nav lietotņu"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Atrašanās vietas iestatījumi"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> nodrošina šai ierīcei atrašanās vietu pakalpojumus. Piekļuves iestatījumus atrašanās vietas datiem var mainīt atrašanās vietas iestatījumos."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Ja nepiešķirsiet šo atļauju, ierīces pamatfunkcijas, iespējams, vairs nedarbosies, kā paredzēts."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Īstenota saskaņā ar politiku"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Piekļuve fonā atspējota saskaņā ar politiku"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Piekļuve fonā iespējota saskaņā ar politiku"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Piekļuve priekšplānā iespējota saskaņā ar politiku"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Kontrolē administrators"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Vienmēr"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Tikai izmantojot lietotni"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nekad"</string>
+    <string name="loading" msgid="7811651799620593731">"Notiek ielāde..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Visas atļaujas"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Citas lietotnes atļaujas"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Atļaujas pieprasījums"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Konstatēts ekrāna pārklājums"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Lai mainītu šo atļaujas iestatījumu, vispirms sadaļā “Iestatījumi un lietotnes” izslēdziet ekrāna pārklājumu."</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Atvērt iestatījumus"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear ierīcē netiek atbalstīta instalēšana/atinstalēšana"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Izvēlieties, kādas piekļuves atļaujas piešķirt lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Lietotne &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ir atjaunināta. Izvēlieties, kādas piekļuves atļaujas tai piešķirt."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Atcelt"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Turpināt"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Jaunas atļaujas"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Pašreizējās atļaujas"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Lietotne tiek izstādīta…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Nezināms"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Drošības nolūkos jūsu planšetdatorā nedrīkst instalēt no šī avota iegūtas nezināmas lietotnes."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Drošības nolūkos jūsu televizorā nedrīkst instalēt no šī avota iegūtas nezināmas lietotnes."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Drošības nolūkos jūsu tālrunī nedrīkst instalēt no šī avota iegūtas nezināmas lietotnes."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Jūsu tālruņa un personas dati ir neaizsargātāki pret uzbrukumiem no nezināmām lietotnēm. Instalējot šo lietotni, jūs piekrītat, ka esat atbildīgs par tālruņa bojājumiem vai datu zudumu, kas var rasties lietotnes dēļ."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Jūsu planšetdatora un personas dati ir neaizsargātāki pret uzbrukumiem no nezināmām lietotnēm. Instalējot šo lietotni, jūs piekrītat, ka esat atbildīgs par planšetdatora bojājumiem vai datu zudumu, kas var rasties lietotnes dēļ."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Jūsu televizora un personas dati ir neaizsargātāki pret uzbrukumiem no nezināmām lietotnēm. Instalējot šo lietotni, jūs piekrītat, ka esat atbildīgs par televizora bojājumiem vai datu zudumu, kas var rasties lietotnes dēļ."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Turpināt"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Iestatījumi"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear lietotņu instalēšana/atinstalēšana"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-mk-television/strings.xml b/packages/PackageInstaller/res/values-mk-television/strings.xml
new file mode 100644
index 0000000..bb3ea92
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mk-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Одбиј и не прашувај повторно"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Може да го промените ова подоцна во Поставки &gt; Апликации"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Прикажи ги системските апликации"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Дозволи за апликацијата"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Дозволи за апликацијата"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Дозволи за <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Дополнителни дозволи"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Дозволи за <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-mk-watch/strings.xml b/packages/PackageInstaller/res/values-mk-watch/strings.xml
new file mode 100644
index 0000000..5906f56
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mk-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Одбиј, не прашувај повторно"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Прикажи ги системските апликации"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Не може да се смени"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Да"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Откажи"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-mk/strings.xml b/packages/PackageInstaller/res/values-mk/strings.xml
new file mode 100644
index 0000000..8cf208e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mk/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Инсталатор на пакет"</string>
+    <string name="next" msgid="3057143178373252333">"Следно"</string>
+    <string name="install" msgid="5896438203900042068">"Инсталирај"</string>
+    <string name="done" msgid="3889387558374211719">"Готово"</string>
+    <string name="cancel" msgid="8360346460165114585">"Откажи"</string>
+    <string name="installing" msgid="8613631001631998372">"Се инсталира..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Се инсталира <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Апликацијата е инсталирана."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Дали сакате да ја инсталирате оваа апликација? Ќе добие пристап до:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Дали сакате да ја инсталирате оваа апликација? Не бара никаков посебен пристап."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Дали сакате да инсталирате надградба на оваа постоечка апликација? Вашите постоечки податоци нема да се изгубат. Ажурираната апликација ќе добие пристап до:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Дали сакате да инсталирате надградба на оваа вградена апликација? Вашите постоечки податоци нема да се изгубат. Ажурираната апликација ќе добие пристап до:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Дали сакате да инсталирате надградба на оваа постоечка апликација? Вашите постоечки податоци нема да се изгубат. Таа не бара никаков посебен пристап."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Дали сакате да инсталирате надградба на оваа вградена апликација? Вашите постоечки податоци нема да се изгубат. Таа не бара никаков посебен пристап."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Апликацијата не е инсталирана."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Инсталирањето на пакетот е блокирано."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Апликација што не е инсталирана како пакет е во конфликт со постоен пакет."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Апликација што не е инсталирана како апликација не е компатибилна со вашиот таблет."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Оваа апликација не е компатибилна со вашиот телевизор."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Апликација што не е инсталирана како апликација не е компатибилна со вашиот телефон."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Апликација што не е инсталирана како пакет се чини дека е неважечка."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> не можеше да се инсталира на вашиот таблет."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> не може да се инсталира на вашиот телевизор."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> не можеше да се инсталира на вашиот телефон."</string>
+    <string name="launch" msgid="4826921505917605463">"Отвори"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Вашиот администратор не дозволува инсталација на апликации добиени од непознати извори"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Корисников не може да инсталира непознати апликации"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"На корисников не му е дозволено да инсталира апликации"</string>
+    <string name="ok" msgid="3468756155452870475">"Во ред"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Управувај со апликации"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Нема простор"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> не можеше да се инсталира. Ослободете простор и обидете се повторно."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Апликацијата не е пронајдена"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Апликацијата не е пронајдена во списокот инсталирани апликации."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Не е дозволено"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Тековниот корисник нема дозвола да ја изведе деинсталацијава."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Грешка"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Не можеше да се деинсталира апликацијата."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Деинсталирај апликација"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Деинсталирај ажурирање"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> е дел од следната апликација:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Дали сакате да ја деинсталирате оваа апликација?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Дали сакате да ја деинсталирате оваа апликација за "<b>"сите"</b>" корисници? Апликацијата и нејзините податоци ќе се отстранат од "<b>"сите"</b>" корисници на уредот."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Дали сакате да ја деинсталирате апликацијава за корисникот <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Сакате да ја замените оваа апликација со фабричката верзија? Сите податоци ќе се отстранат."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Сакате да ја замените оваа апликација со фабричката верзија? Сите податоци ќе се отстранат. Тоа важи за сите корисници на овој уред, вклучувајќи ги и тие со работни профили."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Деинсталации во тек"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Неуспешни деинсталации"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Се деинсталира..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Се деинсталира <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Деинсталирањето заврши."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> е деинсталиран"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Деинсталирањето е неуспешно."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Деинсталирањето на <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> е неуспешно."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Не може да се деинсталира активна апликација на администраторот на уредот"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Не може да се деинсталира активна апликација на администраторот на уредот за <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Апликацијата е неопходна за некои корисници или профили, а за другите е деинсталирана"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Апликацијата е потребна за вашиот профил и не може да се деинсталира."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Апликацијата ја бара администраторот на вашиот уред и не може да се деинсталира."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Управувај со аплик. за администраторот на уредот"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Управувај со корисници"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> не може да се деинсталира."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Настана проблем при разложување на пакетот."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Ново"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Сè"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Приватност"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Пристап кон уредот"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Ова ажурирање не бара нови дозволи."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Одбиј"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Повеќе информации"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Сепак одбиј"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> од <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Дозволете &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Дали секогаш да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Само додека се користи апликацијата"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Секогаш"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Одбиј и не прашувај повторно"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"Оневозможени се <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"сите се оневозможени"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ниедна не е оневозможена"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Овозможи"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Апликации"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Дозволи за апликацијата"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Не прашувај повторно"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Нема дозволи"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Дополнителни дозволи"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Отвора информации за апликација"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <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="old_sdk_deny_warning" msgid="3872277112584842615">"Оваа апликација е дизајнирана за постара верзија на Android. Одбивањето на дозволата може да предизвика веќе да не функционира како што треба."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"изврши непознато дејство"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Дозволени се <xliff:g id="COUNT_0">%1$d</xliff:g> од <xliff:g id="COUNT_1">%2$d</xliff:g> апликации"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Прикажи систем"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Сокриј систем"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Нема апликации"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Поставки за локација"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> е давател на услуги според локација за овој уред. Пристапот до локацијата може да се смени од Поставките за локација."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Ако ја одбиете оваа дозвола, основните функции на вашиот уред можеби веќе нема да функционираат како што треба."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Наложено од политиката"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Пристапот од заднина е оневозможен со правилото"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Пристапот од заднина е овозможен со правилото"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Пристапот од преден план е овозможен со правилото"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Контролирано од администраторот"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Секогаш"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Само додека се користи аплик."</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Никогаш"</string>
+    <string name="loading" msgid="7811651799620593731">"Се вчитува..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Сите дозволи"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Други можности на апликацијата"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Барање дозвола"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Откривме преклопување на екранот"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"За да ја измените оваа поставка за дозвола, прво мора да го исклучите преклопувањето на екранот од Поставки &gt; Апликации"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Отвори поставки"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Дејствата инсталирај/деинсталирај не се поддржани на Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Изберете што да ѝ се овозможи на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; за пристап"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; е ажурирана. Изберете што да ѝ се овозможи на оваа апликација за пристап."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Откажи"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Продолжи"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Нови дозволи"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Тековни дозволи"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Апликацијата се поставува…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Непознато"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"За ваша безбедност, таблетот нема дозвола за инсталирање непознати апликации од изворов."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"За ваша безбедност, телевизорот нема дозвола за инсталирање непознати апликации од изворов."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"За ваша безбедност, телефонот нема дозвола за инсталирање непознати апликации од изворов."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Телефонот и личните податоци се поподложни на напади од непознати апликации. Ако ја инсталирате апликацијава, се согласувате дека сте одговорни за каква било штета на телефонот или губењето податоци што може да произлезат од нејзиното користење."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Таблетот и личните податоци се поподложни на напади од непознати апликации. Ако ја инсталирате апликацијава, се согласувате дека сте одговорни за каква било штета на таблетот или губењето податоци што може да произлезат од нејзиното користење."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Телевизорот и личните податоци се поподложни на напади од непознати апликации. Ако ја инсталирате апликацијава, се согласувате дека сте одговорни за каква било штета на телевизорот или губењето податоци што може да произлезат од нејзиното користење."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Продолжи"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Поставки"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Се инсталираат/деинсталираат аплик. Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ml-television/strings.xml b/packages/PackageInstaller/res/values-ml-television/strings.xml
new file mode 100644
index 0000000..5ede01f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ml-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"നിരസിക്കുക, വീണ്ടും ആവശ്യപ്പെടരുത്"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"പിന്നീട് നിങ്ങൾക്കിത് ക്രമീകരണവും ആപ്സും എന്നതിൽ മാറ്റാനാകും"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"സിസ്റ്റം ആപ്‌സ് കാണിക്കുക"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"ആപ്പ് അനുമതികൾ"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"ആപ്പ് അനുമതികൾ"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> അനുമതികൾ"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"അധിക അനുമതികൾ"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> അനുമതികൾ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ml-watch/strings.xml b/packages/PackageInstaller/res/values-ml-watch/strings.xml
new file mode 100644
index 0000000..13e3876
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ml-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"നിരസിക്കുക, വീണ്ടും ആവശ്യപ്പെടരുത്"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"സിസ്റ്റം ആപ്‌സ് കാണിക്കുക"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"മാറ്റാനാവില്ല"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"അതെ"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"റദ്ദാക്കുക"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ml/strings.xml b/packages/PackageInstaller/res/values-ml/strings.xml
new file mode 100644
index 0000000..9fadd24
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ml/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"പാക്കേജ് ഇൻസ്‌റ്റാളർ"</string>
+    <string name="next" msgid="3057143178373252333">"അടുത്തത്"</string>
+    <string name="install" msgid="5896438203900042068">"ഇൻസ്റ്റാളുചെയ്യുക"</string>
+    <string name="done" msgid="3889387558374211719">"പൂർത്തിയായി"</string>
+    <string name="cancel" msgid="8360346460165114585">"റദ്ദാക്കുക"</string>
+    <string name="installing" msgid="8613631001631998372">"ഇൻസ്റ്റാൾ ചെയ്യുന്നു..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ഇൻസ്‌റ്റാൾ ചെയ്യുന്നു…"</string>
+    <string name="install_done" msgid="3682715442154357097">"അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തു."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"ഈ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്യണോ? ഇതിന് ഇവയിലേക്ക് ആക്‌സസ്സ് ലഭിക്കും:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"നിങ്ങൾക്ക് ഈ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്യണോ? ഇത് പ്രത്യേക ആക്‌സസ്സൊന്നും ആവശ്യമില്ല."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"നിലവിലുള്ള ഈ അപ്ലിക്കേഷന് ഒരു അപ്‌ഡേറ്റ് ഇൻസ്റ്റാളുചെയ്യണോ? നിങ്ങളുടെ നിലവിലെ ഡാറ്റ നഷ്‌ടപ്പെടില്ല. അപ്‌ഡേറ്റുചെയ്‌ത അപ്ലിക്കേഷന് ഇവയിലേക്ക് ആക്‌സസ്സ് ലഭിക്കും:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"ഈ അന്തർ നിർമ്മിത അപ്ലിക്കേഷന് ഒരു അപ്‌ഡേറ്റ് ഇൻസ്റ്റാളുചെയ്യണോ? നിങ്ങളുടെ നിലവിലെ ഡാറ്റ നഷ്‌ടപ്പെടില്ല. അപ്‌ഡേറ്റുചെയ്‌ത അപ്ലിക്കേഷന് ഇവയിലേക്ക് ആക്‌സസ്സ് ലഭിക്കും:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"നിലവിലുള്ള ഈ അപ്ലിക്കേഷന് ഒരു അപ്‌ഡേറ്റ് ഇൻസ്റ്റാളുചെയ്യണോ? നിങ്ങളുടെ നിലവിലെ ഡാറ്റ നഷ്‌ടപ്പെടില്ല. ഇതിന് പ്രത്യേക ആക്‌സസ്സൊന്നും ആവശ്യമില്ല."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"ഈ അന്തർ നിർമ്മിത അപ്ലിക്കേഷന് ഒരു അപ്‌ഡേറ്റ് ഇൻസ്റ്റാളുചെയ്യണോ? നിങ്ങളുടെ നിലവിലെ ഡാറ്റ നഷ്‌ടപ്പെടില്ല. ഇതിന് പ്രത്യേക ആക്‌സസ്സൊന്നും ആവശ്യമില്ല."</string>
+    <string name="install_failed" msgid="6579998651498970899">"അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തില്ല."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"ഇൻസ്റ്റാൾ ചെയ്യുന്നതിൽ നിന്നും പാക്കേജിനെ തടഞ്ഞു."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"നിലവിലുള്ള ഒരു പാക്കേജുമായി പാക്കേജിന് പൊരുത്തക്കേടുള്ളതിനാൽ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തില്ല."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"നിങ്ങളുടെ ടാബ്‌ലെറ്റുമായി അനുയോജ്യത ഇല്ലാത്തതിനാൽ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തില്ല."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"നിങ്ങളുടെ ടിവിയ്‌ക്ക് ഈ ആപ്പ് അനുയോജ്യമല്ല."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"നിങ്ങളുടെ ഫോണുമായി അനുയോജ്യത ഇല്ലാത്തതിനാൽ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തില്ല."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"പാക്കേജ് അസാധുവായി കാണപ്പെടുന്നതിനാൽ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തില്ല."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"നിങ്ങളുടെ ടാബ്‌ലെറ്റിൽ <xliff:g id="APP_NAME">%1$s</xliff:g> ഇൻസ്റ്റാളുചെയ്യാനായില്ല."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g>, നിങ്ങളുടെ ടിവിയിൽ ഇൻസ്റ്റാളുചെയ്യാനായില്ല."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"നിങ്ങളുടെ ഫോണിൽ <xliff:g id="APP_NAME">%1$s</xliff:g> ഇൻസ്റ്റാളുചെയ്യാനായില്ല."</string>
+    <string name="launch" msgid="4826921505917605463">"തുറക്കുക"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"അജ്ഞാത ഉറവിടങ്ങളിൽ നിന്ന് സ്വന്തമാക്കിയ ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യാൻ നിങ്ങളുടെ അഡ്‌മിനിസ്‌ട്രേറ്റർ അനുവദിക്കുന്നില്ല"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"ഈ ഉപയോക്താവിന്, തിരിച്ചറിയാനാകാത്ത ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യാൻ കഴിയില്ല"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യുന്നതിന് ഈ ഉപയോക്താവിന് അനുവാദമില്ല"</string>
+    <string name="ok" msgid="3468756155452870475">"ശരി"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"അപ്ലിക്കേഷനുകൾ നിയന്ത്രിക്കുക"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"പരിധി കഴിഞ്ഞു"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> ഇൻസ്റ്റാളുചെയ്യാനായില്ല. കുറച്ച് ഇടം ശൂന്യമാക്കിയതിനുശേഷം വീണ്ടും ശ്രമിക്കുക."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"അപ്ലിക്കേഷൻ കണ്ടെത്തിയില്ല"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"ഇൻസ്റ്റാളുചെയ്‌ത അപ്ലിക്കേഷനുകളുടെ ലിസ്റ്റിൽ അപ്ലിക്കേഷൻ കണ്ടെത്തിയില്ല."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"അനുവദിച്ചിട്ടില്ല"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"ഈ അൺഇൻസ്റ്റലേഷൻ നിർവഹിക്കാൻ നിലവിലെ ഉപയോക്താവിനെ അനുവദിച്ചിട്ടില്ല."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"പിശക്"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"ആപ്പ് അൺഇൻസ്റ്റാൾ ചെയ്യാൻ കഴിഞ്ഞില്ല."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"അപ്ലിക്കേഷൻ അൺഇൻസ്റ്റാളുചെയ്യുക"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"അപ്‌ഡേറ്റ് അൺഇൻസ്റ്റാളുചെയ്യുക"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> എന്നത് ഇനിപ്പറയുന്ന അപ്ലിക്കേഷന്റെ ഭാഗമാണ്:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"ഈ അപ്ലിക്കേഷൻ അൺഇൻസ്റ്റാളുചെയ്യണോ?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"ഈ അപ്ലിക്കേഷൻ "<b>"എല്ലാ"</b>" ഉപയോക്താക്കൾക്കുമായി അൺഇൻസ്റ്റാളുചെയ്യണോ? ഉപകരണത്തിലെ "<b>"എല്ലാ"</b>" ഉപയോക്താക്കളിൽ നിന്നും അപ്ലിക്കേഷനും അതിന്റെ ഡാറ്റയും നീക്കംചെയ്യപ്പെടും."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"<xliff:g id="USERNAME">%1$s</xliff:g> എന്ന ഉപയോക്താവിനായി ഈ അപ്ലിക്കേഷൻ അൺഇൻസ്റ്റാളുചെയ്യണോ?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"ഫാക്ടറി പതിപ്പ് ഉപയോഗിച്ച് ഈ ആപ്പ് മാറ്റിസ്ഥാപിക്കണോ? എല്ലാ ഡാറ്റയും നീക്കംചെയ്യപ്പെടും."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"ഫാക്ടറി പതിപ്പ് ഉപയോഗിച്ച് ഈ ആപ്പ് മാറ്റിസ്ഥാപിക്കണോ? എല്ലാ ഡാറ്റയും നീക്കംചെയ്യപ്പെടും. ഔദ്യോഗിക പ്രൊഫൈലുകൾ ഉള്ളവർ ഉൾപ്പെടെ, ഈ ഉപകരണത്തിന്റെ എല്ലാ ഉപയോക്താക്കളെയും ഇത് ബാധിക്കുന്നു."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"അൺ ഇൻസ്‌റ്റാൾ ചെയ്‌തുകൊണ്ടിരിക്കുന്നവ"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"അൺ ഇൻസ്‌റ്റാൾ ചെയ്യാൻ കഴിയാഞ്ഞവ"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"അൺഇൻസ്‌‌റ്റാൾ ചെയ്യുന്നു..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> അൺഇൻസ്റ്റാൾ ചെയ്യുന്നു…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"അൺഇൻസ്റ്റാളുചെയ്യൽ പൂർത്തിയായി."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> അൺഇൻസ്‌റ്റാൾ ചെയ്‌തു"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"അൺഇൻസ്റ്റാളുചെയ്തു."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> അൺഇൻസ്റ്റാൾ ചെയ്യൽ പരാജയം."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"സജീവ ഉപകരണ അഡ്‌മിൻ ആപ്പ് അൺഇൻസ്റ്റാൾ ചെയ്യാൻ കഴിയില്ല"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> എന്ന ഉപയോക്താവിന്റെ സജീവ ഉപകരണ അഡ്‌മിൻ ആപ്പ് അൺഇൻസ്റ്റാൾ ചെയ്യാൻ കഴിയില്ല"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"ചില ഉപയോക്താക്കൾക്കോ പ്രൊഫൈലുകൾക്കോ ഈ ആപ്പ് ആവശ്യമാണ്, മറ്റുള്ളവർക്ക് ഈ ആപ്പ് അൺഇൻസ്റ്റാൾ ചെയ്തിരിക്കുന്നു"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലിന് ഈ ആപ്പ് ആവശ്യമുള്ളതിനാൽ അത് അൺ‌ഇൻസ്റ്റാൾ ചെയ്യാനാവില്ല."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"നിങ്ങളുടെ ഉപകരണ അഡ്മിനിസ്ട്രേറ്ററിന് ഈ അപ്ലിക്കേഷൻ ആവശ്യമുള്ളതിനാൽ ഇത് അൺഇൻസ്റ്റാൾ ചെയ്യാനാവില്ല."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"ഉപകരണ അഡ്‌മിൻ ആപ്പുകളെ മാനേജുചെയ്യുക"</string>
+    <string name="manage_users" msgid="3125018886835668847">"ഉപയോക്താക്കളെ മാനേജുചെയ്യുക"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> അൺഇൻസ്റ്റാളുചെയ്യാനായില്ല."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"പാക്കേജ് പാഴ്‌സുചെയ്യുന്നതിൽ ഒരു പ്രശ്‌നമുണ്ടായിരുന്നു."</string>
+    <string name="newPerms" msgid="6039428254474104210">"പുതിയത്"</string>
+    <string name="allPerms" msgid="1024385515840703981">"എല്ലാം"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"സ്വകാര്യത"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"ഉപകരണ ആക്‌സസ്സ്"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"ഈ അപ്‌ഡേറ്റിന് പുതിയ അനുമതികളൊന്നും ആവശ്യമില്ല."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"നിരസിക്കുക"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"കൂടുതൽ‍ വിവരങ്ങള്‍"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"എന്തായാലും നിരസിക്കുക"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"<xliff:g id="ACTION">%2$s</xliff:g> &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കണോ?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ എല്ലായ്‌പ്പോഴും <xliff:g id="ACTION">%2$s</xliff:g> എന്നതിന് അനുവദിക്കണമോ?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"ആപ്പ് ഉപയോഗിക്കുമ്പോൾ മാത്രം"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"എല്ലായ്‌പ്പോഴും"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"നിരസിക്കുക, വീണ്ടും ആവശ്യപ്പെടരുത്"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> പ്രവർത്തനരഹിതമാക്കി"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"എല്ലാം പ്രവർത്തനരഹിതമാക്കി"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ഒന്നും പ്രവർത്തനരഹിതമാക്കിയിട്ടില്ല"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"അനുവദിക്കുക"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"ആപ്സ്"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"ആപ്പ് അനുമതികൾ"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"വീണ്ടും ആവശ്യപ്പെടരുത്"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"അനുമതികൾ ഇല്ല"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"അധിക അനുമതികൾ"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"ആപ്പ് വിവരം തുറക്കുക"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> എണ്ണം കൂടി</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> എണ്ണം കൂടി</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"ഈ ആപ്പ് Android-ന്റെ പഴയ പതിപ്പിനായാണ് രൂപകൽപ്പന ചെയ്‌തിരിക്കുന്നത്. അനുമതി നിരസിക്കുന്നത് തുടർന്ന് ഉദ്ദേശിച്ചവിധം പ്രവർത്തിക്കാതിരിക്കാനിടയാക്കുന്നു."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"ഒരു അജ്ഞാതപ്രവർത്തനം നടത്തുക"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> / <xliff:g id="COUNT_1">%2$d</xliff:g> ആപ്പുകൾ അനുവദനീയം"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"സിസ്റ്റം കാണിക്കുക"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"സിസ്റ്റം മറയ്‌ക്കുക"</string>
+    <string name="no_apps" msgid="1965493419005012569">"ആപ്സ് ഒന്നുമില്ല"</string>
+    <string name="location_settings" msgid="1774875730854491297">"ലൊക്കേഷൻ ക്രമീകരണം"</string>
+    <string name="location_warning" msgid="8778701356292735971">"ഈ ഉപകരണത്തിനായുള്ള ലൊക്കേഷൻ സേവനങ്ങളുടെ ദാതാവ് <xliff:g id="APP_NAME">%1$s</xliff:g> ആണ്. ലൊക്കേഷൻ ക്രമീകരണത്തിൽ നിന്ന് ലൊക്കേഷൻ ആക്സസ് പരിഷ്കരിക്കാവുന്നതാണ്."</string>
+    <string name="system_warning" msgid="7103819124542305179">"നിങ്ങൾ ഈ അനുമതി നിഷേധിക്കുന്നുവെങ്കിൽ, നിങ്ങളുടെ ഉപകരണത്തിന്റെ അടിസ്ഥാന ഫീച്ചറുകൾ ഉദ്ദേശിച്ചത് പോലെ തുടർന്ന് പ്രവർത്തിച്ചേക്കില്ല."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"നയം മുഖേനെ നടപ്പിലാക്കിയത്"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"നയം അനുസരിച്ച് ബാക്ക്‌ഗ്രൗണ്ട് ആക്‌സസ് പ്രവർത്തനരഹിതമാക്കി"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"നയം അനുസരിച്ച് ബാക്ക്‌ഗ്രൗണ്ട് ആക്‌സസ് പ്രവർത്തനക്ഷമമാക്കി"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"നയം അനുസരിച്ച് ഫോർഗ്രൗണ്ട് ആക്‌സസ് പ്രവർത്തനക്ഷമമാക്കി"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"അഡ്‌മിൻ നിയന്ത്രിക്കുന്നത്"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"എല്ലായ്‌പ്പോഴും"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"ആപ്പ് ഉപയോഗിക്കുമ്പോൾ മാത്രം"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"ഒരിക്കലും"</string>
+    <string name="loading" msgid="7811651799620593731">"ലോഡുചെയ്യുന്നു..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"എല്ലാ അനുമതികളും"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"മറ്റ് ആപ്പ് ശേഷികൾ"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"അനുമതി അഭ്യർത്ഥന"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"സ്ക്രീൻ ഓവർലേ കണ്ടെത്തി"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"ഈ അനുമതി ക്രമീകരണം മാറ്റുന്നതിന്, ക്രമീകരണം &gt; ആപ്സ് എന്നതിൽ നിന്ന് നിങ്ങളാദ്യം സ്ക്രീൻ ഓവർലേ ഓഫാക്കേണ്ടതാണ്"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"ക്രമീകരണം തുറക്കുക"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"ഇൻസ്റ്റാളോ അൺഇൻസ്റ്റാളോ ചെയ്യുന്നതിന് Wear-ൽ പിന്തുണയില്ല"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"എന്തൊക്കെ ആക്സസ്സ് ചെയ്യാനാണ് &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കേണ്ടതെന്ന് തിരഞ്ഞെടുക്കുക"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; അപ്‌ഡേറ്റ് ചെയ്തിരിക്കുന്നു. എന്തൊക്കെ ആക്സസ്സ് ചെയ്യാനാണ് ഈ ആപ്പിനെ അനുവദിക്കേണ്ടതെന്ന് തിരഞ്ഞെടുക്കുക."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"റദ്ദാക്കുക"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"തുടരുക"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"പുതിയ അനുമതികൾ"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"നിലവിലെ അനുമതികൾ"</string>
+    <string name="message_staging" msgid="6151794817691100003">"ആപ്പ് തയ്യാറാക്കുന്നു…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"അജ്ഞാതം"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"നിങ്ങളുടെ സുരക്ഷയ്ക്ക്, ഈ ഉറവിടത്തിൽ നിന്നുള്ള, തിരിച്ചറിയാനാകാത്ത ആപ്‌സ് ഇൻസ്റ്റാൾ ചെയ്യാൻ നിങ്ങളുടെ ടാബ്‌ലെറ്റ് അനുവദിക്കപ്പെടില്ല."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"നിങ്ങളുടെ സുരക്ഷയ്ക്ക്, ഈ ഉറവിടത്തിൽ നിന്നുള്ള, തിരിച്ചറിയാനാകാത്ത ആപ്‌സ് ഇൻസ്റ്റാൾ ചെയ്യാൻ നിങ്ങളുടെ ടിവി അനുവദിക്കപ്പെടില്ല."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"നിങ്ങളുടെ സുരക്ഷയ്ക്ക്, ഈ ഉറവിടത്തിൽ നിന്നുള്ള, തിരിച്ചറിയാനാകാത്ത ആപ്‌സ് ഇൻസ്റ്റാൾ ചെയ്യാൻ നിങ്ങളുടെ ഫോൺ അനുവദിക്കപ്പെടില്ല."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"തിരിച്ചറിയാനാകാത്ത ആപ്പുകളാൽ നിങ്ങളുടെ ഫോണും വ്യക്തിഗത ഡാറ്റയും ആക്രമിക്കപ്പെടാനുള്ള സാധ്യത വളരെ കൂടുതലാണ്. ഈ ആപ്പ് ഇൻസ്‌റ്റാൾ ചെയ്യുന്നതിലൂടെ, ഇത് ഉപയോഗിക്കുന്നതിനാൽ നിങ്ങളുടെ ഫോണിന് സംഭവിച്ചേക്കാവുന്ന എല്ലാ നാശനഷ്‌ടങ്ങൾക്കും അല്ലെങ്കിൽ ഡാറ്റാ നഷ്‌ടങ്ങൾക്കും നിങ്ങൾക്കാണ് ഉത്തരവാദിത്തമെന്ന് നിങ്ങൾ അംഗീകരിക്കുന്നു."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"തിരിച്ചറിയാനാകാത്ത ആപ്പുകളാൽ നിങ്ങളുടെ ടാബ്‌ലെറ്റും വ്യക്തിഗത ഡാറ്റയും ആക്രമിക്കപ്പെടാനുള്ള സാധ്യത വളരെ കൂടുതലാണ്. ഈ ആപ്പ് ഇൻസ്‌റ്റാൾ ചെയ്യുന്നതിലൂടെ, ഇത് ഉപയോഗിക്കുന്നതിനാൽ നിങ്ങളുടെ ടാബ്‌ലെറ്റിന് സംഭവിച്ചേക്കാവുന്ന എല്ലാ നാശനഷ്‌ടങ്ങൾക്കും അല്ലെങ്കിൽ ഡാറ്റാ നഷ്‌ടങ്ങൾക്കും നിങ്ങൾക്കാണ് ഉത്തരവാദിത്തമെന്ന് നിങ്ങൾ അംഗീകരിക്കുന്നു."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"തിരിച്ചറിയാനാകാത്ത ആപ്പുകളാൽ നിങ്ങളുടെ ടിവിയും വ്യക്തിഗത ഡാറ്റയും ആക്രമിക്കപ്പെടാനുള്ള സാധ്യത വളരെ കൂടുതലാണ്. ഈ ആപ്പ് ഇൻസ്‌റ്റാൾ ചെയ്യുന്നതിലൂടെ, ഇത് ഉപയോഗിക്കുന്നതിനാൽ നിങ്ങളുടെ ടിവിക്ക് സംഭവിച്ചേക്കാവുന്ന എല്ലാ നാശനഷ്‌ടങ്ങൾക്കും അല്ലെങ്കിൽ ഡാറ്റാ നഷ്‌ടങ്ങൾക്കും നിങ്ങൾക്കാണ് ഉത്തരവാദിത്തമെന്ന് നിങ്ങൾ അംഗീകരിക്കുന്നു."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"തുടരുക"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"ക്രമീകരണം"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear ആപ്പുകൾ ഇൻസ്‌റ്റാൾ/അൺ ഇൻസ്‌റ്റാൾ ചെയ്യൽ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-mn-television/strings.xml b/packages/PackageInstaller/res/values-mn-television/strings.xml
new file mode 100644
index 0000000..39c899f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mn-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Taтгалзаад дахин бүү асуугаарай"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Та дараа үүнийг Toхиргоо &amp; Апп дотроос солих боломжтой"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Системийн аппыг харуулах"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Апп-н зөвшөөрөл"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Апп-н зөвшөөрөл"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> зөвшөөрөл"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Нэмэлт зөвшөөрөл"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> зөвшөөрөл"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-mn-watch/strings.xml b/packages/PackageInstaller/res/values-mn-watch/strings.xml
new file mode 100644
index 0000000..120c336
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mn-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Татгалзах, дахин бүү асуугаарай"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Системийн аппыг харуулах"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Өөрчлөх боломжгүй"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Тийм"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Цуцлах"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-mn/strings.xml b/packages/PackageInstaller/res/values-mn/strings.xml
new file mode 100644
index 0000000..1fd12a2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mn/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Багц суулгагч"</string>
+    <string name="next" msgid="3057143178373252333">"Дараах"</string>
+    <string name="install" msgid="5896438203900042068">"Суулгах"</string>
+    <string name="done" msgid="3889387558374211719">"Дуусгах"</string>
+    <string name="cancel" msgid="8360346460165114585">"Цуцлах"</string>
+    <string name="installing" msgid="8613631001631998372">"Суулгаж байна…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-г суулгаж байна…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Апп суулгагдсан."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Та энэ аппликешныг суулгамаар байна уу? Энэ дараахад хандах болно:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Та энэ аппликешныг суулгах уу? Энэ тусгай хандалт шаардахгүй."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Та энэ аппликейшны шинэчлэлтийг суулгах уу? Таны хуучин дата устах болно. Шинэчлэгдсэн аппликейшн нь дараахад хандаж чадна:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Та энэ үндсэн аппликейшны шинэчлэлтийг суулгах уу? Таны хуучин дата устах болно. Шинэчлэгдсэн аппликейшн нь дараахад хандаж чадна:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Та энэ аппликешны шинэчлэлтийг суулгах уу? Таны хуучин дата устах болно. Энэ ямар нэгэн тусгай эрх шаардахгүй."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Та энэ үндсэн аппликешны шинэчлэлтийг суулгах уу? Таны хуучин дата устах болно. Энэ ямар нэгэн тусгай эрх шаардахгүй."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Апп суулгагдаагүй."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Багц суулгахыг блоклосон байна."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Багц одоогийн багцтай тохирохгүй байгаа тул апп-г суулгаж чадсангүй."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Апп таны таблеттай тохирохгүй байгаа тул үүнийг суулгасангүй."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Энэ апп нь таны ТВ-д нийцэхгүй."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Апп таны утсанд тохирохгүй байгаа тул үүнийг суулгасангүй."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Багц хүчингүй тул апп-г суулгасангүй."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> таны таблет дээр суусангүй."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> нь таны телевизэд суурилуулах боломжгүй байна."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г таны утсан дээр суулгах боломжгүй."</string>
+    <string name="launch" msgid="4826921505917605463">"Нээх"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Таны админ тодорхойгүй сурвалжаас татсан апп суулгахыг зөвшөөрдөггүй"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Энэ хэрэглэгч тодорхойгүй апп суулгах боломжгүй байна"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Энэ хэрэглэгч апп суулгах зөвшөөрөлгүй байна"</string>
+    <string name="ok" msgid="3468756155452870475">"ОК"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Апп удирдах"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Зай дутагдаж байна"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г суулгаж чадсангүй. Зайг чөлөөлөөд дахин оролдоно уу."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Апп олдсонгүй"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Суулгасан апп-н жагсаалт дотроос апп олдсонгүй."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Зөвшөөрөөгүй"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Одоогийн хэрэглэгч үүнийг устгах боломжгүй."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Алдаа"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Апп-г устгаж чадсангүй."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Апп устгах"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Шинэчлэлийг устгах"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> нь дараах апп-н хэсэг болно:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Та энэ апп-г устгамаар байна уу?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Та энэ апп-г "<b>"бүх"</b>" хэрэглэгчээс устгах уу? Аппикешн болон доторх дата нь төхөөрөмж дээрх "<b>"бүх"</b>" хэрэглэгчээс устгагдах болно."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Та энэ апп-г <xliff:g id="USERNAME">%1$s</xliff:g> хэрэглэгчийн хувьд устгах уу?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Энэ апп-г үйлдвэрээс ирсэн хувилбараар нь солих уу? Бүх өгөгдөл устах болно."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Энэ апп-г үйлдвэрээс ирсэн хувилбараар нь солих уу? Бүх өгөгдөл устах болно. Энэ нь ажлын профайлтай хэрэглэгч зэрэг энэ төхөөрөмжийн бүх хэрэглэгчдэд үйлчлэх болно."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Устгаж байна"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Устгаж чадсангүй"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Устгаж байна…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-г устгаж байна…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Устгаж дуусав."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-г устгасан"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Устгалт амжилтгүй болов."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-г устгаж чадсангүй."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Идэвхтэй төхөөрөмжийн админ аппыг устгах боломжгүй"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g>-д идэвхтэй төхөөрөмжийн админ аппыг устгах боломжгүй байна"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Энэ апп нь зарим хэрэглэгч эсвэл профайлд шаардлагатай учир үүнийг тэдгээрээс бусад хэрэглэгчдээс устгасан"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Энэ апп таны профайлд шаардлагатай бөгөөд устгах боломжгүй."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Энэ апп нь таны төхөөрөмжийн админд шаардлагатай бөгөөд устгах боломжгүй."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Төхөөрөмжийн админ аппыг удирдах"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Хэрэгчлэгчдийг удирдах"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г устгаж чадсангүй."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Багцийг задлахад алдаа гарав."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Шинэ"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Бүгд"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Нууцлал"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Төхөөрөмжид хандах"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Энэ шинэчлэл шинэ зөвшөөрөл шаардахгүй."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Татгалзах"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Дэлгэрэнгүй мэдээлэл"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Хэдийд ч татгалзах"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>-ийн <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-г <xliff:g id="ACTION">%2$s</xliff:g>-г зөвшөөрөх үү?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-г <xliff:g id="ACTION">%2$s</xliff:g>-д байнга зөвшөөрөх үү?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Зөвхөн апп ашиглах үед"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Байнга"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Taтгалзаад дахин бүү асуугаарай"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g>-г цуцалсан"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"бүгдийг цуцалсан"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"алийг ч цуцлаагүй"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Зөвшөөрөх"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Апп"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Апп зөвшөөрөл"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Дахиж бүү асуу"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Зөвшөөрөлгүй байна"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Нэмэлт зөвшөөрөл"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Аппын мэдээллийг нээх"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> бусад</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> бусад</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Энэхүү аппыг нь Android-ын хуучин хувилбарт зориулсан. Зөвшөөрлийг үгүйсгэх нь цаашид ажиллахгүй болгож болно."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"Танигдаагүй үйлдлийг гүйцэтгэх"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g>-с <xliff:g id="COUNT_0">%1$d</xliff:g> аппыг зөвшөөрдөг"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Системийг харуулах"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Системийг нуух"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Апп байхгүй"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Байршлын тохиргоо"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> нь энэ төхөөрөмжийн байршлын үйлчилгээ үзүүлэгч юм. Байршилд хандалтыг байршлын тохиргоо хэсгээс өөрчилж болно."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Хэрэв та энэ зөвшөөрөлд татгалзсан тохиолдолд таны төхөөрөмжийн үндсэн функц нь алдаатай ажиллаж магадгүй."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Бодлогын дагуу хэрэгжсэн"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Арын дэвсгэрийн хандалтыг удирдамжаас идэвхгүй болгосон"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Арын дэвсгэрийн хандалтыг удирдамжаас идэвхтэй болгосон"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Нүүрний дэвсгэрийн хандалтыг удирдамжаас идэвхтэй болгосон"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Админ удирддаг"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Байнга"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Зөвхөн апп ашиглах үед"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Хэзээ ч үгүй"</string>
+    <string name="loading" msgid="7811651799620593731">"Ачаалж байна..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Бүх зөвшөөрөл"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Бусад апп-ын боломж"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Зөвшөөрлийн хүсэлт"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Дэлгэцийн давхарга илрүүллээ"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Зөвшөөрлийн тохиргоог өөрчлөхийн тулд, эхлээд Тохиргоо ба Апп хэсгээс дэлгэцийн давхаргыг унтраана уу."</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Тохиргоог нээх"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Суулгах/Устгах үйлдлийг Wear дэмжээгүй."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-н хандаж болох зүйлсийг сонгоно уу"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-г шинэчиллээ. Энэ апп-н хандаж болох зүйлсийг сонгоно уу."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Цуцлах"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Үргэлжлүүлэх"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Шинэ зөвшөөрөл"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Одоогийн зөвшөөрөл"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Апп-г байршуулж байна…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Тодорхойгүй"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Таны аюулгүй байдлыг хангахын тулд таны таблет энэ эх сурвалжаас тодорхойгүй апп суулгахыг зөвшөөрдөггүй."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Таны аюулгүй байдлыг хангахын тулд таны ТВ энэ эх сурвалжаас тодорхойгүй апп суулгахыг зөвшөөрдөггүй."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Таны аюулгүй байдлыг хангахын тулд таны утас энэ эх сурвалжаас тодорхойгүй апп суулгахыг зөвшөөрдөггүй."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Таны утас болон хувийн өгөгдөл тодорхойгүй апп суулгасан тохиолдолд гэмтэж болзошгүй. Энэ аппыг суулгаснаар үүнийг ашигласнаас үүдэн таны утсанд гэмтэл гарах, эсвэл өгөгдөл устах зэрэг эрсдэлийг хариуцна гэдгээ зөвшөөрч байна."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Таны таблет болон хувийн өгөгдөл тодорхойгүй апп суулгасан тохиолдолд гэмтэж болзошгүй. Энэ аппыг суулгаснаар үүнийг ашигласнаас үүдэн таны таблетад гэмтэл гарах, эсвэл өгөгдөл устах зэрэг эрсдэлийг хариуцна гэдгээ зөвшөөрч байна."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Таны ТВ болон хувийн өгөгдөл тодорхойгүй апп суулгасан тохиолдолд гэмтэж болзошгүй. Энэ аппыг суулгаснаар үүнийг ашигласнаас үүдэн таны ТВ-д гэмтэл гарах, эсвэл өгөгдөл устах зэрэг эрсдэлийг хариуцна гэдгээ зөвшөөрч байна."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Үргэлжлүүлэх"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Тохиргоо"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Зүүсгэл аппыг суулгаж/устгаж байна"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-mr-television/strings.xml b/packages/PackageInstaller/res/values-mr-television/strings.xml
new file mode 100644
index 0000000..e648404
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mr-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"नकार द्या आणि पुन्हा विचारू नका"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"तुम्ही हे नंतर सेटिंग्ज आणि अॅप्स मध्ये बदलू शकता"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"सिस्टम अॅप्स दर्शवा"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"अॅप परवानग्या"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"अॅप परवानग्या"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> परवानग्या"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"अतिरिक्त परवानग्या"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> परवानग्या"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-mr-watch/strings.xml b/packages/PackageInstaller/res/values-mr-watch/strings.xml
new file mode 100644
index 0000000..74d32df
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mr-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"नकार द्या, पुन्हा विचारू नका"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"सिस्टम अॅप्स दर्शवा"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"बदलू शकत नाही"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"होय"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"रद्द करा"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml
new file mode 100644
index 0000000..b9acaa5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mr/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"पॅकेज स्‍थापनकर्ता"</string>
+    <string name="next" msgid="3057143178373252333">"पुढील"</string>
+    <string name="install" msgid="5896438203900042068">"स्‍थापित करा"</string>
+    <string name="done" msgid="3889387558374211719">"पूर्ण झाले"</string>
+    <string name="cancel" msgid="8360346460165114585">"रद्द करा"</string>
+    <string name="installing" msgid="8613631001631998372">"इंस्टॉल करत आहे..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> इन्‍स्टॉल करत आहे…"</string>
+    <string name="install_done" msgid="3682715442154357097">"अॅप इंस्टॉल झाला."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"तुम्ही हा अॅप्लिकेशन इंस्टॉल करू इच्छिता? यास यावर प्रवेश मिळेल:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"तुम्ही हा अॅप्लिकेशन इंस्टॉल करू इच्छिता? यास कोणत्याही विशेष प्रवेशाची आवश्यकता नसते."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"तुम्हाला सद्य अॅप्लिकेशनवर अपडेट इंस्टॉल करायची आहे? तुम्ही तुमचा सद्य डेटा गमावणार नाही. अपडेट केलेल्या अॅप्लिकेशनला यावर अॅक्सेस मिळेल:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"तुम्ही या बिल्ट-इन अॅप्लिकेशनवर अपडेट इंस्टॉल करायची आहे? तुम्ही तुमचा सद्य डेटा गमावणार नाही. अपडेट केलेल्या अॅप्लिकेशनला यावर अॅक्सेस मिळेल:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"तुम्हाला सद्य अॅप्लिकेशनवर अपडेट इंस्टॉल करायची आहे? तुम्ही तुमचा सद्य डेटा गमावणार नाही. यासाठी कोणताही विशेष अॅक्सेस आवश्यक नसतो."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"तुम्ही या बिल्ट-इन अॅप्लिकेशनवर अपडेट इंस्टॉल करायची आहे? तुम्ही तुमचा सद्य डेटा गमावणार नाही. यासाठी कोणताही विशेष अॅक्सेस आवश्यक नसतो."</string>
+    <string name="install_failed" msgid="6579998651498970899">"अॅप इंस्टॉल झाला नाही."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"पॅकेेच इंस्टॉल होण्यास अवरोधित केलेले होते."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"पॅकेजचा विद्यमान पॅकेजशी विरोध असल्याने अॅप इंस्टॉल केला नाही."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"अॅप आपल्या टॅब्लेटशी सुसंगत नसल्याने अॅप इंस्टॉल केला नाही."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"हा अॅप आपल्या टीव्हीशी सुसंगत नाही."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"अॅप आपल्या फोनशी सुसंगत नसल्याने अॅप इंस्टॉल केला नाही."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"पॅकेज अवैध असल्याचे दिसत असल्याने अॅप इंस्टॉल केले नाही."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपल्या टॅब्लेटवर इंस्टॉल केला जाऊ शकला नाही."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपल्या टीव्हीवर इंस्टॉल केले जाऊ शकले नाही."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपल्या फोनवर इंस्टॉल केला जाऊ शकला नाही."</string>
+    <string name="launch" msgid="4826921505917605463">"उघडा"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"अज्ञात स्रोतांकडून मिळवलेल्या अॅप्सच्या स्थापनेला आपला प्रशासक अनुमती देत नाही"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"या वापरकर्त्याद्वारे अज्ञात अ‍ॅप्स इंस्टॉल केली जाऊ शकत नाहीत"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"या वापरकर्त्याला अ‍ॅप्स इंस्टॉल करण्याची परवानगी नाही"</string>
+    <string name="ok" msgid="3468756155452870475">"ठीक"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"अ‍ॅप्स व्यवस्थापित करा"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"स्‍थानाबाहेर"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> इंस्टॉल केला जाऊ शकला नाही. काही स्थान मोकळे करा आणि पुन्हा प्रयत्न करा."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"अॅप आढळला नाही"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"इंस्टॉल केलेल्या अॅप्सच्या सूचीमध्ये अॅप आढळला नाही."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"अनुमती नाही"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"हे अनइंस्टॉल करण्याची वर्तमान वापरकर्त्यास अनुमती नाही."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"एरर"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"अॅप अनइंस्टॉल करणे शक्य झाले नाही."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"अॅप अनइंस्टॉल करा"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"अपडेट अनइंस्टॉल करा"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> खालील अॅप चा भाग आहे:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"तुम्ही हा अॅप अनइंस्टॉल करू इच्छिता?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"तुम्ही हा अॅप "<b>"सर्व"</b>" वापरकर्त्यांसाठी अनइंस्टॉल करू इच्छिता? अॅप्लिकेशन आणि त्याचा डेटा डिव्हाइसवरील "<b>"सर्व"</b>" वापरकर्त्यांवरून काढला जाईल."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"तुम्ही <xliff:g id="USERNAME">%1$s</xliff:g> वापरकर्त्यासाठी हा अ‍ॅप विस्‍थापित करु इच्‍छिता?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"फॅक्टरी आवृत्तीसह हा अॅप पुनर्स्थित करायचा? सर्व डेटा काढला जाईल."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"फॅक्टरी आवृत्तीसह हा अॅप पुनर्स्थित करायचा? सर्व डेटा काढला जाईल. हे कार्य प्रोफाईल असलेल्यांसह या डिव्हाइसच्या सर्व वापरकर्त्यांना प्रभावित करते."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"अनइंस्टॉल करणे चालू आहे"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"अनइंस्टॉल करणे अयशस्वी झाले"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"अनइंस्टॉल करत आहे…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> अनइंस्टॉल करत आहे…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"अनइंस्टॉल करणे समाप्त."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> अनइंस्टॉल केले"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"अनइंस्टॉल करणे अयशस्वी."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> अनइंस्टॉल करणे अयशस्वी झाले."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"अॅक्टिव्हेट डिव्हाइस प्रशासक अ‍ॅप अनइंस्टॉल करू शकत नाही"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> साठी अॅक्टिव्हेट डिव्हाइस प्रशासक अ‍ॅप अनइंस्टॉल करू शकत नाही"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"हा अॅप काही वापरकर्ते किंवा प्रोफाईलसाठी आवश्यक आहे आणि इतरांसाठी अनइंस्टॉल केला होता"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"आपल्या प्रोफाईलसाठी हा अ‍ॅप आवश्यक आहे आणि अनइंस्टॉल केला जाऊ शकत नाही."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"तुमच्या डिव्हाइस प्रशासकास हे अ‍ॅप आवश्यक आहे आणि ते अनइंस्टॉल केले जाऊ शकत नाही."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"डिव्हाइस प्रशासक अ‍ॅप्स व्यवस्थापित करा"</string>
+    <string name="manage_users" msgid="3125018886835668847">"वापरकर्त्यांना व्यवस्‍थापित करा"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> अनइंस्टॉल केला जाऊ शकला नाही."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"पॅकेज चे विश्लेषण करताना समस्या आली."</string>
+    <string name="newPerms" msgid="6039428254474104210">"नवीन"</string>
+    <string name="allPerms" msgid="1024385515840703981">"सर्व"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"गोपनीयता"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"डिव्हाइस अॅक्सेस"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"या अद्यतनास कोणत्याही नवीन परवानग्यांची आवश्यकता नाही."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"नकार द्या"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"अधिक माहिती"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"तरीही नकार द्या"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> पैकी <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला <xliff:g id="ACTION">%2$s</xliff:g> ची अनुमती द्यायची?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला नेहमी <xliff:g id="ACTION">%2$s</xliff:g> ची अनुमती द्यायची का?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"फक्त अॅप वापरत असताना"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"नेहमी"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"नकार द्या आणि पुन्हा विचारू नका"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> अक्षम केल्या"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"सर्व अक्षम केल्या"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"कोणत्याही अक्षम केल्या नाहीत"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"अनुमती द्या"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"अॅप्स"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"अॅप परवानग्या"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"पुन्हा विचारू नका"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"परवानग्या नाहीत"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"अतिरिक्त परवानग्या"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"अॅप माहिती उघडा"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <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="old_sdk_deny_warning" msgid="3872277112584842615">"हा अॅप Android च्या जुन्या आवृत्तीसाठी डीझाइन करण्यात आला होता. परवानगी नाकारल्यामुळे तो यापुढे उद्देशाप्रमाणे कार्य करणार नाही."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"अज्ञात क्रिया करा"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g> पैकी <xliff:g id="COUNT_0">%1$d</xliff:g> अ‍ॅप्सना अनुमती दिली"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"सिस्टम दर्शवा"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"सिस्‍टीम लपवा"</string>
+    <string name="no_apps" msgid="1965493419005012569">"कोणतेही अॅप्स नाहीत"</string>
+    <string name="location_settings" msgid="1774875730854491297">"स्थान सेटिंग्ज"</string>
+    <string name="location_warning" msgid="8778701356292735971">"या डिव्‍हाइससाठी <xliff:g id="APP_NAME">%1$s</xliff:g> स्थान सेवांचा प्रदाता आहे. स्थान प्रवेश स्थान सेटिंग्ज वरून सुधारित केला जाऊ शकतो."</string>
+    <string name="system_warning" msgid="7103819124542305179">"तुम्ही ही परवानगी नाकारल्यास, आपल्‍या डिव्‍हाइसची मुलभूत वैशिष्ट्ये अपेक्षित असल्याप्रमाणे कदाचित कार्य करू शकणार नाहीत."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"धोरणाद्वारे सक्ती केली"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"धोरणाद्वारे बॅकग्राउंड अॅक्सेस बंद केला आहे"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"धोरणाद्वारे बॅकग्राउंड अॅक्सेस सुरू केला आहे"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"धोरणाद्वारे फोरग्राउंड अॅक्सेस सुरू केला आहे"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"प्रशासनाद्वारे नियंत्रित केलेले"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"नेहमी"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"फक्त अॅप वापरत असताना"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"कधीही नाही"</string>
+    <string name="loading" msgid="7811651799620593731">"लोड करत आहे..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"सर्व परवानग्या"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"अन्य अॅप क्षमता"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"परवानगीची विनंती"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"स्क्रीन ओव्हरले आढळले"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"हे परवानगी सेटिंग बदलण्‍यासाठी, तुम्हाला सेटिंग्ज &gt; अॅप्स मधून स्क्रीन ओव्हरले बंद करावे लागेल"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"सेटिंग्ज उघडा"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"इंस्टॉल करा/अनइंस्टॉल करा क्रिया Wear वर समर्थित नाहीत."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला कशामध्‍ये प्रवेश करण्‍याची अनुमती द्यावी ते निवडा"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; अपडेट केला गेला आहे. या अॅपला कशामध्‍ये प्रवेश करण्‍याची अनुमती द्यावी ते निवडा."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"रद्द करा"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"सुरू ठेवा"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"नवीन परवानग्या"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"वर्तमान परवानग्या"</string>
+    <string name="message_staging" msgid="6151794817691100003">"अॅप प्रारंभाच्या स्थितीत आहे..."</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"अज्ञात"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"आपल्या सुरक्षिततेसाठी, आपल्या टॅबलेटला या स्रोताकडील अज्ञात अ‍ॅप्स इंस्टॉल करण्याची अनुमती नाही."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"आपल्या सुरक्षिततेसाठी, आपल्या टीव्हीला या स्रोताकडील अज्ञात अ‍ॅप्स इंस्टॉल करण्याची अनुमती नाही."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"आपल्या सुरक्षिततेसाठी, आपल्या फोनला या स्रोताकडील अज्ञात अ‍ॅप्स इंस्टॉल करण्याची अनुमती नाही."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"तुमचा फोन आणि वैयक्तिक डेटा अज्ञात अॅप्‍समुळे होणार्‍या अटॅकमुळे अधिक असुरक्षित आहे. हा अॅप इन्‍स्‍टॉल करून, तुम्‍ही सहमती देता की तो वापरल्‍याने होणार्‍या तुमच्‍या फोनच्‍या कोणत्‍याही प्रकारच्‍या नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"तुमचा टॅबलेट आणि वैयक्तिक डेटा अज्ञात अॅप्‍समुळे होणार्‍या अटॅकमुळे अधिक असुरक्षित आहे. हा अॅप इन्‍स्‍टॉल करून, तुम्‍ही सहमती देता की तो वापरल्‍याने होणार्‍या तुमच्‍या टॅबलेटच्‍या कोणत्‍याही प्रकारच्‍या नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"तुमचा टीव्‍ही आणि वैयक्तिक डेटा अज्ञात अॅप्‍समुळे होणार्‍या अटॅकमुळे अधिक असुरक्षित आहे. हा अॅप इन्‍स्‍टॉल करून, तुम्ही सहमती देता की तो वापरल्‍याने होणार्‍या तुमच्‍या टीव्‍हीच्‍या कोणत्‍याही प्रकारच्‍या नुकसान किंवा डेटा हानीसाठी तुम्‍ही जबाबदार आहात."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"सुरू ठेवा"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"सेटिंग्ज"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"वेअर अ‍ॅप्स इन्‍स्टॉल/अनइन्‍स्टॉल करणे"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ms-television/strings.xml b/packages/PackageInstaller/res/values-ms-television/strings.xml
new file mode 100644
index 0000000..989aba7
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ms-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Tolak dan jangan tanya lagi"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Anda boleh menukar ini nanti dalam Tetapan &gt; Apl"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Tunjukkan apl sistem"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Kebenaran apl"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Kebenaran apl"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Kebenaran <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Kebenaran tambahan"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Kebenaran <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ms-watch/strings.xml b/packages/PackageInstaller/res/values-ms-watch/strings.xml
new file mode 100644
index 0000000..dad185fa
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ms-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Tolak, jangan tanya lagi"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Tunjukkan apl sistem"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Tidak dpt diubah"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Ya"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Batal"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ms/strings.xml b/packages/PackageInstaller/res/values-ms/strings.xml
new file mode 100644
index 0000000..6ab23ac
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ms/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Pemasang pakej"</string>
+    <string name="next" msgid="3057143178373252333">"Seterusnya"</string>
+    <string name="install" msgid="5896438203900042068">"Pasang"</string>
+    <string name="done" msgid="3889387558374211719">"Selesai"</string>
+    <string name="cancel" msgid="8360346460165114585">"Batal"</string>
+    <string name="installing" msgid="8613631001631998372">"Memasang..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Memasang <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplikasi dipasang."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Adakah anda mahu memasang aplikasi ini? Aplikasi ini akan mendapat akses kepada:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Adakah anda mahu memasang aplikasi ini? Aplikasi ini tidak memerlukan sebarang akses khas."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Adakah anda mahu memasang kemas kini kepada aplikasi sedia ada ini? Data sedia ada anda tidak akan hilang. Aplikasi yang dikemaskinikan akan mendapat akses kepada:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Adakah anda ingin memasang kemas kini kepada aplikasi terbina dalam ini? Data sedia ada anda tidak akan hilang. Aplikasi yang dikemaskinikan akan mendapat akses kepada:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Adakah anda mahu memasang kemas kini untuk aplikasi sedia ada ini? Data sedia ada anda tidak akan hilang. Hal ini tidak memerlukan sebarang akses khas."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Adakah anda mahu memasang kemas kini untuk aplikasi terbina dalam ini? Data sedia ada anda tidak akan hilang. Hal ini tidak memerlukan sebarang akses khas."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Aplikasi tidak dipasang."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Pakej ini telah disekat daripada dipasang."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Apl tidak dipasang kerana pakej bercanggah dengan pakej yang sedia ada."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Apl tidak dipasang kerana apl tidak serasi dengan tablet anda."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Apl ini tidak serasi dengan TV anda."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Apl tidak dipasang kerana apl tidak serasi dengan telefon anda."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Apl tidak dipasang kerana pakej tidak sah."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak dapat dipasangkan pada tablet anda."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak boleh dipasang pada TV anda."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak dapat dipasangkan pada telefon anda."</string>
+    <string name="launch" msgid="4826921505917605463">"Buka"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Pentadbir anda tidak membenarkan pemasangan apl yang diperoleh daripada sumber yang tidak diketahui"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Apl yang tidak diketahui tidak boleh dipasang oleh pengguna ini"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Pengguna ini tidak dibenarkan memasang apl"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Urus aplikasi"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Kehabisan ruang"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak dapat dipasang. Kosongkan sebahagian ruang dan cuba lagi."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Aplikasi tidak ditemui"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Aplikasi tidak ditemui dalam senarai aplikasi yang dipasang."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Tidak dibenarkan"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Pengguna semasa tidak dibenarkan untuk melaksanakan penyahpasangan ini."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Ralat"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Apl tidak dapat dinyapasang."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Nyahpasang aplikasi"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Nyahpasang kemas kini"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> merupakan sebahagian daripada aplikasi berikut:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Adakah anda mahu menyahpasang aplikasi ini?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Adakah anda mahu menyahpasang apl ini untuk "<b>"semua"</b>" pengguna? Aplikasi dan datanya akan dialih keluar daripada "<b>"semua"</b>" pengguna pada peranti."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Adakah anda ingin menyahpasang apl ini untuk pengguna <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Gantikan apl ini dengan versi kilang? Semua data akan dialih keluar."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Penyahpasangan yang sedang berjalan"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Penyahpasangan yang gagal"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Menyahpasang..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Menyahpasang <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Nyahpasang selesai."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> dinyahpasang"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Nyahpasang tidak berjaya."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Tidak berjaya menyahpasang <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Tidak dapat menyahpasang apl pentadbir peranti yang aktif"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Tidak dapat menyahpasang apl pentadbir peranti yang aktif untuk <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Apl ini diperlukan untuk sesetengah pengguna atau profil dan telah dinyahpasang untuk yang lain"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Apl ini diperlukan untuk profil anda dan tidak boleh dinyahpasang."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Apl ini diperlukan oleh pentadbir peranti anda dan tidak boleh dinyahpasang."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Urus apl pentadbir peranti"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Urus pengguna"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak dapat dinyahpasang."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Terdapat masalah menghuraikan pakej."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Baharu"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Semua"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privasi"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Akses Peranti"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Kemas kini ini tidak memerlukan kebenaran baharu."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Tolak"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Maklumat lanjut"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Tolak juga"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> daripada <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Sentiasa benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Hanya semasa menggunakan apl"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Sentiasa"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Tolak dan jangan tanya lagi"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> dilumpuhkan"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"semua dilumpuhkan"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"tiada apa-apa yang dilumpuhkan"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Benarkan"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Apl"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Kebenaran apl"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Jangan tanya lagi"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Tiada kebenaran"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Kebenaran tambahan"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Buka maklumat apl"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> lagi</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> lagi</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Apl ini direka bentuk untuk versi Android yang lebih lama. Tindakan menafikan kebenaran boleh menyebabkannya tidak berfungsi seperti yang dimaksudkan lagi."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"laksanakan tindakan yang tidak diketahui"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> daripada <xliff:g id="COUNT_1">%2$d</xliff:g> apl dibenarkan"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Tunjukkan sistem"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Sembunyikan sistem"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Tiada apl"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Tetapan Lokasi"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> ialah pembekal perkhidmatan lokasi untuk peranti ini. Akses lokasi boleh diubah suai daripada tetapan lokasi."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Jika anda tolak kebenaran ini, ciri asas peranti anda mungkin tidak berfungsi seperti yang dimaksudkan lagi."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Dikuatkuasakan oleh dasar"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Akses latar belakang dilumpuhkan oleh dasar"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Akses latar belakang didayakan oleh dasar"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Akses latar depan didayakan oleh dasar"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Dikawal oleh pentadbir"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Sentiasa"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Hanya semasa menggunakan apl"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Jangan sekali-kali"</string>
+    <string name="loading" msgid="7811651799620593731">"Memuatkan…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Semua kebenaran"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Keupayaan apl yang lain"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Permintaan kebenaran"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Tindanan skrin dikesan"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Untuk menukar tetapan kebenaran ini, anda perlu mematikan tindanan skrin daripada Tetapan &gt; Apl terlebih dahulu"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Buka tetapan"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Tindakan pasang/nyahpasang tidak disokong pada Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Pilih perkara yang boleh diakses oleh &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; telah dikemas kini. Pilih perkara yang boleh diakses oleh apl ini."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Batal"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Teruskan"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Kebenaran baharu"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Kebenaran semasa"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Pemeringkatan apl…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Tidak diketahui"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Untuk keselamatan, tablet anda tidak dibenarkan memasang apl yang tidak diketahui daripada sumber ini."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Untuk keselamatan, TV anda tidak dibenarkan memasang apl yang tidak diketahui daripada sumber ini."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Untuk keselamatan, telefon anda tidak dibenarkan memasang apl yang tidak diketahui daripada sumber ini."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Telefon dan data peribadi anda lebih mudah diserang oleh apl yang tidak diketahui. Dengan memasang apl ini, anda bersetuju bahawa anda bertanggungjawab atas sebarang kerosakan pada telefon anda atau kehilangan data yang mungkin disebabkan oleh penggunaan apl tersebut."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Tablet dan data peribadi anda lebih mudah diserang oleh apl yang tidak diketahui. Dengan memasang apl ini, anda bersetuju bahawa anda bertanggungjawab atas sebarang kerosakan pada tablet anda atau kehilangan data yang mungkin disebabkan oleh penggunaan apl tersebut."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"TV dan data peribadi anda lebih mudah diserang oleh apl yang tidak diketahui. Dengan memasang apl ini, anda bersetuju bahawa anda bertanggungjawab atas sebarang kerosakan pada TV anda atau kehilangan data yang mungkin disebabkan oleh penggunaan apl tersebut."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Teruskan"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Tetapan"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Memasang/menyahpasang apl wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-my-television/strings.xml b/packages/PackageInstaller/res/values-my-television/strings.xml
new file mode 100644
index 0000000..b802f59
--- /dev/null
+++ b/packages/PackageInstaller/res/values-my-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"ငြင်းဆိုပြီး ထပ်မံ မမေးပါနှင့်"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"နောင်တွင် ဤသည်အား ဆက်တင်များ &gt; အက်ပ်များတွင် ပြင်နိုင်၏"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"စနစ်အပ်ဖ်များ ပြသရန်"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"အက်ပ်ခွင့်ပြုချက်များ"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"အက်ပ်ခွင့်ပြုချက်များ"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> ခွင့်ပြုချက်များ"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"အပိုဆောင်း ခွင့်ပြုချက်များ"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> ခွင့်ပြုချက်များ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-my-watch/strings.xml b/packages/PackageInstaller/res/values-my-watch/strings.xml
new file mode 100644
index 0000000..21283c0
--- /dev/null
+++ b/packages/PackageInstaller/res/values-my-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"ငြင်းပယ်သည်၊ ထပ်မမေးပါနှင့်"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"စနစ်အပ်ဖ်များ ပြသရန်"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"ပြောင်းလဲ မရနိုင်ပါ"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Yes"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"မလုပ်တော့"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-my/strings.xml b/packages/PackageInstaller/res/values-my/strings.xml
new file mode 100644
index 0000000..02199c3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-my/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Package ထည့်သွင်းခြင်း"</string>
+    <string name="next" msgid="3057143178373252333">"ရှေ့သို့"</string>
+    <string name="install" msgid="5896438203900042068">"ထည့်သွင်းပါ"</string>
+    <string name="done" msgid="3889387558374211719">"ပြီးပါပြီ"</string>
+    <string name="cancel" msgid="8360346460165114585">"မလုပ်တော့"</string>
+    <string name="installing" msgid="8613631001631998372">"ထည့်သွင်းနေပါသည်"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ကို ထည့်သွင်းနေသည်…"</string>
+    <string name="install_done" msgid="3682715442154357097">"အက်ပ်ထည့်သွင်းပြီး"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"ဤအပလီကေးရှင်းကို ထည့်သွင်းပါမလား။ ဤအပလီကေးရှင်း သုံးစွဲခွင့်ရှိမှာ ကတော့:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"ဤအပလီကေးရှင်းကို ထည့်သွင်းပါမလား။ အထူးတလည် သုံးခွင့် မလိုအပ်ပါ"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"လက်ရှိ ရှိပြီးသား အပလီကေးရှင်းကို အပ်ဒိတ်လုပ်လိုပါသလား။ ရှိပြီးသား အချက်အလက်များကို ဆုံးရှုံးမည် မဟုတ်ပါ။ အပ်ဒိတ်လုပ်လိုက်သော အပလီကေးရှင်းသုံးစွဲခွင့်ရှိမှာ များကတော့-"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"အဆင့်သင့် ပါလာသော အပလီကေးရှင်းကို အပ်ဒိတ်လုပ်လိုပါသလား။  ရှိပြီးသား အချက်အလက်များကို ဆုံးရှုံးမည် မဟုတ်ပါ။ အပ်ဒိတ်လုပ်လိုက်သော အပလီကေးရှင်း သုံးစွဲခွင့်ရှိမှာ များကတော့ -"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"လက်ရှိ ရှိပြီးသား အပလီကေးရှင်းကို အပ်ဒိတ်လုပ်လိုပါသလား။  ရှိပြီးသား အချက်အလက်များကို ဆုံးရှုံးမည် မဟုတ်ပါ။ အထူးတလည် သုံးခွင့် မလိုအပ်ပါ"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"အဆင့်သင့် ပါလာသော အပလီကေးရှင်းကို အပ်ဒိတ်လုပ်လိုပါသလား။  ရှိပြီးသား အချက်အလက်များကို ဆုံးရှုံးမည် မဟုတ်ပါ။ အထူးတလည် သုံးခွင့် မလိုအပ်ပါ"</string>
+    <string name="install_failed" msgid="6579998651498970899">"အက်ပ်မထည့်သွင်းရသေးပါ"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"ပက်ကေ့ထည့်သွင်းခြင်းကို ပိတ်ဆို့ထားသည်။"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"ပက်ကေ့ဂျ်အဖြစ် ထည့်သွင်းမထားသော အက်ပ်သည် လက်ရှိပက်ကေ့ဂျ်နှင့် တိုက်နေသည်။"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"အက်ပ်အဖြစ် ထည့်သွင်းမထားသော အက်ပ်သည် သင့်တက်ဘလက်နှင့် ကိုက်ညီမှုမရှိပါ။"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"ဤ အက်ပ်သည် သင့်တီဗွီနှင့် တွဲဖက်သုံးမရပါ။"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"အက်ပ်အဖြစ် ထည့်သွင်းမထားသော အက်ပ်သည် သင့်ဖုန်းနှင့် ကိုက်ညီမှုမရှိပါ။"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"ပက်ကေ့ဂျ်အဖြစ် ထည့်သွင်းမထားသော အက်ပ်သည် မှန်ကန်မှုမရှိပုံပေါ်သည်။"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို သင့်တက်ဘလက်တွင် ထည့်သွင်းလို့ မရနိုင်ပါ"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"သင့်တီဗွီတွင် <xliff:g id="APP_NAME">%1$s</xliff:g> အား မတပ်ဆင်နိုင်ပါ။"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို သင့်ဖုန်းတွင် ထည့်သွင်းလို့ မရနိုင်ပါ"</string>
+    <string name="launch" msgid="4826921505917605463">"ဖွင့်သည်"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"သင်၏ စီမံခန့်ခွဲသူက ရင်းမြစ်မသိသော အက်ပ်များကို ထည့်သွင်းခွင့်မပြုပါ"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"အရင်းအမြစ်မသိသော အက်ပ်များကို ဤအသုံးပြုသူက ထည့်သွင်းခွင့်မရှိပါ"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"ဤအသုံးပြုသူသည် အက်ပ်များကို ထည့်သွင်းခွင့်မရှိပါ"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"အပလီကေးရှင်းများအား ထိန်းသိမ်းခြင်း"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"နေရာလွတ်မရှိပါ"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ထည့်သွင်းလို့ မရနိုင်ပါ။ နေရာအပိုရအောင် ရှင်းလင်းပြီး ပြန်ကြိုးစားပါ"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"အက်ပ်အားမတွေ့ပါ"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"ထည့်သွင်းထားသော အပလီကေးရှင်းထဲတွင် ဤအပလီကေးရှင်း မတွေ့ရှိပါ"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"ခွင့်ပြုမထားပါ"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"ဤဖယ်ရှားမှုပြုလုပ်ရန် လက်ရှိအသုံးပြုသူအား ခွင့်ပြုမထားပါ။"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"အမှားအယွင်း"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"အက်ပ်ကို ဖယ်ရှား၍မရနိုင်ပါ။"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"အပလီကေးရှင်းကို ဖယ်ရှားပါ"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"အပ်ဒိတ်လုပ်ထားခြင်းကို ပြန်ထုတ်ပါ"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ကတော့ အောက်ပါ အက်ပ်၏အစိတ်အပိုင်း တစ်ခု ဖြစ်ပါသည်:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"ဤအပလီကေးရှင်းကို သင်ဖယ်ရှားချင်ပါသလား"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"ဤအပလီကေးရှင်းကို အသုံးပြုသူ"<b>" အားလုံး"</b>" အတွက် ဖယ်ရှားချင်ပါသလား? ဤအပလီကေးရှင်း နှင့် သက်ဆိုင်ရာ အချက်အလက်များ အားလုံးကို "<b>" မှ  အားလုံးသော "</b>" စက်အသုံးပြုသူတွေအတွက် ဖယ်ရှားပစ်ပါလိမ့်မည်"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"သင်သည် အသုံးပြုသူ <xliff:g id="USERNAME">%1$s</xliff:g> အတွက် ဒီအကောင့်ကို ဖြုတ်ပစ်လိုပါသလား?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"ဤအက်ပ်ကို စက်ရုံထုတ်ဗားရှင်းဖြင့် အစားထိုးမလား။ ဒေတာများအားလုံးကို ဖယ်ရှားလိုက်ပါမည်။"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"ဤအက်ပ်ကို စက်ရုံထုတ်ဗားရှင်းဖြင့် အစားထိုးမလား။ ဒေတာများအားလုံးကို ဖယ်ရှားလိုက်ပါမည်။ ၎င်းသည် အလုပ်ပရိုဖိုင်ဖြင့်သုံးသူများအပါအဝင် အသုံးပြုသူများအားလုံးကို အကျိုးသက်ရောက်စေပါလိမ့်မည်။"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"ပရိုဂရမ်ကို ဖယ်ရှားနေပါသည်"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"ပရိုဂရမ်ကို ဖယ်ရှားခြင်းမအောင်မြင်ပါ"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"ဖယ်ထုတ်သည်"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ကို ဖယ်ရှားနေပါသည်…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"ဖယ်ရှားခြင်း ပြီးပါပြီ"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ကို ဖယ်ရှားလိုက်ပါပြီ"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"ဖယ်ရှားမှု မအောင်မြင်ပါ"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ကို ဖယ်ရှားခြင်း မအောင်မြင်ပါ။"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"ဖွင့်ထားသော စက်ပစ္စည်းကို စီမံခန့်ခွဲရန်အက်ပ်အား ဖယ်ရှား၍မရပါ"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> အတွက် ဖွင့်ထားသော စက်ပစ္စည်းကို စီမံခန့်ခွဲရန် အက်ပ်အား ဖယ်ရှား၍မရပါ။"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"အချို့အသုံးပြုသူများ သို့မဟုတ် ပရိုဖိုင်များအတွက် ဤအက်ပ်ကို လိုအပ်သော်လည်း အချို့သူများအတွက် ဖြုတ်ထားပါသည်"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"သင့်ပရိုဖိုင်အတွက် ဤအက်ပ်ကိုလိုအပ်ပြီး ဖြုတ်၍မရပါ။"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"ဒီအက်ပ်မှာ သင်၏ ကိရိယာ စီမံအုပ်ချုပ်သူက လိုအပ်သောကြောင့် ဖြုတ်၍ မရနိုင်ပါ။"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"စက်ပစ္စည်းကို စီမံခန့်ခွဲရန် အက်ပ်များအား စီမံရန်"</string>
+    <string name="manage_users" msgid="3125018886835668847">"အသုံးပြုသူများအား စီမံခန့်ခွဲပါ"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ဖယ်ရှားလို့ မရပါ"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"ဒေတာအချက်အလက်အစုအားဖတ်ရှုစဉ် ပြသနာ တစ်ခု ဖြစ်ပေါ်ပါသည်"</string>
+    <string name="newPerms" msgid="6039428254474104210">"အသစ်"</string>
+    <string name="allPerms" msgid="1024385515840703981">"အားလုံး"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"လုံခြုံမှု"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"စက်ပစ္စည်း အသုံးပြုခွင့်"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"ဤအပ်ဒိတ်အတွက် ခွင့်ပြုချက်အသစ် မလိုအပ်ပါ"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"ငြင်းပယ်သည်"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"အခြားအချက်အလက်များ"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"မည်သို့ပင်ဖြစ်စေ ငြင်းပယ်ပါ"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို <xliff:g id="ACTION">%2$s</xliff:g> ရန်ခွင့်ပြုမလား။"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို <xliff:g id="ACTION">%2$s</xliff:g> ရန် အမြဲခွင့်ပြုသလား။"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"အက်ပ်အသုံးပြုစဉ်သာ"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"အမြဲတမ်း"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"ငြင်းဆိုသည်၊ ထပ်မမေးပါနှင့်"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> ခု ပိတ်ထားသည်"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"အားလုံးပိတ်ထားသည်"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"တစ်ခုမျှ ပိတ်မထားပါ"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"ခွင့်ပြုသည်"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"အက်ပ်များ"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"အက်ပ်ခွင့်ပြုချက်များ"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"နောက်ထပ်မမေးပါနှင့်"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"ခွင့်ပြုချက်မရှိ"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"ထပ်တိုး ခွင့်ပြုချက်များ"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"အက်ပ်အချက်အလက် ဖွင့်ရန်"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"> နောက်ထပ် <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one"> နောက်ထပ် <xliff:g id="COUNT_0">%1$d</xliff:g> </item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"ဤအက်ပ် အား Android ၏ ဗားရှင်းဟောင်းအတွက် ပုံဆွဲရေးဆွဲထား၏။ ခွင့်ပြုချက်ပေးရန် ငြင်းဆိုပါက ရည်ရွယ်ထားသကဲ့သို့ ဆောင်ရွက်လိမ့်မည် မဟုတ်ပါ။"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"အမျိုးအမည်မသိ ဆောင်ရွက်ချက်တစ်ခု လုပ်ရန်"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"အက်ပ် <xliff:g id="COUNT_1">%2$d</xliff:g> မှ <xliff:g id="COUNT_0">%1$d</xliff:g> ခု ခွင့်ပြုသည်"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"စနစ်ကိုပြသရန်"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"စနစ်ကို ဖျောက်မည်"</string>
+    <string name="no_apps" msgid="1965493419005012569">"အက်ပ် မရှိပါ"</string>
+    <string name="location_settings" msgid="1774875730854491297">"တည်နေရာ ဆက်တင်များ"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် ဤစက်ပစ္စည်းအတွက် တည်နေရာ ဝန်ဆောင်မှုများ ထုတ်ပေးသူဖြစ်သည်။ တည်နေရာ အသုံးပြုမှုကို တည်နေရာချိန်ညှိမှုများတွင် ပြုပြင်နိုင်သည်။"</string>
+    <string name="system_warning" msgid="7103819124542305179">"ဤခွင့်ပြုချက်အား သင် ငြင်းဆိုပါက၊ သင့်စက်ကိရိယာ၏ အခြေခံလုပ်ဆောင်ချက်များသည် ရည်ရွယ်ထားသကဲ့သို့ အလုပ်လုပ်မည် မဟုတ်ပါ။"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"မူဝါဒအားဖြင့်ပြဌာန်းရန်"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"နောက်ခံတွင်ဝင်သုံးခွင့်ကို မူဝါဒက ပိတ်ထားသည်"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"နောက်ခံတွင်ဝင်သုံးခွင့်ကို မူဝါဒက ဖွင့်ထားသည်"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"မျက်နှာစာတွင်ဝင်သုံးခွင့်ကို မူဝါဒက ဖွင့်ထားသည်"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"စီမံခန့်ခွဲသူက ထိန်းချုပ်ထားသည်"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"အမြဲတမ်း"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"အက်ပ်အသုံးပြုစဉ်သာ"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"ဘယ်တော့မှ"</string>
+    <string name="loading" msgid="7811651799620593731">"တင်နေ…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"ခွင့်ပြုချက်များ အားလုံး"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"အခြားအပ်ဖ်၏ စွမ်းရည်များ"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"ခွင့်ပြုချက် တောင်းခံမှု"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"မျက်နှာပြင် ထပ်ပေးမှုကို ရှာတွေ့ခဲ့"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"ဒီခွင့်ပြုချက် ဆက်တင်ကို ပြောင်းရန်၊ သင်ဟာ ဦးစွာ ဆက်တင်များ &gt; အက်ပ်များ ထဲတွင် မျက်နှာပြင် ထပ်ပေးမှုကို ပိတ်လိုက်ရန် လိုမယ်"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"ဆက်တင်းများ ဖွင့်ရန်"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear ပေါ်တွင် ထည့်သွင်းခြင်း/ဖြုတ်ခြင်းများကို ပံ့ပိုးမထားပါ။"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&amp;It;b7gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&amp;It;/b&gt; က အသုံးပြုခွင့်ရမည့် အရာတို့ကို ရွေးပါ"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&amp;It;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&amp;It;/b&gt; ကို အပ်ဒိတ်လုပ်ပြီးပါပြီ။ ဤအက်ပ်က အသုံးပြုခွင့်ရမည့်အရာတို့ကို ရွေးပါ။"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"မလုပ်တော့"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"ဆက်လုပ်ရန်"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"ခွင့်ပြုချက် အသစ်များ"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"လက်ရှိ ခွင့်ပြုချက်များ"</string>
+    <string name="message_staging" msgid="6151794817691100003">"အက်ပ်ကို ပြင်ဆင်နေသည်…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"အမျိုးအမည်မသိ"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"လုံခြုံရေးအရ ဤနေရာမှရယူထားသည့် အမျိုးအမည်မသိသောအက်ပ်များကို သင်၏တက်ဘလက်တွင် ထည့်သွင်းခွင့်မရှိပါ။"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"လုံခြုံရေးအရ ဤနေရာမှရယူထားသည့် အမျိုးအမည်မသိသောအက်ပ်များကို သင်၏တီဗီတွင် ထည့်သွင်းခွင့်မရှိပါ။"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"လုံခြုံရေးအရ ဤနေရာမှရယူထားသည့် အမျိုးအမည်မသိသောအက်ပ်များကို သင်၏ဖုန်းတွင် ထည့်သွင်းခွင့်မရှိပါ။"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"သင်၏ဖုန်းနှင့် ကိုယ်ရေးကိုယ်တာ အချက်အလက်များသည် အမျိုးအမည် မသိသောအက်ပ်များ၏ တိုက်ခိုက်ခြင်းကို ပိုမိုခံရနိုင်ပါသည်။ ဤအက်ပ်ကို ထည့်သွင်းအသုံးပြုခြင်းအားဖြင့် ဖြစ်ပေါ်လာနိုင်သော ဖုန်းပျက်စီးမှု သို့မဟုတ် ဒေတာဆုံးရှုံးမှုများအတွက် သင့်ထံ၌သာ တာဝန်ရှိကြောင်း သဘောတူရာရောက်ပါသည်။"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"သင်၏ တက်ဘလက်နှင့် ကိုယ်ရေးကိုယ်တာ အချက်အလက်များသည် အမျိုးအမည် မသိသောအက်ပ်များ၏ တိုက်ခိုက်ခြင်းကို ပိုမိုခံရနိုင်ပါသည်။ ဤအက်ပ်ကို ထည့်သွင်းအသုံးပြုခြင်းအားဖြင့် ဖြစ်ပေါ်လာနိုင်သော တက်ဘလက်ပျက်စီးမှု သို့မဟုတ် ဒေတာဆုံးရှုံးမှုများအတွက် သင့်ထံ၌သာ တာဝန်ရှိကြောင်း သဘောတူရာရောက်ပါသည်။"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"သင်၏ TV နှင့် ကိုယ်ရေးကိုယ်တာ အချက်အလက်များသည် အမျိုးအမည် မသိသောအက်ပ်များ၏ တိုက်ခိုက်ခြင်းကို ပိုမိုခံရနိုင်ပါသည်။ ဤအက်ပ်ကို ထည့်သွင်းအသုံးပြုခြင်းအားဖြင့် ဖြစ်ပေါ်လာနိုင်သော TV ပျက်စီးမှု သို့မဟုတ် ဒေတာဆုံးရှုံးမှုများအတွက် သင့်ထံ၌သာ တာဝန်ရှိကြောင်း သဘောတူရာရောက်ပါသည်။"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"ဆက်လုပ်ရန်"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"ဆက်တင်များ"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"wear အက်ပ်ကိုထည့်သွင်းခြင်း/ဖယ်ရှားခြင်း"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-nb-television/strings.xml b/packages/PackageInstaller/res/values-nb-television/strings.xml
new file mode 100644
index 0000000..57f6a21
--- /dev/null
+++ b/packages/PackageInstaller/res/values-nb-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Avvis, og ikke spør igjen"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Du kan endre dette senere i Innstillinger &gt; Apper"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Vis systemapper"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Apptillatelser"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Apptillatelser"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Tillatelser for <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Flere tillatelser"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Tillatelser for <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-nb-watch/strings.xml b/packages/PackageInstaller/res/values-nb-watch/strings.xml
new file mode 100644
index 0000000..332a8fe
--- /dev/null
+++ b/packages/PackageInstaller/res/values-nb-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Avvis, ikke spør igjen"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Vis systemapper"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Kan ikke endres"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Ja"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Avbryt"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-nb/strings.xml b/packages/PackageInstaller/res/values-nb/strings.xml
new file mode 100644
index 0000000..3ff3de4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-nb/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Pakkeinstallasjon"</string>
+    <string name="next" msgid="3057143178373252333">"Neste"</string>
+    <string name="install" msgid="5896438203900042068">"Installer"</string>
+    <string name="done" msgid="3889387558374211719">"Ferdig"</string>
+    <string name="cancel" msgid="8360346460165114585">"Avbryt"</string>
+    <string name="installing" msgid="8613631001631998372">"Installerer…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Installerer <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> …"</string>
+    <string name="install_done" msgid="3682715442154357097">"Appen er installert."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Ønsker du å installere denne appen? Den får tilgang til følgende:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Ønsker du å installere denne appen? Den krever ingen spesiell tilgang."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Ønsker du å installere en oppdatering for denne eksisterende appen? Du mister ingen eksisterende data. Den oppdaterte appen får tilgangen spesifisert nedenfor."</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Ønsker du å installere en oppdatering for denne innebygde appen? Du mister ingen eksisterende data. Den oppdaterte appen får tilgangen spesifisert nedenfor."</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Vil du installere en oppdatering av denne eksisterende appen? De eksisterende dataene dine går ikke tapt. Dette krever ingen spesiell tilgang."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Vil du installere en oppdatering av denne innebygde appen? De eksisterende dataene dine går ikke tapt. Dette krever ingen spesiell tilgang."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Appen ble ikke installert."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Pakken er blokkert fra å bli installert."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Appen ble ikke installert fordi pakken er i konflikt med en eksisterende pakke."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Appen ble ikke installert fordi appen ikke er kompatibel med nettbrettet ditt."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Denne appen er ikke kompatibel med TV-en din."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Appen ble ikke installert fordi appen ikke er kompatibel med telefonen din."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Appen ble ikke installert fordi pakken ser ut til å være ugyldig."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> kunne ikke installeres på nettbrettet ditt."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> kunne ikke installeres på TV-en."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> kunne ikke installeres på telefonen din."</string>
+    <string name="launch" msgid="4826921505917605463">"Åpne"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Administratoren din tillater ikke installering av apper som er hentet fra ukjente kilder"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Ukjente apper kan ikke installeres av denne brukeren"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Brukeren har ikke tillatelse til å installere apper"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Administrer apper"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Tom for plass"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> kunne ikke installeres. Frigjør plass og prøv på nytt."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Appen ble ikke funnet"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Finner ikke appen i listen over installerte apper."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Ikke tillatt"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Denne brukeren har ikke tillatelse til å utføre denne avinstalleringen."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Feil"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Kunne ikke avinstallere appen."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Avinstaller appen"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Avinstaller oppdateringen"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> er del av følgende app:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Vil du avinstallere denne appen?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Vil du avinstallere denne appen for "<b>"alle"</b>" brukere? Appen og tilhørende data blir fjernet fra "<b>"alle"</b>" brukere på enheten."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Ønsker du å avinstallere denne appen for brukeren <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Vil du erstatte denne appen med den opprinnelige versjonen? Alle dataene fjernes."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Avinstalleringer som er i gang"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Mislykkede avinstalleringer"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Avinstallerer…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Avinstallerer <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> …"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Avinstalleringen er  fullført."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Avinstallerte <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Avinstalleringen mislyktes."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Kunne ikke avinstallere <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Kan ikke avinstallere den aktive appen for enhetsadministrator"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Kan ikke avinstallere den aktive appen for enhetsadministrator for <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Appen er nødvendig for noen brukere eller profiler, og den er avinstallert for andre"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Denne appen er nødvendig for profilen din og kan ikke avinstalleres."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Denne appen kreves av enhetsadministratoren din og kan ikke avinstalleres."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Administrer apper for enhetsadministrator"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Administrer brukere"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> kunne ikke installeres."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Det oppsto et problem med analysen av pakken."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nye"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Alle"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Personvern"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Enhetstilgang"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Denne oppdateringen krever ingen nye tillatelser."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Ikke tillat"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Finn ut mer"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Avvis likevel"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> av <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tillatelse til å <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Vil du alltid tillate at &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; kan <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Bare når appen brukes"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Alltid"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Avvis, og ikke spør igjen"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> er slått av"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"alt er slått av"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ingen er slått av"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Tillat"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Apper"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Apptillatelser"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Ikke spør igjen"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Ingen tillatelser"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Flere tillatelser"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Åpne info om appen"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> til</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> til</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Denne appen er designet for en eldre versjon av Android. Hvis du nekter å gi tillatelse, kan det føre til at den ikke lenger fungerer etter hensikten."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"utfør en ukjent handling"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> av <xliff:g id="COUNT_1">%2$d</xliff:g> apper er tillatt"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Vis systemapper"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Skjul systemet"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Ingen apper"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Posisjonsinnstillinger"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> er en leverandør av posisjonstjenester for denne enheten. Tilgang til posisjon kan endres fra posisjonsinnstillingene."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Hvis du ikke gir denne tillatelsen, kan grunnleggende funksjoner på enheten slutte å fungere som de skal."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Påkrevd ifølge retningslinjene"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Bakgrunnstilgang er slått av pga. retningslinjene"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Bakgrunnstilgang er slått på pga. retningslinjene"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Forgrunnstilgang er slått på pga. retningslinjene"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Kontrollert av administratoren"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Alltid"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Bare når appen brukes"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Aldri"</string>
+    <string name="loading" msgid="7811651799620593731">"Laster inn …"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Alle tillatelser"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Andre appfunksjoner"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Forespørsel om tillatelse"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Skjermoverlegg oppdaget"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"For å endre denne tillatelsesinnstilingen må du først slå av skjermoverlegget fra Innstillinger &gt; Apper"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Åpne innstillingene"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Handlinger for å installere og avinstallere er ikke støttet på Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Velg hva du vil gi <xliff:g id="APP_NAME">%1$s</xliff:g> tilgang til"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"<xliff:g id="APP_NAME">%1$s</xliff:g> er oppdatert. Velg hva du vil gi denne appen tilgang til."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Avbryt"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Fortsett"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nye tillatelser"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Gjeldende tillatelser"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Setter opp appen …"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Ukjent"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Nettbrettet ditt har ikke tillatelse til å installere ukjente apper fra denne kilden, for å ivareta sikkerheten din."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"TV-en din har ikke tillatelse til å installere ukjente apper fra denne kilden, for å ivareta sikkerheten din."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Telefonen din har ikke tillatelse til å installere ukjente apper fra denne kilden, for å ivareta sikkerheten din."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Telefonen din og de personlige dataene dine er mer sårbare for angrep fra ukjente apper. Når du installerer denne appen, samtykker du i at du er ansvarlig for eventuelle skader på telefonen eller tap av data bruk av appen forårsaker."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Nettbrettet ditt og de personlige dataene dine er mer sårbare for angrep fra ukjente apper. Når du installerer denne appen, samtykker du i at du er ansvarlig for eventuelle skader på nettbrettet eller tap av data bruk av appen forårsaker."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"TV-en din og de personlige dataene dine er mer sårbare for angrep fra ukjente apper. Når du installerer denne appen, samtykker du i at du er ansvarlig for eventuelle skader på TV-en eller tap av data bruk av appen forårsaker."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Fortsett"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Innstillinger"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Installerer/avinstallerer wear-apper"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ne-television/strings.xml b/packages/PackageInstaller/res/values-ne-television/strings.xml
new file mode 100644
index 0000000..d6e908c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ne-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"अस्वीकृत गर्नुहोस् र फेरि नसोध्नुहोस्"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"तपाईं यसलाई पछि सेटिङ &gt; अनुप्रयोगमा बदल्न सक्नु हुन्छ"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"प्रणाली अनुप्रयोगहरू देखाउनुहोस्"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"अनुप्रयोग सम्बन्धी अनुमतिहरू"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"अनुप्रयोग सम्बन्धी अनुमतिहरू"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> सम्बन्धी अनुमतिहरू"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"अतिरिक्त अनुमतिहरू"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> सम्बन्धी अनुमतिहरू"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ne-watch/strings.xml b/packages/PackageInstaller/res/values-ne-watch/strings.xml
new file mode 100644
index 0000000..ffcc2f7
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ne-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"अस्वीकार गर्नुहोस्, फेरि नसोध्नुहोस्"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"प्रणाली अनुप्रयोगहरू देखाउनुहोस्"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"परिवर्तन गर्न सकिँदैन"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"हो"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"रद्द गर्नुहोस्"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ne/strings.xml b/packages/PackageInstaller/res/values-ne/strings.xml
new file mode 100644
index 0000000..b2dea8f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ne/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"प्याकेज स्थापनकर्ता"</string>
+    <string name="next" msgid="3057143178373252333">"अर्को"</string>
+    <string name="install" msgid="5896438203900042068">"स्थापना गर्नुहोस्"</string>
+    <string name="done" msgid="3889387558374211719">"भयो"</string>
+    <string name="cancel" msgid="8360346460165114585">"रद्द गर्नुहोस्"</string>
+    <string name="installing" msgid="8613631001631998372">"स्थापित हुँदै…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> स्थापना गर्दै…"</string>
+    <string name="install_done" msgid="3682715442154357097">"अनुप्रयोग स्थापना भयो।"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"के तपाईं यो अनुप्रयोग स्थापन गर्न चाहनु हुन्छ? यसले पहुँच प्राप्त गर्ने छ:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"के तपाईं यस अनुप्रयोगलाई जडान गर्न चाहनु हुन्छ? यसको लागि कुनै विशेष पहुँचको आवश्यकता पर्दैन।"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"के तपाईँसँग अहिले भईरहेको अनुप्रयोगको एक अपडेट लाई स्थापित गर्न चाहानुहुन्छ? तपाईँको अहिलेको डेटा हराउने छैन। अपडेट भएको अनुप्रयोग पहुँच पाउनेछ मा:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"के तपाईं यस पूर्व-निर्मित अनुप्रयोगमा अपडेट स्थापित गर्न चाहनु हुन्छ? तपाईंको रहेको डेटा हराउने छैन। अपडेट गरिएको अनुप्रयोगले पहुँच पाउने छ:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"के तपाईँसँग अहिले भइरहेको अनुप्रयोगको एउटा अपडेटलाई स्थापित गर्न चाहनु हुन्छ? तपाईँको अहिलेको डेटा हराउने छैन। यसलाई कुनै विशेष पहुँचको आवश्यकता छैन।"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"के तपाईं यस जोडिएको अनुप्रयोगको एउटा अपडेटलाई स्थापित गर्न चाहनुहुन्छ? तपाईंको अहिलेको डेटा हराउने छैन। यसलाई कुनै विशेष पहुँचको आवश्यकता छैन।"</string>
+    <string name="install_failed" msgid="6579998651498970899">"अनुप्रयोग स्थापना भएन।"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"यो प्याकेज स्थापना हुनबाट अवरुद्ध भएको थियो।"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"प्याकेजका रूपमा स्थापना नगरिएको अनुप्रयोग विद्यमान प्याकेजसँग मेल खाँदैन।"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"अनुप्रयोगका रूपमा स्थापना नगरिएको अनुप्रयोग तपाईंको ट्याब्लेटसँग मिल्दो छैन।"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"यो अनुप्रयोग तपाईँको TV को लागि उपयुक्त छैन।"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"अनुप्रयोगका रूपमा स्थापना नगरिएको अनुप्रयोग तपाईंको फोनसँग मिल्दो छैन।"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"प्याकेजका रूपमा स्थापना नगरिएको अनुप्रयोग अमान्य जस्तो देखिन्छ।"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> तपाईँको ट्याब्लेटमा स्थापित हुन सकेन।"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"तपाईँको TVमा<xliff:g id="APP_NAME">%1$s</xliff:g>स्थापना गर्न सकिएन।"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"तपाईँको फोनमा <xliff:g id="APP_NAME">%1$s</xliff:g> जडान हुन सकेन।"</string>
+    <string name="launch" msgid="4826921505917605463">"खोल्नुहोस्"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"तपाईंका प्रशासकले अज्ञात स्रोतहरूबाट प्राप्त अनुप्रयोगहरूलाई स्थापना गर्ने अनुमति दिनुहुन्न"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"यस प्रयोगकर्ताले अज्ञात अनुप्रयोगहरू स्थापना गर्न सक्नुहुन्न"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"यो प्रयोगकर्तालाई अनुप्रयोगहरूको स्थापना गर्ने अनुमति छैन"</string>
+    <string name="ok" msgid="3468756155452870475">"ठिक छ"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"अनुप्रयोगहरूको व्यवस्थापन गर्नुहोस्"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"ठाउँभन्दा बाहिर"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> स्थापन गर्न सकिएन। केही ठाउँ खाली गर्नुहोस् र फेरि कोसिस गर्नुहोस्"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"अनुप्रयोग फेला परेन"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"स्थापना भएको अनुप्रयोगहरू सूचीमा अनुप्रयोग फेला परेको थिएन।"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"अनुमति छैन"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"हालको प्रयोगकर्तालाई यो स्थापना रद्द गर्ने कार्य गर्ने अनुमति छैन।"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"त्रुटि"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"अनुप्रयोगको स्थापना रद्द गर्न सकिएन।"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"अनुप्रयोग अस्थापना गर्नुहोस्"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"परिस्कारहरू अस्थापना गर्नुहोस्"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> निम्न अनुप्रयोगको अंश हो:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"के तपाईं यो अनुप्रयोग अस्थापना गर्न चाहनु हुन्छ?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"के तपाईं यो अनुप्रयोग "<b>"सबै"</b>" प्रयोगकर्ताहरूको लागि स्थापना रद्द गर्न चाहनु हुन्छ? अनुप्रयोग र यसको डेटा "<b>"सबै"</b>" प्रयोगकर्ताहरूबाट उपकरणमा हटाइने छ।"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"के तपाईं प्रयोगकर्ता <xliff:g id="USERNAME">%1$s</xliff:g> को लागि यो अनुप्रयोग स्थापना रद्द गर्न चाहनुहुन्छ?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"यस अनुप्रयोगलाई फ्याक्ट्रीको संस्करणले बदल्ने हो? सबै डेटा हटाइनेछ।"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"यस अनुप्रयोगलाई फ्याक्ट्रीको संस्करणले बदल्ने हो? सबै डेटा हटाइनेछ। यसले यस यन्त्रका कार्य प्रोफाइल भएका लगायत सबै प्रयोगकर्ताहरूमा असर पार्छ।"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"चलिरहेका स्थापना रद्द गर्ने कार्यहरू"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"असफल भएका स्थापना रद्द गर्ने कार्यहरू"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"अस्थापना गर्दै..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> को स्थापना रद्द गर्दै…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"स्थापना रद्द गर्न सकियो।"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> को स्थापना रद्द गरियो"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"अस्थापना गर्न असफल"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> को स्थापना रद्द गर्ने कार्य असफल भयो।"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"यन्त्रको सक्रिय प्रशासकीय अनुप्रयोगको स्थापना रद्द गर्न मिल्दैन"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> को यन्त्रको सक्रिय प्रशासकीय अनुप्रयोगको स्थापना रद्द गर्न मिल्दैन"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"अन्य प्रयोगकर्ताहरूका लागि यस अनु्प्रयोगको स्थापना रद्द गरे पनि केही प्रयोगकर्ता वा प्रोफाइलहरूलाई यसको आवश्यकता पर्दछ"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"यो अनुप्रयोग तपाईँको प्रोफाइलका लागि आवश्यक छ र यसको स्थापनालाई रद्द गर्न सकिँदैन।"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"यो अनुप्रयोग तपाईँको उपकरण प्रशासकलाई आवश्यक छ र स्थापना रद्द गर्न सकिँदैन।"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"यन्त्रका प्रशासकीय अनुप्रयोगहरूको व्यवस्थापन गर्नुहोस्"</string>
+    <string name="manage_users" msgid="3125018886835668847">"प्रयोगकर्ताहरूलाई व्यवस्थापन गर्नुहोस्"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> स्थापना रद्द गर्न सकिँदैन।"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"प्याकेजलाई पार्सिङ गर्दा एउटा समस्या आयो।"</string>
+    <string name="newPerms" msgid="6039428254474104210">"नयाँ"</string>
+    <string name="allPerms" msgid="1024385515840703981">"सबै"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"गोपनीयता"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"उपकरण पहुँच"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"यस अपडेटलाई नयाँ अनुमति आवश्यक पर्दैन।"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"अस्वीकार गर्नुहोस्"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"थप जानकारी"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"जे भए पनि अस्वीकार गर्नुहोस्"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> को <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई <xliff:g id="ACTION">%2$s</xliff:g> गर्न अनुमति दिने हो?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई सधैँ <xliff:g id="ACTION">%2$s</xliff:g> गर्ने अनुमति दिने हो?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"अनुप्रयोग प्रयोग गर्दा मात्र"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"सधैँ"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"अस्वीकार गरी फेरि नसोध्नुहोस्"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> अनुमतिहरूलाई असक्षम पारिएको छ"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"सबै अनुमतिहरूलाई असक्षम पारिएको छ"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"कुनै पनि अनुमतिलाई असक्षम पारिएको छैन"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"अनुमति दिनुहोस्"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"अनुप्रयोगहरू"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"अनुप्रयोग अनुमतिहरू"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"फेरि नसोध्नुहोस्"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"कुनै अनुमतिहरू छैनन्"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"अतिरिक्त अनुमतिहरू"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"अनुप्रयोगसम्बन्धी जानकारी खोल्नुहोस्"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> थप</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> थप</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"यो अनुप्रयोग Android को पुरानो संस्करणका लागि डिजाइन गरिएको थियो। अनुमति अस्वीकृत गर्नाले यसले चाहिएको जस्तो कार्य नगर्न सक्छ।"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"एउटा अज्ञात कार्य गर्नुहोस्"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g> को <xliff:g id="COUNT_0">%1$d</xliff:g> अनुप्रयोगहरूलाई अनुमति छ"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"प्रणाली देखाउनुहोस्"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"प्रणाली लुकाउनुहोस्"</string>
+    <string name="no_apps" msgid="1965493419005012569">"कुनै अनुप्रयोगहरू छैनन्।"</string>
+    <string name="location_settings" msgid="1774875730854491297">"स्थान सेटिङहरू"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> यो यन्त्रका लागि स्थान सेवाहरूको एउटा प्रदायक हो। स्थान पहुँच स्थान सेटिङहरूबाट परिमार्जन गर्न सकिन्छ।"</string>
+    <string name="system_warning" msgid="7103819124542305179">"तपाईँले यो अनुमति अस्वीकार गर्नुभयो भने तपाईँको यन्त्रका मूल विशेषताहरू अब चाहेअनुसार कार्य नगर्न सक्छ।"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"नीतिद्वारा लागू गरियो"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"नीतिले पृष्ठभूमिको प‍हुँचलाई असक्षम पारेको छ"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"नीतिले पृष्ठभूमिको प‍हुँचलाई सक्षम पारेको छ"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"नीतिले अग्रभूमिको पहुँचलाई सक्षम पारेको छ"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"प्रशासकले नियन्त्रण गर्नुभएको"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"सधैँ"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"अनुप्रयोग प्रयोग गर्दा"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"कहिल्यै होइन"</string>
+    <string name="loading" msgid="7811651799620593731">"लोड हुँदैछ..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"सबै अनुमतिहरू"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"अन्य अनुप्रयोग क्षमताहरू"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"अनुमति अनुरोध"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"स्क्रिन ओभरले पत्ता लाग्यो"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"यो अनुमति सेटिङ परिवर्तन गर्न, तपाईँले पहिला सेटिङ अनुप्रयोगबाट स्क्रिन ओभरले बन्द गर्नु पर्दछ।"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"सेटिङहरू खोल्नुहोस्"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear मा स्थापना/स्थापना रद्द गर्ने कारबाहीहरू समर्थित छैनन्।"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई के माथि पहुँच राख्न दिने भन्ने कुरा छनौट गर्नुहोस्"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई अद्यावधिक गरिएको छ। यस अनुप्रयोगलाई के माथि पहुँच राख्न दिने भन्ने कुरा छनौट गर्नुहोस्।"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"रद्द गर्नुहोस्"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"जारी राख्नुहोस्"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"नयाँ अनुमतिहरू"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"वर्तमान अनुमतिहरू"</string>
+    <string name="message_staging" msgid="6151794817691100003">"अनुप्रयोगलाई तयार पार्दै…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"अज्ञात"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"तपाईंको सुरक्षाको लागि, तपाईंको ट्याब्लेटलाई यो स्रोतबाट प्राप्त हुने अज्ञात अनुप्रयोगहरू स्थापना गर्ने अनुमति छैन।"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"तपाईंको सुरक्षाको लागि, तपाईंको TV लाई यो स्रोतबाट प्राप्त हुने अज्ञात अनुप्रयोगहरू स्थापना गर्ने अनुमति छैन।"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"तपाईंको सुरक्षाको लागि, तपाईंको फोनलाई यो स्रोतबाट प्राप्त हुने अज्ञात अनुप्रयोगहरू स्थापना गर्ने अनुमति छैन।"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"तपाईंको फोन र व्यक्तिगत डेटा अज्ञात अनुप्रयोगहरूबाट हुने आक्रमणको चपेटामा पर्ने बढी जोखिममा हुन्छन्। यो अनुप्रयोग स्थापना गरेर तपाईं यसको प्रयोगबाट तपाईंको फोनलाई हुनसक्ने क्षति वा डेटाको नोक्सानीका लागि स्वयं जिम्मेवार हुनुहुन्छ भन्ने कुरामा सहमत हुनुहुन्छ।"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"तपाईंको ट्याब्लेट र व्यक्तिगत डेटा अज्ञात अनुप्रयोगहरूबाट हुने आक्रमणको चपेटामा पर्ने बढी जोखिममा हुन्छन्। यो अनुप्रयोग स्थापना गरेर तपाईं यसको प्रयोगबाट तपाईंको ट्याब्लेटलाई हुनसक्ने क्षति वा डेटाको नोक्सानीका लागि स्वयं जिम्मेवार हुनुहुन्छ भन्ने कुरामा सहमत हुनुहुन्छ।"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"तपाईंको TV र व्यक्तिगत डेटा अज्ञात अनुप्रयोगहरूबाट हुने आक्रमणको चपेटामा पर्ने बढी जोखिममा हुन्छन्। यो अनुप्रयोग स्थापना गरेर तपाईं यसको प्रयोगबाट तपाईंको TV लाई हुनसक्ने क्षति वा डेटाको नोक्सानीका लागि स्वयं जिम्मेवार हुनुहुन्छ भन्ने कुरामा सहमत हुनुहुन्छ।"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"जारी राख्नुहोस्"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"सेटिङहरू"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"वेयर एपहरूको स्थापना/स्थापना रद्द गर्दै"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-nl-television/strings.xml b/packages/PackageInstaller/res/values-nl-television/strings.xml
new file mode 100644
index 0000000..c7256e8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-nl-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Weigeren en niet meer vragen"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"U kunt dit later wijzigen in Instellingen &gt; Apps"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Systeem weergeven"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"App-machtigingen"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"App-machtigingen"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Machtigingen voor <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Aanvullende machtigingen"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Machtigingen voor <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-nl-watch/strings.xml b/packages/PackageInstaller/res/values-nl-watch/strings.xml
new file mode 100644
index 0000000..338b79b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-nl-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Weigeren, niet meer vragen"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Systeem weergeven"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Niet aanpasbaar"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Ja"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Annuleren"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml
new file mode 100644
index 0000000..a9e8940
--- /dev/null
+++ b/packages/PackageInstaller/res/values-nl/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Pakket-installatie"</string>
+    <string name="next" msgid="3057143178373252333">"Volgende"</string>
+    <string name="install" msgid="5896438203900042068">"Installeren"</string>
+    <string name="done" msgid="3889387558374211719">"Gereed"</string>
+    <string name="cancel" msgid="8360346460165114585">"Annuleren"</string>
+    <string name="installing" msgid="8613631001631998372">"Installeren..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> installeren…"</string>
+    <string name="install_done" msgid="3682715442154357097">"App geïnstalleerd."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Wil je deze app installeren? Deze krijgt toegang tot:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Wil je deze app installeren? Hiervoor is geen speciale toegang vereist."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Wil je een update voor deze bestaande app installeren? Je huidige gegevens gaan niet verloren. De bijgewerkte app krijgt toegang tot:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Wil je een update van deze ingebouwde app installeren? Je huidige gegevens gaan niet verloren. De bijgewerkte app krijgt toegang tot:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Wil je een update voor deze bestaande app installeren? Je huidige gegevens gaan niet verloren. Hiervoor is geen speciale toegang vereist."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Wil je een update voor deze ingebouwde app installeren? Je huidige gegevens gaan niet verloren. Hiervoor is geen speciale toegang vereist."</string>
+    <string name="install_failed" msgid="6579998651498970899">"App niet geïnstalleerd."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"De installatie van het pakket is geblokkeerd."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"App die niet is geïnstalleerd als pakket conflicteert met een bestaand pakket."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"App die niet is geïnstalleerd als app is niet geschikt voor je tablet."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Deze app is niet compatibel met je tv."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"App die niet is geïnstalleerd als app is niet geschikt voor je telefoon."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"App die niet is geïnstalleerd als pakket lijkt ongeldig te zijn."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> kan niet worden geïnstalleerd op je tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> kan niet worden geïnstalleerd op je tv."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> kan niet worden geïnstalleerd op je telefoon."</string>
+    <string name="launch" msgid="4826921505917605463">"Openen"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Je beheerder staat de installatie van apps afkomstig van onbekende bronnen niet toe"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Onbekende apps kunnen niet worden geïnstalleerd door deze gebruiker"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Deze gebruiker mag geen apps installeren"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Apps beheren"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Geen ruimte beschikbaar"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> kan niet worden geïnstalleerd. Maak ruimte vrij en probeer het opnieuw."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"App niet gevonden"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"De app is niet gevonden in de lijst met geïnstalleerde apps."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Niet toegestaan"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"De huidige gebruiker mag deze verwijdering niet uitvoeren."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Fout"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"App kan niet worden verwijderd."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"App verwijderen"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Update verwijderen"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> maakt deel uit van de volgende app:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Wil je deze app verwijderen?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Wil je deze app verwijderen voor "<b>"alle"</b>" gebruikers? Deze app en de gegevens ervan worden verwijderd voor "<b>"alle"</b>" gebruikers van het apparaat."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Wil je deze app verwijderen voor de gebruiker <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Deze app vervangen door de fabrieksversie? Alle gegevens worden verwijderd."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Actieve verwijderingen"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Mislukte verwijderingen"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Verwijderen..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> verwijderen…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Verwijdering voltooid."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> verwijderd"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Verwijdering mislukt."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kan niet worden verwijderd."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Kan actieve apparaatbeheer-app niet verwijderen"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Kan actieve apparaatbeheer-app niet verwijderen voor <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Deze app is vereist voor sommige gebruikers of profielen en is verwijderd voor andere"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Deze app is vereist voor je profiel en kan niet worden verwijderd."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Deze app is vereist door je apparaatbeheerder en kan niet worden verwijderd."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Apparaatbeheer-apps beheren"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Gebruikers beheren"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> kan niet worden verwijderd."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Er is een probleem opgetreden bij het parseren van het pakket."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nieuw"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Alle"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privacy"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Apparaattoegang"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Voor deze update zijn geen nieuwe machtigingen vereist."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Weigeren"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Meer informatie"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Toch weigeren"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> van <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; het volgende toestaan: <xliff:g id="ACTION">%2$s</xliff:g>."</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; altijd toestaan om <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Alleen als app in gebruik is"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Altijd"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Weigeren en niet meer vragen"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> uitgeschakeld"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"alle rechten uitgeschakeld"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"geen rechten uitgeschakeld"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Toestaan"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Apps"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"App-machtigingen"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Niet meer vragen"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Geen machtigingen"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Aanvullende machtigingen"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"App-info openen"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">Nog <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Nog <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Deze app is ontworpen voor een oudere versie van Android. Als u geen toestemming geeft, kan de app mogelijk niet functioneren zoals is bedoeld."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"een onbekende actie uitvoeren"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Verleend aan <xliff:g id="COUNT_0">%1$d</xliff:g> van <xliff:g id="COUNT_1">%2$d</xliff:g> apps"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Systeem weergeven"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Systeem-apps verbergen"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Geen apps"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Locatie-instellingen"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> is een leverancier van locatieservices voor dit apparaat. Locatietoegang kan worden aangepast via de locatie-instellingen."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Als je deze machtiging weigert, kan het zijn dat basisfuncties van je apparaat niet meer werken zoals bedoeld."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Afgedwongen door beleid"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Toegang op de achtergrond uitgeschakeld op basis van beleid"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Toegang op de achtergrond ingeschakeld op basis van beleid"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Toegang op de voorgrond ingeschakeld op basis van beleid"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Ingesteld door beheerder"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Altijd"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Alleen als app in gebruik is"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nooit"</string>
+    <string name="loading" msgid="7811651799620593731">"Laden…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Alle machtigingen"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Andere app-mogelijkheden"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Toestemmingsverzoek"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Schermoverlay gedetecteerd"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Als u deze instelling voor rechten wilt wijzigen, moet u eerst de schermoverlay uitschakelen via \'Instellingen\' &gt; \'Apps\'"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Instellingen openen"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Acties voor installeren/verwijderen niet ondersteund op Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Kiezen waartoe &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang krijgt"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; is geüpdatet. Kies waartoe je deze app toegang wilt geven."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Annuleren"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Doorgaan"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nieuwe machtigingen"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Huidige machtigingen"</string>
+    <string name="message_staging" msgid="6151794817691100003">"App uitvoeren…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Onbekend"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Uit veiligheidsoverwegingen heeft je tablet geen toestemming om onbekende apps van deze bron te installeren."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Uit veiligheidsoverwegingen heeft je tv geen toestemming om onbekende apps van deze bron te installeren."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Uit veiligheidsoverwegingen heeft je telefoon geen toestemming om onbekende apps van deze bron te installeren."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Je telefoon en persoonlijke gegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Als je deze app installeert, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je telefoon of gegevensverlies als gevolg van het gebruik van de app."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Je tablet en persoonlijke gegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Als je deze app installeert, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je tablet of gegevensverlies als gevolg van het gebruik van de app."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Je tv en persoonlijke gegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Als je deze app installeert, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je tv of gegevensverlies als gevolg van het gebruik van de app."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Doorgaan"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Instellingen"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear-apps installeren/verwijderen"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-notround-watch/dimens.xml b/packages/PackageInstaller/res/values-notround-watch/dimens.xml
new file mode 100644
index 0000000..55d6248
--- /dev/null
+++ b/packages/PackageInstaller/res/values-notround-watch/dimens.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+<resources>
+    <!-- Dimens for dialog layouts -->
+    <dimen name="diag_button_size">44dp</dimen>
+    <dimen name="diag_preferred_padding">8dp</dimen>
+    <dimen name="diag_button_padding_horizontal">16dp</dimen>
+    <dimen name="diag_button_padding_bottom">8dp</dimen>
+    <dimen name="diag_icon_margin_top">8dp</dimen>
+</resources>
diff --git a/packages/PackageInstaller/res/values-or-television/strings.xml b/packages/PackageInstaller/res/values-or-television/strings.xml
new file mode 100644
index 0000000..157b922
--- /dev/null
+++ b/packages/PackageInstaller/res/values-or-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"ମନାକରନ୍ତୁ ଏବଂ ପୁଣି ପଚାରନ୍ତୁ ନାହିଁ"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"ସେଟିଙ୍ଗ ଓ ଆପ୍‌ରେ ଏହାକୁ ଆପଣ ପରେ ବଦଳାଇପାରିବେ"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"ସିଷ୍ଟମ୍‍ ଆପ୍‍ ଦେଖାନ୍ତୁ"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"ଆପ୍‌ର ଅନୁମତି"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"ଆପ୍‌ର ଅନୁମତି"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> ଅନୁମତି"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"ଅତିରିକ୍ତ ଅନୁମତି"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> ଅନୁମତି"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-or-watch/strings.xml b/packages/PackageInstaller/res/values-or-watch/strings.xml
new file mode 100644
index 0000000..b7719a1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-or-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"ମନାକରନ୍ତୁ, ପୁଣି ପଚାରନ୍ତୁ ନାହିଁ"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"ସିଷ୍ଟମ୍‍ ଆପ୍‍ ଦେଖାନ୍ତୁ"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"ବଦଳାଯାଇପାରିବ ନାହିଁ"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"ହଁ"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"କ୍ୟାନ୍ସଲ୍‍ କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-or/strings.xml b/packages/PackageInstaller/res/values-or/strings.xml
new file mode 100644
index 0000000..8d93c6f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-or/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"ପ୍ୟାକେଜ୍‌ ଇନଷ୍ଟଲର୍‍"</string>
+    <string name="next" msgid="3057143178373252333">"ପରବର୍ତ୍ତୀ"</string>
+    <string name="install" msgid="5896438203900042068">"ଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ"</string>
+    <string name="done" msgid="3889387558374211719">"ହୋଇଗଲା"</string>
+    <string name="cancel" msgid="8360346460165114585">"କ୍ୟାନ୍ସଲ୍‍ କରନ୍ତୁ"</string>
+    <string name="installing" msgid="8613631001631998372">"ଇନଷ୍ଟଲ୍ କରାଯାଉଛି…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ଇନଷ୍ଟଲ୍‌ କରାଯାଉଛି…"</string>
+    <string name="install_done" msgid="3682715442154357097">"ଆପ୍‍ ଇନଷ୍ଟଲ୍‌ ହୋଇଗଲା।"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"ଏହି ଆପ୍ଲିକେଶନ୍‍ ଆପଣ ଇନଷ୍ଟଲ୍‍ କରିବେ କି? ଏହିଗୁଡ଼ିକୁ ଏହା ଆକ୍ସେସ୍‌ କରିପାରିବ:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"ଏହି ଆପ୍ଲିକେଶନ୍‍ ଆପଣ ଇନଷ୍ଟଲ୍‍ କରିବେ କି? ଏହା କୌଣସି ବିଶେଷ ପ୍ରକାରର ଆକ୍ସେସ୍‌ ଆବଶ୍ୟକ କରେନାହିଁ।"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"ପୂର୍ବରୁ ଥିବା ଏହି ଆପ୍ଲିକେଶନ୍‍ରେ ଆପଣ ଏକ ଅପଡେଟ୍‍ ଇନଷ୍ଟଲ୍‍ କରିବେ କି? ଏଥିରେ ଥିବା ଆପଣଙ୍କ ଡାଟା ନଷ୍ଟ ହେବନାହିଁ। ଅପଡେଟ୍‍ ହେବାପରେ ଆପ୍ଲିକେଶନ୍‍‍ଟି ଏହିଗୁଡ଼ିକୁ ଆକ୍ସେସ୍‌ କରିପାରିବ:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"ଏହି ବିଲ୍ଟ-ଇନ୍‌ ଆପ୍ଲିକେଶନ୍‍ରେ ଆପଣ ଏକ ଅପଡେଟ୍‍ ଇନଷ୍ଟଲ୍‍ କରିବେ କି? ଏଥିରେ ଥିବା ଆପଣଙ୍କ ଡାଟା ନଷ୍ଟ ହେବନାହିଁ। ଅପଡେଟ୍‍ ହେବାପରେ ଆପ୍ଲିକେଶନ୍‍‍ଟି ଏହିଗୁଡ଼ିକୁ ଆକ୍ସେସ୍‌ କରିପାରିବ:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"ଆପଣ ବର୍ତ୍ତମାନର ଏହି ଆପ୍ଲିକେଶନ୍‍ର ଏକ ଅପଡେଟ୍‌ ଇନଷ୍ଟଲ୍‍ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି? ଆପଣଙ୍କ ବର୍ତ୍ତମାନର ଡାଟା ନଷ୍ଟ ହେବନାହିଁ। ଏଥିରେ କୌଣସି ବିଶେଷ ଆକ୍ସେସ୍‍ର ଆବଶ୍ୟକତା ନାହିଁ।"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"ଆପଣ ବର୍ତ୍ତମାନର ଏହି ବିଲ୍ଟ-ଇନ୍‍ ଆପ୍ଲିକେଶନ୍‍ର ଏକ ଅପଡେଟ୍‌ ଇନଷ୍ଟଲ୍‍ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି? ଆପଣଙ୍କ ବର୍ତ୍ତମାନର ଡାଟା ନଷ୍ଟ ହେବନାହିଁ। ଏଥିରେ କୌଣସି ବିଶେଷ ଆକ୍ସେସ୍‍ର ଆବଶ୍ୟକତା ନାହିଁ।"</string>
+    <string name="install_failed" msgid="6579998651498970899">"ଆପ୍‍ ଇନଷ୍ଟଲ୍‌ ହୋଇନାହିଁ।"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"ଏହି ପ୍ୟାକେଜ୍‌କୁ ଇନଷ୍ଟଲ୍‍ କରାଯିବାରେ ଅବରୋଧ କରାଯାଇଥିଲା।"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"ପୂର୍ବରୁ ଥିବା ପ୍ୟାକେଜ୍‍ ସହ ଏହି ପ୍ୟାକେଜ୍‌ର ସମସ୍ୟା ଉପୁଯିବାରୁ ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ ହୋଇପାରିଲା ନାହିଁ।"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"ଆପ୍‍ ଆପଣଙ୍କ ଟାବଲେଟ୍‌ ସହ କମ୍ପାଟିବଲ୍‍ ନଥିବା ହେତୁ ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ ହୋଇପାରିଲା ନାହିଁ।"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"ଏହି ଆପ୍‍, ଆପଣଙ୍କ ଟିଭି ସହ କମ୍ପାଟିବଲ୍‍ ନୁହେଁ।"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"ଆପ୍‍ ଆପଣଙ୍କ ଫୋନ୍‌ ସହ କମ୍ପାଟିବଲ୍‍ ନଥିବା ହେତୁ ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ ହୋଇପାରିଲା ନାହିଁ।"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"ପ୍ୟାକେଜ୍‍ ଅମାନ୍ୟ ଥିବା ପରି ଜଣାପଡ଼ିବାରୁ ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ ହୋଇପାରିଲା ନାହିଁ।"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g>ଟି ଆପଣଙ୍କ ଟାବଲେଟ୍‍ରେ ଇନଷ୍ଟଲ୍‌ କରାଯାଇପାରିଲା ନାହିଁ।"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କ ଟିଭିରେ ଇନଷ୍ଟଲ୍‍ କରାଯାଇପାରିଲା ନାହିଁ।"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g>ଟି ଆପଣଙ୍କ ଫୋନ୍‍ରେ ଇନଷ୍ଟଲ୍‌ କରାଯାଇପାରିଲା ନାହିଁ।"</string>
+    <string name="launch" msgid="4826921505917605463">"ଖୋଲନ୍ତୁ"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"ଅଜଣା ସୋର୍ସରୁ ଆସିଥିବା ଆପଗୁଡ଼ିକ ଇନଷ୍ଟଲ୍‌ କରିବାକୁ ଆପଣଙ୍କ ଆଡମିନ୍‍ ଅନୁମତି ଦିଅନ୍ତି ନାହିଁ"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"ଏହି ୟୁଜରଙ୍କ ଦ୍ୱାରା ଅଜଣା ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ କରାଯାଇ ପାରିବ ନାହିଁ"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"ଏହି ୟୁଜର୍‌ ଜଣକ ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ କରିପାରିବେ ନାହିଁ"</string>
+    <string name="ok" msgid="3468756155452870475">"ଠିକ୍‍ ଅଛି"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"ଆପ୍‌ଗୁଡ଼ିକର ପରିଚାଳନା କରନ୍ତୁ"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"ଆଉ ସ୍ଥାନ ନାହିଁ"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଇନଷ୍ଟଲ୍‌ କରାଯାଇପାରିଲା ନାହିଁ। କିଛି ସ୍ଥାନ ଖାଲିକରି ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"ଆପ୍‍ ମିଳିଲା ନାହିଁ"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"ଇନଷ୍ଟଲ୍‌ କରାଯାଇଥିବା ଆପ୍‍ ତାଲିକାରେ ଆପ୍‍ଟି ମିଳିଲା ନାହିଁ।"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"ଅନୁମତି ଦିଆଯାଇନାହିଁ"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"ଏହି ଅନଇନଷ୍ଟଲେଶନ୍‍ କରିବାକୁ ବର୍ତ୍ତମାନର ୟୁଜରଙ୍କୁ ଅନୁମତି ଦିଆଯାଇନାହିଁ।"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"ତ୍ରୁଟି"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"ଆପ୍‍ ଅନଇନଷ୍ଟଲ୍‍ କରାହେଲା ନାହିଁ"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"ଆପ୍‍ ଅନଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"ଅପଡେଟ୍‍ ଅନଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ହେଉଛି ନିମ୍ନ ଆପ୍‍ର ଏକ ଅଂଶ।"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"ଆପଣ ଏହି ଆପ୍‍ ଅନଇନଷ୍ଟଲ୍‌ କରିବାକୁ ଚାହାଁନ୍ତି କି?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"ଆପଣ "<b>"ସମସ୍ତ"</b>" ୟୁଜର୍‌ଙ୍କ ପାଇଁ ଏହି ଆପ୍‌କୁ ଅନଷ୍ଟଲ୍‍ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି? ଡିଭାଇସ୍‌ରେ ଥିବା "<b>"ସମସ୍ତ"</b>" ୟୁଜର୍‌ ଆପ୍ଲିକେଶନ୍‍ ଏବଂ ତାହାର ଡାଟା ବାହାର କରିଦିଆଯିବ।"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"ୟୁଜର୍‌ <xliff:g id="USERNAME">%1$s</xliff:g>ଙ୍କ ପାଇଁ ଆପଣ ଏହି ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ କରିବେ କି?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"ଏହି ଆପ୍‍ ଫ୍ୟାକ୍ଟୋରୀ ଭର୍ସନ୍‍‍ ସହ ବଦଳାଇବେ? ସମସ୍ତ ଡାଟା କାଢ଼ିଦିଆଯିବ।"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"ଏହି ଆପ୍‍ ଫ୍ୟାକ୍ଟୋରୀ ଭର୍ସନ୍‍‍ ସହ ବଦଳାଇବେ? ସମସ୍ତ ଡାଟା ବାହାର କରିଦିଆଯିବ। ୱର୍କ ପ୍ରୋଫାଇଲ୍‍ ଥିବା ସମେତ, ଏହାଦ୍ୱାରା ଡିଭାଇସରେ ଥିବା ସମସ୍ତ ୟୁଜର୍‌ ପ୍ରଭାବିତ ହେବେ।"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"ଅନଇନଷ୍ଟଲ୍‌ ଚାଲୁଛି"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"ଅନଇନଷ୍ଟଲ୍‌ କରାଯାଇ ପାରିଲା ନାହିଁ"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"ଅନଇନଷ୍ଟଲ୍‌ କରାଯାଉଛି…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ଅନଇନଷ୍ଟଲ୍‍ କରାଯାଉଛି…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"ଅନଇନଷ୍ଟଲ୍‌ ହୋଇଗଲା।"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ଅନଇନଷ୍ଟଲ୍‌ କରାଗଲା"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"ଅନଇନଷ୍ଟଲ୍‌ କରାଯାଇପାରିଲା ନାହିଁ।"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ଅନଇନଷ୍ଟଲ୍‍ କରିବା ସଫଳ ହେଲାନାହିଁ।"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"ସକ୍ରିୟ ଡିଭାଇସ୍‍ ଆପ୍‍ ଅନଇନଷ୍ଟଲ୍‌ କରିପାରିବ ନାହିଁ"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g>ଙ୍କ ପାଇଁ ସକ୍ରିୟ ଡିଭାଇସ୍‍ ଆଡମିନ୍‍ ଆପ୍‍ ଅନଇନଷ୍ଟଲ୍‍ କରାଯାଇ ପାରିବ ନାହିଁ"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"କିଛି ୟୁଜର୍‌ କିମ୍ବା ପ୍ରୋଫାଇଲ୍‍ ପାଇଁ ଏହି ଆପ୍‍ ଆବଶ୍ୟକ ଏବଂ ଅନ୍ୟ ସମସ୍ତଙ୍କ ପାଇଁ ଅନଇନଷ୍ଟଲ୍‍ କରାଯାଇଥିଲା"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"ଏହି ଆପ୍‍ ଆପଣଙ୍କ ପ୍ରୋଫାଇଲ୍‍ ପାଇଁ ଆବଶ୍ୟକ ଅଟେ ଏବଂ ଅନଇନଷ୍ଟଲ୍‍ କରାଯାଇପାରିବ ନାହିଁ।"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"ଆପଣଙ୍କ ଡିଭାଇସ୍‍ ଆଡମିନିଷ୍ଟ୍ରେଟର୍‍ ଏହି ଆପ୍‍ ଆବଶ୍ୟକ କରନ୍ତୁ ଏବଂ ଏହାକୁ ଅନ୍‍ଇନଷ୍ଟଲ୍‍ କରାଯାଇପାରିବ ନାହିଁ।"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"ଡିଭାଇସ୍‌ ଆଡମିନ୍‌ ଆପ୍‌ ପରିଚାଳନା କରନ୍ତୁ"</string>
+    <string name="manage_users" msgid="3125018886835668847">"ୟୁଜର୍‌ଙ୍କୁ ପରିଚାଳନା କରନ୍ତୁ"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଅନଇନଷ୍ଟଲ୍‍ କରାଯାଇପାରିଲା ନାହିଁ।"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"ଏହି ପ୍ୟାକେଜ୍‍ ପାର୍ସ କରିବାରେ ସମସ୍ୟା ଥିଲା।"</string>
+    <string name="newPerms" msgid="6039428254474104210">"ନୂଆ"</string>
+    <string name="allPerms" msgid="1024385515840703981">"ସମସ୍ତ"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"ଗୋପନୀୟତା"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"ଡିଭାଇସ୍ ଆକ୍ସେସ୍‌"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"ଏହି ଅପଡେଟ୍‍ କୌଣସି ନୂଆ ଅନୁମତି ଆବଶ୍ୟକ କରେନାହିଁ"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"ପ୍ରତ୍ୟାଖ୍ୟାନ"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"ଅଧିକ ସୂଚନା"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"ତଥାପି ଖାରଜ କରନ୍ତୁ"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>ଟିରୁ <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>ଟି"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ <xliff:g id="ACTION">%2$s</xliff:g> ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ସବୁବେଳେ <xliff:g id="ACTION">%2$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"କେବଳ ଆପ୍‍ ବ୍ୟବହାର କରୁଥିବା ସମୟରେ"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"ସର୍ବଦା"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"ମନାକରନ୍ତୁ ଏବଂ ପୁଣି ପଚାରନ୍ତୁ ନାହିଁ"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g>ଟି ଅକ୍ଷମ କରାଯାଇଛି"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"ସମସ୍ତ ଅକ୍ଷମ କରାଯାଇଛି"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"କିଛିବି ଅକ୍ଷମ କରାଯାଇନାହିଁ"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"ଆପ୍‌‍"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"ଆପ୍‌ ଅନୁମତି"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"ପୁଣି ପଚାରନ୍ତୁ ନାହିଁ"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"କୌଣସି ଅନୁମତି ନାହିଁ"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"ଅତିରିକ୍ତ ଅନୁମତି"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"ଆପ୍‌ର ତଥ୍ୟ ଖୋଲନ୍ତୁ"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>ଟି ଅଧିକ</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g>ଟି ଅଧିକ</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"ଏହି ଆପ୍‍ Androidର ଏକ ପୁରୁଣା ସଂସ୍କରଣ ପାଇଁ ଡିଜାଇନ୍‍ କରାଯାଇଥିଲା। ଅନୁମତି ପ୍ରତ୍ୟାଖ୍ୟାନ କରିବା ଦ୍ୱାରା ଏହା ଠିକ୍‍ ଭାବେ ଆଉ କାମ ନକରିପାରେ।"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"ଏକ ଅଜଣା କାମ କରେ"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g>ଟି ଆପରୁ <xliff:g id="COUNT_0">%1$d</xliff:g>ଟିକୁ ଅନୁମତି ଦିଆଯାଇଛି"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"ସିଷ୍ଟମ୍‌କୁ ଦେଖାନ୍ତୁ"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"ସିଷ୍ଟମ୍‌କୁ ଲୁଚାନ୍ତୁ"</string>
+    <string name="no_apps" msgid="1965493419005012569">"କୌଣସି ଆପ୍‌ ନାହିଁ"</string>
+    <string name="location_settings" msgid="1774875730854491297">"ଲୋକେଶନ୍‌ ସେଟିଙ୍ଗ"</string>
+    <string name="location_warning" msgid="8778701356292735971">"ଏହି ଡିଭାଇସ୍‍ ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଲୋକେଶନ୍‍ ସେବା ପ୍ରଦାନ କରେ। ଲୋକେଶନ୍‍ ସେଟିଙ୍ଗରୁ ଲୋକେଶନ୍‍ ଆକ୍ସେସ୍‍ ସଂଶୋଧନ କରାଯାଇପାରିବ।"</string>
+    <string name="system_warning" msgid="7103819124542305179">"ଏହି ଅନୁମତିକୁ ଯଦି ଆପଣ ପ୍ରତ୍ୟାଖ୍ୟାନ କରନ୍ତି, ଆପଣଙ୍କ ଡିଭାଇସ୍‌ର ସାଧାରଣ ସୁବିଧାଗୁଡ଼ିକ ଆଉ କାମ ନକରିପାରେ।"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"ପଲିସୀ ଦ୍ୱାରା ଲାଗୁ"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"ନିତୀ ଅନୁସାରେ ବ୍ୟାକ୍‌ଗ୍ରାଉଣ୍ଡ ଆକ୍ସେସ୍‌ ଅକ୍ଷମ କରାଯାଇଛି"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"ନିତୀ ଅନୁସାରେ ପୃଷ୍ଠପଟ ଆକ୍ସେସ୍‌ ସକ୍ଷମ କରାଯାଇଛି"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"ନିତୀ ଅନୁସାରେ ସମ୍ମୁଖଭାଗ ଆକ୍ସେସ୍‌ ସକ୍ଷମ କରାଯାଇଛି"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"ଆଡ୍‌ମିନ୍‌ ଦ୍ୱାରା ନିୟନ୍ତ୍ରିତ"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"ସର୍ବଦା"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"କେବଳ ଆପ୍‍ ବ୍ୟବହାର କରୁଥିବା ସମୟରେ"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"କେବେ ନୁହେଁ"</string>
+    <string name="loading" msgid="7811651799620593731">"ଲୋଡ୍ କରାଯାଉଛି…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"ସମସ୍ତ ଅନୁମତି"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"ଅନ୍ୟାନ୍ୟ ଆପ୍‍ ଦକ୍ଷତା"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"ଅନୁମତି ଅନୁରୋଧ"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"ସ୍କ୍ରୀନ୍‍ ଓଭର୍‌ଲେ ଚିହ୍ନଟ ହୋଇଛି"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"ଏହି ଅନୁମତି ସେଟିଙ୍ଗ ବଦଳାଇବାକୁ, ପ୍ରଥମେ ଆପଣ ସେଟିଙ୍ଗ ଓ ଆପ୍‌ରୁ ସ୍କ୍ରୀନ୍‍ ଅଫ୍‍ କରନ୍ତୁ"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"ସେଟିଙ୍ଗ ଖୋଲନ୍ତୁ"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android ୱିୟର୍‍"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wearରେ ଇନଷ୍ଟଲ୍‍/ଅନଇନଷ୍ଟଲ୍‍ କାର୍ଯ୍ୟ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ଆକ୍ସେସ୍‍ କରିବା ପାଇଁ କ\'ଣ କ\'ଣ ଅନୁମତି ଦିଆଯିବ, ତାହା ବାଛନ୍ତୁ"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ଅପଡେଟ୍‍ କରାଯାଇଛି। ଏହି ଆପ୍‍ କ\'ଣ କ\'ଣ ଆକ୍ସେସ୍‍ କରିପାରିବ, ତାହା ବାଛନ୍ତୁ।"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"କ୍ୟାନ୍ସଲ୍‍ କରନ୍ତୁ"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"ଜାରି ରଖନ୍ତୁ"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"ନୂଆ ଅନୁମତି"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"ବର୍ତ୍ତମାନର ଅନୁମତି"</string>
+    <string name="message_staging" msgid="6151794817691100003">"ଆପ୍‍ ପର୍ଯ୍ୟାୟଭୁକ୍ତ କରାଯାଉଛି…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"ଅଜଣା"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"ଆପଣଙ୍କ ସୁରକ୍ଷା ପାଇଁ, ଆପଣଙ୍କ ଟାବଲେଟକୁ ଏହି ସୋର୍ସରୁ ଆସିଥିବା ଅଜଣା ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ କରିବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ।"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"ଆପଣଙ୍କ ସୁରକ୍ଷା ପାଇଁ, ଆପଣଙ୍କ ଟିଭିକୁ ଏହି ସୋର୍ସରୁ ଆସିଥିବା ଅଜଣା ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ କରିବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ।"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"ଆପଣଙ୍କ ସୁରକ୍ଷା ପାଇଁ, ଆପଣଙ୍କ ଫୋନକୁ ଏହି ସୋର୍ସରୁ ଆସିଥିବା ଅଜଣା ଆପ୍‍ ଇନଷ୍ଟଲ୍‍ କରିବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ।"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"ଅଜଣା ଆପ୍‌ ଦ୍ୱାରା ଆପଣଙ୍କ ଫୋନ୍‍ ଏବଂ ବ୍ୟକ୍ତିଗତ ଡାଟାକୁ ନଷ୍ଟ କରାଯାଇ ପାରିବାର ସମ୍ଭାବନା ବହୁତ ଅଧିକ। ଏହି ଆପଜୁ ଇନଷ୍ଟଲ୍‌ କରିବାର ଅର୍ଥ, ଆପଣଙ୍କ ଫୋନରେ ଘଟିବା କୌଣସି ପ୍ରକାର କ୍ଷତି କିମ୍ବା ସେଗୁଡ଼ିକର ବ୍ୟବହାରରୁ ହେବା କୌଣସି ପ୍ରକାର ଡାଟାର ହାନୀ ପାଇଁ ଆପଣ ଦାୟୀ ରହିବାକୁ ରାଜି ହୁଅନ୍ତି।"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"ଅଜଣା ଆପ୍‌ ଦ୍ୱାରା ଆପଣଙ୍କ ଟାବଲେଟ୍‍ ଏବଂ ବ୍ୟକ୍ତିଗତ ଡାଟାକୁ ନଷ୍ଟ କରାଯାଇ ପାରିବାର ସମ୍ଭାବନା ବହୁତ ଅଧିକ। ଏହି ଆପଜୁ ଇନଷ୍ଟଲ୍‌ କରିବାର ଅର୍ଥ, ଆପଣଙ୍କ ଟାବଲେଟରେ ଘଟିବା କୌଣସି ପ୍ରକାର କ୍ଷତି କିମ୍ବା ସେଗୁଡ଼ିକର ବ୍ୟବହାରରୁ ହେବା କୌଣସି ପ୍ରକାର ଡାଟାର ହାନୀ ପାଇଁ ଆପଣ ଦାୟୀ ରହିବାକୁ ରାଜି ହୁଅନ୍ତି।"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"ଅଜଣା ଆପ୍‌ ଦ୍ୱାରା ଆପଣଙ୍କ ଟିଭି ଏବଂ ବ୍ୟକ୍ତିଗତ ଡାଟାକୁ ନଷ୍ଟ କରାଯାଇ ପାରିବାର ସମ୍ଭାବନା ବହୁତ ଅଧିକ। ଏହି ଆପଜୁ ଇନଷ୍ଟଲ୍‌ କରିବାର ଅର୍ଥ, ଆପଣଙ୍କ ଟିଭିରେ ଘଟିବା କୌଣସି ପ୍ରକାର କ୍ଷତି କିମ୍ବା ସେଗୁଡ଼ିକର ବ୍ୟବହାରରୁ ହେବା କୌଣସି ପ୍ରକାର ଡାଟାର ହାନୀ ପାଇଁ ଆପଣ ଦାୟୀ ରହିବାକୁ ରାଜି ହୁଅନ୍ତି।"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"ଜାରି ରଖନ୍ତୁ"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"ସେଟିଙ୍ଗ"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"ୱେର୍‍ ଆପ୍‍ ଇନଷ୍ଟଲ୍‌/ଅନଇନଷ୍ଟଲ୍‍ କରାଯାଉଛି"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pa-television/strings.xml b/packages/PackageInstaller/res/values-pa-television/strings.xml
new file mode 100644
index 0000000..76d249c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pa-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"ਅਸਵੀਕਾਰ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਨਾ ਪੁੱਛੋ"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"ਤੁਸੀਂ ਇਸਨੂੰ ਬਾਅਦ ਵਿੱਚ ਸੈਟਿੰਗਾਂ &gt; ਐਪਾਂ ਵਿੱਚ ਬਦਲ ਸਕਦੇ ਹੋ"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"ਸਿਸਟਮ ਐਪਸ ਦਿਖਾਓ"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"ਐਪ ਇਜਾਜ਼ਤਾਂ"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"ਐਪ ਇਜਾਜ਼ਤਾਂ"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> ਇਜਾਜ਼ਤਾਂ"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"ਵਧੀਕ ਇਜਾਜ਼ਤਾਂ"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> ਇਜਾਜ਼ਤਾਂ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pa-watch/strings.xml b/packages/PackageInstaller/res/values-pa-watch/strings.xml
new file mode 100644
index 0000000..e093021
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pa-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"ਅਸਵੀਕਾਰ ਕਰੋ, ਦੁਬਾਰਾ ਨਾ ਪੁੱਛੋ"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"ਸਿਸਟਮ ਐਪਸ ਦਿਖਾਓ"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"ਹਾਂ"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"ਰੱਦ ਕਰੋ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml
new file mode 100644
index 0000000..b364c78
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pa/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"ਪੈਕੇਜ ਸਥਾਪਨਾਕਾਰ"</string>
+    <string name="next" msgid="3057143178373252333">"ਅੱਗੇ"</string>
+    <string name="install" msgid="5896438203900042068">"ਸਥਾਪਤ ਕਰੋ"</string>
+    <string name="done" msgid="3889387558374211719">"ਹੋ ਗਿਆ"</string>
+    <string name="cancel" msgid="8360346460165114585">"ਰੱਦ ਕਰੋ"</string>
+    <string name="installing" msgid="8613631001631998372">"ਇੰਸਟੌਲ ਕਰ ਰਿਹਾ ਹੈ…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ਨੂੰ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
+    <string name="install_done" msgid="3682715442154357097">"ਐਪ ਇੰਸਟੌਲ ਕੀਤਾ।"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"ਕੀ ਤੁਸੀਂ ਇਹ ਐਪਲੀਕੇਸ਼ਨ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਇਹ ਇਸ ਤੱਕ ਪਹੁੰਚ ਪ੍ਰਾਪਤ ਕਰੇਗਾ:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਇਸ ਲਈ ਕਿਸੇ ਖਾਸ ਪਹੁੰਚ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ।"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"ਕੀ ਤੁਸੀਂ ਇਸ ਮੌਜੂਦਾ ਐਪਲੀਕੇਸ਼ਨ ਤੇ ਇੱਕ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਡਾਟਾ ਨਸ਼ਟ ਕੀਤਾ ਜਾਏਗਾ। ਅੱਪਡੇਟ ਕੀਤੀ ਐਪਲੀਕੇਸ਼ਨ ਇਸ ਤੱਕ ਪਹੁੰਚ ਪ੍ਰਾਪਤ ਕਰੇਗੀ:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"ਕੀ ਤੁਸੀਂ ਇਸ ਬਿਲਟ-ਇਨ ਐਪਲੀਕੇਸ਼ਨ ਤੇ ਇੱਕ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਡਾਟਾ ਨਸ਼ਟ ਕੀਤਾ ਜਾਏਗਾ। ਅੱਪਡੇਟ ਕੀਤੀ ਐਪਲੀਕੇਸ਼ਨ ਇਸ ਤੱਕ ਪਹੁੰਚ ਪ੍ਰਾਪਤ ਕਰੇਗੀ:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"ਕੀ ਤੁਸੀਂ ਇਸ ਮੌਜੂਦਾ ਐਪਲੀਕੇਸ਼ਨ ਵਿੱਚ ਇੱਕ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਡਾਟਾ ਨਸ਼ਟ ਨਹੀਂ ਹੋਵੇਗਾ। ਇਸ ਲਈ ਕਿਸੇ ਖਾਸ ਪਹੁੰਚ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ।"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"ਕੀ ਤੁਸੀਂ ਇਸ ਬਿਲਟ-ਇਨ ਐਪਲੀਕੇਸ਼ਨ ਵਿੱਚ ਇੱਕ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਡਾਟਾ ਨਸ਼ਟ ਨਹੀਂ ਹੋਵੇਗਾ। ਇਸ ਲਈ ਕਿਸੇ ਖਾਸ ਪਹੁੰਚ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ।"</string>
+    <string name="install_failed" msgid="6579998651498970899">"ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ।"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"ਪੈਕੇਜ ਨੂੰ ਸਥਾਪਿਤ ਹੋਣ ਤੋਂ ਬਲੌਕ ਕੀਤਾ ਗਿਆ ਸੀ।"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"ਪੈਕੇਜ ਦੇ ਇੱਕ ਮੌਜੂਦਾ ਪੈਕੇਜ ਨਾਲ ਵਿਵਾਦ ਹੋਣ ਕਰਕੇ ਐਪ ਦੀ ਸਥਾਪਨਾ ਨਹੀਂ ਕੀਤੀ ਗਈ।"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"ਐਪ ਦੇ ਤੁਹਾਡੇ ਟੈਬਲੈੱਟ ਦੇ ਅਨੁਰੂਪ ਨਾ ਹੋਣ ਕਰਕੇ ਐਪ ਦੀ ਸਥਾਪਨਾ ਨਹੀਂ ਕੀਤੀ ਗਈ।"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"ਇਹ ਐਪ ਤੁਹਾਡੇ ਟੀਵੀ ਦੇ ਅਨੁਕੂਲ ਨਹੀਂ ਹੈ।"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"ਐਪ ਦੇ ਤੁਹਾਡੇ ਫ਼ੋਨ ਦੇ ਅਨੁਰੂਪ ਨਾ ਹੋਣ ਕਰਕੇ ਐਪ ਦੀ ਸਥਾਪਨਾ ਨਹੀਂ ਕੀਤੀ ਗਈ।"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"ਪੈਕੇਜ ਦੇ ਅਵੈਧ ਪ੍ਰਤੀਤ ਹੋਣ ਕਰਕੇ ਐਪ ਦੀ ਸਥਾਪਨਾ ਨਹੀਂ ਕੀਤੀ ਗਈ।"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਤੁਹਾਡੀ ਟੈਬਲੈੱਟ ਤੇ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਤੁਹਾਡੇ TV ਤੇ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਤੇ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।"</string>
+    <string name="launch" msgid="4826921505917605463">"ਖੋਲ੍ਹੋ"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"ਤੁਹਾਡਾ ਪ੍ਰਸ਼ਾਸਕ ਅਗਿਆਤ ਸਰੋਤਾਂ ਤੋਂ ਪ੍ਰਾਪਤ ਐਪਾਂ ਸਥਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੰਦਾ"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"ਇਸ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਅਗਿਆਤ ਐਪਾਂ ਨੂੰ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"ਇਸ ਵਰਤੋਂਕਾਰ ਨੂੰ ਐਪਾਂ ਸਥਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"</string>
+    <string name="ok" msgid="3468756155452870475">"ਠੀਕ"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"ਐਪਸ ਵਿਵਸਥਿਤ ਕਰੋ"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"ਖਾਲੀ ਸਪੇਸ"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਕੁਝ ਸਪੇਸ ਖਾਲੀ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"ਐਪ ਨਹੀਂ ਮਿਲਿਆ"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"ਐਪ ਇੰਸਟੌਲ ਕੀਤੇ ਐਪਸ ਦੀ ਸੂਚੀ ਵਿੱਚ ਨਹੀਂ ਮਿਲਿਆ ਸੀ।"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"ਇਜਾਜ਼ਤ ਨਹੀਂ"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"ਮੌਜੂਦਾ ਵਰਤੋਂਕਾਰ ਨੂੰ ਇਹ ਅਣਸਥਾਪਨਾ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਹੈ।"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"ਗੜਬੜ"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"ਐਪ ਅਣਸਥਾਪਤ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ।"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"ਐਪ ਅਣਸਥਾਪਤ ਕਰੋ"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"ਅੱਪਡੇਟ ਅਣਸਥਾਪਤ ਕਰੋ"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ਇਸ ਐਪ ਦਾ ਭਾਗ ਹੈ:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਐਪਲੀਕੇਸ਼ਨ ਅਤੇ ਇਸਦਾ ਡਾਟਾ ਡੀਵਾਈਸ ਤੇ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਵੱਲੋਂ ਹਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"ਕੀ ਤੁਸੀਂ ਵਰਤੋਂਕਾਰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਇਸ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਫੈਕਟਰੀ ਸੰਸਕਰਣ ਨਾਲ ਬਦਲਣਾ ਹੈ? ਸਾਰਾ  ਡਾਟਾ  ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਫੈਕਟਰੀ ਵਰਜਨ ਨਾਲ ਬਦਲਣਾ ਹੈ? ਸਾਰਾ ਡਾਟਾ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ। ਇਹ ਇਸ ਡੀਵਾਈਸ ਦੇ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਨੂੰ ਪ੍ਰਭਾਵਿਤ ਕਰੇਗਾ, ਜਿਸ ਵਿੱਚ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਾਲੇ ਵਰਤੋਂਕਾਰ ਵੀ ਸ਼ਾਮਲ ਹਨ।"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"ਚੱਲ ਰਹੀਆਂ ਅਣਸਥਾਪਨਾਵਾਂ"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"ਅਸਫਲ ਅਣਸਥਾਪਨਾਵਾਂ"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"ਅਣਇੰਸਟੌਲ ਕਰ  ਰਿਹਾ ਹੈ…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ਨੂੰ ਅਣਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"ਅਣਸਥਾਪਨਾ ਪੂਰੀ ਹੋਈ।"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ਨੂੰ ਅਣਸਥਾਪਤ ਕੀਤਾ"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"ਅਣਸਥਾਪਨਾ ਅਸਫਲ।"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ।"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"ਕਿਰਿਆਸ਼ੀਲ ਡੀਵਾਈਸ ਪ੍ਰਸ਼ਾਸਕ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਕਿਰਿਆਸ਼ੀਲ ਡੀਵਾਈਸ ਪ੍ਰਸ਼ਾਸਕ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"ਇਹ ਐਪ ਕੁਝ ਵਰਤੋਂਕਾਰਾਂ ਜਾਂ ਪ੍ਰੋਫਾਈਲਾਂ ਲਈ ਲੋੜੀਂਦੀ ਹੈ ਅਤੇ ਹੋਰਾਂ ਲਈ ਅਣਸਥਾਪਤ ਕੀਤੀ ਗਈ ਸੀ"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"ਇਹ ਐਪ ਤੁਹਾਡੀ ਪ੍ਰੋਫਾਈਲ ਲਈ ਲੋੜੀਂਦੀ ਹੈ ਅਤੇ ਇਸ ਨੂੰ ਅਣਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"ਇਹ ਐਪ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਕ ਵੱਲੋਂ ਲੋੜੀਂਦੀ ਹੈ ਅਤੇ ਇਸਨੂੰ ਅਣਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"ਡੀਵਾਈਸ ਪ੍ਰਸ਼ਾਸਕ ਐਪਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
+    <string name="manage_users" msgid="3125018886835668847">"ਵਰਤੋਂਕਾਰਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਅਣਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"ਪੈਕੇਜ ਨੂੰ ਪਾਰਸ ਕਰਨ ਵਿੱਚ ਇੱਕ ਸਮੱਸਿਆ ਸੀ।"</string>
+    <string name="newPerms" msgid="6039428254474104210">"ਨਵਾਂ"</string>
+    <string name="allPerms" msgid="1024385515840703981">"ਸਭ"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"ਪ੍ਰਾਈਵੇਸੀ"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"ਡੀਵਾਈਸ ਪਹੁੰਚ"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"ਇਸ ਅੱਪਡੇਟ ਲਈ ਕਿਸੇ ਨਵੀਆਂ ਅਨੁਮਤੀਆਂ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ।"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"ਹੋਰ ਜਾਣਕਾਰੀ"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"ਫੇਰ ਵੀ ਅਸਵੀਕਾਰ ਕਰੋ"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> ਦਾ <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ <xliff:g id="ACTION">%2$s</xliff:g> ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"ਹਮੇਸ਼ਾਂ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ <xliff:g id="ACTION">%2$s</xliff:g> ਕਰਨ ਦਿਓ?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"ਸਿਰਫ਼ ਐਪ ਵਰਤਣ ਵੇਲੇ"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"ਹਮੇਸ਼ਾਂ"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"ਅਸਵੀਕਾਰ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਨਾ ਪੁੱਛੋ"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"ਸਭ ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ਕਿਸੇ ਨੂੰ ਵੀ ਅਯੋਗ ਨਹੀਂ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"ਆਗਿਆ ਦਿਓ"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"ਐਪਾਂ"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"ਐਪ ਇਜਾਜ਼ਤਾਂ"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"ਦੁਬਾਰਾ ਨਾ ਪੁੱਛੋ"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"ਕੋਈ ਇਜਾਜ਼ਤਾਂ ਨਹੀਂ ਹਨ"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"ਵਧੀਕ ਇਜਾਜ਼ਤਾਂ"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"ਐਪ ਜਾਣਕਾਰੀ ਖੋਲ੍ਹੋ"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <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="old_sdk_deny_warning" msgid="3872277112584842615">"ਇਹ ਐਪ Android ਦੇ ਕਿਸੇ ਪੁਰਾਣੇ ਸੰਸਕਰਣ ਲਈ ਬਣਾਈ ਗਈ ਸੀ। ਅਨੁਮਤੀ ਨੂੰ ਇਨਕਾਰ ਕਰਨਾ ਇਸਦੇ ਉਦੇਸ਼ਿਤ ਫੰਕਸ਼ਨ ਨੂੰ ਪ੍ਰਭਾਵਿਤ ਕਰ ਸਕਦਾ ਹੈ।"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"ਕੋਈ ਅਗਿਆਤ ਕਾਰਵਾਈ ਕਰੋ"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g> ਵਿੱਚੋਂ <xliff:g id="COUNT_0">%1$d</xliff:g> ਐਪਾਂ ਨੂੰ ਆਗਿਆ ਦਿੱਤੀ"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"ਸਿਸਟਮ ਦਿਖਾਓ"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"ਸਿਸਟਮ ਲੁਕਾਓ"</string>
+    <string name="no_apps" msgid="1965493419005012569">"ਕੋਈ ਐਪਾਂ ਨਹੀਂ"</string>
+    <string name="location_settings" msgid="1774875730854491297">"ਟਿਕਾਣਾ ਸੈਟਿੰਗਾਂ"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਇਸ ਡੀਵਾਈਸ ਲਈ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦਾ ਇੱਕ ਪ੍ਰਦਾਤਾ ਹੈ। ਟਿਕਾਣਾ ਪਹੁੰਚ ਨੂੰ ਟਿਕਾਣਾ ਸੈਟਿੰਗਾਂ ਤੋਂ ਸੰਸ਼ੋਧਿਤ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ।"</string>
+    <string name="system_warning" msgid="7103819124542305179">"ਜੇਕਰ ਤੁਸੀਂ ਇਸ ਇਜਾਜ਼ਤ ਨੂੰ ਅਸਵੀਕਾਰ ਕਰਦੇ ਹੋ, ਤਾਂ ਤੁੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀਆਂ ਮੂੂਲ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਆਪਣੇ ਫੰਕਸ਼ਨ ਮੁਤਾਬਕ ਕੰਮ ਨਹੀਂ ਵੀ ਕਰ ਸਕਦੀਆਂ।"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"ਨੀਤੀ ਮੁਤਾਬਕ ਲਾਗੂ ਕੀਤਾ"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"ਨੀਤੀ ਵੱਲੋਂ ਬੈਕਗ੍ਰਾਊਂਡ ਪਹੁੰਚ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"ਨੀਤੀ ਵੱਲੋਂ ਬੈਕਗ੍ਰਾਊਂਡ ਪਹੁੰਚ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"ਨੀਤੀ ਵੱਲੋਂ ਫੋਰਗ੍ਰਾਊਂਡ ਪਹੁੰਚ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਕੰਟਰੋਲ ਕੀਤੀ ਗਈ"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"ਹਮੇਸ਼ਾਂ"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"ਸਿਰਫ਼ ਐਪ ਵਰਤਣ ਦੌਰਾਨ"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"ਕਦੇ ਵੀ ਨਹੀਂ"</string>
+    <string name="loading" msgid="7811651799620593731">"ਲੋਡ ਕਰ ਰਿਹਾ ਹੈ…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"ਸਾਰੀਆਂ ਅਨੁਮਤੀਆਂ"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"ਹੋਰ ਐਪ ਸਮਰੱਥਤਾਵਾਂ"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"ਇਜਾਜ਼ਤ ਬੇਨਤੀ"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"ਸਕਰੀਨ ਓਵਰਲੇਅ ਲੱਭ ਗਿਆ"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"ਇਸ ਇਜ਼ਾਜਤ ਸੈਟਿੰਗ ਨੂੰ ਬਦਲਣ ਲਈ; ਤੁਹਾਨੂੰ ਪਹਿਲਾਂ ਸੈਟਿੰਗਾਂ ਅਤੇ ਐਪਾਂ ਤੋਂ ਸਕ੍ਰੀਨ ਓਵਰਲੇਅ ਬੰਦ ਕਰਨਾ ਪਵੇਗਾ"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"ਵੀਅਰ \'ਤੇ ਸਥਾਪਤ/ਅਣਸਥਾਪਤ ਕਾਰਵਾਈਆਂ ਸਮਰਥਿਤ ਨਹੀਂ ਹਨ।"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"ਇਹ ਚੁਣੋ ਕਿ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਕਿਸ \'ਤੇ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਅੱਪਡੇਟ ਕੀਤਾ ਜਾ ਚੁੱਕਿਆ ਹੈ। ਇਹ ਚੁਣੋ ਕਿ ਇਸ ਐਪ ਨੂੰ ਕਿਸ \'ਤੇ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ।"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"ਰੱਦ ਕਰੋ"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"ਜਾਰੀ ਰੱਖੋ"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"ਨਵੀਆਂ ਇਜਾਜ਼ਤਾਂ"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"ਵਰਤਮਾਨ ਇਜਾਜ਼ਤਾਂ"</string>
+    <string name="message_staging" msgid="6151794817691100003">"ਐਪ ਨੂੰ ਪੜਾਅਬੱਧ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"ਅਗਿਆਤ"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਲਈ, ਤੁਹਾਡੇ ਟੈਬਲੈੱਟ ਨੂੰ ਇਸ ਸਰੋਤ ਤੋਂ ਅਗਿਆਤ ਐਪਾਂ ਸਥਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਲਈ, ਤੁਹਾਡੇ ਟੀਵੀ ਨੂੰ ਇਸ ਸਰੋਤ ਤੋਂ ਅਗਿਆਤ ਐਪਾਂ ਸਥਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਲਈ, ਤੁਹਾਡੇ ਫ਼ੋਨ ਨੂੰ ਇਸ ਸਰੋਤ ਤੋਂ ਅਗਿਆਤ ਐਪਾਂ ਸਥਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"ਤੁਹਾਡਾ ਫ਼ੋਨ ਅਤੇ ਨਿੱਜੀ ਡਾਟਾ ਅਗਿਆਤ ਐਪਾਂ ਤੋਂ ਹਮਲੇ ਪ੍ਰਤੀ ਵਧੇਰੇ ਵਿੰਨਣਸ਼ੀਲ ਹਨ। ਇਹ ਐਪ ਸਥਾਪਤ ਕਰਕੇ, ਤੁਸੀਂ ਸਹਿਮਤੀ ਦਿੰਦੇ ਹੋ ਕਿ ਆਪਣੇ ਫ਼ੋਨ ਨੂੰ ਹੋਣ ਵਾਲੇ ਕਿਸੇ ਵੀ ਨੁਕਸਾਨ ਜਾਂ ਡਾਟੇ ਦੀ ਹਾਨੀ ਲਈ ਤੁਸੀਂ ਜ਼ਿੰਮੇਵਾਰ ਹੋ ਜੋ ਸ਼ਾਇਦ ਇਸ ਐਪ ਨੂੰ ਵਰਤਣ ਦੇ ਨਤੀਜੇ ਵਜੋਂ ਹੋ ਸਕਦਾ ਹੈ।"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਅਤੇ ਨਿੱਜੀ ਡਾਟਾ ਅਗਿਆਤ ਐਪਾਂ ਤੋਂ ਹਮਲੇ ਪ੍ਰਤੀ ਵਧੇਰੇ ਕਮਜ਼ੋਰ ਹੈ। ਇਹ ਐਪ ਸਥਾਪਤ ਕਰਕੇ, ਤੁਸੀਂ ਸਹਿਮਤੀ ਦਿੰਦੇ ਹੋ ਕਿ ਆਪਣੇ ਟੈਬਲੈੱਟ ਨੂੰ ਹੋਣ ਵਾਲੇ ਕਿਸੇ ਵੀ ਨੁਕਸਾਨ ਜਾਂ ਡਾਟੇ ਦੀ ਹਾਨੀ ਲਈ ਤੁਸੀਂ ਜ਼ਿੰਮੇਵਾਰ ਹੋ ਜੋ ਸ਼ਾਇਦ ਇਸ ਐਪ ਨੂੰ ਵਰਤਣ ਦੇ ਨਤੀਜੇ ਵਜੋਂ ਹੋ ਸਕਦਾ ਹੈ।"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"ਤੁਹਾਡਾ ਟੀਵੀ ਅਤੇ ਨਿੱਜੀ  ਡਾਟਾ  ਅਗਿਆਤ ਐਪਾਂ ਤੋਂ ਹਮਲੇ ਪ੍ਰਤੀ ਵਧੇਰੇ ਵਿੰਨਣਸ਼ੀਲ ਹਨ। ਇਹ ਐਪ ਸਥਾਪਤ ਕਰਕੇ, ਤੁਸੀਂ ਸਹਿਮਤੀ ਦਿੰਦੇ ਹੋ ਕਿ ਆਪਣੇ ਟੀਵੀ ਨੂੰ ਹੋਣ ਵਾਲੇ ਕਿਸੇ ਵੀ ਨੁਕਸਾਨ ਜਾਂ  ਡਾਟੇ  ਦੀ ਹਾਨੀ ਲਈ ਤੁਸੀਂ ਜ਼ਿੰਮੇਵਾਰ ਹੋ ਜੋ ਸ਼ਾਇਦ ਇਸ ਐਪ ਨੂੰ ਵਰਤਣ ਦੇ ਨਤੀਜੇ ਵਜੋਂ ਹੋ ਸਕਦਾ ਹੈ।"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"ਜਾਰੀ ਰੱਖੋ"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"ਸੈਟਿੰਗਾਂ"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"ਵੀਅਰ ਐਪਾਂ ਨੂੰ ਸਥਾਪਤ/ਅਣਸਥਾਪਤ ਕਰਨਾ"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pl-television/strings.xml b/packages/PackageInstaller/res/values-pl-television/strings.xml
new file mode 100644
index 0000000..6fede9a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pl-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Odmów i nie pytaj ponownie"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Możesz to później zmienić, wybierając Ustawienia &gt; Aplikacje"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Pokaż aplikacje systemowe"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Uprawnienia aplikacji"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Uprawnienia aplikacji"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> – uprawnienia"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Dodatkowe uprawnienia"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> – uprawnienia"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pl-watch/strings.xml b/packages/PackageInstaller/res/values-pl-watch/strings.xml
new file mode 100644
index 0000000..95c3687
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pl-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Odmów i nie pytaj ponownie"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Pokaż aplikacje systemowe"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Nie można zmienić"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Tak"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Anuluj"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pl/strings.xml b/packages/PackageInstaller/res/values-pl/strings.xml
new file mode 100644
index 0000000..dbedfad
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pl/strings.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Instalator pakietu"</string>
+    <string name="next" msgid="3057143178373252333">"Dalej"</string>
+    <string name="install" msgid="5896438203900042068">"Instaluj"</string>
+    <string name="done" msgid="3889387558374211719">"Gotowe"</string>
+    <string name="cancel" msgid="8360346460165114585">"Anuluj"</string>
+    <string name="installing" msgid="8613631001631998372">"Instalowanie..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Instaluję pakiet <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplikacja została zainstalowana."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Zainstalować tę aplikację? Będzie miała następujące uprawnienia:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Zainstalować tę aplikację? Nie ma specjalnych wymagań dotyczących dostępu."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Zainstalować aktualizację tej aplikacji? Nie utracisz wcześniejszych danych. Zaktualizowana aplikacja będzie miała następujące uprawnienia:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Zainstalować aktualizację fabrycznej aplikacji? Nie utracisz wcześniejszych danych. Zaktualizowana aplikacja będzie miała następujące uprawnienia:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Chcesz zaktualizować tę istniejącą aplikację? Nie utracisz danych. Nie są wymagane specjalne uprawnienia dostępu."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Chcesz zaktualizować tę wbudowaną aplikację? Nie utracisz danych. Nie są wymagane specjalne uprawnienia dostępu."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Aplikacja nie została zainstalowana."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Instalacja pakietu została zablokowana."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Aplikacja nie została zainstalowana, bo powoduje konflikt z istniejącym pakietem."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Aplikacja nie została zainstalowana, bo jest niezgodna z Twoim tabletem."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Aplikacja jest niezgodna z Twoim telewizorem."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Aplikacja nie została zainstalowana, bo jest niezgodna z Twoim telefonem."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Aplikacja nie została zainstalowana, bo pakiet wygląda na nieprawidłowy."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Nie można zainstalować aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> na Twoim tablecie."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Nie udało się zainstalować <xliff:g id="APP_NAME">%1$s</xliff:g> na Twoim telewizorze."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Nie można zainstalować aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> na Twoim telefonie."</string>
+    <string name="launch" msgid="4826921505917605463">"Otwórz"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Twój administrator nie zezwala na instalowanie aplikacji pochodzących z nieznanych źródeł."</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Ten użytkownik nie może instalować nieznanych aplikacji"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Ten użytkownik nie może instalować aplikacji"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Zarządzaj aplikacjami"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Brak miejsca"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Nie można zainstalować aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>. Zwolnij trochę miejsca i spróbuj ponownie."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Nie znaleziono aplikacji"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Aplikacji nie znaleziono na liście zainstalowanych programów."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Niedozwolone"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Bieżący użytkownik nie może tego odinstalować."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Błąd"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Nie można odinstalować aplikacji."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Odinstaluj aplikację"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Odinstaluj aktualizację"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> jest częścią następującej aplikacji:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Odinstalować tę aplikację?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Chcesz odinstalować tę aplikację dla "<b>"wszystkich"</b>" użytkowników? Ta aplikacja i jej dane zostaną usunięte dla "<b>"wszystkich"</b>" użytkowników na urządzeniu."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Chcesz odinstalować tę aplikację dla użytkownika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Przywrócić fabryczną wersję tej aplikacji? Wszystkie dane zostaną usunięte."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Aktywne odinstalowania"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Nieudane odinstalowania"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Odinstalowywanie..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Odinstalowuję pakiet <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Odinstalowywanie zakończone"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Odinstalowano pakiet <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Nie udało się odinstalować."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Nie udało się odinstalować pakietu <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Nie można odinstalować aktywnej aplikacji do administrowania urządzeniem"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Nie można odinstalować aplikacji do administrowania urządzeniem aktywnej dla użytkownika <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Niektórzy użytkownicy i niektóre profile wymagają tej aplikacji, a w innych przypadkach została odinstalowana"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Ta aplikacja jest potrzebna w Twoim profilu i nie można jej odinstalować."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Administrator urządzenia wymaga tej aplikacji i nie można jej odinstalować."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Zarządzaj aplikacjami do administrowania urządzeniem"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Zarządzaj użytkownikami"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Nie można odinstalować aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Podczas analizowania pakietu wystąpił problem."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nowe"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Wszystkie"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Prywatność"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Dostęp do urządzenia"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Ta aktualizacja nie wymaga nowych uprawnień."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Odmów"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Więcej informacji"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Odmów mimo to"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> z <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Zawsze zezwalać aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na: <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Tylko przy używaniu aplikacji"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Zawsze"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Odmów i nie pytaj ponownie"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"wyłączone: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"wszystkie wyłączone"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"brak wyłączonych"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Zezwól"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplikacje"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Uprawnienia aplikacji"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Nie pytaj ponownie"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Brak uprawnień"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Dodatkowe uprawnienia"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Otwórz informacje o aplikacji"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="few">Jeszcze <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="many">Jeszcze <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="other">Jeszcze <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Jeszcze <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Ta aplikacja jest na straszą wersję Androida. Jeśli odmówisz uprawnień, aplikacja może nie działać prawidłowo."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"wykonywanie nieznanych działań"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Dostęp ma: <xliff:g id="COUNT_0">%1$d</xliff:g> z <xliff:g id="COUNT_1">%2$d</xliff:g> aplikacji"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Pokaż systemowe"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Ukryj systemowe"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Brak aplikacji"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Ustawienia lokalizacji"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> jest dostawcą usług lokalizacyjnych dla tego urządzenia. Dostęp do danych lokalizacji można zmienić w ustawieniach lokalizacji."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Jeśli nie przyznasz tych uprawnień, podstawowe funkcje urządzenia mogą nie działać prawidłowo."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Narzucone przez zasady"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Dostęp w tle wyłączony przez zasady"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Dostęp w tle włączony przez zasady"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Dostęp na pierwszym planie włączony przez zasady"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Kontrolowane przez administratora"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Zawsze"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Tylko przy używaniu aplikacji"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nigdy"</string>
+    <string name="loading" msgid="7811651799620593731">"Ładuję…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Wszystkie uprawnienia"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Inne funkcje aplikacji"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Prośba o pozwolenie"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Wykryto nakładkę ekranową"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Aby zmodyfikować te uprawnienia, musisz najpierw wyłączyć nakładkę ekranową, klikając Ustawienia &gt; Aplikacje"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Otwórz ustawienia"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear nie obsługuje instalowania ani odinstalowywania."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Wybierz, jakie uprawnienia dostępu ma mieć &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Aplikacja &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; została zaktualizowana. Wybierz dla niej uprawnienia dostępu."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Anuluj"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Dalej"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nowe uprawnienia"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Aktualne uprawnienia"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Przygotowuję aplikację…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Nieznana"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Ze względów bezpieczeństwa na Twoim tablecie nie można instalować nieznanych aplikacji z tego źródła."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Ze względów bezpieczeństwa na Twoim telewizorze nie można instalować nieznanych aplikacji z tego źródła."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Ze względów bezpieczeństwa na Twoim telefonie nie można instalować nieznanych aplikacji z tego źródła."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Dane na telefonie i prywatne są bardziej narażone na atak nieznanych aplikacji. Instalując tę aplikację, bierzesz na siebie odpowiedzialność za ewentualne uszkodzenie telefonu lub utratę danych w wyniku jej używania."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Dane na tablecie i prywatne są bardziej narażone na atak nieznanych aplikacji. Instalując tę aplikację, bierzesz na siebie odpowiedzialność za ewentualne uszkodzenie tabletu lub utratę danych w wyniku jej używania."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Dane na telewizorze i prywatne są bardziej narażone na atak nieznanych aplikacji. Instalując tę aplikację, bierzesz na siebie odpowiedzialność za ewentualne uszkodzenie telewizora lub utratę danych w wyniku jej używania."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Dalej"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Ustawienia"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Instalowanie/odinstalowywanie aplikacji na Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pt-rBR-television/strings.xml b/packages/PackageInstaller/res/values-pt-rBR-television/strings.xml
new file mode 100644
index 0000000..ba6e68f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-rBR-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Negar e não perguntar novamente"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"É possível alterar isso mais tarde em \"Config.\" &gt; \"Apps\""</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Mostrar apps do sistema"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Permissões do app"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Permissões do app"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Permissões para <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Outras permissões"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Permissões para <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pt-rBR-watch/strings.xml b/packages/PackageInstaller/res/values-pt-rBR-watch/strings.xml
new file mode 100644
index 0000000..8742c2d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-rBR-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Negar e não perguntar de novo"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Mostrar apps do sistema"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Impossível alterar"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Sim"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Cancelar"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pt-rBR/strings.xml b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..feb2337
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Instalador do pacote"</string>
+    <string name="next" msgid="3057143178373252333">"Próximo"</string>
+    <string name="install" msgid="5896438203900042068">"Instalar"</string>
+    <string name="done" msgid="3889387558374211719">"Concluído"</string>
+    <string name="cancel" msgid="8360346460165114585">"Cancelar"</string>
+    <string name="installing" msgid="8613631001631998372">"Instalando..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Instalando <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"App instalado."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Quer instalar este app? Ele terá acesso a:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Quer instalar este app? Não requer acesso especial."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Quer instalar uma atualização para este app? Os dados existentes não serão perdidos. O app atualizado terá acesso a:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Quer instalar uma atualização para este app integrado? Os dados existentes não serão perdidos. O app atualizado terá acesso a:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Quer instalar uma atualização para este app existente? Seus dados existentes não serão perdidos. A atualização não requer qualquer acesso especial."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Quer instalar uma atualização para este app integrado? Seus dados existentes não serão perdidos. A atualização não requer qualquer acesso especial."</string>
+    <string name="install_failed" msgid="6579998651498970899">"O app não foi instalado."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"A instalação do pacote foi bloqueada."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Como o pacote tem um conflito com um pacote já existente, o app não foi instalado."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Como o app não é compatível com seu tablet, ele não foi instalado."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Este app não é compatível com sua TV."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Como o app não é compatível com seu smartphone, ele não foi instalado."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Como o pacote parece ser inválido, o app não foi instalado."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Não foi possível instalar <xliff:g id="APP_NAME">%1$s</xliff:g> em seu tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Não foi possível instalar <xliff:g id="APP_NAME">%1$s</xliff:g> na sua TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Não foi possível instalar <xliff:g id="APP_NAME">%1$s</xliff:g> em seu telefone."</string>
+    <string name="launch" msgid="4826921505917605463">"Abrir"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Seu administrador não permite a instalação de apps transferidos por download de fontes desconhecidas"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Apps desconhecidos não podem ser instalados por este usuário"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Este usuário não tem permissão para instalar apps"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Gerenciar apps"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Sem espaço"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Não foi possível instalar <xliff:g id="APP_NAME">%1$s</xliff:g>. Libere um pouco de espaço e tente novamente."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"App não encontrado"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"O app não foi encontrado na lista de apps instalados."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Não permitido"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"O usuário atual não tem permissão para executar essa desinstalação."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Erro"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Não foi possível desinstalar o app."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Desinstalar app"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Desinstalar atualização"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> é parte do seguinte app:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Quer desinstalar este app?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Quer desinstalar este app para "<b>"todos"</b>" os usuários? O app e seus dados serão removidos para "<b>"todos"</b>" os usuários do dispositivo."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Quer desinstalar este app para o usuário <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Substituir este app pela versão de fábrica? Todos os dados serão removidos."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Executando desinstalações"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Falha nas desinstalações"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Desinstalando..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Desinstalando <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Desinstalação concluída."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> desinstalado"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Desinstalação malsucedida."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Falha na desinstalação de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Não é possível desinstalar o app para administrador ativo do dispositivo"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Não é possível desinstalar o app para administrador ativo do dispositivo de <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"O app é necessário para alguns usuários ou perfis e foi desinstalado para outros"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Este app é necessário para seu perfil e não pode ser desinstalado."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"O app é exigido pelo administrador do dispositivo e não pode ser desinstalado."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Gerenciar apps do administrador do dispositivo"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Gerenciar usuários"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Não foi possível desinstalar <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Ocorreu um problema ao analisar o pacote."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Novas"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Todas"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privacidade"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Acesso ao dispositivo"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Esta atualização não requer novas permissões."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Negar"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Mais informações"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Negar mesmo assim"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Sempre permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Apenas ao usar o app"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Sempre"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Negar e não perguntar novamente"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> desativada(s)"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"todas desativadas"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"nenhuma desativada"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Permitir"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Apps"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Permissões do app"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Não perguntar novamente"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Sem permissões"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Outras permissões"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Abrir informações do app"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one">Mais <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="other">Mais <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Este app foi projetado para uma versão anterior do Android. Negar a permissão pode fazer com que ele deixe de funcionar conforme esperado."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"executar uma ação desconhecida"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> de <xliff:g id="COUNT_1">%2$d</xliff:g> apps permitidos"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Mostrar sistema"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Ocultar sistema"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Nenhum app"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Configurações de localização"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> é um provedor de serviços de localização para este dispositivo. O acesso local pode ser modificado nas configurações de localização."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Se você negar essa permissão, recursos básicos do seu dispositivo poderão não funcionar mais como deveriam."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Aplicável por política"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Acesso em segundo plano desativado pela política"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Acesso em segundo plano ativado pela política"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Acesso em primeiro plano ativado pela política"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Controlada pelo administrador"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Sempre"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Apenas ao usar o app"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nunca"</string>
+    <string name="loading" msgid="7811651799620593731">"Carregando…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Todas as permissões"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Outros recursos do app"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Solicitação de permissão"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Sobreposição de tela detectada"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Para alterar a configuração dessa permissão, você deve primeiro desativar a sobreposição de tela em \"Config.\" &gt; \"Apps\""</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Abrir configurações"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"As ações de instalar/desinstalar não são compatíveis com o Android Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Escolha o que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; terá permissão de acessar"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"O app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; foi atualizado. Escolha o que esse app terá permissão de acessar."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Cancelar"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continuar"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Novas permissões"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Permissões atuais"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Promovendo app…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Desconhecido"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Para sua segurança, seu tablet não tem permissão para instalar apps desconhecidos dessa fonte."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Para sua segurança, sua TV não tem permissão para instalar apps desconhecidos dessa fonte."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Para sua segurança, seu smartphone não tem permissão para instalar apps desconhecidos dessa fonte."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Seu smartphone e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer dano causado ao seu smartphone ou pela perda de dados que possa resultar do uso desse app."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Seu tablet e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer dano causado ao seu tablet ou pela perda de dados que possa resultar do uso desse app."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Sua TV e seus dados pessoais estão mais vulneráveis a ataques por apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer dano à sua TV ou pela perda de dados que possa resultar do uso dese app."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continuar"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Configurações"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Instalando/desinstalando apps do Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pt-rPT-television/strings.xml b/packages/PackageInstaller/res/values-pt-rPT-television/strings.xml
new file mode 100644
index 0000000..206676b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-rPT-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Recusar e não perguntar novamente"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Pode alterar esta definição mais tarde em Definições &gt; Aplicações"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Mostrar aplicações do sistema"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Autorizações da aplicação"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Autorizações da aplicação"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Autorizações de <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Autorizações adicionais"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Autorizações de <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pt-rPT-watch/strings.xml b/packages/PackageInstaller/res/values-pt-rPT-watch/strings.xml
new file mode 100644
index 0000000..e3c9e9f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-rPT-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Recusar e não perguntar nov."</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Mostrar aplicações do sistema"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Não pode ser alterado"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Sim"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Cancelar"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pt-rPT/strings.xml b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..c380839
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Programa de instalação do pacote"</string>
+    <string name="next" msgid="3057143178373252333">"Seguinte"</string>
+    <string name="install" msgid="5896438203900042068">"Instalar"</string>
+    <string name="done" msgid="3889387558374211719">"Concluído"</string>
+    <string name="cancel" msgid="8360346460165114585">"Cancelar"</string>
+    <string name="installing" msgid="8613631001631998372">"A instalar..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"A instalar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplicação instalada."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Pretende instalar esta aplicação? Terá acesso a:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Pretende instalar esta aplicação? Não requer qualquer acesso especial."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Pretende instalar uma atualização para a aplicação existente? Os dados existentes não serão perdidos. A aplicação atualizada terá acesso a:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Pretende instalar uma atualização para a aplicação existente? Os dados existentes não serão perdidos. A aplicação atualizada terá acesso a:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Pretende instalar uma atualização para a aplicação existente? Os dados existentes não serão perdidos. Não é necessário um acesso específico."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Pretende instalar uma atualização para a aplicação integrada? Os dados existentes não serão perdidos. Não é necessário um acesso específico."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Aplicação não instalada."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Foi bloqueada a instalação do pacote."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"A aplicação não foi instalada porque o pacote entra em conflito com um pacote existente."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"A aplicação não foi instalada porque não é compatível com o seu tablet."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Esta aplicação não é compatível com a sua TV."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"A aplicação não foi instalada porque não é compatível com o seu telemóvel."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"A aplicação não foi instalada porque o pacote parece ser inválido."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Não foi possível instalar <xliff:g id="APP_NAME">%1$s</xliff:g> no tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Não foi possível instalar o <xliff:g id="APP_NAME">%1$s</xliff:g> na sua TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Não foi possível instalar <xliff:g id="APP_NAME">%1$s</xliff:g> no telemóvel."</string>
+    <string name="launch" msgid="4826921505917605463">"Abrir"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"O gestor não permite a instalação de aplicações obtidas de fontes desconhecidas"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Este utilizador não pode instalar aplicações desconhecidas"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Este utilizador não tem permissão para instalar aplicações"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Gerir aplicações"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Sem espaço"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Não foi possível instalar <xliff:g id="APP_NAME">%1$s</xliff:g>. Liberte algum espaço e tente novamente."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Aplicação não encontrada"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"A aplicação não foi encontrada na lista de aplicações instaladas."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Não autorizado"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"O utilizador atual não tem autorização para efetuar esta desinstalação."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Erro"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Não foi possível desinstalar a aplicação."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Desinstalar a aplicação"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Desinstalar atualização"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> faz parte da seguinte aplicação:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Pretende desinstalar esta aplicação?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Pretende desinstalar esta aplicação para "<b>"todos"</b>" os utilizadores? A aplicação e os respetivos dados serão removidos de "<b>"todos"</b>" os utilizadores do dispositivo."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Pretende desinstalar esta aplicação para o utilizador <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"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="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Desinstalações em execução"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Desinstalações com falha"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"A desinstalar..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"A desinstalar a aplicação <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Desinstalação concluída."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"A aplicação <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> foi desinstalada"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Desinstalação sem êxito."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Falha ao desinstalar a aplicação <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Não é possível desinstalar a aplicação de administração de dispositivos ativa"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Não é possível desinstalar a aplicação de administração de dispositivos ativa para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Esta aplicação é necessária para alguns utilizadores ou perfis e foi desinstalada para outros"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"O perfil necessita desta aplicação e não é possível desinstalá-la."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Esta aplic. é exigida pelo gestor do disp. e não pode ser desinstalada."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Gerir aplicações de administração de dispositivos"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Gerir utilizadores"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Não foi possível desinstalar <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Ocorreu um problema ao analisar o pacote."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Novas"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Todas"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privacidade"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Acesso ao Dispositivo"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Esta atualização não requer novas permissões."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Recusar"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Mais informações"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Recusar mesmo assim"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Pretende permitir à(ao) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Pretende permitir que a aplicação &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g> sempre?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Apenas ao utilizar a aplicação"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Sempre"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Recusar e não perguntar novamente"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> desativadas"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"todas desativadas"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"nenhuma desativada"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Permitir"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplicações"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Permissões da aplicação"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Não perguntar novamente"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Sem autorizações"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Autorizações adicionais"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Abrir informações da aplicação"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">Mais <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Mais <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Esta aplicação foi concebida para uma versão mais antiga do Android. Negar autorização pode fazer com que deixe de funcionar como pretendido."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"executar uma ação desconhecida"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> de <xliff:g id="COUNT_1">%2$d</xliff:g> aplicações autorizadas"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Mostrar sistema"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Ocultar sistema"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Sem aplicações"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Definições de localização"</string>
+    <string name="location_warning" msgid="8778701356292735971">"O <xliff:g id="APP_NAME">%1$s</xliff:g> é um fornecedor de serviços de localização para este dispositivo. É possível modificar o acesso à localização a partir das definições de localização."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Se negar esta autorização, as funcionalidades básicas do seu dispositivo podem deixar de funcionar corretamente."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Imposta pela política"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Acesso em segundo plano desativado pela política"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Acesso em segundo plano ativado pela política"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Acesso em primeiro plano ativado pela política"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Controlado pelo administrador"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Sempre"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Apenas ao utilizar a aplicação"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nunca"</string>
+    <string name="loading" msgid="7811651799620593731">"A carregar…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Todas as autorizações"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Outras capacidades de aplicações"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Pedido de autorização"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Sobreposição de ecrã detetada"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Para alterar esta definição de autorização, primeiro tem de desativar a sobreposição do ecrã em Definições &gt; Aplicações"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Abrir definições"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"As ações de instalar/desinstalar não são compatíveis com o Android Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Escolher a que conteúdos permite que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"O &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; foi atualizado. Escolha a que conteúdos permite que esta aplicação aceda."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Cancelar"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continuar"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Novas autorizações"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Autorizações atuais"</string>
+    <string name="message_staging" msgid="6151794817691100003">"A preparar a aplicação…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Desconhecido"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Para sua segurança, o tablet não está autorizado a instalar aplicações desconhecidas a partir desta fonte."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Para sua segurança, a TV não está autorizada a instalar aplicações desconhecidas a partir desta fonte."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Para sua segurança, o telemóvel não está autorizado a instalar aplicações desconhecidas a partir desta fonte."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"O seu telemóvel e os dados pessoais estão mais vulneráveis a ataques por parte de aplicações desconhecidas. Ao instalar esta aplicação, concorda que é responsável por quaisquer danos causados ao telemóvel ou pelas perdas de dados que possam resultar da utilização da mesma."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"O seu tablet e os dados pessoais estão mais vulneráveis a ataques por parte de aplicações desconhecidas. Ao instalar esta aplicação, concorda que é responsável por quaisquer danos causados ao tablet ou pelas perdas de dados que possam resultar da utilização da mesma."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"A sua TV e os dados pessoais estão mais vulneráveis a ataques por parte de aplicações desconhecidas. Ao instalar esta aplicação, concorda que é responsável por quaisquer danos causados à TV ou pelas perdas de dados que possam resultar da utilização da mesma."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continuar"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Definições"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Instalar/desinstalar aplicações Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pt-television/strings.xml b/packages/PackageInstaller/res/values-pt-television/strings.xml
new file mode 100644
index 0000000..ba6e68f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Negar e não perguntar novamente"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"É possível alterar isso mais tarde em \"Config.\" &gt; \"Apps\""</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Mostrar apps do sistema"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Permissões do app"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Permissões do app"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Permissões para <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Outras permissões"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Permissões para <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pt-watch/strings.xml b/packages/PackageInstaller/res/values-pt-watch/strings.xml
new file mode 100644
index 0000000..8742c2d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Negar e não perguntar de novo"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Mostrar apps do sistema"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Impossível alterar"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Sim"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Cancelar"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-pt/strings.xml b/packages/PackageInstaller/res/values-pt/strings.xml
new file mode 100644
index 0000000..feb2337
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Instalador do pacote"</string>
+    <string name="next" msgid="3057143178373252333">"Próximo"</string>
+    <string name="install" msgid="5896438203900042068">"Instalar"</string>
+    <string name="done" msgid="3889387558374211719">"Concluído"</string>
+    <string name="cancel" msgid="8360346460165114585">"Cancelar"</string>
+    <string name="installing" msgid="8613631001631998372">"Instalando..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Instalando <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"App instalado."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Quer instalar este app? Ele terá acesso a:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Quer instalar este app? Não requer acesso especial."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Quer instalar uma atualização para este app? Os dados existentes não serão perdidos. O app atualizado terá acesso a:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Quer instalar uma atualização para este app integrado? Os dados existentes não serão perdidos. O app atualizado terá acesso a:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Quer instalar uma atualização para este app existente? Seus dados existentes não serão perdidos. A atualização não requer qualquer acesso especial."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Quer instalar uma atualização para este app integrado? Seus dados existentes não serão perdidos. A atualização não requer qualquer acesso especial."</string>
+    <string name="install_failed" msgid="6579998651498970899">"O app não foi instalado."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"A instalação do pacote foi bloqueada."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Como o pacote tem um conflito com um pacote já existente, o app não foi instalado."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Como o app não é compatível com seu tablet, ele não foi instalado."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Este app não é compatível com sua TV."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Como o app não é compatível com seu smartphone, ele não foi instalado."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Como o pacote parece ser inválido, o app não foi instalado."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Não foi possível instalar <xliff:g id="APP_NAME">%1$s</xliff:g> em seu tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Não foi possível instalar <xliff:g id="APP_NAME">%1$s</xliff:g> na sua TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Não foi possível instalar <xliff:g id="APP_NAME">%1$s</xliff:g> em seu telefone."</string>
+    <string name="launch" msgid="4826921505917605463">"Abrir"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Seu administrador não permite a instalação de apps transferidos por download de fontes desconhecidas"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Apps desconhecidos não podem ser instalados por este usuário"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Este usuário não tem permissão para instalar apps"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Gerenciar apps"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Sem espaço"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Não foi possível instalar <xliff:g id="APP_NAME">%1$s</xliff:g>. Libere um pouco de espaço e tente novamente."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"App não encontrado"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"O app não foi encontrado na lista de apps instalados."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Não permitido"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"O usuário atual não tem permissão para executar essa desinstalação."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Erro"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Não foi possível desinstalar o app."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Desinstalar app"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Desinstalar atualização"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> é parte do seguinte app:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Quer desinstalar este app?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Quer desinstalar este app para "<b>"todos"</b>" os usuários? O app e seus dados serão removidos para "<b>"todos"</b>" os usuários do dispositivo."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Quer desinstalar este app para o usuário <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Substituir este app pela versão de fábrica? Todos os dados serão removidos."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Executando desinstalações"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Falha nas desinstalações"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Desinstalando..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Desinstalando <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Desinstalação concluída."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> desinstalado"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Desinstalação malsucedida."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Falha na desinstalação de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Não é possível desinstalar o app para administrador ativo do dispositivo"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Não é possível desinstalar o app para administrador ativo do dispositivo de <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"O app é necessário para alguns usuários ou perfis e foi desinstalado para outros"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Este app é necessário para seu perfil e não pode ser desinstalado."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"O app é exigido pelo administrador do dispositivo e não pode ser desinstalado."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Gerenciar apps do administrador do dispositivo"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Gerenciar usuários"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Não foi possível desinstalar <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Ocorreu um problema ao analisar o pacote."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Novas"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Todas"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privacidade"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Acesso ao dispositivo"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Esta atualização não requer novas permissões."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Negar"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Mais informações"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Negar mesmo assim"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Sempre permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Apenas ao usar o app"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Sempre"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Negar e não perguntar novamente"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> desativada(s)"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"todas desativadas"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"nenhuma desativada"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Permitir"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Apps"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Permissões do app"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Não perguntar novamente"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Sem permissões"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Outras permissões"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Abrir informações do app"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one">Mais <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="other">Mais <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Este app foi projetado para uma versão anterior do Android. Negar a permissão pode fazer com que ele deixe de funcionar conforme esperado."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"executar uma ação desconhecida"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> de <xliff:g id="COUNT_1">%2$d</xliff:g> apps permitidos"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Mostrar sistema"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Ocultar sistema"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Nenhum app"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Configurações de localização"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> é um provedor de serviços de localização para este dispositivo. O acesso local pode ser modificado nas configurações de localização."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Se você negar essa permissão, recursos básicos do seu dispositivo poderão não funcionar mais como deveriam."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Aplicável por política"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Acesso em segundo plano desativado pela política"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Acesso em segundo plano ativado pela política"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Acesso em primeiro plano ativado pela política"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Controlada pelo administrador"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Sempre"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Apenas ao usar o app"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nunca"</string>
+    <string name="loading" msgid="7811651799620593731">"Carregando…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Todas as permissões"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Outros recursos do app"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Solicitação de permissão"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Sobreposição de tela detectada"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Para alterar a configuração dessa permissão, você deve primeiro desativar a sobreposição de tela em \"Config.\" &gt; \"Apps\""</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Abrir configurações"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"As ações de instalar/desinstalar não são compatíveis com o Android Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Escolha o que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; terá permissão de acessar"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"O app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; foi atualizado. Escolha o que esse app terá permissão de acessar."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Cancelar"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continuar"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Novas permissões"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Permissões atuais"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Promovendo app…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Desconhecido"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Para sua segurança, seu tablet não tem permissão para instalar apps desconhecidos dessa fonte."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Para sua segurança, sua TV não tem permissão para instalar apps desconhecidos dessa fonte."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Para sua segurança, seu smartphone não tem permissão para instalar apps desconhecidos dessa fonte."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Seu smartphone e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer dano causado ao seu smartphone ou pela perda de dados que possa resultar do uso desse app."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Seu tablet e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer dano causado ao seu tablet ou pela perda de dados que possa resultar do uso desse app."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Sua TV e seus dados pessoais estão mais vulneráveis a ataques por apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer dano à sua TV ou pela perda de dados que possa resultar do uso dese app."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continuar"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Configurações"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Instalando/desinstalando apps do Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ro-television/strings.xml b/packages/PackageInstaller/res/values-ro-television/strings.xml
new file mode 100644
index 0000000..4f8992f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ro-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Respingeți și nu se mai întreabă"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Puteți modifica permisiunile ulterior din Setări &gt; Aplicații"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Afișează aplicațiile de sistem"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Permisiuni aplicații"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Permisiuni aplicații"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Permisiuni pentru <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Permisiuni suplimentare"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Permisiuni pentru <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ro-watch/strings.xml b/packages/PackageInstaller/res/values-ro-watch/strings.xml
new file mode 100644
index 0000000..ea699f3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ro-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Respingeți; nu se mai întreabă"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Afișează aplicațiile de sistem"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Nu se poate modifica"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Da"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Anulați"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ro/strings.xml b/packages/PackageInstaller/res/values-ro/strings.xml
new file mode 100644
index 0000000..3b13dab3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ro/strings.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Program de instalare a pachetelor"</string>
+    <string name="next" msgid="3057143178373252333">"Înainte"</string>
+    <string name="install" msgid="5896438203900042068">"Instalați"</string>
+    <string name="done" msgid="3889387558374211719">"Terminat"</string>
+    <string name="cancel" msgid="8360346460165114585">"Anulați"</string>
+    <string name="installing" msgid="8613631001631998372">"În curs de instalare..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Se instalează <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplicație instalată."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Doriți să instalați această aplicație? Aceasta va avea acces la:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Doriți să instalați această aplicație? Aplicația nu solicită un acces special."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Doriți să instalați o actualizare pentru această aplicație existentă? Datele existente nu vor fi pierdute. Aplicația actualizată va avea acces la:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Doriți să instalați o actualizare pentru această aplicație încorporată? Datele existente nu vor fi pierdute. Aplicația actualizată va avea acces la:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Doriți să instalați o actualizare pentru această aplicație existentă? Datele existente nu vor fi pierdute. Actualizarea nu are nevoie de acces special."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Doriți să instalați o actualizare pentru această aplicație încorporată? Datele existente nu vor fi pierdute. Actualizarea nu are nevoie de acces special."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Aplicația nu este instalată."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Instalarea pachetului a fost blocată."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Aplicația nu a fost instalată deoarece pachetul intră în conflict cu un pachet existent."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Aplicația nu a fost instalată deoarece nu este compatibilă cu tableta dvs."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Această aplicație nu este compatibilă cu televizorul dvs."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Aplicația nu a fost instalată deoarece nu este compatibilă cu telefonul dvs."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Aplicația nu a fost instalată deoarece se pare că pachetul este nevalid."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe tableta dvs."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe televizor."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe telefonul dvs."</string>
+    <string name="launch" msgid="4826921505917605463">"Deschideți"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Administratorul nu permite instalarea aplicațiilor obținute din surse necunoscute"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Aplicațiile necunoscute nu pot fi instalate de acest utilizator"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Acest utilizator nu are permisiunea să instaleze aplicații"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Gestionați aplicații"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Spațiu de stocare insuficient"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată. Eliberați spațiu și încercați din nou."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Aplicația nu a fost găsită"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Aplicația nu a fost găsită în lista de aplicații instalate."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Nu are permisiune"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Utilizatorul actual nu are permisiune pentru a face această dezinstalare."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Eroare"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Aplicația nu a putut fi dezinstalată."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Dezinstalați aplicația"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Dezinstalați actualizarea"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">" <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> face parte din următoarea aplicație:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Doriți să dezinstalați această aplicație?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Doriți să dezinstalați această aplicație pentru "<b>"toți"</b>" utilizatorii? Aplicația și datele acesteia vor fi eliminate de la "<b>"toți"</b>" utilizatorii de pe acest dispozitiv."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Dezinstalați această aplicație pentru utilizatorul <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Înlocuiți această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Î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="uninstalling_notification_channel" msgid="5698369661583525583">"Dezinstalări în curs"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Dezinstalări nereușite"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"În curs de dezinstalare..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Se dezinstalează <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Dezinstalare finalizată."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> a fost dezinstalat"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Dezinstalare nefinalizată."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> nu a putut fi dezinstalată."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Nu se poate dezinstala aplicația activă de administrare a dispozitivului"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Nu se poate dezinstala aplicația activă de administrare a dispozitivului pentru <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Aplicația este necesară unor utilizatori sau profiluri și a fost dezinstalată pentru alții"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Aplicația este necesară pentru profilul dvs. și nu poate fi dezinstalată."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Aplicație necesară administratorului dispozitivului. Nu poate fi dezinstalată."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Gestionați aplicațiile de administrare dispozitiv"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Gestionați utilizatorii"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi dezinstalată."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"A apărut o problemă la analizarea pachetului."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Noi"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Toate"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Confidențialitate"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Acces la dispozitiv"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Această actualizare nu necesită permisiuni noi."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Refuzați"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Mai multe informații"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Nu permiteți oricum"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> din <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Permiteți &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Permiteți întotdeauna &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Doar în timp ce folosiți aplicația"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Întotdeauna"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Respingeți și nu se mai întreabă"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> dezactivate"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"toate dezactivate"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"niciuna dezactivată"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Permiteți"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplicații"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Permisiuni pentru aplicație"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Nu mai întreba"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Fără permisiuni"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Permisiuni suplimentare"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Deschideți informațiile despre aplicații"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="few">Încă <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="other">Încă <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Încă <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Această aplicație a fost creată pentru o versiune Android mai veche. Dacă nu acordați permisiunea, este posibil ca aceasta să nu mai funcționeze corespunzător."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"efectuează o acțiune necunoscută"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> din <xliff:g id="COUNT_1">%2$d</xliff:g> aplicații au această permisiune"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Afișați aplicațiile de sistem"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Ascundeți aplicațiile de sistem"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Nicio aplicație"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Setări privind locația"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> este un furnizor de servicii de localizare pentru acest dispozitiv. Accesul la locație poate fi modificat din setările privind locația."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Dacă refuzați această permisiune, este posibil ca funcțiile de bază ale dispozitivului să nu mai funcționeze corespunzător."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Aplicată conform politicii"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Acces la fundal dezactivat de politică"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Acces la fundal activat de politică"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Acces la prim-plan activat de politică"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Controlat de administrator"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Întotdeauna"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Doar în timp ce folosiți aplicația"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Niciodată"</string>
+    <string name="loading" msgid="7811651799620593731">"Se încarcă..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Toate permisiunile"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Alte funcții ale aplicației"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Solicitare de permisiune"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"S-a detectat suprapunerea pe ecran"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Ca să schimbați această setare pentru permisiuni, mai întâi trebuie să dezactivați suprapunerea pe ecran din Setări &gt; Aplicații"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Deschideți setările"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Acțiunile Instalați/Dezinstalați nu sunt acceptate pe Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Alegeți ce va putea accesa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Aplicația &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; a fost actualizată. Alegeți ce va putea accesa această aplicație."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Anulați"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Continuați"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Permisiuni noi"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Permisiuni actuale"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Se pregătește aplicația…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Necunoscut"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Din motive de securitate, tableta dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Din motive de securitate, televizorul dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Din motive de securitate, telefonul dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Telefonul și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați aplicația, acceptați că sunteți singura persoană responsabilă pentru deteriorarea telefonului sau pentru pierderea datelor, care pot avea loc în urma utilizării acesteia."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Tableta și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați aplicația, acceptați că sunteți singura persoană responsabilă pentru deteriorarea tabletei sau pentru pierderea datelor, care pot avea loc în urma utilizării acesteia."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Televizorul și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați această aplicație, acceptați că sunteți singura persoană responsabilă pentru deteriorarea televizorului sau pentru pierderea datelor, care pot avea loc în urma utilizării acesteia."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Continuați"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Setări"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Se (dez)instalează aplicațiile Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-round-watch/dimens.xml b/packages/PackageInstaller/res/values-round-watch/dimens.xml
new file mode 100644
index 0000000..fa36464
--- /dev/null
+++ b/packages/PackageInstaller/res/values-round-watch/dimens.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+<resources>
+    <!-- Dimens for dialog layouts -->
+    <dimen name="diag_button_size">48dp</dimen>
+    <dimen name="diag_preferred_padding">@dimen/screen_percentage_15</dimen>
+    <dimen name="diag_button_padding_horizontal">@dimen/screen_percentage_15</dimen>
+    <dimen name="diag_button_padding_bottom">@dimen/screen_percentage_12</dimen>
+    <dimen name="diag_icon_margin_top">@dimen/screen_percentage_10</dimen>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ru-television/strings.xml b/packages/PackageInstaller/res/values-ru-television/strings.xml
new file mode 100644
index 0000000..e105f92
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ru-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Запретить и больше не спрашивать"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Чтобы изменить разрешения, откройте \"Настройки\" и выберите \"Приложения\"."</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Показать системные приложения"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Разрешения приложений"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Разрешения приложений"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Разрешения (<xliff:g id="PERMISSION">%1$s</xliff:g>)"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Ещё разрешения"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Разрешения (<xliff:g id="PERMISSION">%1$s</xliff:g>)"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ru-watch/strings.xml b/packages/PackageInstaller/res/values-ru-watch/strings.xml
new file mode 100644
index 0000000..f58db32
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ru-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Запретить и не спрашивать"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Показать системные приложения"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Нельзя изменить"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Да"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Отмена"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ru/strings.xml b/packages/PackageInstaller/res/values-ru/strings.xml
new file mode 100644
index 0000000..9ffdf4a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ru/strings.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Установщик пакетов"</string>
+    <string name="next" msgid="3057143178373252333">"Далее"</string>
+    <string name="install" msgid="5896438203900042068">"Установить"</string>
+    <string name="done" msgid="3889387558374211719">"Готово"</string>
+    <string name="cancel" msgid="8360346460165114585">"Отмена"</string>
+    <string name="installing" msgid="8613631001631998372">"Установка..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Устанавливаем <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Приложение установлено."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Хотите ли вы установить это приложение? Оно получит следующие разрешения:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Это приложение не требует специальных разрешений. Установить его?"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Хотите установить обновление для этого приложения? После обновления оно сможет выполнять следующие действия:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Хотите установить обновление для этого приложения? После обновления оно сможет выполнять следующие действия:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Установить обновление этого приложения? На текущих данных это никак не отразится. Специальных прав доступа не требуется."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Установить обновление этого встроенного приложения? На текущих данных это никак не отразится. Специальных прав доступа не требуется."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Приложение не установлено."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Установка пакета заблокирована."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Приложение не установлено, так как оно конфликтует с другим пакетом."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Приложение не установлено, так как оно несовместимо с вашим планшетом."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Приложение несовместимо с вашим телевизором."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Приложение не установлено, так как оно несовместимо с вашим телефоном."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Приложение не установлено, так как его пакет недействителен (например, поврежден)."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Не удалось установить приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" нельзя установить на ваш телевизор."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Не удалось установить приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string>
+    <string name="launch" msgid="4826921505917605463">"Открыть"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Ваш администратор запретил устанавливать приложения из неизвестных источников"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Этот пользователь не может устанавливать неизвестные приложения"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Этому пользователю не разрешено устанавливать приложения"</string>
+    <string name="ok" msgid="3468756155452870475">"ОК"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Управление приложениями"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Недостаточно места"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Не удалось установить приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\". Освободите место и повторите попытку."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Приложение не найдено"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Приложения нет в списке установленных."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Действие запрещено"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Этот пользователь не может удалить приложение."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Ошибка"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Не удалось удалить приложение."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Удаление приложения"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Удаление обновления"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> – часть следующего приложения:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Удалить приложение?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Удалить это приложение для "<b>"всех"</b>" пользователей? После этого "<b>"ни один"</b>" пользователь устройства не будет иметь доступа к приложению и связанным с ним данным."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Удалить это приложение из профиля <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Установить исходную версию приложения? Все его данные будут удалены."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Установить исходную версию приложения? Его данные будут удалены из всех профилей устройства, в том числе рабочих."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Активные процессы удаления"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Ошибки удаления"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Удаление..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Удаление приложения \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\"…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Удаление завершено."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Приложение \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\" удалено"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Ошибка при удалении."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Не удалось удалить приложение \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\"."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Невозможно удалить активное приложение для администрирования устройства"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Невозможно удалить активное приложение для администрирования устройства в профиле <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Это приложение обязательно для некоторых пользователей или профилей."</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Это приложение обязательно для вашего профиля. Его нельзя удалить."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Это приложение указано администратором как обязательное. Его нельзя удалить."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Настроить приложения для администрир. устройства"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Управление пользователями"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Не удалось удалить приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Ошибка при синтаксическом анализе пакета."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Новые"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Все"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Личные данные"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Доступ к устройству"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Установка этого обновления не требует новых разрешений."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Отклонить"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Подробнее"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Все равно запретить"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> из <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Всегда разрешать приложению \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Когда открыто приложение"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Всегда"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Запретить и больше не спрашивать"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"отключено: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"все отключены"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"все включены"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Разрешить"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Приложения"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Разрешения приложений"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Больше не спрашивать"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Нет разрешений"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Ещё разрешения"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"О приложении"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one">Ещё <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="few">Ещё <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="many">Ещё <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="old_sdk_deny_warning" msgid="3872277112584842615">"Это приложение было разработано для более ранней версии Android. Отзыв разрешения может вызвать неполадки в работе."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"выполняет неизвестные действия"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Приложений с разрешением: <xliff:g id="COUNT_0">%1$d</xliff:g> из <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Показать системные процессы"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Скрыть системные процессы"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Нет приложений"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Настройки геоданных"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> является поставщиком услуг геолокации для этого устройства. Вы можете изменить параметры доступа в настройках геоданных."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Если вы отключите это разрешение, основные функции устройства могут работать неправильно."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"В соответствии с политикой"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Доступ в фоновом режиме отключен в соответствии с правилами."</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Доступ в фоновом режиме включен в соответствии с правилами."</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Активный режим включен в соответствии с правилами."</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Контролируется администратором"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Всегда"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Когда открыто приложение"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Никогда"</string>
+    <string name="loading" msgid="7811651799620593731">"Загрузка…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Все разрешения"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Что ещё может приложение"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Запрос разрешений"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Показ поверх других окон"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Чтобы предоставить или отменить разрешение, сначала отключите показ поверх других окон. Для этого нажмите \"Настройки &gt; Приложения\"."</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Открыть настройки"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Установка и удаление не поддерживаются на Android Wear"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Выберите разрешения для приложения &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Приложение &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; обновлено. Выберите разрешения для него."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Отмена"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Далее"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Новые разрешения"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Имеющиеся разрешения"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Подождите…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Неизвестное приложение"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"В целях безопасности ваш планшет блокирует установку приложений из неизвестных источников."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"В целях безопасности ваш телевизор блокирует установку приложений из неизвестных источников."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"В целях безопасности ваш телефон блокирует установку приложений из неизвестных источников."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Ваши личные данные и данные телефона более уязвимы для атак приложений из неизвестных источников. Устанавливая это приложение, вы соглашаетесь с тем, что несете полную ответственность за любой ущерб, нанесенный телефону, и потерю данных, связанные с использованием этого приложения."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Ваши личные данные и данные планшета более уязвимы для атак приложений из неизвестных источников. Устанавливая это приложение, вы соглашаетесь с тем, что несете полную ответственность за любой ущерб, нанесенный планшету, и потерю данных, связанные с использованием этого приложения."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Ваши личные данные и данные телевизора более уязвимы для атак приложений из неизвестных источников. Устанавливая это приложение, вы соглашаетесь с тем, что несете полную ответственность за любой ущерб, нанесенный телевизору, и потерю данных, связанные с использованием этого приложения."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Продолжить"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Настройки"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Установка/удаление приложений для Android Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-si-television/strings.xml b/packages/PackageInstaller/res/values-si-television/strings.xml
new file mode 100644
index 0000000..2ac8d8b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-si-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"ප්‍රතික්ෂේප කරන්න, නැවත අසන්න එපා"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"ඔබට මෙය පසුව සැකසීම් &gt; යෙදුම් තුළ වෙනස් කළ හැකිය"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"පද්ධති යෙදුම් පෙන්වන්න"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"යෙදුම් අවසර"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"යෙදුම් අවසර"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> අවසර"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"අතිරේක අවසර"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> අවසර"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-si-watch/strings.xml b/packages/PackageInstaller/res/values-si-watch/strings.xml
new file mode 100644
index 0000000..c5d9ae4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-si-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"ප්‍රතික්ෂේප කරන්න, නැවත අසන්න එපා"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"පද්ධති යෙදුම් පෙන්වන්න"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"වෙනස් කළ නොහැකිය"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"ඔව්"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"අවලංගු කර."</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-si/strings.xml b/packages/PackageInstaller/res/values-si/strings.xml
new file mode 100644
index 0000000..18fc840
--- /dev/null
+++ b/packages/PackageInstaller/res/values-si/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"පැකේජ ස්ථාපනකරු"</string>
+    <string name="next" msgid="3057143178373252333">"මීලඟ"</string>
+    <string name="install" msgid="5896438203900042068">"ස්ථාපනය"</string>
+    <string name="done" msgid="3889387558374211719">"හරි"</string>
+    <string name="cancel" msgid="8360346460165114585">"අවලංගු කරන්න"</string>
+    <string name="installing" msgid="8613631001631998372">"ස්ථාපනය කරමින්…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ස්ථාපනය කරමින්…"</string>
+    <string name="install_done" msgid="3682715442154357097">"යෙදුම ස්ථාපනය කරන ලදි."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"ඔබට මෙම යෙදුම ස්ථාපනය කිරීමට අවශ්‍යද? පහත ඒවා වෙත එයට ප්‍රවේශය ලැබෙනු ඇත:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"මෙම යෙදුම ස්ථාපනය කිරීමට ඔබට අවශ්‍යද? වෙනත් විශේෂ ප්‍රවේශයක් එයට අවශ්‍ය නොවෙයි."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"දැනට පවතින මෙම යෙදුමට යාවත්කාලීනයක් ස්ථාපනය කිරීමට ඔබට අවශ්‍යද? ඔබගේ පවතින දත්ත නැති නොවේ. යාවත්කාලීන කළ යෙදුම පිවිසීම ලබා ගනී:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"ඔබට මෙම තිළැලි යෙදුමට යාවත්කාලීනය ස්ථාපනය කිරීමට අවශ්‍යද? ඔබගේ දැනට පවතින දත්ත නැති නොවේ. යාවත්කාලීන කරන ලද යෙදුම පහත සඳහා ප්‍රවේශය ලබාගනු ඇත:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"මෙම පවතින යෙදුමට යාවත්කාලීනයක් ස්ථාපනය කිරීමට ඔබට අවශ්‍යද? ඔබගේ පවතින දත්ත නැති නොවේ. එයට විශේෂ ප්‍රවේශයක් අවශ්‍ය නොවේ."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"පවතින මෙම යෙදුමට යාවත්කාලීනයක් ස්ථාපනය කිරීමට ඔබට අවශ්‍යද? ඔබගේ පවතින දත්ත නැති නොවේ. එයට විශේෂ ප්‍රවේශයක් අවශ්‍ය නොවේ."</string>
+    <string name="install_failed" msgid="6579998651498970899">"යෙදුම ස්ථාපනය කරේ නැත."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"මෙම පැකේජය ස්ථාපනය කිරීම අවහිර කරන ලදි."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"පැකේජය දැනට පවතින පැකේජයක් සමග ගැටෙන නිසා යෙදුම ස්ථාපනය නොකරන ලදී."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"යෙදුම ඔබේ ටැබ්ලට් පරිගණකය සමග නොගැළපෙන නිසා යෙදුම ස්ථාපනය නොකරන ලදී."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"මෙම යෙදුම ඔබගේ රූපවාහිනිය හා නොගැළපේ."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"යෙදුම ඔබේ දුරකථනය සමග නොගැළපෙන නිසා යෙදුම ස්ථාපනය නොකරන ලදී."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"පැකේජය වලංගු නොවන බවක් පෙනෙන නිසා යෙදුම ස්ථාපනය නොකරන ලදී."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"ඔබගේ ටැබ්ලටයේ <xliff:g id="APP_NAME">%1$s</xliff:g> ස්ථාපනය කළ නොහැක."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> මෙම රූපවාහිනියෙහි ස්ථාපනය කළ නොහැක."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> දුරකථනයට ස්ථාපිත කිරීමට නොහැකි විය."</string>
+    <string name="launch" msgid="4826921505917605463">"විවෘත කරන්න"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"නාඳුනන මූලයන් වෙතින් ලබාගත් යෙදුම් ස්ථාපනය කිරීමට ඔබගේ පරිපාලකයා ඉඩ නොදේ"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"මෙම පරිශීලකයා මඟින් නොදන්නා යෙදුම් ස්ථාපනය කළ නොහැක"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"මෙම පරිශීලකයාට යෙදුම් ස්ථාපනය කිරීමට අවසර නැත"</string>
+    <string name="ok" msgid="3468756155452870475">"හරි"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"යෙදුම් කළමනාකරණය කරන්න"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"ඉඩ නොමැත"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> ස්ථාපිත කිරීමට නොහැකි විය. ඉඩ පොඩ්ඩක් නිදහස් කොට නැවත උත්සාහ කරන්න."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"යෙදුම හමුවී නැත"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"ස්ථාපිත යෙදුම් ලැයිස්තුවේ යෙදුම සොයා ගත නොහැකි විය."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"ඉඩ නොදෙයි"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"වත්මන් පරිශීලකයාට මෙම අස්ථාපනය සිදු කිරීමට ඉඩ නොදේ."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"දෝෂය"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"යෙදුම අස්ථාපනය කළ නොහැකි විය."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"යෙදුම අස්ථාපනය කරන්න"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"යාවත්කාලිනය අස්ථාපනය කරන්න"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> පහත යෙදුමේ කොටසකි:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"ඔබට මෙම යෙදුම අස්ථාපනය කිරීමට අවශ්‍යද?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222"><b>"සියලු"</b>" පරිශීලකයන්  සඳහා මෙම යෙදුම අස්ථාපනය කිරීමට ඔබට අවශ්‍යද? උපාංගයෙහි "<b>"සියලු"</b>" පරිශීලකයන් සඳහා යෙදුම සහ එහි දත්ත ඉවත්වනු ඇත."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"<xliff:g id="USERNAME">%1$s</xliff:g> පරිශීලකයා සඳහා මෙම යෙදුම අස්ථාපනය කිරීමට ඔබට අවශ්‍යයද?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"මෙම යෙදුම කර්මාන්ත ශාලා අනුවාදයක් සමගින් ප්‍රතිස්ථාපනය කරන්නද? සියලු දත්ත ඉවත් කරනු ඇත."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"මෙම යෙදුම කර්මාන්ත ශාලා අනුවාදයක් සමගින් ප්‍රතිස්ථාපනය කරන්නද? සියලු දත්ත ඉවත් කරනු ඇත. මෙය කාර්යාල පැතිකඩවල් සහිත අය ඇතුළුව, මෙම උපාංගයෙහි සියලු පරිශීලකයන් වෙත බලපානු ඇත."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"අස්ථාපන ධාවනය කරමින්"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"අසාර්ථක වූ අස්ථාපන"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"අස්ථාපනය කරමින්…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> අස්ථාපනය කරමින්…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"අස්ථාපනය අවසන්."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> අස්ථාපනය කරන ලදී"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"අස්ථාපිත විම අසාර්ථකයි."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> අස්ථාපනය කිරීම සාර්ථකයි."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"ක්‍රියාකාරී උපාංගය පරිපාලක යෙදුම අස්ථාපනය කිරීමට නොහැක"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> සඳහා ක්‍රියාකාරී උපාංගය පරිපාලක යෙදුම අස්ථාපනය කිරීමට නොහැක"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"මෙම යෙදුම සමහර පරිශීලකයන්ට සහ පැතිකඩවල්වලට අවශ්‍ය අතර අනෙක් අයට අස්ථාපනය කරන ලදී"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"ඔබේ කාර්ය පැතිකඩ සඳහා මෙම යෙදුම අවශ්‍ය වන අතර අස්ථාපනය කළ නොහැකිය."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"ඔබගේ උපාංගයේ පාලකයාට මෙම යෙදුම අවශ්‍ය වේ එම නිසා අස්ථාපනය කළ නොහැක."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"උපාංග පරිපාලක යෙදුම් කළමනාකරණය කිරීම"</string>
+    <string name="manage_users" msgid="3125018886835668847">"පරිශීලකයන් කළමනාකරණය කරන්න"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> අස්ථාපනය කල නොහැක."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"පැකේජය විග්‍රහ කිරීමේදී ගැටළුවක් ඇති විය."</string>
+    <string name="newPerms" msgid="6039428254474104210">"අලුත්"</string>
+    <string name="allPerms" msgid="1024385515840703981">"සියල්ල"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"පෞද්ගලිකත්වය"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"උපාංගය ප්‍රවේශය"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"මෙම යාවත්කාලිනයට අලුත් අවසරයන් අවශ්‍ය නොවේ."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"ප්‍රතික්ෂේප කරන්න"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"වැඩිදුර තොරතුරු"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"කෙසේ වෙතත් ප්‍රතික්ෂේප කරන්න"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> න් <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ට <xliff:g id="ACTION">%2$s</xliff:g> වෙත ඉඩ දෙන්නද?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"සැම විට &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට <xliff:g id="ACTION">%2$s</xliff:g> වෙත ඉඩ දෙන්නද?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"යෙදුම භාවිතා කරන විට පමණි"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"සැම විට"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"ප්‍රතික්ෂේප කරන්න, නැවත අසන්න එපා"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> අබලයි"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"සියල්ල අබලයි"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"කිසිවක් අබල නැත"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"ඉඩ දෙන්න"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"යෙදුම්"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"යෙදුම් අවසර"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"නැවත අසන්න එපා"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"අවසර නොමැත"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"අතිරේක අවසර"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"යෙදුම් තොරතුරු විවෘත කරන්න"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <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="old_sdk_deny_warning" msgid="3872277112584842615">"මෙම යෙදුම නිර්මාණය කර ඇත්තේ Android වල පැරණි අනුවාදයකට වේ. අවසර නොදීම මඟින් එය බලාපොරොත්තු වන ආකාරයට වැඩ නොකිරීමට හැක."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"නොදන්නා ක්‍රියාවක් සිදු කරන්න"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"යෙදුම් <xliff:g id="COUNT_1">%2$d</xliff:g> න් <xliff:g id="COUNT_0">%1$d</xliff:g> කට ඉඩ දෙන ලදි"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"පද්ධතිය පෙන්වන්න"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"පද්ධතිය සඟවන්න"</string>
+    <string name="no_apps" msgid="1965493419005012569">"යෙදුම් නොමැත"</string>
+    <string name="location_settings" msgid="1774875730854491297">"ස්ථාන සැකසීම්"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> මෙම උපාංගය සඳහා ස්ථාන සේවාවන් සපයන්නෙකු වේ. ස්ථාන ප්‍රවේශය ස්ථාන සැකසීම් වෙතින් වෙනස් කළ හැක."</string>
+    <string name="system_warning" msgid="7103819124542305179">"ඔබ මෙම අවසරය ප්‍රතික්ෂේප කරන්නේ නම්, සමහර යෙදුම් බලාපොරොත්තු පරිදි ක්‍රියා නොකරනු ඇත."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"ප්‍රතිපත්තිය මඟින් බලාත්මක කරයි"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"පසුබිම් ප්‍රවේශය ප්‍රතිපත්තිය මගින් අබල කර ඇත"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"පසුබිම් ප්‍රවේශය ප්‍රතිපත්තිය මගින් සබල කර ඇත"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"පෙරබිම් ප්‍රවේශය ප්‍රතිපත්තිය මගින් සබල කර ඇත"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"පරිපාලක විසින් පාලනය කෙරේ"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"සැම විට"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"යෙදුම භාවිතා කරන විට පමණි"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"කිසි විටක නැත"</string>
+    <string name="loading" msgid="7811651799620593731">"පූරණය කරමින්…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"සියලු අවසර"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"වෙනත් යෙදුම් හැකියාවන්"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"අවසර ඉල්ලීම"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"තිර උඩැතිරියක් අනාවරණය කරන ලදි"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"මෙම අවසර සැකසීම වෙනස් කිරීම සඳහා, ඔබට මුලින්ම සැකසීම් &gt; යෙදුම් වෙතින් තිර උඩැතිරිය අක්‍රිය කර යුතුයි"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"සැකසීම් විවෘත කරන්න"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear මත ස්ථාපන/අස්ථාපනය ක්‍රියා සහාය දක්වන්නේ නැත."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට පිවිසීමට ඉඩ දෙන දේ තෝරන්න"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; යාවත්කාලීන කර ඇත. මෙම යෙදුමට පිවිසීමට ඉඩ දෙන දේ තෝරන්න."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"අවලංගු කරන්න"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"දිගටම කර ගෙන යන්න"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"නව අවසර"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"වත්මන් අවසර"</string>
+    <string name="message_staging" msgid="6151794817691100003">"යෙදුම වේදිකාගත කරමින්..."</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"නොදනී"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"ආරක්ෂාව සඳහා, ඔබගේ ටැබ්ලටය මෙම මුලාශ්‍රයෙන් ලබාගත් නොදන්නා යෙදුම් ස්ථාපනය කිරීමට අවසර නැත."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"ආරක්ෂාව සඳහා, ඔබගේ රූපවාහිනිය මෙම මුලාශ්‍රයෙන් ලබාගත් නොදන්නා යෙදුම් ස්ථාපනය කිරීමට අවසර නැත."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"ආරක්ෂාව සඳහා, ඔබගේ දුරකථනය මෙම මුලාශ්‍රයෙන් ලබාගත් නොදන්නා යෙදුම් ස්ථාපනය කිරීමට අවසර නැත."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"ඔබගේ දුරකථනය සහ පුද්ගලික දත්තවලට නොදන්නා යෙදුම් මඟින් තර්ජන එල්ල කිරීමේ හැකියාව වැඩිය. මෙම යෙදුම් ස්ථාපනය කිරීමෙන් සහ භාවිත කිරීමෙන් ඔබ ඔබේ දුරකථනය සඳහා සිදු වන යම් හානි හෝ එය භාවිත කිරීමේ ප්‍රතිඵලයක් ලෙස සිදු වන දත්ත හානි සඳහා ඔබ වගකිව යුතු බවට එකඟ වේ."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"ඔබගේ ටැබ්ලට් පරිගණකය සහ පුද්ගලික දත්තවලට නොදන්නා යෙදුම් මඟින් තර්ජන එල්ල කිරීමේ හැකියාව වැඩිය. මෙම යෙදුම් ස්ථාපනය කිරීමෙන් සහ භාවිත කිරීමෙන් ඔබ ඔබේ ටැබ්ලට් පරිගණකය සඳහා සිදු වන යම් හානි හෝ එය භාවිත කිරීමේ ප්‍රතිඵලයක් ලෙස සිදු වන දත්ත හානි සඳහා ඔබ වගකිව යුතු බවට එකඟ වේ."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"ඔබගේ TV සහ පුද්ගලික දත්තවලට නොදන්නා යෙදුම් මඟින් තර්ජන එල්ල කිරීමේ හැකියාව වැඩිය. මෙම යෙදුම් ස්ථාපනය කිරීමෙන් සහ භාවිත කිරීමෙන් ඔබ ඔබේ TV සඳහා සිදු වන යම් හානි හෝ එය භාවිත කිරීමේ ප්‍රතිඵලයක් ලෙස සිදු වන දත්ත හානි සඳහා ඔබ වගකිව යුතු බවට එකඟ වේ."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"දිගටම කරගෙන යන්න"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"සැකසීම්"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear යෙදුම් ස්ථාපනය/අස්ථාපනය කරමින්"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sk-television/strings.xml b/packages/PackageInstaller/res/values-sk-television/strings.xml
new file mode 100644
index 0000000..15ecdb8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sk-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Zamietnuť a nabudúce sa nepýtať"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Neskôr to môžete zmeniť v časti Nastavenia &gt; Aplikácie"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Zobraziť systémové aplikácie"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Povolenia aplikácie"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Povolenia aplikácie"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Povolenia – <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Ďalšie povolenia"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Povolenia – <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sk-watch/strings.xml b/packages/PackageInstaller/res/values-sk-watch/strings.xml
new file mode 100644
index 0000000..dc3ce4f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sk-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Zamietnuť a už sa nepýtať"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Zobraziť systémové aplikácie"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Nedá sa zmeniť"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Áno"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Zrušiť"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sk/strings.xml b/packages/PackageInstaller/res/values-sk/strings.xml
new file mode 100644
index 0000000..a27f650
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sk/strings.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Nástroj na inštaláciu balíčkov"</string>
+    <string name="next" msgid="3057143178373252333">"Ďalej"</string>
+    <string name="install" msgid="5896438203900042068">"Inštalovať"</string>
+    <string name="done" msgid="3889387558374211719">"Hotovo"</string>
+    <string name="cancel" msgid="8360346460165114585">"Zrušiť"</string>
+    <string name="installing" msgid="8613631001631998372">"Inštaluje sa..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Inštaluje sa <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplikácia bola nainštalovaná."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Chcete nainštalovať túto aplikáciu? Získa nasledujúce povolenia:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Chcete nainštalovať túto aplikáciu? Nevyžaduje žiadny zvláštny prístup."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Chcete nainštalovať aktualizáciu existujúcej aplikácie? Existujúce údaje sa nestratia. Aktualizovaná aplikácia získa nasledujúce povolenia:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Chcete nainštalovať aktualizáciu tejto integrovanej aplikácie? Existujúce údaje sa nestratia. Aktualizovaná aplikácia získa nasledujúce povolenia:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Chcete nainštalovať aktualizáciu tejto existujúcej aplikácie? Vaše údaje nebudú stratené. Táto akcia nevyžaduje žiadny zvláštny prístup."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Chcete nainštalovať aktualizáciu tejto vstavanej aplikácie? Vaše údaje sa nestratia. Táto akcia nevyžaduje žiadny zvláštny prístup."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Aplikácia nebola nainštalovaná."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Inštalácia balíka bola zablokovaná."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Aplikácia sa nenainštalovala, pretože balík koliduje s existujúcim balíkom."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Aplikácia sa nenainštalovala, pretože nie je kompatibilná s vaším tabletom."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Táto aplikácia nie je kompatibilná s vaším televízorom."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Aplikácia sa nenainštalovala, pretože nie je kompatibilná s vaším telefónom."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Aplikácia sa nenainštalovala, pretože je balík zrejme neplatný."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> sa do vášho tabletu nepodarilo nainštalovať."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> sa nepodarilo nainštalovať na vašom televízore."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> sa do vášho telefónu nepodarilo nainštalovať."</string>
+    <string name="launch" msgid="4826921505917605463">"Otvoriť"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Váš správca zakázal inštaláciu aplikácií z neznámych zdrojov"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Tento používateľ nemôže inštalovať neznáme aplikácie"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Tento používateľ nemá povolené inštalovať aplikácie"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Spravovať aplikácie"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Nedostatok miesta"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> sa nepodarilo nainštalovať. Uvoľnite miesto v pamäti a skúste to znova."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Aplikácia sa nenašla"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Aplikáciu sa nepodarilo nájsť v zozname nainštalovaných aplikácií."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Nie je povolené"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Aktuálny používateľ nemá na odinštalovanie povolenie."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Chyba"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Aplikáciu nie je možné odinštalovať."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Odinštalovať aplikáciu"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Odinštalovať aktualizáciu"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"Aktivita <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je súčasťou nasledujúcej aplikácie:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Chcete túto aplikáciu odinštalovať?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Chcete odinštalovať túto aplikáciu pre "<b>"všetkých"</b>" používateľov? Aplikácia a jej údaje sa odstránia z tohto zariadenia pre "<b>"všetkých"</b>" používateľov."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"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="1394549691152728409">"Nahradiť túto aplikáciu továrenskou verziou? Všetky údaje sa odstránia."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Nahradiť túto aplikáciu továrenskou 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="uninstalling_notification_channel" msgid="5698369661583525583">"Prebiehajúce odinštalácie"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Neúspešné odinštalácie"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Prebieha odinštalovanie..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Prebieha odinštalovanie balíčka <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Odinštalovanie bolo dokončené."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Balíček <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> bol odinštalovaný"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Odinštalovanie bolo neúspešné."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Odinštalovanie balíčka <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> sa nepodarilo."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Aktívna aplikácia na správu zariadenia sa nedá odinštalovať"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Aktívna aplikácia na správu zariadenia sa v prípade používateľa <xliff:g id="USERNAME">%1$s</xliff:g> nedá odinštalovať"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Táto aplikácia sa vyžaduje v prípade niektorých používateľov či profilov a v prípade iných zase bola odinštalovaná"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Táto aplikácia sa vyžaduje pre váš profil a nemôžete ju odinštalovať."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Túto aplikáciu vyžaduje správca vášho zariadenia a nie je ju možné odinštalovať."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Spravovať aplikácie na ovládanie zariadenia"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Spravovať používateľov"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> sa nepodarilo odinštalovať."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Pri analýze balíka sa vyskytol problém."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nové"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Všetko"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Ochrana súkromia"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Prístup k zariadeniu"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Táto aktualizácia nevyžaduje žiadne nové povolenia."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Odmietnuť"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Ďalšie informácie"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Zamietnuť"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> z <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Vždy povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Iba počas používania aplikácie"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Vždy"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Zamietnuť a nabudúce sa nepýtať"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"deaktivované (<xliff:g id="COUNT">%1$d</xliff:g>)"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"všetky sú zakázané"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"žiadne nie sú zakázané"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Povoliť"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplikácie"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Povolenia aplikácií"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Nabudúce sa nepýtať"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Žiadne povolenia"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Ďalšie povolenia"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Otvoriť informácie o aplikácii"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> ďalšie</item>
+      <item quantity="many"><xliff:g id="COUNT_1">%1$d</xliff:g> ďalšieho</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ďalších</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ďalšie</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Táto aplikácia bola navrhnutá pre staršiu verziu systému Android. Odmietnutie povolenia môže spôsobiť, že nebude optimálne fungovať."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"umožňuje vykonať neznámu akciu"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Povolené <xliff:g id="COUNT_0">%1$d</xliff:g> z <xliff:g id="COUNT_1">%2$d</xliff:g> aplikácií"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Zobraziť systémové aplikácie"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Skryť systémové aplikácie"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Žiadne aplikácie"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Nastavenia polohy"</string>
+    <string name="location_warning" msgid="8778701356292735971">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je poskytovateľ služieb určovania polohy tohto zariadenia. Prístup k polohe môžete upraviť v nastaveniach polohy."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Ak toto povolenie zamietnete, základné funkcie vášho zariadenia nemusia pracovať podľa očakávaní."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Vynútené pravidlom"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Prístup na pozadí je zakázaný pravidlom"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Prístup na pozadí je povolený pravidlom"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Prístup na popredí je povolený pravidlom"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Ovládané správcom"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Vždy"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Iba počas používania aplikácie"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nikdy"</string>
+    <string name="loading" msgid="7811651799620593731">"Načítava sa…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Všetky povolenia"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Ďalšie možnosti aplikácie"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Žiadosť o povolenie"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Bolo zistené prekrytie obrazovky"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Ak chcete zmeniť nastavenie tohto povolenia, musíte najprv v časti Nastavenia &gt; Aplikácie vypnúť prekrytie obrazovky"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Otvoriť nastavenia"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Systém Wear nepodporuje akciu inštalácie/odinštalovania."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Vyberte, k čomu môže pristupovať aplikácia &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Aplikácia &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bola aktualizovaná. Vyberte, k čomu môže pristupovať."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Zrušiť"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Pokračovať"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nové povolenia"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Aktuálne povolenia"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Aplikácia je zavádzaná po etapách…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Neznáma"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Váš tablet nemôže z bezpečnostných dôvodov inštalovať neznáme aplikácie z tohto zdroja."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Váš televízor nemôže z bezpečnostných dôvodov inštalovať neznáme aplikácie z tohto zdroja."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Váš telefón nemôže z bezpečnostných dôvodov inštalovať neznáme aplikácie z tohto zdroja."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Váš telefón a osobné údaje sú náchylnejšie na útok z neznámych aplikácií. Inštalovaním tejto aplikácie súhlasíte, že zodpovedáte za akékoľvek poškodenie vášho telefónu či stratu údajov, ku ktorým môže dôjsť v dôsledku jej použitia."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Váš tablet a osobné údaje sú náchylnejšie na útok z neznámych aplikácií. Inštalovaním tejto aplikácie súhlasíte, že zodpovedáte za akékoľvek poškodenie vášho tabletu či stratu údajov, ku ktorým môže dôjsť v dôsledku jej použitia."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Váš televízor a osobné údaje sú náchylnejšie na útok z neznámych aplikácií. Inštalovaním tejto aplikácie súhlasíte, že zodpovedáte za akékoľvek poškodenie vášho televízora či stratu údajov, ku ktorým môže dôjsť v dôsledku jej použitia."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Pokračovať"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Nastavenia"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Inštalácia/odinštalácia aplikácií Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sl-television/strings.xml b/packages/PackageInstaller/res/values-sl-television/strings.xml
new file mode 100644
index 0000000..3e8e4ea
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sl-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Zavrni in ne sprašuj več"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"To lahko pozneje spremenite v »Nastavitve &gt; Aplikacije«"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Prikaz sistemskih aplikacij"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Dovoljenja za aplikacije"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Dovoljenja za aplikacije"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Dovoljenja za: <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Dodatna dovoljenja"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Dovoljenja za: <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sl-watch/strings.xml b/packages/PackageInstaller/res/values-sl-watch/strings.xml
new file mode 100644
index 0000000..906a551
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sl-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Zavrni, ne sprašuj več"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Prikaz sistemskih aplikacij"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Ni mogoče sprem."</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Da"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Prekliči"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sl/strings.xml b/packages/PackageInstaller/res/values-sl/strings.xml
new file mode 100644
index 0000000..4075006
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sl/strings.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Namestitveno orodje za paket"</string>
+    <string name="next" msgid="3057143178373252333">"Naprej"</string>
+    <string name="install" msgid="5896438203900042068">"Namesti"</string>
+    <string name="done" msgid="3889387558374211719">"Dokončano"</string>
+    <string name="cancel" msgid="8360346460165114585">"Prekliči"</string>
+    <string name="installing" msgid="8613631001631998372">"Nameščanje …"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Nameščanje aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> …"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplikacija je nameščena."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Ali želite namestiti to aplikacijo? Imela bo dostop do:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Ali želite namestiti to aplikacijo? Poseben dostop ni potreben."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Ali želite namestiti posodobitev te obstoječe aplikacije? Obstoječi podatki ne bodo izgubljeni. Posodobljena aplikacija bo imela dostop do:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Ali želite namestiti posodobitev za to vgrajeno aplikacijo? Obstoječi podatki ne bodo izgubljeni. Posodobljena aplikacija bo imela dostop do:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Ali želite namestiti posodobitev te obstoječe aplikacije? Obstoječi podatki ne bodo izgubljeni. Za namestitev ne potrebujete posebnega dostopa."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Ali želite namestiti posodobitev te vgrajene aplikacije? Obstoječi podatki ne bodo izgubljeni. Za namestitev ne potrebujete posebnega dostopa."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Aplikacija ni nameščena."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Namestitev paketa je bila blokirana."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Aplikacija ni bila nameščena, ker je paket v navzkrižju z obstoječim paketom."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Aplikacija ni bila nameščena, ker ni združljiva s tabličnim računalnikom."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Ta aplikacija ni združljiva z vašim televizorjem."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Aplikacija ni bila nameščena, ker ni združljiva s telefonom."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Aplikacija ni bila nameščena, ker paket verjetno ni veljaven."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> ni bilo mogoče namestiti v tablični računalnik."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> ni bilo mogoče namestiti v vašem televizorju."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> ni bilo mogoče namestiti v telefon."</string>
+    <string name="launch" msgid="4826921505917605463">"Odpri"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Skrbnik ne dovoli nameščanja aplikacij iz neznanih virov."</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Ta uporabnik nima dovoljenja za nameščanje neznanih aplikacij"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Ta uporabnik nima dovoljenja za nameščanje aplikacij"</string>
+    <string name="ok" msgid="3468756155452870475">"V redu"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Upravljaj aplikacije"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Zmanjkalo je prostora"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> ni bilo mogoče namestiti. Sprostite prostor in poskusite znova."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Aplikacije ni bilo mogoče najti"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Aplikacije ni bilo mogoče najti na seznamu nameščenih aplikacij."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Ni dovoljeno"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Trenutni uporabnik nima dovoljenja za izvedbo te odstranitve."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Napaka"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Aplikacije ni bilo mogoče odstraniti."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Odstrani aplikacijo"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Odstrani posodobitev"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je del te aplikacije:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Ali želite odstraniti to aplikacijo?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Ali želite odstraniti aplikacijo za "<b>"vse"</b>" uporabnike? Aplikacija in njeni podatki bodo odstranjeni iz "<b>"vseh"</b>" uporabnikov v napravi."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Ali želite to aplikacijo odstraniti za uporabnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Želite to aplikacijo nadomestiti s tovarniško različico? Odstranjeni bodo vsi podatki."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Ž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="uninstalling_notification_channel" msgid="5698369661583525583">"Odstranitve v teku"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Neuspele odstranitve"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Odstranjevanje ..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Odstranjevanje aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> …"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Odstranitev je končana."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Aplikacija <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> je bila odstranjena"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Odstranitev ni uspela."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Odstranjevanje aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ni uspelo."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Aktivne skrbniške aplikacije naprave ni mogoče odstraniti"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Aktivne skrbniške aplikacije za uporabnika <xliff:g id="USERNAME">%1$s</xliff:g> ni mogoče odstraniti"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Aplikacija je obvezna za nekatere uporabnike/profile in je odstranjena za druge."</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Ta aplikacija je potrebna za profil in je ni mogoče odstraniti."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"To aplikacijo zahteva skrbnik naprave in je ni mogoče odstraniti."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Upravljanje skrbniških aplikacij naprave"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Upravljanje uporabnikov"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> ni bilo mogoče odstraniti."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Težava pri razčlenjevanju paketa."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Novo"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Vse"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Zasebnost"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Dostop do naprave"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Za to posodobitev niso potrebna nova dovoljenja."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Zavrni"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Več informacij"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Vseeno zavrni"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> od <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Ali dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; izvesti to dejanje: <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; vedno dovoliti to dejanje: <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Samo med uporabo aplikacije"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Vedno"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Zavrni in ne sprašuj več"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"št. onemogočenih: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"vse onemogočeno"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"nič ni onemogočeno"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Dovoli"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplikacije"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Dovoljenja za aplikacije"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Ne sprašuj več"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Ni dovoljenj"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Dodatna dovoljenja"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Odpri podatke o aplikaciji"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one">Še <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="two">Še <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="few">Še <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="other">Še <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Ta aplikacija je bila zasnovana za starejšo različico sistema Android. Če dovoljenje zavrnete, lahko preneha delovati, kot bi morala."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"izvedba neznanega dejanja"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Dovoljene aplikacije: <xliff:g id="COUNT_0">%1$d</xliff:g> od <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Prikaz sistemskih aplikacij"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Skrivanje sistemskih aplikacij"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Ni aplikacij"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Nastavitve lokacije"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> je ponudnik lokacijskih storitev za to napravo. Dostop do lokacije je mogoče spremeniti v nastavitvah lokacije."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Če zavrnete to dovoljenje, osnovne funkcije naprave morda ne bodo več delovale, kot bi morale."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Uveljavlja pravilnik"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Dostop iz ozadja je onemogočen s pravilnikom"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Dostop iz ozadja je omogočen s pravilnikom"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Dostop v ospredju je omogočen s pravilnikom"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Nadzira skrbnik"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Vedno"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Samo med uporabo aplikacije"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Nikoli"</string>
+    <string name="loading" msgid="7811651799620593731">"Nalaganje …"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Vsa dovoljenja"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Druge zmožnosti aplikacije"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Zahteva za dovoljenje"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Zaznano prekrivanje zaslona"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Če želite spremeniti nastavitev tega dovoljenja, morate najprej izklopiti prekrivanje zaslona v »Nastavitve &gt; Aplikacije«"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Odpri nastavitve"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Dejanja namestitve in odstranitve v sistemu Android Wear niso podprta."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Izberite, do česa aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovolite dostop"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; je posodobljena. Izberite, do česa tej aplikaciji dovolite dostop."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Prekliči"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Naprej"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nova dovoljenja"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Trenutna dovoljenja"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Priprava aplikacije …"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Neznano"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Vaš tablični računalnik zaradi varnosti nima dovoljenja za nameščanje neznanih aplikacij iz tega vira."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Vaš televizor zaradi varnosti nima dovoljenja za nameščanje neznanih aplikacij iz tega vira."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Vaš telefon zaradi varnosti nima dovoljenja za nameščanje neznanih aplikacij iz tega vira."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Neznane aplikacije lahko resno ogrozijo varnost telefona in osebnih podatkov. Z namestitvijo te aplikacije se strinjate, da ste sami odgovorni za morebitno škodo, nastalo v telefonu, ali izgubo podatkov, do katerih lahko pride zaradi uporabe te aplikacije."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Neznane aplikacije lahko resno ogrozijo varnost tabličnega računalnika in osebnih podatkov. Z namestitvijo te aplikacije se strinjate, da ste sami odgovorni za morebitno škodo, nastalo v tabličnem računalniku, ali izgubo podatkov, do katerih lahko pride zaradi uporabe te aplikacije."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Neznane aplikacije lahko resno ogrozijo varnost televizorja in osebnih podatkov. Z namestitvijo te aplikacije se strinjate, da ste sami odgovorni za morebitno škodo, nastalo v televizorju, ali izgubo podatkov, do katerih lahko pride zaradi uporabe te aplikacije."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Nadaljuj"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Nastavitve"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Nameščanje/odstranjev. aplikacij za Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sq-television/strings.xml b/packages/PackageInstaller/res/values-sq-television/strings.xml
new file mode 100644
index 0000000..d66231c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sq-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Refuzo dhe mos pyet përsëri"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Këtë mund ta ndryshosh më vonë te Cilësimet &gt; Aplikacionet"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Shfaq aplikacionet e sistemit"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Lejet e aplikacionit"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Lejet e aplikacionit"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Lejet për <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Lejet shtesë"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Lejet për <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sq-watch/strings.xml b/packages/PackageInstaller/res/values-sq-watch/strings.xml
new file mode 100644
index 0000000..772bb7a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sq-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Refuzoje, mos pyet sërish"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Shfaq aplikacionet e sistemit"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Nuk mund të ndryshohet"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Po"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Anulo"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sq/strings.xml b/packages/PackageInstaller/res/values-sq/strings.xml
new file mode 100644
index 0000000..8f7e0fc
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sq/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Instaluesi i paketës"</string>
+    <string name="next" msgid="3057143178373252333">"Përpara"</string>
+    <string name="install" msgid="5896438203900042068">"Instalo"</string>
+    <string name="done" msgid="3889387558374211719">"U krye!"</string>
+    <string name="cancel" msgid="8360346460165114585">"Anulo"</string>
+    <string name="installing" msgid="8613631001631998372">"Po instalon…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Po instalon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Aplikacioni u instalua."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Dëshiron ta instalosh këtë aplikacion? Ai do të ketë qasje në:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Dëshiron ta instalosh këtë aplikacion? Nuk kërkon ndonjë qasje të veçantë."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Dëshiron të instalosh një përditësim në këtë aplikacion ekzistues? Të dhënat e tua ekzistuese nuk do të humbin. Aplikacioni i përditësuar do të ketë qasje në:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Dëshiron të instalosh një përditësim në këtë aplikacion të integruar? Të dhënat e tua ekzistuese nuk do të humbin. Aplikacioni i përditësuar do të ketë qasje në:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Dëshiron të instalosh një përditësim të këtij aplikacioni ekzistues? Të dhënat e tua ekzistuese nuk do të humbasin. Aplikacioni nuk kërkon ndonjë qasje të veçantë."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Dëshiron të instalosh një përditësim të këtij aplikacioni ekzistues? Të dhënat e tua ekzistuese nuk do të humbasin. Aplikacioni nuk kërkon ndonjë qasje të veçantë."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Aplikacioni nuk u instalua."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Instalimi paketës u bllokua."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Aplikacioni nuk u instalua pasi paketa është në konflikt me një paketë ekzistuese."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Aplikacioni nuk u instalua pasi nuk është i përputhet me tabletin tënd."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Ky aplikacion është i papërshtatshëm me televizorin tënd."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Aplikacioni nuk u instalua pasi nuk përputhet me telefonin tënd."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Aplikacioni nuk u instalua pasi paketa duket se nuk është e vlefshme."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk mund të instalohej në tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk mund të instalohej në televizor."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk mundi të instalohej në telefon."</string>
+    <string name="launch" msgid="4826921505917605463">"Hap"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Administratori nuk lejon instalimin e aplikacioneve nga burime të panjohura."</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Aplikacionet e panjohura nuk mund të instalohen nga ky përdorues"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Ky përdorues nuk lejohet të instalojë aplikacione"</string>
+    <string name="ok" msgid="3468756155452870475">"Në rregull"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Menaxho aplikacionet"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Nuk ka hapësirë"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk mund të instalohej. Liro pak hapësirë dhe provo përsëri."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Aplikacioni nuk u gjet"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Aplikacioni nuk u gjet në listën e aplikacioneve të instaluara."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Nuk lejohet"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Përdoruesi aktual nuk lejohet të kryejë këtë çinstalim."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Gabim"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Aplikacioni nuk mund të instalohej."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Çinstalo aplikacionin"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Çinstalo përditësimin"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> është pjesë e aplikacionit të mëposhtëm:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Dëshiron ta çinstalosh këtë aplikacion?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Dëshiron ta çinstalosh këtë aplikacion për "<b>"të gjithë"</b>" përdoruesit? Aplikacioni dhe të dhënat e tij do të hiqen nga "<b>"të gjithë"</b>" përdoruesit e pajisjes."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"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="1394549691152728409">"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="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Çinstalimet në ekzekutim"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Çinstalimet e dështuara"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Po e çinstalon…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> po çinstalohet…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Çinstalimi përfundoi."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> u çinstalua"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Çinstalimi nuk pati sukses."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Çinstalimi i <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> nuk u krye me sukses."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Nuk mund të çinstalohet aplikacioni aktiv i administratorit të pajisjes"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Nuk mund të çinstalohet aplikacioni aktiv i administratorit të pajisjes për <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Ky aplikacion kërkohet për disa përdorues ose profile dhe është çinstaluar për të tjerët"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Ky aplikacion nevojitet për profilin tënd dhe nuk mund të çinstalohet."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Ky aplikacion kërkohet nga administratori i pajisjes dhe nuk mund të çinstalohet."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Menaxho aplikacionet e administratorit të pajisjes"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Menaxho përdoruesit"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk mundi të çinstalohej."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Kishte një problem me analizimin e paketës."</string>
+    <string name="newPerms" msgid="6039428254474104210">"E re"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Të gjitha"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privatësia"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Qasja në pajisje"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Ky përditësim nuk kërkon leje të reja."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Refuzo"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Informacione të tjera"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Refuzo sidoqoftë"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> nga <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Të lejohet &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; që të <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Të lejohet gjithmonë &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; që <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Vetëm gjatë përdorimit të aplikacionit"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Gjithmonë"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Refuzo dhe mos pyet përsëri"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> të çaktivizuara"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"të gjitha të çaktivizuara"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"asnjë e çaktivizuar"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Lejo"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Aplikacionet"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Lejet e aplikacionit"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Mos pyet përsëri"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Nuk ka leje"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Lejet shtesë"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Hap informacionet e aplikacionit"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> të tjera</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> të tjera</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Ky aplikacion është projektuar për një version më të vjetër të Android. Refuzimi i lejeve mund të shkaktojë që ai të mos funksionojë më siç duhet."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"kryej një veprim të panjohur"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> aplikacione nga <xliff:g id="COUNT_1">%2$d</xliff:g> të tilla u lejuan"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Shfaq sistemin"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Fshih sistemin"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Asnjë aplikacion"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Cilësimet e vendndodhjeve"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> është një ofrues i shërbimeve të vendndodhjes për këtë pajisje. Qasja e vendndodhjes mund të modifikohet nga cilësimet e vendndodhjes."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Nëse e refuzon këtë leje, funksionet bazë të pajisjes tënde mund të mos funksionojnë më siç pritet."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Zbatuar nga politika"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Qasja në sfond është e çaktivizuar sipas politikës"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Qasja në sfond është e aktivizuar sipas politikës"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Qasja në planin e parë është e aktivizuar sipas politikës"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Kontrolluar nga administratori"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Gjithmonë"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Vetëm gjatë përdorimit të aplikacionit"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Asnjëherë"</string>
+    <string name="loading" msgid="7811651799620593731">"Po ngarkon..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Të gjitha lejet"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Kapacitete të tjera të aplikacionit"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Kërkesa e lejes"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Mbivendosja e ekranit u zbulua"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Për të ndryshuar këtë cilësim të lejes, në fillim duhet të çaktivizosh mbivendosjen e ekranit nga Cilësimet &gt; Aplikacionet"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Hap cilësimet"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Teknologjia \"Android\" që vishet"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Instalo/çinstalo veprimet që nuk mbështeten në teknologjinë që vishet."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Zgjidh se ku do të lejohet të ketë qasje &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; është përditësuar. Zgjidh se ku do të lejohet të ketë qasje ky aplikacion."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Anulo"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Vazhdo"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Lejet e reja"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Lejet aktuale"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Po vihet në përdorim aplikacioni..."</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"E panjohur"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Për sigurinë tënde, tableti yt nuk lejohet të instalojë aplikacione të panjohura nga ky burim."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Për sigurinë tënde, televizori yt nuk lejohet të instalojë aplikacione të panjohura nga ky burim."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Për sigurinë tënde, telefoni yt nuk lejohet të instalojë aplikacione të panjohura nga ky burim."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Telefoni dhe të dhënat e tua personale janë më të cenueshme për t\'u sulmuar nga aplikacione të panjohura. Duke instaluar këtë aplikacion, ti pranon se je përgjegjës për çdo dëm ndaj telefonit tënd ose çdo humbje të dhënash që mund të rezultojë nga përdorimi i tij."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Tableti dhe të dhënat e tua personale janë më të cenueshme për t\'u sulmuar nga aplikacione të panjohura. Duke instaluar këtë aplikacion, ti pranon se je përgjegjës për çdo dëm ndaj tabletit tënd ose çdo humbje të dhënash që mund të rezultojë nga përdorimi i tij."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Televizori dhe të dhënat e tua personale janë më të cenueshme për t\'u sulmuar nga aplikacione të panjohura. Duke instaluar këtë aplikacion, ti pranon se je përgjegjës për çdo dëm ndaj televizorit tënd ose çdo humbje të dhënash që mund të rezultojë nga përdorimi i tij."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Vazhdo"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Cilësimet"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Instalimi/çinstalimi i aplikacioneve të \"Wear\""</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sr-television/strings.xml b/packages/PackageInstaller/res/values-sr-television/strings.xml
new file mode 100644
index 0000000..fcfe156
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sr-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Одбиј и не питај поново"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Ово можете да промените касније у Подешавањима &gt; Апликације"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Прикажи системске апликације"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Дозволе за апликације"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Дозволе за апликације"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Дозволе за апликацију <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Додатне дозволе"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Дозволе за апликацију <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sr-watch/strings.xml b/packages/PackageInstaller/res/values-sr-watch/strings.xml
new file mode 100644
index 0000000..7e15b01
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sr-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Одбиј и не питај поново"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Прикажи системске апликације"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Не може да се промени"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Да"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Откажи"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sr/strings.xml b/packages/PackageInstaller/res/values-sr/strings.xml
new file mode 100644
index 0000000..494b0d4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sr/strings.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Упаковани програм за инсталацију"</string>
+    <string name="next" msgid="3057143178373252333">"Даље"</string>
+    <string name="install" msgid="5896438203900042068">"Инсталирај"</string>
+    <string name="done" msgid="3889387558374211719">"Готово"</string>
+    <string name="cancel" msgid="8360346460165114585">"Откажи"</string>
+    <string name="installing" msgid="8613631001631998372">"Инсталирање..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Инсталира се <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Апликација је инсталирана."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Желите ли да инсталирате ову апликацију? Имаће приступ следећем:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Желите ли да инсталирате ову апликацију? Не захтева посебан приступ."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Желите ли да инсталирате ажурирање за ову постојећу апликацију? Постојећи подаци неће бити изгубљени. Ажурирана апликација имаће приступ следећем:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Желите ли да инсталирате ажурирање за ову уграђену апликацију? Постојећи подаци неће бити изгубљени. Ажурирана апликација ће имати приступ следећем:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Да ли желите да инсталирате ажурирање ове постојеће апликације? Постојећи подаци неће бити изгубљени. Није потребан посебан приступ."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Да ли желите да инсталирате ажурирање ове уграђене апликације? Постојећи подаци неће бити изгубљени. Није потребан посебан приступ."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Апликација није инсталирана."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Инсталирање пакета је блокирано."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Апликација није инсталирана јер је пакет неусаглашен са постојећим пакетом."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Апликација није инсталирана јер није компатибилна са таблетом."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Ова апликација није компатибилна са ТВ-ом."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Апликација није инсталирана јер није компатибилна са телефоном."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Апликација није инсталирана јер је пакет неважећи."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Није могуће инсталирати апликацију <xliff:g id="APP_NAME">%1$s</xliff:g> на таблет."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Нисмо успели да инсталирамо <xliff:g id="APP_NAME">%1$s</xliff:g> на ТВ."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Није могуће инсталирати апликацију <xliff:g id="APP_NAME">%1$s</xliff:g> на телефон."</string>
+    <string name="launch" msgid="4826921505917605463">"Отвори"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Администратор не дозвољава инсталирање апликација добијених из непознатих извора"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Овај корисник не може да инсталира непознате апликације"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Овом кориснику није дозвољено да инсталира апликације"</string>
+    <string name="ok" msgid="3468756155452870475">"Потврди"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Управљање апликацијама"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Нема више места"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Није могуће инсталирати апликацију <xliff:g id="APP_NAME">%1$s</xliff:g>. Ослободите додатни простор и покушајте поново."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Апликација није пронађена"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Апликација није пронађена на листи инсталираних апликација."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Није дозвољено"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Актуелном кориснику није дозвољено да обави ово деинсталирање."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Грешка"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Деинсталирање апликације није успело."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Деинсталирање апликације"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Деинсталирање ажурирања"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> је део следеће апликације:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Да ли желите да деинсталирате ову апликацију?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Да ли желите да деинсталирате ову апликацију за "<b>"све"</b>" кориснике? Апликација и подаци који се на њу односе биће уклоњени за "<b>"све"</b>" кориснике овог уређаја."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Желите ли да деинсталирате ову апликацију за корисника <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Желите ли да замените ову апликацију фабричком верзијом? Сви подаци ће бити уклоњени."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Желите ли да замените ову апликацију фабричком верзијом? Сви подаци ће бити уклоњени. Ово утиче на све кориснике овог уређаја, укључујући и оне са профилима за Work."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Активна деинсталирања"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Неуспела деинсталирања"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Деинсталирање..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> се деинсталира…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Деинсталирање је завршено."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Апликација <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> је деинсталирана"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Деинсталирање није успело."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Деинсталирање апликације <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> није успело."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Не можете да деинсталирате апликацију за активног администратора уређаја"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Не можете да деинсталирате апликацију за активног администратора уређаја за <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Ова апликација је потребна за неке кориснике или профиле, а деинсталирана је за друге"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Ова апликација је потребна за ваш профил и не може да се деинсталира."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Ова апликација је потребна администратору уређаја и не може да се деинсталира."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Управљај апликацијама за администраторе уређаја"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Управљаj корисницима"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Није могуће деинсталирати апликацију <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Дошло је до проблема при рашчлањивању пакета."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Ново"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Све"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Приватност"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Приступ уређају"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Ово ажурирање не захтева нове дозволе."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Одбаци"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Више информација"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Ипак одбиј"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>. од <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Желите ли да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Желите ли увек да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Само док се апликација користи"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Увек"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Одбиј и не питај поново"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"Онемогућених: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"све су онемогућене"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ниједна није онемогућена"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Дозволи"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Апликације"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Дозволе за апликације"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Не питај поново"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Нема дозвола"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Додатне дозволе"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Отвори информације о апликацији"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one">још <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="few">још <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="old_sdk_deny_warning" msgid="3872277112584842615">"Ова апликација је дизајнирана за старију верзију Android-а. Ако одбијете дозволу, она можда више неће правилно да функционише."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"обавља непознату радњу"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> од <xliff:g id="COUNT_1">%2$d</xliff:g> апликација има дозволу"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Прикажи системске"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Сакриј системске"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Нема апликација"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Подешавања локације"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> пружа услуге локације за овај уређај. Приступ локацији можете да измените у подешавањима локације."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Ако одбијете ову дозволу, основне функције уређаја можда неће више функционисати исправно."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Примењује се у складу са смерницама"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Приступ у позадини је онемогућен смерницама"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Приступ у позадини је омогућен смерницама"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Приступ у првом плану је омогућен смерницама"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Контролише администратор"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Увек"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Само док се апликација користи"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Никада"</string>
+    <string name="loading" msgid="7811651799620593731">"Учитава се…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Све дозволе"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Остале могућности апликације"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Захтев за дозволу"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Откривен је елемент који прекрива садржај екрана"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Да бисте променили подешавање ове дозволе, прво треба да искључите елемент који прекрива садржај екрана у одељку Подешавања &gt; Апликације"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Отвори подешавања"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Радње Инсталирај/Деинсталирај нису подржане у Wear-у."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Изаберите чему &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; може да приступа"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; је ажурирана. Изаберите чему ова апликација може да приступа."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Откажи"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Настави"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Нове дозволе"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Актуелне дозволе"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Апликација се припрема…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Непознато"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Таблету из безбедносних разлога није дозвољено да инсталира непознате апликације из овог извора."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Телевизору из безбедносних разлога није дозвољено да инсталира непознате апликације из овог извора."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Телефону из безбедносних разлога није дозвољено да инсталира непознате апликације из овог извора."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Телефон и лични подаци су подложнији нападу непознатих апликација. Ако инсталирате ову апликацију, прихватате да сте одговорни за евентуална оштећења телефона или губитак података до којих може да дође због њеног коришћења."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Таблет и лични подаци су подложнији нападу непознатих апликација. Ако инсталирате ову апликацију, прихватате да сте одговорни за евентуална оштећења таблета или губитак података до којих може да дође због њеног коришћења."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"ТВ и лични подаци су подложнији нападу непознатих апликација. Ако инсталирате ову апликацију, прихватате да сте одговорни за евентуална оштећења ТВ-а или губитак података до којих може да дође због њеног коришћења."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Настави"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Подешавања"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Инсталирање/деинсталирање Wear апликација"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sv-television/strings.xml b/packages/PackageInstaller/res/values-sv-television/strings.xml
new file mode 100644
index 0000000..5f23767
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sv-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Neka och fråga inte igen"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Du kan ändra detta senare i Inställningar &gt; Appar"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Visa systemappar"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Appens behörigheter"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Appens behörigheter"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Behörigheter för <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Ytterligare behörigheter"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Behörigheter för <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sv-watch/strings.xml b/packages/PackageInstaller/res/values-sv-watch/strings.xml
new file mode 100644
index 0000000..f20ec21
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sv-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Avvisa och fråga inte igen"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Visa systemappar"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Kan inte ändras"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Ja"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Avbryt"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sv/strings.xml b/packages/PackageInstaller/res/values-sv/strings.xml
new file mode 100644
index 0000000..ff4f4c1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sv/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Installationsprogram för paket"</string>
+    <string name="next" msgid="3057143178373252333">"Nästa"</string>
+    <string name="install" msgid="5896438203900042068">"Installera"</string>
+    <string name="done" msgid="3889387558374211719">"Färdig"</string>
+    <string name="cancel" msgid="8360346460165114585">"Avbryt"</string>
+    <string name="installing" msgid="8613631001631998372">"Installerar…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> installeras …"</string>
+    <string name="install_done" msgid="3682715442154357097">"Appen har installerats."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Vill du installera den här appen? Den får åtkomst till:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Vill du installera den här appen? Den kräver ingen särskild åtkomst."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Vill du installera en uppdatering till den här befintliga appen? Dina befintliga data försvinner inte. Den uppdaterade appen får åtkomst till:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Vill du installera en uppdatering till den här befintliga förinstallerade appen? Dina befintliga data försvinner inte. Den uppdaterade appen får åtkomst till:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Vill du installera en uppdatering av den befintliga appen? Dina befintliga data försvinner inte. Ingen särskild åtkomst krävs."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Vill du installera en uppdatering av den inbyggda appen? Dina befintliga data försvinner inte. Ingen särskild åtkomst krävs."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Appen har inte installerats."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Paketet har blockerats för installation."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Appen har inte installerats på grund av en konflikt mellan detta paket och ett befintligt paket."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Appen har inte installerats eftersom den inte är kompatibel med surfplattan."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Appen är inte kompatibel med din TV."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Appen har inte installerats eftersom den inte är kompatibel med mobilen."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Appen har inte installerats eftersom paketet verkar vara ogiltigt."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Det gick inte att installera <xliff:g id="APP_NAME">%1$s</xliff:g> på surfplattan."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> kunde inte installeras på TV:n."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Det gick inte att installera <xliff:g id="APP_NAME">%1$s</xliff:g> på mobilen."</string>
+    <string name="launch" msgid="4826921505917605463">"Öppna"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Administratören tillåter inte installation av appar från okända källor"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Denna användare får inte installera okända appar"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Användaren har inte behörighet att installera appar"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Hantera appar"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Slut på utrymme"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Det gick inte att installera <xliff:g id="APP_NAME">%1$s</xliff:g>. Frigör minne och försök igen."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Appen hittades inte"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Appen fanns inte i listan över installerade appar."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Ingen behörighet"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Den aktuella användaren har inte behörighet att utföra avinstallationen."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Fel"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Det gick inte att installera appen."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Avinstallera appen"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Avinstallera uppdateringen"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> är en del av följande app:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Vill du avinstallera appen?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Vill du avinstallera den här appen för "<b>"alla"</b>" användare? Appen och alla data i den tas bort från "<b>"alla"</b>" användare på enheten."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Vill du avinstallera appen för användaren <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"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="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Avinstallationer som pågår"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Avinstallationer som misslyckats"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Avinstallerar…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> avinstalleras …"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Avinstallationen har slutförts."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> har avinstallerats"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Det gick inte att avinstallera."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Det gick inte att avinstallera <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Det går inte att avinstallera den aktiva appen för enhetsadministratör"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Det går inte att avinstallera den aktiva appen för enhetsadministratör för <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Den här appen är obligatorisk för vissa användare och profiler och har avinstallerats för andra"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Appen behövs i profilen och det går inte att avinstallera den"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Appen krävs av enhetsadministratören och kan därför inte avinstalleras."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Hantera appar för enhetsadministratör"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Hantera användare"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Det gick inte att avinstallera <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Ett problem uppstod när paketet analyserades."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Nytt"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Alla"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Sekretess"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Enhetsåtkomst"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Det krävs inga nya behörigheter för den här uppdateringen."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Neka"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Mer information"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Neka ändå"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> av <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Tillåter du &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; att <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Vill du alltid tillåta att &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Bara när appen används"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Alltid"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Neka och fråga inte igen"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> har inaktiverats"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"alla har inaktiverats"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"inga har inaktiverats"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Tillåt"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Appar"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Appens behörigheter"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Fråga inte igen"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Inga behörigheter"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Ytterligare behörigheter"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Öppna appinformation"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> till</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> till</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Den här appen utformades för en äldre version av Android. Om du nekar appen behörighet kan det hända att den inte längre fungerar som den ska."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"utför en okänd åtgärd"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> av <xliff:g id="COUNT_1">%2$d</xliff:g> appar tillåts"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Visa systemet"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Dölj systemet"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Inga appar"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Platsinställningar"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> är en platstjänstleverantör för enheten. Platsåtkomsten kan redigeras i platsinställningarna."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Om du nekar appen behörighet kan det hända att grundläggande funktioner på enheten inte fungerar som de ska."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Enligt policyn"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Åtkomst i bakgrunden har inaktiverats av en princip"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Åtkomst i bakgrunden har aktiverats av en princip"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Åtkomst i förgrunden har aktiverats av en princip"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Styrs av administratören"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Alltid"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Bara när appen används"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Aldrig"</string>
+    <string name="loading" msgid="7811651799620593731">"Läser in …"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Alla behörigheter"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Andra appbehörigheter"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Begäran om behörighet"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Skärmöverlagring har upptäckts"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Innan du kan ändra den här behörighetsinställningen måste du inaktivera skärmöverlagring under Inställningar &gt; Appar"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Öppna inställningarna"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Åtgärder för att installera/avinstallera stöds inte på Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Välj vad du vill ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; har uppdaterats. Välj vad du vill ge appen åtkomst till."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Avbryt"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Fortsätt"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Nya behörigheter"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Nuvarande behörighet"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Provkör appen …"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Okänd"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Av säkerhetsskäl får okända appar från den här källan inte installeras av surfplattan."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Av säkerhetsskäl får okända appar från den här källan inte installeras av TV:n."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Av säkerhetsskäl får okända appar från den här källan inte installeras av mobilen."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Din mobil och personliga data är mer sårbara för attacker från okända appar. Genom att installera denna app bekräftar du att du är ansvarig för eventuella skador på mobilen och för dataförlust som kan uppstå vid användning av denna app."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Din surfplatta och personliga data är mer sårbara för attacker från okända appar. Genom att installera denna app bekräftar du att du är ansvarig för eventuella skador på surfplattan och för dataförlust som kan uppstå vid användning av denna app."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Din TV och personliga data är mer sårbara för attacker från okända appar. Genom att installera denna app bekräftar du att du är ansvarig för eventuella skador på TV:n och för dataförlust som kan uppstå vid användning av denna app."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Fortsätt"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Inställningar"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear-appar installeras/avinstalleras"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sw-television/strings.xml b/packages/PackageInstaller/res/values-sw-television/strings.xml
new file mode 100644
index 0000000..25fd696
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sw-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Hapana na usiulize tena"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Unaweza kubadilisha hatua hii baadaye kwenye Mipangilio na Programu"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Onyesha programu za mfumo"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Ruhusa za programu"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Ruhusa za programu"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Ruhusa za <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Ruhusa za ziada"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Ruhusa za <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sw-watch/strings.xml b/packages/PackageInstaller/res/values-sw-watch/strings.xml
new file mode 100644
index 0000000..30d68e5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sw-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Hapana, usiulize tena"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Onyesha programu za mfumo"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Haiwezi kubadilishwa"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Ndiyo"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Ghairi"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sw/strings.xml b/packages/PackageInstaller/res/values-sw/strings.xml
new file mode 100644
index 0000000..c431983
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sw/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Kisakinishaji cha furushi"</string>
+    <string name="next" msgid="3057143178373252333">"Inayofuata"</string>
+    <string name="install" msgid="5896438203900042068">"Sakinisha"</string>
+    <string name="done" msgid="3889387558374211719">"Nimemaliza"</string>
+    <string name="cancel" msgid="8360346460165114585">"Ghairi"</string>
+    <string name="installing" msgid="8613631001631998372">"inawekwa..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Inasakinisha <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Programu imewekwa."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Je, ungependa kuiweka programu hii? Itaweza:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Je, ungependa kuiweka programu hii? Haihitaji idhini ya kufikia kitu chochote."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Je, unataka kuweka sasisho katika programu hii? Data yako iliyopo haitapotea. Programu iliyosasishwa itaweza:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Je, unataka kuweka sasisho la programu hii iliyojengewa ndani? Data yako iliyopo haitapotea. Programu iliyosasishwa itaweza:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Je, unataka kuweka sasisho la programu hii? Data yako iliyopo haitapotea. Haihitaji idhini yoyote maalum ya kufikia."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Je, unataka kuweka sasisho la programu hii? Data yako iliyopo haitapotea. Haihitaji idhini yoyote maalum ya kufikia."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Programu haikusakinishwa."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Kifurushi kimezuiwa kisisakinishwe."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Programu haikusakinishwa kwa sababu haiafikiani na kifurushi kingine kilichopo."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Programu haikusakinishwa kwa sababu haioani na kompyuta kibao yako."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Programu hii haioani na runinga yako."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Programu haikusakinishwa kwa sababu haioani na simu yako."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Programu haikusakinishwa kwa sababu inaonekana kuwa kifurushi si sahihi."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> haikuweza kusakinishwa kwenye kompyuta yako kibao."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> haikuweza kusakinishwa kwenye runinga yako."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> haikuweza kusakinishwa kwenye simu yako."</string>
+    <string name="launch" msgid="4826921505917605463">"Fungua"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Msimamizi wako haruhusu usakinishaji wa programu zinazopatikana kutoka vyanzo visivyojulikana"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Mtumiaji huyu hana idhini ya kusakinisha programu ambazo hazijulikani"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Mtumiaji huyu haruhusiwi kusakinisha programu"</string>
+    <string name="ok" msgid="3468756155452870475">"Sawa"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Dhibiti programu"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Nafasi imeisha"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> haingeweza kusakinishwa. Wezesha nafasi kiasi na ujaribu tena."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Programu haikupatikana"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Programu haikupatikana katika orodha ya programu zilizosakinishwa."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Hairuhusiwi"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Mtumiaji wa sasa hana ruhusa ya kuondoa kipengee hiki."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Hitilafu"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Imeshindwa kuondoa programu."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Sanidua programu"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Sanidua kisasisho"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ni sehemu ya programu ifuatayo:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Unataka kusanidua programu hii?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Je, unataka kusanidua programu hii kwa "<b>"watumiaji"</b>" wote? Programu na data yake zitaondolewa kutoka kwa "<b>"watumiaji"</b>" kwenye kifaa."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Je, unataka kuondoa programu hii kwa mtumiaji <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Ungependa kubadilisha programu hii na toleo la kiwanda? Data yote itaondolewa."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Ungependa kubadilisha programu hii na toleo la kiwanda? Data yote itaondolewa. Hatua hii itaathiri watumiaji wote wa kifaa hiki, ikiwa ni pamoja na wale walio na wasifu za kazini."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Mara ambazo programu inaondolewa sasa"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Mara ambazo programu haikuondolewa"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Inasanidua..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Inaondoa <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Imesaniduliwa."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Imeondoa <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Kusanidua hakukufaulu."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Imeshindwa kuondoa <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Imeshindwa kuondoa programu inayotumika ya msimamizi wa kifaa"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Imeshindwa kuondoa programu inayotumika ya msimamizi wa kifaa cha <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Baadhi ya wasifu au watumiaji wanahitaji programu, kwa hivyo haijaondolewa kwa wengine"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Programu hii inahitajika kwa wasifu wako kwa hivyo haiwezi kuondolewa."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Programu hii inahitajika na msimamizi wako wa kifaa na haiwezi kuondolewa."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Dhibiti programu za msimamizi wa kifaa"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Dhibiti watumiaji"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> haingeweza kusaniduliwa."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Kulikuwa na tatizo la kuchanganua furushi."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Mpya"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Zote"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Faragha"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Kufikia Kifaa"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Sasisho hili halihitaji vibali vipya."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Hapana"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Maelezo zaidi"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Kataa"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> kati ya <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; itekeleze <xliff:g id="ACTION">%2$s</xliff:g> kila wakati?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Wakati unatumia programu tu"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Kila wakati"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Kataa na usiulize tena"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> zimezimwa"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"zimezimwa zote"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"hakuna zilizozimwa"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Ndiyo"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Programu"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Ruhusa za programu"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Usiulize tena"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Hakuna ruhusa"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Ruhusa za ziada"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Fungua maelezo ya programu"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> zaidi</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> zaidi</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Programu hii iliundwa kwa ajili ya toleo la zamani la Android. Kuinyima ruhusa kunaweza kusababisha iache kutenda kazi kama ilivyokusudiwa."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"kutekeleza kitendo kisichojulikana"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Inaruhusu programu <xliff:g id="COUNT_0">%1$d</xliff:g> kati ya <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Onyesha mfumo"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Ficha mfumo"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Hakuna programu"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Mipangilio ya Mahali"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> hutoa huduma za mahali kwenye kifaa hiki. Idhini ya kufikia mahali inaweza kurekebishwa katika mipangilio ya mahali."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Usipokubali ruhusa hii, huenda vipengele vya msingi vya kifaa chako havitafanya kazi kama ilivyokusudiwa."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Hutekelezwa na sera"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Ufikiaji wa chinichini umezimwa na sera"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Ufikiaji wa chinichini umewashwa na sera"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Ufikiaji wa hadharani umewashwa na sera"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Imedhibitiwa na msimamizi"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Kila wakati"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Wakati unatumia programu tu"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Kamwe"</string>
+    <string name="loading" msgid="7811651799620593731">"Inapakia…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Ruhusa zote"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Uwezo mwingine wa programu"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Ombi la idhini"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Imetambua tangazo lililowekelewa juu ya skrini"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Ili kubadilisha mpangilio huu wa ruhusa, ni lazima kwanza uzime tangazo lililowekelewa juu ya skrini kwenye Mipangilio na Programu"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Fungua mipangilio"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Huduma ya Android Wear haiwezi kutekeleza vitendo vya Kusakinisha au Kuondoa vipengee."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Chagua vipengee ambavyo unaruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; imesasishwa. Chagua vipengee unavyoruhusu programu hii ifikie."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Ghairi"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Endelea"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Ruhusa mpya"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Ruhusa zilizopo"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Inatayarisha programu..."</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Isiyojulikana"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Kwa sababu ya usalama wako, kompyuta yako kibao haina ruhusa ya kusakinisha programu ambazo hazijulikani, kutoka kwenye chanzo hiki."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Kwa sababu ya usalama wako, TV yako haina ruhusa ya kusakinisha programu ambazo hazijulikani, kutoka kwenye chanzo hiki."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Kwa sababu ya usalama wako, simu yako haina ruhusa ya kusakinisha programu ambazo hazijulikani, kutoka kwenye chanzo hiki."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Data yako ya binafsi na ya simu yako inaweza kuathiriwa na programu ambazo hazijulikani. Kwa kusakinisha programu hii, unakubali kuwajibika kutokana na uharibifu wowote kwenye simu yako au kupotea kwa data kutokana na matumizi yake."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Data yako ya binafsi na ya kompyuta yako kibao inaweza kuathiriwa na programu ambazo hazijulikani. Kwa kusakinisha programu hii, unakubali kuwajibika kutokana na uharibifu wowote kwenye kompyuta yako kibao au kupotea kwa data kutokana na matumizi yake."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Data yako ya binafsi na ya televisheni yako inaweza kuathiriwa na programu ambazo hazijulikani. Kwa kusakinisha programu hii, unakubali kuwajibika kutokana na uharibifu wowote kwenye televisheni yako au kupotea kwa data kutokana na matumizi yake."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Endelea"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Mipangilio"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Inasakinisha/inaondoa programu za Android Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sw180dp-notround-watch/dimens.xml b/packages/PackageInstaller/res/values-sw180dp-notround-watch/dimens.xml
new file mode 100644
index 0000000..84072a2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sw180dp-notround-watch/dimens.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<resources>
+    <!-- Dimens for dialog layouts -->
+    <dimen name="diag_button_size">48dp</dimen>
+</resources>
diff --git a/packages/PackageInstaller/res/values-sw210dp-round-watch/dimens.xml b/packages/PackageInstaller/res/values-sw210dp-round-watch/dimens.xml
new file mode 100644
index 0000000..d896138
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sw210dp-round-watch/dimens.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<resources>
+    <!-- Dimens for dialog layouts -->
+    <dimen name="diag_button_size">54dp</dimen>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ta-television/strings.xml b/packages/PackageInstaller/res/values-ta-television/strings.xml
new file mode 100644
index 0000000..786bbe2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ta-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"நிராகரி, மீண்டும் கேட்காதே"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"அமைப்புகள் &gt; பயன்பாடுகள் என்பதில் பிறகு மாற்றலாம்"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"முறைமைப் பயன்பாடுகளைக் காட்டு"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"பயன்பாட்டு அனுமதிகள்"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"பயன்பாட்டு அனுமதிகள்"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> அனுமதிகள்"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"கூடுதல் அனுமதிகள்"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> அனுமதிகள்"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ta-watch/strings.xml b/packages/PackageInstaller/res/values-ta-watch/strings.xml
new file mode 100644
index 0000000..23dab29
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ta-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"நிராகரி, மீண்டும் கேட்காதே"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"முறைமைப் பயன்பாடுகளைக் காட்டு"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"மாற்ற முடியாது"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"ஆம்"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"ரத்துசெய்"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ta/strings.xml b/packages/PackageInstaller/res/values-ta/strings.xml
new file mode 100644
index 0000000..366f6d9
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ta/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"பேக்கேஜ் இன்ஸ்டாலர்"</string>
+    <string name="next" msgid="3057143178373252333">"அடுத்து"</string>
+    <string name="install" msgid="5896438203900042068">"நிறுவு"</string>
+    <string name="done" msgid="3889387558374211719">"முடிந்தது"</string>
+    <string name="cancel" msgid="8360346460165114585">"ரத்துசெய்"</string>
+    <string name="installing" msgid="8613631001631998372">"நிறுவுகிறது…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ஐ நிறுவுகிறது…"</string>
+    <string name="install_done" msgid="3682715442154357097">"பயன்பாடு நிறுவப்பட்டது."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"இந்தப் பயன்பாட்டை நிறுவ விரும்புகிறீர்களா? அது இதற்கான அணுகலைப் பெறும்:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"இந்தப் பயன்பாட்டை நிறுவ விரும்புகிறீர்களா? இதற்கு எந்தத் தனிப்பட்ட அணுகலும் தேவையில்லை."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"முன்பே உள்ள இந்தப் பயன்பாட்டில் புதுப்பிப்பை நிறுவ விரும்புகிறீர்களா? ஏற்கனவே உள்ள உங்கள் தரவை இழக்க மாட்டீர்கள். புதுப்பிக்கப்பட்ட பயன்பாடு இதற்கான அணுகலைப் பெறும்:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"உள்ளமைக்கப்பட்ட பயன்பாட்டில் புதுப்பிப்பை நிறுவ விரும்புகிறீர்களா? ஏற்கனவே உள்ள உங்கள் தரவை இழக்க மாட்டீர்கள். புதுப்பிக்கப்பட்ட பயன்பாடு இதற்கான அணுகலைப் பெறும்:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"முன்பே உள்ள இந்தப் பயன்பாட்டில் புதுப்பிப்பை நிறுவ விரும்புகிறீர்களா? ஏற்கனவே உள்ள உங்கள் தரவை இழக்கமாட்டீர்கள். இதற்குத் தனிப்பட்ட அணுகல் எதுவும் தேவையில்லை."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"உள்ளமைக்கப்பட்ட பயன்பாட்டில் புதுப்பிப்பை நிறுவ விரும்புகிறீர்களா? ஏற்கனவே உள்ள உங்கள் தரவை இழக்க மாட்டீர்கள். இதற்குத் தனிப்பட்ட அணுகல் எதுவும் தேவையில்லை."</string>
+    <string name="install_failed" msgid="6579998651498970899">"பயன்பாடு நிறுவப்படவில்லை."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"இந்தத் தொகுப்பு நிறுவுவதிலிருந்து தடுக்கப்பட்டது."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"தொகுப்பானது தற்போதுள்ள தொகுப்புடன் இணக்கமற்றதாக உள்ளதால், பயன்பாடு நிறுவப்படவில்லை."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"உங்கள் டேப்லெட்டுடன் இணக்கமற்றதாக உள்ளதால், பயன்பாடு நிறுவப்படவில்லை."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"உங்கள் டிவியுடன் இந்தப் பயன்பாடு இணங்கவில்லை."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"உங்கள் மொபைலுடன் இணக்கமற்றதாக உள்ளதால், பயன்பாடு நிறுவப்படவில்லை."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"தொகுப்பு தவறானது போல் உள்ளதால், பயன்பாடு நிறுவப்படவில்லை."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> பயன்பாட்டை உங்கள் டேப்லெட்டில் நிறுவ முடியாது."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"உங்கள் டிவியில் <xliff:g id="APP_NAME">%1$s</xliff:g>ஐ நிறுவ முடியவில்லை."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> பயன்பாட்டை உங்கள் மொபைலில் நிறுவ முடியாது."</string>
+    <string name="launch" msgid="4826921505917605463">"திற"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"அறியப்படாத மூலங்களிலிருந்து பெற்ற பயன்பாடுகளை நிறுவ, உங்கள் நிர்வாகி அனுமதிக்கவில்லை"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"அறியப்படாத பயன்பாடுகளை, இந்தப் பயனர் நிறுவ முடியாது"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"பயன்பாடுகளை நிறுவ, இந்தப் பயனருக்கு அனுமதியில்லை"</string>
+    <string name="ok" msgid="3468756155452870475">"சரி"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"பயன்பாடுகளை நிர்வகிக்கவும்"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"இடம் இல்லை"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> பயன்பாட்டை நிறுவ முடியாது. சில இடத்தைக் காலி செய்து மீண்டும் முயற்சிக்கவும்."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"பயன்பாடு கண்டறியப்படவில்லை"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"நிறுவிய பயன்பாடுகளின் பட்டியலில் பயன்பாடு இல்லை."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"அனுமதிக்கப்படவில்லை"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"இதை நிறுவல் நீக்குவதற்கு, தற்போதைய பயனர் அனுமதிக்கப்படவில்லை."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"பிழை"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"பயன்பாட்டை நிறுவல் நீக்க முடியவில்லை."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"பயன்பாட்டை நிறுவல் நீக்கு"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"புதுப்பிப்பை நிறுவல் நீக்கு"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ஆனது பின்வரும் பயன்பாட்டின் பகுதியாகும்:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"இந்தப் பயன்பாட்டை நிறுவல் நீக்க விரும்புகிறீர்களா?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"இந்தப் பயன்பாட்டை "<b>"எல்லா"</b>" பயனர்களுக்கும் நிறுவல் நீக்க விரும்புகிறீர்களா? பயன்பாடும், அதன் தரவும் சாதனத்தில் உள்ள "<b>"எல்லா"</b>" பயனர்களிடமிருந்தும் அகற்றப்படும்."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"<xliff:g id="USERNAME">%1$s</xliff:g> பயனருக்கான இந்தப் பயன்பாட்டை நிறுவல்நீக்க விரும்புகிறீர்களா?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"ஆரம்பநிலைப் பதிப்பாக இந்தப் பயன்பாட்டை மாற்றியமைக்கவா? எல்லா தரவும் அகற்றப்படும்."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"ஆரம்பநிலைப் பதிப்பாக இந்தப் பயன்பாட்டை மாற்றியமைக்கவா? எல்லா தரவும் அகற்றப்படும். பணிச் சுயவிவரங்களுடன் உள்ளவர்கள் உட்பட இந்தச் சாதனத்தின் எல்லா பயனர்களையும் இது பாதிக்கும்."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"இயக்கத்திலுள்ள நிறுவல் நீக்கங்கள்"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"தோல்வியடைந்த நிறுவல் நீக்கங்கள்"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"நிறுவலை நீக்குகிறது…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ஐ நிறுவல் நீக்குகிறது…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"நிறுவல் நீக்குவது முடிந்தது."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> நிறுவல் நீக்கப்பட்டது"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"நிறுவல் நீக்குவதில் தோல்வி."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ஐ நிறுவல் நீக்க முடியவில்லை."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"செயலில் உள்ள சாதன நிர்வாகிப் பயன்பாட்டை நிறுவல் நீக்க முடியாது"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g>க்கான செயலில் உள்ள சாதன நிர்வாகிப் பயன்பாட்டை நிறுவல் நீக்க முடியாது"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"இது சில பயனர்கள்/சுயவிவரங்களுக்குத் தேவைப்படுவதால், நிறுவல்நீக்க முடியாது, பிறருக்கு நிறுவல் நீக்கப்பட்டது"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"உங்கள் சுயவிவரத்திற்கு இந்தப் பயன்பாடு தேவைப்படுவதால், அதை நிறுவல்நீக்க முடியாது, பிறருக்கு நிறுவல் நீக்கப்பட்டது."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"சாதன நிர்வாகிக்கு இந்தப் பயன்பாடு தேவைப்படுவதால், நிறுவல்நீக்க முடியாது."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"சாதன நிர்வாகிப் பயன்பாடுகளை நிர்வகி"</string>
+    <string name="manage_users" msgid="3125018886835668847">"பயனர்களை நிர்வகி"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> பயன்பாட்டை நிறுவல் நீக்க முடியாது."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"தொகுப்பைக் குறியீட்டு ஆய்வு செய்வதில் சிக்கல் ஏற்பட்டது."</string>
+    <string name="newPerms" msgid="6039428254474104210">"புதிது"</string>
+    <string name="allPerms" msgid="1024385515840703981">"எல்லாம்"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"தனியுரிமை"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"சாதன அணுகல்"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"இந்தப் புதுப்பிப்பிற்குப் புதிய அனுமதிகள் எதுவும் தேவையில்லை."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"நிராகரி"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"மேலும் தகவல்"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"பரவாயில்லை, நிராகரி"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"செயலைச் செய்ய <xliff:g id="ACTION">%2$s</xliff:g>, &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ஐ அனுமதிக்கவா?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"<xliff:g id="ACTION">%2$s</xliff:g>ஐச் செய்ய &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை எப்போதும் அனுமதிக்கவா?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"ஆப்ஸைப் பயன்படுத்தும்போது மட்டும் அனுமதி"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"எப்போதும் அனுமதி"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"நிராகரி, மீண்டும் கேட்காதே"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> முடக்கப்பட்டன"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"எல்லாம் முடக்கப்பட்டன"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"எதுவும் முடக்கப்படவில்லை"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"அனுமதி"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"ஆப்ஸ்"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"பயன்பாட்டு அனுமதிகள்"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"மீண்டும் கேட்காதே"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"அனுமதிகள் இல்லை"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"கூடுதல் அனுமதிகள்"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"பயன்பாட்டுத் தகவலைத் திற"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">மேலும் <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">மேலும் <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"இந்தப் பயன்பாடு Android இன் பழைய பதிப்புக்காக வடிவமைக்கப்பட்டது. அனுமதியை மறுத்தால் அது சரியாக செயல்படாமல் போகலாம்."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"அறியாத செயலைச் செயல்படுத்தும்"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"அனுமதிக்கப்பட்ட ஆப்ஸ்: <xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"எல்லாம் காட்டு"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"முறைமையை மறை"</string>
+    <string name="no_apps" msgid="1965493419005012569">"பயன்பாடுகள் இல்லை"</string>
+    <string name="location_settings" msgid="1774875730854491297">"இருப்பிட அமைப்புகள்"</string>
+    <string name="location_warning" msgid="8778701356292735971">"இந்தச் சாதனத்திற்கான இருப்பிடச் சேவைகளின் வழங்குநர் <xliff:g id="APP_NAME">%1$s</xliff:g> ஆகும். இருப்பிட அமைப்புகளிலிருந்து இருப்பிட அணுகலை மாற்றலாம்."</string>
+    <string name="system_warning" msgid="7103819124542305179">"இந்த அனுமதியை நிராகரித்தால், உங்கள் சாதனத்தின் அடிப்படை அம்சங்கள் சரியாகச் செயல்படாமல் போகலாம்."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"கொள்கையின் படி செயல்படுத்தப்பட்டது"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"கொள்கையின்படி பின்புல அணுகல் முடக்கப்பட்டது"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"கொள்கையின்படி பின்புல அணுகல் இயக்கப்பட்டது"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"கொள்கையின்படி முன்புல அணுகல் இயக்கப்பட்டது"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"நிர்வாகி கட்டுப்படுத்துகிறார்"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"எப்போதும்"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"ஆப்ஸை உபயோகிக்கும்போது மட்டும்"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"ஒருபோதும் வேண்டாம்"</string>
+    <string name="loading" msgid="7811651799620593731">"ஏற்றுகிறது..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"எல்லா அனுமதிகளும்"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"பயன்பாட்டின் பிற திறன்கள்"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"அனுமதி கோரிக்கை"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"திரையின் மேலே செயல்படும் பயன்பாடுகள் கண்டறியப்பட்டன"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"இந்த அனுமதியை மாற்ற, அமைப்புகள் &gt; பயன்பாடுகள் என்பதற்குச் சென்று, திரையின் மேலே செயல்படும் பயன்பாடுகளை முதலில் முடக்கவும்"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"அமைப்புகளைத் திற"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear இல் நிறுவுதல்/நிறுவல் நீக்குதலுக்கு ஆதரவில்லை."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; எவற்றை அணுகலாம் என்பதைத் தேர்வுசெய்யவும்"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; புதுப்பிக்கப்பட்டது. இந்தப் பயன்பாடு எவற்றை அணுகலாம் என்பதைத் தேர்வுசெய்யவும்."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"ரத்துசெய்"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"தொடர்க"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"புதிய அனுமதிகள்"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"தற்போதைய அனுமதிகள்"</string>
+    <string name="message_staging" msgid="6151794817691100003">"பயன்பாடு தயாராகிறது…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"தெரியாதது"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"உங்கள் பாதுகாப்பிற்காக, இந்த மூலத்திலிருந்து அறியப்படாத பயன்பாடுகளை உங்கள் டேப்லெட்டில் நிறுவ முடியாது."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"உங்கள் பாதுகாப்பிற்காக, இந்த மூலத்திலிருந்து அறியப்படாத பயன்பாடுகளை உங்கள் டிவியில் நிறுவ முடியாது."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"உங்கள் பாதுகாப்பிற்காக, இந்த மூலத்திலிருந்து அறியப்படாத பயன்பாடுகளை உங்கள் மொபைலில் நிறுவ முடியாது."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"அறியப்படாத பயன்பாடுகள், உங்கள் மொபைலையும் தனிப்பட்ட தரவையும் அதிகம் பாதிக்கக்கூடும். இந்தப் பயன்பாட்டை நிறுவுவதால், அவற்றைப் பயன்படுத்தும் போது உங்கள் மொபைலுக்கு ஏதேனும் சேதம் ஏற்பட்டாலோ அல்லது தரவை இழந்தாலோ, அதற்கு நீங்கள்தான் பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"அறியப்படாத பயன்பாடுகள், உங்கள் டேப்லெட்டையும் தனிப்பட்ட தரவையும் அதிகம் பாதிக்கக்கூடும். இந்தப் பயன்பாட்டை நிறுவுவதால், அவற்றைப் பயன்படுத்தும் போது உங்கள் டேப்லெட்டுக்கு ஏதேனும் சேதம் ஏற்பட்டாலோ அல்லது தரவை இழந்தாலோ, அதற்கு நீங்கள்தான் பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"அறியப்படாத பயன்பாடுகள், உங்கள் டிவியையும் தனிப்பட்ட தரவையும் அதிகம் பாதிக்கக்கூடும். இந்தப் பயன்பாட்டை நிறுவுவதால், அவற்றைப் பயன்படுத்தும் போது உங்கள் டிவிக்கு ஏதேனும் சேதம் ஏற்பட்டாலோ அல்லது தரவை இழந்தாலோ, அதற்கு நீங்கள்தான் பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"தொடர்க"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"அமைப்புகள்"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"வியர் ஆப்ஸை நிறுவுதல்/நிறுவல் நீக்குதல்"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-te-television/strings.xml b/packages/PackageInstaller/res/values-te-television/strings.xml
new file mode 100644
index 0000000..51526b7
--- /dev/null
+++ b/packages/PackageInstaller/res/values-te-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"నిరాకరిస్తున్నాను, మళ్లీ అడగవద్దు"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"మీరు దీన్ని తర్వాత సెట్టింగ్‌లు &gt; అనువర్తనాల్లో మార్చవచ్చు"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"సిస్టమ్ అనువర్తనాలను చూపు"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"యాప్ అనుమతులు"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"యాప్ అనుమతులు"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> అనుమతులు"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"అదనపు అనుమతులు"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> అనుమతులు"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-te-watch/strings.xml b/packages/PackageInstaller/res/values-te-watch/strings.xml
new file mode 100644
index 0000000..d97e970
--- /dev/null
+++ b/packages/PackageInstaller/res/values-te-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"నిరాకరిస్తున్నాను,ఇక అడగవద్దు"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"సిస్టమ్ అనువర్తనాలను చూపు"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"మార్చడం సాధ్యపడదు"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"అవును"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"రద్దు చేయి"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
new file mode 100644
index 0000000..b612385
--- /dev/null
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"ప్యాకేజీ ఇన్‌స్టాలర్"</string>
+    <string name="next" msgid="3057143178373252333">"తర్వాత"</string>
+    <string name="install" msgid="5896438203900042068">"ఇన్‌స్టాల్ చేయండి"</string>
+    <string name="done" msgid="3889387558374211719">"పూర్తయింది"</string>
+    <string name="cancel" msgid="8360346460165114585">"రద్దు చేయి"</string>
+    <string name="installing" msgid="8613631001631998372">"ఇన్‌స్టాల్ చేస్తోంది…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ని ఇన్‌స్టాల్ చేస్తోంది…"</string>
+    <string name="install_done" msgid="3682715442154357097">"యాప్ ఇన్‌స్టాల్ చేయబడింది."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"మీరు ఈ అనువర్తనాన్ని ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా? ఇది వీటికి ప్రాప్యతను పొందుతుంది:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"మీరు ఈ యాప్‌ను ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా? దీనికి ఎటువంటి ప్రత్యేక యాక్సెస్ అవసరం లేదు."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"మీరు ఈ ప్రస్తుత యాప్‌నకు అప్‌డేట్‌ను ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా? మీ ప్రస్తుత డేటాను కోల్పోవడం సంభవించదు. అప్‌డేట్ చేసిన యాప్ వీటికి యాక్సెస్‌ను పొందుతుంది:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"మీరు ఈ అంతర్నిర్మిత యాప్‌నకు అప్‌డేట్‌ను ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా? మీ ప్రస్తుత డేటాను కోల్పోవడం సంభవించదు. అప్‌డేట్ చేసిన యాప్ వీటికి యాక్సెస్  పొందుతుంది:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"మీరు ఈ ప్రస్తుత యాప్‌కు అప్‌డేట్‌ను ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా? మీ ప్రస్తుత డేటాను కోల్పోవడం సంభవించదు. దీనికి ఎటువంటి ప్రత్యేక యాక్సెస్ అవసరం లేదు."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"మీరు ఈ అంతర్నిర్మిత యాప్‌కు అప్‌డేట్‌ను ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా? మీ ప్రస్తుత డేటాను కోల్పోవడం సంభవించదు. దీనికి ఎటువంటి ప్రత్యేక యాక్సెస్ అవసరం లేదు."</string>
+    <string name="install_failed" msgid="6579998651498970899">"యాప్ ఇన్‌స్టాల్ చేయబడలేదు."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"ప్యాకేజీ ఇన్‌స్టాల్ కాకుండా బ్లాక్ చేయబడింది."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"ప్యాకేజీ ఇప్పటికే ఉన్న ప్యాకేజీకి వైరుధ్యంగా ఉన్నందున యాప్ ఇన్‌స్టాల్ చేయబడలేదు."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"యాప్ మీ టాబ్లెట్‌కు అనుకూలంగా లేని కారణంగా ఇన్‌స్టాల్ చేయబడలేదు."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"ఈ యాప్ మీ టీవీకి అనుకూలంగా లేదు."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"యాప్ మీ ఫోన్‌కు అనుకూలంగా లేని కారణంగా ఇన్‌స్టాల్ చేయబడలేదు."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"ప్యాకేజీ చెల్లుబాటు కాని విధంగా ఉన్నందున యాప్ ఇన్‌స్టాల్ చేయబడలేదు."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g>ను మీ టాబ్లెట్‌లో ఇన్‌స్టాల్ చేయడం సాధ్యపడలేదు."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g>ని మీ టీవీలో ఇన్‌స్టాల్ చేయడం సాధ్యపడలేదు."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g>ను మీ ఫోన్‌లో ఇన్‌స్టాల్ చేయడం సాధ్యపడలేదు."</string>
+    <string name="launch" msgid="4826921505917605463">"తెరవండి"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"మీ నిర్వాహకులు తెలియని మూలాల నుండి పొందిన అనువర్తనాల ఇన్‌స్టాలేషన్‌ను అనుమతించరు"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"తెలియని అనువర్తనాలను ఈ వినియోగదారు ఇన్‌స్టాల్ చేయలేరు"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"యాప్‌లను ఇన్‌స్టాల్ చేయడానికి ఈ వినియోగదారుకు అనుమతి లేదు"</string>
+    <string name="ok" msgid="3468756155452870475">"సరే"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"అనువర్తనాలను నిర్వహించండి"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"ఖాళీ లేదు"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g>ని ఇన్‌స్టాల్ చేయడం సాధ్యపడలేదు. కొంత స్థలాన్ని ఖాళీ చేసి మళ్లీ ప్రయత్నించండి."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"యాప్ కనుగొనబడలేదు"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"ఇన్‌స్టాల్ చేసిన యాప్‌ల జాబితాలో యాప్‌ కనుగొనబడలేదు."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"అనుమతించబడలేదు"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"ప్రస్తుత వినియోగదారు ఈ అన్ఇన్‌స్టాలేషన్ చేసేందుకు అనుమతి లేదు."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"ఎర్రర్"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"అనువర్తనాన్ని అన్ఇన్‌స్టాల్ చేయడం సాధ్యపడదు."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"యాప్‌ను అన్‌ఇన్‌స్టాల్ చేయండి"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"నవీకరణను అన్‌ఇన్‌స్టాల్ చేయండి"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> అనేది క్రింది యాప్‌లో ఒక భాగం:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"మీరు ఈ యాప్‌ను అన్‌ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"మీరు ఈ యాప్‌ను "<b>"మొత్తం"</b>" వినియోగదారులకు అన్‌ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా? యాప్ మరియు దీని డేటా డివైజ్‌లోని "<b>"మొత్తం"</b>" వినియోగదారుల నుండి తీసివేయబడుతుంది."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"మీరు వినియోగదారు <xliff:g id="USERNAME">%1$s</xliff:g> కోసం ఈ అనువర్తనాన్ని అన్‌ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"ఈ అనువర్తనాన్ని ఫ్యాక్టరీ సంస్కరణతో భర్తీ చేయాలా? మొత్తం డేటా తీసివేయబడుతుంది."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"ఈ అనువర్తనాన్ని ఫ్యాక్టరీ సంస్కరణతో భర్తీ చేయాలా? మొత్తం డేటా తీసివేయబడుతుంది. దీని ప్రభావం కార్యాలయ ప్రొఫైల్‌లు కలిగి ఉన్నవారితో సహా ఈ పరికర వినియోగదారులందరిపై ఉంటుంది."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"అమల్లో ఉన్న అన్‌ఇన్‌స్టాల్‌లు"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"విఫలమైన అన్‌ఇన్‌స్టాల్‌లు"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"అన్‌ఇన్‌స్టాల్ చేస్తోంది…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ని అన్ఇన్‌స్టాల్ చేస్తోంది…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"అన్‌ఇన్‌స్టాల్ చేయడం ముగిసింది."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"అన్ఇన్‌స్టాల్ చేసిన <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"అన్‌ఇన్‌స్టాల్ చేయడం విజయవంతం కాలేదు."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> అన్ఇన్‌స్టాల్ చేయడంలో విఫలమైంది."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"సక్రియ పరికర నిర్వాహక అనువర్తనాన్ని అన్ఇన్‌స్టాల్ చేయడం సాధ్యపడదు"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> కోసం సక్రియ పరికర నిర్వాహక అనువర్తనాన్ని అన్ఇన్‌స్టాల్ చేయడం సాధ్యపడదు"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"ఈ యాప్ కొందరు వినియోగదారులకు లేదా కొన్ని ప్రొఫైల్‌లకు అవసరం, ఇతరులకు అన్‌ఇన్‌స్టాల్ చేయబడింది"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"మీ ప్రొఫైల్ కోసం ఈ యాప్ అవసరం మరియు దీన్ని అన్ఇన్‌స్టాల్ చేయలేరు."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"మీ డివైజ్ నిర్వాహకుడికి ఈ యాప్ అవసరం, అందువల్ల దీన్ని అన్‌ఇన్‌స్టాల్ చేయడం కుదరదు."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"పరికర నిర్వాహక అనువర్తనాలను నిర్వహించు"</string>
+    <string name="manage_users" msgid="3125018886835668847">"వినియోగదారులను నిర్వహించు"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g>ని అన్‌ఇన్‌స్టాల్ చేయడం సాధ్యపడలేదు."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"ప్యాకేజీని అన్వయించడంలో సమస్య ఏర్పడింది."</string>
+    <string name="newPerms" msgid="6039428254474104210">"కొత్తవి"</string>
+    <string name="allPerms" msgid="1024385515840703981">"అన్నీ"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"గోప్యత"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"పరికరం యాక్సెస్"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"ఈ నవీకరణకు కొత్త అనుమతులు అవసరం లేదు."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"తిరస్కరించు"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"మరింత సమాచారం"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"ఏదేమైనా నిరాకరించు"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>లో <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని <xliff:g id="ACTION">%2$s</xliff:g> అనుమతించాలా?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"<xliff:g id="ACTION">%2$s</xliff:g> చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను ఎల్లప్పుడూ అనుమతించాలా?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"యాప్‌ను ఉపయోగిస్తున్నప్పుడు మాత్రమే"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"ఎల్లప్పుడూ"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"నిరాకరించు, మళ్లీ అడగవద్దు"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> నిలిపివేయబడ్డాయి"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"అన్నీ నిలిపివేయబడ్డాయి"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ఏవీ నిలిపివేయబడలేదు"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"అనుమతించు"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"యాప్‌లు"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"యాప్ అనుమతులు"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"మళ్లీ అడగవద్దు"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"అనుమతులు లేవు"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"అదనపు అనుమతులు"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"యాప్ సమాచారాన్ని తెరుస్తుంది"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">మరో <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">మరో <xliff:g id="COUNT_0">%1$d</xliff:g></item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"ఈ యాప్ పాత Android వెర్షన్ కోసం రూపొందించబడింది. అనుమతిని నిరాకరించినట్లయితే ఇది ఇకపై ఉద్దేశించిన రీతిలో పని చేయకపోవచ్చు."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"తెలియని చర్యను చేస్తుంది"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g>లో <xliff:g id="COUNT_0">%1$d</xliff:g> యాప్‌లు అనుమతించబడ్డాయి"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"సిస్టమ్‌ను చూపు"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"సిస్టమ్‌ను దాచు"</string>
+    <string name="no_apps" msgid="1965493419005012569">"అనువర్తనాలు లేవు"</string>
+    <string name="location_settings" msgid="1774875730854491297">"స్థాన సెట్టింగ్‌లు"</string>
+    <string name="location_warning" msgid="8778701356292735971">"ఈ పరికరం కోసం స్థాన సేవల ప్రదాత <xliff:g id="APP_NAME">%1$s</xliff:g>. స్థాన సెట్టింగ్‌ల నుండి స్థాన ప్రాప్యతను సవరించవచ్చు."</string>
+    <string name="system_warning" msgid="7103819124542305179">"మీరు ఈ అనుమతిని నిరాకరిస్తే, మీ పరికర ప్రాథమిక లక్షణాలు ఇకపై ఉద్దేశించిన రీతిలో పని చేయకపోవచ్చు."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"విధానం ద్వారా అమలు చేయబడింది"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"విధానం ద్వారా నేపథ్య యాక్సెస్ నిలిపివేయబడింది"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"విధానం ద్వారా నేపథ్య యాక్సెస్ ప్రారంభించబడింది"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"విధానం ద్వారా ముందుభాగం యాక్సెస్ ప్రారంభించబడింది"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"నిర్వాహకుల నియంత్రణలో ఉంటాయి"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"ఎల్లప్పుడూ"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"యాప్‌ను ఉపయోగిస్తున్నప్పుడు మాత్రమే"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"ఎన్నడూ వద్దు"</string>
+    <string name="loading" msgid="7811651799620593731">"లోడ్ అవుతోంది..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"అన్ని అనుమతులు"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"ఇతర అనువర్తన సామర్థ్యాలు"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"అనుమతి అభ్యర్థన"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"స్క్రీన్ అతివ్యాప్తి గుర్తించబడింది"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"ఈ అనుమతి సెట్టింగ్‌ను మార్చడానికి, మీరు ముందుగా సెట్టింగ్‌లు &gt; అనువర్తనాల నుండి స్క్రీన్ అతివ్యాప్తిని ఆఫ్ చేయాలి"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"సెట్టింగ్‌లను తెరువు"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android వేర్"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wearలో ఇన్‌స్టాల్/అన్ఇన్‌స్టాల్ చర్యలకు మద్దతు లేదు."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; యాక్సెస్ చేయడానికి అనుమతించాల్సిన వాటిని ఎంచుకోండి"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; అప్‌డేట్ చేయబడింది. ఈ యాప్ యాక్సెస్ చేయడానికి అనుమతించాల్సిన వాటిని ఎంచుకోండి."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"రద్దు చేయి"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"కొనసాగించు"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"కొత్త అనుమతులు"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"ప్రస్తుత అనుమతులు"</string>
+    <string name="message_staging" msgid="6151794817691100003">"అనువర్తనాన్ని అందిస్తోంది…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"తెలియదు"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"మీకు భద్రతను అందించడం కోసం, ఈ మూలం నుండి తెలియని యాప్‌లను ఇన్‌స్టాల్ చేయడానికి మీ టాబ్లెట్ అనుమతించబడదు."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"మీకు భద్రతను అందించడం కోసం, ఈ మూలం నుండి తెలియని యాప్‌లను ఇన్‌స్టాల్ చేయడానికి మీ TV అనుమతించబడదు."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"మీకు భద్రతను అందించడం కోసం, ఈ మూలం నుండి తెలియని యాప్‌లను ఇన్‌స్టాల్ చేయడానికి మీ ఫోన్ అనుమతించబడదు."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"మీ ఫోన్ మరియు వ్యక్తిగత డేటా తెలియని మూలాల్లోని అనువర్తనాల ద్వారా దాడికి గురి కావడానికి ఎక్కువ అవకాశం ఉంటుంది. ఈ అనువర్తనాన్ని ఇన్‌స్టాల్ చేయడం ద్వారా, ఈ అనువర్తనాన్ని ఉపయోగించడం వలన మీ ఫోన్‌కు సంభవించే ఏదైనా నష్టానికి లేదా కోల్పోయే డేటాకి బాధ్యత వహించడానికి మీరు అంగీకరిస్తున్నారు."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"మీ టాబ్లెట్ మరియు వ్యక్తిగత డేటా తెలియని మూలాల్లోని అనువర్తనాల ద్వారా దాడికి గురి కావడానికి ఎక్కువ అవకాశం ఉంటుంది. ఈ అనువర్తనాన్ని ఇన్‌స్టాల్ చేయడం ద్వారా, ఈ అనువర్తనాన్ని ఉపయోగించడం ద్వారా మీ టాబ్లెట్‌కు సంభవించే ఏదైనా నష్టానికి లేదా కోల్పోయే డేటాకి బాధ్యత వహించడానికి మీరు అంగీకరిస్తున్నారు."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"మీ TV మరియు వ్యక్తిగత డేటా తెలియని మూలాల్లోని అనువర్తనాల ద్వారా దాడికి గురి కావడానికి ఎక్కువ అవకాశం ఉంటుంది. ఈ అనువర్తనాన్ని ఇన్‌స్టాల్ చేయడం ద్వారా, ఈ అనువర్తనాన్ని ఉపయోగించడం ద్వారా మీ TVకి సంభవించే ఏదైనా నష్టానికి లేదా కోల్పోయే డేటాకి బాధ్యత వహించడానికి మీరు అంగీకరిస్తున్నారు."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"కొనసాగించు"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"సెట్టింగ్‌లు"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"వేర్ ఆప్‌లను ఇన్‌స్టాల్/అన్‌ఇన్‌స్టాల్ చేస్తోంది"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-television/colors.xml b/packages/PackageInstaller/res/values-television/colors.xml
new file mode 100644
index 0000000..1cacc00
--- /dev/null
+++ b/packages/PackageInstaller/res/values-television/colors.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<resources>
+
+    <color name="lb_content_title_text_color">#FFF1F1F1</color>
+    <color name="lb_content_breadcrumb_text_color">#88F1F1F1</color>
+    <color name="lb_content_description_text_color">#88F1F1F1</color>
+    <color name="lb_action_fragment_background">#FF111111</color>
+    <color name="lb_dialog_activity_background">#77000000</color>
+
+    <color name="lb_header_banner_color">#1f292d</color>
+
+    <color name="off_white">#ffeeeeee</color>
+</resources>
diff --git a/packages/PackageInstaller/res/values-television/dimens.xml b/packages/PackageInstaller/res/values-television/dimens.xml
new file mode 100644
index 0000000..d1c232e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-television/dimens.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+
+<resources>
+    <dimen name="action_dialog_z">16dp</dimen>
+    <dimen name="action_dialog_padding_left">52dp</dimen>
+    <dimen name="action_dialog_padding_right">40dp</dimen>
+    <dimen name="action_dialog_padding_top">41dp</dimen>
+    <dimen name="action_dialog_padding_bottom">27dp</dimen>
+
+    <dimen name="action_dialog_content_margin_left">24dp</dimen>
+    <dimen name="action_dialog_content_margin_right">32dp</dimen>
+
+    <dimen name="action_dialog_actions_width">304dp</dimen>
+    <dimen name="action_dialog_actions_margin_left">24dp</dimen>
+    <dimen name="action_dialog_actions_margin_top">18dp</dimen>
+
+    <dimen name="action_dialog_button_padding_left">16dp</dimen>
+    <dimen name="action_dialog_button_padding_right">16dp</dimen>
+    <dimen name="action_dialog_button_padding_top">14dp</dimen>
+    <dimen name="action_dialog_button_padding_bottom">15dp</dimen>
+    <dimen name="action_dialog_button_min_height">48dp</dimen>
+</resources>
diff --git a/packages/PackageInstaller/res/values-th-television/strings.xml b/packages/PackageInstaller/res/values-th-television/strings.xml
new file mode 100644
index 0000000..042a6f1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-th-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"ปฏิเสธและไม่ต้องถามอีก"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"คุณสามารถเปลี่ยนได้ภายหลังในการตั้งค่า &gt; แอป"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"แสดงแอประบบ"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"สิทธิ์ของแอป"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"สิทธิ์ของแอป"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"สิทธิ์เข้าถึง \"<xliff:g id="PERMISSION">%1$s</xliff:g>\""</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"สิทธิ์เพิ่มเติม"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"สิทธิ์เข้าถึง \"<xliff:g id="PERMISSION">%1$s</xliff:g>\""</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-th-watch/strings.xml b/packages/PackageInstaller/res/values-th-watch/strings.xml
new file mode 100644
index 0000000..05af1e8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-th-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"ปฏิเสธ ไม่ต้องถามอีก"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"แสดงแอประบบ"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"ไม่สามารถเปลี่ยน"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"ใช่"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"ยกเลิก"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-th/strings.xml b/packages/PackageInstaller/res/values-th/strings.xml
new file mode 100644
index 0000000..b7735a7
--- /dev/null
+++ b/packages/PackageInstaller/res/values-th/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"โปรแกรมติดตั้งแพ็กเกจ"</string>
+    <string name="next" msgid="3057143178373252333">"ถัดไป"</string>
+    <string name="install" msgid="5896438203900042068">"ติดตั้ง"</string>
+    <string name="done" msgid="3889387558374211719">"เสร็จสิ้น"</string>
+    <string name="cancel" msgid="8360346460165114585">"ยกเลิก"</string>
+    <string name="installing" msgid="8613631001631998372">"กำลังติดตั้ง..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"กำลังติดตั้ง <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"ติดตั้งแอปพลิเคชันแล้ว"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"คุณต้องการติดตั้งแอปพลิเคชันนี้หรือไม่ แอปพลิเคชันจะเข้าถึง:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"คุณต้องการจะติดตั้งแอปพลิเคชันนี้หรือไม่ แอปพลิเคชันไม่ต้องมีการเข้าถึงพิเศษใดๆ"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"คุณต้องการติดตั้งการอัปเดตของแอปพลิเคชันที่มีอยู่นี้หรือไม่ ข้อมูลที่มีอยู่ของคุณจะไม่สูญหายไป แอปพลิเคชันที่อัปเดตแล้วจะเข้าถึง:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"คุณต้องการจะติดตั้งการอัปเดตของแอปพลิเคชันในระบบนี้หรือไม่ ข้อมูลที่มีอยู่ของคุณจะไม่สูญหาย แอปพลิเคชันที่อัปเดตแล้วจะเข้าถึง:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"คุณต้องการติดตั้งการอัปเดตไปยังแอปพลิเคชันที่มีอยู่นี้หรือไม่ ข้อมูลที่มีอยู่ของคุณจะไม่สูญหาย การอัปเดตนี้ไม่จำเป็นต้องใช้การเข้าถึงใดๆ เป็นพิเศษ"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"คุณต้องการติดตั้งการอัปเดตไปยังแอปพลิเคชันในตัวนี้หรือไม่ ข้อมูลที่มีอยู่ของคุณจะไม่สูญหาย การอัปเดตนี้ไม่จำเป็นต้องใช้การเข้าถึงใดๆ เป็นพิเศษ"</string>
+    <string name="install_failed" msgid="6579998651498970899">"ไม่ได้ติดตั้งแอปพลิเคชัน"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"มีการบล็อกแพ็กเกจไม่ให้ติดตั้ง"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"ไม่ได้ติดตั้งแอปเพราะแพ็กเกจขัดแย้งกับแพ็กเกจที่มีอยู่"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"ไม่ได้ติดตั้งแอปเพราะแอปใช้งานไม่ได้กับแท็บเล็ตของคุณ"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"แอปนี้ไม่สามารถใช้งานกับทีวีของคุณ"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"ไม่ได้ติดตั้งแอปเพราะแอปใช้งานไม่ได้กับโทรศัพท์ของคุณ"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"ไม่ได้ติดตั้งแอปเพราะดูเหมือนว่าแพ็กเกจจะไม่ถูกต้อง"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"ไม่สามารถติดตั้ง <xliff:g id="APP_NAME">%1$s</xliff:g> บนแท็บเล็ตของคุณ"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> ไม่สามารถติดตั้งบนทีวีได้"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"ไม่สามารถติดตั้ง <xliff:g id="APP_NAME">%1$s</xliff:g> บนโทรศัพท์ของคุณ"</string>
+    <string name="launch" msgid="4826921505917605463">"เปิด"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"ผู้ดูแลระบบของคุณไม่อนุญาตให้ติดตั้งแอปที่ได้มาจากแหล่งที่มาที่ไม่รู้จัก"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"ผู้ใช้รายนี้ไม่สามารถติดตั้งแอปที่ไม่รู้จัก"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"ผู้ใช้รายนี้ไม่ได้รับอนุญาตให้ติดตั้งแอป"</string>
+    <string name="ok" msgid="3468756155452870475">"ตกลง"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"จัดการแอปพลิเคชัน"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"ไม่มีพื้นที่"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"ติดตั้ง <xliff:g id="APP_NAME">%1$s</xliff:g> ไม่ได้ เพิ่มพื้นที่ว่างแล้วลองอีกครั้ง"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"ไม่พบแอปพลิเคชัน"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"ไม่พบแอปพลิเคชันนี้ในรายการแอปพลิเคชันที่ติดตั้งไว้"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"ไม่ได้รับอนุญาต"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"ผู้ใช้ปัจจุบันไม่ได้รับอนุญาตให้ทำการถอนการติดตั้งนี้"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"พบข้อผิดพลาด"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"ไม่สามารถถอนการติดตั้งแอป"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"ถอนการติดตั้งแอปพลิเคชัน"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"ถอนการติดตั้งการอัปเดต"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> เป็นส่วนหนึ่งของแอปพลิเคชันต่อไปนี้:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"คุณต้องการถอนการติดตั้งแอปพลิเคชันนี้หรือไม่"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"คุณต้องการถอนการติดตั้งแอปพลิเคชันนี้สำหรับผู้ใช้"<b>"ทั้งหมด"</b>"หรือไม่ แอปพลิเคชันนี้และข้อมูลในแอปพลิเคชันจะถูกลบจากผู้ใช้"<b>"ทั้งหมด"</b>"ในอุปกรณ์"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"คุณต้องการถอนการติดตั้งแอปนี้สำหรับผู้ใช้ <xliff:g id="USERNAME">%1$s</xliff:g> ไหม"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"แทนที่แอปนี้ด้วยเวอร์ชันเริ่มต้นไหม ระบบจะนำข้อมูลทั้งหมดออก"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"แทนที่แอปนี้ด้วยเวอร์ชันเริ่มต้นไหม ระบบจะนำข้อมูลทั้งหมดออก วิธีนี้ส่งผลต่อผู้ใช้ทุกคนที่ใช้อุปกรณ์เครื่องนี้ รวมทั้งผู้ที่มีโปรไฟล์งาน"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"กำลังเรียกใช้การถอนการติดตั้ง"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"การถอนการติดตั้งที่ล้มเหลว"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"กำลังถอนการติดตั้ง..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"กำลังถอนการติดตั้ง <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"ถอนการติดตั้งเสร็จแล้ว"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"ถอนการติดตั้ง <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> แล้ว"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"ถอนการติดตั้งไม่สำเร็จ"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"การถอนการติดตั้ง <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ไม่สำเร็จ"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"ไม่สามารถถอนการติดตั้งแอปผู้ดูแลระบบอุปกรณ์ที่มีการใช้งาน"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"ไม่สามารถถอนการติดตั้งแอปผู้ดูแลระบบอุปกรณ์ที่มีการใช้งานสำหรับ <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"แอปนี้จำเป็นสำหรับผู้ใช้หรือโปรไฟล์บางส่วน และถอนการติดตั้งไปแล้วสำหรับส่วนอื่น"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"แอปนี้จำเป็นสำหรับโปรไฟล์ของคุณและไม่สามารถถอนการติดตั้งได้"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"ผู้ดูแลระบบอุปกรณ์กำหนดให้ใช้แอปนี้ และไม่สามารถถอนการติดตั้งได้"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"จัดการแอปผู้ดูแลระบบอุปกรณ์"</string>
+    <string name="manage_users" msgid="3125018886835668847">"จัดการผู้ใช้"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"ไม่สามารถถอดการติดตั้ง <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"พบปัญหาในการแยกวิเคราะห์แพ็กเกจ"</string>
+    <string name="newPerms" msgid="6039428254474104210">"ใหม่"</string>
+    <string name="allPerms" msgid="1024385515840703981">"ทั้งหมด"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"ข้อมูลส่วนบุคคล"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"การเข้าถึงอุปกรณ์"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"การอัปเดตนี้ไม่จำเป็นต้องมีการอนุญาตใหม่"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"ปฏิเสธ"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"ข้อมูลเพิ่มเติม"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"ยืนยันการปฏิเสธ"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> จาก <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> รายการ"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>ไหม"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>ทุกครั้งใช่ไหม"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"ขณะใช้แอปเท่านั้น"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"ตลอดเวลา"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"ปฏิเสธและไม่ต้องถามอีก"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"ปิดใช้ <xliff:g id="COUNT">%1$d</xliff:g> สิทธิ์"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"ปิดใช้สิทธิ์ทั้งหมด"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"ไม่มีการปิดใช้สิทธิ์"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"อนุญาต"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"แอป"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"สิทธิ์ของแอป"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"ไม่ต้องถามอีก"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"ไม่มีสิทธิ์"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"สิทธิ์เพิ่มเติม"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"เปิดข้อมูลแอป"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">อีก <xliff:g id="COUNT_1">%1$d</xliff:g> รายการ</item>
+      <item quantity="one">อีก <xliff:g id="COUNT_0">%1$d</xliff:g> รายการ</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"แอปนี้ออกแบบมาเพื่อ Android เวอร์ชันเก่า การปฏิเสธสิทธิ์อาจทำให้แอปไม่ทำงานตามที่ต้องการอีกต่อไป"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"ดำเนินการทำงานที่ไม่รู้จัก"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"อนุญาตแล้ว <xliff:g id="COUNT_0">%1$d</xliff:g> จาก <xliff:g id="COUNT_1">%2$d</xliff:g> แอป"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"แสดงระบบ"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"ซ่อนระบบ"</string>
+    <string name="no_apps" msgid="1965493419005012569">"ไม่มีแอป"</string>
+    <string name="location_settings" msgid="1774875730854491297">"การตั้งค่าตำแหน่ง"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> เป็นผู้ให้บริการตำแหน่งสำหรับอุปกรณ์นี้ คุณสามารถแก้ไขสิทธิ์เข้าถึงตำแหน่งได้จากการตั้งค่าตำแหน่ง"</string>
+    <string name="system_warning" msgid="7103819124542305179">"หากคุณปฏิเสธสิทธิ์นี้ ฟีเจอร์พื้นฐานของอุปกรณ์อาจไม่ทำงานตามที่ควรจะเป็นอีกต่อไป"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"บังคับใช้โดยนโยบาย"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"ปิดใช้การเข้าถึงในเบื้องหลังโดยนโยบาย"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"เปิดใช้การเข้าถึงในเบื้องหลังโดยนโยบาย"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"เปิดใช้การเข้าถึงในเบื้องหน้าโดยนโยบาย"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"ผู้ดูแลระบบเป็นผู้ควบคุม"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"ตลอดเวลา"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"ขณะใช้แอปเท่านั้น"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"ไม่เลย"</string>
+    <string name="loading" msgid="7811651799620593731">"กำลังโหลด…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"สิทธิ์ทั้งหมด"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"ความสามารถอื่นๆ ของแอป"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"คำขอสิทธิ์"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"ตรวจพบการวางซ้อนหน้าจอ"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"ในการเปลี่ยนแปลงการตั้งค่าสิทธิ์นี้ ก่อนอื่น คุณต้องปิดการวางซ้อนหน้าจอที่การตั้งค่า &gt; แอป"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"เปิดการตั้งค่า"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"ไม่สามารถติดตั้ง/ถอนการติดตั้งบน Wear"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"โปรดเลือกข้อมูลที่อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึง"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"อัปเดต &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; แล้ว โปรดเลือกข้อมูลที่อนุญาตให้แอปนี้เข้าถึง"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"ยกเลิก"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"ต่อไป"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"สิทธิ์ใหม่"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"สิทธิ์ปัจจุบัน"</string>
+    <string name="message_staging" msgid="6151794817691100003">"กำลังปรับสภาพแวดล้อมของแอป…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"ไม่ทราบ"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"เพื่อความปลอดภัย ไม่อนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้ในแท็บเล็ต"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"เพื่อความปลอดภัย ไม่อนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้ในทีวี"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"เพื่อความปลอดภัย ไม่อนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้ในโทรศัพท์"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"โทรศัพท์และข้อมูลส่วนบุคคลของคุณมีความเสี่ยงมากขึ้นที่จะถูกโจมตีจากแอปที่ไม่รู้จัก การติดตั้งแอปนี้เป็นการยอมรับว่าคุณจะรับผิดชอบความเสียหายต่อเครื่องโทรศัพท์หรือการสูญเสียข้อมูลที่อาจเกิดจากการใช้งานแอปดังกล่าว"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"แท็บเล็ตและข้อมูลส่วนบุคคลของคุณมีความเสี่ยงมากขึ้นที่จะถูกโจมตีจากแอปที่ไม่รู้จัก การติดตั้งแอปนี้เป็นการยอมรับว่าคุณจะรับผิดชอบความเสียหายต่อเครื่องแท็บเล็ตหรือการสูญเสียข้อมูลที่อาจเกิดจากการใช้งานแอปดังกล่าว"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"ทีวีและข้อมูลส่วนบุคคลของคุณมีความเสี่ยงมากขึ้นที่จะถูกโจมตีจากแอปที่ไม่รู้จัก การติดตั้งแอปนี้เป็นการยอมรับว่าคุณจะรับผิดชอบความเสียหายต่อเครื่องทีวีหรือการสูญเสียข้อมูลที่อาจเกิดจากการใช้งานแอปดังกล่าว"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"ต่อไป"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"การตั้งค่า"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"กำลังติดตั้ง/ถอนการติดตั้งแอป Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-tl-television/strings.xml b/packages/PackageInstaller/res/values-tl-television/strings.xml
new file mode 100644
index 0000000..df5b98f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-tl-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Tanggihan at huwag nang tatanunging muli"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Mababago mo ito sa ibang pagkakataon sa Mga Setting &gt; Mga App"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Ipakita ang mga app ng system"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Mga pahintulot sa app"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Mga pahintulot sa app"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Mga pahintulot sa <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Mga karagdagang pahintulot"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Mga pahintulot sa <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-tl-watch/strings.xml b/packages/PackageInstaller/res/values-tl-watch/strings.xml
new file mode 100644
index 0000000..8597451
--- /dev/null
+++ b/packages/PackageInstaller/res/values-tl-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Tanggihan, huwag nang tatanunging muli"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Ipakita ang mga app ng system"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Hindi mababago"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Oo"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Kanselahin"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-tl/strings.xml b/packages/PackageInstaller/res/values-tl/strings.xml
new file mode 100644
index 0000000..05eba98
--- /dev/null
+++ b/packages/PackageInstaller/res/values-tl/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Installer ng package"</string>
+    <string name="next" msgid="3057143178373252333">"Susunod"</string>
+    <string name="install" msgid="5896438203900042068">"Mag-install"</string>
+    <string name="done" msgid="3889387558374211719">"Tapos na"</string>
+    <string name="cancel" msgid="8360346460165114585">"Kanselahin"</string>
+    <string name="installing" msgid="8613631001631998372">"Nag-i-install…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Ini-install ang <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Na-install ang app."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Nais mo bang i-install ang application na ito? Magkakaroon ito ng access sa:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Nais mo bang i-install ang application na ito? Hindi ito nangangailangan ng anumang espesyal na access."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Nais mo bang mag-install ng update sa umiiral nang application na ito? Hindi mawawala ang iyong umiiral nang data. Magkakaroon ng access ang na-update na application sa:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Nais mo bang mag-install ng update sa built-in na application na ito? Hindi mawawala ang iyong umiiral na data. Magkakaroon ng access ang na-update na application sa:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Nais mo bang mag-install ng update sa umiiral na application na ito? Hindi mawawala ang iyong umiiral na data. Hindi ito nangangailangan ng anumang espesyal na access."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Nais mo bang mag-install ng update sa built-in na application na ito? Hindi mawawala ang iyong umiiral na data. Hindi ito nangangailangan ng anumang espesyal na access."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Hindi na-install ang app."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Na-block ang pag-install sa package."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Hindi na-install ang app dahil nagkakaproblema ang package sa isang dati nang package."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Hindi na-install ang app dahil hindi tugma ang app sa iyong tablet."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Hindi compatible ang app na ito sa iyong TV."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Hindi na-install ang app dahil hindi tugma ang app sa iyong telepono."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Hindi na-install ang app dahil lumalabas na di-wasto ang package."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Hindi ma-install ang <xliff:g id="APP_NAME">%1$s</xliff:g> sa iyong tablet."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay hindi ma-install sa iyong TV."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Hindi ma-install ang <xliff:g id="APP_NAME">%1$s</xliff:g> sa iyong telepono."</string>
+    <string name="launch" msgid="4826921505917605463">"Buksan"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Hindi pinapayagan ng iyong admin ang pag-install ng mga app na nakuha mula sa mga hindi kilalang pinagmulan"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Hindi maaaring mag-install ang user na ito ng mga hindi kilalang app"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Hindi pinapayagan ang user na mag-install ng mga app"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Pamahalaan ang apps"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Wala ng espasyo"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Hindi ma-install ang <xliff:g id="APP_NAME">%1$s</xliff:g>. Magbakante ng ilang espasyo at subukang muli."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Hindi makita ang app"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Hindi makita ang app sa listahan ng naka-install na apps."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Hindi pinapayagan"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Hindi pinapayagan ang kasalukuyang user na gawin ang pag-uninstall na ito."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Error"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Hindi ma-uninstall ang app."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"I-uninstall ang app"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"I-uninstall ang update"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"Bahagi ang <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ng sumusunod na app:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Gusto mo bang i-uninstall ang app na ito?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Nais mo bang i-uninstall ang app na ito para sa "<b>"lahat"</b>" ng user? Aalisin ang application at ang data nito mula sa "<b>"lahat"</b>" ng user sa device."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"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="1394549691152728409">"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="2083665452990861991">"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, kasama ang mga may profile sa trabaho."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Mga nasa proseso ng pag-uninstall"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Mga hindi na-uninstall"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Ina-uninstall…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Ina-uninstall ang <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Natapos ang pag-uninstall."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Na-uninstall ang <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Di-matagumpay ang pag-uninstall."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Hindi na-uninstall ang <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Hindi ma-uninstall ang aktibong app ng admin ng device"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Hindi ma-uninstall ang aktibong app ng admin ng device para kay  <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Ang app na ito ay kailangan ng ilang user o profile at na-uninstall na ito sa iba pa"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Ang app na ito ay kailangan para sa iyong profile at hindi maaaring i-uninstall."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Kinakailangan app na ito ng administrator ng device mo at di maaari i-uninstall."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Pamahalaan ang mga app ng admin ng device"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Pamahalaan ang mga user"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Hindi ma-install ang <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Nagkaroon ng problema sa pag-parse sa package."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Bago"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Lahat"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Privacy"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Access sa Device"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Walang kinakailangang mga bagong pagpapahintulot ang update na ito."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Tanggihan"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Higit pang impormasyon"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Tanggihan pa rin"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> ng <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Palaging payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Habang ginagamit lang ang app"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Palagi"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Tanggihan at huwag nang itanong muli"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> ang naka-disable"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"naka-disable lahat"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"walang naka-disable"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Payagan"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Mga App"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Mga pahintulot sa app"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Huwag nang tatanunging muli"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Walang pahintulot"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Mga karagdagang pahintulot"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Buksan ang impormasyon ng app"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> pa</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> pa</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Ang app na ito ay idinisenyo para sa mas lumang bersyon ng Android. Kapag tinanggihan ang pahintulot, maaaring hindi na ito gumana ayon sa inaasahan."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"gumawa ng hindi kilalang pagkilos"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Pinapayagan ang <xliff:g id="COUNT_0">%1$d</xliff:g> sa <xliff:g id="COUNT_1">%2$d</xliff:g> (na) app"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Ipakita ang system"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Itago ang system"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Walang mga app"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Mga Setting ng Lokasyon"</string>
+    <string name="location_warning" msgid="8778701356292735971">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay isang provider ng mga serbisyo sa lokasyon para sa device na ito. Mababago ang access sa lokasyon mula sa mga setting ng lokasyon."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Kung tatanggihan mo ang pahintulot na ito, maaaring hindi na gumana ang mga pangunahing feature ng iyong device gaya ng inaasahan."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Ipinapatupad sa pamamagitan ng patakaran"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Na-disable ayon sa patakaran ang pag-access sa background"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Na-enable ayon sa patakaran ang pag-access sa background"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Na-enable ayon sa patakaran ang pag-access sa foreground"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Kinokontrol ng admin"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Palagi"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Habang ginagamit lang ang app"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Huwag Kailanman"</string>
+    <string name="loading" msgid="7811651799620593731">"Naglo-load..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Lahat ng pahintulot"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Iba pang mga kakayahan ng app"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Kahilingan sa pagpapahintulot"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Natukoy ang overlay ng screen"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Upang baguhin ang setting ng pahintulot na ito, kailangan mo munang i-off ang overlay ng screen mula sa Mga Setting &gt; Mga App"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Buksan ang mga setting"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Ang mga pagkilos na I-install/I-uninstall ay hindi sinusuportahan sa Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Piliin kung ano ang papayagang i-access ng &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Na-update na ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. Piliin kung ano ang papayagang i-access ng app na ito."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Kanselahin"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Magpatuloy"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Mga bagong pahintulot"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Mga kasalukuyang pahintulot"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Inihahanda ang app…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Hindi Alam"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Para sa iyong seguridad, hindi pinapayagan ang tablet mo na mag-install ng mga hindi alam na app mula sa pinagmulang ito."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Para sa iyong seguridad, hindi pinapayagan ang TV mo na mag-install ng mga hindi alam na app mula sa pinagmulang ito."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Para sa iyong seguridad, hindi pinapayagan ang telepono mo na mag-install ng mga hindi alam na app mula sa pinagmulang ito."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Mas nanganganib ang iyong telepono at personal na data sa mga pag-atake mula sa mga hindi kilalang app. Sa pamamagitan ng pag-install ng app na ito, sumasang-ayon ka na ikaw ang responsable sa anumang pinsala sa telepono mo o pagkawala ng data na maaaring magresulta mula sa paggamit nito."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Mas nanganganib ang iyong tablet at personal na data sa mga pag-atake mula sa mga hindi kilalang app. Sa pamamagitan ng pag-install ng app na ito, sumasang-ayon ka na ikaw ang responsable sa anumang pinsala sa tablet mo o pagkawala ng data na maaaring magresulta mula sa paggamit nito."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Mas nanganganib ang iyong TV at personal na data sa mga pag-atake mula sa mga hindi kilalang app. Sa pamamagitan ng pag-install ng app na ito, sumasang-ayon ka na ikaw ang responsable sa anumang pinsala sa TV mo o pagkawala ng data na maaaring magresulta mula sa paggamit nito."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Magpatuloy"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Mga Setting"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Ini-install/ina-uninstall ang wear apps"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-tr-television/strings.xml b/packages/PackageInstaller/res/values-tr-television/strings.xml
new file mode 100644
index 0000000..7ed0a68
--- /dev/null
+++ b/packages/PackageInstaller/res/values-tr-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Reddet ve bir daha sorma"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Bu ayarı daha sonra Ayarlar &gt; Uygulamalar\'dan değiştirebilirsiniz"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Sistem uygulamalarını göster"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Uygulama izinleri"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Uygulama izinleri"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> izinleri"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Ek izinler"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> izinleri"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-tr-watch/strings.xml b/packages/PackageInstaller/res/values-tr-watch/strings.xml
new file mode 100644
index 0000000..fbc5b93
--- /dev/null
+++ b/packages/PackageInstaller/res/values-tr-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Reddet, bir daha sorma"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Sistem uygulamalarını göster"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Değiştirilemez"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Evet"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"İptal"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml
new file mode 100644
index 0000000..ef7882d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-tr/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Paket yükleyici"</string>
+    <string name="next" msgid="3057143178373252333">"Sonraki"</string>
+    <string name="install" msgid="5896438203900042068">"Yükle"</string>
+    <string name="done" msgid="3889387558374211719">"Bitti"</string>
+    <string name="cancel" msgid="8360346460165114585">"İptal"</string>
+    <string name="installing" msgid="8613631001631998372">"Yükleniyor…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> yükleniyor…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Uygulama yüklendi."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Bu uygulamayı yüklemek istiyor musunuz? Uygulama şunlara erişebilecektir:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Bu uygulamayı yüklemek istiyor musunuz? Herhangi bir özel erişim gerektirmez."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Bu mevcut uygulamaya ait bir güncellemeyi yüklemek istiyor musunuz? Mevcut verileriniz silinmeyecektir. Güncellenen uygulama şunlara erişebilecektir:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Bu yerleşik uygulamaya ait bir güncellemeyi yüklemek istiyor musunuz? Mevcut verileriniz silinmeyecektir. Güncellenen uygulama şunlara erişebilecektir:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Bu mevcut uygulamaya ait bir güncellemeyi yüklemek istiyor musunuz? Mevcut verileriniz kaybolacaktır. Herhangi bir özel erişim gerektirmez."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Bu yerleşik uygulamaya ait bir güncellemeyi yüklemek istiyor musunuz? Mevcut verileriniz kaybolacaktır. Herhangi bir özel erişim gerektirmez."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Uygulama yüklenmedi."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Paketin yüklemesi engellendi."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Paket, mevcut bir paketle çakıştığından uygulama yüklenemedi."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Tabletinizle uyumlu olmadığından uygulama yüklenemedi."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Bu uygulama TV\'niz ile uyumlu değil."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Telefonunuzla uyumlu olmadığından uygulama yüklenemedi."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Paket geçersiz göründüğünden uygulama yüklenemedi."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> bu tabletinize yüklenemedi."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> TV\'nize yüklenemedi."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> bu telefonunuza yüklenemedi."</string>
+    <string name="launch" msgid="4826921505917605463">"Aç"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Yöneticiniz, bilinmeyen kaynaklardan edinilen uygulamaların yüklenmesine izin vermiyor"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Bilinmeyen uygulamalar bu kullanıcı tarafından yüklenemez"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Bu kullanıcının uygulama yüklemesine izin verilmiyor"</string>
+    <string name="ok" msgid="3468756155452870475">"Tamam"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Uygulamaları yönet"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Yer kalmadı"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> yüklenemedi. Boş alan açın ve yeniden deneyin."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Uygulama bulunamadı"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Uygulama, yüklü uygulamalar listesinde bulunamadı."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"İzin verilmiyor"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Geçerli kullanıcının bu yüklemeyi kaldırma izni yok."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Hata"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Uygulamanın yüklemesi kaldırılamadı."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Uygulamayı kaldır"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Güncelleme kaldırılsın mı?"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, şu uygulamanın bir parçasıdır:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Bu uygulamanın yüklemesini kaldırmak istiyor musunuz?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Bu uygulamanın yüklemesini "<b>"tüm"</b>" kullanıcılar için kaldırmak istiyor musunuz? Uygulama ve verileri cihazdan "<b>"tüm"</b>" kullanıcılar için kaldırılacaktır."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"<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="1394549691152728409">"Bu uygulamayı fabrika sürümüyle değiştirmek istiyor musunuz? Tüm veriler silinecektir."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Devam eden yükleme kaldırma işlemleri"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Başarısız yükleme kaldırma işlemleri"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Kaldırılıyor…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> uygulamasının yüklemesi kaldırılıyor…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Kaldırma işlemi tamamlandı."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> uygulamasının yüklemesi kaldırıldı"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Yükleme kaldırılamadı."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> uygulamasının yüklemesi kaldırılamadı."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Etkin cihaz yönetimi uygulamasının yüklemesi kaldırılamıyor"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> adlı kullanıcı için etkin cihaz yönetimi uygulamasının yüklemesi kaldırılamıyor"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Bu uygulama bazı kullanıcılar veya profiller için gerekli ve diğerleri için uygulamanın yüklemesi kaldırıldı"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Profiliniz için bu uygulama gerekli ve yüklemesi kaldırılamaz."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Bu uygulama, cihazınızın yöneticisi için gereklidir ve yüklemesi kaldırılamaz."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Cihaz yönetimi uygulamalarını yönet"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Kullanıcıları yönetme"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> kaldırılamadı."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Paketin ayrıştırılmasında bir sorun oluştu."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Yeni"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Tümü"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Gizlilik"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Cihaz Erişimi"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Bu güncelleme yeni izin gerektirmiyor."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Reddet"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Daha fazla bilgi"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Yine de reddet"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasına <xliff:g id="ACTION">%2$s</xliff:g> izni verilsin mi?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasına <xliff:g id="ACTION">%2$s</xliff:g> için izin verilsin m?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Sadece uygulama kullanılırken"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Her zaman"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Reddet ve bir daha sorma"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> izin devre dışı"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"tümü devre dışı"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"hiçbiri devre dışı değil"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"İzin ver"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Uygulamalar"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Uygulama izinleri"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Tekrar sorma"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"İzin yok"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Ek izinler"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Uygulama bilgilerini aç"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> tane daha</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> tane daha</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Bu uygulama, Android\'in daha eski bir sürümü için tasarlandı. İznin reddedilmesi, uygulamanın bundan sonra amaçlandığı gibi çalışmamasına neden olabilir."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"bilinmeyen bir işlem gerçekleştirme"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> / <xliff:g id="COUNT_1">%2$d</xliff:g> uygulamaya izin veriliyor"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Sistemi göster"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Sistemi gizle"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Uygulama yok"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Konum Ayarları"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g>, bu cihaz için konum hizmetlerinin bir sağlayıcısıdır. Konum erişimi, konum ayarlarından değiştirilebilir."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Bu izni reddederseniz cihazınızın temel özellikleri artık beklendiği gibi çalışmayabilir."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Politika tarafından zorunlu tutuldu"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Arka plan erişimi politika tarafından devre dışı bırakıldı"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Arka plan erişimi politika tarafından etkinleştirildi"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Ön plan erişimi politika tarafından etkinleştirildi"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Yönetici tarafından kontrol ediliyor"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Her zaman"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Sadece uygulama kullanılırken"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Hiçbir zaman"</string>
+    <string name="loading" msgid="7811651799620593731">"Yükleniyor..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Tüm izinler"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Diğer uygulama özellikleri"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"İzin isteği"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Ekran yer paylaşımı tespit edildi"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Bu izin ayarını değiştirmek için ilk olarak Ayarlar &gt; Uygulamalar\'dan ekran yer paylaşımını kapatmanız gerekir"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Ayarları aç"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Yükleme/Yüklemeyi Kaldırma işlemleri Wear\'da desteklenmiyor."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının nelere erişmesine izin vereceğinizi seçin"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; güncellendi. Bu uygulamanın nelere erişmesine izin verileceğini seçin."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"İptal"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Devam"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Yeni izinler"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Geçerli izinler"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Uygulama hazırlanıyor…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Bilinmiyor"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Güvenlik nedeniyle tabletinizin bu kaynaktan bilinmeyen uygulamalar yüklemesine izin verilmez."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Güvenlik nedeniyle TV\'nizin bu kaynaktan bilinmeyen uygulamalar yüklemesine izin verilmez."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Güvenlik nedeniyle telefonunuzun bu kaynaktan bilinmeyen uygulamalar yüklemesine izin verilmez."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Telefonunuz ve kişisel verileriniz, bilinmeyen uygulamaların saldırılarına karşı daha savunmasızdır. Bu uygulamayı yükleyerek, uygulama kullanımından dolayı telefonunuzda oluşabilecek hasarın veya uğrayabileceğiniz veri kaybının sorumluluğunu kabul etmiş olursunuz."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Tabletiniz ve kişisel verileriniz, bilinmeyen uygulamaların saldırılarına karşı daha savunmasızdır. Bu uygulamayı yükleyerek, uygulama kullanımından dolayı tabletinizde oluşabilecek hasarın veya uğrayabileceğiniz veri kaybının sorumluluğunu kabul etmiş olursunuz."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"TV\'niz ve kişisel verileriniz, bilinmeyen uygulamaların saldırılarına karşı daha savunmasızdır. Bu uygulamayı yükleyerek, uygulama kullanımından dolayı TV\'nizde oluşabilecek hasarın veya uğrayabileceğiniz veri kaybının sorumluluğunu kabul etmiş olursunuz."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Devam"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Ayarlar"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear uyg. yükleme/yüklemesini kaldırma"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-uk-television/strings.xml b/packages/PackageInstaller/res/values-uk-television/strings.xml
new file mode 100644
index 0000000..f4b002f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-uk-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Відмовити й більше не запитувати"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Можна змінити згодом у меню \"Налаштування\" &gt; \"Додатки\""</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> з <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Показати системні додатки"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Дозволи додатка"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Дозволи додатка"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g>: дозволи"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Додаткові дозволи"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g>: дозволи"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-uk-watch/strings.xml b/packages/PackageInstaller/res/values-uk-watch/strings.xml
new file mode 100644
index 0000000..e539d83
--- /dev/null
+++ b/packages/PackageInstaller/res/values-uk-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Відхилити й більше не запитувати"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> з <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Показати системні додатки"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Не можна змінити"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Так"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Скасувати"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-uk/strings.xml b/packages/PackageInstaller/res/values-uk/strings.xml
new file mode 100644
index 0000000..bdc09df
--- /dev/null
+++ b/packages/PackageInstaller/res/values-uk/strings.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Програма встановлення пакета"</string>
+    <string name="next" msgid="3057143178373252333">"Далі"</string>
+    <string name="install" msgid="5896438203900042068">"Установити"</string>
+    <string name="done" msgid="3889387558374211719">"Готово"</string>
+    <string name="cancel" msgid="8360346460165114585">"Скасувати"</string>
+    <string name="installing" msgid="8613631001631998372">"Встановлення…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Установлюється <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Додаток установлено."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Установити цей додаток? Він отримає такі дозволи:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Установити цей додаток? Йому не потрібні спеціальні дозволи."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Дійсно встановити оновлення для цієї наявної програми? Існуючі дані втрачено не буде. Оновлена програма отримає доступ до:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Дійсно встановити оновлення для цієї вбудованої програми? Існуючі дані втрачено не буде. Оновлена програма отримає доступ до:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Хочете встановити оновлення для наявної програми? Ваші наявні дані не зникнуть. Спеціальний доступ не потрібен."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Хочете встановити оновлення для цієї вбудованої програми? Ваші наявні дані не зникнуть. Спеціальний доступ не потрібен."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Додаток не встановлено."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Встановлення пакета заблоковано."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Додаток не встановлено, оскільки пакет конфліктує з наявним пакетом."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Додаток не встановлено, оскільки він несумісний із вашим планшетом."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Цей додаток несумісний із вашим телевізором."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Додаток не встановлено, оскільки він несумісний із вашим телефоном."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Додаток не встановлено, оскільки пакет недійсний."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Програму <xliff:g id="APP_NAME">%1$s</xliff:g> неможливо встановити у вашому планшетному ПК."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Не вдалося встановити додаток <xliff:g id="APP_NAME">%1$s</xliff:g> на ваш телевізор."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Програму <xliff:g id="APP_NAME">%1$s</xliff:g> неможливо встановити у вашому телефоні."</string>
+    <string name="launch" msgid="4826921505917605463">"Відкрити"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Ваш адміністратор заборонив установлювати додатки з невідомих джерел"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Цей користувач не може встановлювати невідомі додатки"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Цей користувач не може встановлювати додатки"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Керувати програмами"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Недостат. місця"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Програму <xliff:g id="APP_NAME">%1$s</xliff:g> неможливо встановити. Звільніть місце та повторіть спробу."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Програму не знайдено"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Програму не знайдено в списку встановлених програм."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Заборонено"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Поточний користувач не може видалити цей додаток."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Помилка"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Не вдалося видалити додаток."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Видалити програму"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Видалити оновлення"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"Дія <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> є частиною такої програми:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Видалити додаток?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Хочете видалити цю програму для "<b>"всіх"</b>" користувачів? Програму та її дані буде видалено для "<b>"всіх"</b>" користувачів цього пристрою."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Видалити цей додаток для користувача <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Відновити заводську версію цього додатка? Усі дані буде видалено."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Відновити заводську версію цього додатка? Усі дані буде видалено. Це вплине на всіх користувачів цього пристрою, зокрема на користувачів із робочими профілями."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Активні видалення"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Невиконані видалення"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Видалення..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Видалення додатка <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Видалення завершено."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Додаток <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> видалено"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Видалення не здійснено."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Не вдалося видалити додаток <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Не вдається видалити активний додаток адміністратора пристрою"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Не вдається видалити активний додаток адміністратора пристрою для користувача <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Цей додаток потрібен для деяких користувачів чи профілів, але його було видалено для інших"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Цей додаток потрібен для вашого профілю, тому його не можна видалити."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Цей додаток не можна видалити – не дозволяє адміністратор пристрою."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Керувати додатками адміністратора пристрою"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Керувати користувачами"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Програму <xliff:g id="APP_NAME">%1$s</xliff:g> неможливо видалити."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Під час аналізу пакету виникла помилка."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Нові"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Усі"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Конфіденційність"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Доступ до пристрою"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Для цього оновлення не потрібні нові дозволи."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Відхилити"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Докладніше"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Усе одно заборонити"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> з <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Завжди дозволяти додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Лише коли додаток активний"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Завжди"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Відхилити й більше не запитувати"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> скасовано"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"усі скасовано"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"нічого не скасовано"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Дозволити"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Додатки"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Дозволи додатків"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Не запитувати знову"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Немає дозволів"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Додаткові дозволи"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Відкрити інформацію про додаток"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one">Ще <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="few">Ще <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+      <item quantity="many">Ще <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="old_sdk_deny_warning" msgid="3872277112584842615">"Цей додаток створено для старішої версії ОС Android. Якщо скасувати дозвіл, він може працювати неналежним чином."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"виконувати невідому дію"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Додатки з дозволом: <xliff:g id="COUNT_0">%1$d</xliff:g> з <xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Показати системні додатки"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Сховати системні додатки"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Немає додатків"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Налаштування геоданих"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> є постачальником служб локації для цього пристрою. Доступом до місцезнаходження можна керувати в налаштуваннях геоданих."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Якщо скасувати цей дозвіл, основні функції вашого пристрою можуть працювати неналежним чином."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Застосовується правилом"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Доступ у фоновому режимі вимкнено правилом"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Доступ у фоновому режимі ввімкнено правилом"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Доступ в активному режимі ввімкнено правилом"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Керує адміністратор"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Завжди"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Лише коли додаток активний"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Ніколи"</string>
+    <string name="loading" msgid="7811651799620593731">"Завантаження…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Усі дозволи"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Інші дозволи додатка"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Запит на дозвіл"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Виявлено накладання на екрані"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Щоб змінити налаштування цього дозволу, спершу вимкніть накладання на екрані в меню \"Налаштування\" &gt; \"Додатки\""</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Відкрити налаштування"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Дії \"установити\" або \"видалити\" не підтримуються на пристроях Android Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Виберіть, до чого &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; матиме доступ"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; оновлено. Виберіть, до чого цей додаток матиме доступ."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Скасувати"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Продовжити"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Нові дозволи"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Поточні дозволи"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Підготовка додатка…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Невідомо"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"З міркувань безпеки на вашому планшеті заборонено встановлювати невідомі додатки з цього джерела."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"З міркувань безпеки на вашому телевізорі заборонено встановлювати невідомі додатки з цього джерела."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"З міркувань безпеки на вашому телефоні заборонено встановлювати невідомі додатки з цього джерела."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Ваш телефон і особисті дані більш уразливі до атак невідомих додатків. Установлюючи цей додаток, ви берете на себе відповідальність за пошкодження телефона чи втрату даних унаслідок використання додатка."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Ваш планшет і особисті дані більш уразливі до атак невідомих додатків. Установлюючи цей додаток, ви берете на себе відповідальність за пошкодження планшета чи втрату даних унаслідок використання додатка."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Ваш телевізор і особисті дані більш уразливі до атак невідомих додатків. Установлюючи цей додаток, ви берете на себе відповідальність за пошкодження телевізора чи втрату даних унаслідок використання додатка."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Продовжити"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Налаштування"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Встановлення або видалення додатків Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ur-television/strings.xml b/packages/PackageInstaller/res/values-ur-television/strings.xml
new file mode 100644
index 0000000..27089dd
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ur-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"انکار کریں اور دوبارہ مت پوچھیں"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"آپ بعد میں ترتیبات &gt; ایپس میں جا کر اسے تبدیل کرسکتے ہیں"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"سسٹم ایپس دکھائیں"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"ایپ کی اجازتیں"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"ایپ کی اجازتیں"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> اجازتیں"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"اضافی اجازتیں"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> اجازتیں"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ur-watch/strings.xml b/packages/PackageInstaller/res/values-ur-watch/strings.xml
new file mode 100644
index 0000000..197ac84
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ur-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"انکار کریں، دوبارہ مت پوچھیں"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"سسٹم ایپس دکھائیں"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"ناقابل تبدیل"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"ہاں"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"منسوخ کریں"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-ur/strings.xml b/packages/PackageInstaller/res/values-ur/strings.xml
new file mode 100644
index 0000000..78135a6
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ur/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"پیکیج انسٹال کنندہ"</string>
+    <string name="next" msgid="3057143178373252333">"اگلا"</string>
+    <string name="install" msgid="5896438203900042068">"انسٹال کریں"</string>
+    <string name="done" msgid="3889387558374211719">"ہو گیا"</string>
+    <string name="cancel" msgid="8360346460165114585">"منسوخ کریں"</string>
+    <string name="installing" msgid="8613631001631998372">"انسٹال کیا جا رہا ہے…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> کو انسٹال کیا جا رہا ہے…"</string>
+    <string name="install_done" msgid="3682715442154357097">"ایپ انسٹال ہوگئی۔"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"کیا آپ یہ ایپلیکیشن انسٹال کرنا چاہتے ہیں؟ اس کو مندرجہ ذیل تک رسائی حاصل ہوگی:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"کیا آپ یہ ایپلیکیشن انسٹال کرنا چاہتے ہیں؟ اس کو کوئی خاص رسائی درکار نہیں۔"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"کیا آپ اس موجودہ ایپلیکیشن میں ایک اپ ڈیٹ انسٹال کرنا چاہتے ہیں؟ آپ کا موجودہ ڈیٹا ضائع نہیں ہوگا۔ اپ ڈیٹ کردہ ایپلیکیشن کو مندرجہ ذیل تک رسائی حاصل ہوگی:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"کیا آپ پہلے سے شامل اس ایپلیکیشن میں ایک اپ ڈیٹ انسٹال کرنا چاہتے ہیں؟ آپ کا موجودہ ڈیٹا ضائع نہیں ہوگا۔ اپ ڈیٹ کردہ ایپلیکیشن کو مندرجہ ذیل تک رسائی حاصل ہوگی:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"کیا آپ اس موجودہ ایپلیکیشن میں ایک اپ ڈیٹ انسٹال کرنا چاہتے ہیں؟ آپ کا موجودہ ڈیٹا ضائع نہیں ہوگا۔ اس کو کوئی خاص رسائی درکار نہیں۔"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"کیا آپ پہلے سے شامل اس ایپلیکیشن میں ایک اپ ڈیٹ انسٹال کرنا چاہتے ہیں؟ آپ کا موجودہ ڈیٹا ضائع نہیں ہوگا۔ اس کو کوئی خاص رسائی درکار نہیں۔"</string>
+    <string name="install_failed" msgid="6579998651498970899">"ایپ انسٹال نہیں ہوئی۔"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"پیکج کو انسٹال ہونے سے روک دیا گیا۔"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"ایپ کو پیکج کے بطور انسٹال نہیں کیا گیا کیونکہ پیکج ایک موجودہ پیکیج سے متصادم ہے۔"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"ایپ انسٹال نہیں ہوئی کیونکہ ایپ آپ کے ٹیبلیٹ کے ساتھ مطابقت پذیر نہیں ہے۔"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"‏یہ ایپ آپ کے TV کے ساتھ مطابقت پذیر نہیں ہے۔"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"ایپ انسٹال نہیں ہوئی کیونکہ ایپ آپ کے فون کے ساتھ مطابقت پذیر نہیں ہے۔"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"ایپ انسٹال نہیں ہوئی کیونکہ پیکیج غلط معلوم ہوتا ہے۔"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> کو آپ کے ٹیبلیٹ پر انسٹال نہیں کیا جا سکا۔"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"‏<xliff:g id="APP_NAME">%1$s</xliff:g> کو آپ کے TV پر انسٹال نہیں کیا جا سکا۔"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> کو آپ کے فون پر انسٹال نہیں کیا جا سکا۔"</string>
+    <string name="launch" msgid="4826921505917605463">"کھولیں"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"آپ کا منتظم نامعلوم ذرائع سے اخذ کردہ ایپس کو انسٹال کرنے کی اجازت نہیں دیتا ہے"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"اس صارف کے ذریعے نامعلوم ایپس کو انسٹال نہیں کیا جا سکتا"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"اس صارف کو ایپس انسٹال کرنے کی اجازت نہیں ہے"</string>
+    <string name="ok" msgid="3468756155452870475">"ٹھیک ہے"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"ایپس کا نظم کریں"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"جگہ ختم ہو گئی ہے"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> کو انسٹال نہیں کیا جا سکا۔ کچھ جگہ خالی کریں اور دوبارہ کوشش کریں۔"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"ایپ نہیں ملی"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"ایپ انسٹال کردہ ایپس کی فہرست میں نہیں ملی۔"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"اجازت نہیں ہے"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"موجودہ صارف کو اس ان انسٹالیشن کو سرانجام دینے کی اجازت نہیں ہے۔"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"خرابی"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"ایپ ان انسٹال نہیں ہو سکی۔"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"ایپ کو اَن انسٹال کریں"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"اپ ڈیٹ اَن انسٹال کریں"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> درج ذیل ایپ کا حصہ ہے:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"کیا آپ یہ ایپ اَن انسٹال کرنا چاہتے ہیں؟"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"کیا آپ "<b>"سبھی"</b>" صارفین کیلئے یہ ایپ اَن انسٹال کرنا چاہتے ہیں؟ ایپلیکیشن اور اس کا ڈیٹا آلے پر موجود "<b>"سبھی"</b>" صارفین سے ہٹا دیا جائے گا۔"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"کیا آپ اس ایپ کو صارف <xliff:g id="USERNAME">%1$s</xliff:g> کیلئے اَن انسٹال کرنا چاہتے ہیں؟"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"اس ایپ کو فیکٹری ورژن سے تبدیل کریں؟ تمام ڈیٹا ہٹا دیا جائے گا۔"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"اس ایپ کو فیکٹری ورژن سے تبدیل کریں؟ تمام ڈیٹا ہٹا دیا جائے گا۔ اس سے اس آلہ کے تمام صارف متاثر ہوں گے بشمول ان کے جن کے پاس دفتری پروفائلز ہیں۔"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"چل رہے اَن انسٹالس"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"ناکام اَن انسٹالس"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"اَن انسٹال ہو رہا ہے…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ان انسٹال ہو رہی ہے…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"اَن انسٹال پورا ہوگیا۔"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ان انسٹال ہو گیا"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"اَن انسٹال ناکام۔"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> کو ان انسٹال کرنا کامیاب نہیں ہوا۔"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"فعال آلہ کے منتظم کی ایپ اَن انسٹال نہیں کر سکتے"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"فعال آلہ کے منتظم کی ایپ <xliff:g id="USERNAME">%1$s</xliff:g> کیلئے اَن انسٹال نہیں کر سکتے"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"یہ ایپ کچھ صارفین اور پروفائلوں کیلئے درکار ہے اور دیگر کیلئے ان انسٹال ہو گئی"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"یہ ایپ آپ کے پروفائل کیلئے درکار ہے اور یہ ان انسٹال نہیں ہو سکتی۔"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"یہ ایپ آپ کے آلہ کے منتظم کو درکار ہے اور اسے اَن انسٹال نہیں کیا جا سکتا ہے۔"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"آلہ کے منتظم کی ایپس کا نظم کریں"</string>
+    <string name="manage_users" msgid="3125018886835668847">"صارفین کا نظم کریں"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> کو اَن انسٹال نہیں کیا جا سکا۔"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"پیکیج کو پارس کرنے میں ایک دشواری پیش آگئی۔"</string>
+    <string name="newPerms" msgid="6039428254474104210">"نئی"</string>
+    <string name="allPerms" msgid="1024385515840703981">"سبھی"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"رازداری"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"آلہ کی رسائی"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"اس اپ ڈیٹ کو کوئی نئی اجازتیں درکار نہیں۔"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"مسترد کریں"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"مزید معلومات"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"بہرصورت انکار کریں"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> از <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو <xliff:g id="ACTION">%2$s</xliff:g> کی اجازت دیں؟"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"‏ہمیشہ ‎&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‎ کو <xliff:g id="ACTION">%2$s</xliff:g> کی اجازت دیں؟"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"صرف ایپ استعمال کرنے کے دوران"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"ہمیشہ"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"انکار کریں اور دوبارہ مت پوچھیں"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> غیر فعال ہو گئیں"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"تمام غیر فعال ہو گئیں"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"کچھ بھی غیر فعال نہیں ہوا"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"اجازت دیں"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"ایپس"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"ایپ کی اجازتیں"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"دوبارہ مت پوچھیں"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"کوئی اجازتیں نہیں ہیں"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"اضافی اجازتیں"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"ایپ کی معلومات کھولیں"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> مزید</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> مزید</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"‏یہ ایپ Android کے ایک پرانے ورژن کیلئے ڈیزائن کی گئی تھی۔ اجازت دینے سے انکار کرنے پر ممکن ہے کہ وہ مزید ٹھیک سے کام نہ کرے۔"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"ایک نامعلوم کارروائی کو انجام دیں"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_1">%2$d</xliff:g> میں سے <xliff:g id="COUNT_0">%1$d</xliff:g> ایپس کو اجازت دے دی گئی"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"سسٹم دکھائیں"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"سسٹم چھپائیں"</string>
+    <string name="no_apps" msgid="1965493419005012569">"کوئی ایپس نہیں ہیں"</string>
+    <string name="location_settings" msgid="1774875730854491297">"مقام کی ترتیبات"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> اس آلہ کیلئے مقام کی سروسز کا فراہم کنندہ ہے۔ مقام کی رسائی میں مقام کی ترتیبات سے ترمیم کی جا سکتی ہے۔"</string>
+    <string name="system_warning" msgid="7103819124542305179">"اگرآپ اس اجازت کو مسترد کرتے ہیں تو شاید آپ کے آلہ کی بنیادی خصوصیات ٹھیک سے کام نہ کریں۔"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"پالیسی کی طرف سے نافذ کردہ"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"پالیسی نے پس منظر کی رسائی غیر فعال کر دی ہے"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"پالیسی نے پس منظر کی رسائی فعال کر دی ہے"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"پالیسی نے پیش منظر کی رسائی فعال کر دی ہے"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"کنٹرول کردہ بذریعہ منتظم"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"ہمیشہ"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"صرف ایپ استعمال کرنے کے دوران"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"کبھی نہیں"</string>
+    <string name="loading" msgid="7811651799620593731">"لوڈ ہورہا ہے…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"تمام اجازاتیں"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"دوسری ایپ اہلیتیں"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"اجازت کی درخواست"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"اسکرین اورلے کا پتہ چلا ہے"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"‏اس اجازت کی ترتیب کو تبدیل کرنے کیلئے آپ کو پہلے ترتیبات &gt; Apps سے سکرین اورلے آف کرنا ہوگا"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"ترتیبات کھولیں"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"‏\'کاروائیاں انسٹال/ان انسٹال کریں\' Wear پر تعاون یافتہ نہیں ہے۔"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"‏انتخاب کریں کہ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو کس تک رسائی کی اجازت دینی ہے"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اپ ڈیٹ ہو گئی ہے۔ انتخاب کریں کہ اس ایپ کو کس تک رسائی کی اجازت دینی ہے۔"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"منسوخ کریں"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"جاری رکھیں"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"نئی اجازتیں"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"موجودہ اجازتیں"</string>
+    <string name="message_staging" msgid="6151794817691100003">"ایپ کی مرحلہ بندی ہو رہی ہے…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"نامعلوم"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"آپ کی سیکیوریٹی کیلئے، آپ کے ٹیبلیٹ کو اس ذریعے سے نامعلوم ایپس انسٹال کرنے کی اجازت نہیں ہے۔"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"‏آپ کی سیکیوریٹی کیلئے، آپ کے TV کو اس ذریعے سے نامعلوم ایپس انسٹال کرنے کی اجازت نہیں ہے۔"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"آپ کی سیکیوریٹی کیلئے، آپ کے فون کو اس ذریعے سے نامعلوم ایپس انسٹال کرنے کی اجازت نہیں ہے۔"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"آپ کے فون اور ذاتی ڈیٹا کو نامعلوم ایپس کی جانب سے حملے کا زیادہ خطرہ ہے۔ اس ایپ کو انسٹال کر کے، آپ اس بات سے اتفاق کرتے ہیں کہ آپ اس سے اپنے فون کو ہونے والے کسی بھی نقصان یا ڈیٹا کے نقصان کیلئے خود ذمہ دار ہیں۔"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"آپ کے ٹیبلیٹ اور ذاتی ڈیٹا کو نامعلوم ایپس کی جانب سے حملے کا زیادہ خطرہ ہے۔ اس ایپ کو انسٹال کر کے، آپ اس بات سے اتفاق کرتے ہیں کہ آپ اس سے اپنے ٹیبلیٹ کو ہونے والے کسی بھی نقصان یا ڈیٹا کے نقصان کیلئے خود ذمہ دار ہیں۔"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"‏آپ کے TV اور ذاتی ڈیٹا کو نامعلوم ایپس کی جانب سے حملے کا زیادہ خطرہ ہے۔ اس ایپ کو انسٹال کر کے، آپ اس بات سے اتفاق کرتے ہیں کہ آپ اس سے اپنے TV کو ہونے والے کسی بھی نقصان یا ڈیٹا کے نقصان کیلئے خود ذمہ دار ہیں۔"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"جاری رکھیں"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"ترتیبات"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"‏wear ایپس کا انسٹال/ان انسٹال کرنا"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-uz-television/strings.xml b/packages/PackageInstaller/res/values-uz-television/strings.xml
new file mode 100644
index 0000000..bcc12ac
--- /dev/null
+++ b/packages/PackageInstaller/res/values-uz-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Rad etilsin va boshqa so‘ralmasin"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Siz buni keyinroq Sozlamalar &gt; Ilovalar bo‘limi orqali ham o‘zgartirishingiz mumkin"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Tizim ilovalarini ko‘rsatish"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Ilova uchun ruxsatlar"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Ilova uchun ruxsatlar"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> uchun ruxsat"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Qo‘shimcha ruxsatlar"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> uchun ruxsat"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-uz-watch/strings.xml b/packages/PackageInstaller/res/values-uz-watch/strings.xml
new file mode 100644
index 0000000..83f8da7
--- /dev/null
+++ b/packages/PackageInstaller/res/values-uz-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Rad etilsin va boshqa so‘ralmasin"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Tizim ilovalarini ko‘rsatish"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"O‘zgartirilmaydi"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Ha"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Bekor qilish"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-uz/strings.xml b/packages/PackageInstaller/res/values-uz/strings.xml
new file mode 100644
index 0000000..15a2e92
--- /dev/null
+++ b/packages/PackageInstaller/res/values-uz/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Paket o‘rnatish vositasi"</string>
+    <string name="next" msgid="3057143178373252333">"Keyingisi"</string>
+    <string name="install" msgid="5896438203900042068">"O‘rnatish"</string>
+    <string name="done" msgid="3889387558374211719">"Tayyor"</string>
+    <string name="cancel" msgid="8360346460165114585">"Bekor qilish"</string>
+    <string name="installing" msgid="8613631001631998372">"O‘rnatilmoqda…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> o‘rnatilmoqda…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Ilova o‘rnatildi."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Bu ilovani o‘rnatmoqchimisiz? U quyidagi ruxsatlarga ega:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Bu ilovani o‘rnatmoqchimisiz? U hech qanday maxsus ruxsat talab qilmaydi."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Bu ilova uchun yangilanishni o‘rnatmoqchimisiz? Yangilanganidan keyin u quyidagi ruxsatlarga ega bo‘ladi:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Bu ilova uchun yangilanishni o‘rnatmoqchimisiz? Yangilanganidan keyin u quyidagi ruxsatlarga ega bo‘ladi:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Ushbu mavjud ilovaga yangilanish o‘rnatilsinmi? Mavjud ma’lumotlaringiz o‘chib ketmaydi. U hech qanday maxsus ruxsat talab qilmaydi."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Ushbu tizim ilovasiga yangilanish o‘rnatilsinmi? Mavjud ma’lumotlaringiz o‘chib ketmaydi. U hech qanday maxsus ruxsat talab qilmaydi."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Ilova o‘rnatilmadi."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Paket o‘rnatilishga qarshi bloklangan."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Paket mavjud paket bilan zid kelganligi uchun ilovani o‘rnatib bo‘lmadi."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Ilova planshetingizga mos kelmaganligi uchun uni o‘rnatib bo‘lmadi."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Bu ilova televizoringiz bilan mos emas."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Ilova telefoningizga mos kelmaganligi uchun uni o‘rnatib bo‘lmadi."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Paket yaroqsiz bo‘lganligi uchun ilovani o‘rnatib bo‘lmadi."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"<xliff:g id="APP_NAME">%1$s</xliff:g> planshetingizga o‘rnatilmadi."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasini televizoringizga o‘rnatib bo‘lmadi."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"<xliff:g id="APP_NAME">%1$s</xliff:g> telefoningizga o‘rnatilmadi."</string>
+    <string name="launch" msgid="4826921505917605463">"Ochish"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Administratoringiz begona manbalardan olingan ilovalarni o‘rnatishga ruxsat bermagan"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Notanish ilovalarni bu foydalanuvchi tomonidan o‘rnatib bo‘lmaydi"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Bu foydalanuvchiga ilovalarni o‘rnatish uchun ruxsat berilmagan"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Ilovalarni boshqarish"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Joy qolmadi"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"<xliff:g id="APP_NAME">%1$s</xliff:g> o‘rnatilmadi. Xotiradan biroz joy bo‘shating va qaytadan urinib ko‘ring."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Ilova topilmadi"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Ilova o‘rnatilgan ilovalar ro‘yxatidan topilmadi."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Ruxsat berilmagan"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Joriy foydalanuvchiga bu o‘chirishni amalga oshirishi uchun ruxsat berilmagan."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Xato"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Ilovani o‘chirib bo‘lmadi"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Ilovani o‘chirish"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Yangilanishni o‘chirish"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> quyidagi ilovaning bir qismidir:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Bu ilova o‘chirib tashlansinmi?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Ushbu ilova "<b>"barcha"</b>" foydalanuvchilar uchun o‘chirilsinmi? Ilova va uning ma’lumotlari qurilmadagi "<b>"barcha"</b>" foydalanuvchilardan o‘chib ketadi."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Haqiqatdan ham <xliff:g id="USERNAME">%1$s</xliff:g> foydalanuvchi uchun ushbu ilovani olib tashlamoqchimisiz?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Bu ilova boshlang‘ich versiyasi bilan almashtirilsinmi? Barcha ma’lumotlar o‘chirib tashlanadi."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Bu ilova boshlang‘ich versiyasi bilan almashtirilsinmi? Barcha ma’lumotlar o‘chirib tashlanadi. Bu qurilmaning barcha foydalanuvchilariga, jumladan, ularning ishchi profillariga ham ta’sir qiladi."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Davom etayotganlar"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Amalga oshmaganlar"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"O‘chirilmoqda…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> o‘chirilmoqda…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"O‘chirib tashlandi."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> o‘chirib tashlandi"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"O‘chirish muvaffaqiyatsizlikka uchradi."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ilovasini o‘chirib bo‘lmadi."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Faol qurilma administratori ilovasini o‘chirib bo‘lmaydi"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"<xliff:g id="USERNAME">%1$s</xliff:g> profilida faol qurilma administratori ilovasini o‘chirib bo‘lmaydi"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Bu ilova ba’zi foydalanuvchi yoki profillar uchun zarur, boshqalar uchun esa o‘chirib tashlangan"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Bu ilova profilingiz uchun kerak va uni o‘chirib bo‘lmaydi."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Ushbu ilova qurilmangiz ma\'muri tomonidan ishlatiladi, shuning uchun uni olib tashlab bo\'lmaydi."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Qurilma administratori ilovalarini boshqarish"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Foydalanuvchilarni boshqarish"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"<xliff:g id="APP_NAME">%1$s</xliff:g> o‘chirilmadi."</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Paketni tahlil qilishda muammo yuz berdi."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Yangi"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Barchasi"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Maxfiylik"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Qurilmalardan foydalanish"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Ushbu yangilanish hech qanday yangi ruxsatlarni talab qilmaydi."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Rad etish"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Batafsil"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Baribir rad etilsin"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ilovasiga <xliff:g id="ACTION">%2$s</xliff:g> uchun ruxsat berilsinmi?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ilovasiga bu amalga bajarishga doim ruxsat berilsinmi: <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Faqat ilova ishlatilayotganda"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Har doim"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Rad etilsin va boshqa so‘ralmasin"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> tasi o‘chiq"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"hammasi o‘chiq"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"hech qaysi o‘chirilmagan"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Ruxsat berish"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Ilovalar"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Ilovalar uchun ruxsatlar"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Boshqa so‘ralmasin"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Hech narsa topilmadi"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Qo‘shimcha ruxsatlar"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Ilovaga oid ma’lumotni ochish"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">Yana <xliff:g id="COUNT_1">%1$d</xliff:g> ta</item>
+      <item quantity="one">Yana <xliff:g id="COUNT_0">%1$d</xliff:g> ta</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Bu ilova Androidning eskiroq versiyasiga mo‘ljallab ishlab chiqilgan. Agar ruxsat bermasangiz, u kutilganidek ishlamasligi mumkin."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"noma’lum amalni bajarish"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Ruxsat berilgan: <xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Tizimga oid jarayonlar"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Tizimga oid jarayonlarni berkitish"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Hech qanday ilova yo‘q"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Joylashuv sozlamalari"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> bu qurilma uchun joylashuvni aniqlash xizmatini taqdim etuvchi ilova hisoblanadi. Joylashuv ma’lumotlariga kirish vakolatini joylashuv sozlamalaridan o‘zgartirish mumkin."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Agar bu ruxsatni rad qilsangiz, qurilmangizning asosiy funksiyalari bundan buyon kutilganidek ishlamasligi mumkin."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Qoidaga muvofiq"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Fon rejimida kirish qoidaga muvofiq taqiqlangan"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Fon rejimida kirish qoidaga muvofiq yoqilgan"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Faol rejimda kirish qoidaga muvofiq yoqilgan"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Administrator tomonidan boshqariladi"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Har doim"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Faqat ilova ishlatilayotganda"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Hech qachon"</string>
+    <string name="loading" msgid="7811651799620593731">"Yuklanmoqda…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Barcha ruxsatnomalar"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Ilovaning boshqa imkoniyatlari"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Ruxsatnoma so‘rovi"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Boshqa oynalar ustidan ochiladigan ilova aniqlandi"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Bu ruxsatnoma parametrini o‘zgartirish uchun avval Sozlamalar &gt; Ilovalar bo‘limidan ekran ustidan ochilish funksiyasini o‘chirib qo‘ying"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Sozlamalarni ochish"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear qurilmasi o‘rnatish/o‘chirish amallarini qo‘llab-quvvatlamaydi."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun beriladigan ruxsatlarni tanlang"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; yangilandi. Unga beriladigan ruxsatlarni tanlang."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Bekor qilish"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Davom etish"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Yangi ruxsatnomalar"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Joriy ruxsatnomalar"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Kutib turing…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Noma’lum"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Xavfsizlik yuzasidan, planshetingizga bu manbadan notanish ilovalarni o‘rnatishga ruxsat berilmagan."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Xavfsizlik yuzasidan, televizoringizga bu manbadan notanish ilovalarni o‘rnatishga ruxsat berilmagan."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Xavfsizlik yuzasidan, telefoningizga bu manbadan notanish ilovalarni o‘rnatishga ruxsat berilmagan."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Telefoningiz va shaxsiy ma‘lumotlaringiz notanish ilovalar xujumiga zaif bo‘ladi. Bu ilovani o‘rnatish bilan telefoningizga yetkaziladigan shikast va ma‘lumotlaringizni o‘chirib yuborilishiga javobgarlikni o‘z zimmangizga olasiz."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Planshetingiz va shaxsiy ma‘lumotlaringiz notanish ilovalar xujumiga zaif bo‘ladi. Bu ilovani o‘rnatish bilan planshetingizga yetkaziladigan shikast va ma‘lumotlaringizni o‘chirib yuborilishiga javobgarlikni o‘z zimmangizga olasiz."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"TV va shaxsiy ma‘lumotlaringiz notanish ilovalar xujumiga zaif bo‘ladi. Bu ilovani o‘rnatish bilan televizoringizga yetkaziladigan shikast va ma‘lumotlaringizni o‘chirib yuborilishiga javobgarlikni o‘z zimmangizga olasiz."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Davom etish"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Sozlamalar"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Wear ilovalarini o‘rnatish/o‘chirish"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-vi-television/strings.xml b/packages/PackageInstaller/res/values-vi-television/strings.xml
new file mode 100644
index 0000000..574bde7
--- /dev/null
+++ b/packages/PackageInstaller/res/values-vi-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Từ chối và không hỏi lại"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Bạn có thể thay đổi cài đặt này sau trong Cài đặt &gt; Ứng dụng"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Hiển thị ứng dụng hệ thống"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Quyền của ứng dụng"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Quyền của ứng dụng"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"Quyền <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Quyền bổ sung"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"Quyền <xliff:g id="PERMISSION">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-vi-watch/strings.xml b/packages/PackageInstaller/res/values-vi-watch/strings.xml
new file mode 100644
index 0000000..d63ef28
--- /dev/null
+++ b/packages/PackageInstaller/res/values-vi-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Từ chối, không hỏi lại"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Hiển thị ứng dụng hệ thống"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Ko thể thay đổi"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Có"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Hủy"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-vi/strings.xml b/packages/PackageInstaller/res/values-vi/strings.xml
new file mode 100644
index 0000000..09998d8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-vi/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Trình cài đặt gói"</string>
+    <string name="next" msgid="3057143178373252333">"Tiếp theo"</string>
+    <string name="install" msgid="5896438203900042068">"Cài đặt"</string>
+    <string name="done" msgid="3889387558374211719">"Xong"</string>
+    <string name="cancel" msgid="8360346460165114585">"Hủy"</string>
+    <string name="installing" msgid="8613631001631998372">"Đang cài đặt…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"Đang cài đặt <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"Ứng dụng đã được cài đặt."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Bạn có muốn cài đặt ứng dụng này không? Ứng dụng sẽ có quyền truy cập vào:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Bạn có muốn cài đặt ứng dụng này không? Ứng dụng này không yêu cầu bất kỳ quyền truy cập đặc biệt nào."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Bạn có muốn cài đặt bản cập nhật cho ứng dụng hiện tại này không? Dữ liệu hiện tại của bạn sẽ không bị mất. Ứng dụng đã cập nhật sẽ có quyền truy cập vào:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Bạn có muốn cài đặt bản cập nhật cho ứng dụng được cài sẵn này không? Dữ liệu hiện tại của bạn sẽ không bị mất. Ứng dụng được cập nhật sẽ có quyền truy cập vào:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Bạn có muốn cài đặt bản cập nhật cho ứng dụng hiện có này không? Dữ liệu hiện có của bạn sẽ không bị mất. Việc cài đặt không yêu cầu bất kỳ quyền truy cập đặc biệt nào."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Bạn có muốn cài đặt bản cập nhật cho ứng dụng cài sẵn này không? Dữ liệu hiện có của bạn sẽ không bị mất. Việc cài đặt không yêu cầu quyền truy cập đặc biệt nào."</string>
+    <string name="install_failed" msgid="6579998651498970899">"Ứng dụng chưa được cài đặt."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Đã chặn cài đặt gói."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Ứng dụng chưa được cài đặt dưới dạng gói xung đột với gói hiện có."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Ứng dụng chưa được cài đặt dưới dạng ứng dụng không tương thích với máy tính bảng của bạn."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Ứng dụng này không tương thích với TV của bạn."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Ứng dụng chưa được cài đặt dưới dạng ứng dụng không tương thích với điện thoại của bạn."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Ứng dụng chưa được cài đặt dưới dạng gói dường như không hợp lệ."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"Không thể cài đặt <xliff:g id="APP_NAME">%1$s</xliff:g> trên máy tính bảng của bạn."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"Không cài đặt được <xliff:g id="APP_NAME">%1$s</xliff:g> trên TV của bạn."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"Không thể cài đặt <xliff:g id="APP_NAME">%1$s</xliff:g> trên điện thoại này."</string>
+    <string name="launch" msgid="4826921505917605463">"Mở"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Quản trị viên của bạn không cho phép cài đặt ứng dụng từ nguồn không xác định"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Người dùng này không thể cài đặt ứng dụng không xác định"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Người dùng này không được phép cài đặt ứng dụng"</string>
+    <string name="ok" msgid="3468756155452870475">"OK"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Quản lý ứng dụng"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Hết dung lượng"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"Không thể cài đặt <xliff:g id="APP_NAME">%1$s</xliff:g>. Hãy giải phóng dung lượng và thử lại."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"Không tìm thấy ứng dụng"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Không tìm thấy ứng dụng trong danh sách các ứng dụng đã cài đặt."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Không được phép"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Người dùng hiện tại không được phép thực hiện quá trình gỡ cài đặt này."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Lỗi"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Không thể gỡ cài đặt ứng dụng."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Gỡ cài đặt ứng dụng"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Gỡ cài đặt cập nhật"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> là một phần của ứng dụng sau:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Bạn có muốn gỡ cài đặt ứng dụng này không?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Bạn có muốn gỡ cài đặt ứng dụng này cho "<b>"tất cả"</b>" người dùng không? Ứng dụng và dữ liệu của ứng dụng sẽ bị xóa khỏi "<b>"tất cả"</b>" người dùng trên thiết bị."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"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="1394549691152728409">"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="2083665452990861991">"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="uninstalling_notification_channel" msgid="5698369661583525583">"Gỡ cài đặt đang chạy"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Gỡ cài đặt không thành công"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Đang gỡ cài đặt..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Đang gỡ cài đặt <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Gỡ cài đặt đã hoàn tất."</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Đã gỡ cài đặt <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Gỡ cài đặt không thành công."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Gỡ cài đặt <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> không thành công."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Không thể gỡ cài đặt ứng dụng dành cho quản trị viên thiết bị đang hoạt động"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Không thể gỡ cài đặt ứng dụng dành cho quản trị viên thiết bị đang hoạt động cho <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Ứng dụng này bắt buộc với một số người dùng hoặc hồ sơ và được gỡ cài đặt cho người khác"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Ứng dụng này là cần thiết cho hồ sơ của bạn và không thể gỡ cài đặt."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Ứng dụng này được quản trị viên thiết bị của bạn yêu cầu và không thể gỡ cài đặt."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Quản lý ứng dụng quản trị thiết bị"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Quản lý người dùng"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"Không thể gỡ cài đặt <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Đã xảy ra sự cố khi phân tích cú pháp gói."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Mới"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Tất cả"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Bảo mật"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Truy cập thiết bị"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Bản cập nhật này không yêu cầu quyền mới."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Từ chối"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Thông tin khác"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Vẫn từ chối"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Luôn cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Chỉ khi sử dụng ứng dụng"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Luôn luôn"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Từ chối và không hỏi lại"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"Đã vô hiệu hóa <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"tất cả quyền đều bị vô hiệu hóa"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"không có quyền nào bị vô hiệu hóa"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Cho phép"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Ứng dụng"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Quyền của ứng dụng"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Không hỏi lại"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Không có quyền"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Quyền khác"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Mở thông tin về ứng dụng"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> quyền khác</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> quyền khác</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Ứng dụng này được thiết kế cho các phiên bản Android cũ hơn. Từ chối quyền có thể làm cho ứng dụng không còn hoạt động như mong muốn."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"thực hiện hành động không xác định"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"Đã cho phép <xliff:g id="COUNT_0">%1$d</xliff:g> trong số <xliff:g id="COUNT_1">%2$d</xliff:g> ứng dụng"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Hiển thị hệ thống"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Ẩn hệ thống"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Không có ứng dụng"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Cài đặt vị trí"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> là nhà cung cấp dịch vụ vị trí cho thiết bị này. Bạn có thể sửa đổi quyền truy cập vị trí từ cài đặt vị trí."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Nếu bạn từ chối quyền này, các tính năng cơ bản trên thiết bị của bạn có thể không còn hoạt động như dự kiến."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Được thực thi bằng chính sách"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Quyền truy cập nền bị tắt theo chính sách"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Quyền truy cập nền được bật theo chính sách"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Quyền truy cập nền trước được bật theo chính sách"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Do quản trị viên kiểm soát"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Luôn luôn"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Chỉ khi sử dụng ứng dụng"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Không bao giờ"</string>
+    <string name="loading" msgid="7811651799620593731">"Đang tải…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Tất cả các quyền"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Các khả năng khác của ứng dụng"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Yêu cầu quyền"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Đã phát hiện lớp phủ màn hình"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Để thay đổi cài đặt quyền này, trước tiên bạn phải tắt lớp phủ màn hình từ Cài đặt &gt; Ứng dụng"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Mở cài đặt"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Không hỗ trợ tác vụ Cài đặt/Gỡ cài đặt trên Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Chọn cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập những gì"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"Đã cập nhật &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. Chọn cho phép ứng dụng này truy cập những gì."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Hủy"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Tiếp tục"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Các quyền mới"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Các quyền hiện tại"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Đang sắp xếp ứng dụng…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Không xác định"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Để bảo mật, máy tính bảng của bạn không được phép cài đặt các ứng dụng không xác định từ nguồn này."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Để bảo mật, TV của bạn không được phép cài đặt các ứng dụng không xác định từ nguồn này."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Để bảo mật, điện thoại của bạn không được phép cài đặt các ứng dụng không xác định từ nguồn này."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Điện thoại và dữ liệu cá nhân của bạn dễ bị các ứng dụng không xác định tấn công hơn. Bằng cách cài đặt ứng dụng này, bạn đồng ý tự chịu trách nhiệm cho mọi hỏng hóc đối với điện thoại của mình hoặc mất mát dữ liệu có thể phát sinh do sử dụng ứng dụng này."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Máy tính bảng và dữ liệu cá nhân của bạn dễ bị các ứng dụng không xác định tấn công hơn. Bằng cách cài đặt ứng dụng này, bạn đồng ý tự chịu trách nhiệm cho mọi hỏng hóc đối với máy tính bảng của mình hoặc mất mát dữ liệu có thể phát sinh do sử dụng ứng dụng này."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"TV và dữ liệu cá nhân của bạn dễ bị các ứng dụng không xác định tấn công hơn. Bằng cách cài đặt ứng dụng này, bạn đồng ý tự chịu trách nhiệm cho mọi hỏng hóc đối với TV của mình hoặc mất mát dữ liệu có thể phát sinh do sử dụng ứng dụng này."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Tiếp tục"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Cài đặt"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Cài đặt/gỡ cài đặt ứng dụng Wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-w192dp-watch/dimens_percent.xml b/packages/PackageInstaller/res/values-w192dp-watch/dimens_percent.xml
new file mode 100644
index 0000000..b5beca3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-w192dp-watch/dimens_percent.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<resources>
+    <dimen name="screen_percentage_05">9.6dp</dimen>
+    <dimen name="screen_percentage_10">19.2dp</dimen>
+    <dimen name="screen_percentage_12">23.04dp</dimen>
+    <dimen name="screen_percentage_15">28.8dp</dimen>
+</resources>
diff --git a/packages/PackageInstaller/res/values-w205dp-watch/dimens_percent.xml b/packages/PackageInstaller/res/values-w205dp-watch/dimens_percent.xml
new file mode 100644
index 0000000..302d23e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-w205dp-watch/dimens_percent.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<resources>
+    <dimen name="screen_percentage_05">10.25dp</dimen>
+    <dimen name="screen_percentage_10">20.5dp</dimen>
+    <dimen name="screen_percentage_12">24.6dp</dimen>
+    <dimen name="screen_percentage_15">30.75dp</dimen>
+</resources>
diff --git a/packages/PackageInstaller/res/values-w225dp-watch/dimens_percent.xml b/packages/PackageInstaller/res/values-w225dp-watch/dimens_percent.xml
new file mode 100644
index 0000000..937c5d0
--- /dev/null
+++ b/packages/PackageInstaller/res/values-w225dp-watch/dimens_percent.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<resources>
+    <dimen name="screen_percentage_05">11.25dp</dimen>
+    <dimen name="screen_percentage_10">22.5dp</dimen>
+    <dimen name="screen_percentage_12">27dp</dimen>
+    <dimen name="screen_percentage_15">33.75dp</dimen>
+</resources>
diff --git a/packages/PackageInstaller/res/values-w228dp-watch/dimens_percent.xml b/packages/PackageInstaller/res/values-w228dp-watch/dimens_percent.xml
new file mode 100644
index 0000000..b2ad334
--- /dev/null
+++ b/packages/PackageInstaller/res/values-w228dp-watch/dimens_percent.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<resources>
+    <dimen name="screen_percentage_05">11.4dp</dimen>
+    <dimen name="screen_percentage_10">22.8dp</dimen>
+    <dimen name="screen_percentage_12">27.36dp</dimen>
+    <dimen name="screen_percentage_15">34.2dp</dimen>
+</resources>
diff --git a/packages/PackageInstaller/res/values-w240dp-watch/dimens_percent.xml b/packages/PackageInstaller/res/values-w240dp-watch/dimens_percent.xml
new file mode 100644
index 0000000..ebc8c75
--- /dev/null
+++ b/packages/PackageInstaller/res/values-w240dp-watch/dimens_percent.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<resources>
+    <dimen name="screen_percentage_05">12dp</dimen>
+    <dimen name="screen_percentage_10">24dp</dimen>
+    <dimen name="screen_percentage_12">28.8dp</dimen>
+    <dimen name="screen_percentage_15">36dp</dimen>
+</resources>
diff --git a/packages/PackageInstaller/res/values-watch/colors.xml b/packages/PackageInstaller/res/values-watch/colors.xml
new file mode 100644
index 0000000..81d0459
--- /dev/null
+++ b/packages/PackageInstaller/res/values-watch/colors.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<resources>
+    <!-- Copied from wearable support -->
+    <color name="circular_button_disabled">#757575</color>
+</resources>
diff --git a/packages/PackageInstaller/res/values-watch/integers.xml b/packages/PackageInstaller/res/values-watch/integers.xml
new file mode 100644
index 0000000..365ac3a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-watch/integers.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- START: Ported values -->
+    <integer name="short_title_length">13</integer>
+    <integer name="char_limit_per_line">18</integer>
+    <!-- END: Ported values -->
+</resources>
diff --git a/packages/PackageInstaller/res/values-watch/strings.xml b/packages/PackageInstaller/res/values-watch/strings.xml
new file mode 100644
index 0000000..25e8389
--- /dev/null
+++ b/packages/PackageInstaller/res/values-watch/strings.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Generic text to indicate a yes. [CHAR LIMIT=10] -->
+    <string name="generic_yes">Yes</string>
+
+    <!-- Generic text to indicate Cancel. [CHAR LIMIT=10] -->
+    <string name="generic_cancel">Cancel</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-watch/styles.xml b/packages/PackageInstaller/res/values-watch/styles.xml
new file mode 100644
index 0000000..805d43d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-watch/styles.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<resources>
+    <style name="BreadcrumbText" parent="@android:style/TextAppearance.Material.Body2"/>
+    <style name="TitleText" parent="@android:style/TextAppearance.Material.Subhead"/>
+</resources>
diff --git a/packages/PackageInstaller/res/values-watch/themes.xml b/packages/PackageInstaller/res/values-watch/themes.xml
new file mode 100644
index 0000000..5e52008
--- /dev/null
+++ b/packages/PackageInstaller/res/values-watch/themes.xml
@@ -0,0 +1,20 @@
+<?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
+  -->
+
+<resources>
+    <style name="DialogWhenLarge" parent="@android:style/Theme.DeviceDefault.NoActionBar"/>
+</resources>
diff --git a/packages/PackageInstaller/res/values-zh-rCN-television/strings.xml b/packages/PackageInstaller/res/values-zh-rCN-television/strings.xml
new file mode 100644
index 0000000..9b43370
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rCN-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"拒绝,不要再询问"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"您以后可以在“设置”&gt;“应用”中更改此设置"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"显示系统应用"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"应用权限"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"应用权限"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g>权限"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"其他权限"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g>权限"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-zh-rCN-watch/strings.xml b/packages/PackageInstaller/res/values-zh-rCN-watch/strings.xml
new file mode 100644
index 0000000..2525619
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rCN-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"拒绝,不要再询问"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"显示系统应用"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"无法更改"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"是"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"取消"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-zh-rCN/strings.xml b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..d559774
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"软件包安装程序"</string>
+    <string name="next" msgid="3057143178373252333">"下一步"</string>
+    <string name="install" msgid="5896438203900042068">"安装"</string>
+    <string name="done" msgid="3889387558374211719">"完成"</string>
+    <string name="cancel" msgid="8360346460165114585">"取消"</string>
+    <string name="installing" msgid="8613631001631998372">"正在安装..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"正在安装<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"应用安装完成。"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"要安装此应用吗?它将获得以下权限:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"您要安装此应用吗?此应用不需要任何特殊权限。"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"您要安装此应用的新版本吗?您现有的数据不会丢失。更新后的应用将具备以下权限:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"您要安装此内置应用的新版本吗?您现有的数据不会丢失。更新后的应用将具备以下权限:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"是否要为这一现有应用安装更新?您现有的数据不会丢失,且安装过程无需任何特殊权限。"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"是否要为这一内置应用安装更新?您现有的数据不会丢失,且安装过程无需任何特殊权限。"</string>
+    <string name="install_failed" msgid="6579998651498970899">"应用未安装。"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"系统禁止安装该软件包。"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"应用未安装:软件包与现有软件包存在冲突。"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"应用未安装:应用与您的平板电脑不兼容。"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"此应用与您的电视不兼容。"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"应用未安装:应用与您的手机不兼容。"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"应用未安装:软件包似乎无效。"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"无法在您的平板电脑上安装“<xliff:g id="APP_NAME">%1$s</xliff:g>”。"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"无法将<xliff:g id="APP_NAME">%1$s</xliff:g>安装到您的电视上。"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"无法在您的手机上安装“<xliff:g id="APP_NAME">%1$s</xliff:g>”。"</string>
+    <string name="launch" msgid="4826921505917605463">"打开"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"您的管理员不允许安装来源不明的应用"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"该用户无法安装未知应用"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"此用户不能安装应用"</string>
+    <string name="ok" msgid="3468756155452870475">"确定"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"管理应用"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"没有存储空间"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"无法安装“<xliff:g id="APP_NAME">%1$s</xliff:g>”,请释放一些存储空间并重试。"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"未找到应用"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"未在已安装应用的列表中找到该应用。"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"不允许"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"当前用户无法执行这项卸载操作。"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"错误"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"无法卸载应用。"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"卸载应用"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"卸载更新"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>属于以下应用:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"要卸载此应用吗?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"是否要为"<b>"所有"</b>"用户卸载此应用?系统将为设备上的"<b>"所有"</b>"用户删除此应用及其数据。"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"您要为用户<xliff:g id="USERNAME">%1$s</xliff:g>卸载此应用吗?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"要将此应用替换为出厂版本吗?这样会移除所有数据。"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"要将此应用替换为出厂版本吗?这样会移除所有数据,并会影响此设备的所有用户(包括已设置工作资料的用户)。"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"进行中的卸载操作"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"失败的卸载操作"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"正在卸载..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"正在卸载<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"卸载完成。"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"已卸载<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"卸载失败。"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"卸载<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>失败。"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"无法卸载正在使用中的设备管理应用"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"无法为<xliff:g id="USERNAME">%1$s</xliff:g>卸载正在使用中的设备管理应用"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"这是部分用户或个人资料所需的应用;已为其他用户或个人资料卸载此应用"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"这是您的个人资料所需的应用,因此无法卸载。"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"这是您的设备管理员要求必须安装的应用,因此无法卸载。"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"管理设备管理应用"</string>
+    <string name="manage_users" msgid="3125018886835668847">"管理用户"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"无法卸载“<xliff:g id="APP_NAME">%1$s</xliff:g>”。"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"解析软件包时出现问题。"</string>
+    <string name="newPerms" msgid="6039428254474104210">"新权限"</string>
+    <string name="allPerms" msgid="1024385515840703981">"全部"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"隐私相关权限"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"设备相关权限"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"新版本不需要任何新的权限。"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"拒绝"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"详情"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"仍然拒绝"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"第 <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> 项权限(共 <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> 项)"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;<xliff:g id="ACTION">%2$s</xliff:g>吗?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"要一律允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;<xliff:g id="ACTION">%2$s</xliff:g>吗?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"仅限使用应用时"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"一律允许"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"拒绝,不要再询问"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> 项已停用"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"全部已停用"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"均未停用"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"允许"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"应用"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"应用权限"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"不再询问"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"没有权限"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"其他权限"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"打开应用信息"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">另外 <xliff:g id="COUNT_1">%1$d</xliff:g> 项</item>
+      <item quantity="one">另外 <xliff:g id="COUNT_0">%1$d</xliff:g> 项</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"此应用专为旧版 Android 打造。拒绝权限可能会导致其无法正常运行。"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"执行未知操作"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"已授权 <xliff:g id="COUNT_0">%1$d</xliff:g> 个应用(共 <xliff:g id="COUNT_1">%2$d</xliff:g> 个)"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"显示系统应用"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"隐藏系统应用"</string>
+    <string name="no_apps" msgid="1965493419005012569">"没有应用"</string>
+    <string name="location_settings" msgid="1774875730854491297">"位置信息设置"</string>
+    <string name="location_warning" msgid="8778701356292735971">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”是此设备的一个位置信息服务提供程序。您可以在位置信息设置中修改位置信息使用权。"</string>
+    <string name="system_warning" msgid="7103819124542305179">"如果您拒绝此权限,您设备的基本功能可能会无法正常使用。"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"依据政策强制执行"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"已根据政策停用后台访问权限"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"已根据政策启用后台访问权限"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"已根据政策启用前台访问权限"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"由管理员控制"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"一律允许"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"仅限使用应用时"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"永不"</string>
+    <string name="loading" msgid="7811651799620593731">"正在加载…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"所有权限"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"其他应用功能"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"权限请求"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"检测到屏幕叠加层"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"要更改此权限设置,您必须首先在“设置”&gt;“应用”中关闭屏幕叠加层"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"打开设置"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear 不支持安装/卸载操作。"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"请选择要向&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;授予哪些权限"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;已更新。请选择要向此应用授予哪些权限。"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"取消"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"继续"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"新权限"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"当前权限"</string>
+    <string name="message_staging" msgid="6151794817691100003">"正在准备安装应用…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"未知"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"出于安全考虑,已禁止您的平板电脑安装来自此来源的未知应用。"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"出于安全考虑,已禁止您的电视安装来自此来源的未知应用。"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"出于安全考虑,已禁止您的手机安装来自此来源的未知应用。"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"来历不明的应用很可能会损害您的手机和个人数据。安装该应用即表示,您同意对于因使用该应用可能导致的任何手机损坏或数据丢失情况,您负有全部责任。"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"来历不明的应用很可能会损害您的平板电脑和个人数据。安装该应用即表示,您同意对于因使用该应用可能导致的任何平板电脑损坏或数据丢失情况,您负有全部责任。"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"来历不明的应用很可能会损害您的电视和个人数据。安装该应用即表示,您同意对于因使用该应用可能导致的任何电视损坏或数据丢失情况,您负有全部责任。"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"继续"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"设置"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"正在安装/卸载 Android Wear 应用"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-zh-rHK-television/strings.xml b/packages/PackageInstaller/res/values-zh-rHK-television/strings.xml
new file mode 100644
index 0000000..52c3d30
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rHK-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"拒絕,不要再問我"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"您日後可以在 [設定] &gt; [應用程式] 中變更這項設定"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"顯示系統應用程式"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"應用程式權限"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"應用程式權限"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g>權限"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"其他權限"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g>權限"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-zh-rHK-watch/strings.xml b/packages/PackageInstaller/res/values-zh-rHK-watch/strings.xml
new file mode 100644
index 0000000..6d0226f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rHK-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"拒絕,不要再問我"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"顯示系統應用程式"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"不可變更"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"是"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"取消"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-zh-rHK/strings.xml b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..0401eb1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"程式安裝器"</string>
+    <string name="next" msgid="3057143178373252333">"下一步"</string>
+    <string name="install" msgid="5896438203900042068">"安裝"</string>
+    <string name="done" msgid="3889387558374211719">"完成"</string>
+    <string name="cancel" msgid="8360346460165114585">"取消"</string>
+    <string name="installing" msgid="8613631001631998372">"正在安裝..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"正在安裝 <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"已安裝應用程式。"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"您要安裝這個應用程式嗎?應用程式將取得以下存取權:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"您要安裝這個應用程式嗎?應用程式不需任何特殊存取權。"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"您要為這個現有的應用程式安裝更新嗎?您的現有資料將會喪失,更新後的應用程式將取得以下存取權:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"您要為這個內置的應用程式安裝更新嗎?您的現有資料將會喪失,更新後的應用程式將取得以下存取權:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"您要為這個現有的應用程式安裝更新嗎?您不會遺失現有的資料,而應用程式無需任何特殊的存取權限。"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"您要為這個內置應用程式安裝更新嗎?您不會遺失現有的資料,而應用程式無需任何特殊的存取權限。"</string>
+    <string name="install_failed" msgid="6579998651498970899">"未安裝應用程式。"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"套件已遭封鎖,無法安裝。"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"套件與現有的套件發生衝突,無法安裝應用程式。"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"應用程式與平板電腦不兼容,無法安裝應用程式。"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"此應用程式與您的電視不相容。"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"應用程式與手機不兼容,無法安裝應用程式。"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"套件好像無效,無法安裝應用程式。"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"無法在您的平板電腦上安裝「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"無法在您的電視上安裝 <xliff:g id="APP_NAME">%1$s</xliff:g>。"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"無法在您的手機上安裝「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
+    <string name="launch" msgid="4826921505917605463">"開啟"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"您的管理員不允許安裝來自不明來源的應用程式"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"此使用者無法安裝不明的應用程式"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"此使用者無法安裝應用程式"</string>
+    <string name="ok" msgid="3468756155452870475">"確定"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"管理應用程式"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"空間不足"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"無法解除安裝「<xliff:g id="APP_NAME">%1$s</xliff:g>」。請先騰出一些空間,然後再試一次。"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"找不到應用程式"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"在已安裝的應用程式清單中找不到這個應用程式。"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"不允許"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"目前的使用者不允許執行這項解除安裝操作。"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"錯誤"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"應用程式無法解除安裝。"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"解除安裝應用程式"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"解除安裝更新"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"「<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>」隸屬於以下應用程式:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"您要解除安裝這個應用程式嗎?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"您要為"<b>"所有"</b>"使用者解除安裝這個應用程式嗎?應用程式及其資料會從裝置上的"<b>"所有"</b>"使用者設定檔中移除。"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"您要為使用者 <xliff:g id="USERNAME">%1$s</xliff:g> 解除安裝這個應用程式嗎?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"要將此應用程式回復至原廠版本嗎?所有資料將會刪除。"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"要將此應用程式回復至原廠版本嗎?所有資料將會刪除,此裝置的所有使用者 (包括使用工作設定檔的使用者) 亦會受影響。"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"正在執行的解除安裝操作"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"失敗的解除安裝操作"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"正在解除安裝..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"正在解除安裝「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"完成解除安裝。"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"已解除安裝「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"解除安裝失敗。"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"解除安裝「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」失敗。"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"無法解除安裝可用的裝置管理員應用程式"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"無法為<xliff:g id="USERNAME">%1$s</xliff:g>解除安裝可用的裝置管理員應用程式"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"這是部分使用者或設定檔所需的應用程式,其他使用者或設定檔已解除安裝此應用程式"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"這是您設定檔所需的應用程式,因此無法解除安裝。"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"這是您的裝置管理員要求安裝的應用程式,因此無法解除安裝。"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"管理裝置管理員應用程式"</string>
+    <string name="manage_users" msgid="3125018886835668847">"管理使用者"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"無法解除安裝「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"剖析套件時發生問題。"</string>
+    <string name="newPerms" msgid="6039428254474104210">"新增"</string>
+    <string name="allPerms" msgid="1024385515840703981">"全部"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"私隱權"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"裝置存取權"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"這項更新不需新權限。"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"拒絕"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"更多資訊"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"一律拒絕"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"第 <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> 個 (共 <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> 個)"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<xliff:g id="ACTION">%2$s</xliff:g>嗎?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"要一律允許「<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt;&lt;/b&gt;」<xliff:g id="ACTION">%2$s</xliff:g>嗎?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"只在使用應用程式時"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"一律"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"拒絕,不要再詢問"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> 個權限已停用"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"所有權限已停用"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"沒有權限已停用"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"允許"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"應用程式"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"應用程式權限"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"不要再問我"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"沒有權限"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"其他權限"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"打開應用程式資料"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">還有 <xliff:g id="COUNT_1">%1$d</xliff:g> 個</item>
+      <item quantity="one">還有 <xliff:g id="COUNT_0">%1$d</xliff:g> 個</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"這個應用程式專為舊版本的 Android 設計。拒絕權限可能會導致它無法如預期 運作。"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"執行不明的操作"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"已允許 <xliff:g id="COUNT_0">%1$d</xliff:g> 個應用程式 (共 <xliff:g id="COUNT_1">%2$d</xliff:g> 個)"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"顯示系統"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"隱藏系統"</string>
+    <string name="no_apps" msgid="1965493419005012569">"沒有應用程式"</string>
+    <string name="location_settings" msgid="1774875730854491297">"位置設定"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g>為此裝置提供位置資訊服務。您可以在位置設定中更改位置存取權。"</string>
+    <string name="system_warning" msgid="7103819124542305179">"如果您拒絕這個權限,您的裝置的基本功能可能無法正常運作。"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"由政策強制執行"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"已根據政策停用背景存取權"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"已根據政策啟用背景存取權"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"已根據政策啟用前景存取權"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"由管理員控制"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"一律"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"只在使用應用程式時"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"永不"</string>
+    <string name="loading" msgid="7811651799620593731">"正在載入…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"所有權限"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"其他應用程式功能"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"權限要求"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"已偵測到螢幕重疊功能"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"如要變更此權限設定,請先前往 [設定] &gt; [應用程式],以關閉螢幕重疊功能"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"開啟設定"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear 不支援安裝/解除安裝操作。"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"選擇允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取的內容"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"已更新「<xliff:g id="APP_NAME">%1$s</xliff:g>」。選擇允許此應用程式存取的內容。"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"取消"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"繼續"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"新權限"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"目前權限"</string>
+    <string name="message_staging" msgid="6151794817691100003">"正在準備安裝應用程式…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"不明"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"為安全起見,您的平板電腦不得安裝此來源的不明應用程式。"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"為安全起見,您的電視不得安裝此來源的不明應用程式。"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"為安全起見,您的手機不得安裝此來源的不明應用程式。"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"來源不明的應用程式可能會侵害您的手機和個人資料。安裝此應用程式,即表示您同意承擔因使用這個應用程式而導致手機損壞或資料遺失的責任。"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"來源不明的應用程式可能會侵害您的平板電腦和個人資料。安裝此應用程式,即表示您同意承擔因使用這個應用程式而導致平板電腦損壞或資料遺失的責任。"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"來源不明的應用程式可能會侵害您的電視和個人資料。安裝此應用程式,即表示您同意承擔因使用這個應用程式而導致電視損壞或資料遺失的責任。"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"繼續"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"設定"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"正在安裝/解除安裝 Wear 應用程式"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-zh-rTW-television/strings.xml b/packages/PackageInstaller/res/values-zh-rTW-television/strings.xml
new file mode 100644
index 0000000..59266f9
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rTW-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"拒絕且不要再詢問"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"你日後可在 [設定] &gt; [應用程式] 中進行變更"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"顯示系統應用程式"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"應用程式權限"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"應用程式權限"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g>權限"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"其他權限"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g>權限"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-zh-rTW-watch/strings.xml b/packages/PackageInstaller/res/values-zh-rTW-watch/strings.xml
new file mode 100644
index 0000000..edaf7ea
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rTW-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"拒絕且不要再詢問"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"顯示系統應用程式"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"無法變更"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"是"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"取消"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-zh-rTW/strings.xml b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..db96924
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"程式安裝器"</string>
+    <string name="next" msgid="3057143178373252333">"下一步"</string>
+    <string name="install" msgid="5896438203900042068">"安裝"</string>
+    <string name="done" msgid="3889387558374211719">"完成"</string>
+    <string name="cancel" msgid="8360346460165114585">"取消"</string>
+    <string name="installing" msgid="8613631001631998372">"安裝中…"</string>
+    <string name="installing_app" msgid="4097935682329028894">"正在安裝「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」…"</string>
+    <string name="install_done" msgid="3682715442154357097">"已安裝應用程式。"</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"你要安裝這個應用程式嗎?應用程式將取得以下權限:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"你要安裝這個應用程式嗎?應用程式不需任何特殊權限。"</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"你要為這個現有的應用程式安裝更新嗎?你的現有資料不會遺失,而更新後的應用程式將取得以下權限:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"你要為這個內建的應用程式安裝更新嗎?你的現有資料不會遺失,而更新後的應用程式將取得以下權限:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"你要為這個現有的應用程式安裝更新嗎?你不會遺失現有的資料,且應用程式不需任何特殊權限。"</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"你要為這個現有的內建應用程式安裝更新嗎?你不會遺失現有的資料,且應用程式不需任何特殊權限。"</string>
+    <string name="install_failed" msgid="6579998651498970899">"未安裝應用程式。"</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"這個套件已遭到封鎖,因此無法安裝。"</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"應用程式套件與現有套件衝突,因此未能完成安裝。"</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"應用程式與你的平板電腦不相容,因此未能完成安裝。"</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"這個應用程式與你的電視不相容。"</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"應用程式與你的手機不相容,因此未能完成安裝。"</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"應用程式套件無效,因此未能完成安裝。"</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"無法在你的平板電腦上安裝「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"無法在你的電視上安裝「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"無法在你的手機上安裝「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
+    <string name="launch" msgid="4826921505917605463">"開啟"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"你的管理員不允許安裝來源不明的應用程式"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"這位使用者無法安裝不明的應用程式"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"這位使用者無法安裝應用程式"</string>
+    <string name="ok" msgid="3468756155452870475">"確定"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"管理應用程式"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"空間不足"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"無法安裝「<xliff:g id="APP_NAME">%1$s</xliff:g>」。請先釋出部分空間,然後再試一次。"</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"找不到應用程式"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"在已安裝的應用程式清單中找不到這個應用程式。"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"不允許此操作"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"目前的使用者無法執行這項解除安裝作業。"</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"發生錯誤"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"無法解除安裝應用程式。"</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"解除安裝應用程式"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"解除安裝更新"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"「<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>」屬於下列應用程式:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"你要解除安裝這個應用程式嗎?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"你要為"<b>"所有"</b>"使用者解除安裝這個應用程式嗎?該應用程式及其資料會從裝置上的"<b>"所有"</b>"使用者設定檔移除。"</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"你要為使用者 <xliff:g id="USERNAME">%1$s</xliff:g> 解除安裝這個應用程式嗎?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"要將應用程式換成原廠版本嗎?這麼做會移除所有資料。"</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"要將應用程式換成原廠版本嗎?這麼做會移除所有資料。凡是這個裝置的使用者 (包括設置工作資料夾的使用者),皆會受到影響。"</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"執行中的解除安裝作業"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"失敗的解除安裝作業"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"解除安裝中…"</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"正在解除安裝「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"解除安裝完成。"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"已解除安裝「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"解除安裝失敗。"</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"無法解除安裝「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」。"</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"無法解除安裝使用中的裝置管理員應用程式"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"無法為<xliff:g id="USERNAME">%1$s</xliff:g>解除安裝使用中的裝置管理員應用程式"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"部分使用者或設定檔需要使用這個應用程式;已為其他使用者解除安裝"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"你的設定檔需要使用這個應用程式,因此無法解除安裝。"</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"這是你的裝置管理員要求安裝的應用程式,因此無法解除安裝。"</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"管理裝置管理員應用程式"</string>
+    <string name="manage_users" msgid="3125018886835668847">"管理使用者"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"無法解除安裝「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"剖析套件時發生問題。"</string>
+    <string name="newPerms" msgid="6039428254474104210">"新增"</string>
+    <string name="allPerms" msgid="1024385515840703981">"全部"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"隱私權"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"裝置存取權"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"這項更新不需新權限。"</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"拒絕"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"瞭解詳情"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"直接拒絕"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<xliff:g id="ACTION">%2$s</xliff:g>嗎?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"要一律允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<xliff:g id="ACTION">%2$s</xliff:g>嗎?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"僅限使用應用程式時"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"一律允許"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"拒絕且不要再詢問"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"已停用 <xliff:g id="COUNT">%1$d</xliff:g> 項權限"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"已停用所有權限"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"未停用任何權限"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"允許"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"應用程式"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"應用程式權限"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"不要再詢問"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"沒有權限"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"其他權限"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"開啟應用程式資訊"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="other">還有 <xliff:g id="COUNT_1">%1$d</xliff:g> 項</item>
+      <item quantity="one">還有 <xliff:g id="COUNT_0">%1$d</xliff:g> 項</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"這個應用程式是為舊版 Android 所開發。拒絕授予權限可能導致應用程式無法正常運作。"</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"執行不明的動作"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"已授權 <xliff:g id="COUNT_0">%1$d</xliff:g> 個應用程式 (共 <xliff:g id="COUNT_1">%2$d</xliff:g> 個)"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"顯示系統"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"隱藏系統"</string>
+    <string name="no_apps" msgid="1965493419005012569">"沒有應用程式"</string>
+    <string name="location_settings" msgid="1774875730854491297">"位置資訊設定"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> 是這台裝置的定位服務供應商。你可以在位置資訊設定中修改位置資訊存取權。"</string>
+    <string name="system_warning" msgid="7103819124542305179">"如果你拒絕這項權限,裝置的基本功能可能無法正常運作。"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"依據政策規定執行"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"已根據政策停用背景存取權"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"已根據政策啟用背景存取權"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"已根據政策啟用前景存取權"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"由管理員控管"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"一律允許"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"僅限使用應用程式時"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"永不"</string>
+    <string name="loading" msgid="7811651799620593731">"載入中…"</string>
+    <string name="all_permissions" msgid="5156669007784613042">"所有權限"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"其他應用程式功能"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"權限要求"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"偵測到畫面重疊圖層"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"如要變更這項權限設定,你必須先依序前往 [設定] &gt; [應用程式],關閉裝置畫面重疊圖層"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"開啟設定"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Wear 不支援安裝及解除安裝操作。"</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"選擇要將哪些存取權限授予「<xliff:g id="APP_NAME">%1$s</xliff:g>」"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」已更新。請選擇要將哪些存取權限授予這個應用程式。"</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"取消"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"繼續"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"新權限"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"目前權限"</string>
+    <string name="message_staging" msgid="6151794817691100003">"正在啟動應用程式安裝程序…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"不明"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"為了安全起見,你的平板電腦禁止安裝這個來源提供的不明應用程式。"</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"為了安全起見,你的電視禁止安裝這個來源提供的不明應用程式。"</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"為了安全起見,你的手機禁止安裝這個來源提供的不明應用程式。"</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"來歷不明的應用程式可能會損害你的手機和個人資料。如因安裝及使用這個應用程式,導致你的手機受損或資料遺失,請自行負責。"</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"來歷不明的應用程式可能會損害你的平板電腦和個人資料。如因安裝及使用這個應用程式,導致你的平板電腦受損或資料遺失,請自行負責。"</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"來歷不明的應用程式可能會損害你的電視和個人資料。如因安裝及使用這個應用程式,導致你的電視受損或資料遺失,請自行負責。"</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"繼續"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"設定"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"安裝/解除安裝 Wear 應用程式"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-zu-television/strings.xml b/packages/PackageInstaller/res/values-zu-television/strings.xml
new file mode 100644
index 0000000..a07ad2e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zu-television/strings.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5694574989758145558">"Yenqaba futhi ungasabuzi"</string>
+    <string name="grant_dialog_how_to_change" msgid="615414835189256888">"Ungashintsha lokhu kamuva kuzilungiselelo &gt; izinhlelo zokusebenza"</string>
+    <string name="current_permission_template" msgid="4793247012451594523">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7330308025768596149">"Bonisa izinhlelo zokusebenza zesistimu"</string>
+    <string name="app_permissions_decor_title" msgid="1461057434211920209">"Izimvume zohlelo lokusebenza"</string>
+    <string name="manage_permissions_decor_title" msgid="4823785025722958092">"Izimvume zohlelo lokusebenza"</string>
+    <string name="permission_apps_decor_title" msgid="3644363529649579576">"<xliff:g id="PERMISSION">%1$s</xliff:g> izimvume"</string>
+    <string name="additional_permissions_decor_title" msgid="7000432624396037882">"Izimvume ezingeziwe"</string>
+    <string name="system_apps_decor_title" msgid="5292119639812561805">"<xliff:g id="PERMISSION">%1$s</xliff:g> izimvume"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-zu-watch/strings.xml b/packages/PackageInstaller/res/values-zu-watch/strings.xml
new file mode 100644
index 0000000..38fe1c8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zu-watch/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5828565432145544298">"Yenqaba, ungangibuzi futhi"</string>
+    <string name="current_permission_template" msgid="6691830243038105737">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="preference_show_system_apps" msgid="7042886929865431207">"Bonisa izinhlelo zokusebenza zesistimu"</string>
+    <string name="permission_summary_enforced_by_policy" msgid="9002523259681588936">"Akukwazi ukushintshwa"</string>
+    <string name="generic_yes" msgid="3394094077553763689">"Yebo"</string>
+    <string name="generic_cancel" msgid="6384078447202012984">"Khansela"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-zu/strings.xml b/packages/PackageInstaller/res/values-zu/strings.xml
new file mode 100644
index 0000000..6ece479
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zu/strings.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="2738748390251381682">"Isifaki sephakheji"</string>
+    <string name="next" msgid="3057143178373252333">"Okulandelayo"</string>
+    <string name="install" msgid="5896438203900042068">"Faka"</string>
+    <string name="done" msgid="3889387558374211719">"Kwenziwe"</string>
+    <string name="cancel" msgid="8360346460165114585">"Khansela"</string>
+    <string name="installing" msgid="8613631001631998372">"Iyafaka..."</string>
+    <string name="installing_app" msgid="4097935682329028894">"Ifaka i-<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="install_done" msgid="3682715442154357097">"I-App ifakiwe."</string>
+    <string name="install_confirm_question" msgid="7295206719219043890">"Ngabe ufuna ukufaka lolu hlelo lokusebenza? Lizothola ukufinyelela ku:"</string>
+    <string name="install_confirm_question_no_perms" msgid="5918305641302873520">"Ngabe ufuna ukufaka lolu hlelo lokusebenza? Alidingi ukufinyelela okukhethekile."</string>
+    <string name="install_confirm_question_update" msgid="4624159567361487964">"Ngabe ufuna ukufaka isibuyekezo ohlelweni lokusebenza olukhona? Idatha yakho ekhona izolahleka. Uhlelo lokusebenza olubuyekeziwe lizothola ukufinyelela ku:"</string>
+    <string name="install_confirm_question_update_system" msgid="1302330093676416336">"Ngabe ufuna ukufaka isibuyekezo kulolu hlelo lokusebenza olakhelwe phakathi? Idatha yakho ekhona izolahleka. Uhlelo lokusebenza olubuyekeziwe luzothola ukufinyelela ku:"</string>
+    <string name="install_confirm_question_update_no_perms" msgid="4885928136844618944">"Ingabe ufuna ukufaka isibuyekezo kulolu hlelo lokusebenza olukhona? Idatha yakho ekhona ngeke ilahleke. Akudingi ukufinyelela okukhethekile."</string>
+    <string name="install_confirm_question_update_system_no_perms" msgid="7676593512694724374">"Ungabe ufuna ukukhipha isibuyekezo kulolu hlelo lokusebenza olakhelwe ngaphakathi? Idatha yakho ekhona ngeke ilahleke. Akudingi ukufinyelela okukhethekile."</string>
+    <string name="install_failed" msgid="6579998651498970899">"I-app ayifakiwe."</string>
+    <string name="install_failed_blocked" msgid="1606870930588770025">"Iphakheji livinjiwe kusukela ekufakweni."</string>
+    <string name="install_failed_conflict" msgid="5336045235168070954">"Uhlelo lokusebenza alufakiwe njengoba ukuphakheja kushayisana nephakheji elikhona."</string>
+    <string name="install_failed_incompatible" product="tablet" msgid="6682387386242708974">"Uhlelo lokusebenza alufakiwe njengoba uhlelo lokusebenza lungahambisani nethebulethi yakho."</string>
+    <string name="install_failed_incompatible" product="tv" msgid="3553367270510072729">"Lolu hlelo lokusebenza aluhambisani ne-TV yakho."</string>
+    <string name="install_failed_incompatible" product="default" msgid="7917996365659426872">"Uhlelo lokusebenza alufakiwe njengoba uhlelo lokusebenza lungahambisani nefoni yakho."</string>
+    <string name="install_failed_invalid_apk" msgid="269885385245534742">"Uhlelo lokusebenza alufakiwe njengoba iphakheji ibonakala ingavumelekile."</string>
+    <string name="install_failed_msg" product="tablet" msgid="8368835262605608787">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayikwazanga ukufakwa kuthebhulethi."</string>
+    <string name="install_failed_msg" product="tv" msgid="3990457938384021566">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayikwazanga ukufakwa ku-TV yakho."</string>
+    <string name="install_failed_msg" product="default" msgid="8554909560982962052">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayikwazanga ukufakwa efonini."</string>
+    <string name="launch" msgid="4826921505917605463">"Vula"</string>
+    <string name="unknown_apps_admin_dlg_text" msgid="7488386758312008790">"Umlawuli wakho akavumeli ukufakwa kwezinhlelo zokusebenza ezitholwe kusukela kumithombo engaziwa"</string>
+    <string name="unknown_apps_user_restriction_dlg_text" msgid="5785226253054083336">"Izinhlelo zokusebenza ezingaziwa azikwazi ukufakwa ilo msebenzisi"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="5041150186260066212">"Lo msebenzisi akavunyelwe ukufaka izinhlelo zokusebenza"</string>
+    <string name="ok" msgid="3468756155452870475">"KULUNGILE"</string>
+    <string name="manage_applications" msgid="4033876279091996596">"Phatha izinhlelo zokusebenza"</string>
+    <string name="out_of_space_dlg_title" msgid="7843674437613797326">"Iphelelwe yisikhala"</string>
+    <string name="out_of_space_dlg_text" msgid="4774775404294282216">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayikwazanga ukufakwa. Khulula isikhala bese uzama futhi."</string>
+    <string name="app_not_found_dlg_title" msgid="2692335460569505484">"I-App ayitholakalanga"</string>
+    <string name="app_not_found_dlg_text" msgid="6107465056055095930">"Uhlelo lokusebenza alutholakalanga ohlwini lwezinhlelo zokusebenza ezifakiwe."</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="118128026847201582">"Akuvumelekile"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="739716827677987545">"Umsebenzisi wamanje akavunyelwe ukwenza lokhu kukhipha."</string>
+    <string name="generic_error_dlg_title" msgid="2684806600635296961">"Iphutha"</string>
+    <string name="generic_error_dlg_text" msgid="4288738047825333954">"Amafu ohlelo lokusebenza angakhishwa."</string>
+    <string name="uninstall_application_title" msgid="1860074100811653963">"Khipha i-app"</string>
+    <string name="uninstall_update_title" msgid="4146940097553335390">"Khipha isibuyekezo"</string>
+    <string name="uninstall_activity_text" msgid="6680688689803932550">"I-<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ingxenye yohlelo lokusebenza olulandelayo:"</string>
+    <string name="uninstall_application_text" msgid="6691975835951187030">"Ufuna ukukhipha le-app?"</string>
+    <string name="uninstall_application_text_all_users" msgid="5574704453233525222">"Ingabe ufuna ukukhipha lolu hlelo lokusebenza kubo "<b>"bonke"</b>" abasebenzisi? Uhlelo lokusebenza nedatha yalo kuzosuswa kubo "<b>"bonke"</b>" abasebenzisi kudivayisi."</string>
+    <string name="uninstall_application_text_user" msgid="8766882355635485733">"Ingabe ufuna ukukhiphela lolu hlelo lokusebenza kumsebenzisi ongu-<xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+    <string name="uninstall_update_text" msgid="1394549691152728409">"Shintshanisa lolu hlelo lokusebenza ngenguqulo yasekuqaleni? Yonke idatha izosuswa."</string>
+    <string name="uninstall_update_text_multiuser" msgid="2083665452990861991">"Shintshanisa lolu hlelo lokusebenza ngenguqulo yasekuqaleni? Yonke idatha izosuswa. Lokhu kuthinta bonke abasebenzisi bale divayisi, abafaka labo abanamaphrofayela wokusebenza."</string>
+    <string name="uninstalling_notification_channel" msgid="5698369661583525583">"Ukukhishwa okuqhubekayo"</string>
+    <string name="uninstall_failure_notification_channel" msgid="8224276726364132314">"Ukukhishwa okuhlulekile"</string>
+    <string name="uninstalling" msgid="5556217435895938250">"Iyakhipha..."</string>
+    <string name="uninstalling_app" msgid="2773617614877719294">"Ikhipha i-<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+    <string name="uninstall_done" msgid="3792487853420281888">"Ukukhipha kuqedile"</string>
+    <string name="uninstall_done_app" msgid="775837862728680479">"Kukhishwe i-<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
+    <string name="uninstall_failed" msgid="631122574306299512">"Ukukhipha akuphumelelanga."</string>
+    <string name="uninstall_failed_app" msgid="945277834056527022">"Ukukhipha i-<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> akuphumelele."</string>
+    <string name="uninstall_failed_device_policy_manager" msgid="2727361164694743362">"Ayikwazi ukukhipha uhlelo lokusebenza lomlawuli ledivayisi esebenzayo"</string>
+    <string name="uninstall_failed_device_policy_manager_of_user" msgid="2161462242935805756">"Ayikwazi ukukhipha uhlelo lokusebenza lomlawuli ledivayisi esebenzayo lika-<xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+    <string name="uninstall_all_blocked_profile_owner" msgid="3544933038594382346">"Lolu hlelo lokusebenza luyadingeka kwabanye abasebenzisi noma amaphrofayela futhi lukhishelwe abanye"</string>
+    <string name="uninstall_blocked_profile_owner" msgid="6912141045528994954">"Lolu hlelo lokusebenza ludingelwa iphrofayela yakho futhi alikwazi ukukhishwa."</string>
+    <string name="uninstall_blocked_device_owner" msgid="7074175526413453063">"Lolu hlelo lokusebenza ludingwa umlawuli wedivayisi yakho futhi alukwazi ukukhishwa."</string>
+    <string name="manage_device_administrators" msgid="118178632652346535">"Phatha izinhlelo zokusebenza zedivayisi yomlawuli"</string>
+    <string name="manage_users" msgid="3125018886835668847">"Phatha abasebenzisi"</string>
+    <string name="uninstall_failed_msg" msgid="8969754702803951175">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayikwazanga ukukhishwa"</string>
+    <string name="Parse_error_dlg_text" msgid="7623286983621067011">"Kube nenkinga yokwehlukanisa iphakheji."</string>
+    <string name="newPerms" msgid="6039428254474104210">"Okusha"</string>
+    <string name="allPerms" msgid="1024385515840703981">"Konke"</string>
+    <string name="privacyPerms" msgid="1850527049572617">"Ubumfihlo"</string>
+    <string name="devicePerms" msgid="6733560207731294504">"Ukufinyelela kwedivayisi"</string>
+    <string name="no_new_perms" msgid="6657813692169565975">"Lesi sibuyekezo asidingi zimvume."</string>
+    <string name="grant_dialog_button_deny" msgid="2176510645406614340">"Phika"</string>
+    <string name="grant_dialog_button_more_info" msgid="2218220771432058426">"Olunye ulwazi"</string>
+    <string name="grant_dialog_button_deny_anyway" msgid="847960499284125250">"Yenqaba noma kunjalo"</string>
+    <string name="current_permission_template" msgid="6378304249516652817">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> kokungu-<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="permission_warning_template" msgid="7332275268559121742">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="permission_add_background_warning_template" msgid="5391175001698541141">"Njalo vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukwenza <xliff:g id="ACTION">%2$s</xliff:g>?"</string>
+    <string name="allow_permission_foreground_only" msgid="1016389465002335286">"Kuphela ngenkathi usebenzisa uhlelo lokusebenza"</string>
+    <string name="allow_permission_always" msgid="7047379650123289823">"Njalo"</string>
+    <string name="deny_permission_deny_and_dont_ask_again" msgid="8003202275002645629">"Yenqaba futhi ungasabuzi"</string>
+    <string name="permission_revoked_count" msgid="7386129423432613024">"<xliff:g id="COUNT">%1$d</xliff:g> kukhutshaziwe"</string>
+    <string name="permission_revoked_all" msgid="8595742638132863678">"konke kukhutshaziwe"</string>
+    <string name="permission_revoked_none" msgid="2059511550181271342">"Lutho olukhutshaziwe"</string>
+    <string name="grant_dialog_button_allow" msgid="4616529495342337095">"Vumela"</string>
+    <string name="app_permissions_breadcrumb" msgid="3390836200791539264">"Izinhlelo zokusebenza"</string>
+    <string name="app_permissions" msgid="3146758905824597178">"Izimvume zohlelo lokusebenza"</string>
+    <string name="never_ask_again" msgid="1089938738199748687">"Ungaphindi ubuze"</string>
+    <string name="no_permissions" msgid="3210542466245591574">"Akukho zimvume"</string>
+    <string name="additional_permissions" msgid="6667573114240111763">"Izimvume ezingeziwe"</string>
+    <string name="app_permissions_info_button_label" msgid="7789812060395257748">"Vula ulwazi lohlelo lokusebenza"</string>
+    <plurals name="additional_permissions_more" formatted="false" msgid="945127158155064388">
+      <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> okuningi</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> okuningi</item>
+    </plurals>
+    <string name="old_sdk_deny_warning" msgid="3872277112584842615">"Lolu hlelo lokusebenza ludizayinelwe inguqulo endala ye-Android. Ukwala imvume kungalibangela ukuthi lingasasebenzi njengoba kuhlosiwe."</string>
+    <string name="default_permission_description" msgid="4992892207044156668">"Yenza isenzo esingaziwa"</string>
+    <string name="app_permissions_group_summary" msgid="4787239772223699263">"<xliff:g id="COUNT_0">%1$d</xliff:g> kuzinhlelo zokusebenza ezingu-<xliff:g id="COUNT_1">%2$d</xliff:g> ezivunyelwe"</string>
+    <string name="menu_show_system" msgid="6773743421743728921">"Bonisa isistimu"</string>
+    <string name="menu_hide_system" msgid="7595471742649432977">"Fihla isistimu"</string>
+    <string name="no_apps" msgid="1965493419005012569">"Azikho izinhlelo zokusebenza"</string>
+    <string name="location_settings" msgid="1774875730854491297">"Izilungiselelo Zendawo"</string>
+    <string name="location_warning" msgid="8778701356292735971">"<xliff:g id="APP_NAME">%1$s</xliff:g> ingumhlinzeki wamasevisi wendawo kule divayisi. Ukufinyelela kwendawo kungashintshwa kusuka kuzilungiselelo zendawo."</string>
+    <string name="system_warning" msgid="7103819124542305179">"Uma unqabela le mvume, izici eziyisisekelo zedivayisi yakho zingahle zingasasebenzi njengoba zihlosiwe."</string>
+    <string name="permission_summary_enforced_by_policy" msgid="3418617316188986205">"Isetshenziswe yinqubomgomo"</string>
+    <string name="permission_summary_disabled_by_policy_background_only" msgid="160007162349980265">"Ukufinyelela kwangemuva kukhutshazwe inqubomgomo"</string>
+    <string name="permission_summary_enabled_by_policy_background_only" msgid="4834971700297385342">"Ukufinyelela kwangemuva kunikwe amandla ngenqubomgomo"</string>
+    <string name="permission_summary_enabled_by_policy_foreground_only" msgid="5150061275247925588">"Ukufinyelela kwangaphambili kunikwe amandla ngenqubomgomo"</string>
+    <string name="permission_summary_enforced_by_admin" msgid="1702707982488952544">"Kulawulwa umqondisi"</string>
+    <!-- no translation found for background_access_chooser_dialog_choices:0 (7142769853837915143) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:1 (7804653189249679164) -->
+    <!-- no translation found for background_access_chooser_dialog_choices:2 (1131794379787779680) -->
+    <string name="permission_access_always" msgid="5642491469836594184">"Njalo"</string>
+    <string name="permission_access_only_foreground" msgid="6906814759741316041">"Kuphela ngenkathi usebenzisa uhlelo lokusebenza"</string>
+    <string name="permission_access_never" msgid="1258330706341318622">"Soze"</string>
+    <string name="loading" msgid="7811651799620593731">"Iyalayisha..."</string>
+    <string name="all_permissions" msgid="5156669007784613042">"Zonke izimvume"</string>
+    <string name="other_permissions" msgid="2016192512386091933">"Amanye amakhono wohlelo lokusebenza"</string>
+    <string name="permission_request_title" msgid="1204446718549121199">"Isicelo semvume"</string>
+    <string name="screen_overlay_title" msgid="3021729846864038529">"Kutholwe imbondela yesikrini"</string>
+    <string name="screen_overlay_message" msgid="2141944461571677331">"Ukuze uguqule lesi silungiselelo semvume, kuzomele uqale uvale imbondela yesikrini kusukela ku-Izilungiselelo &gt; Izinhlelo zokusebenza"</string>
+    <string name="screen_overlay_button" msgid="4344544843349937743">"Vula izilungiselelo"</string>
+    <string name="wear_not_allowed_dlg_title" msgid="8104666773577525713">"I-Android Wear"</string>
+    <string name="wear_not_allowed_dlg_text" msgid="1322352525843583064">"Izenzo zokufaka/ukukhipha azisekelwe ku-Wear."</string>
+    <string name="permission_review_title_template_install" msgid="6819338441305295479">"Khetha ukuthi uzovumela ini ukuthi i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifinyelele kuyo"</string>
+    <string name="permission_review_title_template_update" msgid="8632233603161669426">"I-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ibuyekeziwe. Khetha ukuthi uzovumela ini ukuthi ifinyelelwe ilolu hlelo lokusebenza."</string>
+    <string name="review_button_cancel" msgid="957906817733578877">"Khansela"</string>
+    <string name="review_button_continue" msgid="4809162078179371370">"Qhubeka"</string>
+    <string name="new_permissions_category" msgid="3213523410139204183">"Izimvume ezintsha"</string>
+    <string name="current_permissions_category" msgid="998210994450606094">"Izimvume zamanje"</string>
+    <string name="message_staging" msgid="6151794817691100003">"Ifaka kusiteji uhlelo lokusebenza…"</string>
+    <string name="app_name_unknown" msgid="8931522764510159105">"Akwaziwa"</string>
+    <string name="untrusted_external_source_warning" product="tablet" msgid="1483151219938173935">"Ukuze uvikelwe, ithebulethi yakho ayivunyelwe ukuthi ifake izinhlelo zokusebenza ezingaziwa kusukela kulo mthombo."</string>
+    <string name="untrusted_external_source_warning" product="tv" msgid="5373768281884328560">"Ukuze uvikelwe, i-TV yakho ayivunyelwe ukuthi ifake izinhlelo zokusebenza ezingaziwa kusukela kulo mthombo."</string>
+    <string name="untrusted_external_source_warning" product="default" msgid="2223486836232706553">"Ukuze uvikelwe, ifoni yakho ayivunyelwe ukuthi ifake izinhlelo zokusebenza kusukela kulo mthombo."</string>
+    <string name="anonymous_source_warning" product="default" msgid="7700263729981815614">"Idatha yakho yefoni neyohlelo lwakho lokusebenza isengcupheni kakhulu ekuhlaselweni izinhlelo zokusebenza ezingaziwa. Ngokufaka lolu hlelo lokusebenza, uyavuma ukuthi unesibopho sanoma ikuphi ukonakala kufoni yakho noma ukulahlekelwa kwedatha okungabangelwa ukusetshenziswa kwayo."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="8854462805499848630">"Ithebulethi yakho nedatha yomuntu siqu zisengcupheni kakhulu ekuhlaselweni izinhlelo zokusebenza ezingaziwa. Ngokufaka lolu hlelo lokusebenza, uyavuma ukuthi unesibopho sanoma ikuphi ukonakala kuthebulethi yakho noma ukulahleka kwedatha okungabangelwa ukusetshenziswa kwayo."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="1291472686734385872">"Idatha yakho ye-TV neyomuntu siqu isengcupheni kakhulu ekuhlaselweni izinhlelo zokusebenza ezingaziwa. Ngokufaka lolu hlelo lokusebenza, uyavuma ukuthi unesibopho sanoma ikuphi ukonakala ku-TV yakho noma ukulahlekelwa kwedatha okungabangelwa ukusetshenziswa kwayo."</string>
+    <string name="anonymous_source_continue" msgid="2094381167954332292">"Qhubeka"</string>
+    <string name="external_sources_settings" msgid="8601453744517291632">"Izilungiselelo"</string>
+    <string name="wear_app_channel" msgid="6200840123672949356">"Ifaka/ikhipha izinhlelo zokusebenza ze-wear"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values/attrs.xml b/packages/PackageInstaller/res/values/attrs.xml
new file mode 100644
index 0000000..e220f4c
--- /dev/null
+++ b/packages/PackageInstaller/res/values/attrs.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<resources>
+    <!-- START: Ported from WearableSupport -->
+    <declare-styleable name="CircledImageView">
+        <attr name="android:src" />
+        <attr name="circle_color" format="color" />
+        <attr name="circle_radius" format="dimension" />
+        <attr name="circle_radius_pressed" format="dimension" />
+        <attr name="circle_border_width" format="dimension" />
+        <attr name="circle_border_color" format="color" />
+        <attr name="circle_padding" format="dimension" />
+        <attr name="shadow_width" format="dimension" />
+        <attr name="image_circle_percentage" format="dimension" />
+        <attr name="image_horizontal_offcenter_percentage" format="dimension" />
+        <attr name="image_tint" format="color" />
+        <attr name="circle_radius_percent" format="fraction" />
+        <attr name="circle_radius_pressed_percent" format="fraction" />
+    </declare-styleable>
+    <!-- END: Ported from WearableSupport -->
+</resources>
diff --git a/packages/PackageInstaller/res/values/colors.xml b/packages/PackageInstaller/res/values/colors.xml
new file mode 100644
index 0000000..01a0c9a
--- /dev/null
+++ b/packages/PackageInstaller/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?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.
+  -->
+
+<resources>
+    <color name="bigIconColor">#C8CCCE</color>
+</resources>
diff --git a/packages/PackageInstaller/res/values/dimens.xml b/packages/PackageInstaller/res/values/dimens.xml
new file mode 100644
index 0000000..112723f
--- /dev/null
+++ b/packages/PackageInstaller/res/values/dimens.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+
+<resources>
+    <!-- Header sub settings margin start / end -->
+    <dimen name="header_subsettings_margin_start">72dp</dimen>
+    <dimen name="header_subsettings_margin_end">16dp</dimen>
+
+    <!-- Header margin start / end -->
+    <dimen name="header_margin_start">16dp</dimen>
+    <dimen name="header_margin_end">16dp</dimen>
+
+    <dimen name="lb_action_section_width">384dp</dimen>
+
+    <dimen name="lb_content_section_width">576dp</dimen>
+    <dimen name="lb_content_fragment_start_padding">48dp</dimen>
+    <dimen name="lb_content_fragment_delimiter_padding">16dp</dimen>
+    <dimen name="lb_content_fragment_max_icon_height">280dp</dimen>
+    <dimen name="lb_content_fragment_title_text_top_padding">2dp</dimen>
+    <dimen name="lb_content_fragment_title_text_size">36sp</dimen>
+    <dimen name="lb_content_fragment_icon_width">140dp</dimen>
+
+    <dimen name="lb_content_fragment_breadcrumb_text_size">18sp</dimen>
+    <dimen name="lb_content_fragment_description_text_size">14sp</dimen>
+    <dimen name="lb_content_fragment_title_text_bottom_padding">4dp</dimen>
+
+    <dimen name="headerElevation">8dp</dimen>
+
+    <dimen name="wear_permission_review_pref_padding">8dp</dimen>
+    <dimen name="wear_permission_review_icon_size">24dp</dimen>
+</resources>
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
new file mode 100644
index 0000000..6c7160f
--- /dev/null
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -0,0 +1,231 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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">
+    <!-- [CHAR LIMIT=25] -->
+    <string name="app_name">Package installer</string>
+
+    <!-- [CHAR LIMIT=15] -->
+    <string name="install">Install</string>
+    <!-- [CHAR LIMIT=15] -->
+    <string name="done">Done</string>
+    <!-- [CHAR LIMIT=15] -->
+    <string name="cancel">Cancel</string>
+    <!-- [CHAR LIMIT=50] -->
+    <string name="installing">Installing\u2026</string>
+    <!-- [CHAR LIMIT=50] -->
+    <string name="installing_app">Installing <xliff:g id="package_label">%1$s</xliff:g>\u2026</string>
+    <!-- [CHAR LIMIT=100] -->
+    <string name="install_done">App installed.</string>
+    <!-- Message for installing a new app that requires some permissions [CHAR LIMIT=NONE] -->
+    <!-- Message for installing a new app that does not require permissions [CHAR LIMIT=NONE] -->
+    <string name="install_confirm_question">Do you want to install this application?</string>
+    <!-- Message for updating an existing app [CHAR LIMIT=NONE] -->
+    <string name="install_confirm_question_update">Do you want to install an update
+            to this existing application?  Your existing data will not
+            be lost.  The updated application will get access to:</string>
+    <!-- Message for updating an existing system app [CHAR LIMIT=NONE] -->
+    <string name="install_confirm_question_update_system">Do you want to install an update
+            to this built-in application?  Your existing data will not
+            be lost.  The updated application will get access to:</string>
+    <!-- [CHAR LIMIT=100] -->
+    <string name="install_failed">App not installed.</string>
+    <!-- Reason displayed when installation fails because the package was blocked
+        from being installed (e.g., device policy, verification, ...) [CHAR LIMIT=100] -->
+    <string name="install_failed_blocked">The package was blocked from being installed.</string>
+    <!-- Reason displayed when installation fails because the package conflicts with
+        an existing application (e.g., incompatible certificates) [CHAR LIMIT=100] -->
+    <string name="install_failed_conflict">App not installed as package conflicts with an existing package.</string>
+    <!-- Reason displayed when installation fails because the package is incompatible with
+       the current tablet (e.g., missing native code for the current ABI, newer SDK, ...) [CHAR LIMIT=100] -->
+    <string name="install_failed_incompatible" product="tablet">App not installed as app isn\'t
+        compatible with your tablet.</string>
+    <!-- Reason displayed when installation fails because the package is incompatible with
+       the current TV (e.g., missing native code for the current ABI, newer SDK, ...) [CHAR LIMIT=100] -->
+    <string name="install_failed_incompatible" product="tv">This app isn\'t
+        compatible with your TV.</string>
+    <!-- Reason displayed when installation fails because the package is incompatible with
+       the current phone (e.g., missing native code for the current ABI, newer SDK, ...) [CHAR LIMIT=100] -->
+    <string name="install_failed_incompatible" product="default">App not installed as app isn\'t
+        compatible with your phone.</string>
+    <!-- Reason displayed when installation fails because the installation package itself is invalid
+        in some way (e.g., corrupt) [CHAR LIMIT=100] -->
+    <string name="install_failed_invalid_apk">App not installed as package appears to be invalid.</string>
+    <!-- Message presented when an application could not be installed on the tablet for some reason. [CHAR LIMIT=100] -->
+    <string name="install_failed_msg" product="tablet"><xliff:g id="app_name">%1$s</xliff:g> couldn\'t be installed on your tablet.</string>
+    <!-- Message presented when an application could not be installed on the TV for some reason. [CHAR LIMIT=100] -->
+    <string name="install_failed_msg" product="tv"><xliff:g id="app_name">%1$s</xliff:g> couldn\'t be installed on your TV.</string>
+    <!-- Message presented when an application could not be installed on the phone for some reason. [CHAR LIMIT=100] -->
+    <string name="install_failed_msg" product="default"><xliff:g id="app_name">%1$s</xliff:g> couldn\'t be installed on your phone.</string>
+    <string name="launch">Open</string>
+
+    <!-- Message presented in a dialog box when the device administrator restricts the installation of apps from unknown sources. [CHAR LIMIT=none] -->
+    <string name="unknown_apps_admin_dlg_text">Your admin doesn\'t allow installation of apps
+        obtained from unknown sources</string>
+    <!-- Message presented in a dialog box when the user restriction set by the system restricts the installation of apps from unknown sources. [CHAR LIMIT=none] -->
+    <string name="unknown_apps_user_restriction_dlg_text">Unknown apps can\'t be installed by this
+        user</string>
+    <!-- Message presented in a dialog box when the user restriction set by the system restricts the installation of apps. [CHAR LIMIT=none] -->
+    <string name="install_apps_user_restriction_dlg_text">This user is not allowed to install apps</string>
+
+    <!-- [CHAR LIMIT=15] -->
+    <string name="ok">OK</string>
+    <!-- [CHAR LIMIT=15] -->
+    <string name="manage_applications">Manage apps</string>
+    <!-- [CHAR LIMIT=30] -->
+    <string name="out_of_space_dlg_title">Out of space</string>
+    <!-- [CHAR LIMIT=none] -->
+    <string name="out_of_space_dlg_text"><xliff:g id="app_name">%1$s</xliff:g> couldn\'t be installed. Free up some space and try again.</string>
+    <!-- strings related to uninstall activity -->
+    <!--  [CHAR LIMIT=30] -->
+    <string name="app_not_found_dlg_title">App not found</string>
+    <!--  [CHAR LIMIT=none] -->
+    <string name="app_not_found_dlg_text"> The app wasn\'t found in the list of installed apps.</string>
+    <!--  [CHAR LIMIT=30] -->
+    <string name="user_is_not_allowed_dlg_title">Not allowed</string>
+    <!--  [CHAR LIMIT=none] -->
+    <string name="user_is_not_allowed_dlg_text">The current user is not allowed to perform this uninstallation.</string>
+    <!--  [CHAR LIMIT=30] -->
+    <string name="generic_error_dlg_title">Error</string>
+    <!--  [CHAR LIMIT=none] -->
+    <string name="generic_error_dlg_text">App could not be uninstalled.</string>
+    <!--  [CHAR LIMIT=30] -->
+    <string name="uninstall_application_title">Uninstall app</string>
+    <!--  [CHAR LIMIT=30] -->
+    <string name="uninstall_update_title">Uninstall update</string>
+    <!--  [CHAR LIMIT=none] -->
+    <string name="uninstall_activity_text"><xliff:g id="activity_name">%1$s</xliff:g> is part of the following app:</string>
+    <!--  [CHAR LIMIT=none] -->
+    <string name="uninstall_application_text">Do you want to uninstall this app?</string>
+    <!--  [CHAR LIMIT=none] -->
+    <string name="uninstall_application_text_all_users">Do you want to uninstall this app for <b>all</b>
+        users?  The application and its data will be removed from <b>all</b> users on the device.</string>
+    <!--  [CHAR LIMIT=none] -->
+    <string name="uninstall_application_text_user">Do you want to uninstall this app for the user <xliff:g id="username">%1$s</xliff:g>?</string>
+    <!--  [CHAR LIMIT=none] -->
+    <string name="uninstall_update_text">Replace this app with the factory version? All data will be removed.</string>
+    <!--  [CHAR LIMIT=none] -->
+    <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 for the notification channel containing notifications for current uninstall operations [CHAR LIMIT=40] -->
+    <string name="uninstalling_notification_channel">Running uninstalls</string>
+    <!-- Label for the notification channel containing notifications for failed uninstall operations [CHAR LIMIT=40] -->
+    <string name="uninstall_failure_notification_channel">Failed uninstalls</string>
+
+    <!-- [CHAR LIMIT=50] -->
+    <string name="uninstalling">Uninstalling\u2026</string>
+    <!-- [CHAR LIMIT=50] -->
+    <string name="uninstalling_app">Uninstalling <xliff:g id="package_label">%1$s</xliff:g>\u2026</string>
+    <!-- [CHAR LIMIT=100] -->
+    <string name="uninstall_done">Uninstall finished.</string>
+    <!-- [CHAR LIMIT=100] -->
+    <string name="uninstall_done_app">Uninstalled <xliff:g id="package_label">%1$s</xliff:g></string>
+    <!-- [CHAR LIMIT=100] -->
+    <string name="uninstall_failed">Uninstall unsuccessful.</string>
+    <!-- [CHAR LIMIT=100] -->
+    <string name="uninstall_failed_app">Uninstalling <xliff:g id="package_label">%1$s</xliff:g> unsuccessful.</string>
+    <!-- String presented to the user when uninstalling a package failed because the target package
+        is a current device administrator [CHAR LIMIT=80] -->
+    <string name="uninstall_failed_device_policy_manager">Can\'t uninstall active device admin
+        app</string>
+    <!-- String presented to the user when uninstalling a package failed because the target package
+        is a current device administrator for some user [CHAR LIMIT=100] -->
+    <string name="uninstall_failed_device_policy_manager_of_user">Can\'t uninstall active device
+        admin app for <xliff:g id="username">%1$s</xliff:g></string>
+    <!-- String presented to the admin user when uninstalling a package for all users failed
+    because a profile owner has marked the target package as not able to be uninstalled
+    [CHAR LIMIT=120] -->
+    <string name="uninstall_all_blocked_profile_owner">This app is required for some users or
+        profiles and was uninstalled for others</string>
+    <!-- String presented to the user when uninstalling a package failed because a profile owner
+         has marked the target package as not able to be uninstalled [CHAR LIMIT=120] -->
+    <string name="uninstall_blocked_profile_owner">This app is needed for
+        your profile and can\'t be uninstalled.</string>
+    <!-- String presented to the user when uninstalling a package failed because a device owner
+         has marked the the target package as not able to be uninstalled [CHAR LIMIT=80] -->
+    <string name="uninstall_blocked_device_owner">This app is required
+        by your device administrator and can\'t be uninstalled.</string>
+    <!-- String on a button that leads to the "device admin apps" configuration setting where a
+        user will be able to disable the device admin app in order to uninstall
+        it. [CHAR LIMIT=50] -->
+    <string name="manage_device_administrators">Manage device admin apps</string>
+    <!-- String on a button that leads to the "Users" page in Settings where a
+        user will be able to remove the secondary user(s) in order to uninstall
+        the app. [CHAR LIMIT=50] -->
+    <string name="manage_users">Manage users</string>
+    <!-- [CHAR LIMIT=none] -->
+    <string name="uninstall_failed_msg"><xliff:g id="app_name">%1$s</xliff:g> couldn\'t be uninstalled.</string>
+
+    <!-- Dialog attributes to indicate parse errors -->
+    <string name="Parse_error_dlg_text">There was a problem parsing the package.</string>
+
+    <!-- Title of dialog telling users that Install/Uninstall action is not supported on Android Wear. [CHAR LIMIT=30] -->
+    <string name="wear_not_allowed_dlg_title">Android Wear</string>
+    <!-- Title of dialog telling users that Install/Uninstall action is not supported on Android Wear. [CHAR LIMIT=none] -->
+    <string name="wear_not_allowed_dlg_text">Install/Uninstall actions not supported on Wear.</string>
+
+    <!-- Message that the app to be installed is being staged [CHAR LIMIT=50] -->
+    <string name="message_staging">Staging app&#8230;</string>
+
+    <!-- Placeholder for an app name when it is unknown [CHAR LIMIT=50] -->
+    <string name="app_name_unknown">Unknown</string>
+
+    <!-- Help URL, application permissions [DO NOT TRANSLATE] -->
+    <string name="help_app_permissions" translatable="false"></string>
+
+    <!-- Text to show in warning dialog on the tablet when the app source is not trusted [CHAR LIMIT=NONE] -->
+    <string name="untrusted_external_source_warning" product="tablet">For your security, your tablet is not allowed to install unknown apps from this source.</string>
+
+    <!-- Text to show in warning dialog on the tv when the app source is not trusted [CHAR LIMIT=NONE] -->
+    <string name="untrusted_external_source_warning" product="tv">For your security, your TV is not allowed to install unknown apps from this source.</string>
+
+    <!-- Text to show in warning dialog on the phone when the app source is not trusted [CHAR LIMIT=NONE] -->
+    <string name="untrusted_external_source_warning" product="default">For your security, your phone is not allowed to install unknown apps from this source.</string>
+
+    <!-- Text to show in warning dialog on the phone when the app source cannot be identified [CHAR LIMIT=NONE] -->
+    <string name="anonymous_source_warning" product="default">
+        Your phone and personal data are more vulnerable
+        to attack by unknown apps. By installing this app, you
+        agree that you are responsible for any damage to your
+        phone or loss of data that may result from its use.
+    </string>
+
+    <!-- Text to show in warning dialog on the tablet when the app source cannot be identified [CHAR LIMIT=NONE] -->
+    <string name="anonymous_source_warning" product="tablet">
+        Your tablet and personal data are more vulnerable
+        to attack by unknown apps. By installing this app, you
+        agree that you are responsible for any damage to your
+        tablet or loss of data that may result from its use.
+    </string>
+
+    <!-- Text to show in warning dialog on the tv when the app source cannot be identified [CHAR LIMIT=NONE] -->
+    <string name="anonymous_source_warning" product="tv">
+        Your TV and personal data are more vulnerable
+        to attack by unknown apps. By installing this app, you
+        agree that you are responsible for any damage to your
+        TV or loss of data that may result from its use.
+    </string>
+
+    <!-- Label for button to continue install of an app whose source cannot be identified [CHAR LIMIT=40] -->
+    <string name="anonymous_source_continue">Continue</string>
+
+    <!-- Label for button to open manage external sources settings [CHAR LIMIT=45] -->
+    <string name="external_sources_settings">Settings</string>
+
+    <!-- Label for the notification channel containing notifications for embedded app operations [CHAR LIMIT=40] -->
+    <string name="wear_app_channel">Installing/uninstalling wear apps</string>
+
+</resources>
diff --git a/packages/PackageInstaller/res/values/styles.xml b/packages/PackageInstaller/res/values/styles.xml
new file mode 100755
index 0000000..f79f98f
--- /dev/null
+++ b/packages/PackageInstaller/res/values/styles.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="MediumText"
+            parent="@android:style/TextAppearance.Medium">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="SmallText"
+            parent="@android:style/TextAppearance.Small">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="TitleText">
+        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:textSize">20dp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="ActionBar" parent="@android:style/Widget.Material.ActionBar.Solid">
+        <item name="android:contentInsetStart">72dp</item>
+    </style>
+
+</resources>
diff --git a/packages/PackageInstaller/res/values/themes.xml b/packages/PackageInstaller/res/values/themes.xml
new file mode 100644
index 0000000..6df6246
--- /dev/null
+++ b/packages/PackageInstaller/res/values/themes.xml
@@ -0,0 +1,41 @@
+<?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
+  -->
+
+<resources>
+
+    <style name="DialogWhenLarge"
+            parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+        <item name="android:textAppearanceMedium">@style/MediumText</item>
+        <item name="android:textAppearanceSmall">@style/SmallText</item>
+        <item name="android:titleTextStyle">@style/TitleText</item>
+    </style>
+
+    <style name="DialogWhenLargeNoAnimation" parent="DialogWhenLarge">
+        <item name="android:windowAnimationStyle">@null</item>
+    </style>
+
+    <style name="AlertDialogActivity"
+            parent="@android:style/Theme.DeviceDefault.Light.Panel">
+        <item name="android:backgroundDimEnabled">true</item>
+    </style>
+
+    <style name="Header.Settings"
+            parent="@android:style/Theme.DeviceDefault.Settings">
+    </style>
+
+
+</resources>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
new file mode 100644
index 0000000..399cf1f
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package com.android.packageinstaller;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import java.io.File;
+
+/**
+ * Trampoline activity. Calls PackageInstallerActivity and deletes staged install file onResult.
+ */
+public class DeleteStagedFileOnResult extends Activity {
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState == null) {
+            Intent installIntent = new Intent(getIntent());
+            installIntent.setClass(this, PackageInstallerActivity.class);
+
+            installIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+            startActivityForResult(installIntent, 0);
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        File sourceFile = new File(getIntent().getData().getPath());
+        sourceFile.delete();
+
+        setResult(resultCode, data);
+        finish();
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/DeviceUtils.java b/packages/PackageInstaller/src/com/android/packageinstaller/DeviceUtils.java
new file mode 100644
index 0000000..b54cad6
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/DeviceUtils.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+
+public class DeviceUtils {
+    public static boolean isTelevision(Context context) {
+        int uiMode = context.getResources().getConfiguration().uiMode;
+        return (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION;
+    }
+
+    public static boolean isWear(final Context context) {
+        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+    }
+
+    public static boolean isAuto(Context context) {
+        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java b/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java
new file mode 100644
index 0000000..3a94fdc
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.os.AsyncTask;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.Xml;
+
+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;
+
+/**
+ * Persists results of events and calls back observers when a matching result arrives.
+ */
+class EventResultPersister {
+    private static final String LOG_TAG = EventResultPersister.class.getSimpleName();
+
+    /** Id passed to {@link #addObserver(int, EventResultObserver)} to generate new id */
+    static final int GENERATE_NEW_ID = Integer.MIN_VALUE;
+
+    /**
+     * The extra with the id to set in the intent delivered to
+     * {@link #onEventReceived(Context, Intent)}
+     */
+    static final String EXTRA_ID = "EventResultPersister.EXTRA_ID";
+
+    /** Persisted state of this object */
+    private final AtomicFile mResultsFile;
+
+    private final Object mLock = new Object();
+
+    /** Currently stored but not yet called back results (install id -> status, status message) */
+    private final SparseArray<EventResult> mResults = new SparseArray<>();
+
+    /** Currently registered, not called back observers (install id -> observer) */
+    private final SparseArray<EventResultObserver> mObservers = new SparseArray<>();
+
+    /** Always increasing counter for install event ids */
+    private int mCounter;
+
+    /** If a write that will persist the state is scheduled */
+    private boolean mIsPersistScheduled;
+
+    /** If the state was changed while the data was being persisted */
+    private boolean mIsPersistingStateValid;
+
+    /**
+     * @return a new event id.
+     */
+    public int getNewId() throws OutOfIdsException {
+        synchronized (mLock) {
+            if (mCounter == Integer.MAX_VALUE) {
+                throw new OutOfIdsException();
+            }
+
+            mCounter++;
+            writeState();
+
+            return mCounter - 1;
+        }
+    }
+
+    /** Call back when a result is received. Observer is removed when onResult it called. */
+    interface EventResultObserver {
+        void onResult(int status, int legacyStatus, @Nullable String message);
+    }
+
+    /**
+     * Progress parser to the next element.
+     *
+     * @param parser The parser to progress
+     */
+    private static void nextElement(@NonNull XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        int type;
+        do {
+            type = parser.next();
+        } while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT);
+    }
+
+    /**
+     * Read an int attribute from the current element
+     *
+     * @param parser The parser to read from
+     * @param name The attribute name to read
+     *
+     * @return The value of the attribute
+     */
+    private static int readIntAttribute(@NonNull XmlPullParser parser, @NonNull String name) {
+        return Integer.parseInt(parser.getAttributeValue(null, name));
+    }
+
+    /**
+     * Read an String attribute from the current element
+     *
+     * @param parser The parser to read from
+     * @param name The attribute name to read
+     *
+     * @return The value of the attribute or null if the attribute is not set
+     */
+    private static String readStringAttribute(@NonNull XmlPullParser parser, @NonNull String name) {
+        return parser.getAttributeValue(null, name);
+    }
+
+    /**
+     * Read persisted state.
+     *
+     * @param resultFile The file the results are persisted in
+     */
+    EventResultPersister(@NonNull File resultFile) {
+        mResultsFile = new AtomicFile(resultFile);
+        mCounter = GENERATE_NEW_ID + 1;
+
+        try (FileInputStream stream = mResultsFile.openRead()) {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+            nextElement(parser);
+            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                String tagName = parser.getName();
+                if ("results".equals(tagName)) {
+                    mCounter = readIntAttribute(parser, "counter");
+                } else if ("result".equals(tagName)) {
+                    int id = readIntAttribute(parser, "id");
+                    int status = readIntAttribute(parser, "status");
+                    int legacyStatus = readIntAttribute(parser, "legacyStatus");
+                    String statusMessage = readStringAttribute(parser, "statusMessage");
+
+                    if (mResults.get(id) != null) {
+                        throw new Exception("id " + id + " has two results");
+                    }
+
+                    mResults.put(id, new EventResult(status, legacyStatus, statusMessage));
+                } else {
+                    throw new Exception("unexpected tag");
+                }
+
+                nextElement(parser);
+            }
+        } catch (Exception e) {
+            mResults.clear();
+            writeState();
+        }
+    }
+
+    /**
+     * Add a result. If the result is an pending user action, execute the pending user action
+     * directly and do not queue a result.
+     *
+     * @param context The context the event was received in
+     * @param intent The intent the activity received
+     */
+    void onEventReceived(@NonNull Context context, @NonNull Intent intent) {
+        int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
+
+        if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
+            context.startActivity(intent.getParcelableExtra(Intent.EXTRA_INTENT));
+
+            return;
+        }
+
+        int id = intent.getIntExtra(EXTRA_ID, 0);
+        String statusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+        int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, 0);
+
+        EventResultObserver observerToCall = null;
+        synchronized (mLock) {
+            int numObservers = mObservers.size();
+            for (int i = 0; i < numObservers; i++) {
+                if (mObservers.keyAt(i) == id) {
+                    observerToCall = mObservers.valueAt(i);
+                    mObservers.removeAt(i);
+
+                    break;
+                }
+            }
+
+            if (observerToCall != null) {
+                observerToCall.onResult(status, legacyStatus, statusMessage);
+            } else {
+                mResults.put(id, new EventResult(status, legacyStatus, statusMessage));
+                writeState();
+            }
+        }
+    }
+
+    /**
+     * Persist current state. The persistence might be delayed.
+     */
+    private void writeState() {
+        synchronized (mLock) {
+            mIsPersistingStateValid = false;
+
+            if (!mIsPersistScheduled) {
+                mIsPersistScheduled = true;
+
+                AsyncTask.execute(() -> {
+                    int counter;
+                    SparseArray<EventResult> results;
+
+                    while (true) {
+                        // Take snapshot of state
+                        synchronized (mLock) {
+                            counter = mCounter;
+                            results = mResults.clone();
+                            mIsPersistingStateValid = true;
+                        }
+
+                        FileOutputStream stream = null;
+                        try {
+                            stream = mResultsFile.startWrite();
+                            XmlSerializer serializer = Xml.newSerializer();
+                            serializer.setOutput(stream, StandardCharsets.UTF_8.name());
+                            serializer.startDocument(null, true);
+                            serializer.setFeature(
+                                    "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+                            serializer.startTag(null, "results");
+                            serializer.attribute(null, "counter", Integer.toString(counter));
+
+                            int numResults = results.size();
+                            for (int i = 0; i < numResults; i++) {
+                                serializer.startTag(null, "result");
+                                serializer.attribute(null, "id",
+                                        Integer.toString(results.keyAt(i)));
+                                serializer.attribute(null, "status",
+                                        Integer.toString(results.valueAt(i).status));
+                                serializer.attribute(null, "legacyStatus",
+                                        Integer.toString(results.valueAt(i).legacyStatus));
+                                if (results.valueAt(i).message != null) {
+                                    serializer.attribute(null, "statusMessage",
+                                            results.valueAt(i).message);
+                                }
+                                serializer.endTag(null, "result");
+                            }
+
+                            serializer.endTag(null, "results");
+                            serializer.endDocument();
+
+                            mResultsFile.finishWrite(stream);
+                        } catch (IOException e) {
+                            if (stream != null) {
+                                mResultsFile.failWrite(stream);
+                            }
+
+                            Log.e(LOG_TAG, "error writing results", e);
+                            mResultsFile.delete();
+                        }
+
+                        // Check if there was changed state since we persisted. If so, we need to
+                        // persist again.
+                        synchronized (mLock) {
+                            if (mIsPersistingStateValid) {
+                                mIsPersistScheduled = false;
+                                break;
+                            }
+                        }
+                    }
+                });
+            }
+        }
+    }
+
+    /**
+     * Add an observer. If there is already an event for this id, call back inside of this call.
+     *
+     * @param id       The id the observer is for or {@code GENERATE_NEW_ID} to generate a new one.
+     * @param observer The observer to call back.
+     *
+     * @return The id for this event
+     */
+    int addObserver(int id, @NonNull EventResultObserver observer)
+            throws OutOfIdsException {
+        synchronized (mLock) {
+            int resultIndex = -1;
+
+            if (id == GENERATE_NEW_ID) {
+                id = getNewId();
+            } else {
+                resultIndex = mResults.indexOfKey(id);
+            }
+
+            // Check if we can instantly call back
+            if (resultIndex >= 0) {
+                EventResult result = mResults.valueAt(resultIndex);
+
+                observer.onResult(result.status, result.legacyStatus, result.message);
+                mResults.removeAt(resultIndex);
+                writeState();
+            } else {
+                mObservers.put(id, observer);
+            }
+        }
+
+
+        return id;
+    }
+
+    /**
+     * Remove a observer.
+     *
+     * @param id The id the observer was added for
+     */
+    void removeObserver(int id) {
+        synchronized (mLock) {
+            mObservers.delete(id);
+        }
+    }
+
+    /**
+     * The status from an event.
+     */
+    private class EventResult {
+        public final int status;
+        public final int legacyStatus;
+        @Nullable public final String message;
+
+        private EventResult(int status, int legacyStatus, @Nullable String message) {
+            this.status = status;
+            this.legacyStatus = legacyStatus;
+            this.message = message;
+        }
+    }
+
+    class OutOfIdsException extends Exception {}
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallEventReceiver.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallEventReceiver.java
new file mode 100644
index 0000000..c70d7db
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallEventReceiver.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Receives install events and perists them using a {@link EventResultPersister}.
+ */
+public class InstallEventReceiver extends BroadcastReceiver {
+    private static final Object sLock = new Object();
+    private static EventResultPersister sReceiver;
+
+    /**
+     * Get the event receiver persisting the results
+     *
+     * @return The event receiver.
+     */
+    @NonNull private static EventResultPersister getReceiver(@NonNull Context context) {
+        synchronized (sLock) {
+            if (sReceiver == null) {
+                sReceiver = new EventResultPersister(
+                        TemporaryFileManager.getInstallStateFile(context));
+            }
+        }
+
+        return sReceiver;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        getReceiver(context).onEventReceived(context, intent);
+    }
+
+    /**
+     * Add an observer. If there is already an event for this id, call back inside of this call.
+     *
+     * @param context  A context of the current app
+     * @param id       The id the observer is for or {@code GENERATE_NEW_ID} to generate a new one.
+     * @param observer The observer to call back.
+     *
+     * @return The id for this event
+     */
+    static int addObserver(@NonNull Context context, int id,
+            @NonNull EventResultPersister.EventResultObserver observer)
+            throws EventResultPersister.OutOfIdsException {
+        return getReceiver(context).addObserver(id, observer);
+    }
+
+    /**
+     * Remove a observer.
+     *
+     * @param context  A context of the current app
+     * @param id The id the observer was added for
+     */
+    static void removeObserver(@NonNull Context context, int id) {
+        getReceiver(context).removeObserver(id);
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
new file mode 100644
index 0000000..5ba2d32
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+
+import java.io.File;
+
+/**
+ * Installation failed: Return status code to the caller or display failure UI to user
+ */
+public class InstallFailed extends Activity {
+    private static final String LOG_TAG = InstallFailed.class.getSimpleName();
+
+    /** Label of the app that failed to install */
+    private CharSequence mLabel;
+
+    /**
+     * Convert an package installer status code into the user friendly label.
+     *
+     * @param statusCode The status code from the package installer.
+     *
+     * @return The user friendly label for the status code
+     */
+    private int getExplanationFromErrorCode(int statusCode) {
+        Log.d(LOG_TAG, "Installation status code: " + statusCode);
+
+        switch (statusCode) {
+            case PackageInstaller.STATUS_FAILURE_BLOCKED:
+                return R.string.install_failed_blocked;
+            case PackageInstaller.STATUS_FAILURE_CONFLICT:
+                return R.string.install_failed_conflict;
+            case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
+                return R.string.install_failed_incompatible;
+            case PackageInstaller.STATUS_FAILURE_INVALID:
+                return R.string.install_failed_invalid_apk;
+            default:
+                return R.string.install_failed;
+        }
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        int statusCode = getIntent().getIntExtra(PackageInstaller.EXTRA_STATUS,
+                PackageInstaller.STATUS_FAILURE);
+
+        if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
+            int legacyStatus = getIntent().getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS,
+                    PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
+
+            // Return result if requested
+            Intent result = new Intent();
+            result.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus);
+            setResult(Activity.RESULT_FIRST_USER, result);
+            finish();
+        } else {
+            Intent intent = getIntent();
+            ApplicationInfo appInfo = intent
+                    .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+            Uri packageURI = intent.getData();
+
+            setContentView(R.layout.install_failed);
+
+            // Set header icon and title
+            PackageUtil.AppSnippet as;
+            PackageManager pm = getPackageManager();
+
+            if ("package".equals(packageURI.getScheme())) {
+                as = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
+                        pm.getApplicationIcon(appInfo));
+            } else {
+                final File sourceFile = new File(packageURI.getPath());
+                as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
+            }
+
+            // Store label for dialog
+            mLabel = as.label;
+
+            PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
+
+            // Show out of space dialog if needed
+            if (statusCode == PackageInstaller.STATUS_FAILURE_STORAGE) {
+                (new OutOfSpaceDialog()).show(getFragmentManager(), "outofspace");
+            }
+
+            // Get status messages
+            ((TextView) findViewById(R.id.simple_status)).setText(
+                    getExplanationFromErrorCode(statusCode));
+
+            // Set up "done" button
+            findViewById(R.id.done_button).setOnClickListener(view -> finish());
+        }
+    }
+
+    /**
+     * Dialog shown when we ran out of space during installation. This contains a link to the
+     * "manage applications" settings page.
+     */
+    public static class OutOfSpaceDialog extends DialogFragment {
+        private InstallFailed mActivity;
+
+        @Override
+        public void onAttach(Context context) {
+            super.onAttach(context);
+
+            mActivity = (InstallFailed) context;
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            return new AlertDialog.Builder(mActivity)
+                    .setTitle(R.string.out_of_space_dlg_title)
+                    .setMessage(getString(R.string.out_of_space_dlg_text, mActivity.mLabel))
+                    .setPositiveButton(R.string.manage_applications, (dialog, which) -> {
+                        // launch manage applications
+                        Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
+                        startActivity(intent);
+                        mActivity.finish();
+                    })
+                    .setNegativeButton(R.string.cancel, (dialog, which) -> mActivity.finish())
+                    .create();
+        }
+
+        @Override
+        public void onCancel(DialogInterface dialog) {
+            super.onCancel(dialog);
+
+            mActivity.finish();
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
new file mode 100755
index 0000000..c2dd740
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import static android.content.pm.PackageInstaller.SessionParams.UID_UNKNOWN;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Button;
+import android.widget.ProgressBar;
+
+import com.android.internal.content.PackageHelper;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Send package to the package manager and handle results from package manager. Once the
+ * installation succeeds, start {@link InstallSuccess} or {@link InstallFailed}.
+ * <p>This has two phases: First send the data to the package manager, then wait until the package
+ * manager processed the result.</p>
+ */
+public class InstallInstalling extends Activity {
+    private static final String LOG_TAG = InstallInstalling.class.getSimpleName();
+
+    private static final String SESSION_ID = "com.android.packageinstaller.SESSION_ID";
+    private static final String INSTALL_ID = "com.android.packageinstaller.INSTALL_ID";
+
+    private static final String BROADCAST_ACTION =
+            "com.android.packageinstaller.ACTION_INSTALL_COMMIT";
+
+    /** Listens to changed to the session and updates progress bar */
+    private PackageInstaller.SessionCallback mSessionCallback;
+
+    /** Task that sends the package to the package installer */
+    private InstallingAsyncTask mInstallingTask;
+
+    /** Id of the session to install the package */
+    private int mSessionId;
+
+    /** Id of the install event we wait for */
+    private int mInstallId;
+
+    /** URI of package to install */
+    private Uri mPackageURI;
+
+    /** The button that can cancel this dialog */
+    private Button mCancelButton;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.install_installing);
+
+        ApplicationInfo appInfo = getIntent()
+                .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+        mPackageURI = getIntent().getData();
+
+        if ("package".equals(mPackageURI.getScheme())) {
+            try {
+                getPackageManager().installExistingPackage(appInfo.packageName);
+                launchSuccess();
+            } catch (PackageManager.NameNotFoundException e) {
+                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
+            }
+        } else {
+            final File sourceFile = new File(mPackageURI.getPath());
+            PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo,
+                    sourceFile), R.id.app_snippet);
+
+            if (savedInstanceState != null) {
+                mSessionId = savedInstanceState.getInt(SESSION_ID);
+                mInstallId = savedInstanceState.getInt(INSTALL_ID);
+
+                // Reregister for result; might instantly call back if result was delivered while
+                // activity was destroyed
+                try {
+                    InstallEventReceiver.addObserver(this, mInstallId,
+                            this::launchFinishBasedOnResult);
+                } catch (EventResultPersister.OutOfIdsException e) {
+                    // Does not happen
+                }
+            } else {
+                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+                params.installFlags = PackageManager.INSTALL_FULL_APP;
+                params.referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
+                params.originatingUri = getIntent()
+                        .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
+                params.originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
+                        UID_UNKNOWN);
+                params.installerPackageName =
+                        getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
+
+                File file = new File(mPackageURI.getPath());
+                try {
+                    PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
+                    params.setAppPackageName(pkg.packageName);
+                    params.setInstallLocation(pkg.installLocation);
+                    params.setSize(
+                            PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
+                } catch (PackageParser.PackageParserException e) {
+                    Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
+                    Log.e(LOG_TAG,
+                            "Cannot calculate installed size " + file + ". Try only apk size.");
+                    params.setSize(file.length());
+                } catch (IOException e) {
+                    Log.e(LOG_TAG,
+                            "Cannot calculate installed size " + file + ". Try only apk size.");
+                    params.setSize(file.length());
+                }
+
+                try {
+                    mInstallId = InstallEventReceiver
+                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
+                                    this::launchFinishBasedOnResult);
+                } catch (EventResultPersister.OutOfIdsException e) {
+                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
+                }
+
+                try {
+                    mSessionId = getPackageManager().getPackageInstaller().createSession(params);
+                } catch (IOException e) {
+                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
+                }
+            }
+
+            mCancelButton = (Button) findViewById(R.id.cancel_button);
+
+            mCancelButton.setOnClickListener(view -> {
+                if (mInstallingTask != null) {
+                    mInstallingTask.cancel(true);
+                }
+
+                if (mSessionId > 0) {
+                    getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+                    mSessionId = 0;
+                }
+
+                setResult(RESULT_CANCELED);
+                finish();
+            });
+
+            mSessionCallback = new InstallSessionCallback();
+        }
+    }
+
+    /**
+     * Launch the "success" version of the final package installer dialog
+     */
+    private void launchSuccess() {
+        Intent successIntent = new Intent(getIntent());
+        successIntent.setClass(this, InstallSuccess.class);
+        successIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+
+        startActivity(successIntent);
+        finish();
+    }
+
+    /**
+     * Launch the "failure" version of the final package installer dialog
+     *
+     * @param legacyStatus  The status as used internally in the package manager.
+     * @param statusMessage The status description.
+     */
+    private void launchFailure(int legacyStatus, String statusMessage) {
+        Intent failureIntent = new Intent(getIntent());
+        failureIntent.setClass(this, InstallFailed.class);
+        failureIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+        failureIntent.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, legacyStatus);
+        failureIntent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, statusMessage);
+
+        startActivity(failureIntent);
+        finish();
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+
+        getPackageManager().getPackageInstaller().registerSessionCallback(mSessionCallback);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        // This is the first onResume in a single life of the activity
+        if (mInstallingTask == null) {
+            PackageInstaller installer = getPackageManager().getPackageInstaller();
+            PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
+
+            if (sessionInfo != null && !sessionInfo.isActive()) {
+                mInstallingTask = new InstallingAsyncTask();
+                mInstallingTask.execute();
+            } else {
+                // we will receive a broadcast when the install is finished
+                mCancelButton.setEnabled(false);
+                setFinishOnTouchOutside(false);
+            }
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putInt(SESSION_ID, mSessionId);
+        outState.putInt(INSTALL_ID, mInstallId);
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (mCancelButton.isEnabled()) {
+            super.onBackPressed();
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        getPackageManager().getPackageInstaller().unregisterSessionCallback(mSessionCallback);
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (mInstallingTask != null) {
+            mInstallingTask.cancel(true);
+            synchronized (mInstallingTask) {
+                while (!mInstallingTask.isDone) {
+                    try {
+                        mInstallingTask.wait();
+                    } catch (InterruptedException e) {
+                        Log.i(LOG_TAG, "Interrupted while waiting for installing task to cancel",
+                                e);
+                    }
+                }
+            }
+        }
+
+        InstallEventReceiver.removeObserver(this, mInstallId);
+
+        super.onDestroy();
+    }
+
+    /**
+     * Launch the appropriate finish activity (success or failed) for the installation result.
+     *
+     * @param statusCode    The installation result.
+     * @param legacyStatus  The installation as used internally in the package manager.
+     * @param statusMessage The detailed installation result.
+     */
+    private void launchFinishBasedOnResult(int statusCode, int legacyStatus, String statusMessage) {
+        if (statusCode == PackageInstaller.STATUS_SUCCESS) {
+            launchSuccess();
+        } else {
+            launchFailure(legacyStatus, statusMessage);
+        }
+    }
+
+
+    private class InstallSessionCallback extends PackageInstaller.SessionCallback {
+        @Override
+        public void onCreated(int sessionId) {
+            // empty
+        }
+
+        @Override
+        public void onBadgingChanged(int sessionId) {
+            // empty
+        }
+
+        @Override
+        public void onActiveChanged(int sessionId, boolean active) {
+            // empty
+        }
+
+        @Override
+        public void onProgressChanged(int sessionId, float progress) {
+            if (sessionId == mSessionId) {
+                ProgressBar progressBar = (ProgressBar)findViewById(R.id.progress_bar);
+                progressBar.setMax(Integer.MAX_VALUE);
+                progressBar.setProgress((int) (Integer.MAX_VALUE * progress));
+            }
+        }
+
+        @Override
+        public void onFinished(int sessionId, boolean success) {
+            // empty, finish is handled by InstallResultReceiver
+        }
+    }
+
+    /**
+     * Send the package to the package installer and then register a event result observer that
+     * will call {@link #launchFinishBasedOnResult(int, int, String)}
+     */
+    private final class InstallingAsyncTask extends AsyncTask<Void, Void,
+            PackageInstaller.Session> {
+        volatile boolean isDone;
+
+        @Override
+        protected PackageInstaller.Session doInBackground(Void... params) {
+            PackageInstaller.Session session;
+            try {
+                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
+            } catch (IOException e) {
+                return null;
+            }
+
+            session.setStagingProgress(0);
+
+            try {
+                File file = new File(mPackageURI.getPath());
+
+                try (InputStream in = new FileInputStream(file)) {
+                    long sizeBytes = file.length();
+                    try (OutputStream out = session
+                            .openWrite("PackageInstaller", 0, sizeBytes)) {
+                        byte[] buffer = new byte[1024 * 1024];
+                        while (true) {
+                            int numRead = in.read(buffer);
+
+                            if (numRead == -1) {
+                                session.fsync(out);
+                                break;
+                            }
+
+                            if (isCancelled()) {
+                                session.close();
+                                break;
+                            }
+
+                            out.write(buffer, 0, numRead);
+                            if (sizeBytes > 0) {
+                                float fraction = ((float) numRead / (float) sizeBytes);
+                                session.addProgress(fraction);
+                            }
+                        }
+                    }
+                }
+
+                return session;
+            } catch (IOException | SecurityException e) {
+                Log.e(LOG_TAG, "Could not write package", e);
+
+                session.close();
+
+                return null;
+            } finally {
+                synchronized (this) {
+                    isDone = true;
+                    notifyAll();
+                }
+            }
+        }
+
+        @Override
+        protected void onPostExecute(PackageInstaller.Session session) {
+            if (session != null) {
+                Intent broadcastIntent = new Intent(BROADCAST_ACTION);
+                broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                broadcastIntent.setPackage(getPackageName());
+                broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
+
+                PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                        InstallInstalling.this,
+                        mInstallId,
+                        broadcastIntent,
+                        PendingIntent.FLAG_UPDATE_CURRENT);
+
+                session.commit(pendingIntent.getIntentSender());
+                mCancelButton.setEnabled(false);
+                setFinishOnTouchOutside(false);
+            } else {
+                getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+
+                if (!isCancelled()) {
+                    launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
+                }
+            }
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
new file mode 100644
index 0000000..1bc9dbd
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * If a package gets installed from an content URI this step loads the package and turns it into
+ * and installation from a file. Then it re-starts the installation as usual.
+ */
+public class InstallStaging extends Activity {
+    private static final String LOG_TAG = InstallStaging.class.getSimpleName();
+
+    private static final String STAGED_FILE = "STAGED_FILE";
+
+    /** Currently running task that loads the file from the content URI into a file */
+    private @Nullable StagingAsyncTask mStagingTask;
+
+    /** The file the package is in */
+    private @Nullable File mStagedFile;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.install_staging);
+
+        if (savedInstanceState != null) {
+            mStagedFile = new File(savedInstanceState.getString(STAGED_FILE));
+
+            if (!mStagedFile.exists()) {
+                mStagedFile = null;
+            }
+        }
+
+        findViewById(R.id.cancel_button).setOnClickListener(view -> {
+            if (mStagingTask != null) {
+                mStagingTask.cancel(true);
+            }
+            setResult(RESULT_CANCELED);
+            finish();
+        });
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        // This is the first onResume in a single life of the activity
+        if (mStagingTask == null) {
+            // File does not exist, or became invalid
+            if (mStagedFile == null) {
+                // Create file delayed to be able to show error
+                try {
+                    mStagedFile = TemporaryFileManager.getStagedFile(this);
+                } catch (IOException e) {
+                    showError();
+                    return;
+                }
+            }
+
+            mStagingTask = new StagingAsyncTask();
+            mStagingTask.execute(getIntent().getData());
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putString(STAGED_FILE, mStagedFile.getPath());
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (mStagingTask != null) {
+            mStagingTask.cancel(true);
+        }
+
+        super.onDestroy();
+    }
+
+    /**
+     * Show an error message and set result as error.
+     */
+    private void showError() {
+        (new ErrorDialog()).showAllowingStateLoss(getFragmentManager(), "error");
+
+        Intent result = new Intent();
+        result.putExtra(Intent.EXTRA_INSTALL_RESULT,
+                PackageManager.INSTALL_FAILED_INVALID_APK);
+        setResult(RESULT_FIRST_USER, result);
+    }
+
+    /**
+     * Dialog for errors while staging.
+     */
+    public static class ErrorDialog extends DialogFragment {
+        private Activity mActivity;
+
+        @Override
+        public void onAttach(Context context) {
+            super.onAttach(context);
+
+            mActivity = (Activity) context;
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            AlertDialog alertDialog = new AlertDialog.Builder(mActivity)
+                    .setMessage(R.string.Parse_error_dlg_text)
+                    .setPositiveButton(R.string.ok,
+                            (dialog, which) -> mActivity.finish())
+                    .create();
+            alertDialog.setCanceledOnTouchOutside(false);
+
+            return alertDialog;
+        }
+
+        @Override
+        public void onCancel(DialogInterface dialog) {
+            super.onCancel(dialog);
+
+            mActivity.finish();
+        }
+    }
+
+    private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
+        @Override
+        protected Boolean doInBackground(Uri... params) {
+            if (params == null || params.length <= 0) {
+                return false;
+            }
+            Uri packageUri = params[0];
+            try (InputStream in = getContentResolver().openInputStream(packageUri)) {
+                // Despite the comments in ContentResolver#openInputStream the returned stream can
+                // be null.
+                if (in == null) {
+                    return false;
+                }
+
+                try (OutputStream out = new FileOutputStream(mStagedFile)) {
+                    byte[] buffer = new byte[1024 * 1024];
+                    int bytesRead;
+                    while ((bytesRead = in.read(buffer)) >= 0) {
+                        // Be nice and respond to a cancellation
+                        if (isCancelled()) {
+                            return false;
+                        }
+                        out.write(buffer, 0, bytesRead);
+                    }
+                }
+            } catch (IOException | SecurityException | IllegalStateException e) {
+                Log.w(LOG_TAG, "Error staging apk from content URI", e);
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        protected void onPostExecute(Boolean success) {
+            if (success) {
+                // Now start the installation again from a file
+                Intent installIntent = new Intent(getIntent());
+                installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
+                installIntent.setData(Uri.fromFile(mStagedFile));
+
+                if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
+                    installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+                }
+
+                installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+                startActivity(installIntent);
+
+                InstallStaging.this.finish();
+            } else {
+                showError();
+            }
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
new file mode 100644
index 0000000..b3f1105
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Select which activity is the first visible activity of the installation and forward the intent to
+ * it.
+ */
+public class InstallStart extends Activity {
+    private static final String LOG_TAG = InstallStart.class.getSimpleName();
+
+    private static final String DOWNLOADS_AUTHORITY = "downloads";
+    private IPackageManager mIPackageManager;
+    private boolean mAbortInstall = false;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mIPackageManager = AppGlobals.getPackageManager();
+        Intent intent = getIntent();
+        String callingPackage = getCallingPackage();
+
+        // If the activity was started via a PackageInstaller session, we retrieve the calling
+        // package from that session
+        int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
+        if (callingPackage == null && sessionId != -1) {
+            PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
+            PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);
+            callingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null;
+        }
+
+        final ApplicationInfo sourceInfo = getSourceInfo(callingPackage);
+        final int originatingUid = getOriginatingUid(sourceInfo);
+        boolean isTrustedSource = false;
+        if (sourceInfo != null
+                && (sourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
+            isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
+        }
+
+        if (!isTrustedSource && originatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
+            final int targetSdkVersion = getMaxTargetSdkVersionForUid(this, originatingUid);
+            if (targetSdkVersion < 0) {
+                Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid);
+                // Invalid originating uid supplied. Abort install.
+                mAbortInstall = true;
+            } else if (targetSdkVersion >= Build.VERSION_CODES.O && !declaresAppOpPermission(
+                    originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
+                Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "
+                        + Manifest.permission.REQUEST_INSTALL_PACKAGES);
+                mAbortInstall = true;
+            }
+        }
+        if (mAbortInstall) {
+            setResult(RESULT_CANCELED);
+            finish();
+            return;
+        }
+
+        Intent nextActivity = new Intent(intent);
+        nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+
+        // The the installation source as the nextActivity thinks this activity is the source, hence
+        // set the originating UID and sourceInfo explicitly
+        nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
+        nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
+        nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);
+
+        if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction())) {
+            nextActivity.setClass(this, PackageInstallerActivity.class);
+        } else {
+            Uri packageUri = intent.getData();
+
+            if (packageUri != null && (packageUri.getScheme().equals(ContentResolver.SCHEME_FILE)
+                    || packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT))) {
+                // Copy file to prevent it from being changed underneath this process
+                nextActivity.setClass(this, InstallStaging.class);
+            } else if (packageUri != null && packageUri.getScheme().equals(
+                    PackageInstallerActivity.SCHEME_PACKAGE)) {
+                nextActivity.setClass(this, PackageInstallerActivity.class);
+            } else {
+                Intent result = new Intent();
+                result.putExtra(Intent.EXTRA_INSTALL_RESULT,
+                        PackageManager.INSTALL_FAILED_INVALID_URI);
+                setResult(RESULT_FIRST_USER, result);
+
+                nextActivity = null;
+            }
+        }
+
+        if (nextActivity != null) {
+            startActivity(nextActivity);
+        }
+        finish();
+    }
+
+    private boolean declaresAppOpPermission(int uid, String permission) {
+        try {
+            final String[] packages = mIPackageManager.getAppOpPermissionPackages(permission);
+            if (packages == null) {
+                return false;
+            }
+            for (String packageName : packages) {
+                try {
+                    if (uid == getPackageManager().getPackageUid(packageName, 0)) {
+                        return true;
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                    // Ignore and try the next package
+                }
+            }
+        } catch (RemoteException rexc) {
+            // If remote package manager cannot be reached, install will likely fail anyway.
+        }
+        return false;
+    }
+
+    /**
+     * @return the ApplicationInfo for the installation source (the calling package), if available
+     */
+    private ApplicationInfo getSourceInfo(@Nullable String callingPackage) {
+        if (callingPackage != null) {
+            try {
+                return getPackageManager().getApplicationInfo(callingPackage, 0);
+            } catch (PackageManager.NameNotFoundException ex) {
+                // ignore
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Get the originating uid if possible, or
+     * {@link android.content.pm.PackageInstaller.SessionParams#UID_UNKNOWN} if not available
+     *
+     * @param sourceInfo The source of this installation
+     * @return The UID of the installation source or UID_UNKNOWN
+     */
+    private int getOriginatingUid(@Nullable ApplicationInfo sourceInfo) {
+        // The originating uid from the intent. We only trust/use this if it comes from either
+        // the document manager app or the downloads provider
+        final int uidFromIntent = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
+                PackageInstaller.SessionParams.UID_UNKNOWN);
+
+        final int callingUid;
+        if (sourceInfo != null) {
+            callingUid = sourceInfo.uid;
+        } else {
+            try {
+                callingUid = ActivityManager.getService()
+                        .getLaunchedFromUid(getActivityToken());
+            } catch (RemoteException ex) {
+                // Cannot reach ActivityManager. Aborting install.
+                Log.e(LOG_TAG, "Could not determine the launching uid.");
+                mAbortInstall = true;
+                return PackageInstaller.SessionParams.UID_UNKNOWN;
+            }
+        }
+        try {
+            if (mIPackageManager.checkUidPermission(Manifest.permission.MANAGE_DOCUMENTS,
+                    callingUid) == PackageManager.PERMISSION_GRANTED) {
+                return uidFromIntent;
+            }
+        } catch (RemoteException rexc) {
+            // Ignore. Should not happen.
+        }
+        if (isSystemDownloadsProvider(callingUid)) {
+            return uidFromIntent;
+        }
+        // We don't trust uid from the intent. Use the calling uid instead.
+        return callingUid;
+    }
+
+    private boolean isSystemDownloadsProvider(int uid) {
+        final ProviderInfo downloadProviderPackage = getPackageManager().resolveContentProvider(
+                DOWNLOADS_AUTHORITY, 0);
+        if (downloadProviderPackage == null) {
+            // There seems to be no currently enabled downloads provider on the system.
+            return false;
+        }
+        final ApplicationInfo appInfo = downloadProviderPackage.applicationInfo;
+        return (appInfo.isSystemApp() && uid == appInfo.uid);
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
new file mode 100644
index 0000000..94f6b31
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Button;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Finish installation: Return status code to the caller or display "success" UI to user
+ */
+public class InstallSuccess extends Activity {
+    private static final String LOG_TAG = InstallSuccess.class.getSimpleName();
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
+            // Return result if requested
+            Intent result = new Intent();
+            result.putExtra(Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_SUCCEEDED);
+            setResult(Activity.RESULT_OK, result);
+            finish();
+        } else {
+            Intent intent = getIntent();
+            ApplicationInfo appInfo =
+                    intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+            Uri packageURI = intent.getData();
+
+            setContentView(R.layout.install_success);
+
+            // Set header icon and title
+            PackageUtil.AppSnippet as;
+            PackageManager pm = getPackageManager();
+
+            if ("package".equals(packageURI.getScheme())) {
+                as = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
+                        pm.getApplicationIcon(appInfo));
+            } else {
+                File sourceFile = new File(packageURI.getPath());
+                as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
+            }
+
+            PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
+
+            // Set up "done" button
+            findViewById(R.id.done_button).setOnClickListener(view -> {
+                if (appInfo.packageName != null) {
+                    Log.i(LOG_TAG, "Finished installing " + appInfo.packageName);
+                }
+                finish();
+            });
+
+            // Enable or disable "launch" button
+            Intent launchIntent = getPackageManager().getLaunchIntentForPackage(
+                    appInfo.packageName);
+            boolean enabled = false;
+            if (launchIntent != null) {
+                List<ResolveInfo> list = getPackageManager().queryIntentActivities(launchIntent,
+                        0);
+                if (list != null && list.size() > 0) {
+                    enabled = true;
+                }
+            }
+
+            Button launchButton = (Button)findViewById(R.id.launch_button);
+            if (enabled) {
+                launchButton.setOnClickListener(view -> {
+                    try {
+                        startActivity(launchIntent);
+                    } catch (ActivityNotFoundException | SecurityException e) {
+                        Log.e(LOG_TAG, "Could not start activity", e);
+                    }
+                    finish();
+                });
+            } else {
+                launchButton.setEnabled(false);
+            }
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/OverlayTouchActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/OverlayTouchActivity.java
new file mode 100644
index 0000000..1fdbd97
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/OverlayTouchActivity.java
@@ -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 com.android.packageinstaller;
+
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+class OverlayTouchActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
new file mode 100644
index 0000000..97bafe7
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -0,0 +1,767 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES 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.packageinstaller;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.StringRes;
+import android.app.AlertDialog;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+
+import java.io.File;
+
+/**
+ * This activity is launched when a new application is installed via side loading
+ * The package is first parsed and the user is notified of parse errors via a dialog.
+ * If the package is successfully parsed, the user is notified to turn on the install unknown
+ * applications setting. A memory check is made at this point and the user is notified of out
+ * of memory conditions if any. If the package is already existing on the device,
+ * a confirmation dialog (to replace the existing package) is presented to the user.
+ * Based on the user response the package is then installed by launching InstallAppConfirm
+ * sub activity. All state transitions are handled in this activity
+ */
+public class PackageInstallerActivity extends OverlayTouchActivity implements OnClickListener {
+    private static final String TAG = "PackageInstaller";
+
+    private static final int REQUEST_TRUST_EXTERNAL_SOURCE = 1;
+
+    static final String SCHEME_PACKAGE = "package";
+
+    static final String EXTRA_CALLING_PACKAGE = "EXTRA_CALLING_PACKAGE";
+    static final String EXTRA_ORIGINAL_SOURCE_INFO = "EXTRA_ORIGINAL_SOURCE_INFO";
+    private static final String ALLOW_UNKNOWN_SOURCES_KEY =
+            PackageInstallerActivity.class.getName() + "ALLOW_UNKNOWN_SOURCES_KEY";
+
+    private int mSessionId = -1;
+    private Uri mPackageURI;
+    private Uri mOriginatingURI;
+    private Uri mReferrerURI;
+    private int mOriginatingUid = PackageInstaller.SessionParams.UID_UNKNOWN;
+    private String mOriginatingPackage; // The package name corresponding to #mOriginatingUid
+
+    private boolean localLOGV = false;
+    PackageManager mPm;
+    IPackageManager mIpm;
+    AppOpsManager mAppOpsManager;
+    UserManager mUserManager;
+    PackageInstaller mInstaller;
+    PackageInfo mPkgInfo;
+    String mCallingPackage;
+    ApplicationInfo mSourceInfo;
+
+    // ApplicationInfo object primarily used for already existing applications
+    private ApplicationInfo mAppInfo = null;
+
+    // Buttons to indicate user acceptance
+    private Button mOk;
+    private Button mCancel;
+
+    private PackageUtil.AppSnippet mAppSnippet;
+
+    static final String PREFS_ALLOWED_SOURCES = "allowed_sources";
+
+    // Dialog identifiers used in showDialog
+    private static final int DLG_BASE = 0;
+    private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;
+    private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;
+    private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
+    private static final int DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER = DLG_BASE + 5;
+    private static final int DLG_ANONYMOUS_SOURCE = DLG_BASE + 6;
+    private static final int DLG_NOT_SUPPORTED_ON_WEAR = DLG_BASE + 7;
+    private static final int DLG_EXTERNAL_SOURCE_BLOCKED = DLG_BASE + 8;
+    private static final int DLG_INSTALL_APPS_RESTRICTED_FOR_USER = DLG_BASE + 9;
+
+    // If unknown sources are temporary allowed
+    private boolean mAllowUnknownSources;
+
+    // Would the mOk button be enabled if this activity would be resumed
+    private boolean mEnableOk = false;
+
+    private void startInstallConfirm() {
+        int msg;
+
+        if (mAppInfo != null) {
+            msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+                    ? R.string.install_confirm_question_update_system
+                    : R.string.install_confirm_question_update;
+        } else {
+            // This is a new application with no permissions.
+            msg = R.string.install_confirm_question;
+        }
+
+        ((TextView) findViewById(R.id.install_confirm_question)).setText(msg);
+
+        mEnableOk = true;
+        mOk.setEnabled(true);
+    }
+
+    /**
+     * Replace any dialog shown by the dialog with the one for the given {@link #createDialog id}.
+     *
+     * @param id The dialog type to add
+     */
+    private void showDialogInner(int id) {
+        DialogFragment currentDialog =
+                (DialogFragment) getFragmentManager().findFragmentByTag("dialog");
+        if (currentDialog != null) {
+            currentDialog.dismissAllowingStateLoss();
+        }
+
+        DialogFragment newDialog = createDialog(id);
+        if (newDialog != null) {
+            newDialog.showAllowingStateLoss(getFragmentManager(), "dialog");
+        }
+    }
+
+    /**
+     * Create a new dialog.
+     *
+     * @param id The id of the dialog (determines dialog type)
+     *
+     * @return The dialog
+     */
+    private DialogFragment createDialog(int id) {
+        switch (id) {
+            case DLG_PACKAGE_ERROR:
+                return SimpleErrorDialog.newInstance(R.string.Parse_error_dlg_text);
+            case DLG_OUT_OF_SPACE:
+                return OutOfSpaceDialog.newInstance(
+                        mPm.getApplicationLabel(mPkgInfo.applicationInfo));
+            case DLG_INSTALL_ERROR:
+                return InstallErrorDialog.newInstance(
+                        mPm.getApplicationLabel(mPkgInfo.applicationInfo));
+            case DLG_NOT_SUPPORTED_ON_WEAR:
+                return NotSupportedOnWearDialog.newInstance();
+            case DLG_INSTALL_APPS_RESTRICTED_FOR_USER:
+                return SimpleErrorDialog.newInstance(
+                        R.string.install_apps_user_restriction_dlg_text);
+            case DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER:
+                return SimpleErrorDialog.newInstance(
+                        R.string.unknown_apps_user_restriction_dlg_text);
+            case DLG_EXTERNAL_SOURCE_BLOCKED:
+                return ExternalSourcesBlockedDialog.newInstance(mOriginatingPackage);
+            case DLG_ANONYMOUS_SOURCE:
+                return AnonymousSourceDialog.newInstance();
+        }
+        return null;
+    }
+
+    @Override
+    public void onActivityResult(int request, int result, Intent data) {
+        if (request == REQUEST_TRUST_EXTERNAL_SOURCE && result == RESULT_OK) {
+            // The user has just allowed this package to install other packages (via Settings).
+            mAllowUnknownSources = true;
+
+            // Log the fact that the app is requesting an install, and is now allowed to do it
+            // (before this point we could only log that it's requesting an install, but isn't
+            // allowed to do it yet).
+            int appOpCode =
+                    AppOpsManager.permissionToOpCode(Manifest.permission.REQUEST_INSTALL_PACKAGES);
+            mAppOpsManager.noteOpNoThrow(appOpCode, mOriginatingUid, mOriginatingPackage);
+
+            DialogFragment currentDialog =
+                    (DialogFragment) getFragmentManager().findFragmentByTag("dialog");
+            if (currentDialog != null) {
+                currentDialog.dismissAllowingStateLoss();
+            }
+
+            initiateInstall();
+        } else {
+            finish();
+        }
+    }
+
+    private String getPackageNameForUid(int sourceUid) {
+        String[] packagesForUid = mPm.getPackagesForUid(sourceUid);
+        if (packagesForUid == null) {
+            return null;
+        }
+        if (packagesForUid.length > 1) {
+            if (mCallingPackage != null) {
+                for (String packageName : packagesForUid) {
+                    if (packageName.equals(mCallingPackage)) {
+                        return packageName;
+                    }
+                }
+            }
+            Log.i(TAG, "Multiple packages found for source uid " + sourceUid);
+        }
+        return packagesForUid[0];
+    }
+
+    private boolean isInstallRequestFromUnknownSource(Intent intent) {
+        if (mCallingPackage != null && intent.getBooleanExtra(
+                Intent.EXTRA_NOT_UNKNOWN_SOURCE, false)) {
+            if (mSourceInfo != null) {
+                if ((mSourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
+                        != 0) {
+                    // Privileged apps can bypass unknown sources check if they want.
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private void initiateInstall() {
+        String pkgName = mPkgInfo.packageName;
+        // Check if there is already a package on the device with this name
+        // but it has been renamed to something else.
+        String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
+        if (oldName != null && oldName.length > 0 && oldName[0] != null) {
+            pkgName = oldName[0];
+            mPkgInfo.packageName = pkgName;
+            mPkgInfo.applicationInfo.packageName = pkgName;
+        }
+        // Check if package is already installed. display confirmation dialog if replacing pkg
+        try {
+            // This is a little convoluted because we want to get all uninstalled
+            // apps, but this may include apps with just data, and if it is just
+            // data we still want to count it as "installed".
+            mAppInfo = mPm.getApplicationInfo(pkgName,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES);
+            if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
+                mAppInfo = null;
+            }
+        } catch (NameNotFoundException e) {
+            mAppInfo = null;
+        }
+
+        startInstallConfirm();
+    }
+
+    void setPmResult(int pmResult) {
+        Intent result = new Intent();
+        result.putExtra(Intent.EXTRA_INSTALL_RESULT, pmResult);
+        setResult(pmResult == PackageManager.INSTALL_SUCCEEDED
+                ? RESULT_OK : RESULT_FIRST_USER, result);
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(null);
+
+        if (icicle != null) {
+            mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
+        }
+
+        mPm = getPackageManager();
+        mIpm = AppGlobals.getPackageManager();
+        mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
+        mInstaller = mPm.getPackageInstaller();
+        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
+
+        final Intent intent = getIntent();
+
+        mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
+        mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO);
+        mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
+                PackageInstaller.SessionParams.UID_UNKNOWN);
+        mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN)
+                ? getPackageNameForUid(mOriginatingUid) : null;
+
+
+        final Uri packageUri;
+
+        if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction())) {
+            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
+            final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
+            if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
+                Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
+                finish();
+                return;
+            }
+
+            mSessionId = sessionId;
+            packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
+            mOriginatingURI = null;
+            mReferrerURI = null;
+        } else {
+            mSessionId = -1;
+            packageUri = intent.getData();
+            mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
+            mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
+        }
+
+        // if there's nothing to do, quietly slip into the ether
+        if (packageUri == null) {
+            Log.w(TAG, "Unspecified source");
+            setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
+            finish();
+            return;
+        }
+
+        if (DeviceUtils.isWear(this)) {
+            showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR);
+            return;
+        }
+
+        boolean wasSetUp = processPackageUri(packageUri);
+        if (!wasSetUp) {
+            return;
+        }
+
+        // load dummy layout with OK button disabled until we override this layout in
+        // startInstallConfirm
+        bindUi(R.layout.install_confirm);
+        checkIfAllowedAndInitiateInstall();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        if (mOk != null) {
+            mOk.setEnabled(mEnableOk);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        if (mOk != null) {
+            // Don't allow the install button to be clicked as there might be overlays
+            mOk.setEnabled(false);
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putBoolean(ALLOW_UNKNOWN_SOURCES_KEY, mAllowUnknownSources);
+    }
+
+    private void bindUi(int layout) {
+        setContentView(layout);
+
+        mOk = (Button) findViewById(R.id.ok_button);
+        mCancel = (Button)findViewById(R.id.cancel_button);
+        mOk.setOnClickListener(this);
+        mCancel.setOnClickListener(this);
+
+        mOk.setEnabled(false);
+
+        PackageUtil.initSnippetForNewApp(this, mAppSnippet, R.id.app_snippet);
+    }
+
+    /**
+     * Check if it is allowed to install the package and initiate install if allowed. If not allowed
+     * show the appropriate dialog.
+     */
+    private void checkIfAllowedAndInitiateInstall() {
+        // Check for install apps user restriction first.
+        final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
+                UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
+        if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
+            showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
+            return;
+        } else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
+            startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
+            finish();
+            return;
+        }
+
+        if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
+            initiateInstall();
+        } else {
+            // Check for unknown sources restriction
+            final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(
+                    UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());
+            if ((unknownSourcesRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
+                showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
+            } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
+                startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
+                finish();
+            } else {
+                handleUnknownSources();
+            }
+        }
+    }
+
+    private void handleUnknownSources() {
+        if (mOriginatingPackage == null) {
+            Log.i(TAG, "No source found for package " + mPkgInfo.packageName);
+            showDialogInner(DLG_ANONYMOUS_SOURCE);
+            return;
+        }
+        // Shouldn't use static constant directly, see b/65534401.
+        final int appOpCode =
+                AppOpsManager.permissionToOpCode(Manifest.permission.REQUEST_INSTALL_PACKAGES);
+        final int appOpMode = mAppOpsManager.noteOpNoThrow(appOpCode,
+                mOriginatingUid, mOriginatingPackage);
+        switch (appOpMode) {
+            case AppOpsManager.MODE_DEFAULT:
+                try {
+                    int result = mIpm.checkUidPermission(
+                            Manifest.permission.REQUEST_INSTALL_PACKAGES, mOriginatingUid);
+                    if (result == PackageManager.PERMISSION_GRANTED) {
+                        initiateInstall();
+                        break;
+                    }
+                } catch (RemoteException exc) {
+                    Log.e(TAG, "Unable to talk to package manager");
+                }
+                mAppOpsManager.setMode(appOpCode, mOriginatingUid,
+                        mOriginatingPackage, AppOpsManager.MODE_ERRORED);
+                // fall through
+            case AppOpsManager.MODE_ERRORED:
+                showDialogInner(DLG_EXTERNAL_SOURCE_BLOCKED);
+                break;
+            case AppOpsManager.MODE_ALLOWED:
+                initiateInstall();
+                break;
+            default:
+                Log.e(TAG, "Invalid app op mode " + appOpMode
+                        + " for OP_REQUEST_INSTALL_PACKAGES found for uid " + mOriginatingUid);
+                finish();
+                break;
+        }
+    }
+
+    /**
+     * Parse the Uri and set up the installer for this package.
+     *
+     * @param packageUri The URI to parse
+     *
+     * @return {@code true} iff the installer could be set up
+     */
+    private boolean processPackageUri(final Uri packageUri) {
+        mPackageURI = packageUri;
+
+        final String scheme = packageUri.getScheme();
+
+        switch (scheme) {
+            case SCHEME_PACKAGE: {
+                try {
+                    mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(),
+                            PackageManager.GET_PERMISSIONS
+                                    | PackageManager.MATCH_UNINSTALLED_PACKAGES);
+                } catch (NameNotFoundException e) {
+                }
+                if (mPkgInfo == null) {
+                    Log.w(TAG, "Requested package " + packageUri.getScheme()
+                            + " not available. Discontinuing installation");
+                    showDialogInner(DLG_PACKAGE_ERROR);
+                    setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
+                    return false;
+                }
+                mAppSnippet = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
+                        mPm.getApplicationIcon(mPkgInfo.applicationInfo));
+            } break;
+
+            case ContentResolver.SCHEME_FILE: {
+                File sourceFile = new File(packageUri.getPath());
+                PackageParser.Package parsed = PackageUtil.getPackageInfo(this, sourceFile);
+
+                // Check for parse errors
+                if (parsed == null) {
+                    Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
+                    showDialogInner(DLG_PACKAGE_ERROR);
+                    setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
+                    return false;
+                }
+                mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
+                        PackageManager.GET_PERMISSIONS, 0, 0, null,
+                        new PackageUserState());
+                mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
+            } break;
+
+            default: {
+                throw new IllegalArgumentException("Unexpected URI scheme " + packageUri);
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (mSessionId != -1) {
+            mInstaller.setPermissionsResult(mSessionId, false);
+        }
+        super.onBackPressed();
+    }
+
+    public void onClick(View v) {
+        if (v == mOk) {
+            if (mOk.isEnabled()) {
+                if (mSessionId != -1) {
+                    mInstaller.setPermissionsResult(mSessionId, true);
+                    finish();
+                } else {
+                    startInstall();
+                }
+            }
+        } else if (v == mCancel) {
+            // Cancel and finish
+            setResult(RESULT_CANCELED);
+            if (mSessionId != -1) {
+                mInstaller.setPermissionsResult(mSessionId, false);
+            }
+            finish();
+        }
+    }
+
+    private void startInstall() {
+        // Start subactivity to actually install the application
+        Intent newIntent = new Intent();
+        newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
+                mPkgInfo.applicationInfo);
+        newIntent.setData(mPackageURI);
+        newIntent.setClass(this, InstallInstalling.class);
+        String installerPackageName = getIntent().getStringExtra(
+                Intent.EXTRA_INSTALLER_PACKAGE_NAME);
+        if (mOriginatingURI != null) {
+            newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
+        }
+        if (mReferrerURI != null) {
+            newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
+        }
+        if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
+            newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
+        }
+        if (installerPackageName != null) {
+            newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
+                    installerPackageName);
+        }
+        if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
+            newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
+        }
+        newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+        if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
+        startActivity(newIntent);
+        finish();
+    }
+
+    /**
+     * A simple error dialog showing a message
+     */
+    public static class SimpleErrorDialog extends DialogFragment {
+        private static final String MESSAGE_KEY =
+                SimpleErrorDialog.class.getName() + "MESSAGE_KEY";
+
+        static SimpleErrorDialog newInstance(@StringRes int message) {
+            SimpleErrorDialog dialog = new SimpleErrorDialog();
+
+            Bundle args = new Bundle();
+            args.putInt(MESSAGE_KEY, message);
+            dialog.setArguments(args);
+
+            return dialog;
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            return new AlertDialog.Builder(getActivity())
+                    .setMessage(getArguments().getInt(MESSAGE_KEY))
+                    .setPositiveButton(R.string.ok, (dialog, which) -> getActivity().finish())
+                    .create();
+        }
+    }
+
+    /**
+     * Dialog to show when the source of apk can not be identified
+     */
+    public static class AnonymousSourceDialog extends DialogFragment {
+        static AnonymousSourceDialog newInstance() {
+            return new AnonymousSourceDialog();
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            return new AlertDialog.Builder(getActivity())
+                    .setMessage(R.string.anonymous_source_warning)
+                    .setPositiveButton(R.string.anonymous_source_continue,
+                            ((dialog, which) -> {
+                                PackageInstallerActivity activity = ((PackageInstallerActivity)
+                                        getActivity());
+
+                                activity.mAllowUnknownSources = true;
+                                activity.initiateInstall();
+                            }))
+                    .setNegativeButton(R.string.cancel, ((dialog, which) -> getActivity().finish()))
+                    .create();
+        }
+
+        @Override
+        public void onCancel(DialogInterface dialog) {
+            getActivity().finish();
+        }
+    }
+
+    /**
+     * An error dialog shown when the app is not supported on wear
+     */
+    public static class NotSupportedOnWearDialog extends SimpleErrorDialog {
+        static SimpleErrorDialog newInstance() {
+            return SimpleErrorDialog.newInstance(R.string.wear_not_allowed_dlg_text);
+        }
+
+        @Override
+        public void onCancel(DialogInterface dialog) {
+            getActivity().setResult(RESULT_OK);
+            getActivity().finish();
+        }
+    }
+
+    /**
+     * An error dialog shown when the device is out of space
+     */
+    public static class OutOfSpaceDialog extends AppErrorDialog {
+        static AppErrorDialog newInstance(@NonNull CharSequence applicationLabel) {
+            OutOfSpaceDialog dialog = new OutOfSpaceDialog();
+            dialog.setArgument(applicationLabel);
+            return dialog;
+        }
+
+        @Override
+        protected Dialog createDialog(@NonNull CharSequence argument) {
+            String dlgText = getString(R.string.out_of_space_dlg_text, argument);
+            return new AlertDialog.Builder(getActivity())
+                    .setMessage(dlgText)
+                    .setPositiveButton(R.string.manage_applications, (dialog, which) -> {
+                        // launch manage applications
+                        Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
+                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        startActivity(intent);
+                        getActivity().finish();
+                    })
+                    .setNegativeButton(R.string.cancel, (dialog, which) -> getActivity().finish())
+                    .create();
+        }
+    }
+
+    /**
+     * A generic install-error dialog
+     */
+    public static class InstallErrorDialog extends AppErrorDialog {
+        static AppErrorDialog newInstance(@NonNull CharSequence applicationLabel) {
+            InstallErrorDialog dialog = new InstallErrorDialog();
+            dialog.setArgument(applicationLabel);
+            return dialog;
+        }
+
+        @Override
+        protected Dialog createDialog(@NonNull CharSequence argument) {
+            return new AlertDialog.Builder(getActivity())
+                    .setNeutralButton(R.string.ok, (dialog, which) -> getActivity().finish())
+                    .setMessage(getString(R.string.install_failed_msg, argument))
+                    .create();
+        }
+    }
+
+    /**
+     * An error dialog shown when external sources are not allowed
+     */
+    public static class ExternalSourcesBlockedDialog extends AppErrorDialog {
+        static AppErrorDialog newInstance(@NonNull String originationPkg) {
+            ExternalSourcesBlockedDialog dialog = new ExternalSourcesBlockedDialog();
+            dialog.setArgument(originationPkg);
+            return dialog;
+        }
+
+        @Override
+        protected Dialog createDialog(@NonNull CharSequence argument) {
+            try {
+                PackageManager pm = getActivity().getPackageManager();
+
+                ApplicationInfo sourceInfo = pm.getApplicationInfo(argument.toString(), 0);
+
+                return new AlertDialog.Builder(getActivity())
+                        .setTitle(pm.getApplicationLabel(sourceInfo))
+                        .setIcon(pm.getApplicationIcon(sourceInfo))
+                        .setMessage(R.string.untrusted_external_source_warning)
+                        .setPositiveButton(R.string.external_sources_settings,
+                                (dialog, which) -> {
+                                    Intent settingsIntent = new Intent();
+                                    settingsIntent.setAction(
+                                            Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
+                                    final Uri packageUri = Uri.parse("package:" + argument);
+                                    settingsIntent.setData(packageUri);
+                                    try {
+                                        getActivity().startActivityForResult(settingsIntent,
+                                                REQUEST_TRUST_EXTERNAL_SOURCE);
+                                    } catch (ActivityNotFoundException exc) {
+                                        Log.e(TAG, "Settings activity not found for action: "
+                                                + Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
+                                    }
+                                })
+                        .setNegativeButton(R.string.cancel,
+                                (dialog, which) -> getActivity().finish())
+                        .create();
+            } catch (NameNotFoundException e) {
+                Log.e(TAG, "Did not find app info for " + argument);
+                getActivity().finish();
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Superclass for all error dialogs. Stores a single CharSequence argument
+     */
+    public abstract static class AppErrorDialog extends DialogFragment {
+        private static final String ARGUMENT_KEY = AppErrorDialog.class.getName() + "ARGUMENT_KEY";
+
+        protected void setArgument(@NonNull CharSequence argument) {
+            Bundle args = new Bundle();
+            args.putCharSequence(ARGUMENT_KEY, argument);
+            setArguments(args);
+        }
+
+        protected abstract Dialog createDialog(@NonNull CharSequence argument);
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            return createDialog(getArguments().getString(ARGUMENT_KEY));
+        }
+
+        @Override
+        public void onCancel(DialogInterface dialog) {
+            getActivity().finish();
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerApplication.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerApplication.java
new file mode 100644
index 0000000..9b7e64e
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerApplication.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 com.android.packageinstaller;
+
+import android.app.Application;
+import android.content.pm.PackageItemInfo;
+
+public class PackageInstallerApplication extends Application {
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        PackageItemInfo.setForceSafeLabels(true);
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
new file mode 100644
index 0000000..ba4bf8a
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -0,0 +1,210 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES 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.packageinstaller;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.io.File;
+
+/**
+ * This is a utility class for defining some utility methods and constants
+ * used in the package installer application.
+ */
+public class PackageUtil {
+    private static final String LOG_TAG = PackageUtil.class.getSimpleName();
+
+    public static final String PREFIX="com.android.packageinstaller.";
+    public static final String INTENT_ATTR_INSTALL_STATUS = PREFIX+"installStatus";
+    public static final String INTENT_ATTR_APPLICATION_INFO=PREFIX+"applicationInfo";
+    public static final String INTENT_ATTR_PERMISSIONS_LIST=PREFIX+"PermissionsList";
+    //intent attribute strings related to uninstall
+    public static final String INTENT_ATTR_PACKAGE_NAME=PREFIX+"PackageName";
+
+    /**
+     * Utility method to get package information for a given {@link File}
+     */
+    public static PackageParser.Package getPackageInfo(Context context, File sourceFile) {
+        final PackageParser parser = new PackageParser();
+        parser.setCallback(new PackageParser.CallbackImpl(context.getPackageManager()));
+        try {
+            return parser.parsePackage(sourceFile, 0);
+        } catch (PackageParserException e) {
+            return null;
+        }
+    }
+
+    public static View initSnippet(View snippetView, CharSequence label, Drawable icon) {
+        ((ImageView)snippetView.findViewById(R.id.app_icon)).setImageDrawable(icon);
+        ((TextView)snippetView.findViewById(R.id.app_name)).setText(label);
+        return snippetView;
+    }
+
+    /**
+     * Utility method to display a snippet of an installed application.
+     * The content view should have been set on context before invoking this method.
+     * appSnippet view should include R.id.app_icon and R.id.app_name
+     * defined on it.
+     *
+     * @param pContext context of package that can load the resources
+     * @param componentInfo ComponentInfo object whose resources are to be loaded
+     * @param snippetView the snippet view
+     */
+    public static View initSnippetForInstalledApp(Context pContext,
+            ApplicationInfo appInfo, View snippetView) {
+        return initSnippetForInstalledApp(pContext, appInfo, snippetView, null);
+    }
+
+    /**
+     * Utility method to display a snippet of an installed application.
+     * The content view should have been set on context before invoking this method.
+     * appSnippet view should include R.id.app_icon and R.id.app_name
+     * defined on it.
+     *
+     * @param pContext context of package that can load the resources
+     * @param componentInfo ComponentInfo object whose resources are to be loaded
+     * @param snippetView the snippet view
+     * @param UserHandle user that the app si installed for.
+     */
+    public static View initSnippetForInstalledApp(Context pContext,
+            ApplicationInfo appInfo, View snippetView, UserHandle user) {
+        final PackageManager pm = pContext.getPackageManager();
+        Drawable icon = appInfo.loadIcon(pm);
+        if (user != null) {
+            icon = pContext.getPackageManager().getUserBadgedIcon(icon, user);
+        }
+        return initSnippet(
+                snippetView,
+                appInfo.loadLabel(pm),
+                icon);
+    }
+
+    /**
+     * Utility method to display application snippet of a new package.
+     * The content view should have been set on context before invoking this method.
+     * appSnippet view should include R.id.app_icon and R.id.app_name
+     * defined on it.
+     *
+     * @param pContext context of package that can load the resources
+     * @param as The resources to be loaded
+     * @param snippetId view id of app snippet view
+     */
+    @NonNull public static View initSnippetForNewApp(@NonNull Activity pContext,
+            @NonNull AppSnippet as, int snippetId) {
+        View appSnippet = pContext.findViewById(snippetId);
+        if (as.icon != null) {
+            ((ImageView) appSnippet.findViewById(R.id.app_icon)).setImageDrawable(as.icon);
+        }
+        ((TextView)appSnippet.findViewById(R.id.app_name)).setText(as.label);
+        return appSnippet;
+    }
+
+    static public class AppSnippet {
+        @NonNull public CharSequence label;
+        @Nullable public Drawable icon;
+        public AppSnippet(@NonNull CharSequence label, @Nullable Drawable icon) {
+            this.label = label;
+            this.icon = icon;
+        }
+    }
+
+    /**
+     * Utility method to load application label
+     *
+     * @param pContext context of package that can load the resources
+     * @param appInfo ApplicationInfo object of package whose resources are to be loaded
+     * @param sourceFile File the package is in
+     */
+    public static AppSnippet getAppSnippet(
+            Activity pContext, ApplicationInfo appInfo, File sourceFile) {
+        final String archiveFilePath = sourceFile.getAbsolutePath();
+        Resources pRes = pContext.getResources();
+        AssetManager assmgr = new AssetManager();
+        assmgr.addAssetPath(archiveFilePath);
+        Resources res = new Resources(assmgr, pRes.getDisplayMetrics(), pRes.getConfiguration());
+        CharSequence label = null;
+        // Try to load the label from the package's resources. If an app has not explicitly
+        // specified any label, just use the package name.
+        if (appInfo.labelRes != 0) {
+            try {
+                label = res.getText(appInfo.labelRes);
+            } catch (Resources.NotFoundException e) {
+            }
+        }
+        if (label == null) {
+            label = (appInfo.nonLocalizedLabel != null) ?
+                    appInfo.nonLocalizedLabel : appInfo.packageName;
+        }
+        Drawable icon = null;
+        // Try to load the icon from the package's resources. If an app has not explicitly
+        // specified any resource, just use the default icon for now.
+        try {
+            if (appInfo.icon != 0) {
+                try {
+                    icon = res.getDrawable(appInfo.icon);
+                } catch (Resources.NotFoundException e) {
+                }
+            }
+            if (icon == null) {
+                icon = pContext.getPackageManager().getDefaultActivityIcon();
+            }
+        } catch (OutOfMemoryError e) {
+            Log.i(LOG_TAG, "Could not load app icon", e);
+        }
+        return new PackageUtil.AppSnippet(label, icon);
+    }
+
+    /**
+     * Get the maximum target sdk for a UID.
+     *
+     * @param context The context to use
+     * @param uid The UID requesting the install/uninstall
+     *
+     * @return The maximum target SDK or -1 if the uid does not match any packages.
+     */
+    static int getMaxTargetSdkVersionForUid(@NonNull Context context, int uid) {
+        PackageManager pm = context.getPackageManager();
+        final String[] packages = pm.getPackagesForUid(uid);
+        int targetSdkVersion = -1;
+        if (packages != null) {
+            for (String packageName : packages) {
+                try {
+                    ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+                    targetSdkVersion = Math.max(targetSdkVersion, info.targetSdkVersion);
+                } catch (PackageManager.NameNotFoundException e) {
+                    // Ignore and try the next package
+                }
+            }
+        }
+        return targetSdkVersion;
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/RemoveReceiver.java b/packages/PackageInstaller/src/com/android/packageinstaller/RemoveReceiver.java
new file mode 100644
index 0000000..7d8064d
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/RemoveReceiver.java
@@ -0,0 +1,42 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES 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.packageinstaller;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+
+public class RemoveReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(intent.getAction())) {
+            Uri uri = intent.getData();
+            String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
+            if (pkg != null) {
+                SharedPreferences prefs = context.getSharedPreferences(
+                        PackageInstallerActivity.PREFS_ALLOWED_SOURCES,
+                        Context.MODE_PRIVATE);
+                if (prefs.getBoolean(pkg, false)) {
+                    prefs.edit().remove(pkg).apply();
+                }
+            }
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/TemporaryFileManager.java b/packages/PackageInstaller/src/com/android/packageinstaller/TemporaryFileManager.java
new file mode 100644
index 0000000..f77318c
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/TemporaryFileManager.java
@@ -0,0 +1,95 @@
+/*
+ * 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.packageinstaller;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Manages files of the package installer and resets state during boot.
+ */
+public class TemporaryFileManager extends BroadcastReceiver {
+    private static final String LOG_TAG = TemporaryFileManager.class.getSimpleName();
+
+    /**
+     * Create a new file to hold a staged file.
+     *
+     * @param context The context of the caller
+     *
+     * @return A new file
+     */
+    @NonNull
+    public static File getStagedFile(@NonNull Context context) throws IOException {
+        return File.createTempFile("package", ".apk", context.getNoBackupFilesDir());
+    }
+
+    /**
+     * Get the file used to store the results of installs.
+     *
+     * @param context The context of the caller
+     *
+     * @return the file used to store the results of installs
+     */
+    @NonNull
+    public static File getInstallStateFile(@NonNull Context context) {
+        return new File(context.getNoBackupFilesDir(), "install_results.xml");
+    }
+
+    /**
+     * Get the file used to store the results of uninstalls.
+     *
+     * @param context The context of the caller
+     *
+     * @return the file used to store the results of uninstalls
+     */
+    @NonNull
+    public static File getUninstallStateFile(@NonNull Context context) {
+        return new File(context.getNoBackupFilesDir(), "uninstall_results.xml");
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        long systemBootTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
+
+        File[] filesOnBoot = context.getNoBackupFilesDir().listFiles();
+
+        if (filesOnBoot == null) {
+            return;
+        }
+
+        for (int i = 0; i < filesOnBoot.length; i++) {
+            File fileOnBoot = filesOnBoot[i];
+
+            if (systemBootTime > fileOnBoot.lastModified()) {
+                boolean wasDeleted = fileOnBoot.delete();
+                if (!wasDeleted) {
+                    Log.w(LOG_TAG, "Could not delete " + fileOnBoot.getName() + " onBoot");
+                }
+            } else {
+                Log.w(LOG_TAG, fileOnBoot.getName() + " was created before onBoot broadcast was "
+                        + "received");
+            }
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallEventReceiver.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallEventReceiver.java
new file mode 100644
index 0000000..c3e9c23
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallEventReceiver.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Receives uninstall events and persists them using a {@link EventResultPersister}.
+ */
+public class UninstallEventReceiver extends BroadcastReceiver {
+    private static final Object sLock = new Object();
+    private static EventResultPersister sReceiver;
+
+    /**
+     * Get the event receiver persisting the results
+     *
+     * @return The event receiver.
+     */
+    @NonNull private static EventResultPersister getReceiver(@NonNull Context context) {
+        synchronized (sLock) {
+            if (sReceiver == null) {
+                sReceiver = new EventResultPersister(
+                        TemporaryFileManager.getUninstallStateFile(context));
+            }
+        }
+
+        return sReceiver;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        getReceiver(context).onEventReceived(context, intent);
+    }
+
+    /**
+     * Add an observer. If there is already an event for this id, call back inside of this call.
+     *
+     * @param context  A context of the current app
+     * @param id       The id the observer is for or {@code GENERATE_NEW_ID} to generate a new one.
+     * @param observer The observer to call back.
+     *
+     * @return The id for this event
+     */
+    static int addObserver(@NonNull Context context, int id,
+            @NonNull EventResultPersister.EventResultObserver observer)
+            throws EventResultPersister.OutOfIdsException {
+        return getReceiver(context).addObserver(id, observer);
+    }
+
+    /**
+     * Remove a observer.
+     *
+     * @param context  A context of the current app
+     * @param id The id the observer was added for
+     */
+    static void removeObserver(@NonNull Context context, int id) {
+        getReceiver(context).removeObserver(id);
+    }
+
+    /**
+     * @param context A context of the current app
+     *
+     * @return A new uninstall id
+     */
+    static int getNewId(@NonNull Context context) throws EventResultPersister.OutOfIdsException {
+        return getReceiver(context).getNewId();
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
new file mode 100644
index 0000000..5a51ac2
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.admin.IDevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.graphics.drawable.Icon;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.List;
+
+/**
+ * Finish an uninstallation and show Toast on success or failure notification.
+ */
+public class UninstallFinish extends BroadcastReceiver {
+    private static final String LOG_TAG = UninstallFinish.class.getSimpleName();
+
+    private static final String UNINSTALL_FAILURE_CHANNEL = "uninstall failure";
+
+    static final String EXTRA_UNINSTALL_ID = "com.android.packageinstaller.extra.UNINSTALL_ID";
+    static final String EXTRA_APP_LABEL = "com.android.packageinstaller.extra.APP_LABEL";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        int returnCode = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
+
+        Log.i(LOG_TAG, "Uninstall finished extras=" + intent.getExtras());
+
+        if (returnCode == PackageInstaller.STATUS_PENDING_USER_ACTION) {
+            context.startActivity(intent.getParcelableExtra(Intent.EXTRA_INTENT));
+            return;
+        }
+
+        int uninstallId = intent.getIntExtra(EXTRA_UNINSTALL_ID, 0);
+        ApplicationInfo appInfo = intent.getParcelableExtra(
+                PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+        String appLabel = intent.getStringExtra(EXTRA_APP_LABEL);
+        boolean allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
+
+        NotificationManager notificationManager =
+                context.getSystemService(NotificationManager.class);
+        UserManager userManager = context.getSystemService(UserManager.class);
+
+        NotificationChannel uninstallFailureChannel = new NotificationChannel(
+                UNINSTALL_FAILURE_CHANNEL,
+                context.getString(R.string.uninstall_failure_notification_channel),
+                NotificationManager.IMPORTANCE_DEFAULT);
+        notificationManager.createNotificationChannel(uninstallFailureChannel);
+
+        Notification.Builder uninstallFailedNotification = new Notification.Builder(context,
+                UNINSTALL_FAILURE_CHANNEL);
+
+        switch (returnCode) {
+            case PackageInstaller.STATUS_SUCCESS:
+                notificationManager.cancel(uninstallId);
+
+                Toast.makeText(context, context.getString(R.string.uninstall_done_app, appLabel),
+                        Toast.LENGTH_LONG).show();
+                return;
+            case PackageInstaller.STATUS_FAILURE_BLOCKED: {
+                int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, 0);
+
+                switch (legacyStatus) {
+                    case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER: {
+                        IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
+                                ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+                        // Find out if the package is an active admin for some non-current user.
+                        int myUserId = UserHandle.myUserId();
+                        UserInfo otherBlockingUser = null;
+                        for (UserInfo user : userManager.getUsers()) {
+                            // We only catch the case when the user in question is neither the
+                            // current user nor its profile.
+                            if (isProfileOfOrSame(userManager, myUserId, user.id)) {
+                                continue;
+                            }
+
+                            try {
+                                if (dpm.packageHasActiveAdmins(appInfo.packageName, user.id)) {
+                                    otherBlockingUser = user;
+                                    break;
+                                }
+                            } catch (RemoteException e) {
+                                Log.e(LOG_TAG, "Failed to talk to package manager", e);
+                            }
+                        }
+                        if (otherBlockingUser == null) {
+                            Log.d(LOG_TAG, "Uninstall failed because " + appInfo.packageName
+                                    + " is a device admin");
+
+                            addDeviceManagerButton(context, uninstallFailedNotification);
+                            setBigText(uninstallFailedNotification, context.getString(
+                                    R.string.uninstall_failed_device_policy_manager));
+                        } else {
+                            Log.d(LOG_TAG, "Uninstall failed because " + appInfo.packageName
+                                    + " is a device admin of user " + otherBlockingUser);
+
+                            setBigText(uninstallFailedNotification, String.format(context.getString(
+                                    R.string.uninstall_failed_device_policy_manager_of_user),
+                                    otherBlockingUser.name));
+                        }
+                        break;
+                    }
+                    case PackageManager.DELETE_FAILED_OWNER_BLOCKED: {
+                        IPackageManager packageManager = IPackageManager.Stub.asInterface(
+                                ServiceManager.getService("package"));
+
+                        List<UserInfo> users = userManager.getUsers();
+                        int blockingUserId = UserHandle.USER_NULL;
+                        for (int i = 0; i < users.size(); ++i) {
+                            final UserInfo user = users.get(i);
+                            try {
+                                if (packageManager.getBlockUninstallForUser(appInfo.packageName,
+                                        user.id)) {
+                                    blockingUserId = user.id;
+                                    break;
+                                }
+                            } catch (RemoteException e) {
+                                // Shouldn't happen.
+                                Log.e(LOG_TAG, "Failed to talk to package manager", e);
+                            }
+                        }
+
+                        int myUserId = UserHandle.myUserId();
+                        if (isProfileOfOrSame(userManager, myUserId, blockingUserId)) {
+                            addDeviceManagerButton(context, uninstallFailedNotification);
+                        } else {
+                            addManageUsersButton(context, uninstallFailedNotification);
+                        }
+
+                        if (blockingUserId == UserHandle.USER_NULL) {
+                            Log.d(LOG_TAG,
+                                    "Uninstall failed for " + appInfo.packageName + " with code "
+                                            + returnCode + " no blocking user");
+                        } else if (blockingUserId == UserHandle.USER_SYSTEM) {
+                            setBigText(uninstallFailedNotification,
+                                    context.getString(R.string.uninstall_blocked_device_owner));
+                        } else {
+                            if (allUsers) {
+                                setBigText(uninstallFailedNotification,
+                                        context.getString(
+                                                R.string.uninstall_all_blocked_profile_owner));
+                            } else {
+                                setBigText(uninstallFailedNotification, context.getString(
+                                        R.string.uninstall_blocked_profile_owner));
+                            }
+                        }
+                        break;
+                    }
+                    default:
+                        Log.d(LOG_TAG, "Uninstall blocked for " + appInfo.packageName
+                                + " with legacy code " + legacyStatus);
+                } break;
+            }
+            default:
+                Log.d(LOG_TAG, "Uninstall failed for " + appInfo.packageName + " with code "
+                        + returnCode);
+                break;
+        }
+
+        uninstallFailedNotification.setContentTitle(
+                context.getString(R.string.uninstall_failed_app, appLabel));
+        uninstallFailedNotification.setOngoing(false);
+        uninstallFailedNotification.setSmallIcon(R.drawable.ic_error);
+        notificationManager.notify(uninstallId, uninstallFailedNotification.build());
+    }
+
+    /**
+     * Is a profile part of a user?
+     *
+     * @param userManager The user manager
+     * @param userId The id of the user
+     * @param profileId The id of the profile
+     *
+     * @return If the profile is part of the user or the profile parent of the user
+     */
+    private boolean isProfileOfOrSame(@NonNull UserManager userManager, int userId, int profileId) {
+        if (userId == profileId) {
+            return true;
+        }
+
+        UserInfo parentUser = userManager.getProfileParent(profileId);
+        return parentUser != null && parentUser.id == userId;
+    }
+
+    /**
+     * Set big text for the notification.
+     *
+     * @param builder The builder of the notification
+     * @param text The text to set.
+     */
+    private void setBigText(@NonNull Notification.Builder builder,
+            @NonNull CharSequence text) {
+        builder.setStyle(new Notification.BigTextStyle().bigText(text));
+    }
+
+    /**
+     * Add a button to the notification that links to the user management.
+     *
+     * @param context The context the notification is created in
+     * @param builder The builder of the notification
+     */
+    private void addManageUsersButton(@NonNull Context context,
+            @NonNull Notification.Builder builder) {
+        Intent intent = new Intent(Settings.ACTION_USER_SETTINGS);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        builder.addAction((new Notification.Action.Builder(
+                Icon.createWithResource(context, R.drawable.ic_settings_multiuser),
+                context.getString(R.string.manage_users),
+                PendingIntent.getActivity(context, 0, intent,
+                        PendingIntent.FLAG_UPDATE_CURRENT))).build());
+    }
+
+    /**
+     * Add a button to the notification that links to the device policy management.
+     *
+     * @param context The context the notification is created in
+     * @param builder The builder of the notification
+     */
+    private void addDeviceManagerButton(@NonNull Context context,
+            @NonNull Notification.Builder builder) {
+        Intent intent = new Intent();
+        intent.setClassName("com.android.settings",
+                "com.android.settings.Settings$DeviceAdminSettingsActivity");
+        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        builder.addAction((new Notification.Action.Builder(
+                Icon.createWithResource(context, R.drawable.ic_lock),
+                context.getString(R.string.manage_device_administrators),
+                PendingIntent.getActivity(context, 0, intent,
+                        PendingIntent.FLAG_UPDATE_CURRENT))).build());
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
new file mode 100644
index 0000000..1c0aec1
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDeleteObserver2;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.widget.Toast;
+
+/**
+ * Start an uninstallation, show a dialog while uninstalling and return result to the caller.
+ */
+public class UninstallUninstalling extends Activity implements
+        EventResultPersister.EventResultObserver {
+    private static final String LOG_TAG = UninstallUninstalling.class.getSimpleName();
+
+    private static final String UNINSTALL_ID = "com.android.packageinstaller.UNINSTALL_ID";
+    private static final String BROADCAST_ACTION =
+            "com.android.packageinstaller.ACTION_UNINSTALL_COMMIT";
+
+    static final String EXTRA_APP_LABEL = "com.android.packageinstaller.extra.APP_LABEL";
+
+    private int mUninstallId;
+    private ApplicationInfo mAppInfo;
+    private IBinder mCallback;
+    private boolean mReturnResult;
+    private String mLabel;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setFinishOnTouchOutside(false);
+
+        mAppInfo = getIntent().getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+        mCallback = getIntent().getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
+        mReturnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
+        mLabel = getIntent().getStringExtra(EXTRA_APP_LABEL);
+
+        try {
+            if (savedInstanceState == null) {
+                boolean allUsers = getIntent().getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS,
+                        false);
+                UserHandle user = getIntent().getParcelableExtra(Intent.EXTRA_USER);
+
+                // Show dialog, which is the whole UI
+                FragmentTransaction transaction = getFragmentManager().beginTransaction();
+                Fragment prev = getFragmentManager().findFragmentByTag("dialog");
+                if (prev != null) {
+                    transaction.remove(prev);
+                }
+                DialogFragment dialog = new UninstallUninstallingFragment();
+                dialog.setCancelable(false);
+                dialog.show(transaction, "dialog");
+
+                mUninstallId = UninstallEventReceiver.addObserver(this,
+                        EventResultPersister.GENERATE_NEW_ID, this);
+
+                Intent broadcastIntent = new Intent(BROADCAST_ACTION);
+                broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mUninstallId);
+                broadcastIntent.setPackage(getPackageName());
+
+                PendingIntent pendingIntent = PendingIntent.getBroadcast(this, mUninstallId,
+                        broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+                try {
+                    ActivityThread.getPackageManager().getPackageInstaller().uninstall(
+                            new VersionedPackage(mAppInfo.packageName,
+                                    PackageManager.VERSION_CODE_HIGHEST),
+                            getPackageName(), allUsers ? PackageManager.DELETE_ALL_USERS : 0,
+                            pendingIntent.getIntentSender(), user.getIdentifier());
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
+            } else {
+                mUninstallId = savedInstanceState.getInt(UNINSTALL_ID);
+                UninstallEventReceiver.addObserver(this, mUninstallId, this);
+            }
+        } catch (EventResultPersister.OutOfIdsException | IllegalArgumentException e) {
+            Log.e(LOG_TAG, "Fails to start uninstall", e);
+            onResult(PackageInstaller.STATUS_FAILURE, PackageManager.DELETE_FAILED_INTERNAL_ERROR,
+                    null);
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putInt(UNINSTALL_ID, mUninstallId);
+    }
+
+    @Override
+    public void onBackPressed() {
+        // do nothing
+    }
+
+    @Override
+    public void onResult(int status, int legacyStatus, @Nullable String message) {
+        if (mCallback != null) {
+            // The caller will be informed about the result via a callback
+            final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub
+                    .asInterface(mCallback);
+            try {
+                observer.onPackageDeleted(mAppInfo.packageName, legacyStatus, message);
+            } catch (RemoteException ignored) {
+            }
+        } else if (mReturnResult) {
+            // The caller will be informed about the result and might decide to display it
+            Intent result = new Intent();
+
+            result.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus);
+            setResult(status == PackageInstaller.STATUS_SUCCESS ? Activity.RESULT_OK
+                    : Activity.RESULT_FIRST_USER, result);
+        } else {
+            // This is the rare case that the caller did not ask for the result, but wanted to be
+            // notified via onActivityResult when the installation finishes
+            if (status != PackageInstaller.STATUS_SUCCESS) {
+                Toast.makeText(this, getString(R.string.uninstall_failed_app, mLabel),
+                        Toast.LENGTH_LONG).show();
+            }
+        }
+        finish();
+    }
+
+    @Override
+    protected void onDestroy() {
+        UninstallEventReceiver.removeObserver(this, mUninstallId);
+
+        super.onDestroy();
+    }
+
+    /**
+     * Dialog that shows that the app is uninstalling.
+     */
+    public static class UninstallUninstallingFragment extends DialogFragment {
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
+
+            dialogBuilder.setCancelable(false);
+            dialogBuilder.setMessage(getActivity().getString(R.string.uninstalling_app,
+                    ((UninstallUninstalling) getActivity()).mLabel));
+
+            Dialog dialog = dialogBuilder.create();
+            dialog.setCanceledOnTouchOutside(false);
+
+            return dialog;
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
new file mode 100755
index 0000000..1a01dc0
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -0,0 +1,395 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES 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.packageinstaller;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+
+import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.StringRes;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDeleteObserver2;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import com.android.packageinstaller.handheld.ErrorDialogFragment;
+import com.android.packageinstaller.handheld.UninstallAlertDialogFragment;
+import com.android.packageinstaller.television.ErrorFragment;
+import com.android.packageinstaller.television.UninstallAlertFragment;
+import com.android.packageinstaller.television.UninstallAppProgress;
+
+import java.util.List;
+
+/*
+ * This activity presents UI to uninstall an application. Usually launched with intent
+ * Intent.ACTION_UNINSTALL_PKG_COMMAND and attribute
+ * com.android.packageinstaller.PackageName set to the application package name
+ */
+public class UninstallerActivity extends Activity {
+    private static final String TAG = "UninstallerActivity";
+
+    private static final String UNINSTALLING_CHANNEL = "uninstalling";
+
+    public static class DialogInfo {
+        public ApplicationInfo appInfo;
+        public ActivityInfo activityInfo;
+        public boolean allUsers;
+        public UserHandle user;
+        public IBinder callback;
+    }
+
+    private String mPackageName;
+    private DialogInfo mDialogInfo;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        // Never restore any state, esp. never create any fragments. The data in the fragment might
+        // be stale, if e.g. the app was uninstalled while the activity was destroyed.
+        super.onCreate(null);
+
+        try {
+            int callingUid = ActivityManager.getService().getLaunchedFromUid(getActivityToken());
+
+            String callingPackage = getPackageNameForUid(callingUid);
+            if (callingPackage == null) {
+                Log.e(TAG, "Package not found for originating uid " + callingUid);
+                setResult(Activity.RESULT_FIRST_USER);
+                finish();
+                return;
+            } else {
+                AppOpsManager appOpsManager = (AppOpsManager) getSystemService(
+                        Context.APP_OPS_SERVICE);
+                if (appOpsManager.noteOpNoThrow(
+                        AppOpsManager.OPSTR_REQUEST_DELETE_PACKAGES, callingUid, callingPackage)
+                        != MODE_ALLOWED) {
+                    Log.e(TAG, "Install from uid " + callingUid + " disallowed by AppOps");
+                    setResult(Activity.RESULT_FIRST_USER);
+                    finish();
+                    return;
+                }
+            }
+
+            if (getMaxTargetSdkVersionForUid(this, callingUid)
+                    >= Build.VERSION_CODES.P && AppGlobals.getPackageManager().checkUidPermission(
+                    Manifest.permission.REQUEST_DELETE_PACKAGES, callingUid)
+                    != PackageManager.PERMISSION_GRANTED
+                    && AppGlobals.getPackageManager().checkUidPermission(
+                            Manifest.permission.DELETE_PACKAGES, callingUid)
+                            != PackageManager.PERMISSION_GRANTED) {
+                Log.e(TAG, "Uid " + callingUid + " does not have "
+                        + Manifest.permission.REQUEST_DELETE_PACKAGES + " or "
+                        + Manifest.permission.DELETE_PACKAGES);
+
+                setResult(Activity.RESULT_FIRST_USER);
+                finish();
+                return;
+            }
+        } catch (RemoteException ex) {
+            // Cannot reach Package/ActivityManager. Aborting uninstall.
+            Log.e(TAG, "Could not determine the launching uid.");
+
+            setResult(Activity.RESULT_FIRST_USER);
+            finish();
+            return;
+        }
+
+        // Get intent information.
+        // We expect an intent with URI of the form package://<packageName>#<className>
+        // className is optional; if specified, it is the activity the user chose to uninstall
+        final Intent intent = getIntent();
+        final Uri packageUri = intent.getData();
+        if (packageUri == null) {
+            Log.e(TAG, "No package URI in intent");
+            showAppNotFound();
+            return;
+        }
+        mPackageName = packageUri.getEncodedSchemeSpecificPart();
+        if (mPackageName == null) {
+            Log.e(TAG, "Invalid package name in URI: " + packageUri);
+            showAppNotFound();
+            return;
+        }
+
+        final IPackageManager pm = IPackageManager.Stub.asInterface(
+                ServiceManager.getService("package"));
+
+        mDialogInfo = new DialogInfo();
+
+        mDialogInfo.allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
+        if (mDialogInfo.allUsers && !UserManager.get(this).isAdminUser()) {
+            Log.e(TAG, "Only admin user can request uninstall for all users");
+            showUserIsNotAllowed();
+            return;
+        }
+        mDialogInfo.user = intent.getParcelableExtra(Intent.EXTRA_USER);
+        if (mDialogInfo.user == null) {
+            mDialogInfo.user = android.os.Process.myUserHandle();
+        } else {
+            UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
+            List<UserHandle> profiles = userManager.getUserProfiles();
+            if (!profiles.contains(mDialogInfo.user)) {
+                Log.e(TAG, "User " + android.os.Process.myUserHandle() + " can't request uninstall "
+                        + "for user " + mDialogInfo.user);
+                showUserIsNotAllowed();
+                return;
+            }
+        }
+
+        mDialogInfo.callback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
+
+        try {
+            mDialogInfo.appInfo = pm.getApplicationInfo(mPackageName,
+                    PackageManager.MATCH_ANY_USER, mDialogInfo.user.getIdentifier());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get packageName. Package manager is dead?");
+        }
+
+        if (mDialogInfo.appInfo == null) {
+            Log.e(TAG, "Invalid packageName: " + mPackageName);
+            showAppNotFound();
+            return;
+        }
+
+        // The class name may have been specified (e.g. when deleting an app from all apps)
+        final String className = packageUri.getFragment();
+        if (className != null) {
+            try {
+                mDialogInfo.activityInfo = pm.getActivityInfo(
+                        new ComponentName(mPackageName, className), 0,
+                        mDialogInfo.user.getIdentifier());
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to get className. Package manager is dead?");
+                // Continue as the ActivityInfo isn't critical.
+            }
+        }
+
+        showConfirmationDialog();
+    }
+
+    public DialogInfo getDialogInfo() {
+        return mDialogInfo;
+    }
+
+    private void showConfirmationDialog() {
+        if (isTv()) {
+            showContentFragment(new UninstallAlertFragment(), 0, 0);
+        } else {
+            showDialogFragment(new UninstallAlertDialogFragment(), 0, 0);
+        }
+    }
+
+    private void showAppNotFound() {
+        if (isTv()) {
+            showContentFragment(new ErrorFragment(), R.string.app_not_found_dlg_title,
+                    R.string.app_not_found_dlg_text);
+        } else {
+            showDialogFragment(new ErrorDialogFragment(), R.string.app_not_found_dlg_title,
+                    R.string.app_not_found_dlg_text);
+        }
+    }
+
+    private void showUserIsNotAllowed() {
+        if (isTv()) {
+            showContentFragment(new ErrorFragment(),
+                    R.string.user_is_not_allowed_dlg_title, R.string.user_is_not_allowed_dlg_text);
+        } else {
+            showDialogFragment(new ErrorDialogFragment(), 0, R.string.user_is_not_allowed_dlg_text);
+        }
+    }
+
+    private void showGenericError() {
+        if (isTv()) {
+            showContentFragment(new ErrorFragment(),
+                    R.string.generic_error_dlg_title, R.string.generic_error_dlg_text);
+        } else {
+            showDialogFragment(new ErrorDialogFragment(), 0, R.string.generic_error_dlg_text);
+        }
+    }
+
+    private boolean isTv() {
+        return (getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK)
+                == Configuration.UI_MODE_TYPE_TELEVISION;
+    }
+
+    private void showContentFragment(@NonNull Fragment fragment, @StringRes int title,
+            @StringRes int text) {
+        Bundle args = new Bundle();
+        args.putInt(ErrorFragment.TITLE, title);
+        args.putInt(ErrorFragment.TEXT, text);
+        fragment.setArguments(args);
+
+        getFragmentManager().beginTransaction()
+                .replace(android.R.id.content, fragment)
+                .commit();
+    }
+
+    private void showDialogFragment(@NonNull DialogFragment fragment,
+            @StringRes int title, @StringRes int text) {
+        FragmentTransaction ft = getFragmentManager().beginTransaction();
+        Fragment prev = getFragmentManager().findFragmentByTag("dialog");
+        if (prev != null) {
+            ft.remove(prev);
+        }
+
+        Bundle args = new Bundle();
+        if (title != 0) {
+            args.putInt(ErrorDialogFragment.TITLE, title);
+        }
+        args.putInt(ErrorDialogFragment.TEXT, text);
+
+        fragment.setArguments(args);
+        fragment.show(ft, "dialog");
+    }
+
+    public void startUninstallProgress() {
+        boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
+        CharSequence label = mDialogInfo.appInfo.loadSafeLabel(getPackageManager());
+
+        if (isTv()) {
+            Intent newIntent = new Intent(Intent.ACTION_VIEW);
+            newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user);
+            newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
+            newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
+            newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
+
+            if (returnResult) {
+                newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
+                newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+            }
+
+            newIntent.setClass(this, UninstallAppProgress.class);
+            startActivity(newIntent);
+        } else if (returnResult || mDialogInfo.callback != null || getCallingActivity() != null) {
+            Intent newIntent = new Intent(this, UninstallUninstalling.class);
+
+            newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user);
+            newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
+            newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
+            newIntent.putExtra(UninstallUninstalling.EXTRA_APP_LABEL, label);
+            newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
+
+            if (returnResult) {
+                newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
+            }
+
+            if (returnResult || getCallingActivity() != null) {
+                newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+            }
+
+            startActivity(newIntent);
+        } else {
+            int uninstallId;
+            try {
+                uninstallId = UninstallEventReceiver.getNewId(this);
+            } catch (EventResultPersister.OutOfIdsException e) {
+                showGenericError();
+                return;
+            }
+
+            Intent broadcastIntent = new Intent(this, UninstallFinish.class);
+
+            broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            broadcastIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
+            broadcastIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
+            broadcastIntent.putExtra(UninstallFinish.EXTRA_APP_LABEL, label);
+            broadcastIntent.putExtra(UninstallFinish.EXTRA_UNINSTALL_ID, uninstallId);
+
+            PendingIntent pendingIntent = PendingIntent.getBroadcast(this, uninstallId,
+                    broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+            NotificationManager notificationManager = getSystemService(NotificationManager.class);
+            NotificationChannel uninstallingChannel = new NotificationChannel(UNINSTALLING_CHANNEL,
+                    getString(R.string.uninstalling_notification_channel),
+                    NotificationManager.IMPORTANCE_MIN);
+            notificationManager.createNotificationChannel(uninstallingChannel);
+
+            Notification uninstallingNotification =
+                    (new Notification.Builder(this, UNINSTALLING_CHANNEL))
+                    .setSmallIcon(R.drawable.ic_remove).setProgress(0, 1, true)
+                    .setContentTitle(getString(R.string.uninstalling_app, label)).setOngoing(true)
+                    .build();
+
+            notificationManager.notify(uninstallId, uninstallingNotification);
+
+            try {
+                Log.i(TAG, "Uninstalling extras=" + broadcastIntent.getExtras());
+
+                ActivityThread.getPackageManager().getPackageInstaller().uninstall(
+                        new VersionedPackage(mDialogInfo.appInfo.packageName,
+                                PackageManager.VERSION_CODE_HIGHEST),
+                        getPackageName(), mDialogInfo.allUsers
+                                ? PackageManager.DELETE_ALL_USERS : 0,
+                        pendingIntent.getIntentSender(), mDialogInfo.user.getIdentifier());
+            } catch (Exception e) {
+                notificationManager.cancel(uninstallId);
+
+                Log.e(TAG, "Cannot start uninstall", e);
+                showGenericError();
+            }
+        }
+    }
+
+    public void dispatchAborted() {
+        if (mDialogInfo != null && mDialogInfo.callback != null) {
+            final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub.asInterface(
+                    mDialogInfo.callback);
+            try {
+                observer.onPackageDeleted(mPackageName,
+                        PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user");
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    private String getPackageNameForUid(int sourceUid) {
+        String[] packagesForUid = getPackageManager().getPackagesForUid(sourceUid);
+        if (packagesForUid == null) {
+            return null;
+        }
+        return packagesForUid[0];
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/ErrorDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/ErrorDialogFragment.java
new file mode 100644
index 0000000..4ec6a2d
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/ErrorDialogFragment.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.handheld;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import com.android.packageinstaller.UninstallerActivity;
+
+public class ErrorDialogFragment extends DialogFragment {
+    public static final String TITLE = "com.android.packageinstaller.arg.title";
+    public static final String TEXT = "com.android.packageinstaller.arg.text";
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        AlertDialog.Builder b = new AlertDialog.Builder(getActivity())
+                .setMessage(getArguments().getInt(TEXT))
+                .setPositiveButton(android.R.string.ok, null);
+
+        if (getArguments().containsKey(TITLE)) {
+            b.setTitle(getArguments().getInt(TITLE));
+        }
+
+        return b.create();
+    }
+
+    @Override
+    public void onDismiss(DialogInterface dialog) {
+        super.onDismiss(dialog);
+        if (isAdded()) {
+            if (getActivity() instanceof UninstallerActivity) {
+                ((UninstallerActivity) getActivity()).dispatchAborted();
+            }
+
+            getActivity().setResult(Activity.RESULT_FIRST_USER);
+            getActivity().finish();
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
new file mode 100644
index 0000000..e0ca74e
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.handheld;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserManager;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.UninstallerActivity;
+
+public class UninstallAlertDialogFragment extends DialogFragment implements
+        DialogInterface.OnClickListener {
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final PackageManager pm = getActivity().getPackageManager();
+        final UninstallerActivity.DialogInfo dialogInfo =
+                ((UninstallerActivity) getActivity()).getDialogInfo();
+        final CharSequence appLabel = dialogInfo.appInfo.loadSafeLabel(pm);
+        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
+        StringBuilder messageBuilder = new StringBuilder();
+
+        // If the Activity label differs from the App label, then make sure the user
+        // knows the Activity belongs to the App being uninstalled.
+        if (dialogInfo.activityInfo != null) {
+            final CharSequence activityLabel = dialogInfo.activityInfo.loadSafeLabel(pm);
+            if (!activityLabel.equals(appLabel)) {
+                messageBuilder.append(
+                        getString(R.string.uninstall_activity_text, activityLabel));
+                messageBuilder.append(" ").append(appLabel).append(".\n\n");
+            }
+        }
+
+        final boolean isUpdate =
+                ((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+        UserManager userManager = UserManager.get(getActivity());
+        if (isUpdate) {
+            if (isSingleUser(userManager)) {
+                messageBuilder.append(getString(R.string.uninstall_update_text));
+            } else {
+                messageBuilder.append(getString(R.string.uninstall_update_text_multiuser));
+            }
+        } else {
+            if (dialogInfo.allUsers && !isSingleUser(userManager)) {
+                messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
+            } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) {
+                UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier());
+                messageBuilder.append(
+                        getString(R.string.uninstall_application_text_user, userInfo.name));
+            } else {
+                messageBuilder.append(getString(R.string.uninstall_application_text));
+            }
+        }
+
+        dialogBuilder.setTitle(appLabel);
+        dialogBuilder.setPositiveButton(android.R.string.ok, this);
+        dialogBuilder.setNegativeButton(android.R.string.cancel, this);
+        dialogBuilder.setMessage(messageBuilder.toString());
+        return dialogBuilder.create();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (which == Dialog.BUTTON_POSITIVE) {
+            ((UninstallerActivity) getActivity()).startUninstallProgress();
+        } else {
+            ((UninstallerActivity) getActivity()).dispatchAborted();
+        }
+    }
+
+    @Override
+    public void onDismiss(DialogInterface dialog) {
+        super.onDismiss(dialog);
+        if (isAdded()) {
+            getActivity().finish();
+        }
+    }
+
+    /**
+     * Returns whether there is only one user on this device, not including
+     * the system-only user.
+     */
+    private boolean isSingleUser(UserManager userManager) {
+        final int userCount = userManager.getUserCount();
+        return userCount == 1
+                || (UserManager.isSplitSystemUser() && userCount == 2);
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/ErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/ErrorFragment.java
new file mode 100644
index 0000000..f00f684
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/ErrorFragment.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.television;
+
+import android.app.Activity;
+import android.os.Bundle;
+import androidx.leanback.app.GuidedStepFragment;
+import androidx.leanback.widget.GuidanceStylist;
+import androidx.leanback.widget.GuidedAction;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.UninstallerActivity;
+
+import java.util.List;
+
+public class ErrorFragment extends GuidedStepFragment {
+    public static final String TITLE = "com.android.packageinstaller.arg.title";
+    public static final String TEXT = "com.android.packageinstaller.arg.text";
+
+    @Override
+    public int onProvideTheme() {
+        return R.style.Theme_Leanback_GuidedStep;
+    }
+
+    @Override
+    public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
+        return new GuidanceStylist.Guidance(
+                getString(getArguments().getInt(TITLE)),
+                getString(getArguments().getInt(TEXT)),
+                null,
+                null);
+    }
+
+    @Override
+    public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+        actions.add(new GuidedAction.Builder(getContext())
+                .clickAction(GuidedAction.ACTION_ID_OK)
+                .build());
+    }
+
+    @Override
+    public void onGuidedActionClicked(GuidedAction action) {
+        if (isAdded()) {
+            if (getActivity() instanceof UninstallerActivity) {
+                ((UninstallerActivity) getActivity()).dispatchAborted();
+            }
+
+            getActivity().setResult(Activity.RESULT_FIRST_USER);
+            getActivity().finish();
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
new file mode 100644
index 0000000..828e5db
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.television;
+
+import android.app.Activity;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserManager;
+import androidx.leanback.app.GuidedStepFragment;
+import androidx.leanback.widget.GuidanceStylist;
+import androidx.leanback.widget.GuidedAction;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.UninstallerActivity;
+
+import java.util.List;
+
+public class UninstallAlertFragment extends GuidedStepFragment {
+    @Override
+    public int onProvideTheme() {
+        return R.style.Theme_Leanback_GuidedStep;
+    }
+
+    @Override
+    public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
+        final PackageManager pm = getActivity().getPackageManager();
+        final UninstallerActivity.DialogInfo dialogInfo =
+                ((UninstallerActivity) getActivity()).getDialogInfo();
+        final CharSequence appLabel = dialogInfo.appInfo.loadSafeLabel(pm);
+
+        StringBuilder messageBuilder = new StringBuilder();
+
+        // If the Activity label differs from the App label, then make sure the user
+        // knows the Activity belongs to the App being uninstalled.
+        if (dialogInfo.activityInfo != null) {
+            final CharSequence activityLabel = dialogInfo.activityInfo.loadSafeLabel(pm);
+            if (!activityLabel.equals(appLabel)) {
+                messageBuilder.append(
+                        getString(R.string.uninstall_activity_text, activityLabel));
+                messageBuilder.append(" ").append(appLabel).append(".\n\n");
+            }
+        }
+
+        final boolean isUpdate =
+                ((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+        UserManager userManager = UserManager.get(getActivity());
+        if (isUpdate) {
+            if (isSingleUser(userManager)) {
+                messageBuilder.append(getString(R.string.uninstall_update_text));
+            } else {
+                messageBuilder.append(getString(R.string.uninstall_update_text_multiuser));
+            }
+        } else {
+            if (dialogInfo.allUsers && !isSingleUser(userManager)) {
+                messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
+            } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) {
+                UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier());
+                messageBuilder.append(
+                        getString(R.string.uninstall_application_text_user, userInfo.name));
+            } else {
+                messageBuilder.append(getString(R.string.uninstall_application_text));
+            }
+        }
+
+        return new GuidanceStylist.Guidance(
+                appLabel.toString(),
+                messageBuilder.toString(),
+                null,
+                dialogInfo.appInfo.loadIcon(pm));
+    }
+
+    @Override
+    public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+        actions.add(new GuidedAction.Builder(getContext())
+                .clickAction(GuidedAction.ACTION_ID_OK)
+                .build());
+        actions.add(new GuidedAction.Builder(getContext())
+                .clickAction(GuidedAction.ACTION_ID_CANCEL)
+                .build());
+    }
+
+    @Override
+    public void onGuidedActionClicked(GuidedAction action) {
+        if (isAdded()) {
+            if (action.getId() == GuidedAction.ACTION_ID_OK) {
+                ((UninstallerActivity) getActivity()).startUninstallProgress();
+                getActivity().finish();
+            } else {
+                ((UninstallerActivity) getActivity()).dispatchAborted();
+                getActivity().setResult(Activity.RESULT_FIRST_USER);
+                getActivity().finish();
+            }
+        }
+    }
+
+    /**
+     * Returns whether there is only one user on this device, not including
+     * the system-only user.
+     */
+    private boolean isSingleUser(UserManager userManager) {
+        final int userCount = userManager.getUserCount();
+        return userCount == 1
+                || (UserManager.isSplitSystemUser() && userCount == 2);
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgress.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgress.java
new file mode 100755
index 0000000..a4f217c
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgress.java
@@ -0,0 +1,377 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES 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.packageinstaller.television;
+
+import android.app.Activity;
+import android.app.admin.IDevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.IPackageDeleteObserver2;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.KeyEvent;
+import android.widget.Toast;
+
+import com.android.packageinstaller.PackageUtil;
+import com.android.packageinstaller.R;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+/**
+ * This activity corresponds to a download progress screen that is displayed
+ * when an application is uninstalled. The result of the application uninstall
+ * is indicated in the result code that gets set to 0 or 1. The application gets launched
+ * by an intent with the intent's class name explicitly set to UninstallAppProgress and expects
+ * the application object of the application to uninstall.
+ */
+public class UninstallAppProgress extends Activity {
+    private static final String TAG = "UninstallAppProgress";
+
+    private static final String FRAGMENT_TAG = "progress_fragment";
+
+    private ApplicationInfo mAppInfo;
+    private boolean mAllUsers;
+    private IBinder mCallback;
+
+    private volatile int mResultCode = -1;
+
+    /**
+     * If initView was called. We delay this call to not have to call it at all if the uninstall is
+     * quick
+     */
+    private boolean mIsViewInitialized;
+
+    /** Amount of time to wait until we show the UI */
+    private static final int QUICK_INSTALL_DELAY_MILLIS = 500;
+
+    private static final int UNINSTALL_COMPLETE = 1;
+    private static final int UNINSTALL_IS_SLOW = 2;
+
+    private Handler mHandler = new MessageHandler(this);
+
+    private static class MessageHandler extends Handler {
+        private final WeakReference<UninstallAppProgress> mActivity;
+
+        public MessageHandler(UninstallAppProgress activity) {
+            mActivity = new WeakReference<>(activity);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            UninstallAppProgress activity = mActivity.get();
+            if (activity != null) {
+                activity.handleMessage(msg);
+            }
+        }
+    }
+
+    private void handleMessage(Message msg) {
+        if (isFinishing() || isDestroyed()) {
+            return;
+        }
+
+        switch (msg.what) {
+            case UNINSTALL_IS_SLOW:
+                initView();
+                break;
+            case UNINSTALL_COMPLETE:
+                mHandler.removeMessages(UNINSTALL_IS_SLOW);
+
+                if (msg.arg1 != PackageManager.DELETE_SUCCEEDED) {
+                    initView();
+                }
+
+                mResultCode = msg.arg1;
+                final String packageName = (String) msg.obj;
+
+                if (mCallback != null) {
+                    final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub
+                            .asInterface(mCallback);
+                    try {
+                        observer.onPackageDeleted(mAppInfo.packageName, mResultCode,
+                                packageName);
+                    } catch (RemoteException ignored) {
+                    }
+                    finish();
+                    return;
+                }
+
+                if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
+                    Intent result = new Intent();
+                    result.putExtra(Intent.EXTRA_INSTALL_RESULT, mResultCode);
+                    setResult(mResultCode == PackageManager.DELETE_SUCCEEDED
+                            ? Activity.RESULT_OK : Activity.RESULT_FIRST_USER,
+                            result);
+                    finish();
+                    return;
+                }
+
+                // Update the status text
+                final String statusText;
+                switch (msg.arg1) {
+                    case PackageManager.DELETE_SUCCEEDED:
+                        statusText = getString(R.string.uninstall_done);
+                        // Show a Toast and finish the activity
+                        Context ctx = getBaseContext();
+                        Toast.makeText(ctx, statusText, Toast.LENGTH_LONG).show();
+                        setResultAndFinish();
+                        return;
+                    case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER: {
+                        UserManager userManager =
+                                (UserManager) getSystemService(Context.USER_SERVICE);
+                        IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
+                                ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+                        // Find out if the package is an active admin for some non-current user.
+                        int myUserId = UserHandle.myUserId();
+                        UserInfo otherBlockingUser = null;
+                        for (UserInfo user : userManager.getUsers()) {
+                            // We only catch the case when the user in question is neither the
+                            // current user nor its profile.
+                            if (isProfileOfOrSame(userManager, myUserId, user.id)) continue;
+
+                            try {
+                                if (dpm.packageHasActiveAdmins(packageName, user.id)) {
+                                    otherBlockingUser = user;
+                                    break;
+                                }
+                            } catch (RemoteException e) {
+                                Log.e(TAG, "Failed to talk to package manager", e);
+                            }
+                        }
+                        if (otherBlockingUser == null) {
+                            Log.d(TAG, "Uninstall failed because " + packageName
+                                    + " is a device admin");
+                            getProgressFragment().setDeviceManagerButtonVisible(true);
+                            statusText = getString(
+                                    R.string.uninstall_failed_device_policy_manager);
+                        } else {
+                            Log.d(TAG, "Uninstall failed because " + packageName
+                                    + " is a device admin of user " + otherBlockingUser);
+                            getProgressFragment().setDeviceManagerButtonVisible(false);
+                            statusText = String.format(
+                                    getString(R.string.uninstall_failed_device_policy_manager_of_user),
+                                    otherBlockingUser.name);
+                        }
+                        break;
+                    }
+                    case PackageManager.DELETE_FAILED_OWNER_BLOCKED: {
+                        UserManager userManager =
+                                (UserManager) getSystemService(Context.USER_SERVICE);
+                        IPackageManager packageManager = IPackageManager.Stub.asInterface(
+                                ServiceManager.getService("package"));
+                        List<UserInfo> users = userManager.getUsers();
+                        int blockingUserId = UserHandle.USER_NULL;
+                        for (int i = 0; i < users.size(); ++i) {
+                            final UserInfo user = users.get(i);
+                            try {
+                                if (packageManager.getBlockUninstallForUser(packageName,
+                                        user.id)) {
+                                    blockingUserId = user.id;
+                                    break;
+                                }
+                            } catch (RemoteException e) {
+                                // Shouldn't happen.
+                                Log.e(TAG, "Failed to talk to package manager", e);
+                            }
+                        }
+                        int myUserId = UserHandle.myUserId();
+                        if (isProfileOfOrSame(userManager, myUserId, blockingUserId)) {
+                            getProgressFragment().setDeviceManagerButtonVisible(true);
+                        } else {
+                            getProgressFragment().setDeviceManagerButtonVisible(false);
+                            getProgressFragment().setUsersButtonVisible(true);
+                        }
+                        // TODO: b/25442806
+                        if (blockingUserId == UserHandle.USER_SYSTEM) {
+                            statusText = getString(R.string.uninstall_blocked_device_owner);
+                        } else if (blockingUserId == UserHandle.USER_NULL) {
+                            Log.d(TAG, "Uninstall failed for " + packageName + " with code "
+                                    + msg.arg1 + " no blocking user");
+                            statusText = getString(R.string.uninstall_failed);
+                        } else {
+                            statusText = mAllUsers
+                                    ? getString(R.string.uninstall_all_blocked_profile_owner) :
+                                    getString(R.string.uninstall_blocked_profile_owner);
+                        }
+                        break;
+                    }
+                    default:
+                        Log.d(TAG, "Uninstall failed for " + packageName + " with code "
+                                + msg.arg1);
+                        statusText = getString(R.string.uninstall_failed);
+                        break;
+                }
+                getProgressFragment().showCompletion(statusText);
+                break;
+            default:
+                break;
+        }
+    }
+
+    private boolean isProfileOfOrSame(UserManager userManager, int userId, int profileId) {
+        if (userId == profileId) {
+            return true;
+        }
+        UserInfo parentUser = userManager.getProfileParent(profileId);
+        return parentUser != null && parentUser.id == userId;
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        Intent intent = getIntent();
+        mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+        mCallback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
+
+        // This currently does not support going through a onDestroy->onCreate cycle. Hence if that
+        // happened, just fail the operation for mysterious reasons.
+        if (icicle != null) {
+            mResultCode = PackageManager.DELETE_FAILED_INTERNAL_ERROR;
+
+            if (mCallback != null) {
+                final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub
+                        .asInterface(mCallback);
+                try {
+                    observer.onPackageDeleted(mAppInfo.packageName, mResultCode, null);
+                } catch (RemoteException ignored) {
+                }
+                finish();
+            } else {
+                setResultAndFinish();
+            }
+
+            return;
+        }
+
+        mAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
+        UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+        if (user == null) {
+            user = android.os.Process.myUserHandle();
+        }
+
+        PackageDeleteObserver observer = new PackageDeleteObserver();
+
+        // Make window transparent until initView is called. In many cases we can avoid showing the
+        // UI at all as the app is uninstalled very quickly. If we show the UI and instantly remove
+        // it, it just looks like a flicker.
+        getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+        getWindow().setStatusBarColor(Color.TRANSPARENT);
+        getWindow().setNavigationBarColor(Color.TRANSPARENT);
+
+        try {
+            getPackageManager().deletePackageAsUser(mAppInfo.packageName, observer,
+                    mAllUsers ? PackageManager.DELETE_ALL_USERS : 0, user.getIdentifier());
+        } catch (IllegalArgumentException e) {
+            // Couldn't find the package, no need to call uninstall.
+            Log.w(TAG, "Could not find package, not deleting " + mAppInfo.packageName, e);
+        }
+
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(UNINSTALL_IS_SLOW),
+                QUICK_INSTALL_DELAY_MILLIS);
+    }
+
+    public ApplicationInfo getAppInfo() {
+        return mAppInfo;
+    }
+
+    private class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
+        public void packageDeleted(String packageName, int returnCode) {
+            Message msg = mHandler.obtainMessage(UNINSTALL_COMPLETE);
+            msg.arg1 = returnCode;
+            msg.obj = packageName;
+            mHandler.sendMessage(msg);
+        }
+    }
+
+    public void setResultAndFinish() {
+        setResult(mResultCode);
+        finish();
+    }
+
+    private void initView() {
+        if (mIsViewInitialized) {
+            return;
+        }
+        mIsViewInitialized = true;
+
+        // We set the window background to translucent in constructor, revert this
+        TypedValue attribute = new TypedValue();
+        getTheme().resolveAttribute(android.R.attr.windowBackground, attribute, true);
+        if (attribute.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
+                attribute.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+            getWindow().setBackgroundDrawable(new ColorDrawable(attribute.data));
+        } else {
+            getWindow().setBackgroundDrawable(getResources().getDrawable(attribute.resourceId,
+                    getTheme()));
+        }
+
+        getTheme().resolveAttribute(android.R.attr.navigationBarColor, attribute, true);
+        getWindow().setNavigationBarColor(attribute.data);
+
+        getTheme().resolveAttribute(android.R.attr.statusBarColor, attribute, true);
+        getWindow().setStatusBarColor(attribute.data);
+
+        boolean isUpdate = ((mAppInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+        setTitle(isUpdate ? R.string.uninstall_update_title : R.string.uninstall_application_title);
+
+        getFragmentManager().beginTransaction()
+                .add(android.R.id.content, new UninstallAppProgressFragment(), FRAGMENT_TAG)
+                .commitNowAllowingStateLoss();
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent ev) {
+        if (ev.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+            if (mResultCode == -1) {
+                // Ignore back key when installation is in progress
+                return true;
+            } else {
+                // If installation is done, just set the result code
+                setResult(mResultCode);
+            }
+        }
+        return super.dispatchKeyEvent(ev);
+    }
+
+    private ProgressFragment getProgressFragment() {
+        return (ProgressFragment) getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
+    }
+
+    public interface ProgressFragment {
+        void setUsersButtonVisible(boolean visible);
+        void setDeviceManagerButtonVisible(boolean visible);
+        void showCompletion(CharSequence statusText);
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgressFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgressFragment.java
new file mode 100644
index 0000000..af6d9c5
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgressFragment.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.television;
+
+import android.annotation.Nullable;
+import android.app.Fragment;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.packageinstaller.PackageUtil;
+import com.android.packageinstaller.R;
+
+public class UninstallAppProgressFragment extends Fragment implements View.OnClickListener,
+        UninstallAppProgress.ProgressFragment {
+    private static final String TAG = "UninstallAppProgressF"; // full class name is too long
+
+    private Button mOkButton;
+    private Button mDeviceManagerButton;
+    private Button mUsersButton;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+            Bundle savedInstanceState) {
+        final View root = inflater.inflate(R.layout.uninstall_progress, container, false);
+        // Initialize views
+        View snippetView = root.findViewById(R.id.app_snippet);
+        PackageUtil.initSnippetForInstalledApp(getContext(),
+                ((UninstallAppProgress)getActivity()).getAppInfo(), snippetView);
+        mDeviceManagerButton = (Button) root.findViewById(R.id.device_manager_button);
+        mUsersButton = (Button) root.findViewById(R.id.users_button);
+        mDeviceManagerButton.setVisibility(View.GONE);
+        mDeviceManagerButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent intent = new Intent();
+                intent.setClassName("com.android.settings",
+                        "com.android.settings.Settings$DeviceAdminSettingsActivity");
+                intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
+                startActivity(intent);
+                getActivity().finish();
+            }
+        });
+        mUsersButton.setVisibility(View.GONE);
+        mUsersButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent intent = new Intent(Settings.ACTION_USER_SETTINGS);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
+                startActivity(intent);
+                getActivity().finish();
+            }
+        });
+        // Hide button till progress is being displayed
+        mOkButton = (Button) root.findViewById(R.id.ok_button);
+        mOkButton.setOnClickListener(this);
+
+        return root;
+    }
+
+    public void onClick(View v) {
+        final UninstallAppProgress activity = (UninstallAppProgress) getActivity();
+        if(v == mOkButton && activity != null) {
+            Log.i(TAG, "Finished uninstalling pkg: " +
+                    activity.getAppInfo().packageName);
+            activity.setResultAndFinish();
+        }
+    }
+
+    @Override
+    public void setUsersButtonVisible(boolean visible) {
+        mUsersButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public void setDeviceManagerButtonVisible(boolean visible) {
+        mDeviceManagerButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public void showCompletion(CharSequence statusText) {
+        final View root = getView();
+        root.findViewById(R.id.progress_view).setVisibility(View.GONE);
+        root.findViewById(R.id.status_view).setVisibility(View.VISIBLE);
+        ((TextView) root.findViewById(R.id.status_text)).setText(statusText);
+        root.findViewById(R.id.ok_panel).setVisibility(View.VISIBLE);
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallTask.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallTask.java
new file mode 100644
index 0000000..53a460d
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallTask.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.wear;
+
+import android.content.Context;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Task that installs an APK. This must not be called on the main thread.
+ * This code is based off the Finsky/Wearsky implementation
+ */
+public class InstallTask {
+    private static final String TAG = "InstallTask";
+
+    private static final int DEFAULT_BUFFER_SIZE = 8192;
+
+    private final Context mContext;
+    private String mPackageName;
+    private ParcelFileDescriptor mParcelFileDescriptor;
+    private PackageInstallerImpl.InstallListener mCallback;
+    private PackageInstaller.Session mSession;
+    private IntentSender mCommitCallback;
+
+    private Exception mException = null;
+    private int mErrorCode = 0;
+    private String mErrorDesc = null;
+
+    public InstallTask(Context context, String packageName,
+            ParcelFileDescriptor parcelFileDescriptor,
+            PackageInstallerImpl.InstallListener callback, PackageInstaller.Session session,
+            IntentSender commitCallback) {
+        mContext = context;
+        mPackageName = packageName;
+        mParcelFileDescriptor = parcelFileDescriptor;
+        mCallback = callback;
+        mSession = session;
+        mCommitCallback = commitCallback;
+    }
+
+    public boolean isError() {
+        return mErrorCode != InstallerConstants.STATUS_SUCCESS || !TextUtils.isEmpty(mErrorDesc);
+    }
+
+    public void execute() {
+        if (Looper.myLooper() == Looper.getMainLooper()) {
+            throw new IllegalStateException("This method cannot be called from the UI thread.");
+        }
+
+        OutputStream sessionStream = null;
+        try {
+            sessionStream = mSession.openWrite(mPackageName, 0, -1);
+
+            // 2b: Stream the asset to the installer. Note:
+            // Note: writeToOutputStreamFromAsset() always safely closes the input stream
+            writeToOutputStreamFromAsset(sessionStream);
+            mSession.fsync(sessionStream);
+        } catch (Exception e) {
+            mException = e;
+            mErrorCode = InstallerConstants.ERROR_INSTALL_COPY_STREAM;
+            mErrorDesc = "Could not write to stream";
+        } finally {
+            if (sessionStream != null) {
+                // 2c: close output stream
+                try {
+                    sessionStream.close();
+                } catch (Exception e) {
+                    // Ignore otherwise
+                    if (mException == null) {
+                        mException = e;
+                        mErrorCode = InstallerConstants.ERROR_INSTALL_CLOSE_STREAM;
+                        mErrorDesc = "Could not close session stream";
+                    }
+                }
+            }
+        }
+
+        if (mErrorCode != InstallerConstants.STATUS_SUCCESS) {
+            // An error occurred, we're done
+            Log.e(TAG, "Exception while installing " + mPackageName + ": " + mErrorCode + ", "
+                    + mErrorDesc + ", " + mException);
+            mSession.close();
+            mCallback.installFailed(mErrorCode, "[" + mPackageName + "]" + mErrorDesc);
+        } else {
+            // 3. Commit the session (this actually installs it.)  Session map
+            // will be cleaned up in the callback.
+            mCallback.installBeginning();
+            mSession.commit(mCommitCallback);
+            mSession.close();
+        }
+    }
+
+    /**
+     * {@code PackageInstaller} works with streams. Get the {@code FileDescriptor}
+     * corresponding to the {@code Asset} and then write the contents into an
+     * {@code OutputStream} that is passed in.
+     * <br>
+     * The {@code FileDescriptor} is closed but the {@code OutputStream} is not closed.
+     */
+    private boolean writeToOutputStreamFromAsset(OutputStream outputStream) {
+        if (outputStream == null) {
+            mErrorCode = InstallerConstants.ERROR_INSTALL_COPY_STREAM_EXCEPTION;
+            mErrorDesc = "Got a null OutputStream.";
+            return false;
+        }
+
+        if (mParcelFileDescriptor == null || mParcelFileDescriptor.getFileDescriptor() == null)  {
+            mErrorCode = InstallerConstants.ERROR_COULD_NOT_GET_FD;
+            mErrorDesc = "Could not get FD";
+            return false;
+        }
+
+        InputStream inputStream = null;
+        try {
+            byte[] inputBuf = new byte[DEFAULT_BUFFER_SIZE];
+            int bytesRead;
+            inputStream = new ParcelFileDescriptor.AutoCloseInputStream(mParcelFileDescriptor);
+
+            while ((bytesRead = inputStream.read(inputBuf)) > -1) {
+                if (bytesRead > 0) {
+                    outputStream.write(inputBuf, 0, bytesRead);
+                }
+            }
+
+            outputStream.flush();
+        } catch (IOException e) {
+            mErrorCode = InstallerConstants.ERROR_INSTALL_APK_COPY_FAILURE;
+            mErrorDesc = "Reading from Asset FD or writing to temp file failed: " + e;
+            return false;
+        } finally {
+            safeClose(inputStream);
+        }
+
+        return true;
+    }
+
+    /**
+     * Quietly close a closeable resource (e.g. a stream or file). The input may already
+     * be closed and it may even be null.
+     */
+    public static void safeClose(Closeable resource) {
+        if (resource != null) {
+            try {
+                resource.close();
+            } catch (IOException ioe) {
+                // Catch and discard the error
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallerConstants.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallerConstants.java
new file mode 100644
index 0000000..3daf3d8
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallerConstants.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.wear;
+
+/**
+ * Constants for Installation / Uninstallation requests.
+ * Using the same values as Finsky/Wearsky code for consistency in user analytics of failures
+ */
+public class InstallerConstants {
+    /** Request succeeded */
+    public static final int STATUS_SUCCESS = 0;
+
+    /**
+     * The new PackageInstaller also returns a small set of less granular error codes, which
+     * we'll remap to the range -500 and below to keep away from existing installer codes
+     * (which run from -1 to -110).
+     */
+    public final static int ERROR_PACKAGEINSTALLER_BASE = -500;
+
+    public static final int ERROR_COULD_NOT_GET_FD = -603;
+    /** This node is not targeted by this request. */
+
+    /** The install did not complete because could not create PackageInstaller session */
+    public final static int ERROR_INSTALL_CREATE_SESSION = -612;
+    /** The install did not complete because could not open PackageInstaller session  */
+    public final static int ERROR_INSTALL_OPEN_SESSION = -613;
+    /** The install did not complete because could not open PackageInstaller output stream */
+    public final static int ERROR_INSTALL_OPEN_STREAM = -614;
+    /** The install did not complete because of an exception while streaming bytes */
+    public final static int ERROR_INSTALL_COPY_STREAM_EXCEPTION = -615;
+    /** The install did not complete because of an unexpected exception from PackageInstaller */
+    public final static int ERROR_INSTALL_SESSION_EXCEPTION = -616;
+    /** The install did not complete because of an unexpected userActionRequired callback */
+    public final static int ERROR_INSTALL_USER_ACTION_REQUIRED = -617;
+    /** The install did not complete because of an unexpected broadcast (missing fields) */
+    public final static int ERROR_INSTALL_MALFORMED_BROADCAST = -618;
+    /** The install did not complete because of an error while copying from downloaded file */
+    public final static int ERROR_INSTALL_APK_COPY_FAILURE = -619;
+    /** The install did not complete because of an error while copying to the PackageInstaller
+     * output stream */
+    public final static int ERROR_INSTALL_COPY_STREAM = -620;
+    /** The install did not complete because of an error while closing the PackageInstaller
+     * output stream */
+    public final static int ERROR_INSTALL_CLOSE_STREAM = -621;
+}
\ No newline at end of file
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerFactory.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerFactory.java
new file mode 100644
index 0000000..bdc22cf
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerFactory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.wear;
+
+import android.content.Context;
+
+/**
+ * Factory that creates a Package Installer.
+ */
+public class PackageInstallerFactory {
+    private static PackageInstallerImpl sPackageInstaller;
+
+    /**
+     * Return the PackageInstaller shared object. {@code init} should have already been called.
+     */
+    public synchronized static PackageInstallerImpl getPackageInstaller(Context context) {
+        if (sPackageInstaller == null) {
+            sPackageInstaller = new PackageInstallerImpl(context);
+        }
+        return sPackageInstaller;
+    }
+}
\ No newline at end of file
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
new file mode 100644
index 0000000..bf4b03c
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.wear;
+
+import android.annotation.TargetApi;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of package manager installation using modern PackageInstaller api.
+ *
+ * Heavily copied from Wearsky/Finsky implementation
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class PackageInstallerImpl {
+    private static final String TAG = "PackageInstallerImpl";
+
+    /** Intent actions used for broadcasts from PackageInstaller back to the local receiver */
+    private static final String ACTION_INSTALL_COMMIT =
+            "com.android.vending.INTENT_PACKAGE_INSTALL_COMMIT";
+
+    private final Context mContext;
+    private final PackageInstaller mPackageInstaller;
+    private final Map<String, PackageInstaller.SessionInfo> mSessionInfoMap;
+    private final Map<String, PackageInstaller.Session> mOpenSessionMap;
+
+    public PackageInstallerImpl(Context context) {
+        mContext = context.getApplicationContext();
+        mPackageInstaller = mContext.getPackageManager().getPackageInstaller();
+
+        // Capture a map of known sessions
+        // This list will be pruned a bit later (stale sessions will be canceled)
+        mSessionInfoMap = new HashMap<String, PackageInstaller.SessionInfo>();
+        List<PackageInstaller.SessionInfo> mySessions = mPackageInstaller.getMySessions();
+        for (int i = 0; i < mySessions.size(); i++) {
+            PackageInstaller.SessionInfo sessionInfo = mySessions.get(i);
+            String packageName = sessionInfo.getAppPackageName();
+            PackageInstaller.SessionInfo oldInfo = mSessionInfoMap.put(packageName, sessionInfo);
+
+            // Checking for old info is strictly for logging purposes
+            if (oldInfo != null) {
+                Log.w(TAG, "Multiple sessions for " + packageName + " found. Removing " + oldInfo
+                        .getSessionId() + " & keeping " + mySessions.get(i).getSessionId());
+            }
+        }
+        mOpenSessionMap = new HashMap<String, PackageInstaller.Session>();
+    }
+
+    /**
+     * This callback will be made after an installation attempt succeeds or fails.
+     */
+    public interface InstallListener {
+        /**
+         * This callback signals that preflight checks have succeeded and installation
+         * is beginning.
+         */
+        void installBeginning();
+
+        /**
+         * This callback signals that installation has completed.
+         */
+        void installSucceeded();
+
+        /**
+         * This callback signals that installation has failed.
+         */
+        void installFailed(int errorCode, String errorDesc);
+    }
+
+    /**
+     * This is a placeholder implementation that bundles an entire "session" into a single
+     * call. This will be replaced by more granular versions that allow longer session lifetimes,
+     * download progress tracking, etc.
+     *
+     * This must not be called on main thread.
+     */
+    public void install(final String packageName, ParcelFileDescriptor parcelFileDescriptor,
+            final InstallListener callback) {
+        // 0. Generic try/catch block because I am not really sure what exceptions (other than
+        // IOException) might be thrown by PackageInstaller and I want to handle them
+        // at least slightly gracefully.
+        try {
+            // 1. Create or recover a session, and open it
+            // Try recovery first
+            PackageInstaller.Session session = null;
+            PackageInstaller.SessionInfo sessionInfo = mSessionInfoMap.get(packageName);
+            if (sessionInfo != null) {
+                // See if it's openable, or already held open
+                session = getSession(packageName);
+            }
+            // If open failed, or there was no session, create a new one and open it.
+            // If we cannot create or open here, the failure is terminal.
+            if (session == null) {
+                try {
+                    innerCreateSession(packageName);
+                } catch (IOException ioe) {
+                    Log.e(TAG, "Can't create session for " + packageName + ": " + ioe.getMessage());
+                    callback.installFailed(InstallerConstants.ERROR_INSTALL_CREATE_SESSION,
+                            "Could not create session");
+                    mSessionInfoMap.remove(packageName);
+                    return;
+                }
+                sessionInfo = mSessionInfoMap.get(packageName);
+                try {
+                    session = mPackageInstaller.openSession(sessionInfo.getSessionId());
+                    mOpenSessionMap.put(packageName, session);
+                } catch (SecurityException se) {
+                    Log.e(TAG, "Can't open session for " + packageName + ": " + se.getMessage());
+                    callback.installFailed(InstallerConstants.ERROR_INSTALL_OPEN_SESSION,
+                            "Can't open session");
+                    mSessionInfoMap.remove(packageName);
+                    return;
+                }
+            }
+
+            // 2. Launch task to handle file operations.
+            InstallTask task = new InstallTask( mContext, packageName, parcelFileDescriptor,
+                    callback, session,
+                    getCommitCallback(packageName, sessionInfo.getSessionId(), callback));
+            task.execute();
+            if (task.isError()) {
+                cancelSession(sessionInfo.getSessionId(), packageName);
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Unexpected exception while installing: " + packageName + ": "
+                    + e.getMessage());
+            callback.installFailed(InstallerConstants.ERROR_INSTALL_SESSION_EXCEPTION,
+                    "Unexpected exception while installing " + packageName);
+        }
+    }
+
+    /**
+     * Retrieve an existing session. Will open if needed, but does not attempt to create.
+     */
+    private PackageInstaller.Session getSession(String packageName) {
+        // Check for already-open session
+        PackageInstaller.Session session = mOpenSessionMap.get(packageName);
+        if (session != null) {
+            try {
+                // Probe the session to ensure that it's still open. This may or may not
+                // throw (if non-open), but it may serve as a canary for stale sessions.
+                session.getNames();
+                return session;
+            } catch (IOException ioe) {
+                Log.e(TAG, "Stale open session for " + packageName + ": " + ioe.getMessage());
+                mOpenSessionMap.remove(packageName);
+            } catch (SecurityException se) {
+                Log.e(TAG, "Stale open session for " + packageName + ": " + se.getMessage());
+                mOpenSessionMap.remove(packageName);
+            }
+        }
+        // Check to see if this is a known session
+        PackageInstaller.SessionInfo sessionInfo = mSessionInfoMap.get(packageName);
+        if (sessionInfo == null) {
+            return null;
+        }
+        // Try to open it. If we fail here, assume that the SessionInfo was stale.
+        try {
+            session = mPackageInstaller.openSession(sessionInfo.getSessionId());
+        } catch (SecurityException se) {
+            Log.w(TAG, "SessionInfo was stale for " + packageName + " - deleting info");
+            mSessionInfoMap.remove(packageName);
+            return null;
+        } catch (IOException ioe) {
+            Log.w(TAG, "IOException opening old session for " + ioe.getMessage()
+                    + " - deleting info");
+            mSessionInfoMap.remove(packageName);
+            return null;
+        }
+        mOpenSessionMap.put(packageName, session);
+        return session;
+    }
+
+    /** This version throws an IOException when the session cannot be created */
+    private void innerCreateSession(String packageName) throws IOException {
+        if (mSessionInfoMap.containsKey(packageName)) {
+            Log.w(TAG, "Creating session for " + packageName + " when one already exists");
+            return;
+        }
+        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+        params.setAppPackageName(packageName);
+
+        // IOException may be thrown at this point
+        int sessionId = mPackageInstaller.createSession(params);
+        PackageInstaller.SessionInfo sessionInfo = mPackageInstaller.getSessionInfo(sessionId);
+        mSessionInfoMap.put(packageName, sessionInfo);
+    }
+
+    /**
+     * Cancel a session based on its sessionId. Package name is for logging only.
+     */
+    private void cancelSession(int sessionId, String packageName) {
+        // Close if currently held open
+        closeSession(packageName);
+        // Remove local record
+        mSessionInfoMap.remove(packageName);
+        try {
+            mPackageInstaller.abandonSession(sessionId);
+        } catch (SecurityException se) {
+            // The session no longer exists, so we can exit quietly.
+            return;
+        }
+    }
+
+    /**
+     * Close a session if it happens to be held open.
+     */
+    private void closeSession(String packageName) {
+        PackageInstaller.Session session = mOpenSessionMap.remove(packageName);
+        if (session != null) {
+            // Unfortunately close() is not idempotent. Try our best to make this safe.
+            try {
+                session.close();
+            } catch (Exception e) {
+                Log.w(TAG, "Unexpected error closing session for " + packageName + ": "
+                        + e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Creates a commit callback for the package install that's underway. This will be called
+     * some time after calling session.commit() (above).
+     */
+    private IntentSender getCommitCallback(final String packageName, final int sessionId,
+            final InstallListener callback) {
+        // Create a single-use broadcast receiver
+        BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                mContext.unregisterReceiver(this);
+                handleCommitCallback(intent, packageName, sessionId, callback);
+            }
+        };
+        // Create a matching intent-filter and register the receiver
+        String action = ACTION_INSTALL_COMMIT + "." + packageName;
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(action);
+        mContext.registerReceiver(broadcastReceiver, intentFilter);
+
+        // Create a matching PendingIntent and use it to generate the IntentSender
+        Intent broadcastIntent = new Intent(action);
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, packageName.hashCode(),
+                broadcastIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
+        return pendingIntent.getIntentSender();
+    }
+
+    /**
+     * Examine the extras to determine information about the package update/install, decode
+     * the result, and call the appropriate callback.
+     *
+     * @param intent The intent, which the PackageInstaller will have added Extras to
+     * @param packageName The package name we created the receiver for
+     * @param sessionId The session Id we created the receiver for
+     * @param callback The callback to report success/failure to
+     */
+    private void handleCommitCallback(Intent intent, String packageName, int sessionId,
+            InstallListener callback) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Installation of " + packageName + " finished with extras "
+                    + intent.getExtras());
+        }
+        String statusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+        int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, Integer.MIN_VALUE);
+        if (status == PackageInstaller.STATUS_SUCCESS) {
+            cancelSession(sessionId, packageName);
+            callback.installSucceeded();
+        } else if (status == -1 /*PackageInstaller.STATUS_USER_ACTION_REQUIRED*/) {
+            // TODO - use the constant when the correct/final name is in the SDK
+            // TODO This is unexpected, so we are treating as failure for now
+            cancelSession(sessionId, packageName);
+            callback.installFailed(InstallerConstants.ERROR_INSTALL_USER_ACTION_REQUIRED,
+                    "Unexpected: user action required");
+        } else {
+            cancelSession(sessionId, packageName);
+            int errorCode = getPackageManagerErrorCode(status);
+            Log.e(TAG, "Error " + errorCode + " while installing " + packageName + ": "
+                    + statusMessage);
+            callback.installFailed(errorCode, null);
+        }
+    }
+
+    private int getPackageManagerErrorCode(int status) {
+        // This is a hack: because PackageInstaller now reports error codes
+        // with small positive values, we need to remap them into a space
+        // that is more compatible with the existing package manager error codes.
+        // See https://sites.google.com/a/google.com/universal-store/documentation
+        //       /android-client/download-error-codes
+        int errorCode;
+        if (status == Integer.MIN_VALUE) {
+            errorCode = InstallerConstants.ERROR_INSTALL_MALFORMED_BROADCAST;
+        } else {
+            errorCode = InstallerConstants.ERROR_PACKAGEINSTALLER_BASE - status;
+        }
+        return errorCode;
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageArgs.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageArgs.java
new file mode 100644
index 0000000..2c289b2
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageArgs.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.wear;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * Installation Util that contains a list of parameters that are needed for
+ * installing/uninstalling.
+ */
+public class WearPackageArgs {
+    private static final String KEY_PACKAGE_NAME =
+            "com.google.android.clockwork.EXTRA_PACKAGE_NAME";
+    private static final String KEY_ASSET_URI =
+            "com.google.android.clockwork.EXTRA_ASSET_URI";
+    private static final String KEY_START_ID =
+            "com.google.android.clockwork.EXTRA_START_ID";
+    private static final String KEY_PERM_URI =
+            "com.google.android.clockwork.EXTRA_PERM_URI";
+    private static final String KEY_CHECK_PERMS =
+            "com.google.android.clockwork.EXTRA_CHECK_PERMS";
+    private static final String KEY_SKIP_IF_SAME_VERSION =
+            "com.google.android.clockwork.EXTRA_SKIP_IF_SAME_VERSION";
+    private static final String KEY_COMPRESSION_ALG =
+            "com.google.android.clockwork.EXTRA_KEY_COMPRESSION_ALG";
+    private static final String KEY_COMPANION_SDK_VERSION =
+            "com.google.android.clockwork.EXTRA_KEY_COMPANION_SDK_VERSION";
+    private static final String KEY_COMPANION_DEVICE_VERSION =
+            "com.google.android.clockwork.EXTRA_KEY_COMPANION_DEVICE_VERSION";
+    private static final String KEY_SHOULD_CHECK_GMS_DEPENDENCY =
+            "com.google.android.clockwork.EXTRA_KEY_SHOULD_CHECK_GMS_DEPENDENCY";
+    private static final String KEY_SKIP_IF_LOWER_VERSION =
+            "com.google.android.clockwork.EXTRA_SKIP_IF_LOWER_VERSION";
+
+    public static String getPackageName(Bundle b) {
+        return b.getString(KEY_PACKAGE_NAME);
+    }
+
+    public static Bundle setPackageName(Bundle b, String packageName) {
+        b.putString(KEY_PACKAGE_NAME, packageName);
+        return b;
+    }
+
+    public static Uri getAssetUri(Bundle b) {
+        return b.getParcelable(KEY_ASSET_URI);
+    }
+
+    public static Uri getPermUri(Bundle b) {
+        return b.getParcelable(KEY_PERM_URI);
+    }
+
+    public static boolean checkPerms(Bundle b) {
+        return b.getBoolean(KEY_CHECK_PERMS);
+    }
+
+    public static boolean skipIfSameVersion(Bundle b) {
+        return b.getBoolean(KEY_SKIP_IF_SAME_VERSION);
+    }
+
+    public static int getCompanionSdkVersion(Bundle b) {
+        return b.getInt(KEY_COMPANION_SDK_VERSION);
+    }
+
+    public static int getCompanionDeviceVersion(Bundle b) {
+        return b.getInt(KEY_COMPANION_DEVICE_VERSION);
+    }
+
+    public static String getCompressionAlg(Bundle b) {
+        return b.getString(KEY_COMPRESSION_ALG);
+    }
+
+    public static int getStartId(Bundle b) {
+        return b.getInt(KEY_START_ID);
+    }
+
+    public static boolean skipIfLowerVersion(Bundle b) {
+        return b.getBoolean(KEY_SKIP_IF_LOWER_VERSION, false);
+    }
+
+    public static Bundle setStartId(Bundle b, int startId) {
+        b.putInt(KEY_START_ID, startId);
+        return b;
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageIconProvider.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageIconProvider.java
new file mode 100644
index 0000000..02b9d29
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageIconProvider.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.wear;
+
+import android.annotation.TargetApi;
+import android.app.ActivityManager;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.List;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+public class WearPackageIconProvider extends ContentProvider {
+    private static final String TAG = "WearPackageIconProvider";
+    public static final String AUTHORITY = "com.google.android.packageinstaller.wear.provider";
+
+    private static final String REQUIRED_PERMISSION =
+            "com.google.android.permission.INSTALL_WEARABLE_PACKAGES";
+
+    /** MIME types. */
+    public static final String ICON_TYPE = "vnd.android.cursor.item/cw_package_icon";
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        throw new UnsupportedOperationException("Query is not supported.");
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        if (uri == null) {
+            throw new IllegalArgumentException("URI passed in is null.");
+        }
+
+        if (AUTHORITY.equals(uri.getEncodedAuthority())) {
+            return ICON_TYPE;
+        }
+        return null;
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        throw new UnsupportedOperationException("Insert is not supported.");
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        if (uri == null) {
+            throw new IllegalArgumentException("URI passed in is null.");
+        }
+
+        enforcePermissions(uri);
+
+        if (ICON_TYPE.equals(getType(uri))) {
+            final File file = WearPackageUtil.getIconFile(
+                    this.getContext().getApplicationContext(), getPackageNameFromUri(uri));
+            if (file != null) {
+                file.delete();
+            }
+        }
+
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("Update is not supported.");
+    }
+
+    @Override
+    public ParcelFileDescriptor openFile(
+            Uri uri, @SuppressWarnings("unused") String mode) throws FileNotFoundException {
+        if (uri == null) {
+            throw new IllegalArgumentException("URI passed in is null.");
+        }
+
+        enforcePermissions(uri);
+
+        if (ICON_TYPE.equals(getType(uri))) {
+            final File file = WearPackageUtil.getIconFile(
+                    this.getContext().getApplicationContext(), getPackageNameFromUri(uri));
+            if (file != null) {
+                return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+            }
+        }
+        return null;
+    }
+
+    public static Uri getUriForPackage(final String packageName) {
+        return Uri.parse("content://" + AUTHORITY + "/icons/" + packageName + ".icon");
+    }
+
+    private String getPackageNameFromUri(Uri uri) {
+        if (uri == null) {
+            return null;
+        }
+        List<String> pathSegments = uri.getPathSegments();
+        String packageName = pathSegments.get(pathSegments.size() - 1);
+
+        if (packageName.endsWith(".icon")) {
+            packageName = packageName.substring(0, packageName.lastIndexOf("."));
+        }
+        return packageName;
+    }
+
+    /**
+     * Make sure the calling app is either a system app or the same app or has the right permission.
+     * @throws SecurityException if the caller has insufficient permissions.
+     */
+    @TargetApi(Build.VERSION_CODES.BASE_1_1)
+    private void enforcePermissions(Uri uri) {
+        // Redo some of the permission check in {@link ContentProvider}. Just add an extra check to
+        // allow System process to access this provider.
+        Context context = getContext();
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        final int myUid = android.os.Process.myUid();
+
+        if (uid == myUid || isSystemApp(context, pid)) {
+            return;
+        }
+
+        if (context.checkPermission(REQUIRED_PERMISSION, pid, uid) == PERMISSION_GRANTED) {
+            return;
+        }
+
+        // last chance, check against any uri grants
+        if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+                == PERMISSION_GRANTED) {
+            return;
+        }
+
+        throw new SecurityException("Permission Denial: reading "
+                + getClass().getName() + " uri " + uri + " from pid=" + pid
+                + ", uid=" + uid);
+    }
+
+    /**
+     * From the pid of the calling process, figure out whether this is a system app or not. We do
+     * this by checking the application information corresponding to the pid and then checking if
+     * FLAG_SYSTEM is set.
+     */
+    @TargetApi(Build.VERSION_CODES.CUPCAKE)
+    private boolean isSystemApp(Context context, int pid) {
+        // Get the Activity Manager Object
+        ActivityManager aManager =
+                (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+        // Get the list of running Applications
+        List<ActivityManager.RunningAppProcessInfo> rapInfoList =
+                aManager.getRunningAppProcesses();
+        for (ActivityManager.RunningAppProcessInfo rapInfo : rapInfoList) {
+            if (rapInfo.pid == pid) {
+                try {
+                    PackageInfo pkgInfo = context.getPackageManager().getPackageInfo(
+                            rapInfo.pkgList[0], 0);
+                    if (pkgInfo != null && pkgInfo.applicationInfo != null &&
+                            (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                        Log.d(TAG, pid + " is a system app.");
+                        return true;
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.e(TAG, "Could not find package information.", e);
+                    return false;
+                }
+            }
+        }
+        return false;
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java
new file mode 100644
index 0000000..e5f7613
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.wear;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.FeatureInfo;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.Process;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.packageinstaller.DeviceUtils;
+import com.android.packageinstaller.PackageUtil;
+import com.android.packageinstaller.R;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Service that will install/uninstall packages. It will check for permissions and features as well.
+ *
+ * -----------
+ *
+ * Debugging information:
+ *
+ *  Install Action example:
+ *  adb shell am startservice -a com.android.packageinstaller.wear.INSTALL_PACKAGE \
+ *     -d package://com.google.android.gms \
+ *     --eu com.google.android.clockwork.EXTRA_ASSET_URI content://com.google.android.clockwork.home.provider/host/com.google.android.wearable.app/wearable/com.google.android.gms/apk \
+ *     --es android.intent.extra.INSTALLER_PACKAGE_NAME com.google.android.gms \
+ *     --ez com.google.android.clockwork.EXTRA_CHECK_PERMS false \
+ *     --eu com.google.android.clockwork.EXTRA_PERM_URI content://com.google.android.clockwork.home.provider/host/com.google.android.wearable.app/permissions \
+ *     com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
+ *
+ *  Uninstall Action example:
+ *  adb shell am startservice -a com.android.packageinstaller.wear.UNINSTALL_PACKAGE \
+ *     -d package://com.google.android.gms \
+ *     com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
+ *
+ *  Retry GMS:
+ *  adb shell am startservice -a com.android.packageinstaller.wear.RETRY_GMS \
+ *     com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
+ */
+public class WearPackageInstallerService extends Service {
+    private static final String TAG = "WearPkgInstallerService";
+
+    private static final String WEAR_APPS_CHANNEL = "wear_app_install_uninstall";
+
+    private final int START_INSTALL = 1;
+    private final int START_UNINSTALL = 2;
+
+    private int mInstallNotificationId = 1;
+    private final Map<String, Integer> mNotifIdMap = new ArrayMap<>();
+
+    private final class ServiceHandler extends Handler {
+        public ServiceHandler(Looper looper) {
+            super(looper);
+        }
+
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case START_INSTALL:
+                    installPackage(msg.getData());
+                    break;
+                case START_UNINSTALL:
+                    uninstallPackage(msg.getData());
+                    break;
+            }
+        }
+    }
+    private ServiceHandler mServiceHandler;
+    private NotificationChannel mNotificationChannel;
+    private static volatile PowerManager.WakeLock lockStatic = null;
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        HandlerThread thread = new HandlerThread("PackageInstallerThread",
+                Process.THREAD_PRIORITY_BACKGROUND);
+        thread.start();
+
+        mServiceHandler = new ServiceHandler(thread.getLooper());
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (!DeviceUtils.isWear(this)) {
+            Log.w(TAG, "Not running on wearable.");
+            finishServiceEarly(startId);
+            return START_NOT_STICKY;
+        }
+
+        if (intent == null) {
+            Log.w(TAG, "Got null intent.");
+            finishServiceEarly(startId);
+            return START_NOT_STICKY;
+        }
+
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Got install/uninstall request " + intent);
+        }
+
+        Uri packageUri = intent.getData();
+        if (packageUri == null) {
+            Log.e(TAG, "No package URI in intent");
+            finishServiceEarly(startId);
+            return START_NOT_STICKY;
+        }
+
+        final String packageName = WearPackageUtil.getSanitizedPackageName(packageUri);
+        if (packageName == null) {
+            Log.e(TAG, "Invalid package name in URI (expected package:<pkgName>): " + packageUri);
+            finishServiceEarly(startId);
+            return START_NOT_STICKY;
+        }
+
+        PowerManager.WakeLock lock = getLock(this.getApplicationContext());
+        if (!lock.isHeld()) {
+            lock.acquire();
+        }
+
+        Bundle intentBundle = intent.getExtras();
+        if (intentBundle == null) {
+            intentBundle = new Bundle();
+        }
+        WearPackageArgs.setStartId(intentBundle, startId);
+        WearPackageArgs.setPackageName(intentBundle, packageName);
+        Message msg;
+        String notifTitle;
+        if (Intent.ACTION_INSTALL_PACKAGE.equals(intent.getAction())) {
+            msg = mServiceHandler.obtainMessage(START_INSTALL);
+            notifTitle = getString(R.string.installing);
+        } else if (Intent.ACTION_UNINSTALL_PACKAGE.equals(intent.getAction())) {
+            msg = mServiceHandler.obtainMessage(START_UNINSTALL);
+            notifTitle = getString(R.string.uninstalling);
+        } else {
+            Log.e(TAG, "Unknown action : " + intent.getAction());
+            finishServiceEarly(startId);
+            return START_NOT_STICKY;
+        }
+        Pair<Integer, Notification> notifPair = buildNotification(packageName, notifTitle);
+        startForeground(notifPair.first, notifPair.second);
+        msg.setData(intentBundle);
+        mServiceHandler.sendMessage(msg);
+        return START_NOT_STICKY;
+    }
+
+    private void installPackage(Bundle argsBundle) {
+        int startId = WearPackageArgs.getStartId(argsBundle);
+        final String packageName = WearPackageArgs.getPackageName(argsBundle);
+        final Uri assetUri = WearPackageArgs.getAssetUri(argsBundle);
+        final Uri permUri = WearPackageArgs.getPermUri(argsBundle);
+        boolean checkPerms = WearPackageArgs.checkPerms(argsBundle);
+        boolean skipIfSameVersion = WearPackageArgs.skipIfSameVersion(argsBundle);
+        int companionSdkVersion = WearPackageArgs.getCompanionSdkVersion(argsBundle);
+        int companionDeviceVersion = WearPackageArgs.getCompanionDeviceVersion(argsBundle);
+        String compressionAlg = WearPackageArgs.getCompressionAlg(argsBundle);
+        boolean skipIfLowerVersion = WearPackageArgs.skipIfLowerVersion(argsBundle);
+
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Installing package: " + packageName + ", assetUri: " + assetUri +
+                    ",permUri: " + permUri + ", startId: " + startId + ", checkPerms: " +
+                    checkPerms + ", skipIfSameVersion: " + skipIfSameVersion +
+                    ", compressionAlg: " + compressionAlg + ", companionSdkVersion: " +
+                    companionSdkVersion + ", companionDeviceVersion: " + companionDeviceVersion +
+                    ", skipIfLowerVersion: " + skipIfLowerVersion);
+        }
+        final PackageManager pm = getPackageManager();
+        File tempFile = null;
+        int installFlags = 0;
+        PowerManager.WakeLock lock = getLock(this.getApplicationContext());
+        boolean messageSent = false;
+        try {
+            PackageInfo existingPkgInfo = null;
+            try {
+                existingPkgInfo = pm.getPackageInfo(packageName,
+                        PackageManager.MATCH_ANY_USER | PackageManager.GET_PERMISSIONS);
+                if (existingPkgInfo != null) {
+                    installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                // Ignore this exception. We could not find the package, will treat as a new
+                // installation.
+            }
+            if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "Replacing package:" + packageName);
+                }
+            }
+            // TODO(28021618): This was left as a temp file due to the fact that this code is being
+            //       deprecated and that we need the bare minimum to continue working moving forward
+            //       If this code is used as reference, this permission logic might want to be
+            //       reworked to use a stream instead of a file so that we don't need to write a
+            //       file at all.  Note that there might be some trickiness with opening a stream
+            //       for multiple users.
+            ParcelFileDescriptor parcelFd = getContentResolver()
+                    .openFileDescriptor(assetUri, "r");
+            tempFile = WearPackageUtil.getFileFromFd(WearPackageInstallerService.this,
+                    parcelFd, packageName, compressionAlg);
+            if (tempFile == null) {
+                Log.e(TAG, "Could not create a temp file from FD for " + packageName);
+                return;
+            }
+            PackageParser.Package pkg = PackageUtil.getPackageInfo(this, tempFile);
+            if (pkg == null) {
+                Log.e(TAG, "Could not parse apk information for " + packageName);
+                return;
+            }
+
+            if (!pkg.packageName.equals(packageName)) {
+                Log.e(TAG, "Wearable Package Name has to match what is provided for " +
+                        packageName);
+                return;
+            }
+
+            pkg.applicationInfo.sourceDir = tempFile.getPath();
+            pkg.applicationInfo.publicSourceDir = tempFile.getPath();
+            getLabelAndUpdateNotification(packageName,
+                    getString(R.string.installing_app, pkg.applicationInfo.loadLabel(pm)));
+
+            List<String> wearablePerms = pkg.requestedPermissions;
+
+            // Log if the installed pkg has a higher version number.
+            if (existingPkgInfo != null) {
+                if (existingPkgInfo.getLongVersionCode() == pkg.getLongVersionCode()) {
+                    if (skipIfSameVersion) {
+                        Log.w(TAG, "Version number (" + pkg.getLongVersionCode() +
+                                ") of new app is equal to existing app for " + packageName +
+                                "; not installing due to versionCheck");
+                        return;
+                    } else {
+                        Log.w(TAG, "Version number of new app (" + pkg.getLongVersionCode() +
+                                ") is equal to existing app for " + packageName);
+                    }
+                } else if (existingPkgInfo.getLongVersionCode() > pkg.getLongVersionCode()) {
+                    if (skipIfLowerVersion) {
+                        // Starting in Feldspar, we are not going to allow downgrades of any app.
+                        Log.w(TAG, "Version number of new app (" + pkg.getLongVersionCode() +
+                                ") is lower than existing app ( "
+                                + existingPkgInfo.getLongVersionCode() +
+                                ") for " + packageName + "; not installing due to versionCheck");
+                        return;
+                    } else {
+                        Log.w(TAG, "Version number of new app (" + pkg.getLongVersionCode() +
+                                ") is lower than existing app ( "
+                                + existingPkgInfo.getLongVersionCode() + ") for " + packageName);
+                    }
+                }
+
+                // Following the Android Phone model, we should only check for permissions for any
+                // newly defined perms.
+                if (existingPkgInfo.requestedPermissions != null) {
+                    for (int i = 0; i < existingPkgInfo.requestedPermissions.length; ++i) {
+                        // If the permission is granted, then we will not ask to request it again.
+                        if ((existingPkgInfo.requestedPermissionsFlags[i] &
+                                PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
+                            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                                Log.d(TAG, existingPkgInfo.requestedPermissions[i] +
+                                        " is already granted for " + packageName);
+                            }
+                            wearablePerms.remove(existingPkgInfo.requestedPermissions[i]);
+                        }
+                    }
+                }
+            }
+
+            // Check that the wearable has all the features.
+            boolean hasAllFeatures = true;
+            if (pkg.reqFeatures != null) {
+                for (FeatureInfo feature : pkg.reqFeatures) {
+                    if (feature.name != null && !pm.hasSystemFeature(feature.name) &&
+                            (feature.flags & FeatureInfo.FLAG_REQUIRED) != 0) {
+                        Log.e(TAG, "Wearable does not have required feature: " + feature +
+                                " for " + packageName);
+                        hasAllFeatures = false;
+                    }
+                }
+            }
+
+            if (!hasAllFeatures) {
+                return;
+            }
+
+            // Check permissions on both the new wearable package and also on the already installed
+            // wearable package.
+            // If the app is targeting API level 23, we will also start a service in ClockworkHome
+            // which will ultimately prompt the user to accept/reject permissions.
+            if (checkPerms && !checkPermissions(pkg, companionSdkVersion, companionDeviceVersion,
+                    permUri, wearablePerms, tempFile)) {
+                Log.w(TAG, "Wearable does not have enough permissions.");
+                return;
+            }
+
+            // Finally install the package.
+            ParcelFileDescriptor fd = getContentResolver().openFileDescriptor(assetUri, "r");
+            PackageInstallerFactory.getPackageInstaller(this).install(packageName, fd,
+                    new PackageInstallListener(this, lock, startId, packageName));
+
+            messageSent = true;
+            Log.i(TAG, "Sent installation request for " + packageName);
+        } catch (FileNotFoundException e) {
+            Log.e(TAG, "Could not find the file with URI " + assetUri, e);
+        } finally {
+            if (!messageSent) {
+                // Some error happened. If the message has been sent, we can wait for the observer
+                // which will finish the service.
+                if (tempFile != null) {
+                    tempFile.delete();
+                }
+                finishService(lock, startId);
+            }
+        }
+    }
+
+    // TODO: This was left using the old PackageManager API due to the fact that this code is being
+    //       deprecated and that we need the bare minimum to continue working moving forward
+    //       If this code is used as reference, this logic should be reworked to use the new
+    //       PackageInstaller APIs similar to how installPackage was reworked
+    private void uninstallPackage(Bundle argsBundle) {
+        int startId = WearPackageArgs.getStartId(argsBundle);
+        final String packageName = WearPackageArgs.getPackageName(argsBundle);
+
+        PowerManager.WakeLock lock = getLock(this.getApplicationContext());
+        final PackageManager pm = getPackageManager();
+        try {
+            PackageInfo pkgInfo = pm.getPackageInfo(packageName, 0);
+            getLabelAndUpdateNotification(packageName,
+                    getString(R.string.uninstalling_app, pkgInfo.applicationInfo.loadLabel(pm)));
+
+            // Found package, send uninstall request.
+            pm.deletePackage(packageName, new PackageDeleteObserver(lock, startId),
+                    PackageManager.DELETE_ALL_USERS);
+
+            Log.i(TAG, "Sent delete request for " + packageName);
+        } catch (IllegalArgumentException | PackageManager.NameNotFoundException e) {
+            // Couldn't find the package, no need to call uninstall.
+            Log.w(TAG, "Could not find package, not deleting " + packageName, e);
+            finishService(lock, startId);
+        }
+    }
+
+    private boolean checkPermissions(PackageParser.Package pkg, int companionSdkVersion,
+            int companionDeviceVersion, Uri permUri, List<String> wearablePermissions,
+            File apkFile) {
+        // Assumption: We are running on Android O.
+        // If the Phone App is targeting M, all permissions may not have been granted to the phone
+        // app. If the Wear App is then not targeting M, there may be permissions that are not
+        // granted on the Phone app (by the user) right now and we cannot just grant it for the Wear
+        // app.
+        if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
+            // Install the app if Wear App is ready for the new perms model.
+            return true;
+        }
+
+        if (!doesWearHaveUngrantedPerms(pkg.packageName, permUri, wearablePermissions)) {
+            // All permissions requested by the watch are already granted on the phone, no need
+            // to do anything.
+            return true;
+        }
+
+        // Log an error if Wear is targeting < 23 and phone is targeting >= 23.
+        if (companionSdkVersion == 0 || companionSdkVersion >= Build.VERSION_CODES.M) {
+            Log.e(TAG, "MNC: Wear app's targetSdkVersion should be at least 23, if "
+                    + "phone app is targeting at least 23, will continue.");
+        }
+
+        return false;
+    }
+
+    /**
+     * Given a {@string packageName} corresponding to a phone app, query the provider for all the
+     * perms that are granted.
+     *
+     * @return true if the Wear App has any perms that have not been granted yet on the phone side.
+     * @return true if there is any error cases.
+     */
+    private boolean doesWearHaveUngrantedPerms(String packageName, Uri permUri,
+            List<String> wearablePermissions) {
+        if (permUri == null) {
+            Log.e(TAG, "Permission URI is null");
+            // Pretend there is an ungranted permission to avoid installing for error cases.
+            return true;
+        }
+        Cursor permCursor = getContentResolver().query(permUri, null, null, null, null);
+        if (permCursor == null) {
+            Log.e(TAG, "Could not get the cursor for the permissions");
+            // Pretend there is an ungranted permission to avoid installing for error cases.
+            return true;
+        }
+
+        Set<String> grantedPerms = new HashSet<>();
+        Set<String> ungrantedPerms = new HashSet<>();
+        while(permCursor.moveToNext()) {
+            // Make sure that the MatrixCursor returned by the ContentProvider has 2 columns and
+            // verify their types.
+            if (permCursor.getColumnCount() == 2
+                    && Cursor.FIELD_TYPE_STRING == permCursor.getType(0)
+                    && Cursor.FIELD_TYPE_INTEGER == permCursor.getType(1)) {
+                String perm = permCursor.getString(0);
+                Integer granted = permCursor.getInt(1);
+                if (granted == 1) {
+                    grantedPerms.add(perm);
+                } else {
+                    ungrantedPerms.add(perm);
+                }
+            }
+        }
+        permCursor.close();
+
+        boolean hasUngrantedPerm = false;
+        for (String wearablePerm : wearablePermissions) {
+            if (!grantedPerms.contains(wearablePerm)) {
+                hasUngrantedPerm = true;
+                if (!ungrantedPerms.contains(wearablePerm)) {
+                    // This is an error condition. This means that the wearable has permissions that
+                    // are not even declared in its host app. This is a developer error.
+                    Log.e(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm
+                            + "\" that is not defined in the host application's manifest.");
+                } else {
+                    Log.w(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm +
+                            "\" that is not granted in the host application.");
+                }
+            }
+        }
+        return hasUngrantedPerm;
+    }
+
+    /** Finishes the service after fulfilling obligation to call startForeground. */
+    private void finishServiceEarly(int startId) {
+        Pair<Integer, Notification> notifPair = buildNotification(
+                getApplicationContext().getPackageName(), "");
+        startForeground(notifPair.first, notifPair.second);
+        finishService(null, startId);
+    }
+
+    private void finishService(PowerManager.WakeLock lock, int startId) {
+        if (lock != null && lock.isHeld()) {
+            lock.release();
+        }
+        stopSelf(startId);
+    }
+
+    private synchronized PowerManager.WakeLock getLock(Context context) {
+        if (lockStatic == null) {
+            PowerManager mgr =
+                    (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+            lockStatic = mgr.newWakeLock(
+                    PowerManager.PARTIAL_WAKE_LOCK, context.getClass().getSimpleName());
+            lockStatic.setReferenceCounted(true);
+        }
+        return lockStatic;
+    }
+
+    private class PackageInstallListener implements PackageInstallerImpl.InstallListener {
+        private Context mContext;
+        private PowerManager.WakeLock mWakeLock;
+        private int mStartId;
+        private String mApplicationPackageName;
+        private PackageInstallListener(Context context, PowerManager.WakeLock wakeLock,
+                int startId, String applicationPackageName) {
+            mContext = context;
+            mWakeLock = wakeLock;
+            mStartId = startId;
+            mApplicationPackageName = applicationPackageName;
+        }
+
+        @Override
+        public void installBeginning() {
+            Log.i(TAG, "Package " + mApplicationPackageName + " is being installed.");
+        }
+
+        @Override
+        public void installSucceeded() {
+            try {
+                Log.i(TAG, "Package " + mApplicationPackageName + " was installed.");
+
+                // Delete tempFile from the file system.
+                File tempFile = WearPackageUtil.getTemporaryFile(mContext, mApplicationPackageName);
+                if (tempFile != null) {
+                    tempFile.delete();
+                }
+            } finally {
+                finishService(mWakeLock, mStartId);
+            }
+        }
+
+        @Override
+        public void installFailed(int errorCode, String errorDesc) {
+            Log.e(TAG, "Package install failed " + mApplicationPackageName
+                    + ", errorCode " + errorCode);
+            finishService(mWakeLock, mStartId);
+        }
+    }
+
+    private class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
+        private PowerManager.WakeLock mWakeLock;
+        private int mStartId;
+
+        private PackageDeleteObserver(PowerManager.WakeLock wakeLock, int startId) {
+            mWakeLock = wakeLock;
+            mStartId = startId;
+        }
+
+        public void packageDeleted(String packageName, int returnCode) {
+            try {
+                if (returnCode >= 0) {
+                    Log.i(TAG, "Package " + packageName + " was uninstalled.");
+                } else {
+                    Log.e(TAG, "Package uninstall failed " + packageName + ", returnCode " +
+                            returnCode);
+                }
+            } finally {
+                finishService(mWakeLock, mStartId);
+            }
+        }
+    }
+
+    private synchronized Pair<Integer, Notification> buildNotification(final String packageName,
+            final String title) {
+        int notifId;
+        if (mNotifIdMap.containsKey(packageName)) {
+            notifId = mNotifIdMap.get(packageName);
+        } else {
+            notifId = mInstallNotificationId++;
+            mNotifIdMap.put(packageName, notifId);
+        }
+
+        if (mNotificationChannel == null) {
+            mNotificationChannel = new NotificationChannel(WEAR_APPS_CHANNEL,
+                    getString(R.string.wear_app_channel), NotificationManager.IMPORTANCE_MIN);
+            NotificationManager notificationManager = getSystemService(NotificationManager.class);
+            notificationManager.createNotificationChannel(mNotificationChannel);
+        }
+        return new Pair<>(notifId, new Notification.Builder(this, WEAR_APPS_CHANNEL)
+            .setSmallIcon(R.drawable.ic_file_download)
+            .setContentTitle(title)
+            .build());
+    }
+
+    private void getLabelAndUpdateNotification(String packageName, String title) {
+        // Update notification since we have a label now.
+        NotificationManager notificationManager = getSystemService(NotificationManager.class);
+        Pair<Integer, Notification> notifPair = buildNotification(packageName, title);
+        notificationManager.notify(notifPair.first, notifPair.second);
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java
new file mode 100644
index 0000000..bc740ab
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.wear;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.tukaani.xz.LZMAInputStream;
+import org.tukaani.xz.XZInputStream;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class WearPackageUtil {
+    private static final String TAG = "WearablePkgInstaller";
+
+    private static final String COMPRESSION_LZMA = "lzma";
+    private static final String COMPRESSION_XZ = "xz";
+
+    public static File getTemporaryFile(Context context, String packageName) {
+        try {
+            File newFileDir = new File(context.getFilesDir(), "tmp");
+            newFileDir.mkdirs();
+            Os.chmod(newFileDir.getAbsolutePath(), 0771);
+            File newFile = new File(newFileDir, packageName + ".apk");
+            return newFile;
+        }   catch (ErrnoException e) {
+            Log.e(TAG, "Failed to open.", e);
+            return null;
+        }
+    }
+
+    public static File getIconFile(final Context context, final String packageName) {
+        try {
+            File newFileDir = new File(context.getFilesDir(), "images/icons");
+            newFileDir.mkdirs();
+            Os.chmod(newFileDir.getAbsolutePath(), 0771);
+            return new File(newFileDir, packageName + ".icon");
+        }   catch (ErrnoException e) {
+            Log.e(TAG, "Failed to open.", e);
+            return null;
+        }
+    }
+
+    /**
+     * In order to make sure that the Wearable Asset Manager has a reasonable apk that can be used
+     * by the PackageManager, we will parse it before sending it to the PackageManager.
+     * Unfortunately, PackageParser needs a file to parse. So, we have to temporarily convert the fd
+     * to a File.
+     *
+     * @param context
+     * @param fd FileDescriptor to convert to File
+     * @param packageName Name of package, will define the name of the file
+     * @param compressionAlg Can be null. For ALT mode the APK will be compressed. We will
+     *                       decompress it here
+     */
+    public static File getFileFromFd(Context context, ParcelFileDescriptor fd,
+            String packageName, String compressionAlg) {
+        File newFile = getTemporaryFile(context, packageName);
+        if (fd == null || fd.getFileDescriptor() == null)  {
+            return null;
+        }
+        InputStream fr = new ParcelFileDescriptor.AutoCloseInputStream(fd);
+        try {
+            if (TextUtils.equals(compressionAlg, COMPRESSION_XZ)) {
+                fr = new XZInputStream(fr);
+            } else if (TextUtils.equals(compressionAlg, COMPRESSION_LZMA)) {
+                fr = new LZMAInputStream(fr);
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Compression was set to " + compressionAlg + ", but could not decode ", e);
+            return null;
+        }
+
+        int nRead;
+        byte[] data = new byte[1024];
+        try {
+            final FileOutputStream fo = new FileOutputStream(newFile);
+            while ((nRead = fr.read(data, 0, data.length)) != -1) {
+                fo.write(data, 0, nRead);
+            }
+            fo.flush();
+            fo.close();
+            Os.chmod(newFile.getAbsolutePath(), 0644);
+            return newFile;
+        } catch (IOException e) {
+            Log.e(TAG, "Reading from Asset FD or writing to temp file failed ", e);
+            return null;
+        }   catch (ErrnoException e) {
+            Log.e(TAG, "Could not set permissions on file ", e);
+            return null;
+        } finally {
+            try {
+                fr.close();
+            } catch (IOException e) {
+                Log.e(TAG, "Failed to close the file from FD ", e);
+            }
+        }
+    }
+
+    /**
+     * @return com.google.com from expected formats like
+     * Uri: package:com.google.com, package:/com.google.com, package://com.google.com
+     */
+    public static String getSanitizedPackageName(Uri packageUri) {
+        String packageName = packageUri.getEncodedSchemeSpecificPart();
+        if (packageName != null) {
+            return packageName.replaceAll("^/+", "");
+        }
+        return packageName;
+    }
+}
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less.png
deleted file mode 100644
index b6a5eb5..0000000
--- a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more.png
deleted file mode 100644
index 4e36bd2..0000000
--- a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_grayedout_printer.png b/packages/PrintSpooler/res/drawable-hdpi/ic_grayedout_printer.png
deleted file mode 100644
index 5e54970..0000000
--- a/packages/PrintSpooler/res/drawable-hdpi/ic_grayedout_printer.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_restart.png b/packages/PrintSpooler/res/drawable-hdpi/ic_restart.png
deleted file mode 100644
index bb9d855..0000000
--- a/packages/PrintSpooler/res/drawable-hdpi/ic_restart.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/stat_notify_cancelling.png b/packages/PrintSpooler/res/drawable-hdpi/stat_notify_cancelling.png
deleted file mode 100644
index 2757db0..0000000
--- a/packages/PrintSpooler/res/drawable-hdpi/stat_notify_cancelling.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less.png
deleted file mode 100644
index 428a946..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less_24dp.png b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less_24dp.png
deleted file mode 100644
index 3220eea..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more.png
deleted file mode 100644
index fbbd094..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more_24dp.png b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more_24dp.png
deleted file mode 100644
index 5530f52..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_grayedout_printer.png b/packages/PrintSpooler/res/drawable-mdpi/ic_grayedout_printer.png
deleted file mode 100644
index 5e54970..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/ic_grayedout_printer.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_restart.png b/packages/PrintSpooler/res/drawable-mdpi/ic_restart.png
deleted file mode 100644
index bd611e8..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/ic_restart.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/stat_notify_cancelling.png b/packages/PrintSpooler/res/drawable-mdpi/stat_notify_cancelling.png
deleted file mode 100644
index c1b380a..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/stat_notify_cancelling.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less.png
deleted file mode 100644
index 6161c20..0000000
--- a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more.png
deleted file mode 100644
index 3a89805..0000000
--- a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_grayedout_printer.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_grayedout_printer.png
deleted file mode 100644
index 5e54970..0000000
--- a/packages/PrintSpooler/res/drawable-xhdpi/ic_grayedout_printer.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_restart.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_restart.png
deleted file mode 100644
index a7fdc0d..0000000
--- a/packages/PrintSpooler/res/drawable-xhdpi/ic_restart.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/stat_notify_cancelling.png b/packages/PrintSpooler/res/drawable-xhdpi/stat_notify_cancelling.png
deleted file mode 100644
index fedc00e..0000000
--- a/packages/PrintSpooler/res/drawable-xhdpi/stat_notify_cancelling.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less.png
deleted file mode 100644
index 52a52d9..0000000
--- a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more.png
deleted file mode 100644
index 15e6abd..0000000
--- a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less.png
deleted file mode 100644
index 46811a1..0000000
--- a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more.png
deleted file mode 100644
index 141f28b..0000000
--- a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable/ic_clear.xml b/packages/PrintSpooler/res/drawable/ic_clear.xml
new file mode 100644
index 0000000..076e8ef
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable/ic_clear.xml
@@ -0,0 +1,26 @@
+<?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="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"
+        android:fillColor="?android:colorForeground"/>
+</vector>
diff --git a/packages/PrintSpooler/res/drawable/ic_expand_less.xml b/packages/PrintSpooler/res/drawable/ic_expand_less.xml
index 6f1ece1..c3e87dc 100644
--- a/packages/PrintSpooler/res/drawable/ic_expand_less.xml
+++ b/packages/PrintSpooler/res/drawable/ic_expand_less.xml
@@ -1,43 +1,26 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this 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.
--->
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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"
-    android:autoMirrored="true">
-
-    <item
-        android:state_checked="true">
-        <bitmap
-            android:src="@drawable/ic_expand_less"
-            android:tint="?android:attr/colorControlActivated">
-        </bitmap>
-    </item>
-
-    <item
-        android:state_pressed="true">
-        <bitmap
-            android:src="@drawable/ic_expand_less"
-            android:tint="?android:attr/colorControlActivated">
-        </bitmap>
-    </item>
-
-    <item>
-        <bitmap
-            android:src="@drawable/ic_expand_less"
-            android:tint="?android:attr/colorControlNormal">
-        </bitmap>
-    </item>
-
-</selector>
+<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:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"
+        android:fillColor="?android:colorForeground" />
+</vector>
\ No newline at end of file
diff --git a/packages/PrintSpooler/res/drawable/ic_expand_more.xml b/packages/PrintSpooler/res/drawable/ic_expand_more.xml
index 8d71452..3895144 100644
--- a/packages/PrintSpooler/res/drawable/ic_expand_more.xml
+++ b/packages/PrintSpooler/res/drawable/ic_expand_more.xml
@@ -1,43 +1,26 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this 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.
--->
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT 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"
-    android:autoMirrored="true">
-
-    <item
-        android:state_checked="true">
-        <bitmap
-            android:src="@drawable/ic_expand_more"
-            android:tint="?android:attr/colorControlActivated">
-        </bitmap>
-    </item>
-
-    <item
-        android:state_pressed="true">
-        <bitmap
-            android:src="@drawable/ic_expand_more"
-            android:tint="?android:attr/colorControlActivated">
-        </bitmap>
-    </item>
-
-    <item>
-        <bitmap
-            android:src="@drawable/ic_expand_more"
-            android:tint="?android:attr/colorControlNormal">
-        </bitmap>
-    </item>
-
-</selector>
+<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:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6l-6,-6L7.41,8.59z"
+        android:fillColor="?android:colorForeground" />
+</vector>
\ No newline at end of file
diff --git a/packages/PrintSpooler/res/drawable/print_warning.xml b/packages/PrintSpooler/res/drawable/print_warning.xml
deleted file mode 100644
index 35f0fed..0000000
--- a/packages/PrintSpooler/res/drawable/print_warning.xml
+++ /dev/null
@@ -1,25 +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="96dp"
-        android:height="96dp"
-        android:viewportWidth="96.0"
-        android:viewportHeight="96.0">
-    <path
-        android:fillColor="#C8CCCE"
-        android:pathData="M4,84H92L48,8 4,84zM52,72h-8v-8h8v8zM52,56H44V40h8v16z"/>
-</vector>
diff --git a/packages/PrintSpooler/res/layout/preview_page.xml b/packages/PrintSpooler/res/layout/preview_page.xml
index aafdd8f..8db347e 100644
--- a/packages/PrintSpooler/res/layout/preview_page.xml
+++ b/packages/PrintSpooler/res/layout/preview_page.xml
@@ -44,7 +44,7 @@
             android:layout_height="wrap_content"
             android:layout_centerInParent="true"
             android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="?android:attr/textColorPrimary">
+            android:textColor="@android:color/white">
         </TextView>
 
         <ImageView
diff --git a/packages/PrintSpooler/res/layout/preview_page_error.xml b/packages/PrintSpooler/res/layout/preview_page_error.xml
index 4e9fb77..99ab99d 100644
--- a/packages/PrintSpooler/res/layout/preview_page_error.xml
+++ b/packages/PrintSpooler/res/layout/preview_page_error.xml
@@ -21,11 +21,14 @@
         android:gravity="center">
 
     <ImageView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginBottom="12dip"
-            android:src="@drawable/print_warning"
-            android:contentDescription="@null" />
+        android:layout_width="120dp"
+        android:layout_height="110dp"
+        android:layout_marginBottom="12dip"
+        android:src="@*android:drawable/ic_print_error"
+        android:scaleType="fitEnd"
+        android:alpha="0.1"
+        android:tint="@android:color/black"
+        android:importantForAccessibility="no" />
 
     <TextView
             android:layout_width="wrap_content"
diff --git a/packages/PrintSpooler/res/layout/preview_page_loading.xml b/packages/PrintSpooler/res/layout/preview_page_loading.xml
index 1af3a17..918edd9 100644
--- a/packages/PrintSpooler/res/layout/preview_page_loading.xml
+++ b/packages/PrintSpooler/res/layout/preview_page_loading.xml
@@ -19,14 +19,13 @@
     android:layout_height="fill_parent">
 
     <ImageView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_margin="36dip"
+        android:layout_width="120dp"
+        android:layout_height="110dp"
         android:layout_gravity="center"
-        android:src="@drawable/ic_grayedout_printer"
-        android:contentDescription="@null"
-        android:scaleType="centerInside"
-        android:adjustViewBounds="true">
-    </ImageView>
+        android:src="@*android:drawable/ic_print"
+        android:scaleType="fitCenter"
+        android:alpha="0.1"
+        android:tint="@android:color/black"
+        android:importantForAccessibility="no" />
 
 </FrameLayout>
diff --git a/packages/PrintSpooler/res/layout/preview_page_selected.xml b/packages/PrintSpooler/res/layout/preview_page_selected.xml
index 77f4727..6727142 100644
--- a/packages/PrintSpooler/res/layout/preview_page_selected.xml
+++ b/packages/PrintSpooler/res/layout/preview_page_selected.xml
@@ -42,7 +42,7 @@
             android:layout_height="wrap_content"
             android:layout_centerInParent="true"
             android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="?android:attr/textColorPrimary">
+            android:textColor="@android:color/white">
         </TextView>
 
         <ImageView
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 774f320..212f398 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -29,7 +29,7 @@
         android:paddingStart="8dip"
         android:layout_marginEnd="16dp"
         android:elevation="@dimen/preview_controls_elevation"
-        android:background="?android:attr/colorPrimary">
+        style="?android:actionBarStyle">
 
         <com.android.printspooler.widget.ClickInterceptSpinner
             android:id="@+id/destination_spinner"
@@ -55,7 +55,7 @@
         android:paddingBottom="8dip"
         android:orientation="horizontal"
         android:elevation="@dimen/preview_controls_elevation"
-        android:background="?android:attr/colorPrimary">
+        style="?android:actionBarStyle">
 
         <TextView
             android:layout_width="wrap_content"
@@ -121,7 +121,6 @@
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
         android:animateLayoutChanges="true"
-        android:background="@color/print_preview_background_color"
         android:gravity="center">
 
         <!-- Error message added here -->
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml
index 69d4f91..3aafc99 100644
--- a/packages/PrintSpooler/res/layout/print_activity_controls.xml
+++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml
@@ -22,7 +22,7 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:elevation="@dimen/preview_controls_elevation"
-    android:background="?android:attr/colorPrimary">
+    style="?android:actionBarStyle">
 
     <LinearLayout
         android:id="@+id/draggable_content"
diff --git a/packages/PrintSpooler/res/layout/print_error_fragment.xml b/packages/PrintSpooler/res/layout/print_error_fragment.xml
index 3ea2abd..9d9dd01 100644
--- a/packages/PrintSpooler/res/layout/print_error_fragment.xml
+++ b/packages/PrintSpooler/res/layout/print_error_fragment.xml
@@ -16,35 +16,39 @@
  -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginStart="16dip"
+    android:layout_marginEnd="16dip"
     android:gravity="center"
     android:orientation="vertical">
 
     <ImageView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="12dip"
-        android:src="@drawable/ic_grayedout_printer"
-        android:contentDescription="@null">
-    </ImageView>
+        android:layout_width="120dp"
+        android:layout_height="110dp"
+        android:layout_marginBottom="12dp"
+        android:src="@*android:drawable/ic_print_error"
+        android:scaleType="fitEnd"
+        android:alpha="0.1"
+        android:tint="?android:colorForeground"
+        android:importantForAccessibility="no" />
 
     <TextView
         android:id="@+id/message"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginStart="16dip"
-        android:layout_marginEnd="16dip"
-        android:gravity="center_horizontal"
-        android:text="@string/print_error_default_message"
-        android:textAppearance="?android:attr/textAppearanceLargeInverse">
-    </TextView>
+        android:layout_marginBottom="16dp"
+        android:gravity="center"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:textColor="?android:attr/textColorSecondary"
+        android:text="@string/print_error_default_message" />
 
     <Button
         android:id="@+id/action_button"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@string/print_error_retry">
-    </Button>
+        style="?android:attr/borderlessButtonStyle"
+        android:textColor="?android:attr/textColorSecondary"
+        android:text="@string/print_add_printer" />
 
 </LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/print_progress_fragment.xml b/packages/PrintSpooler/res/layout/print_progress_fragment.xml
index 3b010f8..89071605 100644
--- a/packages/PrintSpooler/res/layout/print_progress_fragment.xml
+++ b/packages/PrintSpooler/res/layout/print_progress_fragment.xml
@@ -15,34 +15,39 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginStart="16dp"
+    android:layout_marginEnd="16dp"
     android:gravity="center"
     android:orientation="vertical">
 
     <ImageView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="12dip"
-        android:src="@drawable/ic_grayedout_printer"
-        android:contentDescription="@null">
-    </ImageView>
-
-    <ProgressBar
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:indeterminate="true"
-        style="?android:attr/progressBarStyleHorizontal">
-    </ProgressBar>
+        android:layout_width="120dp"
+        android:layout_height="110dp"
+        android:layout_marginBottom="12dp"
+        android:src="@*android:drawable/ic_print"
+        android:scaleType="fitEnd"
+        android:alpha="0.1"
+        android:tint="?android:colorForeground"
+        android:importantForAccessibility="no" />
 
     <TextView
         android:id="@+id/message"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceLargeInverse"
-        android:text="@string/print_preparing_preview">
-    </TextView>
+        android:layout_marginBottom="16dp"
+        android:gravity="center"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:textColor="?android:attr/textColorSecondary"
+        android:text="@string/print_preparing_preview" />
+
+    <ProgressBar
+        android:layout_width="300dp"
+        android:layout_height="wrap_content"
+        android:indeterminate="true"
+        android:importantForAccessibility="no"
+        style="?android:attr/progressBarStyleHorizontal" />
 
 </LinearLayout>
 
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 91beff6..681924b 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -31,45 +31,48 @@
         android:visibility="gone">
 
         <LinearLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginStart="16dp"
+            android:layout_marginEnd="16dp"
             android:gravity="center"
             android:orientation="vertical">
 
             <ImageView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginBottom="12dip"
-                android:src="@*android:drawable/ic_grayedout_printer"
-                android:importantForAccessibility="no">
-            </ImageView>
+                android:layout_width="120dp"
+                android:layout_height="110dp"
+                android:layout_marginBottom="12dp"
+                android:src="@*android:drawable/ic_print"
+                android:scaleType="fitEnd"
+                android:alpha="0.1"
+                android:tint="?android:colorForeground"
+                android:importantForAccessibility="no" />
 
             <TextView
                 android:id="@+id/title"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:layout_marginBottom="16dp"
+                android:gravity="center"
                 android:textAppearance="?android:attr/textAppearanceLarge"
                 android:textColor="?android:attr/textColorSecondary"
-                android:text="@string/print_searching_for_printers">
-            </TextView>
+                android:text="@string/print_searching_for_printers" />
 
             <ProgressBar
                 android:id="@+id/progress_bar"
-                android:layout_width="fill_parent"
+                android:layout_width="300dp"
                 android:layout_height="wrap_content"
                 android:indeterminate="true"
-                style="?android:attr/progressBarStyleHorizontal">
-            </ProgressBar>
+                android:importantForAccessibility="no"
+                style="?android:attr/progressBarStyleHorizontal" />
 
             <Button
-                    android:id="@+id/button"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    style="?android:attr/buttonBarButtonStyle"
-                    android:textAppearance="?android:attr/textAppearanceSmall"
-                    android:text="@string/print_add_printer"
-                    android:textAllCaps="true" />
+                android:id="@+id/button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                style="?android:attr/borderlessButtonStyle"
+                android:textColor="?android:attr/textColorSecondary"
+                android:text="@string/print_add_printer" />
 
         </LinearLayout>
 
diff --git a/packages/PrintSpooler/res/values-ar/strings.xml b/packages/PrintSpooler/res/values-ar/strings.xml
index 779d19b..807426b 100644
--- a/packages/PrintSpooler/res/values-ar/strings.xml
+++ b/packages/PrintSpooler/res/values-ar/strings.xml
@@ -111,7 +111,7 @@
     <item msgid="3199660090246166812">"أفقي"</item>
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"تعذرت الكتابة إلى الملف"</string>
-    <string name="print_error_default_message" msgid="8602678405502922346">"عذرًا، هذا لا يعمل. أعد المحاولة."</string>
+    <string name="print_error_default_message" msgid="8602678405502922346">"تعذّرت عملية الطباعة. يُرجى إعادة المحاولة."</string>
     <string name="print_error_retry" msgid="1426421728784259538">"إعادة المحاولة"</string>
     <string name="print_error_printer_unavailable" msgid="8985614415253203381">"الطابعة ليست متوفرة في الوقت الحالي."</string>
     <string name="print_cannot_load_page" msgid="6179560924492912009">"يتعذر عرض المعاينة."</string>
diff --git a/packages/PrintSpooler/res/values-bg/strings.xml b/packages/PrintSpooler/res/values-bg/strings.xml
index d44b4ce..d05d96f 100644
--- a/packages/PrintSpooler/res/values-bg/strings.xml
+++ b/packages/PrintSpooler/res/values-bg/strings.xml
@@ -103,7 +103,7 @@
     <item msgid="3199660090246166812">"Хоризонтално"</item>
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"Не можа да се запише във файла"</string>
-    <string name="print_error_default_message" msgid="8602678405502922346">"За съжаление това не проработи. Опитайте отново."</string>
+    <string name="print_error_default_message" msgid="8602678405502922346">"За съжаление, това не проработи. Опитайте отново."</string>
     <string name="print_error_retry" msgid="1426421728784259538">"Нов опит"</string>
     <string name="print_error_printer_unavailable" msgid="8985614415253203381">"В момента този принтер не е налице."</string>
     <string name="print_cannot_load_page" msgid="6179560924492912009">"Визуализацията не може да се покаже"</string>
diff --git a/packages/PrintSpooler/res/values-da/strings.xml b/packages/PrintSpooler/res/values-da/strings.xml
index f88a453..fa59d99 100644
--- a/packages/PrintSpooler/res/values-da/strings.xml
+++ b/packages/PrintSpooler/res/values-da/strings.xml
@@ -32,7 +32,7 @@
     <string name="template_page_range" msgid="428638530038286328">"Interval på <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
     <string name="pages_range_example" msgid="8558694453556945172">"f.eks. 1-5,8,11-13"</string>
     <string name="print_preview" msgid="8010217796057763343">"Vis udskrift"</string>
-    <string name="install_for_print_preview" msgid="6366303997385509332">"Installer et PDF-visningsprog. for at se eksempel"</string>
+    <string name="install_for_print_preview" msgid="6366303997385509332">"Installer et PDF-visningsprog. for at se en forhåndsvisning"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"Udskrivningsapp gik ned"</string>
     <string name="generating_print_job" msgid="3119608742651698916">"Udskriften generes"</string>
     <string name="save_as_pdf" msgid="5718454119847596853">"Gem som PDF"</string>
@@ -106,6 +106,6 @@
     <string name="print_error_default_message" msgid="8602678405502922346">"Det virkede desværre ikke. Prøv igen."</string>
     <string name="print_error_retry" msgid="1426421728784259538">"Prøv igen"</string>
     <string name="print_error_printer_unavailable" msgid="8985614415253203381">"Denne printer er i øjeblikket ikke tilgængelig."</string>
-    <string name="print_cannot_load_page" msgid="6179560924492912009">"Eksempelvisning kan ikke vises"</string>
-    <string name="print_preparing_preview" msgid="3939930735671364712">"Eksempelvisning forberedes..."</string>
+    <string name="print_cannot_load_page" msgid="6179560924492912009">"Forhåndsvisning kan ikke vises"</string>
+    <string name="print_preparing_preview" msgid="3939930735671364712">"Forhåndsvisning klargøres..."</string>
 </resources>
diff --git a/packages/PrintSpooler/res/values-hu/strings.xml b/packages/PrintSpooler/res/values-hu/strings.xml
index b9a0d9d..c1caf91 100644
--- a/packages/PrintSpooler/res/values-hu/strings.xml
+++ b/packages/PrintSpooler/res/values-hu/strings.xml
@@ -103,7 +103,7 @@
     <item msgid="3199660090246166812">"Fekvő"</item>
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"Nem sikerült írni a fájlba"</string>
-    <string name="print_error_default_message" msgid="8602678405502922346">"Sajnáljuk, de nem sikerült. Próbálja újra."</string>
+    <string name="print_error_default_message" msgid="8602678405502922346">"Sajnáljuk, de nem sikerült. Próbáld újra."</string>
     <string name="print_error_retry" msgid="1426421728784259538">"Újra"</string>
     <string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ez a nyomtató jelenleg nem érhető el."</string>
     <string name="print_cannot_load_page" msgid="6179560924492912009">"Nem lehet megjeleníteni az előnézetet"</string>
diff --git a/packages/PrintSpooler/res/values-hy/strings.xml b/packages/PrintSpooler/res/values-hy/strings.xml
index 56045be..0009397 100644
--- a/packages/PrintSpooler/res/values-hy/strings.xml
+++ b/packages/PrintSpooler/res/values-hy/strings.xml
@@ -99,8 +99,8 @@
     <item msgid="79513688117503758">"Կարճեզր"</item>
   </string-array>
   <string-array name="orientation_labels">
-    <item msgid="4061931020926489228">"Դիմանկար"</item>
-    <item msgid="3199660090246166812">"Լանդշաֆտ"</item>
+    <item msgid="4061931020926489228">"Ուղղաձիգ"</item>
+    <item msgid="3199660090246166812">"Հորիզոնական"</item>
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"Հնարավոր չէ գրել ֆայլում"</string>
     <string name="print_error_default_message" msgid="8602678405502922346">"Չհաջողվեց: Նորից փորձեք:"</string>
diff --git a/packages/PrintSpooler/res/values-km/strings.xml b/packages/PrintSpooler/res/values-km/strings.xml
index ed8219e..c878ebe 100644
--- a/packages/PrintSpooler/res/values-km/strings.xml
+++ b/packages/PrintSpooler/res/values-km/strings.xml
@@ -27,7 +27,7 @@
     <string name="label_duplex" msgid="5370037254347072243">"សងខាង"</string>
     <string name="label_orientation" msgid="2853142581990496477">"ទិស"</string>
     <string name="label_pages" msgid="7768589729282182230">"ទំព័រ"</string>
-    <string name="destination_default_text" msgid="5422708056807065710">"ជ្រើសម៉ាស៊ីនបោះពុម្ព"</string>
+    <string name="destination_default_text" msgid="5422708056807065710">"ជ្រើសរើសម៉ាស៊ីនបោះពុម្ព"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> ទាំងអស់"</string>
     <string name="template_page_range" msgid="428638530038286328">"ជួរ​នៃ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
     <string name="pages_range_example" msgid="8558694453556945172">"ឧ. 1—5,8,11—13"</string>
@@ -103,7 +103,7 @@
     <item msgid="3199660090246166812">"ផ្ដេក"</item>
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"មិន​អាច​សរសេរ​ទៅ​កាន់​ឯកសារ"</string>
-    <string name="print_error_default_message" msgid="8602678405502922346">"សូម​ទោស វា​មិន​ដំណើរ​ការ​ទេ។ ព្យាយាម​ម្ដងទៀត។"</string>
+    <string name="print_error_default_message" msgid="8602678405502922346">"សូមអភ័យ​ទោស វា​មិន​ដំណើរ​ការ​ទេ។ ព្យាយាម​ម្ដងទៀត។"</string>
     <string name="print_error_retry" msgid="1426421728784259538">"ព្យាយាម​ម្ដងទៀត"</string>
     <string name="print_error_printer_unavailable" msgid="8985614415253203381">"ឥឡូវ​នេះ ម៉ាស៊ីន​បោះពុម្ព​នេះ​មិន​អាច​ប្រើ​បាន។"</string>
     <string name="print_cannot_load_page" msgid="6179560924492912009">"មិនអាចបង្ហាញការមើលជាមុនបានទេ"</string>
diff --git a/packages/PrintSpooler/res/values-ml/strings.xml b/packages/PrintSpooler/res/values-ml/strings.xml
index 05be7a7..dbcd34b 100644
--- a/packages/PrintSpooler/res/values-ml/strings.xml
+++ b/packages/PrintSpooler/res/values-ml/strings.xml
@@ -35,7 +35,7 @@
     <string name="install_for_print_preview" msgid="6366303997385509332">"പ്രിവ്യൂ കാണിക്കുന്നതിന് PDF വ്യൂവർ ഇൻസ്റ്റാൾ ചെയ്യുക"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"പ്രിന്റുചെയ്യൽ അപ്ലിക്കേഷൻ ക്രാഷായി"</string>
     <string name="generating_print_job" msgid="3119608742651698916">"പ്രിന്റ് ജോലി സൃഷ്‌ടിക്കുന്നു"</string>
-    <string name="save_as_pdf" msgid="5718454119847596853">"PDF ആയി സംരക്ഷിക്കുക"</string>
+    <string name="save_as_pdf" msgid="5718454119847596853">"PDF-ആയി സംരക്ഷിക്കൂ"</string>
     <string name="all_printers" msgid="5018829726861876202">"എല്ലാ പ്രിന്ററുകളും..."</string>
     <string name="print_dialog" msgid="32628687461331979">"പ്രിന്റ് സംഭാഷണം"</string>
     <string name="current_page_template" msgid="5145005201131935302">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-my/strings.xml b/packages/PrintSpooler/res/values-my/strings.xml
index 8ce23e0..96ed754 100644
--- a/packages/PrintSpooler/res/values-my/strings.xml
+++ b/packages/PrintSpooler/res/values-my/strings.xml
@@ -20,7 +20,7 @@
     <string name="more_options_button" msgid="2243228396432556771">"နောက်ထပ် ရွေးစရာများ"</string>
     <string name="label_destination" msgid="9132510997381599275">"ဦးတည်ရာ"</string>
     <string name="label_copies" msgid="3634531042822968308">"မိတ္တူများ"</string>
-    <string name="label_copies_summary" msgid="3861966063536529540">"မိတ္တူများ:"</string>
+    <string name="label_copies_summary" msgid="3861966063536529540">"မိတ္တူများ-"</string>
     <string name="label_paper_size" msgid="908654383827777759">"စက္ကူ  ဆိုက်"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"စက္ကူ  ဆိုက်:"</string>
     <string name="label_color" msgid="1108690305218188969">"ရောင်စုံ"</string>
@@ -35,7 +35,7 @@
     <string name="install_for_print_preview" msgid="6366303997385509332">"အစမ်းကြည့်ရန် ပီဒီအက်ဖ် ဖတ်ရှုစရာ ထည့်သွင်းပါ"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"စာထုတ်လုပ်သော အက်ပ်ခဏ ပျက်သွားပါသည်"</string>
     <string name="generating_print_job" msgid="3119608742651698916">"စာထုတ်အလုပ်ကို လုပ်နေပါသည်"</string>
-    <string name="save_as_pdf" msgid="5718454119847596853">"ပီဒီအက်ဖ် အဖြစ်သိမ်းဆည်းရန်"</string>
+    <string name="save_as_pdf" msgid="5718454119847596853">"PDF အဖြစ်သိမ်းရန်"</string>
     <string name="all_printers" msgid="5018829726861876202">"စာထုတ်စက် အားလုံး"</string>
     <string name="print_dialog" msgid="32628687461331979">"စာထုတ်ရန် အချက်ပြခြင်း"</string>
     <string name="current_page_template" msgid="5145005201131935302">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
@@ -103,8 +103,8 @@
     <item msgid="3199660090246166812">"အလျားလိုက်"</item>
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"ဖိုင်သို့ မရေးနိုင်ခဲ့"</string>
-    <string name="print_error_default_message" msgid="8602678405502922346">"ဆော်ရီး၊ အဲဒါ အလုပ်မဖြစ်ခဲ့ပါ။ ထပ် စမ်းပါ။"</string>
-    <string name="print_error_retry" msgid="1426421728784259538">"ထပ်စမ်း"</string>
+    <string name="print_error_default_message" msgid="8602678405502922346">"လုပ်၍မရခဲ့ပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
+    <string name="print_error_retry" msgid="1426421728784259538">"ထပ်စမ်းကြည့်ရန်"</string>
     <string name="print_error_printer_unavailable" msgid="8985614415253203381">"ဒီပရင်တာမှာ ယခုအချိန်မှာ မရနိုင်ပါ။"</string>
     <string name="print_cannot_load_page" msgid="6179560924492912009">"အစမ်းကြည့်ခြင်းကို ပြသ၍မရပါ"</string>
     <string name="print_preparing_preview" msgid="3939930735671364712">"အစမ်းကြည့်ရန် ပြင်ဆင်နေ…"</string>
diff --git a/packages/PrintSpooler/res/values-night/themes.xml b/packages/PrintSpooler/res/values-night/themes.xml
new file mode 100644
index 0000000..4428dbb
--- /dev/null
+++ b/packages/PrintSpooler/res/values-night/themes.xml
@@ -0,0 +1,35 @@
+<?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="Theme.AddPrinterActivity" parent="@android:style/Theme.DeviceDefault.Dialog">
+        <item name="android:listSeparatorTextViewStyle">@style/ListSeparator</item>
+        <item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
+    </style>
+
+    <style name="Theme.SelectPrinterActivity"
+           parent="android:style/Theme.DeviceDefault">
+        <item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
+    </style>
+
+    <style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+
+</resources>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index 5da31bd..1c128b4 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -103,7 +103,7 @@
     <item msgid="3199660090246166812">"Horizontal"</item>
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"Não foi possível gravar no ficheiro"</string>
-    <string name="print_error_default_message" msgid="8602678405502922346">"Lamentamos, mas isso não funcionou. Tente novam."</string>
+    <string name="print_error_default_message" msgid="8602678405502922346">"Lamentamos, mas isso não funcionou. Tente novamente."</string>
     <string name="print_error_retry" msgid="1426421728784259538">"Tentar novamente"</string>
     <string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impressora não está atualmente disponível."</string>
     <string name="print_cannot_load_page" msgid="6179560924492912009">"Não é possível apresentar a pré-visualização"</string>
diff --git a/packages/PrintSpooler/res/values-sk/strings.xml b/packages/PrintSpooler/res/values-sk/strings.xml
index 17a029a..184f4d8 100644
--- a/packages/PrintSpooler/res/values-sk/strings.xml
+++ b/packages/PrintSpooler/res/values-sk/strings.xml
@@ -107,7 +107,7 @@
     <item msgid="3199660090246166812">"Na šírku"</item>
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"Do súboru nie je možné zapisovať"</string>
-    <string name="print_error_default_message" msgid="8602678405502922346">"Je nám to ľúto, nefungovalo to. Skúste to znova."</string>
+    <string name="print_error_default_message" msgid="8602678405502922346">"Ľutujeme, nepodarilo sa. Skúste to znova."</string>
     <string name="print_error_retry" msgid="1426421728784259538">"Opakovať"</string>
     <string name="print_error_printer_unavailable" msgid="8985614415253203381">"Táto tlačiareň nie je momentálne k dispozícii."</string>
     <string name="print_cannot_load_page" msgid="6179560924492912009">"Ukážka sa nedá zobraziť"</string>
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index 8aaf6f4..12f5b60 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -103,7 +103,7 @@
     <item msgid="3199660090246166812">"Mlalo"</item>
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"Haikuweza kuandika kwenye faili"</string>
-    <string name="print_error_default_message" msgid="8602678405502922346">"Samahani, hiyo haikufanya kazi. Jaribu tena."</string>
+    <string name="print_error_default_message" msgid="8602678405502922346">"Samahani, imeshindwa kufanya kazi. Jaribu tena."</string>
     <string name="print_error_retry" msgid="1426421728784259538">"Jaribu tena"</string>
     <string name="print_error_printer_unavailable" msgid="8985614415253203381">"Printa hii haipatikani kwa sasa."</string>
     <string name="print_cannot_load_page" msgid="6179560924492912009">"Haiwezi kupakia onyesho la kuchungulia"</string>
diff --git a/packages/PrintSpooler/res/values-uz/strings.xml b/packages/PrintSpooler/res/values-uz/strings.xml
index 8921f8e..7552938 100644
--- a/packages/PrintSpooler/res/values-uz/strings.xml
+++ b/packages/PrintSpooler/res/values-uz/strings.xml
@@ -103,7 +103,7 @@
     <item msgid="3199660090246166812">"Eniga"</item>
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"Faylga yozib bo‘lmadi"</string>
-    <string name="print_error_default_message" msgid="8602678405502922346">"Kechirasiz, ishlamadi. Qayta urinib ko‘ring."</string>
+    <string name="print_error_default_message" msgid="8602678405502922346">"Xatolik yuz berdi. Qaytadan urining."</string>
     <string name="print_error_retry" msgid="1426421728784259538">"Qayta urinish"</string>
     <string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ushbu printer hozirda mavjud emas."</string>
     <string name="print_cannot_load_page" msgid="6179560924492912009">"Oldindan ko‘rsatib bo‘lmaydi"</string>
diff --git a/packages/PrintSpooler/res/values/colors.xml b/packages/PrintSpooler/res/values/colors.xml
index 68bc6f2..cb9e886 100644
--- a/packages/PrintSpooler/res/values/colors.xml
+++ b/packages/PrintSpooler/res/values/colors.xml
@@ -18,8 +18,6 @@
 
     <color name="print_preview_scrim_color">#99000000</color>
 
-    <color name="print_preview_background_color">#F2F1F2</color>
-
     <color name="unselected_page_background_color">#C0C0C0</color>
 
     <color name="material_grey_500">#ffa3a3a3</color>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index a968ffa..844e9c9 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -21,17 +21,14 @@
     </style>
 
     <style name="Theme.SelectPrinterActivity"
-           parent="android:style/Theme.DeviceDefault.Light.DarkActionBar">
+           parent="android:style/Theme.DeviceDefault.Light">
         <item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
     </style>
 
-    <style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault">
+    <style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault.Light">
         <item name="android:windowIsTranslucent">true</item>
-        <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:windowContentOverlay">@null</item>
         <item name="android:windowActionBar">false</item>
         <item name="android:windowNoTitle">true</item>
-        <item name="android:backgroundDimEnabled">false</item>
     </style>
 
 </resources>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index 9d737e0..abdfad5 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -166,7 +166,7 @@
      */
     private Action createCancelAction(PrintJobInfo printJob) {
         return new Action.Builder(
-                Icon.createWithResource(mContext, R.drawable.stat_notify_cancelling),
+                Icon.createWithResource(mContext, R.drawable.ic_clear),
                 mContext.getString(R.string.cancel), createCancelIntent(printJob)).build();
     }
 
@@ -225,7 +225,7 @@
 
     private void createFailedNotification(PrintJobInfo printJob) {
         Action.Builder restartActionBuilder = new Action.Builder(
-                Icon.createWithResource(mContext, R.drawable.ic_restart),
+                Icon.createWithResource(mContext, com.android.internal.R.drawable.ic_restart),
                 mContext.getString(R.string.restart), createRestartIntent(printJob.getId()));
 
         createNotification(printJob, createCancelAction(printJob), restartActionBuilder.build());
@@ -317,7 +317,7 @@
                 if (!printJob.isCancelling()) {
                     return com.android.internal.R.drawable.ic_print;
                 } else {
-                    return R.drawable.stat_notify_cancelling;
+                    return R.drawable.ic_clear;
                 }
             }
         }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 06fbf9f..59f272f 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -297,7 +297,7 @@
                     + " cannot be null");
         }
 
-        mCallingPackageName = extras.getString(DocumentsContract.EXTRA_PACKAGE_NAME);
+        mCallingPackageName = extras.getString(Intent.EXTRA_PACKAGE_NAME);
 
         if (savedInstanceState == null) {
             MetricsLogger.action(this, MetricsEvent.PRINT_PREVIEW, mCallingPackageName);
@@ -715,7 +715,7 @@
         Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
         intent.setType("application/pdf");
         intent.putExtra(Intent.EXTRA_TITLE, info.getName());
-        intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, mCallingPackageName);
+        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mCallingPackageName);
 
         try {
             startActivityForResult(intent, ACTIVITY_REQUEST_CREATE_FILE);
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
index ab03744..96012c1 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -26,6 +26,8 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_MIN_SDK_VERSION := 21
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 # For the test package.
diff --git a/packages/SettingsLib/res/layout-television/settings_with_drawer.xml b/packages/SettingsLib/res/layout-television/settings_with_drawer.xml
deleted file mode 100644
index e8ca691..0000000
--- a/packages/SettingsLib/res/layout-television/settings_with_drawer.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/content_frame"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" />
diff --git a/packages/SettingsLib/res/layout-watch/settings_with_drawer.xml b/packages/SettingsLib/res/layout-watch/settings_with_drawer.xml
deleted file mode 100644
index e8ca691..0000000
--- a/packages/SettingsLib/res/layout-watch/settings_with_drawer.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/content_frame"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" />
diff --git a/packages/SettingsLib/res/layout/preference_category_material_settings.xml b/packages/SettingsLib/res/layout/preference_category_material_settings.xml
deleted file mode 100644
index 1086106..0000000
--- a/packages/SettingsLib/res/layout/preference_category_material_settings.xml
+++ /dev/null
@@ -1,68 +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.
-  -->
-
-<!-- Based off frameworks/base/core/res/res/layout/preference_category_material.xml
-     except that this supports icon -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginTop="8dp"
-    android:layout_marginBottom="8dp"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart" >
-
-    <LinearLayout
-        android:id="@+id/icon_container"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:gravity="start|center_vertical"
-        android:orientation="horizontal">
-        <com.android.internal.widget.PreferenceImageView
-            android:id="@android:id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:tint="?android:attr/textColorPrimary"
-            android:maxWidth="18dp"
-            android:maxHeight="18dp"/>
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingStart="56dp"
-        android:orientation="vertical">
-        <TextView
-            android:id="@android:id/title"
-            android:layout_marginTop="16dp"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
-            android:textAppearance="@android:style/TextAppearance.Material.Body2"
-            android:textAlignment="viewStart"
-            android:textColor="?android:attr/colorAccent"
-            android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"/>
-        <TextView
-            android:id="@android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
-            android:textColor="?android:attr/textColorSecondary"
-            android:ellipsize="end"
-            android:singleLine="true" />
-    </LinearLayout>
-
-</FrameLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout/preference_category_material_settings_with_divider.xml b/packages/SettingsLib/res/layout/preference_category_material_settings_with_divider.xml
index 70d0898..5b5d474 100644
--- a/packages/SettingsLib/res/layout/preference_category_material_settings_with_divider.xml
+++ b/packages/SettingsLib/res/layout/preference_category_material_settings_with_divider.xml
@@ -24,5 +24,5 @@
     android:layout_height="wrap_content">
 
     <include layout="@layout/preference_category_divider"/>
-    <include layout="@layout/preference_category_material_settings"/>
-</LinearLayout>
\ No newline at end of file
+    <include layout="@layout/preference_category_material"/>
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout/settings_with_drawer.xml b/packages/SettingsLib/res/layout/settings_with_drawer.xml
deleted file mode 100644
index e1d5c0f..0000000
--- a/packages/SettingsLib/res/layout/settings_with_drawer.xml
+++ /dev/null
@@ -1,40 +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.
--->
-<!-- The main content view -->
-<LinearLayout
-    android:id="@+id/content_parent"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-    <Toolbar
-        android:id="@+id/action_bar"
-        style="?android:attr/actionBarStyle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:theme="?android:attr/actionBarTheme"
-        android:navigationContentDescription="@*android:string/action_bar_up_description" />
-    <FrameLayout
-        android:id="@+id/content_header_container"
-        style="?android:attr/actionBarStyle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
-    <FrameLayout
-        android:id="@+id/content_frame"
-        android:layout_width="match_parent"
-        android:layout_height="fill_parent"
-        android:background="?android:attr/windowBackground" />
-</LinearLayout>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 9bacaf33..0887010 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"op <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Tydsduur"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vra elke keer"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Sopas"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 329e9fe..b69618d 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"በ<xliff:g id="WHEN">%1$s</xliff:g> ላይ"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"የቆይታ ጊዜ"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ሁልጊዜ ጠይቅ"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"ልክ አሁን"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 846f5d8..37e8346 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -453,5 +453,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"يوم <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"المدة"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"الطلب في كل مرة"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"للتو"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index d8137b4..787d4d5 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -224,8 +224,7 @@
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"ব্লুটুথ অডিঅ\' চ্চেনেল ম\'ড"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="7234956835280563341">"ব্লুটুথ অডিঅ\' ক\'ডেকৰ বাছনি\nআৰম্ভ কৰক: চ্চেনেল ম\'ড"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ব্লুটুথ অডিঅ’ LDAC ক’ডেক: পৰিৱেশনৰ মান"</string>
-    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (6893955536658137179) -->
-    <skip />
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="6893955536658137179">"ব্লুটুথ অডিঅ\' LDAC\nক\'ডেক বাছনি আৰম্ভ কৰক: প্লেবেকৰ গুণগত মান"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ষ্ট্ৰীম কৰি থকা হৈছে: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"ব্যক্তিগত DNS"</string>
     <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"ব্যক্তিগত DNS ম\'ড বাছনি কৰক"</string>
@@ -366,18 +365,12 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"ৰং শুধৰণী"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"এই সুবিধাটো পৰীক্ষামূলক, সেয়ে ই কাৰ্যক্ষমতাৰ ওপৰত প্ৰভাৱ পেলাব পাৰে।"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g>ৰ দ্বাৰা অগ্ৰাহ্য কৰা হৈছে"</string>
-    <!-- no translation found for power_remaining_settings_home_page (4845022416859002011) -->
-    <skip />
-    <!-- no translation found for power_remaining_duration_only (6123167166221295462) -->
-    <skip />
-    <!-- no translation found for power_discharging_duration (8848256785736335185) -->
-    <skip />
-    <!-- no translation found for power_remaining_duration_only_enhanced (4189311599812296592) -->
-    <skip />
-    <!-- no translation found for power_discharging_duration_enhanced (1992003260664804080) -->
-    <skip />
-    <!-- no translation found for power_remaining_duration_only_short (3463575350656389957) -->
-    <skip />
+    <string name="power_remaining_settings_home_page" msgid="4845022416859002011">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
+    <string name="power_remaining_duration_only" msgid="6123167166221295462">"প্রায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে"</string>
+    <string name="power_discharging_duration" msgid="8848256785736335185">"প্রায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+    <string name="power_remaining_duration_only_enhanced" msgid="4189311599812296592">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি প্ৰায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে"</string>
+    <string name="power_discharging_duration_enhanced" msgid="1992003260664804080">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি প্রায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+    <string name="power_remaining_duration_only_short" msgid="3463575350656389957">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে"</string>
     <string name="power_discharge_by_enhanced" msgid="2095821536747992464">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -456,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g> বজাত"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"সময়সীমা"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"প্ৰতিবাৰতে সোধক"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"এই মাত্ৰ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 5a49c62..e321126 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g> olduqda"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Müddət"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Hər dəfə soruşun"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"İndicə"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 75999a4..b4ea7b9 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -450,5 +450,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Trajanje"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Uvek pitaj"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo sada"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 6541383..630d697 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -451,5 +451,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Працягласць"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Заўсёды пытацца"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Зараз"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 6761140..aa42e0a 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"в/ъв <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Времетраене"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Да се пита винаги"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Току-що"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 98a95c2..c11b58e 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -278,7 +278,7 @@
     <string name="media_category" msgid="4388305075496848353">"মিডিয়া"</string>
     <string name="debug_monitoring_category" msgid="7640508148375798343">"পর্যবেক্ষণে রাখা"</string>
     <string name="strict_mode" msgid="1938795874357830695">"কঠোর মোড সক্ষম"</string>
-    <string name="strict_mode_summary" msgid="142834318897332338">"মুখ্য থ্রেডে অ্যাপ্লিকেশানগুলির দীর্ঘ কার্যকলাপের সময় স্ক্রিন ফ্ল্যাশ করে"</string>
+    <string name="strict_mode_summary" msgid="142834318897332338">"মুখ্য থ্রেডে অ্যাপ্লিকেশানগুলির দীর্ঘ অ্যাক্টিভিটির সময় স্ক্রিন ফ্ল্যাশ করে"</string>
     <string name="pointer_location" msgid="6084434787496938001">"পয়েন্টারের লোকেশন"</string>
     <string name="pointer_location_summary" msgid="840819275172753713">"স্ক্রিন ওভারলে বর্তমান স্পর্শ ডেটা দেখাচ্ছে"</string>
     <string name="show_touches" msgid="2642976305235070316">"আলতো চাপ দেখান"</string>
@@ -302,7 +302,7 @@
     <string name="force_rtl_layout_all_locales_summary" msgid="9192797796616132534">"সমস্ত স্থানের জন্য RTL এ স্ক্রিন লেআউট দিকনির্দেশে জোর দেয়"</string>
     <string name="force_msaa" msgid="7920323238677284387">"4x MSAA এ জোর দিন"</string>
     <string name="force_msaa_summary" msgid="9123553203895817537">"OpenGL ES 2.0 অ্যাপ্লিকেশানগুলির মধ্যে 4x MSAA সক্রিয় করুন"</string>
-    <string name="show_non_rect_clip" msgid="505954950474595172">"অ-আয়তক্ষেত্রাকার ক্লিপ কার্যকলাপ ডিবাগ করুন"</string>
+    <string name="show_non_rect_clip" msgid="505954950474595172">"অ-আয়তক্ষেত্রাকার ক্লিপ অ্যাক্টিভিটি ডিবাগ করুন"</string>
     <string name="track_frame_time" msgid="6094365083096851167">"প্রোফাইল HWUI রেন্ডারিং"</string>
     <string name="enable_gpu_debug_layers" msgid="3848838293793255097">"GPU ডিবাগ স্তর সক্ষম করুন"</string>
     <string name="enable_gpu_debug_layers_summary" msgid="8009136940671194940">"ডিবাগ অ্যাপের জন্য GPU ডিবাগ স্তর লোড হতে দিন"</string>
@@ -311,8 +311,8 @@
     <string name="animator_duration_scale_title" msgid="3406722410819934083">"অ্যানিমেটর সময়কাল স্কেল"</string>
     <string name="overlay_display_devices_title" msgid="5364176287998398539">"গৌণ প্রদর্শনগুলি নকল করুন"</string>
     <string name="debug_applications_category" msgid="4206913653849771549">"অ্যাপ"</string>
-    <string name="immediately_destroy_activities" msgid="1579659389568133959">"কার্যকলাপ রাখবেন না"</string>
-    <string name="immediately_destroy_activities_summary" msgid="3592221124808773368">"ব্যবহারকারী এটি ছেড়ে যাওয়ার পরে যত তাড়াতাড়ি সম্ভব প্রতিটি কার্যকলাপ ধ্বংস করুন"</string>
+    <string name="immediately_destroy_activities" msgid="1579659389568133959">"অ্যাক্টিভিটি রাখবেন না"</string>
+    <string name="immediately_destroy_activities_summary" msgid="3592221124808773368">"ব্যবহারকারী এটি ছেড়ে যাওয়ার পরে যত তাড়াতাড়ি সম্ভব প্রতিটি অ্যাক্টিভিটি ধ্বংস করুন"</string>
     <string name="app_process_limit_title" msgid="4280600650253107163">"পশ্চাদপট প্রক্রিয়ার সীমা"</string>
     <string name="show_all_anrs" msgid="4924885492787069007">"ব্যাকগ্রাউন্ডের ANR দেখুন"</string>
     <string name="show_all_anrs_summary" msgid="6636514318275139826">"ব্যাকগ্রাউন্ডের অ্যাপগুলির জন্য \'অ্যাপ থেকে সাড়া পাওয়া যাচ্ছে না\' মেসেজ দেখান"</string>
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"তারিখ ও সময় <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"সময়কাল"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"প্রতিবার জিজ্ঞেস করা হবে"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"এখনই"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml
index 8577779..9b3c38c 100644
--- a/packages/SettingsLib/res/values-bs/arrays.xml
+++ b/packages/SettingsLib/res/values-bs/arrays.xml
@@ -27,7 +27,7 @@
     <item msgid="515055375277271756">"Autentifikacija…"</item>
     <item msgid="1943354004029184381">"Dobivanje IP adrese…"</item>
     <item msgid="4221763391123233270">"Povezano"</item>
-    <item msgid="624838831631122137">"Suspendiran"</item>
+    <item msgid="624838831631122137">"Suspendirano"</item>
     <item msgid="7979680559596111948">"Prekidanje veze…"</item>
     <item msgid="1634960474403853625">"Isključen"</item>
     <item msgid="746097431216080650">"Neuspješno"</item>
@@ -38,11 +38,11 @@
     <item msgid="7714855332363650812"></item>
     <item msgid="8878186979715711006">"Skeniranje…"</item>
     <item msgid="355508996603873860">"Povezivanje na mrežu <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
-    <item msgid="554971459996405634">"Autentifikacija sa mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+    <item msgid="554971459996405634">"Autentifikacija s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
     <item msgid="7928343808033020343">"Dobivanje IP adrese iz mreže <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
-    <item msgid="8937994881315223448">"Povezan na mrežu <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
-    <item msgid="1330262655415760617">"Suspendiran"</item>
-    <item msgid="7698638434317271902">"Prekidanje veze sa mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+    <item msgid="8937994881315223448">"Povezano na mrežu <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+    <item msgid="1330262655415760617">"Suspendirano"</item>
+    <item msgid="7698638434317271902">"Prekidanje veze s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
     <item msgid="197508606402264311">"Isključen"</item>
     <item msgid="8578370891960825148">"Neuspješno"</item>
     <item msgid="5660739516542454527">"Blokirano"</item>
@@ -59,7 +59,7 @@
     <item msgid="45075631231212732">"Uvijek koristi HDCP provjeru"</item>
   </string-array>
   <string-array name="bluetooth_avrcp_versions">
-    <item msgid="5347678900838034763">"AVRCP 1.4 (Zadano)"</item>
+    <item msgid="5347678900838034763">"AVRCP 1.4 (zadano)"</item>
     <item msgid="2809759619990248160">"AVRCP 1.3"</item>
     <item msgid="6199178154704729352">"AVRCP 1.5"</item>
     <item msgid="5172170854953034852">"AVRCP 1.6"</item>
@@ -71,58 +71,58 @@
     <item msgid="3422726142222090896">"avrcp16"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="7065842274271279580">"Koristi odabir sistema (Zadano)"</item>
+    <item msgid="7065842274271279580">"Koristi odabir sistema (zadano)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="686685526567131661">"AAC"</item>
     <item msgid="5254942598247222737">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
     <item msgid="2091430979086738145">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
     <item msgid="6751080638867012696">"LDAC"</item>
-    <item msgid="723675059572222462">"Omogućite opcionalne kodeke"</item>
-    <item msgid="3304843301758635896">"Onemogućite opcionalne kodeke"</item>
+    <item msgid="723675059572222462">"Omogući opcionalne kodeke"</item>
+    <item msgid="3304843301758635896">"Onemogući opcionalne kodeke"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="5062108632402595000">"Koristi odabir sistema (Zadano)"</item>
+    <item msgid="5062108632402595000">"Koristi odabir sistema (zadano)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="6839647709301342559">"AAC"</item>
     <item msgid="7848030269621918608">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
     <item msgid="298198075927343893">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
     <item msgid="7950781694447359344">"LDAC"</item>
-    <item msgid="2209680154067241740">"Omogućite opcionalne kodeke"</item>
-    <item msgid="741805482892725657">"Onemogućite opcionalne kodeke"</item>
+    <item msgid="2209680154067241740">"Omogući opcionalne kodeke"</item>
+    <item msgid="741805482892725657">"Onemogući opcionalne kodeke"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="3093023430402746802">"Koristi odabir sistema (Zadano)"</item>
+    <item msgid="3093023430402746802">"Koristi odabir sistema (zadano)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="3214516120190965356">"Koristi odabir sistema (Zadano)"</item>
+    <item msgid="3214516120190965356">"Koristi odabir sistema (zadano)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="2684127272582591429">"Koristi odabir sistema (Zadano)"</item>
+    <item msgid="2684127272582591429">"Koristi odabir sistema (zadano)"</item>
     <item msgid="5618929009984956469">"16 bitova/uzorak"</item>
     <item msgid="3412640499234627248">"24 bitova/uzorak"</item>
     <item msgid="121583001492929387">"32 bitova/uzorak"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="1081159789834584363">"Koristi odabir sistema (Zadano)"</item>
+    <item msgid="1081159789834584363">"Koristi odabir sistema (zadano)"</item>
     <item msgid="4726688794884191540">"16 bitova/uzorak"</item>
     <item msgid="305344756485516870">"24 bitova/uzorak"</item>
     <item msgid="244568657919675099">"32 bitova/uzorak"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="5226878858503393706">"Koristi odabir sistema (Zadano)"</item>
+    <item msgid="5226878858503393706">"Koristi odabir sistema (zadano)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="4118561796005528173">"Koristi odabir sistema (Zadano)"</item>
+    <item msgid="4118561796005528173">"Koristi odabir sistema (zadano)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
@@ -130,13 +130,13 @@
     <item msgid="7158319962230727476">"Optimizirano za kvalitet zvuka (990 kbps/909 kbps)"</item>
     <item msgid="2921767058740704969">"Uravnotežen kvalitet zvuka i veze (660kbps/606kbps)"</item>
     <item msgid="8860982705384396512">"Optimizirano za kvalitet veze (330 kbps/303 kbps)"</item>
-    <item msgid="4414060457677684127">"Maksimalan napor (Prilagodljiva brzina prijenosa)"</item>
+    <item msgid="4414060457677684127">"Maksimalan napor (prilagodljiva brzina prijenosa)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
     <item msgid="6398189564246596868">"Optimizirano za kvalitet zvuka"</item>
     <item msgid="4327143584633311908">"Uravnotežen kvalitet zvuka i veze"</item>
     <item msgid="4681409244565426925">"Optimizirano za kvalitet veze"</item>
-    <item msgid="364670732877872677">"Maksimalan napor (Prilagodljiva brzina prijenosa)"</item>
+    <item msgid="364670732877872677">"Maksimalan napor (prilagodljiva brzina prijenosa)"</item>
   </string-array>
   <string-array name="bluetooth_audio_active_device_summaries">
     <item msgid="4862957058729193940"></item>
@@ -169,7 +169,7 @@
   <string-array name="select_logpersist_titles">
     <item msgid="1744840221860799971">"Isključeno"</item>
     <item msgid="3054662377365844197">"Sve"</item>
-    <item msgid="688870735111627832">"Svi osim radija"</item>
+    <item msgid="688870735111627832">"Sve osim radija"</item>
     <item msgid="2850427388488887328">"samo kernel"</item>
   </string-array>
   <string-array name="select_logpersist_summaries">
@@ -208,13 +208,13 @@
   <string-array name="overlay_display_devices_entries">
     <item msgid="1606809880904982133">"Nema"</item>
     <item msgid="9033194758688161545">"480p"</item>
-    <item msgid="1025306206556583600">"480p (osiguran)"</item>
+    <item msgid="1025306206556583600">"480p (sigurno)"</item>
     <item msgid="1853913333042744661">"720p"</item>
-    <item msgid="3414540279805870511">"720p (osiguran)"</item>
+    <item msgid="3414540279805870511">"720p (sigurno)"</item>
     <item msgid="9039818062847141551">"1080p"</item>
-    <item msgid="4939496949750174834">"1080p (osiguran)"</item>
+    <item msgid="4939496949750174834">"1080p (sigurno)"</item>
     <item msgid="1833612718524903568">"4K"</item>
-    <item msgid="238303513127879234">"4K (osiguran)"</item>
+    <item msgid="238303513127879234">"4K (sigurno)"</item>
     <item msgid="3547211260846843098">"4K (povećava rezoluciju)"</item>
     <item msgid="5411365648951414254">"4K (povećava rezoluciju, osiguran)"</item>
     <item msgid="1311305077526792901">"720p, 1080p (dupli ekran)"</item>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index b8195b1..887a74b 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -43,7 +43,7 @@
     <string name="wifi_status_no_internet" msgid="5784710974669608361">"Nema internetske veze"</string>
     <string name="wifi_status_sign_in_required" msgid="123517180404752756">"Potrebna je prijava"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Pristupna tačka je privremeno puna"</string>
-    <string name="connected_via_carrier" msgid="7583780074526041912">"Povezana koristeći %1$s"</string>
+    <string name="connected_via_carrier" msgid="7583780074526041912">"Povezano koristeći %1$s"</string>
     <string name="available_via_carrier" msgid="1469036129740799053">"Dostupna koristeći %1$s"</string>
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Veoma sporo"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Sporo"</string>
@@ -84,11 +84,11 @@
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Povezano na slušni aparat"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Povezano sa zvukom medija"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Povezano na zvuk telefona"</string>
-    <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezan na server za prijenos podataka"</string>
+    <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezano sa serverom za prijenos podataka"</string>
     <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Povezano na mapu"</string>
     <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Povezan na SAP"</string>
-    <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Nije povezan na server za prijenos podataka"</string>
-    <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Spojen na ulazni uređaj"</string>
+    <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Nije povezano sa serverom za prijenos podataka"</string>
+    <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Povezano s ulaznim uređajem"</string>
     <string name="bluetooth_pan_user_profile_summary_connected" msgid="6436258151814414028">"Povezano na uređaj za pristup internetu"</string>
     <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1322694224800769308">"Dijeljenje lokalne internetske veze s uređajem"</string>
     <string name="bluetooth_pan_profile_summary_use_for" msgid="5736111170225304239">"Koristi za pristup internetu"</string>
@@ -102,10 +102,10 @@
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Upari"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"UPARI"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Otkaži"</string>
-    <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Uparivanje odobrava pristup kontaktima i istoriji poziva kada je uspostavljeno."</string>
-    <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Nije se moguće upariti s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
-    <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Nije se moguće upariti s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> zbog pogrešnog PIN-a ili pristupnog koda."</string>
-    <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Ne može komunicirati sa uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Uparivanje odobrava pristup kontaktima i historiji poziva kada je uspostavljeno."</string>
+    <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Nije moguće upariti s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Nije moguće upariti s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> zbog pogrešnog PIN-a ili pristupnog koda."</string>
+    <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Nije moguće komunicirati s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Uređaj <xliff:g id="DEVICE_NAME">%1$s</xliff:g> je odbio uparivanje."</string>
     <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Računar"</string>
     <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Slušalice s mikrofonom"</string>
@@ -118,12 +118,12 @@
     <string name="bluetooth_hearingaid_right_pairing_message" msgid="1550373802309160891">"Uparivanje desnog slušnog aparata…"</string>
     <string name="bluetooth_hearingaid_left_battery_level" msgid="8797811465352097562">"Lijevi - <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije"</string>
     <string name="bluetooth_hearingaid_right_battery_level" msgid="7309476148173459677">"Desni - <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije"</string>
-    <string name="accessibility_wifi_off" msgid="1166761729660614716">"WiFi isključen."</string>
+    <string name="accessibility_wifi_off" msgid="1166761729660614716">"WiFi je isključen."</string>
     <string name="accessibility_no_wifi" msgid="8834610636137374508">"WiFi nije povezan."</string>
-    <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"WiFi jedna crtica."</string>
-    <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"WiFi dvije crtice."</string>
-    <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"WiFi tri crtice."</string>
-    <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"WiFi puni signal."</string>
+    <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"WiFi signal ima jednu crtu."</string>
+    <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"WiFi signal ima dvije crte."</string>
+    <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"WiFi signal ima tri crte."</string>
+    <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"WiFi signal je pun."</string>
     <string name="accessibility_wifi_security_type_none" msgid="1223747559986205423">"Otvorena mreža"</string>
     <string name="accessibility_wifi_security_type_secured" msgid="862921720418885331">"Sigurna mreža"</string>
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
@@ -132,7 +132,7 @@
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"Povezivanje mobitela USB-om"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prijenosna pristupna tačka"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Dijeljenje Bluetooth veze"</string>
-    <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Dijeljenje veze"</string>
+    <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Povezivanje putem mobitela"</string>
     <string name="tether_settings_title_all" msgid="8356136101061143841">"Povezivanje putem mobitela i prijenosna pristupna tačka"</string>
     <string name="managed_user_title" msgid="8109605045406748842">"Sve radne aplikacije"</string>
     <string name="user_guest" msgid="8475274842845401871">"Gost"</string>
@@ -145,7 +145,7 @@
     <string name="tts_default_rate_title" msgid="6030550998379310088">"Brzina govora"</string>
     <string name="tts_default_rate_summary" msgid="4061815292287182801">"Brzina kojom se izgovara tekst"</string>
     <string name="tts_default_pitch_title" msgid="6135942113172488671">"Visina"</string>
-    <string name="tts_default_pitch_summary" msgid="1944885882882650009">"Utječe na ton sintetiziranog govora"</string>
+    <string name="tts_default_pitch_summary" msgid="1944885882882650009">"Utiče na ton sintetiziranog govora"</string>
     <string name="tts_default_lang_title" msgid="8018087612299820556">"Jezik"</string>
     <string name="tts_lang_use_system" msgid="2679252467416513208">"Korištenje sistemskog jezika"</string>
     <string name="tts_lang_not_selected" msgid="7395787019276734765">"Jezik nije izabran"</string>
@@ -187,7 +187,7 @@
     <string name="development_settings_summary" msgid="1815795401632854041">"Postavi opcije za razvoj aplikacija"</string>
     <string name="development_settings_not_available" msgid="4308569041701535607">"Opcije za programere nisu dostupne za ovog korisnika"</string>
     <string name="vpn_settings_not_available" msgid="956841430176985598">"VPN postavke nisu dostupne za ovog korisnika"</string>
-    <string name="tethering_settings_not_available" msgid="6765770438438291012">"Postavke za privezivanje nisu dostupne za ovog korisnika"</string>
+    <string name="tethering_settings_not_available" msgid="6765770438438291012">"Postavke za povezivanje putem mobitela nisu dostupne za ovog korisnika"</string>
     <string name="apn_settings_not_available" msgid="7873729032165324000">"Postavke za ime pristupne tačke nisu dostupne za ovog korisnika"</string>
     <string name="enable_adb" msgid="7982306934419797485">"Otklanjanje grešaka putem uređaja spojenog na USB"</string>
     <string name="enable_adb_summary" msgid="4881186971746056635">"Način rada za uklanjanje grešaka kada je povezan USB"</string>
@@ -201,7 +201,7 @@
     <string name="oem_unlock_enable" msgid="6040763321967327691">"OEM otključavanje"</string>
     <string name="oem_unlock_enable_summary" msgid="4720281828891618376">"Dozvoli otključavanje bootloadera"</string>
     <string name="confirm_enable_oem_unlock_title" msgid="4802157344812385674">"Želite li dozvoliti OEM otključavanje?"</string>
-    <string name="confirm_enable_oem_unlock_text" msgid="5517144575601647022">"UPOZORENJE: Funkcije zaštite ovog uređaja neće funkcionisati dok je ova postavka uključena."</string>
+    <string name="confirm_enable_oem_unlock_text" msgid="5517144575601647022">"UPOZORENJE: Funkcije zaštite ovog uređaja neće funkcionirati dok je ova postavka uključena."</string>
     <string name="mock_location_app" msgid="7966220972812881854">"Odaberite aplikaciju za lažne lokacije"</string>
     <string name="mock_location_app_not_set" msgid="809543285495344223">"Aplikacija za lažnu lokaciju nije postavljena"</string>
     <string name="mock_location_app_set" msgid="8966420655295102685">"Aplikacija za lažne lokacije: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -210,7 +210,7 @@
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Omogući detaljniju evidenciju za WiFi"</string>
     <string name="wifi_connected_mac_randomization" msgid="3168165236877957767">"Nasumični odabir MAC adrese pri povezivanju"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobilna mreža za prijenos podataka je uvijek aktivna"</string>
-    <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardversko ubrzavanje dijeljenja veze"</string>
+    <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardversko ubrzavanje za povezivanje putem mobitela"</string>
     <string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Prikaži Bluetooth uređaje bez naziva"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Onemogući apsolutnu jačinu zvuka"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="3750059931120293633">"Bluetooth AVRCP verzija"</string>
@@ -220,7 +220,7 @@
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Brzina uzorkovanja za Bluetooth audio"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="8010380028880963535">"Aktivirajte Bluetooth Audio Codec\nOdabir: Brzina uzorkovanja"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth audio bitovi po uzorku"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="8063859754619484760">"Aktivirajte Bluetooth Audio Codec\nOdabir: Bitovi po semplu"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="8063859754619484760">"Aktivirajte Bluetooth Audio Codec\nOdabir: Bitovi po uzorku"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Način Bluetooth audio kanala"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="7234956835280563341">"Aktivirajte Bluetooth Audio Codec\nOdabir: Način rada po kanalima"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth Audio LDAC kodek: Kvalitet reprodukcije"</string>
@@ -249,7 +249,7 @@
     <string name="allow_mock_location" msgid="2787962564578664888">"Dozvoli lažne lokacije"</string>
     <string name="allow_mock_location_summary" msgid="317615105156345626">"Dozvoli lažne lokacije"</string>
     <string name="debug_view_attributes" msgid="6485448367803310384">"Omogući pregled atributa prikaza"</string>
-    <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Uvijek drži prijenos podataka na mobilnoj mreži aktivnim, čak i kada je WiFi je aktivan (za brzo prebacivanje između mreža)."</string>
+    <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Uvijek drži prijenos podataka na mobilnoj mreži aktivnim, čak i kada je WiFi aktivan (za brzo prebacivanje između mreža)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Koristi hardversko ubrzavanje dijeljenja veze, ako je dostupno"</string>
     <string name="adb_warning_title" msgid="6234463310896563253">"Omogućiti otklanjanje grešaka putem uređaja spojenog na USB?"</string>
     <string name="adb_warning_message" msgid="7316799925425402244">"Otklanjanje grešaka putem uređaja spojenog na USB je namijenjeno samo u svrhe razvoja aplikacija. Koristite ga za kopiranje podataka između računara i uređaja, instaliranje aplikacija na uređaj bez obavještenja te čitanje podataka iz zapisnika."</string>
@@ -277,7 +277,7 @@
     <string name="debug_hw_drawing_category" msgid="6220174216912308658">"Prikaz s hardverskom akceleracijom"</string>
     <string name="media_category" msgid="4388305075496848353">"Mediji"</string>
     <string name="debug_monitoring_category" msgid="7640508148375798343">"Praćenje"</string>
-    <string name="strict_mode" msgid="1938795874357830695">"Omogućen strogi režim"</string>
+    <string name="strict_mode" msgid="1938795874357830695">"Omogućen strogi način rada"</string>
     <string name="strict_mode_summary" msgid="142834318897332338">"Prikaži ekran uz treptanje kada aplikacije vrše duge operacije u glavnoj niti"</string>
     <string name="pointer_location" msgid="6084434787496938001">"Lokacija pokazivača"</string>
     <string name="pointer_location_summary" msgid="840819275172753713">"Trenutni podaci o dodirivanju prikazuju se u nadsloju preko ekrana"</string>
@@ -298,8 +298,8 @@
     <string name="usb_audio_disable_routing_summary" msgid="980282760277312264">"Onemogući autom. usmjerav. na USB audio periferije"</string>
     <string name="debug_layout" msgid="5981361776594526155">"Prikaži granice rasporeda"</string>
     <string name="debug_layout_summary" msgid="2001775315258637682">"Prikaži granice isječka, margine itd."</string>
-    <string name="force_rtl_layout_all_locales" msgid="2259906643093138978">"Prisilno postavi raspored s desna u lijevo"</string>
-    <string name="force_rtl_layout_all_locales_summary" msgid="9192797796616132534">"Prisilno postavi raspored ekrana s desna u lijevo za sve regije"</string>
+    <string name="force_rtl_layout_all_locales" msgid="2259906643093138978">"Prisilno postavi raspored s desna ulijevo"</string>
+    <string name="force_rtl_layout_all_locales_summary" msgid="9192797796616132534">"Prisilno postavi raspored ekrana s desna ulijevo za sve regije"</string>
     <string name="force_msaa" msgid="7920323238677284387">"Prinudno primijeni 4x MSAA"</string>
     <string name="force_msaa_summary" msgid="9123553203895817537">"Omogući 4x MSAA u OpenGL ES 2.0 aplikacijama"</string>
     <string name="show_non_rect_clip" msgid="505954950474595172">"Ispravi greške na nepravougaonim operacijama isjecanja"</string>
@@ -321,19 +321,19 @@
     <string name="force_allow_on_external" msgid="3215759785081916381">"Nametni aplikacije na vanjskoj pohrani"</string>
     <string name="force_allow_on_external_summary" msgid="3640752408258034689">"Omogućava upisivanje svih aplikacija u vanjsku pohranu, bez obzira na prikazane vrijednosti"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Nametni aktivnostima mijenjanje veličina"</string>
-    <string name="force_resizable_activities_summary" msgid="6667493494706124459">"Omogući mijenjanje veličine svih aktivnosti za prikaz sa više prozora, bez obzira na prikazane vrijednosti."</string>
+    <string name="force_resizable_activities_summary" msgid="6667493494706124459">"Omogući mijenjanje veličine svih aktivnosti za prikaz s više prozora, bez obzira na prikazane vrijednosti."</string>
     <string name="enable_freeform_support" msgid="1461893351278940416">"Omogući prozore nepravilnih oblika"</string>
     <string name="enable_freeform_support_summary" msgid="8247310463288834487">"Omogući podršku za eksperimentalne prozore nepravilnih oblika."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Lozinka za sigurnosnu kopiju radne površine"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Potpune sigurnosne kopije za računare trenutno nisu zaštićene"</string>
-    <string name="local_backup_password_summary_change" msgid="5376206246809190364">"Dodirnite da promijenite ili uklonite lozinku za potpune rezervne kopije sa radne površine"</string>
-    <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nova lozinka za sigurnosnu kopiju postavljena"</string>
+    <string name="local_backup_password_summary_change" msgid="5376206246809190364">"Dodirnite da promijenite ili uklonite lozinku za potpune rezervne kopije s radne površine"</string>
+    <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nova lozinka za sigurnosnu kopiju je postavljena"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nova lozinka i potvrda se ne podudaraju"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Nije uspjelo postavljanje lozinke za sigurnosnu kopiju"</string>
   <string-array name="color_mode_names">
-    <item msgid="2425514299220523812">"Živopisan (zadano)"</item>
+    <item msgid="2425514299220523812">"Živopisno (zadano)"</item>
     <item msgid="8446070607501413455">"Prirodan"</item>
-    <item msgid="6553408765810699025">"Standardni"</item>
+    <item msgid="6553408765810699025">"Standardno"</item>
   </string-array>
   <string-array name="color_mode_descriptions">
     <item msgid="4979629397075120893">"Unaprijeđene boje"</item>
@@ -353,9 +353,9 @@
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Pretvaranje…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fajl je već šifriran"</string>
     <string name="title_convert_fbe" msgid="1263622876196444453">"Pretvaranje u šifrirane fajlove"</string>
-    <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Pretvorite particiju sa podacima u particiju šifriranu sistemom fajlova.\n !! Upozorenje!! Ovo će izbrisati sve vaše podatke.\n Ova funkcija je u alfa fazi razvoja i možda neće ispravno raditi.\n Pritisnite \"Obriši i pretvori…\" da nastavite."</string>
+    <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Pretvorite particiju s podacima u particiju šifriranu sistemom fajlova.\n !! Upozorenje!! Ovo će izbrisati sve vaše podatke.\n Ova funkcija je u alfa fazi razvoja i možda neće ispravno raditi.\n Pritisnite \"Obriši i pretvori…\" da nastavite."</string>
     <string name="button_convert_fbe" msgid="5152671181309826405">"Obriši i pretvori…"</string>
-    <string name="picture_color_mode" msgid="4560755008730283695">"Režim boja Slika"</string>
+    <string name="picture_color_mode" msgid="4560755008730283695">"Način rada boja slika"</string>
     <string name="picture_color_mode_desc" msgid="1141891467675548590">"Koristi sRGB"</string>
     <string name="daltonizer_mode_disabled" msgid="7482661936053801862">"Onemogućeno"</string>
     <string name="daltonizer_mode_monochromacy" msgid="8485709880666106721">"Crno-bijelo"</string>
@@ -422,8 +422,8 @@
     <string name="retail_demo_reset_title" msgid="696589204029930100">"Potrebna je lozinka"</string>
     <string name="active_input_method_subtypes" msgid="3596398805424733238">"Aktivne metode unosa"</string>
     <string name="use_system_language_to_select_input_method_subtypes" msgid="5747329075020379587">"Koristi jezik sistema"</string>
-    <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nije uspjelo otvaranje postavki za <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
-    <string name="ime_security_warning" msgid="4135828934735934248">"Ovaj način unosa može prikupiti sav tekst koji otkucate, uključujući lične podatke kao što su lozinke i brojevi kreditnih kartica. Način omogućava aplikacija <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Da li želite koristiti ovaj način unosa?"</string>
+    <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Otvaranje postavki za <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> nije uspjelo"</string>
+    <string name="ime_security_warning" msgid="4135828934735934248">"Ovaj način unosa može prikupiti sav tekst koji upišete, uključujući lične podatke kao što su lozinke i brojevi kreditnih kartica. Način omogućava aplikacija <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Da li želite koristiti ovaj način unosa?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Napomena: Nakon ponovnog pokretanja, ova aplikacija se neće moći pokrenuti dok ne otključate telefon"</string>
     <string name="ims_reg_title" msgid="7609782759207241443">"Stanje IMS registracije"</string>
     <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrirano"</string>
@@ -450,5 +450,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Trajanje"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pitaj svaki put"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 2488f55..7d35095 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -189,9 +189,9 @@
     <string name="vpn_settings_not_available" msgid="956841430176985598">"La configuració de la VPN no està disponible per a aquest usuari."</string>
     <string name="tethering_settings_not_available" msgid="6765770438438291012">"La configuració de compartició de xarxa no està disponible per a aquest usuari."</string>
     <string name="apn_settings_not_available" msgid="7873729032165324000">"La configuració del nom del punt d\'accés no està disponible per a aquest usuari."</string>
-    <string name="enable_adb" msgid="7982306934419797485">"Depuració USB"</string>
+    <string name="enable_adb" msgid="7982306934419797485">"Depuració per USB"</string>
     <string name="enable_adb_summary" msgid="4881186971746056635">"Activa el mode de depuració quan el dispositiu estigui connectat per USB"</string>
-    <string name="clear_adb_keys" msgid="4038889221503122743">"Revoca autoritzacions de depuració USB"</string>
+    <string name="clear_adb_keys" msgid="4038889221503122743">"Revoca autoritzacions de depuració per USB"</string>
     <string name="bugreport_in_power" msgid="7923901846375587241">"Drecera per a informe d\'errors"</string>
     <string name="bugreport_in_power_summary" msgid="1778455732762984579">"Mostra un botó al menú d\'engegada per crear un informe d\'errors"</string>
     <string name="keep_screen_on" msgid="1146389631208760344">"Pantalla sempre activa"</string>
@@ -251,9 +251,9 @@
     <string name="debug_view_attributes" msgid="6485448367803310384">"Inspecció d\'atributs de visualització"</string>
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mantén les dades mòbils sempre actives, fins i tot quan la Wi‑Fi està activada (per canviar de xarxa ràpidament)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Fes servir l\'acceleració per maquinari per compartir la xarxa, si està disponible"</string>
-    <string name="adb_warning_title" msgid="6234463310896563253">"Voleu permetre la depuració USB?"</string>
-    <string name="adb_warning_message" msgid="7316799925425402244">"La depuració USB només està indicada per a activitats de desenvolupament. Fes-la servir intercanviar dades entre l\'ordinador i el dispositiu, per instal·lar aplicacions al dispositiu sense rebre notificacions i per llegir dades de registre."</string>
-    <string name="adb_keys_warning_message" msgid="5659849457135841625">"Vols revocar l\'accés a la depuració d\'USB dels ordinadors que has autoritzat anteriorment?"</string>
+    <string name="adb_warning_title" msgid="6234463310896563253">"Voleu permetre la depuració per USB?"</string>
+    <string name="adb_warning_message" msgid="7316799925425402244">"La depuració per USB només està indicada per a activitats de desenvolupament. Fes-la servir intercanviar dades entre l\'ordinador i el dispositiu, per instal·lar aplicacions al dispositiu sense rebre notificacions i per llegir dades de registre."</string>
+    <string name="adb_keys_warning_message" msgid="5659849457135841625">"Vols revocar l\'accés a la depuració per USB dels ordinadors que has autoritzat anteriorment?"</string>
     <string name="dev_settings_warning_title" msgid="7244607768088540165">"Vols permetre la conf. de desenvolupament?"</string>
     <string name="dev_settings_warning_message" msgid="2298337781139097964">"Aquesta configuració només està prevista per a usos de desenvolupament. Pot fer que el dispositiu i que les aplicacions s\'interrompin o tinguin un comportament inadequat."</string>
     <string name="verify_apps_over_usb_title" msgid="4177086489869041953">"Verifica aplicacions per USB"</string>
@@ -398,8 +398,8 @@
     <string name="enabled_by_admin" msgid="5302986023578399263">"Activada per l\'administrador"</string>
     <string name="disabled_by_admin" msgid="8505398946020816620">"Desactivada per l\'administrador"</string>
     <string name="disabled" msgid="9206776641295849915">"Desactivat"</string>
-    <string name="external_source_trusted" msgid="2707996266575928037">"Permeses"</string>
-    <string name="external_source_untrusted" msgid="2677442511837596726">"No permeses"</string>
+    <string name="external_source_trusted" msgid="2707996266575928037">"Amb permís"</string>
+    <string name="external_source_untrusted" msgid="2677442511837596726">"Sense permís"</string>
     <string name="install_other_apps" msgid="6986686991775883017">"Instal·lar aplicacions desconegudes"</string>
     <string name="home" msgid="3256884684164448244">"Pàgina d\'inici de configuració"</string>
   <string-array name="battery_labels">
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"Data: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Durada"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pregunta sempre"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Ara mateix"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 1579d8f..1057ae0 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -189,9 +189,9 @@
     <string name="vpn_settings_not_available" msgid="956841430176985598">"Nastavení sítě VPN pro tohoto uživatele není dostupné."</string>
     <string name="tethering_settings_not_available" msgid="6765770438438291012">"Nastavení sdíleného připojení pro tohoto uživatele není dostupné."</string>
     <string name="apn_settings_not_available" msgid="7873729032165324000">"Nastavení přístupového bodu pro tohoto uživatele není dostupné."</string>
-    <string name="enable_adb" msgid="7982306934419797485">"Ladění USB"</string>
+    <string name="enable_adb" msgid="7982306934419797485">"Ladění přes USB"</string>
     <string name="enable_adb_summary" msgid="4881186971746056635">"Povolit režim ladění s připojeným zařízením USB"</string>
-    <string name="clear_adb_keys" msgid="4038889221503122743">"Zrušit autorizace k ladění USB"</string>
+    <string name="clear_adb_keys" msgid="4038889221503122743">"Zrušit autorizace k ladění přes USB"</string>
     <string name="bugreport_in_power" msgid="7923901846375587241">"Zástupce hlášení chyb"</string>
     <string name="bugreport_in_power_summary" msgid="1778455732762984579">"Zobrazit v hlavní nabídce tlačítko k vygenerování chybového hlášení"</string>
     <string name="keep_screen_on" msgid="1146389631208760344">"Nevypínat obrazovku"</string>
@@ -251,9 +251,9 @@
     <string name="debug_view_attributes" msgid="6485448367803310384">"Kontrola atributu zobrazení"</string>
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mobilní data budou vždy ponechána aktivní, i když bude aktivní Wi-Fi (za účelem rychlého přepínání sítí)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Pokud je k dispozici hardwarová akceleraci tetheringu, použít ji"</string>
-    <string name="adb_warning_title" msgid="6234463310896563253">"Povolit ladění USB?"</string>
+    <string name="adb_warning_title" msgid="6234463310896563253">"Povolit ladění přes USB?"</string>
     <string name="adb_warning_message" msgid="7316799925425402244">"Ladění prostřednictvím rozhraní USB je určeno pouze pro účely vývoje. Použijte je ke kopírování dat mezi počítačem a zařízením, instalaci aplikací do zařízení bez upozornění a čtení dat protokolů."</string>
-    <string name="adb_keys_warning_message" msgid="5659849457135841625">"Zrušit přístup k ladění USB ze všech počítačů, které jste v minulosti autorizovali?"</string>
+    <string name="adb_keys_warning_message" msgid="5659849457135841625">"Zrušit přístup k ladění přes USB ze všech počítačů, které jste v minulosti autorizovali?"</string>
     <string name="dev_settings_warning_title" msgid="7244607768088540165">"Povolit nastavení pro vývojáře?"</string>
     <string name="dev_settings_warning_message" msgid="2298337781139097964">"Tato nastavení jsou určena pouze pro vývojáře. Mohou způsobit rozbití nebo nesprávné fungování zařízení a nainstalovaných aplikací."</string>
     <string name="verify_apps_over_usb_title" msgid="4177086489869041953">"Ověřit aplikace z USB"</string>
@@ -451,5 +451,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"v <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Trvání"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pokaždé se zeptat"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Právě teď"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index fbedd90..6cd1855 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"på <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Varighed"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spørg hver gang"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Lige nu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 6fb0fc9..aaacc29 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"am <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Dauer"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Jedes Mal fragen"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Gerade eben"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 336075f..eb8346d 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"το/τη(ν) <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Διάρκεια"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Να ερωτώμαι κάθε φορά"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Μόλις τώρα"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index e028e73..a00f018 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Duration"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index e028e73..a00f018 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Duration"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index e028e73..a00f018 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Duration"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index e028e73..a00f018 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Duration"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 5e046e0..0b70def 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‎‎‏‎‎‏‎‏‎‎‎‏‏‎‏‎‏‎‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‎on ‎‏‎‎‏‏‎<xliff:g id="WHEN">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‏‏‏‎‎‏‎‏‎Duration‎‏‎‎‏‎"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎Ask every time‎‏‎‎‏‎"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎Just now‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 6c4f3b0..9085adb 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"el <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Duración"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar siempre"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Recién"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index e9b07a4..038dfd4 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -217,8 +217,8 @@
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7277329668298705702">"Selecciona la versión AVRCP del Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Códec de audio de Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="8436224899475822557">"Activar el códec de audio por Bluetooth\nSelección"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Porcentaje de muestreo de audio por Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="8010380028880963535">"Activar el códec de audio por Bluetooth\nSelección: porcentaje de muestreo"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Frecuencia de muestreo de audio por Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="8010380028880963535">"Activar el códec de audio por Bluetooth\nSelección: frecuencia de muestreo"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bits por muestra del audio Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="8063859754619484760">"Activar el códec de audio por Bluetooth\nSelección: bits por muestra"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Modo de canal de audio por Bluetooth"</string>
@@ -238,7 +238,7 @@
     <string name="wifi_connected_mac_randomization_summary" msgid="1743059848752201485">"Ordenar las direcciones MAC de forma aleatoria al conectarse a redes Wi‑Fi"</string>
     <string name="wifi_metered_label" msgid="4514924227256839725">"Medida"</string>
     <string name="wifi_unmetered_label" msgid="6124098729457992931">"No medida"</string>
-    <string name="select_logd_size_title" msgid="7433137108348553508">"Tamaños de búfer de registrador"</string>
+    <string name="select_logd_size_title" msgid="7433137108348553508">"Tamaños del búfer para registrar"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Elige el tamaño del Logger por búfer"</string>
     <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"¿Borrar almacenamiento continuo del registrador?"</string>
     <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Cuando ya no supervisamos la actividad con el registrador de forma continua, estamos obligados a borrar los datos del registrador almacenados en el dispositivo."</string>
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"Fecha: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Duración"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar siempre"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Justo ahora"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 1f50226..d9a93c9 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"– <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Kestus"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Küsi iga kord"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Äsja"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index ec7bf98..739eaf0 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -165,7 +165,7 @@
     <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> motorraren ezarpenak"</string>
     <string name="tts_engine_settings_button" msgid="1030512042040722285">"Abiarazi motorraren ezarpenak"</string>
     <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Motor hobetsia"</string>
-    <string name="tts_general_section_title" msgid="4402572014604490502">"Orokorra"</string>
+    <string name="tts_general_section_title" msgid="4402572014604490502">"Orokorrak"</string>
     <string name="tts_reset_speech_pitch_title" msgid="5789394019544785915">"Berrezarri ahots-tonua"</string>
     <string name="tts_reset_speech_pitch_summary" msgid="8700539616245004418">"Berrezarri testua esateko ahots-tonu lehenetsia."</string>
   <string-array name="tts_rate_entries">
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"data: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Iraupena"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Galdetu beti"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Oraintxe"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 7dfd429..18fb3e2 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -159,7 +159,7 @@
     <string name="tts_default_sample_string" msgid="4040835213373086322">"این نمونه‌ای از ترکیب گفتار است"</string>
     <string name="tts_status_title" msgid="7268566550242584413">"وضعیت زبان پیش‌فرض"</string>
     <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> کاملاً پشتیبانی می‌شود"</string>
-    <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> به اتصال شبکه نیاز دارد"</string>
+    <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> باید اتصال شبکه داشته باشد"</string>
     <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> پشتیبانی نمی‌شود"</string>
     <string name="tts_status_checking" msgid="5339150797940483592">"در حال بررسی…"</string>
     <string name="tts_engine_settings_title" msgid="3499112142425680334">"تنظیمات برای <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"روز <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"مدت"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"هربار پرسیده شود"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"هم‌اکنون"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 377f8ce..3858486 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Kesto"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Kysy aina"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Äsken"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 67e7779..36efa53 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"le <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Durée"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Toujours demander"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"À l\'instant"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 5eac8fa..9cadd3c 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -371,10 +371,10 @@
     <string name="power_remaining_duration_only_enhanced" msgid="4189311599812296592">"Temps restant en fonction de votre utilisation : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
     <string name="power_discharging_duration_enhanced" msgid="1992003260664804080">"Temps restant en fonction de votre utilisation (<xliff:g id="LEVEL">%2$s</xliff:g>) : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only_short" msgid="3463575350656389957">"Temps restant : <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
-    <string name="power_discharge_by_enhanced" msgid="2095821536747992464">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> en fonction de l\'utilisation (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
-    <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> en fonction de l\'utilisation"</string>
-    <string name="power_discharge_by" msgid="6453537733650125582">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
-    <string name="power_discharge_by_only" msgid="107616694963545745">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_enhanced" msgid="2095821536747992464">"Temps restant estimé en fonction de votre utilisation : <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+    <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Temps restant estimé en fonction de votre utilisation : <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by" msgid="6453537733650125582">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+    <string name="power_discharge_by_only" msgid="107616694963545745">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"le <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Durée"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Toujours demander"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"À l\'instant"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gl/arrays.xml b/packages/SettingsLib/res/values-gl/arrays.xml
index a25d136..f8a69c2 100644
--- a/packages/SettingsLib/res/values-gl/arrays.xml
+++ b/packages/SettingsLib/res/values-gl/arrays.xml
@@ -207,17 +207,17 @@
   </string-array>
   <string-array name="overlay_display_devices_entries">
     <item msgid="1606809880904982133">"Ningunha"</item>
-    <item msgid="9033194758688161545">"480 p"</item>
-    <item msgid="1025306206556583600">"480 p (seguro)"</item>
-    <item msgid="1853913333042744661">"720 p"</item>
-    <item msgid="3414540279805870511">"720 p (seguro)"</item>
-    <item msgid="9039818062847141551">"1080 p"</item>
-    <item msgid="4939496949750174834">"1080 p (seguro)"</item>
+    <item msgid="9033194758688161545">"480p"</item>
+    <item msgid="1025306206556583600">"480p (seguro)"</item>
+    <item msgid="1853913333042744661">"720p"</item>
+    <item msgid="3414540279805870511">"720p (seguro)"</item>
+    <item msgid="9039818062847141551">"1080p"</item>
+    <item msgid="4939496949750174834">"1080p (seguro)"</item>
     <item msgid="1833612718524903568">"4K"</item>
     <item msgid="238303513127879234">"4K (seguro)"</item>
     <item msgid="3547211260846843098">"4K (mellorado)"</item>
     <item msgid="5411365648951414254">"4K (mellorado e seguro)"</item>
-    <item msgid="1311305077526792901">"720 p, 1080 p (pantalla dual)"</item>
+    <item msgid="1311305077526792901">"720p, 1080p (pantalla dual)"</item>
   </string-array>
   <string-array name="enable_opengl_traces_entries">
     <item msgid="3191973083884253830">"Ningún"</item>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 4f26f6a..82a56e6 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -110,7 +110,7 @@
     <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Ordenador"</string>
     <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Auriculares con micrófono"</string>
     <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Teléfono"</string>
-    <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Imaxes"</string>
+    <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Dispositivo de imaxe"</string>
     <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Auriculares"</string>
     <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Periférico de entrada"</string>
     <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
@@ -213,8 +213,8 @@
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleración de hardware para conexión compartida"</string>
     <string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Mostrar dispositivos Bluetooth sen nomes"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Desactivar volume absoluto"</string>
-    <string name="bluetooth_select_avrcp_version_string" msgid="3750059931120293633">"Versión AVRCP de Bluetooth"</string>
-    <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7277329668298705702">"Selecciona a versión AVRCP de Bluetooth"</string>
+    <string name="bluetooth_select_avrcp_version_string" msgid="3750059931120293633">"Versión de Bluetooth AVRCP"</string>
+    <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7277329668298705702">"Selecciona a versión de Bluetooth AVRCP"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Códec de audio por Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="8436224899475822557">"Activar códec de audio por Bluetooth\nSelección"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Taxa de mostraxe de audio por Bluetooth"</string>
@@ -353,8 +353,8 @@
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Xa se encriptou o ficheiro"</string>
     <string name="title_convert_fbe" msgid="1263622876196444453">"Convertendo no encriptado baseado en ficheiros"</string>
-    <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Converte a partición de datos nunha encriptación baseada en ficheiros.\n Advertencia: Esta acción borrará todos os datos.\n Esta función é alfa e quizais non funcione correctamente.\n Para continuar, toca Limpar e converter..."</string>
-    <string name="button_convert_fbe" msgid="5152671181309826405">"Limpar e converter..."</string>
+    <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Converte a partición de datos nunha encriptación baseada en ficheiros.\n Advertencia: Esta acción borrará todos os datos.\n Esta función é alfa e quizais non funcione correctamente.\n Para continuar, toca Borrar e converter..."</string>
+    <string name="button_convert_fbe" msgid="5152671181309826405">"Borrar e converter..."</string>
     <string name="picture_color_mode" msgid="4560755008730283695">"Modo de cor da imaxe"</string>
     <string name="picture_color_mode_desc" msgid="1141891467675548590">"Utiliza sRGB"</string>
     <string name="daltonizer_mode_disabled" msgid="7482661936053801862">"Desactivado"</string>
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"na seguinte data: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Duración"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar sempre"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora mesmo"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index bccf0e6..2af406d 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g> વાગ્યે"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"અવધિ"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"દર વખતે પૂછો"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"હમણાં જ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index c5d662c..4bd79f9 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"अलार्म <xliff:g id="WHEN">%1$s</xliff:g> को बजेगा"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"अवधि"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"हर बार पूछें"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"अभी-अभी"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index f269c38..8eb41b5 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -450,5 +450,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Trajanje"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pitaj svaki put"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo sad"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index a09a4ff..5fee933 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"ezen a napon: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Időtartam"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Mindig kérdezzen rá"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Az imént"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index c808fe7..8f68ac7 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>-ին"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Տևողություն"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Հարցնել ամեն անգամ"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Հենց նոր"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index e4b12e4..7c18a83 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"pada <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Durasi"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Selalu tanya"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Baru saja"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 61510c2..0f2d55e 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"á/í <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Lengd"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spyrja í hvert skipti"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Rétt í þessu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 546495b..f44fbb3 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"il giorno <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Durata"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Chiedi ogni volta"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Adesso"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 015487a..c1d858c 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -451,5 +451,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"ב-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"משך"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"שאל בכל פעם"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"הרגע"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 2c43d33..d59c611 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -141,7 +141,7 @@
     <string name="launch_defaults_some" msgid="313159469856372621">"一部デフォルトで設定"</string>
     <string name="launch_defaults_none" msgid="4241129108140034876">"デフォルトの設定なし"</string>
     <string name="tts_settings" msgid="8186971894801348327">"テキスト読み上げの設定"</string>
-    <string name="tts_settings_title" msgid="1237820681016639683">"テキスト読み上げの出力"</string>
+    <string name="tts_settings_title" msgid="1237820681016639683">"テキスト読み上げの設定"</string>
     <string name="tts_default_rate_title" msgid="6030550998379310088">"音声の速度"</string>
     <string name="tts_default_rate_summary" msgid="4061815292287182801">"テキストの読み上げ速度"</string>
     <string name="tts_default_pitch_title" msgid="6135942113172488671">"音の高さ"</string>
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"期間"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"毎回確認"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"たった今"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index bd09bf1..4418bef 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>-ზე"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"ხანგრძლივობა"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ყოველთვის მკითხეთ"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"ახლახან"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 005ee6d..88669a7 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"Уақыты: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Ұзақтығы"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Әрдайым сұрау"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Дәл қазір"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index d90a89f..509dafa 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"នៅ​ថ្ងៃ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"រយៈពេល"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"សួរគ្រប់ពេល"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"អម្បាញ់មិញ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index af7890e..f01fac7 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g> ಕ್ಕೆ"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"ಅವಧಿ"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ಪ್ರತಿ ಬಾರಿ ಕೇಳಿ"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"ಇದೀಗ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 14ce137..34fa2ae 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"일시: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"지속 시간"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"항상 확인"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"조금 전"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 109c47e..0a3bd80 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Узактыгы"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ар дайым суралсын"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Азыр эле"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 4034a7e..2853fee 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"ເວລາ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"ໄລຍະເວລາ"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ຖາມທຸກເທື່ອ"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"ຕອນນີ້"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index c974812..3866922 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -451,5 +451,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Trukmė"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Klausti kaskart"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Ką tik"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 80aa917..6133f7f 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -450,5 +450,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Ilgums"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vaicāt katru reizi"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Tikko"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 534dc2a..0a27a06 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"во <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Времетраење"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Секогаш прашувај"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Неодамнешни"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 01d7107..8601d97 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>-ന്"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"ദൈർഘ്യം"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"എപ്പോഴും ചോദിക്കുക"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"ഇപ്പോൾ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 960f3e1..62dd1e9 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>-д"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Хугацаа"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Тухай бүрт асуух"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Дөнгөж сая"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 6e89a84..9428338 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g> रोजी"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"कालावधी"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"प्रत्येक वेळी विचारा"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"आत्ताच"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index f3f9eb6..47d4cbc 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"pada <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Tempoh"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Tanya setiap kali"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Sebentar tadi"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index adae0d2..40e351a 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g> တွင်"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"ကြာချိန်"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"အမြဲမေးပါ"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"ယခုလေးတင်"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 7f9f4a6..a9637c1 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -307,7 +307,7 @@
     <string name="enable_gpu_debug_layers" msgid="3848838293793255097">"Slå på GPU-feilsøkingslag"</string>
     <string name="enable_gpu_debug_layers_summary" msgid="8009136940671194940">"Tillat GPU-feilsøkingslag for feilsøkingsapper"</string>
     <string name="window_animation_scale_title" msgid="6162587588166114700">"Animasjonsskala for vindu"</string>
-    <string name="transition_animation_scale_title" msgid="387527540523595875">"Overgangsanimasjonsskala"</string>
+    <string name="transition_animation_scale_title" msgid="387527540523595875">"Animasjonsskala for overgang"</string>
     <string name="animator_duration_scale_title" msgid="3406722410819934083">"Varighetsskala for animasjon"</string>
     <string name="overlay_display_devices_title" msgid="5364176287998398539">"Simulering av sekundærskjermer"</string>
     <string name="debug_applications_category" msgid="4206913653849771549">"Apper"</string>
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Varighet"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spør hver gang"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Nå nettopp"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 3e2afed..6ae6a22 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -320,7 +320,7 @@
     <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"अनुप्रयोगले कुनै मान्य च्यानल बिना सूचना पोस्ट गर्दा स्क्रिनमा चेतावनी देखाउँछ"</string>
     <string name="force_allow_on_external" msgid="3215759785081916381">"बाह्यमा बल प्रयोगको अनुमति प्राप्त अनुप्रयोगहरू"</string>
     <string name="force_allow_on_external_summary" msgid="3640752408258034689">"म्यानिफेेस्टका मानहरूको ख्याल नगरी कुनै पनि अनुप्रयोगलाई बाह्य भण्डारणमा लेख्न सकिने खाले बनाउँछ"</string>
-    <string name="force_resizable_activities" msgid="8615764378147824985">"गतिविधिहरू रिसाइज गर्नको लागि बाध्य गर्नुहोस्"</string>
+    <string name="force_resizable_activities" msgid="8615764378147824985">"आकार बदल्न योग्य हुने बनाउन गतिविधिहरूलाई बाध्यात्मक बनाउनुहोस्।"</string>
     <string name="force_resizable_activities_summary" msgid="6667493494706124459">"म्यानिफेेस्ट मानहरूको ख्याल नगरी, बहु-विन्डोको लागि सबै रिसाइज गर्न सकिने गतिविधिहरू बनाउनुहोस्।"</string>
     <string name="enable_freeform_support" msgid="1461893351278940416">"फ्रिफर्म विन्डोहरू सक्रिय गर्नुहोस्"</string>
     <string name="enable_freeform_support_summary" msgid="8247310463288834487">"प्रयोगात्मक फ्रिफर्म विन्डोहरूका लागि समर्थन सक्रिय गर्नुहोस्।"</string>
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g> मा"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"अवधि"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"प्रत्येक पटक सोध्नुहोस्"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"अहिले भर्खरै"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 41ba2b0..e6a7e0a 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -102,7 +102,7 @@
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Koppelen"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"KOPPELEN"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Annuleren"</string>
-    <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Koppelen verleent toegang tot je contacten en oproepgeschiedenis wanneer de apparaten zijn verbonden."</string>
+    <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Koppelen verleent toegang tot je contacten en gespreksgeschiedenis wanneer de apparaten zijn verbonden."</string>
     <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Kan niet koppelen aan <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Kan niet koppelen aan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> vanwege een onjuiste pincode of toegangscode."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Kan niet communiceren met <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
@@ -306,8 +306,8 @@
     <string name="track_frame_time" msgid="6094365083096851167">"HWUI-weergave van profiel"</string>
     <string name="enable_gpu_debug_layers" msgid="3848838293793255097">"GPU-foutopsporingslagen inschakelen"</string>
     <string name="enable_gpu_debug_layers_summary" msgid="8009136940671194940">"Laden van GPU-foutopsporingslagen toestaan voor foutopsporingsapps"</string>
-    <string name="window_animation_scale_title" msgid="6162587588166114700">"Vensteranimatieschaal"</string>
-    <string name="transition_animation_scale_title" msgid="387527540523595875">"Overgangsanimatieschaal"</string>
+    <string name="window_animation_scale_title" msgid="6162587588166114700">"Venster­animatieschaal"</string>
+    <string name="transition_animation_scale_title" msgid="387527540523595875">"Overgangs­animatieschaal"</string>
     <string name="animator_duration_scale_title" msgid="3406722410819934083">"Duur van animatieschaal"</string>
     <string name="overlay_display_devices_title" msgid="5364176287998398539">"Secundaire displays simuleren"</string>
     <string name="debug_applications_category" msgid="4206913653849771549">"Apps"</string>
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"op <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Duur"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Altijd vragen"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Zojuist"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index c84875e..3532e89 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -224,8 +224,7 @@
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"ବ୍ଲୁ-ଟୂଥ୍‍‌ ଅଡିଓ ଚ୍ୟାନେଲ୍‌ ମୋଡ୍"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="7234956835280563341">"ବ୍ଲୁ-ଟୂଥ୍‍ ଅଡିଓ କୋଡେକ୍\nସିଲେକ୍ସନ୍‌କୁ ଗତିଶୀଳ କରନ୍ତୁ: ଚ୍ୟାନେଲ୍ ମୋଡ୍"</string>
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ବ୍ଲୁ-ଟୂଥ୍‍‌ ଅଡିଓ LDAC କୋଡେକ୍‌: ପ୍ଲେବ୍ୟାକ୍‌ ଗୁଣବତ୍ତା"</string>
-    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (6893955536658137179) -->
-    <skip />
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="6893955536658137179">"ବ୍ଲୁ-ଟୁଥ୍‌ ଅଡିଓ LDAC\nକୋଡେକ୍‌ ଚୟନକୁ ଗତିଶୀଳ କରନ୍ତୁ: ପ୍ଲେବ୍ୟାକ୍‌ କ୍ୱାଲିଟୀ"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ଷ୍ଟ୍ରିମ୍ କରୁଛି: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"ବ୍ୟକ୍ତିଗତ DNS"</string>
     <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"ବ୍ୟକ୍ତିଗତ DNS ମୋଡ୍‌ ବାଛନ୍ତୁ"</string>
@@ -450,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g> ବେଳେ"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"ଅବଧି"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ପ୍ରତ୍ୟେକ ଥର ପଚାରନ୍ତୁ"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"ଏହିକ୍ଷଣି"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 995ebfe..ff1f4f3 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g> ਵਜੇ"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"ਮਿਆਦ"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ਹਰ ਵਾਰ ਪੁੱਛੋ"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"ਹੁਣੇ ਹੀ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 7ec97b5..7ff80c9 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -194,7 +194,7 @@
     <string name="clear_adb_keys" msgid="4038889221503122743">"Odwołaj dostęp do debugowania USB"</string>
     <string name="bugreport_in_power" msgid="7923901846375587241">"Skrót do zgłoszenia błędu"</string>
     <string name="bugreport_in_power_summary" msgid="1778455732762984579">"Pokaż w menu zasilania przycisk zgłaszania błędu"</string>
-    <string name="keep_screen_on" msgid="1146389631208760344">"Pozostaw ekran włączony"</string>
+    <string name="keep_screen_on" msgid="1146389631208760344">"Pozostaw włączony ekran"</string>
     <string name="keep_screen_on_summary" msgid="2173114350754293009">"Ekran nie będzie gaszony podczas ładowania telefonu"</string>
     <string name="bt_hci_snoop_log" msgid="3340699311158865670">"Włącz dziennik snoop Bluetooth HCI"</string>
     <string name="bt_hci_snoop_log_summary" msgid="366083475849911315">"Przechwyć wszystkie pakiety Bluetooth HCI do pliku (przełącz Bluetooth po zmianie tego ustawienia)"</string>
@@ -451,5 +451,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"w: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Czas"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Zawsze pytaj"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Przed chwilą"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
index dfdc09c..563698a 100644
--- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
@@ -160,11 +160,11 @@
   </string-array>
   <string-array name="select_logd_size_summaries">
     <item msgid="6921048829791179331">"Desativado"</item>
-    <item msgid="2969458029344750262">"64 K/buffer de log"</item>
-    <item msgid="1342285115665698168">"256 K/buffer de log"</item>
-    <item msgid="1314234299552254621">"1 M/buffer de log"</item>
-    <item msgid="3606047780792894151">"4 M/buffer de log"</item>
-    <item msgid="5431354956856655120">"16 M/buffer de log"</item>
+    <item msgid="2969458029344750262">"64 K/buffer de registro"</item>
+    <item msgid="1342285115665698168">"256 K/buffer de registro"</item>
+    <item msgid="1314234299552254621">"1 M/buffer de registro"</item>
+    <item msgid="3606047780792894151">"4 M/buffer de registro"</item>
+    <item msgid="5431354956856655120">"16 M/buffer de registro"</item>
   </string-array>
   <string-array name="select_logpersist_titles">
     <item msgid="1744840221860799971">"Desativado"</item>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index d1726d3..585aee6 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -195,8 +195,8 @@
     <string name="bugreport_in_power" msgid="7923901846375587241">"Atalho para relatório do bug"</string>
     <string name="bugreport_in_power_summary" msgid="1778455732762984579">"Mostrar um botão para gerar relatórios de bugs no menu do botão liga/desliga"</string>
     <string name="keep_screen_on" msgid="1146389631208760344">"Permanecer ativo"</string>
-    <string name="keep_screen_on_summary" msgid="2173114350754293009">"A tela nunca entrará em inatividade enquanto estiver carregando."</string>
-    <string name="bt_hci_snoop_log" msgid="3340699311158865670">"Ativar log de rastreamento Bluetooth HCI"</string>
+    <string name="keep_screen_on_summary" msgid="2173114350754293009">"A tela nunca entrará em suspensão enquanto estiver carregando."</string>
+    <string name="bt_hci_snoop_log" msgid="3340699311158865670">"Ativar registro de rastreamento Bluetooth HCI"</string>
     <string name="bt_hci_snoop_log_summary" msgid="366083475849911315">"Capturar todos os pacotes Bluetooth HCI em um arquivo (ative o Bluetooth depois de alterar esta configuração)"</string>
     <string name="oem_unlock_enable" msgid="6040763321967327691">"Desbloqueio de OEM"</string>
     <string name="oem_unlock_enable_summary" msgid="4720281828891618376">"Permitir que o bootloader seja desbloqueado"</string>
@@ -239,7 +239,7 @@
     <string name="wifi_metered_label" msgid="4514924227256839725">"Limitada"</string>
     <string name="wifi_unmetered_label" msgid="6124098729457992931">"Ilimitada"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Tamanhos de buffer de logger"</string>
-    <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Sel. tam. de logger/buffer de log"</string>
+    <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Sel. tam. de logger/buffer de registro"</string>
     <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Limpar armazenamento de logger constante?"</string>
     <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Quando não estivermos mais monitorando com o logger constante, devemos limpar o residente de dados de logger do seu dispositivo."</string>
     <string name="select_logpersist_title" msgid="7530031344550073166">"Armazenar dados de logger constantemente no dispositivo"</string>
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Duração"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index ce40198..e41acde 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"no(a) <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Duração"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora mesmo"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml
index dfdc09c..563698a 100644
--- a/packages/SettingsLib/res/values-pt/arrays.xml
+++ b/packages/SettingsLib/res/values-pt/arrays.xml
@@ -160,11 +160,11 @@
   </string-array>
   <string-array name="select_logd_size_summaries">
     <item msgid="6921048829791179331">"Desativado"</item>
-    <item msgid="2969458029344750262">"64 K/buffer de log"</item>
-    <item msgid="1342285115665698168">"256 K/buffer de log"</item>
-    <item msgid="1314234299552254621">"1 M/buffer de log"</item>
-    <item msgid="3606047780792894151">"4 M/buffer de log"</item>
-    <item msgid="5431354956856655120">"16 M/buffer de log"</item>
+    <item msgid="2969458029344750262">"64 K/buffer de registro"</item>
+    <item msgid="1342285115665698168">"256 K/buffer de registro"</item>
+    <item msgid="1314234299552254621">"1 M/buffer de registro"</item>
+    <item msgid="3606047780792894151">"4 M/buffer de registro"</item>
+    <item msgid="5431354956856655120">"16 M/buffer de registro"</item>
   </string-array>
   <string-array name="select_logpersist_titles">
     <item msgid="1744840221860799971">"Desativado"</item>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index d1726d3..585aee6 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -195,8 +195,8 @@
     <string name="bugreport_in_power" msgid="7923901846375587241">"Atalho para relatório do bug"</string>
     <string name="bugreport_in_power_summary" msgid="1778455732762984579">"Mostrar um botão para gerar relatórios de bugs no menu do botão liga/desliga"</string>
     <string name="keep_screen_on" msgid="1146389631208760344">"Permanecer ativo"</string>
-    <string name="keep_screen_on_summary" msgid="2173114350754293009">"A tela nunca entrará em inatividade enquanto estiver carregando."</string>
-    <string name="bt_hci_snoop_log" msgid="3340699311158865670">"Ativar log de rastreamento Bluetooth HCI"</string>
+    <string name="keep_screen_on_summary" msgid="2173114350754293009">"A tela nunca entrará em suspensão enquanto estiver carregando."</string>
+    <string name="bt_hci_snoop_log" msgid="3340699311158865670">"Ativar registro de rastreamento Bluetooth HCI"</string>
     <string name="bt_hci_snoop_log_summary" msgid="366083475849911315">"Capturar todos os pacotes Bluetooth HCI em um arquivo (ative o Bluetooth depois de alterar esta configuração)"</string>
     <string name="oem_unlock_enable" msgid="6040763321967327691">"Desbloqueio de OEM"</string>
     <string name="oem_unlock_enable_summary" msgid="4720281828891618376">"Permitir que o bootloader seja desbloqueado"</string>
@@ -239,7 +239,7 @@
     <string name="wifi_metered_label" msgid="4514924227256839725">"Limitada"</string>
     <string name="wifi_unmetered_label" msgid="6124098729457992931">"Ilimitada"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Tamanhos de buffer de logger"</string>
-    <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Sel. tam. de logger/buffer de log"</string>
+    <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Sel. tam. de logger/buffer de registro"</string>
     <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Limpar armazenamento de logger constante?"</string>
     <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Quando não estivermos mais monitorando com o logger constante, devemos limpar o residente de dados de logger do seu dispositivo."</string>
     <string name="select_logpersist_title" msgid="7530031344550073166">"Armazenar dados de logger constantemente no dispositivo"</string>
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Duração"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml
index e4a341a..7935a2a 100644
--- a/packages/SettingsLib/res/values-ro/arrays.xml
+++ b/packages/SettingsLib/res/values-ro/arrays.xml
@@ -22,7 +22,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wifi_status">
     <item msgid="1922181315419294640"></item>
-    <item msgid="8934131797783724664">"În curs de scanare..."</item>
+    <item msgid="8934131797783724664">"Se caută..."</item>
     <item msgid="8513729475867537913">"Se conectează..."</item>
     <item msgid="515055375277271756">"În curs de autentificare…"</item>
     <item msgid="1943354004029184381">"Se obține adresa IP..."</item>
@@ -36,7 +36,7 @@
   </string-array>
   <string-array name="wifi_status_with_ssid">
     <item msgid="7714855332363650812"></item>
-    <item msgid="8878186979715711006">"În curs de scanare..."</item>
+    <item msgid="8878186979715711006">"Se caută..."</item>
     <item msgid="355508996603873860">"Se conectează la <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
     <item msgid="554971459996405634">"Se autentifică cu <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
     <item msgid="7928343808033020343">"Se obține adresa IP de la <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index b53a3e3..8024047 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -450,5 +450,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Durată"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Întreabă de fiecare dată"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Chiar acum"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 77de8db..494d107 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -56,7 +56,7 @@
     <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Отключение..."</string>
     <string name="bluetooth_connecting" msgid="8555009514614320497">"Подключение..."</string>
     <string name="bluetooth_connected" msgid="5427152882755735944">"Подключено<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_pairing" msgid="1426882272690346242">"Сопряжение..."</string>
+    <string name="bluetooth_pairing" msgid="1426882272690346242">"Устанавливается соединение..."</string>
     <string name="bluetooth_connected_no_headset" msgid="616068069034994802">"Подключено (кроме звонков)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_a2dp" msgid="3736431800395923868">"Подключено (кроме аудио)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_map" msgid="3200033913678466453">"Подключено (кроме сообщений)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
@@ -102,11 +102,11 @@
     <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Добавить"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ДОБАВИТЬ"</string>
     <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Отмена"</string>
-    <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Сопряжение обеспечивает доступ к вашим контактам и журналу звонков при подключении."</string>
-    <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Не удалось установить сопряжение с устройством \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
-    <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Не удалось установить сопряжение с устройством \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\", так как введен неверный PIN-код или пароль."</string>
+    <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Установление соединения обеспечивает доступ к вашим контактам и журналу звонков при подключении."</string>
+    <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Не удалось подключиться к устройству \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
+    <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Не удалось подключиться к устройству \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\", так как введен неверный PIN-код или пароль."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Не удается установить соединение с устройством \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
-    <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> не разрешает сопряжение."</string>
+    <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> не разрешает подключение."</string>
     <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Компьютер"</string>
     <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Гарнитура"</string>
     <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Телефон"</string>
@@ -451,5 +451,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Длительность"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Всегда спрашивать"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Только что"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 6916787..21c96f1 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>හිදී"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"කාල සීමාව"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"සෑම විටම ඉල්ලන්න"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"මේ දැන්"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index fc30b70..4a92357 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -451,5 +451,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"o <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Trvanie"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vždy sa opýtať"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Teraz"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 458f7f1..fb17c17 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -451,5 +451,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"v <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Trajanje"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vedno vprašaj"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Pravkar"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index cab1201..a8e9a76 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"ditën <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Kohëzgjatja"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pyet çdo herë"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Pikërisht tani"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index d2ebb18..fe158a0 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -450,5 +450,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Трајање"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Увек питај"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Управо сада"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 0f33159..99e915a 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"på <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Varaktighet"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Fråga varje gång"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Nyss"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml
index f13c2ea..fdf9fd1 100644
--- a/packages/SettingsLib/res/values-sw/arrays.xml
+++ b/packages/SettingsLib/res/values-sw/arrays.xml
@@ -59,7 +59,7 @@
     <item msgid="45075631231212732">"Kila wakati tumia ukakuaji wa HDCP"</item>
   </string-array>
   <string-array name="bluetooth_avrcp_versions">
-    <item msgid="5347678900838034763">"AVRCP 1.4 (Chaguo msingi)"</item>
+    <item msgid="5347678900838034763">"AVRCP 1.4 (Chaguomsingi)"</item>
     <item msgid="2809759619990248160">"AVRCP 1.3"</item>
     <item msgid="6199178154704729352">"AVRCP 1.5"</item>
     <item msgid="5172170854953034852">"AVRCP 1.6"</item>
@@ -71,7 +71,7 @@
     <item msgid="3422726142222090896">"avrcp16"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="7065842274271279580">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+    <item msgid="7065842274271279580">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="686685526567131661">"AAC"</item>
     <item msgid="5254942598247222737">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
@@ -81,7 +81,7 @@
     <item msgid="3304843301758635896">"Zima Kodeki Zisizo za Lazima"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="5062108632402595000">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+    <item msgid="5062108632402595000">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="6839647709301342559">"AAC"</item>
     <item msgid="7848030269621918608">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
@@ -91,38 +91,38 @@
     <item msgid="741805482892725657">"Zima Kodeki Zisizo za Lazima"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="3093023430402746802">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+    <item msgid="3093023430402746802">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
     <item msgid="8895532488906185219">"kHz 44.1"</item>
     <item msgid="2909915718994807056">"kHz 48.0"</item>
     <item msgid="3347287377354164611">"kHz 88.2"</item>
     <item msgid="1234212100239985373">"kHz 96.0"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="3214516120190965356">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+    <item msgid="3214516120190965356">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
     <item msgid="4482862757811638365">"kHz 44.1"</item>
     <item msgid="354495328188724404">"kHz 48.0"</item>
     <item msgid="7329816882213695083">"kHz 88.2"</item>
     <item msgid="6967397666254430476">"kHz 96.0"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="2684127272582591429">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+    <item msgid="2684127272582591429">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
     <item msgid="5618929009984956469">"Biti 16 kwa kila sampuli"</item>
     <item msgid="3412640499234627248">"Biti 24 kwa kila sampuli"</item>
     <item msgid="121583001492929387">"Biti 32 kwa kila sampuli"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="1081159789834584363">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+    <item msgid="1081159789834584363">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
     <item msgid="4726688794884191540">"Biti 16 kwa kila sampuli"</item>
     <item msgid="305344756485516870">"Biti 24 kwa kila sampuli"</item>
     <item msgid="244568657919675099">"Biti 32 kwa kila sampuli"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="5226878858503393706">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+    <item msgid="5226878858503393706">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="4118561796005528173">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+    <item msgid="4118561796005528173">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 53f5d0c..ce06a62 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -138,7 +138,7 @@
     <string name="user_guest" msgid="8475274842845401871">"Aliyealikwa"</string>
     <string name="unknown" msgid="1592123443519355854">"Haijulikani"</string>
     <string name="running_process_item_user_label" msgid="3129887865552025943">"Mtumiaji: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
-    <string name="launch_defaults_some" msgid="313159469856372621">"Baadhi ya chaguo msingi zimewekwa"</string>
+    <string name="launch_defaults_some" msgid="313159469856372621">"Baadhi ya chaguomsingi zimewekwa"</string>
     <string name="launch_defaults_none" msgid="4241129108140034876">"Hakuna chaguo-misingi zilizowekwa"</string>
     <string name="tts_settings" msgid="8186971894801348327">"Mipangilio ya kusoma maandishi kwa sauti"</string>
     <string name="tts_settings_title" msgid="1237820681016639683">"Kusoma maandishi kwa sauti"</string>
@@ -157,7 +157,7 @@
     <string name="tts_engine_security_warning" msgid="8786238102020223650">"Hotuba hii inawezesha injini huenda ikaweza kukusanya maandishi ambayo yatazungumziwa, ikijumlisha data ya kibinafsi ya nenosiri na namba ya kaddi ya mkopo. Inatoka kwa injini ya <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> Wezesha matumizi ya hotuba hii iliyowezeshwa ya injini?"</string>
     <string name="tts_engine_network_required" msgid="1190837151485314743">"Lugha hii inahitaji muunganisho wa mtandao unaofanya kazi ili kipengele cha kusoma maandishi kwa sauti kifanye kazi."</string>
     <string name="tts_default_sample_string" msgid="4040835213373086322">"Huu ni mfano wa usanisi usemaji"</string>
-    <string name="tts_status_title" msgid="7268566550242584413">"Hali ya lugha chaguo msingi"</string>
+    <string name="tts_status_title" msgid="7268566550242584413">"Hali ya lugha chaguomsingi"</string>
     <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> inaweza kutumiwa kikamilifu"</string>
     <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> inahitaji muunganisho wa mtandao"</string>
     <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> haiwezi kutumiwa"</string>
@@ -167,7 +167,7 @@
     <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Injini inayofaa"</string>
     <string name="tts_general_section_title" msgid="4402572014604490502">"Kwa ujumla"</string>
     <string name="tts_reset_speech_pitch_title" msgid="5789394019544785915">"Weka upya mipangilio ya ubora wa matamshi"</string>
-    <string name="tts_reset_speech_pitch_summary" msgid="8700539616245004418">"Rejesha mipangilio ya ubora wa matamshi kuwa ya chaguo msingi."</string>
+    <string name="tts_reset_speech_pitch_summary" msgid="8700539616245004418">"Rejesha mipangilio ya ubora wa matamshi kuwa ya chaguomsingi."</string>
   <string-array name="tts_rate_entries">
     <item msgid="6695494874362656215">"Polepole sana"</item>
     <item msgid="4795095314303559268">"Polepole"</item>
@@ -331,7 +331,7 @@
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nenosiri jipya na uthibitisho havioani"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Imeshindwa kuweka nenosiri la hifadhi rudufu"</string>
   <string-array name="color_mode_names">
-    <item msgid="2425514299220523812">"Maridadi (chaguo msingi)"</item>
+    <item msgid="2425514299220523812">"Maridadi (chaguomsingi)"</item>
     <item msgid="8446070607501413455">"Asili"</item>
     <item msgid="6553408765810699025">"Muundo-msingi"</item>
   </string-array>
@@ -410,7 +410,7 @@
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> zilizopita"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Zimesalia <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Ndogo"</string>
-    <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Chaguo msingi"</string>
+    <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Chaguomsingi"</string>
     <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Kubwa"</string>
     <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Kubwa kiasi"</string>
     <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Kubwa zaidi"</string>
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"siku ya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Muda"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Uliza kila wakati"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Sasa hivi"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml
index 7b69c78..54adf82 100644
--- a/packages/SettingsLib/res/values-ta/arrays.xml
+++ b/packages/SettingsLib/res/values-ta/arrays.xml
@@ -22,7 +22,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wifi_status">
     <item msgid="1922181315419294640"></item>
-    <item msgid="8934131797783724664">"ஸ்கேன் செய்கிறது…"</item>
+    <item msgid="8934131797783724664">"தேடுகிறது..."</item>
     <item msgid="8513729475867537913">"இணைக்கிறது..."</item>
     <item msgid="515055375277271756">"அங்கீகரிக்கிறது..."</item>
     <item msgid="1943354004029184381">"IP முகவரியைப் பெறுகிறது…"</item>
@@ -36,7 +36,7 @@
   </string-array>
   <string-array name="wifi_status_with_ssid">
     <item msgid="7714855332363650812"></item>
-    <item msgid="8878186979715711006">"ஸ்கேன் செய்கிறது…"</item>
+    <item msgid="8878186979715711006">"தேடுகிறது..."</item>
     <item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> இல் இணைக்கிறது…"</item>
     <item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> உடன் அங்கீகரிக்கிறது…"</item>
     <item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> இலிருந்து IP முகவரியைப் பெறுகிறது…"</item>
@@ -180,30 +180,30 @@
   </string-array>
   <string-array name="window_animation_scale_entries">
     <item msgid="8134156599370824081">"அனிமேஷனை முடக்கு"</item>
-    <item msgid="6624864048416710414">"அனிமேஷன் அளவு .5x"</item>
-    <item msgid="2219332261255416635">"அனிமேஷன் அளவு 1x"</item>
-    <item msgid="3544428804137048509">"அனிமேஷன் அளவு 1.5x"</item>
-    <item msgid="3110710404225974514">"அனிமேஷன் அளவு 2x"</item>
-    <item msgid="4402738611528318731">"அனிமேஷன் அளவு 5x"</item>
-    <item msgid="6189539267968330656">"அனிமேஷன் அளவு 10x"</item>
+    <item msgid="6624864048416710414">"அனிமேஷன் வேகம் .5x"</item>
+    <item msgid="2219332261255416635">"அனிமேஷன் வேகம் 1x"</item>
+    <item msgid="3544428804137048509">"அனிமேஷன் வேகம் 1.5x"</item>
+    <item msgid="3110710404225974514">"அனிமேஷன் வேகம் 2x"</item>
+    <item msgid="4402738611528318731">"அனிமேஷன் வேகம் 5x"</item>
+    <item msgid="6189539267968330656">"அனிமேஷன் வேகம் 10x"</item>
   </string-array>
   <string-array name="transition_animation_scale_entries">
     <item msgid="8464255836173039442">"அனிமேஷனை முடக்கு"</item>
-    <item msgid="3375781541913316411">"அனிமேஷன் அளவு .5x"</item>
-    <item msgid="1991041427801869945">"அனிமேஷன் அளவு 1x"</item>
-    <item msgid="4012689927622382874">"அனிமேஷன் அளவு 1.5x"</item>
-    <item msgid="3289156759925947169">"அனிமேஷன் அளவு 2x"</item>
-    <item msgid="7705857441213621835">"அனிமேஷன் அளவு 5x"</item>
-    <item msgid="6660750935954853365">"அனிமேஷன் அளவு 10x"</item>
+    <item msgid="3375781541913316411">"அனிமேஷன் வேகம் .5x"</item>
+    <item msgid="1991041427801869945">"அனிமேஷன் வேகம் 1x"</item>
+    <item msgid="4012689927622382874">"அனிமேஷன் வேகம் 1.5x"</item>
+    <item msgid="3289156759925947169">"அனிமேஷன் வேகம் 2x"</item>
+    <item msgid="7705857441213621835">"அனிமேஷன் வேகம் 5x"</item>
+    <item msgid="6660750935954853365">"அனிமேஷன் வேகம் 10x"</item>
   </string-array>
   <string-array name="animator_duration_scale_entries">
     <item msgid="6039901060648228241">"அனிமேஷனை முடக்கு"</item>
-    <item msgid="1138649021950863198">"அனிமேஷன் அளவு .5x"</item>
-    <item msgid="4394388961370833040">"அனிமேஷன் அளவு 1x"</item>
-    <item msgid="8125427921655194973">"அனிமேஷன் அளவு 1.5x"</item>
-    <item msgid="3334024790739189573">"அனிமேஷன் அளவு 2x"</item>
-    <item msgid="3170120558236848008">"அனிமேஷன் அளவு 5x"</item>
-    <item msgid="1069584980746680398">"அனிமேஷன் அளவு 10x"</item>
+    <item msgid="1138649021950863198">"அனிமேஷன் வேகம் .5x"</item>
+    <item msgid="4394388961370833040">"அனிமேஷன் வேகம் 1x"</item>
+    <item msgid="8125427921655194973">"அனிமேஷன் வேகம் 1.5x"</item>
+    <item msgid="3334024790739189573">"அனிமேஷன் வேகம் 2x"</item>
+    <item msgid="3170120558236848008">"அனிமேஷன் வேகம் 5x"</item>
+    <item msgid="1069584980746680398">"அனிமேஷன் வேகம் 10x"</item>
   </string-array>
   <string-array name="overlay_display_devices_entries">
     <item msgid="1606809880904982133">"ஏதுமில்லை"</item>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 828e5427..9c8a2f1 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -306,8 +306,8 @@
     <string name="track_frame_time" msgid="6094365083096851167">"சுயவிவர HWUI ரெண்டரிங்"</string>
     <string name="enable_gpu_debug_layers" msgid="3848838293793255097">"GPU பிழைத்திருத்த லேயர்களை இயக்கு"</string>
     <string name="enable_gpu_debug_layers_summary" msgid="8009136940671194940">"பிழைத்திருத்த ஆப்ஸிற்கு, GPU பிழைத்திருத்த லேயர்களை ஏற்றுவதற்கு அனுமதி"</string>
-    <string name="window_animation_scale_title" msgid="6162587588166114700">"சாளர அனிமேஷன் அளவு"</string>
-    <string name="transition_animation_scale_title" msgid="387527540523595875">"அனிமேஷன் மாற்றத்தின் அளவு"</string>
+    <string name="window_animation_scale_title" msgid="6162587588166114700">"சாளர அனிமேஷன் வேகம்"</string>
+    <string name="transition_animation_scale_title" msgid="387527540523595875">"அனிமேஷன் மாற்றத்தின் வேகம்"</string>
     <string name="animator_duration_scale_title" msgid="3406722410819934083">"அனிமேட்டர் கால அளவு"</string>
     <string name="overlay_display_devices_title" msgid="5364176287998398539">"இரண்டாம்நிலைக் காட்சிகளை உருவகப்படுத்து"</string>
     <string name="debug_applications_category" msgid="4206913653849771549">"ஆப்ஸ்"</string>
@@ -358,7 +358,7 @@
     <string name="picture_color_mode" msgid="4560755008730283695">"படத்தின் வண்ணப் பயன்முறை"</string>
     <string name="picture_color_mode_desc" msgid="1141891467675548590">"sRGBஐப் பயன்படுத்தும்"</string>
     <string name="daltonizer_mode_disabled" msgid="7482661936053801862">"முடக்கப்பட்டது"</string>
-    <string name="daltonizer_mode_monochromacy" msgid="8485709880666106721">"மோனோகுரோமசி"</string>
+    <string name="daltonizer_mode_monochromacy" msgid="8485709880666106721">"ஒற்றை நிறத் தன்மை"</string>
     <string name="daltonizer_mode_deuteranomaly" msgid="5475532989673586329">"நிறம் அடையாளங்காண முடியாமை (சிவப்பு-பச்சை)"</string>
     <string name="daltonizer_mode_protanomaly" msgid="8424148009038666065">"நிறம் அடையாளங்காண முடியாமை (சிவப்பு-பச்சை)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="481725854987912389">"நிறம் அடையாளங்காண முடியாமை (நீலம்-மஞ்சள்)"</string>
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"அலாரம்: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"கால அளவு"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ஒவ்வொரு முறையும் கேள்"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"சற்றுமுன்"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index c30b9cd..cb2403c 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>కి"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"వ్యవధి"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ప్రతిసారి అడుగు"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"ఇప్పుడే"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index b937a7d..e89a4d0 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"วัน<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"ระยะเวลา"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ถามทุกครั้ง"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"เมื่อสักครู่"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 2e62dc0..e527eec 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"sa <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Tagal"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Magtanong palagi"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Ngayon lang"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 0995a80..2f7f2f6 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"zaman: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Süre"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Her zaman sor"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Az önce"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 06fe036..cb99b5d 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -451,5 +451,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Тривалість"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Запитувати щоразу"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Щойно"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 6b33df7..af5e83c 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g> بجے"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"مدت"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ہر بار پوچھیں"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"ابھی ابھی"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index fdb6bb6..0072db8 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Davomiyligi"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Har safar so‘ralsin"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Hozir"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 8872108..b3b5a45 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"vào <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Thời lượng"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Luôn hỏi"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Vừa xong"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index addad73..cdd069b 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"时间:<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"持续时间"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都询问"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"刚刚"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 8054548..f7b6fc1 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"時間:<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"持續時間"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都詢問"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"剛剛"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 95d8b5d..6dc0720 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"時間:<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"時間長度"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都詢問"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"剛剛"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 541c588..ae552fc 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -449,5 +449,7 @@
     <string name="alarm_template_far" msgid="3779172822607461675">"nge-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Ubude besikhathi"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Buza njalo"</string>
+    <!-- no translation found for zen_mode_forever (2704305038191592967) -->
+    <skip />
     <string name="time_unit_just_now" msgid="6363336622778342422">"Khona manje"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7788f7e..6bc07c2 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1117,7 +1117,10 @@
     <string name="zen_mode_duration_settings_title">Duration</string>
     <!-- Do not disturb: Duration option to always prompt for the duration of dnd -->
     <string name="zen_mode_duration_always_prompt_title">Ask every time</string>
+    <!-- Do not disturb: Duration option to always have DND on until it is manually turned off [CHAR LIMIT=60] -->
+    <string name="zen_mode_forever">Until you turn off</string>
 
     <!-- time label for event have that happened very recently [CHAR LIMIT=60] -->
     <string name="time_unit_just_now">Just now</string>
-</resources>
+
+  </resources>
diff --git a/packages/SettingsLib/res/values/styles_support_preference.xml b/packages/SettingsLib/res/values/styles_support_preference.xml
index 6e61196..5d787f8 100644
--- a/packages/SettingsLib/res/values/styles_support_preference.xml
+++ b/packages/SettingsLib/res/values/styles_support_preference.xml
@@ -26,7 +26,7 @@
         <item name="allowDividerAbove">true</item>
     </style>
 
-    <style name="PreferenceThemeOverlay.SettingsBase" parent="@style/PreferenceThemeOverlay">
+    <style name="PreferenceThemeOverlay.SettingsBase" parent="@style/PreferenceThemeOverlay.v14.Material">
         <item name="footerPreferenceStyle">@style/Preference.FooterPreference.SettingsBase</item>
     </style>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreferenceCompat.java b/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreferenceCompat.java
new file mode 100644
index 0000000..6ac9d4e
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreferenceCompat.java
@@ -0,0 +1,129 @@
+/*
+ * 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;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.DialogPreference;
+import androidx.preference.PreferenceDialogFragmentCompat;
+
+public class CustomDialogPreferenceCompat extends DialogPreference {
+
+    private CustomPreferenceDialogFragment mFragment;
+    private DialogInterface.OnShowListener mOnShowListener;
+
+    public CustomDialogPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public CustomDialogPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public CustomDialogPreferenceCompat(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CustomDialogPreferenceCompat(Context context) {
+        super(context);
+    }
+
+    public boolean isDialogOpen() {
+        return getDialog() != null && getDialog().isShowing();
+    }
+
+    public Dialog getDialog() {
+        return mFragment != null ? mFragment.getDialog() : null;
+    }
+
+    public void setOnShowListener(DialogInterface.OnShowListener listner) {
+        mOnShowListener = listner;
+    }
+
+    protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
+            DialogInterface.OnClickListener listener) {
+    }
+
+    protected void onDialogClosed(boolean positiveResult) {
+    }
+
+    protected void onClick(DialogInterface dialog, int which) {
+    }
+
+    protected void onBindDialogView(View view) {
+    }
+
+    private void setFragment(CustomPreferenceDialogFragment fragment) {
+        mFragment = fragment;
+    }
+
+    private DialogInterface.OnShowListener getOnShowListener() {
+        return mOnShowListener;
+    }
+
+    public static class CustomPreferenceDialogFragment extends PreferenceDialogFragmentCompat {
+
+        public static CustomPreferenceDialogFragment newInstance(String key) {
+            final CustomPreferenceDialogFragment fragment = new CustomPreferenceDialogFragment();
+            final Bundle b = new Bundle(1);
+            b.putString(ARG_KEY, key);
+            fragment.setArguments(b);
+            return fragment;
+        }
+
+        private CustomDialogPreferenceCompat getCustomizablePreference() {
+            return (CustomDialogPreferenceCompat) getPreference();
+        }
+
+        @Override
+        protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+            super.onPrepareDialogBuilder(builder);
+            getCustomizablePreference().setFragment(this);
+            getCustomizablePreference().onPrepareDialogBuilder(builder, this);
+        }
+
+        @Override
+        public void onDialogClosed(boolean positiveResult) {
+            getCustomizablePreference().onDialogClosed(positiveResult);
+        }
+
+        @Override
+        protected void onBindDialogView(View view) {
+            super.onBindDialogView(view);
+            getCustomizablePreference().onBindDialogView(view);
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Dialog dialog = super.onCreateDialog(savedInstanceState);
+            dialog.setOnShowListener(getCustomizablePreference().getOnShowListener());
+            return dialog;
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            super.onClick(dialog, which);
+            getCustomizablePreference().onClick(dialog, which);
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreferenceCompat.java b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreferenceCompat.java
new file mode 100644
index 0000000..6ddc89a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreferenceCompat.java
@@ -0,0 +1,136 @@
+/*
+ * 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;
+
+import static android.text.InputType.TYPE_CLASS_TEXT;
+import static android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.EditText;
+
+import androidx.annotation.CallSuper;
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.EditTextPreference;
+import androidx.preference.EditTextPreferenceDialogFragmentCompat;
+
+public class CustomEditTextPreferenceCompat extends EditTextPreference {
+
+    private CustomPreferenceDialogFragment mFragment;
+
+    public CustomEditTextPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public CustomEditTextPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public CustomEditTextPreferenceCompat(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CustomEditTextPreferenceCompat(Context context) {
+        super(context);
+    }
+
+    public EditText getEditText() {
+        if (mFragment != null) {
+            final Dialog dialog = mFragment.getDialog();
+            if (dialog != null) {
+                return (EditText) dialog.findViewById(android.R.id.edit);
+            }
+        }
+        return null;
+    }
+
+    public boolean isDialogOpen() {
+        return getDialog() != null && getDialog().isShowing();
+    }
+
+    public Dialog getDialog() {
+        return mFragment != null ? mFragment.getDialog() : null;
+    }
+
+    protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
+            DialogInterface.OnClickListener listener) {
+    }
+
+    protected void onDialogClosed(boolean positiveResult) {
+    }
+
+    protected void onClick(DialogInterface dialog, int which) {
+    }
+
+    @CallSuper
+    protected void onBindDialogView(View view) {
+        final EditText editText = view.findViewById(android.R.id.edit);
+        if (editText != null) {
+            editText.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_SENTENCES);
+            editText.requestFocus();
+        }
+    }
+
+    private void setFragment(CustomPreferenceDialogFragment fragment) {
+        mFragment = fragment;
+    }
+
+    public static class CustomPreferenceDialogFragment extends
+            EditTextPreferenceDialogFragmentCompat {
+
+        public static CustomPreferenceDialogFragment newInstance(String key) {
+            final CustomPreferenceDialogFragment fragment = new CustomPreferenceDialogFragment();
+            final Bundle b = new Bundle(1);
+            b.putString(ARG_KEY, key);
+            fragment.setArguments(b);
+            return fragment;
+        }
+
+        private CustomEditTextPreferenceCompat getCustomizablePreference() {
+            return (CustomEditTextPreferenceCompat) getPreference();
+        }
+
+        @Override
+        protected void onBindDialogView(View view) {
+            super.onBindDialogView(view);
+            getCustomizablePreference().onBindDialogView(view);
+        }
+
+        @Override
+        protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+            super.onPrepareDialogBuilder(builder);
+            getCustomizablePreference().setFragment(this);
+            getCustomizablePreference().onPrepareDialogBuilder(builder, this);
+        }
+
+        @Override
+        public void onDialogClosed(boolean positiveResult) {
+            super.onDialogClosed(positiveResult);
+            getCustomizablePreference().onDialogClosed(positiveResult);
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            super.onClick(dialog, which);
+            getCustomizablePreference().onClick(dialog, which);
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index 8473c06..a18600a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -40,9 +40,6 @@
 public class AccessibilityUtils {
     public static final char ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ':';
 
-    final static TextUtils.SimpleStringSplitter sStringColonSplitter =
-            new TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
-
     /**
      * @return the set of enabled accessibility services. If there are no services,
      * it returns the unmodifiable {@link Collections#emptySet()}.
@@ -72,16 +69,16 @@
         final String enabledServicesSetting = Settings.Secure.getStringForUser(
                 context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                 userId);
-        if (enabledServicesSetting == null) {
+        if (TextUtils.isEmpty(enabledServicesSetting)) {
             return Collections.emptySet();
         }
 
         final Set<ComponentName> enabledServices = new HashSet<>();
-        final TextUtils.SimpleStringSplitter colonSplitter = sStringColonSplitter;
+        final TextUtils.StringSplitter colonSplitter =
+                new TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
         colonSplitter.setString(enabledServicesSetting);
 
-        while (colonSplitter.hasNext()) {
-            final String componentNameString = colonSplitter.next();
+        for (String componentNameString : colonSplitter) {
             final ComponentName enabledService = ComponentName.unflattenFromString(
                     componentNameString);
             if (enabledService != null) {
@@ -168,8 +165,7 @@
      * an OEM-configurable default if the setting has never been set.
      *
      * @param context A valid context
-     * @param userId The user whose settings should be checked
-     *
+     * @param userId  The user whose settings should be checked
      * @return The component name, flattened to a string, of the target service.
      */
     public static String getShortcutTargetServiceComponentNameString(
@@ -187,9 +183,9 @@
      * Check if the accessibility shortcut is enabled for a user
      *
      * @param context A valid context
-     * @param userId The user of interest
+     * @param userId  The user of interest
      * @return {@code true} if the shortcut is enabled for the user. {@code false} otherwise.
-     *         Note that the shortcut may be enabled, but no action associated with it.
+     * Note that the shortcut may be enabled, but no action associated with it.
      */
     public static boolean isShortcutEnabled(Context context, int userId) {
         return Settings.Secure.getIntForUser(context.getContentResolver(),
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 01efda5..1ce4484 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -71,7 +71,7 @@
                 // we may add a new device here, but generally this should not happen
                 if (device == null) {
                     Log.w(TAG, "A2dpProfile found new device: " + nextDevice);
-                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, nextDevice);
                 }
                 device.onProfileStateChanged(A2dpProfile.this, BluetoothProfile.STATE_CONNECTED);
                 device.refresh();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index 2cf1d58..6a4aa0a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -67,7 +67,7 @@
                 // we may add a new device here, but generally this should not happen
                 if (device == null) {
                     Log.w(TAG, "A2dpSinkProfile found new device: " + nextDevice);
-                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, nextDevice);
                 }
                 device.onProfileStateChanged(A2dpSinkProfile.this, BluetoothProfile.STATE_CONNECTED);
                 device.refresh();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index c7f8c20..a91c45d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -22,14 +22,110 @@
  * UI to receive events from {@link BluetoothEventManager}.
  */
 public interface BluetoothCallback {
-    void onBluetoothStateChanged(int bluetoothState);
-    void onScanningStateChanged(boolean started);
-    void onDeviceAdded(CachedBluetoothDevice cachedDevice);
-    void onDeviceDeleted(CachedBluetoothDevice cachedDevice);
-    void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
-    void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state);
-    void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile);
-    void onAudioModeChanged();
+    /**
+     * It will be called when the state of the local Bluetooth adapter has been changed.
+     * It is listening {@link android.bluetooth.BluetoothAdapter#ACTION_STATE_CHANGED}.
+     * For example, Bluetooth has been turned on or off.
+     *
+     * @param bluetoothState the current Bluetooth state, the possible values are:
+     * {@link android.bluetooth.BluetoothAdapter#STATE_OFF},
+     * {@link android.bluetooth.BluetoothAdapter#STATE_TURNING_ON},
+     * {@link android.bluetooth.BluetoothAdapter#STATE_ON},
+     * {@link android.bluetooth.BluetoothAdapter#STATE_TURNING_OFF}.
+     */
+    default void onBluetoothStateChanged(int bluetoothState) {}
+
+    /**
+     * It will be called when the local Bluetooth adapter has started
+     * or finished the remote device discovery process.
+     * It is listening {@link android.bluetooth.BluetoothAdapter#ACTION_DISCOVERY_STARTED} and
+     * {@link android.bluetooth.BluetoothAdapter#ACTION_DISCOVERY_FINISHED}.
+     *
+     * @param started indicate the current process is started or finished.
+     */
+    default void onScanningStateChanged(boolean started) {}
+
+    /**
+     * It will be called in following situations:
+     * 1. In scanning mode, when a new device has been found.
+     * 2. When a profile service is connected and existing connected devices has been found.
+     * This API only invoked once for each device and all devices will be cached in
+     * {@link CachedBluetoothDeviceManager}.
+     *
+     * @param cachedDevice the Bluetooth device.
+     */
+    default void onDeviceAdded(CachedBluetoothDevice cachedDevice) {}
+
+    /**
+     * It will be called when a remote device that was
+     * found in the last discovery and is not found in the current discovery.
+     * It is listening {@link android.bluetooth.BluetoothDevice#ACTION_DISAPPEARED}
+     *
+     * @param cachedDevice the Bluetooth device.
+     */
+    default void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {}
+
+    /**
+     * It will be called when bond state of a remote device is changed.
+     * It is listening {@link android.bluetooth.BluetoothDevice#ACTION_BOND_STATE_CHANGED}
+     *
+     * @param cachedDevice the Bluetooth device.
+     * @param bondState the Bluetooth device bond state, the possible values are:
+     * {@link android.bluetooth.BluetoothDevice#BOND_NONE},
+     * {@link android.bluetooth.BluetoothDevice#BOND_BONDING},
+     * {@link android.bluetooth.BluetoothDevice#BOND_BONDED}.
+     */
+    default void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {}
+
+    /**
+     * It will be called in following situations:
+     * 1. When the adapter is not connected to any profiles of any remote devices
+     * and it attempts a connection to a profile.
+     * 2. When the adapter disconnects from the last profile of the last device.
+     * It is listening {@link android.bluetooth.BluetoothAdapter#ACTION_CONNECTION_STATE_CHANGED}
+     *
+     * @param cachedDevice the Bluetooth device.
+     * @param state the Bluetooth device connection state, the possible values are:
+     * {@link android.bluetooth.BluetoothAdapter#STATE_DISCONNECTED},
+     * {@link android.bluetooth.BluetoothAdapter#STATE_CONNECTING},
+     * {@link android.bluetooth.BluetoothAdapter#STATE_CONNECTED},
+     * {@link android.bluetooth.BluetoothAdapter#STATE_DISCONNECTING}.
+     */
+    default void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {}
+
+    /**
+     * It will be called when device been set as active for {@code bluetoothProfile}
+     * It is listening in following intent:
+     * {@link android.bluetooth.BluetoothA2dp#ACTION_ACTIVE_DEVICE_CHANGED}
+     * {@link android.bluetooth.BluetoothHeadset#ACTION_ACTIVE_DEVICE_CHANGED}
+     * {@link android.bluetooth.BluetoothHearingAid#ACTION_ACTIVE_DEVICE_CHANGED}
+     *
+     * @param activeDevice the active Bluetooth device.
+     * @param bluetoothProfile the profile of active Bluetooth device.
+     */
+    default void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
+
+    /**
+     * It will be called in following situations:
+     * 1. When the call state on the device is changed.
+     * 2. When the audio connection state of the A2DP profile is changed.
+     * It is listening in following intent:
+     * {@link android.bluetooth.BluetoothHeadset#ACTION_AUDIO_STATE_CHANGED}
+     * {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
+     */
+    default void onAudioModeChanged() {}
+
+    /**
+     * It will be called when one of the bluetooth device profile connection state is changed.
+     *
+     * @param cachedDevice the active Bluetooth device.
+     * @param state the BluetoothProfile connection state, the possible values are:
+     * {@link android.bluetooth.BluetoothProfile#STATE_CONNECTED},
+     * {@link android.bluetooth.BluetoothProfile#STATE_CONNECTING},
+     * {@link android.bluetooth.BluetoothProfile#STATE_DISCONNECTED},
+     * {@link android.bluetooth.BluetoothProfile#STATE_DISCONNECTING}.
+     * @param bluetoothProfile the BluetoothProfile id.
+     */
     default void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
             int state, int bluetoothProfile) {
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 7611e52..a3f3b59 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -49,42 +49,26 @@
 
     private final LocalBluetoothAdapter mLocalAdapter;
     private final CachedBluetoothDeviceManager mDeviceManager;
-    private LocalBluetoothProfileManager mProfileManager;
     private final IntentFilter mAdapterIntentFilter, mProfileIntentFilter;
     private final Map<String, Handler> mHandlerMap;
-    private Context mContext;
-
-    private final Collection<BluetoothCallback> mCallbacks =
-            new ArrayList<BluetoothCallback>();
+    private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
+    private final BroadcastReceiver mProfileBroadcastReceiver = new BluetoothBroadcastReceiver();
+    private final Collection<BluetoothCallback> mCallbacks = new ArrayList<>();
 
     private android.os.Handler mReceiverHandler;
+    private Context mContext;
 
     interface Handler {
         void onReceive(Context context, Intent intent, BluetoothDevice device);
     }
 
-    private void addHandler(String action, Handler handler) {
-        mHandlerMap.put(action, handler);
-        mAdapterIntentFilter.addAction(action);
-    }
-
-    void addProfileHandler(String action, Handler handler) {
-        mHandlerMap.put(action, handler);
-        mProfileIntentFilter.addAction(action);
-    }
-
-    // Set profile manager after construction due to circular dependency
-    void setProfileManager(LocalBluetoothProfileManager manager) {
-        mProfileManager = manager;
-    }
-
     BluetoothEventManager(LocalBluetoothAdapter adapter,
             CachedBluetoothDeviceManager deviceManager, Context context) {
         mLocalAdapter = adapter;
         mDeviceManager = deviceManager;
         mAdapterIntentFilter = new IntentFilter();
         mProfileIntentFilter = new IntentFilter();
-        mHandlerMap = new HashMap<String, Handler>();
+        mHandlerMap = new HashMap<>();
         mContext = context;
 
         // Bluetooth on/off broadcasts
@@ -109,16 +93,11 @@
         addHandler(BluetoothDevice.ACTION_UUID, new UuidChangedHandler());
         addHandler(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED, new BatteryLevelChangedHandler());
 
-        // Dock event broadcasts
-        addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
-
         // Active device broadcasts
-        addHandler(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED,
-                   new ActiveDeviceChangedHandler());
-        addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED,
-                   new ActiveDeviceChangedHandler());
+        addHandler(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED, new ActiveDeviceChangedHandler());
+        addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED, new ActiveDeviceChangedHandler());
         addHandler(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED,
-                   new ActiveDeviceChangedHandler());
+                new ActiveDeviceChangedHandler());
 
         // Headset state changed broadcasts
         addHandler(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED,
@@ -130,10 +109,6 @@
         mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
     }
 
-    void registerProfileIntentReceiver() {
-        mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
-    }
-
     public void setReceiverHandler(android.os.Handler handler) {
         mContext.unregisterReceiver(mBroadcastReceiver);
         mContext.unregisterReceiver(mProfileBroadcastReceiver);
@@ -156,7 +131,93 @@
         }
     }
 
-    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+    void registerProfileIntentReceiver() {
+        mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
+    }
+
+    void addProfileHandler(String action, Handler handler) {
+        mHandlerMap.put(action, handler);
+        mProfileIntentFilter.addAction(action);
+    }
+
+    boolean readPairedDevices() {
+        Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
+        if (bondedDevices == null) {
+            return false;
+        }
+
+        boolean deviceAdded = false;
+        for (BluetoothDevice device : bondedDevices) {
+            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+            if (cachedDevice == null) {
+                cachedDevice = mDeviceManager.addDevice(mLocalAdapter, device);
+                dispatchDeviceAdded(cachedDevice);
+                deviceAdded = true;
+            }
+        }
+
+        return deviceAdded;
+    }
+
+    void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
+        synchronized (mCallbacks) {
+            for (BluetoothCallback callback : mCallbacks) {
+                callback.onDeviceAdded(cachedDevice);
+            }
+        }
+    }
+
+    void dispatchDeviceRemoved(CachedBluetoothDevice cachedDevice) {
+        synchronized (mCallbacks) {
+            for (BluetoothCallback callback : mCallbacks) {
+                callback.onDeviceDeleted(cachedDevice);
+            }
+        }
+    }
+
+    void dispatchProfileConnectionStateChanged(CachedBluetoothDevice device, int state,
+            int bluetoothProfile) {
+        synchronized (mCallbacks) {
+            for (BluetoothCallback callback : mCallbacks) {
+                callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
+            }
+        }
+        mDeviceManager.onProfileConnectionStateChanged(device, state, bluetoothProfile);
+    }
+
+    private void dispatchConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
+        synchronized (mCallbacks) {
+            for (BluetoothCallback callback : mCallbacks) {
+                callback.onConnectionStateChanged(cachedDevice, state);
+            }
+        }
+    }
+
+    private void dispatchAudioModeChanged() {
+        mDeviceManager.dispatchAudioModeChanged();
+        synchronized (mCallbacks) {
+            for (BluetoothCallback callback : mCallbacks) {
+                callback.onAudioModeChanged();
+            }
+        }
+    }
+
+    private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+            int bluetoothProfile) {
+        mDeviceManager.onActiveDeviceChanged(activeDevice, bluetoothProfile);
+        synchronized (mCallbacks) {
+            for (BluetoothCallback callback : mCallbacks) {
+                callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
+            }
+        }
+    }
+
+    private void addHandler(String action, Handler handler) {
+        mHandlerMap.put(action, handler);
+        mAdapterIntentFilter.addAction(action);
+    }
+
+    private class BluetoothBroadcastReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
@@ -168,27 +229,13 @@
                 handler.onReceive(context, intent, device);
             }
         }
-    };
-
-    private final BroadcastReceiver mProfileBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            BluetoothDevice device = intent
-                    .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-
-            Handler handler = mHandlerMap.get(action);
-            if (handler != null) {
-                handler.onReceive(context, intent, device);
-            }
-        }
-    };
+    }
 
     private class AdapterStateChangedHandler implements Handler {
         public void onReceive(Context context, Intent intent,
                 BluetoothDevice device) {
             int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
-                                    BluetoothAdapter.ERROR);
+                    BluetoothAdapter.ERROR);
             // Reregister Profile Broadcast Receiver as part of TURN OFF
             if (state == BluetoothAdapter.STATE_OFF)
             {
@@ -235,7 +282,7 @@
             // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
             if (cachedDevice == null) {
-                cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
+                cachedDevice = mDeviceManager.addDevice(mLocalAdapter, device);
                 Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "
                         + cachedDevice);
             }
@@ -256,30 +303,6 @@
         }
     }
 
-    private void dispatchConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
-        synchronized (mCallbacks) {
-            for (BluetoothCallback callback : mCallbacks) {
-                callback.onConnectionStateChanged(cachedDevice, state);
-            }
-        }
-    }
-
-    void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
-        synchronized (mCallbacks) {
-            for (BluetoothCallback callback : mCallbacks) {
-                callback.onDeviceAdded(cachedDevice);
-            }
-        }
-    }
-
-    void dispatchDeviceRemoved(CachedBluetoothDevice cachedDevice) {
-        synchronized (mCallbacks) {
-            for (BluetoothCallback callback : mCallbacks) {
-                callback.onDeviceDeleted(cachedDevice);
-            }
-        }
-    }
-
     private class DeviceDisappearedHandler implements Handler {
         public void onReceive(Context context, Intent intent,
                 BluetoothDevice device) {
@@ -313,7 +336,7 @@
                 return;
             }
             int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
-                                               BluetoothDevice.ERROR);
+                    BluetoothDevice.ERROR);
             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
             if (cachedDevice == null) {
                 Log.w(TAG, "CachedBluetoothDevice for device " + device +
@@ -326,7 +349,7 @@
                     Log.w(TAG, "Got bonding state changed for " + device +
                             ", but we have no record of that device.");
 
-                    cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
+                    cachedDevice = mDeviceManager.addDevice(mLocalAdapter, device);
                     dispatchDeviceAdded(cachedDevice);
                 }
             }
@@ -360,24 +383,24 @@
             int errorMsg;
 
             switch(reason) {
-            case BluetoothDevice.UNBOND_REASON_AUTH_FAILED:
-                errorMsg = R.string.bluetooth_pairing_pin_error_message;
-                break;
-            case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED:
-                errorMsg = R.string.bluetooth_pairing_rejected_error_message;
-                break;
-            case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN:
-                errorMsg = R.string.bluetooth_pairing_device_down_error_message;
-                break;
-            case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS:
-            case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
-            case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
-            case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
-                errorMsg = R.string.bluetooth_pairing_error_message;
-                break;
-            default:
-                Log.w(TAG, "showUnbondMessage: Not displaying any message for reason: " + reason);
-                return;
+                case BluetoothDevice.UNBOND_REASON_AUTH_FAILED:
+                    errorMsg = R.string.bluetooth_pairing_pin_error_message;
+                    break;
+                case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED:
+                    errorMsg = R.string.bluetooth_pairing_rejected_error_message;
+                    break;
+                case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN:
+                    errorMsg = R.string.bluetooth_pairing_device_down_error_message;
+                    break;
+                case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS:
+                case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
+                case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
+                case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
+                    errorMsg = R.string.bluetooth_pairing_error_message;
+                    break;
+                default:
+                    Log.w(TAG, "showUnbondMessage: Not displaying any message for reason: " + reason);
+                    return;
             }
             BluetoothUtils.showError(context, name, errorMsg);
         }
@@ -397,22 +420,6 @@
         }
     }
 
-    private class DockEventHandler implements Handler {
-        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
-            // Remove if unpair device upon undocking
-            int anythingButUnDocked = Intent.EXTRA_DOCK_STATE_UNDOCKED + 1;
-            int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, anythingButUnDocked);
-            if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
-                if (device != null && device.getBondState() == BluetoothDevice.BOND_NONE) {
-                    CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
-                    if (cachedDevice != null) {
-                        cachedDevice.setJustDiscovered(false);
-                    }
-                }
-            }
-        }
-    }
-
     private class BatteryLevelChangedHandler implements Handler {
         public void onReceive(Context context, Intent intent,
                 BluetoothDevice device) {
@@ -423,25 +430,6 @@
         }
     }
 
-    boolean readPairedDevices() {
-        Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
-        if (bondedDevices == null) {
-            return false;
-        }
-
-        boolean deviceAdded = false;
-        for (BluetoothDevice device : bondedDevices) {
-            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
-            if (cachedDevice == null) {
-                cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
-                dispatchDeviceAdded(cachedDevice);
-                deviceAdded = true;
-            }
-        }
-
-        return deviceAdded;
-    }
-
     private class ActiveDeviceChangedHandler implements Handler {
         @Override
         public void onReceive(Context context, Intent intent, BluetoothDevice device) {
@@ -466,16 +454,6 @@
         }
     }
 
-    private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
-                                             int bluetoothProfile) {
-        mDeviceManager.onActiveDeviceChanged(activeDevice, bluetoothProfile);
-        synchronized (mCallbacks) {
-            for (BluetoothCallback callback : mCallbacks) {
-                callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
-            }
-        }
-    }
-
     private class AudioModeChangedHandler implements Handler {
 
         @Override
@@ -488,23 +466,4 @@
             dispatchAudioModeChanged();
         }
     }
-
-    private void dispatchAudioModeChanged() {
-        mDeviceManager.dispatchAudioModeChanged();
-        synchronized (mCallbacks) {
-            for (BluetoothCallback callback : mCallbacks) {
-                callback.onAudioModeChanged();
-            }
-        }
-    }
-
-    void dispatchProfileConnectionStateChanged(CachedBluetoothDevice device, int state,
-            int bluetoothProfile) {
-        synchronized (mCallbacks) {
-            for (BluetoothCallback callback : mCallbacks) {
-                callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
-            }
-        }
-        mDeviceManager.onProfileConnectionStateChanged(device, state, bluetoothProfile);
-    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 1faca0c..b90d307 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1194,7 +1194,8 @@
      * @return {@code true} if {@code cachedBluetoothDevice} is a2dp device
      */
     public boolean isA2dpDevice() {
-        return mProfileManager.getA2dpProfile().getConnectionStatus(mDevice) ==
+        A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+        return a2dpProfile != null && a2dpProfile.getConnectionStatus(mDevice) ==
                 BluetoothProfile.STATE_CONNECTED;
     }
 
@@ -1202,7 +1203,17 @@
      * @return {@code true} if {@code cachedBluetoothDevice} is HFP device
      */
     public boolean isHfpDevice() {
-        return mProfileManager.getHeadsetProfile().getConnectionStatus(mDevice) ==
+        HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+        return headsetProfile != null && headsetProfile.getConnectionStatus(mDevice) ==
+                BluetoothProfile.STATE_CONNECTED;
+    }
+
+    /**
+     * @return {@code true} if {@code cachedBluetoothDevice} is Hearing Aid device
+     */
+    public boolean isConnectedHearingAidDevice() {
+        HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
+        return hearingAidProfile != null && hearingAidProfile.getConnectionStatus(mDevice) ==
                 BluetoothProfile.STATE_CONNECTED;
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 4104e2f..475ece8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -107,9 +107,8 @@
      * @param device the address of the new Bluetooth device
      * @return the newly created CachedBluetoothDevice object
      */
-    public CachedBluetoothDevice addDevice(LocalBluetoothAdapter adapter,
-            LocalBluetoothProfileManager profileManager,
-            BluetoothDevice device) {
+    public CachedBluetoothDevice addDevice(LocalBluetoothAdapter adapter, BluetoothDevice device) {
+        LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
         CachedBluetoothDevice newDevice = new CachedBluetoothDevice(mContext, adapter,
             profileManager, device);
         if (profileManager.getHearingAidProfile() != null
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 6a4d978..e284382 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -70,7 +70,7 @@
                 // we may add a new device here, but generally this should not happen
                 if (device == null) {
                     Log.w(TAG, "HeadsetProfile found new device: " + nextDevice);
-                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, nextDevice);
                 }
                 device.onProfileStateChanged(HeadsetProfile.this,
                         BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 1747d28..a0cf105 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -64,7 +64,7 @@
                     if (V) {
                         Log.d(TAG, "HearingAidProfile found new device: " + nextDevice);
                     }
-                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, nextDevice);
                 }
                 device.onProfileStateChanged(HearingAidProfile.this,
                         BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 94c84b9..b8c72fb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -71,7 +71,7 @@
                 // we may add a new device here, but generally this should not happen
                 if (device == null) {
                     Log.w(TAG, "HfpClient profile found new device: " + nextDevice);
-                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, nextDevice);
                 }
                 device.onProfileStateChanged(
                     HfpClientProfile.this, BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index 179a60d..f9da109 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -73,7 +73,7 @@
                 // we may add a new device here, but generally this should not happen
                 if (device == null) {
                     Log.w(TAG, "HidProfile found new device: " + nextDevice);
-                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, nextDevice);
                 }
                 Log.d(TAG, "Connection status changed: " + device);
                 device.onProfileStateChanged(HidDeviceProfile.this,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index da348f9..c5ba58c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -62,7 +62,7 @@
                 // we may add a new device here, but generally this should not happen
                 if (device == null) {
                     Log.w(TAG, "HidProfile found new device: " + nextDevice);
-                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, nextDevice);
                 }
                 device.onProfileStateChanged(HidProfile.this, BluetoothProfile.STATE_CONNECTED);
                 device.refresh();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index f8674a6..5e7f6d4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -35,7 +35,10 @@
  * <p>Connection and bonding state changes affecting specific devices
  * are handled by {@link CachedBluetoothDeviceManager},
  * {@link BluetoothEventManager}, and {@link LocalBluetoothProfileManager}.
+ *
+ * @deprecated use {@link BluetoothAdapter} instead.
  */
+@Deprecated
 public class LocalBluetoothAdapter {
     private static final String TAG = "LocalBluetoothAdapter";
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 6256922..3ebbf7e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -37,6 +37,8 @@
 import androidx.annotation.VisibleForTesting;
 import android.util.Log;
 import com.android.internal.R;
+import com.android.internal.util.CollectionUtils;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -51,8 +53,6 @@
 public class LocalBluetoothProfileManager {
     private static final String TAG = "LocalBluetoothProfileManager";
     private static final boolean DEBUG = BluetoothUtils.D;
-    /** Singleton instance. */
-    private static LocalBluetoothProfileManager sInstance;
 
     /**
      * An interface for notifying BluetoothHeadset IPC clients when they have
@@ -89,12 +89,12 @@
     private HfpClientProfile mHfpClientProfile;
     private MapProfile mMapProfile;
     private MapClientProfile mMapClientProfile;
-    private final HidProfile mHidProfile;
+    private HidProfile mHidProfile;
     private HidDeviceProfile mHidDeviceProfile;
     private OppProfile mOppProfile;
-    private final PanProfile mPanProfile;
+    private PanProfile mPanProfile;
     private PbapClientProfile mPbapClientProfile;
-    private final PbapServerProfile mPbapProfile;
+    private PbapServerProfile mPbapProfile;
     private final boolean mUsePbapPce;
     private final boolean mUseMapClient;
     private HearingAidProfile mHearingAidProfile;
@@ -119,179 +119,107 @@
         mUseMapClient = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
         // pass this reference to adapter and event manager (circular dependency)
         mLocalAdapter.setProfileManager(this);
-        mEventManager.setProfileManager(this);
 
-        ParcelUuid[] uuids = adapter.getUuids();
-
-        // uuids may be null if Bluetooth is turned off
-        if (uuids != null) {
-            updateLocalProfiles(uuids);
-        }
-
-        // Always add HID host, HID device, and PAN profiles
-        mHidProfile = new HidProfile(context, mLocalAdapter, mDeviceManager, this);
-        addProfile(mHidProfile, HidProfile.NAME,
-                BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
-
-        mPanProfile = new PanProfile(context, mLocalAdapter);
-        addPanProfile(mPanProfile, PanProfile.NAME,
-                BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
-
-        mHidDeviceProfile = new HidDeviceProfile(context, mLocalAdapter, mDeviceManager, this);
-        addProfile(mHidDeviceProfile, HidDeviceProfile.NAME,
-                BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
-
-        if(DEBUG) Log.d(TAG, "Adding local MAP profile");
-        if (mUseMapClient) {
-            mMapClientProfile = new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
-            addProfile(mMapClientProfile, MapClientProfile.NAME,
-                BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
-        } else {
-            mMapProfile = new MapProfile(mContext, mLocalAdapter, mDeviceManager, this);
-            addProfile(mMapProfile, MapProfile.NAME,
-                    BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
-        }
-
-        //Create PBAP server profile
-        if(DEBUG) Log.d(TAG, "Adding local PBAP profile");
-
-        mPbapProfile = new PbapServerProfile(context);
-        addProfile(mPbapProfile, PbapServerProfile.NAME,
-             BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED);
-
-        List<Integer> supportedList = mLocalAdapter.getSupportedProfiles();
-        if (supportedList.contains(BluetoothProfile.HEARING_AID)) {
-            mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager,
-                                                       this);
-            addProfile(mHearingAidProfile, HearingAidProfile.NAME,
-                       BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
-        }
+        updateLocalProfiles();
         if (DEBUG) Log.d(TAG, "LocalBluetoothProfileManager construction complete");
     }
 
     /**
-     * Initialize or update the local profile objects. If a UUID was previously
-     * present but has been removed, we print a warning but don't remove the
-     * profile object as it might be referenced elsewhere, or the UUID might
-     * come back and we don't want multiple copies of the profile objects.
-     * @param uuids
+     * create profile instance according to bluetooth supported profile list
      */
-    void updateLocalProfiles(ParcelUuid[] uuids) {
-        // A2DP SRC
-        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSource)) {
-            if (mA2dpProfile == null) {
-                if(DEBUG) Log.d(TAG, "Adding local A2DP SRC profile");
-                mA2dpProfile = new A2dpProfile(mContext, mLocalAdapter, mDeviceManager, this);
-                addProfile(mA2dpProfile, A2dpProfile.NAME,
-                        BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
-            }
-        } else if (mA2dpProfile != null) {
-            Log.w(TAG, "Warning: A2DP profile was previously added but the UUID is now missing.");
+    void updateLocalProfiles() {
+        List<Integer> supportedList = mLocalAdapter.getSupportedProfiles();
+        if (CollectionUtils.isEmpty(supportedList)) {
+            if(DEBUG) Log.d(TAG, "supportedList is null");
+            return;
         }
-
-        // A2DP SINK
-        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSink)) {
-            if (mA2dpSinkProfile == null) {
-                if(DEBUG) Log.d(TAG, "Adding local A2DP Sink profile");
-                mA2dpSinkProfile = new A2dpSinkProfile(mContext, mLocalAdapter, mDeviceManager, this);
-                addProfile(mA2dpSinkProfile, A2dpSinkProfile.NAME,
-                        BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
-            }
-        } else if (mA2dpSinkProfile != null) {
-            Log.w(TAG, "Warning: A2DP Sink profile was previously added but the UUID is now missing.");
+        if (mA2dpProfile == null && supportedList.contains(BluetoothProfile.A2DP)) {
+            if(DEBUG) Log.d(TAG, "Adding local A2DP profile");
+            mA2dpProfile = new A2dpProfile(mContext, mLocalAdapter, mDeviceManager, this);
+            addProfile(mA2dpProfile, A2dpProfile.NAME,
+                    BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
         }
-
-        // Headset / Handsfree
-        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) ||
-            BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP_AG)) {
-            if (mHeadsetProfile == null) {
-                if (DEBUG) Log.d(TAG, "Adding local HEADSET profile");
-                mHeadsetProfile = new HeadsetProfile(mContext, mLocalAdapter,
-                        mDeviceManager, this);
-                addHeadsetProfile(mHeadsetProfile, HeadsetProfile.NAME,
-                        BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED,
-                        BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED,
-                        BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
-            }
-        } else if (mHeadsetProfile != null) {
-            Log.w(TAG, "Warning: HEADSET profile was previously added but the UUID is now missing.");
+        if (mA2dpSinkProfile == null && supportedList.contains(BluetoothProfile.A2DP_SINK)) {
+            if(DEBUG) Log.d(TAG, "Adding local A2DP SINK profile");
+            mA2dpSinkProfile = new A2dpSinkProfile(mContext, mLocalAdapter, mDeviceManager, this);
+            addProfile(mA2dpSinkProfile, A2dpSinkProfile.NAME,
+                    BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
         }
-
-        // Headset HF
-        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree)) {
-            if (mHfpClientProfile == null) {
-                if(DEBUG) Log.d(TAG, "Adding local HfpClient profile");
-                mHfpClientProfile =
-                    new HfpClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
-                addHeadsetProfile(mHfpClientProfile, HfpClientProfile.NAME,
-                        BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED,
-                        BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED,
-                        BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED);
-            }
-        } else if (mHfpClientProfile != null) {
-            Log.w(TAG,
-                "Warning: Hfp Client profile was previously added but the UUID is now missing.");
-        } else {
-            Log.d(TAG, "Handsfree Uuid not found.");
+        if (mHeadsetProfile == null && supportedList.contains(BluetoothProfile.HEADSET)) {
+            if (DEBUG) Log.d(TAG, "Adding local HEADSET profile");
+            mHeadsetProfile = new HeadsetProfile(mContext, mLocalAdapter, mDeviceManager, this);
+            addHeadsetProfile(mHeadsetProfile, HeadsetProfile.NAME,
+                    BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED,
+                    BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED,
+                    BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
         }
-
-        // Message Access Profile Client
-        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.MNS)) {
-            if (mMapClientProfile == null) {
-                if(DEBUG) Log.d(TAG, "Adding local Map Client profile");
+        if (mHfpClientProfile == null && supportedList.contains(BluetoothProfile.HEADSET_CLIENT)) {
+            if(DEBUG) Log.d(TAG, "Adding local HfpClient profile");
+            mHfpClientProfile = new HfpClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
+            addHeadsetProfile(mHfpClientProfile, HfpClientProfile.NAME,
+                    BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED,
+                    BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED,
+                    BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED);
+        }
+        if (mUseMapClient) {
+            if (mMapClientProfile == null && supportedList.contains(BluetoothProfile.MAP_CLIENT)) {
+                if(DEBUG) Log.d(TAG, "Adding local MAP CLIENT profile");
                 mMapClientProfile =
-                        new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
+                        new MapClientProfile(mContext, mLocalAdapter, mDeviceManager,this);
                 addProfile(mMapClientProfile, MapClientProfile.NAME,
                         BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
             }
-        } else if (mMapClientProfile != null) {
-            Log.w(TAG,
-                    "Warning: MAP Client profile was previously added but the UUID is now missing.");
-        } else {
-            Log.d(TAG, "MAP Client Uuid not found.");
+        } else if (mMapProfile == null && supportedList.contains(BluetoothProfile.MAP)) {
+            if(DEBUG) Log.d(TAG, "Adding local MAP profile");
+            mMapProfile = new MapProfile(mContext, mLocalAdapter, mDeviceManager, this);
+            addProfile(mMapProfile, MapProfile.NAME, BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
         }
-
-        // OPP
-        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
-            if (mOppProfile == null) {
-                if(DEBUG) Log.d(TAG, "Adding local OPP profile");
-                mOppProfile = new OppProfile();
-                // Note: no event handler for OPP, only name map.
-                mProfileNameMap.put(OppProfile.NAME, mOppProfile);
-            }
-        } else if (mOppProfile != null) {
-            Log.w(TAG, "Warning: OPP profile was previously added but the UUID is now missing.");
+        if (mOppProfile == null && supportedList.contains(BluetoothProfile.OPP)) {
+            if(DEBUG) Log.d(TAG, "Adding local OPP profile");
+            mOppProfile = new OppProfile();
+            // Note: no event handler for OPP, only name map.
+            mProfileNameMap.put(OppProfile.NAME, mOppProfile);
         }
-
-        //PBAP Client
-        if (mUsePbapPce) {
-            if (mPbapClientProfile == null) {
-                if(DEBUG) Log.d(TAG, "Adding local PBAP Client profile");
-                mPbapClientProfile = new PbapClientProfile(mContext, mLocalAdapter, mDeviceManager,
-                        this);
-                addProfile(mPbapClientProfile, PbapClientProfile.NAME,
-                        BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
-            }
-        } else if (mPbapClientProfile != null) {
-            Log.w(TAG,
-                "Warning: PBAP Client profile was previously added but the UUID is now missing.");
+        if (mHearingAidProfile == null && supportedList.contains(BluetoothProfile.HEARING_AID)) {
+            if(DEBUG) Log.d(TAG, "Adding local Hearing Aid profile");
+            mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager,
+                    this);
+            addProfile(mHearingAidProfile, HearingAidProfile.NAME,
+                    BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
         }
-
-        //Hearing Aid Client
-        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) {
-            if (mHearingAidProfile == null) {
-                if(DEBUG) Log.d(TAG, "Adding local Hearing Aid profile");
-                mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager, this);
-                addProfile(mHearingAidProfile, HearingAidProfile.NAME,
-                        BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
-            }
-        } else if (mHearingAidProfile != null) {
-            Log.w(TAG, "Warning: Hearing Aid profile was previously added but the UUID is now missing.");
+        if (mHidProfile == null && supportedList.contains(BluetoothProfile.HID_HOST)) {
+            if(DEBUG) Log.d(TAG, "Adding local HID_HOST profile");
+            mHidProfile = new HidProfile(mContext, mLocalAdapter, mDeviceManager, this);
+            addProfile(mHidProfile, HidProfile.NAME,
+                    BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
         }
-
+        if (mHidDeviceProfile == null && supportedList.contains(BluetoothProfile.HID_DEVICE)) {
+            if(DEBUG) Log.d(TAG, "Adding local HID_DEVICE profile");
+            mHidDeviceProfile = new HidDeviceProfile(mContext, mLocalAdapter, mDeviceManager, this);
+            addProfile(mHidDeviceProfile, HidDeviceProfile.NAME,
+                    BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
+        }
+        if (mPanProfile == null && supportedList.contains(BluetoothProfile.PAN)) {
+            if(DEBUG) Log.d(TAG, "Adding local PAN profile");
+            mPanProfile = new PanProfile(mContext, mLocalAdapter);
+            addPanProfile(mPanProfile, PanProfile.NAME,
+                    BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
+        }
+        if (mPbapProfile == null && supportedList.contains(BluetoothProfile.PBAP)) {
+            if(DEBUG) Log.d(TAG, "Adding local PBAP profile");
+            mPbapProfile = new PbapServerProfile(mContext);
+            addProfile(mPbapProfile, PbapServerProfile.NAME,
+                    BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED);
+        }
+        if (mUsePbapPce && mPbapClientProfile == null && supportedList.contains(
+                BluetoothProfile.PBAP_CLIENT)) {
+            if(DEBUG) Log.d(TAG, "Adding local PBAP Client profile");
+            mPbapClientProfile = new PbapClientProfile(mContext, mLocalAdapter, mDeviceManager,
+                    this);
+            addProfile(mPbapClientProfile, PbapClientProfile.NAME,
+                    BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
+        }
         mEventManager.registerProfileIntentReceiver();
-
-        // There is no local SDP record for HID and Settings app doesn't control PBAP Server.
     }
 
     private void addHeadsetProfile(LocalBluetoothProfile profile, String profileName,
@@ -325,10 +253,7 @@
 
     // Called from LocalBluetoothAdapter when state changes to ON
     void setBluetoothStateOn() {
-        ParcelUuid[] uuids = mLocalAdapter.getUuids();
-        if (uuids != null) {
-            updateLocalProfiles(uuids);
-        }
+        updateLocalProfiles();
         mEventManager.readPairedDevices();
     }
 
@@ -346,8 +271,7 @@
             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
             if (cachedDevice == null) {
                 Log.w(TAG, "StateChangedHandler found new device: " + device);
-                cachedDevice = mDeviceManager.addDevice(mLocalAdapter,
-                        LocalBluetoothProfileManager.this, device);
+                cachedDevice = mDeviceManager.addDevice(mLocalAdapter, device);
             }
             onReceiveInternal(intent, cachedDevice);
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 6aa32fc..63af24c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -71,7 +71,7 @@
                 // we may add a new device here, but generally this should not happen
                 if (device == null) {
                     Log.w(TAG, "MapProfile found new device: " + nextDevice);
-                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, nextDevice);
                 }
                 device.onProfileStateChanged(MapClientProfile.this,
                         BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index c53cacc..2c63d50 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -70,7 +70,7 @@
                 // we may add a new device here, but generally this should not happen
                 if (device == null) {
                     Log.w(TAG, "MapProfile found new device: " + nextDevice);
-                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, nextDevice);
                 }
                 device.onProfileStateChanged(MapProfile.this,
                         BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index b735c23..d34ad30 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -69,7 +69,7 @@
                 // we may add a new device here, but generally this should not happen
                 if (device == null) {
                     Log.w(TAG, "PbapClientProfile found new device: " + nextDevice);
-                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, nextDevice);
                 }
                 device.onProfileStateChanged(PbapClientProfile.this, BluetoothProfile.STATE_CONNECTED);
                 device.refresh();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 60985f3..f1d73ed 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -69,7 +69,7 @@
                 // we may add a new device here, but generally this should not happen
                 if (device == null) {
                     Log.w(TAG, "SapProfile found new device: " + nextDevice);
-                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, nextDevice);
                 }
                 device.onProfileStateChanged(SapProfile.this,
                         BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
index f9aa062..2bd0b27 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
@@ -24,20 +24,22 @@
 
 import android.annotation.Nullable;
 import android.app.Activity;
-import androidx.lifecycle.LifecycleOwner;
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.view.Menu;
 import android.view.MenuItem;
 
+import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.LifecycleOwner;
+
 /**
  * {@link Activity} that has hooks to observe activity lifecycle events.
  */
-public class ObservableActivity extends Activity implements LifecycleOwner {
+public class ObservableActivity extends FragmentActivity implements LifecycleOwner {
 
     private final Lifecycle mLifecycle = new Lifecycle(this);
 
-    public Lifecycle getLifecycle() {
+    public Lifecycle getSettingsLifecycle() {
         return mLifecycle;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java
index 972e062..869f54f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java
@@ -22,14 +22,15 @@
 import static androidx.lifecycle.Lifecycle.Event.ON_START;
 import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
 
-import android.app.DialogFragment;
-import androidx.lifecycle.LifecycleOwner;
 import android.content.Context;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 
+import androidx.fragment.app.DialogFragment;
+import androidx.lifecycle.LifecycleOwner;
+
 /**
  * {@link DialogFragment} that has hooks to observe fragment lifecycle events.
  */
@@ -37,6 +38,10 @@
 
     protected final Lifecycle mLifecycle = new Lifecycle(this);
 
+    public Lifecycle getSettingsLifecycle() {
+        return mLifecycle;
+    }
+
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
@@ -100,9 +105,4 @@
         }
         return lifecycleHandled;
     }
-
-    @Override
-    public Lifecycle getLifecycle() {
-        return mLifecycle;
-    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java
index 55597cc..6ba930d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java
@@ -24,19 +24,20 @@
 import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
 
 import android.annotation.CallSuper;
-import android.app.Fragment;
-import androidx.lifecycle.LifecycleOwner;
 import android.content.Context;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.LifecycleOwner;
+
 public class ObservableFragment extends Fragment implements LifecycleOwner {
 
     private final Lifecycle mLifecycle = new Lifecycle(this);
 
-    public Lifecycle getLifecycle() {
+    public Lifecycle getSettingsLifecycle() {
         return mLifecycle;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
index 904681c..bd1e5a5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
@@ -24,24 +24,25 @@
 import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
 
 import android.annotation.CallSuper;
-import androidx.lifecycle.LifecycleOwner;
 import android.content.Context;
 import android.os.Bundle;
-import androidx.preference.PreferenceFragment;
-import androidx.preference.PreferenceScreen;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+
 /**
- * {@link PreferenceFragment} that has hooks to observe fragment lifecycle events.
+ * {@link PreferenceFragmentCompat} that has hooks to observe fragment lifecycle events.
  */
-public abstract class ObservablePreferenceFragment extends PreferenceFragment
+public abstract class ObservablePreferenceFragment extends PreferenceFragmentCompat
         implements LifecycleOwner {
 
     private final Lifecycle mLifecycle = new Lifecycle(this);
 
-    public Lifecycle getLifecycle() {
+    public Lifecycle getSettingsLifecycle() {
         return mLifecycle;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index 955f64a..75c2533 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -381,7 +381,7 @@
 
             // Create a lookup of local zone IDs.
             final List<String> zoneIds = lookupTimeZoneIdsByCountry(locale.getCountry());
-            localZoneIds = new HashSet<>(zoneIds);
+            localZoneIds = zoneIds != null ? new HashSet<>(zoneIds) : new HashSet<>();
         }
 
         @VisibleForTesting
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
index 332a2a4..ba93970 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
@@ -88,7 +88,7 @@
     }
 
     private void updateTimes() {
-        mUptime.setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime()));
+        mUptime.setSummary(DateUtils.formatElapsedTime(SystemClock.elapsedRealtime() / 1000));
     }
 
     private static class MyHandler extends Handler {
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
index 89d2595..ffbda3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -38,6 +38,10 @@
 
     @VisibleForTesting
     static final String KEY_WIFI_MAC_ADDRESS = "wifi_mac_address";
+    @VisibleForTesting
+    static final int OFF = 0;
+    @VisibleForTesting
+    static final int ON = 1;
 
     private static final String[] CONNECTIVITY_INTENTS = {
             ConnectivityManager.CONNECTIVITY_ACTION,
@@ -80,13 +84,14 @@
     protected void updateConnectivity() {
         WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
         final int macRandomizationMode = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, 0);
+                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, OFF);
         final String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
 
-        if (TextUtils.isEmpty(macAddress)) {
-            mWifiMacAddress.setSummary(R.string.status_unavailable);
-        } else if (macRandomizationMode == 1 && WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
+        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)) {
+            mWifiMacAddress.setSummary(R.string.status_unavailable);
         } else {
             mWifiMacAddress.setSummary(macAddress);
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
deleted file mode 100644
index d960536..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.drawer;
-
-import android.content.ComponentName;
-import android.content.Context;
-import androidx.annotation.VisibleForTesting;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.settingslib.applications.InterestingConfigChanges;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import static java.lang.String.CASE_INSENSITIVE_ORDER;
-
-public class CategoryManager {
-
-    private static final String TAG = "CategoryManager";
-
-    private static CategoryManager sInstance;
-    private final InterestingConfigChanges mInterestingConfigChanges;
-
-    // Tile cache (key: <packageName, activityName>, value: tile)
-    private final Map<Pair<String, String>, Tile> mTileByComponentCache;
-
-    // Tile cache (key: category key, value: category)
-    private final Map<String, DashboardCategory> mCategoryByKeyMap;
-
-    private List<DashboardCategory> mCategories;
-    private String mExtraAction;
-
-    public static CategoryManager get(Context context) {
-        return get(context, null);
-    }
-
-    public static CategoryManager get(Context context, String action) {
-        if (sInstance == null) {
-            sInstance = new CategoryManager(context, action);
-        }
-        return sInstance;
-    }
-
-    CategoryManager(Context context, String action) {
-        mTileByComponentCache = new ArrayMap<>();
-        mCategoryByKeyMap = new ArrayMap<>();
-        mInterestingConfigChanges = new InterestingConfigChanges();
-        mInterestingConfigChanges.applyNewConfig(context.getResources());
-        mExtraAction = action;
-    }
-
-    public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey) {
-        return getTilesByCategory(context, categoryKey, TileUtils.SETTING_PKG);
-    }
-
-    public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey,
-            String settingPkg) {
-        tryInitCategories(context, settingPkg);
-
-        return mCategoryByKeyMap.get(categoryKey);
-    }
-
-    public synchronized List<DashboardCategory> getCategories(Context context) {
-        return getCategories(context, TileUtils.SETTING_PKG);
-    }
-
-    public synchronized List<DashboardCategory> getCategories(Context context, String settingPkg) {
-        tryInitCategories(context, settingPkg);
-        return mCategories;
-    }
-
-    public synchronized void reloadAllCategories(Context context, String settingPkg) {
-        final boolean forceClearCache = mInterestingConfigChanges.applyNewConfig(
-                context.getResources());
-        mCategories = null;
-        tryInitCategories(context, forceClearCache, settingPkg);
-    }
-
-    public synchronized void updateCategoryFromBlacklist(Set<ComponentName> tileBlacklist) {
-        if (mCategories == null) {
-            Log.w(TAG, "Category is null, skipping blacklist update");
-        }
-        for (int i = 0; i < mCategories.size(); i++) {
-            DashboardCategory category = mCategories.get(i);
-            for (int j = 0; j < category.getTilesCount(); j++) {
-                Tile tile = category.getTile(j);
-                if (tileBlacklist.contains(tile.intent.getComponent())) {
-                    category.removeTile(j--);
-                }
-            }
-        }
-    }
-
-    private synchronized void tryInitCategories(Context context, String settingPkg) {
-        // Keep cached tiles by default. The cache is only invalidated when InterestingConfigChange
-        // happens.
-        tryInitCategories(context, false /* forceClearCache */, settingPkg);
-    }
-
-    private synchronized void tryInitCategories(Context context, boolean forceClearCache,
-            String settingPkg) {
-        if (mCategories == null) {
-            if (forceClearCache) {
-                mTileByComponentCache.clear();
-            }
-            mCategoryByKeyMap.clear();
-            mCategories = TileUtils.getCategories(context, mTileByComponentCache,
-                    false /* categoryDefinedInManifest */, mExtraAction, settingPkg);
-            for (DashboardCategory category : mCategories) {
-                mCategoryByKeyMap.put(category.key, category);
-            }
-            backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
-            sortCategories(context, mCategoryByKeyMap);
-            filterDuplicateTiles(mCategoryByKeyMap);
-        }
-    }
-
-    @VisibleForTesting
-    synchronized void backwardCompatCleanupForCategory(
-            Map<Pair<String, String>, Tile> tileByComponentCache,
-            Map<String, DashboardCategory> categoryByKeyMap) {
-        // A package can use a) CategoryKey, b) old category keys, c) both.
-        // Check if a package uses old category key only.
-        // If yes, map them to new category key.
-
-        // Build a package name -> tile map first.
-        final Map<String, List<Tile>> packageToTileMap = new HashMap<>();
-        for (Entry<Pair<String, String>, Tile> tileEntry : tileByComponentCache.entrySet()) {
-            final String packageName = tileEntry.getKey().first;
-            List<Tile> tiles = packageToTileMap.get(packageName);
-            if (tiles == null) {
-                tiles = new ArrayList<>();
-                packageToTileMap.put(packageName, tiles);
-            }
-            tiles.add(tileEntry.getValue());
-        }
-
-        for (Entry<String, List<Tile>> entry : packageToTileMap.entrySet()) {
-            final List<Tile> tiles = entry.getValue();
-            // Loop map, find if all tiles from same package uses old key only.
-            boolean useNewKey = false;
-            boolean useOldKey = false;
-            for (Tile tile : tiles) {
-                if (CategoryKey.KEY_COMPAT_MAP.containsKey(tile.category)) {
-                    useOldKey = true;
-                } else {
-                    useNewKey = true;
-                    break;
-                }
-            }
-            // Uses only old key, map them to new keys one by one.
-            if (useOldKey && !useNewKey) {
-                for (Tile tile : tiles) {
-                    final String newCategoryKey = CategoryKey.KEY_COMPAT_MAP.get(tile.category);
-                    tile.category = newCategoryKey;
-                    // move tile to new category.
-                    DashboardCategory newCategory = categoryByKeyMap.get(newCategoryKey);
-                    if (newCategory == null) {
-                        newCategory = new DashboardCategory();
-                        categoryByKeyMap.put(newCategoryKey, newCategory);
-                    }
-                    newCategory.addTile(tile);
-                }
-            }
-        }
-    }
-
-    /**
-     * Sort the tiles injected from all apps such that if they have the same priority value,
-     * they wil lbe sorted by package name.
-     * <p/>
-     * A list of tiles are considered sorted when their priority value decreases in a linear
-     * scan.
-     */
-    @VisibleForTesting
-    synchronized void sortCategories(Context context,
-            Map<String, DashboardCategory> categoryByKeyMap) {
-        for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
-            categoryEntry.getValue().sortTiles(context.getPackageName());
-        }
-    }
-
-    /**
-     * Filter out duplicate tiles from category. Duplicate tiles are the ones pointing to the
-     * same intent.
-     */
-    @VisibleForTesting
-    synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) {
-        for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
-            final DashboardCategory category = categoryEntry.getValue();
-            final int count = category.getTilesCount();
-            final Set<ComponentName> components = new ArraySet<>();
-            for (int i = count - 1; i >= 0; i--) {
-                final Tile tile = category.getTile(i);
-                if (tile.intent == null) {
-                    continue;
-                }
-                final ComponentName tileComponent = tile.intent.getComponent();
-                if (components.contains(tileComponent)) {
-                    category.removeTile(i);
-                } else {
-                    components.add(tileComponent);
-                }
-            }
-        }
-    }
-
-    /**
-     * Sort priority value for tiles within a single {@code DashboardCategory}.
-     *
-     * @see #sortCategories(Context, Map)
-     */
-    private synchronized void sortCategoriesForExternalTiles(Context context,
-            DashboardCategory dashboardCategory) {
-        dashboardCategory.sortTiles(context.getPackageName());
-
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
index 3a03644..a3dda65 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -5,7 +5,7 @@
  * you may not use this 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,
@@ -18,55 +18,39 @@
 
 import static java.lang.String.CASE_INSENSITIVE_ORDER;
 
-import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
-import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 
 public class DashboardCategory implements Parcelable {
 
-    private static final String TAG = "DashboardCategory";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    /**
-     * Title of the category that is shown to the user.
-     */
-    public CharSequence title;
-
     /**
      * Key used for placing external tiles.
      */
-    public String key;
-
-    /**
-     * Used to control display order.
-     */
-    public int priority;
+    public final String key;
 
     /**
      * List of the category's children
      */
     private List<Tile> mTiles = new ArrayList<>();
 
-    DashboardCategory(DashboardCategory in) {
-        if (in != null) {
-            title = in.title;
-            key = in.key;
-            priority = in.priority;
-            for (Tile tile : in.mTiles) {
-                mTiles.add(tile);
-            }
-        }
+    public DashboardCategory(String key) {
+        this.key = key;
     }
 
-    public DashboardCategory() {
-        // Empty
+    DashboardCategory(Parcel in) {
+        key = in.readString();
+
+        final int count = in.readInt();
+
+        for (int n = 0; n < count; n++) {
+            Tile tile = Tile.CREATOR.createFromParcel(in);
+            mTiles.add(tile);
+        }
     }
 
     /**
@@ -87,14 +71,6 @@
         mTiles.add(tile);
     }
 
-    public synchronized void addTile(int n, Tile tile) {
-        mTiles.add(n, tile);
-    }
-
-    public synchronized void removeTile(Tile tile) {
-        mTiles.remove(tile);
-    }
-
     public synchronized void removeTile(int n) {
         mTiles.remove(n);
     }
@@ -107,44 +83,29 @@
         return mTiles.get(n);
     }
 
-    public synchronized boolean containsComponent(ComponentName component) {
-        for (Tile tile : mTiles) {
-            if (TextUtils.equals(tile.intent.getComponent().getClassName(),
-                    component.getClassName())) {
-                if (DEBUG) {
-                    Log.d(TAG,  "category " + key + "contains component" + component);
-                }
-                return true;
-            }
-        }
-        if (DEBUG) {
-            Log.d(TAG,  "category " + key + " does not contain component" + component);
-        }
-        return false;
-    }
-
     /**
      * Sort priority value for tiles in this category.
      */
     public void sortTiles() {
-        Collections.sort(mTiles, TILE_COMPARATOR);
+        Collections.sort(mTiles, Tile.TILE_COMPARATOR);
     }
 
     /**
      * Sort priority value and package name for tiles in this category.
      */
     public synchronized void sortTiles(String skipPackageName) {
-        // Sort mTiles based on [priority, package within priority]
+        // Sort mTiles based on [order, package within order]
         Collections.sort(mTiles, (tile1, tile2) -> {
-            final String package1 = tile1.intent.getComponent().getPackageName();
-            final String package2 = tile2.intent.getComponent().getPackageName();
-            final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
-            // First sort by priority
-            final int priorityCompare = tile2.priority - tile1.priority;
-            if (priorityCompare != 0) {
-                return priorityCompare;
+            // First sort by order
+            final int orderCompare = tile2.getOrder() - tile1.getOrder();
+            if (orderCompare != 0) {
+                return orderCompare;
             }
+
             // Then sort by package name, skip package take precedence
+            final String package1 = tile1.getPackageName();
+            final String package2 = tile2.getPackageName();
+            final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
             if (packageCompare != 0) {
                 if (TextUtils.equals(package1, skipPackageName)) {
                     return -1;
@@ -164,9 +125,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        TextUtils.writeToParcel(title, dest, flags);
         dest.writeString(key);
-        dest.writeInt(priority);
 
         final int count = mTiles.size();
         dest.writeInt(count);
@@ -177,23 +136,6 @@
         }
     }
 
-    public void readFromParcel(Parcel in) {
-        title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
-        key = in.readString();
-        priority = in.readInt();
-
-        final int count = in.readInt();
-
-        for (int n = 0; n < count; n++) {
-            Tile tile = Tile.CREATOR.createFromParcel(in);
-            mTiles.add(tile);
-        }
-    }
-
-    DashboardCategory(Parcel in) {
-        readFromParcel(in);
-    }
-
     public static final Creator<DashboardCategory> CREATOR = new Creator<DashboardCategory>() {
         public DashboardCategory createFromParcel(Parcel source) {
             return new DashboardCategory(source);
@@ -204,12 +146,4 @@
         }
     };
 
-    public static final Comparator<Tile> TILE_COMPARATOR =
-            new Comparator<Tile>() {
-                @Override
-                public int compare(Tile lhs, Tile rhs) {
-                    return rhs.priority - lhs.priority;
-                }
-            };
-
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java b/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
deleted file mode 100644
index c79b1466d..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.drawer;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.FragmentManager;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-
-import java.util.List;
-
-public class ProfileSelectDialog extends DialogFragment implements OnClickListener {
-
-    private static final String TAG = "ProfileSelectDialog";
-    private static final String ARG_SELECTED_TILE = "selectedTile";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private Tile mSelectedTile;
-
-    public static void show(FragmentManager manager, Tile tile) {
-        ProfileSelectDialog dialog = new ProfileSelectDialog();
-        Bundle args = new Bundle();
-        args.putParcelable(ARG_SELECTED_TILE, tile);
-        dialog.setArguments(args);
-        dialog.show(manager, "select_profile");
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mSelectedTile = getArguments().getParcelable(ARG_SELECTED_TILE);
-    }
-
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        Context context = getActivity();
-        AlertDialog.Builder builder = new AlertDialog.Builder(context);
-        UserAdapter adapter = UserAdapter.createUserAdapter(UserManager.get(context), context,
-                mSelectedTile.userHandle);
-        builder.setTitle(com.android.settingslib.R.string.choose_profile)
-                .setAdapter(adapter, this);
-
-        return builder.create();
-    }
-
-    @Override
-    public void onClick(DialogInterface dialog, int which) {
-        UserHandle user = mSelectedTile.userHandle.get(which);
-        // Show menu on top level items.
-        mSelectedTile.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        getActivity().startActivityAsUser(mSelectedTile.intent, user);
-    }
-
-    public static void updateUserHandlesIfNeeded(Context context, Tile tile) {
-        List<UserHandle> userHandles = tile.userHandle;
-        if (tile.userHandle == null || tile.userHandle.size() <= 1) {
-            return;
-        }
-        final UserManager userManager = UserManager.get(context);
-        for (int i = userHandles.size() - 1; i >= 0; i--) {
-            if (userManager.getUserInfo(userHandles.get(i).getIdentifier()) == null) {
-                if (DEBUG) {
-                    Log.d(TAG, "Delete the user: " + userHandles.get(i).getIdentifier());
-                }
-                userHandles.remove(i);
-            }
-        }
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
deleted file mode 100644
index 68ead09..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.drawer;
-
-import android.annotation.LayoutRes;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.TypedArray;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.util.ArraySet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager.LayoutParams;
-import android.widget.FrameLayout;
-import android.widget.Toolbar;
-
-import com.android.settingslib.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class SettingsDrawerActivity extends Activity {
-
-    protected static final boolean DEBUG_TIMING = false;
-    private static final String TAG = "SettingsDrawerActivity";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    public static final String EXTRA_SHOW_MENU = "show_drawer_menu";
-
-    // Serves as a temporary list of tiles to ignore until we heard back from the PM that they
-    // are disabled.
-    private static ArraySet<ComponentName> sTileBlacklist = new ArraySet<>();
-
-    private final PackageReceiver mPackageReceiver = new PackageReceiver();
-    private final List<CategoryListener> mCategoryListeners = new ArrayList<>();
-
-    private FrameLayout mContentHeaderContainer;
-
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        long startTime = System.currentTimeMillis();
-
-        TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme);
-        if (!theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
-            getWindow().addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
-            requestWindowFeature(Window.FEATURE_NO_TITLE);
-        }
-        super.setContentView(R.layout.settings_with_drawer);
-        mContentHeaderContainer = findViewById(R.id.content_header_container);
-
-        Toolbar toolbar = findViewById(R.id.action_bar);
-        if (theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
-            toolbar.setVisibility(View.GONE);
-            return;
-        }
-        setActionBar(toolbar);
-
-        if (DEBUG_TIMING) {
-            Log.d(TAG, "onCreate took " + (System.currentTimeMillis() - startTime)
-                    + " ms");
-        }
-    }
-
-    @Override
-    public boolean onNavigateUp() {
-        if (!super.onNavigateUp()) {
-            finish();
-        }
-        return true;
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
-        filter.addDataScheme("package");
-        registerReceiver(mPackageReceiver, filter);
-
-        new CategoriesUpdateTask().execute();
-    }
-
-    @Override
-    protected void onPause() {
-        unregisterReceiver(mPackageReceiver);
-        super.onPause();
-    }
-
-    public void addCategoryListener(CategoryListener listener) {
-        mCategoryListeners.add(listener);
-    }
-
-    public void remCategoryListener(CategoryListener listener) {
-        mCategoryListeners.remove(listener);
-    }
-
-    @Override
-    public void setContentView(@LayoutRes int layoutResID) {
-        final ViewGroup parent = findViewById(R.id.content_frame);
-        if (parent != null) {
-            parent.removeAllViews();
-        }
-        LayoutInflater.from(this).inflate(layoutResID, parent);
-    }
-
-    @Override
-    public void setContentView(View view) {
-        ((ViewGroup) findViewById(R.id.content_frame)).addView(view);
-    }
-
-    @Override
-    public void setContentView(View view, ViewGroup.LayoutParams params) {
-        ((ViewGroup) findViewById(R.id.content_frame)).addView(view, params);
-    }
-
-    private void onCategoriesChanged() {
-        final int N = mCategoryListeners.size();
-        for (int i = 0; i < N; i++) {
-            mCategoryListeners.get(i).onCategoriesChanged();
-        }
-    }
-
-    /**
-     * @return whether or not the enabled state actually changed.
-     */
-    public boolean setTileEnabled(ComponentName component, boolean enabled) {
-        PackageManager pm = getPackageManager();
-        int state = pm.getComponentEnabledSetting(component);
-        boolean isEnabled = state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-        if (isEnabled != enabled || state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
-            if (enabled) {
-                sTileBlacklist.remove(component);
-            } else {
-                sTileBlacklist.add(component);
-            }
-            pm.setComponentEnabledSetting(component, enabled
-                            ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
-                            : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                    PackageManager.DONT_KILL_APP);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Updates dashboard categories. Only necessary to call this after setTileEnabled
-     */
-    public void updateCategories() {
-        new CategoriesUpdateTask().execute();
-    }
-
-    public String getSettingPkg() {
-        return TileUtils.SETTING_PKG;
-    }
-
-    public interface CategoryListener {
-        void onCategoriesChanged();
-    }
-
-    private class CategoriesUpdateTask extends AsyncTask<Void, Void, Void> {
-
-        private final CategoryManager mCategoryManager;
-
-        public CategoriesUpdateTask() {
-            mCategoryManager = CategoryManager.get(SettingsDrawerActivity.this);
-        }
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            mCategoryManager.reloadAllCategories(SettingsDrawerActivity.this, getSettingPkg());
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            mCategoryManager.updateCategoryFromBlacklist(sTileBlacklist);
-            onCategoriesChanged();
-        }
-    }
-
-    private class PackageReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            new CategoriesUpdateTask().execute();
-        }
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
index f1d43bf..84c8b21 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
@@ -5,7 +5,7 @@
  * you may not use this 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,
@@ -16,88 +16,88 @@
 
 package com.android.settingslib.drawer;
 
+import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
+
+import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.text.TextUtils;
-import android.widget.RemoteViews;
 
 import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
 
 /**
  * Description of a single dashboard tile that the user can select.
  */
 public class Tile implements Parcelable {
 
+    private static final String TAG = "Tile";
+
     /**
      * Title of the tile that is shown to the user.
+     *
      * @attr ref android.R.styleable#PreferenceHeader_title
      */
     public CharSequence title;
 
     /**
      * Optional summary describing what this tile controls.
+     *
      * @attr ref android.R.styleable#PreferenceHeader_summary
      */
     public CharSequence summary;
 
     /**
-     * Optional icon to show for this tile.
-     * @attr ref android.R.styleable#PreferenceHeader_icon
-     */
-    public Icon icon;
-
-    /**
-     * Whether the icon can be tinted. This should be set to true for monochrome (single-color)
-     * icons that can be tinted to match the design.
-     */
-    public boolean isIconTintable;
-
-    /**
-     * Intent to launch when the preference is selected.
-     */
-    public Intent intent;
-
-    /**
      * Optional list of user handles which the intent should be launched on.
      */
     public ArrayList<UserHandle> userHandle = new ArrayList<>();
 
     /**
-     * Optional additional data for use by subclasses of the activity
-     */
-    public Bundle extras;
-
-    /**
-     * Category in which the tile should be placed.
-     */
-    public String category;
-
-    /**
-     * Priority of the intent filter that created this tile, used for display ordering.
-     */
-    public int priority;
-
-    /**
      * The metaData from the activity that defines this tile.
      */
-    public Bundle metaData;
+    private final Bundle mMetaData;
+    private final String mActivityPackage;
+    private final String mActivityName;
+    private final Intent mIntent;
 
-    /**
-     * Optional key to use for this tile.
-     */
-    public String key;
+    private ActivityInfo mActivityInfo;
+    private String mCategory;
 
-    /**
-     * Optional remote view which will be displayed instead of the regular title-summary item.
-     */
-    public RemoteViews remoteViews;
+    public Tile(ActivityInfo activityInfo, String category) {
+        mActivityInfo = activityInfo;
+        mActivityPackage = mActivityInfo.packageName;
+        mActivityName = mActivityInfo.name;
+        mMetaData = activityInfo.metaData;
+        mCategory = category;
+        mIntent = new Intent().setClassName(mActivityPackage, mActivityName);
+    }
 
-    public Tile() {
-        // Empty
+    Tile(Parcel in) {
+        mActivityPackage = in.readString();
+        mActivityName = in.readString();
+        mIntent = new Intent().setClassName(mActivityPackage, mActivityName);
+        title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        final int N = in.readInt();
+        for (int i = 0; i < N; i++) {
+            userHandle.add(UserHandle.CREATOR.createFromParcel(in));
+        }
+        mCategory = in.readString();
+        mMetaData = in.readBundle();
     }
 
     @Override
@@ -107,66 +107,151 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mActivityPackage);
+        dest.writeString(mActivityName);
         TextUtils.writeToParcel(title, dest, flags);
         TextUtils.writeToParcel(summary, dest, flags);
-        if (icon != null) {
-            dest.writeByte((byte) 1);
-            icon.writeToParcel(dest, flags);
-        } else {
-            dest.writeByte((byte) 0);
-        }
-        if (intent != null) {
-            dest.writeByte((byte) 1);
-            intent.writeToParcel(dest, flags);
-        } else {
-            dest.writeByte((byte) 0);
-        }
         final int N = userHandle.size();
         dest.writeInt(N);
         for (int i = 0; i < N; i++) {
             userHandle.get(i).writeToParcel(dest, flags);
         }
-        dest.writeBundle(extras);
-        dest.writeString(category);
-        dest.writeInt(priority);
-        dest.writeBundle(metaData);
-        dest.writeString(key);
-        dest.writeParcelable(remoteViews, flags);
-        dest.writeBoolean(isIconTintable);
+        dest.writeString(mCategory);
+        dest.writeBundle(mMetaData);
     }
 
-    public void readFromParcel(Parcel in) {
-        title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
-        summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
-        if (in.readByte() != 0) {
-            icon = Icon.CREATOR.createFromParcel(in);
-        }
-        if (in.readByte() != 0) {
-            intent = Intent.CREATOR.createFromParcel(in);
-        }
-        final int N = in.readInt();
-        for (int i = 0; i < N; i++) {
-            userHandle.add(UserHandle.CREATOR.createFromParcel(in));
-        }
-        extras = in.readBundle();
-        category = in.readString();
-        priority = in.readInt();
-        metaData = in.readBundle();
-        key = in.readString();
-        remoteViews = in.readParcelable(RemoteViews.class.getClassLoader());
-        isIconTintable = in.readBoolean();
+    public String getPackageName() {
+        return mActivityPackage;
     }
 
-    Tile(Parcel in) {
-        readFromParcel(in);
+    /**
+     * Intent to launch when the preference is selected.
+     */
+    public Intent getIntent() {
+        return mIntent;
+    }
+
+    /**
+     * Category in which the tile should be placed.
+     */
+    public String getCategory() {
+        return mCategory;
+    }
+
+    public void setCategory(String newCategoryKey) {
+        mCategory = newCategoryKey;
+    }
+
+    /**
+     * Priority of this tile, used for display ordering.
+     */
+    public int getOrder() {
+        if (hasOrder()) {
+            return mMetaData.getInt(META_DATA_KEY_ORDER);
+        } else {
+            return 0;
+        }
+    }
+
+    public boolean hasOrder() {
+        return mMetaData.containsKey(META_DATA_KEY_ORDER)
+                && mMetaData.get(META_DATA_KEY_ORDER) instanceof Integer;
+    }
+
+    public Bundle getMetaData() {
+        return mMetaData;
+    }
+
+    /**
+     * Optional key to use for this tile.
+     */
+    public String getKey(Context context) {
+        if (!hasKey()) {
+            return null;
+        }
+        if (mMetaData.get(META_DATA_PREFERENCE_KEYHINT) instanceof Integer) {
+            return context.getResources().getString(mMetaData.getInt(META_DATA_PREFERENCE_KEYHINT));
+        } else {
+            return mMetaData.getString(META_DATA_PREFERENCE_KEYHINT);
+        }
+    }
+
+    public boolean hasKey() {
+        return mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_KEYHINT);
+    }
+
+    /**
+     * Optional icon to show for this tile.
+     *
+     * @attr ref android.R.styleable#PreferenceHeader_icon
+     */
+    public Icon getIcon(Context context) {
+        if (context == null || mMetaData == null) {
+            return null;
+        }
+
+        int iconResId = mMetaData.getInt(META_DATA_PREFERENCE_ICON);
+        // Set the icon
+        if (iconResId == 0) {
+            // Only fallback to activityinfo.icon if metadata does not contain ICON_URI.
+            // ICON_URI should be loaded in app UI when need the icon object. Handling IPC at this
+            // level is too complex because we don't have a strong threading contract for this class
+            if (!mMetaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
+                iconResId = getActivityInfo(context).icon;
+            }
+        }
+        if (iconResId != 0) {
+            return Icon.createWithResource(getActivityInfo(context).packageName, iconResId);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Whether the icon can be tinted. This is true when icon needs to be monochrome (single-color)
+     */
+    public boolean isIconTintable(Context context) {
+        if (mMetaData != null
+                && mMetaData.containsKey(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE)) {
+            return mMetaData.getBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE);
+        }
+        final String pkgName = context.getPackageName();
+        // If this drawable is coming from outside Settings, tint it to match the color.
+        final ActivityInfo activityInfo = getActivityInfo(context);
+        return activityInfo != null
+                && !TextUtils.equals(pkgName, activityInfo.packageName);
+    }
+
+    private ActivityInfo getActivityInfo(Context context) {
+        if (mActivityInfo == null) {
+            final PackageManager pm = context.getApplicationContext().getPackageManager();
+            final Intent intent = new Intent().setClassName(mActivityPackage, mActivityName);
+            final List<ResolveInfo> infoList =
+                    pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
+            if (infoList != null && !infoList.isEmpty()) {
+                mActivityInfo = infoList.get(0).activityInfo;
+            }
+        }
+        return mActivityInfo;
     }
 
     public static final Creator<Tile> CREATOR = new Creator<Tile>() {
         public Tile createFromParcel(Parcel source) {
             return new Tile(source);
         }
+
         public Tile[] newArray(int size) {
             return new Tile[size];
         }
     };
+
+    public boolean isPrimaryProfileOnly() {
+        String profile = mMetaData != null ?
+                mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
+        profile = (profile != null ? profile : PROFILE_ALL);
+        return TextUtils.equals(profile, PROFILE_PRIMARY);
+    }
+
+    public static final Comparator<Tile> TILE_COMPARATOR =
+            (lhs, rhs) -> rhs.getOrder() - lhs.getOrder();
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 0f0e4e5..f39b851 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -24,7 +24,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
-import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -34,11 +33,10 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
-import android.widget.RemoteViews;
+
+import androidx.annotation.VisibleForTesting;
 
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -49,6 +47,8 @@
     private static final boolean DEBUG_TIMING = false;
 
     private static final String LOG_TAG = "TileUtils";
+    @VisibleForTesting
+    static final String SETTING_PKG = "com.android.settings";
 
     /**
      * Settings will search for system activities of this action and add them as a top level
@@ -65,21 +65,17 @@
      *
      * <p>A summary my be defined by meta-data named {@link #META_DATA_PREFERENCE_SUMMARY}
      */
-    public static final String EXTRA_SETTINGS_ACTION =
-            "com.android.settings.action.EXTRA_SETTINGS";
+    public static final String EXTRA_SETTINGS_ACTION = "com.android.settings.action.EXTRA_SETTINGS";
 
     /**
      * @See {@link #EXTRA_SETTINGS_ACTION}.
      */
-    private static final String IA_SETTINGS_ACTION =
-            "com.android.settings.action.IA_SETTINGS";
-
+    private static final String IA_SETTINGS_ACTION = "com.android.settings.action.IA_SETTINGS";
 
     /**
      * Same as #EXTRA_SETTINGS_ACTION but used for the platform Settings activities.
      */
-    private static final String SETTINGS_ACTION =
-            "com.android.settings.action.SETTINGS";
+    private static final String SETTINGS_ACTION = "com.android.settings.action.SETTINGS";
 
     private static final String OPERATOR_SETTINGS =
             "com.android.settings.OPERATOR_APPLICATION_SETTING";
@@ -96,11 +92,7 @@
     /**
      * The key used to get the category from metadata of activities of action
      * {@link #EXTRA_SETTINGS_ACTION}
-     * The value must be one of:
-     * <li>com.android.settings.category.wireless</li>
-     * <li>com.android.settings.category.device</li>
-     * <li>com.android.settings.category.personal</li>
-     * <li>com.android.settings.category.system</li>
+     * The value must be from {@link CategoryKey}.
      */
     private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
 
@@ -108,7 +100,7 @@
      * The key used to get the package name of the icon resource for the preference.
      */
     private static final String EXTRA_PREFERENCE_ICON_PACKAGE =
-        "com.android.settings.icon_package";
+            "com.android.settings.icon_package";
 
     /**
      * Name of the meta-data item that should be set in the AndroidManifest.xml
@@ -117,6 +109,12 @@
     public static final String META_DATA_PREFERENCE_KEYHINT = "com.android.settings.keyhint";
 
     /**
+     * Order of the item that should be displayed on screen. Bigger value items displays closer on
+     * top.
+     */
+    public static final String META_DATA_KEY_ORDER = "com.android.settings.order";
+
+    /**
      * Name of the meta-data item that should be set in the AndroidManifest.xml
      * to specify the icon that should be displayed for the preference.
      */
@@ -172,49 +170,41 @@
             "com.android.settings.summary_uri";
 
     /**
-     * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
-     * custom view which should be displayed for the preference. The custom view will be inflated
-     * as a remote view.
+     * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile,
+     * the app will always be run in the primary profile.
      *
-     * This also can be used with {@link #META_DATA_PREFERENCE_SUMMARY_URI}, by setting the id
-     * of the summary TextView to '@android:id/summary'.
+     * @see #META_DATA_KEY_PROFILE
      */
-    public static final String META_DATA_PREFERENCE_CUSTOM_VIEW =
-            "com.android.settings.custom_view";
-
-    public static final String SETTING_PKG = "com.android.settings";
+    public static final String PROFILE_PRIMARY = "primary_profile_only";
 
     /**
-     * Build a list of DashboardCategory. Each category must be defined in manifest.
-     * eg: .Settings$DeviceSettings
-     * @deprecated
+     * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile, the user
+     * will be presented with a dialog to choose the profile the app will be run in.
+     *
+     * @see #META_DATA_KEY_PROFILE
      */
-    @Deprecated
-    public static List<DashboardCategory> getCategories(Context context,
-            Map<Pair<String, String>, Tile> cache) {
-        return getCategories(context, cache, true /*categoryDefinedInManifest*/);
-    }
+    public static final String PROFILE_ALL = "all_profiles";
+
+    /**
+     * Name of the meta-data item that should be set in the AndroidManifest.xml
+     * to specify the profile in which the app should be run when the device has a managed profile.
+     * The default value is {@link #PROFILE_ALL} which means the user will be presented with a
+     * dialog to choose the profile. If set to {@link #PROFILE_PRIMARY} the app will always be
+     * run in the primary profile.
+     *
+     * @see #PROFILE_PRIMARY
+     * @see #PROFILE_ALL
+     */
+    public static final String META_DATA_KEY_PROFILE = "com.android.settings.profile";
 
     /**
      * Build a list of DashboardCategory.
-     * @param categoryDefinedInManifest If true, an dummy activity must exists in manifest to
-     * represent this category (eg: .Settings$DeviceSettings)
-     */
-    public static List<DashboardCategory> getCategories(Context context,
-            Map<Pair<String, String>, Tile> cache, boolean categoryDefinedInManifest) {
-        return getCategories(context, cache, categoryDefinedInManifest, null, SETTING_PKG);
-    }
-
-    /**
-     * Build a list of DashboardCategory.
-     * @param categoryDefinedInManifest If true, an dummy activity must exists in manifest to
-     * represent this category (eg: .Settings$DeviceSettings)
+     *
      * @param extraAction additional intent filter action to be usetileutild to build the dashboard
-     * categories
+     *                    categories
      */
     public static List<DashboardCategory> getCategories(Context context,
-            Map<Pair<String, String>, Tile> cache, boolean categoryDefinedInManifest,
-            String extraAction, String settingPkg) {
+            Map<Pair<String, String>, Tile> cache, String extraAction) {
         final long startTime = System.currentTimeMillis();
         boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)
                 != 0;
@@ -224,37 +214,33 @@
             // TODO: Needs much optimization, too many PM queries going on here.
             if (user.getIdentifier() == ActivityManager.getCurrentUser()) {
                 // Only add Settings for this user.
-                getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true,
-                        settingPkg);
+                getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true);
                 getTilesForAction(context, user, OPERATOR_SETTINGS, cache,
-                        OPERATOR_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
+                        OPERATOR_DEFAULT_CATEGORY, tiles, false, true);
                 getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,
-                        MANUFACTURER_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
+                        MANUFACTURER_DEFAULT_CATEGORY, tiles, false, true);
             }
             if (setup) {
-                getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false,
-                        settingPkg);
-                if (!categoryDefinedInManifest) {
-                    getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false,
-                            settingPkg);
-                    if (extraAction != null) {
-                        getTilesForAction(context, user, extraAction, cache, null, tiles, false,
-                                settingPkg);
-                    }
+                getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false);
+                getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false);
+                if (extraAction != null) {
+                    getTilesForAction(context, user, extraAction, cache, null, tiles, false);
                 }
             }
         }
 
         HashMap<String, DashboardCategory> categoryMap = new HashMap<>();
         for (Tile tile : tiles) {
-            DashboardCategory category = categoryMap.get(tile.category);
+            final String categoryKey = tile.getCategory();
+            DashboardCategory category = categoryMap.get(categoryKey);
             if (category == null) {
-                category = createCategory(context, tile.category, categoryDefinedInManifest);
+                category = new DashboardCategory(categoryKey);
+
                 if (category == null) {
-                    Log.w(LOG_TAG, "Couldn't find category " + tile.category);
+                    Log.w(LOG_TAG, "Couldn't find category " + categoryKey);
                     continue;
                 }
-                categoryMap.put(category.key, category);
+                categoryMap.put(categoryKey, category);
             }
             category.addTile(tile);
         }
@@ -262,83 +248,40 @@
         for (DashboardCategory category : categories) {
             category.sortTiles();
         }
-        Collections.sort(categories, CATEGORY_COMPARATOR);
-        if (DEBUG_TIMING) Log.d(LOG_TAG, "getCategories took "
-                + (System.currentTimeMillis() - startTime) + " ms");
+
+        if (DEBUG_TIMING) {
+            Log.d(LOG_TAG, "getCategories took "
+                    + (System.currentTimeMillis() - startTime) + " ms");
+        }
         return categories;
     }
 
-    /**
-     * Create a new DashboardCategory from key.
-     *
-     * @param context Context to query intent
-     * @param categoryKey The category key
-     * @param categoryDefinedInManifest If true, an dummy activity must exists in manifest to
-     * represent this category (eg: .Settings$DeviceSettings)
-     */
-    private static DashboardCategory createCategory(Context context, String categoryKey,
-            boolean categoryDefinedInManifest) {
-        DashboardCategory category = new DashboardCategory();
-        category.key = categoryKey;
-        if (!categoryDefinedInManifest) {
-            return category;
-        }
-        PackageManager pm = context.getPackageManager();
-        List<ResolveInfo> results = pm.queryIntentActivities(new Intent(categoryKey), 0);
-        if (results.size() == 0) {
-            return null;
-        }
-        for (ResolveInfo resolved : results) {
-            if (!resolved.system) {
-                // Do not allow any app to add to settings, only system ones.
-                continue;
-            }
-            category.title = resolved.activityInfo.loadLabel(pm);
-            category.priority = SETTING_PKG.equals(
-                    resolved.activityInfo.applicationInfo.packageName) ? resolved.priority : 0;
-            if (DEBUG) Log.d(LOG_TAG, "Adding category " + category.title);
-        }
-
-        return category;
-    }
-
     private static void getTilesForAction(Context context,
             UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
-            String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings,
-            String settingPkg) {
+            String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings) {
         getTilesForAction(context, user, action, addedCache, defaultCategory, outTiles,
-                requireSettings, requireSettings, settingPkg);
+                requireSettings, requireSettings);
     }
 
     private static void getTilesForAction(Context context,
             UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
             String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings,
-            boolean usePriority, String settingPkg) {
+            boolean usePriority) {
         Intent intent = new Intent(action);
         if (requireSettings) {
-            intent.setPackage(settingPkg);
+            intent.setPackage(SETTING_PKG);
         }
         getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles,
-                usePriority, true, true);
+                usePriority);
     }
 
     public static void getTilesForIntent(
             Context context, UserHandle user, Intent intent,
             Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles,
-            boolean usePriority, boolean checkCategory, boolean forceTintExternalIcon) {
-        getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles,
-                usePriority, checkCategory, forceTintExternalIcon, false /* shouldUpdateTiles */);
-    }
-
-    public static void getTilesForIntent(
-            Context context, UserHandle user, Intent intent,
-            Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles,
-            boolean usePriority, boolean checkCategory, boolean forceTintExternalIcon,
-            boolean shouldUpdateTiles) {
+            boolean usePriority) {
         PackageManager pm = context.getPackageManager();
         List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
                 PackageManager.GET_META_DATA, user.getIdentifier());
-        Map<String, IContentProvider> providerMap = new HashMap<>();
         for (ResolveInfo resolved : results) {
             if (!resolved.system) {
                 // Do not allow any app to add to settings, only system ones.
@@ -349,7 +292,7 @@
             String categoryKey = defaultCategory;
 
             // Load category
-            if (checkCategory && ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY))
+            if ((metaData == null || !metaData.containsKey(EXTRA_CATEGORY_KEY))
                     && categoryKey == null) {
                 Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent "
                         + intent + " missing metadata "
@@ -359,22 +302,13 @@
                 categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
             }
 
-            Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
-                    activityInfo.name);
+            Pair<String, String> key = new Pair<>(activityInfo.packageName, activityInfo.name);
             Tile tile = addedCache.get(key);
             if (tile == null) {
-                tile = new Tile();
-                tile.intent = new Intent().setClassName(
-                        activityInfo.packageName, activityInfo.name);
-                tile.category = categoryKey;
-                tile.priority = usePriority ? resolved.priority : 0;
-                tile.metaData = activityInfo.metaData;
-                updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,
-                        pm, providerMap, forceTintExternalIcon);
+                tile = new Tile(activityInfo, categoryKey);
+                updateTileData(context, tile, activityInfo, activityInfo.applicationInfo, pm);
                 if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);
                 addedCache.put(key, tile);
-            } else if (shouldUpdateTiles) {
-                updateSummaryAndTitle(context, providerMap, tile);
             }
 
             if (!tile.userHandle.contains(user)) {
@@ -387,40 +321,17 @@
     }
 
     private static boolean updateTileData(Context context, Tile tile,
-            ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm,
-            Map<String, IContentProvider> providerMap, boolean forceTintExternalIcon) {
+            ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm) {
         if (applicationInfo.isSystemApp()) {
-            boolean forceTintIcon = false;
-            int icon = 0;
-            Pair<String, Integer> iconFromUri = null;
             CharSequence title = null;
             String summary = null;
-            String keyHint = null;
-            boolean isIconTintable = false;
 
             // Get the activity's meta-data
             try {
                 Resources res = pm.getResourcesForApplication(applicationInfo.packageName);
                 Bundle metaData = activityInfo.metaData;
 
-                if (forceTintExternalIcon
-                        && !context.getPackageName().equals(applicationInfo.packageName)) {
-                    isIconTintable = true;
-                    forceTintIcon = true;
-                }
-
                 if (res != null && metaData != null) {
-                    if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
-                        icon = metaData.getInt(META_DATA_PREFERENCE_ICON);
-                    }
-                    if (metaData.containsKey(META_DATA_PREFERENCE_ICON_TINTABLE)) {
-                        if (forceTintIcon) {
-                            Log.w(LOG_TAG, "Ignoring icon tintable for " + activityInfo);
-                        } else {
-                            isIconTintable =
-                                    metaData.getBoolean(META_DATA_PREFERENCE_ICON_TINTABLE);
-                        }
-                    }
                     if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
                         if (metaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) {
                             title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
@@ -435,18 +346,6 @@
                             summary = metaData.getString(META_DATA_PREFERENCE_SUMMARY);
                         }
                     }
-                    if (metaData.containsKey(META_DATA_PREFERENCE_KEYHINT)) {
-                        if (metaData.get(META_DATA_PREFERENCE_KEYHINT) instanceof Integer) {
-                            keyHint = res.getString(metaData.getInt(META_DATA_PREFERENCE_KEYHINT));
-                        } else {
-                            keyHint = metaData.getString(META_DATA_PREFERENCE_KEYHINT);
-                        }
-                    }
-                    if (metaData.containsKey(META_DATA_PREFERENCE_CUSTOM_VIEW)) {
-                        int layoutId = metaData.getInt(META_DATA_PREFERENCE_CUSTOM_VIEW);
-                        tile.remoteViews = new RemoteViews(applicationInfo.packageName, layoutId);
-                        updateSummaryAndTitle(context, providerMap, tile);
-                    }
                 }
             } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
                 if (DEBUG) Log.d(LOG_TAG, "Couldn't find info", e);
@@ -458,59 +357,21 @@
                 title = activityInfo.loadLabel(pm).toString();
             }
 
-            // Set the icon
-            if (icon == 0) {
-                // Only fallback to activityinfo.icon if metadata does not contain ICON_URI.
-                // ICON_URI should be loaded in app UI when need the icon object.
-                if (!tile.metaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
-                    icon = activityInfo.icon;
-                }
-            }
-            if (icon != 0) {
-                tile.icon = Icon.createWithResource(activityInfo.packageName, icon);
-            }
-
             // Set title and summary for the preference
             tile.title = title;
             tile.summary = summary;
-            // Replace the intent with this specific activity
-            tile.intent = new Intent().setClassName(activityInfo.packageName,
-                    activityInfo.name);
-            // Suggest a key for this tile
-            tile.key = keyHint;
-            tile.isIconTintable = isIconTintable;
-
             return true;
         }
 
         return false;
     }
 
-    private static void updateSummaryAndTitle(
-            Context context, Map<String, IContentProvider> providerMap, Tile tile) {
-        if (tile == null || tile.metaData == null
-                || !tile.metaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) {
-            return;
-        }
-
-        String uriString = tile.metaData.getString(META_DATA_PREFERENCE_SUMMARY_URI);
-        Bundle bundle = getBundleFromUri(context, uriString, providerMap);
-        String overrideSummary = getString(bundle, META_DATA_PREFERENCE_SUMMARY);
-        String overrideTitle = getString(bundle, META_DATA_PREFERENCE_TITLE);
-        if (overrideSummary != null) {
-            tile.remoteViews.setTextViewText(android.R.id.summary, overrideSummary);
-        }
-
-        if (overrideTitle != null) {
-            tile.remoteViews.setTextViewText(android.R.id.title, overrideTitle);
-        }
-    }
-
     /**
      * Gets the icon package name and resource id from content provider.
-     * @param context context
+     *
+     * @param context     context
      * @param packageName package name of the target activity
-     * @param uriString URI for the content provider
+     * @param uriString   URI for the content provider
      * @param providerMap Maps URI authorities to providers
      * @return package name and resource id of the icon specified
      */
@@ -538,10 +399,11 @@
 
     /**
      * Gets text associated with the input key from the content provider.
-     * @param context context
-     * @param uriString URI for the content provider
+     *
+     * @param context     context
+     * @param uriString   URI for the content provider
      * @param providerMap Maps URI authorities to providers
-     * @param key Key mapping to the text in bundle returned by the content provider
+     * @param key         Key mapping to the text in bundle returned by the content provider
      * @return Text associated with the key, if returned by the content provider
      */
     public static String getTextFromUri(Context context, String uriString,
@@ -571,10 +433,6 @@
         }
     }
 
-    private static String getString(Bundle bundle, String key) {
-        return bundle == null ? null : bundle.getString(key);
-    }
-
     private static IContentProvider getProviderFromUri(Context context, Uri uri,
             Map<String, IContentProvider> providerMap) {
         if (uri == null) {
@@ -601,12 +459,4 @@
         }
         return pathSegments.get(0);
     }
-
-    private static final Comparator<DashboardCategory> CATEGORY_COMPARATOR =
-            new Comparator<DashboardCategory>() {
-        @Override
-        public int compare(DashboardCategory lhs, DashboardCategory rhs) {
-            return rhs.priority - lhs.priority;
-        }
-    };
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
deleted file mode 100644
index 8a09df2..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
+++ /dev/null
@@ -1,213 +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.
- */
-
-package com.android.settingslib.drawer;
-
-import android.app.ActivityManager;
-
-import android.content.Context;
-import android.content.pm.UserInfo;
-import android.database.DataSetObserver;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.SpinnerAdapter;
-import android.widget.TextView;
-import com.android.internal.util.UserIcons;
-import com.android.settingslib.drawable.UserIconDrawable;
-
-import com.android.settingslib.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Adapter for a spinner that shows a list of users.
- */
-public class UserAdapter implements SpinnerAdapter, ListAdapter {
-    /** Holder for user details */
-    public static class UserDetails {
-        private final UserHandle mUserHandle;
-        private final String mName;
-        private final Drawable mIcon;
-
-        public UserDetails(UserHandle userHandle, UserManager um, Context context) {
-            mUserHandle = userHandle;
-            UserInfo userInfo = um.getUserInfo(mUserHandle.getIdentifier());
-            Drawable icon;
-            if (userInfo.isManagedProfile()) {
-                mName = context.getString(R.string.managed_user_title);
-                icon = context.getDrawable(
-                    com.android.internal.R.drawable.ic_corp_badge);
-            } else {
-                mName = userInfo.name;
-                final int userId = userInfo.id;
-                if (um.getUserIcon(userId) != null) {
-                    icon = new BitmapDrawable(context.getResources(), um.getUserIcon(userId));
-                } else {
-                    icon = UserIcons.getDefaultUserIcon(
-                            context.getResources(), userId, /* light= */ false);
-                }
-            }
-            this.mIcon = encircle(context, icon);
-        }
-
-        private static Drawable encircle(Context context, Drawable icon) {
-            return new UserIconDrawable(UserIconDrawable.getSizeForList(context))
-                    .setIconDrawable(icon).bake();
-        }
-    }
-    private ArrayList<UserDetails> data;
-    private final LayoutInflater mInflater;
-
-    public UserAdapter(Context context, ArrayList<UserDetails> users) {
-        if (users == null) {
-            throw new IllegalArgumentException("A list of user details must be provided");
-        }
-        this.data = users;
-        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-    }
-
-    public UserHandle getUserHandle(int position) {
-        if (position < 0 || position >= data.size()) {
-            return null;
-        }
-        return data.get(position).mUserHandle;
-    }
-
-    @Override
-    public View getDropDownView(int position, View convertView, ViewGroup parent) {
-        final View row = convertView != null ? convertView : createUser(parent);
-
-        UserDetails user = data.get(position);
-        ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(user.mIcon);
-        ((TextView) row.findViewById(android.R.id.title)).setText(getTitle(user));
-        return row;
-    }
-
-    private int getTitle(UserDetails user) {
-        int userHandle = user.mUserHandle.getIdentifier();
-        if (userHandle == UserHandle.USER_CURRENT
-                || userHandle == ActivityManager.getCurrentUser()) {
-            return R.string.category_personal;
-        } else {
-            return R.string.category_work;
-        }
-    }
-
-    private View createUser(ViewGroup parent) {
-        return mInflater.inflate(R.layout.user_preference, parent, false);
-    }
-
-    @Override
-    public void registerDataSetObserver(DataSetObserver observer) {
-        // We don't support observers
-    }
-
-    @Override
-    public void unregisterDataSetObserver(DataSetObserver observer) {
-        // We don't support observers
-    }
-
-    @Override
-    public int getCount() {
-        return data.size();
-    }
-
-    @Override
-    public UserDetails getItem(int position) {
-        return data.get(position);
-    }
-
-    @Override
-    public long getItemId(int position) {
-        return data.get(position).mUserHandle.getIdentifier();
-    }
-
-    @Override
-    public boolean hasStableIds() {
-        return false;
-    }
-
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        return getDropDownView(position, convertView, parent);
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        return 0;
-    }
-
-    @Override
-    public int getViewTypeCount() {
-        return 1;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return data.isEmpty();
-    }
-
-    @Override
-    public boolean areAllItemsEnabled() {
-        return true;
-    }
-
-    @Override
-    public boolean isEnabled(int position) {
-        return true;
-    }
-
-    /**
-     * Creates a {@link UserAdapter} if there is more than one profile on the device.
-     *
-     * <p> The adapter can be used to populate a spinner that switches between the Settings
-     * app on the different profiles.
-     *
-     * @return a {@link UserAdapter} or null if there is only one profile.
-     */
-    public static UserAdapter createUserSpinnerAdapter(UserManager userManager,
-            Context context) {
-        List<UserHandle> userProfiles = userManager.getUserProfiles();
-        if (userProfiles.size() < 2) {
-            return null;
-        }
-
-        UserHandle myUserHandle = new UserHandle(UserHandle.myUserId());
-        // The first option should be the current profile
-        userProfiles.remove(myUserHandle);
-        userProfiles.add(0, myUserHandle);
-
-        return createUserAdapter(userManager, context, userProfiles);
-    }
-
-    public static UserAdapter createUserAdapter(UserManager userManager,
-            Context context, List<UserHandle> userProfiles) {
-        ArrayList<UserDetails> userDetails = new ArrayList<UserDetails>(userProfiles.size());
-        final int count = userProfiles.size();
-        for (int i = 0; i < count; i++) {
-            userDetails.add(new UserDetails(userProfiles.get(i), userManager, context));
-        }
-        return new UserAdapter(context, userDetails);
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index e5d97c9..3c0f6fe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -256,11 +256,12 @@
         }
     }
 
-    public void launchSettings(DreamInfo dreamInfo) {
+    public void launchSettings(Context uiContext, DreamInfo dreamInfo) {
         logd("launchSettings(%s)", dreamInfo);
-        if (dreamInfo == null || dreamInfo.settingsComponentName == null)
+        if (dreamInfo == null || dreamInfo.settingsComponentName == null) {
             return;
-        mContext.startActivity(new Intent().setComponent(dreamInfo.settingsComponentName));
+        }
+        uiContext.startActivity(new Intent().setComponent(dreamInfo.settingsComponentName));
     }
 
     public void preview(DreamInfo dreamInfo) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
index 8a1c4ef..94efc71 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.fuelgauge;
 
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -90,6 +91,13 @@
         if (TextUtils.equals(pkg, defaultDialer)) {
             return true;
         }
+
+        final DevicePolicyManager devicePolicyManager = mAppContext.getSystemService(
+                DevicePolicyManager.class);
+        if (devicePolicyManager.packageHasActiveAdmins(pkg)) {
+            return true;
+        }
+
         return false;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
index e609a40..17c2b02 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
@@ -210,6 +210,10 @@
         postInvalidate();
     }
 
+    public boolean getPowerSave() {
+        return mPowerSaveEnabled;
+    }
+
     protected void setPowerSaveAsColorError(boolean asError) {
         mPowerSaveAsColorError = asError;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeEnablerManagerCompat.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeEnablerManagerCompat.java
new file mode 100644
index 0000000..ad1368c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeEnablerManagerCompat.java
@@ -0,0 +1,265 @@
+/*
+ * 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.inputmethod;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.text.TextUtils;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.settingslib.R;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.TwoStatePreference;
+
+public class InputMethodAndSubtypeEnablerManagerCompat implements
+        Preference.OnPreferenceChangeListener {
+
+    private final PreferenceFragmentCompat mFragment;
+
+    private boolean mHaveHardKeyboard;
+    private final HashMap<String, List<Preference>> mInputMethodAndSubtypePrefsMap =
+            new HashMap<>();
+    private final HashMap<String, TwoStatePreference> mAutoSelectionPrefsMap = new HashMap<>();
+    private InputMethodManager mImm;
+    // TODO: Change mInputMethodInfoList to Map
+    private List<InputMethodInfo> mInputMethodInfoList;
+    private final Collator mCollator = Collator.getInstance();
+
+    public InputMethodAndSubtypeEnablerManagerCompat(PreferenceFragmentCompat fragment) {
+        mFragment = fragment;
+        mImm = fragment.getContext().getSystemService(InputMethodManager.class);
+
+        mInputMethodInfoList = mImm.getInputMethodList();
+    }
+
+    public void init(PreferenceFragmentCompat fragment, String targetImi, PreferenceScreen root) {
+        final Configuration config = fragment.getResources().getConfiguration();
+        mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY);
+
+        for (final InputMethodInfo imi : mInputMethodInfoList) {
+            // Add subtype preferences of this IME when it is specified or no IME is specified.
+            if (imi.getId().equals(targetImi) || TextUtils.isEmpty(targetImi)) {
+                addInputMethodSubtypePreferences(fragment, imi, root);
+            }
+        }
+    }
+
+    public void refresh(Context context, PreferenceFragmentCompat fragment) {
+        // Refresh internal states in mInputMethodSettingValues to keep the latest
+        // "InputMethodInfo"s and "InputMethodSubtype"s
+        InputMethodSettingValuesWrapper
+                .getInstance(context).refreshAllInputMethodAndSubtypes();
+        InputMethodAndSubtypeUtilCompat.loadInputMethodSubtypeList(fragment,
+                context.getContentResolver(), mInputMethodInfoList, mInputMethodAndSubtypePrefsMap);
+        updateAutoSelectionPreferences();
+    }
+
+    public void save(Context context, PreferenceFragmentCompat fragment) {
+        InputMethodAndSubtypeUtilCompat.saveInputMethodSubtypeList(fragment,
+                context.getContentResolver(), mInputMethodInfoList, mHaveHardKeyboard);
+    }
+
+    @Override
+    public boolean onPreferenceChange(final Preference pref, final Object newValue) {
+        if (!(newValue instanceof Boolean)) {
+            return true; // Invoke default behavior.
+        }
+        final boolean isChecking = (Boolean) newValue;
+        for (final String imiId : mAutoSelectionPrefsMap.keySet()) {
+            // An auto select subtype preference is changing.
+            if (mAutoSelectionPrefsMap.get(imiId) == pref) {
+                final TwoStatePreference autoSelectionPref = (TwoStatePreference) pref;
+                autoSelectionPref.setChecked(isChecking);
+                // Enable or disable subtypes depending on the auto selection preference.
+                setAutoSelectionSubtypesEnabled(imiId, autoSelectionPref.isChecked());
+                return false;
+            }
+        }
+        // A subtype preference is changing.
+        if (pref instanceof InputMethodSubtypePreference) {
+            final InputMethodSubtypePreference subtypePref = (InputMethodSubtypePreference) pref;
+            subtypePref.setChecked(isChecking);
+            if (!subtypePref.isChecked()) {
+                // It takes care of the case where no subtypes are explicitly enabled then the auto
+                // selection preference is going to be checked.
+                updateAutoSelectionPreferences();
+            }
+            return false;
+        }
+        return true; // Invoke default behavior.
+    }
+
+    private void addInputMethodSubtypePreferences(PreferenceFragmentCompat fragment,
+            InputMethodInfo imi, final PreferenceScreen root) {
+        Context prefContext = fragment.getPreferenceManager().getContext();
+
+        final int subtypeCount = imi.getSubtypeCount();
+        if (subtypeCount <= 1) {
+            return;
+        }
+        final String imiId = imi.getId();
+        final PreferenceCategory keyboardSettingsCategory =
+                new PreferenceCategory(prefContext);
+        root.addPreference(keyboardSettingsCategory);
+        final PackageManager pm = prefContext.getPackageManager();
+        final CharSequence label = imi.loadLabel(pm);
+
+        keyboardSettingsCategory.setTitle(label);
+        keyboardSettingsCategory.setKey(imiId);
+        // TODO: Use toggle Preference if images are ready.
+        final TwoStatePreference autoSelectionPref =
+                new SwitchWithNoTextPreference(prefContext);
+        mAutoSelectionPrefsMap.put(imiId, autoSelectionPref);
+        keyboardSettingsCategory.addPreference(autoSelectionPref);
+        autoSelectionPref.setOnPreferenceChangeListener(this);
+
+        final PreferenceCategory activeInputMethodsCategory =
+                new PreferenceCategory(prefContext);
+        activeInputMethodsCategory.setTitle(R.string.active_input_method_subtypes);
+        root.addPreference(activeInputMethodsCategory);
+
+        CharSequence autoSubtypeLabel = null;
+        final ArrayList<Preference> subtypePreferences = new ArrayList<>();
+        for (int index = 0; index < subtypeCount; ++index) {
+            final InputMethodSubtype subtype = imi.getSubtypeAt(index);
+            if (subtype.overridesImplicitlyEnabledSubtype()) {
+                if (autoSubtypeLabel == null) {
+                    autoSubtypeLabel = InputMethodAndSubtypeUtil.getSubtypeLocaleNameAsSentence(
+                            subtype, prefContext, imi);
+                }
+            } else {
+                final Preference subtypePref = new InputMethodSubtypePreference(
+                        prefContext, subtype, imi);
+                subtypePreferences.add(subtypePref);
+            }
+        }
+        subtypePreferences.sort((lhs, rhs) -> {
+            if (lhs instanceof InputMethodSubtypePreference) {
+                return ((InputMethodSubtypePreference) lhs).compareTo(rhs, mCollator);
+            }
+            return lhs.compareTo(rhs);
+        });
+        for (final Preference pref : subtypePreferences) {
+            activeInputMethodsCategory.addPreference(pref);
+            pref.setOnPreferenceChangeListener(this);
+            InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref);
+        }
+        mInputMethodAndSubtypePrefsMap.put(imiId, subtypePreferences);
+        if (TextUtils.isEmpty(autoSubtypeLabel)) {
+            autoSelectionPref.setTitle(
+                    R.string.use_system_language_to_select_input_method_subtypes);
+        } else {
+            autoSelectionPref.setTitle(autoSubtypeLabel);
+        }
+    }
+
+    private boolean isNoSubtypesExplicitlySelected(final String imiId) {
+        final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
+        for (final Preference pref : subtypePrefs) {
+            if (pref instanceof TwoStatePreference && ((TwoStatePreference) pref).isChecked()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void setAutoSelectionSubtypesEnabled(final String imiId,
+            final boolean autoSelectionEnabled) {
+        final TwoStatePreference autoSelectionPref = mAutoSelectionPrefsMap.get(imiId);
+        if (autoSelectionPref == null) {
+            return;
+        }
+        autoSelectionPref.setChecked(autoSelectionEnabled);
+        final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
+        for (final Preference pref : subtypePrefs) {
+            if (pref instanceof TwoStatePreference) {
+                // When autoSelectionEnabled is true, all subtype prefs need to be disabled with
+                // implicitly checked subtypes. In case of false, all subtype prefs need to be
+                // enabled.
+                pref.setEnabled(!autoSelectionEnabled);
+                if (autoSelectionEnabled) {
+                    ((TwoStatePreference) pref).setChecked(false);
+                }
+            }
+        }
+        if (autoSelectionEnabled) {
+            InputMethodAndSubtypeUtilCompat.saveInputMethodSubtypeList(
+                    mFragment, mFragment.getContext().getContentResolver(),
+                    mInputMethodInfoList, mHaveHardKeyboard);
+            updateImplicitlyEnabledSubtypes(imiId);
+        }
+    }
+
+    private void updateImplicitlyEnabledSubtypes(final String targetImiId) {
+        // When targetImiId is null, apply to all subtypes of all IMEs
+        for (final InputMethodInfo imi : mInputMethodInfoList) {
+            final String imiId = imi.getId();
+            final TwoStatePreference autoSelectionPref = mAutoSelectionPrefsMap.get(imiId);
+            // No need to update implicitly enabled subtypes when the user has unchecked the
+            // "subtype auto selection".
+            if (autoSelectionPref == null || !autoSelectionPref.isChecked()) {
+                continue;
+            }
+            if (imiId.equals(targetImiId) || targetImiId == null) {
+                updateImplicitlyEnabledSubtypesOf(imi);
+            }
+        }
+    }
+
+    private void updateImplicitlyEnabledSubtypesOf(final InputMethodInfo imi) {
+        final String imiId = imi.getId();
+        final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
+        final List<InputMethodSubtype> implicitlyEnabledSubtypes =
+                mImm.getEnabledInputMethodSubtypeList(imi, true);
+        if (subtypePrefs == null || implicitlyEnabledSubtypes == null) {
+            return;
+        }
+        for (final Preference pref : subtypePrefs) {
+            if (!(pref instanceof TwoStatePreference)) {
+                continue;
+            }
+            final TwoStatePreference subtypePref = (TwoStatePreference) pref;
+            subtypePref.setChecked(false);
+            for (final InputMethodSubtype subtype : implicitlyEnabledSubtypes) {
+                final String implicitlyEnabledSubtypePrefKey = imiId + subtype.hashCode();
+                if (subtypePref.getKey().equals(implicitlyEnabledSubtypePrefKey)) {
+                    subtypePref.setChecked(true);
+                    break;
+                }
+            }
+        }
+    }
+
+    private void updateAutoSelectionPreferences() {
+        for (final String imiId : mInputMethodAndSubtypePrefsMap.keySet()) {
+            setAutoSelectionSubtypesEnabled(imiId, isNoSubtypesExplicitlySelected(imiId));
+        }
+        updateImplicitlyEnabledSubtypes(null /* targetImiId */  /* check */);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java
new file mode 100644
index 0000000..9ad2adb
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java
@@ -0,0 +1,436 @@
+/*
+ * 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.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.icu.text.ListFormatter;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.app.LocaleHelper;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.TwoStatePreference;
+
+// TODO: Consolidate this with {@link InputMethodSettingValuesWrapper}.
+public class InputMethodAndSubtypeUtilCompat {
+
+    private static final boolean DEBUG = false;
+    private static final String TAG = "InputMethdAndSubtypeUtlCompat";
+
+    private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
+    private static final char INPUT_METHOD_SEPARATER = ':';
+    private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';';
+    private static final int NOT_A_SUBTYPE_ID = -1;
+
+    private static final TextUtils.SimpleStringSplitter sStringInputMethodSplitter
+            = new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATER);
+
+    private static final TextUtils.SimpleStringSplitter sStringInputMethodSubtypeSplitter
+            = new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATER);
+
+    // InputMethods and subtypes are saved in the settings as follows:
+    // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
+    public static String buildInputMethodsAndSubtypesString(
+            final HashMap<String, HashSet<String>> imeToSubtypesMap) {
+        final StringBuilder builder = new StringBuilder();
+        for (final String imi : imeToSubtypesMap.keySet()) {
+            if (builder.length() > 0) {
+                builder.append(INPUT_METHOD_SEPARATER);
+            }
+            final HashSet<String> subtypeIdSet = imeToSubtypesMap.get(imi);
+            builder.append(imi);
+            for (final String subtypeId : subtypeIdSet) {
+                builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId);
+            }
+        }
+        return builder.toString();
+    }
+
+    private static String buildInputMethodsString(final HashSet<String> imiList) {
+        final StringBuilder builder = new StringBuilder();
+        for (final String imi : imiList) {
+            if (builder.length() > 0) {
+                builder.append(INPUT_METHOD_SEPARATER);
+            }
+            builder.append(imi);
+        }
+        return builder.toString();
+    }
+
+    private static int getInputMethodSubtypeSelected(ContentResolver resolver) {
+        try {
+            return Settings.Secure.getInt(resolver,
+                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE);
+        } catch (SettingNotFoundException e) {
+            return NOT_A_SUBTYPE_ID;
+        }
+    }
+
+    private static boolean isInputMethodSubtypeSelected(ContentResolver resolver) {
+        return getInputMethodSubtypeSelected(resolver) != NOT_A_SUBTYPE_ID;
+    }
+
+    private static void putSelectedInputMethodSubtype(ContentResolver resolver, int hashCode) {
+        Settings.Secure.putInt(resolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, hashCode);
+    }
+
+    // Needs to modify InputMethodManageService if you want to change the format of saved string.
+    static HashMap<String, HashSet<String>> getEnabledInputMethodsAndSubtypeList(
+            ContentResolver resolver) {
+        final String enabledInputMethodsStr = Settings.Secure.getString(
+                resolver, Settings.Secure.ENABLED_INPUT_METHODS);
+        if (DEBUG) {
+            Log.d(TAG, "--- Load enabled input methods: " + enabledInputMethodsStr);
+        }
+        return parseInputMethodsAndSubtypesString(enabledInputMethodsStr);
+    }
+
+    public static HashMap<String, HashSet<String>> parseInputMethodsAndSubtypesString(
+            final String inputMethodsAndSubtypesString) {
+        final HashMap<String, HashSet<String>> subtypesMap = new HashMap<>();
+        if (TextUtils.isEmpty(inputMethodsAndSubtypesString)) {
+            return subtypesMap;
+        }
+        sStringInputMethodSplitter.setString(inputMethodsAndSubtypesString);
+        while (sStringInputMethodSplitter.hasNext()) {
+            final String nextImsStr = sStringInputMethodSplitter.next();
+            sStringInputMethodSubtypeSplitter.setString(nextImsStr);
+            if (sStringInputMethodSubtypeSplitter.hasNext()) {
+                final HashSet<String> subtypeIdSet = new HashSet<>();
+                // The first element is {@link InputMethodInfoId}.
+                final String imiId = sStringInputMethodSubtypeSplitter.next();
+                while (sStringInputMethodSubtypeSplitter.hasNext()) {
+                    subtypeIdSet.add(sStringInputMethodSubtypeSplitter.next());
+                }
+                subtypesMap.put(imiId, subtypeIdSet);
+            }
+        }
+        return subtypesMap;
+    }
+
+    private static HashSet<String> getDisabledSystemIMEs(ContentResolver resolver) {
+        HashSet<String> set = new HashSet<>();
+        String disabledIMEsStr = Settings.Secure.getString(
+                resolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS);
+        if (TextUtils.isEmpty(disabledIMEsStr)) {
+            return set;
+        }
+        sStringInputMethodSplitter.setString(disabledIMEsStr);
+        while(sStringInputMethodSplitter.hasNext()) {
+            set.add(sStringInputMethodSplitter.next());
+        }
+        return set;
+    }
+
+    public static void saveInputMethodSubtypeList(PreferenceFragmentCompat context,
+            ContentResolver resolver, List<InputMethodInfo> inputMethodInfos,
+            boolean hasHardKeyboard) {
+        String currentInputMethodId = Settings.Secure.getString(resolver,
+                Settings.Secure.DEFAULT_INPUT_METHOD);
+        final int selectedInputMethodSubtype = getInputMethodSubtypeSelected(resolver);
+        final HashMap<String, HashSet<String>> enabledIMEsAndSubtypesMap =
+                getEnabledInputMethodsAndSubtypeList(resolver);
+        final HashSet<String> disabledSystemIMEs = getDisabledSystemIMEs(resolver);
+
+        boolean needsToResetSelectedSubtype = false;
+        for (final InputMethodInfo imi : inputMethodInfos) {
+            final String imiId = imi.getId();
+            final Preference pref = context.findPreference(imiId);
+            if (pref == null) {
+                continue;
+            }
+            // In the choose input method screen or in the subtype enabler screen,
+            // <code>pref</code> is an instance of TwoStatePreference.
+            final boolean isImeChecked = (pref instanceof TwoStatePreference) ?
+                    ((TwoStatePreference) pref).isChecked()
+                    : enabledIMEsAndSubtypesMap.containsKey(imiId);
+            final boolean isCurrentInputMethod = imiId.equals(currentInputMethodId);
+            final boolean systemIme = imi.isSystem();
+            if ((!hasHardKeyboard && InputMethodSettingValuesWrapper.getInstance(
+                    context.getActivity()).isAlwaysCheckedIme(imi))
+                    || isImeChecked) {
+                if (!enabledIMEsAndSubtypesMap.containsKey(imiId)) {
+                    // imiId has just been enabled
+                    enabledIMEsAndSubtypesMap.put(imiId, new HashSet<>());
+                }
+                final HashSet<String> subtypesSet = enabledIMEsAndSubtypesMap.get(imiId);
+
+                boolean subtypePrefFound = false;
+                final int subtypeCount = imi.getSubtypeCount();
+                for (int i = 0; i < subtypeCount; ++i) {
+                    final InputMethodSubtype subtype = imi.getSubtypeAt(i);
+                    final String subtypeHashCodeStr = String.valueOf(subtype.hashCode());
+                    final TwoStatePreference subtypePref = (TwoStatePreference) context
+                            .findPreference(imiId + subtypeHashCodeStr);
+                    // In the Configure input method screen which does not have subtype preferences.
+                    if (subtypePref == null) {
+                        continue;
+                    }
+                    if (!subtypePrefFound) {
+                        // Once subtype preference is found, subtypeSet needs to be cleared.
+                        // Because of system change, hashCode value could have been changed.
+                        subtypesSet.clear();
+                        // If selected subtype preference is disabled, needs to reset.
+                        needsToResetSelectedSubtype = true;
+                        subtypePrefFound = true;
+                    }
+                    // Checking <code>subtypePref.isEnabled()</code> is insufficient to determine
+                    // whether the user manually enabled this subtype or not.  Implicitly-enabled
+                    // subtypes are also checked just as an indicator to users.  We also need to
+                    // check <code>subtypePref.isEnabled()</code> so that only manually enabled
+                    // subtypes can be saved here.
+                    if (subtypePref.isEnabled() && subtypePref.isChecked()) {
+                        subtypesSet.add(subtypeHashCodeStr);
+                        if (isCurrentInputMethod) {
+                            if (selectedInputMethodSubtype == subtype.hashCode()) {
+                                // Selected subtype is still enabled, there is no need to reset
+                                // selected subtype.
+                                needsToResetSelectedSubtype = false;
+                            }
+                        }
+                    } else {
+                        subtypesSet.remove(subtypeHashCodeStr);
+                    }
+                }
+            } else {
+                enabledIMEsAndSubtypesMap.remove(imiId);
+                if (isCurrentInputMethod) {
+                    // We are processing the current input method, but found that it's not enabled.
+                    // This means that the current input method has been uninstalled.
+                    // If currentInputMethod is already uninstalled, InputMethodManagerService will
+                    // find the applicable IME from the history and the system locale.
+                    if (DEBUG) {
+                        Log.d(TAG, "Current IME was uninstalled or disabled.");
+                    }
+                    currentInputMethodId = null;
+                }
+            }
+            // If it's a disabled system ime, add it to the disabled list so that it
+            // doesn't get enabled automatically on any changes to the package list
+            if (systemIme && hasHardKeyboard) {
+                if (disabledSystemIMEs.contains(imiId)) {
+                    if (isImeChecked) {
+                        disabledSystemIMEs.remove(imiId);
+                    }
+                } else {
+                    if (!isImeChecked) {
+                        disabledSystemIMEs.add(imiId);
+                    }
+                }
+            }
+        }
+
+        final String enabledIMEsAndSubtypesString = buildInputMethodsAndSubtypesString(
+                enabledIMEsAndSubtypesMap);
+        final String disabledSystemIMEsString = buildInputMethodsString(disabledSystemIMEs);
+        if (DEBUG) {
+            Log.d(TAG, "--- Save enabled inputmethod settings. :" + enabledIMEsAndSubtypesString);
+            Log.d(TAG, "--- Save disabled system inputmethod settings. :"
+                    + disabledSystemIMEsString);
+            Log.d(TAG, "--- Save default inputmethod settings. :" + currentInputMethodId);
+            Log.d(TAG, "--- Needs to reset the selected subtype :" + needsToResetSelectedSubtype);
+            Log.d(TAG, "--- Subtype is selected :" + isInputMethodSubtypeSelected(resolver));
+        }
+
+        // Redefines SelectedSubtype when all subtypes are unchecked or there is no subtype
+        // selected. And if the selected subtype of the current input method was disabled,
+        // We should reset the selected input method's subtype.
+        if (needsToResetSelectedSubtype || !isInputMethodSubtypeSelected(resolver)) {
+            if (DEBUG) {
+                Log.d(TAG, "--- Reset inputmethod subtype because it's not defined.");
+            }
+            putSelectedInputMethodSubtype(resolver, NOT_A_SUBTYPE_ID);
+        }
+
+        Settings.Secure.putString(resolver,
+                Settings.Secure.ENABLED_INPUT_METHODS, enabledIMEsAndSubtypesString);
+        if (disabledSystemIMEsString.length() > 0) {
+            Settings.Secure.putString(resolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
+                    disabledSystemIMEsString);
+        }
+        // If the current input method is unset, InputMethodManagerService will find the applicable
+        // IME from the history and the system locale.
+        Settings.Secure.putString(resolver, Settings.Secure.DEFAULT_INPUT_METHOD,
+                currentInputMethodId != null ? currentInputMethodId : "");
+    }
+
+    public static void loadInputMethodSubtypeList(final PreferenceFragmentCompat context,
+            final ContentResolver resolver, final List<InputMethodInfo> inputMethodInfos,
+            final Map<String, List<Preference>> inputMethodPrefsMap) {
+        final HashMap<String, HashSet<String>> enabledSubtypes =
+                getEnabledInputMethodsAndSubtypeList(resolver);
+
+        for (final InputMethodInfo imi : inputMethodInfos) {
+            final String imiId = imi.getId();
+            final Preference pref = context.findPreference(imiId);
+            if (pref instanceof TwoStatePreference) {
+                final TwoStatePreference subtypePref = (TwoStatePreference) pref;
+                final boolean isEnabled = enabledSubtypes.containsKey(imiId);
+                subtypePref.setChecked(isEnabled);
+                if (inputMethodPrefsMap != null) {
+                    for (final Preference childPref: inputMethodPrefsMap.get(imiId)) {
+                        childPref.setEnabled(isEnabled);
+                    }
+                }
+                setSubtypesPreferenceEnabled(context, inputMethodInfos, imiId, isEnabled);
+            }
+        }
+        updateSubtypesPreferenceChecked(context, inputMethodInfos, enabledSubtypes);
+    }
+
+    private static void setSubtypesPreferenceEnabled(final PreferenceFragmentCompat context,
+            final List<InputMethodInfo> inputMethodProperties, final String id,
+            final boolean enabled) {
+        final PreferenceScreen preferenceScreen = context.getPreferenceScreen();
+        for (final InputMethodInfo imi : inputMethodProperties) {
+            if (id.equals(imi.getId())) {
+                final int subtypeCount = imi.getSubtypeCount();
+                for (int i = 0; i < subtypeCount; ++i) {
+                    final InputMethodSubtype subtype = imi.getSubtypeAt(i);
+                    final TwoStatePreference pref = (TwoStatePreference) preferenceScreen
+                            .findPreference(id + subtype.hashCode());
+                    if (pref != null) {
+                        pref.setEnabled(enabled);
+                    }
+                }
+            }
+        }
+    }
+
+    private static void updateSubtypesPreferenceChecked(final PreferenceFragmentCompat context,
+            final List<InputMethodInfo> inputMethodProperties,
+            final HashMap<String, HashSet<String>> enabledSubtypes) {
+        final PreferenceScreen preferenceScreen = context.getPreferenceScreen();
+        for (final InputMethodInfo imi : inputMethodProperties) {
+            final String id = imi.getId();
+            if (!enabledSubtypes.containsKey(id)) {
+                // There is no need to enable/disable subtypes of disabled IMEs.
+                continue;
+            }
+            final HashSet<String> enabledSubtypesSet = enabledSubtypes.get(id);
+            final int subtypeCount = imi.getSubtypeCount();
+            for (int i = 0; i < subtypeCount; ++i) {
+                final InputMethodSubtype subtype = imi.getSubtypeAt(i);
+                final String hashCode = String.valueOf(subtype.hashCode());
+                if (DEBUG) {
+                    Log.d(TAG, "--- Set checked state: " + "id" + ", " + hashCode + ", "
+                            + enabledSubtypesSet.contains(hashCode));
+                }
+                final TwoStatePreference pref = (TwoStatePreference) preferenceScreen
+                        .findPreference(id + hashCode);
+                if (pref != null) {
+                    pref.setChecked(enabledSubtypesSet.contains(hashCode));
+                }
+            }
+        }
+    }
+
+    public static void removeUnnecessaryNonPersistentPreference(final Preference pref) {
+        final String key = pref.getKey();
+        if (pref.isPersistent() || key == null) {
+            return;
+        }
+        final SharedPreferences prefs = pref.getSharedPreferences();
+        if (prefs != null && prefs.contains(key)) {
+            prefs.edit().remove(key).apply();
+        }
+    }
+
+    @NonNull
+    public static String getSubtypeLocaleNameAsSentence(@Nullable InputMethodSubtype subtype,
+            @NonNull final Context context, @NonNull final InputMethodInfo inputMethodInfo) {
+        if (subtype == null) {
+            return "";
+        }
+        final Locale locale = getDisplayLocale(context);
+        final CharSequence subtypeName = subtype.getDisplayName(context,
+                inputMethodInfo.getPackageName(), inputMethodInfo.getServiceInfo()
+                        .applicationInfo);
+        return LocaleHelper.toSentenceCase(subtypeName.toString(), locale);
+    }
+
+    @NonNull
+    public static String getSubtypeLocaleNameListAsSentence(
+            @NonNull final List<InputMethodSubtype> subtypes, @NonNull final Context context,
+            @NonNull final InputMethodInfo inputMethodInfo) {
+        if (subtypes.isEmpty()) {
+            return "";
+        }
+        final Locale locale = getDisplayLocale(context);
+        final int subtypeCount = subtypes.size();
+        final CharSequence[] subtypeNames = new CharSequence[subtypeCount];
+        for (int i = 0; i < subtypeCount; i++) {
+            subtypeNames[i] = subtypes.get(i).getDisplayName(context,
+                    inputMethodInfo.getPackageName(), inputMethodInfo.getServiceInfo()
+                            .applicationInfo);
+        }
+        return LocaleHelper.toSentenceCase(
+                ListFormatter.getInstance(locale).format((Object[]) subtypeNames), locale);
+    }
+
+    @NonNull
+    private static Locale getDisplayLocale(@Nullable final Context context) {
+        if (context == null) {
+            return Locale.getDefault();
+        }
+        if (context.getResources() == null) {
+            return Locale.getDefault();
+        }
+        final Configuration configuration = context.getResources().getConfiguration();
+        if (configuration == null) {
+            return Locale.getDefault();
+        }
+        final Locale configurationLocale = configuration.getLocales().get(0);
+        if (configurationLocale == null) {
+            return Locale.getDefault();
+        }
+        return configurationLocale;
+    }
+
+    public static boolean isValidSystemNonAuxAsciiCapableIme(InputMethodInfo imi) {
+        if (imi.isAuxiliaryIme() || !imi.isSystem()) {
+            return false;
+        }
+        final int subtypeCount = imi.getSubtypeCount();
+        for (int i = 0; i < subtypeCount; ++i) {
+            final InputMethodSubtype subtype = imi.getSubtypeAt(i);
+            if (SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())
+                    && subtype.isAsciiCapable()) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
index 92044c3..3930069 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
@@ -16,14 +16,17 @@
 
 package com.android.settingslib.license;
 
+import static com.android.settingslib.license.LicenseHtmlLoaderCompat.generateHtmlFile;
+import static com.android.settingslib.license.LicenseHtmlLoaderCompat.getCachedHtmlFile;
+import static com.android.settingslib.license.LicenseHtmlLoaderCompat.getVaildXmlFiles;
+import static com.android.settingslib.license.LicenseHtmlLoaderCompat.isCachedHtmlFileOutdated;
+
 import android.content.Context;
-import androidx.annotation.VisibleForTesting;
 import android.util.Log;
 
 import com.android.settingslib.utils.AsyncLoader;
 
 import java.io.File;
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -32,13 +35,6 @@
 public class LicenseHtmlLoader extends AsyncLoader<File> {
     private static final String TAG = "LicenseHtmlLoader";
 
-    private static final String[] DEFAULT_LICENSE_XML_PATHS = {
-            "/system/etc/NOTICE.xml.gz",
-            "/vendor/etc/NOTICE.xml.gz",
-            "/odm/etc/NOTICE.xml.gz",
-            "/oem/etc/NOTICE.xml.gz"};
-    private static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
-
     private Context mContext;
 
     public LicenseHtmlLoader(Context context) {
@@ -62,7 +58,7 @@
             return null;
         }
 
-        File cachedHtmlFile = getCachedHtmlFile();
+        File cachedHtmlFile = getCachedHtmlFile(mContext);
         if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile)
                 || generateHtmlFile(xmlFiles, cachedHtmlFile)) {
             return cachedHtmlFile;
@@ -71,40 +67,4 @@
         return null;
     }
 
-    @VisibleForTesting
-    List<File> getVaildXmlFiles() {
-        final List<File> xmlFiles = new ArrayList();
-        for (final String xmlPath : DEFAULT_LICENSE_XML_PATHS) {
-            File file = new File(xmlPath);
-            if (file.exists() && file.length() != 0) {
-                xmlFiles.add(file);
-            }
-        }
-        return xmlFiles;
-    }
-
-    @VisibleForTesting
-    File getCachedHtmlFile() {
-        return new File(mContext.getCacheDir(), NOTICE_HTML_FILE_NAME);
-    }
-
-    @VisibleForTesting
-    boolean isCachedHtmlFileOutdated(List<File> xmlFiles, File cachedHtmlFile) {
-        boolean outdated = true;
-        if (cachedHtmlFile.exists() && cachedHtmlFile.length() != 0) {
-            outdated = false;
-            for (File file : xmlFiles) {
-                if (cachedHtmlFile.lastModified() < file.lastModified()) {
-                    outdated = true;
-                    break;
-                }
-            }
-        }
-        return outdated;
-    }
-
-    @VisibleForTesting
-    boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
-        return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile);
-    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
new file mode 100644
index 0000000..d3b1903
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.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.settingslib.license;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.android.settingslib.utils.AsyncLoaderCompat;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * LicenseHtmlLoader is a loader which loads a license html file from default license xml files.
+ */
+public class LicenseHtmlLoaderCompat extends AsyncLoaderCompat<File> {
+    private static final String TAG = "LicenseHtmlLoaderCompat";
+
+    static final String[] DEFAULT_LICENSE_XML_PATHS = {
+            "/system/etc/NOTICE.xml.gz",
+            "/vendor/etc/NOTICE.xml.gz",
+            "/odm/etc/NOTICE.xml.gz",
+            "/oem/etc/NOTICE.xml.gz",
+            "/product/etc/NOTICE.xml.gz"};
+    static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
+
+    private final Context mContext;
+
+    public LicenseHtmlLoaderCompat(Context context) {
+        super(context);
+        mContext = context;
+    }
+
+    @Override
+    public File loadInBackground() {
+        return generateHtmlFromDefaultXmlFiles();
+    }
+
+    @Override
+    protected void onDiscardResult(File f) {
+    }
+
+    private File generateHtmlFromDefaultXmlFiles() {
+        final List<File> xmlFiles = getVaildXmlFiles();
+        if (xmlFiles.isEmpty()) {
+            Log.e(TAG, "No notice file exists.");
+            return null;
+        }
+
+        File cachedHtmlFile = getCachedHtmlFile(mContext);
+        if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile)
+                || generateHtmlFile(xmlFiles, cachedHtmlFile)) {
+            return cachedHtmlFile;
+        }
+
+        return null;
+    }
+
+    static List<File> getVaildXmlFiles() {
+        final List<File> xmlFiles = new ArrayList();
+        for (final String xmlPath : DEFAULT_LICENSE_XML_PATHS) {
+            File file = new File(xmlPath);
+            if (file.exists() && file.length() != 0) {
+                xmlFiles.add(file);
+            }
+        }
+        return xmlFiles;
+    }
+
+    static File getCachedHtmlFile(Context context) {
+        return new File(context.getCacheDir(), NOTICE_HTML_FILE_NAME);
+    }
+
+    static boolean isCachedHtmlFileOutdated(List<File> xmlFiles, File cachedHtmlFile) {
+        boolean outdated = true;
+        if (cachedHtmlFile.exists() && cachedHtmlFile.length() != 0) {
+            outdated = false;
+            for (File file : xmlFiles) {
+                if (cachedHtmlFile.lastModified() < file.lastModified()) {
+                    outdated = true;
+                    break;
+                }
+            }
+        }
+        return outdated;
+    }
+
+    static boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
+        return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/InjectedSetting.java b/packages/SettingsLib/src/com/android/settingslib/location/InjectedSetting.java
new file mode 100644
index 0000000..1805f1a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/location/InjectedSetting.java
@@ -0,0 +1,191 @@
+/*
+ * 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.content.Intent;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.Immutable;
+
+import java.util.Objects;
+
+/**
+ * Specifies a setting that is being injected into Settings &gt; Location &gt; Location services.
+ *
+ * @see android.location.SettingInjectorService
+ */
+@Immutable
+public class InjectedSetting {
+
+    /**
+     * Package for the subclass of {@link android.location.SettingInjectorService} and for the
+     * settings activity.
+     */
+    public final String packageName;
+
+    /**
+     * Class name for the subclass of {@link android.location.SettingInjectorService} that
+     * specifies dynamic values for the location setting.
+     */
+    public final String className;
+
+    /**
+     * The {@link android.support.v7.preference.Preference#getTitle()} value.
+     */
+    public final String title;
+
+    /**
+     * The {@link android.support.v7.preference.Preference#getIcon()} value.
+     */
+    public final int iconId;
+
+    /**
+     * The user/profile associated with this setting (e.g. managed profile)
+     */
+    public final UserHandle mUserHandle;
+
+    /**
+     * The activity to launch to allow the user to modify the settings value. Assumed to be in the
+     * {@link #packageName} package.
+     */
+    public final String settingsActivity;
+
+    /**
+     * The user restriction associated with this setting.
+     */
+    public final String userRestriction;
+
+    private InjectedSetting(Builder builder) {
+        this.packageName = builder.mPackageName;
+        this.className = builder.mClassName;
+        this.title = builder.mTitle;
+        this.iconId = builder.mIconId;
+        this.mUserHandle = builder.mUserHandle;
+        this.settingsActivity = builder.mSettingsActivity;
+        this.userRestriction = builder.mUserRestriction;
+    }
+
+    @Override
+    public String toString() {
+        return "InjectedSetting{" +
+                "mPackageName='" + packageName + '\'' +
+                ", mClassName='" + className + '\'' +
+                ", label=" + title +
+                ", iconId=" + iconId +
+                ", userId=" + mUserHandle.getIdentifier() +
+                ", settingsActivity='" + settingsActivity + '\'' +
+                ", userRestriction='" + userRestriction +
+                '}';
+    }
+
+    /**
+     * Returns the intent to start the {@link #className} service.
+     */
+    public Intent getServiceIntent() {
+        Intent intent = new Intent();
+        intent.setClassName(packageName, className);
+        return intent;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof InjectedSetting)) return false;
+
+        InjectedSetting that = (InjectedSetting) o;
+
+        return Objects.equals(packageName, that.packageName)
+                && Objects.equals(className, that.className)
+                && Objects.equals(title, that.title)
+                && Objects.equals(iconId, that.iconId)
+                && Objects.equals(mUserHandle, that.mUserHandle)
+                && Objects.equals(settingsActivity, that.settingsActivity)
+                && Objects.equals(userRestriction, that.userRestriction);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = packageName.hashCode();
+        result = 31 * result + className.hashCode();
+        result = 31 * result + title.hashCode();
+        result = 31 * result + iconId;
+        result = 31 * result + (mUserHandle == null ? 0 : mUserHandle.hashCode());
+        result = 31 * result + settingsActivity.hashCode();
+        result = 31 * result + (userRestriction == null ? 0 : userRestriction.hashCode());
+        return result;
+    }
+
+    public static class Builder {
+        private String mPackageName;
+        private String mClassName;
+        private String mTitle;
+        private int mIconId;
+        private UserHandle mUserHandle;
+        private String mSettingsActivity;
+        private String mUserRestriction;
+
+        public Builder setPackageName(String packageName) {
+            mPackageName = packageName;
+            return this;
+        }
+
+        public Builder setClassName(String className) {
+            mClassName = className;
+            return this;
+        }
+
+        public Builder setTitle(String title) {
+            mTitle = title;
+            return this;
+        }
+
+        public Builder setIconId(int iconId) {
+            mIconId = iconId;
+            return this;
+        }
+
+        public Builder setUserHandle(UserHandle userHandle) {
+            mUserHandle = userHandle;
+            return this;
+        }
+
+        public Builder setSettingsActivity(String settingsActivity) {
+            mSettingsActivity = settingsActivity;
+            return this;
+        }
+
+        public Builder setUserRestriction(String userRestriction) {
+            mUserRestriction = userRestriction;
+            return this;
+        }
+
+        public InjectedSetting build() {
+            if (mPackageName == null || mClassName == null || TextUtils.isEmpty(mTitle)
+                    || TextUtils.isEmpty(mSettingsActivity)) {
+                if (Log.isLoggable(SettingsInjector.TAG, Log.WARN)) {
+                    Log.w(SettingsInjector.TAG, "Illegal setting specification: package="
+                            + mPackageName + ", class=" + mClassName
+                            + ", title=" + mTitle + ", settingsActivity=" + mSettingsActivity);
+                }
+                return null;
+            }
+            return new InjectedSetting(this);
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
new file mode 100644
index 0000000..780fcba
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
@@ -0,0 +1,576 @@
+/*
+ * 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.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.location.SettingInjectorService;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.AttributeSet;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+import android.util.Xml;
+
+import androidx.preference.Preference;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Adds the preferences specified by the {@link InjectedSetting} objects to a preference group.
+ *
+ * Duplicates some code from {@link android.content.pm.RegisteredServicesCache}. We do not use that
+ * class directly because it is not a good match for our use case: we do not need the caching, and
+ * so do not want the additional resource hit at app install/upgrade time; and we would have to
+ * suppress the tie-breaking between multiple services reporting settings with the same name.
+ * Code-sharing would require extracting {@link
+ * android.content.pm.RegisteredServicesCache#parseServiceAttributes(android.content.res.Resources,
+ * String, android.util.AttributeSet)} into an interface, which didn't seem worth it.
+ */
+public class SettingsInjector {
+    static final String TAG = "SettingsInjector";
+
+    /**
+     * If reading the status of a setting takes longer than this, we go ahead and start reading
+     * the next setting.
+     */
+    private static final long INJECTED_STATUS_UPDATE_TIMEOUT_MILLIS = 1000;
+
+    /**
+     * {@link Message#what} value for starting to load status values
+     * in case we aren't already in the process of loading them.
+     */
+    private static final int WHAT_RELOAD = 1;
+
+    /**
+     * {@link Message#what} value sent after receiving a status message.
+     */
+    private static final int WHAT_RECEIVED_STATUS = 2;
+
+    /**
+     * {@link Message#what} value sent after the timeout waiting for a status message.
+     */
+    private static final int WHAT_TIMEOUT = 3;
+
+    private final Context mContext;
+
+    /**
+     * The settings that were injected
+     */
+    protected final Set<Setting> mSettings;
+
+    private final Handler mHandler;
+
+    public SettingsInjector(Context context) {
+        mContext = context;
+        mSettings = new HashSet<Setting>();
+        mHandler = new StatusLoadingHandler();
+    }
+
+    /**
+     * Returns a list for a profile with one {@link InjectedSetting} object for each
+     * {@link android.app.Service} that responds to
+     * {@link SettingInjectorService#ACTION_SERVICE_INTENT} and provides the expected setting
+     * metadata.
+     *
+     * Duplicates some code from {@link android.content.pm.RegisteredServicesCache}.
+     *
+     * TODO: unit test
+     */
+    protected List<InjectedSetting> getSettings(final UserHandle userHandle) {
+        PackageManager pm = mContext.getPackageManager();
+        Intent intent = new Intent(SettingInjectorService.ACTION_SERVICE_INTENT);
+
+        final int profileId = userHandle.getIdentifier();
+        List<ResolveInfo> resolveInfos =
+                pm.queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA, profileId);
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Found services for profile id " + profileId + ": " + resolveInfos);
+        }
+        List<InjectedSetting> settings = new ArrayList<InjectedSetting>(resolveInfos.size());
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            try {
+                InjectedSetting setting = parseServiceInfo(resolveInfo, userHandle, pm);
+                if (setting == null) {
+                    Log.w(TAG, "Unable to load service info " + resolveInfo);
+                } else {
+                    settings.add(setting);
+                }
+            } catch (XmlPullParserException e) {
+                Log.w(TAG, "Unable to load service info " + resolveInfo, e);
+            } catch (IOException e) {
+                Log.w(TAG, "Unable to load service info " + resolveInfo, e);
+            }
+        }
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Loaded settings for profile id " + profileId + ": " + settings);
+        }
+
+        return settings;
+    }
+
+    /**
+     * Adds the InjectedSetting information to a Preference object
+     */
+    private void populatePreference(Preference preference, InjectedSetting setting) {
+        final PackageManager pm = mContext.getPackageManager();
+        Drawable appIcon = null;
+        try {
+            final PackageItemInfo itemInfo = new PackageItemInfo();
+            itemInfo.icon = setting.iconId;
+            itemInfo.packageName = setting.packageName;
+            final ApplicationInfo appInfo = pm.getApplicationInfo(setting.packageName,
+                    PackageManager.GET_META_DATA);
+            appIcon = IconDrawableFactory.newInstance(mContext)
+                    .getBadgedIcon(itemInfo, appInfo, setting.mUserHandle.getIdentifier());
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Can't get ApplicationInfo for " + setting.packageName, e);
+        }
+        preference.setTitle(setting.title);
+        preference.setSummary(null);
+        preference.setIcon(appIcon);
+        preference.setOnPreferenceClickListener(new ServiceSettingClickedListener(setting));
+    }
+
+    /**
+     * Gets a list of preferences that other apps have injected.
+     *
+     * @param profileId Identifier of the user/profile to obtain the injected settings for or
+     *                  UserHandle.USER_CURRENT for all profiles associated with current user.
+     */
+    public List<Preference> getInjectedSettings(Context prefContext, final int profileId) {
+        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        final List<UserHandle> profiles = um.getUserProfiles();
+        ArrayList<Preference> prefs = new ArrayList<>();
+        for (UserHandle userHandle : profiles) {
+            if (profileId == UserHandle.USER_CURRENT || profileId == userHandle.getIdentifier()) {
+                Iterable<InjectedSetting> settings = getSettings(userHandle);
+                for (InjectedSetting setting : settings) {
+                    Preference preference = createPreference(prefContext, setting);
+                    populatePreference(preference, setting);
+                    prefs.add(preference);
+                    mSettings.add(new Setting(setting, preference));
+                }
+            }
+        }
+
+        reloadStatusMessages();
+
+        return prefs;
+    }
+
+    /**
+     * Creates an injected Preference
+     *
+     * @return the created Preference
+     */
+    protected Preference createPreference(Context prefContext, InjectedSetting setting) {
+        return new Preference(prefContext);
+    }
+
+    /**
+     * Returns the settings parsed from the attributes of the
+     * {@link SettingInjectorService#META_DATA_NAME} tag, or null.
+     *
+     * Duplicates some code from {@link android.content.pm.RegisteredServicesCache}.
+     */
+    private static InjectedSetting parseServiceInfo(ResolveInfo service, UserHandle userHandle,
+            PackageManager pm) throws XmlPullParserException, IOException {
+
+        ServiceInfo si = service.serviceInfo;
+        ApplicationInfo ai = si.applicationInfo;
+
+        if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+            if (Log.isLoggable(TAG, Log.WARN)) {
+                Log.w(TAG, "Ignoring attempt to inject setting from app not in system image: "
+                        + service);
+                return null;
+            }
+        }
+
+        XmlResourceParser parser = null;
+        try {
+            parser = si.loadXmlMetaData(pm, SettingInjectorService.META_DATA_NAME);
+            if (parser == null) {
+                throw new XmlPullParserException("No " + SettingInjectorService.META_DATA_NAME
+                        + " meta-data for " + service + ": " + si);
+            }
+
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+            }
+
+            String nodeName = parser.getName();
+            if (!SettingInjectorService.ATTRIBUTES_NAME.equals(nodeName)) {
+                throw new XmlPullParserException("Meta-data does not start with "
+                        + SettingInjectorService.ATTRIBUTES_NAME + " tag");
+            }
+
+            Resources res = pm.getResourcesForApplicationAsUser(si.packageName,
+                    userHandle.getIdentifier());
+            return parseAttributes(si.packageName, si.name, userHandle, res, attrs);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new XmlPullParserException(
+                    "Unable to load resources for package " + si.packageName);
+        } finally {
+            if (parser != null) {
+                parser.close();
+            }
+        }
+    }
+
+    /**
+     * Returns an immutable representation of the static attributes for the setting, or null.
+     */
+    private static InjectedSetting parseAttributes(String packageName, String className,
+            UserHandle userHandle, Resources res, AttributeSet attrs) {
+
+        TypedArray sa = res.obtainAttributes(attrs, android.R.styleable.SettingInjectorService);
+        try {
+            // Note that to help guard against malicious string injection, we do not allow dynamic
+            // specification of the label (setting title)
+            final String title = sa.getString(android.R.styleable.SettingInjectorService_title);
+            final int iconId =
+                    sa.getResourceId(android.R.styleable.SettingInjectorService_icon, 0);
+            final String settingsActivity =
+                    sa.getString(android.R.styleable.SettingInjectorService_settingsActivity);
+            final String userRestriction = sa.getString(
+                    android.R.styleable.SettingInjectorService_userRestriction);
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "parsed title: " + title + ", iconId: " + iconId
+                        + ", settingsActivity: " + settingsActivity);
+            }
+            return new InjectedSetting.Builder()
+                    .setPackageName(packageName)
+                    .setClassName(className)
+                    .setTitle(title)
+                    .setIconId(iconId)
+                    .setUserHandle(userHandle)
+                    .setSettingsActivity(settingsActivity)
+                    .setUserRestriction(userRestriction)
+                    .build();
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    /**
+     * Checks wheteher there is any preference that other apps have injected.
+     *
+     * @param profileId Identifier of the user/profile to obtain the injected settings for or
+     *                  UserHandle.USER_CURRENT for all profiles associated with current user.
+     */
+    public boolean hasInjectedSettings(final int profileId) {
+        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        final List<UserHandle> profiles = um.getUserProfiles();
+        final int profileCount = profiles.size();
+        for (int i = 0; i < profileCount; ++i) {
+            final UserHandle userHandle = profiles.get(i);
+            if (profileId == UserHandle.USER_CURRENT || profileId == userHandle.getIdentifier()) {
+                Iterable<InjectedSetting> settings = getSettings(userHandle);
+                for (InjectedSetting setting : settings) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Reloads the status messages for all the preference items.
+     */
+    public void reloadStatusMessages() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "reloadingStatusMessages: " + mSettings);
+        }
+        mHandler.sendMessage(mHandler.obtainMessage(WHAT_RELOAD));
+    }
+
+    protected class ServiceSettingClickedListener
+            implements Preference.OnPreferenceClickListener {
+        private InjectedSetting mInfo;
+
+        public ServiceSettingClickedListener(InjectedSetting info) {
+            mInfo = info;
+        }
+
+        @Override
+        public boolean onPreferenceClick(Preference preference) {
+            // Activity to start if they click on the preference. Must start in new task to ensure
+            // that "android.settings.LOCATION_SOURCE_SETTINGS" brings user back to
+            // Settings > Location.
+            Intent settingIntent = new Intent();
+            settingIntent.setClassName(mInfo.packageName, mInfo.settingsActivity);
+            // Sometimes the user may navigate back to "Settings" and launch another different
+            // injected setting after one injected setting has been launched.
+            //
+            // FLAG_ACTIVITY_CLEAR_TOP allows multiple Activities to stack on each other. When
+            // "back" button is clicked, the user will navigate through all the injected settings
+            // launched before. Such behavior could be quite confusing sometimes.
+            //
+            // In order to avoid such confusion, we use FLAG_ACTIVITY_CLEAR_TASK, which always clear
+            // up all existing injected settings and make sure that "back" button always brings the
+            // user back to "Settings" directly.
+            settingIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+            mContext.startActivityAsUser(settingIntent, mInfo.mUserHandle);
+            return true;
+        }
+    }
+
+    /**
+     * Loads the setting status values one at a time. Each load starts a subclass of {@link
+     * SettingInjectorService}, so to reduce memory pressure we don't want to load too many at
+     * once.
+     */
+    private final class StatusLoadingHandler extends Handler {
+
+        /**
+         * Settings whose status values need to be loaded. A set is used to prevent redundant loads.
+         */
+        private Set<Setting> mSettingsToLoad = new HashSet<Setting>();
+
+        /**
+         * Settings that are being loaded now and haven't timed out. In practice this should have
+         * zero or one elements.
+         */
+        private Set<Setting> mSettingsBeingLoaded = new HashSet<Setting>();
+
+        /**
+         * Settings that are being loaded but have timed out. If only one setting has timed out, we
+         * will go ahead and start loading the next setting so that one slow load won't delay the
+         * load of the other settings.
+         */
+        private Set<Setting> mTimedOutSettings = new HashSet<Setting>();
+
+        private boolean mReloadRequested;
+
+        private StatusLoadingHandler() {
+            super(Looper.getMainLooper());
+        }
+        @Override
+        public void handleMessage(Message msg) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "handleMessage start: " + msg + ", " + this);
+            }
+
+            // Update state in response to message
+            switch (msg.what) {
+                case WHAT_RELOAD:
+                    mReloadRequested = true;
+                    break;
+                case WHAT_RECEIVED_STATUS:
+                    final Setting receivedSetting = (Setting) msg.obj;
+                    receivedSetting.maybeLogElapsedTime();
+                    mSettingsBeingLoaded.remove(receivedSetting);
+                    mTimedOutSettings.remove(receivedSetting);
+                    removeMessages(WHAT_TIMEOUT, receivedSetting);
+                    break;
+                case WHAT_TIMEOUT:
+                    final Setting timedOutSetting = (Setting) msg.obj;
+                    mSettingsBeingLoaded.remove(timedOutSetting);
+                    mTimedOutSettings.add(timedOutSetting);
+                    if (Log.isLoggable(TAG, Log.WARN)) {
+                        Log.w(TAG, "Timed out after " + timedOutSetting.getElapsedTime()
+                                + " millis trying to get status for: " + timedOutSetting);
+                    }
+                    break;
+                default:
+                    Log.wtf(TAG, "Unexpected what: " + msg);
+            }
+
+            // Decide whether to load additional settings based on the new state. Start by seeing
+            // if we have headroom to load another setting.
+            if (mSettingsBeingLoaded.size() > 0 || mTimedOutSettings.size() > 1) {
+                // Don't load any more settings until one of the pending settings has completed.
+                // To reduce memory pressure, we want to be loading at most one setting (plus at
+                // most one timed-out setting) at a time. This means we'll be responsible for
+                // bringing in at most two services.
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "too many services already live for " + msg + ", " + this);
+                }
+                return;
+            }
+
+            if (mReloadRequested && mSettingsToLoad.isEmpty() && mSettingsBeingLoaded.isEmpty()
+                    && mTimedOutSettings.isEmpty()) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "reloading because idle and reload requesteed " + msg + ", " + this);
+                }
+                // Reload requested, so must reload all settings
+                mSettingsToLoad.addAll(mSettings);
+                mReloadRequested = false;
+            }
+
+            // Remove the next setting to load from the queue, if any
+            Iterator<Setting> iter = mSettingsToLoad.iterator();
+            if (!iter.hasNext()) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "nothing left to do for " + msg + ", " + this);
+                }
+                return;
+            }
+            Setting setting = iter.next();
+            iter.remove();
+
+            // Request the status value
+            setting.startService();
+            mSettingsBeingLoaded.add(setting);
+
+            // Ensure that if receiving the status value takes too long, we start loading the
+            // next value anyway
+            Message timeoutMsg = obtainMessage(WHAT_TIMEOUT, setting);
+            sendMessageDelayed(timeoutMsg, INJECTED_STATUS_UPDATE_TIMEOUT_MILLIS);
+
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "handleMessage end " + msg + ", " + this
+                        + ", started loading " + setting);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "StatusLoadingHandler{" +
+                    "mSettingsToLoad=" + mSettingsToLoad +
+                    ", mSettingsBeingLoaded=" + mSettingsBeingLoaded +
+                    ", mTimedOutSettings=" + mTimedOutSettings +
+                    ", mReloadRequested=" + mReloadRequested +
+                    '}';
+        }
+    }
+
+    /**
+     * Represents an injected setting and the corresponding preference.
+     */
+    protected final class Setting {
+
+        public final InjectedSetting setting;
+        public final Preference preference;
+        public long startMillis;
+
+        public Setting(InjectedSetting setting, Preference preference) {
+            this.setting = setting;
+            this.preference = preference;
+        }
+
+        @Override
+        public String toString() {
+            return "Setting{" +
+                    "setting=" + setting +
+                    ", preference=" + preference +
+                    '}';
+        }
+
+        /**
+         * Returns true if they both have the same {@link #setting} value. Ignores mutable
+         * {@link #preference} and {@link #startMillis} so that it's safe to use in sets.
+         */
+        @Override
+        public boolean equals(Object o) {
+            return this == o || o instanceof Setting && setting.equals(((Setting) o).setting);
+        }
+
+        @Override
+        public int hashCode() {
+            return setting.hashCode();
+        }
+
+        /**
+         * Starts the service to fetch for the current status for the setting, and updates the
+         * preference when the service replies.
+         */
+        public void startService() {
+            final ActivityManager am = (ActivityManager)
+                    mContext.getSystemService(Context.ACTIVITY_SERVICE);
+            if (!am.isUserRunning(setting.mUserHandle.getIdentifier())) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "Cannot start service as user "
+                            + setting.mUserHandle.getIdentifier() + " is not running");
+                }
+                return;
+            }
+            Handler handler = new Handler() {
+                @Override
+                public void handleMessage(Message msg) {
+                    Bundle bundle = msg.getData();
+                    boolean enabled = bundle.getBoolean(SettingInjectorService.ENABLED_KEY, true);
+                    if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        Log.d(TAG, setting + ": received " + msg + ", bundle: " + bundle);
+                    }
+                    preference.setSummary(null);
+                    preference.setEnabled(enabled);
+                    mHandler.sendMessage(
+                            mHandler.obtainMessage(WHAT_RECEIVED_STATUS, Setting.this));
+                }
+            };
+            Messenger messenger = new Messenger(handler);
+
+            Intent intent = setting.getServiceIntent();
+            intent.putExtra(SettingInjectorService.MESSENGER_KEY, messenger);
+
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, setting + ": sending update intent: " + intent
+                        + ", handler: " + handler);
+                startMillis = SystemClock.elapsedRealtime();
+            } else {
+                startMillis = 0;
+            }
+
+            // Start the service, making sure that this is attributed to the user associated with
+            // the setting rather than the system user.
+            mContext.startServiceAsUser(intent, setting.mUserHandle);
+        }
+
+        public long getElapsedTime() {
+            long end = SystemClock.elapsedRealtime();
+            return end - startMillis;
+        }
+
+        public void maybeLogElapsedTime() {
+            if (Log.isLoggable(TAG, Log.DEBUG) && startMillis != 0) {
+                long elapsed = getElapsedTime();
+                Log.d(TAG, this + " update took " + elapsed + " millis");
+            }
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
new file mode 100644
index 0000000..3adbd4d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
@@ -0,0 +1,146 @@
+/*
+ * 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.net;
+
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.SET_FOREGROUND;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
+import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+
+import android.content.Context;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.settingslib.AppItem;
+
+import androidx.loader.content.AsyncTaskLoader;
+
+/**
+ * Loader for historical chart data for both network and UID details.
+ */
+public class ChartDataLoaderCompat extends AsyncTaskLoader<ChartData> {
+    private static final String KEY_TEMPLATE = "template";
+    private static final String KEY_APP = "app";
+    private static final String KEY_FIELDS = "fields";
+
+    private final INetworkStatsSession mSession;
+    private final Bundle mArgs;
+
+    public static Bundle buildArgs(NetworkTemplate template, AppItem app) {
+        return buildArgs(template, app, FIELD_RX_BYTES | FIELD_TX_BYTES);
+    }
+
+    public static Bundle buildArgs(NetworkTemplate template, AppItem app, int fields) {
+        final Bundle args = new Bundle();
+        args.putParcelable(KEY_TEMPLATE, template);
+        args.putParcelable(KEY_APP, app);
+        args.putInt(KEY_FIELDS, fields);
+        return args;
+    }
+
+    public ChartDataLoaderCompat(Context context, INetworkStatsSession session, Bundle args) {
+        super(context);
+        mSession = session;
+        mArgs = args;
+    }
+
+    @Override
+    protected void onStartLoading() {
+        super.onStartLoading();
+        forceLoad();
+    }
+
+    @Override
+    public ChartData loadInBackground() {
+        final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
+        final AppItem app = mArgs.getParcelable(KEY_APP);
+        final int fields = mArgs.getInt(KEY_FIELDS);
+
+        try {
+            return loadInBackground(template, app, fields);
+        } catch (RemoteException e) {
+            // since we can't do much without history, and we don't want to
+            // leave with half-baked UI, we bail hard.
+            throw new RuntimeException("problem reading network stats", e);
+        }
+    }
+
+    private ChartData loadInBackground(NetworkTemplate template, AppItem app, int fields)
+            throws RemoteException {
+        final ChartData data = new ChartData();
+        data.network = mSession.getHistoryForNetwork(template, fields);
+
+        if (app != null) {
+            // load stats for current uid and template
+            final int size = app.uids.size();
+            for (int i = 0; i < size; i++) {
+                final int uid = app.uids.keyAt(i);
+                data.detailDefault = collectHistoryForUid(
+                        template, uid, SET_DEFAULT, data.detailDefault);
+                data.detailForeground = collectHistoryForUid(
+                        template, uid, SET_FOREGROUND, data.detailForeground);
+            }
+
+            if (size > 0) {
+                data.detail = new NetworkStatsHistory(data.detailForeground.getBucketDuration());
+                data.detail.recordEntireHistory(data.detailDefault);
+                data.detail.recordEntireHistory(data.detailForeground);
+            } else {
+                data.detailDefault = new NetworkStatsHistory(HOUR_IN_MILLIS);
+                data.detailForeground = new NetworkStatsHistory(HOUR_IN_MILLIS);
+                data.detail = new NetworkStatsHistory(HOUR_IN_MILLIS);
+            }
+        }
+
+        return data;
+    }
+
+    @Override
+    protected void onStopLoading() {
+        super.onStopLoading();
+        cancelLoad();
+    }
+
+    @Override
+    protected void onReset() {
+        super.onReset();
+        cancelLoad();
+    }
+
+    /**
+     * Collect {@link NetworkStatsHistory} for the requested UID, combining with
+     * an existing {@link NetworkStatsHistory} if provided.
+     */
+    private NetworkStatsHistory collectHistoryForUid(
+            NetworkTemplate template, int uid, int set, NetworkStatsHistory existing)
+            throws RemoteException {
+        final NetworkStatsHistory history = mSession.getHistoryForUid(
+                template, uid, set, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
+
+        if (existing != null) {
+            existing.recordEntireHistory(history);
+            return existing;
+        } else {
+            return history;
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index f7aa297..eeaa987 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -38,12 +38,14 @@
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
 import android.util.Log;
-import android.util.Pair;
+import android.util.Range;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.time.ZonedDateTime;
 import java.util.Date;
+import java.util.Iterator;
 import java.util.Locale;
 
 public class DataUsageController {
@@ -86,7 +88,8 @@
                 * mContext.getResources().getInteger(R.integer.default_data_warning_level_mb);
     }
 
-    private INetworkStatsSession getSession() {
+    @VisibleForTesting
+    INetworkStatsSession getSession() {
         if (mSession == null) {
             try {
                 mSession = mStatsService.openSession();
@@ -134,11 +137,12 @@
             final NetworkStatsHistory history = session.getHistoryForNetwork(template, FIELDS);
             final long now = System.currentTimeMillis();
             final long start, end;
-            if (policy != null) {
-                final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
-                        .cycleIterator(policy).next();
-                start = cycle.first.toInstant().toEpochMilli();
-                end = cycle.second.toInstant().toEpochMilli();
+            final Iterator<Range<ZonedDateTime>> it =
+                    (policy != null) ? policy.cycleIterator() : null;
+            if (it != null && it.hasNext()) {
+                final Range<ZonedDateTime> cycle = it.next();
+                start = cycle.getLower().toInstant().toEpochMilli();
+                end = cycle.getUpper().toInstant().toEpochMilli();
             } else {
                 // period = last 4 wks
                 end = now;
@@ -176,6 +180,30 @@
         }
     }
 
+    /**
+     * Get the total usage level recorded in the network history
+     * @param template the network template to retrieve the network history
+     * @return the total usage level recorded in the network history
+     */
+    public long getHistoriclUsageLevel(NetworkTemplate template) {
+        final INetworkStatsSession session = getSession();
+        if (session != null) {
+            try {
+                final NetworkStatsHistory history = session.getHistoryForNetwork(template, FIELDS);
+                final long now = System.currentTimeMillis();
+                final NetworkStatsHistory.Entry entry =
+                        history.getValues(0L /* start */, now /* end */, now, null /* recycle */);
+                if (entry != null) {
+                    return entry.rxBytes + entry.txBytes;
+                }
+                Log.w(TAG, "Failed to get data usage, no entry data");
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to get data usage, remote call failed");
+            }
+        }
+        return 0L;
+    }
+
     private NetworkPolicy findNetworkPolicy(NetworkTemplate template) {
         if (mPolicyManager == null || template == null) return null;
         final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
new file mode 100644
index 0000000..c311337
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.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 com.android.settingslib.net;
+
+import android.content.Context;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStats;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import androidx.loader.content.AsyncTaskLoader;
+
+public class SummaryForAllUidLoaderCompat extends AsyncTaskLoader<NetworkStats> {
+    private static final String KEY_TEMPLATE = "template";
+    private static final String KEY_START = "start";
+    private static final String KEY_END = "end";
+
+    private final INetworkStatsSession mSession;
+    private final Bundle mArgs;
+
+    public static Bundle buildArgs(NetworkTemplate template, long start, long end) {
+        final Bundle args = new Bundle();
+        args.putParcelable(KEY_TEMPLATE, template);
+        args.putLong(KEY_START, start);
+        args.putLong(KEY_END, end);
+        return args;
+    }
+
+    public SummaryForAllUidLoaderCompat(Context context, INetworkStatsSession session,
+            Bundle args) {
+        super(context);
+        mSession = session;
+        mArgs = args;
+    }
+
+    @Override
+    protected void onStartLoading() {
+        super.onStartLoading();
+        forceLoad();
+    }
+
+    @Override
+    public NetworkStats loadInBackground() {
+        final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
+        final long start = mArgs.getLong(KEY_START);
+        final long end = mArgs.getLong(KEY_END);
+
+        try {
+            return mSession.getSummaryForAllUid(template, start, end, false);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    @Override
+    protected void onStopLoading() {
+        super.onStopLoading();
+        cancelLoad();
+    }
+
+    @Override
+    protected void onReset() {
+        super.onReset();
+        cancelLoad();
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java
index 6da486a..0ef46a1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java
@@ -17,14 +17,12 @@
 package com.android.settingslib.notification;
 
 import android.app.ActivityManager;
-import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.provider.Settings;
 import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
-import androidx.annotation.VisibleForTesting;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.CompoundButton;
@@ -42,6 +40,9 @@
 
 import java.util.Arrays;
 
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AlertDialog;
+
 public class ZenDurationDialog {
     private static final int[] MINUTE_BUCKETS = ZenModeConfig.MINUTE_BUCKETS;
     @VisibleForTesting protected static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
@@ -66,12 +67,17 @@
     }
 
     public Dialog createDialog() {
-        int zenDuration = Settings.Global.getInt(
-                mContext.getContentResolver(), Settings.Global.ZEN_DURATION,
-                Settings.Global.ZEN_DURATION_FOREVER);
+        final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+        setupDialog(builder);
+        return builder.create();
+    }
 
-        final AlertDialog.Builder builder = new AlertDialog.Builder(mContext)
-                .setTitle(R.string.zen_mode_duration_settings_title)
+    public void setupDialog(AlertDialog.Builder builder) {
+        int zenDuration = Settings.Secure.getInt(
+                mContext.getContentResolver(), Settings.Secure.ZEN_DURATION,
+                Settings.Secure.ZEN_DURATION_FOREVER);
+
+        builder.setTitle(R.string.zen_mode_duration_settings_title)
                 .setNegativeButton(R.string.cancel, null)
                 .setPositiveButton(R.string.okay,
                         new DialogInterface.OnClickListener() {
@@ -84,19 +90,18 @@
         View contentView = getContentView();
         setupRadioButtons(zenDuration);
         builder.setView(contentView);
-        return builder.create();
     }
 
     @VisibleForTesting
     protected void updateZenDuration(int currZenDuration) {
         final int checkedRadioButtonId = mZenRadioGroup.getCheckedRadioButtonId();
 
-        int newZenDuration = Settings.Global.getInt(
-                mContext.getContentResolver(), Settings.Global.ZEN_DURATION,
-                Settings.Global.ZEN_DURATION_FOREVER);
+        int newZenDuration = Settings.Secure.getInt(
+                mContext.getContentResolver(), Settings.Secure.ZEN_DURATION,
+                Settings.Secure.ZEN_DURATION_FOREVER);
         switch (checkedRadioButtonId) {
             case FOREVER_CONDITION_INDEX:
-                newZenDuration = Settings.Global.ZEN_DURATION_FOREVER;
+                newZenDuration = Settings.Secure.ZEN_DURATION_FOREVER;
                 MetricsLogger.action(mContext,
                         MetricsProto.MetricsEvent.
                                 NOTIFICATION_ZEN_MODE_DURATION_FOREVER);
@@ -110,7 +115,7 @@
                         newZenDuration);
                 break;
             case ALWAYS_ASK_CONDITION_INDEX:
-                newZenDuration = Settings.Global.ZEN_DURATION_PROMPT;
+                newZenDuration = Settings.Secure.ZEN_DURATION_PROMPT;
                 MetricsLogger.action(mContext,
                         MetricsProto.MetricsEvent.
                                 NOTIFICATION_ZEN_MODE_DURATION_PROMPT);
@@ -118,8 +123,8 @@
         }
 
         if (currZenDuration != newZenDuration) {
-            Settings.Global.putInt(mContext.getContentResolver(),
-                    Settings.Global.ZEN_DURATION, newZenDuration);
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.ZEN_DURATION, newZenDuration);
         }
     }
 
@@ -269,8 +274,7 @@
         String radioContentText = "";
         switch (rowIndex) {
             case FOREVER_CONDITION_INDEX:
-                radioContentText = mContext.getString(
-                        com.android.internal.R.string.zen_mode_forever);
+                radioContentText = mContext.getString(R.string.zen_mode_forever);
                 break;
             case COUNTDOWN_CONDITION_INDEX:
                 Condition condition = ZenModeConfig.toTimeCondition(mContext,
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java
deleted file mode 100644
index 19e556a..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java
+++ /dev/null
@@ -1,25 +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.settingslib.suggestions;
-
-public class SuggestionCategory {
-    public String category;
-    public String pkg;
-    public boolean multiple;
-    public boolean exclusive;
-    public long exclusiveExpireDaysInMillis;
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompat.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompat.java
new file mode 100644
index 0000000..1791217
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompat.java
@@ -0,0 +1,143 @@
+/*
+ * 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.suggestions;
+
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.service.settings.suggestions.Suggestion;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import java.util.List;
+
+import androidx.annotation.Nullable;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+
+/**
+ * Manages IPC communication to SettingsIntelligence for suggestion related services.
+ */
+public class SuggestionControllerMixinCompat implements
+        SuggestionController.ServiceConnectionListener, androidx.lifecycle.LifecycleObserver,
+        LoaderManager.LoaderCallbacks<List<Suggestion>> {
+
+    public interface SuggestionControllerHost {
+        /**
+         * Called when suggestion data fetching is ready.
+         */
+        void onSuggestionReady(List<Suggestion> data);
+
+        /**
+         * Returns {@link LoaderManager} associated with the host. If host is not attached to
+         * activity then return null.
+         */
+        @Nullable
+        LoaderManager getLoaderManager();
+    }
+
+    private static final String TAG = "SuggestionCtrlMixin";
+    private static final boolean DEBUG = false;
+
+    private final Context mContext;
+    private final SuggestionController mSuggestionController;
+    private final SuggestionControllerHost mHost;
+
+    private boolean mSuggestionLoaded;
+
+    public SuggestionControllerMixinCompat(Context context, SuggestionControllerHost host,
+            Lifecycle lifecycle, ComponentName componentName) {
+        mContext = context.getApplicationContext();
+        mHost = host;
+        mSuggestionController = new SuggestionController(mContext, componentName,
+                    this /* serviceConnectionListener */);
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_START)
+    public void onStart() {
+        if (DEBUG) {
+            Log.d(TAG, "SuggestionController started");
+        }
+        mSuggestionController.start();
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+    public void onStop() {
+        if (DEBUG) {
+            Log.d(TAG, "SuggestionController stopped.");
+        }
+        mSuggestionController.stop();
+    }
+
+    @Override
+    public void onServiceConnected() {
+        final LoaderManager loaderManager = mHost.getLoaderManager();
+        if (loaderManager != null) {
+            loaderManager.restartLoader(SuggestionLoader.LOADER_ID_SUGGESTIONS,
+                    null /* args */, this /* callback */);
+        }
+    }
+
+    @Override
+    public void onServiceDisconnected() {
+        if (DEBUG) {
+            Log.d(TAG, "SuggestionService disconnected");
+        }
+        final LoaderManager loaderManager = mHost.getLoaderManager();
+        if (loaderManager != null) {
+            loaderManager.destroyLoader(SuggestionLoader.LOADER_ID_SUGGESTIONS);
+        }
+    }
+
+    @Override
+    public Loader<List<Suggestion>> onCreateLoader(int id, Bundle args) {
+        if (id == SuggestionLoader.LOADER_ID_SUGGESTIONS) {
+            mSuggestionLoaded = false;
+            return new SuggestionLoaderCompat(mContext, mSuggestionController);
+        }
+        throw new IllegalArgumentException("This loader id is not supported " + id);
+    }
+
+    @Override
+    public void onLoadFinished(Loader<List<Suggestion>> loader, List<Suggestion> data) {
+        mSuggestionLoaded = true;
+        mHost.onSuggestionReady(data);
+    }
+
+    @Override
+    public void onLoaderReset(Loader<List<Suggestion>> loader) {
+        mSuggestionLoaded = false;
+    }
+
+    public boolean isSuggestionLoaded() {
+        return mSuggestionLoaded;
+    }
+
+    public void dismissSuggestion(Suggestion suggestion) {
+        mSuggestionController.dismissSuggestions(suggestion);
+    }
+
+    public void launchSuggestion(Suggestion suggestion) {
+        mSuggestionController.launchSuggestion(suggestion);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java
deleted file mode 100644
index a890920..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java
+++ /dev/null
@@ -1,85 +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.settingslib.suggestions;
-
-import android.content.Intent;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import com.android.settingslib.drawer.Tile;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class SuggestionList {
-    // Category -> list of suggestion map
-    private final Map<SuggestionCategory, List<Tile>> mSuggestions;
-
-    // A flatten list of all suggestions.
-    private List<Tile> mSuggestionList;
-
-    public SuggestionList() {
-        mSuggestions = new ArrayMap<>();
-    }
-
-    public void addSuggestions(SuggestionCategory category, List<Tile> suggestions) {
-        mSuggestions.put(category, suggestions);
-    }
-
-    public List<Tile> getSuggestions() {
-        if (mSuggestionList != null) {
-            return mSuggestionList;
-        }
-        mSuggestionList = new ArrayList<>();
-        for (List<Tile> suggestions : mSuggestions.values()) {
-            mSuggestionList.addAll(suggestions);
-        }
-        dedupeSuggestions(mSuggestionList);
-        return mSuggestionList;
-    }
-
-    public boolean isExclusiveSuggestionCategory() {
-        if (mSuggestions.size() != 1) {
-            // If there is no category, or more than 1 category, it's not exclusive by definition.
-            return false;
-        }
-        for (SuggestionCategory category : mSuggestions.keySet()) {
-            if (category.exclusive) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Filter suggestions list so they are all unique.
-     */
-    private void dedupeSuggestions(List<Tile> suggestions) {
-        final Set<String> intents = new ArraySet<>();
-        for (int i = suggestions.size() - 1; i >= 0; i--) {
-            final Tile suggestion = suggestions.get(i);
-            final String intentUri = suggestion.intent.toUri(Intent.URI_INTENT_SCHEME);
-            if (intents.contains(intentUri)) {
-                suggestions.remove(i);
-            } else {
-                intents.add(intentUri);
-            }
-        }
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionLoaderCompat.java
new file mode 100644
index 0000000..066de19
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionLoaderCompat.java
@@ -0,0 +1,54 @@
+/*
+ * 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.settingslib.suggestions;
+
+import android.content.Context;
+import android.service.settings.suggestions.Suggestion;
+import android.util.Log;
+
+import com.android.settingslib.utils.AsyncLoaderCompat;
+
+import java.util.List;
+
+public class SuggestionLoaderCompat extends AsyncLoaderCompat<List<Suggestion>> {
+
+    public static final int LOADER_ID_SUGGESTIONS = 42;
+    private static final String TAG = "SuggestionLoader";
+
+    private final SuggestionController mSuggestionController;
+
+    public SuggestionLoaderCompat(Context context, SuggestionController controller) {
+        super(context);
+        mSuggestionController = controller;
+    }
+
+    @Override
+    protected void onDiscardResult(List<Suggestion> result) {
+
+    }
+
+    @Override
+    public List<Suggestion> loadInBackground() {
+        final List<Suggestion> data = mSuggestionController.getSuggestions();
+        if (data == null) {
+            Log.d(TAG, "data is null");
+        } else {
+            Log.d(TAG, "data size " + data.size());
+        }
+        return data;
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
deleted file mode 100644
index 8705c98..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
+++ /dev/null
@@ -1,498 +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.settingslib.suggestions;
-
-import android.Manifest;
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.annotation.RequiresPermission;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import androidx.annotation.VisibleForTesting;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.ArrayMap;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Xml;
-import android.view.InflateException;
-
-import com.android.settingslib.drawer.Tile;
-import com.android.settingslib.drawer.TileUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-public class SuggestionParser {
-
-    private static final String TAG = "SuggestionParser";
-
-    // If defined, only returns this suggestion if the feature is supported.
-    public static final String META_DATA_REQUIRE_FEATURE = "com.android.settings.require_feature";
-
-    // If defined, only display this optional step if an account of that type exists.
-    private static final String META_DATA_REQUIRE_ACCOUNT = "com.android.settings.require_account";
-
-    // If defined and not true, do not should optional step.
-    private static final String META_DATA_IS_SUPPORTED = "com.android.settings.is_supported";
-
-    // If defined, only display this optional step if the current user is of that type.
-    private static final String META_DATA_REQUIRE_USER_TYPE =
-            "com.android.settings.require_user_type";
-
-    // If defined, only display this optional step if a connection is available.
-    private static final String META_DATA_IS_CONNECTION_REQUIRED =
-            "com.android.settings.require_connection";
-
-    // The valid values that setup wizard recognizes for differentiating user types.
-    private static final String META_DATA_PRIMARY_USER_TYPE_VALUE = "primary";
-    private static final String META_DATA_ADMIN_USER_TYPE_VALUE = "admin";
-    private static final String META_DATA_GUEST_USER_TYPE_VALUE = "guest";
-    private static final String META_DATA_RESTRICTED_USER_TYPE_VALUE = "restricted";
-
-    /**
-     * Allows suggestions to appear after a certain number of days, and to re-appear if dismissed.
-     * For instance:
-     * 0,10
-     * Will appear immediately, but if the user removes it, it will come back after 10 days.
-     *
-     * Another example:
-     * 10,30
-     * Will only show up after 10 days, and then again after 30.
-     */
-    public static final String META_DATA_DISMISS_CONTROL = "com.android.settings.dismiss";
-
-    // Shared prefs keys for storing dismissed state.
-    // Index into current dismissed state.
-    public static final String SETUP_TIME = "_setup_time";
-    private static final String IS_DISMISSED = "_is_dismissed";
-
-    // Default dismiss control for smart suggestions.
-    private static final String DEFAULT_SMART_DISMISS_CONTROL = "0";
-
-    private final Context mContext;
-    private final List<SuggestionCategory> mSuggestionList;
-    private final ArrayMap<Pair<String, String>, Tile> mAddCache = new ArrayMap<>();
-    private final SharedPreferences mSharedPrefs;
-    private final String mDefaultDismissControl;
-
-    public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml,
-            String defaultDismissControl) {
-        this(
-                context,
-                sharedPrefs,
-                (List<SuggestionCategory>) new SuggestionOrderInflater(context).parse(orderXml),
-                defaultDismissControl);
-    }
-
-    public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) {
-        this(context, sharedPrefs, orderXml, DEFAULT_SMART_DISMISS_CONTROL);
-    }
-
-    @VisibleForTesting
-    public SuggestionParser(
-            Context context,
-            SharedPreferences sharedPrefs,
-            List<SuggestionCategory> suggestionList,
-            String defaultDismissControl) {
-        mContext = context;
-        mSuggestionList = suggestionList;
-        mSharedPrefs = sharedPrefs;
-        mDefaultDismissControl = defaultDismissControl;
-    }
-
-    public SuggestionList getSuggestions(boolean isSmartSuggestionEnabled) {
-        final SuggestionList suggestionList = new SuggestionList();
-        final int N = mSuggestionList.size();
-        for (int i = 0; i < N; i++) {
-            final SuggestionCategory category = mSuggestionList.get(i);
-            if (category.exclusive && !isExclusiveCategoryExpired(category)) {
-                // If suggestions from an exclusive category are present, parsing is stopped
-                // and only suggestions from that category are displayed. Note that subsequent
-                // exclusive categories are also ignored.
-                final List<Tile> exclusiveSuggestions = new ArrayList<>();
-
-                // Read suggestion and force isSmartSuggestion to be false so the rule defined
-                // from each suggestion itself is used.
-                readSuggestions(category, exclusiveSuggestions, false /* isSmartSuggestion */);
-                if (!exclusiveSuggestions.isEmpty()) {
-                    final SuggestionList exclusiveList = new SuggestionList();
-                    exclusiveList.addSuggestions(category, exclusiveSuggestions);
-                    return exclusiveList;
-                }
-            } else {
-                // Either the category is not exclusive, or the exclusiveness expired so we should
-                // treat it as a normal category.
-                final List<Tile> suggestions = new ArrayList<>();
-                readSuggestions(category, suggestions, isSmartSuggestionEnabled);
-                suggestionList.addSuggestions(category, suggestions);
-            }
-        }
-        return suggestionList;
-    }
-
-    /**
-     * Dismisses a suggestion, returns true if the suggestion has no more dismisses left and should
-     * be disabled.
-     */
-    public boolean dismissSuggestion(Tile suggestion) {
-        final String keyBase = suggestion.intent.getComponent().flattenToShortString();
-        mSharedPrefs.edit()
-                .putBoolean(keyBase + IS_DISMISSED, true)
-                .commit();
-        return true;
-    }
-
-    @VisibleForTesting
-    public void filterSuggestions(
-            List<Tile> suggestions, int countBefore, boolean isSmartSuggestionEnabled) {
-        for (int i = countBefore; i < suggestions.size(); i++) {
-            if (!isAvailable(suggestions.get(i)) ||
-                    !isSupported(suggestions.get(i)) ||
-                    !satisifesRequiredUserType(suggestions.get(i)) ||
-                    !satisfiesRequiredAccount(suggestions.get(i)) ||
-                    !satisfiesConnectivity(suggestions.get(i)) ||
-                    isDismissed(suggestions.get(i), isSmartSuggestionEnabled)) {
-                suggestions.remove(i--);
-            }
-        }
-    }
-
-    @VisibleForTesting
-    void readSuggestions(
-            SuggestionCategory category, List<Tile> suggestions, boolean isSmartSuggestionEnabled) {
-        int countBefore = suggestions.size();
-        Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.addCategory(category.category);
-        if (category.pkg != null) {
-            intent.setPackage(category.pkg);
-        }
-        TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent,
-                mAddCache, null, suggestions, true, false, false, true /* shouldUpdateTiles */);
-        filterSuggestions(suggestions, countBefore, isSmartSuggestionEnabled);
-        if (!category.multiple && suggestions.size() > (countBefore + 1)) {
-            // If there are too many, remove them all and only re-add the one with the highest
-            // priority.
-            Tile item = suggestions.remove(suggestions.size() - 1);
-            while (suggestions.size() > countBefore) {
-                Tile last = suggestions.remove(suggestions.size() - 1);
-                if (last.priority > item.priority) {
-                    item = last;
-                }
-            }
-            // If category is marked as done, do not add any item.
-            if (!isCategoryDone(category.category)) {
-                suggestions.add(item);
-            }
-        }
-    }
-
-    private boolean isAvailable(Tile suggestion) {
-        final String featuresRequired = suggestion.metaData.getString(META_DATA_REQUIRE_FEATURE);
-        if (featuresRequired != null) {
-            for (String feature : featuresRequired.split(",")) {
-                if (TextUtils.isEmpty(feature)) {
-                    Log.w(TAG, "Found empty substring when parsing required features: "
-                            + featuresRequired);
-                } else if (!mContext.getPackageManager().hasSystemFeature(feature)) {
-                    Log.i(TAG, suggestion.title + " requires unavailable feature " + feature);
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    @RequiresPermission(Manifest.permission.MANAGE_USERS)
-    private boolean satisifesRequiredUserType(Tile suggestion) {
-        final String requiredUser = suggestion.metaData.getString(META_DATA_REQUIRE_USER_TYPE);
-        if (requiredUser != null) {
-            final UserManager userManager = mContext.getSystemService(UserManager.class);
-            UserInfo userInfo = userManager.getUserInfo(UserHandle.myUserId());
-            for (String userType : requiredUser.split("\\|")) {
-                final boolean primaryUserCondtionMet = userInfo.isPrimary()
-                        && META_DATA_PRIMARY_USER_TYPE_VALUE.equals(userType);
-                final boolean adminUserConditionMet = userInfo.isAdmin()
-                        && META_DATA_ADMIN_USER_TYPE_VALUE.equals(userType);
-                final boolean guestUserCondtionMet = userInfo.isGuest()
-                        && META_DATA_GUEST_USER_TYPE_VALUE.equals(userType);
-                final boolean restrictedUserCondtionMet = userInfo.isRestricted()
-                        && META_DATA_RESTRICTED_USER_TYPE_VALUE.equals(userType);
-                if (primaryUserCondtionMet || adminUserConditionMet || guestUserCondtionMet
-                        || restrictedUserCondtionMet) {
-                    return true;
-                }
-            }
-            Log.i(TAG, suggestion.title + " requires user type " + requiredUser);
-            return false;
-        }
-        return true;
-    }
-
-    public boolean satisfiesRequiredAccount(Tile suggestion) {
-        final String requiredAccountType = suggestion.metaData.getString(META_DATA_REQUIRE_ACCOUNT);
-        if (requiredAccountType == null) {
-            return true;
-        }
-        AccountManager accountManager = mContext.getSystemService(AccountManager.class);
-        Account[] accounts = accountManager.getAccountsByType(requiredAccountType);
-        boolean satisfiesRequiredAccount = accounts.length > 0;
-        if (!satisfiesRequiredAccount) {
-            Log.i(TAG, suggestion.title + " requires unavailable account type "
-                    + requiredAccountType);
-        }
-        return satisfiesRequiredAccount;
-    }
-
-    public boolean isSupported(Tile suggestion) {
-        final int isSupportedResource = suggestion.metaData.getInt(META_DATA_IS_SUPPORTED);
-        try {
-            if (suggestion.intent == null) {
-                return false;
-            }
-            final Resources res = mContext.getPackageManager().getResourcesForActivity(
-                    suggestion.intent.getComponent());
-            boolean isSupported =
-                    isSupportedResource != 0 ? res.getBoolean(isSupportedResource) : true;
-            if (!isSupported) {
-                Log.i(TAG, suggestion.title + " requires unsupported resource "
-                        + isSupportedResource);
-            }
-            return isSupported;
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent());
-            return false;
-        } catch (Resources.NotFoundException e) {
-            Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent(), e);
-            return false;
-        }
-    }
-
-    private boolean satisfiesConnectivity(Tile suggestion) {
-        final boolean isConnectionRequired =
-                suggestion.metaData.getBoolean(META_DATA_IS_CONNECTION_REQUIRED);
-        if (!isConnectionRequired) {
-            return true;
-        }
-        ConnectivityManager cm =
-                (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        NetworkInfo netInfo = cm.getActiveNetworkInfo();
-        boolean satisfiesConnectivity = netInfo != null && netInfo.isConnectedOrConnecting();
-        if (!satisfiesConnectivity) {
-            Log.i(TAG, suggestion.title + " is missing required connection.");
-        }
-        return satisfiesConnectivity;
-    }
-
-    public boolean isCategoryDone(String category) {
-        String name = Settings.Secure.COMPLETED_CATEGORY_PREFIX + category;
-        return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) != 0;
-    }
-
-    public void markCategoryDone(String category) {
-        String name = Settings.Secure.COMPLETED_CATEGORY_PREFIX + category;
-        Settings.Secure.putInt(mContext.getContentResolver(), name, 1);
-    }
-
-    /**
-     * Whether or not the category's exclusiveness has expired.
-     */
-    private boolean isExclusiveCategoryExpired(SuggestionCategory category) {
-        final String keySetupTime = category.category + SETUP_TIME;
-        final long currentTime = System.currentTimeMillis();
-        if (!mSharedPrefs.contains(keySetupTime)) {
-            mSharedPrefs.edit()
-                    .putLong(keySetupTime, currentTime)
-                    .commit();
-        }
-        if (category.exclusiveExpireDaysInMillis < 0) {
-            // negative means never expires
-            return false;
-        }
-        final long setupTime = mSharedPrefs.getLong(keySetupTime, 0);
-        final long elapsedTime = currentTime - setupTime;
-        Log.d(TAG, "Day " + elapsedTime / DateUtils.DAY_IN_MILLIS + " for " + category.category);
-        return elapsedTime > category.exclusiveExpireDaysInMillis;
-    }
-
-    @VisibleForTesting
-    boolean isDismissed(Tile suggestion, boolean isSmartSuggestionEnabled) {
-        String dismissControl = getDismissControl(suggestion, isSmartSuggestionEnabled);
-        String keyBase = suggestion.intent.getComponent().flattenToShortString();
-        if (!mSharedPrefs.contains(keyBase + SETUP_TIME)) {
-            mSharedPrefs.edit()
-                    .putLong(keyBase + SETUP_TIME, System.currentTimeMillis())
-                    .commit();
-        }
-        // Check if it's already manually dismissed
-        final boolean isDismissed = mSharedPrefs.getBoolean(keyBase + IS_DISMISSED, false);
-        if (isDismissed) {
-            return true;
-        }
-        if (dismissControl == null) {
-            return false;
-        }
-        // Parse when suggestion should first appear. return true to artificially hide suggestion
-        // before then.
-        int firstAppearDay = parseDismissString(dismissControl);
-        long firstAppearDayInMs = getEndTime(mSharedPrefs.getLong(keyBase + SETUP_TIME, 0),
-                firstAppearDay);
-        if (System.currentTimeMillis() >= firstAppearDayInMs) {
-            // Dismiss timeout has passed, undismiss it.
-            mSharedPrefs.edit()
-                    .putBoolean(keyBase + IS_DISMISSED, false)
-                    .commit();
-            return false;
-        }
-        return true;
-    }
-
-    private long getEndTime(long startTime, int daysDelay) {
-        long days = daysDelay * DateUtils.DAY_IN_MILLIS;
-        return startTime + days;
-    }
-
-    /**
-     * Parse the first int from a string formatted as "0,1,2..."
-     * The value means suggestion should first appear on Day X.
-     */
-    private int parseDismissString(String dismissControl) {
-        final String[] dismissStrs = dismissControl.split(",");
-        return Integer.parseInt(dismissStrs[0]);
-    }
-
-    private String getDismissControl(Tile suggestion, boolean isSmartSuggestionEnabled) {
-        if (isSmartSuggestionEnabled) {
-            return mDefaultDismissControl;
-        } else {
-            return suggestion.metaData.getString(META_DATA_DISMISS_CONTROL);
-        }
-    }
-
-    private static class SuggestionOrderInflater {
-        private static final String TAG_LIST = "optional-steps";
-        private static final String TAG_ITEM = "step";
-
-        private static final String ATTR_CATEGORY = "category";
-        private static final String ATTR_PACKAGE = "package";
-        private static final String ATTR_MULTIPLE = "multiple";
-        private static final String ATTR_EXCLUSIVE = "exclusive";
-        private static final String ATTR_EXCLUSIVE_EXPIRE_DAYS = "exclusiveExpireDays";
-
-        private final Context mContext;
-
-        public SuggestionOrderInflater(Context context) {
-            mContext = context;
-        }
-
-        public Object parse(int resource) {
-            XmlPullParser parser = mContext.getResources().getXml(resource);
-            final AttributeSet attrs = Xml.asAttributeSet(parser);
-            try {
-                // Look for the root node.
-                int type;
-                do {
-                    type = parser.next();
-                } while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT);
-
-                if (type != XmlPullParser.START_TAG) {
-                    throw new InflateException(parser.getPositionDescription()
-                            + ": No start tag found!");
-                }
-
-                // Temp is the root that was found in the xml
-                Object xmlRoot = onCreateItem(parser.getName(), attrs);
-
-                // Inflate all children under temp
-                rParse(parser, xmlRoot, attrs);
-                return xmlRoot;
-            } catch (XmlPullParserException | IOException e) {
-                Log.w(TAG, "Problem parser resource " + resource, e);
-                return null;
-            }
-        }
-
-        /**
-         * Recursive method used to descend down the xml hierarchy and instantiate
-         * items, instantiate their children.
-         */
-        private void rParse(XmlPullParser parser, Object parent, final AttributeSet attrs)
-                throws XmlPullParserException, IOException {
-            final int depth = parser.getDepth();
-
-            int type;
-            while (((type = parser.next()) != XmlPullParser.END_TAG ||
-                    parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
-                if (type != XmlPullParser.START_TAG) {
-                    continue;
-                }
-
-                final String name = parser.getName();
-
-                Object item = onCreateItem(name, attrs);
-                onAddChildItem(parent, item);
-                rParse(parser, item, attrs);
-            }
-        }
-
-        protected void onAddChildItem(Object parent, Object child) {
-            if (parent instanceof List<?> && child instanceof SuggestionCategory) {
-                ((List<SuggestionCategory>) parent).add((SuggestionCategory) child);
-            } else {
-                throw new IllegalArgumentException("Parent was not a list");
-            }
-        }
-
-        protected Object onCreateItem(String name, AttributeSet attrs) {
-            if (name.equals(TAG_LIST)) {
-                return new ArrayList<SuggestionCategory>();
-            } else if (name.equals(TAG_ITEM)) {
-                SuggestionCategory category = new SuggestionCategory();
-                category.category = attrs.getAttributeValue(null, ATTR_CATEGORY);
-                category.pkg = attrs.getAttributeValue(null, ATTR_PACKAGE);
-                String multiple = attrs.getAttributeValue(null, ATTR_MULTIPLE);
-                category.multiple = !TextUtils.isEmpty(multiple) && Boolean.parseBoolean(multiple);
-                String exclusive = attrs.getAttributeValue(null, ATTR_EXCLUSIVE);
-                category.exclusive =
-                        !TextUtils.isEmpty(exclusive) && Boolean.parseBoolean(exclusive);
-                String expireDaysAttr = attrs.getAttributeValue(null,
-                        ATTR_EXCLUSIVE_EXPIRE_DAYS);
-                long expireDays = !TextUtils.isEmpty(expireDaysAttr)
-                        ? Integer.parseInt(expireDaysAttr)
-                        : -1;
-                category.exclusiveExpireDaysInMillis = DateUtils.DAY_IN_MILLIS * expireDays;
-                return category;
-            } else {
-                throw new IllegalArgumentException("Unknown item " + name);
-            }
-        }
-    }
-}
-
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoaderCompat.java
new file mode 100644
index 0000000..916d7e3
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoaderCompat.java
@@ -0,0 +1,111 @@
+/*
+ * 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.utils;
+
+import android.content.Context;
+
+import androidx.loader.content.AsyncTaskLoader;
+
+/**
+ * This class fills in some boilerplate for AsyncTaskLoader to actually load things.
+ *
+ * Subclasses need to implement {@link AsyncLoaderCompat#loadInBackground()} to perform the actual
+ * background task, and {@link AsyncLoaderCompat#onDiscardResult(T)} to clean up previously loaded
+ * results.
+ *
+ * This loader is based on the MailAsyncTaskLoader from the AOSP EmailUnified repo.
+ *
+ * @param <T> the data type to be loaded.
+ */
+public abstract class AsyncLoaderCompat<T> extends AsyncTaskLoader<T> {
+    private T mResult;
+
+    public AsyncLoaderCompat(final Context context) {
+        super(context);
+    }
+
+    @Override
+    protected void onStartLoading() {
+        if (mResult != null) {
+            deliverResult(mResult);
+        }
+
+        if (takeContentChanged() || mResult == null) {
+            forceLoad();
+        }
+    }
+
+    @Override
+    protected void onStopLoading() {
+        cancelLoad();
+    }
+
+    @Override
+    public void deliverResult(final T data) {
+        if (isReset()) {
+            if (data != null) {
+                onDiscardResult(data);
+            }
+            return;
+        }
+
+        final T oldResult = mResult;
+        mResult = data;
+
+        if (isStarted()) {
+            super.deliverResult(data);
+        }
+
+        if (oldResult != null && oldResult != mResult) {
+            onDiscardResult(oldResult);
+        }
+    }
+
+    @Override
+    protected void onReset() {
+        super.onReset();
+
+        onStopLoading();
+
+        if (mResult != null) {
+            onDiscardResult(mResult);
+        }
+        mResult = null;
+    }
+
+    @Override
+    public void onCanceled(final T data) {
+        super.onCanceled(data);
+
+        if (data != null) {
+            onDiscardResult(data);
+        }
+    }
+
+    /**
+     * Called when discarding the load results so subclasses can take care of clean-up or
+     * recycling tasks. This is not called if the same result (by way of pointer equality) is
+     * returned again by a subsequent call to loadInBackground, or if result is null.
+     *
+     * Note that this may be called concurrently with loadInBackground(), and in some circumstances
+     * may be called more than once for a given object.
+     *
+     * @param result The value returned from {@link AsyncLoaderCompat#loadInBackground()} which
+     *               is to be discarded.
+     */
+    protected abstract void onDiscardResult(T result);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreferenceMixinCompat.java b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreferenceMixinCompat.java
new file mode 100644
index 0000000..260ac83
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreferenceMixinCompat.java
@@ -0,0 +1,72 @@
+/*
+ * 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 com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.SetPreferenceScreen;
+
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+
+public class FooterPreferenceMixinCompat implements LifecycleObserver, SetPreferenceScreen {
+
+    private final PreferenceFragmentCompat mFragment;
+    private FooterPreference mFooterPreference;
+
+    public FooterPreferenceMixinCompat(PreferenceFragmentCompat fragment, Lifecycle lifecycle) {
+        mFragment = fragment;
+        lifecycle.addObserver(this);
+    }
+
+    @Override
+    public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
+        if (mFooterPreference != null) {
+            preferenceScreen.addPreference(mFooterPreference);
+        }
+    }
+
+    /**
+     * Creates a new {@link FooterPreference}.
+     */
+    public FooterPreference createFooterPreference() {
+        final PreferenceScreen screen = mFragment.getPreferenceScreen();
+        if (mFooterPreference != null && screen != null) {
+            screen.removePreference(mFooterPreference);
+        }
+        mFooterPreference = new FooterPreference(getPrefContext());
+
+        if (screen != null) {
+            screen.addPreference(mFooterPreference);
+        }
+        return mFooterPreference;
+    }
+
+    /**
+     * Returns an UI context with theme properly set for new Preference objects.
+     */
+    private Context getPrefContext() {
+        return mFragment.getPreferenceManager().getContext();
+    }
+
+    public boolean hasFooter() {
+        return mFooterPreference != null;
+    }
+}
+
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index b9c7601..18a44ed 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -756,10 +756,7 @@
     }
 
     public CharSequence getSsid() {
-        final SpannableString str = new SpannableString(ssid);
-        str.setSpan(new TtsSpan.TelephoneBuilder(ssid).build(), 0, ssid.length(),
-                Spannable.SPAN_INCLUSIVE_INCLUSIVE);
-        return str;
+        return ssid;
     }
 
     public String getConfigName() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java
index 159b2a1..19e3808 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java
@@ -55,8 +55,10 @@
         try {
             List<PasspointConfiguration> savedPasspointConfigs =
                     wifiManager.getPasspointConfigurations();
-            for (PasspointConfiguration config : savedPasspointConfigs) {
-                savedConfigs.add(new AccessPoint(context, config));
+            if (savedPasspointConfigs != null) {
+                for (PasspointConfiguration config : savedPasspointConfigs) {
+                    savedConfigs.add(new AccessPoint(context, config));
+                }
             }
         } catch (UnsupportedOperationException e) {
             // Passpoint not supported.
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/ProfileSelectDialogTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/ProfileSelectDialogTest.java
deleted file mode 100644
index ac2d759..0000000
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/ProfileSelectDialogTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.drawer;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.UserInfo;
-import android.os.UserHandle;
-import android.os.UserManager;
-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 org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import static junit.framework.Assert.assertEquals;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class ProfileSelectDialogTest {
-
-    @Mock
-    private Context mContext;
-    @Mock
-    private UserManager mUserManager;
-    private static final UserHandle NORMAL_USER = UserHandle.of(1111);
-    private static final UserHandle REMOVED_USER = UserHandle.of(2222);
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
-        final UserInfo userInfo = new UserInfo(
-                NORMAL_USER.getIdentifier(), "test_user", UserInfo.FLAG_RESTRICTED);
-        when(mUserManager.getUserInfo(NORMAL_USER.getIdentifier())).thenReturn(userInfo);
-    }
-
-    @Test
-    public void testUpdateUserHandlesIfNeeded_Normal() {
-        final Tile tile = new Tile();
-        tile.intent = new Intent();
-        tile.userHandle.add(NORMAL_USER);
-
-        ProfileSelectDialog.updateUserHandlesIfNeeded(mContext, tile);
-
-        assertEquals(tile.userHandle.size(), 1);
-        assertEquals(tile.userHandle.get(0).getIdentifier(), NORMAL_USER.getIdentifier());
-        verify(mUserManager, never()).getUserInfo(NORMAL_USER.getIdentifier());
-    }
-
-    @Test
-    public void testUpdateUserHandlesIfNeeded_Remove() {
-        final Tile tile = new Tile();
-        tile.intent = new Intent();
-        tile.userHandle.add(REMOVED_USER);
-        tile.userHandle.add(NORMAL_USER);
-        tile.userHandle.add(REMOVED_USER);
-
-        ProfileSelectDialog.updateUserHandlesIfNeeded(mContext, tile);
-
-        assertEquals(tile.userHandle.size(), 1);
-        assertEquals(tile.userHandle.get(0).getIdentifier(), NORMAL_USER.getIdentifier());
-        verify(mUserManager, times(1)).getUserInfo(NORMAL_USER.getIdentifier());
-        verify(mUserManager, times(2)).getUserInfo(REMOVED_USER.getIdentifier());
-    }
-}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
deleted file mode 100644
index 2f417ad..0000000
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.settingslib.drawer;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
-import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
-
-import android.app.Instrumentation;
-import android.content.Intent;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class SettingsDrawerActivityTest {
-
-    @Rule
-    public ActivityTestRule<TestActivity> mActivityRule =
-            new ActivityTestRule<>(TestActivity.class, true, true);
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void startActivity_doNotShowNavUp() {
-        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        final Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        instrumentation.startActivitySync(intent);
-
-        onView(withContentDescription(com.android.internal.R.string.action_bar_up_description))
-                .check(doesNotExist());
-    }
-
-    /**
-     * Test Activity in this test.
-     *
-     * Use this activity because SettingsDrawerActivity hasn't been registered in its
-     * AndroidManifest.xml
-     */
-    public static class TestActivity extends SettingsDrawerActivity {
-    }
-}
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 e435a72..0324799 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
@@ -104,18 +104,13 @@
     }
 
     @Test
-    public void testSsidIsTelephoneSpan() {
+    public void testSsidIsSpannableString_returnFalse() {
         final Bundle bundle = new Bundle();
         bundle.putString("key_ssid", TEST_SSID);
         final AccessPoint ap = new AccessPoint(InstrumentationRegistry.getTargetContext(), bundle);
         final CharSequence ssid = ap.getSsid();
 
-        assertThat(ssid instanceof SpannableString).isTrue();
-
-        TtsSpan[] spans = ((SpannableString) ssid).getSpans(0, TEST_SSID.length(), TtsSpan.class);
-
-        assertThat(spans.length).isEqualTo(1);
-        assertThat(spans[0].getType()).isEqualTo(TtsSpan.TYPE_TELEPHONE);
+        assertThat(ssid instanceof SpannableString).isFalse();
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/res/xml/suggestion_ordering.xml b/packages/SettingsLib/tests/robotests/res/xml/suggestion_ordering.xml
deleted file mode 100644
index f02ac15..0000000
--- a/packages/SettingsLib/tests/robotests/res/xml/suggestion_ordering.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.
--->
-
-<optional-steps>
-    <step category="com.android.settings.suggested.category.DEFERRED_SETUP"
-        exclusive="true" />
-    <step category="com.android.settings.suggested.category.LOCK_SCREEN" />
-    <step category="com.android.settings.suggested.category.TRUST_AGENT" />
-    <step category="com.android.settings.suggested.category.EMAIL" />
-    <step category="com.android.settings.suggested.category.PARTNER_ACCOUNT"
-        multiple="true" />
-    <step category="com.android.settings.suggested.category.GESTURE" />
-    <step category="com.android.settings.suggested.category.HOTWORD" />
-    <step category="com.android.settings.suggested.category.DEFAULT"
-        multiple="true" />
-    <step category="com.android.settings.suggested.category.SETTINGS_ONLY"
-        multiple="true" />
-</optional-steps>
diff --git a/packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecConfig.java b/packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecConfig.java
deleted file mode 100644
index 14b0d59..0000000
--- a/packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecConfig.java
+++ /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.
- */
-
-package android.bluetooth;
-
-/**
- * A placeholder class to prevent ClassNotFound exceptions caused by lack of visibility.
- */
-public class BluetoothCodecConfig {
-    public boolean isMandatoryCodec() { return true; }
-    public String getCodecName() { return null; }
-    public int getCodecType() { return -1; }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecStatus.java b/packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecStatus.java
deleted file mode 100644
index 919ec3f..0000000
--- a/packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecStatus.java
+++ /dev/null
@@ -1,25 +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 android.bluetooth;
-
-/**
- * A placeholder class to prevent ClassNotFound exceptions caused by lack of visibility.
- */
-public class BluetoothCodecStatus {
-    public BluetoothCodecConfig getCodecConfig() { return null; }
-    public BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return null; }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
new file mode 100644
index 0000000..9ba9967
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.EditText;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class CustomEditTextPreferenceComaptTest {
+
+    @Mock
+    private View mView;
+
+    private TestPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mPreference = new TestPreference(RuntimeEnvironment.application);
+    }
+
+    @Test
+    public void bindDialogView_shouldRequestFocus() {
+        final String testText = "";
+        final EditText editText = spy(new EditText(RuntimeEnvironment.application));
+        editText.setText(testText);
+        when(mView.findViewById(android.R.id.edit)).thenReturn(editText);
+
+        mPreference.onBindDialogView(mView);
+
+        verify(editText).requestFocus();
+    }
+
+    @Test
+    public void getEditText_noDialog_shouldNotCrash() {
+        ReflectionHelpers.setField(mPreference, "mFragment",
+                mock(CustomEditTextPreferenceCompat.CustomPreferenceDialogFragment.class));
+
+        mPreference.getEditText();
+
+        // no crash
+    }
+
+    private static class TestPreference extends CustomEditTextPreferenceCompat {
+        public TestPreference(Context context) {
+            super(context);
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
index e153c3e..7553313 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
@@ -17,6 +17,7 @@
 package com.android.settingslib;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.mock;
@@ -43,13 +44,12 @@
 import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 /**
  * Tests for {@link HelpUtils}.
  */
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class HelpUtilsTest {
     private static final String TEST_HELP_URL = "intent:#Intent;action=com.android.test;end";
     private static final String PACKAGE_NAME_KEY = "package-name-key";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
new file mode 100644
index 0000000..152d024
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class AccessibilityUtilsTest {
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+    }
+
+    @Test
+    public void getEnabledServicesFromSettings_noService_emptyResult() {
+        assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext)).isEmpty();
+    }
+
+    @Test
+    public void getEnabledServicesFromSettings_badFormat_emptyResult() {
+        Settings.Secure.putStringForUser(
+                mContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                ":",
+                UserHandle.myUserId());
+
+        assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext)).isEmpty();
+    }
+
+    @Test
+    public void getEnabledServicesFromSettings_1Service_1result() {
+        final ComponentName cn = new ComponentName("pkg", "serv");
+        Settings.Secure.putStringForUser(
+                mContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                cn.flattenToString() + ":",
+                UserHandle.myUserId());
+
+        assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext))
+                .containsExactly(cn);
+    }
+
+    @Test
+    public void getEnabledServicesFromSettings_2Services_2results() {
+        final ComponentName cn1 = new ComponentName("pkg", "serv");
+        final ComponentName cn2 = new ComponentName("pkg", "serv2");
+        Settings.Secure.putStringForUser(
+                mContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                cn1.flattenToString() + ":" + cn2.flattenToString(),
+                UserHandle.myUserId());
+
+        assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext))
+                .containsExactly(cn1, cn2);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index ef13a5f..b2ab45c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -16,6 +16,7 @@
 package com.android.settingslib.bluetooth;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
@@ -31,23 +32,29 @@
 import android.content.res.Resources;
 
 import com.android.settingslib.R;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 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;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class A2dpProfileTest {
 
-    @Mock Context mContext;
-    @Mock LocalBluetoothAdapter mAdapter;
-    @Mock CachedBluetoothDeviceManager mDeviceManager;
-    @Mock LocalBluetoothProfileManager mProfileManager;
-    @Mock BluetoothDevice mDevice;
-    @Mock BluetoothA2dp mBluetoothA2dp;
+    @Mock
+    Context mContext;
+    @Mock
+    LocalBluetoothAdapter mAdapter;
+    @Mock
+    CachedBluetoothDeviceManager mDeviceManager;
+    @Mock
+    LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    BluetoothDevice mDevice;
+    @Mock
+    BluetoothA2dp mBluetoothA2dp;
     BluetoothProfile.ServiceListener mServiceListener;
 
     A2dpProfile mProfile;
@@ -126,7 +133,7 @@
     private static String KNOWN_CODEC_LABEL = "Use high quality audio: %1$s";
     private static String UNKNOWN_CODEC_LABEL = "Use high quality audio";
     private static String[] CODEC_NAMES =
-            new String[] { "Default", "SBC", "AAC", "aptX", "aptX HD", "LDAC" };
+            new String[]{"Default", "SBC", "AAC", "aptX", "aptX HD", "LDAC"};
 
     /**
      * Helper for setting up several tests of getHighQualityAudioOptionLabel
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 1080690..5f42b66 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
@@ -15,26 +15,24 @@
  */
 package com.android.settingslib.bluetooth;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.verify;
 
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.Intent;
-
 import android.telephony.TelephonyManager;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 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;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class BluetoothEventManagerTest {
 
     @Mock
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 16ed85c..b33e9c3 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
@@ -17,12 +17,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doAnswer;
-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.bluetooth.BluetoothAdapter;
@@ -31,20 +27,18 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 
-import com.android.settingslib.R;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 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 java.util.Collection;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class CachedBluetoothDeviceManagerTest {
     private final static String DEVICE_NAME_1 = "TestName_1";
     private final static String DEVICE_NAME_2 = "TestName_2";
@@ -115,6 +109,7 @@
         when(mDevice3.getBluetoothClass()).thenReturn(DEVICE_CLASS_2);
 
         when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
+        when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
         when(mLocalAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON);
         when(mHfpProfile.isProfileReady()).thenReturn(true);
         when(mA2dpProfile.isProfileReady()).thenReturn(true);
@@ -135,10 +130,10 @@
     @Test
     public void testAddDevice_validCachedDevices_devicesAdded() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-                mLocalProfileManager, mDevice1);
+                mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
-                mLocalProfileManager, mDevice2);
+                mDevice2);
         assertThat(cachedDevice2).isNotNull();
 
         Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
@@ -155,7 +150,7 @@
     @Test
     public void testGetName_validCachedDevice_nameFound() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-                mLocalProfileManager, mDevice1);
+                mDevice1);
         assertThat(cachedDevice1).isNotNull();
         assertThat(mCachedDeviceManager.getName(mDevice1)).isEqualTo(DEVICE_ALIAS_1);
     }
@@ -166,7 +161,7 @@
     @Test
     public void testOnDeviceNameUpdated_validName_nameUpdated() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-                mLocalProfileManager, mDevice1);
+                mDevice1);
         assertThat(cachedDevice1).isNotNull();
         assertThat(cachedDevice1.getName()).isEqualTo(DEVICE_ALIAS_1);
 
@@ -182,10 +177,10 @@
     @Test
     public void testClearNonBondedDevices_bondedAndNonBondedDevices_nonBondedDevicesCleared() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-                mLocalProfileManager, mDevice1);
+                mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
-                mLocalProfileManager, mDevice2);
+                mDevice2);
         assertThat(cachedDevice2).isNotNull();
 
         when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
@@ -237,10 +232,10 @@
     @Test
     public void testOnHiSyncIdChanged_sameHiSyncId_populateInDifferentLists() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice1);
+            mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice2);
+            mDevice2);
         assertThat(cachedDevice2).isNotNull();
 
         // Since both devices do not have hiSyncId, they should be added in mCachedDevices.
@@ -272,10 +267,10 @@
     @Test
     public void testOnHiSyncIdChanged_sameHiSyncIdAndOneConnected_chooseConnectedDevice() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice1);
+            mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice2);
+            mDevice2);
         assertThat(cachedDevice2).isNotNull();
         cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
         cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
@@ -309,10 +304,10 @@
     @Test
     public void testOnHiSyncIdChanged_differentHiSyncId_populateInSameList() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice1);
+            mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice2);
+            mDevice2);
         assertThat(cachedDevice2).isNotNull();
 
         // Since both devices do not have hiSyncId, they should be added in mCachedDevices.
@@ -345,7 +340,7 @@
     @Test
     public void testOnProfileConnectionStateChanged_singleDeviceConnected_visible() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice1);
+            mDevice1);
         assertThat(cachedDevice1).isNotNull();
         cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
 
@@ -383,10 +378,10 @@
     @Test
     public void testOnProfileConnectionStateChanged_twoDevicesConnected_oneDeviceVisible() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice1);
+            mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice2);
+            mDevice2);
         assertThat(cachedDevice2).isNotNull();
         cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
         cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
@@ -437,10 +432,10 @@
     @Test
     public void testOnProfileConnectionStateChanged_twoDevicesDisconnected_oneDeviceVisible() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice1);
+            mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice2);
+            mDevice2);
         assertThat(cachedDevice2).isNotNull();
         cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
         cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
@@ -492,10 +487,10 @@
     @Test
     public void testOnDeviceUnpaired_bothHearingAidsPaired_removesItsPairFromList() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice1);
+            mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice2);
+            mDevice2);
         assertThat(cachedDevice2).isNotNull();
 
         cachedDevice1.setHiSyncId(HISYNCID1);
@@ -524,13 +519,13 @@
     @Test
     public void testOnDeviceUnpaired_bothHearingAidsNotPaired_doesNotRemoveAnyDeviceFromList() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice1);
+            mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice2);
+            mDevice2);
         assertThat(cachedDevice2).isNotNull();
         CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice3);
+            mDevice3);
         assertThat(cachedDevice2).isNotNull();
 
         cachedDevice1.setHiSyncId(HISYNCID1);
@@ -576,7 +571,7 @@
         doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
 
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice1);
+            mDevice1);
         assertThat(cachedDevice1).isNotNull();
         // The first hearing aid device should be populated in mCachedDevice and
         // mCachedDevicesMapForHearingAids.
@@ -587,7 +582,7 @@
             .contains(cachedDevice1);
 
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice2);
+            mDevice2);
         assertThat(cachedDevice2).isNotNull();
         // The second hearing aid device should be populated in mHearingAidDevicesNotAddedInCache.
         assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
@@ -605,7 +600,7 @@
         doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
         doAnswer((invocation) -> HISYNCID2).when(mHearingAidProfile).getHiSyncId(mDevice2);
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice1);
+            mDevice1);
         assertThat(cachedDevice1).isNotNull();
         // The first hearing aid device should be populated in mCachedDevice and
         // mCachedDevicesMapForHearingAids.
@@ -616,7 +611,7 @@
             .contains(cachedDevice1);
 
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
-            mLocalProfileManager, mDevice2);
+            mDevice2);
         assertThat(cachedDevice2).isNotNull();
         // The second hearing aid device should also be populated in mCachedDevice
         // and mCachedDevicesMapForHearingAids as its not a pair of the first one.
@@ -686,7 +681,7 @@
     @Test
     public void testOnBtClassChanged_validBtClass_classChanged() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-                mLocalProfileManager, mDevice1);
+                mDevice1);
         assertThat(cachedDevice1).isNotNull();
         assertThat(cachedDevice1.getBtClass()).isEqualTo(DEVICE_CLASS_1);
 
@@ -702,7 +697,7 @@
     @Test
     public void testOnDeviceDisappeared_deviceBondedUnbonded_unbondedDeviceDisappeared() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-                mLocalProfileManager, mDevice1);
+                mDevice1);
         assertThat(cachedDevice1).isNotNull();
 
         when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
@@ -718,10 +713,10 @@
     @Test
     public void testOnActiveDeviceChanged_connectedDevices_activeDeviceChanged() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-                mLocalProfileManager, mDevice1);
+                mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
-                mLocalProfileManager, mDevice2);
+                mDevice2);
         assertThat(cachedDevice2).isNotNull();
 
         when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
@@ -783,10 +778,10 @@
     @Test
     public void testOnActiveDeviceChanged_withA2dpAndHearingAid() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
-                mLocalProfileManager, mDevice1);
+                mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
-                mLocalProfileManager, mDevice2);
+                mDevice2);
         assertThat(cachedDevice2).isNotNull();
 
         when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 927a94f..c39fb85 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -572,4 +572,43 @@
 
         assertThat(mCachedDevice.isHfpDevice()).isFalse();
     }
+
+    @Test
+    public void isConnectedHearingAidDevice_connected_returnTrue() {
+        when(mProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
+        when(mHearingAidProfile.getConnectionStatus(mDevice)).
+                thenReturn(BluetoothProfile.STATE_CONNECTED);
+
+        assertThat(mCachedDevice.isConnectedHearingAidDevice()).isTrue();
+    }
+
+    @Test
+    public void isConnectedHearingAidDevice_disconnected_returnFalse() {
+        when(mProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
+        when(mHearingAidProfile.getConnectionStatus(mDevice)).
+                thenReturn(BluetoothProfile.STATE_DISCONNECTED);
+
+        assertThat(mCachedDevice.isConnectedHearingAidDevice()).isFalse();
+    }
+
+    @Test
+    public void isConnectedHfpDevice_profileIsNull_returnFalse() {
+        when(mProfileManager.getHeadsetProfile()).thenReturn(null);
+
+        assertThat(mCachedDevice.isHfpDevice()).isFalse();
+    }
+
+    @Test
+    public void isConnectedA2dpDevice_profileIsNull_returnFalse() {
+        when(mProfileManager.getA2dpProfile()).thenReturn(null);
+
+        assertThat(mCachedDevice.isA2dpDevice()).isFalse();
+    }
+
+    @Test
+    public void isConnectedHearingAidDevice_profileIsNull_returnFalse() {
+        when(mProfileManager.getHearingAidProfile()).thenReturn(null);
+
+        assertThat(mCachedDevice.isConnectedHearingAidDevice()).isFalse();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
index 03b023b..bc8be4d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
@@ -13,15 +13,16 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 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;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class HeadsetProfileTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index d342bc8..af66f7a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -37,20 +37,19 @@
 import android.content.Intent;
 import android.os.ParcelUuid;
 
-import java.util.ArrayList;
-import java.util.List;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 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;
 
-@RunWith(RobolectricTestRunner.class)
-@Config(resourceDir = "../../res")
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class LocalBluetoothProfileManagerTest {
     @Mock
     private CachedBluetoothDeviceManager mDeviceManager;
@@ -82,6 +81,10 @@
      */
     @Test
     public void constructor_initiateHidAndHidDeviceProfile() {
+        when(mAdapter.getSupportedProfiles()).thenReturn(
+                generateList(new int[] {BluetoothProfile.HID_HOST}));
+        when(mAdapter.getSupportedProfiles()).thenReturn(
+                generateList(new int[] {BluetoothProfile.HID_HOST, BluetoothProfile.HID_DEVICE}));
         mProfileManager =
                 new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager, mEventManager);
 
@@ -96,12 +99,12 @@
     public void updateLocalProfiles_addA2dpToLocalProfiles() {
         mProfileManager =
                 new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager, mEventManager);
-        when(mAdapter.getUuids()).thenReturn(new ParcelUuid[]{BluetoothUuid.AudioSource});
         assertThat(mProfileManager.getA2dpProfile()).isNull();
         assertThat(mProfileManager.getHeadsetProfile()).isNull();
 
-        ParcelUuid[] uuids = mAdapter.getUuids();
-        mProfileManager.updateLocalProfiles(uuids);
+        when(mAdapter.getSupportedProfiles()).thenReturn(generateList(
+                new int[] {BluetoothProfile.A2DP}));
+        mProfileManager.updateLocalProfiles();
 
         assertThat(mProfileManager.getA2dpProfile()).isNotNull();
         assertThat(mProfileManager.getHeadsetProfile()).isNull();
@@ -112,6 +115,8 @@
      */
     @Test
     public void updateProfiles_addHidProfileForRemoteDevice() {
+        when(mAdapter.getSupportedProfiles()).thenReturn(generateList(
+                new int[] {BluetoothProfile.HID_HOST}));
         mProfileManager =
                 new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager, mEventManager);
         ParcelUuid[] uuids = new ParcelUuid[]{BluetoothUuid.Hid};
@@ -133,7 +138,8 @@
      */
     @Test
     public void stateChangedHandler_receiveA2dpConnectionStateChanged_shouldDispatchCallback() {
-        when(mAdapter.getUuids()).thenReturn(new ParcelUuid[]{BluetoothUuid.AudioSource});
+        when(mAdapter.getSupportedProfiles()).thenReturn(generateList(
+                new int[] {BluetoothProfile.A2DP}));
         mProfileManager = new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager,
                 mEventManager);
         // Refer to BluetoothControllerImpl, it will call setReceiverHandler after
@@ -156,7 +162,8 @@
      */
     @Test
     public void stateChangedHandler_receiveHeadsetConnectionStateChanged_shouldDispatchCallback() {
-        when(mAdapter.getUuids()).thenReturn(new ParcelUuid[]{BluetoothUuid.Handsfree_AG});
+        when(mAdapter.getSupportedProfiles()).thenReturn(generateList(
+                new int[] {BluetoothProfile.HEADSET}));
         mProfileManager = new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager,
                 mEventManager);
         // Refer to BluetoothControllerImpl, it will call setReceiverHandler after
@@ -205,7 +212,8 @@
      */
     @Test
     public void stateChangedHandler_receivePanConnectionStateChanged_shouldNotDispatchCallback() {
-        when(mAdapter.getUuids()).thenReturn(new ParcelUuid[]{BluetoothUuid.AudioSource});
+        when(mAdapter.getSupportedProfiles()).thenReturn(
+                generateList(new int[] {BluetoothProfile.PAN}));
         mProfileManager = new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager,
                 mEventManager);
         // Refer to BluetoothControllerImpl, it will call setReceiverHandler after
@@ -227,8 +235,32 @@
      * handler and refresh CachedBluetoothDevice
      */
     @Test
-    public void stateChangedHandler_receivePanConnectionStateChangedWithoutUuid_shouldNotRefresh() {
-        when(mAdapter.getUuids()).thenReturn(null);
+    public void stateChangedHandler_receivePanConnectionStateChangedWithoutProfile_shouldNotRefresh
+    () {
+        when(mAdapter.getSupportedProfiles()).thenReturn(null);
+        mProfileManager = new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager,
+                mEventManager);
+        // Refer to BluetoothControllerImpl, it will call setReceiverHandler after
+        // LocalBluetoothProfileManager created.
+        mEventManager.setReceiverHandler(null);
+        mIntent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
+        mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING);
+        mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mCachedBluetoothDevice, never()).refresh();
+    }
+
+    /**
+     * Verify BluetoothPan.ACTION_CONNECTION_STATE_CHANGED intent with uuids will dispatch to
+     * handler and refresh CachedBluetoothDevice
+     */
+    @Test
+    public void stateChangedHandler_receivePanConnectionStateChangedWithProfile_shouldRefresh() {
+        when(mAdapter.getSupportedProfiles()).thenReturn(generateList(
+                new int[] {BluetoothProfile.PAN}));
         mProfileManager = new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager,
                 mEventManager);
         // Refer to BluetoothControllerImpl, it will call setReceiverHandler after
@@ -244,25 +276,14 @@
         verify(mCachedBluetoothDevice).refresh();
     }
 
-    /**
-     * Verify BluetoothPan.ACTION_CONNECTION_STATE_CHANGED intent with uuids will dispatch to
-     * handler and refresh CachedBluetoothDevice
-     */
-    @Test
-    public void stateChangedHandler_receivePanConnectionStateChangedWithUuids_shouldRefresh() {
-        when(mAdapter.getUuids()).thenReturn(new ParcelUuid[]{BluetoothUuid.AudioSource});
-        mProfileManager = new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager,
-                mEventManager);
-        // Refer to BluetoothControllerImpl, it will call setReceiverHandler after
-        // LocalBluetoothProfileManager created.
-        mEventManager.setReceiverHandler(null);
-        mIntent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
-        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
-        mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING);
-        mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
-
-        mContext.sendBroadcast(mIntent);
-
-        verify(mCachedBluetoothDevice).refresh();
+    private List<Integer> generateList(int[] profile) {
+        if (profile == null) {
+            return null;
+        }
+        final List<Integer> profileList = new ArrayList<>(profile.length);
+        for(int i = 0; i < profile.length; i++) {
+            profileList.add(profile[i]);
+        }
+        return profileList;
     }
 }
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 5261ea0..4d7553c 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
@@ -16,21 +16,24 @@
 package com.android.settingslib.core;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 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;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class AbstractPreferenceControllerTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
index 52068e9..28828a0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
@@ -18,11 +18,12 @@
 import static androidx.lifecycle.Lifecycle.Event.ON_START;
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.lifecycle.LifecycleOwner;
 import android.content.Context;
+import android.os.Bundle;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
+import android.widget.LinearLayout;
 
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.events.OnAttach;
@@ -34,13 +35,15 @@
 import com.android.settingslib.core.lifecycle.events.OnResume;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.testutils.FragmentTestUtils;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
 import org.robolectric.android.controller.ActivityController;
-import org.robolectric.android.controller.FragmentController;
+
+import androidx.lifecycle.LifecycleOwner;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
 public class LifecycleTest {
@@ -64,7 +67,7 @@
 
         public TestFragment() {
             mFragObserver = new TestObserver();
-            getLifecycle().addObserver(mFragObserver);
+            getSettingsLifecycle().addObserver(mFragObserver);
         }
     }
 
@@ -74,9 +77,17 @@
 
         public TestActivity() {
             mActObserver = new TestObserver();
-            getLifecycle().addObserver(mActObserver);
+            getSettingsLifecycle().addObserver(mActObserver);
         }
 
+        @Override
+        protected void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            LinearLayout view = new LinearLayout(this);
+            view.setId(1);
+
+            setContentView(view);
+        }
     }
 
     public static class TestObserver implements LifecycleObserver, OnAttach, OnStart, OnResume,
@@ -151,11 +162,9 @@
     @Test
     public void runThroughActivityLifecycles_shouldObserveEverything() {
         ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class);
-        TestActivity activity = ac.get();
+        TestActivity activity = ac.setup().get();
 
-        ac.start();
         assertThat(activity.mActObserver.mOnStartObserved).isTrue();
-        ac.resume();
         assertThat(activity.mActObserver.mOnResumeObserved).isTrue();
         activity.onCreateOptionsMenu(null);
         assertThat(activity.mActObserver.mOnCreateOptionsMenuObserved).isTrue();
@@ -173,50 +182,50 @@
 
     @Test
     public void runThroughDialogFragmentLifecycles_shouldObserveEverything() {
-        FragmentController<TestDialogFragment> fragmentController =
-                Robolectric.buildFragment(TestDialogFragment.class);
-        TestDialogFragment fragment = fragmentController.get();
+        final TestDialogFragment fragment = new TestDialogFragment();
+        FragmentTestUtils.startFragment(fragment);
 
-        fragmentController.create().start().resume();
         fragment.onCreateOptionsMenu(null, null);
         fragment.onPrepareOptionsMenu(null);
         fragment.onOptionsItemSelected(null);
-        fragmentController.pause().stop().destroy();
+        assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue();
+        assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue();
+        assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue();
 
         assertThat(fragment.mFragObserver.mOnAttachObserved).isTrue();
         assertThat(fragment.mFragObserver.mOnAttachHasContext).isTrue();
         assertThat(fragment.mFragObserver.mOnStartObserved).isTrue();
         assertThat(fragment.mFragObserver.mOnResumeObserved).isTrue();
+        fragment.onPause();
         assertThat(fragment.mFragObserver.mOnPauseObserved).isTrue();
+        fragment.onStop();
         assertThat(fragment.mFragObserver.mOnStopObserved).isTrue();
+        fragment.onDestroy();
         assertThat(fragment.mFragObserver.mOnDestroyObserved).isTrue();
-        assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue();
-        assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue();
-        assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue();
     }
 
     @Test
     public void runThroughFragmentLifecycles_shouldObserveEverything() {
-        FragmentController<TestFragment> fragmentController =
-                Robolectric.buildFragment(TestFragment.class);
-        TestFragment fragment = fragmentController.get();
+        final TestFragment fragment = new TestFragment();
+        FragmentTestUtils.startFragment(fragment);
 
-        fragmentController.create().start().resume();
         fragment.onCreateOptionsMenu(null, null);
         fragment.onPrepareOptionsMenu(null);
         fragment.onOptionsItemSelected(null);
-        fragmentController.pause().stop().destroy();
+        assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue();
+        assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue();
+        assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue();
 
         assertThat(fragment.mFragObserver.mOnAttachObserved).isTrue();
         assertThat(fragment.mFragObserver.mOnAttachHasContext).isTrue();
         assertThat(fragment.mFragObserver.mOnStartObserved).isTrue();
         assertThat(fragment.mFragObserver.mOnResumeObserved).isTrue();
+        fragment.onPause();
         assertThat(fragment.mFragObserver.mOnPauseObserved).isTrue();
+        fragment.onStop();
         assertThat(fragment.mFragObserver.mOnStopObserved).isTrue();
+        fragment.onDestroy();
         assertThat(fragment.mFragObserver.mOnDestroyObserved).isTrue();
-        assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue();
-        assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue();
-        assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue();
     }
 
     @Test
@@ -237,17 +246,16 @@
 
     @Test
     public void onOptionItemSelectedShortCircuitsIfAnObserverHandlesTheMenuItem() {
-        FragmentController<TestFragment> fragmentController =
-                Robolectric.buildFragment(TestFragment.class);
-        TestFragment fragment = fragmentController.get();
-        OptionItemAccepter accepter = new OptionItemAccepter();
+        final TestFragment fragment = new TestFragment();
+        FragmentTestUtils.startFragment(fragment);
+
+        final OptionItemAccepter accepter = new OptionItemAccepter();
         fragment.getLifecycle().addObserver(accepter);
 
-        fragmentController.create().start().resume();
+
         fragment.onCreateOptionsMenu(null, null);
         fragment.onPrepareOptionsMenu(null);
         fragment.onOptionsItemSelected(null);
-        fragmentController.pause().stop().destroy();
 
         assertThat(accepter.wasCalled).isFalse();
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
index ba955f9..20ce465 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
@@ -63,7 +63,8 @@
         uptimePreferenceController.displayPreference(mScreen);
 
         // SystemClock is shadowed so it shouldn't advance unexpectedly while the test is running
-        verify(mPreference).setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime()));
+        verify(mPreference).setSummary(
+                DateUtils.formatElapsedTime(SystemClock.elapsedRealtime() / 1000));
     }
 
     @Test
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 ddbcb87..757df5b 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
@@ -16,11 +16,10 @@
 
 package com.android.settingslib.deviceinfo;
 
+import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.spy;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -40,6 +39,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 
 import java.util.Arrays;
 import java.util.List;
@@ -48,12 +48,16 @@
 @RunWith(SettingsLibRobolectricTestRunner.class)
 public class WifiMacAddressPreferenceControllerTest {
     @Mock
-    private Context mContext;
-    @Mock
     private Lifecycle mLifecycle;
     @Mock
     private PreferenceScreen mScreen;
     @Mock
+    private WifiManager mWifiManager;
+    @Mock
+    private WifiInfo mWifiInfo;
+
+    private AbstractWifiMacAddressPreferenceController mController;
+    private Context mContext;
     private Preference mPreference;
 
     private static final String TEST_MAC_ADDRESS = "00:11:22:33:44:55";
@@ -61,14 +65,20 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+
+        mContext = spy(RuntimeEnvironment.application);
+        mPreference = new Preference(mContext);
+
         doReturn(mPreference).when(mScreen)
                 .findPreference(AbstractWifiMacAddressPreferenceController.KEY_WIFI_MAC_ADDRESS);
+        doReturn(mWifiManager).when(mContext).getSystemService(WifiManager.class);
+        doReturn(mWifiInfo).when(mWifiManager).getConnectionInfo();
+
+        mController = new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle);
     }
 
     @Test
     public void testHasIntentFilters() {
-        final AbstractWifiMacAddressPreferenceController wifiMacAddressPreferenceController =
-                new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle);
         final List<String> expectedIntents = Arrays.asList(
                 ConnectivityManager.CONNECTIVITY_ACTION,
                 WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
@@ -76,37 +86,110 @@
 
 
         assertWithMessage("Intent filter should contain expected intents")
-                .that(wifiMacAddressPreferenceController.getConnectivityIntents())
+                .that(mController.getConnectivityIntents())
                 .asList().containsAllIn(expectedIntents);
     }
 
     @Test
-    public void testWifiMacAddress() {
-        final WifiManager wifiManager = mock(WifiManager.class);
-        final WifiInfo wifiInfo = mock(WifiInfo.class);
-
-        doReturn(null).when(wifiManager).getConnectionInfo();
-        doReturn(wifiManager).when(mContext).getSystemService(WifiManager.class);
-
-        final AbstractWifiMacAddressPreferenceController wifiMacAddressPreferenceController =
-                new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle);
-
-        wifiMacAddressPreferenceController.displayPreference(mScreen);
-        verify(mPreference).setSummary(R.string.status_unavailable);
-
-        doReturn(wifiInfo).when(wifiManager).getConnectionInfo();
-        doReturn(TEST_MAC_ADDRESS).when(wifiInfo).getMacAddress();
-        wifiMacAddressPreferenceController.displayPreference(mScreen);
-        verify(mPreference).setSummary(TEST_MAC_ADDRESS);
-
+    public void updateConnectivity_nullWifiInfoWithMacRandomizationOff_setMacUnavailable() {
         Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, 1);
-        wifiMacAddressPreferenceController.displayPreference(mScreen);
-        verify(mPreference, times(2)).setSummary(TEST_MAC_ADDRESS);
+                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
+                AbstractWifiMacAddressPreferenceController.OFF);
+        doReturn(null).when(mWifiManager).getConnectionInfo();
 
-        doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(wifiInfo).getMacAddress();
-        wifiMacAddressPreferenceController.displayPreference(mScreen);
-        verify(mPreference).setSummary(R.string.wifi_status_mac_randomized);
+        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();
+
+        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/drawer/CategoryKeyTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
index 605c861..c495511 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
@@ -20,13 +20,15 @@
 
 import android.util.ArraySet;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
 
 import java.util.Set;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class CategoryKeyTest {
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
deleted file mode 100644
index 4efcb7e..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.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 com.android.settingslib.drawer;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Pair;
-
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.shadows.ShadowApplication;
-
-import java.util.HashMap;
-import java.util.Map;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-public class CategoryManagerTest {
-
-    private Context mContext;
-    private CategoryManager mCategoryManager;
-    private Map<Pair<String, String>, Tile> mTileByComponentCache;
-    private Map<String, DashboardCategory> mCategoryByKeyMap;
-
-    @Before
-    public void setUp() {
-        mContext = ShadowApplication.getInstance().getApplicationContext();
-        mTileByComponentCache = new HashMap<>();
-        mCategoryByKeyMap = new HashMap<>();
-        mCategoryManager = CategoryManager.get(mContext);
-    }
-
-    @Test
-    public void getInstance_shouldBeSingleton() {
-        assertThat(mCategoryManager).isSameAs(CategoryManager.get(mContext));
-    }
-
-    @Test
-    public void backwardCompatCleanupForCategory_shouldNotChangeCategoryForNewKeys() {
-        final Tile tile1 = new Tile();
-        final Tile tile2 = new Tile();
-        tile1.category = CategoryKey.CATEGORY_ACCOUNT;
-        tile2.category = CategoryKey.CATEGORY_ACCOUNT;
-        final DashboardCategory category = new DashboardCategory();
-        category.addTile(tile1);
-        category.addTile(tile2);
-        mCategoryByKeyMap.put(CategoryKey.CATEGORY_ACCOUNT, category);
-        mTileByComponentCache.put(new Pair<>("PACKAGE", "1"), tile1);
-        mTileByComponentCache.put(new Pair<>("PACKAGE", "2"), tile2);
-
-        mCategoryManager.backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
-
-        assertThat(mCategoryByKeyMap.size()).isEqualTo(1);
-        assertThat(mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT)).isNotNull();
-    }
-
-    @Test
-    public void backwardCompatCleanupForCategory_shouldNotChangeCategoryForMixedKeys() {
-        final Tile tile1 = new Tile();
-        final Tile tile2 = new Tile();
-        final String oldCategory = "com.android.settings.category.wireless";
-        tile1.category = CategoryKey.CATEGORY_ACCOUNT;
-        tile2.category = oldCategory;
-        final DashboardCategory category1 = new DashboardCategory();
-        category1.addTile(tile1);
-        final DashboardCategory category2 = new DashboardCategory();
-        category2.addTile(tile2);
-        mCategoryByKeyMap.put(CategoryKey.CATEGORY_ACCOUNT, category1);
-        mCategoryByKeyMap.put(oldCategory, category2);
-        mTileByComponentCache.put(new Pair<>("PACKAGE", "CLASS1"), tile1);
-        mTileByComponentCache.put(new Pair<>("PACKAGE", "CLASS2"), tile2);
-
-        mCategoryManager.backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
-
-        assertThat(mCategoryByKeyMap.size()).isEqualTo(2);
-        assertThat(
-                mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT).getTilesCount()).isEqualTo(1);
-        assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1);
-    }
-
-    @Test
-    public void backwardCompatCleanupForCategory_shouldChangeCategoryForOldKeys() {
-        final Tile tile1 = new Tile();
-        final String oldCategory = "com.android.settings.category.wireless";
-        tile1.category = oldCategory;
-        final DashboardCategory category1 = new DashboardCategory();
-        category1.addTile(tile1);
-        mCategoryByKeyMap.put(oldCategory, category1);
-        mTileByComponentCache.put(new Pair<>("PACKAGE", "CLASS1"), tile1);
-
-        mCategoryManager.backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
-
-        // Added 1 more category to category map.
-        assertThat(mCategoryByKeyMap.size()).isEqualTo(2);
-        // The new category map has CATEGORY_NETWORK type now, which contains 1 tile.
-        assertThat(
-                mCategoryByKeyMap.get(CategoryKey.CATEGORY_NETWORK).getTilesCount()).isEqualTo(1);
-        // Old category still exists.
-        assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1);
-    }
-
-    @Test
-    public void sortCategories_singlePackage_shouldReorderBasedOnPriority() {
-        // Create some fake tiles that are not sorted.
-        final String testPackage = "com.android.test";
-        final DashboardCategory category = new DashboardCategory();
-        final Tile tile1 = new Tile();
-        tile1.intent =
-                new Intent().setComponent(new ComponentName(testPackage, "class1"));
-        tile1.priority = 100;
-        final Tile tile2 = new Tile();
-        tile2.intent =
-                new Intent().setComponent(new ComponentName(testPackage, "class2"));
-        tile2.priority = 50;
-        final Tile tile3 = new Tile();
-        tile3.intent =
-                new Intent().setComponent(new ComponentName(testPackage, "class3"));
-        tile3.priority = 200;
-        category.addTile(tile1);
-        category.addTile(tile2);
-        category.addTile(tile3);
-        mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
-        // Sort their priorities
-        mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
-                mCategoryByKeyMap);
-
-        // Verify they are now sorted.
-        assertThat(category.getTile(0)).isSameAs(tile3);
-        assertThat(category.getTile(1)).isSameAs(tile1);
-        assertThat(category.getTile(2)).isSameAs(tile2);
-    }
-
-    @Test
-    public void sortCategories_multiPackage_shouldReorderBasedOnPackageAndPriority() {
-        // Create some fake tiles that are not sorted.
-        final String testPackage1 = "com.android.test1";
-        final String testPackage2 = "com.android.test2";
-        final DashboardCategory category = new DashboardCategory();
-        final Tile tile1 = new Tile();
-        tile1.intent =
-                new Intent().setComponent(new ComponentName(testPackage2, "class1"));
-        tile1.priority = 100;
-        final Tile tile2 = new Tile();
-        tile2.intent =
-                new Intent().setComponent(new ComponentName(testPackage1, "class2"));
-        tile2.priority = 100;
-        final Tile tile3 = new Tile();
-        tile3.intent =
-                new Intent().setComponent(new ComponentName(testPackage1, "class3"));
-        tile3.priority = 50;
-        category.addTile(tile1);
-        category.addTile(tile2);
-        category.addTile(tile3);
-        mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
-        // Sort their priorities
-        mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
-                mCategoryByKeyMap);
-
-        // Verify they are now sorted.
-        assertThat(category.getTile(0)).isSameAs(tile2);
-        assertThat(category.getTile(1)).isSameAs(tile1);
-        assertThat(category.getTile(2)).isSameAs(tile3);
-    }
-
-    @Test
-    public void sortCategories_internalPackageTiles_shouldSkipTileForInternalPackage() {
-        // Create some fake tiles that are not sorted.
-        final String testPackage =
-                ShadowApplication.getInstance().getApplicationContext().getPackageName();
-        final DashboardCategory category = new DashboardCategory();
-        final Tile tile1 = new Tile();
-        tile1.intent =
-                new Intent().setComponent(new ComponentName(testPackage, "class1"));
-        tile1.priority = 100;
-        final Tile tile2 = new Tile();
-        tile2.intent =
-                new Intent().setComponent(new ComponentName(testPackage, "class2"));
-        tile2.priority = 100;
-        final Tile tile3 = new Tile();
-        tile3.intent =
-                new Intent().setComponent(new ComponentName(testPackage, "class3"));
-        tile3.priority = 50;
-        category.addTile(tile1);
-        category.addTile(tile2);
-        category.addTile(tile3);
-        mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
-        // Sort their priorities
-        mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
-                mCategoryByKeyMap);
-
-        // Verify the sorting order is not changed
-        assertThat(category.getTile(0)).isSameAs(tile1);
-        assertThat(category.getTile(1)).isSameAs(tile2);
-        assertThat(category.getTile(2)).isSameAs(tile3);
-    }
-
-    @Test
-    public void sortCategories_internalAndExternalPackageTiles_shouldRetainPriorityOrdering() {
-        // Inject one external tile among internal tiles.
-        final String testPackage =
-            ShadowApplication.getInstance().getApplicationContext().getPackageName();
-        final String testPackage2 = "com.google.test2";
-        final DashboardCategory category = new DashboardCategory();
-        final Tile tile1 = new Tile();
-        tile1.intent = new Intent().setComponent(new ComponentName(testPackage, "class1"));
-        tile1.priority = 2;
-        final Tile tile2 = new Tile();
-        tile2.intent = new Intent().setComponent(new ComponentName(testPackage, "class2"));
-        tile2.priority = 1;
-        final Tile tile3 = new Tile();
-        tile3.intent = new Intent().setComponent(new ComponentName(testPackage2, "class0"));
-        tile3.priority = 0;
-        final Tile tile4 = new Tile();
-        tile4.intent = new Intent().setComponent(new ComponentName(testPackage, "class3"));
-        tile4.priority = -1;
-        category.addTile(tile1);
-        category.addTile(tile2);
-        category.addTile(tile3);
-        category.addTile(tile4);
-        mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
-        // Sort their priorities
-        mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
-            mCategoryByKeyMap);
-
-        // Verify the sorting order is not changed
-        assertThat(category.getTile(0)).isSameAs(tile1);
-        assertThat(category.getTile(1)).isSameAs(tile2);
-        assertThat(category.getTile(2)).isSameAs(tile3);
-        assertThat(category.getTile(3)).isSameAs(tile4);
-    }
-
-    @Test
-    public void sortCategories_samePriority_internalPackageTileShouldTakePrecedence() {
-        // Inject one external tile among internal tiles with same priority.
-        final String testPackage =
-            ShadowApplication.getInstance().getApplicationContext().getPackageName();
-        final String testPackage2 = "com.google.test2";
-        final String testPackage3 = "com.abcde.test3";
-        final DashboardCategory category = new DashboardCategory();
-        final Tile tile1 = new Tile();
-        tile1.intent = new Intent().setComponent(new ComponentName(testPackage2, "class1"));
-        tile1.priority = 1;
-        final Tile tile2 = new Tile();
-        tile2.intent = new Intent().setComponent(new ComponentName(testPackage, "class2"));
-        tile2.priority = 1;
-        final Tile tile3 = new Tile();
-        tile3.intent = new Intent().setComponent(new ComponentName(testPackage3, "class3"));
-        tile3.priority = 1;
-        category.addTile(tile1);
-        category.addTile(tile2);
-        category.addTile(tile3);
-        mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
-        // Sort their priorities
-        mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
-            mCategoryByKeyMap);
-
-        // Verify the sorting order is internal first, follow by package name ordering
-        assertThat(category.getTile(0)).isSameAs(tile2);
-        assertThat(category.getTile(1)).isSameAs(tile3);
-        assertThat(category.getTile(2)).isSameAs(tile1);
-    }
-
-    @Test
-    public void filterTiles_noDuplicate_noChange() {
-        // Create some unique tiles
-        final String testPackage =
-                ShadowApplication.getInstance().getApplicationContext().getPackageName();
-        final DashboardCategory category = new DashboardCategory();
-        final Tile tile1 = new Tile();
-        tile1.intent =
-                new Intent().setComponent(new ComponentName(testPackage, "class1"));
-        tile1.priority = 100;
-        final Tile tile2 = new Tile();
-        tile2.intent =
-                new Intent().setComponent(new ComponentName(testPackage, "class2"));
-        tile2.priority = 100;
-        final Tile tile3 = new Tile();
-        tile3.intent =
-                new Intent().setComponent(new ComponentName(testPackage, "class3"));
-        tile3.priority = 50;
-        category.addTile(tile1);
-        category.addTile(tile2);
-        category.addTile(tile3);
-        mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
-        mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
-
-        assertThat(category.getTilesCount()).isEqualTo(3);
-    }
-
-    @Test
-    public void filterTiles_hasDuplicate_shouldOnlyKeepUniqueTiles() {
-        // Create tiles pointing to same intent.
-        final String testPackage =
-                ShadowApplication.getInstance().getApplicationContext().getPackageName();
-        final DashboardCategory category = new DashboardCategory();
-        final Tile tile1 = new Tile();
-        tile1.intent =
-                new Intent().setComponent(new ComponentName(testPackage, "class1"));
-        tile1.priority = 100;
-        final Tile tile2 = new Tile();
-        tile2.intent =
-                new Intent().setComponent(new ComponentName(testPackage, "class1"));
-        tile2.priority = 100;
-        final Tile tile3 = new Tile();
-        tile3.intent =
-                new Intent().setComponent(new ComponentName(testPackage, "class1"));
-        tile3.priority = 50;
-        category.addTile(tile1);
-        category.addTile(tile2);
-        category.addTile(tile3);
-        mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
-        mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
-
-        assertThat(category.getTilesCount()).isEqualTo(1);
-    }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
new file mode 100644
index 0000000..a501ffa
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -0,0 +1,149 @@
+package com.android.settingslib.drawer;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.pm.ActivityInfo;
+import android.os.Bundle;
+
+import com.android.settingslib.R;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class TileTest {
+
+    private ActivityInfo mActivityInfo;
+    private Tile mTile;
+
+    @Before
+    public void setUp() {
+        mActivityInfo = new ActivityInfo();
+        mActivityInfo.packageName = RuntimeEnvironment.application.getPackageName();
+        mActivityInfo.name = "abc";
+        mActivityInfo.icon = R.drawable.ic_plus;
+        mActivityInfo.metaData = new Bundle();
+        mTile = new Tile(mActivityInfo, "category");
+    }
+
+    @Test
+    public void isPrimaryProfileOnly_profilePrimary_shouldReturnTrue() {
+        mActivityInfo.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_PRIMARY);
+        assertThat(mTile.isPrimaryProfileOnly()).isTrue();
+    }
+
+    @Test
+    public void isPrimaryProfileOnly_profileAll_shouldReturnFalse() {
+        mActivityInfo.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_ALL);
+        assertThat(mTile.isPrimaryProfileOnly()).isFalse();
+    }
+
+    @Test
+    public void isPrimaryProfileOnly_noExplicitValue_shouldReturnFalse() {
+        assertThat(mTile.isPrimaryProfileOnly()).isFalse();
+    }
+
+    @Test
+    public void isPrimaryProfileOnly_nullMetadata_shouldReturnFalse() {
+        mActivityInfo.metaData = null;
+        assertThat(mTile.isPrimaryProfileOnly()).isFalse();
+    }
+
+    @Test
+    public void getIcon_noContextOrMetadata_returnNull() {
+        mActivityInfo.metaData = null;
+        final Tile tile = new Tile(mActivityInfo, "category");
+        assertThat(tile.getIcon(null)).isNull();
+        assertThat(tile.getIcon(RuntimeEnvironment.application)).isNull();
+    }
+
+    @Test
+    public void getIcon_providedByUri_returnNull() {
+        mActivityInfo.metaData.putString(META_DATA_PREFERENCE_ICON_URI, "content://foobar/icon");
+
+        assertThat(mTile.getIcon(RuntimeEnvironment.application)).isNull();
+    }
+
+    @Test
+    public void getIcon_hasIconMetadata_returnIcon() {
+        mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, R.drawable.ic_info);
+
+        assertThat(mTile.getIcon(RuntimeEnvironment.application).getResId())
+                .isEqualTo(R.drawable.ic_info);
+    }
+
+    @Test
+    public void getIcon_noIconMetadata_returnActivityIcon() {
+        mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, 0);
+
+        assertThat(mTile.getIcon(RuntimeEnvironment.application).getResId())
+                .isEqualTo(mActivityInfo.icon);
+    }
+
+    @Test
+    public void isIconTintable_hasMetadata_shouldReturnIconTintableMetadata() {
+        final Tile tile = new Tile(mActivityInfo, "category");
+
+        mActivityInfo.metaData.putBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE, false);
+        assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isFalse();
+
+        mActivityInfo.metaData.putBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE, true);
+        assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isTrue();
+    }
+
+    @Test
+    public void isIconTintable_noIcon_shouldReturnFalse() {
+        final Tile tile = new Tile(mActivityInfo, "category");
+
+        assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isFalse();
+    }
+
+    @Test
+    public void isIconTintable_noMetadata_shouldReturnPackageNameCheck() {
+        final Tile tile1 = new Tile(mActivityInfo, "category");
+        assertThat(tile1.isIconTintable(RuntimeEnvironment.application)).isFalse();
+
+        final ActivityInfo activityInfo = new ActivityInfo();
+        activityInfo.packageName = "blah";
+        activityInfo.name = "abc";
+
+        final Tile tile2 = new Tile(activityInfo, "category");
+        assertThat(tile2.isIconTintable(RuntimeEnvironment.application)).isTrue();
+    }
+
+    @Test
+    public void getPriority_noMetadata_return0() {
+        final Tile tile = new Tile(mActivityInfo, "category");
+
+        assertThat(tile.getOrder()).isEqualTo(0);
+    }
+
+    @Test
+    public void getPriority_badMetadata_return0() {
+        mActivityInfo.metaData.putString(META_DATA_KEY_ORDER, "1");
+
+        final Tile tile = new Tile(mActivityInfo, "category");
+
+        assertThat(tile.getOrder()).isEqualTo(0);
+    }
+
+    @Test
+    public void getPriority_validMetadata_returnMetadataValue() {
+        mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 1);
+
+        final Tile tile = new Tile(mActivityInfo, "category");
+
+        assertThat(tile.getOrder()).isEqualTo(1);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index fc1b2238..9f097d8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -16,7 +16,14 @@
 
 package com.android.settingslib.drawer;
 
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI;
+
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -28,7 +35,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.robolectric.RuntimeEnvironment.application;
-import static org.robolectric.shadow.api.Shadow.extract;
 
 import android.app.ActivityManager;
 import android.content.ContentResolver;
@@ -47,13 +53,10 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings.Global;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Pair;
-import android.widget.RemoteViews;
 
-import com.android.settingslib.R;
-import com.android.settingslib.suggestions.SuggestionParser;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -61,18 +64,12 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = TileUtilsTest.TileUtilsShadowRemoteViews.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class TileUtilsTest {
 
     private Context mContext;
@@ -116,11 +113,10 @@
                 .thenReturn(info);
 
         TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, true /* forceTintExternalIcon */);
+                null /* defaultCategory */, outTiles, false /* usePriority */);
 
         assertThat(outTiles.size()).isEqualTo(1);
-        assertThat(outTiles.get(0).category).isEqualTo(testCategory);
+        assertThat(outTiles.get(0).getCategory()).isEqualTo(testCategory);
     }
 
     @Test
@@ -137,11 +133,10 @@
                 .thenReturn(info);
 
         TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, true /* forceTintExternalIcon */);
+                null /* defaultCategory */, outTiles, false /* usePriority */);
 
         assertThat(outTiles.size()).isEqualTo(1);
-        assertThat(outTiles.get(0).key).isEqualTo(keyHint);
+        assertThat(outTiles.get(0).getKey(mContext)).isEqualTo(keyHint);
     }
 
     @Test
@@ -157,42 +152,12 @@
                 .thenReturn(info);
 
         TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, true /* forceTintExternalIcon */);
+                null /* defaultCategory */, outTiles, false /* usePriority */);
 
         assertThat(outTiles.isEmpty()).isTrue();
     }
 
     @Test
-    public void getTilesForIntent_shouldSkipFilteredApps() {
-        Intent intent = new Intent();
-        Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
-        List<Tile> outTiles = new ArrayList<>();
-        List<ResolveInfo> info = new ArrayList<>();
-        ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
-                URI_GET_SUMMARY);
-        addMetadataToInfo(resolveInfo, "com.android.settings.require_account", "com.google");
-        addMetadataToInfo(resolveInfo, "com.android.settings.require_connection", "true");
-        info.add(resolveInfo);
-
-        when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
-                .thenReturn(info);
-
-        TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, true /* forceTintExternalIcon */);
-
-        assertThat(outTiles.size()).isEqualTo(1);
-        SuggestionParser parser = new SuggestionParser(
-                mContext,
-                null,
-                Collections.emptyList(),
-                "0,10");
-        parser.filterSuggestions(outTiles, 0, false);
-        assertThat(outTiles.size()).isEqualTo(0);
-    }
-
-    @Test
     public void getCategories_shouldHandleExtraIntentAction() {
         final String testCategory = "category1";
         final String testAction = "action1";
@@ -209,14 +174,12 @@
                 event -> testAction.equals(event.getAction())), anyInt(), anyInt()))
                 .thenReturn(info);
 
-        List<DashboardCategory> categoryList = TileUtils.getCategories(
-                mContext, cache, false /* categoryDefinedInManifest */, testAction,
-                TileUtils.SETTING_PKG);
-        assertThat(categoryList.get(0).getTile(0).category).isEqualTo(testCategory);
+        List<DashboardCategory> categoryList = TileUtils.getCategories(mContext, cache, testAction);
+        assertThat(categoryList.get(0).getTile(0).getCategory()).isEqualTo(testCategory);
     }
 
     @Test
-    public void getCategories_withPackageName() throws Exception {
+    public void getCategories_withPackageName() {
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
         Map<Pair<String, String>, Tile> cache = new ArrayMap<>();
         Global.putInt(mContext.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
@@ -226,9 +189,7 @@
         userHandleList.add(new UserHandle(ActivityManager.getCurrentUser()));
         when(mUserManager.getUserProfiles()).thenReturn(userHandleList);
 
-        TileUtils.getCategories(
-                mContext, cache, false /* categoryDefinedInManifest */, null /* action */,
-                TileUtils.SETTING_PKG);
+        TileUtils.getCategories(mContext, cache, null /* action */);
         verify(mPackageManager, atLeastOnce()).queryIntentActivitiesAsUser(
                 intentCaptor.capture(), anyInt(), anyInt());
 
@@ -237,7 +198,7 @@
     }
 
     @Test
-    public void getTilesForIntent_shouldReadMetadataTitleAsString() throws RemoteException {
+    public void getTilesForIntent_shouldReadMetadataTitleAsString() {
         Intent intent = new Intent();
         Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
         List<Tile> outTiles = new ArrayList<>();
@@ -250,15 +211,14 @@
                 .thenReturn(info);
 
         TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, true /* forceTintExternalIcon */);
+                null /* defaultCategory */, outTiles, false /* usePriority */);
 
         assertThat(outTiles.size()).isEqualTo(1);
         assertThat(outTiles.get(0).title).isEqualTo("my title");
     }
 
     @Test
-    public void getTilesForIntent_shouldReadMetadataTitleFromResource() throws RemoteException {
+    public void getTilesForIntent_shouldReadMetadataTitleFromResource() {
         Intent intent = new Intent();
         Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
         List<Tile> outTiles = new ArrayList<>();
@@ -274,15 +234,13 @@
                 .thenReturn("my localized title");
 
         TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, true /* forceTintExternalIcon */);
-
+                null /* defaultCategory */, outTiles, false /* usePriority */);
         assertThat(outTiles.size()).isEqualTo(1);
         assertThat(outTiles.get(0).title).isEqualTo("my localized title");
 
         // Icon should be tintable because the tile is not from settings package, and
         // "forceTintExternalIcon" is set
-        assertThat(outTiles.get(0).isIconTintable).isTrue();
+        assertThat(outTiles.get(0).isIconTintable(mContext)).isTrue();
     }
 
     @Test
@@ -301,11 +259,9 @@
                 .thenReturn(info);
 
         TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, true /* forceTintExternalIcon */);
+                null /* defaultCategory */, outTiles, false /* usePriority */);
 
-        assertThat(outTiles.size()).isEqualTo(1);
-        assertThat(outTiles.get(0).isIconTintable).isFalse();
+        assertThat(outTiles.get(0).isIconTintable(mContext)).isFalse();
     }
 
     @Test
@@ -324,11 +280,9 @@
                 .thenReturn(info);
 
         TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, false /* forceTintExternalIcon */);
+                null /* defaultCategory */, outTiles, false /* usePriority */);
 
-        assertThat(outTiles.size()).isEqualTo(1);
-        assertThat(outTiles.get(0).isIconTintable).isTrue();
+        assertThat(outTiles.get(0).isIconTintable(mContext)).isTrue();
     }
 
     @Test
@@ -346,11 +300,10 @@
 
         // Case 1: No provider associated with the uri specified.
         TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, true /* forceTintExternalIcon */);
+                null /* defaultCategory */, outTiles, false /* usePriority */);
 
         assertThat(outTiles.size()).isEqualTo(1);
-        assertThat(outTiles.get(0).icon.getResId()).isEqualTo(314159);
+        assertThat(outTiles.get(0).getIcon(mContext).getResId()).isEqualTo(314159);
         assertThat(outTiles.get(0).summary).isEqualTo("static-summary");
 
         // Case 2: Empty bundle.
@@ -364,16 +317,15 @@
                 .thenReturn(mIContentProvider);
 
         TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, true /* forceTintExternalIcon */);
+                null /* defaultCategory */, outTiles, false /* usePriority */);
 
         assertThat(outTiles.size()).isEqualTo(1);
-        assertThat(outTiles.get(0).icon.getResId()).isEqualTo(314159);
+        assertThat(outTiles.get(0).getIcon(mContext).getResId()).isEqualTo(314159);
         assertThat(outTiles.get(0).summary).isEqualTo("static-summary");
     }
 
     @Test
-    public void getTilesForIntent_shouldProcessUriContentForSystemApp() throws RemoteException {
+    public void getTilesForIntent_shouldProcessUriContentForSystemApp() {
         Intent intent = new Intent();
         Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
         List<Tile> outTiles = new ArrayList<>();
@@ -386,114 +338,11 @@
                 .thenReturn(info);
 
         TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, true /* forceTintExternalIcon */);
+                null /* defaultCategory */, outTiles, false /* usePriority */);
 
         assertThat(outTiles.size()).isEqualTo(1);
     }
 
-    @Test
-    public void getTilesForIntent_shouldShowRemoteViewIfSpecified() {
-        Intent intent = new Intent();
-        Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
-        List<Tile> outTiles = new ArrayList<>();
-        List<ResolveInfo> info = new ArrayList<>();
-        ResolveInfo resolveInfo = newInfo(true, null /* category */);
-        resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
-                R.layout.user_preference);
-        info.add(resolveInfo);
-
-        when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
-                .thenReturn(info);
-
-        TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, true /* forceTintExternalIcon */);
-
-        assertThat(outTiles.size()).isEqualTo(1);
-        Tile tile = outTiles.get(0);
-        assertThat(tile.remoteViews).isNotNull();
-        assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
-    }
-
-    @Test
-    public void getTilesForIntent_summaryUriSpecified_shouldOverrideRemoteViewSummary()
-            throws RemoteException {
-        Intent intent = new Intent();
-        Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
-        List<Tile> outTiles = new ArrayList<>();
-        List<ResolveInfo> info = new ArrayList<>();
-        ResolveInfo resolveInfo = newInfo(true, null /* category */, null,
-                null, URI_GET_SUMMARY);
-        resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
-                R.layout.user_preference);
-        info.add(resolveInfo);
-
-        when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
-                .thenReturn(info);
-
-        // Mock the content provider interaction.
-        Bundle bundle = new Bundle();
-        bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
-        when(mIContentProvider.call(anyString(),
-                eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
-                any())).thenReturn(bundle);
-        when(mContentResolver.acquireUnstableProvider(anyString()))
-                .thenReturn(mIContentProvider);
-        when(mContentResolver.acquireUnstableProvider(any(Uri.class)))
-                .thenReturn(mIContentProvider);
-
-        TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, true /* forceTintExternalIcon */);
-
-        assertThat(outTiles.size()).isEqualTo(1);
-        Tile tile = outTiles.get(0);
-        assertThat(tile.remoteViews).isNotNull();
-        assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
-        // Make sure the summary TextView got a new text string.
-        TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
-        assertThat(shadowRemoteViews.overrideViewId).isEqualTo(android.R.id.summary);
-        assertThat(shadowRemoteViews.overrideText).isEqualTo("new summary text");
-    }
-
-    @Test
-    public void getTilesForIntent_providerUnavailable_shouldNotOverrideRemoteViewSummary()
-            throws RemoteException {
-        Intent intent = new Intent();
-        Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
-        List<Tile> outTiles = new ArrayList<>();
-        List<ResolveInfo> info = new ArrayList<>();
-        ResolveInfo resolveInfo = newInfo(true, null /* category */, null,
-                null, URI_GET_SUMMARY);
-        resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
-                R.layout.user_preference);
-        info.add(resolveInfo);
-
-        when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
-                .thenReturn(info);
-
-        // Mock the content provider interaction.
-        Bundle bundle = new Bundle();
-        bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
-        when(mIContentProvider.call(anyString(),
-                eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
-                any())).thenReturn(bundle);
-
-        TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
-                null /* defaultCategory */, outTiles, false /* usePriority */,
-                false /* checkCategory */, true /* forceTintExternalIcon */);
-
-        assertThat(outTiles.size()).isEqualTo(1);
-        Tile tile = outTiles.get(0);
-        assertThat(tile.remoteViews).isNotNull();
-        assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
-        // Make sure the summary TextView didn't get any text view updates.
-        TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
-        assertThat(shadowRemoteViews.overrideViewId).isNull();
-        assertThat(shadowRemoteViews.overrideText).isNull();
-    }
-
     public static ResolveInfo newInfo(boolean systemApp, String category) {
         return newInfo(systemApp, category, null);
     }
@@ -517,16 +366,16 @@
         info.activityInfo.name = "123";
         info.activityInfo.metaData = new Bundle();
         info.activityInfo.metaData.putString("com.android.settings.category", category);
-        info.activityInfo.metaData.putInt("com.android.settings.icon", 314159);
-        info.activityInfo.metaData.putString("com.android.settings.summary", "static-summary");
+        info.activityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, 314159);
+        info.activityInfo.metaData.putString(META_DATA_PREFERENCE_SUMMARY, "static-summary");
         if (keyHint != null) {
-            info.activityInfo.metaData.putString("com.android.settings.keyhint", keyHint);
+            info.activityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, keyHint);
         }
         if (iconUri != null) {
-            info.activityInfo.metaData.putString("com.android.settings.icon_uri", iconUri);
+            info.activityInfo.metaData.putString(META_DATA_PREFERENCE_ICON_URI, iconUri);
         }
         if (summaryUri != null) {
-            info.activityInfo.metaData.putString("com.android.settings.summary_uri", summaryUri);
+            info.activityInfo.metaData.putString(META_DATA_PREFERENCE_SUMMARY_URI, summaryUri);
         }
         if (titleResId != 0) {
             info.activityInfo.metaData.putInt(TileUtils.META_DATA_PREFERENCE_TITLE, titleResId);
@@ -539,29 +388,4 @@
         }
         return info;
     }
-
-    private void addMetadataToInfo(ResolveInfo info, String key, String value) {
-        if (!TextUtils.isEmpty(key)) {
-            if (info.activityInfo == null) {
-                info.activityInfo = new ActivityInfo();
-            }
-            if (info.activityInfo.metaData == null) {
-                info.activityInfo.metaData = new Bundle();
-            }
-            info.activityInfo.metaData.putString(key, value);
-        }
-    }
-
-    @Implements(RemoteViews.class)
-    public static class TileUtilsShadowRemoteViews {
-
-        private Integer overrideViewId;
-        private CharSequence overrideText;
-
-        @Implementation
-        public void setTextViewText(int viewId, CharSequence text) {
-            overrideViewId = viewId;
-            overrideText = text;
-        }
-    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
index a23eebc..23087a9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
@@ -21,8 +21,10 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -51,7 +53,8 @@
 
     @Mock
     private IDeviceIdleController mDeviceIdleService;
-
+    @Mock
+    private DevicePolicyManager mDevicePolicyManager;
     private PowerWhitelistBackend mPowerWhitelistBackend;
     private ShadowPackageManager mPackageManager;
     private Context mContext;
@@ -59,7 +62,8 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
+        mContext = spy(RuntimeEnvironment.application);
+        doReturn(mContext).when(mContext).getApplicationContext();
         doReturn(new String[] {}).when(mDeviceIdleService).getFullPowerWhitelist();
         doReturn(new String[] {}).when(mDeviceIdleService).getSystemPowerWhitelist();
         doReturn(new String[] {}).when(mDeviceIdleService).getSystemPowerWhitelistExceptIdle();
@@ -67,6 +71,7 @@
         doNothing().when(mDeviceIdleService).removePowerSaveWhitelistApp(anyString());
         mPackageManager = Shadow.extract(mContext.getPackageManager());
         mPackageManager.setSystemFeature(PackageManager.FEATURE_TELEPHONY, true);
+        doReturn(mDevicePolicyManager).when(mContext).getSystemService(DevicePolicyManager.class);
 
         mPowerWhitelistBackend = new PowerWhitelistBackend(mContext, mDeviceIdleService);
     }
@@ -123,6 +128,13 @@
     }
 
     @Test
+    public void isWhitelisted_shouldWhitelistActiveDeviceAdminApp() {
+        doReturn(true).when(mDevicePolicyManager).packageHasActiveAdmins(PACKAGE_ONE);
+
+        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
+    }
+
+    @Test
     public void testIsSystemWhitelisted() throws Exception {
         doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getSystemPowerWhitelist();
         mPowerWhitelistBackend.refreshList();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
new file mode 100644
index 0000000..fa64afe
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
@@ -0,0 +1,272 @@
+/*
+ * 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.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class InputMethodAndSubtypeUtilCompatTest {
+
+    private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
+
+    private static HashSet<String> asHashSet(String... strings) {
+        HashSet<String> hashSet = new HashSet<>();
+        for (String s : strings) {
+            hashSet.add(s);
+        }
+        return hashSet;
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_EmptyString() {
+        assertThat(InputMethodAndSubtypeUtilCompat.
+                parseInputMethodsAndSubtypesString("")).isEmpty();
+        assertThat(InputMethodAndSubtypeUtilCompat.
+                parseInputMethodsAndSubtypesString(null)).isEmpty();
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_SingleImeNoSubtype() {
+        HashMap<String, HashSet<String>> r =
+                InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString("ime0");
+        assertThat(r).containsExactly("ime0", EMPTY_STRING_SET);
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_MultipleImesNoSubtype() {
+        HashMap<String, HashSet<String>> r =
+                InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString("ime0:ime1");
+        assertThat(r).containsExactly("ime0", EMPTY_STRING_SET, "ime1", EMPTY_STRING_SET);
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_SingleImeSingleSubtype() {
+        HashMap<String, HashSet<String>> r =
+                InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString("ime0;subtype0");
+        assertThat(r).containsExactly("ime0", asHashSet("subtype0"));
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_SingleImeDuplicateSameSubtypes() {
+        HashMap<String, HashSet<String>> r =
+                InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
+                        "ime0;subtype0;subtype0");
+        assertThat(r).containsExactly("ime0", asHashSet("subtype0"));
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_SingleImeMultipleSubtypes() {
+        HashMap<String, HashSet<String>> r =
+                InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
+                        "ime0;subtype0;subtype1");
+        assertThat(r).containsExactly("ime0", asHashSet("subtype0", "subtype1"));
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_MultiplePairsOfImeSubtype() {
+        assertThat(InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
+                "ime0;subtype0:ime1;subtype1"))
+                .containsExactly("ime0", asHashSet("subtype0"), "ime1", asHashSet("subtype1"));
+        assertThat(InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
+                "ime0;subtype0;subtype1:ime1;subtype2"))
+                .containsExactly("ime0", asHashSet("subtype0", "subtype1"),
+                        "ime1", asHashSet("subtype2"));
+        assertThat(InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
+                "ime0;subtype0;subtype1:ime1;subtype1;subtype2"))
+                .containsExactly("ime0", asHashSet("subtype0", "subtype1"),
+                        "ime1", asHashSet("subtype1", "subtype2"));
+
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_MixedImeSubtypePairsAndImeNoSubtype() {
+        HashMap<String, HashSet<String>> r =
+                InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
+                        "ime0;subtype0;subtype1:ime1;subtype1;subtype2:ime2");
+        assertThat(r).containsExactly("ime0", asHashSet("subtype0", "subtype1"),
+                "ime1", asHashSet("subtype1", "subtype2"),
+                "ime2", EMPTY_STRING_SET);
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_EmptyInput() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        assertThat(map).isEmpty();
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_SingleIme() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        map.put("ime0", new HashSet<>());
+        String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
+        assertThat(result).isEqualTo("ime0");
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_SingleImeSingleSubtype() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        map.put("ime0", asHashSet("subtype0"));
+        String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
+        assertThat(result).isEqualTo("ime0;subtype0");
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_SingleImeMultipleSubtypes() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        map.put("ime0", asHashSet("subtype0", "subtype1"));
+        String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
+
+        // We do not expect what order will be used to concatenate items in
+        // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
+        // permutations here.
+        assertThat(result).matches("ime0;subtype0;subtype1|ime0;subtype1;subtype0");
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_MultipleImesNoSubtypes() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        map.put("ime0", EMPTY_STRING_SET);
+        map.put("ime1", EMPTY_STRING_SET);
+        String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
+
+        // We do not expect what order will be used to concatenate items in
+        // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
+        // permutations here.
+        assertThat(result).matches("ime0:ime1|ime1:ime0");
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_MultipleImesWithAndWithoutSubtypes() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        map.put("ime0", asHashSet("subtype0", "subtype1"));
+        map.put("ime1", EMPTY_STRING_SET);
+        String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
+
+        // We do not expect what order will be used to concatenate items in
+        // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
+        // permutations here.
+        assertThat(result).matches("ime0;subtype0;subtype1:ime1|ime0;subtype1;subtype0:ime1"
+                + "|ime1:ime0;subtype0;subtype1|ime1:ime0;subtype1;subtype0");
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_MultipleImesWithSubtypes() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        map.put("ime0", asHashSet("subtype0", "subtype1"));
+        map.put("ime1", asHashSet("subtype2", "subtype3"));
+        String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
+
+        // We do not expect what order will be used to concatenate items in
+        // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
+        // permutations here.
+        assertThat(result).matches("ime0;subtype0;subtype1:ime1;subtype2;subtype3"
+                + "|ime0;subtype1;subtype0:ime1;subtype2;subtype3"
+                + "|ime0;subtype0;subtype1:ime1;subtype3;subtype2"
+                + "|ime0;subtype1;subtype0:ime1;subtype3;subtype2"
+                + "|ime1;subtype2;subtype3:ime0;subtype0;subtype1"
+                + "|ime2;subtype3;subtype2:ime0;subtype0;subtype1"
+                + "|ime3;subtype2;subtype3:ime0;subtype1;subtype0"
+                + "|ime4;subtype3;subtype2:ime0;subtype1;subtype0");
+    }
+
+    @Test
+    public void isValidSystemNonAuxAsciiCapableIme() {
+        // System IME w/ no subtype
+        assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(true, false)))
+                .isFalse();
+
+        // System IME w/ non-Aux and non-ASCII-capable "keyboard" subtype
+        assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(true, false, createDummySubtype("keyboard", false, false))))
+                .isFalse();
+
+        // System IME w/ non-Aux and ASCII-capable "keyboard" subtype
+        assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(true, false, createDummySubtype("keyboard", false, true))))
+                .isTrue();
+
+        // System IME w/ Aux and ASCII-capable "keyboard" subtype
+        assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(true, true, createDummySubtype("keyboard", true, true))))
+                .isFalse();
+
+        // System IME w/ non-Aux and ASCII-capable "voice" subtype
+        assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(true, false, createDummySubtype("voice", false, true))))
+                .isFalse();
+
+        // System IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype
+        assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(true, false,
+                        createDummySubtype("keyboard", false, true),
+                        createDummySubtype("keyboard", false, false))))
+                .isTrue();
+
+        // Non-system IME w/ non-Aux and ASCII-capable "keyboard" subtype
+        assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(false, false, createDummySubtype("keyboard", false, true))))
+                .isFalse();
+    }
+
+    private static InputMethodInfo createDummyIme(boolean isSystem, boolean isAuxIme,
+            InputMethodSubtype... subtypes) {
+        final ResolveInfo ri = new ResolveInfo();
+        final ServiceInfo si = new ServiceInfo();
+        final ApplicationInfo ai = new ApplicationInfo();
+        ai.packageName = "com.example.android.dummyime";
+        ai.enabled = true;
+        ai.flags |= (isSystem ? ApplicationInfo.FLAG_SYSTEM : 0);
+        si.applicationInfo = ai;
+        si.enabled = true;
+        si.packageName = "com.example.android.dummyime";
+        si.name = "Dummy IME";
+        si.exported = true;
+        si.nonLocalizedLabel = "Dummy IME";
+        ri.serviceInfo = si;
+        return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false);
+    }
+
+    private static InputMethodSubtype createDummySubtype(
+            String mode, boolean isAuxiliary, boolean isAsciiCapable) {
+        return new InputMethodSubtypeBuilder()
+                .setSubtypeNameResId(0)
+                .setSubtypeIconResId(0)
+                .setSubtypeLocale("en_US")
+                .setLanguageTag("en-US")
+                .setSubtypeMode(mode)
+                .setIsAuxiliary(isAuxiliary)
+                .setIsAsciiCapable(isAsciiCapable)
+                .build();
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
index d0a0686..03ab261 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
@@ -18,10 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
@@ -29,11 +25,16 @@
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class InputMethodAndSubtypeUtilTest {
 
     private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
new file mode 100644
index 0000000..12a4e69
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.settingslib.license;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+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.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = LicenseHtmlLoaderCompatTest.ShadowLicenseHtmlLoaderCompat.class)
+public class LicenseHtmlLoaderCompatTest {
+
+    @Mock
+    private Context mContext;
+    private LicenseHtmlLoaderCompat mLoader;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mLoader = new LicenseHtmlLoaderCompat(mContext);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowLicenseHtmlLoaderCompat.reset();
+    }
+
+    @Test
+    public void testLoadInBackground() {
+        ArrayList<File> xmlFiles = new ArrayList();
+        xmlFiles.add(new File("test.xml"));
+        File cachedHtmlFile = new File("test.html");
+
+        setupFakeData(xmlFiles, cachedHtmlFile, true, true);
+
+        assertThat(mLoader.loadInBackground()).isEqualTo(cachedHtmlFile);
+    }
+
+    @Test
+    public void testLoadInBackgroundWithNoVaildXmlFiles() {
+        ArrayList<File> xmlFiles = new ArrayList();
+        File cachedHtmlFile = new File("test.html");
+
+        setupFakeData(xmlFiles, cachedHtmlFile, true, true);
+
+        assertThat(mLoader.loadInBackground()).isNull();
+    }
+
+    @Test
+    public void testLoadInBackgroundWithNonOutdatedCachedHtmlFile() {
+        ArrayList<File> xmlFiles = new ArrayList();
+        xmlFiles.add(new File("test.xml"));
+        File cachedHtmlFile = new File("test.html");
+
+        setupFakeData(xmlFiles, cachedHtmlFile, false, true);
+
+        assertThat(mLoader.loadInBackground()).isEqualTo(cachedHtmlFile);
+    }
+
+    @Test
+    public void testLoadInBackgroundWithGenerateHtmlFileFailed() {
+        ArrayList<File> xmlFiles = new ArrayList();
+        xmlFiles.add(new File("test.xml"));
+        File cachedHtmlFile = new File("test.html");
+
+        setupFakeData(xmlFiles, cachedHtmlFile, true, false);
+
+        assertThat(mLoader.loadInBackground()).isNull();
+    }
+
+    void setupFakeData(ArrayList<File> xmlFiles,
+            File cachedHtmlFile, boolean isCachedHtmlFileOutdated,
+            boolean generateHtmlFileSucceeded) {
+
+        ShadowLicenseHtmlLoaderCompat.sValidXmlFiles = xmlFiles;
+        ShadowLicenseHtmlLoaderCompat.sCachedHtmlFile = cachedHtmlFile;
+        ShadowLicenseHtmlLoaderCompat.sIsCachedHtmlFileOutdated = isCachedHtmlFileOutdated;
+        ShadowLicenseHtmlLoaderCompat.sGenerateHtmlFileSucceeded = generateHtmlFileSucceeded;
+    }
+
+    @Implements(LicenseHtmlLoaderCompat.class)
+    public static class ShadowLicenseHtmlLoaderCompat {
+
+
+        public static List<File> sValidXmlFiles;
+        public static File sCachedHtmlFile;
+        public static boolean sIsCachedHtmlFileOutdated;
+        public static boolean sGenerateHtmlFileSucceeded;
+
+        @Resetter
+        public static void reset() {
+            sValidXmlFiles = null;
+            sCachedHtmlFile = null;
+            sIsCachedHtmlFileOutdated = false;
+            sGenerateHtmlFileSucceeded = false;
+        }
+
+        @Implementation
+        static List<File> getVaildXmlFiles() {
+            return sValidXmlFiles;
+        }
+
+        @Implementation
+        static File getCachedHtmlFile(Context context) {
+            return sCachedHtmlFile;
+        }
+
+        @Implementation
+        static boolean isCachedHtmlFileOutdated(List<File> xmlFiles, File cachedHtmlFile) {
+            return sIsCachedHtmlFileOutdated;
+        }
+
+        @Implementation
+        static boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
+            return sGenerateHtmlFileSucceeded;
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderTest.java
deleted file mode 100644
index 5095f50..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderTest.java
+++ /dev/null
@@ -1,106 +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.settingslib.license;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.util.ArrayList;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-public class LicenseHtmlLoaderTest {
-    @Mock
-    private Context mContext;
-
-    LicenseHtmlLoader newLicenseHtmlLoader(ArrayList<File> xmlFiles,
-            File cachedHtmlFile, boolean isCachedHtmlFileOutdated,
-            boolean generateHtmlFileSucceeded) {
-        LicenseHtmlLoader loader = spy(new LicenseHtmlLoader(mContext));
-        doReturn(xmlFiles).when(loader).getVaildXmlFiles();
-        doReturn(cachedHtmlFile).when(loader).getCachedHtmlFile();
-        doReturn(isCachedHtmlFileOutdated).when(loader).isCachedHtmlFileOutdated(any(), any());
-        doReturn(generateHtmlFileSucceeded).when(loader).generateHtmlFile(any(), any());
-        return loader;
-    }
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void testLoadInBackground() {
-        ArrayList<File> xmlFiles = new ArrayList();
-        xmlFiles.add(new File("test.xml"));
-        File cachedHtmlFile = new File("test.html");
-
-        LicenseHtmlLoader loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, true, true);
-
-        assertThat(loader.loadInBackground()).isEqualTo(cachedHtmlFile);
-        verify(loader).generateHtmlFile(any(), any());
-    }
-
-    @Test
-    public void testLoadInBackgroundWithNoVaildXmlFiles() {
-        ArrayList<File> xmlFiles = new ArrayList();
-        File cachedHtmlFile = new File("test.html");
-
-        LicenseHtmlLoader loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, true, true);
-
-        assertThat(loader.loadInBackground()).isNull();
-        verify(loader, never()).generateHtmlFile(any(), any());
-    }
-
-    @Test
-    public void testLoadInBackgroundWithNonOutdatedCachedHtmlFile() {
-        ArrayList<File> xmlFiles = new ArrayList();
-        xmlFiles.add(new File("test.xml"));
-        File cachedHtmlFile = new File("test.html");
-
-        LicenseHtmlLoader loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, false, true);
-
-        assertThat(loader.loadInBackground()).isEqualTo(cachedHtmlFile);
-        verify(loader, never()).generateHtmlFile(any(), any());
-    }
-
-    @Test
-    public void testLoadInBackgroundWithGenerateHtmlFileFailed() {
-        ArrayList<File> xmlFiles = new ArrayList();
-        xmlFiles.add(new File("test.xml"));
-        File cachedHtmlFile = new File("test.html");
-
-        LicenseHtmlLoader loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, true, false);
-
-        assertThat(loader.loadInBackground()).isNull();
-        verify(loader).generateHtmlFile(any(), any());
-    }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
new file mode 100644
index 0000000..c29481f
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.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.settingslib.location;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public final class InjectedSettingTest {
+
+    private static final String TEST_STRING = "test";
+
+    @Test
+    public void buildWithoutPackageName_ShouldReturnNull() {
+        assertThat(((new InjectedSetting.Builder())
+                .setClassName(TEST_STRING)
+                .setTitle(TEST_STRING)
+                .setSettingsActivity(TEST_STRING).build())).isNull();
+    }
+
+    private InjectedSetting getTestSetting() {
+        return new InjectedSetting.Builder()
+                .setPackageName(TEST_STRING)
+                .setClassName(TEST_STRING)
+                .setTitle(TEST_STRING)
+                .setSettingsActivity(TEST_STRING).build();
+    }
+
+    @Test
+    public void testEquals() {
+        InjectedSetting setting1 = getTestSetting();
+        InjectedSetting setting2 = getTestSetting();
+        assertThat(setting1).isEqualTo(setting2);
+    }
+
+    @Test
+    public void testHashCode() {
+        InjectedSetting setting = getTestSetting();
+        assertThat(setting.hashCode()).isEqualTo(1225314048);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
new file mode 100644
index 0000000..1be856a
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.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.net;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkStatsHistory.Entry;
+import android.net.NetworkTemplate;
+import android.os.RemoteException;
+import android.text.format.DateUtils;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class DataUsageControllerTest {
+
+    @Mock
+    private INetworkStatsSession mSession;
+
+    private Context mContext;
+    private DataUsageController mController;
+    private NetworkStatsHistory mNetworkStatsHistory;
+
+    @Before
+    public void setUp() throws RemoteException {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = spy(new DataUsageController(mContext));
+        mNetworkStatsHistory = spy(
+                new NetworkStatsHistory(DateUtils.DAY_IN_MILLIS /* bucketDuration */));
+        doReturn(mNetworkStatsHistory)
+                .when(mSession).getHistoryForNetwork(any(NetworkTemplate.class), anyInt());
+    }
+
+    @Test
+    public void getHistoriclUsageLevel_noNetworkSession_shouldReturn0() {
+        doReturn(null).when(mController).getSession();
+
+        assertThat(mController.getHistoriclUsageLevel(null /* template */)).isEqualTo(0L);
+
+    }
+
+    @Test
+    public void getHistoriclUsageLevel_noUsageData_shouldReturn0() {
+        doReturn(mSession).when(mController).getSession();
+
+        assertThat(mController.getHistoriclUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+                .isEqualTo(0L);
+
+    }
+
+    @Test
+    public void getHistoriclUsageLevel_hasUsageData_shouldReturnTotalUsage() {
+        doReturn(mSession).when(mController).getSession();
+        final long receivedBytes = 743823454L;
+        final long transmittedBytes = 16574289L;
+        final Entry entry = new Entry();
+        entry.bucketStart = 1521583200000L;
+        entry.rxBytes = receivedBytes;
+        entry.txBytes = transmittedBytes;
+        when(mNetworkStatsHistory.getValues(eq(0L), anyLong(), anyLong(), nullable(Entry.class)))
+                .thenReturn(entry);
+
+        assertThat(mController.getHistoriclUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+                .isEqualTo(receivedBytes + transmittedBytes);
+
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
index 9b491c2..f47f41c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
@@ -26,7 +26,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
-import android.app.AlertDialog;
+import android.app.Activity;
 import android.app.Fragment;
 import android.app.NotificationManager;
 import android.content.Context;
@@ -40,6 +40,8 @@
 import android.view.View;
 import android.widget.Button;
 
+import androidx.appcompat.app.AlertDialog;
+
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
@@ -47,6 +49,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
 import org.robolectric.RuntimeEnvironment;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
@@ -58,6 +61,7 @@
     private Condition mCountdownCondition;
     private Condition mAlarmCondition;
     private ContentResolver mContentResolver;
+    private AlertDialog.Builder mBuilder;
 
     @Before
     public void setup() {
@@ -68,13 +72,14 @@
         mController = spy(new ZenDurationDialog(mContext));
         mController.mLayoutInflater = mLayoutInflater;
         mController.getContentView();
+        mBuilder = new AlertDialog.Builder(mContext);
     }
 
     @Test
     public void testAlwaysPrompt() {
-        Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION,
+        Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION,
                 Settings.Global.ZEN_DURATION_PROMPT);
-        mController.createDialog();
+        mController.setupDialog(mBuilder);
 
         assertFalse(mController.getConditionTagAt(ZenDurationDialog.FOREVER_CONDITION_INDEX).rb
                 .isChecked());
@@ -86,9 +91,9 @@
 
     @Test
     public void testForever() {
-        Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION,
-                Settings.Global.ZEN_DURATION_FOREVER);
-        mController.createDialog();
+        Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION,
+                Settings.Secure.ZEN_DURATION_FOREVER);
+        mController.setupDialog(mBuilder);
 
         assertTrue(mController.getConditionTagAt(ZenDurationDialog.FOREVER_CONDITION_INDEX).rb
                 .isChecked());
@@ -100,8 +105,8 @@
 
     @Test
     public void testSpecificDuration() {
-        Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION, 45);
-        mController.createDialog();
+        Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION, 45);
+        mController.setupDialog(mBuilder);
 
         assertFalse(mController.getConditionTagAt(ZenDurationDialog.FOREVER_CONDITION_INDEX).rb
                 .isChecked());
@@ -114,53 +119,53 @@
 
     @Test
     public void testChooseAlwaysPromptSetting() {
-        Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION,
-                Settings.Global.ZEN_DURATION_FOREVER);
+        Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION,
+                Settings.Secure.ZEN_DURATION_FOREVER);
 
-        AlertDialog dialog = (AlertDialog) mController.createDialog();
+        mController.setupDialog(mBuilder);
         mController.getConditionTagAt(ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.setChecked(
                 true);
-        mController.updateZenDuration(Settings.Global.ZEN_DURATION_FOREVER);
+        mController.updateZenDuration(Settings.Secure.ZEN_DURATION_FOREVER);
 
-        assertEquals(Settings.Global.ZEN_DURATION_PROMPT, Settings.Global.getInt(mContentResolver,
-                Settings.Global.ZEN_DURATION, Settings.Global.ZEN_DURATION_FOREVER));
+        assertEquals(Settings.Secure.ZEN_DURATION_PROMPT, Settings.Secure.getInt(mContentResolver,
+                Settings.Secure.ZEN_DURATION, Settings.Secure.ZEN_DURATION_FOREVER));
     }
 
     @Test
     public void testChooseForeverSetting() {
-        Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION,
-                Settings.Global.ZEN_DURATION_PROMPT);
+        Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION,
+                Settings.Secure.ZEN_DURATION_PROMPT);
 
-        AlertDialog dialog = (AlertDialog) mController.createDialog();
+        mController.setupDialog(mBuilder);
         mController.getConditionTagAt(ZenDurationDialog.FOREVER_CONDITION_INDEX).rb.setChecked(
                 true);
-        mController.updateZenDuration(Settings.Global.ZEN_DURATION_PROMPT);
+        mController.updateZenDuration(Settings.Secure.ZEN_DURATION_PROMPT);
 
-        assertEquals(Settings.Global.ZEN_DURATION_FOREVER, Settings.Global.getInt(mContentResolver,
-                Settings.Global.ZEN_DURATION, Settings.Global.ZEN_DURATION_PROMPT));
+        assertEquals(Settings.Secure.ZEN_DURATION_FOREVER, Settings.Secure.getInt(mContentResolver,
+                Settings.Secure.ZEN_DURATION, Settings.Secure.ZEN_DURATION_PROMPT));
     }
 
     @Test
     public void testChooseTimeSetting() {
-        Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION,
-                Settings.Global.ZEN_DURATION_PROMPT);
+        Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION,
+                Settings.Secure.ZEN_DURATION_PROMPT);
 
-        AlertDialog dialog = (AlertDialog) mController.createDialog();
+        mController.setupDialog(mBuilder);
         mController.getConditionTagAt(ZenDurationDialog.COUNTDOWN_CONDITION_INDEX).rb.setChecked(
                 true);
-        mController.updateZenDuration(Settings.Global.ZEN_DURATION_PROMPT);
+        mController.updateZenDuration(Settings.Secure.ZEN_DURATION_PROMPT);
 
         // countdown defaults to 60 minutes:
-        assertEquals(60, Settings.Global.getInt(mContentResolver,
-                Settings.Global.ZEN_DURATION, Settings.Global.ZEN_DURATION_PROMPT));
+        assertEquals(60, Settings.Secure.getInt(mContentResolver,
+                Settings.Secure.ZEN_DURATION, Settings.Secure.ZEN_DURATION_PROMPT));
     }
 
     @Test
     public void testGetTimeFromBucket() {
-        Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION,
-                Settings.Global.ZEN_DURATION_PROMPT);
+        Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION,
+                Settings.Secure.ZEN_DURATION_PROMPT);
 
-        AlertDialog dialog = (AlertDialog) mController.createDialog();
+        mController.setupDialog(mBuilder);
         // click time button starts at 60 minutes
         // - 1 hour to MAX_BUCKET_MINUTES (12 hours), increments by 1 hour
         // - 0-60 minutes increments by 15 minutes
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
new file mode 100644
index 0000000..1ee3afa
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.suggestions;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import androidx.lifecycle.LifecycleOwner;
+import androidx.loader.app.LoaderManager;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = ShadowSuggestionController.class)
+public class SuggestionControllerMixinCompatTest {
+
+    @Mock
+    private SuggestionControllerMixinCompat.SuggestionControllerHost mHost;
+
+    private Context mContext;
+    private LifecycleOwner mLifecycleOwner;
+    private Lifecycle mLifecycle;
+    private SuggestionControllerMixinCompat mMixin;
+    private ComponentName mComponentName;
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mLifecycleOwner = () -> mLifecycle;
+        mLifecycle = new Lifecycle(mLifecycleOwner);
+        mComponentName = new ComponentName(
+                "com.android.settings.intelligence",
+                "com.android.settings.intelligence.suggestions.SuggestionService");
+    }
+
+    @After
+    public void tearDown() {
+        ShadowSuggestionController.reset();
+    }
+
+    @Test
+    public void goThroughLifecycle_onStartStop_shouldStartStopController() {
+        mMixin = new SuggestionControllerMixinCompat(mContext, mHost, mLifecycle, mComponentName);
+
+        mLifecycle.handleLifecycleEvent(ON_START);
+        assertThat(ShadowSuggestionController.sStartCalled).isTrue();
+
+        mLifecycle.handleLifecycleEvent(ON_STOP);
+        assertThat(ShadowSuggestionController.sStopCalled).isTrue();
+    }
+
+    @Test
+    public void onServiceConnected_shouldGetSuggestion() {
+        final LoaderManager loaderManager = mock(LoaderManager.class);
+        when(mHost.getLoaderManager()).thenReturn(loaderManager);
+
+        mMixin = new SuggestionControllerMixinCompat(mContext, mHost, mLifecycle, mComponentName);
+        mMixin.onServiceConnected();
+
+        verify(loaderManager).restartLoader(SuggestionLoader.LOADER_ID_SUGGESTIONS,
+                null /* args */, mMixin /* callback */);
+    }
+
+    @Test
+    public void onServiceConnected_hostNotAttached_shouldDoNothing() {
+        when(mHost.getLoaderManager()).thenReturn(null);
+
+        mMixin = new SuggestionControllerMixinCompat(mContext, mHost, mLifecycle, mComponentName);
+        mMixin.onServiceConnected();
+
+        verify(mHost).getLoaderManager();
+    }
+
+    @Test
+    public void onServiceDisconnected_hostNotAttached_shouldDoNothing() {
+        when(mHost.getLoaderManager()).thenReturn(null);
+
+        mMixin = new SuggestionControllerMixinCompat(mContext, mHost, mLifecycle, mComponentName);
+        mMixin.onServiceDisconnected();
+
+        verify(mHost).getLoaderManager();
+    }
+
+    @Test
+    public void doneLoadingg_shouldSetSuggestionLoaded() {
+        mMixin = new SuggestionControllerMixinCompat(mContext, mHost, mLifecycle, mComponentName);
+
+        mMixin.onLoadFinished(mock(SuggestionLoaderCompat.class), null);
+
+        assertThat(mMixin.isSuggestionLoaded()).isTrue();
+
+        mMixin.onLoaderReset(mock(SuggestionLoaderCompat.class));
+
+        assertThat(mMixin.isSuggestionLoaded()).isFalse();
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
deleted file mode 100644
index d05bcfd..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
+++ /dev/null
@@ -1,200 +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.settingslib.suggestions;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.robolectric.RuntimeEnvironment.application;
-import static org.robolectric.shadow.api.Shadow.extract;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-import com.android.settingslib.drawer.Tile;
-import com.android.settingslib.drawer.TileUtilsTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.shadows.ShadowPackageManager;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-public class SuggestionParserTest {
-
-    private ShadowPackageManager mPackageManager;
-    private SuggestionParser mSuggestionParser;
-    private SuggestionCategory mMultipleCategory;
-    private SuggestionCategory mExclusiveCategory;
-    private SuggestionCategory mExpiredExclusiveCategory;
-    private List<Tile> mSuggestionsBeforeDismiss;
-    private List<Tile> mSuggestionsAfterDismiss;
-    private SharedPreferences mPrefs;
-    private Tile mSuggestion;
-
-    @Before
-    public void setUp() {
-        mPackageManager = extract(application.getPackageManager());
-        mPrefs = PreferenceManager.getDefaultSharedPreferences(application);
-        mSuggestion = new Tile();
-        mSuggestion.intent = new Intent("action");
-        mSuggestion.intent.setComponent(new ComponentName("pkg", "cls"));
-        mSuggestion.metaData = new Bundle();
-        mMultipleCategory = new SuggestionCategory();
-        mMultipleCategory.category = "category1";
-        mMultipleCategory.multiple = true;
-        mExclusiveCategory = new SuggestionCategory();
-        mExclusiveCategory.category = "category2";
-        mExclusiveCategory.exclusive = true;
-        mExpiredExclusiveCategory = new SuggestionCategory();
-        mExpiredExclusiveCategory.category = "category3";
-        mExpiredExclusiveCategory.exclusive = true;
-        mExpiredExclusiveCategory.exclusiveExpireDaysInMillis = 0;
-
-        mSuggestionParser = new SuggestionParser(application, mPrefs,
-                Arrays.asList(mMultipleCategory, mExclusiveCategory, mExpiredExclusiveCategory),
-                "0");
-
-        ResolveInfo info1 = TileUtilsTest.newInfo(true, null);
-        info1.activityInfo.packageName = "pkg";
-        ResolveInfo infoDupe1 = TileUtilsTest.newInfo(true, null);
-        infoDupe1.activityInfo.packageName = "pkg";
-
-        ResolveInfo info2 = TileUtilsTest.newInfo(true, null);
-        info2.activityInfo.packageName = "pkg2";
-        ResolveInfo info3 = TileUtilsTest.newInfo(true, null);
-        info3.activityInfo.packageName = "pkg3";
-        ResolveInfo info4 = TileUtilsTest.newInfo(true, null);
-        info4.activityInfo.packageName = "pkg4";
-
-        Intent intent1 = new Intent(Intent.ACTION_MAIN).addCategory("category1");
-        Intent intent2 = new Intent(Intent.ACTION_MAIN).addCategory("category2");
-        Intent intent3 = new Intent(Intent.ACTION_MAIN).addCategory("category3");
-
-        mPackageManager.addResolveInfoForIntent(intent1, info1);
-        mPackageManager.addResolveInfoForIntent(intent1, info2);
-        mPackageManager.addResolveInfoForIntent(intent1, infoDupe1);
-        mPackageManager.addResolveInfoForIntent(intent2, info3);
-        mPackageManager.addResolveInfoForIntent(intent3, info4);
-    }
-
-    @Test
-    public void dismissSuggestion_shouldDismiss() {
-        assertThat(mSuggestionParser.dismissSuggestion(mSuggestion)).isTrue();
-    }
-
-    @Test
-    public void testGetSuggestions_withoutSmartSuggestions_shouldDismiss() {
-        readAndDismissSuggestion(false);
-        mSuggestionParser.readSuggestions(mMultipleCategory, mSuggestionsAfterDismiss, false);
-        assertThat(mSuggestionsBeforeDismiss).hasSize(2);
-        assertThat(mSuggestionsAfterDismiss).hasSize(1);
-        assertThat(mSuggestionsBeforeDismiss.get(1)).isEqualTo(mSuggestionsAfterDismiss.get(0));
-    }
-
-    @Test
-    public void testGetSuggestions_withSmartSuggestions_shouldDismiss() {
-        readAndDismissSuggestion(true);
-        assertThat(mSuggestionsBeforeDismiss).hasSize(2);
-        assertThat(mSuggestionsAfterDismiss).hasSize(1);
-    }
-
-    @Test
-    public void testGetSuggestion_exclusiveNotAvailable_onlyRegularCategoryAndNoDupe() {
-        mPackageManager.removeResolveInfosForIntent(
-                new Intent(Intent.ACTION_MAIN).addCategory("category2"),
-                "pkg3");
-        mPackageManager.removeResolveInfosForIntent(
-                new Intent(Intent.ACTION_MAIN).addCategory("category3"),
-                "pkg4");
-
-        // If exclusive item is not available, the other categories should be shown
-        final SuggestionList sl =
-                mSuggestionParser.getSuggestions(false /* isSmartSuggestionEnabled */);
-        final List<Tile> suggestions = sl.getSuggestions();
-        assertThat(suggestions).hasSize(2);
-
-        assertThat(suggestions.get(0).intent.getComponent().getPackageName()).isEqualTo("pkg");
-        assertThat(suggestions.get(1).intent.getComponent().getPackageName()).isEqualTo("pkg2");
-    }
-
-    @Test
-    public void testGetSuggestion_exclusiveExpiredAvailable_shouldLoadWithRegularCategory() {
-        // First remove permanent exclusive
-        mPackageManager.removeResolveInfosForIntent(
-                new Intent(Intent.ACTION_MAIN).addCategory("category2"),
-                "pkg3");
-        // Set the other exclusive to be expired.
-        mPrefs.edit()
-                .putLong(mExpiredExclusiveCategory.category + "_setup_time",
-                        System.currentTimeMillis() - 1000)
-                .commit();
-
-        // If exclusive is expired, they should be shown together with the other categories
-        final SuggestionList sl =
-                mSuggestionParser.getSuggestions(true /* isSmartSuggestionEnabled */);
-        final List<Tile> suggestions = sl.getSuggestions();
-
-        assertThat(suggestions).hasSize(3);
-    }
-
-    @Test
-    public void testGetSuggestions_exclusive() {
-        final SuggestionList sl =
-                mSuggestionParser.getSuggestions(false /* isSmartSuggestionEnabled */);
-        final List<Tile> suggestions = sl.getSuggestions();
-
-        assertThat(suggestions).hasSize(1);
-    }
-
-    @Test
-    public void isSuggestionDismissed_dismissedSuggestion_shouldReturnTrue() {
-        final Tile suggestion = new Tile();
-        suggestion.metaData = new Bundle();
-        suggestion.metaData.putString(SuggestionParser.META_DATA_DISMISS_CONTROL, "1,2,3");
-        suggestion.intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
-
-        // Dismiss suggestion when smart suggestion is not enabled.
-        mSuggestionParser.dismissSuggestion(suggestion);
-
-        assertThat(mSuggestionParser.isDismissed(suggestion, true /* isSmartSuggestionEnabled */))
-                .isTrue();
-    }
-
-    private void readAndDismissSuggestion(boolean isSmartSuggestionEnabled) {
-        mSuggestionsBeforeDismiss = new ArrayList<>();
-        mSuggestionsAfterDismiss = new ArrayList<>();
-        mSuggestionParser.readSuggestions(
-                mMultipleCategory, mSuggestionsBeforeDismiss, isSmartSuggestionEnabled);
-
-        final Tile suggestion = mSuggestionsBeforeDismiss.get(0);
-        if (mSuggestionParser.dismissSuggestion(suggestion)) {
-            mPackageManager.removeResolveInfosForIntent(
-                    new Intent(Intent.ACTION_MAIN).addCategory(mMultipleCategory.category),
-                    suggestion.intent.getComponent().getPackageName());
-        }
-        mSuggestionParser.readSuggestions(
-                mMultipleCategory, mSuggestionsAfterDismiss, isSmartSuggestionEnabled);
-    }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java
new file mode 100644
index 0000000..8ba8606
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.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.settingslib.testutils;
+
+import android.os.Bundle;
+import android.widget.LinearLayout;
+
+import org.robolectric.Robolectric;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+
+/**
+ * Utilities for creating Fragments for testing.
+ * <p>
+ * TODO(b/111195449) - Duplicated from org.robolectric.shadows.support.v4.SupportFragmentTestUtil
+ */
+@Deprecated
+public class FragmentTestUtils {
+
+    public static void startFragment(Fragment fragment) {
+        buildFragmentManager(FragmentUtilActivity.class)
+                .beginTransaction().add(fragment, null).commit();
+    }
+
+    public static void startFragment(Fragment fragment,
+            Class<? extends FragmentActivity> activityClass) {
+        buildFragmentManager(activityClass)
+                .beginTransaction().add(fragment, null).commit();
+    }
+
+    public static void startVisibleFragment(Fragment fragment) {
+        buildFragmentManager(FragmentUtilActivity.class)
+                .beginTransaction().add(1, fragment, null).commit();
+    }
+
+    public static void startVisibleFragment(Fragment fragment,
+            Class<? extends FragmentActivity> activityClass, int containerViewId) {
+        buildFragmentManager(activityClass)
+                .beginTransaction().add(containerViewId, fragment, null).commit();
+    }
+
+    private static FragmentManager buildFragmentManager(
+            Class<? extends FragmentActivity> activityClass) {
+        FragmentActivity activity = Robolectric.setupActivity(activityClass);
+        return activity.getSupportFragmentManager();
+    }
+
+    private static class FragmentUtilActivity extends FragmentActivity {
+        @Override
+        protected void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            LinearLayout view = new LinearLayout(this);
+            view.setId(1);
+
+            setContentView(view);
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
index 026ad47..645dfa1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
@@ -30,13 +30,14 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class IconCacheTest {
     private Icon mIcon;
     private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
index 83a9d5b..1e066b1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
@@ -17,14 +17,16 @@
 
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.fail;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.shadows.ShadowLooper;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class ThreadUtilsTest {
 
     @Test
@@ -56,7 +58,7 @@
     }
 
     @Test
-    public void testPostOnMainThread_shouldRunOnMainTread() throws Exception {
+    public void testPostOnMainThread_shouldRunOnMainTread() {
         TestRunnable cr = new TestRunnable();
         ShadowLooper.pauseMainLooper();
         ThreadUtils.postOnMainThread(cr);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
index 36abd20..a00f12d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
@@ -22,13 +22,14 @@
 import android.graphics.drawable.AnimatedRotateDrawable;
 import android.view.View;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class AnimatedImageViewTest {
     private AnimatedImageView mAnimatedImageView;
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
new file mode 100644
index 0000000..1abbaba
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.shadows.ShadowApplication;
+
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class FooterPreferenceMixinCompatTest {
+
+    @Mock
+    private PreferenceFragmentCompat mFragment;
+    @Mock
+    private PreferenceScreen mScreen;
+
+    private LifecycleOwner mLifecycleOwner;
+    private Lifecycle mLifecycle;
+    private FooterPreferenceMixinCompat mMixin;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mLifecycleOwner = () -> mLifecycle;
+        mLifecycle = new Lifecycle(mLifecycleOwner);
+        when(mFragment.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
+        when(mFragment.getPreferenceManager().getContext())
+                .thenReturn(ShadowApplication.getInstance().getApplicationContext());
+        mMixin = new FooterPreferenceMixinCompat(mFragment, mLifecycle);
+    }
+
+    @Test
+    public void createFooter_screenNotAvailable_noCrash() {
+        assertThat(mMixin.createFooterPreference()).isNotNull();
+    }
+
+    @Test
+    public void createFooter_screenAvailable_canAttachToScreen() {
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+
+        final FooterPreference preference = mMixin.createFooterPreference();
+
+        assertThat(preference).isNotNull();
+        verify(mScreen).addPreference(preference);
+    }
+
+    @Test
+    public void createFooter_screenAvailableDelayed_canAttachToScreen() {
+        final FooterPreference preference = mMixin.createFooterPreference();
+
+        mLifecycle.setPreferenceScreen(mScreen);
+
+        assertThat(preference).isNotNull();
+        verify(mScreen).addPreference(preference);
+    }
+
+    @Test
+    public void createFooterTwice_screenAvailable_replaceOldFooter() {
+        when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+
+        mMixin.createFooterPreference();
+        mMixin.createFooterPreference();
+
+        verify(mScreen).removePreference(any(FooterPreference.class));
+        verify(mScreen, times(2)).addPreference(any(FooterPreference.class));
+    }
+
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
index 78b7616..8604d18 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
@@ -23,11 +23,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import androidx.lifecycle.LifecycleOwner;
-import androidx.preference.PreferenceFragment;
-import androidx.preference.PreferenceManager;
-import androidx.preference.PreferenceScreen;
-
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
@@ -38,6 +33,11 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.shadows.ShadowApplication;
 
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
 @RunWith(SettingsLibRobolectricTestRunner.class)
 public class FooterPreferenceMixinTest {
 
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index e1a602b..c53417b 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -217,4 +217,10 @@
 
     <!-- Default for Settings.System.VIBRATE_WHEN_RINGING -->
     <bool name="def_vibrate_when_ringing">false</bool>
+
+    <!-- Default for Settings.Secure.CHARGING_VIBRATION_ENABLED -->
+    <bool name="def_charging_vibration_enabled">true</bool>
+
+    <!-- Default for Settings.Secure.CHARGING_SOUNDS_ENABLED -->
+    <bool name="def_charging_sounds_enabled">true</bool>
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index f728684..9d398b5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -841,7 +841,19 @@
             WifiConfiguration config = WifiConfiguration
                     .getWifiConfigFromBackup(new DataInputStream(new ByteArrayInputStream(data)));
             if (DEBUG) Log.d(TAG, "Successfully unMarshaled WifiConfiguration ");
+            int originalApBand = config.apBand;
             mWifiManager.setWifiApConfiguration(config);
+
+            // Depending on device hardware, we may need to notify the user of a setting change for
+            // the apBand preference
+            boolean dualMode = mWifiManager.isDualModeSupported();
+            int storedApBand = mWifiManager.getWifiApConfiguration().apBand;
+            if (dualMode) {
+                if (storedApBand != originalApBand) {
+                    Log.d(TAG, "restored ap configuration requires a conversion, notify the user");
+                    mWifiManager.notifyUserOfApBandConversion();
+                }
+            }
         } catch (IOException | BackupUtils.BadVersionException e) {
             Log.e(TAG, "Failed to unMarshal SoftAPConfiguration " + e.getMessage());
         }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 77eb6c4..007e140 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -186,9 +186,21 @@
                 GlobalSettingsProto.Auto.TIME_ZONE);
         p.end(autoToken);
 
+        final long autofillToken = p.start(GlobalSettingsProto.AUTOFILL);
         dumpSetting(s, p,
                 Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
-                GlobalSettingsProto.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES);
+                GlobalSettingsProto.Autofill.COMPAT_MODE_ALLOWED_PACKAGES);
+        dumpSetting(s, p,
+                Settings.Global.AUTOFILL_LOGGING_LEVEL,
+                GlobalSettingsProto.Autofill.LOGGING_LEVEL);
+        dumpSetting(s, p,
+                Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
+                GlobalSettingsProto.Autofill.MAX_PARTITIONS_SIZE);
+        dumpSetting(s, p,
+                Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
+                GlobalSettingsProto.Autofill.MAX_VISIBLE_DATASETS);
+        p.end(autofillToken);
+
         dumpSetting(s, p,
                 Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
                 GlobalSettingsProto.BACKUP_AGENT_TIMEOUT_PARAMETERS);
@@ -1170,9 +1182,6 @@
         dumpSetting(s, p,
                 Settings.Global.CHARGING_STARTED_SOUND,
                 GlobalSettingsProto.Sounds.CHARGING_STARTED);
-        dumpSetting(s, p,
-                Settings.Global.CHARGING_SOUNDS_ENABLED,
-                GlobalSettingsProto.Sounds.CHARGING_SOUNDS_ENABLED);
         p.end(soundsToken);
 
         final long soundTriggerToken = p.start(GlobalSettingsProto.SOUND_TRIGGER);
@@ -1466,12 +1475,6 @@
         dumpSetting(s, p,
                 Settings.Global.ZEN_MODE_CONFIG_ETAG,
                 GlobalSettingsProto.Zen.MODE_CONFIG_ETAG);
-        dumpSetting(s, p,
-                Settings.Global.ZEN_DURATION,
-                GlobalSettingsProto.Zen.DURATION);
-        dumpSetting(s, p,
-                Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION,
-                GlobalSettingsProto.Zen.SHOW_ZEN_UPGRADE_NOTIFICATION);
         p.end(zenToken);
 
         dumpSetting(s, p,
@@ -1972,6 +1975,9 @@
         dumpSetting(s, p,
                 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING,
                 SecureSettingsProto.Notification.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING);
+        dumpSetting(s, p,
+                Settings.Secure.IN_CALL_NOTIFICATION_ENABLED,
+                SecureSettingsProto.Notification.IN_CALL_NOTIFICATION_ENABLED);
         p.end(notificationToken);
 
         final long packageVerifierToken = p.start(SecureSettingsProto.PACKAGE_VERIFIER);
@@ -2134,6 +2140,16 @@
         dumpSetting(s, p,
                 Settings.Secure.SMS_DEFAULT_APPLICATION,
                 SecureSettingsProto.SMS_DEFAULT_APPLICATION);
+
+        final long soundsToken = p.start(SecureSettingsProto.SOUNDS);
+        dumpSetting(s, p,
+                Settings.Secure.CHARGING_SOUNDS_ENABLED,
+                SecureSettingsProto.Sounds.CHARGING_SOUNDS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.CHARGING_VIBRATION_ENABLED,
+                SecureSettingsProto.Sounds.CHARGING_VIBRATION_ENABLED);
+        p.end(soundsToken);
+
         dumpSetting(s, p,
                 Settings.Secure.SYNC_PARENT_SOUNDS,
                 SecureSettingsProto.SYNC_PARENT_SOUNDS);
@@ -2240,6 +2256,24 @@
                 SecureSettingsProto.Launcher.SWIPE_UP_TO_SWITCH_APPS_ENABLED);
         p.end(launcherToken);
 
+        final long zenToken = p.start(SecureSettingsProto.ZEN);
+        dumpSetting(s, p,
+                Settings.Secure.ZEN_DURATION,
+                SecureSettingsProto.Zen.DURATION);
+        dumpSetting(s, p,
+                Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION,
+                SecureSettingsProto.Zen.SHOW_ZEN_UPGRADE_NOTIFICATION);
+        dumpSetting(s, p,
+                Settings.Secure.SHOW_ZEN_SETTINGS_SUGGESTION,
+                SecureSettingsProto.Zen.SHOW_ZEN_SETTINGS_SUGGESTION);
+        dumpSetting(s, p,
+                Settings.Secure.ZEN_SETTINGS_UPDATED,
+                SecureSettingsProto.Zen.SETTINGS_UPDATED);
+        dumpSetting(s, p,
+                Settings.Secure.ZEN_SETTINGS_SUGGESTION_VIEWED,
+                SecureSettingsProto.Zen.SETTINGS_SUGGESTION_VIEWED);
+        p.end(zenToken);
+
         // Please insert new settings using the same order as in SecureSettingsProto.
         p.end(token);
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 375fef8a..1c13395 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -816,7 +816,7 @@
             @Override
             public void onPackageRemoved(String packageName, int uid) {
                 synchronized (mLock) {
-                    mSettingsRegistry.onPackageRemovedLocked(packageName,
+                    mSettingsRegistry.removeSettingsForPackageLocked(packageName,
                             UserHandle.getUserId(uid));
                 }
             }
@@ -827,6 +827,14 @@
                     mSettingsRegistry.onUidRemovedLocked(uid);
                 }
             }
+
+            @Override
+            public void onPackageDataCleared(String packageName, int uid) {
+                synchronized (mLock) {
+                    mSettingsRegistry.removeSettingsForPackageLocked(packageName,
+                            UserHandle.getUserId(uid));
+                }
+            }
         };
 
         // package changes
@@ -2547,7 +2555,7 @@
             }
         }
 
-        public void onPackageRemovedLocked(String packageName, int userId) {
+        public void removeSettingsForPackageLocked(String packageName, int userId) {
             // Global and secure settings are signature protected. Apps signed
             // by the platform certificate are generally not uninstalled  and
             // the main exception is tests. We trust components signed
@@ -2556,7 +2564,7 @@
             final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);
             SettingsState systemSettings = mSettingsStates.get(systemKey);
             if (systemSettings != null) {
-                systemSettings.onPackageRemovedLocked(packageName);
+                systemSettings.removeSettingsForPackageLocked(packageName);
             }
         }
 
@@ -2935,7 +2943,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 169;
+            private static final int SETTINGS_VERSION = 171;
 
             private final int mUserId;
 
@@ -3595,7 +3603,8 @@
                 }
 
                 if (currentVersion == 156) {
-                    // Version 157: Set a default value for zen duration
+                    // Version 157: Set a default value for zen duration,
+                    // in version 169, zen duration is moved to secure settings
                     final SettingsState globalSettings = getGlobalSettingsLocked();
                     final Setting currentSetting = globalSettings.getSettingLocked(
                             Global.ZEN_DURATION);
@@ -3731,32 +3740,8 @@
                 }
 
                 if (currentVersion == 165) {
-                    // Version 165: Show zen settings suggestion and zen updated
-                    final SettingsState settings = getGlobalSettingsLocked();
-                    final Setting currentSetting = settings.getSettingLocked(
-                            Global.SHOW_ZEN_SETTINGS_SUGGESTION);
-                    if (currentSetting.isNull()) {
-                        settings.insertSettingLocked(
-                                Global.SHOW_ZEN_SETTINGS_SUGGESTION, "1",
-                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
-                    }
-
-                    final Setting currentUpdatedSetting = settings.getSettingLocked(
-                            Global.ZEN_SETTINGS_UPDATED);
-                    if (currentUpdatedSetting.isNull()) {
-                        settings.insertSettingLocked(
-                                Global.ZEN_SETTINGS_UPDATED, "0",
-                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
-                    }
-
-                    final Setting currentSettingSuggestionViewed = settings.getSettingLocked(
-                            Global.ZEN_SETTINGS_SUGGESTION_VIEWED);
-                    if (currentSettingSuggestionViewed.isNull()) {
-                        settings.insertSettingLocked(
-                                Global.ZEN_SETTINGS_SUGGESTION_VIEWED, "0",
-                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
-                    }
-
+                    // Version 165: MOVED: Show zen settings suggestion and zen updated settings
+                    // moved to secure settings and are set in version 169
                     currentVersion = 166;
                 }
 
@@ -3783,15 +3768,8 @@
                 }
 
                 if (currentVersion == 167) {
-                    // Version 167: by default, vibrate for wireless charging
-                    final SettingsState globalSettings = getGlobalSettingsLocked();
-                    final Setting currentSetting = globalSettings.getSettingLocked(
-                            Global.CHARGING_VIBRATION_ENABLED);
-                    if (currentSetting.isNull()) {
-                        globalSettings.insertSettingLocked(
-                                Global.CHARGING_VIBRATION_ENABLED, "1",
-                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
-                    }
+                    // Version 167: MOVED - Settings.Global.CHARGING_VIBRATION_ENABLED moved to
+                    // Settings.Secure.CHARGING_VIBRATION_ENABLED, set in version 170
                     currentVersion = 168;
                 }
 
@@ -3810,6 +3788,113 @@
                     currentVersion = 169;
                 }
 
+                if (currentVersion == 169) {
+                    // Version 169: Set the default value for Secure Settings ZEN_DURATION,
+                    // SHOW_ZEN_SETTINGS_SUGGESTION, ZEN_SETTINGS_UPDATE and
+                    // ZEN_SETTINGS_SUGGESTION_VIEWED
+
+                    final SettingsState globalSettings = getGlobalSettingsLocked();
+                    final Setting globalZenDuration = globalSettings.getSettingLocked(
+                            Global.ZEN_DURATION);
+
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting secureZenDuration = secureSettings.getSettingLocked(
+                            Secure.ZEN_DURATION);
+
+                    // ZEN_DURATION
+                    if (!globalZenDuration.isNull()) {
+                        secureSettings.insertSettingLocked(
+                                Secure.ZEN_DURATION, globalZenDuration.getValue(), null, false,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+
+                        // set global zen duration setting to null since it's deprecated
+                        globalSettings.insertSettingLocked(
+                                Global.ZEN_DURATION, null, null, true,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    } else if (secureZenDuration.isNull()) {
+                        String defaultZenDuration = Integer.toString(getContext()
+                                .getResources().getInteger(R.integer.def_zen_duration));
+                        secureSettings.insertSettingLocked(
+                                Secure.ZEN_DURATION, defaultZenDuration, null, true,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    // SHOW_ZEN_SETTINGS_SUGGESTION
+                    final Setting currentShowZenSettingSuggestion = secureSettings.getSettingLocked(
+                            Secure.SHOW_ZEN_SETTINGS_SUGGESTION);
+                    if (currentShowZenSettingSuggestion.isNull()) {
+                        secureSettings.insertSettingLocked(
+                                Secure.SHOW_ZEN_SETTINGS_SUGGESTION, "1",
+                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    // ZEN_SETTINGS_UPDATED
+                    final Setting currentUpdatedSetting = secureSettings.getSettingLocked(
+                            Secure.ZEN_SETTINGS_UPDATED);
+                    if (currentUpdatedSetting.isNull()) {
+                        secureSettings.insertSettingLocked(
+                                Secure.ZEN_SETTINGS_UPDATED, "0",
+                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    // ZEN_SETTINGS_SUGGESTION_VIEWED
+                    final Setting currentSettingSuggestionViewed = secureSettings.getSettingLocked(
+                            Secure.ZEN_SETTINGS_SUGGESTION_VIEWED);
+                    if (currentSettingSuggestionViewed.isNull()) {
+                        secureSettings.insertSettingLocked(
+                                Secure.ZEN_SETTINGS_SUGGESTION_VIEWED, "0",
+                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    currentVersion = 170;
+                }
+
+                if (currentVersion == 170) {
+                    // Version 170: Set the default value for Secure Settings:
+                    // CHARGING_SOUNDS_ENABLED and CHARGING_VIBRATION_ENABLED
+
+                    final SettingsState globalSettings = getGlobalSettingsLocked();
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+
+                    // CHARGING_SOUNDS_ENABLED
+                    final Setting globalChargingSoundEnabled = globalSettings.getSettingLocked(
+                            Global.CHARGING_SOUNDS_ENABLED);
+                    final Setting secureChargingSoundsEnabled = secureSettings.getSettingLocked(
+                            Secure.CHARGING_SOUNDS_ENABLED);
+
+                    if (!globalChargingSoundEnabled.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(
+                                Global.CHARGING_SOUNDS_ENABLED, null, null, true,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    } else if (secureChargingSoundsEnabled.isNull()) {
+                        String defChargingSoundsEnabled = getContext().getResources()
+                                .getBoolean(R.bool.def_charging_sounds_enabled) ? "1" : "0";
+                        secureSettings.insertSettingLocked(
+                                Secure.CHARGING_SOUNDS_ENABLED, defChargingSoundsEnabled, null,
+                                true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    // CHARGING_VIBRATION_ENABLED
+                    final Setting secureChargingVibrationEnabled = secureSettings.getSettingLocked(
+                            Secure.CHARGING_VIBRATION_ENABLED);
+
+                    if (secureChargingVibrationEnabled.isNull()) {
+                        String defChargingVibrationEnabled = getContext().getResources()
+                                .getBoolean(R.bool.def_charging_vibration_enabled) ? "1" : "0";
+                        secureSettings.insertSettingLocked(
+                                Secure.CHARGING_VIBRATION_ENABLED, defChargingVibrationEnabled,
+                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    currentVersion = 171;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 449946d..e57483a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -293,7 +293,7 @@
     }
 
     // The settings provider must hold its lock when calling here.
-    public void onPackageRemovedLocked(String packageName) {
+    public void removeSettingsForPackageLocked(String packageName) {
         boolean removedSomething = false;
 
         final int settingCount = mSettings.size();
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 8a5cb4a..da870bd 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -144,14 +144,13 @@
     <uses-permission android:name="android.permission.MANAGE_SENSORS" />
     <uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" />
     <uses-permission android:name="android.permission.MANAGE_CAMERA" />
-    <!-- Permission needed to enable/disable Bluetooth/Wifi when on permission review mode -->
-    <uses-permission
-        android:name="android.permission.MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED" />
-    <uses-permission
-        android:name="android.permission.MANAGE_WIFI_WHEN_PERMISSION_REVIEW_REQUIRED" />
+    <!-- Permission needed to enable/disable Bluetooth/Wifi -->
+    <uses-permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED" />
+    <uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
     <uses-permission android:name="android.permission.WATCH_APPOPS" />
 
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
+    <uses-permission android:name="android.permission.SUSPEND_APPS" />
 
     <application android:label="@string/app_label"
                  android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/Shell/res/values-hy/strings.xml b/packages/Shell/res/values-hy/strings.xml
index 95b6131..33f76f0 100644
--- a/packages/Shell/res/values-hy/strings.xml
+++ b/packages/Shell/res/values-hy/strings.xml
@@ -25,9 +25,9 @@
     <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Վրիպակների մասին հաշվետվությունը շուտով կստանաք հեռախոսին"</string>
     <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Ընտրեք՝ վրիպակի զեկույցն ուղարկելու համար"</string>
     <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Հպեք՝ վրիպակի զեկույցը տրամադրելու համար"</string>
-    <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Ընտրեք՝ վրիպակի զեկույցն առանց էկրանի պատկերի ուղարկելու համար կամ սպասեք էկրանի պատկերի ստեղծմանը"</string>
-    <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Հպեք՝ վրիպակի զեկույցն առանց էկրանի պատկերի ուղարկելու համար կամ սպասեք էկրանի պատկերի ստեղծմանը"</string>
-    <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Հպեք՝ վրիպակի զեկույցն առանց էկրանի պատկերի ուղարկելու համար կամ սպասեք էկրանի պատկերի ստեղծմանը"</string>
+    <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Ընտրեք՝ վրիպակի զեկույցն առանց սքրինշոթի ուղարկելու համար կամ սպասեք սքրինշոթի ստեղծմանը"</string>
+    <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Հպեք՝ վրիպակի զեկույցն առանց սքրինշոթի ուղարկելու համար կամ սպասեք սքրինշոթի ստեղծմանը"</string>
+    <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Հպեք՝ վրիպակի զեկույցն առանց սքրինշոթի ուղարկելու համար կամ սպասեք սքրինշոթի ստեղծմանը"</string>
     <string name="bugreport_confirm" msgid="5917407234515812495">"Վրիպակի զեկույցները պարունակում են տվյալներ համակարգի տարբեր մատյաններից և կարող են ներառել տեղեկություններ, որոնք դուք գաղտնի եք համարում (օրինակ՝ հավելվածի օգտագործման կամ տեղադրության մասին): Վրիպակի զեկույցները տրամադրեք միայն վստահելի մարդկանց և հավելվածներին:"</string>
     <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Այլևս ցույց չտալ"</string>
     <string name="bugreport_storage_title" msgid="5332488144740527109">"Վրիպակների հաշվետվություններ"</string>
@@ -35,9 +35,9 @@
     <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Չհաջողվեց ավելացնել վրիպակի զեկույցի մանրամասները zip ֆայլին"</string>
     <string name="bugreport_unnamed" msgid="2800582406842092709">"անանուն"</string>
     <string name="bugreport_info_action" msgid="2158204228510576227">"Մանրամասներ"</string>
-    <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Էկրանի պատկեր"</string>
-    <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Էկրանի պատկերը հաջողությամբ ստացվեց:"</string>
-    <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Չհաջողվեց ստանալ էկրանի պատկերը:"</string>
+    <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Սքրինշոթ"</string>
+    <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Սքրինշոթը հաջողությամբ ստացվեց:"</string>
+    <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Չհաջողվեց ստանալ սքրինշոթը:"</string>
     <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"<xliff:g id="ID">#%d</xliff:g> վրիպակի զեկույցի մանրամասները"</string>
     <string name="bugreport_info_name" msgid="4414036021935139527">"Ֆայլի անունը"</string>
     <string name="bugreport_info_title" msgid="2306030793918239804">"Վրիպակի զեկույցի վերնագիրը"</string>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index beb4e9e..a5616d5 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -120,6 +120,7 @@
     <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
     <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
     <uses-permission android:name="android.permission.TRUST_LISTENER" />
+    <uses-permission android:name="android.permission.USE_BIOMETRIC" />
     <uses-permission android:name="android.permission.USE_FINGERPRINT" />
     <uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
     <uses-permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" />
@@ -338,19 +339,17 @@
             android:exported="false">
         </activity>
 
-        <!-- Springboard for launching the share activity -->
-        <receiver android:name=".screenshot.GlobalScreenshot$ScreenshotActionReceiver"
-            android:process=":screenshot"
+        <!-- Springboard for launching the share and edit activity. This needs to be in the main
+             system ui process since we need to notify the status bar to dismiss the keyguard -->
+        <receiver android:name=".screenshot.GlobalScreenshot$ActionProxyReceiver"
             android:exported="false" />
 
         <!-- Callback for dismissing screenshot notification after a share target is picked -->
         <receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
-            android:process=":screenshot"
             android:exported="false" />
 
         <!-- Callback for deleting screenshot notification -->
         <receiver android:name=".screenshot.GlobalScreenshot$DeleteScreenshotReceiver"
-            android:process=":screenshot"
             android:exported="false" />
 
         <!-- started from UsbDeviceSettingsManager -->
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index e217ace..47f2cdc 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -14,6 +14,7 @@
 jaggies@google.com
 jjaggi@google.com
 juliacr@google.com
+kchyn@google.com
 madym@google.com
 ngmatthew@google.com
 roosa@google.com
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
index d80e4ff..33c5551 100644
--- a/packages/SystemUI/README.md
+++ b/packages/SystemUI/README.md
@@ -162,7 +162,7 @@
 
 Draws decorations about the screen in software (e.g. rounded corners, cutouts).
 
-### [com.android.systemui.fingerprint.FingerprintDialogImpl](/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java)
+### [com.android.systemui.biometrics.BiometricDialogImpl](/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java)
 
 Fingerprint UI.
 
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index e6452e7..fa4c8b5 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -39,6 +39,6 @@
 -keep class ** extends androidx.preference.PreferenceFragment
 -keep class com.android.systemui.tuner.*
 -keep class com.android.systemui.plugins.** {
-    public protected *;
+    *;
 }
 -keep class androidx.core.app.CoreComponentFactory
diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml
index 41a3f10..4e2937a 100644
--- a/packages/SystemUI/res-keyguard/values-af/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-af/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Voer wagwoord in om te ontsluit"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Tik PIN in om te ontsluit"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Voer jou PIN in"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Voer jou patroon in"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Voer jou wagwoord in"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Voer jou patroon in"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Voer jou wagwoord in"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Verkeerde PIN-kode."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ongeldige kaart."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Gelaai"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Volgelaai"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans vinnig"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans stadig"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Die e-SIM kan weens \'n fout nie gedeaktiveer word nie."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Het jy die patroon vergeet?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Verkeerde patroon"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Verkeerde wagwoord"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Verkeerde patroon"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Verkeerde wagwoord"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Verkeerde PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Probeer oor <xliff:g id="NUMBER">%d</xliff:g> sekondes weer.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Toestel is <xliff:g id="NUMBER_1">%d</xliff:g> uur lank nie ontsluit nie. Bevestig wagwoord.</item>
       <item quantity="one">Toestel is <xliff:g id="NUMBER_0">%d</xliff:g> uur lank nie ontsluit nie. Bevestig wagwoord.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nie herken nie"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nie herken nie"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nie herken nie"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Voer SIM-PIN in. Jy het <xliff:g id="NUMBER_1">%d</xliff:g> pogings oor.</item>
       <item quantity="one">Voer SIM-PIN in. Jy het <xliff:g id="NUMBER_0">%d</xliff:g> poging oor voordat jy jou diensverskaffer moet kontak om jou toestel te ontsluit.</item>
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index 0ee1820..c892ac3 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"ለመክፈት የይለፍ ቃል ይተይቡ"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"ለመክፈት ፒን ይተይቡ"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"የእርስዎን ፒን ያስገቡ"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"ስርዓተ-ጥለትዎን ያስገቡ"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"ይለፍ ቃልዎን ያስገቡ"</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">"ትክክል ያልሆነ ፒን  ኮድ።"</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ልክ ያልሆነ ካርድ።"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"ባትሪ ሞልቷል"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"ሙሉ በሙሉ ኃይል ተሞልቷል"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ኃይል በመሙላት ላይ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በፍጥነት ኃይልን በመሙላት ላይ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በዝግታ ኃይልን በመሙላት ላይ"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"በአንድ ስህተት ምክንያት eSIM ሊሰናከል አልቻለም።"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"አስገባ"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ስርዓተ ጥለቱን እርሳ"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"የተሳሳተ ስርዓተ ጥለት"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"የተሳሳተ ይለፍ ቃል"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"የተሳሳተ ሥርዓተ ጥለት"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"የተሳሳተ የይለፍ ቃል"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"የተሳሳተ ፒን"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">በ<xliff:g id="NUMBER">%d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።</item>
@@ -140,7 +140,8 @@
       <item quantity="one">መሣሪያው ለ<xliff:g id="NUMBER_1">%d</xliff:g> ሰዓቶች አልተከፈተም ነበር። የይለፍ ቃል ያረጋግጡ።</item>
       <item quantity="other">መሣሪያው ለ<xliff:g id="NUMBER_1">%d</xliff:g> ሰዓቶች አልተከፈተም ነበር። የይለፍ ቃል ያረጋግጡ።</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"አልታወቀም"</string>
+    <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="one">የሲም ፒን ያስገቡ። <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
       <item quantity="other">የሲም ፒን ያስገቡ። <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 9980d64..af958e5 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"اكتب كلمة المرور لإلغاء التأمين"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"اكتب رمز رقم التعريف الشخصي لإلغاء التأمين"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"‏أدخل رقم التعريف الشخصي (PIN)"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"أدخل النقش"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"أدخل كلمة المرور"</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">"رمز رقم التعريف الشخصي غير صحيح."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"بطاقة غير صالحة."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"تم الشحن"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"تم شحن البطارية بالكامل"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن سريعًا"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن ببطء"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"‏يتعذّر إيقاف eSIM بسبب خطأ."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"نسيت النقش"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"نقش خاطئ"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"كلمة مرور خاطئة"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"نقش غير صحيح"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"كلمة مرور غير صحيحة"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"رقم تعريف شخصي خاطئ"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="zero">حاول مرة أخرى خلال <xliff:g id="NUMBER">%d</xliff:g> ثانية.</item>
@@ -164,7 +164,8 @@
       <item quantity="other">لم يتم إلغاء تأمين الجهاز لمدة <xliff:g id="NUMBER_1">%d</xliff:g> ساعة. تأكيد كلمة المرور.</item>
       <item quantity="one">لم يتم إلغاء تأمين الجهاز لمدة <xliff:g id="NUMBER_0">%d</xliff:g> ساعة. تأكيد كلمة المرور.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"لم يتم التعرف عليها"</string>
+    <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="zero">‏أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة.</item>
       <item quantity="two">‏أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك محاولتان (<xliff:g id="NUMBER_1">%d</xliff:g>).</item>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index 929d89c..89c468d 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"আনলক কৰিবলৈ পাছৱৰ্ড লিখক"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"আনলক কৰিবলৈ পিন লিখক"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"আপোনাৰ পিন দিয়ক"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"আপোনাৰ আৰ্হি দিয়ক"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"আপোনাৰ পাছৱৰ্ড দিয়ক"</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">"ভুল পিন ক\'ড।"</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ব্যৱহাৰৰ অযোগ্য ছিম কাৰ্ড"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"চ্চার্জ কৰা হ\'ল"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"পূৰ্ণৰূপে চ্চাৰ্জ হৈছে"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চ্চার্জ কৰি থকা হৈছে"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • দ্ৰুত গতিৰে চ্চাৰ্জ কৰি থকা হৈছে"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • লাহে লাহে চ্চাৰ্জ কৰি থকা হৈছে"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"এটা আসোঁৱাহৰ কাৰণে ই-ছিম অক্ষম কৰিব পৰা নাযায়।"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"এণ্টাৰ বুটাম"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"আৰ্হি পাহৰিলে নেকি"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"ভুল আৰ্হি"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"ভুল পাছৱৰ্ড"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"ভুল আৰ্হি"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"ভুল পাছৱৰ্ড"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"ভুল পিন"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> ছেকেণ্ডত আকৌ চেষ্টা কৰক।</item>
@@ -140,7 +140,8 @@
       <item quantity="one">ডিভাইচটো <xliff:g id="NUMBER_1">%d</xliff:g> ঘণ্টা ধৰি আনলক কৰা হোৱা নাই। পাছৱৰ্ড নিশ্চিত কৰক।</item>
       <item quantity="other">ডিভাইচটো <xliff:g id="NUMBER_1">%d</xliff:g> ঘণ্টা ধৰি আনলক কৰা হোৱা নাই। পাছৱৰ্ড নিশ্চিত কৰক।</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"চিনাক্ত কৰিব পৰা নগ\'ল"</string>
+    <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="one">ছিমৰ পিন দিয়ক। আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g>বাৰ প্ৰয়াস কৰিব পাৰে।</item>
       <item quantity="other">ছিমৰ পিন দিয়ক। আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g>বাৰ প্ৰয়াস কৰিব পাৰে।</item>
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index 0926194..1867186 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Kilidi açmaq üçün parol daxil edin"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Kilidi açmaq üçün PIN daxil edin"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"PIN kodu daxil edin"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Modeli daxil edin"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Parol daxil edin"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Modeli daxil edin"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Şifrənizi daxil edin"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Yanlış PIN kod."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Yanlış Kart."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Enerji yığdı"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Tam dolub"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Enerji yığır"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sürətlə enerji yığır"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Yavaş enerji yığır"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM xəta səbəbi ilə deaktiv edilmədi."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Daxil edin"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Modeli unutmuşam"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Yanlış Model"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Yanlış Parol"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Yanlış model"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Yanlış parol"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Yanlış PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> saniyə ərzində yenidən cəhd edin.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Cihaz <xliff:g id="NUMBER_1">%d</xliff:g> saat kiliddən çıxarılmayıb. Parolu təsdiq edin.</item>
       <item quantity="one">Cihaz <xliff:g id="NUMBER_0">%d</xliff:g> saat kiliddən çıxarılmayıb. Parolu təsdiq edin.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Tanınmır"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Tanınmır"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Tanınmır"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">SIM PIN-ni daxil edin. <xliff:g id="NUMBER_1">%d</xliff:g> cəhdiniz qalır.</item>
       <item quantity="one">SIM PIN-ni daxil edin. Cihazınızı kiliddən çıxarmaq üçün operatorunuzla əlaqə saxlamadan öncə <xliff:g id="NUMBER_0">%d</xliff:g> cəhdiniz qalır.</item>
diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
index 67da7b8..ef0e1ce 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Unesite lozinku da biste otključali"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Unesite PIN za otključavanje"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Unesite PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Unesite šablon"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Unesite lozinku"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Unesite šablon"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Unesite lozinku"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN kôd je netačan."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nevažeća kartica."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Napunjena je"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Napunjena je u potpunosti"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Puni se"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Brzo se puni"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo se puni"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM ne može da se onemogući zbog greške."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Zaboravio/la sam šablon"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Pogrešan šablon"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Pogrešna lozinka"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Pogrešan šablon"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Pogrešna lozinka"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Pogrešan PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Probajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> sekundu.</item>
@@ -146,7 +146,8 @@
       <item quantity="few">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sata. Potvrdite lozinku.</item>
       <item quantity="other">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite lozinku.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nije prepoznat"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nije prepoznat"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nije prepoznat"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
       <item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index 5b11a09..873a87b 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"Увядзіце ўзор разблакіроўкі"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Увядзіце пароль"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Зараджаны"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Акумулятар поўнасцю зараджаны"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе зарадка"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе хуткая зарадка"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе павольная зарадка"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Немагчыма адключыць eSIM-карту з-за памылкі."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Увесці"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Забыў(-ла) узор"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Няправільны ўзор"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Няправільны пароль"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Няправільны ўзор разблакіроўкі"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Няправільны пароль"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Няправільны PIN-код"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Паўтарыце спробу праз <xliff:g id="NUMBER">%d</xliff:g> секунду.</item>
@@ -152,7 +152,8 @@
       <item quantity="many">Прылада не была разблакіравана на працягу <xliff:g id="NUMBER_1">%d</xliff:g> гадзін. Увядзіце пароль.</item>
       <item quantity="other">Прылада не была разблакіравана на працягу <xliff:g id="NUMBER_1">%d</xliff:g> гадзіны. Увядзіце пароль.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Не распазнаны"</string>
+    <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="one">Увядзіце PIN-код SIM-карты. У вас засталася <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
       <item quantity="few">Увядзіце PIN-код SIM-карты. У вас засталося <xliff:g id="NUMBER_1">%d</xliff:g> спробы.</item>
diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml
index 663d5f0..3725a5e 100644
--- a/packages/SystemUI/res-keyguard/values-bg/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Въведете парола, за да отключите"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Въведете ПИН кода, за да отключите"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Въведете ПИН кода си"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Въведете фигурата си"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Въведете паролата си"</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">"Неправилен ПИН код."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Картата е невалидна."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Заредена"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Напълно заредено"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бързо"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бавно"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Електронната SIM карта не може да бъде деактивирана поради грешка."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"„Enter“"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Забравена фигура"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Грешна фигура"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Грешна парола"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Грешна фигура"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Грешна парола"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Грешен ПИН код"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Опитайте отново след <xliff:g id="NUMBER">%d</xliff:g> секунди.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Устройството не е отключвано от <xliff:g id="NUMBER_1">%d</xliff:g> часа. Потвърдете паролата.</item>
       <item quantity="one">Устройството не е отключвано от <xliff:g id="NUMBER_0">%d</xliff:g> час. Потвърдете паролата.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Не е разпознато"</string>
+    <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 картата – остават ви <xliff:g id="NUMBER_1">%d</xliff:g> опита.</item>
       <item quantity="one">Въведете ПИН кода за SIM картата – остава ви <xliff:g id="NUMBER_0">%d</xliff:g> опит, преди да се наложи да се свържете с оператора си, за да отключите устройството.</item>
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index 7a3b575..ff925f8 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"আনলক করতে পাসওয়ার্ড লিখুন"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"আনলক করতে পিন লিখুন"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"পিন লিখুন"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"প্যাটার্ন আঁকুন"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"পাসওয়ার্ড লিখুন"</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">"ভুল পিন কোড দেওয়া হয়েছে।"</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ভুল কার্ড।"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"চার্জ হয়েছে"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"সম্পূর্ণ চার্জ আছে"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চার্জ হচ্ছে"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • দ্রুত চার্জ হচ্ছে"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ধীরে চার্জ হচ্ছে"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"একটি সমস্যার কারণে ই-সিমটি বন্ধ করা যাচ্ছে না।"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"এন্টার"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"প্যাটার্ন ভুলে গেছি"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"ভুল প্যাটার্ন"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"ভুল পাসওয়ার্ড"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"ভুল প্যাটার্ন"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"ভুল পাসওয়ার্ড"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"ভুল পিন"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন।</item>
@@ -140,7 +140,8 @@
       <item quantity="one">ডিভাইসটি <xliff:g id="NUMBER_1">%d</xliff:g> ঘণ্টা ধরে আনলক করা হয় নি। পাসওয়ার্ড নিশ্চিত করুন।</item>
       <item quantity="other">ডিভাইসটি <xliff:g id="NUMBER_1">%d</xliff:g> ঘণ্টা ধরে আনলক করা হয় নি। পাসওয়ার্ড নিশ্চিত করুন।</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"স্বীকৃত নয়"</string>
+    <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="one">সিমের পিন লিখুন। আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন।</item>
       <item quantity="other">সিমের পিন লিখুন। আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন।</item>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index 319014e..9984061 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Upišite lozinku za otključavanje"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Upišite PIN za otključavanje"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Unesite svoj PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Unesite svoj uzorak"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Unesite svoju lozinku"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Unesite uzorak"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Unesite lozinku"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Pogrešan PIN."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nevažeća kartica."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Napunjeno"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Potpuno napunjen"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Brzo punjenje"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo punjenje"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM nije moguće onemogućiti zbog greške."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Zaboravili ste uzorak?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Pogrešan uzorak"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Pogrešna lozinka"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Pogrešan uzorak"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Pogrešna lozinka"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Pogrešan PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> sekundu.</item>
@@ -146,7 +146,8 @@
       <item quantity="few">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sata. Potvrdite lozinku.</item>
       <item quantity="other">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite lozinku.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nije prepoznat"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nije prepoznato"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nije prepoznato"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
       <item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index 7b53abe..3a734b1 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Escriu la contrasenya per desbloquejar"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Escriu el PIN per desbloquejar"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Introdueix el PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Introdueix el patró"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Introdueix la contrasenya"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Introdueix el patró"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Introdueix la contrasenya"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"El codi PIN no és correcte."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"La targeta no és vàlida."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Bateria carregada"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Completament carregada"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant ràpidament"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant lentament"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"S\'ha produït un error i no es pot desactivar l\'eSIM."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Retorn"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"He oblidat el patró"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"El patró no és correcte"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"La contrasenya no és correcta"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Patró incorrecte"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Contrasenya incorrecta"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"El PIN no és correcte"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Torna-ho a provar d\'aquí a <xliff:g id="NUMBER">%d</xliff:g> segons.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Fa <xliff:g id="NUMBER_1">%d</xliff:g> hores que no es desbloqueja el dispositiu. Confirma la contrasenya.</item>
       <item quantity="one">Fa <xliff:g id="NUMBER_0">%d</xliff:g> hora que no es desbloqueja el dispositiu. Confirma la contrasenya.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"No s\'ha reconegut"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"No s\'ha reconegut"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"No s\'ha reconegut"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Introdueix el PIN de la SIM. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents.</item>
       <item quantity="one">Introdueix el PIN de la SIM. Et queda <xliff:g id="NUMBER_0">%d</xliff:g> intent; si no l\'encertes, contacta amb l\'operador de telefonia mòbil per desbloquejar el dispositiu.</item>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index d827b25..1bfc294 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Zadejte heslo pro odemknutí"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Zadejte kód PIN pro odemknutí"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Zadejte PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Zadejte gesto"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Zadejte heslo"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Zadejte gesto"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Zadejte heslo"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Nesprávný kód PIN."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Neplatná karta."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Nabito"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Plně nabito"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíjení"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Rychlé nabíjení"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pomalé nabíjení"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM kartu kvůli chybě nelze deaktivovat."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Zapomenuté gesto"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Nesprávné gesto"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Nesprávné heslo"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Nesprávné gesto"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Špatné heslo"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Nesprávný kód PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="few">Zkuste to znovu za <xliff:g id="NUMBER">%d</xliff:g> sekundy.</item>
@@ -152,7 +152,8 @@
       <item quantity="other">Zařízení již <xliff:g id="NUMBER_1">%d</xliff:g> hodin nebylo odemknuto. Zadejte heslo.</item>
       <item quantity="one">Zařízení již <xliff:g id="NUMBER_0">%d</xliff:g> hodinu nebylo odemknuto. Zadejte heslo.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nerozpoznáno"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nerozpoznáno"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nerozpoznáno"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="few">Zadejte PIN SIM karty. Zbývají <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
       <item quantity="many">Zadejte PIN SIM karty. Zbývá <xliff:g id="NUMBER_1">%d</xliff:g> pokusu.</item>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index b65087a..b3b9732 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Angiv adgangskoden for at låse op"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Angiv pinkoden for at låse op"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Angiv din pinkode"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Angiv dit mønster"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Angiv din adgangskode"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Angiv dit mønster"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Angiv din adgangskode"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Forkert pinkode."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ugyldigt kort."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Opladet"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Fuldt opladet"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader hurtigt"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader langsomt"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM kan ikke deaktiveres på grund af en fejl."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Har du glemt mønsteret?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Forkert mønster"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Forkert adgangskode"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Forkert mønster"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Forkert adgangskode"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Forkert pinkode"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Prøv igen om <xliff:g id="NUMBER">%d</xliff:g> sekund.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">Enheden blev sidst låst op for <xliff:g id="NUMBER_1">%d</xliff:g> time siden. Bekræft adgangskoden.</item>
       <item quantity="other">Enheden blev sidst låst op for <xliff:g id="NUMBER_1">%d</xliff:g> timer siden. Bekræft adgangskoden.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Ikke genkendt"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Ikke genkendt"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Ikke genkendt"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="one">Angiv pinkoden til SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
       <item quantity="other">Angiv pinkoden til SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index 8a1de2c..83d7da5c 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Bitte gib das Passwort zum Entsperren ein"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Bitte gib die PIN zum Entsperren ein"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Gib deine PIN ein"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Gib dein Muster ein"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Gib dein Passwort ein"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Muster eingeben"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Passwort eingeben"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Falscher PIN-Code."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ungültige Karte."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Aufgeladen"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Vollständig aufgeladen"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird geladen"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird schnell geladen"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird langsam geladen"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Die eSim kann aufgrund eines Fehlers nicht deaktiviert werden."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Eingabe"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Muster vergessen"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Falsches Muster"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Falsches Passwort"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Falsches Muster"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Falsches Passwort"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Falsche PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">In <xliff:g id="NUMBER">%d</xliff:g> Sekunden noch einmal versuchen.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Das Gerät wurde seit <xliff:g id="NUMBER_1">%d</xliff:g> Stunden nicht mehr entsperrt. Bitte bestätige das Passwort.</item>
       <item quantity="one">Das Gerät wurde seit <xliff:g id="NUMBER_0">%d</xliff:g> Stunde nicht mehr entsperrt. Bitte bestätige das Passwort.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nicht erkannt"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nicht erkannt"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nicht erkannt"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Gib die PIN für die SIM-Karte ein. Du hast noch <xliff:g id="NUMBER_1">%d</xliff:g> Versuche.</item>
       <item quantity="one">Gib die PIN für die SIM-Karte ein. Du hast noch <xliff:g id="NUMBER_0">%d</xliff:g> Versuch, bevor das Gerät nur noch vom Mobilfunkanbieter entsperrt werden kann.</item>
diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml
index 2eee705..89b05ec 100644
--- a/packages/SystemUI/res-keyguard/values-el/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-el/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"Εισαγάγετε το μοτίβο σας"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Εισαγάγετε τον κωδικό πρόσβ."</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Φορτίστηκε"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Πλήρως φορτισμένη"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Φόρτιση"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Γρήγορη φόρτιση"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Αργή φόρτιση"</string>
@@ -46,7 +46,7 @@
     <string name="keyguard_missing_sim_instructions" msgid="7350295932015220392">"Τοποθετήστε μια κάρτα SIM."</string>
     <string name="keyguard_missing_sim_instructions_long" msgid="589889372883904477">"Η κάρτα SIM δεν υπάρχει ή δεν είναι δυνατή η ανάγνωσή της. Τοποθετήστε μια κάρτα SIM."</string>
     <string name="keyguard_permanent_disabled_sim_message_short" msgid="654102080186420706">"Η κάρτα SIM δεν μπορεί να χρησιμοποιηθεί."</string>
-    <string name="keyguard_permanent_disabled_sim_instructions" msgid="4683178224791318347">"Η κάρτα SIM έχει απενεργοποιηθεί οριστικά.\n Επικοινωνήστε με τον παροχέα υπηρεσιών ασύρματου δικτύου για να λάβετε μια νέα κάρτα SIM."</string>
+    <string name="keyguard_permanent_disabled_sim_instructions" msgid="4683178224791318347">"Η κάρτα SIM έχει απενεργοποιηθεί οριστικά.\n Επικοινωνήστε με τον πάροχο υπηρεσιών ασύρματου δικτύου για να λάβετε μια νέα κάρτα SIM."</string>
     <string name="keyguard_sim_locked_message" msgid="953766009432168127">"Η κάρτα SIM είναι κλειδωμένη."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="1772789643694942073">"Η κάρτα SIM είναι κλειδωμένη με κωδικό PUK."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="3586601150825821675">"Ξεκλείδωμα κάρτας SIM…"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Δεν είναι δυνατή η απενεργοποίηση της eSIM, εξαιτίας κάποιου σφάλματος."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Ξεχάσατε το μοτίβο"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Λάθος μοτίβο"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Λανθασμένος κωδικός πρόσβασης"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Λανθασμένο μοτίβο"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Λανθασμένος κωδικός πρόσβασης"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Λανθασμένο PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Δοκιμάστε ξανά σε <xliff:g id="NUMBER">%d</xliff:g> δευτερόλεπτα.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Η συσκευή δεν έχει ξεκλειδωθεί εδώ και <xliff:g id="NUMBER_1">%d</xliff:g> ώρες. Επιβεβαιώστε τον κωδικό πρόσβασης.</item>
       <item quantity="one">Η συσκευή δεν έχει ξεκλειδωθεί εδώ και <xliff:g id="NUMBER_0">%d</xliff:g> ώρα. Επιβεβαιώστε τον κωδικό πρόσβασης.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Δεν αναγνωρίστηκε"</string>
+    <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">Εισαγάγετε τον αριθμό PIN της κάρτας SIM. Απομένουν άλλες <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες.</item>
       <item quantity="one">Εισαγάγετε τον αριθμό PIN της κάρτας SIM. Απομένει άλλη <xliff:g id="NUMBER_0">%d</xliff:g> προσπάθεια. Στη συνέχεια, θα πρέπει να επικοινωνήσετε με τον πάροχο κινητής τηλεφωνίας, για να ξεκλειδώσετε τη συσκευή.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index 44ef5524..415e3de 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Type password to unlock"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Type PIN to unlock"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Enter your PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Enter your pattern"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Enter your password"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Enter your pattern"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Enter your password"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Incorrect PIN code."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Invalid card."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Charged"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Fully charged"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"The eSIM can’t be disabled due to an error."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Forgotten Pattern"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Wrong Pattern"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Wrong Password"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Wrong pattern"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Wrong password"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Wrong PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Try again in <xliff:g id="NUMBER">%d</xliff:g> seconds.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm password.</item>
       <item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognised"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Not recognised"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Not recognised"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
       <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index 1bddc86..56a4bcc 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Type password to unlock"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Type PIN to unlock"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Enter your PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Enter your pattern"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Enter your password"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Enter your pattern"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Enter your password"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Incorrect PIN code."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Invalid card."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Charged"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Fully charged"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"The eSIM can’t be disabled due to an error."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Forgotten Pattern"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Wrong Pattern"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Wrong Password"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Wrong pattern"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Wrong password"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Wrong PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Try again in <xliff:g id="NUMBER">%d</xliff:g> seconds.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm password.</item>
       <item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognised"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Not recognised"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Not recognised"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
       <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index 44ef5524..415e3de 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Type password to unlock"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Type PIN to unlock"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Enter your PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Enter your pattern"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Enter your password"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Enter your pattern"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Enter your password"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Incorrect PIN code."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Invalid card."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Charged"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Fully charged"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"The eSIM can’t be disabled due to an error."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Forgotten Pattern"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Wrong Pattern"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Wrong Password"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Wrong pattern"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Wrong password"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Wrong PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Try again in <xliff:g id="NUMBER">%d</xliff:g> seconds.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm password.</item>
       <item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognised"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Not recognised"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Not recognised"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
       <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index 44ef5524..415e3de 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Type password to unlock"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Type PIN to unlock"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Enter your PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Enter your pattern"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Enter your password"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Enter your pattern"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Enter your password"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Incorrect PIN code."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Invalid card."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Charged"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Fully charged"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"The eSIM can’t be disabled due to an error."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Forgotten Pattern"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Wrong Pattern"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Wrong Password"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Wrong pattern"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Wrong password"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Wrong PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Try again in <xliff:g id="NUMBER">%d</xliff:g> seconds.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm password.</item>
       <item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognised"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Not recognised"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Not recognised"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
       <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
index 987d983..f3b398d 100644
--- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‏‏‎‎‎‎‎‎‏‏‏‎‎‎‎Type password to unlock‎‏‎‎‏‎"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‏‏‎‎Type PIN to unlock‎‏‎‎‏‎"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‎‎‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‏‏‏‎Enter your PIN‎‏‎‎‏‎"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‏‏‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‎Enter your Pattern‎‏‎‎‏‎"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎Enter your Password‎‏‎‎‏‎"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‎‎‏‎Enter your pattern‎‏‎‎‏‎"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‏‏‏‎‎‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‏‎‎‏‎‏‎‎‎‏‏‏‎‎‏‎‏‎‎Enter your password‎‏‎‎‏‎"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‏‏‎‏‏‎‎‎‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎Incorrect PIN code.‎‏‎‎‏‎"</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎Invalid Card.‎‏‎‎‏‎"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‎‎‏‎‏‎‏‎Charged‎‏‎‎‏‎"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎‏‎Fully charged‎‏‎‎‏‎"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Charging‎‏‎‎‏‎"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Charging rapidly‎‏‎‎‏‎"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‎‎‎‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Charging slowly‎‏‎‎‏‎"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‏‏‎The eSIM can’t be disabled due to an error.‎‏‎‎‏‎"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‏‏‏‎‎‎Enter‎‏‎‎‏‎"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎Forgot Pattern‎‏‎‎‏‎"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎Wrong Pattern‎‏‎‎‏‎"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‎‏‏‏‏‏‎‎‏‎‏‎‎‏‏‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‏‏‎‏‎Wrong Password‎‏‎‎‏‎"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‏‎‎‎Wrong pattern‎‏‎‎‏‎"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎Wrong password‎‏‎‎‏‎"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎‏‎‏‎‎‎‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎Wrong PIN‎‏‎‎‏‎"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‎‏‎‎‎‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‏‏‎‎‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‏‎‏‏‎Try again in ‎‏‎‎‏‏‎<xliff:g id="NUMBER">%d</xliff:g>‎‏‎‎‏‏‏‎ seconds.‎‏‎‎‏‎</item>
@@ -140,7 +140,8 @@
       <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‏‎Device hasn\'t been unlocked for ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%d</xliff:g>‎‏‎‎‏‏‏‎ hours. Confirm password.‎‏‎‎‏‎</item>
       <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‏‎Device hasn\'t been unlocked for ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%d</xliff:g>‎‏‎‎‏‏‏‎ hour. Confirm password.‎‏‎‎‏‎</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‏‎‎‏‏‏‏‎‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‎Not recognized‎‏‎‎‏‎"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎‏‎‎Not recognized‎‏‎‎‏‎"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎‎‎‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎Not recognized‎‏‎‎‏‎"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‎Enter SIM PIN. You have ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempts.‎‏‎‎‏‎</item>
       <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‎Enter SIM PIN. You have ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempt before you must contact your carrier to unlock your device.‎‏‎‎‏‎</item>
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index 0583d4b..e56fa6b 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Ingresa la contraseña para desbloquearlo"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Ingresa el PIN para desbloquearlo"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Ingresa tu PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Ingresa tu patrón"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Ingresa tu contraseña"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Ingresa tu patrón"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Ingresa tu contraseña"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Código PIN incorrecto"</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Tarjeta no válida"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Cargada"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Carga completa"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rápidamente"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"No se puede inhabilitar la eSIM debido a un error."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Intro"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"¿Olvidaste el patrón?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Patrón incorrecto"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Contraseña incorrecta"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Patrón incorrecto"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Contraseña incorrecta"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN incorrecto"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Vuelve a intentarlo en <xliff:g id="NUMBER">%d</xliff:g> segundos.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Hace <xliff:g id="NUMBER_1">%d</xliff:g> horas que no se desbloquea el dispositivo. Confirma la contraseña.</item>
       <item quantity="one">Hace <xliff:g id="NUMBER_0">%d</xliff:g> hora que no se desbloquea el dispositivo. Confirma la contraseña.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"No se reconoció"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"No se reconoció"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"No se reconoció"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Ingresa el PIN de la SIM. Quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos más.</item>
       <item quantity="one">Ingresa el PIN de la SIM. Queda <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que debas comunicarte con tu proveedor para desbloquear el dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index 2d6721d..8a755b4 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Escribe la contraseña para desbloquear"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Escribe el código PIN para desbloquear"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Introduce tu PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Introduce tu patrón"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Introduce tu contraseña"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Introduce tu patrón"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Introduce tu contraseña"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"El código PIN es incorrecto."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Tarjeta no válida."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Cargada"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Carga completa"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rápidamente"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"No se puede mostrar la tarjeta eSIM debido a un error."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Intro"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"¿Has olvidado el patrón?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Patrón incorrecto"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Contraseña incorrecta"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Patrón incorrecto"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Contraseña incorrecta"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN incorrecto"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Vuelve a intentarlo en <xliff:g id="NUMBER">%d</xliff:g> segundos.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">El dispositivo no se ha desbloqueado durante <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirma la contraseña.</item>
       <item quantity="one">El dispositivo no se ha desbloqueado durante <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirma la contraseña.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"No reconocido"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"No se reconoce"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"No se reconoce"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Introduce el PIN de la tarjeta SIM. Te quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
       <item quantity="one">Introduce el PIN de la tarjeta SIM. Te queda <xliff:g id="NUMBER_0">%d</xliff:g> intento para tener que ponerte en contacto con tu operador para desbloquear el dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index ac37c5c..a19cc23 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Avamiseks sisestage parool"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Avamiseks sisestage PIN-kood"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Sisestage PIN-kood"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Sisestage muster"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Sisestage parool"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Sisestage muster"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Sisestage parool"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Vale PIN-kood."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Kehtetu kaart."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Laetud"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Täielikult laetud"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laadimine"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kiirlaadimine"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Aeglane laadimine"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Vea tõttu ei saa eSIM-kaarte keelata."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Sisesta"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Unustasin mustri"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Vale muster"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Vale parool"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Vale muster"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Vale parool"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Vale PIN-kood"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Proovige uuesti <xliff:g id="NUMBER">%d</xliff:g> sekundi pärast.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Seadet pole avatud <xliff:g id="NUMBER_1">%d</xliff:g> tundi. Kinnitage parool.</item>
       <item quantity="one">Seadet pole avatud <xliff:g id="NUMBER_0">%d</xliff:g> tund. Kinnitage parool.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Ei tuvastatud"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Ei tuvastatud"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Ei tuvastatud"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Sisestage SIM-kaardi PIN-kood. Jäänud on <xliff:g id="NUMBER_1">%d</xliff:g> katset.</item>
       <item quantity="one">Sisestage SIM-kaardi PIN-kood. Jäänud on <xliff:g id="NUMBER_0">%d</xliff:g> katse enne, kui peate seadme avamiseks ühendust võtma operaatoriga.</item>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 06bb32b..b063107 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Idatzi desblokeatzeko pasahitza"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Idatzi desblokeatzeko PIN kodea"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Idatzi PIN kodea"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Marraztu eredua"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Idatzi pasahitza"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Marraztu eredua"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Idatzi pasahitza"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN kode hori ez da zuzena."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Txartelak ez du balio."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Kargatuta"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Erabat kargatuta"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kargatzen"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bizkor kargatzen"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mantso kargatzen"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Errore bat gertatu da eta ezin da desgaitu eSIM txartela."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Sartu"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Eredua ahaztu zaizu"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Eredu hori ez da zuzena"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Pasahitz hori ez da zuzena"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Eredua ez da zuzena"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Pasahitza ez da zuzena"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN kode hori ez da zuzena"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Saiatu berriro <xliff:g id="NUMBER">%d</xliff:g> segundo igarotakoan.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Gailua ez da desblokeatu <xliff:g id="NUMBER_1">%d</xliff:g> orduz. Berretsi pasahitza.</item>
       <item quantity="one">Gailua ez da desblokeatu <xliff:g id="NUMBER_0">%d</xliff:g> orduz. Berretsi pasahitza.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Ez da ezagutu"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Ez da ezagutu"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Ez da ezagutu"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Idatzi SIM txartelaren PIN kodea. <xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu.</item>
       <item quantity="one">Idatzi SIM txartelaren PIN kodea. <xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu; oker idatziz gero, operadoreari eskatu beharko diozu gailua desblokeatzeko.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index f273d6e..db7923d 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"برای بازکردن قفل، گذرواژه را وارد کنید"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"برای بازکردن قفل، پین را تایپ کنید"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"پین را وارد کنید"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"الگویتان را وارد کنید"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"گذرواژه‌تان را وارد کنید"</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">"کد پین اشتباه است."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"کارت نامعتبر"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"شارژ کامل شد"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"شارژ کامل است"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ شدن"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ سریع"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ آهسته"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"به دلیل بروز خطا، سیم‌کارت داخلی غیرفعال نشد."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"الگو را فراموش کرده‌اید"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"الگوی اشتباه"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"گذرواژه اشتباه"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"الگو اشتباه است"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"گذرواژه اشتباه است"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"پین اشتباه"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> ثانیه دیگر دوباره امتحان کنید.</item>
@@ -99,8 +99,8 @@
     <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="2162434417489128282">"<xliff:g id="NUMBER_0">%1$d</xliff:g> تلاش ناموفق برای باز کردن قفل تلفن داشته‌اید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، نمایه کاری پاک می‌شود که با آن همه داده‌های نمایه حذف می‌شود."</string>
     <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="8966727588974691544">"<xliff:g id="NUMBER">%d</xliff:g> تلاش ناموفق برای باز کردن قفل رایانه لوحی داشته‌اید. نمایه کاری پاک می‌شود که با آن همه داده‌های نمایه حذف می‌شود."</string>
     <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="8476407539834855">"<xliff:g id="NUMBER">%d</xliff:g> تلاش ناموفق برای باز کردن قفل تلفن داشته‌اید. نمایه کاری پاک می‌شود که با آن همه داده‌های نمایه حذف می‌شود."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="956706236554092172">"‏شما الگوی باز کردن قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‎اید. بعد از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته می‎شود که با استفاده از یک حساب رایانامه قفل رایانه لوحی خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="8364140853305528449">"‏شما الگوی باز کردن قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‌اید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته می‎شود که با استفاده از یک حساب رایانامه قفل تلفن را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="956706236554092172">"‏شما الگوی باز کردن قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‎اید. بعد از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته می‎شود که با استفاده از یک حساب ایمیل قفل رایانه لوحی خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="8364140853305528449">"‏شما الگوی باز کردن قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‌اید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته می‎شود که با استفاده از یک حساب ایمیل قفل تلفن را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
     <string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"کد پین سیم‌کارت اشتباه است، اکنون برای باز کردن قفل دستگاهتان باید با شرکت مخابراتی تماس بگیرید."</string>
     <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="4314341367727055967">
       <item quantity="one">کد پین سیم‌کارت اشتباه است، <xliff:g id="NUMBER_1">%d</xliff:g> بار دیگر می‌توانید تلاش کنید.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">قفل دستگاه <xliff:g id="NUMBER_1">%d</xliff:g> ساعت باز نشده است. گذرواژه را تأیید کنید.</item>
       <item quantity="other">قفل دستگاه <xliff:g id="NUMBER_1">%d</xliff:g> ساعت باز نشده است. گذرواژه را تأیید کنید.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"شناسایی نشد"</string>
+    <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="one">پین سیم‌کارت را وارد کنید. <xliff:g id="NUMBER_1">%d</xliff:g> تلاش دیگری باقی مانده است.</item>
       <item quantity="other">پین سیم‌کارت را وارد کنید. <xliff:g id="NUMBER_1">%d</xliff:g> تلاش دیگری باقی مانده است.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index 5267151..2bd8314 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Poista lukitus antamalla salasana."</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Poista lukitus antamalla PIN-koodi."</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Syötä PIN-koodi"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Piirrä kuvio"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Kirjoita salasana"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Piirrä kuvio"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Kirjoita salasana"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Väärä PIN-koodi"</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Virheellinen kortti"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Ladattu"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Täyteen ladattu"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan nopeasti"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan hitaasti"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Tapahtui virhe, eikä eSIMiä voitu poistaa käytöstä."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Unohtunut kuvio"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Väärä kuvio"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Väärä salasana"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Väärä kuvio"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Väärä salasana"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Väärä PIN-koodi"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Yritä uudelleen <xliff:g id="NUMBER">%d</xliff:g> sekunnin kuluttua.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Laitteen lukitusta ei ole avattu <xliff:g id="NUMBER_1">%d</xliff:g> tuntiin. Vahvista salasana.</item>
       <item quantity="one">Laitteen lukitusta ei ole avattu <xliff:g id="NUMBER_0">%d</xliff:g> tuntiin. Vahvista salasana.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Ei tunnistettu"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Ei tunnistettu"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Ei tunnistettu"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Anna SIM-kortin PIN-koodi. Sinulla on <xliff:g id="NUMBER_1">%d</xliff:g> yritystä jäljellä.</item>
       <item quantity="one">Anna SIM-kortin PIN-koodi. <xliff:g id="NUMBER_0">%d</xliff:g> yrityksen jälkeen laite lukittuu, ja vain operaattori voi avata sen.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index 742e1eb..9b0e269 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Entrez le mot de passe pour déverrouiller le clavier."</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Entrez le NIP pour déverrouiller le clavier."</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Entrez votre NIP"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Entrez votre schéma"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Entrez votre mot de passe"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Entrez votre schéma"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Entrez votre mot de passe"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"NIP erroné."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Cette carte n\'est pas valide."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Chargé"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Complètement chargé"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"En recharge : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"En recharge rapide : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"En recharge lente : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"La carte eSIM ne peut pas être réinitialisée à cause d\'une erreur."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Entrée"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"J\'ai oublié le schéma"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Schéma incorrect"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Mot de passe incorrect"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Schéma incorrect"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Mot de passe incorrect"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"NIP incorrect"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Réessayer dans <xliff:g id="NUMBER">%d</xliff:g> seconde.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heure. Confirmez le mot de passe.</item>
       <item quantity="other">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heures. Confirmez le mot de passe.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Doigt non reconnu"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Doigt non reconnu"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Doigt non reconnu"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="one">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative.</item>
       <item quantity="other">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index 9ada218..d5e36ce 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Saisissez le mot de passe pour déverrouiller le clavier"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Saisissez le code pour déverrouiller le clavier"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Saisissez le code d\'accès"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Tracez le schéma"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Saisissez le mot de passe"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Tracez le schéma"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Saisissez votre mot de passe"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Le code est incorrect."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Carte non valide."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Chargé"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Complètement chargée"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Rechargement…"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Rechargement rapide…"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Rechargement lent…"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Impossible de désactiver la carte eSIM en raison d\'une erreur."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Entrée"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"J\'ai oublié le schéma"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Schéma incorrect"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Mot de passe incorrect"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Schéma incorrect"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Mot de passe incorrect"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Code incorrect"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Réessayez dans <xliff:g id="NUMBER">%d</xliff:g> seconde.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heure. Confirmez le mot de passe.</item>
       <item quantity="other">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heures. Confirmez le mot de passe.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Non reconnu"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Non reconnu"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Non reconnu"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="one">Saisissez le code de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentative restante.</item>
       <item quantity="other">Saisissez le code de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentatives restantes.</item>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index 4a8fe8a..756fc27 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Escribe o contrasinal para desbloquear"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Escribe o PIN para desbloquear"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Introduce o teu PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Introduce o teu padrón"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Introduce o teu contrasinal"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Introduce o padrón"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Introduce o contrasinal"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Código PIN incorrecto"</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"A tarxeta non é válida."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Cargada"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Batería totalmente cargada"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rapidamente"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"A eSIM non se puido desactivar debido a un erro."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Intro"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Esqueciches o padrón"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Padrón incorrecto"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Contrasinal incorrecto"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"O padrón é incorrecto"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"O contrasinal é incorrecto"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN incorrecto"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Téntao de novo dentro de <xliff:g id="NUMBER">%d</xliff:g> segundos.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">O dispositivo non se desbloqueou durante <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirma o contrasinal.</item>
       <item quantity="one">O dispositivo non se desbloqueou durante <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirma o contrasinal.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Non se recoñece"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Non se recoñeceu"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Non se recoñeceu"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Introduce o código PIN da SIM. Quédanche <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
       <item quantity="one">Introduce o código PIN da SIM. Quédache <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que teñas que contactar co operador para desbloquear o dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index 6a10af5..fb3a14b 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"અનલૉક કરવા માટે પાસવર્ડ લખો"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"અનલૉક કરવા માટે પિન લખો"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"તમારો પિન દાખલ કરો"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"તમારી પૅટર્ન દાખલ કરો"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"તમારો પાસવર્ડ દાખલ કરો"</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">"ખોટો પિન કોડ."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"અમાન્ય કાર્ડ."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"ચાર્જ થઈ ગયું"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"સંપૂર્ણપણે ચાર્જ થયેલ"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જિંગ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ઝડપથી ચાર્જિંગ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ધીમેથી ચાર્જિંગ"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"એક ભૂલને લીધે ઇ-સિમ બંધ કરી શકાતું નથી."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"દાખલ કરો"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"પૅટર્ન ભૂલી ગયાં"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"ખોટી પૅટર્ન"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"ખોટો પાસવર્ડ"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"ખોટી પૅટર્ન"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"ખોટો પાસવર્ડ"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"ખોટો પિન"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> સેકન્ડમાં ફરી પ્રયાસ કરો.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">ઉપકરણને <xliff:g id="NUMBER_1">%d</xliff:g> કલાક માટે અનલૉક કરવામાં આવ્યું નથી. પાસવર્ડની પુષ્ટિ કરો.</item>
       <item quantity="other">ઉપકરણને <xliff:g id="NUMBER_1">%d</xliff:g> કલાક માટે અનલૉક કરવામાં આવ્યું નથી. પાસવર્ડની પુષ્ટિ કરો.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"ઓળખાયેલ નથી"</string>
+    <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="one">સિમનો પિન દાખલ કરો, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસ બાકી છે.</item>
       <item quantity="other">સિમનો પિન દાખલ કરો, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસો બાકી છે.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index 553f663..7bfc635 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"अनलॉक करने के लिए पासवर्ड लिखें"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"अनलॉक करने के लिए पिन लिखें"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"अपना पिन डालें"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"अपना पैटर्न डालें"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"अपना पासवर्ड डालें"</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">"गलत पिन कोड."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"गलत कार्ड."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"चार्ज हो गई है"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"पूरी तरह चार्ज हो गया"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज हो रहा है"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • तेज़ चार्ज हो रहा है"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • धीरे चार्ज हो रहा है"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"किसी गड़बड़ी की वजह से ई-सिम बंद नहीं किया जा सकता."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"पैटर्न भूल गए हैं"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"गलत पैटर्न"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"गलत पासवर्ड"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"डाला गया पैटर्न गलत है"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"डाला गया पासवर्ड गलत है"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"गलत पिन"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> सेकंड में फिर से कोशिश करें.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">डिवाइस को <xliff:g id="NUMBER_1">%d</xliff:g> घंटों से अनलॉक नहीं किया गया है. पासवर्ड की पुष्टि करें.</item>
       <item quantity="other">डिवाइस को <xliff:g id="NUMBER_1">%d</xliff:g> घंटों से अनलॉक नहीं किया गया है. पासवर्ड की पुष्टि करें.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"उंगली की पहचान नहीं हो सकी"</string>
+    <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="one">सिम का पिन डालें. आपके पास <xliff:g id="NUMBER_1">%d</xliff:g> मौके बचे हैं.</item>
       <item quantity="other">सिम का पिन डालें. आपके पास <xliff:g id="NUMBER_1">%d</xliff:g> मौके बचे हैं.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml
index 7485cef..089c6c4 100644
--- a/packages/SystemUI/res-keyguard/values-hr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Unesite zaporku da biste otključali"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Unesite PIN da biste otključali"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Unesite PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Unesite uzorak"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Unesite zaporku"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Unesite uzorak"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Unesite zaporku"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN kôd nije točan."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nevažeća kartica."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Napunjeno"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Potpuno napunjena baterija"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • punjenje"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • brzo punjenje"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • sporo punjenje"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Onemogućivanje eSIM-a nije uspjelo zbog pogreške."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Unos"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Zaboravili ste uzorak"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Pogrešan uzorak"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Pogrešna zaporka"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Pogrešan uzorak"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Pogrešna zaporka"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Pogrešan PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> sekundu</item>
@@ -146,7 +146,8 @@
       <item quantity="few">Uređaj nije bio otključan <xliff:g id="NUMBER_1">%d</xliff:g> sata. Potvrdite zaporku.</item>
       <item quantity="other">Uređaj nije bio otključan <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite zaporku.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nije prepoznat"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nije prepoznat"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nije prepoznato"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
       <item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml
index 371bc64..49d8401 100644
--- a/packages/SystemUI/res-keyguard/values-hu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"A feloldáshoz írja be a jelszót"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"A feloldáshoz írja be a PIN-kódot"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Adja meg PIN-kódját"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Adja meg mintáját"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Adja meg jelszavát"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Adja meg a mintáját"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Adja meg jelszavát"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Helytelen PIN-kód."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Érvénytelen kártya."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Feltöltve"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Teljesen feltöltve"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Töltés"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Gyors töltés"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lassú töltés"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Hiba történt, így az eSIM-et nem lehet letiltani."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Elfelejtettem a mintát"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Helytelen minta"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Helytelen jelszó"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Helytelen minta"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Helytelen jelszó"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Helytelen PIN-kód"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Próbálja újra <xliff:g id="NUMBER">%d</xliff:g> másodperc múlva.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Az eszköz zárolása <xliff:g id="NUMBER_1">%d</xliff:g> órája nem lett feloldva. Erősítse meg a jelszót.</item>
       <item quantity="one">Az eszköz zárolása <xliff:g id="NUMBER_0">%d</xliff:g> órája nem lett feloldva. Erősítse meg a jelszót.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nem sikerült felismerni"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nem ismerhető fel"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nem ismerhető fel"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Adja meg a SIM-kártya PIN-kódját. <xliff:g id="NUMBER_1">%d</xliff:g> próbálkozása maradt.</item>
       <item quantity="one">Adja meg a SIM-kártya PIN-kódját. <xliff:g id="NUMBER_0">%d</xliff:g> próbálkozása maradt. Ha elfogynak a próbálkozási lehetőségek, az eszköz feloldásához fel kell vennie a kapcsolatot szolgáltatójával.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index 92cb87a..5198da6 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"Մուտքագրեք նախշը"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Մուտքագրեք գաղտնաբառը"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Լիցքավորված է"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Ամբողջությամբ լիցքավորված է"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Լիցքավորում"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Արագ լիցքավորում"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Դանդաղ լիցքավորում"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Սխալի պատճառով չհաջողվեց անջատել eSIM-ը։"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Մուտքի ստեղն"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Մոռացել եմ նախշը"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Նախշը սխալ է"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Գաղտնաբառը սխալ է"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Նախշը սխալ է"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Գաղտնաբառը սխալ է"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN կոդը սխալ է"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Փորձեք <xliff:g id="NUMBER">%d</xliff:g> վայրկյանից:</item>
@@ -140,7 +140,8 @@
       <item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm password.</item>
       <item quantity="other">Սարքը չի ապակողպվել <xliff:g id="NUMBER_1">%d</xliff:g> ժամվա ընթացքում: Հաստատեք գաղտնաբառը:</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Չճանաչվեց"</string>
+    <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="one">Մուտքագրեք SIM քարտի PIN կոդը: Մնացել է <xliff:g id="NUMBER_1">%d</xliff:g> փորձ:</item>
       <item quantity="other">Մուտքագրեք SIM քարտի PIN կոդը: Մնացել է <xliff:g id="NUMBER_1">%d</xliff:g> փորձ:</item>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index c0c828b..161287c 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Ketik sandi untuk membuka kunci"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Ketik PIN untuk membuka kunci"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Masukkan PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Masukkan Pola"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Masukkan Sandi"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Masukkan pola"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Masukkan sandi"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Kode PIN salah."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Kartu Tidak Valid"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Terisi"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Terisi penuh"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan cepat"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan lambat"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM tidak dapat dinonaktifkan karena terjadi error."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Masukkan"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Lupa Pola?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Pola Salah"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Sandi Salah"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Pola salah"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Sandi salah"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN Salah"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Coba <xliff:g id="NUMBER">%d</xliff:g> detik lagi.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Perangkat belum dibuka kuncinya selama <xliff:g id="NUMBER_1">%d</xliff:g> jam. Konfirmasi sandi.</item>
       <item quantity="one">Perangkat belum dibuka kuncinya selama <xliff:g id="NUMBER_0">%d</xliff:g> jam. Konfirmasi sandi.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Tidak dikenali"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Tidak dikenali"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Tidak dikenali"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Masukkan PIN SIM. Tersisa <xliff:g id="NUMBER_1">%d</xliff:g> percobaan.</item>
       <item quantity="one">Masukkan PIN SIM. Tersisa <xliff:g id="NUMBER_0">%d</xliff:g> percobaan sebelum Anda harus menghubungi operator untuk membuka kunci perangkat.</item>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index dad5e69..9ed1188 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Sláðu inn aðgangsorðið til að opna"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Sláðu inn PIN-númer til að opna"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Sláðu inn PIN-númer"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Færðu inn mynstur"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Sláðu inn aðgangsorð"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Færðu inn mynstrið þitt"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Sláðu inn aðgangsorðið þitt"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Rangt PIN-númer."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ógilt kort."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Fullhlaðin"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Fullhlaðin"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Í hleðslu"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hröð hleðsla"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hæg hleðsla"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Villa kom í veg fyrir að hægt væri að gera eSIM-kortið óvirkt."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Færa inn"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Man ekki mynstrið"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Rangt mynstur"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Rangt aðgangsorð"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Rangt mynstur"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Rangt aðgangsorð"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Rangt PIN-númer"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Reyndu aftur eftir <xliff:g id="NUMBER">%d</xliff:g> sekúndu.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">Tækið hefur ekki verið tekið úr lás í <xliff:g id="NUMBER_1">%d</xliff:g> klukkustund. Staðfestu aðgangsorðið.</item>
       <item quantity="other">Tækið hefur ekki verið tekið úr lás í <xliff:g id="NUMBER_1">%d</xliff:g> klukkustundir. Staðfestu aðgangsorðið.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Þekktist ekki"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Þekktist ekki"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Þekktist ekki"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="one">Sláðu inn PIN-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraun eftir.</item>
       <item quantity="other">Sláðu inn PIN-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraunir eftir.</item>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index 2f322e8..b5f85f6 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Inserisci password per sbloccare"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Inserisci PIN per sbloccare"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Inserisci il PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Inserisci la sequenza"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Inserisci la password"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Inserisci la sequenza"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Inserisci la password"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Codice PIN errato."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Scheda non valida."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Carico"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Completamente carica"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • In carica"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica veloce"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica lenta"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Impossibile disattivare la eSIM a causa di un errore."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Invio"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Sequenza dimenticata"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Sequenza sbagliata"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Password sbagliata"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Sequenza errata"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Password errata"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN errato"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Riprova fra <xliff:g id="NUMBER">%d</xliff:g> secondi.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Il dispositivo non viene sbloccato da <xliff:g id="NUMBER_1">%d</xliff:g> ore. Conferma la password.</item>
       <item quantity="one">Il dispositivo non viene sbloccato da <xliff:g id="NUMBER_0">%d</xliff:g> ora. Conferma la password.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Non riconosciuta"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Non riconosciuto"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Non riconosciuto"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Inserisci il codice PIN della SIM. Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> tentativi a disposizione.</item>
       <item quantity="one">Inserisci il codice PIN della SIM. Hai ancora <xliff:g id="NUMBER_0">%d</xliff:g> tentativo a disposizione, dopodiché dovrai contattare l\'operatore per sbloccare il dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index cfffcd2..09b3cfa 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"הזן סיסמה לביטול הנעילה"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"הזן את קוד הגישה לביטול הנעילה"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"הזנת קוד גישה"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"הזנת קו ביטול נעילה"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"הזנת סיסמה"</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">"קוד הגישה שגוי"</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"כרטיס לא חוקי."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"הסוללה טעונה"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"טעונה במלואה"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה מהירה"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה איטית"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"‏לא ניתן להשבית את כרטיס ה-eSIM עקב שגיאה."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"שכחתי את קו ביטול הנעילה"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"קו ביטול הנעילה שגוי"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"הסיסמה שגויה"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"קו ביטול נעילה שגוי"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"סיסמה שגויה"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"קוד הגישה שגוי"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="two">אפשר יהיה לנסות שוב בעוד <xliff:g id="NUMBER">%d</xliff:g> שניות.</item>
@@ -152,7 +152,8 @@
       <item quantity="other">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. הזן את הסיסמה.</item>
       <item quantity="one">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_0">%d</xliff:g> שעה. הזן את הסיסמה.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"לא זוהתה"</string>
+    <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="two">‏יש להזין קוד גישה לכרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסונות נוספים.</item>
       <item quantity="many">‏יש להזין קוד גישה לכרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסונות נוספים.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml
index 98e8ce0..08d4b9b 100644
--- a/packages/SystemUI/res-keyguard/values-ja/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"パターンを入力してください"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"パスワードを入力してください"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"充電が完了しました"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"充電完了"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 急速充電中"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 低速充電中"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"エラーのため、eSIM を無効にできません。"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"入力"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"パターンを忘れた場合"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"パターンが正しくありません"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"パスワードが正しくありません"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"パターンが正しくありません"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"パスワードが正しくありません"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN が正しくありません"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> 秒後にもう一度お試しください。</item>
@@ -140,7 +140,8 @@
       <item quantity="other">端末のロックが <xliff:g id="NUMBER_1">%d</xliff:g> 時間、解除されていません。パスワードを確認してください。</item>
       <item quantity="one">端末のロックが <xliff:g id="NUMBER_0">%d</xliff:g> 時間、解除されていません。パスワードを確認してください。</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"認識されませんでした"</string>
+    <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>
diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml
index df96979..f966c33 100644
--- a/packages/SystemUI/res-keyguard/values-ka/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"შეიყვანეთ განმბლოკავი ნიმუში"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"შეიყვანეთ პაროლი"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"დატენილია"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"ბოლომდე დატენილი"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • იტენება"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • სწრაფად იტენება"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ნელა იტენება"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM-ის გათიშვა ვერ ხერხდება წარმოქმნილი შეცდომის გამო."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"შეყვანა"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"დაგავიწყდათ ნიმუში"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"ნიმუში არასწორია"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"პაროლი არასწორია"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"ნიმუში არასწორია"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"პაროლი არასწორია"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN-კოდი არასწორია"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">ცადეთ ხელახლა <xliff:g id="NUMBER">%d</xliff:g> წამში.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">მოწყობილობა არ განბლოკილა <xliff:g id="NUMBER_1">%d</xliff:g> საათის განმავლობაში. დაადასტურეთ პაროლი.</item>
       <item quantity="one">მოწყობილობა არ განბლოკილა <xliff:g id="NUMBER_0">%d</xliff:g> საათის განმავლობაში. დაადასტურეთ პაროლი.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"არ არის ამოცნობილი"</string>
+    <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>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index 6ed9f44..530418f 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"Өрнекті енгізіңіз"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Құпия сөзді енгізіңіз"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Зарядталды"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Толық зарядталды"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядталуда"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жылдам зарядталуда"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Баяу зарядталуда"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Қатеге байланысты eSIM картасы өшірілмеді."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Енгізу"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Өрнекті ұмытып қалдыңыз ба?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Өрнек қате"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Құпия сөз қате"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Өрнек дұрыс емес"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Құпия сөз дұрыс емес"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN коды қате"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"> <xliff:g id="NUMBER">%d</xliff:g> секундтан кейін қайталап көріңіз.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Құрылғы құлпы <xliff:g id="NUMBER_1">%d</xliff:g> сағаттан бері ашылмаған. Құпия сөзді растаңыз.</item>
       <item quantity="one">Құрылғы құлпы <xliff:g id="NUMBER_0">%d</xliff:g> сағаттан бері ашылмаған. Құпия сөзді растаңыз.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Анықталмады"</string>
+    <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> мүмкіндік қалды, одан кейін оператордан SIM картасының құлпын ашуды сұрауға тура келеді.</item>
       <item quantity="one">SIM PIN кодын енгізіңіз. <xliff:g id="NUMBER_0">%d</xliff:g> мүмкіндік қалды, одан кейін оператордан SIM картасының құлпын ашуды сұрауға тура келеді.</item>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index f7960e5..7ab347d 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"បញ្ចូល​លំនាំ​របស់អ្នក"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"បញ្ចូល​ពាក្យ​សម្ងាត់​របស់​អ្នក"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"បាន​សាក​ថ្ម"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"បានសាក​ថ្មពេញ"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុង​សាកថ្ម"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុង​សាកថ្មយ៉ាង​ឆាប់រហ័ស"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុង​សាកថ្មយឺត"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"មិនអាច​បិទ eSIM បានទេ ដោយសារ​មាន​បញ្ហា។"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ភ្លេច​​លំនាំ"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"លំនាំ​មិន​ត្រឹមត្រូវ​ទេ"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"ពាក្យ​សម្ងាត់​មិន​ត្រឹមត្រូវ​ទេ"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"លំនាំមិនត្រឹមត្រូវ"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"ពាក្យសម្ងាត់មិនត្រឹមត្រូវ"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"កូដ PIN មិន​ត្រឹមត្រូវ​ទេ"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">ព្យាយាមម្តងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER">%d</xliff:g> វិនាទី។</item>
@@ -140,7 +140,8 @@
       <item quantity="other">ឧបករណ៍​បាន​ជាប់​សោ​អស់រយៈ​ពេល <xliff:g id="NUMBER_1">%d</xliff:g> ម៉ោង​ហើយ។ សូម​បញ្ជាក់​ពាក្យ​សម្ងាត់។</item>
       <item quantity="one">ឧបករណ៍​បាន​ជាប់​សោ​អស់រយៈ​ពេល <xliff:g id="NUMBER_0">%d</xliff:g> ម៉ោង​ហើយ។ សូម​បញ្ជាក់​ពាក្យ​សម្ងាត់។</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"មិនអាចសម្គាល់បានទេ"</string>
+    <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">បញ្ចូល​កូដ PIN របស់ស៊ីម។ អ្នកនៅ​សល់ការ​ព្យាយាម <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត។</item>
       <item quantity="one">បញ្ចូលកូដ PIN របស់ស៊ីម។ អ្នក​នៅសល់​ការព្យាយាម <xliff:g id="NUMBER_0">%d</xliff:g> ដង​ទៀត មុន​ពេល​ដែលអ្នក​ត្រូវទាក់ទង​ទៅ​ក្រុមហ៊ុន​សេវា​ទូរសព្ទ​របស់អ្នក​ដើម្បី​ដោះសោ​ឧបករណ៍​របស់អ្នក។</item>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index e6b03f9..ef92951 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"ಅನ್‌ಲಾಕ್‌ ಮಾಡಲು ಪಾಸ್‌ವರ್ಡ್‌ ಟೈಪ್‌ ಮಾಡಿ"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"ಅನ್‌ಲಾಕ್‌ ಮಾಡಲು ಪಿನ್‌ ಟೈಪ್‌ ಮಾಡಿ"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"ನಿಮ್ಮ ಪಿನ್ ನಮೂದಿಸಿ"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"ನಿಮ್ಮ ಪ್ಯಾಟರ್ನ್ ನಮೂದಿಸಿ"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್ ನಮೂದಿಸಿ"</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">"ತಪ್ಪಾದ ಪಿನ್‌ ಕೋಡ್."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ಅಮಾನ್ಯ ಕಾರ್ಡ್."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"ಪೂರ್ಣವಾಗಿ ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರ್ಜ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ವೇಗವಾಗಿ ಚಾರ್ಜ್‌ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ನಿಧಾನವಾಗಿ ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"ದೋಷದ ಕಾರಣದಿಂದಾಗಿ eSIM ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"ನಮೂದಿಸಿ"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ಪ್ಯಾಟರ್ನ್ ಮರೆತಿದ್ದೀರಿ"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"ಪ್ಯಾಟರ್ನ್ ತಪ್ಪಾಗಿದೆ"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"ಪಾಸ್‌ವರ್ಡ್ ತಪ್ಪಾಗಿದೆ"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"ಪ್ಯಾಟರ್ನ್ ತಪ್ಪಾಗಿದೆ"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"ತಪ್ಪು ಪಾಸ್‌ವರ್ಡ್"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"ಪಿನ್‌ ತಪ್ಪಾಗಿದೆ"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">ಸಾಧನವನ್ನು <xliff:g id="NUMBER_1">%d</xliff:g> ಗಂಟೆಗಳವರೆಗೆ ಅನ್‌ಲಾಕ್‌ ಮಾಡಿರಲಿಲ್ಲ. ಪಾಸ್‌ವರ್ಡ್‌ ಖಚಿತಪಡಿಸಿ.</item>
       <item quantity="other">ಸಾಧನವನ್ನು <xliff:g id="NUMBER_1">%d</xliff:g> ಗಂಟೆಗಳವರೆಗೆ ಅನ್‌ಲಾಕ್‌ ಮಾಡಿರಲಿಲ್ಲ. ಪಾಸ್‌ವರ್ಡ್‌ ಖಚಿತಪಡಿಸಿ.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
+    <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="one">ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ. ನಿಮ್ಮಲ್ಲಿ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
       <item quantity="other">ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ. ನಿಮ್ಮಲ್ಲಿ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index ffdb1e5..8a65c95 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"패턴을 입력해 주세요."</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"비밀번호를 입력해 주세요."</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"충전됨"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"충전 완료"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 충전 중"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 고속 충전 중"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 저속 충전 중"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"오류로 인해 eSIM을 사용 중지할 수 없습니다."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter 키"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"패턴을 잊음"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"잘못된 패턴"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"잘못된 비밀번호"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"잘못된 패턴"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"잘못된 비밀번호"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"잘못된 PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g>초 후에 다시 시도하세요.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">기기가 <xliff:g id="NUMBER_1">%d</xliff:g>시간 동안 잠금 해제되지 않았습니다. 비밀번호를 입력하세요.</item>
       <item quantity="one">기기가 <xliff:g id="NUMBER_0">%d</xliff:g>시간 동안 잠금 해제되지 않았습니다. 비밀번호를 입력하세요.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"인식할 수 없음"</string>
+    <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>
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index 80ae050..a7d5d45 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"Графикалык ачкычты киргизиңиз"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Сырсөзүңүздү киргизиңиз"</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">"SIM-карта жараксыз."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Кубатталды"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Толук кубатталды"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Кубатталууда"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Тез кубатталууда"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жай кубатталууда"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Катадан улам eSIM-картаны өчүрүүгө болбойт."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Киргизүү"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Графикалык ачкычты унутуп калдым"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Графикалык ачкыч туура эмес"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Сырсөз туура эмес"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Графикалык ачкыч туура эмес"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Сырсөз туура эмес"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN-код туура эмес"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> секунддан кийин кайталаңыз.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Түзмөктүн кулпусу <xliff:g id="NUMBER_1">%d</xliff:g> саат бою ачылган жок. Сырсөздү ырастаңыз.</item>
       <item quantity="one">Түзмөктүн кулпусу <xliff:g id="NUMBER_0">%d</xliff:g> саат бою ачылган жок. Сырсөздү ырастаңыз.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Таанылган жок"</string>
+    <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>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index b05915a..9f3de8b0 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"ໃສ່ຮູບແບບປົດລັອກຂອງທ່ານ"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"ໃສ່ລະຫັດຜ່ານຂອງທ່ານ"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"ສາກເຕັມແລ້ວ."</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"ສາກເຕັມແລ້ວ"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກແບບດ່ວນ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກແບບຊ້າ"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"ບໍ່ສາມາດປິດການນຳໃຊ້ eSIM ໄດ້ເນື່ອງຈາກມີຂໍ້ຜິດພາດ."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"ປ້ອນເຂົ້າ"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ລືມຮູບແບບປົດລັອກ?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"ຮູບແບບຜິດ"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"ລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"ຮູບແບບບໍ່ຖືກຕ້ອງ"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"ລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"ລະຫັດ PIN ບໍ່ຖືກຕ້ອງ"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">ລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER">%d</xliff:g> ວິນາທີ.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">ອຸປະກອນບໍ່ໄດ້ຖືກປົດລັອກເປັນເວລາ <xliff:g id="NUMBER_1">%d</xliff:g> ຊົ່ວໂມງ. ຢືນຢັນລະຫັດຜ່ານ.</item>
       <item quantity="one">ອຸປະກອນບໍ່ໄດ້ຖືກປົດລັອກເປັນເວລາ <xliff:g id="NUMBER_0">%d</xliff:g> ຊົ່ວໂມງ. ຢືນຢັນລະຫັດຜ່ານ.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"ບໍ່ຮັບຮູ້"</string>
+    <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>
diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml
index 5cd696f..bdebf67 100644
--- a/packages/SystemUI/res-keyguard/values-lt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Įveskite slaptažodį, kad atrakintumėte"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Įveskite PIN kodą, kad atrakintumėte"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Įveskite PIN kodą"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Įveskite atrakinimo piešinį"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Įveskite slaptažodį"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Nubrėžkite atrakinimo piešinį"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Įveskite slaptažodį"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Netinkamas PIN kodas."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Netinkama kortelė."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Įkrauta"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Visiškai įkrautas"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Įkraunama"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Greitai įkraunama"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lėtai įkraunama"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Dėl klaidos nepavyko išjungti „eSIM“ kortelės."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Pamiršau atrakinimo piešinį"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Netinkamas atrakinimo piešinys"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Netinkamas slaptažodis"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Netinkamas atrakinimo piešinys"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Netinkamas slaptažodis"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Netinkamas PIN kodas"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Bandykite dar kartą po <xliff:g id="NUMBER">%d</xliff:g> sekundės.</item>
@@ -152,7 +152,8 @@
       <item quantity="many">Įrenginys nebuvo atrakintas <xliff:g id="NUMBER_1">%d</xliff:g> valandos. Patvirtinkite slaptažodį.</item>
       <item quantity="other">Įrenginys nebuvo atrakintas <xliff:g id="NUMBER_1">%d</xliff:g> valandų. Patvirtinkite slaptažodį.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Neatpažinta"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Neatpažinta"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Neatpažinta"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="one">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymas.</item>
       <item quantity="few">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymai.</item>
diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml
index d4f6020..c68761d 100644
--- a/packages/SystemUI/res-keyguard/values-lv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Ievadiet paroli, lai atbloķētu."</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Ievadiet PIN kodu, lai atbloķētu."</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Ievadiet savu PIN kodu"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Ievadiet savu kombināciju"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Ievadiet savu paroli"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Ievadiet savu kombināciju"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Ievadiet paroli"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN kods nav pareizs."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nederīga karte."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Akumulators uzlādēts"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Pilnībā uzlādēts"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek uzlāde"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek ātrā uzlāde"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek lēnā uzlāde"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Kļūdas dēļ nevar atspējot eSIM karti."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Ievadīšanas taustiņš"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Aizmirsu kombināciju"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Nepareiza kombinācija."</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Nepareiza parole."</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Nepareiza kombinācija"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Nepareiza parole"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Nepareizs PIN kods."</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="zero">Mēģiniet vēlreiz pēc <xliff:g id="NUMBER">%d</xliff:g> sekundēm.</item>
@@ -146,7 +146,8 @@
       <item quantity="one">Ierīce nav tikusi atbloķēta <xliff:g id="NUMBER_1">%d</xliff:g> stundu. Apstipriniet paroli.</item>
       <item quantity="other">Ierīce nav tikusi atbloķēta <xliff:g id="NUMBER_1">%d</xliff:g> stundas. Apstipriniet paroli.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nav atpazīts"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nav atpazīts"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nav atpazīts"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="zero">Ievadiet SIM kartes PIN. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizes.</item>
       <item quantity="one">Ievadiet SIM kartes PIN. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizi.</item>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index aff4803..0ace83f 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"Внесете ја шемата"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Внесете ја лозинката"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Полна"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Целосно полна"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Се полни"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо полнење"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Бавно полнење"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM-картичката не може да се оневозможи поради грешка."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Внеси"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Ја заборавивте шемата?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Погрешна шема"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Погрешна лозинка"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Погрешна шема"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Погрешна лозинка"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Погрешен PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Обидете се повторно за <xliff:g id="NUMBER">%d</xliff:g> секунда.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">Уредот не е отклучен веќе <xliff:g id="NUMBER_1">%d</xliff:g> час. Потврдете ја лозинката.</item>
       <item quantity="other">Уредот не е отклучен веќе <xliff:g id="NUMBER_1">%d</xliff:g> часа. Потврдете ја лозинката.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Непознат"</string>
+    <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="one">Внесете PIN-код за SIM-картичката. Ви преостанува уште <xliff:g id="NUMBER_1">%d</xliff:g> обид.</item>
       <item quantity="other">Внесете PIN-код за SIM-картичката. Ви преостануваат уште <xliff:g id="NUMBER_1">%d</xliff:g> обиди.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index d3283e1..779a532 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"അൺലോക്കുചെയ്യുന്നതിന് പാസ്‌വേഡ് ടൈപ്പുചെയ്യുക"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"അൺലോക്കുചെയ്യുന്നതിന് പിൻ ടൈപ്പുചെയ്യുക"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"പിൻ നൽകുക"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"നിങ്ങളുടെ പാറ്റേൺ നൽകുക"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"നിങ്ങളുടെ പാസ്‌വേഡ് നല്‍‌കുക"</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">"പിൻ കോഡ് തെറ്റാണ്."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"അസാധുവായ കാർഡ്."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"ചാർജായി"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"പൂർണ്ണമായി ചാർജ് ചെയ്‌തു"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ചാർജ് ചെയ്യുന്നു"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • വേഗത്തിൽ ചാർജ് ചെയ്യുന്നു"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • പതുക്കെ ചാർജ് ചെയ്യുന്നു"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"പിശക് കാരണം ഇ-സിം പ്രവർത്തനരഹിതമാക്കാനാകുന്നില്ല"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"എന്റർ"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"പാറ്റേൺ മറന്നു"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"പാറ്റേൺ തെറ്റാണ്"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"പാസ്‌വേഡ് തെറ്റാണ്"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"പാറ്റേൺ തെറ്റാണ്"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"പാസ്‌വേഡ് തെറ്റാണ്"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"പിൻ തെറ്റാണ്"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> സെക്കൻഡുകൾക്കുള്ളിൽ വീണ്ടും ശ്രമിക്കുക.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">ഉപകരണം <xliff:g id="NUMBER_1">%d</xliff:g> മണിക്കൂറായി അൺലോക്ക് ചെയ്തിട്ടില്ല. പാസ്‌വേഡ് സ്ഥിരീകരിക്കുക.</item>
       <item quantity="one">ഉപകരണം <xliff:g id="NUMBER_0">%d</xliff:g> മണിക്കൂറായി അൺലോക്ക് ചെയ്തിട്ടില്ല. പാസ്‌വേഡ് സ്ഥിരീകരിക്കുക.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"തിരിച്ചറിഞ്ഞില്ല"</string>
+    <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">സിം പിൻ നൽകുക. നിങ്ങൾക്ക് <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി ശേഷിക്കുന്നു.</item>
       <item quantity="one">സിം പിൻ നൽകുക. ഉപകരണം അൺലോക്ക് ചെയ്യാൻ കാരിയറുമായി ബന്ധപ്പെടേണ്ടിവരുന്നതിന് മുമ്പ് <xliff:g id="NUMBER_0">%d</xliff:g> ശ്രമം കൂടി ശേഷിക്കുന്നു.</item>
diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml
index 99c7339..189d407 100644
--- a/packages/SystemUI/res-keyguard/values-mn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Түгжээг тайлахын тулд нууц үгийг оруулна уу"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Түгжээг тайлахын тулд ПИН кодыг оруулна уу"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"ПИН-ээ оруулна уу"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Загвараа оруулна уу"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Нууц үгээ оруулна уу"</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">"ПИН код буруу байна."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Карт хүчингүй байна."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Цэнэглэсэн"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Бүрэн цэнэглэсэн"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Цэнэглэж байна"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Хурдан цэнэглэж байна"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Удаан цэнэглэж байна"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Алдаа гарсан тул eSIM-г идэвхгүй болгох боломжгүй байна."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Оруулах"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Загварыг мартсан"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Загвар буруу байна"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Нууц үг буруу байна"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Хээ буруу байна"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Нууц үг буруу байна"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"ПИН код буруу байна"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> секундын дараа дахин оролдоно уу.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Төхөөрөмжийн түгжээг <xliff:g id="NUMBER_1">%d</xliff:g> цагийн турш тайлаагүй байна. Нууц үгээ баталгаажуулна уу.</item>
       <item quantity="one">Төхөөрөмжийн түгжээг <xliff:g id="NUMBER_0">%d</xliff:g> цагийн турш тайлаагүй байна. Нууц үгээ баталгаажуулна уу.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Танигдахгүй байна"</string>
+    <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-н ПИН кодыг оруулна уу. Танд <xliff:g id="NUMBER_1">%d</xliff:g> оролдлого үлдлээ.</item>
       <item quantity="one">SIM-н ПИН кодыг оруулна уу. Танд оператор компанитайгаа холбогдохгүйгээр төхөөрөмжийн түгжээг тайлах <xliff:g id="NUMBER_0">%d</xliff:g> оролдлого үлдлээ.</item>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index 16a9d63..c2759da 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"अनलॉक करण्यासाठी पासवर्ड टाइप करा"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"अनलॉक करण्यासाठी पिन टाइप करा"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"तुमचा पिन एंटर करा"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"तुमचा पॅटर्न एंटर करा"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"तुमचा पासवर्ड एंटर करा"</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">"चुकीचा पिन कोड."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"अवैध कार्ड."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"चार्ज झाली"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"पूर्णपणे चार्ज"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज होत आहे"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • वेगाने चार्ज होत आहे"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • सावकाश चार्ज होत आहे"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"एका एररमुळे eSIM बंद होऊ शकत नाही."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"एंटर करा"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"पॅटर्न विसरलात"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"चुकीचा पॅटर्न"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"चुकीचा पासवर्ड"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"चुकीचा पॅटर्न"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"चुकीचा पासवर्ड"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"चुकीचा पिन"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> सेकंदात पुन्हा प्रयत्न करा.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">डिव्हाइस <xliff:g id="NUMBER_1">%d</xliff:g> तासासाठी अनलॉक केले गेले नाही. पासवर्डची खात्री करा.</item>
       <item quantity="other">डिव्हाइस <xliff:g id="NUMBER_1">%d</xliff:g> तासांसाठी अनलॉक केले गेले नाही. पासवर्डची खात्री करा.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"ओळखले नाही"</string>
+    <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="one">सिम पिन एंटर करा, तुमच्याकडे <xliff:g id="NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहे.</item>
       <item quantity="other">सिम पिन एंटर करा, तुमच्याकडे <xliff:g id="NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहेत.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index 19e53d5..9e10298 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Taip kata laluan untuk membuka kunci"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Taip PIN untuk membuka kunci"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Masukkan PIN anda"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Masukkan Corak anda"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Masukkan Kata Laluan anda"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Masukkan corak anda"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Masukkan kata laluan anda"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Kod PIN salah."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Kad Tidak Sah."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Sudah dicas"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Dicas penuh"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan cepat"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan perlahan"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM tidak dapat dilumpuhkan kerana ralat."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Kekunci Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Terlupa Corak"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Corak salah"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Kata Laluan salah"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Corak salah"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Kata laluan salah"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN salah"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Cuba lagi dalam masa <xliff:g id="NUMBER">%d</xliff:g> saat.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Peranti tidak dibuka kuncinya selama <xliff:g id="NUMBER_1">%d</xliff:g> jam. Sahkan kata laluan.</item>
       <item quantity="one">Peranti tidak dibuka kuncinya selama <xliff:g id="NUMBER_0">%d</xliff:g> jam. Sahkan kata laluan.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Tidak dikenali"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Tidak dikenali"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Tidak dikenali"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Masukkan PIN SIM. Tinggal <xliff:g id="NUMBER_1">%d</xliff:g> percubaan lagi.</item>
       <item quantity="one">Masukkan PIN SIM. Tinggal <xliff:g id="NUMBER_0">%d</xliff:g> percubaan lagi sebelum anda perlu menghubungi pembawa anda untuk membuka kunci peranti.</item>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index c9a2e1c..87fca07 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"လော့ခ်ဖွင့်ရန် စကားဝှက်ကို ထည့်ပါ"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"လော့ခ်ဖွင့်ရန် ပင်နံပါတ်ထည့်ပါ"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"သင့်ပင်နံပါတ် ထည့်ပါ"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"သင့်လော့ခ်ဖွင့်ပုံစံ ထည့်ပါ"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"သင့်စကားဝှက် ထည့်ပါ"</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">"ပင်နံပါတ် မှားနေသည်။"</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ကတ် မမှန်ကန်ပါ။"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"အားသွင်းပြီးပါပြီ"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"အားအပြည့်သွင်းထားသည်"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • အားသွင်းနေသည်"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • အမြန်အားသွင်းနေသည်"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • နှေးကွေးစွာ အားသွင်းနေသည်"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"အမှားအယွင်းရှိနေသောကြောင့် eSIM ကို ပိတ်၍မရပါ။"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter ခလုတ်"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ပုံစံအား မေ့သွားပါသည်"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"ပုံစံ မမှန်ကန်ပါ"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"စကားဝှက် မမှန်ကန်ပါ"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"လော့ခ်ဖွင့်ပုံစံ မှားနေသည်"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"စကားဝှက် မှားနေသည်"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"ပင်နံပါတ် မမှန်ကန်ပါ"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> စက္ကန့် အကြာတွင် ထပ်လုပ်ကြည့်ပါ</item>
@@ -140,7 +140,8 @@
       <item quantity="other">စက်ပစ္စည်းကို <xliff:g id="NUMBER_1">%d</xliff:g> နာရီကြာ လော့ခ်ဖွင့်ခဲ့ခြင်း မရှိပါ။ စကားဝှက်အား အတည်ပြုပါ။</item>
       <item quantity="one">စက်ပစ္စည်းကို <xliff:g id="NUMBER_0">%d</xliff:g> နာရီကြာ လော့ခ်ဖွင့်ခဲ့ခြင်း မရှိပါ။ စကားဝှက်အား အတည်ပြုပါ။</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"မသိပါ"</string>
+    <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">ဆင်းမ်ကဒ် ပင်နံပါတ် ထည့်ပါ။ <xliff:g id="NUMBER_1">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့်ရှိပါသေးသည်။</item>
       <item quantity="one">ဆင်းမ်ကဒ် ပင်နံပါတ် ထည့်ပါ။ သင့်စက်ကို လော့ခ်ဖွင့်ပေးရန်အတွက် ဝန်ဆောင်မှုပေးသူသို့ မဆက်သွယ်မီ <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့်ရှိပါသေးသည်။</item>
diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml
index 4573596..977784c 100644
--- a/packages/SystemUI/res-keyguard/values-nb/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Skriv inn passordet for å låse opp"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Skriv inn PIN-koden for å låse opp"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Skriv inn PIN-koden din"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Skriv inn mønsteret ditt"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Skriv inn passordet ditt"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Legg inn mønsteret ditt"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Skriv inn passordet ditt"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Feil PIN-kode."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ugyldig kort."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Oppladet"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Fulladet"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader raskt"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader sakte"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"E-SIM-kortet kan ikke deaktiveres på grunn av en feil."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Har du glemt mønsteret?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Feil mønster"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Feil passord"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Feil mønster"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Feil passord"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Feil PIN-kode"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Prøv på nytt om <xliff:g id="NUMBER">%d</xliff:g> sekunder.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Enheten har ikke blitt låst opp de siste <xliff:g id="NUMBER_1">%d</xliff:g> timene. Bekreft passordet.</item>
       <item quantity="one">Enheten har ikke blitt låst opp på <xliff:g id="NUMBER_0">%d</xliff:g> time. Bekreft passordet.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Ikke gjenkjent"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Ikke gjenkjent"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Ikke gjenkjent"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Skriv inn PIN-koden for SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøk igjen.</item>
       <item quantity="one">Skriv inn PIN-koden for SIM-kortet. Du har <xliff:g id="NUMBER_0">%d</xliff:g> forsøk igjen før du må kontakte operatøren din for å låse opp enheten.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index 3f6b749..913c3bb 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"आफ्नो ढाँचा प्रविष्ट गर्नुहोस्"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"आफ्नो पासवर्ड प्रविष्ट गर्ने"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"चार्ज भयो"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"पूर्ण चार्ज भएको"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज गरिँदै"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • द्रुत गतिमा चार्ज गरिँदै"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • मन्द गतिमा चार्ज गरिँदै"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"कुनै त्रुटिका कारण यो eSIM लाई असक्षम पार्न सकिएन।"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"प्रविष्टि गर्नुहोस्"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ढाँचा बिर्सनुभयो"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"गलत ढाँचा"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"गलत पासवर्ड"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"गलत ढाँचा"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"गलत पासवर्ड"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"गलत PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> सेकेन्डपछि फेरि प्रयास गर्नुहोस्।</item>
@@ -140,7 +140,8 @@
       <item quantity="other">यन्त्र <xliff:g id="NUMBER_1">%d</xliff:g> घन्टा देखि अनलक भएको छैन। पासवर्ड पुष्टि गर्नुहोस्।</item>
       <item quantity="one">यन्त्र <xliff:g id="NUMBER_0">%d</xliff:g> घन्टा देखि अनलक भएको छैन। पासवर्ड पुष्टि गर्नुहोस्।</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"पहिचान भएन"</string>
+    <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>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index e2f5806..f80749f 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Typ het wachtwoord om te ontgrendelen"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Typ pincode om te ontgrendelen"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Geef je pincode op"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Geef je patroon op"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Geef je wachtwoord op"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Geef je patroon op"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Geef je wachtwoord op"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Onjuiste pincode."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ongeldige kaart."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Opgeladen"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Volledig opgeladen"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Opladen"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Snel opladen"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Langzaam opladen"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"De e-simkaart kan niet worden uitgeschakeld vanwege een fout."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Patroon vergeten"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Onjuist patroon"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Onjuist wachtwoord"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Onjuist patroon"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Onjuist wachtwoord"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Onjuiste pincode"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Probeer het over <xliff:g id="NUMBER">%d</xliff:g> seconden opnieuw.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Apparaat is al <xliff:g id="NUMBER_1">%d</xliff:g> uur niet ontgrendeld. Bevestig het wachtwoord.</item>
       <item quantity="one">Apparaat is al <xliff:g id="NUMBER_0">%d</xliff:g> uur niet ontgrendeld. Bevestig het wachtwoord.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Niet herkend"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Niet herkend"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Niet herkend"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Geef de pincode van de simkaart op. Je hebt nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over.</item>
       <item quantity="one">Geef de pincode van de simkaart op. Je hebt nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat je contact met je provider moet opnemen om het apparaat te ontgrendelen.</item>
diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml
index 1ffce78..eff493b 100644
--- a/packages/SystemUI/res-keyguard/values-or/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-or/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"ନିଜର ପାଟର୍ନ ଆଙ୍କନ୍ତୁ"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"ନିଜର ପାସ୍‌ୱର୍ଡ ଲେଖନ୍ତୁ"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"ଚାର୍ଜ ହୋଇଗଲା"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"ସମ୍ପୂର୍ଣ୍ଣ ଭାବରେ ଚାର୍ଜ ହୋ‍ଇଗଲା"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଚାର୍ଜ ହେଉଛି"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଦ୍ରୁତ ଭାବେ ଚାର୍ଜ ହେଉଛି"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଧୀରେ ଚାର୍ଜ ହେଉଛି"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"ଗୋଟିଏ ତ୍ରୁଟି କାରଣରୁ eSIMକୁ ଅକ୍ଷମ କରାଯାଇପାରିବ ନାହିଁ।"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"ଏଣ୍ଟର୍"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ପାଟର୍ନ ଭୁଲି ଯାଇଛନ୍ତି"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"ଭୁଲ ପାଟର୍ନ"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"ଭୁଲ ପାସୱର୍ଡ"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"ଭୁଲ ପାଟର୍ନ"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"ଭୁଲ ପାସ୍‌ୱର୍ଡ"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"ଭୁଲ PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।</item>
@@ -140,7 +140,8 @@
       <item quantity="other"><xliff:g id="NUMBER_1">%d</xliff:g> ଘଣ୍ଟା ପାଇଁ ଡିଭାଇସ୍‍ ଅନଲକ୍‍ କରାଯାଇ ନାହିଁ। ପାସୱର୍ଡ ସୁନିଶ୍ଚିତ କରନ୍ତୁ</item>
       <item quantity="one"><xliff:g id="NUMBER_0">%d</xliff:g> ଘଣ୍ଟା ପାଇଁ ଡିଭାଇସ୍‍ ଅନଲକ୍‍ କରାଯାଇ ନାହିଁ। ପାସୱର୍ଡ ସୁନିଶ୍ଚିତ କରନ୍ତୁ</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
+    <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>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index e1379f6..08245dc 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਪਾਸਵਰਡ ਟਾਈਪ ਕਰੋ"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਪਿੰਨ ਟਾਈਪ ਕਰੋ"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"ਆਪਣਾ ਪਿੰਨ ਦਾਖਲ ਕਰੋ"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"ਆਪਣਾ ਪੈਟਰਨ ਦਾਖਲ ਕਰੋ"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"ਆਪਣਾ ਪਾਸਵਰਡ ਦਾਖਲ ਕਰੋ"</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">"ਗਲਤ ਪਿੰਨ ਕੋਡ।"</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ਅਵੈਧ ਕਾਰਡ।"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"ਚਾਰਜ ਹੋ ਗਿਆ"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"ਪੂਰਾ ਚਾਰਜ"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਤੇਜ਼ੀ ਨਾਲ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਹੌਲੀ-ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"ਕੋਈ ਗੜਬੜ ਹੋਣ ਕਰਕੇ ਈ-ਸਿਮ ਬੰਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"ਦਾਖਲ ਕਰੋ"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ਪੈਟਰਨ ਭੁੱਲ ਗਏ"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"ਗਲਤ ਪੈਟਰਨ"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"ਗਲਤ ਪਾਸਵਰਡ"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"ਗਲਤ ਪੈਟਰਨ"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"ਗਲਤ ਪਾਸਵਰਡ"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"ਗਲਤ ਪਿੰਨ"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> ਸਕਿੰਟ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।</item>
@@ -140,7 +140,8 @@
       <item quantity="one">ਡੀਵਾਈਸ <xliff:g id="NUMBER_1">%d</xliff:g> ਘੰਟੇ ਤੋਂ ਅਣਲਾਕ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ। ਪਾਸਵਰਡ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ</item>
       <item quantity="other">ਡੀਵਾਈਸ <xliff:g id="NUMBER_1">%d</xliff:g> ਘੰਟਿਆਂ ਤੋਂ ਅਣਲਾਕ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ। ਪਾਸਵਰਡ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
+    <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="one">ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ। ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ।</item>
       <item quantity="other">ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ। ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।</item>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 8376a36..49792e2 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Wpisz hasło, aby odblokować"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Wpisz kod PIN, aby odblokować"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Wpisz kod PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Narysuj wzór"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Wpisz hasło"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Narysuj wzór"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Wpisz hasło"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Nieprawidłowy kod PIN."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nieprawidłowa karta."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Naładowana"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Bateria w pełni naładowana"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ładowanie"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Szybkie ładowanie"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wolne ładowanie"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Nie można wyłączyć karty eSIM z powodu błędu."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Nie pamiętam wzoru"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Nieprawidłowy wzór"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Nieprawidłowe hasło"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Nieprawidłowy wzór"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Nieprawidłowe hasło"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Nieprawidłowy kod PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="few">Spróbuj ponownie za <xliff:g id="NUMBER">%d</xliff:g> sekundy.</item>
@@ -152,7 +152,8 @@
       <item quantity="other">Urządzenie nie zostało odblokowane od <xliff:g id="NUMBER_1">%d</xliff:g> godziny. Potwierdź hasło.</item>
       <item quantity="one">Urządzenie nie zostało odblokowane od <xliff:g id="NUMBER_0">%d</xliff:g> godziny. Potwierdź hasło.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nie rozpoznano"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nie rozpoznano"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nie rozpoznano"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="few">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> próby.</item>
       <item quantity="many">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> prób.</item>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index 8970560..862b964a 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Digite a senha para desbloquear"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Insira o PIN para desbloquear"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Digite seu PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Digite seu padrão"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Digite sua senha"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Digite seu padrão"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Digite sua senha"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Código PIN incorreto."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Cartão inválido."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Carregada"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Carga completa"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando rapidamente"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando lentamente"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Não é possível desativar o eSIM devido a um erro."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Inserir"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Esqueci o padrão"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Padrão incorreto"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Senha incorreta"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Padrão incorreto"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Senha incorreta"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN incorreto"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Tente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundo.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> hora. Confirme a senha.</item>
       <item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme a senha.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Não reconhecido"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Não reconhecido"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Não reconhecido"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="one">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
       <item quantity="other">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index a2f8aea..321b898 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Introduza a palavra-passe para desbloquear"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Introduza o PIN para desbloquear"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Introduza o PIN."</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Introduza o padrão."</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Introduza a palavra-passe."</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Introduza o padrão."</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Introduza a palavra-passe."</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Código PIN incorreto."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Cartão inválido."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Carregada"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Totalmente carregada"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar…"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar rapidamente…"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar lentamente…"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Não é possível desativar o eSIM devido a um erro."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Tecla Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Esqueceu-se do padrão"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Padrão incorreto"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Palavra-passe incorreta"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Padrão incorreto."</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Palavra-passe incorreta."</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN incorreto"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Tente novamente dentro de <xliff:g id="NUMBER">%d</xliff:g> segundos.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme a palavra-passe.</item>
       <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme a palavra-passe.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Não reconhecido"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Não reconhecido."</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Não reconhecido."</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Introduza o PIN do cartão SIM. Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas.</item>
       <item quantity="one">Introduza o PIN do cartão SIM. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de ser necessário contactar o operador para desbloquear o dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index 8970560..862b964a 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Digite a senha para desbloquear"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Insira o PIN para desbloquear"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Digite seu PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Digite seu padrão"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Digite sua senha"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Digite seu padrão"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Digite sua senha"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Código PIN incorreto."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Cartão inválido."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Carregada"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Carga completa"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando rapidamente"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando lentamente"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Não é possível desativar o eSIM devido a um erro."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Inserir"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Esqueci o padrão"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Padrão incorreto"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Senha incorreta"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Padrão incorreto"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Senha incorreta"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN incorreto"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Tente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundo.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> hora. Confirme a senha.</item>
       <item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme a senha.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Não reconhecido"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Não reconhecido"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Não reconhecido"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="one">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
       <item quantity="other">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index 148772b..083adbd 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Introduceți parola pentru a debloca"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Introduceți codul PIN pentru a debloca"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Introduceți codul PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Introduceți modelul"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Introduceți parola"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Introduceți modelul"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Introduceți parola"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Cod PIN incorect."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Card nevalid"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Încărcată"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Complet încărcată"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă rapid"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă lent"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Cardul eSIM nu poate fi dezactivat din cauza unei erori."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Introduceți"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Ați uitat modelul"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Model greșit"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Parolă greșită"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Model greșit"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Parolă greșită"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Cod PIN greșit"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="few">Încercați din nou în <xliff:g id="NUMBER">%d</xliff:g> secunde.</item>
@@ -146,7 +146,8 @@
       <item quantity="other">Dispozitivul nu a fost deblocat de <xliff:g id="NUMBER_1">%d</xliff:g> de ore. Confirmați parola.</item>
       <item quantity="one">Dispozitivul nu a fost deblocat de <xliff:g id="NUMBER_0">%d</xliff:g> oră. Confirmați parola.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nu este recunoscută"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nu este recunoscută"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nu este recunoscut"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="few">Introduceți codul PIN pentru cardul SIM. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> încercări.</item>
       <item quantity="other">Introduceți codul PIN pentru cardul SIM. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> de încercări.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index c6d06aa..e5b1d0e 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"Введите графический ключ"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Введите пароль"</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">"Ошибка SIM-карты."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Батарея заряжена"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Аккумулятор полностью заряжен"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"Идет зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"Идет быстрая зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"Идет медленная зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Не удалось отключить eSIM."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Клавиша ввода"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Забыли графический ключ?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Неверный графический ключ"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Неверный пароль"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Неверный графический ключ"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Неверный пароль"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Неверный PIN-код"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Повторите попытку через <xliff:g id="NUMBER">%d</xliff:g> секунду.</item>
@@ -152,7 +152,8 @@
       <item quantity="many">Устройство не разблокировалось в течение <xliff:g id="NUMBER_1">%d</xliff:g> часов. Введите пароль ещё раз.</item>
       <item quantity="other">Устройство не разблокировалось в течение <xliff:g id="NUMBER_1">%d</xliff:g> часа. Введите пароль ещё раз.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Не распознано"</string>
+    <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="one">Введите PIN-код. Осталась <xliff:g id="NUMBER_1">%d</xliff:g> попытка.</item>
       <item quantity="few">Введите PIN-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попытки.</item>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index 7e3b635..1955edd 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"ඔබගේ රටාව ඇතුළු කරන්න"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"ඔබගේ මුරපදය ඇතුළු කරන්න"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"අරෝපිතයි"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"සම්පූර්ණයෙන් ආරෝපණය වී ඇත"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ආරෝපණය වෙමින්"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • වේගයෙන් ආරෝපණය වෙමින්"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • සෙමින් ආරෝපණය වෙමින්"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"දෝෂයක් හේතුවෙන් eSIM අබල කළ නොහැකිය."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"ඇතුල් කරන්න"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"රටාව අමතකයි"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"වැරදි රටාවකි"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"වැරදි මුරපදය"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"වැරදි රටාවකි"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"වැරදි මුරපදයකි"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN එක වැරදියි"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">තත්පර <xliff:g id="NUMBER">%d</xliff:g>කින් නැවත උත්සාහ කරන්න.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">උපාංගය පැය <xliff:g id="NUMBER_1">%d</xliff:g>ක් අගුලු හැර නැත. මුරපදය තහවුරු කරන්න.</item>
       <item quantity="other">උපාංගය පැය <xliff:g id="NUMBER_1">%d</xliff:g>ක් අගුලු හැර නැත. මුරපදය තහවුරු කරන්න.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"හඳුනා නොගන්නා ලදී"</string>
+    <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="one">SIM PIN ඇතුළු කරන්න, ඔබ සතුව උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
       <item quantity="other">SIM PIN ඇතුළු කරන්න, ඔබ සතුව උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index a0cd7a5..f9f39e5 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Zadajte heslo na odomknutie"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Zadajte kód PIN na odomknutie"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Zadajte PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Zadajte vzor"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Zadajte heslo"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Zadajte vzor"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Zadajte heslo"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Nesprávny kód PIN."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Neplatná karta."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Nabité"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Úplne nabité"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa rýchlo"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa pomaly"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM karta sa nedá deaktivovať, pretože sa vyskytla chyba."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Nepamätám si vzor"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Nesprávny vzor"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Nesprávne heslo"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Nesprávny vzor"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Nesprávne heslo"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Nesprávny kód PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="few">Skúste to znova o <xliff:g id="NUMBER">%d</xliff:g> sekundy.</item>
@@ -152,7 +152,8 @@
       <item quantity="other">Zariadenie nebolo odomknuté <xliff:g id="NUMBER_1">%d</xliff:g> hodín. Potvrďte heslo.</item>
       <item quantity="one">Zariadenie nebolo odomknuté <xliff:g id="NUMBER_0">%d</xliff:g> hodinu. Potvrďte heslo.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nerozpoznané"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nerozpoznané"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nerozpoznané"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="few">Zadajte kód PIN SIM karty. Zostávajú vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
       <item quantity="many">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index 6bcae3e..2a4417c 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Vnesite geslo za odklepanje"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Vnesite kodo PIN za odklepanje"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Vnesite kodo PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Vnesite vzorec"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Vnesite geslo"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Vnesite vzorec"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Vnesite geslo"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Napačna koda PIN."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Neveljavna kartica"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Akumulator napolnjen"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Popolnoma napolnjen"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • polnjenje"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • hitro polnjenje"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • počasno polnjenje"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Digitalne kartice e-SIM zaradi napake ni mogoče onemogočiti."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Tipka Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Pozabljen vzorec"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Napačen vzorec"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Napačno geslo"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Napačen vzorec"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Napačno geslo"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Napačna koda PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Poskusite znova čez <xliff:g id="NUMBER">%d</xliff:g> sekundo.</item>
@@ -152,7 +152,8 @@
       <item quantity="few">Naprava ni bila odklenjena <xliff:g id="NUMBER_1">%d</xliff:g> ure. Potrdite geslo.</item>
       <item quantity="other">Naprava ni bila odklenjena <xliff:g id="NUMBER_1">%d</xliff:g> ur. Potrdite geslo.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Ni prepoznano"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Ni prepoznano"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Ni prepoznano"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="one">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskus.</item>
       <item quantity="two">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskusa.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml
index dca23885..4057e03 100644
--- a/packages/SystemUI/res-keyguard/values-sq/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Shkruaj fjalëkalimin për të shkyçur"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Shkruaj kodin PIN për ta shkyçur"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Fut kodin PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Fut motivin"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Fut fjalëkalimin"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Fut motivin"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Fut fjalëkalimin"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Kodi PIN është i pasaktë."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Karta e pavlefshme."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"I ngarkuar"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"I ngarkuar plotësisht"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po ngarkohet"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po ngarkohet me shpejtësi"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po ngarkohet ngadalë"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Karta eSIM nuk mund të çaktivizohet për shkak të një gabimi."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Dërgo"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Harrova motivin"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Motivi është i gabuar"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Fjalëkalim i gabuar"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Motiv i gabuar"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Fjalëkalim i gabuar"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Kod PIN i gabuar"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Provo sërish për <xliff:g id="NUMBER">%d</xliff:g> sekonda.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Pajisja nuk është shkyçur për <xliff:g id="NUMBER_1">%d</xliff:g> orë. Konfirmo fjalëkalimin.</item>
       <item quantity="one">Pajisja nuk është shkyçur për <xliff:g id="NUMBER_0">%d</xliff:g> orë. Konfirmo fjalëkalimin.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nuk njihet"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nuk njihet"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nuk njihet"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Fut kodin PIN të kartës SIM. Të kanë mbetur edhe <xliff:g id="NUMBER_1">%d</xliff:g> tentativa.</item>
       <item quantity="one">Fut kodin PIN të kartës SIM. Të ka mbetur edhe <xliff:g id="NUMBER_0">%d</xliff:g> tentativë para se të duhet të kontaktosh me operatorin tënd celular për ta shkyçur pajisjen.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml
index 6882333..16c1bde 100644
--- a/packages/SystemUI/res-keyguard/values-sr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"Унесите шаблон"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Унесите лозинку"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Напуњена је"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Напуњена је у потпуности"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Пуни се"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо се пуни"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Споро се пуни"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"eSIM не може да се онемогући због грешке."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Заборавио/ла сам шаблон"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Погрешан шаблон"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Погрешна лозинка"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Погрешан шаблон"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Погрешна лозинка"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Погрешан PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Пробајте поново за <xliff:g id="NUMBER">%d</xliff:g> секунду.</item>
@@ -146,7 +146,8 @@
       <item quantity="few">Нисте откључали уређај <xliff:g id="NUMBER_1">%d</xliff:g> сата. Потврдите лозинку.</item>
       <item quantity="other">Нисте откључали уређај <xliff:g id="NUMBER_1">%d</xliff:g> сати. Потврдите лозинку.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Није препознат"</string>
+    <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="one">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушај.</item>
       <item quantity="few">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml
index e8fd26e..8e75aa2 100644
--- a/packages/SystemUI/res-keyguard/values-sv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Lås upp med lösenordet"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Lås upp med pinkoden"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Ange pinkoden"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Ange det grafiska lösenordet"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Ange lösenordet"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Ange det grafiska lösenordet"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Ange ditt lösenord"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Fel pinkod."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ogiltigt kort."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Laddat"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Fulladdad"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas snabbt"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas långsamt"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Det gick inte att inaktivera eSIM-kortet på grund av ett fel."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Retur"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Har du glömt ditt grafiska lösenord?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Fel grafiskt lösenord"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Fel lösenord"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Fel grafiskt lösenord"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Fel lösenord"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Fel pinkod"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Försök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Enheten har inte låsts upp på <xliff:g id="NUMBER_1">%d</xliff:g> timmar. Bekräfta lösenordet.</item>
       <item quantity="one">Enheten har inte låsts upp på <xliff:g id="NUMBER_0">%d</xliff:g> timme. Bekräfta lösenordet.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Identifierades inte"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Identifierades inte"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Identifierades inte"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Ange pinkod för SIM-kortet. <xliff:g id="NUMBER_1">%d</xliff:g> försök återstår.</item>
       <item quantity="one">Ange pinkod för SIM-kortet. <xliff:g id="NUMBER_0">%d</xliff:g> försök återstår innan du måste kontakta operatören för att låsa upp enheten.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index 3dc496a..e5277b2 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Andika nenosiri ili ufungue"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Andika PIN ili ufungue"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Weka PIN yako"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Weka Mchoro wako"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Weka Nenosiri lako"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Weka mchoro wako"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Weka nenosiri lako"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Nambari ya PIN si sahihi."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Kadi si Sahihi."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Betri imejaa"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Imejaa chaji"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji kwa kasi"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji pole pole"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Hitilafu imetokea wakati wa kuzima eSIM."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Weka"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Umesahau Mchoro"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Mchoro si Sahihi"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Nenosiri si Sahihi"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Mchoro si sahihi"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Nenosiri si sahihi"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Nambari ya PIN si sahihi"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Jaribu tena baada ya sekunde <xliff:g id="NUMBER">%d</xliff:g>.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Hujafungua kifaa kwa saa <xliff:g id="NUMBER_1">%d</xliff:g>. Thibitisha nenosiri.</item>
       <item quantity="one">Hujafungua kifaa kwa saa <xliff:g id="NUMBER_0">%d</xliff:g>. Thibitisha nenosiri.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Haikutambua alama ya kidole"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Haitambuliwi"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Haitambuliwi"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Weka PIN ya SIM. Zimesalia mara <xliff:g id="NUMBER_1">%d</xliff:g> za kujaribu.</item>
       <item quantity="one">Weka PIN ya SIM. Ukijaribu tena mara <xliff:g id="NUMBER_0">%d</xliff:g> bila kufaulu, kifaa chako kitafungwa na utalazimika uwasiliane na mtoa huduma wako ili akifungue.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index ae05463..f5fb357 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"திறக்க, கடவுச்சொல்லை உள்ளிடவும்"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"திறக்க, பின்னை உள்ளிடவும்"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"பின்னை உள்ளிடுக"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"பேட்டர்னை உள்ளிடுக"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"கடவுச்சொல்லை உள்ளிடுக"</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">"தவறான பின் குறியீடு."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"செல்லாத சிம் கார்டு."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"சார்ஜ் செய்யப்பட்டது"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"முழுவதுமாகச் சார்ஜ் ஆகிவிட்டது"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • சார்ஜாகிறது"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • வேகமாகச் சார்ஜாகிறது"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • மெதுவாகச் சார்ஜாகிறது"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"பிழை ஏற்பட்டதால் eSIMஐ முடக்க முடியவில்லை."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"என்டர் பொத்தான்"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"பேட்டர்ன் நினைவில்லையா"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"தவறான பேட்டர்ன்"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"தவறான கடவுச்சொல்"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"தவறான பேட்டர்ன்"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"தவறான கடவுச்சொல்"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"தவறான பின்"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> வினாடிகளுக்குப் பிறகு முயலவும்.</item>
@@ -140,7 +140,8 @@
       <item quantity="other"><xliff:g id="NUMBER_1">%d</xliff:g> மணிநேரமாகச் சாதனம் திறக்கப்படவில்லை. கடவுச்சொல்லை உறுதிப்படுத்தவும்.</item>
       <item quantity="one"><xliff:g id="NUMBER_0">%d</xliff:g> மணிநேரமாகச் சாதனம் திறக்கப்படவில்லை. கடவுச்சொல்லை உறுதிப்படுத்தவும்.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"அடையாளங்காண முடியவில்லை"</string>
+    <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">சிம் பின்னை உள்ளிடவும். மேலும், <xliff:g id="NUMBER_1">%d</xliff:g> வாய்ப்புகள் மீதமுள்ளன.</item>
       <item quantity="one">சிம் பின்னை உள்ளிடவும். மீதமுள்ள <xliff:g id="NUMBER_0">%d</xliff:g> வாய்ப்பில் தவறுதலான பின் உள்ளிடப்பட்டால், உங்கள் தொலைத்தொடர்பு நிறுவனத்தைத் தொடர்பு கொண்டு மட்டுமே சாதனத்தைத் திறக்க முடியும்.</item>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index 2f0377f..1e08177 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"అన్‌లాక్ చేయడానికి పాస్‌వర్డ్‌ను టైప్ చేయండి"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"అన్‌లాక్ చేయడానికి పిన్ టైప్ చేయండి"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"మీ పిన్‌ని నమోదు చేయండి"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"మీ ఆకృతిని నమోదు చేయండి"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"మీ పాస్‌వర్డ్‌ను నమోదు చేయండి"</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">"పిన్ కోడ్ తప్పు."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"చెల్లని కార్డ్."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"ఛార్జ్ చేయబడింది"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"పూర్తిగా ఛార్జ్ చేయబడింది"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ఛార్జ్ అవుతోంది"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • వేగంగా ఛార్జ్ అవుతోంది"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • నెమ్మదిగా ఛార్జ్ అవుతోంది"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"ఎర్రర్ కారణంగా eSIMని నిలపడం సాధ్యపడదు."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"నమూనాను మర్చిపోయాను"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"ఆకృతి తప్పు"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"పాస్‌వర్డ్ తప్పు"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"నమూనా తప్పు"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"పాస్‌వర్డ్ తప్పు"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"పిన్ తప్పు"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి.</item>
@@ -140,7 +140,8 @@
       <item quantity="other"><xliff:g id="NUMBER_1">%d</xliff:g> గంటల పాటు పరికరాన్ని అన్‌లాక్ చేయలేదు. పాస్‌వర్డ్‌ని నమోదు చేయండి.</item>
       <item quantity="one"><xliff:g id="NUMBER_0">%d</xliff:g> గంట పాటు పరికరాన్ని అన్‌లాక్ చేయలేదు. పాస్‌వర్డ్‌ని నమోదు చేయండి.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"గుర్తించలేదు"</string>
+    <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 పిన్‌ని నమోదు చేయండి. మీకు <xliff:g id="NUMBER_1">%d</xliff:g> ప్రయత్నలు మిగిలి ఉన్నాయి.</item>
       <item quantity="one">SIM పిన్‌ని నమోదు చేయండి, మీరు మీ పరికరాన్ని అన్‌లాక్ చేయడానికి తప్పనిసరిగా మీ క్యారియర్‌ను సంప్రదించడానికి ముందు మీకు <xliff:g id="NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది.</item>
diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml
index 92a5a08..bde50f3 100644
--- a/packages/SystemUI/res-keyguard/values-th/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-th/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"ป้อนรูปแบบ"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"ป้อนรหัสผ่าน"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"ชาร์จแล้ว"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"ชาร์จเต็มแล้ว"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จอย่างเร็ว"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จอย่างช้าๆ"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"ปิดใช้ eSIM ไม่ได้เนื่องจากมีข้อผิดพลาด"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"ลืมรูปแบบ"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"รูปแบบไม่ถูกต้อง"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"รหัสผ่านไม่ถูกต้อง"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"รูปแบบไม่ถูกต้อง"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"รหัสผ่านไม่ถูกต้อง"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN ไม่ถูกต้อง"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">ลองอีกครั้งใน <xliff:g id="NUMBER">%d</xliff:g> วินาที</item>
@@ -140,7 +140,8 @@
       <item quantity="other">ไม่มีการปลดล็อกอุปกรณ์มา <xliff:g id="NUMBER_1">%d</xliff:g> ชั่วโมงแล้ว ยืนยันรหัสผ่าน</item>
       <item quantity="one">ไม่มีการปลดล็อกอุปกรณ์มา <xliff:g id="NUMBER_0">%d</xliff:g> ชั่วโมงแล้ว ยืนยันรหัสผ่าน</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"ไม่รู้จัก"</string>
+    <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">โปรดป้อน PIN ของซิม คุณพยายามได้อีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง</item>
       <item quantity="one">โปรดป้อน PIN ของซิม คุณพยายามได้อีก <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งก่อนที่จะต้องติดต่อผู้ให้บริการเพื่อปลดล็อกอุปกรณ์</item>
diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml
index b993167..71a7537 100644
--- a/packages/SystemUI/res-keyguard/values-tl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"I-type ang password upang i-unlock"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"I-type ang PIN upang i-unlock"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Ilagay ang iyong PIN"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Ilagay ang iyong Pattern"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Ilagay ang iyong Password"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Ilagay ang iyong pattern"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Ilagay ang iyong password"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Mali ang PIN code."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Di-wasto ang Card."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Tapos nang mag-charge"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Puno na ang baterya"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nagcha-charge"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mabilis na nagcha-charge"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mabagal na nagcha-charge"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Hindi ma-disable ang eSIM dahil sa isang error."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Nakalimutan ang Pattern"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Mali ang Pattern"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Mali ang Password"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Mali ang pattern"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Mali ang password"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Mali ang PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Subukang muli sa loob ng <xliff:g id="NUMBER">%d</xliff:g> segundo.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">Hindi na-unlock ang device sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> oras. Kumpirmahin ang password.</item>
       <item quantity="other">Hindi na-unlock ang device sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> na oras. Kumpirmahin ang password.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Hindi nakilala"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Hindi nakilala"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Hindi nakilala"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="one">Ilagay ang PIN ng SIM. Mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> natitirang pagsubok.</item>
       <item quantity="other">Ilagay ang PIN ng SIM. Mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> na natitirang pagsubok.</item>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index 399134a..fbbf63e 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Kilidi açmak için şifreyi yazın"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Kilidi açmak için PIN kodunu yazın"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"PIN kodunuzu girin"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Deseninizi girin"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Şifrenizi girin"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Deseninizi girin"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Şifrenizi girin"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Yanlış PIN kodu."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Geçersiz Kart."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Ödeme alındı"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Tamamen şarj edildi"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj oluyor"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hızlı şarj oluyor"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Yavaş şarj oluyor"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Bir hata nedeniyle eSIM devre dışı bırakılamıyor."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Deseni unuttunuz mu?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Yanlış Desen"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Yanlış Şifre"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Yanlış desen"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Yanlış şifre"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Yanlış PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> saniye içinde tekrar deneyin.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Cihazın kilidi son <xliff:g id="NUMBER_1">%d</xliff:g> saattir açılmadı. Şifreyi doğrulayın.</item>
       <item quantity="one">Cihazın kilidi son <xliff:g id="NUMBER_0">%d</xliff:g> saattir açılmadı. Şifreyi doğrulayın.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Tanınmadı"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Tanınmadı"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Tanınmadı"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">SIM PIN\'inizi girin. <xliff:g id="NUMBER_1">%d</xliff:g> deneme hakkınız kaldı.</item>
       <item quantity="one">SIM PIN\'inizi girin. Cihazınızın kilidini açmak için operatörünüzle bağlantı kurmak zorunda kalmadan önce <xliff:g id="NUMBER_0">%d</xliff:g> deneme hakkınız kaldı.</item>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index 821257e..feab22f 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"Введіть ключ"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Введіть пароль"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Заряджено"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Повністю заряджений"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Заряджання"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Швидке заряджання"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Повільне заряджання"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Не вдається вимкнути eSIM-карту через помилку."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Ввести"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Не пам’ятаю ключ"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Неправильний ключ"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Неправильний пароль"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Неправильний ключ"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Неправильний пароль"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Неправильний PIN-код"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Повторіть спробу через <xliff:g id="NUMBER">%d</xliff:g> секунду.</item>
@@ -152,7 +152,8 @@
       <item quantity="many">Ви не розблоковували пристрій <xliff:g id="NUMBER_1">%d</xliff:g> годин. Підтвердьте пароль.</item>
       <item quantity="other">Ви не розблоковували пристрій <xliff:g id="NUMBER_1">%d</xliff:g> години. Підтвердьте пароль.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Не розпізнано"</string>
+    <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="one">Введіть PIN-код SIM-карти. Залишилася <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
       <item quantity="few">Введіть PIN-код SIM-карти. Залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроби.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index bfbc70f..928fcbdb 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"اپنا پیٹرن درج کریں"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"اپنا پاس ورڈ درج کریں"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"چارج ہوگئی"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"مکمل طور پر چارج ہو گيا"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • چارج ہو رہا ہے"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • تیزی سے چارج ہو رہا ہے"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • آہستہ چارج ہو رہا ہے"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"‏ایک خرابی کی وجہ سے eSIM کو غیر فعال نہیں کیا جا سکتا۔"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"درج کریں"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"پیٹرن بھول گئے"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"غلط پیٹرن"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"غلط پاسورڈ"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"غلط پیٹرن"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"غلط پاس ورڈ"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"‏غلط PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> سیکنڈز میں دوبارہ کوشش کریں۔</item>
@@ -140,7 +140,8 @@
       <item quantity="other">آلہ <xliff:g id="NUMBER_1">%d</xliff:g> گھنٹوں سے غیر مقفل نہیں کیا گيا۔ پاسورڈ کی توثیق کریں۔</item>
       <item quantity="one">آلہ <xliff:g id="NUMBER_0">%d</xliff:g> گھنٹہ سے غیر مقفل نہیں کیا گیا۔ پاسورڈ کی توثیق کریں۔</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"تسلیم شدہ نہیں ہے"</string>
+    <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>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index ad7a0dc..faa7d50 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -29,11 +29,13 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Qulfni ochish uchun parolni kiriting"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Qulfni ochish uchun PIN kodni kiriting"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"PIN kodni kiriting"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Grafik kalitni chizing"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Parolni kiriting"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Grafik kalitni chizing"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Parolni kiriting"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN kodi xato."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"SIM karta yaroqsiz."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Batareya quvvati to‘ldi"</string>
+    <!-- String.format failed for translation -->
+    <!-- no translation found for keyguard_charged (3316115607283493413) -->
+    <skip />
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Quvvat olmoqda"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Tezkor quvvat olmoqda"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sekin quvvat olmoqda"</string>
@@ -61,8 +63,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Xatolik tufayli eSIM faolsizlantirilmadi."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter tugmasi"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Grafik kalit esimdan chiqdi"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Grafik kalit xato"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Parol xato"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Grafik kalit xato"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Parol xato"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN kod xato"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> soniyadan keyin qaytadan urining.</item>
@@ -140,7 +142,8 @@
       <item quantity="other">Qurilma <xliff:g id="NUMBER_1">%d</xliff:g> soatdan beri qulfdan chiqarilgani yo‘q. Parolni yana bir marta kiriting.</item>
       <item quantity="one">Qurilma <xliff:g id="NUMBER_0">%d</xliff:g> soatdan beri qulfdan chiqarilgani yo‘q. Parolni yana bir marta kiriting.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Barmoq izi aniqlanmadi"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Aniqlanmadi"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Aniqlanmadi"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">SIM PIN kodini kiriting, sizda <xliff:g id="NUMBER_1">%d</xliff:g> ta urinish bor.</item>
       <item quantity="one">SIM PIN kodini kiriting, qurilmani qulfdan chiqarish uchun sizda <xliff:g id="NUMBER_0">%d</xliff:g> ta urinish bor.</item>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index 1a2d6b4..6b06513 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Nhập mật khẩu để mở khóa"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Nhập mã PIN để mở khóa"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Nhập mã PIN của bạn"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Nhập hình mở khóa của bạn"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Nhập mật khẩu của bạn"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Nhập hình mở khóa của bạn"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Nhập mật khẩu của bạn"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Mã PIN không chính xác."</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Thẻ không hợp lệ."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Đã sạc đầy"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Đã sạc đầy"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc nhanh"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc chậm"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"Không thể tắt eSIM do lỗi."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Nhập"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Đã quên hình mở khóa"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Hình mở khóa sai"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Mật khẩu sai"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Hình mở khóa sai"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Mật khẩu sai"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Mã PIN sai"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">Hãy thử lại sau <xliff:g id="NUMBER">%d</xliff:g> giây.</item>
@@ -140,7 +140,8 @@
       <item quantity="other">Thiết bị đã không được mở khóa trong <xliff:g id="NUMBER_1">%d</xliff:g> giờ. Xác nhận mật khẩu.</item>
       <item quantity="one">Thiết bị đã không được mở khóa trong <xliff:g id="NUMBER_0">%d</xliff:g> giờ. Xác nhận mật khẩu.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Không nhận dạng được"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Không nhận dạng được"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Không nhận dạng được"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="other">Hãy nhập mã PIN của SIM. Bạn còn <xliff:g id="NUMBER_1">%d</xliff:g> lần thử.</item>
       <item quantity="one">Hãy nhập mã PIN của SIM. Bạn còn <xliff:g id="NUMBER_0">%d</xliff:g> lần thử trước khi phải liên hệ với nhà mạng để mở khóa thiết bị của mình.</item>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index f6576b3..44f06a6 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"绘制您的图案"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"输入您的密码"</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">"SIM 卡无效。"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"已充满电"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"充电完成"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在充电"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在快速充电"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在慢速充电"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"出现错误,无法停用 eSIM 卡。"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"输入"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"忘记了图案"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"图案错误"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"密码错误"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"图案错误"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"密码错误"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN 码错误"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">请在 <xliff:g id="NUMBER">%d</xliff:g> 秒后重试。</item>
@@ -140,7 +140,8 @@
       <item quantity="other">设备已保持锁定状态达 <xliff:g id="NUMBER_1">%d</xliff:g> 小时。请确认密码。</item>
       <item quantity="one">设备已保持锁定状态达 <xliff:g id="NUMBER_0">%d</xliff:g> 小时。请确认密码。</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"无法识别"</string>
+    <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>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
index af238dd..364f126 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"請輸入圖案"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"請輸入密碼"</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">"SIM 卡無效。"</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"已完成充電"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"充電完成"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在充電"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在快速充電"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> •正在慢速充電"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"發生錯誤,因此無法停用此 eSIM 卡。"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter 鍵 (輸入)"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"忘記上鎖圖案"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"上鎖圖案錯誤"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"密碼錯誤"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"圖案錯誤"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"密碼錯誤"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN 碼錯誤"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">請在 <xliff:g id="NUMBER">%d</xliff:g> 秒後再試一次。</item>
@@ -140,7 +140,8 @@
       <item quantity="other">裝置在過去 <xliff:g id="NUMBER_1">%d</xliff:g> 小時內未有解鎖,請確認密碼。</item>
       <item quantity="one">裝置在過去 <xliff:g id="NUMBER_0">%d</xliff:g> 小時內未有解鎖,請確認密碼。</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"未能識別"</string>
+    <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>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index b8cdfc3..f687397 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -29,11 +29,11 @@
     <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="3915717164691787047">"輸入解鎖圖案"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"輸入密碼"</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>
-    <string name="keyguard_charged" msgid="2222329688813033109">"充電完成"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"充電完成"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 快速充電中"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 慢速充電中"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"發生錯誤,因此無法停用 eSIM 卡。"</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Enter 鍵"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"忘記解鎖圖案"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"解鎖圖案錯誤"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"密碼錯誤"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"圖案錯誤"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"密碼錯誤"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN 碼錯誤"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="other">請於 <xliff:g id="NUMBER">%d</xliff:g> 秒後再試一次。</item>
@@ -140,7 +140,8 @@
       <item quantity="other">裝置已有 <xliff:g id="NUMBER_1">%d</xliff:g> 小時未解鎖。請確認密碼。</item>
       <item quantity="one">裝置已有 <xliff:g id="NUMBER_0">%d</xliff:g> 小時未解鎖。請確認密碼。</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"無法識別"</string>
+    <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>
diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml
index 5409638..66904eb 100644
--- a/packages/SystemUI/res-keyguard/values-zu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml
@@ -29,11 +29,11 @@
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Bhala iphasiwedi ukuze kuvuleke"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Faka i-PIN ukuvula"</string>
     <string name="keyguard_enter_your_pin" msgid="7152989016739952871">"Faka iPHINIKHODI yakho"</string>
-    <string name="keyguard_enter_your_pattern" msgid="3915717164691787047">"Faka iphethini yakho"</string>
-    <string name="keyguard_enter_your_password" msgid="5761514484663983731">"Faka iphasiwedi yakho"</string>
+    <string name="keyguard_enter_your_pattern" msgid="4297890206109830353">"Faka iphethini yakho"</string>
+    <string name="keyguard_enter_your_password" msgid="5397328359341314506">"Faka iphasiwedi yakho"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Ikhodi ye-PIN engalungile!"</string>
     <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ikhadi elingavumelekile."</string>
-    <string name="keyguard_charged" msgid="2222329688813033109">"Kushajiwe"</string>
+    <string name="keyguard_charged" msgid="3316115607283493413">"Ishaje ngokuphelele"</string>
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Iyashaja"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ishaja kaningi"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ishaja kancane"</string>
@@ -61,8 +61,8 @@
     <string name="error_disable_esim_msg" msgid="676694908770135639">"I-eSIM ayikwakhi ukukhutshazwa ngenxa yephutha."</string>
     <string name="keyboardview_keycode_enter" msgid="4505833604411016668">"Faka"</string>
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Ukhohlwe iphethini?"</string>
-    <string name="kg_wrong_pattern" msgid="7620081431514773802">"Iphatheni engalungile"</string>
-    <string name="kg_wrong_password" msgid="4580683060277329277">"Iphasiwedi engalungile"</string>
+    <string name="kg_wrong_pattern" msgid="2873443744087746812">"Iphethini engalungile"</string>
+    <string name="kg_wrong_password" msgid="8060364776224836597">"Iphasiwedi engalungile"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"Iphinikhodi engalungile"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Zama futhi kumasekhondi angu-<xliff:g id="NUMBER">%d</xliff:g>.</item>
@@ -140,7 +140,8 @@
       <item quantity="one">Idivayisi ayikavulwa ngamahora angu-<xliff:g id="NUMBER_1">%d</xliff:g>. Qinisekisa iphasiwedi.</item>
       <item quantity="other">Idivayisi ayikavulwa ngamahora angu-<xliff:g id="NUMBER_1">%d</xliff:g>. Qinisekisa iphasiwedi.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="348813995267914625">"Akubonwa"</string>
+    <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Akwaziwa"</string>
+    <string name="kg_face_not_recognized" msgid="6382535088345875294">"Akwaziwa"</string>
     <plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
       <item quantity="one">Faka i-PIN ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
       <item quantity="other">Faka i-PIN ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
diff --git a/packages/SystemUI/res-keyguard/values/attrs.xml b/packages/SystemUI/res-keyguard/values/attrs.xml
index e2ce210..293b683 100644
--- a/packages/SystemUI/res-keyguard/values/attrs.xml
+++ b/packages/SystemUI/res-keyguard/values/attrs.xml
@@ -38,9 +38,5 @@
         <attr name="android:textColor" format="color" />
     </declare-styleable>
 
-    <declare-styleable name="CarrierText">
-        <attr name="allCaps" format="boolean" />
-    </declare-styleable>
-
     <attr name="passwordStyle" format="reference" />
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 513d848..1d5aa6d 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -48,10 +48,10 @@
     <string name="keyguard_enter_your_pin">Enter your PIN</string>
 
     <!-- Instructions telling the user to enter their pattern to unlock the keyguard [CHAR LIMIT=30] -->
-    <string name="keyguard_enter_your_pattern">Enter your Pattern</string>
+    <string name="keyguard_enter_your_pattern">Enter your pattern</string>
 
     <!-- Instructions telling the user to enter their text password to unlock the keyguard [CHAR LIMIT=30] -->
-    <string name="keyguard_enter_your_password">Enter your Password</string>
+    <string name="keyguard_enter_your_password">Enter your password</string>
 
     <!-- Instructions telling the user that they entered the wrong pin while trying
          to unlock the keyguard.  Displayed in one line in a large font.  -->
@@ -62,7 +62,7 @@
 
     <!-- When the lock screen is showing, the phone is plugged in and the battery is fully
          charged, say that it is charged. -->
-    <string name="keyguard_charged">Charged</string>
+    <string name="keyguard_charged">Fully charged</string>
 
     <!-- When the lock screen is showing and the phone plugged in, and the battery
          is not fully charged, say that it's charging.  -->
@@ -146,9 +146,9 @@
     <!-- Message shown in pattern unlock after some number of unsuccessful attempts -->
     <string name="kg_forgot_pattern_button_text">Forgot Pattern</string>
     <!-- Message shown when user enters wrong pattern -->
-    <string name="kg_wrong_pattern">Wrong Pattern</string>
+    <string name="kg_wrong_pattern">Wrong pattern</string>
     <!-- Message shown when user enters wrong password -->
-    <string name="kg_wrong_password">Wrong Password</string>
+    <string name="kg_wrong_password">Wrong password</string>
     <!-- Message shown when user enters wrong PIN -->
     <string name="kg_wrong_pin">Wrong PIN</string>
     <!-- Countdown message shown after too many failed unlock attempts -->
@@ -381,7 +381,10 @@
     </plurals>
 
     <!-- Fingerprint hint message when finger was not recognized.-->
-    <string name="fingerprint_not_recognized">Not recognized</string>
+    <string name="kg_fingerprint_not_recognized">Not recognized</string>
+
+    <!-- Face hint message when finger was not recognized. [CHAR LIMIT=20] -->
+    <string name="kg_face_not_recognized">Not recognized</string>
 
     <!-- Instructions telling the user remaining times when enter SIM PIN view.  -->
     <plurals name="kg_password_default_pin_message">
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_01_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_01_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_01_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_02_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_02_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_02_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_03_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_03_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_03_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_04_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_04_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_04_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_05_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_05_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_05_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_06_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_06_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_06_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_07_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_07_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_07_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_08_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_08_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_08_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_09_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_09_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_09_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_10_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_10_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_10_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_11_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_11_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_11_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_12_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_12_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_12_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_13_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_13_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_13_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_14_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_14_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_14_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_15_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_15_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_15_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_16_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_16_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_16_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_17_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_17_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_17_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging__dot_18_pivot_animation.xml b/packages/SystemUI/res/anim/wireless_charging__dot_18_pivot_animation.xml
new file mode 100644
index 0000000..fd5561b
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging__dot_18_pivot_animation.xml
@@ -0,0 +1,27 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-25.0 c 0.0,-11.16667 0.0,-55.83333 0.0,-67.0"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_1" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_10_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_10_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_10_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_11_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_11_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_11_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_12_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_12_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_12_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_13_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_13_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_13_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_14_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_14_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_14_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_15_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_15_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_15_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_16_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_16_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_16_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_17_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_17_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_17_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_18_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_18_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_18_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_1_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_1_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_1_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_2_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_2_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_2_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_3_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_3_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_3_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_4_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_4_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_4_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_5_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_5_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_5_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_6_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_6_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_6_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_7_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_7_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_7_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_8_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_8_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_8_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_ellipse_path_9_animation.xml b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_9_animation.xml
new file mode 100644
index 0000000..d20e741
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_ellipse_path_9_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="150"
+        android:propertyName="pathData"
+        android:valueFrom="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-3.5 c 1.9329966243,0.0 3.5,1.5670033757 3.5,3.5 c 0.0,1.9329966243 -1.5670033757,3.5 -3.5,3.5 c -1.9329966243,0.0 -3.5,-1.5670033757 -3.5,-3.5 c 0.0,-1.9329966243 1.5670033757,-3.5 3.5,-3.5 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/wireless_charging_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/wireless_charging_null_1_animation.xml b/packages/SystemUI/res/anim/wireless_charging_null_1_animation.xml
new file mode 100644
index 0000000..fffae80
--- /dev/null
+++ b/packages/SystemUI/res/anim/wireless_charging_null_1_animation.xml
@@ -0,0 +1,28 @@
+<?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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="1166"
+        android:propertyName="rotation"
+        android:valueFrom="0.0"
+        android:valueTo="20.0"
+        android:valueType="floatType"
+        android:interpolator="@android:interpolator/linear" />
+</set>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/color/qs_detail_progress_track.xml b/packages/SystemUI/res/color-night/qs_detail_progress_track.xml
similarity index 100%
rename from packages/overlays/SysuiDarkThemeOverlay/res/color/qs_detail_progress_track.xml
rename to packages/SystemUI/res/color-night/qs_detail_progress_track.xml
diff --git a/packages/SystemUI/res/color/notification_guts_buttons.xml b/packages/SystemUI/res/color/notification_guts_buttons.xml
index 3b8d59b..412e0be 100644
--- a/packages/SystemUI/res/color/notification_guts_buttons.xml
+++ b/packages/SystemUI/res/color/notification_guts_buttons.xml
@@ -2,6 +2,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_checked="true"
           android:color="?android:attr/colorAccent" />
-    <item android:color="@android:color/black"
+    <item android:color="@color/notification_primary_text_color"
           android:alpha=".54" />
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable-hdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-hdpi/qs_scrubber_track.9.png
deleted file mode 100644
index 0899d35..0000000
--- a/packages/SystemUI/res/drawable-hdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-mdpi/qs_scrubber_track.9.png
deleted file mode 100644
index 2266449..0000000
--- a/packages/SystemUI/res/drawable-mdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-xhdpi/qs_scrubber_track.9.png
deleted file mode 100644
index 3328add..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-xxhdpi/qs_scrubber_track.9.png
deleted file mode 100644
index ed651da..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-xxxhdpi/qs_scrubber_track.9.png
deleted file mode 100644
index 06e1202..0000000
--- a/packages/SystemUI/res/drawable-xxxhdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_qs_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_qs_auto_rotate.xml
index 9c24c2c..47e1059 100644
--- a/packages/SystemUI/res/drawable/ic_qs_auto_rotate.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_auto_rotate.xml
@@ -22,5 +22,5 @@
     android:viewportHeight="24.0">
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M12.72,23H11C5.49,23 1,18.51 1,13h2c0,3.78 2.63,6.95 6.15,7.79L8.13,19L9.87,18L12.72,23zM13,1h-1.72l2.85,5L15.87,5l-1.02,-1.79C18.37,4.05 21,7.22 21,11h2C23,5.49 18.51,1 13,1zM10.23,6L18,13.76L13.77,18L6,10.24L10.23,6C10.23,6 10.23,6 10.23,6M10.23,4C9.72,4 9.21,4.2 8.82,4.59L4.59,8.82c-0.78,0.78 -0.78,2.04 0,2.82l7.77,7.77c0.39,0.39 0.9,0.59 1.41,0.59c0.51,0 1.02,-0.2 1.41,-0.59l4.24,-4.24c0.78,-0.78 0.78,-2.04 0,-2.82l-7.77,-7.77C11.26,4.2 10.75,4 10.23,4L10.23,4z"/>
+        android:pathData="M7.4,10.94H2.45V5.99l2,0.01v1.53l4.61,-4.61c0.64,-0.64 1.67,-0.66 2.29,-0.04l8.18,8.18h-2.83l-6.48,-6.48L5.86,8.95H7.4V10.94zM16.6,13.06l0.01,2h1.53l-4.36,4.36l-6.48,-6.48H4.46l8.19,8.19c0.62,0.62 1.65,0.6 2.29,-0.04l4.61,-4.61l0.01,0.01v1.53h1.99v-4.95H16.6z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
index 2cd7883..3304c19 100644
--- a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
+++ b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
@@ -17,16 +17,16 @@
 <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:name="root"
-                android:width="21dp"
-                android:height="21dp"
-                android:viewportWidth="24.0"
-                android:viewportHeight="24.0">
+                android:width="28dp"
+                android:height="28dp"
+                android:viewportWidth="28.0"
+                android:viewportHeight="28.0">
             <!-- Use scaleX to flip icon so arrows always point in the direction of motion -->
-            <group android:name="icon" android:pivotX="12" android:pivotY="12"
+            <group android:name="icon" android:pivotX="14" android:pivotY="14"
                    android:scaleX="?attr/rotateButtonScaleX">
                 <!-- Tint color to be set directly -->
                 <path android:fillColor="#FFFFFFFF"
-                      android:pathData="M19,12c0,1.72 -0.63,3.3 -1.66,4.52l-1.44,-1.44C16.58,14.23 17,13.17 17,12c0,-2.76 -2.24,-5 -5,-5c-0.06,0 -0.11,0.01 -0.17,0.01l1.08,1.08L11.5,9.5L8,6l3.5,-3.5l1.41,1.42l-1.09,1.09C11.88,5.01 11.94,5 12,5C15.87,5 19,8.13 19,12zM12.5,14.51l-1.41,1.41l1.06,1.06C12.1,16.99 12.05,17 12,17c-2.76,0 -5,-2.24 -5,-5c0,-1.17 0.42,-2.23 1.09,-3.08L6.66,7.48C5.62,8.7 5,10.28 5,12c0,3.87 3.13,7 7,7c0.06,0 0.13,-0.01 0.19,-0.01v0l-1.1,1.1l1.41,1.41L16,18L12.5,14.51z"/>
+                      android:pathData="M12.02,10.83L9.25,8.06l2.77,-2.77l1.12,1.12l-0.85,0.86h5.16c0.72,0 1.31,0.56 1.31,1.26v9.16l-1.58,-1.58V8.85h-4.89l0.86,0.86L12.02,10.83zM15.98,17.17l-1.12,1.12l0.85,0.86h-4.88v-7.26L9.25,10.3v9.17c0,0.7 0.59,1.26 1.31,1.26h5.16v0.01l-0.85,0.85l1.12,1.12l2.77,-2.77L15.98,17.17z"/>
             </group>
         </vector>
     </aapt:attr>
diff --git a/packages/SystemUI/res/drawable/wireless_charging.xml b/packages/SystemUI/res/drawable/wireless_charging.xml
new file mode 100644
index 0000000..0bf633a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/wireless_charging.xml
@@ -0,0 +1,300 @@
+<?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:name="wireless_charging"
+    android:width="200dp"
+    android:viewportWidth="200"
+    android:height="200dp"
+    android:viewportHeight="200" >
+    <group
+        android:name="null_1"
+        android:translateX="100"
+        android:translateY="100" >
+        <group
+            android:name="dot_01" >
+            <group
+                android:name="dot_01_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_1" >
+                    <path
+                        android:name="ellipse_path_1"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_02"
+            android:rotation="20" >
+            <group
+                android:name="dot_02_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_2" >
+                    <path
+                        android:name="ellipse_path_2"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_03"
+            android:rotation="40" >
+            <group
+                android:name="dot_03_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_3" >
+                    <path
+                        android:name="ellipse_path_3"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_04"
+            android:rotation="60" >
+            <group
+                android:name="dot_04_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_4" >
+                    <path
+                        android:name="ellipse_path_4"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_05"
+            android:rotation="80" >
+            <group
+                android:name="dot_05_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_5" >
+                    <path
+                        android:name="ellipse_path_5"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_06"
+            android:rotation="100" >
+            <group
+                android:name="dot_06_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_6" >
+                    <path
+                        android:name="ellipse_path_6"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_07"
+            android:rotation="120" >
+            <group
+                android:name="dot_07_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_7" >
+                    <path
+                        android:name="ellipse_path_7"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_08"
+            android:rotation="140" >
+            <group
+                android:name="dot_08_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_8" >
+                    <path
+                        android:name="ellipse_path_8"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_09"
+            android:rotation="160" >
+            <group
+                android:name="dot_09_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_9" >
+                    <path
+                        android:name="ellipse_path_9"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_10"
+            android:rotation="180" >
+            <group
+                android:name="dot_10_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_10" >
+                    <path
+                        android:name="ellipse_path_10"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_11"
+            android:rotation="200" >
+            <group
+                android:name="dot_11_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_11" >
+                    <path
+                        android:name="ellipse_path_11"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_12"
+            android:rotation="220" >
+            <group
+                android:name="dot_12_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_12" >
+                    <path
+                        android:name="ellipse_path_12"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_13"
+            android:rotation="240" >
+            <group
+                android:name="dot_13_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_13" >
+                    <path
+                        android:name="ellipse_path_13"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_14"
+            android:rotation="260" >
+            <group
+                android:name="dot_14_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_14" >
+                    <path
+                        android:name="ellipse_path_14"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_15"
+            android:rotation="280" >
+            <group
+                android:name="dot_15_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_15" >
+                    <path
+                        android:name="ellipse_path_15"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_16"
+            android:rotation="300" >
+            <group
+                android:name="dot_16_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_16" >
+                    <path
+                        android:name="ellipse_path_16"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_17"
+            android:rotation="320" >
+            <group
+                android:name="dot_17_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_17" >
+                    <path
+                        android:name="ellipse_path_17"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="dot_18"
+            android:rotation="340" >
+            <group
+                android:name="dot_18_pivot"
+                android:translateY="-25" >
+                <group
+                    android:name="ellipse_18" >
+                    <path
+                        android:name="ellipse_path_18"
+                        android:fillColor="?attr/chargingAnimColor"
+                        android:pathData="M 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 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/wireless_charging_animation.xml b/packages/SystemUI/res/drawable/wireless_charging_animation.xml
new file mode 100644
index 0000000..4cf13b1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/wireless_charging_animation.xml
@@ -0,0 +1,133 @@
+<?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.
+*/
+-->
+<animated-vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/wireless_charging" >
+    <target
+        android:name="null_1"
+        android:animation="@anim/wireless_charging_null_1_animation" />
+    <target
+        android:name="dot_01_pivot"
+        android:animation="@anim/wireless_charging__dot_01_pivot_animation" />
+    <target
+        android:name="ellipse_path_1"
+        android:animation="@anim/wireless_charging_ellipse_path_1_animation" />
+    <target
+        android:name="dot_02_pivot"
+        android:animation="@anim/wireless_charging__dot_02_pivot_animation" />
+    <target
+        android:name="ellipse_path_2"
+        android:animation="@anim/wireless_charging_ellipse_path_2_animation" />
+    <target
+        android:name="dot_03_pivot"
+        android:animation="@anim/wireless_charging__dot_03_pivot_animation" />
+    <target
+        android:name="ellipse_path_3"
+        android:animation="@anim/wireless_charging_ellipse_path_3_animation" />
+    <target
+        android:name="dot_04_pivot"
+        android:animation="@anim/wireless_charging__dot_04_pivot_animation" />
+    <target
+        android:name="ellipse_path_4"
+        android:animation="@anim/wireless_charging_ellipse_path_4_animation" />
+    <target
+        android:name="dot_05_pivot"
+        android:animation="@anim/wireless_charging__dot_05_pivot_animation" />
+    <target
+        android:name="ellipse_path_5"
+        android:animation="@anim/wireless_charging_ellipse_path_5_animation" />
+    <target
+        android:name="dot_06_pivot"
+        android:animation="@anim/wireless_charging__dot_06_pivot_animation" />
+    <target
+        android:name="ellipse_path_6"
+        android:animation="@anim/wireless_charging_ellipse_path_6_animation" />
+    <target
+        android:name="dot_07_pivot"
+        android:animation="@anim/wireless_charging__dot_07_pivot_animation" />
+    <target
+        android:name="ellipse_path_7"
+        android:animation="@anim/wireless_charging_ellipse_path_7_animation" />
+    <target
+        android:name="dot_08_pivot"
+        android:animation="@anim/wireless_charging__dot_08_pivot_animation" />
+    <target
+        android:name="ellipse_path_8"
+        android:animation="@anim/wireless_charging_ellipse_path_8_animation" />
+    <target
+        android:name="dot_09_pivot"
+        android:animation="@anim/wireless_charging__dot_09_pivot_animation" />
+    <target
+        android:name="ellipse_path_9"
+        android:animation="@anim/wireless_charging_ellipse_path_9_animation" />
+    <target
+        android:name="dot_10_pivot"
+        android:animation="@anim/wireless_charging__dot_10_pivot_animation" />
+    <target
+        android:name="ellipse_path_10"
+        android:animation="@anim/wireless_charging_ellipse_path_10_animation" />
+    <target
+        android:name="dot_11_pivot"
+        android:animation="@anim/wireless_charging__dot_11_pivot_animation" />
+    <target
+        android:name="ellipse_path_11"
+        android:animation="@anim/wireless_charging_ellipse_path_11_animation" />
+    <target
+        android:name="dot_12_pivot"
+        android:animation="@anim/wireless_charging__dot_12_pivot_animation" />
+    <target
+        android:name="ellipse_path_12"
+        android:animation="@anim/wireless_charging_ellipse_path_12_animation" />
+    <target
+        android:name="dot_13_pivot"
+        android:animation="@anim/wireless_charging__dot_13_pivot_animation" />
+    <target
+        android:name="ellipse_path_13"
+        android:animation="@anim/wireless_charging_ellipse_path_13_animation" />
+    <target
+        android:name="dot_14_pivot"
+        android:animation="@anim/wireless_charging__dot_14_pivot_animation" />
+    <target
+        android:name="ellipse_path_14"
+        android:animation="@anim/wireless_charging_ellipse_path_14_animation" />
+    <target
+        android:name="dot_15_pivot"
+        android:animation="@anim/wireless_charging__dot_15_pivot_animation" />
+    <target
+        android:name="ellipse_path_15"
+        android:animation="@anim/wireless_charging_ellipse_path_15_animation" />
+    <target
+        android:name="dot_16_pivot"
+        android:animation="@anim/wireless_charging__dot_16_pivot_animation" />
+    <target
+        android:name="ellipse_path_16"
+        android:animation="@anim/wireless_charging_ellipse_path_16_animation" />
+    <target
+        android:name="dot_17_pivot"
+        android:animation="@anim/wireless_charging__dot_17_pivot_animation" />
+    <target
+        android:name="ellipse_path_17"
+        android:animation="@anim/wireless_charging_ellipse_path_17_animation" />
+    <target
+        android:name="dot_18_pivot"
+        android:animation="@anim/wireless_charging__dot_18_pivot_animation" />
+    <target
+        android:name="ellipse_path_18"
+        android:animation="@anim/wireless_charging_ellipse_path_18_animation" />
+</animated-vector>
diff --git a/packages/SystemUI/res/interpolator/wireless_charging_animation_interpolator_0.xml b/packages/SystemUI/res/interpolator/wireless_charging_animation_interpolator_0.xml
new file mode 100644
index 0000000..3fe59ae
--- /dev/null
+++ b/packages/SystemUI/res/interpolator/wireless_charging_animation_interpolator_0.xml
@@ -0,0 +1,21 @@
+<?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.
+*/
+-->
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 c 0.001,0.0 0.2,1.0 1.0,1.0" />
diff --git a/packages/SystemUI/res/interpolator/wireless_charging_animation_interpolator_1.xml b/packages/SystemUI/res/interpolator/wireless_charging_animation_interpolator_1.xml
new file mode 100644
index 0000000..3fe59ae
--- /dev/null
+++ b/packages/SystemUI/res/interpolator/wireless_charging_animation_interpolator_1.xml
@@ -0,0 +1,21 @@
+<?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.
+*/
+-->
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 c 0.001,0.0 0.2,1.0 1.0,1.0" />
diff --git a/packages/SystemUI/res/layout/app_ops_info.xml b/packages/SystemUI/res/layout/app_ops_info.xml
index 676301e..82a0115 100644
--- a/packages/SystemUI/res/layout/app_ops_info.xml
+++ b/packages/SystemUI/res/layout/app_ops_info.xml
@@ -15,7 +15,7 @@
     limitations under the License.
 -->
 
-<com.android.systemui.statusbar.AppOpsInfo
+<com.android.systemui.statusbar.notification.row.AppOpsInfo
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -88,4 +88,4 @@
             android:layout_marginEnd="-8dp"
             style="@style/TextAppearance.NotificationInfo.Button"/>
     </LinearLayout>
-</com.android.systemui.statusbar.AppOpsInfo>
+</com.android.systemui.statusbar.notification.row.AppOpsInfo>
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
index 8379dbb..ee8d357 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
+++ b/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
@@ -28,7 +28,7 @@
     <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_inverse"
+        android:background="@drawable/car_button_ripple_background_light"
         android:gravity="center"/>
 
     <TextView android:id="@+id/user_name"
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
index 37e2b53..c9f5148 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -38,8 +38,8 @@
             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"
             app:verticallyCenterListContent="true"
-            app:dayNightStyle="always_light"
             app:showPagedListViewDivider="false"
             app:gutter="both"
             app:itemSpacing="@dimen/car_user_switcher_vertical_spacing_between_users"/>
diff --git a/packages/SystemUI/res/layout/car_qs_panel.xml b/packages/SystemUI/res/layout/car_qs_panel.xml
index c43afc9..e7413de 100644
--- a/packages/SystemUI/res/layout/car_qs_panel.xml
+++ b/packages/SystemUI/res/layout/car_qs_panel.xml
@@ -39,7 +39,7 @@
             android:id="@+id/user_grid"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            app:dayNightStyle="always_light"
+            android:theme="@style/Theme.Car.Light.List"
             app:showPagedListViewDivider="false"
             app:gutter="both"
             app:itemSpacing="@dimen/car_user_switcher_vertical_spacing_between_users"/>
diff --git a/packages/SystemUI/res/layout/contextual.xml b/packages/SystemUI/res/layout/contextual.xml
index 94591e9..c8f0a24 100644
--- a/packages/SystemUI/res/layout/contextual.xml
+++ b/packages/SystemUI/res/layout/contextual.xml
@@ -20,6 +20,7 @@
              android:layout_width="@dimen/navigation_key_width"
              android:layout_height="match_parent"
              android:importantForAccessibility="no"
+             android:focusable="false"
              android:clipChildren="false"
              android:clipToPadding="false"
              >
diff --git a/packages/SystemUI/res/layout/global_actions_wrapped.xml b/packages/SystemUI/res/layout/global_actions_wrapped.xml
index b715def..7f4e0d2 100644
--- a/packages/SystemUI/res/layout/global_actions_wrapped.xml
+++ b/packages/SystemUI/res/layout/global_actions_wrapped.xml
@@ -3,7 +3,9 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:layout_gravity="top|right"
     android:layout_marginBottom="0dp"
+    android:orientation="vertical"
     android:paddingTop="@dimen/global_actions_top_padding"
     android:clipToPadding="false"
     android:theme="@style/qs_theme"
@@ -17,7 +19,19 @@
         android:layout_gravity="top|right"
         android:gravity="center"
         android:orientation="vertical"
-        android:padding="12dp"
-        android:translationZ="9dp" />
+        android:padding="@dimen/global_actions_padding"
+        android:translationZ="@dimen/global_actions_translate" />
+
+    <!-- For separated button-->
+    <FrameLayout
+        android:id="@+id/separated_button"
+        android:layout_width="@dimen/global_actions_panel_width"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top|right"
+        android:layout_marginTop="6dp"
+        android:gravity="center"
+        android:orientation="vertical"
+        android:padding="@dimen/global_actions_padding"
+        android:translationZ="@dimen/global_actions_translate" />
 
 </com.android.systemui.HardwareUiLayout>
diff --git a/packages/SystemUI/res/layout/hybrid_notification.xml b/packages/SystemUI/res/layout/hybrid_notification.xml
index 23e8a15..e8d7751 100644
--- a/packages/SystemUI/res/layout/hybrid_notification.xml
+++ b/packages/SystemUI/res/layout/hybrid_notification.xml
@@ -15,7 +15,7 @@
   ~ limitations under the License
   -->
 
-<com.android.systemui.statusbar.notification.HybridNotificationView
+<com.android.systemui.statusbar.notification.row.HybridNotificationView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -35,4 +35,4 @@
         android:singleLine="true"
         style="?attr/hybridNotificationTextStyle"
     />
-</com.android.systemui.statusbar.notification.HybridNotificationView>
\ No newline at end of file
+</com.android.systemui.statusbar.notification.row.HybridNotificationView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index 8e491dc..5b9816d 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -18,7 +18,7 @@
 <!-- Extends RelativeLayout -->
 <com.android.systemui.statusbar.phone.KeyguardStatusBarView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:id="@+id/keyguard_header"
     android:layout_width="match_parent"
     android:layout_height="@dimen/status_bar_header_height_keyguard"
@@ -73,6 +73,8 @@
         android:textDirection="locale"
         android:textAppearance="?android:attr/textAppearanceSmall"
         android:textColor="?attr/wallpaperTextColorSecondary"
-        android:singleLine="true" />
+        android:singleLine="true"
+        systemui:showMissingSim="true"
+        systemui:showAirplaneMode="true" />
 
 </com.android.systemui.statusbar.phone.KeyguardStatusBarView>
diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml
index 9130fb4..8a3a0b1 100644
--- a/packages/SystemUI/res/layout/menu_ime.xml
+++ b/packages/SystemUI/res/layout/menu_ime.xml
@@ -17,13 +17,13 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:id="@+id/menu_container"
-    android:layout_width="match_parent"
+    android:layout_width="@dimen/navigation_key_width"
     android:layout_height="match_parent"
     android:importantForAccessibility="no"
     >
-    <!-- Use width & height=match_parent for parent FrameLayout and buttons because they are placed
-    inside a view that has a size controlled by weight. Ensure weight is large enough to support
-    icon size. -->
+    <!-- Use nav button width & height=match_parent for parent FrameLayout and buttons because they
+    are placed inside a view that has a size controlled by weight. Ensure weight is large enough to
+    support icon size. -->
 
     <com.android.systemui.statusbar.policy.KeyButtonView
         android:id="@+id/menu"
diff --git a/packages/SystemUI/res/layout/notification_children_container.xml b/packages/SystemUI/res/layout/notification_children_container.xml
index ac6a000..d3bb5d5 100644
--- a/packages/SystemUI/res/layout/notification_children_container.xml
+++ b/packages/SystemUI/res/layout/notification_children_container.xml
@@ -15,7 +15,7 @@
   ~ limitations under the License
   -->
 
-<com.android.systemui.statusbar.stack.NotificationChildrenContainer
+<com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content" />
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index 9d8ef83..dc94697 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -15,7 +15,7 @@
     limitations under the License.
 -->
 
-<com.android.systemui.statusbar.NotificationGuts
+<com.android.systemui.statusbar.notification.row.NotificationGuts
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 88d19f4..e1e812b 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -15,7 +15,7 @@
     limitations under the License.
 -->
 
-<com.android.systemui.statusbar.NotificationInfo
+<com.android.systemui.statusbar.notification.row.NotificationInfo
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/notification_guts"
     android:layout_width="match_parent"
@@ -161,30 +161,31 @@
                 style="@style/TextAppearance.NotificationInfo.Button"/>
         </LinearLayout>
     </LinearLayout>
-    <RelativeLayout
+    <com.android.systemui.statusbar.notification.row.NotificationUndoLayout
         android:id="@+id/confirmation"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/notification_guts_button_spacing"
-        android:layout_marginTop="@dimen/notification_guts_button_spacing"
-        android:layout_marginStart="@dimen/notification_guts_button_side_margin"
-        android:layout_marginEnd="@dimen/notification_guts_button_side_margin"
         android:visibility="gone"
         android:orientation="horizontal" >
         <TextView
             android:id="@+id/confirmation_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_centerVertical="true"
+            android:layout_gravity="start|center_vertical"
+            android:layout_marginStart="@*android:dimen/notification_content_margin_start"
+            android:layout_marginEnd="@*android:dimen/notification_content_margin_start"
             android:text="@string/notification_channel_disabled"
             style="@style/TextAppearance.NotificationInfo.Confirmation"/>
         <TextView
             android:id="@+id/undo"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_alignParentEnd="true"
-            android:layout_centerVertical="true"
+            android:layout_marginTop="@dimen/notification_guts_button_spacing"
+            android:layout_marginBottom="@dimen/notification_guts_button_spacing"
+            android:layout_marginStart="@dimen/notification_guts_button_side_margin"
+            android:layout_marginEnd="@dimen/notification_guts_button_side_margin"
+            android:layout_gravity="end|center_vertical"
             android:text="@string/inline_undo"
             style="@style/TextAppearance.NotificationInfo.Button"/>
-    </RelativeLayout>
-</com.android.systemui.statusbar.NotificationInfo>
+    </com.android.systemui.statusbar.notification.row.NotificationUndoLayout>
+</com.android.systemui.statusbar.notification.row.NotificationInfo>
diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml
index ea6ef4c..fae759a 100644
--- a/packages/SystemUI/res/layout/notification_snooze.xml
+++ b/packages/SystemUI/res/layout/notification_snooze.xml
@@ -15,7 +15,7 @@
     limitations under the License.
 -->
 
-<com.android.systemui.statusbar.NotificationSnooze
+<com.android.systemui.statusbar.notification.row.NotificationSnooze
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -36,7 +36,7 @@
             android:layout_alignParentStart="true"
             android:layout_centerVertical="true"
             android:paddingStart="@*android:dimen/notification_content_margin_start"
-            android:textColor="#DD000000"
+            android:textColor="@color/notification_primary_text_color"
             android:paddingEnd="4dp"/>
 
         <ImageView
@@ -72,4 +72,4 @@
         android:paddingBottom="8dp"
         android:orientation="vertical" />
 
-</com.android.systemui.statusbar.NotificationSnooze>
+</com.android.systemui.statusbar.notification.row.NotificationSnooze>
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
index 25b117f..f13f019 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
@@ -43,7 +43,7 @@
         android:layout_weight="0"
         android:paddingStart="@dimen/screen_pinning_request_frame_padding"
         android:paddingEnd="@dimen/screen_pinning_request_frame_padding"
-        android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+        android:theme="@style/ScreenPinningRequestTheme">
 
         <ImageView
             android:id="@+id/screen_pinning_back_bg_light"
@@ -86,7 +86,7 @@
         android:layout_weight="0"
         android:paddingStart="@dimen/screen_pinning_request_frame_padding"
         android:paddingEnd="@dimen/screen_pinning_request_frame_padding"
-        android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+        android:theme="@style/ScreenPinningRequestTheme">
 
         <ImageView
             android:id="@+id/screen_pinning_home_bg_light"
@@ -129,7 +129,7 @@
         android:layout_weight="0"
         android:paddingStart="@dimen/screen_pinning_request_frame_padding"
         android:paddingEnd="@dimen/screen_pinning_request_frame_padding"
-        android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+        android:theme="@style/ScreenPinningRequestTheme">
 
         <ImageView
             android:id="@+id/screen_pinning_recents_bg_light"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
index 367c13c..420b072 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
@@ -39,7 +39,7 @@
         android:layout_height="@dimen/screen_pinning_request_button_width"
         android:layout_width="@dimen/screen_pinning_request_button_height"
         android:layout_weight="0"
-        android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+        android:theme="@style/ScreenPinningRequestTheme">
 
         <ImageView
             android:id="@+id/screen_pinning_recents_bg_light"
@@ -79,7 +79,7 @@
         android:layout_height="@dimen/screen_pinning_request_button_width"
         android:layout_width="@dimen/screen_pinning_request_button_height"
         android:layout_weight="0"
-        android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+        android:theme="@style/ScreenPinningRequestTheme">
 
         <ImageView
             android:id="@+id/screen_pinning_home_bg_light"
@@ -120,7 +120,7 @@
         android:layout_height="@dimen/screen_pinning_request_button_width"
         android:layout_width="@dimen/screen_pinning_request_button_height"
         android:layout_weight="0"
-        android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+        android:theme="@style/ScreenPinningRequestTheme">
 
         <ImageView
             android:id="@+id/screen_pinning_back_bg_light"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
index bac02aa..f9308cd 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
@@ -39,7 +39,7 @@
         android:layout_height="@dimen/screen_pinning_request_button_width"
         android:layout_width="@dimen/screen_pinning_request_button_height"
         android:layout_weight="0"
-        android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+        android:theme="@style/ScreenPinningRequestTheme">
 
         <ImageView
             android:id="@+id/screen_pinning_back_bg_light"
@@ -82,7 +82,7 @@
         android:layout_height="@dimen/screen_pinning_request_button_width"
         android:layout_width="@dimen/screen_pinning_request_button_height"
         android:layout_weight="0"
-        android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent" >
+        android:theme="@style/ScreenPinningRequestTheme" >
 
         <ImageView
             android:id="@+id/screen_pinning_home_bg_light"
@@ -125,7 +125,7 @@
         android:layout_height="@dimen/screen_pinning_request_button_width"
         android:layout_width="@dimen/screen_pinning_request_button_height"
         android:layout_weight="0"
-        android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+        android:theme="@style/ScreenPinningRequestTheme">
 
         <ImageView
             android:id="@+id/screen_pinning_recents_bg_light"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index bef0830..2674f07c 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -47,7 +47,7 @@
             android:clipChildren="false"
             systemui:viewType="com.android.systemui.plugins.qs.QS" />
 
-        <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
+        <com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
             android:id="@+id/notification_stack_scroller"
             android:layout_marginTop="@dimen/notification_panel_margin_top"
             android:layout_width="@dimen/notification_panel_width"
diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
index 8b56b68..42d541e3 100644
--- a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
@@ -58,6 +58,7 @@
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
             android:layout_gravity="center_vertical"
+            android:paddingStart="2.5dp"
             android:paddingEnd="1dp"
             android:visibility="gone" />
         <Space
diff --git a/packages/SystemUI/res/layout/status_bar_no_notifications.xml b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
index 1e00e52..28b5a40 100644
--- a/packages/SystemUI/res/layout/status_bar_no_notifications.xml
+++ b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
@@ -26,9 +26,9 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:minHeight="64dp"
-            android:paddingTop="28dp"
+            android:paddingTop="12dp"
+            android:textAppearance="?android:attr/textAppearanceButton"
             android:gravity="top|center_horizontal"
             android:textColor="?attr/wallpaperTextColor"
-            android:textSize="16sp"
             android:text="@string/empty_shade_text"/>
 </com.android.systemui.statusbar.EmptyShadeView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 6c5cebc..056f16a 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -15,7 +15,7 @@
   -->
 
 <!-- Extends Framelayout -->
-<com.android.systemui.statusbar.FooterView
+<com.android.systemui.statusbar.notification.row.FooterView
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -26,7 +26,7 @@
         android:id="@+id/content"
         android:layout_width="match_parent"
         android:layout_height="wrap_content" >
-        <com.android.systemui.statusbar.FooterViewButton
+        <com.android.systemui.statusbar.notification.row.FooterViewButton
             style="@android:style/Widget.Material.Button.Borderless"
             android:id="@+id/manage_text"
             android:layout_width="wrap_content"
@@ -36,7 +36,7 @@
             android:text="@string/manage_notifications_text"
             android:textColor="?attr/wallpaperTextColor"
             android:textAllCaps="false"/>
-        <com.android.systemui.statusbar.FooterViewButton
+        <com.android.systemui.statusbar.notification.row.FooterViewButton
             style="@android:style/Widget.Material.Button.Borderless"
             android:id="@+id/dismiss_text"
             android:layout_width="wrap_content"
@@ -47,4 +47,4 @@
             android:text="@string/clear_all_notifications_text"
             android:textColor="?attr/wallpaperTextColor"/>
     </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-</com.android.systemui.statusbar.FooterView>
+</com.android.systemui.statusbar.notification.row.FooterView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index f15ca9e..84b9e3d 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -16,7 +16,7 @@
 -->
 
 <!-- extends FrameLayout -->
-<com.android.systemui.statusbar.ExpandableNotificationRow
+<com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -26,21 +26,23 @@
 
     <!-- Menu displayed behind notification added here programmatically -->
 
-    <com.android.systemui.statusbar.NotificationBackgroundView
+    <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
         android:id="@+id/backgroundNormal"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
-    <com.android.systemui.statusbar.NotificationBackgroundView
+    <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
         android:id="@+id/backgroundDimmed"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
-    <com.android.systemui.statusbar.NotificationContentView android:id="@+id/expanded"
+    <com.android.systemui.statusbar.notification.row.NotificationContentView
+        android:id="@+id/expanded"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
 
-    <com.android.systemui.statusbar.NotificationContentView android:id="@+id/expandedPublic"
+    <com.android.systemui.statusbar.notification.row.NotificationContentView
+        android:id="@+id/expandedPublic"
         android:layout_width="match_parent"
         android:layout_height="wrap_content" />
 
@@ -76,4 +78,4 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
-</com.android.systemui.statusbar.ExpandableNotificationRow>
+</com.android.systemui.statusbar.notification.row.ExpandableNotificationRow>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_shelf.xml b/packages/SystemUI/res/layout/status_bar_notification_shelf.xml
index 7bfbd3c..781c015 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_shelf.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_shelf.xml
@@ -23,14 +23,14 @@
     android:clickable="true"
     >
 
-    <com.android.systemui.statusbar.NotificationBackgroundView android:id="@+id/backgroundNormal"
+    <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+        android:id="@+id/backgroundNormal"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        />
-    <com.android.systemui.statusbar.NotificationBackgroundView android:id="@+id/backgroundDimmed"
+        android:layout_height="match_parent" />
+    <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+        android:id="@+id/backgroundDimmed"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        />
+        android:layout_height="match_parent" />
     <com.android.systemui.statusbar.phone.NotificationIconContainer
         android:id="@+id/content"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/tuner_activity.xml b/packages/SystemUI/res/layout/tuner_activity.xml
new file mode 100644
index 0000000..0b792ae
--- /dev/null
+++ b/packages/SystemUI/res/layout/tuner_activity.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.
+  -->
+
+<!-- The tuner content view -->
+<LinearLayout
+    android:id="@+id/content_parent"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <Toolbar
+        android:id="@+id/action_bar"
+        style="?android:attr/actionBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:theme="?android:attr/actionBarTheme"
+        android:navigationContentDescription="@*android:string/action_bar_up_description" />
+    <FrameLayout
+        android:id="@+id/content_frame"
+        android:layout_width="match_parent"
+        android:layout_height="fill_parent"
+        android:background="?android:attr/windowBackground" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/wireless_charging_layout.xml b/packages/SystemUI/res/layout/wireless_charging_layout.xml
index e848901..4610409 100644
--- a/packages/SystemUI/res/layout/wireless_charging_layout.xml
+++ b/packages/SystemUI/res/layout/wireless_charging_layout.xml
@@ -23,11 +23,12 @@
     android:layout_height="match_parent">
 
     <!-- Circle animation -->
-    <com.android.systemui.charging.WirelessChargingView
+    <ImageView
         android:id="@+id/wireless_charging_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:elevation="4dp"/>
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:src="@drawable/wireless_charging_animation"/>
 
     <!-- Text inside circle -->
     <LinearLayout
@@ -42,8 +43,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center"
-            android:textSize="24sp"
-            android:textColor="?attr/wallpaperTextColor"/>
+            android:textSize="24sp"/>
     </LinearLayout>
 
 </FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 09cf5c0..fb9b5df 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Versteek <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Dit sal verskyn die volgende keer wanneer jy dit in instellings aanskakel."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Versteek"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Jy gebruik tans jou werkprofiel"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Bel"</string>
     <string name="stream_system" msgid="7493299064422163147">"Stelsel"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Lui"</string>
diff --git a/packages/SystemUI/res/values-af/strings_car.xml b/packages/SystemUI/res/values-af/strings_car.xml
index 407ddcb..3f47062 100644
--- a/packages/SystemUI/res/values-af/strings_car.xml
+++ b/packages/SystemUI/res/values-af/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Gas"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Voeg gebruiker by"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Nuwe gebruiker"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Wanneer jy \'n nuwe gebruiker byvoeg, moet daardie persoon hul spasie opstel."</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index dfcd419..305082f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"የስራ መገለጫዎን እየተጠቀሙ ነው"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"ጥሪ"</string>
     <string name="stream_system" msgid="7493299064422163147">"ሥርዓት"</string>
     <string name="stream_ring" msgid="8213049469184048338">"ጥሪ"</string>
diff --git a/packages/SystemUI/res/values-am/strings_car.xml b/packages/SystemUI/res/values-am/strings_car.xml
index 635c9de..769cb5b 100644
--- a/packages/SystemUI/res/values-am/strings_car.xml
+++ b/packages/SystemUI/res/values-am/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"እንግዳ"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"ተጠቃሚ አክል"</string>
     <string name="car_new_user" msgid="8142927244990323906">"አዲስ ተጠቃሚ"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"አዲስ ተጠቃሚ ሲያክሉ ያ ሰው የራሳቸውን ቦታ ማቀናበር አለባቸው።"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 4160b3b..dcf02d5 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -534,7 +534,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"أنت تستخدم ملفك الشخصي للعمل"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"الاتصال"</string>
     <string name="stream_system" msgid="7493299064422163147">"النظام"</string>
     <string name="stream_ring" msgid="8213049469184048338">"الرنين"</string>
@@ -800,7 +799,7 @@
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"فتح إعدادات <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"تعديل ترتيب الإعدادات."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"الصفحة <xliff:g id="ID_1">%1$d</xliff:g> من <xliff:g id="ID_2">%2$d</xliff:g>"</string>
-    <string name="tuner_lock_screen" msgid="5755818559638850294">"شاشة التأمين"</string>
+    <string name="tuner_lock_screen" msgid="5755818559638850294">"شاشة القفل"</string>
     <string name="pip_phone_expand" msgid="5889780005575693909">"توسيع"</string>
     <string name="pip_phone_minimize" msgid="1079119422589131792">"تصغير"</string>
     <string name="pip_phone_close" msgid="8416647892889710330">"إغلاق"</string>
diff --git a/packages/SystemUI/res/values-ar/strings_car.xml b/packages/SystemUI/res/values-ar/strings_car.xml
index d6dfa12f..deadaae3 100644
--- a/packages/SystemUI/res/values-ar/strings_car.xml
+++ b/packages/SystemUI/res/values-ar/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"ضيف"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"إضافة المستخدم"</string>
     <string name="car_new_user" msgid="8142927244990323906">"مستخدم جديد"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"عند إضافة مستخدم جديد، عليه إعداد مساحته."</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index a310683..c20b5bc 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -95,8 +95,7 @@
     <string name="accessibility_unlock_button" msgid="128158454631118828">"আনলক কৰক"</string>
     <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"ফিংগাৰপ্ৰিণ্টৰ বাবে ৰৈ থকা হৈছে"</string>
     <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ নকৰাকৈ আনলক কৰক"</string>
-    <!-- no translation found for accessibility_scanning_face (769545173211758586) -->
-    <skip />
+    <string name="accessibility_scanning_face" msgid="769545173211758586">"চেহেৰা স্কেন কৰি থকা হৈছে"</string>
     <string name="accessibility_send_smart_reply" msgid="7766727839703044493">"পঠিয়াওক"</string>
     <string name="unlock_label" msgid="8779712358041029439">"আনলক কৰক"</string>
     <string name="phone_label" msgid="2320074140205331708">"ফ\'ন খোলক"</string>
@@ -523,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"আপুনি আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইল ব্যৱহাৰ কৰি আছে"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"কল"</string>
     <string name="stream_system" msgid="7493299064422163147">"ছিষ্টেম"</string>
     <string name="stream_ring" msgid="8213049469184048338">"ৰিং"</string>
@@ -854,6 +852,5 @@
     <string name="auto_saver_enabled_text" msgid="874711029884777579">"বেটাৰি চ্চাৰ্জৰ স্তৰ <xliff:g id="PERCENTAGE">%d</xliff:g>%%তকৈ কম হোৱাৰ লগে লগে বেটাৰি সঞ্চয়কাৰী স্বয়ংক্ৰিয়ভাৱে অন হ’ব।"</string>
     <string name="open_saver_setting_action" msgid="8314624730997322529">"ছেটিংবোৰ"</string>
     <string name="auto_saver_okay_action" msgid="2701221740227683650">"বুজি পালোঁ"</string>
-    <!-- no translation found for heap_dump_tile_name (9141031328971226374) -->
-    <skip />
+    <string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI হীপ ডাম্প কৰক"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings_car.xml b/packages/SystemUI/res/values-as/strings_car.xml
index e8cb6e3..ce19a03 100644
--- a/packages/SystemUI/res/values-as/strings_car.xml
+++ b/packages/SystemUI/res/values-as/strings_car.xml
@@ -20,10 +20,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"অতিথি"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"ব্যৱহাৰকাৰী যোগ কৰক"</string>
     <string name="car_new_user" msgid="8142927244990323906">"নতুন ব্যৱহাৰকাৰী"</string>
-    <!-- no translation found for user_add_user_message_setup (1791011504259527329) -->
-    <skip />
-    <!-- no translation found for user_add_user_message_update (3383320289232716179) -->
-    <skip />
+    <string name="user_add_user_message_setup" msgid="1791011504259527329">"আপুনি কোনো নতুন ব্য়ৱহাৰকাৰীক যোগ কৰিলে তেখেতে নিজৰ বাবে খালী ঠাই ছেট আপ কৰিব লাগে।"</string>
+    <string name="user_add_user_message_update" msgid="3383320289232716179">"সকলো ব্য়ৱহাৰকাৰীয়ে অইন ব্য়ৱহাৰকাৰীৰ বাবে এপসমূহ আপডেট কৰিব পাৰে।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index ea511c1..239dc20 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> gizlədilsin?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ayarlarda onu aktivləşdirəcəyiniz vaxta qədər o, yenidən görünəcək."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Gizlədin"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"İş profilinizi istifadə edirsiniz"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Zəng"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Zəng Edin"</string>
diff --git a/packages/SystemUI/res/values-az/strings_car.xml b/packages/SystemUI/res/values-az/strings_car.xml
index 79a946c..02ee177 100644
--- a/packages/SystemUI/res/values-az/strings_car.xml
+++ b/packages/SystemUI/res/values-az/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Qonaq"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"İstifadəçi əlavə edin"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Yeni İstifadəçi"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Yeni istifadəçi əlavə etdiyinizdə həmin şəxs öz yerini təyin etməlidir."</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 400302f..47eb266 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -525,7 +525,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite li da sakrijete <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ovo će se ponovo pojaviti kada ga sledeći put budete uključili u podešavanjima."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Sakrij"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Koristite profil za Work"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Poziv"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Zvono"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings_car.xml b/packages/SystemUI/res/values-b+sr+Latn/strings_car.xml
index 381d804..7a2032b 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings_car.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Gost"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Dodaj korisnika"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Novi korisnik"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Kada dodate novog korisnika, ta osoba treba da podesi svoj prostor."</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index ecf32b0..2db91e8 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -530,7 +530,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Вы выкарыстоўваеце свой працоўны профіль"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Выклік"</string>
     <string name="stream_system" msgid="7493299064422163147">"Сістэма"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Званок"</string>
diff --git a/packages/SystemUI/res/values-be/strings_car.xml b/packages/SystemUI/res/values-be/strings_car.xml
index a56dd84..357aeeb 100644
--- a/packages/SystemUI/res/values-be/strings_car.xml
+++ b/packages/SystemUI/res/values-be/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Госць"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Дадаць карыстальніка"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Новы карыстальнік"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Калі вы дадаяце новага карыстальніка, яму трэба наладзіць свой профіль."</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 0833a7a7..850977d 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Използвате служебния си потребителски профил"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Обаждане"</string>
     <string name="stream_system" msgid="7493299064422163147">"Система"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Позвъняване"</string>
diff --git a/packages/SystemUI/res/values-bg/strings_car.xml b/packages/SystemUI/res/values-bg/strings_car.xml
index fe159ea..c117ab4 100644
--- a/packages/SystemUI/res/values-bg/strings_car.xml
+++ b/packages/SystemUI/res/values-bg/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Гост"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Добавяне на потребител"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Нов потребител"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Когато добавите нов потребител, той трябва да настрои работното си пространство."</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 47ad590..a6dc54a 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -47,7 +47,7 @@
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"সেটিংস"</string>
     <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"ওয়াই-ফাই"</string>
     <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"অটো-রোটেট স্ক্রিন"</string>
-    <string name="status_bar_settings_mute_label" msgid="554682549917429396">"নিঃশব্দ করুন"</string>
+    <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>
@@ -472,8 +472,8 @@
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="4683248196789897964">"আপনার প্রতিষ্ঠান আপনার অফিস প্রোফাইলে একটি সার্টিফিকেট কর্তৃপক্ষ ইনস্টল করেছে। আপনার নিরাপদ নেটওয়ার্ক ট্রাফিকে নজর রাখা হতে পারে বা তাতে পরিবর্তন করা হতে পারে।"</string>
     <string name="monitoring_description_ca_certificate" msgid="7886985418413598352">"এই ডিভাইসে একটি সার্টিফিকেট কর্তৃপক্ষ ইনস্টল করা আছে। আপনার নিরাপদ নেটওয়ার্ক ট্রাফিকে নজর রাখা হতে পারে বা তাতে পরিবর্তন করা হতে পারে।"</string>
     <string name="monitoring_description_management_network_logging" msgid="7184005419733060736">"আপনার প্রশাসক নেটওয়ার্ক লগিং চালু করেছেন, যা আপনার ডিভাইসের ট্রাফিকের উপরে নজর রাখে।"</string>
-    <string name="monitoring_description_named_vpn" msgid="7403457334088909254">"আপনি <xliff:g id="VPN_APP">%1$s</xliff:g> এ সংযুক্ত রয়েছেন, যা আপনার ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক কার্যকলাপের উপর নজর রাখতে পারে।"</string>
-    <string name="monitoring_description_two_named_vpns" msgid="4198511413729213802">"আপনি <xliff:g id="VPN_APP_0">%1$s</xliff:g> এবং <xliff:g id="VPN_APP_1">%2$s</xliff:g> এর সাথে সংযুক্ত রয়েছেন, যেগুলি আপনার ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক কার্যকলাপের উপর নজর রাখতে পারে।"</string>
+    <string name="monitoring_description_named_vpn" msgid="7403457334088909254">"আপনি <xliff:g id="VPN_APP">%1$s</xliff:g> এ সংযুক্ত রয়েছেন, যা আপনার ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক অ্যাক্টিভিটির উপর নজর রাখতে পারে।"</string>
+    <string name="monitoring_description_two_named_vpns" msgid="4198511413729213802">"আপনি <xliff:g id="VPN_APP_0">%1$s</xliff:g> এবং <xliff:g id="VPN_APP_1">%2$s</xliff:g> এর সাথে সংযুক্ত রয়েছেন, যেগুলি আপনার ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক অ্যাক্টিভিটির উপর নজর রাখতে পারে।"</string>
     <string name="monitoring_description_managed_profile_named_vpn" msgid="1427905889862420559">"আপনার কর্মস্থলের প্রোফাইল <xliff:g id="VPN_APP">%1$s</xliff:g> এর সাথে সংযুক্ত রয়েছে, যেটি ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক কার্যকলাপে নজর রাখতে পারে।"</string>
     <string name="monitoring_description_personal_profile_named_vpn" msgid="3133980926929069283">"আপনার ব্যক্তিগত প্রোফাইল <xliff:g id="VPN_APP">%1$s</xliff:g> এর সাথে সংযুক্ত রয়েছে, যেটি ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক কার্যকলাপে নজর রাখতে পারে৷"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"আপনার ডিভাইসটি <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> এর দ্বারা পরিচালিত৷"</string>
@@ -481,20 +481,20 @@
     <string name="monitoring_description_do_body" msgid="3639594537660975895">"আপনার প্রশাসক আপনার ডিভাইসের লোকেশন তথ্য সহ এই ডিভাইসের সেটিংস, কর্পোরেট অ্যাক্সেস, অ্যাপ্স, ডেটা নিরীক্ষণ ও পরিচালনা করতে পারেন।"</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"আরও জানুন"</string>
-    <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"আপনি <xliff:g id="VPN_APP">%1$s</xliff:g> এ সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ এবং ওয়েবসাইটগুলি সহ আপনার নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করবে৷"</string>
+    <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"আপনি <xliff:g id="VPN_APP">%1$s</xliff:g> এ সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ এবং ওয়েবসাইটগুলি সহ আপনার নেটওয়ার্ক অ্যাক্টিভিটি নিরীক্ষণ করবে৷"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="6434859242636063861">"VPN সেটিংস খুলুন"</string>
     <string name="monitoring_description_ca_cert_settings_separator" msgid="4987350385906393626">" "</string>
     <string name="monitoring_description_ca_cert_settings" msgid="5489969458872997092">"বিশ্বস্ত শংসাপত্রগুলি খুলুন"</string>
     <string name="monitoring_description_network_logging" msgid="7223505523384076027">"আপনার প্রশাসক নেটওয়ার্ক লগিং চালু করেছেন, যা আপনার ডিভাইসের ট্রাফিক নিরীক্ষণ করে।\n\nআরও তথ্যের জন্য আপনার প্রশাসকের সাথে যোগাযোগ করুন।"</string>
-    <string name="monitoring_description_vpn" msgid="4445150119515393526">"আপনি VPN সংযোগ সেট আপ করার জন্য একটি অ্যাপ্লিকেশানকে অনুমতি দিন৷\n\nএই অ্যাপ্লিকেশানটি ইমেল, অ্যাপ্লিকেশান ও ওয়েবসাইটগুলি সহ আপনার ডিভাইস এবং নেটওয়ার্কের কার্যকলাপ নিরীক্ষণ করতে পারে।"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"আপনার কর্মস্থলের প্রোফাইলটি <xliff:g id="ORGANIZATION">%1$s</xliff:g> দ্বারা পরিচালিত হয়।\n\nআপনার প্রশাসক আপনার ইমেল, অ্যাপ্স ও ওয়েবসাইট সহ কর্মস্থলের নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করতে পারেন।\n\nআরও তথ্যের জন্য আপনার প্রশাসকের সঙ্গে যোগাযোগ করুন।\n\nএছাড়া আপনি একটি VPN এর সাথেও সংযুক্ত যা আপনার নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করতে পারে।"</string>
+    <string name="monitoring_description_vpn" msgid="4445150119515393526">"আপনি VPN সংযোগ সেট আপ করার জন্য একটি অ্যাপ্লিকেশানকে অনুমতি দিন৷\n\nএই অ্যাপ্লিকেশানটি ইমেল, অ্যাপ্লিকেশান ও ওয়েবসাইটগুলি সহ আপনার ডিভাইস এবং নেটওয়ার্কের অ্যাক্টিভিটি নিরীক্ষণ করতে পারে।"</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"আপনার কর্মস্থলের প্রোফাইলটি <xliff:g id="ORGANIZATION">%1$s</xliff:g> দ্বারা পরিচালিত হয়।\n\nআপনার প্রশাসক আপনার ইমেল, অ্যাপ্স ও ওয়েবসাইট সহ কর্মস্থলের নেটওয়ার্ক অ্যাক্টিভিটি নিরীক্ষণ করতে পারেন।\n\nআরও তথ্যের জন্য আপনার প্রশাসকের সঙ্গে যোগাযোগ করুন।\n\nএছাড়া আপনি একটি VPN এর সাথেও সংযুক্ত যা আপনার নেটওয়ার্ক অ্যাক্টিভিটি নিরীক্ষণ করতে পারে।"</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="1828472472674709532">"আপনি <xliff:g id="APPLICATION">%1$s</xliff:g> এর সাথে সংযুক্ত রয়েছেন, যেটি ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক কার্যকলাপে নজর রাখতে পারে৷"</string>
-    <string name="monitoring_description_app_personal" msgid="484599052118316268">"আপনি <xliff:g id="APPLICATION">%1$s</xliff:g> -এ সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ্লিকেশান এবং ওয়েবসাইটগুলি সমেত আপনার ব্যক্তিগত নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করতে পারে৷"</string>
-    <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"আপনি <xliff:g id="APPLICATION">%1$s</xliff:g> এর সাথে সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ এবং ওয়েবসাইটগুলি সহ আপনার ব্যক্তিগত নেটওয়ার্কের কার্যকলাপ নিরীক্ষণ করবে৷"</string>
-    <string name="monitoring_description_app_work" msgid="4612997849787922906">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> আপনার কর্মস্থলের প্রোফাইল পরিচালনা করে। প্রোফাইলটি <xliff:g id="APPLICATION">%2$s</xliff:g> এর সাথে সংযুক্ত, যেটি ইমেল, অ্যাপ, ও ওয়েবসাইট সহ আপনার কর্মস্থলের নেটওয়ার্ক কার্যকলাপের উপরে নজর রাখতে পারে।\n\nআরও তথ্যের জন্য প্রশাসকের সাথে যোগাযোগ করুন।"</string>
-    <string name="monitoring_description_app_personal_work" msgid="5664165460056859391">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> আপনার কর্মস্থলের প্রোফাইল পরিচালনা করে। প্রোফাইলটি <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> এর সাথে সংযুক্ত, যেটি ইমেল অ্যাপ, ও ওয়েবসাইট সহ আপনার কর্মস্থলের নেটওয়ার্ক কার্যকলাপের উপরে নজর রাখতে পারে।\n\n এ ছাড়াও আপনি <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> এর সাথে সংযুক্ত, যেটি আপনার ব্যক্তিগত নেটওয়ার্কে নজর রাখে।"</string>
+    <string name="monitoring_description_app_personal" msgid="484599052118316268">"আপনি <xliff:g id="APPLICATION">%1$s</xliff:g> -এ সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ্লিকেশান এবং ওয়েবসাইটগুলি সমেত আপনার ব্যক্তিগত নেটওয়ার্ক অ্যাক্টিভিটি নিরীক্ষণ করতে পারে৷"</string>
+    <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"আপনি <xliff:g id="APPLICATION">%1$s</xliff:g> এর সাথে সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ এবং ওয়েবসাইটগুলি সহ আপনার ব্যক্তিগত নেটওয়ার্কের অ্যাক্টিভিটি নিরীক্ষণ করবে৷"</string>
+    <string name="monitoring_description_app_work" msgid="4612997849787922906">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> আপনার কর্মস্থলের প্রোফাইল পরিচালনা করে। প্রোফাইলটি <xliff:g id="APPLICATION">%2$s</xliff:g> এর সাথে সংযুক্ত, যেটি ইমেল, অ্যাপ, ও ওয়েবসাইট সহ আপনার কর্মস্থলের নেটওয়ার্ক অ্যাক্টিভিটির উপরে নজর রাখতে পারে।\n\nআরও তথ্যের জন্য প্রশাসকের সাথে যোগাযোগ করুন।"</string>
+    <string name="monitoring_description_app_personal_work" msgid="5664165460056859391">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> আপনার কর্মস্থলের প্রোফাইল পরিচালনা করে। প্রোফাইলটি <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> এর সাথে সংযুক্ত, যেটি ইমেল অ্যাপ, ও ওয়েবসাইট সহ আপনার কর্মস্থলের নেটওয়ার্ক অ্যাক্টিভিটির উপরে নজর রাখতে পারে।\n\n এ ছাড়াও আপনি <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> এর সাথে সংযুক্ত, যেটি আপনার ব্যক্তিগত নেটওয়ার্কে নজর রাখে।"</string>
     <string name="keyguard_indication_trust_granted" msgid="4985003749105182372">"<xliff:g id="USER_NAME">%1$s</xliff:g> এর জন্য আনলক করা হয়েছে"</string>
     <string name="keyguard_indication_trust_managed" msgid="8319646760022357585">"<xliff:g id="TRUST_AGENT">%1$s</xliff:g> চালু আছে"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"আপনি নিজে আনলক না করা পর্যন্ত ডিভাইসটি লক হয়ে থাকবে"</string>
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"আপনি আপনার কাজের প্রোফাইল ব্যবহার করছেন"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"কল"</string>
     <string name="stream_system" msgid="7493299064422163147">"সিস্টেম"</string>
     <string name="stream_ring" msgid="8213049469184048338">"রিং"</string>
@@ -539,10 +538,10 @@
     <string name="qs_status_phone_vibrate" msgid="204362991135761679">"ফোন ভাইব্রেশন মোডে আছে"</string>
     <string name="qs_status_phone_muted" msgid="5437668875879171548">"ফোন মিউট করা আছে"</string>
     <string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s। সশব্দ করতে আলতো চাপুন।"</string>
-    <string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s। কম্পন এ সেট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে নিঃশব্দ করা হতে পারে।"</string>
-    <string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s। নিঃশব্দ করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে নিঃশব্দ করা হতে পারে।"</string>
+    <string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s। কম্পন এ সেট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে মিউট করা হতে পারে।"</string>
+    <string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s। মিউট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে মিউট করা হতে পারে।"</string>
     <string name="volume_stream_content_description_vibrate_a11y" msgid="6427727603978431301">"%1$s। ভাইব্রেট করতে ট্যাপ করুন।"</string>
-    <string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s। নিঃশব্দ করতে ট্যাপ করুন।"</string>
+    <string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s। মিউট করতে ট্যাপ করুন।"</string>
     <string name="volume_ringer_hint_mute" msgid="9199811307292269601">"মিউট করুন"</string>
     <string name="volume_ringer_hint_unmute" msgid="6602880133293060368">"আনমিউট করুন"</string>
     <string name="volume_ringer_hint_vibrate" msgid="4036802135666515202">"ভাইব্রেট করান"</string>
diff --git a/packages/SystemUI/res/values-bn/strings_car.xml b/packages/SystemUI/res/values-bn/strings_car.xml
index 121e517..2c6857d 100644
--- a/packages/SystemUI/res/values-bn/strings_car.xml
+++ b/packages/SystemUI/res/values-bn/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"অতিথি"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"ব্যবহারকারীকে যুক্ত করুন"</string>
     <string name="car_new_user" msgid="8142927244990323906">"নতুন ব্যবহারকারী"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"আপনি কোনও নতুন ব্যবহারকারীকে যোগ করলে তাকে তার স্পেস সেট-আপ করে নিতে হবে।"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 65d9071..483b378 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -310,7 +310,7 @@
     <string name="quick_settings_wifi_label" msgid="9135344704899546041">"WiFi"</string>
     <string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nije povezano"</string>
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nema mreže"</string>
-    <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"WiFi isključen"</string>
+    <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"WiFi je isključen"</string>
     <string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"WiFi uključen"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Nema dostupnih WiFi mreža"</string>
     <string name="quick_settings_wifi_secondary_label_transient" msgid="7748206246119760554">"Uključivanje…"</string>
@@ -328,7 +328,7 @@
     <string name="quick_settings_connected" msgid="1722253542984847487">"Povezano"</string>
     <string name="quick_settings_connected_battery_level" msgid="4136051440381328892">"Povezano, baterija <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="quick_settings_connecting" msgid="47623027419264404">"Povezivanje..."</string>
-    <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Dijeljenje veze"</string>
+    <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Povezivanje putem mobitela"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Pristupna tačka"</string>
     <string name="quick_settings_hotspot_secondary_label_transient" msgid="8010579363691405477">"Uključivanje…"</string>
     <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="5672131949987422420">"Ušteda podataka uklj."</string>
@@ -525,7 +525,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite li sakriti <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Pojavit će se sljedeći put kada opciju uključite u postavkama."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Sakrij"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Koristite svoj profil za posao"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Poziv"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Zvono"</string>
diff --git a/packages/SystemUI/res/values-bs/strings_car.xml b/packages/SystemUI/res/values-bs/strings_car.xml
index 6591880..4d334c5 100644
--- a/packages/SystemUI/res/values-bs/strings_car.xml
+++ b/packages/SystemUI/res/values-bs/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Gost"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Dodaj korisnika"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Novi korisnik"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Kada dodate novog korisnika, ta osoba treba postaviti svoj prostor."</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index ad7e546..9c82b70 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -62,10 +62,10 @@
     <string name="label_view" msgid="6304565553218192990">"Mostra"</string>
     <string name="always_use_device" msgid="4015357883336738417">"Obre sempre <xliff:g id="APPLICATION">%1$s</xliff:g> quan es connecti <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
     <string name="always_use_accessory" msgid="3257892669444535154">"Obre sempre <xliff:g id="APPLICATION">%1$s</xliff:g> quan es connecti <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
-    <string name="usb_debugging_title" msgid="4513918393387141949">"Vols permetre la depuració USB?"</string>
+    <string name="usb_debugging_title" msgid="4513918393387141949">"Vols permetre la depuració per USB?"</string>
     <string name="usb_debugging_message" msgid="2220143855912376496">"L\'empremta digital de la clau de l\'RSA de l\'equip és:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
     <string name="usb_debugging_always" msgid="303335496705863070">"Dona sempre permís des d\'aquest equip"</string>
-    <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"No es permet la depuració USB"</string>
+    <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"No es permet la depuració per USB"</string>
     <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'usuari que té iniciada la sessió al dispositiu en aquest moment no pot activar la depuració per USB. Per utilitzar aquesta funció, cal canviar a l\'usuari principal."</string>
     <string name="compat_mode_on" msgid="6623839244840638213">"Zoom per omplir pantalla"</string>
     <string name="compat_mode_off" msgid="4434467572461327898">"Estira per omplir pant."</string>
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vols amagar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Tornarà a mostrar-se la propera vegada que l\'activis a la configuració."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Amaga"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estàs utilitzant el perfil professional"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Trucada"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
     <string name="stream_ring" msgid="8213049469184048338">"To de trucada"</string>
diff --git a/packages/SystemUI/res/values-ca/strings_car.xml b/packages/SystemUI/res/values-ca/strings_car.xml
index 47d59dd..8cd7d38 100644
--- a/packages/SystemUI/res/values-ca/strings_car.xml
+++ b/packages/SystemUI/res/values-ca/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Convidat"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Afegeix un usuari"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Usuari nou"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Quan s\'afegeix un usuari nou, aquest usuari ha de configurar el seu espai."</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index bc84f59..5b99dd4 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -64,11 +64,11 @@
     <string name="label_view" msgid="6304565553218192990">"Zobrazit"</string>
     <string name="always_use_device" msgid="4015357883336738417">"Když bude připojeno zařízení <xliff:g id="USB_DEVICE">%2$s</xliff:g>, vždy otevřít <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
     <string name="always_use_accessory" msgid="3257892669444535154">"Když bude připojeno zařízení <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>, vždy otevřít <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
-    <string name="usb_debugging_title" msgid="4513918393387141949">"Povolit ladění USB?"</string>
+    <string name="usb_debugging_title" msgid="4513918393387141949">"Povolit ladění přes USB?"</string>
     <string name="usb_debugging_message" msgid="2220143855912376496">"Digitální otisk RSA počítače je:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
     <string name="usb_debugging_always" msgid="303335496705863070">"Vždy povolit z tohoto počítače"</string>
-    <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Ladění USB není povoleno"</string>
-    <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Uživatel aktuálně přihlášený k tomuto zařízení nemůže zapnout ladění USB. Chcete-li tuto funkci použít, přepněte na primárního uživatele."</string>
+    <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Ladění přes USB není povoleno"</string>
+    <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Uživatel aktuálně přihlášený k tomuto zařízení nemůže zapnout ladění přes USB. Chcete-li tuto funkci použít, přepněte na primárního uživatele."</string>
     <string name="compat_mode_on" msgid="6623839244840638213">"Přiblížit na celou obrazovku"</string>
     <string name="compat_mode_off" msgid="4434467572461327898">"Na celou obrazovku"</string>
     <string name="global_action_screenshot" msgid="8329831278085426283">"Snímek obrazovky"</string>
@@ -530,7 +530,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Skrýt <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Tato položka se znovu zobrazí, až ji v nastavení znovu zapnete."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skrýt"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Používáte pracovní profil"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Hovor"</string>
     <string name="stream_system" msgid="7493299064422163147">"Systém"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Vyzvánění"</string>
diff --git a/packages/SystemUI/res/values-cs/strings_car.xml b/packages/SystemUI/res/values-cs/strings_car.xml
index 7f29118..7356fd9 100644
--- a/packages/SystemUI/res/values-cs/strings_car.xml
+++ b/packages/SystemUI/res/values-cs/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Host"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Přidat uživatele"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Nový uživatel"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Každý nově přidaný uživatel si musí nastavit vlastní prostor."</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index c7e856a..8e4f91b 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -43,7 +43,7 @@
     <string name="battery_low_why" msgid="4553600287639198111">"Indstillinger"</string>
     <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"Vil du aktivere Batterisparefunktion?"</string>
     <string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"Aktivér"</string>
-    <string name="battery_saver_start_action" msgid="8187820911065797519">"Aktivér Batterisparefunktion"</string>
+    <string name="battery_saver_start_action" msgid="8187820911065797519">"Aktivér batterisparefunktion"</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Indstillinger"</string>
     <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
     <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"Automatisk skærmrotation"</string>
@@ -431,7 +431,7 @@
     <string name="user_remove_user_remove" msgid="7479275741742178297">"Fjern"</string>
     <string name="battery_saver_notification_title" msgid="8614079794522291840">"Batterisparefunktion er aktiveret"</string>
     <string name="battery_saver_notification_text" msgid="820318788126672692">"Reducerer ydeevne og baggrundsdata"</string>
-    <string name="battery_saver_notification_action_text" msgid="132118784269455533">"Deaktiver Batterisparefunktion"</string>
+    <string name="battery_saver_notification_action_text" msgid="132118784269455533">"Deaktiver batterisparefunktion"</string>
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vil begynde at optage alt, hvad der vises på din skærm."</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"Vis ikke igen"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"Ryd alt"</string>
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vil du skjule <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Den vises igen, næste gang du aktiverer den i indstillingerne."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skjul"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du bruger din arbejdsprofil"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Ring op"</string>
     <string name="stream_system" msgid="7493299064422163147">"System"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
@@ -681,7 +680,7 @@
     <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Applikationer"</string>
     <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assistance"</string>
     <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
-    <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktpersoner"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakter"</string>
     <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
     <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"Sms"</string>
     <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musik"</string>
diff --git a/packages/SystemUI/res/values-da/strings_car.xml b/packages/SystemUI/res/values-da/strings_car.xml
index 1e7bbbf..4112ad9 100644
--- a/packages/SystemUI/res/values-da/strings_car.xml
+++ b/packages/SystemUI/res/values-da/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Gæst"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Tilføj bruger"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Ny bruger"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Når du tilføjer en ny bruger, skal vedkommende konfigurere sit område."</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 42d98e4..d3d9458 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -526,7 +526,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ausblenden?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Sie wird wieder eingeblendet, wenn du sie in den Einstellungen erneut aktivierst."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ausblenden"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du verwendest dein Arbeitsprofil."</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Anruf"</string>
     <string name="stream_system" msgid="7493299064422163147">"System"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Klingelton"</string>
diff --git a/packages/SystemUI/res/values-de/strings_car.xml b/packages/SystemUI/res/values-de/strings_car.xml
index bf03f5d..7dcf6be 100644
--- a/packages/SystemUI/res/values-de/strings_car.xml
+++ b/packages/SystemUI/res/values-de/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Gast"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Nutzer hinzufügen"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Neuer Nutzer"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Wenn du einen neuen Nutzer hinzufügst, muss dieser seinen Bereich einrichten."</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 67afd93..45e1b1b 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Χρησιμοποιείτε το προφίλ εργασίας σας"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Κλήση"</string>
     <string name="stream_system" msgid="7493299064422163147">"Σύστημα"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Κλήση"</string>
diff --git a/packages/SystemUI/res/values-el/strings_car.xml b/packages/SystemUI/res/values-el/strings_car.xml
index 37e4e92b..b4157e7 100644
--- a/packages/SystemUI/res/values-el/strings_car.xml
+++ b/packages/SystemUI/res/values-el/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Επισκέπτης"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Προσθήκη χρήστη"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Νέος χρήστης"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Όταν προσθέτετε έναν νέο χρήστη, αυτός ο χρήστης θα πρέπει να ρυθμίσει τον χώρο του."</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 01cd5b0..561601d 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Call"</string>
     <string name="stream_system" msgid="7493299064422163147">"System"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings_car.xml b/packages/SystemUI/res/values-en-rAU/strings_car.xml
index e058c5b..4bb06c7 100644
--- a/packages/SystemUI/res/values-en-rAU/strings_car.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Guest"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Add User"</string>
     <string name="car_new_user" msgid="8142927244990323906">"New User"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"When you add a new user, that person needs to set up their space."</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index d8c89d3..efa8ad2 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Call"</string>
     <string name="stream_system" msgid="7493299064422163147">"System"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings_car.xml b/packages/SystemUI/res/values-en-rCA/strings_car.xml
index e058c5b..4bb06c7 100644
--- a/packages/SystemUI/res/values-en-rCA/strings_car.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Guest"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Add User"</string>
     <string name="car_new_user" msgid="8142927244990323906">"New User"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"When you add a new user, that person needs to set up their space."</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 01cd5b0..561601d 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Call"</string>
     <string name="stream_system" msgid="7493299064422163147">"System"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings_car.xml b/packages/SystemUI/res/values-en-rGB/strings_car.xml
index e058c5b..4bb06c7 100644
--- a/packages/SystemUI/res/values-en-rGB/strings_car.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Guest"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Add User"</string>
     <string name="car_new_user" msgid="8142927244990323906">"New User"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"When you add a new user, that person needs to set up their space."</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 01cd5b0..561601d 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Call"</string>
     <string name="stream_system" msgid="7493299064422163147">"System"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings_car.xml b/packages/SystemUI/res/values-en-rIN/strings_car.xml
index e058c5b..4bb06c7 100644
--- a/packages/SystemUI/res/values-en-rIN/strings_car.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Guest"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Add User"</string>
     <string name="car_new_user" msgid="8142927244990323906">"New User"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"When you add a new user, that person needs to set up their space."</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 2e6999a..881ee90 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‏‎Hide ‎‏‎‎‏‏‎<xliff:g id="TILE_LABEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‏‏‏‏‏‎‎It will reappear the next time you turn it on in settings.‎‏‎‎‏‎"</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎Hide‎‏‎‎‏‎"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎‎You\'re using your work profile‎‏‎‎‏‎"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‎‎Call‎‏‎‎‏‎"</string>
     <string name="stream_system" msgid="7493299064422163147">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎System‎‏‎‎‏‎"</string>
     <string name="stream_ring" msgid="8213049469184048338">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎Ring‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings_car.xml b/packages/SystemUI/res/values-en-rXC/strings_car.xml
index fe91bea..deb7021 100644
--- a/packages/SystemUI/res/values-en-rXC/strings_car.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings_car.xml
@@ -20,6 +20,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‏‎‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‎Guest‎‏‎‎‏‎"</string>
+    <string name="start_guest_session" msgid="7055742120180595689">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎Guest‎‏‎‎‏‎"</string>
     <string name="car_add_user" msgid="5245196248349230898">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‏‏‎‎‏‎‎Add User‎‏‎‎‏‎"</string>
     <string name="car_new_user" msgid="8142927244990323906">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎New User‎‏‎‎‏‎"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‎‎‏‎When you add a new user, that person needs to set up their space.‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 1f6dd16..6681dbb 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -524,7 +524,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"¿Ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Volverá a aparecer la próxima vez que se active en la configuración."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estás usando tu perfil de trabajo"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Llamada"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Hacer sonar"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings_car.xml b/packages/SystemUI/res/values-es-rUS/strings_car.xml
index c81573eb8..db6c93e 100644
--- a/packages/SystemUI/res/values-es-rUS/strings_car.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Invitado"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Agregar usuario"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Nuevo usuario"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Cuando agregues un usuario nuevo, esa persona deberá configurar su espacio."</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 7ca5991..27daa5c 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -524,7 +524,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"¿Ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Volverá a aparecer la próxima vez que actives esta opción en Ajustes."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estás usando tu perfil de trabajo"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Llamada"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Tono"</string>
diff --git a/packages/SystemUI/res/values-es/strings_car.xml b/packages/SystemUI/res/values-es/strings_car.xml
index 7da21a8..503788c 100644
--- a/packages/SystemUI/res/values-es/strings_car.xml
+++ b/packages/SystemUI/res/values-es/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Invitado"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Añadir usuario"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Nuevo usuario"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Al añadir un nuevo usuario, este debe configurar su espacio."</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 3ef21be..c309294 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -524,7 +524,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Kas peita <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"See kuvatakse uuesti järgmisel korral, kui selle seadetes sisse lülitate."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Peida"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Kasutate oma tööprofiili"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Kõne"</string>
     <string name="stream_system" msgid="7493299064422163147">"Süsteem"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Helin"</string>
diff --git a/packages/SystemUI/res/values-et/strings_car.xml b/packages/SystemUI/res/values-et/strings_car.xml
index 9ed97aa..ac010fe 100644
--- a/packages/SystemUI/res/values-et/strings_car.xml
+++ b/packages/SystemUI/res/values-et/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Külaline"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Kasutaja lisamine"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Uus kasutaja"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Kui lisate uue kasutaja, siis peab ta seadistama oma ruumi."</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 432bfc4..d2dbc79 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -524,7 +524,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ezkutatu nahi duzu?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ezarpenetan aktibatzen duzun hurrengoan agertuko da berriro."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ezkutatu"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Work profila erabiltzen ari zara"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Deia"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Jo tonua"</string>
@@ -564,8 +563,8 @@
     <string name="status_bar" msgid="4877645476959324760">"Egoera-barra"</string>
     <string name="overview" msgid="4018602013895926956">"Ikuspegi orokorra"</string>
     <string name="demo_mode" msgid="2532177350215638026">"Sistemaren erabiltzaile-interfazearen demo modua"</string>
-    <string name="enable_demo_mode" msgid="4844205668718636518">"Gaitu proba modua"</string>
-    <string name="show_demo_mode" msgid="2018336697782464029">"Erakutsi proba modua"</string>
+    <string name="enable_demo_mode" msgid="4844205668718636518">"Gaitu demo modua"</string>
+    <string name="show_demo_mode" msgid="2018336697782464029">"Erakutsi demo modua"</string>
     <string name="status_bar_ethernet" msgid="5044290963549500128">"Ethernet"</string>
     <string name="status_bar_alarm" msgid="8536256753575881818">"Alarma"</string>
     <string name="status_bar_work" msgid="6022553324802866373">"Work profila"</string>
diff --git a/packages/SystemUI/res/values-eu/strings_car.xml b/packages/SystemUI/res/values-eu/strings_car.xml
index 99c66d2..6375ce0 100644
--- a/packages/SystemUI/res/values-eu/strings_car.xml
+++ b/packages/SystemUI/res/values-eu/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Gonbidatua"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Gehitu erabiltzaile bat"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Erabiltzaile berria"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Erabiltzaile bat gehitzen duzunean, bere eremua konfiguratu beharko du."</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 79dc007..db393c0 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -472,28 +472,28 @@
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="4683248196789897964">"سازمان شما مرجع گواهینامه‌ای در نمایه کاری شما نصب کرده است. ممکن است ترافیک امن شبکه شما پایش یا تغییر داده شود."</string>
     <string name="monitoring_description_ca_certificate" msgid="7886985418413598352">"مرجع گواهینامه‌ای در این دستگاه نصب شده است. ممکن است ترافیک امن شبکه شما پایش یا تغییر داده شود."</string>
     <string name="monitoring_description_management_network_logging" msgid="7184005419733060736">"سرپرست سیستم شما گزارش‌گیری از شبکه را (که ترافیک دستگاه شما را پایش می‌کند) روشن کرده است."</string>
-    <string name="monitoring_description_named_vpn" msgid="7403457334088909254">"به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل شده‌اید، که می‌تواند فعالیت شبکه شما را (ازجمله رایانامه‌‌ها، برنامه‌‌ها و وب‌سایت‌ها) پایش کند."</string>
-    <string name="monitoring_description_two_named_vpns" msgid="4198511413729213802">"به <xliff:g id="VPN_APP_0">%1$s</xliff:g> و <xliff:g id="VPN_APP_1">%2$s</xliff:g> متصل شده‌اید، که می‌توانند فعالیت شما را در شبکه (ازجمله رایانامه‌‌ها، برنامه‌‌ها و وب‌سایت‌ها) پایش کنند."</string>
-    <string name="monitoring_description_managed_profile_named_vpn" msgid="1427905889862420559">"نمایه کاری شما به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل است، که می‌تواند فعالیت شما در شبکه (ازجمله رایانامه‌ها، برنامه‌ها و وب‌سایت‌ها) را پایش کند."</string>
-    <string name="monitoring_description_personal_profile_named_vpn" msgid="3133980926929069283">"نمایه شخصی شما به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل شده‌ است، که می‌تواند فعالیت شما در شبکه (ازجمله رایانامه‌‌ها، برنامه‌‌ها و وب‌سایت‌ها) را پایش کند."</string>
+    <string name="monitoring_description_named_vpn" msgid="7403457334088909254">"به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل شده‌اید، که می‌تواند فعالیت شبکه شما را (ازجمله ایمیل‌ها، برنامه‌‌ها و وب‌سایت‌ها) پایش کند."</string>
+    <string name="monitoring_description_two_named_vpns" msgid="4198511413729213802">"به <xliff:g id="VPN_APP_0">%1$s</xliff:g> و <xliff:g id="VPN_APP_1">%2$s</xliff:g> متصل شده‌اید، که می‌توانند فعالیت شما را در شبکه (ازجمله ایمیل‌ها، برنامه‌‌ها و وب‌سایت‌ها) پایش کنند."</string>
+    <string name="monitoring_description_managed_profile_named_vpn" msgid="1427905889862420559">"نمایه کاری شما به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل است، که می‌تواند فعالیت شما در شبکه (ازجمله ایمیل‌ها، برنامه‌ها و وب‌سایت‌ها) را پایش کند."</string>
+    <string name="monitoring_description_personal_profile_named_vpn" msgid="3133980926929069283">"نمایه شخصی شما به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل شده‌ است، که می‌تواند فعالیت شما در شبکه (ازجمله ایمیل‌ها، برنامه‌‌ها و وب‌سایت‌ها) را پایش کند."</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> دستگاه شما را مدیریت می‌کند."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> با استفاده از <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> دستگاهتان را مدیریت می‌کند."</string>
     <string name="monitoring_description_do_body" msgid="3639594537660975895">"سرپرست سیستم شما می‌تواند تنظیمات، دسترسی شرکتی، برنامه‌ها، داده‌های مرتبط با دستگاه و اطلاعات مکان دستگاه شما را مدیریت کند و بر آن‌ها نظارت داشته باشد."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"بیشتر بدانید"</string>
-    <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"به <xliff:g id="VPN_APP">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شما را (ازجمله رایانامه‌‌ها، برنامه‌‌ها و وب‌سایت‌ها) کنترل کند."</string>
+    <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"به <xliff:g id="VPN_APP">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شما را (ازجمله ایمیل‌ها، برنامه‌‌ها و وب‌سایت‌ها) کنترل کند."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="6434859242636063861">"‏باز کردن تنظیمات VPN"</string>
     <string name="monitoring_description_ca_cert_settings_separator" msgid="4987350385906393626">" "</string>
     <string name="monitoring_description_ca_cert_settings" msgid="5489969458872997092">"باز کردن اعتبارنامه مورداعتماد"</string>
     <string name="monitoring_description_network_logging" msgid="7223505523384076027">"سرپرست سیستم شما گزارش‌گیری شبکه را (که بر ترافیک دستگاهتان نظارت می‌کند) روشن کرده است.\n\nبرای اطلاعات بیشتر، با سرپرست خود تماس بگیرید."</string>
-    <string name="monitoring_description_vpn" msgid="4445150119515393526">"‏شما به برنامه‌ای برای تنظیم اتصال VPN اجازه دادید.\n\n این برنامه می‌تواند دستگاه و فعالیت شبکه‌تان را کنترل کند، از جمله رایانامه‌، برنامه‌ و وب‌سایت‌ها."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"‏نمایه کاری شما توسط <xliff:g id="ORGANIZATION">%1$s</xliff:g> مدیریت می‌شود.\n\nسرپرست سیستم شما می‌تواند بر فعالیت شبکه شما (ازجمله رایانامه‌ها، برنامه‌ها و وب‌سایت‌ها) نظارت داشته باشد.\n\nبرای اطلاعات بیشتر، با سرپرست خود تماس بگیرید.\n\nهمچنین به VPN متصل هستید که می‌تواند بر فعالیت شبکه شما نظارت داشته باشد."</string>
+    <string name="monitoring_description_vpn" msgid="4445150119515393526">"‏شما به برنامه‌ای برای تنظیم اتصال VPN اجازه دادید.\n\n این برنامه می‌تواند دستگاه و فعالیت شبکه‌تان را کنترل کند، از جمله ایمیل‌، برنامه‌ و وب‌سایت‌ها."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"‏نمایه کاری شما توسط <xliff:g id="ORGANIZATION">%1$s</xliff:g> مدیریت می‌شود.\n\nسرپرست سیستم شما می‌تواند بر فعالیت شبکه شما (ازجمله ایمیل‌ها، برنامه‌ها و وب‌سایت‌ها) نظارت داشته باشد.\n\nبرای اطلاعات بیشتر، با سرپرست خود تماس بگیرید.\n\nهمچنین به VPN متصل هستید که می‌تواند بر فعالیت شبکه شما نظارت داشته باشد."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
-    <string name="monitoring_description_app" msgid="1828472472674709532">"شما به <xliff:g id="APPLICATION">%1$s</xliff:g> متصل هستید، که می‌تواند فعالیت شما در شبکه (ازجمله رایانامه‌، برنامه‌ و وب‌سایت‌ها) را پایش کند."</string>
-    <string name="monitoring_description_app_personal" msgid="484599052118316268">"شما به <xliff:g id="APPLICATION">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شخصی شما از جمله رایانامه‌، برنامه‌ و وب‌سایت‌ها را کنترل کند."</string>
-    <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"به <xliff:g id="APPLICATION">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شخصی شما را (ازجمله رایانامه‌‌ها، برنامه‌‌ها و وب‌سایت‌ها) کنترل کند."</string>
-    <string name="monitoring_description_app_work" msgid="4612997849787922906">"نمایه کاری شما توسط <xliff:g id="ORGANIZATION">%1$s</xliff:g> مدیریت می‌شود. این نمایه به <xliff:g id="APPLICATION">%2$s</xliff:g> متصل است که می‌تواند فعالیت شما در شبکه (ازجمله رایانامه‌ها، برنامه‌ها و وب‌سایت‌ها) را پایش کند.\n\nبرای اطلاعات بیشتر، با سرپرست سیستم تماس بگیرید."</string>
+    <string name="monitoring_description_app" msgid="1828472472674709532">"شما به <xliff:g id="APPLICATION">%1$s</xliff:g> متصل هستید، که می‌تواند فعالیت شما در شبکه (ازجمله ایمیل‌، برنامه‌ و وب‌سایت‌ها) را پایش کند."</string>
+    <string name="monitoring_description_app_personal" msgid="484599052118316268">"شما به <xliff:g id="APPLICATION">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شخصی شما از جمله ایمیل‌، برنامه‌ و وب‌سایت‌ها را کنترل کند."</string>
+    <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"به <xliff:g id="APPLICATION">%1$s</xliff:g> وصل شده‌اید که می‌تواند فعالیت شبکه شخصی شما را (ازجمله ایمیل‌ها، برنامه‌‌ها و وب‌سایت‌ها) کنترل کند."</string>
+    <string name="monitoring_description_app_work" msgid="4612997849787922906">"نمایه کاری شما توسط <xliff:g id="ORGANIZATION">%1$s</xliff:g> مدیریت می‌شود. این نمایه به <xliff:g id="APPLICATION">%2$s</xliff:g> متصل است که می‌تواند فعالیت شما در شبکه (ازجمله ایمیل‌ها، برنامه‌ها و وب‌سایت‌ها) را پایش کند.\n\nبرای اطلاعات بیشتر، با سرپرست سیستم تماس بگیرید."</string>
     <string name="monitoring_description_app_personal_work" msgid="5664165460056859391">"نمایه کاری‌تان توسط <xliff:g id="ORGANIZATION">%1$s</xliff:g> مدیریت می‌شود. این نمایه به <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> متصل است که می‌تواند تنظیمات، دسترسی شرکتی، برنامه‌ها، داده‌های مرتبط با دستگاه و اطلاعات مکان دستگاه شما را پایش کند.\n\nشما همچنین به <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> متصل هستید که می‌تواند فعالیت خصوصی شما را در شبکه پایش کند."</string>
     <string name="keyguard_indication_trust_granted" msgid="4985003749105182372">"قفل برای <xliff:g id="USER_NAME">%1$s</xliff:g> باز شد"</string>
     <string name="keyguard_indication_trust_managed" msgid="8319646760022357585">"<xliff:g id="TRUST_AGENT">%1$s</xliff:g> درحال اجرا است"</string>
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"درحال استفاده از نمایه کاری‌تان هستید"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"تماس"</string>
     <string name="stream_system" msgid="7493299064422163147">"سیستم"</string>
     <string name="stream_ring" msgid="8213049469184048338">"زنگ زدن"</string>
@@ -682,7 +681,7 @@
     <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"دستیار"</string>
     <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"مرورگر"</string>
     <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"مخاطبین"</string>
-    <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"رایانامه"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ایمیل"</string>
     <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"پیامک"</string>
     <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"موسیقی"</string>
     <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
diff --git a/packages/SystemUI/res/values-fa/strings_car.xml b/packages/SystemUI/res/values-fa/strings_car.xml
index 3328af1..f74c33c 100644
--- a/packages/SystemUI/res/values-fa/strings_car.xml
+++ b/packages/SystemUI/res/values-fa/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"مهمان"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"افزودن کاربر"</string>
     <string name="car_new_user" msgid="8142927244990323906">"کاربر جدید"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"وقتی کاربر جدیدی اضافه می‌کنید، آن فرد باید فضای خود را تنظیم کند."</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index fbccf86..baa7822 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Piilotetaanko <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Se tulee takaisin näkyviin, kun seuraavan kerran otat sen käyttöön asetuksissa."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Piilota"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Käytät työprofiilia."</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Puhelu"</string>
     <string name="stream_system" msgid="7493299064422163147">"Järjestelmä"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Soittoääni"</string>
diff --git a/packages/SystemUI/res/values-fi/strings_car.xml b/packages/SystemUI/res/values-fi/strings_car.xml
index 408eea1..b2501ee 100644
--- a/packages/SystemUI/res/values-fi/strings_car.xml
+++ b/packages/SystemUI/res/values-fi/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Vieras"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Lisää käyttäjä"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Uusi käyttäjä"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Kun lisäät uuden käyttäjän, hänen on määritettävä oman tilansa asetukset."</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index fb1e675..bb4cdfb 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -524,7 +524,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Masquer <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Cet élément réapparaîtra la prochaine fois que vous l\'activerez dans les paramètres."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Masquer"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Vous utilisez votre profil professionnel."</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Appeler"</string>
     <string name="stream_system" msgid="7493299064422163147">"Système"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Sonnerie"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings_car.xml b/packages/SystemUI/res/values-fr-rCA/strings_car.xml
index e1cd5a2..8041e7e 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings_car.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Invité"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Ajouter un utilisateur"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Nouvel utilisateur"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Lorsque vous ajoutez un utilisateur, celui-ci doit configurer son espace."</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index d13e9ff..532fe50 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -524,7 +524,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Masquer <xliff:g id="TILE_LABEL">%1$s</xliff:g> ?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Cet élément réapparaîtra la prochaine fois que vous l\'activerez dans les paramètres."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Masquer"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Vous utilisez votre profil professionnel."</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Appel"</string>
     <string name="stream_system" msgid="7493299064422163147">"Système"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Sonnerie"</string>
@@ -537,7 +536,7 @@
     <string name="ring_toggle_title" msgid="3281244519428819576">"Appels"</string>
     <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Sonnerie"</string>
     <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibreur"</string>
-    <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Silencieux"</string>
+    <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Couper le son"</string>
     <string name="qs_status_phone_vibrate" msgid="204362991135761679">"Mode Vibreur activé"</string>
     <string name="qs_status_phone_muted" msgid="5437668875879171548">"Sons du téléphone désactivés"</string>
     <string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Appuyez pour ne plus ignorer."</string>
diff --git a/packages/SystemUI/res/values-fr/strings_car.xml b/packages/SystemUI/res/values-fr/strings_car.xml
index f35d2f6..4a6bd70 100644
--- a/packages/SystemUI/res/values-fr/strings_car.xml
+++ b/packages/SystemUI/res/values-fr/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Invité"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Ajouter un utilisateur"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Nouvel utilisateur"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Lorsque vous ajoutez un utilisateur, celui-ci doit configurer son espace."</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 6b5e0c3..0e8b093 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -524,7 +524,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Queres ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Volverá aparecer a próxima vez que se active na configuración."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estás usando o perfil de traballo"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Chamada"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Ton"</string>
diff --git a/packages/SystemUI/res/values-gl/strings_car.xml b/packages/SystemUI/res/values-gl/strings_car.xml
index e69c302..d897b34 100644
--- a/packages/SystemUI/res/values-gl/strings_car.xml
+++ b/packages/SystemUI/res/values-gl/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Convidado"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Engadir usuario"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Novo usuario"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Cando engadas un usuario novo, este deberá configurar o seu espazo."</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index a982341..a730f01 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"તમે તમારી કાર્ય પ્રોફાઇલનો ઉપયોગ કરી રહ્યાં છો"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"કૉલ કરો"</string>
     <string name="stream_system" msgid="7493299064422163147">"સિસ્ટમ"</string>
     <string name="stream_ring" msgid="8213049469184048338">"રિંગ વગાડો"</string>
diff --git a/packages/SystemUI/res/values-gu/strings_car.xml b/packages/SystemUI/res/values-gu/strings_car.xml
index 83ae932..e42c247 100644
--- a/packages/SystemUI/res/values-gu/strings_car.xml
+++ b/packages/SystemUI/res/values-gu/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"અતિથિ"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"વપરાશકર્તા ઉમેરો"</string>
     <string name="car_new_user" msgid="8142927244990323906">"નવા વપરાશકર્તા"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"જ્યારે તમે કોઈ નવા વપરાશકર્તાને ઉમેરો છો, ત્યારે તે વ્યક્તિએ તેમની સ્પેસ સેટ કરવાની જરૂર રહે છે."</string>
diff --git a/packages/SystemUI/res/values-hi-ldrtl/strings.xml b/packages/SystemUI/res/values-hi-ldrtl/strings.xml
index 6200b8d..df86c57 100644
--- a/packages/SystemUI/res/values-hi-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-hi-ldrtl/strings.xml
@@ -19,5 +19,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recents_quick_scrub_onboarding" msgid="3449337212132057178">"ऐप्लिकेशन को तेज़ी से स्विच करने के लिए उसे बाईं ओर खींचें और छोड़ें"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="3449337212132057178">"ऐप्लिकेशन को तेज़ी से बदलने के लिए उसे बाईं ओर खींचें और छोड़ें"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 3eb7e97..7049044 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -409,7 +409,7 @@
     <string name="user_add_user" msgid="5110251524486079492">"उपयोगकर्ता जोड़ें"</string>
     <string name="user_new_user_name" msgid="426540612051178753">"नया उपयोगकर्ता"</string>
     <string name="guest_nickname" msgid="8059989128963789678">"मेहमान"</string>
-    <string name="guest_new_guest" msgid="600537543078847803">"अतिथि जोड़ें"</string>
+    <string name="guest_new_guest" msgid="600537543078847803">"मेहमान जोड़ें"</string>
     <string name="guest_exit_guest" msgid="7187359342030096885">"अतिथि को निकालें"</string>
     <string name="guest_exit_guest_dialog_title" msgid="8480693520521766688">"अतिथि को निकालें?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="4155503224769676625">"इस सत्र के सभी ऐप्स और डेटा को हटा दिया जाएगा."</string>
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"आप अपनी कार्य प्रोफ़ाइल का उपयोग कर रहे हैं"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"कॉल करें"</string>
     <string name="stream_system" msgid="7493299064422163147">"सिस्‍टम"</string>
     <string name="stream_ring" msgid="8213049469184048338">"घंटी बजाएं"</string>
diff --git a/packages/SystemUI/res/values-hi/strings_car.xml b/packages/SystemUI/res/values-hi/strings_car.xml
index 468fba0..8820046 100644
--- a/packages/SystemUI/res/values-hi/strings_car.xml
+++ b/packages/SystemUI/res/values-hi/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"मेहमान"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"उपयोगकर्ता जोड़ें"</string>
     <string name="car_new_user" msgid="8142927244990323906">"नया उपयोगकर्ता"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"जब आप कोई नया उपयोगकर्ता जोड़ते हैं तो, उस व्यक्ति को अपनी जगह सेट करनी होती है."</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index d784ac4..a77b7ff 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -525,7 +525,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite li sakriti pločicu <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ponovo će se pojaviti kada je sljedeći put uključite u postavkama."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Sakrij"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Upotrebljavate radni profil"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Nazovi"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sustav"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Zvoni"</string>
diff --git a/packages/SystemUI/res/values-hr/strings_car.xml b/packages/SystemUI/res/values-hr/strings_car.xml
index aff0090..a94f235 100644
--- a/packages/SystemUI/res/values-hr/strings_car.xml
+++ b/packages/SystemUI/res/values-hr/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Gost"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Dodajte korisnika"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Novi korisnik"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Kada dodate novog korisnika, ta osoba mora postaviti vlastiti prostor."</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index c9d83e6..d6e2cb6 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Elrejti ezt: <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Újból megjelenik majd, amikor ismét engedélyezi a beállítások között."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Elrejtés"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"A munkaprofilt használja"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Telefonhívás"</string>
     <string name="stream_system" msgid="7493299064422163147">"Rendszer"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Csörgetés"</string>
diff --git a/packages/SystemUI/res/values-hu/strings_car.xml b/packages/SystemUI/res/values-hu/strings_car.xml
index 1eb17ce..223b354 100644
--- a/packages/SystemUI/res/values-hu/strings_car.xml
+++ b/packages/SystemUI/res/values-hu/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Vendég"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Felhasználó hozzáadása"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Új felhasználó"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Ha új felhasználót ad hozzá, az illetőnek be kell állítania saját felületét."</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 96952f8..7c0ed52 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -69,15 +69,15 @@
     <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Ընթացիկ հաշվի օգտատերը չի կարող միացնել USB վրիպազերծումը: Այս գործառույթը միացնելու համար մուտք գործեք հիմնական օգտատիրոջ հաշվով:"</string>
     <string name="compat_mode_on" msgid="6623839244840638213">"Խոշորացնել` էկրանը լցնելու համար"</string>
     <string name="compat_mode_off" msgid="4434467572461327898">"Ձգել` էկրանը լցնելու համար"</string>
-    <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_text" msgid="7574667448002050363">"Հպեք՝ էկրանի պատկերը տեսնելու համար"</string>
-    <string name="screenshot_failed_title" msgid="7612509838919089748">"Չհաջողվեց պահել էկրանի պատկերը"</string>
+    <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_text" msgid="7574667448002050363">"Հպեք՝ սքրինշոթը տեսնելու համար"</string>
+    <string name="screenshot_failed_title" msgid="7612509838919089748">"Չհաջողվեց պահել սքրինշոթը"</string>
     <string name="screenshot_failed_to_save_unknown_text" msgid="3637758096565605541">"Փորձեք նորից"</string>
-    <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Չհաջողվեց պահել էկրանի պատկերը անբավարար հիշողության պատճառով"</string>
-    <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Հավելվածը կամ ձեր կազմակերպությունը չի թույլատրում էկրանի պատկերի ստացումը"</string>
+    <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Չհաջողվեց պահել սքրինշոթը անբավարար հիշողության պատճառով"</string>
+    <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Հավելվածը կամ ձեր կազմակերպությունը չի թույլատրում սքրինշոթի ստացումը"</string>
     <string name="usb_preference_title" msgid="6551050377388882787">"USB ֆայլերի փոխանցման ընտրանքներ"</string>
     <string name="use_mtp_button_title" msgid="4333504413563023626">"Միացնել որպես մեդիա նվագարկիչ (MTP)"</string>
     <string name="use_ptp_button_title" msgid="7517127540301625751">"Միացնել որպես ֆոտոխցիկ (PTP)"</string>
@@ -292,8 +292,8 @@
     <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_portrait_label" msgid="5102691921442135053">"Դիմանկար"</string>
-    <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Լանդշաֆտ"</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>
     <string name="quick_settings_location_label" msgid="5011327048748762257">"Տեղորոշում"</string>
     <string name="quick_settings_location_off_label" msgid="7464544086507331459">"Անջատել տեղադրությունը"</string>
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Դուք օգտագործում եք ձեր աշխատանքային պրոֆիլը"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Զանգ"</string>
     <string name="stream_system" msgid="7493299064422163147">"Համակարգ"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Զանգ"</string>
@@ -815,7 +814,7 @@
     <string name="tuner_app" msgid="3507057938640108777">"<xliff:g id="APP">%1$s</xliff:g> հավելված"</string>
     <string name="notification_channel_alerts" msgid="4496839309318519037">"Ծանուցումներ"</string>
     <string name="notification_channel_battery" msgid="5786118169182888462">"Մարտկոց"</string>
-    <string name="notification_channel_screenshot" msgid="6314080179230000938">"Էկրանի պատկերներ"</string>
+    <string name="notification_channel_screenshot" msgid="6314080179230000938">"Սքրինշոթներ"</string>
     <string name="notification_channel_general" msgid="4525309436693914482">"Ընդհանուր հաղորդագրություններ"</string>
     <string name="notification_channel_storage" msgid="3077205683020695313">"Տարածք"</string>
     <string name="notification_channel_hints" msgid="7323870212489152689">"Հուշումներ"</string>
diff --git a/packages/SystemUI/res/values-hy/strings_car.xml b/packages/SystemUI/res/values-hy/strings_car.xml
index 555deeb..dd734f0 100644
--- a/packages/SystemUI/res/values-hy/strings_car.xml
+++ b/packages/SystemUI/res/values-hy/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Հյուր"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Ավելացնել օգտատեր"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Նոր օգտատեր"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Երբ դուք նոր օգտատեր եք ավելացնում, նա պետք է կարգավորի իր պրոֆիլը:"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 1a042fc..d75d121 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Sembunyikan <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ini akan muncul kembali saat Anda mengaktifkannya dalam setelan."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Sembunyikan"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Anda menggunakan profil kerja"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Telepon"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Dering"</string>
@@ -580,7 +579,7 @@
     <string name="tuner_warning_title" msgid="7094689930793031682">"Tidak semua orang menganggapnya baik"</string>
     <string name="tuner_warning" msgid="8730648121973575701">"Penyetel Antarmuka Pengguna Sistem memberikan cara tambahan untuk mengubah dan menyesuaikan antarmuka pengguna Android. Fitur eksperimental ini dapat berubah, rusak, atau menghilang dalam rilis di masa mendatang. Lanjutkan dengan hati-hati."</string>
     <string name="tuner_persistent_warning" msgid="8597333795565621795">"Fitur eksperimental ini dapat berubah, rusak, atau menghilang dalam rilis di masa mendatang. Lanjutkan dengan hati-hati."</string>
-    <string name="got_it" msgid="2239653834387972602">"Mengerti"</string>
+    <string name="got_it" msgid="2239653834387972602">"Oke"</string>
     <string name="tuner_toast" msgid="603429811084428439">"Selamat! Penyetel Antarmuka Pengguna Sistem telah ditambahkan ke Setelan"</string>
     <string name="remove_from_settings" msgid="8389591916603406378">"Hapus dari Setelan"</string>
     <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Hapus Penyetel Antarmuka Pengguna Sistem dari Setelan dan berhenti menggunakan semua fiturnya?"</string>
diff --git a/packages/SystemUI/res/values-in/strings_car.xml b/packages/SystemUI/res/values-in/strings_car.xml
index f0620d0..de0a1a8 100644
--- a/packages/SystemUI/res/values-in/strings_car.xml
+++ b/packages/SystemUI/res/values-in/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Tamu"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Tambahkan Pengguna"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Pengguna Baru"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Saat Anda menambahkan pengguna baru, orang tersebut perlu menyiapkan ruangnya sendiri."</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 61596f5..eaad622 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Fela <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Þetta birtist aftur næst þegar þú kveikir á því í stillingunum."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Fela"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Þú ert að nota vinnusniðið"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Símtal"</string>
     <string name="stream_system" msgid="7493299064422163147">"Kerfi"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Hringing"</string>
diff --git a/packages/SystemUI/res/values-is/strings_car.xml b/packages/SystemUI/res/values-is/strings_car.xml
index 2a633f4..f962765 100644
--- a/packages/SystemUI/res/values-is/strings_car.xml
+++ b/packages/SystemUI/res/values-is/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Gestur"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Bæta notanda við"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Nýr notandi"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Þegar þú bætir nýjum notanda við þarf viðkomandi að setja upp sitt eigið svæði."</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 65add2a..7bd52d4 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -524,7 +524,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Nascondere <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Verranno visualizzate di nuovo quando le riattiverai nelle impostazioni."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Nascondi"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Stai utilizzando il profilo di lavoro"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Chiamata"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Suoneria"</string>
diff --git a/packages/SystemUI/res/values-it/strings_car.xml b/packages/SystemUI/res/values-it/strings_car.xml
index 095e5dc..5121eb7 100644
--- a/packages/SystemUI/res/values-it/strings_car.xml
+++ b/packages/SystemUI/res/values-it/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Ospite"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Aggiungi utente"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Nuovo utente"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Il nuovo utente, una volta aggiunto, dovrà configurare il proprio spazio."</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 06b20a7..cac6d31 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -528,7 +528,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"אתה משתמש בפרופיל העבודה שלך"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"שיחה"</string>
     <string name="stream_system" msgid="7493299064422163147">"מערכת"</string>
     <string name="stream_ring" msgid="8213049469184048338">"צלצול"</string>
diff --git a/packages/SystemUI/res/values-iw/strings_car.xml b/packages/SystemUI/res/values-iw/strings_car.xml
index 04a8b9e..8b77940 100644
--- a/packages/SystemUI/res/values-iw/strings_car.xml
+++ b/packages/SystemUI/res/values-iw/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"אורח"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"הוספת משתמש"</string>
     <string name="car_new_user" msgid="8142927244990323906">"משתמש חדש"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"בעת הוספת משתמש חדש, על משתמש זה להגדיר את המרחב שלו."</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 4b3621a..001be11 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -524,7 +524,6 @@
     <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">"次回、設定でONにすると再表示されます。"</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"非表示"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"仕事用プロファイルを使用しています"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"発信"</string>
     <string name="stream_system" msgid="7493299064422163147">"システム"</string>
     <string name="stream_ring" msgid="8213049469184048338">"着信音"</string>
@@ -538,7 +537,7 @@
     <string name="volume_ringer_status_normal" msgid="4273142424125855384">"着信音"</string>
     <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"バイブレーション"</string>
     <string name="volume_ringer_status_silent" msgid="6896394161022916369">"ミュート"</string>
-    <string name="qs_status_phone_vibrate" msgid="204362991135761679">"スマートフォンをバイブレーションに設定"</string>
+    <string name="qs_status_phone_vibrate" msgid="204362991135761679">"スマートフォンのバイブレーションは ON です"</string>
     <string name="qs_status_phone_muted" msgid="5437668875879171548">"スマートフォンをミュートに設定"</string>
     <string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s。タップしてミュートを解除します。"</string>
     <string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s。タップしてバイブレーションに設定します。ユーザー補助機能サービスがミュートされる場合があります。"</string>
@@ -606,7 +605,7 @@
     <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="inline_blocking_helper" msgid="3055064577771478591">"通常、この通知は非表示にしています。\n引き続き、表示しますか?"</string>
+    <string name="inline_blocking_helper" msgid="3055064577771478591">"通常、この通知はスワイプして非表示にしています。\n今後も表示しますか?"</string>
     <string name="inline_keep_showing" msgid="8945102997083836858">"この通知を今後も表示しますか?"</string>
     <string name="inline_stop_button" msgid="4172980096860941033">"通知を表示しない"</string>
     <string name="inline_keep_button" msgid="6665940297019018232">"今後も表示する"</string>
diff --git a/packages/SystemUI/res/values-ja/strings_car.xml b/packages/SystemUI/res/values-ja/strings_car.xml
index 5a2872d..fddd512 100644
--- a/packages/SystemUI/res/values-ja/strings_car.xml
+++ b/packages/SystemUI/res/values-ja/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"ゲスト"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"ユーザーを追加"</string>
     <string name="car_new_user" msgid="8142927244990323906">"新しいユーザー"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"新しいユーザーを追加したら、そのユーザーは自分のスペースをセットアップする必要があります。"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 8a57d62..c28fab7 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"თქვენ სამსახურის პროფილს იყენებთ"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"დარეკვა"</string>
     <string name="stream_system" msgid="7493299064422163147">"სისტემა"</string>
     <string name="stream_ring" msgid="8213049469184048338">"დარეკვა"</string>
diff --git a/packages/SystemUI/res/values-ka/strings_car.xml b/packages/SystemUI/res/values-ka/strings_car.xml
index bb12f6d..b729255 100644
--- a/packages/SystemUI/res/values-ka/strings_car.xml
+++ b/packages/SystemUI/res/values-ka/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"სტუმარი"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"მომხმარებლის დამატება"</string>
     <string name="car_new_user" msgid="8142927244990323906">"ახალი მომხმარებელი"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"ახალი მომხმარებლის დამატებისას, ამ მომხმარებელს საკუთარი სივრცის შექმნა მოუწევს."</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index e63a9d3..f256d33 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Сіз жұмыс профиліңізді пайдаланып жатырсыз"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Қоңырау шалу"</string>
     <string name="stream_system" msgid="7493299064422163147">"Жүйе"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Шылдырлау"</string>
diff --git a/packages/SystemUI/res/values-kk/strings_car.xml b/packages/SystemUI/res/values-kk/strings_car.xml
index 50aedf3..4d1f9cf 100644
--- a/packages/SystemUI/res/values-kk/strings_car.xml
+++ b/packages/SystemUI/res/values-kk/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Қонақ"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Пайдаланушыны енгізу"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Жаңа пайдаланушы"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Енгізілген жаңа пайдаланушы өз профилін реттеуі керек."</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 922d9c2..2c9a554 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"អ្នកកំពុងប្រើប្រវត្តិរូបការងាររបស់អ្នក"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"ហៅ"</string>
     <string name="stream_system" msgid="7493299064422163147">"ប្រព័ន្ធ"</string>
     <string name="stream_ring" msgid="8213049469184048338">"រោទ៍"</string>
diff --git a/packages/SystemUI/res/values-km/strings_car.xml b/packages/SystemUI/res/values-km/strings_car.xml
index 15b462c..a897dba 100644
--- a/packages/SystemUI/res/values-km/strings_car.xml
+++ b/packages/SystemUI/res/values-km/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"ភ្ញៀវ"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"បញ្ចូល​អ្នក​ប្រើប្រាស់"</string>
     <string name="car_new_user" msgid="8142927244990323906">"អ្នក​ប្រើប្រាស់​ថ្មី"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"នៅពេលដែល​អ្នក​បញ្ចូល​អ្នក​ប្រើប្រាស់​ថ្មី បុគ្គល​នោះ​ត្រូវតែ​រៀបចំ​ទំហំ​ផ្ទុក​របស់គេ។"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index ec4c7ce..8760d00 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್‌ ಅನ್ನು ನೀವು ಬಳಸುತ್ತಿರುವಿರಿ"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"ಕರೆಮಾಡಿ"</string>
     <string name="stream_system" msgid="7493299064422163147">"ಸಿಸ್ಟಂ"</string>
     <string name="stream_ring" msgid="8213049469184048338">"ರಿಂಗ್"</string>
diff --git a/packages/SystemUI/res/values-kn/strings_car.xml b/packages/SystemUI/res/values-kn/strings_car.xml
index d06d411..53cdc32 100644
--- a/packages/SystemUI/res/values-kn/strings_car.xml
+++ b/packages/SystemUI/res/values-kn/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"ಅತಿಥಿ"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"ಬಳಕೆದಾರ ಸೇರಿಸು"</string>
     <string name="car_new_user" msgid="8142927244990323906">"ಹೊಸ ಬಳಕೆದಾರ"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"ನೀವು ಹೊಸ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿದಾಗ, ಆ ವ್ಯಕ್ತಿಯು ಅವರ ಸ್ಥಳವನ್ನು ಸೆಟಪ್ ಮಾಡಬೇಕಾಗುತ್ತದೆ."</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 3db09d5..7983966 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -524,7 +524,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"직장 프로필을 사용하고 있습니다."</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"전화걸기"</string>
     <string name="stream_system" msgid="7493299064422163147">"시스템"</string>
     <string name="stream_ring" msgid="8213049469184048338">"벨소리"</string>
diff --git a/packages/SystemUI/res/values-ko/strings_car.xml b/packages/SystemUI/res/values-ko/strings_car.xml
index 1af18aa..9cfaa7f 100644
--- a/packages/SystemUI/res/values-ko/strings_car.xml
+++ b/packages/SystemUI/res/values-ko/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"게스트"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"사용자 추가"</string>
     <string name="car_new_user" msgid="8142927244990323906">"신규 사용자"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"추가된 새로운 사용자는 자신의 공간을 설정해야 합니다."</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 5f4b485..d9c9322 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -299,7 +299,7 @@
     <string name="quick_settings_location_off_label" msgid="7464544086507331459">"Жайгашытрууну өчүрүү"</string>
     <string name="quick_settings_media_device_label" msgid="1302906836372603762">"Медиа түзмөгү"</string>
     <string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
-    <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"Куткаруучуларга чалуу гана"</string>
+    <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"Өзгөчө кырдаалда гана чалууга болот"</string>
     <string name="quick_settings_settings_label" msgid="5326556592578065401">"Жөндөөлөр"</string>
     <string name="quick_settings_time_label" msgid="4635969182239736408">"Убакыт"</string>
     <string name="quick_settings_user_label" msgid="5238995632130897840">"Мен"</string>
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Жумуш профилиңизди колдонуп жатасыз"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Чалуу"</string>
     <string name="stream_system" msgid="7493299064422163147">"Тутум"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Шыңгыратуу"</string>
diff --git a/packages/SystemUI/res/values-ky/strings_car.xml b/packages/SystemUI/res/values-ky/strings_car.xml
index f84e073..67ffdbe 100644
--- a/packages/SystemUI/res/values-ky/strings_car.xml
+++ b/packages/SystemUI/res/values-ky/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Конок"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Колдонуучу кошуу"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Жаңы колдонуучу"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Жаңы колдонуучу кошулганда, ал өзүнүн профилин жөндөп алышы керек."</string>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index c59dbdc..bb0c6f6 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -24,8 +24,6 @@
 
     <dimen name="brightness_mirror_height">40dp</dimen>
 
-    <!-- Width for the spacer, used between QS tiles. -->
-    <dimen name="qs_quick_tile_space_width">38dp</dimen>
     <dimen name="qs_tile_margin_top">2dp</dimen>
     <dimen name="qs_header_tooltip_height">24dp</dimen>
 
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 877b512..7e2375e 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ທ່ານກຳລັງໃຊ້ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານ"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"ໂທ"</string>
     <string name="stream_system" msgid="7493299064422163147">"ລະ​ບົບ"</string>
     <string name="stream_ring" msgid="8213049469184048338">"​ເຕືອນ​ດ້ວຍ​ສຽງ"</string>
diff --git a/packages/SystemUI/res/values-lo/strings_car.xml b/packages/SystemUI/res/values-lo/strings_car.xml
index 124d6a8..a4170098 100644
--- a/packages/SystemUI/res/values-lo/strings_car.xml
+++ b/packages/SystemUI/res/values-lo/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"ແຂກ"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"ເພີ່ມຜູ້ໃຊ້"</string>
     <string name="car_new_user" msgid="8142927244990323906">"ຜູ້ໃຊ້ໃໝ່"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"ເມື່ອທ່ານເພີ່ມຜູ້ໃຊ້ໃໝ່, ບຸກຄົນນັ້ນຈຳເປັນຕ້ອງຕັ້ງຄ່າພື້ນທີ່ຂອງເຂົາເຈົ້າ."</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index eb92531..2c97127 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -528,7 +528,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Slėpti „<xliff:g id="TILE_LABEL">%1$s</xliff:g>“?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Tai bus vėl parodyta, kai kitą kartą įjungsite tai nustatymuose."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Slėpti"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Naudojate darbo profilį"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Skambutis"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Skambutis"</string>
@@ -829,8 +828,8 @@
     <string name="notification_channel_general" msgid="4525309436693914482">"Bendrieji pranešimai"</string>
     <string name="notification_channel_storage" msgid="3077205683020695313">"Saugykla"</string>
     <string name="notification_channel_hints" msgid="7323870212489152689">"Užuominos"</string>
-    <string name="instant_apps" msgid="6647570248119804907">"Akimirksniu įkeliamos programėlės"</string>
-    <string name="instant_apps_message" msgid="8116608994995104836">"Akimirksniu įkeliamų programėlių nereikia įdiegti."</string>
+    <string name="instant_apps" msgid="6647570248119804907">"Akimirksniu įkeliamos programos"</string>
+    <string name="instant_apps_message" msgid="8116608994995104836">"Akimirksniu įkeliamų programų nereikia įdiegti."</string>
     <string name="app_info" msgid="6856026610594615344">"Programos informacija"</string>
     <string name="go_to_web" msgid="2650669128861626071">"Eiti į naršyklę"</string>
     <string name="mobile_data" msgid="7094582042819250762">"Mobilieji duomenys"</string>
diff --git a/packages/SystemUI/res/values-lt/strings_car.xml b/packages/SystemUI/res/values-lt/strings_car.xml
index 7b7856f..704184c 100644
--- a/packages/SystemUI/res/values-lt/strings_car.xml
+++ b/packages/SystemUI/res/values-lt/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Svečias"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Pridėti naudotoją"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Naujas naudotojas"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Kai pridedate naują naudotoją, šis asmuo turi nustatyti savo vietą."</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 28014e4..86e2e4c 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -525,7 +525,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vai paslēpt vienumu <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Tas tiks atkārtoti parādīts, kad nākamreiz ieslēgsiet to iestatījumos."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Paslēpt"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Jūs izmantojat darba profilu."</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Zvans"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistēma"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Zvans"</string>
diff --git a/packages/SystemUI/res/values-lv/strings_car.xml b/packages/SystemUI/res/values-lv/strings_car.xml
index d19834b..8f5c283 100644
--- a/packages/SystemUI/res/values-lv/strings_car.xml
+++ b/packages/SystemUI/res/values-lv/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Viesis"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Lietotāja pievienošana"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Jauns lietotājs"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Kad pievienosiet jaunu lietotāju, viņam būs jāizveido savs profils."</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 5d4dd4d..88ea162 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Го користите работниот профил"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Повик"</string>
     <string name="stream_system" msgid="7493299064422163147">"Систем"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Ѕвони"</string>
diff --git a/packages/SystemUI/res/values-mk/strings_car.xml b/packages/SystemUI/res/values-mk/strings_car.xml
index 0ac6cd3..772f8cf 100644
--- a/packages/SystemUI/res/values-mk/strings_car.xml
+++ b/packages/SystemUI/res/values-mk/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Гостин"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Додај корисник"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Нов корисник"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Кога додавате нов корисник, тоа лице треба да го постави својот простор."</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 9e6c239..9c18a22 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"നിങ്ങൾ ഉപയോഗിക്കുന്നത് ഔദ്യോഗിക പ്രൊഫൈലാണ്"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"കോള്‍"</string>
     <string name="stream_system" msgid="7493299064422163147">"സിസ്റ്റം"</string>
     <string name="stream_ring" msgid="8213049469184048338">"റിംഗുചെയ്യുക"</string>
diff --git a/packages/SystemUI/res/values-ml/strings_car.xml b/packages/SystemUI/res/values-ml/strings_car.xml
index e83baf1..10e28ab 100644
--- a/packages/SystemUI/res/values-ml/strings_car.xml
+++ b/packages/SystemUI/res/values-ml/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"അതിഥി"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"ഉപയോക്താവിനെ ചേര്‍ക്കുക"</string>
     <string name="car_new_user" msgid="8142927244990323906">"പുതിയ ഉപയോക്താവ്"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"നിങ്ങളൊരു പുതിയ ഉപയോക്താവിനെ ചേർക്കുമ്പോൾ, ആ വ്യക്തി സ്വന്തം ഇടം സജ്ജീകരിക്കേണ്ടതുണ്ട്."</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index ba56386..fb26e81 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -520,7 +520,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Та өөрийн ажлын профайлыг ашиглаж байна"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Залгах"</string>
     <string name="stream_system" msgid="7493299064422163147">"Систем"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Хонх"</string>
diff --git a/packages/SystemUI/res/values-mn/strings_car.xml b/packages/SystemUI/res/values-mn/strings_car.xml
index 01a92e7..0b46d3f 100644
--- a/packages/SystemUI/res/values-mn/strings_car.xml
+++ b/packages/SystemUI/res/values-mn/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Зочин"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Хэрэглэгч нэмэх"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Шинэ хэрэглэгч"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Таныг шинэ хэрэглэгч нэмэх үед тухайн хэрэглэгч хувийн орон зайгаа тохируулах шаардлагатай."</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 99b13f6..958a877 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"तुम्ही तुमचे कार्य प्रोफाईल वापरत आहात"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"कॉल करा"</string>
     <string name="stream_system" msgid="7493299064422163147">"सिस्टम"</string>
     <string name="stream_ring" msgid="8213049469184048338">"रिंग करा"</string>
diff --git a/packages/SystemUI/res/values-mr/strings_car.xml b/packages/SystemUI/res/values-mr/strings_car.xml
index 52d6260..5e23287 100644
--- a/packages/SystemUI/res/values-mr/strings_car.xml
+++ b/packages/SystemUI/res/values-mr/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"अतिथी"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"वापरकर्ता जोडा"</string>
     <string name="car_new_user" msgid="8142927244990323906">"नवीन वापरकर्ता"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"तुम्ही नवीन वापरकर्त्याला जोडल्यावर, त्या व्यक्तीने त्यांचे स्थान सेट करणे आवश्यक असते."</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index a1e8267..18007c2 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Sembunyikan <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Mesej itu akan terpapar semula pada kali seterusnya anda menghidupkan apl dalam tetapan."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Sembunyikan"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Anda sedang menggunakan profil kerja"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Panggil"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Dering"</string>
diff --git a/packages/SystemUI/res/values-ms/strings_car.xml b/packages/SystemUI/res/values-ms/strings_car.xml
index 6aaf348..8c002c8 100644
--- a/packages/SystemUI/res/values-ms/strings_car.xml
+++ b/packages/SystemUI/res/values-ms/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Tetamu"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Tambah Pengguna"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Pengguna Baharu"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Apabila anda menambahkan pengguna baharu, orang itu perlu menyediakan ruang mereka."</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 15df61c..f1d48b9 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"သင်သည် အလုပ်ပရိုဖိုင်းအား သုံးနေသည်"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"ဖုန်းခေါ်ရန်"</string>
     <string name="stream_system" msgid="7493299064422163147">"စနစ်"</string>
     <string name="stream_ring" msgid="8213049469184048338">"အသံမြည်စေသည်"</string>
diff --git a/packages/SystemUI/res/values-my/strings_car.xml b/packages/SystemUI/res/values-my/strings_car.xml
index 66ab4af..778a82e 100644
--- a/packages/SystemUI/res/values-my/strings_car.xml
+++ b/packages/SystemUI/res/values-my/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"ဧည့်သည်"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"အသုံးပြုသူ ထည့်ရန်"</string>
     <string name="car_new_user" msgid="8142927244990323906">"အသုံးပြုသူ အသစ်"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"အသုံးပြုသူအသစ် ထည့်သည့်အခါ ထိုသူသည် မိမိ၏ နေရာကို စီစဉ်သတ်မှတ်ရပါမည်။"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 5da9570..6af41ff 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -292,7 +292,7 @@
     <string name="accessibility_quick_settings_rotation" msgid="4231661040698488779">"Rotér skjermen automatisk"</string>
     <string name="accessibility_quick_settings_rotation_value" msgid="8187398200140760213">"<xliff:g id="ID_1">%s</xliff:g>-modus"</string>
     <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotasjonen er låst"</string>
-    <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Portrett"</string>
+    <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Stående"</string>
     <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Landskap"</string>
     <string name="quick_settings_ime_label" msgid="7073463064369468429">"Inndatametode"</string>
     <string name="quick_settings_location_label" msgid="5011327048748762257">"Sted"</string>
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vil du skjule <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Den vises igjen neste gang du slår den på i innstillingene."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skjul"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du bruker jobbprofilen din"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Anrop"</string>
     <string name="stream_system" msgid="7493299064422163147">"System"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
diff --git a/packages/SystemUI/res/values-nb/strings_car.xml b/packages/SystemUI/res/values-nb/strings_car.xml
index f63db63..9db879d 100644
--- a/packages/SystemUI/res/values-nb/strings_car.xml
+++ b/packages/SystemUI/res/values-nb/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Gjest"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Legg til bruker"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Ny bruker"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Når du legger til en ny bruker, må vedkommende konfigurere sitt eget område."</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 6f37055..5cd4325 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"तपाईँले कार्य प्रोफाइल प्रयोग गर्दै हुनुहुन्छ"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"कल"</string>
     <string name="stream_system" msgid="7493299064422163147">"प्रणाली"</string>
     <string name="stream_ring" msgid="8213049469184048338">"घन्टी"</string>
diff --git a/packages/SystemUI/res/values-ne/strings_car.xml b/packages/SystemUI/res/values-ne/strings_car.xml
index 1b92f6e..a435c2f 100644
--- a/packages/SystemUI/res/values-ne/strings_car.xml
+++ b/packages/SystemUI/res/values-ne/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"अतिथि"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"प्रयोगकर्ता थप्नुहोस्"</string>
     <string name="car_new_user" msgid="8142927244990323906">"नयाँ प्रयोगकर्ता"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"तपाईंले नयाँ प्रयोगकर्ता थप्दा ती व्यक्तिले आफ्नो स्थान सेटअप गर्नु पर्छ।"</string>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
new file mode 100644
index 0000000..45d2185
--- /dev/null
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -0,0 +1,44 @@
+<?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
+
+  NOTE: You might also want to edit: core/res/res/values-night/*.xml
+  -->
+<resources>
+    <!-- The color of the material notification background -->
+    <color name="notification_material_background_color">@*android:color/notification_material_background_color</color>
+
+    <!-- The color of the legacy notifications with customs backgrounds (gingerbread and lollipop.)
+    It's fine to override this color since at that point the shade was dark. -->
+    <color name="notification_legacy_background_color">@*android:color/notification_material_background_color</color>
+
+    <!-- The color of the material notification background when dimmed -->
+    <color name="notification_material_background_dimmed_color">#aa212121</color>
+
+    <!-- The color of the dividing line between grouped notifications while . -->
+    <color name="notification_divider_color">#000</color>
+
+    <!-- The background color of the notification shade -->
+    <color name="notification_shade_background_color">#181818</color>
+
+    <!-- The color of the ripples on the untinted notifications -->
+    <color name="notification_ripple_untinted_color">#30ffffff</color>
+
+    <!-- The "inside" of a notification, reached via longpress -->
+    <color name="notification_guts_bg_color">@*android:color/notification_material_background_color</color>
+
+    <!-- The color of the text inside a notification -->
+    <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-night/dimens.xml b/packages/SystemUI/res/values-night/dimens.xml
new file mode 100644
index 0000000..4814839
--- /dev/null
+++ b/packages/SystemUI/res/values-night/dimens.xml
@@ -0,0 +1,21 @@
+<?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>
+    <!-- The height of the divider between the individual notifications. -->
+    <dimen name="notification_divider_height">1dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index def7b19b..1ec5c10 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> verbergen?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Deze wordt opnieuw weergegeven zodra u de instelling weer inschakelt."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Verbergen"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Je gebruikt je werkprofiel"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Bellen"</string>
     <string name="stream_system" msgid="7493299064422163147">"Systeem"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Bellen"</string>
@@ -532,7 +531,7 @@
     <string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
     <string name="stream_dtmf" msgid="2447177903892477915">"Frequentie voor tweevoudige multitoon"</string>
     <string name="stream_accessibility" msgid="301136219144385106">"Toegankelijkheid"</string>
-    <string name="ring_toggle_title" msgid="3281244519428819576">"Oproepen"</string>
+    <string name="ring_toggle_title" msgid="3281244519428819576">"Gesprekken"</string>
     <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Bellen"</string>
     <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Trillen"</string>
     <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Dempen"</string>
@@ -547,7 +546,7 @@
     <string name="volume_ringer_hint_unmute" msgid="6602880133293060368">"dempen opheffen"</string>
     <string name="volume_ringer_hint_vibrate" msgid="4036802135666515202">"trillen"</string>
     <string name="volume_dialog_title" msgid="7272969888820035876">"%s-volumeknoppen"</string>
-    <string name="volume_dialog_ringer_guidance_ring" msgid="3360373718388509040">"Oproepen en meldingen gaan over (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+    <string name="volume_dialog_ringer_guidance_ring" msgid="3360373718388509040">"Gesprekken en meldingen gaan over (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
     <string name="output_title" msgid="5355078100792942802">"Media-uitvoer"</string>
     <string name="output_calls_title" msgid="8717692905017206161">"Uitvoer van telefoongesprek"</string>
     <string name="output_none_found" msgid="5544982839808921091">"Geen apparaten gevonden"</string>
diff --git a/packages/SystemUI/res/values-nl/strings_car.xml b/packages/SystemUI/res/values-nl/strings_car.xml
index 6346037..a3fc561 100644
--- a/packages/SystemUI/res/values-nl/strings_car.xml
+++ b/packages/SystemUI/res/values-nl/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Gast"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Gebruiker toevoegen"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Nieuwe gebruiker"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Wanneer je een nieuwe gebruiker toevoegt, moet die persoon zijn eigen profiel instellen."</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 7dbd5ad..8ac9f5f 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -95,8 +95,7 @@
     <string name="accessibility_unlock_button" msgid="128158454631118828">"ଅନଲକ୍‌ କରନ୍ତୁ"</string>
     <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ପାଇଁ ଅପେକ୍ଷା କରିଛି"</string>
     <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"ଆଙ୍ଗୁଠିଚିହ୍ନ ବ୍ୟବହାର ନକରି ଅନଲକ୍‍ କରନ୍ତୁ"</string>
-    <!-- no translation found for accessibility_scanning_face (769545173211758586) -->
-    <skip />
+    <string name="accessibility_scanning_face" msgid="769545173211758586">"ଫେସ୍ ସ୍କାନିଙ୍ଗ କରାଯାଉଛି"</string>
     <string name="accessibility_send_smart_reply" msgid="7766727839703044493">"ପଠାନ୍ତୁ"</string>
     <string name="unlock_label" msgid="8779712358041029439">"ଅନଲକ୍‌"</string>
     <string name="phone_label" msgid="2320074140205331708">"ଫୋନ୍‌ ଖୋଲନ୍ତୁ"</string>
@@ -151,16 +150,13 @@
     <string name="data_connection_gprs" msgid="7652872568358508452">"GPRS"</string>
     <string name="data_connection_hspa" msgid="1499615426569473562">"HSPA"</string>
     <string name="data_connection_3g" msgid="503045449315378373">"3G"</string>
-    <!-- no translation found for data_connection_3_5g (3164370985817123144) -->
-    <skip />
-    <!-- no translation found for data_connection_3_5g_plus (4464630787664529264) -->
-    <skip />
+    <string name="data_connection_3_5g" msgid="3164370985817123144">"H"</string>
+    <string name="data_connection_3_5g_plus" msgid="4464630787664529264">"H+"</string>
     <string name="data_connection_4g" msgid="9139963475267449144">"4G"</string>
     <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>
-    <!-- no translation found for data_connection_cdma (8176597308239086780) -->
-    <skip />
+    <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>
@@ -526,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ଆପଣ ନିଜ ୱର୍କ ପ୍ରୋଫାଇଲ୍‌ ବ୍ୟବହାର କରୁଛନ୍ତି"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"କଲ୍ କରନ୍ତୁ"</string>
     <string name="stream_system" msgid="7493299064422163147">"ସିଷ୍ଟମ୍‌"</string>
     <string name="stream_ring" msgid="8213049469184048338">"ରିଙ୍ଗ"</string>
@@ -857,6 +852,5 @@
     <string name="auto_saver_enabled_text" msgid="874711029884777579">"ଚାର୍ଜ <xliff:g id="PERCENTAGE">%d</xliff:g>%%ରୁ କମ୍‌ ହେଲେ ବ୍ୟାଟେରୀ ସେଭର୍‌ ଆପେ ଅନ୍‌ ହୋଇଯିବ।"</string>
     <string name="open_saver_setting_action" msgid="8314624730997322529">"ସେଟିଙ୍ଗ"</string>
     <string name="auto_saver_okay_action" msgid="2701221740227683650">"ବୁଝିଲି"</string>
-    <!-- no translation found for heap_dump_tile_name (9141031328971226374) -->
-    <skip />
+    <string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI ହିପ୍ ଡମ୍ପ୍ କରନ୍ତୁ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings_car.xml b/packages/SystemUI/res/values-or/strings_car.xml
index 2dfa37d..aedec96 100644
--- a/packages/SystemUI/res/values-or/strings_car.xml
+++ b/packages/SystemUI/res/values-or/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"ଅତିଥି"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"ୟୁଜର୍‍ଙ୍କୁ ଯୋଡ଼ନ୍ତୁ"</string>
     <string name="car_new_user" msgid="8142927244990323906">"ନୂଆ ୟୁଜର୍‍"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"ଯେତେବେଳେ ଆପଣ ଜଣେ ନୂଆ ୟୁଜର୍‍ଙ୍କୁ ଯୋଡ଼ିବେ, ସେହି ବ୍ୟକ୍ତିଙ୍କୁ ନିଜ ପାଇଁ ସ୍ପେସ୍‍ ସେଟ‍ଅପ୍ କରିବାକୁ ପଡ଼ିବ।"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index a797fbc..f4be71c 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ਤੁਸੀਂ ਆਪਣੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਰਤ ਰਹੇ ਹੋ"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"ਕਾਲ ਕਰੋ"</string>
     <string name="stream_system" msgid="7493299064422163147">"ਸਿਸਟਮ"</string>
     <string name="stream_ring" msgid="8213049469184048338">"ਘੰਟੀ ਵਜਾਓ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings_car.xml b/packages/SystemUI/res/values-pa/strings_car.xml
index 5da8376..e114144 100644
--- a/packages/SystemUI/res/values-pa/strings_car.xml
+++ b/packages/SystemUI/res/values-pa/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"ਮਹਿਮਾਨ"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="car_new_user" msgid="8142927244990323906">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"ਜਦੋਂ ਤੁਸੀਂ ਇੱਕ ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰਦੇ ਹੋ, ਤਾਂ ਉਸ ਵਿਅਕਤੀ ਨੂੰ ਆਪਣੀ ਜਗ੍ਹਾ ਸੈੱਟਅੱਪ ਕਰਨ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ।"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 6f0c064..6e2f664 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -528,7 +528,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ukryć <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Pojawi się ponownie, gdy następnym włączysz go w ustawieniach."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ukryj"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Używasz profilu do pracy"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Połączenie"</string>
     <string name="stream_system" msgid="7493299064422163147">"System"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Dzwonek"</string>
diff --git a/packages/SystemUI/res/values-pl/strings_car.xml b/packages/SystemUI/res/values-pl/strings_car.xml
index 807eeab..c6f4c4d 100644
--- a/packages/SystemUI/res/values-pl/strings_car.xml
+++ b/packages/SystemUI/res/values-pl/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Gość"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Dodaj użytkownika"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Nowy użytkownik"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Gdy dodasz nowego użytkownika, musi on skonfigurować swój profil."</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 895cab4..a4f46a0 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -524,7 +524,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ela reaparecerá na próxima vez que você ativá-la nas configurações."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Você está usando seu perfil de trabalho"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Ligar"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Tocar"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings_car.xml b/packages/SystemUI/res/values-pt-rBR/strings_car.xml
index fa23b58..5e6bab7 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings_car.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Visitante"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Adicionar usuário"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Novo usuário"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Quando você adiciona um usuário novo, essa pessoa precisa configurar o espaço dela."</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 8a6b170..58b0315 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Pretende ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Reaparecerá da próxima vez que a funcionalidade for ativada nas definições."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Está a utilizar o seu perfil de trabalho"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Chamada"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Toque"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings_car.xml b/packages/SystemUI/res/values-pt-rPT/strings_car.xml
index 9ea0e7a..5850c50 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings_car.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Convidado"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Adicionar utilizador"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Novo utilizador"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Ao adicionar um novo utilizador, essa pessoa tem de configurar o respetivo espaço."</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 895cab4..a4f46a0 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -524,7 +524,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ela reaparecerá na próxima vez que você ativá-la nas configurações."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Você está usando seu perfil de trabalho"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Ligar"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Tocar"</string>
diff --git a/packages/SystemUI/res/values-pt/strings_car.xml b/packages/SystemUI/res/values-pt/strings_car.xml
index fa23b58..5e6bab7 100644
--- a/packages/SystemUI/res/values-pt/strings_car.xml
+++ b/packages/SystemUI/res/values-pt/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Visitante"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Adicionar usuário"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Novo usuário"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Quando você adiciona um usuário novo, essa pessoa precisa configurar o espaço dela."</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 3d68c6f..34baa8a 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -527,7 +527,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ascundeți <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Va reapărea la următoarea activare în setări."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ascundeți"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Acum folosiți profilul de serviciu"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Apel"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Sonerie"</string>
diff --git a/packages/SystemUI/res/values-ro/strings_car.xml b/packages/SystemUI/res/values-ro/strings_car.xml
index c9f0132..41713d1 100644
--- a/packages/SystemUI/res/values-ro/strings_car.xml
+++ b/packages/SystemUI/res/values-ro/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Invitat"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Adăugați un utilizator"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Utilizator nou"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Când adăugați un utilizator nou, acesta trebuie să-și configureze spațiul."</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 18c6221..186baca 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -530,7 +530,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Вы перешли в рабочий профиль"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Вызов"</string>
     <string name="stream_system" msgid="7493299064422163147">"Система"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Звонок"</string>
diff --git a/packages/SystemUI/res/values-ru/strings_car.xml b/packages/SystemUI/res/values-ru/strings_car.xml
index 63aefb0..f8418ac 100644
--- a/packages/SystemUI/res/values-ru/strings_car.xml
+++ b/packages/SystemUI/res/values-ru/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Гость"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Добавить пользователя"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Новый пользователь"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Когда вы добавите пользователя, ему потребуется настроить профиль."</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index b84082d..a34cd9d 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ඔබ ඔබේ කාර්යාල පැතිකඩ භාවිත කරමින් සිටී"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"අමතන්න"</string>
     <string name="stream_system" msgid="7493299064422163147">"පද්ධතිය"</string>
     <string name="stream_ring" msgid="8213049469184048338">"නාද කරන්න"</string>
diff --git a/packages/SystemUI/res/values-si/strings_car.xml b/packages/SystemUI/res/values-si/strings_car.xml
index 24b8e49..656696c 100644
--- a/packages/SystemUI/res/values-si/strings_car.xml
+++ b/packages/SystemUI/res/values-si/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"අමුත්තා"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"පරිශීලක එක් කරන්න"</string>
     <string name="car_new_user" msgid="8142927244990323906">"නව පරිශීලක"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"ඔබ අලුත් පරිශීලකයෙක් එක් කරන විට, එම පුද්ගලයාට තමන්ගේ ඉඩ සකසා ගැනීමට අවශ්‍ය වේ."</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index e751dba..e2fa577 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -530,7 +530,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Skryť <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Táto položka sa znova zobrazí, keď ju v nastaveniach opätovne zapnete."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skryť"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Používate svoj pracovný profil."</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Zavolať"</string>
     <string name="stream_system" msgid="7493299064422163147">"Systém"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Zvonenie"</string>
diff --git a/packages/SystemUI/res/values-sk/strings_car.xml b/packages/SystemUI/res/values-sk/strings_car.xml
index 550fecd..2d5c2ba 100644
--- a/packages/SystemUI/res/values-sk/strings_car.xml
+++ b/packages/SystemUI/res/values-sk/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Hosť"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Pridať používateľa"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Nový používateľ"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Keď pridáte nového používateľa, musí si nastaviť vlastný priestor."</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 2478309..3114f10 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -530,7 +530,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite skriti <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Znova se bo pojavila, ko jo naslednjič vklopite v nastavitvah."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skrij"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Uporabljate delovni profil"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Klic"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Sprožitev zvonjenja"</string>
diff --git a/packages/SystemUI/res/values-sl/strings_car.xml b/packages/SystemUI/res/values-sl/strings_car.xml
index c67d11e..bb122b4 100644
--- a/packages/SystemUI/res/values-sl/strings_car.xml
+++ b/packages/SystemUI/res/values-sl/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Gost"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Dodaj uporabnika"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Nov uporabnik"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Ko dodate novega uporabnika, mora ta nastaviti svoj prostor."</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 290e812..7ad3fa3 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Të fshihet <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Do të rishfaqet herën tjetër kur ta aktivizoni te cilësimet."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Fshih"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Po përdor profilin tënd të punës"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Telefono"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistemi"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Bjeri ziles"</string>
diff --git a/packages/SystemUI/res/values-sq/strings_car.xml b/packages/SystemUI/res/values-sq/strings_car.xml
index e622e60..7dc7dd5 100644
--- a/packages/SystemUI/res/values-sq/strings_car.xml
+++ b/packages/SystemUI/res/values-sq/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"I ftuar"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Shto përdorues"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Përdorues i ri"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Kur shton një përdorues të ri, ai person duhet të konfigurojë hapësirën e vet."</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 07e0245..da30392 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -525,7 +525,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Користите профил за Work"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Позив"</string>
     <string name="stream_system" msgid="7493299064422163147">"Систем"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Звоно"</string>
diff --git a/packages/SystemUI/res/values-sr/strings_car.xml b/packages/SystemUI/res/values-sr/strings_car.xml
index d25f0e7..801114d 100644
--- a/packages/SystemUI/res/values-sr/strings_car.xml
+++ b/packages/SystemUI/res/values-sr/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Гост"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Додај корисника"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Нови корисник"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Када додате новог корисника, та особа треба да подеси свој простор."</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index de5c82a..0deb4b4 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vill du dölja <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Den visas på nytt nästa gång du aktiverar den i inställningarna."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Dölj"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du använder din jobbprofil"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Samtal"</string>
     <string name="stream_system" msgid="7493299064422163147">"System"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Ringsignal"</string>
diff --git a/packages/SystemUI/res/values-sv/strings_car.xml b/packages/SystemUI/res/values-sv/strings_car.xml
index c758641..fb801ad 100644
--- a/packages/SystemUI/res/values-sv/strings_car.xml
+++ b/packages/SystemUI/res/values-sv/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Gäst"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Lägg till användare"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Ny användare"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"När du lägger till en ny användare måste den personen konfigurera sitt utrymme."</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index a60c9a4..ef90f3e 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ungependa kuficha <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Itaonekana tena wakati mwingine utakapoiwasha katika mipangilio."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ficha"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Unatumia wasifu wako wa kazini"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Piga simu"</string>
     <string name="stream_system" msgid="7493299064422163147">"Mfumo"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Piga"</string>
@@ -706,7 +705,7 @@
     <string name="nav_bar_layout" msgid="3664072994198772020">"Mpangilio"</string>
     <string name="left_nav_bar_button_type" msgid="8555981238887546528">"Aina ya kitufe cha kushoto cha ziada"</string>
     <string name="right_nav_bar_button_type" msgid="2481056627065649656">"Aina ya kitufe cha kulia cha ziada"</string>
-    <string name="nav_bar_default" msgid="8587114043070993007">"(chaguo msingi)"</string>
+    <string name="nav_bar_default" msgid="8587114043070993007">"(chaguomsingi)"</string>
   <string-array name="nav_bar_buttons">
     <item msgid="1545641631806817203">"Ubao wa kunakili"</item>
     <item msgid="5742013440802239414">"Msimbo wa ufunguo"</item>
@@ -736,12 +735,12 @@
     <string name="tuner_time" msgid="6572217313285536011">"Wakati"</string>
   <string-array name="clock_options">
     <item msgid="5965318737560463480">"Onyesha saa, dakika na sekunde"</item>
-    <item msgid="1427801730816895300">"Onyesha saa na dakika (chaguo msingi)"</item>
+    <item msgid="1427801730816895300">"Onyesha saa na dakika (chaguomsingi)"</item>
     <item msgid="3830170141562534721">"Usionyeshe aikoni hii"</item>
   </string-array>
   <string-array name="battery_options">
     <item msgid="3160236755818672034">"Onyesha asilimia kila wakati"</item>
-    <item msgid="2139628951880142927">"Onyesha asilimia wakati inachaji (chaguo msingi)"</item>
+    <item msgid="2139628951880142927">"Onyesha asilimia wakati inachaji (chaguomsingi)"</item>
     <item msgid="3327323682209964956">"Usionyeshe aikoni hii"</item>
   </string-array>
     <string name="other" msgid="4060683095962566764">"Nyingine"</string>
diff --git a/packages/SystemUI/res/values-sw/strings_car.xml b/packages/SystemUI/res/values-sw/strings_car.xml
index c4b3dcc..bf7fb5b 100644
--- a/packages/SystemUI/res/values-sw/strings_car.xml
+++ b/packages/SystemUI/res/values-sw/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Mgeni"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Ongeza Mtumiaji"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Mtumiaji Mpya"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Ukiongeza mtumiaji mpya, ni lazima aweke kikundi chake."</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 2dc6525..eb5c180 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -17,7 +17,7 @@
 -->
 <resources>
     <!-- Standard notification width + gravity -->
-    <dimen name="notification_panel_width">@dimen/standard_notification_panel_width</dimen>
+    <dimen name="notification_panel_width">416dp</dimen>
 
     <!-- Diameter of outer shape drawable shown in navbar search-->
     <dimen name="navbar_search_outerring_diameter">430dip</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index 791d761..23b64e3 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
     <style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer">
-        <item name="android:layout_width">@dimen/standard_notification_panel_width</item>
+        <item name="android:layout_width">@dimen/notification_panel_width</item>
     </style>
 
     <style name="UserDetailView">
diff --git a/packages/SystemUI/res/values-sw900dp-land/dimen.xml b/packages/SystemUI/res/values-sw900dp-land/dimen.xml
new file mode 100644
index 0000000..ac7e6b8
--- /dev/null
+++ b/packages/SystemUI/res/values-sw900dp-land/dimen.xml
@@ -0,0 +1,26 @@
+<?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.
+*/
+-->
+<resources>
+    <!-- Standard notification width + gravity for tablet large screen device -->
+    <dimen name="notification_panel_width">544dp</dimen>
+
+    <!-- Maximum width of quick quick settings panel. -->
+    <dimen name="qs_quick_layout_width">478dp</dimen>
+
+</resources>
+
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 31eae85..b1eeb41 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"பணி சுயவிவரத்தைப் பயன்படுத்துகிறீர்கள்"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"அழைப்பு"</string>
     <string name="stream_system" msgid="7493299064422163147">"சிஸ்டம்"</string>
     <string name="stream_ring" msgid="8213049469184048338">"ரிங் செய்"</string>
diff --git a/packages/SystemUI/res/values-ta/strings_car.xml b/packages/SystemUI/res/values-ta/strings_car.xml
index 0187c94..731c275 100644
--- a/packages/SystemUI/res/values-ta/strings_car.xml
+++ b/packages/SystemUI/res/values-ta/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"விருந்தினர்"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"பயனரைச் சேர்க்கவும்"</string>
     <string name="car_new_user" msgid="8142927244990323906">"புதிய பயனர்"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"புதிய பயனரைச் சேர்க்கும்போது, அவர் தனக்கான இடத்தை அமைக்க வேண்டும்."</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index b1ed350..20a146e 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"మీరు మీ కార్యాలయ ప్రొఫైల్‌ను ఉపయోగిస్తున్నారు"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"కాల్"</string>
     <string name="stream_system" msgid="7493299064422163147">"సిస్టమ్"</string>
     <string name="stream_ring" msgid="8213049469184048338">"రింగ్"</string>
diff --git a/packages/SystemUI/res/values-te/strings_car.xml b/packages/SystemUI/res/values-te/strings_car.xml
index 36afe5d..ec7a779 100644
--- a/packages/SystemUI/res/values-te/strings_car.xml
+++ b/packages/SystemUI/res/values-te/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"అతిథి"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"వినియోగదారును జోడించండి"</string>
     <string name="car_new_user" msgid="8142927244990323906">"కొత్త వినియోగదారు"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"మీరు కొత్త వినియోగదారును జోడించినప్పుడు, ఆ వ్యక్తి తన స్థలాన్ని సెటప్ చేసుకోవాలి."</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index e7a98b6..4616996 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -57,7 +57,7 @@
     <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"อนุญาตให้ <xliff:g id="APPLICATION">%1$s</xliff:g> เข้าถึง <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ไหม"</string>
     <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"เปิด <xliff:g id="APPLICATION">%1$s</xliff:g> เพื่อจัดการ <xliff:g id="USB_DEVICE">%2$s</xliff:g> ไหม"</string>
     <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"เปิด <xliff:g id="APPLICATION">%1$s</xliff:g> เพื่อจัดการ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ไหม"</string>
-    <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"แอปพลิเคชันที่ติดตั้งใช้กับอุปกรณ์ USB นี้ไม่ได้ เรียนรู้เพิ่มเติมเกี่ยวกับอุปกรณ์เสริมนี้ที่ <xliff:g id="URL">%1$s</xliff:g>"</string>
+    <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"แอปพลิเคชันที่ติดตั้งใช้กับอุปกรณ์ USB นี้ไม่ได้ ดูข้อมูลเพิ่มเติมเกี่ยวกับอุปกรณ์เสริมนี้ที่ <xliff:g id="URL">%1$s</xliff:g>"</string>
     <string name="title_usb_accessory" msgid="4966265263465181372">"อุปกรณ์เสริม USB"</string>
     <string name="label_view" msgid="6304565553218192990">"ดู"</string>
     <string name="always_use_device" msgid="4015357883336738417">"เปิด <xliff:g id="APPLICATION">%1$s</xliff:g> ทุกครั้งเมื่อเชื่อมต่อกับ <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
@@ -85,7 +85,7 @@
     <string name="accessibility_back" msgid="567011538994429120">"กลับ"</string>
     <string name="accessibility_home" msgid="8217216074895377641">"หน้าแรก"</string>
     <string name="accessibility_menu" msgid="316839303324695949">"เมนู"</string>
-    <string name="accessibility_accessibility_button" msgid="7601252764577607915">"ความสามารถเข้าถึงได้ง่าย"</string>
+    <string name="accessibility_accessibility_button" msgid="7601252764577607915">"การเข้าถึงพิเศษ"</string>
     <string name="accessibility_rotate_button" msgid="7402949513740253006">"หมุนหน้าจอ"</string>
     <string name="accessibility_recent" msgid="5208608566793607626">"ภาพรวม"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"ค้นหา"</string>
@@ -480,7 +480,7 @@
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ใช้ <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> เพื่อจัดการอุปกรณ์"</string>
     <string name="monitoring_description_do_body" msgid="3639594537660975895">"ผู้ดูแลระบบสามารถตรวจสอบและจัดการการตั้งค่า การเข้าถึงของบริษัท แอป ข้อมูลที่เชื่อมโยงกับอุปกรณ์ของคุณ และข้อมูลตำแหน่งของอุปกรณ์"</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
-    <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"เรียนรู้เพิ่มเติม"</string>
+    <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"ดูข้อมูลเพิ่มเติม"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"คุณเชื่อมต่อกับ <xliff:g id="VPN_APP">%1$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายของคุณ รวมถึงอีเมล แอป และเว็บไซต์ได้"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="6434859242636063861">"เปิดการตั้งค่า VPN"</string>
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"คุณกำลังใช้โปรไฟล์งานของคุณ"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"การโทร"</string>
     <string name="stream_system" msgid="7493299064422163147">"ระบบ"</string>
     <string name="stream_ring" msgid="8213049469184048338">"เสียงเรียกเข้า"</string>
diff --git a/packages/SystemUI/res/values-th/strings_car.xml b/packages/SystemUI/res/values-th/strings_car.xml
index 92cc60b..17f9b8f 100644
--- a/packages/SystemUI/res/values-th/strings_car.xml
+++ b/packages/SystemUI/res/values-th/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"ผู้มาเยือน"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"เพิ่มผู้ใช้"</string>
     <string name="car_new_user" msgid="8142927244990323906">"ผู้ใช้ใหม่"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"เมื่อคุณเพิ่มผู้ใช้ใหม่ ผู้ใช้ดังกล่าวจะต้องตั้งค่าพื้นที่ของตนเอง"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 7fdee62..0c1b8a5 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Itago ang <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Lalabas itong muli sa susunod na pagkakataon na i-on mo ito sa mga setting."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Itago"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Ginagamit mo ang iyong profile sa trabaho"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Tumawag"</string>
     <string name="stream_system" msgid="7493299064422163147">"System"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Ipa-ring"</string>
diff --git a/packages/SystemUI/res/values-tl/strings_car.xml b/packages/SystemUI/res/values-tl/strings_car.xml
index 19e60a5..0707b2c 100644
--- a/packages/SystemUI/res/values-tl/strings_car.xml
+++ b/packages/SystemUI/res/values-tl/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Bisita"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Magdagdag ng User"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Bagong User"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Kapag nagdagdag ka ng bagong user, kailangang i-set up ng taong iyon ang kanyang espasyo."</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index d537a09..7abf64f 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> gizlensin mi?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ayarlardan etkinleştirdiğiniz bir sonraki sefer tekrar görünür."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Gizle"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"İş profilinizi kullanıyorsunuz"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Çağrı"</string>
     <string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Zili çaldır"</string>
diff --git a/packages/SystemUI/res/values-tr/strings_car.xml b/packages/SystemUI/res/values-tr/strings_car.xml
index f54498b..6dc7e5d 100644
--- a/packages/SystemUI/res/values-tr/strings_car.xml
+++ b/packages/SystemUI/res/values-tr/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Misafir"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Kullanıcı Ekle"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Yeni Kullanıcı"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Yeni kullanıcı eklediğinizde, bu kişinin kendi alanını ayarlaması gerekir."</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 14ff120..f9f5ce8 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -530,7 +530,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Ви в робочому профілі"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Виклик"</string>
     <string name="stream_system" msgid="7493299064422163147">"Система"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Дзвінок"</string>
diff --git a/packages/SystemUI/res/values-uk/strings_car.xml b/packages/SystemUI/res/values-uk/strings_car.xml
index c5e8608..2d36a4b 100644
--- a/packages/SystemUI/res/values-uk/strings_car.xml
+++ b/packages/SystemUI/res/values-uk/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Гість"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Додати користувача"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Новий користувач"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Коли ви додаєте нового користувача, він має налаштувати свій профіль."</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index b4aa388..0566022 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"آپ اپنا دفتری پروفائل استعمال کر رہے ہیں۔"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"کال"</string>
     <string name="stream_system" msgid="7493299064422163147">"سسٹم"</string>
     <string name="stream_ring" msgid="8213049469184048338">"رِنگ"</string>
diff --git a/packages/SystemUI/res/values-ur/strings_car.xml b/packages/SystemUI/res/values-ur/strings_car.xml
index eabff5b..6a85f56 100644
--- a/packages/SystemUI/res/values-ur/strings_car.xml
+++ b/packages/SystemUI/res/values-ur/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"مہمان"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"صارف شامل کریں"</string>
     <string name="car_new_user" msgid="8142927244990323906">"نیا صارف"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"جب آپ ایک نیا صارف شامل کرتے ہیں تو اس شخص کو اپنی جگہ کو ترتیب دینے کی ضرورت ہوتی ہے۔"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 328faa5..5ce50a3 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -524,7 +524,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> berkitilsinmi?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Keyingi safar sozlamalardan yoqilgan paydo bo‘ladi."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Berkitish"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Siz ishchi profildan foydalanmoqdasiz"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Chaqiruv"</string>
     <string name="stream_system" msgid="7493299064422163147">"Tizim"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Jiringlatish"</string>
diff --git a/packages/SystemUI/res/values-uz/strings_car.xml b/packages/SystemUI/res/values-uz/strings_car.xml
index 656e7b3..ad4faf6 100644
--- a/packages/SystemUI/res/values-uz/strings_car.xml
+++ b/packages/SystemUI/res/values-uz/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Mehmon"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Foydalanuvchi qo‘shish"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Yangi foydalanuvchi"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Yangi profil qo‘shilgach, uni sozlash lozim."</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index d6e9b0a..3f0091f 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ẩn <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Thông báo này sẽ xuất hiện lại vào lần tiếp theo bạn bật thông báo trong cài đặt."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ẩn"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Bạn đang sử dụng hồ sơ công việc của mình"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Gọi"</string>
     <string name="stream_system" msgid="7493299064422163147">"Hệ thống"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Chuông"</string>
diff --git a/packages/SystemUI/res/values-vi/strings_car.xml b/packages/SystemUI/res/values-vi/strings_car.xml
index 08aece1..ad9bc9e 100644
--- a/packages/SystemUI/res/values-vi/strings_car.xml
+++ b/packages/SystemUI/res/values-vi/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Khách"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Thêm người dùng"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Người dùng mới"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Khi bạn thêm một người dùng mới, người đó cần thiết lập không gian của họ."</string>
diff --git a/packages/SystemUI/res/values-w550dp-land/dimens.xml b/packages/SystemUI/res/values-w550dp-land/dimens.xml
index eaca9d7..2c66454 100644
--- a/packages/SystemUI/res/values-w550dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-w550dp-land/dimens.xml
@@ -18,4 +18,7 @@
 <resources>
     <!-- Standard notification width + gravity -->
     <dimen name="notification_panel_width">544dp</dimen>
+
+    <!-- Maximum width of quick quick settings panel. -->
+    <dimen name="qs_quick_layout_width">478dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 7d40962..12675b0 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"您当前正在使用工作资料"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"通话"</string>
     <string name="stream_system" msgid="7493299064422163147">"系统"</string>
     <string name="stream_ring" msgid="8213049469184048338">"铃声"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings_car.xml b/packages/SystemUI/res/values-zh-rCN/strings_car.xml
index e29397a..8dbb846 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings_car.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"访客"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"添加用户"</string>
     <string name="car_new_user" msgid="8142927244990323906">"新用户"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"当您添加新用户时,该用户必须设置自己的空间。"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 3ea0272..8f9b33f 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -524,7 +524,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"您正在使用工作設定檔"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"通話"</string>
     <string name="stream_system" msgid="7493299064422163147">"系統"</string>
     <string name="stream_ring" msgid="8213049469184048338">"鈴聲"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings_car.xml b/packages/SystemUI/res/values-zh-rHK/strings_car.xml
index b247dd6..ea8836f 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings_car.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"訪客"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"新增使用者"</string>
     <string name="car_new_user" msgid="8142927244990323906">"新使用者"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"新增的使用者需要自行設定個人空間。"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 6b74a2a..2672138 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -522,7 +522,6 @@
     <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>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"你正在使用工作資料夾"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"通話"</string>
     <string name="stream_system" msgid="7493299064422163147">"系統"</string>
     <string name="stream_ring" msgid="8213049469184048338">"鈴響"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings_car.xml b/packages/SystemUI/res/values-zh-rTW/strings_car.xml
index 6772d37..2779c7b 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings_car.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"訪客"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"新增使用者"</string>
     <string name="car_new_user" msgid="8142927244990323906">"新使用者"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"新增的使用者需要自行設定個人空間。"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 1835d82..099cbfe 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -522,7 +522,6 @@
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Fihla i-<xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Izovela ngesikhathi esilandelayo uma uvule lesi silungiselelo."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Fihla"</string>
-    <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Usebenzisa iphrofayela yakho yomsebenzi"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"Shaya"</string>
     <string name="stream_system" msgid="7493299064422163147">"Isistimu"</string>
     <string name="stream_ring" msgid="8213049469184048338">"Khalisa"</string>
diff --git a/packages/SystemUI/res/values-zu/strings_car.xml b/packages/SystemUI/res/values-zu/strings_car.xml
index f922f46..dc0fec2 100644
--- a/packages/SystemUI/res/values-zu/strings_car.xml
+++ b/packages/SystemUI/res/values-zu/strings_car.xml
@@ -20,6 +20,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="car_guest" msgid="3738772168718508650">"Isivakashi"</string>
+    <!-- no translation found for start_guest_session (7055742120180595689) -->
+    <skip />
     <string name="car_add_user" msgid="5245196248349230898">"Engeza umsebenzisi"</string>
     <string name="car_new_user" msgid="8142927244990323906">"Umsebenzisi omusha"</string>
     <string name="user_add_user_message_setup" msgid="1791011504259527329">"Uma ungeza umsebenzisi omusha, loyo muntu udinga ukusetha izikhala zakhe."</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 3f63f22..f50ef82 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -143,5 +143,15 @@
     <attr name="rotateButtonEndAngle" format="float" />
     <attr name="rotateButtonScaleX" format="float" />
 
+    <!-- Used to style charging animation AVD animation -->
+    <attr name="chargingAnimColor" format="color" />
+
+    <!-- Used display CarrierText in Keyguard or QS Footer -->
+    <declare-styleable name="CarrierText">
+        <attr name="allCaps" format="boolean" />
+        <attr name="showMissingSim" format="boolean" />
+        <attr name="showAirplaneMode" format="boolean" />
+    </declare-styleable>
+
 </resources>
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 975055c..4920fb2 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -94,6 +94,9 @@
     <!-- The color of the gear shown behind a notification -->
     <color name="notification_gear_color">#ff757575</color>
 
+    <!-- The color of the text inside a notification -->
+    <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_light</color>
+
     <!-- The "inside" of a notification, reached via longpress -->
     <color name="notification_guts_bg_color">#f8f9fa</color>
 
@@ -137,8 +140,10 @@
 
     <color name="remote_input_accent">#eeeeee</color>
 
-    <color name="quick_step_track_background_dark">#61000000</color>
-    <color name="quick_step_track_background_light">#33FFFFFF</color>
+    <color name="quick_step_track_background_background_dark">#1F000000</color>
+    <color name="quick_step_track_background_background_light">#33FFFFFF</color>
+    <color name="quick_step_track_background_foreground_dark">#38000000</color>
+    <color name="quick_step_track_background_foreground_light">#59FFFFFF</color>
 
     <!-- Keyboard shortcuts colors -->
     <color name="ksh_application_group_color">#fff44336</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 3b1872a..19492a0 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -104,6 +104,9 @@
          prevent wasting cpu cycles on the dimming animation -->
     <bool name="config_navigation_bar_enable_auto_dim_no_visible_wallpaper">true</bool>
 
+    <!-- The maximum number of tiles in the QuickQSPanel -->
+    <integer name="quick_qs_panel_max_columns">6</integer>
+
     <!-- Whether QuickSettings is in a phone landscape -->
     <bool name="quick_settings_wide">false</bool>
 
@@ -351,7 +354,7 @@
         <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.biometrics.BiometricDialogImpl</item>
         <item>com.android.systemui.SliceBroadcastRelayHandler</item>
     </string-array>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f00957a..ca6b2d9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -55,7 +55,7 @@
     <dimen name="status_bar_left_clock_end_padding">7dp</dimen>
 
     <!-- Spacing after the wifi signals that is present if there are any icons following it. -->
-    <dimen name="status_bar_wifi_signal_spacer_width">4dp</dimen>
+    <dimen name="status_bar_wifi_signal_spacer_width">2.5dp</dimen>
 
     <!-- Spacing before the airplane mode icon if there are any icons preceding it. -->
     <dimen name="status_bar_airplane_spacer_width">4dp</dimen>
@@ -236,6 +236,9 @@
     <!-- Height of search panel including navigation bar height -->
     <dimen name="navbar_search_panel_height">230dip</dimen>
 
+    <!-- Move the back button drawable for 3 button layout upwards in ime mode and in portrait -->
+    <dimen name="navbar_back_button_ime_offset">2dp</dimen>
+
     <!-- Height of the draggable handle at the bottom of the phone notification panel -->
     <dimen name="close_handle_height">36dp</dimen>
 
@@ -252,6 +255,9 @@
          -->
     <dimen name="qs_header_system_icons_area_height">48dp</dimen>
 
+    <!-- How far the quick-quick settings panel extends below the status bar -->
+    <dimen name="qs_quick_header_panel_height">128dp</dimen>
+
     <!-- The height of the container that holds the system icons in the quick settings header in the
          car setting. -->
     <dimen name="car_qs_header_system_icons_area_height">54dp</dimen>
@@ -277,7 +283,6 @@
 
     <!-- Width for the notification panel and related windows -->
     <dimen name="match_parent">-1px</dimen>
-    <dimen name="standard_notification_panel_width">416dp</dimen>
 
     <!-- The top margin of the panel that holds the list of notifications. -->
     <dimen name="notification_panel_margin_top">0dp</dimen>
@@ -349,8 +354,8 @@
     <dimen name="qs_tile_margin_top_bottom">12dp</dimen>
     <dimen name="qs_tile_margin_top">18dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
-    <!-- Width for the spacer, used between QS tiles. -->
-    <dimen name="qs_quick_tile_space_width">0dp</dimen>
+    <!-- Maximum width of quick quick settings panel. Defaults to MATCH_PARENT-->
+    <dimen name="qs_quick_layout_width">-1px</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
     <dimen name="qs_header_gear_translation">16dp</dimen>
     <dimen name="qs_header_tile_margin_horizontal">0dp</dimen>
@@ -764,9 +769,6 @@
     <dimen name="volume_expander_margin_end">2dp</dimen>
     <dimen name="volume_expander_margin_top">6dp</dimen>
 
-    <!-- Padding between icon and text for managed profile toast -->
-    <dimen name="managed_profile_toast_padding">4dp</dimen>
-
     <!-- Thickness of the assist disclosure beams -->
     <dimen name="assist_disclosure_thickness">2.5dp</dimen>
 
@@ -922,14 +924,22 @@
 
     <dimen name="global_actions_top_padding">120dp</dimen>
 
-    <!-- the maximum offset in either direction that elements are moved horizontally to prevent
-            burn-in on AOD -->
+    <dimen name="global_actions_padding">12dp</dimen>
+
+    <dimen name="global_actions_translate">9dp</dimen>
+
+    <!-- The maximum offset in either direction that elements are moved horizontally to prevent
+         burn-in on AOD. -->
     <dimen name="burn_in_prevention_offset_x">8dp</dimen>
 
-    <!-- the maximum offset in either direction that elements are moved vertically to prevent
-            burn-in on AOD -->
+    <!-- The maximum offset in either direction that elements are moved vertically to prevent
+         burn-in on AOD. -->
     <dimen name="burn_in_prevention_offset_y">50dp</dimen>
 
+    <!-- The maximum offset in either direction that the charging indication moves vertically
+         to prevent burn-in on AOD. -->
+    <dimen name="charging_indication_burn_in_prevention_offset_y">5dp</dimen>
+
     <dimen name="corner_size">8dp</dimen>
     <dimen name="top_padding">0dp</dimen>
     <dimen name="bottom_padding">48dp</dimen>
@@ -1012,4 +1022,7 @@
     <!-- How much into a DisplayCutout's bounds we can go, on each side -->
     <dimen name="display_cutout_margin_consumption">0px</dimen>
 
+    <!-- How much we expand the touchable region of the status bar below the notch to catch touches
+         that just start below the notch. -->
+    <dimen name="display_cutout_touchable_region_size">12dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 84a76bc..915fc6e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1326,9 +1326,6 @@
     <!-- Hide quick settings tile confirmation button -->
     <string name="quick_settings_reset_confirmation_button">Hide</string>
 
-    <!-- Toast shown when user unlocks screen and managed profile activity is in the foreground -->
-    <string name="managed_profile_foreground_toast">You\'re using your work profile</string>
-
     <!-- volume stream names. All nouns. -->
     <string name="stream_voice_call">Call</string> <!-- STREAM_VOICE_CALL -->
     <string name="stream_system">System</string> <!-- STREAM_SYSTEM -->
diff --git a/packages/SystemUI/res/values/strings_car.xml b/packages/SystemUI/res/values/strings_car.xml
index 61d734f..2890cf2 100644
--- a/packages/SystemUI/res/values/strings_car.xml
+++ b/packages/SystemUI/res/values/strings_car.xml
@@ -19,7 +19,9 @@
 <resources>
     <!-- Name of Guest Profile. [CHAR LIMIT=30] -->
     <string name="car_guest">Guest</string>
-    <!-- Name of Add User Profile. [CHAR LIMIT=30] -->
+    <!-- Title for button that starts a guest session. [CHAR LIMIT=30] -->
+    <string name="start_guest_session">Guest</string>
+    <!-- Title for button that  adds a new user. [CHAR LIMIT=30] -->
     <string name="car_add_user">Add User</string>
     <!-- Default name of the new user created. [CHAR LIMIT=30] -->
     <string name="car_new_user">New User</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e4f5989..c0b50ea 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -335,10 +335,7 @@
         <item name="*android:errorColor">?android:attr/colorError</item>
     </style>
 
-    <!-- Overlay manager may replace this theme -->
-    <style name="qs_base" parent="@*android:style/Theme.DeviceDefault.QuickSettings" />
-
-    <style name="qs_theme" parent="qs_base">
+    <style name="qs_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
         <item name="lightIconTheme">@style/QSIconTheme</item>
         <item name="darkIconTheme">@style/QSIconTheme</item>
         <item name="android:windowIsFloating">true</item>
@@ -403,6 +400,9 @@
         <item name="fillColor">?android:attr/textColorPrimary</item>
         <item name="singleToneColor">?android:attr/textColorPrimary</item>
     </style>
+    <style name="ScreenPinningRequestTheme" parent="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+        <item name="singleToneColor">@color/light_mode_icon_color_single_tone</item>
+    </style>
 
     <style name="TextAppearance.Volume">
         <item name="android:textStyle">normal</item>
@@ -452,23 +452,20 @@
 
     <style name="TextAppearance.NotificationInfo">
         <item name="android:fontFamily">sans-serif</item>
-        <item name="android:textColor">@android:color/black</item>
+        <item name="android:textColor">@color/notification_primary_text_color</item>
     </style>
 
     <style name="TextAppearance.NotificationInfo.Primary">
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:textSize">16sp</item>
         <item name="android:alpha">0.87</item>
     </style>
 
     <style name="TextAppearance.NotificationInfo.Confirmation">
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:textSize">14sp</item>
         <item name="android:alpha">0.87</item>
     </style>
 
     <style name="TextAppearance.NotificationInfo.Secondary">
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:textSize">14sp</item>
         <item name="android:alpha">0.54</item>
     </style>
@@ -498,7 +495,7 @@
            parent="@*android:style/TextAppearance.Material.Notification.Info">
     </style>
 
-    <style name="edit_theme" parent="qs_base">
+    <style name="edit_theme" parent="qs_theme">
         <item name="android:colorBackground">?android:attr/colorSecondary</item>
     </style>
 
@@ -527,4 +524,16 @@
         <item name="rotateButtonScaleX">-1</item>
     </style>
 
+    <!-- Used to style charging animation AVD animation -->
+    <style name="ChargingAnim" />
+
+    <style name="ChargingAnim.WallpaperBackground">
+        <item name="chargingAnimColor">?attr/wallpaperTextColor</item>
+        <item name="android:textColor">?attr/wallpaperTextColor</item>
+    </style>
+
+    <style name="ChargingAnim.DarkBackground">
+        <item name="chargingAnimColor">@android:color/white</item>
+        <item name="android:textColor">@android:color/white</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
index a04a6a3..f69e911 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.KeyguardManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -92,7 +93,7 @@
         ArrayList<Task> allTasks = new ArrayList<>();
         if (mRawTasks == null) {
             mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
-                    ActivityManager.getMaxRecentTasksStatic(), currentUserId);
+                    ActivityTaskManager.getMaxRecentTasksStatic(), currentUserId);
 
             // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
             Collections.reverse(mRawTasks);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
index 1309a60..ab2e277 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
@@ -17,6 +17,7 @@
 package com.android.systemui.shared.recents.model;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.Context;
@@ -98,7 +99,7 @@
         mSvelteLevel = svelteLevel;
 
         // Initialize the proxy, cache and loaders
-        int numRecentTasks = ActivityManager.getMaxRecentTasksStatic();
+        int numRecentTasks = ActivityTaskManager.getMaxRecentTasksStatic();
         mHighResThumbnailLoader = new HighResThumbnailLoader(ActivityManagerWrapper.getInstance(),
                 Looper.getMainLooper(), ActivityManager.isLowRamDeviceStatic());
         mLoadQueue = new TaskResourceLoadQueue();
@@ -221,14 +222,14 @@
                 // We are leaving recents, so trim the data a bit
                 mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
                 mActivityInfoCache.trimToSize(Math.max(1,
-                        ActivityManager.getMaxRecentTasksStatic() / 2));
+                        ActivityTaskManager.getMaxRecentTasksStatic() / 2));
                 break;
             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
             case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
                 // We are going to be low on memory
                 mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
                 mActivityInfoCache.trimToSize(Math.max(1,
-                        ActivityManager.getMaxRecentTasksStatic() / 4));
+                        ActivityTaskManager.getMaxRecentTasksStatic() / 4));
                 break;
             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
             case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java
index 4bf3500..23582d4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java
@@ -34,7 +34,7 @@
      * Gets a specific entry in the cache with the specified key, regardless of whether the cached
      * value is valid or not.
      */
-    final V get(TaskKey key) {
+    final synchronized V get(TaskKey key) {
         return getCacheEntry(key.id);
     }
 
@@ -42,7 +42,7 @@
      * Returns the value only if the key is valid (has not been updated since the last time it was
      * in the cache)
      */
-    final V getAndInvalidateIfModified(TaskKey key) {
+    final synchronized V getAndInvalidateIfModified(TaskKey key) {
         TaskKey lastKey = mKeys.get(key.id);
         if (lastKey != null) {
             if ((lastKey.windowingMode != key.windowingMode) ||
@@ -59,7 +59,7 @@
     }
 
     /** Puts an entry in the cache for a specific key. */
-    final void put(TaskKey key, V value) {
+    final synchronized void put(TaskKey key, V value) {
         if (key == null || value == null) {
             Log.e(TAG, "Unexpected null key or value: " + key + ", " + value);
             return;
@@ -70,14 +70,14 @@
 
 
     /** Removes a cache entry for a specific key. */
-    final void remove(TaskKey key) {
+    final synchronized void remove(TaskKey key) {
         // Remove the key after the cache value because we need it to make the callback
         removeCacheEntry(key.id);
         mKeys.remove(key.id);
     }
 
     /** Removes all the entries in the cache. */
-    final void evictAll() {
+    final synchronized void evictAll() {
         evictAllCache();
         mKeys.clear();
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 63a4cd4..0ded963 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -30,6 +30,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.RecentTaskInfo;
 import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
 import android.app.AppGlobals;
 import android.app.IAssistDataReceiver;
 import android.app.WindowConfiguration.ActivityType;
@@ -113,7 +114,7 @@
         // Note: The set of running tasks from the system is ordered by recency
         try {
             List<ActivityManager.RunningTaskInfo> tasks =
-                    ActivityManager.getService().getFilteredTasks(1, ignoreActivityType,
+                    ActivityTaskManager.getService().getFilteredTasks(1, ignoreActivityType,
                             WINDOWING_MODE_PINNED /* ignoreWindowingMode */);
             if (tasks.isEmpty()) {
                 return null;
@@ -129,7 +130,7 @@
      */
     public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
         try {
-            return ActivityManager.getService().getRecentTasks(numTasks,
+            return ActivityTaskManager.getService().getRecentTasks(numTasks,
                             RECENT_IGNORE_UNAVAILABLE, userId).getList();
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to get recent tasks", e);
@@ -143,7 +144,7 @@
     public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean reducedResolution) {
         ActivityManager.TaskSnapshot snapshot = null;
         try {
-            snapshot = ActivityManager.getService().getTaskSnapshot(taskId, reducedResolution);
+            snapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId, reducedResolution);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to retrieve task snapshot", e);
         }
@@ -234,7 +235,7 @@
                     }
                 };
             }
-            ActivityManager.getService().startRecentsActivity(intent, receiver, runner);
+            ActivityTaskManager.getService().startRecentsActivity(intent, receiver, runner);
             if (resultCallback != null) {
                 resultCallbackHandler.post(new Runnable() {
                     @Override
@@ -260,7 +261,7 @@
      */
     public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
         try {
-            ActivityManager.getService().cancelRecentsAnimation(restoreHomeStackPosition);
+            ActivityTaskManager.getService().cancelRecentsAnimation(restoreHomeStackPosition);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to cancel recents animation", e);
         }
@@ -333,7 +334,7 @@
     public boolean startActivityFromRecents(int taskId, ActivityOptions options) {
         try {
             Bundle optsBundle = options == null ? null : options.toBundle();
-            ActivityManager.getService().startActivityFromRecents(taskId, optsBundle);
+            ActivityTaskManager.getService().startActivityFromRecents(taskId, optsBundle);
             return true;
         } catch (Exception e) {
             return false;
@@ -384,7 +385,7 @@
             @Override
             public void run() {
                 try {
-                    ActivityManager.getService().removeTask(taskId);
+                    ActivityTaskManager.getService().removeTask(taskId);
                 } catch (RemoteException e) {
                     Log.w(TAG, "Failed to remove task=" + taskId, e);
                 }
@@ -393,11 +394,27 @@
     }
 
     /**
+     * Removes all the recent tasks.
+     */
+    public void removeAllRecentTasks() {
+        mBackgroundExecutor.submit(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    ActivityTaskManager.getService().removeAllVisibleRecentTasks();
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to remove all tasks", e);
+                }
+            }
+        });
+    }
+
+    /**
      * Cancels the current window transtion to/from Recents for the given task id.
      */
     public void cancelWindowTransition(int taskId) {
         try {
-            ActivityManager.getService().cancelTaskWindowTransition(taskId);
+            ActivityTaskManager.getService().cancelTaskWindowTransition(taskId);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to cancel window transition for task=" + taskId, e);
         }
@@ -408,7 +425,7 @@
      */
     public boolean isScreenPinningActive() {
         try {
-            return ActivityManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED;
+            return ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED;
         } catch (RemoteException e) {
             return false;
         }
@@ -427,7 +444,7 @@
      */
     public boolean isLockToAppActive() {
         try {
-            return ActivityManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+            return ActivityTaskManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE;
         } catch (RemoteException e) {
             return false;
         }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 712cca6..36fb3a7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.shared.system;
 
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 
 import android.app.ActivityOptions;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/DockedStackListenerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/DockedStackListenerCompat.java
new file mode 100644
index 0000000..bb319e6
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/DockedStackListenerCompat.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.systemui.shared.system;
+
+import android.view.IDockedStackListener;
+
+/**
+ * An interface to track docked stack changes.
+ */
+public class DockedStackListenerCompat {
+
+    IDockedStackListener.Stub mListener = new IDockedStackListener.Stub() {
+        @Override
+        public void onDividerVisibilityChanged(boolean visible) {}
+
+        @Override
+        public void onDockedStackExistsChanged(boolean exists) {
+            DockedStackListenerCompat.this.onDockedStackExistsChanged(exists);
+        }
+
+        @Override
+        public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
+                boolean isHomeStackResizable) {
+            DockedStackListenerCompat.this.onDockedStackMinimizedChanged(minimized, animDuration,
+                    isHomeStackResizable);
+        }
+
+        @Override
+        public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {}
+
+        @Override
+        public void onDockSideChanged(final int newDockSide) {
+            DockedStackListenerCompat.this.onDockSideChanged(newDockSide);
+        }
+    };
+
+    public void onDockedStackExistsChanged(boolean exists) {
+        // To be overridden
+    }
+
+    public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
+            boolean isHomeStackResizable) {
+        // To be overridden
+    }
+
+    public void onDockSideChanged(final int newDockSide) {
+        // To be overridden
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index cd831d1..d38cc0f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -54,6 +54,7 @@
     public static final int HIT_TARGET_HOME = 2;
     public static final int HIT_TARGET_OVERVIEW = 3;
     public static final int HIT_TARGET_ROTATION = 4;
+    public static final int HIT_TARGET_DEAD_ZONE = 5;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({FLAG_DISABLE_SWIPE_UP,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 5e293c6..628b3c6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shared.system;
 
+import android.app.ActivityTaskManager;
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.IActivityManager;
 import android.app.TaskStackListener;
@@ -57,7 +58,7 @@
         if (!mRegistered) {
             // Register mTaskStackListener to IActivityManager only once if needed.
             try {
-                am.registerTaskStackListener(this);
+                ActivityTaskManager.getService().registerTaskStackListener(this);
                 mRegistered = true;
             } catch (Exception e) {
                 Log.w(TAG, "Failed to call registerTaskStackListener", e);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index ed2f831..d83b36d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -166,4 +166,16 @@
         }
         return NAV_BAR_POS_INVALID;
     }
+
+    /**
+     * Registers a docked stack listener with the system.
+     */
+    public void registerDockedStackListener(DockedStackListenerCompat listener) {
+        try {
+            WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
+                    listener.mListener);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to register docked stack listener");
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierText.java b/packages/SystemUI/src/com/android/keyguard/CarrierText.java
index 66475e2..a0a3687 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierText.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierText.java
@@ -43,11 +43,6 @@
 import android.telephony.TelephonyManager;
 
 public class CarrierText extends TextView {
-    /** Do not show missing sim message. */
-    public static final int FLAG_HIDE_MISSING_SIM = 1 << 0;
-    /** Do not show airplane mode message. */
-    public static final int FLAG_HIDE_AIRPLANE_MODE = 1 << 1;
-
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
     private static final String TAG = "CarrierText";
 
@@ -55,17 +50,23 @@
 
     private final boolean mIsEmergencyCallCapable;
 
+    private boolean mTelephonyCapable;
+
+    private boolean mShowMissingSim;
+
+    private boolean mShowAirplaneMode;
+
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
     private WifiManager mWifiManager;
 
     private boolean[] mSimErrorState = new boolean[TelephonyManager.getDefault().getPhoneCount()];
 
-    private int mFlags;
-
-    private KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
+    private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
         @Override
         public void onRefreshCarrierInfo() {
+            if (DEBUG) Log.d(TAG, "onRefreshCarrierInfo(), mTelephonyCapable: "
+                    + Boolean.toString(mTelephonyCapable));
             updateCarrierText();
         }
 
@@ -77,9 +78,18 @@
             setSelected(true);
         };
 
+        @Override
+        public void onTelephonyCapable(boolean capable) {
+            if (DEBUG) Log.d(TAG, "onTelephonyCapable() mTelephonyCapable: "
+                    + Boolean.toString(capable));
+            mTelephonyCapable = capable;
+            updateCarrierText();
+        }
+
         public void onSimStateChanged(int subId, int slotId, IccCardConstants.State simState) {
             if (slotId < 0) {
-                Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId);
+                Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId
+                        + " mTelephonyCapable: " + Boolean.toString(mTelephonyCapable));
                 return;
             }
 
@@ -91,13 +101,9 @@
                 mSimErrorState[slotId] = false;
                 updateCarrierText();
             }
-        };
+        }
     };
 
-    public void setDisplayFlags(int flags) {
-        mFlags = flags;
-    }
-
     /**
      * The status of this lock screen. Primarily used for widgets on LockScreen.
      */
@@ -110,7 +116,8 @@
         SimLocked, // SIM card is currently locked
         SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure
         SimNotReady, // SIM is not ready yet. May never be on devices w/o a SIM.
-        SimIoError; // SIM card is faulty
+        SimIoError, // SIM card is faulty
+        SimUnknown // SIM card is unknown
     }
 
     public CarrierText(Context context) {
@@ -126,6 +133,8 @@
                 attrs, R.styleable.CarrierText, 0, 0);
         try {
             useAllCaps = a.getBoolean(R.styleable.CarrierText_allCaps, false);
+            mShowAirplaneMode = a.getBoolean(R.styleable.CarrierText_showAirplaneMode, false);
+            mShowMissingSim = a.getBoolean(R.styleable.CarrierText_showMissingSim, false);
         } finally {
             a.recycle();
         }
@@ -249,12 +258,12 @@
     }
 
     private String getMissingSimMessage() {
-        return (mFlags & FLAG_HIDE_MISSING_SIM) == 0
+        return mShowMissingSim && mTelephonyCapable
                 ? getContext().getString(R.string.keyguard_missing_sim_message_short) : "";
     }
 
     private String getAirplaneModeMessage() {
-        return (mFlags & FLAG_HIDE_AIRPLANE_MODE) == 0
+        return mShowAirplaneMode
                 ? getContext().getString(R.string.airplane_mode) : "";
     }
 
@@ -360,6 +369,9 @@
                         getContext().getText(R.string.keyguard_sim_error_message_short),
                         text);
                 break;
+            case SimUnknown:
+                carrierText = null;
+                break;
         }
 
         return carrierText;
@@ -408,11 +420,11 @@
             case PERM_DISABLED:
                 return StatusMode.SimPermDisabled;
             case UNKNOWN:
-                return StatusMode.SimMissing;
+                return StatusMode.SimUnknown;
             case CARD_IO_ERROR:
                 return StatusMode.SimIoError;
         }
-        return StatusMode.SimMissing;
+        return StatusMode.SimUnknown;
     }
 
     private static CharSequence concatenate(CharSequence plmn, CharSequence spn) {
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index 9435589..5605b7a 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
@@ -171,7 +172,7 @@
         // should be the equivalent to the old userActivity(EMERGENCY_CALL_TIMEOUT)
         mPowerManager.userActivity(SystemClock.uptimeMillis(), true);
         try {
-            ActivityManager.getService().stopSystemLockTaskMode();
+            ActivityTaskManager.getService().stopSystemLockTaskMode();
         } catch (RemoteException e) {
             Slog.w(LOG_TAG, "Failed to stop app pinning");
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
index 62b8e7c..0340904 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
@@ -27,5 +27,5 @@
      */
     public static final boolean DEBUG = false;
     public static final boolean DEBUG_SIM_STATES = true;
-    public static final boolean DEBUG_FP_WAKELOCK = true;
+    public static final boolean DEBUG_BIOMETRIC_WAKELOCK = true;
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
index df64160..265a961 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
@@ -242,6 +242,11 @@
         mViewMediatorCallback.resetKeyguard();
     }
 
+    @Override
+    public void onCancelClicked() {
+        mViewMediatorCallback.onCancelClicked();
+    }
+
     public void resetSecurityContainer() {
         mSecurityContainer.reset();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index adb2460..a3862eb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -112,6 +112,7 @@
         if (cancelBtn != null) {
             cancelBtn.setOnClickListener(view -> {
                 mCallback.reset();
+                mCallback.onCancelClicked();
             });
         }
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 81cf3ae..6b24ad5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -201,6 +201,7 @@
         if (cancelBtn != null) {
             cancelBtn.setOnClickListener(view -> {
                 mCallback.reset();
+                mCallback.onCancelClicked();
             });
         }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 50cf5b9..9bf7816 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -163,6 +163,7 @@
         if (cancelBtn != null) {
             cancelBtn.setOnClickListener(view -> {
                 mCallback.reset();
+                mCallback.onCancelClicked();
             });
         }
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
index 5b743c1..cbfbffb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
@@ -48,4 +48,11 @@
      * Resets the keyguard view.
      */
     void reset();
+
+    /**
+     * Call when cancel button is pressed in bouncer.
+     */
+    default void onCancelClicked() {
+        // No-op
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 1942fc1..f4acc0c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -68,6 +68,7 @@
          */
         public void finish(boolean strongAuth, int targetUserId);
         public void reset();
+        public void onCancelClicked();
     }
 
     public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
@@ -143,6 +144,7 @@
             mSecurityViewFlipper.addView(v);
             updateSecurityView(v);
             view = (KeyguardSecurityView)v;
+            view.reset();
         }
 
         return view;
@@ -446,6 +448,10 @@
         public void reset() {
             mSecurityCallback.reset();
         }
+
+        public void onCancelClicked() {
+            mSecurityCallback.onCancelClicked();
+        }
     };
 
     // The following is used to ignore callbacks from SecurityViews that are no longer current
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 4e060f6..6da143c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -73,6 +73,7 @@
 
     private ArraySet<View> mVisibleInDoze;
     private boolean mPulsing;
+    private boolean mWasPulsing;
     private float mDarkAmount = 0;
     private int mTextColor;
     private float mWidgetPadding;
@@ -198,6 +199,9 @@
         mClockView.setElegantTextHeight(false);
     }
 
+    /**
+     * Moves clock and separator, adjusting margins when slice content changes.
+     */
     private void onSliceContentChanged() {
         boolean smallClock = mKeyguardSlice.hasHeader() || mPulsing;
         float clockScale = smallClock ? mSmallClockScale : 1;
@@ -220,11 +224,12 @@
     @Override
     public void onLayoutChange(View view, int left, int top, int right, int bottom,
             int oldLeft, int oldTop, int oldRight, int oldBottom) {
-        int heightOffset = mPulsing ? 0 : getHeight() - mLastLayoutHeight;
+        int heightOffset = mPulsing || mWasPulsing ? 0 : getHeight() - mLastLayoutHeight;
         boolean hasHeader = mKeyguardSlice.hasHeader();
         boolean smallClock = hasHeader || mPulsing;
         long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION;
-        long delay = smallClock ? 0 : duration / 4;
+        long delay = smallClock || mWasPulsing ? 0 : duration / 4;
+        mWasPulsing = false;
 
         boolean shouldAnimate = mKeyguardSlice.getLayoutTransition() != null
                 && mKeyguardSlice.getLayoutTransition().isRunning();
@@ -448,7 +453,18 @@
     }
 
     public void setPulsing(boolean pulsing, boolean animate) {
+        if (mPulsing == pulsing) {
+            return;
+        }
+        if (mPulsing) {
+            mWasPulsing = true;
+        }
         mPulsing = pulsing;
+        // Animation can look really weird when the slice has a header, let's hide the views
+        // immediately instead of fading them away.
+        if (mKeyguardSlice.hasHeader()) {
+            animate = false;
+        }
         mKeyguardSlice.setPulsing(pulsing, animate);
         updateDozeVisibleViews();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 62cd13b..dfd6a18 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -32,12 +32,14 @@
 import android.annotation.AnyThread;
 import android.annotation.MainThread;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
 import android.app.Instrumentation;
 import android.app.PendingIntent;
 import android.app.UserSwitchObserver;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.TrustManager;
+import android.hardware.biometrics.BiometricSourceType;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -47,6 +49,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.database.ContentObserver;
+import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -71,7 +74,6 @@
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
 import android.util.Log;
-import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
@@ -82,6 +84,7 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.settingslib.WirelessUtils;
 import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 
@@ -144,26 +147,27 @@
     private static final int MSG_DREAMING_STATE_CHANGED = 333;
     private static final int MSG_USER_UNLOCKED = 334;
     private static final int MSG_ASSISTANT_STACK_CHANGED = 335;
-    private static final int MSG_FINGERPRINT_AUTHENTICATION_CONTINUE = 336;
+    private static final int MSG_BIOMETRIC_AUTHENTICATION_CONTINUE = 336;
     private static final int MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED = 337;
+    private static final int MSG_TELEPHONY_CAPABLE = 338;
 
-    /** Fingerprint state: Not listening to fingerprint. */
-    private static final int FINGERPRINT_STATE_STOPPED = 0;
+    /** Biometric authentication state: Not listening. */
+    private static final int BIOMETRIC_STATE_STOPPED = 0;
 
-    /** Fingerprint state: Listening. */
-    private static final int FINGERPRINT_STATE_RUNNING = 1;
+    /** Biometric authentication state: Listening. */
+    private static final int BIOMETRIC_STATE_RUNNING = 1;
 
     /**
-     * Fingerprint state: Cancelling and waiting for the confirmation from FingerprintService to
+     * Biometric authentication: Cancelling and waiting for the relevant biometric service to
      * send us the confirmation that cancellation has happened.
      */
-    private static final int FINGERPRINT_STATE_CANCELLING = 2;
+    private static final int BIOMETRIC_STATE_CANCELLING = 2;
 
     /**
-     * Fingerprint state: During cancelling we got another request to start listening, so when we
+     * Biometric state: During cancelling we got another request to start listening, so when we
      * receive the cancellation done signal, we should start listening again.
      */
-    private static final int FINGERPRINT_STATE_CANCELLING_RESTARTING = 3;
+    private static final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3;
 
     private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
 
@@ -202,6 +206,8 @@
     private boolean mHasLockscreenWallpaper;
     private boolean mAssistantVisible;
     private boolean mKeyguardOccluded;
+    @VisibleForTesting
+    protected boolean mTelephonyCapable;
 
     // Device provisioning state
     private boolean mDeviceProvisioned;
@@ -226,7 +232,8 @@
     private List<SubscriptionInfo> mSubscriptionInfo;
     private TrustManager mTrustManager;
     private UserManager mUserManager;
-    private int mFingerprintRunningState = FINGERPRINT_STATE_STOPPED;
+    private int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+    private int mFaceRunningState = BIOMETRIC_STATE_STOPPED;
     private LockPatternUtils mLockPatternUtils;
     private final IDreamManager mDreamManager;
     private boolean mIsDreaming;
@@ -234,14 +241,15 @@
     private boolean mLogoutEnabled;
 
     /**
-     * Short delay before restarting fingerprint authentication after a successful try
-     * This should be slightly longer than the time between onFingerprintAuthenticated and
-     * setKeyguardGoingAway(true).
+     * Short delay before restarting biometric authentication after a successful try
+     * This should be slightly longer than the time between on<biometric>Authenticated
+     * (e.g. onFingerprintAuthenticated) and setKeyguardGoingAway(true).
      */
-    private static final int FINGERPRINT_CONTINUE_DELAY_MS = 500;
+    private static final int BIOMETRIC_CONTINUE_DELAY_MS = 500;
 
     // If FP daemon dies, keyguard should retry after a short delay
-    private int mHardwareUnavailableRetryCount = 0;
+    private int mHardwareFingerprintUnavailableRetryCount = 0;
+    private int mHardwareFaceUnavailableRetryCount = 0;
     private static final int HW_UNAVAILABLE_TIMEOUT = 3000; // ms
     private static final int HW_UNAVAILABLE_RETRY_MAX = 3;
 
@@ -332,14 +340,17 @@
                     break;
                 case MSG_ASSISTANT_STACK_CHANGED:
                     mAssistantVisible = (boolean)msg.obj;
-                    updateFingerprintListeningState();
+                    updateBiometricListeningState();
                     break;
-                case MSG_FINGERPRINT_AUTHENTICATION_CONTINUE:
-                    updateFingerprintListeningState();
+                case MSG_BIOMETRIC_AUTHENTICATION_CONTINUE:
+                    updateBiometricListeningState();
                     break;
                 case MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED:
                     updateLogoutEnabled();
                     break;
+                case MSG_TELEPHONY_CAPABLE:
+                    updateTelephonyCapable((boolean)msg.obj);
+                    break;
                 default:
                     super.handleMessage(msg);
                     break;
@@ -358,10 +369,11 @@
     private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
     private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray();
     private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray();
+    private SparseBooleanArray mUserFaceAuthenticated = new SparseBooleanArray();
     private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray();
 
     private static int sCurrentUser;
-    private Runnable mUpdateFingerprintListeningState = this::updateFingerprintListeningState;
+    private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState;
     private static boolean sDisableHandlerCheckForTesting;
 
     public synchronized static void setCurrentUser(int currentUser) {
@@ -486,7 +498,7 @@
      */
     public void setKeyguardOccluded(boolean occluded) {
         mKeyguardOccluded = occluded;
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
     }
 
     /**
@@ -514,19 +526,19 @@
         mUserFingerprintAuthenticated.put(userId, true);
         // Update/refresh trust state only if user can skip bouncer
         if (getUserCanSkipBouncer(userId)) {
-            mTrustManager.unlockedByFingerprintForUser(userId);
+            mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FINGERPRINT);
         }
         // Don't send cancel if authentication succeeds
         mFingerprintCancelSignal = null;
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintAuthenticated(userId);
+                cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT);
             }
         }
 
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATION_CONTINUE),
-                FINGERPRINT_CONTINUE_DELAY_MS);
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
+                BIOMETRIC_CONTINUE_DELAY_MS);
 
         // Only authenticate fingerprint once when assistant is visible
         mAssistantVisible = false;
@@ -538,10 +550,10 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintAuthFailed();
+                cb.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
             }
         }
-        handleFingerprintHelp(-1, mContext.getString(R.string.fingerprint_not_recognized));
+        handleFingerprintHelp(-1, mContext.getString(R.string.kg_fingerprint_not_recognized));
     }
 
     private void handleFingerprintAcquired(int acquireInfo) {
@@ -551,7 +563,7 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintAcquired();
+                cb.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
             }
         }
     }
@@ -576,7 +588,7 @@
             }
             onFingerprintAuthenticated(userId);
         } finally {
-            setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+            setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
         }
         Trace.endSection();
     }
@@ -585,7 +597,7 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintHelp(msgId, helpString);
+                cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FINGERPRINT);
             }
         }
     }
@@ -594,23 +606,23 @@
         @Override
         public void run() {
             Log.w(TAG, "Retrying fingerprint after HW unavailable, attempt " +
-                    mHardwareUnavailableRetryCount);
+                    mHardwareFingerprintUnavailableRetryCount);
             updateFingerprintListeningState();
         }
     };
 
     private void handleFingerprintError(int msgId, String errString) {
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
-                && mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) {
-            setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+                && mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+            setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
             startListeningForFingerprint();
         } else {
-            setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+            setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
         }
 
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
-            if (mHardwareUnavailableRetryCount < HW_UNAVAILABLE_RETRY_MAX) {
-                mHardwareUnavailableRetryCount++;
+            if (mHardwareFingerprintUnavailableRetryCount < HW_UNAVAILABLE_RETRY_MAX) {
+                mHardwareFingerprintUnavailableRetryCount++;
                 mHandler.removeCallbacks(mRetryFingerprintAuthentication);
                 mHandler.postDelayed(mRetryFingerprintAuthentication, HW_UNAVAILABLE_TIMEOUT);
             }
@@ -625,7 +637,7 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintError(msgId, errString);
+                cb.onBiometricError(msgId, errString, BiometricSourceType.FINGERPRINT);
             }
         }
     }
@@ -635,8 +647,8 @@
     }
 
     private void setFingerprintRunningState(int fingerprintRunningState) {
-        boolean wasRunning = mFingerprintRunningState == FINGERPRINT_STATE_RUNNING;
-        boolean isRunning = fingerprintRunningState == FINGERPRINT_STATE_RUNNING;
+        boolean wasRunning = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
+        boolean isRunning = fingerprintRunningState == BIOMETRIC_STATE_RUNNING;
         mFingerprintRunningState = fingerprintRunningState;
 
         // Clients of KeyguardUpdateMonitor don't care about the internal state about the
@@ -652,10 +664,162 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintRunningStateChanged(isFingerprintDetectionRunning());
+                cb.onBiometricRunningStateChanged(isFingerprintDetectionRunning(),
+                        BiometricSourceType.FINGERPRINT);
             }
         }
     }
+
+    private void onFaceAuthenticated(int userId) {
+        Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated");
+        mUserFaceAuthenticated.put(userId, true);
+        // Update/refresh trust state only if user can skip bouncer
+        if (getUserCanSkipBouncer(userId)) {
+            mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FACE);
+        }
+        // Don't send cancel if authentication succeeds
+        mFaceCancelSignal = null;
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onBiometricAuthenticated(userId,
+                        BiometricSourceType.FACE);
+            }
+        }
+
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
+                BIOMETRIC_CONTINUE_DELAY_MS);
+
+        // Only authenticate face once when assistant is visible
+        mAssistantVisible = false;
+
+        Trace.endSection();
+    }
+
+    private void handleFaceAuthFailed() {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onBiometricAuthFailed(BiometricSourceType.FACE);
+            }
+        }
+        handleFaceHelp(-1, mContext.getString(R.string.kg_face_not_recognized));
+    }
+
+    private void handleFaceAcquired(int acquireInfo) {
+        if (acquireInfo != FaceManager.FACE_ACQUIRED_GOOD) {
+            return;
+        }
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onBiometricAcquired(BiometricSourceType.FACE);
+            }
+        }
+    }
+
+    private void handleFaceAuthenticated(int authUserId) {
+        Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated");
+        try {
+            final int userId;
+            try {
+                userId = ActivityManager.getService().getCurrentUser().id;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to get current user id: ", e);
+                return;
+            }
+            if (userId != authUserId) {
+                Log.d(TAG, "Face authenticated for wrong user: " + authUserId);
+                return;
+            }
+            if (isFaceDisabled(userId)) {
+                Log.d(TAG, "Face authentication disabled by DPM for userId: " + userId);
+                return;
+            }
+            onFaceAuthenticated(userId);
+        } finally {
+            setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+        }
+        Trace.endSection();
+    }
+
+    private void handleFaceHelp(int msgId, String helpString) {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FACE);
+            }
+        }
+    }
+
+    private Runnable mRetryFaceAuthentication = new Runnable() {
+        @Override
+        public void run() {
+            Log.w(TAG, "Retrying face after HW unavailable, attempt " +
+                    mHardwareFaceUnavailableRetryCount);
+            updateFaceListeningState();
+        }
+    };
+
+    private void handleFaceError(int msgId, String errString) {
+        if (msgId == FaceManager.FACE_ERROR_CANCELED
+                && mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+            setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+            startListeningForFace();
+        } else {
+            setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+        }
+
+        if (msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE) {
+            if (mHardwareFaceUnavailableRetryCount < HW_UNAVAILABLE_RETRY_MAX) {
+                mHardwareFaceUnavailableRetryCount++;
+                mHandler.removeCallbacks(mRetryFaceAuthentication);
+                mHandler.postDelayed(mRetryFaceAuthentication, HW_UNAVAILABLE_TIMEOUT);
+            }
+        }
+
+        if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
+            mLockPatternUtils.requireStrongAuth(
+                    LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
+                    getCurrentUser());
+        }
+
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onBiometricError(msgId, errString,
+                        BiometricSourceType.FACE);
+            }
+        }
+    }
+
+    private void handleFaceLockoutReset() {
+        updateFaceListeningState();
+    }
+
+    private void setFaceRunningState(int faceRunningState) {
+        boolean wasRunning = mFaceRunningState == BIOMETRIC_STATE_RUNNING;
+        boolean isRunning = faceRunningState == BIOMETRIC_STATE_RUNNING;
+        mFaceRunningState = faceRunningState;
+
+        // Clients of KeyguardUpdateMonitor don't care about the internal state or about the
+        // asynchronousness of the cancel cycle. So only notify them if the actualy running state
+        // has changed.
+        if (wasRunning != isRunning) {
+            notifyFaceRunningStateChanged();
+        }
+    }
+
+    private void notifyFaceRunningStateChanged() {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onBiometricRunningStateChanged(isFaceDetectionRunning(),
+                        BiometricSourceType.FACE);
+            }
+        }
+    }
+
     private void handleFaceUnlockStateChanged(boolean running, int userId) {
         checkIsHandlerThread();
         mUserFaceUnlockRunning.put(userId, running);
@@ -672,7 +836,11 @@
     }
 
     public boolean isFingerprintDetectionRunning() {
-        return mFingerprintRunningState == FINGERPRINT_STATE_RUNNING;
+        return mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
+    }
+
+    public boolean isFaceDetectionRunning() {
+        return mFaceRunningState == BIOMETRIC_STATE_RUNNING;
     }
 
     private boolean isTrustDisabled(int userId) {
@@ -691,9 +859,18 @@
                 || isSimPinSecure();
     }
 
+    private boolean isFaceDisabled(int userId) {
+        final DevicePolicyManager dpm =
+                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        return dpm != null && (dpm.getKeyguardDisabledFeatures(null, userId)
+                & DevicePolicyManager.KEYGUARD_DISABLE_FACE) != 0
+                || isSimPinSecure();
+    }
+
+
     public boolean getUserCanSkipBouncer(int userId) {
         return getUserHasTrust(userId) || (mUserFingerprintAuthenticated.get(userId)
-                && isUnlockingWithFingerprintAllowed());
+                && isUnlockingWithBiometricAllowed());
     }
 
     public boolean getUserHasTrust(int userId) {
@@ -704,8 +881,8 @@
         return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId);
     }
 
-    public boolean isUnlockingWithFingerprintAllowed() {
-        return mStrongAuthTracker.isUnlockingWithFingerprintAllowed();
+    public boolean isUnlockingWithBiometricAllowed() {
+        return mStrongAuthTracker.isUnlockingWithBiometricAllowed();
     }
 
     public boolean isUserInLockdown(int userId) {
@@ -792,14 +969,18 @@
                                 maxChargingMicroWatt));
                 mHandler.sendMessage(msg);
             } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
+                SimData args = SimData.fromIntent(intent);
                 // ACTION_SIM_STATE_CHANGED is rebroadcast after unlocking the device to
                 // keep compatibility with apps that aren't direct boot aware.
                 // SysUI should just ignore this broadcast because it was already received
                 // and processed previously.
                 if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
+                    // Guarantee mTelephonyCapable state after SysUI crash and restart
+                    if (args.simState == State.ABSENT) {
+                        mHandler.obtainMessage(MSG_TELEPHONY_CAPABLE, true).sendToTarget();
+                    }
                     return;
                 }
-                SimData args = SimData.fromIntent(intent);
                 if (DEBUG_SIM_STATES) {
                     Log.v(TAG, "action " + action
                         + " state: " + intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)
@@ -861,7 +1042,7 @@
         }
     };
 
-    private final FingerprintManager.LockoutResetCallback mLockoutResetCallback
+    private final FingerprintManager.LockoutResetCallback mFingerprintLockoutResetCallback
             = new FingerprintManager.LockoutResetCallback() {
         @Override
         public void onLockoutReset() {
@@ -869,13 +1050,21 @@
         }
     };
 
-    private FingerprintManager.AuthenticationCallback mAuthenticationCallback
+    private final FaceManager.LockoutResetCallback mFaceLockoutResetCallback
+            = new FaceManager.LockoutResetCallback() {
+        @Override
+        public void onLockoutReset() {
+            handleFaceLockoutReset();
+        }
+    };
+
+    private FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
             = new AuthenticationCallback() {
 
         @Override
         public void onAuthenticationFailed() {
             handleFingerprintAuthFailed();
-        };
+        }
 
         @Override
         public void onAuthenticationSucceeded(AuthenticationResult result) {
@@ -899,8 +1088,42 @@
             handleFingerprintAcquired(acquireInfo);
         }
     };
+
+    private FaceManager.AuthenticationCallback mFaceAuthenticationCallback
+            = new FaceManager.AuthenticationCallback() {
+
+        @Override
+        public void onAuthenticationFailed() {
+            handleFaceAuthFailed();
+        }
+
+        @Override
+        public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) {
+            Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
+            handleFaceAuthenticated(result.getUserId());
+            Trace.endSection();
+        }
+
+        @Override
+        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+            handleFaceHelp(helpMsgId, helpString.toString());
+        }
+
+        @Override
+        public void onAuthenticationError(int errMsgId, CharSequence errString) {
+            handleFaceError(errMsgId, errString.toString());
+        }
+
+        @Override
+        public void onAuthenticationAcquired(int acquireInfo) {
+            handleFaceAcquired(acquireInfo);
+        }
+    };
+
     private CancellationSignal mFingerprintCancelSignal;
+    private CancellationSignal mFaceCancelSignal;
     private FingerprintManager mFpm;
+    private FaceManager mFaceAuthenticationManager;
 
     /**
      * When we receive a
@@ -1048,9 +1271,9 @@
             super(context);
         }
 
-        public boolean isUnlockingWithFingerprintAllowed() {
+        public boolean isUnlockingWithBiometricAllowed() {
             int userId = getCurrentUser();
-            return isFingerprintAllowedForUser(userId);
+            return isBiometricAllowedForUser(userId);
         }
 
         public boolean hasUserAuthenticatedSinceBoot() {
@@ -1074,7 +1297,7 @@
 
     protected void handleStartedWakingUp() {
         Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
         final int count = mCallbacks.size();
         for (int i = 0; i < count; i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1086,7 +1309,7 @@
     }
 
     protected void handleStartedGoingToSleep(int arg1) {
-        clearFingerprintRecognized();
+        clearBiometricRecognized();
         final int count = mCallbacks.size();
         for (int i = 0; i < count; i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1095,7 +1318,7 @@
             }
         }
         mGoingToSleep = true;
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
     }
 
     protected void handleFinishedGoingToSleep(int arg1) {
@@ -1107,7 +1330,7 @@
                 cb.onFinishedGoingToSleep(arg1);
             }
         }
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
     }
 
     private void handleScreenTurnedOn() {
@@ -1121,7 +1344,8 @@
     }
 
     private void handleScreenTurnedOff() {
-        mHardwareUnavailableRetryCount = 0;
+        mHardwareFingerprintUnavailableRetryCount = 0;
+        mHardwareFaceUnavailableRetryCount = 0;
         final int count = mCallbacks.size();
         for (int i = 0; i < count; i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1140,7 +1364,7 @@
                 cb.onDreamingStateChanged(mIsDreaming);
             }
         }
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
     }
 
     private void handleUserInfoChanged(int userId) {
@@ -1237,39 +1461,84 @@
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
             mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
         }
-        updateFingerprintListeningState();
+
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
+            mFaceAuthenticationManager =
+                    (FaceManager) context.getSystemService(Context.FACE_SERVICE);
+        }
+        updateBiometricListeningState();
         if (mFpm != null) {
-            mFpm.addLockoutResetCallback(mLockoutResetCallback);
+            mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
+        }
+        if (mFaceAuthenticationManager != null) {
+            mFaceAuthenticationManager.addLockoutResetCallback(mFaceLockoutResetCallback);
         }
 
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
         mUserManager = context.getSystemService(UserManager.class);
         mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
         mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
+        updateAirplaneModeState();
+    }
+
+    private void updateAirplaneModeState() {
+        // ACTION_AIRPLANE_MODE_CHANGED do not broadcast if device set AirplaneMode ON and boot
+        if (!WirelessUtils.isAirplaneModeOn(mContext)
+                || mHandler.hasMessages(MSG_AIRPLANE_MODE_CHANGED)) {
+            return;
+        }
+        mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED);
+    }
+
+    private void updateBiometricListeningState() {
+        updateFingerprintListeningState();
+        updateFaceListeningState();
     }
 
     private void updateFingerprintListeningState() {
         // If this message exists, we should not authenticate again until this message is
         // consumed by the handler
-        if (mHandler.hasMessages(MSG_FINGERPRINT_AUTHENTICATION_CONTINUE)) {
+        if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
             return;
         }
         mHandler.removeCallbacks(mRetryFingerprintAuthentication);
         boolean shouldListenForFingerprint = shouldListenForFingerprint();
-        if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING && !shouldListenForFingerprint) {
+        if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFingerprint) {
             stopListeningForFingerprint();
-        } else if (mFingerprintRunningState != FINGERPRINT_STATE_RUNNING
+        } else if (mFingerprintRunningState != BIOMETRIC_STATE_RUNNING
                 && shouldListenForFingerprint) {
             startListeningForFingerprint();
         }
     }
 
+    private void updateFaceListeningState() {
+        // If this message exists, we should not authenticate again until this message is
+        // consumed by the handler
+        if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
+            return;
+        }
+        mHandler.removeCallbacks(mRetryFaceAuthentication);
+        boolean shouldListenForFace = shouldListenForFace();
+        if (mFaceRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFace) {
+            stopListeningForFace();
+        } else if (mFaceRunningState != BIOMETRIC_STATE_RUNNING
+                && shouldListenForFace) {
+            startListeningForFace();
+        }
+    }
+
     private boolean shouldListenForFingerprintAssistant() {
         return mAssistantVisible && mKeyguardOccluded
                 && !mUserFingerprintAuthenticated.get(getCurrentUser(), false)
                 && !mUserHasTrust.get(getCurrentUser(), false);
     }
 
+    private boolean shouldListenForFaceAssistant() {
+        return mAssistantVisible && mKeyguardOccluded
+                && !mUserFaceAuthenticated.get(getCurrentUser(), false)
+                && !mUserHasTrust.get(getCurrentUser(), false);
+    }
+
     private boolean shouldListenForFingerprint() {
         return (mKeyguardIsVisible || !mDeviceInteractive ||
                 (mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
@@ -1278,9 +1547,18 @@
                 && !mKeyguardGoingAway;
     }
 
+    private boolean shouldListenForFace() {
+        return (mKeyguardIsVisible || !mDeviceInteractive ||
+                (mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
+                shouldListenForFaceAssistant() || (mKeyguardOccluded && mIsDreaming))
+                && !mSwitchingUser && !isFaceDisabled(getCurrentUser())
+                && !mKeyguardGoingAway;
+    }
+
+
     private void startListeningForFingerprint() {
-        if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING) {
-            setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING_RESTARTING);
+        if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING) {
+            setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
             return;
         }
         if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
@@ -1290,9 +1568,27 @@
                 mFingerprintCancelSignal.cancel();
             }
             mFingerprintCancelSignal = new CancellationSignal();
-            mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null,
-                    userId);
-            setFingerprintRunningState(FINGERPRINT_STATE_RUNNING);
+            mFpm.authenticate(null, mFingerprintCancelSignal, 0, mFingerprintAuthenticationCallback,
+                    null, userId);
+            setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
+        }
+    }
+
+    private void startListeningForFace() {
+        if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING) {
+            setFaceRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
+            return;
+        }
+        if (DEBUG) Log.v(TAG, "startListeningForFace()");
+        int userId = ActivityManager.getCurrentUser();
+        if (isUnlockWithFacePossible(userId)) {
+            if (mFaceCancelSignal != null) {
+                mFaceCancelSignal.cancel();
+            }
+            mFaceCancelSignal = new CancellationSignal();
+            mFaceAuthenticationManager.authenticate(null, mFaceCancelSignal, 0,
+                    mFaceAuthenticationCallback, null);
+            setFaceRunningState(BIOMETRIC_STATE_RUNNING);
         }
     }
 
@@ -1301,17 +1597,37 @@
                 && mFpm.getEnrolledFingerprints(userId).size() > 0;
     }
 
+    public boolean isUnlockWithFacePossible(int userId) {
+        return mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()
+                && !isFaceDisabled(userId)
+                && mFaceAuthenticationManager.hasEnrolledFaces(userId);
+    }
+
     private void stopListeningForFingerprint() {
         if (DEBUG) Log.v(TAG, "stopListeningForFingerprint()");
-        if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING) {
+        if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING) {
             if (mFingerprintCancelSignal != null) {
                 mFingerprintCancelSignal.cancel();
                 mFingerprintCancelSignal = null;
             }
-            setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING);
+            setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING);
         }
-        if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) {
-            setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING);
+        if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+            setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING);
+        }
+    }
+
+    private void stopListeningForFace() {
+        if (DEBUG) Log.v(TAG, "stopListeningForFace()");
+        if (mFaceRunningState == BIOMETRIC_STATE_RUNNING) {
+            if (mFaceCancelSignal != null) {
+                mFaceCancelSignal.cancel();
+                mFaceCancelSignal = null;
+            }
+            setFaceRunningState(BIOMETRIC_STATE_CANCELLING);
+        }
+        if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+            setFaceRunningState(BIOMETRIC_STATE_CANCELLING);
         }
     }
 
@@ -1528,6 +1844,23 @@
     }
 
     /**
+     * Handle Telephony status during Boot for CarrierText display policy
+     */
+    @VisibleForTesting
+    void updateTelephonyCapable(boolean capable){
+        if (capable == mTelephonyCapable) {
+            return;
+        }
+        mTelephonyCapable = capable;
+        for (WeakReference<KeyguardUpdateMonitorCallback> ref : mCallbacks) {
+            KeyguardUpdateMonitorCallback cb = ref.get();
+            if (cb != null) {
+                cb.onTelephonyCapable(mTelephonyCapable);
+            }
+        }
+    }
+
+    /**
      * Handle {@link #MSG_SIM_STATE_CHANGE}
      */
     @VisibleForTesting
@@ -1540,6 +1873,10 @@
 
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             Log.w(TAG, "invalid subId in handleSimStateChange()");
+            /* Only handle No SIM(ABSENT) due to handleServiceStateChange() handle other case */
+            if (state == State.ABSENT) {
+                updateTelephonyCapable(true);
+            }
             return;
         }
 
@@ -1568,7 +1905,8 @@
     /**
      * Handle {@link #MSG_SERVICE_STATE_CHANGE}
      */
-    private void handleServiceStateChange(int subId, ServiceState serviceState) {
+    @VisibleForTesting
+    void handleServiceStateChange(int subId, ServiceState serviceState) {
         if (DEBUG) {
             Log.d(TAG,
                     "handleServiceStateChange(subId=" + subId + ", serviceState=" + serviceState);
@@ -1577,6 +1915,8 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             Log.w(TAG, "invalid subId in handleServiceStateChange()");
             return;
+        } else {
+            updateTelephonyCapable(true);
         }
 
         mServiceStates.put(subId, serviceState);
@@ -1589,6 +1929,10 @@
         }
     }
 
+    public boolean isKeyguardVisible() {
+        return mKeyguardIsVisible;
+    }
+
     /**
      * Notifies that the visibility state of Keyguard has changed.
      *
@@ -1604,7 +1948,7 @@
                 cb.onKeyguardVisibilityChangedRaw(showing);
             }
         }
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
     }
 
     /**
@@ -1612,7 +1956,7 @@
      */
     private void handleKeyguardReset() {
         if (DEBUG) Log.d(TAG, "handleKeyguardReset");
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
         mNeedsSlowUnlockTransition = resolveNeedsSlowUnlockTransition();
     }
 
@@ -1641,7 +1985,7 @@
                 cb.onKeyguardBouncerChanged(isBouncer);
             }
         }
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
     }
 
     /**
@@ -1724,7 +2068,7 @@
     public void setSwitchingUser(boolean switching) {
         mSwitchingUser = switching;
         // Since this comes in on a binder thread, we need to post if first
-        mHandler.post(mUpdateFingerprintListeningState);
+        mHandler.post(mUpdateBiometricListeningState);
     }
 
     private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
@@ -1736,6 +2080,7 @@
         callback.onRefreshCarrierInfo();
         callback.onClockVisibilityChanged();
         callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible);
+        callback.onTelephonyCapable(mTelephonyCapable);
         for (Entry<Integer, SimData> data : mSimDatas.entrySet()) {
             final SimData state = data.getValue();
             callback.onSimStateChanged(state.subId, state.slotId, state.simState);
@@ -1812,9 +2157,11 @@
         mFailedAttempts.put(userId, getFailedUnlockAttempts(userId) + 1);
     }
 
-    public void clearFingerprintRecognized() {
+    public void clearBiometricRecognized() {
         mUserFingerprintAuthenticated.clear();
-        mTrustManager.clearAllFingerprints();
+        mUserFaceAuthenticated.clear();
+        mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT);
+        mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE);
     }
 
     public boolean isSimPinVoiceSecure() {
@@ -1843,7 +2190,7 @@
         @Override
         public void onTaskStackChangedBackground() {
             try {
-                ActivityManager.StackInfo info = ActivityManager.getService().getStackInfo(
+                ActivityManager.StackInfo info = ActivityTaskManager.getService().getStackInfo(
                         WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
                 if (info == null) {
                     return;
@@ -2049,7 +2396,7 @@
             final int userId = ActivityManager.getCurrentUser();
             final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
             pw.println("  Fingerprint state (user=" + userId + ")");
-            pw.println("    allowed=" + isUnlockingWithFingerprintAllowed());
+            pw.println("    allowed=" + isUnlockingWithBiometricAllowed());
             pw.println("    auth'd=" + mUserFingerprintAuthenticated.get(userId));
             pw.println("    authSinceBoot="
                     + getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
@@ -2058,5 +2405,18 @@
             pw.println("    strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
             pw.println("    trustManaged=" + getUserTrustIsManaged(userId));
         }
+        if (mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()) {
+            final int userId = ActivityManager.getCurrentUser();
+            final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
+            pw.println("  Face authentication state (user=" + userId + ")");
+            pw.println("    allowed=" + isUnlockingWithBiometricAllowed());
+            pw.println("    auth'd=" + mUserFaceAuthenticated.get(userId));
+            pw.println("    authSinceBoot="
+                    + getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
+            pw.println("    disabled(DPM)=" + isFaceDisabled(userId));
+            pw.println("    possible=" + isUnlockWithFacePossible(userId));
+            pw.println("    strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
+            pw.println("    trustManaged=" + getUserTrustIsManaged(userId));
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 67571bb..f818d05 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -17,7 +17,7 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.graphics.Bitmap;
-import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.biometrics.BiometricSourceType;
 import android.media.AudioManager;
 import android.os.SystemClock;
 import android.telephony.TelephonyManager;
@@ -117,6 +117,12 @@
     public void onUserSwitchComplete(int userId) { }
 
     /**
+     * Called when the Telephony capable
+     * @param capable
+     */
+    public void onTelephonyCapable(boolean capable) { }
+
+    /**
      * Called when the SIM state changes.
      * @param slotId
      * @param simState
@@ -213,38 +219,46 @@
     public void onTrustGrantedWithFlags(int flags, int userId) { }
 
     /**
-     * Called when a finger has been acquired.
+     * Called when a biometric has been acquired.
      * <p>
-     * It is guaranteed that either {@link #onFingerprintAuthenticated} or
-     * {@link #onFingerprintAuthFailed()} is called after this method eventually.
+     * It is guaranteed that either {@link #onBiometricAuthenticated} or
+     * {@link #onBiometricAuthFailed(BiometricSourceType)} is called after this method eventually.
+     * @param biometricSourceType
      */
-    public void onFingerprintAcquired() { }
+    public void onBiometricAcquired(BiometricSourceType biometricSourceType) { }
 
     /**
-     * Called when a fingerprint couldn't be authenticated.
+     * Called when a biometric couldn't be authenticated.
+     * @param biometricSourceType
      */
-    public void onFingerprintAuthFailed() { }
+    public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) { }
 
     /**
-     * Called when a fingerprint is recognized.
-     * @param userId the user id for which the fingerprint was authenticated
+     * Called when a biometric is recognized.
+     * @param userId the user id for which the biometric sample was authenticated
+     * @param biometricSourceType
      */
-    public void onFingerprintAuthenticated(int userId) { }
+    public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { }
 
     /**
-     * Called when fingerprint provides help string (e.g. "Try again")
+     * Called when biometric authentication provides help string (e.g. "Try again")
      * @param msgId
      * @param helpString
+     * @param biometricSourceType
      */
-    public void onFingerprintHelp(int msgId, String helpString) { }
+    public void onBiometricHelp(int msgId, String helpString,
+            BiometricSourceType biometricSourceType) { }
 
     /**
-     * Called when fingerprint provides an semi-permanent error message
-     * (e.g. "Hardware not available").
-     * @param msgId one of the error messages listed in {@link FingerprintManager}
+     * Called when biometric authentication method provides a semi-permanent
+     * error message (e.g. "Hardware not available").
+     * @param msgId one of the error messages listed in
+     *        {@link android.hardware.biometrics.BiometricConstants}
      * @param errString
+     * @param biometricSourceType
      */
-    public void onFingerprintError(int msgId, String errString) { }
+    public void onBiometricError(int msgId, String errString,
+            BiometricSourceType biometricSourceType) { }
 
     /**
      * Called when the state of face unlock changed.
@@ -252,9 +266,10 @@
     public void onFaceUnlockStateChanged(boolean running, int userId) { }
 
     /**
-     * Called when the fingerprint running state changed.
+     * Called when biometric running state changed.
      */
-    public void onFingerprintRunningStateChanged(boolean running) { }
+    public void onBiometricRunningStateChanged(boolean running,
+            BiometricSourceType biometricSourceType) { }
 
     /**
      * Called when the state that the user hasn't used strong authentication since quite some time
diff --git a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
index 5c68123..41b86a7 100644
--- a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
@@ -105,4 +105,9 @@
      * @return Message that should be displayed above the challenge.
      */
     CharSequence consumeCustomMessage();
+
+    /**
+     * Call when cancel button is pressed in bouncer.
+     */
+    void onCancelClicked();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java b/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java
index 01b4254..bbc8ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java
@@ -92,6 +92,11 @@
     }
 
     @Override
+    public void onUiModeChanged() {
+        inflateLayout();
+    }
+
+    @Override
     public void onLocaleListChanged() {
         inflateLayout();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 7aef9fd..d5383b97 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -55,7 +55,10 @@
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 import com.android.systemui.util.Utils.DisableStateTracker;
+import com.android.systemui.R;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.text.NumberFormat;
 
 public class BatteryMeterView extends LinearLayout implements
@@ -72,6 +75,7 @@
     private int mTextColor;
     private int mLevel;
     private boolean mForceShowPercent;
+    private boolean mShowPercentAvailable;
 
     private int mDarkModeBackgroundColor;
     private int mDarkModeFillColor;
@@ -111,6 +115,9 @@
         atts.recycle();
 
         mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));
+        mShowPercentAvailable = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_battery_percentage_setting_available);
+
 
         addOnAttachStateChangeListener(
                 new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS));
@@ -139,6 +146,7 @@
                 getContext().getContentResolver().registerContentObserver(
                         Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver,
                         newUserId);
+                updateShowPercent();
             }
         };
 
@@ -262,8 +270,11 @@
 
     private void updateShowPercent() {
         final boolean showing = mBatteryPercentView != null;
-        if (0 != Settings.System.getIntForUser(getContext().getContentResolver(),
-                SHOW_BATTERY_PERCENT, 0, mUser) || mForceShowPercent) {
+        final boolean systemSetting = 0 != Settings.System
+                .getIntForUser(getContext().getContentResolver(),
+                SHOW_BATTERY_PERCENT, 0, mUser);
+
+        if ((mShowPercentAvailable && systemSetting) || mForceShowPercent) {
             if (!showing) {
                 mBatteryPercentView = loadPercentView();
                 if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
@@ -343,6 +354,17 @@
         return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor);
     }
 
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        String powerSave = mDrawable == null ? null : mDrawable.getPowerSave() + "";
+        CharSequence percent = mBatteryPercentView == null ? null : mBatteryPercentView.getText();
+        pw.println("  BatteryMeterView:");
+        pw.println("    mDrawable.getPowerSave: " + powerSave);
+        pw.println("    mBatteryPercentView.getText(): " + percent);
+        pw.println("    mTextColor: #" + Integer.toHexString(mTextColor));
+        pw.println("    mLevel: " + mLevel);
+        pw.println("    mForceShowPercent: " + mForceShowPercent);
+    }
+
     private final class SettingObserver extends ContentObserver {
         public SettingObserver(Handler handler) {
             super(handler);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 9c9f021..86e0e1f 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -46,7 +46,7 @@
 import com.android.systemui.power.EnhancedEstimatesImpl;
 import com.android.systemui.power.PowerNotificationWarnings;
 import com.android.systemui.power.PowerUI;
-import com.android.systemui.statusbar.AppOpsListener;
+import com.android.systemui.statusbar.notification.AppOpsListener;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 377fab5..f5ad747 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -32,8 +32,8 @@
 import android.view.ViewConfiguration;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
 
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
index 98dc321..198a4e6 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
@@ -27,32 +27,33 @@
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.view.ViewTreeObserver;
-import android.widget.FrameLayout;
 import android.widget.LinearLayout;
+
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 import com.android.systemui.util.leak.RotationUtils;
 
-import java.util.ArrayList;
-
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
 
-public class HardwareUiLayout extends FrameLayout implements Tunable {
+public class HardwareUiLayout extends LinearLayout implements Tunable {
 
     private static final String EDGE_BLEED = "sysui_hwui_edge_bleed";
     private static final String ROUNDED_DIVIDER = "sysui_hwui_rounded_divider";
     private final int[] mTmp2 = new int[2];
-    private View mChild;
+    private View mList;
+    private View mSeparatedView;
     private int mOldHeight;
     private boolean mAnimating;
     private AnimatorSet mAnimation;
     private View mDivision;
     private boolean mHasOutsideTouch;
-    private HardwareBgDrawable mBackground;
+    private HardwareBgDrawable mListBackground;
+    private HardwareBgDrawable mSeparatedViewBackground;
     private Animator mAnimator;
     private boolean mCollapse;
+    private boolean mHasSeparatedButton;
     private int mEndPoint;
     private boolean mEdgeBleed;
     private boolean mRoundedDivider;
@@ -91,16 +92,19 @@
         mRoundedDivider = Settings.Secure.getInt(getContext().getContentResolver(),
                 ROUNDED_DIVIDER, 0) != 0;
         updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
-        mBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, getContext());
-        if (mChild != null) {
-            mChild.setBackground(mBackground);
+        mListBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, getContext());
+        mSeparatedViewBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed,
+                getContext());
+        if (mList != null) {
+            mList.setBackground(mListBackground);
+            mSeparatedView.setBackground(mSeparatedViewBackground);
             requestLayout();
         }
     }
 
     private void updateEdgeMargin(int edge) {
-        if (mChild != null) {
-            MarginLayoutParams params = (MarginLayoutParams) mChild.getLayoutParams();
+        if (mList != null) {
+            MarginLayoutParams params = (MarginLayoutParams) mList.getLayoutParams();
             if (mRotation == ROTATION_LANDSCAPE) {
                 params.topMargin = edge;
             } else if (mRotation == ROTATION_SEASCAPE) {
@@ -108,7 +112,19 @@
             } else {
                 params.rightMargin = edge;
             }
-            mChild.setLayoutParams(params);
+            mList.setLayoutParams(params);
+        }
+
+        if (mSeparatedView != null) {
+            MarginLayoutParams params = (MarginLayoutParams) mSeparatedView.getLayoutParams();
+            if (mRotation == ROTATION_LANDSCAPE) {
+                params.topMargin = edge;
+            } else if (mRotation == ROTATION_SEASCAPE) {
+                params.bottomMargin = edge;
+            } else {
+                params.rightMargin = edge;
+            }
+            mSeparatedView.setLayoutParams(params);
         }
     }
 
@@ -119,13 +135,15 @@
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        if (mChild == null) {
+        if (mList == null) {
             if (getChildCount() != 0) {
-                mChild = getChildAt(0);
-                mChild.setBackground(mBackground);
+                mList = getChildAt(0);
+                mList.setBackground(mListBackground);
+                mSeparatedView = getChildAt(1);
+                mSeparatedView.setBackground(mSeparatedViewBackground);
                 updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
-                mOldHeight = mChild.getMeasuredHeight();
-                mChild.addOnLayoutChangeListener(
+                mOldHeight = mList.getMeasuredHeight();
+                mList.addOnLayoutChangeListener(
                         (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
                                 updatePosition());
                 updateRotation();
@@ -133,7 +151,7 @@
                 return;
             }
         }
-        int newHeight = mChild.getMeasuredHeight();
+        int newHeight = mList.getMeasuredHeight();
         if (newHeight != mOldHeight) {
             animateChild(mOldHeight, newHeight);
         }
@@ -170,37 +188,60 @@
         } else {
             rotateLeft();
         }
+        if (mHasSeparatedButton) {
+            if (from == ROTATION_SEASCAPE || to == ROTATION_SEASCAPE) {
+                // Separated view has top margin, so seascape separated view need special rotation,
+                // not a full left or right rotation.
+                swapLeftAndTop(mSeparatedView);
+            } else if (from == ROTATION_LANDSCAPE) {
+                rotateRight(mSeparatedView);
+            } else {
+                rotateLeft(mSeparatedView);
+            }
+        }
         if (to != ROTATION_NONE) {
-            if (mChild instanceof LinearLayout) {
+            if (mList instanceof LinearLayout) {
                 mRotatedBackground = true;
-                mBackground.setRotatedBackground(true);
-                LinearLayout linearLayout = (LinearLayout) mChild;
+                mListBackground.setRotatedBackground(true);
+                mSeparatedViewBackground.setRotatedBackground(true);
+                LinearLayout linearLayout = (LinearLayout) mList;
                 if (mSwapOrientation) {
                     linearLayout.setOrientation(LinearLayout.HORIZONTAL);
+                    setOrientation(LinearLayout.HORIZONTAL);
                 }
-                swapDimens(this.mChild);
+                swapDimens(mList);
+                swapDimens(mSeparatedView);
             }
         } else {
-            if (mChild instanceof LinearLayout) {
+            if (mList instanceof LinearLayout) {
                 mRotatedBackground = false;
-                mBackground.setRotatedBackground(false);
-                LinearLayout linearLayout = (LinearLayout) mChild;
+                mListBackground.setRotatedBackground(false);
+                mSeparatedViewBackground.setRotatedBackground(false);
+                LinearLayout linearLayout = (LinearLayout) mList;
                 if (mSwapOrientation) {
                     linearLayout.setOrientation(LinearLayout.VERTICAL);
+                    setOrientation(LinearLayout.VERTICAL);
                 }
-                swapDimens(mChild);
+                swapDimens(mList);
+                swapDimens(mSeparatedView);
             }
         }
     }
 
     private void rotateRight() {
         rotateRight(this);
-        rotateRight(mChild);
+        rotateRight(mList);
         swapDimens(this);
 
-        LayoutParams p = (LayoutParams) mChild.getLayoutParams();
+        LayoutParams p = (LayoutParams) mList.getLayoutParams();
         p.gravity = rotateGravityRight(p.gravity);
-        mChild.setLayoutParams(p);
+        mList.setLayoutParams(p);
+
+        LayoutParams separatedViewLayoutParams = (LayoutParams) mSeparatedView.getLayoutParams();
+        separatedViewLayoutParams.gravity = rotateGravityRight(separatedViewLayoutParams.gravity);
+        mSeparatedView.setLayoutParams(separatedViewLayoutParams);
+
+        setGravity(p.gravity);
     }
 
     private void swapDimens(View v) {
@@ -247,12 +288,18 @@
 
     private void rotateLeft() {
         rotateLeft(this);
-        rotateLeft(mChild);
+        rotateLeft(mList);
         swapDimens(this);
 
-        LayoutParams p = (LayoutParams) mChild.getLayoutParams();
+        LayoutParams p = (LayoutParams) mList.getLayoutParams();
         p.gravity = rotateGravityLeft(p.gravity);
-        mChild.setLayoutParams(p);
+        mList.setLayoutParams(p);
+
+        LayoutParams separatedViewLayoutParams = (LayoutParams) mSeparatedView.getLayoutParams();
+        separatedViewLayoutParams.gravity = rotateGravityLeft(separatedViewLayoutParams.gravity);
+        mSeparatedView.setLayoutParams(separatedViewLayoutParams);
+
+        setGravity(p.gravity);
     }
 
     private int rotateGravityLeft(int gravity) {
@@ -310,6 +357,15 @@
         v.setLayoutParams(params);
     }
 
+    private void swapLeftAndTop(View v) {
+        v.setPadding(v.getPaddingTop(), v.getPaddingLeft(), v.getPaddingBottom(),
+                v.getPaddingRight());
+        MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
+        params.setMargins(params.topMargin, params.leftMargin, params.bottomMargin,
+                params.rightMargin);
+        v.setLayoutParams(params);
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
@@ -329,14 +385,14 @@
                 mAnimating = false;
             }
         });
-        int fromTop = mChild.getTop();
-        int fromBottom = mChild.getBottom();
+        int fromTop = mList.getTop();
+        int fromBottom = mList.getBottom();
         int toTop = fromTop - ((newHeight - oldHeight) / 2);
         int toBottom = fromBottom + ((newHeight - oldHeight) / 2);
-        ObjectAnimator top = ObjectAnimator.ofInt(mChild, "top", fromTop, toTop);
-        top.addUpdateListener(animation -> mBackground.invalidateSelf());
+        ObjectAnimator top = ObjectAnimator.ofInt(mList, "top", fromTop, toTop);
+        top.addUpdateListener(animation -> mListBackground.invalidateSelf());
         mAnimation.playTogether(top,
-                ObjectAnimator.ofInt(mChild, "bottom", fromBottom, toBottom));
+                ObjectAnimator.ofInt(mList, "bottom", fromBottom, toBottom));
     }
 
     public void setDivisionView(View v) {
@@ -350,26 +406,30 @@
     }
 
     private void updatePosition() {
-        if (mChild == null) return;
+        if (mList == null) return;
+        // If got separated button, setRotatedBackground to false,
+        // all items won't get white background.
+        mListBackground.setRotatedBackground(mHasSeparatedButton);
+        mSeparatedViewBackground.setRotatedBackground(mHasSeparatedButton);
         if (mDivision != null && mDivision.getVisibility() == VISIBLE) {
             int index = mRotatedBackground ? 0 : 1;
             mDivision.getLocationOnScreen(mTmp2);
             float trans = mRotatedBackground ? mDivision.getTranslationX()
                     : mDivision.getTranslationY();
             int viewTop = (int) (mTmp2[index] + trans);
-            mChild.getLocationOnScreen(mTmp2);
+            mList.getLocationOnScreen(mTmp2);
             viewTop -= mTmp2[index];
             setCutPoint(viewTop);
         } else {
-            setCutPoint(mChild.getMeasuredHeight());
+            setCutPoint(mList.getMeasuredHeight());
         }
     }
 
     private void setCutPoint(int point) {
-        int curPoint = mBackground.getCutPoint();
+        int curPoint = mListBackground.getCutPoint();
         if (curPoint == point) return;
         if (getAlpha() == 0 || curPoint == 0) {
-            mBackground.setCutPoint(point);
+            mListBackground.setCutPoint(point);
             return;
         }
         if (mAnimator != null) {
@@ -379,7 +439,7 @@
             mAnimator.cancel();
         }
         mEndPoint = point;
-        mAnimator = ObjectAnimator.ofInt(mBackground, "cutPoint", curPoint, point);
+        mAnimator = ObjectAnimator.ofInt(mListBackground, "cutPoint", curPoint, point);
         if (mCollapse) {
             mAnimator.setStartDelay(300);
             mCollapse = false;
@@ -404,6 +464,10 @@
         mCollapse = true;
     }
 
+    public void setHasSeparatedButton(boolean hasSeparatedButton) {
+        mHasSeparatedButton = hasSeparatedButton;
+    }
+
     public static HardwareUiLayout get(View v) {
         if (v instanceof HardwareUiLayout) return (HardwareUiLayout) v;
         if (v.getParent() instanceof View) {
@@ -413,14 +477,14 @@
     }
 
     private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> {
-        if (mHasOutsideTouch || (mChild == null)) {
+        if (mHasOutsideTouch || (mList == null)) {
             inoutInfo.setTouchableInsets(
                     ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
             return;
         }
         inoutInfo.setTouchableInsets(
                 ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT);
-        inoutInfo.contentInsets.set(mChild.getLeft(), mChild.getTop(),
-                0, getBottom() - mChild.getBottom());
+        inoutInfo.contentInsets.set(mList.getLeft(), mList.getTop(),
+                0, getBottom() - mList.getBottom());
     };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index b8a57bf..50d862d 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -290,7 +290,7 @@
                         || dh != mLastSurfaceHeight;
 
                 boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation
-                        || mSurfaceRedrawNeeded;
+                        || mSurfaceRedrawNeeded || mNeedsDrawAfterLoadingWallpaper;
                 if (!redrawNeeded && !mOffsetsChanged) {
                     if (DEBUG) {
                         Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index aeef496..2b3ea3a 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -24,13 +24,20 @@
 import android.view.animation.LinearInterpolator;
 import android.view.animation.PathInterpolator;
 
-import com.android.systemui.statusbar.stack.HeadsUpAppearInterpolator;
+import com.android.systemui.statusbar.notification.stack.HeadsUpAppearInterpolator;
 
 /**
  * Utility class to receive interpolators from
  */
 public class Interpolators {
     public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+    /**
+     * Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t
+     * goes from 1 to 0 instead of 0 to 1).
+     */
+    public static final Interpolator FAST_OUT_SLOW_IN_REVERSE =
+            new PathInterpolator(0.8f, 0f, 0.6f, 1f);
     public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
     public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
     public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
@@ -51,4 +58,11 @@
      */
     public static final Interpolator TOUCH_RESPONSE =
             new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
+    /**
+     * Like {@link #TOUCH_RESPONSE}, but used in case the animation is played in reverse (i.e. t
+     * goes from 1 to 0 instead of 0 to 1).
+     */
+    public static final Interpolator TOUCH_RESPONSE_REVERSE =
+            new PathInterpolator(0.9f, 0f, 0.7f, 1f);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index cbb69ee..1e458fa 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui;
 
+import android.hardware.biometrics.BiometricSourceType;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -26,7 +27,7 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.internal.util.LatencyTracker;
-import com.android.systemui.statusbar.phone.FingerprintUnlockController;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.StatusBar;
 
 /**
@@ -72,10 +73,10 @@
     }
 
     private void fakeWakeAndUnlock() {
-        FingerprintUnlockController fingerprintUnlockController = getComponent(StatusBar.class)
-                .getFingerprintUnlockController();
-        fingerprintUnlockController.onFingerprintAcquired();
-        fingerprintUnlockController.onFingerprintAuthenticated(
-                KeyguardUpdateMonitor.getCurrentUser());
+        BiometricUnlockController biometricUnlockController = getComponent(StatusBar.class)
+                .getBiometricUnlockController();
+        biometricUnlockController.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
+        biometricUnlockController.onBiometricAuthenticated(
+                KeyguardUpdateMonitor.getCurrentUser(), BiometricSourceType.FINGERPRINT);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 9fc71c8..3c44eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -30,6 +30,7 @@
 import android.os.PatternMatcher;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Log;
 import android.view.SurfaceControl;
 
@@ -65,7 +66,7 @@
 
     public static final String TAG_OPS = "OverviewProxyService";
     public static final boolean DEBUG_OVERVIEW_PROXY = false;
-    private static final long BACKOFF_MILLIS = 5000;
+    private static final long BACKOFF_MILLIS = 1000;
     private static final long DEFERRED_CALLBACK_MILLIS = 5000;
 
     private final Context mContext;
@@ -81,11 +82,15 @@
     private int mConnectionBackoffAttempts;
     private @InteractionType int mInteractionFlags;
     private boolean mIsEnabled;
+    private int mCurrentBoundedUserId = -1;
 
     private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
 
         public GraphicBufferCompat screenshot(Rect sourceCrop, int width, int height, int minLayer,
                 int maxLayer, boolean useIdentityTransform, int rotation) {
+            if (!verifyCaller("screenshot")) {
+                return null;
+            }
             long token = Binder.clearCallingIdentity();
             try {
                 return new GraphicBufferCompat(SurfaceControl.screenshotToBuffer(sourceCrop, width,
@@ -96,10 +101,13 @@
         }
 
         public void startScreenPinning(int taskId) {
+            if (!verifyCaller("startScreenPinning")) {
+                return;
+            }
             long token = Binder.clearCallingIdentity();
             try {
                 mHandler.post(() -> {
-                    StatusBar statusBar = ((SystemUIApplication) mContext).getComponent(
+                    StatusBar statusBar = SysUiServiceProvider.getComponent(mContext,
                             StatusBar.class);
                     if (statusBar != null) {
                         statusBar.showScreenPinningRequest(taskId, false /* allowCancel */);
@@ -111,6 +119,9 @@
         }
 
         public void onSplitScreenInvoked() {
+            if (!verifyCaller("onSplitScreenInvoked")) {
+                return;
+            }
             long token = Binder.clearCallingIdentity();
             try {
                 EventBus.getDefault().post(new DockedFirstAnimationFrameEvent());
@@ -120,6 +131,9 @@
         }
 
         public void onOverviewShown(boolean fromHome) {
+            if (!verifyCaller("onOverviewShown")) {
+                return;
+            }
             long token = Binder.clearCallingIdentity();
             try {
                 mHandler.post(() -> {
@@ -133,6 +147,9 @@
         }
 
         public void setInteractionState(@InteractionType int flags) {
+            if (!verifyCaller("setInteractionState")) {
+                return;
+            }
             long token = Binder.clearCallingIdentity();
             try {
                 if (mInteractionFlags != flags) {
@@ -150,9 +167,12 @@
         }
 
         public Rect getNonMinimizedSplitScreenSecondaryBounds() {
+            if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) {
+                return null;
+            }
             long token = Binder.clearCallingIdentity();
             try {
-                Divider divider = ((SystemUIApplication) mContext).getComponent(Divider.class);
+                Divider divider = SysUiServiceProvider.getComponent(mContext, Divider.class);
                 if (divider != null) {
                     return divider.getView().getNonMinimizedSplitScreenSecondaryBounds();
                 }
@@ -163,6 +183,9 @@
         }
 
         public void setBackButtonAlpha(float alpha, boolean animate) {
+            if (!verifyCaller("setBackButtonAlpha")) {
+                return;
+            }
             long token = Binder.clearCallingIdentity();
             try {
                 mHandler.post(() -> {
@@ -172,6 +195,16 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        private boolean verifyCaller(String reason) {
+            final int callerId = Binder.getCallingUserHandle().getIdentifier();
+            if (callerId != mCurrentBoundedUserId) {
+                Log.w(TAG_OPS, "Launcher called sysui with invalid user: " + callerId + ", reason: "
+                        + reason);
+                return false;
+            }
+            return true;
+        }
     };
 
     private final Runnable mDeferredConnectionCallback = () -> {
@@ -210,7 +243,9 @@
             }
             try {
                 mOverviewProxy.onBind(mSysUiProxy);
+                mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser();
             } catch (RemoteException e) {
+                mCurrentBoundedUserId = -1;
                 Log.e(TAG_OPS, "Failed to call onBind()", e);
             }
             notifyConnectionChanged();
@@ -219,18 +254,21 @@
         @Override
         public void onNullBinding(ComponentName name) {
             Log.w(TAG_OPS, "Null binding of '" + name + "', try reconnecting");
+            mCurrentBoundedUserId = -1;
             internalConnectToCurrentUser();
         }
 
         @Override
         public void onBindingDied(ComponentName name) {
             Log.w(TAG_OPS, "Binding died of '" + name + "', try reconnecting");
+            mCurrentBoundedUserId = -1;
             internalConnectToCurrentUser();
         }
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
             // Do nothing
+            mCurrentBoundedUserId = -1;
         }
     };
 
@@ -302,7 +340,8 @@
         boolean bound = false;
         try {
             bound = mContext.bindServiceAsUser(launcherServiceIntent,
-                    mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
+                    mOverviewServiceConnection,
+                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                     UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
         } catch (SecurityException e) {
             Log.e(TAG_OPS, "Unable to bind because of security error", e);
@@ -391,14 +430,22 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println(TAG_OPS + " state:");
-        pw.print("  mConnectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
+        pw.print("  recentsComponentName="); pw.println(mRecentsComponentName);
+        pw.print("  isConnected="); pw.println(mOverviewProxy != null);
         pw.print("  isCurrentUserSetup="); pw.println(mDeviceProvisionedController
                 .isCurrentUserSetup());
-        pw.print("  isConnected="); pw.println(mOverviewProxy != null);
-        pw.print("  mRecentsComponentName="); pw.println(mRecentsComponentName);
-        pw.print("  mIsEnabled="); pw.println(isEnabled());
-        pw.print("  mInteractionFlags="); pw.println(mInteractionFlags);
-        pw.print("  mQuickStepIntent="); pw.println(mQuickStepIntent);
+        pw.print("  connectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
+        pw.print("  interactionFlags="); pw.println(mInteractionFlags);
+
+        pw.print("  quickStepIntent="); pw.println(mQuickStepIntent);
+        pw.print("  quickStepIntentResolved="); pw.println(isEnabled());
+
+        final int swipeUpDefaultValue = mContext.getResources()
+                .getBoolean(com.android.internal.R.bool.config_swipe_up_gesture_default) ? 1 : 0;
+        final int swipeUpEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.SWIPE_UP_TO_SWITCH_APPS_ENABLED, swipeUpDefaultValue);
+        pw.print("  swipeUpSetting="); pw.println(swipeUpEnabled != 0);
+        pw.print("  swipeUpSettingDefault="); pw.println(swipeUpDefaultValue != 0);
     }
 
     public interface OverviewProxyListener {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 33cc258..68d77eb 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -26,8 +26,12 @@
 import static com.android.systemui.tuner.TunablePadding.FLAG_START;
 
 import android.annotation.Dimension;
+import android.app.ActivityManager;
 import android.app.Fragment;
+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.Configuration;
 import android.graphics.Canvas;
@@ -39,10 +43,12 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.SystemProperties;
 import android.provider.Settings.Secure;
-import androidx.annotation.VisibleForTesting;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
@@ -52,10 +58,12 @@
 import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import com.android.internal.util.Preconditions;
 import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
@@ -68,11 +76,16 @@
 import com.android.systemui.tuner.TunerService.Tunable;
 import com.android.systemui.util.leak.RotationUtils;
 
+import androidx.annotation.VisibleForTesting;
+
 /**
  * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
  * for antialiasing and emulation purposes.
  */
 public class ScreenDecorations extends SystemUI implements Tunable {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "ScreenDecorations";
+
     public static final String SIZE = "sysui_rounded_size";
     public static final String PADDING = "sysui_rounded_content_padding";
     private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
@@ -91,9 +104,25 @@
     private int mRotation;
     private DisplayCutoutView mCutoutTop;
     private DisplayCutoutView mCutoutBottom;
+    private SecureSetting mColorInversionSetting;
+    private boolean mPendingRotationChange;
+    private Handler mHandler;
 
     @Override
     public void start() {
+        mHandler = startHandlerThread();
+        mHandler.post(this::startOnScreenDecorationsThread);
+        setupStatusBarPaddingIfNeeded();
+    }
+
+    @VisibleForTesting
+    Handler startHandlerThread() {
+        HandlerThread thread = new HandlerThread("ScreenDecorations");
+        thread.start();
+        return thread.getThreadHandler();
+    }
+
+    private void startOnScreenDecorationsThread() {
         mWindowManager = mContext.getSystemService(WindowManager.class);
         mRoundedDefault = mContext.getResources().getDimensionPixelSize(
                 R.dimen.rounded_corner_radius);
@@ -105,12 +134,6 @@
             setupDecorations();
         }
 
-        int padding = mContext.getResources().getDimensionPixelSize(
-                R.dimen.rounded_corner_content_padding);
-        if (padding != 0) {
-            setupPadding(padding);
-        }
-
         mDisplayListener = new DisplayManager.DisplayListener() {
             @Override
             public void onDisplayAdded(int displayId) {
@@ -124,6 +147,26 @@
 
             @Override
             public void onDisplayChanged(int displayId) {
+                final int newRotation = RotationUtils.getExactRotation(mContext);
+                if (mOverlay != null && mBottomOverlay != null && mRotation != newRotation) {
+                    // We cannot immediately update the orientation. Otherwise
+                    // WindowManager is still deferring layout until it has finished dispatching
+                    // the config changes, which may cause divergence between what we draw
+                    // (new orientation), and where we are placed on the screen (old orientation).
+                    // Instead we wait until either:
+                    // - we are trying to redraw. This because WM resized our window and told us to.
+                    // - the config change has been dispatched, so WM is no longer deferring layout.
+                    mPendingRotationChange = true;
+                    if (DEBUG) {
+                        Log.i(TAG, "Rotation changed, deferring " + newRotation + ", staying at "
+                                + mRotation);
+                    }
+
+                    mOverlay.getViewTreeObserver().addOnPreDrawListener(
+                            new RestartingPreDrawListener(mOverlay, newRotation));
+                    mBottomOverlay.getViewTreeObserver().addOnPreDrawListener(
+                            new RestartingPreDrawListener(mBottomOverlay, newRotation));
+                }
                 updateOrientation();
             }
         };
@@ -131,19 +174,19 @@
         mRotation = -1;
         mDisplayManager = (DisplayManager) mContext.getSystemService(
                 Context.DISPLAY_SERVICE);
-        mDisplayManager.registerDisplayListener(mDisplayListener, null);
+        mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
     }
 
     private void setupDecorations() {
         mOverlay = LayoutInflater.from(mContext)
                 .inflate(R.layout.rounded_corners, null);
         mCutoutTop = new DisplayCutoutView(mContext, true,
-                this::updateWindowVisibilities);
+                this::updateWindowVisibilities, this);
         ((ViewGroup)mOverlay).addView(mCutoutTop);
         mBottomOverlay = LayoutInflater.from(mContext)
                 .inflate(R.layout.rounded_corners, null);
         mCutoutBottom = new DisplayCutoutView(mContext, false,
-                this::updateWindowVisibilities);
+                this::updateWindowVisibilities, this);
         ((ViewGroup)mBottomOverlay).addView(mCutoutBottom);
 
         mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
@@ -161,25 +204,23 @@
         mWindowManager.getDefaultDisplay().getMetrics(metrics);
         mDensity = metrics.density;
 
-        Dependency.get(TunerService.class).addTunable(this, SIZE);
+        Dependency.get(Dependency.MAIN_HANDLER).post(
+                () -> Dependency.get(TunerService.class).addTunable(this, SIZE));
 
         // Watch color inversion and invert the overlay as needed.
-        SecureSetting setting = new SecureSetting(mContext, Dependency.get(Dependency.MAIN_HANDLER),
+        mColorInversionSetting = new SecureSetting(mContext, mHandler,
                 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
             @Override
             protected void handleValueChanged(int value, boolean observedChange) {
-                int tint = value != 0 ? Color.WHITE : Color.BLACK;
-                ColorStateList tintList = ColorStateList.valueOf(tint);
-                ((ImageView) mOverlay.findViewById(R.id.left)).setImageTintList(tintList);
-                ((ImageView) mOverlay.findViewById(R.id.right)).setImageTintList(tintList);
-                ((ImageView) mBottomOverlay.findViewById(R.id.left)).setImageTintList(tintList);
-                ((ImageView) mBottomOverlay.findViewById(R.id.right)).setImageTintList(tintList);
-                mCutoutTop.setColor(tint);
-                mCutoutBottom.setColor(tint);
+                updateColorInversion(value);
             }
         };
-        setting.setListening(true);
-        setting.onChange(false);
+        mColorInversionSetting.setListening(true);
+        mColorInversionSetting.onChange(false);
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
+        mContext.registerReceiver(mIntentReceiver, filter, null /* permission */, mHandler);
 
         mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() {
             @Override
@@ -197,17 +238,65 @@
                         .start();
             }
         });
+
+        mOverlay.getViewTreeObserver().addOnPreDrawListener(
+                new ValidatingPreDrawListener(mOverlay));
+        mBottomOverlay.getViewTreeObserver().addOnPreDrawListener(
+                new ValidatingPreDrawListener(mBottomOverlay));
+    }
+
+    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_USER_SWITCHED)) {
+                int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                        ActivityManager.getCurrentUser());
+                // update color inversion setting to the new user
+                mColorInversionSetting.setUserId(newUserId);
+                updateColorInversion(mColorInversionSetting.getValue());
+            }
+        }
+    };
+
+    private void updateColorInversion(int colorsInvertedValue) {
+        int tint = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK;
+        ColorStateList tintList = ColorStateList.valueOf(tint);
+        ((ImageView) mOverlay.findViewById(R.id.left)).setImageTintList(tintList);
+        ((ImageView) mOverlay.findViewById(R.id.right)).setImageTintList(tintList);
+        ((ImageView) mBottomOverlay.findViewById(R.id.left)).setImageTintList(tintList);
+        ((ImageView) mBottomOverlay.findViewById(R.id.right)).setImageTintList(tintList);
+        mCutoutTop.setColor(tint);
+        mCutoutBottom.setColor(tint);
     }
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
-        updateOrientation();
-        if (shouldDrawCutout() && mOverlay == null) {
-            setupDecorations();
-        }
+        mHandler.post(() -> {
+            int oldRotation = mRotation;
+            mPendingRotationChange = false;
+            updateOrientation();
+            if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation);
+            if (shouldDrawCutout() && mOverlay == null) {
+                setupDecorations();
+            }
+            if (mOverlay != null) {
+                // Updating the layout params ensures that ViewRootImpl will call relayoutWindow(),
+                // which ensures that the forced seamless rotation will end, even if we updated
+                // the rotation before window manager was ready (and was still waiting for sending
+                // the updated rotation).
+                updateLayoutParams();
+            }
+        });
     }
 
-    protected void updateOrientation() {
+    private void updateOrientation() {
+        Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(),
+                "must call on " + mHandler.getLooper().getThread()
+                        + ", but was " + Thread.currentThread());
+        if (mPendingRotationChange) {
+            return;
+        }
         int newRotation = RotationUtils.getExactRotation(mContext);
         if (newRotation != mRotation) {
             mRotation = newRotation;
@@ -284,7 +373,19 @@
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
     }
 
-    private void setupPadding(int padding) {
+
+    private void setupStatusBarPaddingIfNeeded() {
+        // TODO: This should be moved to a more appropriate place, as it is not related to the
+        // screen decorations overlay.
+        int padding = mContext.getResources().getDimensionPixelSize(
+                R.dimen.rounded_corner_content_padding);
+        if (padding != 0) {
+            setupStatusBarPadding(padding);
+        }
+
+    }
+
+    private void setupStatusBarPadding(int padding) {
         // Add some padding to all the content near the edge of the screen.
         StatusBar sb = getComponent(StatusBar.class);
         View statusBar = (sb != null ? sb.getStatusBarWindow() : null);
@@ -353,30 +454,32 @@
 
     @Override
     public void onTuningChanged(String key, String newValue) {
-        if (mOverlay == null) return;
-        if (SIZE.equals(key)) {
-            int size = mRoundedDefault;
-            int sizeTop = mRoundedDefaultTop;
-            int sizeBottom = mRoundedDefaultBottom;
-            if (newValue != null) {
-                try {
-                    size = (int) (Integer.parseInt(newValue) * mDensity);
-                } catch (Exception e) {
+        mHandler.post(() -> {
+            if (mOverlay == null) return;
+            if (SIZE.equals(key)) {
+                int size = mRoundedDefault;
+                int sizeTop = mRoundedDefaultTop;
+                int sizeBottom = mRoundedDefaultBottom;
+                if (newValue != null) {
+                    try {
+                        size = (int) (Integer.parseInt(newValue) * mDensity);
+                    } catch (Exception e) {
+                    }
                 }
-            }
 
-            if (sizeTop == 0) {
-                sizeTop = size;
-            }
-            if (sizeBottom == 0) {
-                sizeBottom = size;
-            }
+                if (sizeTop == 0) {
+                    sizeTop = size;
+                }
+                if (sizeBottom == 0) {
+                    sizeBottom = size;
+                }
 
-            setSize(mOverlay.findViewById(R.id.left), sizeTop);
-            setSize(mOverlay.findViewById(R.id.right), sizeTop);
-            setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom);
-            setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom);
-        }
+                setSize(mOverlay.findViewById(R.id.left), sizeTop);
+                setSize(mOverlay.findViewById(R.id.right), sizeTop);
+                setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom);
+                setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom);
+            }
+        });
     }
 
     private void setSize(View view, int pixelSize) {
@@ -423,16 +526,23 @@
         private final int[] mLocation = new int[2];
         private final boolean mInitialStart;
         private final Runnable mVisibilityChangedListener;
+        private final ScreenDecorations mDecorations;
         private int mColor = Color.BLACK;
         private boolean mStart;
         private int mRotation;
 
         public DisplayCutoutView(Context context, boolean start,
-                Runnable visibilityChangedListener) {
+                Runnable visibilityChangedListener, ScreenDecorations decorations) {
             super(context);
             mInitialStart = start;
             mVisibilityChangedListener = visibilityChangedListener;
+            mDecorations = decorations;
             setId(R.id.display_cutout);
+            if (DEBUG) {
+                getViewTreeObserver().addOnDrawListener(() -> Log.i(TAG,
+                        (mInitialStart ? "OverlayTop" : "OverlayBottom")
+                                + " drawn in rot " + mRotation));
+            }
         }
 
         public void setColor(int color) {
@@ -494,10 +604,10 @@
         }
 
         private void update() {
-            mStart = isStart();
-            if (!isAttachedToWindow()) {
+            if (!isAttachedToWindow() || mDecorations.mPendingRotationChange) {
                 return;
             }
+            mStart = isStart();
             requestLayout();
             getDisplay().getDisplayInfo(mInfo);
             mBounds.setEmpty();
@@ -582,31 +692,34 @@
                     resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0));
         }
 
-        public static void boundsFromDirection(DisplayCutout displayCutout, int gravity, Rect out) {
+        public static void boundsFromDirection(DisplayCutout displayCutout, int gravity,
+                Rect out) {
+            Region bounds = boundsFromDirection(displayCutout, gravity);
+            out.set(bounds.getBounds());
+            bounds.recycle();
+        }
+
+        public static Region boundsFromDirection(DisplayCutout displayCutout, int gravity) {
             Region bounds = displayCutout.getBounds();
             switch (gravity) {
                 case Gravity.TOP:
                     bounds.op(0, 0, Integer.MAX_VALUE, displayCutout.getSafeInsetTop(),
                             Region.Op.INTERSECT);
-                    out.set(bounds.getBounds());
                     break;
                 case Gravity.LEFT:
                     bounds.op(0, 0, displayCutout.getSafeInsetLeft(), Integer.MAX_VALUE,
                             Region.Op.INTERSECT);
-                    out.set(bounds.getBounds());
                     break;
                 case Gravity.BOTTOM:
                     bounds.op(0, displayCutout.getSafeInsetTop() + 1, Integer.MAX_VALUE,
                             Integer.MAX_VALUE, Region.Op.INTERSECT);
-                    out.set(bounds.getBounds());
                     break;
                 case Gravity.RIGHT:
                     bounds.op(displayCutout.getSafeInsetLeft() + 1, 0, Integer.MAX_VALUE,
                             Integer.MAX_VALUE, Region.Op.INTERSECT);
-                    out.set(bounds.getBounds());
                     break;
             }
-            bounds.recycle();
+            return bounds;
         }
 
         private void localBounds(Rect out) {
@@ -657,4 +770,74 @@
         return rotation == RotationUtils.ROTATION_LANDSCAPE || rotation ==
                 RotationUtils.ROTATION_SEASCAPE;
     }
+
+    /**
+     * A pre-draw listener, that cancels the draw and restarts the traversal with the updated
+     * window attributes.
+     */
+    private class RestartingPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
+
+        private final View mView;
+        private final int mTargetRotation;
+
+        private RestartingPreDrawListener(View view, int targetRotation) {
+            mView = view;
+            mTargetRotation = targetRotation;
+        }
+
+        @Override
+        public boolean onPreDraw() {
+            mView.getViewTreeObserver().removeOnPreDrawListener(this);
+
+            if (mTargetRotation == mRotation) {
+                if (DEBUG) {
+                    Log.i(TAG, (mView == mOverlay ? "OverlayTop" : "OverlayBottom")
+                            + " already in target rot "
+                            + mTargetRotation + ", allow draw without restarting it");
+                }
+                return true;
+            }
+
+            mPendingRotationChange = false;
+            // This changes the window attributes - we need to restart the traversal for them to
+            // take effect.
+            updateOrientation();
+            if (DEBUG) {
+                Log.i(TAG, (mView == mOverlay ? "OverlayTop" : "OverlayBottom")
+                        + " restarting listener fired, restarting draw for rot " + mRotation);
+            }
+            mView.invalidate();
+            return false;
+        }
+    }
+
+    /**
+     * A pre-draw listener, that validates that the rotation we draw in matches the displays
+     * rotation before continuing the draw.
+     *
+     * This is to prevent a race condition, where we have not received the display changed event
+     * yet, and would thus draw in an old orientation.
+     */
+    private class ValidatingPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
+
+        private final View mView;
+
+        public ValidatingPreDrawListener(View view) {
+            mView = view;
+        }
+
+        @Override
+        public boolean onPreDraw() {
+            final int displayRotation = RotationUtils.getExactRotation(mContext);
+            if (displayRotation != mRotation && !mPendingRotationChange) {
+                if (DEBUG) {
+                    Log.i(TAG, "Drawing rot " + mRotation + ", but display is at rot "
+                            + displayRotation + ". Restarting draw");
+                }
+                mView.invalidate();
+                return false;
+            }
+            return true;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index a64ce29..a3b5395 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -35,8 +35,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 
 public class SwipeHelper implements Gefingerpoken {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 03a6398..311a2f2 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -22,7 +22,6 @@
 import android.util.Log;
 import android.view.ViewGroup;
 
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.function.TriConsumer;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
@@ -32,12 +31,12 @@
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.NotificationEntryManager;
-import com.android.systemui.statusbar.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationLogger;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
new file mode 100644
index 0000000..6e62b0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -0,0 +1,247 @@
+/*
+ * 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.biometrics;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.android.internal.os.SomeArgs;
+import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.CommandQueue;
+
+/**
+ * Receives messages sent from AuthenticationClient and shows the appropriate biometric UI (e.g.
+ * FingerprintDialogView).
+ */
+public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callbacks {
+    private static final String TAG = "FingerprintDialogImpl";
+    private static final boolean DEBUG = true;
+
+    private static final int MSG_SHOW_DIALOG = 1;
+    private static final int MSG_BIOMETRIC_AUTHENTICATED = 2;
+    private static final int MSG_BIOMETRIC_HELP = 3;
+    private static final int MSG_BIOMETRIC_ERROR = 4;
+    private static final int MSG_HIDE_DIALOG = 5;
+    private static final int MSG_BUTTON_NEGATIVE = 6;
+    private static final int MSG_USER_CANCELED = 7;
+    private static final int MSG_BUTTON_POSITIVE = 8;
+
+    private FingerprintDialogView mDialogView;
+    private WindowManager mWindowManager;
+    private IBiometricPromptReceiver mReceiver;
+    private boolean mDialogShowing;
+    private Callback mCallback = new Callback();
+
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case MSG_SHOW_DIALOG:
+                    handleShowDialog((SomeArgs) msg.obj);
+                    break;
+                case MSG_BIOMETRIC_AUTHENTICATED:
+                    handleBiometricAuthenticated();
+                    break;
+                case MSG_BIOMETRIC_HELP:
+                    handleBiometricHelp((String) msg.obj);
+                    break;
+                case MSG_BIOMETRIC_ERROR:
+                    handleBiometricError((String) msg.obj);
+                    break;
+                case MSG_HIDE_DIALOG:
+                    handleHideDialog((Boolean) msg.obj);
+                    break;
+                case MSG_BUTTON_NEGATIVE:
+                    handleButtonNegative();
+                    break;
+                case MSG_USER_CANCELED:
+                    handleUserCanceled();
+                    break;
+                case MSG_BUTTON_POSITIVE:
+                    handleButtonPositive();
+                    break;
+            }
+        }
+    };
+
+    private class Callback implements DialogViewCallback {
+        @Override
+        public void onUserCanceled() {
+            mHandler.obtainMessage(BiometricDialogImpl.MSG_USER_CANCELED).sendToTarget();
+        }
+
+        @Override
+        public void onErrorShown() {
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_HIDE_DIALOG,
+                    false /* userCanceled */), BiometricPrompt.HIDE_DIALOG_DELAY);
+        }
+
+        @Override
+        public void onNegativePressed() {
+            mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget();
+        }
+
+        @Override
+        public void onPositivePressed() {
+            mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_POSITIVE).sendToTarget();
+        }
+    }
+
+    @Override
+    public void start() {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+            return;
+        }
+        getComponent(CommandQueue.class).addCallbacks(this);
+        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+        mDialogView = new FingerprintDialogView(mContext, mCallback);
+    }
+
+    @Override
+    public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
+        if (DEBUG) Log.d(TAG, "showBiometricDialog");
+        // Remove these messages as they are part of the previous client
+        mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
+        mHandler.removeMessages(MSG_BIOMETRIC_HELP);
+        mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = bundle;
+        args.arg2 = receiver;
+        mHandler.obtainMessage(MSG_SHOW_DIALOG, args).sendToTarget();
+    }
+
+    @Override
+    public void onBiometricAuthenticated() {
+        if (DEBUG) Log.d(TAG, "onBiometricAuthenticated");
+        mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
+    }
+
+    @Override
+    public void onBiometricHelp(String message) {
+        if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
+        mHandler.obtainMessage(MSG_BIOMETRIC_HELP, message).sendToTarget();
+    }
+
+    @Override
+    public void onBiometricError(String error) {
+        if (DEBUG) Log.d(TAG, "onBiometricError: " + error);
+        mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, error).sendToTarget();
+    }
+
+    @Override
+    public void hideBiometricDialog() {
+        if (DEBUG) Log.d(TAG, "hideBiometricDialog");
+        mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
+    }
+
+    private void handleShowDialog(SomeArgs args) {
+        if (DEBUG) Log.d(TAG, "handleShowDialog, isAnimatingAway: "
+                + mDialogView.isAnimatingAway());
+        if (mDialogView.isAnimatingAway()) {
+            mDialogView.forceRemove();
+        } else if (mDialogShowing) {
+            Log.w(TAG, "Dialog already showing");
+            return;
+        }
+        mReceiver = (IBiometricPromptReceiver) args.arg2;
+        mDialogView.setBundle((Bundle)args.arg1);
+        mWindowManager.addView(mDialogView, mDialogView.getLayoutParams());
+        mDialogShowing = true;
+    }
+
+    private void handleBiometricAuthenticated() {
+        if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated");
+
+        // TODO: announce correct string depending on modality
+        mDialogView.announceForAccessibility(
+                mContext.getResources().getText(
+                        com.android.internal.R.string.fingerprint_authenticated));
+        handleHideDialog(false /* userCanceled */);
+    }
+
+    private void handleBiometricHelp(String message) {
+        if (DEBUG) Log.d(TAG, "handleBiometricHelp: " + message);
+        mDialogView.showHelpMessage(message);
+    }
+
+    private void handleBiometricError(String error) {
+        if (DEBUG) Log.d(TAG, "handleBiometricError: " + error);
+        if (!mDialogShowing) {
+            if (DEBUG) Log.d(TAG, "Dialog already dismissed");
+            return;
+        }
+        mDialogView.showErrorMessage(error);
+    }
+
+    private void handleHideDialog(boolean userCanceled) {
+        if (DEBUG) Log.d(TAG, "handleHideDialog, userCanceled: " + userCanceled);
+        if (!mDialogShowing) {
+            // This can happen if there's a race and we get called from both
+            // onAuthenticated and onError, etc.
+            Log.w(TAG, "Dialog already dismissed, userCanceled: " + userCanceled);
+            return;
+        }
+        if (userCanceled) {
+            try {
+                mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException when hiding dialog", e);
+            }
+        }
+        mReceiver = null;
+        mDialogShowing = false;
+        mDialogView.startDismiss();
+    }
+
+    private void handleButtonNegative() {
+        if (mReceiver == null) {
+            Log.e(TAG, "Receiver is null");
+            return;
+        }
+        try {
+            mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Remote exception when handling negative button", e);
+        }
+        handleHideDialog(false /* userCanceled */);
+    }
+
+    private void handleButtonPositive() {
+        if (mReceiver == null) {
+            Log.e(TAG, "Receiver is null");
+            return;
+        }
+        try {
+            mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_POSITIVE);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Remote exception when handling positive button", e);
+        }
+        handleHideDialog(false /* userCanceled */);
+    }
+
+    private void handleUserCanceled() {
+        handleHideDialog(true /* userCanceled */);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
new file mode 100644
index 0000000..f388d9c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.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 com.android.systemui.biometrics;
+
+/**
+ * Callback interface for dialog views. These should be implemented by the controller (e.g.
+ * FingerprintDialogImpl) and passed into their views (e.g. FingerprintDialogView).
+ */
+public interface DialogViewCallback {
+    /**
+     * Invoked when the user cancels authentication by tapping outside the prompt, etc. The dialog
+     * should be dismissed.
+     */
+    void onUserCanceled();
+
+    /**
+     * Invoked when an error is shown. The dialog should be dismissed after a set amount of time.
+     */
+    void onErrorShown();
+
+    /**
+     * Invoked when the negative button is pressed. The client should be notified and the dialog
+     * should be dismissed.
+     */
+    void onNegativePressed();
+
+    /**
+     * Invoked when the positive button is pressed. The client should be notified and the dialog
+     * should be dismissed.
+     */
+    void onPositivePressed();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
new file mode 100644
index 0000000..68c2c42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
@@ -0,0 +1,400 @@
+/*
+ * 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.biometrics;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.Interpolator;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.systemui.Interpolators;
+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,
+ * and positive/negative buttons.
+ */
+public class FingerprintDialogView extends LinearLayout {
+
+    private static final String TAG = "FingerprintDialogView";
+
+    private static final int ANIMATION_DURATION_SHOW = 250; // ms
+    private static final int ANIMATION_DURATION_AWAY = 350; // ms
+
+    private static final int MSG_CLEAR_MESSAGE = 1;
+
+    private static final int STATE_NONE = 0;
+    private static final int STATE_FINGERPRINT = 1;
+    private static final int STATE_FINGERPRINT_ERROR = 2;
+    private static final int STATE_FINGERPRINT_AUTHENTICATED = 3;
+
+    private final IBinder mWindowToken = new Binder();
+    private final Interpolator mLinearOutSlowIn;
+    private final WindowManager mWindowManager;
+    private final float mAnimationTranslationOffset;
+    private final int mErrorColor;
+    private final int mTextColor;
+    private final int mFingerprintColor;
+    private final float mDisplayWidth;
+    private final DialogViewCallback mCallback;
+
+    private ViewGroup mLayout;
+    private final TextView mErrorText;
+    private Bundle mBundle;
+    private final LinearLayout mDialog;
+    private int mLastState;
+    private boolean mAnimatingAway;
+    private boolean mWasForceRemoved;
+
+    private final Runnable mShowAnimationRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mLayout.animate()
+                    .alpha(1f)
+                    .setDuration(ANIMATION_DURATION_SHOW)
+                    .setInterpolator(mLinearOutSlowIn)
+                    .withLayer()
+                    .start();
+            mDialog.animate()
+                    .translationY(0)
+                    .setDuration(ANIMATION_DURATION_SHOW)
+                    .setInterpolator(mLinearOutSlowIn)
+                    .withLayer()
+                    .start();
+        }
+    };
+
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case MSG_CLEAR_MESSAGE:
+                    handleClearMessage();
+                    break;
+                default:
+                    Log.e(TAG, "Unhandled message: " + msg.what);
+                    break;
+            }
+        }
+    };
+
+    public FingerprintDialogView(Context context, DialogViewCallback callback) {
+        super(context);
+        mCallback = callback;
+        mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
+        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+        mAnimationTranslationOffset = getResources()
+                .getDimension(R.dimen.fingerprint_dialog_animation_translation_offset);
+        mErrorColor = Color.parseColor(
+                getResources().getString(R.color.fingerprint_dialog_error_color));
+        mTextColor = Color.parseColor(
+                getResources().getString(R.color.fingerprint_dialog_text_light_color));
+        mFingerprintColor = Color.parseColor(
+                getResources().getString(R.color.fingerprint_dialog_fingerprint_color));
+
+        DisplayMetrics metrics = new DisplayMetrics();
+        mWindowManager.getDefaultDisplay().getMetrics(metrics);
+        mDisplayWidth = metrics.widthPixels;
+
+        // Create the dialog
+        LayoutInflater factory = LayoutInflater.from(getContext());
+        mLayout = (ViewGroup) factory.inflate(R.layout.fingerprint_dialog, this, false);
+        addView(mLayout);
+
+        mDialog = mLayout.findViewById(R.id.dialog);
+
+        mErrorText = mLayout.findViewById(R.id.error);
+
+        mLayout.setOnKeyListener(new View.OnKeyListener() {
+            boolean downPressed = false;
+            @Override
+            public boolean onKey(View v, int keyCode, KeyEvent event) {
+                if (keyCode != KeyEvent.KEYCODE_BACK) {
+                    return false;
+                }
+                if (event.getAction() == KeyEvent.ACTION_DOWN && downPressed == false) {
+                    downPressed = true;
+                } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                    downPressed = false;
+                } else if (event.getAction() == KeyEvent.ACTION_UP && downPressed == true) {
+                    downPressed = false;
+                    mCallback.onUserCanceled();
+                }
+                return true;
+            }
+        });
+
+        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);
+
+        setDismissesDialog(space);
+        setDismissesDialog(leftSpace);
+        setDismissesDialog(rightSpace);
+
+        negative.setOnClickListener((View v) -> {
+            mCallback.onNegativePressed();
+        });
+
+        positive.setOnClickListener((View v) -> {
+            mCallback.onPositivePressed();
+        });
+
+        mLayout.setFocusableInTouchMode(true);
+        mLayout.requestFocus();
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        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 Button positive = mLayout.findViewById(R.id.button1);
+
+        mDialog.getLayoutParams().width = (int) mDisplayWidth;
+
+        mLastState = STATE_NONE;
+        updateFingerprintIcon(STATE_FINGERPRINT);
+
+        title.setText(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE));
+        title.setSelected(true);
+
+        final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
+        if (TextUtils.isEmpty(subtitleText)) {
+            subtitle.setVisibility(View.GONE);
+        } else {
+            subtitle.setVisibility(View.VISIBLE);
+            subtitle.setText(subtitleText);
+        }
+
+        final CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
+        if (TextUtils.isEmpty(descriptionText)) {
+            description.setVisibility(View.GONE);
+        } else {
+            description.setVisibility(View.VISIBLE);
+            description.setText(descriptionText);
+        }
+
+        negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
+
+        final CharSequence positiveText =
+                mBundle.getCharSequence(BiometricPrompt.KEY_POSITIVE_TEXT);
+        positive.setText(positiveText); // needs to be set for marquee to work
+        if (positiveText != null) {
+            positive.setVisibility(View.VISIBLE);
+        } else {
+            positive.setVisibility(View.GONE);
+        }
+
+        if (!mWasForceRemoved) {
+            // Dim the background and slide the dialog up
+            mDialog.setTranslationY(mAnimationTranslationOffset);
+            mLayout.setAlpha(0f);
+            postOnAnimation(mShowAnimationRunnable);
+        } else {
+            // Show the dialog immediately
+            mLayout.animate().cancel();
+            mDialog.animate().cancel();
+            mDialog.setAlpha(1.0f);
+            mDialog.setTranslationY(0);
+            mLayout.setAlpha(1.0f);
+        }
+        mWasForceRemoved = false;
+    }
+
+    private void setDismissesDialog(View v) {
+        v.setClickable(true);
+        v.setOnTouchListener((View view, MotionEvent event) -> {
+            mCallback.onUserCanceled();
+            return true;
+        });
+    }
+
+    public void startDismiss() {
+        mAnimatingAway = true;
+
+        final Runnable endActionRunnable = new Runnable() {
+            @Override
+            public void run() {
+                mWindowManager.removeView(FingerprintDialogView.this);
+                mAnimatingAway = false;
+            }
+        };
+
+        postOnAnimation(new Runnable() {
+            @Override
+            public void run() {
+                mLayout.animate()
+                        .alpha(0f)
+                        .setDuration(ANIMATION_DURATION_AWAY)
+                        .setInterpolator(mLinearOutSlowIn)
+                        .withLayer()
+                        .start();
+                mDialog.animate()
+                        .translationY(mAnimationTranslationOffset)
+                        .setDuration(ANIMATION_DURATION_AWAY)
+                        .setInterpolator(mLinearOutSlowIn)
+                        .withLayer()
+                        .withEndAction(endActionRunnable)
+                        .start();
+            }
+        });
+    }
+
+    /**
+     * Force remove the window, cancelling any animation that's happening. This should only be
+     * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
+     * will cause the dialog to show without an animation the next time it's attached.
+     */
+    public void forceRemove() {
+        mLayout.animate().cancel();
+        mDialog.animate().cancel();
+        mWindowManager.removeView(FingerprintDialogView.this);
+        mAnimatingAway = false;
+        mWasForceRemoved = true;
+    }
+
+    public boolean isAnimatingAway() {
+        return mAnimatingAway;
+    }
+
+    public void setBundle(Bundle bundle) {
+        mBundle = bundle;
+    }
+
+    // Clears the temporary message and shows the help message.
+    private void handleClearMessage() {
+        updateFingerprintIcon(STATE_FINGERPRINT);
+        mErrorText.setText(R.string.fingerprint_dialog_touch_sensor);
+        mErrorText.setTextColor(mTextColor);
+    }
+
+    // Shows an error/help message
+    private void showTemporaryMessage(String message) {
+        mHandler.removeMessages(MSG_CLEAR_MESSAGE);
+        updateFingerprintIcon(STATE_FINGERPRINT_ERROR);
+        mErrorText.setText(message);
+        mErrorText.setTextColor(mErrorColor);
+        mErrorText.setContentDescription(message);
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_MESSAGE),
+                BiometricPrompt.HIDE_DIALOG_DELAY);
+    }
+
+    public void showHelpMessage(String message) {
+        showTemporaryMessage(message);
+    }
+
+    public void showErrorMessage(String error) {
+        showTemporaryMessage(error);
+        mCallback.onErrorShown();
+    }
+
+    private void updateFingerprintIcon(int newState) {
+        Drawable icon  = getAnimationForTransition(mLastState, newState);
+
+        if (icon == null) {
+            Log.e(TAG, "Animation not found");
+            return;
+        }
+
+        final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
+                ? (AnimatedVectorDrawable) icon
+                : null;
+
+        final ImageView fingerprint_icon = mLayout.findViewById(R.id.fingerprint_icon);
+        fingerprint_icon.setImageDrawable(icon);
+
+        if (animation != null && shouldAnimateForTransition(mLastState, newState)) {
+            animation.forceAnimationOnUI();
+            animation.start();
+        }
+
+        mLastState = newState;
+    }
+
+    private boolean shouldAnimateForTransition(int oldState, int newState) {
+        if (oldState == STATE_NONE && newState == STATE_FINGERPRINT) {
+            return false;
+        } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) {
+            return true;
+        } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
+            return true;
+        } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_AUTHENTICATED) {
+            // TODO(b/77328470): add animation when fingerprint is authenticated
+            return false;
+        }
+        return false;
+    }
+
+    private Drawable getAnimationForTransition(int oldState, int newState) {
+        int iconRes;
+        if (oldState == STATE_NONE && newState == STATE_FINGERPRINT) {
+            iconRes = R.drawable.fingerprint_dialog_fp_to_error;
+        } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) {
+            iconRes = R.drawable.fingerprint_dialog_fp_to_error;
+        } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
+            iconRes = R.drawable.fingerprint_dialog_error_to_fp;
+        } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_AUTHENTICATED) {
+            // TODO(b/77328470): add animation when fingerprint is authenticated
+            iconRes = R.drawable.fingerprint_dialog_error_to_fp;
+        }
+        else {
+            return null;
+        }
+        return mContext.getDrawable(iconRes);
+    }
+
+    public WindowManager.LayoutParams getLayoutParams() {
+        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+                PixelFormat.TRANSLUCENT);
+        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.setTitle("FingerprintDialogView");
+        lp.token = mWindowToken;
+        return lp;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index a89a8ef..d6a1cf0 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -18,9 +18,9 @@
 import android.content.Context;
 import android.service.notification.StatusBarNotification;
 
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 
 public class CarNotificationEntryManager extends NotificationEntryManager {
     public CarNotificationEntryManager(Context context) {
@@ -29,7 +29,7 @@
 
     /**
      * Returns the
-     * {@link com.android.systemui.statusbar.ExpandableNotificationRow.LongPressListener} that will
+     * {@link ExpandableNotificationRow.LongPressListener} that will
      * be triggered when a notification card is long-pressed.
      */
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
index cc9fec0..a015a18 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
@@ -17,15 +17,13 @@
 
 import android.content.Context;
 import android.util.ArrayMap;
-import android.view.View;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.Dependency.DependencyProvider;
 import com.android.systemui.SystemUIFactory;
-import com.android.systemui.statusbar.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.car.CarFacetButtonController;
-import com.android.systemui.statusbar.car.CarStatusBar;
 import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.car.hvac.HvacController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index e11fe4e..d5b54f9 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -37,7 +37,7 @@
 
     public static final long DURATION = 1133;
     private static final String TAG = "WirelessChargingView";
-    private static final boolean LOCAL_LOGV = false;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final WirelessChargingView mCurrentWirelessChargingView;
     private static WirelessChargingView mPreviousWirelessChargingView;
@@ -95,7 +95,6 @@
         private final Handler mHandler;
 
         private int mGravity;
-
         private View mView;
         private View mNextView;
         private WindowManager mWM;
@@ -112,7 +111,7 @@
             params.width = WindowManager.LayoutParams.MATCH_PARENT;
             params.format = PixelFormat.TRANSLUCENT;
 
-            params.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+            params.type = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
             params.setTitle("Charging Animation");
             params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                     | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
@@ -151,20 +150,20 @@
         }
 
         public void show() {
-            if (LOCAL_LOGV) Log.v(TAG, "SHOW: " + this);
+            if (DEBUG) Slog.d(TAG, "SHOW: " + this);
             mHandler.obtainMessage(SHOW).sendToTarget();
         }
 
         public void hide(long duration) {
             mHandler.removeMessages(HIDE);
 
-            if (LOCAL_LOGV) Log.v(TAG, "HIDE: " + this);
+            if (DEBUG) Slog.d(TAG, "HIDE: " + this);
             mHandler.sendMessageDelayed(Message.obtain(mHandler, HIDE), duration);
         }
 
         private void handleShow() {
-            if (LOCAL_LOGV) {
-                Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView + " mNextView="
+            if (DEBUG) {
+                Slog.d(TAG, "HANDLE SHOW: " + this + " mView=" + mView + " mNextView="
                         + mNextView);
             }
 
@@ -182,10 +181,10 @@
                 mParams.hideTimeoutMilliseconds = DURATION;
 
                 if (mView.getParent() != null) {
-                    if (LOCAL_LOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
+                    if (DEBUG) Slog.d(TAG, "REMOVE! " + mView + " in " + this);
                     mWM.removeView(mView);
                 }
-                if (LOCAL_LOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
+                if (DEBUG) Slog.d(TAG, "ADD! " + mView + " in " + this);
 
                 try {
                     if (mCallback != null) {
@@ -199,10 +198,10 @@
         }
 
         private void handleHide() {
-            if (LOCAL_LOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
+            if (DEBUG) Slog.d(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
             if (mView != null) {
                 if (mView.getParent() != null) {
-                    if (LOCAL_LOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
+                    if (DEBUG) Slog.d(TAG, "REMOVE! " + mView + " in " + this);
                     if (mCallback != null) {
                         mCallback.onAnimationEnded();
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index c8e83de..ec15087 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -20,10 +20,12 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.graphics.Color;
+import android.graphics.drawable.Animatable;
 import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.android.systemui.Interpolators;
@@ -58,19 +60,22 @@
 
     private void init(Context context, AttributeSet attrs, int batteryLevel, boolean isDozing) {
         final int mBatteryLevel = batteryLevel;
-        inflate(context, R.layout.wireless_charging_layout, this);
+
+        // set style based on background
+        int style = R.style.ChargingAnim_WallpaperBackground;
+        if (isDozing) {
+            style = R.style.ChargingAnim_DarkBackground;
+        }
+
+        inflate(new ContextThemeWrapper(context, style), R.layout.wireless_charging_layout, this);
 
         // where the circle animation occurs:
-        final WirelessChargingView mChargingView = findViewById(R.id.wireless_charging_view);
+        final ImageView chargingView = findViewById(R.id.wireless_charging_view);
+        final Animatable chargingAnimation = (Animatable) chargingView.getDrawable();
 
         // amount of battery:
         final TextView mPercentage = findViewById(R.id.wireless_charging_percentage);
 
-        if (isDozing) {
-            mChargingView.setPaintColor(Color.WHITE);
-            mPercentage.setTextColor(Color.WHITE);
-        }
-
         if (batteryLevel != UNKNOWN_BATTERY_LEVEL) {
             mPercentage.setText(NumberFormat.getPercentInstance().format(mBatteryLevel / 100f));
             mPercentage.setAlpha(0);
@@ -106,17 +111,10 @@
         textFadeAnimator.setInterpolator(Interpolators.LINEAR);
         textFadeAnimator.setStartDelay(chargingAnimationFadeStartOffset);
 
-        // Animation Opacity: wireless charging circle animation fades from 1 to 0 opacity
-        ValueAnimator circleFadeAnimator = ObjectAnimator.ofFloat(mChargingView, "alpha",
-                1, 0);
-        circleFadeAnimator.setDuration(chargingAnimationFadeDuration);
-        circleFadeAnimator.setInterpolator(Interpolators.LINEAR);
-        circleFadeAnimator.setStartDelay(chargingAnimationFadeStartOffset);
-
         // play all animations together
         AnimatorSet animatorSet = new AnimatorSet();
-        animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator,
-                circleFadeAnimator);
+        animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator);
+        chargingAnimation.start();
         animatorSet.start();
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java
deleted file mode 100644
index ad6dfa4..0000000
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java
+++ /dev/null
@@ -1,182 +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.charging;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-import com.android.settingslib.Utils;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-
-final class WirelessChargingView extends View {
-
-    private Interpolator mInterpolator;
-    private float mPathGone;
-    private float mInterpolatedPathGone;
-    private long mAnimationStartTime;
-    private long mScaleDotsDuration;
-
-    private boolean mFinishedAnimatingDots = false;
-    private int mNumDots;
-
-    private double mAngleOffset;
-    private double mCurrAngleOffset;
-
-    private int mDotsRadiusStart;
-    private int mDotsRadiusEnd;
-    private int mCurrDotRadius;
-
-    private int mMainCircleStartRadius;
-    private int mMainCircleEndRadius;
-    private int mMainCircleCurrentRadius;
-
-    private int mCenterX;
-    private int mCenterY;
-
-    private Paint mPaint;
-    private Context mContext;
-
-    public WirelessChargingView(Context context) {
-        super(context);
-        init(context, null);
-    }
-
-    public WirelessChargingView(Context context, AttributeSet attr) {
-        super(context, attr);
-        init(context, attr);
-    }
-
-    public WirelessChargingView(Context context, AttributeSet attr, int styleAttr) {
-        super(context, attr, styleAttr);
-        init(context, attr);
-    }
-
-    public void init(Context context, AttributeSet attr) {
-        mContext = context;
-
-        mDotsRadiusStart = context.getResources().getDimensionPixelSize(
-                R.dimen.wireless_charging_dots_radius_start);
-        mDotsRadiusEnd = context.getResources().getDimensionPixelSize(
-                R.dimen.wireless_charging_dots_radius_end);
-
-        mMainCircleStartRadius = context.getResources().getDimensionPixelSize(
-                R.dimen.wireless_charging_circle_radius_start);
-        mMainCircleEndRadius = context.getResources().getDimensionPixelSize(
-                R.dimen.wireless_charging_circle_radius_end);
-        mMainCircleCurrentRadius = mMainCircleStartRadius;
-
-        mAngleOffset = context.getResources().getInteger(R.integer.wireless_charging_angle_offset);
-        mScaleDotsDuration = (long) context.getResources().getInteger(
-                R.integer.wireless_charging_scale_dots_duration);
-        mNumDots = context.getResources().getInteger(R.integer.wireless_charging_num_dots);
-
-        setupPaint();
-        mInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
-    }
-
-    private void setupPaint() {
-        mPaint = new Paint();
-        mPaint.setColor(Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor));
-    }
-
-    public void setPaintColor(int color) {
-        mPaint.setColor(color);
-    }
-
-    @Override
-    protected void onDraw(final Canvas canvas) {
-        super.onDraw(canvas);
-
-        if (mAnimationStartTime == 0) {
-            mAnimationStartTime = System.currentTimeMillis();
-        }
-
-        updateDrawingParameters();
-        drawCircles(canvas);
-
-        if (!mFinishedAnimatingDots) {
-            invalidate();
-        }
-    }
-
-    /**
-     * Draws a larger circle of radius {@link WirelessChargingView#mMainCircleEndRadius} composed of
-     * {@link WirelessChargingView#mNumDots} smaller circles
-     * @param canvas
-     */
-    private void drawCircles(Canvas canvas) {
-        mCenterX = canvas.getWidth() / 2;
-        mCenterY = canvas.getHeight() / 2;
-
-        // draws mNumDots to compose a larger, main circle
-        for (int circle = 0; circle < mNumDots; circle++) {
-            double angle = ((mCurrAngleOffset) * Math.PI / 180) + (circle * ((2 * Math.PI)
-                    / mNumDots));
-
-            int x = (int) (mCenterX + Math.cos(angle) * (mMainCircleCurrentRadius +
-                    mCurrDotRadius));
-            int y = (int) (mCenterY + Math.sin(angle) * (mMainCircleCurrentRadius +
-                    mCurrDotRadius));
-
-            canvas.drawCircle(x, y, mCurrDotRadius, mPaint);
-        }
-
-        if (mMainCircleCurrentRadius >= mMainCircleEndRadius) {
-            mFinishedAnimatingDots = true;
-        }
-    }
-
-    private void updateDrawingParameters() {
-        long now = System.currentTimeMillis();
-        long timeSinceStart = now - mAnimationStartTime;
-        mPathGone = getPathGone(now);
-        mInterpolatedPathGone = mInterpolator.getInterpolation(mPathGone);
-
-        // Position Dots: update main circle radius (that the dots compose)
-        if (mPathGone < 1.0f) {
-            mMainCircleCurrentRadius = mMainCircleStartRadius + (int) (mInterpolatedPathGone *
-                    (mMainCircleEndRadius - mMainCircleStartRadius));
-        } else {
-            mMainCircleCurrentRadius = mMainCircleEndRadius;
-        }
-
-        // Scale Dots: update dot radius
-        if (timeSinceStart < mScaleDotsDuration) {
-            mCurrDotRadius = mDotsRadiusStart + (int) (mInterpolator.getInterpolation((float)
-                    timeSinceStart / mScaleDotsDuration) * (mDotsRadiusEnd - mDotsRadiusStart));
-        } else {
-            mCurrDotRadius = mDotsRadiusEnd;
-        }
-
-        // Rotation Dot Group: dots rotate from 0 to 20 degrees
-        mCurrAngleOffset = mAngleOffset * mPathGone;
-    }
-
-    /**
-     * @return decimal depicting how far along the creation of the larger circle (of circles) is
-     * For values < 1.0, the larger circle is being drawn
-     * For values > 1.0 the larger circle has been drawn and further animation can occur
-     */
-    private float getPathGone(long now) {
-        return (float) (now - mAnimationStartTime) / (WirelessChargingAnimation.DURATION);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index 9a43d9e..3c8a461 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.RemoteException;
-import android.os.Trace;
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.Display;
@@ -46,7 +45,7 @@
 public class SysuiColorExtractor extends ColorExtractor implements Dumpable {
     private static final String TAG = "SysuiColorExtractor";
     private boolean mWallpaperVisible;
-    private boolean mMediaBackdropVisible;
+    private boolean mHasBackdrop;
     // Colors to return when the wallpaper isn't visible
     private final GradientColors mWpHiddenColors;
 
@@ -165,7 +164,7 @@
                 return mWpHiddenColors;
             }
         } else {
-            if (mMediaBackdropVisible) {
+            if (mHasBackdrop) {
                 return mWpHiddenColors;
             } else {
                 return super.getColors(which, type);
@@ -181,9 +180,9 @@
         }
     }
 
-    public void setMediaBackdropVisible(boolean visible) {
-        if (mMediaBackdropVisible != visible) {
-            mMediaBackdropVisible = visible;
+    public void setHasBackdrop(boolean hasBackdrop) {
+        if (mHasBackdrop != hasBackdrop) {
+            mHasBackdrop = hasBackdrop;
             triggerColorsChanged(WallpaperManager.FLAG_LOCK);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 4bb4e79..36b2347 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -23,6 +23,8 @@
 import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.Trace;
+import android.os.UserHandle;
+import android.provider.Settings;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -111,7 +113,7 @@
             int brightness = computeBrightness(mLastSensorValue);
             boolean brightnessReady = brightness > 0;
             if (brightnessReady) {
-                mDozeService.setDozeScreenBrightness(brightness);
+                mDozeService.setDozeScreenBrightness(clampToUserSetting(brightness));
             }
 
             int scrimOpacity = -1;
@@ -150,10 +152,17 @@
     }
 
     private void resetBrightnessToDefault() {
-        mDozeService.setDozeScreenBrightness(mDefaultDozeBrightness);
+        mDozeService.setDozeScreenBrightness(clampToUserSetting(mDefaultDozeBrightness));
         mDozeHost.setAodDimmingScrim(0f);
     }
 
+    private int clampToUserSetting(int brightness) {
+        int userSetting = Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS, Integer.MAX_VALUE,
+                UserHandle.USER_CURRENT);
+        return Math.min(brightness, userSetting);
+    }
+
     private void setLightSensorEnabled(boolean enabled) {
         if (enabled && !mRegistered && mLightSensor != null) {
             // Wait until we get an event from the sensor until indicating ready.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index b7ff984..7863245 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -33,7 +33,6 @@
 import com.android.systemui.util.wakelock.WakeLock;
 
 import java.util.Calendar;
-import java.util.GregorianCalendar;
 
 /**
  * The policy controlling doze.
@@ -113,9 +112,7 @@
                     // The display buffers will be empty and need to be filled.
                     mHost.dozeTimeTick();
                     // The first frame may arrive when the display isn't ready yet.
-                    mHandler.postDelayed(mWakeLock.wrap(mHost::dozeTimeTick), 100);
-                    // The the delayed frame may arrive when the display isn't ready yet either.
-                    mHandler.postDelayed(mWakeLock.wrap(mHost::dozeTimeTick), 1000);
+                    mHandler.postDelayed(mWakeLock.wrap(mHost::dozeTimeTick), 500);
                 }
                 scheduleTimeTick();
                 break;
@@ -184,7 +181,7 @@
     }
 
     private long roundToNextMinute(long timeInMillis) {
-        Calendar calendar = GregorianCalendar.getInstance();
+        Calendar calendar = Calendar.getInstance();
         calendar.setTimeInMillis(timeInMillis);
         calendar.set(Calendar.MILLISECOND, 0);
         calendar.set(Calendar.SECOND, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
deleted file mode 100644
index a81043e..0000000
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
+++ /dev/null
@@ -1,226 +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.fingerprint;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.WindowManager;
-
-import com.android.internal.os.SomeArgs;
-import com.android.systemui.SystemUI;
-import com.android.systemui.statusbar.CommandQueue;
-
-public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Callbacks {
-    private static final String TAG = "FingerprintDialogImpl";
-    private static final boolean DEBUG = true;
-
-    protected static final int MSG_SHOW_DIALOG = 1;
-    protected static final int MSG_FINGERPRINT_AUTHENTICATED = 2;
-    protected static final int MSG_FINGERPRINT_HELP = 3;
-    protected static final int MSG_FINGERPRINT_ERROR = 4;
-    protected static final int MSG_HIDE_DIALOG = 5;
-    protected static final int MSG_BUTTON_NEGATIVE = 6;
-    protected static final int MSG_USER_CANCELED = 7;
-    protected static final int MSG_BUTTON_POSITIVE = 8;
-    protected static final int MSG_CLEAR_MESSAGE = 9;
-
-
-    private FingerprintDialogView mDialogView;
-    private WindowManager mWindowManager;
-    private IBiometricPromptReceiver mReceiver;
-    private boolean mDialogShowing;
-
-    private Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch(msg.what) {
-                case MSG_SHOW_DIALOG:
-                    handleShowDialog((SomeArgs) msg.obj);
-                    break;
-                case MSG_FINGERPRINT_AUTHENTICATED:
-                    handleFingerprintAuthenticated();
-                    break;
-                case MSG_FINGERPRINT_HELP:
-                    handleFingerprintHelp((String) msg.obj);
-                    break;
-                case MSG_FINGERPRINT_ERROR:
-                    handleFingerprintError((String) msg.obj);
-                    break;
-                case MSG_HIDE_DIALOG:
-                    handleHideDialog((Boolean) msg.obj);
-                    break;
-                case MSG_BUTTON_NEGATIVE:
-                    handleButtonNegative();
-                    break;
-                case MSG_USER_CANCELED:
-                    handleUserCanceled();
-                    break;
-                case MSG_BUTTON_POSITIVE:
-                    handleButtonPositive();
-                    break;
-                case MSG_CLEAR_MESSAGE:
-                    handleClearMessage();
-                    break;
-            }
-        }
-    };
-
-    @Override
-    public void start() {
-        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
-            return;
-        }
-        getComponent(CommandQueue.class).addCallbacks(this);
-        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        mDialogView = new FingerprintDialogView(mContext, mHandler);
-    }
-
-    @Override
-    public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
-        if (DEBUG) Log.d(TAG, "showFingerprintDialog");
-        // Remove these messages as they are part of the previous client
-        mHandler.removeMessages(MSG_FINGERPRINT_ERROR);
-        mHandler.removeMessages(MSG_FINGERPRINT_HELP);
-        mHandler.removeMessages(MSG_FINGERPRINT_AUTHENTICATED);
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = bundle;
-        args.arg2 = receiver;
-        mHandler.obtainMessage(MSG_SHOW_DIALOG, args).sendToTarget();
-    }
-
-    @Override
-    public void onFingerprintAuthenticated() {
-        if (DEBUG) Log.d(TAG, "onFingerprintAuthenticated");
-        mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATED).sendToTarget();
-    }
-
-    @Override
-    public void onFingerprintHelp(String message) {
-        if (DEBUG) Log.d(TAG, "onFingerprintHelp: " + message);
-        mHandler.obtainMessage(MSG_FINGERPRINT_HELP, message).sendToTarget();
-    }
-
-    @Override
-    public void onFingerprintError(String error) {
-        if (DEBUG) Log.d(TAG, "onFingerprintError: " + error);
-        mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, error).sendToTarget();
-    }
-
-    @Override
-    public void hideFingerprintDialog() {
-        if (DEBUG) Log.d(TAG, "hideFingerprintDialog");
-        mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
-    }
-
-    private void handleShowDialog(SomeArgs args) {
-        if (DEBUG) Log.d(TAG, "handleShowDialog, isAnimatingAway: "
-                + mDialogView.isAnimatingAway());
-        if (mDialogView.isAnimatingAway()) {
-            mDialogView.forceRemove();
-        } else if (mDialogShowing) {
-            Log.w(TAG, "Dialog already showing");
-            return;
-        }
-        mReceiver = (IBiometricPromptReceiver) args.arg2;
-        mDialogView.setBundle((Bundle)args.arg1);
-        mWindowManager.addView(mDialogView, mDialogView.getLayoutParams());
-        mDialogShowing = true;
-    }
-
-    private void handleFingerprintAuthenticated() {
-        if (DEBUG) Log.d(TAG, "handleFingerprintAuthenticated");
-        mDialogView.announceForAccessibility(
-                mContext.getResources().getText(
-                        com.android.internal.R.string.fingerprint_authenticated));
-        handleHideDialog(false /* userCanceled */);
-    }
-
-    private void handleFingerprintHelp(String message) {
-        if (DEBUG) Log.d(TAG, "handleFingerprintHelp: " + message);
-        mDialogView.showHelpMessage(message);
-    }
-
-    private void handleFingerprintError(String error) {
-        if (DEBUG) Log.d(TAG, "handleFingerprintError: " + error);
-        if (!mDialogShowing) {
-            if (DEBUG) Log.d(TAG, "Dialog already dismissed");
-            return;
-        }
-        mDialogView.showErrorMessage(error);
-    }
-
-    private void handleHideDialog(boolean userCanceled) {
-        if (DEBUG) Log.d(TAG, "handleHideDialog, userCanceled: " + userCanceled);
-        if (!mDialogShowing) {
-            // This can happen if there's a race and we get called from both
-            // onAuthenticated and onError, etc.
-            Log.w(TAG, "Dialog already dismissed, userCanceled: " + userCanceled);
-            return;
-        }
-        if (userCanceled) {
-            try {
-                mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException when hiding dialog", e);
-            }
-        }
-        mReceiver = null;
-        mDialogShowing = false;
-        mDialogView.startDismiss();
-    }
-
-    private void handleButtonNegative() {
-        if (mReceiver == null) {
-            Log.e(TAG, "Receiver is null");
-            return;
-        }
-        try {
-            mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Remote exception when handling negative button", e);
-        }
-        handleHideDialog(false /* userCanceled */);
-    }
-
-    private void handleButtonPositive() {
-        if (mReceiver == null) {
-            Log.e(TAG, "Receiver is null");
-            return;
-        }
-        try {
-            mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_POSITIVE);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Remote exception when handling positive button", e);
-        }
-        handleHideDialog(false /* userCanceled */);
-    }
-
-    private void handleClearMessage() {
-        mDialogView.resetMessage();
-    }
-
-    private void handleUserCanceled() {
-        handleHideDialog(true /* userCanceled */);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
deleted file mode 100644
index 8013a9e..0000000
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
+++ /dev/null
@@ -1,386 +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.fingerprint;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricPrompt;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.text.TextUtils;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.animation.Interpolator;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.systemui.Interpolators;
-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,
- * and positive/negative buttons.
- */
-public class FingerprintDialogView extends LinearLayout {
-
-    private static final String TAG = "FingerprintDialogView";
-
-    private static final int ANIMATION_DURATION_SHOW = 250; // ms
-    private static final int ANIMATION_DURATION_AWAY = 350; // ms
-
-    private static final int STATE_NONE = 0;
-    private static final int STATE_FINGERPRINT = 1;
-    private static final int STATE_FINGERPRINT_ERROR = 2;
-    private static final int STATE_FINGERPRINT_AUTHENTICATED = 3;
-
-    private final IBinder mWindowToken = new Binder();
-    private final Interpolator mLinearOutSlowIn;
-    private final WindowManager mWindowManager;
-    private final float mAnimationTranslationOffset;
-    private final int mErrorColor;
-    private final int mTextColor;
-    private final int mFingerprintColor;
-
-    private ViewGroup mLayout;
-    private final TextView mErrorText;
-    private Handler mHandler;
-    private Bundle mBundle;
-    private final LinearLayout mDialog;
-    private int mLastState;
-    private boolean mAnimatingAway;
-    private boolean mWasForceRemoved;
-
-    private final float mDisplayWidth;
-
-    private final Runnable mShowAnimationRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mLayout.animate()
-                    .alpha(1f)
-                    .setDuration(ANIMATION_DURATION_SHOW)
-                    .setInterpolator(mLinearOutSlowIn)
-                    .withLayer()
-                    .start();
-            mDialog.animate()
-                    .translationY(0)
-                    .setDuration(ANIMATION_DURATION_SHOW)
-                    .setInterpolator(mLinearOutSlowIn)
-                    .withLayer()
-                    .start();
-        }
-    };
-
-    public FingerprintDialogView(Context context, Handler handler) {
-        super(context);
-        mHandler = handler;
-        mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
-        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        mAnimationTranslationOffset = getResources()
-                .getDimension(R.dimen.fingerprint_dialog_animation_translation_offset);
-        mErrorColor = Color.parseColor(
-                getResources().getString(R.color.fingerprint_dialog_error_color));
-        mTextColor = Color.parseColor(
-                getResources().getString(R.color.fingerprint_dialog_text_light_color));
-        mFingerprintColor = Color.parseColor(
-                getResources().getString(R.color.fingerprint_dialog_fingerprint_color));
-
-        DisplayMetrics metrics = new DisplayMetrics();
-        mWindowManager.getDefaultDisplay().getMetrics(metrics);
-        mDisplayWidth = metrics.widthPixels;
-
-        // Create the dialog
-        LayoutInflater factory = LayoutInflater.from(getContext());
-        mLayout = (ViewGroup) factory.inflate(R.layout.fingerprint_dialog, this, false);
-        addView(mLayout);
-
-        mDialog = mLayout.findViewById(R.id.dialog);
-
-        mErrorText = mLayout.findViewById(R.id.error);
-
-        mLayout.setOnKeyListener(new View.OnKeyListener() {
-            boolean downPressed = false;
-            @Override
-            public boolean onKey(View v, int keyCode, KeyEvent event) {
-                if (keyCode != KeyEvent.KEYCODE_BACK) {
-                    return false;
-                }
-                if (event.getAction() == KeyEvent.ACTION_DOWN && downPressed == false) {
-                    downPressed = true;
-                } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
-                    downPressed = false;
-                } else if (event.getAction() == KeyEvent.ACTION_UP && downPressed == true) {
-                    downPressed = false;
-                    mHandler.obtainMessage(FingerprintDialogImpl.MSG_USER_CANCELED).sendToTarget();
-                }
-                return true;
-            }
-        });
-
-        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);
-
-        setDismissesDialog(space);
-        setDismissesDialog(leftSpace);
-        setDismissesDialog(rightSpace);
-
-        negative.setOnClickListener((View v) -> {
-            mHandler.obtainMessage(FingerprintDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget();
-        });
-
-        positive.setOnClickListener((View v) -> {
-            mHandler.obtainMessage(FingerprintDialogImpl.MSG_BUTTON_POSITIVE).sendToTarget();
-        });
-
-        mLayout.setFocusableInTouchMode(true);
-        mLayout.requestFocus();
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        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 Button positive = mLayout.findViewById(R.id.button1);
-
-        mDialog.getLayoutParams().width = (int) mDisplayWidth;
-
-        mLastState = STATE_NONE;
-        updateFingerprintIcon(STATE_FINGERPRINT);
-
-        title.setText(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE));
-        title.setSelected(true);
-
-        final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
-        if (TextUtils.isEmpty(subtitleText)) {
-            subtitle.setVisibility(View.GONE);
-        } else {
-            subtitle.setVisibility(View.VISIBLE);
-            subtitle.setText(subtitleText);
-        }
-
-        final CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
-        if (TextUtils.isEmpty(descriptionText)) {
-            description.setVisibility(View.GONE);
-        } else {
-            description.setVisibility(View.VISIBLE);
-            description.setText(descriptionText);
-        }
-
-        negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
-
-        final CharSequence positiveText =
-                mBundle.getCharSequence(BiometricPrompt.KEY_POSITIVE_TEXT);
-        positive.setText(positiveText); // needs to be set for marquee to work
-        if (positiveText != null) {
-            positive.setVisibility(View.VISIBLE);
-        } else {
-            positive.setVisibility(View.GONE);
-        }
-
-        if (!mWasForceRemoved) {
-            // Dim the background and slide the dialog up
-            mDialog.setTranslationY(mAnimationTranslationOffset);
-            mLayout.setAlpha(0f);
-            postOnAnimation(mShowAnimationRunnable);
-        } else {
-            // Show the dialog immediately
-            mLayout.animate().cancel();
-            mDialog.animate().cancel();
-            mDialog.setAlpha(1.0f);
-            mDialog.setTranslationY(0);
-            mLayout.setAlpha(1.0f);
-        }
-        mWasForceRemoved = false;
-    }
-
-    private void setDismissesDialog(View v) {
-        v.setClickable(true);
-        v.setOnTouchListener((View view, MotionEvent event) -> {
-            mHandler.obtainMessage(FingerprintDialogImpl.MSG_HIDE_DIALOG, true /* userCanceled */)
-                    .sendToTarget();
-            return true;
-        });
-    }
-
-    public void startDismiss() {
-        mAnimatingAway = true;
-
-        final Runnable endActionRunnable = new Runnable() {
-            @Override
-            public void run() {
-                mWindowManager.removeView(FingerprintDialogView.this);
-                mAnimatingAway = false;
-            }
-        };
-
-        postOnAnimation(new Runnable() {
-            @Override
-            public void run() {
-                mLayout.animate()
-                        .alpha(0f)
-                        .setDuration(ANIMATION_DURATION_AWAY)
-                        .setInterpolator(mLinearOutSlowIn)
-                        .withLayer()
-                        .start();
-                mDialog.animate()
-                        .translationY(mAnimationTranslationOffset)
-                        .setDuration(ANIMATION_DURATION_AWAY)
-                        .setInterpolator(mLinearOutSlowIn)
-                        .withLayer()
-                        .withEndAction(endActionRunnable)
-                        .start();
-            }
-        });
-    }
-
-    /**
-     * Force remove the window, cancelling any animation that's happening. This should only be
-     * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
-     * will cause the dialog to show without an animation the next time it's attached.
-     */
-    public void forceRemove() {
-        mLayout.animate().cancel();
-        mDialog.animate().cancel();
-        mWindowManager.removeView(FingerprintDialogView.this);
-        mAnimatingAway = false;
-        mWasForceRemoved = true;
-    }
-
-    public boolean isAnimatingAway() {
-        return mAnimatingAway;
-    }
-
-    public void setBundle(Bundle bundle) {
-        mBundle = bundle;
-    }
-
-    // Clears the temporary message and shows the help message.
-    protected void resetMessage() {
-        updateFingerprintIcon(STATE_FINGERPRINT);
-        mErrorText.setText(R.string.fingerprint_dialog_touch_sensor);
-        mErrorText.setTextColor(mTextColor);
-    }
-
-    // Shows an error/help message
-    private void showTemporaryMessage(String message) {
-        mHandler.removeMessages(FingerprintDialogImpl.MSG_CLEAR_MESSAGE);
-        updateFingerprintIcon(STATE_FINGERPRINT_ERROR);
-        mErrorText.setText(message);
-        mErrorText.setTextColor(mErrorColor);
-        mErrorText.setContentDescription(message);
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_CLEAR_MESSAGE),
-                BiometricPrompt.HIDE_DIALOG_DELAY);
-    }
-
-    public void showHelpMessage(String message) {
-        showTemporaryMessage(message);
-    }
-
-    public void showErrorMessage(String error) {
-        showTemporaryMessage(error);
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_HIDE_DIALOG,
-                false /* userCanceled */), BiometricPrompt.HIDE_DIALOG_DELAY);
-    }
-
-    private void updateFingerprintIcon(int newState) {
-        Drawable icon  = getAnimationForTransition(mLastState, newState);
-
-        if (icon == null) {
-            Log.e(TAG, "Animation not found");
-            return;
-        }
-
-        final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
-                ? (AnimatedVectorDrawable) icon
-                : null;
-
-        final ImageView fingerprint_icon = mLayout.findViewById(R.id.fingerprint_icon);
-        fingerprint_icon.setImageDrawable(icon);
-
-        if (animation != null && shouldAnimateForTransition(mLastState, newState)) {
-            animation.forceAnimationOnUI();
-            animation.start();
-        }
-
-        mLastState = newState;
-    }
-
-    private boolean shouldAnimateForTransition(int oldState, int newState) {
-        if (oldState == STATE_NONE && newState == STATE_FINGERPRINT) {
-            return false;
-        } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) {
-            return true;
-        } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
-            return true;
-        } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_AUTHENTICATED) {
-            // TODO(b/77328470): add animation when fingerprint is authenticated
-            return false;
-        }
-        return false;
-    }
-
-    private Drawable getAnimationForTransition(int oldState, int newState) {
-        int iconRes;
-        if (oldState == STATE_NONE && newState == STATE_FINGERPRINT) {
-            iconRes = R.drawable.fingerprint_dialog_fp_to_error;
-        } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) {
-            iconRes = R.drawable.fingerprint_dialog_fp_to_error;
-        } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
-            iconRes = R.drawable.fingerprint_dialog_error_to_fp;
-        } else if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_AUTHENTICATED) {
-            // TODO(b/77328470): add animation when fingerprint is authenticated
-            iconRes = R.drawable.fingerprint_dialog_error_to_fp;
-        }
-        else {
-            return null;
-        }
-        return mContext.getDrawable(iconRes);
-    }
-
-    public WindowManager.LayoutParams getLayoutParams() {
-        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
-                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
-                PixelFormat.TRANSLUCENT);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-        lp.setTitle("FingerprintDialogView");
-        lp.token = mWindowToken;
-        return lp;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index f422737..0ed1cd1 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -51,7 +51,8 @@
     private final View mRootView;
     private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
             ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
-                | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS);
+                | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS
+                | ActivityInfo.CONFIG_UI_MODE);
     private final FragmentService mManager;
     private final ExtensionFragmentManager mPlugins = new ExtensionFragmentManager();
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index d232108..6c94aa4 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -28,12 +28,10 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.ServiceConnection;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.graphics.Point;
@@ -42,9 +40,7 @@
 import android.net.ConnectivityManager;
 import android.os.Build;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Message;
-import android.os.Messenger;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -68,9 +64,9 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
-import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemLongClickListener;
 import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
 import android.widget.LinearLayout;
@@ -127,6 +123,7 @@
     private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
     private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
     private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
+    private static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
     private static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
 
     private final Context mContext;
@@ -153,6 +150,7 @@
     private boolean mHasVibrator;
     private boolean mHasLogoutButton;
     private boolean mHasLockdownButton;
+    private boolean mSeparatedEmergencyButtonEnabled;
     private final boolean mShowSilentToggle;
     private final EmergencyAffordanceManager mEmergencyAffordanceManager;
     private final ScreenshotHelper mScreenshotHelper;
@@ -267,7 +265,7 @@
         if (!mHasVibrator) {
             mSilentModeAction = new SilentModeToggleAction();
         } else {
-            mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
+            mSilentModeAction = new SilentModeTriStateAction(mAudioManager, mHandler);
         }
         mAirplaneModeOn = new ToggleAction(
                 R.drawable.ic_lock_airplane_mode,
@@ -319,6 +317,8 @@
         ArraySet<String> addedKeys = new ArraySet<String>();
         mHasLogoutButton = false;
         mHasLockdownButton = false;
+        mSeparatedEmergencyButtonEnabled = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED, 0) != 0;
         for (int i = 0; i < defaultActions.length; i++) {
             String actionKey = defaultActions[i];
             if (addedKeys.contains(actionKey)) {
@@ -365,6 +365,11 @@
                     mItems.add(new LogoutAction());
                     mHasLogoutButton = true;
                 }
+            } else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) {
+                if (mSeparatedEmergencyButtonEnabled
+                        && !mEmergencyAffordanceManager.needsEmergencyAffordance()) {
+                    mItems.add(new EmergencyDialerAction());
+                }
             } else {
                 Log.e(TAG, "Invalid global action key " + actionKey);
             }
@@ -386,7 +391,8 @@
             }
             return false;
         };
-        ActionsDialog dialog = new ActionsDialog(mContext, this, mAdapter, onItemLongClickListener);
+        ActionsDialog dialog = new ActionsDialog(mContext, this, mAdapter, onItemLongClickListener,
+                mSeparatedEmergencyButtonEnabled);
         dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
         dialog.setKeyguardShowing(mKeyguardShowing);
 
@@ -441,6 +447,33 @@
         }
     }
 
+    private class EmergencyDialerAction extends SinglePressAction {
+        private static final String ACTION_EMERGENCY_DIALER_DIAL =
+                "com.android.phone.EmergencyDialer.DIAL";
+
+        private EmergencyDialerAction() {
+            super(R.drawable.ic_faster_emergency,
+                    R.string.global_action_emergency);
+        }
+
+        @Override
+        public void onPress() {
+            Intent intent = new Intent(ACTION_EMERGENCY_DIALER_DIAL);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+        }
+
+        @Override
+        public boolean showDuringKeyguard() {
+            return true;
+        }
+
+        @Override
+        public boolean showBeforeProvisioning() {
+            return true;
+        }
+    }
+
     private final class RestartAction extends SinglePressAction implements LongPressAction {
         private RestartAction() {
             super(R.drawable.ic_restart, R.string.global_action_restart);
@@ -626,6 +659,12 @@
     }
 
     private Action getEmergencyAction() {
+        Drawable emergencyIcon = mContext.getDrawable(R.drawable.emergency_icon);
+        if(!mSeparatedEmergencyButtonEnabled) {
+            // use un-colored legacy treatment
+            emergencyIcon.setTintList(null);
+        }
+
         return new SinglePressAction(R.drawable.emergency_icon,
                 R.string.global_action_emergency) {
             @Override
@@ -1177,12 +1216,10 @@
 
         private final AudioManager mAudioManager;
         private final Handler mHandler;
-        private final Context mContext;
 
-        SilentModeTriStateAction(Context context, AudioManager audioManager, Handler handler) {
+        SilentModeTriStateAction(AudioManager audioManager, Handler handler) {
             mAudioManager = audioManager;
             mHandler = handler;
-            mContext = context;
         }
 
         private int ringerModeToIndex(int ringerMode) {
@@ -1354,15 +1391,17 @@
         private final Context mContext;
         private final MyAdapter mAdapter;
         private final LinearLayout mListView;
+        private final FrameLayout mSeparatedView;
         private final HardwareUiLayout mHardwareLayout;
         private final OnClickListener mClickListener;
         private final OnItemLongClickListener mLongClickListener;
         private final GradientDrawable mGradientDrawable;
         private final ColorExtractor mColorExtractor;
         private boolean mKeyguardShowing;
+        private boolean mShouldDisplaySeparatedButton;
 
         public ActionsDialog(Context context, OnClickListener clickListener, MyAdapter adapter,
-                OnItemLongClickListener longClickListener) {
+                OnItemLongClickListener longClickListener, boolean shouldDisplaySeparatedButton) {
             super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
             mContext = context;
             mAdapter = adapter;
@@ -1370,6 +1409,7 @@
             mLongClickListener = longClickListener;
             mGradientDrawable = new GradientDrawable(mContext);
             mColorExtractor = Dependency.get(SysuiColorExtractor.class);
+            mShouldDisplaySeparatedButton = shouldDisplaySeparatedButton;
 
             // Window initialization
             Window window = getWindow();
@@ -1393,8 +1433,13 @@
 
             setContentView(com.android.systemui.R.layout.global_actions_wrapped);
             mListView = findViewById(android.R.id.list);
+            mSeparatedView = findViewById(com.android.systemui.R.id.separated_button);
+            if (!mShouldDisplaySeparatedButton) {
+                mSeparatedView.setVisibility(View.GONE);
+            }
             mHardwareLayout = HardwareUiLayout.get(mListView);
             mHardwareLayout.setOutsideTouchListener(view -> dismiss());
+            mHardwareLayout.setHasSeparatedButton(mShouldDisplaySeparatedButton);
             setTitle(R.string.global_actions);
             mListView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
                 @Override
@@ -1409,13 +1454,16 @@
 
         private void updateList() {
             mListView.removeAllViews();
+            mSeparatedView.removeAllViews();
             for (int i = 0; i < mAdapter.getCount(); i++) {
-                View v = mAdapter.getView(i, null, mListView);
+                ViewGroup parentView = mShouldDisplaySeparatedButton && i == mAdapter.getCount() - 1
+                        ? mSeparatedView : mListView;
+                View v = mAdapter.getView(i, null, parentView);
                 final int pos = i;
                 v.setOnClickListener(view -> mClickListener.onClick(this, pos));
                 v.setOnLongClickListener(view ->
                         mLongClickListener.onItemLongClick(null, v, pos, 0));
-                mListView.addView(v);
+                parentView.addView(v);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 81d700c..a42191c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -318,8 +318,7 @@
     private CachedBluetoothDevice getCachedBluetoothDevice(BluetoothDevice d) {
         CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(d);
         if (cachedDevice == null) {
-            cachedDevice = mCachedDeviceManager.addDevice(
-                    mLocalBluetoothAdapter, mProfileManager, d);
+            cachedDevice = mCachedDeviceManager.addDevice(mLocalBluetoothAdapter, d);
         }
         return cachedDevice;
     }
@@ -599,21 +598,6 @@
             mHandler.obtainMessage(MSG_ON_DEVICE_BOND_STATE_CHANGED,
                     bondState, 0, cachedDevice).sendToTarget();
         }
-
-        @Override
-        public void onDeviceAdded(CachedBluetoothDevice cachedDevice) { }
-        @Override
-        public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) { }
-        @Override
-        public void onScanningStateChanged(boolean started) { }
-        @Override
-        public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
-        @Override
-        public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
-                                          int bluetoothProfile) { }
-
-        @Override
-        public void onAudioModeChanged() { }
     }
 
     private final class BluetoothErrorListener implements BluetoothUtils.ErrorListener {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 5e94dd7..4763fa9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -26,9 +26,11 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
+import android.hardware.biometrics.BiometricSourceType;
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -81,8 +83,8 @@
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.phone.FingerprintUnlockController;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 
@@ -344,6 +346,11 @@
      */
     private WorkLockActivityController mWorkLockController;
 
+    /**
+     * @see #setPulsing(boolean)
+     */
+    private boolean mPulsing;
+
     private boolean mLockLater;
 
     private boolean mWakeAndUnlocking;
@@ -521,18 +528,18 @@
         }
 
         @Override
-        public void onFingerprintAuthFailed() {
+        public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
             final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
             if (mLockPatternUtils.isSecure(currentUser)) {
-                mLockPatternUtils.getDevicePolicyManager().reportFailedFingerprintAttempt(
+                mLockPatternUtils.getDevicePolicyManager().reportFailedBiometricAttempt(
                         currentUser);
             }
         }
 
         @Override
-        public void onFingerprintAuthenticated(int userId) {
+        public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
             if (mLockPatternUtils.isSecure(userId)) {
-                mLockPatternUtils.getDevicePolicyManager().reportSuccessfulFingerprintAttempt(
+                mLockPatternUtils.getDevicePolicyManager().reportSuccessfulBiometricAttempt(
                         userId);
             }
         }
@@ -622,6 +629,11 @@
         }
 
         @Override
+        public void onCancelClicked() {
+            mStatusBarKeyguardViewManager.onCancelClicked();
+        }
+
+        @Override
         public void onBouncerVisiblityChanged(boolean shown) {
             synchronized (KeyguardViewMediator.this) {
                 adjustStatusBarLocked(shown);
@@ -1644,7 +1656,7 @@
         }
 
         mUpdateMonitor.clearFailedUnlockAttempts();
-        mUpdateMonitor.clearFingerprintRecognized();
+        mUpdateMonitor.clearBiometricRecognized();
 
         if (mGoingToSleep) {
             Log.i(TAG, "Device is going to sleep, aborting keyguardDone");
@@ -1750,7 +1762,7 @@
             int secondaryDisplayShowing) {
         mUiOffloadThread.submit(() -> {
             try {
-                ActivityManager.getService().setLockScreenShown(showing, aodShowing,
+                ActivityTaskManager.getService().setLockScreenShown(showing, aodShowing,
                         secondaryDisplayShowing);
             } catch (RemoteException e) {
             }
@@ -1799,10 +1811,12 @@
 
                 int flags = 0;
                 if (mStatusBarKeyguardViewManager.shouldDisableWindowAnimationsForUnlock()
-                        || mWakeAndUnlocking) {
-                    flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
+                        || (mWakeAndUnlocking && !mPulsing)) {
+                    flags |= WindowManagerPolicyConstants
+                            .KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
                 }
-                if (mStatusBarKeyguardViewManager.isGoingToNotificationShade()) {
+                if (mStatusBarKeyguardViewManager.isGoingToNotificationShade()
+                        || (mWakeAndUnlocking && mPulsing)) {
                     flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
                 }
                 if (mStatusBarKeyguardViewManager.isUnlockWithWallpaper()) {
@@ -1813,7 +1827,7 @@
                 // Don't actually hide the Keyguard at the moment, wait for window
                 // manager until it tells us it's safe to do so with
                 // startKeyguardExitAnimation.
-                ActivityManager.getService().keyguardGoingAway(flags);
+                ActivityTaskManager.getService().keyguardGoingAway(flags);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error while calling WindowManager", e);
             }
@@ -2050,9 +2064,9 @@
 
     public StatusBarKeyguardViewManager registerStatusBar(StatusBar statusBar,
             ViewGroup container, NotificationPanelView panelView,
-            FingerprintUnlockController fingerprintUnlockController) {
+            BiometricUnlockController biometricUnlockController) {
         mStatusBarKeyguardViewManager.registerStatusBar(statusBar, container, panelView,
-                fingerprintUnlockController, mDismissCallbackRegistry);
+                biometricUnlockController, mDismissCallbackRegistry);
         return mStatusBarKeyguardViewManager;
     }
 
@@ -2101,10 +2115,20 @@
         pw.print("  mDrawnCallback: "); pw.println(mDrawnCallback);
     }
 
+    /**
+     * @param aodShowing true when AOD - or ambient mode - is showing.
+     */
     public void setAodShowing(boolean aodShowing) {
         setShowingLocked(mShowing, aodShowing);
     }
 
+    /**
+     * @param pulsing true when device temporarily wakes up to display an incoming notification.
+     */
+    public void setPulsing(boolean pulsing) {
+        mPulsing = pulsing;
+    }
+
     private static class StartKeyguardExitAnimParams {
 
         long startTime;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index b9e9e0a..0cedf98 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -18,7 +18,9 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.app.KeyguardManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -37,16 +39,17 @@
     private static final String TAG = WorkLockActivityController.class.getSimpleName();
 
     private final Context mContext;
-    private final IActivityManager mIam;
+    private final IActivityTaskManager mIatm;
 
     public WorkLockActivityController(Context context) {
-        this(context, ActivityManagerWrapper.getInstance(), ActivityManager.getService());
+        this(context, ActivityManagerWrapper.getInstance(), ActivityTaskManager.getService());
     }
 
     @VisibleForTesting
-    WorkLockActivityController(Context context, ActivityManagerWrapper am, IActivityManager iAm) {
+    WorkLockActivityController(
+            Context context, ActivityManagerWrapper am, IActivityTaskManager iAtm) {
         mContext = context;
-        mIam = iAm;
+        mIatm = iAtm;
 
         am.registerTaskStackListener(mLockListener);
     }
@@ -54,7 +57,7 @@
     private void startWorkChallengeInTask(int taskId, int userId) {
         ActivityManager.TaskDescription taskDescription = null;
         try {
-            taskDescription = mIam.getTaskDescription(taskId);
+            taskDescription = mIatm.getTaskDescription(taskId);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to get description for task=" + taskId);
         }
@@ -76,7 +79,7 @@
             // Starting the activity inside the task failed. We can't be sure why, so to be
             // safe just remove the whole task if it still exists.
             try {
-                mIam.removeTask(taskId);
+                mIatm.removeTask(taskId);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to get description for task=" + taskId);
             }
@@ -89,7 +92,7 @@
      */
     private int startActivityAsUser(Intent intent, Bundle options, int userId) {
         try {
-            return mIam.startActivityAsUser(
+            return mIatm.startActivityAsUser(
                     mContext.getIApplicationThread() /*caller*/,
                     mContext.getBasePackageName() /*callingPackage*/,
                     intent /*intent*/,
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index f5f06db..89786ee 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -86,11 +86,12 @@
             // synchronized on mCompletionHandlingLock due to the Object.wait() in startSound(cmd)
             mLooper = Looper.myLooper();
             if (DEBUG) Log.d(mTag, "in run: new looper " + mLooper);
+            MediaPlayer player = null;
             synchronized(this) {
                 AudioManager audioManager =
                     (AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);
                 try {
-                    MediaPlayer player = new MediaPlayer();
+                    player = new MediaPlayer();
                     if (mCmd.attributes == null) {
                         mCmd.attributes = new AudioAttributes.Builder()
                                 .setUsage(AudioAttributes.USAGE_NOTIFICATION)
@@ -137,26 +138,26 @@
                         Log.e(mTag, "Exception while sleeping to sync notification playback"
                                 + " with ducking", e);
                     }
-                    try {
-                        player.start();
-                        if (DEBUG) { Log.d(mTag, "player.start"); }
-                    } catch (Exception e) {
+                    player.start();
+                    if (DEBUG) { Log.d(mTag, "player.start"); }
+                } catch (Exception e) {
+                    if (player != null) {
                         player.release();
                         player = null;
-                        // playing the notification didn't work, revert the focus request
-                        abandonAudioFocusAfterError();
                     }
-                    if (mPlayer != null) {
-                        if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
-                        mPlayer.release();
-                    }
-                    mPlayer = player;
-                }
-                catch (Exception e) {
                     Log.w(mTag, "error loading sound for " + mCmd.uri, e);
                     // playing the notification didn't work, revert the focus request
                     abandonAudioFocusAfterError();
                 }
+                final MediaPlayer mp;
+                synchronized (mPlayerLock) {
+                    mp = mPlayer;
+                    mPlayer = player;
+                }
+                if (mp != null) {
+                    if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
+                    mp.release();
+                }
                 this.notify();
             }
             Looper.loop();
@@ -230,14 +231,20 @@
                     break;
                 case STOP:
                     if (DEBUG) Log.d(mTag, "STOP");
-                    if (mPlayer != null) {
+                    final MediaPlayer mp;
+                    synchronized (mPlayerLock) {
+                        mp = mPlayer;
+                        mPlayer = null;
+                    }
+                    if (mp != null) {
                         long delay = SystemClock.uptimeMillis() - cmd.requestTime;
                         if (delay > 1000) {
                             Log.w(mTag, "Notification stop delayed by " + delay + "msecs");
                         }
-                        mPlayer.stop();
-                        mPlayer.release();
-                        mPlayer = null;
+                        try {
+                            mp.stop();
+                        } catch (Exception e) { }
+                        mp.release();
                         synchronized(mQueueAudioFocusLock) {
                             if (mAudioManagerWithAudioFocus != null) {
                                 if (DEBUG) { Log.d(mTag, "in STOP: abandonning AudioFocus"); }
@@ -297,6 +304,14 @@
                 }
             }
         }
+        synchronized (mPlayerLock) {
+            if (mp == mPlayer) {
+                mPlayer = null;
+            }
+        }
+        if (mp != null) {
+            mp.release();
+        }
     }
 
     public boolean onError(MediaPlayer mp, int what, int extra) {
@@ -311,6 +326,8 @@
     @GuardedBy("mCmdQueue")
     private CmdThread mThread;
 
+    private final Object mPlayerLock = new Object();
+    @GuardedBy("mPlayerLock")
     private MediaPlayer mPlayer;
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 2af7ae2..ee15655 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -20,7 +20,9 @@
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
@@ -55,6 +57,7 @@
 
     private Context mContext;
     private IActivityManager mActivityManager;
+    private IActivityTaskManager mActivityTaskManager;
     private IWindowManager mWindowManager;
     private Handler mHandler = new Handler();
 
@@ -172,6 +175,7 @@
     public void initialize(Context context) {
         mContext = context;
         mActivityManager = ActivityManager.getService();
+        mActivityTaskManager = ActivityTaskManager.getService();
         mWindowManager = WindowManagerGlobal.getWindowManagerService();
 
         try {
@@ -186,8 +190,8 @@
         mMediaController = new PipMediaController(context, mActivityManager);
         mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController,
                 mInputConsumerController);
-        mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController,
-                mInputConsumerController);
+        mTouchHandler = new PipTouchHandler(context, mActivityManager, mActivityTaskManager,
+                mMenuController, mInputConsumerController);
         mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
                 mTouchHandler.getMotionHelper());
         EventBus.getDefault().register(this);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 615b29f..2dc531a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -229,7 +229,9 @@
         mSettingsButton = findViewById(R.id.settings);
         mSettingsButton.setAlpha(0);
         mSettingsButton.setOnClickListener((v) -> {
-            showSettings();
+            if (v.getAlpha() != 0) {
+                showSettings();
+            }
         });
         mDismissButton = findViewById(R.id.dismiss);
         mDismissButton.setAlpha(0);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 26fced3..360fe73 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
+import android.app.ActivityTaskManager;
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityOptions;
 import android.app.IActivityManager;
@@ -384,7 +385,7 @@
     private void startMenuActivity(int menuState, Rect stackBounds, Rect movementBounds,
             boolean allowMenuTimeout, boolean willResizeMenu) {
         try {
-            StackInfo pinnedStackInfo = mActivityManager.getStackInfo(
+            StackInfo pinnedStackInfo = ActivityTaskManager.getService().getStackInfo(
                     WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
             if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
                     pinnedStackInfo.taskIds.length > 0) {
@@ -423,7 +424,7 @@
             // Fetch the pinned stack bounds
             Rect stackBounds = null;
             try {
-                StackInfo pinnedStackInfo = mActivityManager.getStackInfo(
+                StackInfo pinnedStackInfo = ActivityTaskManager.getService().getStackInfo(
                         WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
                 if (pinnedStackInfo != null) {
                     stackBounds = pinnedStackInfo.bounds;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index ad84130..f0ab046 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -30,7 +30,9 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.ActivityManager.StackInfo;
+import android.app.ActivityTaskManager;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -81,6 +83,7 @@
 
     private Context mContext;
     private IActivityManager mActivityManager;
+    private IActivityTaskManager mActivityTaskManager;
     private Handler mHandler;
 
     private PipMenuActivityController mMenuController;
@@ -94,11 +97,12 @@
     private ValueAnimator mBoundsAnimator = null;
 
     public PipMotionHelper(Context context, IActivityManager activityManager,
-            PipMenuActivityController menuController, PipSnapAlgorithm snapAlgorithm,
-            FlingAnimationUtils flingAnimationUtils) {
+            IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
+            PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils) {
         mContext = context;
         mHandler = new Handler(ForegroundThread.get().getLooper(), this);
         mActivityManager = activityManager;
+        mActivityTaskManager = activityTaskManager;
         mMenuController = menuController;
         mSnapAlgorithm = snapAlgorithm;
         mFlingAnimationUtils = flingAnimationUtils;
@@ -121,8 +125,8 @@
     void synchronizePinnedStackBounds() {
         cancelAnimations();
         try {
-            StackInfo stackInfo =
-                    mActivityManager.getStackInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
+            StackInfo stackInfo = mActivityTaskManager.getStackInfo(
+                    WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
             if (stackInfo != null) {
                 mBounds.set(stackInfo.bounds);
             }
@@ -159,7 +163,7 @@
         mMenuController.hideMenuWithoutResize();
         mHandler.post(() -> {
             try {
-                mActivityManager.dismissPip(!skipAnimation, EXPAND_STACK_TO_FULLSCREEN_DURATION);
+                mActivityTaskManager.dismissPip(!skipAnimation, EXPAND_STACK_TO_FULLSCREEN_DURATION);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error expanding PiP activity", e);
             }
@@ -177,7 +181,8 @@
         mMenuController.hideMenuWithoutResize();
         mHandler.post(() -> {
             try {
-                mActivityManager.removeStacksInWindowingModes(new int[]{ WINDOWING_MODE_PINNED });
+                mActivityTaskManager.removeStacksInWindowingModes(
+                        new int[]{ WINDOWING_MODE_PINNED });
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to remove PiP", e);
             }
@@ -512,7 +517,8 @@
                 SomeArgs args = (SomeArgs) msg.obj;
                 Rect toBounds = (Rect) args.arg1;
                 try {
-                    mActivityManager.resizePinnedStack(toBounds, null /* tempPinnedTaskBounds */);
+                    mActivityTaskManager.resizePinnedStack(
+                            toBounds, null /* tempPinnedTaskBounds */);
                     mBounds.set(toBounds);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Could not resize pinned stack to bounds: " + toBounds, e);
@@ -525,7 +531,7 @@
                 Rect toBounds = (Rect) args.arg1;
                 int duration = args.argi1;
                 try {
-                    StackInfo stackInfo = mActivityManager.getStackInfo(
+                    StackInfo stackInfo = mActivityTaskManager.getStackInfo(
                             WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
                     if (stackInfo == null) {
                         // In the case where we've already re-expanded or dismissed the PiP, then
@@ -533,7 +539,7 @@
                         return true;
                     }
 
-                    mActivityManager.resizeStack(stackInfo.stackId, toBounds,
+                    mActivityTaskManager.resizeStack(stackInfo.stackId, toBounds,
                             false /* allowResizeInDockedMode */, true /* preserveWindows */,
                             true /* animate */, duration);
                     mBounds.set(toBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 02345c9..03a573e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -25,6 +25,7 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
@@ -64,11 +65,13 @@
     private static final boolean ENABLE_FLING_DISMISS = false;
 
     private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225;
+    private static final int BOTTOM_OFFSET_BUFFER_DP = 1;
 
     // Allow dragging the PIP to a location to close it
     private final boolean mEnableDimissDragToEdge;
     private final Context mContext;
     private final IActivityManager mActivityManager;
+    private final IActivityTaskManager mActivityTaskManager;
     private final ViewConfiguration mViewConfig;
     private final PipMenuListener mMenuListener = new PipMenuListener();
     private IPinnedStackController mPinnedStackController;
@@ -172,12 +175,13 @@
     }
 
     public PipTouchHandler(Context context, IActivityManager activityManager,
-            PipMenuActivityController menuController,
+            IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
             InputConsumerController inputConsumerController) {
 
         // Initialize the Pip input consumer
         mContext = context;
         mActivityManager = activityManager;
+        mActivityTaskManager = activityTaskManager;
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
         mViewConfig = ViewConfiguration.get(context);
         mMenuController = menuController;
@@ -188,8 +192,8 @@
         mGestures = new PipTouchGesture[] {
                 mDefaultMovementGesture
         };
-        mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mMenuController,
-                mSnapAlgorithm, mFlingAnimationUtils);
+        mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager,
+                mMenuController, mSnapAlgorithm, mFlingAnimationUtils);
         mTouchState = new PipTouchState(mViewConfig, mHandler,
                 () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
                         mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
@@ -311,8 +315,10 @@
                 // above the position as if shelf/IME shows, don't move the PIP window.
                 int movementBoundsAdjustment = toMovementBounds.bottom - mMovementBounds.bottom;
                 int offsetAdjustment = fromImeAdjustment ? mImeOffset : mShelfHeight;
+                final float bottomOffsetBufferInPx = BOTTOM_OFFSET_BUFFER_DP
+                        * mContext.getResources().getDisplayMetrics().density;
                 if (toAdjustedBounds.bottom >= mMovementBounds.bottom
-                        && animatingBounds.top
+                        && animatingBounds.top + Math.round(bottomOffsetBufferInPx)
                         < toAdjustedBounds.bottom - movementBoundsAdjustment - offsetAdjustment) {
                     return;
                 }
@@ -511,7 +517,7 @@
      * Sets the menu visibility.
      */
     private void setMenuState(int menuState, boolean resize) {
-        if (menuState == MENU_STATE_FULL) {
+        if (menuState == MENU_STATE_FULL && mMenuState != MENU_STATE_FULL) {
             // Save the current snap fraction and if we do not drag or move the PiP, then
             // we store back to this snap fraction.  Otherwise, we'll reset the snap
             // fraction and snap to the closest edge
@@ -520,7 +526,7 @@
                 mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
                         mMovementBounds, mExpandedMovementBounds);
             }
-        } else if (menuState == MENU_STATE_NONE) {
+        } else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
             // Try and restore the PiP to the closest edge, using the saved snap fraction
             // if possible
             if (resize) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
index 2f53de9..1ed1904 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
 import android.app.ActivityManager.StackInfo;
+import android.app.ActivityTaskManager;
 import android.app.IActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -39,8 +40,8 @@
             IActivityManager activityManager) {
         try {
             final String sysUiPackageName = context.getPackageName();
-            final StackInfo pinnedStackInfo =
-                    activityManager.getStackInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
+            final StackInfo pinnedStackInfo = ActivityTaskManager.getService().getStackInfo(
+                    WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
             if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
                     pinnedStackInfo.taskIds.length > 0) {
                 for (int i = pinnedStackInfo.taskNames.length - 1; i >= 0; i--) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
old mode 100644
new mode 100755
index d6f6760..020c550
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -19,7 +19,9 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
+import android.app.ActivityTaskManager;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -53,7 +55,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -108,6 +110,7 @@
 
     private Context mContext;
     private IActivityManager mActivityManager;
+    private IActivityTaskManager mActivityTaskManager;
     private IWindowManager mWindowManager;
     private MediaSessionManager mMediaSessionManager;
     private int mState = STATE_NO_PIP;
@@ -238,6 +241,7 @@
         mContext = context;
 
         mActivityManager = ActivityManager.getService();
+        mActivityTaskManager = ActivityTaskManager.getService();
         mWindowManager = WindowManagerGlobal.getWindowManagerService();
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
         IntentFilter intentFilter = new IntentFilter();
@@ -258,12 +262,13 @@
                             entry = Pair.<String, String>create(packageAndClassName[0], null);
                             break;
                         case 2:
-                            if (packageAndClassName[1] != null
-                                    && packageAndClassName[1].startsWith(".")) {
-                                entry = Pair.<String, String>create(
-                                        packageAndClassName[0],
-                                        packageAndClassName[0] + packageAndClassName[1]);
+                            if (packageAndClassName[1] != null) {
+                                entry = Pair.<String, String>create(packageAndClassName[0],
+                                        packageAndClassName[1].startsWith(".")
+                                                ? packageAndClassName[0] + packageAndClassName[1]
+                                                : packageAndClassName[1]);
                             }
+                            break;
                     }
                     if (entry != null) {
                         sSettingsPackageAndClassNamePairList.add(entry);
@@ -343,7 +348,7 @@
         mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
         if (removePipStack) {
             try {
-                mActivityManager.removeStack(mPinnedStackId);
+                mActivityTaskManager.removeStack(mPinnedStackId);
             } catch (RemoteException e) {
                 Log.e(TAG, "removeStack failed", e);
             } finally {
@@ -433,7 +438,7 @@
         }
         try {
             int animationDurationMs = -1;
-            mActivityManager.resizeStack(mPinnedStackId, mCurrentPipBounds,
+            mActivityTaskManager.resizeStack(mPinnedStackId, mCurrentPipBounds,
                     true, true, true, animationDurationMs);
         } catch (RemoteException e) {
             Log.e(TAG, "resizeStack failed", e);
@@ -511,7 +516,7 @@
     private StackInfo getPinnedStackInfo() {
         StackInfo stackInfo = null;
         try {
-            stackInfo = mActivityManager.getStackInfo(
+            stackInfo = ActivityTaskManager.getService().getStackInfo(
                     WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
         } catch (RemoteException e) {
             Log.e(TAG, "getStackInfo failed", e);
@@ -604,7 +609,7 @@
     private boolean isSettingsShown() {
         List<RunningTaskInfo> runningTasks;
         try {
-            runningTasks = mActivityManager.getTasks(1);
+            runningTasks = mActivityTaskManager.getTasks(1);
             if (runningTasks.isEmpty()) {
                 return false;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 9070170..21eab59 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -266,9 +266,12 @@
                 || mEstimate.estimateMillis < mSevereWarningThreshold) {
             nb.setColor(Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorError));
         }
-        nb.addAction(0,
-                mContext.getString(R.string.battery_saver_start_action),
-                pendingBroadcast(ACTION_START_SAVER));
+
+        if (!mPowerMan.isPowerSaveMode()) {
+            nb.addAction(0,
+                    mContext.getString(R.string.battery_saver_start_action),
+                    pendingBroadcast(ACTION_START_SAVER));
+        }
         nb.setOnlyAlertOnce(!mPlaySound);
         mPlaySound = false;
         SystemUI.overrideNotificationAppName(mContext, nb, false);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 6801e69..0b9067e 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -77,6 +77,7 @@
     private int mPlugType = 0;
     private int mInvalidCharger = 0;
     private EnhancedEstimates mEnhancedEstimates;
+    private Estimate mLastEstimate;
     private boolean mLowWarningShownThisChargeCycle;
     private boolean mSevereWarningShownThisChargeCycle;
 
@@ -247,7 +248,8 @@
 
                 // Show the correct version of low battery warning if needed
                 ThreadUtils.postOnBackgroundThread(() -> {
-                    maybeShowBatteryWarning(plugged, oldPlugged, oldBucket, bucket);
+                    maybeShowBatteryWarning(
+                            oldBatteryLevel, plugged, oldPlugged, oldBucket, bucket);
                 });
 
             } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
@@ -262,14 +264,18 @@
         }
     }
 
-    protected void maybeShowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
-        int bucket) {
+    protected void maybeShowBatteryWarning(int oldBatteryLevel, boolean plugged, boolean oldPlugged,
+            int oldBucket, int bucket) {
         boolean isPowerSaver = mPowerManager.isPowerSaveMode();
         // only play SFX when the dialog comes up or the bucket changes
         final boolean playSound = bucket != oldBucket || oldPlugged;
         final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
         if (hybridEnabled) {
-            final Estimate estimate = mEnhancedEstimates.getEstimate();
+            Estimate estimate = mLastEstimate;
+            if (estimate == null || mBatteryLevel != oldBatteryLevel) {
+                estimate = mEnhancedEstimates.getEstimate();
+                mLastEstimate = estimate;
+            }
             // Turbo is not always booted once SysUI is running so we have ot make sure we actually
             // get data back
             if (estimate != null) {
@@ -327,10 +333,11 @@
     @VisibleForTesting
     boolean shouldDismissLowBatteryWarning(boolean plugged, int oldBucket, int bucket,
             long timeRemaining, boolean isPowerSaver) {
-        final boolean hybridWouldDismiss = mEnhancedEstimates.isHybridNotificationEnabled()
+        final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
+        final boolean hybridWouldDismiss = hybridEnabled
                 && timeRemaining > mEnhancedEstimates.getLowWarningThreshold();
         final boolean standardWouldDismiss = (bucket > oldBucket && bucket > 0);
-        return isPowerSaver
+        return (isPowerSaver && !hybridEnabled)
                 || plugged
                 || (standardWouldDismiss && (!mEnhancedEstimates.isHybridNotificationEnabled()
                         || hybridWouldDismiss));
@@ -338,14 +345,14 @@
 
     private boolean isEnhancedTrigger(boolean plugged, long timeRemaining, boolean isPowerSaver,
             int batteryStatus) {
-        if (plugged || isPowerSaver || batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
+        if (plugged || batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
             return false;
         }
         int warnLevel = mLowBatteryReminderLevels[0];
         int critLevel = mLowBatteryReminderLevels[1];
 
-        // Only show the low warning once per charge cycle
-        final boolean canShowWarning = !mLowWarningShownThisChargeCycle
+        // Only show the low warning once per charge cycle & no battery saver
+        final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !isPowerSaver
                 && (timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
                         || mBatteryLevel <= warnLevel);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 100751c..f1b7eec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -55,7 +55,6 @@
     private Scroller mScroller;
 
     private AnimatorSet mBounceAnimatorSet;
-    private int mAnimatingToPage = -1;
     private float mLastExpansion;
 
     public PagedTileLayout(Context context, AttributeSet attrs) {
@@ -95,40 +94,16 @@
     }
 
     @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        // Suppress all touch event during reveal animation.
-        if (mAnimatingToPage != -1) {
-            return true;
-        }
-        return super.onInterceptTouchEvent(ev);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        // Suppress all touch event during reveal animation.
-        if (mAnimatingToPage != -1) {
-            return true;
-        }
-        return super.onTouchEvent(ev);
-    }
-
-    @Override
     public void computeScroll() {
         if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
-            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
-            float pageFraction = (float) getScrollX() / getWidth();
-            int position = (int) pageFraction;
-            float positionOffset = pageFraction - position;
-            mOnPageChangeListener.onPageScrolled(position, positionOffset, getScrollX());
+            fakeDragBy(getScrollX() - mScroller.getCurrX());
             // Keep on drawing until the animation has finished.
             postInvalidateOnAnimation();
             return;
-        }
-        if (mAnimatingToPage != -1) {
-            setCurrentItem(mAnimatingToPage, true);
+        } else if (isFakeDragging()) {
+            endFakeDrag();
             mBounceAnimatorSet.start();
             setOffscreenPageLimit(1);
-            mAnimatingToPage = -1;
         }
         super.computeScroll();
     }
@@ -189,8 +164,9 @@
         // marquee. This will ensure that accessibility doesn't announce the TYPE_VIEW_SELECTED
         // event on any of the children.
         setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+        int currentItem = isLayoutRtl() ? mPages.size() - 1 - getCurrentItem() : getCurrentItem();
         for (int i = 0; i < mPages.size(); i++) {
-            mPages.get(i).setSelected(i == getCurrentItem() ? selected : false);
+            mPages.get(i).setSelected(i == currentItem ? selected : false);
         }
         setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
     }
@@ -286,7 +262,7 @@
     }
 
     public void startTileReveal(Set<String> tileSpecs, final Runnable postAnimation) {
-        if (tileSpecs.isEmpty() || mPages.size() < 2 || getScrollX() != 0) {
+        if (tileSpecs.isEmpty() || mPages.size() < 2 || getScrollX() != 0 || !beginFakeDrag()) {
             // Do not start the reveal animation unless there are tiles to animate, multiple
             // TilePages available and the user has not already started dragging.
             return;
@@ -304,6 +280,7 @@
         if (bounceAnims.isEmpty()) {
             // All tileSpecs are on the first page. Nothing to do.
             // TODO: potentially show a bounce animation for first page QS tiles
+            endFakeDrag();
             return;
         }
 
@@ -316,10 +293,10 @@
                 postAnimation.run();
             }
         });
-        mAnimatingToPage = lastPageNumber;
-        setOffscreenPageLimit(mAnimatingToPage); // Ensure the page to reveal has been inflated.
-        mScroller.startScroll(getScrollX(), getScrollY(), getWidth() * mAnimatingToPage, 0,
-                REVEAL_SCROLL_DURATION_MILLIS);
+        setOffscreenPageLimit(lastPageNumber); // Ensure the page to reveal has been inflated.
+        int dx = getWidth() * lastPageNumber;
+        mScroller.startScroll(getScrollX(), getScrollY(), isLayoutRtl() ? -dx  : dx, 0,
+            REVEAL_SCROLL_DURATION_MILLIS);
         postInvalidateOnAnimation();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index d8bf990..80b6c73 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -17,6 +17,7 @@
 package com.android.systemui.qs;
 
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -77,16 +78,7 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-
-        // Hide the backgrounds when in landscape mode.
-        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            mBackgroundGradient.setVisibility(View.INVISIBLE);
-            mStatusBarBackground.setVisibility(View.INVISIBLE);
-        } else {
-            mBackgroundGradient.setVisibility(View.VISIBLE);
-            mStatusBarBackground.setVisibility(View.VISIBLE);
-        }
-
+        setBackgroundGradientVisibility(newConfig);
         updateResources();
         mSizePoint.set(0, 0); // Will be retrieved on next measure pass.
     }
@@ -128,7 +120,7 @@
         final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
         if (disabled == mQsDisabled) return;
         mQsDisabled = disabled;
-        mBackgroundGradient.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+        setBackgroundGradientVisibility(getResources().getConfiguration());
         mBackground.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
     }
 
@@ -168,6 +160,16 @@
                 + mHeader.getHeight();
     }
 
+    private void setBackgroundGradientVisibility(Configuration newConfig) {
+        if (newConfig.orientation == ORIENTATION_LANDSCAPE) {
+            mBackgroundGradient.setVisibility(View.INVISIBLE);
+            mStatusBarBackground.setVisibility(View.INVISIBLE);
+        } else {
+            mBackgroundGradient.setVisibility(mQsDisabled ? View.INVISIBLE : View.VISIBLE);
+            mStatusBarBackground.setVisibility(View.VISIBLE);
+        }
+    }
+
     public void setExpansion(float expansion) {
         mQsExpansion = expansion;
         updateExpansion();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index a6b065f..f147fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -123,8 +123,6 @@
         mMobileSignal = findViewById(R.id.mobile_signal);
         mMobileRoaming = findViewById(R.id.mobile_roaming);
         mCarrierText = findViewById(R.id.qs_carrier_text);
-        mCarrierText.setDisplayFlags(
-                CarrierText.FLAG_HIDE_AIRPLANE_MODE | CarrierText.FLAG_HIDE_MISSING_SIM);
 
         mMultiUserSwitch = findViewById(R.id.multi_user_switch);
         mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index bd89ad1..7cb22a3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -19,7 +19,6 @@
 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;
@@ -45,7 +44,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
 public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks {
     private static final String TAG = "QS";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 02937d8..bb69b7a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -50,6 +50,7 @@
 import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.function.Predicate;
 
 /** Platform implementation of the quick settings tile host **/
 public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> {
@@ -226,23 +227,22 @@
     }
 
     @Override
-    public void removeTile(String tileSpec) {
-        ArrayList<String> specs = new ArrayList<>(mTileSpecs);
-        specs.remove(tileSpec);
-        Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
-                TextUtils.join(",", specs), ActivityManager.getCurrentUser());
+    public void removeTile(String spec) {
+        changeTileSpecs(tileSpecs-> tileSpecs.remove(spec));
     }
 
     public void addTile(String spec) {
+        changeTileSpecs(tileSpecs-> tileSpecs.add(spec));
+    }
+
+    private void changeTileSpecs(Predicate<List<String>> changeFunction) {
         final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
-                TILES_SETTING, ActivityManager.getCurrentUser());
+            TILES_SETTING, ActivityManager.getCurrentUser());
         final List<String> tileSpecs = loadTileSpecs(mContext, setting);
-        if (tileSpecs.contains(spec)) {
-            return;
-        }
-        tileSpecs.add(spec);
-        Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
+        if (changeFunction.test(tileSpecs)) {
+            Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
                 TextUtils.join(",", tileSpecs), ActivityManager.getCurrentUser());
+        }
     }
 
     public void addTile(ComponentName tile) {
@@ -300,7 +300,7 @@
         throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());
     }
 
-    protected List<String> loadTileSpecs(Context context, String tileList) {
+    protected static List<String> loadTileSpecs(Context context, String tileList) {
         final Resources res = context.getResources();
         final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
         if (tileList == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index c9c04d9..1c50f79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -45,6 +45,7 @@
     public static final String NUM_QUICK_TILES = "sysui_qqs_count";
 
     private boolean mDisabledByPolicy;
+    private static int mDefaultMaxTiles;
     private int mMaxTiles;
     protected QSPanel mFullPanel;
 
@@ -59,6 +60,7 @@
             }
             removeView((View) mTileLayout);
         }
+        mDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
         mTileLayout = new HeaderTileLayout(context);
         mTileLayout.setListening(mListening);
         addView((View) mTileLayout, 0 /* Between brightness and footer */);
@@ -149,7 +151,7 @@
     };
 
     public static int getNumQuickTiles(Context context) {
-        return Dependency.get(TunerService.class).getValue(NUM_QUICK_TILES, 6);
+        return Dependency.get(TunerService.class).getValue(NUM_QUICK_TILES, mDefaultMaxTiles);
     }
 
     void setDisabledByPolicy(boolean disabled) {
@@ -190,29 +192,21 @@
 
             mTileDimensionSize = mContext.getResources().getDimensionPixelSize(
                     R.dimen.qs_quick_tile_size);
-
-            setGravity(Gravity.CENTER);
-            setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+            updateLayoutParams();
         }
 
         @Override
         protected void onConfigurationChanged(Configuration newConfig) {
             super.onConfigurationChanged(newConfig);
+            updateLayoutParams();
+        }
 
+        private void updateLayoutParams() {
             setGravity(Gravity.CENTER);
-            LayoutParams staticSpaceLayoutParams = generateSpaceLayoutParams(
-                    mContext.getResources().getDimensionPixelSize(
-                            R.dimen.qs_quick_tile_space_width));
-
-            // Update space params since they fill any open space in portrait orientation and have
-            // a static width in landscape orientation.
-            final int childViewCount = getChildCount();
-            for (int i = 0; i < childViewCount; i++) {
-                View childView = getChildAt(i);
-                if (childView instanceof Space) {
-                    childView.setLayoutParams(staticSpaceLayoutParams);
-                }
-            }
+            int width = getResources().getDimensionPixelSize(R.dimen.qs_quick_layout_width);
+            LayoutParams lp = new LayoutParams(width, LayoutParams.MATCH_PARENT);
+            lp.gravity = Gravity.CENTER_HORIZONTAL;
+            setLayoutParams(lp);
         }
 
         /**
@@ -220,11 +214,9 @@
          * then we're going to have the space expand to take up as much space as possible. If the
          * width is non-zero, we want the inter-tile spacers to be fixed.
          */
-        private LayoutParams generateSpaceLayoutParams(int spaceWidth) {
-            LayoutParams lp = new LayoutParams(spaceWidth, mTileDimensionSize);
-            if (spaceWidth == 0) {
-                lp.weight = 1;
-            }
+        private LayoutParams generateSpaceLayoutParams() {
+            LayoutParams lp = new LayoutParams(0, mTileDimensionSize);
+            lp.weight = 1;
             lp.gravity = Gravity.CENTER;
             return lp;
         }
@@ -241,13 +233,7 @@
         @Override
         public void addTile(TileRecord tile) {
             if (getChildCount() != 0) {
-                // Add a spacer between tiles. We want static-width spaces if we're in landscape to
-                // keep the tiles close. For portrait, we stick with spaces that fill up any
-                // available space.
-                LayoutParams spaceLayoutParams = generateSpaceLayoutParams(
-                        mContext.getResources().getDimensionPixelSize(
-                                R.dimen.qs_quick_tile_space_width));
-                addView(new Space(mContext), getChildCount(), spaceLayoutParams);
+                addView(new Space(mContext), getChildCount(), generateSpaceLayoutParams());
             }
 
             addView(tile.tileView, getChildCount(), generateTileLayoutParams());
@@ -303,6 +289,10 @@
         @Override
         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            if (hideOverflowingChildren(widthMeasureSpec)) {
+                return; // Rely on visibility change to trigger remeasure.
+            }
+
             if (mRecords != null && mRecords.size() > 0) {
                 View previousView = this;
                 for (TileRecord record : mRecords) {
@@ -315,5 +305,32 @@
                         R.id.expand_indicator);
             }
         }
+
+        /**
+         * Hide child views that would otherwise be clipped.
+         * @return {@code true} if any child visibilities have changed.
+         */
+        private boolean hideOverflowingChildren(int widthMeasureSpec) {
+            if (getChildCount() == 0) {
+                return false;
+            }
+            boolean childVisibilityChanged = false;
+            int widthRemaining = MeasureSpec.getSize(widthMeasureSpec)
+                - getChildAt(0).getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
+            for (int i = 2; i < getChildCount(); i += 2) {
+                View tileChild = getChildAt(i);
+                LayoutParams lp = (LayoutParams) tileChild.getLayoutParams();
+                // All Space views have 0 width; only tiles contribute to the total width.
+                widthRemaining = widthRemaining
+                    - tileChild.getMeasuredWidth() - lp.getMarginEnd() - lp.getMarginStart();
+                int newVisibility = widthRemaining < 0 ? View.GONE : View.VISIBLE;
+                if (tileChild.getVisibility() != newVisibility) {
+                    tileChild.setVisibility(newVisibility);
+                    getChildAt(i - 1).setVisibility(newVisibility); // Hide spacer as well.
+                    childVisibilityChanged = true;
+                }
+            }
+            return childVisibilityChanged;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index a218868..ff5ac76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -33,6 +33,7 @@
 import android.os.Handler;
 import android.provider.AlarmClock;
 import android.service.notification.ZenModeConfig;
+import android.widget.FrameLayout;
 import androidx.annotation.VisibleForTesting;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
@@ -270,8 +271,22 @@
         updateResources();
     }
 
+    /**
+     * The height of QQS should always be the status bar height + 128dp. This is normally easy, but
+     * when there is a notch involved the status bar can remain a fixed pixel size.
+     */
+    private void updateMinimumHeight() {
+        int sbHeight = mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height);
+        int qqsHeight = mContext.getResources().getDimensionPixelSize(
+                R.dimen.qs_quick_header_panel_height);
+
+        setMinimumHeight(sbHeight + qqsHeight);
+    }
+
     private void updateResources() {
         Resources resources = mContext.getResources();
+        updateMinimumHeight();
 
         // Update height for a few views, especially due to landscape mode restricting space.
         mHeaderTextContainerView.getLayoutParams().height =
@@ -282,10 +297,17 @@
                 com.android.internal.R.dimen.quick_qs_offset_height);
         mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
 
-        getLayoutParams().height = resources.getDimensionPixelSize(mQsDisabled
-                ? com.android.internal.R.dimen.quick_qs_offset_height
-                : com.android.internal.R.dimen.quick_qs_total_height);
-        setLayoutParams(getLayoutParams());
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+        if (mQsDisabled) {
+            lp.height = resources.getDimensionPixelSize(
+                    com.android.internal.R.dimen.quick_qs_offset_height);
+        } else {
+            lp.height = Math.max(getMinimumHeight(),
+                    resources.getDimensionPixelSize(
+                            com.android.internal.R.dimen.quick_qs_total_height));
+        }
+
+        setLayoutParams(lp);
 
         updateStatusIconAlphaAnimator();
         updateHeaderTextContainerAlphaAnimator();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index ebfa6af..91afef02 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -265,6 +265,8 @@
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
+        // Clear selected state so it is not announce by talkback.
+        info.setSelected(false);
         if (!TextUtils.isEmpty(mAccessibilityClass)) {
             info.setClassName(mAccessibilityClass);
             if (Switch.class.getName().equals(mAccessibilityClass)) {
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 d1e33d3..f523196 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -142,14 +142,14 @@
 
     @Override
     public void showDetail(boolean show) {
-        int zenDuration = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.ZEN_DURATION, 0);
-        boolean showOnboarding = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0;
+        int zenDuration = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ZEN_DURATION, 0);
+        boolean showOnboarding = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0;
         if (showOnboarding) {
             // don't show on-boarding again or notification ever
-            Settings.Global.putInt(mContext.getContentResolver(),
-                    Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
             // turn on DND
             mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
             // show on-boarding screen
@@ -158,7 +158,7 @@
             Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(intent, 0);
         } else {
             switch (zenDuration) {
-                case Settings.Global.ZEN_DURATION_PROMPT:
+                case Settings.Secure.ZEN_DURATION_PROMPT:
                     mUiHandler.post(() -> {
                         Dialog mDialog = new EnableZenModeDialog(mContext).createDialog();
                         mDialog.getWindow().setType(
@@ -170,7 +170,7 @@
                         mHost.collapsePanels();
                     });
                     break;
-                case Settings.Global.ZEN_DURATION_FOREVER:
+                case Settings.Secure.ZEN_DURATION_FOREVER:
                     mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
                     break;
                 default:
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 e0a9148..c41f087 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -26,7 +26,6 @@
 import android.service.quicksettings.Tile;
 import androidx.annotation.StringRes;
 import android.text.TextUtils;
-import android.text.format.DateFormat;
 import android.util.Log;
 import android.widget.Switch;
 
@@ -37,8 +36,11 @@
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 
+import java.text.DateFormat;
 import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.TimeZone;
 
 public class NightDisplayTile extends QSTileImpl<BooleanState>
         implements ColorDisplayController.Callback {
@@ -144,13 +146,17 @@
                     toggleTimeStringRes = R.string.quick_settings_night_secondary_label_on_at;
                 }
 
-                // Choose between just showing the hour or also showing the minutes (based on the
-                // user-selected toggle time). This helps reduce how much space the label takes.
-                toggleTimeFormat = DateTimeFormatter.ofPattern(
-                        DateFormat.is24HourFormat(mContext) ? PATTERN_HOUR_NINUTE_24 :
-                        toggleTime.getMinute() == 0 ? PATTERN_HOUR : PATTERN_HOUR_MINUTE);
-
-                return mContext.getString(toggleTimeStringRes, toggleTime.format(toggleTimeFormat));
+                // TODO(b/111085930): Move this calendar snippet to a common code location that
+                // settings lib can also access.
+                final Calendar c = Calendar.getInstance();
+                DateFormat nightTileFormat = android.text.format.DateFormat.getTimeFormat(mContext);
+                nightTileFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+                c.setTimeZone(nightTileFormat.getTimeZone());
+                c.set(Calendar.HOUR_OF_DAY, toggleTime.getHour());
+                c.set(Calendar.MINUTE, toggleTime.getMinute());
+                c.set(Calendar.SECOND, 0);
+                c.set(Calendar.MILLISECOND, 0);
+                return mContext.getString(toggleTimeStringRes, nightTileFormat.format(c.getTime()));
 
             default:
                 // No secondary label when auto mode is disabled.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 7e4acc2..63a65d0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -48,6 +48,7 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.OverviewProxyService;
+import com.android.systemui.SysUiServiceProvider;
 import com.google.android.collect.Lists;
 
 import com.android.internal.logging.MetricsLogger;
@@ -1095,7 +1096,7 @@
     }
 
     private StatusBar getStatusBar() {
-        return ((SystemUIApplication) mContext).getComponent(StatusBar.class);
+        return SysUiServiceProvider.getComponent(mContext, StatusBar.class);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index 8d8e206..db2b69f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -88,16 +88,16 @@
     // Show quick scrub tips after opening overview this number of times.
     private static final int QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT = 10;
     // Maximum number of dismissals while still showing swipe-up tips.
-    private static final int MAX_DISMISSAL_ON_SWIPE_UP_SHOW = 4;
+    private static final int MAX_DISMISSAL_ON_SWIPE_UP_SHOW = 2;
     // Number of dismissals for swipe-up tips when exponential backoff starts.
-    private static final int BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW = 2;
+    private static final int BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW = 1;
     // After explicitly dismissing for <= BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW times, show again
     // after launching this number of apps for swipe-up tips.
     private static final int SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS = 5;
     // After explicitly dismissing for > BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW but
     // <= MAX_DISMISSAL_ON_SWIPE_UP_SHOW times, show again after launching this number of apps for
     // swipe-up tips.
-    private static final int SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS_BACK_OFF = 10;
+    private static final int SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS_BACK_OFF = 40;
 
     private final Context mContext;
     private final WindowManager mWindowManager;
@@ -479,7 +479,7 @@
                 ViewGroup.LayoutParams.WRAP_CONTENT,
                 ViewGroup.LayoutParams.WRAP_CONTENT,
                 x, -mNavBarHeight / 2,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                 flags,
                 PixelFormat.TRANSLUCENT);
         lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
index 2c1158d..b4212d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
@@ -21,6 +21,7 @@
 import android.os.IBinder;
 import android.util.Log;
 
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.SystemUIApplication;
 
 /**
@@ -40,8 +41,7 @@
 
     @Override
     public IBinder onBind(Intent intent) {
-        SystemUIApplication app = (SystemUIApplication) getApplication();
-        Recents recents = app.getComponent(Recents.class);
+        Recents recents = SysUiServiceProvider.getComponent(this, Recents.class);
         if (DEBUG) {
             Log.d(TAG, "onBind: " + recents);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 481d54f..91512dd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -19,6 +19,7 @@
 import android.animation.ArgbEvaluator;
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -124,7 +125,7 @@
     public void onClick(View v) {
         if (v.getId() == R.id.screen_pinning_ok_button || mRequestWindow == v) {
             try {
-                ActivityManager.getService().startSystemLockTaskMode(taskId);
+                ActivityTaskManager.getService().startSystemLockTaskMode(taskId);
             } catch (RemoteException e) {}
         }
         clearPrompt();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 544d95c..350fe78 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -28,8 +28,10 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
 import android.app.AppGlobals;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -94,6 +96,7 @@
     AccessibilityManager mAccm;
     ActivityManager mAm;
     IActivityManager mIam;
+    IActivityTaskManager mIatm;
     PackageManager mPm;
     IPackageManager mIpm;
     private final IDreamManager mDreamManager;
@@ -133,6 +136,7 @@
         mAccm = AccessibilityManager.getInstance(context);
         mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
         mIam = ActivityManager.getService();
+        mIatm = ActivityTaskManager.getService();
         mPm = context.getPackageManager();
         mIpm = AppGlobals.getPackageManager();
         mAssistUtils = new AssistUtils(context);
@@ -203,7 +207,7 @@
         if (mIam == null) return false;
 
         try {
-            List<StackInfo> stackInfos = mIam.getAllStackInfos();
+            List<StackInfo> stackInfos = mIatm.getAllStackInfos();
             ActivityManager.StackInfo homeStackInfo = null;
             ActivityManager.StackInfo fullscreenStackInfo = null;
             ActivityManager.StackInfo recentsStackInfo = null;
@@ -261,13 +265,13 @@
     /** Moves an already resumed task to the side of the screen to initiate split screen. */
     public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
             Rect initialBounds) {
-        if (mIam == null) {
+        if (mIatm == null) {
             return false;
         }
 
         try {
-            return mIam.setTaskWindowingModeSplitScreenPrimary(taskId, createMode, true /* onTop */,
-                    false /* animate */, initialBounds, true /* showRecents */);
+            return mIatm.setTaskWindowingModeSplitScreenPrimary(taskId, createMode,
+                    true /* onTop */, false /* animate */, initialBounds, true /* showRecents */);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -276,7 +280,7 @@
 
     public ActivityManager.StackInfo getSplitScreenPrimaryStack() {
         try {
-            return mIam.getStackInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
+            return mIatm.getStackInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
         } catch (RemoteException e) {
             return null;
         }
@@ -324,10 +328,10 @@
 
     /** Set the task's windowing mode. */
     public void setTaskWindowingMode(int taskId, int windowingMode) {
-        if (mIam == null) return;
+        if (mIatm == null) return;
 
         try {
-            mIam.setTaskWindowingMode(taskId, windowingMode, false /* onTop */);
+            mIatm.setTaskWindowingMode(taskId, windowingMode, false /* onTop */);
         } catch (RemoteException | IllegalArgumentException e) {
             e.printStackTrace();
         }
@@ -372,7 +376,7 @@
         if (mIam == null) return false;
 
         try {
-            return mIam.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_PINNED;
+            return mIatm.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_PINNED;
         } catch (RemoteException e) {
             return false;
         }
@@ -413,9 +417,9 @@
         try {
             // Use the recents stack bounds, fallback to fullscreen stack if it is null
             ActivityManager.StackInfo stackInfo =
-                    mIam.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
+                    mIatm.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
             if (stackInfo == null) {
-                stackInfo = mIam.getStackInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+                stackInfo = mIatm.getStackInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
             }
             if (stackInfo != null) {
                 windowRect.set(stackInfo.bounds);
@@ -437,7 +441,7 @@
         if (mIam == null) return;
 
         try {
-            mIam.startInPlaceAnimationOnFrontMostApplication(
+            mIatm.startInPlaceAnimationOnFrontMostApplication(
                     opts == null ? null : opts.toBundle());
         } catch (Exception e) {
             e.printStackTrace();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java b/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java
index 65b96fb..a246141 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.recents.views;
 
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_LEFT;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 0f64ea3..5c925fd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.recents.views;
 
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 
 import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 5c69ae3..53a91e5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents.views;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.view.InputDevice;
@@ -137,7 +138,7 @@
         }
 
         mVisibleDockStates.clear();
-        if (ActivityManager.supportsMultiWindow(mRv.getContext()) && !ssp.hasDockedTask()
+        if (ActivityTaskManager.supportsMultiWindow(mRv.getContext()) && !ssp.hasDockedTask()
                 && mDividerSnapAlgorithm.isSplitScreenFeasible()) {
             Recents.logDockAttempt(mRv.getContext(), event.task.getTopComponent(),
                     event.task.resizeMode);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
index 0fc507b..f217596 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents.views;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.graphics.Point;
 import android.os.Bundle;
@@ -58,7 +59,7 @@
     @Override
     public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(host, info);
-        if (ActivityManager.supportsSplitScreenMultiWindow(mTaskView.getContext())
+        if (ActivityTaskManager.supportsSplitScreenMultiWindow(mTaskView.getContext())
                 && !Recents.getSystemServices().hasDockedTask()) {
             DockState[] dockStates = Recents.getConfiguration()
                     .getDockStatesForCurrentOrientation();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 8a0d7e3..5eaee54 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -16,9 +16,11 @@
 
 package com.android.systemui.screenshot;
 
+import static android.content.Context.NOTIFICATION_SERVICE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-
-import static com.android.systemui.screenshot.GlobalScreenshot.SHARING_INTENT;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_CANCEL_NOTIFICATION;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP;
 import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
 
 import android.animation.Animator;
@@ -26,7 +28,6 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.Notification;
 import android.app.Notification.BigPictureStyle;
@@ -56,10 +57,11 @@
 import android.os.Environment;
 import android.os.PowerManager;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.MediaStore;
+import android.text.TextUtils;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
 import android.view.LayoutInflater;
@@ -72,12 +74,13 @@
 import android.view.animation.Interpolator;
 import android.widget.ImageView;
 import android.widget.Toast;
-
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.SystemUI;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.NotificationChannels;
-
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.OutputStream;
@@ -246,7 +249,10 @@
 
         try {
             // Create screenshot directory if it doesn't exist
-            mScreenshotDir.mkdirs();
+            boolean madeDirs = mScreenshotDir.mkdirs();
+            if (madeDirs == false) {
+                Log.e(TAG, "Couldn't create screenshot directory: " + mScreenshotDir);
+            }
 
             // media provider uses seconds for DATE_MODIFIED and DATE_ADDED, but milliseconds
             // for DATE_TAKEN
@@ -273,7 +279,12 @@
             values.put(MediaStore.Images.ImageColumns.SIZE, new File(mImageFilePath).length());
             Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
 
-            // Create a share intent
+            // Note: Both the share and edit actions are proxied through ActionProxyReceiver in
+            // order to do some common work like dismissing the keyguard and sending
+            // closeSystemWindows
+
+            // Create a share intent, this will always go through the chooser activity first which
+            // should not trigger auto-enter PiP
             String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
             String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
             Intent sharingIntent = new Intent(Intent.ACTION_SEND);
@@ -282,37 +293,47 @@
             sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
             sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
-            // Create a share action for the notification. Note, we proxy the call to
-            // ScreenshotActionReceiver because RemoteViews currently forces an activity options
-            // on the PendingIntent being launched, and since we don't want to trigger the share
-            // sheet in this case, we start the chooser activity directly in
-            // ScreenshotActionReceiver.
-            PendingIntent shareAction = PendingIntent.getBroadcast(context, 0,
-                    new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class)
-                            .putExtra(SHARING_INTENT, sharingIntent),
-                    PendingIntent.FLAG_CANCEL_CURRENT);
+            PendingIntent chooserAction = PendingIntent.getBroadcast(context, 0,
+                    new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+            Intent sharingChooserIntent = Intent.createChooser(sharingIntent, null,
+                    chooserAction.getIntentSender())
+                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            // Create a share action for the notification
+            PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, 0,
+                    new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+                            .putExtra(EXTRA_ACTION_INTENT, sharingChooserIntent)
+                            .putExtra(EXTRA_DISALLOW_ENTER_PIP, true),
+                    PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
             Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
                     R.drawable.ic_screenshot_share,
                     r.getString(com.android.internal.R.string.share), shareAction);
             mNotificationBuilder.addAction(shareActionBuilder.build());
 
+            // Create an edit intent, if a specific package is provided as the editor, then launch
+            // that directly
+            String editorPackage = context.getString(R.string.config_screenshotEditor);
             Intent editIntent = new Intent(Intent.ACTION_EDIT);
+            if (!TextUtils.isEmpty(editorPackage)) {
+                editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
+            }
             editIntent.setType("image/png");
             editIntent.setData(uri);
             editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
             editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
 
-            // Create a edit action for the notification the same way.
-            PendingIntent editAction = PendingIntent.getBroadcast(context, 1,
-                    new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class)
-                            .putExtra(SHARING_INTENT, editIntent),
-                    PendingIntent.FLAG_CANCEL_CURRENT);
+            // Create a edit action
+            PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, 1,
+                    new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+                            .putExtra(EXTRA_ACTION_INTENT, editIntent)
+                            .putExtra(EXTRA_CANCEL_NOTIFICATION, editIntent.getComponent() != null),
+                    PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
             Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
                     R.drawable.ic_screenshot_edit,
                     r.getString(com.android.internal.R.string.screenshot_edit), editAction);
             mNotificationBuilder.addAction(editActionBuilder.build());
 
-
             // Create a delete action for the notification
             PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0,
                     new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
@@ -425,7 +446,9 @@
 
 class GlobalScreenshot {
     static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
-    static final String SHARING_INTENT = "android:screenshot_sharing_intent";
+    static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
+    static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
+    static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
 
     private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
     private static final int SCREENSHOT_DROP_IN_DURATION = 430;
@@ -448,7 +471,6 @@
     private NotificationManager mNotificationManager;
     private Display mDisplay;
     private DisplayMetrics mDisplayMetrics;
-    private Matrix mDisplayMatrix;
 
     private Bitmap mScreenBitmap;
     private View mScreenshotLayout;
@@ -478,7 +500,6 @@
                 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
         // Inflate the screenshot layout
-        mDisplayMatrix = new Matrix();
         mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null);
         mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background);
         mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot);
@@ -508,7 +529,7 @@
         mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         mNotificationManager =
-            (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+            (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
         mDisplay = mWindowManager.getDefaultDisplay();
         mDisplayMetrics = new DisplayMetrics();
         mDisplay.getRealMetrics(mDisplayMetrics);
@@ -558,21 +579,6 @@
     }
 
     /**
-     * @return the current display rotation in degrees
-     */
-    private float getDegreesForRotation(int value) {
-        switch (value) {
-        case Surface.ROTATION_90:
-            return 360f - 90f;
-        case Surface.ROTATION_180:
-            return 360f - 180f;
-        case Surface.ROTATION_270:
-            return 360f - 270f;
-        }
-        return 0f;
-    }
-
-    /**
      * Takes a screenshot of the current display and shows an animation.
      */
     private void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible,
@@ -887,52 +893,39 @@
     }
 
     /**
-     * Receiver to proxy the share or edit intent.
+     * Receiver to proxy the share or edit intent, used to clean up the notification and send
+     * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
      */
-    public static class ScreenshotActionReceiver extends BroadcastReceiver {
+    public static class ActionProxyReceiver extends BroadcastReceiver {
         @Override
-        public void onReceive(Context context, Intent intent) {
-            try {
-                ActivityManager.getService().closeSystemDialogs(SYSTEM_DIALOG_REASON_SCREENSHOT);
-            } catch (RemoteException e) {
-            }
+        public void onReceive(Context context, final Intent intent) {
+            Runnable startActivityRunnable = () -> {
+                ActivityManagerWrapper.getInstance().closeSystemWindows(
+                        SYSTEM_DIALOG_REASON_SCREENSHOT);
 
-            Intent actionIntent = intent.getParcelableExtra(SHARING_INTENT);
-
-            // If this is an edit & default editor exists, route straight there.
-            String editorPackage = context.getResources().getString(R.string.config_screenshotEditor);
-            if (actionIntent.getAction() == Intent.ACTION_EDIT &&
-                    editorPackage != null && editorPackage.length() > 0) {
-                actionIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
-                final NotificationManager nm =
-                        (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-                nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
-            } else {
-                PendingIntent chooseAction = PendingIntent.getBroadcast(context, 0,
-                        new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
-                        PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
-                actionIntent = Intent.createChooser(actionIntent, null,
-                        chooseAction.getIntentSender())
-                        .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
-            }
-
-            ActivityOptions opts = ActivityOptions.makeBasic();
-            opts.setDisallowEnterPictureInPictureWhileLaunching(true);
-
-            context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT);
+                Intent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+                if (intent.getBooleanExtra(EXTRA_CANCEL_NOTIFICATION, false)) {
+                    cancelScreenshotNotification(context);
+                }
+                ActivityOptions opts = ActivityOptions.makeBasic();
+                opts.setDisallowEnterPictureInPictureWhileLaunching(
+                        intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
+                context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT);
+            };
+            StatusBar statusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
+            statusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
+                    true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
         }
     }
 
     /**
-     * Removes the notification for a screenshot after a share or edit target is chosen.
+     * Removes the notification for a screenshot after a share target is chosen.
      */
     public static class TargetChosenReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            // Clear the notification
-            final NotificationManager nm =
-                    (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-            nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
+            // Clear the notification only after the user has chosen a share action
+            cancelScreenshotNotification(context);
         }
     }
 
@@ -946,14 +939,18 @@
                 return;
             }
 
-            // Clear the notification
-            final NotificationManager nm =
-                    (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-            final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
-            nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
+            // Clear the notification when the image is deleted
+            cancelScreenshotNotification(context);
 
             // And delete the image from the media store
+            final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
             new DeleteImageInBackgroundTask(context).execute(uri);
         }
     }
+
+    private static void cancelScreenshotNotification(Context context) {
+        final NotificationManager nm =
+                (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
+        nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index da79884..750002c 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.shortcut;
 
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.os.UserHandle.USER_CURRENT;
 
 import static com.android.systemui.statusbar.phone.NavigationBarGestureHelper.DRAG_MODE_NONE;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 1e5b37c..600964e 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -19,6 +19,7 @@
 import static android.view.WindowManager.DOCKED_INVALID;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.Log;
@@ -71,7 +72,7 @@
                 mTmpRect5.set(mTempOtherInsetRect);
             }
             try {
-                ActivityManager.getService()
+                ActivityTaskManager.getService()
                         .resizeDockedStack(mTmpRect1,
                                 mTmpRect2.isEmpty() ? null : mTmpRect2,
                                 mTmpRect3.isEmpty() ? null : mTmpRect3,
@@ -87,7 +88,7 @@
         @Override
         public void run() {
             try {
-                ActivityManager.getService().dismissSplitScreenMode(false /* onTop */);
+                ActivityTaskManager.getService().dismissSplitScreenMode(false /* onTop */);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to remove stack: " + e);
             }
@@ -98,7 +99,7 @@
         @Override
         public void run() {
             try {
-                ActivityManager.getService().dismissSplitScreenMode(true /* onTop */);
+                ActivityTaskManager.getService().dismissSplitScreenMode(true /* onTop */);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to resize stack: " + e);
             }
@@ -180,7 +181,7 @@
             @Override
             public void run() {
                 try {
-                    ActivityManager.getService().setSplitScreenResizing(resizing);
+                    ActivityTaskManager.getService().setSplitScreenResizing(resizing);
                 } catch (RemoteException e) {
                     Log.w(TAG, "Error calling setDockedStackResizing: " + e);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
deleted file mode 100644
index 6a38797..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ /dev/null
@@ -1,1098 +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
- */
-
-package com.android.systemui.statusbar;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.TimeAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.RectF;
-import android.util.AttributeSet;
-import android.util.MathUtils;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.notification.FakeShadowView;
-import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.phone.DoubleTapHelper;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
-
-/**
- * Base class for both {@link ExpandableNotificationRow} and {@link NotificationShelf}
- * to implement dimming/activating on Keyguard for the double-tap gesture
- */
-public abstract class ActivatableNotificationView extends ExpandableOutlineView {
-
-    private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
-    private static final int ACTIVATE_ANIMATION_LENGTH = 220;
-    private static final long DARK_ANIMATION_LENGTH = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
-
-    /**
-     * The amount of width, which is kept in the end when performing a disappear animation (also
-     * the amount from which the horizontal appearing begins)
-     */
-    private static final float HORIZONTAL_COLLAPSED_REST_PARTIAL = 0.05f;
-
-    /**
-     * At which point from [0,1] does the horizontal collapse animation end (or start when
-     * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
-     */
-    private static final float HORIZONTAL_ANIMATION_END = 0.2f;
-
-    /**
-     * At which point from [0,1] does the alpha animation end (or start when
-     * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
-     */
-    private static final float ALPHA_ANIMATION_END = 0.0f;
-
-    /**
-     * At which point from [0,1] does the horizontal collapse animation start (or start when
-     * expanding)? 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
-     */
-    private static final float HORIZONTAL_ANIMATION_START = 1.0f;
-
-    /**
-     * At which point from [0,1] does the vertical collapse animation start (or end when
-     * expanding) 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
-     */
-    private static final float VERTICAL_ANIMATION_START = 1.0f;
-
-    /**
-     * Scale for the background to animate from when exiting dark mode.
-     */
-    private static final float DARK_EXIT_SCALE_START = 0.93f;
-
-    /**
-     * A sentinel value when no color should be used. Can be used with {@link #setTintColor(int)}
-     * or {@link #setOverrideTintColor(int, float)}.
-     */
-    protected static final int NO_COLOR = 0;
-
-    private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
-            = new PathInterpolator(0.6f, 0, 0.5f, 1);
-    private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
-            = new PathInterpolator(0, 0, 0.5f, 1);
-    private final int mTintedRippleColor;
-    protected final int mNormalRippleColor;
-    private final AccessibilityManager mAccessibilityManager;
-    private final DoubleTapHelper mDoubleTapHelper;
-
-    private boolean mDimmed;
-    private boolean mDark;
-
-    protected int mBgTint = NO_COLOR;
-    private float mBgAlpha = 1f;
-
-    /**
-     * Flag to indicate that the notification has been touched once and the second touch will
-     * click it.
-     */
-    private boolean mActivated;
-
-    private OnActivatedListener mOnActivatedListener;
-
-    private final Interpolator mSlowOutFastInInterpolator;
-    private final Interpolator mSlowOutLinearInInterpolator;
-    private Interpolator mCurrentAppearInterpolator;
-    private Interpolator mCurrentAlphaInterpolator;
-
-    protected NotificationBackgroundView mBackgroundNormal;
-    private NotificationBackgroundView mBackgroundDimmed;
-    private ObjectAnimator mBackgroundAnimator;
-    private RectF mAppearAnimationRect = new RectF();
-    private float mAnimationTranslationY;
-    private boolean mDrawingAppearAnimation;
-    private ValueAnimator mAppearAnimator;
-    private ValueAnimator mBackgroundColorAnimator;
-    private float mAppearAnimationFraction = -1.0f;
-    private float mAppearAnimationTranslation;
-    private final int mNormalColor;
-    private boolean mIsBelowSpeedBump;
-    private FalsingManager mFalsingManager;
-
-    private float mNormalBackgroundVisibilityAmount;
-    private ValueAnimator mFadeInFromDarkAnimator;
-    private float mDimmedBackgroundFadeInAmount = -1;
-    private ValueAnimator.AnimatorUpdateListener mBackgroundVisibilityUpdater
-            = new ValueAnimator.AnimatorUpdateListener() {
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            setNormalBackgroundVisibilityAmount(mBackgroundNormal.getAlpha());
-            mDimmedBackgroundFadeInAmount = mBackgroundDimmed.getAlpha();
-        }
-    };
-    private AnimatorListenerAdapter mFadeInEndListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            super.onAnimationEnd(animation);
-            mFadeInFromDarkAnimator = null;
-            mDimmedBackgroundFadeInAmount = -1;
-            updateBackground();
-        }
-    };
-    private ValueAnimator.AnimatorUpdateListener mUpdateOutlineListener
-            = new ValueAnimator.AnimatorUpdateListener() {
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            updateOutlineAlpha();
-        }
-    };
-    private float mShadowAlpha = 1.0f;
-    private FakeShadowView mFakeShadow;
-    private int mCurrentBackgroundTint;
-    private int mTargetTint;
-    private int mStartTint;
-    private int mOverrideTint;
-    private float mOverrideAmount;
-    private boolean mShadowHidden;
-    /**
-     * Similar to mDimmed but is also true if it's not dimmable but should be
-     */
-    private boolean mNeedsDimming;
-    private int mDimmedAlpha;
-    private boolean mBlockNextTouch;
-    private boolean mIsHeadsUpAnimation;
-    private int mHeadsUpAddStartLocation;
-    private float mHeadsUpLocation;
-    private boolean mIsAppearing;
-
-    public ActivatableNotificationView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
-        mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
-        setClipChildren(false);
-        setClipToPadding(false);
-        mNormalColor = context.getColor(R.color.notification_material_background_color);
-        mTintedRippleColor = context.getColor(
-                R.color.notification_ripple_tinted_color);
-        mNormalRippleColor = context.getColor(
-                R.color.notification_ripple_untinted_color);
-        mFalsingManager = FalsingManager.getInstance(context);
-        mAccessibilityManager = AccessibilityManager.getInstance(mContext);
-
-        mDoubleTapHelper = new DoubleTapHelper(this, (active) -> {
-            if (active) {
-                makeActive();
-            } else {
-                makeInactive(true /* animate */);
-            }
-        }, super::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
-        initDimens();
-    }
-
-    private void initDimens() {
-        mHeadsUpAddStartLocation = getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.notification_content_margin_start);
-    }
-
-    @Override
-    public void onDensityOrFontScaleChanged() {
-        super.onDensityOrFontScaleChanged();
-        initDimens();
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mBackgroundNormal = findViewById(R.id.backgroundNormal);
-        mFakeShadow = findViewById(R.id.fake_shadow);
-        mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
-        mBackgroundDimmed = findViewById(R.id.backgroundDimmed);
-        mDimmedAlpha = Color.alpha(mContext.getColor(
-                R.color.notification_material_background_dimmed_color));
-        initBackground();
-        updateBackground();
-        updateBackgroundTint();
-        updateOutlineAlpha();
-    }
-
-    /**
-     * Sets the custom backgrounds on {@link #mBackgroundNormal} and {@link #mBackgroundDimmed}.
-     * This method can also be used to reload the backgrounds on both of those views, which can
-     * be useful in a configuration change.
-     */
-    protected void initBackground() {
-        mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg);
-        mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim);
-    }
-
-    private final Runnable mTapTimeoutRunnable = new Runnable() {
-        @Override
-        public void run() {
-            makeInactive(true /* animate */);
-        }
-    };
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (mNeedsDimming && ev.getActionMasked() == MotionEvent.ACTION_DOWN
-                && disallowSingleClick(ev) && !isTouchExplorationEnabled()) {
-            if (!mActivated) {
-                return true;
-            } else if (!mDoubleTapHelper.isWithinDoubleTapSlop(ev)) {
-                mBlockNextTouch = true;
-                makeInactive(true /* animate */);
-                return true;
-            }
-        }
-        return super.onInterceptTouchEvent(ev);
-    }
-
-    private boolean isTouchExplorationEnabled() {
-        return mAccessibilityManager.isTouchExplorationEnabled();
-    }
-
-    protected boolean disallowSingleClick(MotionEvent ev) {
-        return false;
-    }
-
-    protected boolean handleSlideBack() {
-        return false;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        boolean result;
-        if (mBlockNextTouch) {
-            mBlockNextTouch = false;
-            return false;
-        }
-        if (mNeedsDimming && !isTouchExplorationEnabled() && isInteractive()) {
-            boolean wasActivated = mActivated;
-            result = handleTouchEventDimmed(event);
-            if (wasActivated && result && event.getAction() == MotionEvent.ACTION_UP) {
-                removeCallbacks(mTapTimeoutRunnable);
-            }
-        } else {
-            result = super.onTouchEvent(event);
-        }
-        return result;
-    }
-
-    /**
-     * @return whether this view is interactive and can be double tapped
-     */
-    protected boolean isInteractive() {
-        return true;
-    }
-
-    @Override
-    public void drawableHotspotChanged(float x, float y) {
-        if (!mDimmed){
-            mBackgroundNormal.drawableHotspotChanged(x, y);
-        }
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mDimmed) {
-            mBackgroundDimmed.setState(getDrawableState());
-        } else {
-            mBackgroundNormal.setState(getDrawableState());
-        }
-    }
-
-    public void setRippleAllowed(boolean allowed) {
-        mBackgroundNormal.setPressedAllowed(allowed);
-    }
-
-    private boolean handleTouchEventDimmed(MotionEvent event) {
-        if (mNeedsDimming && !mDimmed) {
-            // We're actually dimmed, but our content isn't dimmable, let's ensure we have a ripple
-            super.onTouchEvent(event);
-        }
-        return mDoubleTapHelper.onTouchEvent(event, getActualHeight());
-    }
-
-    @Override
-    public boolean performClick() {
-        if (!mNeedsDimming || isTouchExplorationEnabled()) {
-            return super.performClick();
-        }
-        return false;
-    }
-
-    private void makeActive() {
-        mFalsingManager.onNotificationActive();
-        startActivateAnimation(false /* reverse */);
-        mActivated = true;
-        if (mOnActivatedListener != null) {
-            mOnActivatedListener.onActivated(this);
-        }
-    }
-
-    private void startActivateAnimation(final boolean reverse) {
-        if (!isAttachedToWindow()) {
-            return;
-        }
-        if (!isDimmable()) {
-            return;
-        }
-        int widthHalf = mBackgroundNormal.getWidth()/2;
-        int heightHalf = mBackgroundNormal.getActualHeight()/2;
-        float radius = (float) Math.sqrt(widthHalf*widthHalf + heightHalf*heightHalf);
-        Animator animator;
-        if (reverse) {
-            animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
-                    widthHalf, heightHalf, radius, 0);
-        } else {
-            animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
-                    widthHalf, heightHalf, 0, radius);
-        }
-        mBackgroundNormal.setVisibility(View.VISIBLE);
-        Interpolator interpolator;
-        Interpolator alphaInterpolator;
-        if (!reverse) {
-            interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
-            alphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
-        } else {
-            interpolator = ACTIVATE_INVERSE_INTERPOLATOR;
-            alphaInterpolator = ACTIVATE_INVERSE_ALPHA_INTERPOLATOR;
-        }
-        animator.setInterpolator(interpolator);
-        animator.setDuration(ACTIVATE_ANIMATION_LENGTH);
-        if (reverse) {
-            mBackgroundNormal.setAlpha(1f);
-            animator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    updateBackground();
-                }
-            });
-            animator.start();
-        } else {
-            mBackgroundNormal.setAlpha(0.4f);
-            animator.start();
-        }
-        mBackgroundNormal.animate()
-                .alpha(reverse ? 0f : 1f)
-                .setInterpolator(alphaInterpolator)
-                .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                    @Override
-                    public void onAnimationUpdate(ValueAnimator animation) {
-                        float animatedFraction = animation.getAnimatedFraction();
-                        if (reverse) {
-                            animatedFraction = 1.0f - animatedFraction;
-                        }
-                        setNormalBackgroundVisibilityAmount(animatedFraction);
-                    }
-                })
-                .setDuration(ACTIVATE_ANIMATION_LENGTH);
-    }
-
-    /**
-     * Cancels the hotspot and makes the notification inactive.
-     */
-    public void makeInactive(boolean animate) {
-        if (mActivated) {
-            mActivated = false;
-            if (mDimmed) {
-                if (animate) {
-                    startActivateAnimation(true /* reverse */);
-                } else {
-                    updateBackground();
-                }
-            }
-        }
-        if (mOnActivatedListener != null) {
-            mOnActivatedListener.onActivationReset(this);
-        }
-        removeCallbacks(mTapTimeoutRunnable);
-    }
-
-    public void setDimmed(boolean dimmed, boolean fade) {
-        mNeedsDimming = dimmed;
-        dimmed &= isDimmable();
-        if (mDimmed != dimmed) {
-            mDimmed = dimmed;
-            resetBackgroundAlpha();
-            if (fade) {
-                fadeDimmedBackground();
-            } else {
-                updateBackground();
-            }
-        }
-    }
-
-    public boolean isDimmable() {
-        return true;
-    }
-
-    public void setDark(boolean dark, boolean fade, long delay) {
-        super.setDark(dark, fade, delay);
-        if (mDark == dark) {
-            return;
-        }
-        mDark = dark;
-        updateBackground();
-        updateBackgroundTint(false);
-        if (!dark && fade && !shouldHideBackground()) {
-            fadeInFromDark(delay);
-        }
-        updateOutlineAlpha();
-    }
-
-    private void updateOutlineAlpha() {
-        if (mDark) {
-            setOutlineAlpha(0f);
-            return;
-        }
-        float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
-        alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
-        alpha *= mShadowAlpha;
-        if (mFadeInFromDarkAnimator != null) {
-            alpha *= mFadeInFromDarkAnimator.getAnimatedFraction();
-        }
-        setOutlineAlpha(alpha);
-    }
-
-    public void setNormalBackgroundVisibilityAmount(float normalBackgroundVisibilityAmount) {
-        mNormalBackgroundVisibilityAmount = normalBackgroundVisibilityAmount;
-        updateOutlineAlpha();
-    }
-
-    @Override
-    public void setBelowSpeedBump(boolean below) {
-        super.setBelowSpeedBump(below);
-        if (below != mIsBelowSpeedBump) {
-            mIsBelowSpeedBump = below;
-            updateBackgroundTint();
-            onBelowSpeedBumpChanged();
-        }
-    }
-
-    protected void onBelowSpeedBumpChanged() {
-    }
-
-    /**
-     * @return whether we are below the speed bump
-     */
-    public boolean isBelowSpeedBump() {
-        return mIsBelowSpeedBump;
-    }
-
-    /**
-     * Sets the tint color of the background
-     */
-    public void setTintColor(int color) {
-        setTintColor(color, false);
-    }
-
-    /**
-     * Sets the tint color of the background
-     */
-    public void setTintColor(int color, boolean animated) {
-        if (color != mBgTint) {
-            mBgTint = color;
-            updateBackgroundTint(animated);
-        }
-    }
-
-    @Override
-    public void setDistanceToTopRoundness(float distanceToTopRoundness) {
-        super.setDistanceToTopRoundness(distanceToTopRoundness);
-        mBackgroundNormal.setDistanceToTopRoundness(distanceToTopRoundness);
-        mBackgroundDimmed.setDistanceToTopRoundness(distanceToTopRoundness);
-    }
-
-    /**
-     * Set an override tint color that is used for the background.
-     *
-     * @param color the color that should be used to tint the background.
-     *              This can be {@link #NO_COLOR} if the tint should be normally computed.
-     * @param overrideAmount a value from 0 to 1 how much the override tint should be used. The
-     *                       background color will then be the interpolation between this and the
-     *                       regular background color, where 1 means the overrideTintColor is fully
-     *                       used and the background color not at all.
-     */
-    public void setOverrideTintColor(int color, float overrideAmount) {
-        if (mDark) {
-            color = NO_COLOR;
-            overrideAmount = 0;
-        }
-        mOverrideTint = color;
-        mOverrideAmount = overrideAmount;
-        int newColor = calculateBgColor();
-        setBackgroundTintColor(newColor);
-        if (!isDimmable() && mNeedsDimming) {
-           mBackgroundNormal.setDrawableAlpha((int) NotificationUtils.interpolate(255,
-                   mDimmedAlpha,
-                   overrideAmount));
-        } else {
-            mBackgroundNormal.setDrawableAlpha(255);
-        }
-    }
-
-    protected void updateBackgroundTint() {
-        updateBackgroundTint(false /* animated */);
-    }
-
-    private void updateBackgroundTint(boolean animated) {
-        if (mBackgroundColorAnimator != null) {
-            mBackgroundColorAnimator.cancel();
-        }
-        int rippleColor = getRippleColor();
-        mBackgroundDimmed.setRippleColor(rippleColor);
-        mBackgroundNormal.setRippleColor(rippleColor);
-        int color = calculateBgColor();
-        if (!animated) {
-            setBackgroundTintColor(color);
-        } else if (color != mCurrentBackgroundTint) {
-            mStartTint = mCurrentBackgroundTint;
-            mTargetTint = color;
-            mBackgroundColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
-            mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    int newColor = NotificationUtils.interpolateColors(mStartTint, mTargetTint,
-                            animation.getAnimatedFraction());
-                    setBackgroundTintColor(newColor);
-                }
-            });
-            mBackgroundColorAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-            mBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
-            mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mBackgroundColorAnimator = null;
-                }
-            });
-            mBackgroundColorAnimator.start();
-        }
-    }
-
-    protected void setBackgroundTintColor(int color) {
-        if (color != mCurrentBackgroundTint) {
-            mCurrentBackgroundTint = color;
-            if (color == mNormalColor) {
-                // We don't need to tint a normal notification
-                color = 0;
-            }
-            mBackgroundDimmed.setTint(color);
-            mBackgroundNormal.setTint(color);
-        }
-    }
-
-    /**
-     * Fades in the background when exiting dark mode.
-     */
-    private void fadeInFromDark(long delay) {
-        final View background = mDimmed ? mBackgroundDimmed : mBackgroundNormal;
-        background.setAlpha(0f);
-        mBackgroundVisibilityUpdater.onAnimationUpdate(null);
-        background.animate()
-                .alpha(1f)
-                .setDuration(DARK_ANIMATION_LENGTH)
-                .setStartDelay(delay)
-                .setInterpolator(Interpolators.ALPHA_IN)
-                .setListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationCancel(Animator animation) {
-                        // Jump state if we are cancelled
-                        background.setAlpha(1f);
-                    }
-                })
-                .setUpdateListener(mBackgroundVisibilityUpdater)
-                .start();
-        mFadeInFromDarkAnimator = TimeAnimator.ofFloat(0.0f, 1.0f);
-        mFadeInFromDarkAnimator.setDuration(DARK_ANIMATION_LENGTH);
-        mFadeInFromDarkAnimator.setStartDelay(delay);
-        mFadeInFromDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        mFadeInFromDarkAnimator.addListener(mFadeInEndListener);
-        mFadeInFromDarkAnimator.addUpdateListener(mUpdateOutlineListener);
-        mFadeInFromDarkAnimator.start();
-    }
-
-    /**
-     * Fades the background when the dimmed state changes.
-     */
-    private void fadeDimmedBackground() {
-        mBackgroundDimmed.animate().cancel();
-        mBackgroundNormal.animate().cancel();
-        if (mActivated) {
-            updateBackground();
-            return;
-        }
-        if (!shouldHideBackground()) {
-            if (mDimmed) {
-                mBackgroundDimmed.setVisibility(View.VISIBLE);
-            } else {
-                mBackgroundNormal.setVisibility(View.VISIBLE);
-            }
-        }
-        float startAlpha = mDimmed ? 1f : 0;
-        float endAlpha = mDimmed ? 0 : 1f;
-        int duration = BACKGROUND_ANIMATION_LENGTH_MS;
-        // Check whether there is already a background animation running.
-        if (mBackgroundAnimator != null) {
-            startAlpha = (Float) mBackgroundAnimator.getAnimatedValue();
-            duration = (int) mBackgroundAnimator.getCurrentPlayTime();
-            mBackgroundAnimator.removeAllListeners();
-            mBackgroundAnimator.cancel();
-            if (duration <= 0) {
-                updateBackground();
-                return;
-            }
-        }
-        mBackgroundNormal.setAlpha(startAlpha);
-        mBackgroundAnimator =
-                ObjectAnimator.ofFloat(mBackgroundNormal, View.ALPHA, startAlpha, endAlpha);
-        mBackgroundAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mBackgroundAnimator.setDuration(duration);
-        mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                updateBackground();
-                mBackgroundAnimator = null;
-                if (mFadeInFromDarkAnimator == null) {
-                    mDimmedBackgroundFadeInAmount = -1;
-                }
-            }
-        });
-        mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater);
-        mBackgroundAnimator.start();
-    }
-
-    protected void updateBackgroundAlpha(float transformationAmount) {
-        mBgAlpha =  isChildInGroup() && mDimmed ? transformationAmount : 1f;
-        if (mDimmedBackgroundFadeInAmount != -1) {
-            mBgAlpha *= mDimmedBackgroundFadeInAmount;
-        }
-        mBackgroundDimmed.setAlpha(mBgAlpha);
-    }
-
-    protected void resetBackgroundAlpha() {
-        updateBackgroundAlpha(0f /* transformationAmount */);
-    }
-
-    protected void updateBackground() {
-        cancelFadeAnimations();
-        if (shouldHideBackground()) {
-            mBackgroundDimmed.setVisibility(INVISIBLE);
-            mBackgroundNormal.setVisibility(mActivated ? VISIBLE : INVISIBLE);
-        } else if (mDimmed) {
-            // When groups are animating to the expanded state from the lockscreen, show the
-            // normal background instead of the dimmed background
-            final boolean dontShowDimmed = isGroupExpansionChanging() && isChildInGroup();
-            mBackgroundDimmed.setVisibility(dontShowDimmed ? View.INVISIBLE : View.VISIBLE);
-            mBackgroundNormal.setVisibility((mActivated || dontShowDimmed)
-                    ? View.VISIBLE
-                    : View.INVISIBLE);
-        } else {
-            mBackgroundDimmed.setVisibility(View.INVISIBLE);
-            mBackgroundNormal.setVisibility(View.VISIBLE);
-            mBackgroundNormal.setAlpha(1f);
-            removeCallbacks(mTapTimeoutRunnable);
-            // make in inactive to avoid it sticking around active
-            makeInactive(false /* animate */);
-        }
-        setNormalBackgroundVisibilityAmount(
-                mBackgroundNormal.getVisibility() == View.VISIBLE ? 1.0f : 0.0f);
-    }
-
-    protected void updateBackgroundClipping() {
-        mBackgroundNormal.setBottomAmountClips(!isChildInGroup());
-        mBackgroundDimmed.setBottomAmountClips(!isChildInGroup());
-    }
-
-    protected boolean shouldHideBackground() {
-        return mDark;
-    }
-
-    private void cancelFadeAnimations() {
-        if (mBackgroundAnimator != null) {
-            mBackgroundAnimator.cancel();
-        }
-        mBackgroundDimmed.animate().cancel();
-        mBackgroundNormal.animate().cancel();
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        setPivotX(getWidth() / 2);
-    }
-
-    @Override
-    public void setActualHeight(int actualHeight, boolean notifyListeners) {
-        super.setActualHeight(actualHeight, notifyListeners);
-        setPivotY(actualHeight / 2);
-        mBackgroundNormal.setActualHeight(actualHeight);
-        mBackgroundDimmed.setActualHeight(actualHeight);
-    }
-
-    @Override
-    public void setClipTopAmount(int clipTopAmount) {
-        super.setClipTopAmount(clipTopAmount);
-        mBackgroundNormal.setClipTopAmount(clipTopAmount);
-        mBackgroundDimmed.setClipTopAmount(clipTopAmount);
-    }
-
-    @Override
-    public void setClipBottomAmount(int clipBottomAmount) {
-        super.setClipBottomAmount(clipBottomAmount);
-        mBackgroundNormal.setClipBottomAmount(clipBottomAmount);
-        mBackgroundDimmed.setClipBottomAmount(clipBottomAmount);
-    }
-
-    @Override
-    public void performRemoveAnimation(long duration, long delay,
-            float translationDirection, boolean isHeadsUpAnimation, float endLocation,
-            Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
-        enableAppearDrawing(true);
-        mIsHeadsUpAnimation = isHeadsUpAnimation;
-        mHeadsUpLocation = endLocation;
-        if (mDrawingAppearAnimation) {
-            startAppearAnimation(false /* isAppearing */, translationDirection,
-                    delay, duration, onFinishedRunnable, animationListener);
-        } else if (onFinishedRunnable != null) {
-            onFinishedRunnable.run();
-        }
-    }
-
-    @Override
-    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
-        enableAppearDrawing(true);
-        mIsHeadsUpAnimation = isHeadsUpAppear;
-        mHeadsUpLocation = mHeadsUpAddStartLocation;
-        if (mDrawingAppearAnimation) {
-            startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
-                    duration, null, null);
-        }
-    }
-
-    private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
-            long duration, final Runnable onFinishedRunnable,
-            AnimatorListenerAdapter animationListener) {
-        cancelAppearAnimation();
-        mAnimationTranslationY = translationDirection * getActualHeight();
-        if (mAppearAnimationFraction == -1.0f) {
-            // not initialized yet, we start anew
-            if (isAppearing) {
-                mAppearAnimationFraction = 0.0f;
-                mAppearAnimationTranslation = mAnimationTranslationY;
-            } else {
-                mAppearAnimationFraction = 1.0f;
-                mAppearAnimationTranslation = 0;
-            }
-        }
-        mIsAppearing = isAppearing;
-
-        float targetValue;
-        if (isAppearing) {
-            mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
-            mCurrentAlphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
-            targetValue = 1.0f;
-        } else {
-            mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
-            mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
-            targetValue = 0.0f;
-        }
-        mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
-                targetValue);
-        mAppearAnimator.setInterpolator(Interpolators.LINEAR);
-        mAppearAnimator.setDuration(
-                (long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
-        mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                mAppearAnimationFraction = (float) animation.getAnimatedValue();
-                updateAppearAnimationAlpha();
-                updateAppearRect();
-                invalidate();
-            }
-        });
-        if (animationListener != null) {
-            mAppearAnimator.addListener(animationListener);
-        }
-        if (delay > 0) {
-            // we need to apply the initial state already to avoid drawn frames in the wrong state
-            updateAppearAnimationAlpha();
-            updateAppearRect();
-            mAppearAnimator.setStartDelay(delay);
-        }
-        mAppearAnimator.addListener(new AnimatorListenerAdapter() {
-            private boolean mWasCancelled;
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (onFinishedRunnable != null) {
-                    onFinishedRunnable.run();
-                }
-                if (!mWasCancelled) {
-                    enableAppearDrawing(false);
-                    onAppearAnimationFinished(isAppearing);
-                }
-            }
-
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mWasCancelled = false;
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mWasCancelled = true;
-            }
-        });
-        mAppearAnimator.start();
-    }
-
-    protected void onAppearAnimationFinished(boolean wasAppearing) {
-    }
-
-    private void cancelAppearAnimation() {
-        if (mAppearAnimator != null) {
-            mAppearAnimator.cancel();
-            mAppearAnimator = null;
-        }
-    }
-
-    public void cancelAppearDrawing() {
-        cancelAppearAnimation();
-        enableAppearDrawing(false);
-    }
-
-    private void updateAppearRect() {
-        float inverseFraction = (1.0f - mAppearAnimationFraction);
-        float translationFraction = mCurrentAppearInterpolator.getInterpolation(inverseFraction);
-        float translateYTotalAmount = translationFraction * mAnimationTranslationY;
-        mAppearAnimationTranslation = translateYTotalAmount;
-
-        // handle width animation
-        float widthFraction = (inverseFraction - (1.0f - HORIZONTAL_ANIMATION_START))
-                / (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
-        widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
-        widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
-        float startWidthFraction = HORIZONTAL_COLLAPSED_REST_PARTIAL;
-        if (mIsHeadsUpAnimation && !mIsAppearing) {
-            startWidthFraction = 0;
-        }
-        float width = MathUtils.lerp(startWidthFraction, 1.0f, 1.0f - widthFraction)
-                        * getWidth();
-        float left;
-        float right;
-        if (mIsHeadsUpAnimation) {
-            left = MathUtils.lerp(mHeadsUpLocation, 0, 1.0f - widthFraction);
-            right = left + width;
-        } else {
-            left = getWidth() * 0.5f - width / 2.0f;
-            right = getWidth() - left;
-        }
-
-        // handle top animation
-        float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
-                VERTICAL_ANIMATION_START;
-        heightFraction = Math.max(0.0f, heightFraction);
-        heightFraction = mCurrentAppearInterpolator.getInterpolation(heightFraction);
-
-        float top;
-        float bottom;
-        final int actualHeight = getActualHeight();
-        if (mAnimationTranslationY > 0.0f) {
-            bottom = actualHeight - heightFraction * mAnimationTranslationY * 0.1f
-                    - translateYTotalAmount;
-            top = bottom * heightFraction;
-        } else {
-            top = heightFraction * (actualHeight + mAnimationTranslationY) * 0.1f -
-                    translateYTotalAmount;
-            bottom = actualHeight * (1 - heightFraction) + top * heightFraction;
-        }
-        mAppearAnimationRect.set(left, top, right, bottom);
-        setOutlineRect(left, top + mAppearAnimationTranslation, right,
-                bottom + mAppearAnimationTranslation);
-    }
-
-    private void updateAppearAnimationAlpha() {
-        float contentAlphaProgress = mAppearAnimationFraction;
-        contentAlphaProgress = contentAlphaProgress / (1.0f - ALPHA_ANIMATION_END);
-        contentAlphaProgress = Math.min(1.0f, contentAlphaProgress);
-        contentAlphaProgress = mCurrentAlphaInterpolator.getInterpolation(contentAlphaProgress);
-        setContentAlpha(contentAlphaProgress);
-    }
-
-    private void setContentAlpha(float contentAlpha) {
-        View contentView = getContentView();
-        if (contentView.hasOverlappingRendering()) {
-            int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE
-                    : LAYER_TYPE_HARDWARE;
-            int currentLayerType = contentView.getLayerType();
-            if (currentLayerType != layerType) {
-                contentView.setLayerType(layerType, null);
-            }
-        }
-        contentView.setAlpha(contentAlpha);
-    }
-
-    @Override
-    protected void applyRoundness() {
-        super.applyRoundness();
-        applyBackgroundRoundness(getCurrentBackgroundRadiusTop(),
-                getCurrentBackgroundRadiusBottom());
-    }
-
-    protected void applyBackgroundRoundness(float topRadius, float bottomRadius) {
-        mBackgroundDimmed.setRoundness(topRadius, bottomRadius);
-        mBackgroundNormal.setRoundness(topRadius, bottomRadius);
-    }
-
-    @Override
-    protected void setBackgroundTop(int backgroundTop) {
-        mBackgroundDimmed.setBackgroundTop(backgroundTop);
-        mBackgroundNormal.setBackgroundTop(backgroundTop);
-    }
-
-    protected abstract View getContentView();
-
-    public int calculateBgColor() {
-        return calculateBgColor(true /* withTint */, true /* withOverRide */);
-    }
-
-    @Override
-    protected boolean childNeedsClipping(View child) {
-        if (child instanceof NotificationBackgroundView && isClippingNeeded()) {
-            return true;
-        }
-        return super.childNeedsClipping(child);
-    }
-
-    /**
-     * @param withTint should a possible tint be factored in?
-     * @param withOverRide should the value be interpolated with {@link #mOverrideTint}
-     * @return the calculated background color
-     */
-    private int calculateBgColor(boolean withTint, boolean withOverRide) {
-        if (withTint && mDark) {
-            return getContext().getColor(R.color.notification_material_background_dark_color);
-        }
-        if (withOverRide && mOverrideTint != NO_COLOR) {
-            int defaultTint = calculateBgColor(withTint, false);
-            return NotificationUtils.interpolateColors(defaultTint, mOverrideTint, mOverrideAmount);
-        }
-        if (withTint && mBgTint != NO_COLOR) {
-            return mBgTint;
-        } else {
-            return mNormalColor;
-        }
-    }
-
-    protected int getRippleColor() {
-        if (mBgTint != 0) {
-            return mTintedRippleColor;
-        } else {
-            return mNormalRippleColor;
-        }
-    }
-
-    /**
-     * When we draw the appear animation, we render the view in a bitmap and render this bitmap
-     * as a shader of a rect. This call creates the Bitmap and switches the drawing mode,
-     * such that the normal drawing of the views does not happen anymore.
-     *
-     * @param enable Should it be enabled.
-     */
-    private void enableAppearDrawing(boolean enable) {
-        if (enable != mDrawingAppearAnimation) {
-            mDrawingAppearAnimation = enable;
-            if (!enable) {
-                setContentAlpha(1.0f);
-                mAppearAnimationFraction = -1;
-                setOutlineRect(null);
-            }
-            invalidate();
-        }
-    }
-
-    public boolean isDrawingAppearAnimation() {
-        return mDrawingAppearAnimation;
-    }
-
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        if (mDrawingAppearAnimation) {
-            canvas.save();
-            canvas.translate(0, mAppearAnimationTranslation);
-        }
-        super.dispatchDraw(canvas);
-        if (mDrawingAppearAnimation) {
-            canvas.restore();
-        }
-    }
-
-    public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
-        mOnActivatedListener = onActivatedListener;
-    }
-
-    public boolean hasSameBgColor(ActivatableNotificationView otherView) {
-        return calculateBgColor() == otherView.calculateBgColor();
-    }
-
-    @Override
-    public float getShadowAlpha() {
-        return mShadowAlpha;
-    }
-
-    @Override
-    public void setShadowAlpha(float shadowAlpha) {
-        if (shadowAlpha != mShadowAlpha) {
-            mShadowAlpha = shadowAlpha;
-            updateOutlineAlpha();
-        }
-    }
-
-    @Override
-    public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
-            int outlineTranslation) {
-        boolean hiddenBefore = mShadowHidden;
-        mShadowHidden = shadowIntensity == 0.0f;
-        if (!mShadowHidden || !hiddenBefore) {
-            mFakeShadow.setFakeShadowTranslationZ(shadowIntensity * (getTranslationZ()
-                            + FakeShadowView.SHADOW_SIBLING_TRESHOLD), outlineAlpha, shadowYEnd,
-                    outlineTranslation);
-        }
-    }
-
-    public int getBackgroundColorWithoutTint() {
-        return calculateBgColor(false /* withTint */, false /* withOverride */);
-    }
-
-    public boolean isPinned() {
-        return false;
-    }
-
-    public boolean isHeadsUpAnimatingAway() {
-        return false;
-    }
-
-    public interface OnActivatedListener {
-        void onActivated(ActivatableNotificationView view);
-        void onActivationReset(ActivatableNotificationView view);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java
deleted file mode 100644
index 06dc4e6..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java
+++ /dev/null
@@ -1,48 +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
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-/**
- * An ImageView which does not have overlapping renderings commands and therefore does not need a
- * layer when alpha is changed.
- */
-public class AlphaImageView extends ImageView {
-    public AlphaImageView(Context context) {
-        super(context);
-    }
-
-    public AlphaImageView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsInfo.java
deleted file mode 100644
index 7999a6c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsInfo.java
+++ /dev/null
@@ -1,207 +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 android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.os.RemoteException;
-import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
-
-/**
- * The guts of a notification revealed when performing a long press.
- */
-public class AppOpsInfo extends LinearLayout implements NotificationGuts.GutsContent {
-    private static final String TAG = "AppOpsGuts";
-
-    private PackageManager mPm;
-
-    private String mPkg;
-    private String mAppName;
-    private int mAppUid;
-    private StatusBarNotification mSbn;
-    private ArraySet<Integer> mAppOps;
-    private MetricsLogger mMetricsLogger;
-    private OnSettingsClickListener mOnSettingsClickListener;
-    private NotificationGuts mGutsContainer;
-
-    private OnClickListener mOnOk = v -> {
-        closeControls(v);
-    };
-
-    public AppOpsInfo(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public interface OnSettingsClickListener {
-        void onClick(View v, String pkg, int uid, ArraySet<Integer> ops);
-    }
-
-    public void bindGuts(final PackageManager pm,
-            final OnSettingsClickListener onSettingsClick,
-            final StatusBarNotification sbn,
-            ArraySet<Integer> activeOps) {
-        mPkg = sbn.getPackageName();
-        mSbn = sbn;
-        mPm = pm;
-        mAppName = mPkg;
-        mOnSettingsClickListener = onSettingsClick;
-        mAppOps = activeOps;
-
-        bindHeader();
-        bindPrompt();
-        bindButtons();
-
-        mMetricsLogger = new MetricsLogger();
-        mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, true);
-    }
-
-    private void bindHeader() {
-        // Package name
-        Drawable pkgicon = null;
-        ApplicationInfo info;
-        try {
-            info = mPm.getApplicationInfo(mPkg,
-                    PackageManager.MATCH_UNINSTALLED_PACKAGES
-                            | PackageManager.MATCH_DISABLED_COMPONENTS
-                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                            | PackageManager.MATCH_DIRECT_BOOT_AWARE);
-            if (info != null) {
-                mAppUid = mSbn.getUid();
-                mAppName = String.valueOf(mPm.getApplicationLabel(info));
-                pkgicon = mPm.getApplicationIcon(info);
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // app is gone, just show package name and generic icon
-            pkgicon = mPm.getDefaultActivityIcon();
-        }
-        ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
-        ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
-    }
-
-    private void bindPrompt() {
-        final TextView prompt = findViewById(R.id.prompt);
-        prompt.setText(getPrompt());
-    }
-
-    private void bindButtons() {
-        View settings =  findViewById(R.id.settings);
-        settings.setOnClickListener((View view) -> {
-            mOnSettingsClickListener.onClick(view, mPkg, mAppUid, mAppOps);
-        });
-        TextView ok = findViewById(R.id.ok);
-        ok.setOnClickListener(mOnOk);
-    }
-
-    private String getPrompt() {
-        if (mAppOps == null || mAppOps.size() == 0) {
-            return "";
-        } else if (mAppOps.size() == 1) {
-            if (mAppOps.contains(AppOpsManager.OP_CAMERA)) {
-                return mContext.getString(R.string.appops_camera);
-            } else if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) {
-                return mContext.getString(R.string.appops_microphone);
-            } else {
-                return mContext.getString(R.string.appops_overlay);
-            }
-        } else if (mAppOps.size() == 2) {
-            if (mAppOps.contains(AppOpsManager.OP_CAMERA)) {
-                if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) {
-                    return mContext.getString(R.string.appops_camera_mic);
-                } else {
-                    return mContext.getString(R.string.appops_camera_overlay);
-                }
-            } else {
-                return mContext.getString(R.string.appops_mic_overlay);
-            }
-        } else {
-            return mContext.getString(R.string.appops_camera_mic_overlay);
-        }
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        if (mGutsContainer != null &&
-                event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
-            if (mGutsContainer.isExposed()) {
-                event.getText().add(mContext.getString(
-                        R.string.notification_channel_controls_opened_accessibility, mAppName));
-            } else {
-                event.getText().add(mContext.getString(
-                        R.string.notification_channel_controls_closed_accessibility, mAppName));
-            }
-        }
-    }
-
-    private void closeControls(View v) {
-        mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false);
-        int[] parentLoc = new int[2];
-        int[] targetLoc = new int[2];
-        mGutsContainer.getLocationOnScreen(parentLoc);
-        v.getLocationOnScreen(targetLoc);
-        final int centerX = v.getWidth() / 2;
-        final int centerY = v.getHeight() / 2;
-        final int x = targetLoc[0] - parentLoc[0] + centerX;
-        final int y = targetLoc[1] - parentLoc[1] + centerY;
-        mGutsContainer.closeControls(x, y, false, false);
-    }
-
-    @Override
-    public void setGutsParent(NotificationGuts guts) {
-        mGutsContainer = guts;
-    }
-
-    @Override
-    public boolean willBeRemoved() {
-        return false;
-    }
-
-    @Override
-    public boolean shouldBeSaved() {
-        return false;
-    }
-
-    @Override
-    public View getContentView() {
-        return this;
-    }
-
-    @Override
-    public boolean handleCloseControls(boolean save, boolean force) {
-        return false;
-    }
-
-    @Override
-    public int getActualHeight() {
-        return getHeight();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java
deleted file mode 100644
index 019c680..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java
+++ /dev/null
@@ -1,68 +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;
-
-import android.app.AppOpsManager;
-import android.content.Context;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.ForegroundServiceController;
-
-/**
- * This class handles listening to notification updates and passing them along to
- * NotificationPresenter to be displayed to the user.
- */
-public class AppOpsListener implements AppOpsManager.OnOpActiveChangedListener {
-    private static final String TAG = "NotificationListener";
-
-    // Dependencies:
-    private final ForegroundServiceController mFsc =
-            Dependency.get(ForegroundServiceController.class);
-
-    private final Context mContext;
-    protected NotificationPresenter mPresenter;
-    protected NotificationEntryManager mEntryManager;
-    protected final AppOpsManager mAppOps;
-
-    protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA,
-            AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
-            AppOpsManager.OP_RECORD_AUDIO};
-
-    public AppOpsListener(Context context) {
-        mContext = context;
-        mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-    }
-
-    public void setUpWithPresenter(NotificationPresenter presenter,
-            NotificationEntryManager entryManager) {
-        mPresenter = presenter;
-        mEntryManager = entryManager;
-        mAppOps.startWatchingActive(OPS, this);
-    }
-
-    public void destroy() {
-        mAppOps.stopWatchingActive(this);
-    }
-
-    @Override
-    public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
-        mFsc.onAppOpChanged(code, uid, packageName, active);
-        mPresenter.getHandler().post(() -> {
-          mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active);
-        });
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 145a246..909cd79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -85,11 +85,11 @@
     private static final int MSG_SHOW_SHUTDOWN_UI              = 36 << MSG_SHIFT;
     private static final int MSG_SET_TOP_APP_HIDES_STATUS_BAR  = 37 << MSG_SHIFT;
     private static final int MSG_ROTATION_PROPOSAL             = 38 << MSG_SHIFT;
-    private static final int MSG_FINGERPRINT_SHOW              = 39 << MSG_SHIFT;
-    private static final int MSG_FINGERPRINT_AUTHENTICATED     = 40 << MSG_SHIFT;
-    private static final int MSG_FINGERPRINT_HELP              = 41 << MSG_SHIFT;
-    private static final int MSG_FINGERPRINT_ERROR             = 42 << MSG_SHIFT;
-    private static final int MSG_FINGERPRINT_HIDE              = 43 << MSG_SHIFT;
+    private static final int MSG_BIOMETRIC_SHOW                = 39 << MSG_SHIFT;
+    private static final int MSG_BIOMETRIC_AUTHENTICATED       = 40 << MSG_SHIFT;
+    private static final int MSG_BIOMETRIC_HELP                = 41 << MSG_SHIFT;
+    private static final int MSG_BIOMETRIC_ERROR               = 42 << MSG_SHIFT;
+    private static final int MSG_BIOMETRIC_HIDE                = 43 << MSG_SHIFT;
     private static final int MSG_SHOW_CHARGING_ANIMATION       = 44 << MSG_SHIFT;
     private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
     private static final int MSG_SHOW_PINNING_TOAST_ESCAPE     = 46 << MSG_SHIFT;
@@ -160,11 +160,11 @@
 
         default void onRotationProposal(int rotation, boolean isValid) { }
 
-        default void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) { }
-        default void onFingerprintAuthenticated() { }
-        default void onFingerprintHelp(String message) { }
-        default void onFingerprintError(String error) { }
-        default void hideFingerprintDialog() { }
+        default void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver) { }
+        default void onBiometricAuthenticated() { }
+        default void onBiometricHelp(String message) { }
+        default void onBiometricError(String error) { }
+        default void hideBiometricDialog() { }
     }
 
     @VisibleForTesting
@@ -513,41 +513,41 @@
     }
 
     @Override
-    public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
+    public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
         synchronized (mLock) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = bundle;
             args.arg2 = receiver;
-            mHandler.obtainMessage(MSG_FINGERPRINT_SHOW, args)
+            mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
                     .sendToTarget();
         }
     }
 
     @Override
-    public void onFingerprintAuthenticated() {
+    public void onBiometricAuthenticated() {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATED).sendToTarget();
+            mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
         }
     }
 
     @Override
-    public void onFingerprintHelp(String message) {
+    public void onBiometricHelp(String message) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_FINGERPRINT_HELP, message).sendToTarget();
+            mHandler.obtainMessage(MSG_BIOMETRIC_HELP, message).sendToTarget();
         }
     }
 
     @Override
-    public void onFingerprintError(String error) {
+    public void onBiometricError(String error) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, error).sendToTarget();
+            mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, error).sendToTarget();
         }
     }
 
     @Override
-    public void hideFingerprintDialog() {
+    public void hideBiometricDialog() {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_FINGERPRINT_HIDE).sendToTarget();
+            mHandler.obtainMessage(MSG_BIOMETRIC_HIDE).sendToTarget();
         }
     }
 
@@ -752,34 +752,34 @@
                         mCallbacks.get(i).onRotationProposal(msg.arg1, msg.arg2 != 0);
                     }
                     break;
-                case MSG_FINGERPRINT_SHOW:
-                    mHandler.removeMessages(MSG_FINGERPRINT_ERROR);
-                    mHandler.removeMessages(MSG_FINGERPRINT_HELP);
-                    mHandler.removeMessages(MSG_FINGERPRINT_AUTHENTICATED);
+                case MSG_BIOMETRIC_SHOW:
+                    mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
+                    mHandler.removeMessages(MSG_BIOMETRIC_HELP);
+                    mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).showFingerprintDialog(
+                        mCallbacks.get(i).showBiometricDialog(
                                 (Bundle)((SomeArgs)msg.obj).arg1,
                                 (IBiometricPromptReceiver)((SomeArgs)msg.obj).arg2);
                     }
                     break;
-                case MSG_FINGERPRINT_AUTHENTICATED:
+                case MSG_BIOMETRIC_AUTHENTICATED:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).onFingerprintAuthenticated();
+                        mCallbacks.get(i).onBiometricAuthenticated();
                     }
                     break;
-                case MSG_FINGERPRINT_HELP:
+                case MSG_BIOMETRIC_HELP:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).onFingerprintHelp((String) msg.obj);
+                        mCallbacks.get(i).onBiometricHelp((String) msg.obj);
                     }
                     break;
-                case MSG_FINGERPRINT_ERROR:
+                case MSG_BIOMETRIC_ERROR:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).onFingerprintError((String) msg.obj);
+                        mCallbacks.get(i).onBiometricError((String) msg.obj);
                     }
                     break;
-                case MSG_FINGERPRINT_HIDE:
+                case MSG_BIOMETRIC_HIDE:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).hideFingerprintDialog();
+                        mCallbacks.get(i).hideBiometricDialog();
                     }
                     break;
                 case MSG_SHOW_CHARGING_ANIMATION:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index 4728a1d..6c3e504 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -19,7 +19,7 @@
 import android.view.View;
 
 import com.android.systemui.Interpolators;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
 /**
  * A helper to fade views in and out.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 1bbf848..30d9ef7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -30,7 +30,7 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
 
 /**
  * A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 4da1558..745b2f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -25,8 +25,9 @@
 import android.widget.TextView;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.stack.ExpandableViewState;
-import com.android.systemui.statusbar.stack.StackScrollState;
+import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import com.android.systemui.statusbar.notification.stack.StackScrollState;
 
 public class EmptyShadeView extends StackScrollerDecorView {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
deleted file mode 100644
index 135b037..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ /dev/null
@@ -1,2989 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.systemui.statusbar;
-
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.Nullable;
-import android.app.NotificationChannel;
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Path;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.AnimationDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Bundle;
-import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.util.Log;
-import android.util.MathUtils;
-import android.util.Property;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.NotificationHeaderView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.widget.Chronometer;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.RemoteViews;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.ContrastColorUtil;
-import com.android.internal.widget.CachingIconView;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
-import com.android.systemui.statusbar.NotificationGuts.GutsContent;
-import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.HybridNotificationView;
-import com.android.systemui.statusbar.notification.NotificationCounters;
-import com.android.systemui.statusbar.notification.NotificationInflater;
-import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.stack.AmbientState;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.ExpandableViewState;
-import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
-import com.android.systemui.statusbar.stack.StackScrollState;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.BooleanSupplier;
-import java.util.function.Consumer;
-
-/**
- * View representing a notification item - this can be either the individual child notification or
- * the group summary (which contains 1 or more child notifications).
- */
-public class ExpandableNotificationRow extends ActivatableNotificationView
-        implements PluginListener<NotificationMenuRowPlugin> {
-
-    private static final boolean DEBUG = false;
-    private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
-    private static final int COLORED_DIVIDER_ALPHA = 0x7B;
-    private static final int MENU_VIEW_INDEX = 0;
-    private static final String TAG = "ExpandableNotifRow";
-
-    /**
-     * Listener for when {@link ExpandableNotificationRow} is laid out.
-     */
-    public interface LayoutListener {
-        void onLayout();
-    }
-
-    private LayoutListener mLayoutListener;
-    private boolean mDark;
-    private boolean mLowPriorityStateUpdated;
-    private final NotificationInflater mNotificationInflater;
-    private int mIconTransformContentShift;
-    private int mIconTransformContentShiftNoIcon;
-    private int mNotificationMinHeightLegacy;
-    private int mNotificationMinHeightBeforeP;
-    private int mMaxHeadsUpHeightLegacy;
-    private int mMaxHeadsUpHeightBeforeP;
-    private int mMaxHeadsUpHeight;
-    private int mMaxHeadsUpHeightIncreased;
-    private int mNotificationMinHeight;
-    private int mNotificationMinHeightLarge;
-    private int mNotificationMaxHeight;
-    private int mNotificationAmbientHeight;
-    private int mIncreasedPaddingBetweenElements;
-    private int mNotificationLaunchHeight;
-    private boolean mMustStayOnScreen;
-
-    /** Does this row contain layouts that can adapt to row expansion */
-    private boolean mExpandable;
-    /** Has the user actively changed the expansion state of this row */
-    private boolean mHasUserChangedExpansion;
-    /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
-    private boolean mUserExpanded;
-    /** Whether the blocking helper is showing on this notification (even if dismissed) */
-    private boolean mIsBlockingHelperShowing;
-
-    /**
-     * Has this notification been expanded while it was pinned
-     */
-    private boolean mExpandedWhenPinned;
-    /** Is the user touching this row */
-    private boolean mUserLocked;
-    /** Are we showing the "public" version */
-    private boolean mShowingPublic;
-    private boolean mSensitive;
-    private boolean mSensitiveHiddenInGeneral;
-    private boolean mShowingPublicInitialized;
-    private boolean mHideSensitiveForIntrinsicHeight;
-    private float mHeaderVisibleAmount = 1.0f;
-
-    /**
-     * Is this notification expanded by the system. The expansion state can be overridden by the
-     * user expansion.
-     */
-    private boolean mIsSystemExpanded;
-
-    /**
-     * Whether the notification is on the keyguard and the expansion is disabled.
-     */
-    private boolean mOnKeyguard;
-
-    private Animator mTranslateAnim;
-    private ArrayList<View> mTranslateableViews;
-    private NotificationContentView mPublicLayout;
-    private NotificationContentView mPrivateLayout;
-    private NotificationContentView[] mLayouts;
-    private int mNotificationColor;
-    private ExpansionLogger mLogger;
-    private String mLoggingKey;
-    private NotificationGuts mGuts;
-    private NotificationData.Entry mEntry;
-    private StatusBarNotification mStatusBarNotification;
-    private String mAppName;
-    private boolean mIsHeadsUp;
-    private boolean mLastChronometerRunning = true;
-    private ViewStub mChildrenContainerStub;
-    private NotificationGroupManager mGroupManager;
-    private boolean mChildrenExpanded;
-    private boolean mIsSummaryWithChildren;
-    private NotificationChildrenContainer mChildrenContainer;
-    private NotificationMenuRowPlugin mMenuRow;
-    private ViewStub mGutsStub;
-    private boolean mIsSystemChildExpanded;
-    private boolean mIsPinned;
-    private FalsingManager mFalsingManager;
-    private boolean mExpandAnimationRunning;
-    private AboveShelfChangedListener mAboveShelfChangedListener;
-    private HeadsUpManager mHeadsUpManager;
-    private Consumer<Boolean> mHeadsUpAnimatingAwayListener;
-    private boolean mChildIsExpanding;
-
-    private boolean mJustClicked;
-    private boolean mIconAnimationRunning;
-    private boolean mShowNoBackground;
-    private ExpandableNotificationRow mNotificationParent;
-    private OnExpandClickListener mOnExpandClickListener;
-    private View.OnClickListener mOnAppOpsClickListener;
-
-    // Listener will be called when receiving a long click event.
-    // Use #setLongPressPosition to optionally assign positional data with the long press.
-    private LongPressListener mLongPressListener;
-
-    private boolean mGroupExpansionChanging;
-
-    /**
-     * A supplier that returns true if keyguard is secure.
-     */
-    private BooleanSupplier mSecureStateProvider;
-
-    /**
-     * Whether or not a notification that is not part of a group of notifications can be manually
-     * expanded by the user.
-     */
-    private boolean mEnableNonGroupedNotificationExpand;
-
-    /**
-     * Whether or not to update the background of the header of the notification when its expanded.
-     * If {@code true}, the header background will disappear when expanded.
-     */
-    private boolean mShowGroupBackgroundWhenExpanded;
-
-    private OnClickListener mExpandClickListener = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            if (!shouldShowPublic() && (!mIsLowPriority || isExpanded())
-                    && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
-                mGroupExpansionChanging = true;
-                final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
-                boolean nowExpanded = mGroupManager.toggleGroupExpansion(mStatusBarNotification);
-                mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
-                MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER,
-                        nowExpanded);
-                onExpansionChanged(true /* userAction */, wasExpanded);
-            } else if (mEnableNonGroupedNotificationExpand) {
-                if (v.isAccessibilityFocused()) {
-                    mPrivateLayout.setFocusOnVisibilityChange();
-                }
-                boolean nowExpanded;
-                if (isPinned()) {
-                    nowExpanded = !mExpandedWhenPinned;
-                    mExpandedWhenPinned = nowExpanded;
-                } else {
-                    nowExpanded = !isExpanded();
-                    setUserExpanded(nowExpanded);
-                }
-                notifyHeightChanged(true);
-                mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
-                MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_EXPANDER,
-                        nowExpanded);
-            }
-        }
-    };
-    private boolean mForceUnlocked;
-    private boolean mDismissed;
-    private boolean mKeepInParent;
-    private boolean mRemoved;
-    private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
-            new FloatProperty<ExpandableNotificationRow>("translate") {
-                @Override
-                public void setValue(ExpandableNotificationRow object, float value) {
-                    object.setTranslation(value);
-                }
-
-                @Override
-                public Float get(ExpandableNotificationRow object) {
-                    return object.getTranslation();
-                }
-            };
-    private OnClickListener mOnClickListener;
-    private boolean mHeadsupDisappearRunning;
-    private View mChildAfterViewWhenDismissed;
-    private View mGroupParentWhenDismissed;
-    private boolean mRefocusOnDismiss;
-    private float mContentTransformationAmount;
-    private boolean mIconsVisible = true;
-    private boolean mAboveShelf;
-    private boolean mShowAmbient;
-    private boolean mIsLastChild;
-    private Runnable mOnDismissRunnable;
-    private boolean mIsLowPriority;
-    private boolean mIsColorized;
-    private boolean mUseIncreasedCollapsedHeight;
-    private boolean mUseIncreasedHeadsUpHeight;
-    private float mTranslationWhenRemoved;
-    private boolean mWasChildInGroupWhenRemoved;
-    private int mNotificationColorAmbient;
-    private NotificationViewState mNotificationViewState;
-
-    private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
-            new SystemNotificationAsyncTask();
-
-    /**
-     * Returns whether the given {@code statusBarNotification} is a system notification.
-     * <b>Note</b>, this should be run in the background thread if possible as it makes multiple IPC
-     * calls.
-     */
-    private static Boolean isSystemNotification(
-            Context context, StatusBarNotification statusBarNotification) {
-        PackageManager packageManager = StatusBar.getPackageManagerForUser(
-                context, statusBarNotification.getUser().getIdentifier());
-        Boolean isSystemNotification = null;
-
-        try {
-            PackageInfo packageInfo = packageManager.getPackageInfo(
-                    statusBarNotification.getPackageName(), PackageManager.GET_SIGNATURES);
-
-            isSystemNotification =
-                    com.android.settingslib.Utils.isSystemPackage(
-                            context.getResources(), packageManager, packageInfo);
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "cacheIsSystemNotification: Could not find package info");
-        }
-        return isSystemNotification;
-    }
-
-    @Override
-    public boolean isGroupExpansionChanging() {
-        if (isChildInGroup()) {
-            return mNotificationParent.isGroupExpansionChanging();
-        }
-        return mGroupExpansionChanging;
-    }
-
-    public void setGroupExpansionChanging(boolean changing) {
-        mGroupExpansionChanging = changing;
-    }
-
-    @Override
-    public void setActualHeightAnimating(boolean animating) {
-        if (mPrivateLayout != null) {
-            mPrivateLayout.setContentHeightAnimating(animating);
-        }
-    }
-
-    public NotificationContentView getPrivateLayout() {
-        return mPrivateLayout;
-    }
-
-    public NotificationContentView getPublicLayout() {
-        return mPublicLayout;
-    }
-
-    public void setIconAnimationRunning(boolean running) {
-        for (NotificationContentView l : mLayouts) {
-            setIconAnimationRunning(running, l);
-        }
-        if (mIsSummaryWithChildren) {
-            setIconAnimationRunningForChild(running, mChildrenContainer.getHeaderView());
-            setIconAnimationRunningForChild(running, mChildrenContainer.getLowPriorityHeaderView());
-            List<ExpandableNotificationRow> notificationChildren =
-                    mChildrenContainer.getNotificationChildren();
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow child = notificationChildren.get(i);
-                child.setIconAnimationRunning(running);
-            }
-        }
-        mIconAnimationRunning = running;
-    }
-
-    private void setIconAnimationRunning(boolean running, NotificationContentView layout) {
-        if (layout != null) {
-            View contractedChild = layout.getContractedChild();
-            View expandedChild = layout.getExpandedChild();
-            View headsUpChild = layout.getHeadsUpChild();
-            setIconAnimationRunningForChild(running, contractedChild);
-            setIconAnimationRunningForChild(running, expandedChild);
-            setIconAnimationRunningForChild(running, headsUpChild);
-        }
-    }
-
-    private void setIconAnimationRunningForChild(boolean running, View child) {
-        if (child != null) {
-            ImageView icon = (ImageView) child.findViewById(com.android.internal.R.id.icon);
-            setIconRunning(icon, running);
-            ImageView rightIcon = (ImageView) child.findViewById(
-                    com.android.internal.R.id.right_icon);
-            setIconRunning(rightIcon, running);
-        }
-    }
-
-    private void setIconRunning(ImageView imageView, boolean running) {
-        if (imageView != null) {
-            Drawable drawable = imageView.getDrawable();
-            if (drawable instanceof AnimationDrawable) {
-                AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
-                if (running) {
-                    animationDrawable.start();
-                } else {
-                    animationDrawable.stop();
-                }
-            } else if (drawable instanceof AnimatedVectorDrawable) {
-                AnimatedVectorDrawable animationDrawable = (AnimatedVectorDrawable) drawable;
-                if (running) {
-                    animationDrawable.start();
-                } else {
-                    animationDrawable.stop();
-                }
-            }
-        }
-    }
-
-    public void updateNotification(NotificationData.Entry entry) {
-        mEntry = entry;
-        mStatusBarNotification = entry.notification;
-        mNotificationInflater.inflateNotificationViews();
-
-        cacheIsSystemNotification();
-    }
-
-    /**
-     * Caches whether or not this row contains a system notification. Note, this is only cached
-     * once per notification as the packageInfo can't technically change for a notification row.
-     */
-    private void cacheIsSystemNotification() {
-        if (mEntry != null && mEntry.mIsSystemNotification == null) {
-            if (mSystemNotificationAsyncTask.getStatus() == AsyncTask.Status.PENDING) {
-                // Run async task once, only if it hasn't already been executed. Note this is
-                // executed in serial - no need to parallelize this small task.
-                mSystemNotificationAsyncTask.execute();
-            }
-        }
-    }
-
-    /**
-     * Returns whether this row is considered non-blockable (i.e. it's a non-blockable system notif
-     * or is in a whitelist).
-     */
-    public boolean getIsNonblockable() {
-        boolean isNonblockable = Dependency.get(NotificationBlockingHelperManager.class)
-                .isNonblockable(mStatusBarNotification.getPackageName(),
-                        mEntry.channel.getId());
-
-        // If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once
-        // again, but in-place on the main thread this time. This should rarely ever get called.
-        if (mEntry != null && mEntry.mIsSystemNotification == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Retrieving isSystemNotification on main thread");
-            }
-            mSystemNotificationAsyncTask.cancel(true /* mayInterruptIfRunning */);
-            mEntry.mIsSystemNotification = isSystemNotification(mContext, mStatusBarNotification);
-        }
-
-        if (!isNonblockable && mEntry != null && mEntry.mIsSystemNotification != null) {
-            if (mEntry.mIsSystemNotification) {
-                if (mEntry.channel != null
-                        && !mEntry.channel.isBlockableSystem()) {
-                    isNonblockable = true;
-                }
-            }
-        }
-        return isNonblockable;
-    }
-
-    public void onNotificationUpdated() {
-        for (NotificationContentView l : mLayouts) {
-            l.onNotificationUpdated(mEntry);
-        }
-        mIsColorized = mStatusBarNotification.getNotification().isColorized();
-        mShowingPublicInitialized = false;
-        updateNotificationColor();
-        if (mMenuRow != null) {
-            mMenuRow.onNotificationUpdated(mStatusBarNotification);
-            mMenuRow.setAppName(mAppName);
-        }
-        if (mIsSummaryWithChildren) {
-            mChildrenContainer.recreateNotificationHeader(mExpandClickListener);
-            mChildrenContainer.onNotificationUpdated();
-        }
-        if (mIconAnimationRunning) {
-            setIconAnimationRunning(true);
-        }
-        if (mNotificationParent != null) {
-            mNotificationParent.updateChildrenHeaderAppearance();
-        }
-        onChildrenCountChanged();
-        // The public layouts expand button is always visible
-        mPublicLayout.updateExpandButtons(true);
-        updateLimits();
-        updateIconVisibilities();
-        updateShelfIconColor();
-        updateRippleAllowed();
-    }
-
-    @VisibleForTesting
-    void updateShelfIconColor() {
-        StatusBarIconView expandedIcon = mEntry.expandedIcon;
-        boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L));
-        boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon,
-                ContrastColorUtil.getInstance(mContext));
-        int color = StatusBarIconView.NO_COLOR;
-        if (colorize) {
-            NotificationHeaderView header = getVisibleNotificationHeader();
-            if (header != null) {
-                color = header.getOriginalIconColor();
-            } else {
-                color = mEntry.getContrastedColor(mContext, mIsLowPriority && !isExpanded(),
-                        getBackgroundColorWithoutTint());
-            }
-        }
-        expandedIcon.setStaticDrawableColor(color);
-    }
-
-    public void setAboveShelfChangedListener(AboveShelfChangedListener aboveShelfChangedListener) {
-        mAboveShelfChangedListener = aboveShelfChangedListener;
-    }
-
-    /**
-     * Sets a supplier that can determine whether the keyguard is secure or not.
-     * @param secureStateProvider A function that returns true if keyguard is secure.
-     */
-    public void setSecureStateProvider(BooleanSupplier secureStateProvider) {
-        mSecureStateProvider = secureStateProvider;
-    }
-
-    @Override
-    public boolean isDimmable() {
-        if (!getShowingLayout().isDimmable()) {
-            return false;
-        }
-        return super.isDimmable();
-    }
-
-    private void updateLimits() {
-        for (NotificationContentView l : mLayouts) {
-            updateLimitsForView(l);
-        }
-    }
-
-    private void updateLimitsForView(NotificationContentView layout) {
-        boolean customView = layout.getContractedChild().getId()
-                != com.android.internal.R.id.status_bar_latest_event_content;
-        boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
-        boolean beforeP = mEntry.targetSdk < Build.VERSION_CODES.P;
-        int minHeight;
-        if (customView && beforeP && !mIsSummaryWithChildren) {
-            minHeight = beforeN ? mNotificationMinHeightLegacy : mNotificationMinHeightBeforeP;
-        } else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
-            minHeight = mNotificationMinHeightLarge;
-        } else {
-            minHeight = mNotificationMinHeight;
-        }
-        boolean headsUpCustom = layout.getHeadsUpChild() != null &&
-                layout.getHeadsUpChild().getId()
-                        != com.android.internal.R.id.status_bar_latest_event_content;
-        int headsUpheight;
-        if (headsUpCustom && beforeP) {
-            headsUpheight = beforeN ? mMaxHeadsUpHeightLegacy : mMaxHeadsUpHeightBeforeP;
-        } else if (mUseIncreasedHeadsUpHeight && layout == mPrivateLayout) {
-            headsUpheight = mMaxHeadsUpHeightIncreased;
-        } else {
-            headsUpheight = mMaxHeadsUpHeight;
-        }
-        NotificationViewWrapper headsUpWrapper = layout.getVisibleWrapper(
-                NotificationContentView.VISIBLE_TYPE_HEADSUP);
-        if (headsUpWrapper != null) {
-            headsUpheight = Math.max(headsUpheight, headsUpWrapper.getMinLayoutHeight());
-        }
-        layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
-                mNotificationAmbientHeight);
-    }
-
-    public StatusBarNotification getStatusBarNotification() {
-        return mStatusBarNotification;
-    }
-
-    public NotificationData.Entry getEntry() {
-        return mEntry;
-    }
-
-    public boolean isHeadsUp() {
-        return mIsHeadsUp;
-    }
-
-    public void setHeadsUp(boolean isHeadsUp) {
-        boolean wasAboveShelf = isAboveShelf();
-        int intrinsicBefore = getIntrinsicHeight();
-        mIsHeadsUp = isHeadsUp;
-        mPrivateLayout.setHeadsUp(isHeadsUp);
-        if (mIsSummaryWithChildren) {
-            // The overflow might change since we allow more lines as HUN.
-            mChildrenContainer.updateGroupOverflow();
-        }
-        if (intrinsicBefore != getIntrinsicHeight()) {
-            notifyHeightChanged(false  /* needsAnimation */);
-        }
-        if (isHeadsUp) {
-            mMustStayOnScreen = true;
-            setAboveShelf(true);
-        } else if (isAboveShelf() != wasAboveShelf) {
-            mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
-        }
-    }
-
-    public void setGroupManager(NotificationGroupManager groupManager) {
-        mGroupManager = groupManager;
-        mPrivateLayout.setGroupManager(groupManager);
-    }
-
-    public void setRemoteInputController(RemoteInputController r) {
-        mPrivateLayout.setRemoteInputController(r);
-    }
-
-    public void setAppName(String appName) {
-        mAppName = appName;
-        if (mMenuRow != null && mMenuRow.getMenuView() != null) {
-            mMenuRow.setAppName(mAppName);
-        }
-    }
-
-    public void addChildNotification(ExpandableNotificationRow row) {
-        addChildNotification(row, -1);
-    }
-
-    /**
-     * Set the how much the header should be visible. A value of 0 will make the header fully gone
-     * and a value of 1 will make the notification look just like normal.
-     * This is being used for heads up notifications, when they are pinned to the top of the screen
-     * and the header content is extracted to the statusbar.
-     *
-     * @param headerVisibleAmount the amount the header should be visible.
-     */
-    public void setHeaderVisibleAmount(float headerVisibleAmount) {
-        if (mHeaderVisibleAmount != headerVisibleAmount) {
-            mHeaderVisibleAmount = headerVisibleAmount;
-            mPrivateLayout.setHeaderVisibleAmount(headerVisibleAmount);
-            if (mChildrenContainer != null) {
-                mChildrenContainer.setHeaderVisibleAmount(headerVisibleAmount);
-            }
-            notifyHeightChanged(false /* needsAnimation */);
-        }
-    }
-
-    @Override
-    public float getHeaderVisibleAmount() {
-        return mHeaderVisibleAmount;
-    }
-
-    @Override
-    public void setHeadsUpIsVisible() {
-        super.setHeadsUpIsVisible();
-        mMustStayOnScreen = false;
-    }
-
-    /**
-     * Add a child notification to this view.
-     *
-     * @param row the row to add
-     * @param childIndex the index to add it at, if -1 it will be added at the end
-     */
-    public void addChildNotification(ExpandableNotificationRow row, int childIndex) {
-        if (mChildrenContainer == null) {
-            mChildrenContainerStub.inflate();
-        }
-        mChildrenContainer.addNotification(row, childIndex);
-        onChildrenCountChanged();
-        row.setIsChildInGroup(true, this);
-    }
-
-    public void removeChildNotification(ExpandableNotificationRow row) {
-        if (mChildrenContainer != null) {
-            mChildrenContainer.removeNotification(row);
-        }
-        onChildrenCountChanged();
-        row.setIsChildInGroup(false, null);
-        row.setBottomRoundness(0.0f, false /* animate */);
-    }
-
-    @Override
-    public boolean isChildInGroup() {
-        return mNotificationParent != null;
-    }
-
-    /**
-     * @return whether this notification is the only child in the group summary
-     */
-    public boolean isOnlyChildInGroup() {
-        return mGroupManager.isOnlyChildInGroup(getStatusBarNotification());
-    }
-
-    public ExpandableNotificationRow getNotificationParent() {
-        return mNotificationParent;
-    }
-
-    /**
-     * @param isChildInGroup Is this notification now in a group
-     * @param parent the new parent notification
-     */
-    public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {
-        boolean childInGroup = StatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
-        if (mExpandAnimationRunning && !isChildInGroup && mNotificationParent != null) {
-            mNotificationParent.setChildIsExpanding(false);
-            mNotificationParent.setExtraWidthForClipping(0.0f);
-            mNotificationParent.setMinimumHeightForClipping(0);
-        }
-        mNotificationParent = childInGroup ? parent : null;
-        mPrivateLayout.setIsChildInGroup(childInGroup);
-        mNotificationInflater.setIsChildInGroup(childInGroup);
-        resetBackgroundAlpha();
-        updateBackgroundForGroupState();
-        updateClickAndFocus();
-        if (mNotificationParent != null) {
-            setOverrideTintColor(NO_COLOR, 0.0f);
-            // Let's reset the distance to top roundness, as this isn't applied to group children
-            setDistanceToTopRoundness(NO_ROUNDNESS);
-            mNotificationParent.updateBackgroundForGroupState();
-        }
-        updateIconVisibilities();
-        updateBackgroundClipping();
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (event.getActionMasked() != MotionEvent.ACTION_DOWN
-                || !isChildInGroup() || isGroupExpanded()) {
-            return super.onTouchEvent(event);
-        } else {
-            return false;
-        }
-    }
-
-    @Override
-    protected boolean handleSlideBack() {
-        if (mMenuRow != null && mMenuRow.isMenuVisible()) {
-            animateTranslateNotification(0 /* targetLeft */);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    protected boolean shouldHideBackground() {
-        return super.shouldHideBackground() || mShowNoBackground;
-    }
-
-    @Override
-    public boolean isSummaryWithChildren() {
-        return mIsSummaryWithChildren;
-    }
-
-    @Override
-    public boolean areChildrenExpanded() {
-        return mChildrenExpanded;
-    }
-
-    public List<ExpandableNotificationRow> getNotificationChildren() {
-        return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren();
-    }
-
-    public int getNumberOfNotificationChildren() {
-        if (mChildrenContainer == null) {
-            return 0;
-        }
-        return mChildrenContainer.getNotificationChildren().size();
-    }
-
-    /**
-     * Apply the order given in the list to the children.
-     *
-     * @param childOrder the new list order
-     * @param visualStabilityManager
-     * @param callback the callback to invoked in case it is not allowed
-     * @return whether the list order has changed
-     */
-    public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
-            VisualStabilityManager visualStabilityManager,
-            VisualStabilityManager.Callback callback) {
-        return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder,
-                visualStabilityManager, callback);
-    }
-
-    public void getChildrenStates(StackScrollState resultState,
-            AmbientState ambientState) {
-        if (mIsSummaryWithChildren) {
-            ExpandableViewState parentState = resultState.getViewStateForView(this);
-            mChildrenContainer.getState(resultState, parentState, ambientState);
-        }
-    }
-
-    public void applyChildrenState(StackScrollState state) {
-        if (mIsSummaryWithChildren) {
-            mChildrenContainer.applyState(state);
-        }
-    }
-
-    public void prepareExpansionChanged(StackScrollState state) {
-        if (mIsSummaryWithChildren) {
-            mChildrenContainer.prepareExpansionChanged(state);
-        }
-    }
-
-    public void startChildAnimation(StackScrollState finalState, AnimationProperties properties) {
-        if (mIsSummaryWithChildren) {
-            mChildrenContainer.startAnimationToState(finalState, properties);
-        }
-    }
-
-    public ExpandableNotificationRow getViewAtPosition(float y) {
-        if (!mIsSummaryWithChildren || !mChildrenExpanded) {
-            return this;
-        } else {
-            ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
-            return view == null ? this : view;
-        }
-    }
-
-    public NotificationGuts getGuts() {
-        return mGuts;
-    }
-
-    /**
-     * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this
-     * the notification will be rendered on top of the screen.
-     *
-     * @param pinned whether it is pinned
-     */
-    public void setPinned(boolean pinned) {
-        int intrinsicHeight = getIntrinsicHeight();
-        boolean wasAboveShelf = isAboveShelf();
-        mIsPinned = pinned;
-        if (intrinsicHeight != getIntrinsicHeight()) {
-            notifyHeightChanged(false /* needsAnimation */);
-        }
-        if (pinned) {
-            setIconAnimationRunning(true);
-            mExpandedWhenPinned = false;
-        } else if (mExpandedWhenPinned) {
-            setUserExpanded(true);
-        }
-        setChronometerRunning(mLastChronometerRunning);
-        if (isAboveShelf() != wasAboveShelf) {
-            mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
-        }
-    }
-
-    @Override
-    public boolean isPinned() {
-        return mIsPinned;
-    }
-
-    @Override
-    public int getPinnedHeadsUpHeight() {
-        return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
-    }
-
-    /**
-     * @param atLeastMinHeight should the value returned be at least the minimum height.
-     *                         Used to avoid cyclic calls
-     * @return the height of the heads up notification when pinned
-     */
-    private int getPinnedHeadsUpHeight(boolean atLeastMinHeight) {
-        if (mIsSummaryWithChildren) {
-            return mChildrenContainer.getIntrinsicHeight();
-        }
-        if(mExpandedWhenPinned) {
-            return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
-        } else if (atLeastMinHeight) {
-            return Math.max(getCollapsedHeight(), getHeadsUpHeight());
-        } else {
-            return getHeadsUpHeight();
-        }
-    }
-
-    /**
-     * Mark whether this notification was just clicked, i.e. the user has just clicked this
-     * notification in this frame.
-     */
-    public void setJustClicked(boolean justClicked) {
-        mJustClicked = justClicked;
-    }
-
-    /**
-     * @return true if this notification has been clicked in this frame, false otherwise
-     */
-    public boolean wasJustClicked() {
-        return mJustClicked;
-    }
-
-    public void setChronometerRunning(boolean running) {
-        mLastChronometerRunning = running;
-        setChronometerRunning(running, mPrivateLayout);
-        setChronometerRunning(running, mPublicLayout);
-        if (mChildrenContainer != null) {
-            List<ExpandableNotificationRow> notificationChildren =
-                    mChildrenContainer.getNotificationChildren();
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow child = notificationChildren.get(i);
-                child.setChronometerRunning(running);
-            }
-        }
-    }
-
-    private void setChronometerRunning(boolean running, NotificationContentView layout) {
-        if (layout != null) {
-            running = running || isPinned();
-            View contractedChild = layout.getContractedChild();
-            View expandedChild = layout.getExpandedChild();
-            View headsUpChild = layout.getHeadsUpChild();
-            setChronometerRunningForChild(running, contractedChild);
-            setChronometerRunningForChild(running, expandedChild);
-            setChronometerRunningForChild(running, headsUpChild);
-        }
-    }
-
-    private void setChronometerRunningForChild(boolean running, View child) {
-        if (child != null) {
-            View chronometer = child.findViewById(com.android.internal.R.id.chronometer);
-            if (chronometer instanceof Chronometer) {
-                ((Chronometer) chronometer).setStarted(running);
-            }
-        }
-    }
-
-    public NotificationHeaderView getNotificationHeader() {
-        if (mIsSummaryWithChildren) {
-            return mChildrenContainer.getHeaderView();
-        }
-        return mPrivateLayout.getNotificationHeader();
-    }
-
-    /**
-     * @return the currently visible notification header. This can be different from
-     * {@link #getNotificationHeader()} in case it is a low-priority group.
-     */
-    public NotificationHeaderView getVisibleNotificationHeader() {
-        if (mIsSummaryWithChildren && !shouldShowPublic()) {
-            return mChildrenContainer.getVisibleHeader();
-        }
-        return getShowingLayout().getVisibleNotificationHeader();
-    }
-
-
-    /**
-     * @return the contracted notification header. This can be different from
-     * {@link #getNotificationHeader()} and also {@link #getVisibleNotificationHeader()} and only
-     * returns the contracted version.
-     */
-    public NotificationHeaderView getContractedNotificationHeader() {
-        if (mIsSummaryWithChildren) {
-            return mChildrenContainer.getHeaderView();
-        }
-        return mPrivateLayout.getContractedNotificationHeader();
-    }
-
-    public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
-        mOnExpandClickListener = onExpandClickListener;
-    }
-
-    public void setLongPressListener(LongPressListener longPressListener) {
-        mLongPressListener = longPressListener;
-    }
-
-    @Override
-    public void setOnClickListener(@Nullable OnClickListener l) {
-        super.setOnClickListener(l);
-        mOnClickListener = l;
-        updateClickAndFocus();
-    }
-
-    private void updateClickAndFocus() {
-        boolean normalChild = !isChildInGroup() || isGroupExpanded();
-        boolean clickable = mOnClickListener != null && normalChild;
-        if (isFocusable() != normalChild) {
-            setFocusable(normalChild);
-        }
-        if (isClickable() != clickable) {
-            setClickable(clickable);
-        }
-    }
-
-    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
-        mHeadsUpManager = headsUpManager;
-    }
-
-    public void setGutsView(MenuItem item) {
-        if (mGuts != null && item.getGutsView() instanceof GutsContent) {
-            ((GutsContent) item.getGutsView()).setGutsParent(mGuts);
-            mGuts.setGutsContent((GutsContent) item.getGutsView());
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        Dependency.get(PluginManager.class).addPluginListener(this,
-                NotificationMenuRowPlugin.class, false /* Allow multiple */);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        Dependency.get(PluginManager.class).removePluginListener(this);
-    }
-
-    @Override
-    public void onPluginConnected(NotificationMenuRowPlugin plugin, Context pluginContext) {
-        boolean existed = mMenuRow.getMenuView() != null;
-        if (existed) {
-            removeView(mMenuRow.getMenuView());
-        }
-        mMenuRow = plugin;
-        if (mMenuRow.useDefaultMenuItems()) {
-            ArrayList<MenuItem> items = new ArrayList<>();
-            items.add(NotificationMenuRow.createInfoItem(mContext));
-            items.add(NotificationMenuRow.createSnoozeItem(mContext));
-            items.add(NotificationMenuRow.createAppOpsItem(mContext));
-            mMenuRow.setMenuItems(items);
-        }
-        if (existed) {
-            createMenu();
-        }
-    }
-
-    @Override
-    public void onPluginDisconnected(NotificationMenuRowPlugin plugin) {
-        boolean existed = mMenuRow.getMenuView() != null;
-        mMenuRow = new NotificationMenuRow(mContext); // Back to default
-        if (existed) {
-            createMenu();
-        }
-    }
-
-    public NotificationMenuRowPlugin createMenu() {
-        if (mMenuRow.getMenuView() == null) {
-            mMenuRow.createMenu(this, mStatusBarNotification);
-            mMenuRow.setAppName(mAppName);
-            FrameLayout.LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
-                    LayoutParams.MATCH_PARENT);
-            addView(mMenuRow.getMenuView(), MENU_VIEW_INDEX, lp);
-        }
-        return mMenuRow;
-    }
-
-    public NotificationMenuRowPlugin getProvider() {
-        return mMenuRow;
-    }
-
-    @Override
-    public void onDensityOrFontScaleChanged() {
-        super.onDensityOrFontScaleChanged();
-        initDimens();
-        initBackground();
-        // Let's update our childrencontainer. This is intentionally not guarded with
-        // mIsSummaryWithChildren since we might have had children but not anymore.
-        if (mChildrenContainer != null) {
-            mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
-        }
-        if (mGuts != null) {
-            View oldGuts = mGuts;
-            int index = indexOfChild(oldGuts);
-            removeView(oldGuts);
-            mGuts = (NotificationGuts) LayoutInflater.from(mContext).inflate(
-                    R.layout.notification_guts, this, false);
-            mGuts.setVisibility(oldGuts.getVisibility());
-            addView(mGuts, index);
-        }
-        View oldMenu = mMenuRow.getMenuView();
-        if (oldMenu != null) {
-            int menuIndex = indexOfChild(oldMenu);
-            removeView(oldMenu);
-            mMenuRow.createMenu(ExpandableNotificationRow.this, mStatusBarNotification);
-            mMenuRow.setAppName(mAppName);
-            addView(mMenuRow.getMenuView(), menuIndex);
-        }
-        for (NotificationContentView l : mLayouts) {
-            l.initView();
-            l.reInflateViews();
-        }
-        mNotificationInflater.onDensityOrFontScaleChanged();
-        onNotificationUpdated();
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        if (mMenuRow.getMenuView() != null) {
-            mMenuRow.onConfigurationChanged();
-        }
-    }
-
-    public void setContentBackground(int customBackgroundColor, boolean animate,
-            NotificationContentView notificationContentView) {
-        if (getShowingLayout() == notificationContentView) {
-            setTintColor(customBackgroundColor, animate);
-        }
-    }
-
-    @Override
-    protected void setBackgroundTintColor(int color) {
-        super.setBackgroundTintColor(color);
-        NotificationContentView view = getShowingLayout();
-        if (view != null) {
-            view.setBackgroundTintColor(color);
-        }
-    }
-
-    public void closeRemoteInput() {
-        for (NotificationContentView l : mLayouts) {
-            l.closeRemoteInput();
-        }
-    }
-
-    /**
-     * Set by how much the single line view should be indented.
-     */
-    public void setSingleLineWidthIndention(int indention) {
-        mPrivateLayout.setSingleLineWidthIndention(indention);
-    }
-
-    public int getNotificationColor() {
-        return mNotificationColor;
-    }
-
-    private void updateNotificationColor() {
-        mNotificationColor = ContrastColorUtil.resolveContrastColor(mContext,
-                getStatusBarNotification().getNotification().color,
-                getBackgroundColorWithoutTint());
-        mNotificationColorAmbient = ContrastColorUtil.resolveAmbientColor(mContext,
-                getStatusBarNotification().getNotification().color);
-    }
-
-    public HybridNotificationView getSingleLineView() {
-        return mPrivateLayout.getSingleLineView();
-    }
-
-    public HybridNotificationView getAmbientSingleLineView() {
-        return getShowingLayout().getAmbientSingleLineChild();
-    }
-
-    public boolean isOnKeyguard() {
-        return mOnKeyguard;
-    }
-
-    public void removeAllChildren() {
-        List<ExpandableNotificationRow> notificationChildren
-                = mChildrenContainer.getNotificationChildren();
-        ArrayList<ExpandableNotificationRow> clonedList = new ArrayList<>(notificationChildren);
-        for (int i = 0; i < clonedList.size(); i++) {
-            ExpandableNotificationRow row = clonedList.get(i);
-            if (row.keepInParent()) {
-                continue;
-            }
-            mChildrenContainer.removeNotification(row);
-            row.setIsChildInGroup(false, null);
-        }
-        onChildrenCountChanged();
-    }
-
-    public void setForceUnlocked(boolean forceUnlocked) {
-        mForceUnlocked = forceUnlocked;
-        if (mIsSummaryWithChildren) {
-            List<ExpandableNotificationRow> notificationChildren = getNotificationChildren();
-            for (ExpandableNotificationRow child : notificationChildren) {
-                child.setForceUnlocked(forceUnlocked);
-            }
-        }
-    }
-
-    public void setDismissed(boolean fromAccessibility) {
-        setLongPressListener(null);
-        mDismissed = true;
-        mGroupParentWhenDismissed = mNotificationParent;
-        mRefocusOnDismiss = fromAccessibility;
-        mChildAfterViewWhenDismissed = null;
-        mEntry.icon.setDismissed();
-        if (isChildInGroup()) {
-            List<ExpandableNotificationRow> notificationChildren =
-                    mNotificationParent.getNotificationChildren();
-            int i = notificationChildren.indexOf(this);
-            if (i != -1 && i < notificationChildren.size() - 1) {
-                mChildAfterViewWhenDismissed = notificationChildren.get(i + 1);
-            }
-        }
-    }
-
-    public boolean isDismissed() {
-        return mDismissed;
-    }
-
-    public boolean keepInParent() {
-        return mKeepInParent;
-    }
-
-    public void setKeepInParent(boolean keepInParent) {
-        mKeepInParent = keepInParent;
-    }
-
-    @Override
-    public boolean isRemoved() {
-        return mRemoved;
-    }
-
-    public void setRemoved() {
-        mRemoved = true;
-        mTranslationWhenRemoved = getTranslationY();
-        mWasChildInGroupWhenRemoved = isChildInGroup();
-        if (isChildInGroup()) {
-            mTranslationWhenRemoved += getNotificationParent().getTranslationY();
-        }
-        mPrivateLayout.setRemoved();
-    }
-
-    public boolean wasChildInGroupWhenRemoved() {
-        return mWasChildInGroupWhenRemoved;
-    }
-
-    public float getTranslationWhenRemoved() {
-        return mTranslationWhenRemoved;
-    }
-
-    public NotificationChildrenContainer getChildrenContainer() {
-        return mChildrenContainer;
-    }
-
-    public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
-        boolean wasAboveShelf = isAboveShelf();
-        boolean changed = headsUpAnimatingAway != mHeadsupDisappearRunning;
-        mHeadsupDisappearRunning = headsUpAnimatingAway;
-        mPrivateLayout.setHeadsUpAnimatingAway(headsUpAnimatingAway);
-        if (changed && mHeadsUpAnimatingAwayListener != null) {
-            mHeadsUpAnimatingAwayListener.accept(headsUpAnimatingAway);
-        }
-        if (isAboveShelf() != wasAboveShelf) {
-            mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
-        }
-    }
-
-    public void setHeadsUpAnimatingAwayListener(Consumer<Boolean> listener) {
-        mHeadsUpAnimatingAwayListener = listener;
-    }
-
-    /**
-     * @return if the view was just heads upped and is now animating away. During such a time the
-     * layout needs to be kept consistent
-     */
-    @Override
-    public boolean isHeadsUpAnimatingAway() {
-        return mHeadsupDisappearRunning;
-    }
-
-    public View getChildAfterViewWhenDismissed() {
-        return mChildAfterViewWhenDismissed;
-    }
-
-    public View getGroupParentWhenDismissed() {
-        return mGroupParentWhenDismissed;
-    }
-
-    /**
-     * Dismisses the notification with the option of showing the blocking helper in-place if we have
-     * a negative user sentiment.
-     *
-     * @param fromAccessibility whether this dismiss is coming from an accessibility action
-     * @return whether a blocking helper is shown in this row
-     */
-    public boolean performDismissWithBlockingHelper(boolean fromAccessibility) {
-        NotificationBlockingHelperManager manager =
-                Dependency.get(NotificationBlockingHelperManager.class);
-        boolean isBlockingHelperShown = manager.perhapsShowBlockingHelper(this, mMenuRow);
-
-        Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
-
-        // Continue with dismiss since we don't want the blocking helper to be directly associated
-        // with a certain notification.
-        performDismiss(fromAccessibility);
-        return isBlockingHelperShown;
-    }
-
-    public void performDismiss(boolean fromAccessibility) {
-        if (isOnlyChildInGroup()) {
-            ExpandableNotificationRow 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
-                // the blocking helper affordance!
-                groupSummary.performDismiss(fromAccessibility);
-            }
-        }
-        setDismissed(fromAccessibility);
-        if (isClearable()) {
-            if (mOnDismissRunnable != null) {
-                mOnDismissRunnable.run();
-            }
-        }
-    }
-
-    public void setBlockingHelperShowing(boolean isBlockingHelperShowing) {
-        mIsBlockingHelperShowing = isBlockingHelperShowing;
-    }
-
-    public boolean isBlockingHelperShowing() {
-        return mIsBlockingHelperShowing;
-    }
-
-    public void setOnDismissRunnable(Runnable onDismissRunnable) {
-        mOnDismissRunnable = onDismissRunnable;
-    }
-
-    public View getNotificationIcon() {
-        NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
-        if (notificationHeader != null) {
-            return notificationHeader.getIcon();
-        }
-        return null;
-    }
-
-    /**
-     * @return whether the notification is currently showing a view with an icon.
-     */
-    public boolean isShowingIcon() {
-        if (areGutsExposed()) {
-            return false;
-        }
-        return getVisibleNotificationHeader() != null;
-    }
-
-    /**
-     * Set how much this notification is transformed into an icon.
-     *
-     * @param contentTransformationAmount A value from 0 to 1 indicating how much we are transformed
-     *                                 to the content away
-     * @param isLastChild is this the last child in the list. If true, then the transformation is
-     *                    different since it's content fades out.
-     */
-    public void setContentTransformationAmount(float contentTransformationAmount,
-            boolean isLastChild) {
-        boolean changeTransformation = isLastChild != mIsLastChild;
-        changeTransformation |= mContentTransformationAmount != contentTransformationAmount;
-        mIsLastChild = isLastChild;
-        mContentTransformationAmount = contentTransformationAmount;
-        if (changeTransformation) {
-            updateContentTransformation();
-        }
-    }
-
-    /**
-     * Set the icons to be visible of this notification.
-     */
-    public void setIconsVisible(boolean iconsVisible) {
-        if (iconsVisible != mIconsVisible) {
-            mIconsVisible = iconsVisible;
-            updateIconVisibilities();
-        }
-    }
-
-    @Override
-    protected void onBelowSpeedBumpChanged() {
-        updateIconVisibilities();
-    }
-
-    private void updateContentTransformation() {
-        if (mExpandAnimationRunning) {
-            return;
-        }
-        float contentAlpha;
-        float translationY = -mContentTransformationAmount * mIconTransformContentShift;
-        if (mIsLastChild) {
-            contentAlpha = 1.0f - mContentTransformationAmount;
-            contentAlpha = Math.min(contentAlpha / 0.5f, 1.0f);
-            contentAlpha = Interpolators.ALPHA_OUT.getInterpolation(contentAlpha);
-            translationY *= 0.4f;
-        } else {
-            contentAlpha = 1.0f;
-        }
-        for (NotificationContentView l : mLayouts) {
-            l.setAlpha(contentAlpha);
-            l.setTranslationY(translationY);
-        }
-        if (mChildrenContainer != null) {
-            mChildrenContainer.setAlpha(contentAlpha);
-            mChildrenContainer.setTranslationY(translationY);
-            // TODO: handle children fade out better
-        }
-    }
-
-    private void updateIconVisibilities() {
-        boolean visible = isChildInGroup()
-                || (isBelowSpeedBump() && !NotificationShelf.SHOW_AMBIENT_ICONS)
-                || mIconsVisible;
-        for (NotificationContentView l : mLayouts) {
-            l.setIconsVisible(visible);
-        }
-        if (mChildrenContainer != null) {
-            mChildrenContainer.setIconsVisible(visible);
-        }
-    }
-
-    /**
-     * Get the relative top padding of a view relative to this view. This recursively walks up the
-     * hierarchy and does the corresponding measuring.
-     *
-     * @param view the view to the the padding for. The requested view has to be a child of this
-     *             notification.
-     * @return the toppadding
-     */
-    public int getRelativeTopPadding(View view) {
-        int topPadding = 0;
-        while (view.getParent() instanceof ViewGroup) {
-            topPadding += view.getTop();
-            view = (View) view.getParent();
-            if (view instanceof ExpandableNotificationRow) {
-                return topPadding;
-            }
-        }
-        return topPadding;
-    }
-
-    public float getContentTranslation() {
-        return mPrivateLayout.getTranslationY();
-    }
-
-    public void setIsLowPriority(boolean isLowPriority) {
-        mIsLowPriority = isLowPriority;
-        mPrivateLayout.setIsLowPriority(isLowPriority);
-        mNotificationInflater.setIsLowPriority(mIsLowPriority);
-        if (mChildrenContainer != null) {
-            mChildrenContainer.setIsLowPriority(isLowPriority);
-        }
-    }
-
-
-    public void setLowPriorityStateUpdated(boolean lowPriorityStateUpdated) {
-        mLowPriorityStateUpdated = lowPriorityStateUpdated;
-    }
-
-    public boolean hasLowPriorityStateUpdated() {
-        return mLowPriorityStateUpdated;
-    }
-
-    public boolean isLowPriority() {
-        return mIsLowPriority;
-    }
-
-    public void setUseIncreasedCollapsedHeight(boolean use) {
-        mUseIncreasedCollapsedHeight = use;
-        mNotificationInflater.setUsesIncreasedHeight(use);
-    }
-
-    public void setUseIncreasedHeadsUpHeight(boolean use) {
-        mUseIncreasedHeadsUpHeight = use;
-        mNotificationInflater.setUsesIncreasedHeadsUpHeight(use);
-    }
-
-    public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
-        mNotificationInflater.setRemoteViewClickHandler(remoteViewClickHandler);
-    }
-
-    public void setInflationCallback(InflationCallback callback) {
-        mNotificationInflater.setInflationCallback(callback);
-    }
-
-    public void setNeedsRedaction(boolean needsRedaction) {
-        mNotificationInflater.setRedactAmbient(needsRedaction);
-    }
-
-    @VisibleForTesting
-    public NotificationInflater getNotificationInflater() {
-        return mNotificationInflater;
-    }
-
-    public int getNotificationColorAmbient() {
-        return mNotificationColorAmbient;
-    }
-
-    public interface ExpansionLogger {
-        void logNotificationExpansion(String key, boolean userAction, boolean expanded);
-    }
-
-    public ExpandableNotificationRow(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mFalsingManager = FalsingManager.getInstance(context);
-        mNotificationInflater = new NotificationInflater(this);
-        mMenuRow = new NotificationMenuRow(mContext);
-        initDimens();
-    }
-
-    private void initDimens() {
-        mNotificationMinHeightLegacy = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_min_height_legacy);
-        mNotificationMinHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_min_height_before_p);
-        mNotificationMinHeight = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_min_height);
-        mNotificationMinHeightLarge = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_min_height_increased);
-        mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_max_height);
-        mNotificationAmbientHeight = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_ambient_height);
-        mMaxHeadsUpHeightLegacy = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_max_heads_up_height_legacy);
-        mMaxHeadsUpHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_max_heads_up_height_before_p);
-        mMaxHeadsUpHeight = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_max_heads_up_height);
-        mMaxHeadsUpHeightIncreased = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_max_heads_up_height_increased);
-
-        Resources res = getResources();
-        mIncreasedPaddingBetweenElements = res.getDimensionPixelSize(
-                R.dimen.notification_divider_height_increased);
-        mIconTransformContentShiftNoIcon = res.getDimensionPixelSize(
-                R.dimen.notification_icon_transform_content_shift);
-        mEnableNonGroupedNotificationExpand =
-                res.getBoolean(R.bool.config_enableNonGroupedNotificationExpand);
-        mShowGroupBackgroundWhenExpanded =
-                res.getBoolean(R.bool.config_showGroupNotificationBgWhenExpanded);
-    }
-
-    /**
-     * Resets this view so it can be re-used for an updated notification.
-     */
-    public void reset() {
-        mShowingPublicInitialized = false;
-        onHeightReset();
-        requestLayout();
-    }
-
-    public void showAppOpsIcons(ArraySet<Integer> activeOps) {
-        if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) {
-            mChildrenContainer.getHeaderView().showAppOpsIcons(activeOps);
-        }
-        mPrivateLayout.showAppOpsIcons(activeOps);
-        mPublicLayout.showAppOpsIcons(activeOps);
-    }
-
-    public View.OnClickListener getAppOpsOnClickListener() {
-        return mOnAppOpsClickListener;
-    }
-
-    protected void setAppOpsOnClickListener(ExpandableNotificationRow.OnAppOpsClickListener l) {
-        mOnAppOpsClickListener = v -> {
-            createMenu();
-            MenuItem menuItem = getProvider().getAppOpsMenuItem(mContext);
-            if (menuItem != null) {
-                l.onClick(this, v.getWidth() / 2, v.getHeight() / 2, menuItem);
-            }
-        };
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
-        mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
-        mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
-
-        for (NotificationContentView l : mLayouts) {
-            l.setExpandClickListener(mExpandClickListener);
-            l.setContainingNotification(this);
-        }
-        mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
-        mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() {
-            @Override
-            public void onInflate(ViewStub stub, View inflated) {
-                mGuts = (NotificationGuts) inflated;
-                mGuts.setClipTopAmount(getClipTopAmount());
-                mGuts.setActualHeight(getActualHeight());
-                mGutsStub = null;
-            }
-        });
-        mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
-        mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() {
-
-            @Override
-            public void onInflate(ViewStub stub, View inflated) {
-                mChildrenContainer = (NotificationChildrenContainer) inflated;
-                mChildrenContainer.setIsLowPriority(mIsLowPriority);
-                mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
-                mChildrenContainer.onNotificationUpdated();
-
-                if (mShouldTranslateContents) {
-                    mTranslateableViews.add(mChildrenContainer);
-                }
-            }
-        });
-
-        if (mShouldTranslateContents) {
-            // Add the views that we translate to reveal the menu
-            mTranslateableViews = new ArrayList<>();
-            for (int i = 0; i < getChildCount(); i++) {
-                mTranslateableViews.add(getChildAt(i));
-            }
-            // Remove views that don't translate
-            mTranslateableViews.remove(mChildrenContainerStub);
-            mTranslateableViews.remove(mGutsStub);
-        }
-    }
-
-    private void doLongClickCallback() {
-        doLongClickCallback(getWidth() / 2, getHeight() / 2);
-    }
-
-    public void doLongClickCallback(int x, int y) {
-        createMenu();
-        MenuItem menuItem = getProvider().getLongpressMenuItem(mContext);
-        doLongClickCallback(x, y, menuItem);
-    }
-
-    private void doLongClickCallback(int x, int y, MenuItem menuItem) {
-        if (mLongPressListener != null && menuItem != null) {
-            mLongPressListener.onLongPress(this, x, y, menuItem);
-        }
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (KeyEvent.isConfirmKey(keyCode)) {
-            event.startTracking();
-            return true;
-        }
-        return super.onKeyDown(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (KeyEvent.isConfirmKey(keyCode)) {
-            if (!event.isCanceled()) {
-                performClick();
-            }
-            return true;
-        }
-        return super.onKeyUp(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
-        if (KeyEvent.isConfirmKey(keyCode)) {
-            doLongClickCallback();
-            return true;
-        }
-        return false;
-    }
-
-    public void resetTranslation() {
-        if (mTranslateAnim != null) {
-            mTranslateAnim.cancel();
-        }
-
-        if (!mShouldTranslateContents) {
-            setTranslationX(0);
-        } else if (mTranslateableViews != null) {
-            for (int i = 0; i < mTranslateableViews.size(); i++) {
-                mTranslateableViews.get(i).setTranslationX(0);
-            }
-            invalidateOutline();
-            getEntry().expandedIcon.setScrollX(0);
-        }
-
-        mMenuRow.resetMenu();
-    }
-
-    void onGutsOpened() {
-        resetTranslation();
-        updateContentAccessibilityImportanceForGuts(false /* isEnabled */);
-    }
-
-    void onGutsClosed() {
-        updateContentAccessibilityImportanceForGuts(true /* isEnabled */);
-    }
-
-    /**
-     * Updates whether all the non-guts content inside this row is important for accessibility.
-     *
-     * @param isEnabled whether the content views should be enabled for accessibility
-     */
-    private void updateContentAccessibilityImportanceForGuts(boolean isEnabled) {
-        if (mChildrenContainer != null) {
-            updateChildAccessibilityImportance(mChildrenContainer, isEnabled);
-        }
-        if (mLayouts != null) {
-            for (View view : mLayouts) {
-                updateChildAccessibilityImportance(view, isEnabled);
-            }
-        }
-
-        if (isEnabled) {
-            this.requestAccessibilityFocus();
-        }
-    }
-
-    /**
-     * Updates whether the given childView is important for accessibility based on
-     * {@code isEnabled}.
-     */
-    private void updateChildAccessibilityImportance(View childView, boolean isEnabled) {
-        childView.setImportantForAccessibility(isEnabled
-                ? View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
-                : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-    }
-
-    public CharSequence getActiveRemoteInputText() {
-        return mPrivateLayout.getActiveRemoteInputText();
-    }
-
-    public void animateTranslateNotification(final float leftTarget) {
-        if (mTranslateAnim != null) {
-            mTranslateAnim.cancel();
-        }
-        mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */);
-        if (mTranslateAnim != null) {
-            mTranslateAnim.start();
-        }
-    }
-
-    @Override
-    public void setTranslation(float translationX) {
-        if (areGutsExposed()) {
-            // Don't translate if guts are showing.
-            return;
-        }
-        if (!mShouldTranslateContents) {
-            setTranslationX(translationX);
-        } else if (mTranslateableViews != null) {
-            // Translate the group of views
-            for (int i = 0; i < mTranslateableViews.size(); i++) {
-                if (mTranslateableViews.get(i) != null) {
-                    mTranslateableViews.get(i).setTranslationX(translationX);
-                }
-            }
-            invalidateOutline();
-
-            // In order to keep the shelf in sync with this swiping, we're simply translating
-            // it's icon by the same amount. The translation is already being used for the normal
-            // positioning, so we can use the scrollX instead.
-            getEntry().expandedIcon.setScrollX((int) -translationX);
-        }
-        if (mMenuRow.getMenuView() != null) {
-            mMenuRow.onTranslationUpdate(translationX);
-        }
-    }
-
-    @Override
-    public float getTranslation() {
-        if (!mShouldTranslateContents) {
-            return getTranslationX();
-        }
-
-        if (mTranslateableViews != null && mTranslateableViews.size() > 0) {
-            // All of the views in the list should have same translation, just use first one.
-            return mTranslateableViews.get(0).getTranslationX();
-        }
-
-        return 0;
-    }
-
-    public Animator getTranslateViewAnimator(final float leftTarget,
-            AnimatorUpdateListener listener) {
-        if (mTranslateAnim != null) {
-            mTranslateAnim.cancel();
-        }
-        if (areGutsExposed()) {
-            // No translation if guts are exposed.
-            return null;
-        }
-        final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(this, TRANSLATE_CONTENT,
-                leftTarget);
-        if (listener != null) {
-            translateAnim.addUpdateListener(listener);
-        }
-        translateAnim.addListener(new AnimatorListenerAdapter() {
-            boolean cancelled = false;
-
-            @Override
-            public void onAnimationCancel(Animator anim) {
-                cancelled = true;
-            }
-
-            @Override
-            public void onAnimationEnd(Animator anim) {
-                if (!cancelled && leftTarget == 0) {
-                    mMenuRow.resetMenu();
-                    mTranslateAnim = null;
-                }
-            }
-        });
-        mTranslateAnim = translateAnim;
-        return translateAnim;
-    }
-
-    public void inflateGuts() {
-        if (mGuts == null) {
-            mGutsStub.inflate();
-        }
-    }
-
-    private void updateChildrenVisibility() {
-        boolean hideContentWhileLaunching = mExpandAnimationRunning && mGuts != null
-                && mGuts.isExposed();
-        mPrivateLayout.setVisibility(!shouldShowPublic() && !mIsSummaryWithChildren
-                && !hideContentWhileLaunching ? VISIBLE : INVISIBLE);
-        if (mChildrenContainer != null) {
-            mChildrenContainer.setVisibility(!shouldShowPublic() && mIsSummaryWithChildren
-                    && !hideContentWhileLaunching ? VISIBLE
-                    : INVISIBLE);
-        }
-        // The limits might have changed if the view suddenly became a group or vice versa
-        updateLimits();
-    }
-
-    @Override
-    public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
-        if (super.onRequestSendAccessibilityEventInternal(child, event)) {
-            // Add a record for the entire layout since its content is somehow small.
-            // The event comes from a leaf view that is interacted with.
-            AccessibilityEvent record = AccessibilityEvent.obtain();
-            onInitializeAccessibilityEvent(record);
-            dispatchPopulateAccessibilityEvent(record);
-            event.appendRecord(record);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public void setDark(boolean dark, boolean fade, long delay) {
-        super.setDark(dark, fade, delay);
-        mDark = dark;
-        if (!mIsHeadsUp) {
-            // Only fade the showing view of the pulsing notification.
-            fade = false;
-        }
-        final NotificationContentView showing = getShowingLayout();
-        if (showing != null) {
-            showing.setDark(dark, fade, delay);
-        }
-        if (mIsSummaryWithChildren) {
-            mChildrenContainer.setDark(dark, fade, delay);
-        }
-        updateShelfIconColor();
-    }
-
-    public void applyExpandAnimationParams(ExpandAnimationParameters params) {
-        if (params == null) {
-            return;
-        }
-        float zProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
-                params.getProgress(0, 50));
-        float translationZ = MathUtils.lerp(params.getStartTranslationZ(),
-                mNotificationLaunchHeight,
-                zProgress);
-        setTranslationZ(translationZ);
-        float extraWidthForClipping = params.getWidth() - getWidth()
-                + MathUtils.lerp(0, mOutlineRadius * 2, params.getProgress());
-        setExtraWidthForClipping(extraWidthForClipping);
-        int top = params.getTop();
-        float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
-        int startClipTopAmount = params.getStartClipTopAmount();
-        if (mNotificationParent != null) {
-            top -= mNotificationParent.getTranslationY();
-            mNotificationParent.setTranslationZ(translationZ);
-            int parentStartClipTopAmount = params.getParentStartClipTopAmount();
-            if (startClipTopAmount != 0) {
-                int clipTopAmount = (int) MathUtils.lerp(parentStartClipTopAmount,
-                        parentStartClipTopAmount - startClipTopAmount,
-                        interpolation);
-                mNotificationParent.setClipTopAmount(clipTopAmount);
-            }
-            mNotificationParent.setExtraWidthForClipping(extraWidthForClipping);
-            mNotificationParent.setMinimumHeightForClipping(params.getHeight()
-                    + mNotificationParent.getActualHeight());
-        } else if (startClipTopAmount != 0) {
-            int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
-            setClipTopAmount(clipTopAmount);
-        }
-        setTranslationY(top);
-        setActualHeight(params.getHeight());
-
-        mBackgroundNormal.setExpandAnimationParams(params);
-    }
-
-    public void setExpandAnimationRunning(boolean expandAnimationRunning) {
-        View contentView;
-        if (mIsSummaryWithChildren) {
-            contentView =  mChildrenContainer;
-        } else {
-            contentView = getShowingLayout();
-        }
-        if (mGuts != null && mGuts.isExposed()) {
-            contentView = mGuts;
-        }
-        if (expandAnimationRunning) {
-            contentView.animate()
-                    .alpha(0f)
-                    .setDuration(ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT)
-                    .setInterpolator(Interpolators.ALPHA_OUT);
-            setAboveShelf(true);
-            mExpandAnimationRunning = true;
-            mNotificationViewState.cancelAnimations(this);
-            mNotificationLaunchHeight = AmbientState.getNotificationLaunchHeight(getContext());
-        } else {
-            mExpandAnimationRunning = false;
-            setAboveShelf(isAboveShelf());
-            if (mGuts != null) {
-                mGuts.setAlpha(1.0f);
-            }
-            if (contentView != null) {
-                contentView.setAlpha(1.0f);
-            }
-            setExtraWidthForClipping(0.0f);
-            if (mNotificationParent != null) {
-                mNotificationParent.setExtraWidthForClipping(0.0f);
-                mNotificationParent.setMinimumHeightForClipping(0);
-            }
-        }
-        if (mNotificationParent != null) {
-            mNotificationParent.setChildIsExpanding(mExpandAnimationRunning);
-        }
-        updateChildrenVisibility();
-        updateClipping();
-        mBackgroundNormal.setExpandAnimationRunning(expandAnimationRunning);
-    }
-
-    private void setChildIsExpanding(boolean isExpanding) {
-        mChildIsExpanding = isExpanding;
-    }
-
-    @Override
-    public boolean hasExpandingChild() {
-        return mChildIsExpanding;
-    }
-
-    @Override
-    protected boolean shouldClipToActualHeight() {
-        return super.shouldClipToActualHeight() && !mExpandAnimationRunning && !mChildIsExpanding;
-    }
-
-    @Override
-    public boolean isExpandAnimationRunning() {
-        return mExpandAnimationRunning;
-    }
-
-    /**
-     * Tap sounds should not be played when we're unlocking.
-     * Doing so would cause audio collision and the system would feel unpolished.
-     */
-    @Override
-    public boolean isSoundEffectsEnabled() {
-        final boolean mute = mDark && mSecureStateProvider != null &&
-                !mSecureStateProvider.getAsBoolean();
-        return !mute && super.isSoundEffectsEnabled();
-    }
-
-    public boolean isExpandable() {
-        if (mIsSummaryWithChildren && !shouldShowPublic()) {
-            return !mChildrenExpanded;
-        }
-        return mEnableNonGroupedNotificationExpand && mExpandable;
-    }
-
-    public void setExpandable(boolean expandable) {
-        mExpandable = expandable;
-        mPrivateLayout.updateExpandButtons(isExpandable());
-    }
-
-    @Override
-    public void setClipToActualHeight(boolean clipToActualHeight) {
-        super.setClipToActualHeight(clipToActualHeight || isUserLocked());
-        getShowingLayout().setClipToActualHeight(clipToActualHeight || isUserLocked());
-    }
-
-    /**
-     * @return whether the user has changed the expansion state
-     */
-    public boolean hasUserChangedExpansion() {
-        return mHasUserChangedExpansion;
-    }
-
-    public boolean isUserExpanded() {
-        return mUserExpanded;
-    }
-
-    /**
-     * Set this notification to be expanded by the user
-     *
-     * @param userExpanded whether the user wants this notification to be expanded
-     */
-    public void setUserExpanded(boolean userExpanded) {
-        setUserExpanded(userExpanded, false /* allowChildExpansion */);
-    }
-
-    /**
-     * Set this notification to be expanded by the user
-     *
-     * @param userExpanded whether the user wants this notification to be expanded
-     * @param allowChildExpansion whether a call to this method allows expanding children
-     */
-    public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
-        mFalsingManager.setNotificationExpanded();
-        if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion
-                && !mChildrenContainer.showingAsLowPriority()) {
-            final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
-            mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded);
-            onExpansionChanged(true /* userAction */, wasExpanded);
-            return;
-        }
-        if (userExpanded && !mExpandable) return;
-        final boolean wasExpanded = isExpanded();
-        mHasUserChangedExpansion = true;
-        mUserExpanded = userExpanded;
-        onExpansionChanged(true /* userAction */, wasExpanded);
-        if (!wasExpanded && isExpanded()
-                && getActualHeight() != getIntrinsicHeight()) {
-            notifyHeightChanged(true /* needsAnimation */);
-        }
-    }
-
-    public void resetUserExpansion() {
-        boolean changed = mUserExpanded;
-        mHasUserChangedExpansion = false;
-        mUserExpanded = false;
-        if (changed && mIsSummaryWithChildren) {
-            mChildrenContainer.onExpansionChanged();
-        }
-        updateShelfIconColor();
-    }
-
-    public boolean isUserLocked() {
-        return mUserLocked && !mForceUnlocked;
-    }
-
-    public void setUserLocked(boolean userLocked) {
-        mUserLocked = userLocked;
-        mPrivateLayout.setUserExpanding(userLocked);
-        // This is intentionally not guarded with mIsSummaryWithChildren since we might have had
-        // children but not anymore.
-        if (mChildrenContainer != null) {
-            mChildrenContainer.setUserLocked(userLocked);
-            if (mIsSummaryWithChildren && (userLocked || !isGroupExpanded())) {
-                updateBackgroundForGroupState();
-            }
-        }
-    }
-
-    /**
-     * @return has the system set this notification to be expanded
-     */
-    public boolean isSystemExpanded() {
-        return mIsSystemExpanded;
-    }
-
-    /**
-     * Set this notification to be expanded by the system.
-     *
-     * @param expand whether the system wants this notification to be expanded.
-     */
-    public void setSystemExpanded(boolean expand) {
-        if (expand != mIsSystemExpanded) {
-            final boolean wasExpanded = isExpanded();
-            mIsSystemExpanded = expand;
-            notifyHeightChanged(false /* needsAnimation */);
-            onExpansionChanged(false /* userAction */, wasExpanded);
-            if (mIsSummaryWithChildren) {
-                mChildrenContainer.updateGroupOverflow();
-            }
-        }
-    }
-
-    /**
-     * @param onKeyguard whether to prevent notification expansion
-     */
-    public void setOnKeyguard(boolean onKeyguard) {
-        if (onKeyguard != mOnKeyguard) {
-            boolean wasAboveShelf = isAboveShelf();
-            final boolean wasExpanded = isExpanded();
-            mOnKeyguard = onKeyguard;
-            onExpansionChanged(false /* userAction */, wasExpanded);
-            if (wasExpanded != isExpanded()) {
-                if (mIsSummaryWithChildren) {
-                    mChildrenContainer.updateGroupOverflow();
-                }
-                notifyHeightChanged(false /* needsAnimation */);
-            }
-            if (isAboveShelf() != wasAboveShelf) {
-                mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
-            }
-        }
-        updateRippleAllowed();
-    }
-
-    private void updateRippleAllowed() {
-        boolean allowed = isOnKeyguard()
-                || mEntry.notification.getNotification().contentIntent == null;
-        setRippleAllowed(allowed);
-    }
-
-    /**
-     * @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 (mStatusBarNotification == null || !mStatusBarNotification.isClearable()) {
-            return false;
-        }
-        if (mIsSummaryWithChildren) {
-            List<ExpandableNotificationRow> notificationChildren =
-                    mChildrenContainer.getNotificationChildren();
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow child = notificationChildren.get(i);
-                if (!child.isClearable()) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        if (isUserLocked()) {
-            return getActualHeight();
-        }
-        if (mGuts != null && mGuts.isExposed()) {
-            return mGuts.getIntrinsicHeight();
-        } else if ((isChildInGroup() && !isGroupExpanded())) {
-            return mPrivateLayout.getMinHeight();
-        } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
-            return getMinHeight();
-        } else if (mIsSummaryWithChildren && (!mOnKeyguard || mShowAmbient)) {
-            return mChildrenContainer.getIntrinsicHeight();
-        } else if (isHeadsUpAllowed() && (mIsHeadsUp || mHeadsupDisappearRunning)) {
-            if (isPinned() || mHeadsupDisappearRunning) {
-                return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
-            } else if (isExpanded()) {
-                return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
-            } else {
-                return Math.max(getCollapsedHeight(), getHeadsUpHeight());
-            }
-        } else if (isExpanded()) {
-            return getMaxExpandHeight();
-        } else {
-            return getCollapsedHeight();
-        }
-    }
-
-    private boolean isHeadsUpAllowed() {
-        return !mOnKeyguard && !mShowAmbient;
-    }
-
-    @Override
-    public boolean isGroupExpanded() {
-        return mGroupManager.isGroupExpanded(mStatusBarNotification);
-    }
-
-    private void onChildrenCountChanged() {
-        mIsSummaryWithChildren = StatusBar.ENABLE_CHILD_NOTIFICATIONS
-                && mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0;
-        if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
-            mChildrenContainer.recreateNotificationHeader(mExpandClickListener
-            );
-        }
-        getShowingLayout().updateBackgroundColor(false /* animate */);
-        mPrivateLayout.updateExpandButtons(isExpandable());
-        updateChildrenHeaderAppearance();
-        updateChildrenVisibility();
-        applyChildrenRoundness();
-    }
-    /**
-     * Returns the number of channels covered by the notification row (including its children if
-     * it's a summary notification).
-     */
-    public int getNumUniqueChannels() {
-        ArraySet<NotificationChannel> channels = new ArraySet<>();
-
-        channels.add(mEntry.channel);
-
-        // If this is a summary, then add in the children notification channels for the
-        // same user and pkg.
-        if (mIsSummaryWithChildren) {
-            final List<ExpandableNotificationRow> childrenRows = getNotificationChildren();
-            final int numChildren = childrenRows.size();
-            for (int i = 0; i < numChildren; i++) {
-                final ExpandableNotificationRow childRow = childrenRows.get(i);
-                final NotificationChannel childChannel = childRow.getEntry().channel;
-                final StatusBarNotification childSbn = childRow.getStatusBarNotification();
-                if (childSbn.getUser().equals(mStatusBarNotification.getUser()) &&
-                        childSbn.getPackageName().equals(mStatusBarNotification.getPackageName())) {
-                    channels.add(childChannel);
-                }
-            }
-        }
-        return channels.size();
-    }
-
-    public void updateChildrenHeaderAppearance() {
-        if (mIsSummaryWithChildren) {
-            mChildrenContainer.updateChildrenHeaderAppearance();
-        }
-    }
-
-    /**
-     * Check whether the view state is currently expanded. This is given by the system in {@link
-     * #setSystemExpanded(boolean)} and can be overridden by user expansion or
-     * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
-     * view can differ from this state, if layout params are modified from outside.
-     *
-     * @return whether the view state is currently expanded.
-     */
-    public boolean isExpanded() {
-        return isExpanded(false /* allowOnKeyguard */);
-    }
-
-    public boolean isExpanded(boolean allowOnKeyguard) {
-        return (!mOnKeyguard || allowOnKeyguard)
-                && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
-                || isUserExpanded());
-    }
-
-    private boolean isSystemChildExpanded() {
-        return mIsSystemChildExpanded;
-    }
-
-    public void setSystemChildExpanded(boolean expanded) {
-        mIsSystemChildExpanded = expanded;
-    }
-
-    public void setLayoutListener(LayoutListener listener) {
-        mLayoutListener = listener;
-    }
-
-    public void removeListener() {
-        mLayoutListener = null;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        int intrinsicBefore = getIntrinsicHeight();
-        super.onLayout(changed, left, top, right, bottom);
-        if (intrinsicBefore != getIntrinsicHeight()) {
-            notifyHeightChanged(true  /* needsAnimation */);
-        }
-        if (mMenuRow.getMenuView() != null) {
-            mMenuRow.onHeightUpdate();
-        }
-        updateContentShiftHeight();
-        if (mLayoutListener != null) {
-            mLayoutListener.onLayout();
-        }
-    }
-
-    /**
-     * Updates the content shift height such that the header is completely hidden when coming from
-     * the top.
-     */
-    private void updateContentShiftHeight() {
-        NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
-        if (notificationHeader != null) {
-            CachingIconView icon = notificationHeader.getIcon();
-            mIconTransformContentShift = getRelativeTopPadding(icon) + icon.getHeight();
-        } else {
-            mIconTransformContentShift = mIconTransformContentShiftNoIcon;
-        }
-    }
-
-    @Override
-    public void notifyHeightChanged(boolean needsAnimation) {
-        super.notifyHeightChanged(needsAnimation);
-        getShowingLayout().requestSelectLayout(needsAnimation || isUserLocked());
-    }
-
-    public void setSensitive(boolean sensitive, boolean hideSensitive) {
-        mSensitive = sensitive;
-        mSensitiveHiddenInGeneral = hideSensitive;
-    }
-
-    @Override
-    public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
-        mHideSensitiveForIntrinsicHeight = hideSensitive;
-        if (mIsSummaryWithChildren) {
-            List<ExpandableNotificationRow> notificationChildren =
-                    mChildrenContainer.getNotificationChildren();
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow child = notificationChildren.get(i);
-                child.setHideSensitiveForIntrinsicHeight(hideSensitive);
-            }
-        }
-    }
-
-    @Override
-    public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
-            long duration) {
-        if (getVisibility() == GONE) {
-            // If we are GONE, the hideSensitive parameter will not be calculated and always be
-            // false, which is incorrect, let's wait until a real call comes in later.
-            return;
-        }
-        boolean oldShowingPublic = mShowingPublic;
-        mShowingPublic = mSensitive && hideSensitive;
-        if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
-            return;
-        }
-
-        // bail out if no public version
-        if (mPublicLayout.getChildCount() == 0) return;
-
-        if (!animated) {
-            mPublicLayout.animate().cancel();
-            mPrivateLayout.animate().cancel();
-            if (mChildrenContainer != null) {
-                mChildrenContainer.animate().cancel();
-                mChildrenContainer.setAlpha(1f);
-            }
-            mPublicLayout.setAlpha(1f);
-            mPrivateLayout.setAlpha(1f);
-            mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
-            updateChildrenVisibility();
-        } else {
-            animateShowingPublic(delay, duration, mShowingPublic);
-        }
-        NotificationContentView showingLayout = getShowingLayout();
-        showingLayout.updateBackgroundColor(animated);
-        mPrivateLayout.updateExpandButtons(isExpandable());
-        updateShelfIconColor();
-        showingLayout.setDark(isDark(), false /* animate */, 0 /* delay */);
-        mShowingPublicInitialized = true;
-    }
-
-    private void animateShowingPublic(long delay, long duration, boolean showingPublic) {
-        View[] privateViews = mIsSummaryWithChildren
-                ? new View[] {mChildrenContainer}
-                : new View[] {mPrivateLayout};
-        View[] publicViews = new View[] {mPublicLayout};
-        View[] hiddenChildren = showingPublic ? privateViews : publicViews;
-        View[] shownChildren = showingPublic ? publicViews : privateViews;
-        for (final View hiddenView : hiddenChildren) {
-            hiddenView.setVisibility(View.VISIBLE);
-            hiddenView.animate().cancel();
-            hiddenView.animate()
-                    .alpha(0f)
-                    .setStartDelay(delay)
-                    .setDuration(duration)
-                    .withEndAction(new Runnable() {
-                        @Override
-                        public void run() {
-                            hiddenView.setVisibility(View.INVISIBLE);
-                        }
-                    });
-        }
-        for (View showView : shownChildren) {
-            showView.setVisibility(View.VISIBLE);
-            showView.setAlpha(0f);
-            showView.animate().cancel();
-            showView.animate()
-                    .alpha(1f)
-                    .setStartDelay(delay)
-                    .setDuration(duration);
-        }
-    }
-
-    @Override
-    public boolean mustStayOnScreen() {
-        return mIsHeadsUp && mMustStayOnScreen;
-    }
-
-    /**
-     * @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 #isClearable()}.
-     */
-    public boolean canViewBeDismissed() {
-        return isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
-    }
-
-    private boolean shouldShowPublic() {
-        return mSensitive && mHideSensitiveForIntrinsicHeight;
-    }
-
-    public void makeActionsVisibile() {
-        setUserExpanded(true, true);
-        if (isChildInGroup()) {
-            mGroupManager.setGroupExpanded(mStatusBarNotification, true);
-        }
-        notifyHeightChanged(false /* needsAnimation */);
-    }
-
-    public void setChildrenExpanded(boolean expanded, boolean animate) {
-        mChildrenExpanded = expanded;
-        if (mChildrenContainer != null) {
-            mChildrenContainer.setChildrenExpanded(expanded);
-        }
-        updateBackgroundForGroupState();
-        updateClickAndFocus();
-    }
-
-    public static void applyTint(View v, int color) {
-        int alpha;
-        if (color != 0) {
-            alpha = COLORED_DIVIDER_ALPHA;
-        } else {
-            color = 0xff000000;
-            alpha = DEFAULT_DIVIDER_ALPHA;
-        }
-        if (v.getBackground() instanceof ColorDrawable) {
-            ColorDrawable background = (ColorDrawable) v.getBackground();
-            background.mutate();
-            background.setColor(color);
-            background.setAlpha(alpha);
-        }
-    }
-
-    public int getMaxExpandHeight() {
-        return mPrivateLayout.getExpandHeight();
-    }
-
-
-    private int getHeadsUpHeight() {
-        return mPrivateLayout.getHeadsUpHeight();
-    }
-
-    public boolean areGutsExposed() {
-        return (mGuts != null && mGuts.isExposed());
-    }
-
-    @Override
-    public boolean isContentExpandable() {
-        if (mIsSummaryWithChildren && !shouldShowPublic()) {
-            return true;
-        }
-        NotificationContentView showingLayout = getShowingLayout();
-        return showingLayout.isContentExpandable();
-    }
-
-    @Override
-    protected View getContentView() {
-        if (mIsSummaryWithChildren && !shouldShowPublic()) {
-            return mChildrenContainer;
-        }
-        return getShowingLayout();
-    }
-
-    @Override
-    protected void onAppearAnimationFinished(boolean wasAppearing) {
-        super.onAppearAnimationFinished(wasAppearing);
-        if (wasAppearing) {
-            // During the animation the visible view might have changed, so let's make sure all
-            // alphas are reset
-            if (mChildrenContainer != null) {
-                mChildrenContainer.setAlpha(1.0f);
-                mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
-            }
-            for (NotificationContentView l : mLayouts) {
-                l.setAlpha(1.0f);
-                l.setLayerType(LAYER_TYPE_NONE, null);
-            }
-        }
-    }
-
-    @Override
-    public int getExtraBottomPadding() {
-        if (mIsSummaryWithChildren && isGroupExpanded()) {
-            return mIncreasedPaddingBetweenElements;
-        }
-        return 0;
-    }
-
-    @Override
-    public void setActualHeight(int height, boolean notifyListeners) {
-        boolean changed = height != getActualHeight();
-        super.setActualHeight(height, notifyListeners);
-        if (changed && isRemoved()) {
-            // TODO: remove this once we found the gfx bug for this.
-            // This is a hack since a removed view sometimes would just stay blank. it occured
-            // when sending yourself a message and then clicking on it.
-            ViewGroup parent = (ViewGroup) getParent();
-            if (parent != null) {
-                parent.invalidate();
-            }
-        }
-        if (mGuts != null && mGuts.isExposed()) {
-            mGuts.setActualHeight(height);
-            return;
-        }
-        int contentHeight = Math.max(getMinHeight(), height);
-        for (NotificationContentView l : mLayouts) {
-            l.setContentHeight(contentHeight);
-        }
-        if (mIsSummaryWithChildren) {
-            mChildrenContainer.setActualHeight(height);
-        }
-        if (mGuts != null) {
-            mGuts.setActualHeight(height);
-        }
-        if (mMenuRow.getMenuView() != null) {
-            mMenuRow.onHeightUpdate();
-        }
-    }
-
-    @Override
-    public int getMaxContentHeight() {
-        if (mIsSummaryWithChildren && !shouldShowPublic()) {
-            return mChildrenContainer.getMaxContentHeight();
-        }
-        NotificationContentView showingLayout = getShowingLayout();
-        return showingLayout.getMaxHeight();
-    }
-
-    @Override
-    public int getMinHeight(boolean ignoreTemporaryStates) {
-        if (!ignoreTemporaryStates && mGuts != null && mGuts.isExposed()) {
-            return mGuts.getIntrinsicHeight();
-        } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp
-                && mHeadsUpManager.isTrackingHeadsUp()) {
-                return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
-        } else if (mIsSummaryWithChildren && !isGroupExpanded() && !shouldShowPublic()) {
-            return mChildrenContainer.getMinHeight();
-        } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) {
-            return getHeadsUpHeight();
-        }
-        NotificationContentView showingLayout = getShowingLayout();
-        return showingLayout.getMinHeight();
-    }
-
-    @Override
-    public int getCollapsedHeight() {
-        if (mIsSummaryWithChildren && !shouldShowPublic()) {
-            return mChildrenContainer.getCollapsedHeight();
-        }
-        return getMinHeight();
-    }
-
-    @Override
-    public void setClipTopAmount(int clipTopAmount) {
-        super.setClipTopAmount(clipTopAmount);
-        for (NotificationContentView l : mLayouts) {
-            l.setClipTopAmount(clipTopAmount);
-        }
-        if (mGuts != null) {
-            mGuts.setClipTopAmount(clipTopAmount);
-        }
-    }
-
-    @Override
-    public void setClipBottomAmount(int clipBottomAmount) {
-        if (mExpandAnimationRunning) {
-            return;
-        }
-        if (clipBottomAmount != mClipBottomAmount) {
-            super.setClipBottomAmount(clipBottomAmount);
-            for (NotificationContentView l : mLayouts) {
-                l.setClipBottomAmount(clipBottomAmount);
-            }
-            if (mGuts != null) {
-                mGuts.setClipBottomAmount(clipBottomAmount);
-            }
-        }
-        if (mChildrenContainer != null && !mChildIsExpanding) {
-            // We have to update this even if it hasn't changed, since the children locations can
-            // have changed
-            mChildrenContainer.setClipBottomAmount(clipBottomAmount);
-        }
-    }
-
-    public NotificationContentView getShowingLayout() {
-        return shouldShowPublic() ? mPublicLayout : mPrivateLayout;
-    }
-
-    public void setLegacy(boolean legacy) {
-        for (NotificationContentView l : mLayouts) {
-            l.setLegacy(legacy);
-        }
-    }
-
-    @Override
-    protected void updateBackgroundTint() {
-        super.updateBackgroundTint();
-        updateBackgroundForGroupState();
-        if (mIsSummaryWithChildren) {
-            List<ExpandableNotificationRow> notificationChildren =
-                    mChildrenContainer.getNotificationChildren();
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow child = notificationChildren.get(i);
-                child.updateBackgroundForGroupState();
-            }
-        }
-    }
-
-    /**
-     * Called when a group has finished animating from collapsed or expanded state.
-     */
-    public void onFinishedExpansionChange() {
-        mGroupExpansionChanging = false;
-        updateBackgroundForGroupState();
-    }
-
-    /**
-     * Updates the parent and children backgrounds in a group based on the expansion state.
-     */
-    public void updateBackgroundForGroupState() {
-        if (mIsSummaryWithChildren) {
-            // Only when the group has finished expanding do we hide its background.
-            mShowNoBackground = !mShowGroupBackgroundWhenExpanded && isGroupExpanded()
-                    && !isGroupExpansionChanging() && !isUserLocked();
-            mChildrenContainer.updateHeaderForExpansion(mShowNoBackground);
-            List<ExpandableNotificationRow> children = mChildrenContainer.getNotificationChildren();
-            for (int i = 0; i < children.size(); i++) {
-                children.get(i).updateBackgroundForGroupState();
-            }
-        } else if (isChildInGroup()) {
-            final int childColor = getShowingLayout().getBackgroundColorForExpansionState();
-            // Only show a background if the group is expanded OR if it is expanding / collapsing
-            // and has a custom background color.
-            final boolean showBackground = isGroupExpanded()
-                    || ((mNotificationParent.isGroupExpansionChanging()
-                    || mNotificationParent.isUserLocked()) && childColor != 0);
-            mShowNoBackground = !showBackground;
-        } else {
-            // Only children or parents ever need no background.
-            mShowNoBackground = false;
-        }
-        updateOutline();
-        updateBackground();
-    }
-
-    public int getPositionOfChild(ExpandableNotificationRow childRow) {
-        if (mIsSummaryWithChildren) {
-            return mChildrenContainer.getPositionInLinearLayout(childRow);
-        }
-        return 0;
-    }
-
-    public void setExpansionLogger(ExpansionLogger logger, String key) {
-        mLogger = logger;
-        mLoggingKey = key;
-    }
-
-    public void onExpandedByGesture(boolean userExpanded) {
-        int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
-        if (mGroupManager.isSummaryOfGroup(getStatusBarNotification())) {
-            event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER;
-        }
-        MetricsLogger.action(mContext, event, userExpanded);
-    }
-
-    @Override
-    public float getIncreasedPaddingAmount() {
-        if (mIsSummaryWithChildren) {
-            if (isGroupExpanded()) {
-                return 1.0f;
-            } else if (isUserLocked()) {
-                return mChildrenContainer.getIncreasedPaddingAmount();
-            }
-        } else if (isColorized() && (!mIsLowPriority || isExpanded())) {
-            return -1.0f;
-        }
-        return 0.0f;
-    }
-
-    private boolean isColorized() {
-        return mIsColorized && mBgTint != NO_COLOR;
-    }
-
-    @Override
-    protected boolean disallowSingleClick(MotionEvent event) {
-        if (areGutsExposed()) {
-            return false;
-        }
-        float x = event.getX();
-        float y = event.getY();
-        NotificationHeaderView header = getVisibleNotificationHeader();
-        if (header != null && header.isInTouchRect(x - getTranslation(), y)) {
-            return true;
-        }
-        if ((!mIsSummaryWithChildren || shouldShowPublic())
-                && getShowingLayout().disallowSingleClick(x, y)) {
-            return true;
-        }
-        return super.disallowSingleClick(event);
-    }
-
-    private void onExpansionChanged(boolean userAction, boolean wasExpanded) {
-        boolean nowExpanded = isExpanded();
-        if (mIsSummaryWithChildren && (!mIsLowPriority || wasExpanded)) {
-            nowExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
-        }
-        if (nowExpanded != wasExpanded) {
-            updateShelfIconColor();
-            if (mLogger != null) {
-                mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded);
-            }
-            if (mIsSummaryWithChildren) {
-                mChildrenContainer.onExpansionChanged();
-            }
-        }
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
-        if (canViewBeDismissed()) {
-            info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
-        }
-        boolean expandable = shouldShowPublic();
-        boolean isExpanded = false;
-        if (!expandable) {
-            if (mIsSummaryWithChildren) {
-                expandable = true;
-                if (!mIsLowPriority || isExpanded()) {
-                    isExpanded = isGroupExpanded();
-                }
-            } else {
-                expandable = mPrivateLayout.isContentExpandable();
-                isExpanded = isExpanded();
-            }
-        }
-        if (expandable) {
-            if (isExpanded) {
-                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
-            } else {
-                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
-            }
-        }
-        NotificationMenuRowPlugin provider = getProvider();
-        if (provider != null) {
-            MenuItem snoozeMenu = provider.getSnoozeMenuItem(getContext());
-            if (snoozeMenu != null) {
-                AccessibilityAction action = new AccessibilityAction(R.id.action_snooze,
-                    getContext().getResources()
-                        .getString(R.string.notification_menu_snooze_action));
-                info.addAction(action);
-            }
-        }
-    }
-
-    @Override
-    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
-        if (super.performAccessibilityActionInternal(action, arguments)) {
-            return true;
-        }
-        switch (action) {
-            case AccessibilityNodeInfo.ACTION_DISMISS:
-                performDismissWithBlockingHelper(true /* fromAccessibility */);
-                return true;
-            case AccessibilityNodeInfo.ACTION_COLLAPSE:
-            case AccessibilityNodeInfo.ACTION_EXPAND:
-                mExpandClickListener.onClick(this);
-                return true;
-            case AccessibilityNodeInfo.ACTION_LONG_CLICK:
-                doLongClickCallback();
-                return true;
-            case R.id.action_snooze:
-                NotificationMenuRowPlugin provider = getProvider();
-                if (provider == null) {
-                    provider = createMenu();
-                }
-                MenuItem snoozeMenu = provider.getSnoozeMenuItem(getContext());
-                if (snoozeMenu != null) {
-                    doLongClickCallback(getWidth() / 2, getHeight() / 2, snoozeMenu);
-                }
-                return true;
-        }
-        return false;
-    }
-
-    public boolean shouldRefocusOnDismiss() {
-        return mRefocusOnDismiss || isAccessibilityFocused();
-    }
-
-    public interface OnExpandClickListener {
-        void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
-    }
-
-    @Override
-    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
-        mNotificationViewState = new NotificationViewState(stackScrollState);
-        return mNotificationViewState;
-    }
-
-    public NotificationViewState getViewState() {
-        return mNotificationViewState;
-    }
-
-    @Override
-    public boolean isAboveShelf() {
-        return !isOnKeyguard()
-                && (mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf)
-                || mExpandAnimationRunning || mChildIsExpanding);
-    }
-
-    public void setShowAmbient(boolean showAmbient) {
-        if (showAmbient != mShowAmbient) {
-            mShowAmbient = showAmbient;
-            if (mChildrenContainer != null) {
-                mChildrenContainer.notifyShowAmbientChanged();
-            }
-            notifyHeightChanged(false /* needsAnimation */);
-        }
-    }
-
-    @Override
-    public boolean topAmountNeedsClipping() {
-        if (isGroupExpanded()) {
-            return true;
-        }
-        if (isGroupExpansionChanging()) {
-            return true;
-        }
-        if (getShowingLayout().shouldClipToRounding(true /* topRounded */,
-                false /* bottomRounded */)) {
-            return true;
-        }
-        if (mGuts != null && mGuts.getAlpha() != 0.0f) {
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    protected boolean childNeedsClipping(View child) {
-        if (child instanceof NotificationContentView) {
-            NotificationContentView contentView = (NotificationContentView) child;
-            if (isClippingNeeded()) {
-                return true;
-            } else if (!hasNoRounding()
-                    && contentView.shouldClipToRounding(getCurrentTopRoundness() != 0.0f,
-                    getCurrentBottomRoundness() != 0.0f)) {
-                return true;
-            }
-        } else if (child == mChildrenContainer) {
-            if (!mChildIsExpanding && (isClippingNeeded() || !hasNoRounding())) {
-                return true;
-            }
-        } else if (child instanceof NotificationGuts) {
-            return !hasNoRounding();
-        }
-        return super.childNeedsClipping(child);
-    }
-
-    @Override
-    protected void applyRoundness() {
-        super.applyRoundness();
-        applyChildrenRoundness();
-    }
-
-    private void applyChildrenRoundness() {
-        if (mIsSummaryWithChildren) {
-            mChildrenContainer.setCurrentBottomRoundness(getCurrentBottomRoundness());
-        }
-    }
-
-    @Override
-    public Path getCustomClipPath(View child) {
-        if (child instanceof NotificationGuts) {
-            return getClipPath(true, /* ignoreTranslation */
-                    false /* clipRoundedToBottom */);
-        }
-        if (child instanceof NotificationChildrenContainer) {
-            return getClipPath(false, /* ignoreTranslation */
-                    true /* clipRoundedToBottom */);
-        }
-        return super.getCustomClipPath(child);
-    }
-
-    private boolean hasNoRounding() {
-        return getCurrentBottomRoundness() == 0.0f && getCurrentTopRoundness() == 0.0f;
-    }
-
-    public boolean isShowingAmbient() {
-        return mShowAmbient;
-    }
-
-    public void setAboveShelf(boolean aboveShelf) {
-        boolean wasAboveShelf = isAboveShelf();
-        mAboveShelf = aboveShelf;
-        if (isAboveShelf() != wasAboveShelf) {
-            mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
-        }
-    }
-
-    public static class NotificationViewState extends ExpandableViewState {
-
-        private final StackScrollState mOverallState;
-
-
-        private NotificationViewState(StackScrollState stackScrollState) {
-            mOverallState = stackScrollState;
-        }
-
-        @Override
-        public void applyToView(View view) {
-            if (view instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-                if (row.isExpandAnimationRunning()) {
-                    return;
-                }
-                handleFixedTranslationZ(row);
-                super.applyToView(view);
-                row.applyChildrenState(mOverallState);
-            }
-        }
-
-        private void handleFixedTranslationZ(ExpandableNotificationRow row) {
-            if (row.hasExpandingChild()) {
-                zTranslation = row.getTranslationZ();
-                clipTopAmount = row.getClipTopAmount();
-            }
-        }
-
-        @Override
-        protected void onYTranslationAnimationFinished(View view) {
-            super.onYTranslationAnimationFinished(view);
-            if (view instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-                if (row.isHeadsUpAnimatingAway()) {
-                    row.setHeadsUpAnimatingAway(false);
-                }
-            }
-        }
-
-        @Override
-        public void animateTo(View child, AnimationProperties properties) {
-            if (child instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                if (row.isExpandAnimationRunning()) {
-                    return;
-                }
-                handleFixedTranslationZ(row);
-                super.animateTo(child, properties);
-                row.startChildAnimation(mOverallState, properties);
-            }
-        }
-    }
-
-    @VisibleForTesting
-    protected void setChildrenContainer(NotificationChildrenContainer childrenContainer) {
-        mChildrenContainer = childrenContainer;
-    }
-
-    @VisibleForTesting
-    protected void setPrivateLayout(NotificationContentView privateLayout) {
-        mPrivateLayout = privateLayout;
-    }
-
-    @VisibleForTesting
-    protected void setPublicLayout(NotificationContentView publicLayout) {
-        mPublicLayout = publicLayout;
-    }
-
-    /**
-     * Equivalent to View.OnLongClickListener with coordinates
-     */
-    public interface LongPressListener {
-        /**
-         * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
-         * @return whether the longpress was handled
-         */
-        boolean onLongPress(View v, int x, int y, MenuItem item);
-    }
-
-    /**
-     * Equivalent to View.OnClickListener with coordinates
-     */
-    public interface OnAppOpsClickListener {
-        /**
-         * Equivalent to {@link View.OnClickListener#onClick(View)} with coordinates
-         * @return whether the click was handled
-         */
-        boolean onClick(View v, int x, int y, MenuItem item);
-    }
-
-    /**
-     * Background task for executing IPCs to check if the notification is a system notification. The
-     * output is used for both the blocking helper and the notification info.
-     */
-    private class SystemNotificationAsyncTask extends AsyncTask<Void, Void, Boolean> {
-
-        @Override
-        protected Boolean doInBackground(Void... voids) {
-            return isSystemNotification(mContext, mStatusBarNotification);
-        }
-
-        @Override
-        protected void onPostExecute(Boolean result) {
-            if (mEntry != null) {
-                mEntry.mIsSystemNotification = result;
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
deleted file mode 100644
index edfa61b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ /dev/null
@@ -1,452 +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
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Outline;
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-import com.android.settingslib.Utils;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
-
-/**
- * Like {@link ExpandableView}, but setting an outline for the height and clipping.
- */
-public abstract class ExpandableOutlineView extends ExpandableView {
-
-    private static final AnimatableProperty TOP_ROUNDNESS = AnimatableProperty.from(
-            "topRoundness",
-            ExpandableOutlineView::setTopRoundnessInternal,
-            ExpandableOutlineView::getCurrentTopRoundness,
-            R.id.top_roundess_animator_tag,
-            R.id.top_roundess_animator_end_tag,
-            R.id.top_roundess_animator_start_tag);
-    private static final AnimatableProperty BOTTOM_ROUNDNESS = AnimatableProperty.from(
-            "bottomRoundness",
-            ExpandableOutlineView::setBottomRoundnessInternal,
-            ExpandableOutlineView::getCurrentBottomRoundness,
-            R.id.bottom_roundess_animator_tag,
-            R.id.bottom_roundess_animator_end_tag,
-            R.id.bottom_roundess_animator_start_tag);
-    private static final AnimationProperties ROUNDNESS_PROPERTIES =
-            new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-    private static final Path EMPTY_PATH = new Path();
-
-    private final Rect mOutlineRect = new Rect();
-    private final Path mClipPath = new Path();
-    private boolean mCustomOutline;
-    private float mOutlineAlpha = -1f;
-    protected float mOutlineRadius;
-    private boolean mAlwaysRoundBothCorners;
-    private Path mTmpPath = new Path();
-    private Path mTmpPath2 = new Path();
-    private float mCurrentBottomRoundness;
-    private float mCurrentTopRoundness;
-    private float mBottomRoundness;
-    private float mTopRoundness;
-    private int mBackgroundTop;
-
-    /**
-     * {@code true} if the children views of the {@link ExpandableOutlineView} are translated when
-     * it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself.
-     */
-    protected boolean mShouldTranslateContents;
-    private boolean mTopAmountRounded;
-    private float mDistanceToTopRoundness = -1;
-    private float mExtraWidthForClipping;
-    private int mMinimumHeightForClipping = 0;
-
-    private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
-        @Override
-        public void getOutline(View view, Outline outline) {
-            if (!mCustomOutline && mCurrentTopRoundness == 0.0f
-                    && mCurrentBottomRoundness == 0.0f && !mAlwaysRoundBothCorners
-                    && !mTopAmountRounded) {
-                int translation = mShouldTranslateContents ? (int) getTranslation() : 0;
-                int left = Math.max(translation, 0);
-                int top = mClipTopAmount + mBackgroundTop;
-                int right = getWidth() + Math.min(translation, 0);
-                int bottom = Math.max(getActualHeight() - mClipBottomAmount, top);
-                outline.setRect(left, top, right, bottom);
-            } else {
-                Path clipPath = getClipPath();
-                if (clipPath != null && clipPath.isConvex()) {
-                    // The path might not be convex in border cases where the view is small and
-                    // clipped
-                    outline.setConvexPath(clipPath);
-                }
-            }
-            outline.setAlpha(mOutlineAlpha);
-        }
-    };
-
-    private Path getClipPath() {
-        return getClipPath(false, /* ignoreTranslation */
-                false /* clipRoundedToBottom */);
-    }
-
-    protected Path getClipPath(boolean ignoreTranslation, boolean clipRoundedToBottom) {
-        int left;
-        int top;
-        int right;
-        int bottom;
-        int height;
-        Path intersectPath = null;
-        if (!mCustomOutline) {
-            int translation = mShouldTranslateContents && !ignoreTranslation
-                    ? (int) getTranslation() : 0;
-            left = Math.max(translation, 0);
-            top = mClipTopAmount + mBackgroundTop;
-            right = getWidth() + Math.min(translation, 0);
-            bottom = Math.max(getActualHeight(), top);
-            int intersectBottom = Math.max(getActualHeight() - mClipBottomAmount, top);
-            if (bottom != intersectBottom) {
-                if (clipRoundedToBottom) {
-                    bottom = intersectBottom;
-                } else {
-                    getRoundedRectPath(left, top, right,
-                            intersectBottom, 0.0f,
-                            0.0f, mTmpPath2);
-                    intersectPath = mTmpPath2;
-                }
-            }
-        } else {
-            left = mOutlineRect.left;
-            top = mOutlineRect.top;
-            right = mOutlineRect.right;
-            bottom = mOutlineRect.bottom;
-        }
-        height = bottom - top;
-        if (height == 0) {
-            return EMPTY_PATH;
-        }
-        float topRoundness = mAlwaysRoundBothCorners
-                ? mOutlineRadius : getCurrentBackgroundRadiusTop();
-        float bottomRoundness = mAlwaysRoundBothCorners
-                ? mOutlineRadius : getCurrentBackgroundRadiusBottom();
-        if (topRoundness + bottomRoundness > height) {
-            float overShoot = topRoundness + bottomRoundness - height;
-            topRoundness -= overShoot * mCurrentTopRoundness
-                    / (mCurrentTopRoundness + mCurrentBottomRoundness);
-            bottomRoundness -= overShoot * mCurrentBottomRoundness
-                    / (mCurrentTopRoundness + mCurrentBottomRoundness);
-        }
-        getRoundedRectPath(left, top, right, bottom, topRoundness,
-                bottomRoundness, mTmpPath);
-        Path roundedRectPath = mTmpPath;
-        if (intersectPath != null) {
-            roundedRectPath.op(intersectPath, Path.Op.INTERSECT);
-        }
-        return roundedRectPath;
-    }
-
-    public static void getRoundedRectPath(int left, int top, int right, int bottom,
-            float topRoundness, float bottomRoundness, Path outPath) {
-        outPath.reset();
-        int width = right - left;
-        float topRoundnessX = topRoundness;
-        float bottomRoundnessX = bottomRoundness;
-        topRoundnessX = Math.min(width / 2, topRoundnessX);
-        bottomRoundnessX = Math.min(width / 2, bottomRoundnessX);
-        if (topRoundness > 0.0f) {
-            outPath.moveTo(left, top + topRoundness);
-            outPath.quadTo(left, top, left + topRoundnessX, top);
-            outPath.lineTo(right - topRoundnessX, top);
-            outPath.quadTo(right, top, right, top + topRoundness);
-        } else {
-            outPath.moveTo(left, top);
-            outPath.lineTo(right, top);
-        }
-        if (bottomRoundness > 0.0f) {
-            outPath.lineTo(right, bottom - bottomRoundness);
-            outPath.quadTo(right, bottom, right - bottomRoundnessX, bottom);
-            outPath.lineTo(left + bottomRoundnessX, bottom);
-            outPath.quadTo(left, bottom, left, bottom - bottomRoundness);
-        } else {
-            outPath.lineTo(right, bottom);
-            outPath.lineTo(left, bottom);
-        }
-        outPath.close();
-    }
-
-    public ExpandableOutlineView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setOutlineProvider(mProvider);
-        initDimens();
-    }
-
-    @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        canvas.save();
-        Path intersectPath = null;
-        if (mTopAmountRounded && topAmountNeedsClipping()) {
-            int left = (int) (- mExtraWidthForClipping / 2.0f);
-            int top = (int) (mClipTopAmount - mDistanceToTopRoundness);
-            int right = getWidth() + (int) (mExtraWidthForClipping + left);
-            int bottom = (int) Math.max(mMinimumHeightForClipping,
-                    Math.max(getActualHeight() - mClipBottomAmount, top + mOutlineRadius));
-            ExpandableOutlineView.getRoundedRectPath(left, top, right, bottom, mOutlineRadius,
-                    0.0f,
-                    mClipPath);
-            intersectPath = mClipPath;
-        }
-        boolean clipped = false;
-        if (childNeedsClipping(child)) {
-            Path clipPath = getCustomClipPath(child);
-            if (clipPath == null) {
-                clipPath = getClipPath();
-            }
-            if (clipPath != null) {
-                if (intersectPath != null) {
-                    clipPath.op(intersectPath, Path.Op.INTERSECT);
-                }
-                canvas.clipPath(clipPath);
-                clipped = true;
-            }
-        }
-        if (!clipped && intersectPath != null) {
-            canvas.clipPath(intersectPath);
-        }
-        boolean result = super.drawChild(canvas, child, drawingTime);
-        canvas.restore();
-        return result;
-    }
-
-    public void setExtraWidthForClipping(float extraWidthForClipping) {
-        mExtraWidthForClipping = extraWidthForClipping;
-    }
-
-    public void setMinimumHeightForClipping(int minimumHeightForClipping) {
-        mMinimumHeightForClipping = minimumHeightForClipping;
-    }
-
-    @Override
-    public void setDistanceToTopRoundness(float distanceToTopRoundness) {
-        super.setDistanceToTopRoundness(distanceToTopRoundness);
-        if (distanceToTopRoundness != mDistanceToTopRoundness) {
-            mTopAmountRounded = distanceToTopRoundness >= 0;
-            mDistanceToTopRoundness = distanceToTopRoundness;
-            applyRoundness();
-        }
-    }
-
-    protected boolean childNeedsClipping(View child) {
-        return false;
-    }
-
-    public boolean topAmountNeedsClipping() {
-        return true;
-    }
-
-    protected boolean isClippingNeeded() {
-        return mAlwaysRoundBothCorners || mCustomOutline || getTranslation() != 0 ;
-    }
-
-    private void initDimens() {
-        Resources res = getResources();
-        mShouldTranslateContents =
-                res.getBoolean(R.bool.config_translateNotificationContentsOnSwipe);
-        mOutlineRadius = res.getDimension(R.dimen.notification_shadow_radius);
-        mAlwaysRoundBothCorners = res.getBoolean(R.bool.config_clipNotificationsToOutline);
-        if (!mAlwaysRoundBothCorners) {
-            mOutlineRadius = res.getDimensionPixelSize(
-                    Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
-        }
-        setClipToOutline(mAlwaysRoundBothCorners);
-    }
-
-    /**
-     * Set the topRoundness of this view.
-     * @return Whether the roundness was changed.
-     */
-    public boolean setTopRoundness(float topRoundness, boolean animate) {
-        if (mTopRoundness != topRoundness) {
-            mTopRoundness = topRoundness;
-            PropertyAnimator.setProperty(this, TOP_ROUNDNESS, topRoundness,
-                    ROUNDNESS_PROPERTIES, animate);
-            return true;
-        }
-        return false;
-    }
-
-    protected void applyRoundness() {
-        invalidateOutline();
-        invalidate();
-    }
-
-    public float getCurrentBackgroundRadiusTop() {
-        // If this view is top amount notification view, it should always has round corners on top.
-        // It will be applied with applyRoundness()
-        if (mTopAmountRounded) {
-            return mOutlineRadius;
-        }
-        return mCurrentTopRoundness * mOutlineRadius;
-    }
-
-    public float getCurrentTopRoundness() {
-        return mCurrentTopRoundness;
-    }
-
-    public float getCurrentBottomRoundness() {
-        return mCurrentBottomRoundness;
-    }
-
-    protected float getCurrentBackgroundRadiusBottom() {
-        return mCurrentBottomRoundness * mOutlineRadius;
-    }
-
-    /**
-     * Set the bottom roundness of this view.
-     * @return Whether the roundness was changed.
-     */
-    public boolean setBottomRoundness(float bottomRoundness, boolean animate) {
-        if (mBottomRoundness != bottomRoundness) {
-            mBottomRoundness = bottomRoundness;
-            PropertyAnimator.setProperty(this, BOTTOM_ROUNDNESS, bottomRoundness,
-                    ROUNDNESS_PROPERTIES, animate);
-            return true;
-        }
-        return false;
-    }
-
-    protected void setBackgroundTop(int backgroundTop) {
-        if (mBackgroundTop != backgroundTop) {
-            mBackgroundTop = backgroundTop;
-            invalidateOutline();
-        }
-    }
-
-    private void setTopRoundnessInternal(float topRoundness) {
-        mCurrentTopRoundness = topRoundness;
-        applyRoundness();
-    }
-
-    private void setBottomRoundnessInternal(float bottomRoundness) {
-        mCurrentBottomRoundness = bottomRoundness;
-        applyRoundness();
-    }
-
-    public void onDensityOrFontScaleChanged() {
-        initDimens();
-        applyRoundness();
-    }
-
-    @Override
-    public void setActualHeight(int actualHeight, boolean notifyListeners) {
-        int previousHeight = getActualHeight();
-        super.setActualHeight(actualHeight, notifyListeners);
-        if (previousHeight != actualHeight) {
-            applyRoundness();
-        }
-    }
-
-    @Override
-    public void setClipTopAmount(int clipTopAmount) {
-        int previousAmount = getClipTopAmount();
-        super.setClipTopAmount(clipTopAmount);
-        if (previousAmount != clipTopAmount) {
-            applyRoundness();
-        }
-    }
-
-    @Override
-    public void setClipBottomAmount(int clipBottomAmount) {
-        int previousAmount = getClipBottomAmount();
-        super.setClipBottomAmount(clipBottomAmount);
-        if (previousAmount != clipBottomAmount) {
-            applyRoundness();
-        }
-    }
-
-    protected void setOutlineAlpha(float alpha) {
-        if (alpha != mOutlineAlpha) {
-            mOutlineAlpha = alpha;
-            applyRoundness();
-        }
-    }
-
-    @Override
-    public float getOutlineAlpha() {
-        return mOutlineAlpha;
-    }
-
-    protected void setOutlineRect(RectF rect) {
-        if (rect != null) {
-            setOutlineRect(rect.left, rect.top, rect.right, rect.bottom);
-        } else {
-            mCustomOutline = false;
-            applyRoundness();
-        }
-    }
-
-    @Override
-    public int getOutlineTranslation() {
-        return mCustomOutline ? mOutlineRect.left : (int) getTranslation();
-    }
-
-    public void updateOutline() {
-        if (mCustomOutline) {
-            return;
-        }
-        boolean hasOutline = needsOutline();
-        setOutlineProvider(hasOutline ? mProvider : null);
-    }
-
-    /**
-     * @return Whether the view currently needs an outline. This is usually {@code false} in case
-     * it doesn't have a background.
-     */
-    protected boolean needsOutline() {
-        if (isChildInGroup()) {
-            return isGroupExpanded() && !isGroupExpansionChanging();
-        } else if (isSummaryWithChildren()) {
-            return !isGroupExpanded() || isGroupExpansionChanging();
-        }
-        return true;
-    }
-
-    public boolean isOutlineShowing() {
-        ViewOutlineProvider op = getOutlineProvider();
-        return op != null;
-    }
-
-    protected void setOutlineRect(float left, float top, float right, float bottom) {
-        mCustomOutline = true;
-
-        mOutlineRect.set((int) left, (int) top, (int) right, (int) bottom);
-
-        // Outlines need to be at least 1 dp
-        mOutlineRect.bottom = (int) Math.max(top, mOutlineRect.bottom);
-        mOutlineRect.right = (int) Math.max(left, mOutlineRect.right);
-        applyRoundness();
-    }
-
-    public Path getCustomClipPath(View child) {
-        return null;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
deleted file mode 100644
index ae8d844..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ /dev/null
@@ -1,581 +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
- */
-
-package com.android.systemui.statusbar;
-
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import com.android.systemui.statusbar.stack.ExpandableViewState;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.stack.StackScrollState;
-
-import java.util.ArrayList;
-
-/**
- * An abstract view for expandable views.
- */
-public abstract class ExpandableView extends FrameLayout {
-
-    public static final float NO_ROUNDNESS = -1;
-    protected OnHeightChangedListener mOnHeightChangedListener;
-    private int mActualHeight;
-    protected int mClipTopAmount;
-    protected int mClipBottomAmount;
-    private boolean mDark;
-    private ArrayList<View> mMatchParentViews = new ArrayList<View>();
-    private static Rect mClipRect = new Rect();
-    private boolean mWillBeGone;
-    private int mMinClipTopAmount = 0;
-    private boolean mClipToActualHeight = true;
-    private boolean mChangingPosition = false;
-    private ViewGroup mTransientContainer;
-    private boolean mInShelf;
-    private boolean mTransformingInShelf;
-
-    public ExpandableView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int givenSize = MeasureSpec.getSize(heightMeasureSpec);
-        final int viewHorizontalPadding = getPaddingStart() + getPaddingEnd();
-        int ownMaxHeight = Integer.MAX_VALUE;
-        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        if (heightMode != MeasureSpec.UNSPECIFIED && givenSize != 0) {
-            ownMaxHeight = Math.min(givenSize, ownMaxHeight);
-        }
-        int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
-        int maxChildHeight = 0;
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child.getVisibility() == GONE) {
-                continue;
-            }
-            int childHeightSpec = newHeightSpec;
-            ViewGroup.LayoutParams layoutParams = child.getLayoutParams();
-            if (layoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) {
-                if (layoutParams.height >= 0) {
-                    // An actual height is set
-                    childHeightSpec = layoutParams.height > ownMaxHeight
-                        ? MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.EXACTLY)
-                        : MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
-                }
-                child.measure(getChildMeasureSpec(
-                        widthMeasureSpec, viewHorizontalPadding, layoutParams.width),
-                        childHeightSpec);
-                int childHeight = child.getMeasuredHeight();
-                maxChildHeight = Math.max(maxChildHeight, childHeight);
-            } else {
-                mMatchParentViews.add(child);
-            }
-        }
-        int ownHeight = heightMode == MeasureSpec.EXACTLY
-                ? givenSize : Math.min(ownMaxHeight, maxChildHeight);
-        newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY);
-        for (View child : mMatchParentViews) {
-            child.measure(getChildMeasureSpec(
-                    widthMeasureSpec, viewHorizontalPadding, child.getLayoutParams().width),
-                    newHeightSpec);
-        }
-        mMatchParentViews.clear();
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        setMeasuredDimension(width, ownHeight);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        updateClipping();
-    }
-
-    @Override
-    public boolean pointInView(float localX, float localY, float slop) {
-        float top = mClipTopAmount;
-        float bottom = mActualHeight;
-        return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
-                localY < (bottom + slop);
-    }
-
-    /**
-     * Sets the actual height of this notification. This is different than the laid out
-     * {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding.
-     *
-     * @param actualHeight The height of this notification.
-     * @param notifyListeners Whether the listener should be informed about the change.
-     */
-    public void setActualHeight(int actualHeight, boolean notifyListeners) {
-        mActualHeight = actualHeight;
-        updateClipping();
-        if (notifyListeners) {
-            notifyHeightChanged(false  /* needsAnimation */);
-        }
-    }
-
-    /**
-     * Set the distance to the top roundness, from where we should start clipping a value above
-     * or equal to 0 is the effective distance, and if a value below 0 is received, there should
-     * be no clipping.
-     */
-    public void setDistanceToTopRoundness(float distanceToTopRoundness) {
-    }
-
-    public void setActualHeight(int actualHeight) {
-        setActualHeight(actualHeight, true /* notifyListeners */);
-    }
-
-    /**
-     * See {@link #setActualHeight}.
-     *
-     * @return The current actual height of this notification.
-     */
-    public int getActualHeight() {
-        return mActualHeight;
-    }
-
-    public boolean isExpandAnimationRunning() {
-        return false;
-    }
-
-    /**
-     * @return The maximum height of this notification.
-     */
-    public int getMaxContentHeight() {
-        return getHeight();
-    }
-
-    /**
-     * @return The minimum content height of this notification. This also respects the temporary
-     * states of the view.
-     */
-    public int getMinHeight() {
-        return getMinHeight(false /* ignoreTemporaryStates */);
-    }
-
-    /**
-     * Get the minimum height of this view.
-     *
-     * @param ignoreTemporaryStates should temporary states be ignored like the guts or heads-up.
-     *
-     * @return The minimum height that this view needs.
-     */
-    public int getMinHeight(boolean ignoreTemporaryStates) {
-        return getHeight();
-    }
-
-    /**
-     * @return The collapsed height of this view. Note that this might be different
-     * than {@link #getMinHeight()} because some elements like groups may have different sizes when
-     * they are system expanded.
-     */
-    public int getCollapsedHeight() {
-        return getHeight();
-    }
-
-    /**
-     * Sets the notification as dimmed. The default implementation does nothing.
-     *
-     * @param dimmed Whether the notification should be dimmed.
-     * @param fade Whether an animation should be played to change the state.
-     */
-    public void setDimmed(boolean dimmed, boolean fade) {
-    }
-
-    /**
-     * Sets the notification as dark. The default implementation does nothing.
-     *
-     * @param dark Whether the notification should be dark.
-     * @param fade Whether an animation should be played to change the state.
-     * @param delay If fading, the delay of the animation.
-     */
-    public void setDark(boolean dark, boolean fade, long delay) {
-        mDark = dark;
-    }
-
-    public boolean isDark() {
-        return mDark;
-    }
-
-    public boolean isRemoved() {
-        return false;
-    }
-
-    /**
-     * See {@link #setHideSensitive}. This is a variant which notifies this view in advance about
-     * the upcoming state of hiding sensitive notifications. It gets called at the very beginning
-     * of a stack scroller update such that the updated intrinsic height (which is dependent on
-     * whether private or public layout is showing) gets taken into account into all layout
-     * calculations.
-     */
-    public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
-    }
-
-    /**
-     * Sets whether the notification should hide its private contents if it is sensitive.
-     */
-    public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
-            long duration) {
-    }
-
-    /**
-     * @return The desired notification height.
-     */
-    public int getIntrinsicHeight() {
-        return getHeight();
-    }
-
-    /**
-     * Sets the amount this view should be clipped from the top. This is used when an expanded
-     * notification is scrolling in the top or bottom stack.
-     *
-     * @param clipTopAmount The amount of pixels this view should be clipped from top.
-     */
-    public void setClipTopAmount(int clipTopAmount) {
-        mClipTopAmount = clipTopAmount;
-        updateClipping();
-    }
-
-    /**
-     * Set the amount the the notification is clipped on the bottom in addition to the regular
-     * clipping. This is mainly used to clip something in a non-animated way without changing the
-     * actual height of the notification and is purely visual.
-     *
-     * @param clipBottomAmount the amount to clip.
-     */
-    public void setClipBottomAmount(int clipBottomAmount) {
-        mClipBottomAmount = clipBottomAmount;
-        updateClipping();
-    }
-
-    public int getClipTopAmount() {
-        return mClipTopAmount;
-    }
-
-    public int getClipBottomAmount() {
-        return mClipBottomAmount;
-    }
-
-    public void setOnHeightChangedListener(OnHeightChangedListener listener) {
-        mOnHeightChangedListener = listener;
-    }
-
-    /**
-     * @return Whether we can expand this views content.
-     */
-    public boolean isContentExpandable() {
-        return false;
-    }
-
-    public void notifyHeightChanged(boolean needsAnimation) {
-        if (mOnHeightChangedListener != null) {
-            mOnHeightChangedListener.onHeightChanged(this, needsAnimation);
-        }
-    }
-
-    public boolean isTransparent() {
-        return false;
-    }
-
-    /**
-     * Perform a remove animation on this view.
-     * @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.
-     * @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.
-     */
-    public abstract void performRemoveAnimation(long duration,
-            long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
-            Runnable onFinishedRunnable,
-            AnimatorListenerAdapter animationListener);
-
-    public abstract void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear);
-
-    /**
-     * Set the notification appearance to be below the speed bump.
-     * @param below true if it is below.
-     */
-    public void setBelowSpeedBump(boolean below) {
-    }
-
-    public int getPinnedHeadsUpHeight() {
-        return getIntrinsicHeight();
-    }
-
-
-    /**
-     * Sets the translation of the view.
-     */
-    public void setTranslation(float translation) {
-        setTranslationX(translation);
-    }
-
-    /**
-     * Gets the translation of the view.
-     */
-    public float getTranslation() {
-        return getTranslationX();
-    }
-
-    public void onHeightReset() {
-        if (mOnHeightChangedListener != null) {
-            mOnHeightChangedListener.onReset(this);
-        }
-    }
-
-    /**
-     * This method returns the drawing rect for the view which is different from the regular
-     * drawing rect, since we layout all children in the {@link NotificationStackScrollLayout} at
-     * position 0 and usually the translation is neglected. Since we are manually clipping this
-     * view,we also need to subtract the clipTopAmount from the top. This is needed in order to
-     * ensure that accessibility and focusing work correctly.
-     *
-     * @param outRect The (scrolled) drawing bounds of the view.
-     */
-    @Override
-    public void getDrawingRect(Rect outRect) {
-        super.getDrawingRect(outRect);
-        outRect.left += getTranslationX();
-        outRect.right += getTranslationX();
-        outRect.bottom = (int) (outRect.top + getTranslationY() + getActualHeight());
-        outRect.top += getTranslationY() + getClipTopAmount();
-    }
-
-    @Override
-    public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
-        super.getBoundsOnScreen(outRect, clipToParent);
-        if (getTop() + getTranslationY() < 0) {
-            // We got clipped to the parent here - make sure we undo that.
-            outRect.top += getTop() + getTranslationY();
-        }
-        outRect.bottom = outRect.top + getActualHeight();
-        outRect.top += getClipTopAmount();
-    }
-
-    public boolean isSummaryWithChildren() {
-        return false;
-    }
-
-    public boolean areChildrenExpanded() {
-        return false;
-    }
-
-    protected void updateClipping() {
-        if (mClipToActualHeight && shouldClipToActualHeight()) {
-            int top = getClipTopAmount();
-            mClipRect.set(0, top, getWidth(), Math.max(getActualHeight() + getExtraBottomPadding()
-                    - mClipBottomAmount, top));
-            setClipBounds(mClipRect);
-        } else {
-            setClipBounds(null);
-        }
-    }
-
-    public float getHeaderVisibleAmount() {
-        return 1.0f;
-    }
-
-    protected boolean shouldClipToActualHeight() {
-        return true;
-    }
-
-    public void setClipToActualHeight(boolean clipToActualHeight) {
-        mClipToActualHeight = clipToActualHeight;
-        updateClipping();
-    }
-
-    public boolean willBeGone() {
-        return mWillBeGone;
-    }
-
-    public void setWillBeGone(boolean willBeGone) {
-        mWillBeGone = willBeGone;
-    }
-
-    public int getMinClipTopAmount() {
-        return mMinClipTopAmount;
-    }
-
-    public void setMinClipTopAmount(int minClipTopAmount) {
-        mMinClipTopAmount = minClipTopAmount;
-    }
-
-    @Override
-    public void setLayerType(int layerType, Paint paint) {
-        if (hasOverlappingRendering()) {
-            super.setLayerType(layerType, paint);
-        }
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        // Otherwise it will be clipped
-        return super.hasOverlappingRendering() && getActualHeight() <= getHeight();
-    }
-
-    public float getShadowAlpha() {
-        return 0.0f;
-    }
-
-    public void setShadowAlpha(float shadowAlpha) {
-    }
-
-    /**
-     * @return an amount between -1 and 1 of increased padding that this child needs. 1 means it
-     * needs a full increased padding while -1 means it needs no padding at all. For 0.0f the normal
-     * padding is applied.
-     */
-    public float getIncreasedPaddingAmount() {
-        return 0.0f;
-    }
-
-    public boolean mustStayOnScreen() {
-        return false;
-    }
-
-    public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
-            int outlineTranslation) {
-    }
-
-    public float getOutlineAlpha() {
-        return 0.0f;
-    }
-
-    public int getOutlineTranslation() {
-        return 0;
-    }
-
-    public void setChangingPosition(boolean changingPosition) {
-        mChangingPosition = changingPosition;
-    }
-
-    public boolean isChangingPosition() {
-        return mChangingPosition;
-    }
-
-    public void setTransientContainer(ViewGroup transientContainer) {
-        mTransientContainer = transientContainer;
-    }
-
-    public ViewGroup getTransientContainer() {
-        return mTransientContainer;
-    }
-
-    /**
-     * @return padding used to alter how much of the view is clipped.
-     */
-    public int getExtraBottomPadding() {
-        return 0;
-    }
-
-    /**
-     * @return true if the group's expansion state is changing, false otherwise.
-     */
-    public boolean isGroupExpansionChanging() {
-        return false;
-    }
-
-    public boolean isGroupExpanded() {
-        return false;
-    }
-
-    public void setHeadsUpIsVisible() {
-    }
-
-    public boolean isChildInGroup() {
-        return false;
-    }
-
-    public void setActualHeightAnimating(boolean animating) {}
-
-    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
-        return new ExpandableViewState();
-    }
-
-    /**
-     * @return whether the current view doesn't add height to the overall content. This means that
-     * if it is added to a list of items, it's content will still have the same height.
-     * An example is the notification shelf, that is always placed on top of another view.
-     */
-    public boolean hasNoContentHeight() {
-        return false;
-    }
-
-    /**
-     * @param inShelf whether the view is currently fully in the notification shelf.
-     */
-    public void setInShelf(boolean inShelf) {
-        mInShelf = inShelf;
-    }
-
-    public boolean isInShelf() {
-        return mInShelf;
-    }
-
-    /**
-     * @param transformingInShelf whether the view is currently transforming into the shelf in an
-     *                            animated way
-     */
-    public void setTransformingInShelf(boolean transformingInShelf) {
-        mTransformingInShelf = transformingInShelf;
-    }
-
-    public boolean isTransformingIntoShelf() {
-        return mTransformingInShelf;
-    }
-
-    public boolean isAboveShelf() {
-        return false;
-    }
-
-    public boolean hasExpandingChild() {
-        return false;
-    }
-
-    /**
-     * A listener notifying when {@link #getActualHeight} changes.
-     */
-    public interface OnHeightChangedListener {
-
-        /**
-         * @param view the view for which the height changed, or {@code null} if just the top
-         *             padding or the padding between the elements changed
-         * @param needsAnimation whether the view height needs to be animated
-         */
-        void onHeightChanged(ExpandableView view, boolean needsAnimation);
-
-        /**
-         * Called when the view is reset and therefore the height will change abruptly
-         *
-         * @param view The view which was reset.
-         */
-        void onReset(ExpandableView view);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java
deleted file mode 100644
index dc5bb9a..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java
+++ /dev/null
@@ -1,105 +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
- */
-
-package com.android.systemui.statusbar;
-
-import android.annotation.ColorInt;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.stack.ExpandableViewState;
-import com.android.systemui.statusbar.stack.StackScrollState;
-
-public class FooterView extends StackScrollerDecorView {
-    private final int mClearAllTopPadding;
-    private FooterViewButton mDismissButton;
-    private FooterViewButton mManageButton;
-
-    public FooterView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mClearAllTopPadding = context.getResources().getDimensionPixelSize(
-                R.dimen.clear_all_padding_top);
-    }
-
-    @Override
-    protected View findContentView() {
-        return findViewById(R.id.content);
-    }
-
-    protected View findSecondaryView() {
-        return findViewById(R.id.dismiss_text);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mDismissButton = (FooterViewButton) findSecondaryView();
-        mManageButton = findViewById(R.id.manage_text);
-    }
-
-    public void setTextColor(@ColorInt int color) {
-        mManageButton.setTextColor(color);
-        mDismissButton.setTextColor(color);
-    }
-
-    public void setManageButtonClickListener(OnClickListener listener) {
-        mManageButton.setOnClickListener(listener);
-    }
-
-    public void setDismissButtonClickListener(OnClickListener listener) {
-        mDismissButton.setOnClickListener(listener);
-    }
-
-    public boolean isOnEmptySpace(float touchX, float touchY) {
-        return touchX < mContent.getX()
-                || touchX > mContent.getX() + mContent.getWidth()
-                || touchY < mContent.getY()
-                || touchY > mContent.getY() + mContent.getHeight();
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        mDismissButton.setText(R.string.clear_all_notifications_text);
-        mDismissButton.setContentDescription(
-                mContext.getString(R.string.accessibility_clear_all));
-        mManageButton.setText(R.string.manage_notifications_text);
-    }
-
-    public boolean isButtonVisible() {
-        return mManageButton.getAlpha() != 0.0f;
-    }
-
-    @Override
-    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
-        return new FooterViewState();
-    }
-
-    public class FooterViewState extends ExpandableViewState {
-        @Override
-        public void applyToView(View view) {
-            super.applyToView(view);
-            if (view instanceof FooterView) {
-                FooterView footerView = (FooterView) view;
-                boolean visible = this.clipTopAmount < mClearAllTopPadding;
-                footerView.setContentVisible(visible && footerView.isVisible());
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FooterViewButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/FooterViewButton.java
deleted file mode 100644
index 16ca0f2..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FooterViewButton.java
+++ /dev/null
@@ -1,63 +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
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.ViewGroup;
-
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-
-public class FooterViewButton extends AlphaOptimizedButton {
-
-    public FooterViewButton(Context context) {
-        this(context, null);
-    }
-
-    public FooterViewButton(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public FooterViewButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public FooterViewButton(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    /**
-     * This method returns the drawing rect for the view which is different from the regular
-     * drawing rect, since we layout all children in the {@link NotificationStackScrollLayout} at
-     * position 0 and usually the translation is neglected. The standard implementation doesn't
-     * account for translation.
-     *
-     * @param outRect The (scrolled) drawing bounds of the view.
-     */
-    @Override
-    public void getDrawingRect(Rect outRect) {
-        super.getDrawingRect(outRect);
-        float translationX = ((ViewGroup) mParent).getTranslationX();
-        float translationY = ((ViewGroup) mParent).getTranslationY();
-        outRect.left += translationX;
-        outRect.right += translationX;
-        outRect.top += translationY;
-        outRect.bottom += translationY;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index 39485c3..b39a96d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -21,8 +21,9 @@
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Parcelable;
 import android.util.AttributeSet;
-import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.View;
 import android.widget.TextView;
@@ -30,6 +31,7 @@
 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 java.util.List;
@@ -38,6 +40,12 @@
  * The view in the statusBar that contains part of the heads-up information
  */
 public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
+    private static final String HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE =
+            "heads_up_status_bar_view_super_parcelable";
+    private static final String FIRST_LAYOUT = "first_layout";
+    private static final String PUBLIC_MODE = "public_mode";
+    private static final String VISIBILITY = "visibility";
+    private static final String ALPHA = "alpha";
     private int mAbsoluteStartPadding;
     private int mEndMargin;
     private View mIconPlaceholder;
@@ -107,6 +115,39 @@
         updateMaxWidth();
     }
 
+    @Override
+    public Bundle onSaveInstanceState() {
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE,
+                super.onSaveInstanceState());
+        bundle.putBoolean(FIRST_LAYOUT, mFirstLayout);
+        bundle.putBoolean(PUBLIC_MODE, mPublicMode);
+        bundle.putInt(VISIBILITY, getVisibility());
+        bundle.putFloat(ALPHA, getAlpha());
+
+        return bundle;
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        if (state == null || !(state instanceof Bundle)) {
+            super.onRestoreInstanceState(state);
+            return;
+        }
+
+        Bundle bundle = (Bundle) state;
+        Parcelable superState = bundle.getParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE);
+        super.onRestoreInstanceState(superState);
+        mFirstLayout = bundle.getBoolean(FIRST_LAYOUT, true);
+        mPublicMode = bundle.getBoolean(PUBLIC_MODE, false);
+        if (bundle.containsKey(VISIBILITY)) {
+            setVisibility(bundle.getInt(VISIBILITY));
+        }
+        if (bundle.containsKey(ALPHA)) {
+            setAlpha(bundle.getFloat(ALPHA));
+        }
+    }
+
     @VisibleForTesting
     public HeadsUpStatusBarView(Context context, View iconPlaceholder, TextView textView) {
         this(context);
@@ -173,16 +214,12 @@
     }
 
     /** In order to do UI alignment, this view will be notified by
-     * {@link com.android.systemui.statusbar.stack.NotificationStackScrollLayout}.
+     * {@link com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout}.
      * After scroller laid out, the scroller will tell this view about scroller's getX()
      * @param translationX how to translate the horizontal position
      */
     public void setPanelTranslation(float translationX) {
-        if (isLayoutRtl()) {
-            setTranslationX(translationX + mCutOutInset);
-        } else {
-            setTranslationX(translationX - mCutOutInset);
-        }
+        setTranslationX(translationX);
         updateDrawingRect();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 35e1511..551e8a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.app.admin.DevicePolicyManager;
+import android.hardware.biometrics.BiometricSourceType;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -26,6 +27,7 @@
 import android.content.res.Resources;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
+import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
@@ -70,8 +72,8 @@
     private static final boolean DEBUG_CHARGING_SPEED = false;
 
     private static final int MSG_HIDE_TRANSIENT = 1;
-    private static final int MSG_CLEAR_FP_MSG = 2;
-    private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
+    private static final int MSG_CLEAR_BIOMETRIC_MSG = 2;
+    private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
 
     private final Context mContext;
     private ViewGroup mIndicationArea;
@@ -362,22 +364,41 @@
                 R.integer.wired_charging_keyguard_text_animation_duration_up);
         int animateDownDuration = mContext.getResources().getInteger(
                 R.integer.wired_charging_keyguard_text_animation_duration_down);
+        textView.animate().cancel();
+        float translation = textView.getTranslationY();
         textView.animate()
                 .translationYBy(yTranslation)
                 .setInterpolator(Interpolators.LINEAR)
                 .setDuration(animateUpDuration)
                 .setListener(new AnimatorListenerAdapter() {
+                    private boolean mCancelled;
+
                     @Override
                     public void onAnimationStart(Animator animation) {
                         textView.switchIndication(indication);
                     }
+
+                    @Override
+                    public void onAnimationCancel(Animator animation) {
+                        textView.setTranslationY(translation);
+                        mCancelled = true;
+                    }
+
                     @Override
                     public void onAnimationEnd(Animator animation) {
+                        if (mCancelled) {
+                            return;
+                        }
                         textView.animate()
                                 .setDuration(animateDownDuration)
                                 .setInterpolator(Interpolators.BOUNCE)
-                                .translationYBy(-1 * yTranslation)
-                                .setListener(null);
+                                .translationY(translation)
+                                .setListener(new AnimatorListenerAdapter() {
+                                    @Override
+                                    public void onAnimationCancel(Animator animation) {
+                                        textView.setTranslationY(translation);
+                                    }
+                                });
                     }
                 });
     }
@@ -461,7 +482,7 @@
         public void handleMessage(Message msg) {
             if (msg.what == MSG_HIDE_TRANSIENT) {
                 hideTransientIndication();
-            } else if (msg.what == MSG_CLEAR_FP_MSG) {
+            } else if (msg.what == MSG_CLEAR_BIOMETRIC_MSG) {
                 mLockIcon.setTransientFpError(false);
             }
         }
@@ -526,9 +547,10 @@
         }
 
         @Override
-        public void onFingerprintHelp(int msgId, String helpString) {
+        public void onBiometricHelp(int msgId, String helpString,
+                BiometricSourceType biometricSourceType) {
             KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
-            if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
+            if (!updateMonitor.isUnlockingWithBiometricAllowed()) {
                 return;
             }
             ColorStateList errorColorState = Utils.getColorError(mContext);
@@ -538,10 +560,10 @@
             } else if (updateMonitor.isScreenOn()) {
                 mLockIcon.setTransientFpError(true);
                 showTransientIndication(helpString, errorColorState);
-                hideTransientIndicationDelayed(TRANSIENT_FP_ERROR_TIMEOUT);
-                mHandler.removeMessages(MSG_CLEAR_FP_MSG);
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
-                        TRANSIENT_FP_ERROR_TIMEOUT);
+                hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
+                mHandler.removeMessages(MSG_CLEAR_BIOMETRIC_MSG);
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_BIOMETRIC_MSG),
+                        TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
             }
             // Help messages indicate that there was actually a try since the last error, so those
             // are not two successive error messages anymore.
@@ -549,16 +571,15 @@
         }
 
         @Override
-        public void onFingerprintError(int msgId, String errString) {
+        public void onBiometricError(int msgId, String errString,
+                BiometricSourceType biometricSourceType) {
             KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
-            if ((!updateMonitor.isUnlockingWithFingerprintAllowed()
-                    && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
-                    || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
+            if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) {
                 return;
             }
             ColorStateList errorColorState = Utils.getColorError(mContext);
             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
-                // When swiping up right after receiving a fingerprint error, the bouncer calls
+                // When swiping up right after receiving a biometric error, the bouncer calls
                 // authenticate leading to the same message being shown again on the bouncer.
                 // We want to avoid this, as it may confuse the user when the message is too
                 // generic.
@@ -576,6 +597,28 @@
             mLastSuccessiveErrorMessage = msgId;
         }
 
+        private boolean shouldSuppressBiometricError(int msgId,
+                BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
+            if (biometricSourceType == BiometricSourceType.FINGERPRINT)
+                return shouldSuppressFingerprintError(msgId, updateMonitor);
+            if (biometricSourceType == BiometricSourceType.FACE)
+                return shouldSuppressFaceError(msgId, updateMonitor);
+            return false;
+        }
+
+        private boolean shouldSuppressFingerprintError(int msgId,
+                KeyguardUpdateMonitor updateMonitor) {
+            return ((!updateMonitor.isUnlockingWithBiometricAllowed()
+                    && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
+                    || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        }
+
+        private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
+            return ((!updateMonitor.isUnlockingWithBiometricAllowed()
+                    && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
+                    || msgId == FaceManager.FACE_ERROR_CANCELED);
+        }
+
         @Override
         public void onTrustAgentErrorMessage(CharSequence message) {
             showTransientIndication(message, Utils.getColorError(mContext));
@@ -592,21 +635,22 @@
         }
 
         @Override
-        public void onFingerprintRunningStateChanged(boolean running) {
+        public void onBiometricRunningStateChanged(boolean running,
+                BiometricSourceType biometricSourceType) {
             if (running) {
                 mMessageToShowOnScreenOn = null;
             }
         }
 
         @Override
-        public void onFingerprintAuthenticated(int userId) {
-            super.onFingerprintAuthenticated(userId);
+        public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+            super.onBiometricAuthenticated(userId, biometricSourceType);
             mLastSuccessiveErrorMessage = -1;
         }
 
         @Override
-        public void onFingerprintAuthFailed() {
-            super.onFingerprintAuthFailed();
+        public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
+            super.onBiometricAuthFailed(biometricSourceType);
             mLastSuccessiveErrorMessage = -1;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
deleted file mode 100644
index 969e9d9..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ /dev/null
@@ -1,289 +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
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.RippleDrawable;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.internal.util.ArrayUtils;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-
-/**
- * A view that can be used for both the dimmed and normal background of an notification.
- */
-public class NotificationBackgroundView extends View {
-
-    private final boolean mDontModifyCorners;
-    private Drawable mBackground;
-    private int mClipTopAmount;
-    private int mActualHeight;
-    private int mClipBottomAmount;
-    private int mTintColor;
-    private float[] mCornerRadii = new float[8];
-    private boolean mBottomIsRounded;
-    private int mBackgroundTop;
-    private boolean mBottomAmountClips = true;
-    private boolean mExpandAnimationRunning;
-    private float mActualWidth;
-    private int mDrawableAlpha = 255;
-    private boolean mIsPressedAllowed;
-
-    private boolean mTopAmountRounded;
-    private float mDistanceToTopRoundness;
-
-    public NotificationBackgroundView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mDontModifyCorners = getResources().getBoolean(
-                R.bool.config_clipNotificationsToOutline);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        if (mClipTopAmount + mClipBottomAmount < mActualHeight - mBackgroundTop
-                || mExpandAnimationRunning) {
-            canvas.save();
-            if (!mExpandAnimationRunning) {
-                canvas.clipRect(0, mClipTopAmount, getWidth(), mActualHeight - mClipBottomAmount);
-            }
-            draw(canvas, mBackground);
-            canvas.restore();
-        }
-    }
-
-    private void draw(Canvas canvas, Drawable drawable) {
-        if (drawable != null) {
-            int top = mBackgroundTop;
-            int bottom = mActualHeight;
-            if (mBottomIsRounded && mBottomAmountClips && !mExpandAnimationRunning) {
-                bottom -= mClipBottomAmount;
-            }
-            int left = 0;
-            int right = getWidth();
-            if (mExpandAnimationRunning) {
-                left = (int) ((getWidth() - mActualWidth) / 2.0f);
-                right = (int) (left + mActualWidth);
-            }
-            if (mTopAmountRounded) {
-                int clipTop = (int) (mClipTopAmount - mDistanceToTopRoundness);
-                top += clipTop;
-                if (clipTop >= 0) {
-                    bottom += clipTop;
-                }
-            }
-            drawable.setBounds(left, top, right, bottom);
-            drawable.draw(canvas);
-        }
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return super.verifyDrawable(who) || who == mBackground;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        setState(getDrawableState());
-    }
-
-    @Override
-    public void drawableHotspotChanged(float x, float y) {
-        if (mBackground != null) {
-            mBackground.setHotspot(x, y);
-        }
-    }
-
-    /**
-     * Sets a background drawable. As we need to change our bounds independently of layout, we need
-     * the notion of a background independently of the regular View background..
-     */
-    public void setCustomBackground(Drawable background) {
-        if (mBackground != null) {
-            mBackground.setCallback(null);
-            unscheduleDrawable(mBackground);
-        }
-        mBackground = background;
-        mBackground.mutate();
-        if (mBackground != null) {
-            mBackground.setCallback(this);
-            setTint(mTintColor);
-        }
-        if (mBackground instanceof RippleDrawable) {
-            ((RippleDrawable) mBackground).setForceSoftware(true);
-        }
-        updateBackgroundRadii();
-        invalidate();
-    }
-
-    public void setCustomBackground(int drawableResId) {
-        final Drawable d = mContext.getDrawable(drawableResId);
-        setCustomBackground(d);
-    }
-
-    public void setTint(int tintColor) {
-        if (tintColor != 0) {
-            mBackground.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP);
-        } else {
-            mBackground.clearColorFilter();
-        }
-        mTintColor = tintColor;
-        invalidate();
-    }
-
-    public void setActualHeight(int actualHeight) {
-        if (mExpandAnimationRunning) {
-            return;
-        }
-        mActualHeight = actualHeight;
-        invalidate();
-    }
-
-    public int getActualHeight() {
-        return mActualHeight;
-    }
-
-    public void setClipTopAmount(int clipTopAmount) {
-        mClipTopAmount = clipTopAmount;
-        invalidate();
-    }
-
-    public void setClipBottomAmount(int clipBottomAmount) {
-        mClipBottomAmount = clipBottomAmount;
-        invalidate();
-    }
-
-    public void setDistanceToTopRoundness(float distanceToTopRoundness) {
-        if (distanceToTopRoundness != mDistanceToTopRoundness) {
-            mTopAmountRounded = distanceToTopRoundness >= 0;
-            mDistanceToTopRoundness = distanceToTopRoundness;
-            invalidate();
-        }
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-
-        // Prevents this view from creating a layer when alpha is animating.
-        return false;
-    }
-
-    public void setState(int[] drawableState) {
-        if (mBackground != null && mBackground.isStateful()) {
-            if (!mIsPressedAllowed) {
-                drawableState = ArrayUtils.removeInt(drawableState,
-                        com.android.internal.R.attr.state_pressed);
-            }
-            mBackground.setState(drawableState);
-        }
-    }
-
-    public void setRippleColor(int color) {
-        if (mBackground instanceof RippleDrawable) {
-            RippleDrawable ripple = (RippleDrawable) mBackground;
-            ripple.setColor(ColorStateList.valueOf(color));
-        }
-    }
-
-    public void setDrawableAlpha(int drawableAlpha) {
-        mDrawableAlpha = drawableAlpha;
-        if (mExpandAnimationRunning) {
-            return;
-        }
-        mBackground.setAlpha(drawableAlpha);
-    }
-
-    public void setRoundness(float topRoundness, float bottomRoundNess) {
-        if (topRoundness == mCornerRadii[0] && bottomRoundNess == mCornerRadii[4]) {
-            return;
-        }
-        mBottomIsRounded = bottomRoundNess != 0.0f;
-        mCornerRadii[0] = topRoundness;
-        mCornerRadii[1] = topRoundness;
-        mCornerRadii[2] = topRoundness;
-        mCornerRadii[3] = topRoundness;
-        mCornerRadii[4] = bottomRoundNess;
-        mCornerRadii[5] = bottomRoundNess;
-        mCornerRadii[6] = bottomRoundNess;
-        mCornerRadii[7] = bottomRoundNess;
-        updateBackgroundRadii();
-    }
-
-    public void setBottomAmountClips(boolean clips) {
-        if (clips != mBottomAmountClips) {
-            mBottomAmountClips = clips;
-            invalidate();
-        }
-    }
-
-    private void updateBackgroundRadii() {
-        if (mDontModifyCorners) {
-            return;
-        }
-        if (mBackground instanceof LayerDrawable) {
-            GradientDrawable gradientDrawable =
-                    (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
-            gradientDrawable.setCornerRadii(mCornerRadii);
-        }
-    }
-
-    public void setBackgroundTop(int backgroundTop) {
-        mBackgroundTop = backgroundTop;
-        invalidate();
-    }
-
-    public void setExpandAnimationParams(ActivityLaunchAnimator.ExpandAnimationParameters params) {
-        mActualHeight = params.getHeight();
-        mActualWidth = params.getWidth();
-        float alphaProgress = Interpolators.ALPHA_IN.getInterpolation(
-                params.getProgress(
-                        ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT /* delay */,
-                        ActivityLaunchAnimator.ANIMATION_DURATION_FADE_APP /* duration */));
-        mBackground.setAlpha((int) (mDrawableAlpha * (1.0f - alphaProgress)));
-        invalidate();
-    }
-
-    public void setExpandAnimationRunning(boolean running) {
-        mExpandAnimationRunning = running;
-        if (mBackground instanceof LayerDrawable) {
-            GradientDrawable gradientDrawable =
-                    (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
-            gradientDrawable.setXfermode(
-                    running ? new PorterDuffXfermode(PorterDuff.Mode.SRC) : null);
-            // Speed optimization: disable AA if transfer mode is not SRC_OVER. AA is not easy to
-            // spot during animation anyways.
-            gradientDrawable.setAntiAlias(!running);
-        }
-        if (!mExpandAnimationRunning) {
-            setDrawableAlpha(mDrawableAlpha);
-        }
-        invalidate();
-    }
-
-    public void setPressedAllowed(boolean allowed) {
-        mIsPressedAllowed = allowed;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java
deleted file mode 100644
index c78ab8d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java
+++ /dev/null
@@ -1,173 +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 android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
-import androidx.annotation.VisibleForTesting;
-import android.util.Log;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Dependency;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.notification.NotificationCounters;
-import com.android.systemui.statusbar.phone.StatusBar;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
-
-/**
- * Manager for the notification blocking helper - tracks and helps create the blocking helper
- * affordance.
- */
-public class NotificationBlockingHelperManager {
-    /** Enables debug logging and always makes the blocking helper show up after a dismiss. */
-    private static final boolean DEBUG = false;
-    private static final String TAG = "BlockingHelper";
-
-    private final Context mContext;
-    /** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */
-    private ExpandableNotificationRow mBlockingHelperRow;
-    private Set<String> mNonBlockablePkgs;
-
-    /**
-     * Whether the notification shade/stack is expanded - used to determine blocking helper
-     * eligibility.
-     */
-    private boolean mIsShadeExpanded;
-
-    public NotificationBlockingHelperManager(Context context) {
-        mContext = context;
-        mNonBlockablePkgs = new HashSet<>();
-        Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray(
-                com.android.internal.R.array.config_nonBlockableNotificationPackages));
-    }
-
-    /**
-     * Potentially shows the blocking helper, represented via the {@link NotificationInfo} menu
-     * item, in the current row if user sentiment is negative.
-     *
-     * @param row row to render the blocking helper in
-     * @param menuRow menu used to generate the {@link NotificationInfo} view that houses the
-     *                blocking helper UI
-     * @return whether we're showing a blocking helper in the given notification row
-     */
-    boolean perhapsShowBlockingHelper(
-            ExpandableNotificationRow row, NotificationMenuRowPlugin menuRow) {
-        // We only show the blocking helper if:
-        // - User sentiment is negative (DEBUG flag can bypass)
-        // - The notification shade is fully expanded (guarantees we're not touching a HUN).
-        // - The row is blockable (i.e. not non-blockable)
-        // - The dismissed row is a valid group (>1 or 0 children) or the only child in the group
-        if ((row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE || DEBUG)
-                && mIsShadeExpanded
-                && !row.getIsNonblockable()
-                && (!row.isChildInGroup() || row.isOnlyChildInGroup())) {
-            // Dismiss any current blocking helper before continuing forward (only one can be shown
-            // at a given time).
-            dismissCurrentBlockingHelper();
-
-            if (DEBUG) {
-                Log.d(TAG, "Manager.perhapsShowBlockingHelper: Showing new blocking helper");
-            }
-            NotificationGutsManager manager = Dependency.get(NotificationGutsManager.class);
-
-            // Enable blocking helper on the row before moving forward so everything in the guts is
-            // correctly prepped.
-            mBlockingHelperRow = row;
-            mBlockingHelperRow.setBlockingHelperShowing(true);
-
-            // We don't care about the touch origin (x, y) since we're opening guts without any
-            // explicit user interaction.
-            manager.openGuts(mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
-
-            Dependency.get(MetricsLogger.class)
-                    .count(NotificationCounters.BLOCKING_HELPER_SHOWN, 1);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Dismiss the currently showing blocking helper, if any, through a notification update.
-     *
-     * @return whether the blocking helper was dismissed
-     */
-    boolean dismissCurrentBlockingHelper() {
-        if (!isBlockingHelperRowNull()) {
-            if (DEBUG) {
-                Log.d(TAG, "Manager.dismissCurrentBlockingHelper: Dismissing current helper");
-            }
-            if (!mBlockingHelperRow.isBlockingHelperShowing()) {
-                Log.e(TAG, "Manager.dismissCurrentBlockingHelper: "
-                        + "Non-null row is not showing a blocking helper");
-            }
-
-            mBlockingHelperRow.setBlockingHelperShowing(false);
-            if (mBlockingHelperRow.isAttachedToWindow()) {
-                Dependency.get(NotificationEntryManager.class).updateNotifications();
-            }
-            mBlockingHelperRow = null;
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Update the expansion status of the notification shade/stack.
-     *
-     * @param expandedHeight how much the shade is expanded ({code 0} indicating it's collapsed)
-     */
-    public void setNotificationShadeExpanded(float expandedHeight) {
-        mIsShadeExpanded = expandedHeight > 0.0f;
-    }
-
-    /**
-     * Returns whether the given package name is in the list of non-blockable packages.
-     */
-    public boolean isNonblockable(String packageName, String channelName) {
-        return mNonBlockablePkgs.contains(packageName)
-                || mNonBlockablePkgs.contains(makeChannelKey(packageName, channelName));
-    }
-
-    // Format must stay in sync with frameworks/base/core/res/res/values/config.xml
-    // config_nonBlockableNotificationPackages
-    private String makeChannelKey(String pkg, String channel) {
-        return pkg + ":" + channel;
-    }
-
-    @VisibleForTesting
-    boolean isBlockingHelperRowNull() {
-        return mBlockingHelperRow == null;
-    }
-
-    @VisibleForTesting
-    void setBlockingHelperRowForTest(ExpandableNotificationRow blockingHelperRowForTest) {
-        mBlockingHelperRow = blockingHelperRowForTest;
-    }
-
-    @VisibleForTesting
-    void setNonBlockablePkgs(String[] pkgsAndChannels) {
-        mNonBlockablePkgs = new HashSet<>();
-        Collections.addAll(mNonBlockablePkgs, pkgsAndChannels);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
deleted file mode 100644
index a90ddf0..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ /dev/null
@@ -1,1744 +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
- */
-
-package com.android.systemui.statusbar;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.RemoteInput;
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Build;
-import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.NotificationHeaderView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.HybridGroupManager;
-import com.android.systemui.statusbar.notification.HybridNotificationView;
-import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
-import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.policy.RemoteInputView;
-import com.android.systemui.statusbar.policy.SmartReplyConstants;
-import com.android.systemui.statusbar.policy.SmartReplyView;
-
-/**
- * A frame layout containing the actual payload of the notification, including the contracted,
- * expanded and heads up layout. This class is responsible for clipping the content and and
- * switching between the expanded, contracted and the heads up view depending on its clipped size.
- */
-public class NotificationContentView extends FrameLayout {
-
-    private static final String TAG = "NotificationContentView";
-    public static final int VISIBLE_TYPE_CONTRACTED = 0;
-    public static final int VISIBLE_TYPE_EXPANDED = 1;
-    public static final int VISIBLE_TYPE_HEADSUP = 2;
-    private static final int VISIBLE_TYPE_SINGLELINE = 3;
-    public static final int VISIBLE_TYPE_AMBIENT = 4;
-    private static final int VISIBLE_TYPE_AMBIENT_SINGLELINE = 5;
-    public static final int UNDEFINED = -1;
-
-    private final Rect mClipBounds = new Rect();
-
-    private int mMinContractedHeight;
-    private int mNotificationContentMarginEnd;
-    private View mContractedChild;
-    private View mExpandedChild;
-    private View mHeadsUpChild;
-    private HybridNotificationView mSingleLineView;
-    private View mAmbientChild;
-    private HybridNotificationView mAmbientSingleLineChild;
-
-    private RemoteInputView mExpandedRemoteInput;
-    private RemoteInputView mHeadsUpRemoteInput;
-
-    private SmartReplyConstants mSmartReplyConstants;
-    private SmartReplyView mExpandedSmartReplyView;
-    private SmartReplyController mSmartReplyController;
-
-    private NotificationViewWrapper mContractedWrapper;
-    private NotificationViewWrapper mExpandedWrapper;
-    private NotificationViewWrapper mHeadsUpWrapper;
-    private NotificationViewWrapper mAmbientWrapper;
-    private HybridGroupManager mHybridGroupManager;
-    private int mClipTopAmount;
-    private int mContentHeight;
-    private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
-    private boolean mDark;
-    private boolean mAnimate;
-    private boolean mIsHeadsUp;
-    private boolean mLegacy;
-    private boolean mIsChildInGroup;
-    private int mSmallHeight;
-    private int mHeadsUpHeight;
-    private int mNotificationMaxHeight;
-    private int mNotificationAmbientHeight;
-    private StatusBarNotification mStatusBarNotification;
-    private NotificationGroupManager mGroupManager;
-    private RemoteInputController mRemoteInputController;
-    private Runnable mExpandedVisibleListener;
-
-    private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
-            = new ViewTreeObserver.OnPreDrawListener() {
-        @Override
-        public boolean onPreDraw() {
-            // We need to post since we don't want the notification to animate on the very first
-            // frame
-            post(new Runnable() {
-                @Override
-                public void run() {
-                    mAnimate = true;
-                }
-            });
-            getViewTreeObserver().removeOnPreDrawListener(this);
-            return true;
-        }
-    };
-
-    private OnClickListener mExpandClickListener;
-    private boolean mBeforeN;
-    private boolean mExpandable;
-    private boolean mClipToActualHeight = true;
-    private ExpandableNotificationRow mContainingNotification;
-    /** The visible type at the start of a touch driven transformation */
-    private int mTransformationStartVisibleType;
-    /** The visible type at the start of an animation driven transformation */
-    private int mAnimationStartVisibleType = UNDEFINED;
-    private boolean mUserExpanding;
-    private int mSingleLineWidthIndention;
-    private boolean mForceSelectNextLayout = true;
-    private PendingIntent mPreviousExpandedRemoteInputIntent;
-    private PendingIntent mPreviousHeadsUpRemoteInputIntent;
-    private RemoteInputView mCachedExpandedRemoteInput;
-    private RemoteInputView mCachedHeadsUpRemoteInput;
-
-    private int mContentHeightAtAnimationStart = UNDEFINED;
-    private boolean mFocusOnVisibilityChange;
-    private boolean mHeadsUpAnimatingAway;
-    private boolean mIconsVisible;
-    private int mClipBottomAmount;
-    private boolean mIsLowPriority;
-    private boolean mIsContentExpandable;
-    private boolean mRemoteInputVisible;
-    private int mUnrestrictedContentHeight;
-
-
-    public NotificationContentView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mHybridGroupManager = new HybridGroupManager(getContext(), this);
-        mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
-        mSmartReplyController = Dependency.get(SmartReplyController.class);
-        initView();
-    }
-
-    public void initView() {
-        mMinContractedHeight = getResources().getDimensionPixelSize(
-                R.dimen.min_notification_layout_height);
-        mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.notification_content_margin_end);
-    }
-
-    public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
-            int ambientHeight) {
-        mSmallHeight = smallHeight;
-        mHeadsUpHeight = headsUpMaxHeight;
-        mNotificationMaxHeight = maxHeight;
-        mNotificationAmbientHeight = ambientHeight;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
-        boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
-        int maxSize = Integer.MAX_VALUE / 2;
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        if (hasFixedHeight || isHeightLimited) {
-            maxSize = MeasureSpec.getSize(heightMeasureSpec);
-        }
-        int maxChildHeight = 0;
-        if (mExpandedChild != null) {
-            int notificationMaxHeight = mNotificationMaxHeight;
-            if (mExpandedSmartReplyView != null) {
-                notificationMaxHeight += mExpandedSmartReplyView.getHeightUpperLimit();
-            }
-            notificationMaxHeight += mExpandedWrapper.getExtraMeasureHeight();
-            int size = notificationMaxHeight;
-            ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
-            boolean useExactly = false;
-            if (layoutParams.height >= 0) {
-                // An actual height is set
-                size = Math.min(size, layoutParams.height);
-                useExactly = true;
-            }
-            int spec = MeasureSpec.makeMeasureSpec(size, useExactly
-                            ? MeasureSpec.EXACTLY
-                            : MeasureSpec.AT_MOST);
-            measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, spec, 0);
-            maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
-        }
-        if (mContractedChild != null) {
-            int heightSpec;
-            int size = mSmallHeight;
-            ViewGroup.LayoutParams layoutParams = mContractedChild.getLayoutParams();
-            boolean useExactly = false;
-            if (layoutParams.height >= 0) {
-                // An actual height is set
-                size = Math.min(size, layoutParams.height);
-                useExactly = true;
-            }
-            if (shouldContractedBeFixedSize() || useExactly) {
-                heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
-            } else {
-                heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
-            }
-            measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
-            int measuredHeight = mContractedChild.getMeasuredHeight();
-            if (measuredHeight < mMinContractedHeight) {
-                heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
-                measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
-            }
-            maxChildHeight = Math.max(maxChildHeight, measuredHeight);
-            if (updateContractedHeaderWidth()) {
-                measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
-            }
-            if (mExpandedChild != null
-                    && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
-                // the Expanded child is smaller then the collapsed. Let's remeasure it.
-                heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
-                        MeasureSpec.EXACTLY);
-                measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, heightSpec, 0);
-            }
-        }
-        if (mHeadsUpChild != null) {
-            int maxHeight = mHeadsUpHeight;
-            maxHeight += mHeadsUpWrapper.getExtraMeasureHeight();
-            int size = maxHeight;
-            ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
-            boolean useExactly = false;
-            if (layoutParams.height >= 0) {
-                // An actual height is set
-                size = Math.min(size, layoutParams.height);
-                useExactly = true;
-            }
-            measureChildWithMargins(mHeadsUpChild, widthMeasureSpec, 0,
-                    MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
-                            : MeasureSpec.AT_MOST), 0);
-            maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
-        }
-        if (mSingleLineView != null) {
-            int singleLineWidthSpec = widthMeasureSpec;
-            if (mSingleLineWidthIndention != 0
-                    && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
-                singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
-                        width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
-                        MeasureSpec.EXACTLY);
-            }
-            mSingleLineView.measure(singleLineWidthSpec,
-                    MeasureSpec.makeMeasureSpec(mNotificationMaxHeight, MeasureSpec.AT_MOST));
-            maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
-        }
-        if (mAmbientChild != null) {
-            int size = mNotificationAmbientHeight;
-            ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
-            boolean useExactly = false;
-            if (layoutParams.height >= 0) {
-                // An actual height is set
-                size = Math.min(size, layoutParams.height);
-                useExactly = true;
-            }
-            mAmbientChild.measure(widthMeasureSpec,
-                    MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
-                            : MeasureSpec.AT_MOST));
-            maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
-        }
-        if (mAmbientSingleLineChild != null) {
-            int size = mNotificationAmbientHeight;
-            ViewGroup.LayoutParams layoutParams = mAmbientSingleLineChild.getLayoutParams();
-            boolean useExactly = false;
-            if (layoutParams.height >= 0) {
-                // An actual height is set
-                size = Math.min(size, layoutParams.height);
-                useExactly = true;
-            }
-            int ambientSingleLineWidthSpec = widthMeasureSpec;
-            if (mSingleLineWidthIndention != 0
-                    && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
-                ambientSingleLineWidthSpec = MeasureSpec.makeMeasureSpec(
-                        width - mSingleLineWidthIndention + mAmbientSingleLineChild.getPaddingEnd(),
-                        MeasureSpec.EXACTLY);
-            }
-            mAmbientSingleLineChild.measure(ambientSingleLineWidthSpec,
-                    MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
-                            : MeasureSpec.AT_MOST));
-            maxChildHeight = Math.max(maxChildHeight, mAmbientSingleLineChild.getMeasuredHeight());
-        }
-        int ownHeight = Math.min(maxChildHeight, maxSize);
-        setMeasuredDimension(width, ownHeight);
-    }
-
-    /**
-     * Get the extra height that needs to be added to the notification height for a given
-     * {@link RemoteInputView}.
-     * This is needed when the user is inline replying in order to ensure that the reply bar has
-     * enough padding.
-     *
-     * @param remoteInput The remote input to check.
-     * @return The extra height needed.
-     */
-    private int getExtraRemoteInputHeight(RemoteInputView remoteInput) {
-        if (remoteInput != null && (remoteInput.isActive() || remoteInput.isSending())) {
-            return getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.notification_content_margin);
-        }
-        return 0;
-    }
-
-    private boolean updateContractedHeaderWidth() {
-        // We need to update the expanded and the collapsed header to have exactly the same with to
-        // have the expand buttons laid out at the same location.
-        NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
-        if (contractedHeader != null) {
-            if (mExpandedChild != null
-                    && mExpandedWrapper.getNotificationHeader() != null) {
-                NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
-                int expandedSize = expandedHeader.getMeasuredWidth()
-                        - expandedHeader.getPaddingEnd();
-                int collapsedSize = contractedHeader.getMeasuredWidth()
-                        - expandedHeader.getPaddingEnd();
-                if (expandedSize != collapsedSize) {
-                    int paddingEnd = contractedHeader.getMeasuredWidth() - expandedSize;
-                    contractedHeader.setPadding(
-                            contractedHeader.isLayoutRtl()
-                                    ? paddingEnd
-                                    : contractedHeader.getPaddingLeft(),
-                            contractedHeader.getPaddingTop(),
-                            contractedHeader.isLayoutRtl()
-                                    ? contractedHeader.getPaddingLeft()
-                                    : paddingEnd,
-                            contractedHeader.getPaddingBottom());
-                    contractedHeader.setShowWorkBadgeAtEnd(true);
-                    return true;
-                }
-            } else {
-                int paddingEnd = mNotificationContentMarginEnd;
-                if (contractedHeader.getPaddingEnd() != paddingEnd) {
-                    contractedHeader.setPadding(
-                            contractedHeader.isLayoutRtl()
-                                    ? paddingEnd
-                                    : contractedHeader.getPaddingLeft(),
-                            contractedHeader.getPaddingTop(),
-                            contractedHeader.isLayoutRtl()
-                                    ? contractedHeader.getPaddingLeft()
-                                    : paddingEnd,
-                            contractedHeader.getPaddingBottom());
-                    contractedHeader.setShowWorkBadgeAtEnd(false);
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private boolean shouldContractedBeFixedSize() {
-        return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        int previousHeight = 0;
-        if (mExpandedChild != null) {
-            previousHeight = mExpandedChild.getHeight();
-        }
-        super.onLayout(changed, left, top, right, bottom);
-        if (previousHeight != 0 && mExpandedChild.getHeight() != previousHeight) {
-            mContentHeightAtAnimationStart = previousHeight;
-        }
-        updateClipping();
-        invalidateOutline();
-        selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
-        mForceSelectNextLayout = false;
-        updateExpandButtons(mExpandable);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        updateVisibility();
-    }
-
-    public View getContractedChild() {
-        return mContractedChild;
-    }
-
-    public View getExpandedChild() {
-        return mExpandedChild;
-    }
-
-    public View getHeadsUpChild() {
-        return mHeadsUpChild;
-    }
-
-    public View getAmbientChild() {
-        return mAmbientChild;
-    }
-
-    public HybridNotificationView getAmbientSingleLineChild() {
-        return mAmbientSingleLineChild;
-    }
-
-    public void setContractedChild(View child) {
-        if (mContractedChild != null) {
-            mContractedChild.animate().cancel();
-            removeView(mContractedChild);
-        }
-        addView(child);
-        mContractedChild = child;
-        mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
-                mContainingNotification);
-    }
-
-    private NotificationViewWrapper getWrapperForView(View child) {
-        if (child == mContractedChild) {
-            return mContractedWrapper;
-        }
-        if (child == mExpandedChild) {
-            return mExpandedWrapper;
-        }
-        if (child == mHeadsUpChild) {
-            return mHeadsUpWrapper;
-        }
-        if (child == mAmbientChild) {
-            return mAmbientWrapper;
-        }
-        return null;
-    }
-
-    public void setExpandedChild(View child) {
-        if (mExpandedChild != null) {
-            mPreviousExpandedRemoteInputIntent = null;
-            if (mExpandedRemoteInput != null) {
-                mExpandedRemoteInput.onNotificationUpdateOrReset();
-                if (mExpandedRemoteInput.isActive()) {
-                    mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
-                    mCachedExpandedRemoteInput = mExpandedRemoteInput;
-                    mExpandedRemoteInput.dispatchStartTemporaryDetach();
-                    ((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
-                }
-            }
-            mExpandedChild.animate().cancel();
-            removeView(mExpandedChild);
-            mExpandedRemoteInput = null;
-        }
-        if (child == null) {
-            mExpandedChild = null;
-            mExpandedWrapper = null;
-            if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
-                mVisibleType = VISIBLE_TYPE_CONTRACTED;
-            }
-            if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
-                mTransformationStartVisibleType = UNDEFINED;
-            }
-            return;
-        }
-        addView(child);
-        mExpandedChild = child;
-        mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
-                mContainingNotification);
-    }
-
-    public void setHeadsUpChild(View child) {
-        if (mHeadsUpChild != null) {
-            mPreviousHeadsUpRemoteInputIntent = null;
-            if (mHeadsUpRemoteInput != null) {
-                mHeadsUpRemoteInput.onNotificationUpdateOrReset();
-                if (mHeadsUpRemoteInput.isActive()) {
-                    mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
-                    mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
-                    mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
-                    ((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
-                }
-            }
-            mHeadsUpChild.animate().cancel();
-            removeView(mHeadsUpChild);
-            mHeadsUpRemoteInput = null;
-        }
-        if (child == null) {
-            mHeadsUpChild = null;
-            mHeadsUpWrapper = null;
-            if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
-                mVisibleType = VISIBLE_TYPE_CONTRACTED;
-            }
-            if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
-                mTransformationStartVisibleType = UNDEFINED;
-            }
-            return;
-        }
-        addView(child);
-        mHeadsUpChild = child;
-        mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
-                mContainingNotification);
-    }
-
-    public void setAmbientChild(View child) {
-        if (mAmbientChild != null) {
-            mAmbientChild.animate().cancel();
-            removeView(mAmbientChild);
-        }
-        if (child == null) {
-            return;
-        }
-        addView(child);
-        mAmbientChild = child;
-        mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
-                mContainingNotification);
-    }
-
-    @Override
-    protected void onVisibilityChanged(View changedView, int visibility) {
-        super.onVisibilityChanged(changedView, visibility);
-        updateVisibility();
-    }
-
-    private void updateVisibility() {
-        setVisible(isShown());
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
-    }
-
-    private void setVisible(final boolean isVisible) {
-        if (isVisible) {
-            // This call can happen multiple times, but removing only removes a single one.
-            // We therefore need to remove the old one.
-            getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
-            // We only animate if we are drawn at least once, otherwise the view might animate when
-            // it's shown the first time
-            getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
-        } else {
-            getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
-            mAnimate = false;
-        }
-    }
-
-    private void focusExpandButtonIfNecessary() {
-        if (mFocusOnVisibilityChange) {
-            NotificationHeaderView header = getVisibleNotificationHeader();
-            if (header != null) {
-                ImageView expandButton = header.getExpandButton();
-                if (expandButton != null) {
-                    expandButton.requestAccessibilityFocus();
-                }
-            }
-            mFocusOnVisibilityChange = false;
-        }
-    }
-
-    public void setContentHeight(int contentHeight) {
-        mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
-        int maxContentHeight = mContainingNotification.getIntrinsicHeight()
-                - getExtraRemoteInputHeight(mExpandedRemoteInput)
-                - getExtraRemoteInputHeight(mHeadsUpRemoteInput);
-        mContentHeight = Math.min(mUnrestrictedContentHeight, maxContentHeight);
-        selectLayout(mAnimate /* animate */, false /* force */);
-
-        int minHeightHint = getMinContentHeightHint();
-
-        NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
-        if (wrapper != null) {
-            wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
-        }
-
-        wrapper = getVisibleWrapper(mTransformationStartVisibleType);
-        if (wrapper != null) {
-            wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
-        }
-
-        updateClipping();
-        invalidateOutline();
-    }
-
-    /**
-     * @return the minimum apparent height that the wrapper should allow for the purpose
-     *         of aligning elements at the bottom edge. If this is larger than the content
-     *         height, the notification is clipped instead of being further shrunk.
-     */
-    private int getMinContentHeightHint() {
-        if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
-            return mContext.getResources().getDimensionPixelSize(
-                        com.android.internal.R.dimen.notification_action_list_height);
-        }
-
-        // Transition between heads-up & expanded, or pinned.
-        if (mHeadsUpChild != null && mExpandedChild != null) {
-            boolean transitioningBetweenHunAndExpanded =
-                    isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
-                    isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
-            boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
-                    && (mIsHeadsUp || mHeadsUpAnimatingAway)
-                    && !mContainingNotification.isOnKeyguard();
-            if (transitioningBetweenHunAndExpanded || pinned) {
-                return Math.min(getViewHeight(VISIBLE_TYPE_HEADSUP),
-                        getViewHeight(VISIBLE_TYPE_EXPANDED));
-            }
-        }
-
-        // Size change of the expanded version
-        if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
-                && mExpandedChild != null) {
-            return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
-        }
-
-        int hint;
-        if (mAmbientChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
-            hint = mAmbientChild.getHeight();
-        } else if (mAmbientSingleLineChild != null && isVisibleOrTransitioning(
-                VISIBLE_TYPE_AMBIENT_SINGLELINE)) {
-            hint = mAmbientSingleLineChild.getHeight();
-        } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
-            hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
-        } else if (mExpandedChild != null) {
-            hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
-        } else {
-            hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
-                    + mContext.getResources().getDimensionPixelSize(
-                            com.android.internal.R.dimen.notification_action_list_height);
-        }
-
-        if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
-            hint = Math.min(hint, getViewHeight(VISIBLE_TYPE_EXPANDED));
-        }
-        return hint;
-    }
-
-    private boolean isTransitioningFromTo(int from, int to) {
-        return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
-                && mVisibleType == to;
-    }
-
-    private boolean isVisibleOrTransitioning(int type) {
-        return mVisibleType == type || mTransformationStartVisibleType == type
-                || mAnimationStartVisibleType == type;
-    }
-
-    private void updateContentTransformation() {
-        int visibleType = calculateVisibleType();
-        if (visibleType != mVisibleType) {
-            // A new transformation starts
-            mTransformationStartVisibleType = mVisibleType;
-            final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
-            final TransformableView hiddenView = getTransformableViewForVisibleType(
-                    mTransformationStartVisibleType);
-            shownView.transformFrom(hiddenView, 0.0f);
-            getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
-            hiddenView.transformTo(shownView, 0.0f);
-            mVisibleType = visibleType;
-            updateBackgroundColor(true /* animate */);
-        }
-        if (mForceSelectNextLayout) {
-            forceUpdateVisibilities();
-        }
-        if (mTransformationStartVisibleType != UNDEFINED
-                && mVisibleType != mTransformationStartVisibleType
-                && getViewForVisibleType(mTransformationStartVisibleType) != null) {
-            final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
-            final TransformableView hiddenView = getTransformableViewForVisibleType(
-                    mTransformationStartVisibleType);
-            float transformationAmount = calculateTransformationAmount();
-            shownView.transformFrom(hiddenView, transformationAmount);
-            hiddenView.transformTo(shownView, transformationAmount);
-            updateBackgroundTransformation(transformationAmount);
-        } else {
-            updateViewVisibilities(visibleType);
-            updateBackgroundColor(false);
-        }
-    }
-
-    private void updateBackgroundTransformation(float transformationAmount) {
-        int endColor = getBackgroundColor(mVisibleType);
-        int startColor = getBackgroundColor(mTransformationStartVisibleType);
-        if (endColor != startColor) {
-            if (startColor == 0) {
-                startColor = mContainingNotification.getBackgroundColorWithoutTint();
-            }
-            if (endColor == 0) {
-                endColor = mContainingNotification.getBackgroundColorWithoutTint();
-            }
-            endColor = NotificationUtils.interpolateColors(startColor, endColor,
-                    transformationAmount);
-        }
-        mContainingNotification.updateBackgroundAlpha(transformationAmount);
-        mContainingNotification.setContentBackground(endColor, false, this);
-    }
-
-    private float calculateTransformationAmount() {
-        int startHeight = getViewHeight(mTransformationStartVisibleType);
-        int endHeight = getViewHeight(mVisibleType);
-        int progress = Math.abs(mContentHeight - startHeight);
-        int totalDistance = Math.abs(endHeight - startHeight);
-        if (totalDistance == 0) {
-            Log.wtf(TAG, "the total transformation distance is 0"
-                    + "\n StartType: " + mTransformationStartVisibleType + " height: " + startHeight
-                    + "\n VisibleType: " + mVisibleType + " height: " + endHeight
-                    + "\n mContentHeight: " + mContentHeight);
-            return 1.0f;
-        }
-        float amount = (float) progress / (float) totalDistance;
-        return Math.min(1.0f, amount);
-    }
-
-    public int getContentHeight() {
-        return mContentHeight;
-    }
-
-    public int getMaxHeight() {
-        if (mContainingNotification.isShowingAmbient()) {
-            return getShowingAmbientView().getHeight();
-        } else if (mExpandedChild != null) {
-            return getViewHeight(VISIBLE_TYPE_EXPANDED)
-                    + getExtraRemoteInputHeight(mExpandedRemoteInput);
-        } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
-            return getViewHeight(VISIBLE_TYPE_HEADSUP)
-                    + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
-        }
-        return getViewHeight(VISIBLE_TYPE_CONTRACTED);
-    }
-
-    private int getViewHeight(int visibleType) {
-        View view = getViewForVisibleType(visibleType);
-        int height = view.getHeight();
-        NotificationViewWrapper viewWrapper = getWrapperForView(view);
-        if (viewWrapper != null) {
-            height += viewWrapper.getHeaderTranslation();
-        }
-        return height;
-    }
-
-    public int getMinHeight() {
-        return getMinHeight(false /* likeGroupExpanded */);
-    }
-
-    public int getMinHeight(boolean likeGroupExpanded) {
-        if (mContainingNotification.isShowingAmbient()) {
-            return getShowingAmbientView().getHeight();
-        } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
-            return getViewHeight(VISIBLE_TYPE_CONTRACTED);
-        } else {
-            return mSingleLineView.getHeight();
-        }
-    }
-
-    public View getShowingAmbientView() {
-        View v = mIsChildInGroup ? mAmbientSingleLineChild : mAmbientChild;
-        if (v != null) {
-            return v;
-        } else {
-            return mContractedChild;
-        }
-    }
-
-    private boolean isGroupExpanded() {
-        return mGroupManager.isGroupExpanded(mStatusBarNotification);
-    }
-
-    public void setClipTopAmount(int clipTopAmount) {
-        mClipTopAmount = clipTopAmount;
-        updateClipping();
-    }
-
-
-    public void setClipBottomAmount(int clipBottomAmount) {
-        mClipBottomAmount = clipBottomAmount;
-        updateClipping();
-    }
-
-    @Override
-    public void setTranslationY(float translationY) {
-        super.setTranslationY(translationY);
-        updateClipping();
-    }
-
-    private void updateClipping() {
-        if (mClipToActualHeight) {
-            int top = (int) (mClipTopAmount - getTranslationY());
-            int bottom = (int) (mUnrestrictedContentHeight - mClipBottomAmount - getTranslationY());
-            bottom = Math.max(top, bottom);
-            mClipBounds.set(0, top, getWidth(), bottom);
-            setClipBounds(mClipBounds);
-        } else {
-            setClipBounds(null);
-        }
-    }
-
-    public void setClipToActualHeight(boolean clipToActualHeight) {
-        mClipToActualHeight = clipToActualHeight;
-        updateClipping();
-    }
-
-    private void selectLayout(boolean animate, boolean force) {
-        if (mContractedChild == null) {
-            return;
-        }
-        if (mUserExpanding) {
-            updateContentTransformation();
-        } else {
-            int visibleType = calculateVisibleType();
-            boolean changedType = visibleType != mVisibleType;
-            if (changedType || force) {
-                View visibleView = getViewForVisibleType(visibleType);
-                if (visibleView != null) {
-                    visibleView.setVisibility(VISIBLE);
-                    transferRemoteInputFocus(visibleType);
-                }
-
-                if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
-                        || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
-                        || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
-                        || visibleType == VISIBLE_TYPE_CONTRACTED)) {
-                    animateToVisibleType(visibleType);
-                } else {
-                    updateViewVisibilities(visibleType);
-                }
-                mVisibleType = visibleType;
-                if (changedType) {
-                    focusExpandButtonIfNecessary();
-                }
-                NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
-                if (visibleWrapper != null) {
-                    visibleWrapper.setContentHeight(mUnrestrictedContentHeight,
-                            getMinContentHeightHint());
-                }
-                updateBackgroundColor(animate);
-            }
-        }
-    }
-
-    private void forceUpdateVisibilities() {
-        forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
-        forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
-        forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
-        forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
-        forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
-        forceUpdateVisibility(VISIBLE_TYPE_AMBIENT_SINGLELINE, mAmbientSingleLineChild,
-                mAmbientSingleLineChild);
-        fireExpandedVisibleListenerIfVisible();
-        // forceUpdateVisibilities cancels outstanding animations without updating the
-        // mAnimationStartVisibleType. Do so here instead.
-        mAnimationStartVisibleType = UNDEFINED;
-    }
-
-    private void fireExpandedVisibleListenerIfVisible() {
-        if (mExpandedVisibleListener != null && mExpandedChild != null && isShown()
-                && mExpandedChild.getVisibility() == VISIBLE) {
-            Runnable listener = mExpandedVisibleListener;
-            mExpandedVisibleListener = null;
-            listener.run();
-        }
-    }
-
-    private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
-        if (view == null) {
-            return;
-        }
-        boolean visible = mVisibleType == type
-                || mTransformationStartVisibleType == type;
-        if (!visible) {
-            view.setVisibility(INVISIBLE);
-        } else {
-            wrapper.setVisible(true);
-        }
-    }
-
-    public void updateBackgroundColor(boolean animate) {
-        int customBackgroundColor = getBackgroundColor(mVisibleType);
-        mContainingNotification.resetBackgroundAlpha();
-        mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
-    }
-
-    public void setBackgroundTintColor(int color) {
-        if (mExpandedSmartReplyView != null) {
-            mExpandedSmartReplyView.setBackgroundTintColor(color);
-        }
-    }
-
-    public int getVisibleType() {
-        return mVisibleType;
-    }
-
-    public int getBackgroundColorForExpansionState() {
-        // When expanding or user locked we want the new type, when collapsing we want
-        // the original type
-        final int visibleType = (mContainingNotification.isGroupExpanded()
-                || mContainingNotification.isUserLocked())
-                        ? calculateVisibleType()
-                        : getVisibleType();
-        return getBackgroundColor(visibleType);
-    }
-
-    public int getBackgroundColor(int visibleType) {
-        NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
-        int customBackgroundColor = 0;
-        if (currentVisibleWrapper != null) {
-            customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
-        }
-        return customBackgroundColor;
-    }
-
-    private void updateViewVisibilities(int visibleType) {
-        updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
-                mContractedChild, mContractedWrapper);
-        updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
-                mExpandedChild, mExpandedWrapper);
-        updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
-                mHeadsUpChild, mHeadsUpWrapper);
-        updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
-                mSingleLineView, mSingleLineView);
-        updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
-                mAmbientChild, mAmbientWrapper);
-        updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
-                mAmbientSingleLineChild, mAmbientSingleLineChild);
-        fireExpandedVisibleListenerIfVisible();
-        // updateViewVisibilities cancels outstanding animations without updating the
-        // mAnimationStartVisibleType. Do so here instead.
-        mAnimationStartVisibleType = UNDEFINED;
-    }
-
-    private void updateViewVisibility(int visibleType, int type, View view,
-            TransformableView wrapper) {
-        if (view != null) {
-            wrapper.setVisible(visibleType == type);
-        }
-    }
-
-    private void animateToVisibleType(int visibleType) {
-        final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
-        final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
-        if (shownView == hiddenView || hiddenView == null) {
-            shownView.setVisible(true);
-            return;
-        }
-        mAnimationStartVisibleType = mVisibleType;
-        shownView.transformFrom(hiddenView);
-        getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
-        hiddenView.transformTo(shownView, new Runnable() {
-            @Override
-            public void run() {
-                if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
-                    hiddenView.setVisible(false);
-                }
-                mAnimationStartVisibleType = UNDEFINED;
-            }
-        });
-        fireExpandedVisibleListenerIfVisible();
-    }
-
-    private void transferRemoteInputFocus(int visibleType) {
-        if (visibleType == VISIBLE_TYPE_HEADSUP
-                && mHeadsUpRemoteInput != null
-                && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
-            mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
-        }
-        if (visibleType == VISIBLE_TYPE_EXPANDED
-                && mExpandedRemoteInput != null
-                && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
-            mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
-        }
-    }
-
-    /**
-     * @param visibleType one of the static enum types in this view
-     * @return the corresponding transformable view according to the given visible type
-     */
-    private TransformableView getTransformableViewForVisibleType(int visibleType) {
-        switch (visibleType) {
-            case VISIBLE_TYPE_EXPANDED:
-                return mExpandedWrapper;
-            case VISIBLE_TYPE_HEADSUP:
-                return mHeadsUpWrapper;
-            case VISIBLE_TYPE_SINGLELINE:
-                return mSingleLineView;
-            case VISIBLE_TYPE_AMBIENT:
-                return mAmbientWrapper;
-            case VISIBLE_TYPE_AMBIENT_SINGLELINE:
-                return mAmbientSingleLineChild;
-            default:
-                return mContractedWrapper;
-        }
-    }
-
-    /**
-     * @param visibleType one of the static enum types in this view
-     * @return the corresponding view according to the given visible type
-     */
-    private View getViewForVisibleType(int visibleType) {
-        switch (visibleType) {
-            case VISIBLE_TYPE_EXPANDED:
-                return mExpandedChild;
-            case VISIBLE_TYPE_HEADSUP:
-                return mHeadsUpChild;
-            case VISIBLE_TYPE_SINGLELINE:
-                return mSingleLineView;
-            case VISIBLE_TYPE_AMBIENT:
-                return mAmbientChild;
-            case VISIBLE_TYPE_AMBIENT_SINGLELINE:
-                return mAmbientSingleLineChild;
-            default:
-                return mContractedChild;
-        }
-    }
-
-    public NotificationViewWrapper getVisibleWrapper(int visibleType) {
-        switch (visibleType) {
-            case VISIBLE_TYPE_EXPANDED:
-                return mExpandedWrapper;
-            case VISIBLE_TYPE_HEADSUP:
-                return mHeadsUpWrapper;
-            case VISIBLE_TYPE_CONTRACTED:
-                return mContractedWrapper;
-            case VISIBLE_TYPE_AMBIENT:
-                return mAmbientWrapper;
-            default:
-                return null;
-        }
-    }
-
-    /**
-     * @return one of the static enum types in this view, calculated form the current state
-     */
-    public int calculateVisibleType() {
-        if (mContainingNotification.isShowingAmbient()) {
-            if (mIsChildInGroup && mAmbientSingleLineChild != null) {
-                return VISIBLE_TYPE_AMBIENT_SINGLELINE;
-            } else if (mAmbientChild != null) {
-                return VISIBLE_TYPE_AMBIENT;
-            } else {
-                return VISIBLE_TYPE_CONTRACTED;
-            }
-        }
-        if (mUserExpanding) {
-            int height = !mIsChildInGroup || isGroupExpanded()
-                    || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
-                    ? mContainingNotification.getMaxContentHeight()
-                    : mContainingNotification.getShowingLayout().getMinHeight();
-            if (height == 0) {
-                height = mContentHeight;
-            }
-            int expandedVisualType = getVisualTypeForHeight(height);
-            int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
-                    ? VISIBLE_TYPE_SINGLELINE
-                    : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
-            return mTransformationStartVisibleType == collapsedVisualType
-                    ? expandedVisualType
-                    : collapsedVisualType;
-        }
-        int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
-        int viewHeight = mContentHeight;
-        if (intrinsicHeight != 0) {
-            // the intrinsicHeight might be 0 because it was just reset.
-            viewHeight = Math.min(mContentHeight, intrinsicHeight);
-        }
-        return getVisualTypeForHeight(viewHeight);
-    }
-
-    private int getVisualTypeForHeight(float viewHeight) {
-        boolean noExpandedChild = mExpandedChild == null;
-        if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) {
-            return VISIBLE_TYPE_EXPANDED;
-        }
-        if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
-            return VISIBLE_TYPE_SINGLELINE;
-        }
-
-        if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
-                && !mContainingNotification.isOnKeyguard()) {
-            if (viewHeight <= getViewHeight(VISIBLE_TYPE_HEADSUP) || noExpandedChild) {
-                return VISIBLE_TYPE_HEADSUP;
-            } else {
-                return VISIBLE_TYPE_EXPANDED;
-            }
-        } else {
-            if (noExpandedChild || (viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
-                    && (!mIsChildInGroup || isGroupExpanded()
-                            || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
-                return VISIBLE_TYPE_CONTRACTED;
-            } else {
-                return VISIBLE_TYPE_EXPANDED;
-            }
-        }
-    }
-
-    public boolean isContentExpandable() {
-        return mIsContentExpandable;
-    }
-
-    public void setDark(boolean dark, boolean fade, long delay) {
-        if (mContractedChild == null) {
-            return;
-        }
-        mDark = dark;
-        selectLayout(!dark && fade /* animate */, false /* force */);
-    }
-
-    public void setHeadsUp(boolean headsUp) {
-        mIsHeadsUp = headsUp;
-        selectLayout(false /* animate */, true /* force */);
-        updateExpandButtons(mExpandable);
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-
-        // This is not really true, but good enough when fading from the contracted to the expanded
-        // layout, and saves us some layers.
-        return false;
-    }
-
-    public void setLegacy(boolean legacy) {
-        mLegacy = legacy;
-        updateLegacy();
-    }
-
-    private void updateLegacy() {
-        if (mContractedChild != null) {
-            mContractedWrapper.setLegacy(mLegacy);
-        }
-        if (mExpandedChild != null) {
-            mExpandedWrapper.setLegacy(mLegacy);
-        }
-        if (mHeadsUpChild != null) {
-            mHeadsUpWrapper.setLegacy(mLegacy);
-        }
-    }
-
-    public void setIsChildInGroup(boolean isChildInGroup) {
-        mIsChildInGroup = isChildInGroup;
-        if (mContractedChild != null) {
-            mContractedWrapper.setIsChildInGroup(mIsChildInGroup);
-        }
-        if (mExpandedChild != null) {
-            mExpandedWrapper.setIsChildInGroup(mIsChildInGroup);
-        }
-        if (mHeadsUpChild != null) {
-            mHeadsUpWrapper.setIsChildInGroup(mIsChildInGroup);
-        }
-        if (mAmbientChild != null) {
-            mAmbientWrapper.setIsChildInGroup(mIsChildInGroup);
-        }
-        updateAllSingleLineViews();
-    }
-
-    public void onNotificationUpdated(NotificationData.Entry entry) {
-        mStatusBarNotification = entry.notification;
-        mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
-        updateAllSingleLineViews();
-        if (mContractedChild != null) {
-            mContractedWrapper.onContentUpdated(entry.row);
-        }
-        if (mExpandedChild != null) {
-            mExpandedWrapper.onContentUpdated(entry.row);
-        }
-        if (mHeadsUpChild != null) {
-            mHeadsUpWrapper.onContentUpdated(entry.row);
-        }
-        if (mAmbientChild != null) {
-            mAmbientWrapper.onContentUpdated(entry.row);
-        }
-        applyRemoteInputAndSmartReply(entry);
-        updateLegacy();
-        mForceSelectNextLayout = true;
-        setDark(mDark, false /* animate */, 0 /* delay */);
-        mPreviousExpandedRemoteInputIntent = null;
-        mPreviousHeadsUpRemoteInputIntent = null;
-    }
-
-    private void updateAllSingleLineViews() {
-        updateSingleLineView();
-        updateAmbientSingleLineView();
-    }
-    private void updateSingleLineView() {
-        if (mIsChildInGroup) {
-            boolean isNewView = mSingleLineView == null;
-            mSingleLineView = mHybridGroupManager.bindFromNotification(
-                    mSingleLineView, mStatusBarNotification.getNotification());
-            if (isNewView) {
-                updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE,
-                        mSingleLineView, mSingleLineView);
-            }
-        } else if (mSingleLineView != null) {
-            removeView(mSingleLineView);
-            mSingleLineView = null;
-        }
-    }
-
-    private void updateAmbientSingleLineView() {
-        if (mIsChildInGroup) {
-            boolean isNewView = mAmbientSingleLineChild == null;
-            mAmbientSingleLineChild = mHybridGroupManager.bindAmbientFromNotification(
-                    mAmbientSingleLineChild, mStatusBarNotification.getNotification());
-            if (isNewView) {
-                updateViewVisibility(mVisibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
-                        mAmbientSingleLineChild, mAmbientSingleLineChild);
-            }
-        } else if (mAmbientSingleLineChild != null) {
-            removeView(mAmbientSingleLineChild);
-            mAmbientSingleLineChild = null;
-        }
-    }
-
-    private void applyRemoteInputAndSmartReply(final NotificationData.Entry entry) {
-        if (mRemoteInputController == null) {
-            return;
-        }
-
-        boolean enableSmartReplies = (mSmartReplyConstants.isEnabled()
-                && (!mSmartReplyConstants.requiresTargetingP()
-                    || entry.targetSdk >= Build.VERSION_CODES.P));
-
-        boolean hasRemoteInput = false;
-        RemoteInput remoteInputWithChoices = null;
-        PendingIntent pendingIntentWithChoices = null;
-
-        Notification.Action[] actions = entry.notification.getNotification().actions;
-        if (actions != null) {
-            for (Notification.Action a : actions) {
-                if (a.getRemoteInputs() != null) {
-                    for (RemoteInput ri : a.getRemoteInputs()) {
-                        boolean showRemoteInputView = ri.getAllowFreeFormInput();
-                        boolean showSmartReplyView = enableSmartReplies && ri.getChoices() != null
-                                && ri.getChoices().length > 0;
-                        if (showRemoteInputView) {
-                            hasRemoteInput = true;
-                        }
-                        if (showSmartReplyView) {
-                            remoteInputWithChoices = ri;
-                            pendingIntentWithChoices = a.actionIntent;
-                        }
-                        if (showRemoteInputView || showSmartReplyView) {
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-
-        applyRemoteInput(entry, hasRemoteInput);
-        applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry);
-    }
-
-    private void applyRemoteInput(NotificationData.Entry entry, boolean hasRemoteInput) {
-        View bigContentView = mExpandedChild;
-        if (bigContentView != null) {
-            mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
-                    mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
-                    mExpandedWrapper);
-        } else {
-            mExpandedRemoteInput = null;
-        }
-        if (mCachedExpandedRemoteInput != null
-                && mCachedExpandedRemoteInput != mExpandedRemoteInput) {
-            // We had a cached remote input but didn't reuse it. Clean up required.
-            mCachedExpandedRemoteInput.dispatchFinishTemporaryDetach();
-        }
-        mCachedExpandedRemoteInput = null;
-
-        View headsUpContentView = mHeadsUpChild;
-        if (headsUpContentView != null) {
-            mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
-                    mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
-        } else {
-            mHeadsUpRemoteInput = null;
-        }
-        if (mCachedHeadsUpRemoteInput != null
-                && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
-            // We had a cached remote input but didn't reuse it. Clean up required.
-            mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
-        }
-        mCachedHeadsUpRemoteInput = null;
-    }
-
-    private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
-            boolean hasRemoteInput, PendingIntent existingPendingIntent,
-            RemoteInputView cachedView, NotificationViewWrapper wrapper) {
-        View actionContainerCandidate = view.findViewById(
-                com.android.internal.R.id.actions_container);
-        if (actionContainerCandidate instanceof FrameLayout) {
-            RemoteInputView existing = (RemoteInputView)
-                    view.findViewWithTag(RemoteInputView.VIEW_TAG);
-
-            if (existing != null) {
-                existing.onNotificationUpdateOrReset();
-            }
-
-            if (existing == null && hasRemoteInput) {
-                ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
-                if (cachedView == null) {
-                    RemoteInputView riv = RemoteInputView.inflate(
-                            mContext, actionContainer, entry, mRemoteInputController);
-
-                    riv.setVisibility(View.INVISIBLE);
-                    actionContainer.addView(riv, new LayoutParams(
-                            ViewGroup.LayoutParams.MATCH_PARENT,
-                            ViewGroup.LayoutParams.MATCH_PARENT)
-                    );
-                    existing = riv;
-                } else {
-                    actionContainer.addView(cachedView);
-                    cachedView.dispatchFinishTemporaryDetach();
-                    cachedView.requestFocus();
-                    existing = cachedView;
-                }
-            }
-            if (hasRemoteInput) {
-                int color = entry.notification.getNotification().color;
-                if (color == Notification.COLOR_DEFAULT) {
-                    color = mContext.getColor(R.color.default_remote_input_background);
-                }
-                existing.setBackgroundColor(ContrastColorUtil.ensureTextBackgroundColor(color,
-                        mContext.getColor(R.color.remote_input_text_enabled),
-                        mContext.getColor(R.color.remote_input_hint)));
-
-                existing.setWrapper(wrapper);
-                existing.setOnVisibilityChangedListener(this::setRemoteInputVisible);
-
-                if (existingPendingIntent != null || existing.isActive()) {
-                    // The current action could be gone, or the pending intent no longer valid.
-                    // If we find a matching action in the new notification, focus, otherwise close.
-                    Notification.Action[] actions = entry.notification.getNotification().actions;
-                    if (existingPendingIntent != null) {
-                        existing.setPendingIntent(existingPendingIntent);
-                    }
-                    if (existing.updatePendingIntentFromActions(actions)) {
-                        if (!existing.isActive()) {
-                            existing.focus();
-                        }
-                    } else {
-                        if (existing.isActive()) {
-                            existing.close();
-                        }
-                    }
-                }
-            }
-            return existing;
-        }
-        return null;
-    }
-
-    private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent,
-            NotificationData.Entry entry) {
-        if (mExpandedChild != null) {
-            mExpandedSmartReplyView =
-                    applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry);
-            if (mExpandedSmartReplyView != null && remoteInput != null
-                    && remoteInput.getChoices() != null && remoteInput.getChoices().length > 0) {
-                mSmartReplyController.smartRepliesAdded(entry, remoteInput.getChoices().length);
-            }
-        }
-    }
-
-    private SmartReplyView applySmartReplyView(
-            View view, RemoteInput remoteInput, PendingIntent pendingIntent,
-            NotificationData.Entry entry) {
-        View smartReplyContainerCandidate = view.findViewById(
-                com.android.internal.R.id.smart_reply_container);
-        if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
-            return null;
-        }
-        LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
-        if (remoteInput == null || pendingIntent == null) {
-            smartReplyContainer.setVisibility(View.GONE);
-            return null;
-        }
-        // If we are showing the spinner we don't want to add the buttons.
-        boolean showingSpinner = entry.notification.getNotification()
-                .extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
-        if (showingSpinner) {
-            smartReplyContainer.setVisibility(View.GONE);
-            return null;
-        }
-        // If we are keeping the notification around while sending we don't want to add the buttons.
-        boolean hideSmartReplies = entry.notification.getNotification()
-                .extras.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false);
-        if (hideSmartReplies) {
-            smartReplyContainer.setVisibility(View.GONE);
-            return null;
-        }
-        SmartReplyView smartReplyView = null;
-        if (smartReplyContainer.getChildCount() == 0) {
-            smartReplyView = SmartReplyView.inflate(mContext, smartReplyContainer);
-            smartReplyContainer.addView(smartReplyView);
-        } else if (smartReplyContainer.getChildCount() == 1) {
-            View child = smartReplyContainer.getChildAt(0);
-            if (child instanceof SmartReplyView) {
-                smartReplyView = (SmartReplyView) child;
-            }
-        }
-        if (smartReplyView != null) {
-            smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent,
-                    mSmartReplyController, entry, smartReplyContainer);
-            smartReplyContainer.setVisibility(View.VISIBLE);
-        }
-        return smartReplyView;
-    }
-
-    public void closeRemoteInput() {
-        if (mHeadsUpRemoteInput != null) {
-            mHeadsUpRemoteInput.close();
-        }
-        if (mExpandedRemoteInput != null) {
-            mExpandedRemoteInput.close();
-        }
-    }
-
-    public void setGroupManager(NotificationGroupManager groupManager) {
-        mGroupManager = groupManager;
-    }
-
-    public void setRemoteInputController(RemoteInputController r) {
-        mRemoteInputController = r;
-    }
-
-    public void setExpandClickListener(OnClickListener expandClickListener) {
-        mExpandClickListener = expandClickListener;
-    }
-
-    public void updateExpandButtons(boolean expandable) {
-        mExpandable = expandable;
-        // if the expanded child has the same height as the collapsed one we hide it.
-        if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
-            if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
-                    || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
-                if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
-                    expandable = false;
-                }
-            } else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
-                expandable = false;
-            }
-        }
-        if (mExpandedChild != null) {
-            mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
-        }
-        if (mContractedChild != null) {
-            mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
-        }
-        if (mHeadsUpChild != null) {
-            mHeadsUpWrapper.updateExpandability(expandable,  mExpandClickListener);
-        }
-        mIsContentExpandable = expandable;
-    }
-
-    public NotificationHeaderView getNotificationHeader() {
-        NotificationHeaderView header = null;
-        if (mContractedChild != null) {
-            header = mContractedWrapper.getNotificationHeader();
-        }
-        if (header == null && mExpandedChild != null) {
-            header = mExpandedWrapper.getNotificationHeader();
-        }
-        if (header == null && mHeadsUpChild != null) {
-            header = mHeadsUpWrapper.getNotificationHeader();
-        }
-        if (header == null && mAmbientChild != null) {
-            header = mAmbientWrapper.getNotificationHeader();
-        }
-        return header;
-    }
-
-    public void showAppOpsIcons(ArraySet<Integer> activeOps) {
-        if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
-            mContractedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
-        }
-        if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
-            mExpandedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
-        }
-        if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
-            mHeadsUpWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
-        }
-    }
-
-    public NotificationHeaderView getContractedNotificationHeader() {
-        if (mContractedChild != null) {
-            return mContractedWrapper.getNotificationHeader();
-        }
-        return null;
-    }
-
-    public NotificationHeaderView getVisibleNotificationHeader() {
-        NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
-        return wrapper == null ? null : wrapper.getNotificationHeader();
-    }
-
-    public void setContainingNotification(ExpandableNotificationRow containingNotification) {
-        mContainingNotification = containingNotification;
-    }
-
-    public void requestSelectLayout(boolean needsAnimation) {
-        selectLayout(needsAnimation, false);
-    }
-
-    public void reInflateViews() {
-        if (mIsChildInGroup && mSingleLineView != null) {
-            removeView(mSingleLineView);
-            mSingleLineView = null;
-            updateAllSingleLineViews();
-        }
-    }
-
-    public void setUserExpanding(boolean userExpanding) {
-        mUserExpanding = userExpanding;
-        if (userExpanding) {
-            mTransformationStartVisibleType = mVisibleType;
-        } else {
-            mTransformationStartVisibleType = UNDEFINED;
-            mVisibleType = calculateVisibleType();
-            updateViewVisibilities(mVisibleType);
-            updateBackgroundColor(false);
-        }
-    }
-
-    /**
-     * Set by how much the single line view should be indented. Used when a overflow indicator is
-     * present and only during measuring
-     */
-    public void setSingleLineWidthIndention(int singleLineWidthIndention) {
-        if (singleLineWidthIndention != mSingleLineWidthIndention) {
-            mSingleLineWidthIndention = singleLineWidthIndention;
-            mContainingNotification.forceLayout();
-            forceLayout();
-        }
-    }
-
-    public HybridNotificationView getSingleLineView() {
-        return mSingleLineView;
-    }
-
-    public void setRemoved() {
-        if (mExpandedRemoteInput != null) {
-            mExpandedRemoteInput.setRemoved();
-        }
-        if (mHeadsUpRemoteInput != null) {
-            mHeadsUpRemoteInput.setRemoved();
-        }
-    }
-
-    public void setContentHeightAnimating(boolean animating) {
-        if (!animating) {
-            mContentHeightAtAnimationStart = UNDEFINED;
-        }
-    }
-
-    @VisibleForTesting
-    boolean isAnimatingVisibleType() {
-        return mAnimationStartVisibleType != UNDEFINED;
-    }
-
-    public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
-        mHeadsUpAnimatingAway = headsUpAnimatingAway;
-        selectLayout(false /* animate */, true /* force */);
-    }
-
-    public void setFocusOnVisibilityChange() {
-        mFocusOnVisibilityChange = true;
-    }
-
-    public void setIconsVisible(boolean iconsVisible) {
-        mIconsVisible = iconsVisible;
-        updateIconVisibilities();
-    }
-
-    private void updateIconVisibilities() {
-        if (mContractedWrapper != null) {
-            NotificationHeaderView header = mContractedWrapper.getNotificationHeader();
-            if (header != null) {
-                header.getIcon().setForceHidden(!mIconsVisible);
-            }
-        }
-        if (mHeadsUpWrapper != null) {
-            NotificationHeaderView header = mHeadsUpWrapper.getNotificationHeader();
-            if (header != null) {
-                header.getIcon().setForceHidden(!mIconsVisible);
-            }
-        }
-        if (mExpandedWrapper != null) {
-            NotificationHeaderView header = mExpandedWrapper.getNotificationHeader();
-            if (header != null) {
-                header.getIcon().setForceHidden(!mIconsVisible);
-            }
-        }
-    }
-
-    @Override
-    public void onVisibilityAggregated(boolean isVisible) {
-        super.onVisibilityAggregated(isVisible);
-        if (isVisible) {
-            fireExpandedVisibleListenerIfVisible();
-        }
-    }
-
-    /**
-     * Sets a one-shot listener for when the expanded view becomes visible.
-     *
-     * This will fire the listener immediately if the expanded view is already visible.
-     */
-    public void setOnExpandedVisibleListener(Runnable r) {
-        mExpandedVisibleListener = r;
-        fireExpandedVisibleListenerIfVisible();
-    }
-
-    public void setIsLowPriority(boolean isLowPriority) {
-        mIsLowPriority = isLowPriority;
-    }
-
-    public boolean isDimmable() {
-        if (!mContractedWrapper.isDimmable()) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Should a single click be disallowed on this view when on the keyguard?
-     */
-    public boolean disallowSingleClick(float x, float y) {
-        NotificationViewWrapper visibleWrapper = getVisibleWrapper(getVisibleType());
-        if (visibleWrapper != null) {
-            return visibleWrapper.disallowSingleClick(x, y);
-        }
-        return false;
-    }
-
-    public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
-        boolean needsPaddings = shouldClipToRounding(getVisibleType(), topRounded, bottomRounded);
-        if (mUserExpanding) {
-             needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType, topRounded,
-                     bottomRounded);
-        }
-        return needsPaddings;
-    }
-
-    private boolean shouldClipToRounding(int visibleType, boolean topRounded,
-            boolean bottomRounded) {
-        NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
-        if (visibleWrapper == null) {
-            return false;
-        }
-        return visibleWrapper.shouldClipToRounding(topRounded, bottomRounded);
-    }
-
-    public CharSequence getActiveRemoteInputText() {
-        if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) {
-            return mExpandedRemoteInput.getText();
-        }
-        if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) {
-            return mHeadsUpRemoteInput.getText();
-        }
-        return null;
-    }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        float y = ev.getY();
-        // We still want to distribute touch events to the remote input even if it's outside the
-        // view boundary. We're therefore manually dispatching these events to the remote view
-        RemoteInputView riv = getRemoteInputForView(getViewForVisibleType(mVisibleType));
-        if (riv != null && riv.getVisibility() == VISIBLE) {
-            int inputStart = mUnrestrictedContentHeight - riv.getHeight();
-            if (y <= mUnrestrictedContentHeight && y >= inputStart) {
-                ev.offsetLocation(0, -inputStart);
-                return riv.dispatchTouchEvent(ev);
-            }
-        }
-        return super.dispatchTouchEvent(ev);
-    }
-
-    /**
-     * Overridden to make sure touches to the reply action bar actually go through to this view
-     */
-    @Override
-    public boolean pointInView(float localX, float localY, float slop) {
-        float top = mClipTopAmount;
-        float bottom = mUnrestrictedContentHeight;
-        return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
-                localY < (bottom + slop);
-    }
-
-    private RemoteInputView getRemoteInputForView(View child) {
-        if (child == mExpandedChild) {
-            return mExpandedRemoteInput;
-        } else if (child == mHeadsUpChild) {
-            return mHeadsUpRemoteInput;
-        }
-        return null;
-    }
-
-    public int getExpandHeight() {
-        int viewType = VISIBLE_TYPE_EXPANDED;
-        if (mExpandedChild == null) {
-            viewType = VISIBLE_TYPE_CONTRACTED;
-        }
-        return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
-    }
-
-    public int getHeadsUpHeight() {
-        int viewType = VISIBLE_TYPE_HEADSUP;
-        if (mHeadsUpChild == null) {
-            viewType = VISIBLE_TYPE_CONTRACTED;
-        }
-        // The headsUp remote input quickly switches to the expanded one, so lets also include that
-        // one
-        return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
-                + getExtraRemoteInputHeight(mExpandedRemoteInput);
-    }
-
-    public void setRemoteInputVisible(boolean remoteInputVisible) {
-        mRemoteInputVisible = remoteInputVisible;
-        setClipChildren(!remoteInputVisible);
-    }
-
-    @Override
-    public void setClipChildren(boolean clipChildren) {
-        clipChildren = clipChildren && !mRemoteInputVisible;
-        super.setClipChildren(clipChildren);
-    }
-
-    public void setHeaderVisibleAmount(float headerVisibleAmount) {
-        if (mContractedWrapper != null) {
-            mContractedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
-        }
-        if (mHeadsUpWrapper != null) {
-            mHeadsUpWrapper.setHeaderVisibleAmount(headerVisibleAmount);
-        }
-        if (mExpandedWrapper != null) {
-            mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
deleted file mode 100644
index 1a645d1..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ /dev/null
@@ -1,826 +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;
-
-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.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 android.widget.RemoteViews;
-
-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.notification.InflationException;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-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 Environment mEnvironment;
-    private HeadsUpManager mHeadsUpManager;
-
-    final ZenModeController mZen = Dependency.get(ZenModeController.class);
-    final ForegroundServiceController mFsc = Dependency.get(ForegroundServiceController.class);
-
-    public static final class Entry {
-        private static final long LAUNCH_COOLDOWN = 2000;
-        private static final long REMOTE_INPUT_COOLDOWN = 500;
-        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 StatusBarIconView icon;
-        public StatusBarIconView expandedIcon;
-        public ExpandableNotificationRow row; // the outer expanded view
-        private boolean interruption;
-        public boolean autoRedacted; // whether the redacted notification was generated by us
-        public int targetSdk;
-        private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
-        public RemoteViews cachedContentView;
-        public RemoteViews cachedBigContentView;
-        public RemoteViews cachedHeadsUpContentView;
-        public RemoteViews cachedPublicContentView;
-        public RemoteViews cachedAmbientContentView;
-        public CharSequence remoteInputText;
-        public List<SnoozeCriterion> snoozeCriteria;
-        public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
-
-        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;
-        /**
-         * 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;
-
-        public Entry(StatusBarNotification n) {
-            this.key = n.getKey();
-            this.notification = n;
-        }
-
-        public void setInterruption() {
-            interruption = true;
-        }
-
-        public boolean hasInterrupted() {
-            return interruption;
-        }
-
-        /**
-         * Resets the notification entry to be re-used.
-         */
-        public void reset() {
-            if (row != null) {
-                row.reset();
-            }
-        }
-
-        public View getExpandedContentView() {
-            return row.getPrivateLayout().getExpandedChild();
-        }
-
-        public View getPublicContentView() {
-            return row.getPublicLayout().getContractedChild();
-        }
-
-        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;
-        }
-
-        /**
-         * 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;
-        }
-    }
-
-    private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
-    private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>();
-    private final ArrayList<Entry> mFilteredForUser = new ArrayList<>();
-
-    private NotificationGroupManager mGroupManager;
-
-    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 = mEnvironment.getCurrentMediaNotificationKey();
-
-            // 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 (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);
-            }
-        }
-    };
-
-    public NotificationData(Environment environment) {
-        mEnvironment = environment;
-        mGroupManager = environment.getGroupManager();
-    }
-
-    /**
-     * Returns the sorted list of active notifications (depending on {@link Environment}
-     *
-     * <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 (!mEnvironment.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);
-                    }
-                }
-            }
-        }
-    }
-
-    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.channel = getChannel(entry.key);
-                    entry.snoozeCriteria = getSnoozeCriteria(entry.key);
-                    entry.userSentiment = mTmpRanking.getUserSentiment();
-                }
-            }
-        }
-        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 (!(mEnvironment.isDeviceProvisioned() ||
-                showNotificationEvenIfUnprovisioned(sbn))) {
-            return true;
-        }
-
-        if (!mEnvironment.isNotificationForCurrentProfiles(sbn)) {
-            return true;
-        }
-
-        if (mEnvironment.isSecurelyLocked(sbn.getUserId()) &&
-                (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
-                        || mEnvironment.shouldHideNotifications(sbn.getUserId())
-                        || mEnvironment.shouldHideNotifications(sbn.getKey()))) {
-            return true;
-        }
-
-        if (mEnvironment.isDozing() && shouldSuppressAmbient(entry)) {
-            return true;
-        }
-
-        if (!mEnvironment.isDozing() && shouldSuppressNotificationList(entry)) {
-            return true;
-        }
-
-        if (shouldHide(sbn.getKey())) {
-            return true;
-        }
-
-        if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
-                && mGroupManager.isChildInGroupWithSummary(sbn)) {
-            return true;
-        }
-
-        if (mFsc.isDungeonNotification(sbn) && !mFsc.isDungeonNeededForUser(sbn.getUserId())) {
-            // this is a foreground-service disclosure for a user that does not need to show one
-            return true;
-        }
-        if (mFsc.isSystemAlertNotification(sbn)) {
-            final String[] apps = sbn.getNotification().extras.getStringArray(
-                    Notification.EXTRA_FOREGROUND_APPS);
-            if (apps != null && apps.length >= 1) {
-                if (!mFsc.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 Environment {
-        public boolean isSecurelyLocked(int userId);
-        public boolean shouldHideNotifications(int userid);
-        public boolean shouldHideNotifications(String key);
-        public boolean isDeviceProvisioned();
-        public boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
-        public String getCurrentMediaNotificationKey();
-        public NotificationGroupManager getGroupManager();
-        /**
-         * @return true iff the device is dozing
-         */
-        boolean isDozing();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
deleted file mode 100644
index 06f26c9..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ /dev/null
@@ -1,1092 +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;
-
-import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
-import static com.android.systemui.statusbar.NotificationRemoteInputManager
-        .FORCE_REMOTE_INPUT_HISTORY;
-
-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.PowerManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationStats;
-import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
-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.R;
-import com.android.systemui.UiOffloadThread;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.statusbar.notification.InflationException;
-import com.android.systemui.statusbar.notification.NotificationInflater;
-import com.android.systemui.statusbar.notification.RowInflaterTask;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-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;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * 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 {
-    private static final String TAG = "NotificationEntryMgr";
-    protected static final boolean DEBUG = false;
-    protected static final boolean ENABLE_HEADS_UP = true;
-    protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
-
-    protected final NotificationMessagingUtil mMessagingUtil;
-    protected final Context mContext;
-    protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
-    protected final NotificationClicker mNotificationClicker = new NotificationClicker();
-    protected final ArraySet<NotificationData.Entry> mHeadsUpEntriesToRemoveOnSwitch =
-            new ArraySet<>();
-
-    // Dependencies:
-    protected final NotificationLockscreenUserManager mLockscreenUserManager =
-            Dependency.get(NotificationLockscreenUserManager.class);
-    protected final NotificationGroupManager mGroupManager =
-            Dependency.get(NotificationGroupManager.class);
-    protected final NotificationGutsManager mGutsManager =
-            Dependency.get(NotificationGutsManager.class);
-    protected final NotificationRemoteInputManager mRemoteInputManager =
-            Dependency.get(NotificationRemoteInputManager.class);
-    protected final NotificationMediaManager mMediaManager =
-            Dependency.get(NotificationMediaManager.class);
-    protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
-    protected final DeviceProvisionedController mDeviceProvisionedController =
-            Dependency.get(DeviceProvisionedController.class);
-    protected final VisualStabilityManager mVisualStabilityManager =
-            Dependency.get(VisualStabilityManager.class);
-    protected final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
-    protected final ForegroundServiceController mForegroundServiceController =
-            Dependency.get(ForegroundServiceController.class);
-    protected final NotificationListener mNotificationListener =
-            Dependency.get(NotificationListener.class);
-    private final SmartReplyController mSmartReplyController =
-            Dependency.get(SmartReplyController.class);
-
-    protected IStatusBarService mBarService;
-    protected NotificationPresenter mPresenter;
-    protected Callback mCallback;
-    protected PowerManager mPowerManager;
-    protected SystemServicesProxy mSystemServicesProxy;
-    protected NotificationListenerService.RankingMap mLatestRankingMap;
-    protected HeadsUpManager mHeadsUpManager;
-    protected NotificationData mNotificationData;
-    protected ContentObserver mHeadsUpObserver;
-    protected boolean mUseHeadsUp = false;
-    protected boolean mDisableNotificationAlerts;
-    protected NotificationListContainer mListContainer;
-    private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
-    /**
-     * Notifications with keys in this set are not actually around anymore. We kept them around
-     * when they were canceled in response to a remote input interaction. This allows us to show
-     * what you replied and allows you to continue typing into it.
-     */
-    private final ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
-
-
-    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;
-            }
-
-            mPresenter.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 (row.getProvider() != null && row.getProvider().isMenuVisible()) {
-                row.animateTranslateNotification(0);
-                return;
-            }
-
-            // Mark notification for one frame.
-            row.setJustClicked(true);
-            DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
-
-            mCallback.onNotificationClicked(sbn, row);
-        }
-
-        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 DeviceProvisionedController.DeviceProvisionedListener
-            mDeviceProvisionedListener =
-            new DeviceProvisionedController.DeviceProvisionedListener() {
-                @Override
-                public void onDeviceProvisionedChanged() {
-                    updateNotifications();
-                }
-            };
-
-    public NotificationListenerService.RankingMap getLatestRankingMap() {
-        return mLatestRankingMap;
-    }
-
-    public void setLatestRankingMap(NotificationListenerService.RankingMap latestRankingMap) {
-        mLatestRankingMap = latestRankingMap;
-    }
-
-    public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
-        mDisableNotificationAlerts = disableNotificationAlerts;
-        mHeadsUpObserver.onChange(true);
-    }
-
-    public void destroy() {
-        mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
-    }
-
-    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
-        if (!isHeadsUp && mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
-            removeNotification(entry.key, getLatestRankingMap());
-            mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
-            if (mHeadsUpEntriesToRemoveOnSwitch.isEmpty()) {
-                setLatestRankingMap(null);
-            }
-        } else {
-            updateNotificationRanking(null);
-        }
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("NotificationEntryManager state:");
-        pw.print("  mPendingNotifications=");
-        if (mPendingNotifications.size() == 0) {
-            pw.println("null");
-        } else {
-            for (NotificationData.Entry entry : mPendingNotifications.values()) {
-                pw.println(entry.notification);
-            }
-        }
-        pw.print("  mUseHeadsUp=");
-        pw.println(mUseHeadsUp);
-        pw.print("  mKeysKeptForRemoteInput: ");
-        pw.println(mKeysKeptForRemoteInput);
-    }
-
-    public NotificationEntryManager(Context context) {
-        mContext = context;
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        mBarService = IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-        mMessagingUtil = new NotificationMessagingUtil(context);
-        mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
-        mGroupManager.setPendingEntries(mPendingNotifications);
-    }
-
-    public void setUpWithPresenter(NotificationPresenter presenter,
-            NotificationListContainer listContainer, Callback callback,
-            HeadsUpManager headsUpManager) {
-        mPresenter = presenter;
-        mCallback = callback;
-        mNotificationData = new NotificationData(presenter);
-        mHeadsUpManager = headsUpManager;
-        mNotificationData.setHeadsUpManager(mHeadsUpManager);
-        mListContainer = listContainer;
-
-        mHeadsUpObserver = new ContentObserver(mPresenter.getHandler()) {
-            @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);
-        }
-
-        mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
-
-        mHeadsUpObserver.onChange(true); // set up
-        mOnAppOpsClickListener = mGutsManager::openGuts;
-    }
-
-    public NotificationData getNotificationData() {
-        return mNotificationData;
-    }
-
-    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.row != null) {
-            entry.reset();
-            updateNotification(entry, pmUser, sbn, entry.row);
-        } 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);
-        mRemoteInputManager.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());
-
-        if (FORCE_REMOTE_INPUT_HISTORY
-                && mKeysKeptForRemoteInput.contains(n.getKey())) {
-            mKeysKeptForRemoteInput.remove(n.getKey());
-        }
-
-        mRemoteInputManager.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 (isHeadsUp(n.getKey())) {
-                dismissalSurface = NotificationStats.DISMISSAL_PEEK;
-            } else if (mListContainer.hasPulsingNotifications()) {
-                dismissalSurface = NotificationStats.DISMISSAL_AOD;
-            }
-            mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(), dismissalSurface, nv);
-            removeNotification(n.getKey(), null);
-
-        } catch (RemoteException ex) {
-            // system process is dead if we're here.
-        }
-
-        mCallback.onPerformRemoveNotification(n);
-    }
-
-    /**
-     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
-     * about the failure.
-     *
-     * WARNING: this will call back into us.  Don't hold any locks.
-     */
-    void handleNotificationError(StatusBarNotification n, String message) {
-        removeNotification(n.getKey(), null);
-        try {
-            mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
-                    n.getInitialPid(), message, n.getUserId());
-        } catch (RemoteException ex) {
-            // The end is nigh.
-        }
-    }
-
-    private void abortExistingInflation(String key) {
-        if (mPendingNotifications.containsKey(key)) {
-            NotificationData.Entry entry = mPendingNotifications.get(key);
-            entry.abortTask();
-            mPendingNotifications.remove(key);
-        }
-        NotificationData.Entry addedEntry = mNotificationData.get(key);
-        if (addedEntry != null) {
-            addedEntry.abortTask();
-        }
-    }
-
-    @Override
-    public void handleInflationException(StatusBarNotification notification, Exception e) {
-        handleNotificationError(notification, e.getMessage());
-    }
-
-    private void addEntry(NotificationData.Entry shadeEntry) {
-        boolean isHeadsUped = shouldPeek(shadeEntry);
-        if (isHeadsUped) {
-            mHeadsUpManager.showNotification(shadeEntry);
-            // Mark as seen immediately
-            setNotificationShown(shadeEntry.notification);
-        }
-        addNotificationViews(shadeEntry);
-        mCallback.onNotificationAdded(shadeEntry);
-    }
-
-    @Override
-    public void onAsyncInflationFinished(NotificationData.Entry entry) {
-        mPendingNotifications.remove(entry.key);
-        // If there was an async task started after the removal, we don't want to add it back to
-        // the list, otherwise we might get leaks.
-        boolean isNew = mNotificationData.get(entry.key) == null;
-        if (isNew && !entry.row.isRemoved()) {
-            addEntry(entry);
-        } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
-            mVisualStabilityManager.onLowPriorityUpdated(entry);
-            mPresenter.updateNotificationViews();
-        }
-        entry.row.setLowPriorityStateUpdated(false);
-    }
-
-    @Override
-    public void removeNotification(String key, NotificationListenerService.RankingMap ranking) {
-        boolean deferRemoval = false;
-        abortExistingInflation(key);
-        if (mHeadsUpManager.isHeadsUp(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();
-            deferRemoval = !mHeadsUpManager.removeNotification(key,  ignoreEarliestRemovalTime);
-        }
-        mMediaManager.onNotificationRemoved(key);
-
-        NotificationData.Entry entry = mNotificationData.get(key);
-        if (FORCE_REMOTE_INPUT_HISTORY
-                && shouldKeepForRemoteInput(entry)
-                && entry.row != null && !entry.row.isDismissed()) {
-            CharSequence remoteInputText = entry.remoteInputText;
-            if (TextUtils.isEmpty(remoteInputText)) {
-                remoteInputText = entry.remoteInputTextWhenReset;
-            }
-            StatusBarNotification newSbn = rebuildNotificationWithRemoteInput(entry,
-                    remoteInputText, false /* showSpinner */);
-            boolean updated = false;
-            entry.onRemoteInputInserted();
-            try {
-                updateNotificationInternal(newSbn, null);
-                updated = true;
-            } catch (InflationException e) {
-                deferRemoval = false;
-            }
-            if (updated) {
-                Log.w(TAG, "Keeping notification around after sending remote input "+ entry.key);
-                addKeyKeptForRemoteInput(entry.key);
-                return;
-            }
-        }
-
-        if (FORCE_REMOTE_INPUT_HISTORY
-                && shouldKeepForSmartReply(entry)
-                && entry.row != null && !entry.row.isDismissed()) {
-            // Turn off the spinner and hide buttons when an app cancels the notification.
-            StatusBarNotification newSbn = rebuildNotificationForCanceledSmartReplies(entry);
-            boolean updated = false;
-            try {
-                updateNotificationInternal(newSbn, null);
-                updated = true;
-            } catch (InflationException e) {
-                // Ignore just don't keep the notification around.
-            }
-            // Treat the reply as longer sending.
-            mSmartReplyController.stopSending(entry);
-            if (updated) {
-                Log.w(TAG, "Keeping notification around after sending smart reply " + entry.key);
-                addKeyKeptForRemoteInput(entry.key);
-                return;
-            }
-        }
-
-        // Actually removing notification so smart reply controller can forget about it.
-        mSmartReplyController.stopSending(entry);
-
-        if (deferRemoval) {
-            mLatestRankingMap = ranking;
-            mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
-            return;
-        }
-
-        if (mRemoteInputManager.onRemoveNotification(entry)) {
-            mLatestRankingMap = ranking;
-            return;
-        }
-
-        if (entry != null && mGutsManager.getExposedGuts() != null
-                && mGutsManager.getExposedGuts() == entry.row.getGuts()
-                && entry.row.getGuts() != null && !entry.row.getGuts().isLeavebehind()) {
-            Log.w(TAG, "Keeping notification because it's showing guts. " + key);
-            mLatestRankingMap = ranking;
-            mGutsManager.setKeyToRemoveOnGutsClosed(key);
-            return;
-        }
-
-        if (entry != null) {
-            mForegroundServiceController.removeNotification(entry.notification);
-        }
-
-        if (entry != null && entry.row != null) {
-            entry.row.setRemoved();
-            mListContainer.cleanUpViewState(entry.row);
-        }
-        // Let's remove the children if this was a summary
-        handleGroupSummaryRemoved(key);
-        StatusBarNotification old = removeNotificationViews(key, ranking);
-
-        mCallback.onNotificationRemoved(key, old);
-    }
-
-    public StatusBarNotification rebuildNotificationWithRemoteInput(NotificationData.Entry entry,
-            CharSequence remoteInputText, boolean showSpinner) {
-        StatusBarNotification sbn = entry.notification;
-
-        Notification.Builder b = Notification.Builder
-                .recoverBuilder(mContext, sbn.getNotification().clone());
-        if (remoteInputText != null) {
-            CharSequence[] oldHistory = sbn.getNotification().extras
-                    .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
-            CharSequence[] newHistory;
-            if (oldHistory == null) {
-                newHistory = new CharSequence[1];
-            } else {
-                newHistory = new CharSequence[oldHistory.length + 1];
-                System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length);
-            }
-            newHistory[0] = String.valueOf(remoteInputText);
-            b.setRemoteInputHistory(newHistory);
-        }
-        b.setShowRemoteInputSpinner(showSpinner);
-        b.setHideSmartReplies(true);
-
-        Notification newNotification = b.build();
-
-        // Undo any compatibility view inflation
-        newNotification.contentView = sbn.getNotification().contentView;
-        newNotification.bigContentView = sbn.getNotification().bigContentView;
-        newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
-
-        StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(),
-                sbn.getOpPkg(),
-                sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
-                newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
-        return newSbn;
-    }
-
-    @VisibleForTesting
-    StatusBarNotification rebuildNotificationForCanceledSmartReplies(
-            NotificationData.Entry entry) {
-        return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */,
-                false /* showSpinner */);
-    }
-
-    private boolean shouldKeepForSmartReply(NotificationData.Entry entry) {
-        return entry != null && mSmartReplyController.isSendingSmartReply(entry.key);
-    }
-
-    private boolean shouldKeepForRemoteInput(NotificationData.Entry entry) {
-        if (entry == null) {
-            return false;
-        }
-        if (mRemoteInputManager.getController().isSpinning(entry.key)) {
-            return true;
-        }
-        if (entry.hasJustSentRemoteInput()) {
-            return true;
-        }
-        return false;
-    }
-
-    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;
-    }
-
-    /**
-     * Ensures that the group children are cancelled immediately when the group summary is cancelled
-     * instead of waiting for the notification manager to send all cancels. Otherwise this could
-     * lead to flickers.
-     *
-     * This also ensures that the animation looks nice and only consists of a single disappear
-     * animation instead of multiple.
-     *  @param key the key of the notification was removed
-     *
-     */
-    private void handleGroupSummaryRemoved(String key) {
-        NotificationData.Entry entry = mNotificationData.get(key);
-        if (entry != null && entry.row != null
-                && entry.row.isSummaryWithChildren()) {
-            if (entry.notification.getOverrideGroupKey() != null && !entry.row.isDismissed()) {
-                // 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<ExpandableNotificationRow> notificationChildren =
-                    entry.row.getNotificationChildren();
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow row = notificationChildren.get(i);
-                if ((row.getStatusBarNotification().getNotification().flags
-                        & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
-                    // the child is a foreground service notification which we can't remove!
-                    continue;
-                }
-                row.setKeepInParent(true);
-                // we need to set this state earlier as otherwise we might generate some weird
-                // animations
-                row.setRemoved();
-            }
-        }
-    }
-
-    public void updateNotificationsOnDensityOrFontScaleChanged() {
-        ArrayList<NotificationData.Entry> userNotifications =
-                mNotificationData.getNotificationsForCurrentUser();
-        for (int i = 0; i < userNotifications.size(); i++) {
-            NotificationData.Entry entry = userNotifications.get(i);
-            boolean exposedGuts = mGutsManager.getExposedGuts() != null
-                    && entry.row.getGuts() == mGutsManager.getExposedGuts();
-            entry.row.onDensityOrFontScaleChanged();
-            if (exposedGuts) {
-                mGutsManager.onDensityOrFontScaleChanged(entry.row);
-            }
-        }
-    }
-
-    protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
-            StatusBarNotification sbn, ExpandableNotificationRow row) {
-        row.setNeedsRedaction(mLockscreenUserManager.needsRedaction(entry));
-        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.row = row;
-        entry.row.setOnActivatedListener(mPresenter);
-
-        boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
-                mNotificationData.getImportance(sbn.getKey()));
-        boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
-                && !mPresenter.isPresenterFullyCollapsed();
-        row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
-        row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
-        row.updateNotification(entry);
-    }
-
-
-    protected void addNotificationViews(NotificationData.Entry entry) {
-        if (entry == null) {
-            return;
-        }
-        // Add the expanded view and icon.
-        mNotificationData.add(entry);
-        tagForeground(entry.notification);
-        updateNotifications();
-    }
-
-    protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
-            throws InflationException {
-        if (DEBUG) {
-            Log.d(TAG, "createNotificationViews(notification=" + sbn);
-        }
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
-        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 ranking) throws InflationException {
-        String key = notification.getKey();
-        if (DEBUG) Log.d(TAG, "addNotification key=" + key);
-
-        mNotificationData.updateRanking(ranking);
-        NotificationData.Entry shadeEntry = createNotificationViews(notification);
-        boolean isHeadsUped = shouldPeek(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)
-                SystemServicesProxy.getInstance(mContext).awakenDreamsAsync();
-
-                // 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) {
-                }
-            }
-        }
-        abortExistingInflation(key);
-
-        mForegroundServiceController.addNotification(notification,
-                mNotificationData.getImportance(key));
-
-        mPendingNotifications.put(key, shadeEntry);
-        mGroupManager.onPendingEntryAdded(shadeEntry);
-    }
-
-    @VisibleForTesting
-    protected void tagForeground(StatusBarNotification notification) {
-        ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
-                notification.getUserId(), notification.getPackageName());
-        if (activeOps != null) {
-            int N = activeOps.size();
-            for (int i = 0; i < N; i++) {
-                updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(),
-                        notification.getPackageName(), true);
-            }
-        }
-    }
-
-    @Override
-    public void addNotification(StatusBarNotification notification,
-            NotificationListenerService.RankingMap ranking) {
-        try {
-            addNotificationInternal(notification, ranking);
-        } catch (InflationException e) {
-            handleInflationException(notification, e);
-        }
-    }
-
-    public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) {
-        String foregroundKey = mForegroundServiceController.getStandardLayoutKey(
-                UserHandle.getUserId(uid), pkg);
-        if (foregroundKey != null) {
-            mNotificationData.updateAppOp(appOp, uid, pkg, foregroundKey, showIcon);
-            updateNotifications();
-        }
-    }
-
-    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);
-        if (entry == null) {
-            return;
-        }
-        mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
-        mRemoteInputManager.onUpdateNotification(entry);
-        mSmartReplyController.stopSending(entry);
-
-        if (key.equals(mGutsManager.getKeyToRemoveOnGutsClosed())) {
-            mGutsManager.setKeyToRemoveOnGutsClosed(null);
-            Log.w(TAG, "Notification that was kept for guts was updated. " + key);
-        }
-
-        Notification n = notification.getNotification();
-        mNotificationData.updateRanking(ranking);
-
-        final StatusBarNotification oldNotification = entry.notification;
-        entry.notification = notification;
-        mGroupManager.onEntryUpdated(entry, oldNotification);
-
-        entry.updateIcons(mContext, notification);
-        inflateViews(entry, mListContainer.getViewParentForNotification(entry));
-
-        mForegroundServiceController.updateNotification(notification,
-                mNotificationData.getImportance(key));
-
-        boolean shouldPeek = shouldPeek(entry, notification);
-        boolean alertAgain = alertAgain(entry, n);
-
-        updateHeadsUp(key, entry, shouldPeek, alertAgain);
-        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.row);
-        }
-
-        if (DEBUG) {
-            // Is this for you?
-            boolean isForCurrentUser = mPresenter.isNotificationForCurrentProfiles(notification);
-            Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
-        }
-
-        mCallback.onNotificationUpdated(notification);
-    }
-
-    @Override
-    public void updateNotification(StatusBarNotification notification,
-            NotificationListenerService.RankingMap ranking) {
-        try {
-            updateNotificationInternal(notification, ranking);
-        } catch (InflationException e) {
-            handleInflationException(notification, e);
-        }
-    }
-
-    public void updateNotifications() {
-        mNotificationData.filterAndSort();
-
-        mPresenter.updateNotificationViews();
-    }
-
-    public void updateNotificationRanking(NotificationListenerService.RankingMap ranking) {
-        mNotificationData.updateRanking(ranking);
-        updateNotifications();
-    }
-
-    protected boolean shouldPeek(NotificationData.Entry entry) {
-        return shouldPeek(entry, entry.notification);
-    }
-
-    public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) {
-        if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
-            if (DEBUG) Log.d(TAG, "No peeking: no huns or vr mode");
-            return false;
-        }
-
-        if (mNotificationData.shouldFilterOut(entry)) {
-            if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
-            return false;
-        }
-
-        boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming();
-
-        if (!inUse && !mPresenter.isDozing()) {
-            if (DEBUG) {
-                Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(entry)) {
-            if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
-            return false;
-        }
-
-        // Peeking triggers an ambient display pulse, so disable peek is ambient is active
-        if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(entry)) {
-            if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
-            return false;
-        }
-
-        if (entry.hasJustLaunchedFullScreenIntent()) {
-            if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
-            return false;
-        }
-
-        if (isSnoozedPackage(sbn)) {
-            if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
-            return false;
-        }
-
-        // Allow peeking for DEFAULT notifications only if we're on Ambient Display.
-        int importanceLevel = mPresenter.isDozing() ? NotificationManager.IMPORTANCE_DEFAULT
-                : NotificationManager.IMPORTANCE_HIGH;
-        if (mNotificationData.getImportance(sbn.getKey()) < importanceLevel) {
-            if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
-            return false;
-        }
-
-        // Don't peek notifications that are suppressed due to group alert behavior
-        if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
-            if (DEBUG) Log.d(TAG, "No peeking: suppressed due to group alert behavior");
-            return false;
-        }
-
-        if (!mCallback.shouldPeek(entry, sbn)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    protected void setNotificationShown(StatusBarNotification n) {
-        setNotificationsShown(new String[]{n.getKey()});
-    }
-
-    protected void setNotificationsShown(String[] keys) {
-        try {
-            mNotificationListener.setNotificationsShown(keys);
-        } catch (RuntimeException e) {
-            Log.d(TAG, "failed setNotificationsShown: ", e);
-        }
-    }
-
-    protected boolean isSnoozedPackage(StatusBarNotification sbn) {
-        return mHeadsUpManager.isSnoozed(sbn.getPackageName());
-    }
-
-    protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldPeek,
-            boolean alertAgain) {
-        final boolean wasHeadsUp = isHeadsUp(key);
-        if (wasHeadsUp) {
-            if (!shouldPeek) {
-                // We don't want this to be interrupting anymore, lets remove it
-                mHeadsUpManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
-            } else {
-                mHeadsUpManager.updateNotification(entry, alertAgain);
-            }
-        } else if (shouldPeek && alertAgain) {
-            // This notification was updated to be a heads-up, show it!
-            mHeadsUpManager.showNotification(entry);
-        }
-    }
-
-    protected boolean isHeadsUp(String key) {
-        return mHeadsUpManager.isHeadsUp(key);
-    }
-
-    public boolean isNotificationKeptForRemoteInput(String key) {
-        return mKeysKeptForRemoteInput.contains(key);
-    }
-
-    public void removeKeyKeptForRemoteInput(String key) {
-        mKeysKeptForRemoteInput.remove(key);
-    }
-
-    public void addKeyKeptForRemoteInput(String key) {
-        if (FORCE_REMOTE_INPUT_HISTORY) {
-            mKeysKeptForRemoteInput.add(key);
-        }
-    }
-
-    /**
-     * 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 notification is clicked.
-         *
-         * @param sbn notification that was clicked
-         * @param row row for that notification
-         */
-        void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row);
-
-        /**
-         * 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 should peek this notification.
-         *
-         * @param entry entry of the notification that might be peeked
-         * @param sbn notification that might be peeked
-         * @return true if the notification should be peeked
-         */
-        boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
deleted file mode 100644
index 4a8f4bbf..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ /dev/null
@@ -1,428 +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
- */
-
-package com.android.systemui.statusbar;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import androidx.annotation.Nullable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.FrameLayout;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
-
-/**
- * The guts of a notification revealed when performing a long press.
- */
-public class NotificationGuts extends FrameLayout {
-    private static final String TAG = "NotificationGuts";
-    private static final long CLOSE_GUTS_DELAY = 8000;
-
-    private Drawable mBackground;
-    private int mClipTopAmount;
-    private int mClipBottomAmount;
-    private int mActualHeight;
-    private boolean mExposed;
-
-    private Handler mHandler;
-    private Runnable mFalsingCheck;
-    private boolean mNeedsFalsingProtection;
-    private OnGutsClosedListener mClosedListener;
-    private OnHeightChangedListener mHeightListener;
-
-    private GutsContent mGutsContent;
-
-    public interface GutsContent {
-
-        public void setGutsParent(NotificationGuts listener);
-
-        /**
-         * Return the view to be shown in the notification guts.
-         */
-        public View getContentView();
-
-        /**
-         * Return the actual height of the content.
-         */
-        public int getActualHeight();
-
-        /**
-         * Called when the guts view have been told to close, typically after an outside
-         * interaction.
-         *
-         * @param save whether the state should be saved.
-         * @param force whether the guts view should be forced closed regardless of state.
-         * @return if closing the view has been handled.
-         */
-        public boolean handleCloseControls(boolean save, boolean force);
-
-        /**
-         * Return whether the notification associated with these guts is set to be removed.
-         */
-        public boolean willBeRemoved();
-
-        /**
-         * Return whether these guts are a leavebehind (e.g. {@link NotificationSnooze}).
-         */
-        public default boolean isLeavebehind() {
-            return false;
-        }
-
-        /**
-         * Return whether something changed and needs to be saved, possibly requiring a bouncer.
-         */
-        boolean shouldBeSaved();
-    }
-
-    public interface OnGutsClosedListener {
-        public void onGutsClosed(NotificationGuts guts);
-    }
-
-    public interface OnHeightChangedListener {
-        public void onHeightChanged(NotificationGuts guts);
-    }
-
-    interface OnSettingsClickListener {
-        void onClick(View v, int appUid);
-    }
-
-    public NotificationGuts(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setWillNotDraw(false);
-        mHandler = new Handler();
-        mFalsingCheck = new Runnable() {
-            @Override
-            public void run() {
-                if (mNeedsFalsingProtection && mExposed) {
-                    closeControls(-1 /* x */, -1 /* y */, false /* save */, false /* force */);
-                }
-            }
-        };
-        final TypedArray ta = context.obtainStyledAttributes(attrs,
-                com.android.internal.R.styleable.Theme, 0, 0);
-        ta.recycle();
-    }
-
-    public NotificationGuts(Context context) {
-        this(context, null);
-    }
-
-    public void setGutsContent(GutsContent content) {
-        mGutsContent = content;
-        removeAllViews();
-        addView(mGutsContent.getContentView());
-    }
-
-    public GutsContent getGutsContent() {
-        return mGutsContent;
-    }
-
-    public void resetFalsingCheck() {
-        mHandler.removeCallbacks(mFalsingCheck);
-        if (mNeedsFalsingProtection && mExposed) {
-            mHandler.postDelayed(mFalsingCheck, CLOSE_GUTS_DELAY);
-        }
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        draw(canvas, mBackground);
-    }
-
-    private void draw(Canvas canvas, Drawable drawable) {
-        int top = mClipTopAmount;
-        int bottom = mActualHeight - mClipBottomAmount;
-        if (drawable != null && top < bottom) {
-            drawable.setBounds(0, top, getWidth(), bottom);
-            drawable.draw(canvas);
-        }
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mBackground = mContext.getDrawable(R.drawable.notification_guts_bg);
-        if (mBackground != null) {
-            mBackground.setCallback(this);
-        }
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return super.verifyDrawable(who) || who == mBackground;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        drawableStateChanged(mBackground);
-    }
-
-    private void drawableStateChanged(Drawable d) {
-        if (d != null && d.isStateful()) {
-            d.setState(getDrawableState());
-        }
-    }
-
-    @Override
-    public void drawableHotspotChanged(float x, float y) {
-        if (mBackground != null) {
-            mBackground.setHotspot(x, y);
-        }
-    }
-
-    public void openControls(
-            boolean shouldDoCircularReveal,
-            int x,
-            int y,
-            boolean needsFalsingProtection,
-            @Nullable Runnable onAnimationEnd) {
-        animateOpen(shouldDoCircularReveal, x, y, onAnimationEnd);
-        setExposed(true /* exposed */, needsFalsingProtection);
-    }
-
-    /**
-     * Hide controls if they are visible
-     * @param leavebehinds true if leavebehinds should be closed
-     * @param controls true if controls should be closed
-     * @param x x coordinate to animate the close circular reveal with
-     * @param y y coordinate to animate the close circular reveal with
-     * @param force whether the guts should be force-closed regardless of state.
-     */
-    public void closeControls(boolean leavebehinds, boolean controls, int x, int y, boolean force) {
-        if (mGutsContent != null) {
-            if ((mGutsContent.isLeavebehind() && leavebehinds)
-                    || (!mGutsContent.isLeavebehind() && controls)) {
-                closeControls(x, y, mGutsContent.shouldBeSaved(), force);
-            }
-        }
-    }
-
-    /**
-     * Closes any exposed guts/views.
-     *
-     * @param x x coordinate to animate the close circular reveal with
-     * @param y y coordinate to animate the close circular reveal with
-     * @param save whether the state should be saved
-     * @param force whether the guts should be force-closed regardless of state.
-     */
-    public void closeControls(int x, int y, boolean save, boolean force) {
-        // First try to dismiss any blocking helper.
-        boolean wasBlockingHelperDismissed =
-                Dependency.get(NotificationBlockingHelperManager.class)
-                        .dismissCurrentBlockingHelper();
-
-        if (getWindowToken() == null) {
-            if (mClosedListener != null) {
-                mClosedListener.onGutsClosed(this);
-            }
-            return;
-        }
-
-        if (mGutsContent == null
-                || !mGutsContent.handleCloseControls(save, force)
-                || wasBlockingHelperDismissed) {
-            // We only want to do a circular reveal if we're not showing the blocking helper.
-            animateClose(x, y, !wasBlockingHelperDismissed /* shouldDoCircularReveal */);
-
-            setExposed(false, mNeedsFalsingProtection);
-            if (mClosedListener != null) {
-                mClosedListener.onGutsClosed(this);
-            }
-        }
-    }
-
-    /** Animates in the guts view via either a fade or a circular reveal. */
-    private void animateOpen(
-            boolean shouldDoCircularReveal, int x, int y, @Nullable Runnable onAnimationEnd) {
-        if (isAttachedToWindow()) {
-            if (shouldDoCircularReveal) {
-                double horz = Math.max(getWidth() - x, x);
-                double vert = Math.max(getHeight() - y, y);
-                float r = (float) Math.hypot(horz, vert);
-                // Circular reveal originating at (x, y)
-                Animator a = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
-                a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-                a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-                a.addListener(new AnimateOpenListener(onAnimationEnd));
-                a.start();
-            } else {
-                // Fade in content
-                this.setAlpha(0f);
-                this.animate()
-                        .alpha(1f)
-                        .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
-                        .setInterpolator(Interpolators.ALPHA_IN)
-                        .setListener(new AnimateOpenListener(onAnimationEnd))
-                        .start();
-            }
-        } else {
-            Log.w(TAG, "Failed to animate guts open");
-        }
-    }
-
-
-    /** Animates out the guts view via either a fade or a circular reveal. */
-    @VisibleForTesting
-    void animateClose(int x, int y, boolean shouldDoCircularReveal) {
-        if (isAttachedToWindow()) {
-            if (shouldDoCircularReveal) {
-                // Circular reveal originating at (x, y)
-                if (x == -1 || y == -1) {
-                    x = (getLeft() + getRight()) / 2;
-                    y = (getTop() + getHeight() / 2);
-                }
-                double horz = Math.max(getWidth() - x, x);
-                double vert = Math.max(getHeight() - y, y);
-                float r = (float) Math.hypot(horz, vert);
-                Animator a = ViewAnimationUtils.createCircularReveal(this,
-                        x, y, r, 0);
-                a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-                a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-                a.addListener(new AnimateCloseListener(this /* view */));
-                a.start();
-            } else {
-                // Fade in the blocking helper.
-                this.animate()
-                        .alpha(0f)
-                        .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
-                        .setInterpolator(Interpolators.ALPHA_OUT)
-                        .setListener(new AnimateCloseListener(this /* view */))
-                        .start();
-            }
-        } else {
-            Log.w(TAG, "Failed to animate guts close");
-        }
-    }
-
-    public void setActualHeight(int actualHeight) {
-        mActualHeight = actualHeight;
-        invalidate();
-    }
-
-    public int getActualHeight() {
-        return mActualHeight;
-    }
-
-    public int getIntrinsicHeight() {
-        return mGutsContent != null && mExposed ? mGutsContent.getActualHeight() : getHeight();
-    }
-
-    public void setClipTopAmount(int clipTopAmount) {
-        mClipTopAmount = clipTopAmount;
-        invalidate();
-    }
-
-    public void setClipBottomAmount(int clipBottomAmount) {
-        mClipBottomAmount = clipBottomAmount;
-        invalidate();
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        // Prevents this view from creating a layer when alpha is animating.
-        return false;
-    }
-
-    public void setClosedListener(OnGutsClosedListener listener) {
-        mClosedListener = listener;
-    }
-
-    public void setHeightChangedListener(OnHeightChangedListener listener) {
-        mHeightListener = listener;
-    }
-
-    protected void onHeightChanged() {
-        if (mHeightListener != null) {
-            mHeightListener.onHeightChanged(this);
-        }
-    }
-
-    @VisibleForTesting
-    void setExposed(boolean exposed, boolean needsFalsingProtection) {
-        final boolean wasExposed = mExposed;
-        mExposed = exposed;
-        mNeedsFalsingProtection = needsFalsingProtection;
-        if (mExposed && mNeedsFalsingProtection) {
-            resetFalsingCheck();
-        } else {
-            mHandler.removeCallbacks(mFalsingCheck);
-        }
-        if (wasExposed != mExposed && mGutsContent != null) {
-            final View contentView = mGutsContent.getContentView();
-            contentView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-            if (mExposed) {
-                contentView.requestAccessibilityFocus();
-            }
-        }
-    }
-
-    public boolean willBeRemoved() {
-        return mGutsContent != null ? mGutsContent.willBeRemoved() : false;
-    }
-
-    public boolean isExposed() {
-        return mExposed;
-    }
-
-    public boolean isLeavebehind() {
-        return mGutsContent != null && mGutsContent.isLeavebehind();
-    }
-
-    /** Listener for animations executed in {@link #animateOpen(boolean, int, int, Runnable)}. */
-    private static class AnimateOpenListener extends AnimatorListenerAdapter {
-        final Runnable mOnAnimationEnd;
-
-        private AnimateOpenListener(Runnable onAnimationEnd) {
-            mOnAnimationEnd = onAnimationEnd;
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            super.onAnimationEnd(animation);
-            if (mOnAnimationEnd != null) {
-                mOnAnimationEnd.run();
-            }
-        }
-    }
-
-    /** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */
-    private static class AnimateCloseListener extends AnimatorListenerAdapter {
-        final View mView;
-
-        private AnimateCloseListener(View view) {
-            mView = view;
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            super.onAnimationEnd(animation);
-            mView.setVisibility(View.GONE);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
deleted file mode 100644
index 91a381f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ /dev/null
@@ -1,415 +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;
-
-import static android.app.AppOpsManager.OP_CAMERA;
-import static android.app.AppOpsManager.OP_RECORD_AUDIO;
-import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEGATIVE;
-
-import android.app.INotificationManager;
-import android.app.NotificationChannel;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.notification.StatusBarNotification;
-import androidx.annotation.VisibleForTesting;
-import android.util.ArraySet;
-import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.systemui.Dependency;
-import com.android.systemui.Dumpable;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.phone.StatusBar;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * 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.
- */
-public class NotificationGutsManager implements Dumpable {
-    private static final String TAG = "NotificationGutsManager";
-
-    // Must match constant in Settings. Used to highlight preferences when linking to Settings.
-    private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
-
-    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
-    private final Context mContext;
-    private final AccessibilityManager mAccessibilityManager;
-
-    // Dependencies:
-    private final NotificationLockscreenUserManager mLockscreenUserManager =
-            Dependency.get(NotificationLockscreenUserManager.class);
-
-    // which notification is currently being longpress-examined by the user
-    private NotificationGuts mNotificationGutsExposed;
-    private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
-    protected NotificationPresenter mPresenter;
-    protected NotificationEntryManager mEntryManager;
-    private NotificationListContainer mListContainer;
-    private NotificationInfo.CheckSaveListener mCheckSaveListener;
-    private OnSettingsClickListener mOnSettingsClickListener;
-    private String mKeyToRemoveOnGutsClosed;
-
-    public NotificationGutsManager(Context context) {
-        mContext = context;
-        Resources res = context.getResources();
-
-        mAccessibilityManager = (AccessibilityManager)
-                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-    }
-
-    public void setUpWithPresenter(NotificationPresenter presenter,
-            NotificationEntryManager entryManager, NotificationListContainer listContainer,
-            NotificationInfo.CheckSaveListener checkSaveListener,
-            OnSettingsClickListener onSettingsClickListener) {
-        mPresenter = presenter;
-        mEntryManager = entryManager;
-        mListContainer = listContainer;
-        mCheckSaveListener = checkSaveListener;
-        mOnSettingsClickListener = onSettingsClickListener;
-    }
-
-    public String getKeyToRemoveOnGutsClosed() {
-        return mKeyToRemoveOnGutsClosed;
-    }
-
-    public void setKeyToRemoveOnGutsClosed(String keyToRemoveOnGutsClosed) {
-        mKeyToRemoveOnGutsClosed = keyToRemoveOnGutsClosed;
-    }
-
-    public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) {
-        setExposedGuts(row.getGuts());
-        bindGuts(row);
-    }
-
-    /**
-     * Sends an intent to open the app settings for a particular package and optional
-     * channel.
-     */
-    private void startAppNotificationSettingsActivity(String packageName, final int appUid,
-            final NotificationChannel channel, ExpandableNotificationRow row) {
-        final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
-        intent.setData(Uri.fromParts("package", packageName, null));
-        intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
-        intent.putExtra(Settings.EXTRA_APP_UID, appUid);
-        if (channel != null) {
-            intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
-        }
-        mPresenter.startNotificationGutsIntent(intent, appUid, row);
-    }
-
-    protected void startAppOpsSettingsActivity(String pkg, int uid, ArraySet<Integer> ops,
-            ExpandableNotificationRow row) {
-        if (ops.contains(OP_SYSTEM_ALERT_WINDOW)) {
-            if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
-                startAppNotificationSettingsActivity(pkg, uid, null, row);
-            } else {
-                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
-                intent.setData(Uri.fromParts("package", pkg, null));
-                mPresenter.startNotificationGutsIntent(intent, uid, row);
-            }
-        } else if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
-            Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
-            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, pkg);
-            mPresenter.startNotificationGutsIntent(intent, uid, row);
-        }
-    }
-
-    public void bindGuts(final ExpandableNotificationRow row) {
-        bindGuts(row, mGutsMenuItem);
-    }
-
-    private void bindGuts(final ExpandableNotificationRow row,
-            NotificationMenuRowPlugin.MenuItem item) {
-        StatusBarNotification sbn = row.getStatusBarNotification();
-
-        row.inflateGuts();
-        row.setGutsView(item);
-        row.setTag(sbn.getPackageName());
-        row.getGuts().setClosedListener((NotificationGuts g) -> {
-            row.onGutsClosed();
-            if (!g.willBeRemoved() && !row.isRemoved()) {
-                mListContainer.onHeightChanged(
-                        row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
-            }
-            if (mNotificationGutsExposed == g) {
-                mNotificationGutsExposed = null;
-                mGutsMenuItem = null;
-            }
-            String key = sbn.getKey();
-            if (key.equals(mKeyToRemoveOnGutsClosed)) {
-                mKeyToRemoveOnGutsClosed = null;
-                mEntryManager.removeNotification(key, mEntryManager.getLatestRankingMap());
-            }
-        });
-
-        View gutsView = item.getGutsView();
-        if (gutsView instanceof NotificationSnooze) {
-            initializeSnoozeView(row, (NotificationSnooze) gutsView);
-        } else if (gutsView instanceof AppOpsInfo) {
-            initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
-        } else if (gutsView instanceof NotificationInfo) {
-            initializeNotificationInfo(row, (NotificationInfo) gutsView);
-        }
-    }
-
-    /**
-     * Sets up the {@link NotificationSnooze} inside the notification row's guts.
-     *
-     * @param row view to set up the guts for
-     * @param notificationSnoozeView view to set up/bind within {@code row}
-     */
-    private void initializeSnoozeView(
-            final ExpandableNotificationRow row,
-            NotificationSnooze notificationSnoozeView) {
-        NotificationGuts guts = row.getGuts();
-        StatusBarNotification sbn = row.getStatusBarNotification();
-
-        notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
-        notificationSnoozeView.setStatusBarNotification(sbn);
-        notificationSnoozeView.setSnoozeOptions(row.getEntry().snoozeCriteria);
-        guts.setHeightChangedListener((NotificationGuts g) -> {
-            mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
-        });
-    }
-
-    /**
-     * Sets up the {@link AppOpsInfo} inside the notification row's guts.
-     *
-     * @param row view to set up the guts for
-     * @param appOpsInfoView view to set up/bind within {@code row}
-     */
-    private void initializeAppOpsInfo(
-            final ExpandableNotificationRow row,
-            AppOpsInfo appOpsInfoView) {
-        NotificationGuts guts = row.getGuts();
-        StatusBarNotification sbn = row.getStatusBarNotification();
-        UserHandle userHandle = sbn.getUser();
-        PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
-                userHandle.getIdentifier());
-
-        AppOpsInfo.OnSettingsClickListener onSettingsClick =
-                (View v, String pkg, int uid, ArraySet<Integer> ops) -> {
-            mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
-            guts.resetFalsingCheck();
-            startAppOpsSettingsActivity(pkg, uid, ops, row);
-        };
-        if (!row.getEntry().mActiveAppOps.isEmpty()) {
-            appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
-        }
-    }
-
-    /**
-     * Sets up the {@link NotificationInfo} inside the notification row's guts.
-     *
-     * @param row view to set up the guts for
-     * @param notificationInfoView view to set up/bind within {@code row}
-     */
-    @VisibleForTesting
-    void initializeNotificationInfo(
-            final ExpandableNotificationRow row,
-            NotificationInfo notificationInfoView) {
-        NotificationGuts guts = row.getGuts();
-        StatusBarNotification sbn = row.getStatusBarNotification();
-        String packageName = sbn.getPackageName();
-        // Settings link is only valid for notifications that specify a non-system user
-        NotificationInfo.OnSettingsClickListener onSettingsClick = null;
-        UserHandle userHandle = sbn.getUser();
-        PackageManager pmUser = StatusBar.getPackageManagerForUser(
-                mContext, userHandle.getIdentifier());
-        INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-        final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick =
-                (View v, Intent intent) -> {
-                    mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
-                    guts.resetFalsingCheck();
-                    mPresenter.startNotificationGutsIntent(intent, sbn.getUid(), row);
-                };
-        boolean isForBlockingHelper = row.isBlockingHelperShowing();
-
-        if (!userHandle.equals(UserHandle.ALL)
-                || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
-            onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
-                mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
-                guts.resetFalsingCheck();
-                mOnSettingsClickListener.onClick(sbn.getKey());
-                startAppNotificationSettingsActivity(packageName, appUid, channel, row);
-            };
-        }
-
-        try {
-            notificationInfoView.bindNotification(
-                    pmUser,
-                    iNotificationManager,
-                    packageName,
-                    row.getEntry().channel,
-                    row.getNumUniqueChannels(),
-                    sbn,
-                    mCheckSaveListener,
-                    onSettingsClick,
-                    onAppSettingsClick,
-                    row.getIsNonblockable(),
-                    isForBlockingHelper,
-                    row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.toString());
-        }
-    }
-
-    /**
-     * Closes guts or notification menus that might be visible and saves any changes.
-     *
-     * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
-     * @param force true if guts should be closed regardless of state (used for snooze only).
-     * @param removeControls true if controls (e.g. info) should be closed.
-     * @param x if closed based on touch location, this is the x touch location.
-     * @param y if closed based on touch location, this is the y touch location.
-     * @param resetMenu if any notification menus that might be revealed should be closed.
-     */
-    public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
-            int x, int y, boolean resetMenu) {
-        if (mNotificationGutsExposed != null) {
-            mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
-        }
-        if (resetMenu) {
-            mListContainer.resetExposedMenuView(false /* animate */, true /* force */);
-        }
-    }
-
-    /**
-     * Returns the exposed NotificationGuts or null if none are exposed.
-     */
-    public NotificationGuts getExposedGuts() {
-        return mNotificationGutsExposed;
-    }
-
-    public void setExposedGuts(NotificationGuts guts) {
-        mNotificationGutsExposed = guts;
-    }
-
-    /**
-     * Opens guts on the given ExpandableNotificationRow {@code view}. This handles opening guts for
-     * the normal half-swipe and long-press use cases via a circular reveal. When the blocking
-     * helper needs to be shown on the row, this will skip the circular reveal.
-     *
-     * @param view ExpandableNotificationRow to open guts on
-     * @param x x coordinate of origin of circular reveal
-     * @param y y coordinate of origin of circular reveal
-     * @param menuItem MenuItem the guts should display
-     * @return true if guts was opened
-     */
-    boolean openGuts(
-            View view,
-            int x,
-            int y,
-            NotificationMenuRowPlugin.MenuItem menuItem) {
-        if (!(view instanceof ExpandableNotificationRow)) {
-            return false;
-        }
-
-        if (view.getWindowToken() == null) {
-            Log.e(TAG, "Trying to show notification guts, but not attached to window");
-            return false;
-        }
-
-        final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-        if (row.isDark()) {
-            return false;
-        }
-        view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-        if (row.areGutsExposed()) {
-            closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
-                    true /* removeControls */, -1 /* x */, -1 /* y */,
-                    true /* resetMenu */);
-            return false;
-        }
-        bindGuts(row, menuItem);
-        NotificationGuts guts = row.getGuts();
-
-        // Assume we are a status_bar_notification_row
-        if (guts == null) {
-            // This view has no guts. Examples are the more card or the dismiss all view
-            return false;
-        }
-
-        mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS);
-
-        // ensure that it's laid but not visible until actually laid out
-        guts.setVisibility(View.INVISIBLE);
-        // Post to ensure the the guts are properly laid out.
-        guts.post(new Runnable() {
-            @Override
-            public void run() {
-                if (row.getWindowToken() == null) {
-                    Log.e(TAG, "Trying to show notification guts in post(), but not attached to "
-                            + "window");
-                    return;
-                }
-                closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
-                        true /* removeControls */, -1 /* x */, -1 /* y */,
-                        false /* resetMenu */);
-                guts.setVisibility(View.VISIBLE);
-
-                final boolean needsFalsingProtection =
-                        (mPresenter.isPresenterLocked() &&
-                                !mAccessibilityManager.isTouchExplorationEnabled());
-
-                guts.openControls(
-                        !row.isBlockingHelperShowing(),
-                        x,
-                        y,
-                        needsFalsingProtection,
-                        row::onGutsOpened);
-
-                row.closeRemoteInput();
-                mListContainer.onHeightChanged(row, true /* needsAnimation */);
-                mNotificationGutsExposed = guts;
-                mGutsMenuItem = menuItem;
-            }
-        });
-        return true;
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("NotificationGutsManager state:");
-        pw.print("  mKeyToRemoveOnGutsClosed: ");
-        pw.println(mKeyToRemoveOnGutsClosed);
-    }
-
-    public interface OnSettingsClickListener {
-        void onClick(String key);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 886d6f1..ef40d98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar;
 
 import android.app.Notification;
+import android.content.res.Configuration;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Icon;
 import android.text.TextUtils;
@@ -25,6 +26,10 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.android.internal.util.ContrastColorUtil;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentView;
+
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -74,8 +79,11 @@
                 imageView.getDrawable().mutate();
                 if (shouldApply) {
                     // lets gray it out
-                    int grey = view.getContext().getColor(
-                            com.android.internal.R.color.notification_default_color_light);
+                    Configuration config = view.getContext().getResources().getConfiguration();
+                    boolean inNightMode = (config.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+                            == Configuration.UI_MODE_NIGHT_YES;
+                    int grey = ContrastColorUtil.resolveColor(view.getContext(),
+                            Notification.COLOR_DEFAULT, inNightMode);
                     imageView.getDrawable().setColorFilter(grey, PorterDuff.Mode.SRC_ATOP);
                 } else {
                     // lets reset it
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
deleted file mode 100644
index e52829a..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ /dev/null
@@ -1,559 +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;
-
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.app.NotificationManager.IMPORTANCE_NONE;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.annotation.Nullable;
-import android.app.INotificationManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationChannelGroup;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationCounters;
-
-import java.util.List;
-
-/**
- * The guts of a notification revealed when performing a long press. This also houses the blocking
- * helper affordance that allows a user to keep/stop notifications after swiping one away.
- */
-public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
-    private static final String TAG = "InfoGuts";
-
-    private INotificationManager mINotificationManager;
-    private PackageManager mPm;
-    private MetricsLogger mMetricsLogger;
-
-    private String mPackageName;
-    private String mAppName;
-    private int mAppUid;
-    private int mNumUniqueChannelsInRow;
-    private NotificationChannel mSingleNotificationChannel;
-    private int mStartingUserImportance;
-    private int mChosenImportance;
-    private boolean mIsSingleDefaultChannel;
-    private boolean mIsNonblockable;
-    private StatusBarNotification mSbn;
-    private AnimatorSet mExpandAnimation;
-    private boolean mIsForeground;
-
-    private CheckSaveListener mCheckSaveListener;
-    private OnSettingsClickListener mOnSettingsClickListener;
-    private OnAppSettingsClickListener mAppSettingsClickListener;
-    private NotificationGuts mGutsContainer;
-
-    /** Whether this view is being shown as part of the blocking helper. */
-    private boolean mIsForBlockingHelper;
-    private boolean mNegativeUserSentiment;
-
-    /**
-     * String that describes how the user exit or quit out of this view, also used as a counter tag.
-     */
-    private String mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
-
-    private OnClickListener mOnKeepShowing = v -> {
-        mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
-        closeControls(v);
-    };
-
-    private OnClickListener mOnStopOrMinimizeNotifications = v -> {
-        mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS;
-        swapContent(false);
-    };
-
-    private OnClickListener mOnUndo = v -> {
-        // 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);
-        swapContent(true);
-    };
-
-    public NotificationInfo(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    // Specify a CheckSaveListener to override when/if the user's changes are committed.
-    public interface CheckSaveListener {
-        // Invoked when importance has changed and the NotificationInfo wants to try to save it.
-        // Listener should run saveImportance unless the change should be canceled.
-        void checkSave(Runnable saveImportance, StatusBarNotification sbn);
-    }
-
-    public interface OnSettingsClickListener {
-        void onClick(View v, NotificationChannel channel, int appUid);
-    }
-
-    public interface OnAppSettingsClickListener {
-        void onClick(View v, Intent intent);
-    }
-
-    @VisibleForTesting
-    void bindNotification(
-            final PackageManager pm,
-            final INotificationManager iNotificationManager,
-            final String pkg,
-            final NotificationChannel notificationChannel,
-            final int numUniqueChannelsInRow,
-            final StatusBarNotification sbn,
-            final CheckSaveListener checkSaveListener,
-            final OnSettingsClickListener onSettingsClick,
-            final OnAppSettingsClickListener onAppSettingsClick,
-            boolean isNonblockable)
-            throws RemoteException {
-        bindNotification(pm, iNotificationManager, pkg, notificationChannel,
-                numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
-                onAppSettingsClick, isNonblockable, false /* isBlockingHelper */,
-                false /* isUserSentimentNegative */);
-    }
-
-    public void bindNotification(
-            PackageManager pm,
-            INotificationManager iNotificationManager,
-            String pkg,
-            NotificationChannel notificationChannel,
-            int numUniqueChannelsInRow,
-            StatusBarNotification sbn,
-            CheckSaveListener checkSaveListener,
-            OnSettingsClickListener onSettingsClick,
-            OnAppSettingsClickListener onAppSettingsClick,
-            boolean isNonblockable,
-            boolean isForBlockingHelper,
-            boolean isUserSentimentNegative)
-            throws RemoteException {
-        mINotificationManager = iNotificationManager;
-        mMetricsLogger = Dependency.get(MetricsLogger.class);
-        mPackageName = pkg;
-        mNumUniqueChannelsInRow = numUniqueChannelsInRow;
-        mSbn = sbn;
-        mPm = pm;
-        mAppSettingsClickListener = onAppSettingsClick;
-        mAppName = mPackageName;
-        mCheckSaveListener = checkSaveListener;
-        mOnSettingsClickListener = onSettingsClick;
-        mSingleNotificationChannel = notificationChannel;
-        mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance();
-        mNegativeUserSentiment = isUserSentimentNegative;
-        mIsNonblockable = isNonblockable;
-        mIsForeground =
-                (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
-        mIsForBlockingHelper = isForBlockingHelper;
-        mAppUid = mSbn.getUid();
-
-        int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
-                pkg, mAppUid, false /* includeDeleted */);
-        if (mNumUniqueChannelsInRow == 0) {
-            throw new IllegalArgumentException("bindNotification requires at least one channel");
-        } else  {
-            // Special behavior for the Default channel if no other channels have been defined.
-            mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
-                    && mSingleNotificationChannel.getId().equals(
-                            NotificationChannel.DEFAULT_CHANNEL_ID)
-                    && numTotalChannels == 1;
-        }
-
-        bindHeader();
-        bindPrompt();
-        bindButtons();
-    }
-
-    private void bindHeader() throws RemoteException {
-        // Package name
-        Drawable pkgicon = null;
-        ApplicationInfo info;
-        try {
-            info = mPm.getApplicationInfo(
-                    mPackageName,
-                    PackageManager.MATCH_UNINSTALLED_PACKAGES
-                            | PackageManager.MATCH_DISABLED_COMPONENTS
-                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                            | PackageManager.MATCH_DIRECT_BOOT_AWARE);
-            if (info != null) {
-                mAppName = String.valueOf(mPm.getApplicationLabel(info));
-                pkgicon = mPm.getApplicationIcon(info);
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // app is gone, just show package name and generic icon
-            pkgicon = mPm.getDefaultActivityIcon();
-        }
-        ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
-        ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
-
-        // Set group information if this channel has an associated group.
-        CharSequence groupName = null;
-        if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
-            final NotificationChannelGroup notificationChannelGroup =
-                    mINotificationManager.getNotificationChannelGroupForPackage(
-                            mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
-            if (notificationChannelGroup != null) {
-                groupName = notificationChannelGroup.getName();
-            }
-        }
-        TextView groupNameView = findViewById(R.id.group_name);
-        TextView groupDividerView = findViewById(R.id.pkg_group_divider);
-        if (groupName != null) {
-            groupNameView.setText(groupName);
-            groupNameView.setVisibility(View.VISIBLE);
-            groupDividerView.setVisibility(View.VISIBLE);
-        } else {
-            groupNameView.setVisibility(View.GONE);
-            groupDividerView.setVisibility(View.GONE);
-        }
-
-        // Settings button.
-        final View settingsButton = findViewById(R.id.info);
-        if (mAppUid >= 0 && mOnSettingsClickListener != null) {
-            settingsButton.setVisibility(View.VISIBLE);
-            final int appUidF = mAppUid;
-            settingsButton.setOnClickListener(
-                    (View view) -> {
-                        logBlockingHelperCounter(
-                                NotificationCounters.BLOCKING_HELPER_NOTIF_SETTINGS);
-                        mOnSettingsClickListener.onClick(view,
-                                mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
-                                appUidF);
-                    });
-        } else {
-            settingsButton.setVisibility(View.GONE);
-        }
-    }
-
-    private void bindPrompt() {
-        final TextView blockPrompt = findViewById(R.id.block_prompt);
-        bindName();
-        if (mIsNonblockable) {
-            blockPrompt.setText(R.string.notification_unblockable_desc);
-        } else {
-            if (mNegativeUserSentiment) {
-                blockPrompt.setText(R.string.inline_blocking_helper);
-            }  else if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
-                blockPrompt.setText(R.string.inline_keep_showing_app);
-            } else {
-                blockPrompt.setText(R.string.inline_keep_showing);
-            }
-        }
-    }
-
-    private void bindName() {
-        final TextView channelName = findViewById(R.id.channel_name);
-        if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
-            channelName.setVisibility(View.GONE);
-        } else {
-            channelName.setText(mSingleNotificationChannel.getName());
-        }
-    }
-
-    @VisibleForTesting
-    void logBlockingHelperCounter(String counterTag) {
-        if (mIsForBlockingHelper) {
-            mMetricsLogger.count(counterTag, 1);
-        }
-    }
-
-    private boolean hasImportanceChanged() {
-        return mSingleNotificationChannel != null && mStartingUserImportance != mChosenImportance;
-    }
-
-    private void saveImportance() {
-        if (!mIsNonblockable) {
-            // Only go through the lock screen/bouncer if the user hit 'Stop notifications'.
-            // Otherwise, update the importance immediately.
-            if (mCheckSaveListener != null
-                    && NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS.equals(
-                            mExitReason)) {
-                mCheckSaveListener.checkSave(this::updateImportance, mSbn);
-            } else {
-                updateImportance();
-            }
-        }
-    }
-
-    /**
-     * Commits the updated importance values on the background thread.
-     */
-    private void updateImportance() {
-        MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
-                mChosenImportance - mStartingUserImportance);
-
-        Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
-        bgHandler.post(new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
-                mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
-                mStartingUserImportance, mChosenImportance));
-    }
-
-    private void bindButtons() {
-        // Set up stay-in-notification actions
-        View block =  findViewById(R.id.block);
-        TextView keep = findViewById(R.id.keep);
-        View minimize = findViewById(R.id.minimize);
-
-        findViewById(R.id.undo).setOnClickListener(mOnUndo);
-        block.setOnClickListener(mOnStopOrMinimizeNotifications);
-        keep.setOnClickListener(mOnKeepShowing);
-        minimize.setOnClickListener(mOnStopOrMinimizeNotifications);
-
-        if (mIsNonblockable) {
-            keep.setText(android.R.string.ok);
-            block.setVisibility(GONE);
-            minimize.setVisibility(GONE);
-        } else if (mIsForeground) {
-            block.setVisibility(GONE);
-            minimize.setVisibility(VISIBLE);
-        } else if (!mIsForeground) {
-            block.setVisibility(VISIBLE);
-            minimize.setVisibility(GONE);
-        }
-
-        // Set up app settings link (i.e. Customize)
-        TextView settingsLinkView = findViewById(R.id.app_settings);
-        Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName, mSingleNotificationChannel,
-                mSbn.getId(), mSbn.getTag());
-        if (!mIsForBlockingHelper
-                && settingsIntent != null
-                && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
-            settingsLinkView.setVisibility(VISIBLE);
-            settingsLinkView.setText(mContext.getString(R.string.notification_app_settings));
-            settingsLinkView.setOnClickListener((View view) -> {
-                mAppSettingsClickListener.onClick(view, settingsIntent);
-            });
-        } else {
-            settingsLinkView.setVisibility(View.GONE);
-        }
-    }
-
-    private void swapContent(boolean showPrompt) {
-        if (mExpandAnimation != null) {
-            mExpandAnimation.cancel();
-        }
-
-        View prompt = findViewById(R.id.prompt);
-        ViewGroup confirmation = findViewById(R.id.confirmation);
-        TextView confirmationText = findViewById(R.id.confirmation_text);
-        View header = findViewById(R.id.header);
-
-        if (showPrompt) {
-            mChosenImportance = mStartingUserImportance;
-        } else if (mIsForeground) {
-            mChosenImportance = IMPORTANCE_MIN;
-            confirmationText.setText(R.string.notification_channel_minimized);
-        } else {
-            mChosenImportance = IMPORTANCE_NONE;
-            confirmationText.setText(R.string.notification_channel_disabled);
-        }
-
-        ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA,
-                prompt.getAlpha(), showPrompt ? 1f : 0f);
-        promptAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
-        ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
-                confirmation.getAlpha(), showPrompt ? 0f : 1f);
-        confirmAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
-
-        prompt.setVisibility(showPrompt ? VISIBLE : GONE);
-        confirmation.setVisibility(showPrompt ? GONE : VISIBLE);
-        header.setVisibility(showPrompt ? VISIBLE : GONE);
-
-        mExpandAnimation = new AnimatorSet();
-        mExpandAnimation.playTogether(promptAnim, confirmAnim);
-        mExpandAnimation.setDuration(150);
-        mExpandAnimation.addListener(new AnimatorListenerAdapter() {
-            boolean cancelled = false;
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                cancelled = true;
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (!cancelled) {
-                    prompt.setVisibility(showPrompt ? VISIBLE : GONE);
-                    confirmation.setVisibility(showPrompt ? GONE : VISIBLE);
-                }
-            }
-        });
-        mExpandAnimation.start();
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        if (mGutsContainer != null &&
-                event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
-            if (mGutsContainer.isExposed()) {
-                event.getText().add(mContext.getString(
-                        R.string.notification_channel_controls_opened_accessibility, mAppName));
-            } else {
-                event.getText().add(mContext.getString(
-                        R.string.notification_channel_controls_closed_accessibility, mAppName));
-            }
-        }
-    }
-
-    private Intent getAppSettingsIntent(PackageManager pm, String packageName,
-            NotificationChannel channel, int id, String tag) {
-        Intent intent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
-                .setPackage(packageName);
-        final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
-                intent,
-                PackageManager.MATCH_DEFAULT_ONLY
-        );
-        if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) {
-            return null;
-        }
-        final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
-        intent.setClassName(activityInfo.packageName, activityInfo.name);
-        if (channel != null) {
-            intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId());
-        }
-        intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id);
-        intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag);
-        return intent;
-    }
-
-    /**
-     * Closes the controls and commits the updated importance values (indirectly). If this view is
-     * being used to show the blocking helper, this will immediately dismiss the blocking helper and
-     * commit the updated importance.
-     *
-     * <p><b>Note,</b> this will only get called once the view is dismissing. This means that the
-     * user does not have the ability to undo the action anymore. See {@link #swapContent(boolean)}
-     * for where undo is handled.
-     */
-    @VisibleForTesting
-    void closeControls(View v) {
-        int[] parentLoc = new int[2];
-        int[] targetLoc = new int[2];
-        mGutsContainer.getLocationOnScreen(parentLoc);
-        v.getLocationOnScreen(targetLoc);
-        final int centerX = v.getWidth() / 2;
-        final int centerY = v.getHeight() / 2;
-        final int x = targetLoc[0] - parentLoc[0] + centerX;
-        final int y = targetLoc[1] - parentLoc[1] + centerY;
-        mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
-    }
-
-    @Override
-    public void setGutsParent(NotificationGuts guts) {
-        mGutsContainer = guts;
-    }
-
-    @Override
-    public boolean willBeRemoved() {
-        return hasImportanceChanged();
-    }
-
-    @Override
-    public boolean shouldBeSaved() {
-        return hasImportanceChanged();
-    }
-
-    @Override
-    public View getContentView() {
-        return this;
-    }
-
-    @Override
-    public boolean handleCloseControls(boolean save, boolean force) {
-        // Save regardless of the importance so we can lock the importance field if the user wants
-        // to keep getting notifications
-        if (save) {
-            saveImportance();
-        }
-        logBlockingHelperCounter(mExitReason);
-        return false;
-    }
-
-    @Override
-    public int getActualHeight() {
-        return getHeight();
-    }
-
-    /**
-     * Runnable to either update the given channel (with a new importance value) or, if no channel
-     * is provided, update notifications enabled state for the package.
-     */
-    private static class UpdateImportanceRunnable implements Runnable {
-        private final INotificationManager mINotificationManager;
-        private final String mPackageName;
-        private final int mAppUid;
-        private final @Nullable NotificationChannel mChannelToUpdate;
-        private final int mCurrentImportance;
-        private final int mNewImportance;
-
-
-        public UpdateImportanceRunnable(INotificationManager notificationManager,
-                String packageName, int appUid, @Nullable NotificationChannel channelToUpdate,
-                int currentImportance, int newImportance) {
-            mINotificationManager = notificationManager;
-            mPackageName = packageName;
-            mAppUid = appUid;
-            mChannelToUpdate = channelToUpdate;
-            mCurrentImportance = currentImportance;
-            mNewImportance = newImportance;
-        }
-
-        @Override
-        public void run() {
-            try {
-                if (mChannelToUpdate != null) {
-                    mChannelToUpdate.setImportance(mNewImportance);
-                    mChannelToUpdate.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-                    mINotificationManager.updateNotificationChannelForPackage(
-                            mPackageName, mAppUid, mChannelToUpdate);
-                } else {
-                    // For notifications with more than one channel, update notification enabled
-                    // state. If the importance was lowered, we disable notifications.
-                    mINotificationManager.setNotificationsEnabledWithImportanceLockForPackage(
-                            mPackageName, mAppUid, mNewImportance >= mCurrentImportance);
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "Unable to update notification importance", e);
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
deleted file mode 100644
index af9a3a3..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
+++ /dev/null
@@ -1,198 +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;
-
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
-
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-
-/**
- * Interface representing the entity that contains notifications. It can have
- * notification views added and removed from it, and will manage displaying them to the user.
- */
-public interface NotificationListContainer {
-
-    /**
-     * Called when a child is being transferred.
-     *
-     * @param childTransferInProgress whether child transfer is in progress
-     */
-    void setChildTransferInProgress(boolean childTransferInProgress);
-
-    /**
-     * Change the position of child to a new location
-     *
-     * @param child the view to change the position for
-     * @param newIndex the new index
-     */
-    void changeViewPosition(View child, int newIndex);
-
-    /**
-     * Called when a child was added to a group.
-     *
-     * @param row row of the group child that was added
-     */
-    void notifyGroupChildAdded(View row);
-
-    /**
-     * Called when a child was removed from a group.
-     *
-     * @param row row of the child that was removed
-     * @param childrenContainer ViewGroup of the group that the child was removed from
-     */
-    void notifyGroupChildRemoved(View row, ViewGroup childrenContainer);
-
-    /**
-     * Generate an animation for an added child view.
-     *
-     * @param child The view to be added.
-     * @param fromMoreCard Whether this add is coming from the "more" card on lockscreen.
-     */
-    void generateAddAnimation(View child, boolean fromMoreCard);
-
-    /**
-     * Generate a child order changed event.
-     */
-    void generateChildOrderChangedEvent();
-
-    /**
-     * Returns the number of children in the NotificationListContainer.
-     *
-     * @return the number of children in the NotificationListContainer
-     */
-    int getContainerChildCount();
-
-    /**
-     * Gets the ith child in the NotificationListContainer.
-     *
-     * @param i ith child to get
-     * @return the ith child in the list container
-     */
-    View getContainerChildAt(int i);
-
-    /**
-     * Remove a view from the container
-     *
-     * @param v view to remove
-     */
-    void removeContainerView(View v);
-
-    /**
-     * Add a view to the container
-     *
-     * @param v view to add
-     */
-    void addContainerView(View v);
-
-    /**
-     * Sets the maximum number of notifications to display.
-     *
-     * @param maxNotifications max number of notifications to display
-     */
-    void setMaxDisplayedNotifications(int maxNotifications);
-
-    /**
-     * Handle snapping a non-dismissable row back if the user tried to dismiss it.
-     *
-     * @param row row to snap back
-     */
-    void snapViewIfNeeded(ExpandableNotificationRow row);
-
-    /**
-     * 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);
-
-    /**
-     * Called when the height of an expandable view changes.
-     *
-     * @param view view whose height changed
-     * @param animate whether this change should be animated
-     */
-    void onHeightChanged(ExpandableView view, boolean animate);
-
-    /**
-     * Resets the currently exposed menu view.
-     *
-     * @param animate whether to animate the closing/change of menu view
-     * @param force reset the menu view even if it looks like it is already reset
-     */
-    void resetExposedMenuView(boolean animate, boolean force);
-
-    /**
-     * Returns the NotificationSwipeActionHelper for the NotificationListContainer.
-     *
-     * @return swipe action helper for the list container
-     */
-    NotificationSwipeActionHelper getSwipeActionHelper();
-
-    /**
-     * Called when a notification is removed from the shade. This cleans up the state for a
-     * given view.
-     *
-     * @param view view to clean up view state for
-     */
-    void cleanUpViewState(View view);
-
-    /**
-     * Returns whether an ExpandableNotificationRow is in a visible location or not.
-     *
-     * @param row
-     * @return true if row is in a visible location
-     */
-    boolean isInVisibleLocation(ExpandableNotificationRow row);
-
-    /**
-     * Sets a listener to listen for changes in notification locations.
-     *
-     * @param listener listener to set
-     */
-    void setChildLocationsChangedListener(
-            NotificationLogger.OnChildLocationsChangedListener listener);
-
-    /**
-     * Called when an update to the notification view hierarchy is completed.
-     */
-    default void onNotificationViewUpdateFinished() {}
-
-    /**
-     * Returns true if there are pulsing notifications.
-     *
-     * @return true if has pulsing notifications
-     */
-    boolean hasPulsingNotifications();
-
-    /**
-     * Apply parameters of the expand animation to the layout
-     */
-    default void applyExpandAnimationParams(ExpandAnimationParameters params) {}
-
-    default void setExpandingNotification(ExpandableNotificationRow row) {}
-
-    /**
-     * Bind a newly created row.
-     *
-     * @param row The notification to bind.
-     */
-    default void bindRow(ExpandableNotificationRow row) {}
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index a2d0c2b..9b375df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -28,6 +28,7 @@
 import android.util.Log;
 
 import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 1287ced..d6886f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -34,8 +34,6 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
-import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
@@ -43,7 +41,8 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.OverviewProxyService;
-import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 import java.io.FileDescriptor;
@@ -108,16 +107,6 @@
             } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
                 // Start the overview connection to the launcher service
                 Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
-            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
-                try {
-                    final int lastResumedActivityUserId =
-                            ActivityManager.getService().getLastResumedActivityUserId();
-                    if (mUserManager.isManagedProfile(lastResumedActivityUserId)) {
-                        showForegroundManagedProfileActivityToast();
-                    }
-                } catch (RemoteException e) {
-                    // Abandon hope activity manager not running.
-                }
             } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
                 final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
                 final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
@@ -224,7 +213,6 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         filter.addAction(Intent.ACTION_USER_ADDED);
-        filter.addAction(Intent.ACTION_USER_PRESENT);
         filter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiver(mBaseBroadcastReceiver, filter);
 
@@ -237,19 +225,6 @@
         mSettingsObserver.onChange(false);  // set up
     }
 
-    private void showForegroundManagedProfileActivityToast() {
-        Toast toast = Toast.makeText(mContext,
-                R.string.managed_profile_foreground_toast,
-                Toast.LENGTH_SHORT);
-        TextView text = toast.getView().findViewById(android.R.id.message);
-        text.setCompoundDrawablesRelativeWithIntrinsicBounds(
-                R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
-        int paddingPx = mContext.getResources().getDimensionPixelSize(
-                R.dimen.managed_profile_toast_padding);
-        text.setCompoundDrawablePadding(paddingPx);
-        toast.show();
-    }
-
     public boolean shouldShowLockscreenNotifications() {
         return mShowLockscreenNotifications;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
deleted file mode 100644
index 8e8e718..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
+++ /dev/null
@@ -1,250 +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;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.service.notification.NotificationListenerService;
-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 java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-
-/**
- * Handles notification logging, in particular, logging which notifications are visible and which
- * are not.
- */
-public class NotificationLogger {
-    private static final String TAG = "NotificationLogger";
-
-    /** The minimum delay in ms between reports of notification visibility. */
-    private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
-
-    /** Keys of notifications currently visible to the user. */
-    private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications =
-            new ArraySet<>();
-
-    // Dependencies:
-    private final NotificationListenerService mNotificationListener =
-            Dependency.get(NotificationListener.class);
-    private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
-
-    protected NotificationEntryManager mEntryManager;
-    protected Handler mHandler = new Handler();
-    protected IStatusBarService mBarService;
-    private long mLastVisibilityReportUptimeMs;
-    private NotificationListContainer mListContainer;
-
-    protected final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
-            new OnChildLocationsChangedListener() {
-                @Override
-                public void onChildLocationsChanged() {
-                    if (mHandler.hasCallbacks(mVisibilityReporter)) {
-                        // Visibilities will be reported when the existing
-                        // callback is executed.
-                        return;
-                    }
-                    // Calculate when we're allowed to run the visibility
-                    // reporter. Note that this timestamp might already have
-                    // passed. That's OK, the callback will just be executed
-                    // ASAP.
-                    long nextReportUptimeMs =
-                            mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
-                    mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
-                }
-            };
-
-    // Tracks notifications currently visible in mNotificationStackScroller and
-    // emits visibility events via NoMan on changes.
-    protected final Runnable mVisibilityReporter = new Runnable() {
-        private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications =
-                new ArraySet<>();
-        private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications =
-                new ArraySet<>();
-        private final ArraySet<NotificationVisibility> mTmpNoLongerVisibleNotifications =
-                new ArraySet<>();
-
-        @Override
-        public void run() {
-            mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
-
-            // 1. Loop over mNotificationData entries:
-            //   A. Keep list of visible notifications.
-            //   B. Keep list of previously hidden, now visible notifications.
-            // 2. Compute no-longer visible notifications by removing currently
-            //    visible notifications from the set of previously visible
-            //    notifications.
-            // 3. Report newly visible and no-longer visible notifications.
-            // 4. Keep currently visible notifications for next report.
-            ArrayList<NotificationData.Entry> activeNotifications = mEntryManager
-                    .getNotificationData().getActiveNotifications();
-            int N = activeNotifications.size();
-            for (int i = 0; i < N; i++) {
-                NotificationData.Entry entry = activeNotifications.get(i);
-                String key = entry.notification.getKey();
-                boolean isVisible = mListContainer.isInVisibleLocation(entry.row);
-                NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
-                boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
-                if (isVisible) {
-                    // Build new set of visible notifications.
-                    mTmpCurrentlyVisibleNotifications.add(visObj);
-                    if (!previouslyVisible) {
-                        mTmpNewlyVisibleNotifications.add(visObj);
-                    }
-                } else {
-                    // release object
-                    visObj.recycle();
-                }
-            }
-            mTmpNoLongerVisibleNotifications.addAll(mCurrentlyVisibleNotifications);
-            mTmpNoLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications);
-
-            logNotificationVisibilityChanges(
-                    mTmpNewlyVisibleNotifications, mTmpNoLongerVisibleNotifications);
-
-            recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
-            mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications);
-
-            recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications);
-            mTmpCurrentlyVisibleNotifications.clear();
-            mTmpNewlyVisibleNotifications.clear();
-            mTmpNoLongerVisibleNotifications.clear();
-        }
-    };
-
-    public NotificationLogger() {
-        mBarService = IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-    }
-
-    public void setUpWithEntryManager(NotificationEntryManager entryManager,
-            NotificationListContainer listContainer) {
-        mEntryManager = entryManager;
-        mListContainer = listContainer;
-    }
-
-    public void stopNotificationLogging() {
-        // Report all notifications as invisible and turn down the
-        // reporter.
-        if (!mCurrentlyVisibleNotifications.isEmpty()) {
-            logNotificationVisibilityChanges(
-                    Collections.emptyList(), mCurrentlyVisibleNotifications);
-            recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
-        }
-        mHandler.removeCallbacks(mVisibilityReporter);
-        mListContainer.setChildLocationsChangedListener(null);
-    }
-
-    public void startNotificationLogging() {
-        mListContainer.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
-        // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
-        // cause the scroller to emit child location events. Hence generate
-        // one ourselves to guarantee that we're reporting visible
-        // notifications.
-        // (Note that in cases where the scroller does emit events, this
-        // additional event doesn't break anything.)
-        mNotificationLocationsChangedListener.onChildLocationsChanged();
-    }
-
-    private void logNotificationVisibilityChanges(
-            Collection<NotificationVisibility> newlyVisible,
-            Collection<NotificationVisibility> noLongerVisible) {
-        if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
-            return;
-        }
-        final NotificationVisibility[] newlyVisibleAr = cloneVisibilitiesAsArr(newlyVisible);
-        final NotificationVisibility[] noLongerVisibleAr = cloneVisibilitiesAsArr(noLongerVisible);
-
-        mUiOffloadThread.submit(() -> {
-            try {
-                mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
-            } catch (RemoteException e) {
-                // Ignore.
-            }
-
-            final int N = newlyVisible.size();
-            if (N > 0) {
-                String[] newlyVisibleKeyAr = new String[N];
-                for (int i = 0; i < N; i++) {
-                    newlyVisibleKeyAr[i] = newlyVisibleAr[i].key;
-                }
-
-                // TODO: Call NotificationEntryManager to do this, once it exists.
-                // TODO: Consider not catching all runtime exceptions here.
-                try {
-                    mNotificationListener.setNotificationsShown(newlyVisibleKeyAr);
-                } catch (RuntimeException e) {
-                    Log.d(TAG, "failed setNotificationsShown: ", e);
-                }
-            }
-            recycleAllVisibilityObjects(newlyVisibleAr);
-            recycleAllVisibilityObjects(noLongerVisibleAr);
-        });
-    }
-
-    private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
-        final int N = array.size();
-        for (int i = 0 ; i < N; i++) {
-            array.valueAt(i).recycle();
-        }
-        array.clear();
-    }
-
-    private void recycleAllVisibilityObjects(NotificationVisibility[] array) {
-        final int N = array.length;
-        for (int i = 0 ; i < N; i++) {
-            if (array[i] != null) {
-                array[i].recycle();
-            }
-        }
-    }
-
-    private NotificationVisibility[] cloneVisibilitiesAsArr(Collection<NotificationVisibility> c) {
-
-        final NotificationVisibility[] array = new NotificationVisibility[c.size()];
-        int i = 0;
-        for(NotificationVisibility nv: c) {
-            if (nv != null) {
-                array[i] = nv.clone();
-            }
-            i++;
-        }
-        return array;
-    }
-
-    @VisibleForTesting
-    public Runnable getVisibilityReporter() {
-        return mVisibilityReporter;
-    }
-
-    /**
-     * A listener that is notified when some child locations might have changed.
-     */
-    public interface OnChildLocationsChangedListener {
-        void onChildLocationsChanged();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index f737a8c..e89e6e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -26,6 +26,8 @@
 import android.util.Log;
 
 import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
deleted file mode 100644
index ada1a17..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
+++ /dev/null
@@ -1,696 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
-
-import java.util.ArrayList;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.NotificationGuts.GutsContent;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.Nullable;
-import android.app.Notification;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-import android.service.notification.StatusBarNotification;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.FrameLayout.LayoutParams;
-
-public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener,
-        ExpandableNotificationRow.LayoutListener {
-
-    private static final boolean DEBUG = false;
-    private static final String TAG = "swipe";
-
-    private static final int ICON_ALPHA_ANIM_DURATION = 200;
-    private static final long SHOW_MENU_DELAY = 60;
-    private static final long SWIPE_MENU_TIMING = 200;
-
-    // Notification must be swiped at least this fraction of a single menu item to show menu
-    private static final float SWIPED_FAR_ENOUGH_MENU_FRACTION = 0.25f;
-    private static final float SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION = 0.15f;
-
-    // When the menu is displayed, the notification must be swiped within this fraction of a single
-    // menu item to snap back to menu (else it will cover the menu or it'll be dismissed)
-    private static final float SWIPED_BACK_ENOUGH_TO_COVER_FRACTION = 0.2f;
-
-    private ExpandableNotificationRow mParent;
-
-    private Context mContext;
-    private FrameLayout mMenuContainer;
-    private MenuItem mInfoItem;
-    private MenuItem mAppOpsItem;
-    private MenuItem mSnoozeItem;
-    private ArrayList<MenuItem> mMenuItems;
-    private OnMenuEventListener mMenuListener;
-
-    private ValueAnimator mFadeAnimator;
-    private boolean mAnimating;
-    private boolean mMenuFadedIn;
-
-    private boolean mOnLeft;
-    private boolean mIconsPlaced;
-
-    private boolean mDismissing;
-    private boolean mSnapping;
-    private float mTranslation;
-
-    private int[] mIconLocation = new int[2];
-    private int[] mParentLocation = new int[2];
-
-    private float mHorizSpaceForIcon = -1;
-    private int mVertSpaceForIcons = -1;
-    private int mIconPadding = -1;
-    private int mSidePadding;
-
-    private float mAlpha = 0f;
-    private float mPrevX;
-
-    private CheckForDrag mCheckForDrag;
-    private Handler mHandler;
-
-    private boolean mMenuSnappedTo;
-    private boolean mMenuSnappedOnLeft;
-    private boolean mShouldShowMenu;
-
-    private NotificationSwipeActionHelper mSwipeHelper;
-    private boolean mIsUserTouching;
-
-    public NotificationMenuRow(Context context) {
-        mContext = context;
-        mShouldShowMenu = context.getResources().getBoolean(R.bool.config_showNotificationGear);
-        mHandler = new Handler(Looper.getMainLooper());
-        mMenuItems = new ArrayList<>();
-    }
-
-    @Override
-    public ArrayList<MenuItem> getMenuItems(Context context) {
-        return mMenuItems;
-    }
-
-    @Override
-    public MenuItem getLongpressMenuItem(Context context) {
-        return mInfoItem;
-    }
-
-    @Override
-    public MenuItem getAppOpsMenuItem(Context context) {
-        return mAppOpsItem;
-    }
-
-    @Override
-    public MenuItem getSnoozeMenuItem(Context context) {
-        return mSnoozeItem;
-    }
-
-    @Override
-    public void setSwipeActionHelper(NotificationSwipeActionHelper helper) {
-        mSwipeHelper = helper;
-    }
-
-    @Override
-    public void setMenuClickListener(OnMenuEventListener listener) {
-        mMenuListener = listener;
-    }
-
-    @Override
-    public void createMenu(ViewGroup parent, StatusBarNotification sbn) {
-        mParent = (ExpandableNotificationRow) parent;
-        createMenuViews(true /* resetState */);
-    }
-
-    @Override
-    public boolean isMenuVisible() {
-        return mAlpha > 0;
-    }
-
-    @Override
-    public View getMenuView() {
-        return mMenuContainer;
-    }
-
-    @Override
-    public void resetMenu() {
-        resetState(true);
-    }
-
-    @Override
-    public void onNotificationUpdated(StatusBarNotification sbn) {
-        if (mMenuContainer == null) {
-            // Menu hasn't been created yet, no need to do anything.
-            return;
-        }
-        createMenuViews(!isMenuVisible() /* resetState */);
-    }
-
-    @Override
-    public void onConfigurationChanged() {
-        mParent.setLayoutListener(this);
-    }
-
-    @Override
-    public void onLayout() {
-        mIconsPlaced = false; // Force icons to be re-placed
-        setMenuLocation();
-        mParent.removeListener();
-    }
-
-    private void createMenuViews(boolean resetState) {
-        final Resources res = mContext.getResources();
-        mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size);
-        mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
-        mMenuItems.clear();
-        // Construct the menu items based on the notification
-        if (mParent != null && mParent.getStatusBarNotification() != null) {
-            int flags = mParent.getStatusBarNotification().getNotification().flags;
-            boolean isForeground = (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
-            if (!isForeground) {
-                // Only show snooze for non-foreground notifications
-                mSnoozeItem = createSnoozeItem(mContext);
-                mMenuItems.add(mSnoozeItem);
-            }
-        }
-        mInfoItem = createInfoItem(mContext);
-        mMenuItems.add(mInfoItem);
-
-        mAppOpsItem = createAppOpsItem(mContext);
-        mMenuItems.add(mAppOpsItem);
-
-        // Construct the menu views
-        if (mMenuContainer != null) {
-            mMenuContainer.removeAllViews();
-        } else {
-            mMenuContainer = new FrameLayout(mContext);
-        }
-        for (int i = 0; i < mMenuItems.size(); i++) {
-            addMenuView(mMenuItems.get(i), mMenuContainer);
-        }
-        if (resetState) {
-            resetState(false /* notify */);
-        } else {
-            mIconsPlaced = false;
-            setMenuLocation();
-            if (!mIsUserTouching) {
-                // If the # of items showing changed we need to update the snap position
-                showMenu(mParent, mOnLeft ? getSpaceForMenu() : -getSpaceForMenu(),
-                        0 /* velocity */);
-            }
-        }
-    }
-
-    private void resetState(boolean notify) {
-        setMenuAlpha(0f);
-        mIconsPlaced = false;
-        mMenuFadedIn = false;
-        mAnimating = false;
-        mSnapping = false;
-        mDismissing = false;
-        mMenuSnappedTo = false;
-        setMenuLocation();
-        if (mMenuListener != null && notify) {
-            mMenuListener.onMenuReset(mParent);
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(View view, MotionEvent ev, float velocity) {
-        final int action = ev.getActionMasked();
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                mSnapping = false;
-                if (mFadeAnimator != null) {
-                    mFadeAnimator.cancel();
-                }
-                mHandler.removeCallbacks(mCheckForDrag);
-                mCheckForDrag = null;
-                mPrevX = ev.getRawX();
-                mIsUserTouching = true;
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                mSnapping = false;
-                float diffX = ev.getRawX() - mPrevX;
-                mPrevX = ev.getRawX();
-                if (!isTowardsMenu(diffX) && isMenuLocationChange()) {
-                    // Don't consider it "snapped" if location has changed.
-                    mMenuSnappedTo = false;
-
-                    // Changed directions, make sure we check to fade in icon again.
-                    if (!mHandler.hasCallbacks(mCheckForDrag)) {
-                        // No check scheduled, set null to schedule a new one.
-                        mCheckForDrag = null;
-                    } else {
-                        // Check scheduled, reset alpha and update location; check will fade it in
-                        setMenuAlpha(0f);
-                        setMenuLocation();
-                    }
-                }
-                if (mShouldShowMenu
-                        && !NotificationStackScrollLayout.isPinnedHeadsUp(view)
-                        && !mParent.areGutsExposed()
-                        && !mParent.isDark()
-                        && (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag))) {
-                    // Only show the menu if we're not a heads up view and guts aren't exposed.
-                    mCheckForDrag = new CheckForDrag();
-                    mHandler.postDelayed(mCheckForDrag, SHOW_MENU_DELAY);
-                }
-                break;
-
-            case MotionEvent.ACTION_UP:
-                mIsUserTouching = false;
-                return handleUpEvent(ev, view, velocity);
-            case MotionEvent.ACTION_CANCEL:
-                mIsUserTouching = false;
-                cancelDrag();
-                return false;
-        }
-        return false;
-    }
-
-    private boolean handleUpEvent(MotionEvent ev, View animView, float velocity) {
-        // If the menu should not be shown, then there is no need to check if the a swipe
-        // should result in a snapping to the menu. As a result, just check if the swipe
-        // was enough to dismiss the notification.
-        if (!mShouldShowMenu) {
-            if (mSwipeHelper.isDismissGesture(ev)) {
-                dismiss(animView, velocity);
-            } else {
-                snapBack(animView, velocity);
-            }
-            return true;
-        }
-
-        final boolean gestureTowardsMenu = isTowardsMenu(velocity);
-        final boolean gestureFastEnough =
-                mSwipeHelper.getMinDismissVelocity() <= Math.abs(velocity);
-        final boolean gestureFarEnough =
-                mSwipeHelper.swipedFarEnough(mTranslation, mParent.getWidth());
-        final double timeForGesture = ev.getEventTime() - ev.getDownTime();
-        final boolean showMenuForSlowOnGoing = !mParent.canViewBeDismissed()
-                && timeForGesture >= SWIPE_MENU_TIMING;
-        final float menuSnapTarget = mOnLeft ? getSpaceForMenu() : -getSpaceForMenu();
-
-        if (DEBUG) {
-            Log.d(TAG, "mTranslation= " + mTranslation
-                    + " mAlpha= " + mAlpha
-                    + " velocity= " + velocity
-                    + " mMenuSnappedTo= " + mMenuSnappedTo
-                    + " mMenuSnappedOnLeft= " + mMenuSnappedOnLeft
-                    + " mOnLeft= " + mOnLeft
-                    + " minDismissVel= " + mSwipeHelper.getMinDismissVelocity()
-                    + " isDismissGesture= " + mSwipeHelper.isDismissGesture(ev)
-                    + " gestureTowardsMenu= " + gestureTowardsMenu
-                    + " gestureFastEnough= " + gestureFastEnough
-                    + " gestureFarEnough= " + gestureFarEnough);
-        }
-
-        if (mMenuSnappedTo && isMenuVisible() && mMenuSnappedOnLeft == mOnLeft) {
-            // Menu was snapped to previously and we're on the same side, figure out if
-            // we should stick to the menu, snap back into place, or dismiss
-            final float maximumSwipeDistance = mHorizSpaceForIcon
-                    * SWIPED_BACK_ENOUGH_TO_COVER_FRACTION;
-            final float targetLeft = getSpaceForMenu() - maximumSwipeDistance;
-            final float targetRight = mParent.getWidth() * SWIPED_FAR_ENOUGH_SIZE_FRACTION;
-            boolean withinSnapMenuThreshold = mOnLeft
-                    ? mTranslation > targetLeft && mTranslation < targetRight
-                    : mTranslation < -targetLeft && mTranslation > -targetRight;
-            boolean shouldSnapTo = mOnLeft ? mTranslation < targetLeft : mTranslation > -targetLeft;
-            if (DEBUG) {
-                Log.d(TAG, "   withinSnapMenuThreshold= " + withinSnapMenuThreshold
-                        + "   shouldSnapTo= " + shouldSnapTo
-                        + "   targetLeft= " + targetLeft
-                        + "   targetRight= " + targetRight);
-            }
-            if (withinSnapMenuThreshold && !mSwipeHelper.isDismissGesture(ev)) {
-                // Haven't moved enough to unsnap from the menu
-                showMenu(animView, menuSnapTarget, velocity);
-            } else if (mSwipeHelper.isDismissGesture(ev) && !shouldSnapTo) {
-                // Only dismiss if we're not moving towards the menu
-                dismiss(animView, velocity);
-            } else {
-                snapBack(animView, velocity);
-            }
-        } else if (!mSwipeHelper.isFalseGesture(ev)
-                && (swipedEnoughToShowMenu() && (!gestureFastEnough || showMenuForSlowOnGoing))
-                || (gestureTowardsMenu && !mSwipeHelper.isDismissGesture(ev))) {
-            // Menu has not been snapped to previously and this is menu revealing gesture
-            showMenu(animView, menuSnapTarget, velocity);
-        } else if (mSwipeHelper.isDismissGesture(ev) && !gestureTowardsMenu) {
-            dismiss(animView, velocity);
-        } else {
-            snapBack(animView, velocity);
-        }
-        return true;
-    }
-
-    private void showMenu(View animView, float targetLeft, float velocity) {
-        mMenuSnappedTo = true;
-        mMenuSnappedOnLeft = mOnLeft;
-        mMenuListener.onMenuShown(animView);
-        mSwipeHelper.snap(animView, targetLeft, velocity);
-    }
-
-    private void snapBack(View animView, float velocity) {
-        cancelDrag();
-        mMenuSnappedTo = false;
-        mSnapping = true;
-        mSwipeHelper.snap(animView, 0 /* leftTarget */, velocity);
-    }
-
-    private void dismiss(View animView, float velocity) {
-        cancelDrag();
-        mMenuSnappedTo = false;
-        mDismissing = true;
-        mSwipeHelper.dismiss(animView, velocity);
-    }
-
-    private void cancelDrag() {
-        if (mFadeAnimator != null) {
-            mFadeAnimator.cancel();
-        }
-        mHandler.removeCallbacks(mCheckForDrag);
-    }
-
-    /**
-     * @return whether the notification has been translated enough to show the menu and not enough
-     *         to be dismissed.
-     */
-    private boolean swipedEnoughToShowMenu() {
-        final float multiplier = mParent.canViewBeDismissed()
-                ? SWIPED_FAR_ENOUGH_MENU_FRACTION
-                : SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION;
-        final float minimumSwipeDistance = mHorizSpaceForIcon * multiplier;
-        return !mSwipeHelper.swipedFarEnough(0, 0) && isMenuVisible()
-                && (mOnLeft ? mTranslation > minimumSwipeDistance
-                        : mTranslation < -minimumSwipeDistance);
-    }
-
-    /**
-     * Returns whether the gesture is towards the menu location or not.
-     */
-    private boolean isTowardsMenu(float movement) {
-        return isMenuVisible()
-                && ((mOnLeft && movement <= 0)
-                        || (!mOnLeft && movement >= 0));
-    }
-
-    @Override
-    public void setAppName(String appName) {
-        if (appName == null) {
-            return;
-        }
-        Resources res = mContext.getResources();
-        final int count = mMenuItems.size();
-        for (int i = 0; i < count; i++) {
-            MenuItem item = mMenuItems.get(i);
-            String description = String.format(
-                    res.getString(R.string.notification_menu_accessibility),
-                    appName, item.getContentDescription());
-            View menuView = item.getMenuView();
-            if (menuView != null) {
-                menuView.setContentDescription(description);
-            }
-        }
-    }
-
-    @Override
-    public void onHeightUpdate() {
-        if (mParent == null || mMenuItems.size() == 0 || mMenuContainer == null) {
-            return;
-        }
-        int parentHeight = mParent.getActualHeight();
-        float translationY;
-        if (parentHeight < mVertSpaceForIcons) {
-            translationY = (parentHeight / 2) - (mHorizSpaceForIcon / 2);
-        } else {
-            translationY = (mVertSpaceForIcons - mHorizSpaceForIcon) / 2;
-        }
-        mMenuContainer.setTranslationY(translationY);
-    }
-
-    @Override
-    public void onTranslationUpdate(float translation) {
-        mTranslation = translation;
-        if (mAnimating || !mMenuFadedIn) {
-            // Don't adjust when animating, or if the menu hasn't been shown yet.
-            return;
-        }
-        final float fadeThreshold = mParent.getWidth() * 0.3f;
-        final float absTrans = Math.abs(translation);
-        float desiredAlpha = 0;
-        if (absTrans == 0) {
-            desiredAlpha = 0;
-        } else if (absTrans <= fadeThreshold) {
-            desiredAlpha = 1;
-        } else {
-            desiredAlpha = 1 - ((absTrans - fadeThreshold) / (mParent.getWidth() - fadeThreshold));
-        }
-        setMenuAlpha(desiredAlpha);
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (mMenuListener == null) {
-            // Nothing to do
-            return;
-        }
-        v.getLocationOnScreen(mIconLocation);
-        mParent.getLocationOnScreen(mParentLocation);
-        final int centerX = (int) (mHorizSpaceForIcon / 2);
-        final int centerY = v.getHeight() / 2;
-        final int x = mIconLocation[0] - mParentLocation[0] + centerX;
-        final int y = mIconLocation[1] - mParentLocation[1] + centerY;
-        final int index = mMenuContainer.indexOfChild(v);
-        mMenuListener.onMenuClicked(mParent, x, y, mMenuItems.get(index));
-    }
-
-    private boolean isMenuLocationChange() {
-        boolean onLeft = mTranslation > mIconPadding;
-        boolean onRight = mTranslation < -mIconPadding;
-        if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) {
-            return true;
-        }
-        return false;
-    }
-
-    private void setMenuLocation() {
-        boolean showOnLeft = mTranslation > 0;
-        if ((mIconsPlaced && showOnLeft == mOnLeft) || mSnapping || mMenuContainer == null
-                || !mMenuContainer.isAttachedToWindow()) {
-            // Do nothing
-            return;
-        }
-        final int count = mMenuContainer.getChildCount();
-        for (int i = 0; i < count; i++) {
-            final View v = mMenuContainer.getChildAt(i);
-            final float left = i * mHorizSpaceForIcon;
-            final float right = mParent.getWidth() - (mHorizSpaceForIcon * (i + 1));
-            v.setX(showOnLeft ? left : right);
-        }
-        mOnLeft = showOnLeft;
-        mIconsPlaced = true;
-    }
-
-    private void setMenuAlpha(float alpha) {
-        mAlpha = alpha;
-        if (mMenuContainer == null) {
-            return;
-        }
-        if (alpha == 0) {
-            mMenuFadedIn = false; // Can fade in again once it's gone.
-            mMenuContainer.setVisibility(View.INVISIBLE);
-        } else {
-            mMenuContainer.setVisibility(View.VISIBLE);
-        }
-        final int count = mMenuContainer.getChildCount();
-        for (int i = 0; i < count; i++) {
-            mMenuContainer.getChildAt(i).setAlpha(mAlpha);
-        }
-    }
-
-    /**
-     * Returns the horizontal space in pixels required to display the menu.
-     */
-    private float getSpaceForMenu() {
-        return mHorizSpaceForIcon * mMenuContainer.getChildCount();
-    }
-
-    private final class CheckForDrag implements Runnable {
-        @Override
-        public void run() {
-            final float absTransX = Math.abs(mTranslation);
-            final float bounceBackToMenuWidth = getSpaceForMenu();
-            final float notiThreshold = mParent.getWidth() * 0.4f;
-            if ((!isMenuVisible() || isMenuLocationChange())
-                    && absTransX >= bounceBackToMenuWidth * 0.4
-                    && absTransX < notiThreshold) {
-                fadeInMenu(notiThreshold);
-            }
-        }
-    }
-
-    private void fadeInMenu(final float notiThreshold) {
-        if (mDismissing || mAnimating) {
-            return;
-        }
-        if (isMenuLocationChange()) {
-            setMenuAlpha(0f);
-        }
-        final float transX = mTranslation;
-        final boolean fromLeft = mTranslation > 0;
-        setMenuLocation();
-        mFadeAnimator = ValueAnimator.ofFloat(mAlpha, 1);
-        mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                final float absTrans = Math.abs(transX);
-
-                boolean pastMenu = (fromLeft && transX <= notiThreshold)
-                        || (!fromLeft && absTrans <= notiThreshold);
-                if (pastMenu && !mMenuFadedIn) {
-                    setMenuAlpha((float) animation.getAnimatedValue());
-                }
-            }
-        });
-        mFadeAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mAnimating = true;
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                // TODO should animate back to 0f from current alpha
-                setMenuAlpha(0f);
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mAnimating = false;
-                mMenuFadedIn = mAlpha == 1;
-            }
-        });
-        mFadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
-        mFadeAnimator.setDuration(ICON_ALPHA_ANIM_DURATION);
-        mFadeAnimator.start();
-    }
-
-    @Override
-    public void setMenuItems(ArrayList<MenuItem> items) {
-        // Do nothing we use our own for now.
-        // TODO -- handle / allow custom menu items!
-    }
-
-    public static MenuItem createSnoozeItem(Context context) {
-        Resources res = context.getResources();
-        NotificationSnooze content = (NotificationSnooze) LayoutInflater.from(context)
-                .inflate(R.layout.notification_snooze, null, false);
-        String snoozeDescription = res.getString(R.string.notification_menu_snooze_description);
-        MenuItem snooze = new NotificationMenuItem(context, snoozeDescription, content,
-                R.drawable.ic_snooze);
-        return snooze;
-    }
-
-    public static MenuItem createInfoItem(Context context) {
-        Resources res = context.getResources();
-        String infoDescription = res.getString(R.string.notification_menu_gear_description);
-        NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
-                R.layout.notification_info, null, false);
-        MenuItem info = new NotificationMenuItem(context, infoDescription, infoContent,
-                R.drawable.ic_settings);
-        return info;
-    }
-
-    public static MenuItem createAppOpsItem(Context context) {
-        AppOpsInfo appOpsContent = (AppOpsInfo) LayoutInflater.from(context).inflate(
-                R.layout.app_ops_info, null, false);
-        MenuItem info = new NotificationMenuItem(context, null, appOpsContent,
-                -1 /*don't show in slow swipe menu */);
-        return info;
-    }
-
-    private void addMenuView(MenuItem item, ViewGroup parent) {
-        View menuView = item.getMenuView();
-        if (menuView != null) {
-            parent.addView(menuView);
-            menuView.setOnClickListener(this);
-            FrameLayout.LayoutParams lp = (LayoutParams) menuView.getLayoutParams();
-            lp.width = (int) mHorizSpaceForIcon;
-            lp.height = (int) mHorizSpaceForIcon;
-            menuView.setLayoutParams(lp);
-        }
-    }
-
-    public static class NotificationMenuItem implements MenuItem {
-        View mMenuView;
-        GutsContent mGutsContent;
-        String mContentDescription;
-
-        /**
-         * Add a new 'guts' panel. If iconResId < 0 it will not appear in the slow swipe menu
-         * but can still be exposed via other affordances.
-         */
-        public NotificationMenuItem(Context context, String s, GutsContent content, int iconResId) {
-            Resources res = context.getResources();
-            int padding = res.getDimensionPixelSize(R.dimen.notification_menu_icon_padding);
-            int tint = res.getColor(R.color.notification_gear_color);
-            if (iconResId >= 0) {
-                AlphaOptimizedImageView iv = new AlphaOptimizedImageView(context);
-                iv.setPadding(padding, padding, padding, padding);
-                Drawable icon = context.getResources().getDrawable(iconResId);
-                iv.setImageDrawable(icon);
-                iv.setColorFilter(tint);
-                iv.setAlpha(1f);
-                mMenuView = iv;
-            }
-            mContentDescription = s;
-            mGutsContent = content;
-        }
-
-        @Override
-        @Nullable
-        public View getMenuView() {
-            return mMenuView;
-        }
-
-        @Override
-        public View getGutsView() {
-            return mGutsContent.getContentView();
-        }
-
-        @Override
-        public String getContentDescription() {
-            return mContentDescription;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 21d3d81..c58eb80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -19,6 +19,11 @@
 import android.os.Handler;
 import android.view.View;
 
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+
 /**
  * An abstraction of something that presents notifications, e.g. StatusBar. Contains methods
  * for both querying the state of the system (some modularised piece of functionality may
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 9e87a0b..6de0543 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -42,6 +42,9 @@
 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.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.RemoteInputView;
 
 import java.io.FileDescriptor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 3063199..55dfd44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN_REVERSE;
 import static com.android.systemui.statusbar.phone.NotificationIconContainer.IconState.NO_VALUE;
 
 import android.content.Context;
@@ -26,21 +27,26 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.MathUtils;
+import android.view.DisplayCutout;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.statusbar.stack.AmbientState;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.ExpandableViewState;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.stack.StackScrollState;
-import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.StackScrollState;
+import com.android.systemui.statusbar.notification.stack.ViewState;
 
 /**
  * A notification shelf view that is placed inside the notification scroller. It manages the
@@ -86,6 +92,7 @@
     private boolean mShowNotificationShelf;
     private float mFirstElementRoundness;
     private Rect mClipRect = new Rect();
+    private int mCutoutHeight;
 
     public NotificationShelf(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -151,12 +158,11 @@
     }
 
     public void fadeInTranslating() {
-        float translation = mShelfIcons.getTranslationY();
-        mShelfIcons.setTranslationY(translation - mShelfAppearTranslation);
+        mShelfIcons.setTranslationY(-mShelfAppearTranslation);
         mShelfIcons.setAlpha(0);
         mShelfIcons.animate()
                 .setInterpolator(Interpolators.DECELERATE_QUINT)
-                .translationY(translation)
+                .translationY(0)
                 .setDuration(SHELF_IN_TRANSLATION_DURATION)
                 .start();
         mShelfIcons.animate()
@@ -198,8 +204,12 @@
                     0 : mAmbientState.getDarkAmount();
             mShelfState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio);
             mShelfState.zTranslation = ambientState.getBaseZHeight();
+            // For the small display size, it's not enough to make the icon not covered by
+            // the top cutout so the denominator add the height of cutout.
+            // Totally, (getIntrinsicHeight() * 2 + mCutoutHeight) should be smaller then
+            // mAmbientState.getTopPadding().
             float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation())
-                    / (getIntrinsicHeight() * 2);
+                    / (getIntrinsicHeight() * 2 + mCutoutHeight);
             openedAmount = Math.min(1.0f, openedAmount);
             mShelfState.openedAmount = openedAmount;
             mShelfState.clipTopAmount = 0;
@@ -743,6 +753,22 @@
         mRelativeOffset -= mTmp[0];
     }
 
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        WindowInsets ret = super.onApplyWindowInsets(insets);
+
+        // NotificationShelf drag from the status bar and the status bar dock on the top
+        // of the display for current design so just focus on the top of ScreenDecorations.
+        // In landscape or multiple window split mode, the NotificationShelf still drag from
+        // the top and the physical notch/cutout goes to the right, left, or both side of the
+        // display so it doesn't matter for the NotificationSelf in landscape.
+        DisplayCutout displayCutout = insets.getDisplayCutout();
+        mCutoutHeight = displayCutout == null || displayCutout.getSafeInsetTop() < 0
+                ? 0 : displayCutout.getSafeInsetTop();
+
+        return ret;
+    }
+
     private void setOpenedAmount(float openedAmount) {
         mNoAnimationsInThisFrame = openedAmount == 1.0f && mOpenedAmount == 0.0f;
         mOpenedAmount = openedAmount;
@@ -757,7 +783,7 @@
         int width = (int) NotificationUtils.interpolate(
                 start + mCollapsedIcons.getFinalTranslationX(),
                 mShelfIcons.getWidth(),
-                openedAmount);
+                FAST_OUT_SLOW_IN_REVERSE.getInterpolation(openedAmount));
         mShelfIcons.setActualLayoutWidth(width);
         boolean hasOverflow = mCollapsedIcons.hasOverflow();
         int collapsedPadding = mCollapsedIcons.getPaddingEnd();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
deleted file mode 100644
index d84d3dc..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
+++ /dev/null
@@ -1,482 +0,0 @@
-package com.android.systemui.statusbar;
-/*
- * 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
- */
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Typeface;
-import android.metrics.LogMaker;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.service.notification.SnoozeCriterion;
-import android.service.notification.StatusBarNotification;
-import android.text.SpannableString;
-import android.text.style.StyleSpan;
-import android.util.AttributeSet;
-import android.util.KeyValueListParser;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-
-public class NotificationSnooze extends LinearLayout
-        implements NotificationGuts.GutsContent, View.OnClickListener {
-
-    private static final String TAG = "NotificationSnooze";
-    /**
-     * If this changes more number increases, more assistant action resId's should be defined for
-     * accessibility purposes, see {@link #setSnoozeOptions(List)}
-     */
-    private static final int MAX_ASSISTANT_SUGGESTIONS = 1;
-    private static final String KEY_DEFAULT_SNOOZE = "default";
-    private static final String KEY_OPTIONS = "options_array";
-    private static final LogMaker OPTIONS_OPEN_LOG =
-            new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS)
-                    .setType(MetricsEvent.TYPE_OPEN);
-    private static final LogMaker OPTIONS_CLOSE_LOG =
-            new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS)
-                    .setType(MetricsEvent.TYPE_CLOSE);
-    private static final LogMaker UNDO_LOG =
-            new LogMaker(MetricsEvent.NOTIFICATION_UNDO_SNOOZE)
-                    .setType(MetricsEvent.TYPE_ACTION);
-    private NotificationGuts mGutsContainer;
-    private NotificationSwipeActionHelper mSnoozeListener;
-    private StatusBarNotification mSbn;
-
-    private TextView mSelectedOptionText;
-    private TextView mUndoButton;
-    private ImageView mExpandButton;
-    private View mDivider;
-    private ViewGroup mSnoozeOptionContainer;
-    private List<SnoozeOption> mSnoozeOptions;
-    private int mCollapsedHeight;
-    private SnoozeOption mDefaultOption;
-    private SnoozeOption mSelectedOption;
-    private boolean mSnoozing;
-    private boolean mExpanded;
-    private AnimatorSet mExpandAnimation;
-    private KeyValueListParser mParser;
-
-    private final static int[] sAccessibilityActions = {
-            R.id.action_snooze_shorter,
-            R.id.action_snooze_short,
-            R.id.action_snooze_long,
-            R.id.action_snooze_longer,
-    };
-
-    private MetricsLogger mMetricsLogger = new MetricsLogger();
-
-    public NotificationSnooze(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mParser = new KeyValueListParser(',');
-    }
-
-    @VisibleForTesting
-    SnoozeOption getDefaultOption()
-    {
-        return mDefaultOption;
-    }
-
-    @VisibleForTesting
-    void setKeyValueListParser(KeyValueListParser parser) {
-        mParser = parser;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.snooze_snackbar_min_height);
-        findViewById(R.id.notification_snooze).setOnClickListener(this);
-        mSelectedOptionText = (TextView) findViewById(R.id.snooze_option_default);
-        mUndoButton = (TextView) findViewById(R.id.undo);
-        mUndoButton.setOnClickListener(this);
-        mExpandButton = (ImageView) findViewById(R.id.expand_button);
-        mDivider = findViewById(R.id.divider);
-        mDivider.setAlpha(0f);
-        mSnoozeOptionContainer = (ViewGroup) findViewById(R.id.snooze_options);
-        mSnoozeOptionContainer.setVisibility(View.INVISIBLE);
-        mSnoozeOptionContainer.setAlpha(0f);
-
-        // Create the different options based on list
-        mSnoozeOptions = getDefaultSnoozeOptions();
-        createOptionViews();
-
-        setSelected(mDefaultOption, false);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        logOptionSelection(MetricsEvent.NOTIFICATION_SNOOZE_CLICKED, mDefaultOption);
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        if (mGutsContainer != null && mGutsContainer.isExposed()) {
-            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
-                event.getText().add(mSelectedOptionText.getText());
-            }
-        }
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        info.addAction(new AccessibilityAction(R.id.action_snooze_undo,
-                getResources().getString(R.string.snooze_undo)));
-        int count = mSnoozeOptions.size();
-        for (int i = 0; i < count; i++) {
-            AccessibilityAction action = mSnoozeOptions.get(i).getAccessibilityAction();
-            if (action != null) {
-                info.addAction(action);
-            }
-        }
-    }
-
-    @Override
-    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
-        if (super.performAccessibilityActionInternal(action, arguments)) {
-            return true;
-        }
-        if (action == R.id.action_snooze_undo) {
-            undoSnooze(mUndoButton);
-            return true;
-        }
-        for (int i = 0; i < mSnoozeOptions.size(); i++) {
-            SnoozeOption so = mSnoozeOptions.get(i);
-            if (so.getAccessibilityAction() != null
-                    && so.getAccessibilityAction().getId() == action) {
-                setSelected(so, true);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public void setSnoozeOptions(final List<SnoozeCriterion> snoozeList) {
-        if (snoozeList == null) {
-            return;
-        }
-        mSnoozeOptions.clear();
-        mSnoozeOptions = getDefaultSnoozeOptions();
-        final int count = Math.min(MAX_ASSISTANT_SUGGESTIONS, snoozeList.size());
-        for (int i = 0; i < count; i++) {
-            SnoozeCriterion sc = snoozeList.get(i);
-            AccessibilityAction action = new AccessibilityAction(
-                    R.id.action_snooze_assistant_suggestion_1, sc.getExplanation());
-            mSnoozeOptions.add(new NotificationSnoozeOption(sc, 0, sc.getExplanation(),
-                    sc.getConfirmation(), action));
-        }
-        createOptionViews();
-    }
-
-    public boolean isExpanded() {
-        return mExpanded;
-    }
-
-    public void setSnoozeListener(NotificationSwipeActionHelper listener) {
-        mSnoozeListener = listener;
-    }
-
-    public void setStatusBarNotification(StatusBarNotification sbn) {
-        mSbn = sbn;
-    }
-
-    @VisibleForTesting
-    ArrayList<SnoozeOption> getDefaultSnoozeOptions() {
-        final Resources resources = getContext().getResources();
-        ArrayList<SnoozeOption> options = new ArrayList<>();
-        try {
-            final String config = Settings.Global.getString(getContext().getContentResolver(),
-                    Settings.Global.NOTIFICATION_SNOOZE_OPTIONS);
-            mParser.setString(config);
-        } catch (IllegalArgumentException e) {
-            Log.e(TAG, "Bad snooze constants");
-        }
-
-        final int defaultSnooze = mParser.getInt(KEY_DEFAULT_SNOOZE,
-                resources.getInteger(R.integer.config_notification_snooze_time_default));
-        final int[] snoozeTimes = mParser.getIntArray(KEY_OPTIONS,
-                resources.getIntArray(R.array.config_notification_snooze_times));
-
-        for (int i = 0; i < snoozeTimes.length && i < sAccessibilityActions.length; i++) {
-            int snoozeTime = snoozeTimes[i];
-            SnoozeOption option = createOption(snoozeTime, sAccessibilityActions[i]);
-            if (i == 0 || snoozeTime == defaultSnooze) {
-                mDefaultOption = option;
-            }
-            options.add(option);
-        }
-        return options;
-    }
-
-    private SnoozeOption createOption(int minutes, int accessibilityActionId) {
-        Resources res = getResources();
-        boolean showInHours = minutes >= 60;
-        int pluralResId = showInHours
-                ? R.plurals.snoozeHourOptions
-                : R.plurals.snoozeMinuteOptions;
-        int count = showInHours ? (minutes / 60) : minutes;
-        String description = res.getQuantityString(pluralResId, count, count);
-        String resultText = String.format(res.getString(R.string.snoozed_for_time), description);
-        SpannableString string = new SpannableString(resultText);
-        string.setSpan(new StyleSpan(Typeface.BOLD),
-                resultText.length() - description.length(), resultText.length(), 0 /* flags */);
-        AccessibilityAction action = new AccessibilityAction(accessibilityActionId, description);
-        return new NotificationSnoozeOption(null, minutes, description, string,
-                action);
-    }
-
-    private void createOptionViews() {
-        mSnoozeOptionContainer.removeAllViews();
-        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
-        for (int i = 0; i < mSnoozeOptions.size(); i++) {
-            SnoozeOption option = mSnoozeOptions.get(i);
-            TextView tv = (TextView) inflater.inflate(R.layout.notification_snooze_option,
-                    mSnoozeOptionContainer, false);
-            mSnoozeOptionContainer.addView(tv);
-            tv.setText(option.getDescription());
-            tv.setTag(option);
-            tv.setOnClickListener(this);
-        }
-    }
-
-    private void hideSelectedOption() {
-        final int childCount = mSnoozeOptionContainer.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = mSnoozeOptionContainer.getChildAt(i);
-            child.setVisibility(child.getTag() == mSelectedOption ? View.GONE : View.VISIBLE);
-        }
-    }
-
-    private void showSnoozeOptions(boolean show) {
-        int drawableId = show ? com.android.internal.R.drawable.ic_collapse_notification
-                : com.android.internal.R.drawable.ic_expand_notification;
-        mExpandButton.setImageResource(drawableId);
-        if (mExpanded != show) {
-            mExpanded = show;
-            animateSnoozeOptions(show);
-            if (mGutsContainer != null) {
-                mGutsContainer.onHeightChanged();
-            }
-        }
-    }
-
-    private void animateSnoozeOptions(boolean show) {
-        if (mExpandAnimation != null) {
-            mExpandAnimation.cancel();
-        }
-        ObjectAnimator dividerAnim = ObjectAnimator.ofFloat(mDivider, View.ALPHA,
-                mDivider.getAlpha(), show ? 1f : 0f);
-        ObjectAnimator optionAnim = ObjectAnimator.ofFloat(mSnoozeOptionContainer, View.ALPHA,
-                mSnoozeOptionContainer.getAlpha(), show ? 1f : 0f);
-        mSnoozeOptionContainer.setVisibility(View.VISIBLE);
-        mExpandAnimation = new AnimatorSet();
-        mExpandAnimation.playTogether(dividerAnim, optionAnim);
-        mExpandAnimation.setDuration(150);
-        mExpandAnimation.setInterpolator(show ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
-        mExpandAnimation.addListener(new AnimatorListenerAdapter() {
-            boolean cancelled = false;
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                cancelled = true;
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (!show && !cancelled) {
-                    mSnoozeOptionContainer.setVisibility(View.INVISIBLE);
-                    mSnoozeOptionContainer.setAlpha(0f);
-                }
-            }
-        });
-        mExpandAnimation.start();
-    }
-
-    private void setSelected(SnoozeOption option, boolean userAction) {
-        mSelectedOption = option;
-        mSelectedOptionText.setText(option.getConfirmation());
-        showSnoozeOptions(false);
-        hideSelectedOption();
-        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-        if (userAction) {
-            logOptionSelection(MetricsEvent.NOTIFICATION_SELECT_SNOOZE, option);
-        }
-    }
-
-    private void logOptionSelection(int category, SnoozeOption option) {
-        int index = mSnoozeOptions.indexOf(option);
-        long duration = TimeUnit.MINUTES.toMillis(option.getMinutesToSnoozeFor());
-        mMetricsLogger.write(new LogMaker(category)
-                .setType(MetricsEvent.TYPE_ACTION)
-                .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_INDEX, index)
-                .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS, duration));
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (mGutsContainer != null) {
-            mGutsContainer.resetFalsingCheck();
-        }
-        final int id = v.getId();
-        final SnoozeOption tag = (SnoozeOption) v.getTag();
-        if (tag != null) {
-            setSelected(tag, true);
-        } else if (id == R.id.notification_snooze) {
-            // Toggle snooze options
-            showSnoozeOptions(!mExpanded);
-            mMetricsLogger.write(!mExpanded ? OPTIONS_OPEN_LOG : OPTIONS_CLOSE_LOG);
-        } else {
-            // Undo snooze was selected
-            undoSnooze(v);
-            mMetricsLogger.write(UNDO_LOG);
-        }
-    }
-
-    private void undoSnooze(View v) {
-        mSelectedOption = null;
-        int[] parentLoc = new int[2];
-        int[] targetLoc = new int[2];
-        mGutsContainer.getLocationOnScreen(parentLoc);
-        v.getLocationOnScreen(targetLoc);
-        final int centerX = v.getWidth() / 2;
-        final int centerY = v.getHeight() / 2;
-        final int x = targetLoc[0] - parentLoc[0] + centerX;
-        final int y = targetLoc[1] - parentLoc[1] + centerY;
-        showSnoozeOptions(false);
-        mGutsContainer.closeControls(x, y, false /* save */, false /* force */);
-    }
-
-    @Override
-    public int getActualHeight() {
-        return mExpanded ? getHeight() : mCollapsedHeight;
-    }
-
-    @Override
-    public boolean willBeRemoved() {
-        return mSnoozing;
-    }
-
-    @Override
-    public View getContentView() {
-        // Reset the view before use
-        setSelected(mDefaultOption, false);
-        return this;
-    }
-
-    @Override
-    public void setGutsParent(NotificationGuts guts) {
-        mGutsContainer = guts;
-    }
-
-    @Override
-    public boolean handleCloseControls(boolean save, boolean force) {
-        if (mExpanded && !force) {
-            // Collapse expanded state on outside touch
-            showSnoozeOptions(false);
-            return true;
-        } else if (mSnoozeListener != null && mSelectedOption != null) {
-            // Snooze option selected so commit it
-            mSnoozing = true;
-            mSnoozeListener.snooze(mSbn, mSelectedOption);
-            return true;
-        } else {
-            // The view should actually be closed
-            setSelected(mSnoozeOptions.get(0), false);
-            return false; // Return false here so that guts handles closing the view
-        }
-    }
-
-    @Override
-    public boolean isLeavebehind() {
-        return true;
-    }
-
-    @Override
-    public boolean shouldBeSaved() {
-        return true;
-    }
-
-    public class NotificationSnoozeOption implements SnoozeOption {
-        private SnoozeCriterion mCriterion;
-        private int mMinutesToSnoozeFor;
-        private CharSequence mDescription;
-        private CharSequence mConfirmation;
-        private AccessibilityAction mAction;
-
-        public NotificationSnoozeOption(SnoozeCriterion sc, int minToSnoozeFor,
-                CharSequence description,
-                CharSequence confirmation, AccessibilityAction action) {
-            mCriterion = sc;
-            mMinutesToSnoozeFor = minToSnoozeFor;
-            mDescription = description;
-            mConfirmation = confirmation;
-            mAction = action;
-        }
-
-        @Override
-        public SnoozeCriterion getSnoozeCriterion() {
-            return mCriterion;
-        }
-
-        @Override
-        public CharSequence getDescription() {
-            return mDescription;
-        }
-
-        @Override
-        public CharSequence getConfirmation() {
-            return mConfirmation;
-        }
-
-        @Override
-        public int getMinutesToSnoozeFor() {
-            return mMinutesToSnoozeFor;
-        }
-
-        @Override
-        public AccessibilityAction getAccessibilityAction() {
-            return mAction;
-        }
-
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
new file mode 100644
index 0000000..47b7fe9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -0,0 +1,163 @@
+/*
+ * 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.systemui.statusbar;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.RemoteInput;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+
+import com.android.systemui.statusbar.notification.NotificationData;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * By diffing two entries, determines is view reinflation needed.
+ */
+public class NotificationUiAdjustment {
+
+    public final String key;
+    public final List<Notification.Action> smartActions;
+    public final CharSequence[] smartReplies;
+
+    @VisibleForTesting
+    NotificationUiAdjustment(
+            String key, List<Notification.Action> smartActions, CharSequence[] smartReplies) {
+        this.key = key;
+        this.smartActions = smartActions == null
+                ? Collections.emptyList()
+                : new ArrayList<>(smartActions);
+        this.smartReplies = smartReplies == null ? new CharSequence[0] : smartReplies.clone();
+    }
+
+    public static NotificationUiAdjustment extractFromNotificationEntry(
+            NotificationData.Entry entry) {
+        return new NotificationUiAdjustment(entry.key, entry.smartActions, entry.smartReplies);
+    }
+
+    public static boolean needReinflate(
+            @NonNull NotificationUiAdjustment oldAdjustment,
+            @NonNull NotificationUiAdjustment newAdjustment) {
+        if (oldAdjustment == newAdjustment) {
+            return false;
+        }
+        if (areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions)) {
+            return true;
+        }
+        if (!Arrays.equals(oldAdjustment.smartReplies, newAdjustment.smartReplies)) {
+            return true;
+        }
+        return false;
+    }
+
+    public static boolean areDifferent(
+            @NonNull List<Notification.Action> first, @NonNull List<Notification.Action> second) {
+        if (first == second) {
+            return false;
+        }
+        if (first == null || second == null) {
+            return true;
+        }
+        if (first.size() != second.size()) {
+            return true;
+        }
+        for (int i = 0; i < first.size(); i++) {
+            Notification.Action firstAction = first.get(i);
+            Notification.Action secondAction = second.get(i);
+
+            if (!TextUtils.equals(firstAction.title, secondAction.title)) {
+                return true;
+            }
+
+            if (areDifferent(firstAction.getIcon(), secondAction.getIcon())) {
+                return true;
+            }
+
+            if (!Objects.equals(firstAction.actionIntent, secondAction.actionIntent)) {
+                return true;
+            }
+
+            if (areDifferent(firstAction.getRemoteInputs(), secondAction.getRemoteInputs())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean areDifferent(@Nullable Icon first, @Nullable Icon second) {
+        if (first == second) {
+            return false;
+        }
+        if (first == null || second == null) {
+            return true;
+        }
+        return !first.sameAs(second);
+    }
+
+    private static boolean areDifferent(
+            @Nullable RemoteInput[] first, @Nullable RemoteInput[] second) {
+        if (first == second) {
+            return false;
+        }
+        if (first == null || second == null) {
+            return true;
+        }
+        if (first.length != second.length) {
+            return true;
+        }
+        for (int i = 0; i < first.length; i++) {
+            RemoteInput firstRemoteInput = first[i];
+            RemoteInput secondRemoteInput = second[i];
+
+            if (!TextUtils.equals(firstRemoteInput.getLabel(), secondRemoteInput.getLabel())) {
+                return true;
+            }
+            if (areDifferent(firstRemoteInput.getChoices(), secondRemoteInput.getChoices())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean areDifferent(
+            @Nullable CharSequence[] first, @Nullable CharSequence[] second) {
+        if (first == second) {
+            return false;
+        }
+        if (first == null || second == null) {
+            return true;
+        }
+        if (first.length != second.length) {
+            return true;
+        }
+        for (int i = 0; i < first.length; i++) {
+            CharSequence firstCharSequence = first[i];
+            CharSequence secondCharSequence = second[i];
+            if (!TextUtils.equals(firstCharSequence, secondCharSequence)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 341c692..d479838 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -19,14 +19,17 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Trace;
-import android.service.notification.NotificationListenerService;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+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.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 
 import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index b0d5536..7f63191 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -17,8 +17,7 @@
 package com.android.systemui.statusbar;
 
 import com.android.internal.util.Preconditions;
-import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.phone.StatusBarWindowManager;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.policy.RemoteInputView;
 
 import android.app.Notification;
@@ -26,7 +25,6 @@
 import android.content.Context;
 import android.os.SystemProperties;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Pair;
 
 import java.lang.ref.WeakReference;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 5ba75de..e43c9e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -21,6 +21,8 @@
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 
 import java.util.Set;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
deleted file mode 100644
index c5b3560..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
+++ /dev/null
@@ -1,218 +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
- */
-
-package com.android.systemui.statusbar;
-
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Interpolators;
-
-/**
- * A common base class for all views in the notification stack scroller which don't have a
- * background.
- */
-public abstract class StackScrollerDecorView extends ExpandableView {
-
-    protected View mContent;
-    protected View mSecondaryView;
-    private boolean mIsVisible = true;
-    private boolean mContentVisible = true;
-    private boolean mIsSecondaryVisible = true;
-    private int mDuration = 260;
-    private boolean mContentAnimating;
-    private final Runnable mContentVisibilityEndRunnable = () -> {
-        mContentAnimating = false;
-        if (getVisibility() != View.GONE && !mIsVisible) {
-            setVisibility(GONE);
-            setWillBeGone(false);
-            notifyHeightChanged(false /* needsAnimation */);
-        }
-    };
-
-    public StackScrollerDecorView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mContent = findContentView();
-        mSecondaryView = findSecondaryView();
-        setVisible(false /* nowVisible */, false /* animate */);
-        setSecondaryVisible(false /* nowVisible */, false /* animate */);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        setOutlineProvider(null);
-    }
-
-    @Override
-    public boolean isTransparent() {
-        return true;
-    }
-
-    /**
-     * Set the content of this view to be visible in an animated way.
-     *
-     * @param contentVisible True if the content should be visible or false if it should be hidden.
-     */
-    public void setContentVisible(boolean contentVisible) {
-        setContentVisible(contentVisible, true /* animate */);
-    }
-    /**
-     * Set the content of this view to be visible.
-     * @param contentVisible True if the content should be visible or false if it should be hidden.
-     * @param animate Should an animation be performed.
-     */
-    private void setContentVisible(boolean contentVisible, boolean animate) {
-        if (mContentVisible != contentVisible) {
-            mContentAnimating = animate;
-            setViewVisible(mContent, contentVisible, animate, mContentVisibilityEndRunnable);
-            mContentVisible = contentVisible;
-        } if (!mContentAnimating) {
-            mContentVisibilityEndRunnable.run();
-        }
-    }
-
-    public boolean isContentVisible() {
-        return mContentVisible;
-    }
-
-    /**
-     * Make this view visible. If {@code false} is passed, the view will fade out it's content
-     * and set the view Visibility to GONE. If only the content should be changed
-     * {@link #setContentVisible(boolean)} can be used.
-     *
-     * @param nowVisible should the view be visible
-     * @param animate should the change be animated.
-     */
-    public void setVisible(boolean nowVisible, boolean animate) {
-        if (mIsVisible != nowVisible) {
-            mIsVisible = nowVisible;
-            if (animate) {
-                if (nowVisible) {
-                    setVisibility(VISIBLE);
-                    setWillBeGone(false);
-                    notifyHeightChanged(false /* needsAnimation */);
-                } else {
-                    setWillBeGone(true);
-                }
-                setContentVisible(nowVisible, true /* animate */);
-            } else {
-                setVisibility(nowVisible ? VISIBLE : GONE);
-                setContentVisible(nowVisible, false /* animate */);
-                setWillBeGone(false);
-                notifyHeightChanged(false /* needsAnimation */);
-            }
-        }
-    }
-
-    /**
-     * Set the secondary view of this layout to visible.
-     *
-     * @param nowVisible should the secondary view be visible
-     * @param animate should the change be animated
-     */
-    public void setSecondaryVisible(boolean nowVisible, boolean animate) {
-        if (mIsSecondaryVisible != nowVisible) {
-            setViewVisible(mSecondaryView, nowVisible, animate, null /* endRunnable */);
-            mIsSecondaryVisible = nowVisible;
-        }
-    }
-
-    @VisibleForTesting
-    boolean isSecondaryVisible() {
-        return mIsSecondaryVisible;
-    }
-
-    /**
-     * Is this view visible. If a view is currently animating to gone, it will
-     * return {@code false}.
-     */
-    public boolean isVisible() {
-        return mIsVisible;
-    }
-
-    void setDuration(int duration) {
-        mDuration = duration;
-    }
-
-    /**
-     * Animate a view to a new visibility.
-     * @param view Target view, maybe content view or dismiss view.
-     * @param nowVisible Should it now be visible.
-     * @param animate Should this be done in an animated way.
-     * @param endRunnable A runnable that is run when the animation is done.
-     */
-    private void setViewVisible(View view, boolean nowVisible,
-            boolean animate, Runnable endRunnable) {
-        if (view == null) {
-            return;
-        }
-        // cancel any previous animations
-        view.animate().cancel();
-        float endValue = nowVisible ? 1.0f : 0.0f;
-        if (!animate) {
-            view.setAlpha(endValue);
-            if (endRunnable != null) {
-                endRunnable.run();
-            }
-            return;
-        }
-
-        // Animate the view alpha
-        Interpolator interpolator = nowVisible ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT;
-        view.animate()
-                .alpha(endValue)
-                .setInterpolator(interpolator)
-                .setDuration(mDuration)
-                .withEndAction(endRunnable);
-    }
-
-    @Override
-    public void performRemoveAnimation(long duration, long delay,
-            float translationDirection, boolean isHeadsUpAnimation, float endLocation,
-            Runnable onFinishedRunnable,
-            AnimatorListenerAdapter animationListener) {
-        // TODO: Use duration
-        setContentVisible(false);
-    }
-
-    @Override
-    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
-        // TODO: use delay and duration
-        setContentVisible(true);
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-    protected abstract View findContentView();
-
-    /**
-     * Returns a view that might not always appear while the main content view is still visible.
-     */
-    protected abstract View findSecondaryView();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index c820e2b..cc5fbe5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -661,8 +661,9 @@
             if (hsl[1] < 0.2f) {
                 contrastedColor = Notification.COLOR_DEFAULT;
             }
+            boolean isDark = !ContrastColorUtil.isColorLight(mCachedContrastBackgroundColor);
             contrastedColor = ContrastColorUtil.resolveContrastColor(mContext,
-                    contrastedColor, mCachedContrastBackgroundColor);
+                    contrastedColor, mCachedContrastBackgroundColor, isDark);
         }
         mContrastedDrawableColor = contrastedColor;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index ccbf483..3c52e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -126,20 +126,21 @@
     }
 
     public void applyMobileState(MobileIconState state) {
+        boolean requestLayout = false;
         if (state == null) {
+            requestLayout = getVisibility() != View.GONE;
             setVisibility(View.GONE);
             mState = null;
-            return;
-        }
-
-        if (mState == null) {
+        } else if (mState == null) {
+            requestLayout = true;
             mState = state.copy();
             initViewState();
-            return;
+        } else if (!mState.equals(state)) {
+            requestLayout = updateState(state.copy());
         }
 
-        if (!mState.equals(state)) {
-            updateState(state.copy());
+        if (requestLayout) {
+            requestLayout();
         }
     }
 
@@ -162,20 +163,24 @@
         mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
         mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
         mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
-        mOut.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
+        mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE);
         mInoutContainer.setVisibility((mState.activityIn || mState.activityOut)
                 ? View.VISIBLE : View.GONE);
     }
 
-    private void updateState(MobileIconState state) {
+    private boolean updateState(MobileIconState state) {
+        boolean needsLayout = false;
+
         setContentDescription(state.contentDescription);
         if (mState.visible != state.visible) {
             mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
+            needsLayout = true;
         }
         if (mState.strengthId != state.strengthId) {
             mMobileDrawable.setLevel(state.strengthId);
         }
         if (mState.typeId != state.typeId) {
+            needsLayout |= state.typeId == 0 || mState.typeId == 0;
             if (state.typeId != 0) {
                 mMobileType.setContentDescription(state.typeContentDescription);
                 mMobileType.setImageResource(state.typeId);
@@ -188,11 +193,16 @@
         mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
         mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
         mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
-        mOut.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
+        mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE);
         mInoutContainer.setVisibility((state.activityIn || state.activityOut)
                 ? View.VISIBLE : View.GONE);
 
+        needsLayout |= state.roaming != mState.roaming
+                || state.activityIn != mState.activityIn
+                || state.activityOut != mState.activityOut;
+
         mState = state;
+        return needsLayout;
     }
 
     @Override
@@ -243,7 +253,7 @@
     }
 
     @Override
-    public void setVisibleState(int state) {
+    public void setVisibleState(int state, boolean animate) {
         if (state == mVisibleState) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
index 0ed6b77..f3fc99e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
@@ -124,7 +124,7 @@
     }
 
     @Override
-    public void setVisibleState(int state) {
+    public void setVisibleState(int state, boolean animate) {
         if (state == mVisibleState) {
             return;
         }
@@ -191,23 +191,26 @@
     }
 
     public void applyWifiState(WifiIconState state) {
+        boolean requestLayout = false;
+
         if (state == null) {
+            requestLayout = getVisibility() != View.GONE;
             setVisibility(View.GONE);
             mState = null;
-            return;
-        }
-
-        if (mState == null) {
+        } else if (mState == null) {
+            requestLayout = true;
             mState = state.copy();
             initViewState();
+        } else if (!mState.equals(state)) {
+            requestLayout = updateState(state.copy());
         }
 
-        if (!mState.equals(state)) {
-            updateState(state.copy());
+        if (requestLayout) {
+            requestLayout();
         }
     }
 
-    private void updateState(WifiIconState state) {
+    private boolean updateState(WifiIconState state) {
         setContentDescription(state.contentDescription);
         if (mState.resId != state.resId && state.resId >= 0) {
             NeutralGoodDrawable drawable = NeutralGoodDrawable
@@ -222,11 +225,17 @@
                 (state.activityIn || state.activityOut) ? View.VISIBLE : View.GONE);
         mAirplaneSpacer.setVisibility(state.airplaneSpacerVisible ? View.VISIBLE : View.GONE);
         mSignalSpacer.setVisibility(state.signalSpacerVisible ? View.VISIBLE : View.GONE);
+
+        boolean needsLayout = state.activityIn != mState.activityIn
+                ||state.activityOut != mState.activityOut;
+
         if (mState.visible != state.visible) {
+            needsLayout |= true;
             setVisibility(state.visible ? View.VISIBLE : View.GONE);
         }
 
         mState = state;
+        return needsLayout;
     }
 
     private void initViewState() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
index b831b86..beb90b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
@@ -22,7 +22,10 @@
     String getSlot();
     void setStaticDrawableColor(int color);
     void setDecorColor(int color);
-    void setVisibleState(int state);
+    default void setVisibleState(int state) {
+        setVisibleState(state, false);
+    }
+    void setVisibleState(int state, boolean animate);
     int getVisibleState();
     boolean isIconVisible();
     default boolean isIconBlocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index 09b11c2..7b5a70e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -28,7 +28,7 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.TransformState;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
 import java.util.Stack;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 83021ca..aa07c70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -17,9 +17,9 @@
 package com.android.systemui.statusbar.car;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
-import android.os.SystemProperties;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
@@ -28,6 +28,7 @@
 import android.view.WindowManager;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.settingslib.users.UserManagerHelper;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
@@ -49,14 +50,13 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Map;
+
 /**
  * A status bar (and navigation bar) tailored for the automotive use case.
  */
 public class CarStatusBar extends StatusBar implements
         CarBatteryController.BatteryViewHandler {
     private static final String TAG = "CarStatusBar";
-    public static final boolean ENABLE_HVAC_CONNECTION
-            = !SystemProperties.getBoolean("android.car.hvac.demo", true);
 
     private TaskStackListenerImpl mTaskStackListener;
 
@@ -95,10 +95,9 @@
         createBatteryController();
         mCarBatteryController.startListening();
 
-        if (ENABLE_HVAC_CONNECTION) {
-            Log.d(TAG, "Connecting to HVAC service");
-            Dependency.get(HvacController.class).connectToCarService();
-        }
+        Log.d(TAG, "Connecting to HVAC service");
+        Dependency.get(HvacController.class).connectToCarService();
+
         mCarFacetButtonController = Dependency.get(CarFacetButtonController.class);
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
@@ -121,9 +120,9 @@
      */
     private void restartNavBars() {
         mCarFacetButtonController.removeAll();
-        if (ENABLE_HVAC_CONNECTION) {
-            Dependency.get(HvacController.class).removeAllComponents();
-        }
+
+        Dependency.get(HvacController.class).removeAllComponents();
+
         if (mNavigationBarWindow != null) {
             mNavigationBarWindow.removeAllViews();
             mNavigationBarView = null;
@@ -145,7 +144,7 @@
      * Allows for showing or hiding just the navigation bars. This is indented to be used when
      * the full screen user selector is shown.
      */
-     void setNavBarVisibility(@View.Visibility int visibility) {
+    void setNavBarVisibility(@View.Visibility int visibility) {
         if (mNavigationBarWindow != null) {
             mNavigationBarWindow.setVisibility(visibility);
         }
@@ -266,12 +265,12 @@
     private void buildNavBarWindows() {
         if (mShowBottom) {
 
-             mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
+            mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
                     R.layout.navigation_bar_window, null);
         }
         if (mShowLeft) {
             mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext,
-                R.layout.navigation_bar_window, null);
+                    R.layout.navigation_bar_window, null);
         }
         if (mShowRight) {
             mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext,
@@ -376,10 +375,12 @@
                     + "," + mStackScroller.getScrollY());
         }
 
-        pw.print("  mTaskStackListener="); pw.println(mTaskStackListener);
+        pw.print("  mTaskStackListener=");
+        pw.println(mTaskStackListener);
         pw.print("  mCarFacetButtonController=");
         pw.println(mCarFacetButtonController);
-        pw.print("  mFullscreenUserSwitcher="); pw.println(mFullscreenUserSwitcher);
+        pw.print("  mFullscreenUserSwitcher=");
+        pw.println(mFullscreenUserSwitcher);
         pw.print("  mCarBatteryController=");
         pw.println(mCarBatteryController);
         pw.print("  mBatteryMeterView=");
@@ -398,7 +399,10 @@
 
         pw.println("SharedPreferences:");
         for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
-            pw.print("  "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue());
+            pw.print("  ");
+            pw.print(entry.getKey());
+            pw.print("=");
+            pw.println(entry.getValue());
         }
     }
 
@@ -444,15 +448,15 @@
     }
 
     /**
-     * An implementation of SysUiTaskStackChangeListener, that listens for changes in the system task
-     * stack and notifies the navigation bar.
+     * An implementation of SysUiTaskStackChangeListener, that listens for changes in the system
+     * task stack and notifies the navigation bar.
      */
     private class TaskStackListenerImpl extends SysUiTaskStackChangeListener {
         @Override
         public void onTaskStackChanged() {
             try {
                 mCarFacetButtonController.taskChanged(
-                        ActivityManager.getService().getAllStackInfos());
+                        ActivityTaskManager.getService().getAllStackInfos());
             } catch (Exception e) {
                 Log.e(TAG, "Getting StackInfo from activity manager failed", e);
             }
@@ -482,6 +486,13 @@
     @Override
     public void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
         super.updateKeyguardState(goingToFullShade, fromShadeLocked);
+        UserManagerHelper helper = new UserManagerHelper(mContext);
+        if (!helper.isHeadlessSystemUser()) {
+            showUserSwitcher();
+        }
+    }
+
+    public void showUserSwitcher() {
         if (mFullscreenUserSwitcher != null) {
             if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
                 mFullscreenUserSwitcher.show();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
index dabe23e..d0f0629 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
@@ -39,4 +39,13 @@
     protected boolean shouldDestroyViewOnReset() {
         return true;
     }
+
+    /**
+     * Called when cancel button in bouncer is pressed.
+     */
+    @Override
+    public void onCancelClicked() {
+        CarStatusBar statusBar = (CarStatusBar) mStatusBar;
+        statusBar.showUserSwitcher();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index d720aef..506d697 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -40,7 +40,6 @@
     private final int mShortAnimDuration;
     private final StatusBar mStatusBar;
     private final UserManagerHelper mUserManagerHelper;
-    private int mCurrentForegroundUserId;
     private boolean mShowing;
 
     public FullscreenUserSwitcher(StatusBar statusBar, ViewStub containerStub, Context context) {
@@ -55,17 +54,14 @@
         mUserGridView.setUserSelectionListener(this::onUserSelected);
 
         mUserManagerHelper = new UserManagerHelper(context);
-        updateCurrentForegroundUser();
 
         mShortAnimDuration = mContainer.getResources()
             .getInteger(android.R.integer.config_shortAnimTime);
     }
 
     public void show() {
-        // On a switch from the system user, don't show the user switcher
-        if (mUserManagerHelper.isHeadlessSystemUser() && mUserManagerHelper
-            .userIsSystemUser(mUserManagerHelper.getForegroundUserInfo())) {
-            return;
+        if (mUserManagerHelper.isHeadlessSystemUser()) {
+            showUserGrid();
         }
         if (mShowing) {
             return;
@@ -80,32 +76,31 @@
     }
 
     public void onUserSwitched(int newUserId) {
-        // The logic for foreground user change is needed here to exclude the reboot case. On
-        // reboot, system fires ACTION_USER_SWITCHED change from -1 to 0 user. This is not an actual
-        // user switch. We only want to trigger keyguard dismissal when foreground user changes.
-        if (foregroundUserChanged()) {
-            toggleSwitchInProgress(false);
-            updateCurrentForegroundUser();
-            mParent.post(this::dismissKeyguard);
-        }
-    }
-
-    private boolean foregroundUserChanged() {
-        return mCurrentForegroundUserId != mUserManagerHelper.getForegroundUserId();
-    }
-
-    private void updateCurrentForegroundUser() {
-        mCurrentForegroundUserId = mUserManagerHelper.getForegroundUserId();
+        toggleSwitchInProgress(false);
+        mParent.post(this::dismissKeyguard);
     }
 
     private void onUserSelected(UserGridRecyclerView.UserRecord record) {
-        if (record.mIsForeground) {
+        if (mUserManagerHelper.isHeadlessSystemUser()) {
+            hideUserGrid();
+        }
+
+        if (record.mIsForeground || (record.mIsStartGuestSession
+                && mUserManagerHelper.foregroundUserIsGuestUser())) {
             dismissKeyguard();
             return;
         }
         toggleSwitchInProgress(true);
     }
 
+    private void showUserGrid() {
+        mUserGridView.setVisibility(View.VISIBLE);
+    }
+
+    private void hideUserGrid() {
+        mUserGridView.setVisibility(View.INVISIBLE);
+    }
+
     // Dismisses the keyguard and shows bouncer if authentication is necessary.
     private void dismissKeyguard() {
         mStatusBar.executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 257fa75..23724c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -109,14 +109,12 @@
             userRecords.add(record);
         }
 
-        // Add guest user record if the foreground user is not a guest
-        if (!mUserManagerHelper.foregroundUserIsGuestUser()) {
-            userRecords.add(addGuestUserRecord());
-        }
+        // Add button for starting guest session.
+        userRecords.add(createStartGuestUserRecord());
 
         // Add add user record if the foreground user can add users
         if (mUserManagerHelper.foregroundUserCanAddUsers()) {
-            userRecords.add(addUserRecord());
+            userRecords.add(createAddUserRecord());
         }
 
         return userRecords;
@@ -125,17 +123,17 @@
     /**
      * Create guest user record
      */
-    private UserRecord addGuestUserRecord() {
+    private UserRecord createStartGuestUserRecord() {
         UserInfo userInfo = new UserInfo();
-        userInfo.name = mContext.getString(R.string.car_guest);
-        return new UserRecord(userInfo, true /* isStartGuestSession */,
-                false /* isAddUser */, false /* isForeground */);
+        userInfo.name = mContext.getString(R.string.start_guest_session);
+        return new UserRecord(userInfo, true /* isStartGuestSession */, false /* isAddUser */,
+                false /* isForeground */);
     }
 
     /**
      * Create add user record
      */
-    private UserRecord addUserRecord() {
+    private UserRecord createAddUserRecord() {
         UserInfo userInfo = new UserInfo();
         userInfo.name = mContext.getString(R.string.car_add_user);
         return new UserRecord(userInfo, false /* isStartGuestSession */,
@@ -210,8 +208,6 @@
                     return;
                 }
 
-
-                // If the user selects Guest, start the guest session.
                 if (userRecord.mIsStartGuestSession) {
                     notifyUserSelected(userRecord);
                     mUserManagerHelper.startNewGuestSession(mGuestName);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index 81d6191..32fd054 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -147,7 +147,7 @@
                 return;
             }
             view.setTemp(mHvacManager.getFloatProperty(id, zone));
-        } catch (CarNotConnectedException e) {
+        } catch (Exception e) {
             view.setTemp(Float.NaN);
             Log.e(TAG, "Failed to get value from hvac service", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfObserver.java
index f10d2d7..24eb5be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfObserver.java
@@ -20,7 +20,7 @@
 import android.view.ViewGroup;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
  * An observer that listens to the above shelf state and can notify listeners
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 a68d75a..1a1941e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -33,16 +33,14 @@
 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.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationListContainer;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarWindowView;
 
-import java.util.ArrayList;
-
 /**
  * A class that allows activities to be launched in a seamless way where the notification
  * transforms nicely into the starting window.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java
new file mode 100644
index 0000000..8cae806
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java
@@ -0,0 +1,69 @@
+/*
+ * 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.notification;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.statusbar.NotificationPresenter;
+
+/**
+ * This class handles listening to notification updates and passing them along to
+ * NotificationPresenter to be displayed to the user.
+ */
+public class AppOpsListener implements AppOpsManager.OnOpActiveChangedListener {
+    private static final String TAG = "NotificationListener";
+
+    // Dependencies:
+    private final ForegroundServiceController mFsc =
+            Dependency.get(ForegroundServiceController.class);
+
+    private final Context mContext;
+    protected NotificationPresenter mPresenter;
+    protected NotificationEntryManager mEntryManager;
+    protected final AppOpsManager mAppOps;
+
+    protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA,
+            AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+            AppOpsManager.OP_RECORD_AUDIO};
+
+    public AppOpsListener(Context context) {
+        mContext = context;
+        mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+    }
+
+    public void setUpWithPresenter(NotificationPresenter presenter,
+            NotificationEntryManager entryManager) {
+        mPresenter = presenter;
+        mEntryManager = entryManager;
+        mAppOps.startWatchingActive(OPS, this);
+    }
+
+    public void destroy() {
+        mAppOps.stopWatchingActive(this);
+    }
+
+    @Override
+    public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
+        mFsc.onAppOpChanged(code, uid, packageName, active);
+        mPresenter.getHandler().post(() -> {
+          mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active);
+        });
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java
deleted file mode 100644
index ec94df1..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import android.app.Notification;
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-
-/**
- * A class managing hybrid groups that include {@link HybridNotificationView} and the notification
- * group overflow.
- */
-public class HybridGroupManager {
-
-    private final Context mContext;
-    private final NotificationDozeHelper mDozer;
-    private final ViewGroup mParent;
-
-    private float mOverflowNumberSizeDark;
-    private int mOverflowNumberPaddingDark;
-    private float mOverflowNumberSize;
-    private int mOverflowNumberPadding;
-
-    private int mOverflowNumberColor;
-    private int mOverflowNumberColorDark;
-    private float mDarkAmount = 0f;
-
-    public HybridGroupManager(Context ctx, ViewGroup parent) {
-        mContext = ctx;
-        mParent = parent;
-        mDozer = new NotificationDozeHelper();
-        initDimens();
-    }
-
-    public void initDimens() {
-        Resources res = mContext.getResources();
-        mOverflowNumberSize = res.getDimensionPixelSize(
-                R.dimen.group_overflow_number_size);
-        mOverflowNumberSizeDark = res.getDimensionPixelSize(
-                R.dimen.group_overflow_number_size_dark);
-        mOverflowNumberPadding = res.getDimensionPixelSize(
-                R.dimen.group_overflow_number_padding);
-        mOverflowNumberPaddingDark = mOverflowNumberPadding + res.getDimensionPixelSize(
-                R.dimen.group_overflow_number_extra_padding_dark);
-    }
-
-    private HybridNotificationView inflateHybridViewWithStyle(int style) {
-        LayoutInflater inflater = new ContextThemeWrapper(mContext, style)
-                .getSystemService(LayoutInflater.class);
-        HybridNotificationView hybrid = (HybridNotificationView) inflater.inflate(
-                R.layout.hybrid_notification, mParent, false);
-        mParent.addView(hybrid);
-        return hybrid;
-    }
-
-    private TextView inflateOverflowNumber() {
-        LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
-        TextView numberView = (TextView) inflater.inflate(
-                R.layout.hybrid_overflow_number, mParent, false);
-        mParent.addView(numberView);
-        updateOverFlowNumberColor(numberView);
-        return numberView;
-    }
-
-    private void updateOverFlowNumberColor(TextView numberView) {
-        numberView.setTextColor(NotificationUtils.interpolateColors(
-                mOverflowNumberColor, mOverflowNumberColorDark, mDarkAmount));
-    }
-
-    public void setOverflowNumberColor(TextView numberView, int colorRegular, int colorDark) {
-        mOverflowNumberColor = colorRegular;
-        mOverflowNumberColorDark = colorDark;
-        if (numberView != null) {
-            updateOverFlowNumberColor(numberView);
-        }
-    }
-
-    public HybridNotificationView bindFromNotification(HybridNotificationView reusableView,
-            Notification notification) {
-        return bindFromNotificationWithStyle(reusableView, notification,
-                R.style.HybridNotification);
-    }
-
-    public HybridNotificationView bindAmbientFromNotification(HybridNotificationView reusableView,
-            Notification notification) {
-        return bindFromNotificationWithStyle(reusableView, notification,
-                R.style.HybridNotification_Ambient);
-    }
-
-    private HybridNotificationView bindFromNotificationWithStyle(
-            HybridNotificationView reusableView, Notification notification, int style) {
-        if (reusableView == null) {
-            reusableView = inflateHybridViewWithStyle(style);
-        }
-        CharSequence titleText = resolveTitle(notification);
-        CharSequence contentText = resolveText(notification);
-        reusableView.bind(titleText, contentText);
-        return reusableView;
-    }
-
-    private CharSequence resolveText(Notification notification) {
-        CharSequence contentText = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
-        if (contentText == null) {
-            contentText = notification.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
-        }
-        return contentText;
-    }
-
-    private CharSequence resolveTitle(Notification notification) {
-        CharSequence titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
-        if (titleText == null) {
-            titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
-        }
-        return titleText;
-    }
-
-    public TextView bindOverflowNumber(TextView reusableView, int number) {
-        if (reusableView == null) {
-            reusableView = inflateOverflowNumber();
-        }
-        String text = mContext.getResources().getString(
-                R.string.notification_group_overflow_indicator, number);
-        if (!text.equals(reusableView.getText())) {
-            reusableView.setText(text);
-        }
-        String contentDescription = String.format(mContext.getResources().getQuantityString(
-                R.plurals.notification_group_overflow_description, number), number);
-
-        reusableView.setContentDescription(contentDescription);
-        return reusableView;
-    }
-
-    public TextView bindOverflowNumberAmbient(TextView titleView, Notification notification,
-            int number) {
-        String text = mContext.getResources().getString(
-                R.string.notification_group_overflow_indicator_ambient,
-                resolveTitle(notification), number);
-        if (!text.equals(titleView.getText())) {
-            titleView.setText(text);
-        }
-        return titleView;
-    }
-
-    public void setOverflowNumberDark(TextView view, boolean dark, boolean fade, long delay) {
-        mDozer.setIntensityDark((f)->{
-            mDarkAmount = f;
-            updateOverFlowNumberColor(view);
-        }, dark, fade, delay, view);
-        view.setTextSize(TypedValue.COMPLEX_UNIT_PX,
-                dark ? mOverflowNumberSizeDark : mOverflowNumberSize);
-        int paddingEnd = dark ? mOverflowNumberPaddingDark : mOverflowNumberPadding;
-        view.setPaddingRelative(view.getPaddingStart(), view.getPaddingTop(), paddingEnd,
-                view.getPaddingBottom());
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
deleted file mode 100644
index 85f2a63..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.TextView;
-
-import com.android.keyguard.AlphaOptimizedLinearLayout;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.TransformableView;
-import com.android.systemui.statusbar.ViewTransformationHelper;
-
-/**
- * A hybrid view which may contain information about one ore more notifications.
- */
-public class HybridNotificationView extends AlphaOptimizedLinearLayout
-        implements TransformableView {
-
-    private ViewTransformationHelper mTransformationHelper;
-
-    protected TextView mTitleView;
-    protected TextView mTextView;
-
-    public HybridNotificationView(Context context) {
-        this(context, null);
-    }
-
-    public HybridNotificationView(Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public HybridNotificationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public HybridNotificationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    public TextView getTitleView() {
-        return mTitleView;
-    }
-
-    public TextView getTextView() {
-        return mTextView;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mTitleView = (TextView) findViewById(R.id.notification_title);
-        mTextView = (TextView) findViewById(R.id.notification_text);
-        mTransformationHelper = new ViewTransformationHelper();
-        mTransformationHelper.setCustomTransformation(
-                new ViewTransformationHelper.CustomTransformation() {
-                    @Override
-                    public boolean transformTo(TransformState ownState, TransformableView notification,
-                            float transformationAmount) {
-                        // We want to transform to the same y location as the title
-                        TransformState otherState = notification.getCurrentState(
-                                TRANSFORMING_VIEW_TITLE);
-                        CrossFadeHelper.fadeOut(mTextView, transformationAmount);
-                        if (otherState != null) {
-                            ownState.transformViewVerticalTo(otherState, transformationAmount);
-                            otherState.recycle();
-                        }
-                        return true;
-                    }
-
-                    @Override
-                    public boolean transformFrom(TransformState ownState,
-                            TransformableView notification, float transformationAmount) {
-                        // We want to transform from the same y location as the title
-                        TransformState otherState = notification.getCurrentState(
-                                TRANSFORMING_VIEW_TITLE);
-                        CrossFadeHelper.fadeIn(mTextView, transformationAmount);
-                        if (otherState != null) {
-                            ownState.transformViewVerticalFrom(otherState, transformationAmount);
-                            otherState.recycle();
-                        }
-                        return true;
-                    }
-                }, TRANSFORMING_VIEW_TEXT);
-        mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_TITLE, mTitleView);
-        mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_TEXT, mTextView);
-    }
-
-    public void bind(CharSequence title) {
-        bind(title, null);
-    }
-
-    public void bind(CharSequence title, CharSequence text) {
-        mTitleView.setText(title);
-        mTitleView.setVisibility(TextUtils.isEmpty(title) ? GONE : VISIBLE);
-        if (TextUtils.isEmpty(text)) {
-            mTextView.setVisibility(GONE);
-            mTextView.setText(null);
-        } else {
-            mTextView.setVisibility(VISIBLE);
-            mTextView.setText(text.toString());
-        }
-        requestLayout();
-    }
-
-    @Override
-    public TransformState getCurrentState(int fadingView) {
-        return mTransformationHelper.getCurrentState(fadingView);
-    }
-
-    @Override
-    public void transformTo(TransformableView notification, Runnable endRunnable) {
-        mTransformationHelper.transformTo(notification, endRunnable);
-    }
-
-    @Override
-    public void transformTo(TransformableView notification, float transformationAmount) {
-        mTransformationHelper.transformTo(notification, transformationAmount);
-    }
-
-    @Override
-    public void transformFrom(TransformableView notification) {
-        mTransformationHelper.transformFrom(notification);
-    }
-
-    @Override
-    public void transformFrom(TransformableView notification, float transformationAmount) {
-        mTransformationHelper.transformFrom(notification, transformationAmount);
-    }
-
-    @Override
-    public void setVisible(boolean visible) {
-        setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
-        mTransformationHelper.setVisible(visible);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
index d3a325d..4882a23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
@@ -21,13 +21,12 @@
 import android.view.View;
 import android.widget.ImageView;
 
-import com.android.internal.widget.MessagingImageMessage;
-import com.android.internal.widget.MessagingMessage;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.TransformableView;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.row.HybridNotificationView;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
 /**
  * A transform state of a image view.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java
deleted file mode 100644
index cf12e94..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import android.app.Notification;
-import android.content.Context;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.service.notification.StatusBarNotification;
-import android.view.View;
-
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-
-/**
- * Wraps a notification containing a big picture template
- */
-public class NotificationBigPictureTemplateViewWrapper extends NotificationTemplateViewWrapper {
-
-    protected NotificationBigPictureTemplateViewWrapper(Context ctx, View view,
-            ExpandableNotificationRow row) {
-        super(ctx, view, row);
-    }
-
-    @Override
-    public void onContentUpdated(ExpandableNotificationRow row) {
-        super.onContentUpdated(row);
-        updateImageTag(row.getStatusBarNotification());
-    }
-
-    private void updateImageTag(StatusBarNotification notification) {
-        final Bundle extras = notification.getNotification().extras;
-        Icon overRiddenIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG);
-        if (overRiddenIcon != null) {
-            mPicture.setTag(ImageTransformState.ICON_TAG, overRiddenIcon);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java
deleted file mode 100644
index 20a3d8f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import android.content.Context;
-import android.service.notification.StatusBarNotification;
-import android.view.View;
-
-import com.android.internal.widget.ImageFloatingTextView;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.TransformableView;
-
-/**
- * Wraps a notification containing a big text template
- */
-public class NotificationBigTextTemplateViewWrapper extends NotificationTemplateViewWrapper {
-
-    private ImageFloatingTextView mBigtext;
-
-    protected NotificationBigTextTemplateViewWrapper(Context ctx, View view,
-            ExpandableNotificationRow row) {
-        super(ctx, view, row);
-    }
-
-    private void resolveViews(StatusBarNotification notification) {
-        mBigtext = (ImageFloatingTextView) mView.findViewById(com.android.internal.R.id.big_text);
-    }
-
-    @Override
-    public void onContentUpdated(ExpandableNotificationRow row) {
-        // Reinspect the notification. Before the super call, because the super call also updates
-        // the transformation types and we need to have our values set by then.
-        resolveViews(row.getStatusBarNotification());
-        super.onContentUpdated(row);
-    }
-
-    @Override
-    protected void updateTransformedTypes() {
-        // This also clears the existing types
-        super.updateTransformedTypes();
-        if (mBigtext != null) {
-            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT,
-                    mBigtext);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCounters.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCounters.java
deleted file mode 100644
index 9a12e8b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCounters.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.systemui.statusbar.notification;
-
-/**
- * Constants for counter tags for Notification-related actions/views.
- */
-public class NotificationCounters {
-    /** Counter tag for notification dismissal. */
-    public static final String NOTIFICATION_DISMISSED = "notification_dismissed";
-
-    /** Counter tag for when the blocking helper is shown to the user. */
-    public static final String BLOCKING_HELPER_SHOWN = "blocking_helper_shown";
-    /** Counter tag for when the blocking helper is dismissed via a miscellaneous interaction. */
-    public static final String BLOCKING_HELPER_DISMISSED = "blocking_helper_dismissed";
-    /** Counter tag for when the user hits 'stop notifications' in the blocking helper. */
-    public static final String BLOCKING_HELPER_STOP_NOTIFICATIONS =
-            "blocking_helper_stop_notifications";
-    /** Counter tag for when the user hits 'keep showing' in the blocking helper. */
-    public static final String BLOCKING_HELPER_KEEP_SHOWING =
-            "blocking_helper_keep_showing";
-    /**
-     * Counter tag for when the user hits undo in context of the blocking helper - this can happen
-     * multiple times per view.
-     */
-    public static final String BLOCKING_HELPER_UNDO = "blocking_helper_undo";
-    /** Counter tag for when the user hits the notification settings icon in the blocking helper. */
-    public static final String BLOCKING_HELPER_NOTIF_SETTINGS =
-            "blocking_helper_notif_settings";
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
deleted file mode 100644
index 7a51fe1..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ /dev/null
@@ -1,67 +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
- */
-
-package com.android.systemui.statusbar.notification;
-
-import android.content.Context;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-
-/**
- * Wraps a notification containing a custom view.
- */
-public class NotificationCustomViewWrapper extends NotificationViewWrapper {
-
-    private boolean mIsLegacy;
-    private int mLegacyColor;
-
-    protected NotificationCustomViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
-        super(ctx, view, row);
-        mLegacyColor = row.getContext().getColor(R.color.notification_legacy_background_color);
-    }
-
-    @Override
-    public void setVisible(boolean visible) {
-        super.setVisible(visible);
-        mView.setAlpha(visible ? 1.0f : 0.0f);
-    }
-
-    @Override
-    protected boolean shouldClearBackgroundOnReapply() {
-        return false;
-    }
-
-    @Override
-    public int getCustomBackgroundColor() {
-        int customBackgroundColor = super.getCustomBackgroundColor();
-        if (customBackgroundColor == 0 && mIsLegacy) {
-            return mLegacyColor;
-        }
-        return customBackgroundColor;
-    }
-
-    public void setLegacy(boolean legacy) {
-        super.setLegacy(legacy);
-        mIsLegacy = legacy;
-    }
-
-    @Override
-    public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
-        return true;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
new file mode 100644
index 0000000..804e842
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -0,0 +1,867 @@
+/*
+ * 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 android.widget.RemoteViews;
+
+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.StatusBarIconView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+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 Environment mEnvironment;
+    private HeadsUpManager mHeadsUpManager;
+
+    final ZenModeController mZen = Dependency.get(ZenModeController.class);
+    final ForegroundServiceController mFsc = Dependency.get(ForegroundServiceController.class);
+
+    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 StatusBarIconView icon;
+        public StatusBarIconView expandedIcon;
+        public ExpandableNotificationRow row; // the outer expanded view
+        private boolean interruption;
+        public boolean autoRedacted; // whether the redacted notification was generated by us
+        public int targetSdk;
+        private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
+        public RemoteViews cachedContentView;
+        public RemoteViews cachedBigContentView;
+        public RemoteViews cachedHeadsUpContentView;
+        public RemoteViews cachedPublicContentView;
+        public RemoteViews cachedAmbientContentView;
+        public CharSequence remoteInputText;
+        public List<SnoozeCriterion> snoozeCriteria;
+        public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
+        @NonNull
+        public List<Notification.Action> smartActions = Collections.emptyList();
+        public CharSequence[] smartReplies = new CharSequence[0];
+
+        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;
+
+        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();
+            snoozeCriteria = ranking.getSnoozeCriteria();
+            userSentiment = ranking.getUserSentiment();
+            smartActions = 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;
+        }
+
+        /**
+         * Resets the notification entry to be re-used.
+         */
+        public void reset() {
+            if (row != null) {
+                row.reset();
+            }
+        }
+
+        public View getExpandedContentView() {
+            return row.getPrivateLayout().getExpandedChild();
+        }
+
+        public View getPublicContentView() {
+            return row.getPublicLayout().getContractedChild();
+        }
+
+        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;
+            }
+        }
+    }
+
+    private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
+    private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>();
+    private final ArrayList<Entry> mFilteredForUser = new ArrayList<>();
+
+    private NotificationGroupManager mGroupManager;
+
+    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 = mEnvironment.getCurrentMediaNotificationKey();
+
+            // 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 (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);
+            }
+        }
+    };
+
+    public NotificationData(Environment environment) {
+        mEnvironment = environment;
+        mGroupManager = environment.getGroupManager();
+    }
+
+    /**
+     * Returns the sorted list of active notifications (depending on {@link Environment}
+     *
+     * <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 (!mEnvironment.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);
+                    }
+                }
+            }
+        }
+    }
+
+    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 (!(mEnvironment.isDeviceProvisioned() ||
+                showNotificationEvenIfUnprovisioned(sbn))) {
+            return true;
+        }
+
+        if (!mEnvironment.isNotificationForCurrentProfiles(sbn)) {
+            return true;
+        }
+
+        if (mEnvironment.isSecurelyLocked(sbn.getUserId()) &&
+                (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
+                        || mEnvironment.shouldHideNotifications(sbn.getUserId())
+                        || mEnvironment.shouldHideNotifications(sbn.getKey()))) {
+            return true;
+        }
+
+        if (mEnvironment.isDozing() && shouldSuppressAmbient(entry)) {
+            return true;
+        }
+
+        if (!mEnvironment.isDozing() && shouldSuppressNotificationList(entry)) {
+            return true;
+        }
+
+        if (shouldHide(sbn.getKey())) {
+            return true;
+        }
+
+        if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
+                && mGroupManager.isChildInGroupWithSummary(sbn)) {
+            return true;
+        }
+
+        if (mFsc.isDungeonNotification(sbn) && !mFsc.isDungeonNeededForUser(sbn.getUserId())) {
+            // this is a foreground-service disclosure for a user that does not need to show one
+            return true;
+        }
+        if (mFsc.isSystemAlertNotification(sbn)) {
+            final String[] apps = sbn.getNotification().extras.getStringArray(
+                    Notification.EXTRA_FOREGROUND_APPS);
+            if (apps != null && apps.length >= 1) {
+                if (!mFsc.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 Environment {
+        public boolean isSecurelyLocked(int userId);
+        public boolean shouldHideNotifications(int userid);
+        public boolean shouldHideNotifications(String key);
+        public boolean isDeviceProvisioned();
+        public boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
+        public String getCurrentMediaNotificationKey();
+        public NotificationGroupManager getGroupManager();
+
+        /**
+         * @return true iff the device is dozing
+         */
+        boolean isDozing();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
new file mode 100644
index 0000000..dc58cab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -0,0 +1,1152 @@
+/*
+ * 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.notification;
+
+import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
+import static com.android.systemui.statusbar.NotificationRemoteInputManager
+        .FORCE_REMOTE_INPUT_HISTORY;
+
+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.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+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.R;
+import com.android.systemui.UiOffloadThread;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+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.SmartReplyController;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.RowInflaterTask;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+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.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.util.leak.LeakDetector;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * 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 {
+    private static final String TAG = "NotificationEntryMgr";
+    protected static final boolean DEBUG = false;
+    protected static final boolean ENABLE_HEADS_UP = true;
+    protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+
+    protected final NotificationMessagingUtil mMessagingUtil;
+    protected final Context mContext;
+    protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
+    protected final NotificationClicker mNotificationClicker = new NotificationClicker();
+    protected final ArraySet<NotificationData.Entry> mHeadsUpEntriesToRemoveOnSwitch =
+            new ArraySet<>();
+
+    // Dependencies:
+    protected final NotificationLockscreenUserManager mLockscreenUserManager =
+            Dependency.get(NotificationLockscreenUserManager.class);
+    protected final NotificationGroupManager mGroupManager =
+            Dependency.get(NotificationGroupManager.class);
+    protected final NotificationGutsManager mGutsManager =
+            Dependency.get(NotificationGutsManager.class);
+    protected final NotificationRemoteInputManager mRemoteInputManager =
+            Dependency.get(NotificationRemoteInputManager.class);
+    protected final NotificationMediaManager mMediaManager =
+            Dependency.get(NotificationMediaManager.class);
+    protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+    protected final DeviceProvisionedController mDeviceProvisionedController =
+            Dependency.get(DeviceProvisionedController.class);
+    protected final VisualStabilityManager mVisualStabilityManager =
+            Dependency.get(VisualStabilityManager.class);
+    protected final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+    protected final ForegroundServiceController mForegroundServiceController =
+            Dependency.get(ForegroundServiceController.class);
+    protected final NotificationListener mNotificationListener =
+            Dependency.get(NotificationListener.class);
+    private final SmartReplyController mSmartReplyController =
+            Dependency.get(SmartReplyController.class);
+
+    protected IStatusBarService mBarService;
+    protected NotificationPresenter mPresenter;
+    protected Callback mCallback;
+    protected PowerManager mPowerManager;
+    protected SystemServicesProxy mSystemServicesProxy;
+    protected NotificationListenerService.RankingMap mLatestRankingMap;
+    protected HeadsUpManager mHeadsUpManager;
+    protected NotificationData mNotificationData;
+    protected ContentObserver mHeadsUpObserver;
+    protected boolean mUseHeadsUp = false;
+    protected boolean mDisableNotificationAlerts;
+    protected NotificationListContainer mListContainer;
+    private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
+    /**
+     * Notifications with keys in this set are not actually around anymore. We kept them around
+     * when they were canceled in response to a remote input interaction. This allows us to show
+     * what you replied and allows you to continue typing into it.
+     */
+    private final ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
+
+
+    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;
+            }
+
+            mPresenter.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 (row.getProvider() != null && row.getProvider().isMenuVisible()) {
+                row.animateTranslateNotification(0);
+                return;
+            }
+
+            // Mark notification for one frame.
+            row.setJustClicked(true);
+            DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
+
+            mCallback.onNotificationClicked(sbn, row);
+        }
+
+        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 DeviceProvisionedController.DeviceProvisionedListener
+            mDeviceProvisionedListener =
+            new DeviceProvisionedController.DeviceProvisionedListener() {
+                @Override
+                public void onDeviceProvisionedChanged() {
+                    updateNotifications();
+                }
+            };
+
+    public NotificationListenerService.RankingMap getLatestRankingMap() {
+        return mLatestRankingMap;
+    }
+
+    public void setLatestRankingMap(NotificationListenerService.RankingMap latestRankingMap) {
+        mLatestRankingMap = latestRankingMap;
+    }
+
+    public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
+        mDisableNotificationAlerts = disableNotificationAlerts;
+        mHeadsUpObserver.onChange(true);
+    }
+
+    public void destroy() {
+        mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
+    }
+
+    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+        if (!isHeadsUp && mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
+            removeNotification(entry.key, getLatestRankingMap());
+            mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
+            if (mHeadsUpEntriesToRemoveOnSwitch.isEmpty()) {
+                setLatestRankingMap(null);
+            }
+        } else {
+            updateNotificationRanking(null);
+        }
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("NotificationEntryManager state:");
+        pw.print("  mPendingNotifications=");
+        if (mPendingNotifications.size() == 0) {
+            pw.println("null");
+        } else {
+            for (NotificationData.Entry entry : mPendingNotifications.values()) {
+                pw.println(entry.notification);
+            }
+        }
+        pw.print("  mUseHeadsUp=");
+        pw.println(mUseHeadsUp);
+        pw.print("  mKeysKeptForRemoteInput: ");
+        pw.println(mKeysKeptForRemoteInput);
+    }
+
+    public NotificationEntryManager(Context context) {
+        mContext = context;
+        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+        mMessagingUtil = new NotificationMessagingUtil(context);
+        mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
+        mGroupManager.setPendingEntries(mPendingNotifications);
+    }
+
+    public void setUpWithPresenter(NotificationPresenter presenter,
+            NotificationListContainer listContainer, Callback callback,
+            HeadsUpManager headsUpManager) {
+        mPresenter = presenter;
+        mCallback = callback;
+        mNotificationData = new NotificationData(presenter);
+        mHeadsUpManager = headsUpManager;
+        mNotificationData.setHeadsUpManager(mHeadsUpManager);
+        mListContainer = listContainer;
+
+        mHeadsUpObserver = new ContentObserver(mPresenter.getHandler()) {
+            @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);
+        }
+
+        mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
+
+        mHeadsUpObserver.onChange(true); // set up
+        mOnAppOpsClickListener = mGutsManager::openGuts;
+    }
+
+    public NotificationData getNotificationData() {
+        return mNotificationData;
+    }
+
+    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.row != null) {
+            entry.reset();
+            updateNotification(entry, pmUser, sbn, entry.row);
+        } 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);
+        mRemoteInputManager.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());
+
+        if (FORCE_REMOTE_INPUT_HISTORY
+                && mKeysKeptForRemoteInput.contains(n.getKey())) {
+            mKeysKeptForRemoteInput.remove(n.getKey());
+        }
+
+        mRemoteInputManager.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 (isHeadsUp(n.getKey())) {
+                dismissalSurface = NotificationStats.DISMISSAL_PEEK;
+            } else if (mListContainer.hasPulsingNotifications()) {
+                dismissalSurface = NotificationStats.DISMISSAL_AOD;
+            }
+            mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(), dismissalSurface, nv);
+            removeNotification(n.getKey(), null);
+
+        } catch (RemoteException ex) {
+            // system process is dead if we're here.
+        }
+
+        mCallback.onPerformRemoveNotification(n);
+    }
+
+    /**
+     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+     * about the failure.
+     *
+     * WARNING: this will call back into us.  Don't hold any locks.
+     */
+    void handleNotificationError(StatusBarNotification n, String message) {
+        removeNotification(n.getKey(), null);
+        try {
+            mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
+                    n.getInitialPid(), message, n.getUserId());
+        } catch (RemoteException ex) {
+            // The end is nigh.
+        }
+    }
+
+    private void abortExistingInflation(String key) {
+        if (mPendingNotifications.containsKey(key)) {
+            NotificationData.Entry entry = mPendingNotifications.get(key);
+            entry.abortTask();
+            mPendingNotifications.remove(key);
+        }
+        NotificationData.Entry addedEntry = mNotificationData.get(key);
+        if (addedEntry != null) {
+            addedEntry.abortTask();
+        }
+    }
+
+    @Override
+    public void handleInflationException(StatusBarNotification notification, Exception e) {
+        handleNotificationError(notification, e.getMessage());
+    }
+
+    private void addEntry(NotificationData.Entry shadeEntry) {
+        boolean isHeadsUped = shouldPeek(shadeEntry);
+        if (isHeadsUped) {
+            mHeadsUpManager.showNotification(shadeEntry);
+            // Mark as seen immediately
+            setNotificationShown(shadeEntry.notification);
+        }
+        addNotificationViews(shadeEntry);
+        mCallback.onNotificationAdded(shadeEntry);
+    }
+
+    @Override
+    public void onAsyncInflationFinished(NotificationData.Entry entry) {
+        mPendingNotifications.remove(entry.key);
+        // If there was an async task started after the removal, we don't want to add it back to
+        // the list, otherwise we might get leaks.
+        boolean isNew = mNotificationData.get(entry.key) == null;
+        if (isNew && !entry.row.isRemoved()) {
+            addEntry(entry);
+        } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
+            mVisualStabilityManager.onLowPriorityUpdated(entry);
+            mPresenter.updateNotificationViews();
+        }
+        entry.row.setLowPriorityStateUpdated(false);
+    }
+
+    @Override
+    public void removeNotification(String key, NotificationListenerService.RankingMap ranking) {
+        boolean deferRemoval = false;
+        abortExistingInflation(key);
+        if (mHeadsUpManager.isHeadsUp(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();
+            deferRemoval = !mHeadsUpManager.removeNotification(key,  ignoreEarliestRemovalTime);
+        }
+        mMediaManager.onNotificationRemoved(key);
+
+        NotificationData.Entry entry = mNotificationData.get(key);
+        if (FORCE_REMOTE_INPUT_HISTORY
+                && shouldKeepForRemoteInput(entry)
+                && entry.row != null && !entry.row.isDismissed()) {
+            CharSequence remoteInputText = entry.remoteInputText;
+            if (TextUtils.isEmpty(remoteInputText)) {
+                remoteInputText = entry.remoteInputTextWhenReset;
+            }
+            StatusBarNotification newSbn = rebuildNotificationWithRemoteInput(entry,
+                    remoteInputText, false /* showSpinner */);
+            boolean updated = false;
+            entry.onRemoteInputInserted();
+            try {
+                updateNotificationInternal(newSbn, null);
+                updated = true;
+            } catch (InflationException e) {
+                deferRemoval = false;
+            }
+            if (updated) {
+                Log.w(TAG, "Keeping notification around after sending remote input "+ entry.key);
+                addKeyKeptForRemoteInput(entry.key);
+                return;
+            }
+        }
+
+        if (FORCE_REMOTE_INPUT_HISTORY
+                && shouldKeepForSmartReply(entry)
+                && entry.row != null && !entry.row.isDismissed()) {
+            // Turn off the spinner and hide buttons when an app cancels the notification.
+            StatusBarNotification newSbn = rebuildNotificationForCanceledSmartReplies(entry);
+            boolean updated = false;
+            try {
+                updateNotificationInternal(newSbn, null);
+                updated = true;
+            } catch (InflationException e) {
+                // Ignore just don't keep the notification around.
+            }
+            // Treat the reply as longer sending.
+            mSmartReplyController.stopSending(entry);
+            if (updated) {
+                Log.w(TAG, "Keeping notification around after sending smart reply " + entry.key);
+                addKeyKeptForRemoteInput(entry.key);
+                return;
+            }
+        }
+
+        // Actually removing notification so smart reply controller can forget about it.
+        mSmartReplyController.stopSending(entry);
+
+        if (deferRemoval) {
+            mLatestRankingMap = ranking;
+            mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
+            return;
+        }
+
+        if (mRemoteInputManager.onRemoveNotification(entry)) {
+            mLatestRankingMap = ranking;
+            return;
+        }
+
+        if (entry != null && mGutsManager.getExposedGuts() != null
+                && mGutsManager.getExposedGuts() == entry.row.getGuts()
+                && entry.row.getGuts() != null && !entry.row.getGuts().isLeavebehind()) {
+            Log.w(TAG, "Keeping notification because it's showing guts. " + key);
+            mLatestRankingMap = ranking;
+            mGutsManager.setKeyToRemoveOnGutsClosed(key);
+            return;
+        }
+
+        if (entry != null) {
+            mForegroundServiceController.removeNotification(entry.notification);
+        }
+
+        if (entry != null && entry.row != null) {
+            entry.row.setRemoved();
+            mListContainer.cleanUpViewState(entry.row);
+        }
+        // Let's remove the children if this was a summary
+        handleGroupSummaryRemoved(key);
+        StatusBarNotification old = removeNotificationViews(key, ranking);
+
+        mCallback.onNotificationRemoved(key, old);
+    }
+
+    public StatusBarNotification rebuildNotificationWithRemoteInput(NotificationData.Entry entry,
+            CharSequence remoteInputText, boolean showSpinner) {
+        StatusBarNotification sbn = entry.notification;
+
+        Notification.Builder b = Notification.Builder
+                .recoverBuilder(mContext, sbn.getNotification().clone());
+        if (remoteInputText != null) {
+            CharSequence[] oldHistory = sbn.getNotification().extras
+                    .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+            CharSequence[] newHistory;
+            if (oldHistory == null) {
+                newHistory = new CharSequence[1];
+            } else {
+                newHistory = new CharSequence[oldHistory.length + 1];
+                System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length);
+            }
+            newHistory[0] = String.valueOf(remoteInputText);
+            b.setRemoteInputHistory(newHistory);
+        }
+        b.setShowRemoteInputSpinner(showSpinner);
+        b.setHideSmartReplies(true);
+
+        Notification newNotification = b.build();
+
+        // Undo any compatibility view inflation
+        newNotification.contentView = sbn.getNotification().contentView;
+        newNotification.bigContentView = sbn.getNotification().bigContentView;
+        newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
+
+        StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(),
+                sbn.getOpPkg(),
+                sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
+                newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+        return newSbn;
+    }
+
+    @VisibleForTesting
+    StatusBarNotification rebuildNotificationForCanceledSmartReplies(
+            NotificationData.Entry entry) {
+        return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */,
+                false /* showSpinner */);
+    }
+
+    private boolean shouldKeepForSmartReply(NotificationData.Entry entry) {
+        return entry != null && mSmartReplyController.isSendingSmartReply(entry.key);
+    }
+
+    private boolean shouldKeepForRemoteInput(NotificationData.Entry entry) {
+        if (entry == null) {
+            return false;
+        }
+        if (mRemoteInputManager.getController().isSpinning(entry.key)) {
+            return true;
+        }
+        if (entry.hasJustSentRemoteInput()) {
+            return true;
+        }
+        return false;
+    }
+
+    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;
+    }
+
+    /**
+     * Ensures that the group children are cancelled immediately when the group summary is cancelled
+     * instead of waiting for the notification manager to send all cancels. Otherwise this could
+     * lead to flickers.
+     *
+     * This also ensures that the animation looks nice and only consists of a single disappear
+     * animation instead of multiple.
+     *  @param key the key of the notification was removed
+     *
+     */
+    private void handleGroupSummaryRemoved(String key) {
+        NotificationData.Entry entry = mNotificationData.get(key);
+        if (entry != null && entry.row != null
+                && entry.row.isSummaryWithChildren()) {
+            if (entry.notification.getOverrideGroupKey() != null && !entry.row.isDismissed()) {
+                // 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<ExpandableNotificationRow> notificationChildren =
+                    entry.row.getNotificationChildren();
+            for (int i = 0; i < notificationChildren.size(); i++) {
+                ExpandableNotificationRow row = notificationChildren.get(i);
+                if ((row.getStatusBarNotification().getNotification().flags
+                        & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+                    // the child is a foreground service notification which we can't remove!
+                    continue;
+                }
+                row.setKeepInParent(true);
+                // we need to set this state earlier as otherwise we might generate some weird
+                // animations
+                row.setRemoved();
+            }
+        }
+    }
+
+    public void updateNotificationsOnDensityOrFontScaleChanged() {
+        ArrayList<NotificationData.Entry> userNotifications =
+                mNotificationData.getNotificationsForCurrentUser();
+        for (int i = 0; i < userNotifications.size(); i++) {
+            NotificationData.Entry entry = userNotifications.get(i);
+            boolean exposedGuts = mGutsManager.getExposedGuts() != null
+                    && entry.row.getGuts() == mGutsManager.getExposedGuts();
+            entry.row.onDensityOrFontScaleChanged();
+            if (exposedGuts) {
+                mGutsManager.onDensityOrFontScaleChanged(entry.row);
+            }
+        }
+    }
+
+    protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
+            StatusBarNotification sbn, ExpandableNotificationRow row) {
+        row.setNeedsRedaction(mLockscreenUserManager.needsRedaction(entry));
+        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.row = row;
+        entry.row.setOnActivatedListener(mPresenter);
+
+        boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
+                mNotificationData.getImportance(sbn.getKey()));
+        boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
+                && !mPresenter.isPresenterFullyCollapsed();
+        row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
+        row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
+        row.setSmartActions(entry.smartActions);
+        row.updateNotification(entry);
+    }
+
+    protected void addNotificationViews(NotificationData.Entry entry) {
+        if (entry == null) {
+            return;
+        }
+        // Add the expanded view and icon.
+        mNotificationData.add(entry);
+        tagForeground(entry.notification);
+        updateNotifications();
+    }
+
+    protected 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);
+        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();
+        if (DEBUG) Log.d(TAG, "addNotification key=" + key);
+
+        mNotificationData.updateRanking(rankingMap);
+        NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
+        rankingMap.getRanking(key, ranking);
+        NotificationData.Entry shadeEntry = createNotificationViews(notification, ranking);
+        boolean isHeadsUped = shouldPeek(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)
+                SystemServicesProxy.getInstance(mContext).awakenDreamsAsync();
+
+                // 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) {
+                }
+            }
+        }
+        abortExistingInflation(key);
+
+        mForegroundServiceController.addNotification(notification,
+                mNotificationData.getImportance(key));
+
+        mPendingNotifications.put(key, shadeEntry);
+        mGroupManager.onPendingEntryAdded(shadeEntry);
+    }
+
+    @VisibleForTesting
+    protected void tagForeground(StatusBarNotification notification) {
+        ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
+                notification.getUserId(), notification.getPackageName());
+        if (activeOps != null) {
+            int N = activeOps.size();
+            for (int i = 0; i < N; i++) {
+                updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(),
+                        notification.getPackageName(), true);
+            }
+        }
+    }
+
+    @Override
+    public void addNotification(StatusBarNotification notification,
+            NotificationListenerService.RankingMap ranking) {
+        try {
+            addNotificationInternal(notification, ranking);
+        } catch (InflationException e) {
+            handleInflationException(notification, e);
+        }
+    }
+
+    public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) {
+        String foregroundKey = mForegroundServiceController.getStandardLayoutKey(
+                UserHandle.getUserId(uid), pkg);
+        if (foregroundKey != null) {
+            mNotificationData.updateAppOp(appOp, uid, pkg, foregroundKey, showIcon);
+            updateNotifications();
+        }
+    }
+
+    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);
+        if (entry == null) {
+            return;
+        }
+        mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
+        mRemoteInputManager.onUpdateNotification(entry);
+        mSmartReplyController.stopSending(entry);
+
+        if (key.equals(mGutsManager.getKeyToRemoveOnGutsClosed())) {
+            mGutsManager.setKeyToRemoveOnGutsClosed(null);
+            Log.w(TAG, "Notification that was kept for guts was updated. " + key);
+        }
+
+        Notification n = notification.getNotification();
+        mNotificationData.updateRanking(ranking);
+
+        final StatusBarNotification oldNotification = entry.notification;
+        entry.notification = notification;
+        mGroupManager.onEntryUpdated(entry, oldNotification);
+
+        entry.updateIcons(mContext, notification);
+        inflateViews(entry, mListContainer.getViewParentForNotification(entry));
+
+        mForegroundServiceController.updateNotification(notification,
+                mNotificationData.getImportance(key));
+
+        boolean shouldPeek = shouldPeek(entry, notification);
+        boolean alertAgain = alertAgain(entry, n);
+
+        updateHeadsUp(key, entry, shouldPeek, alertAgain);
+        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.row);
+        }
+
+        if (DEBUG) {
+            // Is this for you?
+            boolean isForCurrentUser = mPresenter.isNotificationForCurrentProfiles(notification);
+            Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
+        }
+
+        mCallback.onNotificationUpdated(notification);
+    }
+
+    @Override
+    public void updateNotification(StatusBarNotification notification,
+            NotificationListenerService.RankingMap ranking) {
+        try {
+            updateNotificationInternal(notification, ranking);
+        } catch (InflationException e) {
+            handleInflationException(notification, e);
+        }
+    }
+
+    public void updateNotifications() {
+        mNotificationData.filterAndSort();
+
+        mPresenter.updateNotificationViews();
+    }
+
+    public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
+        List<NotificationData.Entry> 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<>();
+        for (NotificationData.Entry entry : entries) {
+            NotificationUiAdjustment adjustment =
+                    NotificationUiAdjustment.extractFromNotificationEntry(entry);
+            oldAdjustments.put(entry.key, adjustment);
+        }
+
+        // Populate notification entries from the new rankings.
+        mNotificationData.updateRanking(rankingMap);
+        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.row != null) {
+                    entry.reset();
+                    PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+                            entry.notification.getUser().getIdentifier());
+                    updateNotification(entry, pmUser, entry.notification, entry.row);
+                } else {
+                    // Once the RowInflaterTask is done, it will pick up the updated entry, so
+                    // no-op here.
+                }
+            }
+        }
+
+        updateNotifications();
+    }
+
+    private void updateRankingOfPendingNotifications(
+            @Nullable NotificationListenerService.RankingMap rankingMap) {
+        if (rankingMap == null) {
+            return;
+        }
+        NotificationListenerService.Ranking tmpRanking = new NotificationListenerService.Ranking();
+        for (NotificationData.Entry pendingNotification : mPendingNotifications.values()) {
+            rankingMap.getRanking(pendingNotification.key, tmpRanking);
+            pendingNotification.populateFromRanking(tmpRanking);
+        }
+    }
+
+    protected boolean shouldPeek(NotificationData.Entry entry) {
+        return shouldPeek(entry, entry.notification);
+    }
+
+    public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) {
+        if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+            if (DEBUG) Log.d(TAG, "No peeking: no huns or vr mode");
+            return false;
+        }
+
+        if (mNotificationData.shouldFilterOut(entry)) {
+            if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
+            return false;
+        }
+
+        boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming();
+
+        if (!inUse && !mPresenter.isDozing()) {
+            if (DEBUG) {
+                Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(entry)) {
+            if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
+            return false;
+        }
+
+        // Peeking triggers an ambient display pulse, so disable peek is ambient is active
+        if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(entry)) {
+            if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
+            return false;
+        }
+
+        if (entry.hasJustLaunchedFullScreenIntent()) {
+            if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
+            return false;
+        }
+
+        if (isSnoozedPackage(sbn)) {
+            if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
+            return false;
+        }
+
+        // Allow peeking for DEFAULT notifications only if we're on Ambient Display.
+        int importanceLevel = mPresenter.isDozing() ? NotificationManager.IMPORTANCE_DEFAULT
+                : NotificationManager.IMPORTANCE_HIGH;
+        if (mNotificationData.getImportance(sbn.getKey()) < importanceLevel) {
+            if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
+            return false;
+        }
+
+        // Don't peek notifications that are suppressed due to group alert behavior
+        if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
+            if (DEBUG) Log.d(TAG, "No peeking: suppressed due to group alert behavior");
+            return false;
+        }
+
+        if (!mCallback.shouldPeek(entry, sbn)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    protected void setNotificationShown(StatusBarNotification n) {
+        setNotificationsShown(new String[]{n.getKey()});
+    }
+
+    protected void setNotificationsShown(String[] keys) {
+        try {
+            mNotificationListener.setNotificationsShown(keys);
+        } catch (RuntimeException e) {
+            Log.d(TAG, "failed setNotificationsShown: ", e);
+        }
+    }
+
+    protected boolean isSnoozedPackage(StatusBarNotification sbn) {
+        return mHeadsUpManager.isSnoozed(sbn.getPackageName());
+    }
+
+    protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldPeek,
+            boolean alertAgain) {
+        final boolean wasHeadsUp = isHeadsUp(key);
+        if (wasHeadsUp) {
+            if (!shouldPeek) {
+                // We don't want this to be interrupting anymore, lets remove it
+                mHeadsUpManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
+            } else {
+                mHeadsUpManager.updateNotification(entry, alertAgain);
+            }
+        } else if (shouldPeek && alertAgain) {
+            // This notification was updated to be a heads-up, show it!
+            mHeadsUpManager.showNotification(entry);
+        }
+    }
+
+    protected boolean isHeadsUp(String key) {
+        return mHeadsUpManager.isHeadsUp(key);
+    }
+
+    public boolean isNotificationKeptForRemoteInput(String key) {
+        return mKeysKeptForRemoteInput.contains(key);
+    }
+
+    public void removeKeyKeptForRemoteInput(String key) {
+        mKeysKeptForRemoteInput.remove(key);
+    }
+
+    public void addKeyKeptForRemoteInput(String key) {
+        if (FORCE_REMOTE_INPUT_HISTORY) {
+            mKeysKeptForRemoteInput.add(key);
+        }
+    }
+
+    /**
+     * 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 notification is clicked.
+         *
+         * @param sbn notification that was clicked
+         * @param row row for that notification
+         */
+        void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row);
+
+        /**
+         * 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 should peek this notification.
+         *
+         * @param entry entry of the notification that might be peeked
+         * @param sbn notification that might be peeked
+         * @return true if the notification should be peeked
+         */
+        boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
deleted file mode 100644
index 2fc2cdb..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
-
-import android.app.Notification;
-import android.content.Context;
-import android.util.ArraySet;
-import android.view.NotificationHeaderView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.widget.NotificationExpandButton;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.TransformableView;
-import com.android.systemui.statusbar.ViewTransformationHelper;
-
-import java.util.Stack;
-
-/**
- * Wraps a notification header view.
- */
-public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
-
-    private static final Interpolator LOW_PRIORITY_HEADER_CLOSE
-            = new PathInterpolator(0.4f, 0f, 0.7f, 1f);
-
-    protected final ViewTransformationHelper mTransformationHelper;
-    private final int mTranslationForHeader;
-
-    protected int mColor;
-    private ImageView mIcon;
-
-    private NotificationExpandButton mExpandButton;
-    private NotificationHeaderView mNotificationHeader;
-    private TextView mHeaderText;
-    private ImageView mWorkProfileImage;
-    private boolean mIsLowPriority;
-    private boolean mTransformLowPriorityTitle;
-    private boolean mShowExpandButtonAtEnd;
-    protected float mHeaderTranslation;
-
-    protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
-        super(ctx, view, row);
-        mShowExpandButtonAtEnd = ctx.getResources().getBoolean(
-                R.bool.config_showNotificationExpandButtonAtEnd);
-        mTransformationHelper = new ViewTransformationHelper();
-
-        // we want to avoid that the header clashes with the other text when transforming
-        // low-priority
-        mTransformationHelper.setCustomTransformation(
-                new CustomInterpolatorTransformation(TRANSFORMING_VIEW_TITLE) {
-
-                    @Override
-                    public Interpolator getCustomInterpolator(int interpolationType,
-                            boolean isFrom) {
-                        boolean isLowPriority = mView instanceof NotificationHeaderView;
-                        if (interpolationType == TRANSFORM_Y) {
-                            if (isLowPriority && !isFrom
-                                    || !isLowPriority && isFrom) {
-                                return Interpolators.LINEAR_OUT_SLOW_IN;
-                            } else {
-                                return LOW_PRIORITY_HEADER_CLOSE;
-                            }
-                        }
-                        return null;
-                    }
-
-                    @Override
-                    protected boolean hasCustomTransformation() {
-                        return mIsLowPriority && mTransformLowPriorityTitle;
-                    }
-                }, TRANSFORMING_VIEW_TITLE);
-        resolveHeaderViews();
-        addAppOpsOnClickListener(row);
-        mTranslationForHeader = ctx.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.notification_content_margin)
-                - ctx.getResources().getDimensionPixelSize(
-                        com.android.internal.R.dimen.notification_content_margin_top);
-    }
-
-    protected void resolveHeaderViews() {
-        mIcon = mView.findViewById(com.android.internal.R.id.icon);
-        mHeaderText = mView.findViewById(com.android.internal.R.id.header_text);
-        mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
-        mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
-        mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
-        mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
-        mColor = mNotificationHeader.getOriginalIconColor();
-    }
-
-    private void addAppOpsOnClickListener(ExpandableNotificationRow row) {
-        mNotificationHeader.setAppOpsOnClickListener(row.getAppOpsOnClickListener());
-    }
-
-    @Override
-    public void onContentUpdated(ExpandableNotificationRow row) {
-        super.onContentUpdated(row);
-        mIsLowPriority = row.isLowPriority();
-        mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren();
-        ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews();
-
-        // Reinspect the notification.
-        resolveHeaderViews();
-        updateTransformedTypes();
-        addRemainingTransformTypes();
-        updateCropToPaddingForImageViews();
-        Notification notification = row.getStatusBarNotification().getNotification();
-        mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
-        // The work profile image is always the same lets just set the icon tag for it not to
-        // animate
-        mWorkProfileImage.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
-
-        // We need to reset all views that are no longer transforming in case a view was previously
-        // transformed, but now we decided to transform its container instead.
-        ArraySet<View> currentViews = mTransformationHelper.getAllTransformingViews();
-        for (int i = 0; i < previousViews.size(); i++) {
-            View view = previousViews.valueAt(i);
-            if (!currentViews.contains(view)) {
-                mTransformationHelper.resetTransformedView(view);
-            }
-        }
-    }
-
-    /**
-     * Adds the remaining TransformTypes to the TransformHelper. This is done to make sure that each
-     * child is faded automatically and doesn't have to be manually added.
-     * The keys used for the views are the ids.
-     */
-    private void addRemainingTransformTypes() {
-        mTransformationHelper.addRemainingTransformTypes(mView);
-    }
-
-    /**
-     * Since we are deactivating the clipping when transforming the ImageViews don't get clipped
-     * anymore during these transitions. We can avoid that by using
-     * {@link ImageView#setCropToPadding(boolean)} on all ImageViews.
-     */
-    private void updateCropToPaddingForImageViews() {
-        Stack<View> stack = new Stack<>();
-        stack.push(mView);
-        while (!stack.isEmpty()) {
-            View child = stack.pop();
-            if (child instanceof ImageView) {
-                ((ImageView) child).setCropToPadding(true);
-            } else if (child instanceof ViewGroup){
-                ViewGroup group = (ViewGroup) child;
-                for (int i = 0; i < group.getChildCount(); i++) {
-                    stack.push(group.getChildAt(i));
-                }
-            }
-        }
-    }
-
-    protected void updateTransformedTypes() {
-        mTransformationHelper.reset();
-        mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon);
-        if (mIsLowPriority) {
-            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
-                    mHeaderText);
-        }
-    }
-
-    @Override
-    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
-        mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
-        mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
-    }
-
-    @Override
-    public void setHeaderVisibleAmount(float headerVisibleAmount) {
-        super.setHeaderVisibleAmount(headerVisibleAmount);
-        mNotificationHeader.setAlpha(headerVisibleAmount);
-        mHeaderTranslation = (1.0f - headerVisibleAmount) * mTranslationForHeader;
-        mView.setTranslationY(mHeaderTranslation);
-    }
-
-    @Override
-    public int getHeaderTranslation() {
-        return (int) mHeaderTranslation;
-    }
-
-    @Override
-    public NotificationHeaderView getNotificationHeader() {
-        return mNotificationHeader;
-    }
-
-    @Override
-    public TransformState getCurrentState(int fadingView) {
-        return mTransformationHelper.getCurrentState(fadingView);
-    }
-
-    @Override
-    public void transformTo(TransformableView notification, Runnable endRunnable) {
-        mTransformationHelper.transformTo(notification, endRunnable);
-    }
-
-    @Override
-    public void transformTo(TransformableView notification, float transformationAmount) {
-        mTransformationHelper.transformTo(notification, transformationAmount);
-    }
-
-    @Override
-    public void transformFrom(TransformableView notification) {
-        mTransformationHelper.transformFrom(notification);
-    }
-
-    @Override
-    public void transformFrom(TransformableView notification, float transformationAmount) {
-        mTransformationHelper.transformFrom(notification, transformationAmount);
-    }
-
-    @Override
-    public void setIsChildInGroup(boolean isChildInGroup) {
-        super.setIsChildInGroup(isChildInGroup);
-        mTransformLowPriorityTitle = !isChildInGroup;
-    }
-
-    @Override
-    public void setVisible(boolean visible) {
-        super.setVisible(visible);
-        mTransformationHelper.setVisible(visible);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
deleted file mode 100644
index 1303057..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
+++ /dev/null
@@ -1,754 +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.notification;
-
-import android.annotation.Nullable;
-import android.app.Notification;
-import android.content.Context;
-import android.os.AsyncTask;
-import android.os.CancellationSignal;
-import android.service.notification.StatusBarNotification;
-import android.util.Log;
-import android.view.View;
-import android.widget.RemoteViews;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationContentView;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.util.Assert;
-
-import java.util.HashMap;
-import java.util.concurrent.Executor;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * A utility that inflates the right kind of contentView based on the state
- */
-public class NotificationInflater {
-
-    public static final String TAG = "NotificationInflater";
-    @VisibleForTesting
-    static final int FLAG_REINFLATE_ALL = ~0;
-    private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0;
-    @VisibleForTesting
-    static final int FLAG_REINFLATE_EXPANDED_VIEW = 1<<1;
-    private static final int FLAG_REINFLATE_HEADS_UP_VIEW = 1<<2;
-    private static final int FLAG_REINFLATE_PUBLIC_VIEW = 1<<3;
-    private static final int FLAG_REINFLATE_AMBIENT_VIEW = 1<<4;
-    private static final InflationExecutor EXECUTOR = new InflationExecutor();
-
-    private final ExpandableNotificationRow mRow;
-    private boolean mIsLowPriority;
-    private boolean mUsesIncreasedHeight;
-    private boolean mUsesIncreasedHeadsUpHeight;
-    private RemoteViews.OnClickHandler mRemoteViewClickHandler;
-    private boolean mIsChildInGroup;
-    private InflationCallback mCallback;
-    private boolean mRedactAmbient;
-
-    public NotificationInflater(ExpandableNotificationRow row) {
-        mRow = row;
-    }
-
-    public void setIsLowPriority(boolean isLowPriority) {
-        mIsLowPriority = isLowPriority;
-    }
-
-    /**
-     * Set whether the notification is a child in a group
-     *
-     * @return whether the view was re-inflated
-     */
-    public void setIsChildInGroup(boolean childInGroup) {
-        if (childInGroup != mIsChildInGroup) {
-            mIsChildInGroup = childInGroup;
-            if (mIsLowPriority) {
-                int flags = FLAG_REINFLATE_CONTENT_VIEW | FLAG_REINFLATE_EXPANDED_VIEW;
-                inflateNotificationViews(flags);
-            }
-        } ;
-    }
-
-    public void setUsesIncreasedHeight(boolean usesIncreasedHeight) {
-        mUsesIncreasedHeight = usesIncreasedHeight;
-    }
-
-    public void setUsesIncreasedHeadsUpHeight(boolean usesIncreasedHeight) {
-        mUsesIncreasedHeadsUpHeight = usesIncreasedHeight;
-    }
-
-    public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
-        mRemoteViewClickHandler = remoteViewClickHandler;
-    }
-
-    public void setRedactAmbient(boolean redactAmbient) {
-        if (mRedactAmbient != redactAmbient) {
-            mRedactAmbient = redactAmbient;
-            if (mRow.getEntry() == null) {
-                return;
-            }
-            inflateNotificationViews(FLAG_REINFLATE_AMBIENT_VIEW);
-        }
-    }
-
-    /**
-     * Inflate all views of this notification on a background thread. This is asynchronous and will
-     * notify the callback once it's finished.
-     */
-    public void inflateNotificationViews() {
-        inflateNotificationViews(FLAG_REINFLATE_ALL);
-    }
-
-    /**
-     * Reinflate all views for the specified flags on a background thread. This is asynchronous and
-     * will notify the callback once it's finished.
-     *
-     * @param reInflateFlags flags which views should be reinflated. Use {@link #FLAG_REINFLATE_ALL}
-     *                       to reinflate all of views.
-     */
-    @VisibleForTesting
-    void inflateNotificationViews(int reInflateFlags) {
-        if (mRow.isRemoved()) {
-            // We don't want to reinflate anything for removed notifications. Otherwise views might
-            // be readded to the stack, leading to leaks. This may happen with low-priority groups
-            // where the removal of already removed children can lead to a reinflation.
-            return;
-        }
-        StatusBarNotification sbn = mRow.getEntry().notification;
-        AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mRow,
-                mIsLowPriority,
-                mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
-                mCallback, mRemoteViewClickHandler);
-        if (mCallback != null && mCallback.doInflateSynchronous()) {
-            task.onPostExecute(task.doInBackground());
-        } else {
-            task.execute();
-        }
-    }
-
-    @VisibleForTesting
-    InflationProgress inflateNotificationViews(int reInflateFlags,
-            Notification.Builder builder, Context packageContext) {
-        InflationProgress result = createRemoteViews(reInflateFlags, builder, mIsLowPriority,
-                mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
-                mRedactAmbient, packageContext);
-        apply(result, reInflateFlags, mRow, mRedactAmbient, mRemoteViewClickHandler, null);
-        return result;
-    }
-
-    private static InflationProgress createRemoteViews(int reInflateFlags,
-            Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
-            boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
-            Context packageContext) {
-        InflationProgress result = new InflationProgress();
-        isLowPriority = isLowPriority && !isChildInGroup;
-        if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
-            result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
-        }
-
-        if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
-            result.newExpandedView = createExpandedView(builder, isLowPriority);
-        }
-
-        if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
-            result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
-        }
-
-        if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
-            result.newPublicView = builder.makePublicContentView();
-        }
-
-        if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
-            result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
-                    : builder.makeAmbientNotification();
-        }
-        result.packageContext = packageContext;
-        result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
-        result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
-                true /* showingPublic */);
-        return result;
-    }
-
-    public static CancellationSignal apply(InflationProgress result, int reInflateFlags,
-            ExpandableNotificationRow row, boolean redactAmbient,
-            RemoteViews.OnClickHandler remoteViewClickHandler,
-            @Nullable InflationCallback callback) {
-        NotificationData.Entry entry = row.getEntry();
-        NotificationContentView privateLayout = row.getPrivateLayout();
-        NotificationContentView publicLayout = row.getPublicLayout();
-        final HashMap<Integer, CancellationSignal> runningInflations = new HashMap<>();
-
-        int flag = FLAG_REINFLATE_CONTENT_VIEW;
-        if ((reInflateFlags & flag) != 0) {
-            boolean isNewView = !canReapplyRemoteView(result.newContentView, entry.cachedContentView);
-            ApplyCallback applyCallback = new ApplyCallback() {
-                @Override
-                public void setResultView(View v) {
-                    result.inflatedContentView = v;
-                }
-
-                @Override
-                public RemoteViews getRemoteView() {
-                    return result.newContentView;
-                }
-            };
-            applyRemoteView(result, reInflateFlags, flag, row, redactAmbient,
-                    isNewView, remoteViewClickHandler, callback, entry, privateLayout,
-                    privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
-                            NotificationContentView.VISIBLE_TYPE_CONTRACTED),
-                    runningInflations, applyCallback);
-        }
-
-        flag = FLAG_REINFLATE_EXPANDED_VIEW;
-        if ((reInflateFlags & flag) != 0) {
-            if (result.newExpandedView != null) {
-                boolean isNewView = !canReapplyRemoteView(result.newExpandedView,
-                        entry.cachedBigContentView);
-                ApplyCallback applyCallback = new ApplyCallback() {
-                    @Override
-                    public void setResultView(View v) {
-                        result.inflatedExpandedView = v;
-                    }
-
-                    @Override
-                    public RemoteViews getRemoteView() {
-                        return result.newExpandedView;
-                    }
-                };
-                applyRemoteView(result, reInflateFlags, flag, row,
-                        redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
-                        privateLayout, privateLayout.getExpandedChild(),
-                        privateLayout.getVisibleWrapper(
-                                NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
-                        applyCallback);
-            }
-        }
-
-        flag = FLAG_REINFLATE_HEADS_UP_VIEW;
-        if ((reInflateFlags & flag) != 0) {
-            if (result.newHeadsUpView != null) {
-                boolean isNewView = !canReapplyRemoteView(result.newHeadsUpView,
-                        entry.cachedHeadsUpContentView);
-                ApplyCallback applyCallback = new ApplyCallback() {
-                    @Override
-                    public void setResultView(View v) {
-                        result.inflatedHeadsUpView = v;
-                    }
-
-                    @Override
-                    public RemoteViews getRemoteView() {
-                        return result.newHeadsUpView;
-                    }
-                };
-                applyRemoteView(result, reInflateFlags, flag, row,
-                        redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
-                        privateLayout, privateLayout.getHeadsUpChild(),
-                        privateLayout.getVisibleWrapper(
-                                NotificationContentView.VISIBLE_TYPE_HEADSUP), runningInflations,
-                        applyCallback);
-            }
-        }
-
-        flag = FLAG_REINFLATE_PUBLIC_VIEW;
-        if ((reInflateFlags & flag) != 0) {
-            boolean isNewView = !canReapplyRemoteView(result.newPublicView,
-                    entry.cachedPublicContentView);
-            ApplyCallback applyCallback = new ApplyCallback() {
-                @Override
-                public void setResultView(View v) {
-                    result.inflatedPublicView = v;
-                }
-
-                @Override
-                public RemoteViews getRemoteView() {
-                    return result.newPublicView;
-                }
-            };
-            applyRemoteView(result, reInflateFlags, flag, row,
-                    redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
-                    publicLayout, publicLayout.getContractedChild(),
-                    publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
-                    runningInflations, applyCallback);
-        }
-
-        flag = FLAG_REINFLATE_AMBIENT_VIEW;
-        if ((reInflateFlags & flag) != 0) {
-            NotificationContentView newParent = redactAmbient ? publicLayout : privateLayout;
-            boolean isNewView = !canReapplyAmbient(row, redactAmbient) ||
-                    !canReapplyRemoteView(result.newAmbientView, entry.cachedAmbientContentView);
-            ApplyCallback applyCallback = new ApplyCallback() {
-                @Override
-                public void setResultView(View v) {
-                    result.inflatedAmbientView = v;
-                }
-
-                @Override
-                public RemoteViews getRemoteView() {
-                    return result.newAmbientView;
-                }
-            };
-            applyRemoteView(result, reInflateFlags, flag, row,
-                    redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
-                    newParent, newParent.getAmbientChild(), newParent.getVisibleWrapper(
-                            NotificationContentView.VISIBLE_TYPE_AMBIENT), runningInflations,
-                    applyCallback);
-        }
-
-        // Let's try to finish, maybe nobody is even inflating anything
-        finishIfDone(result, reInflateFlags, runningInflations, callback, row,
-                redactAmbient);
-        CancellationSignal cancellationSignal = new CancellationSignal();
-        cancellationSignal.setOnCancelListener(
-                () -> runningInflations.values().forEach(CancellationSignal::cancel));
-        return cancellationSignal;
-    }
-
-    @VisibleForTesting
-    static void applyRemoteView(final InflationProgress result,
-            final int reInflateFlags, int inflationId,
-            final ExpandableNotificationRow row,
-            final boolean redactAmbient, boolean isNewView,
-            RemoteViews.OnClickHandler remoteViewClickHandler,
-            @Nullable final InflationCallback callback, NotificationData.Entry entry,
-            NotificationContentView parentLayout, View existingView,
-            NotificationViewWrapper existingWrapper,
-            final HashMap<Integer, CancellationSignal> runningInflations,
-            ApplyCallback applyCallback) {
-        RemoteViews newContentView = applyCallback.getRemoteView();
-        if (callback != null && callback.doInflateSynchronous()) {
-            try {
-                if (isNewView) {
-                    View v = newContentView.apply(
-                            result.packageContext,
-                            parentLayout,
-                            remoteViewClickHandler);
-                    v.setIsRootNamespace(true);
-                    applyCallback.setResultView(v);
-                } else {
-                    newContentView.reapply(
-                            result.packageContext,
-                            existingView,
-                            remoteViewClickHandler);
-                    existingWrapper.onReinflated();
-                }
-            } catch (Exception e) {
-                handleInflationError(runningInflations, e, entry.notification, callback);
-                // Add a running inflation to make sure we don't trigger callbacks.
-                // Safe to do because only happens in tests.
-                runningInflations.put(inflationId, new CancellationSignal());
-            }
-            return;
-        }
-        RemoteViews.OnViewAppliedListener listener
-                = new RemoteViews.OnViewAppliedListener() {
-
-            @Override
-            public void onViewApplied(View v) {
-                if (isNewView) {
-                    v.setIsRootNamespace(true);
-                    applyCallback.setResultView(v);
-                } else if (existingWrapper != null) {
-                    existingWrapper.onReinflated();
-                }
-                runningInflations.remove(inflationId);
-                finishIfDone(result, reInflateFlags, runningInflations, callback, row,
-                        redactAmbient);
-            }
-
-            @Override
-            public void onError(Exception e) {
-                // Uh oh the async inflation failed. Due to some bugs (see b/38190555), this could
-                // actually also be a system issue, so let's try on the UI thread again to be safe.
-                try {
-                    View newView = existingView;
-                    if (isNewView) {
-                        newView = newContentView.apply(
-                                result.packageContext,
-                                parentLayout,
-                                remoteViewClickHandler);
-                    } else {
-                        newContentView.reapply(
-                                result.packageContext,
-                                existingView,
-                                remoteViewClickHandler);
-                    }
-                    Log.wtf(TAG, "Async Inflation failed but normal inflation finished normally.",
-                            e);
-                    onViewApplied(newView);
-                } catch (Exception anotherException) {
-                    runningInflations.remove(inflationId);
-                    handleInflationError(runningInflations, e, entry.notification, callback);
-                }
-            }
-        };
-        CancellationSignal cancellationSignal;
-        if (isNewView) {
-            cancellationSignal = newContentView.applyAsync(
-                    result.packageContext,
-                    parentLayout,
-                    EXECUTOR,
-                    listener,
-                    remoteViewClickHandler);
-        } else {
-            cancellationSignal = newContentView.reapplyAsync(
-                    result.packageContext,
-                    existingView,
-                    EXECUTOR,
-                    listener,
-                    remoteViewClickHandler);
-        }
-        runningInflations.put(inflationId, cancellationSignal);
-    }
-
-    private static void handleInflationError(HashMap<Integer, CancellationSignal> runningInflations,
-            Exception e, StatusBarNotification notification, @Nullable InflationCallback callback) {
-        Assert.isMainThread();
-        runningInflations.values().forEach(CancellationSignal::cancel);
-        if (callback != null) {
-            callback.handleInflationException(notification, e);
-        }
-    }
-
-    /**
-     * Finish the inflation of the views
-     *
-     * @return true if the inflation was finished
-     */
-    private static boolean finishIfDone(InflationProgress result, int reInflateFlags,
-            HashMap<Integer, CancellationSignal> runningInflations,
-            @Nullable InflationCallback endListener, ExpandableNotificationRow row,
-            boolean redactAmbient) {
-        Assert.isMainThread();
-        NotificationData.Entry entry = row.getEntry();
-        NotificationContentView privateLayout = row.getPrivateLayout();
-        NotificationContentView publicLayout = row.getPublicLayout();
-        if (runningInflations.isEmpty()) {
-            if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
-                if (result.inflatedContentView != null) {
-                    privateLayout.setContractedChild(result.inflatedContentView);
-                }
-                entry.cachedContentView = result.newContentView;
-            }
-
-            if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
-                if (result.inflatedExpandedView != null) {
-                    privateLayout.setExpandedChild(result.inflatedExpandedView);
-                } else if (result.newExpandedView == null) {
-                    privateLayout.setExpandedChild(null);
-                }
-                entry.cachedBigContentView = result.newExpandedView;
-                row.setExpandable(result.newExpandedView != null);
-            }
-
-            if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
-                if (result.inflatedHeadsUpView != null) {
-                    privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
-                } else if (result.newHeadsUpView == null) {
-                    privateLayout.setHeadsUpChild(null);
-                }
-                entry.cachedHeadsUpContentView = result.newHeadsUpView;
-            }
-
-            if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
-                if (result.inflatedPublicView != null) {
-                    publicLayout.setContractedChild(result.inflatedPublicView);
-                }
-                entry.cachedPublicContentView = result.newPublicView;
-            }
-
-            if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
-                if (result.inflatedAmbientView != null) {
-                    NotificationContentView newParent = redactAmbient
-                            ? publicLayout : privateLayout;
-                    NotificationContentView otherParent = !redactAmbient
-                            ? publicLayout : privateLayout;
-                    newParent.setAmbientChild(result.inflatedAmbientView);
-                    otherParent.setAmbientChild(null);
-                }
-                entry.cachedAmbientContentView = result.newAmbientView;
-            }
-            entry.headsUpStatusBarText = result.headsUpStatusBarText;
-            entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
-            if (endListener != null) {
-                endListener.onAsyncInflationFinished(row.getEntry());
-            }
-            return true;
-        }
-        return false;
-    }
-
-    private static RemoteViews createExpandedView(Notification.Builder builder,
-            boolean isLowPriority) {
-        RemoteViews bigContentView = builder.createBigContentView();
-        if (bigContentView != null) {
-            return bigContentView;
-        }
-        if (isLowPriority) {
-            RemoteViews contentView = builder.createContentView();
-            Notification.Builder.makeHeaderExpanded(contentView);
-            return contentView;
-        }
-        return null;
-    }
-
-    private static RemoteViews createContentView(Notification.Builder builder,
-            boolean isLowPriority, boolean useLarge) {
-        if (isLowPriority) {
-            return builder.makeLowPriorityContentView(false /* useRegularSubtext */);
-        }
-        return builder.createContentView(useLarge);
-    }
-
-    /**
-     * @param newView The new view that will be applied
-     * @param oldView The old view that was applied to the existing view before
-     * @return {@code true} if the RemoteViews are the same and the view can be reused to reapply.
-     */
-     @VisibleForTesting
-     static boolean canReapplyRemoteView(final RemoteViews newView,
-            final RemoteViews oldView) {
-        return (newView == null && oldView == null) ||
-                (newView != null && oldView != null
-                        && oldView.getPackage() != null
-                        && newView.getPackage() != null
-                        && newView.getPackage().equals(oldView.getPackage())
-                        && newView.getLayoutId() == oldView.getLayoutId()
-                        && !oldView.isReapplyDisallowed());
-    }
-
-    public void setInflationCallback(InflationCallback callback) {
-        mCallback = callback;
-    }
-
-    public interface InflationCallback {
-        void handleInflationException(StatusBarNotification notification, Exception e);
-        void onAsyncInflationFinished(NotificationData.Entry entry);
-
-        /**
-         * Used to disable async-ness for tests. Should only be used for tests.
-         */
-        default boolean doInflateSynchronous() {
-            return false;
-        }
-    }
-
-    public void onDensityOrFontScaleChanged() {
-        NotificationData.Entry entry = mRow.getEntry();
-        entry.cachedAmbientContentView = null;
-        entry.cachedBigContentView = null;
-        entry.cachedContentView = null;
-        entry.cachedHeadsUpContentView = null;
-        entry.cachedPublicContentView = null;
-        inflateNotificationViews();
-    }
-
-    private static boolean canReapplyAmbient(ExpandableNotificationRow row, boolean redactAmbient) {
-        NotificationContentView ambientView = redactAmbient ? row.getPublicLayout()
-                : row.getPrivateLayout();            ;
-        return ambientView.getAmbientChild() != null;
-    }
-
-    public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
-            implements InflationCallback, InflationTask {
-
-        private final StatusBarNotification mSbn;
-        private final Context mContext;
-        private final boolean mIsLowPriority;
-        private final boolean mIsChildInGroup;
-        private final boolean mUsesIncreasedHeight;
-        private final InflationCallback mCallback;
-        private final boolean mUsesIncreasedHeadsUpHeight;
-        private final boolean mRedactAmbient;
-        private int mReInflateFlags;
-        private ExpandableNotificationRow mRow;
-        private Exception mError;
-        private RemoteViews.OnClickHandler mRemoteViewClickHandler;
-        private CancellationSignal mCancellationSignal;
-
-        private AsyncInflationTask(StatusBarNotification notification,
-                int reInflateFlags, ExpandableNotificationRow row, boolean isLowPriority,
-                boolean isChildInGroup, boolean usesIncreasedHeight,
-                boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
-                InflationCallback callback,
-                RemoteViews.OnClickHandler remoteViewClickHandler) {
-            mRow = row;
-            mSbn = notification;
-            mReInflateFlags = reInflateFlags;
-            mContext = mRow.getContext();
-            mIsLowPriority = isLowPriority;
-            mIsChildInGroup = isChildInGroup;
-            mUsesIncreasedHeight = usesIncreasedHeight;
-            mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight;
-            mRedactAmbient = redactAmbient;
-            mRemoteViewClickHandler = remoteViewClickHandler;
-            mCallback = callback;
-            NotificationData.Entry entry = row.getEntry();
-            entry.setInflationTask(this);
-        }
-
-        @VisibleForTesting
-        public int getReInflateFlags() {
-            return mReInflateFlags;
-        }
-
-        @Override
-        protected InflationProgress doInBackground(Void... params) {
-            try {
-                final Notification.Builder recoveredBuilder
-                        = Notification.Builder.recoverBuilder(mContext,
-                        mSbn.getNotification());
-                Context packageContext = mSbn.getPackageContext(mContext);
-                Notification notification = mSbn.getNotification();
-                if (notification.isMediaNotification()) {
-                    MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
-                            packageContext);
-                    processor.processNotification(notification, recoveredBuilder);
-                }
-                return createRemoteViews(mReInflateFlags,
-                        recoveredBuilder, mIsLowPriority, mIsChildInGroup,
-                        mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
-                        packageContext);
-            } catch (Exception e) {
-                mError = e;
-                return null;
-            }
-        }
-
-        @Override
-        protected void onPostExecute(InflationProgress result) {
-            if (mError == null) {
-                mCancellationSignal = apply(result, mReInflateFlags, mRow, mRedactAmbient,
-                        mRemoteViewClickHandler, this);
-            } else {
-                handleError(mError);
-            }
-        }
-
-        private void handleError(Exception e) {
-            mRow.getEntry().onInflationTaskFinished();
-            StatusBarNotification sbn = mRow.getStatusBarNotification();
-            final String ident = sbn.getPackageName() + "/0x"
-                    + Integer.toHexString(sbn.getId());
-            Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
-            mCallback.handleInflationException(sbn,
-                    new InflationException("Couldn't inflate contentViews" + e));
-        }
-
-        @Override
-        public void abort() {
-            cancel(true /* mayInterruptIfRunning */);
-            if (mCancellationSignal != null) {
-                mCancellationSignal.cancel();
-            }
-        }
-
-        @Override
-        public void supersedeTask(InflationTask task) {
-            if (task instanceof AsyncInflationTask) {
-                // We want to inflate all flags of the previous task as well
-                mReInflateFlags |= ((AsyncInflationTask) task).mReInflateFlags;
-            }
-        }
-
-        @Override
-        public void handleInflationException(StatusBarNotification notification, Exception e) {
-            handleError(e);
-        }
-
-        @Override
-        public void onAsyncInflationFinished(NotificationData.Entry entry) {
-            mRow.getEntry().onInflationTaskFinished();
-            mRow.onNotificationUpdated();
-            mCallback.onAsyncInflationFinished(mRow.getEntry());
-        }
-
-        @Override
-        public boolean doInflateSynchronous() {
-            return mCallback != null && mCallback.doInflateSynchronous();
-        }
-    }
-
-    @VisibleForTesting
-    static class InflationProgress {
-        private RemoteViews newContentView;
-        private RemoteViews newHeadsUpView;
-        private RemoteViews newExpandedView;
-        private RemoteViews newAmbientView;
-        private RemoteViews newPublicView;
-
-        @VisibleForTesting
-        Context packageContext;
-
-        private View inflatedContentView;
-        private View inflatedHeadsUpView;
-        private View inflatedExpandedView;
-        private View inflatedAmbientView;
-        private View inflatedPublicView;
-        private CharSequence headsUpStatusBarText;
-        private CharSequence headsUpStatusBarTextPublic;
-    }
-
-    @VisibleForTesting
-    abstract static class ApplyCallback {
-        public abstract void setResultView(View v);
-        public abstract RemoteViews getRemoteView();
-    }
-
-    /**
-     * A custom executor that allows more tasks to be queued. Default values are copied from
-     * AsyncTask
-      */
-    private static class InflationExecutor implements Executor {
-        private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
-        // We want at least 2 threads and at most 4 threads in the core pool,
-        // preferring to have 1 less than the CPU count to avoid saturating
-        // the CPU with background work
-        private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
-        private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
-        private static final int KEEP_ALIVE_SECONDS = 30;
-
-        private static final ThreadFactory sThreadFactory = new ThreadFactory() {
-            private final AtomicInteger mCount = new AtomicInteger(1);
-
-            public Thread newThread(Runnable r) {
-                return new Thread(r, "InflaterThread #" + mCount.getAndIncrement());
-            }
-        };
-
-        private final ThreadPoolExecutor mExecutor;
-
-        private InflationExecutor() {
-            mExecutor = new ThreadPoolExecutor(
-                    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
-                    new LinkedBlockingQueue<>(), sThreadFactory);
-            mExecutor.allowCoreThreadTimeOut(true);
-        }
-
-        @Override
-        public void execute(Runnable runnable) {
-            mExecutor.execute(runnable);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
deleted file mode 100644
index 548f006..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import android.content.Context;
-import android.view.View;
-
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.TransformableView;
-
-/**
- * Wraps a notification containing a media template
- */
-public class NotificationMediaTemplateViewWrapper extends NotificationTemplateViewWrapper {
-
-    protected NotificationMediaTemplateViewWrapper(Context ctx, View view,
-            ExpandableNotificationRow row) {
-        super(ctx, view, row);
-    }
-
-    View mActions;
-
-    private void resolveViews() {
-        mActions = mView.findViewById(com.android.internal.R.id.media_actions);
-    }
-
-    @Override
-    public void onContentUpdated(ExpandableNotificationRow row) {
-        // Reinspect the notification. Before the super call, because the super call also updates
-        // the transformation types and we need to have our values set by then.
-        resolveViews();
-        super.onContentUpdated(row);
-    }
-
-    @Override
-    protected void updateTransformedTypes() {
-        // This also clears the existing types
-        super.updateTransformedTypes();
-        if (mActions != null) {
-            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ACTIONS,
-                    mActions);
-        }
-    }
-
-    @Override
-    public boolean isDimmable() {
-        return getCustomBackgroundColor() == 0;
-    }
-
-    @Override
-    public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
-        return true;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java
deleted file mode 100644
index fb5644f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import com.android.internal.widget.MessagingLayout;
-import com.android.internal.widget.MessagingLinearLayout;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.TransformableView;
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-
-/**
- * Wraps a notification containing a messaging template
- */
-public class NotificationMessagingTemplateViewWrapper extends NotificationTemplateViewWrapper {
-
-    private final int mMinHeightWithActions;
-    private MessagingLayout mMessagingLayout;
-    private MessagingLinearLayout mMessagingLinearLayout;
-
-    protected NotificationMessagingTemplateViewWrapper(Context ctx, View view,
-            ExpandableNotificationRow row) {
-        super(ctx, view, row);
-        mMessagingLayout = (MessagingLayout) view;
-        mMinHeightWithActions = NotificationUtils.getFontScaledHeight(ctx,
-                R.dimen.notification_messaging_actions_min_height);
-    }
-
-    private void resolveViews() {
-        mMessagingLinearLayout = mMessagingLayout.getMessagingLinearLayout();
-    }
-
-    @Override
-    public void onContentUpdated(ExpandableNotificationRow row) {
-        // Reinspect the notification. Before the super call, because the super call also updates
-        // the transformation types and we need to have our values set by then.
-        resolveViews();
-        super.onContentUpdated(row);
-    }
-
-    @Override
-    protected void updateTransformedTypes() {
-        // This also clears the existing types
-        super.updateTransformedTypes();
-        if (mMessagingLinearLayout != null) {
-            mTransformationHelper.addTransformedView(mMessagingLinearLayout.getId(),
-                    mMessagingLinearLayout);
-        }
-    }
-
-    @Override
-    public void setRemoteInputVisible(boolean visible) {
-        mMessagingLayout.showHistoricMessages(visible);
-    }
-
-    @Override
-    public int getMinLayoutHeight() {
-        if (mActionsContainer != null && mActionsContainer.getVisibility() != View.GONE) {
-            return mMinHeightWithActions;
-        }
-        return super.getMinLayoutHeight();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
deleted file mode 100644
index d4b0be8..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ /dev/null
@@ -1,345 +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
- */
-
-package com.android.systemui.statusbar.notification;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.android.internal.util.ContrastColorUtil;
-import com.android.internal.widget.NotificationActionListLayout;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.UiOffloadThread;
-import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.TransformableView;
-import com.android.systemui.statusbar.ViewTransformationHelper;
-
-/**
- * Wraps a notification view inflated from a template.
- */
-public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapper {
-
-    protected ImageView mPicture;
-    private ProgressBar mProgressBar;
-    private TextView mTitle;
-    private TextView mText;
-    protected View mActionsContainer;
-    private ImageView mReplyAction;
-    private Rect mTmpRect = new Rect();
-
-    private int mContentHeight;
-    private int mMinHeightHint;
-    private NotificationActionListLayout mActions;
-    private ArraySet<PendingIntent> mCancelledPendingIntents = new ArraySet<>();
-    private UiOffloadThread mUiOffloadThread;
-    private View mRemoteInputHistory;
-
-    protected NotificationTemplateViewWrapper(Context ctx, View view,
-            ExpandableNotificationRow row) {
-        super(ctx, view, row);
-        mTransformationHelper.setCustomTransformation(
-                new ViewTransformationHelper.CustomTransformation() {
-                    @Override
-                    public boolean transformTo(TransformState ownState,
-                            TransformableView notification, final float transformationAmount) {
-                        if (!(notification instanceof HybridNotificationView)) {
-                            return false;
-                        }
-                        TransformState otherState = notification.getCurrentState(
-                                TRANSFORMING_VIEW_TITLE);
-                        final View text = ownState.getTransformedView();
-                        CrossFadeHelper.fadeOut(text, transformationAmount);
-                        if (otherState != null) {
-                            ownState.transformViewVerticalTo(otherState, this,
-                                    transformationAmount);
-                            otherState.recycle();
-                        }
-                        return true;
-                    }
-
-                    @Override
-                    public boolean customTransformTarget(TransformState ownState,
-                            TransformState otherState) {
-                        float endY = getTransformationY(ownState, otherState);
-                        ownState.setTransformationEndY(endY);
-                        return true;
-                    }
-
-                    @Override
-                    public boolean transformFrom(TransformState ownState,
-                            TransformableView notification, float transformationAmount) {
-                        if (!(notification instanceof HybridNotificationView)) {
-                            return false;
-                        }
-                        TransformState otherState = notification.getCurrentState(
-                                TRANSFORMING_VIEW_TITLE);
-                        final View text = ownState.getTransformedView();
-                        CrossFadeHelper.fadeIn(text, transformationAmount);
-                        if (otherState != null) {
-                            ownState.transformViewVerticalFrom(otherState, this,
-                                    transformationAmount);
-                            otherState.recycle();
-                        }
-                        return true;
-                    }
-
-                    @Override
-                    public boolean initTransformation(TransformState ownState,
-                            TransformState otherState) {
-                        float startY = getTransformationY(ownState, otherState);
-                        ownState.setTransformationStartY(startY);
-                        return true;
-                    }
-
-                    private float getTransformationY(TransformState ownState,
-                            TransformState otherState) {
-                        int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
-                        int[] ownStablePosition = ownState.getLaidOutLocationOnScreen();
-                        return (otherStablePosition[1]
-                                + otherState.getTransformedView().getHeight()
-                                - ownStablePosition[1]) * 0.33f;
-                    }
-
-                }, TRANSFORMING_VIEW_TEXT);
-    }
-
-    private void resolveTemplateViews(StatusBarNotification notification) {
-        mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
-        if (mPicture != null) {
-            mPicture.setTag(ImageTransformState.ICON_TAG,
-                    notification.getNotification().getLargeIcon());
-        }
-        mTitle = (TextView) mView.findViewById(com.android.internal.R.id.title);
-        mText = (TextView) mView.findViewById(com.android.internal.R.id.text);
-        final View progress = mView.findViewById(com.android.internal.R.id.progress);
-        if (progress instanceof ProgressBar) {
-            mProgressBar = (ProgressBar) progress;
-        } else {
-            // It's still a viewstub
-            mProgressBar = null;
-        }
-        mActionsContainer = mView.findViewById(com.android.internal.R.id.actions_container);
-        mActions = mView.findViewById(com.android.internal.R.id.actions);
-        mReplyAction = mView.findViewById(com.android.internal.R.id.reply_icon_action);
-        mRemoteInputHistory = mView.findViewById(
-                com.android.internal.R.id.notification_material_reply_container);
-        updatePendingIntentCancellations();
-    }
-
-    private void updatePendingIntentCancellations() {
-        if (mActions != null) {
-            int numActions = mActions.getChildCount();
-            for (int i = 0; i < numActions; i++) {
-                Button action = (Button) mActions.getChildAt(i);
-                performOnPendingIntentCancellation(action, () -> {
-                    if (action.isEnabled()) {
-                        action.setEnabled(false);
-                        // The visual appearance doesn't look disabled enough yet, let's add the
-                        // alpha as well. Since Alpha doesn't play nicely right now with the
-                        // transformation, we rather blend it manually with the background color.
-                        ColorStateList textColors = action.getTextColors();
-                        int[] colors = textColors.getColors();
-                        int[] newColors = new int[colors.length];
-                        float disabledAlpha = mView.getResources().getFloat(
-                                com.android.internal.R.dimen.notification_action_disabled_alpha);
-                        for (int j = 0; j < colors.length; j++) {
-                            int color = colors[j];
-                            color = blendColorWithBackground(color, disabledAlpha);
-                            newColors[j] = color;
-                        }
-                        ColorStateList newColorStateList = new ColorStateList(
-                                textColors.getStates(), newColors);
-                        action.setTextColor(newColorStateList);
-                    }
-                });
-            }
-        }
-        if (mReplyAction != null) {
-            // Let's reset the view on update, assuming the new pending intent isn't cancelled
-            // anymore. The color filter automatically resets when it's updated.
-            mReplyAction.setEnabled(true);
-            performOnPendingIntentCancellation(mReplyAction, () -> {
-                if (mReplyAction != null && mReplyAction.isEnabled()) {
-                    mReplyAction.setEnabled(false);
-                    // The visual appearance doesn't look disabled enough yet, let's add the
-                    // alpha as well. Since Alpha doesn't play nicely right now with the
-                    // transformation, we rather blend it manually with the background color.
-                    Drawable drawable = mReplyAction.getDrawable().mutate();
-                    PorterDuffColorFilter colorFilter =
-                            (PorterDuffColorFilter) drawable.getColorFilter();
-                    float disabledAlpha = mView.getResources().getFloat(
-                            com.android.internal.R.dimen.notification_action_disabled_alpha);
-                    if (colorFilter != null) {
-                        int color = colorFilter.getColor();
-                        color = blendColorWithBackground(color, disabledAlpha);
-                        drawable.mutate().setColorFilter(color, colorFilter.getMode());
-                    } else {
-                        mReplyAction.setAlpha(disabledAlpha);
-                    }
-                }
-            });
-        }
-    }
-
-    private int blendColorWithBackground(int color, float alpha) {
-        // alpha doesn't go well for color filters, so let's blend it manually
-        return ContrastColorUtil.compositeColors(Color.argb((int) (alpha * 255),
-                Color.red(color), Color.green(color), Color.blue(color)), resolveBackgroundColor());
-    }
-
-    private void performOnPendingIntentCancellation(View view, Runnable cancellationRunnable) {
-        PendingIntent pendingIntent = (PendingIntent) view.getTag(
-                com.android.internal.R.id.pending_intent_tag);
-        if (pendingIntent == null) {
-            return;
-        }
-        if (mCancelledPendingIntents.contains(pendingIntent)) {
-            cancellationRunnable.run();
-        } else {
-            PendingIntent.CancelListener listener = (PendingIntent intent) -> {
-                mView.post(() -> {
-                    mCancelledPendingIntents.add(pendingIntent);
-                    cancellationRunnable.run();
-                });
-            };
-            if (mUiOffloadThread == null) {
-                mUiOffloadThread = Dependency.get(UiOffloadThread.class);
-            }
-            if (view.isAttachedToWindow()) {
-                mUiOffloadThread.submit(() -> pendingIntent.registerCancelListener(listener));
-            }
-            view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
-                @Override
-                public void onViewAttachedToWindow(View v) {
-                    mUiOffloadThread.submit(() -> pendingIntent.registerCancelListener(listener));
-                }
-
-                @Override
-                public void onViewDetachedFromWindow(View v) {
-                    mUiOffloadThread.submit(() -> pendingIntent.unregisterCancelListener(listener));
-                }
-            });
-        }
-    }
-
-    @Override
-    public boolean disallowSingleClick(float x, float y) {
-        if (mReplyAction != null && mReplyAction.getVisibility() == View.VISIBLE) {
-            if (isOnView(mReplyAction, x, y) || isOnView(mPicture, x, y)) {
-                return true;
-            }
-        }
-        return super.disallowSingleClick(x, y);
-    }
-
-    private boolean isOnView(View view, float x, float y) {
-        View searchView = (View) view.getParent();
-        while (searchView != null && !(searchView instanceof ExpandableNotificationRow)) {
-            searchView.getHitRect(mTmpRect);
-            x -= mTmpRect.left;
-            y -= mTmpRect.top;
-            searchView = (View) searchView.getParent();
-        }
-        view.getHitRect(mTmpRect);
-        return mTmpRect.contains((int) x,(int) y);
-    }
-
-    @Override
-    public void onContentUpdated(ExpandableNotificationRow row) {
-        // Reinspect the notification. Before the super call, because the super call also updates
-        // the transformation types and we need to have our values set by then.
-        resolveTemplateViews(row.getStatusBarNotification());
-        super.onContentUpdated(row);
-    }
-
-    @Override
-    protected void updateTransformedTypes() {
-        // This also clears the existing types
-        super.updateTransformedTypes();
-        if (mTitle != null) {
-            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
-                    mTitle);
-        }
-        if (mText != null) {
-            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT,
-                    mText);
-        }
-        if (mPicture != null) {
-            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE,
-                    mPicture);
-        }
-        if (mProgressBar != null) {
-            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS,
-                    mProgressBar);
-        }
-    }
-
-    @Override
-    public void setContentHeight(int contentHeight, int minHeightHint) {
-        super.setContentHeight(contentHeight, minHeightHint);
-
-        mContentHeight = contentHeight;
-        mMinHeightHint = minHeightHint;
-        updateActionOffset();
-    }
-
-    @Override
-    public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
-        if (super.shouldClipToRounding(topRounded, bottomRounded)) {
-            return true;
-        }
-        return bottomRounded && mActionsContainer != null
-                && mActionsContainer.getVisibility() != View.GONE;
-    }
-
-    private void updateActionOffset() {
-        if (mActionsContainer != null) {
-            // We should never push the actions higher than they are in the headsup view.
-            int constrainedContentHeight = Math.max(mContentHeight, mMinHeightHint);
-
-            // We also need to compensate for any header translation, since we're always at the end.
-            mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight()
-                    - getHeaderTranslation());
-        }
-    }
-
-    @Override
-    public int getExtraMeasureHeight() {
-        int extra = 0;
-        if (mActions != null) {
-            extra = mActions.getExtraMeasureHeight();
-        }
-        if (mRemoteInputHistory != null && mRemoteInputHistory.getVisibility() != View.GONE) {
-            extra += mRow.getContext().getResources().getDimensionPixelSize(
-                    R.dimen.remote_input_history_extra_height);
-        }
-        return extra + super.getExtraMeasureHeight();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
deleted file mode 100644
index 93058b8..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ /dev/null
@@ -1,191 +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
- */
-
-package com.android.systemui.statusbar.notification;
-
-import android.content.Context;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.view.NotificationHeaderView;
-import android.view.View;
-
-import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.TransformableView;
-
-/**
- * Wraps the actual notification content view; used to implement behaviors which are different for
- * the individual templates and custom views.
- */
-public abstract class NotificationViewWrapper implements TransformableView {
-
-    protected final View mView;
-    protected final ExpandableNotificationRow mRow;
-
-    private int mBackgroundColor = 0;
-
-    public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) {
-        if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
-            if ("bigPicture".equals(v.getTag())) {
-                return new NotificationBigPictureTemplateViewWrapper(ctx, v, row);
-            } else if ("bigText".equals(v.getTag())) {
-                return new NotificationBigTextTemplateViewWrapper(ctx, v, row);
-            } else if ("media".equals(v.getTag()) || "bigMediaNarrow".equals(v.getTag())) {
-                return new NotificationMediaTemplateViewWrapper(ctx, v, row);
-            } else if ("messaging".equals(v.getTag())) {
-                return new NotificationMessagingTemplateViewWrapper(ctx, v, row);
-            }
-            return new NotificationTemplateViewWrapper(ctx, v, row);
-        } else if (v instanceof NotificationHeaderView) {
-            return new NotificationHeaderViewWrapper(ctx, v, row);
-        } else {
-            return new NotificationCustomViewWrapper(ctx, v, row);
-        }
-    }
-
-    protected NotificationViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
-        mView = view;
-        mRow = row;
-        onReinflated();
-    }
-
-    /**
-     * Notifies this wrapper that the content of the view might have changed.
-     * @param row the row this wrapper is attached to
-     */
-    public void onContentUpdated(ExpandableNotificationRow row) {
-    }
-
-    public void onReinflated() {
-        if (shouldClearBackgroundOnReapply()) {
-            mBackgroundColor = 0;
-        }
-        Drawable background = mView.getBackground();
-        if (background instanceof ColorDrawable) {
-            mBackgroundColor = ((ColorDrawable) background).getColor();
-            mView.setBackground(null);
-        }
-    }
-
-    protected boolean shouldClearBackgroundOnReapply() {
-        return true;
-    }
-
-    /**
-     * Update the appearance of the expand button.
-     *
-     * @param expandable should this view be expandable
-     * @param onClickListener the listener to invoke when the expand affordance is clicked on
-     */
-    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {}
-
-    /**
-     * @return the notification header if it exists
-     */
-    public NotificationHeaderView getNotificationHeader() {
-        return null;
-    }
-
-    public int getHeaderTranslation() {
-        return 0;
-    }
-
-    @Override
-    public TransformState getCurrentState(int fadingView) {
-        return null;
-    }
-
-    @Override
-    public void transformTo(TransformableView notification, Runnable endRunnable) {
-        // By default we are fading out completely
-        CrossFadeHelper.fadeOut(mView, endRunnable);
-    }
-
-    @Override
-    public void transformTo(TransformableView notification, float transformationAmount) {
-        CrossFadeHelper.fadeOut(mView, transformationAmount);
-    }
-
-    @Override
-    public void transformFrom(TransformableView notification) {
-        // By default we are fading in completely
-        CrossFadeHelper.fadeIn(mView);
-    }
-
-    @Override
-    public void transformFrom(TransformableView notification, float transformationAmount) {
-        CrossFadeHelper.fadeIn(mView, transformationAmount);
-    }
-
-    @Override
-    public void setVisible(boolean visible) {
-        mView.animate().cancel();
-        mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
-    }
-
-    public int getCustomBackgroundColor() {
-        // Parent notifications should always use the normal background color
-        return mRow.isSummaryWithChildren() ? 0 : mBackgroundColor;
-    }
-
-    protected int resolveBackgroundColor() {
-        int customBackgroundColor = getCustomBackgroundColor();
-        if (customBackgroundColor != 0) {
-            return customBackgroundColor;
-        }
-        return mView.getContext().getColor(
-                com.android.internal.R.color.notification_material_background_color);
-    }
-
-    public void setLegacy(boolean legacy) {
-    }
-
-    public void setContentHeight(int contentHeight, int minHeightHint) {
-    }
-
-    public void setRemoteInputVisible(boolean visible) {
-    }
-
-    public void setIsChildInGroup(boolean isChildInGroup) {
-    }
-
-    public boolean isDimmable() {
-        return true;
-    }
-
-    public boolean disallowSingleClick(float x, float y) {
-        return false;
-    }
-
-    public int getMinLayoutHeight() {
-        return 0;
-    }
-
-    public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
-        return false;
-    }
-
-    public void setHeaderVisibleAmount(float headerVisibleAmount) {
-    }
-
-    /**
-     * Get the extra height that needs to be added to this view, such that it can be measured
-     * normally.
-     */
-    public int getExtraMeasureHeight() {
-        return 0;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
index 2efcd16..aacb22d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
@@ -24,11 +24,10 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
-import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.Interpolators;
-import com.android.systemui.statusbar.stack.AnimationFilter;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.statusbar.notification.stack.AnimationFilter;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.ViewState;
 
 /**
  * An animator to animate properties
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java
deleted file mode 100644
index 9b9dfc9..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java
+++ /dev/null
@@ -1,82 +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.notification;
-
-import android.content.Context;
-import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-
-/**
- * An inflater task that asynchronously inflates a ExpandableNotificationRow
- */
-public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInflateFinishedListener {
-
-    private static final String TAG = "RowInflaterTask";
-    private static final boolean TRACE_ORIGIN = true;
-
-    private RowInflationFinishedListener mListener;
-    private NotificationData.Entry 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,
-            RowInflationFinishedListener listener) {
-        if (TRACE_ORIGIN) {
-            mInflateOrigin = new Throwable("inflate requested here");
-        }
-        mListener = listener;
-        AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
-        mEntry = entry;
-        entry.setInflationTask(this);
-        inflater.inflate(R.layout.status_bar_notification_row, parent, this);
-    }
-
-    @Override
-    public void abort() {
-        mCancelled = true;
-    }
-
-    @Override
-    public void onInflateFinished(View view, int resid, ViewGroup parent) {
-        if (!mCancelled) {
-            try {
-                mEntry.onInflationTaskFinished();
-                mListener.onInflationFinished((ExpandableNotificationRow) view);
-            } catch (Throwable t) {
-                if (mInflateOrigin != null) {
-                    Log.e(TAG, "Error in inflation finished listener: " + t, mInflateOrigin);
-                    t.addSuppressed(mInflateOrigin);
-                }
-                throw t;
-            }
-        }
-    }
-
-    public interface RowInflationFinishedListener {
-        void onInflationFinished(ExpandableNotificationRow row);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index 879ac92..07b8c35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -29,7 +29,7 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.TransformableView;
 import com.android.systemui.statusbar.ViewTransformationHelper;
 
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 4a52acc..81208c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.notification;
 
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
  * An object that can determine the visibility of a Notification.
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 7fe01c0..da8954a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -19,8 +19,7 @@
 import androidx.collection.ArraySet;
 import android.view.View;
 
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
 import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java
new file mode 100644
index 0000000..28c07a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java
@@ -0,0 +1,44 @@
+/*
+ * 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.logging;
+
+/**
+ * Constants for counter tags for Notification-related actions/views.
+ */
+public class NotificationCounters {
+    /** Counter tag for notification dismissal. */
+    public static final String NOTIFICATION_DISMISSED = "notification_dismissed";
+
+    /** Counter tag for when the blocking helper is shown to the user. */
+    public static final String BLOCKING_HELPER_SHOWN = "blocking_helper_shown";
+    /** Counter tag for when the blocking helper is dismissed via a miscellaneous interaction. */
+    public static final String BLOCKING_HELPER_DISMISSED = "blocking_helper_dismissed";
+    /** Counter tag for when the user hits 'stop notifications' in the blocking helper. */
+    public static final String BLOCKING_HELPER_STOP_NOTIFICATIONS =
+            "blocking_helper_stop_notifications";
+    /** Counter tag for when the user hits 'keep showing' in the blocking helper. */
+    public static final String BLOCKING_HELPER_KEEP_SHOWING =
+            "blocking_helper_keep_showing";
+    /**
+     * Counter tag for when the user hits undo in context of the blocking helper - this can happen
+     * multiple times per view.
+     */
+    public static final String BLOCKING_HELPER_UNDO = "blocking_helper_undo";
+    /** Counter tag for when the user hits the notification settings icon in the blocking helper. */
+    public static final String BLOCKING_HELPER_NOTIF_SETTINGS =
+            "blocking_helper_notif_settings";
+}
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
new file mode 100644
index 0000000..767b07f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -0,0 +1,254 @@
+/*
+ * 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.notification.logging;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.service.notification.NotificationListenerService;
+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.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Handles notification logging, in particular, logging which notifications are visible and which
+ * are not.
+ */
+public class NotificationLogger {
+    private static final String TAG = "NotificationLogger";
+
+    /** The minimum delay in ms between reports of notification visibility. */
+    private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
+
+    /** Keys of notifications currently visible to the user. */
+    private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications =
+            new ArraySet<>();
+
+    // Dependencies:
+    private final NotificationListenerService mNotificationListener =
+            Dependency.get(NotificationListener.class);
+    private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+
+    protected NotificationEntryManager mEntryManager;
+    protected Handler mHandler = new Handler();
+    protected IStatusBarService mBarService;
+    private long mLastVisibilityReportUptimeMs;
+    private NotificationListContainer mListContainer;
+
+    protected final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
+            new OnChildLocationsChangedListener() {
+                @Override
+                public void onChildLocationsChanged() {
+                    if (mHandler.hasCallbacks(mVisibilityReporter)) {
+                        // Visibilities will be reported when the existing
+                        // callback is executed.
+                        return;
+                    }
+                    // Calculate when we're allowed to run the visibility
+                    // reporter. Note that this timestamp might already have
+                    // passed. That's OK, the callback will just be executed
+                    // ASAP.
+                    long nextReportUptimeMs =
+                            mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
+                    mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
+                }
+            };
+
+    // Tracks notifications currently visible in mNotificationStackScroller and
+    // emits visibility events via NoMan on changes.
+    protected final Runnable mVisibilityReporter = new Runnable() {
+        private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications =
+                new ArraySet<>();
+        private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications =
+                new ArraySet<>();
+        private final ArraySet<NotificationVisibility> mTmpNoLongerVisibleNotifications =
+                new ArraySet<>();
+
+        @Override
+        public void run() {
+            mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
+
+            // 1. Loop over mNotificationData entries:
+            //   A. Keep list of visible notifications.
+            //   B. Keep list of previously hidden, now visible notifications.
+            // 2. Compute no-longer visible notifications by removing currently
+            //    visible notifications from the set of previously visible
+            //    notifications.
+            // 3. Report newly visible and no-longer visible notifications.
+            // 4. Keep currently visible notifications for next report.
+            ArrayList<NotificationData.Entry> activeNotifications = mEntryManager
+                    .getNotificationData().getActiveNotifications();
+            int N = activeNotifications.size();
+            for (int i = 0; i < N; i++) {
+                NotificationData.Entry entry = activeNotifications.get(i);
+                String key = entry.notification.getKey();
+                boolean isVisible = mListContainer.isInVisibleLocation(entry.row);
+                NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
+                boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
+                if (isVisible) {
+                    // Build new set of visible notifications.
+                    mTmpCurrentlyVisibleNotifications.add(visObj);
+                    if (!previouslyVisible) {
+                        mTmpNewlyVisibleNotifications.add(visObj);
+                    }
+                } else {
+                    // release object
+                    visObj.recycle();
+                }
+            }
+            mTmpNoLongerVisibleNotifications.addAll(mCurrentlyVisibleNotifications);
+            mTmpNoLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications);
+
+            logNotificationVisibilityChanges(
+                    mTmpNewlyVisibleNotifications, mTmpNoLongerVisibleNotifications);
+
+            recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
+            mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications);
+
+            recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications);
+            mTmpCurrentlyVisibleNotifications.clear();
+            mTmpNewlyVisibleNotifications.clear();
+            mTmpNoLongerVisibleNotifications.clear();
+        }
+    };
+
+    public NotificationLogger() {
+        mBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+    }
+
+    public void setUpWithEntryManager(NotificationEntryManager entryManager,
+            NotificationListContainer listContainer) {
+        mEntryManager = entryManager;
+        mListContainer = listContainer;
+    }
+
+    public void stopNotificationLogging() {
+        // Report all notifications as invisible and turn down the
+        // reporter.
+        if (!mCurrentlyVisibleNotifications.isEmpty()) {
+            logNotificationVisibilityChanges(
+                    Collections.emptyList(), mCurrentlyVisibleNotifications);
+            recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
+        }
+        mHandler.removeCallbacks(mVisibilityReporter);
+        mListContainer.setChildLocationsChangedListener(null);
+    }
+
+    public void startNotificationLogging() {
+        mListContainer.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
+        // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
+        // cause the scroller to emit child location events. Hence generate
+        // one ourselves to guarantee that we're reporting visible
+        // notifications.
+        // (Note that in cases where the scroller does emit events, this
+        // additional event doesn't break anything.)
+        mNotificationLocationsChangedListener.onChildLocationsChanged();
+    }
+
+    private void logNotificationVisibilityChanges(
+            Collection<NotificationVisibility> newlyVisible,
+            Collection<NotificationVisibility> noLongerVisible) {
+        if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
+            return;
+        }
+        final NotificationVisibility[] newlyVisibleAr = cloneVisibilitiesAsArr(newlyVisible);
+        final NotificationVisibility[] noLongerVisibleAr = cloneVisibilitiesAsArr(noLongerVisible);
+
+        mUiOffloadThread.submit(() -> {
+            try {
+                mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
+            } catch (RemoteException e) {
+                // Ignore.
+            }
+
+            final int N = newlyVisible.size();
+            if (N > 0) {
+                String[] newlyVisibleKeyAr = new String[N];
+                for (int i = 0; i < N; i++) {
+                    newlyVisibleKeyAr[i] = newlyVisibleAr[i].key;
+                }
+
+                // TODO: Call NotificationEntryManager to do this, once it exists.
+                // TODO: Consider not catching all runtime exceptions here.
+                try {
+                    mNotificationListener.setNotificationsShown(newlyVisibleKeyAr);
+                } catch (RuntimeException e) {
+                    Log.d(TAG, "failed setNotificationsShown: ", e);
+                }
+            }
+            recycleAllVisibilityObjects(newlyVisibleAr);
+            recycleAllVisibilityObjects(noLongerVisibleAr);
+        });
+    }
+
+    private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
+        final int N = array.size();
+        for (int i = 0 ; i < N; i++) {
+            array.valueAt(i).recycle();
+        }
+        array.clear();
+    }
+
+    private void recycleAllVisibilityObjects(NotificationVisibility[] array) {
+        final int N = array.length;
+        for (int i = 0 ; i < N; i++) {
+            if (array[i] != null) {
+                array[i].recycle();
+            }
+        }
+    }
+
+    private NotificationVisibility[] cloneVisibilitiesAsArr(Collection<NotificationVisibility> c) {
+
+        final NotificationVisibility[] array = new NotificationVisibility[c.size()];
+        int i = 0;
+        for(NotificationVisibility nv: c) {
+            if (nv != null) {
+                array[i] = nv.clone();
+            }
+            i++;
+        }
+        return array;
+    }
+
+    @VisibleForTesting
+    public Runnable getVisibilityReporter() {
+        return mVisibilityReporter;
+    }
+
+    /**
+     * A listener that is notified when some child locations might have changed.
+     */
+    public interface OnChildLocationsChangedListener {
+        void onChildLocationsChanged();
+    }
+}
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
new file mode 100644
index 0000000..58db03c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -0,0 +1,1109 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.TimeAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.MathUtils;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.notification.FakeShadowView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.phone.DoubleTapHelper;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+
+/**
+ * Base class for both {@link ExpandableNotificationRow} and {@link NotificationShelf}
+ * to implement dimming/activating on Keyguard for the double-tap gesture
+ */
+public abstract class ActivatableNotificationView extends ExpandableOutlineView {
+
+    private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
+    private static final int ACTIVATE_ANIMATION_LENGTH = 220;
+    private static final long DARK_ANIMATION_LENGTH = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
+
+    /**
+     * The amount of width, which is kept in the end when performing a disappear animation (also
+     * the amount from which the horizontal appearing begins)
+     */
+    private static final float HORIZONTAL_COLLAPSED_REST_PARTIAL = 0.05f;
+
+    /**
+     * At which point from [0,1] does the horizontal collapse animation end (or start when
+     * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
+     */
+    private static final float HORIZONTAL_ANIMATION_END = 0.2f;
+
+    /**
+     * At which point from [0,1] does the alpha animation end (or start when
+     * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
+     */
+    private static final float ALPHA_ANIMATION_END = 0.0f;
+
+    /**
+     * At which point from [0,1] does the horizontal collapse animation start (or start when
+     * expanding)? 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
+     */
+    private static final float HORIZONTAL_ANIMATION_START = 1.0f;
+
+    /**
+     * At which point from [0,1] does the vertical collapse animation start (or end when
+     * expanding) 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
+     */
+    private static final float VERTICAL_ANIMATION_START = 1.0f;
+
+    /**
+     * Scale for the background to animate from when exiting dark mode.
+     */
+    private static final float DARK_EXIT_SCALE_START = 0.93f;
+
+    /**
+     * A sentinel value when no color should be used. Can be used with {@link #setTintColor(int)}
+     * or {@link #setOverrideTintColor(int, float)}.
+     */
+    protected static final int NO_COLOR = 0;
+
+    private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
+            = new PathInterpolator(0.6f, 0, 0.5f, 1);
+    private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
+            = new PathInterpolator(0, 0, 0.5f, 1);
+    private int mTintedRippleColor;
+    protected int mNormalRippleColor;
+    private final AccessibilityManager mAccessibilityManager;
+    private final DoubleTapHelper mDoubleTapHelper;
+
+    private boolean mDimmed;
+    private boolean mDark;
+
+    protected int mBgTint = NO_COLOR;
+    private float mBgAlpha = 1f;
+
+    /**
+     * Flag to indicate that the notification has been touched once and the second touch will
+     * click it.
+     */
+    private boolean mActivated;
+
+    private OnActivatedListener mOnActivatedListener;
+
+    private final Interpolator mSlowOutFastInInterpolator;
+    private final Interpolator mSlowOutLinearInInterpolator;
+    private Interpolator mCurrentAppearInterpolator;
+    private Interpolator mCurrentAlphaInterpolator;
+
+    protected NotificationBackgroundView mBackgroundNormal;
+    private NotificationBackgroundView mBackgroundDimmed;
+    private ObjectAnimator mBackgroundAnimator;
+    private RectF mAppearAnimationRect = new RectF();
+    private float mAnimationTranslationY;
+    private boolean mDrawingAppearAnimation;
+    private ValueAnimator mAppearAnimator;
+    private ValueAnimator mBackgroundColorAnimator;
+    private float mAppearAnimationFraction = -1.0f;
+    private float mAppearAnimationTranslation;
+    private int mNormalColor;
+    private boolean mIsBelowSpeedBump;
+    private FalsingManager mFalsingManager;
+
+    private float mNormalBackgroundVisibilityAmount;
+    private ValueAnimator mFadeInFromDarkAnimator;
+    private float mDimmedBackgroundFadeInAmount = -1;
+    private ValueAnimator.AnimatorUpdateListener mBackgroundVisibilityUpdater
+            = new ValueAnimator.AnimatorUpdateListener() {
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            setNormalBackgroundVisibilityAmount(mBackgroundNormal.getAlpha());
+            mDimmedBackgroundFadeInAmount = mBackgroundDimmed.getAlpha();
+        }
+    };
+    private AnimatorListenerAdapter mFadeInEndListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            super.onAnimationEnd(animation);
+            mFadeInFromDarkAnimator = null;
+            mDimmedBackgroundFadeInAmount = -1;
+            updateBackground();
+        }
+    };
+    private ValueAnimator.AnimatorUpdateListener mUpdateOutlineListener
+            = new ValueAnimator.AnimatorUpdateListener() {
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            updateOutlineAlpha();
+        }
+    };
+    private float mShadowAlpha = 1.0f;
+    private FakeShadowView mFakeShadow;
+    private int mCurrentBackgroundTint;
+    private int mTargetTint;
+    private int mStartTint;
+    private int mOverrideTint;
+    private float mOverrideAmount;
+    private boolean mShadowHidden;
+    /**
+     * Similar to mDimmed but is also true if it's not dimmable but should be
+     */
+    private boolean mNeedsDimming;
+    private int mDimmedAlpha;
+    private boolean mBlockNextTouch;
+    private boolean mIsHeadsUpAnimation;
+    private int mHeadsUpAddStartLocation;
+    private float mHeadsUpLocation;
+    private boolean mIsAppearing;
+
+    public ActivatableNotificationView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
+        mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
+        setClipChildren(false);
+        setClipToPadding(false);
+        updateColors();
+        mFalsingManager = FalsingManager.getInstance(context);
+        mAccessibilityManager = AccessibilityManager.getInstance(mContext);
+
+        mDoubleTapHelper = new DoubleTapHelper(this, (active) -> {
+            if (active) {
+                makeActive();
+            } else {
+                makeInactive(true /* animate */);
+            }
+        }, super::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
+        initDimens();
+    }
+
+    private void updateColors() {
+        mNormalColor = mContext.getColor(R.color.notification_material_background_color);
+        mTintedRippleColor = mContext.getColor(
+                R.color.notification_ripple_tinted_color);
+        mNormalRippleColor = mContext.getColor(
+                R.color.notification_ripple_untinted_color);
+        mDimmedAlpha = Color.alpha(mContext.getColor(
+                R.color.notification_material_background_dimmed_color));
+    }
+
+    private void initDimens() {
+        mHeadsUpAddStartLocation = getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_content_margin_start);
+    }
+
+    @Override
+    public void onDensityOrFontScaleChanged() {
+        super.onDensityOrFontScaleChanged();
+        initDimens();
+    }
+
+    public void onUiModeChanged() {
+        updateColors();
+        initBackground();
+        updateBackgroundTint();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mBackgroundNormal = findViewById(R.id.backgroundNormal);
+        mFakeShadow = findViewById(R.id.fake_shadow);
+        mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
+        mBackgroundDimmed = findViewById(R.id.backgroundDimmed);
+        initBackground();
+        updateBackground();
+        updateBackgroundTint();
+        updateOutlineAlpha();
+    }
+
+    /**
+     * Sets the custom backgrounds on {@link #mBackgroundNormal} and {@link #mBackgroundDimmed}.
+     * This method can also be used to reload the backgrounds on both of those views, which can
+     * be useful in a configuration change.
+     */
+    protected void initBackground() {
+        mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg);
+        mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim);
+    }
+
+    private final Runnable mTapTimeoutRunnable = new Runnable() {
+        @Override
+        public void run() {
+            makeInactive(true /* animate */);
+        }
+    };
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (mNeedsDimming && ev.getActionMasked() == MotionEvent.ACTION_DOWN
+                && disallowSingleClick(ev) && !isTouchExplorationEnabled()) {
+            if (!mActivated) {
+                return true;
+            } else if (!mDoubleTapHelper.isWithinDoubleTapSlop(ev)) {
+                mBlockNextTouch = true;
+                makeInactive(true /* animate */);
+                return true;
+            }
+        }
+        return super.onInterceptTouchEvent(ev);
+    }
+
+    private boolean isTouchExplorationEnabled() {
+        return mAccessibilityManager.isTouchExplorationEnabled();
+    }
+
+    protected boolean disallowSingleClick(MotionEvent ev) {
+        return false;
+    }
+
+    protected boolean handleSlideBack() {
+        return false;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        boolean result;
+        if (mBlockNextTouch) {
+            mBlockNextTouch = false;
+            return false;
+        }
+        if (mNeedsDimming && !isTouchExplorationEnabled() && isInteractive()) {
+            boolean wasActivated = mActivated;
+            result = handleTouchEventDimmed(event);
+            if (wasActivated && result && event.getAction() == MotionEvent.ACTION_UP) {
+                removeCallbacks(mTapTimeoutRunnable);
+            }
+        } else {
+            result = super.onTouchEvent(event);
+        }
+        return result;
+    }
+
+    /**
+     * @return whether this view is interactive and can be double tapped
+     */
+    protected boolean isInteractive() {
+        return true;
+    }
+
+    @Override
+    public void drawableHotspotChanged(float x, float y) {
+        if (!mDimmed){
+            mBackgroundNormal.drawableHotspotChanged(x, y);
+        }
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+        if (mDimmed) {
+            mBackgroundDimmed.setState(getDrawableState());
+        } else {
+            mBackgroundNormal.setState(getDrawableState());
+        }
+    }
+
+    public void setRippleAllowed(boolean allowed) {
+        mBackgroundNormal.setPressedAllowed(allowed);
+    }
+
+    private boolean handleTouchEventDimmed(MotionEvent event) {
+        if (mNeedsDimming && !mDimmed) {
+            // We're actually dimmed, but our content isn't dimmable, let's ensure we have a ripple
+            super.onTouchEvent(event);
+        }
+        return mDoubleTapHelper.onTouchEvent(event, getActualHeight());
+    }
+
+    @Override
+    public boolean performClick() {
+        if (!mNeedsDimming || isTouchExplorationEnabled()) {
+            return super.performClick();
+        }
+        return false;
+    }
+
+    private void makeActive() {
+        mFalsingManager.onNotificationActive();
+        startActivateAnimation(false /* reverse */);
+        mActivated = true;
+        if (mOnActivatedListener != null) {
+            mOnActivatedListener.onActivated(this);
+        }
+    }
+
+    private void startActivateAnimation(final boolean reverse) {
+        if (!isAttachedToWindow()) {
+            return;
+        }
+        if (!isDimmable()) {
+            return;
+        }
+        int widthHalf = mBackgroundNormal.getWidth()/2;
+        int heightHalf = mBackgroundNormal.getActualHeight()/2;
+        float radius = (float) Math.sqrt(widthHalf*widthHalf + heightHalf*heightHalf);
+        Animator animator;
+        if (reverse) {
+            animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
+                    widthHalf, heightHalf, radius, 0);
+        } else {
+            animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
+                    widthHalf, heightHalf, 0, radius);
+        }
+        mBackgroundNormal.setVisibility(View.VISIBLE);
+        Interpolator interpolator;
+        Interpolator alphaInterpolator;
+        if (!reverse) {
+            interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
+            alphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
+        } else {
+            interpolator = ACTIVATE_INVERSE_INTERPOLATOR;
+            alphaInterpolator = ACTIVATE_INVERSE_ALPHA_INTERPOLATOR;
+        }
+        animator.setInterpolator(interpolator);
+        animator.setDuration(ACTIVATE_ANIMATION_LENGTH);
+        if (reverse) {
+            mBackgroundNormal.setAlpha(1f);
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    updateBackground();
+                }
+            });
+            animator.start();
+        } else {
+            mBackgroundNormal.setAlpha(0.4f);
+            animator.start();
+        }
+        mBackgroundNormal.animate()
+                .alpha(reverse ? 0f : 1f)
+                .setInterpolator(alphaInterpolator)
+                .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                    @Override
+                    public void onAnimationUpdate(ValueAnimator animation) {
+                        float animatedFraction = animation.getAnimatedFraction();
+                        if (reverse) {
+                            animatedFraction = 1.0f - animatedFraction;
+                        }
+                        setNormalBackgroundVisibilityAmount(animatedFraction);
+                    }
+                })
+                .setDuration(ACTIVATE_ANIMATION_LENGTH);
+    }
+
+    /**
+     * Cancels the hotspot and makes the notification inactive.
+     */
+    public void makeInactive(boolean animate) {
+        if (mActivated) {
+            mActivated = false;
+            if (mDimmed) {
+                if (animate) {
+                    startActivateAnimation(true /* reverse */);
+                } else {
+                    updateBackground();
+                }
+            }
+        }
+        if (mOnActivatedListener != null) {
+            mOnActivatedListener.onActivationReset(this);
+        }
+        removeCallbacks(mTapTimeoutRunnable);
+    }
+
+    public void setDimmed(boolean dimmed, boolean fade) {
+        mNeedsDimming = dimmed;
+        dimmed &= isDimmable();
+        if (mDimmed != dimmed) {
+            mDimmed = dimmed;
+            resetBackgroundAlpha();
+            if (fade) {
+                fadeDimmedBackground();
+            } else {
+                updateBackground();
+            }
+        }
+    }
+
+    public boolean isDimmable() {
+        return true;
+    }
+
+    public void setDark(boolean dark, boolean fade, long delay) {
+        super.setDark(dark, fade, delay);
+        if (mDark == dark) {
+            return;
+        }
+        mDark = dark;
+        updateBackground();
+        updateBackgroundTint(false);
+        if (!dark && fade && !shouldHideBackground()) {
+            fadeInFromDark(delay);
+        }
+        updateOutlineAlpha();
+    }
+
+    private void updateOutlineAlpha() {
+        if (mDark) {
+            setOutlineAlpha(0f);
+            return;
+        }
+        float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
+        alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
+        alpha *= mShadowAlpha;
+        if (mFadeInFromDarkAnimator != null) {
+            alpha *= mFadeInFromDarkAnimator.getAnimatedFraction();
+        }
+        setOutlineAlpha(alpha);
+    }
+
+    public void setNormalBackgroundVisibilityAmount(float normalBackgroundVisibilityAmount) {
+        mNormalBackgroundVisibilityAmount = normalBackgroundVisibilityAmount;
+        updateOutlineAlpha();
+    }
+
+    @Override
+    public void setBelowSpeedBump(boolean below) {
+        super.setBelowSpeedBump(below);
+        if (below != mIsBelowSpeedBump) {
+            mIsBelowSpeedBump = below;
+            updateBackgroundTint();
+            onBelowSpeedBumpChanged();
+        }
+    }
+
+    protected void onBelowSpeedBumpChanged() {
+    }
+
+    /**
+     * @return whether we are below the speed bump
+     */
+    public boolean isBelowSpeedBump() {
+        return mIsBelowSpeedBump;
+    }
+
+    /**
+     * Sets the tint color of the background
+     */
+    public void setTintColor(int color) {
+        setTintColor(color, false);
+    }
+
+    /**
+     * Sets the tint color of the background
+     */
+    public void setTintColor(int color, boolean animated) {
+        if (color != mBgTint) {
+            mBgTint = color;
+            updateBackgroundTint(animated);
+        }
+    }
+
+    @Override
+    public void setDistanceToTopRoundness(float distanceToTopRoundness) {
+        super.setDistanceToTopRoundness(distanceToTopRoundness);
+        mBackgroundNormal.setDistanceToTopRoundness(distanceToTopRoundness);
+        mBackgroundDimmed.setDistanceToTopRoundness(distanceToTopRoundness);
+    }
+
+    /**
+     * Set an override tint color that is used for the background.
+     *
+     * @param color the color that should be used to tint the background.
+     *              This can be {@link #NO_COLOR} if the tint should be normally computed.
+     * @param overrideAmount a value from 0 to 1 how much the override tint should be used. The
+     *                       background color will then be the interpolation between this and the
+     *                       regular background color, where 1 means the overrideTintColor is fully
+     *                       used and the background color not at all.
+     */
+    public void setOverrideTintColor(int color, float overrideAmount) {
+        if (mDark) {
+            color = NO_COLOR;
+            overrideAmount = 0;
+        }
+        mOverrideTint = color;
+        mOverrideAmount = overrideAmount;
+        int newColor = calculateBgColor();
+        setBackgroundTintColor(newColor);
+        if (!isDimmable() && mNeedsDimming) {
+           mBackgroundNormal.setDrawableAlpha((int) NotificationUtils.interpolate(255,
+                   mDimmedAlpha,
+                   overrideAmount));
+        } else {
+            mBackgroundNormal.setDrawableAlpha(255);
+        }
+    }
+
+    protected void updateBackgroundTint() {
+        updateBackgroundTint(false /* animated */);
+    }
+
+    private void updateBackgroundTint(boolean animated) {
+        if (mBackgroundColorAnimator != null) {
+            mBackgroundColorAnimator.cancel();
+        }
+        int rippleColor = getRippleColor();
+        mBackgroundDimmed.setRippleColor(rippleColor);
+        mBackgroundNormal.setRippleColor(rippleColor);
+        int color = calculateBgColor();
+        if (!animated) {
+            setBackgroundTintColor(color);
+        } else if (color != mCurrentBackgroundTint) {
+            mStartTint = mCurrentBackgroundTint;
+            mTargetTint = color;
+            mBackgroundColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
+            mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    int newColor = NotificationUtils.interpolateColors(mStartTint, mTargetTint,
+                            animation.getAnimatedFraction());
+                    setBackgroundTintColor(newColor);
+                }
+            });
+            mBackgroundColorAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+            mBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
+            mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mBackgroundColorAnimator = null;
+                }
+            });
+            mBackgroundColorAnimator.start();
+        }
+    }
+
+    protected void setBackgroundTintColor(int color) {
+        if (color != mCurrentBackgroundTint) {
+            mCurrentBackgroundTint = color;
+            if (color == mNormalColor) {
+                // We don't need to tint a normal notification
+                color = 0;
+            }
+            mBackgroundDimmed.setTint(color);
+            mBackgroundNormal.setTint(color);
+        }
+    }
+
+    /**
+     * Fades in the background when exiting dark mode.
+     */
+    private void fadeInFromDark(long delay) {
+        final View background = mDimmed ? mBackgroundDimmed : mBackgroundNormal;
+        background.setAlpha(0f);
+        mBackgroundVisibilityUpdater.onAnimationUpdate(null);
+        background.animate()
+                .alpha(1f)
+                .setDuration(DARK_ANIMATION_LENGTH)
+                .setStartDelay(delay)
+                .setInterpolator(Interpolators.ALPHA_IN)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationCancel(Animator animation) {
+                        // Jump state if we are cancelled
+                        background.setAlpha(1f);
+                    }
+                })
+                .setUpdateListener(mBackgroundVisibilityUpdater)
+                .start();
+        mFadeInFromDarkAnimator = TimeAnimator.ofFloat(0.0f, 1.0f);
+        mFadeInFromDarkAnimator.setDuration(DARK_ANIMATION_LENGTH);
+        mFadeInFromDarkAnimator.setStartDelay(delay);
+        mFadeInFromDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        mFadeInFromDarkAnimator.addListener(mFadeInEndListener);
+        mFadeInFromDarkAnimator.addUpdateListener(mUpdateOutlineListener);
+        mFadeInFromDarkAnimator.start();
+    }
+
+    /**
+     * Fades the background when the dimmed state changes.
+     */
+    private void fadeDimmedBackground() {
+        mBackgroundDimmed.animate().cancel();
+        mBackgroundNormal.animate().cancel();
+        if (mActivated) {
+            updateBackground();
+            return;
+        }
+        if (!shouldHideBackground()) {
+            if (mDimmed) {
+                mBackgroundDimmed.setVisibility(View.VISIBLE);
+            } else {
+                mBackgroundNormal.setVisibility(View.VISIBLE);
+            }
+        }
+        float startAlpha = mDimmed ? 1f : 0;
+        float endAlpha = mDimmed ? 0 : 1f;
+        int duration = BACKGROUND_ANIMATION_LENGTH_MS;
+        // Check whether there is already a background animation running.
+        if (mBackgroundAnimator != null) {
+            startAlpha = (Float) mBackgroundAnimator.getAnimatedValue();
+            duration = (int) mBackgroundAnimator.getCurrentPlayTime();
+            mBackgroundAnimator.removeAllListeners();
+            mBackgroundAnimator.cancel();
+            if (duration <= 0) {
+                updateBackground();
+                return;
+            }
+        }
+        mBackgroundNormal.setAlpha(startAlpha);
+        mBackgroundAnimator =
+                ObjectAnimator.ofFloat(mBackgroundNormal, View.ALPHA, startAlpha, endAlpha);
+        mBackgroundAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mBackgroundAnimator.setDuration(duration);
+        mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                updateBackground();
+                mBackgroundAnimator = null;
+                if (mFadeInFromDarkAnimator == null) {
+                    mDimmedBackgroundFadeInAmount = -1;
+                }
+            }
+        });
+        mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater);
+        mBackgroundAnimator.start();
+    }
+
+    protected void updateBackgroundAlpha(float transformationAmount) {
+        mBgAlpha =  isChildInGroup() && mDimmed ? transformationAmount : 1f;
+        if (mDimmedBackgroundFadeInAmount != -1) {
+            mBgAlpha *= mDimmedBackgroundFadeInAmount;
+        }
+        mBackgroundDimmed.setAlpha(mBgAlpha);
+    }
+
+    protected void resetBackgroundAlpha() {
+        updateBackgroundAlpha(0f /* transformationAmount */);
+    }
+
+    protected void updateBackground() {
+        cancelFadeAnimations();
+        if (shouldHideBackground()) {
+            mBackgroundDimmed.setVisibility(INVISIBLE);
+            mBackgroundNormal.setVisibility(mActivated ? VISIBLE : INVISIBLE);
+        } else if (mDimmed) {
+            // When groups are animating to the expanded state from the lockscreen, show the
+            // normal background instead of the dimmed background
+            final boolean dontShowDimmed = isGroupExpansionChanging() && isChildInGroup();
+            mBackgroundDimmed.setVisibility(dontShowDimmed ? View.INVISIBLE : View.VISIBLE);
+            mBackgroundNormal.setVisibility((mActivated || dontShowDimmed)
+                    ? View.VISIBLE
+                    : View.INVISIBLE);
+        } else {
+            mBackgroundDimmed.setVisibility(View.INVISIBLE);
+            mBackgroundNormal.setVisibility(View.VISIBLE);
+            mBackgroundNormal.setAlpha(1f);
+            removeCallbacks(mTapTimeoutRunnable);
+            // make in inactive to avoid it sticking around active
+            makeInactive(false /* animate */);
+        }
+        setNormalBackgroundVisibilityAmount(
+                mBackgroundNormal.getVisibility() == View.VISIBLE ? 1.0f : 0.0f);
+    }
+
+    protected void updateBackgroundClipping() {
+        mBackgroundNormal.setBottomAmountClips(!isChildInGroup());
+        mBackgroundDimmed.setBottomAmountClips(!isChildInGroup());
+    }
+
+    protected boolean shouldHideBackground() {
+        return mDark;
+    }
+
+    private void cancelFadeAnimations() {
+        if (mBackgroundAnimator != null) {
+            mBackgroundAnimator.cancel();
+        }
+        mBackgroundDimmed.animate().cancel();
+        mBackgroundNormal.animate().cancel();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        setPivotX(getWidth() / 2);
+    }
+
+    @Override
+    public void setActualHeight(int actualHeight, boolean notifyListeners) {
+        super.setActualHeight(actualHeight, notifyListeners);
+        setPivotY(actualHeight / 2);
+        mBackgroundNormal.setActualHeight(actualHeight);
+        mBackgroundDimmed.setActualHeight(actualHeight);
+    }
+
+    @Override
+    public void setClipTopAmount(int clipTopAmount) {
+        super.setClipTopAmount(clipTopAmount);
+        mBackgroundNormal.setClipTopAmount(clipTopAmount);
+        mBackgroundDimmed.setClipTopAmount(clipTopAmount);
+    }
+
+    @Override
+    public void setClipBottomAmount(int clipBottomAmount) {
+        super.setClipBottomAmount(clipBottomAmount);
+        mBackgroundNormal.setClipBottomAmount(clipBottomAmount);
+        mBackgroundDimmed.setClipBottomAmount(clipBottomAmount);
+    }
+
+    @Override
+    public void performRemoveAnimation(long duration, long delay,
+            float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+            Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
+        enableAppearDrawing(true);
+        mIsHeadsUpAnimation = isHeadsUpAnimation;
+        mHeadsUpLocation = endLocation;
+        if (mDrawingAppearAnimation) {
+            startAppearAnimation(false /* isAppearing */, translationDirection,
+                    delay, duration, onFinishedRunnable, animationListener);
+        } else if (onFinishedRunnable != null) {
+            onFinishedRunnable.run();
+        }
+    }
+
+    @Override
+    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
+        enableAppearDrawing(true);
+        mIsHeadsUpAnimation = isHeadsUpAppear;
+        mHeadsUpLocation = mHeadsUpAddStartLocation;
+        if (mDrawingAppearAnimation) {
+            startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
+                    duration, null, null);
+        }
+    }
+
+    private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
+            long duration, final Runnable onFinishedRunnable,
+            AnimatorListenerAdapter animationListener) {
+        cancelAppearAnimation();
+        mAnimationTranslationY = translationDirection * getActualHeight();
+        if (mAppearAnimationFraction == -1.0f) {
+            // not initialized yet, we start anew
+            if (isAppearing) {
+                mAppearAnimationFraction = 0.0f;
+                mAppearAnimationTranslation = mAnimationTranslationY;
+            } else {
+                mAppearAnimationFraction = 1.0f;
+                mAppearAnimationTranslation = 0;
+            }
+        }
+        mIsAppearing = isAppearing;
+
+        float targetValue;
+        if (isAppearing) {
+            mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
+            mCurrentAlphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
+            targetValue = 1.0f;
+        } else {
+            mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
+            mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
+            targetValue = 0.0f;
+        }
+        mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
+                targetValue);
+        mAppearAnimator.setInterpolator(Interpolators.LINEAR);
+        mAppearAnimator.setDuration(
+                (long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
+        mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                mAppearAnimationFraction = (float) animation.getAnimatedValue();
+                updateAppearAnimationAlpha();
+                updateAppearRect();
+                invalidate();
+            }
+        });
+        if (animationListener != null) {
+            mAppearAnimator.addListener(animationListener);
+        }
+        if (delay > 0) {
+            // we need to apply the initial state already to avoid drawn frames in the wrong state
+            updateAppearAnimationAlpha();
+            updateAppearRect();
+            mAppearAnimator.setStartDelay(delay);
+        }
+        mAppearAnimator.addListener(new AnimatorListenerAdapter() {
+            private boolean mWasCancelled;
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (onFinishedRunnable != null) {
+                    onFinishedRunnable.run();
+                }
+                if (!mWasCancelled) {
+                    enableAppearDrawing(false);
+                    onAppearAnimationFinished(isAppearing);
+                }
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mWasCancelled = false;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mWasCancelled = true;
+            }
+        });
+        mAppearAnimator.start();
+    }
+
+    protected void onAppearAnimationFinished(boolean wasAppearing) {
+    }
+
+    private void cancelAppearAnimation() {
+        if (mAppearAnimator != null) {
+            mAppearAnimator.cancel();
+            mAppearAnimator = null;
+        }
+    }
+
+    public void cancelAppearDrawing() {
+        cancelAppearAnimation();
+        enableAppearDrawing(false);
+    }
+
+    private void updateAppearRect() {
+        float inverseFraction = (1.0f - mAppearAnimationFraction);
+        float translationFraction = mCurrentAppearInterpolator.getInterpolation(inverseFraction);
+        float translateYTotalAmount = translationFraction * mAnimationTranslationY;
+        mAppearAnimationTranslation = translateYTotalAmount;
+
+        // handle width animation
+        float widthFraction = (inverseFraction - (1.0f - HORIZONTAL_ANIMATION_START))
+                / (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
+        widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
+        widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
+        float startWidthFraction = HORIZONTAL_COLLAPSED_REST_PARTIAL;
+        if (mIsHeadsUpAnimation && !mIsAppearing) {
+            startWidthFraction = 0;
+        }
+        float width = MathUtils.lerp(startWidthFraction, 1.0f, 1.0f - widthFraction)
+                        * getWidth();
+        float left;
+        float right;
+        if (mIsHeadsUpAnimation) {
+            left = MathUtils.lerp(mHeadsUpLocation, 0, 1.0f - widthFraction);
+            right = left + width;
+        } else {
+            left = getWidth() * 0.5f - width / 2.0f;
+            right = getWidth() - left;
+        }
+
+        // handle top animation
+        float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
+                VERTICAL_ANIMATION_START;
+        heightFraction = Math.max(0.0f, heightFraction);
+        heightFraction = mCurrentAppearInterpolator.getInterpolation(heightFraction);
+
+        float top;
+        float bottom;
+        final int actualHeight = getActualHeight();
+        if (mAnimationTranslationY > 0.0f) {
+            bottom = actualHeight - heightFraction * mAnimationTranslationY * 0.1f
+                    - translateYTotalAmount;
+            top = bottom * heightFraction;
+        } else {
+            top = heightFraction * (actualHeight + mAnimationTranslationY) * 0.1f -
+                    translateYTotalAmount;
+            bottom = actualHeight * (1 - heightFraction) + top * heightFraction;
+        }
+        mAppearAnimationRect.set(left, top, right, bottom);
+        setOutlineRect(left, top + mAppearAnimationTranslation, right,
+                bottom + mAppearAnimationTranslation);
+    }
+
+    private void updateAppearAnimationAlpha() {
+        float contentAlphaProgress = mAppearAnimationFraction;
+        contentAlphaProgress = contentAlphaProgress / (1.0f - ALPHA_ANIMATION_END);
+        contentAlphaProgress = Math.min(1.0f, contentAlphaProgress);
+        contentAlphaProgress = mCurrentAlphaInterpolator.getInterpolation(contentAlphaProgress);
+        setContentAlpha(contentAlphaProgress);
+    }
+
+    private void setContentAlpha(float contentAlpha) {
+        View contentView = getContentView();
+        if (contentView.hasOverlappingRendering()) {
+            int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE
+                    : LAYER_TYPE_HARDWARE;
+            int currentLayerType = contentView.getLayerType();
+            if (currentLayerType != layerType) {
+                contentView.setLayerType(layerType, null);
+            }
+        }
+        contentView.setAlpha(contentAlpha);
+    }
+
+    @Override
+    protected void applyRoundness() {
+        super.applyRoundness();
+        applyBackgroundRoundness(getCurrentBackgroundRadiusTop(),
+                getCurrentBackgroundRadiusBottom());
+    }
+
+    protected void applyBackgroundRoundness(float topRadius, float bottomRadius) {
+        mBackgroundDimmed.setRoundness(topRadius, bottomRadius);
+        mBackgroundNormal.setRoundness(topRadius, bottomRadius);
+    }
+
+    @Override
+    protected void setBackgroundTop(int backgroundTop) {
+        mBackgroundDimmed.setBackgroundTop(backgroundTop);
+        mBackgroundNormal.setBackgroundTop(backgroundTop);
+    }
+
+    protected abstract View getContentView();
+
+    public int calculateBgColor() {
+        return calculateBgColor(true /* withTint */, true /* withOverRide */);
+    }
+
+    @Override
+    protected boolean childNeedsClipping(View child) {
+        if (child instanceof NotificationBackgroundView && isClippingNeeded()) {
+            return true;
+        }
+        return super.childNeedsClipping(child);
+    }
+
+    /**
+     * @param withTint should a possible tint be factored in?
+     * @param withOverRide should the value be interpolated with {@link #mOverrideTint}
+     * @return the calculated background color
+     */
+    private int calculateBgColor(boolean withTint, boolean withOverRide) {
+        if (withTint && mDark) {
+            return getContext().getColor(R.color.notification_material_background_dark_color);
+        }
+        if (withOverRide && mOverrideTint != NO_COLOR) {
+            int defaultTint = calculateBgColor(withTint, false);
+            return NotificationUtils.interpolateColors(defaultTint, mOverrideTint, mOverrideAmount);
+        }
+        if (withTint && mBgTint != NO_COLOR) {
+            return mBgTint;
+        } else {
+            return mNormalColor;
+        }
+    }
+
+    protected int getRippleColor() {
+        if (mBgTint != 0) {
+            return mTintedRippleColor;
+        } else {
+            return mNormalRippleColor;
+        }
+    }
+
+    /**
+     * When we draw the appear animation, we render the view in a bitmap and render this bitmap
+     * as a shader of a rect. This call creates the Bitmap and switches the drawing mode,
+     * such that the normal drawing of the views does not happen anymore.
+     *
+     * @param enable Should it be enabled.
+     */
+    private void enableAppearDrawing(boolean enable) {
+        if (enable != mDrawingAppearAnimation) {
+            mDrawingAppearAnimation = enable;
+            if (!enable) {
+                setContentAlpha(1.0f);
+                mAppearAnimationFraction = -1;
+                setOutlineRect(null);
+            }
+            invalidate();
+        }
+    }
+
+    public boolean isDrawingAppearAnimation() {
+        return mDrawingAppearAnimation;
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        if (mDrawingAppearAnimation) {
+            canvas.save();
+            canvas.translate(0, mAppearAnimationTranslation);
+        }
+        super.dispatchDraw(canvas);
+        if (mDrawingAppearAnimation) {
+            canvas.restore();
+        }
+    }
+
+    public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
+        mOnActivatedListener = onActivatedListener;
+    }
+
+    public boolean hasSameBgColor(ActivatableNotificationView otherView) {
+        return calculateBgColor() == otherView.calculateBgColor();
+    }
+
+    @Override
+    public float getShadowAlpha() {
+        return mShadowAlpha;
+    }
+
+    @Override
+    public void setShadowAlpha(float shadowAlpha) {
+        if (shadowAlpha != mShadowAlpha) {
+            mShadowAlpha = shadowAlpha;
+            updateOutlineAlpha();
+        }
+    }
+
+    @Override
+    public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
+            int outlineTranslation) {
+        boolean hiddenBefore = mShadowHidden;
+        mShadowHidden = shadowIntensity == 0.0f;
+        if (!mShadowHidden || !hiddenBefore) {
+            mFakeShadow.setFakeShadowTranslationZ(shadowIntensity * (getTranslationZ()
+                            + FakeShadowView.SHADOW_SIBLING_TRESHOLD), outlineAlpha, shadowYEnd,
+                    outlineTranslation);
+        }
+    }
+
+    public int getBackgroundColorWithoutTint() {
+        return calculateBgColor(false /* withTint */, false /* withOverride */);
+    }
+
+    public boolean isPinned() {
+        return false;
+    }
+
+    public boolean isHeadsUpAnimatingAway() {
+        return false;
+    }
+
+    public interface OnActivatedListener {
+        void onActivated(ActivatableNotificationView view);
+        void onActivationReset(ActivatableNotificationView view);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
new file mode 100644
index 0000000..10fc990
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
@@ -0,0 +1,205 @@
+/*
+ * 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.row;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+
+/**
+ * The guts of a notification revealed when performing a long press.
+ */
+public class AppOpsInfo extends LinearLayout implements NotificationGuts.GutsContent {
+    private static final String TAG = "AppOpsGuts";
+
+    private PackageManager mPm;
+
+    private String mPkg;
+    private String mAppName;
+    private int mAppUid;
+    private StatusBarNotification mSbn;
+    private ArraySet<Integer> mAppOps;
+    private MetricsLogger mMetricsLogger;
+    private OnSettingsClickListener mOnSettingsClickListener;
+    private NotificationGuts mGutsContainer;
+
+    private OnClickListener mOnOk = v -> {
+        closeControls(v);
+    };
+
+    public AppOpsInfo(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public interface OnSettingsClickListener {
+        void onClick(View v, String pkg, int uid, ArraySet<Integer> ops);
+    }
+
+    public void bindGuts(final PackageManager pm,
+            final OnSettingsClickListener onSettingsClick,
+            final StatusBarNotification sbn,
+            ArraySet<Integer> activeOps) {
+        mPkg = sbn.getPackageName();
+        mSbn = sbn;
+        mPm = pm;
+        mAppName = mPkg;
+        mOnSettingsClickListener = onSettingsClick;
+        mAppOps = activeOps;
+
+        bindHeader();
+        bindPrompt();
+        bindButtons();
+
+        mMetricsLogger = new MetricsLogger();
+        mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, true);
+    }
+
+    private void bindHeader() {
+        // Package name
+        Drawable pkgicon = null;
+        ApplicationInfo info;
+        try {
+            info = mPm.getApplicationInfo(mPkg,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                            | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+            if (info != null) {
+                mAppUid = mSbn.getUid();
+                mAppName = String.valueOf(mPm.getApplicationLabel(info));
+                pkgicon = mPm.getApplicationIcon(info);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // app is gone, just show package name and generic icon
+            pkgicon = mPm.getDefaultActivityIcon();
+        }
+        ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
+        ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
+    }
+
+    private void bindPrompt() {
+        final TextView prompt = findViewById(R.id.prompt);
+        prompt.setText(getPrompt());
+    }
+
+    private void bindButtons() {
+        View settings =  findViewById(R.id.settings);
+        settings.setOnClickListener((View view) -> {
+            mOnSettingsClickListener.onClick(view, mPkg, mAppUid, mAppOps);
+        });
+        TextView ok = findViewById(R.id.ok);
+        ok.setOnClickListener(mOnOk);
+    }
+
+    private String getPrompt() {
+        if (mAppOps == null || mAppOps.size() == 0) {
+            return "";
+        } else if (mAppOps.size() == 1) {
+            if (mAppOps.contains(AppOpsManager.OP_CAMERA)) {
+                return mContext.getString(R.string.appops_camera);
+            } else if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) {
+                return mContext.getString(R.string.appops_microphone);
+            } else {
+                return mContext.getString(R.string.appops_overlay);
+            }
+        } else if (mAppOps.size() == 2) {
+            if (mAppOps.contains(AppOpsManager.OP_CAMERA)) {
+                if (mAppOps.contains(AppOpsManager.OP_RECORD_AUDIO)) {
+                    return mContext.getString(R.string.appops_camera_mic);
+                } else {
+                    return mContext.getString(R.string.appops_camera_overlay);
+                }
+            } else {
+                return mContext.getString(R.string.appops_mic_overlay);
+            }
+        } else {
+            return mContext.getString(R.string.appops_camera_mic_overlay);
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        if (mGutsContainer != null &&
+                event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+            if (mGutsContainer.isExposed()) {
+                event.getText().add(mContext.getString(
+                        R.string.notification_channel_controls_opened_accessibility, mAppName));
+            } else {
+                event.getText().add(mContext.getString(
+                        R.string.notification_channel_controls_closed_accessibility, mAppName));
+            }
+        }
+    }
+
+    private void closeControls(View v) {
+        mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false);
+        int[] parentLoc = new int[2];
+        int[] targetLoc = new int[2];
+        mGutsContainer.getLocationOnScreen(parentLoc);
+        v.getLocationOnScreen(targetLoc);
+        final int centerX = v.getWidth() / 2;
+        final int centerY = v.getHeight() / 2;
+        final int x = targetLoc[0] - parentLoc[0] + centerX;
+        final int y = targetLoc[1] - parentLoc[1] + centerY;
+        mGutsContainer.closeControls(x, y, false, false);
+    }
+
+    @Override
+    public void setGutsParent(NotificationGuts guts) {
+        mGutsContainer = guts;
+    }
+
+    @Override
+    public boolean willBeRemoved() {
+        return false;
+    }
+
+    @Override
+    public boolean shouldBeSaved() {
+        return false;
+    }
+
+    @Override
+    public View getContentView() {
+        return this;
+    }
+
+    @Override
+    public boolean handleCloseControls(boolean save, boolean force) {
+        return false;
+    }
+
+    @Override
+    public int getActualHeight() {
+        return getHeight();
+    }
+}
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
new file mode 100644
index 0000000..67967d4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -0,0 +1,3008 @@
+/*
+ * Copyright (C) 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
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Path;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.SystemClock;
+import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.Log;
+import android.util.MathUtils;
+import android.util.Property;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.NotificationHeaderView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.widget.Chronometer;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.ContrastColorUtil;
+import com.android.internal.widget.CachingIconView;
+import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.RemoteInputController;
+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.logging.NotificationCounters;
+import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
+import com.android.systemui.statusbar.notification.stack.StackScrollState;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+
+/**
+ * View representing a notification item - this can be either the individual child notification or
+ * the group summary (which contains 1 or more child notifications).
+ */
+public class ExpandableNotificationRow extends ActivatableNotificationView
+        implements PluginListener<NotificationMenuRowPlugin> {
+
+    private static final boolean DEBUG = false;
+    private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
+    private static final int COLORED_DIVIDER_ALPHA = 0x7B;
+    private static final int MENU_VIEW_INDEX = 0;
+    private static final String TAG = "ExpandableNotifRow";
+    public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
+
+    /**
+     * Listener for when {@link ExpandableNotificationRow} is laid out.
+     */
+    public interface LayoutListener {
+        void onLayout();
+    }
+
+    private LayoutListener mLayoutListener;
+    private boolean mDark;
+    private boolean mLowPriorityStateUpdated;
+    private final NotificationInflater mNotificationInflater;
+    private int mIconTransformContentShift;
+    private int mIconTransformContentShiftNoIcon;
+    private int mMaxHeadsUpHeightBeforeN;
+    private int mMaxHeadsUpHeightBeforeP;
+    private int mMaxHeadsUpHeight;
+    private int mMaxHeadsUpHeightIncreased;
+    private int mNotificationMinHeightBeforeN;
+    private int mNotificationMinHeightBeforeP;
+    private int mNotificationMinHeight;
+    private int mNotificationMinHeightLarge;
+    private int mNotificationMaxHeight;
+    private int mNotificationAmbientHeight;
+    private int mIncreasedPaddingBetweenElements;
+    private int mNotificationLaunchHeight;
+    private boolean mMustStayOnScreen;
+
+    /** Does this row contain layouts that can adapt to row expansion */
+    private boolean mExpandable;
+    /** Has the user actively changed the expansion state of this row */
+    private boolean mHasUserChangedExpansion;
+    /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
+    private boolean mUserExpanded;
+    /** Whether the blocking helper is showing on this notification (even if dismissed) */
+    private boolean mIsBlockingHelperShowing;
+
+    /**
+     * Has this notification been expanded while it was pinned
+     */
+    private boolean mExpandedWhenPinned;
+    /** Is the user touching this row */
+    private boolean mUserLocked;
+    /** Are we showing the "public" version */
+    private boolean mShowingPublic;
+    private boolean mSensitive;
+    private boolean mSensitiveHiddenInGeneral;
+    private boolean mShowingPublicInitialized;
+    private boolean mHideSensitiveForIntrinsicHeight;
+    private float mHeaderVisibleAmount = DEFAULT_HEADER_VISIBLE_AMOUNT;
+
+    /**
+     * Is this notification expanded by the system. The expansion state can be overridden by the
+     * user expansion.
+     */
+    private boolean mIsSystemExpanded;
+
+    /**
+     * Whether the notification is on the keyguard and the expansion is disabled.
+     */
+    private boolean mOnKeyguard;
+
+    private Animator mTranslateAnim;
+    private ArrayList<View> mTranslateableViews;
+    private NotificationContentView mPublicLayout;
+    private NotificationContentView mPrivateLayout;
+    private NotificationContentView[] mLayouts;
+    private int mNotificationColor;
+    private ExpansionLogger mLogger;
+    private String mLoggingKey;
+    private NotificationGuts mGuts;
+    private NotificationData.Entry mEntry;
+    private StatusBarNotification mStatusBarNotification;
+    private String mAppName;
+    private boolean mIsHeadsUp;
+    private boolean mLastChronometerRunning = true;
+    private ViewStub mChildrenContainerStub;
+    private NotificationGroupManager mGroupManager;
+    private boolean mChildrenExpanded;
+    private boolean mIsSummaryWithChildren;
+    private NotificationChildrenContainer mChildrenContainer;
+    private NotificationMenuRowPlugin mMenuRow;
+    private ViewStub mGutsStub;
+    private boolean mIsSystemChildExpanded;
+    private boolean mIsPinned;
+    private FalsingManager mFalsingManager;
+    private boolean mExpandAnimationRunning;
+    private AboveShelfChangedListener mAboveShelfChangedListener;
+    private HeadsUpManager mHeadsUpManager;
+    private Consumer<Boolean> mHeadsUpAnimatingAwayListener;
+    private boolean mChildIsExpanding;
+
+    private boolean mJustClicked;
+    private boolean mIconAnimationRunning;
+    private boolean mShowNoBackground;
+    private ExpandableNotificationRow mNotificationParent;
+    private OnExpandClickListener mOnExpandClickListener;
+    private View.OnClickListener mOnAppOpsClickListener;
+
+    // Listener will be called when receiving a long click event.
+    // Use #setLongPressPosition to optionally assign positional data with the long press.
+    private LongPressListener mLongPressListener;
+
+    private boolean mGroupExpansionChanging;
+
+    /**
+     * A supplier that returns true if keyguard is secure.
+     */
+    private BooleanSupplier mSecureStateProvider;
+
+    /**
+     * Whether or not a notification that is not part of a group of notifications can be manually
+     * expanded by the user.
+     */
+    private boolean mEnableNonGroupedNotificationExpand;
+
+    /**
+     * Whether or not to update the background of the header of the notification when its expanded.
+     * If {@code true}, the header background will disappear when expanded.
+     */
+    private boolean mShowGroupBackgroundWhenExpanded;
+
+    private OnClickListener mExpandClickListener = new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            if (!shouldShowPublic() && (!mIsLowPriority || isExpanded())
+                    && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
+                mGroupExpansionChanging = true;
+                final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
+                boolean nowExpanded = mGroupManager.toggleGroupExpansion(mStatusBarNotification);
+                mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
+                MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER,
+                        nowExpanded);
+                onExpansionChanged(true /* userAction */, wasExpanded);
+            } else if (mEnableNonGroupedNotificationExpand) {
+                if (v.isAccessibilityFocused()) {
+                    mPrivateLayout.setFocusOnVisibilityChange();
+                }
+                boolean nowExpanded;
+                if (isPinned()) {
+                    nowExpanded = !mExpandedWhenPinned;
+                    mExpandedWhenPinned = nowExpanded;
+                } else {
+                    nowExpanded = !isExpanded();
+                    setUserExpanded(nowExpanded);
+                }
+                notifyHeightChanged(true);
+                mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
+                MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_EXPANDER,
+                        nowExpanded);
+            }
+        }
+    };
+    private boolean mForceUnlocked;
+    private boolean mDismissed;
+    private boolean mKeepInParent;
+    private boolean mRemoved;
+    private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
+            new FloatProperty<ExpandableNotificationRow>("translate") {
+                @Override
+                public void setValue(ExpandableNotificationRow object, float value) {
+                    object.setTranslation(value);
+                }
+
+                @Override
+                public Float get(ExpandableNotificationRow object) {
+                    return object.getTranslation();
+                }
+            };
+    private OnClickListener mOnClickListener;
+    private boolean mHeadsupDisappearRunning;
+    private View mChildAfterViewWhenDismissed;
+    private View mGroupParentWhenDismissed;
+    private boolean mRefocusOnDismiss;
+    private float mContentTransformationAmount;
+    private boolean mIconsVisible = true;
+    private boolean mAboveShelf;
+    private boolean mShowAmbient;
+    private boolean mIsLastChild;
+    private Runnable mOnDismissRunnable;
+    private boolean mIsLowPriority;
+    private boolean mIsColorized;
+    private boolean mUseIncreasedCollapsedHeight;
+    private boolean mUseIncreasedHeadsUpHeight;
+    private float mTranslationWhenRemoved;
+    private boolean mWasChildInGroupWhenRemoved;
+    private int mNotificationColorAmbient;
+    private NotificationViewState mNotificationViewState;
+
+    private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
+            new SystemNotificationAsyncTask();
+
+    /**
+     * Returns whether the given {@code statusBarNotification} is a system notification.
+     * <b>Note</b>, this should be run in the background thread if possible as it makes multiple IPC
+     * calls.
+     */
+    private static Boolean isSystemNotification(
+            Context context, StatusBarNotification statusBarNotification) {
+        PackageManager packageManager = StatusBar.getPackageManagerForUser(
+                context, statusBarNotification.getUser().getIdentifier());
+        Boolean isSystemNotification = null;
+
+        try {
+            PackageInfo packageInfo = packageManager.getPackageInfo(
+                    statusBarNotification.getPackageName(), PackageManager.GET_SIGNATURES);
+
+            isSystemNotification =
+                    com.android.settingslib.Utils.isSystemPackage(
+                            context.getResources(), packageManager, packageInfo);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "cacheIsSystemNotification: Could not find package info");
+        }
+        return isSystemNotification;
+    }
+
+    @Override
+    public boolean isGroupExpansionChanging() {
+        if (isChildInGroup()) {
+            return mNotificationParent.isGroupExpansionChanging();
+        }
+        return mGroupExpansionChanging;
+    }
+
+    public void setGroupExpansionChanging(boolean changing) {
+        mGroupExpansionChanging = changing;
+    }
+
+    @Override
+    public void setActualHeightAnimating(boolean animating) {
+        if (mPrivateLayout != null) {
+            mPrivateLayout.setContentHeightAnimating(animating);
+        }
+    }
+
+    public NotificationContentView getPrivateLayout() {
+        return mPrivateLayout;
+    }
+
+    public NotificationContentView getPublicLayout() {
+        return mPublicLayout;
+    }
+
+    public void setIconAnimationRunning(boolean running) {
+        for (NotificationContentView l : mLayouts) {
+            setIconAnimationRunning(running, l);
+        }
+        if (mIsSummaryWithChildren) {
+            setIconAnimationRunningForChild(running, mChildrenContainer.getHeaderView());
+            setIconAnimationRunningForChild(running, mChildrenContainer.getLowPriorityHeaderView());
+            List<ExpandableNotificationRow> notificationChildren =
+                    mChildrenContainer.getNotificationChildren();
+            for (int i = 0; i < notificationChildren.size(); i++) {
+                ExpandableNotificationRow child = notificationChildren.get(i);
+                child.setIconAnimationRunning(running);
+            }
+        }
+        mIconAnimationRunning = running;
+    }
+
+    private void setIconAnimationRunning(boolean running, NotificationContentView layout) {
+        if (layout != null) {
+            View contractedChild = layout.getContractedChild();
+            View expandedChild = layout.getExpandedChild();
+            View headsUpChild = layout.getHeadsUpChild();
+            setIconAnimationRunningForChild(running, contractedChild);
+            setIconAnimationRunningForChild(running, expandedChild);
+            setIconAnimationRunningForChild(running, headsUpChild);
+        }
+    }
+
+    private void setIconAnimationRunningForChild(boolean running, View child) {
+        if (child != null) {
+            ImageView icon = (ImageView) child.findViewById(com.android.internal.R.id.icon);
+            setIconRunning(icon, running);
+            ImageView rightIcon = (ImageView) child.findViewById(
+                    com.android.internal.R.id.right_icon);
+            setIconRunning(rightIcon, running);
+        }
+    }
+
+    private void setIconRunning(ImageView imageView, boolean running) {
+        if (imageView != null) {
+            Drawable drawable = imageView.getDrawable();
+            if (drawable instanceof AnimationDrawable) {
+                AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
+                if (running) {
+                    animationDrawable.start();
+                } else {
+                    animationDrawable.stop();
+                }
+            } else if (drawable instanceof AnimatedVectorDrawable) {
+                AnimatedVectorDrawable animationDrawable = (AnimatedVectorDrawable) drawable;
+                if (running) {
+                    animationDrawable.start();
+                } else {
+                    animationDrawable.stop();
+                }
+            }
+        }
+    }
+
+    public void updateNotification(NotificationData.Entry entry) {
+        mEntry = entry;
+        mStatusBarNotification = entry.notification;
+        mNotificationInflater.inflateNotificationViews();
+
+        cacheIsSystemNotification();
+    }
+
+    /**
+     * Caches whether or not this row contains a system notification. Note, this is only cached
+     * once per notification as the packageInfo can't technically change for a notification row.
+     */
+    private void cacheIsSystemNotification() {
+        if (mEntry != null && mEntry.mIsSystemNotification == null) {
+            if (mSystemNotificationAsyncTask.getStatus() == AsyncTask.Status.PENDING) {
+                // Run async task once, only if it hasn't already been executed. Note this is
+                // executed in serial - no need to parallelize this small task.
+                mSystemNotificationAsyncTask.execute();
+            }
+        }
+    }
+
+    /**
+     * Returns whether this row is considered non-blockable (i.e. it's a non-blockable system notif
+     * or is in a whitelist).
+     */
+    public boolean getIsNonblockable() {
+        boolean isNonblockable = Dependency.get(NotificationBlockingHelperManager.class)
+                .isNonblockable(mStatusBarNotification.getPackageName(),
+                        mEntry.channel.getId());
+
+        // If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once
+        // again, but in-place on the main thread this time. This should rarely ever get called.
+        if (mEntry != null && mEntry.mIsSystemNotification == null) {
+            if (DEBUG) {
+                Log.d(TAG, "Retrieving isSystemNotification on main thread");
+            }
+            mSystemNotificationAsyncTask.cancel(true /* mayInterruptIfRunning */);
+            mEntry.mIsSystemNotification = isSystemNotification(mContext, mStatusBarNotification);
+        }
+
+        if (!isNonblockable && mEntry != null && mEntry.mIsSystemNotification != null) {
+            if (mEntry.mIsSystemNotification) {
+                if (mEntry.channel != null
+                        && !mEntry.channel.isBlockableSystem()) {
+                    isNonblockable = true;
+                }
+            }
+        }
+        return isNonblockable;
+    }
+
+    public void onNotificationUpdated() {
+        for (NotificationContentView l : mLayouts) {
+            l.onNotificationUpdated(mEntry);
+        }
+        mIsColorized = mStatusBarNotification.getNotification().isColorized();
+        mShowingPublicInitialized = false;
+        updateNotificationColor();
+        if (mMenuRow != null) {
+            mMenuRow.onNotificationUpdated(mStatusBarNotification);
+            mMenuRow.setAppName(mAppName);
+        }
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.recreateNotificationHeader(mExpandClickListener);
+            mChildrenContainer.onNotificationUpdated();
+        }
+        if (mIconAnimationRunning) {
+            setIconAnimationRunning(true);
+        }
+        if (mNotificationParent != null) {
+            mNotificationParent.updateChildrenHeaderAppearance();
+        }
+        onChildrenCountChanged();
+        // The public layouts expand button is always visible
+        mPublicLayout.updateExpandButtons(true);
+        updateLimits();
+        updateIconVisibilities();
+        updateShelfIconColor();
+        updateRippleAllowed();
+    }
+
+    @VisibleForTesting
+    void updateShelfIconColor() {
+        StatusBarIconView expandedIcon = mEntry.expandedIcon;
+        boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L));
+        boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon,
+                ContrastColorUtil.getInstance(mContext));
+        int color = StatusBarIconView.NO_COLOR;
+        if (colorize) {
+            NotificationHeaderView header = getVisibleNotificationHeader();
+            if (header != null) {
+                color = header.getOriginalIconColor();
+            } else {
+                color = mEntry.getContrastedColor(mContext, mIsLowPriority && !isExpanded(),
+                        getBackgroundColorWithoutTint());
+            }
+        }
+        expandedIcon.setStaticDrawableColor(color);
+    }
+
+    public void setAboveShelfChangedListener(AboveShelfChangedListener aboveShelfChangedListener) {
+        mAboveShelfChangedListener = aboveShelfChangedListener;
+    }
+
+    /**
+     * Sets a supplier that can determine whether the keyguard is secure or not.
+     * @param secureStateProvider A function that returns true if keyguard is secure.
+     */
+    public void setSecureStateProvider(BooleanSupplier secureStateProvider) {
+        mSecureStateProvider = secureStateProvider;
+    }
+
+    @Override
+    public boolean isDimmable() {
+        if (!getShowingLayout().isDimmable()) {
+            return false;
+        }
+        return super.isDimmable();
+    }
+
+    private void updateLimits() {
+        for (NotificationContentView l : mLayouts) {
+            updateLimitsForView(l);
+        }
+    }
+
+    private void updateLimitsForView(NotificationContentView layout) {
+        boolean customView = layout.getContractedChild().getId()
+                != com.android.internal.R.id.status_bar_latest_event_content;
+        boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
+        boolean beforeP = mEntry.targetSdk < Build.VERSION_CODES.P;
+        int minHeight;
+        if (customView && beforeP && !mIsSummaryWithChildren) {
+            minHeight = beforeN ? mNotificationMinHeightBeforeN : mNotificationMinHeightBeforeP;
+        } else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
+            minHeight = mNotificationMinHeightLarge;
+        } else {
+            minHeight = mNotificationMinHeight;
+        }
+        boolean headsUpCustom = layout.getHeadsUpChild() != null &&
+                layout.getHeadsUpChild().getId()
+                        != com.android.internal.R.id.status_bar_latest_event_content;
+        int headsUpHeight;
+        if (headsUpCustom && beforeP) {
+            headsUpHeight = beforeN ? mMaxHeadsUpHeightBeforeN : mMaxHeadsUpHeightBeforeP;
+        } else if (mUseIncreasedHeadsUpHeight && layout == mPrivateLayout) {
+            headsUpHeight = mMaxHeadsUpHeightIncreased;
+        } else {
+            headsUpHeight = mMaxHeadsUpHeight;
+        }
+        NotificationViewWrapper headsUpWrapper = layout.getVisibleWrapper(
+                NotificationContentView.VISIBLE_TYPE_HEADSUP);
+        if (headsUpWrapper != null) {
+            headsUpHeight = Math.max(headsUpHeight, headsUpWrapper.getMinLayoutHeight());
+        }
+        layout.setHeights(minHeight, headsUpHeight, mNotificationMaxHeight,
+                mNotificationAmbientHeight);
+    }
+
+    public StatusBarNotification getStatusBarNotification() {
+        return mStatusBarNotification;
+    }
+
+    public NotificationData.Entry getEntry() {
+        return mEntry;
+    }
+
+    public boolean isHeadsUp() {
+        return mIsHeadsUp;
+    }
+
+    public void setHeadsUp(boolean isHeadsUp) {
+        boolean wasAboveShelf = isAboveShelf();
+        int intrinsicBefore = getIntrinsicHeight();
+        mIsHeadsUp = isHeadsUp;
+        mPrivateLayout.setHeadsUp(isHeadsUp);
+        if (mIsSummaryWithChildren) {
+            // The overflow might change since we allow more lines as HUN.
+            mChildrenContainer.updateGroupOverflow();
+        }
+        if (intrinsicBefore != getIntrinsicHeight()) {
+            notifyHeightChanged(false  /* needsAnimation */);
+        }
+        if (isHeadsUp) {
+            mMustStayOnScreen = true;
+            setAboveShelf(true);
+        } else if (isAboveShelf() != wasAboveShelf) {
+            mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
+        }
+    }
+
+    public void setGroupManager(NotificationGroupManager groupManager) {
+        mGroupManager = groupManager;
+        mPrivateLayout.setGroupManager(groupManager);
+    }
+
+    public void setRemoteInputController(RemoteInputController r) {
+        mPrivateLayout.setRemoteInputController(r);
+    }
+
+    public void setAppName(String appName) {
+        mAppName = appName;
+        if (mMenuRow != null && mMenuRow.getMenuView() != null) {
+            mMenuRow.setAppName(mAppName);
+        }
+    }
+
+    public void addChildNotification(ExpandableNotificationRow row) {
+        addChildNotification(row, -1);
+    }
+
+    /**
+     * Set the how much the header should be visible. A value of 0 will make the header fully gone
+     * and a value of 1 will make the notification look just like normal.
+     * This is being used for heads up notifications, when they are pinned to the top of the screen
+     * and the header content is extracted to the statusbar.
+     *
+     * @param headerVisibleAmount the amount the header should be visible.
+     */
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+        if (mHeaderVisibleAmount != headerVisibleAmount) {
+            mHeaderVisibleAmount = headerVisibleAmount;
+            mPrivateLayout.setHeaderVisibleAmount(headerVisibleAmount);
+            if (mChildrenContainer != null) {
+                mChildrenContainer.setHeaderVisibleAmount(headerVisibleAmount);
+            }
+            notifyHeightChanged(false /* needsAnimation */);
+        }
+    }
+
+    @Override
+    public float getHeaderVisibleAmount() {
+        return mHeaderVisibleAmount;
+    }
+
+    @Override
+    public void setHeadsUpIsVisible() {
+        super.setHeadsUpIsVisible();
+        mMustStayOnScreen = false;
+    }
+
+    /**
+     * Add a child notification to this view.
+     *
+     * @param row the row to add
+     * @param childIndex the index to add it at, if -1 it will be added at the end
+     */
+    public void addChildNotification(ExpandableNotificationRow row, int childIndex) {
+        if (mChildrenContainer == null) {
+            mChildrenContainerStub.inflate();
+        }
+        mChildrenContainer.addNotification(row, childIndex);
+        onChildrenCountChanged();
+        row.setIsChildInGroup(true, this);
+    }
+
+    public void removeChildNotification(ExpandableNotificationRow row) {
+        if (mChildrenContainer != null) {
+            mChildrenContainer.removeNotification(row);
+        }
+        onChildrenCountChanged();
+        row.setIsChildInGroup(false, null);
+        row.setBottomRoundness(0.0f, false /* animate */);
+    }
+
+    @Override
+    public boolean isChildInGroup() {
+        return mNotificationParent != null;
+    }
+
+    /**
+     * @return whether this notification is the only child in the group summary
+     */
+    public boolean isOnlyChildInGroup() {
+        return mGroupManager.isOnlyChildInGroup(getStatusBarNotification());
+    }
+
+    public ExpandableNotificationRow getNotificationParent() {
+        return mNotificationParent;
+    }
+
+    /**
+     * @param isChildInGroup Is this notification now in a group
+     * @param parent the new parent notification
+     */
+    public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {
+        boolean childInGroup = StatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
+        if (mExpandAnimationRunning && !isChildInGroup && mNotificationParent != null) {
+            mNotificationParent.setChildIsExpanding(false);
+            mNotificationParent.setExtraWidthForClipping(0.0f);
+            mNotificationParent.setMinimumHeightForClipping(0);
+        }
+        mNotificationParent = childInGroup ? parent : null;
+        mPrivateLayout.setIsChildInGroup(childInGroup);
+        mNotificationInflater.setIsChildInGroup(childInGroup);
+        resetBackgroundAlpha();
+        updateBackgroundForGroupState();
+        updateClickAndFocus();
+        if (mNotificationParent != null) {
+            setOverrideTintColor(NO_COLOR, 0.0f);
+            // Let's reset the distance to top roundness, as this isn't applied to group children
+            setDistanceToTopRoundness(NO_ROUNDNESS);
+            mNotificationParent.updateBackgroundForGroupState();
+        }
+        updateIconVisibilities();
+        updateBackgroundClipping();
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (event.getActionMasked() != MotionEvent.ACTION_DOWN
+                || !isChildInGroup() || isGroupExpanded()) {
+            return super.onTouchEvent(event);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    protected boolean handleSlideBack() {
+        if (mMenuRow != null && mMenuRow.isMenuVisible()) {
+            animateTranslateNotification(0 /* targetLeft */);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean shouldHideBackground() {
+        return super.shouldHideBackground() || mShowNoBackground;
+    }
+
+    @Override
+    public boolean isSummaryWithChildren() {
+        return mIsSummaryWithChildren;
+    }
+
+    @Override
+    public boolean areChildrenExpanded() {
+        return mChildrenExpanded;
+    }
+
+    public List<ExpandableNotificationRow> getNotificationChildren() {
+        return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren();
+    }
+
+    public int getNumberOfNotificationChildren() {
+        if (mChildrenContainer == null) {
+            return 0;
+        }
+        return mChildrenContainer.getNotificationChildren().size();
+    }
+
+    /**
+     * Apply the order given in the list to the children.
+     *
+     * @param childOrder the new list order
+     * @param visualStabilityManager
+     * @param callback the callback to invoked in case it is not allowed
+     * @return whether the list order has changed
+     */
+    public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
+            VisualStabilityManager visualStabilityManager,
+            VisualStabilityManager.Callback callback) {
+        return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder,
+                visualStabilityManager, callback);
+    }
+
+    public void getChildrenStates(StackScrollState resultState,
+            AmbientState ambientState) {
+        if (mIsSummaryWithChildren) {
+            ExpandableViewState parentState = resultState.getViewStateForView(this);
+            mChildrenContainer.getState(resultState, parentState, ambientState);
+        }
+    }
+
+    public void applyChildrenState(StackScrollState state) {
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.applyState(state);
+        }
+    }
+
+    public void prepareExpansionChanged(StackScrollState state) {
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.prepareExpansionChanged(state);
+        }
+    }
+
+    public void startChildAnimation(StackScrollState finalState, AnimationProperties properties) {
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.startAnimationToState(finalState, properties);
+        }
+    }
+
+    public ExpandableNotificationRow getViewAtPosition(float y) {
+        if (!mIsSummaryWithChildren || !mChildrenExpanded) {
+            return this;
+        } else {
+            ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
+            return view == null ? this : view;
+        }
+    }
+
+    public NotificationGuts getGuts() {
+        return mGuts;
+    }
+
+    /**
+     * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this
+     * the notification will be rendered on top of the screen.
+     *
+     * @param pinned whether it is pinned
+     */
+    public void setPinned(boolean pinned) {
+        int intrinsicHeight = getIntrinsicHeight();
+        boolean wasAboveShelf = isAboveShelf();
+        mIsPinned = pinned;
+        if (intrinsicHeight != getIntrinsicHeight()) {
+            notifyHeightChanged(false /* needsAnimation */);
+        }
+        if (pinned) {
+            setIconAnimationRunning(true);
+            mExpandedWhenPinned = false;
+        } else if (mExpandedWhenPinned) {
+            setUserExpanded(true);
+        }
+        setChronometerRunning(mLastChronometerRunning);
+        if (isAboveShelf() != wasAboveShelf) {
+            mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
+        }
+    }
+
+    @Override
+    public boolean isPinned() {
+        return mIsPinned;
+    }
+
+    @Override
+    public int getPinnedHeadsUpHeight() {
+        return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
+    }
+
+    /**
+     * @param atLeastMinHeight should the value returned be at least the minimum height.
+     *                         Used to avoid cyclic calls
+     * @return the height of the heads up notification when pinned
+     */
+    private int getPinnedHeadsUpHeight(boolean atLeastMinHeight) {
+        if (mIsSummaryWithChildren) {
+            return mChildrenContainer.getIntrinsicHeight();
+        }
+        if(mExpandedWhenPinned) {
+            return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
+        } else if (atLeastMinHeight) {
+            return Math.max(getCollapsedHeight(), getHeadsUpHeight());
+        } else {
+            return getHeadsUpHeight();
+        }
+    }
+
+    /**
+     * Mark whether this notification was just clicked, i.e. the user has just clicked this
+     * notification in this frame.
+     */
+    public void setJustClicked(boolean justClicked) {
+        mJustClicked = justClicked;
+    }
+
+    /**
+     * @return true if this notification has been clicked in this frame, false otherwise
+     */
+    public boolean wasJustClicked() {
+        return mJustClicked;
+    }
+
+    public void setChronometerRunning(boolean running) {
+        mLastChronometerRunning = running;
+        setChronometerRunning(running, mPrivateLayout);
+        setChronometerRunning(running, mPublicLayout);
+        if (mChildrenContainer != null) {
+            List<ExpandableNotificationRow> notificationChildren =
+                    mChildrenContainer.getNotificationChildren();
+            for (int i = 0; i < notificationChildren.size(); i++) {
+                ExpandableNotificationRow child = notificationChildren.get(i);
+                child.setChronometerRunning(running);
+            }
+        }
+    }
+
+    private void setChronometerRunning(boolean running, NotificationContentView layout) {
+        if (layout != null) {
+            running = running || isPinned();
+            View contractedChild = layout.getContractedChild();
+            View expandedChild = layout.getExpandedChild();
+            View headsUpChild = layout.getHeadsUpChild();
+            setChronometerRunningForChild(running, contractedChild);
+            setChronometerRunningForChild(running, expandedChild);
+            setChronometerRunningForChild(running, headsUpChild);
+        }
+    }
+
+    private void setChronometerRunningForChild(boolean running, View child) {
+        if (child != null) {
+            View chronometer = child.findViewById(com.android.internal.R.id.chronometer);
+            if (chronometer instanceof Chronometer) {
+                ((Chronometer) chronometer).setStarted(running);
+            }
+        }
+    }
+
+    public NotificationHeaderView getNotificationHeader() {
+        if (mIsSummaryWithChildren) {
+            return mChildrenContainer.getHeaderView();
+        }
+        return mPrivateLayout.getNotificationHeader();
+    }
+
+    /**
+     * @return the currently visible notification header. This can be different from
+     * {@link #getNotificationHeader()} in case it is a low-priority group.
+     */
+    public NotificationHeaderView getVisibleNotificationHeader() {
+        if (mIsSummaryWithChildren && !shouldShowPublic()) {
+            return mChildrenContainer.getVisibleHeader();
+        }
+        return getShowingLayout().getVisibleNotificationHeader();
+    }
+
+
+    /**
+     * @return the contracted notification header. This can be different from
+     * {@link #getNotificationHeader()} and also {@link #getVisibleNotificationHeader()} and only
+     * returns the contracted version.
+     */
+    public NotificationHeaderView getContractedNotificationHeader() {
+        if (mIsSummaryWithChildren) {
+            return mChildrenContainer.getHeaderView();
+        }
+        return mPrivateLayout.getContractedNotificationHeader();
+    }
+
+    public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
+        mOnExpandClickListener = onExpandClickListener;
+    }
+
+    public void setLongPressListener(LongPressListener longPressListener) {
+        mLongPressListener = longPressListener;
+    }
+
+    @Override
+    public void setOnClickListener(@Nullable OnClickListener l) {
+        super.setOnClickListener(l);
+        mOnClickListener = l;
+        updateClickAndFocus();
+    }
+
+    private void updateClickAndFocus() {
+        boolean normalChild = !isChildInGroup() || isGroupExpanded();
+        boolean clickable = mOnClickListener != null && normalChild;
+        if (isFocusable() != normalChild) {
+            setFocusable(normalChild);
+        }
+        if (isClickable() != clickable) {
+            setClickable(clickable);
+        }
+    }
+
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+    }
+
+    public void setGutsView(MenuItem item) {
+        if (mGuts != null && item.getGutsView() instanceof NotificationGuts.GutsContent) {
+            ((NotificationGuts.GutsContent) item.getGutsView()).setGutsParent(mGuts);
+            mGuts.setGutsContent((NotificationGuts.GutsContent) item.getGutsView());
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mEntry.setInitializationTime(SystemClock.elapsedRealtime());
+        Dependency.get(PluginManager.class).addPluginListener(this,
+                NotificationMenuRowPlugin.class, false /* Allow multiple */);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        Dependency.get(PluginManager.class).removePluginListener(this);
+    }
+
+    @Override
+    public void onPluginConnected(NotificationMenuRowPlugin plugin, Context pluginContext) {
+        boolean existed = mMenuRow.getMenuView() != null;
+        if (existed) {
+            removeView(mMenuRow.getMenuView());
+        }
+        mMenuRow = plugin;
+        if (mMenuRow.useDefaultMenuItems()) {
+            ArrayList<MenuItem> items = new ArrayList<>();
+            items.add(NotificationMenuRow.createInfoItem(mContext));
+            items.add(NotificationMenuRow.createSnoozeItem(mContext));
+            items.add(NotificationMenuRow.createAppOpsItem(mContext));
+            mMenuRow.setMenuItems(items);
+        }
+        if (existed) {
+            createMenu();
+        }
+    }
+
+    @Override
+    public void onPluginDisconnected(NotificationMenuRowPlugin plugin) {
+        boolean existed = mMenuRow.getMenuView() != null;
+        mMenuRow = new NotificationMenuRow(mContext); // Back to default
+        if (existed) {
+            createMenu();
+        }
+    }
+
+    public NotificationMenuRowPlugin createMenu() {
+        if (mMenuRow.getMenuView() == null) {
+            mMenuRow.createMenu(this, mStatusBarNotification);
+            mMenuRow.setAppName(mAppName);
+            FrameLayout.LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
+                    LayoutParams.MATCH_PARENT);
+            addView(mMenuRow.getMenuView(), MENU_VIEW_INDEX, lp);
+        }
+        return mMenuRow;
+    }
+
+    public NotificationMenuRowPlugin getProvider() {
+        return mMenuRow;
+    }
+
+    @Override
+    public void onDensityOrFontScaleChanged() {
+        super.onDensityOrFontScaleChanged();
+        initDimens();
+        initBackground();
+        reInflateViews();
+    }
+
+    private void reInflateViews() {
+        // Let's update our childrencontainer. This is intentionally not guarded with
+        // mIsSummaryWithChildren since we might have had children but not anymore.
+        if (mChildrenContainer != null) {
+            mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
+        }
+        if (mGuts != null) {
+            View oldGuts = mGuts;
+            int index = indexOfChild(oldGuts);
+            removeView(oldGuts);
+            mGuts = (NotificationGuts) LayoutInflater.from(mContext).inflate(
+                    R.layout.notification_guts, this, false);
+            mGuts.setVisibility(oldGuts.getVisibility());
+            addView(mGuts, index);
+        }
+        View oldMenu = mMenuRow.getMenuView();
+        if (oldMenu != null) {
+            int menuIndex = indexOfChild(oldMenu);
+            removeView(oldMenu);
+            mMenuRow.createMenu(ExpandableNotificationRow.this, mStatusBarNotification);
+            mMenuRow.setAppName(mAppName);
+            addView(mMenuRow.getMenuView(), menuIndex);
+        }
+        for (NotificationContentView l : mLayouts) {
+            l.initView();
+            l.reInflateViews();
+        }
+        mNotificationInflater.clearCachesAndReInflate();
+        onNotificationUpdated();
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        if (mMenuRow.getMenuView() != null) {
+            mMenuRow.onConfigurationChanged();
+        }
+    }
+
+    @Override
+    public void onUiModeChanged() {
+        super.onUiModeChanged();
+        reInflateViews();
+        if (mChildrenContainer != null) {
+            for (ExpandableNotificationRow child : mChildrenContainer.getNotificationChildren()) {
+                child.onUiModeChanged();
+            }
+        }
+    }
+
+    public void setContentBackground(int customBackgroundColor, boolean animate,
+            NotificationContentView notificationContentView) {
+        if (getShowingLayout() == notificationContentView) {
+            setTintColor(customBackgroundColor, animate);
+        }
+    }
+
+    @Override
+    protected void setBackgroundTintColor(int color) {
+        super.setBackgroundTintColor(color);
+        NotificationContentView view = getShowingLayout();
+        if (view != null) {
+            view.setBackgroundTintColor(color);
+        }
+    }
+
+    public void closeRemoteInput() {
+        for (NotificationContentView l : mLayouts) {
+            l.closeRemoteInput();
+        }
+    }
+
+    /**
+     * Set by how much the single line view should be indented.
+     */
+    public void setSingleLineWidthIndention(int indention) {
+        mPrivateLayout.setSingleLineWidthIndention(indention);
+    }
+
+    public int getNotificationColor() {
+        return mNotificationColor;
+    }
+
+    private void updateNotificationColor() {
+        mNotificationColor = ContrastColorUtil.resolveContrastColor(mContext,
+                getStatusBarNotification().getNotification().color,
+                getBackgroundColorWithoutTint());
+        mNotificationColorAmbient = ContrastColorUtil.resolveAmbientColor(mContext,
+                getStatusBarNotification().getNotification().color);
+    }
+
+    public HybridNotificationView getSingleLineView() {
+        return mPrivateLayout.getSingleLineView();
+    }
+
+    public HybridNotificationView getAmbientSingleLineView() {
+        return getShowingLayout().getAmbientSingleLineChild();
+    }
+
+    public boolean isOnKeyguard() {
+        return mOnKeyguard;
+    }
+
+    public void removeAllChildren() {
+        List<ExpandableNotificationRow> notificationChildren
+                = mChildrenContainer.getNotificationChildren();
+        ArrayList<ExpandableNotificationRow> clonedList = new ArrayList<>(notificationChildren);
+        for (int i = 0; i < clonedList.size(); i++) {
+            ExpandableNotificationRow row = clonedList.get(i);
+            if (row.keepInParent()) {
+                continue;
+            }
+            mChildrenContainer.removeNotification(row);
+            row.setIsChildInGroup(false, null);
+        }
+        onChildrenCountChanged();
+    }
+
+    public void setForceUnlocked(boolean forceUnlocked) {
+        mForceUnlocked = forceUnlocked;
+        if (mIsSummaryWithChildren) {
+            List<ExpandableNotificationRow> notificationChildren = getNotificationChildren();
+            for (ExpandableNotificationRow child : notificationChildren) {
+                child.setForceUnlocked(forceUnlocked);
+            }
+        }
+    }
+
+    public void setDismissed(boolean fromAccessibility) {
+        setLongPressListener(null);
+        mDismissed = true;
+        mGroupParentWhenDismissed = mNotificationParent;
+        mRefocusOnDismiss = fromAccessibility;
+        mChildAfterViewWhenDismissed = null;
+        mEntry.icon.setDismissed();
+        if (isChildInGroup()) {
+            List<ExpandableNotificationRow> notificationChildren =
+                    mNotificationParent.getNotificationChildren();
+            int i = notificationChildren.indexOf(this);
+            if (i != -1 && i < notificationChildren.size() - 1) {
+                mChildAfterViewWhenDismissed = notificationChildren.get(i + 1);
+            }
+        }
+    }
+
+    public boolean isDismissed() {
+        return mDismissed;
+    }
+
+    public boolean keepInParent() {
+        return mKeepInParent;
+    }
+
+    public void setKeepInParent(boolean keepInParent) {
+        mKeepInParent = keepInParent;
+    }
+
+    @Override
+    public boolean isRemoved() {
+        return mRemoved;
+    }
+
+    public void setRemoved() {
+        mRemoved = true;
+        mTranslationWhenRemoved = getTranslationY();
+        mWasChildInGroupWhenRemoved = isChildInGroup();
+        if (isChildInGroup()) {
+            mTranslationWhenRemoved += getNotificationParent().getTranslationY();
+        }
+        mPrivateLayout.setRemoved();
+    }
+
+    public boolean wasChildInGroupWhenRemoved() {
+        return mWasChildInGroupWhenRemoved;
+    }
+
+    public float getTranslationWhenRemoved() {
+        return mTranslationWhenRemoved;
+    }
+
+    public NotificationChildrenContainer getChildrenContainer() {
+        return mChildrenContainer;
+    }
+
+    public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
+        boolean wasAboveShelf = isAboveShelf();
+        boolean changed = headsUpAnimatingAway != mHeadsupDisappearRunning;
+        mHeadsupDisappearRunning = headsUpAnimatingAway;
+        mPrivateLayout.setHeadsUpAnimatingAway(headsUpAnimatingAway);
+        if (changed && mHeadsUpAnimatingAwayListener != null) {
+            mHeadsUpAnimatingAwayListener.accept(headsUpAnimatingAway);
+        }
+        if (isAboveShelf() != wasAboveShelf) {
+            mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
+        }
+    }
+
+    public void setHeadsUpAnimatingAwayListener(Consumer<Boolean> listener) {
+        mHeadsUpAnimatingAwayListener = listener;
+    }
+
+    /**
+     * @return if the view was just heads upped and is now animating away. During such a time the
+     * layout needs to be kept consistent
+     */
+    @Override
+    public boolean isHeadsUpAnimatingAway() {
+        return mHeadsupDisappearRunning;
+    }
+
+    public View getChildAfterViewWhenDismissed() {
+        return mChildAfterViewWhenDismissed;
+    }
+
+    public View getGroupParentWhenDismissed() {
+        return mGroupParentWhenDismissed;
+    }
+
+    /**
+     * Dismisses the notification with the option of showing the blocking helper in-place if we have
+     * a negative user sentiment.
+     *
+     * @param fromAccessibility whether this dismiss is coming from an accessibility action
+     * @return whether a blocking helper is shown in this row
+     */
+    public boolean performDismissWithBlockingHelper(boolean fromAccessibility) {
+        NotificationBlockingHelperManager manager =
+                Dependency.get(NotificationBlockingHelperManager.class);
+        boolean isBlockingHelperShown = manager.perhapsShowBlockingHelper(this, mMenuRow);
+
+        Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
+
+        // Continue with dismiss since we don't want the blocking helper to be directly associated
+        // with a certain notification.
+        performDismiss(fromAccessibility);
+        return isBlockingHelperShown;
+    }
+
+    public void performDismiss(boolean fromAccessibility) {
+        if (isOnlyChildInGroup()) {
+            ExpandableNotificationRow 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
+                // the blocking helper affordance!
+                groupSummary.performDismiss(fromAccessibility);
+            }
+        }
+        setDismissed(fromAccessibility);
+        if (isClearable()) {
+            if (mOnDismissRunnable != null) {
+                mOnDismissRunnable.run();
+            }
+        }
+    }
+
+    public void setBlockingHelperShowing(boolean isBlockingHelperShowing) {
+        mIsBlockingHelperShowing = isBlockingHelperShowing;
+    }
+
+    public boolean isBlockingHelperShowing() {
+        return mIsBlockingHelperShowing;
+    }
+
+    public void setOnDismissRunnable(Runnable onDismissRunnable) {
+        mOnDismissRunnable = onDismissRunnable;
+    }
+
+    public View getNotificationIcon() {
+        NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
+        if (notificationHeader != null) {
+            return notificationHeader.getIcon();
+        }
+        return null;
+    }
+
+    /**
+     * @return whether the notification is currently showing a view with an icon.
+     */
+    public boolean isShowingIcon() {
+        if (areGutsExposed()) {
+            return false;
+        }
+        return getVisibleNotificationHeader() != null;
+    }
+
+    /**
+     * Set how much this notification is transformed into an icon.
+     *
+     * @param contentTransformationAmount A value from 0 to 1 indicating how much we are transformed
+     *                                 to the content away
+     * @param isLastChild is this the last child in the list. If true, then the transformation is
+     *                    different since it's content fades out.
+     */
+    public void setContentTransformationAmount(float contentTransformationAmount,
+            boolean isLastChild) {
+        boolean changeTransformation = isLastChild != mIsLastChild;
+        changeTransformation |= mContentTransformationAmount != contentTransformationAmount;
+        mIsLastChild = isLastChild;
+        mContentTransformationAmount = contentTransformationAmount;
+        if (changeTransformation) {
+            updateContentTransformation();
+        }
+    }
+
+    /**
+     * Set the icons to be visible of this notification.
+     */
+    public void setIconsVisible(boolean iconsVisible) {
+        if (iconsVisible != mIconsVisible) {
+            mIconsVisible = iconsVisible;
+            updateIconVisibilities();
+        }
+    }
+
+    @Override
+    protected void onBelowSpeedBumpChanged() {
+        updateIconVisibilities();
+    }
+
+    private void updateContentTransformation() {
+        if (mExpandAnimationRunning) {
+            return;
+        }
+        float contentAlpha;
+        float translationY = -mContentTransformationAmount * mIconTransformContentShift;
+        if (mIsLastChild) {
+            contentAlpha = 1.0f - mContentTransformationAmount;
+            contentAlpha = Math.min(contentAlpha / 0.5f, 1.0f);
+            contentAlpha = Interpolators.ALPHA_OUT.getInterpolation(contentAlpha);
+            translationY *= 0.4f;
+        } else {
+            contentAlpha = 1.0f;
+        }
+        for (NotificationContentView l : mLayouts) {
+            l.setAlpha(contentAlpha);
+            l.setTranslationY(translationY);
+        }
+        if (mChildrenContainer != null) {
+            mChildrenContainer.setAlpha(contentAlpha);
+            mChildrenContainer.setTranslationY(translationY);
+            // TODO: handle children fade out better
+        }
+    }
+
+    private void updateIconVisibilities() {
+        boolean visible = isChildInGroup()
+                || (isBelowSpeedBump() && !NotificationShelf.SHOW_AMBIENT_ICONS)
+                || mIconsVisible;
+        for (NotificationContentView l : mLayouts) {
+            l.setIconsVisible(visible);
+        }
+        if (mChildrenContainer != null) {
+            mChildrenContainer.setIconsVisible(visible);
+        }
+    }
+
+    /**
+     * Get the relative top padding of a view relative to this view. This recursively walks up the
+     * hierarchy and does the corresponding measuring.
+     *
+     * @param view the view to the the padding for. The requested view has to be a child of this
+     *             notification.
+     * @return the toppadding
+     */
+    public int getRelativeTopPadding(View view) {
+        int topPadding = 0;
+        while (view.getParent() instanceof ViewGroup) {
+            topPadding += view.getTop();
+            view = (View) view.getParent();
+            if (view instanceof ExpandableNotificationRow) {
+                return topPadding;
+            }
+        }
+        return topPadding;
+    }
+
+    public float getContentTranslation() {
+        return mPrivateLayout.getTranslationY();
+    }
+
+    public void setIsLowPriority(boolean isLowPriority) {
+        mIsLowPriority = isLowPriority;
+        mPrivateLayout.setIsLowPriority(isLowPriority);
+        mNotificationInflater.setIsLowPriority(mIsLowPriority);
+        if (mChildrenContainer != null) {
+            mChildrenContainer.setIsLowPriority(isLowPriority);
+        }
+    }
+
+
+    public void setLowPriorityStateUpdated(boolean lowPriorityStateUpdated) {
+        mLowPriorityStateUpdated = lowPriorityStateUpdated;
+    }
+
+    public boolean hasLowPriorityStateUpdated() {
+        return mLowPriorityStateUpdated;
+    }
+
+    public boolean isLowPriority() {
+        return mIsLowPriority;
+    }
+
+    public void setUseIncreasedCollapsedHeight(boolean use) {
+        mUseIncreasedCollapsedHeight = use;
+        mNotificationInflater.setUsesIncreasedHeight(use);
+    }
+
+    public void setSmartActions(List<Notification.Action> smartActions) {
+        mNotificationInflater.setSmartActions(smartActions);
+    }
+
+    public void setUseIncreasedHeadsUpHeight(boolean use) {
+        mUseIncreasedHeadsUpHeight = use;
+        mNotificationInflater.setUsesIncreasedHeadsUpHeight(use);
+    }
+
+    public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
+        mNotificationInflater.setRemoteViewClickHandler(remoteViewClickHandler);
+    }
+
+    public void setInflationCallback(InflationCallback callback) {
+        mNotificationInflater.setInflationCallback(callback);
+    }
+
+    public void setNeedsRedaction(boolean needsRedaction) {
+        mNotificationInflater.setRedactAmbient(needsRedaction);
+    }
+
+    @VisibleForTesting
+    public NotificationInflater getNotificationInflater() {
+        return mNotificationInflater;
+    }
+
+    public int getNotificationColorAmbient() {
+        return mNotificationColorAmbient;
+    }
+
+    public interface ExpansionLogger {
+        void logNotificationExpansion(String key, boolean userAction, boolean expanded);
+    }
+
+    public ExpandableNotificationRow(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mFalsingManager = FalsingManager.getInstance(context);
+        mNotificationInflater = new NotificationInflater(this);
+        mMenuRow = new NotificationMenuRow(mContext);
+        initDimens();
+    }
+
+    private void initDimens() {
+        mNotificationMinHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
+                R.dimen.notification_min_height_legacy);
+        mNotificationMinHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
+                R.dimen.notification_min_height_before_p);
+        mNotificationMinHeight = NotificationUtils.getFontScaledHeight(mContext,
+                R.dimen.notification_min_height);
+        mNotificationMinHeightLarge = NotificationUtils.getFontScaledHeight(mContext,
+                R.dimen.notification_min_height_increased);
+        mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext,
+                R.dimen.notification_max_height);
+        mNotificationAmbientHeight = NotificationUtils.getFontScaledHeight(mContext,
+                R.dimen.notification_ambient_height);
+        mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
+                R.dimen.notification_max_heads_up_height_legacy);
+        mMaxHeadsUpHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
+                R.dimen.notification_max_heads_up_height_before_p);
+        mMaxHeadsUpHeight = NotificationUtils.getFontScaledHeight(mContext,
+                R.dimen.notification_max_heads_up_height);
+        mMaxHeadsUpHeightIncreased = NotificationUtils.getFontScaledHeight(mContext,
+                R.dimen.notification_max_heads_up_height_increased);
+
+        Resources res = getResources();
+        mIncreasedPaddingBetweenElements = res.getDimensionPixelSize(
+                R.dimen.notification_divider_height_increased);
+        mIconTransformContentShiftNoIcon = res.getDimensionPixelSize(
+                R.dimen.notification_icon_transform_content_shift);
+        mEnableNonGroupedNotificationExpand =
+                res.getBoolean(R.bool.config_enableNonGroupedNotificationExpand);
+        mShowGroupBackgroundWhenExpanded =
+                res.getBoolean(R.bool.config_showGroupNotificationBgWhenExpanded);
+    }
+
+    /**
+     * Resets this view so it can be re-used for an updated notification.
+     */
+    public void reset() {
+        mShowingPublicInitialized = false;
+        onHeightReset();
+        requestLayout();
+    }
+
+    public void showAppOpsIcons(ArraySet<Integer> activeOps) {
+        if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) {
+            mChildrenContainer.getHeaderView().showAppOpsIcons(activeOps);
+        }
+        mPrivateLayout.showAppOpsIcons(activeOps);
+        mPublicLayout.showAppOpsIcons(activeOps);
+    }
+
+    public View.OnClickListener getAppOpsOnClickListener() {
+        return mOnAppOpsClickListener;
+    }
+
+    public void setAppOpsOnClickListener(ExpandableNotificationRow.OnAppOpsClickListener l) {
+        mOnAppOpsClickListener = v -> {
+            createMenu();
+            MenuItem menuItem = getProvider().getAppOpsMenuItem(mContext);
+            if (menuItem != null) {
+                l.onClick(this, v.getWidth() / 2, v.getHeight() / 2, menuItem);
+            }
+        };
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
+        mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
+        mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
+
+        for (NotificationContentView l : mLayouts) {
+            l.setExpandClickListener(mExpandClickListener);
+            l.setContainingNotification(this);
+        }
+        mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
+        mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() {
+            @Override
+            public void onInflate(ViewStub stub, View inflated) {
+                mGuts = (NotificationGuts) inflated;
+                mGuts.setClipTopAmount(getClipTopAmount());
+                mGuts.setActualHeight(getActualHeight());
+                mGutsStub = null;
+            }
+        });
+        mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
+        mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() {
+
+            @Override
+            public void onInflate(ViewStub stub, View inflated) {
+                mChildrenContainer = (NotificationChildrenContainer) inflated;
+                mChildrenContainer.setIsLowPriority(mIsLowPriority);
+                mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
+                mChildrenContainer.onNotificationUpdated();
+
+                if (mShouldTranslateContents) {
+                    mTranslateableViews.add(mChildrenContainer);
+                }
+            }
+        });
+
+        if (mShouldTranslateContents) {
+            // Add the views that we translate to reveal the menu
+            mTranslateableViews = new ArrayList<>();
+            for (int i = 0; i < getChildCount(); i++) {
+                mTranslateableViews.add(getChildAt(i));
+            }
+            // Remove views that don't translate
+            mTranslateableViews.remove(mChildrenContainerStub);
+            mTranslateableViews.remove(mGutsStub);
+        }
+    }
+
+    private void doLongClickCallback() {
+        doLongClickCallback(getWidth() / 2, getHeight() / 2);
+    }
+
+    public void doLongClickCallback(int x, int y) {
+        createMenu();
+        MenuItem menuItem = getProvider().getLongpressMenuItem(mContext);
+        doLongClickCallback(x, y, menuItem);
+    }
+
+    private void doLongClickCallback(int x, int y, MenuItem menuItem) {
+        if (mLongPressListener != null && menuItem != null) {
+            mLongPressListener.onLongPress(this, x, y, menuItem);
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (KeyEvent.isConfirmKey(keyCode)) {
+            event.startTracking();
+            return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (KeyEvent.isConfirmKey(keyCode)) {
+            if (!event.isCanceled()) {
+                performClick();
+            }
+            return true;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+        if (KeyEvent.isConfirmKey(keyCode)) {
+            doLongClickCallback();
+            return true;
+        }
+        return false;
+    }
+
+    public void resetTranslation() {
+        if (mTranslateAnim != null) {
+            mTranslateAnim.cancel();
+        }
+
+        if (!mShouldTranslateContents) {
+            setTranslationX(0);
+        } else if (mTranslateableViews != null) {
+            for (int i = 0; i < mTranslateableViews.size(); i++) {
+                mTranslateableViews.get(i).setTranslationX(0);
+            }
+            invalidateOutline();
+            getEntry().expandedIcon.setScrollX(0);
+        }
+
+        mMenuRow.resetMenu();
+    }
+
+    void onGutsOpened() {
+        resetTranslation();
+        updateContentAccessibilityImportanceForGuts(false /* isEnabled */);
+    }
+
+    void onGutsClosed() {
+        updateContentAccessibilityImportanceForGuts(true /* isEnabled */);
+    }
+
+    /**
+     * Updates whether all the non-guts content inside this row is important for accessibility.
+     *
+     * @param isEnabled whether the content views should be enabled for accessibility
+     */
+    private void updateContentAccessibilityImportanceForGuts(boolean isEnabled) {
+        if (mChildrenContainer != null) {
+            updateChildAccessibilityImportance(mChildrenContainer, isEnabled);
+        }
+        if (mLayouts != null) {
+            for (View view : mLayouts) {
+                updateChildAccessibilityImportance(view, isEnabled);
+            }
+        }
+
+        if (isEnabled) {
+            this.requestAccessibilityFocus();
+        }
+    }
+
+    /**
+     * Updates whether the given childView is important for accessibility based on
+     * {@code isEnabled}.
+     */
+    private void updateChildAccessibilityImportance(View childView, boolean isEnabled) {
+        childView.setImportantForAccessibility(isEnabled
+                ? View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
+                : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+    }
+
+    public CharSequence getActiveRemoteInputText() {
+        return mPrivateLayout.getActiveRemoteInputText();
+    }
+
+    public void animateTranslateNotification(final float leftTarget) {
+        if (mTranslateAnim != null) {
+            mTranslateAnim.cancel();
+        }
+        mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */);
+        if (mTranslateAnim != null) {
+            mTranslateAnim.start();
+        }
+    }
+
+    @Override
+    public void setTranslation(float translationX) {
+        if (areGutsExposed()) {
+            // Don't translate if guts are showing.
+            return;
+        }
+        if (!mShouldTranslateContents) {
+            setTranslationX(translationX);
+        } else if (mTranslateableViews != null) {
+            // Translate the group of views
+            for (int i = 0; i < mTranslateableViews.size(); i++) {
+                if (mTranslateableViews.get(i) != null) {
+                    mTranslateableViews.get(i).setTranslationX(translationX);
+                }
+            }
+            invalidateOutline();
+
+            // In order to keep the shelf in sync with this swiping, we're simply translating
+            // it's icon by the same amount. The translation is already being used for the normal
+            // positioning, so we can use the scrollX instead.
+            getEntry().expandedIcon.setScrollX((int) -translationX);
+        }
+        if (mMenuRow.getMenuView() != null) {
+            mMenuRow.onTranslationUpdate(translationX);
+        }
+    }
+
+    @Override
+    public float getTranslation() {
+        if (!mShouldTranslateContents) {
+            return getTranslationX();
+        }
+
+        if (mTranslateableViews != null && mTranslateableViews.size() > 0) {
+            // All of the views in the list should have same translation, just use first one.
+            return mTranslateableViews.get(0).getTranslationX();
+        }
+
+        return 0;
+    }
+
+    public Animator getTranslateViewAnimator(final float leftTarget,
+            AnimatorUpdateListener listener) {
+        if (mTranslateAnim != null) {
+            mTranslateAnim.cancel();
+        }
+        if (areGutsExposed()) {
+            // No translation if guts are exposed.
+            return null;
+        }
+        final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(this, TRANSLATE_CONTENT,
+                leftTarget);
+        if (listener != null) {
+            translateAnim.addUpdateListener(listener);
+        }
+        translateAnim.addListener(new AnimatorListenerAdapter() {
+            boolean cancelled = false;
+
+            @Override
+            public void onAnimationCancel(Animator anim) {
+                cancelled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator anim) {
+                if (!cancelled && leftTarget == 0) {
+                    mMenuRow.resetMenu();
+                    mTranslateAnim = null;
+                }
+            }
+        });
+        mTranslateAnim = translateAnim;
+        return translateAnim;
+    }
+
+    public void inflateGuts() {
+        if (mGuts == null) {
+            mGutsStub.inflate();
+        }
+    }
+
+    private void updateChildrenVisibility() {
+        boolean hideContentWhileLaunching = mExpandAnimationRunning && mGuts != null
+                && mGuts.isExposed();
+        mPrivateLayout.setVisibility(!shouldShowPublic() && !mIsSummaryWithChildren
+                && !hideContentWhileLaunching ? VISIBLE : INVISIBLE);
+        if (mChildrenContainer != null) {
+            mChildrenContainer.setVisibility(!shouldShowPublic() && mIsSummaryWithChildren
+                    && !hideContentWhileLaunching ? VISIBLE
+                    : INVISIBLE);
+        }
+        // The limits might have changed if the view suddenly became a group or vice versa
+        updateLimits();
+    }
+
+    @Override
+    public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
+        if (super.onRequestSendAccessibilityEventInternal(child, event)) {
+            // Add a record for the entire layout since its content is somehow small.
+            // The event comes from a leaf view that is interacted with.
+            AccessibilityEvent record = AccessibilityEvent.obtain();
+            onInitializeAccessibilityEvent(record);
+            dispatchPopulateAccessibilityEvent(record);
+            event.appendRecord(record);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void setDark(boolean dark, boolean fade, long delay) {
+        super.setDark(dark, fade, delay);
+        mDark = dark;
+        if (!mIsHeadsUp) {
+            // Only fade the showing view of the pulsing notification.
+            fade = false;
+        }
+        final NotificationContentView showing = getShowingLayout();
+        if (showing != null) {
+            showing.setDark(dark, fade, delay);
+        }
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.setDark(dark, fade, delay);
+        }
+        updateShelfIconColor();
+    }
+
+    public void applyExpandAnimationParams(ExpandAnimationParameters params) {
+        if (params == null) {
+            return;
+        }
+        float zProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+                params.getProgress(0, 50));
+        float translationZ = MathUtils.lerp(params.getStartTranslationZ(),
+                mNotificationLaunchHeight,
+                zProgress);
+        setTranslationZ(translationZ);
+        float extraWidthForClipping = params.getWidth() - getWidth()
+                + MathUtils.lerp(0, mOutlineRadius * 2, params.getProgress());
+        setExtraWidthForClipping(extraWidthForClipping);
+        int top = params.getTop();
+        float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
+        int startClipTopAmount = params.getStartClipTopAmount();
+        if (mNotificationParent != null) {
+            top -= mNotificationParent.getTranslationY();
+            mNotificationParent.setTranslationZ(translationZ);
+            int parentStartClipTopAmount = params.getParentStartClipTopAmount();
+            if (startClipTopAmount != 0) {
+                int clipTopAmount = (int) MathUtils.lerp(parentStartClipTopAmount,
+                        parentStartClipTopAmount - startClipTopAmount,
+                        interpolation);
+                mNotificationParent.setClipTopAmount(clipTopAmount);
+            }
+            mNotificationParent.setExtraWidthForClipping(extraWidthForClipping);
+            mNotificationParent.setMinimumHeightForClipping(params.getHeight()
+                    + mNotificationParent.getActualHeight());
+        } else if (startClipTopAmount != 0) {
+            int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
+            setClipTopAmount(clipTopAmount);
+        }
+        setTranslationY(top);
+        setActualHeight(params.getHeight());
+
+        mBackgroundNormal.setExpandAnimationParams(params);
+    }
+
+    public void setExpandAnimationRunning(boolean expandAnimationRunning) {
+        View contentView;
+        if (mIsSummaryWithChildren) {
+            contentView =  mChildrenContainer;
+        } else {
+            contentView = getShowingLayout();
+        }
+        if (mGuts != null && mGuts.isExposed()) {
+            contentView = mGuts;
+        }
+        if (expandAnimationRunning) {
+            contentView.animate()
+                    .alpha(0f)
+                    .setDuration(ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT)
+                    .setInterpolator(Interpolators.ALPHA_OUT);
+            setAboveShelf(true);
+            mExpandAnimationRunning = true;
+            mNotificationViewState.cancelAnimations(this);
+            mNotificationLaunchHeight = AmbientState.getNotificationLaunchHeight(getContext());
+        } else {
+            mExpandAnimationRunning = false;
+            setAboveShelf(isAboveShelf());
+            if (mGuts != null) {
+                mGuts.setAlpha(1.0f);
+            }
+            if (contentView != null) {
+                contentView.setAlpha(1.0f);
+            }
+            setExtraWidthForClipping(0.0f);
+            if (mNotificationParent != null) {
+                mNotificationParent.setExtraWidthForClipping(0.0f);
+                mNotificationParent.setMinimumHeightForClipping(0);
+            }
+        }
+        if (mNotificationParent != null) {
+            mNotificationParent.setChildIsExpanding(mExpandAnimationRunning);
+        }
+        updateChildrenVisibility();
+        updateClipping();
+        mBackgroundNormal.setExpandAnimationRunning(expandAnimationRunning);
+    }
+
+    private void setChildIsExpanding(boolean isExpanding) {
+        mChildIsExpanding = isExpanding;
+    }
+
+    @Override
+    public boolean hasExpandingChild() {
+        return mChildIsExpanding;
+    }
+
+    @Override
+    protected boolean shouldClipToActualHeight() {
+        return super.shouldClipToActualHeight() && !mExpandAnimationRunning && !mChildIsExpanding;
+    }
+
+    @Override
+    public boolean isExpandAnimationRunning() {
+        return mExpandAnimationRunning;
+    }
+
+    /**
+     * Tap sounds should not be played when we're unlocking.
+     * Doing so would cause audio collision and the system would feel unpolished.
+     */
+    @Override
+    public boolean isSoundEffectsEnabled() {
+        final boolean mute = mDark && mSecureStateProvider != null &&
+                !mSecureStateProvider.getAsBoolean();
+        return !mute && super.isSoundEffectsEnabled();
+    }
+
+    public boolean isExpandable() {
+        if (mIsSummaryWithChildren && !shouldShowPublic()) {
+            return !mChildrenExpanded;
+        }
+        return mEnableNonGroupedNotificationExpand && mExpandable;
+    }
+
+    public void setExpandable(boolean expandable) {
+        mExpandable = expandable;
+        mPrivateLayout.updateExpandButtons(isExpandable());
+    }
+
+    @Override
+    public void setClipToActualHeight(boolean clipToActualHeight) {
+        super.setClipToActualHeight(clipToActualHeight || isUserLocked());
+        getShowingLayout().setClipToActualHeight(clipToActualHeight || isUserLocked());
+    }
+
+    /**
+     * @return whether the user has changed the expansion state
+     */
+    public boolean hasUserChangedExpansion() {
+        return mHasUserChangedExpansion;
+    }
+
+    public boolean isUserExpanded() {
+        return mUserExpanded;
+    }
+
+    /**
+     * Set this notification to be expanded by the user
+     *
+     * @param userExpanded whether the user wants this notification to be expanded
+     */
+    public void setUserExpanded(boolean userExpanded) {
+        setUserExpanded(userExpanded, false /* allowChildExpansion */);
+    }
+
+    /**
+     * Set this notification to be expanded by the user
+     *
+     * @param userExpanded whether the user wants this notification to be expanded
+     * @param allowChildExpansion whether a call to this method allows expanding children
+     */
+    public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
+        mFalsingManager.setNotificationExpanded();
+        if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion
+                && !mChildrenContainer.showingAsLowPriority()) {
+            final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
+            mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded);
+            onExpansionChanged(true /* userAction */, wasExpanded);
+            return;
+        }
+        if (userExpanded && !mExpandable) return;
+        final boolean wasExpanded = isExpanded();
+        mHasUserChangedExpansion = true;
+        mUserExpanded = userExpanded;
+        onExpansionChanged(true /* userAction */, wasExpanded);
+        if (!wasExpanded && isExpanded()
+                && getActualHeight() != getIntrinsicHeight()) {
+            notifyHeightChanged(true /* needsAnimation */);
+        }
+    }
+
+    public void resetUserExpansion() {
+        boolean changed = mUserExpanded;
+        mHasUserChangedExpansion = false;
+        mUserExpanded = false;
+        if (changed && mIsSummaryWithChildren) {
+            mChildrenContainer.onExpansionChanged();
+        }
+        updateShelfIconColor();
+    }
+
+    public boolean isUserLocked() {
+        return mUserLocked && !mForceUnlocked;
+    }
+
+    public void setUserLocked(boolean userLocked) {
+        mUserLocked = userLocked;
+        mPrivateLayout.setUserExpanding(userLocked);
+        // This is intentionally not guarded with mIsSummaryWithChildren since we might have had
+        // children but not anymore.
+        if (mChildrenContainer != null) {
+            mChildrenContainer.setUserLocked(userLocked);
+            if (mIsSummaryWithChildren && (userLocked || !isGroupExpanded())) {
+                updateBackgroundForGroupState();
+            }
+        }
+    }
+
+    /**
+     * @return has the system set this notification to be expanded
+     */
+    public boolean isSystemExpanded() {
+        return mIsSystemExpanded;
+    }
+
+    /**
+     * Set this notification to be expanded by the system.
+     *
+     * @param expand whether the system wants this notification to be expanded.
+     */
+    public void setSystemExpanded(boolean expand) {
+        if (expand != mIsSystemExpanded) {
+            final boolean wasExpanded = isExpanded();
+            mIsSystemExpanded = expand;
+            notifyHeightChanged(false /* needsAnimation */);
+            onExpansionChanged(false /* userAction */, wasExpanded);
+            if (mIsSummaryWithChildren) {
+                mChildrenContainer.updateGroupOverflow();
+            }
+        }
+    }
+
+    /**
+     * @param onKeyguard whether to prevent notification expansion
+     */
+    public void setOnKeyguard(boolean onKeyguard) {
+        if (onKeyguard != mOnKeyguard) {
+            boolean wasAboveShelf = isAboveShelf();
+            final boolean wasExpanded = isExpanded();
+            mOnKeyguard = onKeyguard;
+            onExpansionChanged(false /* userAction */, wasExpanded);
+            if (wasExpanded != isExpanded()) {
+                if (mIsSummaryWithChildren) {
+                    mChildrenContainer.updateGroupOverflow();
+                }
+                notifyHeightChanged(false /* needsAnimation */);
+            }
+            if (isAboveShelf() != wasAboveShelf) {
+                mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
+            }
+        }
+        updateRippleAllowed();
+    }
+
+    private void updateRippleAllowed() {
+        boolean allowed = isOnKeyguard()
+                || mEntry.notification.getNotification().contentIntent == null;
+        setRippleAllowed(allowed);
+    }
+
+    /**
+     * @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 (mStatusBarNotification == null || !mStatusBarNotification.isClearable()) {
+            return false;
+        }
+        if (mIsSummaryWithChildren) {
+            List<ExpandableNotificationRow> notificationChildren =
+                    mChildrenContainer.getNotificationChildren();
+            for (int i = 0; i < notificationChildren.size(); i++) {
+                ExpandableNotificationRow child = notificationChildren.get(i);
+                if (!child.isClearable()) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        if (isUserLocked()) {
+            return getActualHeight();
+        }
+        if (mGuts != null && mGuts.isExposed()) {
+            return mGuts.getIntrinsicHeight();
+        } else if ((isChildInGroup() && !isGroupExpanded())) {
+            return mPrivateLayout.getMinHeight();
+        } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
+            return getMinHeight();
+        } else if (mIsSummaryWithChildren && (!mOnKeyguard || mShowAmbient)) {
+            return mChildrenContainer.getIntrinsicHeight();
+        } else if (isHeadsUpAllowed() && (mIsHeadsUp || mHeadsupDisappearRunning)) {
+            if (isPinned() || mHeadsupDisappearRunning) {
+                return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
+            } else if (isExpanded()) {
+                return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
+            } else {
+                return Math.max(getCollapsedHeight(), getHeadsUpHeight());
+            }
+        } else if (isExpanded()) {
+            return getMaxExpandHeight();
+        } else {
+            return getCollapsedHeight();
+        }
+    }
+
+    private boolean isHeadsUpAllowed() {
+        return !mOnKeyguard && !mShowAmbient;
+    }
+
+    @Override
+    public boolean isGroupExpanded() {
+        return mGroupManager.isGroupExpanded(mStatusBarNotification);
+    }
+
+    private void onChildrenCountChanged() {
+        mIsSummaryWithChildren = StatusBar.ENABLE_CHILD_NOTIFICATIONS
+                && mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0;
+        if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
+            mChildrenContainer.recreateNotificationHeader(mExpandClickListener
+            );
+        }
+        getShowingLayout().updateBackgroundColor(false /* animate */);
+        mPrivateLayout.updateExpandButtons(isExpandable());
+        updateChildrenHeaderAppearance();
+        updateChildrenVisibility();
+        applyChildrenRoundness();
+    }
+    /**
+     * Returns the number of channels covered by the notification row (including its children if
+     * it's a summary notification).
+     */
+    public int getNumUniqueChannels() {
+        ArraySet<NotificationChannel> channels = new ArraySet<>();
+
+        channels.add(mEntry.channel);
+
+        // If this is a summary, then add in the children notification channels for the
+        // same user and pkg.
+        if (mIsSummaryWithChildren) {
+            final List<ExpandableNotificationRow> childrenRows = getNotificationChildren();
+            final int numChildren = childrenRows.size();
+            for (int i = 0; i < numChildren; i++) {
+                final ExpandableNotificationRow childRow = childrenRows.get(i);
+                final NotificationChannel childChannel = childRow.getEntry().channel;
+                final StatusBarNotification childSbn = childRow.getStatusBarNotification();
+                if (childSbn.getUser().equals(mStatusBarNotification.getUser()) &&
+                        childSbn.getPackageName().equals(mStatusBarNotification.getPackageName())) {
+                    channels.add(childChannel);
+                }
+            }
+        }
+        return channels.size();
+    }
+
+    public void updateChildrenHeaderAppearance() {
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.updateChildrenHeaderAppearance();
+        }
+    }
+
+    /**
+     * Check whether the view state is currently expanded. This is given by the system in {@link
+     * #setSystemExpanded(boolean)} and can be overridden by user expansion or
+     * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
+     * view can differ from this state, if layout params are modified from outside.
+     *
+     * @return whether the view state is currently expanded.
+     */
+    public boolean isExpanded() {
+        return isExpanded(false /* allowOnKeyguard */);
+    }
+
+    public boolean isExpanded(boolean allowOnKeyguard) {
+        return (!mOnKeyguard || allowOnKeyguard)
+                && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
+                || isUserExpanded());
+    }
+
+    private boolean isSystemChildExpanded() {
+        return mIsSystemChildExpanded;
+    }
+
+    public void setSystemChildExpanded(boolean expanded) {
+        mIsSystemChildExpanded = expanded;
+    }
+
+    public void setLayoutListener(LayoutListener listener) {
+        mLayoutListener = listener;
+    }
+
+    public void removeListener() {
+        mLayoutListener = null;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        int intrinsicBefore = getIntrinsicHeight();
+        super.onLayout(changed, left, top, right, bottom);
+        if (intrinsicBefore != getIntrinsicHeight()) {
+            notifyHeightChanged(true  /* needsAnimation */);
+        }
+        if (mMenuRow.getMenuView() != null) {
+            mMenuRow.onHeightUpdate();
+        }
+        updateContentShiftHeight();
+        if (mLayoutListener != null) {
+            mLayoutListener.onLayout();
+        }
+    }
+
+    /**
+     * Updates the content shift height such that the header is completely hidden when coming from
+     * the top.
+     */
+    private void updateContentShiftHeight() {
+        NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
+        if (notificationHeader != null) {
+            CachingIconView icon = notificationHeader.getIcon();
+            mIconTransformContentShift = getRelativeTopPadding(icon) + icon.getHeight();
+        } else {
+            mIconTransformContentShift = mIconTransformContentShiftNoIcon;
+        }
+    }
+
+    @Override
+    public void notifyHeightChanged(boolean needsAnimation) {
+        super.notifyHeightChanged(needsAnimation);
+        getShowingLayout().requestSelectLayout(needsAnimation || isUserLocked());
+    }
+
+    public void setSensitive(boolean sensitive, boolean hideSensitive) {
+        mSensitive = sensitive;
+        mSensitiveHiddenInGeneral = hideSensitive;
+    }
+
+    @Override
+    public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
+        mHideSensitiveForIntrinsicHeight = hideSensitive;
+        if (mIsSummaryWithChildren) {
+            List<ExpandableNotificationRow> notificationChildren =
+                    mChildrenContainer.getNotificationChildren();
+            for (int i = 0; i < notificationChildren.size(); i++) {
+                ExpandableNotificationRow child = notificationChildren.get(i);
+                child.setHideSensitiveForIntrinsicHeight(hideSensitive);
+            }
+        }
+    }
+
+    @Override
+    public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
+            long duration) {
+        if (getVisibility() == GONE) {
+            // If we are GONE, the hideSensitive parameter will not be calculated and always be
+            // false, which is incorrect, let's wait until a real call comes in later.
+            return;
+        }
+        boolean oldShowingPublic = mShowingPublic;
+        mShowingPublic = mSensitive && hideSensitive;
+        if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
+            return;
+        }
+
+        // bail out if no public version
+        if (mPublicLayout.getChildCount() == 0) return;
+
+        if (!animated) {
+            mPublicLayout.animate().cancel();
+            mPrivateLayout.animate().cancel();
+            if (mChildrenContainer != null) {
+                mChildrenContainer.animate().cancel();
+                mChildrenContainer.setAlpha(1f);
+            }
+            mPublicLayout.setAlpha(1f);
+            mPrivateLayout.setAlpha(1f);
+            mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
+            updateChildrenVisibility();
+        } else {
+            animateShowingPublic(delay, duration, mShowingPublic);
+        }
+        NotificationContentView showingLayout = getShowingLayout();
+        showingLayout.updateBackgroundColor(animated);
+        mPrivateLayout.updateExpandButtons(isExpandable());
+        updateShelfIconColor();
+        showingLayout.setDark(isDark(), false /* animate */, 0 /* delay */);
+        mShowingPublicInitialized = true;
+    }
+
+    private void animateShowingPublic(long delay, long duration, boolean showingPublic) {
+        View[] privateViews = mIsSummaryWithChildren
+                ? new View[] {mChildrenContainer}
+                : new View[] {mPrivateLayout};
+        View[] publicViews = new View[] {mPublicLayout};
+        View[] hiddenChildren = showingPublic ? privateViews : publicViews;
+        View[] shownChildren = showingPublic ? publicViews : privateViews;
+        for (final View hiddenView : hiddenChildren) {
+            hiddenView.setVisibility(View.VISIBLE);
+            hiddenView.animate().cancel();
+            hiddenView.animate()
+                    .alpha(0f)
+                    .setStartDelay(delay)
+                    .setDuration(duration)
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            hiddenView.setVisibility(View.INVISIBLE);
+                        }
+                    });
+        }
+        for (View showView : shownChildren) {
+            showView.setVisibility(View.VISIBLE);
+            showView.setAlpha(0f);
+            showView.animate().cancel();
+            showView.animate()
+                    .alpha(1f)
+                    .setStartDelay(delay)
+                    .setDuration(duration);
+        }
+    }
+
+    @Override
+    public boolean mustStayOnScreen() {
+        return mIsHeadsUp && mMustStayOnScreen;
+    }
+
+    /**
+     * @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 #isClearable()}.
+     */
+    public boolean canViewBeDismissed() {
+        return isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+    }
+
+    private boolean shouldShowPublic() {
+        return mSensitive && mHideSensitiveForIntrinsicHeight;
+    }
+
+    public void makeActionsVisibile() {
+        setUserExpanded(true, true);
+        if (isChildInGroup()) {
+            mGroupManager.setGroupExpanded(mStatusBarNotification, true);
+        }
+        notifyHeightChanged(false /* needsAnimation */);
+    }
+
+    public void setChildrenExpanded(boolean expanded, boolean animate) {
+        mChildrenExpanded = expanded;
+        if (mChildrenContainer != null) {
+            mChildrenContainer.setChildrenExpanded(expanded);
+        }
+        updateBackgroundForGroupState();
+        updateClickAndFocus();
+    }
+
+    public static void applyTint(View v, int color) {
+        int alpha;
+        if (color != 0) {
+            alpha = COLORED_DIVIDER_ALPHA;
+        } else {
+            color = 0xff000000;
+            alpha = DEFAULT_DIVIDER_ALPHA;
+        }
+        if (v.getBackground() instanceof ColorDrawable) {
+            ColorDrawable background = (ColorDrawable) v.getBackground();
+            background.mutate();
+            background.setColor(color);
+            background.setAlpha(alpha);
+        }
+    }
+
+    public int getMaxExpandHeight() {
+        return mPrivateLayout.getExpandHeight();
+    }
+
+
+    private int getHeadsUpHeight() {
+        return mPrivateLayout.getHeadsUpHeight();
+    }
+
+    public boolean areGutsExposed() {
+        return (mGuts != null && mGuts.isExposed());
+    }
+
+    @Override
+    public boolean isContentExpandable() {
+        if (mIsSummaryWithChildren && !shouldShowPublic()) {
+            return true;
+        }
+        NotificationContentView showingLayout = getShowingLayout();
+        return showingLayout.isContentExpandable();
+    }
+
+    @Override
+    protected View getContentView() {
+        if (mIsSummaryWithChildren && !shouldShowPublic()) {
+            return mChildrenContainer;
+        }
+        return getShowingLayout();
+    }
+
+    @Override
+    protected void onAppearAnimationFinished(boolean wasAppearing) {
+        super.onAppearAnimationFinished(wasAppearing);
+        if (wasAppearing) {
+            // During the animation the visible view might have changed, so let's make sure all
+            // alphas are reset
+            if (mChildrenContainer != null) {
+                mChildrenContainer.setAlpha(1.0f);
+                mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
+            }
+            for (NotificationContentView l : mLayouts) {
+                l.setAlpha(1.0f);
+                l.setLayerType(LAYER_TYPE_NONE, null);
+            }
+        }
+    }
+
+    @Override
+    public int getExtraBottomPadding() {
+        if (mIsSummaryWithChildren && isGroupExpanded()) {
+            return mIncreasedPaddingBetweenElements;
+        }
+        return 0;
+    }
+
+    @Override
+    public void setActualHeight(int height, boolean notifyListeners) {
+        boolean changed = height != getActualHeight();
+        super.setActualHeight(height, notifyListeners);
+        if (changed && isRemoved()) {
+            // TODO: remove this once we found the gfx bug for this.
+            // This is a hack since a removed view sometimes would just stay blank. it occured
+            // when sending yourself a message and then clicking on it.
+            ViewGroup parent = (ViewGroup) getParent();
+            if (parent != null) {
+                parent.invalidate();
+            }
+        }
+        if (mGuts != null && mGuts.isExposed()) {
+            mGuts.setActualHeight(height);
+            return;
+        }
+        int contentHeight = Math.max(getMinHeight(), height);
+        for (NotificationContentView l : mLayouts) {
+            l.setContentHeight(contentHeight);
+        }
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.setActualHeight(height);
+        }
+        if (mGuts != null) {
+            mGuts.setActualHeight(height);
+        }
+        if (mMenuRow.getMenuView() != null) {
+            mMenuRow.onHeightUpdate();
+        }
+    }
+
+    @Override
+    public int getMaxContentHeight() {
+        if (mIsSummaryWithChildren && !shouldShowPublic()) {
+            return mChildrenContainer.getMaxContentHeight();
+        }
+        NotificationContentView showingLayout = getShowingLayout();
+        return showingLayout.getMaxHeight();
+    }
+
+    @Override
+    public int getMinHeight(boolean ignoreTemporaryStates) {
+        if (!ignoreTemporaryStates && mGuts != null && mGuts.isExposed()) {
+            return mGuts.getIntrinsicHeight();
+        } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp
+                && mHeadsUpManager.isTrackingHeadsUp()) {
+                return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
+        } else if (mIsSummaryWithChildren && !isGroupExpanded() && !shouldShowPublic()) {
+            return mChildrenContainer.getMinHeight();
+        } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) {
+            return getHeadsUpHeight();
+        }
+        NotificationContentView showingLayout = getShowingLayout();
+        return showingLayout.getMinHeight();
+    }
+
+    @Override
+    public int getCollapsedHeight() {
+        if (mIsSummaryWithChildren && !shouldShowPublic()) {
+            return mChildrenContainer.getCollapsedHeight();
+        }
+        return getMinHeight();
+    }
+
+    @Override
+    public void setClipTopAmount(int clipTopAmount) {
+        super.setClipTopAmount(clipTopAmount);
+        for (NotificationContentView l : mLayouts) {
+            l.setClipTopAmount(clipTopAmount);
+        }
+        if (mGuts != null) {
+            mGuts.setClipTopAmount(clipTopAmount);
+        }
+    }
+
+    @Override
+    public void setClipBottomAmount(int clipBottomAmount) {
+        if (mExpandAnimationRunning) {
+            return;
+        }
+        if (clipBottomAmount != mClipBottomAmount) {
+            super.setClipBottomAmount(clipBottomAmount);
+            for (NotificationContentView l : mLayouts) {
+                l.setClipBottomAmount(clipBottomAmount);
+            }
+            if (mGuts != null) {
+                mGuts.setClipBottomAmount(clipBottomAmount);
+            }
+        }
+        if (mChildrenContainer != null && !mChildIsExpanding) {
+            // We have to update this even if it hasn't changed, since the children locations can
+            // have changed
+            mChildrenContainer.setClipBottomAmount(clipBottomAmount);
+        }
+    }
+
+    public NotificationContentView getShowingLayout() {
+        return shouldShowPublic() ? mPublicLayout : mPrivateLayout;
+    }
+
+    public void setLegacy(boolean legacy) {
+        for (NotificationContentView l : mLayouts) {
+            l.setLegacy(legacy);
+        }
+    }
+
+    @Override
+    protected void updateBackgroundTint() {
+        super.updateBackgroundTint();
+        updateBackgroundForGroupState();
+        if (mIsSummaryWithChildren) {
+            List<ExpandableNotificationRow> notificationChildren =
+                    mChildrenContainer.getNotificationChildren();
+            for (int i = 0; i < notificationChildren.size(); i++) {
+                ExpandableNotificationRow child = notificationChildren.get(i);
+                child.updateBackgroundForGroupState();
+            }
+        }
+    }
+
+    /**
+     * Called when a group has finished animating from collapsed or expanded state.
+     */
+    public void onFinishedExpansionChange() {
+        mGroupExpansionChanging = false;
+        updateBackgroundForGroupState();
+    }
+
+    /**
+     * Updates the parent and children backgrounds in a group based on the expansion state.
+     */
+    public void updateBackgroundForGroupState() {
+        if (mIsSummaryWithChildren) {
+            // Only when the group has finished expanding do we hide its background.
+            mShowNoBackground = !mShowGroupBackgroundWhenExpanded && isGroupExpanded()
+                    && !isGroupExpansionChanging() && !isUserLocked();
+            mChildrenContainer.updateHeaderForExpansion(mShowNoBackground);
+            List<ExpandableNotificationRow> children = mChildrenContainer.getNotificationChildren();
+            for (int i = 0; i < children.size(); i++) {
+                children.get(i).updateBackgroundForGroupState();
+            }
+        } else if (isChildInGroup()) {
+            final int childColor = getShowingLayout().getBackgroundColorForExpansionState();
+            // Only show a background if the group is expanded OR if it is expanding / collapsing
+            // and has a custom background color.
+            final boolean showBackground = isGroupExpanded()
+                    || ((mNotificationParent.isGroupExpansionChanging()
+                    || mNotificationParent.isUserLocked()) && childColor != 0);
+            mShowNoBackground = !showBackground;
+        } else {
+            // Only children or parents ever need no background.
+            mShowNoBackground = false;
+        }
+        updateOutline();
+        updateBackground();
+    }
+
+    public int getPositionOfChild(ExpandableNotificationRow childRow) {
+        if (mIsSummaryWithChildren) {
+            return mChildrenContainer.getPositionInLinearLayout(childRow);
+        }
+        return 0;
+    }
+
+    public void setExpansionLogger(ExpansionLogger logger, String key) {
+        mLogger = logger;
+        mLoggingKey = key;
+    }
+
+    public void onExpandedByGesture(boolean userExpanded) {
+        int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
+        if (mGroupManager.isSummaryOfGroup(getStatusBarNotification())) {
+            event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER;
+        }
+        MetricsLogger.action(mContext, event, userExpanded);
+    }
+
+    @Override
+    public float getIncreasedPaddingAmount() {
+        if (mIsSummaryWithChildren) {
+            if (isGroupExpanded()) {
+                return 1.0f;
+            } else if (isUserLocked()) {
+                return mChildrenContainer.getIncreasedPaddingAmount();
+            }
+        } else if (isColorized() && (!mIsLowPriority || isExpanded())) {
+            return -1.0f;
+        }
+        return 0.0f;
+    }
+
+    private boolean isColorized() {
+        return mIsColorized && mBgTint != NO_COLOR;
+    }
+
+    @Override
+    protected boolean disallowSingleClick(MotionEvent event) {
+        if (areGutsExposed()) {
+            return false;
+        }
+        float x = event.getX();
+        float y = event.getY();
+        NotificationHeaderView header = getVisibleNotificationHeader();
+        if (header != null && header.isInTouchRect(x - getTranslation(), y)) {
+            return true;
+        }
+        if ((!mIsSummaryWithChildren || shouldShowPublic())
+                && getShowingLayout().disallowSingleClick(x, y)) {
+            return true;
+        }
+        return super.disallowSingleClick(event);
+    }
+
+    private void onExpansionChanged(boolean userAction, boolean wasExpanded) {
+        boolean nowExpanded = isExpanded();
+        if (mIsSummaryWithChildren && (!mIsLowPriority || wasExpanded)) {
+            nowExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
+        }
+        if (nowExpanded != wasExpanded) {
+            updateShelfIconColor();
+            if (mLogger != null) {
+                mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded);
+            }
+            if (mIsSummaryWithChildren) {
+                mChildrenContainer.onExpansionChanged();
+            }
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfoInternal(info);
+        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+        if (canViewBeDismissed()) {
+            info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
+        }
+        boolean expandable = shouldShowPublic();
+        boolean isExpanded = false;
+        if (!expandable) {
+            if (mIsSummaryWithChildren) {
+                expandable = true;
+                if (!mIsLowPriority || isExpanded()) {
+                    isExpanded = isGroupExpanded();
+                }
+            } else {
+                expandable = mPrivateLayout.isContentExpandable();
+                isExpanded = isExpanded();
+            }
+        }
+        if (expandable) {
+            if (isExpanded) {
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
+            } else {
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
+            }
+        }
+        NotificationMenuRowPlugin provider = getProvider();
+        if (provider != null) {
+            MenuItem snoozeMenu = provider.getSnoozeMenuItem(getContext());
+            if (snoozeMenu != null) {
+                AccessibilityAction action = new AccessibilityAction(R.id.action_snooze,
+                    getContext().getResources()
+                        .getString(R.string.notification_menu_snooze_action));
+                info.addAction(action);
+            }
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+        if (super.performAccessibilityActionInternal(action, arguments)) {
+            return true;
+        }
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_DISMISS:
+                performDismissWithBlockingHelper(true /* fromAccessibility */);
+                return true;
+            case AccessibilityNodeInfo.ACTION_COLLAPSE:
+            case AccessibilityNodeInfo.ACTION_EXPAND:
+                mExpandClickListener.onClick(this);
+                return true;
+            case AccessibilityNodeInfo.ACTION_LONG_CLICK:
+                doLongClickCallback();
+                return true;
+            case R.id.action_snooze:
+                NotificationMenuRowPlugin provider = getProvider();
+                if (provider == null) {
+                    provider = createMenu();
+                }
+                MenuItem snoozeMenu = provider.getSnoozeMenuItem(getContext());
+                if (snoozeMenu != null) {
+                    doLongClickCallback(getWidth() / 2, getHeight() / 2, snoozeMenu);
+                }
+                return true;
+        }
+        return false;
+    }
+
+    public boolean shouldRefocusOnDismiss() {
+        return mRefocusOnDismiss || isAccessibilityFocused();
+    }
+
+    public interface OnExpandClickListener {
+        void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
+    }
+
+    @Override
+    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+        mNotificationViewState = new NotificationViewState(stackScrollState);
+        return mNotificationViewState;
+    }
+
+    public NotificationViewState getViewState() {
+        return mNotificationViewState;
+    }
+
+    @Override
+    public boolean isAboveShelf() {
+        return !isOnKeyguard()
+                && (mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf)
+                || mExpandAnimationRunning || mChildIsExpanding);
+    }
+
+    public void setShowAmbient(boolean showAmbient) {
+        if (showAmbient != mShowAmbient) {
+            mShowAmbient = showAmbient;
+            if (mChildrenContainer != null) {
+                mChildrenContainer.notifyShowAmbientChanged();
+            }
+            notifyHeightChanged(false /* needsAnimation */);
+        }
+    }
+
+    @Override
+    public boolean topAmountNeedsClipping() {
+        if (isGroupExpanded()) {
+            return true;
+        }
+        if (isGroupExpansionChanging()) {
+            return true;
+        }
+        if (getShowingLayout().shouldClipToRounding(true /* topRounded */,
+                false /* bottomRounded */)) {
+            return true;
+        }
+        if (mGuts != null && mGuts.getAlpha() != 0.0f) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean childNeedsClipping(View child) {
+        if (child instanceof NotificationContentView) {
+            NotificationContentView contentView = (NotificationContentView) child;
+            if (isClippingNeeded()) {
+                return true;
+            } else if (!hasNoRounding()
+                    && contentView.shouldClipToRounding(getCurrentTopRoundness() != 0.0f,
+                    getCurrentBottomRoundness() != 0.0f)) {
+                return true;
+            }
+        } else if (child == mChildrenContainer) {
+            if (!mChildIsExpanding && (isClippingNeeded() || !hasNoRounding())) {
+                return true;
+            }
+        } else if (child instanceof NotificationGuts) {
+            return !hasNoRounding();
+        }
+        return super.childNeedsClipping(child);
+    }
+
+    @Override
+    protected void applyRoundness() {
+        super.applyRoundness();
+        applyChildrenRoundness();
+    }
+
+    private void applyChildrenRoundness() {
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.setCurrentBottomRoundness(getCurrentBottomRoundness());
+        }
+    }
+
+    @Override
+    public Path getCustomClipPath(View child) {
+        if (child instanceof NotificationGuts) {
+            return getClipPath(true /* ignoreTranslation */);
+        }
+        return super.getCustomClipPath(child);
+    }
+
+    private boolean hasNoRounding() {
+        return getCurrentBottomRoundness() == 0.0f && getCurrentTopRoundness() == 0.0f;
+    }
+
+    public boolean isShowingAmbient() {
+        return mShowAmbient;
+    }
+
+    public void setAboveShelf(boolean aboveShelf) {
+        boolean wasAboveShelf = isAboveShelf();
+        mAboveShelf = aboveShelf;
+        if (isAboveShelf() != wasAboveShelf) {
+            mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
+        }
+    }
+
+    public static class NotificationViewState extends ExpandableViewState {
+
+        private final StackScrollState mOverallState;
+
+
+        private NotificationViewState(StackScrollState stackScrollState) {
+            mOverallState = stackScrollState;
+        }
+
+        @Override
+        public void applyToView(View view) {
+            if (view instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+                if (row.isExpandAnimationRunning()) {
+                    return;
+                }
+                handleFixedTranslationZ(row);
+                super.applyToView(view);
+                row.applyChildrenState(mOverallState);
+            }
+        }
+
+        private void handleFixedTranslationZ(ExpandableNotificationRow row) {
+            if (row.hasExpandingChild()) {
+                zTranslation = row.getTranslationZ();
+                clipTopAmount = row.getClipTopAmount();
+            }
+        }
+
+        @Override
+        protected void onYTranslationAnimationFinished(View view) {
+            super.onYTranslationAnimationFinished(view);
+            if (view instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+                if (row.isHeadsUpAnimatingAway()) {
+                    row.setHeadsUpAnimatingAway(false);
+                }
+            }
+        }
+
+        @Override
+        public void animateTo(View child, AnimationProperties properties) {
+            if (child instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                if (row.isExpandAnimationRunning()) {
+                    return;
+                }
+                handleFixedTranslationZ(row);
+                super.animateTo(child, properties);
+                row.startChildAnimation(mOverallState, properties);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    protected void setChildrenContainer(NotificationChildrenContainer childrenContainer) {
+        mChildrenContainer = childrenContainer;
+    }
+
+    @VisibleForTesting
+    protected void setPrivateLayout(NotificationContentView privateLayout) {
+        mPrivateLayout = privateLayout;
+    }
+
+    @VisibleForTesting
+    protected void setPublicLayout(NotificationContentView publicLayout) {
+        mPublicLayout = publicLayout;
+    }
+
+    /**
+     * Equivalent to View.OnLongClickListener with coordinates
+     */
+    public interface LongPressListener {
+        /**
+         * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
+         * @return whether the longpress was handled
+         */
+        boolean onLongPress(View v, int x, int y, MenuItem item);
+    }
+
+    /**
+     * Equivalent to View.OnClickListener with coordinates
+     */
+    public interface OnAppOpsClickListener {
+        /**
+         * Equivalent to {@link View.OnClickListener#onClick(View)} with coordinates
+         * @return whether the click was handled
+         */
+        boolean onClick(View v, int x, int y, MenuItem item);
+    }
+
+    /**
+     * Background task for executing IPCs to check if the notification is a system notification. The
+     * output is used for both the blocking helper and the notification info.
+     */
+    private class SystemNotificationAsyncTask extends AsyncTask<Void, Void, Boolean> {
+
+        @Override
+        protected Boolean doInBackground(Void... voids) {
+            return isSystemNotification(mContext, mStatusBarNotification);
+        }
+
+        @Override
+        protected void onPostExecute(Boolean result) {
+            if (mEntry != null) {
+                mEntry.mIsSystemNotification = result;
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
new file mode 100644
index 0000000..a7aed5f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -0,0 +1,432 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Outline;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+
+/**
+ * Like {@link ExpandableView}, but setting an outline for the height and clipping.
+ */
+public abstract class ExpandableOutlineView extends ExpandableView {
+
+    private static final AnimatableProperty TOP_ROUNDNESS = AnimatableProperty.from(
+            "topRoundness",
+            ExpandableOutlineView::setTopRoundnessInternal,
+            ExpandableOutlineView::getCurrentTopRoundness,
+            R.id.top_roundess_animator_tag,
+            R.id.top_roundess_animator_end_tag,
+            R.id.top_roundess_animator_start_tag);
+    private static final AnimatableProperty BOTTOM_ROUNDNESS = AnimatableProperty.from(
+            "bottomRoundness",
+            ExpandableOutlineView::setBottomRoundnessInternal,
+            ExpandableOutlineView::getCurrentBottomRoundness,
+            R.id.bottom_roundess_animator_tag,
+            R.id.bottom_roundess_animator_end_tag,
+            R.id.bottom_roundess_animator_start_tag);
+    private static final AnimationProperties ROUNDNESS_PROPERTIES =
+            new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+    private static final Path EMPTY_PATH = new Path();
+
+    private final Rect mOutlineRect = new Rect();
+    private final Path mClipPath = new Path();
+    private boolean mCustomOutline;
+    private float mOutlineAlpha = -1f;
+    protected float mOutlineRadius;
+    private boolean mAlwaysRoundBothCorners;
+    private Path mTmpPath = new Path();
+    private float mCurrentBottomRoundness;
+    private float mCurrentTopRoundness;
+    private float mBottomRoundness;
+    private float mTopRoundness;
+    private int mBackgroundTop;
+
+    /**
+     * {@code true} if the children views of the {@link ExpandableOutlineView} are translated when
+     * it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself.
+     */
+    protected boolean mShouldTranslateContents;
+    private boolean mTopAmountRounded;
+    private float mDistanceToTopRoundness = -1;
+    private float mExtraWidthForClipping;
+    private int mMinimumHeightForClipping = 0;
+
+    private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
+        @Override
+        public void getOutline(View view, Outline outline) {
+            if (!mCustomOutline && mCurrentTopRoundness == 0.0f
+                    && mCurrentBottomRoundness == 0.0f && !mAlwaysRoundBothCorners
+                    && !mTopAmountRounded) {
+                int translation = mShouldTranslateContents ? (int) getTranslation() : 0;
+                int left = Math.max(translation, 0);
+                int top = mClipTopAmount + mBackgroundTop;
+                int right = getWidth() + Math.min(translation, 0);
+                int bottom = Math.max(getActualHeight() - mClipBottomAmount, top);
+                outline.setRect(left, top, right, bottom);
+            } else {
+                Path clipPath = getClipPath(false /* ignoreTranslation */);
+                if (clipPath != null && clipPath.isConvex()) {
+                    // The path might not be convex in border cases where the view is small and
+                    // clipped
+                    outline.setConvexPath(clipPath);
+                }
+            }
+            outline.setAlpha(mOutlineAlpha);
+        }
+    };
+
+    protected Path getClipPath(boolean ignoreTranslation) {
+        int left;
+        int top;
+        int right;
+        int bottom;
+        int height;
+        float topRoundness = mAlwaysRoundBothCorners
+                ? mOutlineRadius : getCurrentBackgroundRadiusTop();
+        if (!mCustomOutline) {
+            int translation = mShouldTranslateContents && !ignoreTranslation
+                    ? (int) getTranslation() : 0;
+            left = Math.max(translation, 0);
+            top = mClipTopAmount + mBackgroundTop;
+            right = getWidth() + Math.min(translation, 0);
+            // If the top is rounded we want the bottom to be at most at the top roundness, in order
+            // to avoid the shadow changing when scrolling up.
+            bottom = Math.max(getActualHeight() - mClipBottomAmount, (int) (top + topRoundness));
+        } else {
+            left = mOutlineRect.left;
+            top = mOutlineRect.top;
+            right = mOutlineRect.right;
+            bottom = mOutlineRect.bottom;
+        }
+        height = bottom - top;
+        if (height == 0) {
+            return EMPTY_PATH;
+        }
+        float bottomRoundness = mAlwaysRoundBothCorners
+                ? mOutlineRadius : getCurrentBackgroundRadiusBottom();
+        if (topRoundness + bottomRoundness > height) {
+            float overShoot = topRoundness + bottomRoundness - height;
+            topRoundness -= overShoot * mCurrentTopRoundness
+                    / (mCurrentTopRoundness + mCurrentBottomRoundness);
+            bottomRoundness -= overShoot * mCurrentBottomRoundness
+                    / (mCurrentTopRoundness + mCurrentBottomRoundness);
+        }
+        getRoundedRectPath(left, top, right, bottom, topRoundness,
+                bottomRoundness, mTmpPath);
+        return mTmpPath;
+    }
+
+    public static void getRoundedRectPath(int left, int top, int right, int bottom,
+            float topRoundness, float bottomRoundness, Path outPath) {
+        outPath.reset();
+        int width = right - left;
+        float topRoundnessX = topRoundness;
+        float bottomRoundnessX = bottomRoundness;
+        topRoundnessX = Math.min(width / 2, topRoundnessX);
+        bottomRoundnessX = Math.min(width / 2, bottomRoundnessX);
+        if (topRoundness > 0.0f) {
+            outPath.moveTo(left, top + topRoundness);
+            outPath.quadTo(left, top, left + topRoundnessX, top);
+            outPath.lineTo(right - topRoundnessX, top);
+            outPath.quadTo(right, top, right, top + topRoundness);
+        } else {
+            outPath.moveTo(left, top);
+            outPath.lineTo(right, top);
+        }
+        if (bottomRoundness > 0.0f) {
+            outPath.lineTo(right, bottom - bottomRoundness);
+            outPath.quadTo(right, bottom, right - bottomRoundnessX, bottom);
+            outPath.lineTo(left + bottomRoundnessX, bottom);
+            outPath.quadTo(left, bottom, left, bottom - bottomRoundness);
+        } else {
+            outPath.lineTo(right, bottom);
+            outPath.lineTo(left, bottom);
+        }
+        outPath.close();
+    }
+
+    public ExpandableOutlineView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setOutlineProvider(mProvider);
+        initDimens();
+    }
+
+    @Override
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        canvas.save();
+        Path intersectPath = null;
+        if (mTopAmountRounded && topAmountNeedsClipping()) {
+            int left = (int) (- mExtraWidthForClipping / 2.0f);
+            int top = (int) (mClipTopAmount - mDistanceToTopRoundness);
+            int right = getWidth() + (int) (mExtraWidthForClipping + left);
+            int bottom = (int) Math.max(mMinimumHeightForClipping,
+                    Math.max(getActualHeight() - mClipBottomAmount, top + mOutlineRadius));
+            ExpandableOutlineView.getRoundedRectPath(left, top, right, bottom, mOutlineRadius,
+                    0.0f,
+                    mClipPath);
+            intersectPath = mClipPath;
+        }
+        boolean clipped = false;
+        if (childNeedsClipping(child)) {
+            Path clipPath = getCustomClipPath(child);
+            if (clipPath == null) {
+                clipPath = getClipPath(false /* ignoreTranslation */);
+            }
+            if (clipPath != null) {
+                if (intersectPath != null) {
+                    clipPath.op(intersectPath, Path.Op.INTERSECT);
+                }
+                canvas.clipPath(clipPath);
+                clipped = true;
+            }
+        }
+        if (!clipped && intersectPath != null) {
+            canvas.clipPath(intersectPath);
+        }
+        boolean result = super.drawChild(canvas, child, drawingTime);
+        canvas.restore();
+        return result;
+    }
+
+    public void setExtraWidthForClipping(float extraWidthForClipping) {
+        mExtraWidthForClipping = extraWidthForClipping;
+    }
+
+    public void setMinimumHeightForClipping(int minimumHeightForClipping) {
+        mMinimumHeightForClipping = minimumHeightForClipping;
+    }
+
+    @Override
+    public void setDistanceToTopRoundness(float distanceToTopRoundness) {
+        super.setDistanceToTopRoundness(distanceToTopRoundness);
+        if (distanceToTopRoundness != mDistanceToTopRoundness) {
+            mTopAmountRounded = distanceToTopRoundness >= 0;
+            mDistanceToTopRoundness = distanceToTopRoundness;
+            applyRoundness();
+        }
+    }
+
+    protected boolean childNeedsClipping(View child) {
+        return false;
+    }
+
+    public boolean topAmountNeedsClipping() {
+        return true;
+    }
+
+    protected boolean isClippingNeeded() {
+        return mAlwaysRoundBothCorners || mCustomOutline || getTranslation() != 0 ;
+    }
+
+    private void initDimens() {
+        Resources res = getResources();
+        mShouldTranslateContents =
+                res.getBoolean(R.bool.config_translateNotificationContentsOnSwipe);
+        mOutlineRadius = res.getDimension(R.dimen.notification_shadow_radius);
+        mAlwaysRoundBothCorners = res.getBoolean(R.bool.config_clipNotificationsToOutline);
+        if (!mAlwaysRoundBothCorners) {
+            mOutlineRadius = res.getDimensionPixelSize(
+                    Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
+        }
+        setClipToOutline(mAlwaysRoundBothCorners);
+    }
+
+    /**
+     * Set the topRoundness of this view.
+     * @return Whether the roundness was changed.
+     */
+    public boolean setTopRoundness(float topRoundness, boolean animate) {
+        if (mTopRoundness != topRoundness) {
+            mTopRoundness = topRoundness;
+            PropertyAnimator.setProperty(this, TOP_ROUNDNESS, topRoundness,
+                    ROUNDNESS_PROPERTIES, animate);
+            return true;
+        }
+        return false;
+    }
+
+    protected void applyRoundness() {
+        invalidateOutline();
+        invalidate();
+    }
+
+    public float getCurrentBackgroundRadiusTop() {
+        // If this view is top amount notification view, it should always has round corners on top.
+        // It will be applied with applyRoundness()
+        if (mTopAmountRounded) {
+            return mOutlineRadius;
+        }
+        return mCurrentTopRoundness * mOutlineRadius;
+    }
+
+    public float getCurrentTopRoundness() {
+        return mCurrentTopRoundness;
+    }
+
+    public float getCurrentBottomRoundness() {
+        return mCurrentBottomRoundness;
+    }
+
+    protected float getCurrentBackgroundRadiusBottom() {
+        return mCurrentBottomRoundness * mOutlineRadius;
+    }
+
+    /**
+     * Set the bottom roundness of this view.
+     * @return Whether the roundness was changed.
+     */
+    public boolean setBottomRoundness(float bottomRoundness, boolean animate) {
+        if (mBottomRoundness != bottomRoundness) {
+            mBottomRoundness = bottomRoundness;
+            PropertyAnimator.setProperty(this, BOTTOM_ROUNDNESS, bottomRoundness,
+                    ROUNDNESS_PROPERTIES, animate);
+            return true;
+        }
+        return false;
+    }
+
+    protected void setBackgroundTop(int backgroundTop) {
+        if (mBackgroundTop != backgroundTop) {
+            mBackgroundTop = backgroundTop;
+            invalidateOutline();
+        }
+    }
+
+    private void setTopRoundnessInternal(float topRoundness) {
+        mCurrentTopRoundness = topRoundness;
+        applyRoundness();
+    }
+
+    private void setBottomRoundnessInternal(float bottomRoundness) {
+        mCurrentBottomRoundness = bottomRoundness;
+        applyRoundness();
+    }
+
+    public void onDensityOrFontScaleChanged() {
+        initDimens();
+        applyRoundness();
+    }
+
+    @Override
+    public void setActualHeight(int actualHeight, boolean notifyListeners) {
+        int previousHeight = getActualHeight();
+        super.setActualHeight(actualHeight, notifyListeners);
+        if (previousHeight != actualHeight) {
+            applyRoundness();
+        }
+    }
+
+    @Override
+    public void setClipTopAmount(int clipTopAmount) {
+        int previousAmount = getClipTopAmount();
+        super.setClipTopAmount(clipTopAmount);
+        if (previousAmount != clipTopAmount) {
+            applyRoundness();
+        }
+    }
+
+    @Override
+    public void setClipBottomAmount(int clipBottomAmount) {
+        int previousAmount = getClipBottomAmount();
+        super.setClipBottomAmount(clipBottomAmount);
+        if (previousAmount != clipBottomAmount) {
+            applyRoundness();
+        }
+    }
+
+    protected void setOutlineAlpha(float alpha) {
+        if (alpha != mOutlineAlpha) {
+            mOutlineAlpha = alpha;
+            applyRoundness();
+        }
+    }
+
+    @Override
+    public float getOutlineAlpha() {
+        return mOutlineAlpha;
+    }
+
+    protected void setOutlineRect(RectF rect) {
+        if (rect != null) {
+            setOutlineRect(rect.left, rect.top, rect.right, rect.bottom);
+        } else {
+            mCustomOutline = false;
+            applyRoundness();
+        }
+    }
+
+    @Override
+    public int getOutlineTranslation() {
+        return mCustomOutline ? mOutlineRect.left : (int) getTranslation();
+    }
+
+    public void updateOutline() {
+        if (mCustomOutline) {
+            return;
+        }
+        boolean hasOutline = needsOutline();
+        setOutlineProvider(hasOutline ? mProvider : null);
+    }
+
+    /**
+     * @return Whether the view currently needs an outline. This is usually {@code false} in case
+     * it doesn't have a background.
+     */
+    protected boolean needsOutline() {
+        if (isChildInGroup()) {
+            return isGroupExpanded() && !isGroupExpansionChanging();
+        } else if (isSummaryWithChildren()) {
+            return !isGroupExpanded() || isGroupExpansionChanging();
+        }
+        return true;
+    }
+
+    public boolean isOutlineShowing() {
+        ViewOutlineProvider op = getOutlineProvider();
+        return op != null;
+    }
+
+    protected void setOutlineRect(float left, float top, float right, float bottom) {
+        mCustomOutline = true;
+
+        mOutlineRect.set((int) left, (int) top, (int) right, (int) bottom);
+
+        // Outlines need to be at least 1 dp
+        mOutlineRect.bottom = (int) Math.max(top, mOutlineRect.bottom);
+        mOutlineRect.right = (int) Math.max(left, mOutlineRect.right);
+        applyRoundness();
+    }
+
+    public Path getCustomClipPath(View child) {
+        return null;
+    }
+}
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
new file mode 100644
index 0000000..46019e3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -0,0 +1,581 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.StackScrollState;
+
+import java.util.ArrayList;
+
+/**
+ * An abstract view for expandable views.
+ */
+public abstract class ExpandableView extends FrameLayout {
+
+    public static final float NO_ROUNDNESS = -1;
+    protected OnHeightChangedListener mOnHeightChangedListener;
+    private int mActualHeight;
+    protected int mClipTopAmount;
+    protected int mClipBottomAmount;
+    private boolean mDark;
+    private ArrayList<View> mMatchParentViews = new ArrayList<View>();
+    private static Rect mClipRect = new Rect();
+    private boolean mWillBeGone;
+    private int mMinClipTopAmount = 0;
+    private boolean mClipToActualHeight = true;
+    private boolean mChangingPosition = false;
+    private ViewGroup mTransientContainer;
+    private boolean mInShelf;
+    private boolean mTransformingInShelf;
+
+    public ExpandableView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int givenSize = MeasureSpec.getSize(heightMeasureSpec);
+        final int viewHorizontalPadding = getPaddingStart() + getPaddingEnd();
+        int ownMaxHeight = Integer.MAX_VALUE;
+        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        if (heightMode != MeasureSpec.UNSPECIFIED && givenSize != 0) {
+            ownMaxHeight = Math.min(givenSize, ownMaxHeight);
+        }
+        int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
+        int maxChildHeight = 0;
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() == GONE) {
+                continue;
+            }
+            int childHeightSpec = newHeightSpec;
+            ViewGroup.LayoutParams layoutParams = child.getLayoutParams();
+            if (layoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) {
+                if (layoutParams.height >= 0) {
+                    // An actual height is set
+                    childHeightSpec = layoutParams.height > ownMaxHeight
+                        ? MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.EXACTLY)
+                        : MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
+                }
+                child.measure(getChildMeasureSpec(
+                        widthMeasureSpec, viewHorizontalPadding, layoutParams.width),
+                        childHeightSpec);
+                int childHeight = child.getMeasuredHeight();
+                maxChildHeight = Math.max(maxChildHeight, childHeight);
+            } else {
+                mMatchParentViews.add(child);
+            }
+        }
+        int ownHeight = heightMode == MeasureSpec.EXACTLY
+                ? givenSize : Math.min(ownMaxHeight, maxChildHeight);
+        newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY);
+        for (View child : mMatchParentViews) {
+            child.measure(getChildMeasureSpec(
+                    widthMeasureSpec, viewHorizontalPadding, child.getLayoutParams().width),
+                    newHeightSpec);
+        }
+        mMatchParentViews.clear();
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        setMeasuredDimension(width, ownHeight);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        updateClipping();
+    }
+
+    @Override
+    public boolean pointInView(float localX, float localY, float slop) {
+        float top = mClipTopAmount;
+        float bottom = mActualHeight;
+        return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
+                localY < (bottom + slop);
+    }
+
+    /**
+     * Sets the actual height of this notification. This is different than the laid out
+     * {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding.
+     *
+     * @param actualHeight The height of this notification.
+     * @param notifyListeners Whether the listener should be informed about the change.
+     */
+    public void setActualHeight(int actualHeight, boolean notifyListeners) {
+        mActualHeight = actualHeight;
+        updateClipping();
+        if (notifyListeners) {
+            notifyHeightChanged(false  /* needsAnimation */);
+        }
+    }
+
+    /**
+     * Set the distance to the top roundness, from where we should start clipping a value above
+     * or equal to 0 is the effective distance, and if a value below 0 is received, there should
+     * be no clipping.
+     */
+    public void setDistanceToTopRoundness(float distanceToTopRoundness) {
+    }
+
+    public void setActualHeight(int actualHeight) {
+        setActualHeight(actualHeight, true /* notifyListeners */);
+    }
+
+    /**
+     * See {@link #setActualHeight}.
+     *
+     * @return The current actual height of this notification.
+     */
+    public int getActualHeight() {
+        return mActualHeight;
+    }
+
+    public boolean isExpandAnimationRunning() {
+        return false;
+    }
+
+    /**
+     * @return The maximum height of this notification.
+     */
+    public int getMaxContentHeight() {
+        return getHeight();
+    }
+
+    /**
+     * @return The minimum content height of this notification. This also respects the temporary
+     * states of the view.
+     */
+    public int getMinHeight() {
+        return getMinHeight(false /* ignoreTemporaryStates */);
+    }
+
+    /**
+     * Get the minimum height of this view.
+     *
+     * @param ignoreTemporaryStates should temporary states be ignored like the guts or heads-up.
+     *
+     * @return The minimum height that this view needs.
+     */
+    public int getMinHeight(boolean ignoreTemporaryStates) {
+        return getHeight();
+    }
+
+    /**
+     * @return The collapsed height of this view. Note that this might be different
+     * than {@link #getMinHeight()} because some elements like groups may have different sizes when
+     * they are system expanded.
+     */
+    public int getCollapsedHeight() {
+        return getHeight();
+    }
+
+    /**
+     * Sets the notification as dimmed. The default implementation does nothing.
+     *
+     * @param dimmed Whether the notification should be dimmed.
+     * @param fade Whether an animation should be played to change the state.
+     */
+    public void setDimmed(boolean dimmed, boolean fade) {
+    }
+
+    /**
+     * Sets the notification as dark. The default implementation does nothing.
+     *
+     * @param dark Whether the notification should be dark.
+     * @param fade Whether an animation should be played to change the state.
+     * @param delay If fading, the delay of the animation.
+     */
+    public void setDark(boolean dark, boolean fade, long delay) {
+        mDark = dark;
+    }
+
+    public boolean isDark() {
+        return mDark;
+    }
+
+    public boolean isRemoved() {
+        return false;
+    }
+
+    /**
+     * See {@link #setHideSensitive}. This is a variant which notifies this view in advance about
+     * the upcoming state of hiding sensitive notifications. It gets called at the very beginning
+     * of a stack scroller update such that the updated intrinsic height (which is dependent on
+     * whether private or public layout is showing) gets taken into account into all layout
+     * calculations.
+     */
+    public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
+    }
+
+    /**
+     * Sets whether the notification should hide its private contents if it is sensitive.
+     */
+    public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
+            long duration) {
+    }
+
+    /**
+     * @return The desired notification height.
+     */
+    public int getIntrinsicHeight() {
+        return getHeight();
+    }
+
+    /**
+     * Sets the amount this view should be clipped from the top. This is used when an expanded
+     * notification is scrolling in the top or bottom stack.
+     *
+     * @param clipTopAmount The amount of pixels this view should be clipped from top.
+     */
+    public void setClipTopAmount(int clipTopAmount) {
+        mClipTopAmount = clipTopAmount;
+        updateClipping();
+    }
+
+    /**
+     * Set the amount the the notification is clipped on the bottom in addition to the regular
+     * clipping. This is mainly used to clip something in a non-animated way without changing the
+     * actual height of the notification and is purely visual.
+     *
+     * @param clipBottomAmount the amount to clip.
+     */
+    public void setClipBottomAmount(int clipBottomAmount) {
+        mClipBottomAmount = clipBottomAmount;
+        updateClipping();
+    }
+
+    public int getClipTopAmount() {
+        return mClipTopAmount;
+    }
+
+    public int getClipBottomAmount() {
+        return mClipBottomAmount;
+    }
+
+    public void setOnHeightChangedListener(OnHeightChangedListener listener) {
+        mOnHeightChangedListener = listener;
+    }
+
+    /**
+     * @return Whether we can expand this views content.
+     */
+    public boolean isContentExpandable() {
+        return false;
+    }
+
+    public void notifyHeightChanged(boolean needsAnimation) {
+        if (mOnHeightChangedListener != null) {
+            mOnHeightChangedListener.onHeightChanged(this, needsAnimation);
+        }
+    }
+
+    public boolean isTransparent() {
+        return false;
+    }
+
+    /**
+     * Perform a remove animation on this view.
+     * @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.
+     * @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.
+     */
+    public abstract void performRemoveAnimation(long duration,
+            long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+            Runnable onFinishedRunnable,
+            AnimatorListenerAdapter animationListener);
+
+    public abstract void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear);
+
+    /**
+     * Set the notification appearance to be below the speed bump.
+     * @param below true if it is below.
+     */
+    public void setBelowSpeedBump(boolean below) {
+    }
+
+    public int getPinnedHeadsUpHeight() {
+        return getIntrinsicHeight();
+    }
+
+
+    /**
+     * Sets the translation of the view.
+     */
+    public void setTranslation(float translation) {
+        setTranslationX(translation);
+    }
+
+    /**
+     * Gets the translation of the view.
+     */
+    public float getTranslation() {
+        return getTranslationX();
+    }
+
+    public void onHeightReset() {
+        if (mOnHeightChangedListener != null) {
+            mOnHeightChangedListener.onReset(this);
+        }
+    }
+
+    /**
+     * This method returns the drawing rect for the view which is different from the regular
+     * drawing rect, since we layout all children in the {@link NotificationStackScrollLayout} at
+     * position 0 and usually the translation is neglected. Since we are manually clipping this
+     * view,we also need to subtract the clipTopAmount from the top. This is needed in order to
+     * ensure that accessibility and focusing work correctly.
+     *
+     * @param outRect The (scrolled) drawing bounds of the view.
+     */
+    @Override
+    public void getDrawingRect(Rect outRect) {
+        super.getDrawingRect(outRect);
+        outRect.left += getTranslationX();
+        outRect.right += getTranslationX();
+        outRect.bottom = (int) (outRect.top + getTranslationY() + getActualHeight());
+        outRect.top += getTranslationY() + getClipTopAmount();
+    }
+
+    @Override
+    public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
+        super.getBoundsOnScreen(outRect, clipToParent);
+        if (getTop() + getTranslationY() < 0) {
+            // We got clipped to the parent here - make sure we undo that.
+            outRect.top += getTop() + getTranslationY();
+        }
+        outRect.bottom = outRect.top + getActualHeight();
+        outRect.top += getClipTopAmount();
+    }
+
+    public boolean isSummaryWithChildren() {
+        return false;
+    }
+
+    public boolean areChildrenExpanded() {
+        return false;
+    }
+
+    protected void updateClipping() {
+        if (mClipToActualHeight && shouldClipToActualHeight()) {
+            int top = getClipTopAmount();
+            mClipRect.set(0, top, getWidth(), Math.max(getActualHeight() + getExtraBottomPadding()
+                    - mClipBottomAmount, top));
+            setClipBounds(mClipRect);
+        } else {
+            setClipBounds(null);
+        }
+    }
+
+    public float getHeaderVisibleAmount() {
+        return 1.0f;
+    }
+
+    protected boolean shouldClipToActualHeight() {
+        return true;
+    }
+
+    public void setClipToActualHeight(boolean clipToActualHeight) {
+        mClipToActualHeight = clipToActualHeight;
+        updateClipping();
+    }
+
+    public boolean willBeGone() {
+        return mWillBeGone;
+    }
+
+    public void setWillBeGone(boolean willBeGone) {
+        mWillBeGone = willBeGone;
+    }
+
+    public int getMinClipTopAmount() {
+        return mMinClipTopAmount;
+    }
+
+    public void setMinClipTopAmount(int minClipTopAmount) {
+        mMinClipTopAmount = minClipTopAmount;
+    }
+
+    @Override
+    public void setLayerType(int layerType, Paint paint) {
+        if (hasOverlappingRendering()) {
+            super.setLayerType(layerType, paint);
+        }
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        // Otherwise it will be clipped
+        return super.hasOverlappingRendering() && getActualHeight() <= getHeight();
+    }
+
+    public float getShadowAlpha() {
+        return 0.0f;
+    }
+
+    public void setShadowAlpha(float shadowAlpha) {
+    }
+
+    /**
+     * @return an amount between -1 and 1 of increased padding that this child needs. 1 means it
+     * needs a full increased padding while -1 means it needs no padding at all. For 0.0f the normal
+     * padding is applied.
+     */
+    public float getIncreasedPaddingAmount() {
+        return 0.0f;
+    }
+
+    public boolean mustStayOnScreen() {
+        return false;
+    }
+
+    public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
+            int outlineTranslation) {
+    }
+
+    public float getOutlineAlpha() {
+        return 0.0f;
+    }
+
+    public int getOutlineTranslation() {
+        return 0;
+    }
+
+    public void setChangingPosition(boolean changingPosition) {
+        mChangingPosition = changingPosition;
+    }
+
+    public boolean isChangingPosition() {
+        return mChangingPosition;
+    }
+
+    public void setTransientContainer(ViewGroup transientContainer) {
+        mTransientContainer = transientContainer;
+    }
+
+    public ViewGroup getTransientContainer() {
+        return mTransientContainer;
+    }
+
+    /**
+     * @return padding used to alter how much of the view is clipped.
+     */
+    public int getExtraBottomPadding() {
+        return 0;
+    }
+
+    /**
+     * @return true if the group's expansion state is changing, false otherwise.
+     */
+    public boolean isGroupExpansionChanging() {
+        return false;
+    }
+
+    public boolean isGroupExpanded() {
+        return false;
+    }
+
+    public void setHeadsUpIsVisible() {
+    }
+
+    public boolean isChildInGroup() {
+        return false;
+    }
+
+    public void setActualHeightAnimating(boolean animating) {}
+
+    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+        return new ExpandableViewState();
+    }
+
+    /**
+     * @return whether the current view doesn't add height to the overall content. This means that
+     * if it is added to a list of items, it's content will still have the same height.
+     * An example is the notification shelf, that is always placed on top of another view.
+     */
+    public boolean hasNoContentHeight() {
+        return false;
+    }
+
+    /**
+     * @param inShelf whether the view is currently fully in the notification shelf.
+     */
+    public void setInShelf(boolean inShelf) {
+        mInShelf = inShelf;
+    }
+
+    public boolean isInShelf() {
+        return mInShelf;
+    }
+
+    /**
+     * @param transformingInShelf whether the view is currently transforming into the shelf in an
+     *                            animated way
+     */
+    public void setTransformingInShelf(boolean transformingInShelf) {
+        mTransformingInShelf = transformingInShelf;
+    }
+
+    public boolean isTransformingIntoShelf() {
+        return mTransformingInShelf;
+    }
+
+    public boolean isAboveShelf() {
+        return false;
+    }
+
+    public boolean hasExpandingChild() {
+        return false;
+    }
+
+    /**
+     * A listener notifying when {@link #getActualHeight} changes.
+     */
+    public interface OnHeightChangedListener {
+
+        /**
+         * @param view the view for which the height changed, or {@code null} if just the top
+         *             padding or the padding between the elements changed
+         * @param needsAnimation whether the view height needs to be animated
+         */
+        void onHeightChanged(ExpandableView view, boolean needsAnimation);
+
+        /**
+         * Called when the view is reset and therefore the height will change abruptly
+         *
+         * @param view The view which was reset.
+         */
+        void onReset(ExpandableView view);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
new file mode 100644
index 0000000..1f15ed0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -0,0 +1,105 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import com.android.systemui.statusbar.notification.stack.StackScrollState;
+
+public class FooterView extends StackScrollerDecorView {
+    private final int mClearAllTopPadding;
+    private FooterViewButton mDismissButton;
+    private FooterViewButton mManageButton;
+
+    public FooterView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mClearAllTopPadding = context.getResources().getDimensionPixelSize(
+                R.dimen.clear_all_padding_top);
+    }
+
+    @Override
+    protected View findContentView() {
+        return findViewById(R.id.content);
+    }
+
+    protected View findSecondaryView() {
+        return findViewById(R.id.dismiss_text);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mDismissButton = (FooterViewButton) findSecondaryView();
+        mManageButton = findViewById(R.id.manage_text);
+    }
+
+    public void setTextColor(@ColorInt int color) {
+        mManageButton.setTextColor(color);
+        mDismissButton.setTextColor(color);
+    }
+
+    public void setManageButtonClickListener(OnClickListener listener) {
+        mManageButton.setOnClickListener(listener);
+    }
+
+    public void setDismissButtonClickListener(OnClickListener listener) {
+        mDismissButton.setOnClickListener(listener);
+    }
+
+    public boolean isOnEmptySpace(float touchX, float touchY) {
+        return touchX < mContent.getX()
+                || touchX > mContent.getX() + mContent.getWidth()
+                || touchY < mContent.getY()
+                || touchY > mContent.getY() + mContent.getHeight();
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        mDismissButton.setText(R.string.clear_all_notifications_text);
+        mDismissButton.setContentDescription(
+                mContext.getString(R.string.accessibility_clear_all));
+        mManageButton.setText(R.string.manage_notifications_text);
+    }
+
+    public boolean isButtonVisible() {
+        return mManageButton.getAlpha() != 0.0f;
+    }
+
+    @Override
+    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+        return new FooterViewState();
+    }
+
+    public class FooterViewState extends ExpandableViewState {
+        @Override
+        public void applyToView(View view) {
+            super.applyToView(view);
+            if (view instanceof FooterView) {
+                FooterView footerView = (FooterView) view;
+                boolean visible = this.clipTopAmount < mClearAllTopPadding;
+                footerView.setContentVisible(visible && footerView.isVisible());
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterViewButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterViewButton.java
new file mode 100644
index 0000000..e1c4a0c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterViewButton.java
@@ -0,0 +1,64 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+
+import com.android.systemui.statusbar.AlphaOptimizedButton;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+
+public class FooterViewButton extends AlphaOptimizedButton {
+
+    public FooterViewButton(Context context) {
+        this(context, null);
+    }
+
+    public FooterViewButton(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FooterViewButton(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public FooterViewButton(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    /**
+     * This method returns the drawing rect for the view which is different from the regular
+     * drawing rect, since we layout all children in the {@link NotificationStackScrollLayout} at
+     * position 0 and usually the translation is neglected. The standard implementation doesn't
+     * account for translation.
+     *
+     * @param outRect The (scrolled) drawing bounds of the view.
+     */
+    @Override
+    public void getDrawingRect(Rect outRect) {
+        super.getDrawingRect(outRect);
+        float translationX = ((ViewGroup) mParent).getTranslationX();
+        float translationY = ((ViewGroup) mParent).getTranslationY();
+        outRect.left += translationX;
+        outRect.right += translationX;
+        outRect.top += translationY;
+        outRect.bottom += translationY;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
new file mode 100644
index 0000000..33badaf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.app.Notification;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationDozeHelper;
+import com.android.systemui.statusbar.notification.NotificationUtils;
+
+/**
+ * A class managing hybrid groups that include {@link HybridNotificationView} and the notification
+ * group overflow.
+ */
+public class HybridGroupManager {
+
+    private final Context mContext;
+    private final NotificationDozeHelper mDozer;
+    private final ViewGroup mParent;
+
+    private float mOverflowNumberSizeDark;
+    private int mOverflowNumberPaddingDark;
+    private float mOverflowNumberSize;
+    private int mOverflowNumberPadding;
+
+    private int mOverflowNumberColor;
+    private int mOverflowNumberColorDark;
+    private float mDarkAmount = 0f;
+
+    public HybridGroupManager(Context ctx, ViewGroup parent) {
+        mContext = ctx;
+        mParent = parent;
+        mDozer = new NotificationDozeHelper();
+        initDimens();
+    }
+
+    public void initDimens() {
+        Resources res = mContext.getResources();
+        mOverflowNumberSize = res.getDimensionPixelSize(
+                R.dimen.group_overflow_number_size);
+        mOverflowNumberSizeDark = res.getDimensionPixelSize(
+                R.dimen.group_overflow_number_size_dark);
+        mOverflowNumberPadding = res.getDimensionPixelSize(
+                R.dimen.group_overflow_number_padding);
+        mOverflowNumberPaddingDark = mOverflowNumberPadding + res.getDimensionPixelSize(
+                R.dimen.group_overflow_number_extra_padding_dark);
+    }
+
+    private HybridNotificationView inflateHybridViewWithStyle(int style) {
+        LayoutInflater inflater = new ContextThemeWrapper(mContext, style)
+                .getSystemService(LayoutInflater.class);
+        HybridNotificationView hybrid = (HybridNotificationView) inflater.inflate(
+                R.layout.hybrid_notification, mParent, false);
+        mParent.addView(hybrid);
+        return hybrid;
+    }
+
+    private TextView inflateOverflowNumber() {
+        LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
+        TextView numberView = (TextView) inflater.inflate(
+                R.layout.hybrid_overflow_number, mParent, false);
+        mParent.addView(numberView);
+        updateOverFlowNumberColor(numberView);
+        return numberView;
+    }
+
+    private void updateOverFlowNumberColor(TextView numberView) {
+        numberView.setTextColor(NotificationUtils.interpolateColors(
+                mOverflowNumberColor, mOverflowNumberColorDark, mDarkAmount));
+    }
+
+    public void setOverflowNumberColor(TextView numberView, int colorRegular, int colorDark) {
+        mOverflowNumberColor = colorRegular;
+        mOverflowNumberColorDark = colorDark;
+        if (numberView != null) {
+            updateOverFlowNumberColor(numberView);
+        }
+    }
+
+    public HybridNotificationView bindFromNotification(HybridNotificationView reusableView,
+            Notification notification) {
+        return bindFromNotificationWithStyle(reusableView, notification,
+                R.style.HybridNotification);
+    }
+
+    public HybridNotificationView bindAmbientFromNotification(HybridNotificationView reusableView,
+            Notification notification) {
+        return bindFromNotificationWithStyle(reusableView, notification,
+                R.style.HybridNotification_Ambient);
+    }
+
+    private HybridNotificationView bindFromNotificationWithStyle(
+            HybridNotificationView reusableView, Notification notification, int style) {
+        if (reusableView == null) {
+            reusableView = inflateHybridViewWithStyle(style);
+        }
+        CharSequence titleText = resolveTitle(notification);
+        CharSequence contentText = resolveText(notification);
+        reusableView.bind(titleText, contentText);
+        return reusableView;
+    }
+
+    private CharSequence resolveText(Notification notification) {
+        CharSequence contentText = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
+        if (contentText == null) {
+            contentText = notification.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
+        }
+        return contentText;
+    }
+
+    private CharSequence resolveTitle(Notification notification) {
+        CharSequence titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
+        if (titleText == null) {
+            titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
+        }
+        return titleText;
+    }
+
+    public TextView bindOverflowNumber(TextView reusableView, int number) {
+        if (reusableView == null) {
+            reusableView = inflateOverflowNumber();
+        }
+        String text = mContext.getResources().getString(
+                R.string.notification_group_overflow_indicator, number);
+        if (!text.equals(reusableView.getText())) {
+            reusableView.setText(text);
+        }
+        String contentDescription = String.format(mContext.getResources().getQuantityString(
+                R.plurals.notification_group_overflow_description, number), number);
+
+        reusableView.setContentDescription(contentDescription);
+        return reusableView;
+    }
+
+    public TextView bindOverflowNumberAmbient(TextView titleView, Notification notification,
+            int number) {
+        String text = mContext.getResources().getString(
+                R.string.notification_group_overflow_indicator_ambient,
+                resolveTitle(notification), number);
+        if (!text.equals(titleView.getText())) {
+            titleView.setText(text);
+        }
+        return titleView;
+    }
+
+    public void setOverflowNumberDark(TextView view, boolean dark, boolean fade, long delay) {
+        mDozer.setIntensityDark((f)->{
+            mDarkAmount = f;
+            updateOverFlowNumberColor(view);
+        }, dark, fade, delay, view);
+        view.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+                dark ? mOverflowNumberSizeDark : mOverflowNumberSize);
+        int paddingEnd = dark ? mOverflowNumberPaddingDark : mOverflowNumberPadding;
+        view.setPaddingRelative(view.getPaddingStart(), view.getPaddingTop(), paddingEnd,
+                view.getPaddingBottom());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
new file mode 100644
index 0000000..be25d63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.ViewTransformationHelper;
+import com.android.systemui.statusbar.notification.TransformState;
+
+/**
+ * A hybrid view which may contain information about one ore more notifications.
+ */
+public class HybridNotificationView extends AlphaOptimizedLinearLayout
+        implements TransformableView {
+
+    private ViewTransformationHelper mTransformationHelper;
+
+    protected TextView mTitleView;
+    protected TextView mTextView;
+
+    public HybridNotificationView(Context context) {
+        this(context, null);
+    }
+
+    public HybridNotificationView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public HybridNotificationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public HybridNotificationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public TextView getTitleView() {
+        return mTitleView;
+    }
+
+    public TextView getTextView() {
+        return mTextView;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mTitleView = (TextView) findViewById(R.id.notification_title);
+        mTextView = (TextView) findViewById(R.id.notification_text);
+        mTransformationHelper = new ViewTransformationHelper();
+        mTransformationHelper.setCustomTransformation(
+                new ViewTransformationHelper.CustomTransformation() {
+                    @Override
+                    public boolean transformTo(TransformState ownState, TransformableView notification,
+                            float transformationAmount) {
+                        // We want to transform to the same y location as the title
+                        TransformState otherState = notification.getCurrentState(
+                                TRANSFORMING_VIEW_TITLE);
+                        CrossFadeHelper.fadeOut(mTextView, transformationAmount);
+                        if (otherState != null) {
+                            ownState.transformViewVerticalTo(otherState, transformationAmount);
+                            otherState.recycle();
+                        }
+                        return true;
+                    }
+
+                    @Override
+                    public boolean transformFrom(TransformState ownState,
+                            TransformableView notification, float transformationAmount) {
+                        // We want to transform from the same y location as the title
+                        TransformState otherState = notification.getCurrentState(
+                                TRANSFORMING_VIEW_TITLE);
+                        CrossFadeHelper.fadeIn(mTextView, transformationAmount);
+                        if (otherState != null) {
+                            ownState.transformViewVerticalFrom(otherState, transformationAmount);
+                            otherState.recycle();
+                        }
+                        return true;
+                    }
+                }, TRANSFORMING_VIEW_TEXT);
+        mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_TITLE, mTitleView);
+        mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_TEXT, mTextView);
+    }
+
+    public void bind(CharSequence title) {
+        bind(title, null);
+    }
+
+    public void bind(CharSequence title, CharSequence text) {
+        mTitleView.setText(title);
+        mTitleView.setVisibility(TextUtils.isEmpty(title) ? GONE : VISIBLE);
+        if (TextUtils.isEmpty(text)) {
+            mTextView.setVisibility(GONE);
+            mTextView.setText(null);
+        } else {
+            mTextView.setVisibility(VISIBLE);
+            mTextView.setText(text.toString());
+        }
+        requestLayout();
+    }
+
+    @Override
+    public TransformState getCurrentState(int fadingView) {
+        return mTransformationHelper.getCurrentState(fadingView);
+    }
+
+    @Override
+    public void transformTo(TransformableView notification, Runnable endRunnable) {
+        mTransformationHelper.transformTo(notification, endRunnable);
+    }
+
+    @Override
+    public void transformTo(TransformableView notification, float transformationAmount) {
+        mTransformationHelper.transformTo(notification, transformationAmount);
+    }
+
+    @Override
+    public void transformFrom(TransformableView notification) {
+        mTransformationHelper.transformFrom(notification);
+    }
+
+    @Override
+    public void transformFrom(TransformableView notification, float transformationAmount) {
+        mTransformationHelper.transformFrom(notification, transformationAmount);
+    }
+
+    @Override
+    public void setVisible(boolean visible) {
+        setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+        mTransformationHelper.setVisible(visible);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
new file mode 100644
index 0000000..1ed726d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -0,0 +1,289 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.RippleDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+
+/**
+ * A view that can be used for both the dimmed and normal background of an notification.
+ */
+public class NotificationBackgroundView extends View {
+
+    private final boolean mDontModifyCorners;
+    private Drawable mBackground;
+    private int mClipTopAmount;
+    private int mActualHeight;
+    private int mClipBottomAmount;
+    private int mTintColor;
+    private float[] mCornerRadii = new float[8];
+    private boolean mBottomIsRounded;
+    private int mBackgroundTop;
+    private boolean mBottomAmountClips = true;
+    private boolean mExpandAnimationRunning;
+    private float mActualWidth;
+    private int mDrawableAlpha = 255;
+    private boolean mIsPressedAllowed;
+
+    private boolean mTopAmountRounded;
+    private float mDistanceToTopRoundness;
+
+    public NotificationBackgroundView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mDontModifyCorners = getResources().getBoolean(
+                R.bool.config_clipNotificationsToOutline);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mClipTopAmount + mClipBottomAmount < mActualHeight - mBackgroundTop
+                || mExpandAnimationRunning) {
+            canvas.save();
+            if (!mExpandAnimationRunning) {
+                canvas.clipRect(0, mClipTopAmount, getWidth(), mActualHeight - mClipBottomAmount);
+            }
+            draw(canvas, mBackground);
+            canvas.restore();
+        }
+    }
+
+    private void draw(Canvas canvas, Drawable drawable) {
+        if (drawable != null) {
+            int top = mBackgroundTop;
+            int bottom = mActualHeight;
+            if (mBottomIsRounded && mBottomAmountClips && !mExpandAnimationRunning) {
+                bottom -= mClipBottomAmount;
+            }
+            int left = 0;
+            int right = getWidth();
+            if (mExpandAnimationRunning) {
+                left = (int) ((getWidth() - mActualWidth) / 2.0f);
+                right = (int) (left + mActualWidth);
+            }
+            if (mTopAmountRounded) {
+                int clipTop = (int) (mClipTopAmount - mDistanceToTopRoundness);
+                top += clipTop;
+                if (clipTop >= 0) {
+                    bottom += clipTop;
+                }
+            }
+            drawable.setBounds(left, top, right, bottom);
+            drawable.draw(canvas);
+        }
+    }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || who == mBackground;
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        setState(getDrawableState());
+    }
+
+    @Override
+    public void drawableHotspotChanged(float x, float y) {
+        if (mBackground != null) {
+            mBackground.setHotspot(x, y);
+        }
+    }
+
+    /**
+     * Sets a background drawable. As we need to change our bounds independently of layout, we need
+     * the notion of a background independently of the regular View background..
+     */
+    public void setCustomBackground(Drawable background) {
+        if (mBackground != null) {
+            mBackground.setCallback(null);
+            unscheduleDrawable(mBackground);
+        }
+        mBackground = background;
+        mBackground.mutate();
+        if (mBackground != null) {
+            mBackground.setCallback(this);
+            setTint(mTintColor);
+        }
+        if (mBackground instanceof RippleDrawable) {
+            ((RippleDrawable) mBackground).setForceSoftware(true);
+        }
+        updateBackgroundRadii();
+        invalidate();
+    }
+
+    public void setCustomBackground(int drawableResId) {
+        final Drawable d = mContext.getDrawable(drawableResId);
+        setCustomBackground(d);
+    }
+
+    public void setTint(int tintColor) {
+        if (tintColor != 0) {
+            mBackground.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP);
+        } else {
+            mBackground.clearColorFilter();
+        }
+        mTintColor = tintColor;
+        invalidate();
+    }
+
+    public void setActualHeight(int actualHeight) {
+        if (mExpandAnimationRunning) {
+            return;
+        }
+        mActualHeight = actualHeight;
+        invalidate();
+    }
+
+    public int getActualHeight() {
+        return mActualHeight;
+    }
+
+    public void setClipTopAmount(int clipTopAmount) {
+        mClipTopAmount = clipTopAmount;
+        invalidate();
+    }
+
+    public void setClipBottomAmount(int clipBottomAmount) {
+        mClipBottomAmount = clipBottomAmount;
+        invalidate();
+    }
+
+    public void setDistanceToTopRoundness(float distanceToTopRoundness) {
+        if (distanceToTopRoundness != mDistanceToTopRoundness) {
+            mTopAmountRounded = distanceToTopRoundness >= 0;
+            mDistanceToTopRoundness = distanceToTopRoundness;
+            invalidate();
+        }
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+
+        // Prevents this view from creating a layer when alpha is animating.
+        return false;
+    }
+
+    public void setState(int[] drawableState) {
+        if (mBackground != null && mBackground.isStateful()) {
+            if (!mIsPressedAllowed) {
+                drawableState = ArrayUtils.removeInt(drawableState,
+                        com.android.internal.R.attr.state_pressed);
+            }
+            mBackground.setState(drawableState);
+        }
+    }
+
+    public void setRippleColor(int color) {
+        if (mBackground instanceof RippleDrawable) {
+            RippleDrawable ripple = (RippleDrawable) mBackground;
+            ripple.setColor(ColorStateList.valueOf(color));
+        }
+    }
+
+    public void setDrawableAlpha(int drawableAlpha) {
+        mDrawableAlpha = drawableAlpha;
+        if (mExpandAnimationRunning) {
+            return;
+        }
+        mBackground.setAlpha(drawableAlpha);
+    }
+
+    public void setRoundness(float topRoundness, float bottomRoundNess) {
+        if (topRoundness == mCornerRadii[0] && bottomRoundNess == mCornerRadii[4]) {
+            return;
+        }
+        mBottomIsRounded = bottomRoundNess != 0.0f;
+        mCornerRadii[0] = topRoundness;
+        mCornerRadii[1] = topRoundness;
+        mCornerRadii[2] = topRoundness;
+        mCornerRadii[3] = topRoundness;
+        mCornerRadii[4] = bottomRoundNess;
+        mCornerRadii[5] = bottomRoundNess;
+        mCornerRadii[6] = bottomRoundNess;
+        mCornerRadii[7] = bottomRoundNess;
+        updateBackgroundRadii();
+    }
+
+    public void setBottomAmountClips(boolean clips) {
+        if (clips != mBottomAmountClips) {
+            mBottomAmountClips = clips;
+            invalidate();
+        }
+    }
+
+    private void updateBackgroundRadii() {
+        if (mDontModifyCorners) {
+            return;
+        }
+        if (mBackground instanceof LayerDrawable) {
+            GradientDrawable gradientDrawable =
+                    (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
+            gradientDrawable.setCornerRadii(mCornerRadii);
+        }
+    }
+
+    public void setBackgroundTop(int backgroundTop) {
+        mBackgroundTop = backgroundTop;
+        invalidate();
+    }
+
+    public void setExpandAnimationParams(ActivityLaunchAnimator.ExpandAnimationParameters params) {
+        mActualHeight = params.getHeight();
+        mActualWidth = params.getWidth();
+        float alphaProgress = Interpolators.ALPHA_IN.getInterpolation(
+                params.getProgress(
+                        ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT /* delay */,
+                        ActivityLaunchAnimator.ANIMATION_DURATION_FADE_APP /* duration */));
+        mBackground.setAlpha((int) (mDrawableAlpha * (1.0f - alphaProgress)));
+        invalidate();
+    }
+
+    public void setExpandAnimationRunning(boolean running) {
+        mExpandAnimationRunning = running;
+        if (mBackground instanceof LayerDrawable) {
+            GradientDrawable gradientDrawable =
+                    (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
+            gradientDrawable.setXfermode(
+                    running ? new PorterDuffXfermode(PorterDuff.Mode.SRC) : null);
+            // Speed optimization: disable AA if transfer mode is not SRC_OVER. AA is not easy to
+            // spot during animation anyways.
+            gradientDrawable.setAntiAlias(!running);
+        }
+        if (!mExpandAnimationRunning) {
+            setDrawableAlpha(mDrawableAlpha);
+        }
+        invalidate();
+    }
+
+    public void setPressedAllowed(boolean allowed) {
+        mIsPressedAllowed = allowed;
+    }
+}
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
new file mode 100644
index 0000000..1a4ef09
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.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 com.android.systemui.statusbar.notification.row;
+
+import android.content.Context;
+
+import androidx.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.logging.NotificationCounters;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+
+/**
+ * Manager for the notification blocking helper - tracks and helps create the blocking helper
+ * affordance.
+ */
+public class NotificationBlockingHelperManager {
+    /** Enables debug logging and always makes the blocking helper show up after a dismiss. */
+    private static final boolean DEBUG = false;
+    private static final String TAG = "BlockingHelper";
+
+    private final Context mContext;
+    /** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */
+    private ExpandableNotificationRow mBlockingHelperRow;
+    private Set<String> mNonBlockablePkgs;
+
+    /**
+     * Whether the notification shade/stack is expanded - used to determine blocking helper
+     * eligibility.
+     */
+    private boolean mIsShadeExpanded;
+
+    public NotificationBlockingHelperManager(Context context) {
+        mContext = context;
+        mNonBlockablePkgs = new HashSet<>();
+        Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray(
+                com.android.internal.R.array.config_nonBlockableNotificationPackages));
+    }
+
+    /**
+     * Potentially shows the blocking helper, represented via the {@link NotificationInfo} menu
+     * item, in the current row if user sentiment is negative.
+     *
+     * @param row row to render the blocking helper in
+     * @param menuRow menu used to generate the {@link NotificationInfo} view that houses the
+     *                blocking helper UI
+     * @return whether we're showing a blocking helper in the given notification row
+     */
+    boolean perhapsShowBlockingHelper(
+            ExpandableNotificationRow row, NotificationMenuRowPlugin menuRow) {
+        // We only show the blocking helper if:
+        // - User sentiment is negative (DEBUG flag can bypass)
+        // - The notification shade is fully expanded (guarantees we're not touching a HUN).
+        // - The row is blockable (i.e. not non-blockable)
+        // - The dismissed row is a valid group (>1 or 0 children) or the only child in the group
+        if ((row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE || DEBUG)
+                && mIsShadeExpanded
+                && !row.getIsNonblockable()
+                && (!row.isChildInGroup() || row.isOnlyChildInGroup())) {
+            // Dismiss any current blocking helper before continuing forward (only one can be shown
+            // at a given time).
+            dismissCurrentBlockingHelper();
+
+            if (DEBUG) {
+                Log.d(TAG, "Manager.perhapsShowBlockingHelper: Showing new blocking helper");
+            }
+            NotificationGutsManager manager = Dependency.get(NotificationGutsManager.class);
+
+            // Enable blocking helper on the row before moving forward so everything in the guts is
+            // correctly prepped.
+            mBlockingHelperRow = row;
+            mBlockingHelperRow.setBlockingHelperShowing(true);
+
+            // We don't care about the touch origin (x, y) since we're opening guts without any
+            // explicit user interaction.
+            manager.openGuts(mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
+
+            Dependency.get(MetricsLogger.class)
+                    .count(NotificationCounters.BLOCKING_HELPER_SHOWN, 1);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Dismiss the currently showing blocking helper, if any, through a notification update.
+     *
+     * @return whether the blocking helper was dismissed
+     */
+    boolean dismissCurrentBlockingHelper() {
+        if (!isBlockingHelperRowNull()) {
+            if (DEBUG) {
+                Log.d(TAG, "Manager.dismissCurrentBlockingHelper: Dismissing current helper");
+            }
+            if (!mBlockingHelperRow.isBlockingHelperShowing()) {
+                Log.e(TAG, "Manager.dismissCurrentBlockingHelper: "
+                        + "Non-null row is not showing a blocking helper");
+            }
+
+            mBlockingHelperRow.setBlockingHelperShowing(false);
+            if (mBlockingHelperRow.isAttachedToWindow()) {
+                Dependency.get(NotificationEntryManager.class).updateNotifications();
+            }
+            mBlockingHelperRow = null;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Update the expansion status of the notification shade/stack.
+     *
+     * @param expandedHeight how much the shade is expanded ({code 0} indicating it's collapsed)
+     */
+    public void setNotificationShadeExpanded(float expandedHeight) {
+        mIsShadeExpanded = expandedHeight > 0.0f;
+    }
+
+    /**
+     * Returns whether the given package name is in the list of non-blockable packages.
+     */
+    public boolean isNonblockable(String packageName, String channelName) {
+        return mNonBlockablePkgs.contains(packageName)
+                || mNonBlockablePkgs.contains(makeChannelKey(packageName, channelName));
+    }
+
+    // Format must stay in sync with frameworks/base/core/res/res/values/config.xml
+    // config_nonBlockableNotificationPackages
+    private String makeChannelKey(String pkg, String channel) {
+        return pkg + ":" + channel;
+    }
+
+    @VisibleForTesting
+    boolean isBlockingHelperRowNull() {
+        return mBlockingHelperRow == null;
+    }
+
+    @VisibleForTesting
+    void setBlockingHelperRowForTest(ExpandableNotificationRow blockingHelperRowForTest) {
+        mBlockingHelperRow = blockingHelperRowForTest;
+    }
+
+    @VisibleForTesting
+    void setNonBlockablePkgs(String[] pkgsAndChannels) {
+        mNonBlockablePkgs = new HashSet<>();
+        Collections.addAll(mNonBlockablePkgs, pkgsAndChannels);
+    }
+}
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
new file mode 100644
index 0000000..0110610
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -0,0 +1,1756 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Build;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.NotificationHeaderView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ContrastColorUtil;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
+import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.RemoteInputView;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.statusbar.policy.SmartReplyView;
+
+/**
+ * A frame layout containing the actual payload of the notification, including the contracted,
+ * expanded and heads up layout. This class is responsible for clipping the content and and
+ * switching between the expanded, contracted and the heads up view depending on its clipped size.
+ */
+public class NotificationContentView extends FrameLayout {
+
+    private static final String TAG = "NotificationContentView";
+    public static final int VISIBLE_TYPE_CONTRACTED = 0;
+    public static final int VISIBLE_TYPE_EXPANDED = 1;
+    public static final int VISIBLE_TYPE_HEADSUP = 2;
+    private static final int VISIBLE_TYPE_SINGLELINE = 3;
+    public static final int VISIBLE_TYPE_AMBIENT = 4;
+    private static final int VISIBLE_TYPE_AMBIENT_SINGLELINE = 5;
+    public static final int UNDEFINED = -1;
+
+    private final Rect mClipBounds = new Rect();
+
+    private int mMinContractedHeight;
+    private int mNotificationContentMarginEnd;
+    private View mContractedChild;
+    private View mExpandedChild;
+    private View mHeadsUpChild;
+    private HybridNotificationView mSingleLineView;
+    private View mAmbientChild;
+    private HybridNotificationView mAmbientSingleLineChild;
+
+    private RemoteInputView mExpandedRemoteInput;
+    private RemoteInputView mHeadsUpRemoteInput;
+
+    private SmartReplyConstants mSmartReplyConstants;
+    private SmartReplyView mExpandedSmartReplyView;
+    private SmartReplyController mSmartReplyController;
+
+    private NotificationViewWrapper mContractedWrapper;
+    private NotificationViewWrapper mExpandedWrapper;
+    private NotificationViewWrapper mHeadsUpWrapper;
+    private NotificationViewWrapper mAmbientWrapper;
+    private HybridGroupManager mHybridGroupManager;
+    private int mClipTopAmount;
+    private int mContentHeight;
+    private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
+    private boolean mDark;
+    private boolean mAnimate;
+    private boolean mIsHeadsUp;
+    private boolean mLegacy;
+    private boolean mIsChildInGroup;
+    private int mSmallHeight;
+    private int mHeadsUpHeight;
+    private int mNotificationMaxHeight;
+    private int mNotificationAmbientHeight;
+    private StatusBarNotification mStatusBarNotification;
+    private NotificationGroupManager mGroupManager;
+    private RemoteInputController mRemoteInputController;
+    private Runnable mExpandedVisibleListener;
+
+    private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
+            = new ViewTreeObserver.OnPreDrawListener() {
+        @Override
+        public boolean onPreDraw() {
+            // We need to post since we don't want the notification to animate on the very first
+            // frame
+            post(new Runnable() {
+                @Override
+                public void run() {
+                    mAnimate = true;
+                }
+            });
+            getViewTreeObserver().removeOnPreDrawListener(this);
+            return true;
+        }
+    };
+
+    private OnClickListener mExpandClickListener;
+    private boolean mBeforeN;
+    private boolean mExpandable;
+    private boolean mClipToActualHeight = true;
+    private ExpandableNotificationRow mContainingNotification;
+    /** The visible type at the start of a touch driven transformation */
+    private int mTransformationStartVisibleType;
+    /** The visible type at the start of an animation driven transformation */
+    private int mAnimationStartVisibleType = UNDEFINED;
+    private boolean mUserExpanding;
+    private int mSingleLineWidthIndention;
+    private boolean mForceSelectNextLayout = true;
+    private PendingIntent mPreviousExpandedRemoteInputIntent;
+    private PendingIntent mPreviousHeadsUpRemoteInputIntent;
+    private RemoteInputView mCachedExpandedRemoteInput;
+    private RemoteInputView mCachedHeadsUpRemoteInput;
+
+    private int mContentHeightAtAnimationStart = UNDEFINED;
+    private boolean mFocusOnVisibilityChange;
+    private boolean mHeadsUpAnimatingAway;
+    private boolean mIconsVisible;
+    private int mClipBottomAmount;
+    private boolean mIsLowPriority;
+    private boolean mIsContentExpandable;
+    private boolean mRemoteInputVisible;
+    private int mUnrestrictedContentHeight;
+
+
+    public NotificationContentView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mHybridGroupManager = new HybridGroupManager(getContext(), this);
+        mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
+        mSmartReplyController = Dependency.get(SmartReplyController.class);
+        initView();
+    }
+
+    public void initView() {
+        mMinContractedHeight = getResources().getDimensionPixelSize(
+                R.dimen.min_notification_layout_height);
+        mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_content_margin_end);
+    }
+
+    public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
+            int ambientHeight) {
+        mSmallHeight = smallHeight;
+        mHeadsUpHeight = headsUpMaxHeight;
+        mNotificationMaxHeight = maxHeight;
+        mNotificationAmbientHeight = ambientHeight;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
+        boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
+        int maxSize = Integer.MAX_VALUE / 2;
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        if (hasFixedHeight || isHeightLimited) {
+            maxSize = MeasureSpec.getSize(heightMeasureSpec);
+        }
+        int maxChildHeight = 0;
+        if (mExpandedChild != null) {
+            int notificationMaxHeight = mNotificationMaxHeight;
+            if (mExpandedSmartReplyView != null) {
+                notificationMaxHeight += mExpandedSmartReplyView.getHeightUpperLimit();
+            }
+            notificationMaxHeight += mExpandedWrapper.getExtraMeasureHeight();
+            int size = notificationMaxHeight;
+            ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
+            boolean useExactly = false;
+            if (layoutParams.height >= 0) {
+                // An actual height is set
+                size = Math.min(size, layoutParams.height);
+                useExactly = true;
+            }
+            int spec = MeasureSpec.makeMeasureSpec(size, useExactly
+                            ? MeasureSpec.EXACTLY
+                            : MeasureSpec.AT_MOST);
+            measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, spec, 0);
+            maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
+        }
+        if (mContractedChild != null) {
+            int heightSpec;
+            int size = mSmallHeight;
+            ViewGroup.LayoutParams layoutParams = mContractedChild.getLayoutParams();
+            boolean useExactly = false;
+            if (layoutParams.height >= 0) {
+                // An actual height is set
+                size = Math.min(size, layoutParams.height);
+                useExactly = true;
+            }
+            if (shouldContractedBeFixedSize() || useExactly) {
+                heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+            } else {
+                heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
+            }
+            measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
+            int measuredHeight = mContractedChild.getMeasuredHeight();
+            if (measuredHeight < mMinContractedHeight) {
+                heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
+                measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
+            }
+            maxChildHeight = Math.max(maxChildHeight, measuredHeight);
+            if (updateContractedHeaderWidth()) {
+                measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
+            }
+            if (mExpandedChild != null
+                    && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
+                // the Expanded child is smaller then the collapsed. Let's remeasure it.
+                heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
+                        MeasureSpec.EXACTLY);
+                measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, heightSpec, 0);
+            }
+        }
+        if (mHeadsUpChild != null) {
+            int maxHeight = mHeadsUpHeight;
+            maxHeight += mHeadsUpWrapper.getExtraMeasureHeight();
+            int size = maxHeight;
+            ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
+            boolean useExactly = false;
+            if (layoutParams.height >= 0) {
+                // An actual height is set
+                size = Math.min(size, layoutParams.height);
+                useExactly = true;
+            }
+            measureChildWithMargins(mHeadsUpChild, widthMeasureSpec, 0,
+                    MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
+                            : MeasureSpec.AT_MOST), 0);
+            maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
+        }
+        if (mSingleLineView != null) {
+            int singleLineWidthSpec = widthMeasureSpec;
+            if (mSingleLineWidthIndention != 0
+                    && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
+                singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
+                        width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
+                        MeasureSpec.EXACTLY);
+            }
+            mSingleLineView.measure(singleLineWidthSpec,
+                    MeasureSpec.makeMeasureSpec(mNotificationMaxHeight, MeasureSpec.AT_MOST));
+            maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
+        }
+        if (mAmbientChild != null) {
+            int size = mNotificationAmbientHeight;
+            ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
+            boolean useExactly = false;
+            if (layoutParams.height >= 0) {
+                // An actual height is set
+                size = Math.min(size, layoutParams.height);
+                useExactly = true;
+            }
+            mAmbientChild.measure(widthMeasureSpec,
+                    MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
+                            : MeasureSpec.AT_MOST));
+            maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
+        }
+        if (mAmbientSingleLineChild != null) {
+            int size = mNotificationAmbientHeight;
+            ViewGroup.LayoutParams layoutParams = mAmbientSingleLineChild.getLayoutParams();
+            boolean useExactly = false;
+            if (layoutParams.height >= 0) {
+                // An actual height is set
+                size = Math.min(size, layoutParams.height);
+                useExactly = true;
+            }
+            int ambientSingleLineWidthSpec = widthMeasureSpec;
+            if (mSingleLineWidthIndention != 0
+                    && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
+                ambientSingleLineWidthSpec = MeasureSpec.makeMeasureSpec(
+                        width - mSingleLineWidthIndention + mAmbientSingleLineChild.getPaddingEnd(),
+                        MeasureSpec.EXACTLY);
+            }
+            mAmbientSingleLineChild.measure(ambientSingleLineWidthSpec,
+                    MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
+                            : MeasureSpec.AT_MOST));
+            maxChildHeight = Math.max(maxChildHeight, mAmbientSingleLineChild.getMeasuredHeight());
+        }
+        int ownHeight = Math.min(maxChildHeight, maxSize);
+        setMeasuredDimension(width, ownHeight);
+    }
+
+    /**
+     * Get the extra height that needs to be added to the notification height for a given
+     * {@link RemoteInputView}.
+     * This is needed when the user is inline replying in order to ensure that the reply bar has
+     * enough padding.
+     *
+     * @param remoteInput The remote input to check.
+     * @return The extra height needed.
+     */
+    private int getExtraRemoteInputHeight(RemoteInputView remoteInput) {
+        if (remoteInput != null && (remoteInput.isActive() || remoteInput.isSending())) {
+            return getResources().getDimensionPixelSize(
+                    com.android.internal.R.dimen.notification_content_margin);
+        }
+        return 0;
+    }
+
+    private boolean updateContractedHeaderWidth() {
+        // We need to update the expanded and the collapsed header to have exactly the same with to
+        // have the expand buttons laid out at the same location.
+        NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
+        if (contractedHeader != null) {
+            if (mExpandedChild != null
+                    && mExpandedWrapper.getNotificationHeader() != null) {
+                NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
+                int expandedSize = expandedHeader.getMeasuredWidth()
+                        - expandedHeader.getPaddingEnd();
+                int collapsedSize = contractedHeader.getMeasuredWidth()
+                        - expandedHeader.getPaddingEnd();
+                if (expandedSize != collapsedSize) {
+                    int paddingEnd = contractedHeader.getMeasuredWidth() - expandedSize;
+                    contractedHeader.setPadding(
+                            contractedHeader.isLayoutRtl()
+                                    ? paddingEnd
+                                    : contractedHeader.getPaddingLeft(),
+                            contractedHeader.getPaddingTop(),
+                            contractedHeader.isLayoutRtl()
+                                    ? contractedHeader.getPaddingLeft()
+                                    : paddingEnd,
+                            contractedHeader.getPaddingBottom());
+                    contractedHeader.setShowWorkBadgeAtEnd(true);
+                    return true;
+                }
+            } else {
+                int paddingEnd = mNotificationContentMarginEnd;
+                if (contractedHeader.getPaddingEnd() != paddingEnd) {
+                    contractedHeader.setPadding(
+                            contractedHeader.isLayoutRtl()
+                                    ? paddingEnd
+                                    : contractedHeader.getPaddingLeft(),
+                            contractedHeader.getPaddingTop(),
+                            contractedHeader.isLayoutRtl()
+                                    ? contractedHeader.getPaddingLeft()
+                                    : paddingEnd,
+                            contractedHeader.getPaddingBottom());
+                    contractedHeader.setShowWorkBadgeAtEnd(false);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean shouldContractedBeFixedSize() {
+        return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        int previousHeight = 0;
+        if (mExpandedChild != null) {
+            previousHeight = mExpandedChild.getHeight();
+        }
+        super.onLayout(changed, left, top, right, bottom);
+        if (previousHeight != 0 && mExpandedChild.getHeight() != previousHeight) {
+            mContentHeightAtAnimationStart = previousHeight;
+        }
+        updateClipping();
+        invalidateOutline();
+        selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
+        mForceSelectNextLayout = false;
+        updateExpandButtons(mExpandable);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        updateVisibility();
+    }
+
+    public View getContractedChild() {
+        return mContractedChild;
+    }
+
+    public View getExpandedChild() {
+        return mExpandedChild;
+    }
+
+    public View getHeadsUpChild() {
+        return mHeadsUpChild;
+    }
+
+    public View getAmbientChild() {
+        return mAmbientChild;
+    }
+
+    public HybridNotificationView getAmbientSingleLineChild() {
+        return mAmbientSingleLineChild;
+    }
+
+    public void setContractedChild(View child) {
+        if (mContractedChild != null) {
+            mContractedChild.animate().cancel();
+            removeView(mContractedChild);
+        }
+        addView(child);
+        mContractedChild = child;
+        mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
+                mContainingNotification);
+    }
+
+    private NotificationViewWrapper getWrapperForView(View child) {
+        if (child == mContractedChild) {
+            return mContractedWrapper;
+        }
+        if (child == mExpandedChild) {
+            return mExpandedWrapper;
+        }
+        if (child == mHeadsUpChild) {
+            return mHeadsUpWrapper;
+        }
+        if (child == mAmbientChild) {
+            return mAmbientWrapper;
+        }
+        return null;
+    }
+
+    public void setExpandedChild(View child) {
+        if (mExpandedChild != null) {
+            mPreviousExpandedRemoteInputIntent = null;
+            if (mExpandedRemoteInput != null) {
+                mExpandedRemoteInput.onNotificationUpdateOrReset();
+                if (mExpandedRemoteInput.isActive()) {
+                    mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
+                    mCachedExpandedRemoteInput = mExpandedRemoteInput;
+                    mExpandedRemoteInput.dispatchStartTemporaryDetach();
+                    ((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
+                }
+            }
+            mExpandedChild.animate().cancel();
+            removeView(mExpandedChild);
+            mExpandedRemoteInput = null;
+        }
+        if (child == null) {
+            mExpandedChild = null;
+            mExpandedWrapper = null;
+            if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
+                mVisibleType = VISIBLE_TYPE_CONTRACTED;
+            }
+            if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
+                mTransformationStartVisibleType = UNDEFINED;
+            }
+            return;
+        }
+        addView(child);
+        mExpandedChild = child;
+        mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
+                mContainingNotification);
+    }
+
+    public void setHeadsUpChild(View child) {
+        if (mHeadsUpChild != null) {
+            mPreviousHeadsUpRemoteInputIntent = null;
+            if (mHeadsUpRemoteInput != null) {
+                mHeadsUpRemoteInput.onNotificationUpdateOrReset();
+                if (mHeadsUpRemoteInput.isActive()) {
+                    mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
+                    mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
+                    mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
+                    ((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
+                }
+            }
+            mHeadsUpChild.animate().cancel();
+            removeView(mHeadsUpChild);
+            mHeadsUpRemoteInput = null;
+        }
+        if (child == null) {
+            mHeadsUpChild = null;
+            mHeadsUpWrapper = null;
+            if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
+                mVisibleType = VISIBLE_TYPE_CONTRACTED;
+            }
+            if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
+                mTransformationStartVisibleType = UNDEFINED;
+            }
+            return;
+        }
+        addView(child);
+        mHeadsUpChild = child;
+        mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
+                mContainingNotification);
+    }
+
+    public void setAmbientChild(View child) {
+        if (mAmbientChild != null) {
+            mAmbientChild.animate().cancel();
+            removeView(mAmbientChild);
+        }
+        if (child == null) {
+            return;
+        }
+        addView(child);
+        mAmbientChild = child;
+        mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
+                mContainingNotification);
+    }
+
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        updateVisibility();
+    }
+
+    private void updateVisibility() {
+        setVisible(isShown());
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
+    }
+
+    private void setVisible(final boolean isVisible) {
+        if (isVisible) {
+            // This call can happen multiple times, but removing only removes a single one.
+            // We therefore need to remove the old one.
+            getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
+            // We only animate if we are drawn at least once, otherwise the view might animate when
+            // it's shown the first time
+            getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
+        } else {
+            getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
+            mAnimate = false;
+        }
+    }
+
+    private void focusExpandButtonIfNecessary() {
+        if (mFocusOnVisibilityChange) {
+            NotificationHeaderView header = getVisibleNotificationHeader();
+            if (header != null) {
+                ImageView expandButton = header.getExpandButton();
+                if (expandButton != null) {
+                    expandButton.requestAccessibilityFocus();
+                }
+            }
+            mFocusOnVisibilityChange = false;
+        }
+    }
+
+    public void setContentHeight(int contentHeight) {
+        mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
+        int maxContentHeight = mContainingNotification.getIntrinsicHeight()
+                - getExtraRemoteInputHeight(mExpandedRemoteInput)
+                - getExtraRemoteInputHeight(mHeadsUpRemoteInput);
+        mContentHeight = Math.min(mUnrestrictedContentHeight, maxContentHeight);
+        selectLayout(mAnimate /* animate */, false /* force */);
+
+        int minHeightHint = getMinContentHeightHint();
+
+        NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
+        if (wrapper != null) {
+            wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
+        }
+
+        wrapper = getVisibleWrapper(mTransformationStartVisibleType);
+        if (wrapper != null) {
+            wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
+        }
+
+        updateClipping();
+        invalidateOutline();
+    }
+
+    /**
+     * @return the minimum apparent height that the wrapper should allow for the purpose
+     *         of aligning elements at the bottom edge. If this is larger than the content
+     *         height, the notification is clipped instead of being further shrunk.
+     */
+    private int getMinContentHeightHint() {
+        if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
+            return mContext.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.notification_action_list_height);
+        }
+
+        // Transition between heads-up & expanded, or pinned.
+        if (mHeadsUpChild != null && mExpandedChild != null) {
+            boolean transitioningBetweenHunAndExpanded =
+                    isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
+                    isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
+            boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
+                    && (mIsHeadsUp || mHeadsUpAnimatingAway)
+                    && !mContainingNotification.isOnKeyguard();
+            if (transitioningBetweenHunAndExpanded || pinned) {
+                return Math.min(getViewHeight(VISIBLE_TYPE_HEADSUP),
+                        getViewHeight(VISIBLE_TYPE_EXPANDED));
+            }
+        }
+
+        // Size change of the expanded version
+        if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
+                && mExpandedChild != null) {
+            return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
+        }
+
+        int hint;
+        if (mAmbientChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
+            hint = mAmbientChild.getHeight();
+        } else if (mAmbientSingleLineChild != null && isVisibleOrTransitioning(
+                VISIBLE_TYPE_AMBIENT_SINGLELINE)) {
+            hint = mAmbientSingleLineChild.getHeight();
+        } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
+            hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
+        } else if (mExpandedChild != null) {
+            hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
+        } else {
+            hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
+                    + mContext.getResources().getDimensionPixelSize(
+                            com.android.internal.R.dimen.notification_action_list_height);
+        }
+
+        if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
+            hint = Math.min(hint, getViewHeight(VISIBLE_TYPE_EXPANDED));
+        }
+        return hint;
+    }
+
+    private boolean isTransitioningFromTo(int from, int to) {
+        return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
+                && mVisibleType == to;
+    }
+
+    private boolean isVisibleOrTransitioning(int type) {
+        return mVisibleType == type || mTransformationStartVisibleType == type
+                || mAnimationStartVisibleType == type;
+    }
+
+    private void updateContentTransformation() {
+        int visibleType = calculateVisibleType();
+        if (visibleType != mVisibleType) {
+            // A new transformation starts
+            mTransformationStartVisibleType = mVisibleType;
+            final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
+            final TransformableView hiddenView = getTransformableViewForVisibleType(
+                    mTransformationStartVisibleType);
+            shownView.transformFrom(hiddenView, 0.0f);
+            getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
+            hiddenView.transformTo(shownView, 0.0f);
+            mVisibleType = visibleType;
+            updateBackgroundColor(true /* animate */);
+        }
+        if (mForceSelectNextLayout) {
+            forceUpdateVisibilities();
+        }
+        if (mTransformationStartVisibleType != UNDEFINED
+                && mVisibleType != mTransformationStartVisibleType
+                && getViewForVisibleType(mTransformationStartVisibleType) != null) {
+            final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
+            final TransformableView hiddenView = getTransformableViewForVisibleType(
+                    mTransformationStartVisibleType);
+            float transformationAmount = calculateTransformationAmount();
+            shownView.transformFrom(hiddenView, transformationAmount);
+            hiddenView.transformTo(shownView, transformationAmount);
+            updateBackgroundTransformation(transformationAmount);
+        } else {
+            updateViewVisibilities(visibleType);
+            updateBackgroundColor(false);
+        }
+    }
+
+    private void updateBackgroundTransformation(float transformationAmount) {
+        int endColor = getBackgroundColor(mVisibleType);
+        int startColor = getBackgroundColor(mTransformationStartVisibleType);
+        if (endColor != startColor) {
+            if (startColor == 0) {
+                startColor = mContainingNotification.getBackgroundColorWithoutTint();
+            }
+            if (endColor == 0) {
+                endColor = mContainingNotification.getBackgroundColorWithoutTint();
+            }
+            endColor = NotificationUtils.interpolateColors(startColor, endColor,
+                    transformationAmount);
+        }
+        mContainingNotification.updateBackgroundAlpha(transformationAmount);
+        mContainingNotification.setContentBackground(endColor, false, this);
+    }
+
+    private float calculateTransformationAmount() {
+        int startHeight = getViewHeight(mTransformationStartVisibleType);
+        int endHeight = getViewHeight(mVisibleType);
+        int progress = Math.abs(mContentHeight - startHeight);
+        int totalDistance = Math.abs(endHeight - startHeight);
+        if (totalDistance == 0) {
+            Log.wtf(TAG, "the total transformation distance is 0"
+                    + "\n StartType: " + mTransformationStartVisibleType + " height: " + startHeight
+                    + "\n VisibleType: " + mVisibleType + " height: " + endHeight
+                    + "\n mContentHeight: " + mContentHeight);
+            return 1.0f;
+        }
+        float amount = (float) progress / (float) totalDistance;
+        return Math.min(1.0f, amount);
+    }
+
+    public int getContentHeight() {
+        return mContentHeight;
+    }
+
+    public int getMaxHeight() {
+        if (mContainingNotification.isShowingAmbient()) {
+            return getShowingAmbientView().getHeight();
+        } else if (mExpandedChild != null) {
+            return getViewHeight(VISIBLE_TYPE_EXPANDED)
+                    + getExtraRemoteInputHeight(mExpandedRemoteInput);
+        } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
+            return getViewHeight(VISIBLE_TYPE_HEADSUP)
+                    + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
+        }
+        return getViewHeight(VISIBLE_TYPE_CONTRACTED);
+    }
+
+    private int getViewHeight(int visibleType) {
+        View view = getViewForVisibleType(visibleType);
+        int height = view.getHeight();
+        NotificationViewWrapper viewWrapper = getWrapperForView(view);
+        if (viewWrapper != null) {
+            height += viewWrapper.getHeaderTranslation();
+        }
+        return height;
+    }
+
+    public int getMinHeight() {
+        return getMinHeight(false /* likeGroupExpanded */);
+    }
+
+    public int getMinHeight(boolean likeGroupExpanded) {
+        if (mContainingNotification.isShowingAmbient()) {
+            return getShowingAmbientView().getHeight();
+        } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
+            return getViewHeight(VISIBLE_TYPE_CONTRACTED);
+        } else {
+            return mSingleLineView.getHeight();
+        }
+    }
+
+    public View getShowingAmbientView() {
+        View v = mIsChildInGroup ? mAmbientSingleLineChild : mAmbientChild;
+        if (v != null) {
+            return v;
+        } else {
+            return mContractedChild;
+        }
+    }
+
+    private boolean isGroupExpanded() {
+        return mGroupManager.isGroupExpanded(mStatusBarNotification);
+    }
+
+    public void setClipTopAmount(int clipTopAmount) {
+        mClipTopAmount = clipTopAmount;
+        updateClipping();
+    }
+
+
+    public void setClipBottomAmount(int clipBottomAmount) {
+        mClipBottomAmount = clipBottomAmount;
+        updateClipping();
+    }
+
+    @Override
+    public void setTranslationY(float translationY) {
+        super.setTranslationY(translationY);
+        updateClipping();
+    }
+
+    private void updateClipping() {
+        if (mClipToActualHeight) {
+            int top = (int) (mClipTopAmount - getTranslationY());
+            int bottom = (int) (mUnrestrictedContentHeight - mClipBottomAmount - getTranslationY());
+            bottom = Math.max(top, bottom);
+            mClipBounds.set(0, top, getWidth(), bottom);
+            setClipBounds(mClipBounds);
+        } else {
+            setClipBounds(null);
+        }
+    }
+
+    public void setClipToActualHeight(boolean clipToActualHeight) {
+        mClipToActualHeight = clipToActualHeight;
+        updateClipping();
+    }
+
+    private void selectLayout(boolean animate, boolean force) {
+        if (mContractedChild == null) {
+            return;
+        }
+        if (mUserExpanding) {
+            updateContentTransformation();
+        } else {
+            int visibleType = calculateVisibleType();
+            boolean changedType = visibleType != mVisibleType;
+            if (changedType || force) {
+                View visibleView = getViewForVisibleType(visibleType);
+                if (visibleView != null) {
+                    visibleView.setVisibility(VISIBLE);
+                    transferRemoteInputFocus(visibleType);
+                }
+
+                if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
+                        || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
+                        || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
+                        || visibleType == VISIBLE_TYPE_CONTRACTED)) {
+                    animateToVisibleType(visibleType);
+                } else {
+                    updateViewVisibilities(visibleType);
+                }
+                mVisibleType = visibleType;
+                if (changedType) {
+                    focusExpandButtonIfNecessary();
+                }
+                NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
+                if (visibleWrapper != null) {
+                    visibleWrapper.setContentHeight(mUnrestrictedContentHeight,
+                            getMinContentHeightHint());
+                }
+                updateBackgroundColor(animate);
+            }
+        }
+    }
+
+    private void forceUpdateVisibilities() {
+        forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
+        forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
+        forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
+        forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
+        forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
+        forceUpdateVisibility(VISIBLE_TYPE_AMBIENT_SINGLELINE, mAmbientSingleLineChild,
+                mAmbientSingleLineChild);
+        fireExpandedVisibleListenerIfVisible();
+        // forceUpdateVisibilities cancels outstanding animations without updating the
+        // mAnimationStartVisibleType. Do so here instead.
+        mAnimationStartVisibleType = UNDEFINED;
+    }
+
+    private void fireExpandedVisibleListenerIfVisible() {
+        if (mExpandedVisibleListener != null && mExpandedChild != null && isShown()
+                && mExpandedChild.getVisibility() == VISIBLE) {
+            Runnable listener = mExpandedVisibleListener;
+            mExpandedVisibleListener = null;
+            listener.run();
+        }
+    }
+
+    private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
+        if (view == null) {
+            return;
+        }
+        boolean visible = mVisibleType == type
+                || mTransformationStartVisibleType == type;
+        if (!visible) {
+            view.setVisibility(INVISIBLE);
+        } else {
+            wrapper.setVisible(true);
+        }
+    }
+
+    public void updateBackgroundColor(boolean animate) {
+        int customBackgroundColor = getBackgroundColor(mVisibleType);
+        mContainingNotification.resetBackgroundAlpha();
+        mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
+    }
+
+    public void setBackgroundTintColor(int color) {
+        if (mExpandedSmartReplyView != null) {
+            mExpandedSmartReplyView.setBackgroundTintColor(color);
+        }
+    }
+
+    public int getVisibleType() {
+        return mVisibleType;
+    }
+
+    public int getBackgroundColorForExpansionState() {
+        // When expanding or user locked we want the new type, when collapsing we want
+        // the original type
+        final int visibleType = (mContainingNotification.isGroupExpanded()
+                || mContainingNotification.isUserLocked())
+                        ? calculateVisibleType()
+                        : getVisibleType();
+        return getBackgroundColor(visibleType);
+    }
+
+    public int getBackgroundColor(int visibleType) {
+        NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
+        int customBackgroundColor = 0;
+        if (currentVisibleWrapper != null) {
+            customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
+        }
+        return customBackgroundColor;
+    }
+
+    private void updateViewVisibilities(int visibleType) {
+        updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
+                mContractedChild, mContractedWrapper);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
+                mExpandedChild, mExpandedWrapper);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
+                mHeadsUpChild, mHeadsUpWrapper);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
+                mSingleLineView, mSingleLineView);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
+                mAmbientChild, mAmbientWrapper);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
+                mAmbientSingleLineChild, mAmbientSingleLineChild);
+        fireExpandedVisibleListenerIfVisible();
+        // updateViewVisibilities cancels outstanding animations without updating the
+        // mAnimationStartVisibleType. Do so here instead.
+        mAnimationStartVisibleType = UNDEFINED;
+    }
+
+    private void updateViewVisibility(int visibleType, int type, View view,
+            TransformableView wrapper) {
+        if (view != null) {
+            wrapper.setVisible(visibleType == type);
+        }
+    }
+
+    private void animateToVisibleType(int visibleType) {
+        final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
+        final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
+        if (shownView == hiddenView || hiddenView == null) {
+            shownView.setVisible(true);
+            return;
+        }
+        mAnimationStartVisibleType = mVisibleType;
+        shownView.transformFrom(hiddenView);
+        getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
+        hiddenView.transformTo(shownView, new Runnable() {
+            @Override
+            public void run() {
+                if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
+                    hiddenView.setVisible(false);
+                }
+                mAnimationStartVisibleType = UNDEFINED;
+            }
+        });
+        fireExpandedVisibleListenerIfVisible();
+    }
+
+    private void transferRemoteInputFocus(int visibleType) {
+        if (visibleType == VISIBLE_TYPE_HEADSUP
+                && mHeadsUpRemoteInput != null
+                && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
+            mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
+        }
+        if (visibleType == VISIBLE_TYPE_EXPANDED
+                && mExpandedRemoteInput != null
+                && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
+            mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
+        }
+    }
+
+    /**
+     * @param visibleType one of the static enum types in this view
+     * @return the corresponding transformable view according to the given visible type
+     */
+    private TransformableView getTransformableViewForVisibleType(int visibleType) {
+        switch (visibleType) {
+            case VISIBLE_TYPE_EXPANDED:
+                return mExpandedWrapper;
+            case VISIBLE_TYPE_HEADSUP:
+                return mHeadsUpWrapper;
+            case VISIBLE_TYPE_SINGLELINE:
+                return mSingleLineView;
+            case VISIBLE_TYPE_AMBIENT:
+                return mAmbientWrapper;
+            case VISIBLE_TYPE_AMBIENT_SINGLELINE:
+                return mAmbientSingleLineChild;
+            default:
+                return mContractedWrapper;
+        }
+    }
+
+    /**
+     * @param visibleType one of the static enum types in this view
+     * @return the corresponding view according to the given visible type
+     */
+    private View getViewForVisibleType(int visibleType) {
+        switch (visibleType) {
+            case VISIBLE_TYPE_EXPANDED:
+                return mExpandedChild;
+            case VISIBLE_TYPE_HEADSUP:
+                return mHeadsUpChild;
+            case VISIBLE_TYPE_SINGLELINE:
+                return mSingleLineView;
+            case VISIBLE_TYPE_AMBIENT:
+                return mAmbientChild;
+            case VISIBLE_TYPE_AMBIENT_SINGLELINE:
+                return mAmbientSingleLineChild;
+            default:
+                return mContractedChild;
+        }
+    }
+
+    public NotificationViewWrapper getVisibleWrapper(int visibleType) {
+        switch (visibleType) {
+            case VISIBLE_TYPE_EXPANDED:
+                return mExpandedWrapper;
+            case VISIBLE_TYPE_HEADSUP:
+                return mHeadsUpWrapper;
+            case VISIBLE_TYPE_CONTRACTED:
+                return mContractedWrapper;
+            case VISIBLE_TYPE_AMBIENT:
+                return mAmbientWrapper;
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * @return one of the static enum types in this view, calculated form the current state
+     */
+    public int calculateVisibleType() {
+        if (mContainingNotification.isShowingAmbient()) {
+            if (mIsChildInGroup && mAmbientSingleLineChild != null) {
+                return VISIBLE_TYPE_AMBIENT_SINGLELINE;
+            } else if (mAmbientChild != null) {
+                return VISIBLE_TYPE_AMBIENT;
+            } else {
+                return VISIBLE_TYPE_CONTRACTED;
+            }
+        }
+        if (mUserExpanding) {
+            int height = !mIsChildInGroup || isGroupExpanded()
+                    || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
+                    ? mContainingNotification.getMaxContentHeight()
+                    : mContainingNotification.getShowingLayout().getMinHeight();
+            if (height == 0) {
+                height = mContentHeight;
+            }
+            int expandedVisualType = getVisualTypeForHeight(height);
+            int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
+                    ? VISIBLE_TYPE_SINGLELINE
+                    : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
+            return mTransformationStartVisibleType == collapsedVisualType
+                    ? expandedVisualType
+                    : collapsedVisualType;
+        }
+        int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
+        int viewHeight = mContentHeight;
+        if (intrinsicHeight != 0) {
+            // the intrinsicHeight might be 0 because it was just reset.
+            viewHeight = Math.min(mContentHeight, intrinsicHeight);
+        }
+        return getVisualTypeForHeight(viewHeight);
+    }
+
+    private int getVisualTypeForHeight(float viewHeight) {
+        boolean noExpandedChild = mExpandedChild == null;
+        if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) {
+            return VISIBLE_TYPE_EXPANDED;
+        }
+        if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
+            return VISIBLE_TYPE_SINGLELINE;
+        }
+
+        if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
+                && !mContainingNotification.isOnKeyguard()) {
+            if (viewHeight <= getViewHeight(VISIBLE_TYPE_HEADSUP) || noExpandedChild) {
+                return VISIBLE_TYPE_HEADSUP;
+            } else {
+                return VISIBLE_TYPE_EXPANDED;
+            }
+        } else {
+            if (noExpandedChild || (viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
+                    && (!mIsChildInGroup || isGroupExpanded()
+                            || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
+                return VISIBLE_TYPE_CONTRACTED;
+            } else {
+                return VISIBLE_TYPE_EXPANDED;
+            }
+        }
+    }
+
+    public boolean isContentExpandable() {
+        return mIsContentExpandable;
+    }
+
+    public void setDark(boolean dark, boolean fade, long delay) {
+        if (mContractedChild == null) {
+            return;
+        }
+        mDark = dark;
+        selectLayout(!dark && fade /* animate */, false /* force */);
+    }
+
+    public void setHeadsUp(boolean headsUp) {
+        mIsHeadsUp = headsUp;
+        selectLayout(false /* animate */, true /* force */);
+        updateExpandButtons(mExpandable);
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+
+        // This is not really true, but good enough when fading from the contracted to the expanded
+        // layout, and saves us some layers.
+        return false;
+    }
+
+    public void setLegacy(boolean legacy) {
+        mLegacy = legacy;
+        updateLegacy();
+    }
+
+    private void updateLegacy() {
+        if (mContractedChild != null) {
+            mContractedWrapper.setLegacy(mLegacy);
+        }
+        if (mExpandedChild != null) {
+            mExpandedWrapper.setLegacy(mLegacy);
+        }
+        if (mHeadsUpChild != null) {
+            mHeadsUpWrapper.setLegacy(mLegacy);
+        }
+    }
+
+    public void setIsChildInGroup(boolean isChildInGroup) {
+        mIsChildInGroup = isChildInGroup;
+        if (mContractedChild != null) {
+            mContractedWrapper.setIsChildInGroup(mIsChildInGroup);
+        }
+        if (mExpandedChild != null) {
+            mExpandedWrapper.setIsChildInGroup(mIsChildInGroup);
+        }
+        if (mHeadsUpChild != null) {
+            mHeadsUpWrapper.setIsChildInGroup(mIsChildInGroup);
+        }
+        if (mAmbientChild != null) {
+            mAmbientWrapper.setIsChildInGroup(mIsChildInGroup);
+        }
+        updateAllSingleLineViews();
+    }
+
+    public void onNotificationUpdated(NotificationData.Entry entry) {
+        mStatusBarNotification = entry.notification;
+        mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
+        updateAllSingleLineViews();
+        if (mContractedChild != null) {
+            mContractedWrapper.onContentUpdated(entry.row);
+        }
+        if (mExpandedChild != null) {
+            mExpandedWrapper.onContentUpdated(entry.row);
+        }
+        if (mHeadsUpChild != null) {
+            mHeadsUpWrapper.onContentUpdated(entry.row);
+        }
+        if (mAmbientChild != null) {
+            mAmbientWrapper.onContentUpdated(entry.row);
+        }
+        applyRemoteInputAndSmartReply(entry);
+        updateLegacy();
+        mForceSelectNextLayout = true;
+        setDark(mDark, false /* animate */, 0 /* delay */);
+        mPreviousExpandedRemoteInputIntent = null;
+        mPreviousHeadsUpRemoteInputIntent = null;
+    }
+
+    private void updateAllSingleLineViews() {
+        updateSingleLineView();
+        updateAmbientSingleLineView();
+    }
+    private void updateSingleLineView() {
+        if (mIsChildInGroup) {
+            boolean isNewView = mSingleLineView == null;
+            mSingleLineView = mHybridGroupManager.bindFromNotification(
+                    mSingleLineView, mStatusBarNotification.getNotification());
+            if (isNewView) {
+                updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE,
+                        mSingleLineView, mSingleLineView);
+            }
+        } else if (mSingleLineView != null) {
+            removeView(mSingleLineView);
+            mSingleLineView = null;
+        }
+    }
+
+    private void updateAmbientSingleLineView() {
+        if (mIsChildInGroup) {
+            boolean isNewView = mAmbientSingleLineChild == null;
+            mAmbientSingleLineChild = mHybridGroupManager.bindAmbientFromNotification(
+                    mAmbientSingleLineChild, mStatusBarNotification.getNotification());
+            if (isNewView) {
+                updateViewVisibility(mVisibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
+                        mAmbientSingleLineChild, mAmbientSingleLineChild);
+            }
+        } else if (mAmbientSingleLineChild != null) {
+            removeView(mAmbientSingleLineChild);
+            mAmbientSingleLineChild = null;
+        }
+    }
+
+    private void applyRemoteInputAndSmartReply(final NotificationData.Entry entry) {
+        if (mRemoteInputController == null) {
+            return;
+        }
+
+        boolean enableSmartReplies = (mSmartReplyConstants.isEnabled()
+                && (!mSmartReplyConstants.requiresTargetingP()
+                    || entry.targetSdk >= Build.VERSION_CODES.P));
+
+        boolean hasRemoteInput = false;
+        RemoteInput remoteInputWithChoices = null;
+        PendingIntent pendingIntentWithChoices = null;
+        CharSequence[] choices = null;
+
+        Notification.Action[] actions = entry.notification.getNotification().actions;
+        if (actions != null) {
+            for (Notification.Action a : actions) {
+                if (a.getRemoteInputs() == null) {
+                    continue;
+                }
+                for (RemoteInput ri : a.getRemoteInputs()) {
+                    boolean showRemoteInputView = ri.getAllowFreeFormInput();
+                    boolean showSmartReplyView = enableSmartReplies
+                            && (ArrayUtils.isEmpty(ri.getChoices())
+                            || (showRemoteInputView && !ArrayUtils.isEmpty(entry.smartReplies)));
+                    if (showRemoteInputView) {
+                        hasRemoteInput = true;
+                    }
+                    if (showSmartReplyView) {
+                        remoteInputWithChoices = ri;
+                        pendingIntentWithChoices = a.actionIntent;
+                        if (!ArrayUtils.isEmpty(ri.getChoices())) {
+                            choices = ri.getChoices();
+                        } else {
+                            choices = entry.smartReplies;
+                        }
+                    }
+                    if (showRemoteInputView || showSmartReplyView) {
+                        break;
+                    }
+                }
+            }
+        }
+
+        applyRemoteInput(entry, hasRemoteInput);
+        applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry, choices);
+    }
+
+    private void applyRemoteInput(NotificationData.Entry entry, boolean hasRemoteInput) {
+        View bigContentView = mExpandedChild;
+        if (bigContentView != null) {
+            mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
+                    mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
+                    mExpandedWrapper);
+        } else {
+            mExpandedRemoteInput = null;
+        }
+        if (mCachedExpandedRemoteInput != null
+                && mCachedExpandedRemoteInput != mExpandedRemoteInput) {
+            // We had a cached remote input but didn't reuse it. Clean up required.
+            mCachedExpandedRemoteInput.dispatchFinishTemporaryDetach();
+        }
+        mCachedExpandedRemoteInput = null;
+
+        View headsUpContentView = mHeadsUpChild;
+        if (headsUpContentView != null) {
+            mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
+                    mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
+        } else {
+            mHeadsUpRemoteInput = null;
+        }
+        if (mCachedHeadsUpRemoteInput != null
+                && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
+            // We had a cached remote input but didn't reuse it. Clean up required.
+            mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
+        }
+        mCachedHeadsUpRemoteInput = null;
+    }
+
+    private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
+            boolean hasRemoteInput, PendingIntent existingPendingIntent,
+            RemoteInputView cachedView, NotificationViewWrapper wrapper) {
+        View actionContainerCandidate = view.findViewById(
+                com.android.internal.R.id.actions_container);
+        if (actionContainerCandidate instanceof FrameLayout) {
+            RemoteInputView existing = (RemoteInputView)
+                    view.findViewWithTag(RemoteInputView.VIEW_TAG);
+
+            if (existing != null) {
+                existing.onNotificationUpdateOrReset();
+            }
+
+            if (existing == null && hasRemoteInput) {
+                ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
+                if (cachedView == null) {
+                    RemoteInputView riv = RemoteInputView.inflate(
+                            mContext, actionContainer, entry, mRemoteInputController);
+
+                    riv.setVisibility(View.INVISIBLE);
+                    actionContainer.addView(riv, new LayoutParams(
+                            ViewGroup.LayoutParams.MATCH_PARENT,
+                            ViewGroup.LayoutParams.MATCH_PARENT)
+                    );
+                    existing = riv;
+                } else {
+                    actionContainer.addView(cachedView);
+                    cachedView.dispatchFinishTemporaryDetach();
+                    cachedView.requestFocus();
+                    existing = cachedView;
+                }
+            }
+            if (hasRemoteInput) {
+                int color = entry.notification.getNotification().color;
+                if (color == Notification.COLOR_DEFAULT) {
+                    color = mContext.getColor(R.color.default_remote_input_background);
+                }
+                existing.setBackgroundColor(ContrastColorUtil.ensureTextBackgroundColor(color,
+                        mContext.getColor(R.color.remote_input_text_enabled),
+                        mContext.getColor(R.color.remote_input_hint)));
+
+                existing.setWrapper(wrapper);
+                existing.setOnVisibilityChangedListener(this::setRemoteInputVisible);
+
+                if (existingPendingIntent != null || existing.isActive()) {
+                    // The current action could be gone, or the pending intent no longer valid.
+                    // If we find a matching action in the new notification, focus, otherwise close.
+                    Notification.Action[] actions = entry.notification.getNotification().actions;
+                    if (existingPendingIntent != null) {
+                        existing.setPendingIntent(existingPendingIntent);
+                    }
+                    if (existing.updatePendingIntentFromActions(actions)) {
+                        if (!existing.isActive()) {
+                            existing.focus();
+                        }
+                    } else {
+                        if (existing.isActive()) {
+                            existing.close();
+                        }
+                    }
+                }
+            }
+            return existing;
+        }
+        return null;
+    }
+
+    private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent,
+            NotificationData.Entry entry, CharSequence[] choices) {
+        if (mExpandedChild != null) {
+            mExpandedSmartReplyView =
+                    applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry, choices);
+            if (mExpandedSmartReplyView != null && remoteInput != null
+                    && remoteInput.getChoices() != null && remoteInput.getChoices().length > 0) {
+                mSmartReplyController.smartRepliesAdded(entry, remoteInput.getChoices().length);
+            }
+        }
+    }
+
+    private SmartReplyView applySmartReplyView(
+            View view, RemoteInput remoteInput, PendingIntent pendingIntent,
+            NotificationData.Entry entry, CharSequence[] choices) {
+        View smartReplyContainerCandidate = view.findViewById(
+                com.android.internal.R.id.smart_reply_container);
+        if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
+            return null;
+        }
+        LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
+        if (remoteInput == null || pendingIntent == null) {
+            smartReplyContainer.setVisibility(View.GONE);
+            return null;
+        }
+        // If we are showing the spinner we don't want to add the buttons.
+        boolean showingSpinner = entry.notification.getNotification()
+                .extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
+        if (showingSpinner) {
+            smartReplyContainer.setVisibility(View.GONE);
+            return null;
+        }
+        // If we are keeping the notification around while sending we don't want to add the buttons.
+        boolean hideSmartReplies = entry.notification.getNotification()
+                .extras.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false);
+        if (hideSmartReplies) {
+            smartReplyContainer.setVisibility(View.GONE);
+            return null;
+        }
+        SmartReplyView smartReplyView = null;
+        if (smartReplyContainer.getChildCount() == 0) {
+            smartReplyView = SmartReplyView.inflate(mContext, smartReplyContainer);
+            smartReplyContainer.addView(smartReplyView);
+        } else if (smartReplyContainer.getChildCount() == 1) {
+            View child = smartReplyContainer.getChildAt(0);
+            if (child instanceof SmartReplyView) {
+                smartReplyView = (SmartReplyView) child;
+            }
+        }
+        if (smartReplyView != null) {
+            smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent,
+                    mSmartReplyController, entry, smartReplyContainer, choices
+            );
+            smartReplyContainer.setVisibility(View.VISIBLE);
+        }
+        return smartReplyView;
+    }
+
+    public void closeRemoteInput() {
+        if (mHeadsUpRemoteInput != null) {
+            mHeadsUpRemoteInput.close();
+        }
+        if (mExpandedRemoteInput != null) {
+            mExpandedRemoteInput.close();
+        }
+    }
+
+    public void setGroupManager(NotificationGroupManager groupManager) {
+        mGroupManager = groupManager;
+    }
+
+    public void setRemoteInputController(RemoteInputController r) {
+        mRemoteInputController = r;
+    }
+
+    public void setExpandClickListener(OnClickListener expandClickListener) {
+        mExpandClickListener = expandClickListener;
+    }
+
+    public void updateExpandButtons(boolean expandable) {
+        mExpandable = expandable;
+        // if the expanded child has the same height as the collapsed one we hide it.
+        if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
+            if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
+                    || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
+                if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
+                    expandable = false;
+                }
+            } else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
+                expandable = false;
+            }
+        }
+        if (mExpandedChild != null) {
+            mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
+        }
+        if (mContractedChild != null) {
+            mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
+        }
+        if (mHeadsUpChild != null) {
+            mHeadsUpWrapper.updateExpandability(expandable,  mExpandClickListener);
+        }
+        mIsContentExpandable = expandable;
+    }
+
+    public NotificationHeaderView getNotificationHeader() {
+        NotificationHeaderView header = null;
+        if (mContractedChild != null) {
+            header = mContractedWrapper.getNotificationHeader();
+        }
+        if (header == null && mExpandedChild != null) {
+            header = mExpandedWrapper.getNotificationHeader();
+        }
+        if (header == null && mHeadsUpChild != null) {
+            header = mHeadsUpWrapper.getNotificationHeader();
+        }
+        if (header == null && mAmbientChild != null) {
+            header = mAmbientWrapper.getNotificationHeader();
+        }
+        return header;
+    }
+
+    public void showAppOpsIcons(ArraySet<Integer> activeOps) {
+        if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
+            mContractedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
+        }
+        if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
+            mExpandedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
+        }
+        if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
+            mHeadsUpWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
+        }
+    }
+
+    public NotificationHeaderView getContractedNotificationHeader() {
+        if (mContractedChild != null) {
+            return mContractedWrapper.getNotificationHeader();
+        }
+        return null;
+    }
+
+    public NotificationHeaderView getVisibleNotificationHeader() {
+        NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
+        return wrapper == null ? null : wrapper.getNotificationHeader();
+    }
+
+    public void setContainingNotification(ExpandableNotificationRow containingNotification) {
+        mContainingNotification = containingNotification;
+    }
+
+    public void requestSelectLayout(boolean needsAnimation) {
+        selectLayout(needsAnimation, false);
+    }
+
+    public void reInflateViews() {
+        if (mIsChildInGroup && mSingleLineView != null) {
+            removeView(mSingleLineView);
+            mSingleLineView = null;
+            updateAllSingleLineViews();
+        }
+    }
+
+    public void setUserExpanding(boolean userExpanding) {
+        mUserExpanding = userExpanding;
+        if (userExpanding) {
+            mTransformationStartVisibleType = mVisibleType;
+        } else {
+            mTransformationStartVisibleType = UNDEFINED;
+            mVisibleType = calculateVisibleType();
+            updateViewVisibilities(mVisibleType);
+            updateBackgroundColor(false);
+        }
+    }
+
+    /**
+     * Set by how much the single line view should be indented. Used when a overflow indicator is
+     * present and only during measuring
+     */
+    public void setSingleLineWidthIndention(int singleLineWidthIndention) {
+        if (singleLineWidthIndention != mSingleLineWidthIndention) {
+            mSingleLineWidthIndention = singleLineWidthIndention;
+            mContainingNotification.forceLayout();
+            forceLayout();
+        }
+    }
+
+    public HybridNotificationView getSingleLineView() {
+        return mSingleLineView;
+    }
+
+    public void setRemoved() {
+        if (mExpandedRemoteInput != null) {
+            mExpandedRemoteInput.setRemoved();
+        }
+        if (mHeadsUpRemoteInput != null) {
+            mHeadsUpRemoteInput.setRemoved();
+        }
+    }
+
+    public void setContentHeightAnimating(boolean animating) {
+        if (!animating) {
+            mContentHeightAtAnimationStart = UNDEFINED;
+        }
+    }
+
+    @VisibleForTesting
+    boolean isAnimatingVisibleType() {
+        return mAnimationStartVisibleType != UNDEFINED;
+    }
+
+    public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
+        mHeadsUpAnimatingAway = headsUpAnimatingAway;
+        selectLayout(false /* animate */, true /* force */);
+    }
+
+    public void setFocusOnVisibilityChange() {
+        mFocusOnVisibilityChange = true;
+    }
+
+    public void setIconsVisible(boolean iconsVisible) {
+        mIconsVisible = iconsVisible;
+        updateIconVisibilities();
+    }
+
+    private void updateIconVisibilities() {
+        if (mContractedWrapper != null) {
+            NotificationHeaderView header = mContractedWrapper.getNotificationHeader();
+            if (header != null) {
+                header.getIcon().setForceHidden(!mIconsVisible);
+            }
+        }
+        if (mHeadsUpWrapper != null) {
+            NotificationHeaderView header = mHeadsUpWrapper.getNotificationHeader();
+            if (header != null) {
+                header.getIcon().setForceHidden(!mIconsVisible);
+            }
+        }
+        if (mExpandedWrapper != null) {
+            NotificationHeaderView header = mExpandedWrapper.getNotificationHeader();
+            if (header != null) {
+                header.getIcon().setForceHidden(!mIconsVisible);
+            }
+        }
+    }
+
+    @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        super.onVisibilityAggregated(isVisible);
+        if (isVisible) {
+            fireExpandedVisibleListenerIfVisible();
+        }
+    }
+
+    /**
+     * Sets a one-shot listener for when the expanded view becomes visible.
+     *
+     * This will fire the listener immediately if the expanded view is already visible.
+     */
+    public void setOnExpandedVisibleListener(Runnable r) {
+        mExpandedVisibleListener = r;
+        fireExpandedVisibleListenerIfVisible();
+    }
+
+    public void setIsLowPriority(boolean isLowPriority) {
+        mIsLowPriority = isLowPriority;
+    }
+
+    public boolean isDimmable() {
+        if (!mContractedWrapper.isDimmable()) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Should a single click be disallowed on this view when on the keyguard?
+     */
+    public boolean disallowSingleClick(float x, float y) {
+        NotificationViewWrapper visibleWrapper = getVisibleWrapper(getVisibleType());
+        if (visibleWrapper != null) {
+            return visibleWrapper.disallowSingleClick(x, y);
+        }
+        return false;
+    }
+
+    public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
+        boolean needsPaddings = shouldClipToRounding(getVisibleType(), topRounded, bottomRounded);
+        if (mUserExpanding) {
+             needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType, topRounded,
+                     bottomRounded);
+        }
+        return needsPaddings;
+    }
+
+    private boolean shouldClipToRounding(int visibleType, boolean topRounded,
+            boolean bottomRounded) {
+        NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
+        if (visibleWrapper == null) {
+            return false;
+        }
+        return visibleWrapper.shouldClipToRounding(topRounded, bottomRounded);
+    }
+
+    public CharSequence getActiveRemoteInputText() {
+        if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) {
+            return mExpandedRemoteInput.getText();
+        }
+        if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) {
+            return mHeadsUpRemoteInput.getText();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        float y = ev.getY();
+        // We still want to distribute touch events to the remote input even if it's outside the
+        // view boundary. We're therefore manually dispatching these events to the remote view
+        RemoteInputView riv = getRemoteInputForView(getViewForVisibleType(mVisibleType));
+        if (riv != null && riv.getVisibility() == VISIBLE) {
+            int inputStart = mUnrestrictedContentHeight - riv.getHeight();
+            if (y <= mUnrestrictedContentHeight && y >= inputStart) {
+                ev.offsetLocation(0, -inputStart);
+                return riv.dispatchTouchEvent(ev);
+            }
+        }
+        return super.dispatchTouchEvent(ev);
+    }
+
+    /**
+     * Overridden to make sure touches to the reply action bar actually go through to this view
+     */
+    @Override
+    public boolean pointInView(float localX, float localY, float slop) {
+        float top = mClipTopAmount;
+        float bottom = mUnrestrictedContentHeight;
+        return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
+                localY < (bottom + slop);
+    }
+
+    private RemoteInputView getRemoteInputForView(View child) {
+        if (child == mExpandedChild) {
+            return mExpandedRemoteInput;
+        } else if (child == mHeadsUpChild) {
+            return mHeadsUpRemoteInput;
+        }
+        return null;
+    }
+
+    public int getExpandHeight() {
+        int viewType = VISIBLE_TYPE_EXPANDED;
+        if (mExpandedChild == null) {
+            viewType = VISIBLE_TYPE_CONTRACTED;
+        }
+        return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
+    }
+
+    public int getHeadsUpHeight() {
+        int viewType = VISIBLE_TYPE_HEADSUP;
+        if (mHeadsUpChild == null) {
+            viewType = VISIBLE_TYPE_CONTRACTED;
+        }
+        // The headsUp remote input quickly switches to the expanded one, so lets also include that
+        // one
+        return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
+                + getExtraRemoteInputHeight(mExpandedRemoteInput);
+    }
+
+    public void setRemoteInputVisible(boolean remoteInputVisible) {
+        mRemoteInputVisible = remoteInputVisible;
+        setClipChildren(!remoteInputVisible);
+    }
+
+    @Override
+    public void setClipChildren(boolean clipChildren) {
+        clipChildren = clipChildren && !mRemoteInputVisible;
+        super.setClipChildren(clipChildren);
+    }
+
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+        if (mContractedWrapper != null) {
+            mContractedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
+        }
+        if (mHeadsUpWrapper != null) {
+            mHeadsUpWrapper.setHeaderVisibleAmount(headerVisibleAmount);
+        }
+        if (mExpandedWrapper != null) {
+            mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
new file mode 100644
index 0000000..0a197da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -0,0 +1,428 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import androidx.annotation.Nullable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.FrameLayout;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+
+/**
+ * The guts of a notification revealed when performing a long press.
+ */
+public class NotificationGuts extends FrameLayout {
+    private static final String TAG = "NotificationGuts";
+    private static final long CLOSE_GUTS_DELAY = 8000;
+
+    private Drawable mBackground;
+    private int mClipTopAmount;
+    private int mClipBottomAmount;
+    private int mActualHeight;
+    private boolean mExposed;
+
+    private Handler mHandler;
+    private Runnable mFalsingCheck;
+    private boolean mNeedsFalsingProtection;
+    private OnGutsClosedListener mClosedListener;
+    private OnHeightChangedListener mHeightListener;
+
+    private GutsContent mGutsContent;
+
+    public interface GutsContent {
+
+        public void setGutsParent(NotificationGuts listener);
+
+        /**
+         * Return the view to be shown in the notification guts.
+         */
+        public View getContentView();
+
+        /**
+         * Return the actual height of the content.
+         */
+        public int getActualHeight();
+
+        /**
+         * Called when the guts view have been told to close, typically after an outside
+         * interaction.
+         *
+         * @param save whether the state should be saved.
+         * @param force whether the guts view should be forced closed regardless of state.
+         * @return if closing the view has been handled.
+         */
+        public boolean handleCloseControls(boolean save, boolean force);
+
+        /**
+         * Return whether the notification associated with these guts is set to be removed.
+         */
+        public boolean willBeRemoved();
+
+        /**
+         * Return whether these guts are a leavebehind (e.g. {@link NotificationSnooze}).
+         */
+        public default boolean isLeavebehind() {
+            return false;
+        }
+
+        /**
+         * Return whether something changed and needs to be saved, possibly requiring a bouncer.
+         */
+        boolean shouldBeSaved();
+    }
+
+    public interface OnGutsClosedListener {
+        public void onGutsClosed(NotificationGuts guts);
+    }
+
+    public interface OnHeightChangedListener {
+        public void onHeightChanged(NotificationGuts guts);
+    }
+
+    interface OnSettingsClickListener {
+        void onClick(View v, int appUid);
+    }
+
+    public NotificationGuts(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(false);
+        mHandler = new Handler();
+        mFalsingCheck = new Runnable() {
+            @Override
+            public void run() {
+                if (mNeedsFalsingProtection && mExposed) {
+                    closeControls(-1 /* x */, -1 /* y */, false /* save */, false /* force */);
+                }
+            }
+        };
+        final TypedArray ta = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.Theme, 0, 0);
+        ta.recycle();
+    }
+
+    public NotificationGuts(Context context) {
+        this(context, null);
+    }
+
+    public void setGutsContent(GutsContent content) {
+        mGutsContent = content;
+        removeAllViews();
+        addView(mGutsContent.getContentView());
+    }
+
+    public GutsContent getGutsContent() {
+        return mGutsContent;
+    }
+
+    public void resetFalsingCheck() {
+        mHandler.removeCallbacks(mFalsingCheck);
+        if (mNeedsFalsingProtection && mExposed) {
+            mHandler.postDelayed(mFalsingCheck, CLOSE_GUTS_DELAY);
+        }
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        draw(canvas, mBackground);
+    }
+
+    private void draw(Canvas canvas, Drawable drawable) {
+        int top = mClipTopAmount;
+        int bottom = mActualHeight - mClipBottomAmount;
+        if (drawable != null && top < bottom) {
+            drawable.setBounds(0, top, getWidth(), bottom);
+            drawable.draw(canvas);
+        }
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mBackground = mContext.getDrawable(R.drawable.notification_guts_bg);
+        if (mBackground != null) {
+            mBackground.setCallback(this);
+        }
+    }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || who == mBackground;
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        drawableStateChanged(mBackground);
+    }
+
+    private void drawableStateChanged(Drawable d) {
+        if (d != null && d.isStateful()) {
+            d.setState(getDrawableState());
+        }
+    }
+
+    @Override
+    public void drawableHotspotChanged(float x, float y) {
+        if (mBackground != null) {
+            mBackground.setHotspot(x, y);
+        }
+    }
+
+    public void openControls(
+            boolean shouldDoCircularReveal,
+            int x,
+            int y,
+            boolean needsFalsingProtection,
+            @Nullable Runnable onAnimationEnd) {
+        animateOpen(shouldDoCircularReveal, x, y, onAnimationEnd);
+        setExposed(true /* exposed */, needsFalsingProtection);
+    }
+
+    /**
+     * Hide controls if they are visible
+     * @param leavebehinds true if leavebehinds should be closed
+     * @param controls true if controls should be closed
+     * @param x x coordinate to animate the close circular reveal with
+     * @param y y coordinate to animate the close circular reveal with
+     * @param force whether the guts should be force-closed regardless of state.
+     */
+    public void closeControls(boolean leavebehinds, boolean controls, int x, int y, boolean force) {
+        if (mGutsContent != null) {
+            if ((mGutsContent.isLeavebehind() && leavebehinds)
+                    || (!mGutsContent.isLeavebehind() && controls)) {
+                closeControls(x, y, mGutsContent.shouldBeSaved(), force);
+            }
+        }
+    }
+
+    /**
+     * Closes any exposed guts/views.
+     *
+     * @param x x coordinate to animate the close circular reveal with
+     * @param y y coordinate to animate the close circular reveal with
+     * @param save whether the state should be saved
+     * @param force whether the guts should be force-closed regardless of state.
+     */
+    public void closeControls(int x, int y, boolean save, boolean force) {
+        // First try to dismiss any blocking helper.
+        boolean wasBlockingHelperDismissed =
+                Dependency.get(NotificationBlockingHelperManager.class)
+                        .dismissCurrentBlockingHelper();
+
+        if (getWindowToken() == null) {
+            if (mClosedListener != null) {
+                mClosedListener.onGutsClosed(this);
+            }
+            return;
+        }
+
+        if (mGutsContent == null
+                || !mGutsContent.handleCloseControls(save, force)
+                || wasBlockingHelperDismissed) {
+            // We only want to do a circular reveal if we're not showing the blocking helper.
+            animateClose(x, y, !wasBlockingHelperDismissed /* shouldDoCircularReveal */);
+
+            setExposed(false, mNeedsFalsingProtection);
+            if (mClosedListener != null) {
+                mClosedListener.onGutsClosed(this);
+            }
+        }
+    }
+
+    /** Animates in the guts view via either a fade or a circular reveal. */
+    private void animateOpen(
+            boolean shouldDoCircularReveal, int x, int y, @Nullable Runnable onAnimationEnd) {
+        if (isAttachedToWindow()) {
+            if (shouldDoCircularReveal) {
+                double horz = Math.max(getWidth() - x, x);
+                double vert = Math.max(getHeight() - y, y);
+                float r = (float) Math.hypot(horz, vert);
+                // Circular reveal originating at (x, y)
+                Animator a = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
+                a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+                a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+                a.addListener(new AnimateOpenListener(onAnimationEnd));
+                a.start();
+            } else {
+                // Fade in content
+                this.setAlpha(0f);
+                this.animate()
+                        .alpha(1f)
+                        .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
+                        .setInterpolator(Interpolators.ALPHA_IN)
+                        .setListener(new AnimateOpenListener(onAnimationEnd))
+                        .start();
+            }
+        } else {
+            Log.w(TAG, "Failed to animate guts open");
+        }
+    }
+
+
+    /** Animates out the guts view via either a fade or a circular reveal. */
+    @VisibleForTesting
+    void animateClose(int x, int y, boolean shouldDoCircularReveal) {
+        if (isAttachedToWindow()) {
+            if (shouldDoCircularReveal) {
+                // Circular reveal originating at (x, y)
+                if (x == -1 || y == -1) {
+                    x = (getLeft() + getRight()) / 2;
+                    y = (getTop() + getHeight() / 2);
+                }
+                double horz = Math.max(getWidth() - x, x);
+                double vert = Math.max(getHeight() - y, y);
+                float r = (float) Math.hypot(horz, vert);
+                Animator a = ViewAnimationUtils.createCircularReveal(this,
+                        x, y, r, 0);
+                a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+                a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+                a.addListener(new AnimateCloseListener(this /* view */));
+                a.start();
+            } else {
+                // Fade in the blocking helper.
+                this.animate()
+                        .alpha(0f)
+                        .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
+                        .setInterpolator(Interpolators.ALPHA_OUT)
+                        .setListener(new AnimateCloseListener(this /* view */))
+                        .start();
+            }
+        } else {
+            Log.w(TAG, "Failed to animate guts close");
+        }
+    }
+
+    public void setActualHeight(int actualHeight) {
+        mActualHeight = actualHeight;
+        invalidate();
+    }
+
+    public int getActualHeight() {
+        return mActualHeight;
+    }
+
+    public int getIntrinsicHeight() {
+        return mGutsContent != null && mExposed ? mGutsContent.getActualHeight() : getHeight();
+    }
+
+    public void setClipTopAmount(int clipTopAmount) {
+        mClipTopAmount = clipTopAmount;
+        invalidate();
+    }
+
+    public void setClipBottomAmount(int clipBottomAmount) {
+        mClipBottomAmount = clipBottomAmount;
+        invalidate();
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        // Prevents this view from creating a layer when alpha is animating.
+        return false;
+    }
+
+    public void setClosedListener(OnGutsClosedListener listener) {
+        mClosedListener = listener;
+    }
+
+    public void setHeightChangedListener(OnHeightChangedListener listener) {
+        mHeightListener = listener;
+    }
+
+    protected void onHeightChanged() {
+        if (mHeightListener != null) {
+            mHeightListener.onHeightChanged(this);
+        }
+    }
+
+    @VisibleForTesting
+    void setExposed(boolean exposed, boolean needsFalsingProtection) {
+        final boolean wasExposed = mExposed;
+        mExposed = exposed;
+        mNeedsFalsingProtection = needsFalsingProtection;
+        if (mExposed && mNeedsFalsingProtection) {
+            resetFalsingCheck();
+        } else {
+            mHandler.removeCallbacks(mFalsingCheck);
+        }
+        if (wasExposed != mExposed && mGutsContent != null) {
+            final View contentView = mGutsContent.getContentView();
+            contentView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+            if (mExposed) {
+                contentView.requestAccessibilityFocus();
+            }
+        }
+    }
+
+    public boolean willBeRemoved() {
+        return mGutsContent != null ? mGutsContent.willBeRemoved() : false;
+    }
+
+    public boolean isExposed() {
+        return mExposed;
+    }
+
+    public boolean isLeavebehind() {
+        return mGutsContent != null && mGutsContent.isLeavebehind();
+    }
+
+    /** Listener for animations executed in {@link #animateOpen(boolean, int, int, Runnable)}. */
+    private static class AnimateOpenListener extends AnimatorListenerAdapter {
+        final Runnable mOnAnimationEnd;
+
+        private AnimateOpenListener(Runnable onAnimationEnd) {
+            mOnAnimationEnd = onAnimationEnd;
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            super.onAnimationEnd(animation);
+            if (mOnAnimationEnd != null) {
+                mOnAnimationEnd.run();
+            }
+        }
+    }
+
+    /** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */
+    private static class AnimateCloseListener extends AnimatorListenerAdapter {
+        final View mView;
+
+        private AnimateCloseListener(View view) {
+            mView = view;
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            super.onAnimationEnd(animation);
+            mView.setVisibility(View.GONE);
+        }
+    }
+}
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
new file mode 100644
index 0000000..ef0be880
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -0,0 +1,415 @@
+/*
+ * 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.notification.row;
+
+import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+import static android.service.notification.NotificationListenerService.Ranking
+        .USER_SENTIMENT_NEGATIVE;
+
+import android.app.INotificationManager;
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+import androidx.annotation.VisibleForTesting;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * 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.
+ */
+public class NotificationGutsManager implements Dumpable {
+    private static final String TAG = "NotificationGutsManager";
+
+    // Must match constant in Settings. Used to highlight preferences when linking to Settings.
+    private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+
+    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+    private final Context mContext;
+    private final AccessibilityManager mAccessibilityManager;
+
+    // Dependencies:
+    private final NotificationLockscreenUserManager mLockscreenUserManager =
+            Dependency.get(NotificationLockscreenUserManager.class);
+
+    // which notification is currently being longpress-examined by the user
+    private NotificationGuts mNotificationGutsExposed;
+    private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
+    protected NotificationPresenter mPresenter;
+    protected NotificationEntryManager mEntryManager;
+    private NotificationListContainer mListContainer;
+    private NotificationInfo.CheckSaveListener mCheckSaveListener;
+    private OnSettingsClickListener mOnSettingsClickListener;
+    private String mKeyToRemoveOnGutsClosed;
+
+    public NotificationGutsManager(Context context) {
+        mContext = context;
+        Resources res = context.getResources();
+
+        mAccessibilityManager = (AccessibilityManager)
+                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+    }
+
+    public void setUpWithPresenter(NotificationPresenter presenter,
+            NotificationEntryManager entryManager, NotificationListContainer listContainer,
+            NotificationInfo.CheckSaveListener checkSaveListener,
+            OnSettingsClickListener onSettingsClickListener) {
+        mPresenter = presenter;
+        mEntryManager = entryManager;
+        mListContainer = listContainer;
+        mCheckSaveListener = checkSaveListener;
+        mOnSettingsClickListener = onSettingsClickListener;
+    }
+
+    public String getKeyToRemoveOnGutsClosed() {
+        return mKeyToRemoveOnGutsClosed;
+    }
+
+    public void setKeyToRemoveOnGutsClosed(String keyToRemoveOnGutsClosed) {
+        mKeyToRemoveOnGutsClosed = keyToRemoveOnGutsClosed;
+    }
+
+    public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) {
+        setExposedGuts(row.getGuts());
+        bindGuts(row);
+    }
+
+    /**
+     * Sends an intent to open the app settings for a particular package and optional
+     * channel.
+     */
+    private void startAppNotificationSettingsActivity(String packageName, final int appUid,
+            final NotificationChannel channel, ExpandableNotificationRow row) {
+        final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+        intent.setData(Uri.fromParts("package", packageName, null));
+        intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
+        intent.putExtra(Settings.EXTRA_APP_UID, appUid);
+        if (channel != null) {
+            intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
+        }
+        mPresenter.startNotificationGutsIntent(intent, appUid, row);
+    }
+
+    protected void startAppOpsSettingsActivity(String pkg, int uid, ArraySet<Integer> ops,
+            ExpandableNotificationRow row) {
+        if (ops.contains(OP_SYSTEM_ALERT_WINDOW)) {
+            if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
+                startAppNotificationSettingsActivity(pkg, uid, null, row);
+            } else {
+                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
+                intent.setData(Uri.fromParts("package", pkg, null));
+                mPresenter.startNotificationGutsIntent(intent, uid, row);
+            }
+        } else if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
+            Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
+            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, pkg);
+            mPresenter.startNotificationGutsIntent(intent, uid, row);
+        }
+    }
+
+    public void bindGuts(final ExpandableNotificationRow row) {
+        bindGuts(row, mGutsMenuItem);
+    }
+
+    private void bindGuts(final ExpandableNotificationRow row,
+            NotificationMenuRowPlugin.MenuItem item) {
+        StatusBarNotification sbn = row.getStatusBarNotification();
+
+        row.inflateGuts();
+        row.setGutsView(item);
+        row.setTag(sbn.getPackageName());
+        row.getGuts().setClosedListener((NotificationGuts g) -> {
+            row.onGutsClosed();
+            if (!g.willBeRemoved() && !row.isRemoved()) {
+                mListContainer.onHeightChanged(
+                        row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
+            }
+            if (mNotificationGutsExposed == g) {
+                mNotificationGutsExposed = null;
+                mGutsMenuItem = null;
+            }
+            String key = sbn.getKey();
+            if (key.equals(mKeyToRemoveOnGutsClosed)) {
+                mKeyToRemoveOnGutsClosed = null;
+                mEntryManager.removeNotification(key, mEntryManager.getLatestRankingMap());
+            }
+        });
+
+        View gutsView = item.getGutsView();
+        if (gutsView instanceof NotificationSnooze) {
+            initializeSnoozeView(row, (NotificationSnooze) gutsView);
+        } else if (gutsView instanceof AppOpsInfo) {
+            initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
+        } else if (gutsView instanceof NotificationInfo) {
+            initializeNotificationInfo(row, (NotificationInfo) gutsView);
+        }
+    }
+
+    /**
+     * Sets up the {@link NotificationSnooze} inside the notification row's guts.
+     *
+     * @param row view to set up the guts for
+     * @param notificationSnoozeView view to set up/bind within {@code row}
+     */
+    private void initializeSnoozeView(
+            final ExpandableNotificationRow row,
+            NotificationSnooze notificationSnoozeView) {
+        NotificationGuts guts = row.getGuts();
+        StatusBarNotification sbn = row.getStatusBarNotification();
+
+        notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
+        notificationSnoozeView.setStatusBarNotification(sbn);
+        notificationSnoozeView.setSnoozeOptions(row.getEntry().snoozeCriteria);
+        guts.setHeightChangedListener((NotificationGuts g) -> {
+            mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
+        });
+    }
+
+    /**
+     * Sets up the {@link AppOpsInfo} inside the notification row's guts.
+     *
+     * @param row view to set up the guts for
+     * @param appOpsInfoView view to set up/bind within {@code row}
+     */
+    private void initializeAppOpsInfo(
+            final ExpandableNotificationRow row,
+            AppOpsInfo appOpsInfoView) {
+        NotificationGuts guts = row.getGuts();
+        StatusBarNotification sbn = row.getStatusBarNotification();
+        UserHandle userHandle = sbn.getUser();
+        PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+                userHandle.getIdentifier());
+
+        AppOpsInfo.OnSettingsClickListener onSettingsClick =
+                (View v, String pkg, int uid, ArraySet<Integer> ops) -> {
+            mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
+            guts.resetFalsingCheck();
+            startAppOpsSettingsActivity(pkg, uid, ops, row);
+        };
+        if (!row.getEntry().mActiveAppOps.isEmpty()) {
+            appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
+        }
+    }
+
+    /**
+     * Sets up the {@link NotificationInfo} inside the notification row's guts.
+     *
+     * @param row view to set up the guts for
+     * @param notificationInfoView view to set up/bind within {@code row}
+     */
+    @VisibleForTesting
+    void initializeNotificationInfo(
+            final ExpandableNotificationRow row,
+            NotificationInfo notificationInfoView) {
+        NotificationGuts guts = row.getGuts();
+        StatusBarNotification sbn = row.getStatusBarNotification();
+        String packageName = sbn.getPackageName();
+        // Settings link is only valid for notifications that specify a non-system user
+        NotificationInfo.OnSettingsClickListener onSettingsClick = null;
+        UserHandle userHandle = sbn.getUser();
+        PackageManager pmUser = StatusBar.getPackageManagerForUser(
+                mContext, userHandle.getIdentifier());
+        INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick =
+                (View v, Intent intent) -> {
+                    mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
+                    guts.resetFalsingCheck();
+                    mPresenter.startNotificationGutsIntent(intent, sbn.getUid(), row);
+                };
+        boolean isForBlockingHelper = row.isBlockingHelperShowing();
+
+        if (!userHandle.equals(UserHandle.ALL)
+                || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
+            onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
+                mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
+                guts.resetFalsingCheck();
+                mOnSettingsClickListener.onClick(sbn.getKey());
+                startAppNotificationSettingsActivity(packageName, appUid, channel, row);
+            };
+        }
+
+        try {
+            notificationInfoView.bindNotification(
+                    pmUser,
+                    iNotificationManager,
+                    packageName,
+                    row.getEntry().channel,
+                    row.getNumUniqueChannels(),
+                    sbn,
+                    mCheckSaveListener,
+                    onSettingsClick,
+                    onAppSettingsClick,
+                    row.getIsNonblockable(),
+                    isForBlockingHelper,
+                    row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE);
+        } catch (RemoteException e) {
+            Log.e(TAG, e.toString());
+        }
+    }
+
+    /**
+     * Closes guts or notification menus that might be visible and saves any changes.
+     *
+     * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
+     * @param force true if guts should be closed regardless of state (used for snooze only).
+     * @param removeControls true if controls (e.g. info) should be closed.
+     * @param x if closed based on touch location, this is the x touch location.
+     * @param y if closed based on touch location, this is the y touch location.
+     * @param resetMenu if any notification menus that might be revealed should be closed.
+     */
+    public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
+            int x, int y, boolean resetMenu) {
+        if (mNotificationGutsExposed != null) {
+            mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
+        }
+        if (resetMenu) {
+            mListContainer.resetExposedMenuView(false /* animate */, true /* force */);
+        }
+    }
+
+    /**
+     * Returns the exposed NotificationGuts or null if none are exposed.
+     */
+    public NotificationGuts getExposedGuts() {
+        return mNotificationGutsExposed;
+    }
+
+    public void setExposedGuts(NotificationGuts guts) {
+        mNotificationGutsExposed = guts;
+    }
+
+    /**
+     * Opens guts on the given ExpandableNotificationRow {@code view}. This handles opening guts for
+     * the normal half-swipe and long-press use cases via a circular reveal. When the blocking
+     * helper needs to be shown on the row, this will skip the circular reveal.
+     *
+     * @param view ExpandableNotificationRow to open guts on
+     * @param x x coordinate of origin of circular reveal
+     * @param y y coordinate of origin of circular reveal
+     * @param menuItem MenuItem the guts should display
+     * @return true if guts was opened
+     */
+    public boolean openGuts(
+            View view,
+            int x,
+            int y,
+            NotificationMenuRowPlugin.MenuItem menuItem) {
+        if (!(view instanceof ExpandableNotificationRow)) {
+            return false;
+        }
+
+        if (view.getWindowToken() == null) {
+            Log.e(TAG, "Trying to show notification guts, but not attached to window");
+            return false;
+        }
+
+        final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+        if (row.isDark()) {
+            return false;
+        }
+        view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        if (row.areGutsExposed()) {
+            closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
+                    true /* removeControls */, -1 /* x */, -1 /* y */,
+                    true /* resetMenu */);
+            return false;
+        }
+        bindGuts(row, menuItem);
+        NotificationGuts guts = row.getGuts();
+
+        // Assume we are a status_bar_notification_row
+        if (guts == null) {
+            // This view has no guts. Examples are the more card or the dismiss all view
+            return false;
+        }
+
+        mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS);
+
+        // ensure that it's laid but not visible until actually laid out
+        guts.setVisibility(View.INVISIBLE);
+        // Post to ensure the the guts are properly laid out.
+        guts.post(new Runnable() {
+            @Override
+            public void run() {
+                if (row.getWindowToken() == null) {
+                    Log.e(TAG, "Trying to show notification guts in post(), but not attached to "
+                            + "window");
+                    return;
+                }
+                closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+                        true /* removeControls */, -1 /* x */, -1 /* y */,
+                        false /* resetMenu */);
+                guts.setVisibility(View.VISIBLE);
+
+                final boolean needsFalsingProtection =
+                        (mPresenter.isPresenterLocked() &&
+                                !mAccessibilityManager.isTouchExplorationEnabled());
+
+                guts.openControls(
+                        !row.isBlockingHelperShowing(),
+                        x,
+                        y,
+                        needsFalsingProtection,
+                        row::onGutsOpened);
+
+                row.closeRemoteInput();
+                mListContainer.onHeightChanged(row, true /* needsAnimation */);
+                mNotificationGutsExposed = guts;
+                mGutsMenuItem = menuItem;
+            }
+        });
+        return true;
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("NotificationGutsManager state:");
+        pw.print("  mKeyToRemoveOnGutsClosed: ");
+        pw.println(mKeyToRemoveOnGutsClosed);
+    }
+
+    public interface OnSettingsClickListener {
+        void onClick(String key);
+    }
+}
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
new file mode 100644
index 0000000..aa4765a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -0,0 +1,782 @@
+/*
+ * 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.notification.row;
+
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.CancellationSignal;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.view.View;
+import android.widget.RemoteViews;
+
+import com.android.internal.annotations.VisibleForTesting;
+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.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.util.Assert;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A utility that inflates the right kind of contentView based on the state
+ */
+public class NotificationInflater {
+
+    public static final String TAG = "NotificationInflater";
+    @VisibleForTesting
+    static final int FLAG_REINFLATE_ALL = ~0;
+    private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0;
+    @VisibleForTesting
+    static final int FLAG_REINFLATE_EXPANDED_VIEW = 1<<1;
+    private static final int FLAG_REINFLATE_HEADS_UP_VIEW = 1<<2;
+    private static final int FLAG_REINFLATE_PUBLIC_VIEW = 1<<3;
+    private static final int FLAG_REINFLATE_AMBIENT_VIEW = 1<<4;
+    private static final InflationExecutor EXECUTOR = new InflationExecutor();
+
+    private final ExpandableNotificationRow mRow;
+    private boolean mIsLowPriority;
+    private boolean mUsesIncreasedHeight;
+    private boolean mUsesIncreasedHeadsUpHeight;
+    private RemoteViews.OnClickHandler mRemoteViewClickHandler;
+    private boolean mIsChildInGroup;
+    private InflationCallback mCallback;
+    private boolean mRedactAmbient;
+    private List<Notification.Action> mSmartActions;
+
+    public NotificationInflater(ExpandableNotificationRow row) {
+        mRow = row;
+    }
+
+    public void setIsLowPriority(boolean isLowPriority) {
+        mIsLowPriority = isLowPriority;
+    }
+
+    /**
+     * Set whether the notification is a child in a group
+     *
+     * @return whether the view was re-inflated
+     */
+    public void setIsChildInGroup(boolean childInGroup) {
+        if (childInGroup != mIsChildInGroup) {
+            mIsChildInGroup = childInGroup;
+            if (mIsLowPriority) {
+                int flags = FLAG_REINFLATE_CONTENT_VIEW | FLAG_REINFLATE_EXPANDED_VIEW;
+                inflateNotificationViews(flags);
+            }
+        } ;
+    }
+
+    public void setUsesIncreasedHeight(boolean usesIncreasedHeight) {
+        mUsesIncreasedHeight = usesIncreasedHeight;
+    }
+
+    public void setSmartActions(List<Notification.Action> smartActions) {
+        mSmartActions = smartActions;
+    }
+
+    public void setUsesIncreasedHeadsUpHeight(boolean usesIncreasedHeight) {
+        mUsesIncreasedHeadsUpHeight = usesIncreasedHeight;
+    }
+
+    public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
+        mRemoteViewClickHandler = remoteViewClickHandler;
+    }
+
+    public void setRedactAmbient(boolean redactAmbient) {
+        if (mRedactAmbient != redactAmbient) {
+            mRedactAmbient = redactAmbient;
+            if (mRow.getEntry() == null) {
+                return;
+            }
+            inflateNotificationViews(FLAG_REINFLATE_AMBIENT_VIEW);
+        }
+    }
+
+    /**
+     * Inflate all views of this notification on a background thread. This is asynchronous and will
+     * notify the callback once it's finished.
+     */
+    public void inflateNotificationViews() {
+        inflateNotificationViews(FLAG_REINFLATE_ALL);
+    }
+
+    /**
+     * Reinflate all views for the specified flags on a background thread. This is asynchronous and
+     * will notify the callback once it's finished.
+     *
+     * @param reInflateFlags flags which views should be reinflated. Use {@link #FLAG_REINFLATE_ALL}
+     *                       to reinflate all of views.
+     */
+    @VisibleForTesting
+    void inflateNotificationViews(int reInflateFlags) {
+        if (mRow.isRemoved()) {
+            // We don't want to reinflate anything for removed notifications. Otherwise views might
+            // be readded to the stack, leading to leaks. This may happen with low-priority groups
+            // where the removal of already removed children can lead to a reinflation.
+            return;
+        }
+        StatusBarNotification sbn = mRow.getEntry().notification;
+        AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mRow,
+                mIsLowPriority,
+                mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
+                mCallback, mRemoteViewClickHandler, mSmartActions);
+        if (mCallback != null && mCallback.doInflateSynchronous()) {
+            task.onPostExecute(task.doInBackground());
+        } else {
+            task.execute();
+        }
+    }
+
+    @VisibleForTesting
+    InflationProgress inflateNotificationViews(int reInflateFlags,
+            Notification.Builder builder, Context packageContext) {
+        InflationProgress result = createRemoteViews(reInflateFlags, builder, mIsLowPriority,
+                mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
+                mRedactAmbient, packageContext);
+        apply(result, reInflateFlags, mRow, mRedactAmbient, mRemoteViewClickHandler, null);
+        return result;
+    }
+
+    private static InflationProgress createRemoteViews(int reInflateFlags,
+            Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
+            boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
+            Context packageContext) {
+        InflationProgress result = new InflationProgress();
+        isLowPriority = isLowPriority && !isChildInGroup;
+        if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
+            result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
+        }
+
+        if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
+            result.newExpandedView = createExpandedView(builder, isLowPriority);
+        }
+
+        if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
+            result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
+        }
+
+        if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
+            result.newPublicView = builder.makePublicContentView();
+        }
+
+        if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
+            result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
+                    : builder.makeAmbientNotification();
+        }
+        result.packageContext = packageContext;
+        result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
+        result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
+                true /* showingPublic */);
+        return result;
+    }
+
+    public static CancellationSignal apply(InflationProgress result, int reInflateFlags,
+            ExpandableNotificationRow row, boolean redactAmbient,
+            RemoteViews.OnClickHandler remoteViewClickHandler,
+            @Nullable InflationCallback callback) {
+        NotificationData.Entry entry = row.getEntry();
+        NotificationContentView privateLayout = row.getPrivateLayout();
+        NotificationContentView publicLayout = row.getPublicLayout();
+        final HashMap<Integer, CancellationSignal> runningInflations = new HashMap<>();
+
+        int flag = FLAG_REINFLATE_CONTENT_VIEW;
+        if ((reInflateFlags & flag) != 0) {
+            boolean isNewView = !canReapplyRemoteView(result.newContentView, entry.cachedContentView);
+            ApplyCallback applyCallback = new ApplyCallback() {
+                @Override
+                public void setResultView(View v) {
+                    result.inflatedContentView = v;
+                }
+
+                @Override
+                public RemoteViews getRemoteView() {
+                    return result.newContentView;
+                }
+            };
+            applyRemoteView(result, reInflateFlags, flag, row, redactAmbient,
+                    isNewView, remoteViewClickHandler, callback, entry, privateLayout,
+                    privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
+                            NotificationContentView.VISIBLE_TYPE_CONTRACTED),
+                    runningInflations, applyCallback);
+        }
+
+        flag = FLAG_REINFLATE_EXPANDED_VIEW;
+        if ((reInflateFlags & flag) != 0) {
+            if (result.newExpandedView != null) {
+                boolean isNewView = !canReapplyRemoteView(result.newExpandedView,
+                        entry.cachedBigContentView);
+                ApplyCallback applyCallback = new ApplyCallback() {
+                    @Override
+                    public void setResultView(View v) {
+                        result.inflatedExpandedView = v;
+                    }
+
+                    @Override
+                    public RemoteViews getRemoteView() {
+                        return result.newExpandedView;
+                    }
+                };
+                applyRemoteView(result, reInflateFlags, flag, row,
+                        redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+                        privateLayout, privateLayout.getExpandedChild(),
+                        privateLayout.getVisibleWrapper(
+                                NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
+                        applyCallback);
+            }
+        }
+
+        flag = FLAG_REINFLATE_HEADS_UP_VIEW;
+        if ((reInflateFlags & flag) != 0) {
+            if (result.newHeadsUpView != null) {
+                boolean isNewView = !canReapplyRemoteView(result.newHeadsUpView,
+                        entry.cachedHeadsUpContentView);
+                ApplyCallback applyCallback = new ApplyCallback() {
+                    @Override
+                    public void setResultView(View v) {
+                        result.inflatedHeadsUpView = v;
+                    }
+
+                    @Override
+                    public RemoteViews getRemoteView() {
+                        return result.newHeadsUpView;
+                    }
+                };
+                applyRemoteView(result, reInflateFlags, flag, row,
+                        redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+                        privateLayout, privateLayout.getHeadsUpChild(),
+                        privateLayout.getVisibleWrapper(
+                                NotificationContentView.VISIBLE_TYPE_HEADSUP), runningInflations,
+                        applyCallback);
+            }
+        }
+
+        flag = FLAG_REINFLATE_PUBLIC_VIEW;
+        if ((reInflateFlags & flag) != 0) {
+            boolean isNewView = !canReapplyRemoteView(result.newPublicView,
+                    entry.cachedPublicContentView);
+            ApplyCallback applyCallback = new ApplyCallback() {
+                @Override
+                public void setResultView(View v) {
+                    result.inflatedPublicView = v;
+                }
+
+                @Override
+                public RemoteViews getRemoteView() {
+                    return result.newPublicView;
+                }
+            };
+            applyRemoteView(result, reInflateFlags, flag, row,
+                    redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+                    publicLayout, publicLayout.getContractedChild(),
+                    publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
+                    runningInflations, applyCallback);
+        }
+
+        flag = FLAG_REINFLATE_AMBIENT_VIEW;
+        if ((reInflateFlags & flag) != 0) {
+            NotificationContentView newParent = redactAmbient ? publicLayout : privateLayout;
+            boolean isNewView = !canReapplyAmbient(row, redactAmbient) ||
+                    !canReapplyRemoteView(result.newAmbientView, entry.cachedAmbientContentView);
+            ApplyCallback applyCallback = new ApplyCallback() {
+                @Override
+                public void setResultView(View v) {
+                    result.inflatedAmbientView = v;
+                }
+
+                @Override
+                public RemoteViews getRemoteView() {
+                    return result.newAmbientView;
+                }
+            };
+            applyRemoteView(result, reInflateFlags, flag, row,
+                    redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+                    newParent, newParent.getAmbientChild(), newParent.getVisibleWrapper(
+                            NotificationContentView.VISIBLE_TYPE_AMBIENT), runningInflations,
+                    applyCallback);
+        }
+
+        // Let's try to finish, maybe nobody is even inflating anything
+        finishIfDone(result, reInflateFlags, runningInflations, callback, row,
+                redactAmbient);
+        CancellationSignal cancellationSignal = new CancellationSignal();
+        cancellationSignal.setOnCancelListener(
+                () -> runningInflations.values().forEach(CancellationSignal::cancel));
+        return cancellationSignal;
+    }
+
+    @VisibleForTesting
+    static void applyRemoteView(final InflationProgress result,
+            final int reInflateFlags, int inflationId,
+            final ExpandableNotificationRow row,
+            final boolean redactAmbient, boolean isNewView,
+            RemoteViews.OnClickHandler remoteViewClickHandler,
+            @Nullable final InflationCallback callback, NotificationData.Entry entry,
+            NotificationContentView parentLayout, View existingView,
+            NotificationViewWrapper existingWrapper,
+            final HashMap<Integer, CancellationSignal> runningInflations,
+            ApplyCallback applyCallback) {
+        RemoteViews newContentView = applyCallback.getRemoteView();
+        if (callback != null && callback.doInflateSynchronous()) {
+            try {
+                if (isNewView) {
+                    View v = newContentView.apply(
+                            result.packageContext,
+                            parentLayout,
+                            remoteViewClickHandler);
+                    v.setIsRootNamespace(true);
+                    applyCallback.setResultView(v);
+                } else {
+                    newContentView.reapply(
+                            result.packageContext,
+                            existingView,
+                            remoteViewClickHandler);
+                    existingWrapper.onReinflated();
+                }
+            } catch (Exception e) {
+                handleInflationError(runningInflations, e, entry.notification, callback);
+                // Add a running inflation to make sure we don't trigger callbacks.
+                // Safe to do because only happens in tests.
+                runningInflations.put(inflationId, new CancellationSignal());
+            }
+            return;
+        }
+        RemoteViews.OnViewAppliedListener listener
+                = new RemoteViews.OnViewAppliedListener() {
+
+            @Override
+            public void onViewApplied(View v) {
+                if (isNewView) {
+                    v.setIsRootNamespace(true);
+                    applyCallback.setResultView(v);
+                } else if (existingWrapper != null) {
+                    existingWrapper.onReinflated();
+                }
+                runningInflations.remove(inflationId);
+                finishIfDone(result, reInflateFlags, runningInflations, callback, row,
+                        redactAmbient);
+            }
+
+            @Override
+            public void onError(Exception e) {
+                // Uh oh the async inflation failed. Due to some bugs (see b/38190555), this could
+                // actually also be a system issue, so let's try on the UI thread again to be safe.
+                try {
+                    View newView = existingView;
+                    if (isNewView) {
+                        newView = newContentView.apply(
+                                result.packageContext,
+                                parentLayout,
+                                remoteViewClickHandler);
+                    } else {
+                        newContentView.reapply(
+                                result.packageContext,
+                                existingView,
+                                remoteViewClickHandler);
+                    }
+                    Log.wtf(TAG, "Async Inflation failed but normal inflation finished normally.",
+                            e);
+                    onViewApplied(newView);
+                } catch (Exception anotherException) {
+                    runningInflations.remove(inflationId);
+                    handleInflationError(runningInflations, e, entry.notification, callback);
+                }
+            }
+        };
+        CancellationSignal cancellationSignal;
+        if (isNewView) {
+            cancellationSignal = newContentView.applyAsync(
+                    result.packageContext,
+                    parentLayout,
+                    EXECUTOR,
+                    listener,
+                    remoteViewClickHandler);
+        } else {
+            cancellationSignal = newContentView.reapplyAsync(
+                    result.packageContext,
+                    existingView,
+                    EXECUTOR,
+                    listener,
+                    remoteViewClickHandler);
+        }
+        runningInflations.put(inflationId, cancellationSignal);
+    }
+
+    private static void handleInflationError(HashMap<Integer, CancellationSignal> runningInflations,
+            Exception e, StatusBarNotification notification, @Nullable InflationCallback callback) {
+        Assert.isMainThread();
+        runningInflations.values().forEach(CancellationSignal::cancel);
+        if (callback != null) {
+            callback.handleInflationException(notification, e);
+        }
+    }
+
+    /**
+     * Finish the inflation of the views
+     *
+     * @return true if the inflation was finished
+     */
+    private static boolean finishIfDone(InflationProgress result, int reInflateFlags,
+            HashMap<Integer, CancellationSignal> runningInflations,
+            @Nullable InflationCallback endListener, ExpandableNotificationRow row,
+            boolean redactAmbient) {
+        Assert.isMainThread();
+        NotificationData.Entry entry = row.getEntry();
+        NotificationContentView privateLayout = row.getPrivateLayout();
+        NotificationContentView publicLayout = row.getPublicLayout();
+        if (runningInflations.isEmpty()) {
+            if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
+                if (result.inflatedContentView != null) {
+                    privateLayout.setContractedChild(result.inflatedContentView);
+                }
+                entry.cachedContentView = result.newContentView;
+            }
+
+            if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
+                if (result.inflatedExpandedView != null) {
+                    privateLayout.setExpandedChild(result.inflatedExpandedView);
+                } else if (result.newExpandedView == null) {
+                    privateLayout.setExpandedChild(null);
+                }
+                entry.cachedBigContentView = result.newExpandedView;
+                row.setExpandable(result.newExpandedView != null);
+            }
+
+            if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
+                if (result.inflatedHeadsUpView != null) {
+                    privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
+                } else if (result.newHeadsUpView == null) {
+                    privateLayout.setHeadsUpChild(null);
+                }
+                entry.cachedHeadsUpContentView = result.newHeadsUpView;
+            }
+
+            if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
+                if (result.inflatedPublicView != null) {
+                    publicLayout.setContractedChild(result.inflatedPublicView);
+                }
+                entry.cachedPublicContentView = result.newPublicView;
+            }
+
+            if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
+                if (result.inflatedAmbientView != null) {
+                    NotificationContentView newParent = redactAmbient
+                            ? publicLayout : privateLayout;
+                    NotificationContentView otherParent = !redactAmbient
+                            ? publicLayout : privateLayout;
+                    newParent.setAmbientChild(result.inflatedAmbientView);
+                    otherParent.setAmbientChild(null);
+                }
+                entry.cachedAmbientContentView = result.newAmbientView;
+            }
+            entry.headsUpStatusBarText = result.headsUpStatusBarText;
+            entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
+            if (endListener != null) {
+                endListener.onAsyncInflationFinished(row.getEntry());
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private static RemoteViews createExpandedView(Notification.Builder builder,
+            boolean isLowPriority) {
+        RemoteViews bigContentView = builder.createBigContentView();
+        if (bigContentView != null) {
+            return bigContentView;
+        }
+        if (isLowPriority) {
+            RemoteViews contentView = builder.createContentView();
+            Notification.Builder.makeHeaderExpanded(contentView);
+            return contentView;
+        }
+        return null;
+    }
+
+    private static RemoteViews createContentView(Notification.Builder builder,
+            boolean isLowPriority, boolean useLarge) {
+        if (isLowPriority) {
+            return builder.makeLowPriorityContentView(false /* useRegularSubtext */);
+        }
+        return builder.createContentView(useLarge);
+    }
+
+    /**
+     * @param newView The new view that will be applied
+     * @param oldView The old view that was applied to the existing view before
+     * @return {@code true} if the RemoteViews are the same and the view can be reused to reapply.
+     */
+     @VisibleForTesting
+     static boolean canReapplyRemoteView(final RemoteViews newView,
+            final RemoteViews oldView) {
+        return (newView == null && oldView == null) ||
+                (newView != null && oldView != null
+                        && oldView.getPackage() != null
+                        && newView.getPackage() != null
+                        && newView.getPackage().equals(oldView.getPackage())
+                        && newView.getLayoutId() == oldView.getLayoutId()
+                        && !oldView.isReapplyDisallowed());
+    }
+
+    public void setInflationCallback(InflationCallback callback) {
+        mCallback = callback;
+    }
+
+    public interface InflationCallback {
+        void handleInflationException(StatusBarNotification notification, Exception e);
+        void onAsyncInflationFinished(NotificationData.Entry entry);
+
+        /**
+         * Used to disable async-ness for tests. Should only be used for tests.
+         */
+        default boolean doInflateSynchronous() {
+            return false;
+        }
+    }
+
+    public void clearCachesAndReInflate() {
+        NotificationData.Entry entry = mRow.getEntry();
+        entry.cachedAmbientContentView = null;
+        entry.cachedBigContentView = null;
+        entry.cachedContentView = null;
+        entry.cachedHeadsUpContentView = null;
+        entry.cachedPublicContentView = null;
+        inflateNotificationViews();
+    }
+
+    private static boolean canReapplyAmbient(ExpandableNotificationRow row, boolean redactAmbient) {
+        NotificationContentView ambientView = redactAmbient ? row.getPublicLayout()
+                : row.getPrivateLayout();            ;
+        return ambientView.getAmbientChild() != null;
+    }
+
+    public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
+            implements InflationCallback, InflationTask {
+
+        private final StatusBarNotification mSbn;
+        private final Context mContext;
+        private final boolean mIsLowPriority;
+        private final boolean mIsChildInGroup;
+        private final boolean mUsesIncreasedHeight;
+        private final InflationCallback mCallback;
+        private final boolean mUsesIncreasedHeadsUpHeight;
+        private final boolean mRedactAmbient;
+        private int mReInflateFlags;
+        private ExpandableNotificationRow mRow;
+        private Exception mError;
+        private RemoteViews.OnClickHandler mRemoteViewClickHandler;
+        private CancellationSignal mCancellationSignal;
+        private List<Notification.Action> mSmartActions;
+
+        private AsyncInflationTask(StatusBarNotification notification,
+                int reInflateFlags, ExpandableNotificationRow row, boolean isLowPriority,
+                boolean isChildInGroup, boolean usesIncreasedHeight,
+                boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
+                InflationCallback callback,
+                RemoteViews.OnClickHandler remoteViewClickHandler,
+                List<Notification.Action> smartActions) {
+            mRow = row;
+            mSbn = notification;
+            mReInflateFlags = reInflateFlags;
+            mContext = mRow.getContext();
+            mIsLowPriority = isLowPriority;
+            mIsChildInGroup = isChildInGroup;
+            mUsesIncreasedHeight = usesIncreasedHeight;
+            mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight;
+            mRedactAmbient = redactAmbient;
+            mRemoteViewClickHandler = remoteViewClickHandler;
+            mCallback = callback;
+            mSmartActions = smartActions == null
+                    ? Collections.emptyList()
+                    : new ArrayList<>(smartActions);
+            NotificationData.Entry entry = row.getEntry();
+            entry.setInflationTask(this);
+        }
+
+        @VisibleForTesting
+        public int getReInflateFlags() {
+            return mReInflateFlags;
+        }
+
+        @Override
+        protected InflationProgress doInBackground(Void... params) {
+            try {
+                final Notification.Builder recoveredBuilder
+                        = Notification.Builder.recoverBuilder(mContext,
+                        mSbn.getNotification());
+
+                applyChanges(recoveredBuilder);
+
+                Context packageContext = mSbn.getPackageContext(mContext);
+                Notification notification = mSbn.getNotification();
+                if (notification.isMediaNotification()) {
+                    MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
+                            packageContext);
+                    processor.processNotification(notification, recoveredBuilder);
+                }
+                return createRemoteViews(mReInflateFlags,
+                        recoveredBuilder, mIsLowPriority, mIsChildInGroup,
+                        mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
+                        packageContext);
+            } catch (Exception e) {
+                mError = e;
+                return null;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(InflationProgress result) {
+            if (mError == null) {
+                mCancellationSignal = apply(result, mReInflateFlags, mRow, mRedactAmbient,
+                        mRemoteViewClickHandler, this);
+            } else {
+                handleError(mError);
+            }
+        }
+
+        /**
+         * Apply changes to the given notification builder, like adding smart actions suggested by
+         * a {@link android.service.notification.NotificationAssistantService}.
+         */
+        private void applyChanges(Notification.Builder builder) {
+            if (mSmartActions != null) {
+                for (Notification.Action smartAction : mSmartActions) {
+                    builder.addAction(smartAction);
+                }
+            }
+        }
+
+        private void handleError(Exception e) {
+            mRow.getEntry().onInflationTaskFinished();
+            StatusBarNotification sbn = mRow.getStatusBarNotification();
+            final String ident = sbn.getPackageName() + "/0x"
+                    + Integer.toHexString(sbn.getId());
+            Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
+            mCallback.handleInflationException(sbn,
+                    new InflationException("Couldn't inflate contentViews" + e));
+        }
+
+        @Override
+        public void abort() {
+            cancel(true /* mayInterruptIfRunning */);
+            if (mCancellationSignal != null) {
+                mCancellationSignal.cancel();
+            }
+        }
+
+        @Override
+        public void supersedeTask(InflationTask task) {
+            if (task instanceof AsyncInflationTask) {
+                // We want to inflate all flags of the previous task as well
+                mReInflateFlags |= ((AsyncInflationTask) task).mReInflateFlags;
+            }
+        }
+
+        @Override
+        public void handleInflationException(StatusBarNotification notification, Exception e) {
+            handleError(e);
+        }
+
+        @Override
+        public void onAsyncInflationFinished(NotificationData.Entry entry) {
+            mRow.getEntry().onInflationTaskFinished();
+            mRow.onNotificationUpdated();
+            mCallback.onAsyncInflationFinished(mRow.getEntry());
+        }
+
+        @Override
+        public boolean doInflateSynchronous() {
+            return mCallback != null && mCallback.doInflateSynchronous();
+        }
+    }
+
+    @VisibleForTesting
+    static class InflationProgress {
+        private RemoteViews newContentView;
+        private RemoteViews newHeadsUpView;
+        private RemoteViews newExpandedView;
+        private RemoteViews newAmbientView;
+        private RemoteViews newPublicView;
+
+        @VisibleForTesting
+        Context packageContext;
+
+        private View inflatedContentView;
+        private View inflatedHeadsUpView;
+        private View inflatedExpandedView;
+        private View inflatedAmbientView;
+        private View inflatedPublicView;
+        private CharSequence headsUpStatusBarText;
+        private CharSequence headsUpStatusBarTextPublic;
+    }
+
+    @VisibleForTesting
+    abstract static class ApplyCallback {
+        public abstract void setResultView(View v);
+        public abstract RemoteViews getRemoteView();
+    }
+
+    /**
+     * A custom executor that allows more tasks to be queued. Default values are copied from
+     * AsyncTask
+      */
+    private static class InflationExecutor implements Executor {
+        private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
+        // We want at least 2 threads and at most 4 threads in the core pool,
+        // preferring to have 1 less than the CPU count to avoid saturating
+        // the CPU with background work
+        private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
+        private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
+        private static final int KEEP_ALIVE_SECONDS = 30;
+
+        private static final ThreadFactory sThreadFactory = new ThreadFactory() {
+            private final AtomicInteger mCount = new AtomicInteger(1);
+
+            public Thread newThread(Runnable r) {
+                return new Thread(r, "InflaterThread #" + mCount.getAndIncrement());
+            }
+        };
+
+        private final ThreadPoolExecutor mExecutor;
+
+        private InflationExecutor() {
+            mExecutor = new ThreadPoolExecutor(
+                    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
+                    new LinkedBlockingQueue<>(), sThreadFactory);
+            mExecutor.allowCoreThreadTimeOut(true);
+        }
+
+        @Override
+        public void execute(Runnable runnable) {
+            mExecutor.execute(runnable);
+        }
+    }
+}
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
new file mode 100644
index 0000000..3e380d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -0,0 +1,565 @@
+/*
+ * 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.notification.row;
+
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.logging.NotificationCounters;
+
+import java.util.List;
+
+/**
+ * The guts of a notification revealed when performing a long press. This also houses the blocking
+ * helper affordance that allows a user to keep/stop notifications after swiping one away.
+ */
+public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
+    private static final String TAG = "InfoGuts";
+
+    private INotificationManager mINotificationManager;
+    private PackageManager mPm;
+    private MetricsLogger mMetricsLogger;
+
+    private String mPackageName;
+    private String mAppName;
+    private int mAppUid;
+    private int mNumUniqueChannelsInRow;
+    private NotificationChannel mSingleNotificationChannel;
+    private int mStartingUserImportance;
+    private int mChosenImportance;
+    private boolean mIsSingleDefaultChannel;
+    private boolean mIsNonblockable;
+    private StatusBarNotification mSbn;
+    private AnimatorSet mExpandAnimation;
+    private boolean mIsForeground;
+
+    private CheckSaveListener mCheckSaveListener;
+    private OnSettingsClickListener mOnSettingsClickListener;
+    private OnAppSettingsClickListener mAppSettingsClickListener;
+    private NotificationGuts mGutsContainer;
+
+    /** Whether this view is being shown as part of the blocking helper. */
+    private boolean mIsForBlockingHelper;
+    private boolean mNegativeUserSentiment;
+
+    /**
+     * String that describes how the user exit or quit out of this view, also used as a counter tag.
+     */
+    private String mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
+
+    private OnClickListener mOnKeepShowing = v -> {
+        mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
+        closeControls(v);
+    };
+
+    private OnClickListener mOnStopOrMinimizeNotifications = v -> {
+        mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS;
+        swapContent(false);
+    };
+
+    private OnClickListener mOnUndo = v -> {
+        // 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);
+        swapContent(true);
+    };
+
+    public NotificationInfo(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    // Specify a CheckSaveListener to override when/if the user's changes are committed.
+    public interface CheckSaveListener {
+        // Invoked when importance has changed and the NotificationInfo wants to try to save it.
+        // Listener should run saveImportance unless the change should be canceled.
+        void checkSave(Runnable saveImportance, StatusBarNotification sbn);
+    }
+
+    public interface OnSettingsClickListener {
+        void onClick(View v, NotificationChannel channel, int appUid);
+    }
+
+    public interface OnAppSettingsClickListener {
+        void onClick(View v, Intent intent);
+    }
+
+    @VisibleForTesting
+    void bindNotification(
+            final PackageManager pm,
+            final INotificationManager iNotificationManager,
+            final String pkg,
+            final NotificationChannel notificationChannel,
+            final int numUniqueChannelsInRow,
+            final StatusBarNotification sbn,
+            final CheckSaveListener checkSaveListener,
+            final OnSettingsClickListener onSettingsClick,
+            final OnAppSettingsClickListener onAppSettingsClick,
+            boolean isNonblockable)
+            throws RemoteException {
+        bindNotification(pm, iNotificationManager, pkg, notificationChannel,
+                numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
+                onAppSettingsClick, isNonblockable, false /* isBlockingHelper */,
+                false /* isUserSentimentNegative */);
+    }
+
+    public void bindNotification(
+            PackageManager pm,
+            INotificationManager iNotificationManager,
+            String pkg,
+            NotificationChannel notificationChannel,
+            int numUniqueChannelsInRow,
+            StatusBarNotification sbn,
+            CheckSaveListener checkSaveListener,
+            OnSettingsClickListener onSettingsClick,
+            OnAppSettingsClickListener onAppSettingsClick,
+            boolean isNonblockable,
+            boolean isForBlockingHelper,
+            boolean isUserSentimentNegative)
+            throws RemoteException {
+        mINotificationManager = iNotificationManager;
+        mMetricsLogger = Dependency.get(MetricsLogger.class);
+        mPackageName = pkg;
+        mNumUniqueChannelsInRow = numUniqueChannelsInRow;
+        mSbn = sbn;
+        mPm = pm;
+        mAppSettingsClickListener = onAppSettingsClick;
+        mAppName = mPackageName;
+        mCheckSaveListener = checkSaveListener;
+        mOnSettingsClickListener = onSettingsClick;
+        mSingleNotificationChannel = notificationChannel;
+        mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance();
+        mNegativeUserSentiment = isUserSentimentNegative;
+        mIsNonblockable = isNonblockable;
+        mIsForeground =
+                (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
+        mIsForBlockingHelper = isForBlockingHelper;
+        mAppUid = mSbn.getUid();
+
+        int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
+                pkg, mAppUid, false /* includeDeleted */);
+        if (mNumUniqueChannelsInRow == 0) {
+            throw new IllegalArgumentException("bindNotification requires at least one channel");
+        } else  {
+            // Special behavior for the Default channel if no other channels have been defined.
+            mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
+                    && mSingleNotificationChannel.getId().equals(
+                            NotificationChannel.DEFAULT_CHANNEL_ID)
+                    && numTotalChannels == 1;
+        }
+
+        bindHeader();
+        bindPrompt();
+        bindButtons();
+    }
+
+    private void bindHeader() throws RemoteException {
+        // Package name
+        Drawable pkgicon = null;
+        ApplicationInfo info;
+        try {
+            info = mPm.getApplicationInfo(
+                    mPackageName,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                            | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+            if (info != null) {
+                mAppName = String.valueOf(mPm.getApplicationLabel(info));
+                pkgicon = mPm.getApplicationIcon(info);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // app is gone, just show package name and generic icon
+            pkgicon = mPm.getDefaultActivityIcon();
+        }
+        ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
+        ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
+
+        // Set group information if this channel has an associated group.
+        CharSequence groupName = null;
+        if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
+            final NotificationChannelGroup notificationChannelGroup =
+                    mINotificationManager.getNotificationChannelGroupForPackage(
+                            mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
+            if (notificationChannelGroup != null) {
+                groupName = notificationChannelGroup.getName();
+            }
+        }
+        TextView groupNameView = findViewById(R.id.group_name);
+        TextView groupDividerView = findViewById(R.id.pkg_group_divider);
+        if (groupName != null) {
+            groupNameView.setText(groupName);
+            groupNameView.setVisibility(View.VISIBLE);
+            groupDividerView.setVisibility(View.VISIBLE);
+        } else {
+            groupNameView.setVisibility(View.GONE);
+            groupDividerView.setVisibility(View.GONE);
+        }
+
+        // Settings button.
+        final View settingsButton = findViewById(R.id.info);
+        if (mAppUid >= 0 && mOnSettingsClickListener != null) {
+            settingsButton.setVisibility(View.VISIBLE);
+            final int appUidF = mAppUid;
+            settingsButton.setOnClickListener(
+                    (View view) -> {
+                        logBlockingHelperCounter(
+                                NotificationCounters.BLOCKING_HELPER_NOTIF_SETTINGS);
+                        mOnSettingsClickListener.onClick(view,
+                                mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
+                                appUidF);
+                    });
+        } else {
+            settingsButton.setVisibility(View.GONE);
+        }
+    }
+
+    private void bindPrompt() {
+        final TextView blockPrompt = findViewById(R.id.block_prompt);
+        bindName();
+        if (mIsNonblockable) {
+            blockPrompt.setText(R.string.notification_unblockable_desc);
+        } else {
+            if (mNegativeUserSentiment) {
+                blockPrompt.setText(R.string.inline_blocking_helper);
+            }  else if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
+                blockPrompt.setText(R.string.inline_keep_showing_app);
+            } else {
+                blockPrompt.setText(R.string.inline_keep_showing);
+            }
+        }
+    }
+
+    private void bindName() {
+        final TextView channelName = findViewById(R.id.channel_name);
+        if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
+            channelName.setVisibility(View.GONE);
+        } else {
+            channelName.setText(mSingleNotificationChannel.getName());
+        }
+    }
+
+    @VisibleForTesting
+    void logBlockingHelperCounter(String counterTag) {
+        if (mIsForBlockingHelper) {
+            mMetricsLogger.count(counterTag, 1);
+        }
+    }
+
+    private boolean hasImportanceChanged() {
+        return mSingleNotificationChannel != null && mStartingUserImportance != mChosenImportance;
+    }
+
+    private void saveImportance() {
+        if (!mIsNonblockable) {
+            // Only go through the lock screen/bouncer if the user hit 'Stop notifications'.
+            // Otherwise, update the importance immediately.
+            if (mCheckSaveListener != null
+                    && NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS.equals(
+                            mExitReason)) {
+                mCheckSaveListener.checkSave(this::updateImportance, mSbn);
+            } else {
+                updateImportance();
+            }
+        }
+    }
+
+    /**
+     * Commits the updated importance values on the background thread.
+     */
+    private void updateImportance() {
+        MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
+                mChosenImportance - mStartingUserImportance);
+
+        Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
+        bgHandler.post(new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
+                mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
+                mStartingUserImportance, mChosenImportance));
+    }
+
+    private void bindButtons() {
+        // Set up stay-in-notification actions
+        View block =  findViewById(R.id.block);
+        TextView keep = findViewById(R.id.keep);
+        View minimize = findViewById(R.id.minimize);
+
+        findViewById(R.id.undo).setOnClickListener(mOnUndo);
+        block.setOnClickListener(mOnStopOrMinimizeNotifications);
+        keep.setOnClickListener(mOnKeepShowing);
+        minimize.setOnClickListener(mOnStopOrMinimizeNotifications);
+
+        if (mIsNonblockable) {
+            keep.setText(android.R.string.ok);
+            block.setVisibility(GONE);
+            minimize.setVisibility(GONE);
+        } else if (mIsForeground) {
+            block.setVisibility(GONE);
+            minimize.setVisibility(VISIBLE);
+        } else if (!mIsForeground) {
+            block.setVisibility(VISIBLE);
+            minimize.setVisibility(GONE);
+        }
+
+        // Set up app settings link (i.e. Customize)
+        TextView settingsLinkView = findViewById(R.id.app_settings);
+        Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName, mSingleNotificationChannel,
+                mSbn.getId(), mSbn.getTag());
+        if (!mIsForBlockingHelper
+                && settingsIntent != null
+                && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
+            settingsLinkView.setVisibility(VISIBLE);
+            settingsLinkView.setText(mContext.getString(R.string.notification_app_settings));
+            settingsLinkView.setOnClickListener((View view) -> {
+                mAppSettingsClickListener.onClick(view, settingsIntent);
+            });
+        } else {
+            settingsLinkView.setVisibility(View.GONE);
+        }
+    }
+
+    private void swapContent(boolean showPrompt) {
+        if (mExpandAnimation != null) {
+            mExpandAnimation.cancel();
+        }
+
+        View prompt = findViewById(R.id.prompt);
+        ViewGroup confirmation = findViewById(R.id.confirmation);
+        TextView confirmationText = findViewById(R.id.confirmation_text);
+        View header = findViewById(R.id.header);
+
+        if (showPrompt) {
+            mChosenImportance = mStartingUserImportance;
+        } else if (mIsForeground) {
+            mChosenImportance = IMPORTANCE_MIN;
+            confirmationText.setText(R.string.notification_channel_minimized);
+        } else {
+            mChosenImportance = IMPORTANCE_NONE;
+            confirmationText.setText(R.string.notification_channel_disabled);
+        }
+
+        ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA,
+                prompt.getAlpha(), showPrompt ? 1f : 0f);
+        promptAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
+        ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
+                confirmation.getAlpha(), showPrompt ? 0f : 1f);
+        confirmAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
+
+        prompt.setVisibility(showPrompt ? VISIBLE : GONE);
+        confirmation.setVisibility(showPrompt ? GONE : VISIBLE);
+        header.setVisibility(showPrompt ? VISIBLE : GONE);
+
+        mExpandAnimation = new AnimatorSet();
+        mExpandAnimation.playTogether(promptAnim, confirmAnim);
+        mExpandAnimation.setDuration(150);
+        mExpandAnimation.addListener(new AnimatorListenerAdapter() {
+            boolean cancelled = false;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                cancelled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!cancelled) {
+                    prompt.setVisibility(showPrompt ? VISIBLE : GONE);
+                    confirmation.setVisibility(showPrompt ? GONE : VISIBLE);
+                }
+            }
+        });
+        mExpandAnimation.start();
+
+        // Since we're swapping/update the content, reset the timeout so the UI can't close
+        // immediately after the update.
+        if (mGutsContainer != null) {
+            mGutsContainer.resetFalsingCheck();
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        if (mGutsContainer != null &&
+                event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+            if (mGutsContainer.isExposed()) {
+                event.getText().add(mContext.getString(
+                        R.string.notification_channel_controls_opened_accessibility, mAppName));
+            } else {
+                event.getText().add(mContext.getString(
+                        R.string.notification_channel_controls_closed_accessibility, mAppName));
+            }
+        }
+    }
+
+    private Intent getAppSettingsIntent(PackageManager pm, String packageName,
+            NotificationChannel channel, int id, String tag) {
+        Intent intent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
+                .setPackage(packageName);
+        final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
+                intent,
+                PackageManager.MATCH_DEFAULT_ONLY
+        );
+        if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) {
+            return null;
+        }
+        final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
+        intent.setClassName(activityInfo.packageName, activityInfo.name);
+        if (channel != null) {
+            intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId());
+        }
+        intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id);
+        intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag);
+        return intent;
+    }
+
+    /**
+     * Closes the controls and commits the updated importance values (indirectly). If this view is
+     * being used to show the blocking helper, this will immediately dismiss the blocking helper and
+     * commit the updated importance.
+     *
+     * <p><b>Note,</b> this will only get called once the view is dismissing. This means that the
+     * user does not have the ability to undo the action anymore. See {@link #swapContent(boolean)}
+     * for where undo is handled.
+     */
+    @VisibleForTesting
+    void closeControls(View v) {
+        int[] parentLoc = new int[2];
+        int[] targetLoc = new int[2];
+        mGutsContainer.getLocationOnScreen(parentLoc);
+        v.getLocationOnScreen(targetLoc);
+        final int centerX = v.getWidth() / 2;
+        final int centerY = v.getHeight() / 2;
+        final int x = targetLoc[0] - parentLoc[0] + centerX;
+        final int y = targetLoc[1] - parentLoc[1] + centerY;
+        mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
+    }
+
+    @Override
+    public void setGutsParent(NotificationGuts guts) {
+        mGutsContainer = guts;
+    }
+
+    @Override
+    public boolean willBeRemoved() {
+        return hasImportanceChanged();
+    }
+
+    @Override
+    public boolean shouldBeSaved() {
+        return hasImportanceChanged();
+    }
+
+    @Override
+    public View getContentView() {
+        return this;
+    }
+
+    @Override
+    public boolean handleCloseControls(boolean save, boolean force) {
+        // Save regardless of the importance so we can lock the importance field if the user wants
+        // to keep getting notifications
+        if (save) {
+            saveImportance();
+        }
+        logBlockingHelperCounter(mExitReason);
+        return false;
+    }
+
+    @Override
+    public int getActualHeight() {
+        return getHeight();
+    }
+
+    /**
+     * Runnable to either update the given channel (with a new importance value) or, if no channel
+     * is provided, update notifications enabled state for the package.
+     */
+    private static class UpdateImportanceRunnable implements Runnable {
+        private final INotificationManager mINotificationManager;
+        private final String mPackageName;
+        private final int mAppUid;
+        private final @Nullable NotificationChannel mChannelToUpdate;
+        private final int mCurrentImportance;
+        private final int mNewImportance;
+
+
+        public UpdateImportanceRunnable(INotificationManager notificationManager,
+                String packageName, int appUid, @Nullable NotificationChannel channelToUpdate,
+                int currentImportance, int newImportance) {
+            mINotificationManager = notificationManager;
+            mPackageName = packageName;
+            mAppUid = appUid;
+            mChannelToUpdate = channelToUpdate;
+            mCurrentImportance = currentImportance;
+            mNewImportance = newImportance;
+        }
+
+        @Override
+        public void run() {
+            try {
+                if (mChannelToUpdate != null) {
+                    mChannelToUpdate.setImportance(mNewImportance);
+                    mChannelToUpdate.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+                    mINotificationManager.updateNotificationChannelForPackage(
+                            mPackageName, mAppUid, mChannelToUpdate);
+                } else {
+                    // For notifications with more than one channel, update notification enabled
+                    // state. If the importance was lowered, we disable notifications.
+                    mINotificationManager.setNotificationsEnabledWithImportanceLockForPackage(
+                            mPackageName, mAppUid, mNewImportance >= mCurrentImportance);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to update notification importance", e);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
new file mode 100644
index 0000000..dec88d4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
+
+import java.util.ArrayList;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.AlphaOptimizedImageView;
+import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.service.notification.StatusBarNotification;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
+
+public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener,
+        ExpandableNotificationRow.LayoutListener {
+
+    private static final boolean DEBUG = false;
+    private static final String TAG = "swipe";
+
+    private static final int ICON_ALPHA_ANIM_DURATION = 200;
+    private static final long SHOW_MENU_DELAY = 60;
+    private static final long SWIPE_MENU_TIMING = 200;
+
+    // Notification must be swiped at least this fraction of a single menu item to show menu
+    private static final float SWIPED_FAR_ENOUGH_MENU_FRACTION = 0.25f;
+    private static final float SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION = 0.15f;
+
+    // When the menu is displayed, the notification must be swiped within this fraction of a single
+    // menu item to snap back to menu (else it will cover the menu or it'll be dismissed)
+    private static final float SWIPED_BACK_ENOUGH_TO_COVER_FRACTION = 0.2f;
+
+    private ExpandableNotificationRow mParent;
+
+    private Context mContext;
+    private FrameLayout mMenuContainer;
+    private MenuItem mInfoItem;
+    private MenuItem mAppOpsItem;
+    private MenuItem mSnoozeItem;
+    private ArrayList<MenuItem> mMenuItems;
+    private OnMenuEventListener mMenuListener;
+
+    private ValueAnimator mFadeAnimator;
+    private boolean mAnimating;
+    private boolean mMenuFadedIn;
+
+    private boolean mOnLeft;
+    private boolean mIconsPlaced;
+
+    private boolean mDismissing;
+    private boolean mSnapping;
+    private float mTranslation;
+
+    private int[] mIconLocation = new int[2];
+    private int[] mParentLocation = new int[2];
+
+    private float mHorizSpaceForIcon = -1;
+    private int mVertSpaceForIcons = -1;
+    private int mIconPadding = -1;
+    private int mSidePadding;
+
+    private float mAlpha = 0f;
+    private float mPrevX;
+
+    private CheckForDrag mCheckForDrag;
+    private Handler mHandler;
+
+    private boolean mMenuSnappedTo;
+    private boolean mMenuSnappedOnLeft;
+    private boolean mShouldShowMenu;
+
+    private NotificationSwipeActionHelper mSwipeHelper;
+    private boolean mIsUserTouching;
+
+    public NotificationMenuRow(Context context) {
+        mContext = context;
+        mShouldShowMenu = context.getResources().getBoolean(R.bool.config_showNotificationGear);
+        mHandler = new Handler(Looper.getMainLooper());
+        mMenuItems = new ArrayList<>();
+    }
+
+    @Override
+    public ArrayList<MenuItem> getMenuItems(Context context) {
+        return mMenuItems;
+    }
+
+    @Override
+    public MenuItem getLongpressMenuItem(Context context) {
+        return mInfoItem;
+    }
+
+    @Override
+    public MenuItem getAppOpsMenuItem(Context context) {
+        return mAppOpsItem;
+    }
+
+    @Override
+    public MenuItem getSnoozeMenuItem(Context context) {
+        return mSnoozeItem;
+    }
+
+    @Override
+    public void setSwipeActionHelper(NotificationSwipeActionHelper helper) {
+        mSwipeHelper = helper;
+    }
+
+    @Override
+    public void setMenuClickListener(OnMenuEventListener listener) {
+        mMenuListener = listener;
+    }
+
+    @Override
+    public void createMenu(ViewGroup parent, StatusBarNotification sbn) {
+        mParent = (ExpandableNotificationRow) parent;
+        createMenuViews(true /* resetState */);
+    }
+
+    @Override
+    public boolean isMenuVisible() {
+        return mAlpha > 0;
+    }
+
+    @Override
+    public View getMenuView() {
+        return mMenuContainer;
+    }
+
+    @Override
+    public void resetMenu() {
+        resetState(true);
+    }
+
+    @Override
+    public void onNotificationUpdated(StatusBarNotification sbn) {
+        if (mMenuContainer == null) {
+            // Menu hasn't been created yet, no need to do anything.
+            return;
+        }
+        createMenuViews(!isMenuVisible() /* resetState */);
+    }
+
+    @Override
+    public void onConfigurationChanged() {
+        mParent.setLayoutListener(this);
+    }
+
+    @Override
+    public void onLayout() {
+        mIconsPlaced = false; // Force icons to be re-placed
+        setMenuLocation();
+        mParent.removeListener();
+    }
+
+    private void createMenuViews(boolean resetState) {
+        final Resources res = mContext.getResources();
+        mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size);
+        mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
+        mMenuItems.clear();
+        // Construct the menu items based on the notification
+        if (mParent != null && mParent.getStatusBarNotification() != null) {
+            int flags = mParent.getStatusBarNotification().getNotification().flags;
+            boolean isForeground = (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
+            if (!isForeground) {
+                // Only show snooze for non-foreground notifications
+                mSnoozeItem = createSnoozeItem(mContext);
+                mMenuItems.add(mSnoozeItem);
+            }
+        }
+        mInfoItem = createInfoItem(mContext);
+        mMenuItems.add(mInfoItem);
+
+        mAppOpsItem = createAppOpsItem(mContext);
+        mMenuItems.add(mAppOpsItem);
+
+        // Construct the menu views
+        if (mMenuContainer != null) {
+            mMenuContainer.removeAllViews();
+        } else {
+            mMenuContainer = new FrameLayout(mContext);
+        }
+        for (int i = 0; i < mMenuItems.size(); i++) {
+            addMenuView(mMenuItems.get(i), mMenuContainer);
+        }
+        if (resetState) {
+            resetState(false /* notify */);
+        } else {
+            mIconsPlaced = false;
+            setMenuLocation();
+            if (!mIsUserTouching) {
+                // If the # of items showing changed we need to update the snap position
+                showMenu(mParent, mOnLeft ? getSpaceForMenu() : -getSpaceForMenu(),
+                        0 /* velocity */);
+            }
+        }
+    }
+
+    private void resetState(boolean notify) {
+        setMenuAlpha(0f);
+        mIconsPlaced = false;
+        mMenuFadedIn = false;
+        mAnimating = false;
+        mSnapping = false;
+        mDismissing = false;
+        mMenuSnappedTo = false;
+        setMenuLocation();
+        if (mMenuListener != null && notify) {
+            mMenuListener.onMenuReset(mParent);
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(View view, MotionEvent ev, float velocity) {
+        final int action = ev.getActionMasked();
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                mSnapping = false;
+                if (mFadeAnimator != null) {
+                    mFadeAnimator.cancel();
+                }
+                mHandler.removeCallbacks(mCheckForDrag);
+                mCheckForDrag = null;
+                mPrevX = ev.getRawX();
+                mIsUserTouching = true;
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                mSnapping = false;
+                float diffX = ev.getRawX() - mPrevX;
+                mPrevX = ev.getRawX();
+                if (!isTowardsMenu(diffX) && isMenuLocationChange()) {
+                    // Don't consider it "snapped" if location has changed.
+                    mMenuSnappedTo = false;
+
+                    // Changed directions, make sure we check to fade in icon again.
+                    if (!mHandler.hasCallbacks(mCheckForDrag)) {
+                        // No check scheduled, set null to schedule a new one.
+                        mCheckForDrag = null;
+                    } else {
+                        // Check scheduled, reset alpha and update location; check will fade it in
+                        setMenuAlpha(0f);
+                        setMenuLocation();
+                    }
+                }
+                if (mShouldShowMenu
+                        && !NotificationStackScrollLayout.isPinnedHeadsUp(view)
+                        && !mParent.areGutsExposed()
+                        && !mParent.isDark()
+                        && (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag))) {
+                    // Only show the menu if we're not a heads up view and guts aren't exposed.
+                    mCheckForDrag = new CheckForDrag();
+                    mHandler.postDelayed(mCheckForDrag, SHOW_MENU_DELAY);
+                }
+                break;
+
+            case MotionEvent.ACTION_UP:
+                mIsUserTouching = false;
+                return handleUpEvent(ev, view, velocity);
+            case MotionEvent.ACTION_CANCEL:
+                mIsUserTouching = false;
+                cancelDrag();
+                return false;
+        }
+        return false;
+    }
+
+    private boolean handleUpEvent(MotionEvent ev, View animView, float velocity) {
+        // If the menu should not be shown, then there is no need to check if the a swipe
+        // should result in a snapping to the menu. As a result, just check if the swipe
+        // was enough to dismiss the notification.
+        if (!mShouldShowMenu) {
+            if (mSwipeHelper.isDismissGesture(ev)) {
+                dismiss(animView, velocity);
+            } else {
+                snapBack(animView, velocity);
+            }
+            return true;
+        }
+
+        final boolean gestureTowardsMenu = isTowardsMenu(velocity);
+        final boolean gestureFastEnough =
+                mSwipeHelper.getMinDismissVelocity() <= Math.abs(velocity);
+        final boolean gestureFarEnough =
+                mSwipeHelper.swipedFarEnough(mTranslation, mParent.getWidth());
+        final double timeForGesture = ev.getEventTime() - ev.getDownTime();
+        final boolean showMenuForSlowOnGoing = !mParent.canViewBeDismissed()
+                && timeForGesture >= SWIPE_MENU_TIMING;
+        final float menuSnapTarget = mOnLeft ? getSpaceForMenu() : -getSpaceForMenu();
+
+        if (DEBUG) {
+            Log.d(TAG, "mTranslation= " + mTranslation
+                    + " mAlpha= " + mAlpha
+                    + " velocity= " + velocity
+                    + " mMenuSnappedTo= " + mMenuSnappedTo
+                    + " mMenuSnappedOnLeft= " + mMenuSnappedOnLeft
+                    + " mOnLeft= " + mOnLeft
+                    + " minDismissVel= " + mSwipeHelper.getMinDismissVelocity()
+                    + " isDismissGesture= " + mSwipeHelper.isDismissGesture(ev)
+                    + " gestureTowardsMenu= " + gestureTowardsMenu
+                    + " gestureFastEnough= " + gestureFastEnough
+                    + " gestureFarEnough= " + gestureFarEnough);
+        }
+
+        if (mMenuSnappedTo && isMenuVisible() && mMenuSnappedOnLeft == mOnLeft) {
+            // Menu was snapped to previously and we're on the same side, figure out if
+            // we should stick to the menu, snap back into place, or dismiss
+            final float maximumSwipeDistance = mHorizSpaceForIcon
+                    * SWIPED_BACK_ENOUGH_TO_COVER_FRACTION;
+            final float targetLeft = getSpaceForMenu() - maximumSwipeDistance;
+            final float targetRight = mParent.getWidth() * SWIPED_FAR_ENOUGH_SIZE_FRACTION;
+            boolean withinSnapMenuThreshold = mOnLeft
+                    ? mTranslation > targetLeft && mTranslation < targetRight
+                    : mTranslation < -targetLeft && mTranslation > -targetRight;
+            boolean shouldSnapTo = mOnLeft ? mTranslation < targetLeft : mTranslation > -targetLeft;
+            if (DEBUG) {
+                Log.d(TAG, "   withinSnapMenuThreshold= " + withinSnapMenuThreshold
+                        + "   shouldSnapTo= " + shouldSnapTo
+                        + "   targetLeft= " + targetLeft
+                        + "   targetRight= " + targetRight);
+            }
+            if (withinSnapMenuThreshold && !mSwipeHelper.isDismissGesture(ev)) {
+                // Haven't moved enough to unsnap from the menu
+                showMenu(animView, menuSnapTarget, velocity);
+            } else if (mSwipeHelper.isDismissGesture(ev) && !shouldSnapTo) {
+                // Only dismiss if we're not moving towards the menu
+                dismiss(animView, velocity);
+            } else {
+                snapBack(animView, velocity);
+            }
+        } else if (!mSwipeHelper.isFalseGesture(ev)
+                && (swipedEnoughToShowMenu() && (!gestureFastEnough || showMenuForSlowOnGoing))
+                || (gestureTowardsMenu && !mSwipeHelper.isDismissGesture(ev))) {
+            // Menu has not been snapped to previously and this is menu revealing gesture
+            showMenu(animView, menuSnapTarget, velocity);
+        } else if (mSwipeHelper.isDismissGesture(ev) && !gestureTowardsMenu) {
+            dismiss(animView, velocity);
+        } else {
+            snapBack(animView, velocity);
+        }
+        return true;
+    }
+
+    private void showMenu(View animView, float targetLeft, float velocity) {
+        mMenuSnappedTo = true;
+        mMenuSnappedOnLeft = mOnLeft;
+        mMenuListener.onMenuShown(animView);
+        mSwipeHelper.snap(animView, targetLeft, velocity);
+    }
+
+    private void snapBack(View animView, float velocity) {
+        cancelDrag();
+        mMenuSnappedTo = false;
+        mSnapping = true;
+        mSwipeHelper.snap(animView, 0 /* leftTarget */, velocity);
+    }
+
+    private void dismiss(View animView, float velocity) {
+        cancelDrag();
+        mMenuSnappedTo = false;
+        mDismissing = true;
+        mSwipeHelper.dismiss(animView, velocity);
+    }
+
+    private void cancelDrag() {
+        if (mFadeAnimator != null) {
+            mFadeAnimator.cancel();
+        }
+        mHandler.removeCallbacks(mCheckForDrag);
+    }
+
+    /**
+     * @return whether the notification has been translated enough to show the menu and not enough
+     *         to be dismissed.
+     */
+    private boolean swipedEnoughToShowMenu() {
+        final float multiplier = mParent.canViewBeDismissed()
+                ? SWIPED_FAR_ENOUGH_MENU_FRACTION
+                : SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION;
+        final float minimumSwipeDistance = mHorizSpaceForIcon * multiplier;
+        return !mSwipeHelper.swipedFarEnough(0, 0) && isMenuVisible()
+                && (mOnLeft ? mTranslation > minimumSwipeDistance
+                        : mTranslation < -minimumSwipeDistance);
+    }
+
+    /**
+     * Returns whether the gesture is towards the menu location or not.
+     */
+    private boolean isTowardsMenu(float movement) {
+        return isMenuVisible()
+                && ((mOnLeft && movement <= 0)
+                        || (!mOnLeft && movement >= 0));
+    }
+
+    @Override
+    public void setAppName(String appName) {
+        if (appName == null) {
+            return;
+        }
+        Resources res = mContext.getResources();
+        final int count = mMenuItems.size();
+        for (int i = 0; i < count; i++) {
+            MenuItem item = mMenuItems.get(i);
+            String description = String.format(
+                    res.getString(R.string.notification_menu_accessibility),
+                    appName, item.getContentDescription());
+            View menuView = item.getMenuView();
+            if (menuView != null) {
+                menuView.setContentDescription(description);
+            }
+        }
+    }
+
+    @Override
+    public void onHeightUpdate() {
+        if (mParent == null || mMenuItems.size() == 0 || mMenuContainer == null) {
+            return;
+        }
+        int parentHeight = mParent.getActualHeight();
+        float translationY;
+        if (parentHeight < mVertSpaceForIcons) {
+            translationY = (parentHeight / 2) - (mHorizSpaceForIcon / 2);
+        } else {
+            translationY = (mVertSpaceForIcons - mHorizSpaceForIcon) / 2;
+        }
+        mMenuContainer.setTranslationY(translationY);
+    }
+
+    @Override
+    public void onTranslationUpdate(float translation) {
+        mTranslation = translation;
+        if (mAnimating || !mMenuFadedIn) {
+            // Don't adjust when animating, or if the menu hasn't been shown yet.
+            return;
+        }
+        final float fadeThreshold = mParent.getWidth() * 0.3f;
+        final float absTrans = Math.abs(translation);
+        float desiredAlpha = 0;
+        if (absTrans == 0) {
+            desiredAlpha = 0;
+        } else if (absTrans <= fadeThreshold) {
+            desiredAlpha = 1;
+        } else {
+            desiredAlpha = 1 - ((absTrans - fadeThreshold) / (mParent.getWidth() - fadeThreshold));
+        }
+        setMenuAlpha(desiredAlpha);
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (mMenuListener == null) {
+            // Nothing to do
+            return;
+        }
+        v.getLocationOnScreen(mIconLocation);
+        mParent.getLocationOnScreen(mParentLocation);
+        final int centerX = (int) (mHorizSpaceForIcon / 2);
+        final int centerY = v.getHeight() / 2;
+        final int x = mIconLocation[0] - mParentLocation[0] + centerX;
+        final int y = mIconLocation[1] - mParentLocation[1] + centerY;
+        final int index = mMenuContainer.indexOfChild(v);
+        mMenuListener.onMenuClicked(mParent, x, y, mMenuItems.get(index));
+    }
+
+    private boolean isMenuLocationChange() {
+        boolean onLeft = mTranslation > mIconPadding;
+        boolean onRight = mTranslation < -mIconPadding;
+        if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) {
+            return true;
+        }
+        return false;
+    }
+
+    private void setMenuLocation() {
+        boolean showOnLeft = mTranslation > 0;
+        if ((mIconsPlaced && showOnLeft == mOnLeft) || mSnapping || mMenuContainer == null
+                || !mMenuContainer.isAttachedToWindow()) {
+            // Do nothing
+            return;
+        }
+        final int count = mMenuContainer.getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View v = mMenuContainer.getChildAt(i);
+            final float left = i * mHorizSpaceForIcon;
+            final float right = mParent.getWidth() - (mHorizSpaceForIcon * (i + 1));
+            v.setX(showOnLeft ? left : right);
+        }
+        mOnLeft = showOnLeft;
+        mIconsPlaced = true;
+    }
+
+    private void setMenuAlpha(float alpha) {
+        mAlpha = alpha;
+        if (mMenuContainer == null) {
+            return;
+        }
+        if (alpha == 0) {
+            mMenuFadedIn = false; // Can fade in again once it's gone.
+            mMenuContainer.setVisibility(View.INVISIBLE);
+        } else {
+            mMenuContainer.setVisibility(View.VISIBLE);
+        }
+        final int count = mMenuContainer.getChildCount();
+        for (int i = 0; i < count; i++) {
+            mMenuContainer.getChildAt(i).setAlpha(mAlpha);
+        }
+    }
+
+    /**
+     * Returns the horizontal space in pixels required to display the menu.
+     */
+    private float getSpaceForMenu() {
+        return mHorizSpaceForIcon * mMenuContainer.getChildCount();
+    }
+
+    private final class CheckForDrag implements Runnable {
+        @Override
+        public void run() {
+            final float absTransX = Math.abs(mTranslation);
+            final float bounceBackToMenuWidth = getSpaceForMenu();
+            final float notiThreshold = mParent.getWidth() * 0.4f;
+            if ((!isMenuVisible() || isMenuLocationChange())
+                    && absTransX >= bounceBackToMenuWidth * 0.4
+                    && absTransX < notiThreshold) {
+                fadeInMenu(notiThreshold);
+            }
+        }
+    }
+
+    private void fadeInMenu(final float notiThreshold) {
+        if (mDismissing || mAnimating) {
+            return;
+        }
+        if (isMenuLocationChange()) {
+            setMenuAlpha(0f);
+        }
+        final float transX = mTranslation;
+        final boolean fromLeft = mTranslation > 0;
+        setMenuLocation();
+        mFadeAnimator = ValueAnimator.ofFloat(mAlpha, 1);
+        mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                final float absTrans = Math.abs(transX);
+
+                boolean pastMenu = (fromLeft && transX <= notiThreshold)
+                        || (!fromLeft && absTrans <= notiThreshold);
+                if (pastMenu && !mMenuFadedIn) {
+                    setMenuAlpha((float) animation.getAnimatedValue());
+                }
+            }
+        });
+        mFadeAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mAnimating = true;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                // TODO should animate back to 0f from current alpha
+                setMenuAlpha(0f);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimating = false;
+                mMenuFadedIn = mAlpha == 1;
+            }
+        });
+        mFadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
+        mFadeAnimator.setDuration(ICON_ALPHA_ANIM_DURATION);
+        mFadeAnimator.start();
+    }
+
+    @Override
+    public void setMenuItems(ArrayList<MenuItem> items) {
+        // Do nothing we use our own for now.
+        // TODO -- handle / allow custom menu items!
+    }
+
+    public static MenuItem createSnoozeItem(Context context) {
+        Resources res = context.getResources();
+        NotificationSnooze content = (NotificationSnooze) LayoutInflater.from(context)
+                .inflate(R.layout.notification_snooze, null, false);
+        String snoozeDescription = res.getString(R.string.notification_menu_snooze_description);
+        MenuItem snooze = new NotificationMenuItem(context, snoozeDescription, content,
+                R.drawable.ic_snooze);
+        return snooze;
+    }
+
+    public static MenuItem createInfoItem(Context context) {
+        Resources res = context.getResources();
+        String infoDescription = res.getString(R.string.notification_menu_gear_description);
+        NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
+                R.layout.notification_info, null, false);
+        MenuItem info = new NotificationMenuItem(context, infoDescription, infoContent,
+                R.drawable.ic_settings);
+        return info;
+    }
+
+    public static MenuItem createAppOpsItem(Context context) {
+        AppOpsInfo appOpsContent = (AppOpsInfo) LayoutInflater.from(context).inflate(
+                R.layout.app_ops_info, null, false);
+        MenuItem info = new NotificationMenuItem(context, null, appOpsContent,
+                -1 /*don't show in slow swipe menu */);
+        return info;
+    }
+
+    private void addMenuView(MenuItem item, ViewGroup parent) {
+        View menuView = item.getMenuView();
+        if (menuView != null) {
+            parent.addView(menuView);
+            menuView.setOnClickListener(this);
+            FrameLayout.LayoutParams lp = (LayoutParams) menuView.getLayoutParams();
+            lp.width = (int) mHorizSpaceForIcon;
+            lp.height = (int) mHorizSpaceForIcon;
+            menuView.setLayoutParams(lp);
+        }
+    }
+
+    public static class NotificationMenuItem implements MenuItem {
+        View mMenuView;
+        GutsContent mGutsContent;
+        String mContentDescription;
+
+        /**
+         * Add a new 'guts' panel. If iconResId < 0 it will not appear in the slow swipe menu
+         * but can still be exposed via other affordances.
+         */
+        public NotificationMenuItem(Context context, String s, GutsContent content, int iconResId) {
+            Resources res = context.getResources();
+            int padding = res.getDimensionPixelSize(R.dimen.notification_menu_icon_padding);
+            int tint = res.getColor(R.color.notification_gear_color);
+            if (iconResId >= 0) {
+                AlphaOptimizedImageView iv = new AlphaOptimizedImageView(context);
+                iv.setPadding(padding, padding, padding, padding);
+                Drawable icon = context.getResources().getDrawable(iconResId);
+                iv.setImageDrawable(icon);
+                iv.setColorFilter(tint);
+                iv.setAlpha(1f);
+                mMenuView = iv;
+            }
+            mContentDescription = s;
+            mGutsContent = content;
+        }
+
+        @Override
+        @Nullable
+        public View getMenuView() {
+            return mMenuView;
+        }
+
+        @Override
+        public View getGutsView() {
+            return mGutsContent.getContentView();
+        }
+
+        @Override
+        public String getContentDescription() {
+            return mContentDescription;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
new file mode 100644
index 0000000..75b05c2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -0,0 +1,487 @@
+/*
+ * 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.notification.row;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.metrics.LogMaker;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+import android.text.SpannableString;
+import android.text.style.StyleSpan;
+import android.util.AttributeSet;
+import android.util.KeyValueListParser;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+
+public class NotificationSnooze extends LinearLayout
+        implements NotificationGuts.GutsContent, View.OnClickListener {
+
+    private static final String TAG = "NotificationSnooze";
+    /**
+     * If this changes more number increases, more assistant action resId's should be defined for
+     * accessibility purposes, see {@link #setSnoozeOptions(List)}
+     */
+    private static final int MAX_ASSISTANT_SUGGESTIONS = 1;
+    private static final String KEY_DEFAULT_SNOOZE = "default";
+    private static final String KEY_OPTIONS = "options_array";
+    private static final LogMaker OPTIONS_OPEN_LOG =
+            new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS)
+                    .setType(MetricsEvent.TYPE_OPEN);
+    private static final LogMaker OPTIONS_CLOSE_LOG =
+            new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS)
+                    .setType(MetricsEvent.TYPE_CLOSE);
+    private static final LogMaker UNDO_LOG =
+            new LogMaker(MetricsEvent.NOTIFICATION_UNDO_SNOOZE)
+                    .setType(MetricsEvent.TYPE_ACTION);
+    private NotificationGuts mGutsContainer;
+    private NotificationSwipeActionHelper mSnoozeListener;
+    private StatusBarNotification mSbn;
+
+    private TextView mSelectedOptionText;
+    private TextView mUndoButton;
+    private ImageView mExpandButton;
+    private View mDivider;
+    private ViewGroup mSnoozeOptionContainer;
+    private List<SnoozeOption> mSnoozeOptions;
+    private int mCollapsedHeight;
+    private SnoozeOption mDefaultOption;
+    private SnoozeOption mSelectedOption;
+    private boolean mSnoozing;
+    private boolean mExpanded;
+    private AnimatorSet mExpandAnimation;
+    private KeyValueListParser mParser;
+
+    private final static int[] sAccessibilityActions = {
+            R.id.action_snooze_shorter,
+            R.id.action_snooze_short,
+            R.id.action_snooze_long,
+            R.id.action_snooze_longer,
+    };
+
+    private MetricsLogger mMetricsLogger = new MetricsLogger();
+
+    public NotificationSnooze(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mParser = new KeyValueListParser(',');
+    }
+
+    @VisibleForTesting
+    SnoozeOption getDefaultOption()
+    {
+        return mDefaultOption;
+    }
+
+    @VisibleForTesting
+    void setKeyValueListParser(KeyValueListParser parser) {
+        mParser = parser;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.snooze_snackbar_min_height);
+        findViewById(R.id.notification_snooze).setOnClickListener(this);
+        mSelectedOptionText = (TextView) findViewById(R.id.snooze_option_default);
+        mUndoButton = (TextView) findViewById(R.id.undo);
+        mUndoButton.setOnClickListener(this);
+        mExpandButton = (ImageView) findViewById(R.id.expand_button);
+        mDivider = findViewById(R.id.divider);
+        mDivider.setAlpha(0f);
+        mSnoozeOptionContainer = (ViewGroup) findViewById(R.id.snooze_options);
+        mSnoozeOptionContainer.setVisibility(View.INVISIBLE);
+        mSnoozeOptionContainer.setAlpha(0f);
+
+        // Create the different options based on list
+        mSnoozeOptions = getDefaultSnoozeOptions();
+        createOptionViews();
+
+        setSelected(mDefaultOption, false);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        logOptionSelection(MetricsEvent.NOTIFICATION_SNOOZE_CLICKED, mDefaultOption);
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        if (mGutsContainer != null && mGutsContainer.isExposed()) {
+            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+                event.getText().add(mSelectedOptionText.getText());
+            }
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.addAction(new AccessibilityAction(R.id.action_snooze_undo,
+                getResources().getString(R.string.snooze_undo)));
+        int count = mSnoozeOptions.size();
+        for (int i = 0; i < count; i++) {
+            AccessibilityAction action = mSnoozeOptions.get(i).getAccessibilityAction();
+            if (action != null) {
+                info.addAction(action);
+            }
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+        if (super.performAccessibilityActionInternal(action, arguments)) {
+            return true;
+        }
+        if (action == R.id.action_snooze_undo) {
+            undoSnooze(mUndoButton);
+            return true;
+        }
+        for (int i = 0; i < mSnoozeOptions.size(); i++) {
+            SnoozeOption so = mSnoozeOptions.get(i);
+            if (so.getAccessibilityAction() != null
+                    && so.getAccessibilityAction().getId() == action) {
+                setSelected(so, true);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void setSnoozeOptions(final List<SnoozeCriterion> snoozeList) {
+        if (snoozeList == null) {
+            return;
+        }
+        mSnoozeOptions.clear();
+        mSnoozeOptions = getDefaultSnoozeOptions();
+        final int count = Math.min(MAX_ASSISTANT_SUGGESTIONS, snoozeList.size());
+        for (int i = 0; i < count; i++) {
+            SnoozeCriterion sc = snoozeList.get(i);
+            AccessibilityAction action = new AccessibilityAction(
+                    R.id.action_snooze_assistant_suggestion_1, sc.getExplanation());
+            mSnoozeOptions.add(new NotificationSnoozeOption(sc, 0, sc.getExplanation(),
+                    sc.getConfirmation(), action));
+        }
+        createOptionViews();
+    }
+
+    public boolean isExpanded() {
+        return mExpanded;
+    }
+
+    public void setSnoozeListener(NotificationSwipeActionHelper listener) {
+        mSnoozeListener = listener;
+    }
+
+    public void setStatusBarNotification(StatusBarNotification sbn) {
+        mSbn = sbn;
+    }
+
+    @VisibleForTesting
+    ArrayList<SnoozeOption> getDefaultSnoozeOptions() {
+        final Resources resources = getContext().getResources();
+        ArrayList<SnoozeOption> options = new ArrayList<>();
+        try {
+            final String config = Settings.Global.getString(getContext().getContentResolver(),
+                    Settings.Global.NOTIFICATION_SNOOZE_OPTIONS);
+            mParser.setString(config);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Bad snooze constants");
+        }
+
+        final int defaultSnooze = mParser.getInt(KEY_DEFAULT_SNOOZE,
+                resources.getInteger(R.integer.config_notification_snooze_time_default));
+        final int[] snoozeTimes = mParser.getIntArray(KEY_OPTIONS,
+                resources.getIntArray(R.array.config_notification_snooze_times));
+
+        for (int i = 0; i < snoozeTimes.length && i < sAccessibilityActions.length; i++) {
+            int snoozeTime = snoozeTimes[i];
+            SnoozeOption option = createOption(snoozeTime, sAccessibilityActions[i]);
+            if (i == 0 || snoozeTime == defaultSnooze) {
+                mDefaultOption = option;
+            }
+            options.add(option);
+        }
+        return options;
+    }
+
+    private SnoozeOption createOption(int minutes, int accessibilityActionId) {
+        Resources res = getResources();
+        boolean showInHours = minutes >= 60;
+        int pluralResId = showInHours
+                ? R.plurals.snoozeHourOptions
+                : R.plurals.snoozeMinuteOptions;
+        int count = showInHours ? (minutes / 60) : minutes;
+        String description = res.getQuantityString(pluralResId, count, count);
+        String resultText = String.format(res.getString(R.string.snoozed_for_time), description);
+        AccessibilityAction action = new AccessibilityAction(accessibilityActionId, description);
+        final int index = resultText.indexOf(description);
+        if (index == -1) {
+            return new NotificationSnoozeOption(null, minutes, description, resultText, action);
+        }
+        SpannableString string = new SpannableString(resultText);
+        string.setSpan(new StyleSpan(Typeface.BOLD),
+                index, index + description.length(), 0 /* flags */);
+        return new NotificationSnoozeOption(null, minutes, description, string,
+                action);
+    }
+
+    private void createOptionViews() {
+        mSnoozeOptionContainer.removeAllViews();
+        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+        for (int i = 0; i < mSnoozeOptions.size(); i++) {
+            SnoozeOption option = mSnoozeOptions.get(i);
+            TextView tv = (TextView) inflater.inflate(R.layout.notification_snooze_option,
+                    mSnoozeOptionContainer, false);
+            mSnoozeOptionContainer.addView(tv);
+            tv.setText(option.getDescription());
+            tv.setTag(option);
+            tv.setOnClickListener(this);
+        }
+    }
+
+    private void hideSelectedOption() {
+        final int childCount = mSnoozeOptionContainer.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = mSnoozeOptionContainer.getChildAt(i);
+            child.setVisibility(child.getTag() == mSelectedOption ? View.GONE : View.VISIBLE);
+        }
+    }
+
+    private void showSnoozeOptions(boolean show) {
+        int drawableId = show ? com.android.internal.R.drawable.ic_collapse_notification
+                : com.android.internal.R.drawable.ic_expand_notification;
+        mExpandButton.setImageResource(drawableId);
+        if (mExpanded != show) {
+            mExpanded = show;
+            animateSnoozeOptions(show);
+            if (mGutsContainer != null) {
+                mGutsContainer.onHeightChanged();
+            }
+        }
+    }
+
+    private void animateSnoozeOptions(boolean show) {
+        if (mExpandAnimation != null) {
+            mExpandAnimation.cancel();
+        }
+        ObjectAnimator dividerAnim = ObjectAnimator.ofFloat(mDivider, View.ALPHA,
+                mDivider.getAlpha(), show ? 1f : 0f);
+        ObjectAnimator optionAnim = ObjectAnimator.ofFloat(mSnoozeOptionContainer, View.ALPHA,
+                mSnoozeOptionContainer.getAlpha(), show ? 1f : 0f);
+        mSnoozeOptionContainer.setVisibility(View.VISIBLE);
+        mExpandAnimation = new AnimatorSet();
+        mExpandAnimation.playTogether(dividerAnim, optionAnim);
+        mExpandAnimation.setDuration(150);
+        mExpandAnimation.setInterpolator(show ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
+        mExpandAnimation.addListener(new AnimatorListenerAdapter() {
+            boolean cancelled = false;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                cancelled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!show && !cancelled) {
+                    mSnoozeOptionContainer.setVisibility(View.INVISIBLE);
+                    mSnoozeOptionContainer.setAlpha(0f);
+                }
+            }
+        });
+        mExpandAnimation.start();
+    }
+
+    private void setSelected(SnoozeOption option, boolean userAction) {
+        mSelectedOption = option;
+        mSelectedOptionText.setText(option.getConfirmation());
+        showSnoozeOptions(false);
+        hideSelectedOption();
+        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+        if (userAction) {
+            logOptionSelection(MetricsEvent.NOTIFICATION_SELECT_SNOOZE, option);
+        }
+    }
+
+    private void logOptionSelection(int category, SnoozeOption option) {
+        int index = mSnoozeOptions.indexOf(option);
+        long duration = TimeUnit.MINUTES.toMillis(option.getMinutesToSnoozeFor());
+        mMetricsLogger.write(new LogMaker(category)
+                .setType(MetricsEvent.TYPE_ACTION)
+                .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_INDEX, index)
+                .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS, duration));
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (mGutsContainer != null) {
+            mGutsContainer.resetFalsingCheck();
+        }
+        final int id = v.getId();
+        final SnoozeOption tag = (SnoozeOption) v.getTag();
+        if (tag != null) {
+            setSelected(tag, true);
+        } else if (id == R.id.notification_snooze) {
+            // Toggle snooze options
+            showSnoozeOptions(!mExpanded);
+            mMetricsLogger.write(!mExpanded ? OPTIONS_OPEN_LOG : OPTIONS_CLOSE_LOG);
+        } else {
+            // Undo snooze was selected
+            undoSnooze(v);
+            mMetricsLogger.write(UNDO_LOG);
+        }
+    }
+
+    private void undoSnooze(View v) {
+        mSelectedOption = null;
+        int[] parentLoc = new int[2];
+        int[] targetLoc = new int[2];
+        mGutsContainer.getLocationOnScreen(parentLoc);
+        v.getLocationOnScreen(targetLoc);
+        final int centerX = v.getWidth() / 2;
+        final int centerY = v.getHeight() / 2;
+        final int x = targetLoc[0] - parentLoc[0] + centerX;
+        final int y = targetLoc[1] - parentLoc[1] + centerY;
+        showSnoozeOptions(false);
+        mGutsContainer.closeControls(x, y, false /* save */, false /* force */);
+    }
+
+    @Override
+    public int getActualHeight() {
+        return mExpanded ? getHeight() : mCollapsedHeight;
+    }
+
+    @Override
+    public boolean willBeRemoved() {
+        return mSnoozing;
+    }
+
+    @Override
+    public View getContentView() {
+        // Reset the view before use
+        setSelected(mDefaultOption, false);
+        return this;
+    }
+
+    @Override
+    public void setGutsParent(NotificationGuts guts) {
+        mGutsContainer = guts;
+    }
+
+    @Override
+    public boolean handleCloseControls(boolean save, boolean force) {
+        if (mExpanded && !force) {
+            // Collapse expanded state on outside touch
+            showSnoozeOptions(false);
+            return true;
+        } else if (mSnoozeListener != null && mSelectedOption != null) {
+            // Snooze option selected so commit it
+            mSnoozing = true;
+            mSnoozeListener.snooze(mSbn, mSelectedOption);
+            return true;
+        } else {
+            // The view should actually be closed
+            setSelected(mSnoozeOptions.get(0), false);
+            return false; // Return false here so that guts handles closing the view
+        }
+    }
+
+    @Override
+    public boolean isLeavebehind() {
+        return true;
+    }
+
+    @Override
+    public boolean shouldBeSaved() {
+        return true;
+    }
+
+    public class NotificationSnoozeOption implements SnoozeOption {
+        private SnoozeCriterion mCriterion;
+        private int mMinutesToSnoozeFor;
+        private CharSequence mDescription;
+        private CharSequence mConfirmation;
+        private AccessibilityAction mAction;
+
+        public NotificationSnoozeOption(SnoozeCriterion sc, int minToSnoozeFor,
+                CharSequence description,
+                CharSequence confirmation, AccessibilityAction action) {
+            mCriterion = sc;
+            mMinutesToSnoozeFor = minToSnoozeFor;
+            mDescription = description;
+            mConfirmation = confirmation;
+            mAction = action;
+        }
+
+        @Override
+        public SnoozeCriterion getSnoozeCriterion() {
+            return mCriterion;
+        }
+
+        @Override
+        public CharSequence getDescription() {
+            return mDescription;
+        }
+
+        @Override
+        public CharSequence getConfirmation() {
+            return mConfirmation;
+        }
+
+        @Override
+        public int getMinutesToSnoozeFor() {
+            return mMinutesToSnoozeFor;
+        }
+
+        @Override
+        public AccessibilityAction getAccessibilityAction() {
+            return mAction;
+        }
+
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationUndoLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationUndoLayout.java
new file mode 100644
index 0000000..3ea8195
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationUndoLayout.java
@@ -0,0 +1,139 @@
+/*
+ * 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.row;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.systemui.R;
+
+/**
+ * Custom view for the NotificationInfo confirmation views so that the confirmation text can
+ * occupy the full width of the notification and push the undo button down to the next line if
+ * necessary.
+ *
+ * @see NotificationInfo
+ */
+public class NotificationUndoLayout extends FrameLayout {
+    /**
+     * View for the prompt/confirmation text to tell the user the previous action was successful.
+     */
+    private View mConfirmationTextView;
+    /** Undo button (actionable text) view. */
+    private View mUndoView;
+
+    /**
+     * Whether {@link #mConfirmationTextView} is multiline and will require the full width of the
+     * parent (which causes the {@link #mUndoView} to push down).
+     */
+    private boolean mIsMultiline = false;
+    private int mMultilineTopMargin;
+
+    public NotificationUndoLayout(Context context) {
+        this(context, null);
+    }
+
+    public NotificationUndoLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NotificationUndoLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mConfirmationTextView = findViewById(R.id.confirmation_text);
+        mUndoView = findViewById(R.id.undo);
+
+        mMultilineTopMargin = getResources().getDimensionPixelOffset(
+                com.android.internal.R.dimen.notification_content_margin_start);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        LayoutParams confirmationLayoutParams =
+                (LayoutParams) mConfirmationTextView.getLayoutParams();
+        LayoutParams undoLayoutParams =(LayoutParams) mUndoView.getLayoutParams();
+
+        int measuredWidth = getMeasuredWidth();
+        // Ignore the left margin on the undo button - no need for additional extra space between
+        // the text and the button.
+        int requiredWidth = mConfirmationTextView.getMeasuredWidth()
+                + confirmationLayoutParams.rightMargin
+                + confirmationLayoutParams.leftMargin
+                + mUndoView.getMeasuredWidth()
+                + undoLayoutParams.rightMargin;
+        // If the measured width isn't enough to accommodate both the undo button and the text in
+        // the same line, we'll need to adjust the view to be multi-line. Otherwise, we're done.
+        if (requiredWidth > measuredWidth) {
+            mIsMultiline = true;
+
+            // Update height requirement to the text height and the button's height (along with
+            // additional spacing for the top of the text).
+            int updatedHeight = mMultilineTopMargin
+                    + mConfirmationTextView.getMeasuredHeight()
+                    + mUndoView.getMeasuredHeight()
+                    + undoLayoutParams.topMargin
+                    + undoLayoutParams.bottomMargin;
+
+            setMeasuredDimension(measuredWidth, updatedHeight);
+        } else {
+            mIsMultiline = false;
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        // If the text view and undo view don't fit on the same line, we'll need to manually lay
+        // out the content.
+        if (mIsMultiline) {
+            // Re-align parent right/bottom values. Left and top are considered to be 0.
+            int parentBottom = getMeasuredHeight();
+            int parentRight = getMeasuredWidth();
+
+            LayoutParams confirmationLayoutParams =
+                    (LayoutParams) mConfirmationTextView.getLayoutParams();
+            LayoutParams undoLayoutParams = (LayoutParams) mUndoView.getLayoutParams();
+
+            // The confirmation text occupies the full width as computed earlier. Both side margins
+            // are equivalent, so we only need to grab the left one here.
+            mConfirmationTextView.layout(
+                    confirmationLayoutParams.leftMargin,
+                    mMultilineTopMargin,
+                    confirmationLayoutParams.leftMargin + mConfirmationTextView.getMeasuredWidth(),
+                    mMultilineTopMargin + mConfirmationTextView.getMeasuredHeight());
+
+            // The undo button is aligned bottom|end with the parent in the case of multiline text.
+            int undoViewLeft = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+                    ? undoLayoutParams.rightMargin
+                    : parentRight - mUndoView.getMeasuredWidth() - undoLayoutParams.rightMargin;
+            mUndoView.layout(
+                    undoViewLeft,
+                    parentBottom - mUndoView.getMeasuredHeight() - undoLayoutParams.bottomMargin,
+                    undoViewLeft + mUndoView.getMeasuredWidth(),
+                    parentBottom - undoLayoutParams.bottomMargin);
+        } else {
+            super.onLayout(changed, left, top, right, bottom);
+        }
+    }
+}
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
new file mode 100644
index 0000000..a21794b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -0,0 +1,81 @@
+/*
+ * 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.notification.row;
+
+import android.content.Context;
+import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.notification.NotificationData;
+
+/**
+ * An inflater task that asynchronously inflates a ExpandableNotificationRow
+ */
+public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInflateFinishedListener {
+
+    private static final String TAG = "RowInflaterTask";
+    private static final boolean TRACE_ORIGIN = true;
+
+    private RowInflationFinishedListener mListener;
+    private NotificationData.Entry 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,
+            RowInflationFinishedListener listener) {
+        if (TRACE_ORIGIN) {
+            mInflateOrigin = new Throwable("inflate requested here");
+        }
+        mListener = listener;
+        AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
+        mEntry = entry;
+        entry.setInflationTask(this);
+        inflater.inflate(R.layout.status_bar_notification_row, parent, this);
+    }
+
+    @Override
+    public void abort() {
+        mCancelled = true;
+    }
+
+    @Override
+    public void onInflateFinished(View view, int resid, ViewGroup parent) {
+        if (!mCancelled) {
+            try {
+                mEntry.onInflationTaskFinished();
+                mListener.onInflationFinished((ExpandableNotificationRow) view);
+            } catch (Throwable t) {
+                if (mInflateOrigin != null) {
+                    Log.e(TAG, "Error in inflation finished listener: " + t, mInflateOrigin);
+                    t.addSuppressed(mInflateOrigin);
+                }
+                throw t;
+            }
+        }
+    }
+
+    public interface RowInflationFinishedListener {
+        void onInflationFinished(ExpandableNotificationRow row);
+    }
+}
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
new file mode 100644
index 0000000..8a061a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -0,0 +1,219 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+
+/**
+ * A common base class for all views in the notification stack scroller which don't have a
+ * background.
+ */
+public abstract class StackScrollerDecorView extends ExpandableView {
+
+    protected View mContent;
+    protected View mSecondaryView;
+    private boolean mIsVisible = true;
+    private boolean mContentVisible = true;
+    private boolean mIsSecondaryVisible = true;
+    private int mDuration = 260;
+    private boolean mContentAnimating;
+    private final Runnable mContentVisibilityEndRunnable = () -> {
+        mContentAnimating = false;
+        if (getVisibility() != View.GONE && !mIsVisible) {
+            setVisibility(GONE);
+            setWillBeGone(false);
+            notifyHeightChanged(false /* needsAnimation */);
+        }
+    };
+
+    public StackScrollerDecorView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mContent = findContentView();
+        mSecondaryView = findSecondaryView();
+        setVisible(false /* nowVisible */, false /* animate */);
+        setSecondaryVisible(false /* nowVisible */, false /* animate */);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        setOutlineProvider(null);
+    }
+
+    @Override
+    public boolean isTransparent() {
+        return true;
+    }
+
+    /**
+     * Set the content of this view to be visible in an animated way.
+     *
+     * @param contentVisible True if the content should be visible or false if it should be hidden.
+     */
+    public void setContentVisible(boolean contentVisible) {
+        setContentVisible(contentVisible, true /* animate */);
+    }
+    /**
+     * Set the content of this view to be visible.
+     * @param contentVisible True if the content should be visible or false if it should be hidden.
+     * @param animate Should an animation be performed.
+     */
+    private void setContentVisible(boolean contentVisible, boolean animate) {
+        if (mContentVisible != contentVisible) {
+            mContentAnimating = animate;
+            setViewVisible(mContent, contentVisible, animate, mContentVisibilityEndRunnable);
+            mContentVisible = contentVisible;
+        } if (!mContentAnimating) {
+            mContentVisibilityEndRunnable.run();
+        }
+    }
+
+    public boolean isContentVisible() {
+        return mContentVisible;
+    }
+
+    /**
+     * Make this view visible. If {@code false} is passed, the view will fade out it's content
+     * and set the view Visibility to GONE. If only the content should be changed
+     * {@link #setContentVisible(boolean)} can be used.
+     *
+     * @param nowVisible should the view be visible
+     * @param animate should the change be animated.
+     */
+    public void setVisible(boolean nowVisible, boolean animate) {
+        if (mIsVisible != nowVisible) {
+            mIsVisible = nowVisible;
+            if (animate) {
+                if (nowVisible) {
+                    setVisibility(VISIBLE);
+                    setWillBeGone(false);
+                    notifyHeightChanged(false /* needsAnimation */);
+                } else {
+                    setWillBeGone(true);
+                }
+                setContentVisible(nowVisible, true /* animate */);
+            } else {
+                setVisibility(nowVisible ? VISIBLE : GONE);
+                setContentVisible(nowVisible, false /* animate */);
+                setWillBeGone(false);
+                notifyHeightChanged(false /* needsAnimation */);
+            }
+        }
+    }
+
+    /**
+     * Set the secondary view of this layout to visible.
+     *
+     * @param nowVisible should the secondary view be visible
+     * @param animate should the change be animated
+     */
+    public void setSecondaryVisible(boolean nowVisible, boolean animate) {
+        if (mIsSecondaryVisible != nowVisible) {
+            setViewVisible(mSecondaryView, nowVisible, animate, null /* endRunnable */);
+            mIsSecondaryVisible = nowVisible;
+        }
+    }
+
+    @VisibleForTesting
+    boolean isSecondaryVisible() {
+        return mIsSecondaryVisible;
+    }
+
+    /**
+     * Is this view visible. If a view is currently animating to gone, it will
+     * return {@code false}.
+     */
+    public boolean isVisible() {
+        return mIsVisible;
+    }
+
+    void setDuration(int duration) {
+        mDuration = duration;
+    }
+
+    /**
+     * Animate a view to a new visibility.
+     * @param view Target view, maybe content view or dismiss view.
+     * @param nowVisible Should it now be visible.
+     * @param animate Should this be done in an animated way.
+     * @param endRunnable A runnable that is run when the animation is done.
+     */
+    private void setViewVisible(View view, boolean nowVisible,
+            boolean animate, Runnable endRunnable) {
+        if (view == null) {
+            return;
+        }
+        // cancel any previous animations
+        view.animate().cancel();
+        float endValue = nowVisible ? 1.0f : 0.0f;
+        if (!animate) {
+            view.setAlpha(endValue);
+            if (endRunnable != null) {
+                endRunnable.run();
+            }
+            return;
+        }
+
+        // Animate the view alpha
+        Interpolator interpolator = nowVisible ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT;
+        view.animate()
+                .alpha(endValue)
+                .setInterpolator(interpolator)
+                .setDuration(mDuration)
+                .withEndAction(endRunnable);
+    }
+
+    @Override
+    public void performRemoveAnimation(long duration, long delay,
+            float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+            Runnable onFinishedRunnable,
+            AnimatorListenerAdapter animationListener) {
+        // TODO: Use duration
+        setContentVisible(false);
+    }
+
+    @Override
+    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
+        // TODO: use delay and duration
+        setContentVisible(true);
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    protected abstract View findContentView();
+
+    /**
+     * Returns a view that might not always appear while the main content view is still visible.
+     */
+    protected abstract View findSecondaryView();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
new file mode 100644
index 0000000..2da4d2c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper;
+
+import android.app.Notification;
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
+import android.view.View;
+
+import com.android.systemui.statusbar.notification.ImageTransformState;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+
+/**
+ * Wraps a notification containing a big picture template
+ */
+public class NotificationBigPictureTemplateViewWrapper extends NotificationTemplateViewWrapper {
+
+    protected NotificationBigPictureTemplateViewWrapper(Context ctx, View view,
+            ExpandableNotificationRow row) {
+        super(ctx, view, row);
+    }
+
+    @Override
+    public void onContentUpdated(ExpandableNotificationRow row) {
+        super.onContentUpdated(row);
+        updateImageTag(row.getStatusBarNotification());
+    }
+
+    private void updateImageTag(StatusBarNotification notification) {
+        final Bundle extras = notification.getNotification().extras;
+        Icon overRiddenIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG);
+        if (overRiddenIcon != null) {
+            mPicture.setTag(ImageTransformState.ICON_TAG, overRiddenIcon);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
new file mode 100644
index 0000000..133df3c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper;
+
+import android.content.Context;
+import android.service.notification.StatusBarNotification;
+import android.view.View;
+
+import com.android.internal.widget.ImageFloatingTextView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.TransformableView;
+
+/**
+ * Wraps a notification containing a big text template
+ */
+public class NotificationBigTextTemplateViewWrapper extends NotificationTemplateViewWrapper {
+
+    private ImageFloatingTextView mBigtext;
+
+    protected NotificationBigTextTemplateViewWrapper(Context ctx, View view,
+            ExpandableNotificationRow row) {
+        super(ctx, view, row);
+    }
+
+    private void resolveViews(StatusBarNotification notification) {
+        mBigtext = (ImageFloatingTextView) mView.findViewById(com.android.internal.R.id.big_text);
+    }
+
+    @Override
+    public void onContentUpdated(ExpandableNotificationRow row) {
+        // Reinspect the notification. Before the super call, because the super call also updates
+        // the transformation types and we need to have our values set by then.
+        resolveViews(row.getStatusBarNotification());
+        super.onContentUpdated(row);
+    }
+
+    @Override
+    protected void updateTransformedTypes() {
+        // This also clears the existing types
+        super.updateTransformedTypes();
+        if (mBigtext != null) {
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT,
+                    mBigtext);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
new file mode 100644
index 0000000..db7b4fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
@@ -0,0 +1,67 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+
+/**
+ * Wraps a notification containing a custom view.
+ */
+public class NotificationCustomViewWrapper extends NotificationViewWrapper {
+
+    private boolean mIsLegacy;
+    private int mLegacyColor;
+
+    protected NotificationCustomViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
+        super(ctx, view, row);
+        mLegacyColor = row.getContext().getColor(R.color.notification_legacy_background_color);
+    }
+
+    @Override
+    public void setVisible(boolean visible) {
+        super.setVisible(visible);
+        mView.setAlpha(visible ? 1.0f : 0.0f);
+    }
+
+    @Override
+    protected boolean shouldClearBackgroundOnReapply() {
+        return false;
+    }
+
+    @Override
+    public int getCustomBackgroundColor() {
+        int customBackgroundColor = super.getCustomBackgroundColor();
+        if (customBackgroundColor == 0 && mIsLegacy) {
+            return mLegacyColor;
+        }
+        return customBackgroundColor;
+    }
+
+    public void setLegacy(boolean legacy) {
+        super.setLegacy(legacy);
+        mIsLegacy = legacy;
+    }
+
+    @Override
+    public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
+        return true;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
new file mode 100644
index 0000000..6ca07ed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper;
+
+import static com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+        .DEFAULT_HEADER_VISIBLE_AMOUNT;
+import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
+
+import android.app.Notification;
+import android.content.Context;
+import android.util.ArraySet;
+import android.view.NotificationHeaderView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.widget.NotificationExpandButton;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.CustomInterpolatorTransformation;
+import com.android.systemui.statusbar.notification.ImageTransformState;
+import com.android.systemui.statusbar.notification.TransformState;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.ViewTransformationHelper;
+
+import java.util.Stack;
+
+/**
+ * Wraps a notification header view.
+ */
+public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
+
+    private static final Interpolator LOW_PRIORITY_HEADER_CLOSE
+            = new PathInterpolator(0.4f, 0f, 0.7f, 1f);
+
+    protected final ViewTransformationHelper mTransformationHelper;
+    private final int mTranslationForHeader;
+
+    protected int mColor;
+    private ImageView mIcon;
+
+    private NotificationExpandButton mExpandButton;
+    private NotificationHeaderView mNotificationHeader;
+    private TextView mHeaderText;
+    private ImageView mWorkProfileImage;
+    private boolean mIsLowPriority;
+    private boolean mTransformLowPriorityTitle;
+    private boolean mShowExpandButtonAtEnd;
+    protected float mHeaderTranslation;
+
+    protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
+        super(ctx, view, row);
+        mShowExpandButtonAtEnd = ctx.getResources().getBoolean(
+                R.bool.config_showNotificationExpandButtonAtEnd);
+        mTransformationHelper = new ViewTransformationHelper();
+
+        // we want to avoid that the header clashes with the other text when transforming
+        // low-priority
+        mTransformationHelper.setCustomTransformation(
+                new CustomInterpolatorTransformation(TRANSFORMING_VIEW_TITLE) {
+
+                    @Override
+                    public Interpolator getCustomInterpolator(int interpolationType,
+                            boolean isFrom) {
+                        boolean isLowPriority = mView instanceof NotificationHeaderView;
+                        if (interpolationType == TRANSFORM_Y) {
+                            if (isLowPriority && !isFrom
+                                    || !isLowPriority && isFrom) {
+                                return Interpolators.LINEAR_OUT_SLOW_IN;
+                            } else {
+                                return LOW_PRIORITY_HEADER_CLOSE;
+                            }
+                        }
+                        return null;
+                    }
+
+                    @Override
+                    protected boolean hasCustomTransformation() {
+                        return mIsLowPriority && mTransformLowPriorityTitle;
+                    }
+                }, TRANSFORMING_VIEW_TITLE);
+        resolveHeaderViews();
+        addAppOpsOnClickListener(row);
+        mTranslationForHeader = ctx.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_content_margin)
+                - ctx.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.notification_content_margin_top);
+    }
+
+    protected void resolveHeaderViews() {
+        mIcon = mView.findViewById(com.android.internal.R.id.icon);
+        mHeaderText = mView.findViewById(com.android.internal.R.id.header_text);
+        mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
+        mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
+        mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
+        mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
+        mColor = mNotificationHeader.getOriginalIconColor();
+    }
+
+    private void addAppOpsOnClickListener(ExpandableNotificationRow row) {
+        mNotificationHeader.setAppOpsOnClickListener(row.getAppOpsOnClickListener());
+    }
+
+    @Override
+    public void onContentUpdated(ExpandableNotificationRow row) {
+        super.onContentUpdated(row);
+        mIsLowPriority = row.isLowPriority();
+        mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren();
+        ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews();
+
+        // Reinspect the notification.
+        resolveHeaderViews();
+        if (row.getHeaderVisibleAmount() != DEFAULT_HEADER_VISIBLE_AMOUNT) {
+            setHeaderVisibleAmount(row.getHeaderVisibleAmount());
+        }
+        updateTransformedTypes();
+        addRemainingTransformTypes();
+        updateCropToPaddingForImageViews();
+        Notification notification = row.getStatusBarNotification().getNotification();
+        mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
+        // The work profile image is always the same lets just set the icon tag for it not to
+        // animate
+        mWorkProfileImage.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
+
+        // We need to reset all views that are no longer transforming in case a view was previously
+        // transformed, but now we decided to transform its container instead.
+        ArraySet<View> currentViews = mTransformationHelper.getAllTransformingViews();
+        for (int i = 0; i < previousViews.size(); i++) {
+            View view = previousViews.valueAt(i);
+            if (!currentViews.contains(view)) {
+                mTransformationHelper.resetTransformedView(view);
+            }
+        }
+    }
+
+    /**
+     * Adds the remaining TransformTypes to the TransformHelper. This is done to make sure that each
+     * child is faded automatically and doesn't have to be manually added.
+     * The keys used for the views are the ids.
+     */
+    private void addRemainingTransformTypes() {
+        mTransformationHelper.addRemainingTransformTypes(mView);
+    }
+
+    /**
+     * Since we are deactivating the clipping when transforming the ImageViews don't get clipped
+     * anymore during these transitions. We can avoid that by using
+     * {@link ImageView#setCropToPadding(boolean)} on all ImageViews.
+     */
+    private void updateCropToPaddingForImageViews() {
+        Stack<View> stack = new Stack<>();
+        stack.push(mView);
+        while (!stack.isEmpty()) {
+            View child = stack.pop();
+            if (child instanceof ImageView) {
+                ((ImageView) child).setCropToPadding(true);
+            } else if (child instanceof ViewGroup){
+                ViewGroup group = (ViewGroup) child;
+                for (int i = 0; i < group.getChildCount(); i++) {
+                    stack.push(group.getChildAt(i));
+                }
+            }
+        }
+    }
+
+    protected void updateTransformedTypes() {
+        mTransformationHelper.reset();
+        mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon);
+        if (mIsLowPriority) {
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
+                    mHeaderText);
+        }
+    }
+
+    @Override
+    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
+        mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
+        mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
+    }
+
+    @Override
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+        super.setHeaderVisibleAmount(headerVisibleAmount);
+        mNotificationHeader.setAlpha(headerVisibleAmount);
+        mHeaderTranslation = (1.0f - headerVisibleAmount) * mTranslationForHeader;
+        mView.setTranslationY(mHeaderTranslation);
+    }
+
+    @Override
+    public int getHeaderTranslation() {
+        return (int) mHeaderTranslation;
+    }
+
+    @Override
+    public NotificationHeaderView getNotificationHeader() {
+        return mNotificationHeader;
+    }
+
+    @Override
+    public TransformState getCurrentState(int fadingView) {
+        return mTransformationHelper.getCurrentState(fadingView);
+    }
+
+    @Override
+    public void transformTo(TransformableView notification, Runnable endRunnable) {
+        mTransformationHelper.transformTo(notification, endRunnable);
+    }
+
+    @Override
+    public void transformTo(TransformableView notification, float transformationAmount) {
+        mTransformationHelper.transformTo(notification, transformationAmount);
+    }
+
+    @Override
+    public void transformFrom(TransformableView notification) {
+        mTransformationHelper.transformFrom(notification);
+    }
+
+    @Override
+    public void transformFrom(TransformableView notification, float transformationAmount) {
+        mTransformationHelper.transformFrom(notification, transformationAmount);
+    }
+
+    @Override
+    public void setIsChildInGroup(boolean isChildInGroup) {
+        super.setIsChildInGroup(isChildInGroup);
+        mTransformLowPriorityTitle = !isChildInGroup;
+    }
+
+    @Override
+    public void setVisible(boolean visible) {
+        super.setVisible(visible);
+        mTransformationHelper.setVisible(visible);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
new file mode 100644
index 0000000..37d2f6b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.TransformableView;
+
+/**
+ * Wraps a notification containing a media template
+ */
+public class NotificationMediaTemplateViewWrapper extends NotificationTemplateViewWrapper {
+
+    protected NotificationMediaTemplateViewWrapper(Context ctx, View view,
+            ExpandableNotificationRow row) {
+        super(ctx, view, row);
+    }
+
+    View mActions;
+
+    private void resolveViews() {
+        mActions = mView.findViewById(com.android.internal.R.id.media_actions);
+    }
+
+    @Override
+    public void onContentUpdated(ExpandableNotificationRow row) {
+        // Reinspect the notification. Before the super call, because the super call also updates
+        // the transformation types and we need to have our values set by then.
+        resolveViews();
+        super.onContentUpdated(row);
+    }
+
+    @Override
+    protected void updateTransformedTypes() {
+        // This also clears the existing types
+        super.updateTransformedTypes();
+        if (mActions != null) {
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ACTIONS,
+                    mActions);
+        }
+    }
+
+    @Override
+    public boolean isDimmable() {
+        return getCustomBackgroundColor() == 0;
+    }
+
+    @Override
+    public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
+        return true;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java
new file mode 100644
index 0000000..13c5960
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper;
+
+import com.android.internal.widget.MessagingLayout;
+import com.android.internal.widget.MessagingLinearLayout;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+
+import android.content.Context;
+import android.view.View;
+
+/**
+ * Wraps a notification containing a messaging template
+ */
+public class NotificationMessagingTemplateViewWrapper extends NotificationTemplateViewWrapper {
+
+    private final int mMinHeightWithActions;
+    private MessagingLayout mMessagingLayout;
+    private MessagingLinearLayout mMessagingLinearLayout;
+
+    protected NotificationMessagingTemplateViewWrapper(Context ctx, View view,
+            ExpandableNotificationRow row) {
+        super(ctx, view, row);
+        mMessagingLayout = (MessagingLayout) view;
+        mMinHeightWithActions = NotificationUtils.getFontScaledHeight(ctx,
+                R.dimen.notification_messaging_actions_min_height);
+    }
+
+    private void resolveViews() {
+        mMessagingLinearLayout = mMessagingLayout.getMessagingLinearLayout();
+    }
+
+    @Override
+    public void onContentUpdated(ExpandableNotificationRow row) {
+        // Reinspect the notification. Before the super call, because the super call also updates
+        // the transformation types and we need to have our values set by then.
+        resolveViews();
+        super.onContentUpdated(row);
+    }
+
+    @Override
+    protected void updateTransformedTypes() {
+        // This also clears the existing types
+        super.updateTransformedTypes();
+        if (mMessagingLinearLayout != null) {
+            mTransformationHelper.addTransformedView(mMessagingLinearLayout.getId(),
+                    mMessagingLinearLayout);
+        }
+    }
+
+    @Override
+    public void setRemoteInputVisible(boolean visible) {
+        mMessagingLayout.showHistoricMessages(visible);
+    }
+
+    @Override
+    public int getMinLayoutHeight() {
+        if (mActionsContainer != null && mActionsContainer.getVisibility() != View.GONE) {
+            return mMinHeightWithActions;
+        }
+        return super.getMinLayoutHeight();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
new file mode 100644
index 0000000..d934902
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -0,0 +1,348 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.android.internal.util.ContrastColorUtil;
+import com.android.internal.widget.NotificationActionListLayout;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.UiOffloadThread;
+import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.notification.ImageTransformState;
+import com.android.systemui.statusbar.notification.TransformState;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.ViewTransformationHelper;
+import com.android.systemui.statusbar.notification.row.HybridNotificationView;
+
+/**
+ * Wraps a notification view inflated from a template.
+ */
+public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapper {
+
+    protected ImageView mPicture;
+    private ProgressBar mProgressBar;
+    private TextView mTitle;
+    private TextView mText;
+    protected View mActionsContainer;
+    private ImageView mReplyAction;
+    private Rect mTmpRect = new Rect();
+
+    private int mContentHeight;
+    private int mMinHeightHint;
+    private NotificationActionListLayout mActions;
+    private ArraySet<PendingIntent> mCancelledPendingIntents = new ArraySet<>();
+    private UiOffloadThread mUiOffloadThread;
+    private View mRemoteInputHistory;
+
+    protected NotificationTemplateViewWrapper(Context ctx, View view,
+            ExpandableNotificationRow row) {
+        super(ctx, view, row);
+        mTransformationHelper.setCustomTransformation(
+                new ViewTransformationHelper.CustomTransformation() {
+                    @Override
+                    public boolean transformTo(TransformState ownState,
+                            TransformableView notification, final float transformationAmount) {
+                        if (!(notification instanceof HybridNotificationView)) {
+                            return false;
+                        }
+                        TransformState otherState = notification.getCurrentState(
+                                TRANSFORMING_VIEW_TITLE);
+                        final View text = ownState.getTransformedView();
+                        CrossFadeHelper.fadeOut(text, transformationAmount);
+                        if (otherState != null) {
+                            ownState.transformViewVerticalTo(otherState, this,
+                                    transformationAmount);
+                            otherState.recycle();
+                        }
+                        return true;
+                    }
+
+                    @Override
+                    public boolean customTransformTarget(TransformState ownState,
+                            TransformState otherState) {
+                        float endY = getTransformationY(ownState, otherState);
+                        ownState.setTransformationEndY(endY);
+                        return true;
+                    }
+
+                    @Override
+                    public boolean transformFrom(TransformState ownState,
+                            TransformableView notification, float transformationAmount) {
+                        if (!(notification instanceof HybridNotificationView)) {
+                            return false;
+                        }
+                        TransformState otherState = notification.getCurrentState(
+                                TRANSFORMING_VIEW_TITLE);
+                        final View text = ownState.getTransformedView();
+                        CrossFadeHelper.fadeIn(text, transformationAmount);
+                        if (otherState != null) {
+                            ownState.transformViewVerticalFrom(otherState, this,
+                                    transformationAmount);
+                            otherState.recycle();
+                        }
+                        return true;
+                    }
+
+                    @Override
+                    public boolean initTransformation(TransformState ownState,
+                            TransformState otherState) {
+                        float startY = getTransformationY(ownState, otherState);
+                        ownState.setTransformationStartY(startY);
+                        return true;
+                    }
+
+                    private float getTransformationY(TransformState ownState,
+                            TransformState otherState) {
+                        int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
+                        int[] ownStablePosition = ownState.getLaidOutLocationOnScreen();
+                        return (otherStablePosition[1]
+                                + otherState.getTransformedView().getHeight()
+                                - ownStablePosition[1]) * 0.33f;
+                    }
+
+                }, TRANSFORMING_VIEW_TEXT);
+    }
+
+    private void resolveTemplateViews(StatusBarNotification notification) {
+        mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
+        if (mPicture != null) {
+            mPicture.setTag(ImageTransformState.ICON_TAG,
+                    notification.getNotification().getLargeIcon());
+        }
+        mTitle = (TextView) mView.findViewById(com.android.internal.R.id.title);
+        mText = (TextView) mView.findViewById(com.android.internal.R.id.text);
+        final View progress = mView.findViewById(com.android.internal.R.id.progress);
+        if (progress instanceof ProgressBar) {
+            mProgressBar = (ProgressBar) progress;
+        } else {
+            // It's still a viewstub
+            mProgressBar = null;
+        }
+        mActionsContainer = mView.findViewById(com.android.internal.R.id.actions_container);
+        mActions = mView.findViewById(com.android.internal.R.id.actions);
+        mReplyAction = mView.findViewById(com.android.internal.R.id.reply_icon_action);
+        mRemoteInputHistory = mView.findViewById(
+                com.android.internal.R.id.notification_material_reply_container);
+        updatePendingIntentCancellations();
+    }
+
+    private void updatePendingIntentCancellations() {
+        if (mActions != null) {
+            int numActions = mActions.getChildCount();
+            for (int i = 0; i < numActions; i++) {
+                Button action = (Button) mActions.getChildAt(i);
+                performOnPendingIntentCancellation(action, () -> {
+                    if (action.isEnabled()) {
+                        action.setEnabled(false);
+                        // The visual appearance doesn't look disabled enough yet, let's add the
+                        // alpha as well. Since Alpha doesn't play nicely right now with the
+                        // transformation, we rather blend it manually with the background color.
+                        ColorStateList textColors = action.getTextColors();
+                        int[] colors = textColors.getColors();
+                        int[] newColors = new int[colors.length];
+                        float disabledAlpha = mView.getResources().getFloat(
+                                com.android.internal.R.dimen.notification_action_disabled_alpha);
+                        for (int j = 0; j < colors.length; j++) {
+                            int color = colors[j];
+                            color = blendColorWithBackground(color, disabledAlpha);
+                            newColors[j] = color;
+                        }
+                        ColorStateList newColorStateList = new ColorStateList(
+                                textColors.getStates(), newColors);
+                        action.setTextColor(newColorStateList);
+                    }
+                });
+            }
+        }
+        if (mReplyAction != null) {
+            // Let's reset the view on update, assuming the new pending intent isn't cancelled
+            // anymore. The color filter automatically resets when it's updated.
+            mReplyAction.setEnabled(true);
+            performOnPendingIntentCancellation(mReplyAction, () -> {
+                if (mReplyAction != null && mReplyAction.isEnabled()) {
+                    mReplyAction.setEnabled(false);
+                    // The visual appearance doesn't look disabled enough yet, let's add the
+                    // alpha as well. Since Alpha doesn't play nicely right now with the
+                    // transformation, we rather blend it manually with the background color.
+                    Drawable drawable = mReplyAction.getDrawable().mutate();
+                    PorterDuffColorFilter colorFilter =
+                            (PorterDuffColorFilter) drawable.getColorFilter();
+                    float disabledAlpha = mView.getResources().getFloat(
+                            com.android.internal.R.dimen.notification_action_disabled_alpha);
+                    if (colorFilter != null) {
+                        int color = colorFilter.getColor();
+                        color = blendColorWithBackground(color, disabledAlpha);
+                        drawable.mutate().setColorFilter(color, colorFilter.getMode());
+                    } else {
+                        mReplyAction.setAlpha(disabledAlpha);
+                    }
+                }
+            });
+        }
+    }
+
+    private int blendColorWithBackground(int color, float alpha) {
+        // alpha doesn't go well for color filters, so let's blend it manually
+        return ContrastColorUtil.compositeColors(Color.argb((int) (alpha * 255),
+                Color.red(color), Color.green(color), Color.blue(color)), resolveBackgroundColor());
+    }
+
+    private void performOnPendingIntentCancellation(View view, Runnable cancellationRunnable) {
+        PendingIntent pendingIntent = (PendingIntent) view.getTag(
+                com.android.internal.R.id.pending_intent_tag);
+        if (pendingIntent == null) {
+            return;
+        }
+        if (mCancelledPendingIntents.contains(pendingIntent)) {
+            cancellationRunnable.run();
+        } else {
+            PendingIntent.CancelListener listener = (PendingIntent intent) -> {
+                mView.post(() -> {
+                    mCancelledPendingIntents.add(pendingIntent);
+                    cancellationRunnable.run();
+                });
+            };
+            if (mUiOffloadThread == null) {
+                mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+            }
+            if (view.isAttachedToWindow()) {
+                mUiOffloadThread.submit(() -> pendingIntent.registerCancelListener(listener));
+            }
+            view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View v) {
+                    mUiOffloadThread.submit(() -> pendingIntent.registerCancelListener(listener));
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                    mUiOffloadThread.submit(() -> pendingIntent.unregisterCancelListener(listener));
+                }
+            });
+        }
+    }
+
+    @Override
+    public boolean disallowSingleClick(float x, float y) {
+        if (mReplyAction != null && mReplyAction.getVisibility() == View.VISIBLE) {
+            if (isOnView(mReplyAction, x, y) || isOnView(mPicture, x, y)) {
+                return true;
+            }
+        }
+        return super.disallowSingleClick(x, y);
+    }
+
+    private boolean isOnView(View view, float x, float y) {
+        View searchView = (View) view.getParent();
+        while (searchView != null && !(searchView instanceof ExpandableNotificationRow)) {
+            searchView.getHitRect(mTmpRect);
+            x -= mTmpRect.left;
+            y -= mTmpRect.top;
+            searchView = (View) searchView.getParent();
+        }
+        view.getHitRect(mTmpRect);
+        return mTmpRect.contains((int) x,(int) y);
+    }
+
+    @Override
+    public void onContentUpdated(ExpandableNotificationRow row) {
+        // Reinspect the notification. Before the super call, because the super call also updates
+        // the transformation types and we need to have our values set by then.
+        resolveTemplateViews(row.getStatusBarNotification());
+        super.onContentUpdated(row);
+    }
+
+    @Override
+    protected void updateTransformedTypes() {
+        // This also clears the existing types
+        super.updateTransformedTypes();
+        if (mTitle != null) {
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
+                    mTitle);
+        }
+        if (mText != null) {
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT,
+                    mText);
+        }
+        if (mPicture != null) {
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE,
+                    mPicture);
+        }
+        if (mProgressBar != null) {
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS,
+                    mProgressBar);
+        }
+    }
+
+    @Override
+    public void setContentHeight(int contentHeight, int minHeightHint) {
+        super.setContentHeight(contentHeight, minHeightHint);
+
+        mContentHeight = contentHeight;
+        mMinHeightHint = minHeightHint;
+        updateActionOffset();
+    }
+
+    @Override
+    public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
+        if (super.shouldClipToRounding(topRounded, bottomRounded)) {
+            return true;
+        }
+        return bottomRounded && mActionsContainer != null
+                && mActionsContainer.getVisibility() != View.GONE;
+    }
+
+    private void updateActionOffset() {
+        if (mActionsContainer != null) {
+            // We should never push the actions higher than they are in the headsup view.
+            int constrainedContentHeight = Math.max(mContentHeight, mMinHeightHint);
+
+            // We also need to compensate for any header translation, since we're always at the end.
+            mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight()
+                    - getHeaderTranslation());
+        }
+    }
+
+    @Override
+    public int getExtraMeasureHeight() {
+        int extra = 0;
+        if (mActions != null) {
+            extra = mActions.getExtraMeasureHeight();
+        }
+        if (mRemoteInputHistory != null && mRemoteInputHistory.getVisibility() != View.GONE) {
+            extra += mRow.getContext().getResources().getDimensionPixelSize(
+                    R.dimen.remote_input_history_extra_height);
+        }
+        return extra + super.getExtraMeasureHeight();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
new file mode 100644
index 0000000..2ca7282
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -0,0 +1,192 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper;
+
+import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.NotificationHeaderView;
+import android.view.View;
+
+import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.notification.TransformState;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.TransformableView;
+
+/**
+ * Wraps the actual notification content view; used to implement behaviors which are different for
+ * the individual templates and custom views.
+ */
+public abstract class NotificationViewWrapper implements TransformableView {
+
+    protected final View mView;
+    protected final ExpandableNotificationRow mRow;
+
+    private int mBackgroundColor = 0;
+
+    public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) {
+        if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
+            if ("bigPicture".equals(v.getTag())) {
+                return new NotificationBigPictureTemplateViewWrapper(ctx, v, row);
+            } else if ("bigText".equals(v.getTag())) {
+                return new NotificationBigTextTemplateViewWrapper(ctx, v, row);
+            } else if ("media".equals(v.getTag()) || "bigMediaNarrow".equals(v.getTag())) {
+                return new NotificationMediaTemplateViewWrapper(ctx, v, row);
+            } else if ("messaging".equals(v.getTag())) {
+                return new NotificationMessagingTemplateViewWrapper(ctx, v, row);
+            }
+            return new NotificationTemplateViewWrapper(ctx, v, row);
+        } else if (v instanceof NotificationHeaderView) {
+            return new NotificationHeaderViewWrapper(ctx, v, row);
+        } else {
+            return new NotificationCustomViewWrapper(ctx, v, row);
+        }
+    }
+
+    protected NotificationViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
+        mView = view;
+        mRow = row;
+        onReinflated();
+    }
+
+    /**
+     * Notifies this wrapper that the content of the view might have changed.
+     * @param row the row this wrapper is attached to
+     */
+    public void onContentUpdated(ExpandableNotificationRow row) {
+    }
+
+    public void onReinflated() {
+        if (shouldClearBackgroundOnReapply()) {
+            mBackgroundColor = 0;
+        }
+        Drawable background = mView.getBackground();
+        if (background instanceof ColorDrawable) {
+            mBackgroundColor = ((ColorDrawable) background).getColor();
+            mView.setBackground(null);
+        }
+    }
+
+    protected boolean shouldClearBackgroundOnReapply() {
+        return true;
+    }
+
+    /**
+     * Update the appearance of the expand button.
+     *
+     * @param expandable should this view be expandable
+     * @param onClickListener the listener to invoke when the expand affordance is clicked on
+     */
+    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {}
+
+    /**
+     * @return the notification header if it exists
+     */
+    public NotificationHeaderView getNotificationHeader() {
+        return null;
+    }
+
+    public int getHeaderTranslation() {
+        return 0;
+    }
+
+    @Override
+    public TransformState getCurrentState(int fadingView) {
+        return null;
+    }
+
+    @Override
+    public void transformTo(TransformableView notification, Runnable endRunnable) {
+        // By default we are fading out completely
+        CrossFadeHelper.fadeOut(mView, endRunnable);
+    }
+
+    @Override
+    public void transformTo(TransformableView notification, float transformationAmount) {
+        CrossFadeHelper.fadeOut(mView, transformationAmount);
+    }
+
+    @Override
+    public void transformFrom(TransformableView notification) {
+        // By default we are fading in completely
+        CrossFadeHelper.fadeIn(mView);
+    }
+
+    @Override
+    public void transformFrom(TransformableView notification, float transformationAmount) {
+        CrossFadeHelper.fadeIn(mView, transformationAmount);
+    }
+
+    @Override
+    public void setVisible(boolean visible) {
+        mView.animate().cancel();
+        mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+    }
+
+    public int getCustomBackgroundColor() {
+        // Parent notifications should always use the normal background color
+        return mRow.isSummaryWithChildren() ? 0 : mBackgroundColor;
+    }
+
+    protected int resolveBackgroundColor() {
+        int customBackgroundColor = getCustomBackgroundColor();
+        if (customBackgroundColor != 0) {
+            return customBackgroundColor;
+        }
+        return mView.getContext().getColor(
+                com.android.internal.R.color.notification_material_background_color);
+    }
+
+    public void setLegacy(boolean legacy) {
+    }
+
+    public void setContentHeight(int contentHeight, int minHeightHint) {
+    }
+
+    public void setRemoteInputVisible(boolean visible) {
+    }
+
+    public void setIsChildInGroup(boolean isChildInGroup) {
+    }
+
+    public boolean isDimmable() {
+        return true;
+    }
+
+    public boolean disallowSingleClick(float x, float y) {
+        return false;
+    }
+
+    public int getMinLayoutHeight() {
+        return 0;
+    }
+
+    public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
+        return false;
+    }
+
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+    }
+
+    /**
+     * Get the extra height that needs to be added to this view, such that it can be measured
+     * normally.
+     */
+    public int getExtraMeasureHeight() {
+        return 0;
+    }
+}
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
new file mode 100644
index 0000000..bb07f33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -0,0 +1,447 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import java.util.ArrayList;
+
+/**
+ * A global state to track all input states for the algorithm.
+ */
+public class AmbientState {
+    private ArrayList<View> mDraggedViews = new ArrayList<View>();
+    private int mScrollY;
+    private boolean mDimmed;
+    private ActivatableNotificationView mActivatedChild;
+    private float mOverScrollTopAmount;
+    private float mOverScrollBottomAmount;
+    private int mSpeedBumpIndex = -1;
+    private boolean mDark;
+    private boolean mHideSensitive;
+    private HeadsUpManager mHeadsUpManager;
+    private float mStackTranslation;
+    private int mLayoutHeight;
+    private int mTopPadding;
+    private boolean mShadeExpanded;
+    private float mMaxHeadsUpTranslation;
+    private boolean mDismissAllInProgress;
+    private int mLayoutMinHeight;
+    private NotificationShelf mShelf;
+    private int mZDistanceBetweenElements;
+    private int mBaseZHeight;
+    private int mMaxLayoutHeight;
+    private ActivatableNotificationView mLastVisibleBackgroundChild;
+    private float mCurrentScrollVelocity;
+    private int mStatusBarState;
+    private float mExpandingVelocity;
+    private boolean mPanelTracking;
+    private boolean mExpansionChanging;
+    private boolean mPanelFullWidth;
+    private boolean mPulsing;
+    private boolean mUnlockHintRunning;
+    private boolean mQsCustomizerShowing;
+    private int mIntrinsicPadding;
+    private int mExpandAnimationTopChange;
+    private ExpandableNotificationRow mExpandingNotification;
+    private int mDarkTopPadding;
+    private float mDarkAmount;
+    private boolean mAppearing;
+
+    public AmbientState(Context context) {
+        reload(context);
+    }
+
+    /**
+     * Reload the dimens e.g. if the density changed.
+     */
+    public void reload(Context context) {
+        mZDistanceBetweenElements = getZDistanceBetweenElements(context);
+        mBaseZHeight = getBaseHeight(mZDistanceBetweenElements);
+    }
+
+    private static int getZDistanceBetweenElements(Context context) {
+        return Math.max(1, context.getResources()
+                .getDimensionPixelSize(R.dimen.z_distance_between_notifications));
+    }
+
+    private static int getBaseHeight(int zdistanceBetweenElements) {
+        return 4 * zdistanceBetweenElements;
+    }
+
+    /**
+     * @return the launch height for notifications that are launched
+     */
+    public static int getNotificationLaunchHeight(Context context) {
+        int zDistance = getZDistanceBetweenElements(context);
+        return getBaseHeight(zDistance) * 2;
+    }
+
+    /**
+     * @return the basic Z height on which notifications remain.
+     */
+    public int getBaseZHeight() {
+        return mBaseZHeight;
+    }
+
+    /**
+     * @return the distance in Z between two overlaying notifications.
+     */
+    public int getZDistanceBetweenElements() {
+        return mZDistanceBetweenElements;
+    }
+
+    public int getScrollY() {
+        return mScrollY;
+    }
+
+    public void setScrollY(int scrollY) {
+        this.mScrollY = scrollY;
+    }
+
+    public void onBeginDrag(View view) {
+        mDraggedViews.add(view);
+    }
+
+    public void onDragFinished(View view) {
+        mDraggedViews.remove(view);
+    }
+
+    public ArrayList<View> getDraggedViews() {
+        return mDraggedViews;
+    }
+
+    /**
+     * @param dimmed Whether we are in a dimmed state (on the lockscreen), where the backgrounds are
+     *               translucent and everything is scaled back a bit.
+     */
+    public void setDimmed(boolean dimmed) {
+        mDimmed = dimmed;
+    }
+
+    /** In dark mode, we draw as little as possible, assuming a black background */
+    public void setDark(boolean dark) {
+        mDark = dark;
+    }
+
+    /** Dark ratio of the status bar **/
+    public void setDarkAmount(float darkAmount) {
+        mDarkAmount = darkAmount;
+    }
+
+    /** Returns the dark ratio of the status bar */
+    public float getDarkAmount() {
+        return mDarkAmount;
+    }
+
+    public void setHideSensitive(boolean hideSensitive) {
+        mHideSensitive = hideSensitive;
+    }
+
+    /**
+     * In dimmed mode, a child can be activated, which happens on the first tap of the double-tap
+     * interaction. This child is then scaled normally and its background is fully opaque.
+     */
+    public void setActivatedChild(ActivatableNotificationView activatedChild) {
+        mActivatedChild = activatedChild;
+    }
+
+    public boolean isDimmed() {
+        return mDimmed;
+    }
+
+    public boolean isDark() {
+        return mDark;
+    }
+
+    public boolean isHideSensitive() {
+        return mHideSensitive;
+    }
+
+    public ActivatableNotificationView getActivatedChild() {
+        return mActivatedChild;
+    }
+
+    public void setOverScrollAmount(float amount, boolean onTop) {
+        if (onTop) {
+            mOverScrollTopAmount = amount;
+        } else {
+            mOverScrollBottomAmount = amount;
+        }
+    }
+
+    public float getOverScrollAmount(boolean top) {
+        return top ? mOverScrollTopAmount : mOverScrollBottomAmount;
+    }
+
+    public int getSpeedBumpIndex() {
+        return mSpeedBumpIndex;
+    }
+
+    public void setSpeedBumpIndex(int shelfIndex) {
+        mSpeedBumpIndex = shelfIndex;
+    }
+
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+    }
+
+    public float getStackTranslation() {
+        return mStackTranslation;
+    }
+
+    public void setStackTranslation(float stackTranslation) {
+        mStackTranslation = stackTranslation;
+    }
+
+    public void setLayoutHeight(int layoutHeight) {
+        mLayoutHeight = layoutHeight;
+    }
+
+    public float getTopPadding() {
+        return mTopPadding;
+    }
+
+    public void setTopPadding(int topPadding) {
+        mTopPadding = topPadding;
+    }
+
+    public int getInnerHeight() {
+        return Math.max(Math.min(mLayoutHeight, mMaxLayoutHeight) - mTopPadding, mLayoutMinHeight);
+    }
+
+    public boolean isShadeExpanded() {
+        return mShadeExpanded;
+    }
+
+    public void setShadeExpanded(boolean shadeExpanded) {
+        mShadeExpanded = shadeExpanded;
+    }
+
+    public void setMaxHeadsUpTranslation(float maxHeadsUpTranslation) {
+        mMaxHeadsUpTranslation = maxHeadsUpTranslation;
+    }
+
+    public float getMaxHeadsUpTranslation() {
+        return mMaxHeadsUpTranslation;
+    }
+
+    public void setDismissAllInProgress(boolean dismissAllInProgress) {
+        mDismissAllInProgress = dismissAllInProgress;
+    }
+
+    public boolean isDismissAllInProgress() {
+        return mDismissAllInProgress;
+    }
+
+    public void setLayoutMinHeight(int layoutMinHeight) {
+        mLayoutMinHeight = layoutMinHeight;
+    }
+
+    public void setShelf(NotificationShelf shelf) {
+        mShelf = shelf;
+    }
+
+    @Nullable
+    public NotificationShelf getShelf() {
+        return mShelf;
+    }
+
+    public void setLayoutMaxHeight(int maxLayoutHeight) {
+        mMaxLayoutHeight = maxLayoutHeight;
+    }
+
+    /**
+     * Sets the last visible view of the host layout, that has a background, i.e the very last
+     * view in the shade, without the clear all button.
+     */
+    public void setLastVisibleBackgroundChild(
+            ActivatableNotificationView lastVisibleBackgroundChild) {
+        mLastVisibleBackgroundChild = lastVisibleBackgroundChild;
+    }
+
+    public ActivatableNotificationView getLastVisibleBackgroundChild() {
+        return mLastVisibleBackgroundChild;
+    }
+
+    public void setCurrentScrollVelocity(float currentScrollVelocity) {
+        mCurrentScrollVelocity = currentScrollVelocity;
+    }
+
+    public float getCurrentScrollVelocity() {
+        return mCurrentScrollVelocity;
+    }
+
+    public boolean isOnKeyguard() {
+        return mStatusBarState == StatusBarState.KEYGUARD;
+    }
+
+    public void setStatusBarState(int statusBarState) {
+        mStatusBarState = statusBarState;
+    }
+
+    public void setExpandingVelocity(float expandingVelocity) {
+        mExpandingVelocity = expandingVelocity;
+    }
+
+    public void setExpansionChanging(boolean expansionChanging) {
+        mExpansionChanging = expansionChanging;
+    }
+
+    public boolean isExpansionChanging() {
+        return mExpansionChanging;
+    }
+
+    public float getExpandingVelocity() {
+        return mExpandingVelocity;
+    }
+
+    public void setPanelTracking(boolean panelTracking) {
+        mPanelTracking = panelTracking;
+    }
+
+    public boolean hasPulsingNotifications() {
+        return mPulsing;
+    }
+
+    public void setPulsing(boolean hasPulsing) {
+        mPulsing = hasPulsing;
+    }
+
+    public boolean isPulsing(NotificationData.Entry entry) {
+        if (!mPulsing || mHeadsUpManager == null) {
+            return false;
+        }
+        return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry));
+    }
+
+    public boolean isPanelTracking() {
+        return mPanelTracking;
+    }
+
+    public boolean isPanelFullWidth() {
+        return mPanelFullWidth;
+    }
+
+    public void setPanelFullWidth(boolean panelFullWidth) {
+        mPanelFullWidth = panelFullWidth;
+    }
+
+    public void setUnlockHintRunning(boolean unlockHintRunning) {
+        mUnlockHintRunning = unlockHintRunning;
+    }
+
+    public boolean isUnlockHintRunning() {
+        return mUnlockHintRunning;
+    }
+
+    public boolean isQsCustomizerShowing() {
+        return mQsCustomizerShowing;
+    }
+
+    public void setQsCustomizerShowing(boolean qsCustomizerShowing) {
+        mQsCustomizerShowing = qsCustomizerShowing;
+    }
+
+    public void setIntrinsicPadding(int intrinsicPadding) {
+        mIntrinsicPadding = intrinsicPadding;
+    }
+
+    public int getIntrinsicPadding() {
+        return mIntrinsicPadding;
+    }
+
+    /**
+     * Similar to the normal is above shelf logic but doesn't allow it to be above in AOD1.
+     *
+     * @param expandableView the view to check
+     */
+    public boolean isAboveShelf(ExpandableView expandableView) {
+        if (!(expandableView instanceof ExpandableNotificationRow)) {
+            return expandableView.isAboveShelf();
+        }
+        ExpandableNotificationRow row = (ExpandableNotificationRow) expandableView;
+        return row.isAboveShelf() && !isDozingAndNotPulsing(row);
+    }
+
+    /**
+     * @return whether a view is dozing and not pulsing right now
+     */
+    public boolean isDozingAndNotPulsing(ExpandableView view) {
+        if (view instanceof ExpandableNotificationRow) {
+            return isDozingAndNotPulsing((ExpandableNotificationRow) view);
+        }
+        return false;
+    }
+
+    /**
+     * @return whether a row is dozing and not pulsing right now
+     */
+    public boolean isDozingAndNotPulsing(ExpandableNotificationRow row) {
+        return isDark() && !isPulsing(row.getEntry());
+    }
+
+    public void setExpandAnimationTopChange(int expandAnimationTopChange) {
+        mExpandAnimationTopChange = expandAnimationTopChange;
+    }
+
+    public void setExpandingNotification(ExpandableNotificationRow row) {
+        mExpandingNotification = row;
+    }
+
+    public ExpandableNotificationRow getExpandingNotification() {
+        return mExpandingNotification;
+    }
+
+    public int getExpandAnimationTopChange() {
+        return mExpandAnimationTopChange;
+    }
+
+    /**
+     * @return {@code true } when shade is completely dark: in AOD or ambient display.
+     */
+    public boolean isFullyDark() {
+        return mDarkAmount == 1;
+    }
+
+    public void setDarkTopPadding(int darkTopPadding) {
+        mDarkTopPadding = darkTopPadding;
+    }
+
+    public int getDarkTopPadding() {
+        return mDarkTopPadding;
+    }
+
+    public void setAppearing(boolean appearing) {
+        mAppearing = appearing;
+    }
+
+    public boolean isAppearing() {
+        return mAppearing;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationFilter.java
new file mode 100644
index 0000000..c6f953c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationFilter.java
@@ -0,0 +1,191 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import androidx.collection.ArraySet;
+import android.util.Property;
+import android.view.View;
+
+import java.util.ArrayList;
+
+/**
+ * Filters the animations for only a certain type of properties.
+ */
+public class AnimationFilter {
+    public static final int NO_DELAY = -1;
+    boolean animateAlpha;
+    boolean animateX;
+    boolean animateY;
+    ArraySet<View> animateYViews = new ArraySet<>();
+    boolean animateZ;
+    boolean animateHeight;
+    boolean animateTopInset;
+    boolean animateDimmed;
+    boolean animateDark;
+    boolean animateHideSensitive;
+    public boolean animateShadowAlpha;
+    boolean hasDelays;
+    boolean hasGoToFullShadeEvent;
+    long customDelay;
+    private ArraySet<Property> mAnimatedProperties = new ArraySet<>();
+
+    public AnimationFilter animateAlpha() {
+        animateAlpha = true;
+        return this;
+    }
+
+    public AnimationFilter animateScale() {
+        animate(View.SCALE_X);
+        animate(View.SCALE_Y);
+        return this;
+    }
+
+    public AnimationFilter animateX() {
+        animateX = true;
+        return this;
+    }
+
+    public AnimationFilter animateY() {
+        animateY = true;
+        return this;
+    }
+
+    public AnimationFilter hasDelays() {
+        hasDelays = true;
+        return this;
+    }
+
+    public AnimationFilter animateZ() {
+        animateZ = true;
+        return this;
+    }
+
+    public AnimationFilter animateHeight() {
+        animateHeight = true;
+        return this;
+    }
+
+    public AnimationFilter animateTopInset() {
+        animateTopInset = true;
+        return this;
+    }
+
+    public AnimationFilter animateDimmed() {
+        animateDimmed = true;
+        return this;
+    }
+
+    public AnimationFilter animateDark() {
+        animateDark = true;
+        return this;
+    }
+
+    public AnimationFilter animateHideSensitive() {
+        animateHideSensitive = true;
+        return this;
+    }
+
+    public AnimationFilter animateShadowAlpha() {
+        animateShadowAlpha = true;
+        return this;
+    }
+
+    public AnimationFilter animateY(View view) {
+        animateYViews.add(view);
+        return this;
+    }
+
+    public boolean shouldAnimateY(View view) {
+        return animateY || animateYViews.contains(view);
+    }
+
+    /**
+     * Combines multiple filters into {@code this} filter, using or as the operand .
+     *
+     * @param events The animation events from the filters to combine.
+     */
+    public void applyCombination(ArrayList<NotificationStackScrollLayout.AnimationEvent> events) {
+        reset();
+        int size = events.size();
+        for (int i = 0; i < size; i++) {
+            NotificationStackScrollLayout.AnimationEvent ev = events.get(i);
+            combineFilter(events.get(i).filter);
+            if (ev.animationType ==
+                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE) {
+                hasGoToFullShadeEvent = true;
+            }
+            if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
+                    .ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
+                customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP;
+            } else if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
+                    .ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
+                // We need both timeouts when clicking, one to delay it and one for the animation
+                // to look nice
+                customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP_CLICKED
+                        + StackStateAnimator.ANIMATION_DELAY_HEADS_UP;
+            } else if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
+                    .ANIMATION_TYPE_PULSE_APPEAR || ev.animationType ==
+                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
+                customDelay = StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR / 2;
+            }
+        }
+    }
+
+    public void combineFilter(AnimationFilter filter) {
+        animateAlpha |= filter.animateAlpha;
+        animateX |= filter.animateX;
+        animateY |= filter.animateY;
+        animateYViews.addAll(filter.animateYViews);
+        animateZ |= filter.animateZ;
+        animateHeight |= filter.animateHeight;
+        animateTopInset |= filter.animateTopInset;
+        animateDimmed |= filter.animateDimmed;
+        animateDark |= filter.animateDark;
+        animateHideSensitive |= filter.animateHideSensitive;
+        animateShadowAlpha |= filter.animateShadowAlpha;
+        hasDelays |= filter.hasDelays;
+        mAnimatedProperties.addAll(filter.mAnimatedProperties);
+    }
+
+    public void reset() {
+        animateAlpha = false;
+        animateX = false;
+        animateY = false;
+        animateYViews.clear();
+        animateZ = false;
+        animateHeight = false;
+        animateShadowAlpha = false;
+        animateTopInset = false;
+        animateDimmed = false;
+        animateDark = false;
+        animateHideSensitive = false;
+        hasDelays = false;
+        hasGoToFullShadeEvent = false;
+        customDelay = NO_DELAY;
+        mAnimatedProperties.clear();
+    }
+
+    public AnimationFilter animate(Property property) {
+        mAnimatedProperties.add(property);
+        return this;
+    }
+
+    public boolean shouldAnimateProperty(Property property) {
+        // TODO: migrate all existing animators to properties
+        return mAnimatedProperties.contains(property);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
new file mode 100644
index 0000000..87a3cc9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import android.animation.AnimatorListenerAdapter;
+import android.util.ArrayMap;
+import android.util.Property;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+/**
+ * Properties for a View animation
+ */
+public class AnimationProperties {
+    public long duration;
+    public long delay;
+    private ArrayMap<Property, Interpolator> mInterpolatorMap;
+    private AnimatorListenerAdapter mAnimatorListenerAdapter;
+
+    /**
+     * @return an animation filter for this animation.
+     */
+    public AnimationFilter getAnimationFilter() {
+        return new AnimationFilter() {
+            @Override
+            public boolean shouldAnimateProperty(Property property) {
+                return true;
+            }
+        };
+    }
+
+    /**
+     * @return a listener that should be run whenever any property finished its animation
+     */
+    public AnimatorListenerAdapter getAnimationFinishListener() {
+        return mAnimatorListenerAdapter;
+    }
+
+    public AnimationProperties setAnimationFinishListener(AnimatorListenerAdapter listener) {
+        mAnimatorListenerAdapter = listener;
+        return this;
+    }
+
+    public boolean wasAdded(View view) {
+        return false;
+    }
+
+    /**
+     * Get a custom interpolator for a property instead of the normal one.
+     */
+    public Interpolator getCustomInterpolator(View child, Property property) {
+        return mInterpolatorMap != null ? mInterpolatorMap.get(property) : null;
+    }
+
+
+    public void combineCustomInterpolators(AnimationProperties iconAnimationProperties) {
+        ArrayMap<Property, Interpolator> map = iconAnimationProperties.mInterpolatorMap;
+        if (map != null) {
+            if (mInterpolatorMap == null) {
+                mInterpolatorMap = new ArrayMap<>();
+            }
+            mInterpolatorMap.putAll(map);
+        }
+    }
+
+    /**
+     * Set a custom interpolator to use for all views for a property.
+     */
+    public AnimationProperties setCustomInterpolator(Property property, Interpolator interpolator) {
+        if (mInterpolatorMap == null) {
+            mInterpolatorMap = new ArrayMap<>();
+        }
+        mInterpolatorMap.put(property, interpolator);
+        return this;
+    }
+
+    public AnimationProperties setDuration(long duration) {
+        this.duration = duration;
+        return this;
+    }
+
+    public AnimationProperties setDelay(long delay) {
+        this.delay = delay;
+        return this;
+    }
+
+    public AnimationProperties resetCustomInterpolators() {
+        mInterpolatorMap = null;
+        return this;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
new file mode 100644
index 0000000..8c1a788
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.view.View;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+
+/**
+* A state of an expandable view
+*/
+public class ExpandableViewState extends ViewState {
+
+    private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
+    private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
+    private static final int TAG_ANIMATOR_SHADOW_ALPHA = R.id.shadow_alpha_animator_tag;
+    private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
+    private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
+    private static final int TAG_END_SHADOW_ALPHA = R.id.shadow_alpha_animator_end_value_tag;
+    private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag;
+    private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
+    private static final int TAG_START_SHADOW_ALPHA = R.id.shadow_alpha_animator_start_value_tag;
+
+    // These are flags such that we can create masks for filtering.
+
+    /**
+     * No known location. This is the default and should not be set after an invocation of the
+     * algorithm.
+     */
+    public static final int LOCATION_UNKNOWN = 0x00;
+
+    /**
+     * The location is the first heads up notification, so on the very top.
+     */
+    public static final int LOCATION_FIRST_HUN = 0x01;
+
+    /**
+     * The location is hidden / scrolled away on the top.
+     */
+    public static final int LOCATION_HIDDEN_TOP = 0x02;
+
+    /**
+     * The location is in the main area of the screen and visible.
+     */
+    public static final int LOCATION_MAIN_AREA = 0x04;
+
+    /**
+     * The location is in the bottom stack and it's peeking
+     */
+    public static final int LOCATION_BOTTOM_STACK_PEEKING = 0x08;
+
+    /**
+     * The location is in the bottom stack and it's hidden.
+     */
+    public static final int LOCATION_BOTTOM_STACK_HIDDEN = 0x10;
+
+    /**
+     * The view isn't laid out at all.
+     */
+    public static final int LOCATION_GONE = 0x40;
+
+    /**
+     * The visible locations of a view.
+     */
+    public static final int VISIBLE_LOCATIONS = ExpandableViewState.LOCATION_FIRST_HUN
+            | ExpandableViewState.LOCATION_MAIN_AREA;
+
+    public int height;
+    public boolean dimmed;
+    public boolean dark;
+    public boolean hideSensitive;
+    public boolean belowSpeedBump;
+    public float shadowAlpha;
+    public boolean inShelf;
+
+    /**
+     * A state indicating whether a headsup is currently fully visible, even when not scrolled.
+     * Only valid if the view is heads upped.
+     */
+    public boolean headsUpIsVisible;
+
+    /**
+     * How much the child overlaps with the previous child on top. This is used to
+     * show the background properly when the child on top is translating away.
+     */
+    public int clipTopAmount;
+
+    /**
+     * The index of the view, only accounting for views not equal to GONE
+     */
+    public int notGoneIndex;
+
+    /**
+     * The location this view is currently rendered at.
+     *
+     * <p>See <code>LOCATION_</code> flags.</p>
+     */
+    public int location;
+
+    @Override
+    public void copyFrom(ViewState viewState) {
+        super.copyFrom(viewState);
+        if (viewState instanceof ExpandableViewState) {
+            ExpandableViewState svs = (ExpandableViewState) viewState;
+            height = svs.height;
+            dimmed = svs.dimmed;
+            shadowAlpha = svs.shadowAlpha;
+            dark = svs.dark;
+            hideSensitive = svs.hideSensitive;
+            belowSpeedBump = svs.belowSpeedBump;
+            clipTopAmount = svs.clipTopAmount;
+            notGoneIndex = svs.notGoneIndex;
+            location = svs.location;
+            headsUpIsVisible = svs.headsUpIsVisible;
+        }
+    }
+
+    /**
+     * Applies a {@link ExpandableViewState} to a {@link ExpandableView}.
+     */
+    @Override
+    public void applyToView(View view) {
+        super.applyToView(view);
+        if (view instanceof ExpandableView) {
+            ExpandableView expandableView = (ExpandableView) view;
+
+            int height = expandableView.getActualHeight();
+            int newHeight = this.height;
+
+            // apply height
+            if (height != newHeight) {
+                expandableView.setActualHeight(newHeight, false /* notifyListeners */);
+            }
+
+            float shadowAlpha = expandableView.getShadowAlpha();
+            float newShadowAlpha = this.shadowAlpha;
+
+            // apply shadowAlpha
+            if (shadowAlpha != newShadowAlpha) {
+                expandableView.setShadowAlpha(newShadowAlpha);
+            }
+
+            // apply dimming
+            expandableView.setDimmed(this.dimmed, false /* animate */);
+
+            // apply hiding sensitive
+            expandableView.setHideSensitive(
+                    this.hideSensitive, false /* animated */, 0 /* delay */, 0 /* duration */);
+
+            // apply below shelf speed bump
+            expandableView.setBelowSpeedBump(this.belowSpeedBump);
+
+            // apply dark
+            expandableView.setDark(this.dark, false /* animate */, 0 /* delay */);
+
+            // apply clipping
+            float oldClipTopAmount = expandableView.getClipTopAmount();
+            if (oldClipTopAmount != this.clipTopAmount) {
+                expandableView.setClipTopAmount(this.clipTopAmount);
+            }
+
+            expandableView.setTransformingInShelf(false);
+            expandableView.setInShelf(inShelf);
+
+            if (headsUpIsVisible) {
+                expandableView.setHeadsUpIsVisible();
+            }
+        }
+    }
+
+    @Override
+    public void animateTo(View child, AnimationProperties properties) {
+        super.animateTo(child, properties);
+        if (!(child instanceof ExpandableView)) {
+            return;
+        }
+        ExpandableView expandableView = (ExpandableView) child;
+        AnimationFilter animationFilter = properties.getAnimationFilter();
+
+        // start height animation
+        if (this.height != expandableView.getActualHeight()) {
+            startHeightAnimation(expandableView, properties);
+        }  else {
+            abortAnimation(child, TAG_ANIMATOR_HEIGHT);
+        }
+
+        // start shadow alpha animation
+        if (this.shadowAlpha != expandableView.getShadowAlpha()) {
+            startShadowAlphaAnimation(expandableView, properties);
+        } else {
+            abortAnimation(child, TAG_ANIMATOR_SHADOW_ALPHA);
+        }
+
+        // start top inset animation
+        if (this.clipTopAmount != expandableView.getClipTopAmount()) {
+            startInsetAnimation(expandableView, properties);
+        } else {
+            abortAnimation(child, TAG_ANIMATOR_TOP_INSET);
+        }
+
+        // start dimmed animation
+        expandableView.setDimmed(this.dimmed, animationFilter.animateDimmed);
+
+        // apply below the speed bump
+        expandableView.setBelowSpeedBump(this.belowSpeedBump);
+
+        // start hiding sensitive animation
+        expandableView.setHideSensitive(this.hideSensitive, animationFilter.animateHideSensitive,
+                properties.delay, properties.duration);
+
+        // start dark animation
+        expandableView.setDark(this.dark, animationFilter.animateDark, properties.delay);
+
+        if (properties.wasAdded(child) && !hidden) {
+            expandableView.performAddAnimation(properties.delay, properties.duration,
+                    false /* isHeadsUpAppear */);
+        }
+
+        if (!expandableView.isInShelf() && this.inShelf) {
+            expandableView.setTransformingInShelf(true);
+        }
+        expandableView.setInShelf(this.inShelf);
+
+        if (headsUpIsVisible) {
+            expandableView.setHeadsUpIsVisible();
+        }
+    }
+
+    private void startHeightAnimation(final ExpandableView child, AnimationProperties properties) {
+        Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
+        Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
+        int newEndValue = this.height;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
+            return;
+        }
+        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT);
+        AnimationFilter filter = properties.getAnimationFilter();
+        if (!filter.animateHeight) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                int relativeDiff = newEndValue - previousEndValue;
+                int newStartValue = previousStartValue + relativeDiff;
+                values[0].setIntValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_HEIGHT, newStartValue);
+                child.setTag(TAG_END_HEIGHT, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setActualHeight(newEndValue, false);
+                return;
+            }
+        }
+
+        ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                child.setActualHeight((int) animation.getAnimatedValue(),
+                        false /* notifyListeners */);
+            }
+        });
+        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
+        animator.setDuration(newDuration);
+        if (properties.delay > 0 && (previousAnimator == null
+                || previousAnimator.getAnimatedFraction() == 0)) {
+            animator.setStartDelay(properties.delay);
+        }
+        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+        if (listener != null) {
+            animator.addListener(listener);
+        }
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            boolean mWasCancelled;
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setTag(TAG_ANIMATOR_HEIGHT, null);
+                child.setTag(TAG_START_HEIGHT, null);
+                child.setTag(TAG_END_HEIGHT, null);
+                child.setActualHeightAnimating(false);
+                if (!mWasCancelled && child instanceof ExpandableNotificationRow) {
+                    ((ExpandableNotificationRow) child).setGroupExpansionChanging(
+                            false /* isExpansionChanging */);
+                }
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mWasCancelled = false;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mWasCancelled = true;
+            }
+        });
+        startAnimator(animator, listener);
+        child.setTag(TAG_ANIMATOR_HEIGHT, animator);
+        child.setTag(TAG_START_HEIGHT, child.getActualHeight());
+        child.setTag(TAG_END_HEIGHT, newEndValue);
+        child.setActualHeightAnimating(true);
+    }
+
+    private void startShadowAlphaAnimation(final ExpandableView child,
+            AnimationProperties properties) {
+        Float previousStartValue = getChildTag(child, TAG_START_SHADOW_ALPHA);
+        Float previousEndValue = getChildTag(child, TAG_END_SHADOW_ALPHA);
+        float newEndValue = this.shadowAlpha;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
+            return;
+        }
+        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SHADOW_ALPHA);
+        AnimationFilter filter = properties.getAnimationFilter();
+        if (!filter.animateShadowAlpha) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                float relativeDiff = newEndValue - previousEndValue;
+                float newStartValue = previousStartValue + relativeDiff;
+                values[0].setFloatValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_SHADOW_ALPHA, newStartValue);
+                child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setShadowAlpha(newEndValue);
+                return;
+            }
+        }
+
+        ValueAnimator animator = ValueAnimator.ofFloat(child.getShadowAlpha(), newEndValue);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                child.setShadowAlpha((float) animation.getAnimatedValue());
+            }
+        });
+        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
+        animator.setDuration(newDuration);
+        if (properties.delay > 0 && (previousAnimator == null
+                || previousAnimator.getAnimatedFraction() == 0)) {
+            animator.setStartDelay(properties.delay);
+        }
+        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+        if (listener != null) {
+            animator.addListener(listener);
+        }
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, null);
+                child.setTag(TAG_START_SHADOW_ALPHA, null);
+                child.setTag(TAG_END_SHADOW_ALPHA, null);
+            }
+        });
+        startAnimator(animator, listener);
+        child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, animator);
+        child.setTag(TAG_START_SHADOW_ALPHA, child.getShadowAlpha());
+        child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
+    }
+
+    private void startInsetAnimation(final ExpandableView child, AnimationProperties properties) {
+        Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET);
+        Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET);
+        int newEndValue = this.clipTopAmount;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
+            return;
+        }
+        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET);
+        AnimationFilter filter = properties.getAnimationFilter();
+        if (!filter.animateTopInset) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                int relativeDiff = newEndValue - previousEndValue;
+                int newStartValue = previousStartValue + relativeDiff;
+                values[0].setIntValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_TOP_INSET, newStartValue);
+                child.setTag(TAG_END_TOP_INSET, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setClipTopAmount(newEndValue);
+                return;
+            }
+        }
+
+        ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                child.setClipTopAmount((int) animation.getAnimatedValue());
+            }
+        });
+        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
+        animator.setDuration(newDuration);
+        if (properties.delay > 0 && (previousAnimator == null
+                || previousAnimator.getAnimatedFraction() == 0)) {
+            animator.setStartDelay(properties.delay);
+        }
+        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+        if (listener != null) {
+            animator.addListener(listener);
+        }
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setTag(TAG_ANIMATOR_TOP_INSET, null);
+                child.setTag(TAG_START_TOP_INSET, null);
+                child.setTag(TAG_END_TOP_INSET, null);
+            }
+        });
+        startAnimator(animator, listener);
+        child.setTag(TAG_ANIMATOR_TOP_INSET, animator);
+        child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount());
+        child.setTag(TAG_END_TOP_INSET, newEndValue);
+    }
+
+    /**
+     * Get the end value of the height animation running on a view or the actualHeight
+     * if no animation is running.
+     */
+    public static int getFinalActualHeight(ExpandableView view) {
+        if (view == null) {
+            return 0;
+        }
+        ValueAnimator heightAnimator = getChildTag(view, TAG_ANIMATOR_HEIGHT);
+        if (heightAnimator == null) {
+            return view.getActualHeight();
+        } else {
+            return getChildTag(view, TAG_END_HEIGHT);
+        }
+    }
+
+    @Override
+    public void cancelAnimations(View view) {
+        super.cancelAnimations(view);
+        Animator animator = getChildTag(view, TAG_ANIMATOR_HEIGHT);
+        if (animator != null) {
+            animator.cancel();
+        }
+        animator = getChildTag(view, TAG_ANIMATOR_SHADOW_ALPHA);
+        if (animator != null) {
+            animator.cancel();
+        }
+        animator = getChildTag(view, TAG_ANIMATOR_TOP_INSET);
+        if (animator != null) {
+            animator.cancel();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/HeadsUpAppearInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/HeadsUpAppearInterpolator.java
new file mode 100644
index 0000000..24e1f32
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/HeadsUpAppearInterpolator.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import android.graphics.Path;
+import android.view.animation.PathInterpolator;
+
+/**
+ * An interpolator specifically designed for the appear animation of heads up notifications.
+ */
+public class HeadsUpAppearInterpolator extends PathInterpolator {
+
+    private static float X1 = 250f;
+    private static float X2 = 200f;
+    private static float XTOT = (X1 + X2);;
+
+    public HeadsUpAppearInterpolator() {
+        super(getAppearPath());
+    }
+
+    private static Path getAppearPath() {
+        Path path = new Path();
+        path.moveTo(0, 0);
+        float y1 = 90f;
+        float y2 = 80f;
+        path.cubicTo(X1 * 0.8f / XTOT, y1 / y2,
+                X1 * 0.8f / XTOT, y1 / y2,
+                X1 / XTOT, y1 / y2);
+        path.cubicTo((X1 + X2 * 0.4f) / XTOT, y1 / y2,
+                (X1 + X2 * 0.2f) / XTOT, 1.0f,
+                1.0f , 1.0f);
+        return path;
+    }
+
+    public static float getFractionUntilOvershoot() {
+        return X1 / XTOT;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
new file mode 100644
index 0000000..3d44e3c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -0,0 +1,1325 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import android.app.Notification;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.drawable.ColorDrawable;
+import android.service.notification.StatusBarNotification;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.NotificationHeaderView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationHeaderUtil;
+import com.android.systemui.statusbar.notification.row.HybridGroupManager;
+import com.android.systemui.statusbar.notification.row.HybridNotificationView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A container containing child notifications
+ */
+public class NotificationChildrenContainer extends ViewGroup {
+
+    @VisibleForTesting
+    static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
+    @VisibleForTesting
+    static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
+    @VisibleForTesting
+    static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
+    @VisibleForTesting
+    static final int NUMBER_OF_CHILDREN_WHEN_AMBIENT = 1;
+    private static final AnimationProperties ALPHA_FADE_IN = new AnimationProperties() {
+        private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
+
+        @Override
+        public AnimationFilter getAnimationFilter() {
+            return mAnimationFilter;
+        }
+    }.setDuration(200);
+
+    private final List<View> mDividers = new ArrayList<>();
+    private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
+    private final HybridGroupManager mHybridGroupManager;
+    private int mChildPadding;
+    private int mDividerHeight;
+    private float mDividerAlpha;
+    private int mNotificationHeaderMargin;
+
+    private int mNotificatonTopPadding;
+    private float mCollapsedBottompadding;
+    private boolean mChildrenExpanded;
+    private ExpandableNotificationRow mContainingNotification;
+    private TextView mOverflowNumber;
+    private ViewState mGroupOverFlowState;
+    private int mRealHeight;
+    private boolean mUserLocked;
+    private int mActualHeight;
+    private boolean mNeverAppliedGroupState;
+    private int mHeaderHeight;
+
+    /**
+     * Whether or not individual notifications that are part of this container will have shadows.
+     */
+    private boolean mEnableShadowOnChildNotifications;
+
+    private NotificationHeaderView mNotificationHeader;
+    private NotificationViewWrapper mNotificationHeaderWrapper;
+    private NotificationHeaderView mNotificationHeaderLowPriority;
+    private NotificationViewWrapper mNotificationHeaderWrapperLowPriority;
+    private ViewGroup mNotificationHeaderAmbient;
+    private NotificationViewWrapper mNotificationHeaderWrapperAmbient;
+    private NotificationHeaderUtil mHeaderUtil;
+    private ViewState mHeaderViewState;
+    private int mClipBottomAmount;
+    private boolean mIsLowPriority;
+    private OnClickListener mHeaderClickListener;
+    private ViewGroup mCurrentHeader;
+
+    private boolean mShowDividersWhenExpanded;
+    private boolean mHideDividersDuringExpand;
+    private int mTranslationForHeader;
+    private int mCurrentHeaderTranslation = 0;
+    private float mHeaderVisibleAmount = 1.0f;
+
+    public NotificationChildrenContainer(Context context) {
+        this(context, null);
+    }
+
+    public NotificationChildrenContainer(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mHybridGroupManager = new HybridGroupManager(getContext(), this);
+        initDimens();
+        setClipChildren(false);
+    }
+
+    private void initDimens() {
+        Resources res = getResources();
+        mChildPadding = res.getDimensionPixelSize(R.dimen.notification_children_padding);
+        mDividerHeight = res.getDimensionPixelSize(
+                R.dimen.notification_children_container_divider_height);
+        mDividerAlpha = res.getFloat(R.dimen.notification_divider_alpha);
+        mNotificationHeaderMargin = res.getDimensionPixelSize(
+                R.dimen.notification_children_container_margin_top);
+        mNotificatonTopPadding = res.getDimensionPixelSize(
+                R.dimen.notification_children_container_top_padding);
+        mHeaderHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
+        mCollapsedBottompadding = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_content_margin);
+        mEnableShadowOnChildNotifications =
+                res.getBoolean(R.bool.config_enableShadowOnChildNotifications);
+        mShowDividersWhenExpanded =
+                res.getBoolean(R.bool.config_showDividersWhenGroupNotificationExpanded);
+        mHideDividersDuringExpand =
+                res.getBoolean(R.bool.config_hideDividersDuringExpand);
+        mTranslationForHeader = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_content_margin)
+                - mNotificationHeaderMargin;
+        mHybridGroupManager.initDimens();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+        for (int i = 0; i < childCount; i++) {
+            View child = mChildren.get(i);
+            // We need to layout all children even the GONE ones, such that the heights are
+            // calculated correctly as they are used to calculate how many we can fit on the screen
+            child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
+            mDividers.get(i).layout(0, 0, getWidth(), mDividerHeight);
+        }
+        if (mOverflowNumber != null) {
+            boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+            int left = (isRtl ? 0 : getWidth() - mOverflowNumber.getMeasuredWidth());
+            int right = left + mOverflowNumber.getMeasuredWidth();
+            mOverflowNumber.layout(left, 0, right, mOverflowNumber.getMeasuredHeight());
+        }
+        if (mNotificationHeader != null) {
+            mNotificationHeader.layout(0, 0, mNotificationHeader.getMeasuredWidth(),
+                    mNotificationHeader.getMeasuredHeight());
+        }
+        if (mNotificationHeaderLowPriority != null) {
+            mNotificationHeaderLowPriority.layout(0, 0,
+                    mNotificationHeaderLowPriority.getMeasuredWidth(),
+                    mNotificationHeaderLowPriority.getMeasuredHeight());
+        }
+        if (mNotificationHeaderAmbient != null) {
+            mNotificationHeaderAmbient.layout(0, 0,
+                    mNotificationHeaderAmbient.getMeasuredWidth(),
+                    mNotificationHeaderAmbient.getMeasuredHeight());
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
+        boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
+        int size = MeasureSpec.getSize(heightMeasureSpec);
+        int newHeightSpec = heightMeasureSpec;
+        if (hasFixedHeight || isHeightLimited) {
+            newHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
+        }
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        if (mOverflowNumber != null) {
+            mOverflowNumber.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+                    newHeightSpec);
+        }
+        int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
+        int height = mNotificationHeaderMargin + mNotificatonTopPadding;
+        int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+        int collapsedChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
+        int overflowIndex = childCount > collapsedChildren ? collapsedChildren - 1 : -1;
+        for (int i = 0; i < childCount; i++) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            // We need to measure all children even the GONE ones, such that the heights are
+            // calculated correctly as they are used to calculate how many we can fit on the screen.
+            boolean isOverflow = i == overflowIndex;
+            child.setSingleLineWidthIndention(isOverflow && mOverflowNumber != null &&
+                    !mContainingNotification.isShowingAmbient()
+                    ? mOverflowNumber.getMeasuredWidth() : 0);
+            child.measure(widthMeasureSpec, newHeightSpec);
+            // layout the divider
+            View divider = mDividers.get(i);
+            divider.measure(widthMeasureSpec, dividerHeightSpec);
+            if (child.getVisibility() != GONE) {
+                height += child.getMeasuredHeight() + mDividerHeight;
+            }
+        }
+        mRealHeight = height;
+        if (heightMode != MeasureSpec.UNSPECIFIED) {
+            height = Math.min(height, size);
+        }
+
+        int headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
+        if (mNotificationHeader != null) {
+            mNotificationHeader.measure(widthMeasureSpec, headerHeightSpec);
+        }
+        if (mNotificationHeaderLowPriority != null) {
+            headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
+            mNotificationHeaderLowPriority.measure(widthMeasureSpec, headerHeightSpec);
+        }
+        if (mNotificationHeaderAmbient != null) {
+            headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
+            mNotificationHeaderAmbient.measure(widthMeasureSpec, headerHeightSpec);
+        }
+
+        setMeasuredDimension(width, height);
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    @Override
+    public boolean pointInView(float localX, float localY, float slop) {
+        return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
+                localY < (mRealHeight + slop);
+    }
+
+    /**
+     * Add a child notification to this view.
+     *
+     * @param row the row to add
+     * @param childIndex the index to add it at, if -1 it will be added at the end
+     */
+    public void addNotification(ExpandableNotificationRow row, int childIndex) {
+        int newIndex = childIndex < 0 ? mChildren.size() : childIndex;
+        mChildren.add(newIndex, row);
+        addView(row);
+        row.setUserLocked(mUserLocked);
+
+        View divider = inflateDivider();
+        addView(divider);
+        mDividers.add(newIndex, divider);
+
+        updateGroupOverflow();
+        row.setContentTransformationAmount(0, false /* isLastChild */);
+        // It doesn't make sense to keep old animations around, lets cancel them!
+        ExpandableNotificationRow.NotificationViewState viewState = row.getViewState();
+        if (viewState != null) {
+            viewState.cancelAnimations(row);
+            row.cancelAppearDrawing();
+        }
+    }
+
+    public void removeNotification(ExpandableNotificationRow row) {
+        int childIndex = mChildren.indexOf(row);
+        mChildren.remove(row);
+        removeView(row);
+
+        final View divider = mDividers.remove(childIndex);
+        removeView(divider);
+        getOverlay().add(divider);
+        CrossFadeHelper.fadeOut(divider, new Runnable() {
+            @Override
+            public void run() {
+                getOverlay().remove(divider);
+            }
+        });
+
+        row.setSystemChildExpanded(false);
+        row.setUserLocked(false);
+        updateGroupOverflow();
+        if (!row.isRemoved()) {
+            mHeaderUtil.restoreNotificationHeader(row);
+        }
+    }
+
+    /**
+     * @return The number of notification children in the container.
+     */
+    public int getNotificationChildCount() {
+        return mChildren.size();
+    }
+
+    public void recreateNotificationHeader(OnClickListener listener) {
+        mHeaderClickListener = listener;
+        StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
+        final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
+                notification.getNotification());
+        RemoteViews header = builder.makeNotificationHeader(false /* ambient */);
+        if (mNotificationHeader == null) {
+            mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
+            final View expandButton = mNotificationHeader.findViewById(
+                    com.android.internal.R.id.expand_button);
+            expandButton.setVisibility(VISIBLE);
+            mNotificationHeader.setOnClickListener(mHeaderClickListener);
+            mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
+                    mNotificationHeader, mContainingNotification);
+            addView(mNotificationHeader, 0);
+            invalidate();
+        } else {
+            header.reapply(getContext(), mNotificationHeader);
+        }
+        mNotificationHeaderWrapper.onContentUpdated(mContainingNotification);
+        recreateLowPriorityHeader(builder);
+        recreateAmbientHeader(builder);
+        updateHeaderVisibility(false /* animate */);
+        updateChildrenHeaderAppearance();
+    }
+
+    private void recreateAmbientHeader(Notification.Builder builder) {
+        RemoteViews header;
+        StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
+        if (builder == null) {
+            builder = Notification.Builder.recoverBuilder(getContext(),
+                    notification.getNotification());
+        }
+        header = builder.makeNotificationHeader(true /* ambient */);
+        if (mNotificationHeaderAmbient == null) {
+            mNotificationHeaderAmbient = (ViewGroup) header.apply(getContext(), this);
+            mNotificationHeaderWrapperAmbient = NotificationViewWrapper.wrap(getContext(),
+                    mNotificationHeaderAmbient, mContainingNotification);
+            mNotificationHeaderWrapperAmbient.onContentUpdated(mContainingNotification);
+            addView(mNotificationHeaderAmbient, 0);
+            invalidate();
+        } else {
+            header.reapply(getContext(), mNotificationHeaderAmbient);
+        }
+        resetHeaderVisibilityIfNeeded(mNotificationHeaderAmbient, calculateDesiredHeader());
+        mNotificationHeaderWrapperAmbient.onContentUpdated(mContainingNotification);
+    }
+
+    /**
+     * Recreate the low-priority header.
+     *
+     * @param builder a builder to reuse. Otherwise the builder will be recovered.
+     */
+    private void recreateLowPriorityHeader(Notification.Builder builder) {
+        RemoteViews header;
+        StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
+        if (mIsLowPriority) {
+            if (builder == null) {
+                builder = Notification.Builder.recoverBuilder(getContext(),
+                        notification.getNotification());
+            }
+            header = builder.makeLowPriorityContentView(true /* useRegularSubtext */);
+            if (mNotificationHeaderLowPriority == null) {
+                mNotificationHeaderLowPriority = (NotificationHeaderView) header.apply(getContext(),
+                        this);
+                final View expandButton = mNotificationHeaderLowPriority.findViewById(
+                        com.android.internal.R.id.expand_button);
+                expandButton.setVisibility(VISIBLE);
+                mNotificationHeaderLowPriority.setOnClickListener(mHeaderClickListener);
+                mNotificationHeaderWrapperLowPriority = NotificationViewWrapper.wrap(getContext(),
+                        mNotificationHeaderLowPriority, mContainingNotification);
+                addView(mNotificationHeaderLowPriority, 0);
+                invalidate();
+            } else {
+                header.reapply(getContext(), mNotificationHeaderLowPriority);
+            }
+            mNotificationHeaderWrapperLowPriority.onContentUpdated(mContainingNotification);
+            resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, calculateDesiredHeader());
+        } else {
+            removeView(mNotificationHeaderLowPriority);
+            mNotificationHeaderLowPriority = null;
+            mNotificationHeaderWrapperLowPriority = null;
+        }
+    }
+
+    public void updateChildrenHeaderAppearance() {
+        mHeaderUtil.updateChildrenHeaderAppearance();
+    }
+
+    public void updateGroupOverflow() {
+        int childCount = mChildren.size();
+        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
+        if (childCount > maxAllowedVisibleChildren) {
+            int number = childCount - maxAllowedVisibleChildren;
+            mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number);
+            if (mContainingNotification.isShowingAmbient()) {
+                ExpandableNotificationRow overflowView = mChildren.get(0);
+                HybridNotificationView ambientSingleLineView = overflowView == null ? null
+                        : overflowView.getAmbientSingleLineView();
+                if (ambientSingleLineView != null) {
+                    mHybridGroupManager.bindOverflowNumberAmbient(
+                            ambientSingleLineView.getTitleView(),
+                            mContainingNotification.getStatusBarNotification().getNotification(),
+                            number);
+                }
+            }
+            if (mGroupOverFlowState == null) {
+                mGroupOverFlowState = new ViewState();
+                mNeverAppliedGroupState = true;
+            }
+        } else if (mOverflowNumber != null) {
+            removeView(mOverflowNumber);
+            if (isShown() && isAttachedToWindow()) {
+                final View removedOverflowNumber = mOverflowNumber;
+                addTransientView(removedOverflowNumber, getTransientViewCount());
+                CrossFadeHelper.fadeOut(removedOverflowNumber, new Runnable() {
+                    @Override
+                    public void run() {
+                        removeTransientView(removedOverflowNumber);
+                    }
+                });
+            }
+            mOverflowNumber = null;
+            mGroupOverFlowState = null;
+        }
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateGroupOverflow();
+    }
+
+    private View inflateDivider() {
+        return LayoutInflater.from(mContext).inflate(
+                R.layout.notification_children_divider, this, false);
+    }
+
+    public List<ExpandableNotificationRow> getNotificationChildren() {
+        return mChildren;
+    }
+
+    /**
+     * Apply the order given in the list to the children.
+     *
+     * @param childOrder the new list order
+     * @param visualStabilityManager
+     * @param callback
+     * @return whether the list order has changed
+     */
+    public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
+            VisualStabilityManager visualStabilityManager,
+            VisualStabilityManager.Callback callback) {
+        if (childOrder == null) {
+            return false;
+        }
+        boolean result = false;
+        for (int i = 0; i < mChildren.size() && i < childOrder.size(); i++) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableNotificationRow desiredChild = childOrder.get(i);
+            if (child != desiredChild) {
+                if (visualStabilityManager.canReorderNotification(desiredChild)) {
+                    mChildren.remove(desiredChild);
+                    mChildren.add(i, desiredChild);
+                    result = true;
+                } else {
+                    visualStabilityManager.addReorderingAllowedCallback(callback);
+                }
+            }
+        }
+        updateExpansionStates();
+        return result;
+    }
+
+    private void updateExpansionStates() {
+        if (mChildrenExpanded || mUserLocked) {
+            // we don't modify it the group is expanded or if we are expanding it
+            return;
+        }
+        int size = mChildren.size();
+        for (int i = 0; i < size; i++) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            child.setSystemChildExpanded(i == 0 && size == 1);
+        }
+    }
+
+    /**
+     *
+     * @return the intrinsic size of this children container, i.e the natural fully expanded state
+     */
+    public int getIntrinsicHeight() {
+        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
+        return getIntrinsicHeight(maxAllowedVisibleChildren);
+    }
+
+    /**
+     * @return the intrinsic height with a number of children given
+     *         in @param maxAllowedVisibleChildren
+     */
+    private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
+        if (showingAsLowPriority()) {
+            return mNotificationHeaderLowPriority.getHeight();
+        }
+        int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
+        int visibleChildren = 0;
+        int childCount = mChildren.size();
+        boolean firstChild = true;
+        float expandFactor = 0;
+        if (mUserLocked) {
+            expandFactor = getGroupExpandFraction();
+        }
+        boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isShowingAmbient();
+        for (int i = 0; i < childCount; i++) {
+            if (visibleChildren >= maxAllowedVisibleChildren) {
+                break;
+            }
+            if (!firstChild) {
+                if (mUserLocked) {
+                    intrinsicHeight += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
+                            expandFactor);
+                } else {
+                    intrinsicHeight += childrenExpanded ? mDividerHeight : mChildPadding;
+                }
+            } else {
+                if (mUserLocked) {
+                    intrinsicHeight += NotificationUtils.interpolate(
+                            0,
+                            mNotificatonTopPadding + mDividerHeight,
+                            expandFactor);
+                } else {
+                    intrinsicHeight += childrenExpanded
+                            ? mNotificatonTopPadding + mDividerHeight
+                            : 0;
+                }
+                firstChild = false;
+            }
+            ExpandableNotificationRow child = mChildren.get(i);
+            intrinsicHeight += child.getIntrinsicHeight();
+            visibleChildren++;
+        }
+        if (mUserLocked) {
+            intrinsicHeight += NotificationUtils.interpolate(mCollapsedBottompadding, 0.0f,
+                    expandFactor);
+        } else if (!childrenExpanded) {
+            intrinsicHeight += mCollapsedBottompadding;
+        }
+        return intrinsicHeight;
+    }
+
+    /**
+     * Update the state of all its children based on a linear layout algorithm.
+     *  @param resultState the state to update
+     * @param parentState the state of the parent
+     * @param ambientState
+     */
+    public void getState(StackScrollState resultState, ExpandableViewState parentState,
+            AmbientState ambientState) {
+        int childCount = mChildren.size();
+        int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation;
+        boolean firstChild = true;
+        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
+        int lastVisibleIndex = maxAllowedVisibleChildren - 1;
+        int firstOverflowIndex = lastVisibleIndex + 1;
+        float expandFactor = 0;
+        boolean expandingToExpandedGroup = mUserLocked && !showingAsLowPriority();
+        if (mUserLocked) {
+            expandFactor = getGroupExpandFraction();
+            firstOverflowIndex = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
+        }
+
+        boolean childrenExpandedAndNotAnimating = mChildrenExpanded
+                && !mContainingNotification.isGroupExpansionChanging();
+        int launchTransitionCompensation = 0;
+        for (int i = 0; i < childCount; i++) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            if (!firstChild) {
+                if (expandingToExpandedGroup) {
+                    yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
+                            expandFactor);
+                } else {
+                    yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
+                }
+            } else {
+                if (expandingToExpandedGroup) {
+                    yPosition += NotificationUtils.interpolate(
+                            0,
+                            mNotificatonTopPadding + mDividerHeight,
+                            expandFactor);
+                } else {
+                    yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
+                }
+                firstChild = false;
+            }
+
+            ExpandableViewState childState = resultState.getViewStateForView(child);
+            int intrinsicHeight = child.getIntrinsicHeight();
+            childState.height = intrinsicHeight;
+            childState.yTranslation = yPosition + launchTransitionCompensation;
+            childState.hidden = false;
+            // When the group is expanded, the children cast the shadows rather than the parent
+            // so use the parent's elevation here.
+            childState.zTranslation =
+                    (childrenExpandedAndNotAnimating && mEnableShadowOnChildNotifications)
+                    ? parentState.zTranslation
+                    : 0;
+            childState.dimmed = parentState.dimmed;
+            childState.dark = parentState.dark;
+            childState.hideSensitive = parentState.hideSensitive;
+            childState.belowSpeedBump = parentState.belowSpeedBump;
+            childState.clipTopAmount = 0;
+            childState.alpha = 0;
+            if (i < firstOverflowIndex) {
+                childState.alpha = showingAsLowPriority() ? expandFactor : 1.0f;
+            } else if (expandFactor == 1.0f && i <= lastVisibleIndex) {
+                childState.alpha = (mActualHeight - childState.yTranslation) / childState.height;
+                childState.alpha = Math.max(0.0f, Math.min(1.0f, childState.alpha));
+            }
+            childState.location = parentState.location;
+            childState.inShelf = parentState.inShelf;
+            yPosition += intrinsicHeight;
+            if (child.isExpandAnimationRunning()) {
+                launchTransitionCompensation = -ambientState.getExpandAnimationTopChange();
+            }
+
+        }
+        if (mOverflowNumber != null) {
+            ExpandableNotificationRow overflowView = mChildren.get(Math.min(
+                    getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1);
+            mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
+
+            if (mContainingNotification.isShowingAmbient()) {
+                mGroupOverFlowState.alpha = 0.0f;
+            } else if (!mChildrenExpanded) {
+                HybridNotificationView alignView = overflowView.getSingleLineView();
+                if (alignView != null) {
+                    View mirrorView = alignView.getTextView();
+                    if (mirrorView.getVisibility() == GONE) {
+                        mirrorView = alignView.getTitleView();
+                    }
+                    if (mirrorView.getVisibility() == GONE) {
+                        mirrorView = alignView;
+                    }
+                    mGroupOverFlowState.alpha = mirrorView.getAlpha();
+                    mGroupOverFlowState.yTranslation += NotificationUtils.getRelativeYOffset(
+                            mirrorView, overflowView);
+                }
+            } else {
+                mGroupOverFlowState.yTranslation += mNotificationHeaderMargin;
+                mGroupOverFlowState.alpha = 0.0f;
+            }
+        }
+        if (mNotificationHeader != null) {
+            if (mHeaderViewState == null) {
+                mHeaderViewState = new ViewState();
+            }
+            mHeaderViewState.initFrom(mNotificationHeader);
+            mHeaderViewState.zTranslation = childrenExpandedAndNotAnimating
+                    ? parentState.zTranslation
+                    : 0;
+            mHeaderViewState.yTranslation = mCurrentHeaderTranslation;
+            mHeaderViewState.alpha = mHeaderVisibleAmount;
+            // The hiding is done automatically by the alpha, otherwise we'll pick it up again
+            // in the next frame with the initFrom call above and have an invisible header
+            mHeaderViewState.hidden = false;
+        }
+    }
+
+    /**
+     * When moving into the bottom stack, the bottom visible child in an expanded group adjusts its
+     * height, children in the group after this are gone.
+     *
+     * @param child the child who's height to adjust.
+     * @param parentHeight the height of the parent.
+     * @param childState the state to update.
+     * @param yPosition the yPosition of the view.
+     * @return true if children after this one should be hidden.
+     */
+    private boolean updateChildStateForExpandedGroup(ExpandableNotificationRow child,
+            int parentHeight, ExpandableViewState childState, int yPosition) {
+        final int top = yPosition + child.getClipTopAmount();
+        final int intrinsicHeight = child.getIntrinsicHeight();
+        final int bottom = top + intrinsicHeight;
+        int newHeight = intrinsicHeight;
+        if (bottom >= parentHeight) {
+            // Child is either clipped or gone
+            newHeight = Math.max((parentHeight - top), 0);
+        }
+        childState.hidden = newHeight == 0;
+        childState.height = newHeight;
+        return childState.height != intrinsicHeight && !childState.hidden;
+    }
+
+    @VisibleForTesting
+    int getMaxAllowedVisibleChildren() {
+        return getMaxAllowedVisibleChildren(false /* likeCollapsed */);
+    }
+
+    @VisibleForTesting
+    int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
+        if (mContainingNotification.isShowingAmbient()) {
+            return NUMBER_OF_CHILDREN_WHEN_AMBIENT;
+        }
+        if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())
+                && !showingAsLowPriority()) {
+            return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
+        }
+        if (mIsLowPriority || !mContainingNotification.isOnKeyguard()
+                && (mContainingNotification.isExpanded() || mContainingNotification.isHeadsUp())) {
+            return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
+        }
+        return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
+    }
+
+    public void applyState(StackScrollState state) {
+        int childCount = mChildren.size();
+        ViewState tmpState = new ViewState();
+        float expandFraction = 0.0f;
+        if (mUserLocked) {
+            expandFraction = getGroupExpandFraction();
+        }
+        final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
+                || (mChildrenExpanded && mShowDividersWhenExpanded)
+                || (mContainingNotification.isGroupExpansionChanging()
+                && !mHideDividersDuringExpand);
+        for (int i = 0; i < childCount; i++) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableViewState viewState = state.getViewStateForView(child);
+            viewState.applyToView(child);
+
+            // layout the divider
+            View divider = mDividers.get(i);
+            tmpState.initFrom(divider);
+            tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
+            float alpha = mChildrenExpanded && viewState.alpha != 0 ? mDividerAlpha : 0;
+            if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
+                alpha = NotificationUtils.interpolate(0, 0.5f,
+                        Math.min(viewState.alpha, expandFraction));
+            }
+            tmpState.hidden = !dividersVisible;
+            tmpState.alpha = alpha;
+            tmpState.applyToView(divider);
+            // There is no fake shadow to be drawn on the children
+            child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
+        }
+        if (mGroupOverFlowState != null) {
+            mGroupOverFlowState.applyToView(mOverflowNumber);
+            mNeverAppliedGroupState = false;
+        }
+        if (mHeaderViewState != null) {
+            mHeaderViewState.applyToView(mNotificationHeader);
+        }
+        updateChildrenClipping();
+    }
+
+    private void updateChildrenClipping() {
+        if (mContainingNotification.hasExpandingChild()) {
+            return;
+        }
+        int childCount = mChildren.size();
+        int layoutEnd = mContainingNotification.getActualHeight() - mClipBottomAmount;
+        for (int i = 0; i < childCount; i++) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            if (child.getVisibility() == GONE) {
+                continue;
+            }
+            float childTop = child.getTranslationY();
+            float childBottom = childTop + child.getActualHeight();
+            boolean visible = true;
+            int clipBottomAmount = 0;
+            if (childTop > layoutEnd) {
+                visible = false;
+            } else if (childBottom > layoutEnd) {
+                clipBottomAmount = (int) (childBottom - layoutEnd);
+            }
+
+            boolean isVisible = child.getVisibility() == VISIBLE;
+            if (visible != isVisible) {
+                child.setVisibility(visible ? VISIBLE : INVISIBLE);
+            }
+
+            child.setClipBottomAmount(clipBottomAmount);
+        }
+    }
+
+    /**
+     * This is called when the children expansion has changed and positions the children properly
+     * for an appear animation.
+     *
+     * @param state the new state we animate to
+     */
+    public void prepareExpansionChanged(StackScrollState state) {
+        // TODO: do something that makes sense, like placing the invisible views correctly
+        return;
+    }
+
+    public void startAnimationToState(StackScrollState state, AnimationProperties properties) {
+        int childCount = mChildren.size();
+        ViewState tmpState = new ViewState();
+        float expandFraction = getGroupExpandFraction();
+        final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
+                || (mChildrenExpanded && mShowDividersWhenExpanded)
+                || (mContainingNotification.isGroupExpansionChanging()
+                && !mHideDividersDuringExpand);
+        for (int i = childCount - 1; i >= 0; i--) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            ExpandableViewState viewState = state.getViewStateForView(child);
+            viewState.animateTo(child, properties);
+
+            // layout the divider
+            View divider = mDividers.get(i);
+            tmpState.initFrom(divider);
+            tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
+            float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+            if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
+                alpha = NotificationUtils.interpolate(0, 0.5f,
+                        Math.min(viewState.alpha, expandFraction));
+            }
+            tmpState.hidden = !dividersVisible;
+            tmpState.alpha = alpha;
+            tmpState.animateTo(divider, properties);
+            // There is no fake shadow to be drawn on the children
+            child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
+        }
+        if (mOverflowNumber != null) {
+            if (mNeverAppliedGroupState) {
+                float alpha = mGroupOverFlowState.alpha;
+                mGroupOverFlowState.alpha = 0;
+                mGroupOverFlowState.applyToView(mOverflowNumber);
+                mGroupOverFlowState.alpha = alpha;
+                mNeverAppliedGroupState = false;
+            }
+            mGroupOverFlowState.animateTo(mOverflowNumber, properties);
+        }
+        if (mNotificationHeader != null) {
+            mHeaderViewState.applyToView(mNotificationHeader);
+        }
+        updateChildrenClipping();
+    }
+
+    public ExpandableNotificationRow getViewAtPosition(float y) {
+        // find the view under the pointer, accounting for GONE views
+        final int count = mChildren.size();
+        for (int childIdx = 0; childIdx < count; childIdx++) {
+            ExpandableNotificationRow slidingChild = mChildren.get(childIdx);
+            float childTop = slidingChild.getTranslationY();
+            float top = childTop + slidingChild.getClipTopAmount();
+            float bottom = childTop + slidingChild.getActualHeight();
+            if (y >= top && y <= bottom) {
+                return slidingChild;
+            }
+        }
+        return null;
+    }
+
+    public void setChildrenExpanded(boolean childrenExpanded) {
+        mChildrenExpanded = childrenExpanded;
+        updateExpansionStates();
+        if (mNotificationHeader != null) {
+            mNotificationHeader.setExpanded(childrenExpanded);
+        }
+        final int count = mChildren.size();
+        for (int childIdx = 0; childIdx < count; childIdx++) {
+            ExpandableNotificationRow child = mChildren.get(childIdx);
+            child.setChildrenExpanded(childrenExpanded, false);
+        }
+    }
+
+    public void setContainingNotification(ExpandableNotificationRow parent) {
+        mContainingNotification = parent;
+        mHeaderUtil = new NotificationHeaderUtil(mContainingNotification);
+    }
+
+    public ExpandableNotificationRow getContainingNotification() {
+        return mContainingNotification;
+    }
+
+    public NotificationHeaderView getHeaderView() {
+        return mNotificationHeader;
+    }
+
+    public NotificationHeaderView getLowPriorityHeaderView() {
+        return mNotificationHeaderLowPriority;
+    }
+
+    @VisibleForTesting
+    public ViewGroup getCurrentHeaderView() {
+        return mCurrentHeader;
+    }
+
+    public void notifyShowAmbientChanged() {
+        updateHeaderVisibility(false);
+        updateGroupOverflow();
+    }
+
+    private void updateHeaderVisibility(boolean animate) {
+        ViewGroup desiredHeader;
+        ViewGroup currentHeader = mCurrentHeader;
+        desiredHeader = calculateDesiredHeader();
+
+        if (currentHeader == desiredHeader) {
+            return;
+        }
+        if (desiredHeader == mNotificationHeaderAmbient
+                || currentHeader == mNotificationHeaderAmbient) {
+            animate = false;
+        }
+
+        if (animate) {
+            if (desiredHeader != null && currentHeader != null) {
+                currentHeader.setVisibility(VISIBLE);
+                desiredHeader.setVisibility(VISIBLE);
+                NotificationViewWrapper visibleWrapper = getWrapperForView(desiredHeader);
+                NotificationViewWrapper hiddenWrapper = getWrapperForView(currentHeader);
+                visibleWrapper.transformFrom(hiddenWrapper);
+                hiddenWrapper.transformTo(visibleWrapper, () -> updateHeaderVisibility(false));
+                startChildAlphaAnimations(desiredHeader == mNotificationHeader);
+            } else {
+                animate = false;
+            }
+        }
+        if (!animate) {
+            if (desiredHeader != null) {
+                getWrapperForView(desiredHeader).setVisible(true);
+                desiredHeader.setVisibility(VISIBLE);
+            }
+            if (currentHeader != null) {
+                // Wrapper can be null if we were a low priority notification
+                // and just destroyed it by calling setIsLowPriority(false)
+                NotificationViewWrapper wrapper = getWrapperForView(currentHeader);
+                if (wrapper != null) {
+                    wrapper.setVisible(false);
+                }
+                currentHeader.setVisibility(INVISIBLE);
+            }
+        }
+
+        resetHeaderVisibilityIfNeeded(mNotificationHeader, desiredHeader);
+        resetHeaderVisibilityIfNeeded(mNotificationHeaderAmbient, desiredHeader);
+        resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, desiredHeader);
+
+        mCurrentHeader = desiredHeader;
+    }
+
+    private void resetHeaderVisibilityIfNeeded(View header, View desiredHeader) {
+        if (header == null) {
+            return;
+        }
+        if (header != mCurrentHeader && header != desiredHeader) {
+            getWrapperForView(header).setVisible(false);
+            header.setVisibility(INVISIBLE);
+        }
+        if (header == desiredHeader && header.getVisibility() != VISIBLE) {
+            getWrapperForView(header).setVisible(true);
+            header.setVisibility(VISIBLE);
+        }
+    }
+
+    private ViewGroup calculateDesiredHeader() {
+        ViewGroup desiredHeader;
+        if (mContainingNotification.isShowingAmbient()) {
+            desiredHeader = mNotificationHeaderAmbient;
+        } else if (showingAsLowPriority()) {
+            desiredHeader = mNotificationHeaderLowPriority;
+        } else {
+            desiredHeader = mNotificationHeader;
+        }
+        return desiredHeader;
+    }
+
+    private void startChildAlphaAnimations(boolean toVisible) {
+        float target = toVisible ? 1.0f : 0.0f;
+        float start = 1.0f - target;
+        int childCount = mChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            if (i >= NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED) {
+                break;
+            }
+            ExpandableNotificationRow child = mChildren.get(i);
+            child.setAlpha(start);
+            ViewState viewState = new ViewState();
+            viewState.initFrom(child);
+            viewState.alpha = target;
+            ALPHA_FADE_IN.setDelay(i * 50);
+            viewState.animateTo(child, ALPHA_FADE_IN);
+        }
+    }
+
+
+    private void updateHeaderTransformation() {
+        if (mUserLocked && showingAsLowPriority()) {
+            float fraction = getGroupExpandFraction();
+            mNotificationHeaderWrapper.transformFrom(mNotificationHeaderWrapperLowPriority,
+                    fraction);
+            mNotificationHeader.setVisibility(VISIBLE);
+            mNotificationHeaderWrapperLowPriority.transformTo(mNotificationHeaderWrapper,
+                    fraction);
+        }
+
+    }
+
+    private NotificationViewWrapper getWrapperForView(View visibleHeader) {
+        if (visibleHeader == mNotificationHeader) {
+            return mNotificationHeaderWrapper;
+        }
+        if (visibleHeader == mNotificationHeaderAmbient) {
+            return mNotificationHeaderWrapperAmbient;
+        }
+        return mNotificationHeaderWrapperLowPriority;
+    }
+
+    /**
+     * Called when a groups expansion changes to adjust the background of the header view.
+     *
+     * @param expanded whether the group is expanded.
+     */
+    public void updateHeaderForExpansion(boolean expanded) {
+        if (mNotificationHeader != null) {
+            if (expanded) {
+                ColorDrawable cd = new ColorDrawable();
+                cd.setColor(mContainingNotification.calculateBgColor());
+                mNotificationHeader.setHeaderBackgroundDrawable(cd);
+            } else {
+                mNotificationHeader.setHeaderBackgroundDrawable(null);
+            }
+        }
+    }
+
+    public int getMaxContentHeight() {
+        if (showingAsLowPriority()) {
+            return getMinHeight(NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED, true
+                    /* likeHighPriority */);
+        }
+        int maxContentHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
+                + mNotificatonTopPadding;
+        int visibleChildren = 0;
+        int childCount = mChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
+                break;
+            }
+            ExpandableNotificationRow child = mChildren.get(i);
+            float childHeight = child.isExpanded(true /* allowOnKeyguard */)
+                    ? child.getMaxExpandHeight()
+                    : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
+            maxContentHeight += childHeight;
+            visibleChildren++;
+        }
+        if (visibleChildren > 0) {
+            maxContentHeight += visibleChildren * mDividerHeight;
+        }
+        return maxContentHeight;
+    }
+
+    public void setActualHeight(int actualHeight) {
+        if (!mUserLocked) {
+            return;
+        }
+        mActualHeight = actualHeight;
+        float fraction = getGroupExpandFraction();
+        boolean showingLowPriority = showingAsLowPriority();
+        updateHeaderTransformation();
+        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
+        int childCount = mChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            float childHeight;
+            if (showingLowPriority) {
+                childHeight = child.getShowingLayout().getMinHeight(false /* likeGroupExpanded */);
+            } else if (child.isExpanded(true /* allowOnKeyguard */)) {
+                childHeight = child.getMaxExpandHeight();
+            } else {
+                childHeight = child.getShowingLayout().getMinHeight(
+                        true /* likeGroupExpanded */);
+            }
+            if (i < maxAllowedVisibleChildren) {
+                float singleLineHeight = child.getShowingLayout().getMinHeight(
+                        false /* likeGroupExpanded */);
+                child.setActualHeight((int) NotificationUtils.interpolate(singleLineHeight,
+                        childHeight, fraction), false);
+            } else {
+                child.setActualHeight((int) childHeight, false);
+            }
+        }
+    }
+
+    public float getGroupExpandFraction() {
+        int visibleChildrenExpandedHeight = showingAsLowPriority() ? getMaxContentHeight()
+                : getVisibleChildrenExpandHeight();
+        int minExpandHeight = getCollapsedHeight();
+        float factor = (mActualHeight - minExpandHeight)
+                / (float) (visibleChildrenExpandedHeight - minExpandHeight);
+        return Math.max(0.0f, Math.min(1.0f, factor));
+    }
+
+    private int getVisibleChildrenExpandHeight() {
+        int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
+                + mNotificatonTopPadding + mDividerHeight;
+        int visibleChildren = 0;
+        int childCount = mChildren.size();
+        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
+        for (int i = 0; i < childCount; i++) {
+            if (visibleChildren >= maxAllowedVisibleChildren) {
+                break;
+            }
+            ExpandableNotificationRow child = mChildren.get(i);
+            float childHeight = child.isExpanded(true /* allowOnKeyguard */)
+                    ? child.getMaxExpandHeight()
+                    : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
+            intrinsicHeight += childHeight;
+            visibleChildren++;
+        }
+        return intrinsicHeight;
+    }
+
+    public int getMinHeight() {
+        return getMinHeight(mContainingNotification.isShowingAmbient()
+                ? NUMBER_OF_CHILDREN_WHEN_AMBIENT
+                : NUMBER_OF_CHILDREN_WHEN_COLLAPSED, false /* likeHighPriority */);
+    }
+
+    public int getCollapsedHeight() {
+        return getMinHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */),
+                false /* likeHighPriority */);
+    }
+
+    /**
+     * Get the minimum Height for this group.
+     *
+     * @param maxAllowedVisibleChildren the number of children that should be visible
+     * @param likeHighPriority if the height should be calculated as if it were not low priority
+     */
+    private int getMinHeight(int maxAllowedVisibleChildren, boolean likeHighPriority) {
+        if (!likeHighPriority && showingAsLowPriority()) {
+            return mNotificationHeaderLowPriority.getHeight();
+        }
+        int minExpandHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
+        int visibleChildren = 0;
+        boolean firstChild = true;
+        int childCount = mChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            if (visibleChildren >= maxAllowedVisibleChildren) {
+                break;
+            }
+            if (!firstChild) {
+                minExpandHeight += mChildPadding;
+            } else {
+                firstChild = false;
+            }
+            ExpandableNotificationRow child = mChildren.get(i);
+            minExpandHeight += child.getSingleLineView().getHeight();
+            visibleChildren++;
+        }
+        minExpandHeight += mCollapsedBottompadding;
+        return minExpandHeight;
+    }
+
+    public boolean showingAsLowPriority() {
+        return mIsLowPriority && !mContainingNotification.isExpanded();
+    }
+
+    public void setDark(boolean dark, boolean fade, long delay) {
+        if (mOverflowNumber != null) {
+            mHybridGroupManager.setOverflowNumberDark(mOverflowNumber, dark, fade, delay);
+        }
+    }
+
+    public void reInflateViews(OnClickListener listener, StatusBarNotification notification) {
+        if (mNotificationHeader != null) {
+            removeView(mNotificationHeader);
+            mNotificationHeader = null;
+        }
+        if (mNotificationHeaderLowPriority != null) {
+            removeView(mNotificationHeaderLowPriority);
+            mNotificationHeaderLowPriority = null;
+        }
+        if (mNotificationHeaderAmbient != null) {
+            removeView(mNotificationHeaderAmbient);
+            mNotificationHeaderAmbient = null;
+        }
+        recreateNotificationHeader(listener);
+        initDimens();
+        for (int i = 0; i < mDividers.size(); i++) {
+            View prevDivider = mDividers.get(i);
+            int index = indexOfChild(prevDivider);
+            removeView(prevDivider);
+            View divider = inflateDivider();
+            addView(divider, index);
+            mDividers.set(i, divider);
+        }
+        removeView(mOverflowNumber);
+        mOverflowNumber = null;
+        mGroupOverFlowState = null;
+        updateGroupOverflow();
+    }
+
+    public void setUserLocked(boolean userLocked) {
+        mUserLocked = userLocked;
+        if (!mUserLocked) {
+            updateHeaderVisibility(false /* animate */);
+        }
+        int childCount = mChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            child.setUserLocked(userLocked && !showingAsLowPriority());
+        }
+    }
+
+    public void onNotificationUpdated() {
+        mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
+                mContainingNotification.getNotificationColor(),
+                mContainingNotification.getNotificationColorAmbient());
+    }
+
+    public int getPositionInLinearLayout(View childInGroup) {
+        int position = mNotificationHeaderMargin + mCurrentHeaderTranslation
+                + mNotificatonTopPadding;
+
+        for (int i = 0; i < mChildren.size(); i++) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            boolean notGone = child.getVisibility() != View.GONE;
+            if (notGone) {
+                position += mDividerHeight;
+            }
+            if (child == childInGroup) {
+                return position;
+            }
+            if (notGone) {
+                position += child.getIntrinsicHeight();
+            }
+        }
+        return 0;
+    }
+
+    public void setIconsVisible(boolean iconsVisible) {
+        if (mNotificationHeaderWrapper != null) {
+            NotificationHeaderView header = mNotificationHeaderWrapper.getNotificationHeader();
+            if (header != null) {
+                header.getIcon().setForceHidden(!iconsVisible);
+            }
+        }
+        if (mNotificationHeaderWrapperLowPriority != null) {
+            NotificationHeaderView header
+                    = mNotificationHeaderWrapperLowPriority.getNotificationHeader();
+            if (header != null) {
+                header.getIcon().setForceHidden(!iconsVisible);
+            }
+        }
+    }
+
+    public void setClipBottomAmount(int clipBottomAmount) {
+        mClipBottomAmount = clipBottomAmount;
+        updateChildrenClipping();
+    }
+
+    public void setIsLowPriority(boolean isLowPriority) {
+        mIsLowPriority = isLowPriority;
+        if (mContainingNotification != null) { /* we're not yet set up yet otherwise */
+            recreateLowPriorityHeader(null /* existingBuilder */);
+            updateHeaderVisibility(false /* animate */);
+        }
+        if (mUserLocked) {
+            setUserLocked(mUserLocked);
+        }
+    }
+
+    public NotificationHeaderView getVisibleHeader() {
+        NotificationHeaderView header = mNotificationHeader;
+        if (showingAsLowPriority()) {
+            header = mNotificationHeaderLowPriority;
+        }
+        return header;
+    }
+
+    public void onExpansionChanged() {
+        if (mIsLowPriority) {
+            if (mUserLocked) {
+                setUserLocked(mUserLocked);
+            }
+            updateHeaderVisibility(true /* animate */);
+        }
+    }
+
+    public float getIncreasedPaddingAmount() {
+        if (showingAsLowPriority()) {
+            return 0.0f;
+        }
+        return getGroupExpandFraction();
+    }
+
+    @VisibleForTesting
+    public boolean isUserLocked() {
+        return mUserLocked;
+    }
+
+    public void setCurrentBottomRoundness(float currentBottomRoundness) {
+        boolean last = true;
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            if (child.getVisibility() == View.GONE) {
+                continue;
+            }
+            float bottomRoundness = last ? currentBottomRoundness : 0.0f;
+            child.setBottomRoundness(bottomRoundness, isShown() /* animate */);
+            last = false;
+        }
+    }
+
+    public void setHeaderVisibleAmount(float headerVisibleAmount) {
+        mHeaderVisibleAmount = headerVisibleAmount;
+        mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader);
+    }
+}
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
new file mode 100644
index 0000000..fa75c71
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -0,0 +1,202 @@
+/*
+ * 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.notification.stack;
+
+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.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+
+/**
+ * Interface representing the entity that contains notifications. It can have
+ * notification views added and removed from it, and will manage displaying them to the user.
+ */
+public interface NotificationListContainer {
+
+    /**
+     * Called when a child is being transferred.
+     *
+     * @param childTransferInProgress whether child transfer is in progress
+     */
+    void setChildTransferInProgress(boolean childTransferInProgress);
+
+    /**
+     * Change the position of child to a new location
+     *
+     * @param child the view to change the position for
+     * @param newIndex the new index
+     */
+    void changeViewPosition(View child, int newIndex);
+
+    /**
+     * Called when a child was added to a group.
+     *
+     * @param row row of the group child that was added
+     */
+    void notifyGroupChildAdded(View row);
+
+    /**
+     * Called when a child was removed from a group.
+     *
+     * @param row row of the child that was removed
+     * @param childrenContainer ViewGroup of the group that the child was removed from
+     */
+    void notifyGroupChildRemoved(View row, ViewGroup childrenContainer);
+
+    /**
+     * Generate an animation for an added child view.
+     *
+     * @param child The view to be added.
+     * @param fromMoreCard Whether this add is coming from the "more" card on lockscreen.
+     */
+    void generateAddAnimation(View child, boolean fromMoreCard);
+
+    /**
+     * Generate a child order changed event.
+     */
+    void generateChildOrderChangedEvent();
+
+    /**
+     * Returns the number of children in the NotificationListContainer.
+     *
+     * @return the number of children in the NotificationListContainer
+     */
+    int getContainerChildCount();
+
+    /**
+     * Gets the ith child in the NotificationListContainer.
+     *
+     * @param i ith child to get
+     * @return the ith child in the list container
+     */
+    View getContainerChildAt(int i);
+
+    /**
+     * Remove a view from the container
+     *
+     * @param v view to remove
+     */
+    void removeContainerView(View v);
+
+    /**
+     * Add a view to the container
+     *
+     * @param v view to add
+     */
+    void addContainerView(View v);
+
+    /**
+     * Sets the maximum number of notifications to display.
+     *
+     * @param maxNotifications max number of notifications to display
+     */
+    void setMaxDisplayedNotifications(int maxNotifications);
+
+    /**
+     * Handle snapping a non-dismissable row back if the user tried to dismiss it.
+     *
+     * @param row row to snap back
+     */
+    void snapViewIfNeeded(ExpandableNotificationRow row);
+
+    /**
+     * 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);
+
+    /**
+     * Called when the height of an expandable view changes.
+     *
+     * @param view view whose height changed
+     * @param animate whether this change should be animated
+     */
+    void onHeightChanged(ExpandableView view, boolean animate);
+
+    /**
+     * Resets the currently exposed menu view.
+     *
+     * @param animate whether to animate the closing/change of menu view
+     * @param force reset the menu view even if it looks like it is already reset
+     */
+    void resetExposedMenuView(boolean animate, boolean force);
+
+    /**
+     * Returns the NotificationSwipeActionHelper for the NotificationListContainer.
+     *
+     * @return swipe action helper for the list container
+     */
+    NotificationSwipeActionHelper getSwipeActionHelper();
+
+    /**
+     * Called when a notification is removed from the shade. This cleans up the state for a
+     * given view.
+     *
+     * @param view view to clean up view state for
+     */
+    void cleanUpViewState(View view);
+
+    /**
+     * Returns whether an ExpandableNotificationRow is in a visible location or not.
+     *
+     * @param row
+     * @return true if row is in a visible location
+     */
+    boolean isInVisibleLocation(ExpandableNotificationRow row);
+
+    /**
+     * Sets a listener to listen for changes in notification locations.
+     *
+     * @param listener listener to set
+     */
+    void setChildLocationsChangedListener(
+            NotificationLogger.OnChildLocationsChangedListener listener);
+
+    /**
+     * Called when an update to the notification view hierarchy is completed.
+     */
+    default void onNotificationViewUpdateFinished() {}
+
+    /**
+     * Returns true if there are pulsing notifications.
+     *
+     * @return true if has pulsing notifications
+     */
+    boolean hasPulsingNotifications();
+
+    /**
+     * Apply parameters of the expand animation to the layout
+     */
+    default void applyExpandAnimationParams(ExpandAnimationParameters params) {}
+
+    default void setExpandingNotification(ExpandableNotificationRow row) {}
+
+    /**
+     * Bind a newly created row.
+     *
+     * @param row The notification to bind.
+     */
+    default void bindRow(ExpandableNotificationRow row) {}
+}
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
new file mode 100644
index 0000000..e32df42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.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.statusbar.notification.stack;
+
+import android.view.View;
+
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import java.util.HashSet;
+
+/**
+ * A class that manages the roundness for notification views
+ */
+class NotificationRoundnessManager implements OnHeadsUpChangedListener {
+
+    private boolean mExpanded;
+    private ActivatableNotificationView mFirst;
+    private ActivatableNotificationView mLast;
+    private HashSet<View> mAnimatedChildren;
+    private Runnable mRoundingChangedCallback;
+    private ExpandableNotificationRow mTrackedHeadsUp;
+    private float mAppearFraction;
+
+    @Override
+    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+        updateRounding(headsUp, false /* animate */);
+    }
+
+    @Override
+    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+        updateRounding(headsUp, true /* animate */);
+    }
+
+    public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row,
+            boolean isAnimatingAway) {
+        updateRounding(row, false /* animate */);
+    }
+
+    private void updateRounding(ActivatableNotificationView view, boolean animate) {
+        float topRoundness = getRoundness(view, true /* top */);
+        float bottomRoundness = getRoundness(view, false /* top */);
+        boolean firstChanged = view.setTopRoundness(topRoundness, animate);
+        boolean secondChanged = view.setBottomRoundness(bottomRoundness, animate);
+        if ((view == mFirst || view == mLast) && (firstChanged || secondChanged)) {
+            mRoundingChangedCallback.run();
+        }
+    }
+
+    private float getRoundness(ActivatableNotificationView view, boolean top) {
+        if ((view.isPinned() || view.isHeadsUpAnimatingAway()) && !mExpanded) {
+            return 1.0f;
+        }
+        if (view == mFirst && top) {
+            return 1.0f;
+        }
+        if (view == mLast && !top) {
+            return 1.0f;
+        }
+        if (view == mTrackedHeadsUp && mAppearFraction <= 0.0f) {
+            // If we're pushing up on a headsup the appear fraction is < 0 and it needs to still be
+            // rounded.
+            return 1.0f;
+        }
+        return 0.0f;
+    }
+
+    public void setExpanded(float expandedHeight, float appearFraction) {
+        mExpanded = expandedHeight != 0.0f;
+        mAppearFraction = appearFraction;
+        if (mTrackedHeadsUp != null) {
+            updateRounding(mTrackedHeadsUp, true);
+        }
+    }
+
+    public void setFirstAndLastBackgroundChild(ActivatableNotificationView first,
+            ActivatableNotificationView last) {
+        boolean firstChanged = mFirst != first;
+        boolean lastChanged = mLast != last;
+        if (!firstChanged && !lastChanged) {
+            return;
+        }
+        ActivatableNotificationView oldFirst = mFirst;
+        ActivatableNotificationView oldLast = mLast;
+        mFirst = first;
+        mLast = last;
+        if (firstChanged && oldFirst != null && !oldFirst.isRemoved()) {
+            updateRounding(oldFirst, oldFirst.isShown());
+        }
+        if (lastChanged && oldLast != null && !oldLast.isRemoved()) {
+            updateRounding(oldLast, oldLast.isShown());
+        }
+        if (mFirst != null) {
+            updateRounding(mFirst, mFirst.isShown() && !mAnimatedChildren.contains(mFirst));
+        }
+        if (mLast != null) {
+            updateRounding(mLast, mLast.isShown() && !mAnimatedChildren.contains(mLast));
+        }
+        mRoundingChangedCallback.run();
+    }
+
+    public void setAnimatedChildren(HashSet<View> animatedChildren) {
+        mAnimatedChildren = animatedChildren;
+    }
+
+    public void setOnRoundingChangedCallback(Runnable roundingChangedCallback) {
+        mRoundingChangedCallback = roundingChangedCallback;
+    }
+
+    public void setTrackingHeadsUp(ExpandableNotificationRow row) {
+        mTrackedHeadsUp = row;
+    }
+}
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
new file mode 100644
index 0000000..987de0e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -0,0 +1,5251 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
+        .ExpandAnimationParameters;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.TimeAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.FloatRange;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.service.notification.StatusBarNotification;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.ColorUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.MathUtils;
+import android.util.Pair;
+import android.view.ContextThemeWrapper;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.OverScroller;
+import android.widget.ScrollView;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.keyguard.KeyguardSliceView;
+import com.android.settingslib.Utils;
+import com.android.systemui.Dependency;
+import com.android.systemui.ExpandHelper;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.SwipeHelper;
+import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.FooterView;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.notification.row.NotificationSnooze;
+import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.FakeShadowView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
+import com.android.systemui.statusbar.policy.ScrollAdapter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.function.BiConsumer;
+
+/**
+ * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
+ */
+public class NotificationStackScrollLayout extends ViewGroup
+        implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
+        ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
+        NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider,
+        NotificationListContainer {
+
+    public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
+    private static final String TAG = "StackScroller";
+    private static final boolean DEBUG = false;
+    private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
+    private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
+    private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
+    /**
+     * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
+     */
+    private static final int INVALID_POINTER = -1;
+
+    private ExpandHelper mExpandHelper;
+    private NotificationSwipeHelper mSwipeHelper;
+    private boolean mSwipingInProgress;
+    private int mCurrentStackHeight = Integer.MAX_VALUE;
+    private final Paint mBackgroundPaint = new Paint();
+    private final boolean mShouldDrawNotificationBackground;
+
+    private float mExpandedHeight;
+    private int mOwnScrollY;
+    private int mMaxLayoutHeight;
+
+    private VelocityTracker mVelocityTracker;
+    private OverScroller mScroller;
+    private Runnable mFinishScrollingCallback;
+    private int mTouchSlop;
+    private int mMinimumVelocity;
+    private int mMaximumVelocity;
+    private int mOverflingDistance;
+    private float mMaxOverScroll;
+    private boolean mIsBeingDragged;
+    private int mLastMotionY;
+    private int mDownX;
+    private int mActivePointerId = INVALID_POINTER;
+    private boolean mTouchIsClick;
+    private float mInitialTouchX;
+    private float mInitialTouchY;
+
+    private Paint mDebugPaint;
+    private int mContentHeight;
+    private int mIntrinsicContentHeight;
+    private int mCollapsedSize;
+    private int mPaddingBetweenElements;
+    private int mIncreasedPaddingBetweenElements;
+    private int mMaxTopPadding;
+    private int mRegularTopPadding;
+    private int mDarkTopPadding;
+    // Current padding, will be either mRegularTopPadding or mDarkTopPadding
+    private int mTopPadding;
+    // Distance between AOD separator and shelf
+    private int mDarkSeparatorPadding;
+    private int mBottomMargin;
+    private int mBottomInset = 0;
+    private float mQsExpansionFraction;
+
+    /**
+     * The algorithm which calculates the properties for our children
+     */
+    protected final StackScrollAlgorithm mStackScrollAlgorithm;
+
+    /**
+     * The current State this Layout is in
+     */
+    private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
+    private final AmbientState mAmbientState;
+    private NotificationGroupManager mGroupManager;
+    private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
+    private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
+    private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
+    private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
+    private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
+    private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
+    private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
+    private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
+    private ArrayList<View> mSwipedOutViews = new ArrayList<>();
+    private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
+    private boolean mAnimationsEnabled;
+    private boolean mChangePositionInProgress;
+    private boolean mChildTransferInProgress;
+
+    /**
+     * The raw amount of the overScroll on the top, which is not rubber-banded.
+     */
+    private float mOverScrolledTopPixels;
+
+    /**
+     * The raw amount of the overScroll on the bottom, which is not rubber-banded.
+     */
+    private float mOverScrolledBottomPixels;
+    private NotificationLogger.OnChildLocationsChangedListener mListener;
+    private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
+    private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
+    private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
+    private boolean mNeedsAnimation;
+    private boolean mTopPaddingNeedsAnimation;
+    private boolean mDimmedNeedsAnimation;
+    private boolean mHideSensitiveNeedsAnimation;
+    private boolean mDarkNeedsAnimation;
+    private int mDarkAnimationOriginIndex;
+    private boolean mActivateNeedsAnimation;
+    private boolean mGoToFullShadeNeedsAnimation;
+    private boolean mIsExpanded = true;
+    private boolean mChildrenUpdateRequested;
+    private boolean mIsExpansionChanging;
+    private boolean mPanelTracking;
+    private boolean mExpandingNotification;
+    private boolean mExpandedInThisMotion;
+    private boolean mShouldShowShelfOnly;
+    protected boolean mScrollingEnabled;
+    protected FooterView mFooterView;
+    protected EmptyShadeView mEmptyShadeView;
+    private boolean mDismissAllInProgress;
+    private boolean mFadeNotificationsOnDismiss;
+
+    /**
+     * Was the scroller scrolled to the top when the down motion was observed?
+     */
+    private boolean mScrolledToTopOnFirstDown;
+    /**
+     * The minimal amount of over scroll which is needed in order to switch to the quick settings
+     * when over scrolling on a expanded card.
+     */
+    private float mMinTopOverScrollToEscape;
+    private int mIntrinsicPadding;
+    private float mStackTranslation;
+    private float mTopPaddingOverflow;
+    private boolean mDontReportNextOverScroll;
+    private boolean mDontClampNextScroll;
+    private boolean mNeedViewResizeAnimation;
+    private View mExpandedGroupView;
+    private boolean mEverythingNeedsAnimation;
+
+    /**
+     * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
+     * This is needed to avoid scrolling too far after the notification was collapsed in the same
+     * motion.
+     */
+    private int mMaxScrollAfterExpand;
+    private ExpandableNotificationRow.LongPressListener mLongPressListener;
+
+    private NotificationMenuRowPlugin mCurrMenuRow;
+    private View mTranslatingParentView;
+    private View mMenuExposedView;
+    boolean mCheckForLeavebehind;
+
+    /**
+     * Should in this touch motion only be scrolling allowed? It's true when the scroller was
+     * animating.
+     */
+    private boolean mOnlyScrollingInThisMotion;
+    private boolean mDisallowDismissInThisMotion;
+    private boolean mDisallowScrollingInThisMotion;
+    private long mGoToFullShadeDelay;
+    private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
+            = new ViewTreeObserver.OnPreDrawListener() {
+        @Override
+        public boolean onPreDraw() {
+            updateForcedScroll();
+            updateChildren();
+            mChildrenUpdateRequested = false;
+            getViewTreeObserver().removeOnPreDrawListener(this);
+            return true;
+        }
+    };
+    private StatusBar mStatusBar;
+    private int[] mTempInt2 = new int[2];
+    private boolean mGenerateChildOrderChangedEvent;
+    private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
+    private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>();
+    private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
+            = new HashSet<>();
+    private HeadsUpManagerPhone mHeadsUpManager;
+    private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
+    private boolean mTrackingHeadsUp;
+    private ScrimController mScrimController;
+    private boolean mForceNoOverlappingRendering;
+    private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
+    private FalsingManager mFalsingManager;
+    private boolean mAnimationRunning;
+    private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
+            = new ViewTreeObserver.OnPreDrawListener() {
+        @Override
+        public boolean onPreDraw() {
+            onPreDrawDuringAnimation();
+            return true;
+        }
+    };
+    private Rect mBackgroundBounds = new Rect();
+    private Rect mStartAnimationRect = new Rect();
+    private Rect mEndAnimationRect = new Rect();
+    private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
+    private boolean mAnimateNextBackgroundBottom;
+    private boolean mAnimateNextBackgroundTop;
+    private ObjectAnimator mBottomAnimator = null;
+    private ObjectAnimator mTopAnimator = null;
+    private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
+    private ActivatableNotificationView mLastVisibleBackgroundChild = null;
+    private int mBgColor;
+    private float mDimAmount;
+    private ValueAnimator mDimAnimator;
+    private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
+    private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mDimAnimator = null;
+        }
+    };
+    private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
+            = new ValueAnimator.AnimatorUpdateListener() {
+
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            setDimAmount((Float) animation.getAnimatedValue());
+        }
+    };
+    protected ViewGroup mQsContainer;
+    private boolean mContinuousShadowUpdate;
+    private ViewTreeObserver.OnPreDrawListener mShadowUpdater
+            = new ViewTreeObserver.OnPreDrawListener() {
+
+        @Override
+        public boolean onPreDraw() {
+            updateViewShadows();
+            return true;
+        }
+    };
+    private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
+        @Override
+        public int compare(ExpandableView view, ExpandableView otherView) {
+            float endY = view.getTranslationY() + view.getActualHeight();
+            float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
+            if (endY < otherEndY) {
+                return -1;
+            } else if (endY > otherEndY) {
+                return 1;
+            } else {
+                // The two notifications end at the same location
+                return 0;
+            }
+        }
+    };
+    private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
+    private boolean mPulsing;
+    private boolean mDrawBackgroundAsSrc;
+    private boolean mFadingOut;
+    private boolean mParentNotFullyVisible;
+    private boolean mGroupExpandedForMeasure;
+    private boolean mScrollable;
+    private View mForcedScroll;
+    private View mNeedingPulseAnimation;
+
+    /**
+     * @see #setDarkAmount(float, float)
+     */
+    private float mInterpolatedDarkAmount = 0f;
+
+    /**
+     * @see #setDarkAmount(float, float)
+     */
+    private float mLinearDarkAmount = 0f;
+
+    /**
+     * How fast the background scales in the X direction as a factor of the Y expansion.
+     */
+    private float mBackgroundXFactor = 1f;
+
+    private boolean mUsingLightTheme;
+    private boolean mQsExpanded;
+    private boolean mForwardScrollable;
+    private boolean mBackwardScrollable;
+    private NotificationShelf mShelf;
+    private int mMaxDisplayedNotifications = -1;
+    private int mStatusBarHeight;
+    private int mMinInteractionHeight;
+    private boolean mNoAmbient;
+    private final Rect mClipRect = new Rect();
+    private boolean mIsClipped;
+    private Rect mRequestedClipBounds;
+    private boolean mInHeadsUpPinnedMode;
+    private boolean mHeadsUpAnimatingAway;
+    private int mStatusBarState;
+    private int mCachedBackgroundColor;
+    private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
+    private Runnable mAnimateScroll = this::animateScroll;
+    private int mCornerRadius;
+    private int mSidePaddings;
+    private final int mSeparatorWidth;
+    private final int mSeparatorThickness;
+    private final Rect mBackgroundAnimationRect = new Rect();
+    private int mAntiBurnInOffsetX;
+    private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
+    private int mHeadsUpInset;
+    private HeadsUpAppearanceController mHeadsUpAppearanceController;
+    private NotificationIconAreaController mIconAreaController;
+    private float mVerticalPanelTranslation;
+
+    private Interpolator mDarkXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
+
+    public NotificationStackScrollLayout(Context context) {
+        this(context, null);
+    }
+
+    public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        Resources res = getResources();
+
+        mAmbientState = new AmbientState(context);
+        mBgColor = context.getColor(R.color.notification_shade_background_color);
+        int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
+        int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
+        mExpandHelper = new ExpandHelper(getContext(), this,
+                minHeight, maxHeight);
+        mExpandHelper.setEventSource(this);
+        mExpandHelper.setScrollAdapter(this);
+        mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
+        mStackScrollAlgorithm = createStackScrollAlgorithm(context);
+        initView(context);
+        mFalsingManager = FalsingManager.getInstance(context);
+        mShouldDrawNotificationBackground =
+                res.getBoolean(R.bool.config_drawNotificationBackground);
+        mFadeNotificationsOnDismiss =
+                res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
+        mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
+        mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
+        mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
+        mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
+        mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
+        addOnExpandedHeightListener(mRoundnessManager::setExpanded);
+
+        // Blocking helper manager wants to know the expanded state, update as well.
+        NotificationBlockingHelperManager blockingHelperManager =
+                Dependency.get(NotificationBlockingHelperManager.class);
+        addOnExpandedHeightListener((height, unused) -> {
+            blockingHelperManager.setNotificationShadeExpanded(height);
+        });
+
+        updateWillNotDraw();
+        mBackgroundPaint.setAntiAlias(true);
+        if (DEBUG) {
+            mDebugPaint = new Paint();
+            mDebugPaint.setColor(0xffff0000);
+            mDebugPaint.setStrokeWidth(2);
+            mDebugPaint.setStyle(Paint.Style.STROKE);
+        }
+    }
+
+    @Override
+    public NotificationSwipeActionHelper getSwipeActionHelper() {
+        return mSwipeHelper;
+    }
+
+    @Override
+    public void onMenuClicked(View view, int x, int y, MenuItem item) {
+        if (mLongPressListener == null) {
+            return;
+        }
+        if (view instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
+                    row.getStatusBarNotification().getPackageName());
+        }
+        mLongPressListener.onLongPress(view, x, y, item);
+    }
+
+    @Override
+    public void onMenuReset(View row) {
+        if (mTranslatingParentView != null && row == mTranslatingParentView) {
+            mMenuExposedView = null;
+            mTranslatingParentView = null;
+        }
+    }
+
+    @Override
+    public void onMenuShown(View row) {
+        mMenuExposedView = mTranslatingParentView;
+        if (row instanceof ExpandableNotificationRow) {
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
+                    ((ExpandableNotificationRow) row).getStatusBarNotification()
+                            .getPackageName());
+        }
+        mSwipeHelper.onMenuShown(row);
+    }
+
+    public void onUiModeChanged() {
+        mBgColor = mContext.getColor(R.color.notification_shade_background_color);
+        updateBackgroundDimming();
+
+        // Re-inflate all notification views
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child instanceof ActivatableNotificationView) {
+                ((ActivatableNotificationView) child).onUiModeChanged();
+            }
+        }
+    }
+
+    protected void onDraw(Canvas canvas) {
+        if (mShouldDrawNotificationBackground
+                && (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
+            drawBackground(canvas);
+        }
+
+        if (DEBUG) {
+            int y = mTopPadding;
+            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+            y = getLayoutHeight();
+            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+            y = getHeight() - getEmptyBottomMargin();
+            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+        }
+    }
+
+    private void drawBackground(Canvas canvas) {
+        final int lockScreenLeft = mSidePaddings;
+        final int lockScreenRight = getWidth() - mSidePaddings;
+        final int lockScreenTop = mCurrentBounds.top;
+        final int lockScreenBottom = mCurrentBounds.bottom;
+        int separatorWidth = 0;
+        int separatorThickness = 0;
+        if (mIconAreaController.hasShelfIconsWhenFullyDark()) {
+            separatorThickness = mSeparatorThickness;
+            separatorWidth = mSeparatorWidth;
+        }
+        final int darkLeft = getWidth() / 2 - separatorWidth / 2;
+        final int darkRight = darkLeft + separatorWidth;
+        final int darkTop = (int) (mRegularTopPadding + separatorThickness / 2f);
+        final int darkBottom = darkTop + separatorThickness;
+
+        if (mAmbientState.hasPulsingNotifications()) {
+            // No divider, we have a notification icon instead
+        } else if (mAmbientState.isFullyDark()) {
+            // Only draw divider on AOD if we actually have notifications
+            if (mFirstVisibleBackgroundChild != null) {
+                canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
+            }
+        } else {
+            float yProgress = 1 - mInterpolatedDarkAmount;
+            float xProgress = mDarkXInterpolator.getInterpolation(
+                    (1 - mLinearDarkAmount) * mBackgroundXFactor);
+
+            mBackgroundAnimationRect.set(
+                    (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
+                    (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
+                    (int) MathUtils.lerp(darkRight, lockScreenRight, xProgress),
+                    (int) MathUtils.lerp(darkBottom, lockScreenBottom, yProgress));
+
+            if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
+                canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
+                        mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
+                        mCornerRadius, mCornerRadius, mBackgroundPaint);
+            }
+        }
+        updateClipping();
+    }
+
+    private void updateBackgroundDimming() {
+        // No need to update the background color if it's not being drawn.
+        if (!mShouldDrawNotificationBackground) {
+            return;
+        }
+
+        float alpha =
+                BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
+        alpha *= 1f - mInterpolatedDarkAmount;
+        // We need to manually blend in the background color.
+        int scrimColor = mScrimController.getBackgroundColor();
+        int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
+
+        // Interpolate between semi-transparent notification panel background color
+        // and white AOD separator.
+        float colorInterpolation = MathUtils.smoothStep(0.4f /* start */, 1f /* end */,
+                mLinearDarkAmount);
+        int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation);
+
+        if (mCachedBackgroundColor != color) {
+            mCachedBackgroundColor = color;
+            mBackgroundPaint.setColor(color);
+            invalidate();
+        }
+    }
+
+    private void initView(Context context) {
+        mScroller = new OverScroller(getContext());
+        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
+        setClipChildren(false);
+        final ViewConfiguration configuration = ViewConfiguration.get(context);
+        mTouchSlop = configuration.getScaledTouchSlop();
+        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
+        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+        mOverflingDistance = configuration.getScaledOverflingDistance();
+
+        Resources res = context.getResources();
+        mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
+        mStackScrollAlgorithm.initView(context);
+        mAmbientState.reload(context);
+        mPaddingBetweenElements = Math.max(1,
+                res.getDimensionPixelSize(R.dimen.notification_divider_height));
+        mIncreasedPaddingBetweenElements =
+                res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
+        mMinTopOverScrollToEscape = res.getDimensionPixelSize(
+                R.dimen.min_top_overscroll_to_qs);
+        mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
+        mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
+        mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
+        mMinInteractionHeight = res.getDimensionPixelSize(
+                R.dimen.notification_min_interaction_height);
+        mCornerRadius = res.getDimensionPixelSize(
+                Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
+        mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
+                R.dimen.heads_up_status_bar_padding);
+    }
+
+    public void setDrawBackgroundAsSrc(boolean asSrc) {
+        mDrawBackgroundAsSrc = asSrc;
+        updateSrcDrawing();
+    }
+
+    private void updateSrcDrawing() {
+        if (!mShouldDrawNotificationBackground) {
+            return;
+        }
+
+        mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
+                ? mSrcMode : null);
+        invalidate();
+    }
+
+    private void notifyHeightChangeListener(ExpandableView view) {
+        notifyHeightChangeListener(view, false /* needsAnimation */);
+    }
+
+    private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
+        if (mOnHeightChangedListener != null) {
+            mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
+                MeasureSpec.getMode(widthMeasureSpec));
+        // Don't constrain the height of the children so we know how big they'd like to be
+        int childHeightSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
+                MeasureSpec.UNSPECIFIED);
+
+        // We need to measure all children even the GONE ones, such that the heights are calculated
+        // correctly as they are used to calculate how many we can fit on the screen.
+        final int size = getChildCount();
+        for (int i = 0; i < size; i++) {
+            measureChild(getChildAt(i), childWidthSpec, childHeightSpec);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        // we layout all our children centered on the top
+        float centerX = getWidth() / 2.0f;
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            // We need to layout all children even the GONE ones, such that the heights are
+            // calculated correctly as they are used to calculate how many we can fit on the screen
+            float width = child.getMeasuredWidth();
+            float height = child.getMeasuredHeight();
+            child.layout((int) (centerX - width / 2.0f),
+                    0,
+                    (int) (centerX + width / 2.0f),
+                    (int) height);
+        }
+        setMaxLayoutHeight(getHeight());
+        updateContentHeight();
+        clampScrollPosition();
+        requestChildrenUpdate();
+        updateFirstAndLastBackgroundViews();
+        updateAlgorithmLayoutMinHeight();
+    }
+
+    private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
+        if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
+            mNeedViewResizeAnimation = true;
+            mNeedsAnimation = true;
+        }
+    }
+
+    public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
+        mAmbientState.setSpeedBumpIndex(newIndex);
+        mNoAmbient = noAmbient;
+    }
+
+    @Override
+    public void setChildLocationsChangedListener(
+            NotificationLogger.OnChildLocationsChangedListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    public boolean isInVisibleLocation(ExpandableNotificationRow row) {
+        ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
+        if (childViewState == null) {
+            return false;
+        }
+        if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
+            return false;
+        }
+        if (row.getVisibility() != View.VISIBLE) {
+            return false;
+        }
+        return true;
+    }
+
+    private void setMaxLayoutHeight(int maxLayoutHeight) {
+        mMaxLayoutHeight = maxLayoutHeight;
+        mShelf.setMaxLayoutHeight(maxLayoutHeight);
+        updateAlgorithmHeightAndPadding();
+    }
+
+    private void updateAlgorithmHeightAndPadding() {
+        mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding,
+                mInterpolatedDarkAmount);
+        mAmbientState.setLayoutHeight(getLayoutHeight());
+        updateAlgorithmLayoutMinHeight();
+        mAmbientState.setTopPadding(mTopPadding);
+    }
+
+    private void updateAlgorithmLayoutMinHeight() {
+        mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
+                ? getLayoutMinHeight() : 0);
+    }
+
+    /**
+     * Updates the children views according to the stack scroll algorithm. Call this whenever
+     * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
+     */
+    private void updateChildren() {
+        updateScrollStateForAddedChildren();
+        mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
+                ? 0
+                : mScroller.getCurrVelocity());
+        mAmbientState.setScrollY(mOwnScrollY);
+        mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
+        if (!isCurrentlyAnimating() && !mNeedsAnimation) {
+            applyCurrentState();
+        } else {
+            startAnimationToState();
+        }
+    }
+
+    private void onPreDrawDuringAnimation() {
+        mShelf.updateAppearance();
+        updateClippingToTopRoundedCorner();
+        if (!mNeedsAnimation && !mChildrenUpdateRequested) {
+            updateBackground();
+        }
+    }
+
+    private void updateClippingToTopRoundedCorner() {
+        Float clipStart = (float) mTopPadding
+                                 + mStackTranslation
+                                 + mAmbientState.getExpandAnimationTopChange();
+        Float clipEnd = clipStart + mCornerRadius;
+        boolean first = true;
+        for (int i = 0; i < getChildCount(); i++) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            if (child.getVisibility() == GONE) {
+                continue;
+            }
+            float start = child.getTranslationY();
+            float end = start + child.getActualHeight();
+            boolean clip = clipStart > start && clipStart < end
+                    || clipEnd >= start && clipEnd <= end;
+            clip &= !(first && mOwnScrollY == 0);
+            child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
+                    : ExpandableView.NO_ROUNDNESS);
+            first = false;
+        }
+    }
+
+    private void updateScrollStateForAddedChildren() {
+        if (mChildrenToAddAnimated.isEmpty()) {
+            return;
+        }
+        for (int i = 0; i < getChildCount(); i++) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            if (mChildrenToAddAnimated.contains(child)) {
+                int startingPosition = getPositionInLinearLayout(child);
+                float increasedPaddingAmount = child.getIncreasedPaddingAmount();
+                int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
+                        : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
+                int childHeight = getIntrinsicHeight(child) + padding;
+                if (startingPosition < mOwnScrollY) {
+                    // This child starts off screen, so let's keep it offscreen to keep the others visible
+
+                    setOwnScrollY(mOwnScrollY + childHeight);
+                }
+            }
+        }
+        clampScrollPosition();
+    }
+
+    private void updateForcedScroll() {
+        if (mForcedScroll != null && (!mForcedScroll.hasFocus()
+                || !mForcedScroll.isAttachedToWindow())) {
+            mForcedScroll = null;
+        }
+        if (mForcedScroll != null) {
+            ExpandableView expandableView = (ExpandableView) mForcedScroll;
+            int positionInLinearLayout = getPositionInLinearLayout(expandableView);
+            int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
+            int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
+
+            targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
+
+            // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
+            // that it is not visible anymore.
+            if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
+                setOwnScrollY(targetScroll);
+            }
+        }
+    }
+
+    private void requestChildrenUpdate() {
+        if (!mChildrenUpdateRequested) {
+            getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
+            mChildrenUpdateRequested = true;
+            invalidate();
+        }
+    }
+
+    private boolean isCurrentlyAnimating() {
+        return mStateAnimator.isRunning();
+    }
+
+    private void clampScrollPosition() {
+        int scrollRange = getScrollRange();
+        if (scrollRange < mOwnScrollY) {
+            setOwnScrollY(scrollRange);
+        }
+    }
+
+    public int getTopPadding() {
+        return mTopPadding;
+    }
+
+    private void setTopPadding(int topPadding, boolean animate) {
+        if (mRegularTopPadding != topPadding) {
+            mRegularTopPadding = topPadding;
+            mDarkTopPadding = topPadding + mDarkSeparatorPadding;
+            mAmbientState.setDarkTopPadding(mDarkTopPadding);
+            updateAlgorithmHeightAndPadding();
+            updateContentHeight();
+            if (animate && mAnimationsEnabled && mIsExpanded) {
+                mTopPaddingNeedsAnimation = true;
+                mNeedsAnimation =  true;
+            }
+            requestChildrenUpdate();
+            notifyHeightChangeListener(null, animate);
+        }
+    }
+
+    /**
+     * Update the height of the panel.
+     *
+     * @param height the expanded height of the panel
+     */
+    public void setExpandedHeight(float height) {
+        mExpandedHeight = height;
+        setIsExpanded(height > 0);
+        int minExpansionHeight = getMinExpansionHeight();
+        if (height < minExpansionHeight) {
+            mClipRect.left = 0;
+            mClipRect.right = getWidth();
+            mClipRect.top = 0;
+            mClipRect.bottom = (int) height;
+            height = minExpansionHeight;
+            setRequestedClipBounds(mClipRect);
+        } else {
+            setRequestedClipBounds(null);
+        }
+        int stackHeight;
+        float translationY;
+        float appearEndPosition = getAppearEndPosition();
+        float appearStartPosition = getAppearStartPosition();
+        float appearFraction = 1.0f;
+        boolean appearing = height < appearEndPosition;
+        mAmbientState.setAppearing(appearing);
+        if (!appearing) {
+            translationY = 0;
+            if (mShouldShowShelfOnly) {
+                stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
+            } else if (mQsExpanded) {
+                int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
+                int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
+                if (stackStartPosition <= stackEndPosition) {
+                    stackHeight = stackEndPosition;
+                } else {
+                    stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
+                            stackEndPosition, mQsExpansionFraction);
+                }
+            } else {
+                stackHeight = (int) height;
+            }
+        } else {
+            appearFraction = getAppearFraction(height);
+            if (appearFraction >= 0) {
+                translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
+                        appearFraction);
+            } else {
+                // This may happen when pushing up a heads up. We linearly push it up from the
+                // start
+                translationY = height - appearStartPosition + getExpandTranslationStart();
+            }
+            if (isHeadsUpTransition()) {
+                stackHeight = mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
+                translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
+            } else {
+                stackHeight = (int) (height - translationY);
+            }
+        }
+        if (stackHeight != mCurrentStackHeight) {
+            mCurrentStackHeight = stackHeight;
+            updateAlgorithmHeightAndPadding();
+            requestChildrenUpdate();
+        }
+        setStackTranslation(translationY);
+        for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
+            BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
+            listener.accept(mExpandedHeight, appearFraction);
+        }
+    }
+
+    private void setRequestedClipBounds(Rect clipRect) {
+        mRequestedClipBounds = clipRect;
+        updateClipping();
+    }
+
+    /**
+     * Return the height of the content ignoring the footer.
+     */
+    public int getIntrinsicContentHeight() {
+        return mIntrinsicContentHeight;
+    }
+
+    public void updateClipping() {
+        boolean animatingClipping = mInterpolatedDarkAmount > 0 && mInterpolatedDarkAmount < 1;
+        boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
+                && !mHeadsUpAnimatingAway;
+        if (mIsClipped != clipped) {
+            mIsClipped = clipped;
+            updateFadingState();
+        }
+
+        if (animatingClipping) {
+            setClipBounds(mBackgroundAnimationRect);
+        } else if (clipped) {
+            setClipBounds(mRequestedClipBounds);
+        } else {
+            setClipBounds(null);
+        }
+    }
+
+    /**
+     * @return The translation at the beginning when expanding.
+     *         Measured relative to the resting position.
+     */
+    private float getExpandTranslationStart() {
+        return -mTopPadding + getMinExpansionHeight();
+    }
+
+    /**
+     * @return the position from where the appear transition starts when expanding.
+     *         Measured in absolute height.
+     */
+    private float getAppearStartPosition() {
+        if (isHeadsUpTransition()) {
+            return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
+        }
+        return getMinExpansionHeight();
+    }
+
+    /**
+     * @return the height of the top heads up notification when pinned. This is different from the
+     *         intrinsic height, which also includes whether the notification is system expanded and
+     *         is mainly used when dragging down from a heads up notification.
+     */
+    private int getTopHeadsUpPinnedHeight() {
+        NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
+        if (topEntry == null) {
+            return 0;
+        }
+        ExpandableNotificationRow row = topEntry.row;
+        if (row.isChildInGroup()) {
+            final ExpandableNotificationRow groupSummary
+                    = mGroupManager.getGroupSummary(row.getStatusBarNotification());
+            if (groupSummary != null) {
+                row = groupSummary;
+            }
+        }
+        return row.getPinnedHeadsUpHeight();
+    }
+
+    /**
+     * @return the position from where the appear transition ends when expanding.
+     *         Measured in absolute height.
+     */
+    private float getAppearEndPosition() {
+        int appearPosition;
+        int notGoneChildCount = getNotGoneChildCount();
+        if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
+            if (isHeadsUpTransition()
+                    || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
+                appearPosition = getTopHeadsUpPinnedHeight();
+            } else {
+                appearPosition = 0;
+                if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
+                    appearPosition += mShelf.getIntrinsicHeight();
+                }
+            }
+        } else {
+            appearPosition = mEmptyShadeView.getHeight();
+        }
+        return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
+    }
+
+    private boolean isHeadsUpTransition() {
+        return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null
+                && mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild);
+    }
+
+    /**
+     * @param height the height of the panel
+     * @return the fraction of the appear animation that has been performed
+     */
+    public float getAppearFraction(float height) {
+        float appearEndPosition = getAppearEndPosition();
+        float appearStartPosition = getAppearStartPosition();
+        return (height - appearStartPosition)
+                / (appearEndPosition - appearStartPosition);
+    }
+
+    public float getStackTranslation() {
+        return mStackTranslation;
+    }
+
+    private void setStackTranslation(float stackTranslation) {
+        if (stackTranslation != mStackTranslation) {
+            mStackTranslation = stackTranslation;
+            mAmbientState.setStackTranslation(stackTranslation);
+            requestChildrenUpdate();
+        }
+    }
+
+    /**
+     * Get the current height of the view. This is at most the msize of the view given by a the
+     * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
+     *
+     * @return either the layout height or the externally defined height, whichever is smaller
+     */
+    private int getLayoutHeight() {
+        return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
+    }
+
+    public int getFirstItemMinHeight() {
+        final ExpandableView firstChild = getFirstChildNotGone();
+        return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
+    }
+
+    public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
+        mLongPressListener = listener;
+    }
+
+    public void setQsContainer(ViewGroup qsContainer) {
+        mQsContainer = qsContainer;
+    }
+
+    /**
+     * Handles cleanup after the given {@code view} has been fully swiped out (including
+     * re-invoking dismiss logic in case the notification has not made its way out yet).
+     */
+    @Override
+    public void onChildDismissed(View view) {
+        ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+        if (!row.isDismissed()) {
+            handleChildViewDismissed(view);
+        }
+        ViewGroup transientContainer = row.getTransientContainer();
+        if (transientContainer != null) {
+            transientContainer.removeTransientView(view);
+        }
+    }
+
+    /**
+     * Starts up notification dismiss and tells the notification, if any, to remove itself from
+     * layout.
+     *
+     * @param view view (e.g. notification) to dismiss from the layout
+     */
+    private void handleChildViewDismissed(View view) {
+        if (mDismissAllInProgress) {
+            return;
+        }
+
+        boolean isBlockingHelperShown = false;
+
+        setSwipingInProgress(false);
+        if (mDragAnimPendingChildren.contains(view)) {
+            // We start the swipe and finish it in the same frame; we don't want a drag animation.
+            mDragAnimPendingChildren.remove(view);
+        }
+        mAmbientState.onDragFinished(view);
+        updateContinuousShadowDrawing();
+
+        if (view instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+            if (row.isHeadsUp()) {
+                mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
+            }
+            isBlockingHelperShown =
+                    row.performDismissWithBlockingHelper(false /* fromAccessibility */);
+        }
+
+        if (!isBlockingHelperShown) {
+            mSwipedOutViews.add(view);
+        }
+        mFalsingManager.onNotificationDismissed();
+        if (mFalsingManager.shouldEnforceBouncer()) {
+            mStatusBar.executeRunnableDismissingKeyguard(
+                    null,
+                    null /* cancelAction */,
+                    false /* dismissShade */,
+                    true /* afterKeyguardGone */,
+                    false /* deferred */);
+        }
+    }
+
+    @Override
+    public void onChildSnappedBack(View animView, float targetLeft) {
+        mAmbientState.onDragFinished(animView);
+        updateContinuousShadowDrawing();
+        if (!mDragAnimPendingChildren.contains(animView)) {
+            if (mAnimationsEnabled) {
+                mSnappedBackChildren.add(animView);
+                mNeedsAnimation = true;
+            }
+            requestChildrenUpdate();
+        } else {
+            // We start the swipe and snap back in the same frame, we don't want any animation
+            mDragAnimPendingChildren.remove(animView);
+        }
+        if (mCurrMenuRow != null && targetLeft == 0) {
+            mCurrMenuRow.resetMenu();
+            mCurrMenuRow = null;
+        }
+    }
+
+    @Override
+    public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
+        // Returning true prevents alpha fading.
+        return !mFadeNotificationsOnDismiss;
+    }
+
+    @Override
+    public void onBeginDrag(View v) {
+        mFalsingManager.onNotificatonStartDismissing();
+        setSwipingInProgress(true);
+        mAmbientState.onBeginDrag(v);
+        updateContinuousShadowDrawing();
+        if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
+            mDragAnimPendingChildren.add(v);
+            mNeedsAnimation = true;
+        }
+        requestChildrenUpdate();
+    }
+
+    public static boolean isPinnedHeadsUp(View v) {
+        if (v instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+            return row.isHeadsUp() && row.isPinned();
+        }
+        return false;
+    }
+
+    private boolean isHeadsUp(View v) {
+        if (v instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+            return row.isHeadsUp();
+        }
+        return false;
+    }
+
+    @Override
+    public void onDragCancelled(View v) {
+        mFalsingManager.onNotificatonStopDismissing();
+        setSwipingInProgress(false);
+    }
+
+    @Override
+    public float getFalsingThresholdFactor() {
+        return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+    }
+
+    @Override
+    public View getChildAtPosition(MotionEvent ev) {
+        View child = getChildAtPosition(ev.getX(), ev.getY());
+        if (child instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+            ExpandableNotificationRow parent = row.getNotificationParent();
+            if (parent != null && parent.areChildrenExpanded()
+                    && (parent.areGutsExposed()
+                            || mMenuExposedView == parent
+                        || (parent.getNotificationChildren().size() == 1
+                                && parent.isClearable()))) {
+                // In this case the group is expanded and showing the menu for the
+                // group, further interaction should apply to the group, not any
+                // child notifications so we use the parent of the child. We also do the same
+                // if we only have a single child.
+                child = parent;
+            }
+        }
+        return child;
+    }
+
+    public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
+        getLocationOnScreen(mTempInt2);
+        float localTouchY = touchY - mTempInt2[1];
+
+        ExpandableView closestChild = null;
+        float minDist = Float.MAX_VALUE;
+
+        // find the view closest to the location, accounting for GONE views
+        final int count = getChildCount();
+        for (int childIdx = 0; childIdx < count; childIdx++) {
+            ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
+            if (slidingChild.getVisibility() == GONE
+                    || slidingChild instanceof StackScrollerDecorView) {
+                continue;
+            }
+            float childTop = slidingChild.getTranslationY();
+            float top = childTop + slidingChild.getClipTopAmount();
+            float bottom = childTop + slidingChild.getActualHeight()
+                    - slidingChild.getClipBottomAmount();
+
+            float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
+            if (dist < minDist) {
+                closestChild = slidingChild;
+                minDist = dist;
+            }
+        }
+        return closestChild;
+    }
+
+    @Override
+    public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
+        getLocationOnScreen(mTempInt2);
+        return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
+    }
+
+    @Override
+    public ExpandableView getChildAtPosition(float touchX, float touchY) {
+        return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
+
+    }
+
+    /**
+     * Get the child at a certain screen location.
+     *
+     * @param touchX the x coordinate
+     * @param touchY the y coordinate
+     * @param requireMinHeight Whether a minimum height is required for a child to be returned.
+     * @return the child at the given location.
+     */
+    private ExpandableView getChildAtPosition(float touchX, float touchY,
+            boolean requireMinHeight) {
+        // find the view under the pointer, accounting for GONE views
+        final int count = getChildCount();
+        for (int childIdx = 0; childIdx < count; childIdx++) {
+            ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
+            if (slidingChild.getVisibility() != VISIBLE
+                    || slidingChild instanceof StackScrollerDecorView) {
+                continue;
+            }
+            float childTop = slidingChild.getTranslationY();
+            float top = childTop + slidingChild.getClipTopAmount();
+            float bottom = childTop + slidingChild.getActualHeight()
+                    - slidingChild.getClipBottomAmount();
+
+            // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
+            // camera affordance).
+            int left = 0;
+            int right = getWidth();
+
+            if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
+                    && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
+                if (slidingChild instanceof ExpandableNotificationRow) {
+                    ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
+                    if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
+                            && mHeadsUpManager.getTopEntry().row != row
+                            && mGroupManager.getGroupSummary(
+                                mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
+                                != row) {
+                        continue;
+                    }
+                    return row.getViewAtPosition(touchY - childTop);
+                }
+                return slidingChild;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean canChildBeExpanded(View v) {
+        return v instanceof ExpandableNotificationRow
+                && ((ExpandableNotificationRow) v).isExpandable()
+                && !((ExpandableNotificationRow) v).areGutsExposed()
+                && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
+    }
+
+    /* Only ever called as a consequence of an expansion gesture in the shade. */
+    @Override
+    public void setUserExpandedChild(View v, boolean userExpanded) {
+        if (v instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+            if (userExpanded && onKeyguard()) {
+                // Due to a race when locking the screen while touching, a notification may be
+                // expanded even after we went back to keyguard. An example of this happens if
+                // you click in the empty space while expanding a group.
+
+                // We also need to un-user lock it here, since otherwise the content height
+                // calculated might be wrong. We also can't invert the two calls since
+                // un-userlocking it will trigger a layout switch in the content view.
+                row.setUserLocked(false);
+                updateContentHeight();
+                notifyHeightChangeListener(row);
+                return;
+            }
+            row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
+            row.onExpandedByGesture(userExpanded);
+        }
+    }
+
+    @Override
+    public void setExpansionCancelled(View v) {
+        if (v instanceof ExpandableNotificationRow) {
+            ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
+        }
+    }
+
+    @Override
+    public void setUserLockedChild(View v, boolean userLocked) {
+        if (v instanceof ExpandableNotificationRow) {
+            ((ExpandableNotificationRow) v).setUserLocked(userLocked);
+        }
+        cancelLongPress();
+        requestDisallowInterceptTouchEvent(true);
+    }
+
+    @Override
+    public void expansionStateChanged(boolean isExpanding) {
+        mExpandingNotification = isExpanding;
+        if (!mExpandedInThisMotion) {
+            mMaxScrollAfterExpand = mOwnScrollY;
+            mExpandedInThisMotion = true;
+        }
+    }
+
+    @Override
+    public int getMaxExpandHeight(ExpandableView view) {
+        return view.getMaxContentHeight();
+    }
+
+    public void setScrollingEnabled(boolean enable) {
+        mScrollingEnabled = enable;
+    }
+
+    public void lockScrollTo(View v) {
+        if (mForcedScroll == v) {
+            return;
+        }
+        mForcedScroll = v;
+        scrollTo(v);
+    }
+
+    public boolean scrollTo(View v) {
+        ExpandableView expandableView = (ExpandableView) v;
+        int positionInLinearLayout = getPositionInLinearLayout(v);
+        int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
+        int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
+
+        // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
+        // that it is not visible anymore.
+        if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
+            mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
+            mDontReportNextOverScroll = true;
+            animateScroll();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
+     *         the IME.
+     */
+    private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
+        return positionInLinearLayout + v.getIntrinsicHeight() +
+                getImeInset() - getHeight()
+                + ((!isExpanded() && isPinnedHeadsUp(v)) ? mHeadsUpInset : getTopPadding());
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        mBottomInset = insets.getSystemWindowInsetBottom();
+
+        int range = getScrollRange();
+        if (mOwnScrollY > range) {
+            // HACK: We're repeatedly getting staggered insets here while the IME is
+            // animating away. To work around that we'll wait until things have settled.
+            removeCallbacks(mReclamp);
+            postDelayed(mReclamp, 50);
+        } else if (mForcedScroll != null) {
+            // The scroll was requested before we got the actual inset - in case we need
+            // to scroll up some more do so now.
+            scrollTo(mForcedScroll);
+        }
+        return insets;
+    }
+
+    private Runnable mReclamp = new Runnable() {
+        @Override
+        public void run() {
+            int range = getScrollRange();
+            mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
+            mDontReportNextOverScroll = true;
+            mDontClampNextScroll = true;
+            animateScroll();
+        }
+    };
+
+    public void setExpandingEnabled(boolean enable) {
+        mExpandHelper.setEnabled(enable);
+    }
+
+    private boolean isScrollingEnabled() {
+        return mScrollingEnabled;
+    }
+
+    @Override
+    public boolean canChildBeDismissed(View v) {
+        return StackScrollAlgorithm.canChildBeDismissed(v);
+    }
+
+    @Override
+    public boolean isAntiFalsingNeeded() {
+        return onKeyguard();
+    }
+
+    private boolean onKeyguard() {
+        return mStatusBarState == StatusBarState.KEYGUARD;
+    }
+
+    private void setSwipingInProgress(boolean isSwiped) {
+        mSwipingInProgress = isSwiped;
+        if(isSwiped) {
+            requestDisallowInterceptTouchEvent(true);
+        }
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
+        float densityScale = getResources().getDisplayMetrics().density;
+        mSwipeHelper.setDensityScale(densityScale);
+        float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
+        mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
+        initView(getContext());
+    }
+
+    public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
+        mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
+                true /* isDismissAll */);
+    }
+
+    @Override
+    public void snapViewIfNeeded(ExpandableNotificationRow child) {
+        boolean animate = mIsExpanded || isPinnedHeadsUp(child);
+        // If the child is showing the notification menu snap to that
+        float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
+        mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
+    }
+
+    @Override
+    public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
+        return this;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
+                || ev.getActionMasked()== MotionEvent.ACTION_UP;
+        handleEmptySpaceClick(ev);
+        boolean expandWantsIt = false;
+        if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
+            if (isCancelOrUp) {
+                mExpandHelper.onlyObserveMovements(false);
+            }
+            boolean wasExpandingBefore = mExpandingNotification;
+            expandWantsIt = mExpandHelper.onTouchEvent(ev);
+            if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
+                    && !mDisallowScrollingInThisMotion) {
+                dispatchDownEventToScroller(ev);
+            }
+        }
+        boolean scrollerWantsIt = false;
+        if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
+                && !mDisallowScrollingInThisMotion) {
+            scrollerWantsIt = onScrollTouch(ev);
+        }
+        boolean horizontalSwipeWantsIt = false;
+        if (!mIsBeingDragged
+                && !mExpandingNotification
+                && !mExpandedInThisMotion
+                && !mOnlyScrollingInThisMotion
+                && !mDisallowDismissInThisMotion) {
+            horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
+        }
+
+        // Check if we need to clear any snooze leavebehinds
+        NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
+        if (guts != null && !isTouchInView(ev, guts)
+                && guts.getGutsContent() instanceof NotificationSnooze) {
+            NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
+            if ((ns.isExpanded() && isCancelOrUp)
+                    || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
+                // If the leavebehind is expanded we clear it on the next up event, otherwise we
+                // clear it on the next non-horizontal swipe or expand event.
+                checkSnoozeLeavebehind();
+            }
+        }
+        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+            mCheckForLeavebehind = true;
+        }
+        return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
+    }
+
+    private void dispatchDownEventToScroller(MotionEvent ev) {
+        MotionEvent downEvent = MotionEvent.obtain(ev);
+        downEvent.setAction(MotionEvent.ACTION_DOWN);
+        onScrollTouch(downEvent);
+        downEvent.recycle();
+    }
+
+    @Override
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
+                || mDisallowScrollingInThisMotion) {
+            return false;
+        }
+        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_SCROLL: {
+                    if (!mIsBeingDragged) {
+                        final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+                        if (vscroll != 0) {
+                            final int delta = (int) (vscroll * getVerticalScrollFactor());
+                            final int range = getScrollRange();
+                            int oldScrollY = mOwnScrollY;
+                            int newScrollY = oldScrollY - delta;
+                            if (newScrollY < 0) {
+                                newScrollY = 0;
+                            } else if (newScrollY > range) {
+                                newScrollY = range;
+                            }
+                            if (newScrollY != oldScrollY) {
+                                setOwnScrollY(newScrollY);
+                                return true;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return super.onGenericMotionEvent(event);
+    }
+
+    private boolean onScrollTouch(MotionEvent ev) {
+        if (!isScrollingEnabled()) {
+            return false;
+        }
+        if (isInsideQsContainer(ev) && !mIsBeingDragged) {
+            return false;
+        }
+        mForcedScroll = null;
+        initVelocityTrackerIfNotExists();
+        mVelocityTracker.addMovement(ev);
+
+        final int action = ev.getAction();
+
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN: {
+                if (getChildCount() == 0 || !isInContentBounds(ev)) {
+                    return false;
+                }
+                boolean isBeingDragged = !mScroller.isFinished();
+                setIsBeingDragged(isBeingDragged);
+                /*
+                 * If being flinged and user touches, stop the fling. isFinished
+                 * will be false if being flinged.
+                 */
+                if (!mScroller.isFinished()) {
+                    mScroller.forceFinished(true);
+                }
+
+                // Remember where the motion event started
+                mLastMotionY = (int) ev.getY();
+                mDownX = (int) ev.getX();
+                mActivePointerId = ev.getPointerId(0);
+                break;
+            }
+            case MotionEvent.ACTION_MOVE:
+                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (activePointerIndex == -1) {
+                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
+                    break;
+                }
+
+                final int y = (int) ev.getY(activePointerIndex);
+                final int x = (int) ev.getX(activePointerIndex);
+                int deltaY = mLastMotionY - y;
+                final int xDiff = Math.abs(x - mDownX);
+                final int yDiff = Math.abs(deltaY);
+                if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
+                    setIsBeingDragged(true);
+                    if (deltaY > 0) {
+                        deltaY -= mTouchSlop;
+                    } else {
+                        deltaY += mTouchSlop;
+                    }
+                }
+                if (mIsBeingDragged) {
+                    // Scroll to follow the motion event
+                    mLastMotionY = y;
+                    int range = getScrollRange();
+                    if (mExpandedInThisMotion) {
+                        range = Math.min(range, mMaxScrollAfterExpand);
+                    }
+
+                    float scrollAmount;
+                    if (deltaY < 0) {
+                        scrollAmount = overScrollDown(deltaY);
+                    } else {
+                        scrollAmount = overScrollUp(deltaY, range);
+                    }
+
+                    // Calling customOverScrollBy will call onCustomOverScrolled, which
+                    // sets the scrolling if applicable.
+                    if (scrollAmount != 0.0f) {
+                        // The scrolling motion could not be compensated with the
+                        // existing overScroll, we have to scroll the view
+                        customOverScrollBy((int) scrollAmount, mOwnScrollY,
+                                range, getHeight() / 2);
+                        // If we're scrolling, leavebehinds should be dismissed
+                        checkSnoozeLeavebehind();
+                    }
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                if (mIsBeingDragged) {
+                    final VelocityTracker velocityTracker = mVelocityTracker;
+                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+                    int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
+
+                    if (shouldOverScrollFling(initialVelocity)) {
+                        onOverScrollFling(true, initialVelocity);
+                    } else {
+                        if (getChildCount() > 0) {
+                            if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+                                float currentOverScrollTop = getCurrentOverScrollAmount(true);
+                                if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
+                                    fling(-initialVelocity);
+                                } else {
+                                    onOverScrollFling(false, initialVelocity);
+                                }
+                            } else {
+                                if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
+                                        getScrollRange())) {
+                                    animateScroll();
+                                }
+                            }
+                        }
+                    }
+                    mActivePointerId = INVALID_POINTER;
+                    endDrag();
+                }
+
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                if (mIsBeingDragged && getChildCount() > 0) {
+                    if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
+                        animateScroll();
+                    }
+                    mActivePointerId = INVALID_POINTER;
+                    endDrag();
+                }
+                break;
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                final int index = ev.getActionIndex();
+                mLastMotionY = (int) ev.getY(index);
+                mDownX = (int) ev.getX(index);
+                mActivePointerId = ev.getPointerId(index);
+                break;
+            }
+            case MotionEvent.ACTION_POINTER_UP:
+                onSecondaryPointerUp(ev);
+                mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
+                mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
+                break;
+        }
+        return true;
+    }
+
+    protected boolean isInsideQsContainer(MotionEvent ev) {
+        return ev.getY() < mQsContainer.getBottom();
+    }
+
+    private void onOverScrollFling(boolean open, int initialVelocity) {
+        if (mOverscrollTopChangedListener != null) {
+            mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
+        }
+        mDontReportNextOverScroll = true;
+        setOverScrollAmount(0.0f, true, false);
+    }
+
+    /**
+     * Perform a scroll upwards and adapt the overscroll amounts accordingly
+     *
+     * @param deltaY The amount to scroll upwards, has to be positive.
+     * @return The amount of scrolling to be performed by the scroller,
+     *         not handled by the overScroll amount.
+     */
+    private float overScrollUp(int deltaY, int range) {
+        deltaY = Math.max(deltaY, 0);
+        float currentTopAmount = getCurrentOverScrollAmount(true);
+        float newTopAmount = currentTopAmount - deltaY;
+        if (currentTopAmount > 0) {
+            setOverScrollAmount(newTopAmount, true /* onTop */,
+                    false /* animate */);
+        }
+        // Top overScroll might not grab all scrolling motion,
+        // we have to scroll as well.
+        float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
+        float newScrollY = mOwnScrollY + scrollAmount;
+        if (newScrollY > range) {
+            if (!mExpandedInThisMotion) {
+                float currentBottomPixels = getCurrentOverScrolledPixels(false);
+                // We overScroll on the top
+                setOverScrolledPixels(currentBottomPixels + newScrollY - range,
+                        false /* onTop */,
+                        false /* animate */);
+            }
+            setOwnScrollY(range);
+            scrollAmount = 0.0f;
+        }
+        return scrollAmount;
+    }
+
+    /**
+     * Perform a scroll downward and adapt the overscroll amounts accordingly
+     *
+     * @param deltaY The amount to scroll downwards, has to be negative.
+     * @return The amount of scrolling to be performed by the scroller,
+     *         not handled by the overScroll amount.
+     */
+    private float overScrollDown(int deltaY) {
+        deltaY = Math.min(deltaY, 0);
+        float currentBottomAmount = getCurrentOverScrollAmount(false);
+        float newBottomAmount = currentBottomAmount + deltaY;
+        if (currentBottomAmount > 0) {
+            setOverScrollAmount(newBottomAmount, false /* onTop */,
+                    false /* animate */);
+        }
+        // Bottom overScroll might not grab all scrolling motion,
+        // we have to scroll as well.
+        float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
+        float newScrollY = mOwnScrollY + scrollAmount;
+        if (newScrollY < 0) {
+            float currentTopPixels = getCurrentOverScrolledPixels(true);
+            // We overScroll on the top
+            setOverScrolledPixels(currentTopPixels - newScrollY,
+                    true /* onTop */,
+                    false /* animate */);
+            setOwnScrollY(0);
+            scrollAmount = 0.0f;
+        }
+        return scrollAmount;
+    }
+
+    private void onSecondaryPointerUp(MotionEvent ev) {
+        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
+                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+        final int pointerId = ev.getPointerId(pointerIndex);
+        if (pointerId == mActivePointerId) {
+            // This was our active pointer going up. Choose a new
+            // active pointer and adjust accordingly.
+            // TODO: Make this decision more intelligent.
+            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+            mLastMotionY = (int) ev.getY(newPointerIndex);
+            mActivePointerId = ev.getPointerId(newPointerIndex);
+            if (mVelocityTracker != null) {
+                mVelocityTracker.clear();
+            }
+        }
+    }
+
+    private void initVelocityTrackerIfNotExists() {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+    }
+
+    private void recycleVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+    }
+
+    private void initOrResetVelocityTracker() {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        } else {
+            mVelocityTracker.clear();
+        }
+    }
+
+    public void setFinishScrollingCallback(Runnable runnable) {
+        mFinishScrollingCallback = runnable;
+    }
+
+    private void animateScroll() {
+        if (mScroller.computeScrollOffset()) {
+            int oldY = mOwnScrollY;
+            int y = mScroller.getCurrY();
+
+            if (oldY != y) {
+                int range = getScrollRange();
+                if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
+                    float currVelocity = mScroller.getCurrVelocity();
+                    if (currVelocity >= mMinimumVelocity) {
+                        mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
+                    }
+                }
+
+                if (mDontClampNextScroll) {
+                    range = Math.max(range, oldY);
+                }
+                customOverScrollBy(y - oldY, oldY, range,
+                        (int) (mMaxOverScroll));
+            }
+
+            postOnAnimation(mAnimateScroll);
+        } else {
+            mDontClampNextScroll = false;
+            if (mFinishScrollingCallback != null) {
+                mFinishScrollingCallback.run();
+            }
+        }
+    }
+
+    private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
+            int maxOverScrollY) {
+
+        int newScrollY = scrollY + deltaY;
+        final int top = -maxOverScrollY;
+        final int bottom = maxOverScrollY + scrollRangeY;
+
+        boolean clampedY = false;
+        if (newScrollY > bottom) {
+            newScrollY = bottom;
+            clampedY = true;
+        } else if (newScrollY < top) {
+            newScrollY = top;
+            clampedY = true;
+        }
+
+        onCustomOverScrolled(newScrollY, clampedY);
+
+        return clampedY;
+    }
+
+    /**
+     * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
+     * overscroll effect based on numPixels. By default this will also cancel animations on the
+     * same overScroll edge.
+     *
+     * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
+     *                  the rubber-banding logic.
+     * @param onTop Should the effect be applied on top of the scroller.
+     * @param animate Should an animation be performed.
+     */
+    public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
+        setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
+    }
+
+    /**
+     * Set the effective overScroll amount which will be directly reflected in the layout.
+     * By default this will also cancel animations on the same overScroll edge.
+     *
+     * @param amount The amount to overScroll by.
+     * @param onTop Should the effect be applied on top of the scroller.
+     * @param animate Should an animation be performed.
+     */
+    public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
+        setOverScrollAmount(amount, onTop, animate, true);
+    }
+
+    /**
+     * Set the effective overScroll amount which will be directly reflected in the layout.
+     *
+     * @param amount The amount to overScroll by.
+     * @param onTop Should the effect be applied on top of the scroller.
+     * @param animate Should an animation be performed.
+     * @param cancelAnimators Should running animations be cancelled.
+     */
+    public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
+            boolean cancelAnimators) {
+        setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
+    }
+
+    /**
+     * Set the effective overScroll amount which will be directly reflected in the layout.
+     *
+     * @param amount The amount to overScroll by.
+     * @param onTop Should the effect be applied on top of the scroller.
+     * @param animate Should an animation be performed.
+     * @param cancelAnimators Should running animations be cancelled.
+     * @param isRubberbanded The value which will be passed to
+     *                     {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
+     */
+    public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
+            boolean cancelAnimators, boolean isRubberbanded) {
+        if (cancelAnimators) {
+            mStateAnimator.cancelOverScrollAnimators(onTop);
+        }
+        setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
+    }
+
+    private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
+            boolean isRubberbanded) {
+        amount = Math.max(0, amount);
+        if (animate) {
+            mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
+        } else {
+            setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
+            mAmbientState.setOverScrollAmount(amount, onTop);
+            if (onTop) {
+                notifyOverscrollTopListener(amount, isRubberbanded);
+            }
+            requestChildrenUpdate();
+        }
+    }
+
+    private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
+        mExpandHelper.onlyObserveMovements(amount > 1.0f);
+        if (mDontReportNextOverScroll) {
+            mDontReportNextOverScroll = false;
+            return;
+        }
+        if (mOverscrollTopChangedListener != null) {
+            mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
+        }
+    }
+
+    public void setOverscrollTopChangedListener(
+            OnOverscrollTopChangedListener overscrollTopChangedListener) {
+        mOverscrollTopChangedListener = overscrollTopChangedListener;
+    }
+
+    public float getCurrentOverScrollAmount(boolean top) {
+        return mAmbientState.getOverScrollAmount(top);
+    }
+
+    public float getCurrentOverScrolledPixels(boolean top) {
+        return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
+    }
+
+    private void setOverScrolledPixels(float amount, boolean onTop) {
+        if (onTop) {
+            mOverScrolledTopPixels = amount;
+        } else {
+            mOverScrolledBottomPixels = amount;
+        }
+    }
+
+    private void onCustomOverScrolled(int scrollY, boolean clampedY) {
+        // Treat animating scrolls differently; see #computeScroll() for why.
+        if (!mScroller.isFinished()) {
+            setOwnScrollY(scrollY);
+            if (clampedY) {
+                springBack();
+            } else {
+                float overScrollTop = getCurrentOverScrollAmount(true);
+                if (mOwnScrollY < 0) {
+                    notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
+                } else {
+                    notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
+                }
+            }
+        } else {
+            setOwnScrollY(scrollY);
+        }
+    }
+
+    private void springBack() {
+        int scrollRange = getScrollRange();
+        boolean overScrolledTop = mOwnScrollY <= 0;
+        boolean overScrolledBottom = mOwnScrollY >= scrollRange;
+        if (overScrolledTop || overScrolledBottom) {
+            boolean onTop;
+            float newAmount;
+            if (overScrolledTop) {
+                onTop = true;
+                newAmount = -mOwnScrollY;
+                setOwnScrollY(0);
+                mDontReportNextOverScroll = true;
+            } else {
+                onTop = false;
+                newAmount = mOwnScrollY - scrollRange;
+                setOwnScrollY(scrollRange);
+            }
+            setOverScrollAmount(newAmount, onTop, false);
+            setOverScrollAmount(0.0f, onTop, true);
+            mScroller.forceFinished(true);
+        }
+    }
+
+    private int getScrollRange() {
+        // In current design, it only use the top HUN to treat all of HUNs
+        // although there are more than one HUNs
+        int contentHeight = mContentHeight;
+        if (!isExpanded() && mHeadsUpManager.hasPinnedHeadsUp()) {
+            contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
+        }
+        int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
+        int imeInset = getImeInset();
+        scrollRange += Math.min(imeInset, Math.max(0, contentHeight - (getHeight() - imeInset)));
+        return scrollRange;
+    }
+
+    private int getImeInset() {
+        return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
+    }
+
+    /**
+     * @return the first child which has visibility unequal to GONE
+     */
+    public ExpandableView getFirstChildNotGone() {
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != View.GONE && child != mShelf) {
+                return (ExpandableView) child;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return the child before the given view which has visibility unequal to GONE
+     */
+    public ExpandableView getViewBeforeView(ExpandableView view) {
+        ExpandableView previousView = null;
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child == view) {
+                return previousView;
+            }
+            if (child.getVisibility() != View.GONE) {
+                previousView = (ExpandableView) child;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return The first child which has visibility unequal to GONE which is currently below the
+     *         given translationY or equal to it.
+     */
+    private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() == View.GONE) {
+                continue;
+            }
+            float rowTranslation = child.getTranslationY();
+            if (rowTranslation >= translationY) {
+                return child;
+            } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
+                    List<ExpandableNotificationRow> notificationChildren =
+                            row.getNotificationChildren();
+                    for (int childIndex = 0; childIndex < notificationChildren.size();
+                            childIndex++) {
+                        ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
+                        if (rowChild.getTranslationY() + rowTranslation >= translationY) {
+                            return rowChild;
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return the last child which has visibility unequal to GONE
+     */
+    public View getLastChildNotGone() {
+        int childCount = getChildCount();
+        for (int i = childCount - 1; i >= 0; i--) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != View.GONE && child != mShelf) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return the number of children which have visibility unequal to GONE
+     */
+    public int getNotGoneChildCount() {
+        int childCount = getChildCount();
+        int count = 0;
+        for (int i = 0; i < childCount; i++) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    private void updateContentHeight() {
+        int height = 0;
+        float previousPaddingRequest = mPaddingBetweenElements;
+        float previousPaddingAmount = 0.0f;
+        int numShownItems = 0;
+        boolean finish = false;
+        int maxDisplayedNotifications = mAmbientState.isFullyDark()
+                ? (hasPulsingNotifications() ? 1 : 0)
+                : mMaxDisplayedNotifications;
+
+        for (int i = 0; i < getChildCount(); i++) {
+            ExpandableView expandableView = (ExpandableView) getChildAt(i);
+            boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
+            if (expandableView.getVisibility() != View.GONE
+                    && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
+                boolean limitReached = maxDisplayedNotifications != -1
+                        && numShownItems >= maxDisplayedNotifications;
+                boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark()
+                        && hasPulsingNotifications()
+                        && expandableView instanceof ExpandableNotificationRow
+                        && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
+                if (limitReached || notificationOnAmbientThatIsNotPulsing) {
+                    expandableView = mShelf;
+                    finish = true;
+                }
+                float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
+                float padding;
+                if (increasedPaddingAmount >= 0.0f) {
+                    padding = (int) NotificationUtils.interpolate(
+                            previousPaddingRequest,
+                            mIncreasedPaddingBetweenElements,
+                            increasedPaddingAmount);
+                    previousPaddingRequest = (int) NotificationUtils.interpolate(
+                            mPaddingBetweenElements,
+                            mIncreasedPaddingBetweenElements,
+                            increasedPaddingAmount);
+                } else {
+                    int ownPadding = (int) NotificationUtils.interpolate(
+                            0,
+                            mPaddingBetweenElements,
+                            1.0f + increasedPaddingAmount);
+                    if (previousPaddingAmount > 0.0f) {
+                        padding = (int) NotificationUtils.interpolate(
+                                ownPadding,
+                                mIncreasedPaddingBetweenElements,
+                                previousPaddingAmount);
+                    } else {
+                        padding = ownPadding;
+                    }
+                    previousPaddingRequest = ownPadding;
+                }
+                if (height != 0) {
+                    height += padding;
+                }
+                previousPaddingAmount = increasedPaddingAmount;
+                height += expandableView.getIntrinsicHeight();
+                numShownItems++;
+                if (finish) {
+                    break;
+                }
+            }
+        }
+        mIntrinsicContentHeight = height;
+
+        // We don't want to use the toppadding since that might be interpolated and we want
+        // to take the final value of the animation.
+        int topPadding = mAmbientState.isFullyDark() ? mDarkTopPadding : mRegularTopPadding;
+        mContentHeight = height + topPadding + mBottomMargin;
+        updateScrollability();
+        clampScrollPosition();
+        mAmbientState.setLayoutMaxHeight(mContentHeight);
+    }
+
+    private boolean isPulsing(NotificationData.Entry entry) {
+        return mAmbientState.isPulsing(entry);
+    }
+
+    @Override
+    public boolean hasPulsingNotifications() {
+        return mPulsing;
+    }
+
+    private void updateScrollability() {
+        boolean scrollable = !mQsExpanded && getScrollRange() > 0;
+        if (scrollable != mScrollable) {
+            mScrollable = scrollable;
+            setFocusable(scrollable);
+            updateForwardAndBackwardScrollability();
+        }
+    }
+
+    private void updateForwardAndBackwardScrollability() {
+        boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
+        boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
+        boolean changed = forwardScrollable != mForwardScrollable
+                || backwardsScrollable != mBackwardScrollable;
+        mForwardScrollable = forwardScrollable;
+        mBackwardScrollable = backwardsScrollable;
+        if (changed) {
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        }
+    }
+
+    private void updateBackground() {
+        // No need to update the background color if it's not being drawn.
+        if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
+            return;
+        }
+
+        updateBackgroundBounds();
+        if (!mCurrentBounds.equals(mBackgroundBounds)) {
+            boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
+                    || areBoundsAnimating();
+            if (!isExpanded()) {
+                abortBackgroundAnimators();
+                animate = false;
+            }
+            if (animate) {
+                startBackgroundAnimation();
+            } else {
+                mCurrentBounds.set(mBackgroundBounds);
+                applyCurrentBackgroundBounds();
+            }
+        } else {
+            abortBackgroundAnimators();
+        }
+        mAnimateNextBackgroundBottom = false;
+        mAnimateNextBackgroundTop = false;
+    }
+
+    private void abortBackgroundAnimators() {
+        if (mBottomAnimator != null) {
+            mBottomAnimator.cancel();
+        }
+        if (mTopAnimator != null) {
+            mTopAnimator.cancel();
+        }
+    }
+
+    private boolean areBoundsAnimating() {
+        return mBottomAnimator != null || mTopAnimator != null;
+    }
+
+    private void startBackgroundAnimation() {
+        // left and right are always instantly applied
+        mCurrentBounds.left = mBackgroundBounds.left;
+        mCurrentBounds.right = mBackgroundBounds.right;
+        startBottomAnimation();
+        startTopAnimation();
+    }
+
+    private void startTopAnimation() {
+        int previousEndValue = mEndAnimationRect.top;
+        int newEndValue = mBackgroundBounds.top;
+        ObjectAnimator previousAnimator = mTopAnimator;
+        if (previousAnimator != null && previousEndValue == newEndValue) {
+            return;
+        }
+        if (!mAnimateNextBackgroundTop) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                int previousStartValue = mStartAnimationRect.top;
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                values[0].setIntValues(previousStartValue, newEndValue);
+                mStartAnimationRect.top = previousStartValue;
+                mEndAnimationRect.top = newEndValue;
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                setBackgroundTop(newEndValue);
+                return;
+            }
+        }
+        if (previousAnimator != null) {
+            previousAnimator.cancel();
+        }
+        ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
+                mCurrentBounds.top, newEndValue);
+        Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
+        animator.setInterpolator(interpolator);
+        animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mStartAnimationRect.top = -1;
+                mEndAnimationRect.top = -1;
+                mTopAnimator = null;
+            }
+        });
+        animator.start();
+        mStartAnimationRect.top = mCurrentBounds.top;
+        mEndAnimationRect.top = newEndValue;
+        mTopAnimator = animator;
+    }
+
+    private void startBottomAnimation() {
+        int previousStartValue = mStartAnimationRect.bottom;
+        int previousEndValue = mEndAnimationRect.bottom;
+        int newEndValue = mBackgroundBounds.bottom;
+        ObjectAnimator previousAnimator = mBottomAnimator;
+        if (previousAnimator != null && previousEndValue == newEndValue) {
+            return;
+        }
+        if (!mAnimateNextBackgroundBottom) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                values[0].setIntValues(previousStartValue, newEndValue);
+                mStartAnimationRect.bottom = previousStartValue;
+                mEndAnimationRect.bottom = newEndValue;
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                setBackgroundBottom(newEndValue);
+                return;
+            }
+        }
+        if (previousAnimator != null) {
+            previousAnimator.cancel();
+        }
+        ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
+                mCurrentBounds.bottom, newEndValue);
+        Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
+        animator.setInterpolator(interpolator);
+        animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mStartAnimationRect.bottom = -1;
+                mEndAnimationRect.bottom = -1;
+                mBottomAnimator = null;
+            }
+        });
+        animator.start();
+        mStartAnimationRect.bottom = mCurrentBounds.bottom;
+        mEndAnimationRect.bottom = newEndValue;
+        mBottomAnimator = animator;
+    }
+
+    private void setBackgroundTop(int top) {
+        mCurrentBounds.top = top;
+        applyCurrentBackgroundBounds();
+    }
+
+    public void setBackgroundBottom(int bottom) {
+        mCurrentBounds.bottom = bottom;
+        applyCurrentBackgroundBounds();
+    }
+
+    private void applyCurrentBackgroundBounds() {
+        // If the background of the notification is not being drawn, then there is no need to
+        // exclude an area in the scrim. Rather, the scrim's color should serve as the background.
+        if (!mShouldDrawNotificationBackground) {
+            return;
+        }
+
+        final boolean awake = mInterpolatedDarkAmount != 0 || mAmbientState.isDark();
+        mScrimController.setExcludedBackgroundArea(
+                mFadingOut || mParentNotFullyVisible || awake || mIsClipped ? null
+                        : mCurrentBounds);
+        invalidate();
+    }
+
+    /**
+     * Update the background bounds to the new desired bounds
+     */
+    private void updateBackgroundBounds() {
+        getLocationInWindow(mTempInt2);
+        mBackgroundBounds.left = mTempInt2[0] + mSidePaddings;
+        mBackgroundBounds.right = mTempInt2[0] + getWidth() - mSidePaddings;
+
+        if (!mIsExpanded) {
+            mBackgroundBounds.top = 0;
+            mBackgroundBounds.bottom = 0;
+            return;
+        }
+        ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
+        int top = 0;
+        if (firstView != null) {
+            // Round Y up to avoid seeing the background during animation
+            int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
+            if (mAnimateNextBackgroundTop
+                    || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
+                    || mTopAnimator != null && mEndAnimationRect.top == 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());
+            }
+        }
+        ActivatableNotificationView lastView =
+                mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
+                        ? mShelf
+                        : mLastVisibleBackgroundChild;
+        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
+                    || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
+                    || mBottomAnimator != null && mEndAnimationRect.bottom == 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());
+            }
+        } else {
+            top = mTopPadding;
+            bottom = top;
+        }
+        if (mStatusBarState != StatusBarState.KEYGUARD) {
+            top = (int) Math.max(mTopPadding + mStackTranslation, top);
+        } else {
+            // otherwise the animation from the shade to the keyguard will jump as it's maxed
+            top = Math.max(0, top);
+        }
+        mBackgroundBounds.top = top;
+        mBackgroundBounds.bottom = Math.max(bottom, top);
+    }
+
+    private ActivatableNotificationView getFirstPinnedHeadsUp() {
+        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()) {
+                    return row;
+                }
+            }
+        }
+        return null;
+    }
+
+    private ActivatableNotificationView getLastChildWithBackground() {
+        int childCount = getChildCount();
+        for (int i = childCount - 1; i >= 0; i--) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
+                    && child != mShelf) {
+                return (ActivatableNotificationView) child;
+            }
+        }
+        return null;
+    }
+
+    private ActivatableNotificationView getFirstChildWithBackground() {
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
+                    && child != mShelf) {
+                return (ActivatableNotificationView) child;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Fling the scroll view
+     *
+     * @param velocityY The initial velocity in the Y direction. Positive
+     *                  numbers mean that the finger/cursor is moving down the screen,
+     *                  which means we want to scroll towards the top.
+     */
+    protected void fling(int velocityY) {
+        if (getChildCount() > 0) {
+            int scrollRange = getScrollRange();
+
+            float topAmount = getCurrentOverScrollAmount(true);
+            float bottomAmount = getCurrentOverScrollAmount(false);
+            if (velocityY < 0 && topAmount > 0) {
+                setOwnScrollY(mOwnScrollY - (int) topAmount);
+                mDontReportNextOverScroll = true;
+                setOverScrollAmount(0, true, false);
+                mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
+                        * mOverflingDistance + topAmount;
+            } else if (velocityY > 0 && bottomAmount > 0) {
+                setOwnScrollY((int) (mOwnScrollY + bottomAmount));
+                setOverScrollAmount(0, false, false);
+                mMaxOverScroll = Math.abs(velocityY) / 1000f
+                        * getRubberBandFactor(false /* onTop */) * mOverflingDistance
+                        +  bottomAmount;
+            } else {
+                // it will be set once we reach the boundary
+                mMaxOverScroll = 0.0f;
+            }
+            int minScrollY = Math.max(0, scrollRange);
+            if (mExpandedInThisMotion) {
+                minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
+            }
+            mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
+                    mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
+
+            animateScroll();
+        }
+    }
+
+    /**
+     * @return Whether a fling performed on the top overscroll edge lead to the expanded
+     * overScroll view (i.e QS).
+     */
+    private boolean shouldOverScrollFling(int initialVelocity) {
+        float topOverScroll = getCurrentOverScrollAmount(true);
+        return mScrolledToTopOnFirstDown
+                && !mExpandedInThisMotion
+                && topOverScroll > mMinTopOverScrollToEscape
+                && initialVelocity > 0;
+    }
+
+    /**
+     * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
+     * account.
+     *
+     * @param qsHeight the top padding imposed by the quick settings panel
+     * @param animate whether to animate the change
+     * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
+     *                               {@code qsHeight} is the final top padding
+     */
+    public void updateTopPadding(float qsHeight, boolean animate,
+            boolean ignoreIntrinsicPadding) {
+        int topPadding = (int) qsHeight;
+        int minStackHeight = getLayoutMinHeight();
+        if (topPadding + minStackHeight > getHeight()) {
+            mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
+        } else {
+            mTopPaddingOverflow = 0;
+        }
+        setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
+                animate);
+        setExpandedHeight(mExpandedHeight);
+    }
+
+    public void setMaxTopPadding(int maxTopPadding) {
+        mMaxTopPadding = maxTopPadding;
+    }
+
+    public int getLayoutMinHeight() {
+        if (isHeadsUpTransition()) {
+            return getTopHeadsUpPinnedHeight();
+        }
+        return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
+    }
+
+    public int getFirstChildIntrinsicHeight() {
+        final ExpandableView firstChild = getFirstChildNotGone();
+        int firstChildMinHeight = firstChild != null
+                ? firstChild.getIntrinsicHeight()
+                : mEmptyShadeView != null
+                        ? mEmptyShadeView.getIntrinsicHeight()
+                        : mCollapsedSize;
+        if (mOwnScrollY > 0) {
+            firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
+        }
+        return firstChildMinHeight;
+    }
+
+    public float getTopPaddingOverflow() {
+        return mTopPaddingOverflow;
+    }
+
+    public int getPeekHeight() {
+        final ExpandableView firstChild = getFirstChildNotGone();
+        final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
+                : mCollapsedSize;
+        int shelfHeight = 0;
+        if (mLastVisibleBackgroundChild != null && mShelf.getVisibility() != GONE) {
+            shelfHeight = mShelf.getIntrinsicHeight();
+        }
+        return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
+    }
+
+    private int clampPadding(int desiredPadding) {
+        return Math.max(desiredPadding, mIntrinsicPadding);
+    }
+
+    private float getRubberBandFactor(boolean onTop) {
+        if (!onTop) {
+            return RUBBER_BAND_FACTOR_NORMAL;
+        }
+        if (mExpandedInThisMotion) {
+            return RUBBER_BAND_FACTOR_AFTER_EXPAND;
+        } else if (mIsExpansionChanging || mPanelTracking) {
+            return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
+        } else if (mScrolledToTopOnFirstDown) {
+            return 1.0f;
+        }
+        return RUBBER_BAND_FACTOR_NORMAL;
+    }
+
+    /**
+     * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
+     * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
+     * overscroll view (e.g. expand QS).
+     */
+    private boolean isRubberbanded(boolean onTop) {
+        return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
+                || !mScrolledToTopOnFirstDown;
+    }
+
+    private void endDrag() {
+        setIsBeingDragged(false);
+
+        recycleVelocityTracker();
+
+        if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
+            setOverScrollAmount(0, true /* onTop */, true /* animate */);
+        }
+        if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
+            setOverScrollAmount(0, false /* onTop */, true /* animate */);
+        }
+    }
+
+    private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
+        ev.offsetLocation(sourceView.getX(), sourceView.getY());
+        ev.offsetLocation(-targetView.getX(), -targetView.getY());
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        initDownStates(ev);
+        handleEmptySpaceClick(ev);
+        boolean expandWantsIt = false;
+        if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
+            expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
+        }
+        boolean scrollWantsIt = false;
+        if (!mSwipingInProgress && !mExpandingNotification) {
+            scrollWantsIt = onInterceptTouchEventScroll(ev);
+        }
+        boolean swipeWantsIt = false;
+        if (!mIsBeingDragged
+                && !mExpandingNotification
+                && !mExpandedInThisMotion
+                && !mOnlyScrollingInThisMotion
+                && !mDisallowDismissInThisMotion) {
+            swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
+        }
+        // Check if we need to clear any snooze leavebehinds
+        boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
+        NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
+        if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
+                && !scrollWantsIt) {
+            mCheckForLeavebehind = false;
+            mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
+        }
+        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+            mCheckForLeavebehind = true;
+        }
+        return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
+    }
+
+    private void handleEmptySpaceClick(MotionEvent ev) {
+        switch (ev.getActionMasked()) {
+            case MotionEvent.ACTION_MOVE:
+                if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
+                        || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
+                    mTouchIsClick = false;
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
+                        isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
+                    mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
+                }
+                break;
+        }
+    }
+
+    private void initDownStates(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mExpandedInThisMotion = false;
+            mOnlyScrollingInThisMotion = !mScroller.isFinished();
+            mDisallowScrollingInThisMotion = false;
+            mDisallowDismissInThisMotion = false;
+            mTouchIsClick = true;
+            mInitialTouchX = ev.getX();
+            mInitialTouchY = ev.getY();
+        }
+    }
+
+    public void setChildTransferInProgress(boolean childTransferInProgress) {
+        mChildTransferInProgress = childTransferInProgress;
+    }
+
+    @Override
+    public void onViewRemoved(View child) {
+        super.onViewRemoved(child);
+        // we only call our internal methods if this is actually a removal and not just a
+        // notification which becomes a child notification
+        if (!mChildTransferInProgress) {
+            onViewRemovedInternal(child, this);
+        }
+    }
+
+    @Override
+    public void cleanUpViewState(View child) {
+        if (child == mTranslatingParentView) {
+            mTranslatingParentView = null;
+        }
+        mCurrentStackScrollState.removeViewStateForView(child);
+    }
+
+    @Override
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        super.requestDisallowInterceptTouchEvent(disallowIntercept);
+        if (disallowIntercept) {
+            cancelLongPress();
+        }
+    }
+
+    private void onViewRemovedInternal(View child, ViewGroup container) {
+        if (mChangePositionInProgress) {
+            // This is only a position change, don't do anything special
+            return;
+        }
+        ExpandableView expandableView = (ExpandableView) child;
+        expandableView.setOnHeightChangedListener(null);
+        mCurrentStackScrollState.removeViewStateForView(child);
+        updateScrollStateForRemovedChild(expandableView);
+        boolean animationGenerated = generateRemoveAnimation(child);
+        if (animationGenerated) {
+            if (!mSwipedOutViews.contains(child)
+                    || Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
+                container.addTransientView(child, 0);
+                expandableView.setTransientContainer(container);
+            }
+        } else {
+            mSwipedOutViews.remove(child);
+        }
+        updateAnimationState(false, child);
+
+        focusNextViewIfFocused(child);
+    }
+
+    private void focusNextViewIfFocused(View view) {
+        if (view instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+            if (row.shouldRefocusOnDismiss()) {
+                View nextView = row.getChildAfterViewWhenDismissed();
+                if (nextView == null) {
+                    View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
+                    nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
+                            ? groupParentWhenDismissed.getTranslationY()
+                            : view.getTranslationY(), true /* ignoreChildren */);
+                }
+                if (nextView != null) {
+                    nextView.requestAccessibilityFocus();
+                }
+            }
+        }
+
+    }
+
+    private boolean isChildInGroup(View child) {
+        return child instanceof ExpandableNotificationRow
+                && mGroupManager.isChildInGroupWithSummary(
+                        ((ExpandableNotificationRow) child).getStatusBarNotification());
+    }
+
+    /**
+     * Generate a remove animation for a child view.
+     *
+     * @param child The view to generate the remove animation for.
+     * @return Whether an animation was generated.
+     */
+    private boolean generateRemoveAnimation(View child) {
+        if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
+            mAddedHeadsUpChildren.remove(child);
+            return false;
+        }
+        if (isClickedHeadsUp(child)) {
+            // An animation is already running, add it transiently
+            mClearTransientViewsWhenFinished.add((ExpandableView) child);
+            return true;
+        }
+        if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
+            if (!mChildrenToAddAnimated.contains(child)) {
+                // Generate Animations
+                mChildrenToRemoveAnimated.add(child);
+                mNeedsAnimation = true;
+                return true;
+            } else {
+                mChildrenToAddAnimated.remove(child);
+                mFromMoreCardAdditions.remove(child);
+                return false;
+            }
+        }
+        return false;
+    }
+
+    private boolean isClickedHeadsUp(View child) {
+        return HeadsUpUtil.isClickedHeadsUpNotification(child);
+    }
+
+    /**
+     * Remove a removed child view from the heads up animations if it was just added there
+     *
+     * @return whether any child was removed from the list to animate
+     */
+    private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
+        boolean hasAddEvent = false;
+        for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
+            ExpandableNotificationRow row = eventPair.first;
+            boolean isHeadsUp = eventPair.second;
+            if (child == row) {
+                mTmpList.add(eventPair);
+                hasAddEvent |= isHeadsUp;
+            }
+        }
+        if (hasAddEvent) {
+            // This child was just added lets remove all events.
+            mHeadsUpChangeAnimations.removeAll(mTmpList);
+            ((ExpandableNotificationRow ) child).setHeadsUpAnimatingAway(false);
+        }
+        mTmpList.clear();
+        return hasAddEvent;
+    }
+
+    /**
+     * @param child the child to query
+     * @return whether a view is not a top level child but a child notification and that group is
+     *         not expanded
+     */
+    private boolean isChildInInvisibleGroup(View child) {
+        if (child instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+            ExpandableNotificationRow groupSummary =
+                    mGroupManager.getGroupSummary(row.getStatusBarNotification());
+            if (groupSummary != null && groupSummary != row) {
+                return row.getVisibility() == View.INVISIBLE;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Updates the scroll position when a child was removed
+     *
+     * @param removedChild the removed child
+     */
+    private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
+        int startingPosition = getPositionInLinearLayout(removedChild);
+        float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
+        int padding;
+        if (increasedPaddingAmount >= 0) {
+            padding = (int) NotificationUtils.interpolate(
+                    mPaddingBetweenElements,
+                    mIncreasedPaddingBetweenElements,
+                    increasedPaddingAmount);
+        } else {
+            padding = (int) NotificationUtils.interpolate(
+                    0,
+                    mPaddingBetweenElements,
+                    1.0f + increasedPaddingAmount);
+        }
+        int childHeight = getIntrinsicHeight(removedChild) + padding;
+        int endPosition = startingPosition + childHeight;
+        if (endPosition <= mOwnScrollY) {
+            // This child is fully scrolled of the top, so we have to deduct its height from the
+            // scrollPosition
+            setOwnScrollY(mOwnScrollY - childHeight);
+        } else if (startingPosition < mOwnScrollY) {
+            // This child is currently being scrolled into, set the scroll position to the start of
+            // this child
+            setOwnScrollY(startingPosition);
+        }
+    }
+
+    private int getIntrinsicHeight(View view) {
+        if (view instanceof ExpandableView) {
+            ExpandableView expandableView = (ExpandableView) view;
+            return expandableView.getIntrinsicHeight();
+        }
+        return view.getHeight();
+    }
+
+    public int getPositionInLinearLayout(View requestedView) {
+        ExpandableNotificationRow childInGroup = null;
+        ExpandableNotificationRow requestedRow = null;
+        if (isChildInGroup(requestedView)) {
+            // We're asking for a child in a group. Calculate the position of the parent first,
+            // then within the parent.
+            childInGroup = (ExpandableNotificationRow) requestedView;
+            requestedView = requestedRow = childInGroup.getNotificationParent();
+        }
+        int position = 0;
+        float previousPaddingRequest = mPaddingBetweenElements;
+        float previousPaddingAmount = 0.0f;
+        for (int i = 0; i < getChildCount(); i++) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            boolean notGone = child.getVisibility() != View.GONE;
+            if (notGone && !child.hasNoContentHeight()) {
+                float increasedPaddingAmount = child.getIncreasedPaddingAmount();
+                float padding;
+                if (increasedPaddingAmount >= 0.0f) {
+                    padding = (int) NotificationUtils.interpolate(
+                            previousPaddingRequest,
+                            mIncreasedPaddingBetweenElements,
+                            increasedPaddingAmount);
+                    previousPaddingRequest = (int) NotificationUtils.interpolate(
+                            mPaddingBetweenElements,
+                            mIncreasedPaddingBetweenElements,
+                            increasedPaddingAmount);
+                } else {
+                    int ownPadding = (int) NotificationUtils.interpolate(
+                            0,
+                            mPaddingBetweenElements,
+                            1.0f + increasedPaddingAmount);
+                    if (previousPaddingAmount > 0.0f) {
+                        padding = (int) NotificationUtils.interpolate(
+                                ownPadding,
+                                mIncreasedPaddingBetweenElements,
+                                previousPaddingAmount);
+                    } else {
+                        padding = ownPadding;
+                    }
+                    previousPaddingRequest = ownPadding;
+                }
+                if (position != 0) {
+                    position += padding;
+                }
+                previousPaddingAmount = increasedPaddingAmount;
+            }
+            if (child == requestedView) {
+                if (requestedRow != null) {
+                    position += requestedRow.getPositionOfChild(childInGroup);
+                }
+                return position;
+            }
+            if (notGone) {
+                position += getIntrinsicHeight(child);
+            }
+        }
+        return 0;
+    }
+
+    @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        onViewAddedInternal(child);
+    }
+
+    private void updateFirstAndLastBackgroundViews() {
+        ActivatableNotificationView firstChild = getFirstChildWithBackground();
+        ActivatableNotificationView lastChild = getLastChildWithBackground();
+        if (mAnimationsEnabled && mIsExpanded) {
+            mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
+            mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
+        } else {
+            mAnimateNextBackgroundTop = false;
+            mAnimateNextBackgroundBottom = false;
+        }
+        mFirstVisibleBackgroundChild = firstChild;
+        mLastVisibleBackgroundChild = lastChild;
+        mAmbientState.setLastVisibleBackgroundChild(lastChild);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirstVisibleBackgroundChild,
+                mLastVisibleBackgroundChild);
+        invalidate();
+    }
+
+    private void onViewAddedInternal(View child) {
+        updateHideSensitiveForChild(child);
+        ((ExpandableView) child).setOnHeightChangedListener(this);
+        generateAddAnimation(child, false /* fromMoreCard */);
+        updateAnimationState(child);
+        updateChronometerForChild(child);
+    }
+
+    private void updateHideSensitiveForChild(View child) {
+        if (child instanceof ExpandableView) {
+            ExpandableView expandableView = (ExpandableView) child;
+            expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
+        }
+    }
+
+    @Override
+    public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
+        onViewRemovedInternal(row, childrenContainer);
+    }
+
+    @Override
+    public void notifyGroupChildAdded(View row) {
+        onViewAddedInternal(row);
+    }
+
+    public void setAnimationsEnabled(boolean animationsEnabled) {
+        mAnimationsEnabled = animationsEnabled;
+        updateNotificationAnimationStates();
+        if (!animationsEnabled) {
+            mSwipedOutViews.clear();
+            mChildrenToRemoveAnimated.clear();
+            clearTemporaryViewsInGroup(this);
+        }
+    }
+
+    private void updateNotificationAnimationStates() {
+        boolean running = mAnimationsEnabled || hasPulsingNotifications();
+        mShelf.setAnimationsEnabled(running);
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            running &= mIsExpanded || isPinnedHeadsUp(child);
+            updateAnimationState(running, child);
+        }
+    }
+
+    private void updateAnimationState(View child) {
+        updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
+                && (mIsExpanded || isPinnedHeadsUp(child)), child);
+    }
+
+    @Override
+    public void setExpandingNotification(ExpandableNotificationRow row) {
+        mAmbientState.setExpandingNotification(row);
+        requestChildrenUpdate();
+    }
+
+    @Override
+    public void bindRow(ExpandableNotificationRow row) {
+        row.setHeadsUpAnimatingAwayListener(animatingAway -> {
+            mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
+            mHeadsUpAppearanceController.updateHeader(row.getEntry());
+        });
+    }
+
+    @Override
+    public void applyExpandAnimationParams(ExpandAnimationParameters params) {
+        mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
+        requestChildrenUpdate();
+    }
+
+    private void updateAnimationState(boolean running, View child) {
+        if (child instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+            row.setIconAnimationRunning(running);
+        }
+    }
+
+    public boolean isAddOrRemoveAnimationPending() {
+        return mNeedsAnimation
+                && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
+    }
+
+    @Override
+    public void generateAddAnimation(View child, boolean fromMoreCard) {
+        if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
+            // Generate Animations
+            mChildrenToAddAnimated.add(child);
+            if (fromMoreCard) {
+                mFromMoreCardAdditions.add(child);
+            }
+            mNeedsAnimation = true;
+        }
+        if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
+            mAddedHeadsUpChildren.add(child);
+            mChildrenToAddAnimated.remove(child);
+        }
+    }
+
+    @Override
+    public void changeViewPosition(View child, int newIndex) {
+        int currentIndex = indexOfChild(child);
+
+        if (currentIndex == -1) {
+            boolean isTransient = false;
+            if (child instanceof ExpandableNotificationRow
+                    && ((ExpandableNotificationRow)child).getTransientContainer() != null) {
+                isTransient = true;
+            }
+            Log.e(TAG, "Attempting to re-position "
+                    + (isTransient ? "transient" : "")
+                    + " view {"
+                    + child
+                    + "}");
+            return;
+        }
+
+        if (child != null && child.getParent() == this && currentIndex != newIndex) {
+            mChangePositionInProgress = true;
+            ((ExpandableView)child).setChangingPosition(true);
+            removeView(child);
+            addView(child, newIndex);
+            ((ExpandableView)child).setChangingPosition(false);
+            mChangePositionInProgress = false;
+            if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
+                mChildrenChangingPositions.add(child);
+                mNeedsAnimation = true;
+            }
+        }
+    }
+
+    private void startAnimationToState() {
+        if (mNeedsAnimation) {
+            generateAllAnimationEvents();
+            mNeedsAnimation = false;
+        }
+        if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
+            setAnimationRunning(true);
+            mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
+                    mGoToFullShadeDelay);
+            mAnimationEvents.clear();
+            updateBackground();
+            updateViewShadows();
+            updateClippingToTopRoundedCorner();
+        } else {
+            applyCurrentState();
+        }
+        mGoToFullShadeDelay = 0;
+    }
+
+    private void generateAllAnimationEvents() {
+        generateHeadsUpAnimationEvents();
+        generateChildRemovalEvents();
+        generateChildAdditionEvents();
+        generatePositionChangeEvents();
+        generateSnapBackEvents();
+        generateDragEvents();
+        generateTopPaddingEvent();
+        generateActivateEvent();
+        generateDimmedEvent();
+        generateHideSensitiveEvent();
+        generateDarkEvent();
+        generateGoToFullShadeEvent();
+        generateViewResizeEvent();
+        generateGroupExpansionEvent();
+        generateAnimateEverythingEvent();
+        generatePulsingAnimationEvent();
+    }
+
+    private void generateHeadsUpAnimationEvents() {
+        for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
+            ExpandableNotificationRow row = eventPair.first;
+            boolean isHeadsUp = eventPair.second;
+            int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
+            boolean onBottom = false;
+            boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
+            if (!mIsExpanded && !isHeadsUp) {
+                type = row.wasJustClicked()
+                        ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+                        : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
+                if (row.isChildInGroup()) {
+                    // We can otherwise get stuck in there if it was just isolated
+                    row.setHeadsUpAnimatingAway(false);
+                    continue;
+                }
+            } else {
+                ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
+                if (viewState == null) {
+                    // A view state was never generated for this view, so we don't need to animate
+                    // this. This may happen with notification children.
+                    continue;
+                }
+                if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
+                    if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
+                        // Our custom add animation
+                        type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
+                    } else {
+                        // Normal add animation
+                        type = AnimationEvent.ANIMATION_TYPE_ADD;
+                    }
+                    onBottom = !pinnedAndClosed;
+                }
+            }
+            AnimationEvent event = new AnimationEvent(row, type);
+            event.headsUpFromBottom = onBottom;
+            mAnimationEvents.add(event);
+        }
+        mHeadsUpChangeAnimations.clear();
+        mAddedHeadsUpChildren.clear();
+    }
+
+    private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
+        if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
+            return false;
+        }
+        return true;
+    }
+
+    private void generateGroupExpansionEvent() {
+        // Generate a group expansion/collapsing event if there is such a group at all
+        if (mExpandedGroupView != null) {
+            mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
+                    AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
+            mExpandedGroupView = null;
+        }
+    }
+
+    private void generateViewResizeEvent() {
+        if (mNeedViewResizeAnimation) {
+            boolean hasDisappearAnimation = false;
+            for (AnimationEvent animationEvent : mAnimationEvents) {
+                final int type = animationEvent.animationType;
+                if (type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+                    || type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
+                    hasDisappearAnimation = true;
+                    break;
+                }
+            }
+
+            if (!hasDisappearAnimation) {
+                mAnimationEvents.add(
+                        new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
+            }
+        }
+        mNeedViewResizeAnimation = false;
+    }
+
+    private void generateSnapBackEvents() {
+        for (View child : mSnappedBackChildren) {
+            mAnimationEvents.add(new AnimationEvent(child,
+                    AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
+        }
+        mSnappedBackChildren.clear();
+    }
+
+    private void generateDragEvents() {
+        for (View child : mDragAnimPendingChildren) {
+            mAnimationEvents.add(new AnimationEvent(child,
+                    AnimationEvent.ANIMATION_TYPE_START_DRAG));
+        }
+        mDragAnimPendingChildren.clear();
+    }
+
+    private void generateChildRemovalEvents() {
+        for (View child : mChildrenToRemoveAnimated) {
+            boolean childWasSwipedOut = mSwipedOutViews.contains(child);
+
+            // we need to know the view after this one
+            float removedTranslation = child.getTranslationY();
+            boolean ignoreChildren = true;
+            if (child instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
+                    removedTranslation = row.getTranslationWhenRemoved();
+                    ignoreChildren = false;
+                }
+                childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
+            }
+            if (!childWasSwipedOut) {
+                Rect clipBounds = child.getClipBounds();
+                childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
+
+                if (childWasSwipedOut && child instanceof ExpandableView) {
+                    // Clean up any potential transient views if the child has already been swiped
+                    // out, as we won't be animating it further (due to its height already being
+                    // clipped to 0.
+                    ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer();
+                    if (transientContainer != null) {
+                        transientContainer.removeTransientView(child);
+                    }
+                }
+            }
+            int animationType = childWasSwipedOut
+                    ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
+                    : AnimationEvent.ANIMATION_TYPE_REMOVE;
+            AnimationEvent event = new AnimationEvent(child, animationType);
+            event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
+                    ignoreChildren);
+            mAnimationEvents.add(event);
+            mSwipedOutViews.remove(child);
+        }
+        mChildrenToRemoveAnimated.clear();
+    }
+
+    private void generatePositionChangeEvents() {
+        for (View child : mChildrenChangingPositions) {
+            mAnimationEvents.add(new AnimationEvent(child,
+                    AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
+        }
+        mChildrenChangingPositions.clear();
+        if (mGenerateChildOrderChangedEvent) {
+            mAnimationEvents.add(new AnimationEvent(null,
+                    AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
+            mGenerateChildOrderChangedEvent = false;
+        }
+    }
+
+    private void generateChildAdditionEvents() {
+        for (View child : mChildrenToAddAnimated) {
+            if (mFromMoreCardAdditions.contains(child)) {
+                mAnimationEvents.add(new AnimationEvent(child,
+                        AnimationEvent.ANIMATION_TYPE_ADD,
+                        StackStateAnimator.ANIMATION_DURATION_STANDARD));
+            } else {
+                mAnimationEvents.add(new AnimationEvent(child,
+                        AnimationEvent.ANIMATION_TYPE_ADD));
+            }
+        }
+        mChildrenToAddAnimated.clear();
+        mFromMoreCardAdditions.clear();
+    }
+
+    private void generateTopPaddingEvent() {
+        if (mTopPaddingNeedsAnimation) {
+            AnimationEvent event;
+            if (mAmbientState.isDark()) {
+                event = new AnimationEvent(null /* view */,
+                        AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED,
+                        KeyguardSliceView.DEFAULT_ANIM_DURATION);
+            } else {
+                event = new AnimationEvent(null /* view */,
+                        AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED);
+            }
+            mAnimationEvents.add(event);
+        }
+        mTopPaddingNeedsAnimation = false;
+    }
+
+    private void generateActivateEvent() {
+        if (mActivateNeedsAnimation) {
+            mAnimationEvents.add(
+                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
+        }
+        mActivateNeedsAnimation = false;
+    }
+
+    private void generateAnimateEverythingEvent() {
+        if (mEverythingNeedsAnimation) {
+            mAnimationEvents.add(
+                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
+        }
+        mEverythingNeedsAnimation = false;
+    }
+
+    private void generateDimmedEvent() {
+        if (mDimmedNeedsAnimation) {
+            mAnimationEvents.add(
+                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
+        }
+        mDimmedNeedsAnimation = false;
+    }
+
+    private void generateHideSensitiveEvent() {
+        if (mHideSensitiveNeedsAnimation) {
+            mAnimationEvents.add(
+                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
+        }
+        mHideSensitiveNeedsAnimation = false;
+    }
+
+    private void generateDarkEvent() {
+        if (mDarkNeedsAnimation) {
+            AnimationEvent ev = new AnimationEvent(null,
+                    AnimationEvent.ANIMATION_TYPE_DARK,
+                    new AnimationFilter()
+                            .animateDark()
+                            .animateY(mShelf));
+            ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
+            mAnimationEvents.add(ev);
+        }
+        mDarkNeedsAnimation = false;
+    }
+
+    private void generateGoToFullShadeEvent() {
+        if (mGoToFullShadeNeedsAnimation) {
+            mAnimationEvents.add(
+                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
+        }
+        mGoToFullShadeNeedsAnimation = false;
+    }
+
+    private boolean onInterceptTouchEventScroll(MotionEvent ev) {
+        if (!isScrollingEnabled()) {
+            return false;
+        }
+        /*
+         * This method JUST determines whether we want to intercept the motion.
+         * If we return true, onMotionEvent will be called and we do the actual
+         * scrolling there.
+         */
+
+        /*
+        * Shortcut the most recurring case: the user is in the dragging
+        * state and is moving their finger.  We want to intercept this
+        * motion.
+        */
+        final int action = ev.getAction();
+        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
+            return true;
+        }
+
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_MOVE: {
+                /*
+                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
+                 * whether the user has moved far enough from the original down touch.
+                 */
+
+                /*
+                * Locally do absolute value. mLastMotionY is set to the y value
+                * of the down event.
+                */
+                final int activePointerId = mActivePointerId;
+                if (activePointerId == INVALID_POINTER) {
+                    // If we don't have a valid id, the touch down wasn't on content.
+                    break;
+                }
+
+                final int pointerIndex = ev.findPointerIndex(activePointerId);
+                if (pointerIndex == -1) {
+                    Log.e(TAG, "Invalid pointerId=" + activePointerId
+                            + " in onInterceptTouchEvent");
+                    break;
+                }
+
+                final int y = (int) ev.getY(pointerIndex);
+                final int x = (int) ev.getX(pointerIndex);
+                final int yDiff = Math.abs(y - mLastMotionY);
+                final int xDiff = Math.abs(x - mDownX);
+                if (yDiff > mTouchSlop && yDiff > xDiff) {
+                    setIsBeingDragged(true);
+                    mLastMotionY = y;
+                    mDownX = x;
+                    initVelocityTrackerIfNotExists();
+                    mVelocityTracker.addMovement(ev);
+                }
+                break;
+            }
+
+            case MotionEvent.ACTION_DOWN: {
+                final int y = (int) ev.getY();
+                mScrolledToTopOnFirstDown = isScrolledToTop();
+                if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
+                    setIsBeingDragged(false);
+                    recycleVelocityTracker();
+                    break;
+                }
+
+                /*
+                 * Remember location of down touch.
+                 * ACTION_DOWN always refers to pointer index 0.
+                 */
+                mLastMotionY = y;
+                mDownX = (int) ev.getX();
+                mActivePointerId = ev.getPointerId(0);
+
+                initOrResetVelocityTracker();
+                mVelocityTracker.addMovement(ev);
+                /*
+                * If being flinged and user touches the screen, initiate drag;
+                * otherwise don't.  mScroller.isFinished should be false when
+                * being flinged.
+                */
+                boolean isBeingDragged = !mScroller.isFinished();
+                setIsBeingDragged(isBeingDragged);
+                break;
+            }
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                /* Release the drag */
+                setIsBeingDragged(false);
+                mActivePointerId = INVALID_POINTER;
+                recycleVelocityTracker();
+                if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
+                    animateScroll();
+                }
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                onSecondaryPointerUp(ev);
+                break;
+        }
+
+        /*
+        * The only time we want to intercept motion events is if we are in the
+        * drag mode.
+        */
+        return mIsBeingDragged;
+    }
+
+    protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
+        return new StackScrollAlgorithm(context);
+    }
+
+    /**
+     * @return Whether the specified motion event is actually happening over the content.
+     */
+    private boolean isInContentBounds(MotionEvent event) {
+        return isInContentBounds(event.getY());
+    }
+
+    /**
+     * @return Whether a y coordinate is inside the content.
+     */
+    public boolean isInContentBounds(float y) {
+        return y < getHeight() - getEmptyBottomMargin();
+    }
+
+    private void setIsBeingDragged(boolean isDragged) {
+        mIsBeingDragged = isDragged;
+        if (isDragged) {
+            requestDisallowInterceptTouchEvent(true);
+            cancelLongPress();
+        }
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        super.onWindowFocusChanged(hasWindowFocus);
+        if (!hasWindowFocus) {
+            cancelLongPress();
+        }
+    }
+
+    @Override
+    public void clearChildFocus(View child) {
+        super.clearChildFocus(child);
+        if (mForcedScroll == child) {
+            mForcedScroll = null;
+        }
+    }
+
+    public void requestDisallowLongPress() {
+        cancelLongPress();
+    }
+
+    public void requestDisallowDismiss() {
+        mDisallowDismissInThisMotion = true;
+    }
+
+    public void cancelLongPress() {
+        mSwipeHelper.cancelLongPress();
+    }
+
+    @Override
+    public boolean isScrolledToTop() {
+        return mOwnScrollY == 0;
+    }
+
+    @Override
+    public boolean isScrolledToBottom() {
+        return mOwnScrollY >= getScrollRange();
+    }
+
+    @Override
+    public View getHostView() {
+        return this;
+    }
+
+    public int getEmptyBottomMargin() {
+        return Math.max(mMaxLayoutHeight - mContentHeight, 0);
+    }
+
+    public void checkSnoozeLeavebehind() {
+        if (mCheckForLeavebehind) {
+            mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
+            mCheckForLeavebehind = false;
+        }
+    }
+
+    public void resetCheckSnoozeLeavebehind() {
+        mCheckForLeavebehind = true;
+    }
+
+    public void onExpansionStarted() {
+        mIsExpansionChanging = true;
+        mAmbientState.setExpansionChanging(true);
+        checkSnoozeLeavebehind();
+    }
+
+    public void onExpansionStopped() {
+        mIsExpansionChanging = false;
+        resetCheckSnoozeLeavebehind();
+        mAmbientState.setExpansionChanging(false);
+        if (!mIsExpanded) {
+            setOwnScrollY(0);
+            mStatusBar.resetUserExpandedStates();
+            clearTemporaryViews();
+            clearUserLockedViews();
+        }
+    }
+
+    private void clearUserLockedViews() {
+        for (int i = 0; i < getChildCount(); i++) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            if (child instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                row.setUserLocked(false);
+            }
+        }
+    }
+
+    private void clearTemporaryViews() {
+        // lets make sure nothing is transient anymore
+        clearTemporaryViewsInGroup(this);
+        for (int i = 0; i < getChildCount(); i++) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            if (child instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                clearTemporaryViewsInGroup(row.getChildrenContainer());
+            }
+        }
+    }
+
+    private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
+        while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
+            viewGroup.removeTransientView(viewGroup.getTransientView(0));
+        }
+    }
+
+    public void onPanelTrackingStarted() {
+        mPanelTracking = true;
+        mAmbientState.setPanelTracking(true);
+    }
+    public void onPanelTrackingStopped() {
+        mPanelTracking = false;
+        mAmbientState.setPanelTracking(false);
+    }
+
+    public void resetScrollPosition() {
+        mScroller.abortAnimation();
+        setOwnScrollY(0);
+    }
+
+    private void setIsExpanded(boolean isExpanded) {
+        boolean changed = isExpanded != mIsExpanded;
+        mIsExpanded = isExpanded;
+        mStackScrollAlgorithm.setIsExpanded(isExpanded);
+        if (changed) {
+            if (!mIsExpanded) {
+                mGroupManager.collapseAllGroups();
+                mExpandHelper.cancelImmediately();
+            }
+            updateNotificationAnimationStates();
+            updateChronometers();
+            requestChildrenUpdate();
+        }
+    }
+
+    private void updateChronometers() {
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            updateChronometerForChild(getChildAt(i));
+        }
+    }
+
+    private void updateChronometerForChild(View child) {
+        if (child instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+            row.setChronometerRunning(mIsExpanded);
+        }
+    }
+
+    @Override
+    public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
+        updateContentHeight();
+        updateScrollPositionOnExpandInBottom(view);
+        clampScrollPosition();
+        notifyHeightChangeListener(view, needsAnimation);
+        ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
+                ? (ExpandableNotificationRow) view
+                : null;
+        if (row != null && (row == mFirstVisibleBackgroundChild
+                || row.getNotificationParent() == mFirstVisibleBackgroundChild)) {
+            updateAlgorithmLayoutMinHeight();
+        }
+        if (needsAnimation) {
+            requestAnimationOnViewResize(row);
+        }
+        requestChildrenUpdate();
+    }
+
+    @Override
+    public void onReset(ExpandableView view) {
+        updateAnimationState(view);
+        updateChronometerForChild(view);
+    }
+
+    private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
+        if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+            if (row.isUserLocked() && row != getFirstChildNotGone()) {
+                if (row.isSummaryWithChildren()) {
+                    return;
+                }
+                // We are actually expanding this view
+                float endPosition = row.getTranslationY() + row.getActualHeight();
+                if (row.isChildInGroup()) {
+                    endPosition += row.getNotificationParent().getTranslationY();
+                }
+                int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
+                if (row != mLastVisibleBackgroundChild && mShelf.getVisibility() != GONE) {
+                    layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
+                }
+                if (endPosition > layoutEnd) {
+                    setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
+                    mDisallowScrollingInThisMotion = true;
+                }
+            }
+        }
+    }
+
+    public void setOnHeightChangedListener(
+            ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
+        this.mOnHeightChangedListener = mOnHeightChangedListener;
+    }
+
+    public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
+        mOnEmptySpaceClickListener = listener;
+    }
+
+    public void onChildAnimationFinished() {
+        setAnimationRunning(false);
+        requestChildrenUpdate();
+        runAnimationFinishedRunnables();
+        clearTransient();
+        clearHeadsUpDisappearRunning();
+    }
+
+    private void clearHeadsUpDisappearRunning() {
+        for (int i = 0; i < getChildCount(); i++) {
+            View view = getChildAt(i);
+            if (view instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+                row.setHeadsUpAnimatingAway(false);
+                if (row.isSummaryWithChildren()) {
+                    for (ExpandableNotificationRow child : row.getNotificationChildren()) {
+                        child.setHeadsUpAnimatingAway(false);
+                    }
+                }
+            }
+        }
+    }
+
+    private void clearTransient() {
+        for (ExpandableView view : mClearTransientViewsWhenFinished) {
+            StackStateAnimator.removeTransientView(view);
+        }
+        mClearTransientViewsWhenFinished.clear();
+    }
+
+    private void runAnimationFinishedRunnables() {
+        for (Runnable runnable : mAnimationFinishedRunnables) {
+            runnable.run();
+        }
+        mAnimationFinishedRunnables.clear();
+    }
+
+    /**
+     * See {@link AmbientState#setDimmed}.
+     */
+    public void setDimmed(boolean dimmed, boolean animate) {
+        dimmed &= onKeyguard();
+        mAmbientState.setDimmed(dimmed);
+        if (animate && mAnimationsEnabled) {
+            mDimmedNeedsAnimation = true;
+            mNeedsAnimation =  true;
+            animateDimmed(dimmed);
+        } else {
+            setDimAmount(dimmed ? 1.0f : 0.0f);
+        }
+        requestChildrenUpdate();
+    }
+
+    @VisibleForTesting
+    boolean isDimmed() {
+        return mAmbientState.isDimmed();
+    }
+
+    private void setDimAmount(float dimAmount) {
+        mDimAmount = dimAmount;
+        updateBackgroundDimming();
+    }
+
+    private void animateDimmed(boolean dimmed) {
+        if (mDimAnimator != null) {
+            mDimAnimator.cancel();
+        }
+        float target = dimmed ? 1.0f : 0.0f;
+        if (target == mDimAmount) {
+            return;
+        }
+        mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
+        mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
+        mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mDimAnimator.addListener(mDimEndListener);
+        mDimAnimator.addUpdateListener(mDimUpdateListener);
+        mDimAnimator.start();
+    }
+
+    public void setHideSensitive(boolean hideSensitive, boolean animate) {
+        if (hideSensitive != mAmbientState.isHideSensitive()) {
+            int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                ExpandableView v = (ExpandableView) getChildAt(i);
+                v.setHideSensitiveForIntrinsicHeight(hideSensitive);
+            }
+            mAmbientState.setHideSensitive(hideSensitive);
+            if (animate && mAnimationsEnabled) {
+                mHideSensitiveNeedsAnimation = true;
+                mNeedsAnimation =  true;
+            }
+            updateContentHeight();
+            requestChildrenUpdate();
+        }
+    }
+
+    /**
+     * See {@link AmbientState#setActivatedChild}.
+     */
+    public void setActivatedChild(ActivatableNotificationView activatedChild) {
+        mAmbientState.setActivatedChild(activatedChild);
+        if (mAnimationsEnabled) {
+            mActivateNeedsAnimation = true;
+            mNeedsAnimation =  true;
+        }
+        requestChildrenUpdate();
+    }
+
+    public ActivatableNotificationView getActivatedChild() {
+        return mAmbientState.getActivatedChild();
+    }
+
+    private void applyCurrentState() {
+        mCurrentStackScrollState.apply();
+        if (mListener != null) {
+            mListener.onChildLocationsChanged();
+        }
+        runAnimationFinishedRunnables();
+        setAnimationRunning(false);
+        updateBackground();
+        updateViewShadows();
+        updateClippingToTopRoundedCorner();
+    }
+
+    private void updateViewShadows() {
+        // we need to work around an issue where the shadow would not cast between siblings when
+        // their z difference is between 0 and 0.1
+
+        // Lefts first sort by Z difference
+        for (int i = 0; i < getChildCount(); i++) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                mTmpSortedChildren.add(child);
+            }
+        }
+        Collections.sort(mTmpSortedChildren, mViewPositionComparator);
+
+        // Now lets update the shadow for the views
+        ExpandableView previous = null;
+        for (int i = 0; i < mTmpSortedChildren.size(); i++) {
+            ExpandableView expandableView = mTmpSortedChildren.get(i);
+            float translationZ = expandableView.getTranslationZ();
+            float otherZ = previous == null ? translationZ : previous.getTranslationZ();
+            float diff = otherZ - translationZ;
+            if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
+                // There is no fake shadow to be drawn
+                expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
+            } else {
+                float yLocation = previous.getTranslationY() + previous.getActualHeight() -
+                        expandableView.getTranslationY() - previous.getExtraBottomPadding();
+                expandableView.setFakeShadowIntensity(
+                        diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
+                        previous.getOutlineAlpha(), (int) yLocation,
+                        previous.getOutlineTranslation());
+            }
+            previous = expandableView;
+        }
+
+        mTmpSortedChildren.clear();
+    }
+
+    /**
+     * Update colors of "dismiss" and "empty shade" views.
+     *
+     * @param lightTheme True if light theme should be used.
+     */
+    public void updateDecorViews(boolean lightTheme) {
+        if (lightTheme == mUsingLightTheme) {
+            return;
+        }
+        mUsingLightTheme = lightTheme;
+        Context context = new ContextThemeWrapper(mContext,
+                lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
+        final int textColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor);
+        mFooterView.setTextColor(textColor);
+        mEmptyShadeView.setTextColor(textColor);
+    }
+
+    public void goToFullShade(long delay) {
+        mGoToFullShadeNeedsAnimation = true;
+        mGoToFullShadeDelay = delay;
+        mNeedsAnimation = true;
+        requestChildrenUpdate();
+    }
+
+    public void cancelExpandHelper() {
+        mExpandHelper.cancel();
+    }
+
+    public void setIntrinsicPadding(int intrinsicPadding) {
+        mIntrinsicPadding = intrinsicPadding;
+        mAmbientState.setIntrinsicPadding(intrinsicPadding);
+    }
+
+    public int getIntrinsicPadding() {
+        return mIntrinsicPadding;
+    }
+
+    /**
+     * @return the y position of the first notification
+     */
+    public float getNotificationsTopY() {
+        return mTopPadding + getStackTranslation();
+    }
+
+    @Override
+    public boolean shouldDelayChildPressedState() {
+        return true;
+    }
+
+    /**
+     * See {@link AmbientState#setDark}.
+     */
+    public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
+        if (mAmbientState.isDark() == dark) {
+            return;
+        }
+        mAmbientState.setDark(dark);
+        if (animate && mAnimationsEnabled) {
+            mDarkNeedsAnimation = true;
+            mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
+            mNeedsAnimation = true;
+        } else {
+            setDarkAmount(dark ? 1f : 0f);
+            updateBackground();
+        }
+        requestChildrenUpdate();
+        applyCurrentBackgroundBounds();
+        updateWillNotDraw();
+        notifyHeightChangeListener(mShelf);
+    }
+
+    private void updatePanelTranslation() {
+        setTranslationX(mVerticalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedDarkAmount);
+    }
+
+    public void setVerticalPanelTranslation(float verticalPanelTranslation) {
+        mVerticalPanelTranslation = verticalPanelTranslation;
+        updatePanelTranslation();
+    }
+
+    /**
+     * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
+     * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
+     * {@link #mAmbientState}'s dark mode is toggled.
+     */
+    private void updateWillNotDraw() {
+        boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
+        setWillNotDraw(!willDraw);
+    }
+
+    private void setDarkAmount(float darkAmount) {
+        setDarkAmount(darkAmount, darkAmount);
+    }
+
+    /**
+     * Sets the current dark amount.
+     *
+     * @param linearDarkAmount The dark amount that follows linear interpoloation in the animation,
+     *                         i.e. animates from 0 to 1 or vice-versa in a linear manner.
+     * @param interpolatedDarkAmount The dark amount that follows the actual interpolation of the
+     *                                animation curve.
+     */
+    public void setDarkAmount(float linearDarkAmount, float interpolatedDarkAmount) {
+        mLinearDarkAmount = linearDarkAmount;
+        mInterpolatedDarkAmount = interpolatedDarkAmount;
+        boolean wasFullyDark = mAmbientState.isFullyDark();
+        mAmbientState.setDarkAmount(interpolatedDarkAmount);
+        boolean nowFullyDark = mAmbientState.isFullyDark();
+        if (nowFullyDark != wasFullyDark) {
+            updateContentHeight();
+            DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
+            if (nowFullyDark && dozeParameters.shouldControlScreenOff()) {
+                mShelf.fadeInTranslating();
+            }
+            if (mIconAreaController != null) {
+                mIconAreaController.setFullyDark(nowFullyDark);
+            }
+        }
+        updateAlgorithmHeightAndPadding();
+        updateBackgroundDimming();
+        updatePanelTranslation();
+        requestChildrenUpdate();
+    }
+
+    public void notifyDarkAnimationStart(boolean dark) {
+        // We only swap the scaling factor if we're fully dark or fully awake to avoid
+        // interpolation issues when playing with the power button.
+        if (mInterpolatedDarkAmount == 0 || mInterpolatedDarkAmount == 1) {
+            mBackgroundXFactor = dark ? 1.8f : 1.5f;
+            mDarkXInterpolator = dark
+                    ? Interpolators.FAST_OUT_SLOW_IN_REVERSE
+                    : Interpolators.FAST_OUT_SLOW_IN;
+        }
+    }
+
+    public long getDarkAnimationDuration(boolean dark) {
+        long duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
+        // Longer animation when sleeping with more than 1 notification
+        if (dark && getNotGoneChildCount() > 2) {
+            duration *= 1.2f;
+        }
+        return duration;
+    }
+
+    private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
+        if (screenLocation == null || screenLocation.y < mTopPadding) {
+            return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
+        }
+        if (screenLocation.y > getBottomMostNotificationBottom()) {
+            return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
+        }
+        View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
+        if (child != null) {
+            return getNotGoneIndex(child);
+        } else {
+            return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
+        }
+    }
+
+    private int getNotGoneIndex(View child) {
+        int count = getChildCount();
+        int notGoneIndex = 0;
+        for (int i = 0; i < count; i++) {
+            View v = getChildAt(i);
+            if (child == v) {
+                return notGoneIndex;
+            }
+            if (v.getVisibility() != View.GONE) {
+                notGoneIndex++;
+            }
+        }
+        return -1;
+    }
+
+    public void setFooterView(@NonNull FooterView footerView) {
+        int index = -1;
+        if (mFooterView != null) {
+            index = indexOfChild(mFooterView);
+            removeView(mFooterView);
+        }
+        mFooterView = footerView;
+        addView(mFooterView, index);
+    }
+
+    public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
+        int index = -1;
+        if (mEmptyShadeView != null) {
+            index = indexOfChild(mEmptyShadeView);
+            removeView(mEmptyShadeView);
+        }
+        mEmptyShadeView = emptyShadeView;
+        addView(mEmptyShadeView, index);
+    }
+
+    public void updateEmptyShadeView(boolean visible) {
+        mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
+
+        int oldTextRes = mEmptyShadeView.getTextResource();
+        int newTextRes = mStatusBar.areNotificationsHidden()
+                ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
+        if (oldTextRes != newTextRes) {
+            mEmptyShadeView.setText(newTextRes);
+        }
+    }
+
+    public void updateFooterView(boolean visible, boolean showDismissView) {
+        if (mFooterView == null) {
+            return;
+        }
+        boolean animate = mIsExpanded && mAnimationsEnabled;
+        mFooterView.setVisible(visible, animate);
+        mFooterView.setSecondaryVisible(showDismissView, animate);
+    }
+
+    public void setDismissAllInProgress(boolean dismissAllInProgress) {
+        mDismissAllInProgress = dismissAllInProgress;
+        mAmbientState.setDismissAllInProgress(dismissAllInProgress);
+        handleDismissAllClipping();
+    }
+
+    private void handleDismissAllClipping() {
+        final int count = getChildCount();
+        boolean previousChildWillBeDismissed = false;
+        for (int i = 0; i < count; i++) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            if (child.getVisibility() == GONE) {
+                continue;
+            }
+            if (mDismissAllInProgress && previousChildWillBeDismissed) {
+                child.setMinClipTopAmount(child.getClipTopAmount());
+            } else {
+                child.setMinClipTopAmount(0);
+            }
+            previousChildWillBeDismissed = canChildBeDismissed(child);
+        }
+    }
+
+    public boolean isFooterViewNotGone() {
+        return mFooterView != null
+                && mFooterView.getVisibility() != View.GONE
+                && !mFooterView.willBeGone();
+    }
+
+    public boolean isFooterViewContentVisible() {
+        return mFooterView != null && mFooterView.isContentVisible();
+    }
+
+    public int getFooterViewHeight() {
+        return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
+    }
+
+    public int getEmptyShadeViewHeight() {
+        return mEmptyShadeView.getHeight();
+    }
+
+    public float getBottomMostNotificationBottom() {
+        final int count = getChildCount();
+        float max = 0;
+        for (int childIdx = 0; childIdx < count; childIdx++) {
+            ExpandableView child = (ExpandableView) getChildAt(childIdx);
+            if (child.getVisibility() == GONE) {
+                continue;
+            }
+            float bottom = child.getTranslationY() + child.getActualHeight()
+                    - child.getClipBottomAmount();
+            if (bottom > max) {
+                max = bottom;
+            }
+        }
+        return max + getStackTranslation();
+    }
+
+    public void setStatusBar(StatusBar statusBar) {
+        this.mStatusBar = statusBar;
+    }
+
+    public void setGroupManager(NotificationGroupManager groupManager) {
+        this.mGroupManager = groupManager;
+    }
+
+    public void onGoToKeyguard() {
+        requestAnimateEverything();
+    }
+
+    private void requestAnimateEverything() {
+        if (mIsExpanded && mAnimationsEnabled) {
+            mEverythingNeedsAnimation = true;
+            mNeedsAnimation = true;
+            requestChildrenUpdate();
+        }
+    }
+
+    public boolean isBelowLastNotification(float touchX, float touchY) {
+        int childCount = getChildCount();
+        for (int i = childCount - 1; i >= 0; i--) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            if (child.getVisibility() != View.GONE) {
+                float childTop = child.getY();
+                if (childTop > touchY) {
+                    // we are above a notification entirely let's abort
+                    return false;
+                }
+                boolean belowChild = touchY > childTop + child.getActualHeight()
+                        - child.getClipBottomAmount();
+                if (child == mFooterView) {
+                    if(!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
+                                    touchY - childTop)) {
+                        // We clicked on the dismiss button
+                        return false;
+                    }
+                } else if (child == mEmptyShadeView) {
+                    // We arrived at the empty shade view, for which we accept all clicks
+                    return true;
+                } else if (!belowChild){
+                    // We are on a child
+                    return false;
+                }
+            }
+        }
+        return touchY > mTopPadding + mStackTranslation;
+    }
+
+    @Override
+    public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
+        boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
+                && (mIsExpanded || changedRow.isPinned());
+        if (animated) {
+            mExpandedGroupView = changedRow;
+            mNeedsAnimation = true;
+        }
+        changedRow.setChildrenExpanded(expanded, animated);
+        if (!mGroupExpandedForMeasure) {
+            onHeightChanged(changedRow, false /* needsAnimation */);
+        }
+        runAfterAnimationFinished(new Runnable() {
+            @Override
+            public void run() {
+                changedRow.onFinishedExpansionChange();
+            }
+        });
+    }
+
+    @Override
+    public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
+        mStatusBar.requestNotificationUpdate();
+    }
+
+    /** @hide */
+    @Override
+    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEventInternal(event);
+        event.setScrollable(mScrollable);
+        event.setScrollX(mScrollX);
+        event.setScrollY(mOwnScrollY);
+        event.setMaxScrollX(mScrollX);
+        event.setMaxScrollY(getScrollRange());
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfoInternal(info);
+        if (mScrollable) {
+            info.setScrollable(true);
+            if (mBackwardScrollable) {
+                info.addAction(
+                        AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
+            }
+            if (mForwardScrollable) {
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
+            }
+        }
+        // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
+        info.setClassName(ScrollView.class.getName());
+    }
+
+    /** @hide */
+    @Override
+    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+        if (super.performAccessibilityActionInternal(action, arguments)) {
+            return true;
+        }
+        if (!isEnabled()) {
+            return false;
+        }
+        int direction = -1;
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+                // fall through
+            case android.R.id.accessibilityActionScrollDown:
+                direction = 1;
+                // fall through
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+                // fall through
+            case android.R.id.accessibilityActionScrollUp:
+                final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
+                        - mShelf.getIntrinsicHeight();
+                final int targetScrollY = Math.max(0,
+                        Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
+                if (targetScrollY != mOwnScrollY) {
+                    mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
+                    animateScroll();
+                    return true;
+                }
+                break;
+        }
+        return false;
+    }
+
+    @Override
+    public void onGroupsChanged() {
+        mStatusBar.requestNotificationUpdate();
+    }
+
+    public void generateChildOrderChangedEvent() {
+        if (mIsExpanded && mAnimationsEnabled) {
+            mGenerateChildOrderChangedEvent = true;
+            mNeedsAnimation = true;
+            requestChildrenUpdate();
+        }
+    }
+
+    @Override
+    public int getContainerChildCount() {
+        return getChildCount();
+    }
+
+    @Override
+    public View getContainerChildAt(int i) {
+        return getChildAt(i);
+    }
+
+    @Override
+    public void removeContainerView(View v) {
+        removeView(v);
+    }
+
+    @Override
+    public void addContainerView(View v) {
+        addView(v);
+    }
+
+    public void runAfterAnimationFinished(Runnable runnable) {
+        mAnimationFinishedRunnables.add(runnable);
+    }
+
+    public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+        mAmbientState.setHeadsUpManager(headsUpManager);
+        mHeadsUpManager.addListener(mRoundnessManager);
+    }
+
+    public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
+        if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
+            mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
+            mNeedsAnimation = true;
+            if (!mIsExpanded && !isHeadsUp) {
+                row.setHeadsUpAnimatingAway(true);
+            }
+            requestChildrenUpdate();
+        }
+    }
+
+    public void setShadeExpanded(boolean shadeExpanded) {
+        mAmbientState.setShadeExpanded(shadeExpanded);
+        mStateAnimator.setShadeExpanded(shadeExpanded);
+    }
+
+    /**
+     * Set the boundary for the bottom heads up position. The heads up will always be above this
+     * position.
+     *
+     * @param height the height of the screen
+     * @param bottomBarHeight the height of the bar on the bottom
+     */
+    public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
+        mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
+        mStateAnimator.setHeadsUpAppearHeightBottom(height);
+        requestChildrenUpdate();
+    }
+
+    public void setTrackingHeadsUp(ExpandableNotificationRow row) {
+        mTrackingHeadsUp = row != null;
+        mRoundnessManager.setTrackingHeadsUp(row);
+    }
+
+    public void setScrimController(ScrimController scrimController) {
+        mScrimController = scrimController;
+        mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
+    }
+
+    public void forceNoOverlappingRendering(boolean force) {
+        mForceNoOverlappingRendering = force;
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
+    }
+
+    public void setAnimationRunning(boolean animationRunning) {
+        if (animationRunning != mAnimationRunning) {
+            if (animationRunning) {
+                getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
+            } else {
+                getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
+            }
+            mAnimationRunning = animationRunning;
+            updateContinuousShadowDrawing();
+        }
+    }
+
+    public boolean isExpanded() {
+        return mIsExpanded;
+    }
+
+    public void setPulsing(boolean pulsing, boolean animated) {
+        if (!mPulsing && !pulsing) {
+            return;
+        }
+        mPulsing = pulsing;
+        mNeedingPulseAnimation = animated ? getFirstChildNotGone() : null;
+        mAmbientState.setPulsing(pulsing);
+        updateNotificationAnimationStates();
+        updateAlgorithmHeightAndPadding();
+        updateContentHeight();
+        requestChildrenUpdate();
+        notifyHeightChangeListener(null, animated);
+        mNeedsAnimation |= animated;
+    }
+
+    private void generatePulsingAnimationEvent() {
+        if (mNeedingPulseAnimation != null) {
+            int type = mPulsing ? AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR
+                    : AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR;
+            mAnimationEvents.add(new AnimationEvent(mNeedingPulseAnimation, type));
+            mNeedingPulseAnimation = null;
+        }
+    }
+
+    public void setFadingOut(boolean fadingOut) {
+        if (fadingOut != mFadingOut) {
+            mFadingOut = fadingOut;
+            updateFadingState();
+        }
+    }
+
+    public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
+        if (mScrimController == null) {
+            // we're not set up yet.
+            return;
+        }
+        if (parentNotFullyVisible != mParentNotFullyVisible) {
+            mParentNotFullyVisible = parentNotFullyVisible;
+            updateFadingState();
+        }
+    }
+
+    private void updateFadingState() {
+        applyCurrentBackgroundBounds();
+        updateSrcDrawing();
+    }
+
+    @Override
+    public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
+        super.setAlpha(alpha);
+        setFadingOut(alpha != 1.0f);
+    }
+
+    public void setQsExpanded(boolean qsExpanded) {
+        mQsExpanded = qsExpanded;
+        updateAlgorithmLayoutMinHeight();
+        updateScrollability();
+    }
+
+    public void setQsExpansionFraction(float qsExpansionFraction) {
+        mQsExpansionFraction = qsExpansionFraction;
+    }
+
+    public void setOwnScrollY(int ownScrollY) {
+        if (ownScrollY != mOwnScrollY) {
+            // We still want to call the normal scrolled changed for accessibility reasons
+            onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
+            mOwnScrollY = ownScrollY;
+            updateForwardAndBackwardScrollability();
+            requestChildrenUpdate();
+        }
+    }
+
+    public void setShelf(NotificationShelf shelf) {
+        int index = -1;
+        if (mShelf != null) {
+            index = indexOfChild(mShelf);
+            removeView(mShelf);
+        }
+        mShelf = shelf;
+        addView(mShelf, index);
+        mAmbientState.setShelf(shelf);
+        mStateAnimator.setShelf(shelf);
+        shelf.bind(mAmbientState, this);
+    }
+
+    public NotificationShelf getNotificationShelf() {
+        return mShelf;
+    }
+
+    public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
+        if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
+            mMaxDisplayedNotifications = maxDisplayedNotifications;
+            updateContentHeight();
+            notifyHeightChangeListener(mShelf);
+        }
+    }
+
+    public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
+        mShouldShowShelfOnly =  shouldShowShelfOnly;
+        updateAlgorithmLayoutMinHeight();
+    }
+
+    public int getMinExpansionHeight() {
+        return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
+    }
+
+    public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
+        mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
+        updateClipping();
+    }
+
+    public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
+        mHeadsUpAnimatingAway = headsUpAnimatingAway;
+        updateClipping();
+    }
+
+    public void setStatusBarState(int statusBarState) {
+        mStatusBarState = statusBarState;
+        mAmbientState.setStatusBarState(statusBarState);
+    }
+
+    public void setExpandingVelocity(float expandingVelocity) {
+        mAmbientState.setExpandingVelocity(expandingVelocity);
+    }
+
+    public float getOpeningHeight() {
+        if (mEmptyShadeView.getVisibility() == GONE) {
+            return getMinExpansionHeight();
+        } else {
+            return getAppearEndPosition();
+        }
+    }
+
+    public void setIsFullWidth(boolean isFullWidth) {
+        mAmbientState.setPanelFullWidth(isFullWidth);
+    }
+
+    public void setUnlockHintRunning(boolean running) {
+        mAmbientState.setUnlockHintRunning(running);
+    }
+
+    public void setQsCustomizerShowing(boolean isShowing) {
+        mAmbientState.setQsCustomizerShowing(isShowing);
+        requestChildrenUpdate();
+    }
+
+    public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
+        mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
+    }
+
+    public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
+        mAntiBurnInOffsetX = antiBurnInOffsetX;
+        updatePanelTranslation();
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
+                        + " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
+                        + " qsExpandFraction=%f]",
+                this.getClass().getSimpleName(),
+                mPulsing ? "T":"f",
+                mAmbientState.isQsCustomizerShowing() ? "T":"f",
+                getVisibility() == View.VISIBLE ? "visible"
+                        : getVisibility() == View.GONE ? "gone"
+                                : "invisible",
+                getAlpha(),
+                mAmbientState.getScrollY(),
+                mMaxTopPadding,
+                mShouldShowShelfOnly ? "T":"f",
+                mQsExpansionFraction));
+    }
+
+    public boolean isFullyDark() {
+        return mAmbientState.isFullyDark();
+    }
+
+    /**
+     * Add a listener whenever the expanded height changes. The first value passed as an argument
+     * is the expanded height and the second one is the appearFraction.
+     *
+     * @param listener the listener to notify.
+     */
+    public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+        mExpandedHeightListeners.add(listener);
+    }
+
+    /**
+     * Stop a listener from listening to the expandedHeight.
+     */
+    public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+        mExpandedHeightListeners.remove(listener);
+    }
+
+    public void setHeadsUpAppearanceController(
+            HeadsUpAppearanceController headsUpAppearanceController) {
+        mHeadsUpAppearanceController = headsUpAppearanceController;
+    }
+
+    public void setIconAreaController(NotificationIconAreaController controller) {
+        mIconAreaController = controller;
+    }
+
+    /**
+     * A listener that is notified when the empty space below the notifications is clicked on
+     */
+    public interface OnEmptySpaceClickListener {
+        void onEmptySpaceClicked(float x, float y);
+    }
+
+    /**
+     * A listener that gets notified when the overscroll at the top has changed.
+     */
+    public interface OnOverscrollTopChangedListener {
+
+        /**
+         * Notifies a listener that the overscroll has changed.
+         *
+         * @param amount the amount of overscroll, in pixels
+         * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
+         *                     unrubberbanded motion to directly expand overscroll view (e.g expand
+         *                     QS)
+         */
+        void onOverscrollTopChanged(float amount, boolean isRubberbanded);
+
+        /**
+         * Notify a listener that the scroller wants to escape from the scrolling motion and
+         * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
+         *
+         * @param velocity The velocity that the Scroller had when over flinging
+         * @param open Should the fling open or close the overscroll view.
+         */
+        void flingTopOverscroll(float velocity, boolean open);
+    }
+
+    private class NotificationSwipeHelper extends SwipeHelper
+            implements NotificationSwipeActionHelper {
+        private static final long COVER_MENU_DELAY = 4000;
+        private Runnable mFalsingCheck;
+        private Handler mHandler;
+
+        public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
+            super(swipeDirection, callback, context);
+            mHandler = new Handler();
+            mFalsingCheck = new Runnable() {
+                @Override
+                public void run() {
+                    resetExposedMenuView(true /* animate */, true /* force */);
+                }
+            };
+        }
+
+        @Override
+        public void onDownUpdate(View currView, MotionEvent ev) {
+            mTranslatingParentView = currView;
+            if (mCurrMenuRow != null) {
+                mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
+            }
+            mCurrMenuRow = null;
+            mHandler.removeCallbacks(mFalsingCheck);
+
+            // Slide back any notifications that might be showing a menu
+            resetExposedMenuView(true /* animate */, false /* force */);
+
+            if (currView instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
+
+                if (row.getEntry().hasFinishedInitialization()) {
+                    mCurrMenuRow = row.createMenu();
+                    mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
+                    mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
+                    mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
+                }
+            }
+        }
+
+        @Override
+        public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
+            mHandler.removeCallbacks(mFalsingCheck);
+            if (mCurrMenuRow != null) {
+                mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
+            }
+        }
+
+        @Override
+        public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
+                float translation) {
+            if (mCurrMenuRow != null) {
+                return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
+            }
+            return false;
+        }
+
+        @Override
+        public void dismissChild(final View view, float velocity,
+                boolean useAccelerateInterpolator) {
+            super.dismissChild(view, velocity, useAccelerateInterpolator);
+            if (mIsExpanded) {
+                // We don't want to quick-dismiss when it's a heads up as this might lead to closing
+                // of the panel early.
+                handleChildViewDismissed(view);
+            }
+            mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
+            handleMenuCoveredOrDismissed();
+        }
+
+        @Override
+        public void snapChild(final View animView, final float targetLeft, float velocity) {
+            super.snapChild(animView, targetLeft, velocity);
+            onDragCancelled(animView);
+            if (targetLeft == 0) {
+                handleMenuCoveredOrDismissed();
+            }
+        }
+
+        @Override
+        public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
+            mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
+        }
+
+        public boolean isFalseGesture(MotionEvent ev) {
+            return super.isFalseGesture(ev);
+        }
+
+        private void handleMenuCoveredOrDismissed() {
+            if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
+                mMenuExposedView = null;
+            }
+        }
+
+        @Override
+        public Animator getViewTranslationAnimator(View v, float target,
+                AnimatorUpdateListener listener) {
+            if (v instanceof ExpandableNotificationRow) {
+                return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
+            } else {
+                return super.getViewTranslationAnimator(v, target, listener);
+            }
+        }
+
+        @Override
+        public void setTranslation(View v, float translate) {
+            ((ExpandableView) v).setTranslation(translate);
+        }
+
+        @Override
+        public float getTranslation(View v) {
+            return ((ExpandableView) v).getTranslation();
+        }
+
+        @Override
+        public void dismiss(View animView, float velocity) {
+            dismissChild(animView, velocity,
+                    !swipedFastEnough(0, 0) /* useAccelerateInterpolator */);
+        }
+
+        @Override
+        public void snap(View animView, float targetLeft, float velocity) {
+            snapChild(animView, targetLeft, velocity);
+        }
+
+        @Override
+        public boolean swipedFarEnough(float translation, float viewSize) {
+            return swipedFarEnough();
+        }
+
+        @Override
+        public boolean swipedFastEnough(float translation, float velocity) {
+            return swipedFastEnough();
+        }
+
+        @Override
+        public float getMinDismissVelocity() {
+            return getEscapeVelocity();
+        }
+
+        public void onMenuShown(View animView) {
+            onDragCancelled(animView);
+
+            // If we're on the lockscreen we want to false this.
+            if (isAntiFalsingNeeded()) {
+                mHandler.removeCallbacks(mFalsingCheck);
+                mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
+            }
+        }
+
+        public void closeControlsIfOutsideTouch(MotionEvent ev) {
+            NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
+            View view = null;
+            if (guts != null && !guts.getGutsContent().isLeavebehind()) {
+                // Only close visible guts if they're not a leavebehind.
+                view = guts;
+            } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
+                    && mTranslatingParentView != null) {
+                // Checking menu
+                view = mTranslatingParentView;
+            }
+            if (view != null && !isTouchInView(ev, view)) {
+                // Touch was outside visible guts / menu notification, close what's visible
+                mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
+                        false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+                        false /* resetMenu */);
+                resetExposedMenuView(true /* animate */, true /* force */);
+            }
+        }
+
+        public void resetExposedMenuView(boolean animate, boolean force) {
+            if (mMenuExposedView == null
+                    || (!force && mMenuExposedView == mTranslatingParentView)) {
+                // If no menu is showing or it's showing for this view we do nothing.
+                return;
+            }
+            final View prevMenuExposedView = mMenuExposedView;
+            if (animate) {
+                Animator anim = getViewTranslationAnimator(prevMenuExposedView,
+                        0 /* leftTarget */, null /* updateListener */);
+                if (anim != null) {
+                    anim.start();
+                }
+            } else if (mMenuExposedView instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) mMenuExposedView;
+                if (!row.isRemoved()) {
+                    row.resetTranslation();
+                }
+            }
+            mMenuExposedView = null;
+        }
+    }
+
+    private boolean isTouchInView(MotionEvent ev, View view) {
+        if (view == null) {
+            return false;
+        }
+        final int height = (view instanceof ExpandableView)
+                ? ((ExpandableView) view).getActualHeight()
+                : view.getHeight();
+        final int rx = (int) ev.getRawX();
+        final int ry = (int) ev.getRawY();
+        view.getLocationOnScreen(mTempInt2);
+        final int x = mTempInt2[0];
+        final int y = mTempInt2[1];
+        Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
+        boolean ret = rect.contains(rx, ry);
+        return ret;
+    }
+
+    private void updateContinuousShadowDrawing() {
+        boolean continuousShadowUpdate = mAnimationRunning
+                || !mAmbientState.getDraggedViews().isEmpty();
+        if (continuousShadowUpdate != mContinuousShadowUpdate) {
+            if (continuousShadowUpdate) {
+                getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
+            } else {
+                getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
+            }
+            mContinuousShadowUpdate = continuousShadowUpdate;
+        }
+    }
+
+    @Override
+    public void resetExposedMenuView(boolean animate, boolean force) {
+        mSwipeHelper.resetExposedMenuView(animate, force);
+    }
+
+    public void closeControlsIfOutsideTouch(MotionEvent ev) {
+        mSwipeHelper.closeControlsIfOutsideTouch(ev);
+    }
+
+    static class AnimationEvent {
+
+        static AnimationFilter[] FILTERS = new AnimationFilter[] {
+
+                // ANIMATION_TYPE_ADD
+                new AnimationFilter()
+                        .animateShadowAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ()
+                        .hasDelays(),
+
+                // ANIMATION_TYPE_REMOVE
+                new AnimationFilter()
+                        .animateShadowAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ()
+                        .hasDelays(),
+
+                // ANIMATION_TYPE_REMOVE_SWIPED_OUT
+                new AnimationFilter()
+                        .animateShadowAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ()
+                        .hasDelays(),
+
+                // ANIMATION_TYPE_TOP_PADDING_CHANGED
+                new AnimationFilter()
+                        .animateShadowAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateDimmed()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_START_DRAG
+                new AnimationFilter()
+                        .animateShadowAlpha(),
+
+                // ANIMATION_TYPE_SNAP_BACK
+                new AnimationFilter()
+                        .animateShadowAlpha()
+                        .animateHeight(),
+
+                // ANIMATION_TYPE_ACTIVATED_CHILD
+                new AnimationFilter()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_DIMMED
+                new AnimationFilter()
+                        .animateDimmed(),
+
+                // ANIMATION_TYPE_CHANGE_POSITION
+                new AnimationFilter()
+                        .animateAlpha() // maybe the children change positions
+                        .animateShadowAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_DARK
+                null, // Unused
+
+                // ANIMATION_TYPE_GO_TO_FULL_SHADE
+                new AnimationFilter()
+                        .animateShadowAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateDimmed()
+                        .animateZ()
+                        .hasDelays(),
+
+                // ANIMATION_TYPE_HIDE_SENSITIVE
+                new AnimationFilter()
+                        .animateHideSensitive(),
+
+                // ANIMATION_TYPE_VIEW_RESIZE
+                new AnimationFilter()
+                        .animateShadowAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateShadowAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_HEADS_UP_APPEAR
+                new AnimationFilter()
+                        .animateShadowAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
+                new AnimationFilter()
+                        .animateShadowAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ()
+                        .hasDelays(),
+
+                // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+                new AnimationFilter()
+                        .animateShadowAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ()
+                        .hasDelays(),
+
+                // ANIMATION_TYPE_HEADS_UP_OTHER
+                new AnimationFilter()
+                        .animateShadowAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_EVERYTHING
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateShadowAlpha()
+                        .animateDark()
+                        .animateDimmed()
+                        .animateHideSensitive()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_PULSE_APPEAR
+                new AnimationFilter()
+                        .animateAlpha()
+                        .hasDelays()
+                        .animateY(),
+
+                // ANIMATION_TYPE_PULSE_DISAPPEAR
+                new AnimationFilter()
+                        .animateAlpha()
+                        .hasDelays()
+                        .animateY(),
+        };
+
+        static int[] LENGTHS = new int[] {
+
+                // ANIMATION_TYPE_ADD
+                StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
+
+                // ANIMATION_TYPE_REMOVE
+                StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
+
+                // ANIMATION_TYPE_REMOVE_SWIPED_OUT
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_TOP_PADDING_CHANGED
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_START_DRAG
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_SNAP_BACK
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_ACTIVATED_CHILD
+                StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
+
+                // ANIMATION_TYPE_DIMMED
+                StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
+
+                // ANIMATION_TYPE_CHANGE_POSITION
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_DARK
+                StackStateAnimator.ANIMATION_DURATION_WAKEUP,
+
+                // ANIMATION_TYPE_GO_TO_FULL_SHADE
+                StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
+
+                // ANIMATION_TYPE_HIDE_SENSITIVE
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_VIEW_RESIZE
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_HEADS_UP_APPEAR
+                StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
+
+                // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
+                StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
+
+                // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+                StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
+
+                // ANIMATION_TYPE_HEADS_UP_OTHER
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_EVERYTHING
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_PULSE_APPEAR
+                StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR,
+
+                // ANIMATION_TYPE_PULSE_DISAPPEAR
+                StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR / 2,
+        };
+
+        static final int ANIMATION_TYPE_ADD = 0;
+        static final int ANIMATION_TYPE_REMOVE = 1;
+        static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
+        static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
+        static final int ANIMATION_TYPE_START_DRAG = 4;
+        static final int ANIMATION_TYPE_SNAP_BACK = 5;
+        static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
+        static final int ANIMATION_TYPE_DIMMED = 7;
+        static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
+        static final int ANIMATION_TYPE_DARK = 9;
+        static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
+        static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
+        static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
+        static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
+        static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
+        static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
+        static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
+        static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
+        static final int ANIMATION_TYPE_EVERYTHING = 18;
+        static final int ANIMATION_TYPE_PULSE_APPEAR = 19;
+        static final int ANIMATION_TYPE_PULSE_DISAPPEAR = 20;
+
+        static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
+        static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
+
+        final long eventStartTime;
+        final View changingView;
+        final int animationType;
+        final AnimationFilter filter;
+        final long length;
+        View viewAfterChangingView;
+        int darkAnimationOriginIndex;
+        boolean headsUpFromBottom;
+
+        AnimationEvent(View view, int type) {
+            this(view, type, LENGTHS[type]);
+        }
+
+        AnimationEvent(View view, int type, AnimationFilter filter) {
+            this(view, type, LENGTHS[type], filter);
+        }
+
+        AnimationEvent(View view, int type, long length) {
+            this(view, type, length, FILTERS[type]);
+        }
+
+        AnimationEvent(View view, int type, long length, AnimationFilter filter) {
+            eventStartTime = AnimationUtils.currentAnimationTimeMillis();
+            changingView = view;
+            animationType = type;
+            this.length = length;
+            this.filter = filter;
+        }
+
+        /**
+         * Combines the length of several animation events into a single value.
+         *
+         * @param events The events of the lengths to combine.
+         * @return The combined length. Depending on the event types, this might be the maximum of
+         *         all events or the length of a specific event.
+         */
+        static long combineLength(ArrayList<AnimationEvent> events) {
+            long length = 0;
+            int size = events.size();
+            for (int i = 0; i < size; i++) {
+                AnimationEvent event = events.get(i);
+                length = Math.max(length, event.length);
+                if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
+                    return event.length;
+                }
+            }
+            return length;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
new file mode 100644
index 0000000..742d89d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -0,0 +1,655 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.FooterView;
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.notification.NotificationUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * The Algorithm of the {@link com.android.systemui.statusbar.notification.stack
+ * .NotificationStackScrollLayout} which can be queried for {@link com.android.systemui.statusbar
+ * .stack.StackScrollState}
+ */
+public class StackScrollAlgorithm {
+
+    private static final String LOG_TAG = "StackScrollAlgorithm";
+
+    private int mPaddingBetweenElements;
+    private int mIncreasedPaddingBetweenElements;
+    private int mCollapsedSize;
+
+    private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
+    private boolean mIsExpanded;
+    private boolean mClipNotificationScrollToTop;
+    private int mStatusBarHeight;
+    private float mHeadsUpInset;
+    private int mPinnedZTranslationExtra;
+
+    public StackScrollAlgorithm(Context context) {
+        initView(context);
+    }
+
+    public void initView(Context context) {
+        initConstants(context);
+    }
+
+    private void initConstants(Context context) {
+        Resources res = context.getResources();
+        mPaddingBetweenElements = res.getDimensionPixelSize(
+                R.dimen.notification_divider_height);
+        mIncreasedPaddingBetweenElements =
+                res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
+        mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
+        mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
+        mClipNotificationScrollToTop = res.getBoolean(R.bool.config_clipNotificationScrollToTop);
+        mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
+                R.dimen.heads_up_status_bar_padding);
+        mPinnedZTranslationExtra = res.getDimensionPixelSize(
+                R.dimen.heads_up_pinned_elevation);
+    }
+
+    public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
+        // The state of the local variables are saved in an algorithmState to easily subdivide it
+        // into multiple phases.
+        StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
+
+        // First we reset the view states to their default values.
+        resultState.resetViewStates();
+
+        initAlgorithmState(resultState, algorithmState, ambientState);
+
+        updatePositionsForState(resultState, algorithmState, ambientState);
+
+        updateZValuesForState(resultState, algorithmState, ambientState);
+
+        updateHeadsUpStates(resultState, algorithmState, ambientState);
+
+        handleDraggedViews(ambientState, resultState, algorithmState);
+        updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState);
+        updateClipping(resultState, algorithmState, ambientState);
+        updateSpeedBumpState(resultState, algorithmState, ambientState);
+        updateShelfState(resultState, ambientState);
+        getNotificationChildrenStates(resultState, algorithmState, ambientState);
+    }
+
+    private void getNotificationChildrenStates(StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState) {
+        int childCount = algorithmState.visibleChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            ExpandableView v = algorithmState.visibleChildren.get(i);
+            if (v instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+                row.getChildrenStates(resultState, ambientState);
+            }
+        }
+    }
+
+    private void updateSpeedBumpState(StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+        int childCount = algorithmState.visibleChildren.size();
+        int belowSpeedBump = ambientState.getSpeedBumpIndex();
+        for (int i = 0; i < childCount; i++) {
+            View child = algorithmState.visibleChildren.get(i);
+            ExpandableViewState childViewState = resultState.getViewStateForView(child);
+
+            // The speed bump can also be gone, so equality needs to be taken when comparing
+            // indices.
+            childViewState.belowSpeedBump = i >= belowSpeedBump;
+        }
+
+    }
+    private void updateShelfState(StackScrollState resultState, AmbientState ambientState) {
+        NotificationShelf shelf = ambientState.getShelf();
+        if (shelf != null) {
+            shelf.updateState(resultState, ambientState);
+        }
+    }
+
+    private void updateClipping(StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+        float drawStart = !ambientState.isOnKeyguard() ? ambientState.getTopPadding()
+                + ambientState.getStackTranslation() + ambientState.getExpandAnimationTopChange()
+                : 0;
+        float previousNotificationEnd = 0;
+        float previousNotificationStart = 0;
+        int childCount = algorithmState.visibleChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            ExpandableView child = algorithmState.visibleChildren.get(i);
+            ExpandableViewState state = resultState.getViewStateForView(child);
+            if (!child.mustStayOnScreen() || state.headsUpIsVisible) {
+                previousNotificationEnd = Math.max(drawStart, previousNotificationEnd);
+                previousNotificationStart = Math.max(drawStart, previousNotificationStart);
+            }
+            float newYTranslation = state.yTranslation;
+            float newHeight = state.height;
+            float newNotificationEnd = newYTranslation + newHeight;
+            boolean isHeadsUp = (child instanceof ExpandableNotificationRow)
+                    && ((ExpandableNotificationRow) child).isPinned();
+            if (mClipNotificationScrollToTop
+                    && !state.inShelf && newYTranslation < previousNotificationEnd
+                    && (!isHeadsUp || ambientState.isShadeExpanded())) {
+                // The previous view is overlapping on top, clip!
+                float overlapAmount = previousNotificationEnd - newYTranslation;
+                state.clipTopAmount = (int) overlapAmount;
+            } else {
+                state.clipTopAmount = 0;
+            }
+
+            if (!child.isTransparent()) {
+                // Only update the previous values if we are not transparent,
+                // otherwise we would clip to a transparent view.
+                previousNotificationEnd = newNotificationEnd;
+                previousNotificationStart = newYTranslation;
+            }
+        }
+    }
+
+    public static boolean canChildBeDismissed(View v) {
+        if (!(v instanceof ExpandableNotificationRow)) {
+            return false;
+        }
+        ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+        if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
+            return false;
+        }
+        return row.canViewBeDismissed();
+    }
+
+    /**
+     * Updates the dimmed, activated and hiding sensitive states of the children.
+     */
+    private void updateDimmedActivatedHideSensitive(AmbientState ambientState,
+            StackScrollState resultState, StackScrollAlgorithmState algorithmState) {
+        boolean dimmed = ambientState.isDimmed();
+        boolean dark = ambientState.isFullyDark();
+        boolean hideSensitive = ambientState.isHideSensitive();
+        View activatedChild = ambientState.getActivatedChild();
+        int childCount = algorithmState.visibleChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            View child = algorithmState.visibleChildren.get(i);
+            ExpandableViewState childViewState = resultState.getViewStateForView(child);
+            childViewState.dimmed = dimmed;
+            childViewState.dark = dark;
+            childViewState.hideSensitive = hideSensitive;
+            boolean isActivatedChild = activatedChild == child;
+            if (dimmed && isActivatedChild) {
+                childViewState.zTranslation += 2.0f * ambientState.getZDistanceBetweenElements();
+            }
+        }
+    }
+
+    /**
+     * Handle the special state when views are being dragged
+     */
+    private void handleDraggedViews(AmbientState ambientState, StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState) {
+        ArrayList<View> draggedViews = ambientState.getDraggedViews();
+        for (View draggedView : draggedViews) {
+            int childIndex = algorithmState.visibleChildren.indexOf(draggedView);
+            if (childIndex >= 0 && childIndex < algorithmState.visibleChildren.size() - 1) {
+                View nextChild = algorithmState.visibleChildren.get(childIndex + 1);
+                if (!draggedViews.contains(nextChild)) {
+                    // only if the view is not dragged itself we modify its state to be fully
+                    // visible
+                    ExpandableViewState viewState = resultState.getViewStateForView(
+                            nextChild);
+                    // The child below the dragged one must be fully visible
+                    if (ambientState.isShadeExpanded()) {
+                        viewState.shadowAlpha = 1;
+                        viewState.hidden = false;
+                    }
+                }
+
+                // Lets set the alpha to the one it currently has, as its currently being dragged
+                ExpandableViewState viewState = resultState.getViewStateForView(draggedView);
+                // The dragged child should keep the set alpha
+                viewState.alpha = draggedView.getAlpha();
+            }
+        }
+    }
+
+    /**
+     * Initialize the algorithm state like updating the visible children.
+     */
+    private void initAlgorithmState(StackScrollState resultState, StackScrollAlgorithmState state,
+            AmbientState ambientState) {
+        float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
+
+        int scrollY = ambientState.getScrollY();
+
+        // Due to the overScroller, the stackscroller can have negative scroll state. This is
+        // already accounted for by the top padding and doesn't need an additional adaption
+        scrollY = Math.max(0, scrollY);
+        state.scrollY = (int) (scrollY + bottomOverScroll);
+
+        //now init the visible children and update paddings
+        ViewGroup hostView = resultState.getHostView();
+        int childCount = hostView.getChildCount();
+        state.visibleChildren.clear();
+        state.visibleChildren.ensureCapacity(childCount);
+        state.paddingMap.clear();
+        int notGoneIndex = 0;
+        ExpandableView lastView = null;
+        int firstHiddenIndex = ambientState.isDark()
+                ? (ambientState.hasPulsingNotifications() ? 1 : 0)
+                : childCount;
+
+        // The goal here is to fill the padding map, by iterating over how much padding each child
+        // needs. The map is thereby reused, by first filling it with the padding amount and when
+        // iterating over it again, it's filled with the actual resolved value.
+
+        for (int i = 0; i < childCount; i++) {
+            ExpandableView v = (ExpandableView) hostView.getChildAt(i);
+            if (v.getVisibility() != View.GONE) {
+                if (v == ambientState.getShelf()) {
+                    continue;
+                }
+                if (i >= firstHiddenIndex) {
+                    // we need normal padding now, to be in sync with what the stack calculates
+                    lastView = null;
+                }
+                notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
+                float increasedPadding = v.getIncreasedPaddingAmount();
+                if (increasedPadding != 0.0f) {
+                    state.paddingMap.put(v, increasedPadding);
+                    if (lastView != null) {
+                        Float prevValue = state.paddingMap.get(lastView);
+                        float newValue = getPaddingForValue(increasedPadding);
+                        if (prevValue != null) {
+                            float prevPadding = getPaddingForValue(prevValue);
+                            if (increasedPadding > 0) {
+                                newValue = NotificationUtils.interpolate(
+                                        prevPadding,
+                                        newValue,
+                                        increasedPadding);
+                            } else if (prevValue > 0) {
+                                newValue = NotificationUtils.interpolate(
+                                        newValue,
+                                        prevPadding,
+                                        prevValue);
+                            }
+                        }
+                        state.paddingMap.put(lastView, newValue);
+                    }
+                } else if (lastView != null) {
+
+                    // Let's now resolve the value to an actual padding
+                    float newValue = getPaddingForValue(state.paddingMap.get(lastView));
+                    state.paddingMap.put(lastView, newValue);
+                }
+                if (v instanceof ExpandableNotificationRow) {
+                    ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+
+                    // handle the notgoneIndex for the children as well
+                    List<ExpandableNotificationRow> children =
+                            row.getNotificationChildren();
+                    if (row.isSummaryWithChildren() && children != null) {
+                        for (ExpandableNotificationRow childRow : children) {
+                            if (childRow.getVisibility() != View.GONE) {
+                                ExpandableViewState childState
+                                        = resultState.getViewStateForView(childRow);
+                                childState.notGoneIndex = notGoneIndex;
+                                notGoneIndex++;
+                            }
+                        }
+                    }
+                }
+                lastView = v;
+            }
+        }
+        ExpandableNotificationRow expandingNotification = ambientState.getExpandingNotification();
+        state.indexOfExpandingNotification = expandingNotification != null
+                ? expandingNotification.isChildInGroup()
+                    ? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
+                    : state.visibleChildren.indexOf(expandingNotification)
+                : -1;
+    }
+
+    private float getPaddingForValue(Float increasedPadding) {
+        if (increasedPadding == null) {
+            return mPaddingBetweenElements;
+        } else if (increasedPadding >= 0.0f) {
+            return NotificationUtils.interpolate(
+                    mPaddingBetweenElements,
+                    mIncreasedPaddingBetweenElements,
+                    increasedPadding);
+        } else {
+            return NotificationUtils.interpolate(
+                    0,
+                    mPaddingBetweenElements,
+                    1.0f + increasedPadding);
+        }
+    }
+
+    private int updateNotGoneIndex(StackScrollState resultState,
+            StackScrollAlgorithmState state, int notGoneIndex,
+            ExpandableView v) {
+        ExpandableViewState viewState = resultState.getViewStateForView(v);
+        viewState.notGoneIndex = notGoneIndex;
+        state.visibleChildren.add(v);
+        notGoneIndex++;
+        return notGoneIndex;
+    }
+
+    /**
+     * Determine the positions for the views. This is the main part of the algorithm.
+     *
+     * @param resultState The result state to update if a change to the properties of a child occurs
+     * @param algorithmState The state in which the current pass of the algorithm is currently in
+     * @param ambientState The current ambient state
+     */
+    private void updatePositionsForState(StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+
+        // The y coordinate of the current child.
+        float currentYPosition = -algorithmState.scrollY;
+        int childCount = algorithmState.visibleChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            currentYPosition = updateChild(i, resultState, algorithmState, ambientState,
+                    currentYPosition);
+        }
+    }
+
+    protected float updateChild(int i, StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState, AmbientState ambientState,
+            float currentYPosition) {
+        ExpandableView child = algorithmState.visibleChildren.get(i);
+        ExpandableViewState childViewState = resultState.getViewStateForView(child);
+        childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
+        int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
+        int childHeight = getMaxAllowedChildHeight(child);
+        childViewState.yTranslation = currentYPosition;
+        boolean isFooterView = child instanceof FooterView;
+        boolean isEmptyShadeView = child instanceof EmptyShadeView;
+
+        childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
+        float inset = ambientState.getTopPadding() + ambientState.getStackTranslation();
+        if (i <= algorithmState.getIndexOfExpandingNotification()) {
+            inset += ambientState.getExpandAnimationTopChange();
+        }
+        if (child.mustStayOnScreen() && childViewState.yTranslation >= 0) {
+            // Even if we're not scrolled away we're in view and we're also not in the
+            // shelf. We can relax the constraints and let us scroll off the top!
+            float end = childViewState.yTranslation + childViewState.height + inset;
+            childViewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation();
+        }
+        if (isFooterView) {
+            childViewState.yTranslation = Math.min(childViewState.yTranslation,
+                    ambientState.getInnerHeight() - childHeight);
+        } else if (isEmptyShadeView) {
+            childViewState.yTranslation = ambientState.getInnerHeight() - childHeight
+                    + ambientState.getStackTranslation() * 0.25f;
+        } else {
+            clampPositionToShelf(child, childViewState, ambientState);
+        }
+
+        currentYPosition = childViewState.yTranslation + childHeight + paddingAfterChild;
+        if (currentYPosition <= 0) {
+            childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
+        }
+        if (childViewState.location == ExpandableViewState.LOCATION_UNKNOWN) {
+            Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
+        }
+
+        childViewState.yTranslation += inset;
+        return currentYPosition;
+    }
+
+    protected int getPaddingAfterChild(StackScrollAlgorithmState algorithmState,
+            ExpandableView child) {
+        return algorithmState.getPaddingAfterChild(child);
+    }
+
+    private void updateHeadsUpStates(StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+        int childCount = algorithmState.visibleChildren.size();
+        ExpandableNotificationRow topHeadsUpEntry = null;
+        for (int i = 0; i < childCount; i++) {
+            View child = algorithmState.visibleChildren.get(i);
+            if (!(child instanceof ExpandableNotificationRow)) {
+                break;
+            }
+            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+            if (!row.isHeadsUp()) {
+                break;
+            }
+            ExpandableViewState childState = resultState.getViewStateForView(row);
+            if (topHeadsUpEntry == null && row.mustStayOnScreen() && !childState.headsUpIsVisible) {
+                topHeadsUpEntry = row;
+                childState.location = ExpandableViewState.LOCATION_FIRST_HUN;
+            }
+            boolean isTopEntry = topHeadsUpEntry == row;
+            float unmodifiedEndLocation = childState.yTranslation + childState.height;
+            if (mIsExpanded) {
+                if (row.mustStayOnScreen() && !childState.headsUpIsVisible) {
+                    // Ensure that the heads up is always visible even when scrolled off
+                    clampHunToTop(ambientState, row, childState);
+                    if (i == 0 && ambientState.isAboveShelf(row)) {
+                        // the first hun can't get off screen.
+                        clampHunToMaxTranslation(ambientState, row, childState);
+                        childState.hidden = false;
+                    }
+                }
+            }
+            if (row.isPinned()) {
+                childState.yTranslation = Math.max(childState.yTranslation, mHeadsUpInset);
+                childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
+                childState.hidden = false;
+                ExpandableViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
+                if (topState != null && !isTopEntry && (!mIsExpanded
+                        || unmodifiedEndLocation < topState.yTranslation + topState.height)) {
+                    // Ensure that a headsUp doesn't vertically extend further than the heads-up at
+                    // the top most z-position
+                    childState.height = row.getIntrinsicHeight();
+                    childState.yTranslation = topState.yTranslation + topState.height
+                            - childState.height;
+                }
+
+                // heads up notification show and this row is the top entry of heads up
+                // notifications. i.e. this row should be the only one row that has input field
+                // To check if the row need to do translation according to scroll Y
+                // heads up show full of row's content and any scroll y indicate that the
+                // translationY need to move up the HUN.
+                if (!mIsExpanded && isTopEntry && ambientState.getScrollY() > 0) {
+                    childState.yTranslation -= ambientState.getScrollY();
+                }
+            }
+            if (row.isHeadsUpAnimatingAway()) {
+                childState.hidden = false;
+            }
+        }
+    }
+
+    private void clampHunToTop(AmbientState ambientState, ExpandableNotificationRow row,
+            ExpandableViewState childState) {
+        float newTranslation = Math.max(ambientState.getTopPadding()
+                + ambientState.getStackTranslation(), childState.yTranslation);
+        childState.height = (int) Math.max(childState.height - (newTranslation
+                - childState.yTranslation), row.getCollapsedHeight());
+        childState.yTranslation = newTranslation;
+    }
+
+    private void clampHunToMaxTranslation(AmbientState ambientState, ExpandableNotificationRow row,
+            ExpandableViewState childState) {
+        float newTranslation;
+        float maxHeadsUpTranslation = ambientState.getMaxHeadsUpTranslation();
+        float maxShelfPosition = ambientState.getInnerHeight() + ambientState.getTopPadding()
+                + ambientState.getStackTranslation();
+        maxHeadsUpTranslation = Math.min(maxHeadsUpTranslation, maxShelfPosition);
+        float bottomPosition = maxHeadsUpTranslation - row.getCollapsedHeight();
+        newTranslation = Math.min(childState.yTranslation, bottomPosition);
+        childState.height = (int) Math.min(childState.height, maxHeadsUpTranslation
+                - newTranslation);
+        childState.yTranslation = newTranslation;
+    }
+
+    /**
+     * Clamp the height of the child down such that its end is at most on the beginning of
+     * the shelf.
+     *
+     * @param child
+     * @param childViewState the view state of the child
+     * @param ambientState the ambient state
+     */
+    private void clampPositionToShelf(ExpandableView child,
+            ExpandableViewState childViewState,
+            AmbientState ambientState) {
+        if (ambientState.getShelf() == null) {
+            return;
+        }
+
+        int shelfStart = ambientState.getInnerHeight()
+                - ambientState.getShelf().getIntrinsicHeight();
+        if (ambientState.isAppearing() && !child.isAboveShelf()) {
+            // Don't show none heads-up notifications while in appearing phase.
+            childViewState.yTranslation = Math.max(childViewState.yTranslation, shelfStart);
+        }
+        childViewState.yTranslation = Math.min(childViewState.yTranslation, shelfStart);
+        if (childViewState.yTranslation >= shelfStart) {
+            childViewState.hidden = !child.isExpandAnimationRunning() && !child.hasExpandingChild();
+            childViewState.inShelf = true;
+            childViewState.headsUpIsVisible = false;
+        }
+    }
+
+    protected int getMaxAllowedChildHeight(View child) {
+        if (child instanceof ExpandableView) {
+            ExpandableView expandableView = (ExpandableView) child;
+            return expandableView.getIntrinsicHeight();
+        }
+        return child == null? mCollapsedSize : child.getHeight();
+    }
+
+    /**
+     * Calculate the Z positions for all children based on the number of items in both stacks and
+     * save it in the resultState
+     *  @param resultState The result state to update the zTranslation values
+     * @param algorithmState The state in which the current pass of the algorithm is currently in
+     * @param ambientState The ambient state of the algorithm
+     */
+    private void updateZValuesForState(StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+        int childCount = algorithmState.visibleChildren.size();
+        float childrenOnTop = 0.0f;
+        for (int i = childCount - 1; i >= 0; i--) {
+            childrenOnTop = updateChildZValue(i, childrenOnTop,
+                    resultState, algorithmState, ambientState);
+        }
+    }
+
+    protected float updateChildZValue(int i, float childrenOnTop,
+            StackScrollState resultState, StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState) {
+        ExpandableView child = algorithmState.visibleChildren.get(i);
+        ExpandableViewState childViewState = resultState.getViewStateForView(child);
+        int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements();
+        float baseZ = ambientState.getBaseZHeight();
+        if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
+                && !ambientState.isDozingAndNotPulsing(child)
+                && childViewState.yTranslation < ambientState.getTopPadding()
+                + ambientState.getStackTranslation()) {
+            if (childrenOnTop != 0.0f) {
+                childrenOnTop++;
+            } else {
+                float overlap = ambientState.getTopPadding()
+                        + ambientState.getStackTranslation() - childViewState.yTranslation;
+                childrenOnTop += Math.min(1.0f, overlap / childViewState.height);
+            }
+            childViewState.zTranslation = baseZ
+                    + childrenOnTop * zDistanceBetweenElements;
+        } else if (i == 0 && ambientState.isAboveShelf(child)) {
+            // In case this is a new view that has never been measured before, we don't want to
+            // elevate if we are currently expanded more then the notification
+            int shelfHeight = ambientState.getShelf() == null ? 0 :
+                    ambientState.getShelf().getIntrinsicHeight();
+            float shelfStart = ambientState.getInnerHeight()
+                    - shelfHeight + ambientState.getTopPadding()
+                    + ambientState.getStackTranslation();
+            float notificationEnd = childViewState.yTranslation + child.getPinnedHeadsUpHeight()
+                    + mPaddingBetweenElements;
+            if (shelfStart > notificationEnd) {
+                childViewState.zTranslation = baseZ;
+            } else {
+                float factor = (notificationEnd - shelfStart) / shelfHeight;
+                factor = Math.min(factor, 1.0f);
+                childViewState.zTranslation = baseZ + factor * zDistanceBetweenElements;
+            }
+        } else {
+            childViewState.zTranslation = baseZ;
+        }
+
+        // We need to scrim the notification more from its surrounding content when we are pinned,
+        // and we therefore elevate it higher.
+        // We can use the headerVisibleAmount for this, since the value nicely goes from 0 to 1 when
+        // expanding after which we have a normal elevation again.
+        childViewState.zTranslation += (1.0f - child.getHeaderVisibleAmount())
+                * mPinnedZTranslationExtra;
+        return childrenOnTop;
+    }
+
+    public void setIsExpanded(boolean isExpanded) {
+        this.mIsExpanded = isExpanded;
+    }
+
+    public class StackScrollAlgorithmState {
+
+        /**
+         * The scroll position of the algorithm
+         */
+        public int scrollY;
+
+        /**
+         * The children from the host view which are not gone.
+         */
+        public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>();
+
+        /**
+         * The padding after each child measured in pixels.
+         */
+        public final HashMap<ExpandableView, Float> paddingMap = new HashMap<>();
+        private int indexOfExpandingNotification;
+
+        public int getPaddingAfterChild(ExpandableView child) {
+            Float padding = paddingMap.get(child);
+            if (padding == null) {
+                // Should only happen for the last view
+                return mPaddingBetweenElements;
+            }
+            return (int) padding.floatValue();
+        }
+
+        public int getIndexOfExpandingNotification() {
+            return indexOfExpandingNotification;
+        }
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java
new file mode 100644
index 0000000..c03fd22
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java
@@ -0,0 +1,118 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+
+import java.util.List;
+import java.util.WeakHashMap;
+
+/**
+ * A state of a
+ * {@link com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout} which
+ * can be applied to a viewGroup.
+ */
+public class StackScrollState {
+
+    private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
+
+    private final ViewGroup mHostView;
+    private WeakHashMap<ExpandableView, ExpandableViewState> mStateMap;
+
+    public StackScrollState(ViewGroup hostView) {
+        mHostView = hostView;
+        mStateMap = new WeakHashMap<>();
+    }
+
+    public ViewGroup getHostView() {
+        return mHostView;
+    }
+
+    public void resetViewStates() {
+        int numChildren = mHostView.getChildCount();
+        for (int i = 0; i < numChildren; i++) {
+            ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
+            resetViewState(child);
+
+            // handling reset for child notifications
+            if (child instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                List<ExpandableNotificationRow> children =
+                        row.getNotificationChildren();
+                if (row.isSummaryWithChildren() && children != null) {
+                    for (ExpandableNotificationRow childRow : children) {
+                        resetViewState(childRow);
+                    }
+                }
+            }
+        }
+    }
+
+    private void resetViewState(ExpandableView view) {
+        ExpandableViewState viewState = mStateMap.get(view);
+        if (viewState == null) {
+            viewState = view.createNewViewState(this);
+            mStateMap.put(view, viewState);
+        }
+        // initialize with the default values of the view
+        viewState.height = view.getIntrinsicHeight();
+        viewState.gone = view.getVisibility() == View.GONE;
+        viewState.alpha = 1f;
+        viewState.shadowAlpha = 1f;
+        viewState.notGoneIndex = -1;
+        viewState.xTranslation = view.getTranslationX();
+        viewState.hidden = false;
+        viewState.scaleX = view.getScaleX();
+        viewState.scaleY = view.getScaleY();
+        viewState.inShelf = false;
+        viewState.headsUpIsVisible = false;
+    }
+
+    public ExpandableViewState getViewStateForView(View requestedView) {
+        return mStateMap.get(requestedView);
+    }
+
+    public void removeViewStateForView(View child) {
+        mStateMap.remove(child);
+    }
+
+    /**
+     * Apply the properties saved in {@link #mStateMap} to the children of the {@link #mHostView}.
+     * The properties are only applied if they effectively changed.
+     */
+    public void apply() {
+        int numChildren = mHostView.getChildCount();
+        for (int i = 0; i < numChildren; i++) {
+            ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
+            ExpandableViewState state = mStateMap.get(child);
+            if (state == null) {
+                Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
+                        "to the hostView");
+                continue;
+            }
+            if (state.gone) {
+                continue;
+            }
+            state.applyToView(child);
+        }
+    }
+}
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
new file mode 100644
index 0000000..da3fb66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -0,0 +1,590 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.util.Property;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.keyguard.KeyguardSliceView;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.StatusBarIconView;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Stack;
+
+/**
+ * An stack state animator which handles animations to new StackScrollStates
+ */
+public class StackStateAnimator {
+
+    public static final int ANIMATION_DURATION_STANDARD = 360;
+    public static final int ANIMATION_DURATION_WAKEUP = 500;
+    public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
+    public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
+    public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
+    public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
+    public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 550;
+    public static final int ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED
+            = (int) (ANIMATION_DURATION_HEADS_UP_APPEAR
+                    * HeadsUpAppearInterpolator.getFractionUntilOvershoot());
+    public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 300;
+    public static final int ANIMATION_DURATION_PULSE_APPEAR =
+            KeyguardSliceView.DEFAULT_ANIM_DURATION;
+    public static final int ANIMATION_DURATION_BLOCKING_HELPER_FADE = 240;
+    public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
+    public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
+    public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
+    public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
+    public static final int ANIMATION_DELAY_HEADS_UP = 120;
+    public static final int ANIMATION_DELAY_HEADS_UP_CLICKED= 120;
+
+    private final int mGoToFullShadeAppearingTranslation;
+    private final int mPulsingAppearingTranslation;
+    private final ExpandableViewState mTmpState = new ExpandableViewState();
+    private final AnimationProperties mAnimationProperties;
+    public NotificationStackScrollLayout mHostLayout;
+    private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
+            new ArrayList<>();
+    private ArrayList<View> mNewAddChildren = new ArrayList<>();
+    private HashSet<View> mHeadsUpAppearChildren = new HashSet<>();
+    private HashSet<View> mHeadsUpDisappearChildren = new HashSet<>();
+    private HashSet<Animator> mAnimatorSet = new HashSet<>();
+    private Stack<AnimatorListenerAdapter> mAnimationListenerPool = new Stack<>();
+    private AnimationFilter mAnimationFilter = new AnimationFilter();
+    private long mCurrentLength;
+    private long mCurrentAdditionalDelay;
+
+    /** The current index for the last child which was not added in this event set. */
+    private int mCurrentLastNotAddedIndex;
+    private ValueAnimator mTopOverScrollAnimator;
+    private ValueAnimator mBottomOverScrollAnimator;
+    private int mHeadsUpAppearHeightBottom;
+    private boolean mShadeExpanded;
+    private ArrayList<ExpandableView> mTransientViewsToRemove = new ArrayList<>();
+    private NotificationShelf mShelf;
+    private float mStatusBarIconLocation;
+    private int[] mTmpLocation = new int[2];
+
+    public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
+        mHostLayout = hostLayout;
+        mGoToFullShadeAppearingTranslation =
+                hostLayout.getContext().getResources().getDimensionPixelSize(
+                        R.dimen.go_to_full_shade_appearing_translation);
+        mPulsingAppearingTranslation =
+                hostLayout.getContext().getResources().getDimensionPixelSize(
+                        R.dimen.pulsing_notification_appear_translation);
+        mAnimationProperties = new AnimationProperties() {
+            @Override
+            public AnimationFilter getAnimationFilter() {
+                return mAnimationFilter;
+            }
+
+            @Override
+            public AnimatorListenerAdapter getAnimationFinishListener() {
+                return getGlobalAnimationFinishedListener();
+            }
+
+            @Override
+            public boolean wasAdded(View view) {
+                return mNewAddChildren.contains(view);
+            }
+
+            @Override
+            public Interpolator getCustomInterpolator(View child, Property property) {
+                if (mHeadsUpAppearChildren.contains(child) && View.TRANSLATION_Y.equals(property)) {
+                    return Interpolators.HEADS_UP_APPEAR;
+                }
+                return null;
+            }
+        };
+    }
+
+    public boolean isRunning() {
+        return !mAnimatorSet.isEmpty();
+    }
+
+    public void startAnimationForEvents(
+            ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents,
+            StackScrollState finalState, long additionalDelay) {
+
+        processAnimationEvents(mAnimationEvents, finalState);
+
+        int childCount = mHostLayout.getChildCount();
+        mAnimationFilter.applyCombination(mNewEvents);
+        mCurrentAdditionalDelay = additionalDelay;
+        mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
+        mCurrentLastNotAddedIndex = findLastNotAddedIndex(finalState);
+        for (int i = 0; i < childCount; i++) {
+            final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
+
+            ExpandableViewState viewState = finalState.getViewStateForView(child);
+            if (viewState == null || child.getVisibility() == View.GONE
+                    || applyWithoutAnimation(child, viewState, finalState)) {
+                continue;
+            }
+
+            initAnimationProperties(finalState, child, viewState);
+            viewState.animateTo(child, mAnimationProperties);
+        }
+        if (!isRunning()) {
+            // no child has preformed any animation, lets finish
+            onAnimationFinished();
+        }
+        mHeadsUpAppearChildren.clear();
+        mHeadsUpDisappearChildren.clear();
+        mNewEvents.clear();
+        mNewAddChildren.clear();
+    }
+
+    private void initAnimationProperties(StackScrollState finalState, ExpandableView child,
+            ExpandableViewState viewState) {
+        boolean wasAdded = mAnimationProperties.wasAdded(child);
+        mAnimationProperties.duration = mCurrentLength;
+        adaptDurationWhenGoingToFullShade(child, viewState, wasAdded);
+        mAnimationProperties.delay = 0;
+        if (wasAdded || mAnimationFilter.hasDelays
+                        && (viewState.yTranslation != child.getTranslationY()
+                        || viewState.zTranslation != child.getTranslationZ()
+                        || viewState.alpha != child.getAlpha()
+                        || viewState.height != child.getActualHeight()
+                        || viewState.clipTopAmount != child.getClipTopAmount()
+                        || viewState.dark != child.isDark()
+                        || viewState.shadowAlpha != child.getShadowAlpha())) {
+            mAnimationProperties.delay = mCurrentAdditionalDelay
+                    + calculateChildAnimationDelay(viewState, finalState);
+        }
+    }
+
+    private void adaptDurationWhenGoingToFullShade(ExpandableView child,
+            ExpandableViewState viewState, boolean wasAdded) {
+        if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) {
+            child.setTranslationY(child.getTranslationY() + mGoToFullShadeAppearingTranslation);
+            float longerDurationFactor = viewState.notGoneIndex - mCurrentLastNotAddedIndex;
+            longerDurationFactor = (float) Math.pow(longerDurationFactor, 0.7f);
+            mAnimationProperties.duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50 +
+                    (long) (100 * longerDurationFactor);
+        }
+    }
+
+    /**
+     * Determines if a view should not perform an animation and applies it directly.
+     *
+     * @return true if no animation should be performed
+     */
+    private boolean applyWithoutAnimation(ExpandableView child, ExpandableViewState viewState,
+            StackScrollState finalState) {
+        if (mShadeExpanded) {
+            return false;
+        }
+        if (ViewState.isAnimatingY(child)) {
+            // A Y translation animation is running
+            return false;
+        }
+        if (mHeadsUpDisappearChildren.contains(child) || mHeadsUpAppearChildren.contains(child)) {
+            // This is a heads up animation
+            return false;
+        }
+        if (NotificationStackScrollLayout.isPinnedHeadsUp(child)) {
+            // This is another headsUp which might move. Let's animate!
+            return false;
+        }
+        viewState.applyToView(child);
+        return true;
+    }
+
+    private int findLastNotAddedIndex(StackScrollState finalState) {
+        int childCount = mHostLayout.getChildCount();
+        for (int i = childCount - 1; i >= 0; i--) {
+            final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
+
+            ExpandableViewState viewState = finalState.getViewStateForView(child);
+            if (viewState == null || child.getVisibility() == View.GONE) {
+                continue;
+            }
+            if (!mNewAddChildren.contains(child)) {
+                return viewState.notGoneIndex;
+            }
+        }
+        return -1;
+    }
+
+    private long calculateChildAnimationDelay(ExpandableViewState viewState,
+            StackScrollState finalState) {
+        if (mAnimationFilter.hasGoToFullShadeEvent) {
+            return calculateDelayGoToFullShade(viewState);
+        }
+        if (mAnimationFilter.customDelay != AnimationFilter.NO_DELAY) {
+            return mAnimationFilter.customDelay;
+        }
+        long minDelay = 0;
+        for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
+            long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING;
+            switch (event.animationType) {
+                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: {
+                    int ownIndex = viewState.notGoneIndex;
+                    int changingIndex = finalState
+                            .getViewStateForView(event.changingView).notGoneIndex;
+                    int difference = Math.abs(ownIndex - changingIndex);
+                    difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
+                            difference - 1));
+                    long delay = (DELAY_EFFECT_MAX_INDEX_DIFFERENCE - difference) * delayPerElement;
+                    minDelay = Math.max(delay, minDelay);
+                    break;
+                }
+                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT:
+                    delayPerElement = ANIMATION_DELAY_PER_ELEMENT_MANUAL;
+                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: {
+                    int ownIndex = viewState.notGoneIndex;
+                    boolean noNextView = event.viewAfterChangingView == null;
+                    View viewAfterChangingView = noNextView
+                            ? mHostLayout.getLastChildNotGone()
+                            : event.viewAfterChangingView;
+                    if (viewAfterChangingView == null) {
+                        // This can happen when the last view in the list is removed.
+                        // Since the shelf is still around and the only view, the code still goes
+                        // in here and tries to calculate the delay for it when case its properties
+                        // have changed.
+                        continue;
+                    }
+                    int nextIndex = finalState
+                            .getViewStateForView(viewAfterChangingView).notGoneIndex;
+                    if (ownIndex >= nextIndex) {
+                        // we only have the view afterwards
+                        ownIndex++;
+                    }
+                    int difference = Math.abs(ownIndex - nextIndex);
+                    difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
+                            difference - 1));
+                    long delay = difference * delayPerElement;
+                    minDelay = Math.max(delay, minDelay);
+                    break;
+                }
+                default:
+                    break;
+            }
+        }
+        return minDelay;
+    }
+
+    private long calculateDelayGoToFullShade(ExpandableViewState viewState) {
+        int shelfIndex = mShelf.getNotGoneIndex();
+        float index = viewState.notGoneIndex;
+        long result = 0;
+        if (index > shelfIndex) {
+            float diff = index - shelfIndex;
+            diff = (float) Math.pow(diff, 0.7f);
+            result += (long) (diff * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE * 0.25);
+            index = shelfIndex;
+        }
+        index = (float) Math.pow(index, 0.7f);
+        result += (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE);
+        return result;
+    }
+
+    /**
+     * @return an adapter which ensures that onAnimationFinished is called once no animation is
+     *         running anymore
+     */
+    private AnimatorListenerAdapter getGlobalAnimationFinishedListener() {
+        if (!mAnimationListenerPool.empty()) {
+            return mAnimationListenerPool.pop();
+        }
+
+        // We need to create a new one, no reusable ones found
+        return new AnimatorListenerAdapter() {
+            private boolean mWasCancelled;
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimatorSet.remove(animation);
+                if (mAnimatorSet.isEmpty() && !mWasCancelled) {
+                    onAnimationFinished();
+                }
+                mAnimationListenerPool.push(this);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mWasCancelled = true;
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mWasCancelled = false;
+                mAnimatorSet.add(animation);
+            }
+        };
+    }
+
+    private void onAnimationFinished() {
+        mHostLayout.onChildAnimationFinished();
+
+        for (ExpandableView transientViewsToRemove : mTransientViewsToRemove) {
+            transientViewsToRemove.getTransientContainer()
+                    .removeTransientView(transientViewsToRemove);
+        }
+        mTransientViewsToRemove.clear();
+    }
+
+    /**
+     * Process the animationEvents for a new animation
+     *
+     * @param animationEvents the animation events for the animation to perform
+     * @param finalState the final state to animate to
+     */
+    private void processAnimationEvents(
+            ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
+            StackScrollState finalState) {
+        for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
+            final ExpandableView changingView = (ExpandableView) event.changingView;
+            if (event.animationType ==
+                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
+
+                // This item is added, initialize it's properties.
+                ExpandableViewState viewState = finalState
+                        .getViewStateForView(changingView);
+                if (viewState == null || viewState.gone) {
+                    // The position for this child was never generated, let's continue.
+                    continue;
+                }
+                viewState.applyToView(changingView);
+                mNewAddChildren.add(changingView);
+
+            } else if (event.animationType ==
+                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
+                if (changingView.getVisibility() != View.VISIBLE) {
+                    removeTransientView(changingView);
+                    continue;
+                }
+
+                // Find the amount to translate up. This is needed in order to understand the
+                // direction of the remove animation (either downwards or upwards)
+                ExpandableViewState viewState = finalState
+                        .getViewStateForView(event.viewAfterChangingView);
+                int actualHeight = changingView.getActualHeight();
+                // upwards by default
+                float translationDirection = -1.0f;
+                if (viewState != null) {
+                    float ownPosition = changingView.getTranslationY();
+                    if (changingView instanceof ExpandableNotificationRow
+                            && event.viewAfterChangingView instanceof ExpandableNotificationRow) {
+                        ExpandableNotificationRow changingRow =
+                                (ExpandableNotificationRow) changingView;
+                        ExpandableNotificationRow nextRow =
+                                (ExpandableNotificationRow) event.viewAfterChangingView;
+                        if (changingRow.isRemoved()
+                                && changingRow.wasChildInGroupWhenRemoved()
+                                && !nextRow.isChildInGroup()) {
+                            // the next row isn't actually a child from a group! Let's
+                            // compare absolute positions!
+                            ownPosition = changingRow.getTranslationWhenRemoved();
+                        }
+                    }
+                    // there was a view after this one, Approximate the distance the next child
+                    // travelled
+                    translationDirection = ((viewState.yTranslation
+                            - (ownPosition + actualHeight / 2.0f)) * 2 /
+                            actualHeight);
+                    translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
+
+                }
+                changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
+                        0 /* delay */, translationDirection,  false /* isHeadsUpAppear */,
+                        0, new Runnable() {
+                    @Override
+                    public void run() {
+                        removeTransientView(changingView);
+                    }
+                }, null);
+            } else if (event.animationType ==
+                NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
+                if (Math.abs(changingView.getTranslation()) == changingView.getWidth()
+                        && changingView.getTransientContainer() != null) {
+                    changingView.getTransientContainer().removeTransientView(changingView);
+                }
+            } else if (event.animationType == NotificationStackScrollLayout
+                    .AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
+                row.prepareExpansionChanged(finalState);
+            } else if (event.animationType == NotificationStackScrollLayout
+                    .AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR) {
+                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+                if (viewState != null) {
+                    mTmpState.copyFrom(viewState);
+                    mTmpState.yTranslation += mPulsingAppearingTranslation;
+                    mTmpState.alpha = 0;
+                    mTmpState.applyToView(changingView);
+                }
+            } else if (event.animationType == NotificationStackScrollLayout
+                    .AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
+                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+                if (viewState != null) {
+                    viewState.alpha = 0;
+                    // We want to animate the alpha away before the view starts translating,
+                    // otherwise everything will overlap and look xtra ugly.
+                    float originalYTranslation = viewState.yTranslation;
+                    viewState.yTranslation = changingView.getTranslationY();
+                    mAnimationFilter.animateAlpha = true;
+                    mAnimationProperties.duration = ANIMATION_DURATION_PULSE_APPEAR / 2;
+                    viewState.animateTo(changingView, mAnimationProperties);
+                    viewState.yTranslation = originalYTranslation;
+                }
+            } else if (event.animationType == NotificationStackScrollLayout
+                    .AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
+                // This item is added, initialize it's properties.
+                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+                mTmpState.copyFrom(viewState);
+                if (event.headsUpFromBottom) {
+                    mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
+                } else {
+                    mTmpState.yTranslation = 0;
+                    changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED,
+                            true /* isHeadsUpAppear */);
+                }
+                mHeadsUpAppearChildren.add(changingView);
+                mTmpState.applyToView(changingView);
+            } else if (event.animationType == NotificationStackScrollLayout
+                            .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR ||
+                    event.animationType == NotificationStackScrollLayout
+                            .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
+                mHeadsUpDisappearChildren.add(changingView);
+                Runnable endRunnable = null;
+                // We need some additional delay in case we were removed to make sure we're not
+                // lagging
+                int extraDelay = event.animationType == NotificationStackScrollLayout
+                        .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+                        ? ANIMATION_DELAY_HEADS_UP_CLICKED
+                        : 0;
+                if (changingView.getParent() == null) {
+                    // This notification was actually removed, so we need to add it transiently
+                    mHostLayout.addTransientView(changingView, 0);
+                    changingView.setTransientContainer(mHostLayout);
+                    mTmpState.initFrom(changingView);
+                    mTmpState.yTranslation = 0;
+                    // We temporarily enable Y animations, the real filter will be combined
+                    // afterwards anyway
+                    mAnimationFilter.animateY = true;
+                    mAnimationProperties.delay = extraDelay + ANIMATION_DELAY_HEADS_UP;
+                    mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR;
+                    mTmpState.animateTo(changingView, mAnimationProperties);
+                    endRunnable = () -> removeTransientView(changingView);
+                }
+                float targetLocation = 0;
+                boolean needsAnimation = true;
+                if (changingView instanceof ExpandableNotificationRow) {
+                    ExpandableNotificationRow row = (ExpandableNotificationRow) changingView;
+                    if (row.isDismissed()) {
+                        needsAnimation = false;
+                    }
+                    StatusBarIconView icon = row.getEntry().icon;
+                    if (icon.getParent() != null) {
+                        icon.getLocationOnScreen(mTmpLocation);
+                        float iconPosition = mTmpLocation[0] - icon.getTranslationX()
+                                + ViewState.getFinalTranslationX(icon) + icon.getWidth() * 0.25f;
+                        mHostLayout.getLocationOnScreen(mTmpLocation);
+                        targetLocation = iconPosition - mTmpLocation[0];
+                    }
+                }
+
+                if (needsAnimation) {
+                    // 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());
+                } else if (endRunnable != null) {
+                    endRunnable.run();
+                }
+            }
+            mNewEvents.add(event);
+        }
+    }
+
+    public static void removeTransientView(ExpandableView viewToRemove) {
+        if (viewToRemove.getTransientContainer() != null) {
+            viewToRemove.getTransientContainer().removeTransientView(viewToRemove);
+        }
+    }
+
+    public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
+            final boolean isRubberbanded) {
+        final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
+        if (targetAmount == startOverScrollAmount) {
+            return;
+        }
+        cancelOverScrollAnimators(onTop);
+        ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount,
+                targetAmount);
+        overScrollAnimator.setDuration(ANIMATION_DURATION_STANDARD);
+        overScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float currentOverScroll = (float) animation.getAnimatedValue();
+                mHostLayout.setOverScrollAmount(
+                        currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */,
+                        isRubberbanded);
+            }
+        });
+        overScrollAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        overScrollAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (onTop) {
+                    mTopOverScrollAnimator = null;
+                } else {
+                    mBottomOverScrollAnimator = null;
+                }
+            }
+        });
+        overScrollAnimator.start();
+        if (onTop) {
+            mTopOverScrollAnimator = overScrollAnimator;
+        } else {
+            mBottomOverScrollAnimator = overScrollAnimator;
+        }
+    }
+
+    public void cancelOverScrollAnimators(boolean onTop) {
+        ValueAnimator currentAnimator = onTop ? mTopOverScrollAnimator : mBottomOverScrollAnimator;
+        if (currentAnimator != null) {
+            currentAnimator.cancel();
+        }
+    }
+
+    public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
+        mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
+    }
+
+    public void setShadeExpanded(boolean shadeExpanded) {
+        mShadeExpanded = shadeExpanded;
+    }
+
+    public void setShelf(NotificationShelf shelf) {
+        mShelf = shelf;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
new file mode 100644
index 0000000..1f3244f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -0,0 +1,713 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.util.Property;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
+
+/**
+ * A state of a view. This can be used to apply a set of view properties to a view with
+ * {@link com.android.systemui.statusbar.notification.stack.StackScrollState} or start
+ * animations with {@link com.android.systemui.statusbar.notification.stack.StackStateAnimator}.
+*/
+public class ViewState {
+
+    /**
+     * Some animation properties that can be used to update running animations but not creating
+     * any new ones.
+     */
+    protected static final AnimationProperties NO_NEW_ANIMATIONS = new AnimationProperties() {
+        AnimationFilter mAnimationFilter = new AnimationFilter();
+        @Override
+        public AnimationFilter getAnimationFilter() {
+            return mAnimationFilter;
+        }
+    };
+    private static final int TAG_ANIMATOR_TRANSLATION_X = R.id.translation_x_animator_tag;
+    private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
+    private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
+    private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
+    private static final int TAG_END_TRANSLATION_X = R.id.translation_x_animator_end_value_tag;
+    private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
+    private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
+    private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
+    private static final int TAG_START_TRANSLATION_X = R.id.translation_x_animator_start_value_tag;
+    private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag;
+    private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag;
+    private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag;
+
+    private static final AnimatableProperty SCALE_X_PROPERTY
+            = new AnimatableProperty() {
+
+        @Override
+        public int getAnimationStartTag() {
+            return R.id.scale_x_animator_start_value_tag;
+        }
+
+        @Override
+        public int getAnimationEndTag() {
+            return R.id.scale_x_animator_end_value_tag;
+        }
+
+        @Override
+        public int getAnimatorTag() {
+            return R.id.scale_x_animator_tag;
+        }
+
+        @Override
+        public Property getProperty() {
+            return View.SCALE_X;
+        }
+    };
+
+    private static final AnimatableProperty SCALE_Y_PROPERTY
+            = new AnimatableProperty() {
+
+        @Override
+        public int getAnimationStartTag() {
+            return R.id.scale_y_animator_start_value_tag;
+        }
+
+        @Override
+        public int getAnimationEndTag() {
+            return R.id.scale_y_animator_end_value_tag;
+        }
+
+        @Override
+        public int getAnimatorTag() {
+            return R.id.scale_y_animator_tag;
+        }
+
+        @Override
+        public Property getProperty() {
+            return View.SCALE_Y;
+        }
+    };
+
+    public float alpha;
+    public float xTranslation;
+    public float yTranslation;
+    public float zTranslation;
+    public boolean gone;
+    public boolean hidden;
+    public float scaleX = 1.0f;
+    public float scaleY = 1.0f;
+
+    public void copyFrom(ViewState viewState) {
+        alpha = viewState.alpha;
+        xTranslation = viewState.xTranslation;
+        yTranslation = viewState.yTranslation;
+        zTranslation = viewState.zTranslation;
+        gone = viewState.gone;
+        hidden = viewState.hidden;
+        scaleX = viewState.scaleX;
+        scaleY = viewState.scaleY;
+    }
+
+    public void initFrom(View view) {
+        alpha = view.getAlpha();
+        xTranslation = view.getTranslationX();
+        yTranslation = view.getTranslationY();
+        zTranslation = view.getTranslationZ();
+        gone = view.getVisibility() == View.GONE;
+        hidden = view.getVisibility() == View.INVISIBLE;
+        scaleX = view.getScaleX();
+        scaleY = view.getScaleY();
+    }
+
+    /**
+     * Applies a {@link ViewState} to a normal view.
+     */
+    public void applyToView(View view) {
+        if (this.gone) {
+            // don't do anything with it
+            return;
+        }
+
+        // apply xTranslation
+        boolean animatingX = isAnimating(view, TAG_ANIMATOR_TRANSLATION_X);
+        if (animatingX) {
+            updateAnimationX(view);
+        } else if (view.getTranslationX() != this.xTranslation){
+            view.setTranslationX(this.xTranslation);
+        }
+
+        // apply yTranslation
+        boolean animatingY = isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y);
+        if (animatingY) {
+            updateAnimationY(view);
+        } else if (view.getTranslationY() != this.yTranslation) {
+            view.setTranslationY(this.yTranslation);
+        }
+
+        // apply zTranslation
+        boolean animatingZ = isAnimating(view, TAG_ANIMATOR_TRANSLATION_Z);
+        if (animatingZ) {
+            updateAnimationZ(view);
+        } else if (view.getTranslationZ() != this.zTranslation) {
+            view.setTranslationZ(this.zTranslation);
+        }
+
+        // apply scaleX
+        boolean animatingScaleX = isAnimating(view, SCALE_X_PROPERTY);
+        if (animatingScaleX) {
+            updateAnimation(view, SCALE_X_PROPERTY, scaleX);
+        } else if (view.getScaleX() != scaleX) {
+            view.setScaleX(scaleX);
+        }
+
+        // apply scaleY
+        boolean animatingScaleY = isAnimating(view, SCALE_Y_PROPERTY);
+        if (animatingScaleY) {
+            updateAnimation(view, SCALE_Y_PROPERTY, scaleY);
+        } else if (view.getScaleY() != scaleY) {
+            view.setScaleY(scaleY);
+        }
+
+        int oldVisibility = view.getVisibility();
+        boolean becomesInvisible = this.alpha == 0.0f
+                || (this.hidden && (!isAnimating(view) || oldVisibility != View.VISIBLE));
+        boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA);
+        if (animatingAlpha) {
+            updateAlphaAnimation(view);
+        } else if (view.getAlpha() != this.alpha) {
+            // apply layer type
+            boolean becomesFullyVisible = this.alpha == 1.0f;
+            boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
+                    && view.hasOverlappingRendering();
+            int layerType = view.getLayerType();
+            int newLayerType = newLayerTypeIsHardware
+                    ? View.LAYER_TYPE_HARDWARE
+                    : View.LAYER_TYPE_NONE;
+            if (layerType != newLayerType) {
+                view.setLayerType(newLayerType, null);
+            }
+
+            // apply alpha
+            view.setAlpha(this.alpha);
+        }
+
+        // apply visibility
+        int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
+        if (newVisibility != oldVisibility) {
+            if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) {
+                // We don't want views to change visibility when they are animating to GONE
+                view.setVisibility(newVisibility);
+            }
+        }
+    }
+
+    public boolean isAnimating(View view) {
+        if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_X)) {
+            return true;
+        }
+        if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y)) {
+            return true;
+        }
+        if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Z)) {
+            return true;
+        }
+        if (isAnimating(view, TAG_ANIMATOR_ALPHA)) {
+            return true;
+        }
+        if (isAnimating(view, SCALE_X_PROPERTY)) {
+            return true;
+        }
+        if (isAnimating(view, SCALE_Y_PROPERTY)) {
+            return true;
+        }
+        return false;
+    }
+
+    private static boolean isAnimating(View view, int tag) {
+        return getChildTag(view, tag) != null;
+    }
+
+    public static boolean isAnimating(View view, AnimatableProperty property) {
+        return getChildTag(view, property.getAnimatorTag()) != null;
+    }
+
+    /**
+     * Start an animation to this viewstate
+     * @param child the view to animate
+     * @param animationProperties the properties of the animation
+     */
+    public void animateTo(View child, AnimationProperties animationProperties) {
+        boolean wasVisible = child.getVisibility() == View.VISIBLE;
+        final float alpha = this.alpha;
+        if (!wasVisible && (alpha != 0 || child.getAlpha() != 0)
+                && !this.gone && !this.hidden) {
+            child.setVisibility(View.VISIBLE);
+        }
+        float childAlpha = child.getAlpha();
+        boolean alphaChanging = this.alpha != childAlpha;
+        if (child instanceof ExpandableView) {
+            // We don't want views to change visibility when they are animating to GONE
+            alphaChanging &= !((ExpandableView) child).willBeGone();
+        }
+
+        // start translationX animation
+        if (child.getTranslationX() != this.xTranslation) {
+            startXTranslationAnimation(child, animationProperties);
+        } else {
+            abortAnimation(child, TAG_ANIMATOR_TRANSLATION_X);
+        }
+
+        // start translationY animation
+        if (child.getTranslationY() != this.yTranslation) {
+            startYTranslationAnimation(child, animationProperties);
+        } else {
+            abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Y);
+        }
+
+        // start translationZ animation
+        if (child.getTranslationZ() != this.zTranslation) {
+            startZTranslationAnimation(child, animationProperties);
+        } else {
+            abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Z);
+        }
+
+        // start scaleX animation
+        if (child.getScaleX() != scaleX) {
+            PropertyAnimator.startAnimation(child, SCALE_X_PROPERTY, scaleX, animationProperties);
+        } else {
+            abortAnimation(child, SCALE_X_PROPERTY.getAnimatorTag());
+        }
+
+        // start scaleX animation
+        if (child.getScaleY() != scaleY) {
+            PropertyAnimator.startAnimation(child, SCALE_Y_PROPERTY, scaleY, animationProperties);
+        } else {
+            abortAnimation(child, SCALE_Y_PROPERTY.getAnimatorTag());
+        }
+
+        // start alpha animation
+        if (alphaChanging) {
+            startAlphaAnimation(child, animationProperties);
+        }  else {
+            abortAnimation(child, TAG_ANIMATOR_ALPHA);
+        }
+    }
+
+    private void updateAlphaAnimation(View view) {
+        startAlphaAnimation(view, NO_NEW_ANIMATIONS);
+    }
+
+    private void startAlphaAnimation(final View child, AnimationProperties properties) {
+        Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
+        Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
+        final float newEndValue = this.alpha;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
+            return;
+        }
+        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA);
+        AnimationFilter filter = properties.getAnimationFilter();
+        if (!filter.animateAlpha) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                float relativeDiff = newEndValue - previousEndValue;
+                float newStartValue = previousStartValue + relativeDiff;
+                values[0].setFloatValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_ALPHA, newStartValue);
+                child.setTag(TAG_END_ALPHA, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setAlpha(newEndValue);
+                if (newEndValue == 0) {
+                    child.setVisibility(View.INVISIBLE);
+                }
+            }
+        }
+
+        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
+                child.getAlpha(), newEndValue);
+        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        // Handle layer type
+        child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        animator.addListener(new AnimatorListenerAdapter() {
+            public boolean mWasCancelled;
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setLayerType(View.LAYER_TYPE_NONE, null);
+                if (newEndValue == 0 && !mWasCancelled) {
+                    child.setVisibility(View.INVISIBLE);
+                }
+                // remove the tag when the animation is finished
+                child.setTag(TAG_ANIMATOR_ALPHA, null);
+                child.setTag(TAG_START_ALPHA, null);
+                child.setTag(TAG_END_ALPHA, null);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mWasCancelled = true;
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mWasCancelled = false;
+            }
+        });
+        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
+        animator.setDuration(newDuration);
+        if (properties.delay > 0 && (previousAnimator == null
+                || previousAnimator.getAnimatedFraction() == 0)) {
+            animator.setStartDelay(properties.delay);
+        }
+        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+        if (listener != null) {
+            animator.addListener(listener);
+        }
+
+        startAnimator(animator, listener);
+        child.setTag(TAG_ANIMATOR_ALPHA, animator);
+        child.setTag(TAG_START_ALPHA, child.getAlpha());
+        child.setTag(TAG_END_ALPHA, newEndValue);
+    }
+
+    private void updateAnimationZ(View view) {
+        startZTranslationAnimation(view, NO_NEW_ANIMATIONS);
+    }
+
+    private void updateAnimation(View view, AnimatableProperty property,
+            float endValue) {
+        PropertyAnimator.startAnimation(view, property, endValue, NO_NEW_ANIMATIONS);
+    }
+
+    private void startZTranslationAnimation(final View child, AnimationProperties properties) {
+        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
+        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
+        float newEndValue = this.zTranslation;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
+            return;
+        }
+        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z);
+        AnimationFilter filter = properties.getAnimationFilter();
+        if (!filter.animateZ) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                float relativeDiff = newEndValue - previousEndValue;
+                float newStartValue = previousStartValue + relativeDiff;
+                values[0].setFloatValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_TRANSLATION_Z, newStartValue);
+                child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setTranslationZ(newEndValue);
+            }
+        }
+
+        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
+                child.getTranslationZ(), newEndValue);
+        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
+        animator.setDuration(newDuration);
+        if (properties.delay > 0 && (previousAnimator == null
+                || previousAnimator.getAnimatedFraction() == 0)) {
+            animator.setStartDelay(properties.delay);
+        }
+        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+        if (listener != null) {
+            animator.addListener(listener);
+        }
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null);
+                child.setTag(TAG_START_TRANSLATION_Z, null);
+                child.setTag(TAG_END_TRANSLATION_Z, null);
+            }
+        });
+        startAnimator(animator, listener);
+        child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
+        child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ());
+        child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
+    }
+
+    private void updateAnimationX(View view) {
+        startXTranslationAnimation(view, NO_NEW_ANIMATIONS);
+    }
+
+    private void startXTranslationAnimation(final View child, AnimationProperties properties) {
+        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_X);
+        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_X);
+        float newEndValue = this.xTranslation;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
+            return;
+        }
+        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_X);
+        AnimationFilter filter = properties.getAnimationFilter();
+        if (!filter.animateX) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                float relativeDiff = newEndValue - previousEndValue;
+                float newStartValue = previousStartValue + relativeDiff;
+                values[0].setFloatValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_TRANSLATION_X, newStartValue);
+                child.setTag(TAG_END_TRANSLATION_X, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setTranslationX(newEndValue);
+                return;
+            }
+        }
+
+        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_X,
+                child.getTranslationX(), newEndValue);
+        Interpolator customInterpolator = properties.getCustomInterpolator(child,
+                View.TRANSLATION_X);
+        Interpolator interpolator =  customInterpolator != null ? customInterpolator
+                : Interpolators.FAST_OUT_SLOW_IN;
+        animator.setInterpolator(interpolator);
+        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
+        animator.setDuration(newDuration);
+        if (properties.delay > 0 && (previousAnimator == null
+                || previousAnimator.getAnimatedFraction() == 0)) {
+            animator.setStartDelay(properties.delay);
+        }
+        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+        if (listener != null) {
+            animator.addListener(listener);
+        }
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setTag(TAG_ANIMATOR_TRANSLATION_X, null);
+                child.setTag(TAG_START_TRANSLATION_X, null);
+                child.setTag(TAG_END_TRANSLATION_X, null);
+            }
+        });
+        startAnimator(animator, listener);
+        child.setTag(TAG_ANIMATOR_TRANSLATION_X, animator);
+        child.setTag(TAG_START_TRANSLATION_X, child.getTranslationX());
+        child.setTag(TAG_END_TRANSLATION_X, newEndValue);
+    }
+
+    private void updateAnimationY(View view) {
+        startYTranslationAnimation(view, NO_NEW_ANIMATIONS);
+    }
+
+    private void startYTranslationAnimation(final View child, AnimationProperties properties) {
+        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
+        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
+        float newEndValue = this.yTranslation;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
+            return;
+        }
+        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y);
+        AnimationFilter filter = properties.getAnimationFilter();
+        if (!filter.shouldAnimateY(child)) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                float relativeDiff = newEndValue - previousEndValue;
+                float newStartValue = previousStartValue + relativeDiff;
+                values[0].setFloatValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_TRANSLATION_Y, newStartValue);
+                child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setTranslationY(newEndValue);
+                return;
+            }
+        }
+
+        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
+                child.getTranslationY(), newEndValue);
+        Interpolator customInterpolator = properties.getCustomInterpolator(child,
+                View.TRANSLATION_Y);
+        Interpolator interpolator =  customInterpolator != null ? customInterpolator
+                : Interpolators.FAST_OUT_SLOW_IN;
+        animator.setInterpolator(interpolator);
+        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
+        animator.setDuration(newDuration);
+        if (properties.delay > 0 && (previousAnimator == null
+                || previousAnimator.getAnimatedFraction() == 0)) {
+            animator.setStartDelay(properties.delay);
+        }
+        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
+        if (listener != null) {
+            animator.addListener(listener);
+        }
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                HeadsUpUtil.setIsClickedHeadsUpNotification(child, false);
+                child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
+                child.setTag(TAG_START_TRANSLATION_Y, null);
+                child.setTag(TAG_END_TRANSLATION_Y, null);
+                onYTranslationAnimationFinished(child);
+            }
+        });
+        startAnimator(animator, listener);
+        child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
+        child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY());
+        child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
+    }
+
+    protected void onYTranslationAnimationFinished(View view) {
+        if (hidden && !gone) {
+            view.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    public static void startAnimator(Animator animator, AnimatorListenerAdapter listener) {
+        if (listener != null) {
+            // Even if there's a delay we'd want to notify it of the start immediately.
+            listener.onAnimationStart(animator);
+        }
+        animator.start();
+    }
+
+    public static <T> T getChildTag(View child, int tag) {
+        return (T) child.getTag(tag);
+    }
+
+    protected void abortAnimation(View child, int animatorTag) {
+        Animator previousAnimator = getChildTag(child, animatorTag);
+        if (previousAnimator != null) {
+            previousAnimator.cancel();
+        }
+    }
+
+    /**
+     * Cancel the previous animator and get the duration of the new animation.
+     *
+     * @param duration the new duration
+     * @param previousAnimator the animator which was running before
+     * @return the new duration
+     */
+    public static long cancelAnimatorAndGetNewDuration(long duration,
+            ValueAnimator previousAnimator) {
+        long newDuration = duration;
+        if (previousAnimator != null) {
+            // We take either the desired length of the new animation or the remaining time of
+            // the previous animator, whichever is longer.
+            newDuration = Math.max(previousAnimator.getDuration()
+                    - previousAnimator.getCurrentPlayTime(), newDuration);
+            previousAnimator.cancel();
+        }
+        return newDuration;
+    }
+
+    /**
+     * Get the end value of the xTranslation animation running on a view or the xTranslation
+     * if no animation is running.
+     */
+    public static float getFinalTranslationX(View view) {
+        if (view == null) {
+            return 0;
+        }
+        ValueAnimator xAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X);
+        if (xAnimator == null) {
+            return view.getTranslationX();
+        } else {
+            return getChildTag(view, TAG_END_TRANSLATION_X);
+        }
+    }
+
+    /**
+     * Get the end value of the yTranslation animation running on a view or the yTranslation
+     * if no animation is running.
+     */
+    public static float getFinalTranslationY(View view) {
+        if (view == null) {
+            return 0;
+        }
+        ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
+        if (yAnimator == null) {
+            return view.getTranslationY();
+        } else {
+            return getChildTag(view, TAG_END_TRANSLATION_Y);
+        }
+    }
+
+    /**
+     * Get the end value of the zTranslation animation running on a view or the zTranslation
+     * if no animation is running.
+     */
+    public static float getFinalTranslationZ(View view) {
+        if (view == null) {
+            return 0;
+        }
+        ValueAnimator zAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Z);
+        if (zAnimator == null) {
+            return view.getTranslationZ();
+        } else {
+            return getChildTag(view, TAG_END_TRANSLATION_Z);
+        }
+    }
+
+    public static boolean isAnimatingY(View child) {
+        return getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null;
+    }
+
+    public void cancelAnimations(View view) {
+        Animator animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X);
+        if (animator != null) {
+            animator.cancel();
+        }
+        animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
+        if (animator != null) {
+            animator.cancel();
+        }
+        animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Z);
+        if (animator != null) {
+            animator.cancel();
+        }
+        animator = getChildTag(view, TAG_ANIMATOR_ALPHA);
+        if (animator != null) {
+            animator.cancel();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
new file mode 100644
index 0000000..2087a16
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -0,0 +1,448 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.hardware.biometrics.BiometricSourceType;
+import android.content.Context;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.Log;
+
+import com.android.keyguard.KeyguardConstants;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.internal.util.LatencyTracker;
+import com.android.systemui.Dependency;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+
+import java.io.PrintWriter;
+
+/**
+ * Controller which coordinates all the biometric unlocking actions with the UI.
+ */
+public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
+
+    private static final String TAG = "BiometricUnlockController";
+    private static final boolean DEBUG_BIO_WAKELOCK = KeyguardConstants.DEBUG_BIOMETRIC_WAKELOCK;
+    private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
+    private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
+
+    /**
+     * Mode in which we don't need to wake up the device when we get a fingerprint.
+     */
+    public static final int MODE_NONE = 0;
+
+    /**
+     * Mode in which we wake up the device, and directly dismiss Keyguard. Active when we acquire
+     * a fingerprint while the screen is off and the device was sleeping.
+     */
+    public static final int MODE_WAKE_AND_UNLOCK = 1;
+
+    /**
+     * Mode in which we wake the device up, and fade out the Keyguard contents because they were
+     * already visible while pulsing in doze mode.
+     */
+    public static final int MODE_WAKE_AND_UNLOCK_PULSING = 2;
+
+    /**
+     * Mode in which we wake up the device, but play the normal dismiss animation. Active when we
+     * acquire a fingerprint pulsing in doze mode.
+     */
+    public static final int MODE_SHOW_BOUNCER = 3;
+
+    /**
+     * Mode in which we only wake up the device, and keyguard was not showing when we acquired a
+     * fingerprint.
+     * */
+    public static final int MODE_ONLY_WAKE = 4;
+
+    /**
+     * Mode in which fingerprint unlocks the device.
+     */
+    public static final int MODE_UNLOCK = 5;
+
+    /**
+     * Mode in which fingerprint brings up the bouncer because fingerprint unlocking is currently
+     * not allowed.
+     */
+    public static final int MODE_DISMISS_BOUNCER = 6;
+
+    /**
+     * Mode in which fingerprint wakes and unlocks the device from a dream.
+     */
+    public static final int MODE_WAKE_AND_UNLOCK_FROM_DREAM = 7;
+
+    /**
+     * How much faster we collapse the lockscreen when authenticating with biometric.
+     */
+    private static final float BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
+
+    private PowerManager mPowerManager;
+    private Handler mHandler = new Handler();
+    private PowerManager.WakeLock mWakeLock;
+    private KeyguardUpdateMonitor mUpdateMonitor;
+    private int mMode;
+    private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    private StatusBarWindowManager mStatusBarWindowManager;
+    private DozeScrimController mDozeScrimController;
+    private KeyguardViewMediator mKeyguardViewMediator;
+    private ScrimController mScrimController;
+    private StatusBar mStatusBar;
+    private final UnlockMethodCache mUnlockMethodCache;
+    private final Context mContext;
+    private int mPendingAuthenticatedUserId = -1;
+    private BiometricSourceType mPendingAuthenticatedBioSourceType = null;
+    private boolean mPendingShowBouncer;
+    private boolean mHasScreenTurnedOnSinceAuthenticating;
+
+    public BiometricUnlockController(Context context,
+                                     DozeScrimController dozeScrimController,
+                                     KeyguardViewMediator keyguardViewMediator,
+                                     ScrimController scrimController,
+                                     StatusBar statusBar,
+                                     UnlockMethodCache unlockMethodCache) {
+        mContext = context;
+        mPowerManager = context.getSystemService(PowerManager.class);
+        mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
+        mUpdateMonitor.registerCallback(this);
+        Dependency.get(WakefulnessLifecycle.class).addObserver(mWakefulnessObserver);
+        Dependency.get(ScreenLifecycle.class).addObserver(mScreenObserver);
+        mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
+        mDozeScrimController = dozeScrimController;
+        mKeyguardViewMediator = keyguardViewMediator;
+        mScrimController = scrimController;
+        mStatusBar = statusBar;
+        mUnlockMethodCache = unlockMethodCache;
+    }
+
+    public void setStatusBarKeyguardViewManager(
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+    }
+
+    private final Runnable mReleaseBiometricWakeLockRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (DEBUG_BIO_WAKELOCK) {
+                Log.i(TAG, "biometric wakelock: TIMEOUT!!");
+            }
+            releaseBiometricWakeLock();
+        }
+    };
+
+    private void releaseBiometricWakeLock() {
+        if (mWakeLock != null) {
+            mHandler.removeCallbacks(mReleaseBiometricWakeLockRunnable);
+            if (DEBUG_BIO_WAKELOCK) {
+                Log.i(TAG, "releasing biometric wakelock");
+            }
+            mWakeLock.release();
+            mWakeLock = null;
+        }
+    }
+
+    @Override
+    public void onBiometricAcquired(BiometricSourceType biometricSourceType) {
+        Trace.beginSection("BiometricUnlockController#onBiometricAcquired");
+        releaseBiometricWakeLock();
+        if (!mUpdateMonitor.isDeviceInteractive()) {
+            if (LatencyTracker.isEnabled(mContext)) {
+                int action = LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
+                if (biometricSourceType == BiometricSourceType.FACE) {
+                    action = LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK;
+                }
+                LatencyTracker.getInstance(mContext).onActionStart(action);
+            }
+            mWakeLock = mPowerManager.newWakeLock(
+                    PowerManager.PARTIAL_WAKE_LOCK, BIOMETRIC_WAKE_LOCK_NAME);
+            Trace.beginSection("acquiring wake-and-unlock");
+            mWakeLock.acquire();
+            Trace.endSection();
+            if (DEBUG_BIO_WAKELOCK) {
+                Log.i(TAG, "biometric acquired, grabbing biometric wakelock");
+            }
+            mHandler.postDelayed(mReleaseBiometricWakeLockRunnable,
+                    BIOMETRIC_WAKELOCK_TIMEOUT_MS);
+        }
+        Trace.endSection();
+    }
+
+    private boolean pulsingOrAod() {
+        final ScrimState scrimState = mScrimController.getState();
+        return scrimState == ScrimState.AOD
+                || scrimState == ScrimState.PULSING;
+    }
+
+    @Override
+    public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+        Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated");
+        if (mUpdateMonitor.isGoingToSleep()) {
+            mPendingAuthenticatedUserId = userId;
+            mPendingAuthenticatedBioSourceType = biometricSourceType;
+            Trace.endSection();
+            return;
+        }
+        startWakeAndUnlock(calculateMode());
+    }
+
+    public void startWakeAndUnlock(int mode) {
+        // TODO(b/62444020): remove when this bug is fixed
+        Log.v(TAG, "startWakeAndUnlock(" + mode + ")");
+        boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
+        mMode = mode;
+        mHasScreenTurnedOnSinceAuthenticating = false;
+        if (mMode == MODE_WAKE_AND_UNLOCK_PULSING && pulsingOrAod()) {
+            // If we are waking the device up while we are pulsing the clock and the
+            // notifications would light up first, creating an unpleasant animation.
+            // Defer changing the screen brightness by forcing doze brightness on our window
+            // until the clock and the notifications are faded out.
+            mStatusBarWindowManager.setForceDozeBrightness(true);
+        }
+        // During wake and unlock, we need to draw black before waking up to avoid abrupt
+        // brightness changes due to display state transitions.
+        boolean alwaysOnEnabled = DozeParameters.getInstance(mContext).getAlwaysOn();
+        boolean delayWakeUp = mode == MODE_WAKE_AND_UNLOCK && alwaysOnEnabled;
+        Runnable wakeUp = ()-> {
+            if (!wasDeviceInteractive) {
+                if (DEBUG_BIO_WAKELOCK) {
+                    Log.i(TAG, "bio wakelock: Authenticated, waking up...");
+                }
+                mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:BIOMETRIC");
+            }
+            if (delayWakeUp) {
+                mKeyguardViewMediator.onWakeAndUnlocking();
+            }
+            Trace.beginSection("release wake-and-unlock");
+            releaseBiometricWakeLock();
+            Trace.endSection();
+        };
+
+        if (!delayWakeUp) {
+            wakeUp.run();
+        }
+        switch (mMode) {
+            case MODE_DISMISS_BOUNCER:
+                Trace.beginSection("MODE_DISMISS");
+                mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated(
+                        false /* strongAuth */);
+                Trace.endSection();
+                break;
+            case MODE_UNLOCK:
+            case MODE_SHOW_BOUNCER:
+                Trace.beginSection("MODE_UNLOCK or MODE_SHOW_BOUNCER");
+                if (!wasDeviceInteractive) {
+                    mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
+                    mPendingShowBouncer = true;
+                } else {
+                    showBouncer();
+                }
+                Trace.endSection();
+                break;
+            case MODE_WAKE_AND_UNLOCK_FROM_DREAM:
+            case MODE_WAKE_AND_UNLOCK_PULSING:
+            case MODE_WAKE_AND_UNLOCK:
+                if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) {
+                    Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
+                    mStatusBar.updateMediaMetaData(false /* metaDataChanged */,
+                            true /* allowEnterAnimation */);
+                } else if (mMode == MODE_WAKE_AND_UNLOCK){
+                    Trace.beginSection("MODE_WAKE_AND_UNLOCK");
+                } else {
+                    Trace.beginSection("MODE_WAKE_AND_UNLOCK_FROM_DREAM");
+                    mUpdateMonitor.awakenFromDream();
+                }
+                mStatusBarWindowManager.setStatusBarFocusable(false);
+                if (delayWakeUp) {
+                    mHandler.postDelayed(wakeUp, 50);
+                } else {
+                    mKeyguardViewMediator.onWakeAndUnlocking();
+                }
+                if (mStatusBar.getNavigationBarView() != null) {
+                    mStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
+                }
+                Trace.endSection();
+                break;
+            case MODE_ONLY_WAKE:
+            case MODE_NONE:
+                break;
+        }
+        mStatusBar.notifyBiometricAuthModeChanged();
+        Trace.endSection();
+    }
+
+    private void showBouncer() {
+        if (calculateMode() == MODE_SHOW_BOUNCER) {
+            mStatusBarKeyguardViewManager.showBouncer(false);
+        }
+        mStatusBarKeyguardViewManager.animateCollapsePanels(
+                BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR);
+        mPendingShowBouncer = false;
+    }
+
+    @Override
+    public void onStartedGoingToSleep(int why) {
+        resetMode();
+        mPendingAuthenticatedUserId = -1;
+        mPendingAuthenticatedBioSourceType = null;
+    }
+
+    @Override
+    public void onFinishedGoingToSleep(int why) {
+        Trace.beginSection("BiometricUnlockController#onFinishedGoingToSleep");
+        if (mPendingAuthenticatedUserId != -1) {
+
+            // Post this to make sure it's executed after the device is fully locked.
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    onBiometricAuthenticated(mPendingAuthenticatedUserId,
+                            mPendingAuthenticatedBioSourceType);
+                }
+            });
+        }
+        mPendingAuthenticatedUserId = -1;
+        mPendingAuthenticatedBioSourceType = null;
+        Trace.endSection();
+    }
+
+    public boolean hasPendingAuthentication() {
+        return mPendingAuthenticatedUserId != -1
+                && mUpdateMonitor.isUnlockingWithBiometricAllowed()
+                && mPendingAuthenticatedUserId == KeyguardUpdateMonitor.getCurrentUser();
+    }
+
+    public int getMode() {
+        return mMode;
+    }
+
+    private int calculateMode() {
+        boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed();
+        boolean deviceDreaming = mUpdateMonitor.isDreaming();
+
+        if (!mUpdateMonitor.isDeviceInteractive()) {
+            if (!mStatusBarKeyguardViewManager.isShowing()) {
+                return MODE_ONLY_WAKE;
+            } else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
+                return MODE_WAKE_AND_UNLOCK_PULSING;
+            } else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) {
+                return MODE_WAKE_AND_UNLOCK;
+            } else {
+                return MODE_SHOW_BOUNCER;
+            }
+        }
+        if (unlockingAllowed && deviceDreaming) {
+            return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
+        }
+        if (mStatusBarKeyguardViewManager.isShowing()) {
+            if (mStatusBarKeyguardViewManager.isBouncerShowing() && unlockingAllowed) {
+                return MODE_DISMISS_BOUNCER;
+            } else if (unlockingAllowed) {
+                return MODE_UNLOCK;
+            } else if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
+                return MODE_SHOW_BOUNCER;
+            }
+        }
+        return MODE_NONE;
+    }
+
+    @Override
+    public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
+        cleanup();
+    }
+
+    @Override
+    public void onBiometricError(int msgId, String errString,
+            BiometricSourceType biometricSourceType) {
+        cleanup();
+    }
+
+    private void cleanup() {
+        releaseBiometricWakeLock();
+    }
+
+    public void startKeyguardFadingAway() {
+
+        // Disable brightness override when the ambient contents are fully invisible.
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                mStatusBarWindowManager.setForceDozeBrightness(false);
+            }
+        }, StatusBar.FADE_KEYGUARD_DURATION_PULSING);
+    }
+
+    public void finishKeyguardFadingAway() {
+        resetMode();
+    }
+
+    private void resetMode() {
+        mMode = MODE_NONE;
+        mStatusBarWindowManager.setForceDozeBrightness(false);
+        if (mStatusBar.getNavigationBarView() != null) {
+            mStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
+        }
+        mStatusBar.notifyBiometricAuthModeChanged();
+    }
+
+    private final WakefulnessLifecycle.Observer mWakefulnessObserver =
+            new WakefulnessLifecycle.Observer() {
+        @Override
+        public void onFinishedWakingUp() {
+            if (mPendingShowBouncer) {
+                BiometricUnlockController.this.showBouncer();
+            }
+        }
+    };
+
+    private final ScreenLifecycle.Observer mScreenObserver =
+            new ScreenLifecycle.Observer() {
+                @Override
+                public void onScreenTurnedOn() {
+                    mHasScreenTurnedOnSinceAuthenticating = true;
+                }
+            };
+
+    public boolean hasScreenTurnedOnSinceAuthenticating() {
+        return mHasScreenTurnedOnSinceAuthenticating;
+    }
+
+    public void dump(PrintWriter pw) {
+        pw.println(" BiometricUnlockController:");
+        pw.print("   mMode="); pw.println(mMode);
+        pw.print("   mWakeLock="); pw.println(mWakeLock);
+    }
+
+    /**
+     * Successful authentication with fingerprint that wakes up the device.
+     */
+    public boolean isWakeAndUnlock() {
+        return mMode == MODE_WAKE_AND_UNLOCK
+                || mMode == MODE_WAKE_AND_UNLOCK_PULSING
+                || mMode == MODE_WAKE_AND_UNLOCK_FROM_DREAM;
+    }
+
+    /**
+     * Successful authentication with fingerprint when the screen was either on or off.
+     */
+    public boolean isBiometricUnlock() {
+        return isWakeAndUnlock() || mMode == MODE_UNLOCK;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 894ea62..f48c3f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -134,6 +134,9 @@
                 ((ButtonInterface) mViews.get(i)).setImageDrawable(mImageDrawable);
             }
         }
+        if (mImageDrawable != null) {
+            mImageDrawable.setCallback(mCurrentView);
+        }
     }
 
     public void setVisibility(int visibility) {
@@ -266,6 +269,9 @@
 
     public void setCurrentView(View currentView) {
         mCurrentView = currentView.findViewById(mId);
+        if (mImageDrawable != null) {
+            mImageDrawable.setCallback(mCurrentView);
+        }
     }
 
     public void setVertical(boolean vertical) {
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 f7b7eeb..a781be6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -22,6 +22,8 @@
 import android.app.Fragment;
 import android.app.StatusBarManager;
 import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -89,7 +91,8 @@
         super.onViewCreated(view, savedInstanceState);
         mStatusBar = (PhoneStatusBarView) view;
         if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
-            mStatusBar.go(savedInstanceState.getInt(EXTRA_PANEL_STATE));
+            mStatusBar.restoreHierarchyState(
+                    savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
         }
         mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));
         mDarkIconManager.setShouldLog(true);
@@ -105,7 +108,9 @@
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        outState.putInt(EXTRA_PANEL_STATE, mStatusBar.getState());
+        SparseArray<Parcelable> states = new SparseArray<>();
+        mStatusBar.saveHierarchyState(states);
+        outState.putSparseParcelableArray(EXTRA_PANEL_STATE, states);
     }
 
     @Override
@@ -184,6 +189,14 @@
             state |= DISABLE_SYSTEM_INFO;
             state |= DISABLE_CLOCK;
         }
+
+        // In landscape, the heads up show but shouldHideNotificationIcons() return false
+        // because the visual icon is in notification icon area rather than heads up's space.
+        // whether the notification icon show or not, clock should hide when heads up show.
+        if (mStatusBarComponent.isHeadsUpShouldBeVisible()) {
+            state |= DISABLE_CLOCK;
+        }
+
         if (mNetworkController != null && EncryptionHelper.IS_DATA_ENCRYPTED) {
             if (mNetworkController.hasEmergencyCryptKeeperText()) {
                 state |= DISABLE_NOTIFICATION_ICONS;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.java
index 6f53844..7ddca17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.java
@@ -64,8 +64,9 @@
         final float fontScale = newConfig.fontScale;
         final int density = newConfig.densityDpi;
         int uiMode = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+        boolean uiModeChanged = uiMode != mUiMode;
         if (density != mDensity || fontScale != mFontScale
-                || (mInCarMode && uiMode != mUiMode)) {
+                || (mInCarMode && uiModeChanged)) {
             listeners.forEach(l -> {
                 if (mListeners.contains(l)) {
                     l.onDensityOrFontScaleChanged();
@@ -73,7 +74,6 @@
             });
             mDensity = density;
             mFontScale = fontScale;
-            mUiMode = uiMode;
         }
 
         final LocaleList localeList = newConfig.getLocales();
@@ -86,6 +86,15 @@
             });
         }
 
+        if (uiModeChanged) {
+            mUiMode = uiMode;
+            listeners.forEach(l -> {
+                if (mListeners.contains(l)) {
+                    l.onUiModeChanged();
+                }
+            });
+        }
+
         if ((mLastConfig.updateFrom(newConfig) & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
                 listeners.forEach(l -> {
                     if (mListeners.contains(l)) {
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 e0e991b..e052e53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -203,7 +203,7 @@
         v.set(icon);
         v.setStaticDrawableColor(mColor);
         v.setDecorColor(mColor);
-        addView(v, 0, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize));
+        addView(v, 0, createLayoutParams());
     }
 
     public void addDemoWifiView(WifiIconState state) {
@@ -223,7 +223,7 @@
         mWifiView = view;
         mWifiView.applyWifiState(state);
         mWifiView.setStaticDrawableColor(mColor);
-        addView(view, viewIndex);
+        addView(view, viewIndex, createLayoutParams());
     }
 
     public void updateWifiState(WifiIconState state) {
@@ -244,7 +244,7 @@
 
         // mobile always goes at the end
         mMobileViews.add(view);
-        addView(view, getChildCount());
+        addView(view, getChildCount(), createLayoutParams());
     }
 
     public void updateMobileState(MobileIconState state) {
@@ -290,6 +290,10 @@
         return null;
     }
 
+    private LayoutParams createLayoutParams() {
+        return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
+    }
+
     @Override
     public void onDarkChanged(Rect area, float darkIntensity, int tint) {
         setColor(DarkIconDispatcher.getTint(area, mStatusIcons, tint));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
deleted file mode 100644
index f0b1a82..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.util.Log;
-
-import com.android.keyguard.KeyguardConstants;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.internal.util.LatencyTracker;
-import com.android.systemui.Dependency;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-
-import java.io.PrintWriter;
-
-/**
- * Controller which coordinates all the fingerprint unlocking actions with the UI.
- */
-public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback {
-
-    private static final String TAG = "FingerprintController";
-    private static final boolean DEBUG_FP_WAKELOCK = KeyguardConstants.DEBUG_FP_WAKELOCK;
-    private static final long FINGERPRINT_WAKELOCK_TIMEOUT_MS = 15 * 1000;
-    private static final String FINGERPRINT_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
-
-    /**
-     * Mode in which we don't need to wake up the device when we get a fingerprint.
-     */
-    public static final int MODE_NONE = 0;
-
-    /**
-     * Mode in which we wake up the device, and directly dismiss Keyguard. Active when we acquire
-     * a fingerprint while the screen is off and the device was sleeping.
-     */
-    public static final int MODE_WAKE_AND_UNLOCK = 1;
-
-    /**
-     * Mode in which we wake the device up, and fade out the Keyguard contents because they were
-     * already visible while pulsing in doze mode.
-     */
-    public static final int MODE_WAKE_AND_UNLOCK_PULSING = 2;
-
-    /**
-     * Mode in which we wake up the device, but play the normal dismiss animation. Active when we
-     * acquire a fingerprint pulsing in doze mode.
-     */
-    public static final int MODE_SHOW_BOUNCER = 3;
-
-    /**
-     * Mode in which we only wake up the device, and keyguard was not showing when we acquired a
-     * fingerprint.
-     * */
-    public static final int MODE_ONLY_WAKE = 4;
-
-    /**
-     * Mode in which fingerprint unlocks the device.
-     */
-    public static final int MODE_UNLOCK = 5;
-
-    /**
-     * Mode in which fingerprint brings up the bouncer because fingerprint unlocking is currently
-     * not allowed.
-     */
-    public static final int MODE_DISMISS_BOUNCER = 6;
-
-    /**
-     * Mode in which fingerprint wakes and unlocks the device from a dream.
-     */
-    public static final int MODE_WAKE_AND_UNLOCK_FROM_DREAM = 7;
-
-    /**
-     * How much faster we collapse the lockscreen when authenticating with fingerprint.
-     */
-    private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
-
-    private PowerManager mPowerManager;
-    private Handler mHandler = new Handler();
-    private PowerManager.WakeLock mWakeLock;
-    private KeyguardUpdateMonitor mUpdateMonitor;
-    private int mMode;
-    private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    private StatusBarWindowManager mStatusBarWindowManager;
-    private DozeScrimController mDozeScrimController;
-    private KeyguardViewMediator mKeyguardViewMediator;
-    private ScrimController mScrimController;
-    private StatusBar mStatusBar;
-    private final UnlockMethodCache mUnlockMethodCache;
-    private final Context mContext;
-    private int mPendingAuthenticatedUserId = -1;
-    private boolean mPendingShowBouncer;
-    private boolean mHasScreenTurnedOnSinceAuthenticating;
-
-    public FingerprintUnlockController(Context context,
-            DozeScrimController dozeScrimController,
-            KeyguardViewMediator keyguardViewMediator,
-            ScrimController scrimController,
-            StatusBar statusBar,
-            UnlockMethodCache unlockMethodCache) {
-        mContext = context;
-        mPowerManager = context.getSystemService(PowerManager.class);
-        mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
-        mUpdateMonitor.registerCallback(this);
-        Dependency.get(WakefulnessLifecycle.class).addObserver(mWakefulnessObserver);
-        Dependency.get(ScreenLifecycle.class).addObserver(mScreenObserver);
-        mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
-        mDozeScrimController = dozeScrimController;
-        mKeyguardViewMediator = keyguardViewMediator;
-        mScrimController = scrimController;
-        mStatusBar = statusBar;
-        mUnlockMethodCache = unlockMethodCache;
-    }
-
-    public void setStatusBarKeyguardViewManager(
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
-        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
-    }
-
-    private final Runnable mReleaseFingerprintWakeLockRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if (DEBUG_FP_WAKELOCK) {
-                Log.i(TAG, "fp wakelock: TIMEOUT!!");
-            }
-            releaseFingerprintWakeLock();
-        }
-    };
-
-    private void releaseFingerprintWakeLock() {
-        if (mWakeLock != null) {
-            mHandler.removeCallbacks(mReleaseFingerprintWakeLockRunnable);
-            if (DEBUG_FP_WAKELOCK) {
-                Log.i(TAG, "releasing fp wakelock");
-            }
-            mWakeLock.release();
-            mWakeLock = null;
-        }
-    }
-
-    @Override
-    public void onFingerprintAcquired() {
-        Trace.beginSection("FingerprintUnlockController#onFingerprintAcquired");
-        releaseFingerprintWakeLock();
-        if (!mUpdateMonitor.isDeviceInteractive()) {
-            if (LatencyTracker.isEnabled(mContext)) {
-                LatencyTracker.getInstance(mContext).onActionStart(
-                        LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK);
-            }
-            mWakeLock = mPowerManager.newWakeLock(
-                    PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
-            Trace.beginSection("acquiring wake-and-unlock");
-            mWakeLock.acquire();
-            Trace.endSection();
-            if (DEBUG_FP_WAKELOCK) {
-                Log.i(TAG, "fingerprint acquired, grabbing fp wakelock");
-            }
-            mHandler.postDelayed(mReleaseFingerprintWakeLockRunnable,
-                    FINGERPRINT_WAKELOCK_TIMEOUT_MS);
-        }
-        Trace.endSection();
-    }
-
-    private boolean pulsingOrAod() {
-        final ScrimState scrimState = mScrimController.getState();
-        return scrimState == ScrimState.AOD
-                || scrimState == ScrimState.PULSING;
-    }
-
-    @Override
-    public void onFingerprintAuthenticated(int userId) {
-        Trace.beginSection("FingerprintUnlockController#onFingerprintAuthenticated");
-        if (mUpdateMonitor.isGoingToSleep()) {
-            mPendingAuthenticatedUserId = userId;
-            Trace.endSection();
-            return;
-        }
-        startWakeAndUnlock(calculateMode());
-    }
-
-    public void startWakeAndUnlock(int mode) {
-        // TODO(b/62444020): remove when this bug is fixed
-        Log.v(TAG, "startWakeAndUnlock(" + mode + ")");
-        boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
-        mMode = mode;
-        mHasScreenTurnedOnSinceAuthenticating = false;
-        if (mMode == MODE_WAKE_AND_UNLOCK_PULSING && pulsingOrAod()) {
-            // If we are waking the device up while we are pulsing the clock and the
-            // notifications would light up first, creating an unpleasant animation.
-            // Defer changing the screen brightness by forcing doze brightness on our window
-            // until the clock and the notifications are faded out.
-            mStatusBarWindowManager.setForceDozeBrightness(true);
-        }
-        if (!wasDeviceInteractive) {
-            if (DEBUG_FP_WAKELOCK) {
-                Log.i(TAG, "fp wakelock: Authenticated, waking up...");
-            }
-            mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:FINGERPRINT");
-        }
-        Trace.beginSection("release wake-and-unlock");
-        releaseFingerprintWakeLock();
-        Trace.endSection();
-        switch (mMode) {
-            case MODE_DISMISS_BOUNCER:
-                Trace.beginSection("MODE_DISMISS");
-                mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated(
-                        false /* strongAuth */);
-                Trace.endSection();
-                break;
-            case MODE_UNLOCK:
-            case MODE_SHOW_BOUNCER:
-                Trace.beginSection("MODE_UNLOCK or MODE_SHOW_BOUNCER");
-                if (!wasDeviceInteractive) {
-                    mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
-                    mPendingShowBouncer = true;
-                } else {
-                    showBouncer();
-                }
-                Trace.endSection();
-                break;
-            case MODE_WAKE_AND_UNLOCK_FROM_DREAM:
-            case MODE_WAKE_AND_UNLOCK_PULSING:
-            case MODE_WAKE_AND_UNLOCK:
-                if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) {
-                    Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
-                    mStatusBar.updateMediaMetaData(false /* metaDataChanged */,
-                            true /* allowEnterAnimation */);
-                } else if (mMode == MODE_WAKE_AND_UNLOCK){
-                    Trace.beginSection("MODE_WAKE_AND_UNLOCK");
-                } else {
-                    Trace.beginSection("MODE_WAKE_AND_UNLOCK_FROM_DREAM");
-                    mUpdateMonitor.awakenFromDream();
-                }
-                mStatusBarWindowManager.setStatusBarFocusable(false);
-                mKeyguardViewMediator.onWakeAndUnlocking();
-                if (mStatusBar.getNavigationBarView() != null) {
-                    mStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
-                }
-                Trace.endSection();
-                break;
-            case MODE_ONLY_WAKE:
-            case MODE_NONE:
-                break;
-        }
-        mStatusBar.notifyFpAuthModeChanged();
-        Trace.endSection();
-    }
-
-    private void showBouncer() {
-        if (calculateMode() == MODE_SHOW_BOUNCER) {
-            mStatusBarKeyguardViewManager.showBouncer(false);
-        }
-        mStatusBarKeyguardViewManager.animateCollapsePanels(
-                FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
-        mPendingShowBouncer = false;
-    }
-
-    @Override
-    public void onStartedGoingToSleep(int why) {
-        resetMode();
-        mPendingAuthenticatedUserId = -1;
-    }
-
-    @Override
-    public void onFinishedGoingToSleep(int why) {
-        Trace.beginSection("FingerprintUnlockController#onFinishedGoingToSleep");
-        if (mPendingAuthenticatedUserId != -1) {
-
-            // Post this to make sure it's executed after the device is fully locked.
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    onFingerprintAuthenticated(mPendingAuthenticatedUserId);
-                }
-            });
-        }
-        mPendingAuthenticatedUserId = -1;
-        Trace.endSection();
-    }
-
-    public boolean hasPendingAuthentication() {
-        return mPendingAuthenticatedUserId != -1
-                && mUpdateMonitor.isUnlockingWithFingerprintAllowed()
-                && mPendingAuthenticatedUserId == KeyguardUpdateMonitor.getCurrentUser();
-    }
-
-    public int getMode() {
-        return mMode;
-    }
-
-    private int calculateMode() {
-        boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithFingerprintAllowed();
-        boolean deviceDreaming = mUpdateMonitor.isDreaming();
-
-        if (!mUpdateMonitor.isDeviceInteractive()) {
-            if (!mStatusBarKeyguardViewManager.isShowing()) {
-                return MODE_ONLY_WAKE;
-            } else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
-                return MODE_WAKE_AND_UNLOCK_PULSING;
-            } else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) {
-                return MODE_WAKE_AND_UNLOCK;
-            } else {
-                return MODE_SHOW_BOUNCER;
-            }
-        }
-        if (unlockingAllowed && deviceDreaming) {
-            return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
-        }
-        if (mStatusBarKeyguardViewManager.isShowing()) {
-            if (mStatusBarKeyguardViewManager.isBouncerShowing() && unlockingAllowed) {
-                return MODE_DISMISS_BOUNCER;
-            } else if (unlockingAllowed) {
-                return MODE_UNLOCK;
-            } else if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
-                return MODE_SHOW_BOUNCER;
-            }
-        }
-        return MODE_NONE;
-    }
-
-    @Override
-    public void onFingerprintAuthFailed() {
-        cleanup();
-    }
-
-    @Override
-    public void onFingerprintError(int msgId, String errString) {
-        cleanup();
-    }
-
-    private void cleanup() {
-        releaseFingerprintWakeLock();
-    }
-
-    public void startKeyguardFadingAway() {
-
-        // Disable brightness override when the ambient contents are fully invisible.
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                mStatusBarWindowManager.setForceDozeBrightness(false);
-            }
-        }, StatusBar.FADE_KEYGUARD_DURATION_PULSING);
-    }
-
-    public void finishKeyguardFadingAway() {
-        resetMode();
-    }
-
-    private void resetMode() {
-        mMode = MODE_NONE;
-        mStatusBarWindowManager.setForceDozeBrightness(false);
-        if (mStatusBar.getNavigationBarView() != null) {
-            mStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
-        }
-        mStatusBar.notifyFpAuthModeChanged();
-    }
-
-    private final WakefulnessLifecycle.Observer mWakefulnessObserver =
-            new WakefulnessLifecycle.Observer() {
-        @Override
-        public void onFinishedWakingUp() {
-            if (mPendingShowBouncer) {
-                FingerprintUnlockController.this.showBouncer();
-            }
-        }
-    };
-
-    private final ScreenLifecycle.Observer mScreenObserver =
-            new ScreenLifecycle.Observer() {
-                @Override
-                public void onScreenTurnedOn() {
-                    mHasScreenTurnedOnSinceAuthenticating = true;
-                }
-            };
-
-    public boolean hasScreenTurnedOnSinceAuthenticating() {
-        return mHasScreenTurnedOnSinceAuthenticating;
-    }
-
-    public void dump(PrintWriter pw) {
-        pw.println(" FingerprintUnlockController:");
-        pw.print("   mMode="); pw.println(mMode);
-        pw.print("   mWakeLock="); pw.println(mWakeLock);
-    }
-
-    /**
-     * Successful authentication with fingerprint that wakes up the device.
-     */
-    public boolean isWakeAndUnlock() {
-        return mMode == MODE_WAKE_AND_UNLOCK
-                || mMode == MODE_WAKE_AND_UNLOCK_PULSING
-                || mMode == MODE_WAKE_AND_UNLOCK_FROM_DREAM;
-    }
-
-    /**
-     * Successful authentication with fingerprint when the screen was either on or off.
-     */
-    public boolean isFingerprintUnlock() {
-        return isWakeAndUnlock() || mMode == MODE_UNLOCK;
-    }
-}
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 409a783..9acaf21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.view.DisplayCutout;
 import android.view.View;
 import android.view.WindowInsets;
 
@@ -25,12 +26,12 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
@@ -53,9 +54,12 @@
             mSetTrackingHeadsUp = this::setTrackingHeadsUp;
     private final Runnable mUpdatePanelTranslation = this::updatePanelTranslation;
     private final BiConsumer<Float, Float> mSetExpandedHeight = this::setExpandedHeight;
-    private float mExpandedHeight;
-    private boolean mIsExpanded;
-    private float mExpandFraction;
+    @VisibleForTesting
+    float mExpandedHeight;
+    @VisibleForTesting
+    boolean mIsExpanded;
+    @VisibleForTesting
+    float mExpandFraction;
     private ExpandableNotificationRow mTrackedChild;
     private boolean mShown;
     private final View.OnLayoutChangeListener mStackScrollLayoutChangeListener =
@@ -99,6 +103,20 @@
         mClockView = clockView;
         mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
         mDarkIconDispatcher.addDarkReceiver(this);
+
+        mHeadsUpStatusBarView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                if (shouldBeVisible()) {
+                    updateTopEntry();
+
+                    // trigger scroller to notify the latest panel translation
+                    mStackScroller.requestLayout();
+                }
+                mHeadsUpStatusBarView.removeOnLayoutChangeListener(this);
+            }
+        });
     }
 
 
@@ -159,8 +177,15 @@
         }
 
         WindowInsets windowInset = mStackScroller.getRootWindowInsets();
-        return windowInset.getSystemWindowInsetLeft() + mStackScroller.getRight()
-                + windowInset.getSystemWindowInsetRight() - realDisplaySize;
+        DisplayCutout cutout = (windowInset != null) ? windowInset.getDisplayCutout() : null;
+        int sysWinLeft = (windowInset != null) ? windowInset.getStableInsetLeft() : 0;
+        int sysWinRight = (windowInset != null) ? windowInset.getStableInsetRight() : 0;
+        int cutoutLeft = (cutout != null) ? cutout.getSafeInsetLeft() : 0;
+        int cutoutRight = (cutout != null) ? cutout.getSafeInsetRight() : 0;
+        int leftInset = Math.max(sysWinLeft, cutoutLeft);
+        int rightInset = Math.max(sysWinRight, cutoutRight);
+
+        return leftInset + mStackScroller.getRight() + rightInset - realDisplaySize;
     }
 
     public void updatePanelTranslation() {
@@ -293,4 +318,13 @@
         mHeadsUpStatusBarView.setPublicMode(publicMode);
         updateTopEntry();
     }
+
+    void readFrom(HeadsUpAppearanceController oldController) {
+        if (oldController != null) {
+            mTrackedChild = oldController.mTrackedChild;
+            mExpandedHeight = oldController.mExpandedHeight;
+            mIsExpanded = oldController.mIsExpanded;
+            mExpandFraction = oldController.mExpandFraction;
+        }
+    }
 }
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 a4bf6ea..89107bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -22,16 +22,21 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import androidx.collection.ArraySet;
+import android.graphics.Region;
+import android.graphics.Region.Op;
 import android.util.Log;
 import android.util.Pools;
+import android.view.DisplayCutout;
+import android.view.Gravity;
 import android.view.View;
 import android.view.ViewTreeObserver;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.ScreenDecorations;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -60,6 +65,7 @@
 
     private int mStatusBarHeight;
     private int mHeadsUpInset;
+    private int mDisplayCutoutTouchableRegionSize;
     private boolean mTrackingHeadsUp;
     private HashSet<String> mSwipedOutKeys = new HashSet<>();
     private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
@@ -120,6 +126,8 @@
                 com.android.internal.R.dimen.status_bar_height);
         mHeadsUpInset = mStatusBarHeight + resources.getDimensionPixelSize(
                 R.dimen.heads_up_status_bar_padding);
+        mDisplayCutoutTouchableRegionSize = resources.getDimensionPixelSize(
+                R.dimen.display_cutout_touchable_region_size);
     }
 
     @Override
@@ -128,6 +136,11 @@
         initResources();
     }
 
+    @Override
+    public void onOverlayChanged() {
+        initResources();
+    }
+
     ///////////////////////////////////////////////////////////////////////////////////////////////
     //  Public methods:
 
@@ -301,12 +314,32 @@
 
             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
             info.touchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
-        } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
-            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-            info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
+        } else {
+            setCollapsedTouchableInsets(info);
         }
     }
 
+    private void setCollapsedTouchableInsets(ViewTreeObserver.InternalInsetsInfo info) {
+        info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+        info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
+        updateRegionForNotch(info.touchableRegion);
+    }
+
+    private void updateRegionForNotch(Region region) {
+        DisplayCutout cutout = mStatusBarWindowView.getRootWindowInsets().getDisplayCutout();
+        if (cutout == null) {
+            return;
+        }
+
+        // Expand touchable region such that we also catch touches that just start below the notch
+        // area.
+        Region bounds = ScreenDecorations.DisplayCutoutView.boundsFromDirection(
+                cutout, Gravity.TOP);
+        bounds.translate(0, mDisplayCutoutTouchableRegionSize);
+        region.op(bounds, Op.UNION);
+        bounds.recycle();
+    }
+
     @Override
     public void onConfigChanged(Configuration newConfig) {
         Resources resources = mContext.getResources();
@@ -403,7 +436,8 @@
 
     private void updateTouchableRegionListener() {
         boolean shouldObserve = hasPinnedHeadsUp() || mHeadsUpGoingAway
-                || mWaitingOnCollapseWhenGoingAway;
+                || mWaitingOnCollapseWhenGoingAway
+                || mStatusBarWindowView.getRootWindowInsets().getDisplayCutout() != null;
         if (shouldObserve == mIsObserving) {
             return;
         }
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 182293f..4df1e3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -21,10 +21,10 @@
 import android.view.ViewConfiguration;
 
 import com.android.systemui.Gefingerpoken;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 /**
  * A helper class to handle touches on the heads-up views.
@@ -118,7 +118,7 @@
                     mPanel.startExpandingFromPeek();
                     // This call needs to be after the expansion start otherwise we will get a
                     // flicker of one frame as it's not expanded yet.
-                    mHeadsUpManager.unpinAll();
+                    mHeadsUpManager.unpinAll(true);
                     mPanel.clearNotificationEffects();
                     endMotion();
                     return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 46d9827..e2f3319 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -367,10 +367,10 @@
         }
     }
 
-    private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable,
+    private void startFinishingCircleAnimation(float velocity, Runnable animationEndRunnable,
             boolean right) {
         KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
-        targetView.finishAnimation(velocity, mAnimationEndRunnable);
+        targetView.finishAnimation(velocity, animationEndRunnable);
     }
 
     private void setTranslation(float translation, boolean isReset, boolean animateReset) {
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 0716b37..d89bcda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -26,7 +26,9 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
 import android.app.admin.DevicePolicyManager;
+import android.hardware.biometrics.BiometricSourceType;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -172,6 +174,7 @@
     private int mIndicationBottomMarginAmbient;
     private float mDarkAmount;
     private int mBurnInXOffset;
+    private int mBurnInYOffset;
 
     public KeyguardBottomAreaView(Context context) {
         this(context, null);
@@ -245,6 +248,8 @@
                 R.dimen.keyguard_indication_margin_bottom);
         mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
                 R.dimen.keyguard_indication_margin_bottom_ambient);
+        mBurnInYOffset = getResources().getDimensionPixelSize(
+                R.dimen.charging_indication_burn_in_prevention_offset_y);
         updateCameraVisibility();
         mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
         mUnlockMethodCache.addListener(this);
@@ -317,6 +322,8 @@
                 R.dimen.keyguard_indication_margin_bottom);
         mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
                 R.dimen.keyguard_indication_margin_bottom_ambient);
+        mBurnInYOffset = getResources().getDimensionPixelSize(
+                R.dimen.charging_indication_burn_in_prevention_offset_y);
         MarginLayoutParams mlp = (MarginLayoutParams) mIndicationArea.getLayoutParams();
         if (mlp.bottomMargin != mIndicationBottomMargin) {
             mlp.bottomMargin = mIndicationBottomMargin;
@@ -523,7 +530,7 @@
                     o.setRotationAnimationHint(
                             WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
                     try {
-                        result = ActivityManager.getService().startActivityAsUser(
+                        result = ActivityTaskManager.getService().startActivityAsUser(
                                 null, getContext().getBasePackageName(),
                                 intent,
                                 intent.resolveTypeIfNeeded(getContext().getContentResolver()),
@@ -560,12 +567,6 @@
             return;
         }
         mDarkAmount = darkAmount;
-        // Let's randomize the bottom margin every time we wake up to avoid burn-in.
-        if (darkAmount == 0) {
-            mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
-                    R.dimen.keyguard_indication_margin_bottom_ambient)
-                    + (int) (Math.random() * mIndicationText.getTextSize());
-        }
         mIndicationArea.setAlpha(MathUtils.lerp(1f, 0.7f, darkAmount));
         mIndicationArea.setTranslationY(MathUtils.lerp(0,
                 mIndicationBottomMargin - mIndicationBottomMarginAmbient, darkAmount));
@@ -773,7 +774,8 @@
                 }
 
                 @Override
-                public void onFingerprintRunningStateChanged(boolean running) {
+                public void onBiometricRunningStateChanged(boolean running,
+                        BiometricSourceType biometricSourceType) {
                     mLockIcon.update();
                 }
 
@@ -841,8 +843,9 @@
     public void dozeTimeTick() {
         if (mDarkAmount == 1) {
             // Move indication every minute to avoid burn-in
-            final int dozeTranslation = mIndicationBottomMargin - mIndicationBottomMarginAmbient;
-            mIndicationArea.setTranslationY(dozeTranslation + (float) Math.random() * 5);
+            int dozeTranslation = mIndicationBottomMargin - mIndicationBottomMarginAmbient;
+            int burnInYOffset = (int) (-mBurnInYOffset + Math.random() * mBurnInYOffset * 2);
+            mIndicationArea.setTranslationY(dozeTranslation + burnInYOffset);
         }
     }
 
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 e096f3f..20ea27a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -57,6 +57,9 @@
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 /**
  * The header group on Keyguard.
  */
@@ -67,6 +70,7 @@
     private static final int LAYOUT_CUTOUT = 1;
     private static final int LAYOUT_NO_CUTOUT = 2;
 
+    private boolean mShowPercentAvailable;
     private boolean mBatteryCharging;
     private boolean mKeyguardUserSwitcherShowing;
     private boolean mBatteryListening;
@@ -165,6 +169,8 @@
                 R.dimen.system_icons_super_container_avatarless_margin_end);
         mCutoutSideNudge = getResources().getDimensionPixelSize(
                 R.dimen.display_cutout_margin_consumption);
+        mShowPercentAvailable = getContext().getResources().getBoolean(
+                com.android.internal.R.bool.config_battery_percentage_setting_available);
     }
 
     private void updateVisibilities() {
@@ -186,7 +192,7 @@
                 mMultiUserSwitch.setVisibility(View.GONE);
             }
         }
-        mBatteryView.setForceShowPercent(mBatteryCharging);
+        mBatteryView.setForceShowPercent(mBatteryCharging && mShowPercentAvailable);
     }
 
     private void updateSystemIconsLayoutParams() {
@@ -458,4 +464,15 @@
             ((DarkReceiver) v).onDarkChanged(tintArea, intensity, color);
         }
     }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("KeyguardStatusBarView:");
+        pw.println("  mBatteryCharging: " + mBatteryCharging);
+        pw.println("  mKeyguardUserSwitcherShowing: " + mKeyguardUserSwitcherShowing);
+        pw.println("  mBatteryListening: " + mBatteryListening);
+        pw.println("  mLayoutState: " + mLayoutState);
+        if (mBatteryView != null) {
+            mBatteryView.dump(fd, pw, args);
+        }
+    }
 }
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 8b8cbfe..8cace72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -44,7 +44,7 @@
 
     private final DarkIconDispatcher mStatusBarIconController;
     private final BatteryController mBatteryController;
-    private FingerprintUnlockController mFingerprintUnlockController;
+    private BiometricUnlockController mBiometricUnlockController;
 
     private LightBarTransitionsController mNavigationBarController;
     private int mSystemUiVisibility;
@@ -89,9 +89,9 @@
         updateNavigation();
     }
 
-    public void setFingerprintUnlockController(
-            FingerprintUnlockController fingerprintUnlockController) {
-        mFingerprintUnlockController = fingerprintUnlockController;
+    public void setBiometricUnlockController(
+            BiometricUnlockController biometricUnlockController) {
+        mBiometricUnlockController = biometricUnlockController;
     }
 
     public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis,
@@ -178,12 +178,12 @@
     }
 
     private boolean animateChange() {
-        if (mFingerprintUnlockController == null) {
+        if (mBiometricUnlockController == null) {
             return false;
         }
-        int unlockMode = mFingerprintUnlockController.getMode();
-        return unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
-                && unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+        int unlockMode = mBiometricUnlockController.getMode();
+        return unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                && unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
     }
 
     private void updateStatus(Rect fullscreenStackBounds, Rect dockedStackBounds) {
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 4b66ee5a..8928530 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -306,7 +306,7 @@
     private int getState() {
         KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
         boolean fingerprintRunning = updateMonitor.isFingerprintDetectionRunning();
-        boolean unlockingAllowed = updateMonitor.isUnlockingWithFingerprintAllowed();
+        boolean unlockingAllowed = updateMonitor.isUnlockingWithBiometricAllowed();
         if (mTransientFpError) {
             return STATE_FINGERPRINT_ERROR;
         } else if (mUnlockMethodCache.canSkipBouncer()) {
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 f9a540c..f3e100d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -22,7 +22,9 @@
 import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
 
 import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
 import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
 import static com.android.systemui.OverviewProxyService.OverviewProxyListener;
@@ -33,10 +35,10 @@
 import android.animation.ObjectAnimator;
 import android.annotation.IdRes;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
+import android.app.ActivityTaskManager;
 import android.app.Fragment;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -96,7 +98,7 @@
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 import com.android.systemui.statusbar.policy.KeyButtonView;
 import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -660,6 +662,10 @@
             nbModeChanged = nbMode != -1;
             if (nbModeChanged) {
                 if (mNavigationBarMode != nbMode) {
+                    if (mNavigationBarMode == MODE_TRANSPARENT
+                            || mNavigationBarMode == MODE_LIGHTS_OUT_TRANSPARENT) {
+                        mNavigationBarView.hideRecentsOnboarding();
+                    }
                     mNavigationBarMode = nbMode;
                     checkNavBarModes();
                 }
@@ -879,7 +885,7 @@
     private boolean onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2) {
         try {
             boolean sendBackLongPress = false;
-            IActivityManager activityManager = ActivityManagerNative.getDefault();
+            IActivityTaskManager activityManager = ActivityTaskManager.getService();
             boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
             boolean inLockTaskMode = activityManager.isInLockTaskMode();
             if (inLockTaskMode && !touchExplorationEnabled) {
@@ -933,7 +939,7 @@
     }
 
     private boolean onLongPressRecents() {
-        if (mRecents == null || !ActivityManager.supportsMultiWindow(getContext())
+        if (mRecents == null || !ActivityTaskManager.supportsMultiWindow(getContext())
                 || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()
                 || Recents.getConfiguration().isLowRamDevice
                 // If we are connected to the overview service, then disable the recents button
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 18e8775..ed1ae10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_TOP;
@@ -73,7 +75,6 @@
     private int mTouchDownY;
     private boolean mDownOnRecents;
     private VelocityTracker mVelocityTracker;
-    private boolean mIsInScreenPinning;
     private boolean mNotificationsVisibleOnDown;
 
     private boolean mDockWindowEnabled;
@@ -108,7 +109,6 @@
 
     public boolean onInterceptTouchEvent(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            mIsInScreenPinning = mNavigationBarView.inScreenPinning();
             mNotificationsVisibleOnDown = !mStatusBar.isPresenterFullyCollapsed();
         }
         if (!canHandleGestures()) {
@@ -216,7 +216,7 @@
                     && mDivider.getView().getWindowManagerProxy().getDockSide() == DOCKED_INVALID) {
                 Rect initialBounds = null;
                 int dragMode = calculateDragMode();
-                int createMode = ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+                int createMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
                 if (dragMode == DRAG_MODE_DIVIDER) {
                     initialBounds = new Rect();
                     mDivider.getView().calculateBoundsForPosition(mIsVertical
@@ -228,7 +228,7 @@
                             initialBounds);
                 } else if (dragMode == DRAG_MODE_RECENTS && mTouchDownX
                         < mContext.getResources().getDisplayMetrics().widthPixels / 2) {
-                    createMode = ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+                    createMode = SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
                 }
                 boolean docked = mRecentsComponent.splitPrimaryTask(dragMode, createMode,
                         initialBounds, MetricsEvent.ACTION_WINDOW_DOCK_SWIPE);
@@ -275,8 +275,7 @@
     }
 
     private boolean canHandleGestures() {
-        return !mIsInScreenPinning && !mStatusBar.isKeyguardShowing()
-                && !mNotificationsVisibleOnDown;
+        return !mStatusBar.isKeyguardShowing() && !mNotificationsVisibleOnDown;
     }
 
     private int calculateDragMode() {
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 8b9e12c..ad2e9dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -18,12 +18,14 @@
 
 import static android.view.MotionEvent.ACTION_DOWN;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
 
 import android.animation.LayoutTransition;
 import android.animation.LayoutTransition.TransitionListener;
 import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.annotation.DrawableRes;
@@ -60,6 +62,7 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.DockedStackExistsListener;
+import com.android.systemui.Interpolators;
 import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
@@ -161,6 +164,13 @@
 
     private int mRotateBtnStyle = R.style.RotateButtonCCWStart90;
 
+    /**
+     * Helper that is responsible for showing the right toast when a disallowed activity operation
+     * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
+     * fully locked mode we only show that unlocking is blocked.
+     */
+    private ScreenPinningNotify mScreenPinningNotify;
+
     private class NavTransitionListener implements TransitionListener {
         private boolean mBackTransitioning;
         private boolean mHomeAppearing;
@@ -286,6 +296,7 @@
         mConfiguration.updateFrom(context.getResources().getConfiguration());
         reloadNavIcons();
 
+        mScreenPinningNotify = new ScreenPinningNotify(mContext);
         mBarTransitions = new NavigationBarTransitions(this);
 
         mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
@@ -328,15 +339,15 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        if (shouldDeadZoneConsumeTouchEvents(event)) {
-            return true;
-        }
+        final boolean deadZoneConsumed = shouldDeadZoneConsumeTouchEvents(event);
         switch (event.getActionMasked()) {
             case ACTION_DOWN:
                 int x = (int) event.getX();
                 int y = (int) event.getY();
                 mDownHitTarget = HIT_TARGET_NONE;
-                if (getBackButton().isVisible() && mBackButtonBounds.contains(x, y)) {
+                if (deadZoneConsumed) {
+                    mDownHitTarget = HIT_TARGET_DEAD_ZONE;
+                } else if (getBackButton().isVisible() && mBackButtonBounds.contains(x, y)) {
                     mDownHitTarget = HIT_TARGET_BACK;
                 } else if (getHomeButton().isVisible() && mHomeButtonBounds.contains(x, y)) {
                     mDownHitTarget = HIT_TARGET_HOME;
@@ -353,9 +364,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        if (shouldDeadZoneConsumeTouchEvents(event)) {
-            return true;
-        }
+        shouldDeadZoneConsumeTouchEvents(event);
         if (mGestureHelper.onTouchEvent(event)) {
             return true;
         }
@@ -476,14 +485,15 @@
         Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme);
         Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme);
 
-        if (oldConfig.orientation != newConfig.orientation
-                || oldConfig.densityDpi != newConfig.densityDpi) {
+        final boolean orientationChange = oldConfig.orientation != newConfig.orientation;
+        final boolean densityChange = oldConfig.densityDpi != newConfig.densityDpi;
+        final boolean dirChange = oldConfig.getLayoutDirection() != newConfig.getLayoutDirection();
+
+        if (orientationChange || densityChange) {
             mDockedIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_docked);
             mHomeDefaultIcon = getHomeDrawable(lightContext, darkContext);
         }
-        if (oldConfig.densityDpi != newConfig.densityDpi
-                || oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) {
-            mBackIcon = getBackDrawable(lightContext, darkContext);
+        if (densityChange || dirChange) {
             mRecentIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_recent);
             mMenuIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_menu);
 
@@ -499,6 +509,9 @@
                 updateCarModeIcons(ctx);
             }
         }
+        if (orientationChange || densityChange || dirChange) {
+            mBackIcon = getBackDrawable(lightContext, darkContext);
+        }
     }
 
     public KeyButtonDrawable getBackDrawable(Context lightContext, Context darkContext) {
@@ -520,9 +533,26 @@
 
     private void orientBackButton(KeyButtonDrawable drawable) {
         final boolean useAltBack =
-            (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
-        drawable.setRotation(useAltBack
-                ? -90 : (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) ? 180 : 0);
+                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+        final boolean isRtl = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+        float degrees = useAltBack
+                ? (isRtl ? 270 : -90)
+                : (isRtl ? 180 : 0);
+        if (drawable.getRotation() == degrees) {
+            return;
+        }
+
+        // Animate the back button's rotation to the new degrees and only in portrait move up the
+        // back button to line up with the other buttons
+        float targetY = !mOverviewProxyService.shouldShowSwipeUpUI() && !mVertical && useAltBack
+                ? - getResources().getDimension(R.dimen.navbar_back_button_ime_offset)
+                : 0;
+        ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable,
+                PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_ROTATE, degrees),
+                PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_TRANSLATE_Y, targetY));
+        navBarAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        navBarAnimator.setDuration(200);
+        navBarAnimator.start();
     }
 
     private void orientHomeButton(KeyButtonDrawable drawable) {
@@ -764,7 +794,7 @@
                 showSwipeUpUI ? mQuickStepAccessibilityDelegate : null);
     }
 
-    private void updateSlippery() {
+    public void updateSlippery() {
         setSlippery(!isQuickStepSwipeUpEnabled() || mPanelView.isFullyExpanded());
     }
 
@@ -879,6 +909,10 @@
 
     public boolean isRotateButtonVisible() { return mShowRotateButton; }
 
+    void hideRecentsOnboarding() {
+        mRecentsOnboarding.hide(true);
+    }
+
     /**
      * @return the button at the given {@param x} and {@param y}.
      */
@@ -983,6 +1017,18 @@
         mBarTransitions.reapplyDarkIntensity();
     }
 
+    public void showPinningEnterExitToast(boolean entering) {
+        if (entering) {
+            mScreenPinningNotify.showPinningStartToast();
+        } else {
+            mScreenPinningNotify.showPinningExitToast();
+        }
+    }
+
+    public void showPinningEscapeToast() {
+        mScreenPinningNotify.showEscapeToast(isRecentsButtonVisible());
+    }
+
     public boolean isVertical() {
         return mVertical;
     }
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 8858381..37c2fdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -22,8 +22,8 @@
 import androidx.annotation.Nullable;
 import android.util.Log;
 
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
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 d859b5c..cb3a0d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -14,15 +14,15 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
 import java.util.function.Function;
@@ -47,6 +47,8 @@
     private final Rect mTintArea = new Rect();
     private NotificationStackScrollLayout mNotificationScrollLayout;
     private Context mContext;
+    private boolean mFullyDark;
+    private boolean mHasShelfIconsWhenFullyDark;
 
     public NotificationIconAreaController(Context context, StatusBar statusBar) {
         mStatusBar = statusBar;
@@ -173,13 +175,40 @@
     public void updateNotificationIcons() {
 
         updateStatusBarIcons();
-        updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
-                NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */,
-                false /* hideRepliedMessages */);
+        updateShelfIcons();
+        updateHasShelfIconsWhenFullyDark();
 
         applyNotificationIconsTint();
     }
 
+    private void updateHasShelfIconsWhenFullyDark() {
+        boolean hasIconsWhenFullyDark = false;
+        for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
+            View view = mNotificationScrollLayout.getChildAt(i);
+            if (view instanceof ExpandableNotificationRow) {
+                NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
+                if (shouldShowNotificationIcon(ent,
+                        NotificationShelf.SHOW_AMBIENT_ICONS /* showAmbient */,
+                        false /* hideDismissed */,
+                        true /* hideReplied */)) {
+                    hasIconsWhenFullyDark = true;
+                    break;
+                }
+            }
+        }
+        mHasShelfIconsWhenFullyDark = hasIconsWhenFullyDark;
+    }
+
+    public boolean hasShelfIconsWhenFullyDark() {
+        return mHasShelfIconsWhenFullyDark;
+    }
+
+    private void updateShelfIcons() {
+        updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
+                NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */,
+                mFullyDark /* hideRepliedMessages */);
+    }
+
     public void updateStatusBarIcons() {
         updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
                 false /* showAmbient */, true /* hideDismissed */, true /* hideRepliedMessages */);
@@ -320,6 +349,11 @@
         v.setDecorColor(mIconTint);
     }
 
+    public void setFullyDark(boolean fullyDark) {
+        mFullyDark = fullyDark;
+        updateShelfIcons();
+    }
+
     public void setDark(boolean dark) {
         mNotificationIcons.setDark(dark, false, 0);
         mShelfIcons.setDark(dark, false, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 653471d..0db408c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -35,9 +35,9 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
 import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.stack.AnimationFilter;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.statusbar.notification.stack.AnimationFilter;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.ViewState;
 
 import java.util.ArrayList;
 import java.util.HashMap;
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 2f18aad9..be8bf02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -47,6 +47,7 @@
 import android.view.ViewGroup;
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityManager;
+import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 
 import com.android.internal.logging.MetricsLogger;
@@ -59,13 +60,13 @@
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
@@ -73,10 +74,12 @@
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Consumer;
@@ -107,17 +110,20 @@
     private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = new AnimationProperties()
             .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
     private static final FloatProperty<NotificationPanelView> SET_DARK_AMOUNT_PROPERTY =
-            new FloatProperty<NotificationPanelView>("mDarkAmount") {
+            new FloatProperty<NotificationPanelView>("mInterpolatedDarkAmount") {
+
                 @Override
                 public void setValue(NotificationPanelView object, float value) {
-                    object.setDarkAmount(value);
+                    object.setDarkAmount(value, object.mDarkInterpolator.getInterpolation(value));
                 }
 
                 @Override
                 public Float get(NotificationPanelView object) {
-                    return object.mDarkAmount;
+                    return object.mLinearDarkAmount;
                 }
             };
+
+    private Interpolator mDarkInterpolator;
     private final PowerManager mPowerManager;
     private final AccessibilityManager mAccessibilityManager;
 
@@ -221,6 +227,7 @@
     private boolean mClosingWithAlphaFadeOut;
     private boolean mHeadsUpAnimatingAway;
     private boolean mLaunchingAffordance;
+    private boolean mAffordanceHasPreview;
     private FalsingManager mFalsingManager;
     private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
 
@@ -236,7 +243,18 @@
     private int mIndicationBottomPadding;
     private int mAmbientIndicationBottomPadding;
     private boolean mIsFullWidth;
-    private float mDarkAmount;
+
+    /**
+     * Current dark amount that follows regular interpolation curve of animation.
+     */
+    private float mInterpolatedDarkAmount;
+
+    /**
+     * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the
+     * interpolation curve is different.
+     */
+    private float mLinearDarkAmount;
+
     private float mDarkAmountTarget;
     private boolean mPulsing;
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
@@ -391,7 +409,7 @@
                 false);
         addView(mKeyguardBottomArea, index);
         initBottomArea();
-        setDarkAmount(mDarkAmount);
+        setDarkAmount(mLinearDarkAmount, mInterpolatedDarkAmount);
 
         setKeyguardStatusViewVisibility(mStatusBarState, false, false);
         setKeyguardBottomAreaVisibility(mStatusBarState, false);
@@ -505,7 +523,7 @@
                     getExpandedFraction(),
                     totalHeight,
                     mKeyguardStatusView.getHeight(),
-                    mDarkAmount,
+                    mInterpolatedDarkAmount,
                     mStatusBar.isKeyguardCurrentlySecure(),
                     mPulsing,
                     mBouncerTop);
@@ -1342,9 +1360,7 @@
         mQsExpansionHeight = height;
         updateQsExpansion();
         requestScrollerTopPaddingUpdate(false /* animate */);
-        if (mKeyguardShowing) {
-            updateHeaderKeyguardAlpha();
-        }
+        updateHeaderKeyguardAlpha();
         if (mStatusBarState == StatusBarState.SHADE_LOCKED
                 || mStatusBarState == StatusBarState.KEYGUARD) {
             updateKeyguardBottomAreaAlpha();
@@ -1747,6 +1763,9 @@
     }
 
     private void updateHeaderKeyguardAlpha() {
+        if (!mKeyguardShowing) {
+            return;
+        }
         float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
         mKeyguardStatusBar.setAlpha(Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
                 * mKeyguardStatusBarAnimateAlpha);
@@ -1916,7 +1935,7 @@
         if (view == null && mQsExpanded) {
             return;
         }
-        if (needsAnimation && mDarkAmount == 0) {
+        if (needsAnimation && mInterpolatedDarkAmount == 0) {
             mAnimateNextPositionUpdate = true;
         }
         ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
@@ -2489,7 +2508,7 @@
     }
 
     protected void setVerticalPanelTranslation(float translation) {
-        mNotificationStackScroller.setTranslationX(translation);
+        mNotificationStackScroller.setVerticalPanelTranslation(translation);
         mQsFrame.setTranslationX(translation);
         int size = mVerticalTranslationListener.size();
         for (int i = 0; i < size; i++) {
@@ -2515,7 +2534,8 @@
     }
 
     private void updateStatusBarIcons() {
-        boolean showIconsWhenExpanded = isFullWidth() && getExpandedHeight() < getOpeningHeight();
+        boolean showIconsWhenExpanded = (isPanelVisibleBecauseOfHeadsUp() || isFullWidth())
+                && getExpandedHeight() < getOpeningHeight();
         if (showIconsWhenExpanded && mNoVisibleNotifications && isOnKeyguard()) {
             showIconsWhenExpanded = false;
         }
@@ -2569,6 +2589,7 @@
         } else {
             animate = false;
         }
+        mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null;
         mAffordanceHelper.launchAffordance(animate, getLayoutDirection() == LAYOUT_DIRECTION_RTL);
     }
 
@@ -2614,6 +2635,13 @@
     }
 
     /**
+     * Return true when a bottom affordance is launching an occluded activity with a splash screen.
+     */
+    public boolean isLaunchingAffordanceWithPreview() {
+        return mLaunchingAffordance && mAffordanceHasPreview;
+    }
+
+    /**
      * Whether the camera application can be launched for the camera launch gesture.
      *
      * @param keyguardIsShowing whether keyguard is being shown
@@ -2718,31 +2746,40 @@
         }
         mDarkAmountTarget = darkAmount;
         if (animate) {
+            if (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f) {
+                mDarkInterpolator = dozing
+                        ? Interpolators.FAST_OUT_SLOW_IN
+                        : Interpolators.TOUCH_RESPONSE_REVERSE;
+            }
+            mNotificationStackScroller.notifyDarkAnimationStart(dozing);
             mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, darkAmount);
-            mDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-            mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
+            mDarkAnimator.setInterpolator(Interpolators.LINEAR);
+            mDarkAnimator.setDuration(mNotificationStackScroller.getDarkAnimationDuration(dozing));
             mDarkAnimator.start();
         } else {
-            setDarkAmount(darkAmount);
+            setDarkAmount(darkAmount, darkAmount);
         }
     }
 
-    private void setDarkAmount(float amount) {
-        mDarkAmount = amount;
-        mKeyguardStatusView.setDarkAmount(mDarkAmount);
-        mKeyguardBottomArea.setDarkAmount(mDarkAmount);
+    private void setDarkAmount(float linearAmount, float amount) {
+        mInterpolatedDarkAmount = amount;
+        mLinearDarkAmount = linearAmount;
+        mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount);
+        mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
         positionClockAndNotifications();
+        mNotificationStackScroller.setDarkAmount(linearAmount, mInterpolatedDarkAmount);
     }
 
     public void setPulsing(boolean pulsing) {
         mPulsing = pulsing;
-        final boolean canAnimatePulse =
-                !DozeParameters.getInstance(mContext).getDisplayNeedsBlanking();
-        if (canAnimatePulse) {
+        DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
+        final boolean animatePulse = !dozeParameters.getDisplayNeedsBlanking()
+                && dozeParameters.getAlwaysOn();
+        if (animatePulse) {
             mAnimateNextPositionUpdate = true;
         }
-        mNotificationStackScroller.setPulsing(pulsing, canAnimatePulse);
-        mKeyguardStatusView.setPulsing(pulsing, canAnimatePulse);
+        mNotificationStackScroller.setPulsing(pulsing, animatePulse);
+        mKeyguardStatusView.setPulsing(pulsing, animatePulse);
     }
 
     public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
@@ -2755,7 +2792,7 @@
     public void dozeTimeTick() {
         mKeyguardStatusView.dozeTimeTick();
         mKeyguardBottomArea.dozeTimeTick();
-        if (mDarkAmount > 0) {
+        if (mInterpolatedDarkAmount > 0) {
             positionClockAndNotifications();
         }
     }
@@ -2827,4 +2864,12 @@
         setKeyguardStatusViewVisibility(mStatusBarState, true /* keyguardFadingAway */,
                 false /* goingToFullShade */);
     }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        super.dump(fd, pw, args);
+        if (mKeyguardStatusBar != null) {
+            mKeyguardStatusBar.dump(fd, pw, args);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 641f485..e7ede6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -29,13 +29,11 @@
 import android.widget.FrameLayout;
 
 import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 /**
  * The container with notification stack scroller and quick settings inside.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index c4d7e72..5d23494 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -27,8 +29,11 @@
     public static final boolean DEBUG = false;
     public static final String TAG = PanelBar.class.getSimpleName();
     private static final boolean SPEW = false;
+    private static final String PANEL_BAR_SUPER_PARCELABLE = "panel_bar_super_parcelable";
+    private static final String STATE = "state";
     private boolean mBouncerShowing;
     private boolean mExpanded;
+    protected float mPanelFraction;
 
     public static final void LOG(String fmt, Object... args) {
         if (!DEBUG) return;
@@ -48,8 +53,26 @@
         mState = state;
     }
 
-    public int getState() {
-        return mState;
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(PANEL_BAR_SUPER_PARCELABLE, super.onSaveInstanceState());
+        bundle.putInt(STATE, mState);
+        return bundle;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        if (state == null || !(state instanceof Bundle)) {
+            super.onRestoreInstanceState(state);
+            return;
+        }
+
+        Bundle bundle = (Bundle) state;
+        super.onRestoreInstanceState(bundle.getParcelable(PANEL_BAR_SUPER_PARCELABLE));
+        if (((Bundle) state).containsKey(STATE)) {
+            go(bundle.getInt(STATE, STATE_CLOSED));
+        }
     }
 
     public PanelBar(Context context, AttributeSet attrs) {
@@ -77,6 +100,14 @@
         if (mPanel != null) mPanel.setImportantForAccessibility(important);
     }
 
+    public float getExpansionFraction() {
+        return mPanelFraction;
+    }
+
+    public boolean isExpanded() {
+        return mExpanded;
+    }
+
     private void updateVisibility() {
         mPanel.setVisibility(mExpanded || mBouncerShowing ? VISIBLE : INVISIBLE);
     }
@@ -131,6 +162,7 @@
         if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
         PanelView pv = mPanel;
         mExpanded = expanded;
+        mPanelFraction = frac;
         updateVisibility();
         // adjust any other panels that may be partially visible
         if (expanded) {
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 e9c0f5d..57e01e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -23,6 +23,7 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityManager.StackInfo;
+import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
 import android.app.AlarmManager.AlarmClockInfo;
 import android.app.AppGlobals;
@@ -505,7 +506,7 @@
         mUiOffloadThread.submit(() -> {
             final int userId;
             try {
-                userId = ActivityManager.getService().getLastResumedActivityUserId();
+                userId = ActivityTaskManager.getService().getLastResumedActivityUserId();
                 boolean isManagedProfile = mUserManager.isManagedProfile(userId);
                 mHandler.post(() -> {
                     final boolean showIcon;
@@ -536,7 +537,8 @@
         mCurrentNotifs.clear();
         mUiOffloadThread.submit(() -> {
             try {
-                final StackInfo focusedStack = ActivityManager.getService().getFocusedStackInfo();
+                final StackInfo focusedStack =
+                        ActivityTaskManager.getService().getFocusedStackInfo();
                 if (focusedStack != null) {
                     final int windowingMode =
                             focusedStack.configuration.windowConfiguration.getWindowingMode();
@@ -562,7 +564,7 @@
             ArraySet<Pair<String, Integer>> notifs, NotificationManager noMan, IPackageManager pm) {
         try {
             final StackInfo info =
-                    ActivityManager.getService().getStackInfo(windowingMode, activityType);
+                    ActivityTaskManager.getService().getStackInfo(windowingMode, activityType);
             checkStack(info, notifs, noMan, pm);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
@@ -625,7 +627,6 @@
                     .putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName)
                     .putExtra(Intent.EXTRA_VERSION_CODE, (int) (appInfo.versionCode & 0x7fffffff))
                     .putExtra(Intent.EXTRA_LONG_VERSION_CODE, appInfo.versionCode)
-                    .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent)
                     .putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, pendingIntent);
 
             PendingIntent webPendingIntent = PendingIntent.getActivity(mContext, 0, goToWebIntent, 0);
@@ -652,7 +653,7 @@
     private Intent getTaskIntent(int taskId, int userId) {
         try {
             final List<ActivityManager.RecentTaskInfo> tasks =
-                    ActivityManager.getService().getRecentTasks(
+                    ActivityTaskManager.getService().getRecentTasks(
                             NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId).getList();
             for (int i = 0; i < tasks.size(); i++) {
                 if (tasks.get(i).id == taskId) {
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 5477f88..59863ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -59,7 +59,6 @@
     private final PhoneStatusBarTransitions mBarTransitions;
     private ScrimController mScrimController;
     private float mMinFraction;
-    private float mPanelFraction;
     private Runnable mHideExpandedRunnable = new Runnable() {
         @Override
         public void run() {
@@ -269,7 +268,6 @@
     @Override
     public void panelExpansionChanged(float frac, boolean expanded) {
         super.panelExpansionChanged(frac, expanded);
-        mPanelFraction = frac;
         updateScrimFraction();
         if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
             mBar.getNavigationBarView().onPanelExpandedChange(expanded);
@@ -331,30 +329,25 @@
         // or letterboxing from the right or left sides.
 
         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
-        if (mDisplayCutout == null) {
+        if (mDisplayCutout == null || mDisplayCutout.isEmpty()
+                || mLastOrientation != ORIENTATION_PORTRAIT || cornerCutoutMargins == null) {
             lp.leftMargin = 0;
             lp.rightMargin = 0;
             return;
         }
 
-        lp.leftMargin = mDisplayCutout.getSafeInsetLeft();
-        lp.rightMargin = mDisplayCutout.getSafeInsetRight();
+        lp.leftMargin = Math.max(lp.leftMargin, cornerCutoutMargins.first);
+        lp.rightMargin = Math.max(lp.rightMargin, cornerCutoutMargins.second);
 
-        if (cornerCutoutMargins != null) {
-            lp.leftMargin = Math.max(lp.leftMargin, cornerCutoutMargins.first);
-            lp.rightMargin = Math.max(lp.rightMargin, cornerCutoutMargins.second);
-
-            // If we're already inset enough (e.g. on the status bar side), we can have 0 margin
-            WindowInsets insets = getRootWindowInsets();
-            int leftInset = insets.getSystemWindowInsetLeft();
-            int rightInset = insets.getSystemWindowInsetRight();
-            if (lp.leftMargin <= leftInset) {
-                lp.leftMargin = 0;
-            }
-            if (lp.rightMargin <= rightInset) {
-                lp.rightMargin = 0;
-            }
-
+        // If we're already inset enough (e.g. on the status bar side), we can have 0 margin
+        WindowInsets insets = getRootWindowInsets();
+        int leftInset = insets.getSystemWindowInsetLeft();
+        int rightInset = insets.getSystemWindowInsetRight();
+        if (lp.leftMargin <= leftInset) {
+            lp.leftMargin = 0;
+        }
+        if (lp.rightMargin <= rightInset) {
+            lp.rightMargin = 0;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index ea1b980..6cc88bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -22,20 +22,21 @@
 import static com.android.systemui.Interpolators.ALPHA_OUT;
 import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
 import static com.android.systemui.OverviewProxyService.TAG_OPS;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RadialGradient;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
+import android.graphics.Shader;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.FloatProperty;
@@ -54,7 +55,6 @@
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shared.system.NavigationBarCompat;
-import com.android.internal.graphics.ColorUtils;
 
 /**
  * Class to detect gestures on the navigation bar and implement quick scrub.
@@ -65,6 +65,7 @@
     private static final int ANIM_IN_DURATION_MS = 150;
     private static final int ANIM_OUT_DURATION_MS = 134;
     private static final float TRACK_SCALE = 0.95f;
+    private static final float GRADIENT_WIDTH = .75f;
 
     private NavigationBarView mNavigationBarView;
 
@@ -78,23 +79,23 @@
     private boolean mIsRTL;
     private float mTrackAlpha;
     private float mTrackScale = TRACK_SCALE;
-    private int mLightTrackColor;
-    private int mDarkTrackColor;
     private float mDarkIntensity;
+    private RadialGradient mHighlight;
+    private float mHighlightCenter;
     private AnimatorSet mTrackAnimator;
     private ButtonDispatcher mHitTarget;
     private View mCurrentNavigationBarView;
+    private boolean mIsInScreenPinning;
 
     private final Handler mHandler = new Handler();
     private final Rect mTrackRect = new Rect();
-    private final Drawable mTrackDrawable;
     private final OverviewProxyService mOverviewEventSender;
     private final int mTrackThickness;
     private final int mTrackEndPadding;
     private final Context mContext;
     private final Matrix mTransformGlobalMatrix = new Matrix();
     private final Matrix mTransformLocalMatrix = new Matrix();
-    private final ArgbEvaluator mTrackColorEvaluator = new ArgbEvaluator();
+    private final Paint mTrackPaint = new Paint();
 
     private final FloatProperty<QuickStepController> mTrackAlphaProperty =
             new FloatProperty<QuickStepController>("TrackAlpha") {
@@ -155,7 +156,8 @@
         mOverviewEventSender = Dependency.get(OverviewProxyService.class);
         mTrackThickness = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_thickness);
         mTrackEndPadding = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
-        mTrackDrawable = context.getDrawable(R.drawable.qs_scrubber_track).mutate();
+        mTrackPaint.setAntiAlias(true);
+        mTrackPaint.setDither(true);
     }
 
     public void setComponents(NavigationBarView navigationBarView) {
@@ -184,6 +186,8 @@
     }
 
     private boolean handleTouchEvent(MotionEvent event) {
+        final boolean deadZoneConsumed =
+                mNavigationBarView.getDownHitTarget() == HIT_TARGET_DEAD_ZONE;
         if (mOverviewEventSender.getProxy() == null || (!mNavigationBarView.isQuickScrubEnabled()
                 && !mNavigationBarView.isQuickStepSwipeUpEnabled())) {
             return false;
@@ -195,6 +199,7 @@
             case MotionEvent.ACTION_DOWN: {
                 int x = (int) event.getX();
                 int y = (int) event.getY();
+                mIsInScreenPinning = mNavigationBarView.inScreenPinning();
 
                 // End any existing quickscrub animations before starting the new transition
                 if (mTrackAnimator != null) {
@@ -287,6 +292,8 @@
                     } catch (RemoteException e) {
                         Log.e(TAG, "Failed to send progress of quick scrub.", e);
                     }
+                    mHighlightCenter = x;
+                    mNavigationBarView.invalidate();
                 }
                 break;
             }
@@ -298,11 +305,11 @@
 
         // Proxy motion events to launcher if not handled by quick scrub
         // Proxy motion events up/cancel that would be sent after long press on any nav button
-        if (!mQuickScrubActive && (mAllowGestureDetection || action == MotionEvent.ACTION_CANCEL
-                || action == MotionEvent.ACTION_UP)) {
+        if (!mQuickScrubActive && !mIsInScreenPinning && (mAllowGestureDetection
+                || action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP)) {
             proxyMotionEvents(event);
         }
-        return mQuickScrubActive || mQuickStepStarted;
+        return mQuickScrubActive || mQuickStepStarted || deadZoneConsumed;
     }
 
     @Override
@@ -310,18 +317,18 @@
         if (!mNavigationBarView.isQuickScrubEnabled()) {
             return;
         }
-        int color = (int) mTrackColorEvaluator.evaluate(mDarkIntensity, mLightTrackColor,
-                mDarkTrackColor);
-        int colorAlpha = ColorUtils.setAlphaComponent(color,
-                (int) (Color.alpha(color) * mTrackAlpha));
-        mTrackDrawable.setTint(colorAlpha);
+        mTrackPaint.setAlpha(Math.round(255f * mTrackAlpha));
 
         // Scale the track, but apply the inverse scale from the nav bar
+        final float radius = mTrackRect.height() / 2;
         canvas.save();
+        float translate = Utilities.clamp(mHighlightCenter, mTrackRect.left, mTrackRect.right);
+        canvas.translate(translate, 0);
         canvas.scale(mTrackScale / mNavigationBarView.getScaleX(),
                 1f / mNavigationBarView.getScaleY(),
                 mTrackRect.centerX(), mTrackRect.centerY());
-        mTrackDrawable.draw(canvas);
+        canvas.drawRoundRect(mTrackRect.left - translate, mTrackRect.top,
+                mTrackRect.right - translate, mTrackRect.bottom, radius, radius, mTrackPaint);
         canvas.restore();
     }
 
@@ -346,12 +353,20 @@
             x2 = x1 + width - 2 * mTrackEndPadding;
         }
         mTrackRect.set(x1, y1, x2, y2);
-        mTrackDrawable.setBounds(mTrackRect);
+        updateHighlight();
     }
 
     @Override
     public void onDarkIntensityChange(float intensity) {
+        final float oldIntensity = mDarkIntensity;
         mDarkIntensity = intensity;
+
+        // When in quick scrub, invalidate gradient if changing intensity from black to white and
+        // vice-versa
+        if (mNavigationBarView.isQuickScrubEnabled()
+                && Math.round(intensity) != Math.round(oldIntensity)) {
+            updateHighlight();
+        }
         mNavigationBarView.invalidate();
     }
 
@@ -382,6 +397,12 @@
     }
 
     private void startQuickStep(MotionEvent event) {
+        if (mIsInScreenPinning) {
+            mNavigationBarView.showPinningEscapeToast();
+            mAllowGestureDetection = false;
+            return;
+        }
+
         mQuickStepStarted = true;
         event.transform(mTransformGlobalMatrix);
         try {
@@ -407,11 +428,15 @@
     }
 
     private void startQuickScrub() {
-        if (!mQuickScrubActive) {
-            mQuickScrubActive = true;
-            mLightTrackColor = mContext.getColor(R.color.quick_step_track_background_light);
-            mDarkTrackColor = mContext.getColor(R.color.quick_step_track_background_dark);
+        if (mIsInScreenPinning) {
+            mNavigationBarView.showPinningEscapeToast();
+            mAllowGestureDetection = false;
+            return;
+        }
 
+        if (!mQuickScrubActive) {
+            updateHighlight();
+            mQuickScrubActive = true;
             ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
                     PropertyValuesHolder.ofFloat(mTrackAlphaProperty, 1f),
                     PropertyValuesHolder.ofFloat(mTrackScaleProperty, 1f));
@@ -424,6 +449,9 @@
             mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
             mTrackAnimator.start();
 
+            // Disable slippery for quick scrub to not cancel outside the nav bar
+            mNavigationBarView.updateSlippery();
+
             try {
                 mOverviewEventSender.getProxy().onQuickScrubStart();
                 if (DEBUG_OVERVIEW_PROXY) {
@@ -482,7 +510,29 @@
     private void resetQuickScrub() {
         mQuickScrubActive = false;
         mAllowGestureDetection = false;
+        if (mCurrentNavigationBarView != null) {
+            mCurrentNavigationBarView.setAlpha(1f);
+        }
         mCurrentNavigationBarView = null;
+        updateHighlight();
+    }
+
+    private void updateHighlight() {
+        if (mTrackRect.isEmpty()) {
+            return;
+        }
+        int colorBase, colorGrad;
+        if (mDarkIntensity > 0.5f) {
+            colorBase = mContext.getColor(R.color.quick_step_track_background_background_dark);
+            colorGrad = mContext.getColor(R.color.quick_step_track_background_foreground_dark);
+        } else {
+            colorBase = mContext.getColor(R.color.quick_step_track_background_background_light);
+            colorGrad = mContext.getColor(R.color.quick_step_track_background_foreground_light);
+        }
+        mHighlight = new RadialGradient(0, mTrackRect.height() / 2,
+                mTrackRect.width() * GRADIENT_WIDTH, colorGrad, colorBase,
+                Shader.TileMode.CLAMP);
+        mTrackPaint.setShader(mHighlight);
     }
 
     private boolean proxyMotionEvents(MotionEvent event) {
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 de499d6..0c361ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -29,9 +29,7 @@
 import android.os.Trace;
 import android.util.Log;
 import android.util.MathUtils;
-import android.view.Choreographer;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
@@ -43,14 +41,13 @@
 import com.android.internal.graphics.ColorUtils;
 import com.android.internal.util.function.TriConsumer;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.statusbar.notification.stack.ViewState;
 import com.android.systemui.util.AlarmTimeout;
 import com.android.systemui.util.wakelock.DelayedWakeLock;
 import com.android.systemui.util.wakelock.WakeLock;
@@ -116,6 +113,7 @@
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final DozeParameters mDozeParameters;
     private final AlarmTimeout mTimeTicker;
+    private final KeyguardVisibilityCallback mKeyguardVisibilityCallback;
 
     private final SysuiColorExtractor mColorExtractor;
     private GradientColors mLockColors;
@@ -174,6 +172,8 @@
         mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
         mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
         mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+        mKeyguardVisibilityCallback = new KeyguardVisibilityCallback();
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
         mScrimBehindAlphaResValue = mContext.getResources().getFloat(R.dimen.scrim_behind_alpha);
         mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout,
                 "hide_aod_wallpaper", new Handler());
@@ -482,21 +482,13 @@
         // Make sure we have the right gradients and their opacities will satisfy GAR.
         if (mNeedsDrawableColorUpdate) {
             mNeedsDrawableColorUpdate = false;
-            final GradientColors currentScrimColors;
-            if (mState == ScrimState.KEYGUARD || mState == ScrimState.BOUNCER_SCRIMMED
-                    || mState == ScrimState.BOUNCER) {
-                // Always animate color changes if we're seeing the keyguard
-                mScrimInFront.setColors(mLockColors, true /* animated */);
-                mScrimBehind.setColors(mLockColors, true /* animated */);
-                currentScrimColors = mLockColors;
-            } else {
-                // Only animate scrim color if the scrim view is actually visible
-                boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0;
-                boolean animateScrimBehind = mScrimBehind.getViewAlpha() != 0;
-                mScrimInFront.setColors(mSystemColors, animateScrimInFront);
-                mScrimBehind.setColors(mSystemColors, animateScrimBehind);
-                currentScrimColors = mSystemColors;
-            }
+            boolean isKeyguard = mKeyguardUpdateMonitor.isKeyguardVisible() && !mKeyguardOccluded;
+            GradientColors currentScrimColors = isKeyguard ? mLockColors : mSystemColors;
+            // Only animate scrim color if the scrim view is actually visible
+            boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0 && !mBlankScreen;
+            boolean animateScrimBehind = mScrimBehind.getViewAlpha() != 0 && !mBlankScreen;
+            mScrimInFront.setColors(currentScrimColors, animateScrimInFront);
+            mScrimBehind.setColors(currentScrimColors, animateScrimBehind);
 
             // Calculate minimum scrim opacity for white or black text.
             int textColor = currentScrimColors.supportsDarkText() ? Color.BLACK : Color.WHITE;
@@ -899,6 +891,28 @@
         updateScrims();
     }
 
+    public void setHasBackdrop(boolean hasBackdrop) {
+        for (ScrimState state : ScrimState.values()) {
+            state.setHasBackdrop(hasBackdrop);
+        }
+
+        // Backdrop event may arrive after state was already applied,
+        // in this case, back-scrim needs to be re-evaluated
+        if (mState == ScrimState.AOD || mState == ScrimState.PULSING) {
+            float newBehindAlpha = mState.getBehindAlpha(mNotificationDensity);
+            if (mCurrentBehindAlpha != newBehindAlpha) {
+                mCurrentBehindAlpha = newBehindAlpha;
+                updateScrims();
+            }
+        }
+    }
+
+    public void setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview) {
+        for (ScrimState state : ScrimState.values()) {
+            state.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
+        }
+    }
+
     public interface Callback {
         default void onStart() {
         }
@@ -909,4 +923,16 @@
         default void onCancelled() {
         }
     }
+
+    /**
+     * Simple keyguard callback that updates scrims when keyguard visibility changes.
+     */
+    private class KeyguardVisibilityCallback extends KeyguardUpdateMonitorCallback {
+
+        @Override
+        public void onKeyguardVisibilityChanged(boolean showing) {
+            mNeedsDrawableColorUpdate = true;
+            scheduleUpdate();
+        }
+    }
 }
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 713356b..085f7b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -20,9 +20,8 @@
 import android.os.Trace;
 import android.util.MathUtils;
 
-import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
 /**
  * Possible states of the ScrimController state machine.
@@ -106,8 +105,6 @@
         public void prepare(ScrimState previousState) {
             final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
             mBlankScreen = mDisplayRequiresBlanking;
-            mCurrentBehindAlpha = mWallpaperSupportsAmbientMode
-                    && !mKeyguardUpdateMonitor.hasLockscreenWallpaper() ? 0f : 1f;
             mCurrentInFrontAlpha = alwaysOnEnabled ? mAodFrontScrimAlpha : 1f;
             mCurrentInFrontTint = Color.BLACK;
             mCurrentBehindTint = Color.BLACK;
@@ -118,6 +115,11 @@
         }
 
         @Override
+        public float getBehindAlpha(float busyness) {
+            return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
+        }
+
+        @Override
         public boolean isLowPowerState() {
             return true;
         }
@@ -131,11 +133,14 @@
         public void prepare(ScrimState previousState) {
             mCurrentInFrontAlpha = 0;
             mCurrentInFrontTint = Color.BLACK;
-            mCurrentBehindAlpha = mWallpaperSupportsAmbientMode
-                    && !mKeyguardUpdateMonitor.hasLockscreenWallpaper() ? 0f : 1f;
             mCurrentBehindTint = Color.BLACK;
             mBlankScreen = mDisplayRequiresBlanking;
         }
+
+        @Override
+        public float getBehindAlpha(float busyness) {
+            return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
+        }
     },
 
     /**
@@ -147,8 +152,9 @@
             mCurrentBehindAlpha = 0;
             mCurrentInFrontAlpha = 0;
             mAnimationDuration = StatusBar.FADE_KEYGUARD_DURATION;
+            mAnimateChange = !mLaunchingAffordanceWithPreview;
 
-            if (previousState == ScrimState.AOD || previousState == ScrimState.PULSING) {
+            if (previousState == ScrimState.AOD) {
                 // Fade from black to transparent when coming directly from AOD
                 updateScrimColor(mScrimInFront, 1, Color.BLACK);
                 updateScrimColor(mScrimBehind, 1, Color.BLACK);
@@ -178,8 +184,9 @@
     DozeParameters mDozeParameters;
     boolean mDisplayRequiresBlanking;
     boolean mWallpaperSupportsAmbientMode;
-    KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     int mIndex;
+    boolean mHasBackdrop;
+    boolean mLaunchingAffordanceWithPreview;
 
     ScrimState(int index) {
         mIndex = index;
@@ -190,7 +197,6 @@
         mScrimBehind = scrimBehind;
         mDozeParameters = dozeParameters;
         mDisplayRequiresBlanking = dozeParameters.getDisplayNeedsBlanking();
-        mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(scrimInFront.getContext());
     }
 
     public void prepare(ScrimState previousState) {
@@ -253,7 +259,15 @@
         mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
     }
 
+    public void setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview) {
+        mLaunchingAffordanceWithPreview = launchingAffordanceWithPreview;
+    }
+
     public boolean isLowPowerState() {
         return false;
     }
+
+    public void setHasBackdrop(boolean hasBackdrop) {
+        mHasBackdrop = hasBackdrop;
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadowKeyDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadowKeyDrawable.java
index 8311dfd..8bd8048 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadowKeyDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadowKeyDrawable.java
@@ -28,8 +28,6 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 
-import com.android.systemui.R;
-
 /**
  * A drawable which adds shadow around a child drawable.
  */
@@ -60,6 +58,23 @@
         }
     }
 
+    public void setTranslationX(float x) {
+        setTranslation(x, mState.mTranslationY);
+    }
+
+    public void setTranslationY(float y) {
+        setTranslation(mState.mTranslationX, y);
+    }
+
+    public void setTranslation(float x, float y) {
+        if (mState.mTranslationX != x || mState.mTranslationY != y) {
+            mState.mTranslationX = x;
+            mState.mTranslationY = y;
+            mState.mLastDrawnBitmap = null;
+            invalidateSelf();
+        }
+    }
+
     public void setShadowProperties(int x, int y, int size, int color) {
         if (mState.mShadowOffsetX != x || mState.mShadowOffsetY != y
                 || mState.mShadowSize != size || mState.mShadowColor != color) {
@@ -76,13 +91,26 @@
         return mState.mRotateDegrees;
     }
 
+    public float getTranslationX() {
+        return mState.mTranslationX;
+    }
+
+    public float getTranslationY() {
+        return mState.mTranslationY;
+    }
+
     @Override
     public void draw(Canvas canvas) {
         Rect bounds = getBounds();
         if (bounds.isEmpty()) {
             return;
         }
-        if (mState.mLastDrawnBitmap == null) {
+
+        // If no cache or previous cached bitmap is hardware/software acceleration does not match
+        // the current canvas on draw then regenerate
+        if (mState.mLastDrawnBitmap == null
+                || mState.mIsHardwareBitmap != canvas.isHardwareAccelerated()) {
+            mState.mIsHardwareBitmap = canvas.isHardwareAccelerated();
             regenerateBitmapCache();
         }
         canvas.drawBitmap(mState.mLastDrawnBitmap, null, bounds, mPaint);
@@ -146,6 +174,7 @@
         // Call mutate, so that the pixel allocation by the underlying vector drawable is cleared.
         final Drawable d = mState.mChildState.newDrawable().mutate();
         d.setBounds(0, 0, mState.mBaseWidth, mState.mBaseHeight);
+        canvas.translate(mState.mTranslationX, mState.mTranslationY);
         d.draw(canvas);
 
         if (mState.mShadowSize > 0) {
@@ -163,15 +192,18 @@
             canvas.rotate(mState.mRotateDegrees, width / 2, height / 2);
 
             final float shadowOffsetX = (float) (Math.sin(radians) * mState.mShadowOffsetY
-                    + Math.cos(radians) * mState.mShadowOffsetX);
+                    + Math.cos(radians) * mState.mShadowOffsetX) - mState.mTranslationX;
             final float shadowOffsetY = (float) (Math.cos(radians) * mState.mShadowOffsetY
-                    - Math.sin(radians) * mState.mShadowOffsetX);
+                    - Math.sin(radians) * mState.mShadowOffsetX) - mState.mTranslationY;
 
             canvas.drawBitmap(shadow, offset[0] + shadowOffsetX, offset[1] + shadowOffsetY, paint);
             d.draw(canvas);
         }
 
-        bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
+        if (mState.mIsHardwareBitmap) {
+            bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
+        }
+
         mState.mLastDrawnBitmap = bitmap;
         canvas.restore();
     }
@@ -181,11 +213,14 @@
         int mBaseWidth;
         int mBaseHeight;
         float mRotateDegrees;
+        float mTranslationX;
+        float mTranslationY;
         int mShadowOffsetX;
         int mShadowOffsetY;
         int mShadowSize;
         int mShadowColor;
 
+        boolean mIsHardwareBitmap;
         Bitmap mLastDrawnBitmap;
         ConstantState mChildState;
 
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 7bbeed6..4c91a9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
@@ -44,6 +46,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
 import android.app.IWallpaperManager;
 import android.app.KeyguardManager;
@@ -52,6 +55,7 @@
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.app.TaskStackBuilder;
+import android.app.UiModeManager;
 import android.app.WallpaperColors;
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
@@ -184,26 +188,26 @@
 import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.stackdivider.WindowManagerProxy;
-import com.android.systemui.statusbar.ActivatableNotificationView;
-import com.android.systemui.statusbar.AppOpsListener;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.AppOpsListener;
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.FooterView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.FooterView;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationData.Entry;
-import com.android.systemui.statusbar.NotificationEntryManager;
-import com.android.systemui.statusbar.NotificationGutsManager;
-import com.android.systemui.statusbar.NotificationInfo;
+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.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationInfo;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationLogger;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -216,6 +220,7 @@
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -239,7 +244,6 @@
 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.stack.NotificationStackScrollLayout;
 import com.android.systemui.volume.VolumeComponent;
 
 import java.io.FileDescriptor;
@@ -358,7 +362,7 @@
     private VolumeComponent mVolumeComponent;
     private BrightnessMirrorController mBrightnessMirrorController;
     private boolean mBrightnessMirrorVisible;
-    protected FingerprintUnlockController mFingerprintUnlockController;
+    protected BiometricUnlockController mBiometricUnlockController;
     private LightBarController mLightBarController;
     protected LockscreenWallpaper mLockscreenWallpaper;
 
@@ -420,13 +424,6 @@
     protected KeyguardViewMediator mKeyguardViewMediator;
     private ZenModeController mZenController;
 
-    /**
-     * Helper that is responsible for showing the right toast when a disallowed activity operation
-     * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
-     * fully locked mode we only show that unlocking is blocked.
-     */
-    private ScreenPinningNotify mScreenPinningNotify;
-
     // for disabling the status bar
     private int mDisabled1 = 0;
     private int mDisabled2 = 0;
@@ -509,7 +506,7 @@
     protected NotificationLockscreenUserManager mLockscreenUserManager;
     protected NotificationRemoteInputManager mRemoteInputManager;
 
-    private BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
@@ -517,9 +514,9 @@
                 Log.w(TAG, "WallpaperManager not available");
                 return;
             }
-            WallpaperInfo info = wallpaperManager.getWallpaperInfo();
+            WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
             final boolean supportsAmbientMode = info != null &&
-                    info.getSupportsAmbientMode();
+                    info.supportsAmbientMode();
 
             mStatusBarWindowManager.setWallpaperSupportsAmbientMode(supportsAmbientMode);
             mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
@@ -577,7 +574,7 @@
             = (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
     private BatteryController mBatteryController;
     protected boolean mPanelExpanded;
-    private IOverlayManager mOverlayManager;
+    private UiModeManager mUiModeManager;
     private boolean mKeyguardRequested;
     private boolean mIsKeyguard;
     private LogMaker mStatusBarStateLog;
@@ -607,6 +604,12 @@
                         maybeEscalateHeadsUp();
                     }
                 }
+
+                @Override
+                public void onStrongAuthStateChanged(int userId) {
+                    super.onStrongAuthStateChanged(userId);
+                    mEntryManager.updateNotifications();
+                }
             };
 
     private NavigationBarFragment mNavigationBar;
@@ -632,8 +635,7 @@
         mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
         mBatteryController = Dependency.get(BatteryController.class);
         mAssistManager = Dependency.get(AssistManager.class);
-        mOverlayManager = IOverlayManager.Stub.asInterface(
-                ServiceManager.getService(Context.OVERLAY_SERVICE));
+        mUiModeManager = mContext.getSystemService(UiModeManager.class);
         mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class);
         mGutsManager = Dependency.get(NotificationGutsManager.class);
         mMediaManager = Dependency.get(NotificationMediaManager.class);
@@ -705,7 +707,8 @@
 
         // Make sure we always have the most current wallpaper info.
         IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
-        mContext.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter);
+        mContext.registerReceiverAsUser(mWallpaperChangedReceiver, UserHandle.ALL,
+                wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */);
         mWallpaperChangedReceiver.onReceive(mContext, null);
 
         mLockscreenUserManager.setUpWithPresenter(this, mEntryManager);
@@ -823,23 +826,45 @@
                 .createNotificationIconAreaController(context, this);
         inflateShelf();
         mNotificationIconAreaController.setupShelf(mNotificationShelf);
+        mStackScroller.setIconAreaController(mNotificationIconAreaController);
         Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
         FragmentHostManager.get(mStatusBarWindow)
                 .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                     CollapsedStatusBarFragment statusBarFragment =
                             (CollapsedStatusBarFragment) fragment;
                     statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
+                    PhoneStatusBarView oldStatusBarView = mStatusBarView;
                     mStatusBarView = (PhoneStatusBarView) fragment.getView();
                     mStatusBarView.setBar(this);
                     mStatusBarView.setPanel(mNotificationPanel);
                     mStatusBarView.setScrimController(mScrimController);
+
+                    // CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of
+                    // mStatusBarView.mExpanded and mStatusBarView.mBouncerShowing are false.
+                    // PhoneStatusBarView's new instance will set to be gone in
+                    // PanelBar.updateVisibility after calling mStatusBarView.setBouncerShowing
+                    // that will trigger PanelBar.updateVisibility. If there is a heads up showing,
+                    // it needs to notify PhoneStatusBarView's new instance to update the correct
+                    // status by calling mNotificationPanel.notifyBarPanelExpansionChanged().
+                    if (mHeadsUpManager.hasPinnedHeadsUp()) {
+                        mNotificationPanel.notifyBarPanelExpansionChanged();
+                    }
                     mStatusBarView.setBouncerShowing(mBouncerShowing);
+                    if (oldStatusBarView != null) {
+                        float fraction = oldStatusBarView.getExpansionFraction();
+                        boolean expanded = oldStatusBarView.isExpanded();
+                        mStatusBarView.panelExpansionChanged(fraction, expanded);
+                    }
+
+                    HeadsUpAppearanceController oldController = mHeadsUpAppearanceController;
                     if (mHeadsUpAppearanceController != null) {
                         // This view is being recreated, let's destroy the old one
                         mHeadsUpAppearanceController.destroy();
                     }
                     mHeadsUpAppearanceController = new HeadsUpAppearanceController(
                             mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
+                    mHeadsUpAppearanceController.readFrom(oldController);
+                    mStatusBarWindow.setStatusBarView(mStatusBarView);
                     setAreThereNotifications();
                     checkBarModes();
                 }).getFragmentManager()
@@ -877,7 +902,6 @@
         } catch (RemoteException ex) {
             // no window manager? good luck with that
         }
-        mScreenPinningNotify = new ScreenPinningNotify(mContext);
         mStackScroller.setLongPressListener(mEntryManager.getNotificationLongClicker());
         mStackScroller.setStatusBar(this);
         mStackScroller.setGroupManager(mGroupManager);
@@ -1122,6 +1146,8 @@
         mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
         mHeadsUpManager.onDensityOrFontScaleChanged();
 
+        inflateFooterView();
+        inflateEmptyShadeView();
         reevaluateStyles();
     }
 
@@ -1150,10 +1176,8 @@
         }
     }
 
-    protected void reevaluateStyles() {
-        inflateFooterView();
+    private void reevaluateStyles() {
         updateFooter();
-        inflateEmptyShadeView();
         updateEmptyShadeView();
     }
 
@@ -1164,6 +1188,21 @@
         }
     }
 
+    @Override
+    public void onUiModeChanged() {
+        // UiMode will change the style was already evaluated.
+        // We need to force the re-evaluation to make sure that all parents
+        // are up to date and new attrs will be rettrieved.
+        mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
+
+        if (mBrightnessMirrorController != null) {
+            mBrightnessMirrorController.onUiModeChanged();
+        }
+        if (mStackScroller != null) {
+            mStackScroller.onUiModeChanged();
+        }
+    }
+
     private void inflateEmptyShadeView() {
         if (mStackScroller == null) {
             return;
@@ -1174,7 +1213,8 @@
         mStackScroller.setEmptyShadeView(mEmptyShadeView);
     }
 
-    private void inflateFooterView() {
+    @VisibleForTesting
+    protected void inflateFooterView() {
         if (mStackScroller == null) {
             return;
         }
@@ -1304,18 +1344,18 @@
     protected void startKeyguard() {
         Trace.beginSection("StatusBar#startKeyguard");
         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
-        mFingerprintUnlockController = new FingerprintUnlockController(mContext,
+        mBiometricUnlockController = new BiometricUnlockController(mContext,
                 mDozeScrimController, keyguardViewMediator,
                 mScrimController, this, UnlockMethodCache.getInstance(mContext));
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
-                getBouncerContainer(), mNotificationPanel, mFingerprintUnlockController);
+                getBouncerContainer(), mNotificationPanel, mBiometricUnlockController);
         mKeyguardIndicationController
                 .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
-        mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
+        mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         mRemoteInputManager.getController().addCallback(mStatusBarKeyguardViewManager);
 
         mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
-        mLightBarController.setFingerprintUnlockController(mFingerprintUnlockController);
+        mLightBarController.setBiometricUnlockController(mBiometricUnlockController);
         Dependency.get(KeyguardDismissUtil.class).setDismissHandler(this::executeWhenUnlocked);
         Trace.endSection();
     }
@@ -1352,8 +1392,8 @@
                 return false;
             }
             int createMode = navbarPos == NAV_BAR_POS_LEFT
-                    ? ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT
-                    : ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+                    ? SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT
+                    : SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
             return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE, createMode,
                     null, metricsDockAction);
         } else {
@@ -1599,8 +1639,8 @@
             return; // called too early
         }
 
-        boolean wakeAndUnlock = mFingerprintUnlockController != null
-            && mFingerprintUnlockController.isWakeAndUnlock();
+        boolean wakeAndUnlock = mBiometricUnlockController != null
+            && mBiometricUnlockController.isWakeAndUnlock();
         if (mLaunchTransitionFadingAway || wakeAndUnlock) {
             mBackdrop.setVisibility(View.INVISIBLE);
             Trace.endSection();
@@ -1645,11 +1685,15 @@
                 && mStatusBarKeyguardViewManager.isOccluded();
 
         final boolean hasArtwork = artworkDrawable != null;
+        mColorExtractor.setHasBackdrop(hasArtwork);
+        if (mScrimController != null) {
+            mScrimController.setHasBackdrop(hasArtwork);
+        }
 
-        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) && !mDozing
+        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
                 && (mState != StatusBarState.SHADE || allowWhenShade)
-                && mFingerprintUnlockController.getMode()
-                        != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                && mBiometricUnlockController.getMode()
+                        != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
                 && !hideBecauseOccluded) {
             // time to show some art!
             if (mBackdrop.getVisibility() != View.VISIBLE) {
@@ -1662,7 +1706,6 @@
                     mBackdrop.setAlpha(1f);
                 }
                 mStatusBarWindowManager.setBackdropShowing(true);
-                mColorExtractor.setMediaBackdropVisible(true);
                 metaDataChanged = true;
                 if (DEBUG_MEDIA) {
                     Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
@@ -1714,10 +1757,9 @@
                 if (DEBUG_MEDIA) {
                     Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
                 }
-                mColorExtractor.setMediaBackdropVisible(false);
                 boolean cannotAnimateDoze = mDozing && !ScrimState.AOD.getAnimateChange();
-                if (mFingerprintUnlockController.getMode()
-                        == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                if (mBiometricUnlockController.getMode()
+                        == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
                         || hideBecauseOccluded || cannotAnimateDoze) {
 
                     // We are unlocking directly - no animation!
@@ -2061,17 +2103,6 @@
         updateTheme();
     }
 
-    public boolean isUsingDarkTheme() {
-        OverlayInfo themeInfo = null;
-        try {
-            themeInfo = mOverlayManager.getOverlayInfo("com.android.systemui.theme.dark",
-                    mLockscreenUserManager.getCurrentUserId());
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        }
-        return themeInfo != null && themeInfo.isEnabled();
-    }
-
     @Nullable
     public View getAmbientIndicationContainer() {
         return mAmbientIndicationContainer;
@@ -2127,6 +2158,10 @@
         }
     }
 
+    public boolean isHeadsUpShouldBeVisible() {
+        return mHeadsUpAppearanceController.shouldBeVisible();
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
@@ -2213,17 +2248,16 @@
 
     @Override
     public void showPinningEnterExitToast(boolean entering) {
-        if (entering) {
-            mScreenPinningNotify.showPinningStartToast();
-        } else {
-            mScreenPinningNotify.showPinningExitToast();
+        if (getNavigationBarView() != null) {
+            getNavigationBarView().showPinningEnterExitToast(entering);
         }
     }
 
     @Override
     public void showPinningEscapeToast() {
-        mScreenPinningNotify.showEscapeToast(getNavigationBarView() == null
-                || getNavigationBarView().isRecentsButtonVisible());
+        if (getNavigationBarView() != null) {
+            getNavigationBarView().showPinningEscapeToast();
+        }
     }
 
     boolean panelsEnabled() {
@@ -2450,8 +2484,8 @@
         return mGestureRec;
     }
 
-    public FingerprintUnlockController getFingerprintUnlockController() {
-        return mFingerprintUnlockController;
+    public BiometricUnlockController getBiometricUnlockController() {
+        return mBiometricUnlockController;
     }
 
     @Override // CommandQueue
@@ -2775,18 +2809,18 @@
             mStackScroller.dump(fd, pw, args);
         }
         pw.println("  Theme:");
-        if (mOverlayManager == null) {
-            pw.println("    overlay manager not initialized!");
-        } else {
-            pw.println("    dark overlay on: " + isUsingDarkTheme());
-        }
+        String nightMode = mUiModeManager == null ? "null" : mUiModeManager.getNightMode() + "";
+        pw.println("    dark theme: " + nightMode +
+                " (auto: " + UiModeManager.MODE_NIGHT_AUTO +
+                ", yes: " + UiModeManager.MODE_NIGHT_YES +
+                ", no: " + UiModeManager.MODE_NIGHT_NO + ")");
         final boolean lightWpTheme = mContext.getThemeResId() == R.style.Theme_SystemUI_Light;
         pw.println("    light wallpaper theme: " + lightWpTheme);
 
         DozeLog.dump(pw);
 
-        if (mFingerprintUnlockController != null) {
-            mFingerprintUnlockController.dump(pw);
+        if (mBiometricUnlockController != null) {
+            mBiometricUnlockController.dump(pw);
         }
 
         if (mKeyguardIndicationController != null) {
@@ -2951,7 +2985,7 @@
                         WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
             }
             try {
-                result = ActivityManager.getService().startActivityAsUser(
+                result = ActivityTaskManager.getService().startActivityAsUser(
                         null, mContext.getBasePackageName(),
                         intent,
                         intent.resolveTypeIfNeeded(mContext.getContentResolver()),
@@ -3094,10 +3128,10 @@
                 && mUnlockMethodCache.canSkipBouncer()
                 && !mLeaveOpenOnKeyguardHide
                 && isPulsing()) {
-            // Reuse the fingerprint wake-and-unlock transition if we dismiss keyguard from a pulse.
-            // TODO: Factor this transition out of FingerprintUnlockController.
-            mFingerprintUnlockController.startWakeAndUnlock(
-                    FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
+            // Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a pulse.
+            // TODO: Factor this transition out of BiometricUnlockController.
+            mBiometricUnlockController.startWakeAndUnlock(
+                    BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
         }
         if (mStatusBarKeyguardViewManager.isShowing()) {
             mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
@@ -3137,6 +3171,7 @@
         updateNotificationViews();
         mMediaManager.clearCurrentMediaNotification();
         setLockscreenUser(newUserId);
+        mWallpaperChangedReceiver.onReceive(mContext, null);
     }
 
     @Override
@@ -3501,8 +3536,8 @@
     }
 
     private boolean updateIsKeyguard() {
-        boolean wakeAndUnlocking = mFingerprintUnlockController.getMode()
-                == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+        boolean wakeAndUnlocking = mBiometricUnlockController.getMode()
+                == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
 
         // For dozing, keyguard needs to be shown whenever the device is non-interactive. Otherwise
         // there's no surface we can show to the user. Note that the device goes fully interactive
@@ -3549,8 +3584,8 @@
     }
 
     private void updatePanelExpansionForKeyguard() {
-        if (mState == StatusBarState.KEYGUARD && mFingerprintUnlockController.getMode()
-                != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
+        if (mState == StatusBarState.KEYGUARD && mBiometricUnlockController.getMode()
+                != BiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
             instantExpandNotificationsPanel();
         } else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
             instantCollapseNotificationPanel();
@@ -3846,23 +3881,7 @@
      * Switches theme from light to dark and vice-versa.
      */
     protected void updateTheme() {
-        final boolean inflated = mStackScroller != null;
-
-        // The system wallpaper defines if QS should be light or dark.
-        WallpaperColors systemColors = mColorExtractor
-                .getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
-        final boolean useDarkTheme = systemColors != null
-                && (systemColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
-        if (isUsingDarkTheme() != useDarkTheme) {
-            mUiOffloadThread.submit(() -> {
-                try {
-                    mOverlayManager.setEnabled("com.android.systemui.theme.dark",
-                            useDarkTheme, mLockscreenUserManager.getCurrentUserId());
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Can't change theme", e);
-                }
-            });
-        }
+        final boolean inflated = mStackScroller != null && mStatusBarWindowManager != null;
 
         // Lock wallpaper defines the color of the majority of the views, hence we'll use it
         // to set our default theme.
@@ -4451,12 +4470,15 @@
         updateScrimController();
     }
 
-    public void cancelCurrentTouch() {
+    /**
+     * Collapses the notification shade if it is tracking or expanded.
+     */
+    public void collapseShade() {
         if (mNotificationPanel.isTracking()) {
             mStatusBarWindow.cancelCurrentTouch();
-            if (mState == StatusBarState.SHADE) {
-                animateCollapsePanels();
-            }
+        }
+        if (mPanelExpanded && mState == StatusBarState.SHADE) {
+            animateCollapsePanels();
         }
     }
 
@@ -4675,7 +4697,7 @@
                 || mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
     }
 
-    public void notifyFpAuthModeChanged() {
+    public void notifyBiometricAuthModeChanged() {
         updateDozing();
         updateScrimController();
     }
@@ -4684,18 +4706,17 @@
         Trace.beginSection("StatusBar#updateDozing");
         // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
         boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD
-                || mFingerprintUnlockController.getMode()
-                        == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
-        final boolean alwaysOn = DozeParameters.getInstance(mContext).getAlwaysOn();
+                || mBiometricUnlockController.getMode()
+                        == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
         // When in wake-and-unlock we may not have received a change to mState
         // but we still should not be dozing, manually set to false.
-        if (mFingerprintUnlockController.getMode() ==
-                FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
+        if (mBiometricUnlockController.getMode() ==
+                mBiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
             dozing = false;
         }
         if (mDozing != dozing) {
             mDozing = dozing;
-            mKeyguardViewMediator.setAodShowing(mDozing && alwaysOn);
+            mKeyguardViewMediator.setAodShowing(mDozing);
             mStatusBarWindowManager.setDozing(mDozing);
             mStatusBarKeyguardViewManager.setDozing(mDozing);
             if (mAmbientIndicationContainer instanceof DozeReceiver) {
@@ -4714,22 +4735,25 @@
 
         // We don't want to end up in KEYGUARD state when we're unlocking with
         // fingerprint from doze. We should cross fade directly from black.
-        boolean wakeAndUnlocking = mFingerprintUnlockController.isWakeAndUnlock();
+        boolean wakeAndUnlocking = mBiometricUnlockController.isWakeAndUnlock();
 
         // Do not animate the scrim expansion when triggered by the fingerprint sensor.
         mScrimController.setExpansionAffectsAlpha(
-                !mFingerprintUnlockController.isFingerprintUnlock());
+                !mBiometricUnlockController.isBiometricUnlock());
+
+        boolean launchingAffordanceWithPreview =
+                mNotificationPanel.isLaunchingAffordanceWithPreview();
+        mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
 
         if (mBouncerShowing) {
             // Bouncer needs the front scrim when it's on top of an activity,
             // tapping on a notification, editing QS or being dismissed by
             // FLAG_DISMISS_KEYGUARD_ACTIVITY.
-            ScrimState state = mIsOccluded || mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
-                    || mStatusBarKeyguardViewManager.willDismissWithAction()
-                    || mStatusBarKeyguardViewManager.isFullscreenBouncer() ?
-                    ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
+            ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
+                    ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
             mScrimController.transitionTo(state);
-        } else if (mLaunchCameraOnScreenTurningOn || isInLaunchTransition()) {
+        } else if (isInLaunchTransition() || mLaunchCameraOnScreenTurningOn
+                || launchingAffordanceWithPreview) {
             mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
         } else if (mBrightnessMirrorVisible) {
             mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
@@ -4822,6 +4846,7 @@
                 }
 
                 private void setPulsing(boolean pulsing) {
+                    mKeyguardViewMediator.setPulsing(pulsing);
                     mNotificationPanel.setPulsing(pulsing);
                     mVisualStabilityManager.setPulsing(pulsing);
                     mIgnoreTouchWhilePulsing = false;
@@ -4862,8 +4887,8 @@
 
         @Override
         public boolean isPulsingBlocked() {
-            return mFingerprintUnlockController.getMode()
-                    == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+            return mBiometricUnlockController.getMode()
+                    == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
         }
 
         @Override
@@ -4874,7 +4899,7 @@
 
         @Override
         public boolean isBlockingDoze() {
-            if (mFingerprintUnlockController.hasPendingAuthentication()) {
+            if (mBiometricUnlockController.hasPendingAuthentication()) {
                 Log.i(TAG, "Blocking AOD because fingerprint has authenticated");
                 return true;
             }
@@ -5144,7 +5169,7 @@
                             row, wasOccluded);
                     try {
                         if (adapter != null) {
-                            ActivityManager.getService()
+                            ActivityTaskManager.getService()
                                     .registerRemoteAnimationForNextActivityStart(
                                             intent.getCreatorPackage(), adapter);
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index fe86378..c4424d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -17,8 +17,8 @@
 package com.android.systemui.statusbar.phone;
 
 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
-import static com.android.systemui.statusbar.phone.FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
-import static com.android.systemui.statusbar.phone.FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
 
 import android.content.ComponentCallbacks2;
 import android.content.Context;
@@ -91,7 +91,7 @@
     protected ViewMediatorCallback mViewMediatorCallback;
     protected StatusBar mStatusBar;
     private NotificationPanelView mNotificationPanelView;
-    private FingerprintUnlockController mFingerprintUnlockController;
+    private BiometricUnlockController mBiometricUnlockController;
 
     private ViewGroup mContainer;
 
@@ -108,7 +108,7 @@
     private boolean mLastBouncerDismissible;
     protected boolean mLastRemoteInputActive;
     private boolean mLastDozing;
-    private int mLastFpMode;
+    private int mLastBiometricMode;
     private boolean mGoingToSleepVisibleNotOccluded;
 
     private OnDismissAction mAfterKeyguardGoneAction;
@@ -142,11 +142,11 @@
     public void registerStatusBar(StatusBar statusBar,
             ViewGroup container,
             NotificationPanelView notificationPanelView,
-            FingerprintUnlockController fingerprintUnlockController,
+            BiometricUnlockController biometricUnlockController,
             DismissCallbackRegistry dismissCallbackRegistry) {
         mStatusBar = statusBar;
         mContainer = container;
-        mFingerprintUnlockController = fingerprintUnlockController;
+        mBiometricUnlockController = biometricUnlockController;
         mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
                 mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
                 mExpansionCallback);
@@ -170,11 +170,10 @@
         // • Full-screen user switcher is displayed.
         if (mNotificationPanelView.isUnlockHintRunning()) {
             mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
-        } else if (mOccluded || mBouncer.willDismissWithAction() || mBouncer.isShowingScrimmed()
-                || mStatusBar.isFullScreenUserSwitcherState()) {
+        } else if (bouncerNeedsScrimming()) {
             mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
         } else if (mShowing && !mDozing) {
-            if (!isWakeAndUnlocking()) {
+            if (!isWakeAndUnlocking() && !mStatusBar.isInLaunchTransition()) {
                 mBouncer.setExpansion(expansion);
             }
             if (expansion != KeyguardBouncer.EXPANSION_HIDDEN && tracking
@@ -260,7 +259,7 @@
     }
 
     private boolean isWakeAndUnlocking() {
-        int mode = mFingerprintUnlockController.getMode();
+        int mode = mBiometricUnlockController.getMode();
         return mode == MODE_WAKE_AND_UNLOCK || mode == MODE_WAKE_AND_UNLOCK_PULSING;
     }
 
@@ -443,13 +442,13 @@
         } else {
             executeAfterKeyguardGoneAction();
             boolean wakeUnlockPulsing =
-                    mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
+                    mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
             if (wakeUnlockPulsing) {
                 delay = 0;
                 fadeoutDuration = 240;
             }
             mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
-            mFingerprintUnlockController.startKeyguardFadingAway();
+            mBiometricUnlockController.startKeyguardFadingAway();
             hideBouncer(true /* destroyView */);
             if (wakeUnlockPulsing) {
                 mStatusBar.fadeKeyguardWhilePulsing();
@@ -461,7 +460,7 @@
                     wakeAndUnlockDejank();
                 } else {
                     mStatusBar.finishKeyguardFadingAway();
-                    mFingerprintUnlockController.finishKeyguardFadingAway();
+                    mBiometricUnlockController.finishKeyguardFadingAway();
                 }
             }
             updateStates();
@@ -485,14 +484,14 @@
         mContainer.postDelayed(() -> mStatusBarWindowManager.setKeyguardFadingAway(false),
                 100);
         mStatusBar.finishKeyguardFadingAway();
-        mFingerprintUnlockController.finishKeyguardFadingAway();
+        mBiometricUnlockController.finishKeyguardFadingAway();
         WindowManagerGlobal.getInstance().trimMemory(
                 ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
 
     }
 
     private void wakeAndUnlockDejank() {
-        if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
+        if (mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
                 && LatencyTracker.isEnabled(mContext)) {
             DejankUtils.postAfterTraversal(() ->
                     LatencyTracker.getInstance(mContext).onActionEnd(
@@ -619,7 +618,7 @@
         mLastBouncerDismissible = bouncerDismissible;
         mLastRemoteInputActive = remoteInputActive;
         mLastDozing = mDozing;
-        mLastFpMode = mFingerprintUnlockController.getMode();
+        mLastBiometricMode = mBiometricUnlockController.getMode();
         mStatusBar.onKeyguardViewManagerStatesUpdated();
     }
 
@@ -644,9 +643,9 @@
      * @return Whether the navigation bar should be made visible based on the current state.
      */
     protected boolean isNavBarVisible() {
-        int fpMode = mFingerprintUnlockController.getMode();
+        int biometricMode = mBiometricUnlockController.getMode();
         boolean keyguardShowing = mShowing && !mOccluded;
-        boolean hideWhileDozing = mDozing && fpMode != MODE_WAKE_AND_UNLOCK_PULSING;
+        boolean hideWhileDozing = mDozing && biometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
         return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
                 || mRemoteInputActive);
     }
@@ -656,7 +655,7 @@
      */
     protected boolean getLastNavBarVisible() {
         boolean keyguardShowing = mLastShowing && !mLastOccluded;
-        boolean hideWhileDozing = mLastDozing && mLastFpMode != MODE_WAKE_AND_UNLOCK_PULSING;
+        boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
         return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
                 || mLastRemoteInputActive);
     }
@@ -694,6 +693,14 @@
                 false /* delayed */, speedUpFactor);
     }
 
+
+    /**
+     * Called when cancel button in bouncer is pressed.
+     */
+    public void onCancelClicked() {
+        // No-op
+    }
+
     /**
      * Notifies that the user has authenticated by other means than using the bouncer, for example,
      * fingerprint.
@@ -731,12 +738,9 @@
         }
     }
 
-    public boolean willDismissWithAction() {
-        return mBouncer.willDismissWithAction();
-    }
-
     public boolean bouncerNeedsScrimming() {
-        return mBouncer.isShowingScrimmed();
+        return mOccluded || mBouncer.willDismissWithAction()  || mBouncer.needsFullscreenBouncer()
+                || mStatusBar.isFullScreenUserSwitcherState() || mBouncer.isShowingScrimmed();
     }
 
     public void dump(PrintWriter pw) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index fadc0ea..a38328a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -180,6 +180,15 @@
         mLpChanged.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
     }
 
+    private void applyExpandedFlag(State state) {
+        if (state.panelExpanded || state.isKeyguardShowingAndNotOccluded() || state.bouncerShowing
+                || ENABLE_REMOTE_INPUT && state.remoteInputActive) {
+            mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_STATUS_BAR_EXPANDED;
+        } else {
+            mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_STATUS_BAR_EXPANDED;
+        }
+    }
+
     private void applyHeight(State state) {
         boolean expanded = isExpanded(state);
         if (state.forcePluginOpen) {
@@ -234,6 +243,7 @@
         applyKeyguardFlags(state);
         applyForceStatusBarVisibleFlag(state);
         applyFocusableFlag(state);
+        applyExpandedFlag(state);
         adjustScreenOrientation(state);
         applyHeight(state);
         applyUserActivityTimeout(state);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index a79a41b..5cc0202 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -37,6 +37,7 @@
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.view.ActionMode;
+import android.view.DisplayCutout;
 import android.view.InputDevice;
 import android.view.InputQueue;
 import android.view.KeyEvent;
@@ -60,7 +61,7 @@
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -75,6 +76,7 @@
     private NotificationStackScrollLayout mStackScrollLayout;
     private NotificationPanelView mNotificationPanel;
     private View mBrightnessMirror;
+    private PhoneStatusBarView mStatusBarView;
 
     private int mRightInset = 0;
     private int mLeftInset = 0;
@@ -94,6 +96,12 @@
     private boolean mExpandAnimationRunning;
     private boolean mExpandAnimationPending;
 
+    /**
+     * If set to true, the current gesture started below the notch and we need to dispatch touch
+     * events manually as it's outside of the regular view bounds.
+     */
+    private boolean mExpandingBelowNotch;
+
     public StatusBarWindowView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setMotionEventSplittingEnabled(false);
@@ -112,10 +120,21 @@
             boolean paddingChanged = insets.top != getPaddingTop()
                     || insets.bottom != getPaddingBottom();
 
+            int rightCutout = 0;
+            int leftCutout = 0;
+            DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
+            if (displayCutout != null) {
+                leftCutout = displayCutout.getSafeInsetLeft();
+                rightCutout = displayCutout.getSafeInsetRight();
+            }
+
+            int targetLeft = Math.max(insets.left, leftCutout);
+            int targetRight = Math.max(insets.right, rightCutout);
+
             // Super-special right inset handling, because scrims and backdrop need to ignore it.
-            if (insets.right != mRightInset || insets.left != mLeftInset) {
-                mRightInset = insets.right;
-                mLeftInset = insets.left;
+            if (targetRight != mRightInset || targetLeft != mLeftInset) {
+                mRightInset = targetRight;
+                mLeftInset = targetLeft;
                 applyMargins();
             }
             // Drop top inset, and pass through bottom inset.
@@ -186,6 +205,10 @@
         }
     }
 
+    public void setStatusBarView(PhoneStatusBarView statusBarView) {
+        mStatusBarView = statusBarView;
+    }
+
     public void setService(StatusBar service) {
         mService = service;
         setDragDownHelper(new DragDownHelper(getContext(), this, mStackScrollLayout, mService));
@@ -258,7 +281,16 @@
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         boolean isDown = ev.getActionMasked() == MotionEvent.ACTION_DOWN;
+        boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
         boolean isCancel = ev.getActionMasked() == MotionEvent.ACTION_CANCEL;
+
+        // Reset manual touch dispatch state here but make sure the UP/CANCEL event still gets
+        // delivered.
+        boolean expandingBelowNotch = mExpandingBelowNotch;
+        if (isUp || isCancel) {
+            mExpandingBelowNotch = false;
+        }
+
         if (!isCancel && mService.shouldIgnoreTouch()) {
             return false;
         }
@@ -291,6 +323,17 @@
             mService.mDozeScrimController.extendPulse();
         }
 
+        // In case we start outside of the view bounds (below the status bar), we need to dispatch
+        // the touch manually as the view system can't accomodate for touches outside of the
+        // regular view bounds.
+        if (isDown && ev.getY() >= mBottom) {
+            mExpandingBelowNotch = true;
+            expandingBelowNotch = true;
+        }
+        if (expandingBelowNotch) {
+            return mStatusBarView.dispatchTouchEvent(ev);
+        }
+
         return super.dispatchTouchEvent(ev);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 0811179..56a177e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -33,9 +33,9 @@
 import com.android.keyguard.AlphaOptimizedLinearLayout;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.StatusIconDisplayable;
-import com.android.systemui.statusbar.stack.AnimationFilter;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.statusbar.notification.stack.AnimationFilter;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.ViewState;
 import java.util.ArrayList;
 
 /**
@@ -301,11 +301,7 @@
 
             vs.initFrom(child);
             vs.alpha = 1.0f;
-            if (child instanceof StatusIconDisplayable) {
-                vs.hidden = !((StatusIconDisplayable)child).isIconVisible();
-            } else {
-                vs.hidden = false;
-            }
+            vs.hidden = false;
         }
     }
 
@@ -333,22 +329,33 @@
             }
             StatusIconDisplayable icon = (StatusIconDisplayable) view;
             AnimationProperties animationProperties = null;
-            boolean animate = false;
+            boolean animateVisibility = true;
 
-            if (justAdded) {
+            // Figure out which properties of the state transition (if any) we need to animate
+            if (justAdded
+                    || icon.getVisibleState() == STATE_HIDDEN && visibleState == STATE_ICON) {
+                // Icon is appearing, fade it in by putting it where it will be and animating alpha
                 super.applyToView(view);
+                view.setAlpha(0.f);
+                icon.setVisibleState(STATE_HIDDEN);
                 animationProperties = ADD_ICON_PROPERTIES;
-                animate = true;
             } else if (icon.getVisibleState() != visibleState) {
-                animationProperties = DOT_ANIMATION_PROPERTIES;
-                animate = true;
+                if (icon.getVisibleState() == STATE_ICON && visibleState == STATE_HIDDEN) {
+                    // Disappearing, don't do anything fancy
+                    animateVisibility = false;
+                } else {
+                    // all other transitions (to/from dot, etc)
+                    animationProperties = ANIMATE_ALL_PROPERTIES;
+                }
+            } else if (visibleState != STATE_HIDDEN && xTranslation != view.getTranslationX()) {
+                // Visibility isn't changing, just animate position
+                animationProperties = X_ANIMATION_PROPERTIES;
             }
 
-            if (animate) {
+            icon.setVisibleState(visibleState, animateVisibility);
+            if (animationProperties != null) {
                 animateTo(view, animationProperties);
-                icon.setVisibleState(visibleState);
             } else {
-                icon.setVisibleState(visibleState);
                 super.applyToView(view);
             }
 
@@ -365,7 +372,7 @@
         }
     }.setDuration(200).setDelay(50);
 
-    private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() {
+    private static final AnimationProperties X_ANIMATION_PROPERTIES = new AnimationProperties() {
         private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
 
         @Override
@@ -373,4 +380,14 @@
             return mAnimationFilter;
         }
     }.setDuration(200);
+
+    private static final AnimationProperties ANIMATE_ALL_PROPERTIES = new AnimationProperties() {
+        private AnimationFilter mAnimationFilter = new AnimationFilter().animateX().animateY()
+                .animateAlpha().animateScale();
+
+        @Override
+        public AnimationFilter getAnimationFilter() {
+            return mAnimationFilter;
+        }
+    }.setDuration(200);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
index f9c2130..e5925f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.hardware.biometrics.BiometricSourceType;
 import android.content.Context;
 import android.os.Trace;
 
@@ -135,9 +136,9 @@
         }
 
         @Override
-        public void onFingerprintAuthenticated(int userId) {
-            Trace.beginSection("KeyguardUpdateMonitorCallback#onFingerprintAuthenticated");
-            if (!mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed()) {
+        public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+            Trace.beginSection("KeyguardUpdateMonitorCallback#onBiometricAuthenticated");
+            if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) {
                 Trace.endSection();
                 return;
             }
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 8df51db..1085b06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -239,11 +239,6 @@
     }
 
     @Override
-    public void onScanningStateChanged(boolean started) {
-        // Don't care.
-    }
-
-    @Override
     public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
         cachedDevice.registerCallback(this);
         updateConnected();
@@ -277,12 +272,6 @@
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
     }
 
-    @Override
-    public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
-
-    @Override
-    public void onAudioModeChanged() {}
-
     private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
         ActuallyCachedState state = mCachedState.get(device);
         if (state == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index e9bdc68..b198678 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.content.res.Resources;
 import android.util.ArraySet;
-import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.FrameLayout;
@@ -105,11 +104,9 @@
     }
 
     private void reinflate() {
-        ContextThemeWrapper qsThemeContext =
-                new ContextThemeWrapper(mBrightnessMirror.getContext(), R.style.qs_theme);
         int index = mStatusBarWindow.indexOfChild(mBrightnessMirror);
         mStatusBarWindow.removeView(mBrightnessMirror);
-        mBrightnessMirror = LayoutInflater.from(qsThemeContext).inflate(
+        mBrightnessMirror = LayoutInflater.from(mBrightnessMirror.getContext()).inflate(
                 R.layout.brightness_mirror, mStatusBarWindow, false);
         mStatusBarWindow.addView(mBrightnessMirror, index);
 
@@ -129,6 +126,10 @@
         mBrightnessMirrorListeners.remove(listener);
     }
 
+    public void onUiModeChanged() {
+        reinflate();
+    }
+
     public interface BrightnessMirrorListener {
         void onBrightnessMirrorReinflated(View brightnessMirror);
     }
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 9aa8044..8517d90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -25,6 +25,7 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Parcelable;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.Spannable;
@@ -65,6 +66,12 @@
         DarkReceiver, ConfigurationListener {
 
     public static final String CLOCK_SECONDS = "clock_seconds";
+    private static final String CLOCK_SUPER_PARCELABLE = "clock_super_parcelable";
+    private static final String CURRENT_USER_ID = "current_user_id";
+    private static final String VISIBLE_BY_POLICY = "visible_by_policy";
+    private static final String VISIBLE_BY_USER = "visible_by_user";
+    private static final String SHOW_SECONDS = "show_seconds";
+    private static final String VISIBILITY = "visibility";
 
     private final CurrentUserTracker mCurrentUserTracker;
     private int mCurrentUserId;
@@ -129,6 +136,40 @@
     }
 
     @Override
+    public Parcelable onSaveInstanceState() {
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(CLOCK_SUPER_PARCELABLE, super.onSaveInstanceState());
+        bundle.putInt(CURRENT_USER_ID, mCurrentUserId);
+        bundle.putBoolean(VISIBLE_BY_POLICY, mClockVisibleByPolicy);
+        bundle.putBoolean(VISIBLE_BY_USER, mClockVisibleByUser);
+        bundle.putBoolean(SHOW_SECONDS, mShowSeconds);
+        bundle.putInt(VISIBILITY, getVisibility());
+
+        return bundle;
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        if (state == null || !(state instanceof Bundle)) {
+            super.onRestoreInstanceState(state);
+            return;
+        }
+
+        Bundle bundle = (Bundle) state;
+        Parcelable superState = bundle.getParcelable(CLOCK_SUPER_PARCELABLE);
+        super.onRestoreInstanceState(superState);
+        if (bundle.containsKey(CURRENT_USER_ID)) {
+            mCurrentUserId = bundle.getInt(CURRENT_USER_ID);
+        }
+        mClockVisibleByPolicy = bundle.getBoolean(VISIBLE_BY_POLICY, true);
+        mClockVisibleByUser = bundle.getBoolean(VISIBLE_BY_USER, true);
+        mShowSeconds = bundle.getBoolean(SHOW_SECONDS, false);
+        if (bundle.containsKey(VISIBILITY)) {
+            setVisibility(bundle.getInt(VISIBILITY));
+        }
+    }
+
+    @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
index 3dca371..8c631d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
@@ -28,6 +28,7 @@
         default void onConfigChanged(Configuration newConfig) {}
         default void onDensityOrFontScaleChanged() {}
         default void onOverlayChanged() {}
+        default void onUiModeChanged() {}
         default void onLocaleListChanged() {}
     }
 }
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 aeda55a..677dd73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -31,8 +31,8 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -389,13 +389,23 @@
 
     /**
      * Unpins all pinned Heads Up Notifications.
+     * @param userUnPinned The unpinned action is trigger by user real operation.
      */
-    public void unpinAll() {
+    public void unpinAll(boolean userUnPinned) {
         for (String key : mHeadsUpEntries.keySet()) {
             HeadsUpEntry entry = mHeadsUpEntries.get(key);
             setEntryPinned(entry, false /* isPinned */);
             // maybe it got un sticky
             entry.updateEntry(false /* updatePostTime */);
+
+            // when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay
+            // on the screen.
+            if (userUnPinned && entry.entry != null && entry.entry.row != null) {
+                ExpandableNotificationRow row = entry.entry.row;
+                if (row.mustStayOnScreen()) {
+                    row.setHeadsUpIsVisible();
+                }
+            }
         }
     }
 
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 d6d0673..3c16329 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -134,6 +134,10 @@
 
     @Override
     public void setHotspotEnabled(boolean enabled) {
+        if (mWaitingForCallback) {
+            if (DEBUG) Log.d(TAG, "Ignoring setHotspotEnabled; waiting for callback.");
+            return;
+        }
         if (enabled) {
             OnStartTetheringCallback callback = new OnStartTetheringCallback();
             mWaitingForCallback = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
index 1a85c47..8e31f31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
@@ -22,6 +22,7 @@
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
+import android.util.FloatProperty;
 import android.view.Gravity;
 
 import com.android.systemui.R;
@@ -33,6 +34,32 @@
  */
 public class KeyButtonDrawable extends LayerDrawable {
 
+    public static final FloatProperty<KeyButtonDrawable> KEY_DRAWABLE_ROTATE =
+        new FloatProperty<KeyButtonDrawable>("KeyButtonRotation") {
+            @Override
+            public void setValue(KeyButtonDrawable drawable, float degree) {
+                drawable.setRotation(degree);
+            }
+
+            @Override
+            public Float get(KeyButtonDrawable drawable) {
+                return drawable.getRotation();
+            }
+        };
+
+    public static final FloatProperty<KeyButtonDrawable> KEY_DRAWABLE_TRANSLATE_Y =
+        new FloatProperty<KeyButtonDrawable>("KeyButtonTranslateY") {
+            @Override
+            public void setValue(KeyButtonDrawable drawable, float y) {
+                drawable.setTranslationY(y);
+            }
+
+            @Override
+            public Float get(KeyButtonDrawable drawable) {
+                return drawable.getTranslationY();
+            }
+        };
+
     private final boolean mHasDarkDrawable;
 
     public static KeyButtonDrawable create(Context lightContext, Drawable lightDrawable,
@@ -83,4 +110,33 @@
             ((ShadowKeyDrawable) getDrawable(1)).setRotation(degrees);
         }
     }
+
+    public void setTranslationY(float y) {
+        if (getDrawable(0) instanceof ShadowKeyDrawable) {
+            ((ShadowKeyDrawable) getDrawable(0)).setTranslationY(y);
+        }
+        if (mHasDarkDrawable && getDrawable(1) instanceof ShadowKeyDrawable) {
+            ((ShadowKeyDrawable) getDrawable(1)).setTranslationY(y);
+        }
+    }
+
+    public float getRotation() {
+        if (getDrawable(0) instanceof ShadowKeyDrawable) {
+            return ((ShadowKeyDrawable) getDrawable(0)).getRotation();
+        }
+        if (mHasDarkDrawable && getDrawable(1) instanceof ShadowKeyDrawable) {
+            return ((ShadowKeyDrawable) getDrawable(1)).getRotation();
+        }
+        return 0;
+    }
+
+    public float getTranslationY() {
+        if (getDrawable(0) instanceof ShadowKeyDrawable) {
+            return ((ShadowKeyDrawable) getDrawable(0)).getTranslationY();
+        }
+        if (mHasDarkDrawable && getDrawable(1) instanceof ShadowKeyDrawable) {
+            return ((ShadowKeyDrawable) getDrawable(1)).getTranslationY();
+        }
+        return 0;
+    }
 }
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 25261c0..8dbdd21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -16,6 +16,12 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -55,7 +61,6 @@
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 
-import com.android.systemui.statusbar.policy.MobileSignalController.MobileState;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -65,8 +70,6 @@
 import java.util.List;
 import java.util.Locale;
 
-import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
-
 /** Platform implementation of the network controller. **/
 public class NetworkControllerImpl extends BroadcastReceiver
         implements NetworkController, DemoMode, DataUsageController.NetworkNameProvider,
@@ -849,20 +852,20 @@
                 if (activity != null) {
                     switch (activity) {
                         case "inout":
-                            mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_INOUT);
+                            mWifiSignalController.setActivity(DATA_ACTIVITY_INOUT);
                             break;
                         case "in":
-                            mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_IN);
+                            mWifiSignalController.setActivity(DATA_ACTIVITY_IN);
                             break;
                         case "out":
-                            mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_OUT);
+                            mWifiSignalController.setActivity(DATA_ACTIVITY_OUT);
                             break;
                         default:
-                            mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_NONE);
+                            mWifiSignalController.setActivity(DATA_ACTIVITY_NONE);
                             break;
                     }
                 } else {
-                    mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_NONE);
+                    mWifiSignalController.setActivity(DATA_ACTIVITY_NONE);
                 }
                 String ssid = args.getString("ssid");
                 if (ssid != null) {
@@ -976,7 +979,7 @@
 
     private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
         SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
-                null, 0, 0, "");
+                null, null, null, "");
         MobileSignalController controller = new MobileSignalController(mContext,
                 mConfig, mHasMobileDataFeature, mPhone, mCallbackHandler, this, info,
                 mSubDefaults, mReceiverHandler.getLooper());
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 5444f06..5028fd1 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,8 @@
 
 package com.android.systemui.statusbar.policy;
 
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
 
 /**
  * A listener to heads up changes
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 b814478..52b813f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -39,7 +39,6 @@
 import android.view.View;
 import android.view.ViewAnimationUtils;
 import android.view.ViewGroup;
-import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.EditorInfo;
@@ -56,10 +55,10 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
 import java.util.function.Consumer;
 
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 cda9d04..c76a4b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -31,7 +31,7 @@
 import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
@@ -173,14 +173,14 @@
                 Math.max(getChildCount(), 1), DECREASING_MEASURED_WIDTH_WITHOUT_PADDING_COMPARATOR);
     }
 
-    public void setRepliesFromRemoteInput(RemoteInput remoteInput, PendingIntent pendingIntent,
+    public void setRepliesFromRemoteInput(
+            RemoteInput remoteInput, PendingIntent pendingIntent,
             SmartReplyController smartReplyController, NotificationData.Entry entry,
-            View smartReplyContainer) {
+            View smartReplyContainer, CharSequence[] choices) {
         mSmartReplyContainer = smartReplyContainer;
         removeAllViews();
         mCurrentBackgroundColor = mDefaultBackgroundColor;
         if (remoteInput != null && pendingIntent != null) {
-            CharSequence[] choices = remoteInput.getChoices();
             if (choices != null) {
                 for (int i = 0; i < choices.length; ++i) {
                     Button replyButton = inflateReplyButton(
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 cf80988..0233ad1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -15,21 +15,19 @@
  */
 package com.android.systemui.statusbar.policy;
 
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
+
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
 import android.net.NetworkScoreManager;
 import android.net.wifi.WifiManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
 import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
 import com.android.settingslib.wifi.WifiStatusTracker;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
@@ -37,10 +35,8 @@
 
 import java.util.Objects;
 
-
 public class WifiSignalController extends
         SignalController<WifiSignalController.WifiState, SignalController.IconGroup> {
-    private final AsyncChannel mWifiChannel;
     private final boolean mHasMobileData;
     private final WifiStatusTracker mWifiTracker;
 
@@ -57,12 +53,7 @@
                 connectivityManager, this::handleStatusUpdated);
         mWifiTracker.setListening(true);
         mHasMobileData = hasMobileData;
-        Handler handler = new WifiHandler(Looper.getMainLooper());
-        mWifiChannel = new AsyncChannel();
-        Messenger wifiMessenger = wifiManager.getWifiServiceMessenger();
-        if (wifiMessenger != null) {
-            mWifiChannel.connect(context, handler, wifiMessenger);
-        }
+        wifiManager.registerTrafficStateCallback(new WifiTrafficStateCallback(),  null);
         // WiFi only has one state.
         mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
                 "Wi-Fi Icons",
@@ -124,39 +115,20 @@
 
     @VisibleForTesting
     void setActivity(int wifiActivity) {
-        mCurrentState.activityIn = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
-                || wifiActivity == WifiManager.DATA_ACTIVITY_IN;
-        mCurrentState.activityOut = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
-                || wifiActivity == WifiManager.DATA_ACTIVITY_OUT;
+        mCurrentState.activityIn = wifiActivity == DATA_ACTIVITY_INOUT
+                || wifiActivity == DATA_ACTIVITY_IN;
+        mCurrentState.activityOut = wifiActivity == DATA_ACTIVITY_INOUT
+                || wifiActivity == DATA_ACTIVITY_OUT;
         notifyListenersIfNecessary();
     }
 
     /**
      * Handler to receive the data activity on wifi.
      */
-    private class WifiHandler extends Handler {
-        WifiHandler(Looper looper) {
-            super(looper);
-        }
-
+    private class WifiTrafficStateCallback implements WifiManager.TrafficStateCallback {
         @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
-                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                        mWifiChannel.sendMessage(Message.obtain(this,
-                                AsyncChannel.CMD_CHANNEL_FULL_CONNECTION));
-                    } else {
-                        Log.e(mTag, "Failed to connect to wifi");
-                    }
-                    break;
-                case WifiManager.DATA_ACTIVITY_NOTIFICATION:
-                    setActivity(msg.arg1);
-                    break;
-                default:
-                    // Ignore
-                    break;
-            }
+        public void onStateChanged(int state) {
+            setActivity(state);
         }
     }
 
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 2031b27..ef51bf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -32,32 +32,33 @@
 import android.os.UserManager;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
-import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ZenRule;
+import android.text.format.DateFormat;
 import android.util.Log;
-import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dumpable;
 import com.android.systemui.qs.GlobalSetting;
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.util.Utils;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.LinkedHashMap;
 import java.util.Objects;
 
 /** Platform implementation of the zen mode controller. **/
-public class ZenModeControllerImpl extends CurrentUserTracker implements ZenModeController {
+public class ZenModeControllerImpl extends CurrentUserTracker
+        implements ZenModeController, Dumpable {
     private static final String TAG = "ZenModeController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+    private final ArrayList<Callback> mCallbacks = new ArrayList<>();
     private final Context mContext;
     private final GlobalSetting mModeSetting;
     private final GlobalSetting mConfigSetting;
     private final NotificationManager mNoMan;
-    private final LinkedHashMap<Uri, Condition> mConditions = new LinkedHashMap<Uri, Condition>();
     private final AlarmManager mAlarmManager;
     private final SetupObserver mSetupObserver;
     private final UserManager mUserManager;
@@ -66,6 +67,7 @@
     private boolean mRegistered;
     private ZenModeConfig mConfig;
     private int mZenMode;
+    private long mZenUpdateTime;
 
     public ZenModeControllerImpl(Context context, Handler handler) {
         super(context);
@@ -84,7 +86,6 @@
             }
         };
         mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-        mConfig = mNoMan.getZenModeConfig();
         mModeSetting.setListening(true);
         updateZenMode(mModeSetting.getValue());
         mConfigSetting.setListening(true);
@@ -113,10 +114,6 @@
 
     @Override
     public void addCallback(Callback callback) {
-        if (callback == null) {
-            Slog.e(TAG, "Attempted to add a null callback.");
-            return;
-        }
         mCallbacks.add(callback);
     }
 
@@ -213,6 +210,7 @@
     @VisibleForTesting
     protected void updateZenMode(int mode) {
         mZenMode = mode;
+        mZenUpdateTime = System.currentTimeMillis();
     }
 
     @VisibleForTesting
@@ -221,6 +219,7 @@
         if (Objects.equals(config, mConfig)) return;
         final ZenRule oldRule = mConfig != null ? mConfig.manualRule : null;
         mConfig = config;
+        mZenUpdateTime = System.currentTimeMillis();
         fireConfigChanged(config);
         final ZenRule newRule = config != null ? config.manualRule : null;
         if (Objects.equals(oldRule, newRule)) return;
@@ -239,6 +238,14 @@
         }
     };
 
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("ZenModeControllerImpl:");
+        pw.println("  mZenMode=" + mZenMode);
+        pw.println("  mConfig=" + mConfig);
+        pw.println("  mZenUpdateTime=" + DateFormat.format("MM-dd HH:mm:ss", mZenUpdateTime));
+    }
+
     private final class SetupObserver extends ContentObserver {
         private final ContentResolver mResolver;
 
@@ -265,6 +272,7 @@
                     Global.getUriFor(Global.DEVICE_PROVISIONED), false, this);
             mResolver.registerContentObserver(
                     Secure.getUriFor(Secure.USER_SETUP_COMPLETE), false, this, mUserId);
+            mRegistered = true;
             fireZenAvailableChanged(isZenAvailable());
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
deleted file mode 100644
index db3f0d9..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ /dev/null
@@ -1,447 +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
- */
-
-package com.android.systemui.statusbar.stack;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.ActivatableNotificationView;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-
-import java.util.ArrayList;
-
-/**
- * A global state to track all input states for the algorithm.
- */
-public class AmbientState {
-    private ArrayList<View> mDraggedViews = new ArrayList<View>();
-    private int mScrollY;
-    private boolean mDimmed;
-    private ActivatableNotificationView mActivatedChild;
-    private float mOverScrollTopAmount;
-    private float mOverScrollBottomAmount;
-    private int mSpeedBumpIndex = -1;
-    private boolean mDark;
-    private boolean mHideSensitive;
-    private HeadsUpManager mHeadsUpManager;
-    private float mStackTranslation;
-    private int mLayoutHeight;
-    private int mTopPadding;
-    private boolean mShadeExpanded;
-    private float mMaxHeadsUpTranslation;
-    private boolean mDismissAllInProgress;
-    private int mLayoutMinHeight;
-    private NotificationShelf mShelf;
-    private int mZDistanceBetweenElements;
-    private int mBaseZHeight;
-    private int mMaxLayoutHeight;
-    private ActivatableNotificationView mLastVisibleBackgroundChild;
-    private float mCurrentScrollVelocity;
-    private int mStatusBarState;
-    private float mExpandingVelocity;
-    private boolean mPanelTracking;
-    private boolean mExpansionChanging;
-    private boolean mPanelFullWidth;
-    private boolean mPulsing;
-    private boolean mUnlockHintRunning;
-    private boolean mQsCustomizerShowing;
-    private int mIntrinsicPadding;
-    private int mExpandAnimationTopChange;
-    private ExpandableNotificationRow mExpandingNotification;
-    private int mDarkTopPadding;
-    private float mDarkAmount;
-    private boolean mAppearing;
-
-    public AmbientState(Context context) {
-        reload(context);
-    }
-
-    /**
-     * Reload the dimens e.g. if the density changed.
-     */
-    public void reload(Context context) {
-        mZDistanceBetweenElements = getZDistanceBetweenElements(context);
-        mBaseZHeight = getBaseHeight(mZDistanceBetweenElements);
-    }
-
-    private static int getZDistanceBetweenElements(Context context) {
-        return Math.max(1, context.getResources()
-                .getDimensionPixelSize(R.dimen.z_distance_between_notifications));
-    }
-
-    private static int getBaseHeight(int zdistanceBetweenElements) {
-        return 4 * zdistanceBetweenElements;
-    }
-
-    /**
-     * @return the launch height for notifications that are launched
-     */
-    public static int getNotificationLaunchHeight(Context context) {
-        int zDistance = getZDistanceBetweenElements(context);
-        return getBaseHeight(zDistance) * 2;
-    }
-
-    /**
-     * @return the basic Z height on which notifications remain.
-     */
-    public int getBaseZHeight() {
-        return mBaseZHeight;
-    }
-
-    /**
-     * @return the distance in Z between two overlaying notifications.
-     */
-    public int getZDistanceBetweenElements() {
-        return mZDistanceBetweenElements;
-    }
-
-    public int getScrollY() {
-        return mScrollY;
-    }
-
-    public void setScrollY(int scrollY) {
-        this.mScrollY = scrollY;
-    }
-
-    public void onBeginDrag(View view) {
-        mDraggedViews.add(view);
-    }
-
-    public void onDragFinished(View view) {
-        mDraggedViews.remove(view);
-    }
-
-    public ArrayList<View> getDraggedViews() {
-        return mDraggedViews;
-    }
-
-    /**
-     * @param dimmed Whether we are in a dimmed state (on the lockscreen), where the backgrounds are
-     *               translucent and everything is scaled back a bit.
-     */
-    public void setDimmed(boolean dimmed) {
-        mDimmed = dimmed;
-    }
-
-    /** In dark mode, we draw as little as possible, assuming a black background */
-    public void setDark(boolean dark) {
-        mDark = dark;
-    }
-
-    /** Dark ratio of the status bar **/
-    public void setDarkAmount(float darkAmount) {
-        mDarkAmount = darkAmount;
-    }
-
-    /** Returns the dark ratio of the status bar */
-    public float getDarkAmount() {
-        return mDarkAmount;
-    }
-
-    public void setHideSensitive(boolean hideSensitive) {
-        mHideSensitive = hideSensitive;
-    }
-
-    /**
-     * In dimmed mode, a child can be activated, which happens on the first tap of the double-tap
-     * interaction. This child is then scaled normally and its background is fully opaque.
-     */
-    public void setActivatedChild(ActivatableNotificationView activatedChild) {
-        mActivatedChild = activatedChild;
-    }
-
-    public boolean isDimmed() {
-        return mDimmed;
-    }
-
-    public boolean isDark() {
-        return mDark;
-    }
-
-    public boolean isHideSensitive() {
-        return mHideSensitive;
-    }
-
-    public ActivatableNotificationView getActivatedChild() {
-        return mActivatedChild;
-    }
-
-    public void setOverScrollAmount(float amount, boolean onTop) {
-        if (onTop) {
-            mOverScrollTopAmount = amount;
-        } else {
-            mOverScrollBottomAmount = amount;
-        }
-    }
-
-    public float getOverScrollAmount(boolean top) {
-        return top ? mOverScrollTopAmount : mOverScrollBottomAmount;
-    }
-
-    public int getSpeedBumpIndex() {
-        return mSpeedBumpIndex;
-    }
-
-    public void setSpeedBumpIndex(int shelfIndex) {
-        mSpeedBumpIndex = shelfIndex;
-    }
-
-    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
-        mHeadsUpManager = headsUpManager;
-    }
-
-    public float getStackTranslation() {
-        return mStackTranslation;
-    }
-
-    public void setStackTranslation(float stackTranslation) {
-        mStackTranslation = stackTranslation;
-    }
-
-    public void setLayoutHeight(int layoutHeight) {
-        mLayoutHeight = layoutHeight;
-    }
-
-    public float getTopPadding() {
-        return mTopPadding;
-    }
-
-    public void setTopPadding(int topPadding) {
-        mTopPadding = topPadding;
-    }
-
-    public int getInnerHeight() {
-        return Math.max(Math.min(mLayoutHeight, mMaxLayoutHeight) - mTopPadding, mLayoutMinHeight);
-    }
-
-    public boolean isShadeExpanded() {
-        return mShadeExpanded;
-    }
-
-    public void setShadeExpanded(boolean shadeExpanded) {
-        mShadeExpanded = shadeExpanded;
-    }
-
-    public void setMaxHeadsUpTranslation(float maxHeadsUpTranslation) {
-        mMaxHeadsUpTranslation = maxHeadsUpTranslation;
-    }
-
-    public float getMaxHeadsUpTranslation() {
-        return mMaxHeadsUpTranslation;
-    }
-
-    public void setDismissAllInProgress(boolean dismissAllInProgress) {
-        mDismissAllInProgress = dismissAllInProgress;
-    }
-
-    public boolean isDismissAllInProgress() {
-        return mDismissAllInProgress;
-    }
-
-    public void setLayoutMinHeight(int layoutMinHeight) {
-        mLayoutMinHeight = layoutMinHeight;
-    }
-
-    public void setShelf(NotificationShelf shelf) {
-        mShelf = shelf;
-    }
-
-    @Nullable
-    public NotificationShelf getShelf() {
-        return mShelf;
-    }
-
-    public void setLayoutMaxHeight(int maxLayoutHeight) {
-        mMaxLayoutHeight = maxLayoutHeight;
-    }
-
-    /**
-     * Sets the last visible view of the host layout, that has a background, i.e the very last
-     * view in the shade, without the clear all button.
-     */
-    public void setLastVisibleBackgroundChild(
-            ActivatableNotificationView lastVisibleBackgroundChild) {
-        mLastVisibleBackgroundChild = lastVisibleBackgroundChild;
-    }
-
-    public ActivatableNotificationView getLastVisibleBackgroundChild() {
-        return mLastVisibleBackgroundChild;
-    }
-
-    public void setCurrentScrollVelocity(float currentScrollVelocity) {
-        mCurrentScrollVelocity = currentScrollVelocity;
-    }
-
-    public float getCurrentScrollVelocity() {
-        return mCurrentScrollVelocity;
-    }
-
-    public boolean isOnKeyguard() {
-        return mStatusBarState == StatusBarState.KEYGUARD;
-    }
-
-    public void setStatusBarState(int statusBarState) {
-        mStatusBarState = statusBarState;
-    }
-
-    public void setExpandingVelocity(float expandingVelocity) {
-        mExpandingVelocity = expandingVelocity;
-    }
-
-    public void setExpansionChanging(boolean expansionChanging) {
-        mExpansionChanging = expansionChanging;
-    }
-
-    public boolean isExpansionChanging() {
-        return mExpansionChanging;
-    }
-
-    public float getExpandingVelocity() {
-        return mExpandingVelocity;
-    }
-
-    public void setPanelTracking(boolean panelTracking) {
-        mPanelTracking = panelTracking;
-    }
-
-    public boolean hasPulsingNotifications() {
-        return mPulsing;
-    }
-
-    public void setPulsing(boolean hasPulsing) {
-        mPulsing = hasPulsing;
-    }
-
-    public boolean isPulsing(NotificationData.Entry entry) {
-        if (!mPulsing || mHeadsUpManager == null) {
-            return false;
-        }
-        return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry));
-    }
-
-    public boolean isPanelTracking() {
-        return mPanelTracking;
-    }
-
-    public boolean isPanelFullWidth() {
-        return mPanelFullWidth;
-    }
-
-    public void setPanelFullWidth(boolean panelFullWidth) {
-        mPanelFullWidth = panelFullWidth;
-    }
-
-    public void setUnlockHintRunning(boolean unlockHintRunning) {
-        mUnlockHintRunning = unlockHintRunning;
-    }
-
-    public boolean isUnlockHintRunning() {
-        return mUnlockHintRunning;
-    }
-
-    public boolean isQsCustomizerShowing() {
-        return mQsCustomizerShowing;
-    }
-
-    public void setQsCustomizerShowing(boolean qsCustomizerShowing) {
-        mQsCustomizerShowing = qsCustomizerShowing;
-    }
-
-    public void setIntrinsicPadding(int intrinsicPadding) {
-        mIntrinsicPadding = intrinsicPadding;
-    }
-
-    public int getIntrinsicPadding() {
-        return mIntrinsicPadding;
-    }
-
-    /**
-     * Similar to the normal is above shelf logic but doesn't allow it to be above in AOD1.
-     *
-     * @param expandableView the view to check
-     */
-    public boolean isAboveShelf(ExpandableView expandableView) {
-        if (!(expandableView instanceof ExpandableNotificationRow)) {
-            return expandableView.isAboveShelf();
-        }
-        ExpandableNotificationRow row = (ExpandableNotificationRow) expandableView;
-        return row.isAboveShelf() && !isDozingAndNotPulsing(row);
-    }
-
-    /**
-     * @return whether a view is dozing and not pulsing right now
-     */
-    public boolean isDozingAndNotPulsing(ExpandableView view) {
-        if (view instanceof ExpandableNotificationRow) {
-            return isDozingAndNotPulsing((ExpandableNotificationRow) view);
-        }
-        return false;
-    }
-
-    /**
-     * @return whether a row is dozing and not pulsing right now
-     */
-    public boolean isDozingAndNotPulsing(ExpandableNotificationRow row) {
-        return isDark() && !isPulsing(row.getEntry());
-    }
-
-    public void setExpandAnimationTopChange(int expandAnimationTopChange) {
-        mExpandAnimationTopChange = expandAnimationTopChange;
-    }
-
-    public void setExpandingNotification(ExpandableNotificationRow row) {
-        mExpandingNotification = row;
-    }
-
-    public ExpandableNotificationRow getExpandingNotification() {
-        return mExpandingNotification;
-    }
-
-    public int getExpandAnimationTopChange() {
-        return mExpandAnimationTopChange;
-    }
-
-    /**
-     * @return {@code true } when shade is completely dark: in AOD or ambient display.
-     */
-    public boolean isFullyDark() {
-        return mDarkAmount == 1;
-    }
-
-    public void setDarkTopPadding(int darkTopPadding) {
-        mDarkTopPadding = darkTopPadding;
-    }
-
-    public int getDarkTopPadding() {
-        return mDarkTopPadding;
-    }
-
-    public void setAppearing(boolean appearing) {
-        mAppearing = appearing;
-    }
-
-    public boolean isAppearing() {
-        return mAppearing;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
deleted file mode 100644
index a8464b3..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ /dev/null
@@ -1,187 +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
- */
-
-package com.android.systemui.statusbar.stack;
-
-import androidx.collection.ArraySet;
-import android.util.Property;
-import android.view.View;
-
-import java.util.ArrayList;
-
-/**
- * Filters the animations for only a certain type of properties.
- */
-public class AnimationFilter {
-    public static final int NO_DELAY = -1;
-    boolean animateAlpha;
-    boolean animateX;
-    boolean animateY;
-    ArraySet<View> animateYViews = new ArraySet<>();
-    boolean animateZ;
-    boolean animateHeight;
-    boolean animateTopInset;
-    boolean animateDimmed;
-    boolean animateDark;
-    boolean animateHideSensitive;
-    public boolean animateShadowAlpha;
-    boolean hasDelays;
-    boolean hasGoToFullShadeEvent;
-    long customDelay;
-    private ArraySet<Property> mAnimatedProperties = new ArraySet<>();
-
-    public AnimationFilter animateAlpha() {
-        animateAlpha = true;
-        return this;
-    }
-
-    public AnimationFilter animateScale() {
-        animate(View.SCALE_X);
-        animate(View.SCALE_Y);
-        return this;
-    }
-
-    public AnimationFilter animateX() {
-        animateX = true;
-        return this;
-    }
-
-    public AnimationFilter animateY() {
-        animateY = true;
-        return this;
-    }
-
-    public AnimationFilter hasDelays() {
-        hasDelays = true;
-        return this;
-    }
-
-    public AnimationFilter animateZ() {
-        animateZ = true;
-        return this;
-    }
-
-    public AnimationFilter animateHeight() {
-        animateHeight = true;
-        return this;
-    }
-
-    public AnimationFilter animateTopInset() {
-        animateTopInset = true;
-        return this;
-    }
-
-    public AnimationFilter animateDimmed() {
-        animateDimmed = true;
-        return this;
-    }
-
-    public AnimationFilter animateDark() {
-        animateDark = true;
-        return this;
-    }
-
-    public AnimationFilter animateHideSensitive() {
-        animateHideSensitive = true;
-        return this;
-    }
-
-    public AnimationFilter animateShadowAlpha() {
-        animateShadowAlpha = true;
-        return this;
-    }
-
-    public AnimationFilter animateY(View view) {
-        animateYViews.add(view);
-        return this;
-    }
-
-    public boolean shouldAnimateY(View view) {
-        return animateY || animateYViews.contains(view);
-    }
-
-    /**
-     * Combines multiple filters into {@code this} filter, using or as the operand .
-     *
-     * @param events The animation events from the filters to combine.
-     */
-    public void applyCombination(ArrayList<NotificationStackScrollLayout.AnimationEvent> events) {
-        reset();
-        int size = events.size();
-        for (int i = 0; i < size; i++) {
-            NotificationStackScrollLayout.AnimationEvent ev = events.get(i);
-            combineFilter(events.get(i).filter);
-            if (ev.animationType ==
-                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE) {
-                hasGoToFullShadeEvent = true;
-            }
-            if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
-                    .ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
-                customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP;
-            } else if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
-                    .ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
-                // We need both timeouts when clicking, one to delay it and one for the animation
-                // to look nice
-                customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP_CLICKED
-                        + StackStateAnimator.ANIMATION_DELAY_HEADS_UP;
-            }
-        }
-    }
-
-    public void combineFilter(AnimationFilter filter) {
-        animateAlpha |= filter.animateAlpha;
-        animateX |= filter.animateX;
-        animateY |= filter.animateY;
-        animateYViews.addAll(filter.animateYViews);
-        animateZ |= filter.animateZ;
-        animateHeight |= filter.animateHeight;
-        animateTopInset |= filter.animateTopInset;
-        animateDimmed |= filter.animateDimmed;
-        animateDark |= filter.animateDark;
-        animateHideSensitive |= filter.animateHideSensitive;
-        animateShadowAlpha |= filter.animateShadowAlpha;
-        hasDelays |= filter.hasDelays;
-        mAnimatedProperties.addAll(filter.mAnimatedProperties);
-    }
-
-    public void reset() {
-        animateAlpha = false;
-        animateX = false;
-        animateY = false;
-        animateYViews.clear();
-        animateZ = false;
-        animateHeight = false;
-        animateShadowAlpha = false;
-        animateTopInset = false;
-        animateDimmed = false;
-        animateDark = false;
-        animateHideSensitive = false;
-        hasDelays = false;
-        hasGoToFullShadeEvent = false;
-        customDelay = NO_DELAY;
-        mAnimatedProperties.clear();
-    }
-
-    public AnimationFilter animate(Property property) {
-        mAnimatedProperties.add(property);
-        return this;
-    }
-
-    public boolean shouldAnimateProperty(Property property) {
-        // TODO: migrate all existing animators to properties
-        return mAnimatedProperties.contains(property);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java
deleted file mode 100644
index 47df226..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.stack;
-
-import android.animation.AnimatorListenerAdapter;
-import android.util.ArrayMap;
-import android.util.Property;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-import java.util.HashMap;
-
-/**
- * Properties for a View animation
- */
-public class AnimationProperties {
-    public long duration;
-    public long delay;
-    private ArrayMap<Property, Interpolator> mInterpolatorMap;
-    private AnimatorListenerAdapter mAnimatorListenerAdapter;
-
-    /**
-     * @return an animation filter for this animation.
-     */
-    public AnimationFilter getAnimationFilter() {
-        return new AnimationFilter() {
-            @Override
-            public boolean shouldAnimateProperty(Property property) {
-                return true;
-            }
-        };
-    }
-
-    /**
-     * @return a listener that should be run whenever any property finished its animation
-     */
-    public AnimatorListenerAdapter getAnimationFinishListener() {
-        return mAnimatorListenerAdapter;
-    }
-
-    public AnimationProperties setAnimationFinishListener(AnimatorListenerAdapter listener) {
-        mAnimatorListenerAdapter = listener;
-        return this;
-    }
-
-    public boolean wasAdded(View view) {
-        return false;
-    }
-
-    /**
-     * Get a custom interpolator for a property instead of the normal one.
-     */
-    public Interpolator getCustomInterpolator(View child, Property property) {
-        return mInterpolatorMap != null ? mInterpolatorMap.get(property) : null;
-    }
-
-
-    public void combineCustomInterpolators(AnimationProperties iconAnimationProperties) {
-        ArrayMap<Property, Interpolator> map = iconAnimationProperties.mInterpolatorMap;
-        if (map != null) {
-            if (mInterpolatorMap == null) {
-                mInterpolatorMap = new ArrayMap<>();
-            }
-            mInterpolatorMap.putAll(map);
-        }
-    }
-
-    /**
-     * Set a custom interpolator to use for all views for a property.
-     */
-    public AnimationProperties setCustomInterpolator(Property property, Interpolator interpolator) {
-        if (mInterpolatorMap == null) {
-            mInterpolatorMap = new ArrayMap<>();
-        }
-        mInterpolatorMap.put(property, interpolator);
-        return this;
-    }
-
-    public AnimationProperties setDuration(long duration) {
-        this.duration = duration;
-        return this;
-    }
-
-    public AnimationProperties setDelay(long delay) {
-        this.delay = delay;
-        return this;
-    }
-
-    public AnimationProperties resetCustomInterpolators() {
-        mInterpolatorMap = null;
-        return this;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
deleted file mode 100644
index a7925aa..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
+++ /dev/null
@@ -1,488 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.stack;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.PropertyValuesHolder;
-import android.animation.ValueAnimator;
-import android.view.View;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
-
-/**
-* A state of an expandable view
-*/
-public class ExpandableViewState extends ViewState {
-
-    private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
-    private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
-    private static final int TAG_ANIMATOR_SHADOW_ALPHA = R.id.shadow_alpha_animator_tag;
-    private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
-    private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
-    private static final int TAG_END_SHADOW_ALPHA = R.id.shadow_alpha_animator_end_value_tag;
-    private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag;
-    private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
-    private static final int TAG_START_SHADOW_ALPHA = R.id.shadow_alpha_animator_start_value_tag;
-
-    // These are flags such that we can create masks for filtering.
-
-    /**
-     * No known location. This is the default and should not be set after an invocation of the
-     * algorithm.
-     */
-    public static final int LOCATION_UNKNOWN = 0x00;
-
-    /**
-     * The location is the first heads up notification, so on the very top.
-     */
-    public static final int LOCATION_FIRST_HUN = 0x01;
-
-    /**
-     * The location is hidden / scrolled away on the top.
-     */
-    public static final int LOCATION_HIDDEN_TOP = 0x02;
-
-    /**
-     * The location is in the main area of the screen and visible.
-     */
-    public static final int LOCATION_MAIN_AREA = 0x04;
-
-    /**
-     * The location is in the bottom stack and it's peeking
-     */
-    public static final int LOCATION_BOTTOM_STACK_PEEKING = 0x08;
-
-    /**
-     * The location is in the bottom stack and it's hidden.
-     */
-    public static final int LOCATION_BOTTOM_STACK_HIDDEN = 0x10;
-
-    /**
-     * The view isn't laid out at all.
-     */
-    public static final int LOCATION_GONE = 0x40;
-
-    /**
-     * The visible locations of a view.
-     */
-    public static final int VISIBLE_LOCATIONS = ExpandableViewState.LOCATION_FIRST_HUN
-            | ExpandableViewState.LOCATION_MAIN_AREA;
-
-    public int height;
-    public boolean dimmed;
-    public boolean dark;
-    public boolean hideSensitive;
-    public boolean belowSpeedBump;
-    public float shadowAlpha;
-    public boolean inShelf;
-
-    /**
-     * A state indicating whether a headsup is currently fully visible, even when not scrolled.
-     * Only valid if the view is heads upped.
-     */
-    public boolean headsUpIsVisible;
-
-    /**
-     * How much the child overlaps with the previous child on top. This is used to
-     * show the background properly when the child on top is translating away.
-     */
-    public int clipTopAmount;
-
-    /**
-     * The index of the view, only accounting for views not equal to GONE
-     */
-    public int notGoneIndex;
-
-    /**
-     * The location this view is currently rendered at.
-     *
-     * <p>See <code>LOCATION_</code> flags.</p>
-     */
-    public int location;
-
-    @Override
-    public void copyFrom(ViewState viewState) {
-        super.copyFrom(viewState);
-        if (viewState instanceof ExpandableViewState) {
-            ExpandableViewState svs = (ExpandableViewState) viewState;
-            height = svs.height;
-            dimmed = svs.dimmed;
-            shadowAlpha = svs.shadowAlpha;
-            dark = svs.dark;
-            hideSensitive = svs.hideSensitive;
-            belowSpeedBump = svs.belowSpeedBump;
-            clipTopAmount = svs.clipTopAmount;
-            notGoneIndex = svs.notGoneIndex;
-            location = svs.location;
-            headsUpIsVisible = svs.headsUpIsVisible;
-        }
-    }
-
-    /**
-     * Applies a {@link ExpandableViewState} to a {@link ExpandableView}.
-     */
-    @Override
-    public void applyToView(View view) {
-        super.applyToView(view);
-        if (view instanceof ExpandableView) {
-            ExpandableView expandableView = (ExpandableView) view;
-
-            int height = expandableView.getActualHeight();
-            int newHeight = this.height;
-
-            // apply height
-            if (height != newHeight) {
-                expandableView.setActualHeight(newHeight, false /* notifyListeners */);
-            }
-
-            float shadowAlpha = expandableView.getShadowAlpha();
-            float newShadowAlpha = this.shadowAlpha;
-
-            // apply shadowAlpha
-            if (shadowAlpha != newShadowAlpha) {
-                expandableView.setShadowAlpha(newShadowAlpha);
-            }
-
-            // apply dimming
-            expandableView.setDimmed(this.dimmed, false /* animate */);
-
-            // apply hiding sensitive
-            expandableView.setHideSensitive(
-                    this.hideSensitive, false /* animated */, 0 /* delay */, 0 /* duration */);
-
-            // apply below shelf speed bump
-            expandableView.setBelowSpeedBump(this.belowSpeedBump);
-
-            // apply dark
-            expandableView.setDark(this.dark, false /* animate */, 0 /* delay */);
-
-            // apply clipping
-            float oldClipTopAmount = expandableView.getClipTopAmount();
-            if (oldClipTopAmount != this.clipTopAmount) {
-                expandableView.setClipTopAmount(this.clipTopAmount);
-            }
-
-            expandableView.setTransformingInShelf(false);
-            expandableView.setInShelf(inShelf);
-
-            if (headsUpIsVisible) {
-                expandableView.setHeadsUpIsVisible();
-            }
-        }
-    }
-
-    @Override
-    public void animateTo(View child, AnimationProperties properties) {
-        super.animateTo(child, properties);
-        if (!(child instanceof ExpandableView)) {
-            return;
-        }
-        ExpandableView expandableView = (ExpandableView) child;
-        AnimationFilter animationFilter = properties.getAnimationFilter();
-
-        // start height animation
-        if (this.height != expandableView.getActualHeight()) {
-            startHeightAnimation(expandableView, properties);
-        }  else {
-            abortAnimation(child, TAG_ANIMATOR_HEIGHT);
-        }
-
-        // start shadow alpha animation
-        if (this.shadowAlpha != expandableView.getShadowAlpha()) {
-            startShadowAlphaAnimation(expandableView, properties);
-        } else {
-            abortAnimation(child, TAG_ANIMATOR_SHADOW_ALPHA);
-        }
-
-        // start top inset animation
-        if (this.clipTopAmount != expandableView.getClipTopAmount()) {
-            startInsetAnimation(expandableView, properties);
-        } else {
-            abortAnimation(child, TAG_ANIMATOR_TOP_INSET);
-        }
-
-        // start dimmed animation
-        expandableView.setDimmed(this.dimmed, animationFilter.animateDimmed);
-
-        // apply below the speed bump
-        expandableView.setBelowSpeedBump(this.belowSpeedBump);
-
-        // start hiding sensitive animation
-        expandableView.setHideSensitive(this.hideSensitive, animationFilter.animateHideSensitive,
-                properties.delay, properties.duration);
-
-        // start dark animation
-        expandableView.setDark(this.dark, animationFilter.animateDark, properties.delay);
-
-        if (properties.wasAdded(child) && !hidden) {
-            expandableView.performAddAnimation(properties.delay, properties.duration,
-                    false /* isHeadsUpAppear */);
-        }
-
-        if (!expandableView.isInShelf() && this.inShelf) {
-            expandableView.setTransformingInShelf(true);
-        }
-        expandableView.setInShelf(this.inShelf);
-
-        if (headsUpIsVisible) {
-            expandableView.setHeadsUpIsVisible();
-        }
-    }
-
-    private void startHeightAnimation(final ExpandableView child, AnimationProperties properties) {
-        Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
-        Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
-        int newEndValue = this.height;
-        if (previousEndValue != null && previousEndValue == newEndValue) {
-            return;
-        }
-        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT);
-        AnimationFilter filter = properties.getAnimationFilter();
-        if (!filter.animateHeight) {
-            // just a local update was performed
-            if (previousAnimator != null) {
-                // we need to increase all animation keyframes of the previous animator by the
-                // relative change to the end value
-                PropertyValuesHolder[] values = previousAnimator.getValues();
-                int relativeDiff = newEndValue - previousEndValue;
-                int newStartValue = previousStartValue + relativeDiff;
-                values[0].setIntValues(newStartValue, newEndValue);
-                child.setTag(TAG_START_HEIGHT, newStartValue);
-                child.setTag(TAG_END_HEIGHT, newEndValue);
-                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
-                return;
-            } else {
-                // no new animation needed, let's just apply the value
-                child.setActualHeight(newEndValue, false);
-                return;
-            }
-        }
-
-        ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue);
-        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                child.setActualHeight((int) animation.getAnimatedValue(),
-                        false /* notifyListeners */);
-            }
-        });
-        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
-        animator.setDuration(newDuration);
-        if (properties.delay > 0 && (previousAnimator == null
-                || previousAnimator.getAnimatedFraction() == 0)) {
-            animator.setStartDelay(properties.delay);
-        }
-        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
-        if (listener != null) {
-            animator.addListener(listener);
-        }
-        // remove the tag when the animation is finished
-        animator.addListener(new AnimatorListenerAdapter() {
-            boolean mWasCancelled;
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                child.setTag(TAG_ANIMATOR_HEIGHT, null);
-                child.setTag(TAG_START_HEIGHT, null);
-                child.setTag(TAG_END_HEIGHT, null);
-                child.setActualHeightAnimating(false);
-                if (!mWasCancelled && child instanceof ExpandableNotificationRow) {
-                    ((ExpandableNotificationRow) child).setGroupExpansionChanging(
-                            false /* isExpansionChanging */);
-                }
-            }
-
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mWasCancelled = false;
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mWasCancelled = true;
-            }
-        });
-        startAnimator(animator, listener);
-        child.setTag(TAG_ANIMATOR_HEIGHT, animator);
-        child.setTag(TAG_START_HEIGHT, child.getActualHeight());
-        child.setTag(TAG_END_HEIGHT, newEndValue);
-        child.setActualHeightAnimating(true);
-    }
-
-    private void startShadowAlphaAnimation(final ExpandableView child,
-            AnimationProperties properties) {
-        Float previousStartValue = getChildTag(child, TAG_START_SHADOW_ALPHA);
-        Float previousEndValue = getChildTag(child, TAG_END_SHADOW_ALPHA);
-        float newEndValue = this.shadowAlpha;
-        if (previousEndValue != null && previousEndValue == newEndValue) {
-            return;
-        }
-        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SHADOW_ALPHA);
-        AnimationFilter filter = properties.getAnimationFilter();
-        if (!filter.animateShadowAlpha) {
-            // just a local update was performed
-            if (previousAnimator != null) {
-                // we need to increase all animation keyframes of the previous animator by the
-                // relative change to the end value
-                PropertyValuesHolder[] values = previousAnimator.getValues();
-                float relativeDiff = newEndValue - previousEndValue;
-                float newStartValue = previousStartValue + relativeDiff;
-                values[0].setFloatValues(newStartValue, newEndValue);
-                child.setTag(TAG_START_SHADOW_ALPHA, newStartValue);
-                child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
-                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
-                return;
-            } else {
-                // no new animation needed, let's just apply the value
-                child.setShadowAlpha(newEndValue);
-                return;
-            }
-        }
-
-        ValueAnimator animator = ValueAnimator.ofFloat(child.getShadowAlpha(), newEndValue);
-        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                child.setShadowAlpha((float) animation.getAnimatedValue());
-            }
-        });
-        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
-        animator.setDuration(newDuration);
-        if (properties.delay > 0 && (previousAnimator == null
-                || previousAnimator.getAnimatedFraction() == 0)) {
-            animator.setStartDelay(properties.delay);
-        }
-        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
-        if (listener != null) {
-            animator.addListener(listener);
-        }
-        // remove the tag when the animation is finished
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, null);
-                child.setTag(TAG_START_SHADOW_ALPHA, null);
-                child.setTag(TAG_END_SHADOW_ALPHA, null);
-            }
-        });
-        startAnimator(animator, listener);
-        child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, animator);
-        child.setTag(TAG_START_SHADOW_ALPHA, child.getShadowAlpha());
-        child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
-    }
-
-    private void startInsetAnimation(final ExpandableView child, AnimationProperties properties) {
-        Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET);
-        Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET);
-        int newEndValue = this.clipTopAmount;
-        if (previousEndValue != null && previousEndValue == newEndValue) {
-            return;
-        }
-        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET);
-        AnimationFilter filter = properties.getAnimationFilter();
-        if (!filter.animateTopInset) {
-            // just a local update was performed
-            if (previousAnimator != null) {
-                // we need to increase all animation keyframes of the previous animator by the
-                // relative change to the end value
-                PropertyValuesHolder[] values = previousAnimator.getValues();
-                int relativeDiff = newEndValue - previousEndValue;
-                int newStartValue = previousStartValue + relativeDiff;
-                values[0].setIntValues(newStartValue, newEndValue);
-                child.setTag(TAG_START_TOP_INSET, newStartValue);
-                child.setTag(TAG_END_TOP_INSET, newEndValue);
-                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
-                return;
-            } else {
-                // no new animation needed, let's just apply the value
-                child.setClipTopAmount(newEndValue);
-                return;
-            }
-        }
-
-        ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue);
-        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                child.setClipTopAmount((int) animation.getAnimatedValue());
-            }
-        });
-        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
-        animator.setDuration(newDuration);
-        if (properties.delay > 0 && (previousAnimator == null
-                || previousAnimator.getAnimatedFraction() == 0)) {
-            animator.setStartDelay(properties.delay);
-        }
-        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
-        if (listener != null) {
-            animator.addListener(listener);
-        }
-        // remove the tag when the animation is finished
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                child.setTag(TAG_ANIMATOR_TOP_INSET, null);
-                child.setTag(TAG_START_TOP_INSET, null);
-                child.setTag(TAG_END_TOP_INSET, null);
-            }
-        });
-        startAnimator(animator, listener);
-        child.setTag(TAG_ANIMATOR_TOP_INSET, animator);
-        child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount());
-        child.setTag(TAG_END_TOP_INSET, newEndValue);
-    }
-
-    /**
-     * Get the end value of the height animation running on a view or the actualHeight
-     * if no animation is running.
-     */
-    public static int getFinalActualHeight(ExpandableView view) {
-        if (view == null) {
-            return 0;
-        }
-        ValueAnimator heightAnimator = getChildTag(view, TAG_ANIMATOR_HEIGHT);
-        if (heightAnimator == null) {
-            return view.getActualHeight();
-        } else {
-            return getChildTag(view, TAG_END_HEIGHT);
-        }
-    }
-
-    @Override
-    public void cancelAnimations(View view) {
-        super.cancelAnimations(view);
-        Animator animator = getChildTag(view, TAG_ANIMATOR_HEIGHT);
-        if (animator != null) {
-            animator.cancel();
-        }
-        animator = getChildTag(view, TAG_ANIMATOR_SHADOW_ALPHA);
-        if (animator != null) {
-            animator.cancel();
-        }
-        animator = getChildTag(view, TAG_ANIMATOR_TOP_INSET);
-        if (animator != null) {
-            animator.cancel();
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
deleted file mode 100644
index 59ce0ca..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.stack;
-
-import android.graphics.Path;
-import android.view.animation.PathInterpolator;
-
-/**
- * An interpolator specifically designed for the appear animation of heads up notifications.
- */
-public class HeadsUpAppearInterpolator extends PathInterpolator {
-
-    private static float X1 = 250f;
-    private static float X2 = 200f;
-    private static float XTOT = (X1 + X2);;
-
-    public HeadsUpAppearInterpolator() {
-        super(getAppearPath());
-    }
-
-    private static Path getAppearPath() {
-        Path path = new Path();
-        path.moveTo(0, 0);
-        float y1 = 90f;
-        float y2 = 80f;
-        path.cubicTo(X1 * 0.8f / XTOT, y1 / y2,
-                X1 * 0.8f / XTOT, y1 / y2,
-                X1 / XTOT, y1 / y2);
-        path.cubicTo((X1 + X2 * 0.4f) / XTOT, y1 / y2,
-                (X1 + X2 * 0.2f) / XTOT, 1.0f,
-                1.0f , 1.0f);
-        return path;
-    }
-
-    public static float getFractionUntilOvershoot() {
-        return X1 / XTOT;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
deleted file mode 100644
index 2dd3d4e..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ /dev/null
@@ -1,1318 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.stack;
-
-import android.app.Notification;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.drawable.ColorDrawable;
-import android.service.notification.StatusBarNotification;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.NotificationHeaderView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.RemoteViews;
-import android.widget.TextView;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationHeaderUtil;
-import com.android.systemui.statusbar.notification.HybridGroupManager;
-import com.android.systemui.statusbar.notification.HybridNotificationView;
-import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A container containing child notifications
- */
-public class NotificationChildrenContainer extends ViewGroup {
-
-    private static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
-    private static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
-    private static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
-    private static final int NUMBER_OF_CHILDREN_WHEN_AMBIENT = 1;
-    private static final AnimationProperties ALPHA_FADE_IN = new AnimationProperties() {
-        private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
-
-        @Override
-        public AnimationFilter getAnimationFilter() {
-            return mAnimationFilter;
-        }
-    }.setDuration(200);
-
-    private final List<View> mDividers = new ArrayList<>();
-    private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
-    private final HybridGroupManager mHybridGroupManager;
-    private int mChildPadding;
-    private int mDividerHeight;
-    private float mDividerAlpha;
-    private int mNotificationHeaderMargin;
-
-    private int mNotificatonTopPadding;
-    private float mCollapsedBottompadding;
-    private boolean mChildrenExpanded;
-    private ExpandableNotificationRow mContainingNotification;
-    private TextView mOverflowNumber;
-    private ViewState mGroupOverFlowState;
-    private int mRealHeight;
-    private boolean mUserLocked;
-    private int mActualHeight;
-    private boolean mNeverAppliedGroupState;
-    private int mHeaderHeight;
-
-    /**
-     * Whether or not individual notifications that are part of this container will have shadows.
-     */
-    private boolean mEnableShadowOnChildNotifications;
-
-    private NotificationHeaderView mNotificationHeader;
-    private NotificationViewWrapper mNotificationHeaderWrapper;
-    private NotificationHeaderView mNotificationHeaderLowPriority;
-    private NotificationViewWrapper mNotificationHeaderWrapperLowPriority;
-    private ViewGroup mNotificationHeaderAmbient;
-    private NotificationViewWrapper mNotificationHeaderWrapperAmbient;
-    private NotificationHeaderUtil mHeaderUtil;
-    private ViewState mHeaderViewState;
-    private int mClipBottomAmount;
-    private boolean mIsLowPriority;
-    private OnClickListener mHeaderClickListener;
-    private ViewGroup mCurrentHeader;
-
-    private boolean mShowDividersWhenExpanded;
-    private boolean mHideDividersDuringExpand;
-    private int mTranslationForHeader;
-    private int mCurrentHeaderTranslation = 0;
-    private float mHeaderVisibleAmount = 1.0f;
-
-    public NotificationChildrenContainer(Context context) {
-        this(context, null);
-    }
-
-    public NotificationChildrenContainer(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        mHybridGroupManager = new HybridGroupManager(getContext(), this);
-        initDimens();
-        setClipChildren(false);
-    }
-
-    private void initDimens() {
-        Resources res = getResources();
-        mChildPadding = res.getDimensionPixelSize(R.dimen.notification_children_padding);
-        mDividerHeight = res.getDimensionPixelSize(
-                R.dimen.notification_children_container_divider_height);
-        mDividerAlpha = res.getFloat(R.dimen.notification_divider_alpha);
-        mNotificationHeaderMargin = res.getDimensionPixelSize(
-                R.dimen.notification_children_container_margin_top);
-        mNotificatonTopPadding = res.getDimensionPixelSize(
-                R.dimen.notification_children_container_top_padding);
-        mHeaderHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
-        mCollapsedBottompadding = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.notification_content_margin);
-        mEnableShadowOnChildNotifications =
-                res.getBoolean(R.bool.config_enableShadowOnChildNotifications);
-        mShowDividersWhenExpanded =
-                res.getBoolean(R.bool.config_showDividersWhenGroupNotificationExpanded);
-        mHideDividersDuringExpand =
-                res.getBoolean(R.bool.config_hideDividersDuringExpand);
-        mTranslationForHeader = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.notification_content_margin)
-                - mNotificationHeaderMargin;
-        mHybridGroupManager.initDimens();
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
-        for (int i = 0; i < childCount; i++) {
-            View child = mChildren.get(i);
-            // We need to layout all children even the GONE ones, such that the heights are
-            // calculated correctly as they are used to calculate how many we can fit on the screen
-            child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
-            mDividers.get(i).layout(0, 0, getWidth(), mDividerHeight);
-        }
-        if (mOverflowNumber != null) {
-            boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-            int left = (isRtl ? 0 : getWidth() - mOverflowNumber.getMeasuredWidth());
-            int right = left + mOverflowNumber.getMeasuredWidth();
-            mOverflowNumber.layout(left, 0, right, mOverflowNumber.getMeasuredHeight());
-        }
-        if (mNotificationHeader != null) {
-            mNotificationHeader.layout(0, 0, mNotificationHeader.getMeasuredWidth(),
-                    mNotificationHeader.getMeasuredHeight());
-        }
-        if (mNotificationHeaderLowPriority != null) {
-            mNotificationHeaderLowPriority.layout(0, 0,
-                    mNotificationHeaderLowPriority.getMeasuredWidth(),
-                    mNotificationHeaderLowPriority.getMeasuredHeight());
-        }
-        if (mNotificationHeaderAmbient != null) {
-            mNotificationHeaderAmbient.layout(0, 0,
-                    mNotificationHeaderAmbient.getMeasuredWidth(),
-                    mNotificationHeaderAmbient.getMeasuredHeight());
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
-        boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
-        int size = MeasureSpec.getSize(heightMeasureSpec);
-        int newHeightSpec = heightMeasureSpec;
-        if (hasFixedHeight || isHeightLimited) {
-            newHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
-        }
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        if (mOverflowNumber != null) {
-            mOverflowNumber.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
-                    newHeightSpec);
-        }
-        int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
-        int height = mNotificationHeaderMargin + mNotificatonTopPadding;
-        int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
-        int collapsedChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
-        int overflowIndex = childCount > collapsedChildren ? collapsedChildren - 1 : -1;
-        for (int i = 0; i < childCount; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
-            // We need to measure all children even the GONE ones, such that the heights are
-            // calculated correctly as they are used to calculate how many we can fit on the screen.
-            boolean isOverflow = i == overflowIndex;
-            child.setSingleLineWidthIndention(isOverflow && mOverflowNumber != null &&
-                    !mContainingNotification.isShowingAmbient()
-                    ? mOverflowNumber.getMeasuredWidth() : 0);
-            child.measure(widthMeasureSpec, newHeightSpec);
-            // layout the divider
-            View divider = mDividers.get(i);
-            divider.measure(widthMeasureSpec, dividerHeightSpec);
-            if (child.getVisibility() != GONE) {
-                height += child.getMeasuredHeight() + mDividerHeight;
-            }
-        }
-        mRealHeight = height;
-        if (heightMode != MeasureSpec.UNSPECIFIED) {
-            height = Math.min(height, size);
-        }
-
-        int headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
-        if (mNotificationHeader != null) {
-            mNotificationHeader.measure(widthMeasureSpec, headerHeightSpec);
-        }
-        if (mNotificationHeaderLowPriority != null) {
-            headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
-            mNotificationHeaderLowPriority.measure(widthMeasureSpec, headerHeightSpec);
-        }
-        if (mNotificationHeaderAmbient != null) {
-            headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
-            mNotificationHeaderAmbient.measure(widthMeasureSpec, headerHeightSpec);
-        }
-
-        setMeasuredDimension(width, height);
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-    @Override
-    public boolean pointInView(float localX, float localY, float slop) {
-        return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
-                localY < (mRealHeight + slop);
-    }
-
-    /**
-     * Add a child notification to this view.
-     *
-     * @param row the row to add
-     * @param childIndex the index to add it at, if -1 it will be added at the end
-     */
-    public void addNotification(ExpandableNotificationRow row, int childIndex) {
-        int newIndex = childIndex < 0 ? mChildren.size() : childIndex;
-        mChildren.add(newIndex, row);
-        addView(row);
-        row.setUserLocked(mUserLocked);
-
-        View divider = inflateDivider();
-        addView(divider);
-        mDividers.add(newIndex, divider);
-
-        updateGroupOverflow();
-        row.setContentTransformationAmount(0, false /* isLastChild */);
-        // It doesn't make sense to keep old animations around, lets cancel them!
-        ExpandableNotificationRow.NotificationViewState viewState = row.getViewState();
-        if (viewState != null) {
-            viewState.cancelAnimations(row);
-            row.cancelAppearDrawing();
-        }
-    }
-
-    public void removeNotification(ExpandableNotificationRow row) {
-        int childIndex = mChildren.indexOf(row);
-        mChildren.remove(row);
-        removeView(row);
-
-        final View divider = mDividers.remove(childIndex);
-        removeView(divider);
-        getOverlay().add(divider);
-        CrossFadeHelper.fadeOut(divider, new Runnable() {
-            @Override
-            public void run() {
-                getOverlay().remove(divider);
-            }
-        });
-
-        row.setSystemChildExpanded(false);
-        row.setUserLocked(false);
-        updateGroupOverflow();
-        if (!row.isRemoved()) {
-            mHeaderUtil.restoreNotificationHeader(row);
-        }
-    }
-
-    /**
-     * @return The number of notification children in the container.
-     */
-    public int getNotificationChildCount() {
-        return mChildren.size();
-    }
-
-    public void recreateNotificationHeader(OnClickListener listener) {
-        mHeaderClickListener = listener;
-        StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
-        final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
-                notification.getNotification());
-        RemoteViews header = builder.makeNotificationHeader(false /* ambient */);
-        if (mNotificationHeader == null) {
-            mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
-            final View expandButton = mNotificationHeader.findViewById(
-                    com.android.internal.R.id.expand_button);
-            expandButton.setVisibility(VISIBLE);
-            mNotificationHeader.setOnClickListener(mHeaderClickListener);
-            mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
-                    mNotificationHeader, mContainingNotification);
-            addView(mNotificationHeader, 0);
-            invalidate();
-        } else {
-            header.reapply(getContext(), mNotificationHeader);
-        }
-        mNotificationHeaderWrapper.onContentUpdated(mContainingNotification);
-        recreateLowPriorityHeader(builder);
-        recreateAmbientHeader(builder);
-        updateHeaderVisibility(false /* animate */);
-        updateChildrenHeaderAppearance();
-    }
-
-    private void recreateAmbientHeader(Notification.Builder builder) {
-        RemoteViews header;
-        StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
-        if (builder == null) {
-            builder = Notification.Builder.recoverBuilder(getContext(),
-                    notification.getNotification());
-        }
-        header = builder.makeNotificationHeader(true /* ambient */);
-        if (mNotificationHeaderAmbient == null) {
-            mNotificationHeaderAmbient = (ViewGroup) header.apply(getContext(), this);
-            mNotificationHeaderWrapperAmbient = NotificationViewWrapper.wrap(getContext(),
-                    mNotificationHeaderAmbient, mContainingNotification);
-            mNotificationHeaderWrapperAmbient.onContentUpdated(mContainingNotification);
-            addView(mNotificationHeaderAmbient, 0);
-            invalidate();
-        } else {
-            header.reapply(getContext(), mNotificationHeaderAmbient);
-        }
-        resetHeaderVisibilityIfNeeded(mNotificationHeaderAmbient, calculateDesiredHeader());
-        mNotificationHeaderWrapperAmbient.onContentUpdated(mContainingNotification);
-    }
-
-    /**
-     * Recreate the low-priority header.
-     *
-     * @param builder a builder to reuse. Otherwise the builder will be recovered.
-     */
-    private void recreateLowPriorityHeader(Notification.Builder builder) {
-        RemoteViews header;
-        StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
-        if (mIsLowPriority) {
-            if (builder == null) {
-                builder = Notification.Builder.recoverBuilder(getContext(),
-                        notification.getNotification());
-            }
-            header = builder.makeLowPriorityContentView(true /* useRegularSubtext */);
-            if (mNotificationHeaderLowPriority == null) {
-                mNotificationHeaderLowPriority = (NotificationHeaderView) header.apply(getContext(),
-                        this);
-                final View expandButton = mNotificationHeaderLowPriority.findViewById(
-                        com.android.internal.R.id.expand_button);
-                expandButton.setVisibility(VISIBLE);
-                mNotificationHeaderLowPriority.setOnClickListener(mHeaderClickListener);
-                mNotificationHeaderWrapperLowPriority = NotificationViewWrapper.wrap(getContext(),
-                        mNotificationHeaderLowPriority, mContainingNotification);
-                addView(mNotificationHeaderLowPriority, 0);
-                invalidate();
-            } else {
-                header.reapply(getContext(), mNotificationHeaderLowPriority);
-            }
-            mNotificationHeaderWrapperLowPriority.onContentUpdated(mContainingNotification);
-            resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, calculateDesiredHeader());
-        } else {
-            removeView(mNotificationHeaderLowPriority);
-            mNotificationHeaderLowPriority = null;
-            mNotificationHeaderWrapperLowPriority = null;
-        }
-    }
-
-    public void updateChildrenHeaderAppearance() {
-        mHeaderUtil.updateChildrenHeaderAppearance();
-    }
-
-    public void updateGroupOverflow() {
-        int childCount = mChildren.size();
-        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
-        if (childCount > maxAllowedVisibleChildren) {
-            int number = childCount - maxAllowedVisibleChildren;
-            mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number);
-            if (mContainingNotification.isShowingAmbient()) {
-                ExpandableNotificationRow overflowView = mChildren.get(0);
-                HybridNotificationView ambientSingleLineView = overflowView == null ? null
-                        : overflowView.getAmbientSingleLineView();
-                if (ambientSingleLineView != null) {
-                    mHybridGroupManager.bindOverflowNumberAmbient(
-                            ambientSingleLineView.getTitleView(),
-                            mContainingNotification.getStatusBarNotification().getNotification(),
-                            number);
-                }
-            }
-            if (mGroupOverFlowState == null) {
-                mGroupOverFlowState = new ViewState();
-                mNeverAppliedGroupState = true;
-            }
-        } else if (mOverflowNumber != null) {
-            removeView(mOverflowNumber);
-            if (isShown() && isAttachedToWindow()) {
-                final View removedOverflowNumber = mOverflowNumber;
-                addTransientView(removedOverflowNumber, getTransientViewCount());
-                CrossFadeHelper.fadeOut(removedOverflowNumber, new Runnable() {
-                    @Override
-                    public void run() {
-                        removeTransientView(removedOverflowNumber);
-                    }
-                });
-            }
-            mOverflowNumber = null;
-            mGroupOverFlowState = null;
-        }
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        updateGroupOverflow();
-    }
-
-    private View inflateDivider() {
-        return LayoutInflater.from(mContext).inflate(
-                R.layout.notification_children_divider, this, false);
-    }
-
-    public List<ExpandableNotificationRow> getNotificationChildren() {
-        return mChildren;
-    }
-
-    /**
-     * Apply the order given in the list to the children.
-     *
-     * @param childOrder the new list order
-     * @param visualStabilityManager
-     * @param callback
-     * @return whether the list order has changed
-     */
-    public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
-            VisualStabilityManager visualStabilityManager,
-            VisualStabilityManager.Callback callback) {
-        if (childOrder == null) {
-            return false;
-        }
-        boolean result = false;
-        for (int i = 0; i < mChildren.size() && i < childOrder.size(); i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
-            ExpandableNotificationRow desiredChild = childOrder.get(i);
-            if (child != desiredChild) {
-                if (visualStabilityManager.canReorderNotification(desiredChild)) {
-                    mChildren.remove(desiredChild);
-                    mChildren.add(i, desiredChild);
-                    result = true;
-                } else {
-                    visualStabilityManager.addReorderingAllowedCallback(callback);
-                }
-            }
-        }
-        updateExpansionStates();
-        return result;
-    }
-
-    private void updateExpansionStates() {
-        if (mChildrenExpanded || mUserLocked) {
-            // we don't modify it the group is expanded or if we are expanding it
-            return;
-        }
-        int size = mChildren.size();
-        for (int i = 0; i < size; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
-            child.setSystemChildExpanded(i == 0 && size == 1);
-        }
-    }
-
-    /**
-     *
-     * @return the intrinsic size of this children container, i.e the natural fully expanded state
-     */
-    public int getIntrinsicHeight() {
-        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
-        return getIntrinsicHeight(maxAllowedVisibleChildren);
-    }
-
-    /**
-     * @return the intrinsic height with a number of children given
-     *         in @param maxAllowedVisibleChildren
-     */
-    private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
-        if (showingAsLowPriority()) {
-            return mNotificationHeaderLowPriority.getHeight();
-        }
-        int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
-        int visibleChildren = 0;
-        int childCount = mChildren.size();
-        boolean firstChild = true;
-        float expandFactor = 0;
-        if (mUserLocked) {
-            expandFactor = getGroupExpandFraction();
-        }
-        boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isShowingAmbient();
-        for (int i = 0; i < childCount; i++) {
-            if (visibleChildren >= maxAllowedVisibleChildren) {
-                break;
-            }
-            if (!firstChild) {
-                if (mUserLocked) {
-                    intrinsicHeight += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
-                            expandFactor);
-                } else {
-                    intrinsicHeight += childrenExpanded ? mDividerHeight : mChildPadding;
-                }
-            } else {
-                if (mUserLocked) {
-                    intrinsicHeight += NotificationUtils.interpolate(
-                            0,
-                            mNotificatonTopPadding + mDividerHeight,
-                            expandFactor);
-                } else {
-                    intrinsicHeight += childrenExpanded
-                            ? mNotificatonTopPadding + mDividerHeight
-                            : 0;
-                }
-                firstChild = false;
-            }
-            ExpandableNotificationRow child = mChildren.get(i);
-            intrinsicHeight += child.getIntrinsicHeight();
-            visibleChildren++;
-        }
-        if (mUserLocked) {
-            intrinsicHeight += NotificationUtils.interpolate(mCollapsedBottompadding, 0.0f,
-                    expandFactor);
-        } else if (!childrenExpanded) {
-            intrinsicHeight += mCollapsedBottompadding;
-        }
-        return intrinsicHeight;
-    }
-
-    /**
-     * Update the state of all its children based on a linear layout algorithm.
-     *  @param resultState the state to update
-     * @param parentState the state of the parent
-     * @param ambientState
-     */
-    public void getState(StackScrollState resultState, ExpandableViewState parentState,
-            AmbientState ambientState) {
-        int childCount = mChildren.size();
-        int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation;
-        boolean firstChild = true;
-        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
-        int lastVisibleIndex = maxAllowedVisibleChildren - 1;
-        int firstOverflowIndex = lastVisibleIndex + 1;
-        float expandFactor = 0;
-        boolean expandingToExpandedGroup = mUserLocked && !showingAsLowPriority();
-        if (mUserLocked) {
-            expandFactor = getGroupExpandFraction();
-            firstOverflowIndex = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
-        }
-
-        boolean childrenExpandedAndNotAnimating = mChildrenExpanded
-                && !mContainingNotification.isGroupExpansionChanging();
-        int launchTransitionCompensation = 0;
-        for (int i = 0; i < childCount; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
-            if (!firstChild) {
-                if (expandingToExpandedGroup) {
-                    yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
-                            expandFactor);
-                } else {
-                    yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
-                }
-            } else {
-                if (expandingToExpandedGroup) {
-                    yPosition += NotificationUtils.interpolate(
-                            0,
-                            mNotificatonTopPadding + mDividerHeight,
-                            expandFactor);
-                } else {
-                    yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
-                }
-                firstChild = false;
-            }
-
-            ExpandableViewState childState = resultState.getViewStateForView(child);
-            int intrinsicHeight = child.getIntrinsicHeight();
-            childState.height = intrinsicHeight;
-            childState.yTranslation = yPosition + launchTransitionCompensation;
-            childState.hidden = false;
-            // When the group is expanded, the children cast the shadows rather than the parent
-            // so use the parent's elevation here.
-            childState.zTranslation =
-                    (childrenExpandedAndNotAnimating && mEnableShadowOnChildNotifications)
-                    ? parentState.zTranslation
-                    : 0;
-            childState.dimmed = parentState.dimmed;
-            childState.dark = parentState.dark;
-            childState.hideSensitive = parentState.hideSensitive;
-            childState.belowSpeedBump = parentState.belowSpeedBump;
-            childState.clipTopAmount = 0;
-            childState.alpha = 0;
-            if (i < firstOverflowIndex) {
-                childState.alpha = showingAsLowPriority() ? expandFactor : 1.0f;
-            } else if (expandFactor == 1.0f && i <= lastVisibleIndex) {
-                childState.alpha = (mActualHeight - childState.yTranslation) / childState.height;
-                childState.alpha = Math.max(0.0f, Math.min(1.0f, childState.alpha));
-            }
-            childState.location = parentState.location;
-            childState.inShelf = parentState.inShelf;
-            yPosition += intrinsicHeight;
-            if (child.isExpandAnimationRunning()) {
-                launchTransitionCompensation = -ambientState.getExpandAnimationTopChange();
-            }
-
-        }
-        if (mOverflowNumber != null) {
-            ExpandableNotificationRow overflowView = mChildren.get(Math.min(
-                    getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1);
-            mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
-
-            if (mContainingNotification.isShowingAmbient()) {
-                mGroupOverFlowState.alpha = 0.0f;
-            } else if (!mChildrenExpanded) {
-                HybridNotificationView alignView = overflowView.getSingleLineView();
-                if (alignView != null) {
-                    View mirrorView = alignView.getTextView();
-                    if (mirrorView.getVisibility() == GONE) {
-                        mirrorView = alignView.getTitleView();
-                    }
-                    if (mirrorView.getVisibility() == GONE) {
-                        mirrorView = alignView;
-                    }
-                    mGroupOverFlowState.alpha = mirrorView.getAlpha();
-                    mGroupOverFlowState.yTranslation += NotificationUtils.getRelativeYOffset(
-                            mirrorView, overflowView);
-                }
-            } else {
-                mGroupOverFlowState.yTranslation += mNotificationHeaderMargin;
-                mGroupOverFlowState.alpha = 0.0f;
-            }
-        }
-        if (mNotificationHeader != null) {
-            if (mHeaderViewState == null) {
-                mHeaderViewState = new ViewState();
-            }
-            mHeaderViewState.initFrom(mNotificationHeader);
-            mHeaderViewState.zTranslation = childrenExpandedAndNotAnimating
-                    ? parentState.zTranslation
-                    : 0;
-            mHeaderViewState.yTranslation = mCurrentHeaderTranslation;
-            mHeaderViewState.alpha = mHeaderVisibleAmount;
-            // The hiding is done automatically by the alpha, otherwise we'll pick it up again
-            // in the next frame with the initFrom call above and have an invisible header
-            mHeaderViewState.hidden = false;
-        }
-    }
-
-    /**
-     * When moving into the bottom stack, the bottom visible child in an expanded group adjusts its
-     * height, children in the group after this are gone.
-     *
-     * @param child the child who's height to adjust.
-     * @param parentHeight the height of the parent.
-     * @param childState the state to update.
-     * @param yPosition the yPosition of the view.
-     * @return true if children after this one should be hidden.
-     */
-    private boolean updateChildStateForExpandedGroup(ExpandableNotificationRow child,
-            int parentHeight, ExpandableViewState childState, int yPosition) {
-        final int top = yPosition + child.getClipTopAmount();
-        final int intrinsicHeight = child.getIntrinsicHeight();
-        final int bottom = top + intrinsicHeight;
-        int newHeight = intrinsicHeight;
-        if (bottom >= parentHeight) {
-            // Child is either clipped or gone
-            newHeight = Math.max((parentHeight - top), 0);
-        }
-        childState.hidden = newHeight == 0;
-        childState.height = newHeight;
-        return childState.height != intrinsicHeight && !childState.hidden;
-    }
-
-    private int getMaxAllowedVisibleChildren() {
-        return getMaxAllowedVisibleChildren(false /* likeCollapsed */);
-    }
-
-    private int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
-        if (mContainingNotification.isShowingAmbient()) {
-            return NUMBER_OF_CHILDREN_WHEN_AMBIENT;
-        }
-        if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())) {
-            return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
-        }
-        if (mIsLowPriority || !mContainingNotification.isOnKeyguard()
-                && (mContainingNotification.isExpanded() || mContainingNotification.isHeadsUp())) {
-            return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
-        }
-        return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
-    }
-
-    public void applyState(StackScrollState state) {
-        int childCount = mChildren.size();
-        ViewState tmpState = new ViewState();
-        float expandFraction = 0.0f;
-        if (mUserLocked) {
-            expandFraction = getGroupExpandFraction();
-        }
-        final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
-                || (mChildrenExpanded && mShowDividersWhenExpanded)
-                || (mContainingNotification.isGroupExpansionChanging()
-                && !mHideDividersDuringExpand);
-        for (int i = 0; i < childCount; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
-            ExpandableViewState viewState = state.getViewStateForView(child);
-            viewState.applyToView(child);
-
-            // layout the divider
-            View divider = mDividers.get(i);
-            tmpState.initFrom(divider);
-            tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
-            float alpha = mChildrenExpanded && viewState.alpha != 0 ? mDividerAlpha : 0;
-            if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
-                alpha = NotificationUtils.interpolate(0, 0.5f,
-                        Math.min(viewState.alpha, expandFraction));
-            }
-            tmpState.hidden = !dividersVisible;
-            tmpState.alpha = alpha;
-            tmpState.applyToView(divider);
-            // There is no fake shadow to be drawn on the children
-            child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
-        }
-        if (mGroupOverFlowState != null) {
-            mGroupOverFlowState.applyToView(mOverflowNumber);
-            mNeverAppliedGroupState = false;
-        }
-        if (mHeaderViewState != null) {
-            mHeaderViewState.applyToView(mNotificationHeader);
-        }
-        updateChildrenClipping();
-    }
-
-    private void updateChildrenClipping() {
-        if (mContainingNotification.hasExpandingChild()) {
-            return;
-        }
-        int childCount = mChildren.size();
-        int layoutEnd = mContainingNotification.getActualHeight() - mClipBottomAmount;
-        for (int i = 0; i < childCount; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
-            if (child.getVisibility() == GONE) {
-                continue;
-            }
-            float childTop = child.getTranslationY();
-            float childBottom = childTop + child.getActualHeight();
-            boolean visible = true;
-            int clipBottomAmount = 0;
-            if (childTop > layoutEnd) {
-                visible = false;
-            } else if (childBottom > layoutEnd) {
-                clipBottomAmount = (int) (childBottom - layoutEnd);
-            }
-
-            boolean isVisible = child.getVisibility() == VISIBLE;
-            if (visible != isVisible) {
-                child.setVisibility(visible ? VISIBLE : INVISIBLE);
-            }
-
-            child.setClipBottomAmount(clipBottomAmount);
-        }
-    }
-
-    /**
-     * This is called when the children expansion has changed and positions the children properly
-     * for an appear animation.
-     *
-     * @param state the new state we animate to
-     */
-    public void prepareExpansionChanged(StackScrollState state) {
-        // TODO: do something that makes sense, like placing the invisible views correctly
-        return;
-    }
-
-    public void startAnimationToState(StackScrollState state, AnimationProperties properties) {
-        int childCount = mChildren.size();
-        ViewState tmpState = new ViewState();
-        float expandFraction = getGroupExpandFraction();
-        final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
-                || (mChildrenExpanded && mShowDividersWhenExpanded)
-                || (mContainingNotification.isGroupExpansionChanging()
-                && !mHideDividersDuringExpand);
-        for (int i = childCount - 1; i >= 0; i--) {
-            ExpandableNotificationRow child = mChildren.get(i);
-            ExpandableViewState viewState = state.getViewStateForView(child);
-            viewState.animateTo(child, properties);
-
-            // layout the divider
-            View divider = mDividers.get(i);
-            tmpState.initFrom(divider);
-            tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
-            float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
-            if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
-                alpha = NotificationUtils.interpolate(0, 0.5f,
-                        Math.min(viewState.alpha, expandFraction));
-            }
-            tmpState.hidden = !dividersVisible;
-            tmpState.alpha = alpha;
-            tmpState.animateTo(divider, properties);
-            // There is no fake shadow to be drawn on the children
-            child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
-        }
-        if (mOverflowNumber != null) {
-            if (mNeverAppliedGroupState) {
-                float alpha = mGroupOverFlowState.alpha;
-                mGroupOverFlowState.alpha = 0;
-                mGroupOverFlowState.applyToView(mOverflowNumber);
-                mGroupOverFlowState.alpha = alpha;
-                mNeverAppliedGroupState = false;
-            }
-            mGroupOverFlowState.animateTo(mOverflowNumber, properties);
-        }
-        if (mNotificationHeader != null) {
-            mHeaderViewState.applyToView(mNotificationHeader);
-        }
-        updateChildrenClipping();
-    }
-
-    public ExpandableNotificationRow getViewAtPosition(float y) {
-        // find the view under the pointer, accounting for GONE views
-        final int count = mChildren.size();
-        for (int childIdx = 0; childIdx < count; childIdx++) {
-            ExpandableNotificationRow slidingChild = mChildren.get(childIdx);
-            float childTop = slidingChild.getTranslationY();
-            float top = childTop + slidingChild.getClipTopAmount();
-            float bottom = childTop + slidingChild.getActualHeight();
-            if (y >= top && y <= bottom) {
-                return slidingChild;
-            }
-        }
-        return null;
-    }
-
-    public void setChildrenExpanded(boolean childrenExpanded) {
-        mChildrenExpanded = childrenExpanded;
-        updateExpansionStates();
-        if (mNotificationHeader != null) {
-            mNotificationHeader.setExpanded(childrenExpanded);
-        }
-        final int count = mChildren.size();
-        for (int childIdx = 0; childIdx < count; childIdx++) {
-            ExpandableNotificationRow child = mChildren.get(childIdx);
-            child.setChildrenExpanded(childrenExpanded, false);
-        }
-    }
-
-    public void setContainingNotification(ExpandableNotificationRow parent) {
-        mContainingNotification = parent;
-        mHeaderUtil = new NotificationHeaderUtil(mContainingNotification);
-    }
-
-    public ExpandableNotificationRow getContainingNotification() {
-        return mContainingNotification;
-    }
-
-    public NotificationHeaderView getHeaderView() {
-        return mNotificationHeader;
-    }
-
-    public NotificationHeaderView getLowPriorityHeaderView() {
-        return mNotificationHeaderLowPriority;
-    }
-
-    @VisibleForTesting
-    public ViewGroup getCurrentHeaderView() {
-        return mCurrentHeader;
-    }
-
-    public void notifyShowAmbientChanged() {
-        updateHeaderVisibility(false);
-        updateGroupOverflow();
-    }
-
-    private void updateHeaderVisibility(boolean animate) {
-        ViewGroup desiredHeader;
-        ViewGroup currentHeader = mCurrentHeader;
-        desiredHeader = calculateDesiredHeader();
-
-        if (currentHeader == desiredHeader) {
-            return;
-        }
-        if (desiredHeader == mNotificationHeaderAmbient
-                || currentHeader == mNotificationHeaderAmbient) {
-            animate = false;
-        }
-
-        if (animate) {
-            if (desiredHeader != null && currentHeader != null) {
-                currentHeader.setVisibility(VISIBLE);
-                desiredHeader.setVisibility(VISIBLE);
-                NotificationViewWrapper visibleWrapper = getWrapperForView(desiredHeader);
-                NotificationViewWrapper hiddenWrapper = getWrapperForView(currentHeader);
-                visibleWrapper.transformFrom(hiddenWrapper);
-                hiddenWrapper.transformTo(visibleWrapper, () -> updateHeaderVisibility(false));
-                startChildAlphaAnimations(desiredHeader == mNotificationHeader);
-            } else {
-                animate = false;
-            }
-        }
-        if (!animate) {
-            if (desiredHeader != null) {
-                getWrapperForView(desiredHeader).setVisible(true);
-                desiredHeader.setVisibility(VISIBLE);
-            }
-            if (currentHeader != null) {
-                // Wrapper can be null if we were a low priority notification
-                // and just destroyed it by calling setIsLowPriority(false)
-                NotificationViewWrapper wrapper = getWrapperForView(currentHeader);
-                if (wrapper != null) {
-                    wrapper.setVisible(false);
-                }
-                currentHeader.setVisibility(INVISIBLE);
-            }
-        }
-
-        resetHeaderVisibilityIfNeeded(mNotificationHeader, desiredHeader);
-        resetHeaderVisibilityIfNeeded(mNotificationHeaderAmbient, desiredHeader);
-        resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, desiredHeader);
-
-        mCurrentHeader = desiredHeader;
-    }
-
-    private void resetHeaderVisibilityIfNeeded(View header, View desiredHeader) {
-        if (header == null) {
-            return;
-        }
-        if (header != mCurrentHeader && header != desiredHeader) {
-            getWrapperForView(header).setVisible(false);
-            header.setVisibility(INVISIBLE);
-        }
-        if (header == desiredHeader && header.getVisibility() != VISIBLE) {
-            getWrapperForView(header).setVisible(true);
-            header.setVisibility(VISIBLE);
-        }
-    }
-
-    private ViewGroup calculateDesiredHeader() {
-        ViewGroup desiredHeader;
-        if (mContainingNotification.isShowingAmbient()) {
-            desiredHeader = mNotificationHeaderAmbient;
-        } else if (showingAsLowPriority()) {
-            desiredHeader = mNotificationHeaderLowPriority;
-        } else {
-            desiredHeader = mNotificationHeader;
-        }
-        return desiredHeader;
-    }
-
-    private void startChildAlphaAnimations(boolean toVisible) {
-        float target = toVisible ? 1.0f : 0.0f;
-        float start = 1.0f - target;
-        int childCount = mChildren.size();
-        for (int i = 0; i < childCount; i++) {
-            if (i >= NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED) {
-                break;
-            }
-            ExpandableNotificationRow child = mChildren.get(i);
-            child.setAlpha(start);
-            ViewState viewState = new ViewState();
-            viewState.initFrom(child);
-            viewState.alpha = target;
-            ALPHA_FADE_IN.setDelay(i * 50);
-            viewState.animateTo(child, ALPHA_FADE_IN);
-        }
-    }
-
-
-    private void updateHeaderTransformation() {
-        if (mUserLocked && showingAsLowPriority()) {
-            float fraction = getGroupExpandFraction();
-            mNotificationHeaderWrapper.transformFrom(mNotificationHeaderWrapperLowPriority,
-                    fraction);
-            mNotificationHeader.setVisibility(VISIBLE);
-            mNotificationHeaderWrapperLowPriority.transformTo(mNotificationHeaderWrapper,
-                    fraction);
-        }
-
-    }
-
-    private NotificationViewWrapper getWrapperForView(View visibleHeader) {
-        if (visibleHeader == mNotificationHeader) {
-            return mNotificationHeaderWrapper;
-        }
-        if (visibleHeader == mNotificationHeaderAmbient) {
-            return mNotificationHeaderWrapperAmbient;
-        }
-        return mNotificationHeaderWrapperLowPriority;
-    }
-
-    /**
-     * Called when a groups expansion changes to adjust the background of the header view.
-     *
-     * @param expanded whether the group is expanded.
-     */
-    public void updateHeaderForExpansion(boolean expanded) {
-        if (mNotificationHeader != null) {
-            if (expanded) {
-                ColorDrawable cd = new ColorDrawable();
-                cd.setColor(mContainingNotification.calculateBgColor());
-                mNotificationHeader.setHeaderBackgroundDrawable(cd);
-            } else {
-                mNotificationHeader.setHeaderBackgroundDrawable(null);
-            }
-        }
-    }
-
-    public int getMaxContentHeight() {
-        if (showingAsLowPriority()) {
-            return getMinHeight(NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED, true
-                    /* likeHighPriority */);
-        }
-        int maxContentHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
-                + mNotificatonTopPadding;
-        int visibleChildren = 0;
-        int childCount = mChildren.size();
-        for (int i = 0; i < childCount; i++) {
-            if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
-                break;
-            }
-            ExpandableNotificationRow child = mChildren.get(i);
-            float childHeight = child.isExpanded(true /* allowOnKeyguard */)
-                    ? child.getMaxExpandHeight()
-                    : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
-            maxContentHeight += childHeight;
-            visibleChildren++;
-        }
-        if (visibleChildren > 0) {
-            maxContentHeight += visibleChildren * mDividerHeight;
-        }
-        return maxContentHeight;
-    }
-
-    public void setActualHeight(int actualHeight) {
-        if (!mUserLocked) {
-            return;
-        }
-        mActualHeight = actualHeight;
-        float fraction = getGroupExpandFraction();
-        boolean showingLowPriority = showingAsLowPriority();
-        updateHeaderTransformation();
-        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
-        int childCount = mChildren.size();
-        for (int i = 0; i < childCount; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
-            float childHeight;
-            if (showingLowPriority) {
-                childHeight = child.getShowingLayout().getMinHeight(false /* likeGroupExpanded */);
-            } else if (child.isExpanded(true /* allowOnKeyguard */)) {
-                childHeight = child.getMaxExpandHeight();
-            } else {
-                childHeight = child.getShowingLayout().getMinHeight(
-                        true /* likeGroupExpanded */);
-            }
-            if (i < maxAllowedVisibleChildren) {
-                float singleLineHeight = child.getShowingLayout().getMinHeight(
-                        false /* likeGroupExpanded */);
-                child.setActualHeight((int) NotificationUtils.interpolate(singleLineHeight,
-                        childHeight, fraction), false);
-            } else {
-                child.setActualHeight((int) childHeight, false);
-            }
-        }
-    }
-
-    public float getGroupExpandFraction() {
-        int visibleChildrenExpandedHeight = showingAsLowPriority() ? getMaxContentHeight()
-                : getVisibleChildrenExpandHeight();
-        int minExpandHeight = getCollapsedHeight();
-        float factor = (mActualHeight - minExpandHeight)
-                / (float) (visibleChildrenExpandedHeight - minExpandHeight);
-        return Math.max(0.0f, Math.min(1.0f, factor));
-    }
-
-    private int getVisibleChildrenExpandHeight() {
-        int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
-                + mNotificatonTopPadding + mDividerHeight;
-        int visibleChildren = 0;
-        int childCount = mChildren.size();
-        int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
-        for (int i = 0; i < childCount; i++) {
-            if (visibleChildren >= maxAllowedVisibleChildren) {
-                break;
-            }
-            ExpandableNotificationRow child = mChildren.get(i);
-            float childHeight = child.isExpanded(true /* allowOnKeyguard */)
-                    ? child.getMaxExpandHeight()
-                    : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
-            intrinsicHeight += childHeight;
-            visibleChildren++;
-        }
-        return intrinsicHeight;
-    }
-
-    public int getMinHeight() {
-        return getMinHeight(mContainingNotification.isShowingAmbient()
-                ? NUMBER_OF_CHILDREN_WHEN_AMBIENT
-                : NUMBER_OF_CHILDREN_WHEN_COLLAPSED, false /* likeHighPriority */);
-    }
-
-    public int getCollapsedHeight() {
-        return getMinHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */),
-                false /* likeHighPriority */);
-    }
-
-    /**
-     * Get the minimum Height for this group.
-     *
-     * @param maxAllowedVisibleChildren the number of children that should be visible
-     * @param likeHighPriority if the height should be calculated as if it were not low priority
-     */
-    private int getMinHeight(int maxAllowedVisibleChildren, boolean likeHighPriority) {
-        if (!likeHighPriority && showingAsLowPriority()) {
-            return mNotificationHeaderLowPriority.getHeight();
-        }
-        int minExpandHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
-        int visibleChildren = 0;
-        boolean firstChild = true;
-        int childCount = mChildren.size();
-        for (int i = 0; i < childCount; i++) {
-            if (visibleChildren >= maxAllowedVisibleChildren) {
-                break;
-            }
-            if (!firstChild) {
-                minExpandHeight += mChildPadding;
-            } else {
-                firstChild = false;
-            }
-            ExpandableNotificationRow child = mChildren.get(i);
-            minExpandHeight += child.getSingleLineView().getHeight();
-            visibleChildren++;
-        }
-        minExpandHeight += mCollapsedBottompadding;
-        return minExpandHeight;
-    }
-
-    public boolean showingAsLowPriority() {
-        return mIsLowPriority && !mContainingNotification.isExpanded();
-    }
-
-    public void setDark(boolean dark, boolean fade, long delay) {
-        if (mOverflowNumber != null) {
-            mHybridGroupManager.setOverflowNumberDark(mOverflowNumber, dark, fade, delay);
-        }
-    }
-
-    public void reInflateViews(OnClickListener listener, StatusBarNotification notification) {
-        if (mNotificationHeader != null) {
-            removeView(mNotificationHeader);
-            mNotificationHeader = null;
-        }
-        if (mNotificationHeaderLowPriority != null) {
-            removeView(mNotificationHeaderLowPriority);
-            mNotificationHeaderLowPriority = null;
-        }
-        if (mNotificationHeaderAmbient != null) {
-            removeView(mNotificationHeaderAmbient);
-            mNotificationHeaderAmbient = null;
-        }
-        recreateNotificationHeader(listener);
-        initDimens();
-        for (int i = 0; i < mDividers.size(); i++) {
-            View prevDivider = mDividers.get(i);
-            int index = indexOfChild(prevDivider);
-            removeView(prevDivider);
-            View divider = inflateDivider();
-            addView(divider, index);
-            mDividers.set(i, divider);
-        }
-        removeView(mOverflowNumber);
-        mOverflowNumber = null;
-        mGroupOverFlowState = null;
-        updateGroupOverflow();
-    }
-
-    public void setUserLocked(boolean userLocked) {
-        mUserLocked = userLocked;
-        if (!mUserLocked) {
-            updateHeaderVisibility(false /* animate */);
-        }
-        int childCount = mChildren.size();
-        for (int i = 0; i < childCount; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
-            child.setUserLocked(userLocked && !showingAsLowPriority());
-        }
-    }
-
-    public void onNotificationUpdated() {
-        mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
-                mContainingNotification.getNotificationColor(),
-                mContainingNotification.getNotificationColorAmbient());
-    }
-
-    public int getPositionInLinearLayout(View childInGroup) {
-        int position = mNotificationHeaderMargin + mCurrentHeaderTranslation
-                + mNotificatonTopPadding;
-
-        for (int i = 0; i < mChildren.size(); i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
-            boolean notGone = child.getVisibility() != View.GONE;
-            if (notGone) {
-                position += mDividerHeight;
-            }
-            if (child == childInGroup) {
-                return position;
-            }
-            if (notGone) {
-                position += child.getIntrinsicHeight();
-            }
-        }
-        return 0;
-    }
-
-    public void setIconsVisible(boolean iconsVisible) {
-        if (mNotificationHeaderWrapper != null) {
-            NotificationHeaderView header = mNotificationHeaderWrapper.getNotificationHeader();
-            if (header != null) {
-                header.getIcon().setForceHidden(!iconsVisible);
-            }
-        }
-        if (mNotificationHeaderWrapperLowPriority != null) {
-            NotificationHeaderView header
-                    = mNotificationHeaderWrapperLowPriority.getNotificationHeader();
-            if (header != null) {
-                header.getIcon().setForceHidden(!iconsVisible);
-            }
-        }
-    }
-
-    public void setClipBottomAmount(int clipBottomAmount) {
-        mClipBottomAmount = clipBottomAmount;
-        updateChildrenClipping();
-    }
-
-    public void setIsLowPriority(boolean isLowPriority) {
-        mIsLowPriority = isLowPriority;
-        if (mContainingNotification != null) { /* we're not yet set up yet otherwise */
-            recreateLowPriorityHeader(null /* existingBuilder */);
-            updateHeaderVisibility(false /* animate */);
-        }
-        if (mUserLocked) {
-            setUserLocked(mUserLocked);
-        }
-    }
-
-    public NotificationHeaderView getVisibleHeader() {
-        NotificationHeaderView header = mNotificationHeader;
-        if (showingAsLowPriority()) {
-            header = mNotificationHeaderLowPriority;
-        }
-        return header;
-    }
-
-    public void onExpansionChanged() {
-        if (mIsLowPriority) {
-            if (mUserLocked) {
-                setUserLocked(mUserLocked);
-            }
-            updateHeaderVisibility(true /* animate */);
-        }
-    }
-
-    public float getIncreasedPaddingAmount() {
-        if (showingAsLowPriority()) {
-            return 0.0f;
-        }
-        return getGroupExpandFraction();
-    }
-
-    @VisibleForTesting
-    public boolean isUserLocked() {
-        return mUserLocked;
-    }
-
-    public void setCurrentBottomRoundness(float currentBottomRoundness) {
-        boolean last = true;
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            ExpandableNotificationRow child = mChildren.get(i);
-            if (child.getVisibility() == View.GONE) {
-                continue;
-            }
-            float bottomRoundness = last ? currentBottomRoundness : 0.0f;
-            child.setBottomRoundness(bottomRoundness, isShown() /* animate */);
-            last = false;
-        }
-    }
-
-    public void setHeaderVisibleAmount(float headerVisibleAmount) {
-        mHeaderVisibleAmount = headerVisibleAmount;
-        mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
deleted file mode 100644
index f98b3d9..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
+++ /dev/null
@@ -1,128 +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.stack;
-
-import android.view.View;
-
-import com.android.systemui.statusbar.ActivatableNotificationView;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-
-import java.util.HashSet;
-
-/**
- * A class that manages the roundness for notification views
- */
-class NotificationRoundnessManager implements OnHeadsUpChangedListener {
-
-    private boolean mExpanded;
-    private ActivatableNotificationView mFirst;
-    private ActivatableNotificationView mLast;
-    private HashSet<View> mAnimatedChildren;
-    private Runnable mRoundingChangedCallback;
-    private ExpandableNotificationRow mTrackedHeadsUp;
-    private float mAppearFraction;
-
-    @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
-        updateRounding(headsUp, false /* animate */);
-    }
-
-    @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
-        updateRounding(headsUp, true /* animate */);
-    }
-
-    public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row,
-            boolean isAnimatingAway) {
-        updateRounding(row, false /* animate */);
-    }
-
-    private void updateRounding(ActivatableNotificationView view, boolean animate) {
-        float topRoundness = getRoundness(view, true /* top */);
-        float bottomRoundness = getRoundness(view, false /* top */);
-        boolean firstChanged = view.setTopRoundness(topRoundness, animate);
-        boolean secondChanged = view.setBottomRoundness(bottomRoundness, animate);
-        if ((view == mFirst || view == mLast) && (firstChanged || secondChanged)) {
-            mRoundingChangedCallback.run();
-        }
-    }
-
-    private float getRoundness(ActivatableNotificationView view, boolean top) {
-        if ((view.isPinned() || view.isHeadsUpAnimatingAway()) && !mExpanded) {
-            return 1.0f;
-        }
-        if (view == mFirst && top) {
-            return 1.0f;
-        }
-        if (view == mLast && !top) {
-            return 1.0f;
-        }
-        if (view == mTrackedHeadsUp && mAppearFraction <= 0.0f) {
-            // If we're pushing up on a headsup the appear fraction is < 0 and it needs to still be
-            // rounded.
-            return 1.0f;
-        }
-        return 0.0f;
-    }
-
-    public void setExpanded(float expandedHeight, float appearFraction) {
-        mExpanded = expandedHeight != 0.0f;
-        mAppearFraction = appearFraction;
-        if (mTrackedHeadsUp != null) {
-            updateRounding(mTrackedHeadsUp, true);
-        }
-    }
-
-    public void setFirstAndLastBackgroundChild(ActivatableNotificationView first,
-            ActivatableNotificationView last) {
-        boolean firstChanged = mFirst != first;
-        boolean lastChanged = mLast != last;
-        if (!firstChanged && !lastChanged) {
-            return;
-        }
-        ActivatableNotificationView oldFirst = mFirst;
-        ActivatableNotificationView oldLast = mLast;
-        mFirst = first;
-        mLast = last;
-        if (firstChanged && oldFirst != null && !oldFirst.isRemoved()) {
-            updateRounding(oldFirst, oldFirst.isShown());
-        }
-        if (lastChanged && oldLast != null && !oldLast.isRemoved()) {
-            updateRounding(oldLast, oldLast.isShown());
-        }
-        if (mFirst != null) {
-            updateRounding(mFirst, mFirst.isShown() && !mAnimatedChildren.contains(mFirst));
-        }
-        if (mLast != null) {
-            updateRounding(mLast, mLast.isShown() && !mAnimatedChildren.contains(mLast));
-        }
-        mRoundingChangedCallback.run();
-    }
-
-    public void setAnimatedChildren(HashSet<View> animatedChildren) {
-        mAnimatedChildren = animatedChildren;
-    }
-
-    public void setOnRoundingChangedCallback(Runnable roundingChangedCallback) {
-        mRoundingChangedCallback = roundingChangedCallback;
-    }
-
-    public void setTrackingHeadsUp(ExpandableNotificationRow row) {
-        mTrackedHeadsUp = row;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
deleted file mode 100644
index 7b9e711..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ /dev/null
@@ -1,5182 +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
- */
-
-package com.android.systemui.statusbar.stack;
-
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
-        .ExpandAnimationParameters;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.TimeAnimator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.FloatRange;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PointF;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Handler;
-import android.service.notification.StatusBarNotification;
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.graphics.ColorUtils;
-import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.util.Log;
-import android.util.MathUtils;
-import android.util.Pair;
-import android.util.Property;
-import android.view.ContextThemeWrapper;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.WindowInsets;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.widget.OverScroller;
-import android.widget.ScrollView;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.keyguard.KeyguardSliceView;
-import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
-import com.android.systemui.ExpandHelper;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.SwipeHelper;
-import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.ActivatableNotificationView;
-import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.FooterView;
-import com.android.systemui.statusbar.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationGuts;
-import com.android.systemui.statusbar.NotificationListContainer;
-import com.android.systemui.statusbar.NotificationLogger;
-import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.NotificationSnooze;
-import com.android.systemui.statusbar.StackScrollerDecorView;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.FakeShadowView;
-import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
-import com.android.systemui.statusbar.policy.ScrollAdapter;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.function.BiConsumer;
-
-/**
- * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
- */
-public class NotificationStackScrollLayout extends ViewGroup
-        implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
-        ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
-        NotificationMenuRowPlugin.OnMenuEventListener, VisibilityLocationProvider,
-        NotificationListContainer {
-
-    public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
-    private static final String TAG = "StackScroller";
-    private static final boolean DEBUG = false;
-    private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
-    private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
-    private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
-    /**
-     * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
-     */
-    private static final int INVALID_POINTER = -1;
-
-    private ExpandHelper mExpandHelper;
-    private NotificationSwipeHelper mSwipeHelper;
-    private boolean mSwipingInProgress;
-    private int mCurrentStackHeight = Integer.MAX_VALUE;
-    private final Paint mBackgroundPaint = new Paint();
-    private final Path mBackgroundPath = new Path();
-    private final boolean mShouldDrawNotificationBackground;
-
-    private float mExpandedHeight;
-    private int mOwnScrollY;
-    private int mMaxLayoutHeight;
-
-    private VelocityTracker mVelocityTracker;
-    private OverScroller mScroller;
-    private Runnable mFinishScrollingCallback;
-    private int mTouchSlop;
-    private int mMinimumVelocity;
-    private int mMaximumVelocity;
-    private int mOverflingDistance;
-    private float mMaxOverScroll;
-    private boolean mIsBeingDragged;
-    private int mLastMotionY;
-    private int mDownX;
-    private int mActivePointerId = INVALID_POINTER;
-    private boolean mTouchIsClick;
-    private float mInitialTouchX;
-    private float mInitialTouchY;
-
-    private Paint mDebugPaint;
-    private int mContentHeight;
-    private int mIntrinsicContentHeight;
-    private int mCollapsedSize;
-    private int mPaddingBetweenElements;
-    private int mIncreasedPaddingBetweenElements;
-    private int mMaxTopPadding;
-    private int mRegularTopPadding;
-    private int mDarkTopPadding;
-    // Current padding, will be either mRegularTopPadding or mDarkTopPadding
-    private int mTopPadding;
-    // Distance between AOD separator and shelf
-    private int mDarkSeparatorPadding;
-    private int mBottomMargin;
-    private int mBottomInset = 0;
-    private float mQsExpansionFraction;
-
-    /**
-     * The algorithm which calculates the properties for our children
-     */
-    protected final StackScrollAlgorithm mStackScrollAlgorithm;
-
-    /**
-     * The current State this Layout is in
-     */
-    private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
-    private final AmbientState mAmbientState;
-    private NotificationGroupManager mGroupManager;
-    private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
-    private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
-    private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
-    private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
-    private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
-    private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
-    private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
-    private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
-    private ArrayList<View> mSwipedOutViews = new ArrayList<>();
-    private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
-    private boolean mAnimationsEnabled;
-    private boolean mChangePositionInProgress;
-    private boolean mChildTransferInProgress;
-
-    /**
-     * The raw amount of the overScroll on the top, which is not rubber-banded.
-     */
-    private float mOverScrolledTopPixels;
-
-    /**
-     * The raw amount of the overScroll on the bottom, which is not rubber-banded.
-     */
-    private float mOverScrolledBottomPixels;
-    private NotificationLogger.OnChildLocationsChangedListener mListener;
-    private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
-    private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
-    private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
-    private boolean mNeedsAnimation;
-    private boolean mTopPaddingNeedsAnimation;
-    private boolean mDimmedNeedsAnimation;
-    private boolean mHideSensitiveNeedsAnimation;
-    private boolean mDarkNeedsAnimation;
-    private int mDarkAnimationOriginIndex;
-    private boolean mActivateNeedsAnimation;
-    private boolean mGoToFullShadeNeedsAnimation;
-    private boolean mIsExpanded = true;
-    private boolean mChildrenUpdateRequested;
-    private boolean mIsExpansionChanging;
-    private boolean mPanelTracking;
-    private boolean mExpandingNotification;
-    private boolean mExpandedInThisMotion;
-    private boolean mShouldShowShelfOnly;
-    protected boolean mScrollingEnabled;
-    protected FooterView mFooterView;
-    protected EmptyShadeView mEmptyShadeView;
-    private boolean mDismissAllInProgress;
-    private boolean mFadeNotificationsOnDismiss;
-
-    /**
-     * Was the scroller scrolled to the top when the down motion was observed?
-     */
-    private boolean mScrolledToTopOnFirstDown;
-    /**
-     * The minimal amount of over scroll which is needed in order to switch to the quick settings
-     * when over scrolling on a expanded card.
-     */
-    private float mMinTopOverScrollToEscape;
-    private int mIntrinsicPadding;
-    private float mStackTranslation;
-    private float mTopPaddingOverflow;
-    private boolean mDontReportNextOverScroll;
-    private boolean mDontClampNextScroll;
-    private boolean mNeedViewResizeAnimation;
-    private View mExpandedGroupView;
-    private boolean mEverythingNeedsAnimation;
-
-    /**
-     * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
-     * This is needed to avoid scrolling too far after the notification was collapsed in the same
-     * motion.
-     */
-    private int mMaxScrollAfterExpand;
-    private ExpandableNotificationRow.LongPressListener mLongPressListener;
-
-    private NotificationMenuRowPlugin mCurrMenuRow;
-    private View mTranslatingParentView;
-    private View mMenuExposedView;
-    boolean mCheckForLeavebehind;
-
-    /**
-     * Should in this touch motion only be scrolling allowed? It's true when the scroller was
-     * animating.
-     */
-    private boolean mOnlyScrollingInThisMotion;
-    private boolean mDisallowDismissInThisMotion;
-    private boolean mDisallowScrollingInThisMotion;
-    private long mGoToFullShadeDelay;
-    private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
-            = new ViewTreeObserver.OnPreDrawListener() {
-        @Override
-        public boolean onPreDraw() {
-            updateForcedScroll();
-            updateChildren();
-            mChildrenUpdateRequested = false;
-            getViewTreeObserver().removeOnPreDrawListener(this);
-            return true;
-        }
-    };
-    private StatusBar mStatusBar;
-    private int[] mTempInt2 = new int[2];
-    private boolean mGenerateChildOrderChangedEvent;
-    private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
-    private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>();
-    private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
-            = new HashSet<>();
-    private HeadsUpManagerPhone mHeadsUpManager;
-    private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
-    private boolean mTrackingHeadsUp;
-    private ScrimController mScrimController;
-    private boolean mForceNoOverlappingRendering;
-    private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
-    private FalsingManager mFalsingManager;
-    private boolean mAnimationRunning;
-    private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
-            = new ViewTreeObserver.OnPreDrawListener() {
-        @Override
-        public boolean onPreDraw() {
-            onPreDrawDuringAnimation();
-            return true;
-        }
-    };
-    private Rect mBackgroundBounds = new Rect();
-    private Rect mStartAnimationRect = new Rect();
-    private Rect mEndAnimationRect = new Rect();
-    private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
-    private boolean mAnimateNextBackgroundBottom;
-    private boolean mAnimateNextBackgroundTop;
-    private ObjectAnimator mBottomAnimator = null;
-    private ObjectAnimator mTopAnimator = null;
-    private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
-    private ActivatableNotificationView mLastVisibleBackgroundChild = null;
-    private int mBgColor;
-    private float mDimAmount;
-    private ValueAnimator mDimAnimator;
-    private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
-    private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            mDimAnimator = null;
-        }
-    };
-    private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
-            = new ValueAnimator.AnimatorUpdateListener() {
-
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            setDimAmount((Float) animation.getAnimatedValue());
-        }
-    };
-    protected ViewGroup mQsContainer;
-    private boolean mContinuousShadowUpdate;
-    private ViewTreeObserver.OnPreDrawListener mShadowUpdater
-            = new ViewTreeObserver.OnPreDrawListener() {
-
-        @Override
-        public boolean onPreDraw() {
-            updateViewShadows();
-            return true;
-        }
-    };
-    private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() {
-        @Override
-        public int compare(ExpandableView view, ExpandableView otherView) {
-            float endY = view.getTranslationY() + view.getActualHeight();
-            float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
-            if (endY < otherEndY) {
-                return -1;
-            } else if (endY > otherEndY) {
-                return 1;
-            } else {
-                // The two notifications end at the same location
-                return 0;
-            }
-        }
-    };
-    private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
-    private boolean mPulsing;
-    private boolean mDrawBackgroundAsSrc;
-    private boolean mFadingOut;
-    private boolean mParentNotFullyVisible;
-    private boolean mGroupExpandedForMeasure;
-    private boolean mScrollable;
-    private View mForcedScroll;
-    private View mNeedingPulseAnimation;
-    private float mDarkAmount = 0f;
-    private static final Property<NotificationStackScrollLayout, Float> DARK_AMOUNT =
-            new FloatProperty<NotificationStackScrollLayout>("darkAmount") {
-                @Override
-                public void setValue(NotificationStackScrollLayout object, float value) {
-                    object.setDarkAmount(value);
-                }
-
-                @Override
-                public Float get(NotificationStackScrollLayout object) {
-                    return object.getDarkAmount();
-                }
-            };
-    private ObjectAnimator mDarkAmountAnimator;
-    private boolean mUsingLightTheme;
-    private boolean mQsExpanded;
-    private boolean mForwardScrollable;
-    private boolean mBackwardScrollable;
-    private NotificationShelf mShelf;
-    private int mMaxDisplayedNotifications = -1;
-    private int mStatusBarHeight;
-    private int mMinInteractionHeight;
-    private boolean mNoAmbient;
-    private final Rect mClipRect = new Rect();
-    private boolean mIsClipped;
-    private Rect mRequestedClipBounds;
-    private boolean mInHeadsUpPinnedMode;
-    private boolean mHeadsUpAnimatingAway;
-    private int mStatusBarState;
-    private int mCachedBackgroundColor;
-    private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
-    private Runnable mAnimateScroll = this::animateScroll;
-    private int mCornerRadius;
-    private int mSidePaddings;
-    private final int mSeparatorWidth;
-    private final int mSeparatorThickness;
-    private final Rect mBackgroundAnimationRect = new Rect();
-    private int mAntiBurnInOffsetX;
-    private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
-    private int mHeadsUpInset;
-    private HeadsUpAppearanceController mHeadsUpAppearanceController;
-
-    public NotificationStackScrollLayout(Context context) {
-        this(context, null);
-    }
-
-    public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        Resources res = getResources();
-
-        mAmbientState = new AmbientState(context);
-        mBgColor = context.getColor(R.color.notification_shade_background_color);
-        int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
-        int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
-        mExpandHelper = new ExpandHelper(getContext(), this,
-                minHeight, maxHeight);
-        mExpandHelper.setEventSource(this);
-        mExpandHelper.setScrollAdapter(this);
-        mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
-        mStackScrollAlgorithm = createStackScrollAlgorithm(context);
-        initView(context);
-        mFalsingManager = FalsingManager.getInstance(context);
-        mShouldDrawNotificationBackground =
-                res.getBoolean(R.bool.config_drawNotificationBackground);
-        mFadeNotificationsOnDismiss =
-                res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
-        mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
-        mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
-        mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
-        mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
-        mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
-        addOnExpandedHeightListener(mRoundnessManager::setExpanded);
-
-        // Blocking helper manager wants to know the expanded state, update as well.
-        NotificationBlockingHelperManager blockingHelperManager =
-                Dependency.get(NotificationBlockingHelperManager.class);
-        addOnExpandedHeightListener((height, unused) -> {
-            blockingHelperManager.setNotificationShadeExpanded(height);
-        });
-
-        updateWillNotDraw();
-        mBackgroundPaint.setAntiAlias(true);
-        if (DEBUG) {
-            mDebugPaint = new Paint();
-            mDebugPaint.setColor(0xffff0000);
-            mDebugPaint.setStrokeWidth(2);
-            mDebugPaint.setStyle(Paint.Style.STROKE);
-        }
-    }
-
-    @Override
-    public NotificationSwipeActionHelper getSwipeActionHelper() {
-        return mSwipeHelper;
-    }
-
-    @Override
-    public void onMenuClicked(View view, int x, int y, MenuItem item) {
-        if (mLongPressListener == null) {
-            return;
-        }
-        if (view instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-            MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
-                    row.getStatusBarNotification().getPackageName());
-        }
-        mLongPressListener.onLongPress(view, x, y, item);
-    }
-
-    @Override
-    public void onMenuReset(View row) {
-        if (mTranslatingParentView != null && row == mTranslatingParentView) {
-            mMenuExposedView = null;
-            mTranslatingParentView = null;
-        }
-    }
-
-    @Override
-    public void onMenuShown(View row) {
-        mMenuExposedView = mTranslatingParentView;
-        if (row instanceof ExpandableNotificationRow) {
-            MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
-                    ((ExpandableNotificationRow) row).getStatusBarNotification()
-                            .getPackageName());
-        }
-        mSwipeHelper.onMenuShown(row);
-    }
-
-    protected void onDraw(Canvas canvas) {
-        if (mShouldDrawNotificationBackground
-                && (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
-            drawBackground(canvas);
-        }
-
-        if (DEBUG) {
-            int y = mTopPadding;
-            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-            y = getLayoutHeight();
-            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-            y = getHeight() - getEmptyBottomMargin();
-            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-        }
-    }
-
-    private void drawBackground(Canvas canvas) {
-        final int lockScreenLeft = mSidePaddings;
-        final int lockScreenRight = getWidth() - mSidePaddings;
-        final int lockScreenTop = mCurrentBounds.top;
-        final int lockScreenBottom = mCurrentBounds.bottom;
-        final int darkLeft = getWidth() / 2 - mSeparatorWidth / 2;
-        final int darkRight = darkLeft + mSeparatorWidth;
-        final int darkTop = (int) (mRegularTopPadding + mSeparatorThickness / 2f);
-        final int darkBottom = darkTop + mSeparatorThickness;
-
-        if (mAmbientState.hasPulsingNotifications()) {
-            // No divider, we have a notification icon instead
-        } else if (mAmbientState.isFullyDark()) {
-            // Only draw divider on AOD if we actually have notifications
-            if (mFirstVisibleBackgroundChild != null) {
-                canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
-            }
-        } else {
-            float inverseDark = 1 - mDarkAmount;
-            float yProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(inverseDark);
-            float xProgress = Interpolators.FAST_OUT_SLOW_IN
-                    .getInterpolation(inverseDark * 2f);
-
-            mBackgroundAnimationRect.set(
-                    (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
-                    (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
-                    (int) MathUtils.lerp(darkRight, lockScreenRight, xProgress),
-                    (int) MathUtils.lerp(darkBottom, lockScreenBottom, yProgress));
-            if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
-                canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
-                        mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
-                        mCornerRadius, mCornerRadius, mBackgroundPaint);
-            }
-        }
-        updateClipping();
-    }
-
-    private void updateBackgroundDimming() {
-        // No need to update the background color if it's not being drawn.
-        if (!mShouldDrawNotificationBackground) {
-            return;
-        }
-
-        float alpha =
-                BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
-        alpha *= 1f - mDarkAmount;
-        // We need to manually blend in the background color.
-        int scrimColor = mScrimController.getBackgroundColor();
-        int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
-
-        // Interpolate between semi-transparent notification panel background color
-        // and white AOD separator.
-        float colorInterpolation = Interpolators.DECELERATE_QUINT.getInterpolation(mDarkAmount);
-        int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation);
-
-        if (mCachedBackgroundColor != color) {
-            mCachedBackgroundColor = color;
-            mBackgroundPaint.setColor(color);
-            invalidate();
-        }
-    }
-
-    private void initView(Context context) {
-        mScroller = new OverScroller(getContext());
-        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
-        setClipChildren(false);
-        final ViewConfiguration configuration = ViewConfiguration.get(context);
-        mTouchSlop = configuration.getScaledTouchSlop();
-        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
-        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-        mOverflingDistance = configuration.getScaledOverflingDistance();
-
-        Resources res = context.getResources();
-        mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
-        mStackScrollAlgorithm.initView(context);
-        mAmbientState.reload(context);
-        mPaddingBetweenElements = Math.max(1,
-                res.getDimensionPixelSize(R.dimen.notification_divider_height));
-        mIncreasedPaddingBetweenElements =
-                res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
-        mMinTopOverScrollToEscape = res.getDimensionPixelSize(
-                R.dimen.min_top_overscroll_to_qs);
-        mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
-        mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
-        mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
-        mMinInteractionHeight = res.getDimensionPixelSize(
-                R.dimen.notification_min_interaction_height);
-        mCornerRadius = res.getDimensionPixelSize(
-                Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
-        mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
-                R.dimen.heads_up_status_bar_padding);
-    }
-
-    public void setDrawBackgroundAsSrc(boolean asSrc) {
-        mDrawBackgroundAsSrc = asSrc;
-        updateSrcDrawing();
-    }
-
-    private void updateSrcDrawing() {
-        if (!mShouldDrawNotificationBackground) {
-            return;
-        }
-
-        mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
-                ? mSrcMode : null);
-        invalidate();
-    }
-
-    private void notifyHeightChangeListener(ExpandableView view) {
-        notifyHeightChangeListener(view, false /* needsAnimation */);
-    }
-
-    private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
-        if (mOnHeightChangedListener != null) {
-            mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
-                MeasureSpec.getMode(widthMeasureSpec));
-        // We need to measure all children even the GONE ones, such that the heights are calculated
-        // correctly as they are used to calculate how many we can fit on the screen.
-        final int size = getChildCount();
-        for (int i = 0; i < size; i++) {
-            measureChild(getChildAt(i), childWidthSpec, heightMeasureSpec);
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        // we layout all our children centered on the top
-        float centerX = getWidth() / 2.0f;
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-            // We need to layout all children even the GONE ones, such that the heights are
-            // calculated correctly as they are used to calculate how many we can fit on the screen
-            float width = child.getMeasuredWidth();
-            float height = child.getMeasuredHeight();
-            child.layout((int) (centerX - width / 2.0f),
-                    0,
-                    (int) (centerX + width / 2.0f),
-                    (int) height);
-        }
-        setMaxLayoutHeight(getHeight());
-        updateContentHeight();
-        clampScrollPosition();
-        requestChildrenUpdate();
-        updateFirstAndLastBackgroundViews();
-        updateAlgorithmLayoutMinHeight();
-    }
-
-    private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
-        if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
-            mNeedViewResizeAnimation = true;
-            mNeedsAnimation = true;
-        }
-    }
-
-    public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
-        mAmbientState.setSpeedBumpIndex(newIndex);
-        mNoAmbient = noAmbient;
-    }
-
-    @Override
-    public void setChildLocationsChangedListener(
-            NotificationLogger.OnChildLocationsChangedListener listener) {
-        mListener = listener;
-    }
-
-    @Override
-    public boolean isInVisibleLocation(ExpandableNotificationRow row) {
-        ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
-        if (childViewState == null) {
-            return false;
-        }
-        if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
-            return false;
-        }
-        if (row.getVisibility() != View.VISIBLE) {
-            return false;
-        }
-        return true;
-    }
-
-    private void setMaxLayoutHeight(int maxLayoutHeight) {
-        mMaxLayoutHeight = maxLayoutHeight;
-        mShelf.setMaxLayoutHeight(maxLayoutHeight);
-        updateAlgorithmHeightAndPadding();
-    }
-
-    private void updateAlgorithmHeightAndPadding() {
-        mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount);
-        mAmbientState.setLayoutHeight(getLayoutHeight());
-        updateAlgorithmLayoutMinHeight();
-        mAmbientState.setTopPadding(mTopPadding);
-    }
-
-    private void updateAlgorithmLayoutMinHeight() {
-        mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
-                ? getLayoutMinHeight() : 0);
-    }
-
-    /**
-     * Updates the children views according to the stack scroll algorithm. Call this whenever
-     * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
-     */
-    private void updateChildren() {
-        updateScrollStateForAddedChildren();
-        mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
-                ? 0
-                : mScroller.getCurrVelocity());
-        mAmbientState.setScrollY(mOwnScrollY);
-        mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
-        if (!isCurrentlyAnimating() && !mNeedsAnimation) {
-            applyCurrentState();
-        } else {
-            startAnimationToState();
-        }
-    }
-
-    private void onPreDrawDuringAnimation() {
-        mShelf.updateAppearance();
-        updateClippingToTopRoundedCorner();
-        if (!mNeedsAnimation && !mChildrenUpdateRequested) {
-            updateBackground();
-        }
-    }
-
-    private void updateClippingToTopRoundedCorner() {
-        Float clipStart = (float) mTopPadding
-                                 + mStackTranslation
-                                 + mAmbientState.getExpandAnimationTopChange();
-        Float clipEnd = clipStart + mCornerRadius;
-        boolean first = true;
-        for (int i = 0; i < getChildCount(); i++) {
-            ExpandableView child = (ExpandableView) getChildAt(i);
-            if (child.getVisibility() == GONE) {
-                continue;
-            }
-            float start = child.getTranslationY();
-            float end = start + child.getActualHeight();
-            boolean clip = clipStart > start && clipStart < end
-                    || clipEnd >= start && clipEnd <= end;
-            clip &= !(first && mOwnScrollY == 0);
-            child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
-                    : ExpandableView.NO_ROUNDNESS);
-            first = false;
-        }
-    }
-
-    private void updateScrollStateForAddedChildren() {
-        if (mChildrenToAddAnimated.isEmpty()) {
-            return;
-        }
-        for (int i = 0; i < getChildCount(); i++) {
-            ExpandableView child = (ExpandableView) getChildAt(i);
-            if (mChildrenToAddAnimated.contains(child)) {
-                int startingPosition = getPositionInLinearLayout(child);
-                float increasedPaddingAmount = child.getIncreasedPaddingAmount();
-                int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
-                        : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
-                int childHeight = getIntrinsicHeight(child) + padding;
-                if (startingPosition < mOwnScrollY) {
-                    // This child starts off screen, so let's keep it offscreen to keep the others visible
-
-                    setOwnScrollY(mOwnScrollY + childHeight);
-                }
-            }
-        }
-        clampScrollPosition();
-    }
-
-    private void updateForcedScroll() {
-        if (mForcedScroll != null && (!mForcedScroll.hasFocus()
-                || !mForcedScroll.isAttachedToWindow())) {
-            mForcedScroll = null;
-        }
-        if (mForcedScroll != null) {
-            ExpandableView expandableView = (ExpandableView) mForcedScroll;
-            int positionInLinearLayout = getPositionInLinearLayout(expandableView);
-            int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
-            int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
-
-            targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
-
-            // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
-            // that it is not visible anymore.
-            if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
-                setOwnScrollY(targetScroll);
-            }
-        }
-    }
-
-    private void requestChildrenUpdate() {
-        if (!mChildrenUpdateRequested) {
-            getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
-            mChildrenUpdateRequested = true;
-            invalidate();
-        }
-    }
-
-    private boolean isCurrentlyAnimating() {
-        return mStateAnimator.isRunning();
-    }
-
-    private void clampScrollPosition() {
-        int scrollRange = getScrollRange();
-        if (scrollRange < mOwnScrollY) {
-            setOwnScrollY(scrollRange);
-        }
-    }
-
-    public int getTopPadding() {
-        return mTopPadding;
-    }
-
-    private void setTopPadding(int topPadding, boolean animate) {
-        if (mRegularTopPadding != topPadding) {
-            mRegularTopPadding = topPadding;
-            mDarkTopPadding = topPadding + mDarkSeparatorPadding;
-            mAmbientState.setDarkTopPadding(mDarkTopPadding);
-            updateAlgorithmHeightAndPadding();
-            updateContentHeight();
-            if (animate && mAnimationsEnabled && mIsExpanded) {
-                mTopPaddingNeedsAnimation = true;
-                mNeedsAnimation =  true;
-            }
-            requestChildrenUpdate();
-            notifyHeightChangeListener(null, animate);
-        }
-    }
-
-    /**
-     * Update the height of the panel.
-     *
-     * @param height the expanded height of the panel
-     */
-    public void setExpandedHeight(float height) {
-        mExpandedHeight = height;
-        setIsExpanded(height > 0);
-        int minExpansionHeight = getMinExpansionHeight();
-        if (height < minExpansionHeight) {
-            mClipRect.left = 0;
-            mClipRect.right = getWidth();
-            mClipRect.top = 0;
-            mClipRect.bottom = (int) height;
-            height = minExpansionHeight;
-            setRequestedClipBounds(mClipRect);
-        } else {
-            setRequestedClipBounds(null);
-        }
-        int stackHeight;
-        float translationY;
-        float appearEndPosition = getAppearEndPosition();
-        float appearStartPosition = getAppearStartPosition();
-        float appearFraction = 1.0f;
-        boolean appearing = height < appearEndPosition;
-        mAmbientState.setAppearing(appearing);
-        if (!appearing) {
-            translationY = 0;
-            if (mShouldShowShelfOnly) {
-                stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
-            } else if (mQsExpanded) {
-                int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
-                int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
-                if (stackStartPosition <= stackEndPosition) {
-                    stackHeight = stackEndPosition;
-                } else {
-                    stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
-                            stackEndPosition, mQsExpansionFraction);
-                }
-            } else {
-                stackHeight = (int) height;
-            }
-        } else {
-            appearFraction = getAppearFraction(height);
-            if (appearFraction >= 0) {
-                translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
-                        appearFraction);
-            } else {
-                // This may happen when pushing up a heads up. We linearly push it up from the
-                // start
-                translationY = height - appearStartPosition + getExpandTranslationStart();
-            }
-            if (isHeadsUpTransition()) {
-                stackHeight = mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
-                translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
-            } else {
-                stackHeight = (int) (height - translationY);
-            }
-        }
-        if (stackHeight != mCurrentStackHeight) {
-            mCurrentStackHeight = stackHeight;
-            updateAlgorithmHeightAndPadding();
-            requestChildrenUpdate();
-        }
-        setStackTranslation(translationY);
-        for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
-            BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
-            listener.accept(mExpandedHeight, appearFraction);
-        }
-    }
-
-    private void setRequestedClipBounds(Rect clipRect) {
-        mRequestedClipBounds = clipRect;
-        updateClipping();
-    }
-
-    /**
-     * Return the height of the content ignoring the footer.
-     */
-    public int getIntrinsicContentHeight() {
-        return mIntrinsicContentHeight;
-    }
-
-    public void updateClipping() {
-        boolean animatingClipping = mDarkAmount > 0 && mDarkAmount < 1;
-        boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
-                && !mHeadsUpAnimatingAway;
-        if (mIsClipped != clipped) {
-            mIsClipped = clipped;
-            updateFadingState();
-        }
-
-        if (animatingClipping) {
-            setClipBounds(mBackgroundAnimationRect);
-        } else if (clipped) {
-            setClipBounds(mRequestedClipBounds);
-        } else {
-            setClipBounds(null);
-        }
-    }
-
-    /**
-     * @return The translation at the beginning when expanding.
-     *         Measured relative to the resting position.
-     */
-    private float getExpandTranslationStart() {
-        return - mTopPadding;
-    }
-
-    /**
-     * @return the position from where the appear transition starts when expanding.
-     *         Measured in absolute height.
-     */
-    private float getAppearStartPosition() {
-        if (isHeadsUpTransition()) {
-            return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
-        }
-        return getMinExpansionHeight();
-    }
-
-    /**
-     * @return the height of the top heads up notification when pinned. This is different from the
-     *         intrinsic height, which also includes whether the notification is system expanded and
-     *         is mainly used when dragging down from a heads up notification.
-     */
-    private int getTopHeadsUpPinnedHeight() {
-        NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
-        if (topEntry == null) {
-            return 0;
-        }
-        ExpandableNotificationRow row = topEntry.row;
-        if (row.isChildInGroup()) {
-            final ExpandableNotificationRow groupSummary
-                    = mGroupManager.getGroupSummary(row.getStatusBarNotification());
-            if (groupSummary != null) {
-                row = groupSummary;
-            }
-        }
-        return row.getPinnedHeadsUpHeight();
-    }
-
-    /**
-     * @return the position from where the appear transition ends when expanding.
-     *         Measured in absolute height.
-     */
-    private float getAppearEndPosition() {
-        int appearPosition;
-        int notGoneChildCount = getNotGoneChildCount();
-        if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
-            if (isHeadsUpTransition()
-                    || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
-                appearPosition = getTopHeadsUpPinnedHeight();
-            } else {
-                appearPosition = 0;
-                if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
-                    appearPosition += mShelf.getIntrinsicHeight();
-                }
-            }
-        } else {
-            appearPosition = mEmptyShadeView.getHeight();
-        }
-        return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
-    }
-
-    private boolean isHeadsUpTransition() {
-        return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null
-                && mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild);
-    }
-
-    /**
-     * @param height the height of the panel
-     * @return the fraction of the appear animation that has been performed
-     */
-    public float getAppearFraction(float height) {
-        float appearEndPosition = getAppearEndPosition();
-        float appearStartPosition = getAppearStartPosition();
-        return (height - appearStartPosition)
-                / (appearEndPosition - appearStartPosition);
-    }
-
-    public float getStackTranslation() {
-        return mStackTranslation;
-    }
-
-    private void setStackTranslation(float stackTranslation) {
-        if (stackTranslation != mStackTranslation) {
-            mStackTranslation = stackTranslation;
-            mAmbientState.setStackTranslation(stackTranslation);
-            requestChildrenUpdate();
-        }
-    }
-
-    /**
-     * Get the current height of the view. This is at most the msize of the view given by a the
-     * layout but it can also be made smaller by setting {@link #mCurrentStackHeight}
-     *
-     * @return either the layout height or the externally defined height, whichever is smaller
-     */
-    private int getLayoutHeight() {
-        return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
-    }
-
-    public int getFirstItemMinHeight() {
-        final ExpandableView firstChild = getFirstChildNotGone();
-        return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
-    }
-
-    public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
-        mLongPressListener = listener;
-    }
-
-    public void setQsContainer(ViewGroup qsContainer) {
-        mQsContainer = qsContainer;
-    }
-
-    /**
-     * Handles cleanup after the given {@code view} has been fully swiped out (including
-     * re-invoking dismiss logic in case the notification has not made its way out yet).
-     */
-    @Override
-    public void onChildDismissed(View view) {
-        ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-        if (!row.isDismissed()) {
-            handleChildViewDismissed(view);
-        }
-        ViewGroup transientContainer = row.getTransientContainer();
-        if (transientContainer != null) {
-            transientContainer.removeTransientView(view);
-        }
-    }
-
-    /**
-     * Starts up notification dismiss and tells the notification, if any, to remove itself from
-     * layout.
-     *
-     * @param view view (e.g. notification) to dismiss from the layout
-     */
-    private void handleChildViewDismissed(View view) {
-        if (mDismissAllInProgress) {
-            return;
-        }
-
-        boolean isBlockingHelperShown = false;
-
-        setSwipingInProgress(false);
-        if (mDragAnimPendingChildren.contains(view)) {
-            // We start the swipe and finish it in the same frame; we don't want a drag animation.
-            mDragAnimPendingChildren.remove(view);
-        }
-        mAmbientState.onDragFinished(view);
-        updateContinuousShadowDrawing();
-
-        if (view instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-            if (row.isHeadsUp()) {
-                mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
-            }
-            isBlockingHelperShown =
-                    row.performDismissWithBlockingHelper(false /* fromAccessibility */);
-        }
-
-        if (!isBlockingHelperShown) {
-            mSwipedOutViews.add(view);
-        }
-        mFalsingManager.onNotificationDismissed();
-        if (mFalsingManager.shouldEnforceBouncer()) {
-            mStatusBar.executeRunnableDismissingKeyguard(
-                    null,
-                    null /* cancelAction */,
-                    false /* dismissShade */,
-                    true /* afterKeyguardGone */,
-                    false /* deferred */);
-        }
-    }
-
-    @Override
-    public void onChildSnappedBack(View animView, float targetLeft) {
-        mAmbientState.onDragFinished(animView);
-        updateContinuousShadowDrawing();
-        if (!mDragAnimPendingChildren.contains(animView)) {
-            if (mAnimationsEnabled) {
-                mSnappedBackChildren.add(animView);
-                mNeedsAnimation = true;
-            }
-            requestChildrenUpdate();
-        } else {
-            // We start the swipe and snap back in the same frame, we don't want any animation
-            mDragAnimPendingChildren.remove(animView);
-        }
-        if (mCurrMenuRow != null && targetLeft == 0) {
-            mCurrMenuRow.resetMenu();
-            mCurrMenuRow = null;
-        }
-    }
-
-    @Override
-    public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
-        // Returning true prevents alpha fading.
-        return !mFadeNotificationsOnDismiss;
-    }
-
-    @Override
-    public void onBeginDrag(View v) {
-        mFalsingManager.onNotificatonStartDismissing();
-        setSwipingInProgress(true);
-        mAmbientState.onBeginDrag(v);
-        updateContinuousShadowDrawing();
-        if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
-            mDragAnimPendingChildren.add(v);
-            mNeedsAnimation = true;
-        }
-        requestChildrenUpdate();
-    }
-
-    public static boolean isPinnedHeadsUp(View v) {
-        if (v instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-            return row.isHeadsUp() && row.isPinned();
-        }
-        return false;
-    }
-
-    private boolean isHeadsUp(View v) {
-        if (v instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-            return row.isHeadsUp();
-        }
-        return false;
-    }
-
-    @Override
-    public void onDragCancelled(View v) {
-        mFalsingManager.onNotificatonStopDismissing();
-        setSwipingInProgress(false);
-    }
-
-    @Override
-    public float getFalsingThresholdFactor() {
-        return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
-    }
-
-    @Override
-    public View getChildAtPosition(MotionEvent ev) {
-        View child = getChildAtPosition(ev.getX(), ev.getY());
-        if (child instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            ExpandableNotificationRow parent = row.getNotificationParent();
-            if (parent != null && parent.areChildrenExpanded()
-                    && (parent.areGutsExposed()
-                            || mMenuExposedView == parent
-                        || (parent.getNotificationChildren().size() == 1
-                                && parent.isClearable()))) {
-                // In this case the group is expanded and showing the menu for the
-                // group, further interaction should apply to the group, not any
-                // child notifications so we use the parent of the child. We also do the same
-                // if we only have a single child.
-                child = parent;
-            }
-        }
-        return child;
-    }
-
-    public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
-        getLocationOnScreen(mTempInt2);
-        float localTouchY = touchY - mTempInt2[1];
-
-        ExpandableView closestChild = null;
-        float minDist = Float.MAX_VALUE;
-
-        // find the view closest to the location, accounting for GONE views
-        final int count = getChildCount();
-        for (int childIdx = 0; childIdx < count; childIdx++) {
-            ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
-            if (slidingChild.getVisibility() == GONE
-                    || slidingChild instanceof StackScrollerDecorView) {
-                continue;
-            }
-            float childTop = slidingChild.getTranslationY();
-            float top = childTop + slidingChild.getClipTopAmount();
-            float bottom = childTop + slidingChild.getActualHeight()
-                    - slidingChild.getClipBottomAmount();
-
-            float dist = Math.min(Math.abs(top - localTouchY), Math.abs(bottom - localTouchY));
-            if (dist < minDist) {
-                closestChild = slidingChild;
-                minDist = dist;
-            }
-        }
-        return closestChild;
-    }
-
-    @Override
-    public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
-        getLocationOnScreen(mTempInt2);
-        return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
-    }
-
-    @Override
-    public ExpandableView getChildAtPosition(float touchX, float touchY) {
-        return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
-
-    }
-
-    /**
-     * Get the child at a certain screen location.
-     *
-     * @param touchX the x coordinate
-     * @param touchY the y coordinate
-     * @param requireMinHeight Whether a minimum height is required for a child to be returned.
-     * @return the child at the given location.
-     */
-    private ExpandableView getChildAtPosition(float touchX, float touchY,
-            boolean requireMinHeight) {
-        // find the view under the pointer, accounting for GONE views
-        final int count = getChildCount();
-        for (int childIdx = 0; childIdx < count; childIdx++) {
-            ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
-            if (slidingChild.getVisibility() != VISIBLE
-                    || slidingChild instanceof StackScrollerDecorView) {
-                continue;
-            }
-            float childTop = slidingChild.getTranslationY();
-            float top = childTop + slidingChild.getClipTopAmount();
-            float bottom = childTop + slidingChild.getActualHeight()
-                    - slidingChild.getClipBottomAmount();
-
-            // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
-            // camera affordance).
-            int left = 0;
-            int right = getWidth();
-
-            if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
-                    && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
-                if (slidingChild instanceof ExpandableNotificationRow) {
-                    ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
-                    if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
-                            && mHeadsUpManager.getTopEntry().row != row
-                            && mGroupManager.getGroupSummary(
-                                mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
-                                != row) {
-                        continue;
-                    }
-                    return row.getViewAtPosition(touchY - childTop);
-                }
-                return slidingChild;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public boolean canChildBeExpanded(View v) {
-        return v instanceof ExpandableNotificationRow
-                && ((ExpandableNotificationRow) v).isExpandable()
-                && !((ExpandableNotificationRow) v).areGutsExposed()
-                && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
-    }
-
-    /* Only ever called as a consequence of an expansion gesture in the shade. */
-    @Override
-    public void setUserExpandedChild(View v, boolean userExpanded) {
-        if (v instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-            if (userExpanded && onKeyguard()) {
-                // Due to a race when locking the screen while touching, a notification may be
-                // expanded even after we went back to keyguard. An example of this happens if
-                // you click in the empty space while expanding a group.
-
-                // We also need to un-user lock it here, since otherwise the content height
-                // calculated might be wrong. We also can't invert the two calls since
-                // un-userlocking it will trigger a layout switch in the content view.
-                row.setUserLocked(false);
-                updateContentHeight();
-                notifyHeightChangeListener(row);
-                return;
-            }
-            row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
-            row.onExpandedByGesture(userExpanded);
-        }
-    }
-
-    @Override
-    public void setExpansionCancelled(View v) {
-        if (v instanceof ExpandableNotificationRow) {
-            ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
-        }
-    }
-
-    @Override
-    public void setUserLockedChild(View v, boolean userLocked) {
-        if (v instanceof ExpandableNotificationRow) {
-            ((ExpandableNotificationRow) v).setUserLocked(userLocked);
-        }
-        cancelLongPress();
-        requestDisallowInterceptTouchEvent(true);
-    }
-
-    @Override
-    public void expansionStateChanged(boolean isExpanding) {
-        mExpandingNotification = isExpanding;
-        if (!mExpandedInThisMotion) {
-            mMaxScrollAfterExpand = mOwnScrollY;
-            mExpandedInThisMotion = true;
-        }
-    }
-
-    @Override
-    public int getMaxExpandHeight(ExpandableView view) {
-        return view.getMaxContentHeight();
-    }
-
-    public void setScrollingEnabled(boolean enable) {
-        mScrollingEnabled = enable;
-    }
-
-    public void lockScrollTo(View v) {
-        if (mForcedScroll == v) {
-            return;
-        }
-        mForcedScroll = v;
-        scrollTo(v);
-    }
-
-    public boolean scrollTo(View v) {
-        ExpandableView expandableView = (ExpandableView) v;
-        int positionInLinearLayout = getPositionInLinearLayout(v);
-        int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
-        int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
-
-        // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
-        // that it is not visible anymore.
-        if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
-            mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
-            mDontReportNextOverScroll = true;
-            animateScroll();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * @return the scroll necessary to make the bottom edge of {@param v} align with the top of
-     *         the IME.
-     */
-    private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
-        return positionInLinearLayout + v.getIntrinsicHeight() +
-                getImeInset() - getHeight() + getTopPadding();
-    }
-
-    @Override
-    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        mBottomInset = insets.getSystemWindowInsetBottom();
-
-        int range = getScrollRange();
-        if (mOwnScrollY > range) {
-            // HACK: We're repeatedly getting staggered insets here while the IME is
-            // animating away. To work around that we'll wait until things have settled.
-            removeCallbacks(mReclamp);
-            postDelayed(mReclamp, 50);
-        } else if (mForcedScroll != null) {
-            // The scroll was requested before we got the actual inset - in case we need
-            // to scroll up some more do so now.
-            scrollTo(mForcedScroll);
-        }
-        return insets;
-    }
-
-    private Runnable mReclamp = new Runnable() {
-        @Override
-        public void run() {
-            int range = getScrollRange();
-            mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
-            mDontReportNextOverScroll = true;
-            mDontClampNextScroll = true;
-            animateScroll();
-        }
-    };
-
-    public void setExpandingEnabled(boolean enable) {
-        mExpandHelper.setEnabled(enable);
-    }
-
-    private boolean isScrollingEnabled() {
-        return mScrollingEnabled;
-    }
-
-    @Override
-    public boolean canChildBeDismissed(View v) {
-        return StackScrollAlgorithm.canChildBeDismissed(v);
-    }
-
-    @Override
-    public boolean isAntiFalsingNeeded() {
-        return onKeyguard();
-    }
-
-    private boolean onKeyguard() {
-        return mStatusBarState == StatusBarState.KEYGUARD;
-    }
-
-    private void setSwipingInProgress(boolean isSwiped) {
-        mSwipingInProgress = isSwiped;
-        if(isSwiped) {
-            requestDisallowInterceptTouchEvent(true);
-        }
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
-        float densityScale = getResources().getDisplayMetrics().density;
-        mSwipeHelper.setDensityScale(densityScale);
-        float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
-        mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
-        initView(getContext());
-    }
-
-    public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
-        mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
-                true /* isDismissAll */);
-    }
-
-    @Override
-    public void snapViewIfNeeded(ExpandableNotificationRow child) {
-        boolean animate = mIsExpanded || isPinnedHeadsUp(child);
-        // If the child is showing the notification menu snap to that
-        float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
-        mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
-    }
-
-    @Override
-    public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
-        return this;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
-                || ev.getActionMasked()== MotionEvent.ACTION_UP;
-        handleEmptySpaceClick(ev);
-        boolean expandWantsIt = false;
-        if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
-            if (isCancelOrUp) {
-                mExpandHelper.onlyObserveMovements(false);
-            }
-            boolean wasExpandingBefore = mExpandingNotification;
-            expandWantsIt = mExpandHelper.onTouchEvent(ev);
-            if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
-                    && !mDisallowScrollingInThisMotion) {
-                dispatchDownEventToScroller(ev);
-            }
-        }
-        boolean scrollerWantsIt = false;
-        if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
-                && !mDisallowScrollingInThisMotion) {
-            scrollerWantsIt = onScrollTouch(ev);
-        }
-        boolean horizontalSwipeWantsIt = false;
-        if (!mIsBeingDragged
-                && !mExpandingNotification
-                && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion
-                && !mDisallowDismissInThisMotion) {
-            horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
-        }
-
-        // Check if we need to clear any snooze leavebehinds
-        NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
-        if (guts != null && !isTouchInView(ev, guts)
-                && guts.getGutsContent() instanceof NotificationSnooze) {
-            NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
-            if ((ns.isExpanded() && isCancelOrUp)
-                    || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
-                // If the leavebehind is expanded we clear it on the next up event, otherwise we
-                // clear it on the next non-horizontal swipe or expand event.
-                checkSnoozeLeavebehind();
-            }
-        }
-        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
-            mCheckForLeavebehind = true;
-        }
-        return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
-    }
-
-    private void dispatchDownEventToScroller(MotionEvent ev) {
-        MotionEvent downEvent = MotionEvent.obtain(ev);
-        downEvent.setAction(MotionEvent.ACTION_DOWN);
-        onScrollTouch(downEvent);
-        downEvent.recycle();
-    }
-
-    @Override
-    public boolean onGenericMotionEvent(MotionEvent event) {
-        if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
-                || mDisallowScrollingInThisMotion) {
-            return false;
-        }
-        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-            switch (event.getAction()) {
-                case MotionEvent.ACTION_SCROLL: {
-                    if (!mIsBeingDragged) {
-                        final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
-                        if (vscroll != 0) {
-                            final int delta = (int) (vscroll * getVerticalScrollFactor());
-                            final int range = getScrollRange();
-                            int oldScrollY = mOwnScrollY;
-                            int newScrollY = oldScrollY - delta;
-                            if (newScrollY < 0) {
-                                newScrollY = 0;
-                            } else if (newScrollY > range) {
-                                newScrollY = range;
-                            }
-                            if (newScrollY != oldScrollY) {
-                                setOwnScrollY(newScrollY);
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return super.onGenericMotionEvent(event);
-    }
-
-    private boolean onScrollTouch(MotionEvent ev) {
-        if (!isScrollingEnabled()) {
-            return false;
-        }
-        if (isInsideQsContainer(ev) && !mIsBeingDragged) {
-            return false;
-        }
-        mForcedScroll = null;
-        initVelocityTrackerIfNotExists();
-        mVelocityTracker.addMovement(ev);
-
-        final int action = ev.getAction();
-
-        switch (action & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_DOWN: {
-                if (getChildCount() == 0 || !isInContentBounds(ev)) {
-                    return false;
-                }
-                boolean isBeingDragged = !mScroller.isFinished();
-                setIsBeingDragged(isBeingDragged);
-                /*
-                 * If being flinged and user touches, stop the fling. isFinished
-                 * will be false if being flinged.
-                 */
-                if (!mScroller.isFinished()) {
-                    mScroller.forceFinished(true);
-                }
-
-                // Remember where the motion event started
-                mLastMotionY = (int) ev.getY();
-                mDownX = (int) ev.getX();
-                mActivePointerId = ev.getPointerId(0);
-                break;
-            }
-            case MotionEvent.ACTION_MOVE:
-                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
-                if (activePointerIndex == -1) {
-                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
-                    break;
-                }
-
-                final int y = (int) ev.getY(activePointerIndex);
-                final int x = (int) ev.getX(activePointerIndex);
-                int deltaY = mLastMotionY - y;
-                final int xDiff = Math.abs(x - mDownX);
-                final int yDiff = Math.abs(deltaY);
-                if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
-                    setIsBeingDragged(true);
-                    if (deltaY > 0) {
-                        deltaY -= mTouchSlop;
-                    } else {
-                        deltaY += mTouchSlop;
-                    }
-                }
-                if (mIsBeingDragged) {
-                    // Scroll to follow the motion event
-                    mLastMotionY = y;
-                    int range = getScrollRange();
-                    if (mExpandedInThisMotion) {
-                        range = Math.min(range, mMaxScrollAfterExpand);
-                    }
-
-                    float scrollAmount;
-                    if (deltaY < 0) {
-                        scrollAmount = overScrollDown(deltaY);
-                    } else {
-                        scrollAmount = overScrollUp(deltaY, range);
-                    }
-
-                    // Calling customOverScrollBy will call onCustomOverScrolled, which
-                    // sets the scrolling if applicable.
-                    if (scrollAmount != 0.0f) {
-                        // The scrolling motion could not be compensated with the
-                        // existing overScroll, we have to scroll the view
-                        customOverScrollBy((int) scrollAmount, mOwnScrollY,
-                                range, getHeight() / 2);
-                        // If we're scrolling, leavebehinds should be dismissed
-                        checkSnoozeLeavebehind();
-                    }
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-                if (mIsBeingDragged) {
-                    final VelocityTracker velocityTracker = mVelocityTracker;
-                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                    int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
-
-                    if (shouldOverScrollFling(initialVelocity)) {
-                        onOverScrollFling(true, initialVelocity);
-                    } else {
-                        if (getChildCount() > 0) {
-                            if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
-                                float currentOverScrollTop = getCurrentOverScrollAmount(true);
-                                if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
-                                    fling(-initialVelocity);
-                                } else {
-                                    onOverScrollFling(false, initialVelocity);
-                                }
-                            } else {
-                                if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
-                                        getScrollRange())) {
-                                    animateScroll();
-                                }
-                            }
-                        }
-                    }
-                    mActivePointerId = INVALID_POINTER;
-                    endDrag();
-                }
-
-                break;
-            case MotionEvent.ACTION_CANCEL:
-                if (mIsBeingDragged && getChildCount() > 0) {
-                    if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
-                        animateScroll();
-                    }
-                    mActivePointerId = INVALID_POINTER;
-                    endDrag();
-                }
-                break;
-            case MotionEvent.ACTION_POINTER_DOWN: {
-                final int index = ev.getActionIndex();
-                mLastMotionY = (int) ev.getY(index);
-                mDownX = (int) ev.getX(index);
-                mActivePointerId = ev.getPointerId(index);
-                break;
-            }
-            case MotionEvent.ACTION_POINTER_UP:
-                onSecondaryPointerUp(ev);
-                mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
-                mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
-                break;
-        }
-        return true;
-    }
-
-    protected boolean isInsideQsContainer(MotionEvent ev) {
-        return ev.getY() < mQsContainer.getBottom();
-    }
-
-    private void onOverScrollFling(boolean open, int initialVelocity) {
-        if (mOverscrollTopChangedListener != null) {
-            mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
-        }
-        mDontReportNextOverScroll = true;
-        setOverScrollAmount(0.0f, true, false);
-    }
-
-    /**
-     * Perform a scroll upwards and adapt the overscroll amounts accordingly
-     *
-     * @param deltaY The amount to scroll upwards, has to be positive.
-     * @return The amount of scrolling to be performed by the scroller,
-     *         not handled by the overScroll amount.
-     */
-    private float overScrollUp(int deltaY, int range) {
-        deltaY = Math.max(deltaY, 0);
-        float currentTopAmount = getCurrentOverScrollAmount(true);
-        float newTopAmount = currentTopAmount - deltaY;
-        if (currentTopAmount > 0) {
-            setOverScrollAmount(newTopAmount, true /* onTop */,
-                    false /* animate */);
-        }
-        // Top overScroll might not grab all scrolling motion,
-        // we have to scroll as well.
-        float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
-        float newScrollY = mOwnScrollY + scrollAmount;
-        if (newScrollY > range) {
-            if (!mExpandedInThisMotion) {
-                float currentBottomPixels = getCurrentOverScrolledPixels(false);
-                // We overScroll on the top
-                setOverScrolledPixels(currentBottomPixels + newScrollY - range,
-                        false /* onTop */,
-                        false /* animate */);
-            }
-            setOwnScrollY(range);
-            scrollAmount = 0.0f;
-        }
-        return scrollAmount;
-    }
-
-    /**
-     * Perform a scroll downward and adapt the overscroll amounts accordingly
-     *
-     * @param deltaY The amount to scroll downwards, has to be negative.
-     * @return The amount of scrolling to be performed by the scroller,
-     *         not handled by the overScroll amount.
-     */
-    private float overScrollDown(int deltaY) {
-        deltaY = Math.min(deltaY, 0);
-        float currentBottomAmount = getCurrentOverScrollAmount(false);
-        float newBottomAmount = currentBottomAmount + deltaY;
-        if (currentBottomAmount > 0) {
-            setOverScrollAmount(newBottomAmount, false /* onTop */,
-                    false /* animate */);
-        }
-        // Bottom overScroll might not grab all scrolling motion,
-        // we have to scroll as well.
-        float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
-        float newScrollY = mOwnScrollY + scrollAmount;
-        if (newScrollY < 0) {
-            float currentTopPixels = getCurrentOverScrolledPixels(true);
-            // We overScroll on the top
-            setOverScrolledPixels(currentTopPixels - newScrollY,
-                    true /* onTop */,
-                    false /* animate */);
-            setOwnScrollY(0);
-            scrollAmount = 0.0f;
-        }
-        return scrollAmount;
-    }
-
-    private void onSecondaryPointerUp(MotionEvent ev) {
-        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
-                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-        final int pointerId = ev.getPointerId(pointerIndex);
-        if (pointerId == mActivePointerId) {
-            // This was our active pointer going up. Choose a new
-            // active pointer and adjust accordingly.
-            // TODO: Make this decision more intelligent.
-            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-            mLastMotionY = (int) ev.getY(newPointerIndex);
-            mActivePointerId = ev.getPointerId(newPointerIndex);
-            if (mVelocityTracker != null) {
-                mVelocityTracker.clear();
-            }
-        }
-    }
-
-    private void initVelocityTrackerIfNotExists() {
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-    }
-
-    private void recycleVelocityTracker() {
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-    }
-
-    private void initOrResetVelocityTracker() {
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        } else {
-            mVelocityTracker.clear();
-        }
-    }
-
-    public void setFinishScrollingCallback(Runnable runnable) {
-        mFinishScrollingCallback = runnable;
-    }
-
-    private void animateScroll() {
-        if (mScroller.computeScrollOffset()) {
-            int oldY = mOwnScrollY;
-            int y = mScroller.getCurrY();
-
-            if (oldY != y) {
-                int range = getScrollRange();
-                if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
-                    float currVelocity = mScroller.getCurrVelocity();
-                    if (currVelocity >= mMinimumVelocity) {
-                        mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
-                    }
-                }
-
-                if (mDontClampNextScroll) {
-                    range = Math.max(range, oldY);
-                }
-                customOverScrollBy(y - oldY, oldY, range,
-                        (int) (mMaxOverScroll));
-            }
-
-            postOnAnimation(mAnimateScroll);
-        } else {
-            mDontClampNextScroll = false;
-            if (mFinishScrollingCallback != null) {
-                mFinishScrollingCallback.run();
-            }
-        }
-    }
-
-    private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
-            int maxOverScrollY) {
-
-        int newScrollY = scrollY + deltaY;
-        final int top = -maxOverScrollY;
-        final int bottom = maxOverScrollY + scrollRangeY;
-
-        boolean clampedY = false;
-        if (newScrollY > bottom) {
-            newScrollY = bottom;
-            clampedY = true;
-        } else if (newScrollY < top) {
-            newScrollY = top;
-            clampedY = true;
-        }
-
-        onCustomOverScrolled(newScrollY, clampedY);
-
-        return clampedY;
-    }
-
-    /**
-     * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
-     * overscroll effect based on numPixels. By default this will also cancel animations on the
-     * same overScroll edge.
-     *
-     * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
-     *                  the rubber-banding logic.
-     * @param onTop Should the effect be applied on top of the scroller.
-     * @param animate Should an animation be performed.
-     */
-    public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
-        setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
-    }
-
-    /**
-     * Set the effective overScroll amount which will be directly reflected in the layout.
-     * By default this will also cancel animations on the same overScroll edge.
-     *
-     * @param amount The amount to overScroll by.
-     * @param onTop Should the effect be applied on top of the scroller.
-     * @param animate Should an animation be performed.
-     */
-    public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
-        setOverScrollAmount(amount, onTop, animate, true);
-    }
-
-    /**
-     * Set the effective overScroll amount which will be directly reflected in the layout.
-     *
-     * @param amount The amount to overScroll by.
-     * @param onTop Should the effect be applied on top of the scroller.
-     * @param animate Should an animation be performed.
-     * @param cancelAnimators Should running animations be cancelled.
-     */
-    public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
-            boolean cancelAnimators) {
-        setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
-    }
-
-    /**
-     * Set the effective overScroll amount which will be directly reflected in the layout.
-     *
-     * @param amount The amount to overScroll by.
-     * @param onTop Should the effect be applied on top of the scroller.
-     * @param animate Should an animation be performed.
-     * @param cancelAnimators Should running animations be cancelled.
-     * @param isRubberbanded The value which will be passed to
-     *                     {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
-     */
-    public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
-            boolean cancelAnimators, boolean isRubberbanded) {
-        if (cancelAnimators) {
-            mStateAnimator.cancelOverScrollAnimators(onTop);
-        }
-        setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
-    }
-
-    private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
-            boolean isRubberbanded) {
-        amount = Math.max(0, amount);
-        if (animate) {
-            mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
-        } else {
-            setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
-            mAmbientState.setOverScrollAmount(amount, onTop);
-            if (onTop) {
-                notifyOverscrollTopListener(amount, isRubberbanded);
-            }
-            requestChildrenUpdate();
-        }
-    }
-
-    private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
-        mExpandHelper.onlyObserveMovements(amount > 1.0f);
-        if (mDontReportNextOverScroll) {
-            mDontReportNextOverScroll = false;
-            return;
-        }
-        if (mOverscrollTopChangedListener != null) {
-            mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
-        }
-    }
-
-    public void setOverscrollTopChangedListener(
-            OnOverscrollTopChangedListener overscrollTopChangedListener) {
-        mOverscrollTopChangedListener = overscrollTopChangedListener;
-    }
-
-    public float getCurrentOverScrollAmount(boolean top) {
-        return mAmbientState.getOverScrollAmount(top);
-    }
-
-    public float getCurrentOverScrolledPixels(boolean top) {
-        return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
-    }
-
-    private void setOverScrolledPixels(float amount, boolean onTop) {
-        if (onTop) {
-            mOverScrolledTopPixels = amount;
-        } else {
-            mOverScrolledBottomPixels = amount;
-        }
-    }
-
-    private void onCustomOverScrolled(int scrollY, boolean clampedY) {
-        // Treat animating scrolls differently; see #computeScroll() for why.
-        if (!mScroller.isFinished()) {
-            setOwnScrollY(scrollY);
-            if (clampedY) {
-                springBack();
-            } else {
-                float overScrollTop = getCurrentOverScrollAmount(true);
-                if (mOwnScrollY < 0) {
-                    notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
-                } else {
-                    notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
-                }
-            }
-        } else {
-            setOwnScrollY(scrollY);
-        }
-    }
-
-    private void springBack() {
-        int scrollRange = getScrollRange();
-        boolean overScrolledTop = mOwnScrollY <= 0;
-        boolean overScrolledBottom = mOwnScrollY >= scrollRange;
-        if (overScrolledTop || overScrolledBottom) {
-            boolean onTop;
-            float newAmount;
-            if (overScrolledTop) {
-                onTop = true;
-                newAmount = -mOwnScrollY;
-                setOwnScrollY(0);
-                mDontReportNextOverScroll = true;
-            } else {
-                onTop = false;
-                newAmount = mOwnScrollY - scrollRange;
-                setOwnScrollY(scrollRange);
-            }
-            setOverScrollAmount(newAmount, onTop, false);
-            setOverScrollAmount(0.0f, onTop, true);
-            mScroller.forceFinished(true);
-        }
-    }
-
-    private int getScrollRange() {
-        int contentHeight = getContentHeight();
-        int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
-        int imeInset = getImeInset();
-        scrollRange += Math.min(imeInset, Math.max(0,
-                getContentHeight() - (getHeight() - imeInset)));
-        return scrollRange;
-    }
-
-    private int getImeInset() {
-        return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
-    }
-
-    /**
-     * @return the first child which has visibility unequal to GONE
-     */
-    public ExpandableView getFirstChildNotGone() {
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child.getVisibility() != View.GONE && child != mShelf) {
-                return (ExpandableView) child;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @return the child before the given view which has visibility unequal to GONE
-     */
-    public ExpandableView getViewBeforeView(ExpandableView view) {
-        ExpandableView previousView = null;
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child == view) {
-                return previousView;
-            }
-            if (child.getVisibility() != View.GONE) {
-                previousView = (ExpandableView) child;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @return The first child which has visibility unequal to GONE which is currently below the
-     *         given translationY or equal to it.
-     */
-    private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child.getVisibility() == View.GONE) {
-                continue;
-            }
-            float rowTranslation = child.getTranslationY();
-            if (rowTranslation >= translationY) {
-                return child;
-            } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
-                    List<ExpandableNotificationRow> notificationChildren =
-                            row.getNotificationChildren();
-                    for (int childIndex = 0; childIndex < notificationChildren.size();
-                            childIndex++) {
-                        ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
-                        if (rowChild.getTranslationY() + rowTranslation >= translationY) {
-                            return rowChild;
-                        }
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @return the last child which has visibility unequal to GONE
-     */
-    public View getLastChildNotGone() {
-        int childCount = getChildCount();
-        for (int i = childCount - 1; i >= 0; i--) {
-            View child = getChildAt(i);
-            if (child.getVisibility() != View.GONE && child != mShelf) {
-                return child;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @return the number of children which have visibility unequal to GONE
-     */
-    public int getNotGoneChildCount() {
-        int childCount = getChildCount();
-        int count = 0;
-        for (int i = 0; i < childCount; i++) {
-            ExpandableView child = (ExpandableView) getChildAt(i);
-            if (child.getVisibility() != View.GONE && !child.willBeGone() && child != mShelf) {
-                count++;
-            }
-        }
-        return count;
-    }
-
-    public int getContentHeight() {
-        return mContentHeight;
-    }
-
-    private void updateContentHeight() {
-        int height = 0;
-        float previousPaddingRequest = mPaddingBetweenElements;
-        float previousPaddingAmount = 0.0f;
-        int numShownItems = 0;
-        boolean finish = false;
-        int maxDisplayedNotifications = mAmbientState.isFullyDark()
-                ? (hasPulsingNotifications() ? 1 : 0)
-                : mMaxDisplayedNotifications;
-
-        for (int i = 0; i < getChildCount(); i++) {
-            ExpandableView expandableView = (ExpandableView) getChildAt(i);
-            boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard();
-            if (expandableView.getVisibility() != View.GONE
-                    && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) {
-                boolean limitReached = maxDisplayedNotifications != -1
-                        && numShownItems >= maxDisplayedNotifications;
-                boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark()
-                        && hasPulsingNotifications()
-                        && expandableView instanceof ExpandableNotificationRow
-                        && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
-                if (limitReached || notificationOnAmbientThatIsNotPulsing) {
-                    expandableView = mShelf;
-                    finish = true;
-                }
-                float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
-                float padding;
-                if (increasedPaddingAmount >= 0.0f) {
-                    padding = (int) NotificationUtils.interpolate(
-                            previousPaddingRequest,
-                            mIncreasedPaddingBetweenElements,
-                            increasedPaddingAmount);
-                    previousPaddingRequest = (int) NotificationUtils.interpolate(
-                            mPaddingBetweenElements,
-                            mIncreasedPaddingBetweenElements,
-                            increasedPaddingAmount);
-                } else {
-                    int ownPadding = (int) NotificationUtils.interpolate(
-                            0,
-                            mPaddingBetweenElements,
-                            1.0f + increasedPaddingAmount);
-                    if (previousPaddingAmount > 0.0f) {
-                        padding = (int) NotificationUtils.interpolate(
-                                ownPadding,
-                                mIncreasedPaddingBetweenElements,
-                                previousPaddingAmount);
-                    } else {
-                        padding = ownPadding;
-                    }
-                    previousPaddingRequest = ownPadding;
-                }
-                if (height != 0) {
-                    height += padding;
-                }
-                previousPaddingAmount = increasedPaddingAmount;
-                height += expandableView.getIntrinsicHeight();
-                numShownItems++;
-                if (finish) {
-                    break;
-                }
-            }
-        }
-        mIntrinsicContentHeight = height;
-        mContentHeight = height + mTopPadding + mBottomMargin;
-        updateScrollability();
-        clampScrollPosition();
-        mAmbientState.setLayoutMaxHeight(mContentHeight);
-    }
-
-    private boolean isPulsing(NotificationData.Entry entry) {
-        return mAmbientState.isPulsing(entry);
-    }
-
-    @Override
-    public boolean hasPulsingNotifications() {
-        return mPulsing;
-    }
-
-    private void updateScrollability() {
-        boolean scrollable = !mQsExpanded && getScrollRange() > 0;
-        if (scrollable != mScrollable) {
-            mScrollable = scrollable;
-            setFocusable(scrollable);
-            updateForwardAndBackwardScrollability();
-        }
-    }
-
-    private void updateForwardAndBackwardScrollability() {
-        boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
-        boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
-        boolean changed = forwardScrollable != mForwardScrollable
-                || backwardsScrollable != mBackwardScrollable;
-        mForwardScrollable = forwardScrollable;
-        mBackwardScrollable = backwardsScrollable;
-        if (changed) {
-            sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-        }
-    }
-
-    private void updateBackground() {
-        // No need to update the background color if it's not being drawn.
-        if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
-            return;
-        }
-
-        updateBackgroundBounds();
-        if (!mCurrentBounds.equals(mBackgroundBounds)) {
-            boolean animate = mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom
-                    || areBoundsAnimating();
-            if (!isExpanded()) {
-                abortBackgroundAnimators();
-                animate = false;
-            }
-            if (animate) {
-                startBackgroundAnimation();
-            } else {
-                mCurrentBounds.set(mBackgroundBounds);
-                applyCurrentBackgroundBounds();
-            }
-        } else {
-            abortBackgroundAnimators();
-        }
-        mAnimateNextBackgroundBottom = false;
-        mAnimateNextBackgroundTop = false;
-    }
-
-    private void abortBackgroundAnimators() {
-        if (mBottomAnimator != null) {
-            mBottomAnimator.cancel();
-        }
-        if (mTopAnimator != null) {
-            mTopAnimator.cancel();
-        }
-    }
-
-    private boolean areBoundsAnimating() {
-        return mBottomAnimator != null || mTopAnimator != null;
-    }
-
-    private void startBackgroundAnimation() {
-        // left and right are always instantly applied
-        mCurrentBounds.left = mBackgroundBounds.left;
-        mCurrentBounds.right = mBackgroundBounds.right;
-        startBottomAnimation();
-        startTopAnimation();
-    }
-
-    private void startTopAnimation() {
-        int previousEndValue = mEndAnimationRect.top;
-        int newEndValue = mBackgroundBounds.top;
-        ObjectAnimator previousAnimator = mTopAnimator;
-        if (previousAnimator != null && previousEndValue == newEndValue) {
-            return;
-        }
-        if (!mAnimateNextBackgroundTop) {
-            // just a local update was performed
-            if (previousAnimator != null) {
-                // we need to increase all animation keyframes of the previous animator by the
-                // relative change to the end value
-                int previousStartValue = mStartAnimationRect.top;
-                PropertyValuesHolder[] values = previousAnimator.getValues();
-                values[0].setIntValues(previousStartValue, newEndValue);
-                mStartAnimationRect.top = previousStartValue;
-                mEndAnimationRect.top = newEndValue;
-                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
-                return;
-            } else {
-                // no new animation needed, let's just apply the value
-                setBackgroundTop(newEndValue);
-                return;
-            }
-        }
-        if (previousAnimator != null) {
-            previousAnimator.cancel();
-        }
-        ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
-                mCurrentBounds.top, newEndValue);
-        Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
-        animator.setInterpolator(interpolator);
-        animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-        // remove the tag when the animation is finished
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mStartAnimationRect.top = -1;
-                mEndAnimationRect.top = -1;
-                mTopAnimator = null;
-            }
-        });
-        animator.start();
-        mStartAnimationRect.top = mCurrentBounds.top;
-        mEndAnimationRect.top = newEndValue;
-        mTopAnimator = animator;
-    }
-
-    private void startBottomAnimation() {
-        int previousStartValue = mStartAnimationRect.bottom;
-        int previousEndValue = mEndAnimationRect.bottom;
-        int newEndValue = mBackgroundBounds.bottom;
-        ObjectAnimator previousAnimator = mBottomAnimator;
-        if (previousAnimator != null && previousEndValue == newEndValue) {
-            return;
-        }
-        if (!mAnimateNextBackgroundBottom) {
-            // just a local update was performed
-            if (previousAnimator != null) {
-                // we need to increase all animation keyframes of the previous animator by the
-                // relative change to the end value
-                PropertyValuesHolder[] values = previousAnimator.getValues();
-                values[0].setIntValues(previousStartValue, newEndValue);
-                mStartAnimationRect.bottom = previousStartValue;
-                mEndAnimationRect.bottom = newEndValue;
-                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
-                return;
-            } else {
-                // no new animation needed, let's just apply the value
-                setBackgroundBottom(newEndValue);
-                return;
-            }
-        }
-        if (previousAnimator != null) {
-            previousAnimator.cancel();
-        }
-        ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
-                mCurrentBounds.bottom, newEndValue);
-        Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
-        animator.setInterpolator(interpolator);
-        animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-        // remove the tag when the animation is finished
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mStartAnimationRect.bottom = -1;
-                mEndAnimationRect.bottom = -1;
-                mBottomAnimator = null;
-            }
-        });
-        animator.start();
-        mStartAnimationRect.bottom = mCurrentBounds.bottom;
-        mEndAnimationRect.bottom = newEndValue;
-        mBottomAnimator = animator;
-    }
-
-    private void setBackgroundTop(int top) {
-        mCurrentBounds.top = top;
-        applyCurrentBackgroundBounds();
-    }
-
-    public void setBackgroundBottom(int bottom) {
-        mCurrentBounds.bottom = bottom;
-        applyCurrentBackgroundBounds();
-    }
-
-    private void applyCurrentBackgroundBounds() {
-        // If the background of the notification is not being drawn, then there is no need to
-        // exclude an area in the scrim. Rather, the scrim's color should serve as the background.
-        if (!mShouldDrawNotificationBackground) {
-            return;
-        }
-
-        final boolean awake = mDarkAmount != 0 || mAmbientState.isDark();
-        mScrimController.setExcludedBackgroundArea(
-                mFadingOut || mParentNotFullyVisible || awake || mIsClipped ? null
-                        : mCurrentBounds);
-        invalidate();
-    }
-
-    /**
-     * Update the background bounds to the new desired bounds
-     */
-    private void updateBackgroundBounds() {
-        getLocationInWindow(mTempInt2);
-        mBackgroundBounds.left = mTempInt2[0] + mSidePaddings;
-        mBackgroundBounds.right = mTempInt2[0] + getWidth() - mSidePaddings;
-
-        if (!mIsExpanded) {
-            mBackgroundBounds.top = 0;
-            mBackgroundBounds.bottom = 0;
-            return;
-        }
-        ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
-        int top = 0;
-        if (firstView != null) {
-            // Round Y up to avoid seeing the background during animation
-            int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
-            if (mAnimateNextBackgroundTop
-                    || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
-                    || mTopAnimator != null && mEndAnimationRect.top == 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());
-            }
-        }
-        ActivatableNotificationView lastView =
-                mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
-                        ? mShelf
-                        : mLastVisibleBackgroundChild;
-        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
-                    || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
-                    || mBottomAnimator != null && mEndAnimationRect.bottom == 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());
-            }
-        } else {
-            top = mTopPadding;
-            bottom = top;
-        }
-        if (mStatusBarState != StatusBarState.KEYGUARD) {
-            top = (int) Math.max(mTopPadding + mStackTranslation, top);
-        } else {
-            // otherwise the animation from the shade to the keyguard will jump as it's maxed
-            top = Math.max(0, top);
-        }
-        mBackgroundBounds.top = top;
-        mBackgroundBounds.bottom = Math.max(bottom, top);
-    }
-
-    private ActivatableNotificationView getFirstPinnedHeadsUp() {
-        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()) {
-                    return row;
-                }
-            }
-        }
-        return null;
-    }
-
-    private ActivatableNotificationView getLastChildWithBackground() {
-        int childCount = getChildCount();
-        for (int i = childCount - 1; i >= 0; i--) {
-            View child = getChildAt(i);
-            if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
-                    && child != mShelf) {
-                return (ActivatableNotificationView) child;
-            }
-        }
-        return null;
-    }
-
-    private ActivatableNotificationView getFirstChildWithBackground() {
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child.getVisibility() != View.GONE && child instanceof ActivatableNotificationView
-                    && child != mShelf) {
-                return (ActivatableNotificationView) child;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Fling the scroll view
-     *
-     * @param velocityY The initial velocity in the Y direction. Positive
-     *                  numbers mean that the finger/cursor is moving down the screen,
-     *                  which means we want to scroll towards the top.
-     */
-    protected void fling(int velocityY) {
-        if (getChildCount() > 0) {
-            int scrollRange = getScrollRange();
-
-            float topAmount = getCurrentOverScrollAmount(true);
-            float bottomAmount = getCurrentOverScrollAmount(false);
-            if (velocityY < 0 && topAmount > 0) {
-                setOwnScrollY(mOwnScrollY - (int) topAmount);
-                mDontReportNextOverScroll = true;
-                setOverScrollAmount(0, true, false);
-                mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
-                        * mOverflingDistance + topAmount;
-            } else if (velocityY > 0 && bottomAmount > 0) {
-                setOwnScrollY((int) (mOwnScrollY + bottomAmount));
-                setOverScrollAmount(0, false, false);
-                mMaxOverScroll = Math.abs(velocityY) / 1000f
-                        * getRubberBandFactor(false /* onTop */) * mOverflingDistance
-                        +  bottomAmount;
-            } else {
-                // it will be set once we reach the boundary
-                mMaxOverScroll = 0.0f;
-            }
-            int minScrollY = Math.max(0, scrollRange);
-            if (mExpandedInThisMotion) {
-                minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
-            }
-            mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
-                    mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
-
-            animateScroll();
-        }
-    }
-
-    /**
-     * @return Whether a fling performed on the top overscroll edge lead to the expanded
-     * overScroll view (i.e QS).
-     */
-    private boolean shouldOverScrollFling(int initialVelocity) {
-        float topOverScroll = getCurrentOverScrollAmount(true);
-        return mScrolledToTopOnFirstDown
-                && !mExpandedInThisMotion
-                && topOverScroll > mMinTopOverScrollToEscape
-                && initialVelocity > 0;
-    }
-
-    /**
-     * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
-     * account.
-     *
-     * @param qsHeight the top padding imposed by the quick settings panel
-     * @param animate whether to animate the change
-     * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
-     *                               {@code qsHeight} is the final top padding
-     */
-    public void updateTopPadding(float qsHeight, boolean animate,
-            boolean ignoreIntrinsicPadding) {
-        int topPadding = (int) qsHeight;
-        int minStackHeight = getLayoutMinHeight();
-        if (topPadding + minStackHeight > getHeight()) {
-            mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
-        } else {
-            mTopPaddingOverflow = 0;
-        }
-        setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
-                animate);
-        setExpandedHeight(mExpandedHeight);
-    }
-
-    public void setMaxTopPadding(int maxTopPadding) {
-        mMaxTopPadding = maxTopPadding;
-    }
-
-    public int getLayoutMinHeight() {
-        if (isHeadsUpTransition()) {
-            return getTopHeadsUpPinnedHeight();
-        }
-        return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
-    }
-
-    public int getFirstChildIntrinsicHeight() {
-        final ExpandableView firstChild = getFirstChildNotGone();
-        int firstChildMinHeight = firstChild != null
-                ? firstChild.getIntrinsicHeight()
-                : mEmptyShadeView != null
-                        ? mEmptyShadeView.getIntrinsicHeight()
-                        : mCollapsedSize;
-        if (mOwnScrollY > 0) {
-            firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
-        }
-        return firstChildMinHeight;
-    }
-
-    public float getTopPaddingOverflow() {
-        return mTopPaddingOverflow;
-    }
-
-    public int getPeekHeight() {
-        final ExpandableView firstChild = getFirstChildNotGone();
-        final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
-                : mCollapsedSize;
-        int shelfHeight = 0;
-        if (mLastVisibleBackgroundChild != null && mShelf.getVisibility() != GONE) {
-            shelfHeight = mShelf.getIntrinsicHeight();
-        }
-        return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
-    }
-
-    private int clampPadding(int desiredPadding) {
-        return Math.max(desiredPadding, mIntrinsicPadding);
-    }
-
-    private float getRubberBandFactor(boolean onTop) {
-        if (!onTop) {
-            return RUBBER_BAND_FACTOR_NORMAL;
-        }
-        if (mExpandedInThisMotion) {
-            return RUBBER_BAND_FACTOR_AFTER_EXPAND;
-        } else if (mIsExpansionChanging || mPanelTracking) {
-            return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
-        } else if (mScrolledToTopOnFirstDown) {
-            return 1.0f;
-        }
-        return RUBBER_BAND_FACTOR_NORMAL;
-    }
-
-    /**
-     * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
-     * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
-     * overscroll view (e.g. expand QS).
-     */
-    private boolean isRubberbanded(boolean onTop) {
-        return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
-                || !mScrolledToTopOnFirstDown;
-    }
-
-    private void endDrag() {
-        setIsBeingDragged(false);
-
-        recycleVelocityTracker();
-
-        if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
-            setOverScrollAmount(0, true /* onTop */, true /* animate */);
-        }
-        if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
-            setOverScrollAmount(0, false /* onTop */, true /* animate */);
-        }
-    }
-
-    private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
-        ev.offsetLocation(sourceView.getX(), sourceView.getY());
-        ev.offsetLocation(-targetView.getX(), -targetView.getY());
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        initDownStates(ev);
-        handleEmptySpaceClick(ev);
-        boolean expandWantsIt = false;
-        if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
-            expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
-        }
-        boolean scrollWantsIt = false;
-        if (!mSwipingInProgress && !mExpandingNotification) {
-            scrollWantsIt = onInterceptTouchEventScroll(ev);
-        }
-        boolean swipeWantsIt = false;
-        if (!mIsBeingDragged
-                && !mExpandingNotification
-                && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion
-                && !mDisallowDismissInThisMotion) {
-            swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
-        }
-        // Check if we need to clear any snooze leavebehinds
-        boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
-        NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
-        if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
-                && !scrollWantsIt) {
-            mCheckForLeavebehind = false;
-            mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
-                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-        }
-        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
-            mCheckForLeavebehind = true;
-        }
-        return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
-    }
-
-    private void handleEmptySpaceClick(MotionEvent ev) {
-        switch (ev.getActionMasked()) {
-            case MotionEvent.ACTION_MOVE:
-                if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
-                        || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) {
-                    mTouchIsClick = false;
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-                if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
-                        isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
-                    mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
-                }
-                break;
-        }
-    }
-
-    private void initDownStates(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mExpandedInThisMotion = false;
-            mOnlyScrollingInThisMotion = !mScroller.isFinished();
-            mDisallowScrollingInThisMotion = false;
-            mDisallowDismissInThisMotion = false;
-            mTouchIsClick = true;
-            mInitialTouchX = ev.getX();
-            mInitialTouchY = ev.getY();
-        }
-    }
-
-    public void setChildTransferInProgress(boolean childTransferInProgress) {
-        mChildTransferInProgress = childTransferInProgress;
-    }
-
-    @Override
-    public void onViewRemoved(View child) {
-        super.onViewRemoved(child);
-        // we only call our internal methods if this is actually a removal and not just a
-        // notification which becomes a child notification
-        if (!mChildTransferInProgress) {
-            onViewRemovedInternal(child, this);
-        }
-    }
-
-    @Override
-    public void cleanUpViewState(View child) {
-        if (child == mTranslatingParentView) {
-            mTranslatingParentView = null;
-        }
-        mCurrentStackScrollState.removeViewStateForView(child);
-    }
-
-    @Override
-    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        super.requestDisallowInterceptTouchEvent(disallowIntercept);
-        if (disallowIntercept) {
-            cancelLongPress();
-        }
-    }
-
-    private void onViewRemovedInternal(View child, ViewGroup container) {
-        if (mChangePositionInProgress) {
-            // This is only a position change, don't do anything special
-            return;
-        }
-        ExpandableView expandableView = (ExpandableView) child;
-        expandableView.setOnHeightChangedListener(null);
-        mCurrentStackScrollState.removeViewStateForView(child);
-        updateScrollStateForRemovedChild(expandableView);
-        boolean animationGenerated = generateRemoveAnimation(child);
-        if (animationGenerated) {
-            if (!mSwipedOutViews.contains(child)
-                    || Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
-                container.addTransientView(child, 0);
-                expandableView.setTransientContainer(container);
-            }
-        } else {
-            mSwipedOutViews.remove(child);
-        }
-        updateAnimationState(false, child);
-
-        focusNextViewIfFocused(child);
-    }
-
-    private void focusNextViewIfFocused(View view) {
-        if (view instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-            if (row.shouldRefocusOnDismiss()) {
-                View nextView = row.getChildAfterViewWhenDismissed();
-                if (nextView == null) {
-                    View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
-                    nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
-                            ? groupParentWhenDismissed.getTranslationY()
-                            : view.getTranslationY(), true /* ignoreChildren */);
-                }
-                if (nextView != null) {
-                    nextView.requestAccessibilityFocus();
-                }
-            }
-        }
-
-    }
-
-    private boolean isChildInGroup(View child) {
-        return child instanceof ExpandableNotificationRow
-                && mGroupManager.isChildInGroupWithSummary(
-                        ((ExpandableNotificationRow) child).getStatusBarNotification());
-    }
-
-    /**
-     * Generate a remove animation for a child view.
-     *
-     * @param child The view to generate the remove animation for.
-     * @return Whether an animation was generated.
-     */
-    private boolean generateRemoveAnimation(View child) {
-        if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
-            mAddedHeadsUpChildren.remove(child);
-            return false;
-        }
-        if (isClickedHeadsUp(child)) {
-            // An animation is already running, add it transiently
-            mClearTransientViewsWhenFinished.add((ExpandableView) child);
-            return true;
-        }
-        if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
-            if (!mChildrenToAddAnimated.contains(child)) {
-                // Generate Animations
-                mChildrenToRemoveAnimated.add(child);
-                mNeedsAnimation = true;
-                return true;
-            } else {
-                mChildrenToAddAnimated.remove(child);
-                mFromMoreCardAdditions.remove(child);
-                return false;
-            }
-        }
-        return false;
-    }
-
-    private boolean isClickedHeadsUp(View child) {
-        return HeadsUpUtil.isClickedHeadsUpNotification(child);
-    }
-
-    /**
-     * Remove a removed child view from the heads up animations if it was just added there
-     *
-     * @return whether any child was removed from the list to animate
-     */
-    private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
-        boolean hasAddEvent = false;
-        for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
-            ExpandableNotificationRow row = eventPair.first;
-            boolean isHeadsUp = eventPair.second;
-            if (child == row) {
-                mTmpList.add(eventPair);
-                hasAddEvent |= isHeadsUp;
-            }
-        }
-        if (hasAddEvent) {
-            // This child was just added lets remove all events.
-            mHeadsUpChangeAnimations.removeAll(mTmpList);
-            ((ExpandableNotificationRow ) child).setHeadsUpAnimatingAway(false);
-        }
-        mTmpList.clear();
-        return hasAddEvent;
-    }
-
-    /**
-     * @param child the child to query
-     * @return whether a view is not a top level child but a child notification and that group is
-     *         not expanded
-     */
-    private boolean isChildInInvisibleGroup(View child) {
-        if (child instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            ExpandableNotificationRow groupSummary =
-                    mGroupManager.getGroupSummary(row.getStatusBarNotification());
-            if (groupSummary != null && groupSummary != row) {
-                return row.getVisibility() == View.INVISIBLE;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Updates the scroll position when a child was removed
-     *
-     * @param removedChild the removed child
-     */
-    private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
-        int startingPosition = getPositionInLinearLayout(removedChild);
-        float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
-        int padding;
-        if (increasedPaddingAmount >= 0) {
-            padding = (int) NotificationUtils.interpolate(
-                    mPaddingBetweenElements,
-                    mIncreasedPaddingBetweenElements,
-                    increasedPaddingAmount);
-        } else {
-            padding = (int) NotificationUtils.interpolate(
-                    0,
-                    mPaddingBetweenElements,
-                    1.0f + increasedPaddingAmount);
-        }
-        int childHeight = getIntrinsicHeight(removedChild) + padding;
-        int endPosition = startingPosition + childHeight;
-        if (endPosition <= mOwnScrollY) {
-            // This child is fully scrolled of the top, so we have to deduct its height from the
-            // scrollPosition
-            setOwnScrollY(mOwnScrollY - childHeight);
-        } else if (startingPosition < mOwnScrollY) {
-            // This child is currently being scrolled into, set the scroll position to the start of
-            // this child
-            setOwnScrollY(startingPosition);
-        }
-    }
-
-    private int getIntrinsicHeight(View view) {
-        if (view instanceof ExpandableView) {
-            ExpandableView expandableView = (ExpandableView) view;
-            return expandableView.getIntrinsicHeight();
-        }
-        return view.getHeight();
-    }
-
-    public int getPositionInLinearLayout(View requestedView) {
-        ExpandableNotificationRow childInGroup = null;
-        ExpandableNotificationRow requestedRow = null;
-        if (isChildInGroup(requestedView)) {
-            // We're asking for a child in a group. Calculate the position of the parent first,
-            // then within the parent.
-            childInGroup = (ExpandableNotificationRow) requestedView;
-            requestedView = requestedRow = childInGroup.getNotificationParent();
-        }
-        int position = 0;
-        float previousPaddingRequest = mPaddingBetweenElements;
-        float previousPaddingAmount = 0.0f;
-        for (int i = 0; i < getChildCount(); i++) {
-            ExpandableView child = (ExpandableView) getChildAt(i);
-            boolean notGone = child.getVisibility() != View.GONE;
-            if (notGone && !child.hasNoContentHeight()) {
-                float increasedPaddingAmount = child.getIncreasedPaddingAmount();
-                float padding;
-                if (increasedPaddingAmount >= 0.0f) {
-                    padding = (int) NotificationUtils.interpolate(
-                            previousPaddingRequest,
-                            mIncreasedPaddingBetweenElements,
-                            increasedPaddingAmount);
-                    previousPaddingRequest = (int) NotificationUtils.interpolate(
-                            mPaddingBetweenElements,
-                            mIncreasedPaddingBetweenElements,
-                            increasedPaddingAmount);
-                } else {
-                    int ownPadding = (int) NotificationUtils.interpolate(
-                            0,
-                            mPaddingBetweenElements,
-                            1.0f + increasedPaddingAmount);
-                    if (previousPaddingAmount > 0.0f) {
-                        padding = (int) NotificationUtils.interpolate(
-                                ownPadding,
-                                mIncreasedPaddingBetweenElements,
-                                previousPaddingAmount);
-                    } else {
-                        padding = ownPadding;
-                    }
-                    previousPaddingRequest = ownPadding;
-                }
-                if (position != 0) {
-                    position += padding;
-                }
-                previousPaddingAmount = increasedPaddingAmount;
-            }
-            if (child == requestedView) {
-                if (requestedRow != null) {
-                    position += requestedRow.getPositionOfChild(childInGroup);
-                }
-                return position;
-            }
-            if (notGone) {
-                position += getIntrinsicHeight(child);
-            }
-        }
-        return 0;
-    }
-
-    @Override
-    public void onViewAdded(View child) {
-        super.onViewAdded(child);
-        onViewAddedInternal(child);
-    }
-
-    private void updateFirstAndLastBackgroundViews() {
-        ActivatableNotificationView firstChild = getFirstChildWithBackground();
-        ActivatableNotificationView lastChild = getLastChildWithBackground();
-        if (mAnimationsEnabled && mIsExpanded) {
-            mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
-            mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
-        } else {
-            mAnimateNextBackgroundTop = false;
-            mAnimateNextBackgroundBottom = false;
-        }
-        mFirstVisibleBackgroundChild = firstChild;
-        mLastVisibleBackgroundChild = lastChild;
-        mAmbientState.setLastVisibleBackgroundChild(lastChild);
-        mRoundnessManager.setFirstAndLastBackgroundChild(mFirstVisibleBackgroundChild,
-                mLastVisibleBackgroundChild);
-        invalidate();
-    }
-
-    private void onViewAddedInternal(View child) {
-        updateHideSensitiveForChild(child);
-        ((ExpandableView) child).setOnHeightChangedListener(this);
-        generateAddAnimation(child, false /* fromMoreCard */);
-        updateAnimationState(child);
-        updateChronometerForChild(child);
-    }
-
-    private void updateHideSensitiveForChild(View child) {
-        if (child instanceof ExpandableView) {
-            ExpandableView expandableView = (ExpandableView) child;
-            expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
-        }
-    }
-
-    @Override
-    public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
-        onViewRemovedInternal(row, childrenContainer);
-    }
-
-    @Override
-    public void notifyGroupChildAdded(View row) {
-        onViewAddedInternal(row);
-    }
-
-    public void setAnimationsEnabled(boolean animationsEnabled) {
-        mAnimationsEnabled = animationsEnabled;
-        updateNotificationAnimationStates();
-        if (!animationsEnabled) {
-            mSwipedOutViews.clear();
-            mChildrenToRemoveAnimated.clear();
-            clearTemporaryViewsInGroup(this);
-        }
-    }
-
-    private void updateNotificationAnimationStates() {
-        boolean running = mAnimationsEnabled || hasPulsingNotifications();
-        mShelf.setAnimationsEnabled(running);
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            running &= mIsExpanded || isPinnedHeadsUp(child);
-            updateAnimationState(running, child);
-        }
-    }
-
-    private void updateAnimationState(View child) {
-        updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
-                && (mIsExpanded || isPinnedHeadsUp(child)), child);
-    }
-
-    @Override
-    public void setExpandingNotification(ExpandableNotificationRow row) {
-        mAmbientState.setExpandingNotification(row);
-        requestChildrenUpdate();
-    }
-
-    @Override
-    public void bindRow(ExpandableNotificationRow row) {
-        row.setHeadsUpAnimatingAwayListener(animatingAway -> {
-            mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
-            mHeadsUpAppearanceController.updateHeader(row.getEntry());
-        });
-    }
-
-    @Override
-    public void applyExpandAnimationParams(ExpandAnimationParameters params) {
-        mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
-        requestChildrenUpdate();
-    }
-
-    private void updateAnimationState(boolean running, View child) {
-        if (child instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            row.setIconAnimationRunning(running);
-        }
-    }
-
-    public boolean isAddOrRemoveAnimationPending() {
-        return mNeedsAnimation
-                && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
-    }
-
-    @Override
-    public void generateAddAnimation(View child, boolean fromMoreCard) {
-        if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
-            // Generate Animations
-            mChildrenToAddAnimated.add(child);
-            if (fromMoreCard) {
-                mFromMoreCardAdditions.add(child);
-            }
-            mNeedsAnimation = true;
-        }
-        if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) {
-            mAddedHeadsUpChildren.add(child);
-            mChildrenToAddAnimated.remove(child);
-        }
-    }
-
-    @Override
-    public void changeViewPosition(View child, int newIndex) {
-        int currentIndex = indexOfChild(child);
-
-        if (currentIndex == -1) {
-            boolean isTransient = false;
-            if (child instanceof ExpandableNotificationRow
-                    && ((ExpandableNotificationRow)child).getTransientContainer() != null) {
-                isTransient = true;
-            }
-            Log.e(TAG, "Attempting to re-position "
-                    + (isTransient ? "transient" : "")
-                    + " view {"
-                    + child
-                    + "}");
-            return;
-        }
-
-        if (child != null && child.getParent() == this && currentIndex != newIndex) {
-            mChangePositionInProgress = true;
-            ((ExpandableView)child).setChangingPosition(true);
-            removeView(child);
-            addView(child, newIndex);
-            ((ExpandableView)child).setChangingPosition(false);
-            mChangePositionInProgress = false;
-            if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
-                mChildrenChangingPositions.add(child);
-                mNeedsAnimation = true;
-            }
-        }
-    }
-
-    private void startAnimationToState() {
-        if (mNeedsAnimation) {
-            generateChildHierarchyEvents();
-            mNeedsAnimation = false;
-        }
-        if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
-            setAnimationRunning(true);
-            mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
-                    mGoToFullShadeDelay);
-            mAnimationEvents.clear();
-            updateBackground();
-            updateViewShadows();
-            updateClippingToTopRoundedCorner();
-        } else {
-            applyCurrentState();
-        }
-        mGoToFullShadeDelay = 0;
-    }
-
-    private void generateChildHierarchyEvents() {
-        generateHeadsUpAnimationEvents();
-        generateChildRemovalEvents();
-        generateChildAdditionEvents();
-        generatePositionChangeEvents();
-        generateSnapBackEvents();
-        generateDragEvents();
-        generateTopPaddingEvent();
-        generateActivateEvent();
-        generateDimmedEvent();
-        generateHideSensitiveEvent();
-        generateDarkEvent();
-        generateGoToFullShadeEvent();
-        generateViewResizeEvent();
-        generateGroupExpansionEvent();
-        generateAnimateEverythingEvent();
-        generatePulsingAnimationEvent();
-        mNeedsAnimation = false;
-    }
-
-    private void generateHeadsUpAnimationEvents() {
-        for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
-            ExpandableNotificationRow row = eventPair.first;
-            boolean isHeadsUp = eventPair.second;
-            int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
-            boolean onBottom = false;
-            boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
-            if (!mIsExpanded && !isHeadsUp) {
-                type = row.wasJustClicked()
-                        ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
-                        : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
-                if (row.isChildInGroup()) {
-                    // We can otherwise get stuck in there if it was just isolated
-                    row.setHeadsUpAnimatingAway(false);
-                    continue;
-                }
-            } else {
-                ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
-                if (viewState == null) {
-                    // A view state was never generated for this view, so we don't need to animate
-                    // this. This may happen with notification children.
-                    continue;
-                }
-                if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
-                    if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
-                        // Our custom add animation
-                        type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
-                    } else {
-                        // Normal add animation
-                        type = AnimationEvent.ANIMATION_TYPE_ADD;
-                    }
-                    onBottom = !pinnedAndClosed;
-                }
-            }
-            AnimationEvent event = new AnimationEvent(row, type);
-            event.headsUpFromBottom = onBottom;
-            mAnimationEvents.add(event);
-        }
-        mHeadsUpChangeAnimations.clear();
-        mAddedHeadsUpChildren.clear();
-    }
-
-    private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
-        if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
-            return false;
-        }
-        return true;
-    }
-
-    private void generateGroupExpansionEvent() {
-        // Generate a group expansion/collapsing event if there is such a group at all
-        if (mExpandedGroupView != null) {
-            mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
-                    AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
-            mExpandedGroupView = null;
-        }
-    }
-
-    private void generateViewResizeEvent() {
-        if (mNeedViewResizeAnimation) {
-            mAnimationEvents.add(
-                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
-        }
-        mNeedViewResizeAnimation = false;
-    }
-
-    private void generateSnapBackEvents() {
-        for (View child : mSnappedBackChildren) {
-            mAnimationEvents.add(new AnimationEvent(child,
-                    AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
-        }
-        mSnappedBackChildren.clear();
-    }
-
-    private void generateDragEvents() {
-        for (View child : mDragAnimPendingChildren) {
-            mAnimationEvents.add(new AnimationEvent(child,
-                    AnimationEvent.ANIMATION_TYPE_START_DRAG));
-        }
-        mDragAnimPendingChildren.clear();
-    }
-
-    private void generateChildRemovalEvents() {
-        for (View child : mChildrenToRemoveAnimated) {
-            boolean childWasSwipedOut = mSwipedOutViews.contains(child);
-
-            // we need to know the view after this one
-            float removedTranslation = child.getTranslationY();
-            boolean ignoreChildren = true;
-            if (child instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
-                    removedTranslation = row.getTranslationWhenRemoved();
-                    ignoreChildren = false;
-                }
-                childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
-            }
-            if (!childWasSwipedOut) {
-                Rect clipBounds = child.getClipBounds();
-                childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
-
-                if (childWasSwipedOut && child instanceof ExpandableView) {
-                    // Clean up any potential transient views if the child has already been swiped
-                    // out, as we won't be animating it further (due to its height already being
-                    // clipped to 0.
-                    ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer();
-                    if (transientContainer != null) {
-                        transientContainer.removeTransientView(child);
-                    }
-                }
-            }
-            int animationType = childWasSwipedOut
-                    ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
-                    : AnimationEvent.ANIMATION_TYPE_REMOVE;
-            AnimationEvent event = new AnimationEvent(child, animationType);
-            event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
-                    ignoreChildren);
-            mAnimationEvents.add(event);
-            mSwipedOutViews.remove(child);
-        }
-        mChildrenToRemoveAnimated.clear();
-    }
-
-    private void generatePositionChangeEvents() {
-        for (View child : mChildrenChangingPositions) {
-            mAnimationEvents.add(new AnimationEvent(child,
-                    AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
-        }
-        mChildrenChangingPositions.clear();
-        if (mGenerateChildOrderChangedEvent) {
-            mAnimationEvents.add(new AnimationEvent(null,
-                    AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
-            mGenerateChildOrderChangedEvent = false;
-        }
-    }
-
-    private void generateChildAdditionEvents() {
-        for (View child : mChildrenToAddAnimated) {
-            if (mFromMoreCardAdditions.contains(child)) {
-                mAnimationEvents.add(new AnimationEvent(child,
-                        AnimationEvent.ANIMATION_TYPE_ADD,
-                        StackStateAnimator.ANIMATION_DURATION_STANDARD));
-            } else {
-                mAnimationEvents.add(new AnimationEvent(child,
-                        AnimationEvent.ANIMATION_TYPE_ADD));
-            }
-        }
-        mChildrenToAddAnimated.clear();
-        mFromMoreCardAdditions.clear();
-    }
-
-    private void generateTopPaddingEvent() {
-        if (mTopPaddingNeedsAnimation) {
-            AnimationEvent event;
-            if (mAmbientState.isDark()) {
-                event = new AnimationEvent(null /* view */,
-                        AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED,
-                        KeyguardSliceView.DEFAULT_ANIM_DURATION);
-            } else {
-                event = new AnimationEvent(null /* view */,
-                        AnimationEvent.ANIMATION_TYPE_TOP_PADDING_CHANGED);
-            }
-            mAnimationEvents.add(event);
-        }
-        mTopPaddingNeedsAnimation = false;
-    }
-
-    private void generateActivateEvent() {
-        if (mActivateNeedsAnimation) {
-            mAnimationEvents.add(
-                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
-        }
-        mActivateNeedsAnimation = false;
-    }
-
-    private void generateAnimateEverythingEvent() {
-        if (mEverythingNeedsAnimation) {
-            mAnimationEvents.add(
-                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_EVERYTHING));
-        }
-        mEverythingNeedsAnimation = false;
-    }
-
-    private void generateDimmedEvent() {
-        if (mDimmedNeedsAnimation) {
-            mAnimationEvents.add(
-                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
-        }
-        mDimmedNeedsAnimation = false;
-    }
-
-    private void generateHideSensitiveEvent() {
-        if (mHideSensitiveNeedsAnimation) {
-            mAnimationEvents.add(
-                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_HIDE_SENSITIVE));
-        }
-        mHideSensitiveNeedsAnimation = false;
-    }
-
-    private void generateDarkEvent() {
-        if (mDarkNeedsAnimation) {
-            AnimationEvent ev = new AnimationEvent(null,
-                    AnimationEvent.ANIMATION_TYPE_DARK,
-                    new AnimationFilter()
-                            .animateDark()
-                            .animateY(mShelf));
-            ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
-            mAnimationEvents.add(ev);
-            startDarkAmountAnimation();
-        }
-        mDarkNeedsAnimation = false;
-    }
-
-    private void generateGoToFullShadeEvent() {
-        if (mGoToFullShadeNeedsAnimation) {
-            mAnimationEvents.add(
-                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE));
-        }
-        mGoToFullShadeNeedsAnimation = false;
-    }
-
-    private boolean onInterceptTouchEventScroll(MotionEvent ev) {
-        if (!isScrollingEnabled()) {
-            return false;
-        }
-        /*
-         * This method JUST determines whether we want to intercept the motion.
-         * If we return true, onMotionEvent will be called and we do the actual
-         * scrolling there.
-         */
-
-        /*
-        * Shortcut the most recurring case: the user is in the dragging
-        * state and is moving their finger.  We want to intercept this
-        * motion.
-        */
-        final int action = ev.getAction();
-        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
-            return true;
-        }
-
-        switch (action & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_MOVE: {
-                /*
-                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
-                 * whether the user has moved far enough from the original down touch.
-                 */
-
-                /*
-                * Locally do absolute value. mLastMotionY is set to the y value
-                * of the down event.
-                */
-                final int activePointerId = mActivePointerId;
-                if (activePointerId == INVALID_POINTER) {
-                    // If we don't have a valid id, the touch down wasn't on content.
-                    break;
-                }
-
-                final int pointerIndex = ev.findPointerIndex(activePointerId);
-                if (pointerIndex == -1) {
-                    Log.e(TAG, "Invalid pointerId=" + activePointerId
-                            + " in onInterceptTouchEvent");
-                    break;
-                }
-
-                final int y = (int) ev.getY(pointerIndex);
-                final int x = (int) ev.getX(pointerIndex);
-                final int yDiff = Math.abs(y - mLastMotionY);
-                final int xDiff = Math.abs(x - mDownX);
-                if (yDiff > mTouchSlop && yDiff > xDiff) {
-                    setIsBeingDragged(true);
-                    mLastMotionY = y;
-                    mDownX = x;
-                    initVelocityTrackerIfNotExists();
-                    mVelocityTracker.addMovement(ev);
-                }
-                break;
-            }
-
-            case MotionEvent.ACTION_DOWN: {
-                final int y = (int) ev.getY();
-                mScrolledToTopOnFirstDown = isScrolledToTop();
-                if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
-                    setIsBeingDragged(false);
-                    recycleVelocityTracker();
-                    break;
-                }
-
-                /*
-                 * Remember location of down touch.
-                 * ACTION_DOWN always refers to pointer index 0.
-                 */
-                mLastMotionY = y;
-                mDownX = (int) ev.getX();
-                mActivePointerId = ev.getPointerId(0);
-
-                initOrResetVelocityTracker();
-                mVelocityTracker.addMovement(ev);
-                /*
-                * If being flinged and user touches the screen, initiate drag;
-                * otherwise don't.  mScroller.isFinished should be false when
-                * being flinged.
-                */
-                boolean isBeingDragged = !mScroller.isFinished();
-                setIsBeingDragged(isBeingDragged);
-                break;
-            }
-
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-                /* Release the drag */
-                setIsBeingDragged(false);
-                mActivePointerId = INVALID_POINTER;
-                recycleVelocityTracker();
-                if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
-                    animateScroll();
-                }
-                break;
-            case MotionEvent.ACTION_POINTER_UP:
-                onSecondaryPointerUp(ev);
-                break;
-        }
-
-        /*
-        * The only time we want to intercept motion events is if we are in the
-        * drag mode.
-        */
-        return mIsBeingDragged;
-    }
-
-    protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
-        return new StackScrollAlgorithm(context);
-    }
-
-    /**
-     * @return Whether the specified motion event is actually happening over the content.
-     */
-    private boolean isInContentBounds(MotionEvent event) {
-        return isInContentBounds(event.getY());
-    }
-
-    /**
-     * @return Whether a y coordinate is inside the content.
-     */
-    public boolean isInContentBounds(float y) {
-        return y < getHeight() - getEmptyBottomMargin();
-    }
-
-    private void setIsBeingDragged(boolean isDragged) {
-        mIsBeingDragged = isDragged;
-        if (isDragged) {
-            requestDisallowInterceptTouchEvent(true);
-            cancelLongPress();
-        }
-    }
-
-    @Override
-    public void onWindowFocusChanged(boolean hasWindowFocus) {
-        super.onWindowFocusChanged(hasWindowFocus);
-        if (!hasWindowFocus) {
-            cancelLongPress();
-        }
-    }
-
-    @Override
-    public void clearChildFocus(View child) {
-        super.clearChildFocus(child);
-        if (mForcedScroll == child) {
-            mForcedScroll = null;
-        }
-    }
-
-    public void requestDisallowLongPress() {
-        cancelLongPress();
-    }
-
-    public void requestDisallowDismiss() {
-        mDisallowDismissInThisMotion = true;
-    }
-
-    public void cancelLongPress() {
-        mSwipeHelper.cancelLongPress();
-    }
-
-    @Override
-    public boolean isScrolledToTop() {
-        return mOwnScrollY == 0;
-    }
-
-    @Override
-    public boolean isScrolledToBottom() {
-        return mOwnScrollY >= getScrollRange();
-    }
-
-    @Override
-    public View getHostView() {
-        return this;
-    }
-
-    public int getEmptyBottomMargin() {
-        return Math.max(mMaxLayoutHeight - mContentHeight, 0);
-    }
-
-    public void checkSnoozeLeavebehind() {
-        if (mCheckForLeavebehind) {
-            mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
-                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-            mCheckForLeavebehind = false;
-        }
-    }
-
-    public void resetCheckSnoozeLeavebehind() {
-        mCheckForLeavebehind = true;
-    }
-
-    public void onExpansionStarted() {
-        mIsExpansionChanging = true;
-        mAmbientState.setExpansionChanging(true);
-        checkSnoozeLeavebehind();
-    }
-
-    public void onExpansionStopped() {
-        mIsExpansionChanging = false;
-        resetCheckSnoozeLeavebehind();
-        mAmbientState.setExpansionChanging(false);
-        if (!mIsExpanded) {
-            setOwnScrollY(0);
-            mStatusBar.resetUserExpandedStates();
-            clearTemporaryViews();
-            clearUserLockedViews();
-        }
-    }
-
-    private void clearUserLockedViews() {
-        for (int i = 0; i < getChildCount(); i++) {
-            ExpandableView child = (ExpandableView) getChildAt(i);
-            if (child instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                row.setUserLocked(false);
-            }
-        }
-    }
-
-    private void clearTemporaryViews() {
-        // lets make sure nothing is transient anymore
-        clearTemporaryViewsInGroup(this);
-        for (int i = 0; i < getChildCount(); i++) {
-            ExpandableView child = (ExpandableView) getChildAt(i);
-            if (child instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                clearTemporaryViewsInGroup(row.getChildrenContainer());
-            }
-        }
-    }
-
-    private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
-        while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
-            viewGroup.removeTransientView(viewGroup.getTransientView(0));
-        }
-    }
-
-    public void onPanelTrackingStarted() {
-        mPanelTracking = true;
-        mAmbientState.setPanelTracking(true);
-    }
-    public void onPanelTrackingStopped() {
-        mPanelTracking = false;
-        mAmbientState.setPanelTracking(false);
-    }
-
-    public void resetScrollPosition() {
-        mScroller.abortAnimation();
-        setOwnScrollY(0);
-    }
-
-    private void setIsExpanded(boolean isExpanded) {
-        boolean changed = isExpanded != mIsExpanded;
-        mIsExpanded = isExpanded;
-        mStackScrollAlgorithm.setIsExpanded(isExpanded);
-        if (changed) {
-            if (!mIsExpanded) {
-                mGroupManager.collapseAllGroups();
-                mExpandHelper.cancelImmediately();
-            }
-            updateNotificationAnimationStates();
-            updateChronometers();
-            requestChildrenUpdate();
-        }
-    }
-
-    private void updateChronometers() {
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            updateChronometerForChild(getChildAt(i));
-        }
-    }
-
-    private void updateChronometerForChild(View child) {
-        if (child instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            row.setChronometerRunning(mIsExpanded);
-        }
-    }
-
-    @Override
-    public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
-        updateContentHeight();
-        updateScrollPositionOnExpandInBottom(view);
-        clampScrollPosition();
-        notifyHeightChangeListener(view, needsAnimation);
-        ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
-                ? (ExpandableNotificationRow) view
-                : null;
-        if (row != null && (row == mFirstVisibleBackgroundChild
-                || row.getNotificationParent() == mFirstVisibleBackgroundChild)) {
-            updateAlgorithmLayoutMinHeight();
-        }
-        if (needsAnimation) {
-            requestAnimationOnViewResize(row);
-        }
-        requestChildrenUpdate();
-    }
-
-    @Override
-    public void onReset(ExpandableView view) {
-        updateAnimationState(view);
-        updateChronometerForChild(view);
-    }
-
-    private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
-        if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-            if (row.isUserLocked() && row != getFirstChildNotGone()) {
-                if (row.isSummaryWithChildren()) {
-                    return;
-                }
-                // We are actually expanding this view
-                float endPosition = row.getTranslationY() + row.getActualHeight();
-                if (row.isChildInGroup()) {
-                    endPosition += row.getNotificationParent().getTranslationY();
-                }
-                int layoutEnd = mMaxLayoutHeight + (int) mStackTranslation;
-                if (row != mLastVisibleBackgroundChild && mShelf.getVisibility() != GONE) {
-                    layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
-                }
-                if (endPosition > layoutEnd) {
-                    setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
-                    mDisallowScrollingInThisMotion = true;
-                }
-            }
-        }
-    }
-
-    public void setOnHeightChangedListener(
-            ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
-        this.mOnHeightChangedListener = mOnHeightChangedListener;
-    }
-
-    public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
-        mOnEmptySpaceClickListener = listener;
-    }
-
-    public void onChildAnimationFinished() {
-        setAnimationRunning(false);
-        requestChildrenUpdate();
-        runAnimationFinishedRunnables();
-        clearTransient();
-        clearHeadsUpDisappearRunning();
-    }
-
-    private void clearHeadsUpDisappearRunning() {
-        for (int i = 0; i < getChildCount(); i++) {
-            View view = getChildAt(i);
-            if (view instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-                row.setHeadsUpAnimatingAway(false);
-                if (row.isSummaryWithChildren()) {
-                    for (ExpandableNotificationRow child : row.getNotificationChildren()) {
-                        child.setHeadsUpAnimatingAway(false);
-                    }
-                }
-            }
-        }
-    }
-
-    private void clearTransient() {
-        for (ExpandableView view : mClearTransientViewsWhenFinished) {
-            StackStateAnimator.removeTransientView(view);
-        }
-        mClearTransientViewsWhenFinished.clear();
-    }
-
-    private void runAnimationFinishedRunnables() {
-        for (Runnable runnable : mAnimationFinishedRunnables) {
-            runnable.run();
-        }
-        mAnimationFinishedRunnables.clear();
-    }
-
-    /**
-     * See {@link AmbientState#setDimmed}.
-     */
-    public void setDimmed(boolean dimmed, boolean animate) {
-        dimmed &= onKeyguard();
-        mAmbientState.setDimmed(dimmed);
-        if (animate && mAnimationsEnabled) {
-            mDimmedNeedsAnimation = true;
-            mNeedsAnimation =  true;
-            animateDimmed(dimmed);
-        } else {
-            setDimAmount(dimmed ? 1.0f : 0.0f);
-        }
-        requestChildrenUpdate();
-    }
-
-    @VisibleForTesting
-    boolean isDimmed() {
-        return mAmbientState.isDimmed();
-    }
-
-    private void setDimAmount(float dimAmount) {
-        mDimAmount = dimAmount;
-        updateBackgroundDimming();
-    }
-
-    private void animateDimmed(boolean dimmed) {
-        if (mDimAnimator != null) {
-            mDimAnimator.cancel();
-        }
-        float target = dimmed ? 1.0f : 0.0f;
-        if (target == mDimAmount) {
-            return;
-        }
-        mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
-        mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
-        mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mDimAnimator.addListener(mDimEndListener);
-        mDimAnimator.addUpdateListener(mDimUpdateListener);
-        mDimAnimator.start();
-    }
-
-    public void setHideSensitive(boolean hideSensitive, boolean animate) {
-        if (hideSensitive != mAmbientState.isHideSensitive()) {
-            int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                ExpandableView v = (ExpandableView) getChildAt(i);
-                v.setHideSensitiveForIntrinsicHeight(hideSensitive);
-            }
-            mAmbientState.setHideSensitive(hideSensitive);
-            if (animate && mAnimationsEnabled) {
-                mHideSensitiveNeedsAnimation = true;
-                mNeedsAnimation =  true;
-            }
-            updateContentHeight();
-            requestChildrenUpdate();
-        }
-    }
-
-    /**
-     * See {@link AmbientState#setActivatedChild}.
-     */
-    public void setActivatedChild(ActivatableNotificationView activatedChild) {
-        mAmbientState.setActivatedChild(activatedChild);
-        if (mAnimationsEnabled) {
-            mActivateNeedsAnimation = true;
-            mNeedsAnimation =  true;
-        }
-        requestChildrenUpdate();
-    }
-
-    public ActivatableNotificationView getActivatedChild() {
-        return mAmbientState.getActivatedChild();
-    }
-
-    private void applyCurrentState() {
-        mCurrentStackScrollState.apply();
-        if (mListener != null) {
-            mListener.onChildLocationsChanged();
-        }
-        runAnimationFinishedRunnables();
-        setAnimationRunning(false);
-        updateBackground();
-        updateViewShadows();
-        updateClippingToTopRoundedCorner();
-    }
-
-    private void updateViewShadows() {
-        // we need to work around an issue where the shadow would not cast between siblings when
-        // their z difference is between 0 and 0.1
-
-        // Lefts first sort by Z difference
-        for (int i = 0; i < getChildCount(); i++) {
-            ExpandableView child = (ExpandableView) getChildAt(i);
-            if (child.getVisibility() != GONE) {
-                mTmpSortedChildren.add(child);
-            }
-        }
-        Collections.sort(mTmpSortedChildren, mViewPositionComparator);
-
-        // Now lets update the shadow for the views
-        ExpandableView previous = null;
-        for (int i = 0; i < mTmpSortedChildren.size(); i++) {
-            ExpandableView expandableView = mTmpSortedChildren.get(i);
-            float translationZ = expandableView.getTranslationZ();
-            float otherZ = previous == null ? translationZ : previous.getTranslationZ();
-            float diff = otherZ - translationZ;
-            if (diff <= 0.0f || diff >= FakeShadowView.SHADOW_SIBLING_TRESHOLD) {
-                // There is no fake shadow to be drawn
-                expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
-            } else {
-                float yLocation = previous.getTranslationY() + previous.getActualHeight() -
-                        expandableView.getTranslationY() - previous.getExtraBottomPadding();
-                expandableView.setFakeShadowIntensity(
-                        diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
-                        previous.getOutlineAlpha(), (int) yLocation,
-                        previous.getOutlineTranslation());
-            }
-            previous = expandableView;
-        }
-
-        mTmpSortedChildren.clear();
-    }
-
-    /**
-     * Update colors of "dismiss" and "empty shade" views.
-     *
-     * @param lightTheme True if light theme should be used.
-     */
-    public void updateDecorViews(boolean lightTheme) {
-        if (lightTheme == mUsingLightTheme) {
-            return;
-        }
-        mUsingLightTheme = lightTheme;
-        Context context = new ContextThemeWrapper(mContext,
-                lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI);
-        final int textColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor);
-        mFooterView.setTextColor(textColor);
-        mEmptyShadeView.setTextColor(textColor);
-    }
-
-    public void goToFullShade(long delay) {
-        mGoToFullShadeNeedsAnimation = true;
-        mGoToFullShadeDelay = delay;
-        mNeedsAnimation = true;
-        requestChildrenUpdate();
-    }
-
-    public void cancelExpandHelper() {
-        mExpandHelper.cancel();
-    }
-
-    public void setIntrinsicPadding(int intrinsicPadding) {
-        mIntrinsicPadding = intrinsicPadding;
-        mAmbientState.setIntrinsicPadding(intrinsicPadding);
-    }
-
-    public int getIntrinsicPadding() {
-        return mIntrinsicPadding;
-    }
-
-    /**
-     * @return the y position of the first notification
-     */
-    public float getNotificationsTopY() {
-        return mTopPadding + getStackTranslation();
-    }
-
-    @Override
-    public boolean shouldDelayChildPressedState() {
-        return true;
-    }
-
-    /**
-     * See {@link AmbientState#setDark}.
-     */
-    public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
-        if (mAmbientState.isDark() == dark) {
-            return;
-        }
-        mAmbientState.setDark(dark);
-        if (animate && mAnimationsEnabled) {
-            mDarkNeedsAnimation = true;
-            mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
-            mNeedsAnimation =  true;
-        } else {
-            if (mDarkAmountAnimator != null) {
-                mDarkAmountAnimator.cancel();
-            }
-            setDarkAmount(dark ? 1f : 0f);
-            updateBackground();
-        }
-        requestChildrenUpdate();
-        applyCurrentBackgroundBounds();
-        updateWillNotDraw();
-        notifyHeightChangeListener(mShelf);
-    }
-
-    private void updateAntiBurnInTranslation() {
-        setTranslationX(mAntiBurnInOffsetX * mDarkAmount);
-    }
-
-    /**
-     * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or
-     * not {@link #onDraw(Canvas)} is called). This method should be called whenever the
-     * {@link #mAmbientState}'s dark mode is toggled.
-     */
-    private void updateWillNotDraw() {
-        boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
-        setWillNotDraw(!willDraw);
-    }
-
-    private void setDarkAmount(float darkAmount) {
-        mDarkAmount = darkAmount;
-        boolean wasFullyDark = mAmbientState.isFullyDark();
-        mAmbientState.setDarkAmount(darkAmount);
-        if (mAmbientState.isFullyDark() != wasFullyDark) {
-            updateContentHeight();
-            DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
-            if (mAmbientState.isFullyDark() && dozeParameters.shouldControlScreenOff()) {
-                mShelf.fadeInTranslating();
-            }
-        }
-        updateAlgorithmHeightAndPadding();
-        updateBackgroundDimming();
-        updateAntiBurnInTranslation();
-        requestChildrenUpdate();
-    }
-
-    public float getDarkAmount() {
-        return mDarkAmount;
-    }
-
-    private void startDarkAmountAnimation() {
-        ObjectAnimator darkAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, mDarkAmount,
-                mAmbientState.isDark() ? 1f : 0);
-        darkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
-        darkAnimator.setInterpolator(Interpolators.ALPHA_IN);
-        darkAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mDarkAmountAnimator = null;
-            }
-        });
-        if (mDarkAmountAnimator != null) {
-            mDarkAmountAnimator.cancel();
-        }
-        mDarkAmountAnimator = darkAnimator;
-        mDarkAmountAnimator.start();
-    }
-
-    private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
-        if (screenLocation == null || screenLocation.y < mTopPadding) {
-            return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
-        }
-        if (screenLocation.y > getBottomMostNotificationBottom()) {
-            return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW;
-        }
-        View child = getClosestChildAtRawPosition(screenLocation.x, screenLocation.y);
-        if (child != null) {
-            return getNotGoneIndex(child);
-        } else {
-            return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
-        }
-    }
-
-    private int getNotGoneIndex(View child) {
-        int count = getChildCount();
-        int notGoneIndex = 0;
-        for (int i = 0; i < count; i++) {
-            View v = getChildAt(i);
-            if (child == v) {
-                return notGoneIndex;
-            }
-            if (v.getVisibility() != View.GONE) {
-                notGoneIndex++;
-            }
-        }
-        return -1;
-    }
-
-    public void setFooterView(@NonNull FooterView footerView) {
-        int index = -1;
-        if (mFooterView != null) {
-            index = indexOfChild(mFooterView);
-            removeView(mFooterView);
-        }
-        mFooterView = footerView;
-        addView(mFooterView, index);
-    }
-
-    public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
-        int index = -1;
-        if (mEmptyShadeView != null) {
-            index = indexOfChild(mEmptyShadeView);
-            removeView(mEmptyShadeView);
-        }
-        mEmptyShadeView = emptyShadeView;
-        addView(mEmptyShadeView, index);
-    }
-
-    public void updateEmptyShadeView(boolean visible) {
-        mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
-
-        int oldTextRes = mEmptyShadeView.getTextResource();
-        int newTextRes = mStatusBar.areNotificationsHidden()
-                ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
-        if (oldTextRes != newTextRes) {
-            mEmptyShadeView.setText(newTextRes);
-        }
-    }
-
-    public void updateFooterView(boolean visible, boolean showDismissView) {
-        if (mFooterView == null) {
-            return;
-        }
-        boolean animate = mIsExpanded && mAnimationsEnabled;
-        mFooterView.setVisible(visible, animate);
-        mFooterView.setSecondaryVisible(showDismissView, animate);
-    }
-
-    public void setDismissAllInProgress(boolean dismissAllInProgress) {
-        mDismissAllInProgress = dismissAllInProgress;
-        mAmbientState.setDismissAllInProgress(dismissAllInProgress);
-        handleDismissAllClipping();
-    }
-
-    private void handleDismissAllClipping() {
-        final int count = getChildCount();
-        boolean previousChildWillBeDismissed = false;
-        for (int i = 0; i < count; i++) {
-            ExpandableView child = (ExpandableView) getChildAt(i);
-            if (child.getVisibility() == GONE) {
-                continue;
-            }
-            if (mDismissAllInProgress && previousChildWillBeDismissed) {
-                child.setMinClipTopAmount(child.getClipTopAmount());
-            } else {
-                child.setMinClipTopAmount(0);
-            }
-            previousChildWillBeDismissed = canChildBeDismissed(child);
-        }
-    }
-
-    public boolean isFooterViewNotGone() {
-        return mFooterView != null
-                && mFooterView.getVisibility() != View.GONE
-                && !mFooterView.willBeGone();
-    }
-
-    public boolean isFooterViewContentVisible() {
-        return mFooterView != null && mFooterView.isContentVisible();
-    }
-
-    public int getFooterViewHeight() {
-        return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
-    }
-
-    public int getEmptyShadeViewHeight() {
-        return mEmptyShadeView.getHeight();
-    }
-
-    public float getBottomMostNotificationBottom() {
-        final int count = getChildCount();
-        float max = 0;
-        for (int childIdx = 0; childIdx < count; childIdx++) {
-            ExpandableView child = (ExpandableView) getChildAt(childIdx);
-            if (child.getVisibility() == GONE) {
-                continue;
-            }
-            float bottom = child.getTranslationY() + child.getActualHeight()
-                    - child.getClipBottomAmount();
-            if (bottom > max) {
-                max = bottom;
-            }
-        }
-        return max + getStackTranslation();
-    }
-
-    public void setStatusBar(StatusBar statusBar) {
-        this.mStatusBar = statusBar;
-    }
-
-    public void setGroupManager(NotificationGroupManager groupManager) {
-        this.mGroupManager = groupManager;
-    }
-
-    public void onGoToKeyguard() {
-        requestAnimateEverything();
-    }
-
-    private void requestAnimateEverything() {
-        if (mIsExpanded && mAnimationsEnabled) {
-            mEverythingNeedsAnimation = true;
-            mNeedsAnimation = true;
-            requestChildrenUpdate();
-        }
-    }
-
-    public boolean isBelowLastNotification(float touchX, float touchY) {
-        int childCount = getChildCount();
-        for (int i = childCount - 1; i >= 0; i--) {
-            ExpandableView child = (ExpandableView) getChildAt(i);
-            if (child.getVisibility() != View.GONE) {
-                float childTop = child.getY();
-                if (childTop > touchY) {
-                    // we are above a notification entirely let's abort
-                    return false;
-                }
-                boolean belowChild = touchY > childTop + child.getActualHeight()
-                        - child.getClipBottomAmount();
-                if (child == mFooterView) {
-                    if(!belowChild && !mFooterView.isOnEmptySpace(touchX - mFooterView.getX(),
-                                    touchY - childTop)) {
-                        // We clicked on the dismiss button
-                        return false;
-                    }
-                } else if (child == mEmptyShadeView) {
-                    // We arrived at the empty shade view, for which we accept all clicks
-                    return true;
-                } else if (!belowChild){
-                    // We are on a child
-                    return false;
-                }
-            }
-        }
-        return touchY > mTopPadding + mStackTranslation;
-    }
-
-    @Override
-    public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
-        boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
-                && (mIsExpanded || changedRow.isPinned());
-        if (animated) {
-            mExpandedGroupView = changedRow;
-            mNeedsAnimation = true;
-        }
-        changedRow.setChildrenExpanded(expanded, animated);
-        if (!mGroupExpandedForMeasure) {
-            onHeightChanged(changedRow, false /* needsAnimation */);
-        }
-        runAfterAnimationFinished(new Runnable() {
-            @Override
-            public void run() {
-                changedRow.onFinishedExpansionChange();
-            }
-        });
-    }
-
-    @Override
-    public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
-        mStatusBar.requestNotificationUpdate();
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setScrollable(mScrollable);
-        event.setScrollX(mScrollX);
-        event.setScrollY(mOwnScrollY);
-        event.setMaxScrollX(mScrollX);
-        event.setMaxScrollY(getScrollRange());
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        if (mScrollable) {
-            info.setScrollable(true);
-            if (mBackwardScrollable) {
-                info.addAction(
-                        AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
-                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
-            }
-            if (mForwardScrollable) {
-                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
-                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
-            }
-        }
-        // Talkback only listenes to scroll events of certain classes, let's make us a scrollview
-        info.setClassName(ScrollView.class.getName());
-    }
-
-    /** @hide */
-    @Override
-    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
-        if (super.performAccessibilityActionInternal(action, arguments)) {
-            return true;
-        }
-        if (!isEnabled()) {
-            return false;
-        }
-        int direction = -1;
-        switch (action) {
-            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
-                // fall through
-            case android.R.id.accessibilityActionScrollDown:
-                direction = 1;
-                // fall through
-            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
-                // fall through
-            case android.R.id.accessibilityActionScrollUp:
-                final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
-                        - mShelf.getIntrinsicHeight();
-                final int targetScrollY = Math.max(0,
-                        Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
-                if (targetScrollY != mOwnScrollY) {
-                    mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
-                    animateScroll();
-                    return true;
-                }
-                break;
-        }
-        return false;
-    }
-
-    @Override
-    public void onGroupsChanged() {
-        mStatusBar.requestNotificationUpdate();
-    }
-
-    public void generateChildOrderChangedEvent() {
-        if (mIsExpanded && mAnimationsEnabled) {
-            mGenerateChildOrderChangedEvent = true;
-            mNeedsAnimation = true;
-            requestChildrenUpdate();
-        }
-    }
-
-    @Override
-    public int getContainerChildCount() {
-        return getChildCount();
-    }
-
-    @Override
-    public View getContainerChildAt(int i) {
-        return getChildAt(i);
-    }
-
-    @Override
-    public void removeContainerView(View v) {
-        removeView(v);
-    }
-
-    @Override
-    public void addContainerView(View v) {
-        addView(v);
-    }
-
-    public void runAfterAnimationFinished(Runnable runnable) {
-        mAnimationFinishedRunnables.add(runnable);
-    }
-
-    public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
-        mHeadsUpManager = headsUpManager;
-        mAmbientState.setHeadsUpManager(headsUpManager);
-        mHeadsUpManager.addListener(mRoundnessManager);
-    }
-
-    public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
-        if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
-            mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
-            mNeedsAnimation = true;
-            if (!mIsExpanded && !isHeadsUp) {
-                row.setHeadsUpAnimatingAway(true);
-            }
-            requestChildrenUpdate();
-        }
-    }
-
-    public void setShadeExpanded(boolean shadeExpanded) {
-        mAmbientState.setShadeExpanded(shadeExpanded);
-        mStateAnimator.setShadeExpanded(shadeExpanded);
-    }
-
-    /**
-     * Set the boundary for the bottom heads up position. The heads up will always be above this
-     * position.
-     *
-     * @param height the height of the screen
-     * @param bottomBarHeight the height of the bar on the bottom
-     */
-    public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
-        mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
-        mStateAnimator.setHeadsUpAppearHeightBottom(height);
-        requestChildrenUpdate();
-    }
-
-    public void setTrackingHeadsUp(ExpandableNotificationRow row) {
-        mTrackingHeadsUp = row != null;
-        mRoundnessManager.setTrackingHeadsUp(row);
-    }
-
-    public void setScrimController(ScrimController scrimController) {
-        mScrimController = scrimController;
-        mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
-    }
-
-    public void forceNoOverlappingRendering(boolean force) {
-        mForceNoOverlappingRendering = force;
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
-    }
-
-    public void setAnimationRunning(boolean animationRunning) {
-        if (animationRunning != mAnimationRunning) {
-            if (animationRunning) {
-                getViewTreeObserver().addOnPreDrawListener(mRunningAnimationUpdater);
-            } else {
-                getViewTreeObserver().removeOnPreDrawListener(mRunningAnimationUpdater);
-            }
-            mAnimationRunning = animationRunning;
-            updateContinuousShadowDrawing();
-        }
-    }
-
-    public boolean isExpanded() {
-        return mIsExpanded;
-    }
-
-    public void setPulsing(boolean pulsing, boolean animated) {
-        if (!mPulsing && !pulsing) {
-            return;
-        }
-        mPulsing = pulsing;
-        mNeedingPulseAnimation = animated ? getFirstChildNotGone() : null;
-        mAmbientState.setPulsing(pulsing);
-        updateNotificationAnimationStates();
-        updateAlgorithmHeightAndPadding();
-        updateContentHeight();
-        requestChildrenUpdate();
-        notifyHeightChangeListener(null, animated);
-        mNeedsAnimation |= animated;
-    }
-
-    private void generatePulsingAnimationEvent() {
-        if (mNeedingPulseAnimation != null) {
-            int type = mPulsing ? AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR
-                    : AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR;
-            mAnimationEvents.add(new AnimationEvent(mNeedingPulseAnimation, type));
-            mNeedingPulseAnimation = null;
-        }
-    }
-
-    public void setFadingOut(boolean fadingOut) {
-        if (fadingOut != mFadingOut) {
-            mFadingOut = fadingOut;
-            updateFadingState();
-        }
-    }
-
-    public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
-        if (mScrimController == null) {
-            // we're not set up yet.
-            return;
-        }
-        if (parentNotFullyVisible != mParentNotFullyVisible) {
-            mParentNotFullyVisible = parentNotFullyVisible;
-            updateFadingState();
-        }
-    }
-
-    private void updateFadingState() {
-        applyCurrentBackgroundBounds();
-        updateSrcDrawing();
-    }
-
-    @Override
-    public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
-        super.setAlpha(alpha);
-        setFadingOut(alpha != 1.0f);
-    }
-
-    public void setQsExpanded(boolean qsExpanded) {
-        mQsExpanded = qsExpanded;
-        updateAlgorithmLayoutMinHeight();
-        updateScrollability();
-    }
-
-    public void setQsExpansionFraction(float qsExpansionFraction) {
-        mQsExpansionFraction = qsExpansionFraction;
-    }
-
-    public void setOwnScrollY(int ownScrollY) {
-        if (ownScrollY != mOwnScrollY) {
-            // We still want to call the normal scrolled changed for accessibility reasons
-            onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
-            mOwnScrollY = ownScrollY;
-            updateForwardAndBackwardScrollability();
-            requestChildrenUpdate();
-        }
-    }
-
-    public void setShelf(NotificationShelf shelf) {
-        int index = -1;
-        if (mShelf != null) {
-            index = indexOfChild(mShelf);
-            removeView(mShelf);
-        }
-        mShelf = shelf;
-        addView(mShelf, index);
-        mAmbientState.setShelf(shelf);
-        mStateAnimator.setShelf(shelf);
-        shelf.bind(mAmbientState, this);
-    }
-
-    public NotificationShelf getNotificationShelf() {
-        return mShelf;
-    }
-
-    public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
-        if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
-            mMaxDisplayedNotifications = maxDisplayedNotifications;
-            updateContentHeight();
-            notifyHeightChangeListener(mShelf);
-        }
-    }
-
-    public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
-        mShouldShowShelfOnly =  shouldShowShelfOnly;
-        updateAlgorithmLayoutMinHeight();
-    }
-
-    public int getMinExpansionHeight() {
-        return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
-    }
-
-    public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
-        mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
-        updateClipping();
-    }
-
-    public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
-        mHeadsUpAnimatingAway = headsUpAnimatingAway;
-        updateClipping();
-    }
-
-    public void setStatusBarState(int statusBarState) {
-        mStatusBarState = statusBarState;
-        mAmbientState.setStatusBarState(statusBarState);
-    }
-
-    public void setExpandingVelocity(float expandingVelocity) {
-        mAmbientState.setExpandingVelocity(expandingVelocity);
-    }
-
-    public float getOpeningHeight() {
-        if (mEmptyShadeView.getVisibility() == GONE) {
-            return getMinExpansionHeight();
-        } else {
-            return getAppearEndPosition();
-        }
-    }
-
-    public void setIsFullWidth(boolean isFullWidth) {
-        mAmbientState.setPanelFullWidth(isFullWidth);
-    }
-
-    public void setUnlockHintRunning(boolean running) {
-        mAmbientState.setUnlockHintRunning(running);
-    }
-
-    public void setQsCustomizerShowing(boolean isShowing) {
-        mAmbientState.setQsCustomizerShowing(isShowing);
-        requestChildrenUpdate();
-    }
-
-    public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
-        mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
-    }
-
-    public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
-        mAntiBurnInOffsetX = antiBurnInOffsetX;
-        updateAntiBurnInTranslation();
-    }
-
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
-                        + " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
-                        + " qsExpandFraction=%f]",
-                this.getClass().getSimpleName(),
-                mPulsing ? "T":"f",
-                mAmbientState.isQsCustomizerShowing() ? "T":"f",
-                getVisibility() == View.VISIBLE ? "visible"
-                        : getVisibility() == View.GONE ? "gone"
-                                : "invisible",
-                getAlpha(),
-                mAmbientState.getScrollY(),
-                mMaxTopPadding,
-                mShouldShowShelfOnly ? "T":"f",
-                mQsExpansionFraction));
-    }
-
-    public boolean isFullyDark() {
-        return mAmbientState.isFullyDark();
-    }
-
-    /**
-     * Add a listener whenever the expanded height changes. The first value passed as an argument
-     * is the expanded height and the second one is the appearFraction.
-     *
-     * @param listener the listener to notify.
-     */
-    public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
-        mExpandedHeightListeners.add(listener);
-    }
-
-    /**
-     * Stop a listener from listening to the expandedHeight.
-     */
-    public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
-        mExpandedHeightListeners.remove(listener);
-    }
-
-    public void setHeadsUpAppearanceController(
-            HeadsUpAppearanceController headsUpAppearanceController) {
-        mHeadsUpAppearanceController = headsUpAppearanceController;
-    }
-
-    /**
-     * A listener that is notified when the empty space below the notifications is clicked on
-     */
-    public interface OnEmptySpaceClickListener {
-        void onEmptySpaceClicked(float x, float y);
-    }
-
-    /**
-     * A listener that gets notified when the overscroll at the top has changed.
-     */
-    public interface OnOverscrollTopChangedListener {
-
-        /**
-         * Notifies a listener that the overscroll has changed.
-         *
-         * @param amount the amount of overscroll, in pixels
-         * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
-         *                     unrubberbanded motion to directly expand overscroll view (e.g expand
-         *                     QS)
-         */
-        void onOverscrollTopChanged(float amount, boolean isRubberbanded);
-
-        /**
-         * Notify a listener that the scroller wants to escape from the scrolling motion and
-         * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
-         *
-         * @param velocity The velocity that the Scroller had when over flinging
-         * @param open Should the fling open or close the overscroll view.
-         */
-        void flingTopOverscroll(float velocity, boolean open);
-    }
-
-    private class NotificationSwipeHelper extends SwipeHelper
-            implements NotificationSwipeActionHelper {
-        private static final long COVER_MENU_DELAY = 4000;
-        private Runnable mFalsingCheck;
-        private Handler mHandler;
-
-        public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
-            super(swipeDirection, callback, context);
-            mHandler = new Handler();
-            mFalsingCheck = new Runnable() {
-                @Override
-                public void run() {
-                    resetExposedMenuView(true /* animate */, true /* force */);
-                }
-            };
-        }
-
-        @Override
-        public void onDownUpdate(View currView, MotionEvent ev) {
-            mTranslatingParentView = currView;
-            if (mCurrMenuRow != null) {
-                mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
-            }
-            mCurrMenuRow = null;
-            mHandler.removeCallbacks(mFalsingCheck);
-
-            // Slide back any notifications that might be showing a menu
-            resetExposedMenuView(true /* animate */, false /* force */);
-
-            if (currView instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
-                mCurrMenuRow = row.createMenu();
-                mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
-                mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
-                mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
-            }
-        }
-
-        @Override
-        public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
-            mHandler.removeCallbacks(mFalsingCheck);
-            if (mCurrMenuRow != null) {
-                mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
-            }
-        }
-
-        @Override
-        public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
-                float translation) {
-            if (mCurrMenuRow != null) {
-                return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
-            }
-            return false;
-        }
-
-        @Override
-        public void dismissChild(final View view, float velocity,
-                boolean useAccelerateInterpolator) {
-            super.dismissChild(view, velocity, useAccelerateInterpolator);
-            if (mIsExpanded) {
-                // We don't want to quick-dismiss when it's a heads up as this might lead to closing
-                // of the panel early.
-                handleChildViewDismissed(view);
-            }
-            mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
-                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-            handleMenuCoveredOrDismissed();
-        }
-
-        @Override
-        public void snapChild(final View animView, final float targetLeft, float velocity) {
-            super.snapChild(animView, targetLeft, velocity);
-            onDragCancelled(animView);
-            if (targetLeft == 0) {
-                handleMenuCoveredOrDismissed();
-            }
-        }
-
-        @Override
-        public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
-            mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
-        }
-
-        public boolean isFalseGesture(MotionEvent ev) {
-            return super.isFalseGesture(ev);
-        }
-
-        private void handleMenuCoveredOrDismissed() {
-            if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
-                mMenuExposedView = null;
-            }
-        }
-
-        @Override
-        public Animator getViewTranslationAnimator(View v, float target,
-                AnimatorUpdateListener listener) {
-            if (v instanceof ExpandableNotificationRow) {
-                return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
-            } else {
-                return super.getViewTranslationAnimator(v, target, listener);
-            }
-        }
-
-        @Override
-        public void setTranslation(View v, float translate) {
-            ((ExpandableView) v).setTranslation(translate);
-        }
-
-        @Override
-        public float getTranslation(View v) {
-            return ((ExpandableView) v).getTranslation();
-        }
-
-        @Override
-        public void dismiss(View animView, float velocity) {
-            dismissChild(animView, velocity,
-                    !swipedFastEnough(0, 0) /* useAccelerateInterpolator */);
-        }
-
-        @Override
-        public void snap(View animView, float targetLeft, float velocity) {
-            snapChild(animView, targetLeft, velocity);
-        }
-
-        @Override
-        public boolean swipedFarEnough(float translation, float viewSize) {
-            return swipedFarEnough();
-        }
-
-        @Override
-        public boolean swipedFastEnough(float translation, float velocity) {
-            return swipedFastEnough();
-        }
-
-        @Override
-        public float getMinDismissVelocity() {
-            return getEscapeVelocity();
-        }
-
-        public void onMenuShown(View animView) {
-            onDragCancelled(animView);
-
-            // If we're on the lockscreen we want to false this.
-            if (isAntiFalsingNeeded()) {
-                mHandler.removeCallbacks(mFalsingCheck);
-                mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
-            }
-        }
-
-        public void closeControlsIfOutsideTouch(MotionEvent ev) {
-            NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
-            View view = null;
-            if (guts != null && !guts.getGutsContent().isLeavebehind()) {
-                // Only close visible guts if they're not a leavebehind.
-                view = guts;
-            } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
-                    && mTranslatingParentView != null) {
-                // Checking menu
-                view = mTranslatingParentView;
-            }
-            if (view != null && !isTouchInView(ev, view)) {
-                // Touch was outside visible guts / menu notification, close what's visible
-                mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
-                        false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
-                        false /* resetMenu */);
-                resetExposedMenuView(true /* animate */, true /* force */);
-            }
-        }
-
-        public void resetExposedMenuView(boolean animate, boolean force) {
-            if (mMenuExposedView == null
-                    || (!force && mMenuExposedView == mTranslatingParentView)) {
-                // If no menu is showing or it's showing for this view we do nothing.
-                return;
-            }
-            final View prevMenuExposedView = mMenuExposedView;
-            if (animate) {
-                Animator anim = getViewTranslationAnimator(prevMenuExposedView,
-                        0 /* leftTarget */, null /* updateListener */);
-                if (anim != null) {
-                    anim.start();
-                }
-            } else if (mMenuExposedView instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) mMenuExposedView;
-                if (!row.isRemoved()) {
-                    row.resetTranslation();
-                }
-            }
-            mMenuExposedView = null;
-        }
-    }
-
-    private boolean isTouchInView(MotionEvent ev, View view) {
-        if (view == null) {
-            return false;
-        }
-        final int height = (view instanceof ExpandableView)
-                ? ((ExpandableView) view).getActualHeight()
-                : view.getHeight();
-        final int rx = (int) ev.getRawX();
-        final int ry = (int) ev.getRawY();
-        view.getLocationOnScreen(mTempInt2);
-        final int x = mTempInt2[0];
-        final int y = mTempInt2[1];
-        Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
-        boolean ret = rect.contains(rx, ry);
-        return ret;
-    }
-
-    private void updateContinuousShadowDrawing() {
-        boolean continuousShadowUpdate = mAnimationRunning
-                || !mAmbientState.getDraggedViews().isEmpty();
-        if (continuousShadowUpdate != mContinuousShadowUpdate) {
-            if (continuousShadowUpdate) {
-                getViewTreeObserver().addOnPreDrawListener(mShadowUpdater);
-            } else {
-                getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
-            }
-            mContinuousShadowUpdate = continuousShadowUpdate;
-        }
-    }
-
-    @Override
-    public void resetExposedMenuView(boolean animate, boolean force) {
-        mSwipeHelper.resetExposedMenuView(animate, force);
-    }
-
-    public void closeControlsIfOutsideTouch(MotionEvent ev) {
-        mSwipeHelper.closeControlsIfOutsideTouch(ev);
-    }
-
-    static class AnimationEvent {
-
-        static AnimationFilter[] FILTERS = new AnimationFilter[] {
-
-                // ANIMATION_TYPE_ADD
-                new AnimationFilter()
-                        .animateShadowAlpha()
-                        .animateHeight()
-                        .animateTopInset()
-                        .animateY()
-                        .animateZ()
-                        .hasDelays(),
-
-                // ANIMATION_TYPE_REMOVE
-                new AnimationFilter()
-                        .animateShadowAlpha()
-                        .animateHeight()
-                        .animateTopInset()
-                        .animateY()
-                        .animateZ()
-                        .hasDelays(),
-
-                // ANIMATION_TYPE_REMOVE_SWIPED_OUT
-                new AnimationFilter()
-                        .animateShadowAlpha()
-                        .animateHeight()
-                        .animateTopInset()
-                        .animateY()
-                        .animateZ()
-                        .hasDelays(),
-
-                // ANIMATION_TYPE_TOP_PADDING_CHANGED
-                new AnimationFilter()
-                        .animateShadowAlpha()
-                        .animateHeight()
-                        .animateTopInset()
-                        .animateY()
-                        .animateDimmed()
-                        .animateZ(),
-
-                // ANIMATION_TYPE_START_DRAG
-                new AnimationFilter()
-                        .animateShadowAlpha(),
-
-                // ANIMATION_TYPE_SNAP_BACK
-                new AnimationFilter()
-                        .animateShadowAlpha()
-                        .animateHeight(),
-
-                // ANIMATION_TYPE_ACTIVATED_CHILD
-                new AnimationFilter()
-                        .animateZ(),
-
-                // ANIMATION_TYPE_DIMMED
-                new AnimationFilter()
-                        .animateDimmed(),
-
-                // ANIMATION_TYPE_CHANGE_POSITION
-                new AnimationFilter()
-                        .animateAlpha() // maybe the children change positions
-                        .animateShadowAlpha()
-                        .animateHeight()
-                        .animateTopInset()
-                        .animateY()
-                        .animateZ(),
-
-                // ANIMATION_TYPE_DARK
-                null, // Unused
-
-                // ANIMATION_TYPE_GO_TO_FULL_SHADE
-                new AnimationFilter()
-                        .animateShadowAlpha()
-                        .animateHeight()
-                        .animateTopInset()
-                        .animateY()
-                        .animateDimmed()
-                        .animateZ()
-                        .hasDelays(),
-
-                // ANIMATION_TYPE_HIDE_SENSITIVE
-                new AnimationFilter()
-                        .animateHideSensitive(),
-
-                // ANIMATION_TYPE_VIEW_RESIZE
-                new AnimationFilter()
-                        .animateShadowAlpha()
-                        .animateHeight()
-                        .animateTopInset()
-                        .animateY()
-                        .animateZ(),
-
-                // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
-                new AnimationFilter()
-                        .animateAlpha()
-                        .animateShadowAlpha()
-                        .animateHeight()
-                        .animateTopInset()
-                        .animateY()
-                        .animateZ(),
-
-                // ANIMATION_TYPE_HEADS_UP_APPEAR
-                new AnimationFilter()
-                        .animateShadowAlpha()
-                        .animateHeight()
-                        .animateTopInset()
-                        .animateY()
-                        .animateZ(),
-
-                // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
-                new AnimationFilter()
-                        .animateShadowAlpha()
-                        .animateHeight()
-                        .animateTopInset()
-                        .animateY()
-                        .animateZ()
-                        .hasDelays(),
-
-                // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
-                new AnimationFilter()
-                        .animateShadowAlpha()
-                        .animateHeight()
-                        .animateTopInset()
-                        .animateY()
-                        .animateZ()
-                        .hasDelays(),
-
-                // ANIMATION_TYPE_HEADS_UP_OTHER
-                new AnimationFilter()
-                        .animateShadowAlpha()
-                        .animateHeight()
-                        .animateTopInset()
-                        .animateY()
-                        .animateZ(),
-
-                // ANIMATION_TYPE_EVERYTHING
-                new AnimationFilter()
-                        .animateAlpha()
-                        .animateShadowAlpha()
-                        .animateDark()
-                        .animateDimmed()
-                        .animateHideSensitive()
-                        .animateHeight()
-                        .animateTopInset()
-                        .animateY()
-                        .animateZ(),
-
-                // ANIMATION_TYPE_PULSE_APPEAR
-                new AnimationFilter()
-                        .animateAlpha()
-                        .animateY(),
-
-                // ANIMATION_TYPE_PULSE_DISAPPEAR
-                new AnimationFilter()
-                        .animateAlpha()
-                        .animateY(),
-        };
-
-        static int[] LENGTHS = new int[] {
-
-                // ANIMATION_TYPE_ADD
-                StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
-
-                // ANIMATION_TYPE_REMOVE
-                StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
-
-                // ANIMATION_TYPE_REMOVE_SWIPED_OUT
-                StackStateAnimator.ANIMATION_DURATION_STANDARD,
-
-                // ANIMATION_TYPE_TOP_PADDING_CHANGED
-                StackStateAnimator.ANIMATION_DURATION_STANDARD,
-
-                // ANIMATION_TYPE_START_DRAG
-                StackStateAnimator.ANIMATION_DURATION_STANDARD,
-
-                // ANIMATION_TYPE_SNAP_BACK
-                StackStateAnimator.ANIMATION_DURATION_STANDARD,
-
-                // ANIMATION_TYPE_ACTIVATED_CHILD
-                StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
-
-                // ANIMATION_TYPE_DIMMED
-                StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
-
-                // ANIMATION_TYPE_CHANGE_POSITION
-                StackStateAnimator.ANIMATION_DURATION_STANDARD,
-
-                // ANIMATION_TYPE_DARK
-                StackStateAnimator.ANIMATION_DURATION_WAKEUP,
-
-                // ANIMATION_TYPE_GO_TO_FULL_SHADE
-                StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
-
-                // ANIMATION_TYPE_HIDE_SENSITIVE
-                StackStateAnimator.ANIMATION_DURATION_STANDARD,
-
-                // ANIMATION_TYPE_VIEW_RESIZE
-                StackStateAnimator.ANIMATION_DURATION_STANDARD,
-
-                // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
-                StackStateAnimator.ANIMATION_DURATION_STANDARD,
-
-                // ANIMATION_TYPE_HEADS_UP_APPEAR
-                StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
-
-                // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
-                StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
-
-                // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
-                StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
-
-                // ANIMATION_TYPE_HEADS_UP_OTHER
-                StackStateAnimator.ANIMATION_DURATION_STANDARD,
-
-                // ANIMATION_TYPE_EVERYTHING
-                StackStateAnimator.ANIMATION_DURATION_STANDARD,
-
-                // ANIMATION_TYPE_PULSE_APPEAR
-                KeyguardSliceView.DEFAULT_ANIM_DURATION,
-
-                // ANIMATION_TYPE_PULSE_DISAPPEAR
-                KeyguardSliceView.DEFAULT_ANIM_DURATION / 2,
-        };
-
-        static final int ANIMATION_TYPE_ADD = 0;
-        static final int ANIMATION_TYPE_REMOVE = 1;
-        static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
-        static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
-        static final int ANIMATION_TYPE_START_DRAG = 4;
-        static final int ANIMATION_TYPE_SNAP_BACK = 5;
-        static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
-        static final int ANIMATION_TYPE_DIMMED = 7;
-        static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
-        static final int ANIMATION_TYPE_DARK = 9;
-        static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
-        static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
-        static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
-        static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
-        static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
-        static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
-        static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
-        static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
-        static final int ANIMATION_TYPE_EVERYTHING = 18;
-        static final int ANIMATION_TYPE_PULSE_APPEAR = 19;
-        static final int ANIMATION_TYPE_PULSE_DISAPPEAR = 20;
-
-        static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
-        static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
-
-        final long eventStartTime;
-        final View changingView;
-        final int animationType;
-        final AnimationFilter filter;
-        final long length;
-        View viewAfterChangingView;
-        int darkAnimationOriginIndex;
-        boolean headsUpFromBottom;
-
-        AnimationEvent(View view, int type) {
-            this(view, type, LENGTHS[type]);
-        }
-
-        AnimationEvent(View view, int type, AnimationFilter filter) {
-            this(view, type, LENGTHS[type], filter);
-        }
-
-        AnimationEvent(View view, int type, long length) {
-            this(view, type, length, FILTERS[type]);
-        }
-
-        AnimationEvent(View view, int type, long length, AnimationFilter filter) {
-            eventStartTime = AnimationUtils.currentAnimationTimeMillis();
-            changingView = view;
-            animationType = type;
-            this.length = length;
-            this.filter = filter;
-        }
-
-        /**
-         * Combines the length of several animation events into a single value.
-         *
-         * @param events The events of the lengths to combine.
-         * @return The combined length. Depending on the event types, this might be the maximum of
-         *         all events or the length of a specific event.
-         */
-        static long combineLength(ArrayList<AnimationEvent> events) {
-            long length = 0;
-            int size = events.size();
-            for (int i = 0; i < size; i++) {
-                AnimationEvent event = events.get(i);
-                length = Math.max(length, event.length);
-                if (event.animationType == ANIMATION_TYPE_GO_TO_FULL_SHADE) {
-                    return event.length;
-                }
-            }
-            return length;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
deleted file mode 100644
index 0d50f5a..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ /dev/null
@@ -1,646 +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
- */
-
-package com.android.systemui.statusbar.stack;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.FooterView;
-import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.notification.NotificationUtils;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * The Algorithm of the {@link com.android.systemui.statusbar.stack
- * .NotificationStackScrollLayout} which can be queried for {@link com.android.systemui.statusbar
- * .stack.StackScrollState}
- */
-public class StackScrollAlgorithm {
-
-    private static final String LOG_TAG = "StackScrollAlgorithm";
-
-    private int mPaddingBetweenElements;
-    private int mIncreasedPaddingBetweenElements;
-    private int mCollapsedSize;
-
-    private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
-    private boolean mIsExpanded;
-    private boolean mClipNotificationScrollToTop;
-    private int mStatusBarHeight;
-    private float mHeadsUpInset;
-    private int mPinnedZTranslationExtra;
-
-    public StackScrollAlgorithm(Context context) {
-        initView(context);
-    }
-
-    public void initView(Context context) {
-        initConstants(context);
-    }
-
-    private void initConstants(Context context) {
-        Resources res = context.getResources();
-        mPaddingBetweenElements = res.getDimensionPixelSize(
-                R.dimen.notification_divider_height);
-        mIncreasedPaddingBetweenElements =
-                res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
-        mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
-        mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
-        mClipNotificationScrollToTop = res.getBoolean(R.bool.config_clipNotificationScrollToTop);
-        mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
-                R.dimen.heads_up_status_bar_padding);
-        mPinnedZTranslationExtra = res.getDimensionPixelSize(
-                R.dimen.heads_up_pinned_elevation);
-    }
-
-    public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
-        // The state of the local variables are saved in an algorithmState to easily subdivide it
-        // into multiple phases.
-        StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
-
-        // First we reset the view states to their default values.
-        resultState.resetViewStates();
-
-        initAlgorithmState(resultState, algorithmState, ambientState);
-
-        updatePositionsForState(resultState, algorithmState, ambientState);
-
-        updateZValuesForState(resultState, algorithmState, ambientState);
-
-        updateHeadsUpStates(resultState, algorithmState, ambientState);
-
-        handleDraggedViews(ambientState, resultState, algorithmState);
-        updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState);
-        updateClipping(resultState, algorithmState, ambientState);
-        updateSpeedBumpState(resultState, algorithmState, ambientState);
-        updateShelfState(resultState, ambientState);
-        getNotificationChildrenStates(resultState, algorithmState, ambientState);
-    }
-
-    private void getNotificationChildrenStates(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState,
-            AmbientState ambientState) {
-        int childCount = algorithmState.visibleChildren.size();
-        for (int i = 0; i < childCount; i++) {
-            ExpandableView v = algorithmState.visibleChildren.get(i);
-            if (v instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-                row.getChildrenStates(resultState, ambientState);
-            }
-        }
-    }
-
-    private void updateSpeedBumpState(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
-        int childCount = algorithmState.visibleChildren.size();
-        int belowSpeedBump = ambientState.getSpeedBumpIndex();
-        for (int i = 0; i < childCount; i++) {
-            View child = algorithmState.visibleChildren.get(i);
-            ExpandableViewState childViewState = resultState.getViewStateForView(child);
-
-            // The speed bump can also be gone, so equality needs to be taken when comparing
-            // indices.
-            childViewState.belowSpeedBump = i >= belowSpeedBump;
-        }
-
-    }
-    private void updateShelfState(StackScrollState resultState, AmbientState ambientState) {
-        NotificationShelf shelf = ambientState.getShelf();
-        if (shelf != null) {
-            shelf.updateState(resultState, ambientState);
-        }
-    }
-
-    private void updateClipping(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
-        float drawStart = !ambientState.isOnKeyguard() ? ambientState.getTopPadding()
-                + ambientState.getStackTranslation() + ambientState.getExpandAnimationTopChange()
-                : 0;
-        float previousNotificationEnd = 0;
-        float previousNotificationStart = 0;
-        int childCount = algorithmState.visibleChildren.size();
-        for (int i = 0; i < childCount; i++) {
-            ExpandableView child = algorithmState.visibleChildren.get(i);
-            ExpandableViewState state = resultState.getViewStateForView(child);
-            if (!child.mustStayOnScreen() || state.headsUpIsVisible) {
-                previousNotificationEnd = Math.max(drawStart, previousNotificationEnd);
-                previousNotificationStart = Math.max(drawStart, previousNotificationStart);
-            }
-            float newYTranslation = state.yTranslation;
-            float newHeight = state.height;
-            float newNotificationEnd = newYTranslation + newHeight;
-            boolean isHeadsUp = (child instanceof ExpandableNotificationRow)
-                    && ((ExpandableNotificationRow) child).isPinned();
-            if (mClipNotificationScrollToTop
-                    && !state.inShelf && newYTranslation < previousNotificationEnd
-                    && (!isHeadsUp || ambientState.isShadeExpanded())) {
-                // The previous view is overlapping on top, clip!
-                float overlapAmount = previousNotificationEnd - newYTranslation;
-                state.clipTopAmount = (int) overlapAmount;
-            } else {
-                state.clipTopAmount = 0;
-            }
-
-            if (!child.isTransparent()) {
-                // Only update the previous values if we are not transparent,
-                // otherwise we would clip to a transparent view.
-                previousNotificationEnd = newNotificationEnd;
-                previousNotificationStart = newYTranslation;
-            }
-        }
-    }
-
-    public static boolean canChildBeDismissed(View v) {
-        if (!(v instanceof ExpandableNotificationRow)) {
-            return false;
-        }
-        ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-        if (row.areGutsExposed()) {
-            return false;
-        }
-        return row.canViewBeDismissed();
-    }
-
-    /**
-     * Updates the dimmed, activated and hiding sensitive states of the children.
-     */
-    private void updateDimmedActivatedHideSensitive(AmbientState ambientState,
-            StackScrollState resultState, StackScrollAlgorithmState algorithmState) {
-        boolean dimmed = ambientState.isDimmed();
-        boolean dark = ambientState.isFullyDark();
-        boolean hideSensitive = ambientState.isHideSensitive();
-        View activatedChild = ambientState.getActivatedChild();
-        int childCount = algorithmState.visibleChildren.size();
-        for (int i = 0; i < childCount; i++) {
-            View child = algorithmState.visibleChildren.get(i);
-            ExpandableViewState childViewState = resultState.getViewStateForView(child);
-            childViewState.dimmed = dimmed;
-            childViewState.dark = dark;
-            childViewState.hideSensitive = hideSensitive;
-            boolean isActivatedChild = activatedChild == child;
-            if (dimmed && isActivatedChild) {
-                childViewState.zTranslation += 2.0f * ambientState.getZDistanceBetweenElements();
-            }
-        }
-    }
-
-    /**
-     * Handle the special state when views are being dragged
-     */
-    private void handleDraggedViews(AmbientState ambientState, StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState) {
-        ArrayList<View> draggedViews = ambientState.getDraggedViews();
-        for (View draggedView : draggedViews) {
-            int childIndex = algorithmState.visibleChildren.indexOf(draggedView);
-            if (childIndex >= 0 && childIndex < algorithmState.visibleChildren.size() - 1) {
-                View nextChild = algorithmState.visibleChildren.get(childIndex + 1);
-                if (!draggedViews.contains(nextChild)) {
-                    // only if the view is not dragged itself we modify its state to be fully
-                    // visible
-                    ExpandableViewState viewState = resultState.getViewStateForView(
-                            nextChild);
-                    // The child below the dragged one must be fully visible
-                    if (ambientState.isShadeExpanded()) {
-                        viewState.shadowAlpha = 1;
-                        viewState.hidden = false;
-                    }
-                }
-
-                // Lets set the alpha to the one it currently has, as its currently being dragged
-                ExpandableViewState viewState = resultState.getViewStateForView(draggedView);
-                // The dragged child should keep the set alpha
-                viewState.alpha = draggedView.getAlpha();
-            }
-        }
-    }
-
-    /**
-     * Initialize the algorithm state like updating the visible children.
-     */
-    private void initAlgorithmState(StackScrollState resultState, StackScrollAlgorithmState state,
-            AmbientState ambientState) {
-        float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
-
-        int scrollY = ambientState.getScrollY();
-
-        // Due to the overScroller, the stackscroller can have negative scroll state. This is
-        // already accounted for by the top padding and doesn't need an additional adaption
-        scrollY = Math.max(0, scrollY);
-        state.scrollY = (int) (scrollY + bottomOverScroll);
-
-        //now init the visible children and update paddings
-        ViewGroup hostView = resultState.getHostView();
-        int childCount = hostView.getChildCount();
-        state.visibleChildren.clear();
-        state.visibleChildren.ensureCapacity(childCount);
-        state.paddingMap.clear();
-        int notGoneIndex = 0;
-        ExpandableView lastView = null;
-        int firstHiddenIndex = ambientState.isDark()
-                ? (ambientState.hasPulsingNotifications() ? 1 : 0)
-                : childCount;
-
-        // The goal here is to fill the padding map, by iterating over how much padding each child
-        // needs. The map is thereby reused, by first filling it with the padding amount and when
-        // iterating over it again, it's filled with the actual resolved value.
-
-        for (int i = 0; i < childCount; i++) {
-            ExpandableView v = (ExpandableView) hostView.getChildAt(i);
-            if (v.getVisibility() != View.GONE) {
-                if (v == ambientState.getShelf()) {
-                    continue;
-                }
-                if (i >= firstHiddenIndex) {
-                    // we need normal padding now, to be in sync with what the stack calculates
-                    lastView = null;
-                }
-                notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
-                float increasedPadding = v.getIncreasedPaddingAmount();
-                if (increasedPadding != 0.0f) {
-                    state.paddingMap.put(v, increasedPadding);
-                    if (lastView != null) {
-                        Float prevValue = state.paddingMap.get(lastView);
-                        float newValue = getPaddingForValue(increasedPadding);
-                        if (prevValue != null) {
-                            float prevPadding = getPaddingForValue(prevValue);
-                            if (increasedPadding > 0) {
-                                newValue = NotificationUtils.interpolate(
-                                        prevPadding,
-                                        newValue,
-                                        increasedPadding);
-                            } else if (prevValue > 0) {
-                                newValue = NotificationUtils.interpolate(
-                                        newValue,
-                                        prevPadding,
-                                        prevValue);
-                            }
-                        }
-                        state.paddingMap.put(lastView, newValue);
-                    }
-                } else if (lastView != null) {
-
-                    // Let's now resolve the value to an actual padding
-                    float newValue = getPaddingForValue(state.paddingMap.get(lastView));
-                    state.paddingMap.put(lastView, newValue);
-                }
-                if (v instanceof ExpandableNotificationRow) {
-                    ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-
-                    // handle the notgoneIndex for the children as well
-                    List<ExpandableNotificationRow> children =
-                            row.getNotificationChildren();
-                    if (row.isSummaryWithChildren() && children != null) {
-                        for (ExpandableNotificationRow childRow : children) {
-                            if (childRow.getVisibility() != View.GONE) {
-                                ExpandableViewState childState
-                                        = resultState.getViewStateForView(childRow);
-                                childState.notGoneIndex = notGoneIndex;
-                                notGoneIndex++;
-                            }
-                        }
-                    }
-                }
-                lastView = v;
-            }
-        }
-        ExpandableNotificationRow expandingNotification = ambientState.getExpandingNotification();
-        state.indexOfExpandingNotification = expandingNotification != null
-                ? expandingNotification.isChildInGroup()
-                    ? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
-                    : state.visibleChildren.indexOf(expandingNotification)
-                : -1;
-    }
-
-    private float getPaddingForValue(Float increasedPadding) {
-        if (increasedPadding == null) {
-            return mPaddingBetweenElements;
-        } else if (increasedPadding >= 0.0f) {
-            return NotificationUtils.interpolate(
-                    mPaddingBetweenElements,
-                    mIncreasedPaddingBetweenElements,
-                    increasedPadding);
-        } else {
-            return NotificationUtils.interpolate(
-                    0,
-                    mPaddingBetweenElements,
-                    1.0f + increasedPadding);
-        }
-    }
-
-    private int updateNotGoneIndex(StackScrollState resultState,
-            StackScrollAlgorithmState state, int notGoneIndex,
-            ExpandableView v) {
-        ExpandableViewState viewState = resultState.getViewStateForView(v);
-        viewState.notGoneIndex = notGoneIndex;
-        state.visibleChildren.add(v);
-        notGoneIndex++;
-        return notGoneIndex;
-    }
-
-    /**
-     * Determine the positions for the views. This is the main part of the algorithm.
-     *
-     * @param resultState The result state to update if a change to the properties of a child occurs
-     * @param algorithmState The state in which the current pass of the algorithm is currently in
-     * @param ambientState The current ambient state
-     */
-    private void updatePositionsForState(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
-
-        // The y coordinate of the current child.
-        float currentYPosition = -algorithmState.scrollY;
-        int childCount = algorithmState.visibleChildren.size();
-        for (int i = 0; i < childCount; i++) {
-            currentYPosition = updateChild(i, resultState, algorithmState, ambientState,
-                    currentYPosition);
-        }
-    }
-
-    protected float updateChild(int i, StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState,
-            float currentYPosition) {
-        ExpandableView child = algorithmState.visibleChildren.get(i);
-        ExpandableViewState childViewState = resultState.getViewStateForView(child);
-        childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
-        int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
-        int childHeight = getMaxAllowedChildHeight(child);
-        childViewState.yTranslation = currentYPosition;
-        boolean isFooterView = child instanceof FooterView;
-        boolean isEmptyShadeView = child instanceof EmptyShadeView;
-
-        childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
-        float inset = ambientState.getTopPadding() + ambientState.getStackTranslation();
-        if (i <= algorithmState.getIndexOfExpandingNotification()) {
-            inset += ambientState.getExpandAnimationTopChange();
-        }
-        if (child.mustStayOnScreen() && childViewState.yTranslation >= 0) {
-            // Even if we're not scrolled away we're in view and we're also not in the
-            // shelf. We can relax the constraints and let us scroll off the top!
-            float end = childViewState.yTranslation + childViewState.height + inset;
-            childViewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation();
-        }
-        if (isFooterView) {
-            childViewState.yTranslation = Math.min(childViewState.yTranslation,
-                    ambientState.getInnerHeight() - childHeight);
-        } else if (isEmptyShadeView) {
-            childViewState.yTranslation = ambientState.getInnerHeight() - childHeight
-                    + ambientState.getStackTranslation() * 0.25f;
-        } else {
-            clampPositionToShelf(child, childViewState, ambientState);
-        }
-
-        currentYPosition = childViewState.yTranslation + childHeight + paddingAfterChild;
-        if (currentYPosition <= 0) {
-            childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
-        }
-        if (childViewState.location == ExpandableViewState.LOCATION_UNKNOWN) {
-            Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
-        }
-
-        childViewState.yTranslation += inset;
-        return currentYPosition;
-    }
-
-    protected int getPaddingAfterChild(StackScrollAlgorithmState algorithmState,
-            ExpandableView child) {
-        return algorithmState.getPaddingAfterChild(child);
-    }
-
-    private void updateHeadsUpStates(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
-        int childCount = algorithmState.visibleChildren.size();
-        ExpandableNotificationRow topHeadsUpEntry = null;
-        for (int i = 0; i < childCount; i++) {
-            View child = algorithmState.visibleChildren.get(i);
-            if (!(child instanceof ExpandableNotificationRow)) {
-                break;
-            }
-            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            if (!row.isHeadsUp()) {
-                break;
-            }
-            ExpandableViewState childState = resultState.getViewStateForView(row);
-            if (topHeadsUpEntry == null && row.mustStayOnScreen() && !childState.headsUpIsVisible) {
-                topHeadsUpEntry = row;
-                childState.location = ExpandableViewState.LOCATION_FIRST_HUN;
-            }
-            boolean isTopEntry = topHeadsUpEntry == row;
-            float unmodifiedEndLocation = childState.yTranslation + childState.height;
-            if (mIsExpanded) {
-                if (row.mustStayOnScreen() && !childState.headsUpIsVisible) {
-                    // Ensure that the heads up is always visible even when scrolled off
-                    clampHunToTop(ambientState, row, childState);
-                    if (i == 0 && ambientState.isAboveShelf(row)) {
-                        // the first hun can't get off screen.
-                        clampHunToMaxTranslation(ambientState, row, childState);
-                        childState.hidden = false;
-                    }
-                }
-            }
-            if (row.isPinned()) {
-                childState.yTranslation = Math.max(childState.yTranslation, mHeadsUpInset);
-                childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
-                childState.hidden = false;
-                ExpandableViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
-                if (topState != null && !isTopEntry && (!mIsExpanded
-                        || unmodifiedEndLocation < topState.yTranslation + topState.height)) {
-                    // Ensure that a headsUp doesn't vertically extend further than the heads-up at
-                    // the top most z-position
-                    childState.height = row.getIntrinsicHeight();
-                    childState.yTranslation = topState.yTranslation + topState.height
-                            - childState.height;
-                }
-            }
-            if (row.isHeadsUpAnimatingAway()) {
-                childState.hidden = false;
-            }
-        }
-    }
-
-    private void clampHunToTop(AmbientState ambientState, ExpandableNotificationRow row,
-            ExpandableViewState childState) {
-        float newTranslation = Math.max(ambientState.getTopPadding()
-                + ambientState.getStackTranslation(), childState.yTranslation);
-        childState.height = (int) Math.max(childState.height - (newTranslation
-                - childState.yTranslation), row.getCollapsedHeight());
-        childState.yTranslation = newTranslation;
-    }
-
-    private void clampHunToMaxTranslation(AmbientState ambientState, ExpandableNotificationRow row,
-            ExpandableViewState childState) {
-        float newTranslation;
-        float maxHeadsUpTranslation = ambientState.getMaxHeadsUpTranslation();
-        float maxShelfPosition = ambientState.getInnerHeight() + ambientState.getTopPadding()
-                + ambientState.getStackTranslation();
-        maxHeadsUpTranslation = Math.min(maxHeadsUpTranslation, maxShelfPosition);
-        float bottomPosition = maxHeadsUpTranslation - row.getCollapsedHeight();
-        newTranslation = Math.min(childState.yTranslation, bottomPosition);
-        childState.height = (int) Math.min(childState.height, maxHeadsUpTranslation
-                - newTranslation);
-        childState.yTranslation = newTranslation;
-    }
-
-    /**
-     * Clamp the height of the child down such that its end is at most on the beginning of
-     * the shelf.
-     *
-     * @param child
-     * @param childViewState the view state of the child
-     * @param ambientState the ambient state
-     */
-    private void clampPositionToShelf(ExpandableView child,
-            ExpandableViewState childViewState,
-            AmbientState ambientState) {
-        if (ambientState.getShelf() == null) {
-            return;
-        }
-
-        int shelfStart = ambientState.getInnerHeight()
-                - ambientState.getShelf().getIntrinsicHeight();
-        if (ambientState.isAppearing() && !child.isAboveShelf()) {
-            // Don't show none heads-up notifications while in appearing phase.
-            childViewState.yTranslation = Math.max(childViewState.yTranslation, shelfStart);
-        }
-        childViewState.yTranslation = Math.min(childViewState.yTranslation, shelfStart);
-        if (childViewState.yTranslation >= shelfStart) {
-            childViewState.hidden = !child.isExpandAnimationRunning() && !child.hasExpandingChild();
-            childViewState.inShelf = true;
-            childViewState.headsUpIsVisible = false;
-        }
-    }
-
-    protected int getMaxAllowedChildHeight(View child) {
-        if (child instanceof ExpandableView) {
-            ExpandableView expandableView = (ExpandableView) child;
-            return expandableView.getIntrinsicHeight();
-        }
-        return child == null? mCollapsedSize : child.getHeight();
-    }
-
-    /**
-     * Calculate the Z positions for all children based on the number of items in both stacks and
-     * save it in the resultState
-     *  @param resultState The result state to update the zTranslation values
-     * @param algorithmState The state in which the current pass of the algorithm is currently in
-     * @param ambientState The ambient state of the algorithm
-     */
-    private void updateZValuesForState(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
-        int childCount = algorithmState.visibleChildren.size();
-        float childrenOnTop = 0.0f;
-        for (int i = childCount - 1; i >= 0; i--) {
-            childrenOnTop = updateChildZValue(i, childrenOnTop,
-                    resultState, algorithmState, ambientState);
-        }
-    }
-
-    protected float updateChildZValue(int i, float childrenOnTop,
-            StackScrollState resultState, StackScrollAlgorithmState algorithmState,
-            AmbientState ambientState) {
-        ExpandableView child = algorithmState.visibleChildren.get(i);
-        ExpandableViewState childViewState = resultState.getViewStateForView(child);
-        int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements();
-        float baseZ = ambientState.getBaseZHeight();
-        if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
-                && !ambientState.isDozingAndNotPulsing(child)
-                && childViewState.yTranslation < ambientState.getTopPadding()
-                + ambientState.getStackTranslation()) {
-            if (childrenOnTop != 0.0f) {
-                childrenOnTop++;
-            } else {
-                float overlap = ambientState.getTopPadding()
-                        + ambientState.getStackTranslation() - childViewState.yTranslation;
-                childrenOnTop += Math.min(1.0f, overlap / childViewState.height);
-            }
-            childViewState.zTranslation = baseZ
-                    + childrenOnTop * zDistanceBetweenElements;
-        } else if (i == 0 && ambientState.isAboveShelf(child)) {
-            // In case this is a new view that has never been measured before, we don't want to
-            // elevate if we are currently expanded more then the notification
-            int shelfHeight = ambientState.getShelf() == null ? 0 :
-                    ambientState.getShelf().getIntrinsicHeight();
-            float shelfStart = ambientState.getInnerHeight()
-                    - shelfHeight + ambientState.getTopPadding()
-                    + ambientState.getStackTranslation();
-            float notificationEnd = childViewState.yTranslation + child.getPinnedHeadsUpHeight()
-                    + mPaddingBetweenElements;
-            if (shelfStart > notificationEnd) {
-                childViewState.zTranslation = baseZ;
-            } else {
-                float factor = (notificationEnd - shelfStart) / shelfHeight;
-                factor = Math.min(factor, 1.0f);
-                childViewState.zTranslation = baseZ + factor * zDistanceBetweenElements;
-            }
-        } else {
-            childViewState.zTranslation = baseZ;
-        }
-
-        // We need to scrim the notification more from its surrounding content when we are pinned,
-        // and we therefore elevate it higher.
-        // We can use the headerVisibleAmount for this, since the value nicely goes from 0 to 1 when
-        // expanding after which we have a normal elevation again.
-        childViewState.zTranslation += (1.0f - child.getHeaderVisibleAmount())
-                * mPinnedZTranslationExtra;
-        return childrenOnTop;
-    }
-
-    public void setIsExpanded(boolean isExpanded) {
-        this.mIsExpanded = isExpanded;
-    }
-
-    public class StackScrollAlgorithmState {
-
-        /**
-         * The scroll position of the algorithm
-         */
-        public int scrollY;
-
-        /**
-         * The children from the host view which are not gone.
-         */
-        public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>();
-
-        /**
-         * The padding after each child measured in pixels.
-         */
-        public final HashMap<ExpandableView, Float> paddingMap = new HashMap<>();
-        private int indexOfExpandingNotification;
-
-        public int getPaddingAfterChild(ExpandableView child) {
-            Float padding = paddingMap.get(child);
-            if (padding == null) {
-                // Should only happen for the last view
-                return mPaddingBetweenElements;
-            }
-            return (int) padding.floatValue();
-        }
-
-        public int getIndexOfExpandingNotification() {
-            return indexOfExpandingNotification;
-        }
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
deleted file mode 100644
index 588b758..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ /dev/null
@@ -1,118 +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
- */
-
-package com.android.systemui.statusbar.stack;
-
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
-
-import java.util.List;
-import java.util.WeakHashMap;
-
-/**
- * A state of a {@link com.android.systemui.statusbar.stack.NotificationStackScrollLayout} which
- * can be applied to a viewGroup.
- */
-public class StackScrollState {
-
-    private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
-
-    private final ViewGroup mHostView;
-    private WeakHashMap<ExpandableView, ExpandableViewState> mStateMap;
-
-    public StackScrollState(ViewGroup hostView) {
-        mHostView = hostView;
-        mStateMap = new WeakHashMap<>();
-    }
-
-    public ViewGroup getHostView() {
-        return mHostView;
-    }
-
-    public void resetViewStates() {
-        int numChildren = mHostView.getChildCount();
-        for (int i = 0; i < numChildren; i++) {
-            ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
-            resetViewState(child);
-
-            // handling reset for child notifications
-            if (child instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                List<ExpandableNotificationRow> children =
-                        row.getNotificationChildren();
-                if (row.isSummaryWithChildren() && children != null) {
-                    for (ExpandableNotificationRow childRow : children) {
-                        resetViewState(childRow);
-                    }
-                }
-            }
-        }
-    }
-
-    private void resetViewState(ExpandableView view) {
-        ExpandableViewState viewState = mStateMap.get(view);
-        if (viewState == null) {
-            viewState = view.createNewViewState(this);
-            mStateMap.put(view, viewState);
-        }
-        // initialize with the default values of the view
-        viewState.height = view.getIntrinsicHeight();
-        viewState.gone = view.getVisibility() == View.GONE;
-        viewState.alpha = 1f;
-        viewState.shadowAlpha = 1f;
-        viewState.notGoneIndex = -1;
-        viewState.xTranslation = view.getTranslationX();
-        viewState.hidden = false;
-        viewState.scaleX = view.getScaleX();
-        viewState.scaleY = view.getScaleY();
-        viewState.inShelf = false;
-        viewState.headsUpIsVisible = false;
-    }
-
-    public ExpandableViewState getViewStateForView(View requestedView) {
-        return mStateMap.get(requestedView);
-    }
-
-    public void removeViewStateForView(View child) {
-        mStateMap.remove(child);
-    }
-
-    /**
-     * Apply the properties saved in {@link #mStateMap} to the children of the {@link #mHostView}.
-     * The properties are only applied if they effectively changed.
-     */
-    public void apply() {
-        int numChildren = mHostView.getChildCount();
-        for (int i = 0; i < numChildren; i++) {
-            ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
-            ExpandableViewState state = mStateMap.get(child);
-            if (state == null) {
-                Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
-                        "to the hostView");
-                continue;
-            }
-            if (state.gone) {
-                continue;
-            }
-            state.applyToView(child);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
deleted file mode 100644
index a75d40f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ /dev/null
@@ -1,577 +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
- */
-
-package com.android.systemui.statusbar.stack;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.util.Property;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.Interpolator;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.StatusBarIconView;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Stack;
-
-/**
- * An stack state animator which handles animations to new StackScrollStates
- */
-public class StackStateAnimator {
-
-    public static final int ANIMATION_DURATION_STANDARD = 360;
-    public static final int ANIMATION_DURATION_WAKEUP = 500;
-    public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
-    public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
-    public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
-    public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
-    public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 550;
-    public static final int ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED
-            = (int) (ANIMATION_DURATION_HEADS_UP_APPEAR
-                    * HeadsUpAppearInterpolator.getFractionUntilOvershoot());
-    public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 300;
-    public static final int ANIMATION_DURATION_BLOCKING_HELPER_FADE = 240;
-    public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
-    public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
-    public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
-    public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
-    public static final int ANIMATION_DELAY_HEADS_UP = 120;
-    public static final int ANIMATION_DELAY_HEADS_UP_CLICKED= 120;
-
-    private final int mGoToFullShadeAppearingTranslation;
-    private final int mPulsingAppearingTranslation;
-    private final ExpandableViewState mTmpState = new ExpandableViewState();
-    private final AnimationProperties mAnimationProperties;
-    public NotificationStackScrollLayout mHostLayout;
-    private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
-            new ArrayList<>();
-    private ArrayList<View> mNewAddChildren = new ArrayList<>();
-    private HashSet<View> mHeadsUpAppearChildren = new HashSet<>();
-    private HashSet<View> mHeadsUpDisappearChildren = new HashSet<>();
-    private HashSet<Animator> mAnimatorSet = new HashSet<>();
-    private Stack<AnimatorListenerAdapter> mAnimationListenerPool = new Stack<>();
-    private AnimationFilter mAnimationFilter = new AnimationFilter();
-    private long mCurrentLength;
-    private long mCurrentAdditionalDelay;
-
-    /** The current index for the last child which was not added in this event set. */
-    private int mCurrentLastNotAddedIndex;
-    private ValueAnimator mTopOverScrollAnimator;
-    private ValueAnimator mBottomOverScrollAnimator;
-    private int mHeadsUpAppearHeightBottom;
-    private boolean mShadeExpanded;
-    private ArrayList<ExpandableView> mTransientViewsToRemove = new ArrayList<>();
-    private NotificationShelf mShelf;
-    private float mStatusBarIconLocation;
-    private int[] mTmpLocation = new int[2];
-
-    public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
-        mHostLayout = hostLayout;
-        mGoToFullShadeAppearingTranslation =
-                hostLayout.getContext().getResources().getDimensionPixelSize(
-                        R.dimen.go_to_full_shade_appearing_translation);
-        mPulsingAppearingTranslation =
-                hostLayout.getContext().getResources().getDimensionPixelSize(
-                        R.dimen.pulsing_notification_appear_translation);
-        mAnimationProperties = new AnimationProperties() {
-            @Override
-            public AnimationFilter getAnimationFilter() {
-                return mAnimationFilter;
-            }
-
-            @Override
-            public AnimatorListenerAdapter getAnimationFinishListener() {
-                return getGlobalAnimationFinishedListener();
-            }
-
-            @Override
-            public boolean wasAdded(View view) {
-                return mNewAddChildren.contains(view);
-            }
-
-            @Override
-            public Interpolator getCustomInterpolator(View child, Property property) {
-                if (mHeadsUpAppearChildren.contains(child) && View.TRANSLATION_Y.equals(property)) {
-                    return Interpolators.HEADS_UP_APPEAR;
-                }
-                return null;
-            }
-        };
-    }
-
-    public boolean isRunning() {
-        return !mAnimatorSet.isEmpty();
-    }
-
-    public void startAnimationForEvents(
-            ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents,
-            StackScrollState finalState, long additionalDelay) {
-
-        processAnimationEvents(mAnimationEvents, finalState);
-
-        int childCount = mHostLayout.getChildCount();
-        mAnimationFilter.applyCombination(mNewEvents);
-        mCurrentAdditionalDelay = additionalDelay;
-        mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
-        mCurrentLastNotAddedIndex = findLastNotAddedIndex(finalState);
-        for (int i = 0; i < childCount; i++) {
-            final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
-
-            ExpandableViewState viewState = finalState.getViewStateForView(child);
-            if (viewState == null || child.getVisibility() == View.GONE
-                    || applyWithoutAnimation(child, viewState, finalState)) {
-                continue;
-            }
-
-            initAnimationProperties(finalState, child, viewState);
-            viewState.animateTo(child, mAnimationProperties);
-        }
-        if (!isRunning()) {
-            // no child has preformed any animation, lets finish
-            onAnimationFinished();
-        }
-        mHeadsUpAppearChildren.clear();
-        mHeadsUpDisappearChildren.clear();
-        mNewEvents.clear();
-        mNewAddChildren.clear();
-    }
-
-    private void initAnimationProperties(StackScrollState finalState, ExpandableView child,
-            ExpandableViewState viewState) {
-        boolean wasAdded = mAnimationProperties.wasAdded(child);
-        mAnimationProperties.duration = mCurrentLength;
-        adaptDurationWhenGoingToFullShade(child, viewState, wasAdded);
-        mAnimationProperties.delay = 0;
-        if (wasAdded || mAnimationFilter.hasDelays
-                        && (viewState.yTranslation != child.getTranslationY()
-                        || viewState.zTranslation != child.getTranslationZ()
-                        || viewState.alpha != child.getAlpha()
-                        || viewState.height != child.getActualHeight()
-                        || viewState.clipTopAmount != child.getClipTopAmount()
-                        || viewState.dark != child.isDark()
-                        || viewState.shadowAlpha != child.getShadowAlpha())) {
-            mAnimationProperties.delay = mCurrentAdditionalDelay
-                    + calculateChildAnimationDelay(viewState, finalState);
-        }
-    }
-
-    private void adaptDurationWhenGoingToFullShade(ExpandableView child,
-            ExpandableViewState viewState, boolean wasAdded) {
-        if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) {
-            child.setTranslationY(child.getTranslationY() + mGoToFullShadeAppearingTranslation);
-            float longerDurationFactor = viewState.notGoneIndex - mCurrentLastNotAddedIndex;
-            longerDurationFactor = (float) Math.pow(longerDurationFactor, 0.7f);
-            mAnimationProperties.duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50 +
-                    (long) (100 * longerDurationFactor);
-        }
-    }
-
-    /**
-     * Determines if a view should not perform an animation and applies it directly.
-     *
-     * @return true if no animation should be performed
-     */
-    private boolean applyWithoutAnimation(ExpandableView child, ExpandableViewState viewState,
-            StackScrollState finalState) {
-        if (mShadeExpanded) {
-            return false;
-        }
-        if (ViewState.isAnimatingY(child)) {
-            // A Y translation animation is running
-            return false;
-        }
-        if (mHeadsUpDisappearChildren.contains(child) || mHeadsUpAppearChildren.contains(child)) {
-            // This is a heads up animation
-            return false;
-        }
-        if (NotificationStackScrollLayout.isPinnedHeadsUp(child)) {
-            // This is another headsUp which might move. Let's animate!
-            return false;
-        }
-        viewState.applyToView(child);
-        return true;
-    }
-
-    private int findLastNotAddedIndex(StackScrollState finalState) {
-        int childCount = mHostLayout.getChildCount();
-        for (int i = childCount - 1; i >= 0; i--) {
-            final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
-
-            ExpandableViewState viewState = finalState.getViewStateForView(child);
-            if (viewState == null || child.getVisibility() == View.GONE) {
-                continue;
-            }
-            if (!mNewAddChildren.contains(child)) {
-                return viewState.notGoneIndex;
-            }
-        }
-        return -1;
-    }
-
-    private long calculateChildAnimationDelay(ExpandableViewState viewState,
-            StackScrollState finalState) {
-        if (mAnimationFilter.hasGoToFullShadeEvent) {
-            return calculateDelayGoToFullShade(viewState);
-        }
-        if (mAnimationFilter.customDelay != AnimationFilter.NO_DELAY) {
-            return mAnimationFilter.customDelay;
-        }
-        long minDelay = 0;
-        for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
-            long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING;
-            switch (event.animationType) {
-                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: {
-                    int ownIndex = viewState.notGoneIndex;
-                    int changingIndex = finalState
-                            .getViewStateForView(event.changingView).notGoneIndex;
-                    int difference = Math.abs(ownIndex - changingIndex);
-                    difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
-                            difference - 1));
-                    long delay = (DELAY_EFFECT_MAX_INDEX_DIFFERENCE - difference) * delayPerElement;
-                    minDelay = Math.max(delay, minDelay);
-                    break;
-                }
-                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT:
-                    delayPerElement = ANIMATION_DELAY_PER_ELEMENT_MANUAL;
-                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: {
-                    int ownIndex = viewState.notGoneIndex;
-                    boolean noNextView = event.viewAfterChangingView == null;
-                    View viewAfterChangingView = noNextView
-                            ? mHostLayout.getLastChildNotGone()
-                            : event.viewAfterChangingView;
-                    if (viewAfterChangingView == null) {
-                        // This can happen when the last view in the list is removed.
-                        // Since the shelf is still around and the only view, the code still goes
-                        // in here and tries to calculate the delay for it when case its properties
-                        // have changed.
-                        continue;
-                    }
-                    int nextIndex = finalState
-                            .getViewStateForView(viewAfterChangingView).notGoneIndex;
-                    if (ownIndex >= nextIndex) {
-                        // we only have the view afterwards
-                        ownIndex++;
-                    }
-                    int difference = Math.abs(ownIndex - nextIndex);
-                    difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
-                            difference - 1));
-                    long delay = difference * delayPerElement;
-                    minDelay = Math.max(delay, minDelay);
-                    break;
-                }
-                default:
-                    break;
-            }
-        }
-        return minDelay;
-    }
-
-    private long calculateDelayGoToFullShade(ExpandableViewState viewState) {
-        int shelfIndex = mShelf.getNotGoneIndex();
-        float index = viewState.notGoneIndex;
-        long result = 0;
-        if (index > shelfIndex) {
-            float diff = index - shelfIndex;
-            diff = (float) Math.pow(diff, 0.7f);
-            result += (long) (diff * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE * 0.25);
-            index = shelfIndex;
-        }
-        index = (float) Math.pow(index, 0.7f);
-        result += (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE);
-        return result;
-    }
-
-    /**
-     * @return an adapter which ensures that onAnimationFinished is called once no animation is
-     *         running anymore
-     */
-    private AnimatorListenerAdapter getGlobalAnimationFinishedListener() {
-        if (!mAnimationListenerPool.empty()) {
-            return mAnimationListenerPool.pop();
-        }
-
-        // We need to create a new one, no reusable ones found
-        return new AnimatorListenerAdapter() {
-            private boolean mWasCancelled;
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mAnimatorSet.remove(animation);
-                if (mAnimatorSet.isEmpty() && !mWasCancelled) {
-                    onAnimationFinished();
-                }
-                mAnimationListenerPool.push(this);
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mWasCancelled = true;
-            }
-
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mWasCancelled = false;
-                mAnimatorSet.add(animation);
-            }
-        };
-    }
-
-    private void onAnimationFinished() {
-        mHostLayout.onChildAnimationFinished();
-
-        for (ExpandableView transientViewsToRemove : mTransientViewsToRemove) {
-            transientViewsToRemove.getTransientContainer()
-                    .removeTransientView(transientViewsToRemove);
-        }
-        mTransientViewsToRemove.clear();
-    }
-
-    /**
-     * Process the animationEvents for a new animation
-     *
-     * @param animationEvents the animation events for the animation to perform
-     * @param finalState the final state to animate to
-     */
-    private void processAnimationEvents(
-            ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
-            StackScrollState finalState) {
-        for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
-            final ExpandableView changingView = (ExpandableView) event.changingView;
-            if (event.animationType ==
-                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
-
-                // This item is added, initialize it's properties.
-                ExpandableViewState viewState = finalState
-                        .getViewStateForView(changingView);
-                if (viewState == null || viewState.gone) {
-                    // The position for this child was never generated, let's continue.
-                    continue;
-                }
-                viewState.applyToView(changingView);
-                mNewAddChildren.add(changingView);
-
-            } else if (event.animationType ==
-                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
-                if (changingView.getVisibility() != View.VISIBLE) {
-                    removeTransientView(changingView);
-                    continue;
-                }
-
-                // Find the amount to translate up. This is needed in order to understand the
-                // direction of the remove animation (either downwards or upwards)
-                ExpandableViewState viewState = finalState
-                        .getViewStateForView(event.viewAfterChangingView);
-                int actualHeight = changingView.getActualHeight();
-                // upwards by default
-                float translationDirection = -1.0f;
-                if (viewState != null) {
-                    float ownPosition = changingView.getTranslationY();
-                    if (changingView instanceof ExpandableNotificationRow
-                            && event.viewAfterChangingView instanceof ExpandableNotificationRow) {
-                        ExpandableNotificationRow changingRow =
-                                (ExpandableNotificationRow) changingView;
-                        ExpandableNotificationRow nextRow =
-                                (ExpandableNotificationRow) event.viewAfterChangingView;
-                        if (changingRow.isRemoved()
-                                && changingRow.wasChildInGroupWhenRemoved()
-                                && !nextRow.isChildInGroup()) {
-                            // the next row isn't actually a child from a group! Let's
-                            // compare absolute positions!
-                            ownPosition = changingRow.getTranslationWhenRemoved();
-                        }
-                    }
-                    // there was a view after this one, Approximate the distance the next child
-                    // travelled
-                    translationDirection = ((viewState.yTranslation
-                            - (ownPosition + actualHeight / 2.0f)) * 2 /
-                            actualHeight);
-                    translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
-
-                }
-                changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
-                        0 /* delay */, translationDirection,  false /* isHeadsUpAppear */,
-                        0, new Runnable() {
-                    @Override
-                    public void run() {
-                        removeTransientView(changingView);
-                    }
-                }, null);
-            } else if (event.animationType ==
-                NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
-                if (Math.abs(changingView.getTranslation()) == changingView.getWidth()
-                        && changingView.getTransientContainer() != null) {
-                    changingView.getTransientContainer().removeTransientView(changingView);
-                }
-            } else if (event.animationType == NotificationStackScrollLayout
-                    .AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
-                row.prepareExpansionChanged(finalState);
-            } else if (event.animationType == NotificationStackScrollLayout
-                    .AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR) {
-                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
-                mTmpState.copyFrom(viewState);
-                mTmpState.yTranslation += mPulsingAppearingTranslation;
-                mTmpState.alpha = 0;
-                mTmpState.applyToView(changingView);
-            } else if (event.animationType == NotificationStackScrollLayout
-                    .AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
-                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
-                viewState.yTranslation += mPulsingAppearingTranslation;
-                viewState.alpha = 0;
-            } else if (event.animationType == NotificationStackScrollLayout
-                    .AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
-                // This item is added, initialize it's properties.
-                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
-                mTmpState.copyFrom(viewState);
-                if (event.headsUpFromBottom) {
-                    mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
-                } else {
-                    mTmpState.yTranslation = 0;
-                    changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED,
-                            true /* isHeadsUpAppear */);
-                }
-                mHeadsUpAppearChildren.add(changingView);
-                mTmpState.applyToView(changingView);
-            } else if (event.animationType == NotificationStackScrollLayout
-                            .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR ||
-                    event.animationType == NotificationStackScrollLayout
-                            .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
-                mHeadsUpDisappearChildren.add(changingView);
-                Runnable endRunnable = null;
-                // We need some additional delay in case we were removed to make sure we're not
-                // lagging
-                int extraDelay = event.animationType == NotificationStackScrollLayout
-                        .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
-                        ? ANIMATION_DELAY_HEADS_UP_CLICKED
-                        : 0;
-                if (changingView.getParent() == null) {
-                    // This notification was actually removed, so we need to add it transiently
-                    mHostLayout.addTransientView(changingView, 0);
-                    changingView.setTransientContainer(mHostLayout);
-                    mTmpState.initFrom(changingView);
-                    mTmpState.yTranslation = 0;
-                    // We temporarily enable Y animations, the real filter will be combined
-                    // afterwards anyway
-                    mAnimationFilter.animateY = true;
-                    mAnimationProperties.delay = extraDelay + ANIMATION_DELAY_HEADS_UP;
-                    mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR;
-                    mTmpState.animateTo(changingView, mAnimationProperties);
-                    endRunnable = () -> removeTransientView(changingView);
-                }
-                float targetLocation = 0;
-                boolean needsAnimation = true;
-                if (changingView instanceof ExpandableNotificationRow) {
-                    ExpandableNotificationRow row = (ExpandableNotificationRow) changingView;
-                    if (row.isDismissed()) {
-                        needsAnimation = false;
-                    }
-                    StatusBarIconView icon = row.getEntry().icon;
-                    if (icon.getParent() != null) {
-                        icon.getLocationOnScreen(mTmpLocation);
-                        float iconPosition = mTmpLocation[0] - icon.getTranslationX()
-                                + ViewState.getFinalTranslationX(icon) + icon.getWidth() * 0.25f;
-                        mHostLayout.getLocationOnScreen(mTmpLocation);
-                        targetLocation = iconPosition - mTmpLocation[0];
-                    }
-                }
-
-                if (needsAnimation) {
-                    // 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());
-                } else if (endRunnable != null) {
-                    endRunnable.run();
-                }
-            }
-            mNewEvents.add(event);
-        }
-    }
-
-    public static void removeTransientView(ExpandableView viewToRemove) {
-        if (viewToRemove.getTransientContainer() != null) {
-            viewToRemove.getTransientContainer().removeTransientView(viewToRemove);
-        }
-    }
-
-    public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
-            final boolean isRubberbanded) {
-        final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
-        if (targetAmount == startOverScrollAmount) {
-            return;
-        }
-        cancelOverScrollAnimators(onTop);
-        ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount,
-                targetAmount);
-        overScrollAnimator.setDuration(ANIMATION_DURATION_STANDARD);
-        overScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                float currentOverScroll = (float) animation.getAnimatedValue();
-                mHostLayout.setOverScrollAmount(
-                        currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */,
-                        isRubberbanded);
-            }
-        });
-        overScrollAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        overScrollAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (onTop) {
-                    mTopOverScrollAnimator = null;
-                } else {
-                    mBottomOverScrollAnimator = null;
-                }
-            }
-        });
-        overScrollAnimator.start();
-        if (onTop) {
-            mTopOverScrollAnimator = overScrollAnimator;
-        } else {
-            mBottomOverScrollAnimator = overScrollAnimator;
-        }
-    }
-
-    public void cancelOverScrollAnimators(boolean onTop) {
-        ValueAnimator currentAnimator = onTop ? mTopOverScrollAnimator : mBottomOverScrollAnimator;
-        if (currentAnimator != null) {
-            currentAnimator.cancel();
-        }
-    }
-
-    public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
-        mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
-    }
-
-    public void setShadeExpanded(boolean shadeExpanded) {
-        mShadeExpanded = shadeExpanded;
-    }
-
-    public void setShelf(NotificationShelf shelf) {
-        mShelf = shelf;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
deleted file mode 100644
index 4b3643f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ /dev/null
@@ -1,713 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.stack;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.ValueAnimator;
-import android.util.Property;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
-
-/**
- * A state of a view. This can be used to apply a set of view properties to a view with
- * {@link com.android.systemui.statusbar.stack.StackScrollState} or start animations with
- * {@link com.android.systemui.statusbar.stack.StackStateAnimator}.
-*/
-public class ViewState {
-
-    /**
-     * Some animation properties that can be used to update running animations but not creating
-     * any new ones.
-     */
-    protected static final AnimationProperties NO_NEW_ANIMATIONS = new AnimationProperties() {
-        AnimationFilter mAnimationFilter = new AnimationFilter();
-        @Override
-        public AnimationFilter getAnimationFilter() {
-            return mAnimationFilter;
-        }
-    };
-    private static final int TAG_ANIMATOR_TRANSLATION_X = R.id.translation_x_animator_tag;
-    private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
-    private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
-    private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
-    private static final int TAG_END_TRANSLATION_X = R.id.translation_x_animator_end_value_tag;
-    private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
-    private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
-    private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
-    private static final int TAG_START_TRANSLATION_X = R.id.translation_x_animator_start_value_tag;
-    private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag;
-    private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag;
-    private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag;
-
-    private static final AnimatableProperty SCALE_X_PROPERTY
-            = new AnimatableProperty() {
-
-        @Override
-        public int getAnimationStartTag() {
-            return R.id.scale_x_animator_start_value_tag;
-        }
-
-        @Override
-        public int getAnimationEndTag() {
-            return R.id.scale_x_animator_end_value_tag;
-        }
-
-        @Override
-        public int getAnimatorTag() {
-            return R.id.scale_x_animator_tag;
-        }
-
-        @Override
-        public Property getProperty() {
-            return View.SCALE_X;
-        }
-    };
-
-    private static final AnimatableProperty SCALE_Y_PROPERTY
-            = new AnimatableProperty() {
-
-        @Override
-        public int getAnimationStartTag() {
-            return R.id.scale_y_animator_start_value_tag;
-        }
-
-        @Override
-        public int getAnimationEndTag() {
-            return R.id.scale_y_animator_end_value_tag;
-        }
-
-        @Override
-        public int getAnimatorTag() {
-            return R.id.scale_y_animator_tag;
-        }
-
-        @Override
-        public Property getProperty() {
-            return View.SCALE_Y;
-        }
-    };
-
-    public float alpha;
-    public float xTranslation;
-    public float yTranslation;
-    public float zTranslation;
-    public boolean gone;
-    public boolean hidden;
-    public float scaleX = 1.0f;
-    public float scaleY = 1.0f;
-
-    public void copyFrom(ViewState viewState) {
-        alpha = viewState.alpha;
-        xTranslation = viewState.xTranslation;
-        yTranslation = viewState.yTranslation;
-        zTranslation = viewState.zTranslation;
-        gone = viewState.gone;
-        hidden = viewState.hidden;
-        scaleX = viewState.scaleX;
-        scaleY = viewState.scaleY;
-    }
-
-    public void initFrom(View view) {
-        alpha = view.getAlpha();
-        xTranslation = view.getTranslationX();
-        yTranslation = view.getTranslationY();
-        zTranslation = view.getTranslationZ();
-        gone = view.getVisibility() == View.GONE;
-        hidden = view.getVisibility() == View.INVISIBLE;
-        scaleX = view.getScaleX();
-        scaleY = view.getScaleY();
-    }
-
-    /**
-     * Applies a {@link ViewState} to a normal view.
-     */
-    public void applyToView(View view) {
-        if (this.gone) {
-            // don't do anything with it
-            return;
-        }
-
-        // apply xTranslation
-        boolean animatingX = isAnimating(view, TAG_ANIMATOR_TRANSLATION_X);
-        if (animatingX) {
-            updateAnimationX(view);
-        } else if (view.getTranslationX() != this.xTranslation){
-            view.setTranslationX(this.xTranslation);
-        }
-
-        // apply yTranslation
-        boolean animatingY = isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y);
-        if (animatingY) {
-            updateAnimationY(view);
-        } else if (view.getTranslationY() != this.yTranslation) {
-            view.setTranslationY(this.yTranslation);
-        }
-
-        // apply zTranslation
-        boolean animatingZ = isAnimating(view, TAG_ANIMATOR_TRANSLATION_Z);
-        if (animatingZ) {
-            updateAnimationZ(view);
-        } else if (view.getTranslationZ() != this.zTranslation) {
-            view.setTranslationZ(this.zTranslation);
-        }
-
-        // apply scaleX
-        boolean animatingScaleX = isAnimating(view, SCALE_X_PROPERTY);
-        if (animatingScaleX) {
-            updateAnimation(view, SCALE_X_PROPERTY, scaleX);
-        } else if (view.getScaleX() != scaleX) {
-            view.setScaleX(scaleX);
-        }
-
-        // apply scaleY
-        boolean animatingScaleY = isAnimating(view, SCALE_Y_PROPERTY);
-        if (animatingScaleY) {
-            updateAnimation(view, SCALE_Y_PROPERTY, scaleY);
-        } else if (view.getScaleY() != scaleY) {
-            view.setScaleY(scaleY);
-        }
-
-        int oldVisibility = view.getVisibility();
-        boolean becomesInvisible = this.alpha == 0.0f
-                || (this.hidden && (!isAnimating(view) || oldVisibility != View.VISIBLE));
-        boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA);
-        if (animatingAlpha) {
-            updateAlphaAnimation(view);
-        } else if (view.getAlpha() != this.alpha) {
-            // apply layer type
-            boolean becomesFullyVisible = this.alpha == 1.0f;
-            boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
-                    && view.hasOverlappingRendering();
-            int layerType = view.getLayerType();
-            int newLayerType = newLayerTypeIsHardware
-                    ? View.LAYER_TYPE_HARDWARE
-                    : View.LAYER_TYPE_NONE;
-            if (layerType != newLayerType) {
-                view.setLayerType(newLayerType, null);
-            }
-
-            // apply alpha
-            view.setAlpha(this.alpha);
-        }
-
-        // apply visibility
-        int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
-        if (newVisibility != oldVisibility) {
-            if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) {
-                // We don't want views to change visibility when they are animating to GONE
-                view.setVisibility(newVisibility);
-            }
-        }
-    }
-
-    public boolean isAnimating(View view) {
-        if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_X)) {
-            return true;
-        }
-        if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y)) {
-            return true;
-        }
-        if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Z)) {
-            return true;
-        }
-        if (isAnimating(view, TAG_ANIMATOR_ALPHA)) {
-            return true;
-        }
-        if (isAnimating(view, SCALE_X_PROPERTY)) {
-            return true;
-        }
-        if (isAnimating(view, SCALE_Y_PROPERTY)) {
-            return true;
-        }
-        return false;
-    }
-
-    private static boolean isAnimating(View view, int tag) {
-        return getChildTag(view, tag) != null;
-    }
-
-    public static boolean isAnimating(View view, AnimatableProperty property) {
-        return getChildTag(view, property.getAnimatorTag()) != null;
-    }
-
-    /**
-     * Start an animation to this viewstate
-     * @param child the view to animate
-     * @param animationProperties the properties of the animation
-     */
-    public void animateTo(View child, AnimationProperties animationProperties) {
-        boolean wasVisible = child.getVisibility() == View.VISIBLE;
-        final float alpha = this.alpha;
-        if (!wasVisible && (alpha != 0 || child.getAlpha() != 0)
-                && !this.gone && !this.hidden) {
-            child.setVisibility(View.VISIBLE);
-        }
-        float childAlpha = child.getAlpha();
-        boolean alphaChanging = this.alpha != childAlpha;
-        if (child instanceof ExpandableView) {
-            // We don't want views to change visibility when they are animating to GONE
-            alphaChanging &= !((ExpandableView) child).willBeGone();
-        }
-
-        // start translationX animation
-        if (child.getTranslationX() != this.xTranslation) {
-            startXTranslationAnimation(child, animationProperties);
-        } else {
-            abortAnimation(child, TAG_ANIMATOR_TRANSLATION_X);
-        }
-
-        // start translationY animation
-        if (child.getTranslationY() != this.yTranslation) {
-            startYTranslationAnimation(child, animationProperties);
-        } else {
-            abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Y);
-        }
-
-        // start translationZ animation
-        if (child.getTranslationZ() != this.zTranslation) {
-            startZTranslationAnimation(child, animationProperties);
-        } else {
-            abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Z);
-        }
-
-        // start scaleX animation
-        if (child.getScaleX() != scaleX) {
-            PropertyAnimator.startAnimation(child, SCALE_X_PROPERTY, scaleX, animationProperties);
-        } else {
-            abortAnimation(child, SCALE_X_PROPERTY.getAnimatorTag());
-        }
-
-        // start scaleX animation
-        if (child.getScaleY() != scaleY) {
-            PropertyAnimator.startAnimation(child, SCALE_Y_PROPERTY, scaleY, animationProperties);
-        } else {
-            abortAnimation(child, SCALE_Y_PROPERTY.getAnimatorTag());
-        }
-
-        // start alpha animation
-        if (alphaChanging) {
-            startAlphaAnimation(child, animationProperties);
-        }  else {
-            abortAnimation(child, TAG_ANIMATOR_ALPHA);
-        }
-    }
-
-    private void updateAlphaAnimation(View view) {
-        startAlphaAnimation(view, NO_NEW_ANIMATIONS);
-    }
-
-    private void startAlphaAnimation(final View child, AnimationProperties properties) {
-        Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
-        Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
-        final float newEndValue = this.alpha;
-        if (previousEndValue != null && previousEndValue == newEndValue) {
-            return;
-        }
-        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA);
-        AnimationFilter filter = properties.getAnimationFilter();
-        if (!filter.animateAlpha) {
-            // just a local update was performed
-            if (previousAnimator != null) {
-                // we need to increase all animation keyframes of the previous animator by the
-                // relative change to the end value
-                PropertyValuesHolder[] values = previousAnimator.getValues();
-                float relativeDiff = newEndValue - previousEndValue;
-                float newStartValue = previousStartValue + relativeDiff;
-                values[0].setFloatValues(newStartValue, newEndValue);
-                child.setTag(TAG_START_ALPHA, newStartValue);
-                child.setTag(TAG_END_ALPHA, newEndValue);
-                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
-                return;
-            } else {
-                // no new animation needed, let's just apply the value
-                child.setAlpha(newEndValue);
-                if (newEndValue == 0) {
-                    child.setVisibility(View.INVISIBLE);
-                }
-            }
-        }
-
-        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
-                child.getAlpha(), newEndValue);
-        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        // Handle layer type
-        child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-        animator.addListener(new AnimatorListenerAdapter() {
-            public boolean mWasCancelled;
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                child.setLayerType(View.LAYER_TYPE_NONE, null);
-                if (newEndValue == 0 && !mWasCancelled) {
-                    child.setVisibility(View.INVISIBLE);
-                }
-                // remove the tag when the animation is finished
-                child.setTag(TAG_ANIMATOR_ALPHA, null);
-                child.setTag(TAG_START_ALPHA, null);
-                child.setTag(TAG_END_ALPHA, null);
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mWasCancelled = true;
-            }
-
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mWasCancelled = false;
-            }
-        });
-        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
-        animator.setDuration(newDuration);
-        if (properties.delay > 0 && (previousAnimator == null
-                || previousAnimator.getAnimatedFraction() == 0)) {
-            animator.setStartDelay(properties.delay);
-        }
-        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
-        if (listener != null) {
-            animator.addListener(listener);
-        }
-
-        startAnimator(animator, listener);
-        child.setTag(TAG_ANIMATOR_ALPHA, animator);
-        child.setTag(TAG_START_ALPHA, child.getAlpha());
-        child.setTag(TAG_END_ALPHA, newEndValue);
-    }
-
-    private void updateAnimationZ(View view) {
-        startZTranslationAnimation(view, NO_NEW_ANIMATIONS);
-    }
-
-    private void updateAnimation(View view, AnimatableProperty property,
-            float endValue) {
-        PropertyAnimator.startAnimation(view, property, endValue, NO_NEW_ANIMATIONS);
-    }
-
-    private void startZTranslationAnimation(final View child, AnimationProperties properties) {
-        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
-        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
-        float newEndValue = this.zTranslation;
-        if (previousEndValue != null && previousEndValue == newEndValue) {
-            return;
-        }
-        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z);
-        AnimationFilter filter = properties.getAnimationFilter();
-        if (!filter.animateZ) {
-            // just a local update was performed
-            if (previousAnimator != null) {
-                // we need to increase all animation keyframes of the previous animator by the
-                // relative change to the end value
-                PropertyValuesHolder[] values = previousAnimator.getValues();
-                float relativeDiff = newEndValue - previousEndValue;
-                float newStartValue = previousStartValue + relativeDiff;
-                values[0].setFloatValues(newStartValue, newEndValue);
-                child.setTag(TAG_START_TRANSLATION_Z, newStartValue);
-                child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
-                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
-                return;
-            } else {
-                // no new animation needed, let's just apply the value
-                child.setTranslationZ(newEndValue);
-            }
-        }
-
-        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
-                child.getTranslationZ(), newEndValue);
-        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
-        animator.setDuration(newDuration);
-        if (properties.delay > 0 && (previousAnimator == null
-                || previousAnimator.getAnimatedFraction() == 0)) {
-            animator.setStartDelay(properties.delay);
-        }
-        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
-        if (listener != null) {
-            animator.addListener(listener);
-        }
-        // remove the tag when the animation is finished
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null);
-                child.setTag(TAG_START_TRANSLATION_Z, null);
-                child.setTag(TAG_END_TRANSLATION_Z, null);
-            }
-        });
-        startAnimator(animator, listener);
-        child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
-        child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ());
-        child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
-    }
-
-    private void updateAnimationX(View view) {
-        startXTranslationAnimation(view, NO_NEW_ANIMATIONS);
-    }
-
-    private void startXTranslationAnimation(final View child, AnimationProperties properties) {
-        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_X);
-        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_X);
-        float newEndValue = this.xTranslation;
-        if (previousEndValue != null && previousEndValue == newEndValue) {
-            return;
-        }
-        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_X);
-        AnimationFilter filter = properties.getAnimationFilter();
-        if (!filter.animateX) {
-            // just a local update was performed
-            if (previousAnimator != null) {
-                // we need to increase all animation keyframes of the previous animator by the
-                // relative change to the end value
-                PropertyValuesHolder[] values = previousAnimator.getValues();
-                float relativeDiff = newEndValue - previousEndValue;
-                float newStartValue = previousStartValue + relativeDiff;
-                values[0].setFloatValues(newStartValue, newEndValue);
-                child.setTag(TAG_START_TRANSLATION_X, newStartValue);
-                child.setTag(TAG_END_TRANSLATION_X, newEndValue);
-                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
-                return;
-            } else {
-                // no new animation needed, let's just apply the value
-                child.setTranslationX(newEndValue);
-                return;
-            }
-        }
-
-        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_X,
-                child.getTranslationX(), newEndValue);
-        Interpolator customInterpolator = properties.getCustomInterpolator(child,
-                View.TRANSLATION_X);
-        Interpolator interpolator =  customInterpolator != null ? customInterpolator
-                : Interpolators.FAST_OUT_SLOW_IN;
-        animator.setInterpolator(interpolator);
-        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
-        animator.setDuration(newDuration);
-        if (properties.delay > 0 && (previousAnimator == null
-                || previousAnimator.getAnimatedFraction() == 0)) {
-            animator.setStartDelay(properties.delay);
-        }
-        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
-        if (listener != null) {
-            animator.addListener(listener);
-        }
-        // remove the tag when the animation is finished
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                child.setTag(TAG_ANIMATOR_TRANSLATION_X, null);
-                child.setTag(TAG_START_TRANSLATION_X, null);
-                child.setTag(TAG_END_TRANSLATION_X, null);
-            }
-        });
-        startAnimator(animator, listener);
-        child.setTag(TAG_ANIMATOR_TRANSLATION_X, animator);
-        child.setTag(TAG_START_TRANSLATION_X, child.getTranslationX());
-        child.setTag(TAG_END_TRANSLATION_X, newEndValue);
-    }
-
-    private void updateAnimationY(View view) {
-        startYTranslationAnimation(view, NO_NEW_ANIMATIONS);
-    }
-
-    private void startYTranslationAnimation(final View child, AnimationProperties properties) {
-        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
-        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
-        float newEndValue = this.yTranslation;
-        if (previousEndValue != null && previousEndValue == newEndValue) {
-            return;
-        }
-        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y);
-        AnimationFilter filter = properties.getAnimationFilter();
-        if (!filter.shouldAnimateY(child)) {
-            // just a local update was performed
-            if (previousAnimator != null) {
-                // we need to increase all animation keyframes of the previous animator by the
-                // relative change to the end value
-                PropertyValuesHolder[] values = previousAnimator.getValues();
-                float relativeDiff = newEndValue - previousEndValue;
-                float newStartValue = previousStartValue + relativeDiff;
-                values[0].setFloatValues(newStartValue, newEndValue);
-                child.setTag(TAG_START_TRANSLATION_Y, newStartValue);
-                child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
-                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
-                return;
-            } else {
-                // no new animation needed, let's just apply the value
-                child.setTranslationY(newEndValue);
-                return;
-            }
-        }
-
-        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
-                child.getTranslationY(), newEndValue);
-        Interpolator customInterpolator = properties.getCustomInterpolator(child,
-                View.TRANSLATION_Y);
-        Interpolator interpolator =  customInterpolator != null ? customInterpolator
-                : Interpolators.FAST_OUT_SLOW_IN;
-        animator.setInterpolator(interpolator);
-        long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
-        animator.setDuration(newDuration);
-        if (properties.delay > 0 && (previousAnimator == null
-                || previousAnimator.getAnimatedFraction() == 0)) {
-            animator.setStartDelay(properties.delay);
-        }
-        AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
-        if (listener != null) {
-            animator.addListener(listener);
-        }
-        // remove the tag when the animation is finished
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                HeadsUpUtil.setIsClickedHeadsUpNotification(child, false);
-                child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
-                child.setTag(TAG_START_TRANSLATION_Y, null);
-                child.setTag(TAG_END_TRANSLATION_Y, null);
-                onYTranslationAnimationFinished(child);
-            }
-        });
-        startAnimator(animator, listener);
-        child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
-        child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY());
-        child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
-    }
-
-    protected void onYTranslationAnimationFinished(View view) {
-        if (hidden && !gone) {
-            view.setVisibility(View.INVISIBLE);
-        }
-    }
-
-    public static void startAnimator(Animator animator, AnimatorListenerAdapter listener) {
-        if (listener != null) {
-            // Even if there's a delay we'd want to notify it of the start immediately.
-            listener.onAnimationStart(animator);
-        }
-        animator.start();
-    }
-
-    public static <T> T getChildTag(View child, int tag) {
-        return (T) child.getTag(tag);
-    }
-
-    protected void abortAnimation(View child, int animatorTag) {
-        Animator previousAnimator = getChildTag(child, animatorTag);
-        if (previousAnimator != null) {
-            previousAnimator.cancel();
-        }
-    }
-
-    /**
-     * Cancel the previous animator and get the duration of the new animation.
-     *
-     * @param duration the new duration
-     * @param previousAnimator the animator which was running before
-     * @return the new duration
-     */
-    public static long cancelAnimatorAndGetNewDuration(long duration,
-            ValueAnimator previousAnimator) {
-        long newDuration = duration;
-        if (previousAnimator != null) {
-            // We take either the desired length of the new animation or the remaining time of
-            // the previous animator, whichever is longer.
-            newDuration = Math.max(previousAnimator.getDuration()
-                    - previousAnimator.getCurrentPlayTime(), newDuration);
-            previousAnimator.cancel();
-        }
-        return newDuration;
-    }
-
-    /**
-     * Get the end value of the xTranslation animation running on a view or the xTranslation
-     * if no animation is running.
-     */
-    public static float getFinalTranslationX(View view) {
-        if (view == null) {
-            return 0;
-        }
-        ValueAnimator xAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X);
-        if (xAnimator == null) {
-            return view.getTranslationX();
-        } else {
-            return getChildTag(view, TAG_END_TRANSLATION_X);
-        }
-    }
-
-    /**
-     * Get the end value of the yTranslation animation running on a view or the yTranslation
-     * if no animation is running.
-     */
-    public static float getFinalTranslationY(View view) {
-        if (view == null) {
-            return 0;
-        }
-        ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
-        if (yAnimator == null) {
-            return view.getTranslationY();
-        } else {
-            return getChildTag(view, TAG_END_TRANSLATION_Y);
-        }
-    }
-
-    /**
-     * Get the end value of the zTranslation animation running on a view or the zTranslation
-     * if no animation is running.
-     */
-    public static float getFinalTranslationZ(View view) {
-        if (view == null) {
-            return 0;
-        }
-        ValueAnimator zAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Z);
-        if (zAnimator == null) {
-            return view.getTranslationZ();
-        } else {
-            return getChildTag(view, TAG_END_TRANSLATION_Z);
-        }
-    }
-
-    public static boolean isAnimatingY(View child) {
-        return getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null;
-    }
-
-    public void cancelAnimations(View view) {
-        Animator animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X);
-        if (animator != null) {
-            animator.cancel();
-        }
-        animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
-        if (animator != null) {
-            animator.cancel();
-        }
-        animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Z);
-        if (animator != null) {
-            animator.cancel();
-        }
-        animator = getChildTag(view, TAG_ANIMATOR_ALPHA);
-        if (animator != null) {
-            animator.cancel();
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 526e69b..1d4f9b3 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -15,21 +15,25 @@
  */
 package com.android.systemui.tuner;
 
+import android.app.Activity;
 import android.app.Fragment;
 import android.app.FragmentTransaction;
 import android.os.Bundle;
-import androidx.preference.PreferenceFragment;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
 import android.util.Log;
 import android.view.MenuItem;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Toolbar;
 
-import com.android.settingslib.drawer.SettingsDrawerActivity;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceScreen;
+
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.fragments.FragmentService;
 
-public class TunerActivity extends SettingsDrawerActivity implements
+public class TunerActivity extends Activity implements
         PreferenceFragment.OnPreferenceStartFragmentCallback,
         PreferenceFragment.OnPreferenceStartScreenCallback {
 
@@ -37,6 +41,15 @@
 
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        setContentView(R.layout.tuner_activity);
+        Toolbar toolbar = findViewById(R.id.action_bar);
+        if (toolbar != null) {
+            setActionBar(toolbar);
+        }
+
         Dependency.initDependencies(this);
 
         if (getFragmentManager().findFragmentByTag(TAG_TUNER) == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 1bdb7ad..196d9bc 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -119,7 +119,9 @@
         // 3 Removed because of a revert.
         if (oldVersion < 4) {
             // Delay this so that we can wait for everything to be registered first.
-            new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(() -> clearAll(), 5000);
+            final int user = mCurrentUser;
+            new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(
+                    () -> clearAllFromUser(user), 5000);
         }
         setValue(TUNER_VERSION, newVersion);
     }
@@ -221,6 +223,10 @@
 
     @Override
     public void clearAll() {
+        clearAllFromUser(mCurrentUser);
+    }
+
+    public void clearAllFromUser(int user) {
         // A couple special cases.
         Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null);
         Intent intent = new Intent(DemoMode.ACTION_DEMO);
@@ -231,7 +237,7 @@
             if (ArrayUtils.contains(RESET_BLACKLIST, key)) {
                 continue;
             }
-            Settings.Secure.putString(mContentResolver, key, null);
+            Settings.Secure.putStringForUser(mContentResolver, key, null, user);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 67fa049..c468fef 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -113,7 +113,7 @@
         NotificationChannel screenshotChannel = new NotificationChannel(SCREENSHOTS_HEADSUP,
                 name, NotificationManager.IMPORTANCE_HIGH); // pop on screen
 
-        screenshotChannel.setSound(Uri.parse(""), // silent
+        screenshotChannel.setSound(null, // silent
                 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
         screenshotChannel.setBlockableSystem(true);
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index eca6127..6812410 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -26,11 +26,14 @@
 
     /**
      * Allows lambda iteration over a list. It is done in reverse order so it is safe
-     * to add or remove items during the iteration.
+     * to add or remove items during the iteration.  Skips over null items.
      */
     public static <T> void safeForeach(List<T> list, Consumer<T> c) {
         for (int i = list.size() - 1; i >= 0; i--) {
-            c.accept(list.get(i));
+            T item = list.get(i);
+            if (item != null) {
+                c.accept(item);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
index a901e88..b835909 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
@@ -23,7 +23,7 @@
  */
 public class DelayedWakeLock implements WakeLock {
 
-    private static final long RELEASE_DELAY_MS = 140;
+    private static final long RELEASE_DELAY_MS = 100;
 
     private final Handler mHandler;
     private final WakeLock mInner;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index d7fad67..2cbb78a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -193,11 +193,11 @@
   }
 
   public void show(int reason) {
-    mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget();
+    mHandler.obtainMessage(H.SHOW, reason).sendToTarget();
   }
 
   public void dismiss(int reason) {
-    mHandler.obtainMessage(H.DISMISS, reason, 0).sendToTarget();
+    mHandler.obtainMessage(H.DISMISS, reason).sendToTarget();
   }
 
   private void showH(int reason) {
@@ -223,7 +223,7 @@
     mHandler.removeMessages(H.DISMISS);
     final int timeout = computeTimeoutH();
     mHandler.sendMessageDelayed(mHandler
-        .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
+        .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout);
 
     if (D.BUG) {
       Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
@@ -246,7 +246,6 @@
     }
 
     mListView.animate().cancel();
-    mShowing = false;
 
     mListView.setTranslationY(0);
     mListView.setAlpha(1);
@@ -260,6 +259,7 @@
             Log.d(TAG, "mDialog.dismiss()");
           }
           mDialog.dismiss();
+          mShowing = false;
         }, DISMISS_DELAY_IN_MILLIS))
         .start();
 
@@ -351,11 +351,11 @@
     listItem.setOnSeekBarChangeListener(
         new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager));
     Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
-    primaryIcon.setTint(color);
+    primaryIcon.mutate().setTint(color);
     listItem.setPrimaryActionIcon(primaryIcon);
     if (supplementalIconId != 0) {
       Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
-      supplementalIcon.setTint(color);
+      supplementalIcon.mutate().setTint(color);
       listItem.setSupplementalIcon(supplementalIcon, true,
           supplementalIconOnClickListener);
     } else {
@@ -436,7 +436,8 @@
     public boolean onTouchEvent(MotionEvent event) {
       if (isShowing()) {
         if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
-          dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
+          mHandler.obtainMessage(
+            H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget();
           return true;
         }
       }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index dd55264..2861dff 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -61,7 +61,7 @@
     private final VolumeDialogControllerImpl mController;
     private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
             ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
-            | ActivityInfo.CONFIG_ASSETS_PATHS);
+            | ActivityInfo.CONFIG_ASSETS_PATHS | ActivityInfo.CONFIG_UI_MODE);
     private VolumeDialog mDialog;
     private VolumePolicy mVolumePolicy = new VolumePolicy(
             DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT,  // volumeDownToEnterSilent
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 955939c..02babac 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -40,13 +40,13 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
 import android.graphics.drawable.ColorDrawable;
 import android.media.AudioManager;
 import android.media.AudioSystem;
@@ -63,7 +63,6 @@
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.view.ContextThemeWrapper;
-import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
@@ -92,7 +91,6 @@
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.plugins.VolumeDialogController.State;
 import com.android.systemui.plugins.VolumeDialogController.StreamState;
-import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
@@ -149,6 +147,8 @@
     private State mState;
     private SafetyWarningDialog mSafetyWarning;
     private boolean mHovering = false;
+    private boolean mShowActiveStreamOnly;
+    private boolean mConfigChanged = false;
 
     public VolumeDialogImpl(Context context) {
         mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
@@ -156,6 +156,7 @@
         mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
         mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+        mShowActiveStreamOnly = showActiveStreamOnly();
     }
 
     public void init(int windowType, Callback callback) {
@@ -193,16 +194,16 @@
                 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
         mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
         mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
-        final WindowManager.LayoutParams lp = mWindow.getAttributes();
+        WindowManager.LayoutParams lp = mWindow.getAttributes();
         lp.format = PixelFormat.TRANSLUCENT;
         lp.setTitle(VolumeDialogImpl.class.getSimpleName());
-        lp.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
         lp.windowAnimations = -1;
         mWindow.setAttributes(lp);
         mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
 
-        mDialog.setCanceledOnTouchOutside(true);
         mDialog.setContentView(R.layout.volume_dialog);
+        mDialogView = mDialog.findViewById(R.id.volume_dialog);
+        mDialog.setCanceledOnTouchOutside(true);
         mDialog.setOnShowListener(dialog -> {
             if (!isLandscape()) mDialogView.setTranslationX(mDialogView.getWidth() / 2);
             mDialogView.setAlpha(0);
@@ -213,12 +214,14 @@
                     .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
                     .withEndAction(() -> {
                         if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
-                            mRingerIcon.postOnAnimationDelayed(mSinglePress, 1500);
+                            if (mRingerIcon != null) {
+                                mRingerIcon.postOnAnimationDelayed(mSinglePress, 1500);
+                            }
                         }
                     })
                     .start();
         });
-        mDialogView = mDialog.findViewById(R.id.volume_dialog);
+
         mDialogView.setOnHoverListener((v, event) -> {
             int action = event.getActionMasked();
             mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
@@ -227,6 +230,10 @@
             return true;
         });
 
+        lp = mWindow.getAttributes();
+        lp.gravity = ((FrameLayout.LayoutParams) mDialogView.getLayoutParams()).gravity;
+        mWindow.setAttributes(lp);
+
         mActiveTint = Utils.getColorAccent(mContext);
         mActiveAlpha = Color.alpha(mActiveTint.getDefaultColor());
         mInactiveTint = Utils.getColorAttr(mContext, android.R.attr.colorForeground);
@@ -234,8 +241,10 @@
 
         mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows);
         mRinger = mDialog.findViewById(R.id.ringer);
-        mRingerIcon = mRinger.findViewById(R.id.ringer_icon);
-        mZenIcon = mRinger.findViewById(R.id.dnd_icon);
+        if (mRinger != null) {
+            mRingerIcon = mRinger.findViewById(R.id.ringer_icon);
+            mZenIcon = mRinger.findViewById(R.id.dnd_icon);
+        }
         mSettingsView = mDialog.findViewById(R.id.settings_container);
         mSettingsIcon = mDialog.findViewById(R.id.settings);
 
@@ -420,49 +429,56 @@
     }
 
     public void initSettingsH() {
-        mSettingsView.setVisibility(
-                mDeviceProvisionedController.isCurrentUserSetup() ? VISIBLE : GONE);
-        mSettingsIcon.setOnClickListener(v -> {
-            Events.writeEvent(mContext, Events.EVENT_SETTINGS_CLICK);
-            Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            dismissH(DISMISS_REASON_SETTINGS_CLICKED);
-            Dependency.get(ActivityStarter.class).startActivity(intent, true /* dismissShade */);
-        });
+        if (mSettingsView != null) {
+            mSettingsView.setVisibility(
+                    mDeviceProvisionedController.isCurrentUserSetup() ? VISIBLE : GONE);
+        }
+        if (mSettingsIcon != null) {
+            mSettingsIcon.setOnClickListener(v -> {
+                Events.writeEvent(mContext, Events.EVENT_SETTINGS_CLICK);
+                Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                dismissH(DISMISS_REASON_SETTINGS_CLICKED);
+                Dependency.get(ActivityStarter.class).startActivity(intent,
+                        true /* dismissShade */);
+            });
+        }
     }
 
     public void initRingerH() {
-        mRingerIcon.setOnClickListener(v -> {
-            Prefs.putBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true);
-            final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
-            if (ss == null) {
-                return;
-            }
-            // normal -> vibrate -> silent -> normal (skip vibrate if device doesn't have
-            // a vibrator.
-            int newRingerMode;
-            final boolean hasVibrator = mController.hasVibrator();
-            if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
-                if (hasVibrator) {
-                    newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
-                } else {
+        if (mRingerIcon != null) {
+            mRingerIcon.setOnClickListener(v -> {
+                Prefs.putBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true);
+                final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
+                if (ss == null) {
+                    return;
+                }
+                // normal -> vibrate -> silent -> normal (skip vibrate if device doesn't have
+                // a vibrator.
+                int newRingerMode;
+                final boolean hasVibrator = mController.hasVibrator();
+                if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
+                    if (hasVibrator) {
+                        newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
+                    } else {
+                        newRingerMode = AudioManager.RINGER_MODE_SILENT;
+                    }
+                } else if (mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
                     newRingerMode = AudioManager.RINGER_MODE_SILENT;
+                } else {
+                    newRingerMode = AudioManager.RINGER_MODE_NORMAL;
+                    if (ss.level == 0) {
+                        mController.setStreamVolume(AudioManager.STREAM_RING, 1);
+                    }
                 }
-            } else if (mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
-                newRingerMode = AudioManager.RINGER_MODE_SILENT;
-            } else {
-                newRingerMode = AudioManager.RINGER_MODE_NORMAL;
-                if (ss.level == 0) {
-                    mController.setStreamVolume(AudioManager.STREAM_RING, 1);
-                }
-            }
-            Events.writeEvent(mContext, Events.EVENT_RINGER_TOGGLE, newRingerMode);
-            incrementManualToggleCount();
-            updateRingerH();
-            provideTouchFeedbackH(newRingerMode);
-            mController.setRingerMode(newRingerMode, false);
-            maybeShowToastH(newRingerMode);
-        });
+                Events.writeEvent(mContext, Events.EVENT_RINGER_TOGGLE, newRingerMode);
+                incrementManualToggleCount();
+                updateRingerH();
+                provideTouchFeedbackH(newRingerMode);
+                mController.setRingerMode(newRingerMode, false);
+                maybeShowToastH(newRingerMode);
+            });
+        }
         updateRingerH();
     }
 
@@ -536,6 +552,11 @@
         rescheduleTimeoutH();
         mShowing = true;
 
+        if (mConfigChanged) {
+            initDialog();
+            mConfigurableTexts.update();
+            mConfigChanged = false;
+        }
         initSettingsH();
         mDialog.show();
         Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
@@ -587,29 +608,37 @@
         }
     }
 
+    private boolean showActiveStreamOnly() {
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+                || mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION);
+    }
+
     private boolean shouldBeVisibleH(VolumeRow row, VolumeRow activeRow) {
         boolean isActive = row.stream == activeRow.stream;
-        if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) {
-            return mShowA11yStream;
-        }
-
-        // if the active row is accessibility, then continue to display previous
-        // active row since accessibility is displayed under it
-        if (activeRow.stream == AudioSystem.STREAM_ACCESSIBILITY &&
-                row.stream == mPrevActiveStream) {
-            return true;
-        }
 
         if (isActive) {
             return true;
         }
 
-        if (row.defaultStream) {
-            return activeRow.stream == STREAM_RING
-                    || activeRow.stream == STREAM_ALARM
-                    || activeRow.stream == STREAM_VOICE_CALL
-                    || activeRow.stream == STREAM_ACCESSIBILITY
-                    || mDynamic.get(activeRow.stream);
+        if (!mShowActiveStreamOnly) {
+            if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) {
+                return mShowA11yStream;
+            }
+
+            // if the active row is accessibility, then continue to display previous
+            // active row since accessibility is displayed under it
+            if (activeRow.stream == AudioSystem.STREAM_ACCESSIBILITY &&
+                    row.stream == mPrevActiveStream) {
+                return true;
+            }
+
+            if (row.defaultStream) {
+                return activeRow.stream == STREAM_RING
+                        || activeRow.stream == STREAM_ALARM
+                        || activeRow.stream == STREAM_VOICE_CALL
+                        || activeRow.stream == STREAM_ACCESSIBILITY
+                        || mDynamic.get(activeRow.stream);
+            }
         }
 
         return false;
@@ -721,8 +750,12 @@
      * @param enable whether to enable ringer views and hide dnd icon
      */
     private void enableRingerViewsH(boolean enable) {
-        mRingerIcon.setEnabled(enable);
-        mZenIcon.setVisibility(enable ? GONE : VISIBLE);
+        if (mRingerIcon != null) {
+            mRingerIcon.setEnabled(enable);
+        }
+        if (mZenIcon != null) {
+            mZenIcon.setVisibility(enable ? GONE : VISIBLE);
+        }
     }
 
     private void trimObsoleteH() {
@@ -1029,15 +1062,19 @@
     private Runnable mSinglePress = new Runnable() {
         @Override
         public void run() {
-            mRingerIcon.setPressed(true);
-            mRingerIcon.postOnAnimationDelayed(mSingleUnpress, 200);
+            if (mRingerIcon != null) {
+                mRingerIcon.setPressed(true);
+                mRingerIcon.postOnAnimationDelayed(mSingleUnpress, 200);
+            }
         }
     };
 
     private Runnable mSingleUnpress = new Runnable() {
         @Override
         public void run() {
-            mRingerIcon.setPressed(false);
+            if (mRingerIcon != null) {
+                mRingerIcon.setPressed(false);
+            }
         }
     };
 
@@ -1071,8 +1108,7 @@
         @Override
         public void onConfigurationChanged() {
             mDialog.dismiss();
-            initDialog();
-            mConfigurableTexts.update();
+            mConfigChanged = true;
         }
 
         @Override
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 1be8322..e604877 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -49,6 +49,7 @@
     <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
     <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS" />
 
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index d46a974..2055519 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -16,14 +16,22 @@
 
 package com.android.keyguard;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.truth.Truth.*;
+
 import android.content.Context;
 import android.content.Intent;
+import android.os.Bundle;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
 import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.systemui.SysuiTestCase;
 
@@ -70,6 +78,184 @@
                 keyguardUpdateMonitor.hasSimStateJustChanged());
     }
 
+    @Test
+    public void testTelephonyCapable_BootInitState() {
+        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
+                new TestableKeyguardUpdateMonitor(getContext());
+        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+    }
+
+    @Test
+    public void testTelephonyCapable_SimState_Absent() {
+        Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
+                , IccCardConstants.INTENT_VALUE_ICC_ABSENT);
+        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
+                new TestableKeyguardUpdateMonitor(getContext());
+        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+                , putPhoneInfo(intent,null, false));
+        mTestableLooper.processAllMessages();
+        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isTrue();
+    }
+
+    @Test
+    public void testTelephonyCapable_SimInvalid_ServiceState_InService() {
+        // SERVICE_STATE - IN_SERVICE, but SIM_STATE is invalid TelephonyCapable should be False
+        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
+                new TestableKeyguardUpdateMonitor(getContext());
+        Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        Bundle data = new Bundle();
+        ServiceState state = new ServiceState();
+        state.setState(ServiceState.STATE_IN_SERVICE);
+        state.fillInNotifierBundle(data);
+        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+                , putPhoneInfo(intent, data, false));
+        mTestableLooper.processAllMessages();
+        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+    }
+
+    @Test
+    public void testTelephonyCapable_SimValid_ServiceState_PowerOff() {
+        // Simulate AirplaneMode case, SERVICE_STATE - POWER_OFF, check TelephonyCapable False
+        // Only receive ServiceState callback IN_SERVICE -> OUT_OF_SERVICE -> POWER_OFF
+        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
+                new TestableKeyguardUpdateMonitor(getContext());
+        Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
+                , IccCardConstants.INTENT_VALUE_ICC_LOADED);
+        Bundle data = new Bundle();
+        ServiceState state = new ServiceState();
+        state.setState(ServiceState.STATE_POWER_OFF);
+        state.fillInNotifierBundle(data);
+        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+                , putPhoneInfo(intent, data, true));
+        mTestableLooper.processAllMessages();
+        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isTrue();
+    }
+
+    /* Normal SIM inserted flow
+     * ServiceState:    ---OutOfServie----->PowerOff->OutOfServie--->InService
+     * SimState:        ----NOT_READY---->READY----------------------LOADED>>>
+     * Subscription:    --------null---->null--->"Chunghwa Telecom"-------->>>
+     * System:          -------------------------------BOOT_COMPLETED------>>>
+     * TelephonyCapable:(F)-(F)-(F)-(F)-(F)-(F)-(F)-(F)-(F)-(F)------(T)-(T)>>
+     */
+    @Test
+    public void testTelephonyCapable_BootInitState_ServiceState_OutOfService() {
+        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
+                new TestableKeyguardUpdateMonitor(getContext());
+        Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        Bundle data = new Bundle();
+        ServiceState state = new ServiceState();
+        state.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        state.fillInNotifierBundle(data);
+        intent.putExtras(data);
+        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+                , putPhoneInfo(intent, data, false));
+        mTestableLooper.processAllMessages();
+        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+    }
+
+    @Test
+    public void testTelephonyCapable_BootInitState_SimState_NotReady() {
+        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
+                new TestableKeyguardUpdateMonitor(getContext());
+        Bundle data = new Bundle();
+        ServiceState state = new ServiceState();
+        state.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        state.fillInNotifierBundle(data);
+        Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
+                , IccCardConstants.INTENT_VALUE_ICC_NOT_READY);
+        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+                , putPhoneInfo(intent, data, false));
+        mTestableLooper.processAllMessages();
+        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+    }
+
+    @Test
+    public void testTelephonyCapable_BootInitState_SimState_Ready() {
+        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
+                new TestableKeyguardUpdateMonitor(getContext());
+        Bundle data = new Bundle();
+        ServiceState state = new ServiceState();
+        state.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        state.fillInNotifierBundle(data);
+        Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
+                , IccCardConstants.INTENT_VALUE_ICC_READY);
+        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+                , putPhoneInfo(intent, data, false));
+        mTestableLooper.processAllMessages();
+        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+    }
+
+    @Test
+    public void testTelephonyCapable_BootInitState_ServiceState_PowerOff() {
+        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
+                new TestableKeyguardUpdateMonitor(getContext());
+        Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        Bundle data = new Bundle();
+        ServiceState state = new ServiceState();
+        state.setState(ServiceState.STATE_POWER_OFF);
+        state.fillInNotifierBundle(data);
+        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+                , putPhoneInfo(intent, data, false));
+        mTestableLooper.processAllMessages();
+        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+    }
+
+    @Test
+    public void testTelephonyCapable_SimValid_ServiceState_InService() {
+        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
+                new TestableKeyguardUpdateMonitor(getContext());
+        Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        Bundle data = new Bundle();
+        ServiceState state = new ServiceState();
+        state.setState(ServiceState.STATE_IN_SERVICE);
+        state.fillInNotifierBundle(data);
+        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+                , putPhoneInfo(intent, data, true));
+        mTestableLooper.processAllMessages();
+        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isTrue();
+    }
+
+    @Test
+    public void testTelephonyCapable_SimValid_SimState_Loaded() {
+        TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
+                new TestableKeyguardUpdateMonitor(getContext());
+        Bundle data = new Bundle();
+        ServiceState state = new ServiceState();
+        state.setState(ServiceState.STATE_IN_SERVICE);
+        state.fillInNotifierBundle(data);
+        Intent intentSimState = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        intentSimState.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
+                , IccCardConstants.INTENT_VALUE_ICC_LOADED);
+        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+                , putPhoneInfo(intentSimState, data, true));
+        mTestableLooper.processAllMessages();
+        // Even SimState Loaded, still need ACTION_SERVICE_STATE_CHANGED turn on mTelephonyCapable
+        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+
+        Intent intentServiceState =  new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        intentSimState.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
+                , IccCardConstants.INTENT_VALUE_ICC_LOADED);
+        keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+                , putPhoneInfo(intentServiceState, data, true));
+        mTestableLooper.processAllMessages();
+        assertThat(keyguardUpdateMonitor.mTelephonyCapable).isTrue();
+    }
+
+    private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
+        int subscription = simInited
+                ? 1/* mock subid=1 */ : SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE;
+        if (data != null) intent.putExtras(data);
+        intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
+        intent.putExtra("subscription", subscription);
+        intent.putExtra("slot", 0/* SLOT 1 */);
+        return intent;
+    }
+
     private class TestableKeyguardUpdateMonitor extends KeyguardUpdateMonitor {
         AtomicBoolean mSimStateChanged = new AtomicBoolean(false);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index 08c4235..c180ff8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -25,7 +25,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationTestHelper;
 
 import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index f1bf31d..644c0b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -34,8 +34,10 @@
 
 import android.app.Fragment;
 import android.content.res.Configuration;
+import android.os.Handler;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.Display;
 import android.view.View;
@@ -60,6 +62,7 @@
 @SmallTest
 public class ScreenDecorationsTest extends SysuiTestCase {
 
+    private TestableLooper mTestableLooper;
     private ScreenDecorations mScreenDecorations;
     private StatusBar mStatusBar;
     private WindowManager mWindowManager;
@@ -71,6 +74,10 @@
 
     @Before
     public void setup() {
+        mTestableLooper = TestableLooper.get(this);
+        mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
+                new Handler(mTestableLooper.getLooper()));
+
         mStatusBar = mock(StatusBar.class);
         mWindowManager = mock(WindowManager.class);
         mView = spy(new StatusBarWindowView(mContext, null));
@@ -88,7 +95,31 @@
 
         mTunerService = mDependency.injectMockDependency(TunerService.class);
 
-        mScreenDecorations = new ScreenDecorations();
+
+        mScreenDecorations = new ScreenDecorations() {
+            @Override
+            public void start() {
+                super.start();
+                mTestableLooper.processAllMessages();
+            }
+
+            @Override
+            Handler startHandlerThread() {
+                return new Handler(mTestableLooper.getLooper());
+            }
+
+            @Override
+            protected void onConfigurationChanged(Configuration newConfig) {
+                super.onConfigurationChanged(newConfig);
+                mTestableLooper.processAllMessages();
+            }
+
+            @Override
+            public void onTuningChanged(String key, String newValue) {
+                super.onTuningChanged(key, newValue);
+                mTestableLooper.processAllMessages();
+            }
+        };
         mScreenDecorations.mContext = mContext;
         mScreenDecorations.mComponents = mContext.getComponents();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index 8153953..13dc36d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -93,7 +93,7 @@
         SysuiColorExtractor extractor = getTestableExtractor(colors);
         simulateEvent(extractor);
         extractor.setWallpaperVisible(true);
-        extractor.setMediaBackdropVisible(true);
+        extractor.setHasBackdrop(true);
 
         ColorExtractor.GradientColors fallbackColors = extractor.getFallbackColors();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
index 64507be..bb67d6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.doze;
 
-import static org.junit.Assert.assertTrue;
+import static junit.framework.TestCase.assertEquals;
 
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -42,14 +42,15 @@
     }
 
     @Test
-    public void alwaysOn_onByDefault() throws Exception {
+    public void alwaysOn_followsConfigByDefault() throws Exception {
         if (!mDozeConfig.alwaysOnAvailable()) {
             return;
         }
 
         Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.DOZE_ALWAYS_ON,
                 null);
-
-        assertTrue(mDozeConfig.alwaysOnEnabled(UserHandle.USER_CURRENT));
+        boolean defaultValue = mContext.getResources()
+                .getBoolean(com.android.internal.R.bool.config_dozeAlwaysOnEnabled);
+        assertEquals(mDozeConfig.alwaysOnEnabled(UserHandle.USER_CURRENT), defaultValue);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 5e12781..eaa0dcf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -34,6 +34,8 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -60,6 +62,9 @@
 
     @Before
     public void setUp() throws Exception {
+        Settings.System.putIntForUser(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS, DEFAULT_BRIGHTNESS,
+                UserHandle.USER_CURRENT);
         mServiceFake = new DozeServiceFake();
         mHostFake = new DozeHostFake();
         mSensorManager = new FakeSensorManager(mContext);
@@ -88,6 +93,17 @@
     }
 
     @Test
+    public void testAod_usesLightSensorRespectingUserSetting() throws Exception {
+        int maxBrightness = 3;
+        Settings.System.putIntForUser(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS, maxBrightness,
+                UserHandle.USER_CURRENT);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        assertEquals(maxBrightness, mServiceFake.screenBrightness);
+    }
+
+    @Test
     public void testPausingAod_doesntPauseLightSensor() throws Exception {
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
index cf32760..f8aa28d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
@@ -27,7 +27,7 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.app.IApplicationThread;
 import android.app.ProfilerInfo;
 import android.content.ComponentName;
@@ -62,7 +62,7 @@
 
     private @Mock Context mContext;
     private @Mock ActivityManagerWrapper mActivityManager;
-    private @Mock IActivityManager mIActivityManager;
+    private @Mock IActivityTaskManager mIActivityTaskManager;
 
     private WorkLockActivityController mController;
     private SysUiTaskStackChangeListener mTaskStackListener;
@@ -77,7 +77,8 @@
         // Construct controller. Save the TaskStackListener for injecting events.
         final ArgumentCaptor<SysUiTaskStackChangeListener> listenerCaptor =
                 ArgumentCaptor.forClass(SysUiTaskStackChangeListener.class);
-        mController = new WorkLockActivityController(mContext, mActivityManager, mIActivityManager);
+        mController = new WorkLockActivityController(mContext, mActivityManager,
+                mIActivityTaskManager);
 
         verify(mActivityManager).registerTaskStackListener(listenerCaptor.capture());
         mTaskStackListener = listenerCaptor.getValue();
@@ -93,7 +94,7 @@
 
         // The overlay should start and the task the activity started in should not be removed.
         verifyStartActivity(TASK_ID, true /*taskOverlay*/);
-        verify(mIActivityManager, never()).removeTask(anyInt() /*taskId*/);
+        verify(mIActivityTaskManager, never()).removeTask(anyInt() /*taskId*/);
     }
 
     @Test
@@ -107,14 +108,14 @@
         // The task the activity started in should be removed to prevent the locked task from
         // being shown.
         verifyStartActivity(TASK_ID, true /*taskOverlay*/);
-        verify(mIActivityManager).removeTask(TASK_ID);
+        verify(mIActivityTaskManager).removeTask(TASK_ID);
     }
 
     // End of tests, start of helpers
     // ------------------------------
 
     private void setActivityStartCode(int taskId, boolean taskOverlay, int code) throws Exception {
-        doReturn(code).when(mIActivityManager).startActivityAsUser(
+        doReturn(code).when(mIActivityTaskManager).startActivityAsUser(
                 eq((IApplicationThread) null),
                 eq((String) null),
                 any(Intent.class),
@@ -129,7 +130,7 @@
     }
 
     private void verifyStartActivity(int taskId, boolean taskOverlay) throws Exception {
-        verify(mIActivityManager).startActivityAsUser(
+        verify(mIActivityTaskManager).startActivityAsUser(
                 eq((IApplicationThread) null),
                 eq((String) null),
                 any(Intent.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index d19715d..a9d49f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -24,6 +24,7 @@
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -66,6 +67,8 @@
     public static final long BELOW_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(2);
     public static final long ABOVE_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(4);
     private static final long ABOVE_CHARGE_CYCLE_THRESHOLD = Duration.ofHours(8).toMillis();
+    private static final int OLD_BATTERY_LEVEL_NINE = 9;
+    private static final int OLD_BATTERY_LEVEL_10 = 10;
     private HardwarePropertiesManager mHardProps;
     private WarningsUI mMockWarnings;
     private PowerUI mPowerUI;
@@ -307,8 +310,8 @@
                 .thenReturn(new Estimate(BELOW_HYBRID_THRESHOLD, true));
         mPowerUI.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
 
-        mPowerUI.maybeShowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
-                ABOVE_WARNING_BUCKET);
+        mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_NINE, UNPLUGGED, UNPLUGGED,
+                ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
 
         // reduce battery level to handle time based trigger -> level trigger interactions
         mPowerUI.mBatteryLevel = 10;
@@ -320,9 +323,9 @@
     }
 
     @Test
-    public void testShouldDismissLowBatteryWarning_dismissWhenPowerSaverEnabled() {
+    public void testShouldDismissLowBatteryWarning_dismissWhenPowerSaverEnabledLegacy() {
         mPowerUI.start();
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(false);
         when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
         when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
 
@@ -334,6 +337,20 @@
     }
 
     @Test
+    public void testShouldNotDismissLowBatteryWarning_dismissWhenPowerSaverEnabledHybrid() {
+        mPowerUI.start();
+        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
+        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
+
+        // device that gets power saver turned on should dismiss
+        boolean shouldDismiss =
+            mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
+                BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, !POWER_SAVER_OFF);
+        assertFalse(shouldDismiss);
+    }
+
+    @Test
     public void testShouldDismissLowBatteryWarning_dismissWhenPlugged() {
         mPowerUI.start();
         when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
@@ -449,6 +466,33 @@
         verify(mMockWarnings, never()).dismissLowBatteryWarning();
     }
 
+    @Test
+    public void testMaybeShowBatteryWarning_onlyQueriesEstimateOnBatteryLevelChangeOrNull() {
+        mPowerUI.start();
+        Estimate estimate = new Estimate(BELOW_HYBRID_THRESHOLD, true);
+        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
+        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
+        when(mEnhancedEstimates.getEstimate()).thenReturn(estimate);
+        mPowerUI.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
+
+        // we expect that the first time it will query even if the level is the same
+        mPowerUI.mBatteryLevel = 9;
+        mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_NINE, UNPLUGGED, UNPLUGGED,
+                ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
+        verify(mEnhancedEstimates, times(1)).getEstimate();
+
+        // We should NOT query again if the battery level hasn't changed
+        mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_NINE, UNPLUGGED, UNPLUGGED,
+                ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
+        verify(mEnhancedEstimates, times(1)).getEstimate();
+
+        // Battery level has changed, so we should query again
+        mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_10, UNPLUGGED, UNPLUGGED,
+                ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
+        verify(mEnhancedEstimates, times(2)).getEstimate();
+    }
+
     private void setCurrentTemp(float temp) {
         when(mHardProps.getDeviceTemperatures(DEVICE_TEMPERATURE_SKIN, TEMPERATURE_CURRENT))
                 .thenReturn(new float[] { temp });
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java
index 703b4d5..8503962 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java
@@ -41,7 +41,6 @@
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 @SmallTest
-@Ignore("failing")
 public class QSFooterImplTest extends LeakCheckedTest {
 
     private QSFooterImpl mFooter;
@@ -60,6 +59,7 @@
     }
 
     @Test
+    @Ignore("failing")
     public void testSettings_UserNotSetup() {
         View settingsButton = mFooter.findViewById(id.settings_button);
         when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
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 8cece92..4e24354 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -52,7 +52,7 @@
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
-@Ignore("failing")
+@Ignore
 public class QSFragmentTest extends SysuiBaseFragmentTest {
 
     private MetricsLogger mMockMetricsLogger;
@@ -62,6 +62,7 @@
     }
 
     @Before
+    @Ignore("failing")
     public void addLeakCheckDependencies() {
         mMockMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
         mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
@@ -80,6 +81,7 @@
     }
 
     @Test
+    @Ignore("failing")
     public void testListening() {
         assertEquals(Looper.myLooper(), Looper.getMainLooper());
         QSFragment qs = (QSFragment) mFragment;
@@ -103,6 +105,7 @@
     }
 
     @Test
+    @Ignore("failing")
     public void testSaveState() {
         QSFragment qs = (QSFragment) mFragment;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
index c3defa4..f89a932 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
@@ -42,7 +42,7 @@
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
-@Ignore("Flaky")
+@Ignore
 public class CarQsFragmentTest extends SysuiBaseFragmentTest {
     public CarQsFragmentTest() {
         super(CarQSFragment.class);
@@ -64,6 +64,7 @@
     }
 
     @Test
+    @Ignore("Flaky")
     public void testLayoutInflation() {
         CarQSFragment fragment = (CarQSFragment) mFragment;
         mFragments.dispatchResume();
@@ -73,6 +74,7 @@
     }
 
     @Test
+    @Ignore("Flaky")
     public void testListening() {
         CarQSFragment qs = (CarQSFragment) mFragment;
         mFragments.dispatchResume();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java
index bdbd244..2160f9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java
@@ -23,9 +23,9 @@
 
 import static org.junit.Assert.fail;
 
-import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.IActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
 import android.os.SystemClock;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -44,8 +44,8 @@
     @Test
     public void testRecentsActivityType() throws Exception {
         // Clear the state
-        final IActivityManager am = ActivityManager.getService();
-        am.removeStacksWithActivityTypes(new int[] { ACTIVITY_TYPE_RECENTS });
+        final IActivityTaskManager atm = ActivityTaskManager.getService();
+        atm.removeStacksWithActivityTypes(new int[] { ACTIVITY_TYPE_RECENTS });
 
         // Toggle recents, use a shell command because it is not exported
         runShellCommand("am start -n " + RECENTS_PACKAGE + "/" + RECENTS_ACTIVITY);
@@ -53,7 +53,7 @@
         // Verify that an activity was launched with the right activity type
         int retryCount = 0;
         while (retryCount < 10) {
-            List<RunningTaskInfo> tasks = am.getTasks(Integer.MAX_VALUE);
+            List<RunningTaskInfo> tasks = atm.getTasks(Integer.MAX_VALUE);
             for (RunningTaskInfo info : tasks) {
                 if (info.configuration.windowConfiguration.getActivityType()
                         == ACTIVITY_TYPE_RECENTS) {
@@ -66,4 +66,4 @@
         }
         fail("Expected Recents activity with ACTIVITY_TYPE_RECENTS");
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsInfoTest.java
deleted file mode 100644
index 660d2dc..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsInfoTest.java
+++ /dev/null
@@ -1,220 +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.app.AppOpsManager.OP_CAMERA;
-import static android.app.AppOpsManager.OP_RECORD_AUDIO;
-import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.UiThreadTest;
-import android.util.ArraySet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.CountDownLatch;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@UiThreadTest
-public class AppOpsInfoTest extends SysuiTestCase {
-    private static final String TEST_PACKAGE_NAME = "test_package";
-    private static final int TEST_UID = 1;
-
-    private AppOpsInfo mAppOpsInfo;
-    private final PackageManager mMockPackageManager = mock(PackageManager.class);
-    private final NotificationGuts mGutsParent = mock(NotificationGuts.class);
-    private StatusBarNotification mSbn;
-
-    @Before
-    public void setUp() throws Exception {
-        // Inflate the layout
-        final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
-        mAppOpsInfo = (AppOpsInfo) layoutInflater.inflate(R.layout.app_ops_info, null);
-        mAppOpsInfo.setGutsParent(mGutsParent);
-
-        // PackageManager must return a packageInfo and applicationInfo.
-        final PackageInfo packageInfo = new PackageInfo();
-        packageInfo.packageName = TEST_PACKAGE_NAME;
-        when(mMockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt()))
-                .thenReturn(packageInfo);
-        final ApplicationInfo applicationInfo = new ApplicationInfo();
-        applicationInfo.uid = TEST_UID;  // non-zero
-        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
-                applicationInfo);
-
-        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
-                new Notification(), UserHandle.CURRENT, null, 0);
-    }
-
-    @Test
-    public void testBindNotification_SetsTextApplicationName() {
-        when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
-        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, new ArraySet<>());
-        final TextView textView = mAppOpsInfo.findViewById(R.id.pkgname);
-        assertTrue(textView.getText().toString().contains("App Name"));
-    }
-
-    @Test
-    public void testBindNotification_SetsPackageIcon() {
-        final Drawable iconDrawable = mock(Drawable.class);
-        when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
-                .thenReturn(iconDrawable);
-        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, new ArraySet<>());
-        final ImageView iconView = mAppOpsInfo.findViewById(R.id.pkgicon);
-        assertEquals(iconDrawable, iconView.getDrawable());
-    }
-
-    @Test
-    public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
-        ArraySet<Integer> expectedOps = new ArraySet<>();
-        expectedOps.add(OP_CAMERA);
-        final CountDownLatch latch = new CountDownLatch(1);
-        mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid,
-                ArraySet<Integer> ops) -> {
-            assertEquals(TEST_PACKAGE_NAME, pkg);
-            assertEquals(expectedOps, ops);
-            assertEquals(TEST_UID, uid);
-            latch.countDown();
-        }, mSbn, expectedOps);
-
-        final View settingsButton = mAppOpsInfo.findViewById(R.id.settings);
-        settingsButton.performClick();
-        // Verify that listener was triggered.
-        assertEquals(0, latch.getCount());
-    }
-
-    @Test
-    public void testOk() {
-        ArraySet<Integer> expectedOps = new ArraySet<>();
-        expectedOps.add(OP_CAMERA);
-        final CountDownLatch latch = new CountDownLatch(1);
-        mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid,
-                ArraySet<Integer> ops) -> {
-            assertEquals(TEST_PACKAGE_NAME, pkg);
-            assertEquals(expectedOps, ops);
-            assertEquals(TEST_UID, uid);
-            latch.countDown();
-        }, mSbn, expectedOps);
-
-        final View okButton = mAppOpsInfo.findViewById(R.id.ok);
-        okButton.performClick();
-        assertEquals(1, latch.getCount());
-        verify(mGutsParent, times(1)).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
-    }
-
-    @Test
-    public void testPrompt_camera() {
-        ArraySet<Integer> expectedOps = new ArraySet<>();
-        expectedOps.add(OP_CAMERA);
-        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
-        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
-        assertEquals("This app is using the camera.", prompt.getText());
-    }
-
-    @Test
-    public void testPrompt_mic() {
-        ArraySet<Integer> expectedOps = new ArraySet<>();
-        expectedOps.add(OP_RECORD_AUDIO);
-        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
-        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
-        assertEquals("This app is using the microphone.", prompt.getText());
-    }
-
-    @Test
-    public void testPrompt_overlay() {
-        ArraySet<Integer> expectedOps = new ArraySet<>();
-        expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
-        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
-        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
-        assertEquals("This app is displaying over other apps on your screen.", prompt.getText());
-    }
-
-    @Test
-    public void testPrompt_camera_mic() {
-        ArraySet<Integer> expectedOps = new ArraySet<>();
-        expectedOps.add(OP_CAMERA);
-        expectedOps.add(OP_RECORD_AUDIO);
-        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
-        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
-        assertEquals("This app is using the microphone and camera.", prompt.getText());
-    }
-
-    @Test
-    public void testPrompt_camera_mic_overlay() {
-        ArraySet<Integer> expectedOps = new ArraySet<>();
-        expectedOps.add(OP_CAMERA);
-        expectedOps.add(OP_RECORD_AUDIO);
-        expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
-        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
-        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
-        assertEquals("This app is displaying over other apps on your screen and using"
-                + " the microphone and camera.", prompt.getText());
-    }
-
-    @Test
-    public void testPrompt_camera_overlay() {
-        ArraySet<Integer> expectedOps = new ArraySet<>();
-        expectedOps.add(OP_CAMERA);
-        expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
-        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
-        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
-        assertEquals("This app is displaying over other apps on your screen and using"
-                + " the camera.", prompt.getText());
-    }
-
-    @Test
-    public void testPrompt_mic_overlay() {
-        ArraySet<Integer> expectedOps = new ArraySet<>();
-        expectedOps.add(OP_RECORD_AUDIO);
-        expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
-        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
-        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
-        assertEquals("This app is displaying over other apps on your screen and using"
-                + " the microphone.", prompt.getText());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java
deleted file mode 100644
index 0feaa5a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java
+++ /dev/null
@@ -1,98 +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 org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.AppOpsManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.SysuiTestCase;
-
-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)
-@TestableLooper.RunWithLooper
-public class AppOpsListenerTest extends SysuiTestCase {
-    private static final String TEST_PACKAGE_NAME = "test";
-    private static final int TEST_UID = 0;
-
-    @Mock private NotificationPresenter mPresenter;
-    @Mock private AppOpsManager mAppOpsManager;
-
-    // Dependency mocks:
-    @Mock private NotificationEntryManager mEntryManager;
-    @Mock private ForegroundServiceController mFsc;
-
-    private AppOpsListener mListener;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
-        mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
-        getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
-        when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper()));
-
-        mListener = new AppOpsListener(mContext);
-    }
-
-    @Test
-    public void testOnlyListenForFewOps() {
-        mListener.setUpWithPresenter(mPresenter, mEntryManager);
-
-        verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsListener.OPS, mListener);
-    }
-
-    @Test
-    public void testStopListening() {
-        mListener.destroy();
-        verify(mAppOpsManager, times(1)).stopWatchingActive(mListener);
-    }
-
-    @Test
-    public void testInformEntryMgrOnAppOpsChange() {
-        mListener.setUpWithPresenter(mPresenter, mEntryManager);
-        mListener.onOpActiveChanged(
-                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
-        TestableLooper.get(this).processAllMessages();
-        verify(mEntryManager, times(1)).updateNotificationsForAppOp(
-                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
-    }
-
-    @Test
-    public void testInformFscOnAppOpsChange() {
-        mListener.setUpWithPresenter(mPresenter, mEntryManager);
-        mListener.onOpActiveChanged(
-                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
-        TestableLooper.get(this).processAllMessages();
-        verify(mFsc, times(1)).onAppOpChanged(
-                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
deleted file mode 100644
index ff12c53..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ /dev/null
@@ -1,311 +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;
-
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-
-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.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.AppOpsManager;
-import android.app.NotificationChannel;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.util.ArraySet;
-import android.view.NotificationHeaderView;
-import android.view.View;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
-import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
-public class ExpandableNotificationRowTest extends SysuiTestCase {
-
-    private ExpandableNotificationRow mGroupRow;
-
-    private NotificationTestHelper mNotificationTestHelper;
-    boolean mHeadsUpAnimatingAway = false;
-
-    @Rule public MockitoRule mockito = MockitoJUnit.rule();
-    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
-
-    @Before
-    public void setUp() throws Exception {
-        mNotificationTestHelper = new NotificationTestHelper(mContext);
-        mGroupRow = mNotificationTestHelper.createGroup();
-        mGroupRow.setHeadsUpAnimatingAwayListener(
-                animatingAway -> mHeadsUpAnimatingAway = animatingAway);
-        mDependency.injectTestDependency(
-                NotificationBlockingHelperManager.class,
-                mBlockingHelperManager);
-    }
-
-    @Test
-    public void testGroupSummaryNotShowingIconWhenPublic() {
-        mGroupRow.setSensitive(true, true);
-        mGroupRow.setHideSensitiveForIntrinsicHeight(true);
-        assertTrue(mGroupRow.isSummaryWithChildren());
-        assertFalse(mGroupRow.isShowingIcon());
-    }
-
-    @Test
-    public void testNotificationHeaderVisibleWhenAnimating() {
-        mGroupRow.setSensitive(true, true);
-        mGroupRow.setHideSensitive(true, false, 0, 0);
-        mGroupRow.setHideSensitive(false, true, 0, 0);
-        assertTrue(mGroupRow.getChildrenContainer().getVisibleHeader().getVisibility()
-                == View.VISIBLE);
-    }
-
-    @Test
-    public void testUserLockedResetEvenWhenNoChildren() {
-        mGroupRow.setUserLocked(true);
-        mGroupRow.removeAllChildren();
-        mGroupRow.setUserLocked(false);
-        assertFalse("The childrencontainer should not be userlocked but is, the state "
-                + "seems out of sync.", mGroupRow.getChildrenContainer().isUserLocked());
-    }
-
-    @Test
-    public void testReinflatedOnDensityChange() {
-        mGroupRow.setUserLocked(true);
-        mGroupRow.removeAllChildren();
-        mGroupRow.setUserLocked(false);
-        NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
-        mGroupRow.setChildrenContainer(mockContainer);
-        mGroupRow.onDensityOrFontScaleChanged();
-        verify(mockContainer).reInflateViews(any(), any());
-    }
-
-    @Test
-    public void testIconColorShouldBeUpdatedWhenSensitive() throws Exception {
-        ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow());
-        row.setSensitive(true, true);
-        row.setHideSensitive(true, false, 0, 0);
-        verify(row).updateShelfIconColor();
-    }
-
-    @Test
-    public void testIconColorShouldBeUpdatedWhenSettingDark() throws Exception {
-        ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow());
-        row.setDark(true, false, 0);
-        verify(row).updateShelfIconColor();
-    }
-
-    @Test
-    public void testAboveShelfChangedListenerCalled() throws Exception {
-        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
-        row.setAboveShelfChangedListener(listener);
-        row.setHeadsUp(true);
-        verify(listener).onAboveShelfStateChanged(true);
-    }
-
-    @Test
-    public void testAboveShelfChangedListenerCalledPinned() throws Exception {
-        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
-        row.setAboveShelfChangedListener(listener);
-        row.setPinned(true);
-        verify(listener).onAboveShelfStateChanged(true);
-    }
-
-    @Test
-    public void testAboveShelfChangedListenerCalledHeadsUpGoingAway() throws Exception {
-        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
-        row.setAboveShelfChangedListener(listener);
-        row.setHeadsUpAnimatingAway(true);
-        verify(listener).onAboveShelfStateChanged(true);
-    }
-    @Test
-    public void testAboveShelfChangedListenerCalledWhenGoingBelow() throws Exception {
-        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setHeadsUp(true);
-        AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
-        row.setAboveShelfChangedListener(listener);
-        row.setAboveShelf(false);
-        verify(listener).onAboveShelfStateChanged(false);
-    }
-
-    @Test
-    public void testClickSound() throws Exception {
-        assertTrue("Should play sounds by default.", mGroupRow.isSoundEffectsEnabled());
-        mGroupRow.setDark(true /* dark */, false /* fade */, 0 /* delay */);
-        mGroupRow.setSecureStateProvider(()-> false);
-        assertFalse("Shouldn't play sounds when dark and trusted.",
-                mGroupRow.isSoundEffectsEnabled());
-        mGroupRow.setSecureStateProvider(()-> true);
-        assertTrue("Should always play sounds when not trusted.",
-                mGroupRow.isSoundEffectsEnabled());
-    }
-
-    @Test
-    public void testSetDismissed_longPressListenerRemoved() {
-        ExpandableNotificationRow.LongPressListener listener =
-                mock(ExpandableNotificationRow.LongPressListener.class);
-        mGroupRow.setLongPressListener(listener);
-        mGroupRow.doLongClickCallback(0,0);
-        verify(listener, times(1)).onLongPress(eq(mGroupRow), eq(0), eq(0),
-                any(NotificationMenuRowPlugin.MenuItem.class));
-        reset(listener);
-
-        mGroupRow.setDismissed(true);
-        mGroupRow.doLongClickCallback(0,0);
-        verify(listener, times(0)).onLongPress(eq(mGroupRow), eq(0), eq(0),
-                any(NotificationMenuRowPlugin.MenuItem.class));
-    }
-
-    @Test
-    public void testShowAppOps_noHeader() {
-        // public notification is custom layout - no header
-        mGroupRow.setSensitive(true, true);
-        mGroupRow.setAppOpsOnClickListener(null);
-        mGroupRow.showAppOpsIcons(null);
-    }
-
-    @Test
-    public void testShowAppOpsIcons_header() {
-        NotificationHeaderView mockHeader = mock(NotificationHeaderView.class);
-
-        NotificationContentView publicLayout = mock(NotificationContentView.class);
-        mGroupRow.setPublicLayout(publicLayout);
-        NotificationContentView privateLayout = mock(NotificationContentView.class);
-        mGroupRow.setPrivateLayout(privateLayout);
-        NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
-        when(mockContainer.getNotificationChildCount()).thenReturn(1);
-        when(mockContainer.getHeaderView()).thenReturn(mockHeader);
-        mGroupRow.setChildrenContainer(mockContainer);
-
-        ArraySet<Integer> ops = new ArraySet<>();
-        ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
-        mGroupRow.showAppOpsIcons(ops);
-
-        verify(mockHeader, times(1)).showAppOpsIcons(ops);
-        verify(privateLayout, times(1)).showAppOpsIcons(ops);
-        verify(publicLayout, times(1)).showAppOpsIcons(ops);
-
-    }
-
-    @Test
-    public void testAppOpsOnClick() {
-        ExpandableNotificationRow.OnAppOpsClickListener l = mock(
-                ExpandableNotificationRow.OnAppOpsClickListener.class);
-        View view = mock(View.class);
-
-        mGroupRow.setAppOpsOnClickListener(l);
-
-        mGroupRow.getAppOpsOnClickListener().onClick(view);
-        verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
-    }
-
-    @Test
-    public void testHeadsUpAnimatingAwayListener() {
-        mGroupRow.setHeadsUpAnimatingAway(true);
-        Assert.assertEquals(true, mHeadsUpAnimatingAway);
-        mGroupRow.setHeadsUpAnimatingAway(false);
-        Assert.assertEquals(false, mHeadsUpAnimatingAway);
-    }
-
-    @Test
-    public void testPerformDismissWithBlockingHelper_falseWhenBlockingHelperIsntShown() {
-        when(mBlockingHelperManager.perhapsShowBlockingHelper(
-                eq(mGroupRow), any(NotificationMenuRowPlugin.class))).thenReturn(false);
-
-        assertFalse(
-                mGroupRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
-    }
-
-    @Test
-    public void testPerformDismissWithBlockingHelper_doesntPerformOnGroupSummary() {
-        ExpandableNotificationRow childRow = mGroupRow.getChildrenContainer().getViewAtPosition(0);
-        when(mBlockingHelperManager.perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class)))
-                .thenReturn(true);
-
-        assertTrue(
-                childRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
-
-        verify(mBlockingHelperManager, times(1))
-                .perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class));
-        verify(mBlockingHelperManager, times(0))
-                .perhapsShowBlockingHelper(eq(mGroupRow), any(NotificationMenuRowPlugin.class));
-    }
-
-    @Test
-    public void testIsBlockingHelperShowing_isCorrectlyUpdated() {
-        mGroupRow.setBlockingHelperShowing(true);
-        assertTrue(mGroupRow.isBlockingHelperShowing());
-
-        mGroupRow.setBlockingHelperShowing(false);
-        assertFalse(mGroupRow.isBlockingHelperShowing());
-    }
-
-    @Test
-    public void testGetNumUniqueChildren_defaultChannel() {
-        assertEquals(1, mGroupRow.getNumUniqueChannels());
-    }
-
-    @Test
-    public void testGetNumUniqueChildren_multiChannel() {
-        List<ExpandableNotificationRow> childRows =
-                mGroupRow.getChildrenContainer().getNotificationChildren();
-        // Give each child a unique channel id/name.
-        int i = 0;
-        for (ExpandableNotificationRow childRow : childRows) {
-            childRow.getEntry().channel =
-                    new NotificationChannel("id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT);
-            i++;
-        }
-
-        assertEquals(3, mGroupRow.getNumUniqueChannels());
-    }
-
-    @Test
-    public void testIconScrollXAfterTranslationAndReset() throws Exception {
-        mGroupRow.setTranslation(50);
-        assertEquals(50, -mGroupRow.getEntry().expandedIcon.getScrollX());
-
-        mGroupRow.resetTranslation();
-        assertEquals(0, mGroupRow.getEntry().expandedIcon.getScrollX());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java
deleted file mode 100644
index e6fdfa4..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java
+++ /dev/null
@@ -1,90 +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 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;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class FooterViewTest extends SysuiTestCase {
-
-    FooterView mView;
-
-    @Before
-    public void setUp() {
-        mView = (FooterView) LayoutInflater.from(mContext).inflate(
-                R.layout.status_bar_notification_footer, null, false);
-        mView.setDuration(0);
-    }
-
-    @Test
-    public void testViewsNotNull() {
-        assertNotNull(mView.findContentView());
-        assertNotNull(mView.findSecondaryView());
-    }
-
-    @Test
-    public void setDismissOnClick() {
-        mView.setDismissButtonClickListener(mock(View.OnClickListener.class));
-        assertTrue(mView.findSecondaryView().hasOnClickListeners());
-    }
-
-    @Test
-    public void setManageOnClick() {
-        mView.setManageButtonClickListener(mock(View.OnClickListener.class));
-        assertTrue(mView.findViewById(R.id.manage_text).hasOnClickListeners());
-    }
-
-    @Test
-    public void testPerformVisibilityAnimation() {
-        mView.setVisible(false /* visible */, false /* animate */);
-        assertFalse(mView.isVisible());
-
-        mView.setVisible(true /* visible */, true /* animate */);
-    }
-
-    @Test
-    public void testPerformSecondaryVisibilityAnimation() {
-        mView.setSecondaryVisible(false /* visible */, false /* animate */);
-        assertFalse(mView.isSecondaryVisible());
-
-        mView.setSecondaryVisible(true /* visible */, true /* animate */);
-    }
-}
-
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 2af0c3e..9121473 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -27,6 +27,11 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationInfo;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBarWindowManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java
deleted file mode 100644
index 4366032..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java
+++ /dev/null
@@ -1,253 +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 com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-
-import android.content.Context;
-import android.support.test.filters.FlakyTest;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.View;
-
-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 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.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-/**
- * Tests for {@link NotificationBlockingHelperManager}.
- */
-@SmallTest
-@FlakyTest
-@org.junit.runner.RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
-
-    private NotificationBlockingHelperManager mBlockingHelperManager;
-
-    private NotificationTestHelper mHelper;
-
-    @Mock private NotificationGutsManager mGutsManager;
-    @Mock private NotificationEntryManager mEntryManager;
-    @Mock private NotificationMenuRow mMenuRow;
-    @Mock private NotificationMenuRowPlugin.MenuItem mMenuItem;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        when(mGutsManager.openGuts(
-                any(View.class),
-                anyInt(),
-                anyInt(),
-                any(NotificationMenuRowPlugin.MenuItem.class)))
-                .thenReturn(true);
-        when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
-        mDependency.injectTestDependency(NotificationGutsManager.class, mGutsManager);
-        mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
-
-        mHelper = new NotificationTestHelper(mContext);
-
-        mBlockingHelperManager = new NotificationBlockingHelperManager(mContext);
-        // By default, have the shade visible/expanded.
-        mBlockingHelperManager.setNotificationShadeExpanded(1f);
-    }
-
-    @Test
-    public void testDismissCurrentBlockingHelper_nullBlockingHelperRow() {
-        // By default, this shouldn't dismiss (no pointers/vars set up!)
-        assertFalse(mBlockingHelperManager.dismissCurrentBlockingHelper());
-        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
-    }
-
-    @Test
-    public void testDismissCurrentBlockingHelper_withDetachedBlockingHelperRow() throws Exception {
-        ExpandableNotificationRow row = createBlockableRowSpy();
-        row.setBlockingHelperShowing(true);
-        when(row.isAttachedToWindow()).thenReturn(false);
-        mBlockingHelperManager.setBlockingHelperRowForTest(row);
-
-        assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
-        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
-
-        verify(mEntryManager, times(0)).updateNotifications();
-    }
-
-    @Test
-    public void testDismissCurrentBlockingHelper_withAttachedBlockingHelperRow() throws Exception {
-        ExpandableNotificationRow row = createBlockableRowSpy();
-        row.setBlockingHelperShowing(true);
-        when(row.isAttachedToWindow()).thenReturn(true);
-        mBlockingHelperManager.setBlockingHelperRowForTest(row);
-
-        assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
-        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
-
-        verify(mEntryManager).updateNotifications();
-    }
-
-    @Test
-    public void testPerhapsShowBlockingHelper_shown() throws Exception {
-        ExpandableNotificationRow row = createBlockableRowSpy();
-        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
-
-        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
-
-        verify(mGutsManager).openGuts(row, 0, 0, mMenuItem);
-    }
-
-
-    @Test
-    public void testPerhapsShowBlockingHelper_shownForLargeGroup() throws Exception {
-        ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(10);
-        groupRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
-
-        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(groupRow, mMenuRow));
-
-        verify(mGutsManager).openGuts(groupRow, 0, 0, mMenuItem);
-    }
-
-    @Test
-    public void testPerhapsShowBlockingHelper_shownForOnlyChildNotification()
-            throws Exception {
-        ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(1);
-        // Explicitly get the children container & call getViewAtPosition on it instead of the row
-        // as other factors such as view expansion may cause us to get the parent row back instead
-        // of the child row.
-        ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
-        childRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
-        assertFalse(childRow.getIsNonblockable());
-
-        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
-
-        verify(mGutsManager).openGuts(childRow, 0, 0, mMenuItem);
-    }
-
-    @Test
-    public void testPerhapsShowBlockingHelper_notShownDueToNeutralUserSentiment() throws Exception {
-        ExpandableNotificationRow row = createBlockableRowSpy();
-        row.getEntry().userSentiment = USER_SENTIMENT_NEUTRAL;
-
-        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
-    }
-
-    @Test
-    public void testPerhapsShowBlockingHelper_notShownDueToPositiveUserSentiment()
-            throws Exception {
-        ExpandableNotificationRow row = createBlockableRowSpy();
-        row.getEntry().userSentiment = USER_SENTIMENT_POSITIVE;
-
-        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
-    }
-
-    @Test
-    public void testPerhapsShowBlockingHelper_notShownDueToShadeVisibility() throws Exception {
-        ExpandableNotificationRow row = createBlockableRowSpy();
-        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
-        // Hide the shade
-        mBlockingHelperManager.setNotificationShadeExpanded(0f);
-
-        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
-    }
-
-    @Test
-    public void testPerhapsShowBlockingHelper_notShownDueToNonblockability() throws Exception {
-        ExpandableNotificationRow row = createBlockableRowSpy();
-        when(row.getIsNonblockable()).thenReturn(true);
-        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
-
-        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
-    }
-
-    @Test
-    public void testPerhapsShowBlockingHelper_notShownAsNotificationIsInMultipleChildGroup()
-            throws Exception {
-        ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(2);
-        // Explicitly get the children container & call getViewAtPosition on it instead of the row
-        // as other factors such as view expansion may cause us to get the parent row back instead
-        // of the child row.
-        ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
-        childRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
-
-        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
-    }
-
-    @Test
-    public void testBlockingHelperShowAndDismiss() throws Exception{
-        ExpandableNotificationRow row = createBlockableRowSpy();
-        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
-        when(row.isAttachedToWindow()).thenReturn(true);
-
-        // Show check
-        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
-
-        verify(mGutsManager).openGuts(row, 0, 0, mMenuItem);
-
-        // Dismiss check
-        assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
-        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
-
-        verify(mEntryManager).updateNotifications();
-    }
-
-    @Test
-    public void testNonBlockable_package() {
-        mBlockingHelperManager.setNonBlockablePkgs(new String[] {"banana", "strawberry:pie"});
-
-        assertFalse(mBlockingHelperManager.isNonblockable("orange", "pie"));
-
-        assertTrue(mBlockingHelperManager.isNonblockable("banana", "pie"));
-    }
-
-    @Test
-    public void testNonBlockable_channel() {
-        mBlockingHelperManager.setNonBlockablePkgs(new String[] {"banana", "strawberry:pie"});
-
-        assertFalse(mBlockingHelperManager.isNonblockable("strawberry", "shortcake"));
-
-        assertTrue(mBlockingHelperManager.isNonblockable("strawberry", "pie"));
-    }
-
-    private ExpandableNotificationRow createBlockableRowSpy() throws Exception {
-        ExpandableNotificationRow row = spy(mHelper.createRow());
-        when(row.getIsNonblockable()).thenReturn(false);
-        return row;
-    }
-
-    private ExpandableNotificationRow createBlockableGroupRowSpy(int numChildren) throws Exception {
-        ExpandableNotificationRow row = spy(mHelper.createGroup(numChildren));
-        when(row.getIsNonblockable()).thenReturn(false);
-        return row;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
deleted file mode 100644
index 1fb4c37..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
+++ /dev/null
@@ -1,118 +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
- */
-
-package com.android.systemui.statusbar;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.Mockito.doNothing;
-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.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.AppOpsManager;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.ArraySet;
-import android.view.NotificationHeaderView;
-import android.view.View;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class NotificationContentViewTest extends SysuiTestCase {
-
-    NotificationContentView mView;
-
-    @Before
-    @UiThreadTest
-    public void setup() {
-        mView = new NotificationContentView(mContext, null);
-        ExpandableNotificationRow row = new ExpandableNotificationRow(mContext, null);
-        ExpandableNotificationRow mockRow = spy(row);
-        doNothing().when(mockRow).updateBackgroundAlpha(anyFloat());
-        doReturn(10).when(mockRow).getIntrinsicHeight();
-
-        mView.setContainingNotification(mockRow);
-        mView.setHeights(10, 20, 30, 40);
-
-        mView.setContractedChild(createViewWithHeight(10));
-        mView.setExpandedChild(createViewWithHeight(20));
-        mView.setHeadsUpChild(createViewWithHeight(30));
-        mView.setAmbientChild(createViewWithHeight(40));
-
-        mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
-        mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
-    }
-
-    private View createViewWithHeight(int height) {
-        View view = new View(mContext, null);
-        view.setMinimumHeight(height);
-        return view;
-    }
-
-    @Test
-    @UiThreadTest
-    public void animationStartType_getsClearedAfterUpdatingVisibilitiesWithoutAnimation() {
-        mView.setHeadsUp(true);
-        mView.setDark(true, false, 0);
-        mView.setDark(false, true, 0);
-        mView.setHeadsUpAnimatingAway(true);
-        Assert.assertFalse(mView.isAnimatingVisibleType());
-    }
-
-    @Test
-    @UiThreadTest
-    public void testShowAppOpsIcons() {
-        NotificationHeaderView mockContracted = mock(NotificationHeaderView.class);
-        when(mockContracted.findViewById(com.android.internal.R.id.notification_header))
-                .thenReturn(mockContracted);
-        NotificationHeaderView mockExpanded = mock(NotificationHeaderView.class);
-        when(mockExpanded.findViewById(com.android.internal.R.id.notification_header))
-                .thenReturn(mockExpanded);
-        NotificationHeaderView mockHeadsUp = mock(NotificationHeaderView.class);
-        when(mockHeadsUp.findViewById(com.android.internal.R.id.notification_header))
-                .thenReturn(mockHeadsUp);
-        NotificationHeaderView mockAmbient = mock(NotificationHeaderView.class);
-        when(mockAmbient.findViewById(com.android.internal.R.id.notification_header))
-                .thenReturn(mockAmbient);
-
-        mView.setContractedChild(mockContracted);
-        mView.setExpandedChild(mockExpanded);
-        mView.setHeadsUpChild(mockHeadsUp);
-        mView.setAmbientChild(mockAmbient);
-
-        ArraySet<Integer> ops = new ArraySet<>();
-        ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
-        mView.showAppOpsIcons(ops);
-
-        verify(mockContracted, times(1)).showAppOpsIcons(ops);
-        verify(mockExpanded, times(1)).showAppOpsIcons(ops);
-        verify(mockAmbient, never()).showAppOpsIcons(ops);
-        verify(mockHeadsUp, times(1)).showAppOpsIcons(any());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java
deleted file mode 100644
index a34588d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.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.systemui.statusbar;
-
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-import android.widget.RemoteViews;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
-public class NotificationCustomViewWrapperTest extends SysuiTestCase {
-
-    private ExpandableNotificationRow mRow;
-
-    @Before
-    public void setUp() throws Exception {
-        mRow = new NotificationTestHelper(mContext).createRow();
-    }
-
-    @Test
-    public void testBackgroundPersists() {
-        RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.custom_view_dark);
-        View v = views.apply(mContext, null);
-        NotificationViewWrapper wrap = NotificationCustomViewWrapper.wrap(mContext, v, mRow);
-        wrap.onContentUpdated(mRow);
-        Assert.assertTrue("No background set, when applying custom background view",
-                wrap.getCustomBackgroundColor() != 0);
-        views.reapply(mContext, v);
-        wrap.onReinflated();
-        wrap.onContentUpdated(mRow);
-        Assert.assertTrue("Reapplying a custom remote view lost it's background!",
-                wrap.getCustomBackgroundColor() != 0);
-    }
-
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
deleted file mode 100644
index 77522e4..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
+++ /dev/null
@@ -1,423 +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;
-
-import static android.app.AppOpsManager.OP_ACCEPT_HANDOVER;
-import static android.app.AppOpsManager.OP_CAMERA;
-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 junit.framework.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.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.Manifest;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.media.session.MediaSession;
-import android.os.Bundle;
-import android.service.notification.NotificationListenerService;
-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.RunWithLooper;
-import android.util.ArraySet;
-
-import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
-public class NotificationDataTest 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 static final String TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY = "exempt";
-
-    private final StatusBarNotification mMockStatusBarNotification =
-            mock(StatusBarNotification.class);
-    @Mock
-    ForegroundServiceController mFsc;
-    @Mock
-    NotificationData.Environment mEnvironment;
-
-    private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
-    private NotificationData mNotificationData;
-    private ExpandableNotificationRow mRow;
-
-    @Before
-    public void setUp() throws Exception {
-        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);
-        when(mEnvironment.getGroupManager()).thenReturn(new NotificationGroupManager());
-        when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
-        when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
-        mNotificationData = new TestableNotificationData(mEnvironment);
-        mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
-        mRow = new NotificationTestHelper(getContext()).createRow();
-    }
-
-    @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());
-        Assert.assertTrue(mRow.getEntry().channel != null);
-    }
-
-
-
-    @Test
-    public void testAllRelevantNotisTaggedWithAppOps() throws Exception {
-        mNotificationData.add(mRow.getEntry());
-        ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
-        mNotificationData.add(row2.getEntry());
-        ExpandableNotificationRow diffPkg =
-                new NotificationTestHelper(getContext()).createRow("pkg", 4000);
-        mNotificationData.add(diffPkg.getEntry());
-
-        ArraySet<Integer> expectedOps = new ArraySet<>();
-        expectedOps.add(OP_CAMERA);
-        expectedOps.add(OP_ACCEPT_HANDOVER);
-
-        for (int op : expectedOps) {
-            mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
-                    NotificationTestHelper.PKG, mRow.getEntry().key, true);
-            mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
-                    NotificationTestHelper.PKG, row2.getEntry().key, true);
-        }
-        for (int op : expectedOps) {
-            assertTrue(mRow.getEntry().key + " doesn't have op " + op,
-                    mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(op));
-            assertTrue(row2.getEntry().key + " doesn't have op " + op,
-                    mNotificationData.get(row2.getEntry().key).mActiveAppOps.contains(op));
-            assertFalse(diffPkg.getEntry().key + " has op " + op,
-                    mNotificationData.get(diffPkg.getEntry().key).mActiveAppOps.contains(op));
-        }
-    }
-
-    @Test
-    public void testAppOpsRemoval() throws Exception {
-        mNotificationData.add(mRow.getEntry());
-        ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
-        mNotificationData.add(row2.getEntry());
-
-        ArraySet<Integer> expectedOps = new ArraySet<>();
-        expectedOps.add(OP_CAMERA);
-        expectedOps.add(OP_ACCEPT_HANDOVER);
-
-        for (int op : expectedOps) {
-            mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
-                    NotificationTestHelper.PKG, row2.getEntry().key, true);
-        }
-
-        expectedOps.remove(OP_ACCEPT_HANDOVER);
-        mNotificationData.updateAppOp(OP_ACCEPT_HANDOVER, NotificationTestHelper.UID,
-                NotificationTestHelper.PKG, row2.getEntry().key, false);
-
-        assertTrue(mRow.getEntry().key + " doesn't have op " + OP_CAMERA,
-                mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(OP_CAMERA));
-        assertTrue(row2.getEntry().key + " doesn't have op " + OP_CAMERA,
-                mNotificationData.get(row2.getEntry().key).mActiveAppOps.contains(OP_CAMERA));
-        assertFalse(mRow.getEntry().key + " has op " + OP_ACCEPT_HANDOVER,
-                mNotificationData.get(mRow.getEntry().key)
-                        .mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
-        assertFalse(row2.getEntry().key + " has op " + OP_ACCEPT_HANDOVER,
-                mNotificationData.get(row2.getEntry().key)
-                        .mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
-    }
-
-    @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());
-        ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
-        mNotificationData.add(row2.getEntry());
-
-        when(mEnvironment.isNotificationForCurrentProfiles(
-                mRow.getEntry().notification)).thenReturn(false);
-        when(mEnvironment.isNotificationForCurrentProfiles(
-                row2.getEntry().notification)).thenReturn(true);
-        ArrayList<NotificationData.Entry> reuslt =
-                mNotificationData.getNotificationsForCurrentUser();
-
-        assertEquals(reuslt.size(), 1);
-        assertEquals(reuslt.get(0), row2.getEntry());
-    }
-
-    @Test
-    public void testIsExemptFromDndVisualSuppression_foreground() {
-        initStatusBarNotification(false);
-        when(mMockStatusBarNotification.getKey()).thenReturn(
-                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
-        Notification n = mMockStatusBarNotification.getNotification();
-        n.flags = Notification.FLAG_FOREGROUND_SERVICE;
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
-
-        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
-        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
-    }
-
-    @Test
-    public void testIsExemptFromDndVisualSuppression_media() {
-        initStatusBarNotification(false);
-        when(mMockStatusBarNotification.getKey()).thenReturn(
-                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
-        Notification n = mMockStatusBarNotification.getNotification();
-        Notification.Builder nb = Notification.Builder.recoverBuilder(mContext, n);
-        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);
-
-        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
-        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
-    }
-
-    @Test
-    public void testIsExemptFromDndVisualSuppression_system() {
-        initStatusBarNotification(false);
-        when(mMockStatusBarNotification.getKey()).thenReturn(
-                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
-        entry.mIsSystemNotification = true;
-
-        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
-        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
-    }
-
-    @Test
-    public void testIsNotExemptFromDndVisualSuppression_hiddenCategories() {
-        initStatusBarNotification(false);
-        when(mMockStatusBarNotification.getKey()).thenReturn(
-                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
-        entry.mIsSystemNotification = true;
-        when(mMockStatusBarNotification.getNotification()).thenReturn(
-                new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build());
-
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
-        assertTrue(mNotificationData.shouldSuppressAmbient(entry));
-
-        when(mMockStatusBarNotification.getNotification()).thenReturn(
-                new Notification.Builder(mContext, "").setCategory(CATEGORY_REMINDER).build());
-
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
-
-        when(mMockStatusBarNotification.getNotification()).thenReturn(
-                new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build());
-
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
-
-        when(mMockStatusBarNotification.getNotification()).thenReturn(
-                new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build());
-
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
-
-        when(mMockStatusBarNotification.getNotification()).thenReturn(
-                new Notification.Builder(mContext, "").setCategory(CATEGORY_MESSAGE).build());
-
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(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);
-    }
-
-    private class TestableNotificationData extends NotificationData {
-        public TestableNotificationData(Environment environment) {
-            super(environment);
-        }
-
-        @Override
-        public NotificationChannel getChannel(String key) {
-            return new NotificationChannel(null, null, 0);
-        }
-
-        @Override
-        protected boolean getRanking(String key, NotificationListenerService.Ranking outRanking) {
-            super.getRanking(key, outRanking);
-            if (key.equals(TEST_HIDDEN_NOTIFICATION_KEY)) {
-                outRanking.populate(key, outRanking.getRank(),
-                        outRanking.matchesInterruptionFilter(),
-                        outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
-                        outRanking.getImportance(), outRanking.getImportanceExplanation(),
-                        outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
-                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
-            } 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);
-            } else {
-                outRanking.populate(key, outRanking.getRank(),
-                        outRanking.matchesInterruptionFilter(),
-                        outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
-                        outRanking.getImportance(), outRanking.getImportanceExplanation(),
-                        outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
-                        outRanking.canShowBadge(), outRanking.getUserSentiment(), false);
-            }
-            return true;
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
deleted file mode 100644
index afe16cf..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
+++ /dev/null
@@ -1,430 +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;
-
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.assertFalse;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.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;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.util.ArraySet;
-import android.widget.FrameLayout;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-
-import junit.framework.Assert;
-
-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.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class NotificationEntryManagerTest extends SysuiTestCase {
-    private static final String TEST_PACKAGE_NAME = "test";
-    private static final int TEST_UID = 0;
-
-    @Mock private NotificationPresenter mPresenter;
-    @Mock private ExpandableNotificationRow mRow;
-    @Mock private NotificationListContainer mListContainer;
-    @Mock private NotificationEntryManager.Callback mCallback;
-    @Mock private HeadsUpManager mHeadsUpManager;
-    @Mock private NotificationListenerService.RankingMap mRankingMap;
-    @Mock private RemoteInputController mRemoteInputController;
-    @Mock private IStatusBarService mBarService;
-
-    // Dependency mocks:
-    @Mock private ForegroundServiceController mForegroundServiceController;
-    @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
-    @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;
-
-    private NotificationData.Entry mEntry;
-    private StatusBarNotification mSbn;
-    private TestableNotificationEntryManager mEntryManager;
-    private CountDownLatch mCountDownLatch;
-
-    private class TestableNotificationEntryManager extends NotificationEntryManager {
-        private final CountDownLatch mCountDownLatch;
-
-        public TestableNotificationEntryManager(Context context, IStatusBarService barService) {
-            super(context);
-            mBarService = barService;
-            mCountDownLatch = new CountDownLatch(1);
-            mUseHeadsUp = true;
-        }
-
-        @Override
-        public void onAsyncInflationFinished(NotificationData.Entry entry) {
-            super.onAsyncInflationFinished(entry);
-
-            mCountDownLatch.countDown();
-        }
-
-        public CountDownLatch getCountDownLatch() {
-            return mCountDownLatch;
-        }
-    }
-
-    private void setUserSentiment(String key, int sentiment) {
-        doAnswer(invocationOnMock -> {
-            NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
-                    invocationOnMock.getArguments()[1];
-            ranking.populate(
-                    key,
-                    0,
-                    false,
-                    0,
-                    0,
-                    NotificationManager.IMPORTANCE_DEFAULT,
-                    null, null,
-                    null, null, null, true, sentiment, false);
-            return true;
-        }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
-    }
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mDependency.injectTestDependency(ForegroundServiceController.class,
-                mForegroundServiceController);
-        mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
-                mLockscreenUserManager);
-        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);
-        mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
-        mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
-        mDependency.injectTestDependency(SmartReplyController.class, mSmartReplyController);
-
-        mCountDownLatch = new CountDownLatch(1);
-
-        when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper()));
-        when(mPresenter.getNotificationLockscreenUserManager()).thenReturn(mLockscreenUserManager);
-        when(mPresenter.getGroupManager()).thenReturn(mGroupManager);
-        when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
-        when(mListContainer.getViewParentForNotification(any())).thenReturn(
-                new FrameLayout(mContext));
-
-        Notification.Builder n = new Notification.Builder(mContext, "")
-                .setSmallIcon(R.drawable.ic_person)
-                .setContentTitle("Title")
-                .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.expandedIcon = mock(StatusBarIconView.class);
-
-        mEntryManager = new TestableNotificationEntryManager(mContext, mBarService);
-        mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager);
-
-        setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
-    }
-
-    @Test
-    public void testAddNotification() throws Exception {
-        com.android.systemui.util.Assert.isNotMainThread();
-        TestableLooper.get(this).processAllMessages();
-
-        doAnswer(invocation -> {
-            mCountDownLatch.countDown();
-            return null;
-        }).when(mCallback).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.
-        mEntryManager.addNotification(mSbn, mRankingMap);
-        TestableLooper.get(this).processMessages(1);
-        assertTrue(mCountDownLatch.await(10, TimeUnit.SECONDS));
-        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());
-
-        // 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();
-        verify(mRemoteInputManager).bindRow(entry.row);
-
-        // Row content inflation:
-        verify(mCallback).onNotificationAdded(entry);
-        verify(mPresenter).updateNotificationViews();
-
-        assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
-        assertNotNull(entry.row);
-        assertEquals(mEntry.userSentiment,
-                NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
-    }
-
-    @Test
-    public void testUpdateNotification() throws Exception {
-        com.android.systemui.util.Assert.isNotMainThread();
-        TestableLooper.get(this).processAllMessages();
-
-        mEntryManager.getNotificationData().add(mEntry);
-
-        setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
-
-        mEntryManager.updateNotification(mSbn, mRankingMap);
-        TestableLooper.get(this).processMessages(1);
-        // Wait for content update.
-        assertTrue(mEntryManager.getCountDownLatch().await(10, TimeUnit.SECONDS));
-
-        verify(mBarService, never()).onNotificationError(any(), any(), anyInt(), anyInt(), anyInt(),
-                any(), anyInt());
-
-        verify(mRemoteInputManager).onUpdateNotification(mEntry);
-        verify(mPresenter).updateNotificationViews();
-        verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
-        verify(mCallback).onNotificationUpdated(mSbn);
-        assertNotNull(mEntry.row);
-        assertEquals(mEntry.userSentiment,
-                NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
-    }
-
-    @Test
-    public void testRemoveNotification() throws Exception {
-        com.android.systemui.util.Assert.isNotMainThread();
-
-        mEntry.row = mRow;
-        mEntryManager.getNotificationData().add(mEntry);
-
-        mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
-
-        verify(mBarService, never()).onNotificationError(any(), any(), anyInt(), anyInt(), anyInt(),
-                any(), anyInt());
-
-        verify(mMediaManager).onNotificationRemoved(mSbn.getKey());
-        verify(mRemoteInputManager).onRemoveNotification(mEntry);
-        verify(mSmartReplyController).stopSending(mEntry);
-        verify(mForegroundServiceController).removeNotification(mSbn);
-        verify(mListContainer).cleanUpViewState(mRow);
-        verify(mPresenter).updateNotificationViews();
-        verify(mCallback).onNotificationRemoved(mSbn.getKey(), mSbn);
-        verify(mRow).setRemoved();
-
-        assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
-    }
-
-    @Test
-    public void testRemoveNotification_blockedBySendingSmartReply() throws Exception {
-        com.android.systemui.util.Assert.isNotMainThread();
-
-        mEntry.row = mRow;
-        mEntryManager.getNotificationData().add(mEntry);
-        when(mSmartReplyController.isSendingSmartReply(mEntry.key)).thenReturn(true);
-
-        mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
-
-        assertNotNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
-        assertTrue(mEntryManager.isNotificationKeptForRemoteInput(mEntry.key));
-    }
-
-    @Test
-    public void testUpdateAppOps_foregroundNoti() {
-        com.android.systemui.util.Assert.isNotMainThread();
-
-        when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
-                .thenReturn(mEntry.key);
-        mEntry.row = mRow;
-        mEntryManager.getNotificationData().add(mEntry);
-
-        mEntryManager.updateNotificationsForAppOp(
-                AppOpsManager.OP_CAMERA, mEntry.notification.getUid(),
-                mEntry.notification.getPackageName(), true);
-
-        verify(mPresenter, times(1)).updateNotificationViews();
-        assertTrue(mEntryManager.getNotificationData().get(mEntry.key).mActiveAppOps.contains(
-                AppOpsManager.OP_CAMERA));
-    }
-
-    @Test
-    public void testUpdateAppOps_otherNoti() {
-        com.android.systemui.util.Assert.isNotMainThread();
-
-        when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
-                .thenReturn(null);
-        mEntryManager.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
-
-        verify(mPresenter, never()).updateNotificationViews();
-    }
-
-    @Test
-    public void testAddNotificationExistingAppOps() {
-        mEntry.row = mRow;
-        mEntryManager.getNotificationData().add(mEntry);
-        ArraySet<Integer> expected = new ArraySet<>();
-        expected.add(3);
-        expected.add(235);
-        expected.add(1);
-
-        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
-                mEntry.notification.getPackageName())).thenReturn(expected);
-        when(mForegroundServiceController.getStandardLayoutKey(
-                mEntry.notification.getUserId(),
-                mEntry.notification.getPackageName())).thenReturn(mEntry.key);
-
-        mEntryManager.tagForeground(mEntry.notification);
-
-        Assert.assertEquals(expected.size(), mEntry.mActiveAppOps.size());
-        for (int op : expected) {
-            assertTrue("Entry missing op " + op, mEntry.mActiveAppOps.contains(op));
-        }
-    }
-
-    @Test
-    public void testAdd_noExistingAppOps() {
-        mEntry.row = mRow;
-        mEntryManager.getNotificationData().add(mEntry);
-        when(mForegroundServiceController.getStandardLayoutKey(
-                mEntry.notification.getUserId(),
-                mEntry.notification.getPackageName())).thenReturn(mEntry.key);
-        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
-                mEntry.notification.getPackageName())).thenReturn(null);
-
-        mEntryManager.tagForeground(mEntry.notification);
-        Assert.assertEquals(0, mEntry.mActiveAppOps.size());
-    }
-
-    @Test
-    public void testAdd_existingAppOpsNotForegroundNoti() {
-        mEntry.row = mRow;
-        mEntryManager.getNotificationData().add(mEntry);
-        ArraySet<Integer> ops = new ArraySet<>();
-        ops.add(3);
-        ops.add(235);
-        ops.add(1);
-        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
-                mEntry.notification.getPackageName())).thenReturn(ops);
-        when(mForegroundServiceController.getStandardLayoutKey(
-                mEntry.notification.getUserId(),
-                mEntry.notification.getPackageName())).thenReturn("something else");
-
-        mEntryManager.tagForeground(mEntry.notification);
-        Assert.assertEquals(0, mEntry.mActiveAppOps.size());
-    }
-
-    @Test
-    public void testRebuildWithRemoteInput_noExistingInputNoSpinner() {
-        StatusBarNotification newSbn =
-                mEntryManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
-        CharSequence[] messages = newSbn.getNotification().extras
-                .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
-        Assert.assertEquals(1, messages.length);
-        Assert.assertEquals("A Reply", messages[0]);
-        Assert.assertFalse(newSbn.getNotification().extras
-                .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
-        Assert.assertTrue(newSbn.getNotification().extras
-                .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false));
-    }
-
-    @Test
-    public void testRebuildWithRemoteInput_noExistingInputWithSpinner() {
-        StatusBarNotification newSbn =
-                mEntryManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", true);
-        CharSequence[] messages = newSbn.getNotification().extras
-                .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
-        Assert.assertEquals(1, messages.length);
-        Assert.assertEquals("A Reply", messages[0]);
-        Assert.assertTrue(newSbn.getNotification().extras
-                .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
-        Assert.assertTrue(newSbn.getNotification().extras
-                .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false));
-    }
-
-    @Test
-    public void testRebuildWithRemoteInput_withExistingInput() {
-        // Setup a notification entry with 1 remote input.
-        StatusBarNotification newSbn =
-                mEntryManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
-        NotificationData.Entry entry = new NotificationData.Entry(newSbn);
-
-        // Try rebuilding to add another reply.
-        newSbn = mEntryManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true);
-        CharSequence[] messages = newSbn.getNotification().extras
-                .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
-        Assert.assertEquals(2, messages.length);
-        Assert.assertEquals("Reply 2", messages[0]);
-        Assert.assertEquals("A Reply", messages[1]);
-    }
-
-    @Test
-    public void testRebuildNotificationForCanceledSmartReplies() {
-        // Try rebuilding to remove spinner and hide buttons.
-        StatusBarNotification newSbn =
-                mEntryManager.rebuildNotificationForCanceledSmartReplies(mEntry);
-        Assert.assertFalse(newSbn.getNotification().extras
-                .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
-        Assert.assertTrue(newSbn.getNotification().extras
-                .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false));
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
deleted file mode 100644
index 72255f3..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
+++ /dev/null
@@ -1,349 +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.app.AppOpsManager.OP_CAMERA;
-import static android.app.AppOpsManager.OP_RECORD_AUDIO;
-import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
-import static junit.framework.Assert.assertNotNull;
-
-import static org.junit.Assert.assertEquals;
-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.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.times;
-
-import android.app.INotificationManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Binder;
-import android.os.Handler;
-import android.provider.Settings;
-import android.service.notification.StatusBarNotification;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.util.ArraySet;
-import android.view.View;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoRule;
-import org.mockito.junit.MockitoJUnit;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Tests for {@link NotificationGutsManager}.
- */
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class NotificationGutsManagerTest extends SysuiTestCase {
-    private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
-
-    private NotificationChannel mTestNotificationChannel = new NotificationChannel(
-            TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
-    private TestableLooper mTestableLooper;
-    private Handler mHandler;
-    private NotificationTestHelper mHelper;
-    private NotificationGutsManager mGutsManager;
-
-    @Rule public MockitoRule mockito = MockitoJUnit.rule();
-    @Mock private NotificationPresenter mPresenter;
-    @Mock private NotificationEntryManager mEntryManager;
-    @Mock private NotificationStackScrollLayout mStackScroller;
-    @Mock private NotificationInfo.CheckSaveListener mCheckSaveListener;
-    @Mock private NotificationGutsManager.OnSettingsClickListener mOnSettingsClickListener;
-
-    @Before
-    public void setUp() {
-        mTestableLooper = TestableLooper.get(this);
-        mHandler = Handler.createAsync(mTestableLooper.getLooper());
-
-        mHelper = new NotificationTestHelper(mContext);
-
-        mGutsManager = new NotificationGutsManager(mContext);
-        mGutsManager.setUpWithPresenter(mPresenter, mEntryManager, mStackScroller,
-                mCheckSaveListener, mOnSettingsClickListener);
-    }
-
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    // Test methods:
-
-    @Test
-    public void testOpenAndCloseGuts() {
-        NotificationGuts guts = spy(new NotificationGuts(mContext));
-        when(guts.post(any())).thenAnswer(invocation -> {
-            mHandler.post(((Runnable) invocation.getArguments()[0]));
-            return null;
-        });
-
-        // Test doesn't support animation since the guts view is not attached.
-        doNothing().when(guts).openControls(
-                eq(true) /* shouldDoCircularReveal */,
-                anyInt(),
-                anyInt(),
-                anyBoolean(),
-                any(Runnable.class));
-
-        ExpandableNotificationRow realRow = createTestNotificationRow();
-        NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
-
-        ExpandableNotificationRow row = spy(realRow);
-        when(row.getWindowToken()).thenReturn(new Binder());
-        when(row.getGuts()).thenReturn(guts);
-
-        mGutsManager.openGuts(row, 0, 0, menuItem);
-        assertEquals(View.INVISIBLE, guts.getVisibility());
-        mTestableLooper.processAllMessages();
-        verify(guts).openControls(
-                eq(true),
-                anyInt(),
-                anyInt(),
-                anyBoolean(),
-                any(Runnable.class));
-
-        assertEquals(View.VISIBLE, guts.getVisibility());
-        mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
-
-        verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
-        verify(row, times(1)).setGutsView(any());
-    }
-
-    @Test
-    public void testChangeDensityOrFontScale() {
-        NotificationGuts guts = spy(new NotificationGuts(mContext));
-        when(guts.post(any())).thenAnswer(invocation -> {
-            mHandler.post(((Runnable) invocation.getArguments()[0]));
-            return null;
-        });
-
-        // Test doesn't support animation since the guts view is not attached.
-        doNothing().when(guts).openControls(
-                eq(true) /* shouldDoCircularReveal */,
-                anyInt(),
-                anyInt(),
-                anyBoolean(),
-                any(Runnable.class));
-
-        ExpandableNotificationRow realRow = createTestNotificationRow();
-        NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
-
-        ExpandableNotificationRow row = spy(realRow);
-        when(row.getWindowToken()).thenReturn(new Binder());
-        when(row.getGuts()).thenReturn(guts);
-        doNothing().when(row).inflateGuts();
-
-        mGutsManager.openGuts(row, 0, 0, menuItem);
-        mTestableLooper.processAllMessages();
-        verify(guts).openControls(
-                eq(true),
-                anyInt(),
-                anyInt(),
-                anyBoolean(),
-                any(Runnable.class));
-
-        row.onDensityOrFontScaleChanged();
-        mGutsManager.onDensityOrFontScaleChanged(row);
-        mTestableLooper.processAllMessages();
-
-        mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
-
-        verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
-        verify(row, times(2)).setGutsView(any());
-    }
-
-    @Test
-    public void testAppOpsSettingsIntent_camera() {
-        ArraySet<Integer> ops = new ArraySet<>();
-        ops.add(OP_CAMERA);
-        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
-        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
-        assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
-    }
-
-    @Test
-    public void testAppOpsSettingsIntent_mic() {
-        ArraySet<Integer> ops = new ArraySet<>();
-        ops.add(OP_RECORD_AUDIO);
-        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
-        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
-        assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
-    }
-
-    @Test
-    public void testAppOpsSettingsIntent_camera_mic() {
-        ArraySet<Integer> ops = new ArraySet<>();
-        ops.add(OP_CAMERA);
-        ops.add(OP_RECORD_AUDIO);
-        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
-        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
-        assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
-    }
-
-    @Test
-    public void testAppOpsSettingsIntent_overlay() {
-        ArraySet<Integer> ops = new ArraySet<>();
-        ops.add(OP_SYSTEM_ALERT_WINDOW);
-        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
-        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
-        assertEquals(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, captor.getValue().getAction());
-    }
-
-    @Test
-    public void testAppOpsSettingsIntent_camera_mic_overlay() {
-        ArraySet<Integer> ops = new ArraySet<>();
-        ops.add(OP_CAMERA);
-        ops.add(OP_RECORD_AUDIO);
-        ops.add(OP_SYSTEM_ALERT_WINDOW);
-        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
-        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
-        assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
-    }
-
-    @Test
-    public void testAppOpsSettingsIntent_camera_overlay() {
-        ArraySet<Integer> ops = new ArraySet<>();
-        ops.add(OP_CAMERA);
-        ops.add(OP_SYSTEM_ALERT_WINDOW);
-        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
-        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
-        assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
-    }
-
-    @Test
-    public void testAppOpsSettingsIntent_mic_overlay() {
-        ArraySet<Integer> ops = new ArraySet<>();
-        ops.add(OP_RECORD_AUDIO);
-        ops.add(OP_SYSTEM_ALERT_WINDOW);
-        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
-        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
-        assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
-    }
-
-    @Test
-    public void testInitializeNotificationInfoView_showBlockingHelper() throws Exception {
-        NotificationInfo notificationInfoView = mock(NotificationInfo.class);
-        ExpandableNotificationRow row = spy(mHelper.createRow());
-        row.setBlockingHelperShowing(true);
-        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
-        when(row.getIsNonblockable()).thenReturn(false);
-        StatusBarNotification statusBarNotification = row.getStatusBarNotification();
-
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
-
-        verify(notificationInfoView).bindNotification(
-                any(PackageManager.class),
-                any(INotificationManager.class),
-                eq(statusBarNotification.getPackageName()),
-                any(NotificationChannel.class),
-                anyInt(),
-                eq(statusBarNotification),
-                any(NotificationInfo.CheckSaveListener.class),
-                any(NotificationInfo.OnSettingsClickListener.class),
-                any(NotificationInfo.OnAppSettingsClickListener.class),
-                eq(false),
-                eq(true) /* isForBlockingHelper */,
-                eq(true) /* isUserSentimentNegative */);
-    }
-
-    @Test
-    public void testInitializeNotificationInfoView_dontShowBlockingHelper() throws Exception {
-        NotificationInfo notificationInfoView = mock(NotificationInfo.class);
-        ExpandableNotificationRow row = spy(mHelper.createRow());
-        row.setBlockingHelperShowing(false);
-        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
-        when(row.getIsNonblockable()).thenReturn(false);
-        StatusBarNotification statusBarNotification = row.getStatusBarNotification();
-
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
-
-        verify(notificationInfoView).bindNotification(
-                any(PackageManager.class),
-                any(INotificationManager.class),
-                eq(statusBarNotification.getPackageName()),
-                any(NotificationChannel.class),
-                anyInt(),
-                eq(statusBarNotification),
-                any(NotificationInfo.CheckSaveListener.class),
-                any(NotificationInfo.OnSettingsClickListener.class),
-                any(NotificationInfo.OnAppSettingsClickListener.class),
-                eq(false),
-                eq(false) /* isForBlockingHelper */,
-                eq(true) /* isUserSentimentNegative */);
-    }
-
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    // Utility methods:
-
-    private ExpandableNotificationRow createTestNotificationRow() {
-        Notification.Builder nb = new Notification.Builder(mContext,
-                mTestNotificationChannel.getId())
-                                        .setContentTitle("foo")
-                                        .setColorized(true)
-                                        .setFlag(Notification.FLAG_CAN_COLORIZE, true)
-                                        .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
-        try {
-            ExpandableNotificationRow row = mHelper.createRow(nb.build());
-            row.getEntry().channel = mTestNotificationChannel;
-            return row;
-        } catch (Exception e) {
-            fail();
-            return null;
-        }
-    }
-
-    private NotificationMenuRowPlugin.MenuItem createTestMenuItem(ExpandableNotificationRow row) {
-        NotificationMenuRowPlugin menuRow = new NotificationMenuRow(mContext);
-        menuRow.createMenu(row, row.getStatusBarNotification());
-
-        NotificationMenuRowPlugin.MenuItem menuItem = menuRow.getLongpressMenuItem(mContext);
-        assertNotNull(menuItem);
-        return menuItem;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
deleted file mode 100644
index a72fed4..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ /dev/null
@@ -1,994 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
-import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
-import static android.view.View.GONE;
-import static android.view.View.VISIBLE;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.INotificationManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationChannelGroup;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.IBinder;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.PollingCheck;
-import android.testing.TestableLooper;
-import android.testing.UiThreadTest;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class NotificationInfoTest extends SysuiTestCase {
-    private static final String TEST_PACKAGE_NAME = "test_package";
-    private static final String TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME;
-    private static final int TEST_UID = 1;
-    private static final int MULTIPLE_CHANNEL_COUNT = 2;
-    private static final String TEST_CHANNEL = "test_channel";
-    private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
-
-    private TestableLooper mTestableLooper;
-    private NotificationInfo mNotificationInfo;
-    private NotificationChannel mNotificationChannel;
-    private NotificationChannel mDefaultNotificationChannel;
-    private StatusBarNotification mSbn;
-
-    @Rule public MockitoRule mockito = MockitoJUnit.rule();
-    @Mock private MetricsLogger mMetricsLogger;
-    @Mock private INotificationManager mMockINotificationManager;
-    @Mock private PackageManager mMockPackageManager;
-    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
-
-    @Before
-    public void setUp() throws Exception {
-        mDependency.injectTestDependency(
-                NotificationBlockingHelperManager.class,
-                mBlockingHelperManager);
-        mTestableLooper = TestableLooper.get(this);
-        mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
-        mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
-        // Inflate the layout
-        final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
-        mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
-                null);
-        mNotificationInfo.setGutsParent(mock(NotificationGuts.class));
-
-        // PackageManager must return a packageInfo and applicationInfo.
-        final PackageInfo packageInfo = new PackageInfo();
-        packageInfo.packageName = TEST_PACKAGE_NAME;
-        when(mMockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt()))
-                .thenReturn(packageInfo);
-        final ApplicationInfo applicationInfo = new ApplicationInfo();
-        applicationInfo.uid = TEST_UID;  // non-zero
-        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
-                applicationInfo);
-        final PackageInfo systemPackageInfo = new PackageInfo();
-        systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME;
-        when(mMockPackageManager.getPackageInfo(eq(TEST_SYSTEM_PACKAGE_NAME), anyInt()))
-                .thenReturn(systemPackageInfo);
-        when(mMockPackageManager.getPackageInfo(eq("android"), anyInt()))
-                .thenReturn(packageInfo);
-
-        // Package has one channel by default.
-        when(mMockINotificationManager.getNumNotificationChannelsForPackage(
-                eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(1);
-
-        // Some test channels.
-        mNotificationChannel = new NotificationChannel(
-                TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
-        mDefaultNotificationChannel = new NotificationChannel(
-                NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
-                IMPORTANCE_LOW);
-        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
-                new Notification(), UserHandle.CURRENT, null, 0);
-    }
-
-    // TODO: if tests are taking too long replace this with something that makes the animation
-    // finish instantly.
-    private void waitForUndoButton() {
-        PollingCheck.waitFor(1000,
-                () -> VISIBLE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility());
-    }
-    private void waitForStopButton() {
-        PollingCheck.waitFor(1000,
-                () -> VISIBLE == mNotificationInfo.findViewById(R.id.prompt).getVisibility());
-    }
-
-    @Test
-    public void testBindNotification_SetsTextApplicationName() throws Exception {
-        when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-        final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
-        assertTrue(textView.getText().toString().contains("App Name"));
-        assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
-    }
-
-    @Test
-    public void testBindNotification_SetsPackageIcon() throws Exception {
-        final Drawable iconDrawable = mock(Drawable.class);
-        when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
-                .thenReturn(iconDrawable);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-        final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
-        assertEquals(iconDrawable, iconView.getDrawable());
-    }
-
-    @Test
-    public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-        final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
-        assertEquals(GONE, groupNameView.getVisibility());
-        final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
-        assertEquals(GONE, groupDividerView.getVisibility());
-    }
-
-    @Test
-    public void testBindNotification_SetsGroupNameIfNonNull() throws Exception {
-        mNotificationChannel.setGroup("test_group_id");
-        final NotificationChannelGroup notificationChannelGroup =
-                new NotificationChannelGroup("test_group_id", "Test Group Name");
-        when(mMockINotificationManager.getNotificationChannelGroupForPackage(
-                eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
-                .thenReturn(notificationChannelGroup);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-        final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
-        assertEquals(View.VISIBLE, groupNameView.getVisibility());
-        assertEquals("Test Group Name", groupNameView.getText());
-        final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
-        assertEquals(View.VISIBLE, groupDividerView.getVisibility());
-    }
-
-    @Test
-    public void testBindNotification_SetsTextChannelName() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-        final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
-        assertEquals(TEST_CHANNEL_NAME, textView.getText());
-    }
-
-    @Test
-    public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, false);
-        final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
-        assertEquals(GONE, textView.getVisibility());
-    }
-
-    @Test
-    public void testBindNotification_DefaultChannelUsesChannelNameIfMoreChannelsExist()
-            throws Exception {
-        // Package has one channel by default.
-        when(mMockINotificationManager.getNumNotificationChannelsForPackage(
-                eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, false);
-        final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
-        assertEquals(VISIBLE, textView.getVisibility());
-    }
-
-    @Test
-    public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
-        final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
-        assertEquals(VISIBLE, textView.getVisibility());
-    }
-
-    @Test
-    public void testBindNotification_BlockButton() throws Exception {
-       mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-        final View block = mNotificationInfo.findViewById(R.id.block);
-        final View minimize = mNotificationInfo.findViewById(R.id.minimize);
-        assertEquals(VISIBLE, block.getVisibility());
-        assertEquals(GONE, minimize.getVisibility());
-    }
-
-    @Test
-    public void testBindNotification_MinButton() throws Exception {
-        mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-        final View block = mNotificationInfo.findViewById(R.id.block);
-        final View minimize = mNotificationInfo.findViewById(R.id.minimize);
-        assertEquals(GONE, block.getVisibility());
-        assertEquals(VISIBLE, minimize.getVisibility());
-    }
-
-    @Test
-    public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
-                (View v, NotificationChannel c, int appUid) -> {
-                    assertEquals(mNotificationChannel, c);
-                    latch.countDown();
-                }, null, false);
-
-        final View settingsButton = mNotificationInfo.findViewById(R.id.info);
-        settingsButton.performClick();
-        // Verify that listener was triggered.
-        assertEquals(0, latch.getCount());
-    }
-
-    @Test
-    public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-        final View settingsButton = mNotificationInfo.findViewById(R.id.info);
-        assertTrue(settingsButton.getVisibility() != View.VISIBLE);
-    }
-
-    @Test
-    public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
-                (View v, NotificationChannel c, int appUid) -> {
-                }, null, false);
-        final View settingsButton = mNotificationInfo.findViewById(R.id.info);
-        assertEquals(View.VISIBLE, settingsButton.getVisibility());
-    }
-
-    @Test
-    public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-        mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
-        verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
-    }
-
-    @Test
-    public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
-                true);
-        mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
-        verify(mMetricsLogger, times(1)).count(anyString(), anyInt());
-    }
-
-    @Test
-    public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null,
-                (View v, NotificationChannel c, int appUid) -> {
-                    assertEquals(null, c);
-                    latch.countDown();
-                }, null, true);
-
-        mNotificationInfo.findViewById(R.id.info).performClick();
-        // Verify that listener was triggered.
-        assertEquals(0, latch.getCount());
-    }
-
-    @Test
-    @UiThreadTest
-    public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels()
-            throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
-                null, true);
-        final TextView channelNameView =
-                mNotificationInfo.findViewById(R.id.channel_name);
-        assertEquals(GONE, channelNameView.getVisibility());
-    }
-
-    @Test
-    @UiThreadTest
-    public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
-                null, true);
-        final TextView blockView = mNotificationInfo.findViewById(R.id.block);
-        assertEquals(GONE, blockView.getVisibility());
-    }
-
-    @Test
-    public void testbindNotification_BlockingHelper() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false,
-                true);
-        final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
-        assertEquals(View.VISIBLE, view.getVisibility());
-        assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
-    }
-
-    @Test
-    public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
-        final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
-        assertEquals(View.VISIBLE, view.getVisibility());
-        assertEquals(mContext.getString(R.string.notification_unblockable_desc),
-                view.getText());
-    }
-
-    @Test
-    public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), any());
-    }
-
-    @Test
-    public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), any());
-    }
-
-    @Test
-    public void testDoesNotUpdateNotificationChannelAfterImportanceChangedMin()
-            throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-
-        mNotificationInfo.findViewById(R.id.minimize).performClick();
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), any());
-    }
-
-    @Test
-    public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged()
-            throws Exception {
-        int originalImportance = mNotificationChannel.getImportance();
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-
-        mNotificationInfo.handleCloseControls(true, false);
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), any());
-        assertEquals(originalImportance, mNotificationChannel.getImportance());
-    }
-
-    @Test
-    public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnspecified()
-            throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), any());
-        assertEquals(IMPORTANCE_UNSPECIFIED, mNotificationChannel.getImportance());
-    }
-
-    @Test
-    public void testHandleCloseControls_setsNotificationsDisabledForMultipleChannelNotifications()
-            throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
-                10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
-                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
-                false /* isNonblockable */);
-
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, times(1))
-                .setNotificationsEnabledWithImportanceLockForPackage(
-                        anyString(), eq(TEST_UID), eq(false));
-    }
-
-
-    @Test
-    public void testHandleCloseControls_keepsNotificationsEnabledForMultipleChannelNotifications()
-            throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
-                10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
-                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
-                false /* isNonblockable */);
-
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, times(1))
-                .setNotificationsEnabledWithImportanceLockForPackage(
-                        anyString(), eq(TEST_UID), eq(false));
-    }
-
-    @Test
-    public void testCloseControls_blockingHelperSavesImportanceForMultipleChannelNotifications()
-            throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
-                10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
-                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
-                false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */);
-
-        NotificationGuts guts = spy(new NotificationGuts(mContext, null));
-        when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
-        doNothing().when(guts).animateClose(anyInt(), anyInt(), anyBoolean());
-        doNothing().when(guts).setExposed(anyBoolean(), anyBoolean());
-        guts.setGutsContent(mNotificationInfo);
-        mNotificationInfo.setGutsParent(guts);
-
-        mNotificationInfo.findViewById(R.id.keep).performClick();
-
-        verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, times(1))
-                .setNotificationsEnabledWithImportanceLockForPackage(
-                        anyString(), eq(TEST_UID), eq(true));
-    }
-
-    @Test
-    public void testCloseControls_nonNullCheckSaveListenerDoesntDelayKeepShowing()
-            throws Exception {
-        NotificationInfo.CheckSaveListener listener =
-                mock(NotificationInfo.CheckSaveListener.class);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
-                10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
-                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
-                false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */);
-
-        NotificationGuts guts = spy(new NotificationGuts(mContext, null));
-        when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
-        doNothing().when(guts).animateClose(anyInt(), anyInt(), anyBoolean());
-        doNothing().when(guts).setExposed(anyBoolean(), anyBoolean());
-        guts.setGutsContent(mNotificationInfo);
-        mNotificationInfo.setGutsParent(guts);
-
-        mNotificationInfo.findViewById(R.id.keep).performClick();
-
-        verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, times(1))
-                .setNotificationsEnabledWithImportanceLockForPackage(
-                        anyString(), eq(TEST_UID), eq(true));
-    }
-
-    @Test
-    public void testCloseControls_nonNullCheckSaveListenerDoesntDelayDismiss()
-            throws Exception {
-        NotificationInfo.CheckSaveListener listener =
-                mock(NotificationInfo.CheckSaveListener.class);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
-                10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
-                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
-                false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */);
-
-        mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
-
-        mTestableLooper.processAllMessages();
-        verify(listener, times(0)).checkSave(any(Runnable.class), eq(mSbn));
-    }
-
-    @Test
-    public void testCloseControls_checkSaveListenerDelaysStopNotifications()
-            throws Exception {
-        NotificationInfo.CheckSaveListener listener =
-                mock(NotificationInfo.CheckSaveListener.class);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
-                10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
-                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
-                false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */);
-
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
-
-        mTestableLooper.processAllMessages();
-        verify(listener).checkSave(any(Runnable.class), eq(mSbn));
-    }
-
-    @Test
-    public void testCloseControls_blockingHelperDismissedIfShown() throws Exception {
-        mNotificationInfo.bindNotification(
-                mMockPackageManager,
-                mMockINotificationManager,
-                TEST_PACKAGE_NAME,
-                mNotificationChannel,
-                1 /* numChannels */,
-                mSbn,
-                null /* checkSaveListener */,
-                null /* onSettingsClick */,
-                null /* onAppSettingsClick */,
-                false /* isNonblockable */,
-                true /* isForBlockingHelper */,
-                false /* isUserSentimentNegative */);
-        NotificationGuts guts = mock(NotificationGuts.class);
-        doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
-        mNotificationInfo.setGutsParent(guts);
-
-        mNotificationInfo.closeControls(mNotificationInfo);
-
-        verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
-    }
-
-    @Test
-    public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        waitForUndoButton();
-
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), any());
-    }
-
-    @Test
-    public void testBlockChangedCallsUpdateNotificationChannel() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        ArgumentCaptor<NotificationChannel> updated =
-                ArgumentCaptor.forClass(NotificationChannel.class);
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), updated.capture());
-        assertTrue((updated.getValue().getUserLockedFields()
-                & USER_LOCKED_IMPORTANCE) != 0);
-        assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance());
-    }
-
-    @Test
-    public void testBlockChangedCallsUpdateNotificationChannel_blockingHelper() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(
-                mMockPackageManager,
-                mMockINotificationManager,
-                TEST_PACKAGE_NAME,
-                mNotificationChannel,
-                1 /* numChannels */,
-                mSbn,
-                null /* checkSaveListener */,
-                null /* onSettingsClick */,
-                null /* onAppSettingsClick */,
-                false /* isNonblockable */,
-                true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */);
-
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        ArgumentCaptor<NotificationChannel> updated =
-                ArgumentCaptor.forClass(NotificationChannel.class);
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), updated.capture());
-        assertTrue((updated.getValue().getUserLockedFields()
-                & USER_LOCKED_IMPORTANCE) != 0);
-        assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance());
-    }
-
-
-    @Test
-    public void testNonBlockableAppDoesNotBecomeMin() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
-        mNotificationInfo.findViewById(R.id.minimize).performClick();
-        waitForUndoButton();
-
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), any());
-    }
-
-    @Test
-    public void testMinChangedCallsUpdateNotificationChannel() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-
-        mNotificationInfo.findViewById(R.id.minimize).performClick();
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        ArgumentCaptor<NotificationChannel> updated =
-                ArgumentCaptor.forClass(NotificationChannel.class);
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), updated.capture());
-        assertTrue((updated.getValue().getUserLockedFields()
-                & USER_LOCKED_IMPORTANCE) != 0);
-        assertEquals(IMPORTANCE_MIN, updated.getValue().getImportance());
-    }
-
-    @Test
-    public void testKeepUpdatesNotificationChannel() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-
-        mNotificationInfo.handleCloseControls(true, false);
-
-        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());
-    }
-
-    @Test
-    public void testBlockUndoDoesNotBlockNotificationChannel() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        waitForUndoButton();
-        mNotificationInfo.findViewById(R.id.undo).performClick();
-        waitForStopButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        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());
-    }
-
-    @Test
-    public void testMinUndoDoesNotMinNotificationChannel() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
-
-        mNotificationInfo.findViewById(R.id.minimize).performClick();
-        waitForUndoButton();
-        mNotificationInfo.findViewById(R.id.undo).performClick();
-        waitForStopButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        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());
-    }
-
-    @Test
-    public void testCloseControlsDoesNotUpdateiMinIfSaveIsFalse() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
-
-        mNotificationInfo.findViewById(R.id.minimize).performClick();
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(false, false);
-
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
-                eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
-    }
-
-    @Test
-    public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
-
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(false, false);
-
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
-                eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
-    }
-
-    @Test
-    public void testCloseControlsDoesNotUpdateIfCheckSaveListenerIsNoOp() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
-                (Runnable saveImportance, StatusBarNotification sbn) -> {
-                }, null, null, true);
-
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
-                eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
-    }
-
-    @Test
-    public void testCloseControlsUpdatesWhenCheckSaveListenerUsesCallback() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
-                (Runnable saveImportance, StatusBarNotification sbn) -> {
-                    saveImportance.run();
-                }, null, null, false);
-
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
-    }
-
-    @Test
-    public void testDisplaySettingsLink() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        final String settingsText = "work chats";
-        final ResolveInfo ri = new ResolveInfo();
-        ri.activityInfo = new ActivityInfo();
-        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
-        ri.activityInfo.name = "something";
-        List<ResolveInfo> ris = new ArrayList<>();
-        ris.add(ri);
-        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
-                .setSettingsText(settingsText).build();
-        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
-                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null,
-                (View v, Intent intent) -> {
-                    latch.countDown();
-                }, false);
-        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
-        assertEquals(View.VISIBLE, settingsLink.getVisibility());
-        settingsLink.performClick();
-        assertEquals(0, latch.getCount());
-    }
-
-    @Test
-    public void testDisplaySettingsLink_multipleChannels() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        final String settingsText = "work chats";
-        final ResolveInfo ri = new ResolveInfo();
-        ri.activityInfo = new ActivityInfo();
-        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
-        ri.activityInfo.name = "something";
-        List<ResolveInfo> ris = new ArrayList<>();
-        ris.add(ri);
-        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
-                .setSettingsText(settingsText).build();
-        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
-                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
-                (View v, Intent intent) -> {
-                    latch.countDown();
-                }, false);
-        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
-        assertEquals(View.VISIBLE, settingsLink.getVisibility());
-        settingsLink.performClick();
-        assertEquals(0, latch.getCount());
-    }
-
-    @Test
-    public void testNoSettingsLink_noHandlingActivity() throws Exception {
-        final String settingsText = "work chats";
-        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(null);
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
-                .setSettingsText(settingsText).build();
-        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
-                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
-                null, false);
-        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
-        assertEquals(GONE, settingsLink.getVisibility());
-    }
-
-    @Test
-    public void testNoSettingsLink_noLinkText() throws Exception {
-        final ResolveInfo ri = new ResolveInfo();
-        ri.activityInfo = new ActivityInfo();
-        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
-        ri.activityInfo.name = "something";
-        List<ResolveInfo> ris = new ArrayList<>();
-        ris.add(ri);
-        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()).build();
-        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
-                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false);
-        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
-        assertEquals(GONE, settingsLink.getVisibility());
-    }
-
-    @Test
-    public void testBindHeader_noSettingsLinkWhenIsForBlockingHelper() throws Exception {
-        final String settingsText = "work chats";
-        final ResolveInfo ri = new ResolveInfo();
-        ri.activityInfo = new ActivityInfo();
-        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
-        ri.activityInfo.name = "something";
-        List<ResolveInfo> ris = new ArrayList<>();
-        ris.add(ri);
-        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
-                .setSettingsText(settingsText).build();
-        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
-                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true,
-                true);
-        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
-        assertEquals(GONE, settingsLink.getVisibility());
-    }
-
-
-    @Test
-    public void testWillBeRemovedReturnsFalseBeforeBind() throws Exception {
-        assertFalse(mNotificationInfo.willBeRemoved());
-    }
-
-    @Test
-    public void testUndoText_min() throws Exception {
-        mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
-
-        mNotificationInfo.findViewById(R.id.minimize).performClick();
-        waitForUndoButton();
-        TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
-        assertTrue(confirmationText.getText().toString().contains("minimized"));
-    }
-
-    @Test
-    public void testUndoText_block() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
-
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        waitForUndoButton();
-        TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
-        assertTrue(confirmationText.getText().toString().contains("won't see"));
-    }
-
-    @Test
-    public void testNoHeaderOnConfirmation() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
-
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        waitForUndoButton();
-        assertEquals(GONE, mNotificationInfo.findViewById(R.id.header).getVisibility());
-    }
-
-    @Test
-    public void testHeaderOnUndo() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
-
-        mNotificationInfo.findViewById(R.id.block).performClick();
-        waitForUndoButton();
-        mNotificationInfo.findViewById(R.id.undo).performClick();
-        waitForStopButton();
-        assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
-    }
-}
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 26f91b3..3cafaf4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -33,6 +33,8 @@
 import android.testing.TestableLooper;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -40,9 +42,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.HashSet;
-import java.util.Set;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
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 2401519..a7758a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -41,6 +41,7 @@
 import android.testing.TestableLooper;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 import com.google.android.collect.Lists;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java
deleted file mode 100644
index 42bf290..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java
+++ /dev/null
@@ -1,165 +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;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-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;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.SysuiTestCase;
-
-import com.google.android.collect.Lists;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class NotificationLoggerTest extends SysuiTestCase {
-    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;
-    @Mock private ExpandableNotificationRow mRow;
-
-    // Dependency mocks:
-    @Mock private NotificationEntryManager mEntryManager;
-    @Mock private NotificationListener mListener;
-
-    private NotificationData.Entry mEntry;
-    private StatusBarNotification mSbn;
-    private TestableNotificationLogger mLogger;
-    private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>();
-
-    @Before
-    public void setUp() throws RemoteException {
-        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,
-                0, new Notification(), UserHandle.CURRENT, null, 0);
-        mEntry = new NotificationData.Entry(mSbn);
-        mEntry.row = mRow;
-
-        mLogger = new TestableNotificationLogger(mBarService);
-        mLogger.setUpWithEntryManager(mEntryManager, mListContainer);
-    }
-
-    @Test
-    public void testOnChildLocationsChangedReportsVisibilityChanged() throws Exception {
-        NotificationVisibility[] newlyVisibleKeys = {
-                NotificationVisibility.obtain(mEntry.key, 0, 1, true)
-        };
-        NotificationVisibility[] noLongerVisibleKeys = {};
-        doAnswer((Answer) invocation -> {
-                    try {
-                        assertArrayEquals(newlyVisibleKeys,
-                                (NotificationVisibility[]) invocation.getArguments()[0]);
-                        assertArrayEquals(noLongerVisibleKeys,
-                                (NotificationVisibility[]) invocation.getArguments()[1]);
-                    } catch (AssertionError error) {
-                        mErrorQueue.offer(error);
-                    }
-                    return null;
-                }
-        ).when(mBarService).onNotificationVisibilityChanged(any(NotificationVisibility[].class),
-                any(NotificationVisibility[].class));
-
-        when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
-        when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry));
-        mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
-        TestableLooper.get(this).processAllMessages();
-        waitForUiOffloadThread();
-
-        if(!mErrorQueue.isEmpty()) {
-            throw mErrorQueue.poll();
-        }
-
-        // |mEntry| won't change visibility, so it shouldn't be reported again:
-        Mockito.reset(mBarService);
-        mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
-        TestableLooper.get(this).processAllMessages();
-        waitForUiOffloadThread();
-
-        verify(mBarService, never()).onNotificationVisibilityChanged(any(), any());
-    }
-
-    @Test
-    public void testStoppingNotificationLoggingReportsCurrentNotifications()
-            throws Exception {
-        when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
-        when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry));
-        mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
-        TestableLooper.get(this).processAllMessages();
-        waitForUiOffloadThread();
-        Mockito.reset(mBarService);
-
-        mLogger.stopNotificationLogging();
-        waitForUiOffloadThread();
-        // The visibility objects are recycled by NotificationLogger, so we can't use specific
-        // matchers here.
-        verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
-    }
-
-    private class TestableNotificationLogger extends NotificationLogger {
-
-        public TestableNotificationLogger(IStatusBarService barService) {
-            mBarService = barService;
-            // Make this on the current thread so we can wait for it during tests.
-            mHandler = Handler.createAsync(Looper.myLooper());
-        }
-
-        public OnChildLocationsChangedListener
-                getChildLocationsChangedListenerForTest() {
-            return mNotificationLocationsChangedListener;
-        }
-
-        public Handler getHandlerForTest() {
-            return mHandler;
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
deleted file mode 100644
index 2a5a1ee..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
+++ /dev/null
@@ -1,90 +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;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.service.notification.StatusBarNotification;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.testing.ViewUtils;
-import android.testing.ViewUtils;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.utils.leaks.LeakCheckedTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
-@SmallTest
-public class NotificationMenuRowTest extends LeakCheckedTest {
-
-    @Before
-    public void setup() {
-        injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
-    }
-
-    @Test
-    public void testAttachDetach() {
-        NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
-        row.createMenu(null, null);
-        ViewUtils.attachView(row.getMenuView());
-        TestableLooper.get(this).processAllMessages();
-        ViewUtils.detachView(row.getMenuView());
-        TestableLooper.get(this).processAllMessages();
-    }
-
-    @Test
-    public void testRecreateMenu() {
-        NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
-        row.createMenu(null, null);
-        assertTrue(row.getMenuView() != null);
-        row.createMenu(null, null);
-        assertTrue(row.getMenuView() != null);
-    }
-
-    @Test
-    public void testResetUncreatedMenu() {
-        NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
-        row.resetMenu();
-    }
-
-    @Test
-    public void testNoAppOpsInSlowSwipe() {
-        NotificationMenuRow row = new NotificationMenuRow(mContext);
-        Notification n = mock(Notification.class);
-        StatusBarNotification sbn = mock(StatusBarNotification.class);
-        when(sbn.getNotification()).thenReturn(n);
-        ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
-        when(parent.getStatusBarNotification()).thenReturn(sbn);
-        row.createMenu(parent, null);
-
-        ViewGroup container = (ViewGroup) row.getMenuView();
-        // one for snooze and one for noti blocking
-        assertEquals(2, container.getChildCount());
-    }
-}
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 7a2cb3a..afe2cf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -19,6 +19,9 @@
 
 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.row.ExpandableNotificationRow;
 import com.google.android.collect.Sets;
 
 import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
deleted file mode 100644
index 756bb1c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
+++ /dev/null
@@ -1,121 +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;
-
-import android.provider.Settings;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableResources;
-import android.testing.UiThreadTest;
-import android.util.KeyValueListParser;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@UiThreadTest
-public class NotificationSnoozeTest extends SysuiTestCase {
-    private static final int RES_DEFAULT = 2;
-    private static final int[] RES_OPTIONS = {1, 2, 3};
-    private NotificationSnooze mNotificationSnooze;
-    private KeyValueListParser mMockParser;
-
-    @Before
-    public void setUp() throws Exception {
-        Settings.Global.putString(mContext.getContentResolver(),
-                Settings.Global.NOTIFICATION_SNOOZE_OPTIONS, null);
-        TestableResources resources = mContext.getOrCreateTestableResources();
-        resources.addOverride(R.integer.config_notification_snooze_time_default, RES_DEFAULT);
-        resources.addOverride(R.array.config_notification_snooze_times, RES_OPTIONS);
-        mNotificationSnooze = new NotificationSnooze(mContext, null);
-        mMockParser = mock(KeyValueListParser.class);
-    }
-
-    @Test
-    public void testGetOptionsWithNoConfig() throws Exception {
-        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
-        assertEquals(3, result.size());
-        assertEquals(1, result.get(0).getMinutesToSnoozeFor());  // respect order
-        assertEquals(2, result.get(1).getMinutesToSnoozeFor());
-        assertEquals(3, result.get(2).getMinutesToSnoozeFor());
-        assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
-    }
-
-    @Test
-    public void testGetOptionsWithInvalidConfig() throws Exception {
-        Settings.Global.putString(mContext.getContentResolver(),
-                Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
-                "this is garbage");
-        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
-        assertEquals(3, result.size());
-        assertEquals(1, result.get(0).getMinutesToSnoozeFor());  // respect order
-        assertEquals(2, result.get(1).getMinutesToSnoozeFor());
-        assertEquals(3, result.get(2).getMinutesToSnoozeFor());
-        assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
-    }
-
-    @Test
-    public void testGetOptionsWithValidDefault() throws Exception {
-        Settings.Global.putString(mContext.getContentResolver(),
-                Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
-                "default=10,options_array=4:5:6:7");
-        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
-        assertNotNull(mNotificationSnooze.getDefaultOption());  // pick one
-    }
-
-    @Test
-    public void testGetOptionsWithValidConfig() throws Exception {
-        Settings.Global.putString(mContext.getContentResolver(),
-                Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
-                "default=6,options_array=4:5:6:7");
-        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
-        assertEquals(4, result.size());
-        assertEquals(4, result.get(0).getMinutesToSnoozeFor());  // respect order
-        assertEquals(5, result.get(1).getMinutesToSnoozeFor());
-        assertEquals(6, result.get(2).getMinutesToSnoozeFor());
-        assertEquals(7, result.get(3).getMinutesToSnoozeFor());
-        assertEquals(6, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
-    }
-
-    @Test
-    public void testGetOptionsWithLongConfig() throws Exception {
-        Settings.Global.putString(mContext.getContentResolver(),
-                Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
-                "default=6,options_array=4:5:6:7:8:9:10:11:12:13:14:15:16:17");
-        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
-        assertTrue(result.size() > 3);
-        assertEquals(4, result.get(0).getMinutesToSnoozeFor());  // respect order
-        assertEquals(5, result.get(1).getMinutesToSnoozeFor());
-        assertEquals(6, result.get(2).getMinutesToSnoozeFor());
-    }
-}
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 c6bcd36..b2170fa 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,9 @@
 import android.widget.RemoteViews;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationInflaterTest;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -43,8 +45,11 @@
  */
 public class NotificationTestHelper {
 
-    static final String PKG = "com.android.systemui";
-    static final int UID = 1000;
+    /** Package name for testing purposes. */
+    public static final String PKG = "com.android.systemui";
+    /** System UI id for testing purposes. */
+    public static final int UID = 1000;
+
     private static final String GROUP_KEY = "gruKey";
 
     private final Context mContext;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
new file mode 100644
index 0000000..db1e049
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.systemui.statusbar;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.support.test.filters.SmallTest;
+
+import com.android.internal.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
+public class NotificationUiAdjustmentTest extends SysuiTestCase {
+
+    @Test
+    public void needReinflate_differentLength() {
+        PendingIntent pendingIntent =
+                PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Notification.Action action =
+                createActionBuilder("first", R.drawable.ic_corp_icon, pendingIntent).build();
+        assertThat(NotificationUiAdjustment.needReinflate(
+                createUiAdjustmentFromSmartActions("first", Collections.emptyList()),
+                createUiAdjustmentFromSmartActions("second", Collections.singletonList(action))))
+                .isTrue();
+    }
+
+    @Test
+    public void needReinflate_differentLabels() {
+        PendingIntent pendingIntent =
+                PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Notification.Action firstAction =
+                createActionBuilder("first", R.drawable.ic_corp_icon, pendingIntent).build();
+        Notification.Action secondAction =
+                createActionBuilder("second", R.drawable.ic_corp_icon, pendingIntent).build();
+
+        assertThat(NotificationUiAdjustment.needReinflate(
+                createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+                createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
+                .isTrue();
+    }
+
+    @Test
+    public void needReinflate_differentIcons() {
+        PendingIntent pendingIntent =
+                PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Notification.Action firstAction =
+                createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent).build();
+        Notification.Action secondAction =
+                createActionBuilder("same", R.drawable.ic_account_circle, pendingIntent)
+                        .build();
+
+        assertThat(NotificationUiAdjustment.needReinflate(
+                createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+                createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
+                .isTrue();
+    }
+
+    @Test
+    public void needReinflate_differentPendingIntent() {
+        PendingIntent firstPendingIntent =
+                PendingIntent.getActivity(mContext, 0, new Intent(Intent.ACTION_VIEW), 0);
+        PendingIntent secondPendingIntent =
+                PendingIntent.getActivity(mContext, 0, new Intent(Intent.ACTION_PROCESS_TEXT), 0);
+        Notification.Action firstAction =
+                createActionBuilder("same", R.drawable.ic_corp_icon, firstPendingIntent)
+                        .build();
+        Notification.Action secondAction =
+                createActionBuilder("same", R.drawable.ic_corp_icon, secondPendingIntent)
+                        .build();
+
+        assertThat(NotificationUiAdjustment.needReinflate(
+                createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+                createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
+                .isTrue();
+    }
+
+    @Test
+    public void needReinflate_differentChoices() {
+        PendingIntent pendingIntent =
+                PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+
+        RemoteInput firstRemoteInput =
+                createRemoteInput("same", "same", new CharSequence[] {"first"});
+        RemoteInput secondRemoteInput =
+                createRemoteInput("same", "same", new CharSequence[] {"second"});
+
+        Notification.Action firstAction =
+                createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent)
+                        .addRemoteInput(firstRemoteInput)
+                        .build();
+        Notification.Action secondAction =
+                createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent)
+                        .addRemoteInput(secondRemoteInput)
+                        .build();
+
+        assertThat(NotificationUiAdjustment.needReinflate(
+                createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+                createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
+                .isTrue();
+    }
+
+    @Test
+    public void needReinflate_differentRemoteInputLabel() {
+        PendingIntent pendingIntent =
+                PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+
+        RemoteInput firstRemoteInput =
+                createRemoteInput("same", "first", new CharSequence[] {"same"});
+        RemoteInput secondRemoteInput =
+                createRemoteInput("same", "second", new CharSequence[] {"same"});
+
+        Notification.Action firstAction =
+                createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent)
+                        .addRemoteInput(firstRemoteInput)
+                        .build();
+        Notification.Action secondAction =
+                createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent)
+                        .addRemoteInput(secondRemoteInput)
+                        .build();
+
+        assertThat(NotificationUiAdjustment.needReinflate(
+                createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+                createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
+                .isTrue();
+    }
+
+    @Test
+    public void needReinflate_negative() {
+        PendingIntent pendingIntent =
+                PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        RemoteInput firstRemoteInput =
+                createRemoteInput("same", "same", new CharSequence[] {"same"});
+        RemoteInput secondRemoteInput =
+                createRemoteInput("same", "same", new CharSequence[] {"same"});
+
+        Notification.Action firstAction =
+                createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent)
+                        .addRemoteInput(firstRemoteInput).build();
+        Notification.Action secondAction =
+                createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent)
+                        .addRemoteInput(secondRemoteInput).build();
+
+        assertThat(NotificationUiAdjustment.needReinflate(
+                createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+                createUiAdjustmentFromSmartActions(
+                        "second", Collections.singletonList(secondAction))))
+                .isFalse();
+    }
+
+    @Test
+    public void needReinflate_differentSmartReplies() {
+        assertThat(NotificationUiAdjustment.needReinflate(
+                createUiAdjustmentFromSmartReplies("first", new CharSequence[]{"a", "b"}),
+                createUiAdjustmentFromSmartReplies("first", new CharSequence[] {"b", "a"})))
+                .isTrue();
+    }
+
+    @Test
+    public void needReinflate_sameSmartReplies() {
+        assertThat(NotificationUiAdjustment.needReinflate(
+                createUiAdjustmentFromSmartReplies("first", new CharSequence[] {"a", "b"}),
+                createUiAdjustmentFromSmartReplies("first", new CharSequence[] {"a", "b"})))
+                .isFalse();
+    }
+
+    private Notification.Action.Builder createActionBuilder(
+            String title, int drawableRes, PendingIntent pendingIntent) {
+        return new Notification.Action.Builder(
+                Icon.createWithResource(mContext, drawableRes), title, pendingIntent);
+    }
+
+    private RemoteInput createRemoteInput(String resultKey, String label, CharSequence[] choices) {
+        return new RemoteInput.Builder(resultKey).setLabel(label).setChoices(choices).build();
+    }
+
+    private NotificationUiAdjustment createUiAdjustmentFromSmartActions(
+            String key, List<Notification.Action> actions) {
+        return new NotificationUiAdjustment(key, actions, new CharSequence[0]);
+    }
+
+    private NotificationUiAdjustment createUiAdjustmentFromSmartReplies(
+            String key, CharSequence[] replies) {
+        return new NotificationUiAdjustment(key, Collections.emptyList(), replies);
+    }
+}
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 0d0d1f86..15c18e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -34,7 +34,13 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+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.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 
 import com.google.android.collect.Lists;
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 e91530d..ada5785 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -33,6 +33,8 @@
 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 org.junit.Before;
 import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
index 00e9995..b7aa21b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
@@ -26,7 +26,7 @@
 import android.widget.FrameLayout;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationTestHelper;
 
 import org.junit.Assert;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java
new file mode 100644
index 0000000..78be783
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.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.systemui.statusbar.notification;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationPresenter;
+
+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)
+@TestableLooper.RunWithLooper
+public class AppOpsListenerTest extends SysuiTestCase {
+    private static final String TEST_PACKAGE_NAME = "test";
+    private static final int TEST_UID = 0;
+
+    @Mock private NotificationPresenter mPresenter;
+    @Mock private AppOpsManager mAppOpsManager;
+
+    // Dependency mocks:
+    @Mock private NotificationEntryManager mEntryManager;
+    @Mock private ForegroundServiceController mFsc;
+
+    private AppOpsListener mListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
+        mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
+        getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
+        when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper()));
+
+        mListener = new AppOpsListener(mContext);
+    }
+
+    @Test
+    public void testOnlyListenForFewOps() {
+        mListener.setUpWithPresenter(mPresenter, mEntryManager);
+
+        verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsListener.OPS, mListener);
+    }
+
+    @Test
+    public void testStopListening() {
+        mListener.destroy();
+        verify(mAppOpsManager, times(1)).stopWatchingActive(mListener);
+    }
+
+    @Test
+    public void testInformEntryMgrOnAppOpsChange() {
+        mListener.setUpWithPresenter(mPresenter, mEntryManager);
+        mListener.onOpActiveChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+        TestableLooper.get(this).processAllMessages();
+        verify(mEntryManager, times(1)).updateNotificationsForAppOp(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+    }
+
+    @Test
+    public void testInformFscOnAppOpsChange() {
+        mListener.setUpWithPresenter(mPresenter, mEntryManager);
+        mListener.onOpActiveChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+        TestableLooper.get(this).processAllMessages();
+        verify(mFsc, times(1)).onAppOpChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
new file mode 100644
index 0000000..de5a8a0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -0,0 +1,460 @@
+/*
+ * 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.notification;
+
+import static android.app.AppOpsManager.OP_ACCEPT_HANDOVER;
+import static android.app.AppOpsManager.OP_CAMERA;
+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 junit.framework.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.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
+import android.media.session.MediaSession;
+import android.os.Bundle;
+import android.service.notification.NotificationListenerService;
+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.RunWithLooper;
+import android.util.ArraySet;
+
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+public class NotificationDataTest 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 static final String TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY = "exempt";
+    private static final NotificationChannel NOTIFICATION_CHANNEL =
+            new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE);
+
+    private final StatusBarNotification mMockStatusBarNotification =
+            mock(StatusBarNotification.class);
+    @Mock
+    ForegroundServiceController mFsc;
+    @Mock
+    NotificationData.Environment mEnvironment;
+
+    private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
+    private NotificationData mNotificationData;
+    private ExpandableNotificationRow mRow;
+
+    @Before
+    public void setUp() throws Exception {
+        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);
+        when(mEnvironment.getGroupManager()).thenReturn(new NotificationGroupManager());
+        when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
+        when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
+        mNotificationData = new TestableNotificationData(mEnvironment);
+        mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
+        mRow = new NotificationTestHelper(getContext()).createRow();
+    }
+
+    @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);
+    }
+
+    @Test
+    public void testAllRelevantNotisTaggedWithAppOps() throws Exception {
+        mNotificationData.add(mRow.getEntry());
+        ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
+        mNotificationData.add(row2.getEntry());
+        ExpandableNotificationRow diffPkg =
+                new NotificationTestHelper(getContext()).createRow("pkg", 4000);
+        mNotificationData.add(diffPkg.getEntry());
+
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        expectedOps.add(OP_ACCEPT_HANDOVER);
+
+        for (int op : expectedOps) {
+            mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
+                    NotificationTestHelper.PKG, mRow.getEntry().key, true);
+            mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
+                    NotificationTestHelper.PKG, row2.getEntry().key, true);
+        }
+        for (int op : expectedOps) {
+            assertTrue(mRow.getEntry().key + " doesn't have op " + op,
+                    mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(op));
+            assertTrue(row2.getEntry().key + " doesn't have op " + op,
+                    mNotificationData.get(row2.getEntry().key).mActiveAppOps.contains(op));
+            assertFalse(diffPkg.getEntry().key + " has op " + op,
+                    mNotificationData.get(diffPkg.getEntry().key).mActiveAppOps.contains(op));
+        }
+    }
+
+    @Test
+    public void testAppOpsRemoval() throws Exception {
+        mNotificationData.add(mRow.getEntry());
+        ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
+        mNotificationData.add(row2.getEntry());
+
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        expectedOps.add(OP_ACCEPT_HANDOVER);
+
+        for (int op : expectedOps) {
+            mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
+                    NotificationTestHelper.PKG, row2.getEntry().key, true);
+        }
+
+        expectedOps.remove(OP_ACCEPT_HANDOVER);
+        mNotificationData.updateAppOp(OP_ACCEPT_HANDOVER, NotificationTestHelper.UID,
+                NotificationTestHelper.PKG, row2.getEntry().key, false);
+
+        assertTrue(mRow.getEntry().key + " doesn't have op " + OP_CAMERA,
+                mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(OP_CAMERA));
+        assertTrue(row2.getEntry().key + " doesn't have op " + OP_CAMERA,
+                mNotificationData.get(row2.getEntry().key).mActiveAppOps.contains(OP_CAMERA));
+        assertFalse(mRow.getEntry().key + " has op " + OP_ACCEPT_HANDOVER,
+                mNotificationData.get(mRow.getEntry().key)
+                        .mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
+        assertFalse(row2.getEntry().key + " has op " + OP_ACCEPT_HANDOVER,
+                mNotificationData.get(row2.getEntry().key)
+                        .mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
+    }
+
+    @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());
+        ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
+        mNotificationData.add(row2.getEntry());
+
+        when(mEnvironment.isNotificationForCurrentProfiles(
+                mRow.getEntry().notification)).thenReturn(false);
+        when(mEnvironment.isNotificationForCurrentProfiles(
+                row2.getEntry().notification)).thenReturn(true);
+        ArrayList<NotificationData.Entry> reuslt =
+                mNotificationData.getNotificationsForCurrentUser();
+
+        assertEquals(reuslt.size(), 1);
+        junit.framework.Assert.assertEquals(reuslt.get(0), row2.getEntry());
+    }
+
+    @Test
+    public void testIsExemptFromDndVisualSuppression_foreground() {
+        initStatusBarNotification(false);
+        when(mMockStatusBarNotification.getKey()).thenReturn(
+                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
+        Notification n = mMockStatusBarNotification.getNotification();
+        n.flags = Notification.FLAG_FOREGROUND_SERVICE;
+        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+
+        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
+        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+    }
+
+    @Test
+    public void testIsExemptFromDndVisualSuppression_media() {
+        initStatusBarNotification(false);
+        when(mMockStatusBarNotification.getKey()).thenReturn(
+                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
+        Notification n = mMockStatusBarNotification.getNotification();
+        Notification.Builder nb = Notification.Builder.recoverBuilder(mContext, n);
+        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);
+
+        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
+        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+    }
+
+    @Test
+    public void testIsExemptFromDndVisualSuppression_system() {
+        initStatusBarNotification(false);
+        when(mMockStatusBarNotification.getKey()).thenReturn(
+                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
+        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        entry.mIsSystemNotification = true;
+
+        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
+        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+    }
+
+    @Test
+    public void testIsNotExemptFromDndVisualSuppression_hiddenCategories() {
+        initStatusBarNotification(false);
+        when(mMockStatusBarNotification.getKey()).thenReturn(
+                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
+        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        entry.mIsSystemNotification = true;
+        when(mMockStatusBarNotification.getNotification()).thenReturn(
+                new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build());
+
+        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+        assertTrue(mNotificationData.shouldSuppressAmbient(entry));
+
+        when(mMockStatusBarNotification.getNotification()).thenReturn(
+                new Notification.Builder(mContext, "").setCategory(CATEGORY_REMINDER).build());
+
+        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+
+        when(mMockStatusBarNotification.getNotification()).thenReturn(
+                new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build());
+
+        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+
+        when(mMockStatusBarNotification.getNotification()).thenReturn(
+                new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build());
+
+        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+
+        when(mMockStatusBarNotification.getNotification()).thenReturn(
+                new Notification.Builder(mContext, "").setCategory(CATEGORY_MESSAGE).build());
+
+        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+    }
+
+    @Test
+    public void testCreateNotificationDataEntry_RankingUpdate() {
+        Ranking ranking = mock(Ranking.class);
+
+        ArrayList<Notification.Action> smartActions = new ArrayList<>();
+        smartActions.add(createAction());
+        when(ranking.getSmartActions()).thenReturn(smartActions);
+
+        when(ranking.getChannel()).thenReturn(NOTIFICATION_CHANNEL);
+
+        when(ranking.getUserSentiment()).thenReturn(Ranking.USER_SENTIMENT_NEGATIVE);
+
+        SnoozeCriterion snoozeCriterion = new SnoozeCriterion("id", "explanation", "confirmation");
+        ArrayList<SnoozeCriterion> snoozeCriterions = new ArrayList<>();
+        snoozeCriterions.add(snoozeCriterion);
+        when(ranking.getSnoozeCriteria()).thenReturn(snoozeCriterions);
+
+        NotificationData.Entry entry =
+                new NotificationData.Entry(mMockStatusBarNotification, ranking);
+
+        assertEquals(smartActions, entry.smartActions);
+        assertEquals(NOTIFICATION_CHANNEL, entry.channel);
+        assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, entry.userSentiment);
+        assertEquals(snoozeCriterions, entry.snoozeCriteria);
+    }
+
+    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);
+    }
+
+    private class TestableNotificationData extends NotificationData {
+        public TestableNotificationData(Environment environment) {
+            super(environment);
+        }
+
+        @Override
+        protected boolean getRanking(String key, Ranking outRanking) {
+            super.getRanking(key, outRanking);
+            if (key.equals(TEST_HIDDEN_NOTIFICATION_KEY)) {
+                outRanking.populate(key, outRanking.getRank(),
+                        outRanking.matchesInterruptionFilter(),
+                        outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
+                        outRanking.getImportance(), outRanking.getImportanceExplanation(),
+                        outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
+                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true,
+                        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, null, null);
+            } else {
+                outRanking.populate(key, outRanking.getRank(),
+                        outRanking.matchesInterruptionFilter(),
+                        outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
+                        outRanking.getImportance(), outRanking.getImportanceExplanation(),
+                        outRanking.getOverrideGroupKey(), NOTIFICATION_CHANNEL, null, null,
+                        outRanking.canShowBadge(), outRanking.getUserSentiment(), false, null,
+                        null);
+            }
+            return true;
+        }
+    }
+
+    private Notification.Action createAction() {
+        return new Notification.Action.Builder(
+                Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
+                "action",
+                PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
+    }
+}
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
new file mode 100644
index 0000000..6543bdb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -0,0 +1,534 @@
+/*
+ * 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.notification;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertFalse;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.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;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.ArraySet;
+import android.widget.FrameLayout;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+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.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+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.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import junit.framework.Assert;
+
+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.Arrays;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationEntryManagerTest extends SysuiTestCase {
+    private static final String TEST_PACKAGE_NAME = "test";
+    private static final int TEST_UID = 0;
+
+    @Mock private NotificationPresenter mPresenter;
+    @Mock private ExpandableNotificationRow mRow;
+    @Mock private NotificationListContainer mListContainer;
+    @Mock private NotificationEntryManager.Callback mCallback;
+    @Mock private HeadsUpManager mHeadsUpManager;
+    @Mock private NotificationListenerService.RankingMap mRankingMap;
+    @Mock private RemoteInputController mRemoteInputController;
+    @Mock private IStatusBarService mBarService;
+
+    // Dependency mocks:
+    @Mock private ForegroundServiceController mForegroundServiceController;
+    @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+    @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;
+
+    private NotificationData.Entry mEntry;
+    private StatusBarNotification mSbn;
+    private TestableNotificationEntryManager mEntryManager;
+    private CountDownLatch mCountDownLatch;
+
+    private class TestableNotificationEntryManager extends NotificationEntryManager {
+        private final CountDownLatch mCountDownLatch;
+
+        public TestableNotificationEntryManager(Context context, IStatusBarService barService) {
+            super(context);
+            mBarService = barService;
+            mCountDownLatch = new CountDownLatch(1);
+            mUseHeadsUp = true;
+        }
+
+        @Override
+        public void onAsyncInflationFinished(NotificationData.Entry entry) {
+            super.onAsyncInflationFinished(entry);
+
+            mCountDownLatch.countDown();
+        }
+
+        public CountDownLatch getCountDownLatch() {
+            return mCountDownLatch;
+        }
+    }
+
+    private void setUserSentiment(String key, int sentiment) {
+        doAnswer(invocationOnMock -> {
+            NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
+                    invocationOnMock.getArguments()[1];
+            ranking.populate(
+                    key,
+                    0,
+                    false,
+                    0,
+                    0,
+                    NotificationManager.IMPORTANCE_DEFAULT,
+                    null, null,
+                    null, null, null, true, sentiment, false, null, null);
+            return true;
+        }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
+    }
+
+    private void setSmartActions(String key, ArrayList<Notification.Action> smartActions) {
+        doAnswer(invocationOnMock -> {
+            NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
+                    invocationOnMock.getArguments()[1];
+            ranking.populate(
+                    key,
+                    0,
+                    false,
+                    0,
+                    0,
+                    NotificationManager.IMPORTANCE_DEFAULT,
+                    null, null,
+                    null, null, null, true,
+                    NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false,
+                    smartActions, null);
+            return true;
+        }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mDependency.injectTestDependency(ForegroundServiceController.class,
+                mForegroundServiceController);
+        mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
+                mLockscreenUserManager);
+        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);
+        mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
+        mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
+        mDependency.injectTestDependency(SmartReplyController.class, mSmartReplyController);
+
+        mCountDownLatch = new CountDownLatch(1);
+
+        when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper()));
+        when(mPresenter.getNotificationLockscreenUserManager()).thenReturn(mLockscreenUserManager);
+        when(mPresenter.getGroupManager()).thenReturn(mGroupManager);
+        when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
+        when(mListContainer.getViewParentForNotification(any())).thenReturn(
+                new FrameLayout(mContext));
+
+        Notification.Builder n = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setContentTitle("Title")
+                .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.expandedIcon = mock(StatusBarIconView.class);
+
+        mEntryManager = new TestableNotificationEntryManager(mContext, mBarService);
+        mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager);
+
+        setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
+    }
+
+    @Test
+    public void testAddNotification() throws Exception {
+        com.android.systemui.util.Assert.isNotMainThread();
+        TestableLooper.get(this).processAllMessages();
+
+        doAnswer(invocation -> {
+            mCountDownLatch.countDown();
+            return null;
+        }).when(mCallback).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.
+        mEntryManager.addNotification(mSbn, mRankingMap);
+        TestableLooper.get(this).processMessages(1);
+        assertTrue(mCountDownLatch.await(10, TimeUnit.SECONDS));
+        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());
+
+        // 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();
+        verify(mRemoteInputManager).bindRow(entry.row);
+
+        // Row content inflation:
+        verify(mCallback).onNotificationAdded(entry);
+        verify(mPresenter).updateNotificationViews();
+
+        assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
+        assertNotNull(entry.row);
+        assertEquals(mEntry.userSentiment,
+                NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
+    }
+
+    @Test
+    public void testUpdateNotification() throws Exception {
+        com.android.systemui.util.Assert.isNotMainThread();
+        TestableLooper.get(this).processAllMessages();
+
+        mEntryManager.getNotificationData().add(mEntry);
+
+        setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+
+        mEntryManager.updateNotification(mSbn, mRankingMap);
+        TestableLooper.get(this).processMessages(1);
+        // Wait for content update.
+        assertTrue(mEntryManager.getCountDownLatch().await(10, TimeUnit.SECONDS));
+
+        verify(mBarService, never()).onNotificationError(any(), any(), anyInt(), anyInt(), anyInt(),
+                any(), anyInt());
+
+        verify(mRemoteInputManager).onUpdateNotification(mEntry);
+        verify(mPresenter).updateNotificationViews();
+        verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
+        verify(mCallback).onNotificationUpdated(mSbn);
+        assertNotNull(mEntry.row);
+        assertEquals(mEntry.userSentiment,
+                NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+    }
+
+    @Test
+    public void testRemoveNotification() throws Exception {
+        com.android.systemui.util.Assert.isNotMainThread();
+
+        mEntry.row = mRow;
+        mEntryManager.getNotificationData().add(mEntry);
+
+        mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
+
+        verify(mBarService, never()).onNotificationError(any(), any(), anyInt(), anyInt(), anyInt(),
+                any(), anyInt());
+
+        verify(mMediaManager).onNotificationRemoved(mSbn.getKey());
+        verify(mRemoteInputManager).onRemoveNotification(mEntry);
+        verify(mSmartReplyController).stopSending(mEntry);
+        verify(mForegroundServiceController).removeNotification(mSbn);
+        verify(mListContainer).cleanUpViewState(mRow);
+        verify(mPresenter).updateNotificationViews();
+        verify(mCallback).onNotificationRemoved(mSbn.getKey(), mSbn);
+        verify(mRow).setRemoved();
+
+        assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
+    }
+
+    @Test
+    public void testRemoveNotification_blockedBySendingSmartReply() throws Exception {
+        com.android.systemui.util.Assert.isNotMainThread();
+
+        mEntry.row = mRow;
+        mEntryManager.getNotificationData().add(mEntry);
+        when(mSmartReplyController.isSendingSmartReply(mEntry.key)).thenReturn(true);
+
+        mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
+
+        assertNotNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
+        assertTrue(mEntryManager.isNotificationKeptForRemoteInput(mEntry.key));
+    }
+
+    @Test
+    public void testUpdateAppOps_foregroundNoti() {
+        com.android.systemui.util.Assert.isNotMainThread();
+
+        when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
+                .thenReturn(mEntry.key);
+        mEntry.row = mRow;
+        mEntryManager.getNotificationData().add(mEntry);
+
+        mEntryManager.updateNotificationsForAppOp(
+                AppOpsManager.OP_CAMERA, mEntry.notification.getUid(),
+                mEntry.notification.getPackageName(), true);
+
+        verify(mPresenter, times(1)).updateNotificationViews();
+        assertTrue(mEntryManager.getNotificationData().get(mEntry.key).mActiveAppOps.contains(
+                AppOpsManager.OP_CAMERA));
+    }
+
+    @Test
+    public void testUpdateAppOps_otherNoti() {
+        com.android.systemui.util.Assert.isNotMainThread();
+
+        when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
+                .thenReturn(null);
+        mEntryManager.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
+
+        verify(mPresenter, never()).updateNotificationViews();
+    }
+
+    @Test
+    public void testAddNotificationExistingAppOps() {
+        mEntry.row = mRow;
+        mEntryManager.getNotificationData().add(mEntry);
+        ArraySet<Integer> expected = new ArraySet<>();
+        expected.add(3);
+        expected.add(235);
+        expected.add(1);
+
+        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
+                mEntry.notification.getPackageName())).thenReturn(expected);
+        when(mForegroundServiceController.getStandardLayoutKey(
+                mEntry.notification.getUserId(),
+                mEntry.notification.getPackageName())).thenReturn(mEntry.key);
+
+        mEntryManager.tagForeground(mEntry.notification);
+
+        Assert.assertEquals(expected.size(), mEntry.mActiveAppOps.size());
+        for (int op : expected) {
+            assertTrue("Entry missing op " + op, mEntry.mActiveAppOps.contains(op));
+        }
+    }
+
+    @Test
+    public void testAdd_noExistingAppOps() {
+        mEntry.row = mRow;
+        mEntryManager.getNotificationData().add(mEntry);
+        when(mForegroundServiceController.getStandardLayoutKey(
+                mEntry.notification.getUserId(),
+                mEntry.notification.getPackageName())).thenReturn(mEntry.key);
+        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
+                mEntry.notification.getPackageName())).thenReturn(null);
+
+        mEntryManager.tagForeground(mEntry.notification);
+        Assert.assertEquals(0, mEntry.mActiveAppOps.size());
+    }
+
+    @Test
+    public void testAdd_existingAppOpsNotForegroundNoti() {
+        mEntry.row = mRow;
+        mEntryManager.getNotificationData().add(mEntry);
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(3);
+        ops.add(235);
+        ops.add(1);
+        when(mForegroundServiceController.getAppOps(mEntry.notification.getUserId(),
+                mEntry.notification.getPackageName())).thenReturn(ops);
+        when(mForegroundServiceController.getStandardLayoutKey(
+                mEntry.notification.getUserId(),
+                mEntry.notification.getPackageName())).thenReturn("something else");
+
+        mEntryManager.tagForeground(mEntry.notification);
+        Assert.assertEquals(0, mEntry.mActiveAppOps.size());
+    }
+
+    @Test
+    public void testRebuildWithRemoteInput_noExistingInputNoSpinner() {
+        StatusBarNotification newSbn =
+                mEntryManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
+        CharSequence[] messages = newSbn.getNotification().extras
+                .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+        Assert.assertEquals(1, messages.length);
+        Assert.assertEquals("A Reply", messages[0]);
+        Assert.assertFalse(newSbn.getNotification().extras
+                .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
+        Assert.assertTrue(newSbn.getNotification().extras
+                .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false));
+    }
+
+    @Test
+    public void testRebuildWithRemoteInput_noExistingInputWithSpinner() {
+        StatusBarNotification newSbn =
+                mEntryManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", true);
+        CharSequence[] messages = newSbn.getNotification().extras
+                .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+        Assert.assertEquals(1, messages.length);
+        Assert.assertEquals("A Reply", messages[0]);
+        Assert.assertTrue(newSbn.getNotification().extras
+                .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
+        Assert.assertTrue(newSbn.getNotification().extras
+                .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false));
+    }
+
+    @Test
+    public void testRebuildWithRemoteInput_withExistingInput() {
+        // Setup a notification entry with 1 remote input.
+        StatusBarNotification newSbn =
+                mEntryManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
+        NotificationData.Entry entry = new NotificationData.Entry(newSbn);
+
+        // Try rebuilding to add another reply.
+        newSbn = mEntryManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true);
+        CharSequence[] messages = newSbn.getNotification().extras
+                .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+        Assert.assertEquals(2, messages.length);
+        Assert.assertEquals("Reply 2", messages[0]);
+        Assert.assertEquals("A Reply", messages[1]);
+    }
+
+    @Test
+    public void testRebuildNotificationForCanceledSmartReplies() {
+        // Try rebuilding to remove spinner and hide buttons.
+        StatusBarNotification newSbn =
+                mEntryManager.rebuildNotificationForCanceledSmartReplies(mEntry);
+        Assert.assertFalse(newSbn.getNotification().extras
+                .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
+        Assert.assertTrue(newSbn.getNotification().extras
+                .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false));
+    }
+
+    @Test
+    public void testUpdateNotificationRanking() {
+        when(mPresenter.isDeviceProvisioned()).thenReturn(true);
+        when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true);
+
+        mEntry.row = mRow;
+        mEntry.setInflationTask(mAsyncInflationTask);
+        mEntryManager.getNotificationData().add(mEntry);
+        setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
+
+        mEntryManager.updateNotificationRanking(mRankingMap);
+        verify(mRow).updateNotification(eq(mEntry));
+        assertEquals(1, mEntry.smartActions.size());
+        assertEquals("action", mEntry.smartActions.get(0).title);
+    }
+
+    @Test
+    public void testUpdateNotificationRanking_noChange() {
+        when(mPresenter.isDeviceProvisioned()).thenReturn(true);
+        when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true);
+
+        mEntry.row = mRow;
+        mEntryManager.getNotificationData().add(mEntry);
+        setSmartActions(mEntry.key, null);
+
+        mEntryManager.updateNotificationRanking(mRankingMap);
+        verify(mRow, never()).updateNotification(eq(mEntry));
+        assertEquals(0, mEntry.smartActions.size());
+    }
+
+    @Test
+    public void testUpdateNotificationRanking_rowNotInflatedYet() {
+        when(mPresenter.isDeviceProvisioned()).thenReturn(true);
+        when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true);
+
+        mEntry.row = null;
+        mEntryManager.getNotificationData().add(mEntry);
+        setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
+
+        mEntryManager.updateNotificationRanking(mRankingMap);
+        verify(mRow, never()).updateNotification(eq(mEntry));
+        assertEquals(1, mEntry.smartActions.size());
+        assertEquals("action", mEntry.smartActions.get(0).title);
+    }
+
+    @Test
+    public void testUpdateNotificationRanking_pendingNotification() {
+        when(mPresenter.isDeviceProvisioned()).thenReturn(true);
+        when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true);
+
+        mEntry.row = null;
+        mEntryManager.mPendingNotifications.put(mEntry.key, mEntry);
+        setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
+
+        mEntryManager.updateNotificationRanking(mRankingMap);
+        verify(mRow, never()).updateNotification(eq(mEntry));
+        assertEquals(1, mEntry.smartActions.size());
+        assertEquals("action", mEntry.smartActions.get(0).title);
+    }
+
+    private Notification.Action createAction() {
+        return new Notification.Action.Builder(
+                Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
+                "action",
+                PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
deleted file mode 100644
index aa8a08c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
+++ /dev/null
@@ -1,287 +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.notification;
-
-import static com.android.systemui.statusbar.notification.NotificationInflater.FLAG_REINFLATE_ALL;
-
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.app.Notification;
-import android.content.Context;
-import android.os.CancellationSignal;
-import android.os.Handler;
-import android.os.Looper;
-import android.service.notification.StatusBarNotification;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.RemoteViews;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationTestHelper;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.HashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
-public class NotificationInflaterTest extends SysuiTestCase {
-
-    private NotificationInflater mNotificationInflater;
-    private Notification.Builder mBuilder;
-    private ExpandableNotificationRow mRow;
-
-    @Before
-    public void setUp() throws Exception {
-        mBuilder = new Notification.Builder(mContext).setSmallIcon(
-                R.drawable.ic_person)
-                .setContentTitle("Title")
-                .setContentText("Text")
-                .setStyle(new Notification.BigTextStyle().bigText("big text"));
-        ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(
-                mBuilder.build());
-        mRow = spy(row);
-        mNotificationInflater = new NotificationInflater(mRow);
-        mNotificationInflater.setInflationCallback(new NotificationInflater.InflationCallback() {
-            @Override
-            public void handleInflationException(StatusBarNotification notification,
-                    Exception e) {
-            }
-
-            @Override
-            public void onAsyncInflationFinished(NotificationData.Entry entry) {
-            }
-        });
-    }
-
-    @Test
-    public void testIncreasedHeadsUpBeingUsed() {
-        mNotificationInflater.setUsesIncreasedHeadsUpHeight(true);
-        Notification.Builder builder = spy(mBuilder);
-        mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
-        verify(builder).createHeadsUpContentView(true);
-    }
-
-    @Test
-    public void testIncreasedHeightBeingUsed() {
-        mNotificationInflater.setUsesIncreasedHeight(true);
-        Notification.Builder builder = spy(mBuilder);
-        mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
-        verify(builder).createContentView(true);
-    }
-
-    @Test
-    public void testInflationCallsUpdated() throws Exception {
-        runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
-                mNotificationInflater);
-        verify(mRow).onNotificationUpdated();
-    }
-
-    @Test
-    public void testInflationCallsOnlyRightMethod() throws Exception {
-        mRow.getPrivateLayout().removeAllViews();
-        mRow.getEntry().cachedBigContentView = null;
-        runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(
-                NotificationInflater.FLAG_REINFLATE_EXPANDED_VIEW), mNotificationInflater);
-        assertTrue(mRow.getPrivateLayout().getChildCount() == 1);
-        assertTrue(mRow.getPrivateLayout().getChildAt(0)
-                == mRow.getPrivateLayout().getExpandedChild());
-        verify(mRow).onNotificationUpdated();
-    }
-
-    @Test
-    public void testInflationThrowsErrorDoesntCallUpdated() throws Exception {
-        mRow.getPrivateLayout().removeAllViews();
-        mRow.getStatusBarNotification().getNotification().contentView
-                = new RemoteViews(mContext.getPackageName(), R.layout.status_bar);
-        runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
-                true /* expectingException */, mNotificationInflater);
-        assertTrue(mRow.getPrivateLayout().getChildCount() == 0);
-        verify(mRow, times(0)).onNotificationUpdated();
-    }
-
-    @Test
-    public void testAsyncTaskRemoved() throws Exception {
-        mRow.getEntry().abortTask();
-        runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
-                mNotificationInflater);
-        verify(mRow).onNotificationUpdated();
-    }
-
-    @Test
-    public void testRemovedNotInflated() throws Exception {
-        mRow.setRemoved();
-        mNotificationInflater.inflateNotificationViews();
-        Assert.assertNull(mRow.getEntry().getRunningTask());
-    }
-
-    @Test
-    @Ignore
-    public void testInflationIsRetriedIfAsyncFails() throws Exception {
-        NotificationInflater.InflationProgress result =
-                new NotificationInflater.InflationProgress();
-        result.packageContext = mContext;
-        CountDownLatch countDownLatch = new CountDownLatch(1);
-        NotificationInflater.applyRemoteView(result,
-                NotificationInflater.FLAG_REINFLATE_EXPANDED_VIEW, 0, mRow,
-                false /* redactAmbient */, true /* isNewView */, new RemoteViews.OnClickHandler(),
-                new NotificationInflater.InflationCallback() {
-                    @Override
-                    public void handleInflationException(StatusBarNotification notification,
-                            Exception e) {
-                        countDownLatch.countDown();
-                        throw new RuntimeException("No Exception expected");
-                    }
-
-                    @Override
-                    public void onAsyncInflationFinished(NotificationData.Entry entry) {
-                        countDownLatch.countDown();
-                    }
-                }, mRow.getEntry(), mRow.getPrivateLayout(), null, null, new HashMap<>(),
-                new NotificationInflater.ApplyCallback() {
-                    @Override
-                    public void setResultView(View v) {
-                    }
-
-                    @Override
-                    public RemoteViews getRemoteView() {
-                        return new AsyncFailRemoteView(mContext.getPackageName(),
-                                R.layout.custom_view_dark);
-                    }
-                });
-        assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
-    }
-
-    /* Cancelling requires us to be on the UI thread otherwise we might have a race */
-    @Test
-    public void testSupersedesExistingTask() throws Exception {
-        mNotificationInflater.inflateNotificationViews();
-        mNotificationInflater.setIsLowPriority(true);
-        mNotificationInflater.setIsChildInGroup(true);
-        InflationTask runningTask = mRow.getEntry().getRunningTask();
-        NotificationInflater.AsyncInflationTask asyncInflationTask =
-                (NotificationInflater.AsyncInflationTask) runningTask;
-        Assert.assertSame("Successive inflations don't inherit the previous flags!",
-                asyncInflationTask.getReInflateFlags(),
-                NotificationInflater.FLAG_REINFLATE_ALL);
-        runningTask.abort();
-    }
-
-    @Test
-    public void doesntReapplyDisallowedRemoteView() throws Exception {
-        mBuilder.setStyle(new Notification.MediaStyle());
-        RemoteViews mediaView = mBuilder.createContentView();
-        mBuilder.setStyle(new Notification.DecoratedCustomViewStyle());
-        mBuilder.setCustomContentView(new RemoteViews(getContext().getPackageName(),
-                R.layout.custom_view_dark));
-        RemoteViews decoratedMediaView = mBuilder.createContentView();
-        Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!",
-                NotificationInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
-    }
-
-    public static void runThenWaitForInflation(Runnable block,
-            NotificationInflater inflater) throws Exception {
-        runThenWaitForInflation(block, false /* expectingException */, inflater);
-    }
-
-    private static void runThenWaitForInflation(Runnable block, boolean expectingException,
-            NotificationInflater inflater) throws Exception {
-        CountDownLatch countDownLatch = new CountDownLatch(1);
-        final ExceptionHolder exceptionHolder = new ExceptionHolder();
-        inflater.setInflationCallback(new NotificationInflater.InflationCallback() {
-            @Override
-            public void handleInflationException(StatusBarNotification notification,
-                    Exception e) {
-                if (!expectingException) {
-                    exceptionHolder.setException(e);
-                }
-                countDownLatch.countDown();
-            }
-
-            @Override
-            public void onAsyncInflationFinished(NotificationData.Entry entry) {
-                if (expectingException) {
-                    exceptionHolder.setException(new RuntimeException(
-                            "Inflation finished even though there should be an error"));
-                }
-                countDownLatch.countDown();
-            }
-
-            @Override
-            public boolean doInflateSynchronous() {
-                return true;
-            }
-        });
-        block.run();
-        assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
-        if (exceptionHolder.mException != null) {
-            throw exceptionHolder.mException;
-        }
-    }
-
-    private static class ExceptionHolder {
-        private Exception mException;
-
-        public void setException(Exception exception) {
-            mException = exception;
-        }
-    }
-
-    private class AsyncFailRemoteView extends RemoteViews {
-        Handler mHandler = Handler.createAsync(Looper.getMainLooper());
-
-        public AsyncFailRemoteView(String packageName, int layoutId) {
-            super(packageName, layoutId);
-        }
-
-        @Override
-        public View apply(Context context, ViewGroup parent) {
-            return super.apply(context, parent);
-        }
-
-        @Override
-        public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
-                OnViewAppliedListener listener, OnClickHandler handler) {
-            mHandler.post(() -> listener.onError(new RuntimeException("Failed to inflate async")));
-            return new CancellationSignal();
-        }
-
-        @Override
-        public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
-                OnViewAppliedListener listener) {
-            return applyAsync(context, parent, executor, listener, null);
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
index 7e2e505..63d1e8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
@@ -25,10 +25,10 @@
 import android.view.View;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
index f0ca3ef..6359234 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
@@ -35,11 +35,10 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.stack.AnimationFilter;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.statusbar.notification.stack.AnimationFilter;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.ViewState;
 
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
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 95ce0d8..ffb1c2d 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
@@ -21,10 +21,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 import org.junit.Before;
 import org.junit.Test;
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
new file mode 100644
index 0000000..ca62c3b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.notification.logging;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+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;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.SysuiTestCase;
+
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.google.android.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class NotificationLoggerTest extends SysuiTestCase {
+    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;
+    @Mock private ExpandableNotificationRow mRow;
+
+    // Dependency mocks:
+    @Mock private NotificationEntryManager mEntryManager;
+    @Mock private NotificationListener mListener;
+
+    private NotificationData.Entry mEntry;
+    private StatusBarNotification mSbn;
+    private TestableNotificationLogger mLogger;
+    private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>();
+
+    @Before
+    public void setUp() throws RemoteException {
+        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,
+                0, new Notification(), UserHandle.CURRENT, null, 0);
+        mEntry = new NotificationData.Entry(mSbn);
+        mEntry.row = mRow;
+
+        mLogger = new TestableNotificationLogger(mBarService);
+        mLogger.setUpWithEntryManager(mEntryManager, mListContainer);
+    }
+
+    @Test
+    public void testOnChildLocationsChangedReportsVisibilityChanged() throws Exception {
+        NotificationVisibility[] newlyVisibleKeys = {
+                NotificationVisibility.obtain(mEntry.key, 0, 1, true)
+        };
+        NotificationVisibility[] noLongerVisibleKeys = {};
+        doAnswer((Answer) invocation -> {
+                    try {
+                        assertArrayEquals(newlyVisibleKeys,
+                                (NotificationVisibility[]) invocation.getArguments()[0]);
+                        assertArrayEquals(noLongerVisibleKeys,
+                                (NotificationVisibility[]) invocation.getArguments()[1]);
+                    } catch (AssertionError error) {
+                        mErrorQueue.offer(error);
+                    }
+                    return null;
+                }
+        ).when(mBarService).onNotificationVisibilityChanged(any(NotificationVisibility[].class),
+                any(NotificationVisibility[].class));
+
+        when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
+        when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry));
+        mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
+        TestableLooper.get(this).processAllMessages();
+        waitForUiOffloadThread();
+
+        if(!mErrorQueue.isEmpty()) {
+            throw mErrorQueue.poll();
+        }
+
+        // |mEntry| won't change visibility, so it shouldn't be reported again:
+        Mockito.reset(mBarService);
+        mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
+        TestableLooper.get(this).processAllMessages();
+        waitForUiOffloadThread();
+
+        verify(mBarService, never()).onNotificationVisibilityChanged(any(), any());
+    }
+
+    @Test
+    public void testStoppingNotificationLoggingReportsCurrentNotifications()
+            throws Exception {
+        when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
+        when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry));
+        mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
+        TestableLooper.get(this).processAllMessages();
+        waitForUiOffloadThread();
+        Mockito.reset(mBarService);
+
+        mLogger.stopNotificationLogging();
+        waitForUiOffloadThread();
+        // The visibility objects are recycled by NotificationLogger, so we can't use specific
+        // matchers here.
+        verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
+    }
+
+    private class TestableNotificationLogger extends NotificationLogger {
+
+        public TestableNotificationLogger(IStatusBarService barService) {
+            mBarService = barService;
+            // Make this on the current thread so we can wait for it during tests.
+            mHandler = Handler.createAsync(Looper.myLooper());
+        }
+
+        public OnChildLocationsChangedListener
+                getChildLocationsChangedListenerForTest() {
+            return mNotificationLocationsChangedListener;
+        }
+
+        public Handler getHandlerForTest() {
+            return mHandler;
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
new file mode 100644
index 0000000..dd5cb58
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
@@ -0,0 +1,220 @@
+/*
+ * 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.row;
+
+import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.UiThreadTest;
+import android.util.ArraySet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@UiThreadTest
+public class AppOpsInfoTest extends SysuiTestCase {
+    private static final String TEST_PACKAGE_NAME = "test_package";
+    private static final int TEST_UID = 1;
+
+    private AppOpsInfo mAppOpsInfo;
+    private final PackageManager mMockPackageManager = mock(PackageManager.class);
+    private final NotificationGuts mGutsParent = mock(NotificationGuts.class);
+    private StatusBarNotification mSbn;
+
+    @Before
+    public void setUp() throws Exception {
+        // Inflate the layout
+        final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+        mAppOpsInfo = (AppOpsInfo) layoutInflater.inflate(R.layout.app_ops_info, null);
+        mAppOpsInfo.setGutsParent(mGutsParent);
+
+        // PackageManager must return a packageInfo and applicationInfo.
+        final PackageInfo packageInfo = new PackageInfo();
+        packageInfo.packageName = TEST_PACKAGE_NAME;
+        when(mMockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt()))
+                .thenReturn(packageInfo);
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.uid = TEST_UID;  // non-zero
+        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
+                applicationInfo);
+
+        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
+                new Notification(), UserHandle.CURRENT, null, 0);
+    }
+
+    @Test
+    public void testBindNotification_SetsTextApplicationName() {
+        when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, new ArraySet<>());
+        final TextView textView = mAppOpsInfo.findViewById(R.id.pkgname);
+        assertTrue(textView.getText().toString().contains("App Name"));
+    }
+
+    @Test
+    public void testBindNotification_SetsPackageIcon() {
+        final Drawable iconDrawable = mock(Drawable.class);
+        when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
+                .thenReturn(iconDrawable);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, new ArraySet<>());
+        final ImageView iconView = mAppOpsInfo.findViewById(R.id.pkgicon);
+        assertEquals(iconDrawable, iconView.getDrawable());
+    }
+
+    @Test
+    public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        final CountDownLatch latch = new CountDownLatch(1);
+        mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid,
+                ArraySet<Integer> ops) -> {
+            assertEquals(TEST_PACKAGE_NAME, pkg);
+            assertEquals(expectedOps, ops);
+            assertEquals(TEST_UID, uid);
+            latch.countDown();
+        }, mSbn, expectedOps);
+
+        final View settingsButton = mAppOpsInfo.findViewById(R.id.settings);
+        settingsButton.performClick();
+        // Verify that listener was triggered.
+        assertEquals(0, latch.getCount());
+    }
+
+    @Test
+    public void testOk() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        final CountDownLatch latch = new CountDownLatch(1);
+        mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid,
+                ArraySet<Integer> ops) -> {
+            assertEquals(TEST_PACKAGE_NAME, pkg);
+            assertEquals(expectedOps, ops);
+            assertEquals(TEST_UID, uid);
+            latch.countDown();
+        }, mSbn, expectedOps);
+
+        final View okButton = mAppOpsInfo.findViewById(R.id.ok);
+        okButton.performClick();
+        assertEquals(1, latch.getCount());
+        verify(mGutsParent, times(1)).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
+    }
+
+    @Test
+    public void testPrompt_camera() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is using the camera.", prompt.getText());
+    }
+
+    @Test
+    public void testPrompt_mic() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_RECORD_AUDIO);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is using the microphone.", prompt.getText());
+    }
+
+    @Test
+    public void testPrompt_overlay() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is displaying over other apps on your screen.", prompt.getText());
+    }
+
+    @Test
+    public void testPrompt_camera_mic() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        expectedOps.add(OP_RECORD_AUDIO);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is using the microphone and camera.", prompt.getText());
+    }
+
+    @Test
+    public void testPrompt_camera_mic_overlay() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        expectedOps.add(OP_RECORD_AUDIO);
+        expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is displaying over other apps on your screen and using"
+                + " the microphone and camera.", prompt.getText());
+    }
+
+    @Test
+    public void testPrompt_camera_overlay() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_CAMERA);
+        expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is displaying over other apps on your screen and using"
+                + " the camera.", prompt.getText());
+    }
+
+    @Test
+    public void testPrompt_mic_overlay() {
+        ArraySet<Integer> expectedOps = new ArraySet<>();
+        expectedOps.add(OP_RECORD_AUDIO);
+        expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
+        mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, expectedOps);
+        TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+        assertEquals("This app is displaying over other apps on your screen and using"
+                + " the microphone.", prompt.getText());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
new file mode 100644
index 0000000..743b307
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -0,0 +1,320 @@
+/*
+ * 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.notification.row;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+
+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.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.app.NotificationChannel;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.util.ArraySet;
+import android.view.NotificationHeaderView;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
+import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+public class ExpandableNotificationRowTest extends SysuiTestCase {
+
+    private ExpandableNotificationRow mGroupRow;
+
+    private NotificationTestHelper mNotificationTestHelper;
+    boolean mHeadsUpAnimatingAway = false;
+
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
+
+    @Before
+    public void setUp() throws Exception {
+        mNotificationTestHelper = new NotificationTestHelper(mContext);
+        mGroupRow = mNotificationTestHelper.createGroup();
+        mGroupRow.setHeadsUpAnimatingAwayListener(
+                animatingAway -> mHeadsUpAnimatingAway = animatingAway);
+        mDependency.injectTestDependency(
+                NotificationBlockingHelperManager.class,
+                mBlockingHelperManager);
+    }
+
+    @Test
+    public void testGroupSummaryNotShowingIconWhenPublic() {
+        mGroupRow.setSensitive(true, true);
+        mGroupRow.setHideSensitiveForIntrinsicHeight(true);
+        assertTrue(mGroupRow.isSummaryWithChildren());
+        assertFalse(mGroupRow.isShowingIcon());
+    }
+
+    @Test
+    public void testNotificationHeaderVisibleWhenAnimating() {
+        mGroupRow.setSensitive(true, true);
+        mGroupRow.setHideSensitive(true, false, 0, 0);
+        mGroupRow.setHideSensitive(false, true, 0, 0);
+        assertTrue(mGroupRow.getChildrenContainer().getVisibleHeader().getVisibility()
+                == View.VISIBLE);
+    }
+
+    @Test
+    public void testUserLockedResetEvenWhenNoChildren() {
+        mGroupRow.setUserLocked(true);
+        mGroupRow.removeAllChildren();
+        mGroupRow.setUserLocked(false);
+        assertFalse("The childrencontainer should not be userlocked but is, the state "
+                + "seems out of sync.", mGroupRow.getChildrenContainer().isUserLocked());
+    }
+
+    @Test
+    public void testReinflatedOnDensityChange() {
+        mGroupRow.setUserLocked(true);
+        mGroupRow.removeAllChildren();
+        mGroupRow.setUserLocked(false);
+        NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
+        mGroupRow.setChildrenContainer(mockContainer);
+        mGroupRow.onDensityOrFontScaleChanged();
+        verify(mockContainer).reInflateViews(any(), any());
+    }
+
+    @Test
+    public void testIconColorShouldBeUpdatedWhenSensitive() throws Exception {
+        ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow());
+        row.setSensitive(true, true);
+        row.setHideSensitive(true, false, 0, 0);
+        verify(row).updateShelfIconColor();
+    }
+
+    @Test
+    public void testIconColorShouldBeUpdatedWhenSettingDark() throws Exception {
+        ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow());
+        row.setDark(true, false, 0);
+        verify(row).updateShelfIconColor();
+    }
+
+    @Test
+    public void testAboveShelfChangedListenerCalled() throws Exception {
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+        AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
+        row.setAboveShelfChangedListener(listener);
+        row.setHeadsUp(true);
+        verify(listener).onAboveShelfStateChanged(true);
+    }
+
+    @Test
+    public void testAboveShelfChangedListenerCalledPinned() throws Exception {
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+        AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
+        row.setAboveShelfChangedListener(listener);
+        row.setPinned(true);
+        verify(listener).onAboveShelfStateChanged(true);
+    }
+
+    @Test
+    public void testAboveShelfChangedListenerCalledHeadsUpGoingAway() throws Exception {
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+        AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
+        row.setAboveShelfChangedListener(listener);
+        row.setHeadsUpAnimatingAway(true);
+        verify(listener).onAboveShelfStateChanged(true);
+    }
+    @Test
+    public void testAboveShelfChangedListenerCalledWhenGoingBelow() throws Exception {
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+        row.setHeadsUp(true);
+        AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
+        row.setAboveShelfChangedListener(listener);
+        row.setAboveShelf(false);
+        verify(listener).onAboveShelfStateChanged(false);
+    }
+
+    @Test
+    public void testClickSound() throws Exception {
+        assertTrue("Should play sounds by default.", mGroupRow.isSoundEffectsEnabled());
+        mGroupRow.setDark(true /* dark */, false /* fade */, 0 /* delay */);
+        mGroupRow.setSecureStateProvider(()-> false);
+        assertFalse("Shouldn't play sounds when dark and trusted.",
+                mGroupRow.isSoundEffectsEnabled());
+        mGroupRow.setSecureStateProvider(()-> true);
+        assertTrue("Should always play sounds when not trusted.",
+                mGroupRow.isSoundEffectsEnabled());
+    }
+
+    @Test
+    public void testSetDismissed_longPressListenerRemoved() {
+        ExpandableNotificationRow.LongPressListener listener =
+                mock(ExpandableNotificationRow.LongPressListener.class);
+        mGroupRow.setLongPressListener(listener);
+        mGroupRow.doLongClickCallback(0,0);
+        verify(listener, times(1)).onLongPress(eq(mGroupRow), eq(0), eq(0),
+                any(NotificationMenuRowPlugin.MenuItem.class));
+        reset(listener);
+
+        mGroupRow.setDismissed(true);
+        mGroupRow.doLongClickCallback(0,0);
+        verify(listener, times(0)).onLongPress(eq(mGroupRow), eq(0), eq(0),
+                any(NotificationMenuRowPlugin.MenuItem.class));
+    }
+
+    @Test
+    public void testShowAppOps_noHeader() {
+        // public notification is custom layout - no header
+        mGroupRow.setSensitive(true, true);
+        mGroupRow.setAppOpsOnClickListener(null);
+        mGroupRow.showAppOpsIcons(null);
+    }
+
+    @Test
+    public void testShowAppOpsIcons_header() {
+        NotificationHeaderView mockHeader = mock(NotificationHeaderView.class);
+
+        NotificationContentView publicLayout = mock(NotificationContentView.class);
+        mGroupRow.setPublicLayout(publicLayout);
+        NotificationContentView privateLayout = mock(NotificationContentView.class);
+        mGroupRow.setPrivateLayout(privateLayout);
+        NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
+        when(mockContainer.getNotificationChildCount()).thenReturn(1);
+        when(mockContainer.getHeaderView()).thenReturn(mockHeader);
+        mGroupRow.setChildrenContainer(mockContainer);
+
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
+        mGroupRow.showAppOpsIcons(ops);
+
+        verify(mockHeader, times(1)).showAppOpsIcons(ops);
+        verify(privateLayout, times(1)).showAppOpsIcons(ops);
+        verify(publicLayout, times(1)).showAppOpsIcons(ops);
+
+    }
+
+    @Test
+    public void testAppOpsOnClick() {
+        ExpandableNotificationRow.OnAppOpsClickListener l = mock(
+                ExpandableNotificationRow.OnAppOpsClickListener.class);
+        View view = mock(View.class);
+
+        mGroupRow.setAppOpsOnClickListener(l);
+
+        mGroupRow.getAppOpsOnClickListener().onClick(view);
+        verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
+    }
+
+    @Test
+    public void testHeadsUpAnimatingAwayListener() {
+        mGroupRow.setHeadsUpAnimatingAway(true);
+        Assert.assertEquals(true, mHeadsUpAnimatingAway);
+        mGroupRow.setHeadsUpAnimatingAway(false);
+        Assert.assertEquals(false, mHeadsUpAnimatingAway);
+    }
+
+    @Test
+    public void testPerformDismissWithBlockingHelper_falseWhenBlockingHelperIsntShown() {
+        when(mBlockingHelperManager.perhapsShowBlockingHelper(
+                eq(mGroupRow), any(NotificationMenuRowPlugin.class))).thenReturn(false);
+
+        assertFalse(
+                mGroupRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
+    }
+
+    @Test
+    public void testPerformDismissWithBlockingHelper_doesntPerformOnGroupSummary() {
+        ExpandableNotificationRow childRow = mGroupRow.getChildrenContainer().getViewAtPosition(0);
+        when(mBlockingHelperManager.perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class)))
+                .thenReturn(true);
+
+        assertTrue(
+                childRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
+
+        verify(mBlockingHelperManager, times(1))
+                .perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class));
+        verify(mBlockingHelperManager, times(0))
+                .perhapsShowBlockingHelper(eq(mGroupRow), any(NotificationMenuRowPlugin.class));
+    }
+
+    @Test
+    public void testIsBlockingHelperShowing_isCorrectlyUpdated() {
+        mGroupRow.setBlockingHelperShowing(true);
+        assertTrue(mGroupRow.isBlockingHelperShowing());
+
+        mGroupRow.setBlockingHelperShowing(false);
+        assertFalse(mGroupRow.isBlockingHelperShowing());
+    }
+
+    @Test
+    public void testGetNumUniqueChildren_defaultChannel() {
+        assertEquals(1, mGroupRow.getNumUniqueChannels());
+    }
+
+    @Test
+    public void testGetNumUniqueChildren_multiChannel() {
+        List<ExpandableNotificationRow> childRows =
+                mGroupRow.getChildrenContainer().getNotificationChildren();
+        // Give each child a unique channel id/name.
+        int i = 0;
+        for (ExpandableNotificationRow childRow : childRows) {
+            childRow.getEntry().channel =
+                    new NotificationChannel("id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT);
+            i++;
+        }
+
+        assertEquals(3, mGroupRow.getNumUniqueChannels());
+    }
+
+    @Test
+    public void testIconScrollXAfterTranslationAndReset() throws Exception {
+        mGroupRow.setTranslation(50);
+        assertEquals(50, -mGroupRow.getEntry().expandedIcon.getScrollX());
+
+        mGroupRow.resetTranslation();
+        assertEquals(0, mGroupRow.getEntry().expandedIcon.getScrollX());
+    }
+
+    @Test
+    public void testIsExpanded_userExpanded() {
+        mGroupRow.setExpandable(true);
+        Assert.assertFalse(mGroupRow.isExpanded());
+        mGroupRow.setUserExpanded(true);
+        Assert.assertTrue(mGroupRow.isExpanded());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
new file mode 100644
index 0000000..aa7889a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.row;
+
+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;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FooterViewTest extends SysuiTestCase {
+
+    FooterView mView;
+
+    @Before
+    public void setUp() {
+        mView = (FooterView) LayoutInflater.from(mContext).inflate(
+                R.layout.status_bar_notification_footer, null, false);
+        mView.setDuration(0);
+    }
+
+    @Test
+    public void testViewsNotNull() {
+        assertNotNull(mView.findContentView());
+        assertNotNull(mView.findSecondaryView());
+    }
+
+    @Test
+    public void setDismissOnClick() {
+        mView.setDismissButtonClickListener(mock(View.OnClickListener.class));
+        assertTrue(mView.findSecondaryView().hasOnClickListeners());
+    }
+
+    @Test
+    public void setManageOnClick() {
+        mView.setManageButtonClickListener(mock(View.OnClickListener.class));
+        assertTrue(mView.findViewById(R.id.manage_text).hasOnClickListeners());
+    }
+
+    @Test
+    public void testPerformVisibilityAnimation() {
+        mView.setVisible(false /* visible */, false /* animate */);
+        assertFalse(mView.isVisible());
+
+        mView.setVisible(true /* visible */, true /* animate */);
+    }
+
+    @Test
+    public void testPerformSecondaryVisibilityAnimation() {
+        mView.setSecondaryVisible(false /* visible */, false /* animate */);
+        assertFalse(mView.isSecondaryVisible());
+
+        mView.setSecondaryVisible(true /* visible */, true /* animate */);
+    }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
new file mode 100644
index 0000000..4efab53
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -0,0 +1,252 @@
+/*
+ * 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.row;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.NotificationTestHelper;
+
+import android.content.Context;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+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.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link NotificationBlockingHelperManager}.
+ */
+@SmallTest
+@FlakyTest
+@org.junit.runner.RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
+
+    private NotificationBlockingHelperManager mBlockingHelperManager;
+
+    private NotificationTestHelper mHelper;
+
+    @Mock private NotificationGutsManager mGutsManager;
+    @Mock private NotificationEntryManager mEntryManager;
+    @Mock private NotificationMenuRow mMenuRow;
+    @Mock private NotificationMenuRowPlugin.MenuItem mMenuItem;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mGutsManager.openGuts(
+                any(View.class),
+                anyInt(),
+                anyInt(),
+                any(NotificationMenuRowPlugin.MenuItem.class)))
+                .thenReturn(true);
+        when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
+        mDependency.injectTestDependency(NotificationGutsManager.class, mGutsManager);
+        mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
+
+        mHelper = new NotificationTestHelper(mContext);
+
+        mBlockingHelperManager = new NotificationBlockingHelperManager(mContext);
+        // By default, have the shade visible/expanded.
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+    }
+
+    @Test
+    public void testDismissCurrentBlockingHelper_nullBlockingHelperRow() {
+        // By default, this shouldn't dismiss (no pointers/vars set up!)
+        assertFalse(mBlockingHelperManager.dismissCurrentBlockingHelper());
+        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
+    }
+
+    @Test
+    public void testDismissCurrentBlockingHelper_withDetachedBlockingHelperRow() throws Exception {
+        ExpandableNotificationRow row = createBlockableRowSpy();
+        row.setBlockingHelperShowing(true);
+        when(row.isAttachedToWindow()).thenReturn(false);
+        mBlockingHelperManager.setBlockingHelperRowForTest(row);
+
+        assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
+        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
+
+        verify(mEntryManager, times(0)).updateNotifications();
+    }
+
+    @Test
+    public void testDismissCurrentBlockingHelper_withAttachedBlockingHelperRow() throws Exception {
+        ExpandableNotificationRow row = createBlockableRowSpy();
+        row.setBlockingHelperShowing(true);
+        when(row.isAttachedToWindow()).thenReturn(true);
+        mBlockingHelperManager.setBlockingHelperRowForTest(row);
+
+        assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
+        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
+
+        verify(mEntryManager).updateNotifications();
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_shown() throws Exception {
+        ExpandableNotificationRow row = createBlockableRowSpy();
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+
+        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+
+        verify(mGutsManager).openGuts(row, 0, 0, mMenuItem);
+    }
+
+
+    @Test
+    public void testPerhapsShowBlockingHelper_shownForLargeGroup() throws Exception {
+        ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(10);
+        groupRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+
+        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(groupRow, mMenuRow));
+
+        verify(mGutsManager).openGuts(groupRow, 0, 0, mMenuItem);
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_shownForOnlyChildNotification()
+            throws Exception {
+        ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(1);
+        // Explicitly get the children container & call getViewAtPosition on it instead of the row
+        // as other factors such as view expansion may cause us to get the parent row back instead
+        // of the child row.
+        ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
+        childRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        assertFalse(childRow.getIsNonblockable());
+
+        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
+
+        verify(mGutsManager).openGuts(childRow, 0, 0, mMenuItem);
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownDueToNeutralUserSentiment() throws Exception {
+        ExpandableNotificationRow row = createBlockableRowSpy();
+        row.getEntry().userSentiment = USER_SENTIMENT_NEUTRAL;
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownDueToPositiveUserSentiment()
+            throws Exception {
+        ExpandableNotificationRow row = createBlockableRowSpy();
+        row.getEntry().userSentiment = USER_SENTIMENT_POSITIVE;
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownDueToShadeVisibility() throws Exception {
+        ExpandableNotificationRow row = createBlockableRowSpy();
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        // Hide the shade
+        mBlockingHelperManager.setNotificationShadeExpanded(0f);
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownDueToNonblockability() throws Exception {
+        ExpandableNotificationRow row = createBlockableRowSpy();
+        when(row.getIsNonblockable()).thenReturn(true);
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownAsNotificationIsInMultipleChildGroup()
+            throws Exception {
+        ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(2);
+        // Explicitly get the children container & call getViewAtPosition on it instead of the row
+        // as other factors such as view expansion may cause us to get the parent row back instead
+        // of the child row.
+        ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
+        childRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
+    }
+
+    @Test
+    public void testBlockingHelperShowAndDismiss() throws Exception{
+        ExpandableNotificationRow row = createBlockableRowSpy();
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        when(row.isAttachedToWindow()).thenReturn(true);
+
+        // Show check
+        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+
+        verify(mGutsManager).openGuts(row, 0, 0, mMenuItem);
+
+        // Dismiss check
+        assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
+        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
+
+        verify(mEntryManager).updateNotifications();
+    }
+
+    @Test
+    public void testNonBlockable_package() {
+        mBlockingHelperManager.setNonBlockablePkgs(new String[] {"banana", "strawberry:pie"});
+
+        assertFalse(mBlockingHelperManager.isNonblockable("orange", "pie"));
+
+        assertTrue(mBlockingHelperManager.isNonblockable("banana", "pie"));
+    }
+
+    @Test
+    public void testNonBlockable_channel() {
+        mBlockingHelperManager.setNonBlockablePkgs(new String[] {"banana", "strawberry:pie"});
+
+        assertFalse(mBlockingHelperManager.isNonblockable("strawberry", "shortcake"));
+
+        assertTrue(mBlockingHelperManager.isNonblockable("strawberry", "pie"));
+    }
+
+    private ExpandableNotificationRow createBlockableRowSpy() throws Exception {
+        ExpandableNotificationRow row = spy(mHelper.createRow());
+        when(row.getIsNonblockable()).thenReturn(false);
+        return row;
+    }
+
+    private ExpandableNotificationRow createBlockableGroupRowSpy(int numChildren) throws Exception {
+        ExpandableNotificationRow row = spy(mHelper.createGroup(numChildren));
+        when(row.getIsNonblockable()).thenReturn(false);
+        return row;
+    }
+}
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
new file mode 100644
index 0000000..c189c95
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.Mockito.doNothing;
+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.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
+import android.view.NotificationHeaderView;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationContentViewTest extends SysuiTestCase {
+
+    NotificationContentView mView;
+
+    @Before
+    @UiThreadTest
+    public void setup() {
+        mView = new NotificationContentView(mContext, null);
+        ExpandableNotificationRow row = new ExpandableNotificationRow(mContext, null);
+        ExpandableNotificationRow mockRow = spy(row);
+        doNothing().when(mockRow).updateBackgroundAlpha(anyFloat());
+        doReturn(10).when(mockRow).getIntrinsicHeight();
+
+        mView.setContainingNotification(mockRow);
+        mView.setHeights(10, 20, 30, 40);
+
+        mView.setContractedChild(createViewWithHeight(10));
+        mView.setExpandedChild(createViewWithHeight(20));
+        mView.setHeadsUpChild(createViewWithHeight(30));
+        mView.setAmbientChild(createViewWithHeight(40));
+
+        mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+        mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+    }
+
+    private View createViewWithHeight(int height) {
+        View view = new View(mContext, null);
+        view.setMinimumHeight(height);
+        return view;
+    }
+
+    @Test
+    @UiThreadTest
+    public void animationStartType_getsClearedAfterUpdatingVisibilitiesWithoutAnimation() {
+        mView.setHeadsUp(true);
+        mView.setDark(true, false, 0);
+        mView.setDark(false, true, 0);
+        mView.setHeadsUpAnimatingAway(true);
+        Assert.assertFalse(mView.isAnimatingVisibleType());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testShowAppOpsIcons() {
+        NotificationHeaderView mockContracted = mock(NotificationHeaderView.class);
+        when(mockContracted.findViewById(com.android.internal.R.id.notification_header))
+                .thenReturn(mockContracted);
+        NotificationHeaderView mockExpanded = mock(NotificationHeaderView.class);
+        when(mockExpanded.findViewById(com.android.internal.R.id.notification_header))
+                .thenReturn(mockExpanded);
+        NotificationHeaderView mockHeadsUp = mock(NotificationHeaderView.class);
+        when(mockHeadsUp.findViewById(com.android.internal.R.id.notification_header))
+                .thenReturn(mockHeadsUp);
+        NotificationHeaderView mockAmbient = mock(NotificationHeaderView.class);
+        when(mockAmbient.findViewById(com.android.internal.R.id.notification_header))
+                .thenReturn(mockAmbient);
+
+        mView.setContractedChild(mockContracted);
+        mView.setExpandedChild(mockExpanded);
+        mView.setHeadsUpChild(mockHeadsUp);
+        mView.setAmbientChild(mockAmbient);
+
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
+        mView.showAppOpsIcons(ops);
+
+        verify(mockContracted, times(1)).showAppOpsIcons(ops);
+        verify(mockExpanded, times(1)).showAppOpsIcons(ops);
+        verify(mockAmbient, never()).showAppOpsIcons(ops);
+        verify(mockHeadsUp, times(1)).showAppOpsIcons(any());
+    }
+}
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
new file mode 100644
index 0000000..e56ccef
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -0,0 +1,348 @@
+/*
+ * 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.row;
+
+import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static junit.framework.Assert.assertNotNull;
+
+import static org.junit.Assert.assertEquals;
+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.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
+
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.ArraySet;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoRule;
+import org.mockito.junit.MockitoJUnit;
+
+/**
+ * Tests for {@link NotificationGutsManager}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class NotificationGutsManagerTest extends SysuiTestCase {
+    private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
+
+    private NotificationChannel mTestNotificationChannel = new NotificationChannel(
+            TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+    private TestableLooper mTestableLooper;
+    private Handler mHandler;
+    private NotificationTestHelper mHelper;
+    private NotificationGutsManager mGutsManager;
+
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock private NotificationPresenter mPresenter;
+    @Mock private NotificationEntryManager mEntryManager;
+    @Mock private NotificationStackScrollLayout mStackScroller;
+    @Mock private NotificationInfo.CheckSaveListener mCheckSaveListener;
+    @Mock private NotificationGutsManager.OnSettingsClickListener mOnSettingsClickListener;
+
+    @Before
+    public void setUp() {
+        mTestableLooper = TestableLooper.get(this);
+        mHandler = Handler.createAsync(mTestableLooper.getLooper());
+
+        mHelper = new NotificationTestHelper(mContext);
+
+        mGutsManager = new NotificationGutsManager(mContext);
+        mGutsManager.setUpWithPresenter(mPresenter, mEntryManager, mStackScroller,
+                mCheckSaveListener, mOnSettingsClickListener);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    // Test methods:
+
+    @Test
+    public void testOpenAndCloseGuts() {
+        NotificationGuts guts = spy(new NotificationGuts(mContext));
+        when(guts.post(any())).thenAnswer(invocation -> {
+            mHandler.post(((Runnable) invocation.getArguments()[0]));
+            return null;
+        });
+
+        // Test doesn't support animation since the guts view is not attached.
+        doNothing().when(guts).openControls(
+                eq(true) /* shouldDoCircularReveal */,
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                any(Runnable.class));
+
+        ExpandableNotificationRow realRow = createTestNotificationRow();
+        NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
+
+        ExpandableNotificationRow row = spy(realRow);
+        when(row.getWindowToken()).thenReturn(new Binder());
+        when(row.getGuts()).thenReturn(guts);
+
+        mGutsManager.openGuts(row, 0, 0, menuItem);
+        assertEquals(View.INVISIBLE, guts.getVisibility());
+        mTestableLooper.processAllMessages();
+        verify(guts).openControls(
+                eq(true),
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                any(Runnable.class));
+
+        assertEquals(View.VISIBLE, guts.getVisibility());
+        mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
+
+        verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+        verify(row, times(1)).setGutsView(any());
+    }
+
+    @Test
+    public void testChangeDensityOrFontScale() {
+        NotificationGuts guts = spy(new NotificationGuts(mContext));
+        when(guts.post(any())).thenAnswer(invocation -> {
+            mHandler.post(((Runnable) invocation.getArguments()[0]));
+            return null;
+        });
+
+        // Test doesn't support animation since the guts view is not attached.
+        doNothing().when(guts).openControls(
+                eq(true) /* shouldDoCircularReveal */,
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                any(Runnable.class));
+
+        ExpandableNotificationRow realRow = createTestNotificationRow();
+        NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
+
+        ExpandableNotificationRow row = spy(realRow);
+        when(row.getWindowToken()).thenReturn(new Binder());
+        when(row.getGuts()).thenReturn(guts);
+        doNothing().when(row).inflateGuts();
+
+        mGutsManager.openGuts(row, 0, 0, menuItem);
+        mTestableLooper.processAllMessages();
+        verify(guts).openControls(
+                eq(true),
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                any(Runnable.class));
+
+        row.onDensityOrFontScaleChanged();
+        mGutsManager.onDensityOrFontScaleChanged(row);
+        mTestableLooper.processAllMessages();
+
+        mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
+
+        verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+        verify(row, times(2)).setGutsView(any());
+    }
+
+    @Test
+    public void testAppOpsSettingsIntent_camera() {
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(OP_CAMERA);
+        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
+        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
+    }
+
+    @Test
+    public void testAppOpsSettingsIntent_mic() {
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(OP_RECORD_AUDIO);
+        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
+        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
+    }
+
+    @Test
+    public void testAppOpsSettingsIntent_camera_mic() {
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(OP_CAMERA);
+        ops.add(OP_RECORD_AUDIO);
+        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
+        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
+    }
+
+    @Test
+    public void testAppOpsSettingsIntent_overlay() {
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(OP_SYSTEM_ALERT_WINDOW);
+        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
+        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        assertEquals(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, captor.getValue().getAction());
+    }
+
+    @Test
+    public void testAppOpsSettingsIntent_camera_mic_overlay() {
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(OP_CAMERA);
+        ops.add(OP_RECORD_AUDIO);
+        ops.add(OP_SYSTEM_ALERT_WINDOW);
+        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
+        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
+    }
+
+    @Test
+    public void testAppOpsSettingsIntent_camera_overlay() {
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(OP_CAMERA);
+        ops.add(OP_SYSTEM_ALERT_WINDOW);
+        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
+        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
+    }
+
+    @Test
+    public void testAppOpsSettingsIntent_mic_overlay() {
+        ArraySet<Integer> ops = new ArraySet<>();
+        ops.add(OP_RECORD_AUDIO);
+        ops.add(OP_SYSTEM_ALERT_WINDOW);
+        mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
+        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
+    }
+
+    @Test
+    public void testInitializeNotificationInfoView_showBlockingHelper() throws Exception {
+        NotificationInfo notificationInfoView = mock(NotificationInfo.class);
+        ExpandableNotificationRow row = spy(mHelper.createRow());
+        row.setBlockingHelperShowing(true);
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        when(row.getIsNonblockable()).thenReturn(false);
+        StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+
+        verify(notificationInfoView).bindNotification(
+                any(PackageManager.class),
+                any(INotificationManager.class),
+                eq(statusBarNotification.getPackageName()),
+                any(NotificationChannel.class),
+                anyInt(),
+                eq(statusBarNotification),
+                any(NotificationInfo.CheckSaveListener.class),
+                any(NotificationInfo.OnSettingsClickListener.class),
+                any(NotificationInfo.OnAppSettingsClickListener.class),
+                eq(false),
+                eq(true) /* isForBlockingHelper */,
+                eq(true) /* isUserSentimentNegative */);
+    }
+
+    @Test
+    public void testInitializeNotificationInfoView_dontShowBlockingHelper() throws Exception {
+        NotificationInfo notificationInfoView = mock(NotificationInfo.class);
+        ExpandableNotificationRow row = spy(mHelper.createRow());
+        row.setBlockingHelperShowing(false);
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        when(row.getIsNonblockable()).thenReturn(false);
+        StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+
+        verify(notificationInfoView).bindNotification(
+                any(PackageManager.class),
+                any(INotificationManager.class),
+                eq(statusBarNotification.getPackageName()),
+                any(NotificationChannel.class),
+                anyInt(),
+                eq(statusBarNotification),
+                any(NotificationInfo.CheckSaveListener.class),
+                any(NotificationInfo.OnSettingsClickListener.class),
+                any(NotificationInfo.OnAppSettingsClickListener.class),
+                eq(false),
+                eq(false) /* isForBlockingHelper */,
+                eq(true) /* isUserSentimentNegative */);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    // Utility methods:
+
+    private ExpandableNotificationRow createTestNotificationRow() {
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                                        .setContentTitle("foo")
+                                        .setColorized(true)
+                                        .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+                                        .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+        try {
+            ExpandableNotificationRow row = mHelper.createRow(nb.build());
+            row.getEntry().channel = mTestNotificationChannel;
+            return row;
+        } catch (Exception e) {
+            fail();
+            return null;
+        }
+    }
+
+    private NotificationMenuRowPlugin.MenuItem createTestMenuItem(ExpandableNotificationRow row) {
+        NotificationMenuRowPlugin menuRow = new NotificationMenuRow(mContext);
+        menuRow.createMenu(row, row.getStatusBarNotification());
+
+        NotificationMenuRowPlugin.MenuItem menuItem = menuRow.getLongpressMenuItem(mContext);
+        assertNotNull(menuItem);
+        return menuItem;
+    }
+}
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
new file mode 100644
index 0000000..81e79d1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
@@ -0,0 +1,287 @@
+/*
+ * 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.notification.row;
+
+
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_REINFLATE_ALL;
+
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_REINFLATE_EXPANDED_VIEW;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Notification;
+import android.content.Context;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RemoteViews;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.NotificationTestHelper;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+public class NotificationInflaterTest extends SysuiTestCase {
+
+    private NotificationInflater mNotificationInflater;
+    private Notification.Builder mBuilder;
+    private ExpandableNotificationRow mRow;
+
+    @Before
+    public void setUp() throws Exception {
+        mBuilder = new Notification.Builder(mContext).setSmallIcon(
+                R.drawable.ic_person)
+                .setContentTitle("Title")
+                .setContentText("Text")
+                .setStyle(new Notification.BigTextStyle().bigText("big text"));
+        ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(
+                mBuilder.build());
+        mRow = spy(row);
+        mNotificationInflater = new NotificationInflater(mRow);
+        mNotificationInflater.setInflationCallback(new NotificationInflater.InflationCallback() {
+            @Override
+            public void handleInflationException(StatusBarNotification notification,
+                    Exception e) {
+            }
+
+            @Override
+            public void onAsyncInflationFinished(NotificationData.Entry entry) {
+            }
+        });
+    }
+
+    @Test
+    public void testIncreasedHeadsUpBeingUsed() {
+        mNotificationInflater.setUsesIncreasedHeadsUpHeight(true);
+        Notification.Builder builder = spy(mBuilder);
+        mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
+        verify(builder).createHeadsUpContentView(true);
+    }
+
+    @Test
+    public void testIncreasedHeightBeingUsed() {
+        mNotificationInflater.setUsesIncreasedHeight(true);
+        Notification.Builder builder = spy(mBuilder);
+        mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
+        verify(builder).createContentView(true);
+    }
+
+    @Test
+    public void testInflationCallsUpdated() throws Exception {
+        runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
+                mNotificationInflater);
+        verify(mRow).onNotificationUpdated();
+    }
+
+    @Test
+    public void testInflationCallsOnlyRightMethod() throws Exception {
+        mRow.getPrivateLayout().removeAllViews();
+        mRow.getEntry().cachedBigContentView = null;
+        runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(
+                FLAG_REINFLATE_EXPANDED_VIEW), mNotificationInflater);
+        assertTrue(mRow.getPrivateLayout().getChildCount() == 1);
+        assertTrue(mRow.getPrivateLayout().getChildAt(0)
+                == mRow.getPrivateLayout().getExpandedChild());
+        verify(mRow).onNotificationUpdated();
+    }
+
+    @Test
+    public void testInflationThrowsErrorDoesntCallUpdated() throws Exception {
+        mRow.getPrivateLayout().removeAllViews();
+        mRow.getStatusBarNotification().getNotification().contentView
+                = new RemoteViews(mContext.getPackageName(), R.layout.status_bar);
+        runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
+                true /* expectingException */, mNotificationInflater);
+        assertTrue(mRow.getPrivateLayout().getChildCount() == 0);
+        verify(mRow, times(0)).onNotificationUpdated();
+    }
+
+    @Test
+    public void testAsyncTaskRemoved() throws Exception {
+        mRow.getEntry().abortTask();
+        runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
+                mNotificationInflater);
+        verify(mRow).onNotificationUpdated();
+    }
+
+    @Test
+    public void testRemovedNotInflated() throws Exception {
+        mRow.setRemoved();
+        mNotificationInflater.inflateNotificationViews();
+        Assert.assertNull(mRow.getEntry().getRunningTask());
+    }
+
+    @Test
+    @Ignore
+    public void testInflationIsRetriedIfAsyncFails() throws Exception {
+        NotificationInflater.InflationProgress result =
+                new NotificationInflater.InflationProgress();
+        result.packageContext = mContext;
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        NotificationInflater.applyRemoteView(result, FLAG_REINFLATE_EXPANDED_VIEW, 0, mRow,
+                false /* redactAmbient */, true /* isNewView */, new RemoteViews.OnClickHandler(),
+                new NotificationInflater.InflationCallback() {
+                    @Override
+                    public void handleInflationException(StatusBarNotification notification,
+                            Exception e) {
+                        countDownLatch.countDown();
+                        throw new RuntimeException("No Exception expected");
+                    }
+
+                    @Override
+                    public void onAsyncInflationFinished(NotificationData.Entry entry) {
+                        countDownLatch.countDown();
+                    }
+                }, mRow.getEntry(), mRow.getPrivateLayout(), null, null, new HashMap<>(),
+                new NotificationInflater.ApplyCallback() {
+                    @Override
+                    public void setResultView(View v) {
+                    }
+
+                    @Override
+                    public RemoteViews getRemoteView() {
+                        return new AsyncFailRemoteView(mContext.getPackageName(),
+                                R.layout.custom_view_dark);
+                    }
+                });
+        assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
+    }
+
+    /* Cancelling requires us to be on the UI thread otherwise we might have a race */
+    @Test
+    public void testSupersedesExistingTask() throws Exception {
+        mNotificationInflater.inflateNotificationViews();
+        mNotificationInflater.setIsLowPriority(true);
+        mNotificationInflater.setIsChildInGroup(true);
+        InflationTask runningTask = mRow.getEntry().getRunningTask();
+        NotificationInflater.AsyncInflationTask asyncInflationTask =
+                (NotificationInflater.AsyncInflationTask) runningTask;
+        Assert.assertSame("Successive inflations don't inherit the previous flags!",
+                asyncInflationTask.getReInflateFlags(),
+                NotificationInflater.FLAG_REINFLATE_ALL);
+        runningTask.abort();
+    }
+
+    @Test
+    public void doesntReapplyDisallowedRemoteView() throws Exception {
+        mBuilder.setStyle(new Notification.MediaStyle());
+        RemoteViews mediaView = mBuilder.createContentView();
+        mBuilder.setStyle(new Notification.DecoratedCustomViewStyle());
+        mBuilder.setCustomContentView(new RemoteViews(getContext().getPackageName(),
+                R.layout.custom_view_dark));
+        RemoteViews decoratedMediaView = mBuilder.createContentView();
+        Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!",
+                NotificationInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
+    }
+
+    public static void runThenWaitForInflation(Runnable block,
+            NotificationInflater inflater) throws Exception {
+        runThenWaitForInflation(block, false /* expectingException */, inflater);
+    }
+
+    private static void runThenWaitForInflation(Runnable block, boolean expectingException,
+            NotificationInflater inflater) throws Exception {
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        final ExceptionHolder exceptionHolder = new ExceptionHolder();
+        inflater.setInflationCallback(new NotificationInflater.InflationCallback() {
+            @Override
+            public void handleInflationException(StatusBarNotification notification,
+                    Exception e) {
+                if (!expectingException) {
+                    exceptionHolder.setException(e);
+                }
+                countDownLatch.countDown();
+            }
+
+            @Override
+            public void onAsyncInflationFinished(NotificationData.Entry entry) {
+                if (expectingException) {
+                    exceptionHolder.setException(new RuntimeException(
+                            "Inflation finished even though there should be an error"));
+                }
+                countDownLatch.countDown();
+            }
+
+            @Override
+            public boolean doInflateSynchronous() {
+                return true;
+            }
+        });
+        block.run();
+        assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
+        if (exceptionHolder.mException != null) {
+            throw exceptionHolder.mException;
+        }
+    }
+
+    private static class ExceptionHolder {
+        private Exception mException;
+
+        public void setException(Exception exception) {
+            mException = exception;
+        }
+    }
+
+    private class AsyncFailRemoteView extends RemoteViews {
+        Handler mHandler = Handler.createAsync(Looper.getMainLooper());
+
+        public AsyncFailRemoteView(String packageName, int layoutId) {
+            super(packageName, layoutId);
+        }
+
+        @Override
+        public View apply(Context context, ViewGroup parent) {
+            return super.apply(context, parent);
+        }
+
+        @Override
+        public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
+                OnViewAppliedListener listener, OnClickHandler handler) {
+            mHandler.post(() -> listener.onError(new RuntimeException("Failed to inflate async")));
+            return new CancellationSignal();
+        }
+
+        @Override
+        public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
+                OnViewAppliedListener listener) {
+            return applyAsync(context, parent, executor, listener, null);
+        }
+    }
+}
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
new file mode 100644
index 0000000..5ce53cf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -0,0 +1,994 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.PollingCheck;
+import android.testing.TestableLooper;
+import android.testing.UiThreadTest;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationInfoTest extends SysuiTestCase {
+    private static final String TEST_PACKAGE_NAME = "test_package";
+    private static final String TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME;
+    private static final int TEST_UID = 1;
+    private static final int MULTIPLE_CHANNEL_COUNT = 2;
+    private static final String TEST_CHANNEL = "test_channel";
+    private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
+
+    private TestableLooper mTestableLooper;
+    private NotificationInfo mNotificationInfo;
+    private NotificationChannel mNotificationChannel;
+    private NotificationChannel mDefaultNotificationChannel;
+    private StatusBarNotification mSbn;
+
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock private MetricsLogger mMetricsLogger;
+    @Mock private INotificationManager mMockINotificationManager;
+    @Mock private PackageManager mMockPackageManager;
+    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
+
+    @Before
+    public void setUp() throws Exception {
+        mDependency.injectTestDependency(
+                NotificationBlockingHelperManager.class,
+                mBlockingHelperManager);
+        mTestableLooper = TestableLooper.get(this);
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
+        mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
+        // Inflate the layout
+        final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+        mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
+                null);
+        mNotificationInfo.setGutsParent(mock(NotificationGuts.class));
+
+        // PackageManager must return a packageInfo and applicationInfo.
+        final PackageInfo packageInfo = new PackageInfo();
+        packageInfo.packageName = TEST_PACKAGE_NAME;
+        when(mMockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt()))
+                .thenReturn(packageInfo);
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.uid = TEST_UID;  // non-zero
+        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
+                applicationInfo);
+        final PackageInfo systemPackageInfo = new PackageInfo();
+        systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME;
+        when(mMockPackageManager.getPackageInfo(eq(TEST_SYSTEM_PACKAGE_NAME), anyInt()))
+                .thenReturn(systemPackageInfo);
+        when(mMockPackageManager.getPackageInfo(eq("android"), anyInt()))
+                .thenReturn(packageInfo);
+
+        // Package has one channel by default.
+        when(mMockINotificationManager.getNumNotificationChannelsForPackage(
+                eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(1);
+
+        // Some test channels.
+        mNotificationChannel = new NotificationChannel(
+                TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
+        mDefaultNotificationChannel = new NotificationChannel(
+                NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
+                IMPORTANCE_LOW);
+        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
+                new Notification(), UserHandle.CURRENT, null, 0);
+    }
+
+    // TODO: if tests are taking too long replace this with something that makes the animation
+    // finish instantly.
+    private void waitForUndoButton() {
+        PollingCheck.waitFor(1000,
+                () -> VISIBLE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility());
+    }
+    private void waitForStopButton() {
+        PollingCheck.waitFor(1000,
+                () -> VISIBLE == mNotificationInfo.findViewById(R.id.prompt).getVisibility());
+    }
+
+    @Test
+    public void testBindNotification_SetsTextApplicationName() throws Exception {
+        when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+        final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
+        assertTrue(textView.getText().toString().contains("App Name"));
+        assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
+    }
+
+    @Test
+    public void testBindNotification_SetsPackageIcon() throws Exception {
+        final Drawable iconDrawable = mock(Drawable.class);
+        when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
+                .thenReturn(iconDrawable);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+        final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
+        assertEquals(iconDrawable, iconView.getDrawable());
+    }
+
+    @Test
+    public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+        final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
+        assertEquals(GONE, groupNameView.getVisibility());
+        final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
+        assertEquals(GONE, groupDividerView.getVisibility());
+    }
+
+    @Test
+    public void testBindNotification_SetsGroupNameIfNonNull() throws Exception {
+        mNotificationChannel.setGroup("test_group_id");
+        final NotificationChannelGroup notificationChannelGroup =
+                new NotificationChannelGroup("test_group_id", "Test Group Name");
+        when(mMockINotificationManager.getNotificationChannelGroupForPackage(
+                eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
+                .thenReturn(notificationChannelGroup);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+        final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
+        assertEquals(View.VISIBLE, groupNameView.getVisibility());
+        assertEquals("Test Group Name", groupNameView.getText());
+        final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
+        assertEquals(View.VISIBLE, groupDividerView.getVisibility());
+    }
+
+    @Test
+    public void testBindNotification_SetsTextChannelName() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+        final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
+        assertEquals(TEST_CHANNEL_NAME, textView.getText());
+    }
+
+    @Test
+    public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, false);
+        final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
+        assertEquals(GONE, textView.getVisibility());
+    }
+
+    @Test
+    public void testBindNotification_DefaultChannelUsesChannelNameIfMoreChannelsExist()
+            throws Exception {
+        // Package has one channel by default.
+        when(mMockINotificationManager.getNumNotificationChannelsForPackage(
+                eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, false);
+        final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
+        assertEquals(VISIBLE, textView.getVisibility());
+    }
+
+    @Test
+    public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
+        final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
+        assertEquals(VISIBLE, textView.getVisibility());
+    }
+
+    @Test
+    public void testBindNotification_BlockButton() throws Exception {
+       mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+        final View block = mNotificationInfo.findViewById(R.id.block);
+        final View minimize = mNotificationInfo.findViewById(R.id.minimize);
+        assertEquals(VISIBLE, block.getVisibility());
+        assertEquals(GONE, minimize.getVisibility());
+    }
+
+    @Test
+    public void testBindNotification_MinButton() throws Exception {
+        mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+        final View block = mNotificationInfo.findViewById(R.id.block);
+        final View minimize = mNotificationInfo.findViewById(R.id.minimize);
+        assertEquals(GONE, block.getVisibility());
+        assertEquals(VISIBLE, minimize.getVisibility());
+    }
+
+    @Test
+    public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
+                (View v, NotificationChannel c, int appUid) -> {
+                    assertEquals(mNotificationChannel, c);
+                    latch.countDown();
+                }, null, false);
+
+        final View settingsButton = mNotificationInfo.findViewById(R.id.info);
+        settingsButton.performClick();
+        // Verify that listener was triggered.
+        assertEquals(0, latch.getCount());
+    }
+
+    @Test
+    public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+        final View settingsButton = mNotificationInfo.findViewById(R.id.info);
+        assertTrue(settingsButton.getVisibility() != View.VISIBLE);
+    }
+
+    @Test
+    public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
+                (View v, NotificationChannel c, int appUid) -> {
+                }, null, false);
+        final View settingsButton = mNotificationInfo.findViewById(R.id.info);
+        assertEquals(View.VISIBLE, settingsButton.getVisibility());
+    }
+
+    @Test
+    public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+        mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
+        verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
+    }
+
+    @Test
+    public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
+                true);
+        mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
+        verify(mMetricsLogger, times(1)).count(anyString(), anyInt());
+    }
+
+    @Test
+    public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null,
+                (View v, NotificationChannel c, int appUid) -> {
+                    assertEquals(null, c);
+                    latch.countDown();
+                }, null, true);
+
+        mNotificationInfo.findViewById(R.id.info).performClick();
+        // Verify that listener was triggered.
+        assertEquals(0, latch.getCount());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels()
+            throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
+                null, true);
+        final TextView channelNameView =
+                mNotificationInfo.findViewById(R.id.channel_name);
+        assertEquals(GONE, channelNameView.getVisibility());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
+                null, true);
+        final TextView blockView = mNotificationInfo.findViewById(R.id.block);
+        assertEquals(GONE, blockView.getVisibility());
+    }
+
+    @Test
+    public void testbindNotification_BlockingHelper() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false,
+                true);
+        final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
+        assertEquals(View.VISIBLE, view.getVisibility());
+        assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
+    }
+
+    @Test
+    public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
+        final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
+        assertEquals(View.VISIBLE, view.getVisibility());
+        assertEquals(mContext.getString(R.string.notification_unblockable_desc),
+                view.getText());
+    }
+
+    @Test
+    public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), any());
+    }
+
+    @Test
+    public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), any());
+    }
+
+    @Test
+    public void testDoesNotUpdateNotificationChannelAfterImportanceChangedMin()
+            throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+
+        mNotificationInfo.findViewById(R.id.minimize).performClick();
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), any());
+    }
+
+    @Test
+    public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged()
+            throws Exception {
+        int originalImportance = mNotificationChannel.getImportance();
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+
+        mNotificationInfo.handleCloseControls(true, false);
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), any());
+        assertEquals(originalImportance, mNotificationChannel.getImportance());
+    }
+
+    @Test
+    public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnspecified()
+            throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), any());
+        assertEquals(IMPORTANCE_UNSPECIFIED, mNotificationChannel.getImportance());
+    }
+
+    @Test
+    public void testHandleCloseControls_setsNotificationsDisabledForMultipleChannelNotifications()
+            throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
+                10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
+                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
+                false /* isNonblockable */);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, times(1))
+                .setNotificationsEnabledWithImportanceLockForPackage(
+                        anyString(), eq(TEST_UID), eq(false));
+    }
+
+
+    @Test
+    public void testHandleCloseControls_keepsNotificationsEnabledForMultipleChannelNotifications()
+            throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
+                10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
+                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
+                false /* isNonblockable */);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, times(1))
+                .setNotificationsEnabledWithImportanceLockForPackage(
+                        anyString(), eq(TEST_UID), eq(false));
+    }
+
+    @Test
+    public void testCloseControls_blockingHelperSavesImportanceForMultipleChannelNotifications()
+            throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
+                10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
+                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
+                false /* isNonblockable */, true /* isForBlockingHelper */,
+                true /* isUserSentimentNegative */);
+
+        NotificationGuts guts = spy(new NotificationGuts(mContext, null));
+        when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
+        doNothing().when(guts).animateClose(anyInt(), anyInt(), anyBoolean());
+        doNothing().when(guts).setExposed(anyBoolean(), anyBoolean());
+        guts.setGutsContent(mNotificationInfo);
+        mNotificationInfo.setGutsParent(guts);
+
+        mNotificationInfo.findViewById(R.id.keep).performClick();
+
+        verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, times(1))
+                .setNotificationsEnabledWithImportanceLockForPackage(
+                        anyString(), eq(TEST_UID), eq(true));
+    }
+
+    @Test
+    public void testCloseControls_nonNullCheckSaveListenerDoesntDelayKeepShowing()
+            throws Exception {
+        NotificationInfo.CheckSaveListener listener =
+                mock(NotificationInfo.CheckSaveListener.class);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
+                10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
+                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
+                false /* isNonblockable */, true /* isForBlockingHelper */,
+                true /* isUserSentimentNegative */);
+
+        NotificationGuts guts = spy(new NotificationGuts(mContext, null));
+        when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
+        doNothing().when(guts).animateClose(anyInt(), anyInt(), anyBoolean());
+        doNothing().when(guts).setExposed(anyBoolean(), anyBoolean());
+        guts.setGutsContent(mNotificationInfo);
+        mNotificationInfo.setGutsParent(guts);
+
+        mNotificationInfo.findViewById(R.id.keep).performClick();
+
+        verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, times(1))
+                .setNotificationsEnabledWithImportanceLockForPackage(
+                        anyString(), eq(TEST_UID), eq(true));
+    }
+
+    @Test
+    public void testCloseControls_nonNullCheckSaveListenerDoesntDelayDismiss()
+            throws Exception {
+        NotificationInfo.CheckSaveListener listener =
+                mock(NotificationInfo.CheckSaveListener.class);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
+                10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
+                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
+                false /* isNonblockable */, true /* isForBlockingHelper */,
+                true /* isUserSentimentNegative */);
+
+        mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
+
+        mTestableLooper.processAllMessages();
+        verify(listener, times(0)).checkSave(any(Runnable.class), eq(mSbn));
+    }
+
+    @Test
+    public void testCloseControls_checkSaveListenerDelaysStopNotifications()
+            throws Exception {
+        NotificationInfo.CheckSaveListener listener =
+                mock(NotificationInfo.CheckSaveListener.class);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
+                10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
+                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
+                false /* isNonblockable */, true /* isForBlockingHelper */,
+                true /* isUserSentimentNegative */);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
+
+        mTestableLooper.processAllMessages();
+        verify(listener).checkSave(any(Runnable.class), eq(mSbn));
+    }
+
+    @Test
+    public void testCloseControls_blockingHelperDismissedIfShown() throws Exception {
+        mNotificationInfo.bindNotification(
+                mMockPackageManager,
+                mMockINotificationManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                1 /* numChannels */,
+                mSbn,
+                null /* checkSaveListener */,
+                null /* onSettingsClick */,
+                null /* onAppSettingsClick */,
+                false /* isNonblockable */,
+                true /* isForBlockingHelper */,
+                false /* isUserSentimentNegative */);
+        NotificationGuts guts = mock(NotificationGuts.class);
+        doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
+        mNotificationInfo.setGutsParent(guts);
+
+        mNotificationInfo.closeControls(mNotificationInfo);
+
+        verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
+    }
+
+    @Test
+    public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), any());
+    }
+
+    @Test
+    public void testBlockChangedCallsUpdateNotificationChannel() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        ArgumentCaptor<NotificationChannel> updated =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), updated.capture());
+        assertTrue((updated.getValue().getUserLockedFields()
+                & USER_LOCKED_IMPORTANCE) != 0);
+        assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance());
+    }
+
+    @Test
+    public void testBlockChangedCallsUpdateNotificationChannel_blockingHelper() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(
+                mMockPackageManager,
+                mMockINotificationManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                1 /* numChannels */,
+                mSbn,
+                null /* checkSaveListener */,
+                null /* onSettingsClick */,
+                null /* onAppSettingsClick */,
+                false /* isNonblockable */,
+                true /* isForBlockingHelper */,
+                true /* isUserSentimentNegative */);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        ArgumentCaptor<NotificationChannel> updated =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), updated.capture());
+        assertTrue((updated.getValue().getUserLockedFields()
+                & USER_LOCKED_IMPORTANCE) != 0);
+        assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance());
+    }
+
+
+    @Test
+    public void testNonBlockableAppDoesNotBecomeMin() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
+        mNotificationInfo.findViewById(R.id.minimize).performClick();
+        waitForUndoButton();
+
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), any());
+    }
+
+    @Test
+    public void testMinChangedCallsUpdateNotificationChannel() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+
+        mNotificationInfo.findViewById(R.id.minimize).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        ArgumentCaptor<NotificationChannel> updated =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), updated.capture());
+        assertTrue((updated.getValue().getUserLockedFields()
+                & USER_LOCKED_IMPORTANCE) != 0);
+        assertEquals(IMPORTANCE_MIN, updated.getValue().getImportance());
+    }
+
+    @Test
+    public void testKeepUpdatesNotificationChannel() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+
+        mNotificationInfo.handleCloseControls(true, false);
+
+        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());
+    }
+
+    @Test
+    public void testBlockUndoDoesNotBlockNotificationChannel() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+        mNotificationInfo.findViewById(R.id.undo).performClick();
+        waitForStopButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        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());
+    }
+
+    @Test
+    public void testMinUndoDoesNotMinNotificationChannel() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+
+        mNotificationInfo.findViewById(R.id.minimize).performClick();
+        waitForUndoButton();
+        mNotificationInfo.findViewById(R.id.undo).performClick();
+        waitForStopButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        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());
+    }
+
+    @Test
+    public void testCloseControlsDoesNotUpdateiMinIfSaveIsFalse() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
+
+        mNotificationInfo.findViewById(R.id.minimize).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(false, false);
+
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+                eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
+    }
+
+    @Test
+    public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(false, false);
+
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+                eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
+    }
+
+    @Test
+    public void testCloseControlsDoesNotUpdateIfCheckSaveListenerIsNoOp() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+                (Runnable saveImportance, StatusBarNotification sbn) -> {
+                }, null, null, true);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+                eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
+    }
+
+    @Test
+    public void testCloseControlsUpdatesWhenCheckSaveListenerUsesCallback() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+                (Runnable saveImportance, StatusBarNotification sbn) -> {
+                    saveImportance.run();
+                }, null, null, false);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
+    }
+
+    @Test
+    public void testDisplaySettingsLink() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final String settingsText = "work chats";
+        final ResolveInfo ri = new ResolveInfo();
+        ri.activityInfo = new ActivityInfo();
+        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+        ri.activityInfo.name = "something";
+        List<ResolveInfo> ris = new ArrayList<>();
+        ris.add(ri);
+        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+                .setSettingsText(settingsText).build();
+        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null,
+                (View v, Intent intent) -> {
+                    latch.countDown();
+                }, false);
+        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+        assertEquals(View.VISIBLE, settingsLink.getVisibility());
+        settingsLink.performClick();
+        assertEquals(0, latch.getCount());
+    }
+
+    @Test
+    public void testDisplaySettingsLink_multipleChannels() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final String settingsText = "work chats";
+        final ResolveInfo ri = new ResolveInfo();
+        ri.activityInfo = new ActivityInfo();
+        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+        ri.activityInfo.name = "something";
+        List<ResolveInfo> ris = new ArrayList<>();
+        ris.add(ri);
+        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+                .setSettingsText(settingsText).build();
+        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
+                (View v, Intent intent) -> {
+                    latch.countDown();
+                }, false);
+        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+        assertEquals(View.VISIBLE, settingsLink.getVisibility());
+        settingsLink.performClick();
+        assertEquals(0, latch.getCount());
+    }
+
+    @Test
+    public void testNoSettingsLink_noHandlingActivity() throws Exception {
+        final String settingsText = "work chats";
+        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(null);
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+                .setSettingsText(settingsText).build();
+        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
+                null, false);
+        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+        assertEquals(GONE, settingsLink.getVisibility());
+    }
+
+    @Test
+    public void testNoSettingsLink_noLinkText() throws Exception {
+        final ResolveInfo ri = new ResolveInfo();
+        ri.activityInfo = new ActivityInfo();
+        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+        ri.activityInfo.name = "something";
+        List<ResolveInfo> ris = new ArrayList<>();
+        ris.add(ri);
+        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()).build();
+        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false);
+        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+        assertEquals(GONE, settingsLink.getVisibility());
+    }
+
+    @Test
+    public void testBindHeader_noSettingsLinkWhenIsForBlockingHelper() throws Exception {
+        final String settingsText = "work chats";
+        final ResolveInfo ri = new ResolveInfo();
+        ri.activityInfo = new ActivityInfo();
+        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+        ri.activityInfo.name = "something";
+        List<ResolveInfo> ris = new ArrayList<>();
+        ris.add(ri);
+        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+                .setSettingsText(settingsText).build();
+        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true,
+                true);
+        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+        assertEquals(GONE, settingsLink.getVisibility());
+    }
+
+
+    @Test
+    public void testWillBeRemovedReturnsFalseBeforeBind() throws Exception {
+        assertFalse(mNotificationInfo.willBeRemoved());
+    }
+
+    @Test
+    public void testUndoText_min() throws Exception {
+        mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
+
+        mNotificationInfo.findViewById(R.id.minimize).performClick();
+        waitForUndoButton();
+        TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
+        assertTrue(confirmationText.getText().toString().contains("minimized"));
+    }
+
+    @Test
+    public void testUndoText_block() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+        TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
+        assertTrue(confirmationText.getText().toString().contains("won't see"));
+    }
+
+    @Test
+    public void testNoHeaderOnConfirmation() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+        assertEquals(GONE, mNotificationInfo.findViewById(R.id.header).getVisibility());
+    }
+
+    @Test
+    public void testHeaderOnUndo() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+        mNotificationInfo.findViewById(R.id.undo).performClick();
+        waitForStopButton();
+        assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
+    }
+}
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
new file mode 100644
index 0000000..06265e5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.notification.row;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.testing.ViewUtils;
+import android.testing.ViewUtils;
+import android.view.ViewGroup;
+
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class NotificationMenuRowTest extends LeakCheckedTest {
+
+    @Before
+    public void setup() {
+        injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
+    }
+
+    @Test
+    public void testAttachDetach() {
+        NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
+        row.createMenu(null, null);
+        ViewUtils.attachView(row.getMenuView());
+        TestableLooper.get(this).processAllMessages();
+        ViewUtils.detachView(row.getMenuView());
+        TestableLooper.get(this).processAllMessages();
+    }
+
+    @Test
+    public void testRecreateMenu() {
+        NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
+        row.createMenu(null, null);
+        assertTrue(row.getMenuView() != null);
+        row.createMenu(null, null);
+        assertTrue(row.getMenuView() != null);
+    }
+
+    @Test
+    public void testResetUncreatedMenu() {
+        NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
+        row.resetMenu();
+    }
+
+    @Test
+    public void testNoAppOpsInSlowSwipe() {
+        NotificationMenuRow row = new NotificationMenuRow(mContext);
+        Notification n = mock(Notification.class);
+        StatusBarNotification sbn = mock(StatusBarNotification.class);
+        when(sbn.getNotification()).thenReturn(n);
+        ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
+        when(parent.getStatusBarNotification()).thenReturn(sbn);
+        row.createMenu(parent, null);
+
+        ViewGroup container = (ViewGroup) row.getMenuView();
+        // one for snooze and one for noti blocking
+        assertEquals(2, container.getChildCount());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
new file mode 100644
index 0000000..f8f7af0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.notification.row;
+
+import android.provider.Settings;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableResources;
+import android.testing.UiThreadTest;
+import android.util.KeyValueListParser;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.mock;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@UiThreadTest
+public class NotificationSnoozeTest extends SysuiTestCase {
+    private static final int RES_DEFAULT = 2;
+    private static final int[] RES_OPTIONS = {1, 2, 3};
+    private NotificationSnooze mNotificationSnooze;
+    private KeyValueListParser mMockParser;
+
+    @Before
+    public void setUp() throws Exception {
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.NOTIFICATION_SNOOZE_OPTIONS, null);
+        TestableResources resources = mContext.getOrCreateTestableResources();
+        resources.addOverride(R.integer.config_notification_snooze_time_default, RES_DEFAULT);
+        resources.addOverride(R.array.config_notification_snooze_times, RES_OPTIONS);
+        mNotificationSnooze = new NotificationSnooze(mContext, null);
+        mMockParser = mock(KeyValueListParser.class);
+    }
+
+    @Test
+    public void testGetOptionsWithNoConfig() throws Exception {
+        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+        assertEquals(3, result.size());
+        assertEquals(1, result.get(0).getMinutesToSnoozeFor());  // respect order
+        assertEquals(2, result.get(1).getMinutesToSnoozeFor());
+        assertEquals(3, result.get(2).getMinutesToSnoozeFor());
+        assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+    }
+
+    @Test
+    public void testGetOptionsWithInvalidConfig() throws Exception {
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
+                "this is garbage");
+        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+        assertEquals(3, result.size());
+        assertEquals(1, result.get(0).getMinutesToSnoozeFor());  // respect order
+        assertEquals(2, result.get(1).getMinutesToSnoozeFor());
+        assertEquals(3, result.get(2).getMinutesToSnoozeFor());
+        assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+    }
+
+    @Test
+    public void testGetOptionsWithValidDefault() throws Exception {
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
+                "default=10,options_array=4:5:6:7");
+        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+        assertNotNull(mNotificationSnooze.getDefaultOption());  // pick one
+    }
+
+    @Test
+    public void testGetOptionsWithValidConfig() throws Exception {
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
+                "default=6,options_array=4:5:6:7");
+        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+        assertEquals(4, result.size());
+        assertEquals(4, result.get(0).getMinutesToSnoozeFor());  // respect order
+        assertEquals(5, result.get(1).getMinutesToSnoozeFor());
+        assertEquals(6, result.get(2).getMinutesToSnoozeFor());
+        assertEquals(7, result.get(3).getMinutesToSnoozeFor());
+        assertEquals(6, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+    }
+
+    @Test
+    public void testGetOptionsWithLongConfig() throws Exception {
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
+                "default=6,options_array=4:5:6:7:8:9:10:11:12:13:14:15:16:17");
+        ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+        assertTrue(result.size() > 3);
+        assertEquals(4, result.get(0).getMinutesToSnoozeFor());  // respect order
+        assertEquals(5, result.get(1).getMinutesToSnoozeFor());
+        assertEquals(6, result.get(2).getMinutesToSnoozeFor());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
new file mode 100644
index 0000000..4b94a25
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.notification.row.wrapper;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.widget.RemoteViews;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+public class NotificationCustomViewWrapperTest extends SysuiTestCase {
+
+    private ExpandableNotificationRow mRow;
+
+    @Before
+    public void setUp() throws Exception {
+        mRow = new NotificationTestHelper(mContext).createRow();
+    }
+
+    @Test
+    public void testBackgroundPersists() {
+        RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.custom_view_dark);
+        View v = views.apply(mContext, null);
+        NotificationViewWrapper wrap = NotificationCustomViewWrapper.wrap(mContext, v, mRow);
+        wrap.onContentUpdated(mRow);
+        Assert.assertTrue("No background set, when applying custom background view",
+                wrap.getCustomBackgroundColor() != 0);
+        views.reapply(mContext, v);
+        wrap.onReinflated();
+        wrap.onContentUpdated(mRow);
+        Assert.assertTrue("Reapplying a custom remote view lost it's background!",
+                wrap.getCustomBackgroundColor() != 0);
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
new file mode 100644
index 0000000..087aa59
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.notification.stack;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.NotificationHeaderView;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationTestHelper;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+public class NotificationChildrenContainerTest extends SysuiTestCase {
+
+    private ExpandableNotificationRow mGroup;
+    private int mId;
+    private NotificationTestHelper mNotificationTestHelper;
+    private NotificationChildrenContainer mChildrenContainer;
+
+    @Before
+    public void setUp() throws Exception {
+        mNotificationTestHelper = new NotificationTestHelper(mContext);
+        mGroup = mNotificationTestHelper.createGroup();
+        mChildrenContainer = mGroup.getChildrenContainer();
+    }
+
+    @Test
+    public void testGetMaxAllowedVisibleChildren_ambient() {
+        mGroup.setShowAmbient(true);
+        Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+            NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_AMBIENT);
+    }
+
+    @Test
+    public void testGetMaxAllowedVisibleChildren_lowPriority() {
+        mChildrenContainer.setIsLowPriority(true);
+        Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+            NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+    }
+
+    @Test
+    public void testGetMaxAllowedVisibleChildren_headsUp() {
+        mGroup.setHeadsUp(true);
+        Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+                NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+    }
+
+    @Test
+    public void testGetMaxAllowedVisibleChildren_lowPriority_expandedChildren() {
+        mChildrenContainer.setIsLowPriority(true);
+        mChildrenContainer.setChildrenExpanded(true);
+        Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+            NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+    }
+
+    @Test
+    public void testGetMaxAllowedVisibleChildren_lowPriority_userLocked() {
+        mChildrenContainer.setIsLowPriority(true);
+        mChildrenContainer.setUserLocked(true);
+        Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+            NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+    }
+
+    @Test
+    public void testGetMaxAllowedVisibleChildren_likeCollapsed() {
+        Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(true),
+            NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_COLLAPSED);
+    }
+
+
+    @Test
+    public void testGetMaxAllowedVisibleChildren_expandedChildren() {
+        mChildrenContainer.setChildrenExpanded(true);
+        Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+                NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+    }
+
+    @Test
+    public void testGetMaxAllowedVisibleChildren_userLocked() {
+        mGroup.setUserLocked(true);
+        Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+                NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+    }
+
+    @Test
+    public void testShowingAsLowPriority_lowPriority() {
+        mChildrenContainer.setIsLowPriority(true);
+        Assert.assertTrue(mChildrenContainer.showingAsLowPriority());
+    }
+
+    @Test
+    public void testShowingAsLowPriority_notLowPriority() {
+        Assert.assertFalse(mChildrenContainer.showingAsLowPriority());
+    }
+
+    @Test
+    public void testShowingAsLowPriority_lowPriority_expanded() {
+        mChildrenContainer.setIsLowPriority(true);
+        mGroup.setExpandable(true);
+        mGroup.setUserExpanded(true, false);
+        Assert.assertFalse(mChildrenContainer.showingAsLowPriority());
+    }
+
+    @Test
+    public void testGetMaxAllowedVisibleChildren_userLocked_expandedChildren_lowPriority() {
+        mGroup.setUserLocked(true);
+        mGroup.setExpandable(true);
+        mGroup.setUserExpanded(true);
+        mChildrenContainer.setIsLowPriority(true);
+        Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+                NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+    }
+
+    @Test
+    public void testLowPriorityHeaderCleared() {
+        mGroup.setIsLowPriority(true);
+        NotificationHeaderView lowPriorityHeaderView = mChildrenContainer.getLowPriorityHeaderView();
+        Assert.assertTrue(lowPriorityHeaderView.getVisibility() == View.VISIBLE);
+        Assert.assertTrue(lowPriorityHeaderView.getParent() == mChildrenContainer);
+        mGroup.setIsLowPriority(false);
+        Assert.assertTrue(lowPriorityHeaderView.getParent() == null);
+        Assert.assertTrue(mChildrenContainer.getLowPriorityHeaderView() == null);
+    }
+
+    @Test
+    public void testRecreateNotificationHeader_hasHeader() {
+        mChildrenContainer.recreateNotificationHeader(null);
+        Assert.assertNotNull("Children container must have a header after recreation",
+                mChildrenContainer.getCurrentHeaderView());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
new file mode 100644
index 0000000..f2431b4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.stack;
+
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationTestHelper;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+public class NotificationRoundnessManagerTest extends SysuiTestCase {
+
+    private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
+    private HashSet<View> mAnimatedChildren = new HashSet<>();
+    private Runnable mRoundnessCallback = mock(Runnable.class);
+    private ExpandableNotificationRow mFirst;
+    private ExpandableNotificationRow mSecond;
+
+    @Before
+    public void setUp() throws Exception {
+        NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
+        mFirst = testHelper.createRow();
+        mFirst.setHeadsUpAnimatingAwayListener(animatingAway
+                -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mFirst, animatingAway));
+        mSecond = testHelper.createRow();
+        mSecond.setHeadsUpAnimatingAwayListener(animatingAway
+                -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mSecond, animatingAway));
+        mRoundnessManager.setOnRoundingChangedCallback(mRoundnessCallback);
+        mRoundnessManager.setAnimatedChildren(mAnimatedChildren);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mFirst);
+        mRoundnessManager.setExpanded(1.0f, 1.0f);
+    }
+
+    @Test
+    public void testCallbackCalledWhenSecondChanged() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mSecond);
+        verify(mRoundnessCallback, atLeast(1)).run();
+    }
+
+    @Test
+    public void testCallbackCalledWhenFirstChanged() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mFirst);
+        verify(mRoundnessCallback, atLeast(1)).run();
+    }
+
+    @Test
+    public void testRoundnessSetOnLast() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mSecond);
+        Assert.assertEquals(1.0f, mSecond.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mSecond.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundnessSetOnNew() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, null);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testCompleteReplacement() {
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testNotCalledWhenRemoved() {
+        mFirst.setRemoved();
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundedWhenPinnedAndCollapsed() {
+        mFirst.setPinned(true);
+        mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundedWhenGoingAwayAndCollapsed() {
+        mFirst.setHeadsUpAnimatingAway(true);
+        mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundedNormalRoundingWhenExpanded() {
+        mFirst.setHeadsUpAnimatingAway(true);
+        mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.0f /* appearFraction */);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testTrackingHeadsUpRoundedIfPushingUp() {
+        mRoundnessManager.setExpanded(1.0f /* expandedHeight */, -0.5f /* appearFraction */);
+        mRoundnessManager.setTrackingHeadsUp(mFirst);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testTrackingHeadsUpNotRoundedIfPushingDown() {
+        mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.5f /* appearFraction */);
+        mRoundnessManager.setTrackingHeadsUp(mFirst);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+    @Test
+    public void testRoundingUpdatedWhenAnimatingAwayTrue() {
+        mRoundnessManager.setExpanded(0.0f, 0.0f);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        mFirst.setHeadsUpAnimatingAway(true);
+        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+
+
+    @Test
+    public void testRoundingUpdatedWhenAnimatingAwayFalse() {
+        mRoundnessManager.setExpanded(0.0f, 0.0f);
+        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+        mFirst.setHeadsUpAnimatingAway(true);
+        mFirst.setHeadsUpAnimatingAway(false);
+        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+    }
+}
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
new file mode 100644
index 0000000..8fb2447
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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.notification.stack;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.systemui.ExpandHelper;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.notification.row.FooterView;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Tests for {@link NotificationStackScrollLayout}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationStackScrollLayoutTest extends SysuiTestCase {
+
+    private NotificationStackScrollLayout mStackScroller;
+
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock private StatusBar mBar;
+    @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
+    @Mock private NotificationGroupManager mGroupManager;
+    @Mock private ExpandHelper mExpandHelper;
+    @Mock private EmptyShadeView mEmptyShadeView;
+
+    @Before
+    @UiThreadTest
+    public void setUp() throws Exception {
+        // Inject dependencies before initializing the layout
+        mDependency.injectTestDependency(
+                NotificationBlockingHelperManager.class,
+                mBlockingHelperManager);
+
+        NotificationShelf notificationShelf = spy(new NotificationShelf(getContext(), null));
+        mStackScroller = new NotificationStackScrollLayout(getContext());
+        mStackScroller.setShelf(notificationShelf);
+        mStackScroller.setStatusBar(mBar);
+        mStackScroller.setScrimController(mock(ScrimController.class));
+        mStackScroller.setHeadsUpManager(mHeadsUpManager);
+        mStackScroller.setGroupManager(mGroupManager);
+        mStackScroller.setEmptyShadeView(mEmptyShadeView);
+
+        // Stub out functionality that isn't necessary to test.
+        doNothing().when(mBar)
+                .executeRunnableDismissingKeyguard(any(Runnable.class),
+                        any(Runnable.class),
+                        anyBoolean(),
+                        anyBoolean(),
+                        anyBoolean());
+        doNothing().when(mGroupManager).collapseAllGroups();
+        doNothing().when(mExpandHelper).cancelImmediately();
+        doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
+        doNothing().when(notificationShelf).fadeInTranslating();
+    }
+
+    @Test
+    public void testNotDimmedOnKeyguard() {
+        when(mBar.getBarState()).thenReturn(StatusBarState.SHADE);
+        mStackScroller.setDimmed(true /* dimmed */, false /* animate */);
+        mStackScroller.setDimmed(true /* dimmed */, true /* animate */);
+        Assert.assertFalse(mStackScroller.isDimmed());
+    }
+
+    @Test
+    public void testAntiBurnInOffset() {
+        final int burnInOffset = 30;
+        mStackScroller.setAntiBurnInOffsetX(burnInOffset);
+        mStackScroller.setDark(false /* dark */, false /* animated */, null /* touch */);
+        Assert.assertEquals(0 /* expected */, mStackScroller.getTranslationX(), 0.01 /* delta */);
+        mStackScroller.setDark(true /* dark */, false /* animated */, null /* touch */);
+        Assert.assertEquals(burnInOffset /* expected */, mStackScroller.getTranslationX(),
+                0.01 /* delta */);
+    }
+
+    @Test
+    public void updateEmptyView_dndSuppressing() {
+        when(mEmptyShadeView.willBeGone()).thenReturn(true);
+        when(mBar.areNotificationsHidden()).thenReturn(true);
+
+        mStackScroller.updateEmptyShadeView(true);
+
+        verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
+    }
+
+    @Test
+    public void updateEmptyView_dndNotSuppressing() {
+        mStackScroller.setEmptyShadeView(mEmptyShadeView);
+        when(mEmptyShadeView.willBeGone()).thenReturn(true);
+        when(mBar.areNotificationsHidden()).thenReturn(false);
+
+        mStackScroller.updateEmptyShadeView(true);
+
+        verify(mEmptyShadeView).setText(R.string.empty_shade_text);
+    }
+
+    @Test
+    public void updateEmptyView_noNotificationsToDndSuppressing() {
+        mStackScroller.setEmptyShadeView(mEmptyShadeView);
+        when(mEmptyShadeView.willBeGone()).thenReturn(true);
+        when(mBar.areNotificationsHidden()).thenReturn(false);
+        mStackScroller.updateEmptyShadeView(true);
+        verify(mEmptyShadeView).setText(R.string.empty_shade_text);
+
+        when(mBar.areNotificationsHidden()).thenReturn(true);
+        mStackScroller.updateEmptyShadeView(true);
+        verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
+    }
+
+    @Test
+    @UiThreadTest
+    public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
+        mStackScroller.setExpandedHeight(0f);
+        verify(mBlockingHelperManager).setNotificationShadeExpanded(0f);
+        reset(mBlockingHelperManager);
+
+        mStackScroller.setExpandedHeight(100f);
+        verify(mBlockingHelperManager).setNotificationShadeExpanded(100f);
+    }
+
+    @Test
+    public void manageNotifications_visible() {
+        FooterView view = mock(FooterView.class);
+        mStackScroller.setFooterView(view);
+        when(view.willBeGone()).thenReturn(true);
+
+        mStackScroller.updateFooterView(true, false);
+
+        verify(view).setVisible(eq(true), anyBoolean());
+        verify(view).setSecondaryVisible(eq(false), anyBoolean());
+    }
+
+    @Test
+    public void clearAll_visible() {
+        FooterView view = mock(FooterView.class);
+        mStackScroller.setFooterView(view);
+        when(view.willBeGone()).thenReturn(true);
+
+        mStackScroller.updateFooterView(true, true);
+
+        verify(view).setVisible(eq(true), anyBoolean());
+        verify(view).setSecondaryVisible(eq(true), anyBoolean());
+    }
+}
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 537bfd4..a4004ae 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
@@ -17,8 +17,6 @@
 package com.android.systemui.statusbar.phone;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyObject;
-import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -31,23 +29,18 @@
 import android.view.View;
 import android.widget.TextView;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.TestableDependency;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.HashSet;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
@@ -123,6 +116,27 @@
     }
 
     @Test
+    public void testHeaderReadFromOldController() {
+        mHeadsUpAppearanceController.setExpandedHeight(1.0f, 1.0f);
+
+        HeadsUpAppearanceController newController = new HeadsUpAppearanceController(
+                mock(NotificationIconAreaController.class),
+                mHeadsUpManager,
+                mHeadsUpStatusBarView,
+                mStackScroller,
+                mPanelView,
+                new View(mContext));
+        newController.readFrom(mHeadsUpAppearanceController);
+
+        Assert.assertEquals(mHeadsUpAppearanceController.mExpandedHeight,
+                newController.mExpandedHeight, 0.0f);
+        Assert.assertEquals(mHeadsUpAppearanceController.mExpandFraction,
+                newController.mExpandFraction, 0.0f);
+        Assert.assertEquals(mHeadsUpAppearanceController.mIsExpanded,
+                newController.mIsExpanded);
+    }
+
+    @Test
     public void testDestroy() {
         reset(mHeadsUpManager);
         reset(mDarkIconDispatcher);
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 aa991cb..1837909 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
@@ -29,8 +29,8 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 
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 e95702c..9c55874 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
@@ -44,7 +44,6 @@
 
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
 import com.android.internal.util.function.TriConsumer;
-import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.util.wakelock.WakeLock;
@@ -94,6 +93,7 @@
                     mScrimInFrontColor = scrimInFrontColor;
                 },
                 visible -> mScrimVisibility = visible, mDozeParamenters, mAlarmManager);
+        mScrimController.setHasBackdrop(false);
     }
 
     @Test
@@ -140,12 +140,7 @@
 
     @Test
     public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() {
-        ScrimState.AOD.mKeyguardUpdateMonitor = new KeyguardUpdateMonitor(getContext()) {
-            @Override
-            public boolean hasLockscreenWallpaper() {
-                return true;
-            }
-        };
+        mScrimController.setHasBackdrop(true);
         mScrimController.setWallpaperSupportsAmbientMode(true);
         mScrimController.transitionTo(ScrimState.AOD);
         mScrimController.finishAnimationsImmediately();
@@ -157,6 +152,20 @@
     }
 
     @Test
+    public void setHasBackdrop_withAodWallpaperAndAlbumArt() {
+        mScrimController.setWallpaperSupportsAmbientMode(true);
+        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.finishAnimationsImmediately();
+        mScrimController.setHasBackdrop(true);
+        mScrimController.finishAnimationsImmediately();
+        // Front scrim should be transparent
+        // Back scrim should be visible with tint
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+        assertScrimTint(mScrimBehind, true /* tinted */);
+        assertScrimTint(mScrimInFront, true /* tinted */);
+    }
+
+    @Test
     public void transitionToAod_withFrontAlphaUpdates() {
         // Assert that setting the AOD front scrim alpha doesn't take effect in a non-AOD state.
         mScrimController.transitionTo(ScrimState.KEYGUARD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 85135ac..6933328 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -60,7 +60,7 @@
     @Mock
     private NotificationPanelView mNotificationPanelView;
     @Mock
-    private FingerprintUnlockController mFingerprintUnlockController;
+    private BiometricUnlockController mBiometrucUnlockController;
     @Mock
     private DismissCallbackRegistry mDismissCallbackRegistry;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -72,7 +72,7 @@
         mStatusBarKeyguardViewManager = new TestableStatusBarKeyguardViewManager(getContext(),
                 mViewMediatorCallback, mLockPatternUtils);
         mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
-                mNotificationPanelView, mFingerprintUnlockController, mDismissCallbackRegistry);
+                mNotificationPanelView, mBiometrucUnlockController, mDismissCallbackRegistry);
         mStatusBarKeyguardViewManager.show(null);
     }
 
@@ -170,8 +170,16 @@
 
     @Test
     public void onPanelExpansionChanged_neverTranslatesBouncerWhenWakeAndUnlock() {
-        when(mFingerprintUnlockController.getMode())
-                .thenReturn(FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
+        when(mBiometrucUnlockController.getMode())
+                .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
+        mStatusBarKeyguardViewManager.onPanelExpansionChanged(KeyguardBouncer.EXPANSION_VISIBLE,
+                false /* tracking */);
+        verify(mBouncer, never()).setExpansion(anyFloat());
+    }
+
+    @Test
+    public void onPanelExpansionChanged_neverTranslatesBouncerWhenLaunchingApp() {
+        when(mStatusBar.isInLaunchTransition()).thenReturn(true);
         mStatusBarKeyguardViewManager.onPanelExpansionChanged(KeyguardBouncer.EXPANSION_VISIBLE,
                 false /* tracking */);
         verify(mBouncer, never()).setExpansion(anyFloat());
@@ -188,7 +196,7 @@
         @Override
         public void registerStatusBar(StatusBar statusBar, ViewGroup container,
                 NotificationPanelView notificationPanelView,
-                FingerprintUnlockController fingerprintUnlockController,
+                BiometricUnlockController fingerprintUnlockController,
                 DismissCallbackRegistry dismissCallbackRegistry) {
             super.registerStatusBar(statusBar, container, notificationPanelView,
                     fingerprintUnlockController, dismissCallbackRegistry);
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 0a412b9..e39238d 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
@@ -41,12 +41,10 @@
 import android.app.StatusBarManager;
 import android.app.trust.TrustManager;
 import android.content.Context;
-import android.content.pm.UserInfo;
 import android.hardware.fingerprint.FingerprintManager;
 import android.metrics.LogMaker;
 import android.os.Binder;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IPowerManager;
 import android.os.Looper;
 import android.os.PowerManager;
@@ -59,8 +57,6 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.SparseArray;
-import android.view.Gravity;
-import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 
 import com.android.internal.logging.MetricsLogger;
@@ -76,21 +72,20 @@
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.statusbar.ActivatableNotificationView;
-import com.android.systemui.statusbar.AppOpsListener;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.AppOpsListener;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.FooterView;
-import com.android.systemui.statusbar.FooterViewButton;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.FooterView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationData.Entry;
-import com.android.systemui.statusbar.NotificationEntryManager;
-import com.android.systemui.statusbar.NotificationGutsManager;
-import com.android.systemui.statusbar.NotificationListContainer;
+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.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationLogger;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -104,7 +99,7 @@
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -116,7 +111,6 @@
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.function.Predicate;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -132,7 +126,7 @@
     @Mock private IStatusBarService mBarService;
     @Mock private ScrimController mScrimController;
     @Mock private ArrayList<Entry> mNotificationList;
-    @Mock private FingerprintUnlockController mFingerprintUnlockController;
+    @Mock private BiometricUnlockController mBiometricUnlockController;
     @Mock private NotificationData mNotificationData;
 
     // Mock dependencies:
@@ -207,7 +201,7 @@
                 mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
                 mPowerManager, mNotificationPanelView, mBarService, mNotificationListener,
                 mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager,
-                mEntryManager, mScrimController, mFingerprintUnlockController,
+                mEntryManager, mScrimController, mBiometricUnlockController,
                 mock(ActivityLaunchAnimator.class), mKeyguardViewMediator,
                 mRemoteInputManager, mock(NotificationGroupManager.class),
                 mock(FalsingManager.class), mock(StatusBarWindowManager.class),
@@ -572,15 +566,15 @@
 
     @Test
     public void testFingerprintNotification_UpdatesScrims() {
-        mStatusBar.notifyFpAuthModeChanged();
+        mStatusBar.notifyBiometricAuthModeChanged();
         verify(mScrimController).transitionTo(any(), any());
     }
 
     @Test
     public void testFingerprintUnlock_UpdatesScrims() {
         // Simulate unlocking from AoD with fingerprint.
-        when(mFingerprintUnlockController.getMode())
-                .thenReturn(FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
+        when(mBiometricUnlockController.getMode())
+                .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
         mStatusBar.updateScrimController();
         verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
     }
@@ -597,7 +591,7 @@
 
     @Test
     public void testInflateFooterView() {
-        mStatusBar.reevaluateStyles();
+        mStatusBar.inflateFooterView();
         ArgumentCaptor<FooterView> captor = ArgumentCaptor.forClass(FooterView.class);
         verify(mStackScroller).setFooterView(captor.capture());
 
@@ -661,7 +655,7 @@
     @Test
     public void testUpdateFooter_atEnd() {
         // add footer
-        mStatusBar.reevaluateStyles();
+        mStatusBar.inflateFooterView();
 
         // add notification
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
@@ -700,6 +694,13 @@
         assertTrue(mStatusBar.mState == StatusBarState.FULLSCREEN_USER_SWITCHER);
     }
 
+    @Test
+    public void testOnDensityOrFontScaleChanged_reInflatesFooterViews() {
+        mStatusBar.onDensityOrFontScaleChanged();
+        verify(mStackScroller).setFooterView(any());
+        verify(mStackScroller).setEmptyShadeView(any());
+    }
+
     static class TestableStatusBar extends StatusBar {
         public TestableStatusBar(StatusBarKeyguardViewManager man,
                 UnlockMethodCache unlock, KeyguardIndicationController key,
@@ -710,7 +711,7 @@
                 VisualStabilityManager visualStabilityManager,
                 NotificationViewHierarchyManager viewHierarchyManager,
                 TestableNotificationEntryManager entryManager, ScrimController scrimController,
-                FingerprintUnlockController fingerprintUnlockController,
+                BiometricUnlockController biometricUnlockController,
                 ActivityLaunchAnimator launchAnimator, KeyguardViewMediator keyguardViewMediator,
                 NotificationRemoteInputManager notificationRemoteInputManager,
                 NotificationGroupManager notificationGroupManager,
@@ -736,7 +737,7 @@
             mViewHierarchyManager = viewHierarchyManager;
             mEntryManager = entryManager;
             mScrimController = scrimController;
-            mFingerprintUnlockController = fingerprintUnlockController;
+            mBiometricUnlockController = biometricUnlockController;
             mActivityLaunchAnimator = launchAnimator;
             mKeyguardViewMediator = keyguardViewMediator;
             mClearAllEnabled = true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index a068a5e..33705e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -20,19 +20,14 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.Context;
 import android.os.SystemClock;
-import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.MotionEvent;
-import android.view.View;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.DragDownHelper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.StatusBarState;
 
 import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
index e1b97bda..a0fb330 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -36,7 +36,6 @@
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 @SmallTest
-@Ignore
 public class LocationControllerImplTest extends SysuiTestCase {
 
     private LocationControllerImpl mLocationController;
@@ -48,6 +47,7 @@
     }
 
     @Test
+    @Ignore("flaky")
     public void testRemoveSelfActive_DoesNotCrash() {
         LocationController.LocationChangeCallback callback = new LocationChangeCallback() {
             @Override
@@ -69,6 +69,7 @@
     }
 
     @Test
+    @Ignore("flaky")
     public void testRemoveSelfSettings_DoesNotCrash() {
         LocationController.LocationChangeCallback callback = new LocationChangeCallback() {
             @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index dff0665..6e3d906 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -87,15 +87,15 @@
                 WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel], testSsid);
 
         // Set to different activity state first to ensure a callback happens.
-        setWifiActivity(WifiManager.DATA_ACTIVITY_IN);
+        setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN);
 
-        setWifiActivity(WifiManager.DATA_ACTIVITY_NONE);
+        setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE);
         verifyLastQsDataDirection(false, false);
-        setWifiActivity(WifiManager.DATA_ACTIVITY_IN);
+        setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN);
         verifyLastQsDataDirection(true, false);
-        setWifiActivity(WifiManager.DATA_ACTIVITY_OUT);
+        setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT);
         verifyLastQsDataDirection(false, true);
-        setWifiActivity(WifiManager.DATA_ACTIVITY_INOUT);
+        setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT);
         verifyLastQsDataDirection(true, true);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index a6fa4f5..3164c04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -32,7 +32,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.RemoteInputController;
 
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 c573ca8..01e6307 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
@@ -44,7 +44,7 @@
 import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
@@ -375,7 +375,7 @@
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
                 new Intent(TEST_ACTION), 0);
         RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
-        mView.setRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, mContainer);
+        mView.setRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, mContainer, choices);
     }
 
     /** Builds a {@link ViewGroup} whose measures and layout mirror a {@link SmartReplyView}. */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
deleted file mode 100644
index 8a74019..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
+++ /dev/null
@@ -1,69 +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.stack;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.NotificationHeaderView;
-import android.view.View;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationTestHelper;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
-public class NotificationChildrenContainerTest extends SysuiTestCase {
-
-    private ExpandableNotificationRow mGroup;
-    private int mId;
-    private NotificationTestHelper mNotificationTestHelper;
-
-    @Before
-    public void setUp() throws Exception {
-        mNotificationTestHelper = new NotificationTestHelper(mContext);
-        mGroup = mNotificationTestHelper.createGroup();
-    }
-
-    @Test
-    public void testLowPriorityHeaderCleared() {
-        mGroup.setIsLowPriority(true);
-        NotificationChildrenContainer childrenContainer = mGroup.getChildrenContainer();
-        NotificationHeaderView lowPriorityHeaderView = childrenContainer.getLowPriorityHeaderView();
-        Assert.assertTrue(lowPriorityHeaderView.getVisibility() == View.VISIBLE);
-        Assert.assertTrue(lowPriorityHeaderView.getParent() == childrenContainer);
-        mGroup.setIsLowPriority(false);
-        Assert.assertTrue(lowPriorityHeaderView.getParent() == null);
-        Assert.assertTrue(childrenContainer.getLowPriorityHeaderView() == null);
-    }
-
-    @Test
-    public void testRecreateNotificationHeader_hasHeader() {
-        NotificationChildrenContainer childrenContainer = mGroup.getChildrenContainer();
-        childrenContainer.recreateNotificationHeader(null);
-        Assert.assertNotNull("Children container must have a header after recreation",
-                childrenContainer.getCurrentHeaderView());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
deleted file mode 100644
index 16e69f4..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
+++ /dev/null
@@ -1,170 +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.stack;
-
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationTestHelper;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.HashSet;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
-public class NotificationRoundnessManagerTest extends SysuiTestCase {
-
-    private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
-    private HashSet<View> mAnimatedChildren = new HashSet<>();
-    private Runnable mRoundnessCallback = mock(Runnable.class);
-    private ExpandableNotificationRow mFirst;
-    private ExpandableNotificationRow mSecond;
-
-    @Before
-    public void setUp() throws Exception {
-        NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
-        mFirst = testHelper.createRow();
-        mFirst.setHeadsUpAnimatingAwayListener(animatingAway
-                -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mFirst, animatingAway));
-        mSecond = testHelper.createRow();
-        mSecond.setHeadsUpAnimatingAwayListener(animatingAway
-                -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mSecond, animatingAway));
-        mRoundnessManager.setOnRoundingChangedCallback(mRoundnessCallback);
-        mRoundnessManager.setAnimatedChildren(mAnimatedChildren);
-        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mFirst);
-        mRoundnessManager.setExpanded(1.0f, 1.0f);
-    }
-
-    @Test
-    public void testCallbackCalledWhenSecondChanged() {
-        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mSecond);
-        verify(mRoundnessCallback, atLeast(1)).run();
-    }
-
-    @Test
-    public void testCallbackCalledWhenFirstChanged() {
-        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mFirst);
-        verify(mRoundnessCallback, atLeast(1)).run();
-    }
-
-    @Test
-    public void testRoundnessSetOnLast() {
-        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mSecond);
-        Assert.assertEquals(1.0f, mSecond.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(0.0f, mSecond.getCurrentTopRoundness(), 0.0f);
-    }
-
-    @Test
-    public void testRoundnessSetOnNew() {
-        mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, null);
-        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
-    }
-
-    @Test
-    public void testCompleteReplacement() {
-        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
-        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
-    }
-
-    @Test
-    public void testNotCalledWhenRemoved() {
-        mFirst.setRemoved();
-        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
-        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
-    }
-
-    @Test
-    public void testRoundedWhenPinnedAndCollapsed() {
-        mFirst.setPinned(true);
-        mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */);
-        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
-        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
-    }
-
-    @Test
-    public void testRoundedWhenGoingAwayAndCollapsed() {
-        mFirst.setHeadsUpAnimatingAway(true);
-        mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */);
-        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
-        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
-    }
-
-    @Test
-    public void testRoundedNormalRoundingWhenExpanded() {
-        mFirst.setHeadsUpAnimatingAway(true);
-        mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.0f /* appearFraction */);
-        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
-        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
-    }
-
-    @Test
-    public void testTrackingHeadsUpRoundedIfPushingUp() {
-        mRoundnessManager.setExpanded(1.0f /* expandedHeight */, -0.5f /* appearFraction */);
-        mRoundnessManager.setTrackingHeadsUp(mFirst);
-        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
-        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
-    }
-
-    @Test
-    public void testTrackingHeadsUpNotRoundedIfPushingDown() {
-        mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.5f /* appearFraction */);
-        mRoundnessManager.setTrackingHeadsUp(mFirst);
-        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
-        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
-    }
-
-    @Test
-    public void testRoundingUpdatedWhenAnimatingAwayTrue() {
-        mRoundnessManager.setExpanded(0.0f, 0.0f);
-        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
-        mFirst.setHeadsUpAnimatingAway(true);
-        Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
-    }
-
-
-    @Test
-    public void testRoundingUpdatedWhenAnimatingAwayFalse() {
-        mRoundnessManager.setExpanded(0.0f, 0.0f);
-        mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
-        mFirst.setHeadsUpAnimatingAway(true);
-        mFirst.setHeadsUpAnimatingAway(false);
-        Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
deleted file mode 100644
index 5400e3b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
+++ /dev/null
@@ -1,189 +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.stack;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.systemui.ExpandHelper;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.FooterView;
-import com.android.systemui.statusbar.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.StatusBar;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-/**
- * Tests for {@link NotificationStackScrollLayout}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class NotificationStackScrollLayoutTest extends SysuiTestCase {
-
-    private NotificationStackScrollLayout mStackScroller;
-
-    @Rule public MockitoRule mockito = MockitoJUnit.rule();
-    @Mock private StatusBar mBar;
-    @Mock private HeadsUpManagerPhone mHeadsUpManager;
-    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
-    @Mock private NotificationGroupManager mGroupManager;
-    @Mock private ExpandHelper mExpandHelper;
-    @Mock private EmptyShadeView mEmptyShadeView;
-
-    @Before
-    @UiThreadTest
-    public void setUp() throws Exception {
-        // Inject dependencies before initializing the layout
-        mDependency.injectTestDependency(
-                NotificationBlockingHelperManager.class,
-                mBlockingHelperManager);
-
-        NotificationShelf notificationShelf = spy(new NotificationShelf(getContext(), null));
-        mStackScroller = new NotificationStackScrollLayout(getContext());
-        mStackScroller.setShelf(notificationShelf);
-        mStackScroller.setStatusBar(mBar);
-        mStackScroller.setScrimController(mock(ScrimController.class));
-        mStackScroller.setHeadsUpManager(mHeadsUpManager);
-        mStackScroller.setGroupManager(mGroupManager);
-        mStackScroller.setEmptyShadeView(mEmptyShadeView);
-
-        // Stub out functionality that isn't necessary to test.
-        doNothing().when(mBar)
-                .executeRunnableDismissingKeyguard(any(Runnable.class),
-                        any(Runnable.class),
-                        anyBoolean(),
-                        anyBoolean(),
-                        anyBoolean());
-        doNothing().when(mGroupManager).collapseAllGroups();
-        doNothing().when(mExpandHelper).cancelImmediately();
-        doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
-        doNothing().when(notificationShelf).fadeInTranslating();
-    }
-
-    @Test
-    public void testNotDimmedOnKeyguard() {
-        when(mBar.getBarState()).thenReturn(StatusBarState.SHADE);
-        mStackScroller.setDimmed(true /* dimmed */, false /* animate */);
-        mStackScroller.setDimmed(true /* dimmed */, true /* animate */);
-        Assert.assertFalse(mStackScroller.isDimmed());
-    }
-
-    @Test
-    public void testAntiBurnInOffset() {
-        final int burnInOffset = 30;
-        mStackScroller.setAntiBurnInOffsetX(burnInOffset);
-        mStackScroller.setDark(false /* dark */, false /* animated */, null /* touch */);
-        Assert.assertEquals(0 /* expected */, mStackScroller.getTranslationX(), 0.01 /* delta */);
-        mStackScroller.setDark(true /* dark */, false /* animated */, null /* touch */);
-        Assert.assertEquals(burnInOffset /* expected */, mStackScroller.getTranslationX(),
-                0.01 /* delta */);
-    }
-
-    @Test
-    public void updateEmptyView_dndSuppressing() {
-        when(mEmptyShadeView.willBeGone()).thenReturn(true);
-        when(mBar.areNotificationsHidden()).thenReturn(true);
-
-        mStackScroller.updateEmptyShadeView(true);
-
-        verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
-    }
-
-    @Test
-    public void updateEmptyView_dndNotSuppressing() {
-        mStackScroller.setEmptyShadeView(mEmptyShadeView);
-        when(mEmptyShadeView.willBeGone()).thenReturn(true);
-        when(mBar.areNotificationsHidden()).thenReturn(false);
-
-        mStackScroller.updateEmptyShadeView(true);
-
-        verify(mEmptyShadeView).setText(R.string.empty_shade_text);
-    }
-
-    @Test
-    public void updateEmptyView_noNotificationsToDndSuppressing() {
-        mStackScroller.setEmptyShadeView(mEmptyShadeView);
-        when(mEmptyShadeView.willBeGone()).thenReturn(true);
-        when(mBar.areNotificationsHidden()).thenReturn(false);
-        mStackScroller.updateEmptyShadeView(true);
-        verify(mEmptyShadeView).setText(R.string.empty_shade_text);
-
-        when(mBar.areNotificationsHidden()).thenReturn(true);
-        mStackScroller.updateEmptyShadeView(true);
-        verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
-    }
-
-    @Test
-    @UiThreadTest
-    public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
-        mStackScroller.setExpandedHeight(0f);
-        verify(mBlockingHelperManager).setNotificationShadeExpanded(0f);
-        reset(mBlockingHelperManager);
-
-        mStackScroller.setExpandedHeight(100f);
-        verify(mBlockingHelperManager).setNotificationShadeExpanded(100f);
-    }
-
-    @Test
-    public void manageNotifications_visible() {
-        FooterView view = mock(FooterView.class);
-        mStackScroller.setFooterView(view);
-        when(view.willBeGone()).thenReturn(true);
-
-        mStackScroller.updateFooterView(true, false);
-
-        verify(view).setVisible(eq(true), anyBoolean());
-        verify(view).setSecondaryVisible(eq(false), anyBoolean());
-    }
-
-    @Test
-    public void clearAll_visible() {
-        FooterView view = mock(FooterView.class);
-        mStackScroller.setFooterView(view);
-        when(view.willBeGone()).thenReturn(true);
-
-        mStackScroller.updateFooterView(true, true);
-
-        verify(view).setVisible(eq(true), anyBoolean());
-        verify(view).setSecondaryVisible(eq(true), anyBoolean());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
index 50b4f3f..0d398be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
@@ -103,7 +103,7 @@
                 NotificationManager.IMPORTANCE_MIN);
         NotificationChannel newChannel =
                 NotificationChannels.createScreenshotChannel("newName", legacyChannel);
-        assertEquals(Uri.EMPTY, newChannel.getSound());
+        assertEquals(null, newChannel.getSound());
         assertEquals("newName", newChannel.getName());
         // MIN importance not user locked, so HIGH wins out.
         assertEquals(NotificationManager.IMPORTANCE_HIGH, newChannel.getImportance());
@@ -113,7 +113,7 @@
     public void testInheritFromLegacy_noLegacyExists() {
         NotificationChannel newChannel =
                 NotificationChannels.createScreenshotChannel("newName", null);
-        assertEquals(Uri.EMPTY, newChannel.getSound());
+        assertEquals(null, newChannel.getSound());
         assertEquals("newName", newChannel.getName());
         assertEquals(NotificationManager.IMPORTANCE_HIGH, newChannel.getImportance());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 43d60e4..c536dca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -56,10 +56,10 @@
 
 import java.util.function.Predicate;
 
-@Ignore
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
+@Ignore
 public class VolumeDialogImplTest extends SysuiTestCase {
 
     VolumeDialogImpl mDialog;
diff --git a/packages/VpnDialogs/res/drawable-hdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-hdpi/ic_vpn_dialog.png
deleted file mode 100644
index a0b4b61..0000000
--- a/packages/VpnDialogs/res/drawable-hdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-mdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-mdpi/ic_vpn_dialog.png
deleted file mode 100644
index df5dfe8..0000000
--- a/packages/VpnDialogs/res/drawable-mdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-xhdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-xhdpi/ic_vpn_dialog.png
deleted file mode 100644
index 18d5a3a..0000000
--- a/packages/VpnDialogs/res/drawable-xhdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-xxhdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-xxhdpi/ic_vpn_dialog.png
deleted file mode 100644
index 4d475dc..0000000
--- a/packages/VpnDialogs/res/drawable-xxhdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-xxxhdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-xxxhdpi/ic_vpn_dialog.png
deleted file mode 100644
index 9d458b4..0000000
--- a/packages/VpnDialogs/res/drawable-xxxhdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable/ic_vpn_dialog.xml b/packages/VpnDialogs/res/drawable/ic_vpn_dialog.xml
new file mode 100644
index 0000000..24f622e
--- /dev/null
+++ b/packages/VpnDialogs/res/drawable/ic_vpn_dialog.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.
+*/
+-->
+<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="#FF000000"
+        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="#FF000000"
+        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/VpnDialogs/res/values-th/strings.xml b/packages/VpnDialogs/res/values-th/strings.xml
index f6dfca5..333ff5f 100644
--- a/packages/VpnDialogs/res/values-th/strings.xml
+++ b/packages/VpnDialogs/res/values-th/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"ขอการเชื่อมต่อ"</string>
-    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ต้องการติดตั้งการเชื่อมต่อ VPN เพื่อให้แอปสามารถตรวจสอบการเข้าใช้งานเครือข่าย โปรดยอมรับหากคุณเชื่อถือแหล่งที่มานี้เท่านั้น &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; จะปรากฏที่ด้านบนหน้าจอเมื่อมีการใช้งาน VPN อยู่"</string>
+    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ต้องการสร้างการเชื่อมต่อ VPN เพื่อให้แอปสามารถตรวจสอบการเข้าใช้งานเครือข่าย โปรดยอมรับหากคุณเชื่อถือแหล่งที่มานี้เท่านั้น &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; จะปรากฏที่ด้านบนหน้าจอเมื่อมีการใช้งาน VPN อยู่"</string>
     <string name="legacy_title" msgid="192936250066580964">"เชื่อมต่อ VPN แล้ว"</string>
     <string name="session" msgid="6470628549473641030">"เซสชัน"</string>
     <string name="duration" msgid="3584782459928719435">"ระยะเวลา:"</string>
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-pl/strings.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-pl/strings.xml
index 6768407..dd68d59 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-pl/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-pl/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="display_cutout_emulation_overlay" msgid="4967302169856689448">"Wycięcie wyświetlacza z narożnikami"</string>
+    <string name="display_cutout_emulation_overlay" msgid="4967302169856689448">"Wycięcie w ekranie z narożnikami"</string>
 </resources>
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml
index 80d8066..9254b4d 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml
@@ -37,6 +37,8 @@
         @right
     </string>
 
+    <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@*android:string/config_mainBuiltInDisplayCutout</string>
+
     <!-- Whether the display cutout region of the main built-in display should be forced to
          black in software (to avoid aliasing or emulate a cutout that is not physically existent).
      -->
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-pl/strings.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-pl/strings.xml
index fdc9a20..9534412 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-pl/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-pl/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="display_cutout_emulation_overlay" msgid="5659433562878674546">"Podwójne wycięcie wyświetlacza"</string>
+    <string name="display_cutout_emulation_overlay" msgid="5659433562878674546">"Podwójne wycięcie w ekranie"</string>
 </resources>
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml
index ca261f9..80c997a 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml
@@ -49,6 +49,8 @@
         @dp
     </string>
 
+    <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@*android:string/config_mainBuiltInDisplayCutout</string>
+
     <!-- Whether the display cutout region of the main built-in display should be forced to
          black in software (to avoid aliasing or emulate a cutout that is not physically existent).
      -->
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-pl/strings.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-pl/strings.xml
index f3261de..1e0b3ff 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-pl/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-pl/strings.xml
@@ -19,5 +19,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="display_cutout_emulation_overlay" msgid="6129374114103110395">"Wąskie wycięcie wyświetlacza"</string>
+    <string name="display_cutout_emulation_overlay" msgid="6129374114103110395">"Wąskie wycięcie w ekranie"</string>
 </resources>
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
index c22b2e7..6fb3c7f 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
@@ -40,6 +40,8 @@
         @dp
     </string>
 
+    <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@*android:string/config_mainBuiltInDisplayCutout</string>
+
     <!-- Whether the display cutout region of the main built-in display should be forced to
          black in software (to avoid aliasing or emulate a cutout that is not physically existent).
      -->
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-pl/strings.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-pl/strings.xml
index 561947e..4bd3c48 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-pl/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-pl/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="display_cutout_emulation_overlay" msgid="4955013674374634273">"Wysokie wycięcie wyświetlacza"</string>
+    <string name="display_cutout_emulation_overlay" msgid="4955013674374634273">"Wysokie wycięcie w ekranie"</string>
 </resources>
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
index 401e092..7c29ffb 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
@@ -40,6 +40,8 @@
         @dp
     </string>
 
+    <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@*android:string/config_mainBuiltInDisplayCutout</string>
+
     <!-- Whether the display cutout region of the main built-in display should be forced to
          black in software (to avoid aliasing or emulate a cutout that is not physically existent).
      -->
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-pl/strings.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-pl/strings.xml
index 082946f..f406c72 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-pl/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-pl/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="display_cutout_emulation_overlay" msgid="3249818092477753587">"Szerokie wycięcie wyświetlacza"</string>
+    <string name="display_cutout_emulation_overlay" msgid="3249818092477753587">"Szerokie wycięcie w ekranie"</string>
 </resources>
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
index f328b83..5fb8b9e 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
@@ -40,6 +40,8 @@
         @dp
     </string>
 
+    <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@*android:string/config_mainBuiltInDisplayCutout</string>
+
     <!-- Whether the display cutout region of the main built-in display should be forced to
          black in software (to avoid aliasing or emulate a cutout that is not physically existent).
      -->
diff --git a/packages/overlays/SysuiDarkThemeOverlay/Android.mk b/packages/overlays/SysuiDarkThemeOverlay/Android.mk
deleted file mode 100644
index 7b277bc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_RRO_THEME := SysuiDarkTheme
-LOCAL_CERTIFICATE := platform
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_PACKAGE_NAME := SysuiDarkThemeOverlay
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml b/packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml
deleted file mode 100644
index 8b6ee2b..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.systemui.theme.dark"
-    android:versionCode="1"
-    android:versionName="1.0">
-    <overlay android:targetPackage="com.android.systemui" android:priority="1"/>
-
-    <application android:label="@string/sysui_overlay_dark" android:hasCode="false"/>
-</manifest>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-af/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-af/strings.xml
deleted file mode 100644
index 33c6982..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-af/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Donker"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-am/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-am/strings.xml
deleted file mode 100644
index 5979569..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-am/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"ጨለማ"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ar/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ar/strings.xml
deleted file mode 100644
index 7b20c01..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ar/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"داكن"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-as/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-as/strings.xml
deleted file mode 100644
index 0910e7e..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-as/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"গাঢ়"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-az/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-az/strings.xml
deleted file mode 100644
index a9db75c..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-az/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Qaranlıq"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index b63dcbc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Tamno"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-be/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-be/strings.xml
deleted file mode 100644
index eb875b3..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-be/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Цёмная"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-bg/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-bg/strings.xml
deleted file mode 100644
index 7b39462..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-bg/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Тъмно"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-bn/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-bn/strings.xml
deleted file mode 100644
index 0910e7e..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-bn/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"গাঢ়"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-bs/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-bs/strings.xml
deleted file mode 100644
index b63dcbc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-bs/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Tamno"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ca/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ca/strings.xml
deleted file mode 100644
index 02ee226..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ca/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Fosc"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-cs/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-cs/strings.xml
deleted file mode 100644
index 5d11f07..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-cs/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Tmavé"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-da/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-da/strings.xml
deleted file mode 100644
index 460ebe7..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-da/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Mørk"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-de/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-de/strings.xml
deleted file mode 100644
index 4b54b8e..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-de/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Dunkel"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-el/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-el/strings.xml
deleted file mode 100644
index c58061d..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-el/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Σκοτεινό"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rAU/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rAU/strings.xml
deleted file mode 100644
index 7c94a51..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Dark"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rCA/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rCA/strings.xml
deleted file mode 100644
index 7c94a51..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Dark"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rGB/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rGB/strings.xml
deleted file mode 100644
index 7c94a51..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Dark"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rIN/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rIN/strings.xml
deleted file mode 100644
index 7c94a51..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Dark"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rXC/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rXC/strings.xml
deleted file mode 100644
index cbdd3d2..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‎‎Dark‎‏‎‎‏‎"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-es-rUS/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-es-rUS/strings.xml
deleted file mode 100644
index 2717f0f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Oscuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-es/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-es/strings.xml
deleted file mode 100644
index 2717f0f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-es/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Oscuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-et/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-et/strings.xml
deleted file mode 100644
index e0cce05..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-et/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Tume"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-eu/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-eu/strings.xml
deleted file mode 100644
index 44cee4c..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-eu/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Iluna"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-fa/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-fa/strings.xml
deleted file mode 100644
index fdd1df5..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-fa/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"تیره"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-fi/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-fi/strings.xml
deleted file mode 100644
index 237fe70..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-fi/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Tumma"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-fr-rCA/strings.xml
deleted file mode 100644
index f92c2ef..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Sombre"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-fr/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-fr/strings.xml
deleted file mode 100644
index eac51d3..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-fr/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Foncé"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-gl/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-gl/strings.xml
deleted file mode 100644
index 300868f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-gl/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Escuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-gu/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-gu/strings.xml
deleted file mode 100644
index 6a4cd62..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-gu/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"ઘેરી"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-hi/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-hi/strings.xml
deleted file mode 100644
index c5bc0e2..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-hi/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"गहरे रंग की थीम"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-hr/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-hr/strings.xml
deleted file mode 100644
index b63dcbc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-hr/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Tamno"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-hu/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-hu/strings.xml
deleted file mode 100644
index 84a3ab8..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-hu/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Sötét"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-hy/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-hy/strings.xml
deleted file mode 100644
index 555cb64..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-hy/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Մուգ"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-in/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-in/strings.xml
deleted file mode 100644
index 391451b..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-in/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Gelap"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-is/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-is/strings.xml
deleted file mode 100644
index f4d1531..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-is/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Dökkt"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-it/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-it/strings.xml
deleted file mode 100644
index b59155b..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-it/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Scuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-iw/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-iw/strings.xml
deleted file mode 100644
index 3ecf444..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-iw/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"כהה"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ja/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ja/strings.xml
deleted file mode 100644
index 3a2dba0..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ja/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"ダーク"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ka/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ka/strings.xml
deleted file mode 100644
index 36bf77e..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ka/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"მუქი"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-kk/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-kk/strings.xml
deleted file mode 100644
index 913c0b1..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-kk/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Қараңғы"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-km/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-km/strings.xml
deleted file mode 100644
index b56c490..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-km/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"ងងឹត"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-kn/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-kn/strings.xml
deleted file mode 100644
index e757116..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-kn/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"ಕತ್ತಲೆ"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ko/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ko/strings.xml
deleted file mode 100644
index ca4ab1e..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ko/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"어두움"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ky/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ky/strings.xml
deleted file mode 100644
index e8e8279..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ky/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Караңгы"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-lo/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-lo/strings.xml
deleted file mode 100644
index 0434a41..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-lo/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"ມືດ"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-lt/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-lt/strings.xml
deleted file mode 100644
index 147779b..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-lt/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Tamsi"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-lv/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-lv/strings.xml
deleted file mode 100644
index 7a296ec..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-lv/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Tumšs"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-mk/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-mk/strings.xml
deleted file mode 100644
index 6be693a..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-mk/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Темна"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ml/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ml/strings.xml
deleted file mode 100644
index f8a24fa..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ml/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"ഡാർക്ക്"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-mn/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-mn/strings.xml
deleted file mode 100644
index e65d9c7..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-mn/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Бараан"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-mr/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-mr/strings.xml
deleted file mode 100644
index 854af00..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-mr/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"गडद"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ms/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ms/strings.xml
deleted file mode 100644
index 391451b..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ms/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Gelap"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-my/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-my/strings.xml
deleted file mode 100644
index 008e9c6..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-my/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"မှောင်သော"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-nb/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-nb/strings.xml
deleted file mode 100644
index 460ebe7..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-nb/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Mørk"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ne/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ne/strings.xml
deleted file mode 100644
index 8f2c5ba..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ne/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"अँध्यारो"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-nl/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-nl/strings.xml
deleted file mode 100644
index 33c6982..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-nl/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Donker"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-or/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-or/strings.xml
deleted file mode 100644
index d8045bd..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-or/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"ଗାଢ଼"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-pa/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-pa/strings.xml
deleted file mode 100644
index 7110303..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-pa/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"ਗੂੜ੍ਹਾ"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-pl/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-pl/strings.xml
deleted file mode 100644
index 25ca20f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-pl/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Ciemna"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-pt-rBR/strings.xml
deleted file mode 100644
index 300868f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Escuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-pt-rPT/strings.xml
deleted file mode 100644
index 300868f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Escuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-pt/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-pt/strings.xml
deleted file mode 100644
index 300868f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-pt/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Escuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ro/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ro/strings.xml
deleted file mode 100644
index de73f36..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ro/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Întunecată"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ru/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ru/strings.xml
deleted file mode 100644
index b05e844..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ru/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Темный"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-si/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-si/strings.xml
deleted file mode 100644
index f0f5725..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-si/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"අඳුරු"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-sk/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-sk/strings.xml
deleted file mode 100644
index 5df6895..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-sk/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Tmavý"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-sl/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-sl/strings.xml
deleted file mode 100644
index ad58250..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-sl/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Temno"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-sq/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-sq/strings.xml
deleted file mode 100644
index 0e1eae7..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-sq/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"E errët"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-sr/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-sr/strings.xml
deleted file mode 100644
index 1561ee2..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-sr/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Тамно"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-sv/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-sv/strings.xml
deleted file mode 100644
index 676de42..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-sv/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Mörk"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-sw/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-sw/strings.xml
deleted file mode 100644
index cc1f120..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-sw/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Nyeusi"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ta/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ta/strings.xml
deleted file mode 100644
index af98172..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ta/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"டார்க்"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-te/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-te/strings.xml
deleted file mode 100644
index 446455f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-te/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"ముదురు రంగు"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-th/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-th/strings.xml
deleted file mode 100644
index 9e3462b..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-th/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"เข้ม"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-tl/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-tl/strings.xml
deleted file mode 100644
index 5502d90..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-tl/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Madilim"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-tr/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-tr/strings.xml
deleted file mode 100644
index 368b398..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-tr/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Koyu"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-uk/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-uk/strings.xml
deleted file mode 100644
index 6e67e45..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-uk/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Темна тема"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ur/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ur/strings.xml
deleted file mode 100644
index 1d5d6de..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ur/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"گہرا"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-uz/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-uz/strings.xml
deleted file mode 100644
index 957c28f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-uz/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Tungi"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-vi/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-vi/strings.xml
deleted file mode 100644
index a458889..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-vi/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Tối"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rCN/strings.xml
deleted file mode 100644
index c9b43dc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"深色"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rHK/strings.xml
deleted file mode 100644
index c9b43dc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"深色"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rTW/strings.xml
deleted file mode 100644
index c9b43dc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"深色"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-zu/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-zu/strings.xml
deleted file mode 100644
index 6d328da..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-zu/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="sysui_overlay_dark" msgid="557336259295713662">"Emnyama"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values/strings.xml
deleted file mode 100644
index 71f48d6..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values/strings.xml
+++ /dev/null
@@ -1,24 +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.
- */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <string name="sysui_overlay_dark">Dark</string>
-
-</resources>
-
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values/styles.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values/styles.xml
deleted file mode 100644
index 41a2940..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values/styles.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <style name="qs_base" parent="android:Theme.DeviceDefault">
-        <item name="android:colorPrimary">@*android:color/primary_device_default_settings</item>
-        <item name="android:colorPrimaryDark">@*android:color/primary_dark_device_default_settings</item>
-        <item name="android:colorSecondary">@*android:color/secondary_device_default_settings</item>
-        <item name="android:colorAccent">@*android:color/accent_device_default_dark</item>
-        <item name="android:colorControlNormal">?android:attr/textColorPrimary</item>
-        <item name="android:colorBackgroundFloating">@*android:color/material_grey_900</item>
-        <item name="android:panelColorBackground">@*android:color/material_grey_800</item>
-    </style>
-</resources>
\ No newline at end of file
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index d2990de..e0cf846 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -941,9 +941,10 @@
     // OS: 6.0
     NOTIFICATION_ZEN_MODE_EVENT_RULE = 146;
 
-    // ACTION: App notification settings > Block Notifications
+    // ACTION: App notification settings > Block Notifications or long press on
+    // notification blocks.
     // CATEGORY: SETTINGS
-    // OS: 6.0
+    // OS: 9.0
     ACTION_BAN_APP_NOTES = 147;
 
     // ACTION: Notification shade > Dismiss all button
@@ -6118,8 +6119,312 @@
 
     // ---- End P Constants, all P constants go above this line ----
 
-    // First Q constant in master goes here:
-    // Please delete these lines and use 1500 for your first enum.
+    // Time since this notification last interrupted (visibly or audible) the user
+    NOTIFICATION_SINCE_INTERRUPTION_MILLIS = 1500;
+
+    // OPEN: Notification interrupted the user, either audibly or visually.
+    //   Tagged data: NOTIFICATION_SINCE_INTERRUPTION_MILLIS
+    // CATEGORY: NOTIFICATION
+    NOTIFICATION_INTERRUPTION = 1501;
+
+    // OPEN: Settings
+    // CATEGORY: SETTINGS
+    // OS: Q
+    SETTINGS_HOMEPAGE = 1502;
+
+    // OPEN: Settings > Create shortcut(widget)
+    // CATEGORY: SETTINGS
+    // OS: Q
+    SETTINGS_CREATE_SHORTCUT = 1503;
+
+    // ACTION: Authenticate using fingerprint
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ACTION_FACE_AUTH = 1504;
+
+    // ACTION: Add fingerprint > Enroll fingerprint
+    // CATEGORY: SETTINGS
+    // OS: Q
+    ACTION_FACE_ENROLL = 1505;
+
+    // OPEN: Face Enroll introduction
+    // CATEGORY: SETTINGS
+    // OS: Q
+    FACE_ENROLL_INTRO = 1506;
+
+    // OPEN: Face Enroll introduction
+    // CATEGORY: SETTINGS
+    // OS: Q
+    FACE_ENROLL_ENROLLING = 1507;
+
+    // OPEN: Face Enroll introduction
+    // CATEGORY: SETTINGS
+    // OS: Q
+    FACE_ENROLL_FINISHED = 1508;
+
+    // OPEN: Face Enroll sidecar
+    // CATEGORY: SETTINGS
+    // OS: Q
+    FACE_ENROLL_SIDECAR = 1509;
+
+    // OPEN: Settings > Add face > Error dialog
+    // OS: Q
+    DIALOG_FACE_ERROR = 1510;
+
+    // OPEN: Settings > Security > Face
+    // CATEGORY: SETTINGS
+    // OS: Q
+    FACE = 1511;
+
+    // OPEN: Settings > Acessibility > HearingAid pairing instructions dialog
+    // CATEGORY: SETTINGS
+    // OS: Q
+    DIALOG_ACCESSIBILITY_HEARINGAID = 1512;
+
+    // ACTION: Activity start
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    ACTION_ACTIVITY_START = 1513;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Calling UID
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_CALLING_UID = 1514;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Calling package name
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_CALLING_PACKAGE_NAME = 1515;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Calling UID proc state
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_CALLING_UID_PROC_STATE = 1516;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Calling UID has any visible window
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_CALLING_UID_HAS_ANY_VISIBLE_WINDOW = 1517;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Real calling UID
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_REAL_CALLING_UID = 1518;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Real calling UID proc state
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_REAL_CALLING_UID_PROC_STATE = 1519;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Real calling UID has any visible window
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_REAL_CALLING_UID_HAS_ANY_VISIBLE_WINDOW = 1520;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Target UID
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_TARGET_UID = 1521;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Target UID package name
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_TARGET_PACKAGE_NAME = 1522;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Target UID proc state
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_TARGET_UID_PROC_STATE = 1523;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Target UID has any visible window
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW = 1524;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Target doze whitelist tag
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_TARGET_WHITELIST_TAG = 1525;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Target short component name
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_TARGET_SHORT_COMPONENT_NAME = 1526;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Coming from pending intent
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_COMING_FROM_PENDING_INTENT = 1527;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Intent action
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_INTENT_ACTION = 1528;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Caller app process record process name
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_PROCESS_RECORD_PROCESS_NAME = 1529;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Caller app process record current proc state
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_PROCESS_RECORD_CUR_PROC_STATE = 1530;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Caller app process record has client activities
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_PROCESS_RECORD_HAS_CLIENT_ACTIVITIES = 1531;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Caller app process record has foreground services
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_PROCESS_RECORD_HAS_FOREGROUND_SERVICES = 1532;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Caller app process record has foreground activities
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_PROCESS_RECORD_HAS_FOREGROUND_ACTIVITIES = 1533;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Caller app process record has top UI
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_PROCESS_RECORD_HAS_TOP_UI = 1534;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Caller app process record has overlay UI
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_PROCESS_RECORD_HAS_OVERLAY_UI = 1535;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Caller app process record pending UI clean
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_PROCESS_RECORD_PENDING_UI_CLEAN = 1536;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Millis since caller app's process record last interaction event
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_PROCESS_RECORD_MILLIS_SINCE_LAST_INTERACTION_EVENT = 1537;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Millis since caller app's process record fg interaction
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_PROCESS_RECORD_MILLIS_SINCE_FG_INTERACTION = 1538;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Millis since caller app's process record last became unimportant
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_PROCESS_RECORD_MILLIS_SINCE_UNIMPORTANT = 1539;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Activity record launch mode
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_LAUNCH_MODE = 1540;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Activity record target activity
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY = 1541;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Activity record flags
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_FLAGS = 1542;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Activity record real activity
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_REAL_ACTIVITY = 1543;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Activity record short component name
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME = 1544;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Activity record process name
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_PROCESS_NAME = 1545;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Activity record is fullscreen
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_IS_FULLSCREEN = 1546;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Activity record is no display
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY = 1547;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Millis since activity was last visible
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE = 1548;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Activity record's resultTo packageName
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME = 1549;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Activity record's resultTo shortComponentName
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME = 1550;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Activity record is visible
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_IS_VISIBLE = 1551;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Activity record is visible ignoring keyguard
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD = 1552;
+
+    // Tagged data for ACTION_ACTIVITY_START.
+    // FIELD: Millis since activity's last launch
+    // CATEGORY: OTHER
+    // OS: Q (will also ship in PQ1A)
+    FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH = 1553;
 
     // ---- End Q Constants, all Q constants go above this line ----
 
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index a9a14ca..fba639c 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -212,6 +212,10 @@
     // Package: android
     NOTE_AUTO_SAVER_SUGGESTION = 49;
 
+    // Notify the user that their softap config preference has changed.
+    // Package: android
+    NOTE_SOFTAP_CONFIG_CHANGED = 50;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 72f11e0..f7fcf5c 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -457,6 +457,25 @@
   // Identifier for experimental scoring parameter settings.
   optional string score_experiment_id = 117;
 
+  // Data on wifi radio usage
+  optional WifiRadioUsage wifi_radio_usage = 118;
+
+  // Stores settings values used for metrics testing.
+  optional ExperimentValues experiment_values = 119;
+
+  // List of WifiIsUnusableEvents which get logged when we notice that WiFi is unusable.
+  // Collected only when WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED Settings is enabled.
+  repeated WifiIsUnusableEvent wifi_is_unusable_event_list = 120;
+
+  // Counts the occurrences of each link speed (Mbps) level
+  // with rssi (dBm) and rssi^2 sums (dBm^2)
+  repeated LinkSpeedCount link_speed_counts = 121;
+
+  // Number of times the SarManager failed to register SAR sensor listener
+  optional int32 num_sar_sensor_registration_failures = 122;
+
+  // Histogram of the EAP method type of all installed Passpoint profiles
+  repeated PasspointProfileTypeCount installed_passpoint_profile_type = 123;
 }
 
 // Information that gets logged for every WiFi connection.
@@ -642,6 +661,22 @@
   optional int32 count = 2;
 }
 
+// Number of occurrences of a specific link speed (Mbps)
+// and sum of rssi (dBm) and rssi^2 (dBm^2)
+message LinkSpeedCount {
+  // Link speed (Mbps)
+  optional int32 link_speed_mbps = 1;
+
+  // Number of RSSI polls with link_speed
+  optional int32 count = 2;
+
+  // Sum of absolute values of rssi values (dBm)
+  optional int32 rssi_sum_dbm = 3;
+
+  // Sum of squares of rssi values (dBm^2)
+  optional int64 rssi_sum_of_squares_dbm_sq = 4;
+}
+
 // Number of occurrences of Soft AP session durations
 message SoftApDurationBucket {
   // Bucket covers duration : [duration_sec, duration_sec + bucket_size_sec)
@@ -874,7 +909,7 @@
     STATE_INVALID = 12;
   }
 
-  // Bit mask of all supplicant state changes that occured since the last event
+  // Bit mask of all supplicant state changes that occurred since the last event
   optional uint32 supplicant_state_changes_bitmask = 9 [default = 0];
 
   // The number of milliseconds that have elapsed since the device booted
@@ -882,7 +917,7 @@
 
   optional FrameworkDisconnectReason framework_disconnect_reason = 11 [default = DISCONNECT_UNKNOWN];
 
-  // Flag which indicates if an association rejection event occured due to a timeout
+  // Flag which indicates if an association rejection event occurred due to a timeout
   optional bool association_timed_out = 12 [default = false];
 
   // Authentication failure reason, as reported by WifiManager (calculated from state & deauth code)
@@ -1503,3 +1538,111 @@
     optional int32 count = 2;
   }
 }
+
+// Usage data for the wifi radio while device is running on battery.
+message WifiRadioUsage {
+  // Duration of log (ms)
+  optional int64 logging_duration_ms = 1;
+
+  // Total time for which the radio is awake due to scan.
+  optional int64 scan_time_ms = 2;
+}
+
+message ExperimentValues {
+  // Indicates if we are logging WifiIsUnusableEvent in metrics
+  optional bool wifi_is_unusable_logging_enabled = 1;
+
+  // Minimum number of txBad to trigger a data stall
+  optional int32 wifi_data_stall_min_tx_bad = 2;
+
+  // Minimum number of txSuccess to trigger a data stall
+  // when rxSuccess is 0
+  optional int32 wifi_data_stall_min_tx_success_without_rx = 3;
+
+  // Indicates if we are logging LinkSpeedCount in metrics
+  optional bool link_speed_counts_logging_enabled = 4;
+}
+
+message WifiIsUnusableEvent {
+  enum TriggerType {
+    // Default/Invalid event
+    TYPE_UNKNOWN = 0;
+
+    // There is a data stall from tx failures
+    TYPE_DATA_STALL_BAD_TX = 1;
+
+    // There is a data stall from rx failures
+    TYPE_DATA_STALL_TX_WITHOUT_RX = 2;
+
+    // There is a data stall from both tx and rx failures
+    TYPE_DATA_STALL_BOTH = 3;
+
+    // Firmware generated an alert
+    TYPE_FIRMWARE_ALERT = 4;
+  }
+
+  // What event triggered WifiIsUnusableEvent.
+  optional TriggerType type = 1;
+
+  // The timestamp at which this event occurred.
+  // Measured in milliseconds that have elapsed since the device booted.
+  optional int64 start_time_millis = 2;
+
+  // NetworkAgent score of connected wifi.
+  // Defaults to -1 if the score was never set.
+  optional int32 last_score = 3 [default = -1];
+
+  // Delta of successfully transmitted (ACKed) unicast data packets
+  // between the last two WifiLinkLayerStats.
+  optional int64 tx_success_delta = 4;
+
+  // Delta of transmitted unicast data retry packets
+  // between the last two WifiLinkLayerStats.
+  optional int64 tx_retries_delta = 5;
+
+  // Delta of lost (not ACKed) transmitted unicast data packets
+  // between the last two WifiLinkLayerStats.
+  optional int64 tx_bad_delta = 6;
+
+  // Delta of received unicast data packets
+  // between the last two WifiLinkLayerStats.
+  optional int64 rx_success_delta = 7;
+
+  // Time in millisecond between the last two WifiLinkLayerStats.
+  optional int64 packet_update_time_delta = 8;
+
+  // The timestamp at which the last WifiLinkLayerStats was updated.
+  // Measured in milliseconds that have elapsed since the device booted.
+  optional int64 last_link_layer_stats_update_time = 9;
+
+  // Firmware alert code. Only valid when the event was triggered by a firmware alert, otherwise -1.
+  optional int32 firmware_alert_code = 10 [default = -1];
+}
+
+message PasspointProfileTypeCount {
+  enum EapMethod {
+    // Unknown Type
+    TYPE_UNKNOWN = 0;
+
+    // EAP_TLS (13)
+    TYPE_EAP_TLS = 1;
+
+    // EAP_TTLS (21)
+    TYPE_EAP_TTLS = 2;
+
+    // EAP_SIM (18)
+    TYPE_EAP_SIM = 3;
+
+    // EAP_AKA (23)
+    TYPE_EAP_AKA = 4;
+
+    // EAP_AKA_PRIME (50)
+    TYPE_EAP_AKA_PRIME = 5;
+  }
+
+  // Eap method type set in Passpoint profile
+  optional EapMethod eap_method_type = 1;
+
+  // Num of installed Passpoint profile with same eap method
+  optional int32 count = 2;
+}
\ No newline at end of file
diff --git a/rs/java/android/renderscript/BaseObj.java b/rs/java/android/renderscript/BaseObj.java
index f95af16..b7e05d9 100644
--- a/rs/java/android/renderscript/BaseObj.java
+++ b/rs/java/android/renderscript/BaseObj.java
@@ -16,6 +16,7 @@
 
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
 import dalvik.system.CloseGuard;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
@@ -73,6 +74,7 @@
     final CloseGuard guard = CloseGuard.get();
     private boolean mDestroyed;
     private String mName;
+    @UnsupportedAppUsage
     RenderScript mRS;
 
     /**
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index 667bf71..b8eb3a1 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -16,6 +16,8 @@
 
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * <p>An Element represents one item within an {@link
  * android.renderscript.Allocation}.  An Element is roughly equivalent to a C
@@ -1146,6 +1148,7 @@
      * @param dt The DataType for the new element.
      * @return Element
      */
+    @UnsupportedAppUsage
     static Element createUser(RenderScript rs, DataType dt) {
         DataKind dk = DataKind.USER;
         boolean norm = false;
diff --git a/rs/java/android/renderscript/FileA3D.java b/rs/java/android/renderscript/FileA3D.java
index 278d309..9a6b0bc 100644
--- a/rs/java/android/renderscript/FileA3D.java
+++ b/rs/java/android/renderscript/FileA3D.java
@@ -19,6 +19,7 @@
 import java.io.File;
 import java.io.InputStream;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 
@@ -53,6 +54,7 @@
         * @deprecated in API 16
         * RenderScript Mesh object
         **/
+        @UnsupportedAppUsage
         MESH (1);
 
         int mID;
@@ -100,6 +102,7 @@
         * @return type of a renderscript object the index entry
         *         describes
         */
+        @UnsupportedAppUsage
         public EntryType getEntryType() {
             return mEntryType;
         }
@@ -109,6 +112,7 @@
         * Used to load the object described by the index entry
         * @return base renderscript object described by the entry
         */
+        @UnsupportedAppUsage
         public BaseObj getObject() {
             mRS.validate();
             BaseObj obj = internalCreate(mRS, this);
@@ -212,6 +216,7 @@
     *
     * @return entry in the a3d file described by the index
     */
+    @UnsupportedAppUsage
     public IndexEntry getIndexEntry(int index) {
         if(getIndexEntryCount() == 0 || index < 0 || index >= mFileEntries.length) {
             return null;
@@ -284,6 +289,7 @@
     *
     * @return a3d file containing renderscript objects
     */
+    @UnsupportedAppUsage
     static public FileA3D createFromResource(RenderScript rs, Resources res, int id) {
 
         rs.validate();
diff --git a/rs/java/android/renderscript/Font.java b/rs/java/android/renderscript/Font.java
index d5ca31e..583350e 100644
--- a/rs/java/android/renderscript/Font.java
+++ b/rs/java/android/renderscript/Font.java
@@ -23,6 +23,7 @@
 
 import android.os.Environment;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 
@@ -85,6 +86,7 @@
         /**
          * @deprecated in API 16
          */
+        @UnsupportedAppUsage
         ITALIC,
         /**
          * @deprecated in API 16
@@ -236,6 +238,7 @@
      *
      * Returns default font if no match could be found.
      */
+    @UnsupportedAppUsage
     static public Font create(RenderScript rs, Resources res, String familyName, Style fontStyle, float pointSize) {
         String fileName = getFontFileName(familyName, fontStyle);
         String fontPath = Environment.getRootDirectory().getAbsolutePath();
diff --git a/rs/java/android/renderscript/Matrix4f.java b/rs/java/android/renderscript/Matrix4f.java
index 5d5bf5f..026c9fb 100644
--- a/rs/java/android/renderscript/Matrix4f.java
+++ b/rs/java/android/renderscript/Matrix4f.java
@@ -16,6 +16,7 @@
 
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
 import java.lang.Math;
 
 
@@ -489,5 +490,6 @@
         }
     }
 
+    @UnsupportedAppUsage
     final float[] mMat;
 }
diff --git a/rs/java/android/renderscript/Mesh.java b/rs/java/android/renderscript/Mesh.java
index 9e4f905..5321dcb 100644
--- a/rs/java/android/renderscript/Mesh.java
+++ b/rs/java/android/renderscript/Mesh.java
@@ -16,6 +16,7 @@
 
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
 import java.util.Vector;
 
 /**
@@ -49,6 +50,7 @@
         * @deprecated in API 16
         * Vertex data will be rendered as a series of points
         */
+        @UnsupportedAppUsage
         POINT (0),
         /**
         * @deprecated in API 16
@@ -64,6 +66,7 @@
         * @deprecated in API 16
         * Vertices will be rendered as individual triangles
         */
+        @UnsupportedAppUsage
         TRIANGLE (3),
         /**
         * @deprecated in API 16
@@ -111,6 +114,7 @@
     * @return vertex data allocation at the given index
     *
     **/
+    @UnsupportedAppUsage
     public Allocation getVertexAllocation(int slot) {
         return mVertexBuffers[slot];
     }
@@ -424,6 +428,7 @@
         /**
         * @deprecated in API 16
         **/
+        @UnsupportedAppUsage
         public AllocationBuilder(RenderScript rs) {
             mRS = rs;
             mVertexTypeCount = 0;
@@ -458,6 +463,7 @@
         *
         * @return this
         **/
+        @UnsupportedAppUsage
         public AllocationBuilder addVertexAllocation(Allocation a) throws IllegalStateException {
             if (mVertexTypeCount >= mVertexTypes.length) {
                 throw new IllegalStateException("Max vertex types exceeded.");
@@ -479,6 +485,7 @@
         *
         * @return this
         **/
+        @UnsupportedAppUsage
         public AllocationBuilder addIndexSetAllocation(Allocation a, Primitive p) {
             Entry indexType = new Entry();
             indexType.a = a;
@@ -495,6 +502,7 @@
         *
         * @return this
         **/
+        @UnsupportedAppUsage
         public AllocationBuilder addIndexSetType(Primitive p) {
             Entry indexType = new Entry();
             indexType.a = null;
@@ -508,6 +516,7 @@
         * Create a Mesh object from the current state of the builder
         *
         **/
+        @UnsupportedAppUsage
         public Mesh create() {
             mRS.validate();
 
@@ -596,6 +605,7 @@
         *              channels are present in the mesh
         *
         **/
+        @UnsupportedAppUsage
         public TriangleMeshBuilder(RenderScript rs, int vtxSize, int flags) {
             mRS = rs;
             mVtxCount = 0;
@@ -652,6 +662,7 @@
         * @return this
         *
         **/
+        @UnsupportedAppUsage
         public TriangleMeshBuilder addVertex(float x, float y) {
             if (mVtxSize != 2) {
                 throw new IllegalStateException("add mistmatch with declared components.");
@@ -757,6 +768,7 @@
         *
         * @return this
         **/
+        @UnsupportedAppUsage
         public TriangleMeshBuilder addTriangle(int idx1, int idx2, int idx3) {
             if((idx1 >= mMaxIndex) || (idx1 < 0) ||
                (idx2 >= mMaxIndex) || (idx2 < 0) ||
@@ -789,6 +801,7 @@
         *                             accessible memory
         *
         **/
+        @UnsupportedAppUsage
         public Mesh create(boolean uploadToBufferObject) {
             Element.Builder b = new Element.Builder(mRS);
             b.add(Element.createVector(mRS,
diff --git a/rs/java/android/renderscript/Program.java b/rs/java/android/renderscript/Program.java
index 772021c..e28d646 100644
--- a/rs/java/android/renderscript/Program.java
+++ b/rs/java/android/renderscript/Program.java
@@ -21,6 +21,7 @@
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.util.Log;
 
@@ -45,6 +46,7 @@
      *
      **/
     public enum TextureType {
+        @UnsupportedAppUsage
         TEXTURE_2D (0),
         TEXTURE_CUBE (1);
 
@@ -199,20 +201,30 @@
 
 
     public static class BaseProgramBuilder {
+        @UnsupportedAppUsage
         RenderScript mRS;
+        @UnsupportedAppUsage
         Element mInputs[];
+        @UnsupportedAppUsage
         Element mOutputs[];
+        @UnsupportedAppUsage
         Type mConstants[];
         Type mTextures[];
         TextureType mTextureTypes[];
         String mTextureNames[];
+        @UnsupportedAppUsage
         int mInputCount;
+        @UnsupportedAppUsage
         int mOutputCount;
+        @UnsupportedAppUsage
         int mConstantCount;
+        @UnsupportedAppUsage
         int mTextureCount;
+        @UnsupportedAppUsage
         String mShader;
 
 
+        @UnsupportedAppUsage
         protected BaseProgramBuilder(RenderScript rs) {
             mRS = rs;
             mInputs = new Element[MAX_INPUT];
diff --git a/rs/java/android/renderscript/ProgramFragment.java b/rs/java/android/renderscript/ProgramFragment.java
index 5f71bd1..3dde9b6 100644
--- a/rs/java/android/renderscript/ProgramFragment.java
+++ b/rs/java/android/renderscript/ProgramFragment.java
@@ -16,6 +16,8 @@
 
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
+
 
 /**
  * @hide
@@ -50,6 +52,7 @@
          *
          * @param rs Context to which the program will belong.
          */
+        @UnsupportedAppUsage
         public Builder(RenderScript rs) {
             super(rs);
         }
@@ -60,6 +63,7 @@
          *
          * @return  ProgramFragment
          */
+        @UnsupportedAppUsage
         public ProgramFragment create() {
             mRS.validate();
             long[] tmp = new long[(mInputCount + mOutputCount + mConstantCount + mTextureCount) * 2];
diff --git a/rs/java/android/renderscript/ProgramFragmentFixedFunction.java b/rs/java/android/renderscript/ProgramFragmentFixedFunction.java
index 2b647c76..d05d41d 100644
--- a/rs/java/android/renderscript/ProgramFragmentFixedFunction.java
+++ b/rs/java/android/renderscript/ProgramFragmentFixedFunction.java
@@ -16,6 +16,8 @@
 
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
+
 
 /**
  * @hide
@@ -102,10 +104,12 @@
             /**
              * @deprecated in API 16
              **/
+            @UnsupportedAppUsage
             REPLACE (1),
             /**
              * @deprecated in API 16
              **/
+            @UnsupportedAppUsage
             MODULATE (2),
             /**
              * @deprecated in API 16
@@ -128,6 +132,7 @@
             /**
              * @deprecated in API 16
              **/
+            @UnsupportedAppUsage
             ALPHA (1),
             /**
              * @deprecated in API 16
@@ -136,10 +141,12 @@
             /**
              * @deprecated in API 16
              **/
+            @UnsupportedAppUsage
             RGB (3),
             /**
              * @deprecated in API 16
              **/
+            @UnsupportedAppUsage
             RGBA (4);
 
             int mID;
@@ -228,6 +235,7 @@
          *
          * @param rs Context to which the program will belong.
          */
+        @UnsupportedAppUsage
         public Builder(RenderScript rs) {
             mRS = rs;
             mSlots = new Slot[MAX_TEXTURE];
@@ -248,6 +256,7 @@
          *
          * @return this
          */
+        @UnsupportedAppUsage
         public Builder setTexture(EnvMode env, Format fmt, int slot)
             throws IllegalArgumentException {
             if((slot < 0) || (slot >= MAX_TEXTURE)) {
@@ -277,6 +286,7 @@
          * fragment shader
          *
          **/
+        @UnsupportedAppUsage
         public Builder setVaryingColor(boolean enable) {
             mVaryingColorEnable = enable;
             return this;
@@ -288,6 +298,7 @@
         * state of the builder.
         *
         */
+        @UnsupportedAppUsage
         public ProgramFragmentFixedFunction create() {
             InternalBuilder sb = new InternalBuilder(mRS);
             mNumTextures = 0;
diff --git a/rs/java/android/renderscript/ProgramRaster.java b/rs/java/android/renderscript/ProgramRaster.java
index 8c7c9aa..33000ac 100644
--- a/rs/java/android/renderscript/ProgramRaster.java
+++ b/rs/java/android/renderscript/ProgramRaster.java
@@ -16,6 +16,8 @@
 
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
+
 
 /**
  * @hide
@@ -124,6 +126,7 @@
         /**
          * @deprecated in API 16
          */
+        @UnsupportedAppUsage
         public Builder(RenderScript rs) {
             mRS = rs;
             mPointSprite = false;
@@ -133,6 +136,7 @@
         /**
          * @deprecated in API 16
          */
+        @UnsupportedAppUsage
         public Builder setPointSpriteEnabled(boolean enable) {
             mPointSprite = enable;
             return this;
@@ -149,6 +153,7 @@
         /**
          * @deprecated in API 16
          */
+        @UnsupportedAppUsage
         public ProgramRaster create() {
             mRS.validate();
             long id = mRS.nProgramRasterCreate(mPointSprite, mCullMode.mID);
diff --git a/rs/java/android/renderscript/ProgramStore.java b/rs/java/android/renderscript/ProgramStore.java
index c0fa9c4..622fe21 100644
--- a/rs/java/android/renderscript/ProgramStore.java
+++ b/rs/java/android/renderscript/ProgramStore.java
@@ -16,6 +16,8 @@
 
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
+
 
 /**
  * @hide
@@ -45,11 +47,13 @@
         /**
         * Always drawn
         */
+        @UnsupportedAppUsage
         ALWAYS (0),
         /**
         * Drawn if the incoming depth value is less than that in the
         * depth buffer
         */
+        @UnsupportedAppUsage
         LESS (1),
         /**
         * Drawn if the incoming depth value is less or equal to that in
@@ -93,9 +97,11 @@
     */
     public enum BlendSrcFunc {
         ZERO (0),
+        @UnsupportedAppUsage
         ONE (1),
         DST_COLOR (2),
         ONE_MINUS_DST_COLOR (3),
+        @UnsupportedAppUsage
         SRC_ALPHA (4),
         ONE_MINUS_SRC_ALPHA (5),
         DST_ALPHA (6),
@@ -118,11 +124,14 @@
     *
     */
     public enum BlendDstFunc {
+        @UnsupportedAppUsage
         ZERO (0),
+        @UnsupportedAppUsage
         ONE (1),
         SRC_COLOR (2),
         ONE_MINUS_SRC_COLOR (3),
         SRC_ALPHA (4),
+        @UnsupportedAppUsage
         ONE_MINUS_SRC_ALPHA (5),
         DST_ALPHA (6),
         ONE_MINUS_DST_ALPHA (7);
@@ -299,6 +308,7 @@
     *
     *  @param rs Context to which the program will belong.
     **/
+    @UnsupportedAppUsage
     public static ProgramStore BLEND_ALPHA_DEPTH_NONE(RenderScript rs) {
         if(rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_DEPTH == null) {
             ProgramStore.Builder builder = new ProgramStore.Builder(rs);
@@ -328,6 +338,7 @@
         BlendDstFunc mBlendDst;
         boolean mDither;
 
+        @UnsupportedAppUsage
         public Builder(RenderScript rs) {
             mRS = rs;
             mDepthFunc = DepthFunc.ALWAYS;
@@ -347,6 +358,7 @@
         *
         * @return this
         */
+        @UnsupportedAppUsage
         public Builder setDepthFunc(DepthFunc func) {
             mDepthFunc = func;
             return this;
@@ -360,6 +372,7 @@
         *
         * @return this
         */
+        @UnsupportedAppUsage
         public Builder setDepthMaskEnabled(boolean enable) {
             mDepthMask = enable;
             return this;
@@ -394,6 +407,7 @@
         *
         * @return this
         */
+        @UnsupportedAppUsage
         public Builder setBlendFunc(BlendSrcFunc src, BlendDstFunc dst) {
             mBlendSrc = src;
             mBlendDst = dst;
@@ -408,6 +422,7 @@
         *
         * @return this
         */
+        @UnsupportedAppUsage
         public Builder setDitherEnabled(boolean enable) {
             mDither = enable;
             return this;
@@ -416,6 +431,7 @@
         /**
         * Creates a program store from the current state of the builder
         */
+        @UnsupportedAppUsage
         public ProgramStore create() {
             mRS.validate();
             long id = mRS.nProgramStoreCreate(mColorMaskR, mColorMaskG, mColorMaskB, mColorMaskA,
diff --git a/rs/java/android/renderscript/ProgramVertex.java b/rs/java/android/renderscript/ProgramVertex.java
index 0d7e2d9..83d9ea7 100644
--- a/rs/java/android/renderscript/ProgramVertex.java
+++ b/rs/java/android/renderscript/ProgramVertex.java
@@ -38,6 +38,8 @@
  **/
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
+
 
 /**
  * @hide
@@ -90,6 +92,7 @@
          *
          * @param rs Context to which the program will belong.
          */
+        @UnsupportedAppUsage
         public Builder(RenderScript rs) {
             super(rs);
         }
@@ -102,6 +105,7 @@
          *          structure
          * @return  self
          */
+        @UnsupportedAppUsage
         public Builder addInput(Element e) throws IllegalStateException {
             // Should check for consistant and non-conflicting names...
             if(mInputCount >= MAX_INPUT) {
@@ -120,6 +124,7 @@
          *
          * @return  ProgramVertex
          */
+        @UnsupportedAppUsage
         public ProgramVertex create() {
             mRS.validate();
             long[] tmp = new long[(mInputCount + mOutputCount + mConstantCount + mTextureCount) * 2];
diff --git a/rs/java/android/renderscript/ProgramVertexFixedFunction.java b/rs/java/android/renderscript/ProgramVertexFixedFunction.java
index 45840ae..579d3bb 100644
--- a/rs/java/android/renderscript/ProgramVertexFixedFunction.java
+++ b/rs/java/android/renderscript/ProgramVertexFixedFunction.java
@@ -16,6 +16,8 @@
 
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
+
 
 /**
  * @hide
@@ -38,6 +40,7 @@
      *
      * @param va allocation containing fixed function matrices
      */
+    @UnsupportedAppUsage
     public void bindConstants(Constants va) {
         mRS.validate();
         bindConstants(va.getAllocation(), 0);
@@ -118,6 +121,7 @@
          *
          * @param rs Context to which the program will belong.
          */
+        @UnsupportedAppUsage
         public Builder(RenderScript rs) {
             mRS = rs;
         }
@@ -170,6 +174,7 @@
          *
          * @return Fixed function emulation ProgramVertex
          */
+        @UnsupportedAppUsage
         public ProgramVertexFixedFunction create() {
             buildShaderString();
 
@@ -215,6 +220,7 @@
         *
         * @param rs Context to which the allocation will belong.
         **/
+        @UnsupportedAppUsage
         public Constants(RenderScript rs) {
             Type constInputType = ProgramVertexFixedFunction.Builder.getConstantInputType(rs);
             mAlloc = Allocation.createTyped(rs, constInputType);
@@ -268,6 +274,7 @@
         *
         * @param m projection matrix
         */
+        @UnsupportedAppUsage
         public void setProjection(Matrix4f m) {
             mProjection.load(m);
             addToBuffer(PROJECTION_OFFSET*4, m);
diff --git a/rs/java/android/renderscript/RSSurfaceView.java b/rs/java/android/renderscript/RSSurfaceView.java
index 5db72d9..561373c 100644
--- a/rs/java/android/renderscript/RSSurfaceView.java
+++ b/rs/java/android/renderscript/RSSurfaceView.java
@@ -16,6 +16,7 @@
 
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.SurfaceHolder;
@@ -42,6 +43,7 @@
      * must call {@link android.opengl.GLSurfaceView#setRenderer} to
      * register a renderer.
      */
+    @UnsupportedAppUsage
     public RSSurfaceView(Context context) {
         super(context);
         init();
@@ -54,6 +56,7 @@
      * must call {@link android.opengl.GLSurfaceView#setRenderer} to
      * register a renderer.
      */
+    @UnsupportedAppUsage
     public RSSurfaceView(Context context, AttributeSet attrs) {
         super(context, attrs);
         init();
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 0f22568..4293157 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -22,6 +22,7 @@
 import java.util.ArrayList;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.AssetManager;
 import android.graphics.Bitmap;
@@ -103,6 +104,7 @@
      * Detect the bitness of the VM to allow FieldPacker to do the right thing.
      */
     static native int rsnSystemGetPointerSize();
+    @UnsupportedAppUsage
     static int sPointerSize;
 
     static {
@@ -153,6 +155,7 @@
      * @return Always return 1
      *
      */
+    @UnsupportedAppUsage
     public static long getMinorID() {
         return 1;
     }
@@ -833,6 +836,7 @@
 
     native long rsnScriptCCreate(long con, String resName, String cacheDir,
                                  byte[] script, int length);
+    @UnsupportedAppUsage
     synchronized long nScriptCCreate(String resName, String cacheDir, byte[] script, int length) {
         validate();
         return rsnScriptCCreate(mContext, resName, cacheDir, script, length);
@@ -1158,6 +1162,7 @@
      * sendToClient} by scripts from this context.
      *
      */
+    @UnsupportedAppUsage
     RSMessageHandler mMessageCallback = null;
 
     public void setMessageHandler(RSMessageHandler msg) {
@@ -1232,6 +1237,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     void validate() {
         if (mContext == 0) {
             throw new RSInvalidStateException("Calling RS with no Context active.");
@@ -1495,6 +1501,7 @@
      * @param sdkVersion The target SDK Version.
      * @return RenderScript
      */
+    @UnsupportedAppUsage
     public static RenderScript create(Context ctx, int sdkVersion) {
         return create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE);
     }
@@ -1508,6 +1515,7 @@
      * @param flags The OR of the CREATE_FLAG_* options desired
      * @return RenderScript
      */
+    @UnsupportedAppUsage
     private static RenderScript create(Context ctx, int sdkVersion, ContextType ct, int flags) {
         if (sdkVersion < 23) {
             return internalCreate(ctx, sdkVersion, ct, flags);
diff --git a/rs/java/android/renderscript/RenderScriptCacheDir.java b/rs/java/android/renderscript/RenderScriptCacheDir.java
index 95a9d75..1797bef 100644
--- a/rs/java/android/renderscript/RenderScriptCacheDir.java
+++ b/rs/java/android/renderscript/RenderScriptCacheDir.java
@@ -16,6 +16,7 @@
 
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
 import java.io.File;
 
 /**
@@ -30,11 +31,13 @@
      * @hide
      * @param cacheDir A directory the current process can write to
      */
+    @UnsupportedAppUsage
     public static void setupDiskCache(File cacheDir) {
         // Defer creation of cache path to nScriptCCreate().
         mCacheDir = cacheDir;
     }
 
+    @UnsupportedAppUsage
     static File mCacheDir;
 
 }
diff --git a/rs/java/android/renderscript/RenderScriptGL.java b/rs/java/android/renderscript/RenderScriptGL.java
index be1f899..6fac83e 100644
--- a/rs/java/android/renderscript/RenderScriptGL.java
+++ b/rs/java/android/renderscript/RenderScriptGL.java
@@ -16,6 +16,7 @@
 
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.SurfaceTexture;
 import android.view.Surface;
@@ -65,6 +66,7 @@
         /**
          * @deprecated in API 16
          */
+        @UnsupportedAppUsage
         public SurfaceConfig() {
         }
 
@@ -132,6 +134,7 @@
          * @param minimum
          * @param preferred
          */
+        @UnsupportedAppUsage
         public void setDepth(int minimum, int preferred) {
             validateRange(minimum, preferred, 0, 24);
             mDepthMin = minimum;
@@ -169,6 +172,7 @@
      * @param ctx The context.
      * @param sc The desired format of the primary rendering surface.
      */
+    @UnsupportedAppUsage
     public RenderScriptGL(Context ctx, SurfaceConfig sc) {
         super(ctx);
         mSurfaceConfig = new SurfaceConfig(sc);
@@ -202,6 +206,7 @@
      * @param h
      * @param sur
      */
+    @UnsupportedAppUsage
     public void setSurface(SurfaceHolder sur, int w, int h) {
         validate();
         Surface s = null;
@@ -281,6 +286,7 @@
      *
      * @param s Graphics script to process rendering requests.
      */
+    @UnsupportedAppUsage
     public void bindRootScript(Script s) {
         validate();
         nContextBindRootScript((int)safeID(s));
@@ -293,6 +299,7 @@
      *
      * @param p
      */
+    @UnsupportedAppUsage
     public void bindProgramStore(ProgramStore p) {
         validate();
         nContextBindProgramStore((int)safeID(p));
@@ -317,6 +324,7 @@
      *
      * @param p
      */
+    @UnsupportedAppUsage
     public void bindProgramRaster(ProgramRaster p) {
         validate();
         nContextBindProgramRaster((int)safeID(p));
@@ -329,6 +337,7 @@
      *
      * @param p
      */
+    @UnsupportedAppUsage
     public void bindProgramVertex(ProgramVertex p) {
         validate();
         nContextBindProgramVertex((int)safeID(p));
diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java
index d0d9a11..9ad9aea 100644
--- a/rs/java/android/renderscript/Script.java
+++ b/rs/java/android/renderscript/Script.java
@@ -16,6 +16,7 @@
 
 package android.renderscript;
 
+import android.annotation.UnsupportedAppUsage;
 import android.util.SparseArray;
 
 /**
@@ -475,8 +476,10 @@
      *
      */
     public static class Builder {
+        @UnsupportedAppUsage
         RenderScript mRS;
 
+        @UnsupportedAppUsage
         Builder(RenderScript rs) {
             mRS = rs;
         }
diff --git a/sax/tests/saxtests/src/android/sax/ExpatPerformanceTest.java b/sax/tests/saxtests/src/android/sax/ExpatPerformanceTest.java
deleted file mode 100644
index 892c490..0000000
--- a/sax/tests/saxtests/src/android/sax/ExpatPerformanceTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.sax;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-import android.util.Xml;
-import org.kxml2.io.KXmlParser;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import com.android.frameworks.saxtests.R;
-
-public class ExpatPerformanceTest extends AndroidTestCase {
-
-    private static final String TAG = ExpatPerformanceTest.class.getSimpleName();
-
-    private byte[] mXmlBytes;
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        InputStream in = mContext.getResources().openRawResource(R.raw.youtube);
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        byte[] buffer = new byte[1024];
-        int length;
-        while ((length = in.read(buffer)) != -1) {
-            out.write(buffer, 0, length);
-        }
-        mXmlBytes = out.toByteArray();
-
-        Log.i("***", "File size: " + (mXmlBytes.length / 1024) + "k");
-    }
-
-    @LargeTest
-    public void testPerformance() throws Exception {
-//        try {
-//            Debug.startMethodTracing("expat3");
-//        for (int i = 0; i < 1; i++) {
-            runJavaPullParser();
-            runSax();
-            runExpatPullParser();
-//        }
-//    } finally {
-//            Debug.stopMethodTracing();
-//        }
-    }
-
-    private InputStream newInputStream() {
-        return new ByteArrayInputStream(mXmlBytes);
-    }
-
-    private void runSax() throws IOException, SAXException {
-        long start = System.currentTimeMillis();
-        Xml.parse(newInputStream(), Xml.Encoding.UTF_8, new DefaultHandler());
-        long elapsed = System.currentTimeMillis() - start;
-        Log.i(TAG, "expat SAX: " + elapsed + "ms");
-    }
-
-    private void runExpatPullParser() throws XmlPullParserException, IOException {
-        long start = System.currentTimeMillis();
-        XmlPullParser pullParser = Xml.newPullParser();
-        pullParser.setInput(newInputStream(), "UTF-8");
-        withPullParser(pullParser);
-        long elapsed = System.currentTimeMillis() - start;
-        Log.i(TAG, "expat pull: " + elapsed + "ms");
-    }
-
-    private void runJavaPullParser() throws XmlPullParserException, IOException {
-        XmlPullParser pullParser;
-        long start = System.currentTimeMillis();
-        pullParser = new KXmlParser();
-        pullParser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-        pullParser.setInput(newInputStream(), "UTF-8");
-        withPullParser(pullParser);
-        long elapsed = System.currentTimeMillis() - start;
-        Log.i(TAG, "java pull parser: " + elapsed + "ms");
-    }
-
-    private static void withPullParser(XmlPullParser pullParser)
-            throws IOException, XmlPullParserException {
-        int eventType = pullParser.next();
-        while (eventType != XmlPullParser.END_DOCUMENT) {
-            switch (eventType) {
-                case XmlPullParser.START_TAG:
-                    pullParser.getName();
-//                        int nattrs = pullParser.getAttributeCount();
-//                        for (int i = 0; i < nattrs; ++i) {
-//                            pullParser.getAttributeName(i);
-//                            pullParser.getAttributeValue(i);
-//                        }
-                    break;
-                case XmlPullParser.END_TAG:
-                    pullParser.getName();
-                    break;
-                case XmlPullParser.TEXT:
-                    pullParser.getText();
-                    break;
-            }
-            eventType = pullParser.next();
-        }
-    }
-}
diff --git a/services/Android.bp b/services/Android.bp
index d125adc..bea51be 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -2,6 +2,7 @@
 // ============================================================
 java_library {
     name: "services",
+    installable: true,
 
     dex_preopt: {
         app_image: true,
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 28aa984..46c515f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -860,7 +860,10 @@
         }
         final long identity = Binder.clearCallingIdentity();
         try {
-            return mSystemSupport.getMagnificationController().reset(animate);
+            MagnificationController magnificationController =
+                    mSystemSupport.getMagnificationController();
+            return (magnificationController.reset(animate)
+                    || !magnificationController.isMagnifying());
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
index b95d2e6..075c2fe 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
@@ -235,14 +235,15 @@
      * callback on mListener is called, and the return value of the callback is
      * passed to the caller.
      *
-     * @param event The raw motion event.  It's important that this be the raw
+     * @param event The transformed motion event to be handled.
+     * @param rawEvent The raw motion event.  It's important that this be the raw
      * event, before any transformations have been applied, so that measurements
      * can be made in physical units.
      * @param policyFlags Policy flags for the event.
      *
      * @return true if the event is consumed, else false
      */
-    public boolean onMotionEvent(MotionEvent event, int policyFlags) {
+    public boolean onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
 
         // Construct GestureDetector double-tap detector on demand, so that testable sub-class
         // can use mock GestureDetector.
@@ -255,12 +256,14 @@
             mGestureDetector.setOnDoubleTapListener(this);
         }
 
-        final float x = event.getX();
-        final float y = event.getY();
-        final long time = event.getEventTime();
+        // The accessibility gesture detector is interested in the movements in physical space,
+        // so it uses the rawEvent to ignore magnification and other transformations.
+        final float x = rawEvent.getX();
+        final float y = rawEvent.getY();
+        final long time = rawEvent.getEventTime();
 
         mPolicyFlags = policyFlags;
-        switch (event.getActionMasked()) {
+        switch (rawEvent.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
                 mDoubleTapDetected = false;
                 mSecondFingerDoubleTap = false;
@@ -311,7 +314,7 @@
                         // timeout, cancel gesture detection.
                         if (timeDelta > threshold) {
                             cancelGesture();
-                            return mListener.onGestureCancelled(event, policyFlags);
+                            return mListener.onGestureCancelled(rawEvent, policyFlags);
                         }
                     }
 
@@ -327,7 +330,7 @@
 
             case MotionEvent.ACTION_UP:
                 if (mDoubleTapDetected) {
-                    return finishDoubleTap(event, policyFlags);
+                    return finishDoubleTap(rawEvent, policyFlags);
                 }
                 if (mGestureStarted) {
                     final float dX = Math.abs(x - mPreviousGestureX);
@@ -335,7 +338,7 @@
                     if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
                         mStrokeBuffer.add(new GesturePoint(x, y, time));
                     }
-                    return recognizeGesture(event, policyFlags);
+                    return recognizeGesture(rawEvent, policyFlags);
                 }
                 break;
 
@@ -344,7 +347,7 @@
                 // recognizing a gesture.
                 cancelGesture();
 
-                if (event.getPointerCount() == 2) {
+                if (rawEvent.getPointerCount() == 2) {
                     // If this was the second finger, attempt to recognize double
                     // taps on it.
                     mSecondFingerDoubleTap = true;
@@ -360,7 +363,7 @@
                 // If we're detecting taps on the second finger, see if we
                 // should finish the double tap.
                 if (mSecondFingerDoubleTap && mDoubleTapDetected) {
-                    return finishDoubleTap(event, policyFlags);
+                    return finishDoubleTap(rawEvent, policyFlags);
                 }
                 break;
 
@@ -372,7 +375,7 @@
         // If we're detecting taps on the second finger, map events from the
         // finger to the first finger.
         if (mSecondFingerDoubleTap) {
-            MotionEvent newEvent = mapSecondPointerToFirstPointer(event);
+            MotionEvent newEvent = mapSecondPointerToFirstPointer(rawEvent);
             if (newEvent == null) {
                 return false;
             }
@@ -385,7 +388,7 @@
             return false;
         }
 
-        // Pass the event on to the standard gesture detector.
+        // Pass the transformed event on to the standard gesture detector.
         return mGestureDetector.onTouchEvent(event);
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 11b2343..cf08681 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -22,10 +22,8 @@
 import android.util.DebugUtils;
 import android.util.ExceptionUtils;
 import android.util.Log;
-import android.util.Pools.SimplePool;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
-import android.view.Choreographer;
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputFilter;
@@ -104,31 +102,12 @@
             | FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION
             | FLAG_FEATURE_SCREEN_MAGNIFIER | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
 
-    private final Runnable mProcessBatchedEventsRunnable = new Runnable() {
-        @Override
-        public void run() {
-            final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
-            if (DEBUG) {
-                Slog.i(TAG, "Begin batch processing for frame: " + frameTimeNanos);
-            }
-            processBatchedEvents(frameTimeNanos);
-            if (DEBUG) {
-                Slog.i(TAG, "End batch processing.");
-            }
-            if (mEventQueue != null) {
-                scheduleProcessBatchedEvents();
-            }
-        }
-    };
-
     private final Context mContext;
 
     private final PowerManager mPm;
 
     private final AccessibilityManagerService mAms;
 
-    private final Choreographer mChoreographer;
-
     private boolean mInstalled;
 
     private int mUserId;
@@ -147,8 +126,6 @@
 
     private EventStreamTransformation mEventHandler;
 
-    private MotionEventHolder mEventQueue;
-
     private EventStreamState mMouseStreamState;
 
     private EventStreamState mTouchScreenStreamState;
@@ -160,7 +137,6 @@
         mContext = context;
         mAms = service;
         mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mChoreographer = Choreographer.getInstance();
     }
 
     @Override
@@ -274,7 +250,7 @@
             return;
         }
 
-        batchMotionEvent(event, policyFlags);
+        handleMotionEvent(event, policyFlags);
     }
 
     private void processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags) {
@@ -285,68 +261,14 @@
         mEventHandler.onKeyEvent(event, policyFlags);
     }
 
-    private void scheduleProcessBatchedEvents() {
-        mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
-                mProcessBatchedEventsRunnable, null);
-    }
-
-    private void batchMotionEvent(MotionEvent event, int policyFlags) {
-        if (DEBUG) {
-            Slog.i(TAG, "Batching event: " + event + ", policyFlags: " + policyFlags);
-        }
-        if (mEventQueue == null) {
-            mEventQueue = MotionEventHolder.obtain(event, policyFlags);
-            scheduleProcessBatchedEvents();
-            return;
-        }
-        if (mEventQueue.event.addBatch(event)) {
-            return;
-        }
-        MotionEventHolder holder = MotionEventHolder.obtain(event, policyFlags);
-        holder.next = mEventQueue;
-        mEventQueue.previous = holder;
-        mEventQueue = holder;
-    }
-
-    private void processBatchedEvents(long frameNanos) {
-        MotionEventHolder current = mEventQueue;
-        if (current == null) {
-            return;
-        }
-        while (current.next != null) {
-            current = current.next;
-        }
-        while (true) {
-            if (current == null) {
-                mEventQueue = null;
-                break;
-            }
-            if (current.event.getEventTimeNano() >= frameNanos) {
-                // Finished with this choreographer frame. Do the rest on the next one.
-                current.next = null;
-                break;
-            }
-            handleMotionEvent(current.event, current.policyFlags);
-            MotionEventHolder prior = current;
-            current = current.previous;
-            prior.recycle();
-        }
-    }
-
     private void handleMotionEvent(MotionEvent event, int policyFlags) {
         if (DEBUG) {
-            Slog.i(TAG, "Handling batched event: " + event + ", policyFlags: " + policyFlags);
+            Slog.i(TAG, "Handling motion event: " + event + ", policyFlags: " + policyFlags);
         }
-        // Since we do batch processing it is possible that by the time the
-        // next batch is processed the event handle had been set to null.
-        if (mEventHandler != null) {
-            mPm.userActivity(event.getEventTime(), false);
-            MotionEvent transformedEvent = MotionEvent.obtain(event);
-            mEventHandler.onMotionEvent(transformedEvent, event, policyFlags);
-            transformedEvent.recycle();
-        } else {
-            if (DEBUG) Slog.d(TAG, "mEventHandler == null for " + event);
-        }
+        mPm.userActivity(event.getEventTime(), false);
+        MotionEvent transformedEvent = MotionEvent.obtain(event);
+        mEventHandler.onMotionEvent(transformedEvent, event, policyFlags);
+        transformedEvent.recycle();
     }
 
     @Override
@@ -469,9 +391,6 @@
     }
 
     private void disableFeatures() {
-        // Give the features a chance to process any batched events so we'll keep a consistent
-        // event stream
-        processBatchedEvents(Long.MAX_VALUE);
         if (mMotionEventInjector != null) {
             mAms.setMotionEventInjector(null);
             mMotionEventInjector.onDestroy();
@@ -515,36 +434,6 @@
         /* ignore */
     }
 
-    private static class MotionEventHolder {
-        private static final int MAX_POOL_SIZE = 32;
-        private static final SimplePool<MotionEventHolder> sPool =
-                new SimplePool<MotionEventHolder>(MAX_POOL_SIZE);
-
-        public int policyFlags;
-        public MotionEvent event;
-        public MotionEventHolder next;
-        public MotionEventHolder previous;
-
-        public static MotionEventHolder obtain(MotionEvent event, int policyFlags) {
-            MotionEventHolder holder = sPool.acquire();
-            if (holder == null) {
-                holder = new MotionEventHolder();
-            }
-            holder.event = MotionEvent.obtain(event);
-            holder.policyFlags = policyFlags;
-            return holder;
-        }
-
-        public void recycle() {
-            event.recycle();
-            event = null;
-            policyFlags = 0;
-            next = null;
-            previous = null;
-            sPool.release(this);
-        }
-    }
-
     /**
      * Keeps state of event streams observed for an input device with a certain source.
      * Provides information about whether motion and key events should be processed by accessibility
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b2c1bed..acbb67c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,13 +16,18 @@
 
 package com.android.server.accessibility;
 
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
-
 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_WITH_HARD_KEYBOARD;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
 
 import android.Manifest;
 import android.accessibilityservice.AccessibilityService;
@@ -30,7 +35,6 @@
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManagerInternal;
 import android.app.AlertDialog;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
@@ -111,10 +115,10 @@
 import com.android.internal.util.IntPair;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
-import libcore.util.EmptyArray;
-
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.FileDescriptor;
@@ -133,6 +137,8 @@
 import java.util.function.Consumer;
 import java.util.function.IntSupplier;
 
+import libcore.util.EmptyArray;
+
 /**
  * This class is instantiated by the system as a system level service and can be
  * accessed only by the system. The task of this service is to be a centralized
@@ -261,6 +267,25 @@
         return getUserStateLocked(mCurrentUserId);
     }
 
+    public static final class Lifecycle extends SystemService {
+        private final AccessibilityManagerService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+            mService = new AccessibilityManagerService(context);
+        }
+
+        @Override
+        public void onStart() {
+            publishBinderService(Context.ACCESSIBILITY_SERVICE, mService);
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            mService.onBootPhase(phase);
+        }
+    }
+
     /**
      * Creates a new instance.
      *
@@ -297,6 +322,14 @@
         return mFingerprintGestureDispatcher;
     }
 
+    private void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
+                mAppWidgetService = LocalServices.getService(AppWidgetManagerInternal.class);
+            }
+        }
+    }
+
     private UserState getUserState(int userId) {
         synchronized (mLock) {
             return getUserStateLocked(userId);
@@ -668,6 +701,7 @@
     public int addAccessibilityInteractionConnection(IWindow windowToken,
             IAccessibilityInteractionConnection connection, String packageName,
             int userId) throws RemoteException {
+        final int windowId;
         synchronized (mLock) {
             // We treat calls from a profile as if made by its parent as profiles
             // share the accessibility state of the parent. The call below
@@ -680,7 +714,7 @@
             packageName = mSecurityPolicy.resolveValidReportedPackageLocked(
                     packageName, UserHandle.getCallingAppId(), resolvedUserId);
 
-            final int windowId = sNextWindowId++;
+            windowId = sNextWindowId++;
             // If the window is from a process that runs across users such as
             // the system UI or the system we add it to the global state that
             // is shared across users.
@@ -708,8 +742,10 @@
                             + " and  token: " + windowToken.asBinder());
                 }
             }
-            return windowId;
         }
+        WindowManagerInternal wm = LocalServices.getService(WindowManagerInternal.class);
+        wm.computeWindowsForAccessibility();
+        return windowId;
     }
 
     @Override
@@ -1780,7 +1816,6 @@
         updateDisplayDaltonizerLocked(userState);
         updateDisplayInversionLocked(userState);
         updateMagnificationLocked(userState);
-        updateSoftKeyboardShowModeLocked(userState);
         scheduleUpdateFingerprintGestureHandling(userState);
         scheduleUpdateInputFilter(userState);
         scheduleUpdateClientsIfNeededLocked(userState);
@@ -1974,18 +2009,6 @@
         return false;
     }
 
-    private boolean readSoftKeyboardShowModeChangedLocked(UserState userState) {
-        final int softKeyboardShowMode = Settings.Secure.getIntForUser(
-                mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0,
-                userState.mUserId);
-        if (softKeyboardShowMode != userState.mSoftKeyboardShowMode) {
-            userState.mSoftKeyboardShowMode = softKeyboardShowMode;
-            return true;
-        }
-        return false;
-    }
-
     private void updateTouchExplorationLocked(UserState userState) {
         boolean enabled = mUiAutomationManager.isTouchExplorationEnabledLocked();
         final int serviceCount = userState.mBoundServices.size();
@@ -2184,34 +2207,6 @@
         return false;
     }
 
-    private void updateSoftKeyboardShowModeLocked(UserState userState) {
-        final int userId = userState.mUserId;
-        // Only check whether we need to reset the soft keyboard mode if it is not set to the
-        // default.
-        if ((userId == mCurrentUserId) && (userState.mSoftKeyboardShowMode != 0)) {
-            // Check whether the last AccessibilityService that changed the soft keyboard mode to
-            // something other than the default is still enabled and, if not, remove flag and
-            // reset to the default soft keyboard behavior.
-            boolean serviceChangingSoftKeyboardModeIsEnabled =
-                    userState.mEnabledServices.contains(userState.mServiceChangingSoftKeyboardMode);
-
-            if (!serviceChangingSoftKeyboardModeIsEnabled) {
-                final long identity = Binder.clearCallingIdentity();
-                try {
-                    Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                            Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                            0,
-                            userState.mUserId);
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
-                }
-                userState.mSoftKeyboardShowMode = 0;
-                userState.mServiceChangingSoftKeyboardMode = null;
-                notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
-            }
-        }
-    }
-
     private void updateFingerprintGestureHandling(UserState userState) {
         final List<AccessibilityServiceConnection> services;
         synchronized (mLock) {
@@ -2446,6 +2441,15 @@
         }
     }
 
+    private void putSecureIntForUser(String key, int value, int userid) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userid);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     class RemoteAccessibilityConnection implements DeathRecipient {
         private final int mUid;
         private final String mPackageName;
@@ -2624,7 +2628,7 @@
                     PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0);
 
             if (activityToken != null) {
-                LocalServices.getService(ActivityManagerInternal.class)
+                LocalServices.getService(ActivityTaskManagerInternal.class)
                         .setFocusedActivity(activityToken);
             }
             connection.mConnection.performAccessibilityAction(accessibilityNodeId, action,
@@ -2685,16 +2689,6 @@
         }
     }
 
-    private AppWidgetManagerInternal getAppWidgetManager() {
-        synchronized (mLock) {
-            if (mAppWidgetService == null
-                    && mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
-                mAppWidgetService = LocalServices.getService(AppWidgetManagerInternal.class);
-            }
-            return mAppWidgetService;
-        }
-    }
-
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
             FileDescriptor err, String[] args, ShellCallback callback,
@@ -3023,8 +3017,7 @@
                 return packageName.toString();
             }
             // Appwidget hosts get to pass packages for widgets they host
-            final AppWidgetManagerInternal appWidgetManager = getAppWidgetManager();
-            if (appWidgetManager != null && ArrayUtils.contains(appWidgetManager
+            if (mAppWidgetService != null && ArrayUtils.contains(mAppWidgetService
                             .getHostedWidgetPackages(resolvedUid), packageNameStr)) {
                 return packageName.toString();
             }
@@ -3052,9 +3045,8 @@
             // IMPORTANT: The target package is already vetted to be in the target UID
             String[] uidPackages = new String[]{targetPackage};
             // Appwidget hosts get to pass packages for widgets they host
-            final AppWidgetManagerInternal appWidgetManager = getAppWidgetManager();
-            if (appWidgetManager != null) {
-                final ArraySet<String> widgetPackages = appWidgetManager
+            if (mAppWidgetService != null) {
+                final ArraySet<String> widgetPackages = mAppWidgetService
                         .getHostedWidgetPackages(targetUid);
                 if (widgetPackages != null && !widgetPackages.isEmpty()) {
                     final String[] validPackages = new String[uidPackages.length
@@ -3668,7 +3660,7 @@
 
         public int mLastSentClientState = -1;
 
-        public int mSoftKeyboardShowMode = 0;
+        private int mSoftKeyboardShowMode = 0;
 
         public boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
         public ComponentName mServiceAssignedToAccessibilityButton;
@@ -3729,7 +3721,6 @@
             mServiceAssignedToAccessibilityButton = null;
             mIsNavBarMagnificationAssignedToAccessibilityButton = false;
             mIsAutoclickEnabled = false;
-            mSoftKeyboardShowMode = 0;
         }
 
         public void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
@@ -3751,6 +3742,11 @@
         public void removeServiceLocked(AccessibilityServiceConnection serviceConnection) {
             mBoundServices.remove(serviceConnection);
             serviceConnection.onRemoved();
+            if ((mServiceChangingSoftKeyboardMode != null)
+                    && (mServiceChangingSoftKeyboardMode.equals(
+                            serviceConnection.getServiceInfo().getComponentName()))) {
+                setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+            }
             // It may be possible to bind a service twice, which confuses the map. Rebuild the map
             // to make sure we can still reach a service
             mComponentNameToServiceMap.clear();
@@ -3777,6 +3773,134 @@
         public Set<ComponentName> getBindingServicesLocked() {
             return mBindingServices;
         }
+
+        public int getSoftKeyboardShowMode() {
+            return mSoftKeyboardShowMode;
+        }
+
+        /**
+         * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings.
+         * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system
+         * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting
+         * setting can be changed by the user, and prevents the system from suppressing the soft
+         * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer
+         * to the user's preference, if they have supplied one.
+         *
+         * @param newMode The new mode
+         * @param requester The service requesting the change, so we can undo it when the
+         *                  service stops. Set to null if something other than a service is forcing
+         *                  the change.
+         *
+         * @return Whether or not the soft keyboard mode equals the new mode after the call
+         */
+        public boolean setSoftKeyboardModeLocked(int newMode, @Nullable ComponentName requester) {
+            if ((newMode != SHOW_MODE_AUTO) && (newMode != SHOW_MODE_HIDDEN)
+                    && (newMode != SHOW_MODE_WITH_HARD_KEYBOARD))
+            {
+                Slog.w(LOG_TAG, "Invalid soft keyboard mode");
+                return false;
+            }
+            if (mSoftKeyboardShowMode == newMode) return true;
+
+            if (newMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+                if (hasUserOverriddenHardKeyboardSettingLocked()) {
+                    // The user has specified a default for this setting
+                    return false;
+                }
+                // Save the original value. But don't do this if the value in settings is already
+                // the new mode. That happens when we start up after a reboot, and we don't want
+                // to overwrite the value we had from when we first started controlling the setting.
+                if (getSoftKeyboardValueFromSettings() != SHOW_MODE_WITH_HARD_KEYBOARD) {
+                    setOriginalHardKeyboardValue(
+                            Settings.Secure.getInt(mContext.getContentResolver(),
+                                    Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0);
+                }
+                putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
+            } else if (mSoftKeyboardShowMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+                putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+                        getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
+            }
+
+            saveSoftKeyboardValueToSettings(newMode);
+            mSoftKeyboardShowMode = newMode;
+            mServiceChangingSoftKeyboardMode = requester;
+            notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode);
+            return true;
+        }
+
+        /**
+         * If the settings are inconsistent with the internal state, make the internal state
+         * match the settings.
+         */
+        public void reconcileSoftKeyboardModeWithSettingsLocked() {
+            final ContentResolver cr = mContext.getContentResolver();
+            final boolean showWithHardKeyboardSettings =
+                    Settings.Secure.getInt(cr, Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
+            if (mSoftKeyboardShowMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+                if (!showWithHardKeyboardSettings) {
+                    // The user has overridden the setting. Respect that and prevent further changes
+                    // to this behavior.
+                    setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+                    setUserOverridesHardKeyboardSettingLocked();
+                }
+            }
+
+            // If the setting and the internal state are out of sync, set both to default
+            if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode)
+            {
+                Slog.e(LOG_TAG,
+                        "Show IME setting inconsistent with internal state. Overwriting");
+                setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+                putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                        SHOW_MODE_AUTO, mUserId);
+            }
+        }
+
+        private void setUserOverridesHardKeyboardSettingLocked() {
+            final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                    softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
+                    mUserId);
+        }
+
+        private boolean hasUserOverriddenHardKeyboardSettingLocked() {
+            final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+            return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN)
+                    != 0;
+        }
+
+        private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) {
+            final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+            final int newSoftKeyboardSetting = oldSoftKeyboardSetting
+                    & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE)
+                    | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0);
+            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                    newSoftKeyboardSetting, mUserId);
+        }
+
+        private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) {
+            final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+            final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK)
+                    | softKeyboardShowMode;
+            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                    newSoftKeyboardSetting, mUserId);
+        }
+
+        private int getSoftKeyboardValueFromSettings() {
+            return Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                    SHOW_MODE_AUTO) & SHOW_MODE_MASK;
+        }
+
+        private boolean getOriginalHardKeyboardValue() {
+            return (Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0)
+                    & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0;
+        }
     }
 
     private final class AccessibilityContentObserver extends ContentObserver {
@@ -3814,6 +3938,9 @@
         private final Uri mAccessibilitySoftKeyboardModeUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
 
+        private final Uri mShowImeWithHardKeyboardUri = Settings.Secure.getUriFor(
+                Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
+
         private final Uri mAccessibilityShortcutServiceIdUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
 
@@ -3849,6 +3976,8 @@
             contentResolver.registerContentObserver(
                     mAccessibilitySoftKeyboardModeUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
+                    mShowImeWithHardKeyboardUri, false, this, UserHandle.USER_ALL);
+            contentResolver.registerContentObserver(
                     mAccessibilityShortcutServiceIdUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
                     mAccessibilityButtonComponentIdUri, false, this, UserHandle.USER_ALL);
@@ -3891,11 +4020,9 @@
                     if (readHighTextContrastEnabledSettingLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
-                } else if (mAccessibilitySoftKeyboardModeUri.equals(uri)) {
-                    if (readSoftKeyboardShowModeChangedLocked(userState)) {
-                        notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
-                        onUserStateChangedLocked(userState);
-                    }
+                } else if (mAccessibilitySoftKeyboardModeUri.equals(uri)
+                        || mShowImeWithHardKeyboardUri.equals(uri)) {
+                    userState.reconcileSoftKeyboardModeWithSettingsLocked();
                 } else if (mAccessibilityShortcutServiceIdUri.equals(uri)) {
                     if (readAccessibilityShortcutSettingLocked(userState)) {
                         onUserStateChangedLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 105df92..e0eb269 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -111,6 +111,7 @@
         UserState userState = mUserStateWeakReference.get();
         if (userState == null) return;
         userState.removeServiceLocked(this);
+        mSystemSupport.getMagnificationController().resetIfNeeded(mId);
         resetLocked();
     }
 
@@ -218,26 +219,20 @@
             if (!isCalledForCurrentUserLocked()) {
                 return false;
             }
+            final UserState userState = mUserStateWeakReference.get();
+            if (userState == null) return false;
+            return userState.setSoftKeyboardModeLocked(showMode, mComponentName);
         }
-        UserState userState = mUserStateWeakReference.get();
-        if (userState == null) return false;
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            // Keep track of the last service to request a non-default show mode. The show mode
-            // should be restored to default should this service be disabled.
-            userState.mServiceChangingSoftKeyboardMode = (showMode == SHOW_MODE_AUTO)
-                    ? null : mComponentName;
-
-            Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, showMode,
-                    userState.mUserId);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-        return true;
     }
 
     @Override
+    public int getSoftKeyboardShowMode() {
+        final UserState userState = mUserStateWeakReference.get();
+        return (userState != null) ? userState.getSoftKeyboardShowMode() : 0;
+    }
+
+
+    @Override
     public boolean isAccessibilityButtonAvailable() {
         synchronized (mLock) {
             if (!isCalledForCurrentUserLocked()) {
@@ -258,13 +253,12 @@
                 return;
             }
             mWasConnectedAndDied = true;
+            mSystemSupport.getKeyEventDispatcher().flush(this);
             UserState userState = mUserStateWeakReference.get();
             if (userState != null) {
                 userState.serviceDisconnectedLocked(this);
             }
-            if (mId == mSystemSupport.getMagnificationController().getIdOfLastServiceToMagnify()) {
-                mSystemSupport.getMagnificationController().resetIfNeeded(true);
-            }
+            mSystemSupport.getMagnificationController().resetIfNeeded(mId);
             mSystemSupport.onClientChange(false);
         }
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index b697434..b938f3b 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -722,6 +722,19 @@
         }
     }
 
+    /**
+     * Resets magnification if last magnifying service is disabled.
+     *
+     * @param connectionId the connection ID be disabled.
+     * @return {@code true} on success, {@code false} on failure
+     */
+    boolean resetIfNeeded(int connectionId) {
+        if (mIdOfLastServiceToMagnify == connectionId) {
+            return resetIfNeeded(true /*animate*/);
+        }
+        return false;
+    }
+
     void setForceShowMagnifiableBounds(boolean show) {
         if (mRegistered) {
             mWindowManager.setForceShowMagnifiableBounds(show);
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 6c6dd5b..8d691ff 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -247,6 +247,9 @@
         if (mScreenStateReceiver != null) {
             mScreenStateReceiver.unregister();
         }
+        // Check if need to reset when MagnificationGestureHandler is the last magnifying service.
+        mMagnificationController.resetIfNeeded(
+                AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
         clearAndTransitionToStateDetecting();
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 84a8d45..347a084 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -268,10 +268,7 @@
 
         mReceivedPointerTracker.onMotionEvent(rawEvent);
 
-        // The motion detector is interested in the movements in physical space,
-        // so it uses the rawEvent to ignore magnification and other
-        // transformations.
-        if (mGestureDetector.onMotionEvent(rawEvent, policyFlags)) {
+        if (mGestureDetector.onMotionEvent(event, rawEvent, policyFlags)) {
             // Event was handled by the gesture detector.
             return;
         }
@@ -378,6 +375,7 @@
             return false;
         }
 
+        mAms.onTouchInteractionEnd();
         // Remove pending event deliveries.
         mSendHoverEnterAndMoveDelayed.cancel();
         mSendHoverExitDelayed.cancel();
@@ -385,9 +383,9 @@
         if (mSendTouchExplorationEndDelayed.isPending()) {
             mSendTouchExplorationEndDelayed.forceSendAndRemove();
         }
-        if (mSendTouchInteractionEndDelayed.isPending()) {
-            mSendTouchInteractionEndDelayed.forceSendAndRemove();
-        }
+
+        // Announce the end of a new touch interaction.
+        sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
 
         // Try to use the standard accessibility API to click
         if (mAms.performActionOnAccessibilityFocusedItem(
@@ -490,20 +488,25 @@
             case MotionEvent.ACTION_DOWN: {
                 mAms.onTouchInteractionStart();
 
-                sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
-
                 // If we still have not notified the user for the last
                 // touch, we figure out what to do. If were waiting
                 // we resent the delayed callback and wait again.
                 mSendHoverEnterAndMoveDelayed.cancel();
                 mSendHoverExitDelayed.cancel();
 
-                if (mSendTouchExplorationEndDelayed.isPending()) {
-                    mSendTouchExplorationEndDelayed.forceSendAndRemove();
+                // If a touch exploration gesture is in progress send events for its end.
+                if(mTouchExplorationInProgress) {
+                    sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
                 }
 
-                if (mSendTouchInteractionEndDelayed.isPending()) {
+                // Avoid duplicated TYPE_TOUCH_INTERACTION_START event when 2nd tap of double tap.
+                if (!mGestureDetector.firstTapDetected()) {
+                    mSendTouchExplorationEndDelayed.forceSendAndRemove();
                     mSendTouchInteractionEndDelayed.forceSendAndRemove();
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
+                } else {
+                    // Let gesture to handle to avoid duplicated TYPE_TOUCH_INTERACTION_END event.
+                    mSendTouchInteractionEndDelayed.cancel();
                 }
 
                 if (!mGestureDetector.firstTapDetected() && !mTouchExplorationInProgress) {
@@ -575,6 +578,8 @@
                             }
                         }
 
+                        // Remove move history before send injected non-move events
+                        event = MotionEvent.obtainNoHistory(event);
                         if (isDraggingGesture(event)) {
                             // Two pointers moving in the same direction within
                             // a given distance perform a drag.
@@ -605,6 +610,7 @@
 
                         // More than two pointers are delegated to the view hierarchy.
                         mCurrentState = STATE_DELEGATING;
+                        event = MotionEvent.obtainNoHistory(event);
                         sendDownForAllNotInjectedPointers(event, policyFlags);
                     }
                 }
@@ -693,6 +699,8 @@
                             // The two pointers are moving either in different directions or
                             // no close enough => delegate the gesture to the view hierarchy.
                             mCurrentState = STATE_DELEGATING;
+                            // Remove move history before send injected non-move events
+                            event = MotionEvent.obtainNoHistory(event);
                             // Send an event to the end of the drag gesture.
                             sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
                                     policyFlags);
@@ -702,6 +710,7 @@
                     } break;
                     default: {
                         mCurrentState = STATE_DELEGATING;
+                        event = MotionEvent.obtainNoHistory(event);
                         // Send an event to the end of the drag gesture.
                         sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
                                 policyFlags);
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index ed3b3e7..ff29311 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -259,6 +259,11 @@
         }
 
         @Override
+        public int getSoftKeyboardShowMode() {
+            return 0;
+        }
+
+        @Override
         public boolean isAccessibilityButtonAvailable() {
             return false;
         }
diff --git a/services/art-profile b/services/art-profile
index a33527e..cbc00ea 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -9258,24 +9258,24 @@
 PLcom/android/server/backup/internal/BackupState;-><init>(Ljava/lang/String;I)V
 PLcom/android/server/backup/internal/BackupState;->values()[Lcom/android/server/backup/internal/BackupState;
 PLcom/android/server/backup/internal/Operation;-><init>(ILcom/android/server/backup/BackupRestoreTask;I)V
-PLcom/android/server/backup/internal/PerformBackupTask;-><init>(Lcom/android/server/backup/BackupManagerService;Lcom/android/server/backup/transport/TransportClient;Ljava/lang/String;Ljava/util/ArrayList;Lcom/android/server/backup/DataChangedJournal;Landroid/app/backup/IBackupObserver;Landroid/app/backup/IBackupManagerMonitor;Lcom/android/server/backup/internal/OnTaskFinishedListener;Ljava/util/List;ZZ)V
-PLcom/android/server/backup/internal/PerformBackupTask;->backupPm()V
-PLcom/android/server/backup/internal/PerformBackupTask;->beginBackup()V
-PLcom/android/server/backup/internal/PerformBackupTask;->clearAgentState()V
-PLcom/android/server/backup/internal/PerformBackupTask;->execute()V
-PLcom/android/server/backup/internal/PerformBackupTask;->executeNextState(Lcom/android/server/backup/internal/BackupState;)V
-PLcom/android/server/backup/internal/PerformBackupTask;->finalizeBackup()V
-PLcom/android/server/backup/internal/PerformBackupTask;->invokeAgentForBackup(Ljava/lang/String;Landroid/app/IBackupAgent;)I
-PLcom/android/server/backup/internal/PerformBackupTask;->invokeNextAgent()V
-PLcom/android/server/backup/internal/PerformBackupTask;->operationComplete(J)V
-PLcom/android/server/backup/internal/PerformBackupTask;->registerTask()V
-PLcom/android/server/backup/internal/PerformBackupTask;->revertAndEndBackup()V
-PLcom/android/server/backup/internal/PerformBackupTask;->unregisterTask()V
-PLcom/android/server/backup/internal/PerformBackupTask;->writeWidgetPayloadIfAppropriate(Ljava/io/FileDescriptor;Ljava/lang/String;)V
 PLcom/android/server/backup/internal/ProvisionedObserver;-><init>(Lcom/android/server/backup/BackupManagerService;Landroid/os/Handler;)V
 PLcom/android/server/backup/internal/RunBackupReceiver;-><init>(Lcom/android/server/backup/BackupManagerService;)V
 PLcom/android/server/backup/internal/RunBackupReceiver;->onReceive(Landroid/content/Context;Landroid/content/Intent;)V
 PLcom/android/server/backup/internal/RunInitializeReceiver;-><init>(Lcom/android/server/backup/BackupManagerService;)V
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;-><init>(Lcom/android/server/backup/BackupManagerService;Lcom/android/server/backup/transport/TransportClient;Ljava/lang/String;Ljava/util/List;Lcom/android/server/backup/DataChangedJournal;Landroid/app/backup/IBackupObserver;Landroid/app/backup/IBackupManagerMonitor;Lcom/android/server/backup/internal/OnTaskFinishedListener;Ljava/util/List;ZZ)V
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;->backupPm()V
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;->beginBackup()V
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;->clearAgentState()V
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;->execute()V
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;->executeNextState(Lcom/android/server/backup/internal/BackupState;)V
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;->finalizeBackup()V
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;->invokeAgentForBackup(Ljava/lang/String;Landroid/app/IBackupAgent;)I
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;->invokeNextAgent()V
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;->operationComplete(J)V
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;->registerTask()V
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;->revertAndEndBackup()V
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;->unregisterTask()V
+PLcom/android/server/backup/keyvalue/KeyValueBackupTask;->writeWidgetPayloadIfAppropriate(Ljava/io/FileDescriptor;Ljava/lang/String;)V
 PLcom/android/server/backup/transport/-$$Lambda$TransportClient$ciIUj0x0CRg93UETUpy2FB5aqCQ;-><init>(Lcom/android/server/backup/transport/TransportClient;Lcom/android/server/backup/transport/TransportConnectionListener;Lcom/android/internal/backup/IBackupTransport;)V
 PLcom/android/server/backup/transport/-$$Lambda$TransportClient$ciIUj0x0CRg93UETUpy2FB5aqCQ;->run()V
 PLcom/android/server/backup/transport/-$$Lambda$TransportClient$uc3fygwQjQIS_JT7mlt-yMBfJcE;-><init>(Ljava/util/concurrent/CompletableFuture;)V
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 41e9d2b..6f03b76 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -29,6 +29,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.ActivityThread;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -42,10 +43,10 @@
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Parcelable;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -165,9 +166,9 @@
         mContext = context;
         mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
 
-        final boolean debug = Build.IS_DEBUGGABLE;
-        Slog.i(TAG, "Setting debug to " + debug);
-        setDebugLocked(debug);
+        setLogLevelFromSettings();
+        setMaxPartitionsFromSettings();
+        setMaxVisibleDatasetsFromSettings();
 
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -437,12 +438,28 @@
         Slog.i(TAG, "setLogLevel(): " + level);
         mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.AUTOFILL_LOGGING_LEVEL, level);
+    }
+
+    private void setLogLevelFromSettings() {
+        final int level = Settings.Global.getInt(
+                mContext.getContentResolver(),
+                Settings.Global.AUTOFILL_LOGGING_LEVEL, AutofillManager.DEFAULT_LOGGING_LEVEL);
         boolean debug = false;
         boolean verbose = false;
-        if (level == AutofillManager.FLAG_ADD_CLIENT_VERBOSE) {
-            debug = verbose = true;
-        } else if (level == AutofillManager.FLAG_ADD_CLIENT_DEBUG) {
-            debug = true;
+        if (level != AutofillManager.NO_LOGGING) {
+            if (level == AutofillManager.FLAG_ADD_CLIENT_VERBOSE) {
+                debug = verbose = true;
+            } else if (level == AutofillManager.FLAG_ADD_CLIENT_DEBUG) {
+                debug = true;
+            } else {
+                Slog.w(TAG,  "setLogLevelFromSettings(): invalid level: " + level);
+            }
+        }
+        if (debug || sDebug) {
+            Slog.d(TAG, "setLogLevelFromSettings(): level=" + level + ", debug=" + debug
+                    + ", verbose=" + verbose);
         }
         synchronized (mLock) {
             setDebugLocked(debug);
@@ -474,6 +491,17 @@
     void setMaxPartitions(int max) {
         mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxPartitions(): " + max);
+
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE, max);
+    }
+
+    private void setMaxPartitionsFromSettings() {
+        final int max = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
+                AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE);
+        if (sDebug) Slog.d(TAG, "setMaxPartitionsFromSettings(): " + max);
+
         synchronized (mLock) {
             sPartitionMaxCount = max;
         }
@@ -492,6 +520,16 @@
     void setMaxVisibleDatasets(int max) {
         mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxVisibleDatasets(): " + max);
+
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, max);
+    }
+
+    private void setMaxVisibleDatasetsFromSettings() {
+        final int max = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, 0);
+
+        if (sDebug) Slog.d(TAG, "setMaxVisibleDatasetsFromSettings(): " + max);
         synchronized (mLock) {
             sVisibleDatasetsMaxCount = max;
         }
@@ -622,6 +660,38 @@
         return getWhitelistedCompatModePackages(getWhitelistedCompatModePackagesFromSettings());
     }
 
+    private void send(@NonNull IResultReceiver receiver, int value) {
+        try {
+            receiver.send(value, null);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Error async reporting result to client: " + e);
+        }
+    }
+
+    private void send(@NonNull IResultReceiver receiver, @NonNull Bundle value) {
+        try {
+            receiver.send(0, value);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Error async reporting result to client: " + e);
+        }
+    }
+
+    private void send(@NonNull IResultReceiver receiver, @Nullable String value) {
+        send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
+    }
+
+    private void send(@NonNull IResultReceiver receiver, @Nullable String[] value) {
+        send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
+    }
+
+    private void send(@NonNull IResultReceiver receiver, @Nullable Parcelable value) {
+        send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
+    }
+
+    private void send(@NonNull IResultReceiver receiver, boolean value) {
+        send(receiver, value ? 1 : 0);
+    }
+
     @Nullable
     @VisibleForTesting
     static Map<String, String[]> getWhitelistedCompatModePackages(String setting) {
@@ -827,9 +897,10 @@
 
     final class AutoFillManagerServiceStub extends IAutoFillManager.Stub {
         @Override
-        public int addClient(IAutoFillManagerClient client, int userId) {
+        public void addClient(IAutoFillManagerClient client, int userId,
+                @NonNull IResultReceiver receiver) {
+            int flags = 0;
             synchronized (mLock) {
-                int flags = 0;
                 if (getServiceForUserLocked(userId).addClientLocked(client)) {
                     flags |= AutofillManager.FLAG_ADD_CLIENT_ENABLED;
                 }
@@ -839,8 +910,8 @@
                 if (sVerbose) {
                     flags |= AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
                 }
-                return flags;
             }
+            send(receiver, flags);
         }
 
         @Override
@@ -874,9 +945,9 @@
         }
 
         @Override
-        public int startSession(IBinder activityToken, IBinder appCallback, AutofillId autofillId,
+        public void startSession(IBinder activityToken, IBinder appCallback, AutofillId autofillId,
                 Rect bounds, AutofillValue value, int userId, boolean hasCallback, int flags,
-                ComponentName componentName, boolean compatMode) {
+                ComponentName componentName, boolean compatMode, IResultReceiver receiver) {
 
             activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
             appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
@@ -892,61 +963,71 @@
                 throw new IllegalArgumentException(packageName + " is not a valid package", e);
             }
 
+            // TODO(b/112051762): rather than always call AM here, call it on demand on
+            // getPreviousSessionsLocked()? That way we save space / time here, and don't set
+            // a callback on AM unnecessarily (see TODO below :-)
+            final ActivityManagerInternal am = LocalServices
+                    .getService(ActivityManagerInternal.class);
+            // TODO(b/112051762): add a callback method on AM to be notified when a task is finished
+            // so we can clean up sessions kept alive
+            final int taskId = am.getTaskIdForActivity(activityToken, false);
+            final int sessionId;
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
-                return service.startSessionLocked(activityToken, getCallingUid(), appCallback,
-                        autofillId, bounds, value, hasCallback, componentName, compatMode,
-                        mAllowInstantService, flags);
+                sessionId = service.startSessionLocked(activityToken, taskId, getCallingUid(),
+                        appCallback, autofillId, bounds, value, hasCallback, componentName,
+                        compatMode, mAllowInstantService, flags);
             }
+            send(receiver, sessionId);
         }
 
         @Override
-        public FillEventHistory getFillEventHistory() throws RemoteException {
+        public void getFillEventHistory(@NonNull IResultReceiver receiver) throws RemoteException {
             final int userId = UserHandle.getCallingUserId();
 
+            FillEventHistory fillEventHistory = null;
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                 if (service != null) {
-                    return service.getFillEventHistory(getCallingUid());
+                    fillEventHistory = service.getFillEventHistory(getCallingUid());
                 } else if (sVerbose) {
                     Slog.v(TAG, "getFillEventHistory(): no service for " + userId);
                 }
             }
-
-            return null;
+            send(receiver, fillEventHistory);
         }
 
         @Override
-        public UserData getUserData() throws RemoteException {
+        public void getUserData(@NonNull IResultReceiver receiver) throws RemoteException {
             final int userId = UserHandle.getCallingUserId();
 
+            UserData userData = null;
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                 if (service != null) {
-                    return service.getUserData(getCallingUid());
+                    userData = service.getUserData(getCallingUid());
                 } else if (sVerbose) {
                     Slog.v(TAG, "getUserData(): no service for " + userId);
                 }
             }
-
-            return null;
+            send(receiver, userData);
         }
 
         @Override
-        public String getUserDataId() throws RemoteException {
+        public void getUserDataId(@NonNull IResultReceiver receiver) throws RemoteException {
             final int userId = UserHandle.getCallingUserId();
+            UserData userData = null;
 
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                 if (service != null) {
-                    final UserData userData = service.getUserData(getCallingUid());
-                    return userData == null ? null : userData.getId();
+                    userData = service.getUserData(getCallingUid());
                 } else if (sVerbose) {
                     Slog.v(TAG, "getUserDataId(): no service for " + userId);
                 }
             }
-
-            return null;
+            final String userDataId = userData == null ? null : userData.getId();
+            send(receiver, userDataId);
         }
 
         @Override
@@ -964,89 +1045,96 @@
         }
 
         @Override
-        public boolean isFieldClassificationEnabled() throws RemoteException {
+        public void isFieldClassificationEnabled(@NonNull IResultReceiver receiver)
+                throws RemoteException {
             final int userId = UserHandle.getCallingUserId();
+            boolean enabled = false;
 
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                 if (service != null) {
-                    return service.isFieldClassificationEnabled(getCallingUid());
+                    enabled = service.isFieldClassificationEnabled(getCallingUid());
                 } else if (sVerbose) {
                     Slog.v(TAG, "isFieldClassificationEnabled(): no service for " + userId);
                 }
             }
-
-            return false;
+            send(receiver, enabled);
         }
 
         @Override
-        public String getDefaultFieldClassificationAlgorithm() throws RemoteException {
+        public void getDefaultFieldClassificationAlgorithm(@NonNull IResultReceiver receiver)
+                throws RemoteException {
             final int userId = UserHandle.getCallingUserId();
+            String algorithm = null;
 
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                 if (service != null) {
-                    return service.getDefaultFieldClassificationAlgorithm(getCallingUid());
+                    algorithm = service.getDefaultFieldClassificationAlgorithm(getCallingUid());
                 } else {
                     if (sVerbose) {
                         Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId);
                     }
-                    return null;
                }
             }
+            send(receiver, algorithm);
         }
 
         @Override
-        public String[] getAvailableFieldClassificationAlgorithms() throws RemoteException {
+        public void getAvailableFieldClassificationAlgorithms(@NonNull IResultReceiver receiver)
+                throws RemoteException {
             final int userId = UserHandle.getCallingUserId();
+            String[] algorithms = null;
 
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                 if (service != null) {
-                    return service.getAvailableFieldClassificationAlgorithms(getCallingUid());
+                    algorithms = service.getAvailableFieldClassificationAlgorithms(getCallingUid());
                 } else {
                     if (sVerbose) {
                         Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId);
                     }
-                    return null;
                 }
             }
+            send(receiver, algorithms);
         }
 
         @Override
-        public ComponentName getAutofillServiceComponentName() throws RemoteException {
+        public void getAutofillServiceComponentName(@NonNull IResultReceiver receiver)
+                throws RemoteException {
             final int userId = UserHandle.getCallingUserId();
 
+            ComponentName componentName = null;
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                 if (service != null) {
-                    return service.getServiceComponentName();
+                    componentName = service.getServiceComponentName();
                 } else if (sVerbose) {
                     Slog.v(TAG, "getAutofillServiceComponentName(): no service for " + userId);
                 }
             }
-
-            return null;
+            send(receiver, componentName);
         }
 
         @Override
-        public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback)
+        public void restoreSession(int sessionId, @NonNull IBinder activityToken,
+                @NonNull IBinder appCallback, @NonNull IResultReceiver receiver)
                 throws RemoteException {
             final int userId = UserHandle.getCallingUserId();
             activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
             appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
 
+            boolean restored = false;
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = mServicesCache.get(userId);
                 if (service != null) {
-                    return service.restoreSession(sessionId, getCallingUid(), activityToken,
+                    restored = service.restoreSession(sessionId, getCallingUid(), activityToken,
                             appCallback);
                 } else if (sVerbose) {
                     Slog.v(TAG, "restoreSession(): no service for " + userId);
                 }
             }
-
-            return false;
+            send(receiver, restored);
         }
 
         @Override
@@ -1112,23 +1200,27 @@
         }
 
         @Override
-        public boolean isServiceSupported(int userId) {
+        public void isServiceSupported(int userId, @NonNull IResultReceiver receiver) {
+            boolean supported = false;
             synchronized (mLock) {
-                return !mDisabledUsers.get(userId);
+                supported = !mDisabledUsers.get(userId);
             }
+            send(receiver, supported);
         }
 
         @Override
-        public boolean isServiceEnabled(int userId, String packageName) {
+        public void isServiceEnabled(int userId, @NonNull String packageName,
+                @NonNull IResultReceiver receiver) {
+            boolean enabled = false;
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                 if (service != null) {
-                    return Objects.equals(packageName, service.getServicePackageName());
+                    enabled = Objects.equals(packageName, service.getServicePackageName());
                 } else if (sVerbose) {
                     Slog.v(TAG, "isServiceEnabled(): no service for " + userId);
                 }
-                return false;
             }
+            send(receiver, enabled);
         }
 
         @Override
@@ -1242,13 +1334,34 @@
             resolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, this,
                     UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.AUTOFILL_LOGGING_LEVEL), false, this,
+                    UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE), false, this,
+                    UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, this,
+                    UserHandle.USER_ALL);
         }
 
         @Override
         public void onChange(boolean selfChange, Uri uri, int userId) {
             if (sVerbose) Slog.v(TAG, "onChange(): uri=" + uri + ", userId=" + userId);
-            synchronized (mLock) {
-                updateCachedServiceLocked(userId);
+            switch (uri.getLastPathSegment()) {
+                case Settings.Global.AUTOFILL_LOGGING_LEVEL:
+                    setLogLevelFromSettings();
+                    break;
+                case Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE:
+                    setMaxPartitionsFromSettings();
+                    break;
+                case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS:
+                    setMaxVisibleDatasetsFromSettings();
+                    break;
+                default:
+                synchronized (mLock) {
+                    updateCachedServiceLocked(userId);
+                }
             }
         }
     }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 1ccce17..48b3798 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -25,10 +25,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
-import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -339,7 +339,7 @@
     }
 
     @GuardedBy("mLock")
-    int startSessionLocked(@NonNull IBinder activityToken, int uid,
+    int startSessionLocked(@NonNull IBinder activityToken, int taskId, int uid,
             @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
             @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
             @NonNull ComponentName componentName, boolean compatMode,
@@ -372,8 +372,9 @@
         // Occasionally clean up abandoned sessions
         pruneAbandonedSessionsLocked();
 
-        final Session newSession = createSessionByTokenLocked(activityToken, uid, appCallbackToken,
-                hasCallback, componentName, compatMode, bindInstantServiceAllowed, flags);
+        final Session newSession = createSessionByTokenLocked(activityToken, taskId, uid,
+                appCallbackToken, hasCallback, componentName, compatMode, bindInstantServiceAllowed,
+                flags);
         if (newSession == null) {
             return NO_SESSION;
         }
@@ -490,7 +491,7 @@
     }
 
     @GuardedBy("mLock")
-    private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid,
+    private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int taskId, int uid,
             @NonNull IBinder appCallbackToken, boolean hasCallback,
             @NonNull ComponentName componentName, boolean compatMode,
             boolean bindInstantServiceAllowed, int flags) {
@@ -510,9 +511,9 @@
         assertCallerLocked(componentName, compatMode);
 
         final Session newSession = new Session(this, mUi, mContext, mHandler, mUserId, mLock,
-                sessionId, uid, activityToken, appCallbackToken, hasCallback, mUiLatencyHistory,
-                mWtfHistory, mInfo.getServiceInfo().getComponentName(), componentName, compatMode,
-                bindInstantServiceAllowed, flags);
+                sessionId, taskId, uid, activityToken, appCallbackToken, hasCallback,
+                mUiLatencyHistory, mWtfHistory, mInfo.getServiceInfo().getComponentName(),
+                componentName, compatMode, bindInstantServiceAllowed, flags);
         mSessions.put(newSession.id, newSession);
 
         return newSession;
@@ -604,6 +605,30 @@
         mSessions.remove(sessionId);
     }
 
+    /**
+     * Ges the previous sessions asked to be kept alive in a given activity task.
+     *
+     * @param session session calling this method (so it's excluded from the result).
+     */
+    @Nullable
+    @GuardedBy("mLock")
+    ArrayList<Session> getPreviousSessionsLocked(@NonNull Session session) {
+        final int size = mSessions.size();
+        ArrayList<Session> previousSessions = null;
+        for (int i = 0; i < size; i++) {
+            final Session previousSession = mSessions.valueAt(i);
+            // TODO(b/112051762): only return sessions asked to be kept alive / add CTS test
+            if (previousSession.taskId == session.taskId && previousSession.id != session.id) {
+                if (previousSessions == null) {
+                    previousSessions = new ArrayList<>(size);
+                }
+                previousSessions.add(previousSession);
+            }
+        }
+        // TODO(b/112051762): remove returned sessions / add CTS test
+        return previousSessions;
+    }
+
     void handleSessionSave(Session session) {
         synchronized (mLock) {
             if (mSessions.get(session.id) == null) {
@@ -1296,13 +1321,13 @@
                 }
             }
 
-            IActivityManager am = ActivityManager.getService();
+            final IActivityTaskManager atm = ActivityTaskManager.getService();
 
             // Only remove sessions which's activities are not known to the activity manager anymore
             for (int i = 0; i < numSessionsToRemove; i++) {
                 try {
                     // The activity manager cannot resolve activities that have been removed
-                    if (am.getActivityClassForToken(sessionsToRemove.valueAt(i)) != null) {
+                    if (atm.getActivityClassForToken(sessionsToRemove.valueAt(i)) != null) {
                         sessionsToRemove.removeAt(i);
                         i--;
                         numSessionsToRemove--;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index f7b7ceb4..522280e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -185,7 +185,7 @@
                 mService.setLogLevel(AutofillManager.FLAG_ADD_CLIENT_DEBUG);
                 return 0;
             case "off":
-                mService.setLogLevel(0);
+                mService.setLogLevel(AutofillManager.NO_LOGGING);
                 return 0;
             default:
                 pw.println("Invalid level: " + logLevel);
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
index ba544f1..8ee6571 100644
--- a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
@@ -17,8 +17,8 @@
 
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
-import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
-import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM;
+import static android.service.autofill.AutofillFieldClassificationService.RESOURCE_AVAILABLE_ALGORITHMS;
+import static android.service.autofill.AutofillFieldClassificationService.RESOURCE_DEFAULT_ALGORITHM;
 
 import android.Manifest;
 import android.annotation.MainThread;
@@ -226,7 +226,7 @@
      */
     @Nullable
     String[] getAvailableAlgorithms() {
-        return getMetadataValue(SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS,
+        return getMetadataValue(RESOURCE_AVAILABLE_ALGORITHMS, "array",
                 (res, id) -> res.getStringArray(id));
     }
 
@@ -235,11 +235,12 @@
      */
     @Nullable
     String getDefaultAlgorithm() {
-        return getMetadataValue(SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM, (res, id) -> res.getString(id));
+        return getMetadataValue(RESOURCE_DEFAULT_ALGORITHM, "string",
+                (res, id) -> res.getString(id));
     }
 
     @Nullable
-    private <T> T getMetadataValue(String field, MetadataParser<T> parser) {
+    private <T> T getMetadataValue(String field, String type, MetadataParser<T> parser) {
         final ServiceInfo serviceInfo = getServiceInfo();
         if (serviceInfo == null) return null;
 
@@ -253,7 +254,7 @@
             return null;
         }
 
-        final int resourceId = serviceInfo.metaData.getInt(field);
+        final int resourceId = res.getIdentifier(field, type, serviceInfo.packageName);
         return parser.get(res, resourceId);
     }
 
@@ -282,9 +283,13 @@
         }
         pw.println(impl.flattenToShortString());
 
-        pw.print(prefix); pw.print("Available algorithms: ");
-        pw.println(Arrays.toString(getAvailableAlgorithms()));
-        pw.print(prefix); pw.print("Default algorithm: "); pw.println(getDefaultAlgorithm());
+        try {
+            pw.print(prefix); pw.print("Available algorithms: ");
+            pw.println(Arrays.toString(getAvailableAlgorithms()));
+            pw.print(prefix); pw.print("Default algorithm: "); pw.println(getDefaultAlgorithm());
+        } catch (Exception e) {
+            pw.print("ERROR CALLING SERVICE: " ); pw.println(e);
+        }
     }
 
     private static interface Command {
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 4f45a77..420c2be 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -28,6 +28,7 @@
 import android.util.Slog;
 import android.view.WindowManager;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -43,28 +44,32 @@
 
     /**
      * Defines a logging flag that can be dynamically changed at runtime using
-     * {@code cmd autofill set log_level debug}.
+     * {@code cmd autofill set log_level debug} or through
+     * {@link android.provider.Settings.Global#AUTOFILL_LOGGING_LEVEL}.
      */
     public static boolean sDebug = false;
 
     /**
      * Defines a logging flag that can be dynamically changed at runtime using
-     * {@code cmd autofill set log_level verbose}.
+     * {@code cmd autofill set log_level verbose} or through
+     * {@link android.provider.Settings.Global#AUTOFILL_LOGGING_LEVEL}.
      */
     public static boolean sVerbose = false;
 
     /**
      * Maximum number of partitions that can be allowed in a session.
      *
-     * <p>Can be modified using {@code cmd autofill set max_partitions}.
+     * <p>Can be modified using {@code cmd autofill set max_partitions} or through
+     * {@link android.provider.Settings.Global#AUTOFILL_MAX_PARTITIONS_SIZE}.
      */
-    static int sPartitionMaxCount = 10;
+    static int sPartitionMaxCount = AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE;
 
     /**
      * Maximum number of visible datasets in the dataset picker UI, or {@code 0} to use default
      * value from resources.
      *
-     * <p>Can be modified using {@code cmd autofill set max_visible_datasets}.
+     * <p>Can be modified using {@code cmd autofill set max_visible_datasets} or through
+     * {@link android.provider.Settings.Global#AUTOFILL_MAX_VISIBLE_DATASETS}.
      */
     public static int sVisibleDatasetsMaxCount = 0;
 
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 3e932e8..ad80cc26 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -101,9 +101,8 @@
     public interface FillServiceCallbacks {
         void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
                 @NonNull String servicePackageName, int requestFlags);
-        void onFillRequestFailure(int requestId, @Nullable CharSequence message,
-                @NonNull String servicePackageName);
-        void onFillRequestTimeout(int requestId, @NonNull String servicePackageName);
+        void onFillRequestFailure(int requestId, @Nullable CharSequence message);
+        void onFillRequestTimeout(int requestId);
         void onSaveRequestSuccess(@NonNull String servicePackageName,
                 @Nullable IntentSender intentSender);
         // TODO(b/80093094): add timeout here too?
@@ -124,8 +123,7 @@
     }
 
     public void destroy() {
-        mHandler.sendMessage(obtainMessage(
-                RemoteFillService::handleDestroy, this));
+        mHandler.sendMessage(obtainMessage(RemoteFillService::handleDestroy, this));
     }
 
     private void handleDestroy() {
@@ -317,8 +315,7 @@
             @Nullable CharSequence message) {
         mHandler.post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
-                mCallbacks.onFillRequestFailure(pendingRequest.mRequest.getId(), message,
-                        mComponentName.getPackageName());
+                mCallbacks.onFillRequestFailure(pendingRequest.mRequest.getId(), message);
             }
         });
     }
@@ -326,8 +323,7 @@
     private void dispatchOnFillRequestTimeout(@NonNull PendingFillRequest pendingRequest) {
         mHandler.post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
-                mCallbacks.onFillRequestTimeout(pendingRequest.mRequest.getId(),
-                        mComponentName.getPackageName());
+                mCallbacks.onFillRequestTimeout(pendingRequest.mRequest.getId());
             }
         });
     }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1168f55..101bf49 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -16,8 +16,6 @@
 
 package com.android.server.autofill;
 
-import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
 import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
@@ -33,11 +31,13 @@
 import static com.android.server.autofill.Helper.sVerbose;
 import static com.android.server.autofill.Helper.toArray;
 import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
-import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.IAssistDataReceiver;
 import android.app.assist.AssistStructure;
 import android.app.assist.AssistStructure.AutofillOverlay;
@@ -129,12 +129,15 @@
 
     private static AtomicInteger sIdCounter = new AtomicInteger();
 
-    /** Id of the session */
+    /** ID of the session */
     public final int id;
 
     /** uid the session is for */
     public final int uid;
 
+    /** ID of the task associated with this session's activity */
+    public final int taskId;
+
     /** Flags used to start the session */
     public final int mFlags;
 
@@ -268,7 +271,7 @@
                 // change AssistStructure so it provides a "one-way" writeToParcel() method that
                 // sends all the data
                 try {
-                    structure.ensureData();
+                    structure.ensureDataForAutofill();
                 } catch (RuntimeException e) {
                     wtf(e, "Exception lazy loading assist structure for %s: %s",
                             structure.getActivityComponent(), e);
@@ -329,8 +332,8 @@
                 // until the dispatch happens. The items in the list don't need to be cloned
                 // since we don't hold on them anywhere else. The client state is not touched
                 // by us, so no need to copy.
-                request = new FillRequest(requestId, new ArrayList<>(mContexts),
-                        mClientState, flags);
+                request = new FillRequest(requestId, new ArrayList<>(mContexts), mClientState,
+                        flags);
             }
 
             mRemoteFillService.onFillRequest(request);
@@ -515,7 +518,7 @@
             receiverExtras.putInt(EXTRA_REQUEST_ID, requestId);
             final long identity = Binder.clearCallingIdentity();
             try {
-                if (!ActivityManager.getService().requestAutofillData(mAssistReceiver,
+                if (!ActivityTaskManager.getService().requestAutofillData(mAssistReceiver,
                         receiverExtras, mActivityToken, flags)) {
                     Slog.w(TAG, "failed to request autofill data for " + mActivityToken);
                 }
@@ -528,14 +531,15 @@
     }
 
     Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui,
-            @NonNull Context context, @NonNull Handler handler, int userId,
-            @NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken,
+            @NonNull Context context, @NonNull Handler handler, int userId, @NonNull Object lock,
+            int sessionId, int taskId, int uid, @NonNull IBinder activityToken,
             @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory,
-            @NonNull LocalLog wtfHistory,
-            @NonNull ComponentName serviceComponentName, @NonNull ComponentName componentName,
-            boolean compatMode, boolean bindInstantServiceAllowed, int flags) {
+            @NonNull LocalLog wtfHistory, @NonNull ComponentName serviceComponentName,
+            @NonNull ComponentName componentName, boolean compatMode,
+            boolean bindInstantServiceAllowed, int flags) {
         id = sessionId;
         mFlags = flags;
+        this.taskId = taskId;
         this.uid = uid;
         mStartTime = SystemClock.elapsedRealtime();
         mService = service;
@@ -703,19 +707,18 @@
 
     // FillServiceCallbacks
     @Override
-    public void onFillRequestFailure(int requestId, @Nullable CharSequence message,
-            @NonNull String servicePackageName) {
-        onFillRequestFailureOrTimeout(requestId, false, message, servicePackageName);
+    public void onFillRequestFailure(int requestId, @Nullable CharSequence message) {
+        onFillRequestFailureOrTimeout(requestId, false, message);
     }
 
     // FillServiceCallbacks
     @Override
-    public void onFillRequestTimeout(int requestId, @NonNull String servicePackageName) {
-        onFillRequestFailureOrTimeout(requestId, true, null, servicePackageName);
+    public void onFillRequestTimeout(int requestId) {
+        onFillRequestFailureOrTimeout(requestId, true, null);
     }
 
     private void onFillRequestFailureOrTimeout(int requestId, boolean timedOut,
-            @Nullable CharSequence message, @NonNull String servicePackageName) {
+            @Nullable CharSequence message) {
         synchronized (mLock) {
             if (mDestroyed) {
                 Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId
@@ -839,7 +842,8 @@
     // FillServiceCallbacks
     @Override
     public void onServiceDied(RemoteFillService service) {
-        // TODO(b/337565347): implement
+        Slog.w(TAG, "removing session because service died");
+        forceRemoveSelfLocked();
     }
 
     // AutoFillUiCallback
@@ -993,6 +997,7 @@
         final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId);
         final FillResponse authenticatedResponse = mResponses.get(requestId);
         if (authenticatedResponse == null || data == null) {
+            Slog.w(TAG, "no authenticated response");
             removeSelf();
             return;
         }
@@ -1003,6 +1008,7 @@
         if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
             final Dataset dataset = authenticatedResponse.getDatasets().get(datasetIdx);
             if (dataset == null) {
+                Slog.w(TAG, "no dataset with index " + datasetIdx + " on fill response");
                 removeSelf();
                 return;
             }
@@ -1012,7 +1018,7 @@
         final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE);
         if (sDebug) {
             Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result
-                    + ", clientState=" + newClientState);
+                    + ", clientState=" + newClientState + ", authenticationId=" + authenticationId);
         }
         if (result instanceof FillResponse) {
             logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED);
@@ -1029,6 +1035,8 @@
                 authenticatedResponse.getDatasets().set(datasetIdx, dataset);
                 autoFill(requestId, datasetIdx, dataset, false);
             } else {
+                Slog.w(TAG, "invalid index (" + datasetIdx + ") for authentication id "
+                        + authenticationId);
                 logAuthenticationStatusLocked(requestId,
                         MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION);
             }
@@ -1427,7 +1435,8 @@
     /**
      * Shows the save UI, when session can be saved.
      *
-     * @return {@code true} if session is done, or {@code false} if it's pending user action.
+     * @return {@code true} if session is done and could be removed, or {@code false} if it's
+     * pending user action or the service asked to keep it alive (for multi-screens workflow).
      */
     @GuardedBy("mLock")
     public boolean showSaveLocked() {
@@ -1447,12 +1456,19 @@
          * - autofillValue of at least one id (required or optional) has changed.
          * - there is no Dataset in the last FillResponse whose values of all dataset fields matches
          *   the current values of all fields in the screen.
+         * - server didn't ask to keep session alive
          */
         if (saveInfo == null) {
             if (sVerbose) Slog.v(TAG, "showSaveLocked(): no saveInfo from service");
             return true;
         }
 
+        if ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0) {
+            // TODO(b/112051762): log metrics
+            if (sDebug) Slog.v(TAG, "showSaveLocked(): service asked to delay save");
+            return false;
+        }
+
         final ArrayMap<AutofillId, InternalSanitizer> sanitizers = createSanitizers(saveInfo);
 
         // Cache used to make sure changed fields do not belong to a dataset.
@@ -1765,6 +1781,66 @@
     }
 
     /**
+     * Update the {@link AutofillValue values} of the {@link AssistStructure} before sending it to
+     * the service on save().
+     */
+    private void updateValuesForSaveLocked() {
+        final ArrayMap<AutofillId, InternalSanitizer> sanitizers =
+                createSanitizers(getSaveInfoLocked());
+
+        final int numContexts = mContexts.size();
+        for (int contextNum = 0; contextNum < numContexts; contextNum++) {
+            final FillContext context = mContexts.get(contextNum);
+
+            final ViewNode[] nodes =
+                context.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked());
+
+            if (sVerbose) Slog.v(TAG, "updateValuesForSaveLocked(): updating " + context);
+
+            for (int viewStateNum = 0; viewStateNum < mViewStates.size(); viewStateNum++) {
+                final ViewState viewState = mViewStates.valueAt(viewStateNum);
+
+                final AutofillId id = viewState.id;
+                final AutofillValue value = viewState.getCurrentValue();
+                if (value == null) {
+                    if (sVerbose) Slog.v(TAG, "updateValuesForSaveLocked(): skipping " + id);
+                    continue;
+                }
+                final ViewNode node = nodes[viewStateNum];
+                if (node == null) {
+                    Slog.w(TAG, "callSaveLocked(): did not find node with id " + id);
+                    continue;
+                }
+                if (sVerbose) {
+                    Slog.v(TAG, "updateValuesForSaveLocked(): updating " + id + " to " + value);
+                }
+
+                AutofillValue sanitizedValue = viewState.getSanitizedValue();
+
+                if (sanitizedValue == null) {
+                    // Field is optional and haven't been sanitized yet.
+                    sanitizedValue = getSanitizedValue(sanitizers, id, value);
+                }
+                if (sanitizedValue != null) {
+                    node.updateAutofillValue(sanitizedValue);
+                } else if (sDebug) {
+                    Slog.d(TAG, "updateValuesForSaveLocked(): not updating field " + id
+                            + " because it failed sanitization");
+                }
+            }
+
+            // Sanitize structure before it's sent to service.
+            context.getStructure().sanitizeForParceling(false);
+
+            if (sVerbose) {
+                Slog.v(TAG, "updateValuesForSaveLocked(): dumping structure of " + context
+                        + " before calling service.save()");
+                context.getStructure().dump(false);
+            }
+        }
+    }
+
+    /**
      * Calls service when user requested save.
      */
     @GuardedBy("mLock")
@@ -1782,66 +1858,49 @@
             return;
         }
 
-        final ArrayMap<AutofillId, InternalSanitizer> sanitizers =
-                createSanitizers(getSaveInfoLocked());
-
-        final int numContexts = mContexts.size();
-
-        for (int contextNum = 0; contextNum < numContexts; contextNum++) {
-            final FillContext context = mContexts.get(contextNum);
-
-            final ViewNode[] nodes =
-                context.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked());
-
-            if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + context);
-
-            for (int viewStateNum = 0; viewStateNum < mViewStates.size(); viewStateNum++) {
-                final ViewState viewState = mViewStates.valueAt(viewStateNum);
-
-                final AutofillId id = viewState.id;
-                final AutofillValue value = viewState.getCurrentValue();
-                if (value == null) {
-                    if (sVerbose) Slog.v(TAG, "callSaveLocked(): skipping " + id);
-                    continue;
-                }
-                final ViewNode node = nodes[viewStateNum];
-                if (node == null) {
-                    Slog.w(TAG, "callSaveLocked(): did not find node with id " + id);
-                    continue;
-                }
-                if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value);
-
-                AutofillValue sanitizedValue = viewState.getSanitizedValue();
-
-                if (sanitizedValue == null) {
-                    // Field is optional and haven't been sanitized yet.
-                    sanitizedValue = getSanitizedValue(sanitizers, id, value);
-                }
-                if (sanitizedValue != null) {
-                    node.updateAutofillValue(sanitizedValue);
-                } else if (sDebug) {
-                    Slog.d(TAG, "Not updating field " + id + " because it failed sanitization");
-                }
-            }
-
-            // Sanitize structure before it's sent to service.
-            context.getStructure().sanitizeForParceling(false);
-
-            if (sVerbose) {
-                Slog.v(TAG, "Dumping structure of " + context + " before calling service.save()");
-                context.getStructure().dump(false);
-            }
-        }
+        updateValuesForSaveLocked();
 
         // Remove pending fill requests as the session is finished.
         cancelCurrentRequestLocked();
 
-        // Dispatch a snapshot of the current contexts list since it may change
-        // until the dispatch happens. The items in the list don't need to be cloned
-        // since we don't hold on them anywhere else. The client state is not touched
-        // by us, so no need to copy.
-        final SaveRequest saveRequest = new SaveRequest(new ArrayList<>(mContexts), mClientState,
-                mSelectedDatasetIds);
+        // Merge the previous sessions that the service asked to be kept alive
+        final ArrayList<Session> previousSessions = mService.getPreviousSessionsLocked(this);
+        final ArrayList<FillContext> contexts;
+        final Bundle clientState;
+        if (previousSessions != null) {
+            if (sDebug) {
+                Slog.d(TAG, "callSaveLocked(): Merging the content of " + previousSessions.size()
+                        + " sessions for task " + taskId);
+            }
+            contexts = new ArrayList<>();
+            for (int i = 0; i < previousSessions.size(); i++) {
+                final Session previousSession = previousSessions.get(i);
+                final ArrayList<FillContext> previousContexts = previousSession.mContexts;
+                if (previousContexts == null) {
+                    Slog.w(TAG, "callSaveLocked(): Not merging null contexts from "
+                            + previousSession.id);
+                    continue;
+                }
+                previousSession.updateValuesForSaveLocked();
+                if (sVerbose) {
+                    Slog.v(TAG, "callSaveLocked(): adding " + previousContexts.size()
+                            + " context from previous session #" + previousSession.id);
+                }
+                contexts.addAll(previousContexts);
+            }
+            contexts.addAll(mContexts);
+            // TODO(b/112051762): decided what to do with client state / add CTS test
+            clientState = mClientState;
+        } else {
+            // Dispatch a snapshot of the current contexts list since it may change
+            // until the dispatch happens. The items in the list don't need to be cloned
+            // since we don't hold on them anywhere else. The client state is not touched
+            // by us, so no need to copy.
+            contexts = new ArrayList<>(mContexts);
+            clientState = mClientState;
+        }
+
+        final SaveRequest saveRequest = new SaveRequest(contexts, clientState, mSelectedDatasetIds);
         mRemoteFillService.onSaveRequest(saveRequest);
     }
 
@@ -2506,6 +2565,7 @@
         final String prefix2 = prefix + "  ";
         pw.print(prefix); pw.print("id: "); pw.println(id);
         pw.print(prefix); pw.print("uid: "); pw.println(uid);
+        pw.print(prefix); pw.print("taskId: "); pw.println(taskId);
         pw.print(prefix); pw.print("flags: "); pw.println(mFlags);
         pw.print(prefix); pw.print("mComponentName: "); pw.println(mComponentName);
         pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 8119054..68a495f 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -21,7 +21,6 @@
 import static com.android.server.autofill.Helper.sVerbose;
 import static com.android.server.autofill.Helper.sVisibleDatasetsMaxCount;
 
-import android.annotation.AttrRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
@@ -37,7 +36,6 @@
 import android.service.autofill.Dataset.DatasetFieldFilter;
 import android.service.autofill.FillResponse;
 import android.text.TextUtils;
-import android.util.AttributeSet;
 import android.util.Slog;
 import android.util.TypedValue;
 import android.view.KeyEvent;
@@ -54,7 +52,6 @@
 import android.widget.BaseAdapter;
 import android.widget.Filter;
 import android.widget.Filterable;
-import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
@@ -679,7 +676,7 @@
                 // WM throws an ISE if mContentView was added twice; this should never happen -
                 // since show() and hide() are always called in the UIThread - but when it does,
                 // it should not crash the system.
-                Slog.e(TAG, "Exception showing window " + params, e);
+                Slog.wtf(TAG, "Exception showing window " + params, e);
                 mCallback.onDestroy();
             }
         }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index cd1bd67..c26ac17 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -38,6 +38,7 @@
 import android.app.IActivityManager;
 import android.app.IBackupAgent;
 import android.app.PendingIntent;
+import android.app.backup.BackupAgent;
 import android.app.backup.BackupManager;
 import android.app.backup.BackupManagerMonitor;
 import android.app.backup.FullBackup;
@@ -79,6 +80,7 @@
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.os.WorkSource;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
@@ -102,7 +104,7 @@
 import com.android.server.backup.fullbackup.FullBackupEntry;
 import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
 import com.android.server.backup.internal.BackupHandler;
-import com.android.server.backup.internal.BackupRequest;
+import com.android.server.backup.keyvalue.BackupRequest;
 import com.android.server.backup.internal.ClearDataObserver;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.internal.Operation;
@@ -386,6 +388,16 @@
         return mWakelock;
     }
 
+    /**
+     * Sets the {@link WorkSource} of the {@link PowerManager.WakeLock} returned by {@link
+     * #getWakelock()}.
+     */
+    @VisibleForTesting
+    public void setWorkSource(@Nullable WorkSource workSource) {
+        // TODO: This is for testing, unfortunately WakeLock is final and WorkSource is not exposed
+        mWakelock.setWorkSource(workSource);
+    }
+
     public void setWakelock(PowerManager.WakeLock wakelock) {
         mWakelock = wakelock;
     }
@@ -496,6 +508,7 @@
         mDataDir = dataDir;
     }
 
+    @Nullable
     public DataChangedJournal getJournal() {
         return mJournal;
     }
@@ -704,7 +717,7 @@
      * process-local non-lifecycle agent instance, so we manually set up the context
      * topology for it.
      */
-    public PackageManagerBackupAgent makeMetadataAgent() {
+    public BackupAgent makeMetadataAgent() {
         PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
         pmAgent.attach(mContext);
         pmAgent.onCreate();
@@ -784,7 +797,7 @@
     }
 
     @VisibleForTesting
-    BackupManagerService(
+    public BackupManagerService(
             Context context,
             Trampoline parent,
             HandlerThread backupThread,
@@ -1478,8 +1491,8 @@
         }
     }
 
-    // fire off a backup agent, blocking until it attaches or times out
-    @Override
+    /** Fires off a backup agent, blocking until it attaches or times out. */
+    @Nullable
     public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
         IBackupAgent agent = null;
         synchronized (mAgentConnectLock) {
@@ -1527,6 +1540,14 @@
         return agent;
     }
 
+    public void unbindAgent(ApplicationInfo app) {
+        try {
+            mActivityManager.unbindBackupAgent(app);
+        } catch (RemoteException e) {
+            // Can't happen - activity manager is local
+        }
+    }
+
     // clear an application's data, blocking until the operation completes or times out
     // if keepSystemState is true, we intentionally do not also clear system state that
     // would ordinarily also be cleared, because we aren't actually wiping the app back
@@ -1749,6 +1770,16 @@
         }
     }
 
+    public void putOperation(int token, Operation operation) {
+        if (MORE_DEBUG) {
+            Slog.d(TAG, "Adding operation token=" + Integer.toHexString(token) + ", operation type="
+                    + operation.type);
+        }
+        synchronized (mCurrentOpLock) {
+            mCurrentOperations.put(token, operation);
+        }
+    }
+
     public void removeOperation(int token) {
         if (MORE_DEBUG) {
             Slog.d(TAG, "Removing operation token=" + Integer.toHexString(token));
diff --git a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
index f2219a0..a38a0e9 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
@@ -49,9 +49,6 @@
 
   boolean hasBackupPassword();
 
-  // fire off a backup agent, blocking until it attaches or times out
-  IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode);
-
   // Get the restore-set token for the best-available restore set for this package:
   // the active set if possible, else the ancestral one.  Returns zero if none available.
   long getAvailableRestoreToken(String packageName);
diff --git a/services/backup/java/com/android/server/backup/DataChangedJournal.java b/services/backup/java/com/android/server/backup/DataChangedJournal.java
index c2d3829..498185c 100644
--- a/services/backup/java/com/android/server/backup/DataChangedJournal.java
+++ b/services/backup/java/com/android/server/backup/DataChangedJournal.java
@@ -25,6 +25,8 @@
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
 
 /**
  * A journal of packages that have indicated that their data has changed (and therefore should be
@@ -64,12 +66,12 @@
     }
 
     /**
-     * Invokes {@link Consumer#accept(String)} with every package name in the journal file.
+     * Invokes {@link Consumer#accept(Object)} with every package name in the journal file.
      *
      * @param consumer The callback.
      * @throws IOException If there is an IO error reading from the file.
      */
-    public void forEach(Consumer consumer) throws IOException {
+    public void forEach(Consumer<String> consumer) throws IOException {
         try (
             BufferedInputStream bufferedInputStream = new BufferedInputStream(
                     new FileInputStream(mFile), BUFFER_SIZE_BYTES);
@@ -83,6 +85,17 @@
     }
 
     /**
+     * Returns a list with the packages in this journal.
+     *
+     * @throws IOException If there is an IO error reading from the file.
+     */
+    public List<String> getPackages() throws IOException {
+        List<String> packages = new ArrayList<>();
+        forEach(packages::add);
+        return packages;
+    }
+
+    /**
      * Deletes the journal from the filesystem.
      *
      * @return {@code true} if successfully deleted journal.
@@ -110,14 +123,6 @@
     }
 
     /**
-     * Consumer for iterating over package names in the journal.
-     */
-    @FunctionalInterface
-    public interface Consumer {
-        void accept(String packageName);
-    }
-
-    /**
      * Creates a new journal with a random file name in the given journal directory.
      *
      * @param journalDirectory The directory where journals are kept.
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 7f0030a..30ec8ab 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -9,6 +9,7 @@
 
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
+import android.app.backup.IBackupCallback;
 import android.app.backup.FullBackup;
 import android.app.backup.FullBackupDataOutput;
 import android.content.pm.ApplicationInfo;
@@ -20,6 +21,7 @@
 import android.util.Slog;
 
 import com.android.internal.util.Preconditions;
+import com.android.server.backup.remote.ServiceBackupCallback;
 import com.android.server.backup.utils.FullBackupUtils;
 
 import libcore.io.IoUtils;
@@ -47,7 +49,7 @@
     private static final String BACKUP_KEY_VALUE_BACKUP_DATA_FILENAME_SUFFIX = ".data";
     private static final String BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX = ".new";
 
-    private BackupManagerServiceInterface mBackupManagerService;
+    private BackupManagerService mBackupManagerService;
     private final PackageManager mPackageManager;
     private final OutputStream mOutput;
     private final PackageInfo mCurrentPackage;
@@ -63,7 +65,7 @@
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo,
-            BackupManagerServiceInterface backupManagerService, PackageManager packageManager,
+            BackupManagerService backupManagerService, PackageManager packageManager,
             File baseStateDir, File dataDir) {
         mOutput = output;
         mCurrentPackage = packageInfo;
@@ -158,10 +160,17 @@
             mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
                     OP_TYPE_BACKUP_WAIT);
 
+            IBackupCallback callback =
+                    new ServiceBackupCallback(
+                            mBackupManagerService.getBackupManagerBinder(), token);
             // Start backup and wait for BackupManagerService to get callback for success or timeout
             agent.doBackup(
-                    mSavedState, mBackupData, mNewState, Long.MAX_VALUE, token,
-                    mBackupManagerService.getBackupManagerBinder(), /*transportFlags=*/ 0);
+                    mSavedState,
+                    mBackupData,
+                    mNewState,
+                    /* quotaBytes */ Long.MAX_VALUE,
+                    callback,
+                    /* transportFlags */ 0);
             if (!mBackupManagerService.waitUntilOperationComplete(token)) {
                 Slog.e(TAG, "Key-value backup failed on package " + packageName);
                 return false;
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index d8411e2..c805783 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -102,6 +102,12 @@
         }
     }
 
+    public static boolean isScheduled() {
+        synchronized (KeyValueBackupJob.class) {
+            return sScheduled;
+        }
+    }
+
     @Override
     public boolean onStartJob(JobParameters params) {
         synchronized (KeyValueBackupJob.class) {
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 69f08ae..2722729 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -38,10 +38,11 @@
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.DataChangedJournal;
-import com.android.server.backup.transport.TransportClient;
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.fullbackup.PerformAdbBackupTask;
 import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
+import com.android.server.backup.keyvalue.BackupRequest;
+import com.android.server.backup.keyvalue.KeyValueBackupTask;
 import com.android.server.backup.params.AdbBackupParams;
 import com.android.server.backup.params.AdbParams;
 import com.android.server.backup.params.AdbRestoreParams;
@@ -52,6 +53,7 @@
 import com.android.server.backup.params.RestoreParams;
 import com.android.server.backup.restore.PerformAdbRestoreTask;
 import com.android.server.backup.restore.PerformUnifiedRestoreTask;
+import com.android.server.backup.transport.TransportClient;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -150,17 +152,22 @@
                 if (queue.size() > 0) {
                     // Spin up a backup state sequence and set it running
                     try {
-                        String dirName = transport.transportDirName();
                         OnTaskFinishedListener listener =
                                 caller ->
                                         transportManager
                                                 .disposeOfTransportClient(transportClient, caller);
-                        PerformBackupTask pbt = new PerformBackupTask(
-                                backupManagerService, transportClient, dirName, queue,
-                                oldJournal, null, null, listener, Collections.emptyList(), false,
-                                false /* nonIncremental */);
-                        Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
-                        sendMessage(pbtMessage);
+                        KeyValueBackupTask.start(
+                                backupManagerService,
+                                transportClient,
+                                transport.transportDirName(),
+                                queue,
+                                oldJournal,
+                                /* observer */ null,
+                                /* monitor */ null,
+                                listener,
+                                Collections.emptyList(),
+                                /* userInitiated */ false,
+                                /* nonIncremental */ false);
                     } catch (Exception e) {
                         // unable to ask the transport its dir name -- transient failure, since
                         // the above check succeeded.  Try again next time.
@@ -405,13 +412,18 @@
                 backupManagerService.setBackupRunning(true);
                 backupManagerService.getWakelock().acquire();
 
-                PerformBackupTask pbt = new PerformBackupTask(
+                KeyValueBackupTask.start(
                         backupManagerService,
-                        params.transportClient, params.dirName,
-                        kvQueue, null, params.observer, params.monitor, params.listener,
-                        params.fullPackages, true, params.nonIncrementalBackup);
-                Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
-                sendMessage(pbtMessage);
+                        params.transportClient,
+                        params.dirName,
+                        kvQueue,
+                        /* dataChangedJournal */ null,
+                        params.observer,
+                        params.monitor,
+                        params.listener,
+                        params.fullPackages,
+                        /* userInitiated */ true,
+                        params.nonIncrementalBackup);
                 break;
             }
 
diff --git a/services/backup/java/com/android/server/backup/internal/BackupRequest.java b/services/backup/java/com/android/server/backup/internal/BackupRequest.java
deleted file mode 100644
index 01e4385..0000000
--- a/services/backup/java/com/android/server/backup/internal/BackupRequest.java
+++ /dev/null
@@ -1,51 +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 java.util.Objects;
-
-/**
- * Set of backup services that have pending changes.
- */
-public class BackupRequest {
-    public String packageName;
-
-    public BackupRequest(String pkgName) {
-        packageName = pkgName;
-    }
-
-    public String toString() {
-        return "BackupRequest{pkg=" + packageName + "}";
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof BackupRequest)) {
-            return false;
-        }
-        BackupRequest that = (BackupRequest) o;
-        return Objects.equals(packageName, that.packageName);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(packageName);
-    }
-}
diff --git a/services/backup/java/com/android/server/backup/internal/BackupState.java b/services/backup/java/com/android/server/backup/internal/BackupState.java
deleted file mode 100644
index 937b167..0000000
--- a/services/backup/java/com/android/server/backup/internal/BackupState.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.android.server.backup.internal;
-
-/**
- * Current state of the backup.
- */
-enum BackupState {
-    INITIAL,
-    BACKUP_PM,
-    RUNNING_QUEUE,
-    FINAL
-}
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
deleted file mode 100644
index ae43299..0000000
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ /dev/null
@@ -1,1217 +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.DEBUG;
-import static com.android.server.backup.BackupManagerService.DEBUG_BACKUP_TRACE;
-import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_PENDING;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
-import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
-
-import android.annotation.Nullable;
-import android.app.ApplicationThreadConstants;
-import android.app.IBackupAgent;
-import android.app.backup.BackupDataInput;
-import android.app.backup.BackupDataOutput;
-import android.app.backup.BackupManager;
-import android.app.backup.BackupManagerMonitor;
-import android.app.backup.BackupTransport;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Message;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.SELinux;
-import android.os.UserHandle;
-import android.os.WorkSource;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.util.EventLog;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.backup.IBackupTransport;
-import com.android.internal.util.Preconditions;
-import com.android.server.AppWidgetBackupBridge;
-import com.android.server.EventLogTags;
-import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupRestoreTask;
-import com.android.server.backup.DataChangedJournal;
-import com.android.server.backup.KeyValueBackupJob;
-import com.android.server.backup.PackageManagerBackupAgent;
-import com.android.server.backup.BackupManagerService;
-import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
-import com.android.server.backup.transport.TransportClient;
-import com.android.server.backup.transport.TransportUtils;
-import com.android.server.backup.utils.AppBackupUtils;
-import com.android.server.backup.utils.BackupManagerMonitorUtils;
-import com.android.server.backup.utils.BackupObserverUtils;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * This class handles the process of backing up a given list of key/value backup packages.
- * Also takes in a list of pending dolly backups and kicks them off when key/value backups
- * are done.
- *
- * Flow:
- * If required, backup @pm@.
- * For each pending key/value backup package:
- *     - Bind to agent.
- *     - Call agent.doBackup()
- *     - Wait either for cancel/timeout or operationComplete() callback from the agent.
- * Start task to perform dolly backups.
- *
- * There are three entry points into this class:
- *     - execute() [Called from the handler thread]
- *     - operationComplete(long result) [Called from the handler thread]
- *     - handleCancel(boolean cancelAll) [Can be called from any thread]
- * These methods synchronize on mCancelLock.
- *
- * Interaction with mCurrentOperations:
- *     - An entry for this task is put into mCurrentOperations for the entire lifetime of the
- *       task. This is useful to cancel the task if required.
- *     - An ephemeral entry is put into mCurrentOperations each time we are waiting on for
- *       response from a backup agent. This is used to plumb timeouts and completion callbacks.
- */
-public class PerformBackupTask implements BackupRestoreTask {
-    private static final String TAG = "PerformBackupTask";
-
-    private BackupManagerService backupManagerService;
-    private final Object mCancelLock = new Object();
-
-    private ArrayList<BackupRequest> mQueue;
-    private ArrayList<BackupRequest> mOriginalQueue;
-    private File mStateDir;
-    @Nullable private DataChangedJournal mJournal;
-    private BackupState mCurrentState;
-    private List<String> mPendingFullBackups;
-    private IBackupObserver mObserver;
-    private IBackupManagerMonitor mMonitor;
-
-    private final TransportClient mTransportClient;
-    private final OnTaskFinishedListener mListener;
-    private final PerformFullTransportBackupTask mFullBackupTask;
-    private final int mCurrentOpToken;
-    private volatile int mEphemeralOpToken;
-
-    // carried information about the current in-flight operation
-    private IBackupAgent mAgentBinder;
-    private PackageInfo mCurrentPackage;
-    private File mSavedStateName;
-    private File mBackupDataName;
-    private File mNewStateName;
-    private ParcelFileDescriptor mSavedState;
-    private ParcelFileDescriptor mBackupData;
-    private ParcelFileDescriptor mNewState;
-    private int mStatus;
-    private boolean mFinished;
-    private final boolean mUserInitiated;
-    private final boolean mNonIncremental;
-    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
-
-    private volatile boolean mCancelAll;
-
-    public PerformBackupTask(BackupManagerService backupManagerService,
-            TransportClient transportClient, String dirName,
-            ArrayList<BackupRequest> queue, @Nullable DataChangedJournal journal,
-            IBackupObserver observer, IBackupManagerMonitor monitor,
-            @Nullable OnTaskFinishedListener listener, List<String> pendingFullBackups,
-            boolean userInitiated, boolean nonIncremental) {
-        this.backupManagerService = backupManagerService;
-        mTransportClient = transportClient;
-        mOriginalQueue = queue;
-        mQueue = new ArrayList<>();
-        mJournal = journal;
-        mObserver = observer;
-        mMonitor = monitor;
-        mListener = (listener != null) ? listener : OnTaskFinishedListener.NOP;
-        mPendingFullBackups = pendingFullBackups;
-        mUserInitiated = userInitiated;
-        mNonIncremental = nonIncremental;
-        mAgentTimeoutParameters = Preconditions.checkNotNull(
-                backupManagerService.getAgentTimeoutParameters(),
-                "Timeout parameters cannot be null");
-
-        mStateDir = new File(backupManagerService.getBaseStateDir(), dirName);
-        mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
-
-        mFinished = false;
-
-        synchronized (backupManagerService.getCurrentOpLock()) {
-            if (backupManagerService.isBackupOperationInProgress()) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Skipping backup since one is already in progress.");
-                }
-                mCancelAll = true;
-                mFullBackupTask = null;
-                mCurrentState = BackupState.FINAL;
-                backupManagerService.addBackupTrace("Skipped. Backup already in progress.");
-            } else {
-                mCurrentState = BackupState.INITIAL;
-                CountDownLatch latch = new CountDownLatch(1);
-                String[] fullBackups =
-                        mPendingFullBackups.toArray(new String[mPendingFullBackups.size()]);
-                mFullBackupTask =
-                        new PerformFullTransportBackupTask(backupManagerService,
-                                transportClient,
-                                /*fullBackupRestoreObserver*/ null,
-                                fullBackups, /*updateSchedule*/ false, /*runningJob*/ null,
-                                latch,
-                                mObserver, mMonitor, mListener, mUserInitiated);
-
-                registerTask();
-                backupManagerService.addBackupTrace("STATE => INITIAL");
-            }
-        }
-    }
-
-    /**
-     * Put this task in the repository of running tasks.
-     */
-    private void registerTask() {
-        synchronized (backupManagerService.getCurrentOpLock()) {
-            backupManagerService.getCurrentOperations().put(
-                    mCurrentOpToken, new Operation(OP_PENDING, this, OP_TYPE_BACKUP));
-        }
-    }
-
-    /**
-     * Remove this task from repository of running tasks.
-     */
-    private void unregisterTask() {
-        backupManagerService.removeOperation(mCurrentOpToken);
-    }
-
-    // Main entry point: perform one chunk of work, updating the state as appropriate
-    // and reposting the next chunk to the primary backup handler thread.
-    @Override
-    @GuardedBy("mCancelLock")
-    public void execute() {
-        synchronized (mCancelLock) {
-            switch (mCurrentState) {
-                case INITIAL:
-                    beginBackup();
-                    break;
-
-                case BACKUP_PM:
-                    backupPm();
-                    break;
-
-                case RUNNING_QUEUE:
-                    invokeNextAgent();
-                    break;
-
-                case FINAL:
-                    if (!mFinished) {
-                        finalizeBackup();
-                    } else {
-                        Slog.e(TAG, "Duplicate finish of K/V pass");
-                    }
-                    break;
-            }
-        }
-    }
-
-    // We're starting a backup pass.  Initialize the transport if we haven't already.
-    private void beginBackup() {
-        if (DEBUG_BACKUP_TRACE) {
-            backupManagerService.clearBackupTrace();
-            StringBuilder b = new StringBuilder(256);
-            b.append("beginBackup: [");
-            for (BackupRequest req : mOriginalQueue) {
-                b.append(' ');
-                b.append(req.packageName);
-            }
-            b.append(" ]");
-            backupManagerService.addBackupTrace(b.toString());
-        }
-
-        mAgentBinder = null;
-        mStatus = BackupTransport.TRANSPORT_OK;
-
-        // Sanity check: if the queue is empty we have no work to do.
-        if (mOriginalQueue.isEmpty() && mPendingFullBackups.isEmpty()) {
-            Slog.w(TAG, "Backup begun with an empty queue - nothing to do.");
-            backupManagerService.addBackupTrace("queue empty at begin");
-            executeNextState(BackupState.FINAL);
-            return;
-        }
-
-        // We need to retain the original queue contents in case of transport
-        // failure, but we want a working copy that we can manipulate along
-        // the way.
-        mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone();
-
-        // When the transport is forcing non-incremental key/value payloads, we send the
-        // metadata only if it explicitly asks for it.
-        boolean skipPm = mNonIncremental;
-
-        // The app metadata pseudopackage might also be represented in the
-        // backup queue if apps have been added/removed since the last time
-        // we performed a backup.  Drop it from the working queue now that
-        // we're committed to evaluating it for backup regardless.
-        for (int i = 0; i < mQueue.size(); i++) {
-            if (PACKAGE_MANAGER_SENTINEL.equals(
-                    mQueue.get(i).packageName)) {
-                if (MORE_DEBUG) {
-                    Slog.i(TAG, "Metadata in queue; eliding");
-                }
-                mQueue.remove(i);
-                skipPm = false;
-                break;
-            }
-        }
-
-        if (DEBUG) {
-            Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
-        }
-        File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
-        try {
-            IBackupTransport transport = mTransportClient.connectOrThrow("PBT.beginBackup()");
-            final String transportName = transport.transportDirName();
-            EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
-
-            // If we haven't stored package manager metadata yet, we must init the transport.
-            if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) {
-                Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
-                backupManagerService.addBackupTrace("initializing transport " + transportName);
-                backupManagerService.resetBackupState(mStateDir);  // Just to make sure.
-                mStatus = transport.initializeDevice();
-
-                backupManagerService.addBackupTrace("transport.initializeDevice() == " + mStatus);
-                if (mStatus == BackupTransport.TRANSPORT_OK) {
-                    EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
-                } else {
-                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
-                    Slog.e(TAG, "Transport error in initializeDevice()");
-                }
-            }
-
-            if (skipPm) {
-                Slog.d(TAG, "Skipping backup of package metadata.");
-                executeNextState(BackupState.RUNNING_QUEUE);
-            } else {
-                // As the package manager is running here in the system process we can just set up
-                // its agent directly. Thus we always run this pass because it's cheap and this way
-                // we guarantee that we don't get out of step even if we're selecting among various
-                // transports at run time.
-                if (mStatus == BackupTransport.TRANSPORT_OK) {
-                    executeNextState(BackupState.BACKUP_PM);
-                }
-            }
-        } catch (Exception e) {
-            Slog.e(TAG, "Error in backup thread during init", e);
-            backupManagerService.addBackupTrace("Exception in backup thread during init: " + e);
-            mStatus = BackupTransport.TRANSPORT_ERROR;
-        } finally {
-            // If we've succeeded so far, we will move to the BACKUP_PM state. If something has gone
-            // wrong then that won't have happen so cleanup.
-            backupManagerService.addBackupTrace("exiting prelim: " + mStatus);
-            if (mStatus != BackupTransport.TRANSPORT_OK) {
-                // if things went wrong at this point, we need to
-                // restage everything and try again later.
-                backupManagerService.resetBackupState(mStateDir);  // Just to make sure.
-                // In case of any other error, it's backup transport error.
-                executeNextState(BackupState.FINAL);
-            }
-        }
-    }
-
-    private void backupPm() {
-        try {
-            // The package manager doesn't have a proper <application> etc, but since it's running
-            // here in the system process we can just set up its agent directly and use a synthetic
-            // BackupRequest.
-            PackageManagerBackupAgent pmAgent = backupManagerService.makeMetadataAgent();
-            mStatus = invokeAgentForBackup(
-                    PACKAGE_MANAGER_SENTINEL,
-                    IBackupAgent.Stub.asInterface(pmAgent.onBind()));
-            backupManagerService.addBackupTrace("PMBA invoke: " + mStatus);
-
-            // Because the PMBA is a local instance, it has already executed its backup callback and
-            // returned.  Blow away the lingering (spurious) pending timeout message for it.
-            backupManagerService.getBackupHandler().removeMessages(
-                    MSG_BACKUP_OPERATION_TIMEOUT);
-        } catch (Exception e) {
-            Slog.e(TAG, "Error in backup thread during pm", e);
-            backupManagerService.addBackupTrace("Exception in backup thread during pm: " + e);
-            mStatus = BackupTransport.TRANSPORT_ERROR;
-        } finally {
-            // If we've succeeded so far, invokeAgentForBackup() will have run the PM
-            // metadata and its completion/timeout callback will continue the state
-            // machine chain.  If it failed that won't happen; we handle that now.
-            backupManagerService.addBackupTrace("exiting backupPm: " + mStatus);
-            if (mStatus != BackupTransport.TRANSPORT_OK) {
-                // if things went wrong at this point, we need to
-                // restage everything and try again later.
-                backupManagerService.resetBackupState(mStateDir);  // Just to make sure.
-                executeNextState(BackupState.FINAL);
-            }
-        }
-    }
-
-    // Transport has been initialized and the PM metadata submitted successfully
-    // if that was warranted.  Now we process the single next thing in the queue.
-    private void invokeNextAgent() {
-        mStatus = BackupTransport.TRANSPORT_OK;
-        backupManagerService.addBackupTrace("invoke q=" + mQueue.size());
-
-        // Sanity check that we have work to do.  If not, skip to the end where
-        // we reestablish the wakelock invariants etc.
-        if (mQueue.isEmpty()) {
-            if (MORE_DEBUG) Slog.i(TAG, "queue now empty");
-            executeNextState(BackupState.FINAL);
-            return;
-        }
-
-        // pop the entry we're going to process on this step
-        BackupRequest request = mQueue.get(0);
-        mQueue.remove(0);
-
-        Slog.d(TAG, "starting key/value backup of " + request);
-        backupManagerService.addBackupTrace("launch agent for " + request.packageName);
-
-        // Verify that the requested app exists; it might be something that
-        // requested a backup but was then uninstalled.  The request was
-        // journalled and rather than tamper with the journal it's safer
-        // to sanity-check here.  This also gives us the classname of the
-        // package's backup agent.
-        try {
-            PackageManager pm = backupManagerService.getPackageManager();
-            mCurrentPackage = pm.getPackageInfo(request.packageName,
-                    PackageManager.GET_SIGNING_CERTIFICATES);
-            if (!AppBackupUtils.appIsEligibleForBackup(mCurrentPackage.applicationInfo, pm)) {
-                // The manifest has changed but we had a stale backup request pending.
-                // This won't happen again because the app won't be requesting further
-                // backups.
-                Slog.i(TAG, "Package " + request.packageName
-                        + " no longer supports backup; skipping");
-                backupManagerService.addBackupTrace("skipping - not eligible, completion is noop");
-                // Shouldn't happen in case of requested backup, as pre-check was done in
-                // #requestBackup(), except to app update done concurrently
-                BackupObserverUtils.sendBackupOnPackageResult(mObserver,
-                        mCurrentPackage.packageName,
-                        BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-                executeNextState(BackupState.RUNNING_QUEUE);
-                return;
-            }
-
-            if (AppBackupUtils.appGetsFullBackup(mCurrentPackage)) {
-                // It's possible that this app *formerly* was enqueued for key/value backup,
-                // but has since been updated and now only supports the full-data path.
-                // Don't proceed with a key/value backup for it in this case.
-                Slog.i(TAG, "Package " + request.packageName
-                        + " requests full-data rather than key/value; skipping");
-                backupManagerService.addBackupTrace(
-                        "skipping - fullBackupOnly, completion is noop");
-                // Shouldn't happen in case of requested backup, as pre-check was done in
-                // #requestBackup()
-                BackupObserverUtils.sendBackupOnPackageResult(mObserver,
-                        mCurrentPackage.packageName,
-                        BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-                executeNextState(BackupState.RUNNING_QUEUE);
-                return;
-            }
-
-            if (AppBackupUtils.appIsStopped(mCurrentPackage.applicationInfo)) {
-                // The app has been force-stopped or cleared or just installed,
-                // and not yet launched out of that state, so just as it won't
-                // receive broadcasts, we won't run it for backup.
-                backupManagerService.addBackupTrace("skipping - stopped");
-                BackupObserverUtils.sendBackupOnPackageResult(mObserver,
-                        mCurrentPackage.packageName,
-                        BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-                executeNextState(BackupState.RUNNING_QUEUE);
-                return;
-            }
-
-            IBackupAgent agent = null;
-            try {
-                backupManagerService.getWakelock().setWorkSource(
-                        new WorkSource(mCurrentPackage.applicationInfo.uid));
-                agent = backupManagerService.bindToAgentSynchronous(mCurrentPackage.applicationInfo,
-                        ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
-                backupManagerService.addBackupTrace("agent bound; a? = " + (agent != null));
-                if (agent != null) {
-                    mAgentBinder = agent;
-                    mStatus = invokeAgentForBackup(request.packageName, agent);
-                    // at this point we'll either get a completion callback from the
-                    // agent, or a timeout message on the main handler.  either way, we're
-                    // done here as long as we're successful so far.
-                } else {
-                    // Timeout waiting for the agent
-                    mStatus = BackupTransport.AGENT_ERROR;
-                }
-            } catch (SecurityException ex) {
-                // Try for the next one.
-                Slog.d(TAG, "error in bind/backup", ex);
-                mStatus = BackupTransport.AGENT_ERROR;
-                backupManagerService.addBackupTrace("agent SE");
-            }
-        } catch (NameNotFoundException e) {
-            Slog.d(TAG, "Package does not exist; skipping");
-            backupManagerService.addBackupTrace("no such package");
-            mStatus = BackupTransport.AGENT_UNKNOWN;
-        } finally {
-            backupManagerService.getWakelock().setWorkSource(null);
-
-            // If there was an agent error, no timeout/completion handling will occur.
-            // That means we need to direct to the next state ourselves.
-            if (mStatus != BackupTransport.TRANSPORT_OK) {
-                BackupState nextState = BackupState.RUNNING_QUEUE;
-                mAgentBinder = null;
-
-                // An agent-level failure means we reenqueue this one agent for
-                // a later retry, but otherwise proceed normally.
-                if (mStatus == BackupTransport.AGENT_ERROR) {
-                    if (MORE_DEBUG) {
-                        Slog.i(TAG, "Agent failure for " + request.packageName
-                                + " - restaging");
-                    }
-                    backupManagerService.dataChangedImpl(request.packageName);
-                    mStatus = BackupTransport.TRANSPORT_OK;
-                    if (mQueue.isEmpty()) nextState = BackupState.FINAL;
-                    BackupObserverUtils
-                            .sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
-                                    BackupManager.ERROR_AGENT_FAILURE);
-                } else if (mStatus == BackupTransport.AGENT_UNKNOWN) {
-                    // Failed lookup of the app, so we couldn't bring up an agent, but
-                    // we're otherwise fine.  Just drop it and go on to the next as usual.
-                    mStatus = BackupTransport.TRANSPORT_OK;
-                    BackupObserverUtils
-                            .sendBackupOnPackageResult(mObserver, request.packageName,
-                                    BackupManager.ERROR_PACKAGE_NOT_FOUND);
-                } else {
-                    // Transport-level failure means we reenqueue everything
-                    revertAndEndBackup();
-                    nextState = BackupState.FINAL;
-                }
-
-                executeNextState(nextState);
-            } else {
-                // success case
-                backupManagerService.addBackupTrace("expecting completion/timeout callback");
-            }
-        }
-    }
-
-    private void finalizeBackup() {
-        backupManagerService.addBackupTrace("finishing");
-
-        // Mark packages that we didn't backup (because backup was cancelled, etc.) as needing
-        // backup.
-        for (BackupRequest req : mQueue) {
-            backupManagerService.dataChangedImpl(req.packageName);
-        }
-
-        // Either backup was successful, in which case we of course do not need
-        // this pass's journal any more; or it failed, in which case we just
-        // re-enqueued all of these packages in the current active journal.
-        // Either way, we no longer need this pass's journal.
-        if (mJournal != null && !mJournal.delete()) {
-            Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
-        }
-
-        // If everything actually went through and this is the first time we've
-        // done a backup, we can now record what the current backup dataset token
-        // is.
-        String callerLogString = "PBT.finalizeBackup()";
-        if ((backupManagerService.getCurrentToken() == 0) && (mStatus
-                == BackupTransport.TRANSPORT_OK)) {
-            backupManagerService.addBackupTrace("success; recording token");
-            try {
-                IBackupTransport transport =
-                        mTransportClient.connectOrThrow(callerLogString);
-                backupManagerService.setCurrentToken(transport.getCurrentRestoreSet());
-                backupManagerService.writeRestoreTokens();
-            } catch (Exception e) {
-                // nothing for it at this point, unfortunately, but this will be
-                // recorded the next time we fully succeed.
-                Slog.e(TAG, "Transport threw reporting restore set: " + e.getMessage());
-                backupManagerService.addBackupTrace("transport threw returning token");
-            }
-        }
-
-        // Set up the next backup pass - at this point we can set mBackupRunning
-        // to false to allow another pass to fire, because we're done with the
-        // state machine sequence and the wakelock is refcounted.
-        synchronized (backupManagerService.getQueueLock()) {
-            backupManagerService.setBackupRunning(false);
-            if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
-                // Make sure we back up everything and perform the one-time init
-                if (MORE_DEBUG) {
-                    Slog.d(TAG, "Server requires init; rerunning");
-                }
-                backupManagerService.addBackupTrace("init required; rerunning");
-                try {
-                    String name = backupManagerService.getTransportManager()
-                            .getTransportName(mTransportClient.getTransportComponent());
-                    backupManagerService.getPendingInits().add(name);
-                } catch (Exception e) {
-                    Slog.w(TAG, "Failed to query transport name for init: " + e.getMessage());
-                    // swallow it and proceed; we don't rely on this
-                }
-                clearMetadata();
-                backupManagerService.backupNow();
-            }
-        }
-
-        backupManagerService.clearBackupTrace();
-
-        unregisterTask();
-
-        if (!mCancelAll && mStatus == BackupTransport.TRANSPORT_OK &&
-                mPendingFullBackups != null && !mPendingFullBackups.isEmpty()) {
-            Slog.d(TAG, "Starting full backups for: " + mPendingFullBackups);
-            // Acquiring wakelock for PerformFullTransportBackupTask before its start.
-            backupManagerService.getWakelock().acquire();
-            // The full-backup task is now responsible for calling onFinish() on mListener, which
-            // was the listener we passed it.
-            (new Thread(mFullBackupTask, "full-transport-requested")).start();
-        } else if (mCancelAll) {
-            mListener.onFinished(callerLogString);
-            if (mFullBackupTask != null) {
-                mFullBackupTask.unregisterTask();
-            }
-            BackupObserverUtils.sendBackupFinished(mObserver,
-                    BackupManager.ERROR_BACKUP_CANCELLED);
-        } else {
-            mListener.onFinished(callerLogString);
-            mFullBackupTask.unregisterTask();
-            switch (mStatus) {
-                case BackupTransport.TRANSPORT_OK:
-                case BackupTransport.TRANSPORT_QUOTA_EXCEEDED:
-                case BackupTransport.TRANSPORT_PACKAGE_REJECTED:
-                    BackupObserverUtils.sendBackupFinished(mObserver,
-                            BackupManager.SUCCESS);
-                    break;
-                case BackupTransport.TRANSPORT_NOT_INITIALIZED:
-                    BackupObserverUtils.sendBackupFinished(mObserver,
-                            BackupManager.ERROR_TRANSPORT_ABORTED);
-                    break;
-                case BackupTransport.TRANSPORT_ERROR:
-                default:
-                    BackupObserverUtils.sendBackupFinished(mObserver,
-                            BackupManager.ERROR_TRANSPORT_ABORTED);
-                    break;
-            }
-        }
-        mFinished = true;
-        Slog.i(TAG, "K/V backup pass finished.");
-        // Only once we're entirely finished do we release the wakelock for k/v backup.
-        backupManagerService.getWakelock().release();
-    }
-
-    // Remove the PM metadata state. This will generate an init on the next pass.
-    private void clearMetadata() {
-        final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
-        if (pmState.exists()) pmState.delete();
-    }
-
-    // Invoke an agent's doBackup() and start a timeout message spinning on the main
-    // handler in case it doesn't get back to us.
-    private int invokeAgentForBackup(String packageName, IBackupAgent agent) {
-        if (DEBUG) {
-            Slog.d(TAG, "invokeAgentForBackup on " + packageName);
-        }
-        backupManagerService.addBackupTrace("invoking " + packageName);
-
-        File blankStateName = new File(mStateDir, "blank_state");
-        mSavedStateName = new File(mStateDir, packageName);
-        mBackupDataName = new File(backupManagerService.getDataDir(), packageName + ".data");
-        mNewStateName = new File(mStateDir, packageName + ".new");
-        if (MORE_DEBUG) Slog.d(TAG, "data file: " + mBackupDataName);
-
-        mSavedState = null;
-        mBackupData = null;
-        mNewState = null;
-
-        boolean callingAgent = false;
-        mEphemeralOpToken = backupManagerService.generateRandomIntegerToken();
-        try {
-            // Look up the package info & signatures.  This is first so that if it
-            // throws an exception, there's no file setup yet that would need to
-            // be unraveled.
-            if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
-                // The metadata 'package' is synthetic; construct one and make
-                // sure our global state is pointed at it
-                mCurrentPackage = new PackageInfo();
-                mCurrentPackage.packageName = packageName;
-            }
-
-            // In a full backup, we pass a null ParcelFileDescriptor as
-            // the saved-state "file". For key/value backups we pass the old state if
-            // an incremental backup is required, and a blank state otherwise.
-            mSavedState = ParcelFileDescriptor.open(
-                    mNonIncremental ? blankStateName : mSavedStateName,
-                    ParcelFileDescriptor.MODE_READ_ONLY |
-                            ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
-
-            mBackupData = ParcelFileDescriptor.open(mBackupDataName,
-                    ParcelFileDescriptor.MODE_READ_WRITE |
-                            ParcelFileDescriptor.MODE_CREATE |
-                            ParcelFileDescriptor.MODE_TRUNCATE);
-
-            if (!SELinux.restorecon(mBackupDataName)) {
-                Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName);
-            }
-
-            mNewState = ParcelFileDescriptor.open(mNewStateName,
-                    ParcelFileDescriptor.MODE_READ_WRITE |
-                            ParcelFileDescriptor.MODE_CREATE |
-                            ParcelFileDescriptor.MODE_TRUNCATE);
-
-            IBackupTransport transport =
-                    mTransportClient.connectOrThrow("PBT.invokeAgentForBackup()");
-
-            final long quota = transport.getBackupQuota(packageName, false /* isFullBackup */);
-            callingAgent = true;
-
-            // Initiate the target's backup pass
-            backupManagerService.addBackupTrace("setting timeout");
-            long kvBackupAgentTimeoutMillis =
-                    mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
-            backupManagerService.prepareOperationTimeout(
-                    mEphemeralOpToken, kvBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
-            backupManagerService.addBackupTrace("calling agent doBackup()");
-
-            agent.doBackup(
-                    mSavedState, mBackupData, mNewState, quota, mEphemeralOpToken,
-                    backupManagerService.getBackupManagerBinder(), transport.getTransportFlags());
-        } catch (Exception e) {
-            Slog.e(TAG, "Error invoking for backup on " + packageName + ". " + e);
-            backupManagerService.addBackupTrace("exception: " + e);
-            EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
-                    e.toString());
-            errorCleanup();
-            return callingAgent ? BackupTransport.AGENT_ERROR
-                    : BackupTransport.TRANSPORT_ERROR;
-        } finally {
-            if (mNonIncremental) {
-                blankStateName.delete();
-            }
-        }
-
-        // At this point the agent is off and running.  The next thing to happen will
-        // either be a callback from the agent, at which point we'll process its data
-        // for transport, or a timeout.  Either way the next phase will happen in
-        // response to the TimeoutHandler interface callbacks.
-        backupManagerService.addBackupTrace("invoke success");
-        return BackupTransport.TRANSPORT_OK;
-    }
-
-    private void failAgent(IBackupAgent agent, String message) {
-        try {
-            agent.fail(message);
-        } catch (Exception e) {
-            Slog.w(TAG, "Error conveying failure to " + mCurrentPackage.packageName);
-        }
-    }
-
-    // SHA-1 a byte array and return the result in hex
-    private String SHA1Checksum(byte[] input) {
-        final byte[] checksum;
-        try {
-            MessageDigest md = MessageDigest.getInstance("SHA-1");
-            checksum = md.digest(input);
-        } catch (NoSuchAlgorithmException e) {
-            Slog.e(TAG, "Unable to use SHA-1!");
-            return "00";
-        }
-
-        StringBuffer sb = new StringBuffer(checksum.length * 2);
-        for (int i = 0; i < checksum.length; i++) {
-            sb.append(Integer.toHexString(checksum[i]));
-        }
-        return sb.toString();
-    }
-
-    private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
-            throws IOException {
-        // TODO: http://b/22388012
-        byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
-                UserHandle.USER_SYSTEM);
-        // has the widget state changed since last time?
-        final File widgetFile = new File(mStateDir, pkgName + "_widget");
-        final boolean priorStateExists = widgetFile.exists();
-
-        if (MORE_DEBUG) {
-            if (priorStateExists || widgetState != null) {
-                Slog.i(TAG, "Checking widget update: state=" + (widgetState != null)
-                        + " prior=" + priorStateExists);
-            }
-        }
-
-        if (!priorStateExists && widgetState == null) {
-            // no prior state, no new state => nothing to do
-            return;
-        }
-
-        // if the new state is not null, we might need to compare checksums to
-        // determine whether to update the widget blob in the archive.  If the
-        // widget state *is* null, we know a priori at this point that we simply
-        // need to commit a deletion for it.
-        String newChecksum = null;
-        if (widgetState != null) {
-            newChecksum = SHA1Checksum(widgetState);
-            if (priorStateExists) {
-                final String priorChecksum;
-                try (
-                        FileInputStream fin = new FileInputStream(widgetFile);
-                        DataInputStream in = new DataInputStream(fin)
-                ) {
-                    priorChecksum = in.readUTF();
-                }
-                if (Objects.equals(newChecksum, priorChecksum)) {
-                    // Same checksum => no state change => don't rewrite the widget data
-                    return;
-                }
-            }
-        } // else widget state *became* empty, so we need to commit a deletion
-
-        BackupDataOutput out = new BackupDataOutput(fd);
-        if (widgetState != null) {
-            try (
-                    FileOutputStream fout = new FileOutputStream(widgetFile);
-                    DataOutputStream stateOut = new DataOutputStream(fout)
-            ) {
-                stateOut.writeUTF(newChecksum);
-            }
-
-            out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
-            out.writeEntityData(widgetState, widgetState.length);
-        } else {
-            // Widget state for this app has been removed; commit a deletion
-            out.writeEntityHeader(KEY_WIDGET_STATE, -1);
-            widgetFile.delete();
-        }
-    }
-
-    @Override
-    @GuardedBy("mCancelLock")
-    public void operationComplete(long unusedResult) {
-        backupManagerService.removeOperation(mEphemeralOpToken);
-        synchronized (mCancelLock) {
-            // The agent reported back to us!
-            if (mFinished) {
-                Slog.d(TAG, "operationComplete received after task finished.");
-                return;
-            }
-
-            if (mBackupData == null) {
-                // This callback was racing with our timeout, so we've cleaned up the
-                // agent state already and are on to the next thing.  We have nothing
-                // further to do here: agent state having been cleared means that we've
-                // initiated the appropriate next operation.
-                final String pkg = (mCurrentPackage != null)
-                        ? mCurrentPackage.packageName : "[none]";
-                if (MORE_DEBUG) {
-                    Slog.i(TAG, "Callback after agent teardown: " + pkg);
-                }
-                backupManagerService.addBackupTrace("late opComplete; curPkg = " + pkg);
-                return;
-            }
-
-            final String pkgName = mCurrentPackage.packageName;
-            final long filepos = mBackupDataName.length();
-            FileDescriptor fd = mBackupData.getFileDescriptor();
-            try {
-                // If it's a 3rd party app, see whether they wrote any protected keys
-                // and complain mightily if they are attempting shenanigans.
-                if (mCurrentPackage.applicationInfo != null &&
-                        (mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
-                                == 0) {
-                    ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataName,
-                            ParcelFileDescriptor.MODE_READ_ONLY);
-                    BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor());
-                    try {
-                        while (in.readNextHeader()) {
-                            final String key = in.getKey();
-                            if (key != null && key.charAt(0) >= 0xff00) {
-                                // Not okay: crash them and bail.
-                                failAgent(mAgentBinder, "Illegal backup key: " + key);
-                                backupManagerService
-                                        .addBackupTrace("illegal key " + key + " from " + pkgName);
-                                EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName,
-                                        "bad key");
-                                mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
-                                        BackupManagerMonitor.LOG_EVENT_ID_ILLEGAL_KEY,
-                                        mCurrentPackage,
-                                        BackupManagerMonitor
-                                                .LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
-                                        BackupManagerMonitorUtils.putMonitoringExtra(null,
-                                                BackupManagerMonitor.EXTRA_LOG_ILLEGAL_KEY,
-                                                key));
-                                backupManagerService.getBackupHandler().removeMessages(
-                                        MSG_BACKUP_OPERATION_TIMEOUT);
-                                BackupObserverUtils
-                                        .sendBackupOnPackageResult(mObserver, pkgName,
-                                                BackupManager.ERROR_AGENT_FAILURE);
-                                errorCleanup();
-                                if (MORE_DEBUG) {
-                                    Slog.i(TAG, "Agent failure for " + pkgName
-                                            + " with illegal key: " + key + "; dropped");
-                                }
-                                executeNextState(mQueue.isEmpty() ? BackupState.FINAL
-                                        : BackupState.RUNNING_QUEUE);
-                                return;
-                            }
-                            in.skipEntityData();
-                        }
-                    } finally {
-                        if (readFd != null) {
-                            readFd.close();
-                        }
-                    }
-                }
-
-                // Piggyback the widget state payload, if any
-                writeWidgetPayloadIfAppropriate(fd, pkgName);
-            } catch (IOException e) {
-                // Hard disk error; recovery/failure policy TBD.  For now roll back,
-                // but we may want to consider this a transport-level failure (i.e.
-                // we're in such a bad state that we can't contemplate doing backup
-                // operations any more during this pass).
-                Slog.w(TAG, "Unable to save widget state for " + pkgName);
-                try {
-                    Os.ftruncate(fd, filepos);
-                } catch (ErrnoException ee) {
-                    Slog.w(TAG, "Unable to roll back!");
-                }
-            }
-
-            // Spin the data off to the transport and proceed with the next stage.
-            if (MORE_DEBUG) {
-                Slog.v(TAG, "operationComplete(): sending data to transport for "
-                        + pkgName);
-            }
-            backupManagerService.getBackupHandler().removeMessages(MSG_BACKUP_OPERATION_TIMEOUT);
-            clearAgentState();
-            backupManagerService.addBackupTrace("operation complete");
-
-            IBackupTransport transport = mTransportClient.connect("PBT.operationComplete()");
-            ParcelFileDescriptor backupData = null;
-            mStatus = BackupTransport.TRANSPORT_OK;
-            long size = 0;
-            try {
-                TransportUtils.checkTransportNotNull(transport);
-                size = mBackupDataName.length();
-                if (size > 0) {
-                    boolean isNonIncremental = mSavedStateName.length() == 0;
-                    if (mStatus == BackupTransport.TRANSPORT_OK) {
-                        backupData = ParcelFileDescriptor.open(mBackupDataName,
-                                ParcelFileDescriptor.MODE_READ_ONLY);
-                        backupManagerService.addBackupTrace("sending data to transport");
-
-                        int userInitiatedFlag =
-                                mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
-                        int incrementalFlag =
-                                isNonIncremental
-                                    ? BackupTransport.FLAG_NON_INCREMENTAL
-                                    : BackupTransport.FLAG_INCREMENTAL;
-                        int flags = userInitiatedFlag | incrementalFlag;
-
-                        mStatus = transport.performBackup(mCurrentPackage, backupData, flags);
-                    }
-
-                    if (isNonIncremental
-                        && mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
-                        // TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED is only valid if the backup was
-                        // incremental, as if the backup is non-incremental there is no state to
-                        // clear. This avoids us ending up in a retry loop if the transport always
-                        // returns this code.
-                        Slog.w(TAG,
-                                "Transport requested non-incremental but already the case, error");
-                        backupManagerService.addBackupTrace(
-                                "Transport requested non-incremental but already the case, error");
-                        mStatus = BackupTransport.TRANSPORT_ERROR;
-                    }
-
-                    // TODO - We call finishBackup() for each application backed up, because
-                    // we need to know now whether it succeeded or failed.  Instead, we should
-                    // hold off on finishBackup() until the end, which implies holding off on
-                    // renaming *all* the output state files (see below) until that happens.
-
-                    backupManagerService.addBackupTrace("data delivered: " + mStatus);
-                    if (mStatus == BackupTransport.TRANSPORT_OK) {
-                        backupManagerService.addBackupTrace("finishing op on transport");
-                        mStatus = transport.finishBackup();
-                        backupManagerService.addBackupTrace("finished: " + mStatus);
-                    } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
-                        backupManagerService.addBackupTrace("transport rejected package");
-                    }
-                } else {
-                    if (MORE_DEBUG) {
-                        Slog.i(TAG, "no backup data written; not calling transport");
-                    }
-                    backupManagerService.addBackupTrace("no data to send");
-                    mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
-                            BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND,
-                            mCurrentPackage,
-                            BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
-                            null);
-                }
-
-                if (mStatus == BackupTransport.TRANSPORT_OK) {
-                    // After successful transport, delete the now-stale data
-                    // and juggle the files so that next time we supply the agent
-                    // with the new state file it just created.
-                    mBackupDataName.delete();
-                    mNewStateName.renameTo(mSavedStateName);
-                    BackupObserverUtils
-                            .sendBackupOnPackageResult(mObserver, pkgName, BackupManager.SUCCESS);
-                    EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
-                    backupManagerService.logBackupComplete(pkgName);
-                } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
-                    // The transport has rejected backup of this specific package.  Roll it
-                    // back but proceed with running the rest of the queue.
-                    mBackupDataName.delete();
-                    mNewStateName.delete();
-                    BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
-                            BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
-                    EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
-                } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
-                    BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
-                            BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
-                    EventLog.writeEvent(EventLogTags.BACKUP_QUOTA_EXCEEDED, pkgName);
-
-                } else if (mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
-                    Slog.i(TAG, "Transport lost data, retrying package");
-                    backupManagerService.addBackupTrace(
-                            "Transport lost data, retrying package:" + pkgName);
-                    BackupManagerMonitorUtils.monitorEvent(
-                            mMonitor,
-                            BackupManagerMonitor
-                                    .LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED,
-                            mCurrentPackage,
-                            BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
-                            /*extras=*/ null);
-
-                    mBackupDataName.delete();
-                    mSavedStateName.delete();
-                    mNewStateName.delete();
-
-                    // Immediately retry the package by adding it back to the front of the queue.
-                    // We cannot add @pm@ to the queue because we back it up separately at the start
-                    // of the backup pass in state BACKUP_PM. Instead we retry this state (see
-                    // below).
-                    if (!PACKAGE_MANAGER_SENTINEL.equals(pkgName)) {
-                        mQueue.add(0, new BackupRequest(pkgName));
-                    }
-
-                } else {
-                    // Actual transport-level failure to communicate the data to the backend
-                    BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
-                            BackupManager.ERROR_TRANSPORT_ABORTED);
-                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
-                }
-            } catch (Exception e) {
-                BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
-                        BackupManager.ERROR_TRANSPORT_ABORTED);
-                Slog.e(TAG, "Transport error backing up " + pkgName, e);
-                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
-                mStatus = BackupTransport.TRANSPORT_ERROR;
-            } finally {
-                try {
-                    if (backupData != null) backupData.close();
-                } catch (IOException e) {
-                }
-            }
-
-            final BackupState nextState;
-            if (mStatus == BackupTransport.TRANSPORT_OK
-                    || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
-                // Success or single-package rejection.  Proceed with the next app if any,
-                // otherwise we're done.
-                nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
-
-            } else if (mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
-                // We want to immediately retry the current package.
-                if (PACKAGE_MANAGER_SENTINEL.equals(pkgName)) {
-                    nextState = BackupState.BACKUP_PM;
-                } else {
-                    // This is an ordinary package so we will have added it back into the queue
-                    // above. Thus, we proceed processing the queue.
-                    nextState = BackupState.RUNNING_QUEUE;
-                }
-
-            } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
-                if (MORE_DEBUG) {
-                    Slog.d(TAG, "Package " + mCurrentPackage.packageName +
-                            " hit quota limit on k/v backup");
-                }
-                if (mAgentBinder != null) {
-                    try {
-                        TransportUtils.checkTransportNotNull(transport);
-                        long quota = transport.getBackupQuota(mCurrentPackage.packageName, false);
-                        mAgentBinder.doQuotaExceeded(size, quota);
-                    } catch (Exception e) {
-                        Slog.e(TAG, "Unable to notify about quota exceeded: " + e.getMessage());
-                    }
-                }
-                nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
-            } else {
-                // Any other error here indicates a transport-level failure.  That means
-                // we need to halt everything and reschedule everything for next time.
-                revertAndEndBackup();
-                nextState = BackupState.FINAL;
-            }
-
-            executeNextState(nextState);
-        }
-    }
-
-
-    @Override
-    @GuardedBy("mCancelLock")
-    public void handleCancel(boolean cancelAll) {
-        backupManagerService.removeOperation(mEphemeralOpToken);
-        synchronized (mCancelLock) {
-            if (mFinished) {
-                // We have already cancelled this operation.
-                if (MORE_DEBUG) {
-                    Slog.d(TAG, "Ignoring stale cancel. cancelAll=" + cancelAll);
-                }
-                return;
-            }
-            mCancelAll = cancelAll;
-            final String logPackageName = (mCurrentPackage != null)
-                    ? mCurrentPackage.packageName
-                    : "no_package_yet";
-            Slog.i(TAG, "Cancel backing up " + logPackageName);
-            EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, logPackageName);
-            backupManagerService.addBackupTrace(
-                    "cancel of " + logPackageName + ", cancelAll=" + cancelAll);
-            mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
-                    BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL,
-                    mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
-                    BackupManagerMonitorUtils.putMonitoringExtra(null,
-                            BackupManagerMonitor.EXTRA_LOG_CANCEL_ALL,
-                            mCancelAll));
-            errorCleanup();
-            if (!cancelAll) {
-                // The current agent either timed out or was cancelled running doBackup().
-                // Restage it for the next time we run a backup pass.
-                // !!! TODO: keep track of failure counts per agent, and blacklist those which
-                // fail repeatedly (i.e. have proved themselves to be buggy).
-                executeNextState(
-                        mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE);
-                backupManagerService.dataChangedImpl(mCurrentPackage.packageName);
-            } else {
-                finalizeBackup();
-            }
-        }
-    }
-
-    private void revertAndEndBackup() {
-        if (MORE_DEBUG) {
-            Slog.i(TAG, "Reverting backup queue - restaging everything");
-        }
-        backupManagerService.addBackupTrace("transport error; reverting");
-
-        // We want to reset the backup schedule based on whatever the transport suggests
-        // by way of retry/backoff time.
-        long delay;
-        try {
-            IBackupTransport transport =
-                    mTransportClient.connectOrThrow("PBT.revertAndEndBackup()");
-            delay = transport.requestBackupTime();
-        } catch (Exception e) {
-            Slog.w(TAG, "Unable to contact transport for recommended backoff: " + e.getMessage());
-            delay = 0;  // use the scheduler's default
-        }
-        KeyValueBackupJob.schedule(backupManagerService.getContext(), delay,
-                backupManagerService.getConstants());
-
-        for (BackupRequest request : mOriginalQueue) {
-            backupManagerService.dataChangedImpl(request.packageName);
-        }
-
-    }
-
-    private void errorCleanup() {
-        mBackupDataName.delete();
-        mNewStateName.delete();
-        clearAgentState();
-    }
-
-    // Cleanup common to both success and failure cases
-    private void clearAgentState() {
-        try {
-            if (mSavedState != null) mSavedState.close();
-        } catch (IOException e) {
-        }
-        try {
-            if (mBackupData != null) mBackupData.close();
-        } catch (IOException e) {
-        }
-        try {
-            if (mNewState != null) mNewState.close();
-        } catch (IOException e) {
-        }
-        synchronized (backupManagerService.getCurrentOpLock()) {
-            // Current-operation callback handling requires the validity of these various
-            // bits of internal state as an invariant of the operation still being live.
-            // This means we make sure to clear all of the state in unison inside the lock.
-            backupManagerService.getCurrentOperations().remove(mEphemeralOpToken);
-            mSavedState = mBackupData = mNewState = null;
-        }
-
-        // If this was a pseudopackage there's no associated Activity Manager state
-        if (mCurrentPackage.applicationInfo != null) {
-            backupManagerService.addBackupTrace("unbinding " + mCurrentPackage.packageName);
-            try {  // unbind even on timeout, just in case
-                backupManagerService.getActivityManager().unbindBackupAgent(
-                        mCurrentPackage.applicationInfo);
-            } catch (RemoteException e) { /* can't happen; activity manager is local */ }
-        }
-    }
-
-    private void executeNextState(BackupState nextState) {
-        if (MORE_DEBUG) {
-            Slog.i(TAG, " => executing next step on "
-                    + this + " nextState=" + nextState);
-        }
-        backupManagerService.addBackupTrace("executeNextState => " + nextState);
-        mCurrentState = nextState;
-        Message msg = backupManagerService.getBackupHandler().obtainMessage(
-                MSG_BACKUP_RESTORE_STEP, this);
-        backupManagerService.getBackupHandler().sendMessage(msg);
-    }
-}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/BackupRequest.java b/services/backup/java/com/android/server/backup/keyvalue/BackupRequest.java
new file mode 100644
index 0000000..67b2f72
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/keyvalue/BackupRequest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.keyvalue;
+
+import java.util.Objects;
+
+/**
+ * Set of backup services that have pending changes.
+ */
+public class BackupRequest {
+    public String packageName;
+
+    public BackupRequest(String pkgName) {
+        packageName = pkgName;
+    }
+
+    public String toString() {
+        return "BackupRequest{pkg=" + packageName + "}";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof BackupRequest)) {
+            return false;
+        }
+        BackupRequest that = (BackupRequest) o;
+        return Objects.equals(packageName, that.packageName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(packageName);
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
new file mode 100644
index 0000000..1cb9933
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -0,0 +1,1366 @@
+/*
+ * 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.keyvalue;
+
+import static com.android.server.backup.BackupManagerService.DEBUG_BACKUP_TRACE;
+import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE;
+import static com.android.server.backup.BackupManagerService.OP_PENDING;
+import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
+import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
+
+import android.annotation.Nullable;
+import android.app.ApplicationThreadConstants;
+import android.app.IBackupAgent;
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupManagerMonitor;
+import android.app.backup.BackupTransport;
+import android.app.backup.IBackupCallback;
+import android.app.backup.IBackupManager;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.ConditionVariable;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.os.UserHandle;
+import android.os.WorkSource;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.EventLog;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
+import com.android.server.AppWidgetBackupBridge;
+import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.internal.Operation;
+import com.android.server.backup.remote.RemoteCall;
+import com.android.server.backup.remote.RemoteCallable;
+import com.android.server.backup.remote.RemoteResult;
+import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.utils.AppBackupUtils;
+import com.android.server.backup.utils.BackupManagerMonitorUtils;
+import com.android.server.backup.utils.BackupObserverUtils;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Represents the task of performing a sequence of key-value backups for a given list of packages.
+ * Method {@link #run()} executes the backups to the transport specified via the {@code
+ * transportClient} parameter in the constructor.
+ *
+ * <p>A few definitions:
+ *
+ * <ul>
+ *   <li>State directory: {@link BackupManagerService#getBaseStateDir()}/&lt;transport&gt;
+ *   <li>State file: {@link
+ *       BackupManagerService#getBaseStateDir()}/&lt;transport&gt;/&lt;package&gt;<br>
+ *       Represents the state of the backup data for a specific package in the current dataset.
+ *   <li>Stage directory: {@link BackupManagerService#getDataDir()}
+ *   <li>Stage file: {@link BackupManagerService#getDataDir()}/&lt;package&gt;.data<br>
+ *       Contains staged data that the agents wrote via {@link BackupDataOutput}, to be transmitted
+ *       to the transport.
+ * </ul>
+ *
+ * If there is no PackageManager (PM) pseudo-package state file in the state directory, the
+ * specified transport will be initialized with {@link IBackupTransport#initializeDevice()}.
+ *
+ * <p>The PM pseudo-package is the first package to be backed-up and sent to the transport in case
+ * of incremental choice. If non-incremental, PM will only be backed-up if specified in the queue,
+ * and if it's the case it will be re-positioned at the head of the queue.
+ *
+ * <p>Before starting, this task will register itself in {@link BackupManagerService} current
+ * operations.
+ *
+ * <p>In summary, this task will for each package:
+ *
+ * <ul>
+ *   <li>Bind to its {@link IBackupAgent}.
+ *   <li>Request transport quota and flags.
+ *   <li>Call {@link IBackupAgent#doBackup(ParcelFileDescriptor, ParcelFileDescriptor,
+ *       ParcelFileDescriptor, long, int, IBackupManager, int)} via {@link RemoteCall} passing the
+ *       old state file descriptor (read), the backup data file descriptor (write), the new state
+ *       file descriptor (write), the quota and the transport flags. This will call {@link
+ *       BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)} with
+ *       the old state file to be read, a {@link BackupDataOutput} object to write the backup data
+ *       and the new state file to write. By writing to {@link BackupDataOutput}, the agent will
+ *       write data to the stage file. The task will block waiting for either:
+ *       <ul>
+ *         <li>Agent response.
+ *         <li>Agent time-out (specified via {@link
+ *             BackupManagerService#getAgentTimeoutParameters()}.
+ *         <li>External cancellation or thread interrupt.
+ *       </ul>
+ *   <li>Unbind the agent.
+ *   <li>Assuming agent response, send the staged data that the agent wrote to disk to the transport
+ *       via {@link IBackupTransport#performBackup(PackageInfo, ParcelFileDescriptor, int)}.
+ *   <li>Call {@link IBackupTransport#finishBackup()} if previous call was successful.
+ *   <li>Save the new state in the state file. During the agent call it was being written to
+ *       &lt;state file&gt;.new, here we rename it and replace the old one.
+ *   <li>Delete the stage file.
+ * </ul>
+ *
+ * In the end, this task will:
+ *
+ * <ul>
+ *   <li>Mark data-changed for the remaining packages in the queue (skipped packages).
+ *   <li>Delete the {@link DataChangedJournal} provided. Note that this should not be the current
+ *       journal.
+ *   <li>Set {@link BackupManagerService} current token as {@link
+ *       IBackupTransport#getCurrentRestoreSet()}, if applicable.
+ *   <li>Add the transport to the list of transports pending initialization ({@link
+ *       BackupManagerService#getPendingInits()}) and kick-off initialization if the transport ever
+ *       returned {@link BackupTransport#TRANSPORT_NOT_INITIALIZED}.
+ *   <li>Unregister the task in current operations.
+ *   <li>Release the wakelock.
+ *   <li>Kick-off {@link PerformFullTransportBackupTask} if a list of full-backup packages was
+ *       provided.
+ * </ul>
+ *
+ * The caller can specify whether this should be an incremental or non-incremental backup. In the
+ * case of non-incremental the agents will be passed an empty old state file, which signals that a
+ * complete backup should be performed.
+ *
+ * <p>This task is designed to run on a dedicated thread, with the exception of the {@link
+ * #handleCancel(boolean)} method, which can be called from any thread.
+ */
+// TODO: Stop poking into BMS state and doing things for it (e.g. synchronizing on public locks)
+// TODO: Consider having the caller responsible for some clean-up (like resetting state)
+// TODO: Distinguish between cancel and time-out where possible for logging/monitoring/observing
+public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
+    private static final String TAG = "KeyValueBackupTask";
+    private static final boolean DEBUG = BackupManagerService.DEBUG || true;
+    private static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG || false;
+    private static final int THREAD_PRIORITY = Process.THREAD_PRIORITY_BACKGROUND;
+    private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
+    private static final String BLANK_STATE_FILE_NAME = "blank_state";
+    @VisibleForTesting
+    public static final String STAGING_FILE_SUFFIX = ".data";
+    @VisibleForTesting
+    public static final String NEW_STATE_FILE_SUFFIX = ".new";
+
+    /**
+     * Creates a new {@link KeyValueBackupTask} for key-value backup operation, spins up a new
+     * dedicated thread and kicks off the operation in it.
+     *
+     * @param backupManagerService The {@link BackupManagerService} system service.
+     * @param transportClient The {@link TransportClient} that contains the transport used for the
+     *     operation.
+     * @param transportDirName The value of {@link IBackupTransport#transportDirName()} for the
+     *     transport whose {@link TransportClient} was provided above.
+     * @param queue The list of packages that will be backed-up, in the form of {@link
+     *     BackupRequest}.
+     * @param dataChangedJournal The old data-changed journal file that will be deleted when the
+     *     operation finishes (successfully or not) or {@code null}.
+     * @param observer A {@link IBackupObserver}.
+     * @param monitor A {@link IBackupManagerMonitor}.
+     * @param listener A {@link OnTaskFinishedListener} or {@code null}.
+     * @param pendingFullBackups The list of packages that will be passed for a new {@link
+     *     PerformFullTransportBackupTask} operation, which will be started when this finishes.
+     * @param userInitiated Whether this was user-initiated or not.
+     * @param nonIncremental If {@code true}, this will be a complete backup for each package,
+     *     otherwise it will be just an incremental one over the current dataset.
+     * @return The {@link KeyValueBackupTask} that was started.
+     */
+    public static KeyValueBackupTask start(
+            BackupManagerService backupManagerService,
+            TransportClient transportClient,
+            String transportDirName,
+            List<BackupRequest> queue,
+            @Nullable DataChangedJournal dataChangedJournal,
+            IBackupObserver observer,
+            IBackupManagerMonitor monitor,
+            @Nullable OnTaskFinishedListener listener,
+            List<String> pendingFullBackups,
+            boolean userInitiated,
+            boolean nonIncremental) {
+        KeyValueBackupTask task =
+                new KeyValueBackupTask(
+                        backupManagerService,
+                        transportClient,
+                        transportDirName,
+                        queue,
+                        dataChangedJournal,
+                        observer,
+                        monitor,
+                        listener,
+                        pendingFullBackups,
+                        userInitiated,
+                        nonIncremental);
+        Thread thread = new Thread(task, "key-value-backup-" + THREAD_COUNT.incrementAndGet());
+        if (DEBUG) {
+            Slog.d(TAG, "Spinning thread " + thread.getName());
+        }
+        thread.start();
+        return task;
+    }
+
+    private final BackupManagerService mBackupManagerService;
+    private final TransportClient mTransportClient;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+    private final IBackupObserver mObserver;
+    private final OnTaskFinishedListener mListener;
+    private final boolean mUserInitiated;
+    private final boolean mNonIncremental;
+    private final int mCurrentOpToken;
+    private final File mStateDir;
+    private final List<BackupRequest> mOriginalQueue;
+    private final List<BackupRequest> mQueue;
+    private final List<String> mPendingFullBackups;
+    @Nullable private final DataChangedJournal mJournal;
+    private IBackupManagerMonitor mMonitor;
+    @Nullable private PerformFullTransportBackupTask mFullBackupTask;
+
+    private IBackupAgent mAgentBinder;
+    private PackageInfo mCurrentPackage;
+    private File mSavedStateFile;
+    private File mBackupDataFile;
+    private File mNewStateFile;
+    private ParcelFileDescriptor mSavedState;
+    private ParcelFileDescriptor mBackupData;
+    private ParcelFileDescriptor mNewState;
+    private int mStatus;
+
+    /**
+     * This {@link ConditionVariable} is used to signal that the cancel operation has been
+     * received by the task and that no more transport calls will be made. Anyone can call {@link
+     * ConditionVariable#block()} to wait for these conditions to hold true, but there should only
+     * be one place where {@link ConditionVariable#open()} is called. Also there should be no calls
+     * to {@link ConditionVariable#close()}, which means there is only one cancel per backup -
+     * subsequent calls to block will return immediately.
+     */
+    private final ConditionVariable mCancelAcknowledged = new ConditionVariable(false);
+
+    /**
+     * Set it to {@code true} and block on {@code mCancelAcknowledged} to wait for the cancellation.
+     * DO NOT set it to {@code false}.
+     */
+    private volatile boolean mCancelled = false;
+
+    /**
+     * If non-{@code null} there is a pending agent call being made. This call can be cancelled (and
+     * control returned to this task) with {@link RemoteCall#cancel()}.
+     */
+    @Nullable private volatile RemoteCall mPendingCall;
+
+    @VisibleForTesting
+    public KeyValueBackupTask(
+            BackupManagerService backupManagerService,
+            TransportClient transportClient,
+            String transportDirName,
+            List<BackupRequest> queue,
+            @Nullable DataChangedJournal journal,
+            IBackupObserver observer,
+            IBackupManagerMonitor monitor,
+            @Nullable OnTaskFinishedListener listener,
+            List<String> pendingFullBackups,
+            boolean userInitiated,
+            boolean nonIncremental) {
+        mBackupManagerService = backupManagerService;
+        mTransportClient = transportClient;
+        mOriginalQueue = queue;
+        // We need to retain the original queue contents in case of transport failure
+        mQueue = new ArrayList<>(mOriginalQueue);
+        mJournal = journal;
+        mObserver = observer;
+        mMonitor = monitor;
+        mListener = (listener != null) ? listener : OnTaskFinishedListener.NOP;
+        mPendingFullBackups = pendingFullBackups;
+        mUserInitiated = userInitiated;
+        mNonIncremental = nonIncremental;
+        mAgentTimeoutParameters =
+                Preconditions.checkNotNull(
+                        backupManagerService.getAgentTimeoutParameters(),
+                        "Timeout parameters cannot be null");
+        mStateDir = new File(backupManagerService.getBaseStateDir(), transportDirName);
+        mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
+    }
+
+    private void registerTask() {
+        mBackupManagerService.putOperation(
+                mCurrentOpToken, new Operation(OP_PENDING, this, OP_TYPE_BACKUP));
+    }
+
+    private void unregisterTask() {
+        mBackupManagerService.removeOperation(mCurrentOpToken);
+    }
+
+    @Override
+    public void run() {
+        Process.setThreadPriority(THREAD_PRIORITY);
+
+        BackupState state = beginBackup();
+        while (state == BackupState.RUNNING_QUEUE || state == BackupState.BACKUP_PM) {
+            if (mCancelled) {
+                state = BackupState.CANCELLED;
+            }
+            switch (state) {
+                case BACKUP_PM:
+                    state = backupPm();
+                    break;
+                case RUNNING_QUEUE:
+                    Pair<BackupState, RemoteResult> stateAndResult = invokeNextAgent();
+                    state = stateAndResult.first;
+                    if (state == null) {
+                        state = processAgentInvocation(stateAndResult.second);
+                    }
+                    break;
+            }
+        }
+        if (state == BackupState.CANCELLED) {
+            finalizeCancelledBackup();
+        } else {
+            finalizeBackup();
+        }
+    }
+
+    private BackupState processAgentInvocation(RemoteResult result) {
+        if (result == RemoteResult.FAILED_THREAD_INTERRUPTED) {
+            // Not an explicit cancel, we need to flag it
+            mCancelled = true;
+            handleAgentCancelled();
+            return BackupState.CANCELLED;
+        }
+        if (result == RemoteResult.FAILED_CANCELLED) {
+            handleAgentCancelled();
+            return BackupState.CANCELLED;
+        }
+        if (result == RemoteResult.FAILED_TIMED_OUT) {
+            handleAgentTimeout();
+            return BackupState.RUNNING_QUEUE;
+        }
+        Preconditions.checkState(result.succeeded());
+        return handleAgentResult(result.get());
+    }
+
+    @Override
+    public void execute() {}
+
+    @Override
+    public void operationComplete(long unusedResult) {}
+
+    private BackupState beginBackup() {
+        if (DEBUG_BACKUP_TRACE) {
+            mBackupManagerService.clearBackupTrace();
+            StringBuilder b = new StringBuilder(256);
+            b.append("beginBackup: [");
+            for (BackupRequest req : mOriginalQueue) {
+                b.append(' ');
+                b.append(req.packageName);
+            }
+            b.append(" ]");
+            mBackupManagerService.addBackupTrace(b.toString());
+        }
+        synchronized (mBackupManagerService.getCurrentOpLock()) {
+            if (mBackupManagerService.isBackupOperationInProgress()) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Skipping backup since one is already in progress");
+                }
+                mBackupManagerService.addBackupTrace("Skipped. Backup already in progress.");
+                return BackupState.FINAL;
+            }
+        }
+
+        String[] fullBackups = mPendingFullBackups.toArray(new String[mPendingFullBackups.size()]);
+        mFullBackupTask =
+                new PerformFullTransportBackupTask(
+                        mBackupManagerService,
+                        mTransportClient,
+                        /* fullBackupRestoreObserver */ null,
+                        fullBackups,
+                        /* updateSchedule */ false,
+                        /* runningJob */ null,
+                        new CountDownLatch(1),
+                        mObserver,
+                        mMonitor,
+                        mListener,
+                        mUserInitiated);
+        registerTask();
+        mBackupManagerService.addBackupTrace("STATE => INITIAL");
+
+        mAgentBinder = null;
+        mStatus = BackupTransport.TRANSPORT_OK;
+
+        // Sanity check: if the queue is empty we have no work to do.
+        if (mOriginalQueue.isEmpty() && mPendingFullBackups.isEmpty()) {
+            Slog.w(TAG, "Backup begun with an empty queue, nothing to do.");
+            mBackupManagerService.addBackupTrace("queue empty at begin");
+            return BackupState.FINAL;
+        }
+
+        // When the transport is forcing non-incremental key/value payloads, we send the
+        // metadata only if it explicitly asks for it.
+        boolean skipPm = mNonIncremental;
+
+        // The app metadata pseudopackage might also be represented in the
+        // backup queue if apps have been added/removed since the last time
+        // we performed a backup.  Drop it from the working queue now that
+        // we're committed to evaluating it for backup regardless.
+        for (int i = 0; i < mQueue.size(); i++) {
+            if (PACKAGE_MANAGER_SENTINEL.equals(mQueue.get(i).packageName)) {
+                if (MORE_DEBUG) {
+                    Slog.i(TAG, "PM metadata in queue, removing");
+                }
+                mQueue.remove(i);
+                skipPm = false;
+                break;
+            }
+        }
+
+        if (DEBUG) {
+            Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
+        }
+        File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
+        try {
+            IBackupTransport transport = mTransportClient.connectOrThrow("KVBT.beginBackup()");
+            String transportName = transport.name();
+            EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
+
+            // If we haven't stored package manager metadata yet, we must init the transport.
+            if (pmState.length() <= 0) {
+                Slog.i(TAG, "Initializing transport and resetting backup state");
+                mBackupManagerService.addBackupTrace("initializing transport " + transportName);
+                mBackupManagerService.resetBackupState(mStateDir);  // Just to make sure.
+                mStatus = transport.initializeDevice();
+
+                mBackupManagerService.addBackupTrace("transport.initializeDevice() == " + mStatus);
+                if (mStatus == BackupTransport.TRANSPORT_OK) {
+                    EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
+                } else {
+                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
+                    Slog.e(TAG, "Transport error in initializeDevice()");
+                }
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Error during initialization", e);
+            mBackupManagerService.addBackupTrace("Exception in backup thread during init: " + e);
+            mStatus = BackupTransport.TRANSPORT_ERROR;
+        }
+        mBackupManagerService.addBackupTrace("exiting prelim: " + mStatus);
+
+        if (mStatus != BackupTransport.TRANSPORT_OK) {
+            // if things went wrong at this point, we need to
+            // restage everything and try again later.
+            mBackupManagerService.resetBackupState(mStateDir);  // Just to make sure.
+            return BackupState.FINAL;
+        }
+
+        if (skipPm) {
+            Slog.d(TAG, "Skipping backup of PM metadata");
+            return BackupState.RUNNING_QUEUE;
+        }
+
+        return BackupState.BACKUP_PM;
+    }
+
+    private BackupState backupPm() {
+        RemoteResult agentResult = null;
+        BackupState nextState;
+        try {
+            // The package manager doesn't have a proper <application> etc, but since it's running
+            // here in the system process we can just set up its agent directly and use a synthetic
+            // BackupRequest.
+            BackupAgent pmAgent = mBackupManagerService.makeMetadataAgent();
+            Pair<Integer, RemoteResult> statusAndResult =
+                    invokeAgentForBackup(
+                            PACKAGE_MANAGER_SENTINEL,
+                            IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+            mStatus = statusAndResult.first;
+            agentResult = statusAndResult.second;
+
+            mBackupManagerService.addBackupTrace("PM agent invoke: " + mStatus);
+        } catch (Exception e) {
+            Slog.e(TAG, "Error during PM metadata backup", e);
+            mBackupManagerService.addBackupTrace("Exception in backup thread during pm: " + e);
+            mStatus = BackupTransport.TRANSPORT_ERROR;
+        }
+        mBackupManagerService.addBackupTrace("exiting backupPm: " + mStatus);
+
+        if (mStatus == BackupTransport.TRANSPORT_OK) {
+            Preconditions.checkNotNull(agentResult);
+            nextState = processAgentInvocation(agentResult);
+        } else {
+            // if things went wrong at this point, we need to
+            // restage everything and try again later.
+            mBackupManagerService.resetBackupState(mStateDir);  // Just to make sure.
+            nextState = BackupState.FINAL;
+        }
+
+        return nextState;
+    }
+
+    /**
+     * Returns either:
+     *
+     * <ul>
+     *   <li>(next state, {@code null}): In case we failed to call the agent.
+     *   <li>({@code null}, agent result): In case we successfully called the agent.
+     * </ul>
+     */
+    private Pair<BackupState, RemoteResult> invokeNextAgent() {
+        mStatus = BackupTransport.TRANSPORT_OK;
+        mBackupManagerService.addBackupTrace("invoke q=" + mQueue.size());
+
+        // Sanity check that we have work to do.  If not, skip to the end where
+        // we reestablish the wakelock invariants etc.
+        if (mQueue.isEmpty()) {
+            if (MORE_DEBUG) {
+                Slog.i(TAG, "Queue now empty");
+            }
+            return Pair.create(BackupState.FINAL, null);
+        }
+
+        // pop the entry we're going to process on this step
+        BackupRequest request = mQueue.remove(0);
+
+        Slog.d(TAG, "Starting key-value backup of " + request);
+        mBackupManagerService.addBackupTrace("launch agent for " + request.packageName);
+
+        // Verify that the requested app exists; it might be something that
+        // requested a backup but was then uninstalled.  The request was
+        // journalled and rather than tamper with the journal it's safer
+        // to sanity-check here.  This also gives us the classname of the
+        // package's backup agent.
+        RemoteResult agentResult = null;
+        try {
+            PackageManager pm = mBackupManagerService.getPackageManager();
+            mCurrentPackage = pm.getPackageInfo(request.packageName,
+                    PackageManager.GET_SIGNING_CERTIFICATES);
+            if (!AppBackupUtils.appIsEligibleForBackup(mCurrentPackage.applicationInfo, pm)) {
+                // The manifest has changed but we had a stale backup request pending. This won't
+                // happen again because the app won't be requesting further backups.
+                Slog.i(TAG, "Package " + request.packageName
+                        + " no longer supports backup, skipping");
+                mBackupManagerService.addBackupTrace("skipping - not eligible, completion is noop");
+                // Shouldn't happen in case of requested backup, as pre-check was done in
+                // #requestBackup(), except to app update done concurrently
+                BackupObserverUtils.sendBackupOnPackageResult(mObserver,
+                        mCurrentPackage.packageName,
+                        BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+                return Pair.create(BackupState.RUNNING_QUEUE, null);
+            }
+
+            if (AppBackupUtils.appGetsFullBackup(mCurrentPackage)) {
+                // It's possible that this app *formerly* was enqueued for key-value backup, but has
+                // since been updated and now only supports the full-backup path. Don't proceed with
+                // a key-value backup for it in this case.
+                Slog.i(TAG, "Package " + request.packageName
+                        + " performs full-backup rather than key-value, skipping");
+                mBackupManagerService.addBackupTrace(
+                        "skipping - fullBackupOnly, completion is noop");
+                // Shouldn't happen in case of requested backup, as pre-check was done in
+                // #requestBackup()
+                BackupObserverUtils.sendBackupOnPackageResult(mObserver,
+                        mCurrentPackage.packageName,
+                        BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+                return Pair.create(BackupState.RUNNING_QUEUE, null);
+            }
+
+            if (AppBackupUtils.appIsStopped(mCurrentPackage.applicationInfo)) {
+                // The app has been force-stopped or cleared or just installed,
+                // and not yet launched out of that state, so just as it won't
+                // receive broadcasts, we won't run it for backup.
+                mBackupManagerService.addBackupTrace("skipping - stopped");
+                BackupObserverUtils.sendBackupOnPackageResult(mObserver,
+                        mCurrentPackage.packageName,
+                        BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+                return Pair.create(BackupState.RUNNING_QUEUE, null);
+            }
+
+            try {
+                mBackupManagerService.setWorkSource(
+                        new WorkSource(mCurrentPackage.applicationInfo.uid));
+                IBackupAgent agent =
+                        mBackupManagerService.bindToAgentSynchronous(
+                                mCurrentPackage.applicationInfo,
+                                ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
+                mBackupManagerService.addBackupTrace("agent bound; a? = " + (agent != null));
+                if (agent != null) {
+                    mAgentBinder = agent;
+                    Pair<Integer, RemoteResult> statusAndResult =
+                            invokeAgentForBackup(request.packageName, agent);
+                    mStatus = statusAndResult.first;
+                    agentResult = statusAndResult.second;
+                } else {
+                    // Timeout waiting for the agent
+                    mStatus = BackupTransport.AGENT_ERROR;
+                }
+            } catch (SecurityException se) {
+                // Try for the next one.
+                Slog.d(TAG, "Error in bind/backup", se);
+                mStatus = BackupTransport.AGENT_ERROR;
+                mBackupManagerService.addBackupTrace("agent SE");
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.d(TAG, "Package does not exist, skipping");
+            mBackupManagerService.addBackupTrace("no such package");
+            mStatus = BackupTransport.AGENT_UNKNOWN;
+        } finally {
+            mBackupManagerService.setWorkSource(null);
+        }
+
+        if (mStatus != BackupTransport.TRANSPORT_OK) {
+            BackupState nextState = BackupState.RUNNING_QUEUE;
+            mAgentBinder = null;
+
+            // An agent-level failure means we re-enqueue this one agent for
+            // a later retry, but otherwise proceed normally.
+            if (mStatus == BackupTransport.AGENT_ERROR) {
+                if (MORE_DEBUG) {
+                    Slog.i(TAG, "Agent failure for " + request.packageName + ", re-staging");
+                }
+                mBackupManagerService.dataChangedImpl(request.packageName);
+                mStatus = BackupTransport.TRANSPORT_OK;
+                BackupObserverUtils
+                        .sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
+                                BackupManager.ERROR_AGENT_FAILURE);
+            } else if (mStatus == BackupTransport.AGENT_UNKNOWN) {
+                // Failed lookup of the app, so we couldn't bring up an agent, but
+                // we're otherwise fine.  Just drop it and go on to the next as usual.
+                mStatus = BackupTransport.TRANSPORT_OK;
+                BackupObserverUtils
+                        .sendBackupOnPackageResult(mObserver, request.packageName,
+                                BackupManager.ERROR_PACKAGE_NOT_FOUND);
+            } else {
+                // Transport-level failure means we re-enqueue everything
+                revertAndEndBackup();
+                nextState = BackupState.FINAL;
+            }
+
+            return Pair.create(nextState, null);
+        }
+
+        // Success: caller will figure out the state based on call result
+        mBackupManagerService.addBackupTrace("call made; result = " + agentResult);
+        return Pair.create(null, agentResult);
+    }
+
+    private void finalizeBackup() {
+        mBackupManagerService.addBackupTrace("finishing");
+
+        // Mark packages that we didn't backup (because backup was cancelled, etc.) as needing
+        // backup.
+        for (BackupRequest req : mQueue) {
+            mBackupManagerService.dataChangedImpl(req.packageName);
+        }
+
+        // Either backup was successful, in which case we of course do not need
+        // this pass's journal any more; or it failed, in which case we just
+        // re-enqueued all of these packages in the current active journal.
+        // Either way, we no longer need this pass's journal.
+        if (mJournal != null && !mJournal.delete()) {
+            Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
+        }
+
+        // If everything actually went through and this is the first time we've
+        // done a backup, we can now record what the current backup dataset token
+        // is.
+        String callerLogString = "KVBT.finalizeBackup()";
+        if ((mBackupManagerService.getCurrentToken() == 0) && (mStatus
+                == BackupTransport.TRANSPORT_OK)) {
+            mBackupManagerService.addBackupTrace("success; recording token");
+            try {
+                IBackupTransport transport = mTransportClient.connectOrThrow(callerLogString);
+                mBackupManagerService.setCurrentToken(transport.getCurrentRestoreSet());
+                mBackupManagerService.writeRestoreTokens();
+            } catch (Exception e) {
+                // nothing for it at this point, unfortunately, but this will be
+                // recorded the next time we fully succeed.
+                Slog.e(TAG, "Transport threw reporting restore set: " + e);
+                mBackupManagerService.addBackupTrace("transport threw returning token");
+            }
+        }
+
+        // Set up the next backup pass - at this point we can set mBackupRunning
+        // to false to allow another pass to fire
+        synchronized (mBackupManagerService.getQueueLock()) {
+            mBackupManagerService.setBackupRunning(false);
+            if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
+                if (MORE_DEBUG) {
+                    Slog.d(TAG, "Transport requires initialization, rerunning");
+                }
+                mBackupManagerService.addBackupTrace("init required; rerunning");
+                try {
+                    String name = mBackupManagerService.getTransportManager()
+                            .getTransportName(mTransportClient.getTransportComponent());
+                    mBackupManagerService.getPendingInits().add(name);
+                } catch (Exception e) {
+                    Slog.w(TAG, "Failed to query transport name for init: " + e);
+                    // swallow it and proceed; we don't rely on this
+                }
+                clearMetadata();
+                mBackupManagerService.backupNow();
+            }
+        }
+
+        mBackupManagerService.clearBackupTrace();
+
+        unregisterTask();
+
+        if (!mCancelled && mStatus == BackupTransport.TRANSPORT_OK &&
+                mPendingFullBackups != null && !mPendingFullBackups.isEmpty()) {
+            Slog.d(TAG, "Starting full backups for: " + mPendingFullBackups);
+            // Acquiring wakelock for PerformFullTransportBackupTask before its start.
+            mBackupManagerService.getWakelock().acquire();
+            // The full-backup task is now responsible for calling onFinish() on mListener, which
+            // was the listener we passed it.
+            (new Thread(mFullBackupTask, "full-transport-requested")).start();
+        } else if (mCancelled) {
+            mListener.onFinished(callerLogString);
+            if (mFullBackupTask != null) {
+                mFullBackupTask.unregisterTask();
+            }
+            BackupObserverUtils.sendBackupFinished(mObserver, BackupManager.ERROR_BACKUP_CANCELLED);
+        } else {
+            mListener.onFinished(callerLogString);
+            if (mFullBackupTask != null) {
+                mFullBackupTask.unregisterTask();
+            }
+            switch (mStatus) {
+                case BackupTransport.TRANSPORT_OK:
+                case BackupTransport.TRANSPORT_QUOTA_EXCEEDED:
+                case BackupTransport.TRANSPORT_PACKAGE_REJECTED:
+                    BackupObserverUtils.sendBackupFinished(mObserver, BackupManager.SUCCESS);
+                    break;
+                case BackupTransport.TRANSPORT_NOT_INITIALIZED:
+                    BackupObserverUtils.sendBackupFinished(mObserver,
+                            BackupManager.ERROR_TRANSPORT_ABORTED);
+                    break;
+                case BackupTransport.TRANSPORT_ERROR:
+                default:
+                    BackupObserverUtils.sendBackupFinished(mObserver,
+                            BackupManager.ERROR_TRANSPORT_ABORTED);
+                    break;
+            }
+        }
+        Slog.i(TAG, "K/V backup pass finished");
+        mBackupManagerService.getWakelock().release();
+    }
+
+    // Remove the PM metadata state. This will generate an init on the next pass.
+    private void clearMetadata() {
+        final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
+        if (pmState.exists()) pmState.delete();
+    }
+
+    /**
+     * Returns a {@link Pair}. The first of the pair contains the status. In case the status is
+     * {@link BackupTransport#TRANSPORT_OK}, the second of the pair contains the agent result,
+     * otherwise {@code null}.
+     */
+    private Pair<Integer, RemoteResult> invokeAgentForBackup(
+            String packageName, IBackupAgent agent) {
+        if (DEBUG) {
+            Slog.d(TAG, "Invoking agent on " + packageName);
+        }
+        mBackupManagerService.addBackupTrace("invoking " + packageName);
+
+        File blankStateFile = new File(mStateDir, BLANK_STATE_FILE_NAME);
+        mSavedStateFile = new File(mStateDir, packageName);
+        mBackupDataFile =
+                new File(mBackupManagerService.getDataDir(), packageName + STAGING_FILE_SUFFIX);
+        mNewStateFile = new File(mStateDir, packageName + NEW_STATE_FILE_SUFFIX);
+        if (MORE_DEBUG) {
+            Slog.d(TAG, "Data file: " + mBackupDataFile);
+        }
+
+
+        mSavedState = null;
+        mBackupData = null;
+        mNewState = null;
+
+        boolean callingAgent = false;
+        final RemoteResult agentResult;
+        try {
+            // Look up the package info & signatures.  This is first so that if it
+            // throws an exception, there's no file setup yet that would need to
+            // be unraveled.
+            if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
+                // The metadata 'package' is synthetic; construct one and make
+                // sure our global state is pointed at it
+                mCurrentPackage = new PackageInfo();
+                mCurrentPackage.packageName = packageName;
+            }
+
+            mSavedState = ParcelFileDescriptor.open(
+                    (mNonIncremental) ? blankStateFile : mSavedStateFile,
+                    ParcelFileDescriptor.MODE_READ_ONLY |
+                            ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
+
+            mBackupData = ParcelFileDescriptor.open(mBackupDataFile,
+                    ParcelFileDescriptor.MODE_READ_WRITE |
+                            ParcelFileDescriptor.MODE_CREATE |
+                            ParcelFileDescriptor.MODE_TRUNCATE);
+
+            if (!SELinux.restorecon(mBackupDataFile)) {
+                Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataFile);
+            }
+
+            mNewState = ParcelFileDescriptor.open(mNewStateFile,
+                    ParcelFileDescriptor.MODE_READ_WRITE |
+                            ParcelFileDescriptor.MODE_CREATE |
+                            ParcelFileDescriptor.MODE_TRUNCATE);
+
+            IBackupTransport transport =
+                    mTransportClient.connectOrThrow("KVBT.invokeAgentForBackup()");
+
+            final long quota = transport.getBackupQuota(packageName, false /* isFullBackup */);
+            callingAgent = true;
+
+            // Initiate the target's backup pass
+            long kvBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
+            mBackupManagerService.addBackupTrace("calling agent doBackup()");
+
+            agentResult =
+                    remoteCall(
+                            callback ->
+                                    agent.doBackup(
+                                            mSavedState,
+                                            mBackupData,
+                                            mNewState,
+                                            quota,
+                                            callback,
+                                            transport.getTransportFlags()),
+                            kvBackupAgentTimeoutMillis);
+        } catch (Exception e) {
+            Slog.e(TAG, "Error invoking agent on " + packageName + ": " + e);
+            mBackupManagerService.addBackupTrace("exception: " + e);
+            EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, e.toString());
+            errorCleanup();
+            int status =
+                    callingAgent ? BackupTransport.AGENT_ERROR : BackupTransport.TRANSPORT_ERROR;
+            return Pair.create(status, null);
+        } finally {
+            if (mNonIncremental) {
+                blankStateFile.delete();
+            }
+        }
+
+        return Pair.create(BackupTransport.TRANSPORT_OK, agentResult);
+    }
+
+    private void failAgent(IBackupAgent agent, String message) {
+        try {
+            agent.fail(message);
+        } catch (Exception e) {
+            Slog.w(TAG, "Error conveying failure to " + mCurrentPackage.packageName);
+        }
+    }
+
+    // SHA-1 a byte array and return the result in hex
+    private String SHA1Checksum(byte[] input) {
+        final byte[] checksum;
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-1");
+            checksum = md.digest(input);
+        } catch (NoSuchAlgorithmException e) {
+            Slog.e(TAG, "Unable to use SHA-1!");
+            return "00";
+        }
+
+        StringBuffer sb = new StringBuffer(checksum.length * 2);
+        for (byte item : checksum) {
+            sb.append(Integer.toHexString(item));
+        }
+        return sb.toString();
+    }
+
+    private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
+            throws IOException {
+        // TODO: http://b/22388012
+        byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, UserHandle.USER_SYSTEM);
+        // has the widget state changed since last time?
+        final File widgetFile = new File(mStateDir, pkgName + "_widget");
+        final boolean priorStateExists = widgetFile.exists();
+
+        if (MORE_DEBUG) {
+            if (priorStateExists || widgetState != null) {
+                Slog.i(TAG, "Checking widget update: state=" + (widgetState != null)
+                        + " prior=" + priorStateExists);
+            }
+        }
+
+        if (!priorStateExists && widgetState == null) {
+            // no prior state, no new state => nothing to do
+            return;
+        }
+
+        // if the new state is not null, we might need to compare checksums to
+        // determine whether to update the widget blob in the archive.  If the
+        // widget state *is* null, we know a priori at this point that we simply
+        // need to commit a deletion for it.
+        String newChecksum = null;
+        if (widgetState != null) {
+            newChecksum = SHA1Checksum(widgetState);
+            if (priorStateExists) {
+                final String priorChecksum;
+                try (
+                        FileInputStream fin = new FileInputStream(widgetFile);
+                        DataInputStream in = new DataInputStream(fin)
+                ) {
+                    priorChecksum = in.readUTF();
+                }
+                if (Objects.equals(newChecksum, priorChecksum)) {
+                    // Same checksum => no state change => don't rewrite the widget data
+                    return;
+                }
+            }
+        } // else widget state *became* empty, so we need to commit a deletion
+
+        BackupDataOutput out = new BackupDataOutput(fd);
+        if (widgetState != null) {
+            try (
+                    FileOutputStream fout = new FileOutputStream(widgetFile);
+                    DataOutputStream stateOut = new DataOutputStream(fout)
+            ) {
+                stateOut.writeUTF(newChecksum);
+            }
+
+            out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
+            out.writeEntityData(widgetState, widgetState.length);
+        } else {
+            // Widget state for this app has been removed; commit a deletion
+            out.writeEntityHeader(KEY_WIDGET_STATE, -1);
+            widgetFile.delete();
+        }
+    }
+
+    private BackupState handleAgentResult(long unusedResult) {
+        Preconditions.checkState(mBackupData != null);
+
+        final String pkgName = mCurrentPackage.packageName;
+        final long filepos = mBackupDataFile.length();
+        FileDescriptor fd = mBackupData.getFileDescriptor();
+        try {
+            // If it's a 3rd party app, see whether they wrote any protected keys
+            // and complain mightily if they are attempting shenanigans.
+            if (mCurrentPackage.applicationInfo != null &&
+                    (mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
+                            == 0) {
+                ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataFile,
+                        ParcelFileDescriptor.MODE_READ_ONLY);
+                BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor());
+                try {
+                    while (in.readNextHeader()) {
+                        final String key = in.getKey();
+                        if (key != null && key.charAt(0) >= 0xff00) {
+                            // Not okay: crash them and bail.
+                            failAgent(mAgentBinder, "Illegal backup key: " + key);
+                            mBackupManagerService
+                                    .addBackupTrace("illegal key " + key + " from " + pkgName);
+                            EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName,
+                                    "bad key");
+                            mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+                                    BackupManagerMonitor.LOG_EVENT_ID_ILLEGAL_KEY,
+                                    mCurrentPackage,
+                                    BackupManagerMonitor
+                                            .LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                    BackupManagerMonitorUtils.putMonitoringExtra(null,
+                                            BackupManagerMonitor.EXTRA_LOG_ILLEGAL_KEY,
+                                            key));
+                            BackupObserverUtils
+                                    .sendBackupOnPackageResult(mObserver, pkgName,
+                                            BackupManager.ERROR_AGENT_FAILURE);
+                            errorCleanup();
+                            if (MORE_DEBUG) {
+                                Slog.i(TAG, "Agent failure for " + pkgName
+                                        + " with illegal key " + key + ", dropped");
+                            }
+
+                            return BackupState.RUNNING_QUEUE;
+                        }
+                        in.skipEntityData();
+                    }
+                } finally {
+                    if (readFd != null) {
+                        readFd.close();
+                    }
+                }
+            }
+
+            // Piggyback the widget state payload, if any
+            writeWidgetPayloadIfAppropriate(fd, pkgName);
+        } catch (IOException e) {
+            // Hard disk error; recovery/failure policy TBD.  For now roll back,
+            // but we may want to consider this a transport-level failure (i.e.
+            // we're in such a bad state that we can't contemplate doing backup
+            // operations any more during this pass).
+            Slog.w(TAG, "Unable read backup data or to save widget state for " + pkgName);
+            try {
+                Os.ftruncate(fd, filepos);
+            } catch (ErrnoException ee) {
+                Slog.w(TAG, "Unable to roll back");
+            }
+        }
+
+        clearAgentState();
+        mBackupManagerService.addBackupTrace("operation complete");
+
+        ParcelFileDescriptor backupData = null;
+        mStatus = BackupTransport.TRANSPORT_OK;
+        long size = 0;
+        try {
+            IBackupTransport transport = mTransportClient.connectOrThrow("KVBT.handleAgentResult()");
+            size = mBackupDataFile.length();
+            if (size > 0) {
+                if (MORE_DEBUG) {
+                    Slog.v(TAG, "Sending non-empty data to transport for " + pkgName);
+                }
+                boolean isNonIncremental = mSavedStateFile.length() == 0;
+                if (mStatus == BackupTransport.TRANSPORT_OK) {
+                    backupData = ParcelFileDescriptor.open(mBackupDataFile,
+                            ParcelFileDescriptor.MODE_READ_ONLY);
+                    mBackupManagerService.addBackupTrace("sending data to transport");
+
+                    int userInitiatedFlag =
+                            mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
+                    int incrementalFlag =
+                            isNonIncremental
+                                    ? BackupTransport.FLAG_NON_INCREMENTAL
+                                    : BackupTransport.FLAG_INCREMENTAL;
+                    int flags = userInitiatedFlag | incrementalFlag;
+
+                    mStatus = transport.performBackup(mCurrentPackage, backupData, flags);
+                }
+
+                if (isNonIncremental
+                        && mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
+                    // TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED is only valid if the backup was
+                    // incremental, as if the backup is non-incremental there is no state to
+                    // clear. This avoids us ending up in a retry loop if the transport always
+                    // returns this code.
+                    Slog.e(TAG, "Transport requested non-incremental but already the case");
+                    mBackupManagerService.addBackupTrace(
+                            "Transport requested non-incremental but already the case, error");
+                    mStatus = BackupTransport.TRANSPORT_ERROR;
+                }
+
+                mBackupManagerService.addBackupTrace("data delivered: " + mStatus);
+                if (mStatus == BackupTransport.TRANSPORT_OK) {
+                    mBackupManagerService.addBackupTrace("finishing op on transport");
+                    mStatus = transport.finishBackup();
+                    mBackupManagerService.addBackupTrace("finished: " + mStatus);
+                } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+                    mBackupManagerService.addBackupTrace("transport rejected package");
+                }
+            } else {
+                if (MORE_DEBUG) {
+                    Slog.i(TAG, "No backup data written, not calling transport");
+                }
+                mBackupManagerService.addBackupTrace("no data to send");
+                mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+                        BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND,
+                        mCurrentPackage,
+                        BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                        null);
+            }
+
+            if (mStatus == BackupTransport.TRANSPORT_OK) {
+                // After successful transport, delete the now-stale data
+                // and juggle the files so that next time we supply the agent
+                // with the new state file it just created.
+                mBackupDataFile.delete();
+                mNewStateFile.renameTo(mSavedStateFile);
+                BackupObserverUtils.sendBackupOnPackageResult(
+                        mObserver, pkgName, BackupManager.SUCCESS);
+                EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
+                mBackupManagerService.logBackupComplete(pkgName);
+            } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+                // The transport has rejected backup of this specific package.  Roll it
+                // back but proceed with running the rest of the queue.
+                mBackupDataFile.delete();
+                mNewStateFile.delete();
+                BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
+                        BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
+                EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
+            } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
+                BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
+                        BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
+                EventLog.writeEvent(EventLogTags.BACKUP_QUOTA_EXCEEDED, pkgName);
+
+            } else if (mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
+                Slog.i(TAG, "Transport lost data, retrying package");
+                mBackupManagerService.addBackupTrace(
+                        "Transport lost data, retrying package:" + pkgName);
+                BackupManagerMonitorUtils.monitorEvent(
+                        mMonitor,
+                        BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED,
+                        mCurrentPackage,
+                        BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+                        /*extras=*/ null);
+
+                mBackupDataFile.delete();
+                mSavedStateFile.delete();
+                mNewStateFile.delete();
+
+                // Immediately retry the package by adding it back to the front of the queue.
+                // We cannot add @pm@ to the queue because we back it up separately at the start
+                // of the backup pass in state BACKUP_PM. Instead we retry this state (see
+                // below).
+                if (!PACKAGE_MANAGER_SENTINEL.equals(pkgName)) {
+                    mQueue.add(0, new BackupRequest(pkgName));
+                }
+
+            } else {
+                // Actual transport-level failure to communicate the data to the backend
+                BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
+                        BackupManager.ERROR_TRANSPORT_ABORTED);
+                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
+            }
+        } catch (Exception e) {
+            BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
+                    BackupManager.ERROR_TRANSPORT_ABORTED);
+            Slog.e(TAG, "Transport error backing up " + pkgName, e);
+            EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
+            mStatus = BackupTransport.TRANSPORT_ERROR;
+        } finally {
+            try {
+                if (backupData != null) {
+                    backupData.close();
+                }
+            } catch (IOException e) {
+                Slog.w(TAG, "Error closing backup data file-descriptor");
+            }
+        }
+
+        final BackupState nextState;
+        if (mStatus == BackupTransport.TRANSPORT_OK
+                || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+            // Success or single-package rejection.  Proceed with the next app if any,
+            // otherwise we're done.
+            nextState = BackupState.RUNNING_QUEUE;
+
+        } else if (mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
+            // We want to immediately retry the current package.
+            if (PACKAGE_MANAGER_SENTINEL.equals(pkgName)) {
+                nextState = BackupState.BACKUP_PM;
+            } else {
+                // This is an ordinary package so we will have added it back into the queue
+                // above. Thus, we proceed processing the queue.
+                nextState = BackupState.RUNNING_QUEUE;
+            }
+
+        } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "Package " + mCurrentPackage.packageName +
+                        " hit quota limit on key-value backup");
+            }
+            if (mAgentBinder != null) {
+                try {
+                    IBackupTransport transport =
+                            mTransportClient.connectOrThrow("KVBT.handleAgentResult()");
+                    long quota = transport.getBackupQuota(mCurrentPackage.packageName, false);
+                    mAgentBinder.doQuotaExceeded(size, quota);
+                } catch (Exception e) {
+                    Slog.e(TAG, "Unable to notify about quota exceeded: " + e.getMessage());
+                }
+            }
+            nextState = BackupState.RUNNING_QUEUE;
+        } else {
+            // Any other error here indicates a transport-level failure.  That means
+            // we need to halt everything and reschedule everything for next time.
+            revertAndEndBackup();
+            nextState = BackupState.FINAL;
+        }
+
+        return nextState;
+    }
+
+    /**
+     * Cancels this task. After this method returns there will be no more calls to the transport.
+     *
+     * <p>If this method is executed while an agent is performing a backup, we will stop waiting for
+     * it, disregard its backup data and finalize the task. However, if this method is executed in
+     * between agent calls, the backup data of the last called agent will be sent to
+     * the transport and we will not consider the next agent (nor the rest of the queue), proceeding
+     * to finalize the backup.
+     *
+     * @param cancelAll MUST be {@code true}. Will be removed.
+     */
+    @Override
+    public void handleCancel(boolean cancelAll) {
+        Preconditions.checkArgument(cancelAll, "Can't partially cancel a key-value backup task");
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "Cancel received");
+        }
+        mCancelled = true;
+        RemoteCall pendingCall = mPendingCall;
+        if (pendingCall != null) {
+            pendingCall.cancel();
+        }
+        mCancelAcknowledged.block();
+    }
+
+    private void handleAgentTimeout() {
+        String packageName = getPackageNameForLog();
+        Slog.i(TAG, "Agent " + packageName + " timed out");
+        EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
+        mBackupManagerService.addBackupTrace("timeout of " + packageName);
+        mMonitor =
+                BackupManagerMonitorUtils.monitorEvent(
+                        mMonitor,
+                        BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL,
+                        mCurrentPackage,
+                        BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+                        BackupManagerMonitorUtils.putMonitoringExtra(
+                                null, BackupManagerMonitor.EXTRA_LOG_CANCEL_ALL, false));
+        errorCleanup();
+    }
+
+    private void handleAgentCancelled() {
+        String packageName = getPackageNameForLog();
+        Slog.i(TAG, "Cancel backing up " + packageName);
+        EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
+        mBackupManagerService.addBackupTrace("cancel of " + packageName);
+        errorCleanup();
+    }
+
+    private void finalizeCancelledBackup() {
+        mMonitor =
+                BackupManagerMonitorUtils.monitorEvent(
+                        mMonitor,
+                        BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL,
+                        mCurrentPackage,
+                        BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+                        BackupManagerMonitorUtils.putMonitoringExtra(
+                                null, BackupManagerMonitor.EXTRA_LOG_CANCEL_ALL, true));
+        finalizeBackup();
+        // finalizeBackup() may call the transport, so we only acknowledge the cancellation here.
+        mCancelAcknowledged.open();
+    }
+
+    private String getPackageNameForLog() {
+        return (mCurrentPackage != null) ? mCurrentPackage.packageName : "no_package_yet";
+    }
+
+    private void revertAndEndBackup() {
+        if (MORE_DEBUG) {
+            Slog.i(TAG, "Reverting backup queue, re-staging everything");
+        }
+        mBackupManagerService.addBackupTrace("transport error; reverting");
+
+        // We want to reset the backup schedule based on whatever the transport suggests
+        // by way of retry/backoff time.
+        long delay;
+        try {
+            IBackupTransport transport =
+                    mTransportClient.connectOrThrow("KVBT.revertAndEndBackup()");
+            delay = transport.requestBackupTime();
+        } catch (Exception e) {
+            Slog.w(TAG, "Unable to contact transport for recommended backoff: " + e);
+            delay = 0;  // use the scheduler's default
+        }
+        KeyValueBackupJob.schedule(mBackupManagerService.getContext(), delay,
+                mBackupManagerService.getConstants());
+
+        for (BackupRequest request : mOriginalQueue) {
+            mBackupManagerService.dataChangedImpl(request.packageName);
+        }
+    }
+
+    private void errorCleanup() {
+        mBackupDataFile.delete();
+        mNewStateFile.delete();
+        clearAgentState();
+    }
+
+    // Cleanup common to both success and failure cases
+    private void clearAgentState() {
+        try {
+            if (mSavedState != null) {
+                mSavedState.close();
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Error closing old state file-descriptor");
+        }
+        try {
+            if (mBackupData != null) {
+                mBackupData.close();
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Error closing backup data file-descriptor");
+        }
+        try {
+            if (mNewState != null) {
+                mNewState.close();
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Error closing new state file-descriptor");
+        }
+        synchronized (mBackupManagerService.getCurrentOpLock()) {
+            // Current-operation callback handling requires the validity of these various
+            // bits of internal state as an invariant of the operation still being live.
+            // This means we make sure to clear all of the state in unison inside the lock.
+            mSavedState = mBackupData = mNewState = null;
+        }
+
+        // If this was a pseudo-package there's no associated Activity Manager state
+        if (mCurrentPackage.applicationInfo != null) {
+            mBackupManagerService.addBackupTrace("unbinding " + mCurrentPackage.packageName);
+            mBackupManagerService.unbindAgent(mCurrentPackage.applicationInfo);
+        }
+    }
+
+    private RemoteResult remoteCall(RemoteCallable<IBackupCallback> remoteCallable, long timeoutMs)
+            throws RemoteException {
+        mPendingCall = new RemoteCall(mCancelled, remoteCallable, timeoutMs);
+        RemoteResult result = mPendingCall.call();
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "Agent call returned " + result);
+        }
+        mPendingCall = null;
+        return result;
+    }
+
+    private enum BackupState {
+        INITIAL,
+        BACKUP_PM,
+        RUNNING_QUEUE,
+        CANCELLED,
+        FINAL
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/remote/FutureBackupCallback.java b/services/backup/java/com/android/server/backup/remote/FutureBackupCallback.java
new file mode 100644
index 0000000..1445cc3
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/remote/FutureBackupCallback.java
@@ -0,0 +1,39 @@
+/*
+ * 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.remote;
+
+import android.app.backup.IBackupCallback;
+import android.os.RemoteException;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * An implementation of {@link IBackupCallback} that completes the {@link CompletableFuture}
+ * provided in the constructor with a successful {@link RemoteResult}.
+ */
+public class FutureBackupCallback extends IBackupCallback.Stub {
+    private final CompletableFuture<RemoteResult> mFuture;
+
+    FutureBackupCallback(CompletableFuture<RemoteResult> future) {
+        mFuture = future;
+    }
+
+    @Override
+    public void operationComplete(long result) throws RemoteException {
+        mFuture.complete(RemoteResult.successful(result));
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/remote/RemoteCall.java b/services/backup/java/com/android/server/backup/remote/RemoteCall.java
new file mode 100644
index 0000000..ac84811
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/remote/RemoteCall.java
@@ -0,0 +1,134 @@
+/*
+ * 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.remote;
+
+import android.annotation.WorkerThread;
+import android.app.backup.IBackupCallback;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * A wrapper that encapsulates an outbound call from the system process, converting an asynchronous
+ * operation into a synchronous operation with time-out and cancellation built-in. This was built to
+ * be able to call one-way binder methods that accept a {@link IBackupCallback} as a callback and
+ * handle the result inline.
+ *
+ * <p>Create one {@link RemoteCall} object providing the actual call in the form of a {@link
+ * RemoteCallable} that accepts a {@link IBackupCallback}. Perform the call by calling {@link
+ * #call()}, at which point {@link RemoteCall} will execute the callable providing an implementation
+ * of the callback that communicates the result back to this object. Even if the call returns
+ * straight away (which is the case for one-way methods) the method will only return when either the
+ * callback is called, time-out happens, or someone calls {@link #cancel()}.
+ *
+ * <p>This class was designed to have the method {@link #call()} called only once.
+ */
+// TODO: Kick-off callable in dedicated thread (because of local calls, which are synchronous)
+public class RemoteCall {
+    private final RemoteCallable<IBackupCallback> mCallable;
+    private final CompletableFuture<RemoteResult> mFuture;
+    private final long mTimeoutMs;
+
+    /**
+     * Creates a new {@link RemoteCall} object for a given callable.
+     *
+     * @param callable A function that signals its completion by calling {@link
+     *     IBackupCallback#operationComplete(long)} on the object provided as a parameter.
+     * @param timeoutMs The time in milliseconds after which {@link #call()} will return with {@link
+     *     RemoteResult#FAILED_TIMED_OUT} if the callable hasn't completed and no one canceled. The
+     *     time starts to be counted in {@link #call()}.
+     */
+    public RemoteCall(RemoteCallable<IBackupCallback> callable, long timeoutMs) {
+        this(false, callable, timeoutMs);
+    }
+
+    /**
+     * Same as {@link #RemoteCall(RemoteCallable, long)} but with parameter {@code cancelled}.
+     *
+     * @param cancelled Whether the call has already been canceled. It has the same effect of
+     *     calling {@link #cancel()} before {@link #call()}.
+     * @see #RemoteCall(RemoteCallable, long)
+     */
+    public RemoteCall(boolean cancelled, RemoteCallable<IBackupCallback> callable, long timeoutMs) {
+        mCallable = callable;
+        mTimeoutMs = timeoutMs;
+        mFuture = new CompletableFuture<>();
+        if (cancelled) {
+            cancel();
+        }
+    }
+
+    /**
+     * Kicks-off the callable provided in the constructor and blocks before returning, waiting for
+     * the first of these to happen:
+     *
+     * <ul>
+     *   <li>The callback passed to {@link RemoteCallable} is called with the result. We return a
+     *       successful {@link RemoteResult} with the result.
+     *   <li>Time-out happens. We return {@link RemoteResult#FAILED_TIMED_OUT}.
+     *   <li>Someone calls {@link #cancel()} on this object. We return {@link
+     *       RemoteResult#FAILED_CANCELLED}.
+     * </ul>
+     *
+     * <p>This method can't be called from the main thread and was designed to be called only once.
+     *
+     * @return A {@link RemoteResult} with the result of the operation.
+     * @throws RemoteException If the callable throws it.
+     */
+    @WorkerThread
+    public RemoteResult call() throws RemoteException {
+        // If called on the main-thread we would never get a time-out != 0
+        Preconditions.checkState(
+                !Looper.getMainLooper().isCurrentThread(), "Can't call call() on main thread");
+
+        if (!mFuture.isDone()) {
+            if (mTimeoutMs == 0L) {
+                timeOut();
+            } else {
+                Handler.getMain().postDelayed(this::timeOut, mTimeoutMs);
+                mCallable.call(new FutureBackupCallback(mFuture));
+            }
+        }
+        try {
+            return mFuture.get();
+        } catch (InterruptedException e) {
+            return RemoteResult.FAILED_THREAD_INTERRUPTED;
+        } catch (ExecutionException e) {
+            throw new IllegalStateException("Future unexpectedly completed with an exception");
+        }
+    }
+
+    /**
+     * Attempts to cancel the operation. It will only be successful if executed before the callback
+     * is called and before the time-out.
+     *
+     * <p>This method can be called from any thread, any time, including the same thread that called
+     * {@link #call()} (which is obviously only possible if the former is called before the latter).
+     */
+    public void cancel() {
+        mFuture.complete(RemoteResult.FAILED_CANCELLED);
+    }
+
+    private void timeOut() {
+        mFuture.complete(RemoteResult.FAILED_TIMED_OUT);
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/remote/RemoteCallable.java b/services/backup/java/com/android/server/backup/remote/RemoteCallable.java
new file mode 100644
index 0000000..d2671ff
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/remote/RemoteCallable.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 com.android.server.backup.remote;
+
+import android.os.RemoteException;
+
+/**
+ * Implementations should perform a remote call in {@link #call(Object)}, possibly throwing {@link
+ * RemoteException}.
+ */
+@FunctionalInterface
+public interface RemoteCallable<T> {
+    void call(T input) throws RemoteException;
+}
diff --git a/services/backup/java/com/android/server/backup/remote/RemoteResult.java b/services/backup/java/com/android/server/backup/remote/RemoteResult.java
new file mode 100644
index 0000000..7f4f469
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/remote/RemoteResult.java
@@ -0,0 +1,116 @@
+/*
+ * 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.remote;
+
+import android.annotation.IntDef;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Represents the result of a {@link RemoteCall}. It can be either {@link #FAILED_TIMED_OUT}, {@link
+ * #FAILED_CANCELLED}, {@link #FAILED_THREAD_INTERRUPTED} or a successful result, in which case
+ * {@link #get()} returns its value.
+ *
+ * <p>Use {@link #succeeded()} to check for successful result, or direct identity comparison to
+ * check for specific failures, like {@code result == RemoteResult.FAILED_CANCELLED}.
+ */
+public class RemoteResult {
+    public static final RemoteResult FAILED_TIMED_OUT = new RemoteResult(Type.FAILED_TIMED_OUT, 0);
+    public static final RemoteResult FAILED_CANCELLED = new RemoteResult(Type.FAILED_CANCELLED, 0);
+    public static final RemoteResult FAILED_THREAD_INTERRUPTED =
+            new RemoteResult(Type.FAILED_THREAD_INTERRUPTED, 0);
+
+    public static RemoteResult successful(long value) {
+        return new RemoteResult(Type.SUCCESS, value);
+    }
+
+    @Type private final int mType;
+    private final long mValue;
+
+    private RemoteResult(@Type int type, long value) {
+        mType = type;
+        mValue = value;
+    }
+
+    public boolean succeeded() {
+        return mType == Type.SUCCESS;
+    }
+
+    /**
+     * Returns the value of this result.
+     *
+     * @throws IllegalStateException in case this is not a successful result.
+     */
+    public long get() {
+        Preconditions.checkState(succeeded(), "Can't obtain value of failed result");
+        return mValue;
+    }
+
+    @Override
+    public String toString() {
+        return "RemoteResult{" + toStringDescription() + "}";
+    }
+
+    private String toStringDescription() {
+        switch (mType) {
+            case Type.SUCCESS:
+                return Long.toString(mValue);
+            case Type.FAILED_TIMED_OUT:
+                return "FAILED_TIMED_OUT";
+            case Type.FAILED_CANCELLED:
+                return "FAILED_CANCELLED";
+            case Type.FAILED_THREAD_INTERRUPTED:
+                return "FAILED_THREAD_INTERRUPTED";
+        }
+        throw new AssertionError("Unknown type");
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof RemoteResult)) {
+            return false;
+        }
+        RemoteResult that = (RemoteResult) o;
+        return mType == that.mType && mValue == that.mValue;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mType, mValue);
+    }
+
+    @IntDef({
+        Type.SUCCESS,
+        Type.FAILED_TIMED_OUT,
+        Type.FAILED_CANCELLED,
+        Type.FAILED_THREAD_INTERRUPTED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface Type {
+        int SUCCESS = 0;
+        int FAILED_TIMED_OUT = 1;
+        int FAILED_CANCELLED = 2;
+        int FAILED_THREAD_INTERRUPTED = 3;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java b/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
new file mode 100644
index 0000000..28d85a6
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
@@ -0,0 +1,43 @@
+/*
+ * 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.remote;
+
+import android.app.backup.IBackupCallback;
+import android.app.backup.IBackupManager;
+import android.os.RemoteException;
+
+import com.android.server.backup.BackupManagerService;
+
+/**
+ * An implementation of {@link IBackupCallback} that routes the result to {@link
+ * BackupManagerService} via {@link IBackupManager#opComplete(int, long)} passing the token provided
+ * in the constructor.
+ */
+public class ServiceBackupCallback extends IBackupCallback.Stub {
+    private final IBackupManager mBackupManager;
+    private final int mToken;
+
+    public ServiceBackupCallback(IBackupManager backupManager, int token) {
+        mBackupManager = backupManager;
+        mToken = token;
+    }
+
+    @Override
+    public void operationComplete(long result) throws RemoteException {
+        mBackupManager.opComplete(mToken, result);
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
new file mode 100644
index 0000000..dc7044e
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
@@ -0,0 +1,33 @@
+package com.android.server.backup.restore;
+
+import android.app.IBackupAgent;
+import android.os.RemoteException;
+
+import com.android.server.backup.BackupManagerService;
+
+/**
+ * Runner that can be placed on a separate thread to do in-process invocation of the "restore
+ * finished" API asynchronously.  Used by adb restore.
+ */
+public class AdbRestoreFinishedRunnable implements Runnable {
+
+    private final IBackupAgent mAgent;
+    private final int mToken;
+    private final BackupManagerService mBackupManagerService;
+
+    AdbRestoreFinishedRunnable(IBackupAgent agent, int token,
+            BackupManagerService backupManagerService) {
+        mAgent = agent;
+        mToken = token;
+        mBackupManagerService = backupManagerService;
+    }
+
+    @Override
+    public void run() {
+        try {
+            mAgent.doRestoreFinished(mToken, mBackupManagerService.getBackupManagerBinder());
+        } catch (RemoteException e) {
+            // never happens; this is used only for local binder calls
+        }
+    }
+}
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 6bc7530..1084f52 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -122,15 +122,17 @@
 
     // Widget blob to be restored out-of-band
     private byte[] mWidgetData = null;
+    private long mAppVersion;
 
     final int mEphemeralOpToken;
 
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+    final boolean mIsAdbRestore;
 
     public FullRestoreEngine(BackupManagerService backupManagerService,
             BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
             IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
-            boolean allowObbs, int ephemeralOpToken) {
+            boolean allowObbs, int ephemeralOpToken, boolean isAdbRestore) {
         mBackupManagerService = backupManagerService;
         mEphemeralOpToken = ephemeralOpToken;
         mMonitorTask = monitorTask;
@@ -144,6 +146,7 @@
         mAgentTimeoutParameters = Preconditions.checkNotNull(
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
+        mIsAdbRestore = isAdbRestore;
     }
 
     public IBackupAgent getAgent() {
@@ -209,7 +212,7 @@
                         }
                         // Now we're really done
                         tearDownPipes();
-                        tearDownAgent(mTargetApp);
+                        tearDownAgent(mTargetApp, mIsAdbRestore);
                         mTargetApp = null;
                         mAgentPackage = null;
                     }
@@ -218,6 +221,9 @@
                 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
                     Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
                             info);
+                    // readAppManifestAndReturnSignatures() will have extracted the version from
+                    // the manifest, so we save it to use in adb key-value restore later.
+                    mAppVersion = info.version;
                     PackageManagerInternal pmi = LocalServices.getService(
                             PackageManagerInternal.class);
                     RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
@@ -362,7 +368,9 @@
                             // All set; now set up the IPC and launch the agent
                             setUpPipes();
                             mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
-                                    ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
+                                    FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)
+                                            ? ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
+                                            : ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
                             mAgentPackage = pkg;
                         } catch (IOException e) {
                             // fall through to error handling
@@ -419,6 +427,8 @@
                                     Slog.d(TAG, "Restoring key-value file for " + pkg
                                             + " : " + info.path);
                                 }
+                                // Set the version saved from manifest entry.
+                                info.version = mAppVersion;
                                 KeyValueAdbRestoreEngine restoreEngine =
                                         new KeyValueAdbRestoreEngine(
                                                 mBackupManagerService,
@@ -506,7 +516,7 @@
                             mBackupManagerService.getBackupHandler().removeMessages(
                                     MSG_RESTORE_OPERATION_TIMEOUT);
                             tearDownPipes();
-                            tearDownAgent(mTargetApp);
+                            tearDownAgent(mTargetApp, false);
                             mAgent = null;
                             mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
 
@@ -559,7 +569,7 @@
             tearDownPipes();
             setRunning(false);
             if (mustKillAgent) {
-                tearDownAgent(mTargetApp);
+                tearDownAgent(mTargetApp, mIsAdbRestore);
             }
         }
         return (info != null);
@@ -588,9 +598,37 @@
         }
     }
 
-    private void tearDownAgent(ApplicationInfo app) {
+    private void tearDownAgent(ApplicationInfo app, boolean doRestoreFinished) {
         if (mAgent != null) {
-            mBackupManagerService.tearDownAgentAndKill(app);
+            try {
+                // In the adb restore case, we do restore-finished here
+                if (doRestoreFinished) {
+                    final int token = mBackupManagerService.generateRandomIntegerToken();
+                    long fullBackupAgentTimeoutMillis =
+                            mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
+                    final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
+                            mBackupManagerService, token);
+                    mBackupManagerService.prepareOperationTimeout(
+                            token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT);
+                    if (mTargetApp.processName.equals("system")) {
+                        if (MORE_DEBUG) {
+                            Slog.d(TAG, "system agent - restoreFinished on thread");
+                        }
+                        Runnable runner = new AdbRestoreFinishedRunnable(mAgent, token,
+                                mBackupManagerService);
+                        new Thread(runner, "restore-sys-finished-runner").start();
+                    } else {
+                        mAgent.doRestoreFinished(token,
+                                mBackupManagerService.getBackupManagerBinder());
+                    }
+
+                    latch.await();
+                }
+
+                mBackupManagerService.tearDownAgentAndKill(app);
+            } catch (RemoteException e) {
+                Slog.d(TAG, "Lost app trying to shut down");
+            }
             mAgent = null;
         }
     }
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngineThread.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngineThread.java
new file mode 100644
index 0000000..7075608
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngineThread.java
@@ -0,0 +1,63 @@
+package com.android.server.backup.restore;
+
+import android.os.ParcelFileDescriptor;
+
+import libcore.io.IoUtils;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+
+class FullRestoreEngineThread implements Runnable {
+
+    FullRestoreEngine mEngine;
+    InputStream mEngineStream;
+    private final boolean mMustKillAgent;
+
+    FullRestoreEngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) {
+        mEngine = engine;
+        engine.setRunning(true);
+        // We *do* want this FileInputStream to own the underlying fd, so that
+        // when we are finished with it, it closes this end of the pipe in a way
+        // that signals its other end.
+        mEngineStream = new FileInputStream(engineSocket.getFileDescriptor(), true);
+        // Tell it to be sure to leave the agent instance up after finishing
+        mMustKillAgent = false;
+    }
+
+    //for adb restore
+    FullRestoreEngineThread(FullRestoreEngine engine, InputStream inputStream) {
+        mEngine = engine;
+        engine.setRunning(true);
+        mEngineStream = inputStream;
+        // philippov: in adb agent is killed after restore.
+        mMustKillAgent = true;
+    }
+
+    public boolean isRunning() {
+        return mEngine.isRunning();
+    }
+
+    public int waitForResult() {
+        return mEngine.waitForResult();
+    }
+
+    @Override
+    public void run() {
+        try {
+            while (mEngine.isRunning()) {
+                mEngine.restoreOneFile(mEngineStream, mMustKillAgent, mEngine.mBuffer,
+                        mEngine.mOnlyPackage, mEngine.mAllowApks, mEngine.mEphemeralOpToken,
+                        mEngine.mMonitor);
+            }
+        } finally {
+            // Because mEngineStream adopted its underlying FD, this also
+            // closes this end of the pipe.
+            IoUtils.closeQuietly(mEngineStream);
+        }
+    }
+
+    public void handleTimeout() {
+        IoUtils.closeQuietly(mEngineStream);
+        mEngine.handleTimeout();
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 0c99b44..32dbad9 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -18,8 +18,6 @@
 
 import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
 import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
@@ -30,15 +28,11 @@
 import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 
-import android.app.ApplicationThreadConstants;
+
 import android.app.IBackupAgent;
-import android.app.backup.FullBackup;
-import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.BackupAgent;
 import android.app.backup.IFullBackupRestoreObserver;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.Signature;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
@@ -50,18 +44,12 @@
 import com.android.server.LocalServices;
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
-import com.android.server.backup.FileMetadata;
-import com.android.server.backup.KeyValueAdbRestoreEngine;
 import com.android.server.backup.PackageManagerBackupAgent;
 import com.android.server.backup.fullbackup.FullBackupObbConnection;
-import com.android.server.backup.utils.BytesReadListener;
 import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
 import com.android.server.backup.utils.PasswordUtils;
-import com.android.server.backup.utils.RestoreUtils;
-import com.android.server.backup.utils.TarBackupReader;
 
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.security.InvalidAlgorithmParameterException;
@@ -89,7 +77,7 @@
     private final String mCurrentPassword;
     private final String mDecryptPassword;
     private final AtomicBoolean mLatchObject;
-    private final PackageManagerBackupAgent mPackageManagerBackupAgent;
+    private final BackupAgent mPackageManagerBackupAgent;
     private final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
 
     private IFullBackupRestoreObserver mObserver;
@@ -104,31 +92,6 @@
     private long mBytes;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
-    // Runner that can be placed on a separate thread to do in-process invocation
-    // of the "restore finished" API asynchronously.  Used by adb restore.
-    private static class RestoreFinishedRunnable implements Runnable {
-
-        private final IBackupAgent mAgent;
-        private final int mToken;
-        private final BackupManagerService mBackupManagerService;
-
-        RestoreFinishedRunnable(IBackupAgent agent, int token,
-                BackupManagerService backupManagerService) {
-            mAgent = agent;
-            mToken = token;
-            mBackupManagerService = backupManagerService;
-        }
-
-        @Override
-        public void run() {
-            try {
-                mAgent.doRestoreFinished(mToken, mBackupManagerService.getBackupManagerBinder());
-            } catch (RemoteException e) {
-                // never happens; this is used only for local binder calls
-            }
-        }
-    }
-
     // possible handling states for a given package in the restore dataset
     private final HashMap<String, RestorePolicy> mPackagePolicies
             = new HashMap<>();
@@ -199,23 +162,18 @@
                 return;
             }
 
-            byte[] buffer = new byte[32 * 1024];
-            boolean didRestore;
-            do {
-                didRestore = restoreOneFile(tarInputStream, false /* mustKillAgent */, buffer,
-                        null /* onlyPackage */, true /* allowApks */,
-                        mBackupManagerService.generateRandomIntegerToken(), null /* monitor */);
-            } while (didRestore);
+            FullRestoreEngine mEngine = new FullRestoreEngine(mBackupManagerService, null,
+                    mObserver, null, null, true, true/*unused*/, 0 /*unused*/, true);
+            FullRestoreEngineThread mEngineThread = new FullRestoreEngineThread(mEngine,
+                    tarInputStream);
+            mEngineThread.run();
 
             if (MORE_DEBUG) {
-                Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
+                Slog.v(TAG, "Done consuming input tarfile.");
             }
         } catch (IOException e) {
             Slog.e(TAG, "Unable to read restore input");
         } finally {
-            tearDownPipes();
-            tearDownAgent(mTargetApp, true);
-
             try {
                 if (rawInStream != null) {
                     rawInStream.close();
@@ -433,432 +391,4 @@
 
         return result;
     }
-
-    boolean restoreOneFile(InputStream instream, boolean mustKillAgent, byte[] buffer,
-            PackageInfo onlyPackage, boolean allowApks, int token, IBackupManagerMonitor monitor) {
-        BytesReadListener bytesReadListener = new BytesReadListener() {
-            @Override
-            public void onBytesRead(long bytesRead) {
-                mBytes += bytesRead;
-            }
-        };
-        TarBackupReader tarBackupReader = new TarBackupReader(instream,
-                bytesReadListener, monitor);
-        FileMetadata info;
-        try {
-            info = tarBackupReader.readTarHeaders();
-            if (info != null) {
-                if (MORE_DEBUG) {
-                    info.dump();
-                }
-
-                final String pkg = info.packageName;
-                if (!pkg.equals(mAgentPackage)) {
-                    // okay, change in package; set up our various
-                    // bookkeeping if we haven't seen it yet
-                    if (!mPackagePolicies.containsKey(pkg)) {
-                        mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
-                    }
-
-                    // Clean up the previous agent relationship if necessary,
-                    // and let the observer know we're considering a new app.
-                    if (mAgent != null) {
-                        if (DEBUG) {
-                            Slog.d(TAG, "Saw new package; finalizing old one");
-                        }
-                        // Now we're really done
-                        tearDownPipes();
-                        tearDownAgent(mTargetApp, true);
-                        mTargetApp = null;
-                        mAgentPackage = null;
-                    }
-                }
-
-                if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
-                    Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
-                            info);
-                    // readAppManifestAndReturnSignatures() will have extracted the version from
-                    // the manifest, so we save it to use in key-value restore later.
-                    mAppVersion = info.version;
-                    PackageManagerInternal pmi = LocalServices.getService(
-                            PackageManagerInternal.class);
-                    RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
-                            mBackupManagerService.getPackageManager(), allowApks,
-                            info, signatures, pmi);
-                    mManifestSignatures.put(info.packageName, signatures);
-                    mPackagePolicies.put(pkg, restorePolicy);
-                    mPackageInstallers.put(pkg, info.installerPackageName);
-                    // We've read only the manifest content itself at this point,
-                    // so consume the footer before looping around to the next
-                    // input file
-                    tarBackupReader.skipTarPadding(info.size);
-                    mObserver = FullBackupRestoreObserverUtils.sendOnRestorePackage(mObserver, pkg);
-                } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
-                    // Metadata blobs!
-                    tarBackupReader.readMetadata(info);
-
-                    // The following only exist because we want to keep refactoring as safe as
-                    // possible, without changing too much.
-                    // TODO: Refactor, so that there are no funny things like this.
-                    // This is read during TarBackupReader.readMetadata().
-                    mWidgetData = tarBackupReader.getWidgetData();
-                    // This can be nulled during TarBackupReader.readMetadata().
-                    monitor = tarBackupReader.getMonitor();
-
-                    tarBackupReader.skipTarPadding(info.size);
-                } else {
-                    // Non-manifest, so it's actual file data.  Is this a package
-                    // we're ignoring?
-                    boolean okay = true;
-                    RestorePolicy policy = mPackagePolicies.get(pkg);
-                    switch (policy) {
-                        case IGNORE:
-                            okay = false;
-                            break;
-
-                        case ACCEPT_IF_APK:
-                            // If we're in accept-if-apk state, then the first file we
-                            // see MUST be the apk.
-                            if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
-                                if (DEBUG) {
-                                    Slog.d(TAG, "APK file; installing");
-                                }
-                                // Try to install the app.
-                                String installerPackageName = mPackageInstallers.get(pkg);
-                                boolean isSuccessfullyInstalled = RestoreUtils.installApk(instream,
-                                        mBackupManagerService.getContext(),
-                                        mDeleteObserver, mManifestSignatures, mPackagePolicies,
-                                        info, installerPackageName, bytesReadListener);
-                                // good to go; promote to ACCEPT
-                                mPackagePolicies.put(pkg, isSuccessfullyInstalled
-                                        ? RestorePolicy.ACCEPT
-                                        : RestorePolicy.IGNORE);
-                                // At this point we've consumed this file entry
-                                // ourselves, so just strip the tar footer and
-                                // go on to the next file in the input stream
-                                tarBackupReader.skipTarPadding(info.size);
-                                return true;
-                            } else {
-                                // File data before (or without) the apk.  We can't
-                                // handle it coherently in this case so ignore it.
-                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
-                                okay = false;
-                            }
-                            break;
-
-                        case ACCEPT:
-                            if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
-                                if (DEBUG) {
-                                    Slog.d(TAG, "apk present but ACCEPT");
-                                }
-                                // we can take the data without the apk, so we
-                                // *want* to do so.  skip the apk by declaring this
-                                // one file not-okay without changing the restore
-                                // policy for the package.
-                                okay = false;
-                            }
-                            break;
-
-                        default:
-                            // Something has gone dreadfully wrong when determining
-                            // the restore policy from the manifest.  Ignore the
-                            // rest of this package's data.
-                            Slog.e(TAG, "Invalid policy from manifest");
-                            okay = false;
-                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
-                            break;
-                    }
-
-                    // The path needs to be canonical
-                    if (!isCanonicalFilePath(info.path)) {
-                        okay = false;
-                    }
-
-                    // If the policy is satisfied, go ahead and set up to pipe the
-                    // data to the agent.
-                    if (DEBUG && okay && mAgent != null) {
-                        Slog.i(TAG, "Reusing existing agent instance");
-                    }
-                    if (okay && mAgent == null) {
-                        if (DEBUG) {
-                            Slog.d(TAG, "Need to launch agent for " + pkg);
-                        }
-
-                        try {
-                            mTargetApp =
-                                    mBackupManagerService.getPackageManager().getApplicationInfo(
-                                            pkg, 0);
-
-                            // If we haven't sent any data to this app yet, we probably
-                            // need to clear it first.  Check that.
-                            if (!mClearedPackages.contains(pkg)) {
-                                // apps with their own backup agents are
-                                // responsible for coherently managing a full
-                                // restore.
-                                if (mTargetApp.backupAgentName == null) {
-                                    if (DEBUG) {
-                                        Slog.d(TAG,
-                                                "Clearing app data preparatory to full restore");
-                                    }
-                                    mBackupManagerService.clearApplicationDataSynchronous(pkg, true);
-                                } else {
-                                    if (DEBUG) {
-                                        Slog.d(TAG, "backup agent ("
-                                                + mTargetApp.backupAgentName + ") => no clear");
-                                    }
-                                }
-                                mClearedPackages.add(pkg);
-                            } else {
-                                if (DEBUG) {
-                                    Slog.d(TAG, "We've initialized this app already; no clear "
-                                            + "required");
-                                }
-                            }
-
-                            // All set; now set up the IPC and launch the agent
-                            setUpPipes();
-                            mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
-                                    FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)
-                                            ? ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
-                                            : ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
-                            mAgentPackage = pkg;
-                        } catch (IOException e) {
-                            // fall through to error handling
-                        } catch (NameNotFoundException e) {
-                            // fall through to error handling
-                        }
-
-                        if (mAgent == null) {
-                            Slog.e(TAG, "Unable to create agent for " + pkg);
-                            okay = false;
-                            tearDownPipes();
-                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
-                        }
-                    }
-
-                    // Sanity check: make sure we never give data to the wrong app.  This
-                    // should never happen but a little paranoia here won't go amiss.
-                    if (okay && !pkg.equals(mAgentPackage)) {
-                        Slog.e(TAG, "Restoring data for " + pkg
-                                + " but agent is for " + mAgentPackage);
-                        okay = false;
-                    }
-
-                    // At this point we have an agent ready to handle the full
-                    // restore data as well as a pipe for sending data to
-                    // that agent.  Tell the agent to start reading from the
-                    // pipe.
-                    if (okay) {
-                        boolean agentSuccess = true;
-                        long toCopy = info.size;
-                        long restoreAgentTimeoutMillis =
-                                mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
-                        try {
-                            mBackupManagerService.prepareOperationTimeout(
-                                    token, restoreAgentTimeoutMillis, null, OP_TYPE_RESTORE_WAIT);
-
-                            if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
-                                if (DEBUG) {
-                                    Slog.d(TAG, "Restoring OBB file for " + pkg
-                                            + " : " + info.path);
-                                }
-                                mObbConnection.restoreObbFile(pkg, mPipes[0],
-                                        info.size, info.type, info.path, info.mode,
-                                        info.mtime, token,
-                                        mBackupManagerService.getBackupManagerBinder());
-                            } else if (FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)) {
-                                if (DEBUG) {
-                                    Slog.d(TAG, "Restoring key-value file for " + pkg
-                                            + " : " + info.path);
-                                }
-                                // Set the version saved from manifest entry.
-                                info.version = mAppVersion;
-                                KeyValueAdbRestoreEngine restoreEngine =
-                                        new KeyValueAdbRestoreEngine(
-                                                mBackupManagerService,
-                                                mBackupManagerService.getDataDir(), info, mPipes[0],
-                                                mAgent, token);
-                                new Thread(restoreEngine, "restore-key-value-runner").start();
-                            } else {
-                                if (DEBUG) {
-                                    Slog.d(TAG, "Invoking agent to restore file " + info.path);
-                                }
-                                // fire up the app's agent listening on the socket.  If
-                                // the agent is running in the system process we can't
-                                // just invoke it asynchronously, so we provide a thread
-                                // for it here.
-                                if (mTargetApp.processName.equals("system")) {
-                                    Slog.d(TAG, "system process agent - spinning a thread");
-                                    RestoreFileRunnable runner = new RestoreFileRunnable(
-                                            mBackupManagerService, mAgent, info, mPipes[0], token);
-                                    new Thread(runner, "restore-sys-runner").start();
-                                } else {
-                                    mAgent.doRestoreFile(mPipes[0], info.size, info.type,
-                                            info.domain, info.path, info.mode, info.mtime,
-                                            token, mBackupManagerService.getBackupManagerBinder());
-                                }
-                            }
-                        } catch (IOException e) {
-                            // couldn't dup the socket for a process-local restore
-                            Slog.d(TAG, "Couldn't establish restore");
-                            agentSuccess = false;
-                            okay = false;
-                        } catch (RemoteException e) {
-                            // whoops, remote entity went away.  We'll eat the content
-                            // ourselves, then, and not copy it over.
-                            Slog.e(TAG, "Agent crashed during full restore");
-                            agentSuccess = false;
-                            okay = false;
-                        }
-
-                        // Copy over the data if the agent is still good
-                        if (okay) {
-                            boolean pipeOkay = true;
-                            FileOutputStream pipe = new FileOutputStream(
-                                    mPipes[1].getFileDescriptor());
-                            while (toCopy > 0) {
-                                int toRead = (toCopy > buffer.length)
-                                        ? buffer.length : (int) toCopy;
-                                int nRead = instream.read(buffer, 0, toRead);
-                                if (nRead >= 0) {
-                                    mBytes += nRead;
-                                }
-                                if (nRead <= 0) {
-                                    break;
-                                }
-                                toCopy -= nRead;
-
-                                // send it to the output pipe as long as things
-                                // are still good
-                                if (pipeOkay) {
-                                    try {
-                                        pipe.write(buffer, 0, nRead);
-                                    } catch (IOException e) {
-                                        Slog.e(TAG, "Failed to write to restore pipe", e);
-                                        pipeOkay = false;
-                                    }
-                                }
-                            }
-
-                            // done sending that file!  Now we just need to consume
-                            // the delta from info.size to the end of block.
-                            tarBackupReader.skipTarPadding(info.size);
-
-                            // and now that we've sent it all, wait for the remote
-                            // side to acknowledge receipt
-                            agentSuccess = mBackupManagerService.waitUntilOperationComplete(token);
-                        }
-
-                        // okay, if the remote end failed at any point, deal with
-                        // it by ignoring the rest of the restore on it
-                        if (!agentSuccess) {
-                            if (DEBUG) {
-                                Slog.d(TAG, "Agent failure restoring " + pkg + "; now ignoring");
-                            }
-                            mBackupManagerService.getBackupHandler().removeMessages(
-                                    MSG_RESTORE_OPERATION_TIMEOUT);
-                            tearDownPipes();
-                            tearDownAgent(mTargetApp, false);
-                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
-                        }
-                    }
-
-                    // Problems setting up the agent communication, or an already-
-                    // ignored package: skip to the next tar stream entry by
-                    // reading and discarding this file.
-                    if (!okay) {
-                        if (DEBUG) {
-                            Slog.d(TAG, "[discarding file content]");
-                        }
-                        long bytesToConsume = (info.size + 511) & ~511;
-                        while (bytesToConsume > 0) {
-                            int toRead = (bytesToConsume > buffer.length)
-                                    ? buffer.length : (int) bytesToConsume;
-                            long nRead = instream.read(buffer, 0, toRead);
-                            if (nRead >= 0) {
-                                mBytes += nRead;
-                            }
-                            if (nRead <= 0) {
-                                break;
-                            }
-                            bytesToConsume -= nRead;
-                        }
-                    }
-                }
-            }
-        } catch (IOException e) {
-            if (DEBUG) {
-                Slog.w(TAG, "io exception on restore socket read", e);
-            }
-            // treat as EOF
-            info = null;
-        }
-
-        return (info != null);
-    }
-
-    private static boolean isCanonicalFilePath(String path) {
-        if (path.contains("..") || path.contains("//")) {
-            if (MORE_DEBUG) {
-                Slog.w(TAG, "Dropping invalid path " + path);
-            }
-            return false;
-        }
-
-        return true;
-    }
-
-    private void setUpPipes() throws IOException {
-        mPipes = ParcelFileDescriptor.createPipe();
-    }
-
-    private void tearDownPipes() {
-        if (mPipes != null) {
-            try {
-                mPipes[0].close();
-                mPipes[0] = null;
-                mPipes[1].close();
-                mPipes[1] = null;
-            } catch (IOException e) {
-                Slog.w(TAG, "Couldn't close agent pipes", e);
-            }
-            mPipes = null;
-        }
-    }
-
-    private void tearDownAgent(ApplicationInfo app, boolean doRestoreFinished) {
-        if (mAgent != null) {
-            try {
-                // In the adb restore case, we do restore-finished here
-                if (doRestoreFinished) {
-                    final int token = mBackupManagerService.generateRandomIntegerToken();
-                    long fullBackupAgentTimeoutMillis =
-                            mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
-                    final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
-                            mBackupManagerService, token);
-                    mBackupManagerService.prepareOperationTimeout(
-                            token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT);
-                    if (mTargetApp.processName.equals("system")) {
-                        if (MORE_DEBUG) {
-                            Slog.d(TAG, "system agent - restoreFinished on thread");
-                        }
-                        Runnable runner = new RestoreFinishedRunnable(mAgent, token,
-                                mBackupManagerService);
-                        new Thread(runner, "restore-sys-finished-runner").start();
-                    } else {
-                        mAgent.doRestoreFinished(token,
-                                mBackupManagerService.getBackupManagerBinder());
-                    }
-
-                    latch.await();
-                }
-
-                mBackupManagerService.tearDownAgentAndKill(app);
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Lost app trying to shut down");
-            }
-            mAgent = null;
-        }
-    }
-
 }
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 12d72d8..580f70a 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -841,7 +841,7 @@
 
         final String TAG = "StreamFeederThread";
         FullRestoreEngine mEngine;
-        EngineThread mEngineThread;
+        FullRestoreEngineThread mEngineThread;
 
         // pipe through which we read data from the transport. [0] read, [1] write
         ParcelFileDescriptor[] mTransportPipes;
@@ -867,8 +867,8 @@
                     mCurrentPackage.packageName);
 
             mEngine = new FullRestoreEngine(backupManagerService, this, null,
-                    mMonitor, mCurrentPackage, false, false, mEphemeralOpToken);
-            mEngineThread = new EngineThread(mEngine, mEnginePipes[0]);
+                    mMonitor, mCurrentPackage, false, false, mEphemeralOpToken, false);
+            mEngineThread = new FullRestoreEngineThread(mEngine, mEnginePipes[0]);
 
             ParcelFileDescriptor eWriteEnd = mEnginePipes[1];
             ParcelFileDescriptor tReadEnd = mTransportPipes[0];
@@ -1031,50 +1031,6 @@
         }
     }
 
-    class EngineThread implements Runnable {
-
-        FullRestoreEngine mEngine;
-        FileInputStream mEngineStream;
-
-        EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) {
-            mEngine = engine;
-            engine.setRunning(true);
-            // We *do* want this FileInputStream to own the underlying fd, so that
-            // when we are finished with it, it closes this end of the pipe in a way
-            // that signals its other end.
-            mEngineStream = new FileInputStream(engineSocket.getFileDescriptor(), true);
-        }
-
-        public boolean isRunning() {
-            return mEngine.isRunning();
-        }
-
-        public int waitForResult() {
-            return mEngine.waitForResult();
-        }
-
-        @Override
-        public void run() {
-            try {
-                while (mEngine.isRunning()) {
-                    // Tell it to be sure to leave the agent instance up after finishing
-                    mEngine.restoreOneFile(mEngineStream, false, mEngine.mBuffer,
-                            mEngine.mOnlyPackage, mEngine.mAllowApks, mEngine.mEphemeralOpToken,
-                            mEngine.mMonitor);
-                }
-            } finally {
-                // Because mEngineStream adopted its underlying FD, this also
-                // closes this end of the pipe.
-                IoUtils.closeQuietly(mEngineStream);
-            }
-        }
-
-        public void handleTimeout() {
-            IoUtils.closeQuietly(mEngineStream);
-            mEngine.handleTimeout();
-        }
-    }
-
     // state FINAL : tear everything down and we're done.
     private void finalizeRestore() {
         if (MORE_DEBUG) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 8da6d1e..d9519e0 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -26,7 +26,6 @@
         "android.hardware.light-V2.0-java",
         "android.hardware.power-V1.0-java",
         "android.hardware.tv.cec-V1.0-java",
-        "android.hidl.manager-V1.0-java",
     ],
 
     static_libs: [
@@ -37,12 +36,14 @@
         "android.hardware.health-V1.0-java",
         "android.hardware.health-V2.0-java",
         "android.hardware.weaver-V1.0-java",
+        "android.hardware.biometrics.face-V1.0-java",
         "android.hardware.biometrics.fingerprint-V2.1-java",
         "android.hardware.oemlock-V1.0-java",
         "android.hardware.tetheroffload.control-V1.0-java",
         "android.hardware.vibrator-V1.0-java",
         "android.hardware.configstore-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
+        "android.hidl.manager-V1.0-java",
     ],
 }
 
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 6ca81c2..26ef42f 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -172,7 +172,8 @@
     long mNativeData;
     private long mNextWakeup;
     private long mNextNonWakeup;
-    private long mLastWakeupSet;
+    private long mNextWakeUpSetAt;
+    private long mNextNonWakeUpSetAt;
     private long mLastWakeup;
     private long mLastTrigger;
     private long mLastTickSet;
@@ -1939,7 +1940,7 @@
             TimeUtils.formatDuration(nowELAPSED - mLastAlarmDeliveryTime, pw);
             pw.println();
             pw.print("  Next non-wakeup delivery time: ");
-            TimeUtils.formatDuration(nowELAPSED - mNextNonWakeupDeliveryTime, pw);
+            TimeUtils.formatDuration(mNextNonWakeupDeliveryTime, nowELAPSED, pw);
             pw.println();
 
             long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED);
@@ -1948,10 +1949,12 @@
                     TimeUtils.formatDuration(mNextNonWakeup, nowELAPSED, pw);
                     pw.print(" = "); pw.print(mNextNonWakeup);
                     pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC)));
+            pw.print("    set at "); TimeUtils.formatDuration(mNextNonWakeUpSetAt, nowELAPSED, pw);
+            pw.println();
             pw.print("  Next wakeup alarm: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
                     pw.print(" = "); pw.print(mNextWakeup);
                     pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
-            pw.print("    set at "); TimeUtils.formatDuration(mLastWakeupSet, nowELAPSED, pw);
+            pw.print("    set at "); TimeUtils.formatDuration(mNextWakeUpSetAt, nowELAPSED, pw);
                     pw.println();
 
             pw.print("  Next kernel non-wakeup alarm: ");
@@ -2290,7 +2293,7 @@
             proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_LAST_WAKEUP_MS,
                     nowElapsed - mLastWakeup);
             proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_LAST_WAKEUP_SET_MS,
-                    nowElapsed - mLastWakeupSet);
+                    nowElapsed - mNextWakeUpSetAt);
             proto.write(AlarmManagerServiceDumpProto.TIME_CHANGE_EVENT_COUNT, mNumTimeChanged);
 
             final TreeSet<Integer> users = new TreeSet<>();
@@ -2675,16 +2678,53 @@
                 DateFormat.format(pattern, info.getTriggerTime()).toString();
     }
 
+    /**
+     * If the last time AlarmThread woke up precedes any due wakeup or non-wakeup alarm that we set
+     * by more than half a minute, log a wtf.
+     */
+    private void validateLastAlarmExpiredLocked(long nowElapsed) {
+        final StringBuilder errorMsg = new StringBuilder();
+        boolean stuck = false;
+        if (mNextNonWakeup < (nowElapsed - 10_000) && mLastWakeup < mNextNonWakeup) {
+            stuck = true;
+            errorMsg.append("[mNextNonWakeup=");
+            TimeUtils.formatDuration(mNextNonWakeup - nowElapsed, errorMsg);
+            errorMsg.append(" set at ");
+            TimeUtils.formatDuration(mNextNonWakeUpSetAt - nowElapsed, errorMsg);
+            errorMsg.append(", mLastWakeup=");
+            TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg);
+            errorMsg.append(", timerfd_gettime=" + getNextAlarm(mNativeData, ELAPSED_REALTIME));
+            errorMsg.append("];");
+        }
+        if (mNextWakeup < (nowElapsed - 10_000) && mLastWakeup < mNextWakeup) {
+            stuck = true;
+            errorMsg.append("[mNextWakeup=");
+            TimeUtils.formatDuration(mNextWakeup - nowElapsed, errorMsg);
+            errorMsg.append(" set at ");
+            TimeUtils.formatDuration(mNextWakeUpSetAt - nowElapsed, errorMsg);
+            errorMsg.append(", mLastWakeup=");
+            TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg);
+            errorMsg.append(", timerfd_gettime="
+                    + getNextAlarm(mNativeData, ELAPSED_REALTIME_WAKEUP));
+            errorMsg.append("];");
+        }
+        if (stuck) {
+            Slog.wtf(TAG, "Alarm delivery stuck: " + errorMsg.toString());
+        }
+    }
+
     void rescheduleKernelAlarmsLocked() {
         // Schedule the next upcoming wakeup alarm.  If there is a deliverable batch
         // prior to that which contains no wakeups, we schedule that as well.
+        final long nowElapsed = SystemClock.elapsedRealtime();
+        validateLastAlarmExpiredLocked(nowElapsed);
         long nextNonWakeup = 0;
         if (mAlarmBatches.size() > 0) {
             final Batch firstWakeup = findFirstWakeupBatchLocked();
             final Batch firstBatch = mAlarmBatches.get(0);
             if (firstWakeup != null) {
                 mNextWakeup = firstWakeup.start;
-                mLastWakeupSet = SystemClock.elapsedRealtime();
+                mNextWakeUpSetAt = nowElapsed;
                 setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
             }
             if (firstBatch != firstWakeup) {
@@ -2698,6 +2738,7 @@
         }
         if (nextNonWakeup != 0) {
             mNextNonWakeup = nextNonWakeup;
+            mNextNonWakeUpSetAt = nowElapsed;
             setLocked(ELAPSED_REALTIME, nextNonWakeup);
         }
     }
@@ -3477,9 +3518,13 @@
 
     private class AlarmThread extends Thread
     {
+        private int mFalseWakeups;
+        private int mWtfThreshold;
         public AlarmThread()
         {
             super("AlarmManager");
+            mFalseWakeups = 0;
+            mWtfThreshold = 10;
         }
 
         public void run()
@@ -3592,6 +3637,17 @@
                                 }
                                 mPendingNonWakeupAlarms.clear();
                             }
+                            if (mLastTimeChangeRealtime != nowELAPSED && triggerList.isEmpty()) {
+                                if (++mFalseWakeups >= mWtfThreshold) {
+                                    Slog.wtf(TAG, "Too many (" + mFalseWakeups
+                                            + ") false wakeups, nowElapsed=" + nowELAPSED);
+                                    if (mWtfThreshold < 100_000) {
+                                        mWtfThreshold *= 10;
+                                    } else {
+                                        mFalseWakeups = 0;
+                                    }
+                                }
+                            }
                             final ArraySet<Pair<String, Integer>> triggerPackages =
                                     new ArraySet<>();
                             for (int i = 0; i < triggerList.size(); i++) {
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 786d757..b3f6bd1 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -22,6 +22,7 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.AppOpsManagerInternal;
+import android.app.AppOpsManagerInternal.CheckOpsDelegate;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -198,7 +199,7 @@
     @VisibleForTesting
     final SparseArray<UidState> mUidStates = new SparseArray<>();
 
-    long mLastUptime;
+    long mLastRealtime;
 
     /*
      * These are app op restrictions imposed per user from various parties.
@@ -207,6 +208,8 @@
 
     SparseIntArray mProfileOwners;
 
+    private CheckOpsDelegate mCheckOpsDelegate;
+
     /**
      * All times are in milliseconds. These constants are kept synchronized with the system
      * global Settings. Any access to this class or its fields should be done while
@@ -770,7 +773,7 @@
                     } else {
                         settleTime = mConstants.BG_STATE_SETTLE_TIME;
                     }
-                    uidState.pendingStateCommitTime = SystemClock.uptimeMillis() + settleTime;
+                    uidState.pendingStateCommitTime = SystemClock.elapsedRealtime() + settleTime;
                 }
                 if (uidState.startNesting != 0) {
                     // There is some actively running operation...  need to find it
@@ -1411,15 +1414,39 @@
         }
     }
 
+    public CheckOpsDelegate getAppOpsServiceDelegate() {
+        synchronized (this) {
+            return mCheckOpsDelegate;
+        }
+    }
+
+    public void setAppOpsServiceDelegate(CheckOpsDelegate delegate) {
+        synchronized (this) {
+            mCheckOpsDelegate = delegate;
+        }
+    }
+
     @Override
     public int checkOperation(int code, int uid, String packageName) {
-        verifyIncomingUid(uid);
-        verifyIncomingOp(code);
-        String resolvedPackageName = resolvePackageName(uid, packageName);
-        if (resolvedPackageName == null) {
-            return AppOpsManager.MODE_IGNORED;
-        }
+        final CheckOpsDelegate delegate;
         synchronized (this) {
+            if (mCheckOpsDelegate == null) {
+                return checkOperationImpl(code, uid, packageName);
+            }
+            delegate = mCheckOpsDelegate;
+        }
+        return delegate.checkOperation(code, uid, packageName,
+                    AppOpsService.this::checkOperationImpl);
+    }
+
+    private int checkOperationImpl(int code, int uid, String packageName) {
+        synchronized (this) {
+            verifyIncomingUid(uid);
+            verifyIncomingOp(code);
+            String resolvedPackageName = resolvePackageName(uid, packageName);
+            if (resolvedPackageName == null) {
+                return AppOpsManager.MODE_IGNORED;
+            }
             if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
                 return AppOpsManager.MODE_IGNORED;
             }
@@ -1439,20 +1466,33 @@
 
     @Override
     public int checkAudioOperation(int code, int usage, int uid, String packageName) {
-        boolean suspended;
-        try {
-            suspended = isPackageSuspendedForUser(packageName, uid);
-        } catch (IllegalArgumentException ex) {
-            // Package not found.
-            suspended = false;
-        }
-
-        if (suspended) {
-            Slog.i(TAG, "Audio disabled for suspended package=" + packageName + " for uid=" + uid);
-            return AppOpsManager.MODE_IGNORED;
-        }
-
+        final CheckOpsDelegate delegate;
         synchronized (this) {
+            if (mCheckOpsDelegate == null) {
+                return checkAudioOperationImpl(code, usage, uid, packageName);
+            }
+            delegate = mCheckOpsDelegate;
+        }
+        return delegate.checkAudioOperation(code, usage, uid, packageName,
+                AppOpsService.this::checkAudioOperationImpl);
+    }
+
+    private int checkAudioOperationImpl(int code, int usage, int uid, String packageName) {
+        synchronized (this) {
+            boolean suspended;
+            try {
+                suspended = isPackageSuspendedForUser(packageName, uid);
+            } catch (IllegalArgumentException ex) {
+                // Package not found.
+                suspended = false;
+            }
+
+            if (suspended) {
+                Slog.i(TAG, "Audio disabled for suspended package=" + packageName
+                        + " for uid=" + uid);
+                return AppOpsManager.MODE_IGNORED;
+            }
+
             final int mode = checkRestrictionLocked(code, usage, uid, packageName);
             if (mode != AppOpsManager.MODE_ALLOWED) {
                 return mode;
@@ -1530,10 +1570,10 @@
     }
 
     @Override
-    public int noteProxyOperation(int code, String proxyPackageName,
-            int proxiedUid, String proxiedPackageName) {
+    public int noteProxyOperation(int code, int proxyUid,
+            String proxyPackageName, int proxiedUid, String proxiedPackageName) {
+        verifyIncomingUid(proxyUid);
         verifyIncomingOp(code);
-        final int proxyUid = Binder.getCallingUid();
         String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
         if (resolveProxyPackageName == null) {
             return AppOpsManager.MODE_IGNORED;
@@ -1553,6 +1593,18 @@
 
     @Override
     public int noteOperation(int code, int uid, String packageName) {
+        final CheckOpsDelegate delegate;
+        synchronized (this) {
+            if (mCheckOpsDelegate == null) {
+                return noteOperationImpl(code, uid, packageName);
+            }
+            delegate = mCheckOpsDelegate;
+        }
+        return delegate.noteOperation(code, uid, packageName,
+                AppOpsService.this::noteOperationImpl);
+    }
+
+    private int noteOperationImpl(int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         String resolvedPackageName = resolvePackageName(uid, packageName);
@@ -1881,11 +1933,11 @@
             mUidStates.put(uid, uidState);
         } else {
             if (uidState.pendingStateCommitTime != 0) {
-                if (uidState.pendingStateCommitTime < mLastUptime) {
+                if (uidState.pendingStateCommitTime < mLastRealtime) {
                     commitUidPendingStateLocked(uidState);
                 } else {
-                    mLastUptime = SystemClock.uptimeMillis();
-                    if (uidState.pendingStateCommitTime < mLastUptime) {
+                    mLastRealtime = SystemClock.elapsedRealtime();
+                    if (uidState.pendingStateCommitTime < mLastRealtime) {
                         commitUidPendingStateLocked(uidState);
                     }
                 }
@@ -3284,7 +3336,7 @@
                 }
                 if (uidState.pendingStateCommitTime != 0) {
                     pw.print("    pendingStateCommitTime=");
-                    TimeUtils.formatDuration(uidState.pendingStateCommitTime, nowUptime, pw);
+                    TimeUtils.formatDuration(uidState.pendingStateCommitTime, nowElapsed, pw);
                     pw.println();
                 }
                 if (uidState.startNesting != 0) {
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index ce1be6f..9a7c345 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -17,22 +17,31 @@
 package com.android.server;
 
 import android.app.AppGlobals;
+import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.KeyValueListParser;
 import android.util.Slog;
 
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BinderCallsStats;
+import com.android.internal.os.BinderInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 
 public class BinderCallsStatsService extends Binder {
 
@@ -41,23 +50,132 @@
     private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
             = "persist.sys.binder_calls_detailed_tracking";
 
-    public static void start() {
-        BinderCallsStatsService service = new BinderCallsStatsService();
-        ServiceManager.addService("binder_calls_stats", service);
-        boolean detailedTrackingEnabled = SystemProperties.getBoolean(
-                PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false);
+    /** Listens for flag changes. */
+    private static class SettingsObserver extends ContentObserver {
+        private static final String SETTINGS_ENABLED_KEY = "enabled";
+        private static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking";
+        private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data";
+        private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
 
-        if (detailedTrackingEnabled) {
-            Slog.i(TAG, "Enabled CPU usage tracking for binder calls. Controlled by "
-                    + PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
-                    + " or via dumpsys binder_calls_stats --enable-detailed-tracking");
-            BinderCallsStats.getInstance().setDetailedTracking(true);
+        private boolean mEnabled;
+        private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
+        private final Context mContext;
+        private final KeyValueListParser mParser = new KeyValueListParser(',');
+        private final BinderCallsStats mBinderCallsStats;
+
+        public SettingsObserver(Context context, BinderCallsStats binderCallsStats) {
+            super(BackgroundThread.getHandler());
+            mContext = context;
+            context.getContentResolver().registerContentObserver(mUri, false, this,
+                    UserHandle.USER_SYSTEM);
+            mBinderCallsStats = binderCallsStats;
+            // Always kick once to ensure that we match current state
+            onChange();
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            if (mUri.equals(uri)) {
+                onChange();
+            }
+        }
+
+        public void onChange() {
+            // Do not overwrite the default set manually.
+            if (!SystemProperties.get(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING).isEmpty()) {
+              return;
+            }
+
+            try {
+                    mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
+                            Settings.Global.BINDER_CALLS_STATS));
+            } catch (IllegalArgumentException e) {
+                    Slog.e(TAG, "Bad binder call stats settings", e);
+            }
+            mBinderCallsStats.setDetailedTracking(mParser.getBoolean(
+                    SETTINGS_DETAILED_TRACKING_KEY, BinderCallsStats.DETAILED_TRACKING_DEFAULT));
+            mBinderCallsStats.setSamplingInterval(mParser.getInt(
+                    SETTINGS_SAMPLING_INTERVAL_KEY,
+                    BinderCallsStats.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
+
+
+            final boolean enabled =
+                    mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT);
+            if (mEnabled != enabled) {
+                Binder.setObserver(enabled ? mBinderCallsStats : null);
+                mEnabled = enabled;
+                mBinderCallsStats.reset();
+            }
         }
     }
 
-    public static void reset() {
+    /**
+     * @hide Only for use within the system server.
+     */
+    public static class Internal {
+        private final BinderCallsStats mBinderCallsStats;
+
+        Internal(BinderCallsStats binderCallsStats) {
+            this.mBinderCallsStats = binderCallsStats;
+        }
+
+        public ArrayList<BinderCallsStats.ExportedCallStat> getExportedCallStats() {
+            return mBinderCallsStats.getExportedCallStats();
+        }
+
+        public ArrayMap<String, Integer> getExportedExceptionStats() {
+            return mBinderCallsStats.getExportedExceptionStats();
+        }
+    }
+
+    public static class LifeCycle extends SystemService {
+        private BinderCallsStatsService mService;
+        private BinderCallsStats mBinderCallsStats;
+
+        public LifeCycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
+            mService = new BinderCallsStatsService(mBinderCallsStats);
+            publishLocalService(Internal.class, new Internal(mBinderCallsStats));
+            publishBinderService("binder_calls_stats", mService);
+            boolean detailedTrackingEnabled = SystemProperties.getBoolean(
+                    PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false);
+
+            if (detailedTrackingEnabled) {
+                Slog.i(TAG, "Enabled CPU usage tracking for binder calls. Controlled by "
+                        + PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
+                        + " or via dumpsys binder_calls_stats --enable-detailed-tracking");
+                mBinderCallsStats.setDetailedTracking(true);
+            }
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
+                mService.systemReady(getContext());
+                mBinderCallsStats.systemReady(getContext());
+            }
+        }
+    }
+
+    private SettingsObserver mSettingsObserver;
+    private final BinderCallsStats mBinderCallsStats;
+
+    BinderCallsStatsService(BinderCallsStats binderCallsStats) {
+        mBinderCallsStats = binderCallsStats;
+    }
+
+    public void systemReady(Context context) {
+        mSettingsObserver = new SettingsObserver(context, mBinderCallsStats);
+    }
+
+    public void reset() {
         Slog.i(TAG, "Resetting stats");
-        BinderCallsStats.getInstance().reset();
+        mBinderCallsStats.reset();
     }
 
     @Override
@@ -73,12 +191,12 @@
                     return;
                 } else if ("--enable-detailed-tracking".equals(arg)) {
                     SystemProperties.set(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, "1");
-                    BinderCallsStats.getInstance().setDetailedTracking(true);
+                    mBinderCallsStats.setDetailedTracking(true);
                     pw.println("Detailed tracking enabled");
                     return;
                 } else if ("--disable-detailed-tracking".equals(arg)) {
                     SystemProperties.set(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, "");
-                    BinderCallsStats.getInstance().setDetailedTracking(false);
+                    mBinderCallsStats.setDetailedTracking(false);
                     pw.println("Detailed tracking disabled");
                     return;
                 } else if ("-h".equals(arg)) {
@@ -92,7 +210,7 @@
                 }
             }
         }
-        BinderCallsStats.getInstance().dump(pw, getAppIdToPackagesMap(), verbose);
+        mBinderCallsStats.dump(pw, getAppIdToPackagesMap(), verbose);
     }
 
     private Map<Integer, String> getAppIdToPackagesMap() {
@@ -106,7 +224,13 @@
         }
         Map<Integer,String> map = new HashMap<>();
         for (PackageInfo pkg : packages) {
-            map.put(pkg.applicationInfo.uid, pkg.packageName);
+            String name = pkg.packageName;
+            int uid = pkg.applicationInfo.uid;
+            // Use sharedUserId string as package name if there are collisions
+            if (pkg.sharedUserId != null && map.containsKey(uid)) {
+                name = "shared:" + pkg.sharedUserId;
+            }
+            map.put(uid, name);
         }
         return map;
     }
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index aa426d3..f81541e 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -53,12 +53,16 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
+import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
+import android.util.Log;
 import android.util.Slog;
 import android.util.StatsLog;
 
@@ -207,7 +211,7 @@
     // bluetooth profile services
     private final Map<Integer, ProfileServiceConnections> mProfileServices = new HashMap<>();
 
-    private final boolean mPermissionReviewRequired;
+    private final boolean mWirelessConsentRequired;
 
     private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
         @Override
@@ -364,8 +368,8 @@
 
         mContext = context;
 
-        mPermissionReviewRequired = context.getResources()
-                .getBoolean(com.android.internal.R.bool.config_permissionReviewRequired);
+        mWirelessConsentRequired = context.getResources()
+                .getBoolean(com.android.internal.R.bool.config_wirelessConsentRequired);
 
         mCrashes = 0;
         mBluetooth = null;
@@ -386,6 +390,15 @@
         mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
         mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
 
+        // TODO: We need a more generic way to initialize the persist keys of FeatureFlagUtils
+        boolean isHearingAidEnabled;
+        String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.HEARING_AID_SETTINGS);
+        if (!TextUtils.isEmpty(value)) {
+            isHearingAidEnabled = Boolean.parseBoolean(value);
+            Log.v(TAG, "set feature flag HEARING_AID_SETTINGS to " + isHearingAidEnabled);
+            FeatureFlagUtils.setEnabled(context, FeatureFlagUtils.HEARING_AID_SETTINGS, isHearingAidEnabled);
+        }
+
         IntentFilter filter = new IntentFilter();
         filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
         filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
@@ -872,7 +885,7 @@
             mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                     "Need BLUETOOTH ADMIN permission");
 
-            if (!isEnabled() && mPermissionReviewRequired && startConsentUiIfNeeded(packageName,
+            if (!isEnabled() && mWirelessConsentRequired && startConsentUiIfNeeded(packageName,
                     callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
                 return false;
             }
@@ -909,7 +922,7 @@
             mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                     "Need BLUETOOTH ADMIN permission");
 
-            if (isEnabled() && mPermissionReviewRequired && startConsentUiIfNeeded(packageName,
+            if (isEnabled() && mWirelessConsentRequired && startConsentUiIfNeeded(packageName,
                     callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) {
                 return false;
             }
@@ -932,7 +945,7 @@
 
     private boolean startConsentUiIfNeeded(String packageName,
             int callingUid, String intentAction) throws RemoteException {
-        if (checkBluetoothPermissionWhenPermissionReviewRequired()) {
+        if (checkBluetoothPermissionWhenWirelessConsentRequired()) {
             return false;
         }
         try {
@@ -965,21 +978,18 @@
 
     /**
      * Check if the caller must still pass permission check or if the caller is exempted
-     * from the consent UI via the MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED check.
+     * from the consent UI via the MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED check.
      *
      * Commands from some callers may be exempted from triggering the consent UI when
      * enabling bluetooth. This exemption is checked via the
-     * MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED and allows calls to skip
+     * MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED and allows calls to skip
      * the consent UI where it may otherwise be required.
      *
      * @hide
      */
-    private boolean checkBluetoothPermissionWhenPermissionReviewRequired() {
-        if (!mPermissionReviewRequired) {
-            return false;
-        }
+    private boolean checkBluetoothPermissionWhenWirelessConsentRequired() {
         int result = mContext.checkCallingPermission(
-                android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED);
+                android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED);
         return result == PackageManager.PERMISSION_GRANTED;
     }
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2bd6ebc..6b3f8f8 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -155,6 +155,7 @@
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
 import com.android.server.connectivity.PacManager;
 import com.android.server.connectivity.PermissionMonitor;
+import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.Tethering;
 import com.android.server.connectivity.Vpn;
 import com.android.server.connectivity.tethering.TetheringDependencies;
@@ -429,15 +430,9 @@
     private int mNetTransitionWakeLockTimeout;
     private final PowerManager.WakeLock mPendingIntentWakeLock;
 
-    // track the current default http proxy - tell the world if we get a new one (real change)
-    private volatile ProxyInfo mDefaultProxy = null;
-    private final Object mProxyLock = new Object();
-    private boolean mDefaultProxyDisabled = false;
-
-    // track the global proxy.
-    private ProxyInfo mGlobalProxy = null;
-
-    private PacManager mPacManager = null;
+    // A helper object to track the current default HTTP proxy. ConnectivityService needs to tell
+    // the world when it changes.
+    private final ProxyTracker mProxyTracker;
 
     final private SettingsObserver mSettingsObserver;
 
@@ -505,6 +500,9 @@
 
     private final IpConnectivityLog mMetricsLog;
 
+    @GuardedBy("mBandwidthRequests")
+    private final SparseArray<Integer> mBandwidthRequests = new SparseArray(10);
+
     @VisibleForTesting
     final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
 
@@ -761,6 +759,7 @@
         mPolicyManagerInternal = checkNotNull(
                 LocalServices.getService(NetworkPolicyManagerInternal.class),
                 "missing NetworkPolicyManagerInternal");
+        mProxyTracker = new ProxyTracker(context, mHandler, EVENT_PROXY_HAS_CHANGED);
 
         mKeyStore = KeyStore.getInstance();
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -871,8 +870,6 @@
         final DataConnectionStats dataConnectionStats = new DataConnectionStats(mContext);
         dataConnectionStats.startMonitoring();
 
-        mPacManager = new PacManager(mContext, mHandler, EVENT_PROXY_HAS_CHANGED);
-
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
 
         mKeepaliveTracker = new KeepaliveTracker(mHandler);
@@ -904,6 +901,10 @@
             public boolean isTetheringSupported() {
                 return ConnectivityService.this.isTetheringSupported();
             }
+            @Override
+            public NetworkRequest getDefaultNetworkRequest() {
+                return mDefaultRequest;
+            }
         };
         return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
                 IoThread.get().getLooper(), new MockableSystemProperties(),
@@ -921,7 +922,7 @@
 
     private NetworkRequest createDefaultInternetRequestForTransport(
             int transportType, NetworkRequest.Type type) {
-        NetworkCapabilities netCap = new NetworkCapabilities();
+        final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
         netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
         if (transportType > -1) {
@@ -1001,7 +1002,7 @@
         throw new IllegalStateException("No free netIds");
     }
 
-    private NetworkState getFilteredNetworkState(int networkType, int uid, boolean ignoreBlocked) {
+    private NetworkState getFilteredNetworkState(int networkType, int uid) {
         if (mLegacyTypeTracker.isTypeSupported(networkType)) {
             final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
             final NetworkState state;
@@ -1019,14 +1020,15 @@
                 state = new NetworkState(info, new LinkProperties(), capabilities,
                         null, null, null);
             }
-            filterNetworkStateForUid(state, uid, ignoreBlocked);
+            filterNetworkStateForUid(state, uid, false);
             return state;
         } else {
             return NetworkState.EMPTY;
         }
     }
 
-    private NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
+    @VisibleForTesting
+    protected NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
         if (network == null) {
             return null;
         }
@@ -1224,7 +1226,7 @@
                 return state.networkInfo;
             }
         }
-        final NetworkState state = getFilteredNetworkState(networkType, uid, false);
+        final NetworkState state = getFilteredNetworkState(networkType, uid);
         return state.networkInfo;
     }
 
@@ -1259,7 +1261,7 @@
     public Network getNetworkForType(int networkType) {
         enforceAccessPermission();
         final int uid = Binder.getCallingUid();
-        NetworkState state = getFilteredNetworkState(networkType, uid, false);
+        NetworkState state = getFilteredNetworkState(networkType, uid);
         if (!isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, false)) {
             return state.network;
         }
@@ -2108,6 +2110,18 @@
                 pw.println("currently holding WakeLock for: " + (duration / 1000) + "s");
             }
             mWakelockLogs.reverseDump(fd, pw, args);
+
+            pw.println();
+            pw.println("bandwidth update requests (by uid):");
+            pw.increaseIndent();
+            synchronized (mBandwidthRequests) {
+                for (int i = 0; i < mBandwidthRequests.size(); i++) {
+                    pw.println("[" + mBandwidthRequests.keyAt(i)
+                            + "]: " + mBandwidthRequests.valueAt(i));
+                }
+            }
+            pw.decreaseIndent();
+
             pw.decreaseIndent();
         }
     }
@@ -2230,6 +2244,7 @@
                         updateCapabilities(oldScore, nai, nai.networkCapabilities);
                         // If score has changed, rebroadcast to NetworkFactories. b/17726566
                         if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
+                        if (valid) handleFreshlyValidatedNetwork(nai);
                     }
                     updateInetCondition(nai);
                     // Let the NetworkAgent know the state of its network
@@ -2324,6 +2339,16 @@
                 mDefaultRequest.networkCapabilities, nai.networkCapabilities);
     }
 
+    private void handleFreshlyValidatedNetwork(NetworkAgentInfo nai) {
+        if (nai == null) return;
+        // If the Private DNS mode is opportunistic, reprogram the DNS servers
+        // in order to restart a validation pass from within netd.
+        final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
+        if (cfg.useTls && TextUtils.isEmpty(cfg.hostname)) {
+            updateDnses(nai.linkProperties, null, nai.network.netId);
+        }
+    }
+
     private void handlePrivateDnsSettingsChanged() {
         final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
 
@@ -3325,22 +3350,10 @@
         nai.networkMonitor.forceReevaluation(uid);
     }
 
-    private ProxyInfo getDefaultProxy() {
-        // this information is already available as a world read/writable jvm property
-        // so this API change wouldn't have a benefit.  It also breaks the passing
-        // of proxy info to all the JVMs.
-        // enforceAccessPermission();
-        synchronized (mProxyLock) {
-            ProxyInfo ret = mGlobalProxy;
-            if ((ret == null) && !mDefaultProxyDisabled) ret = mDefaultProxy;
-            return ret;
-        }
-    }
-
     @Override
     public ProxyInfo getProxyForNetwork(Network network) {
-        if (network == null) return getDefaultProxy();
-        final ProxyInfo globalProxy = getGlobalProxy();
+        if (network == null) return mProxyTracker.getDefaultProxy();
+        final ProxyInfo globalProxy = mProxyTracker.getGlobalProxy();
         if (globalProxy != null) return globalProxy;
         if (!NetworkUtils.queryUserAccess(Binder.getCallingUid(), network.netId)) return null;
         // Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
@@ -3354,80 +3367,10 @@
         }
     }
 
-    // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
-    // (e.g. if mGlobalProxy==null fall back to network-specific proxy, if network-specific
-    // proxy is null then there is no proxy in place).
-    private ProxyInfo canonicalizeProxyInfo(ProxyInfo proxy) {
-        if (proxy != null && TextUtils.isEmpty(proxy.getHost())
-                && (proxy.getPacFileUrl() == null || Uri.EMPTY.equals(proxy.getPacFileUrl()))) {
-            proxy = null;
-        }
-        return proxy;
-    }
-
-    // ProxyInfo equality function with a couple modifications over ProxyInfo.equals() to make it
-    // better for determining if a new proxy broadcast is necessary:
-    // 1. Canonicalize empty ProxyInfos to null so an empty proxy compares equal to null so as to
-    //    avoid unnecessary broadcasts.
-    // 2. Make sure all parts of the ProxyInfo's compare true, including the host when a PAC URL
-    //    is in place.  This is important so legacy PAC resolver (see com.android.proxyhandler)
-    //    changes aren't missed.  The legacy PAC resolver pretends to be a simple HTTP proxy but
-    //    actually uses the PAC to resolve; this results in ProxyInfo's with PAC URL, host and port
-    //    all set.
-    private boolean proxyInfoEqual(ProxyInfo a, ProxyInfo b) {
-        a = canonicalizeProxyInfo(a);
-        b = canonicalizeProxyInfo(b);
-        // ProxyInfo.equals() doesn't check hosts when PAC URLs are present, but we need to check
-        // hosts even when PAC URLs are present to account for the legacy PAC resolver.
-        return Objects.equals(a, b) && (a == null || Objects.equals(a.getHost(), b.getHost()));
-    }
-
-    public void setGlobalProxy(ProxyInfo proxyProperties) {
+    @Override
+    public void setGlobalProxy(final ProxyInfo proxyProperties) {
         enforceConnectivityInternalPermission();
-
-        synchronized (mProxyLock) {
-            if (proxyProperties == mGlobalProxy) return;
-            if (proxyProperties != null && proxyProperties.equals(mGlobalProxy)) return;
-            if (mGlobalProxy != null && mGlobalProxy.equals(proxyProperties)) return;
-
-            String host = "";
-            int port = 0;
-            String exclList = "";
-            String pacFileUrl = "";
-            if (proxyProperties != null && (!TextUtils.isEmpty(proxyProperties.getHost()) ||
-                    !Uri.EMPTY.equals(proxyProperties.getPacFileUrl()))) {
-                if (!proxyProperties.isValid()) {
-                    if (DBG)
-                        log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
-                    return;
-                }
-                mGlobalProxy = new ProxyInfo(proxyProperties);
-                host = mGlobalProxy.getHost();
-                port = mGlobalProxy.getPort();
-                exclList = mGlobalProxy.getExclusionListAsString();
-                if (!Uri.EMPTY.equals(proxyProperties.getPacFileUrl())) {
-                    pacFileUrl = proxyProperties.getPacFileUrl().toString();
-                }
-            } else {
-                mGlobalProxy = null;
-            }
-            ContentResolver res = mContext.getContentResolver();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, host);
-                Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, port);
-                Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
-                        exclList);
-                Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-
-            if (mGlobalProxy == null) {
-                proxyProperties = mDefaultProxy;
-            }
-            sendProxyBroadcast(proxyProperties);
-        }
+        mProxyTracker.setGlobalProxy(proxyProperties);
     }
 
     private void loadGlobalProxy() {
@@ -3449,20 +3392,16 @@
                 return;
             }
 
-            synchronized (mProxyLock) {
-                mGlobalProxy = proxyProperties;
+            synchronized (mProxyTracker.mProxyLock) {
+                mProxyTracker.mGlobalProxy = proxyProperties;
             }
         }
     }
 
+    @Override
+    @Nullable
     public ProxyInfo getGlobalProxy() {
-        // this information is already available as a world read/writable jvm property
-        // so this API change wouldn't have a benefit.  It also breaks the passing
-        // of proxy info to all the JVMs.
-        // enforceAccessPermission();
-        synchronized (mProxyLock) {
-            return mGlobalProxy;
-        }
+        return mProxyTracker.getGlobalProxy();
     }
 
     private void handleApplyDefaultProxy(ProxyInfo proxy) {
@@ -3470,33 +3409,7 @@
                 && Uri.EMPTY.equals(proxy.getPacFileUrl())) {
             proxy = null;
         }
-        synchronized (mProxyLock) {
-            if (mDefaultProxy != null && mDefaultProxy.equals(proxy)) return;
-            if (mDefaultProxy == proxy) return; // catches repeated nulls
-            if (proxy != null &&  !proxy.isValid()) {
-                if (DBG) log("Invalid proxy properties, ignoring: " + proxy.toString());
-                return;
-            }
-
-            // This call could be coming from the PacManager, containing the port of the local
-            // proxy.  If this new proxy matches the global proxy then copy this proxy to the
-            // global (to get the correct local port), and send a broadcast.
-            // TODO: Switch PacManager to have its own message to send back rather than
-            // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
-            if ((mGlobalProxy != null) && (proxy != null)
-                    && (!Uri.EMPTY.equals(proxy.getPacFileUrl()))
-                    && proxy.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) {
-                mGlobalProxy = proxy;
-                sendProxyBroadcast(mGlobalProxy);
-                return;
-            }
-            mDefaultProxy = proxy;
-
-            if (mGlobalProxy != null) return;
-            if (!mDefaultProxyDisabled) {
-                sendProxyBroadcast(proxy);
-            }
-        }
+        mProxyTracker.setDefaultProxy(proxy);
     }
 
     // If the proxy has changed from oldLp to newLp, resend proxy broadcast with default proxy.
@@ -3508,8 +3421,8 @@
         ProxyInfo newProxyInfo = newLp == null ? null : newLp.getHttpProxy();
         ProxyInfo oldProxyInfo = oldLp == null ? null : oldLp.getHttpProxy();
 
-        if (!proxyInfoEqual(newProxyInfo, oldProxyInfo)) {
-            sendProxyBroadcast(getDefaultProxy());
+        if (!ProxyTracker.proxyInfoEqual(newProxyInfo, oldProxyInfo)) {
+            mProxyTracker.sendProxyBroadcast(mProxyTracker.getDefaultProxy());
         }
     }
 
@@ -3536,22 +3449,6 @@
         }
     }
 
-    private void sendProxyBroadcast(ProxyInfo proxy) {
-        if (proxy == null) proxy = new ProxyInfo("", 0, "");
-        if (mPacManager.setCurrentProxyScriptUrl(proxy)) return;
-        if (DBG) log("sending Proxy Broadcast for " + proxy);
-        Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
-            Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
     private static class SettingsObserver extends ContentObserver {
         final private HashMap<Uri, Integer> mUriEventMap;
         final private Context mContext;
@@ -4385,6 +4282,14 @@
         }
         if (nai != null) {
             nai.asyncChannel.sendMessage(android.net.NetworkAgent.CMD_REQUEST_BANDWIDTH_UPDATE);
+            synchronized (mBandwidthRequests) {
+                final int uid = Binder.getCallingUid();
+                Integer uidReqs = mBandwidthRequests.get(uid);
+                if (uidReqs == null) {
+                    uidReqs = new Integer(0);
+                }
+                mBandwidthRequests.put(uid, ++uidReqs);
+            }
             return true;
         }
         return false;
@@ -5570,11 +5475,12 @@
 
             if (networkAgent.isVPN()) {
                 // Temporarily disable the default proxy (not global).
-                synchronized (mProxyLock) {
-                    if (!mDefaultProxyDisabled) {
-                        mDefaultProxyDisabled = true;
-                        if (mGlobalProxy == null && mDefaultProxy != null) {
-                            sendProxyBroadcast(null);
+                synchronized (mProxyTracker.mProxyLock) {
+                    if (!mProxyTracker.mDefaultProxyDisabled) {
+                        mProxyTracker.mDefaultProxyDisabled = true;
+                        if (mProxyTracker.mGlobalProxy == null
+                                && mProxyTracker.mDefaultProxy != null) {
+                            mProxyTracker.sendProxyBroadcast(null);
                         }
                     }
                 }
@@ -5599,11 +5505,12 @@
         } else if (state == NetworkInfo.State.DISCONNECTED) {
             networkAgent.asyncChannel.disconnect();
             if (networkAgent.isVPN()) {
-                synchronized (mProxyLock) {
-                    if (mDefaultProxyDisabled) {
-                        mDefaultProxyDisabled = false;
-                        if (mGlobalProxy == null && mDefaultProxy != null) {
-                            sendProxyBroadcast(mDefaultProxy);
+                synchronized (mProxyTracker.mProxyLock) {
+                    if (mProxyTracker.mDefaultProxyDisabled) {
+                        mProxyTracker.mDefaultProxyDisabled = false;
+                        if (mProxyTracker.mGlobalProxy == null
+                                && mProxyTracker.mDefaultProxy != null) {
+                            mProxyTracker.sendProxyBroadcast(mProxyTracker.mDefaultProxy);
                         }
                     }
                 }
@@ -5979,4 +5886,4 @@
             pw.println("    Get airplane mode.");
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index b8f395b..a34c2b9 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -30,15 +30,15 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.ContentObserver;
 import android.hardware.Sensor;
-import android.hardware.SensorManager;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
 import android.hardware.TriggerEvent;
 import android.hardware.TriggerEventListener;
-import android.location.LocationRequest;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
+import android.location.LocationRequest;
 import android.net.ConnectivityManager;
 import android.net.INetworkPolicyManager;
 import android.net.NetworkInfo;
@@ -82,10 +82,10 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -118,6 +118,7 @@
     private AlarmManager mAlarmManager;
     private IBatteryStats mBatteryStats;
     private ActivityManagerInternal mLocalActivityManager;
+    private ActivityTaskManagerInternal mLocalActivityTaskManager;
     private PowerManagerInternal mLocalPowerManager;
     private PowerManager mPowerManager;
     private ConnectivityService mConnectivityService;
@@ -1359,8 +1360,8 @@
         }
     }
 
-    private ActivityManagerInternal.ScreenObserver mScreenObserver =
-            new ActivityManagerInternal.ScreenObserver() {
+    private ActivityTaskManagerInternal.ScreenObserver mScreenObserver =
+            new ActivityTaskManagerInternal.ScreenObserver() {
                 @Override
                 public void onAwakeStateChanged(boolean isAwake) { }
 
@@ -1461,6 +1462,7 @@
                 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
                 mBatteryStats = BatteryStatsService.getService();
                 mLocalActivityManager = getLocalService(ActivityManagerInternal.class);
+                mLocalActivityTaskManager = getLocalService(ActivityTaskManagerInternal.class);
                 mLocalPowerManager = getLocalService(PowerManagerInternal.class);
                 mPowerManager = getContext().getSystemService(PowerManager.class);
                 mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
@@ -1539,7 +1541,7 @@
                         mPowerSaveWhitelistAllAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray);
                 mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
 
-                mLocalActivityManager.registerScreenObserver(mScreenObserver);
+                mLocalActivityTaskManager.registerScreenObserver(mScreenObserver);
 
                 passWhiteListsToForceAppStandbyTrackerLocked();
                 updateInteractivityLocked();
diff --git a/services/core/java/com/android/server/InputContentUriTokenHandler.java b/services/core/java/com/android/server/InputContentUriTokenHandler.java
index 57cdc94..6338b2f 100644
--- a/services/core/java/com/android/server/InputContentUriTokenHandler.java
+++ b/services/core/java/com/android/server/InputContentUriTokenHandler.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.UriGrantsManager;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Binder;
@@ -27,6 +28,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 final class InputContentUriTokenHandler extends IInputContentUriToken.Stub {
 
@@ -63,12 +65,8 @@
                 return;
             }
 
-            try {
-                mPermissionOwnerToken = ActivityManager.getService()
-                        .newUriPermissionOwner("InputContentUriTokenHandler");
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            }
+            mPermissionOwnerToken = LocalServices.getService(UriGrantsManagerInternal.class)
+                    .newUriPermissionOwner("InputContentUriTokenHandler");
 
             doTakeLocked(mPermissionOwnerToken);
         }
@@ -78,7 +76,7 @@
         long origId = Binder.clearCallingIdentity();
         try {
             try {
-                ActivityManager.getService().grantUriPermissionFromOwner(
+                UriGrantsManager.getService().grantUriPermissionFromOwner(
                         permissionOwner, mSourceUid, mTargetPackage, mUri,
                         Intent.FLAG_GRANT_READ_URI_PERMISSION, mSourceUserId, mTargetUserId);
             } catch (RemoteException e) {
@@ -96,11 +94,9 @@
                 return;
             }
             try {
-                ActivityManager.getService().revokeUriPermissionFromOwner(
-                        mPermissionOwnerToken, mUri,
-                        Intent.FLAG_GRANT_READ_URI_PERMISSION, mSourceUserId);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
+                LocalServices.getService(UriGrantsManagerInternal.class)
+                        .revokeUriPermissionFromOwner(mPermissionOwnerToken, mUri,
+                                Intent.FLAG_GRANT_READ_URI_PERMISSION, mSourceUserId);
             } finally {
                 mPermissionOwnerToken = null;
             }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index eedafae..784dfb4 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -51,6 +51,7 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.Manifest;
+import android.accessibilityservice.AccessibilityService;
 import android.annotation.AnyThread;
 import android.annotation.BinderThread;
 import android.annotation.ColorInt;
@@ -324,6 +325,15 @@
 
     // Used to bring IME service up to visible adjustment while it is being shown.
     final ServiceConnection mVisibleConnection = new ServiceConnection() {
+        @Override public void onBindingDied(ComponentName name) {
+            synchronized (mMethodMap) {
+                if (mVisibleBound) {
+                    mContext.unbindService(mVisibleConnection);
+                    mVisibleBound = false;
+                }
+            }
+        }
+
         @Override public void onServiceConnected(ComponentName name, IBinder service) {
         }
 
@@ -459,6 +469,13 @@
     IBinder mCurFocusedWindow;
 
     /**
+     * The last window token that we confirmed that IME started talking to.  This is always updated
+     * upon reports from the input method.  If the window state is already changed before the report
+     * is handled, this field just keeps the last value.
+     */
+    IBinder mLastImeTargetWindow;
+
+    /**
      * {@link WindowManager.LayoutParams#softInputMode} of {@link #mCurFocusedWindow}.
      *
      * @see #mCurFocusedWindow
@@ -679,7 +696,7 @@
     }
 
     @GuardedBy("mMethodMap")
-    private final WeakHashMap<IBinder, StartInputInfo> mStartInputMap = new WeakHashMap<>();
+    private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
 
     /**
      * A ring buffer to store the history of {@link StartInputInfo}.
@@ -872,10 +889,12 @@
                 if (showImeUri.equals(uri)) {
                     updateKeyboardFromSettingsLocked();
                 } else if (accessibilityRequestingNoImeUri.equals(uri)) {
-                    mAccessibilityRequestingNoSoftKeyboard = Settings.Secure.getIntForUser(
+                    final int accessibilitySoftKeyboardSetting = Settings.Secure.getIntForUser(
                             mContext.getContentResolver(),
-                            Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                            0, mUserId) == 1;
+                            Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, mUserId);
+                    mAccessibilityRequestingNoSoftKeyboard =
+                            (accessibilitySoftKeyboardSetting & AccessibilityService.SHOW_MODE_MASK)
+                                    == AccessibilityService.SHOW_MODE_HIDDEN;
                     if (mAccessibilityRequestingNoSoftKeyboard) {
                         final boolean showRequested = mShowRequested;
                         hideCurrentInputLocked(0, null);
@@ -1800,7 +1819,7 @@
         final StartInputInfo info = new StartInputInfo(mCurToken, mCurId, startInputReason,
                 !initial, mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
                 mCurSeq);
-        mStartInputMap.put(startInputToken, info);
+        mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
         mStartInputHistory.addEntry(info);
 
         final SessionState session = mCurClient.curSession;
@@ -1818,50 +1837,6 @@
 
     @GuardedBy("mMethodMap")
     @NonNull
-    InputBindResult startInputLocked(
-            /* @InputMethodClient.StartInputReason */ final int startInputReason,
-            IInputMethodClient client, IInputContext inputContext,
-            /* @InputConnectionInspector.missingMethods */ final int missingMethods,
-            @Nullable EditorInfo attribute, int controlFlags) {
-        // If no method is currently selected, do nothing.
-        if (mCurMethodId == null) {
-            return InputBindResult.NO_IME;
-        }
-
-        ClientState cs = mClients.get(client.asBinder());
-        if (cs == null) {
-            throw new IllegalArgumentException("unknown client "
-                    + client.asBinder());
-        }
-
-        if (attribute == null) {
-            Slog.w(TAG, "Ignoring startInput with null EditorInfo."
-                    + " uid=" + cs.uid + " pid=" + cs.pid);
-            return InputBindResult.NULL_EDITOR_INFO;
-        }
-
-        try {
-            if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) {
-                // Check with the window manager to make sure this client actually
-                // has a window with focus.  If not, reject.  This is thread safe
-                // because if the focus changes some time before or after, the
-                // next client receiving focus that has any interest in input will
-                // be calling through here after that change happens.
-                if (DEBUG) {
-                    Slog.w(TAG, "Starting input on non-focused client " + cs.client
-                            + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
-                }
-                return InputBindResult.NOT_IME_TARGET_WINDOW;
-            }
-        } catch (RemoteException e) {
-        }
-
-        return startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
-                controlFlags, startInputReason);
-    }
-
-    @GuardedBy("mMethodMap")
-    @NonNull
     InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
             /* @InputConnectionInspector.missingMethods */ final int missingMethods,
             @NonNull EditorInfo attribute, int controlFlags,
@@ -1941,6 +1916,7 @@
         return startInputInnerLocked();
     }
 
+    @GuardedBy("mMethodMap")
     InputBindResult startInputInnerLocked() {
         if (mCurMethodId == null) {
             return InputBindResult.NO_IME;
@@ -1988,36 +1964,6 @@
         return InputBindResult.IME_NOT_CONNECTED;
     }
 
-    @NonNull
-    private InputBindResult startInput(
-            /* @InputMethodClient.StartInputReason */ final int startInputReason,
-            IInputMethodClient client, IInputContext inputContext,
-            /* @InputConnectionInspector.missingMethods */ final int missingMethods,
-            @Nullable EditorInfo attribute, int controlFlags) {
-        if (!calledFromValidUser()) {
-            return InputBindResult.INVALID_USER;
-        }
-        synchronized (mMethodMap) {
-            if (DEBUG) {
-                Slog.v(TAG, "startInput: reason="
-                        + InputMethodClient.getStartInputReason(startInputReason)
-                        + " client = " + client.asBinder()
-                        + " inputContext=" + inputContext
-                        + " missingMethods="
-                        + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
-                        + " attribute=" + attribute
-                        + " controlFlags=#" + Integer.toHexString(controlFlags));
-            }
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                return startInputLocked(startInputReason, client, inputContext, missingMethods,
-                        attribute, controlFlags);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
     @Override
     public void finishInput(IInputMethodClient client) {
     }
@@ -2284,15 +2230,12 @@
     @BinderThread
     @SuppressWarnings("deprecation")
     @Override
-    public void setImeWindowStatus(IBinder token, IBinder startInputToken, int vis,
-            int backDisposition) {
+    public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
         if (!calledWithValidToken(token)) {
             return;
         }
 
-        final StartInputInfo info;
         synchronized (mMethodMap) {
-            info = mStartInputMap.get(startInputToken);
             mImeWindowVis = vis;
             mBackDisposition = backDisposition;
             updateSystemUiLocked(token, vis, backDisposition);
@@ -2312,8 +2255,7 @@
                 break;
         }
         mWindowManagerInternal.updateInputMethodWindowStatus(token,
-                (vis & InputMethodService.IME_VISIBLE) != 0,
-                dismissImeOnBackKeyPressed, info != null ? info.mTargetWindow : null);
+                (vis & InputMethodService.IME_VISIBLE) != 0, dismissImeOnBackKeyPressed);
     }
 
     private void updateSystemUi(IBinder token, int vis, int backDisposition) {
@@ -2322,6 +2264,22 @@
         }
     }
 
+    @BinderThread
+    @Override
+    public void reportStartInput(IBinder token, IBinder startInputToken) {
+        if (!calledWithValidToken(token)) {
+            return;
+        }
+
+        synchronized (mMethodMap) {
+            final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
+            if (targetWindow != null && mLastImeTargetWindow != targetWindow) {
+                mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
+            }
+            mLastImeTargetWindow = targetWindow;
+        }
+    }
+
     // Caution! This method is called in this class. Handle multi-user carefully
     private void updateSystemUiLocked(IBinder token, int vis, int backDisposition) {
         if (!calledWithValidToken(token)) {
@@ -2595,6 +2553,7 @@
         }
     }
 
+    @GuardedBy("mMethodMap")
     boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
         mShowRequested = true;
         if (mAccessibilityRequestingNoSoftKeyboard) {
@@ -2732,15 +2691,13 @@
             int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
             /* @InputConnectionInspector.missingMethods */ final int missingMethods,
             int unverifiedTargetSdkVersion) {
-        final InputBindResult result;
-        if (windowToken != null) {
-            result = windowGainedFocus(startInputReason, client, windowToken, controlFlags,
-                    softInputMode, windowFlags, attribute, inputContext, missingMethods,
-                    unverifiedTargetSdkVersion);
-        } else {
-            result = startInput(startInputReason, client, inputContext, missingMethods, attribute,
-                    controlFlags);
+        if (windowToken == null) {
+            Slog.e(TAG, "windowToken cannot be null.");
+            return InputBindResult.NULL;
         }
+        final InputBindResult result = startInputOrWindowGainedFocusInternal(startInputReason,
+                client, windowToken, controlFlags, softInputMode, windowFlags, attribute,
+                inputContext, missingMethods, unverifiedTargetSdkVersion);
         if (result == null) {
             // This must never happen, but just in case.
             Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
@@ -2753,9 +2710,9 @@
     }
 
     @NonNull
-    private InputBindResult windowGainedFocus(
+    private InputBindResult startInputOrWindowGainedFocusInternal(
             /* @InputMethodClient.StartInputReason */ final int startInputReason,
-            IInputMethodClient client, IBinder windowToken, int controlFlags,
+            IInputMethodClient client, @NonNull IBinder windowToken, int controlFlags,
             /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
             int windowFlags, EditorInfo attribute, IInputContext inputContext,
             /* @InputConnectionInspector.missingMethods */  final int missingMethods,
@@ -2766,7 +2723,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mMethodMap) {
-                if (DEBUG) Slog.v(TAG, "windowGainedFocus: reason="
+                if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason="
                         + InputMethodClient.getStartInputReason(startInputReason)
                         + " client=" + client.asBinder()
                         + " inputContext=" + inputContext
@@ -4587,8 +4544,8 @@
                     InputMethodClient.softInputModeToString(mCurFocusedWindowSoftInputMode)
                     + " client=" + mCurFocusedWindowClient);
             focusedWindowClient = mCurFocusedWindowClient;
-            p.println("  mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection
-                    + " mBoundToMethod=" + mBoundToMethod);
+            p.println("  mCurId=" + mCurId + " mHaveConnection=" + mHaveConnection
+                    + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" + mVisibleBound);
             p.println("  mCurToken=" + mCurToken);
             p.println("  mCurIntent=" + mCurIntent);
             method = mCurMethod;
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index c5ab932..232c151 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -153,6 +153,10 @@
     // default background throttling interval if not overriden in settings
     private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
 
+    // Default value for maximum age of last location returned to applications with foreground-only
+    // location permissions.
+    private static final long DEFAULT_LAST_LOCATION_MAX_AGE_MS = 20 * 60 * 1000;
+
     // Location Providers may sometimes deliver location updates
     // slightly faster that requested - provide grace period so
     // we don't unnecessarily filter events that are otherwise on
@@ -241,6 +245,9 @@
     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;
@@ -308,7 +315,8 @@
                     }
                 }
             };
-            mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null, callback);
+            mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
+                    AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
 
             PackageManager.OnPermissionsChangedListener permissionListener
                     = new PackageManager.OnPermissionsChangedListener() {
@@ -341,6 +349,7 @@
             updateUserProfiles(mCurrentUserId);
 
             updateBackgroundThrottlingWhitelistLocked();
+            updateLastLocationMaxAgeLocked();
 
             // prepare providers
             loadProvidersLocked();
@@ -370,6 +379,18 @@
                     }
                 }, 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,
@@ -382,6 +403,7 @@
                         }
                     }
                 }, UserHandle.USER_ALL);
+
         mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
 
         // listen for user change
@@ -773,6 +795,7 @@
      * location updates.
      */
     private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
+        private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
         final Identity mIdentity;
         final int mAllowedResolutionLevel;  // resolution level allowed to receiver
 
@@ -816,6 +839,10 @@
                 workSource = new WorkSource(mIdentity.mUid, mIdentity.mPackageName);
             }
             mWakeLock.setWorkSource(workSource);
+
+            // For a non-reference counted wakelock, each acquire will reset the timeout, and we
+            // only need to release it once.
+            mWakeLock.setReferenceCounted(false);
         }
 
         @Override
@@ -841,6 +868,7 @@
             for (String p : mUpdateRecords.keySet()) {
                 s.append(" ").append(mUpdateRecords.get(p).toString());
             }
+            s.append(" monitoring location: ").append(mOpMonitoring);
             s.append("]");
             return s.toString();
         }
@@ -913,7 +941,7 @@
                 }
             } else {
                 if (!allowMonitoring
-                        || mAppOps.checkOpNoThrow(op, mIdentity.mUid, mIdentity.mPackageName)
+                        || mAppOps.noteOpNoThrow(op, mIdentity.mUid, mIdentity.mPackageName)
                         != AppOpsManager.MODE_ALLOWED) {
                     mAppOps.finishOp(op, mIdentity.mUid, mIdentity.mPackageName);
                     return false;
@@ -1076,9 +1104,8 @@
         // this must be called while synchronized by caller in a synchronized block
         // containing the sending of the broadcaset
         private void incrementPendingBroadcastsLocked() {
-            if (mPendingBroadcasts++ == 0) {
-                mWakeLock.acquire();
-            }
+            mPendingBroadcasts++;
+            mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
         }
 
         private void decrementPendingBroadcastsLocked() {
@@ -1526,6 +1553,22 @@
         return -1;
     }
 
+    private static String resolutionLevelToOpStr(int allowedResolutionLevel) {
+        switch(allowedResolutionLevel) {
+            case RESOLUTION_LEVEL_COARSE:
+                return AppOpsManager.OPSTR_COARSE_LOCATION;
+            case RESOLUTION_LEVEL_FINE:
+                return AppOpsManager.OPSTR_FINE_LOCATION;
+            case RESOLUTION_LEVEL_NONE:
+                // The client is not allowed to get any location, so both FINE and COARSE ops will
+                // be denied. Pick the most restrictive one to be safe.
+                return AppOpsManager.OPSTR_FINE_LOCATION;
+            default:
+                // Use the most restrictive ops if not sure.
+                return AppOpsManager.OPSTR_FINE_LOCATION;
+        }
+    }
+
     boolean reportLocationAccessNoThrow(
             int pid, int uid, String packageName, int allowedResolutionLevel) {
         int op = resolutionLevelToOp(allowedResolutionLevel);
@@ -1541,7 +1584,7 @@
     boolean checkLocationAccess(int pid, int uid, String packageName, int allowedResolutionLevel) {
         int op = resolutionLevelToOp(allowedResolutionLevel);
         if (op >= 0) {
-            if (mAppOps.checkOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+            if (mAppOps.noteOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
                 return false;
             }
         }
@@ -1857,6 +1900,14 @@
                 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) {
         if (identity.mUid == Process.SYSTEM_UID) {
             return true;
@@ -2262,6 +2313,17 @@
                 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);
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 8402087..f15cd2a 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -42,6 +42,7 @@
 import android.util.Slog;
 
 import com.android.internal.telephony.IMms;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import java.util.List;
 
@@ -512,7 +513,7 @@
 
             long token = Binder.clearCallingIdentity();
             try {
-                LocalServices.getService(ActivityManagerInternal.class)
+                LocalServices.getService(UriGrantsManagerInternal.class)
                         .grantUriPermissionFromIntent(callingUid, PHONE_PACKAGE_NAME,
                                 grantIntent, UserHandle.USER_SYSTEM);
 
@@ -523,7 +524,7 @@
                 List<String> carrierPackages = telephonyManager.getCarrierPackageNamesForIntent(
                         intent);
                 if (carrierPackages != null && carrierPackages.size() == 1) {
-                    LocalServices.getService(ActivityManagerInternal.class)
+                    LocalServices.getService(UriGrantsManagerInternal.class)
                             .grantUriPermissionFromIntent(callingUid, carrierPackages.get(0),
                                     grantIntent, UserHandle.USER_SYSTEM);
                 }
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 74d8755..abcd6ef 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1495,10 +1495,9 @@
             }
 
             try {
-                mConnector.execute("idletimer", "add", iface, Integer.toString(timeout),
-                        Integer.toString(type));
-            } catch (NativeDaemonConnectorException e) {
-                throw e.rethrowAsParcelableException();
+                mNetdService.idletimerAddInterface(iface, timeout, Integer.toString(type));
+            } catch (RemoteException | ServiceSpecificException e) {
+                throw new IllegalStateException(e);
             }
             mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, type));
 
@@ -1529,10 +1528,10 @@
             }
 
             try {
-                mConnector.execute("idletimer", "remove", iface,
-                        Integer.toString(params.timeout), Integer.toString(params.type));
-            } catch (NativeDaemonConnectorException e) {
-                throw e.rethrowAsParcelableException();
+                mNetdService.idletimerRemoveInterface(iface,
+                        params.timeout, Integer.toString(params.type));
+            } catch (RemoteException | ServiceSpecificException e) {
+                throw new IllegalStateException(e);
             }
             mActiveIdleTimers.remove(iface);
             mDaemonHandler.post(new Runnable() {
@@ -2271,6 +2270,7 @@
         return ruleName;
     }
 
+    @GuardedBy("mRulesLock")
     private @NonNull SparseIntArray getUidFirewallRulesLR(int chain) {
         switch (chain) {
             case FIREWALL_CHAIN_STANDBY:
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index b3a8fb6..1ff455ea 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -16,314 +16,15 @@
 
 package com.android.server;
 
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.NtpTrustedTime;
-import android.util.TimeUtils;
-
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.DumpUtils;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
+import android.os.IBinder;
 
 /**
- * Monitors the network time and updates the system time if it is out of sync
- * and there hasn't been any NITZ update from the carrier recently.
- * If looking up the network time fails for some reason, it tries a few times with a short
- * interval and then resets to checking on longer intervals.
- * <p>
- * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
- * available.
- * </p>
+ * An interface for NetworkTimeUpdateService implementations. Eventually part or all of this service
+ * will be subsumed into {@link com.android.server.timedetector.TimeDetectorService}. In the
+ * meantime this interface allows Android to use either the old or new implementation.
  */
-public class NetworkTimeUpdateService extends Binder {
-
-    private static final String TAG = "NetworkTimeUpdateService";
-    private static final boolean DBG = false;
-
-    private static final int EVENT_AUTO_TIME_CHANGED = 1;
-    private static final int EVENT_POLL_NETWORK_TIME = 2;
-    private static final int EVENT_NETWORK_CHANGED = 3;
-
-    private static final String ACTION_POLL =
-            "com.android.server.NetworkTimeUpdateService.action.POLL";
-
-    private static final int POLL_REQUEST = 0;
-
-    private static final long NOT_SET = -1;
-    private long mNitzTimeSetTime = NOT_SET;
-    private Network mDefaultNetwork = null;
-
-    private final Context mContext;
-    private final NtpTrustedTime mTime;
-    private final AlarmManager mAlarmManager;
-    private final ConnectivityManager mCM;
-    private final PendingIntent mPendingPollIntent;
-    private final PowerManager.WakeLock mWakeLock;
-
-    // NTP lookup is done on this thread and handler
-    private Handler mHandler;
-    private SettingsObserver mSettingsObserver;
-    private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
-
-    // Normal polling frequency
-    private final long mPollingIntervalMs;
-    // Try-again polling interval, in case the network request failed
-    private final long mPollingIntervalShorterMs;
-    // Number of times to try again
-    private final int mTryAgainTimesMax;
-    // If the time difference is greater than this threshold, then update the time.
-    private final int mTimeErrorThresholdMs;
-    // Keeps track of how many quick attempts were made to fetch NTP time.
-    // During bootup, the network may not have been up yet, or it's taking time for the
-    // connection to happen.
-    private int mTryAgainCounter;
-
-    public NetworkTimeUpdateService(Context context) {
-        mContext = context;
-        mTime = NtpTrustedTime.getInstance(context);
-        mAlarmManager = mContext.getSystemService(AlarmManager.class);
-        mCM = mContext.getSystemService(ConnectivityManager.class);
-
-        Intent pollIntent = new Intent(ACTION_POLL, null);
-        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
-
-        mPollingIntervalMs = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_ntpPollingInterval);
-        mPollingIntervalShorterMs = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_ntpPollingIntervalShorter);
-        mTryAgainTimesMax = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_ntpRetry);
-        mTimeErrorThresholdMs = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_ntpThreshold);
-
-        mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
-                PowerManager.PARTIAL_WAKE_LOCK, TAG);
-    }
+public interface NetworkTimeUpdateService extends IBinder {
 
     /** Initialize the receivers and initiate the first NTP request */
-    public void systemRunning() {
-        registerForTelephonyIntents();
-        registerForAlarms();
-
-        HandlerThread thread = new HandlerThread(TAG);
-        thread.start();
-        mHandler = new MyHandler(thread.getLooper());
-        mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
-        mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
-
-        mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
-        mSettingsObserver.observe(mContext);
-    }
-
-    private void registerForTelephonyIntents() {
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
-        mContext.registerReceiver(mNitzReceiver, intentFilter);
-    }
-
-    private void registerForAlarms() {
-        mContext.registerReceiver(
-            new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
-                }
-            }, new IntentFilter(ACTION_POLL));
-    }
-
-    private void onPollNetworkTime(int event) {
-        // If Automatic time is not set, don't bother. Similarly, if we don't
-        // have any default network, don't bother.
-        if (mDefaultNetwork == null) return;
-        mWakeLock.acquire();
-        try {
-            onPollNetworkTimeUnderWakeLock(event);
-        } finally {
-            mWakeLock.release();
-        }
-    }
-
-    private void onPollNetworkTimeUnderWakeLock(int event) {
-        // Force an NTP fix when outdated
-        if (mTime.getCacheAge() >= mPollingIntervalMs) {
-            if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
-            mTime.forceRefresh();
-        }
-
-        if (mTime.getCacheAge() < mPollingIntervalMs) {
-            // Obtained fresh fix; schedule next normal update
-            resetAlarm(mPollingIntervalMs);
-            if (isAutomaticTimeRequested()) {
-                updateSystemClock(event);
-            }
-
-        } else {
-            // No fresh fix; schedule retry
-            mTryAgainCounter++;
-            if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
-                resetAlarm(mPollingIntervalShorterMs);
-            } else {
-                // Try much later
-                mTryAgainCounter = 0;
-                resetAlarm(mPollingIntervalMs);
-            }
-        }
-    }
-
-    private long getNitzAge() {
-        if (mNitzTimeSetTime == NOT_SET) {
-            return Long.MAX_VALUE;
-        } else {
-            return SystemClock.elapsedRealtime() - mNitzTimeSetTime;
-        }
-    }
-
-    /**
-     * Consider updating system clock based on current NTP fix, if requested by
-     * user, significant enough delta, and we don't have a recent NITZ.
-     */
-    private void updateSystemClock(int event) {
-        final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
-        if (!forceUpdate) {
-            if (getNitzAge() < mPollingIntervalMs) {
-                if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
-                return;
-            }
-
-            final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
-            if (skew < mTimeErrorThresholdMs) {
-                if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
-                return;
-            }
-        }
-
-        SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
-    }
-
-    /**
-     * Cancel old alarm and starts a new one for the specified interval.
-     *
-     * @param interval when to trigger the alarm, starting from now.
-     */
-    private void resetAlarm(long interval) {
-        mAlarmManager.cancel(mPendingPollIntent);
-        long now = SystemClock.elapsedRealtime();
-        long next = now + interval;
-        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
-    }
-
-    /**
-     * Checks if the user prefers to automatically set the time.
-     */
-    private boolean isAutomaticTimeRequested() {
-        return Settings.Global.getInt(
-                mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
-    }
-
-    /** Receiver for Nitz time events */
-    private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (DBG) Log.d(TAG, "Received " + action);
-            if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
-                mNitzTimeSetTime = SystemClock.elapsedRealtime();
-            }
-        }
-    };
-
-    /** Handler to do the network accesses on */
-    private class MyHandler extends Handler {
-
-        public MyHandler(Looper l) {
-            super(l);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case EVENT_AUTO_TIME_CHANGED:
-                case EVENT_POLL_NETWORK_TIME:
-                case EVENT_NETWORK_CHANGED:
-                    onPollNetworkTime(msg.what);
-                    break;
-            }
-        }
-    }
-
-    private class NetworkTimeUpdateCallback extends NetworkCallback {
-        @Override
-        public void onAvailable(Network network) {
-            Log.d(TAG, String.format("New default network %s; checking time.", network));
-            mDefaultNetwork = network;
-            // Running on mHandler so invoke directly.
-            onPollNetworkTime(EVENT_NETWORK_CHANGED);
-        }
-
-        @Override
-        public void onLost(Network network) {
-            if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
-        }
-    }
-
-    /** Observer to watch for changes to the AUTO_TIME setting */
-    private static class SettingsObserver extends ContentObserver {
-
-        private int mMsg;
-        private Handler mHandler;
-
-        SettingsObserver(Handler handler, int msg) {
-            super(handler);
-            mHandler = handler;
-            mMsg = msg;
-        }
-
-        void observe(Context context) {
-            ContentResolver resolver = context.getContentResolver();
-            resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
-                    false, this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            mHandler.obtainMessage(mMsg).sendToTarget();
-        }
-    }
-
-    @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-        pw.print("PollingIntervalMs: ");
-        TimeUtils.formatDuration(mPollingIntervalMs, pw);
-        pw.print("\nPollingIntervalShorterMs: ");
-        TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
-        pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
-        pw.print("TimeErrorThresholdMs: ");
-        TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
-        pw.println("\nTryAgainCounter: " + mTryAgainCounter);
-        pw.println("NTP cache age: " + mTime.getCacheAge());
-        pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
-        pw.println();
-    }
+    void systemRunning();
 }
diff --git a/services/core/java/com/android/server/NewNetworkTimeUpdateService.java b/services/core/java/com/android/server/NewNetworkTimeUpdateService.java
new file mode 100644
index 0000000..d21741a
--- /dev/null
+++ b/services/core/java/com/android/server/NewNetworkTimeUpdateService.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.NtpTrustedTime;
+import android.util.TimeUtils;
+
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Monitors the network time and updates the system time if it is out of sync
+ * and there hasn't been any NITZ update from the carrier recently.
+ * If looking up the network time fails for some reason, it tries a few times with a short
+ * interval and then resets to checking on longer intervals.
+ * <p>
+ * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
+ * available.
+ * </p>
+ */
+public class NewNetworkTimeUpdateService extends Binder implements NetworkTimeUpdateService {
+
+    private static final String TAG = "NetworkTimeUpdateService";
+    private static final boolean DBG = false;
+
+    private static final int EVENT_AUTO_TIME_CHANGED = 1;
+    private static final int EVENT_POLL_NETWORK_TIME = 2;
+    private static final int EVENT_NETWORK_CHANGED = 3;
+
+    private static final String ACTION_POLL =
+            "com.android.server.NetworkTimeUpdateService.action.POLL";
+
+    private static final int POLL_REQUEST = 0;
+
+    private static final long NOT_SET = -1;
+    private long mNitzTimeSetTime = NOT_SET;
+    private Network mDefaultNetwork = null;
+
+    private final Context mContext;
+    private final NtpTrustedTime mTime;
+    private final AlarmManager mAlarmManager;
+    private final ConnectivityManager mCM;
+    private final PendingIntent mPendingPollIntent;
+    private final PowerManager.WakeLock mWakeLock;
+
+    // NTP lookup is done on this thread and handler
+    private Handler mHandler;
+    private SettingsObserver mSettingsObserver;
+    private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
+
+    // Normal polling frequency
+    private final long mPollingIntervalMs;
+    // Try-again polling interval, in case the network request failed
+    private final long mPollingIntervalShorterMs;
+    // Number of times to try again
+    private final int mTryAgainTimesMax;
+    // If the time difference is greater than this threshold, then update the time.
+    private final int mTimeErrorThresholdMs;
+    // Keeps track of how many quick attempts were made to fetch NTP time.
+    // During bootup, the network may not have been up yet, or it's taking time for the
+    // connection to happen.
+    private int mTryAgainCounter;
+
+    public NewNetworkTimeUpdateService(Context context) {
+        mContext = context;
+        mTime = NtpTrustedTime.getInstance(context);
+        mAlarmManager = mContext.getSystemService(AlarmManager.class);
+        mCM = mContext.getSystemService(ConnectivityManager.class);
+
+        Intent pollIntent = new Intent(ACTION_POLL, null);
+        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
+
+        mPollingIntervalMs = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_ntpPollingInterval);
+        mPollingIntervalShorterMs = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_ntpPollingIntervalShorter);
+        mTryAgainTimesMax = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_ntpRetry);
+        mTimeErrorThresholdMs = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_ntpThreshold);
+
+        mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
+                PowerManager.PARTIAL_WAKE_LOCK, TAG);
+    }
+
+    @Override
+    public void systemRunning() {
+        registerForTelephonyIntents();
+        registerForAlarms();
+
+        HandlerThread thread = new HandlerThread(TAG);
+        thread.start();
+        mHandler = new MyHandler(thread.getLooper());
+        mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
+        mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
+
+        mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
+        mSettingsObserver.observe(mContext);
+    }
+
+    private void registerForTelephonyIntents() {
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+        mContext.registerReceiver(mNitzReceiver, intentFilter);
+    }
+
+    private void registerForAlarms() {
+        mContext.registerReceiver(
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+                }
+            }, new IntentFilter(ACTION_POLL));
+    }
+
+    private void onPollNetworkTime(int event) {
+        // If Automatic time is not set, don't bother. Similarly, if we don't
+        // have any default network, don't bother.
+        if (mDefaultNetwork == null) return;
+        mWakeLock.acquire();
+        try {
+            onPollNetworkTimeUnderWakeLock(event);
+        } finally {
+            mWakeLock.release();
+        }
+    }
+
+    private void onPollNetworkTimeUnderWakeLock(int event) {
+        // Force an NTP fix when outdated
+        if (mTime.getCacheAge() >= mPollingIntervalMs) {
+            if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
+            mTime.forceRefresh();
+        }
+
+        if (mTime.getCacheAge() < mPollingIntervalMs) {
+            // Obtained fresh fix; schedule next normal update
+            resetAlarm(mPollingIntervalMs);
+            if (isAutomaticTimeRequested()) {
+                updateSystemClock(event);
+            }
+
+        } else {
+            // No fresh fix; schedule retry
+            mTryAgainCounter++;
+            if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
+                resetAlarm(mPollingIntervalShorterMs);
+            } else {
+                // Try much later
+                mTryAgainCounter = 0;
+                resetAlarm(mPollingIntervalMs);
+            }
+        }
+    }
+
+    private long getNitzAge() {
+        if (mNitzTimeSetTime == NOT_SET) {
+            return Long.MAX_VALUE;
+        } else {
+            return SystemClock.elapsedRealtime() - mNitzTimeSetTime;
+        }
+    }
+
+    /**
+     * Consider updating system clock based on current NTP fix, if requested by
+     * user, significant enough delta, and we don't have a recent NITZ.
+     */
+    private void updateSystemClock(int event) {
+        final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
+        if (!forceUpdate) {
+            if (getNitzAge() < mPollingIntervalMs) {
+                if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
+                return;
+            }
+
+            final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
+            if (skew < mTimeErrorThresholdMs) {
+                if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
+                return;
+            }
+        }
+
+        SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
+    }
+
+    /**
+     * Cancel old alarm and starts a new one for the specified interval.
+     *
+     * @param interval when to trigger the alarm, starting from now.
+     */
+    private void resetAlarm(long interval) {
+        mAlarmManager.cancel(mPendingPollIntent);
+        long now = SystemClock.elapsedRealtime();
+        long next = now + interval;
+        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
+    }
+
+    /**
+     * Checks if the user prefers to automatically set the time.
+     */
+    private boolean isAutomaticTimeRequested() {
+        return Settings.Global.getInt(
+                mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
+    }
+
+    /** Receiver for Nitz time events */
+    private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (DBG) Log.d(TAG, "Received " + action);
+            if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
+                mNitzTimeSetTime = SystemClock.elapsedRealtime();
+            }
+        }
+    };
+
+    /** Handler to do the network accesses on */
+    private class MyHandler extends Handler {
+
+        public MyHandler(Looper l) {
+            super(l);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_AUTO_TIME_CHANGED:
+                case EVENT_POLL_NETWORK_TIME:
+                case EVENT_NETWORK_CHANGED:
+                    onPollNetworkTime(msg.what);
+                    break;
+            }
+        }
+    }
+
+    private class NetworkTimeUpdateCallback extends NetworkCallback {
+        @Override
+        public void onAvailable(Network network) {
+            Log.d(TAG, String.format("New default network %s; checking time.", network));
+            mDefaultNetwork = network;
+            // Running on mHandler so invoke directly.
+            onPollNetworkTime(EVENT_NETWORK_CHANGED);
+        }
+
+        @Override
+        public void onLost(Network network) {
+            if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
+        }
+    }
+
+    /** Observer to watch for changes to the AUTO_TIME setting */
+    private static class SettingsObserver extends ContentObserver {
+
+        private int mMsg;
+        private Handler mHandler;
+
+        SettingsObserver(Handler handler, int msg) {
+            super(handler);
+            mHandler = handler;
+            mMsg = msg;
+        }
+
+        void observe(Context context) {
+            ContentResolver resolver = context.getContentResolver();
+            resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
+                    false, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mHandler.obtainMessage(mMsg).sendToTarget();
+        }
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+        pw.print("PollingIntervalMs: ");
+        TimeUtils.formatDuration(mPollingIntervalMs, pw);
+        pw.print("\nPollingIntervalShorterMs: ");
+        TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
+        pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
+        pw.print("TimeErrorThresholdMs: ");
+        TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
+        pw.println("\nTryAgainCounter: " + mTryAgainCounter);
+        pw.println("NTP cache age: " + mTime.getCacheAge());
+        pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
+        pw.println();
+    }
+}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 3ca3a96..4fa0c07 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,15 +1,21 @@
 # Connectivity / Networking
+per-file ConnectivityService.java=codewiz@google.com
 per-file ConnectivityService.java=ek@google.com
 per-file ConnectivityService.java=jchalard@google.com
 per-file ConnectivityService.java=lorenzo@google.com
+per-file ConnectivityService.java=reminv@google.com
 per-file ConnectivityService.java=satk@google.com
+per-file NetworkManagementService.java=codewiz@google.com
 per-file NetworkManagementService.java=ek@google.com
 per-file NetworkManagementService.java=jchalard@google.com
 per-file NetworkManagementService.java=lorenzo@google.com
+per-file NetworkManagementService.java=reminv@google.com
 per-file NetworkManagementService.java=satk@google.com
+per-file NsdService.java=codewiz@google.com
 per-file NsdService.java=ek@google.com
 per-file NsdService.java=jchalard@google.com
 per-file NsdService.java=lorenzo@google.com
+per-file NsdService.java=reminv@google.com
 per-file NsdService.java=satk@google.com
 
 # Vibrator
diff --git a/services/core/java/com/android/server/OldNetworkTimeUpdateService.java b/services/core/java/com/android/server/OldNetworkTimeUpdateService.java
new file mode 100644
index 0000000..068b83d
--- /dev/null
+++ b/services/core/java/com/android/server/OldNetworkTimeUpdateService.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.NtpTrustedTime;
+import android.util.TimeUtils;
+
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Monitors the network time and updates the system time if it is out of sync
+ * and there hasn't been any NITZ update from the carrier recently.
+ * If looking up the network time fails for some reason, it tries a few times with a short
+ * interval and then resets to checking on longer intervals.
+ * <p>
+ * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
+ * available.
+ * </p>
+ */
+public class OldNetworkTimeUpdateService extends Binder implements NetworkTimeUpdateService {
+
+    private static final String TAG = "NetworkTimeUpdateService";
+    private static final boolean DBG = false;
+
+    private static final int EVENT_AUTO_TIME_CHANGED = 1;
+    private static final int EVENT_POLL_NETWORK_TIME = 2;
+    private static final int EVENT_NETWORK_CHANGED = 3;
+
+    private static final String ACTION_POLL =
+            "com.android.server.NetworkTimeUpdateService.action.POLL";
+
+    private static final int POLL_REQUEST = 0;
+
+    private static final long NOT_SET = -1;
+    private long mNitzTimeSetTime = NOT_SET;
+    private Network mDefaultNetwork = null;
+
+    private final Context mContext;
+    private final NtpTrustedTime mTime;
+    private final AlarmManager mAlarmManager;
+    private final ConnectivityManager mCM;
+    private final PendingIntent mPendingPollIntent;
+    private final PowerManager.WakeLock mWakeLock;
+
+    // NTP lookup is done on this thread and handler
+    private Handler mHandler;
+    private SettingsObserver mSettingsObserver;
+    private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
+
+    // Normal polling frequency
+    private final long mPollingIntervalMs;
+    // Try-again polling interval, in case the network request failed
+    private final long mPollingIntervalShorterMs;
+    // Number of times to try again
+    private final int mTryAgainTimesMax;
+    // If the time difference is greater than this threshold, then update the time.
+    private final int mTimeErrorThresholdMs;
+    // Keeps track of how many quick attempts were made to fetch NTP time.
+    // During bootup, the network may not have been up yet, or it's taking time for the
+    // connection to happen.
+    private int mTryAgainCounter;
+
+    public OldNetworkTimeUpdateService(Context context) {
+        mContext = context;
+        mTime = NtpTrustedTime.getInstance(context);
+        mAlarmManager = mContext.getSystemService(AlarmManager.class);
+        mCM = mContext.getSystemService(ConnectivityManager.class);
+
+        Intent pollIntent = new Intent(ACTION_POLL, null);
+        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
+
+        mPollingIntervalMs = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_ntpPollingInterval);
+        mPollingIntervalShorterMs = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_ntpPollingIntervalShorter);
+        mTryAgainTimesMax = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_ntpRetry);
+        mTimeErrorThresholdMs = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_ntpThreshold);
+
+        mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
+                PowerManager.PARTIAL_WAKE_LOCK, TAG);
+    }
+
+    @Override
+    public void systemRunning() {
+        registerForTelephonyIntents();
+        registerForAlarms();
+
+        HandlerThread thread = new HandlerThread(TAG);
+        thread.start();
+        mHandler = new MyHandler(thread.getLooper());
+        mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
+        mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
+
+        mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
+        mSettingsObserver.observe(mContext);
+    }
+
+    private void registerForTelephonyIntents() {
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+        mContext.registerReceiver(mNitzReceiver, intentFilter);
+    }
+
+    private void registerForAlarms() {
+        mContext.registerReceiver(
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+                }
+            }, new IntentFilter(ACTION_POLL));
+    }
+
+    private void onPollNetworkTime(int event) {
+        // If Automatic time is not set, don't bother. Similarly, if we don't
+        // have any default network, don't bother.
+        if (mDefaultNetwork == null) return;
+        mWakeLock.acquire();
+        try {
+            onPollNetworkTimeUnderWakeLock(event);
+        } finally {
+            mWakeLock.release();
+        }
+    }
+
+    private void onPollNetworkTimeUnderWakeLock(int event) {
+        // Force an NTP fix when outdated
+        if (mTime.getCacheAge() >= mPollingIntervalMs) {
+            if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
+            mTime.forceRefresh();
+        }
+
+        if (mTime.getCacheAge() < mPollingIntervalMs) {
+            // Obtained fresh fix; schedule next normal update
+            resetAlarm(mPollingIntervalMs);
+            if (isAutomaticTimeRequested()) {
+                updateSystemClock(event);
+            }
+
+        } else {
+            // No fresh fix; schedule retry
+            mTryAgainCounter++;
+            if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
+                resetAlarm(mPollingIntervalShorterMs);
+            } else {
+                // Try much later
+                mTryAgainCounter = 0;
+                resetAlarm(mPollingIntervalMs);
+            }
+        }
+    }
+
+    private long getNitzAge() {
+        if (mNitzTimeSetTime == NOT_SET) {
+            return Long.MAX_VALUE;
+        } else {
+            return SystemClock.elapsedRealtime() - mNitzTimeSetTime;
+        }
+    }
+
+    /**
+     * Consider updating system clock based on current NTP fix, if requested by
+     * user, significant enough delta, and we don't have a recent NITZ.
+     */
+    private void updateSystemClock(int event) {
+        final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
+        if (!forceUpdate) {
+            if (getNitzAge() < mPollingIntervalMs) {
+                if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
+                return;
+            }
+
+            final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
+            if (skew < mTimeErrorThresholdMs) {
+                if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
+                return;
+            }
+        }
+
+        SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
+    }
+
+    /**
+     * Cancel old alarm and starts a new one for the specified interval.
+     *
+     * @param interval when to trigger the alarm, starting from now.
+     */
+    private void resetAlarm(long interval) {
+        mAlarmManager.cancel(mPendingPollIntent);
+        long now = SystemClock.elapsedRealtime();
+        long next = now + interval;
+        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
+    }
+
+    /**
+     * Checks if the user prefers to automatically set the time.
+     */
+    private boolean isAutomaticTimeRequested() {
+        return Settings.Global.getInt(
+                mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
+    }
+
+    /** Receiver for Nitz time events */
+    private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (DBG) Log.d(TAG, "Received " + action);
+            if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
+                mNitzTimeSetTime = SystemClock.elapsedRealtime();
+            }
+        }
+    };
+
+    /** Handler to do the network accesses on */
+    private class MyHandler extends Handler {
+
+        public MyHandler(Looper l) {
+            super(l);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_AUTO_TIME_CHANGED:
+                case EVENT_POLL_NETWORK_TIME:
+                case EVENT_NETWORK_CHANGED:
+                    onPollNetworkTime(msg.what);
+                    break;
+            }
+        }
+    }
+
+    private class NetworkTimeUpdateCallback extends NetworkCallback {
+        @Override
+        public void onAvailable(Network network) {
+            Log.d(TAG, String.format("New default network %s; checking time.", network));
+            mDefaultNetwork = network;
+            // Running on mHandler so invoke directly.
+            onPollNetworkTime(EVENT_NETWORK_CHANGED);
+        }
+
+        @Override
+        public void onLost(Network network) {
+            if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
+        }
+    }
+
+    /** Observer to watch for changes to the AUTO_TIME setting */
+    private static class SettingsObserver extends ContentObserver {
+
+        private int mMsg;
+        private Handler mHandler;
+
+        SettingsObserver(Handler handler, int msg) {
+            super(handler);
+            mHandler = handler;
+            mMsg = msg;
+        }
+
+        void observe(Context context) {
+            ContentResolver resolver = context.getContentResolver();
+            resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
+                    false, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mHandler.obtainMessage(mMsg).sendToTarget();
+        }
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+        pw.print("PollingIntervalMs: ");
+        TimeUtils.formatDuration(mPollingIntervalMs, pw);
+        pw.print("\nPollingIntervalShorterMs: ");
+        TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
+        pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
+        pw.print("TimeErrorThresholdMs: ");
+        TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
+        pw.println("\nTryAgainCounter: " + mTryAgainCounter);
+        pw.println("NTP cache age: " + mTime.getCacheAge());
+        pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
+        pw.println();
+    }
+}
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 5a25f48..a05a3e7 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -16,8 +16,16 @@
 
 package com.android.server;
 
+import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
+import static android.app.ActivityManager.UID_OBSERVER_GONE;
+
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.IActivityManager;
+import android.app.IUidObserver;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -26,24 +34,29 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.MediaStore;
+import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
-import android.system.StructStat;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import dalvik.system.DexFile;
 import dalvik.system.VMRuntime;
@@ -53,12 +66,13 @@
 import java.io.InputStream;
 import java.io.DataInputStream;
 import java.io.IOException;
-import java.io.EOFException;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 import java.util.ArrayList;
 
 import java.util.zip.ZipFile;
-import java.util.zip.ZipException;
 import java.util.zip.ZipEntry;
 
 /**
@@ -70,16 +84,50 @@
 public final class PinnerService extends SystemService {
     private static final boolean DEBUG = false;
     private static final String TAG = "PinnerService";
-    private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); //80MB max
+
     private static final String PIN_META_FILENAME = "pinlist.meta";
     private static final int PAGE_SIZE = (int) Os.sysconf(OsConstants._SC_PAGESIZE);
+    private static final int MATCH_FLAGS = PackageManager.MATCH_DEFAULT_ONLY
+            | PackageManager.MATCH_DIRECT_BOOT_AWARE
+            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
+    private static final int KEY_CAMERA = 0;
+    private static final int KEY_HOME = 1;
+
+    private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app.
+    private static final int MAX_HOME_PIN_SIZE = 6 * (1 << 20); // 6MB max for home app.
+
+    @IntDef({KEY_CAMERA, KEY_HOME})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AppKey {}
 
     private final Context mContext;
-    private final boolean mShouldPinCamera;
+    private final ActivityManagerInternal mAmInternal;
+    private final IActivityManager mAm;
 
-    /* These lists protected by PinnerService monitor lock */
-    private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<PinnedFile>();
-    private final ArrayList<PinnedFile> mPinnedCameraFiles = new ArrayList<PinnedFile>();
+    /** The list of the statically pinned files. */
+    @GuardedBy("this")
+    private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<>();
+
+    /** The list of the pinned apps. This is a map from {@link AppKey} to a pinned app. */
+    @GuardedBy("this")
+    private final ArrayMap<Integer, PinnedApp> mPinnedApps = new ArrayMap<>();
+
+    /**
+     * The list of the pinned apps that need to be repinned as soon as the all processes of a given
+     * uid are no longer active. Note that with background dex opt, the new dex/vdex files are only
+     * loaded into the processes once it restarts. So in case background dex opt recompiled these
+     * files, we still need to keep the old ones pinned until the processes restart.
+     * <p>
+     * This is a map from uid to {@link AppKey}
+     */
+    @GuardedBy("this")
+    private final ArrayMap<Integer, Integer> mPendingRepin = new ArrayMap<>();
+
+    /**
+     * A set of {@link AppKey} that are configured to be pinned.
+     */
+    private final ArraySet<Integer> mPinKeys = new ArraySet<>();
 
     private BinderService mBinderService;
     private PinnerHandler mPinnerHandler = null;
@@ -87,13 +135,13 @@
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-          // If this user's camera app has been updated, update pinned files accordingly.
-          if (intent.getAction() == Intent.ACTION_PACKAGE_REPLACED) {
+          // If an app has updated, update pinned files accordingly.
+          if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
                 Uri packageUri = intent.getData();
                 String packageName = packageUri.getSchemeSpecificPart();
                 ArraySet<String> updatedPackages = new ArraySet<>();
                 updatedPackages.add(packageName);
-                update(updatedPackages);
+                update(updatedPackages, true /* force */);
             }
         }
     };
@@ -102,14 +150,28 @@
         super(context);
 
         mContext = context;
-        mShouldPinCamera = context.getResources().getBoolean(
+        boolean shouldPinCamera = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_pinnerCameraApp);
+        boolean shouldPinHome = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_pinnerHomeApp);
+        if (shouldPinCamera) {
+            mPinKeys.add(KEY_CAMERA);
+        }
+        if (shouldPinHome) {
+            mPinKeys.add(KEY_HOME);
+        }
         mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
 
+        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+        mAm = ActivityManager.getService();
+
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
         filter.addDataScheme("package");
         mContext.registerReceiver(mBroadcastReceiver, filter);
+
+        registerUidListener();
+        registerUserSetupCompleteListener();
     }
 
     @Override
@@ -122,32 +184,39 @@
         publishLocalService(PinnerService.class, this);
 
         mPinnerHandler.obtainMessage(PinnerHandler.PIN_ONSTART_MSG).sendToTarget();
-        mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, UserHandle.USER_SYSTEM, 0)
-                .sendToTarget();
+        sendPinAppsMessage(UserHandle.USER_SYSTEM);
     }
 
     /**
-     * Pin camera on user switch.
-     * If more than one user is using the device
-     * each user may set a different preference for the camera app.
-     * Make sure that user's preference is pinned into memory.
+     * Repin apps on user switch.
+     * <p>
+     * If more than one user is using the device each user may set a different preference for the
+     * individual apps. Make sure that user's preference is pinned into memory.
      */
     @Override
     public void onSwitchUser(int userHandle) {
-        mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, userHandle, 0).sendToTarget();
+        sendPinAppsMessage(userHandle);
+    }
+
+    @Override
+    public void onUnlockUser(int userHandle) {
+        sendPinAppsMessage(userHandle);
     }
 
     /**
      * Update the currently pinned files.
-     * Specifically, this only updates camera pinning.
+     * Specifically, this only updates pinning for the apps that need to be pinned.
      * The other files pinned in onStart will not need to be updated.
      */
-    public void update(ArraySet<String> updatedPackages) {
-        ApplicationInfo cameraInfo = getCameraInfo(UserHandle.USER_SYSTEM);
-        if (cameraInfo != null && updatedPackages.contains(cameraInfo.packageName)) {
-            Slog.i(TAG, "Updating pinned files.");
-            mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, UserHandle.USER_SYSTEM, 0)
-                    .sendToTarget();
+    public void update(ArraySet<String> updatedPackages, boolean force) {
+        int currentUser = ActivityManager.getCurrentUser();
+        for (int i = mPinKeys.size() - 1; i >= 0; i--) {
+            int key = mPinKeys.valueAt(i);
+            ApplicationInfo info = getInfoForKey(key, currentUser);
+            if (info != null && updatedPackages.contains(info.packageName)) {
+                Slog.i(TAG, "Updating pinned files for " + info.packageName + " force=" + force);
+                sendPinAppMessage(key, currentUser, force);
+            }
         }
     }
 
@@ -175,24 +244,99 @@
     }
 
     /**
-     * Handler for camera pinning message
+     * Registers a listener to repin the home app when user setup is complete, as the home intent
+     * initially resolves to setup wizard, but once setup is complete, it will resolve to the
+     * regular home app.
      */
-    private void handlePinCamera(int userHandle) {
-        if (!mShouldPinCamera) return;
-        if (!pinCamera(userHandle)) {
-            if (DEBUG) {
-                Slog.v(TAG, "Failed to pin camera.");
+    private void registerUserSetupCompleteListener() {
+        Uri userSetupCompleteUri = Settings.Secure.getUriFor(
+                Settings.Secure.USER_SETUP_COMPLETE);
+        mContext.getContentResolver().registerContentObserver(userSetupCompleteUri,
+                false, new ContentObserver(null) {
+                    @Override
+                    public void onChange(boolean selfChange, Uri uri) {
+                        if (userSetupCompleteUri.equals(uri)) {
+                            sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(),
+                                    true /* force */);
+                        }
+                    }
+                }, UserHandle.USER_ALL);
+    }
+
+    private void registerUidListener() {
+        try {
+            mAm.registerUidObserver(new IUidObserver.Stub() {
+                @Override
+                public void onUidGone(int uid, boolean disabled) throws RemoteException {
+                    mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
+                            PinnerService::handleUidGone, PinnerService.this, uid));
+                }
+
+                @Override
+                public void onUidActive(int uid) throws RemoteException {
+                    mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
+                            PinnerService::handleUidActive, PinnerService.this, uid));
+                }
+
+                @Override
+                public void onUidIdle(int uid, boolean disabled) throws RemoteException {
+                }
+
+                @Override
+                public void onUidStateChanged(int uid, int procState, long procStateSeq)
+                        throws RemoteException {
+                }
+
+                @Override
+                public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
+                }
+            }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, "system");
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to register uid observer", e);
+        }
+    }
+
+    private void handleUidGone(int uid) {
+        updateActiveState(uid, false /* active */);
+        int key;
+        synchronized (this) {
+
+            // In case we have a pending repin, repin now. See mPendingRepin for more information.
+            key = mPendingRepin.getOrDefault(uid, -1);
+            if (key == -1) {
+                return;
+            }
+            mPendingRepin.remove(uid);
+        }
+        pinApp(key, ActivityManager.getCurrentUser(), false /* force */);
+    }
+
+    private void handleUidActive(int uid) {
+        updateActiveState(uid, true /* active */);
+    }
+
+    private void updateActiveState(int uid, boolean active) {
+        synchronized (this) {
+            for (int i = mPinnedApps.size() - 1; i >= 0; i--) {
+                PinnedApp app = mPinnedApps.valueAt(i);
+                if (app.uid == uid) {
+                    app.active = active;
+                }
             }
         }
     }
 
-    private void unpinCameraApp() {
-        ArrayList<PinnedFile> pinnedCameraFiles;
+    private void unpinApp(@AppKey int key) {
+        ArrayList<PinnedFile> pinnedAppFiles;
         synchronized (this) {
-            pinnedCameraFiles = new ArrayList<>(mPinnedCameraFiles);
-            mPinnedCameraFiles.clear();
+            PinnedApp app = mPinnedApps.get(key);
+            if (app == null) {
+                return;
+            }
+            mPinnedApps.remove(key);
+            pinnedAppFiles = new ArrayList<>(app.mFiles);
         }
-        for (PinnedFile pinnedFile : pinnedCameraFiles) {
+        for (PinnedFile pinnedFile : pinnedAppFiles) {
             pinnedFile.close();
         }
     }
@@ -202,68 +346,216 @@
     }
 
     private ApplicationInfo getCameraInfo(int userHandle) {
-        //  find the camera via an intent
-        //  use INTENT_ACTION_STILL_IMAGE_CAMERA instead of _SECURE.  On a
-        //  device without a fbe enabled, the _SECURE intent will never get set.
         Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
-        PackageManager pm = mContext.getPackageManager();
-        ResolveInfo cameraResolveInfo = pm.resolveActivityAsUser(cameraIntent,
-                PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                userHandle);
-        if (cameraResolveInfo == null ) {
-            //this is not necessarily an error
-            if (DEBUG) {
-              Slog.v(TAG, "Unable to resolve camera intent");
-            }
+        ApplicationInfo info = getApplicationInfoForIntent(cameraIntent, userHandle,
+            false /* defaultToSystemApp */);
+
+        // If the STILL_IMAGE_CAMERA intent doesn't resolve, try the _SECURE intent.
+        // We don't use _SECURE first because it will never get set on a device
+        // without File-based Encryption. But if the user has only set the intent
+        // before unlocking their device, we may still be able to identify their
+        // preference using this intent.
+        if (info == null) {
+            cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
+            info = getApplicationInfoForIntent(cameraIntent, userHandle,
+                false /* defaultToSystemApp */);
+        }
+
+        // If the _SECURE intent doesn't resolve, try the original intent but request
+        // the system app for camera if there was more than one result.
+        if (info == null) {
+            cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+            info = getApplicationInfoForIntent(cameraIntent, userHandle,
+                true /* defaultToSystemApp */);
+        }
+        return info;
+    }
+
+    private ApplicationInfo getHomeInfo(int userHandle) {
+        Intent intent = mAmInternal.getHomeIntent();
+        return getApplicationInfoForIntent(intent, userHandle, false);
+    }
+
+    private ApplicationInfo getApplicationInfoForIntent(Intent intent, int userHandle,
+            boolean defaultToSystemApp) {
+        if (intent == null) {
             return null;
         }
 
-        if (isResolverActivity(cameraResolveInfo.activityInfo))
-        {
-            if (DEBUG) {
-              Slog.v(TAG, "cameraIntent returned resolverActivity");
-            }
+        ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivityAsUser(intent,
+                MATCH_FLAGS, userHandle);
+
+        // If this intent can resolve to only one app, choose that one.
+        // Otherwise, if we've requested to default to the system app, return it;
+        // if we have not requested that default, return null if there's more than one option.
+        // If there's more than one system app, return null since we don't know which to pick.
+        if (resolveInfo == null) {
             return null;
         }
 
-        return cameraResolveInfo.activityInfo.applicationInfo;
+        if (!isResolverActivity(resolveInfo.activityInfo)) {
+            return resolveInfo.activityInfo.applicationInfo;
+        }
+
+        if (defaultToSystemApp) {
+            List<ResolveInfo> infoList = mContext.getPackageManager()
+                .queryIntentActivitiesAsUser(intent, MATCH_FLAGS, userHandle);
+            ApplicationInfo systemAppInfo = null;
+            for (ResolveInfo info : infoList) {
+                if ((info.activityInfo.applicationInfo.flags
+                      & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                    if (systemAppInfo == null) {
+                        systemAppInfo = info.activityInfo.applicationInfo;
+                    } else {
+                        // If there's more than one system app, return null due to ambiguity.
+                        return null;
+                    }
+                }
+            }
+            return systemAppInfo;
+        }
+
+        return null;
+    }
+
+    private void sendPinAppsMessage(int userHandle) {
+        mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApps, this,
+                userHandle));
+    }
+
+    private void pinApps(int userHandle) {
+        for (int i = mPinKeys.size() - 1; i >= 0; i--) {
+            int key = mPinKeys.valueAt(i);
+            pinApp(key, userHandle, true /* force */);
+        }
     }
 
     /**
-     * If the camera app is already pinned, unpin and repin it.
+     * @see #pinApp(int, int, boolean)
      */
-    private boolean pinCamera(int userHandle){
-        ApplicationInfo cameraInfo = getCameraInfo(userHandle);
-        if (cameraInfo == null) {
-            return false;
+    private void sendPinAppMessage(int key, int userHandle, boolean force) {
+        mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApp, this,
+                key, userHandle, force));
+    }
+
+    /**
+     * Pins an app of a specific type {@code key}.
+     *
+     * @param force If false, this will not repin the app if it's currently active. See
+     *              {@link #mPendingRepin}.
+     */
+    private void pinApp(int key, int userHandle, boolean force) {
+        int uid = getUidForKey(key);
+
+        // In case the app is currently active, don't repin until next process restart. See
+        // mPendingRepin for more information.
+        if (!force && uid != -1) {
+            synchronized (this) {
+                mPendingRepin.put(uid, key);
+            }
+            return;
+        }
+        unpinApp(key);
+        ApplicationInfo info = getInfoForKey(key, userHandle);
+        if (info != null) {
+            pinApp(key, info);
+        }
+    }
+
+    /**
+     * Checks whether the pinned package with {@code key} is active or not.
+
+     * @return The uid of the pinned app, or {@code -1} otherwise.
+     */
+    private int getUidForKey(@AppKey int key) {
+        synchronized (this) {
+            PinnedApp existing = mPinnedApps.get(key);
+            return existing != null && existing.active
+                    ? existing.uid
+                    : -1;
+        }
+    }
+
+    /**
+     * Retrieves the current application info for the given app type.
+     *
+     * @param key The app type to retrieve the info for.
+     * @param userHandle The user id of the current user.
+     */
+    private @Nullable ApplicationInfo getInfoForKey(@AppKey int key, int userHandle) {
+        switch (key) {
+            case KEY_CAMERA:
+                return getCameraInfo(userHandle);
+            case KEY_HOME:
+                return getHomeInfo(userHandle);
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * @return The app type name for {@code key}.
+     */
+    private String getNameForKey(@AppKey int key) {
+        switch (key) {
+            case KEY_CAMERA:
+                return "Camera";
+            case KEY_HOME:
+                return "Home";
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * @return The maximum amount of bytes to be pinned for an app of type {@code key}.
+     */
+    private int getSizeLimitForKey(@AppKey int key) {
+        switch (key) {
+            case KEY_CAMERA:
+                return MAX_CAMERA_PIN_SIZE;
+            case KEY_HOME:
+                return MAX_HOME_PIN_SIZE;
+            default:
+                return 0;
+        }
+    }
+
+    /**
+     * Pins an application.
+     *
+     * @param key The key of the app to pin.
+     * @param appInfo The corresponding app info.
+     */
+    private void pinApp(@AppKey int key, @Nullable ApplicationInfo appInfo) {
+        if (appInfo == null) {
+            return;
         }
 
-        //unpin after checking that the camera intent has resolved
-        //this prevents us from thrashing when switching users with
-        //FBE enabled, because the intent won't resolve until the unlock
-        unpinCameraApp();
+        PinnedApp pinnedApp = new PinnedApp(appInfo);
+        synchronized (this) {
+            mPinnedApps.put(key, pinnedApp);
+        }
 
-        //pin APK
-        String camAPK = cameraInfo.sourceDir;
-        PinnedFile pf = pinFile(camAPK,
-                                MAX_CAMERA_PIN_SIZE,
-                                /*attemptPinIntrospection=*/true);
+        // pin APK
+        int pinSizeLimit = getSizeLimitForKey(key);
+        String apk = appInfo.sourceDir;
+        PinnedFile pf = pinFile(apk, pinSizeLimit, /*attemptPinIntrospection=*/true);
         if (pf == null) {
-            Slog.e(TAG, "Failed to pin " + camAPK);
-            return false;
+            Slog.e(TAG, "Failed to pin " + apk);
+            return;
         }
         if (DEBUG) {
             Slog.i(TAG, "Pinned " + pf.fileName);
         }
         synchronized (this) {
-            mPinnedCameraFiles.add(pf);
+            pinnedApp.mFiles.add(pf);
         }
 
         // determine the ABI from either ApplicationInfo or Build
         String arch = "arm";
-        if (cameraInfo.primaryCpuAbi != null) {
-            if (VMRuntime.is64BitAbi(cameraInfo.primaryCpuAbi)) {
+        if (appInfo.primaryCpuAbi != null) {
+            if (VMRuntime.is64BitAbi(appInfo.primaryCpuAbi)) {
                 arch = arch + "64";
             }
         } else {
@@ -273,32 +565,29 @@
         }
 
         // get the path to the odex or oat file
-        String baseCodePath = cameraInfo.getBaseCodePath();
+        String baseCodePath = appInfo.getBaseCodePath();
         String[] files = null;
         try {
             files = DexFile.getDexFileOutputPaths(baseCodePath, arch);
         } catch (IOException ioe) {}
         if (files == null) {
-            return true;
+            return;
         }
 
         //not pinning the oat/odex is not a fatal error
         for (String file : files) {
-            pf = pinFile(file, MAX_CAMERA_PIN_SIZE, /*attemptPinIntrospection=*/false);
+            pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
             if (pf != null) {
                 synchronized (this) {
-                    mPinnedCameraFiles.add(pf);
+                    pinnedApp.mFiles.add(pf);
                 }
                 if (DEBUG) {
                     Slog.i(TAG, "Pinned " + pf.fileName);
                 }
             }
         }
-
-        return true;
     }
 
-
     /** mlock length bytes of fileToPin in memory
      *
      * If attemptPinIntrospection is true, then treat the file to pin as a zip file and
@@ -581,24 +870,38 @@
         }
     }
 
-    private synchronized ArrayList<PinnedFile> snapshotPinnedFiles() {
-        int nrPinnedFiles = mPinnedFiles.size() + mPinnedCameraFiles.size();
-        ArrayList<PinnedFile> pinnedFiles = new ArrayList<>(nrPinnedFiles);
-        pinnedFiles.addAll(mPinnedFiles);
-        pinnedFiles.addAll(mPinnedCameraFiles);
-        return pinnedFiles;
-    }
-
     private final class BinderService extends Binder {
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-            long totalSize = 0;
-            for (PinnedFile pinnedFile : snapshotPinnedFiles()) {
-                pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
-                totalSize += pinnedFile.bytesPinned;
+            synchronized (PinnerService.this) {
+                long totalSize = 0;
+                for (PinnedFile pinnedFile : mPinnedFiles) {
+                    pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
+                    totalSize += pinnedFile.bytesPinned;
+                }
+                pw.println();
+                for (int key : mPinnedApps.keySet()) {
+                    PinnedApp app = mPinnedApps.get(key);
+                    pw.print(getNameForKey(key));
+                    pw.print(" uid="); pw.print(app.uid);
+                    pw.print(" active="); pw.print(app.active);
+                    pw.println();
+                    for (PinnedFile pf : mPinnedApps.get(key).mFiles) {
+                        pw.print("  "); pw.format("%s %s\n", pf.fileName, pf.bytesPinned);
+                        totalSize += pf.bytesPinned;
+                    }
+                }
+                pw.format("Total size: %s\n", totalSize);
+                pw.println();
+                if (!mPendingRepin.isEmpty()) {
+                    pw.print("Pending repin: ");
+                    for (int key : mPendingRepin.values()) {
+                        pw.print(getNameForKey(key)); pw.print(' ');
+                    }
+                    pw.println();
+                }
             }
-            pw.format("Total size: %s\n", totalSize);
         }
     }
 
@@ -634,8 +937,30 @@
         int length;
     }
 
+    /**
+     * Represents an app that was pinned.
+     */
+    private final class PinnedApp {
+
+        /**
+         * The uid of the package being pinned. This stays constant while the package stays
+         * installed.
+         */
+        final int uid;
+
+        /** Whether it is currently active, i.e. there is a running process from that package. */
+        boolean active;
+
+        /** List of pinned files. */
+        final ArrayList<PinnedFile> mFiles = new ArrayList<>();
+
+        private PinnedApp(ApplicationInfo appInfo) {
+            uid = appInfo.uid;
+            active = mAmInternal.isUidActive(uid);
+        }
+    }
+
     final class PinnerHandler extends Handler {
-        static final int PIN_CAMERA_MSG  = 4000;
         static final int PIN_ONSTART_MSG = 4001;
 
         public PinnerHandler(Looper looper) {
@@ -645,13 +970,6 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-
-                case PIN_CAMERA_MSG:
-                {
-                    handlePinCamera(msg.arg1);
-                }
-                break;
-
                 case PIN_ONSTART_MSG:
                 {
                     handlePinOnStart();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 2deb759..42157cc 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -24,22 +24,18 @@
 import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIED;
 import static android.os.storage.OnObbStateChangeListener.MOUNTED;
 import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
-
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readLongAttribute;
 import static com.android.internal.util.XmlUtils.readStringAttribute;
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
-
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.Manifest;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityManagerInternal.ScreenObserver;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.KeyguardManager;
@@ -51,8 +47,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ProviderInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
@@ -81,10 +79,12 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
 import android.os.storage.DiskInfo;
 import android.os.storage.IObbActionListener;
 import android.os.storage.IStorageEventListener;
@@ -101,11 +101,13 @@
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.DataUnit;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 
@@ -125,9 +127,8 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.storage.AppFuseBridge;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -163,6 +164,9 @@
 import javax.crypto.SecretKeyFactory;
 import javax.crypto.spec.PBEKeySpec;
 
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
 /**
  * Service responsible for various storage media. Connects to {@code vold} to
  * watch for and manage dynamically added storage, such as SD cards and USB mass
@@ -176,7 +180,12 @@
 
     /* Read during boot to decide whether to enable zram when available */
     private static final String ZRAM_ENABLED_PROPERTY =
-        "persist.sys.zram_enabled";
+            "persist.sys.zram_enabled";
+
+    private static final String ISOLATED_STORAGE_PROPERTY =
+            "persist.sys.isolated_storage";
+
+    private static final String SHARED_SANDBOX_ID_PREFIX = "shared:";
 
     public static class Lifecycle extends SystemService {
         private StorageManagerService mStorageManagerService;
@@ -269,6 +278,18 @@
      */
     private final Object mLock = LockGuard.installNewLock(LockGuard.INDEX_STORAGE);
 
+    /**
+     * Similar to {@link #mLock}, never hold this lock while performing downcalls into vold.
+     * Also, never hold this while calling into PackageManagerService since it is used in callbacks
+     * from PackageManagerService.
+     *
+     * If both {@link #mLock} and this lock need to be held, {@link #mLock} should be acquired
+     * before this.
+     *
+     * Use -PL suffix for methods that need to called with this lock held.
+     */
+    private final Object mPackagesLock = new Object();
+
     /** Set of users that we know are unlocked. */
     @GuardedBy("mLock")
     private int[] mLocalUnlockedUsers = EmptyArray.INT;
@@ -298,6 +319,15 @@
     @GuardedBy("mLock")
     private String mMoveTargetUuid;
 
+    @GuardedBy("mPackagesLock")
+    private final SparseArray<ArraySet<String>> mPackages = new SparseArray<>();
+
+    @GuardedBy("mPackagesLock")
+    private final ArrayMap<String, Integer> mAppIds = new ArrayMap<>();
+
+    @GuardedBy("mPackagesLock")
+    private final SparseArray<String> mSandboxIds = new SparseArray<>();
+
     private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
 
     /** Holding lock for AppFuse business */
@@ -419,6 +449,7 @@
     private volatile boolean mSecureKeyguardShowing = true;
 
     private PackageManagerService mPms;
+    private PackageManagerInternal mPmInternal;
 
     private final Callbacks mCallbacks;
     private final LockPatternUtils mLockPatternUtils;
@@ -870,12 +901,13 @@
             try {
                 mVold.reset();
 
+                pushPackagesInfo();
                 // Tell vold about all existing and started users
                 for (UserInfo user : users) {
                     mVold.onUserAdded(user.id, user.serialNumber);
                 }
                 for (int userId : systemUnlockedUsers) {
-                    mVold.onUserStarted(userId);
+                    mVold.onUserStarted(userId, getPackagesArrayForUser(userId));
                     mStoraged.onUserStarted(userId);
                 }
                 mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
@@ -892,7 +924,7 @@
         // staging area is ready so it's ready for zygote-forked apps to
         // bind mount against.
         try {
-            mVold.onUserStarted(userId);
+            mVold.onUserStarted(userId, getPackagesArrayForUser(userId));
             mStoraged.onUserStarted(userId);
         } catch (Exception e) {
             Slog.wtf(TAG, e);
@@ -1398,6 +1430,7 @@
 
         // XXX: This will go away soon in favor of IMountServiceObserver
         mPms = (PackageManagerService) ServiceManager.getService("package");
+        mPmInternal = LocalServices.getService(PackageManagerInternal.class);
 
         HandlerThread hthread = new HandlerThread(TAG);
         hthread.start();
@@ -1447,9 +1480,99 @@
     }
 
     private void start() {
+        collectPackagesInfo();
         connect();
     }
 
+    private void collectPackagesInfo() {
+        if (!SystemProperties.getBoolean(ISOLATED_STORAGE_PROPERTY, false)) {
+            return;
+        }
+        resetPackageData();
+        final SparseArray<String> sharedUserIds = mPmInternal.getAppsWithSharedUserIds();
+        final int[] userIds = LocalServices.getService(
+                UserManagerInternal.class).getUserIds();
+        for (int userId : userIds) {
+            final List<ApplicationInfo> appInfos
+                    = mContext.getPackageManager().getInstalledApplicationsAsUser(
+                            PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+            synchronized (mPackagesLock) {
+                final ArraySet<String> userPackages = getPackagesForUserPL(userId);
+                for (int i = appInfos.size() - 1; i >= 0; --i) {
+                    if (appInfos.get(i).isInstantApp()) {
+                        continue;
+                    }
+                    final String packageName = appInfos.get(i).packageName;
+                    userPackages.add(packageName);
+
+                    final int appId = UserHandle.getAppId(appInfos.get(i).uid);
+                    mAppIds.put(packageName, appId);
+                    mSandboxIds.put(appId, getSandboxId(packageName, sharedUserIds.get(appId)));
+                }
+            }
+        }
+    }
+
+    private void resetPackageData() {
+        synchronized (mPackagesLock) {
+            mPackages.clear();
+            mAppIds.clear();
+            mSandboxIds.clear();
+        }
+    }
+
+    private static String getSandboxId(String packageName, String sharedUserId) {
+        return sharedUserId == null ? packageName : SHARED_SANDBOX_ID_PREFIX + sharedUserId;
+    }
+    private void pushPackagesInfo() throws RemoteException {
+        if (!SystemProperties.getBoolean(ISOLATED_STORAGE_PROPERTY, false)) {
+            return;
+        }
+        // Arrays to fill up from {@link #mAppIds}
+        final String[] allPackageNames;
+        final int[] appIdsForPackages;
+
+        // Arrays to fill up from {@link #mSandboxIds}
+        final int[] allAppIds;
+        final String[] sandboxIdsForApps;
+        synchronized (mPackagesLock) {
+            allPackageNames = new String[mAppIds.size()];
+            appIdsForPackages = new int[mAppIds.size()];
+            for (int i = mAppIds.size() - 1; i >= 0; --i) {
+                allPackageNames[i] = mAppIds.keyAt(i);
+                appIdsForPackages[i] = mAppIds.valueAt(i);
+            }
+
+            allAppIds = new int[mSandboxIds.size()];
+            sandboxIdsForApps = new String[mSandboxIds.size()];
+            for (int i = mSandboxIds.size() - 1; i >= 0; --i) {
+                allAppIds[i] = mSandboxIds.keyAt(i);
+                sandboxIdsForApps[i] = mSandboxIds.valueAt(i);
+            }
+        }
+        mVold.addAppIds(allPackageNames, appIdsForPackages);
+        mVold.addSandboxIds(allAppIds, sandboxIdsForApps);
+    }
+
+    @GuardedBy("mPackagesLock")
+    private ArraySet<String> getPackagesForUserPL(int userId) {
+        ArraySet<String> userPackages = mPackages.get(userId);
+        if (userPackages == null) {
+            userPackages = new ArraySet<>();
+            mPackages.put(userId, userPackages);
+        }
+        return userPackages;
+    }
+
+    private String[] getPackagesArrayForUser(int userId) {
+        if (!SystemProperties.getBoolean(ISOLATED_STORAGE_PROPERTY, false)) {
+            return new String[0];
+        }
+        synchronized (mPackagesLock) {
+            return getPackagesForUserPL(userId).toArray(new String[0]);
+        }
+    }
+
     private void connect() {
         IBinder binder = ServiceManager.getService("storaged");
         if (binder != null) {
@@ -1511,7 +1634,7 @@
     }
 
     private void systemReady() {
-        LocalServices.getService(ActivityManagerInternal.class)
+        LocalServices.getService(ActivityTaskManagerInternal.class)
                 .registerScreenObserver(this);
 
         mSystemReady = true;
@@ -2258,6 +2381,9 @@
                 }
             }, DateUtils.SECOND_IN_MILLIS);
             return 0;
+        } catch (ServiceSpecificException e) {
+            Slog.e(TAG, "fdeCheckPassword failed", e);
+            return e.errorCode;
         } catch (Exception e) {
             Slog.wtf(TAG, e);
             return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN;
@@ -3637,6 +3763,10 @@
 
         @Override
         public void onExternalStoragePolicyChanged(int uid, String packageName) {
+            // No runtime storage permissions in isolated storage world, so nothing to do here.
+            if (SystemProperties.getBoolean(ISOLATED_STORAGE_PROPERTY, false)) {
+                return;
+            }
             final int mountMode = getExternalStorageMountMode(uid, packageName);
             remountUidExternalStorage(uid, mountMode);
         }
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 63584d9..c5b4966 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -18,10 +18,12 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.os.Environment;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.util.Slog;
 
+import java.io.File;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
@@ -36,6 +38,7 @@
     private static final String TAG = "SystemServiceManager";
     private static final int SERVICE_CALL_WARN_TIME_MS = 50;
 
+    private static File sSystemDir;
     private final Context mContext;
     private boolean mSafeMode;
     private boolean mRuntimeRestarted;
@@ -318,6 +321,22 @@
     }
 
     /**
+     * Ensures that the system directory exist creating one if needed.
+     * @deprecated Use {@link Environment#getDataSystemCeDirectory()}
+     * or {@link Environment#getDataSystemDeDirectory()} instead.
+     * @return The system directory.
+     */
+    @Deprecated
+    public static File ensureSystemDir() {
+        if (sSystemDir == null) {
+            File dataDir = Environment.getDataDirectory();
+            sSystemDir = new File(dataDir, "system");
+            sSystemDir.mkdirs();
+        }
+        return sSystemDir;
+    }
+
+    /**
      * Outputs the state of this manager to the System log.
      */
     public void dump() {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 607db4e..ad9fa40 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1588,10 +1588,10 @@
         // Wakeup apps for the (SUBSCRIPTION_)PHONE_STATE broadcast.
         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
 
+        // Create a version of the intent with the number always populated.
         Intent intentWithPhoneNumber = new Intent(intent);
-        if (!TextUtils.isEmpty(incomingNumber)) {
-            intentWithPhoneNumber.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
-        }
+        intentWithPhoneNumber.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
+
         // Send broadcast twice, once for apps that have PRIVILEGED permission and once for those
         // that have the runtime one
         mContext.sendBroadcastAsUser(intentWithPhoneNumber, UserHandle.ALL,
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index f24d8cd..40f81b3 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -57,20 +57,20 @@
 import android.text.TextUtils;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
 import android.view.textservice.SpellCheckerInfo;
 import android.view.textservice.SpellCheckerSubtype;
 
 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.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.function.Predicate;
 
 public class TextServicesManagerService extends ITextServicesManager.Stub {
@@ -251,7 +251,6 @@
                 for (int j = 0; j < numOnGoingSessionRequests; j++) {
                     final SessionRequest req = grp.mOnGoingSessionRequests.get(j);
                     pw.println("      " + "On going Request #" + j + ":");
-                    ++j;
                     pw.println("        " + "mTsListener=" + req.mTsListener);
                     pw.println("        " + "mScListener=" + req.mScListener);
                     pw.println(
@@ -343,6 +342,7 @@
         mMonitor.register(context, null, UserHandle.ALL, true);
     }
 
+    @GuardedBy("mLock")
     private void initializeInternalStateLocked(@UserIdInt int userId) {
         // When DISABLE_PER_PROFILE_SPELL_CHECKER is true, we make sure here that work profile users
         // will never have non-null TextServicesData for their user ID.
@@ -510,7 +510,7 @@
     // TODO: Save SpellCheckerSubtype by supported languages by looking at "locale".
     @Override
     public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
-            String locale, boolean allowImplicitlySelectedSubtype) {
+            boolean allowImplicitlySelectedSubtype) {
         final int subtypeHashCode;
         final SpellCheckerInfo sci;
         final Locale systemLocale;
@@ -538,53 +538,39 @@
                 && !allowImplicitlySelectedSubtype) {
             return null;
         }
-        String candidateLocale = null;
-        if (subtypeHashCode == 0) {
-            // Spell checker language settings == "auto"
-            final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
-            if (imm != null) {
-                final InputMethodSubtype currentInputMethodSubtype =
-                        imm.getCurrentInputMethodSubtype();
-                if (currentInputMethodSubtype != null) {
-                    final String localeString = currentInputMethodSubtype.getLocale();
-                    if (!TextUtils.isEmpty(localeString)) {
-                        // 1. Use keyboard locale if available in the spell checker
-                        candidateLocale = localeString;
-                    }
+
+        final int numSubtypes = sci.getSubtypeCount();
+        if (subtypeHashCode != 0) {
+            // Use the user specified spell checker subtype
+            for (int i = 0; i < numSubtypes; ++i) {
+                final SpellCheckerSubtype scs = sci.getSubtypeAt(i);
+                if (scs.hashCode() == subtypeHashCode) {
+                    return scs;
                 }
             }
-            if (candidateLocale == null) {
-                // 2. Use System locale if available in the spell checker
-                candidateLocale = systemLocale.toString();
-            }
+            return null;
         }
-        SpellCheckerSubtype candidate = null;
+
+        // subtypeHashCode == 0 means spell checker language settings is "auto"
+
+        if (systemLocale == null) {
+            return null;
+        }
+        SpellCheckerSubtype firstLanguageMatchingSubtype = null;
         for (int i = 0; i < sci.getSubtypeCount(); ++i) {
             final SpellCheckerSubtype scs = sci.getSubtypeAt(i);
-            if (subtypeHashCode == 0) {
-                final String scsLocale = scs.getLocale();
-                if (candidateLocale.equals(scsLocale)) {
-                    return scs;
-                } else if (candidate == null) {
-                    if (candidateLocale.length() >= 2 && scsLocale.length() >= 2
-                            && candidateLocale.startsWith(scsLocale)) {
-                        // Fall back to the applicable language
-                        candidate = scs;
-                    }
-                }
-            } else if (scs.hashCode() == subtypeHashCode) {
-                if (DBG) {
-                    Slog.w(TAG, "Return subtype " + scs.hashCode() + ", input= " + locale
-                            + ", " + scs.getLocale());
-                }
-                // 3. Use the user specified spell check language
+            final Locale scsLocale = scs.getLocaleObject();
+            if (Objects.equals(scsLocale, systemLocale)) {
+                // Exact match wins.
                 return scs;
             }
+            if (firstLanguageMatchingSubtype == null && scsLocale != null
+                    && TextUtils.equals(systemLocale.getLanguage(), scsLocale.getLanguage())) {
+                // Remember as a fall back candidate
+                firstLanguageMatchingSubtype = scs;
+            }
         }
-        // 4. Fall back to the applicable language and return it if not null
-        // 5. Simply just return it even if it's null which means we could find no suitable
-        // spell check languages
-        return candidate;
+        return firstLanguageMatchingSubtype;
     }
 
     @Override
@@ -771,6 +757,7 @@
      * @return {@link TextServicesData} for the given user.  {@code null} if spell checker is not
      *         temporarily / permanently available for the specified user
      */
+    @GuardedBy("mLock")
     @Nullable
     private TextServicesData getDataFromCallingUserIdLocked(@UserIdInt int callingUserId) {
         final int spellCheckerOwnerUserId = mSpellCheckerOwnerUserIdMap.get(callingUserId);
@@ -1040,19 +1027,46 @@
     private static final class ISpellCheckerServiceCallbackBinder
             extends ISpellCheckerServiceCallback.Stub {
         @NonNull
-        private final SpellCheckerBindGroup mBindGroup;
-        @NonNull
-        private final SessionRequest mRequest;
+        private final Object mCallbackLock = new Object();
 
-        ISpellCheckerServiceCallbackBinder(@NonNull final SpellCheckerBindGroup bindGroup,
-                @NonNull final SessionRequest request) {
-            mBindGroup = bindGroup;
-            mRequest = request;
+        @GuardedBy("mCallbackLock")
+        @Nullable
+        private WeakReference<SpellCheckerBindGroup> mBindGroup;
+
+        /**
+         * Original {@link SessionRequest} that is associated with this callback.
+         *
+         * <p>Note that {@link SpellCheckerBindGroup#mOnGoingSessionRequests} guarantees that this
+         * {@link SessionRequest} object is kept alive until the request is canceled.</p>
+         */
+        @GuardedBy("mCallbackLock")
+        @Nullable
+        private WeakReference<SessionRequest> mRequest;
+
+        ISpellCheckerServiceCallbackBinder(@NonNull SpellCheckerBindGroup bindGroup,
+                @NonNull SessionRequest request) {
+            synchronized (mCallbackLock) {
+                mBindGroup = new WeakReference<>(bindGroup);
+                mRequest = new WeakReference<>(request);
+            }
         }
 
         @Override
         public void onSessionCreated(@Nullable ISpellCheckerSession newSession) {
-            mBindGroup.onSessionCreated(newSession, mRequest);
+            final SpellCheckerBindGroup group;
+            final SessionRequest request;
+            synchronized (mCallbackLock) {
+                if (mBindGroup == null || mRequest == null) {
+                    return;
+                }
+                group = mBindGroup.get();
+                request = mRequest.get();
+                mBindGroup = null;
+                mRequest = null;
+            }
+            if (group != null && request != null) {
+                group.onSessionCreated(newSession, request);
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 6b5366a..cb03255 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.IUiModeManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -81,6 +82,7 @@
 
     private boolean mCarModeEnabled = false;
     private boolean mCharging = false;
+    private boolean mPowerSave = false;
     private int mDefaultUiModeType;
     private boolean mCarModeKeepsScreenOn;
     private boolean mDeskModeKeepsScreenOn;
@@ -159,7 +161,14 @@
     private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            mCharging = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
+            switch (intent.getAction()) {
+                case Intent.ACTION_BATTERY_CHANGED:
+                    mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+                    break;
+                case PowerManager.ACTION_POWER_SAVE_MODE_CHANGING:
+                    mPowerSave = intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false);
+                    break;
+            }
             synchronized (mLock) {
                 if (mSystemReady) {
                     updateLocked(0, 0);
@@ -202,8 +211,9 @@
 
         context.registerReceiver(mDockModeReceiver,
                 new IntentFilter(Intent.ACTION_DOCK_EVENT));
-        context.registerReceiver(mBatteryReceiver,
-                new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+        IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+        batteryFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
+        context.registerReceiver(mBatteryReceiver, batteryFilter);
 
         mConfiguration.setToDefaults();
 
@@ -456,6 +466,11 @@
             uiMode |= mNightMode << 4;
         }
 
+        if (mPowerSave && !mNightModeLocked) {
+            uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
+            uiMode |= Configuration.UI_MODE_NIGHT_YES;
+        }
+
         if (LOG) {
             Slog.d(TAG,
                 "updateConfigurationLocked: mDockState=" + mDockState
@@ -475,7 +490,7 @@
             mSetUiMode = mConfiguration.uiMode;
 
             try {
-                ActivityManager.getService().updateConfiguration(mConfiguration);
+                ActivityTaskManager.getService().updateConfiguration(mConfiguration);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Failure communicating with activity manager", e);
             }
@@ -637,7 +652,7 @@
             Intent homeIntent = buildHomeIntent(category);
             if (Sandman.shouldStartDockApp(getContext(), homeIntent)) {
                 try {
-                    int result = ActivityManager.getService().startActivityWithConfig(
+                    int result = ActivityTaskManager.getService().startActivityWithConfig(
                             null, null, homeIntent, null, null, null, 0, 0,
                             mConfiguration, null, UserHandle.USER_CURRENT);
                     if (ActivityManager.isStartResultSuccessful(result)) {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 95e5518..ae3946a 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -66,6 +66,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Date;
 
 public class VibratorService extends IVibratorService.Stub
@@ -158,6 +159,7 @@
         public final int usageHint;
         public final int uid;
         public final String opPkg;
+        public final String reason;
 
         // The actual effect to be played.
         public VibrationEffect effect;
@@ -167,7 +169,7 @@
         public VibrationEffect originalEffect;
 
         private Vibration(IBinder token, VibrationEffect effect,
-                int usageHint, int uid, String opPkg) {
+                int usageHint, int uid, String opPkg, String reason) {
             this.token = token;
             this.effect = effect;
             this.startTime = SystemClock.elapsedRealtime();
@@ -175,6 +177,7 @@
             this.usageHint = usageHint;
             this.uid = uid;
             this.opPkg = opPkg;
+            this.reason = reason;
         }
 
         public void binderDied() {
@@ -233,7 +236,7 @@
 
         public VibrationInfo toInfo() {
             return new VibrationInfo(
-                    startTimeDebug, effect, originalEffect, usageHint, uid, opPkg);
+                    startTimeDebug, effect, originalEffect, usageHint, uid, opPkg, reason);
         }
     }
 
@@ -244,15 +247,18 @@
         private final int mUsageHint;
         private final int mUid;
         private final String mOpPkg;
+        private final String mReason;
 
         public VibrationInfo(long startTimeDebug, VibrationEffect effect,
-                VibrationEffect originalEffect, int usageHint, int uid, String opPkg) {
+                VibrationEffect originalEffect, int usageHint, int uid,
+                String opPkg, String reason) {
             mStartTimeDebug = startTimeDebug;
             mEffect = effect;
             mOriginalEffect = originalEffect;
             mUsageHint = usageHint;
             mUid = uid;
             mOpPkg = opPkg;
+            mReason = reason;
         }
 
         @Override
@@ -270,6 +276,8 @@
                     .append(mUid)
                     .append(", opPkg: ")
                     .append(mOpPkg)
+                    .append(", reason: ")
+                    .append(mReason)
                     .toString();
         }
     }
@@ -482,9 +490,9 @@
     }
 
     @Override // Binder call
-    public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint,
+    public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint, String reason,
             IBinder token) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate");
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
         try {
             if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -531,10 +539,11 @@
                     return;
                 }
 
-                Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
+                Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg, reason);
                 linkVibration(vib);
                 long ident = Binder.clearCallingIdentity();
                 try {
+
                     doCancelVibrateLocked();
                     startVibrationLocked(vib);
                     addToPreviousVibrationsLocked(vib);
@@ -1001,8 +1010,8 @@
                 Slog.w(TAG, "Failed to play prebaked effect, no fallback");
                 return 0;
             }
-            Vibration fallbackVib =
-                    new Vibration(vib.token, effect, vib.usageHint, vib.uid, vib.opPkg);
+            Vibration fallbackVib = new Vibration(vib.token, effect, vib.usageHint, vib.uid,
+                    vib.opPkg, vib.reason + " (fallback)");
             final int intensity = getCurrentIntensityLocked(fallbackVib);
             linkVibration(fallbackVib);
             applyVibrationIntensityScalingLocked(fallbackVib, intensity);
@@ -1292,7 +1301,7 @@
                 VibrationEffect effect =
                         VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
                 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
-                        mToken);
+                        "Shell Command", mToken);
                 return 0;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 59093c1..e7a8221 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -21,6 +21,7 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.system.ErrnoException;
+import android.system.Os;
 import android.system.OsConstants;
 import android.system.StructRlimit;
 import com.android.internal.os.ZygoteConnectionConstants;
@@ -47,6 +48,10 @@
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -621,23 +626,38 @@
             mFdHighWaterMark = fdThreshold;
         }
 
+        /**
+         * Dumps open file descriptors and their full paths to a temporary file in {@code mDumpDir}.
+         */
         private void dumpOpenDescriptors() {
+            // We cannot exec lsof to get more info about open file descriptors because a newly
+            // forked process will not have the permissions to readlink. Instead list all open
+            // descriptors from /proc/pid/fd and resolve them.
+            List<String> dumpInfo = new ArrayList<>();
+            String fdDirPath = String.format("/proc/%d/fd/", Process.myPid());
+            File[] fds = new File(fdDirPath).listFiles();
+            if (fds == null) {
+                dumpInfo.add("Unable to list " + fdDirPath);
+            } else {
+                for (File f : fds) {
+                    String fdSymLink = f.getAbsolutePath();
+                    String resolvedPath = "";
+                    try {
+                        resolvedPath = Os.readlink(fdSymLink);
+                    } catch (ErrnoException ex) {
+                        resolvedPath = ex.getMessage();
+                    }
+                    dumpInfo.add(fdSymLink + "\t" + resolvedPath);
+                }
+            }
+
+            // Dump the fds & paths to a temp file.
             try {
                 File dumpFile = File.createTempFile("anr_fd_", "", mDumpDir);
-                java.lang.Process proc = new ProcessBuilder()
-                    .command("/system/bin/lsof", "-p", String.valueOf(Process.myPid()))
-                    .redirectErrorStream(true)
-                    .redirectOutput(dumpFile)
-                    .start();
-
-                int returnCode = proc.waitFor();
-                if (returnCode != 0) {
-                    Slog.w(TAG, "Unable to dump open descriptors, lsof return code: "
-                        + returnCode);
-                    dumpFile.delete();
-                }
-            } catch (IOException | InterruptedException ex) {
-                Slog.w(TAG, "Unable to dump open descriptors: " + ex);
+                Path out = Paths.get(dumpFile.getAbsolutePath());
+                Files.write(out, dumpInfo, StandardCharsets.UTF_8);
+            } catch (IOException ex) {
+                Slog.w(TAG, "Unable to write open descriptors to file: " + ex);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index ca715b5..5bf6892 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -24,7 +24,6 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -32,6 +31,7 @@
 import java.util.Set;
 import java.util.function.Predicate;
 
+import android.app.ActivityManagerInternal;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
@@ -59,14 +59,12 @@
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.TransferPipe;
-import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.server.AppStateTracker;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
-import com.android.server.am.ActivityManagerService.NeededUriGrants;
 
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -97,6 +95,7 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.webkit.WebViewZygote;
+import com.android.server.uri.NeededUriGrants;
 
 public final class ActiveServices {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActiveServices" : TAG_AM;
@@ -513,19 +512,18 @@
             fgRequired = false;
         }
 
-        NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
+        NeededUriGrants neededGrants = mAm.mUgmInternal.checkGrantUriPermissionFromIntent(
                 callingUid, r.packageName, service, service.getFlags(), null, r.userId);
 
         // If permissions need a review before any of the app components can run,
         // we do not start the service and launch a review activity if the calling app
         // is in the foreground passing it a pending intent to start the service when
         // review is completed.
-        if (mAm.mPermissionReviewRequired) {
-            // XXX This is not dealing with fgRequired!
-            if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
-                    callingUid, service, callerFg, userId)) {
-                return null;
-            }
+
+        // XXX This is not dealing with fgRequired!
+        if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
+                callingUid, service, callerFg, userId)) {
+            return null;
         }
 
         if (unscheduleServiceRestartLocked(r, callingUid, false)) {
@@ -540,6 +538,11 @@
 
         if (fgRequired) {
             // We are now effectively running a foreground service.
+            ServiceState stracker = r.getTracker();
+            if (stracker != null) {
+                stracker.setForeground(true, mAm.mProcessStats.getMemFactorLocked(),
+                        r.lastActivity);
+            }
             mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                     AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, true);
         }
@@ -1191,13 +1194,14 @@
                         r.app.pid, r.appInfo.uid, "startForeground");
             }
             boolean alreadyStartedOp = false;
+            boolean stopProcStatsOp = false;
             if (r.fgRequired) {
                 if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) {
                     Slog.i(TAG, "Service called startForeground() as required: " + r);
                 }
                 r.fgRequired = false;
                 r.fgWaiting = false;
-                alreadyStartedOp = true;
+                alreadyStartedOp = stopProcStatsOp = true;
                 mAm.mHandler.removeMessages(
                         ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
             }
@@ -1265,6 +1269,15 @@
                             active.mNumActive++;
                         }
                         r.isForeground = true;
+                        if (!stopProcStatsOp) {
+                            ServiceState stracker = r.getTracker();
+                            if (stracker != null) {
+                                stracker.setForeground(true,
+                                        mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
+                            }
+                        } else {
+                            stopProcStatsOp = false;
+                        }
                         mAm.mAppOpsService.startOperation(
                                 AppOpsManager.getToken(mAm.mAppOpsService),
                                 AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName,
@@ -1286,6 +1299,15 @@
                     }
                 }
             } finally {
+                if (stopProcStatsOp) {
+                    // We got through to this point with it actively being started foreground,
+                    // and never decided we wanted to keep it like that, so drop it.
+                    ServiceState stracker = r.getTracker();
+                    if (stracker != null) {
+                        stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
+                                r.lastActivity);
+                    }
+                }
                 if (alreadyStartedOp) {
                     // If we had previously done a start op for direct foreground start,
                     // we have cleared the flag so can now drop it.
@@ -1301,6 +1323,11 @@
                     decActiveForegroundAppLocked(smap, r);
                 }
                 r.isForeground = false;
+                ServiceState stracker = r.getTracker();
+                if (stracker != null) {
+                    stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
+                            r.lastActivity);
+                }
                 mAm.mAppOpsService.finishOperation(
                         AppOpsManager.getToken(mAm.mAppOpsService),
                         AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
@@ -1390,7 +1417,7 @@
     private boolean updateServiceClientActivitiesLocked(ProcessRecord proc,
             ConnectionRecord modCr, boolean updateLru) {
         if (modCr != null && modCr.binding.client != null) {
-            if (modCr.binding.client.activities.size() <= 0) {
+            if (!modCr.binding.client.hasActivities()) {
                 // This connection is from a client without activities, so adding
                 // and removing is not interesting.
                 return false;
@@ -1408,7 +1435,7 @@
                         // Binding to ourself is not interesting.
                         continue;
                     }
-                    if (cr.binding.client.activities.size() > 0) {
+                    if (cr.binding.client.hasActivities()) {
                         anyClientActivities = true;
                         break;
                     }
@@ -1507,75 +1534,73 @@
         // we schedule binding to the service but do not start its process, then
         // we launch a review activity to which is passed a callback to invoke
         // when done to start the bound service's process to completing the binding.
-        if (mAm.mPermissionReviewRequired) {
-            if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
-                    s.packageName, s.userId)) {
+        if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+                s.packageName, s.userId)) {
 
-                permissionsReviewRequired = true;
+            permissionsReviewRequired = true;
 
-                // Show a permission review UI only for binding from a foreground app
-                if (!callerFg) {
-                    Slog.w(TAG, "u" + s.userId + " Binding to a service in package"
-                            + s.packageName + " requires a permissions review");
-                    return 0;
-                }
+            // Show a permission review UI only for binding from a foreground app
+            if (!callerFg) {
+                Slog.w(TAG, "u" + s.userId + " Binding to a service in package"
+                        + s.packageName + " requires a permissions review");
+                return 0;
+            }
 
-                final ServiceRecord serviceRecord = s;
-                final Intent serviceIntent = service;
+            final ServiceRecord serviceRecord = s;
+            final Intent serviceIntent = service;
 
-                RemoteCallback callback = new RemoteCallback(
-                        new RemoteCallback.OnResultListener() {
-                    @Override
-                    public void onResult(Bundle result) {
-                        synchronized(mAm) {
-                            final long identity = Binder.clearCallingIdentity();
-                            try {
-                                if (!mPendingServices.contains(serviceRecord)) {
-                                    return;
-                                }
-                                // If there is still a pending record, then the service
-                                // binding request is still valid, so hook them up. We
-                                // proceed only if the caller cleared the review requirement
-                                // otherwise we unbind because the user didn't approve.
-                                if (!mAm.getPackageManagerInternalLocked()
-                                        .isPermissionsReviewRequired(
-                                                serviceRecord.packageName,
-                                                serviceRecord.userId)) {
-                                    try {
-                                        bringUpServiceLocked(serviceRecord,
-                                                serviceIntent.getFlags(),
-                                                callerFg, false, false);
-                                    } catch (RemoteException e) {
-                                        /* ignore - local call */
-                                    }
-                                } else {
-                                    unbindServiceLocked(connection);
-                                }
-                            } finally {
-                                Binder.restoreCallingIdentity(identity);
+            RemoteCallback callback = new RemoteCallback(
+                    new RemoteCallback.OnResultListener() {
+                @Override
+                public void onResult(Bundle result) {
+                    synchronized(mAm) {
+                        final long identity = Binder.clearCallingIdentity();
+                        try {
+                            if (!mPendingServices.contains(serviceRecord)) {
+                                return;
                             }
+                            // If there is still a pending record, then the service
+                            // binding request is still valid, so hook them up. We
+                            // proceed only if the caller cleared the review requirement
+                            // otherwise we unbind because the user didn't approve.
+                            if (!mAm.getPackageManagerInternalLocked()
+                                    .isPermissionsReviewRequired(
+                                            serviceRecord.packageName,
+                                            serviceRecord.userId)) {
+                                try {
+                                    bringUpServiceLocked(serviceRecord,
+                                            serviceIntent.getFlags(),
+                                            callerFg, false, false);
+                                } catch (RemoteException e) {
+                                    /* ignore - local call */
+                                }
+                            } else {
+                                unbindServiceLocked(connection);
+                            }
+                        } finally {
+                            Binder.restoreCallingIdentity(identity);
                         }
                     }
-                });
-
-                final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                intent.putExtra(Intent.EXTRA_PACKAGE_NAME, s.packageName);
-                intent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback);
-
-                if (DEBUG_PERMISSIONS_REVIEW) {
-                    Slog.i(TAG, "u" + s.userId + " Launching permission review for package "
-                            + s.packageName);
                 }
+            });
 
-                mAm.mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mAm.mContext.startActivityAsUser(intent, new UserHandle(userId));
-                    }
-                });
+            final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, s.packageName);
+            intent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback);
+
+            if (DEBUG_PERMISSIONS_REVIEW) {
+                Slog.i(TAG, "u" + s.userId + " Launching permission review for package "
+                        + s.packageName);
             }
+
+            mAm.mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mAm.mContext.startActivityAsUser(intent, new UserHandle(userId));
+                }
+            });
         }
 
         final long origId = Binder.clearCallingIdentity();
@@ -1599,7 +1624,7 @@
             }
 
             mAm.startAssociationLocked(callerApp.uid, callerApp.processName, callerApp.curProcState,
-                    s.appInfo.uid, s.name, s.processName);
+                    s.appInfo.uid, s.appInfo.longVersionCode, s.name, s.processName);
             // Once the apps have become associated, if one of them is caller is ephemeral
             // the target app should now be able to see the calling app
             mAm.grantEphemeralAccessLocked(callerApp.userId, service,
@@ -1607,7 +1632,8 @@
 
             AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
             ConnectionRecord c = new ConnectionRecord(b, activity,
-                    connection, flags, clientLabel, clientIntent);
+                    connection, flags, clientLabel, clientIntent,
+                    callerApp.uid, callerApp.processName);
 
             IBinder binder = connection.asBinder();
             ArrayList<ConnectionRecord> clist = s.connections.get(binder);
@@ -1624,6 +1650,7 @@
                 activity.connections.add(c);
             }
             b.client.connections.add(c);
+            c.startAssociationIfNeeded();
             if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
                 b.client.hasAboveClient = true;
             }
@@ -1869,7 +1896,7 @@
                 + " type=" + resolvedType + " callingUid=" + callingUid);
 
         userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
-                ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null);
+                ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE, "service", null);
 
         ServiceMap smap = getServiceMapLocked(userId);
         final ComponentName comp = service.getComponent();
@@ -2082,7 +2109,7 @@
                 bumpServiceExecutingLocked(r, execInFg, "bind");
                 r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
                 r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
-                        r.app.repProcState);
+                        r.app.getReportedProcState());
                 if (!rebind) {
                     i.requested = true;
                 }
@@ -2108,7 +2135,7 @@
     private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) {
         boolean canceled = false;
 
-        if (mAm.isShuttingDownLocked()) {
+        if (mAm.mAtmInternal.isShuttingDown()) {
             Slog.w(TAG, "Not scheduling restart of crashed service " + r.shortName
                     + " - system is shutting down");
             return false;
@@ -2438,7 +2465,7 @@
         if (DEBUG_MU)
             Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
                     + ", ProcessRecord.uid = " + app.uid);
-        r.app = app;
+        r.setProcess(app);
         r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
 
         final boolean newService = app.services.add(r);
@@ -2464,7 +2491,7 @@
             app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
             app.thread.scheduleCreateService(r, r.serviceInfo,
                     mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
-                    app.repProcState);
+                    app.getReportedProcState());
             r.postNotification();
             created = true;
         } catch (DeadObjectException e) {
@@ -2480,7 +2507,7 @@
                 // Cleanup.
                 if (newService) {
                     app.services.remove(r);
-                    r.app = null;
+                    r.setProcess(null);
                 }
 
                 // Retry.
@@ -2551,7 +2578,7 @@
             r.deliveredStarts.add(si);
             si.deliveryCount++;
             if (si.neededGrants != null) {
-                mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
+                mAm.mUgmInternal.grantUriPermissionUncheckedFromIntent(si.neededGrants,
                         si.getUriPermissionsLocked());
             }
             mAm.grantEphemeralAccessLocked(r.userId, si.intent,
@@ -2664,6 +2691,7 @@
                 // There is still a connection to the service that is
                 // being brought down.  Mark it as dead.
                 cr.serviceDead = true;
+                cr.stopAssociation();
                 try {
                     cr.conn.connected(r.name, null, true);
                 } catch (Exception e) {
@@ -2704,6 +2732,11 @@
                     + r);
             r.fgRequired = false;
             r.fgWaiting = false;
+            ServiceState stracker = r.getTracker();
+            if (stracker != null) {
+                stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
+                        r.lastActivity);
+            }
             mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                     AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
             mAm.mHandler.removeMessages(
@@ -2756,6 +2789,11 @@
         cancelForegroundNotificationLocked(r);
         if (r.isForeground) {
             decActiveForegroundAppLocked(smap, r);
+            ServiceState stracker = r.getTracker();
+            if (stracker != null) {
+                stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
+                        r.lastActivity);
+            }
             mAm.mAppOpsService.finishOperation(
                     AppOpsManager.getToken(mAm.mAppOpsService),
                     AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
@@ -2836,6 +2874,7 @@
             }
         }
         b.connections.remove(c);
+        c.stopAssociation();
         if (c.activity != null && c.activity != skipAct) {
             if (c.activity.connections != null) {
                 c.activity.connections.remove(c);
@@ -2866,7 +2905,8 @@
             }
         }
 
-        mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid, s.name);
+        mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid,
+                s.appInfo.longVersionCode, s.name, s.processName);
 
         if (b.connections.size() == 0) {
             b.intent.apps.remove(b.client);
@@ -3051,13 +3091,13 @@
                 }
             }
             if (finishing) {
-                if (r.app != null && !r.app.persistent) {
+                if (r.app != null && !r.app.isPersistent()) {
                     r.app.services.remove(r);
                     if (r.whitelistManager) {
                         updateWhitelistManagerLocked(r.app);
                     }
                 }
-                r.app = null;
+                r.setProcess(null);
             }
         }
     }
@@ -3141,7 +3181,7 @@
                         && (filterByClasses == null
                             || filterByClasses.contains(service.name.getClassName())));
             if (sameComponent
-                    && (service.app == null || evenPersistent || !service.app.persistent)) {
+                    && (service.app == null || evenPersistent || !service.app.isPersistent())) {
                 if (!doit) {
                     return true;
                 }
@@ -3149,14 +3189,14 @@
                 Slog.i(TAG, "  Force stopping service " + service);
                 if (service.app != null) {
                     service.app.removed = killProcess;
-                    if (!service.app.persistent) {
+                    if (!service.app.isPersistent()) {
                         service.app.services.remove(service);
                         if (service.whitelistManager) {
                             updateWhitelistManagerLocked(service.app);
                         }
                     }
                 }
-                service.app = null;
+                service.setProcess(null);
                 service.isolatedProc = null;
                 if (mTmpCollectionResults == null) {
                     mTmpCollectionResults = new ArrayList<>();
@@ -3303,10 +3343,10 @@
             synchronized (sr.stats.getBatteryStats()) {
                 sr.stats.stopLaunchedLocked();
             }
-            if (sr.app != app && sr.app != null && !sr.app.persistent) {
+            if (sr.app != app && sr.app != null && !sr.app.isPersistent()) {
                 sr.app.services.remove(sr);
             }
-            sr.app = null;
+            sr.setProcess(null);
             sr.isolatedProc = null;
             sr.executeNesting = 0;
             sr.forceClearTracker();
@@ -3348,7 +3388,7 @@
                         continue;
                     }
                     // XXX turned off for now until we have more time to get a better policy.
-                    if (false && proc != null && !proc.persistent && proc.thread != null
+                    if (false && proc != null && !proc.isPersistent() && proc.thread != null
                             && proc.pid != 0 && proc.pid != ActivityManagerService.MY_PID
                             && proc.setProcState >= ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
                         proc.kill("bound to service " + sr.name.flattenToShortString()
@@ -3366,7 +3406,7 @@
 
             // Unless the process is persistent, this process record is going away,
             // so make sure the service is cleaned out of it.
-            if (!app.persistent) {
+            if (!app.isPersistent()) {
                 app.services.removeAt(i);
             }
 
@@ -3476,7 +3516,7 @@
         if (r.app != null && r.app.pid == ActivityManagerService.MY_PID) {
             info.flags |= ActivityManager.RunningServiceInfo.FLAG_SYSTEM_PROCESS;
         }
-        if (r.app != null && r.app.persistent) {
+        if (r.app != null && r.app.isPersistent()) {
             info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS;
         }
 
@@ -3618,7 +3658,7 @@
             }
 
             app = r.app;
-            if (app != null && app.debugging) {
+            if (app != null && app.isDebugging()) {
                 // The app's being debugged; let it ride
                 return;
             }
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 698b6f7..73ffd5c 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -30,16 +31,20 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.FLAG_PRIVATE;
 import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
+import static com.android.server.am.ActivityDisplayProto.CONFIGURATION_CONTAINER;
+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.STACKS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
 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.ActivityDisplayProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityDisplayProto.STACKS;
-import static com.android.server.am.ActivityDisplayProto.ID;
+import static com.android.server.am.ActivityStackSupervisor.TAG_STATES;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
 import android.app.WindowConfiguration;
 import android.graphics.Point;
@@ -47,11 +52,13 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
+
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.ConfigurationContainer;
 import com.android.server.wm.DisplayWindowController;
-
 import com.android.server.wm.WindowContainerListener;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
@@ -90,9 +97,9 @@
     private IntArray mDisplayAccessUIDs = new IntArray();
 
     /** All tokens used to put activities on this stack to sleep (including mOffToken) */
-    final ArrayList<ActivityManagerInternal.SleepToken> mAllSleepTokens = new ArrayList<>();
+    final ArrayList<ActivityTaskManagerInternal.SleepToken> mAllSleepTokens = new ArrayList<>();
     /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
-    ActivityManagerInternal.SleepToken mOffToken;
+    ActivityTaskManagerInternal.SleepToken mOffToken;
 
     private boolean mSleeping;
 
@@ -152,33 +159,53 @@
         onStackOrderChanged();
     }
 
-    void positionChildAtTop(ActivityStack stack) {
-        positionChildAt(stack, mStacks.size());
+    void positionChildAtTop(ActivityStack stack, boolean includingParents) {
+        positionChildAt(stack, mStacks.size(), includingParents);
     }
 
     void positionChildAtBottom(ActivityStack stack) {
-        positionChildAt(stack, 0);
+        positionChildAt(stack, 0, false /* includingParents */);
     }
 
     private void positionChildAt(ActivityStack stack, int position) {
+        positionChildAt(stack, position, false /* includingParents */);
+    }
+
+    private void positionChildAt(ActivityStack stack, int position, boolean includingParents) {
         // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
         //       the position internally, also update the logic here
         mStacks.remove(stack);
         final int insertPosition = getTopInsertPosition(stack, position);
         mStacks.add(insertPosition, stack);
-        mWindowContainerController.positionChildAt(stack.getWindowContainerController(),
-                insertPosition);
+        // Since positionChildAt() is called during the creation process of pinned stacks,
+        // ActivityStack#getWindowContainerController() can be null. In this special case,
+        // since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
+        // we don't have to call WindowContainerController#positionChildAt() here.
+        if (stack.getWindowContainerController() != null) {
+            mWindowContainerController.positionChildAt(stack.getWindowContainerController(),
+                    insertPosition, includingParents);
+        }
         onStackOrderChanged();
     }
 
     private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
         int position = mStacks.size();
-        if (position > 0) {
-            final ActivityStack topStack = mStacks.get(position - 1);
-            if (topStack.getWindowConfiguration().isAlwaysOnTop() && topStack != stack) {
-                // If the top stack is always on top, we move this stack just below it.
-                position--;
+        if (stack.inPinnedWindowingMode()) {
+            // Stack in pinned windowing mode is z-ordered on-top of all other stacks so okay to
+            // just return the candidate position.
+            return Math.min(position, candidatePosition);
+        }
+        while (position > 0) {
+            final ActivityStack targetStack = mStacks.get(position - 1);
+            if (!targetStack.isAlwaysOnTop()) {
+                // We reached a stack that isn't always-on-top.
+                break;
             }
+            if (stack.isAlwaysOnTop() && !targetStack.inPinnedWindowingMode()) {
+                // Always on-top non-pinned windowing mode stacks can go anywhere below pinned stack.
+                break;
+            }
+            position--;
         }
         return Math.min(position, candidatePosition);
     }
@@ -290,7 +317,7 @@
             }
         }
 
-        final ActivityManagerService service = mSupervisor.mService;
+        final ActivityTaskManagerService service = mSupervisor.mService;
         if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
                 service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
                 service.mSupportsPictureInPicture, activityType)) {
@@ -298,16 +325,6 @@
                     + windowingMode);
         }
 
-        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
-            // TODO: Should be okay to have stacks with with undefined windowing mode long term, but
-            // have to set them to something for now due to logic that depending on them.
-            windowingMode = getWindowingMode(); // Put in current display's windowing mode
-            if (windowingMode == WINDOWING_MODE_UNDEFINED) {
-                // Else fullscreen for now...
-                windowingMode = WINDOWING_MODE_FULLSCREEN;
-            }
-        }
-
         final int stackId = getNextStackId();
         return createStackUnchecked(windowingMode, activityType, stackId, onTop);
     }
@@ -322,6 +339,101 @@
                         this, stackId, mSupervisor, windowingMode, activityType, onTop);
     }
 
+    ActivityStack getFocusedStack() {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final ActivityStack stack = mStacks.get(i);
+            if (stack.isFocusable() && stack.shouldBeVisible(null /* starting */)) {
+                return stack;
+            }
+        }
+
+        return null;
+    }
+
+    ActivityStack getNextFocusableStack() {
+        return getNextFocusableStack(null /* currentFocus */, false /* ignoreCurrent */);
+    }
+
+    ActivityStack getNextFocusableStack(ActivityStack currentFocus, boolean ignoreCurrent) {
+        final int currentWindowingMode = currentFocus != null
+                ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
+
+        ActivityStack candidate = null;
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final ActivityStack stack = mStacks.get(i);
+            if (ignoreCurrent && stack == currentFocus) {
+                continue;
+            }
+            if (!stack.isFocusable() || !stack.shouldBeVisible(null)) {
+                continue;
+            }
+
+            if (currentWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                    && candidate == null && stack.inSplitScreenPrimaryWindowingMode()) {
+                // If the currently focused stack is in split-screen secondary we save off the
+                // top primary split-screen stack as a candidate for focus because we might
+                // prefer focus to move to an other stack to avoid primary split-screen stack
+                // overlapping with a fullscreen stack when a fullscreen stack is higher in z
+                // than the next split-screen stack. Assistant stack, I am looking at you...
+                // We only move the focus to the primary-split screen stack if there isn't a
+                // better alternative.
+                candidate = stack;
+                continue;
+            }
+            if (candidate != null && stack.inSplitScreenSecondaryWindowingMode()) {
+                // Use the candidate stack since we are now at the secondary split-screen.
+                return candidate;
+            }
+            return stack;
+        }
+        return candidate;
+    }
+
+    ActivityRecord getResumedActivity() {
+        final ActivityStack focusedStack = getFocusedStack();
+        if (focusedStack == null) {
+            return null;
+        }
+        // TODO(b/111541062): Move this into ActivityStack#getResumedActivity()
+        // Check if the focused stack has the resumed activity
+        ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+        if (resumedActivity == null || resumedActivity.app == null) {
+            // If there is no registered resumed activity in the stack or it is not running -
+            // try to use previously resumed one.
+            resumedActivity = focusedStack.mPausingActivity;
+            if (resumedActivity == null || resumedActivity.app == null) {
+                // If previously resumed activity doesn't work either - find the topmost running
+                // activity that can be focused.
+                resumedActivity = focusedStack.topRunningActivityLocked(true /* focusableOnly */);
+            }
+        }
+        return resumedActivity;
+    }
+
+    /**
+     * Pause all activities in either all of the stacks or just the back stacks.
+     * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
+     * @param resuming The resuming activity.
+     * @param dontWait The resuming activity isn't going to wait for all activities to be paused
+     *                 before resuming.
+     * @return true if any activity was paused as a result of this call.
+     */
+    boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
+        boolean someActivityPaused = false;
+        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = mStacks.get(stackNdx);
+            // TODO(b/111541062): Check if resumed activity on this display instead
+            if (!mSupervisor.isTopDisplayFocusedStack(stack)
+                    && stack.getResumedActivity() != null) {
+                if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
+                        " mResumedActivity=" + stack.getResumedActivity());
+                someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
+                        dontWait);
+            }
+        }
+        return someActivityPaused;
+    }
+
     /**
      * Removes stacks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
@@ -372,11 +484,16 @@
         final int windowingMode = stack.getWindowingMode();
 
         if (activityType == ACTIVITY_TYPE_HOME) {
+            // TODO(b/111363427) Rollback to throws exceptions once we figure out how to properly
+            // deal with home type stack when external display removed
             if (mHomeStack != null && mHomeStack != stack) {
-                throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
+                // throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
+                //         + mHomeStack + " already exist on display=" + this + " stack=" + stack);
+                Slog.e(TAG, "addStackReferenceIfNeeded: home stack="
                         + mHomeStack + " already exist on display=" + this + " stack=" + stack);
+            } else {
+                mHomeStack = stack;
             }
-            mHomeStack = stack;
         } else if (activityType == ACTIVITY_TYPE_RECENTS) {
             if (mRecentsStack != null && mRecentsStack != stack) {
                 throw new IllegalArgumentException("addStackReferenceIfNeeded: recents stack="
@@ -524,9 +641,23 @@
                 windowingMode = getWindowingMode();
             }
         }
+        return validateWindowingMode(windowingMode, r, task, activityType);
+    }
 
+    /**
+     * Check that the requested windowing-mode is appropriate for the specified task and/or activity
+     * on this display.
+     *
+     * @param windowingMode The windowing-mode to validate.
+     * @param r The {@link ActivityRecord} to check against.
+     * @param task The {@link TaskRecord} to check against.
+     * @param activityType An activity type.
+     * @return The provided windowingMode or the closest valid mode which is appropriate.
+     */
+    int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r,
+        @Nullable TaskRecord task, int activityType) {
         // Make sure the windowing mode we are trying to use makes sense for what is supported.
-        final ActivityManagerService service = mSupervisor.mService;
+        final ActivityTaskManagerService service = mSupervisor.mService;
         boolean supportsMultiWindow = service.mSupportsMultiWindow;
         boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow;
         boolean supportsFreeform = service.mSupportsFreeformWindowManagement;
@@ -568,7 +699,7 @@
 
     /**
      * Get the topmost stack on the display. It may be different from focused stack, because
-     * focus may be on another display.
+     * some stacks are not focusable (e.g. PiP).
      */
     ActivityStack getTopStack() {
         return mStacks.isEmpty() ? null : mStacks.get(mStacks.size() - 1);
@@ -844,6 +975,16 @@
         final long token = proto.start(fieldId);
         super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
         proto.write(ID, mDisplayId);
+        final ActivityStack focusedStack = getFocusedStack();
+        if (focusedStack != null) {
+            proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+            final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
+            if (focusedActivity != null) {
+                focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+            }
+        } else {
+            proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
+        }
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = mStacks.get(stackNdx);
             stack.writeToProto(proto, STACKS);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 6550d06..9bf72fb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -207,6 +207,10 @@
     // Indicates if the processes need to be started asynchronously.
     public boolean FLAG_PROCESS_START_ASYNC = DEFAULT_PROCESS_START_ASYNC;
 
+    // Indicates whether the activity starts logging is enabled.
+    // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
+    boolean mFlagActivityStartsLoggingEnabled;
+
     private final ActivityManagerService mService;
     private ContentResolver mResolver;
     private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -235,6 +239,12 @@
     // memory trimming.
     public int CUR_TRIM_CACHED_PROCESSES;
 
+    private static final Uri ACTIVITY_MANAGER_CONSTANTS_URI = Settings.Global.getUriFor(
+                Settings.Global.ACTIVITY_MANAGER_CONSTANTS);
+
+    private static final Uri ACTIVITY_STARTS_LOGGING_ENABLED_URI = Settings.Global.getUriFor(
+                Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED);
+
     public ActivityManagerConstants(ActivityManagerService service, Handler handler) {
         super(handler);
         mService = service;
@@ -243,9 +253,10 @@
 
     public void start(ContentResolver resolver) {
         mResolver = resolver;
-        mResolver.registerContentObserver(Settings.Global.getUriFor(
-                Settings.Global.ACTIVITY_MANAGER_CONSTANTS), false, this);
+        mResolver.registerContentObserver(ACTIVITY_MANAGER_CONSTANTS_URI, false, this);
+        mResolver.registerContentObserver(ACTIVITY_STARTS_LOGGING_ENABLED_URI, false, this);
         updateConstants();
+        updateActivityStartsLoggingEnabled();
     }
 
     public void setOverrideMaxCachedProcesses(int value) {
@@ -263,7 +274,12 @@
 
     @Override
     public void onChange(boolean selfChange, Uri uri) {
-        updateConstants();
+        if (uri == null) return;
+        if (ACTIVITY_MANAGER_CONSTANTS_URI.equals(uri)) {
+            updateConstants();
+        } else if (ACTIVITY_STARTS_LOGGING_ENABLED_URI.equals(uri)) {
+            updateActivityStartsLoggingEnabled();
+        }
     }
 
     private void updateConstants() {
@@ -337,6 +353,11 @@
         }
     }
 
+    private void updateActivityStartsLoggingEnabled() {
+        mFlagActivityStartsLoggingEnabled = Settings.Global.getInt(mResolver,
+                Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 0) == 1;
+    }
+
     private void updateMaxCachedProcesses() {
         CUR_MAX_CACHED_PROCESSES = mOverrideMaxCachedProcesses < 0
                 ? MAX_CACHED_PROCESSES : mOverrideMaxCachedProcesses;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 900bf8f..1d01a572 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,45 +16,22 @@
 
 package com.android.server.am;
 
-import static android.Manifest.permission.BIND_VOICE_INTERACTION;
 import static android.Manifest.permission.CHANGE_CONFIGURATION;
 import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
-import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
-import static android.Manifest.permission.READ_FRAME_BUFFER;
 import static android.Manifest.permission.REMOVE_TASKS;
-import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.Manifest.permission.STOP_APP_SWITCHES;
-import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_CONTENT;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
 import static android.app.AppOpsManager.OP_NONE;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
-import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
-import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
-import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.GET_PROVIDERS;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
@@ -63,10 +40,8 @@
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
-import static android.os.Build.VERSION_CODES.N;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
@@ -81,14 +56,12 @@
 import static android.os.Process.PROC_OUT_LONG;
 import static android.os.Process.PROC_PARENS;
 import static android.os.Process.PROC_SPACE_TERM;
-import static android.os.Process.ProcessStartResult;
 import static android.os.Process.ROOT_UID;
 import static android.os.Process.SCHED_FIFO;
 import static android.os.Process.SCHED_OTHER;
 import static android.os.Process.SCHED_RESET_ON_FORK;
 import static android.os.Process.SE_UID;
 import static android.os.Process.SHELL_UID;
-import static android.os.Process.SIGNAL_QUIT;
 import static android.os.Process.SIGNAL_USR1;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE;
@@ -112,26 +85,12 @@
 import static android.os.Process.setThreadScheduler;
 import static android.os.Process.startWebView;
 import static android.os.Process.zygoteProcess;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
 import static android.provider.Settings.Global.DEBUG_APP;
-import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
-import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
-import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
-import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
 import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
-import static android.provider.Settings.System.FONT_SCALE;
-import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
-import static com.android.internal.util.XmlUtils.readBooleanAttribute;
-import static com.android.internal.util.XmlUtils.readIntAttribute;
-import static com.android.internal.util.XmlUtils.readLongAttribute;
-import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
-import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.internal.util.XmlUtils.writeLongAttribute;
+
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
@@ -141,8 +100,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CLEANUP;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
@@ -156,20 +113,14 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_URI_PERMISSION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
@@ -180,37 +131,15 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROVIDER;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMISSION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
 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.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
 import static com.android.server.am.MemoryStatUtil.hasMemcg;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
-import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
-import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
-import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
-import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
 
 import android.Manifest;
 import android.Manifest.permission;
@@ -221,22 +150,18 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
-import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityManagerInternal;
-import android.app.ActivityManagerInternal.ScreenObserver;
-import android.app.ActivityManagerInternal.SleepToken;
 import android.app.ActivityManagerProto;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
-import android.app.AlertDialog;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.AppOpsManagerInternal.CheckOpsDelegate;
 import android.app.ApplicationErrorReport;
 import android.app.ApplicationThreadConstants;
 import android.app.BroadcastOptions;
 import android.app.ContentProviderHolder;
 import android.app.Dialog;
-import android.app.GrantedUriPermission;
 import android.app.IActivityController;
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
@@ -254,30 +179,20 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.app.PictureInPictureParams;
 import android.app.ProcessMemoryState;
 import android.app.ProfilerInfo;
-import android.app.RemoteAction;
-import android.app.WaitResult;
 import android.app.WindowConfiguration.ActivityType;
 import android.app.WindowConfiguration.WindowingMode;
-import android.app.admin.DevicePolicyCache;
-import android.app.assist.AssistContent;
-import android.app.assist.AssistStructure;
 import android.app.backup.IBackupManager;
-import android.app.servertransaction.ConfigurationChangeItem;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
-import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
-import android.content.ClipData;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.IContentProvider;
 import android.content.IIntentReceiver;
 import android.content.IIntentSender;
@@ -294,6 +209,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManagerInternal.CheckPermissionDelegate;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PathPermission;
 import android.content.pm.PermissionInfo;
@@ -306,25 +222,21 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
-import android.graphics.Bitmap;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManagerInternal;
 import android.location.LocationManager;
 import android.media.audiofx.AudioEffect;
-import android.metrics.LogMaker;
 import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.os.BatteryStats;
 import android.os.Binder;
+import android.os.BinderProxy;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.DropBoxManager;
-import android.os.Environment;
 import android.os.FactoryTest;
-import android.os.FileObserver;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
@@ -332,16 +244,14 @@
 import android.os.IPermissionController;
 import android.os.IProcessInfoService;
 import android.os.IProgressListener;
-import android.os.LocaleList;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
-import android.os.PersistableBundle;
-import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
 import android.os.PowerManagerInternal;
 import android.os.Process;
+import android.os.Process.ProcessStartResult;
 import android.os.RemoteCallback;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -353,25 +263,18 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.TransactionTooLargeException;
-import android.os.UpdateLock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.WorkSource;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
-import android.provider.Downloads;
 import android.provider.Settings;
-import android.service.voice.IVoiceInteractionSession;
-import android.service.voice.VoiceInteractionManagerInternal;
-import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
-import android.text.format.Time;
 import android.text.style.SuggestionSpan;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.AtomicFile;
 import android.util.DebugUtils;
 import android.util.EventLog;
 import android.util.Log;
@@ -384,14 +287,11 @@
 import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.TimingsTraceLog;
-import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
 import android.view.Gravity;
 import android.view.IRecentsAnimationRunner;
 import android.view.LayoutInflater;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationDefinition;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.autofill.AutofillManagerInternal;
@@ -399,40 +299,33 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.AssistUtils;
 import com.android.internal.app.DumpHeapActivity;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
-import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.ProcessMap;
 import com.android.internal.app.SystemUserHomeActivity;
 import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.BinderInternal;
-import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.os.ByteTransferPipe;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.os.Zygote;
-import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.policy.KeyguardDismissCallback;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.MemInfoReader;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.TriFunction;
 import com.android.server.AlarmManagerInternal;
 import com.android.server.AppOpsService;
 import com.android.server.AttributeCache;
-import com.android.server.BinderCallsStatsService;
 import com.android.server.DeviceIdleController;
 import com.android.server.IntentResolver;
 import com.android.server.IoThread;
@@ -446,55 +339,37 @@
 import com.android.server.SystemServiceManager;
 import com.android.server.ThreadPriorityBooster;
 import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
-import com.android.server.am.ActivityManagerServiceProto;
-import com.android.server.am.ActivityManagerServiceDumpActivitiesProto;
-import com.android.server.am.ActivityManagerServiceDumpBroadcastsProto;
-import com.android.server.am.ActivityManagerServiceDumpProcessesProto;
-import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
-import com.android.server.am.ActivityManagerServiceDumpServicesProto;
-import com.android.server.am.GrantUriProto;
-import com.android.server.am.ImportanceTokenProto;
-import com.android.server.am.MemInfoDumpProto;
-import com.android.server.am.NeededUriGrantsProto;
-import com.android.server.am.ProcessOomProto;
-import com.android.server.am.ProcessToGcProto;
-import com.android.server.am.StickyBroadcastProto;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.dex.DexManager;
+import com.android.server.uri.GrantUri;
+import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.utils.PriorityDump;
 import com.android.server.vr.VrManagerInternal;
-import com.android.server.wm.PinnedStackWindowController;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerService;
 
 import dalvik.system.VMRuntime;
 
-import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.io.UnsupportedEncodingException;
 import java.lang.ref.WeakReference;
-import java.nio.charset.StandardCharsets;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -514,6 +389,7 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BiFunction;
 
 public class ActivityManagerService extends IActivityManager.Stub
         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@@ -528,8 +404,6 @@
     private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
     private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
-    private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
-    private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
     private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
     private static final String TAG_LRU = TAG + POSTFIX_LRU;
     private static final String TAG_MU = TAG + POSTFIX_MU;
@@ -540,13 +414,10 @@
     private static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
     private static final String TAG_PROVIDER = TAG + POSTFIX_PROVIDER;
     private static final String TAG_PSS = TAG + POSTFIX_PSS;
-    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
     private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
-    private static final String TAG_STACK = TAG + POSTFIX_STACK;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
     private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
     private static final String TAG_URI_PERMISSION = TAG + POSTFIX_URI_PERMISSION;
-    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
 
     // Mock "pretend we're idle now" broadcast action to the job scheduler; declared
     // here so that while the job scheduler can depend on AMS, the other way around
@@ -568,13 +439,11 @@
 
     static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
 
+    private static final String ANR_TRACE_DIR = "/data/anr";
+
     // Maximum number of receivers an app can register.
     private static final int MAX_RECEIVERS_ALLOWED_PER_APP = 1000;
 
-    // Amount of time after a call to stopAppSwitches() during which we will
-    // prevent further untrusted switches from happening.
-    static final long APP_SWITCH_DELAY_TIME = 5*1000;
-
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real.
     static final int PROC_START_TIMEOUT = 10*1000;
@@ -592,30 +461,10 @@
     static final int BROADCAST_FG_TIMEOUT = 10*1000;
     static final int BROADCAST_BG_TIMEOUT = 60*1000;
 
-    // How long we wait until we timeout on key dispatching.
-    static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
-
-    // How long we wait until we timeout on key dispatching during instrumentation.
-    static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT = 60*1000;
-
     // Disable hidden API checks for the newly started instrumentation.
     // Must be kept in sync with Am.
     private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
 
-    // How long to wait in getAssistContextExtras for the activity and foreground services
-    // to respond with the result.
-    static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
-
-    // How long top wait when going through the modern assist (which doesn't need to block
-    // on getting this result before starting to launch its UI).
-    static final int PENDING_ASSIST_EXTRAS_LONG_TIMEOUT = 2000;
-
-    // How long to wait in getAutofillAssistStructure() for the activity to respond with the result.
-    static final int PENDING_AUTOFILL_ASSIST_STRUCTURE_TIMEOUT = 2000;
-
-    // Maximum number of persisted Uri grants a package is allowed
-    static final int MAX_PERSISTED_URI_GRANTS = 128;
-
     static final int MY_PID = myPid();
 
     static final String[] EMPTY_STRING_ARRAY = new String[0];
@@ -629,11 +478,6 @@
     /** If a UID observer takes more than this long, send a WTF. */
     private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
 
-    // Access modes for handleIncomingUser.
-    static final int ALLOW_NON_FULL = 0;
-    static final int ALLOW_NON_FULL_IN_PROFILE = 1;
-    static final int ALLOW_FULL_ONLY = 2;
-
     // Necessary ApplicationInfo flags to mark an app as persistent
     private static final int PERSISTENT_MASK =
             ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
@@ -645,8 +489,8 @@
     // Used to indicate that an app transition should be animated.
     static final boolean ANIMATE = true;
 
-    // Determines whether to take full screen screenshots
-    static final boolean TAKE_FULLSCREEN_SCREENSHOTS = true;
+    // If set, we will push process association information in to procstats.
+    static final boolean TRACK_PROCSTATS_ASSOCIATIONS = true;
 
     /**
      * Default value for {@link Settings.Global#NETWORK_ACCESS_TIMEOUT_MS}.
@@ -680,24 +524,10 @@
     /** All system services */
     SystemServiceManager mSystemServiceManager;
 
-    // Wrapper around VoiceInteractionServiceManager
-    private AssistUtils mAssistUtils;
-
-    // Keeps track of the active voice interaction service component, notified from
-    // VoiceInteractionManagerService
-    ComponentName mActiveVoiceInteractionServiceComponent;
-
     private Installer mInstaller;
 
     /** Run all ActivityStacks through this */
-    final ActivityStackSupervisor mStackSupervisor;
-    private final KeyguardController mKeyguardController;
-
-    private final ActivityStartController mActivityStartController;
-
-    private final ClientLifecycleManager mLifecycleManager;
-
-    final TaskChangeNotificationController mTaskChangeNotificationController;
+    ActivityStackSupervisor mStackSupervisor;
 
     final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter();
 
@@ -705,21 +535,11 @@
 
     public final IntentFirewall mIntentFirewall;
 
-    // Whether we should show our dialogs (ANR, crash, etc) or just perform their
-    // default action automatically.  Important for devices without direct input
-    // devices.
-    private boolean mShowDialogs = true;
-
-    private final VrController mVrController;
-
-    // VR Vr2d Display Id.
-    int mVr2dDisplayId = INVALID_DISPLAY;
+    public OomAdjProfiler mOomAdjProfiler = new OomAdjProfiler();
 
     // Whether we should use SCHED_FIFO for UI and RenderThreads.
     private boolean mUseFifoUiScheduling = false;
 
-    private static final String SYSUI_COMPONENT_NAME = "com.android.systemui/.SystemUIService";
-
     BroadcastQueue mFgBroadcastQueue;
     BroadcastQueue mBgBroadcastQueue;
     // Convenient for easy iteration over the queues. Foreground is first
@@ -738,51 +558,14 @@
     }
 
     /**
-     * The last resumed activity. This is identical to the current resumed activity most
-     * of the time but could be different when we're pausing one activity before we resume
-     * another activity.
-     */
-    private ActivityRecord mLastResumedActivity;
-
-    /**
-     * The activity that is currently being traced as the active resumed activity.
-     *
-     * @see #updateResumedAppTrace
-     */
-    private @Nullable ActivityRecord mTracedResumedActivity;
-
-    /**
-     * If non-null, we are tracking the time the user spends in the currently focused app.
-     */
-    private AppTimeTracker mCurAppTimeTracker;
-
-    /**
-     * List of intents that were used to start the most recent tasks.
-     */
-    private final RecentTasks mRecentTasks;
-
-    /**
      * The package name of the DeviceOwner. This package is not permitted to have its data cleared.
      */
     String mDeviceOwnerName;
 
-    /**
-     * The controller for all operations related to locktask.
-     */
-    private final LockTaskController mLockTaskController;
-
     final UserController mUserController;
 
-    /**
-     * Packages that are being allowed to perform unrestricted app switches.  Mapping is
-     * User -> Type -> uid.
-     */
-    final SparseArray<ArrayMap<String, Integer>> mAllowAppSwitchUids = new SparseArray<>();
-
     final AppErrors mAppErrors;
 
-    final AppWarnings mAppWarnings;
-
     /**
      * Dump of the activity state at the time of the last ANR. Cleared after
      * {@link WindowManagerService#LAST_ANR_LIFETIME_DURATION_MSECS}
@@ -811,7 +594,7 @@
                 boolean asProto) {
             if (asProto) return;
             doDump(fd, pw, new String[]{"activities"}, asProto);
-            doDump(fd, pw, new String[]{"service", SYSUI_COMPONENT_NAME}, asProto);
+            doDump(fd, pw, new String[]{"service", "all-platform-critical"}, asProto);
         }
 
         @Override
@@ -825,15 +608,6 @@
         }
     };
 
-    public boolean canShowErrorDialogs() {
-        return mShowDialogs && !mSleeping && !mShuttingDown
-                && !mKeyguardController.isKeyguardOrAodShowing(DEFAULT_DISPLAY)
-                && !mUserController.hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
-                        mUserController.getCurrentUserId())
-                && !(UserManager.isDeviceInDemoMode(mContext)
-                        && mUserController.getCurrentUser().isDemo());
-    }
-
     private static ThreadPriorityBooster sThreadPriorityBooster = new ThreadPriorityBooster(
             THREAD_PRIORITY_FOREGROUND, LockGuard.INDEX_ACTIVITY);
 
@@ -845,45 +619,6 @@
         sThreadPriorityBooster.reset();
     }
 
-    public class PendingAssistExtras extends Binder implements Runnable {
-        public final ActivityRecord activity;
-        public boolean isHome;
-        public final Bundle extras;
-        public final Intent intent;
-        public final String hint;
-        public final IAssistDataReceiver receiver;
-        public final int userHandle;
-        public boolean haveResult = false;
-        public Bundle result = null;
-        public AssistStructure structure = null;
-        public AssistContent content = null;
-        public Bundle receiverExtras;
-
-        public PendingAssistExtras(ActivityRecord _activity, Bundle _extras, Intent _intent,
-                String _hint, IAssistDataReceiver _receiver, Bundle _receiverExtras,
-                int _userHandle) {
-            activity = _activity;
-            extras = _extras;
-            intent = _intent;
-            hint = _hint;
-            receiver = _receiver;
-            receiverExtras = _receiverExtras;
-            userHandle = _userHandle;
-        }
-
-        @Override
-        public void run() {
-            Slog.w(TAG, "getAssistContextExtras failed: timeout retrieving from " + activity);
-            synchronized (this) {
-                haveResult = true;
-                notifyAll();
-            }
-            pendingAssistExtrasTimedOut(this);
-        }
-    }
-
-    final ArrayList<PendingAssistExtras> mPendingAssistExtras = new ArrayList<>();
-
     /**
      * Process management.
      */
@@ -895,7 +630,22 @@
      * returned by the package manager), and the keys are ApplicationRecord
      * objects.
      */
-    final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
+    final MyProcessMap mProcessNames = new MyProcessMap();
+    final class MyProcessMap extends ProcessMap<ProcessRecord> {
+        @Override
+        public ProcessRecord put(String name, int uid, ProcessRecord value) {
+            final ProcessRecord r = super.put(name, uid, value);
+            mAtmInternal.onProcessAdded(r.getWindowProcessController());
+            return r;
+        }
+
+        @Override
+        public ProcessRecord remove(String name, int uid) {
+            final ProcessRecord r = super.remove(name, uid);
+            mAtmInternal.onProcessRemoved(name, uid);
+            return r;
+        }
+    }
 
     /**
      * Tracking long-term execution of processes to look for abuse and other
@@ -906,7 +656,7 @@
     /**
      * The currently running isolated processes.
      */
-    final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<ProcessRecord>();
+    final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
 
     /**
      * Counter for assigning isolated process uids, to avoid frequently reusing the
@@ -935,10 +685,42 @@
      * All of the processes we currently have running organized by pid.
      * The keys are the pid running the application.
      *
-     * <p>NOTE: This object is protected by its own lock, NOT the global
-     * activity manager lock!
+     * <p>NOTE: This object is protected by its own lock, NOT the global activity manager lock!
      */
-    final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();
+    final PidMap mPidsSelfLocked = new PidMap();
+    final class PidMap {
+        private final SparseArray<ProcessRecord> mPidMap = new SparseArray<>();
+
+        void put(int key, ProcessRecord value) {
+            mPidMap.put(key, value);
+            mAtmInternal.onProcessMapped(key, value.getWindowProcessController());
+        }
+
+        void remove(int pid) {
+            mPidMap.remove(pid);
+            mAtmInternal.onProcessUnMapped(pid);
+        }
+
+        ProcessRecord get(int pid) {
+            return mPidMap.get(pid);
+        }
+
+        int size() {
+            return mPidMap.size();
+        }
+
+        ProcessRecord valueAt(int index) {
+            return mPidMap.valueAt(index);
+        }
+
+        int keyAt(int index) {
+            return mPidMap.keyAt(index);
+        }
+
+        int indexOfKey(int key) {
+            return mPidMap.indexOfKey(key);
+        }
+    }
 
     /**
      * All of the processes that have been forced to be important.  The key
@@ -1033,23 +815,6 @@
     boolean mFullPssPending = false;
 
     /**
-     * This is the process holding what we currently consider to be
-     * the "home" activity.
-     */
-    ProcessRecord mHomeProcess;
-
-    /**
-     * This is the process holding the activity the user last visited that
-     * is in a different process from the one they are currently in.
-     */
-    ProcessRecord mPreviousProcess;
-
-    /**
-     * The time at which the previous process was last visible.
-     */
-    long mPreviousProcessVisibleTime;
-
-    /**
      * Track all uids that have actively running processes.
      */
     final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
@@ -1188,126 +953,12 @@
      * application is currently being launched and the provider will be
      * removed from this list once it is published.
      */
-    final ArrayList<ContentProviderRecord> mLaunchingProviders
-            = new ArrayList<ContentProviderRecord>();
-
-    /**
-     * File storing persisted {@link #mGrantedUriPermissions}.
-     */
-    private final AtomicFile mGrantFile;
-
-    /** XML constants used in {@link #mGrantFile} */
-    private static final String TAG_URI_GRANTS = "uri-grants";
-    private static final String TAG_URI_GRANT = "uri-grant";
-    private static final String ATTR_USER_HANDLE = "userHandle";
-    private static final String ATTR_SOURCE_USER_ID = "sourceUserId";
-    private static final String ATTR_TARGET_USER_ID = "targetUserId";
-    private static final String ATTR_SOURCE_PKG = "sourcePkg";
-    private static final String ATTR_TARGET_PKG = "targetPkg";
-    private static final String ATTR_URI = "uri";
-    private static final String ATTR_MODE_FLAGS = "modeFlags";
-    private static final String ATTR_CREATED_TIME = "createdTime";
-    private static final String ATTR_PREFIX = "prefix";
-
-    /**
-     * Global set of specific {@link Uri} permissions that have been granted.
-     * This optimized lookup structure maps from {@link UriPermission#targetUid}
-     * to {@link UriPermission#uri} to {@link UriPermission}.
-     */
-    @GuardedBy("this")
-    private final SparseArray<ArrayMap<GrantUri, UriPermission>>
-            mGrantedUriPermissions = new SparseArray<ArrayMap<GrantUri, UriPermission>>();
-
-    public static class GrantUri {
-        public final int sourceUserId;
-        public final Uri uri;
-        public boolean prefix;
-
-        public GrantUri(int sourceUserId, Uri uri, boolean prefix) {
-            this.sourceUserId = sourceUserId;
-            this.uri = uri;
-            this.prefix = prefix;
-        }
-
-        @Override
-        public int hashCode() {
-            int hashCode = 1;
-            hashCode = 31 * hashCode + sourceUserId;
-            hashCode = 31 * hashCode + uri.hashCode();
-            hashCode = 31 * hashCode + (prefix ? 1231 : 1237);
-            return hashCode;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o instanceof GrantUri) {
-                GrantUri other = (GrantUri) o;
-                return uri.equals(other.uri) && (sourceUserId == other.sourceUserId)
-                        && prefix == other.prefix;
-            }
-            return false;
-        }
-
-        @Override
-        public String toString() {
-            String result = uri.toString() + " [user " + sourceUserId + "]";
-            if (prefix) result += " [prefix]";
-            return result;
-        }
-
-        public String toSafeString() {
-            String result = uri.toSafeString() + " [user " + sourceUserId + "]";
-            if (prefix) result += " [prefix]";
-            return result;
-        }
-
-        public void writeToProto(ProtoOutputStream proto, long fieldId) {
-            long token = proto.start(fieldId);
-            proto.write(GrantUriProto.URI, uri.toString());
-            proto.write(GrantUriProto.SOURCE_USER_ID, sourceUserId);
-            proto.end(token);
-        }
-
-        public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
-            if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
-                return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
-                        ContentProvider.getUriWithoutUserId(uri), false);
-            } else {
-                return new GrantUri(defaultSourceUserHandle, uri, false);
-            }
-        }
-    }
+    final ArrayList<ContentProviderRecord> mLaunchingProviders = new ArrayList<>();
 
     boolean mSystemProvidersInstalled;
 
     CoreSettingsObserver mCoreSettingsObserver;
 
-    FontScaleSettingObserver mFontScaleSettingObserver;
-
-    private final class FontScaleSettingObserver extends ContentObserver {
-        private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE);
-        private final Uri mHideErrorDialogsUri = Settings.Global.getUriFor(HIDE_ERROR_DIALOGS);
-
-        public FontScaleSettingObserver() {
-            super(mHandler);
-            ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(mFontScaleUri, false, this, UserHandle.USER_ALL);
-            resolver.registerContentObserver(mHideErrorDialogsUri, false, this,
-                    UserHandle.USER_ALL);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
-            if (mFontScaleUri.equals(uri)) {
-                updateFontScaleIfNeeded(userId);
-            } else if (mHideErrorDialogsUri.equals(uri)) {
-                synchronized (ActivityManagerService.this) {
-                    updateShouldShowDialogsLocked(getGlobalConfiguration());
-                }
-            }
-        }
-    }
-
     DevelopmentSettingsObserver mDevelopmentSettingsObserver;
 
     private final class DevelopmentSettingsObserver extends ContentObserver {
@@ -1418,32 +1069,6 @@
      */
     final AppOpsService mAppOpsService;
 
-    /** Current sequencing integer of the configuration, for skipping old configurations. */
-    private int mConfigurationSeq;
-
-    /**
-     * Temp object used when global and/or display override configuration is updated. It is also
-     * sent to outer world instead of {@link #getGlobalConfiguration} because we don't trust
-     * anyone...
-     */
-    private Configuration mTempConfig = new Configuration();
-
-    private final UpdateConfigurationResult mTmpUpdateConfigurationResult =
-            new UpdateConfigurationResult();
-    private static final class UpdateConfigurationResult {
-        // Configuration changes that were updated.
-        int changes;
-        // If the activity was relaunched to match the new configuration.
-        boolean activityRelaunched;
-
-        void reset() {
-            changes = 0;
-            activityRelaunched = false;
-        }
-    }
-
-    boolean mSuppressResizeConfigChanges;
-
     /**
      * Hardware-reported OpenGLES version.
      */
@@ -1488,77 +1113,21 @@
     final Context mUiContext;
 
     /**
-     * The time at which we will allow normal application switches again,
-     * after a call to {@link #stopAppSwitches()}.
-     */
-    long mAppSwitchesAllowedTime;
-
-    /**
-     * This is set to true after the first switch after mAppSwitchesAllowedTime
-     * is set; any switches after that will clear the time.
-     */
-    boolean mDidAppSwitch;
-
-    /**
      * Last time (in uptime) at which we checked for power usage.
      */
     long mLastPowerCheckUptime;
 
     /**
-     * Set while we are wanting to sleep, to prevent any
-     * activities from being started/resumed.
-     *
-     * TODO(b/33594039): Clarify the actual state transitions represented by mSleeping.
-     *
-     * Currently mSleeping is set to true when transitioning into the sleep state, and remains true
-     * while in the sleep state until there is a pending transition out of sleep, in which case
-     * mSleeping is set to false, and remains false while awake.
-     *
-     * Whether mSleeping can quickly toggled between true/false without the device actually
-     * display changing states is undefined.
-     */
-    private boolean mSleeping = false;
-
-    /**
-     * The process state used for processes that are running the top activities.
-     * This changes between TOP and TOP_SLEEPING to following mSleeping.
-     */
-    int mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
-
-    /**
-     * Set while we are running a voice interaction.  This overrides
-     * sleeping while it is active.
-     */
-    IVoiceInteractionSession mRunningVoice;
-
-    /**
      * For some direct access we need to power manager.
      */
     PowerManagerInternal mLocalPowerManager;
 
     /**
-     * We want to hold a wake lock while running a voice interaction session, since
-     * this may happen with the screen off and we need to keep the CPU running to
-     * be able to continue to interact with the user.
-     */
-    PowerManager.WakeLock mVoiceWakeLock;
-
-    /**
      * State of external calls telling us if the device is awake or asleep.
      */
     private int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
 
     /**
-     * State of external calls telling us if the device is awake or asleep.
-     */
-    private boolean mKeyguardShown = false;
-
-    /**
-     * Set if we are shutting down the system, similar to sleeping.
-     */
-    boolean mShuttingDown = false;
-
-    /**
      * Current sequence id for oom_adj computation traversal.
      */
     int mAdjSeq = 0;
@@ -1655,29 +1224,7 @@
     String mOrigDebugApp = null;
     boolean mOrigWaitForDebugger = false;
     boolean mAlwaysFinishActivities = false;
-    boolean mForceResizableActivities;
-    /**
-     * Flag that indicates if multi-window is enabled.
-     *
-     * For any particular form of multi-window to be enabled, generic multi-window must be enabled
-     * in {@link com.android.internal.R.bool#config_supportsMultiWindow} config or
-     * {@link Settings.Global#DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES} development option set.
-     * At least one of the forms of multi-window must be enabled in order for this flag to be
-     * initialized to 'true'.
-     *
-     * @see #mSupportsSplitScreenMultiWindow
-     * @see #mSupportsFreeformWindowManagement
-     * @see #mSupportsPictureInPicture
-     * @see #mSupportsMultiDisplay
-     */
-    boolean mSupportsMultiWindow;
-    boolean mSupportsSplitScreenMultiWindow;
-    boolean mSupportsFreeformWindowManagement;
-    boolean mSupportsPictureInPicture;
-    boolean mSupportsMultiDisplay;
-    boolean mSupportsLeanbackOnly;
-    IActivityController mController = null;
-    boolean mControllerIsAMonkey = false;
+
     String mProfileApp = null;
     ProcessRecord mProfileProc = null;
     ProfilerInfo mProfilerInfo = null;
@@ -1801,8 +1348,6 @@
         }
     }
 
-    final List<ScreenObserver> mScreenObservers = new ArrayList<>();
-
     final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
     ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
 
@@ -1844,12 +1389,6 @@
     long mLastWriteTime = 0;
 
     /**
-     * Used to retain an update lock when the foreground activity is in
-     * immersive mode.
-     */
-    final UpdateLock mUpdateLock = new UpdateLock("immersive");
-
-    /**
      * Set to true after the system has finished booting.
      */
     boolean mBooted = false;
@@ -1860,6 +1399,9 @@
     int mBootPhase;
 
     WindowManagerService mWindowManager;
+    ActivityTaskManagerService mActivityTaskManager;
+    ActivityTaskManagerInternal mAtmInternal;
+    UriGrantsManagerInternal mUgmInternal;
     final ActivityThread mSystemThread;
 
     private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -1896,8 +1438,6 @@
     static final int WAIT_FOR_DEBUGGER_UI_MSG = 6;
     static final int SERVICE_TIMEOUT_MSG = 12;
     static final int UPDATE_TIME_ZONE = 13;
-    static final int SHOW_UID_ERROR_UI_MSG = 14;
-    static final int SHOW_FINGERPRINT_ERROR_UI_MSG = 15;
     static final int PROC_START_TIMEOUT_MSG = 20;
     static final int KILL_APPLICATION_MSG = 22;
     static final int FINALIZE_PENDING_INTENT_MSG = 23;
@@ -1907,30 +1447,20 @@
     static final int CHECK_EXCESSIVE_POWER_USE_MSG = 27;
     static final int CLEAR_DNS_CACHE_MSG = 28;
     static final int UPDATE_HTTP_PROXY_MSG = 29;
-    static final int SHOW_COMPAT_MODE_DIALOG_UI_MSG = 30;
     static final int DISPATCH_PROCESSES_CHANGED_UI_MSG = 31;
     static final int DISPATCH_PROCESS_DIED_UI_MSG = 32;
     static final int REPORT_MEM_USAGE_MSG = 33;
-    static final int IMMERSIVE_MODE_LOCK_MSG = 37;
-    static final int PERSIST_URI_GRANTS_MSG = 38;
     static final int UPDATE_TIME_PREFERENCE_MSG = 41;
-    static final int ENTER_ANIMATION_COMPLETE_MSG = 44;
     static final int FINISH_BOOTING_MSG = 45;
     static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
-    static final int DISMISS_DIALOG_UI_MSG = 48;
     static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49;
     static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50;
     static final int DELETE_DUMPHEAP_MSG = 51;
     static final int DISPATCH_UIDS_CHANGED_UI_MSG = 53;
-    static final int REPORT_TIME_TRACKER_MSG = 54;
     static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 56;
     static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 57;
     static final int IDLE_UIDS_MSG = 58;
-    static final int LOG_STACK_STATE = 60;
-    static final int VR_MODE_CHANGE_MSG = 61;
     static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
-    static final int DISPATCH_SCREEN_AWAKE_MSG = 64;
-    static final int DISPATCH_SCREEN_KEYGUARD_MSG = 65;
     static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
     static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
     static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
@@ -1947,7 +1477,6 @@
     static ServiceThread sKillThread = null;
     static KillHandler sKillHandler = null;
 
-    CompatModeDialog mCompatModeDialog;
     long mLastMemUsageReportTime = 0;
 
     /**
@@ -1956,11 +1485,6 @@
      */
     private boolean mUserIsMonkey;
 
-    /** The dimensions of the thumbnails in the Recents UI. */
-    int mThumbnailWidth;
-    int mThumbnailHeight;
-    float mFullscreenThumbnailScale;
-
     final ServiceThread mHandlerThread;
     final MainHandler mHandler;
     final Handler mUiHandler;
@@ -1974,12 +1498,6 @@
 
     PackageManagerInternal mPackageManagerInt;
 
-    // VoiceInteraction session ID that changes for each new request except when
-    // being called for multiwindow assist in a single session.
-    private int mViSessionId = 1000;
-
-    final boolean mPermissionReviewRequired;
-
     boolean mHasHeavyWeightFeature;
 
     /**
@@ -2050,7 +1568,7 @@
                         return;
                     }
                     AppErrorResult res = (AppErrorResult) data.get("result");
-                    if (mShowDialogs && !mSleeping && !mShuttingDown) {
+                    if (mAtmInternal.showStrictModeViolationDialog()) {
                         Dialog d = new StrictModeViolationDialog(mUiContext,
                                 ActivityManagerService.this, res, proc);
                         d.show();
@@ -2089,63 +1607,6 @@
                     }
                 }
             } break;
-            case SHOW_UID_ERROR_UI_MSG: {
-                if (mShowDialogs) {
-                    AlertDialog d = new BaseErrorDialog(mUiContext);
-                    d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
-                    d.setCancelable(false);
-                    d.setTitle(mUiContext.getText(R.string.android_system_label));
-                    d.setMessage(mUiContext.getText(R.string.system_error_wipe_data));
-                    d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.ok),
-                            obtainMessage(DISMISS_DIALOG_UI_MSG, d));
-                    d.show();
-                }
-            } break;
-            case SHOW_FINGERPRINT_ERROR_UI_MSG: {
-                if (mShowDialogs) {
-                    AlertDialog d = new BaseErrorDialog(mUiContext);
-                    d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
-                    d.setCancelable(false);
-                    d.setTitle(mUiContext.getText(R.string.android_system_label));
-                    d.setMessage(mUiContext.getText(R.string.system_error_manufacturer));
-                    d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.ok),
-                            obtainMessage(DISMISS_DIALOG_UI_MSG, d));
-                    d.show();
-                }
-            } break;
-            case SHOW_COMPAT_MODE_DIALOG_UI_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    ActivityRecord ar = (ActivityRecord) msg.obj;
-                    if (mCompatModeDialog != null) {
-                        if (mCompatModeDialog.mAppInfo.packageName.equals(
-                                ar.info.applicationInfo.packageName)) {
-                            return;
-                        }
-                        mCompatModeDialog.dismiss();
-                        mCompatModeDialog = null;
-                    }
-                    if (ar != null && false) {
-                        if (mCompatModePackages.getPackageAskCompatModeLocked(
-                                ar.packageName)) {
-                            int mode = mCompatModePackages.computeCompatModeLocked(
-                                    ar.info.applicationInfo);
-                            if (mode == ActivityManager.COMPAT_MODE_DISABLED
-                                    || mode == ActivityManager.COMPAT_MODE_ENABLED) {
-                                mCompatModeDialog = new CompatModeDialog(
-                                        ActivityManagerService.this, mUiContext,
-                                        ar.info.applicationInfo);
-                                mCompatModeDialog.show();
-                            }
-                        }
-                    }
-                }
-                break;
-            }
-            case DISMISS_DIALOG_UI_MSG: {
-                final Dialog d = (Dialog) msg.obj;
-                d.dismiss();
-                break;
-            }
             case DISPATCH_PROCESSES_CHANGED_UI_MSG: {
                 dispatchProcessesChanged();
                 break;
@@ -2208,6 +1669,9 @@
                     }
                 }
                 callbacks.finishBroadcast();
+                // We have to clean up the RemoteCallbackList here, because otherwise it will
+                // needlessly hold the enclosed callbacks until the remote process dies.
+                callbacks.kill();
             } break;
             case UPDATE_TIME_ZONE: {
                 synchronized (ActivityManagerService.this) {
@@ -2298,13 +1762,13 @@
                 }
 
                 ActivityRecord root = (ActivityRecord)msg.obj;
-                ProcessRecord process = root.app;
+                final WindowProcessController process = root.app;
                 if (process == null) {
                     return;
                 }
 
                 try {
-                    Context context = mContext.createPackageContext(process.info.packageName, 0);
+                    Context context = mContext.createPackageContext(process.mInfo.packageName, 0);
                     String text = mContext.getString(R.string.heavy_weight_notification,
                             context.getApplicationInfo().loadLabel(context.getPackageManager()));
                     Notification notification =
@@ -2368,24 +1832,6 @@
                 thread.start();
                 break;
             }
-            case IMMERSIVE_MODE_LOCK_MSG: {
-                final boolean nextState = (msg.arg1 != 0);
-                if (mUpdateLock.isHeld() != nextState) {
-                    if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE,
-                            "Applying new update lock state '" + nextState
-                            + "' for " + (ActivityRecord)msg.obj);
-                    if (nextState) {
-                        mUpdateLock.acquire();
-                    } else {
-                        mUpdateLock.release();
-                    }
-                }
-                break;
-            }
-            case PERSIST_URI_GRANTS_MSG: {
-                writeGrantedUriPermissions();
-                break;
-            }
             case UPDATE_TIME_PREFERENCE_MSG: {
                 // The user's time format preference might have changed.
                 // For convenience we re-use the Intent extra values.
@@ -2404,18 +1850,6 @@
                 }
                 break;
             }
-            case ENTER_ANIMATION_COMPLETE_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
-                    if (r != null && r.app != null && r.app.thread != null) {
-                        try {
-                            r.app.thread.scheduleEnterAnimationComplete(r.appToken);
-                        } catch (RemoteException e) {
-                        }
-                    }
-                }
-                break;
-            }
             case FINISH_BOOTING_MSG: {
                 if (msg.arg1 != 0) {
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
@@ -2423,7 +1857,7 @@
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                 }
                 if (msg.arg2 != 0) {
-                    enableScreenAfterBoot();
+                    mAtmInternal.enableScreenAfterBoot(mBooted);
                 }
                 break;
             }
@@ -2543,10 +1977,6 @@
                     mMemWatchDumpUid = -1;
                 }
             } break;
-            case REPORT_TIME_TRACKER_MSG: {
-                AppTimeTracker tracker = (AppTimeTracker)msg.obj;
-                tracker.deliverResult(mContext);
-            } break;
             case SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG: {
                 IUiAutomationConnection connection = (IUiAutomationConnection) msg.obj;
                 try {
@@ -2561,32 +1991,6 @@
             case IDLE_UIDS_MSG: {
                 idleUids();
             } break;
-            case VR_MODE_CHANGE_MSG: {
-                if (!mVrController.onVrModeChanged((ActivityRecord) msg.obj)) {
-                    return;
-                }
-                synchronized (ActivityManagerService.this) {
-                    final boolean disableNonVrUi = mVrController.shouldDisableNonVrUiLocked();
-                    mWindowManager.disableNonVrUi(disableNonVrUi);
-                    if (disableNonVrUi) {
-                        // If we are in a VR mode where Picture-in-Picture mode is unsupported,
-                        // then remove the pinned stack.
-                        mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
-                    }
-                }
-            } break;
-            case DISPATCH_SCREEN_AWAKE_MSG: {
-                final boolean isAwake = msg.arg1 != 0;
-                for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
-                    mScreenObservers.get(i).onAwakeStateChanged(isAwake);
-                }
-            } break;
-            case DISPATCH_SCREEN_KEYGUARD_MSG: {
-                final boolean isShowing = msg.arg1 != 0;
-                for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
-                    mScreenObservers.get(i).onKeyguardStateChanged(isShowing);
-                }
-            } break;
             case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
                 synchronized (ActivityManagerService.this) {
                     for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -2740,8 +2144,9 @@
 
             synchronized (this) {
                 ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
-                app.persistent = true;
+                app.setPersistent(true);
                 app.pid = MY_PID;
+                app.getWindowProcessController().setPid(MY_PID);
                 app.maxAdj = ProcessList.SYSTEM_ADJ;
                 app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
                 synchronized (mPidsSelfLocked) {
@@ -2772,8 +2177,8 @@
     public void setWindowManager(WindowManagerService wm) {
         synchronized (this) {
             mWindowManager = wm;
+            mActivityTaskManager.setWindowManager(wm);
             mStackSupervisor.setWindowManager(wm);
-            mLockTaskController.setWindowManager(wm);
         }
     }
 
@@ -2877,10 +2282,17 @@
 
     public static final class Lifecycle extends SystemService {
         private final ActivityManagerService mService;
+        private static ActivityTaskManagerService sAtm;
 
         public Lifecycle(Context context) {
             super(context);
-            mService = new ActivityManagerService(context);
+            mService = new ActivityManagerService(context, sAtm);
+        }
+
+        public static ActivityManagerService startService(
+                SystemServiceManager ssm, ActivityTaskManagerService atm) {
+            sAtm = atm;
+            return ssm.startService(ActivityManagerService.Lifecycle.class).getService();
         }
 
         @Override
@@ -3012,32 +2424,21 @@
         mContext = mInjector.getContext();
         mUiContext = null;
         GL_ES_VERSION = 0;
-        mActivityStartController = null;
         mAppErrors = null;
-        mAppWarnings = null;
         mAppOpsService = mInjector.getAppOpsService(null, null);
         mBatteryStatsService = null;
         mCompatModePackages = null;
         mConstants = null;
-        mGrantFile = null;
         mHandler = null;
         mHandlerThread = null;
         mIntentFirewall = null;
-        mKeyguardController = null;
-        mPermissionReviewRequired = false;
         mProcessCpuThread = null;
         mProcessStats = null;
         mProviderMap = null;
-        mRecentTasks = null;
         mServices = null;
-        mStackSupervisor = null;
         mSystemThread = null;
-        mTaskChangeNotificationController = null;
         mUiHandler = injector.getUiHandler(null);
         mUserController = null;
-        mVrController = null;
-        mLockTaskController = null;
-        mLifecycleManager = null;
         mProcStartHandlerThread = null;
         mProcStartHandler = null;
         mHiddenApiBlacklist = null;
@@ -3045,7 +2446,7 @@
 
     // Note: This method is invoked on the main thread but may need to attach various
     // handlers to other threads.  So take care to be explicit about the looper.
-    public ActivityManagerService(Context systemContext) {
+    public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
         LockGuard.installLock(this, LockGuard.INDEX_ACTIVITY);
         mInjector = new Injector();
         mContext = systemContext;
@@ -3056,9 +2457,6 @@
 
         Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
 
-        mPermissionReviewRequired = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_permissionReviewRequired);
-
         mHandlerThread = new ServiceThread(TAG,
                 THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
         mHandlerThread.start();
@@ -3091,11 +2489,7 @@
         mProviderMap = new ProviderMap(this);
         mAppErrors = new AppErrors(mUiContext, this);
 
-        File dataDir = Environment.getDataDirectory();
-        File systemDir = new File(dataDir, "system");
-        systemDir.mkdirs();
-
-        mAppWarnings = new AppWarnings(this, mUiContext, mHandler, mUiHandler, systemDir);
+        final File systemDir = SystemServiceManager.ensureSystemDir();
 
         // TODO: Move creation of battery stats service outside of activity manager service.
         mBatteryStatsService = new BatteryStatsService(systemContext, systemDir, mHandler);
@@ -3104,17 +2498,16 @@
         mOnBattery = DEBUG_POWER ? true
                 : mBatteryStatsService.getActiveStatistics().getIsOnBattery();
         mBatteryStatsService.getActiveStatistics().setCallback(this);
+        mOomAdjProfiler.batteryPowerChanged(mOnBattery);
 
         mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
 
         mAppOpsService = mInjector.getAppOpsService(new File(systemDir, "appops.xml"), mHandler);
 
-        mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants");
+        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
 
         mUserController = new UserController(this);
 
-        mVrController = new VrController(this);
-
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
             ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
 
@@ -3123,21 +2516,13 @@
         }
 
         mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
-        mTempConfig.setToDefaults();
-        mTempConfig.setLocales(LocaleList.getDefault());
-        mConfigurationSeq = mTempConfig.seq = 1;
-        mStackSupervisor = createStackSupervisor();
-        mStackSupervisor.onConfigurationChanged(mTempConfig);
-        mKeyguardController = mStackSupervisor.getKeyguardController();
         mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
         mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
-        mTaskChangeNotificationController =
-                new TaskChangeNotificationController(this, mStackSupervisor, mHandler);
-        mActivityStartController = new ActivityStartController(this);
-        mRecentTasks = createRecentTasks();
-        mStackSupervisor.setRecentTasks(mRecentTasks);
-        mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler);
-        mLifecycleManager = new ClientLifecycleManager();
+
+        mActivityTaskManager = atm;
+        mActivityTaskManager.setActivityManagerService(this);
+        mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+        mStackSupervisor = mActivityTaskManager.mStackSupervisor;
 
         mProcessCpuThread = new Thread("CpuTracker") {
             @Override
@@ -3191,20 +2576,6 @@
 
     }
 
-    protected ActivityStackSupervisor createStackSupervisor() {
-        final ActivityStackSupervisor supervisor = new ActivityStackSupervisor(this, mHandler.getLooper());
-        supervisor.initialize();
-        return supervisor;
-    }
-
-    protected RecentTasks createRecentTasks() {
-        return new RecentTasks(this, mStackSupervisor);
-    }
-
-    RecentTasks getRecentTasks() {
-        return mRecentTasks;
-    }
-
     public void setSystemServiceManager(SystemServiceManager mgr) {
         mSystemServiceManager = mgr;
     }
@@ -3221,8 +2592,10 @@
         mAppOpsService.publish(mContext);
         Slog.d("AppOps", "AppOpsService published");
         LocalServices.addService(ActivityManagerInternal.class, new LocalService());
+        mActivityTaskManager.onActivityManagerInternalAdded();
+        mUgmInternal.onActivityManagerInternalAdded();
         // Wait for the synchronized block started in mProcessCpuThread,
-        // so that any other acccess to mProcessCpuTracker from main thread
+        // so that any other access to mProcessCpuTracker from main thread
         // will be blocked during mProcessCpuTracker initialization.
         try {
             mProcessCpuInitLatch.await();
@@ -3233,18 +2606,10 @@
         }
     }
 
-    void onUserStoppedLocked(int userId) {
-        mRecentTasks.unloadUserDataFromMemoryLocked(userId);
-        mAllowAppSwitchUids.remove(userId);
-    }
-
     public void initPowerManagement() {
-        mStackSupervisor.initPowerManagement();
+        mActivityTaskManager.onInitPowerManagement();
         mBatteryStatsService.initPowerManagement();
         mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
-        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
-        mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
-        mVoiceWakeLock.setReferenceCounted(false);
     }
 
     private ArraySet<String> getBackgroundLaunchBroadcasts() {
@@ -3423,12 +2788,13 @@
             synchronized(mPidsSelfLocked) {
                 mOnBattery = DEBUG_POWER ? true : onBattery;
             }
+            mOomAdjProfiler.batteryPowerChanged(onBattery);
         }
     }
 
     @Override
     public void batteryStatsReset() {
-        BinderCallsStatsService.reset();
+        mOomAdjProfiler.reset();
     }
 
     @Override
@@ -3495,224 +2861,30 @@
         }
     }
 
-    /**
-     * Update AMS states when an activity is resumed. This should only be called by
-     * {@link ActivityStack#onActivityStateChanged(ActivityRecord, ActivityState, String)} when an
-     * activity is resumed.
-     */
-    @GuardedBy("this")
-    void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
-        final TaskRecord task = r.getTask();
-        if (task.isActivityTypeStandard()) {
-            if (mCurAppTimeTracker != r.appTimeTracker) {
-                // We are switching app tracking.  Complete the current one.
-                if (mCurAppTimeTracker != null) {
-                    mCurAppTimeTracker.stop();
-                    mHandler.obtainMessage(
-                            REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget();
-                    mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
-                    mCurAppTimeTracker = null;
-                }
-                if (r.appTimeTracker != null) {
-                    mCurAppTimeTracker = r.appTimeTracker;
-                    startTimeTrackingFocusedActivityLocked();
-                }
-            } else {
-                startTimeTrackingFocusedActivityLocked();
-            }
-        } else {
-            r.appTimeTracker = null;
-        }
-        // TODO: VI Maybe r.task.voiceInteractor || r.voiceInteractor != null
-        // TODO: Probably not, because we don't want to resume voice on switching
-        // back to this activity
-        if (task.voiceInteractor != null) {
-            startRunningVoiceLocked(task.voiceSession, r.info.applicationInfo.uid);
-        } else {
-            finishRunningVoiceLocked();
-
-            if (mLastResumedActivity != null) {
-                final IVoiceInteractionSession session;
-
-                final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask();
-                if (lastResumedActivityTask != null
-                        && lastResumedActivityTask.voiceSession != null) {
-                    session = lastResumedActivityTask.voiceSession;
-                } else {
-                    session = mLastResumedActivity.voiceSession;
-                }
-
-                if (session != null) {
-                    // We had been in a voice interaction session, but now focused has
-                    // move to something different.  Just finish the session, we can't
-                    // return to it and retain the proper state and synchronization with
-                    // the voice interaction service.
-                    finishVoiceTask(session);
-                }
-            }
-        }
-
-        if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) {
-            mUserController.sendForegroundProfileChanged(r.userId);
-        }
-        updateResumedAppTrace(r);
-        mLastResumedActivity = r;
-
-        mWindowManager.setFocusedApp(r.appToken, true);
-
-        applyUpdateLockStateLocked(r);
-        applyUpdateVrModeLocked(r);
-
-        EventLogTags.writeAmSetResumedActivity(
-                r == null ? -1 : r.userId,
-                r == null ? "NULL" : r.shortComponentName,
-                reason);
-    }
-
-    private void updateResumedAppTrace(@Nullable ActivityRecord resumed) {
-        if (mTracedResumedActivity != null) {
-            Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER,
-                    constructResumedTraceName(mTracedResumedActivity.packageName), 0);
-        }
-        if (resumed != null) {
-            Trace.asyncTraceBegin(TRACE_TAG_ACTIVITY_MANAGER,
-                    constructResumedTraceName(resumed.packageName), 0);
-        }
-        mTracedResumedActivity = resumed;
-    }
-
-    private String constructResumedTraceName(String packageName) {
-        return "focused app: " + packageName;
-    }
-
     @Override
     public void setFocusedStack(int stackId) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
-        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId);
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
-                if (stack == null) {
-                    Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
-                    return;
-                }
-                final ActivityRecord r = stack.topRunningActivityLocked();
-                if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, "setFocusedStack")) {
-                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-    }
-
-    @Override
-    public void setFocusedTask(int taskId) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
-        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task == null) {
-                    return;
-                }
-                final ActivityRecord r = task.topRunningActivityLocked();
-                if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, "setFocusedTask")) {
-                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
+        mActivityTaskManager.setFocusedStack(stackId);
     }
 
     /** Sets the task stack listener that gets callbacks when a task stack changes. */
     @Override
-    public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "registerTaskStackListener()");
-        mTaskChangeNotificationController.registerTaskStackListener(listener);
+    public void registerTaskStackListener(ITaskStackListener listener) {
+        mActivityTaskManager.registerTaskStackListener(listener);
     }
 
     /**
      * Unregister a task stack listener so that it stops receiving callbacks.
      */
     @Override
-    public void unregisterTaskStackListener(ITaskStackListener listener) throws RemoteException {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "unregisterTaskStackListener()");
-         mTaskChangeNotificationController.unregisterTaskStackListener(listener);
-     }
-
-    @Override
-    public void notifyActivityDrawn(IBinder token) {
-        if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
-        synchronized (this) {
-            ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token);
-            if (r != null) {
-                r.getStack().notifyActivityDrawnLocked(r);
-            }
-        }
-    }
-
-    final void applyUpdateLockStateLocked(ActivityRecord r) {
-        // Modifications to the UpdateLock state are done on our handler, outside
-        // the activity manager's locks.  The new state is determined based on the
-        // state *now* of the relevant activity record.  The object is passed to
-        // the handler solely for logging detail, not to be consulted/modified.
-        final boolean nextState = r != null && r.immersive;
-        mHandler.sendMessage(
-                mHandler.obtainMessage(IMMERSIVE_MODE_LOCK_MSG, (nextState) ? 1 : 0, 0, r));
-    }
-
-    final void applyUpdateVrModeLocked(ActivityRecord r) {
-        // VR apps are expected to run in a main display. If an app is turning on VR for
-        // itself, but lives in a dynamic stack, then make sure that it is moved to the main
-        // fullscreen stack before enabling VR Mode.
-        // TODO: The goal of this code is to keep the VR app on the main display. When the
-        // stack implementation changes in the future, keep in mind that the use of the fullscreen
-        // stack is a means to move the activity to the main display and a moveActivityToDisplay()
-        // option would be a better choice here.
-        if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
-            Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
-                    + " to main stack for VR");
-            final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
-                    WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
-            moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */);
-        }
-        mHandler.sendMessage(
-                mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
-    }
-
-    final void showAskCompatModeDialogLocked(ActivityRecord r) {
-        Message msg = Message.obtain();
-        msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
-        msg.obj = r.getTask().askedCompatMode ? null : r;
-        mUiHandler.sendMessage(msg);
-    }
-
-    final AppWarnings getAppWarningsLocked() {
-        return mAppWarnings;
-    }
-
-    /**
-     * Shows app warning dialogs, if necessary.
-     *
-     * @param r activity record for which the warnings may be displayed
-     */
-    final void showAppWarningsIfNeededLocked(ActivityRecord r) {
-        mAppWarnings.showUnsupportedCompileSdkDialogIfNeeded(r);
-        mAppWarnings.showUnsupportedDisplaySizeDialogIfNeeded(r);
-        mAppWarnings.showDeprecatedTargetDialogIfNeeded(r);
+    public void unregisterTaskStackListener(ITaskStackListener listener) {
+        mActivityTaskManager.unregisterTaskStackListener(listener);
     }
 
     private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
             String what, Object obj, ProcessRecord srcApp) {
         app.lastActivityTime = now;
 
-        if (app.activities.size() > 0 || app.recentTasks.size() > 0) {
+        if (app.hasActivitiesOrRecentTasks()) {
             // Don't want to touch dependent processes that are hosting activities.
             return index;
         }
@@ -3759,7 +2931,7 @@
         int lrui = mLruProcesses.lastIndexOf(app);
         if (lrui >= 0) {
             if (!app.killed) {
-                if (app.persistent) {
+                if (app.isPersistent()) {
                     Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
                 } else {
                     Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
@@ -3783,8 +2955,8 @@
 
     final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
             ProcessRecord client) {
-        final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
-                || app.treatLikeActivity || app.recentTasks.size() > 0;
+        final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities
+                || app.treatLikeActivity;
         final boolean hasService = false; // not impl yet. app.services.size() > 0;
         if (!activityChange && hasActivity) {
             // The process has activities, so we are only allowing activity-based adjustments
@@ -3816,7 +2988,7 @@
 
         int lrui = mLruProcesses.lastIndexOf(app);
 
-        if (app.persistent && lrui >= 0) {
+        if (app.isPersistent() && lrui >= 0) {
             // We don't care about the position of persistent processes, as long as
             // they are in the list.
             if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
@@ -3888,7 +3060,7 @@
         int nextIndex;
         if (hasActivity) {
             final int N = mLruProcesses.size();
-            if ((app.activities.size() == 0 || app.recentTasks.size() > 0)
+            if ((!app.hasActivities() || app.hasRecentTasks())
                     && mLruProcessActivityStart < (N - 1)) {
                 // Process doesn't have activities, but has clients with
                 // activities...  move it up, but one below the top (the top
@@ -3963,14 +3135,14 @@
             if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
                     && cr.binding.service.app != null
                     && cr.binding.service.app.lruSeq != mLruSeq
-                    && !cr.binding.service.app.persistent) {
+                    && !cr.binding.service.app.isPersistent()) {
                 nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
                         "service connection", cr, app);
             }
         }
         for (int j=app.conProviders.size()-1; j>=0; j--) {
             ContentProviderRecord cpr = app.conProviders.get(j).provider;
-            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {
+            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
                 nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
                         "provider reference", cpr, app);
             }
@@ -4000,7 +3172,15 @@
                 && proc.lastCachedPss >= 4000) {
             // Turn this condition on to cause killing to happen regularly, for testing.
             if (proc.baseProcessTracker != null) {
-                proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
+                proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
+                for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                    ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+                    StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
+                            proc.info.uid,
+                            holder.state.getName(),
+                            holder.state.getPackage(),
+                            proc.lastCachedPss, holder.appVersion);
+                }
             }
             proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
         } else if (proc != null && !keepIfLarge
@@ -4009,7 +3189,15 @@
             if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
             if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
                 if (proc.baseProcessTracker != null) {
-                    proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
+                    proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
+                    for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                        ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+                        StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
+                                proc.info.uid,
+                                holder.state.getName(),
+                                holder.state.getPackage(),
+                                proc.lastCachedPss, holder.appVersion);
+                    }
                 }
                 proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
             }
@@ -4023,13 +3211,6 @@
         }
     }
 
-    boolean isNextTransitionForward() {
-        int transit = mWindowManager.getPendingAppTransition();
-        return transit == TRANSIT_ACTIVITY_OPEN
-                || transit == TRANSIT_TASK_OPEN
-                || transit == TRANSIT_TASK_TO_FRONT;
-    }
-
     boolean startIsolatedProcess(String entryPoint, String[] entryPointArgs,
             String processName, String abiOverride, int uid, Runnable crashHandler) {
         synchronized(this) {
@@ -4322,7 +3503,7 @@
             }
 
             if (app.info.isPrivilegedApp() &&
-                    DexManager.isPackageSelectedToRunOob(app.pkgList.keySet())) {
+                    DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet())) {
                 runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
             }
 
@@ -4364,7 +3545,7 @@
             }
 
             app.gids = gids;
-            app.requiredAbi = requiredAbi;
+            app.setRequiredAbi(requiredAbi);
             app.instructionSet = instructionSet;
 
             // the per-user SELinux context must be set
@@ -4392,7 +3573,7 @@
             // the package was initially frozen through KILL_APPLICATION_MSG, so
             // it doesn't hurt to use it again.)
             forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid), false,
-                    false, true, false, false, UserHandle.getUserId(app.userId), "start failure");
+                    false, true, false, false, app.userId, "start failure");
             return false;
         }
     }
@@ -4421,8 +3602,8 @@
                             app.pendingStart = false;
                             return;
                         }
-                        app.usingWrapper = invokeWith != null
-                                || SystemProperties.get("wrap." + app.processName) != null;
+                        app.setUsingWrapper(invokeWith != null
+                                || SystemProperties.get("wrap." + app.processName) != null);
                         mPendingStarts.put(startSeq, app);
                     }
                     final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint,
@@ -4437,8 +3618,7 @@
                         mPendingStarts.remove(startSeq);
                         app.pendingStart = false;
                         forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
-                                false, false, true, false, false,
-                                UserHandle.getUserId(app.userId), "start failure");
+                                false, false, true, false, false, app.userId, "start failure");
                     }
                 }
             });
@@ -4454,8 +3634,7 @@
                 Slog.e(TAG, "Failure starting process " + app.processName, e);
                 app.pendingStart = false;
                 forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
-                        false, false, true, false, false,
-                        UserHandle.getUserId(app.userId), "start failure");
+                        false, false, true, false, false, app.userId, "start failure");
             }
             return app.pid > 0;
         }
@@ -4474,13 +3653,13 @@
                 startResult = startWebView(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
-                        app.info.dataDir, null,
+                        app.info.dataDir, null, app.info.packageName,
                         new String[] {PROC_START_SEQ_IDENT + app.startSeq});
             } else {
                 startResult = Process.start(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
-                        app.info.dataDir, invokeWith,
+                        app.info.dataDir, invokeWith, app.info.packageName,
                         new String[] {PROC_START_SEQ_IDENT + app.startSeq});
             }
             checkTime(startTime, "startProcess: returned from zygote!");
@@ -4518,7 +3697,7 @@
         // Indicates that this process start has been taken care of.
         if (mPendingStarts.get(expectedStartSeq) == null) {
             if (pending.pid == startResult.pid) {
-                pending.usingWrapper = startResult.usingWrapper;
+                pending.setUsingWrapper(startResult.usingWrapper);
                 // TODO: Update already existing clients of usingWrapper
             }
             return false;
@@ -4555,7 +3734,7 @@
             // Ignore
         }
 
-        if (app.persistent) {
+        if (app.isPersistent()) {
             Watchdog.getInstance().processStarted(app.processName, pid);
         }
 
@@ -4581,7 +3760,7 @@
         }
         reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
         app.setPid(pid);
-        app.usingWrapper = usingWrapper;
+        app.setUsingWrapper(usingWrapper);
         app.pendingStart = false;
         checkTime(app.startTime, "startProcess: starting to update pids map");
         ProcessRecord oldApp;
@@ -4614,7 +3793,7 @@
                 "updateUsageStats: comp=" + component + "res=" + resumed);
         final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
         StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
-            component.app.uid, component.realActivity.getPackageName(),
+            component.app.mUid, component.realActivity.getPackageName(),
             component.realActivity.getShortClassName(), resumed ?
                         StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
                         StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND);
@@ -4625,7 +3804,7 @@
 
             }
             synchronized (stats) {
-                stats.noteActivityResumedLocked(component.app.uid);
+                stats.noteActivityResumedLocked(component.app.mUid);
             }
         } else {
             if (mUsageStatsService != null) {
@@ -4633,7 +3812,7 @@
                         UsageEvents.Event.MOVE_TO_BACKGROUND);
             }
             synchronized (stats) {
-                stats.noteActivityPausedLocked(component.app.uid);
+                stats.noteActivityPausedLocked(component.app.mUid);
             }
         }
     }
@@ -4666,13 +3845,13 @@
             aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
             ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                     aInfo.applicationInfo.uid, true);
-            if (app == null || app.instr == null) {
+            if (app == null || app.getActiveInstrumentation() == null) {
                 intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
                 final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
                 // For ANR debugging to verify if the user activity is the one that actually
                 // launched.
                 final String myReason = reason + ":" + userId + ":" + resolvedUserId;
-                mActivityStartController.startHomeActivity(intent, aInfo, myReason);
+                mActivityTaskManager.getActivityStartController().startHomeActivity(intent, aInfo, myReason);
             }
         } else {
             Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
@@ -4717,30 +3896,13 @@
         return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
     }
 
-    void enforceNotIsolatedCaller(String caller) {
+    private void enforceNotIsolatedCaller(String caller) {
         if (UserHandle.isIsolated(Binder.getCallingUid())) {
             throw new SecurityException("Isolated process not allowed to call " + caller);
         }
     }
 
     @Override
-    public int getFrontActivityScreenCompatMode() {
-        enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
-        synchronized (this) {
-            return mCompatModePackages.getFrontActivityScreenCompatModeLocked();
-        }
-    }
-
-    @Override
-    public void setFrontActivityScreenCompatMode(int mode) {
-        enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
-                "setFrontActivityScreenCompatMode");
-        synchronized (this) {
-            mCompatModePackages.setFrontActivityScreenCompatModeLocked(mode);
-        }
-    }
-
-    @Override
     public int getPackageScreenCompatMode(String packageName) {
         enforceNotIsolatedCaller("getPackageScreenCompatMode");
         synchronized (this) {
@@ -5077,558 +4239,37 @@
     }
 
     @Override
-    public final int startActivity(IApplicationThread caller, String callingPackage,
+    public int startActivity(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
-        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
-                resultWho, requestCode, startFlags, profilerInfo, bOptions,
-                UserHandle.getCallingUserId());
+        return mActivityTaskManager.startActivity(caller, callingPackage, intent, resolvedType,
+                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);
     }
 
     @Override
     public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
-        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
-                resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
-                true /*validateIncomingUser*/);
-    }
-
-    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
-            boolean validateIncomingUser) {
-        enforceNotIsolatedCaller("startActivity");
-
-        userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser,
-                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
-
-        // TODO: Switch to user app stacks here.
-        return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
-                .setCaller(caller)
-                .setCallingPackage(callingPackage)
-                .setResolvedType(resolvedType)
-                .setResultTo(resultTo)
-                .setResultWho(resultWho)
-                .setRequestCode(requestCode)
-                .setStartFlags(startFlags)
-                .setProfilerInfo(profilerInfo)
-                .setActivityOptions(bOptions)
-                .setMayWait(userId)
-                .execute();
-
-    }
-
-    @Override
-    public final int startActivityAsCaller(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, boolean ignoreTargetSecurity,
-            int userId) {
-
-        // This is very dangerous -- it allows you to perform a start activity (including
-        // permission grants) as any app that may launch one of your own activities.  So
-        // we will only allow this to be done from activities that are part of the core framework,
-        // and then only when they are running as the system.
-        final ActivityRecord sourceRecord;
-        final int targetUid;
-        final String targetPackage;
-        final boolean isResolver;
-        synchronized (this) {
-            if (resultTo == null) {
-                throw new SecurityException("Must be called from an activity");
-            }
-            sourceRecord = mStackSupervisor.isInAnyStackLocked(resultTo);
-            if (sourceRecord == null) {
-                throw new SecurityException("Called with bad activity token: " + resultTo);
-            }
-            if (!sourceRecord.info.packageName.equals("android")) {
-                throw new SecurityException(
-                        "Must be called from an activity that is declared in the android package");
-            }
-            if (sourceRecord.app == null) {
-                throw new SecurityException("Called without a process attached to activity");
-            }
-            if (UserHandle.getAppId(sourceRecord.app.uid) != SYSTEM_UID) {
-                // This is still okay, as long as this activity is running under the
-                // uid of the original calling activity.
-                if (sourceRecord.app.uid != sourceRecord.launchedFromUid) {
-                    throw new SecurityException(
-                            "Calling activity in uid " + sourceRecord.app.uid
-                                    + " must be system uid or original calling uid "
-                                    + sourceRecord.launchedFromUid);
-                }
-            }
-            if (ignoreTargetSecurity) {
-                if (intent.getComponent() == null) {
-                    throw new SecurityException(
-                            "Component must be specified with ignoreTargetSecurity");
-                }
-                if (intent.getSelector() != null) {
-                    throw new SecurityException(
-                            "Selector not allowed with ignoreTargetSecurity");
-                }
-            }
-            targetUid = sourceRecord.launchedFromUid;
-            targetPackage = sourceRecord.launchedFromPackage;
-            isResolver = sourceRecord.isResolverOrChildActivity();
-        }
-
-        if (userId == UserHandle.USER_NULL) {
-            userId = UserHandle.getUserId(sourceRecord.app.uid);
-        }
-
-        // TODO: Switch to user app stacks here.
-        try {
-            return mActivityStartController.obtainStarter(intent, "startActivityAsCaller")
-                    .setCallingUid(targetUid)
-                    .setCallingPackage(targetPackage)
-                    .setResolvedType(resolvedType)
-                    .setResultTo(resultTo)
-                    .setResultWho(resultWho)
-                    .setRequestCode(requestCode)
-                    .setStartFlags(startFlags)
-                    .setActivityOptions(bOptions)
-                    .setMayWait(userId)
-                    .setIgnoreTargetSecurity(ignoreTargetSecurity)
-                    .setFilterCallingUid(isResolver ? 0 /* system */ : targetUid)
-                    .execute();
-        } catch (SecurityException e) {
-            // XXX need to figure out how to propagate to original app.
-            // A SecurityException here is generally actually a fault of the original
-            // calling activity (such as a fairly granting permissions), so propagate it
-            // back to them.
-            /*
-            StringBuilder msg = new StringBuilder();
-            msg.append("While launching");
-            msg.append(intent.toString());
-            msg.append(": ");
-            msg.append(e.getMessage());
-            */
-            throw e;
-        }
-    }
-
-    @Override
-    public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
-        enforceNotIsolatedCaller("startActivityAndWait");
-        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
-                userId, false, ALLOW_FULL_ONLY, "startActivityAndWait", null);
-        WaitResult res = new WaitResult();
-        // TODO: Switch to user app stacks here.
-        mActivityStartController.obtainStarter(intent, "startActivityAndWait")
-                .setCaller(caller)
-                .setCallingPackage(callingPackage)
-                .setResolvedType(resolvedType)
-                .setResultTo(resultTo)
-                .setResultWho(resultWho)
-                .setRequestCode(requestCode)
-                .setStartFlags(startFlags)
-                .setActivityOptions(bOptions)
-                .setMayWait(userId)
-                .setProfilerInfo(profilerInfo)
-                .setWaitResult(res)
-                .execute();
-        return res;
-    }
-
-    @Override
-    public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, Configuration config, Bundle bOptions, int userId) {
-        enforceNotIsolatedCaller("startActivityWithConfig");
-        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
-                userId, false, ALLOW_FULL_ONLY, "startActivityWithConfig", null);
-        // TODO: Switch to user app stacks here.
-        return mActivityStartController.obtainStarter(intent, "startActivityWithConfig")
-                .setCaller(caller)
-                .setCallingPackage(callingPackage)
-                .setResolvedType(resolvedType)
-                .setResultTo(resultTo)
-                .setResultWho(resultWho)
-                .setRequestCode(requestCode)
-                .setStartFlags(startFlags)
-                .setGlobalConfiguration(config)
-                .setActivityOptions(bOptions)
-                .setMayWait(userId)
-                .execute();
-    }
-
-    @Override
-    public int startActivityIntentSender(IApplicationThread caller, IIntentSender target,
-            IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo,
-            String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions)
-            throws TransactionTooLargeException {
-        enforceNotIsolatedCaller("startActivityIntentSender");
-        // Refuse possible leaked file descriptors
-        if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
-
-        if (!(target instanceof PendingIntentRecord)) {
-            throw new IllegalArgumentException("Bad PendingIntent object");
-        }
-
-        PendingIntentRecord pir = (PendingIntentRecord)target;
-
-        synchronized (this) {
-            // If this is coming from the currently resumed activity, it is
-            // effectively saying that app switches are allowed at this point.
-            final ActivityStack stack = getFocusedStack();
-            if (stack.mResumedActivity != null &&
-                    stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
-                mAppSwitchesAllowedTime = 0;
-            }
-        }
-        int ret = pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null,
-                resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions);
-        return ret;
-    }
-
-    @Override
-    public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
-            Intent intent, String resolvedType, IVoiceInteractionSession session,
-            IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
-            Bundle bOptions, int userId) {
-        enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
-        if (session == null || interactor == null) {
-            throw new NullPointerException("null session or interactor");
-        }
-        userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
-                ALLOW_FULL_ONLY, "startVoiceActivity", null);
-        // TODO: Switch to user app stacks here.
-        return mActivityStartController.obtainStarter(intent, "startVoiceActivity")
-                .setCallingUid(callingUid)
-                .setCallingPackage(callingPackage)
-                .setResolvedType(resolvedType)
-                .setVoiceSession(session)
-                .setVoiceInteractor(interactor)
-                .setStartFlags(startFlags)
-                .setProfilerInfo(profilerInfo)
-                .setActivityOptions(bOptions)
-                .setMayWait(userId)
-                .execute();
-    }
-
-    @Override
-    public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
-            Intent intent, String resolvedType, Bundle bOptions, int userId) {
-        enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
-        userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
-                ALLOW_FULL_ONLY, "startAssistantActivity", null);
-
-        return mActivityStartController.obtainStarter(intent, "startAssistantActivity")
-                .setCallingUid(callingUid)
-                .setCallingPackage(callingPackage)
-                .setResolvedType(resolvedType)
-                .setActivityOptions(bOptions)
-                .setMayWait(userId)
-                .execute();
-    }
-
-    @Override
-    public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
-                IRecentsAnimationRunner recentsAnimationRunner) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
-        final int callingPid = Binder.getCallingPid();
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
-                final int recentsUid = mRecentTasks.getRecentsComponentUid();
-
-                // Start a new recents animation
-                final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
-                        mActivityStartController, mWindowManager, mUserController, callingPid);
-                anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
-                        recentsUid, assistDataReceiver);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
-        final long callingUid = Binder.getCallingUid();
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                // Cancel the recents animation synchronously (do not hold the WM lock)
-                mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
-                        ? REORDER_MOVE_TO_ORIGINAL_POSITION
-                        : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options)
-            throws RemoteException {
-        Slog.i(TAG, "Activity tried to startVoiceInteraction");
-        synchronized (this) {
-            ActivityRecord activity = getFocusedStack().getTopActivity();
-            if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
-                throw new SecurityException("Only focused activity can call startVoiceInteraction");
-            }
-            if (mRunningVoice != null || activity.getTask().voiceSession != null
-                    || activity.voiceSession != null) {
-                Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction");
-                return;
-            }
-            if (activity.pendingVoiceInteractionStart) {
-                Slog.w(TAG, "Pending start of voice interaction already.");
-                return;
-            }
-            activity.pendingVoiceInteractionStart = true;
-        }
-        LocalServices.getService(VoiceInteractionManagerInternal.class)
-                .startLocalVoiceInteraction(callingActivity, options);
-    }
-
-    @Override
-    public void stopLocalVoiceInteraction(IBinder callingActivity) throws RemoteException {
-        LocalServices.getService(VoiceInteractionManagerInternal.class)
-                .stopLocalVoiceInteraction(callingActivity);
-    }
-
-    @Override
-    public boolean supportsLocalVoiceInteraction() throws RemoteException {
-        return LocalServices.getService(VoiceInteractionManagerInternal.class)
-                .supportsLocalVoiceInteraction();
-    }
-
-    @GuardedBy("this")
-    void onLocalVoiceInteractionStartedLocked(IBinder activity,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
-        ActivityRecord activityToCallback = ActivityRecord.forTokenLocked(activity);
-        if (activityToCallback == null) return;
-        activityToCallback.setVoiceSessionLocked(voiceSession);
-
-        // Inform the activity
-        try {
-            activityToCallback.app.thread.scheduleLocalVoiceInteractionStarted(activity,
-                    voiceInteractor);
-            long token = Binder.clearCallingIdentity();
-            try {
-                startRunningVoiceLocked(voiceSession, activityToCallback.appInfo.uid);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            // TODO: VI Should we cache the activity so that it's easier to find later
-            // rather than scan through all the stacks and activities?
-        } catch (RemoteException re) {
-            activityToCallback.clearVoiceSessionLocked();
-            // TODO: VI Should this terminate the voice session?
-        }
-    }
-
-    @Override
-    public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake) {
-        synchronized (this) {
-            if (mRunningVoice != null && mRunningVoice.asBinder() == session.asBinder()) {
-                if (keepAwake) {
-                    mVoiceWakeLock.acquire();
-                } else {
-                    mVoiceWakeLock.release();
-                }
-            }
-        }
-    }
-
-    @Override
-    public boolean startNextMatchingActivity(IBinder callingActivity,
-            Intent intent, Bundle bOptions) {
-        // Refuse possible leaked file descriptors
-        if (intent != null && intent.hasFileDescriptors() == true) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
-        SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
-
-        synchronized (this) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(callingActivity);
-            if (r == null) {
-                SafeActivityOptions.abort(options);
-                return false;
-            }
-            if (r.app == null || r.app.thread == null) {
-                // The caller is not running...  d'oh!
-                SafeActivityOptions.abort(options);
-                return false;
-            }
-            intent = new Intent(intent);
-            // The caller is not allowed to change the data.
-            intent.setDataAndType(r.intent.getData(), r.intent.getType());
-            // And we are resetting to find the next component...
-            intent.setComponent(null);
-
-            final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
-
-            ActivityInfo aInfo = null;
-            try {
-                List<ResolveInfo> resolves =
-                    AppGlobals.getPackageManager().queryIntentActivities(
-                            intent, r.resolvedType,
-                            PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS,
-                            UserHandle.getCallingUserId()).getList();
-
-                // Look for the original activity in the list...
-                final int N = resolves != null ? resolves.size() : 0;
-                for (int i=0; i<N; i++) {
-                    ResolveInfo rInfo = resolves.get(i);
-                    if (rInfo.activityInfo.packageName.equals(r.packageName)
-                            && rInfo.activityInfo.name.equals(r.info.name)) {
-                        // We found the current one...  the next matching is
-                        // after it.
-                        i++;
-                        if (i<N) {
-                            aInfo = resolves.get(i).activityInfo;
-                        }
-                        if (debug) {
-                            Slog.v(TAG, "Next matching activity: found current " + r.packageName
-                                    + "/" + r.info.name);
-                            Slog.v(TAG, "Next matching activity: next is " + ((aInfo == null)
-                                    ? "null" : aInfo.packageName + "/" + aInfo.name));
-                        }
-                        break;
-                    }
-                }
-            } catch (RemoteException e) {
-            }
-
-            if (aInfo == null) {
-                // Nobody who is next!
-                SafeActivityOptions.abort(options);
-                if (debug) Slog.d(TAG, "Next matching activity: nothing found");
-                return false;
-            }
-
-            intent.setComponent(new ComponentName(
-                    aInfo.applicationInfo.packageName, aInfo.name));
-            intent.setFlags(intent.getFlags()&~(
-                    Intent.FLAG_ACTIVITY_FORWARD_RESULT|
-                    Intent.FLAG_ACTIVITY_CLEAR_TOP|
-                    Intent.FLAG_ACTIVITY_MULTIPLE_TASK|
-                    FLAG_ACTIVITY_NEW_TASK));
-
-            // Okay now we need to start the new activity, replacing the
-            // currently running activity.  This is a little tricky because
-            // we want to start the new one as if the current one is finished,
-            // but not finish the current one first so that there is no flicker.
-            // And thus...
-            final boolean wasFinishing = r.finishing;
-            r.finishing = true;
-
-            // Propagate reply information over to the new activity.
-            final ActivityRecord resultTo = r.resultTo;
-            final String resultWho = r.resultWho;
-            final int requestCode = r.requestCode;
-            r.resultTo = null;
-            if (resultTo != null) {
-                resultTo.removeResultsLocked(r, resultWho, requestCode);
-            }
-
-            final long origId = Binder.clearCallingIdentity();
-            // TODO(b/64750076): Check if calling pid should really be -1.
-            final int res = mActivityStartController
-                    .obtainStarter(intent, "startNextMatchingActivity")
-                    .setCaller(r.app.thread)
-                    .setResolvedType(r.resolvedType)
-                    .setActivityInfo(aInfo)
-                    .setResultTo(resultTo != null ? resultTo.appToken : null)
-                    .setResultWho(resultWho)
-                    .setRequestCode(requestCode)
-                    .setCallingPid(-1)
-                    .setCallingUid(r.launchedFromUid)
-                    .setCallingPackage(r.launchedFromPackage)
-                    .setRealCallingPid(-1)
-                    .setRealCallingUid(r.launchedFromUid)
-                    .setActivityOptions(options)
-                    .execute();
-            Binder.restoreCallingIdentity(origId);
-
-            r.finishing = wasFinishing;
-            if (res != ActivityManager.START_SUCCESS) {
-                return false;
-            }
-            return true;
-        }
+        return mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent,
+                resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
+                userId);
     }
 
     @Override
     public final int startActivityFromRecents(int taskId, Bundle bOptions) {
-        enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
-                "startActivityFromRecents()");
-
-        final int callingPid = Binder.getCallingPid();
-        final int callingUid = Binder.getCallingUid();
-        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                return mStackSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
-                        safeOptions);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
+        return mActivityTaskManager.startActivityFromRecents(taskId, bOptions);
     }
 
     @Override
-    public final int startActivities(IApplicationThread caller, String callingPackage,
-            Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
-            int userId) {
-        final String reason = "startActivities";
-        enforceNotIsolatedCaller(reason);
-        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
-                userId, false, ALLOW_FULL_ONLY, reason, null);
-        // TODO: Switch to user app stacks here.
-        int ret = mActivityStartController.startActivities(caller, -1, callingPackage,
-                intents, resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId,
-                reason);
-        return ret;
+    public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
+            IRecentsAnimationRunner recentsAnimationRunner) {
+        mActivityTaskManager.startRecentsActivity(
+                intent, assistDataReceiver, recentsAnimationRunner);
     }
 
     @Override
-    public void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) {
-        synchronized (this) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return;
-            }
-            r.reportFullyDrawnLocked(restoredFromBundle);
-        }
-    }
-
-    @Override
-    public void setRequestedOrientation(IBinder token, int requestedOrientation) {
-        synchronized (this) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return;
-            }
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                r.setRequestedOrientation(requestedOrientation);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public int getRequestedOrientation(IBinder token) {
-        synchronized (this) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-            }
-            return r.getRequestedOrientation();
-        }
+    public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
+        mActivityTaskManager.cancelRecentsAnimation(restoreHomeStackPosition);
     }
 
     /**
@@ -5644,76 +4285,12 @@
     @Override
     public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
             int finishTask) {
-        // Refuse possible leaked file descriptors
-        if (resultData != null && resultData.hasFileDescriptors() == true) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
+        return mActivityTaskManager.finishActivity(token, resultCode, resultData, finishTask);
+    }
 
-        synchronized(this) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return true;
-            }
-            // Keep track of the root activity of the task before we finish it
-            TaskRecord tr = r.getTask();
-            ActivityRecord rootR = tr.getRootActivity();
-            if (rootR == null) {
-                Slog.w(TAG, "Finishing task with all activities already finished");
-            }
-            // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can
-            // finish.
-            if (mLockTaskController.activityBlockedFromFinish(r)) {
-                return false;
-            }
-
-            if (mController != null) {
-                // Find the first activity that is not finishing.
-                ActivityRecord next = r.getStack().topRunningActivityLocked(token, 0);
-                if (next != null) {
-                    // ask watcher if this is allowed
-                    boolean resumeOK = true;
-                    try {
-                        resumeOK = mController.activityResuming(next.packageName);
-                    } catch (RemoteException e) {
-                        mController = null;
-                        Watchdog.getInstance().setActivityController(null);
-                    }
-
-                    if (!resumeOK) {
-                        Slog.i(TAG, "Not finishing activity because controller resumed");
-                        return false;
-                    }
-                }
-            }
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                boolean res;
-                final boolean finishWithRootActivity =
-                        finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
-                if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
-                        || (finishWithRootActivity && r == rootR)) {
-                    // If requested, remove the task that is associated to this activity only if it
-                    // was the root activity in the task. The result code and data is ignored
-                    // because we don't support returning them across task boundaries. Also, to
-                    // keep backwards compatibility we remove the task from recents when finishing
-                    // task with root activity.
-                    res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
-                            finishWithRootActivity, "finish-activity");
-                    if (!res) {
-                        Slog.i(TAG, "Removing task failed to finish activity");
-                    }
-                } else {
-                    res = tr.getStack().requestFinishActivityLocked(token, resultCode,
-                            resultData, "app-request", true);
-                    if (!res) {
-                        Slog.i(TAG, "Failed to finish by app-request");
-                    }
-                }
-                return res;
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
+    @Override
+    public void setRequestedOrientation(IBinder token, int requestedOrientation) {
+        mActivityTaskManager.setRequestedOrientation(token, requestedOrientation);
     }
 
     @Override
@@ -5734,14 +4311,7 @@
                 return;
             }
 
-            ArrayList<ActivityRecord> activities = new ArrayList<>(proc.activities);
-            for (int i = 0; i < activities.size(); i++) {
-                ActivityRecord r = activities.get(i);
-                if (!r.finishing && r.isInStackLocked()) {
-                    r.getStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
-                            null, "finish-heavy", true);
-                }
-            }
+            proc.getWindowProcessController().finishActivities();
 
             mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
                     proc.userId, 0));
@@ -5767,117 +4337,6 @@
         }
     }
 
-    @Override
-    public final void finishSubActivity(IBinder token, String resultWho,
-            int requestCode) {
-        synchronized(this) {
-            final long origId = Binder.clearCallingIdentity();
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r != null) {
-                r.getStack().finishSubActivityLocked(r, resultWho, requestCode);
-            }
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public boolean finishActivityAffinity(IBinder token) {
-        synchronized(this) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r == null) {
-                    return false;
-                }
-
-                // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps
-                // can finish.
-                final TaskRecord task = r.getTask();
-                if (mLockTaskController.activityBlockedFromFinish(r)) {
-                    return false;
-                }
-                return task.getStack().finishActivityAffinityLocked(r);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public void finishVoiceTask(IVoiceInteractionSession session) {
-        synchronized (this) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                // TODO: VI Consider treating local voice interactions and voice tasks
-                // differently here
-                mStackSupervisor.finishVoiceTask(session);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-
-    }
-
-    @Override
-    public boolean releaseActivityInstance(IBinder token) {
-        synchronized(this) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r == null) {
-                    return false;
-                }
-                return r.getStack().safelyDestroyActivityLocked(r, "app-req");
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public void releaseSomeActivities(IApplicationThread appInt) {
-        synchronized(this) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                ProcessRecord app = getRecordForAppLocked(appInt);
-                mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    @Override
-    public boolean willActivityBeVisible(IBinder token) {
-        synchronized(this) {
-            ActivityStack stack = ActivityRecord.getStackLocked(token);
-            if (stack != null) {
-                return stack.willActivityBeVisibleLocked(token);
-            }
-            return false;
-        }
-    }
-
-    @Override
-    public void overridePendingTransition(IBinder token, String packageName,
-            int enterAnim, int exitAnim) {
-        synchronized(this) {
-            ActivityRecord self = ActivityRecord.isInStackLocked(token);
-            if (self == null) {
-                return;
-            }
-
-            final long origId = Binder.clearCallingIdentity();
-
-            if (self.isState(ActivityState.RESUMED, ActivityState.PAUSING)) {
-                mWindowManager.overridePendingAppTransition(packageName,
-                        enterAnim, exitAnim, null);
-            }
-
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
     /**
      * Main function for removing an existing process from the activity manager
      * as a result of that process going away.  Clears out all connections
@@ -5901,15 +4360,14 @@
         }
 
         // Remove this application's activities from active lists.
-        boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app);
+        boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app.getWindowProcessController());
 
         app.clearRecentTasks();
+        app.clearActivities();
 
-        app.activities.clear();
-
-        if (app.instr != null) {
+        if (app.getActiveInstrumentation() != null) {
             Slog.w(TAG, "Crash of app " + app.processName
-                  + " running instrumentation " + app.instr.mClass);
+                  + " running instrumentation " + app.getActiveInstrumentation().mClass);
             Bundle info = new Bundle();
             info.putString("shortMsg", "Process crashed.");
             finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
@@ -5918,7 +4376,7 @@
         mWindowManager.deferSurfaceLayout();
         try {
             if (!restarting && hasVisibleActivities
-                    && !mStackSupervisor.resumeFocusedStackTopActivityLocked()) {
+                    && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) {
                 // If there was nothing to resume, and we are not already restarting this process, but
                 // there is a visible activity that is hosted by the process...  then make sure all
                 // visible activities are running, taking care of restarting this process.
@@ -5978,7 +4436,7 @@
         for (int i=mLruProcesses.size()-1; i>=0; i--) {
             ProcessRecord rec = mLruProcesses.get(i);
             if (rec.thread != null
-                    && rec.setProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+                    && rec.setProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                 haveBg = true;
                 break;
             }
@@ -6063,7 +4521,7 @@
         // Clean up already done if the process has been re-started.
         if (app.pid == pid && app.thread != null &&
                 app.thread.asBinder() == thread.asBinder()) {
-            boolean doLowMem = app.instr == null;
+            boolean doLowMem = app.getActiveInstrumentation() == null;
             boolean doOomAdj = doLowMem;
             if (!app.killedByAm) {
                 reportUidInfoMessageLocked(TAG,
@@ -6148,7 +4606,7 @@
             }
         }
 
-        final File tracesDir = new File("/data/anr");
+        final File tracesDir = new File(ANR_TRACE_DIR);
         // Each set of ANR traces is written to a separate file and dumpstate will process
         // all such files and add them to a captured bug report if they're recent enough.
         maybePruneOldTraces(tracesDir);
@@ -6230,7 +4688,7 @@
         return SystemClock.elapsedRealtime() - timeStart;
     }
 
-    private static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
+    public static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
             ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) {
 
         // We don't need any sort of inotify based monitoring when we're dumping traces via
@@ -6306,90 +4764,6 @@
         }
     }
 
-    final void logAppTooSlow(ProcessRecord app, long startTime, String msg) {
-        if (true || Build.IS_USER) {
-            return;
-        }
-
-        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-        StrictMode.allowThreadDiskWrites();
-        try {
-            File tracesDir = new File("/data/anr");
-            File tracesFile = null;
-            try {
-                tracesFile = File.createTempFile("app_slow", null, tracesDir);
-
-                StringBuilder sb = new StringBuilder();
-                Time tobj = new Time();
-                tobj.set(System.currentTimeMillis());
-                sb.append(tobj.format("%Y-%m-%d %H:%M:%S"));
-                sb.append(": ");
-                TimeUtils.formatDuration(SystemClock.uptimeMillis()-startTime, sb);
-                sb.append(" since ");
-                sb.append(msg);
-                FileOutputStream fos = new FileOutputStream(tracesFile);
-                fos.write(sb.toString().getBytes());
-                if (app == null) {
-                    fos.write("\n*** No application process!".getBytes());
-                }
-                fos.close();
-                FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1); // -rw-rw-rw-
-            } catch (IOException e) {
-                Slog.w(TAG, "Unable to prepare slow app traces file: " + tracesFile, e);
-                return;
-            }
-
-            if (app != null && app.pid > 0) {
-                ArrayList<Integer> firstPids = new ArrayList<Integer>();
-                firstPids.add(app.pid);
-                dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null);
-            }
-
-            File lastTracesFile = null;
-            File curTracesFile = null;
-            for (int i=9; i>=0; i--) {
-                String name = String.format(Locale.US, "slow%02d.txt", i);
-                curTracesFile = new File(tracesDir, name);
-                if (curTracesFile.exists()) {
-                    if (lastTracesFile != null) {
-                        curTracesFile.renameTo(lastTracesFile);
-                    } else {
-                        curTracesFile.delete();
-                    }
-                }
-                lastTracesFile = curTracesFile;
-            }
-            tracesFile.renameTo(curTracesFile);
-        } finally {
-            StrictMode.setThreadPolicy(oldPolicy);
-        }
-    }
-
-    @GuardedBy("this")
-    final void showLaunchWarningLocked(final ActivityRecord cur, final ActivityRecord next) {
-        if (!mLaunchWarningShown) {
-            mLaunchWarningShown = true;
-            mUiHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    synchronized (ActivityManagerService.this) {
-                        final Dialog d = new LaunchWarningWindow(mContext, cur, next);
-                        d.show();
-                        mUiHandler.postDelayed(new Runnable() {
-                            @Override
-                            public void run() {
-                                synchronized (ActivityManagerService.this) {
-                                    d.dismiss();
-                                    mLaunchWarningShown = false;
-                                }
-                            }
-                        }, 4000);
-                    }
-                }
-            });
-        }
-    }
-
     @Override
     public boolean clearApplicationUserData(final String packageName, boolean keepState,
             final IPackageDataObserver observer, int userId) {
@@ -6456,7 +4830,7 @@
 
                 if (appInfo != null) {
                     forceStopPackageLocked(packageName, appInfo.uid, "clear data");
-                    mRecentTasks.removeTasksByPackageName(packageName, resolvedUserId);
+                    mActivityTaskManager.getRecentTasks().removeTasksByPackageName(packageName, resolvedUserId);
                 }
             }
 
@@ -6499,11 +4873,9 @@
                     // so it told us to keep those intact -- it's about to emplace app data
                     // that is appropriate for those bits of system state.
                     if (!keepState) {
-                        synchronized (this) {
-                            // Remove all permissions granted from/to this package
-                            removeUriPermissionsForPackageLocked(packageName, resolvedUserId, true,
-                                    false);
-                        }
+                        // Remove all permissions granted from/to this package
+                        mUgmInternal.removeUriPermissionsForPackage(packageName, resolvedUserId,
+                                true, false);
 
                         // Reset notification state
                         INotificationManager inm = NotificationManager.getService();
@@ -6590,7 +4962,7 @@
                     final int NA = apps.size();
                     for (int ia = 0; ia < NA; ia++) {
                         final ProcessRecord app = apps.valueAt(ia);
-                        if (app.persistent) {
+                        if (app.isPersistent()) {
                             // We don't kill persistent processes.
                             continue;
                         }
@@ -6627,7 +4999,7 @@
      * @param maxProcState the process state at or below which to preserve
      *                     processes, or {@code -1} to ignore the process state
      */
-    private void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState) {
+    void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState) {
         if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
                 != PackageManager.PERMISSION_GRANTED) {
             final String msg = "Permission Denial: killAllBackgroundProcessesExcept() from pid="
@@ -6845,7 +5217,20 @@
                         proc.baseProcessTracker.addPss(infos[i].getTotalPss(),
                                 infos[i].getTotalUss(), infos[i].getTotalRss(), false,
                                 ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime-startTime,
-                                proc.pkgList);
+                                proc.pkgList.mPkgList);
+                        for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                            ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+                            StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+                                    proc.info.uid,
+                                    holder.state.getName(),
+                                    holder.state.getPackage(),
+                                    infos[i].getTotalPss(),
+                                    infos[i].getTotalUss(),
+                                    infos[i].getTotalRss(),
+                                    ProcessStats.ADD_PSS_EXTERNAL_SLOW,
+                                    endTime-startTime,
+                                    holder.appVersion);
+                        }
                     }
                 }
             }
@@ -6875,7 +5260,17 @@
                     if (proc.thread != null && proc.setAdj == oomAdj) {
                         // Record this for posterity if the process has been stable.
                         proc.baseProcessTracker.addPss(pss[i], tmpUss[0], tmpUss[2], false,
-                                ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime, proc.pkgList);
+                                ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime, proc.pkgList.mPkgList);
+                        for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                            ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+                            StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+                                    proc.info.uid,
+                                    holder.state.getName(),
+                                    holder.state.getPackage(),
+                                    pss[i], tmpUss[0], tmpUss[2],
+                                    ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime,
+                                    holder.appVersion);
+                        }
                     }
                 }
             }
@@ -6948,7 +5343,7 @@
             final int NA = apps.size();
             for (int ia=0; ia<NA; ia++) {
                 ProcessRecord app = apps.valueAt(ia);
-                if (app.persistent && !evenPersistent) {
+                if (app.isPersistent() && !evenPersistent) {
                     // we don't kill persistent processes
                     continue;
                 }
@@ -7069,12 +5464,12 @@
         // Clean-up disabled activities.
         if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
                 packageName, disabledClasses, true, false, userId) && mBooted) {
-            mStackSupervisor.resumeFocusedStackTopActivityLocked();
+            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
             mStackSupervisor.scheduleIdleLocked();
         }
 
         // Clean-up disabled tasks
-        mRecentTasks.cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId);
+        mActivityTaskManager.getRecentTasks().cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId);
 
         // Clean-up disabled services.
         mServices.bringDownDisabledPackageServicesLocked(
@@ -7138,7 +5533,7 @@
                 ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
                 packageName == null ? ("stop user " + userId) : ("stop " + packageName));
 
-        didSomething |= mActivityStartController.clearPendingActivityLaunches(packageName);
+        didSomething |= mActivityTaskManager.getActivityStartController().clearPendingActivityLaunches(packageName);
 
         if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
                 packageName, null, doit, evenPersistent, userId)) {
@@ -7174,7 +5569,7 @@
         }
 
         // Remove transient permissions granted from/to this package/user
-        removeUriPermissionsForPackageLocked(packageName, userId, false, false);
+        mUgmInternal.removeUriPermissionsForPackage(packageName, userId, false, false);
 
         if (doit) {
             for (i = mBroadcastQueues.length - 1; i >= 0; i--) {
@@ -7243,7 +5638,7 @@
                 }
             }
             if (mBooted) {
-                mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
                 mStackSupervisor.scheduleIdleLocked();
             }
         }
@@ -7285,7 +5680,7 @@
         // We shouldn't already have a process under this name, but just in case we
         // need to clean up whatever may be there now.
         ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
-        if (old == proc && proc.persistent) {
+        if (old == proc && proc.isPersistent()) {
             // We are re-adding a persistent process.  Whatevs!  Just leave it there.
             Slog.w(TAG, "Re-adding persistent process " + proc);
         } else if (old != null) {
@@ -7352,7 +5747,7 @@
                 }
             }
             boolean willRestart = false;
-            if (app.persistent && !app.isolated) {
+            if (app.isPersistent() && !app.isolated) {
                 if (!callerWillRestart) {
                     willRestart = true;
                 } else {
@@ -7455,7 +5850,7 @@
         if (app == null && startSeq > 0) {
             final ProcessRecord pending = mPendingStarts.get(startSeq);
             if (pending != null && pending.startUid == callingUid
-                    && handleProcessStartedLocked(pending, pid, pending.usingWrapper,
+                    && handleProcessStartedLocked(pending, pid, pending.isUsingWrapper(),
                             startSeq, true)) {
                 app = pending;
             }
@@ -7505,11 +5900,11 @@
 
         app.makeActive(thread, mProcessStats);
         app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
-        app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+        app.setCurrentSchedulingGroup(app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT);
         app.forcingToImportant = null;
         updateProcessForegroundLocked(app, false, false);
         app.hasShownUi = false;
-        app.debugging = false;
+        app.setDebugging(false);
         app.cached = false;
         app.killedByAm = false;
         app.killed = false;
@@ -7546,7 +5941,7 @@
                 testMode = mWaitForDebugger
                     ? ApplicationThreadConstants.DEBUG_WAIT
                     : ApplicationThreadConstants.DEBUG_ON;
-                app.debugging = true;
+                app.setDebugging(true);
                 if (mDebugTransient) {
                     mDebugApp = mOrigDebugApp;
                     mWaitForDebugger = mOrigWaitForDebugger;
@@ -7568,13 +5963,15 @@
                                 || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL));
             }
 
-            if (app.instr != null) {
-                notifyPackageUse(app.instr.mClass.getPackageName(),
+            final ActiveInstrumentation instr = app.getActiveInstrumentation();
+
+            if (instr != null) {
+                notifyPackageUse(instr.mClass.getPackageName(),
                                  PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION);
             }
             if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
                     + processName + " with config " + getGlobalConfiguration());
-            ApplicationInfo appInfo = app.instr != null ? app.instr.mTargetInfo : app.info;
+            ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
             app.compat = compatibilityInfoForPackageLocked(appInfo);
 
             ProfilerInfo profilerInfo = null;
@@ -7591,8 +5988,8 @@
                         preBindAgent = mProfilerInfo.agent;
                     }
                 }
-            } else if (app.instr != null && app.instr.mProfileFile != null) {
-                profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
+            } else if (instr != null && instr.mProfileFile != null) {
+                profilerInfo = new ProfilerInfo(instr.mProfileFile, null, 0, false, false,
                         null, false);
             }
             if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
@@ -7618,10 +6015,9 @@
             }
 
             // We deprecated Build.SERIAL and it is not accessible to
-            // apps that target the v2 security sandbox and to apps that
-            // target APIs higher than O MR1. Since access to the serial
+            // Instant Apps and target APIs higher than O MR1. Since access to the serial
             // is now behind a permission we push down the value.
-            final String buildSerial = (appInfo.targetSandboxVersion < 2
+            final String buildSerial = (!appInfo.isInstantApp()
                     && appInfo.targetSdkVersion < Build.VERSION_CODES.P)
                             ? sTheRealBuildSerial : Build.UNKNOWN;
 
@@ -7629,21 +6025,22 @@
             // currently active instrumentation.  (Note we do this AFTER all of the profiling
             // stuff above because profiling can currently happen only in the primary
             // instrumentation process.)
-            if (mActiveInstrumentation.size() > 0 && app.instr == null) {
-                for (int i = mActiveInstrumentation.size() - 1; i >= 0 && app.instr == null; i--) {
+            if (mActiveInstrumentation.size() > 0 && instr == null) {
+                for (int i = mActiveInstrumentation.size() - 1;
+                        i >= 0 && app.getActiveInstrumentation() == null; i--) {
                     ActiveInstrumentation aInstr = mActiveInstrumentation.get(i);
                     if (!aInstr.mFinished && aInstr.mTargetInfo.uid == app.uid) {
                         if (aInstr.mTargetProcesses.length == 0) {
                             // This is the wildcard mode, where every process brought up for
                             // the target instrumentation should be included.
                             if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) {
-                                app.instr = aInstr;
+                                app.setActiveInstrumentation(aInstr);
                                 aInstr.mRunningProcesses.add(app);
                             }
                         } else {
                             for (String proc : aInstr.mTargetProcesses) {
                                 if (proc.equals(app.processName)) {
-                                    app.instr = aInstr;
+                                    app.setActiveInstrumentation(aInstr);
                                     aInstr.mRunningProcesses.add(app);
                                     break;
                                 }
@@ -7673,18 +6070,19 @@
 
             checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
             mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(app);
+            final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
             if (app.isolatedEntryPoint != null) {
                 // This is an isolated process which should just call an entry point instead of
                 // being bound to an application.
                 thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
-            } else if (app.instr != null) {
+            } else if (instr2 != null) {
                 thread.bindApplication(processName, appInfo, providers,
-                        app.instr.mClass,
-                        profilerInfo, app.instr.mArguments,
-                        app.instr.mWatcher,
-                        app.instr.mUiAutomationConnection, testMode,
+                        instr2.mClass,
+                        profilerInfo, instr2.mArguments,
+                        instr2.mWatcher,
+                        instr2.mUiAutomationConnection, testMode,
                         mBinderTransactionTrackingEnabled, enableTrackAllocation,
-                        isRestrictedBackupMode || !normalMode, app.persistent,
+                        isRestrictedBackupMode || !normalMode, app.isPersistent(),
                         new Configuration(getGlobalConfiguration()), app.compat,
                         getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
@@ -7693,7 +6091,7 @@
                 thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                         null, null, null, testMode,
                         mBinderTransactionTrackingEnabled, enableTrackAllocation,
-                        isRestrictedBackupMode || !normalMode, app.persistent,
+                        isRestrictedBackupMode || !normalMode, app.isPersistent(),
                         new Configuration(getGlobalConfiguration()), app.compat,
                         getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
@@ -7804,40 +6202,11 @@
         }
     }
 
-    @Override
-    public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
-        final long origId = Binder.clearCallingIdentity();
-        synchronized (this) {
-            ActivityStack stack = ActivityRecord.getStackLocked(token);
-            if (stack != null) {
-                ActivityRecord r =
-                        mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */,
-                                false /* processPausingActivities */, config);
-                if (stopProfiling) {
-                    if ((mProfileProc == r.app) && mProfilerInfo != null) {
-                        clearProfilerLocked();
-                    }
-                }
-            }
-        }
-        Binder.restoreCallingIdentity(origId);
-    }
-
     void postFinishBooting(boolean finishBooting, boolean enableScreen) {
         mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG,
                 finishBooting ? 1 : 0, enableScreen ? 1 : 0));
     }
 
-    void enableScreenAfterBoot() {
-        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
-                SystemClock.uptimeMillis());
-        mWindowManager.enableScreenAfterBoot();
-
-        synchronized (this) {
-            updateEventDispatchingLocked();
-        }
-    }
-
     @Override
     public void showBootMessage(final CharSequence msg, final boolean always) {
         if (Binder.getCallingUid() != myUid()) {
@@ -7846,26 +6215,6 @@
         mWindowManager.showBootMessage(msg, always);
     }
 
-    @Override
-    public void keyguardGoingAway(int flags) {
-        enforceNotIsolatedCaller("keyguardGoingAway");
-        final long token = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                mKeyguardController.keyguardGoingAway(flags);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * @return whther the keyguard is currently locked.
-     */
-    boolean isKeyguardLocked() {
-        return mKeyguardController.isKeyguardLocked();
-    }
-
     final void finishBooting() {
         synchronized (this) {
             if (!mBootAnimationComplete) {
@@ -8005,156 +6354,7 @@
         }
 
         if (enableScreen) {
-            enableScreenAfterBoot();
-        }
-    }
-
-    @Override
-    public final void activityResumed(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        synchronized(this) {
-            ActivityRecord.activityResumedLocked(token);
-            mWindowManager.notifyAppResumedFinished(token);
-        }
-        Binder.restoreCallingIdentity(origId);
-    }
-
-    @Override
-    public final void activityPaused(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        synchronized(this) {
-            ActivityStack stack = ActivityRecord.getStackLocked(token);
-            if (stack != null) {
-                stack.activityPausedLocked(token, false);
-            }
-        }
-        Binder.restoreCallingIdentity(origId);
-    }
-
-    @Override
-    public final void activityStopped(IBinder token, Bundle icicle,
-            PersistableBundle persistentState, CharSequence description) {
-        if (DEBUG_ALL) Slog.v(TAG, "Activity stopped: token=" + token);
-
-        // Refuse possible leaked file descriptors
-        if (icicle != null && icicle.hasFileDescriptors()) {
-            throw new IllegalArgumentException("File descriptors passed in Bundle");
-        }
-
-        final long origId = Binder.clearCallingIdentity();
-
-        synchronized (this) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r != null) {
-                r.activityStoppedLocked(icicle, persistentState, description);
-            }
-        }
-
-        trimApplications();
-
-        Binder.restoreCallingIdentity(origId);
-    }
-
-    @Override
-    public final void activityDestroyed(IBinder token) {
-        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
-        synchronized (this) {
-            ActivityStack stack = ActivityRecord.getStackLocked(token);
-            if (stack != null) {
-                stack.activityDestroyedLocked(token, "activityDestroyed");
-            }
-        }
-    }
-
-    @Override
-    public final void activityRelaunched(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        synchronized (this) {
-            mStackSupervisor.activityRelaunchedLocked(token);
-        }
-        Binder.restoreCallingIdentity(origId);
-    }
-
-    @Override
-    public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
-            int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
-        if (DEBUG_CONFIGURATION) Slog.v(TAG, "Report configuration: " + token + " "
-                + horizontalSizeConfiguration + " " + verticalSizeConfigurations);
-        synchronized (this) {
-            ActivityRecord record = ActivityRecord.isInStackLocked(token);
-            if (record == null) {
-                throw new IllegalArgumentException("reportSizeConfigurations: ActivityRecord not "
-                        + "found for: " + token);
-            }
-            record.setSizeConfigurations(horizontalSizeConfiguration,
-                    verticalSizeConfigurations, smallestSizeConfigurations);
-        }
-    }
-
-    @Override
-    public final void notifyLaunchTaskBehindComplete(IBinder token) {
-        mStackSupervisor.scheduleLaunchTaskBehindComplete(token);
-    }
-
-    @Override
-    public final void notifyEnterAnimationComplete(IBinder token) {
-        mHandler.sendMessage(mHandler.obtainMessage(ENTER_ANIMATION_COMPLETE_MSG, token));
-    }
-
-    @Override
-    public String getCallingPackage(IBinder token) {
-        synchronized (this) {
-            ActivityRecord r = getCallingRecordLocked(token);
-            return r != null ? r.info.packageName : null;
-        }
-    }
-
-    @Override
-    public ComponentName getCallingActivity(IBinder token) {
-        synchronized (this) {
-            ActivityRecord r = getCallingRecordLocked(token);
-            return r != null ? r.intent.getComponent() : null;
-        }
-    }
-
-    private ActivityRecord getCallingRecordLocked(IBinder token) {
-        ActivityRecord r = ActivityRecord.isInStackLocked(token);
-        if (r == null) {
-            return null;
-        }
-        return r.resultTo;
-    }
-
-    @Override
-    public ComponentName getActivityClassForToken(IBinder token) {
-        synchronized(this) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return null;
-            }
-            return r.intent.getComponent();
-        }
-    }
-
-    @Override
-    public String getPackageForToken(IBinder token) {
-        synchronized(this) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return null;
-            }
-            return r.packageName;
-        }
-    }
-
-    @Override
-    public boolean isRootVoiceInteraction(IBinder token) {
-        synchronized(this) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return false;
-            }
-            return r.rootVoiceInteraction;
+            mAtmInternal.enableScreenAfterBoot(mBooted);
         }
     }
 
@@ -8650,199 +6850,6 @@
         return uidRec == null ? ActivityManager.PROCESS_STATE_NONEXISTENT : uidRec.curProcState;
     }
 
-    @Override
-    public boolean isInMultiWindowMode(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized(this) {
-                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r == null) {
-                    return false;
-                }
-                // An activity is consider to be in multi-window mode if its task isn't fullscreen.
-                return r.inMultiWindowMode();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public boolean isInPictureInPictureMode(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized(this) {
-                return isInPictureInPictureMode(ActivityRecord.forTokenLocked(token));
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    private boolean isInPictureInPictureMode(ActivityRecord r) {
-        if (r == null || r.getStack() == null || !r.inPinnedWindowingMode()
-                || r.getStack().isInStackLocked(r) == null) {
-            return false;
-        }
-
-        // If we are animating to fullscreen then we have already dispatched the PIP mode
-        // changed, so we should reflect that check here as well.
-        final PinnedActivityStack stack = r.getStack();
-        final PinnedStackWindowController windowController = stack.getWindowContainerController();
-        return !windowController.isAnimatingBoundsToFullscreen();
-    }
-
-    @Override
-    public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized(this) {
-                final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
-                        "enterPictureInPictureMode", token, params);
-
-                // If the activity is already in picture in picture mode, then just return early
-                if (isInPictureInPictureMode(r)) {
-                    return true;
-                }
-
-                // Activity supports picture-in-picture, now check that we can enter PiP at this
-                // point, if it is
-                if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode",
-                        false /* beforeStopping */)) {
-                    return false;
-                }
-
-                final Runnable enterPipRunnable = () -> {
-                    // Only update the saved args from the args that are set
-                    r.pictureInPictureArgs.copyOnlySet(params);
-                    final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
-                    final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
-                    // Adjust the source bounds by the insets for the transition down
-                    final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint());
-                    mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
-                            "enterPictureInPictureMode");
-                    final PinnedActivityStack stack = r.getStack();
-                    stack.setPictureInPictureAspectRatio(aspectRatio);
-                    stack.setPictureInPictureActions(actions);
-                    MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
-                            r.shortComponentName, r.supportsEnterPipOnTaskSwitch);
-                    logPictureInPictureArgs(params);
-                };
-
-                if (isKeyguardLocked()) {
-                    // If the keyguard is showing or occluded, then try and dismiss it before
-                    // entering picture-in-picture (this will prompt the user to authenticate if the
-                    // device is currently locked).
-                    try {
-                        dismissKeyguard(token, new KeyguardDismissCallback() {
-                            @Override
-                            public void onDismissSucceeded() throws RemoteException {
-                                mHandler.post(enterPipRunnable);
-                            }
-                        }, null /* message */);
-                    } catch (RemoteException e) {
-                        // Local call
-                    }
-                } else {
-                    // Enter picture in picture immediately otherwise
-                    enterPipRunnable.run();
-                }
-                return true;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized(this) {
-                final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
-                        "setPictureInPictureParams", token, params);
-
-                // Only update the saved args from the args that are set
-                r.pictureInPictureArgs.copyOnlySet(params);
-                if (r.inPinnedWindowingMode()) {
-                    // If the activity is already in picture-in-picture, update the pinned stack now
-                    // if it is not already expanding to fullscreen. Otherwise, the arguments will
-                    // be used the next time the activity enters PiP
-                    final PinnedActivityStack stack = r.getStack();
-                    if (!stack.isAnimatingBoundsToFullscreen()) {
-                        stack.setPictureInPictureAspectRatio(
-                                r.pictureInPictureArgs.getAspectRatio());
-                        stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
-                    }
-                }
-                logPictureInPictureArgs(params);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public int getMaxNumPictureInPictureActions(IBinder token) {
-        // Currently, this is a static constant, but later, we may change this to be dependent on
-        // the context of the activity
-        return 3;
-    }
-
-    private void logPictureInPictureArgs(PictureInPictureParams params) {
-        if (params.hasSetActions()) {
-            MetricsLogger.histogram(mContext, "tron_varz_picture_in_picture_actions_count",
-                    params.getActions().size());
-        }
-        if (params.hasSetAspectRatio()) {
-            LogMaker lm = new LogMaker(MetricsEvent.ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED);
-            lm.addTaggedData(MetricsEvent.PICTURE_IN_PICTURE_ASPECT_RATIO, params.getAspectRatio());
-            MetricsLogger.action(lm);
-        }
-    }
-
-    /**
-     * Checks the state of the system and the activity associated with the given {@param token} to
-     * verify that picture-in-picture is supported for that activity.
-     *
-     * @return the activity record for the given {@param token} if all the checks pass.
-     */
-    private ActivityRecord ensureValidPictureInPictureActivityParamsLocked(String caller,
-            IBinder token, PictureInPictureParams params) {
-        if (!mSupportsPictureInPicture) {
-            throw new IllegalStateException(caller
-                    + ": Device doesn't support picture-in-picture mode.");
-        }
-
-        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        if (r == null) {
-            throw new IllegalStateException(caller
-                    + ": Can't find activity for token=" + token);
-        }
-
-        if (!r.supportsPictureInPicture()) {
-            throw new IllegalStateException(caller
-                    + ": Current activity does not support picture-in-picture.");
-        }
-
-        if (params.hasSetAspectRatio()
-                && !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
-                        params.getAspectRatio())) {
-            final float minAspectRatio = mContext.getResources().getFloat(
-                    com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
-            final float maxAspectRatio = mContext.getResources().getFloat(
-                    com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
-            throw new IllegalArgumentException(String.format(caller
-                    + ": Aspect ratio is too extreme (must be between %f and %f).",
-                            minAspectRatio, maxAspectRatio));
-        }
-
-        // Truncate the number of actions if necessary
-        params.truncateActions(getMaxNumPictureInPictureActions(token));
-
-        return r;
-    }
-
     // =========================================================
     // PROCESS INFO
     // =========================================================
@@ -8969,7 +6976,7 @@
         }
     }
 
-    int checkComponentPermission(String permission, int pid, int uid,
+    static int checkComponentPermission(String permission, int pid, int uid,
             int owningUid, boolean exported) {
         if (pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
@@ -9056,114 +7063,6 @@
         throw new SecurityException(msg);
     }
 
-    /**
-     * This can be called with or without the global lock held.
-     */
-    void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
-        if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) {
-            enforceCallingPermission(permission, func);
-        }
-    }
-
-    /**
-     * Determine if UID is holding permissions required to access {@link Uri} in
-     * the given {@link ProviderInfo}. Final permission checking is always done
-     * in {@link ContentProvider}.
-     */
-    private final boolean checkHoldingPermissionsLocked(
-            IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                "checkHoldingPermissionsLocked: uri=" + grantUri + " uid=" + uid);
-        if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
-            if (ActivityManager.checkComponentPermission(INTERACT_ACROSS_USERS, uid, -1, true)
-                    != PERMISSION_GRANTED) {
-                return false;
-            }
-        }
-        return checkHoldingPermissionsInternalLocked(pm, pi, grantUri, uid, modeFlags, true);
-    }
-
-    private final boolean checkHoldingPermissionsInternalLocked(IPackageManager pm, ProviderInfo pi,
-            GrantUri grantUri, int uid, final int modeFlags, boolean considerUidPermissions) {
-        if (pi.applicationInfo.uid == uid) {
-            return true;
-        } else if (!pi.exported) {
-            return false;
-        }
-
-        boolean readMet = (modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0;
-        boolean writeMet = (modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
-        try {
-            // check if target holds top-level <provider> permissions
-            if (!readMet && pi.readPermission != null && considerUidPermissions
-                    && (pm.checkUidPermission(pi.readPermission, uid) == PERMISSION_GRANTED)) {
-                readMet = true;
-            }
-            if (!writeMet && pi.writePermission != null && considerUidPermissions
-                    && (pm.checkUidPermission(pi.writePermission, uid) == PERMISSION_GRANTED)) {
-                writeMet = true;
-            }
-
-            // track if unprotected read/write is allowed; any denied
-            // <path-permission> below removes this ability
-            boolean allowDefaultRead = pi.readPermission == null;
-            boolean allowDefaultWrite = pi.writePermission == null;
-
-            // check if target holds any <path-permission> that match uri
-            final PathPermission[] pps = pi.pathPermissions;
-            if (pps != null) {
-                final String path = grantUri.uri.getPath();
-                int i = pps.length;
-                while (i > 0 && (!readMet || !writeMet)) {
-                    i--;
-                    PathPermission pp = pps[i];
-                    if (pp.match(path)) {
-                        if (!readMet) {
-                            final String pprperm = pp.getReadPermission();
-                            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                                    "Checking read perm for " + pprperm + " for " + pp.getPath()
-                                    + ": match=" + pp.match(path)
-                                    + " check=" + pm.checkUidPermission(pprperm, uid));
-                            if (pprperm != null) {
-                                if (considerUidPermissions && pm.checkUidPermission(pprperm, uid)
-                                        == PERMISSION_GRANTED) {
-                                    readMet = true;
-                                } else {
-                                    allowDefaultRead = false;
-                                }
-                            }
-                        }
-                        if (!writeMet) {
-                            final String ppwperm = pp.getWritePermission();
-                            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                                    "Checking write perm " + ppwperm + " for " + pp.getPath()
-                                    + ": match=" + pp.match(path)
-                                    + " check=" + pm.checkUidPermission(ppwperm, uid));
-                            if (ppwperm != null) {
-                                if (considerUidPermissions && pm.checkUidPermission(ppwperm, uid)
-                                        == PERMISSION_GRANTED) {
-                                    writeMet = true;
-                                } else {
-                                    allowDefaultWrite = false;
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-
-            // grant unprotected <provider> read/write, if not blocked by
-            // <path-permission> above
-            if (allowDefaultRead) readMet = true;
-            if (allowDefaultWrite) writeMet = true;
-
-        } catch (RemoteException e) {
-            return false;
-        }
-
-        return readMet && writeMet;
-    }
-
     public boolean isAppStartModeDisabled(int uid, String packageName) {
         synchronized (this) {
             return getAppStartModeLocked(uid, packageName, 0, -1, false, true, false)
@@ -9315,6 +7214,20 @@
                 || mPendingTempWhitelist.indexOfKey(uid) >= 0;
     }
 
+    /**
+     * @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on
+     * the whitelist
+     */
+    String getPendingTempWhitelistTagForUidLocked(int uid) {
+        final PendingTempWhitelist ptw = mPendingTempWhitelist.get(uid);
+        return ptw != null ? ptw.tag : null;
+    }
+
+    @VisibleForTesting
+    boolean isActivityStartsLoggingEnabled() {
+        return mConstants.mFlagActivityStartsLoggingEnabled;
+    }
+
     private ProviderInfo getProviderInfoLocked(String authority, int userHandle, int pmFlags) {
         ProviderInfo pi = null;
         ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userHandle);
@@ -9337,67 +7250,6 @@
                 grantEphemeralAccess(userId, intent, targetAppId, ephemeralAppId);
     }
 
-    @GuardedBy("this")
-    private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) {
-        final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
-        if (targetUris != null) {
-            return targetUris.get(grantUri);
-        }
-        return null;
-    }
-
-    @GuardedBy("this")
-    private UriPermission findOrCreateUriPermissionLocked(String sourcePkg,
-            String targetPkg, int targetUid, GrantUri grantUri) {
-        ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
-        if (targetUris == null) {
-            targetUris = Maps.newArrayMap();
-            mGrantedUriPermissions.put(targetUid, targetUris);
-        }
-
-        UriPermission perm = targetUris.get(grantUri);
-        if (perm == null) {
-            perm = new UriPermission(sourcePkg, targetPkg, targetUid, grantUri);
-            targetUris.put(grantUri, perm);
-        }
-
-        return perm;
-    }
-
-    @GuardedBy("this")
-    private final boolean checkUriPermissionLocked(GrantUri grantUri, int uid,
-            final int modeFlags) {
-        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
-        final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE
-                : UriPermission.STRENGTH_OWNED;
-
-        // Root gets to do everything.
-        if (uid == 0) {
-            return true;
-        }
-
-        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid);
-        if (perms == null) return false;
-
-        // First look for exact match
-        final UriPermission exactPerm = perms.get(grantUri);
-        if (exactPerm != null && exactPerm.getStrength(modeFlags) >= minStrength) {
-            return true;
-        }
-
-        // No exact match, look for prefixes
-        final int N = perms.size();
-        for (int i = 0; i < N; i++) {
-            final UriPermission perm = perms.valueAt(i);
-            if (perm.uri.prefix && grantUri.uri.isPathPrefixMatch(perm.uri.uri)
-                    && perm.getStrength(modeFlags) >= minStrength) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
     /**
      * @param uri This uri must NOT contain an embedded userId.
      * @param userId The userId in which the uri is to be resolved.
@@ -9419,395 +7271,8 @@
         if (pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
         }
-        synchronized (this) {
-            return checkUriPermissionLocked(new GrantUri(userId, uri, false), uid, modeFlags)
-                    ? PackageManager.PERMISSION_GRANTED
-                    : PackageManager.PERMISSION_DENIED;
-        }
-    }
-
-    /**
-     * Check if the targetPkg can be granted permission to access uri by
-     * the callingUid using the given modeFlags.  Throws a security exception
-     * if callingUid is not allowed to do this.  Returns the uid of the target
-     * if the URI permission grant should be performed; returns -1 if it is not
-     * needed (for example targetPkg already has permission to access the URI).
-     * If you already know the uid of the target, you can supply it in
-     * lastTargetUid else set that to -1.
-     */
-    @GuardedBy("this")
-    int checkGrantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
-            final int modeFlags, int lastTargetUid) {
-        if (!Intent.isAccessUriMode(modeFlags)) {
-            return -1;
-        }
-
-        if (targetPkg != null) {
-            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                    "Checking grant " + targetPkg + " permission to " + grantUri);
-        }
-
-        final IPackageManager pm = AppGlobals.getPackageManager();
-
-        // If this is not a content: uri, we can't do anything with it.
-        if (!ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) {
-            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                    "Can't grant URI permission for non-content URI: " + grantUri);
-            return -1;
-        }
-
-        // Bail early if system is trying to hand out permissions directly; it
-        // must always grant permissions on behalf of someone explicit.
-        final int callingAppId = UserHandle.getAppId(callingUid);
-        if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) {
-            if ("com.android.settings.files".equals(grantUri.uri.getAuthority())) {
-                // Exempted authority for
-                // 1. cropping user photos and sharing a generated license html
-                //    file in Settings app
-                // 2. sharing a generated license html file in TvSettings app
-            } else {
-                Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
-                        + " grant to " + grantUri + "; use startActivityAsCaller() instead");
-                return -1;
-            }
-        }
-
-        final String authority = grantUri.uri.getAuthority();
-        final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId,
-                MATCH_DEBUG_TRIAGED_MISSING);
-        if (pi == null) {
-            Slog.w(TAG, "No content provider found for permission check: " +
-                    grantUri.uri.toSafeString());
-            return -1;
-        }
-
-        int targetUid = lastTargetUid;
-        if (targetUid < 0 && targetPkg != null) {
-            try {
-                targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
-                        UserHandle.getUserId(callingUid));
-                if (targetUid < 0) {
-                    if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                            "Can't grant URI permission no uid for: " + targetPkg);
-                    return -1;
-                }
-            } catch (RemoteException ex) {
-                return -1;
-            }
-        }
-
-        // If we're extending a persistable grant, then we always need to create
-        // the grant data structure so that take/release APIs work
-        if ((modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0) {
-            return targetUid;
-        }
-
-        if (targetUid >= 0) {
-            // First...  does the target actually need this permission?
-            if (checkHoldingPermissionsLocked(pm, pi, grantUri, targetUid, modeFlags)) {
-                // No need to grant the target this permission.
-                if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                        "Target " + targetPkg + " already has full permission to " + grantUri);
-                return -1;
-            }
-        } else {
-            // First...  there is no target package, so can anyone access it?
-            boolean allowed = pi.exported;
-            if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
-                if (pi.readPermission != null) {
-                    allowed = false;
-                }
-            }
-            if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
-                if (pi.writePermission != null) {
-                    allowed = false;
-                }
-            }
-            if (pi.pathPermissions != null) {
-                final int N = pi.pathPermissions.length;
-                for (int i=0; i<N; i++) {
-                    if (pi.pathPermissions[i] != null
-                            && pi.pathPermissions[i].match(grantUri.uri.getPath())) {
-                        if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
-                            if (pi.pathPermissions[i].getReadPermission() != null) {
-                                allowed = false;
-                            }
-                        }
-                        if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
-                            if (pi.pathPermissions[i].getWritePermission() != null) {
-                                allowed = false;
-                            }
-                        }
-                        break;
-                    }
-                }
-            }
-            if (allowed) {
-                return -1;
-            }
-        }
-
-        /* There is a special cross user grant if:
-         * - The target is on another user.
-         * - Apps on the current user can access the uri without any uid permissions.
-         * In this case, we grant a uri permission, even if the ContentProvider does not normally
-         * grant uri permissions.
-         */
-        boolean specialCrossUserGrant = UserHandle.getUserId(targetUid) != grantUri.sourceUserId
-                && checkHoldingPermissionsInternalLocked(pm, pi, grantUri, callingUid,
-                modeFlags, false /*without considering the uid permissions*/);
-
-        // Second...  is the provider allowing granting of URI permissions?
-        if (!specialCrossUserGrant) {
-            if (!pi.grantUriPermissions) {
-                throw new SecurityException("Provider " + pi.packageName
-                        + "/" + pi.name
-                        + " does not allow granting of Uri permissions (uri "
-                        + grantUri + ")");
-            }
-            if (pi.uriPermissionPatterns != null) {
-                final int N = pi.uriPermissionPatterns.length;
-                boolean allowed = false;
-                for (int i=0; i<N; i++) {
-                    if (pi.uriPermissionPatterns[i] != null
-                            && pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) {
-                        allowed = true;
-                        break;
-                    }
-                }
-                if (!allowed) {
-                    throw new SecurityException("Provider " + pi.packageName
-                            + "/" + pi.name
-                            + " does not allow granting of permission to path of Uri "
-                            + grantUri);
-                }
-            }
-        }
-
-        // Third...  does the caller itself have permission to access
-        // this uri?
-        if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
-            // Require they hold a strong enough Uri permission
-            if (!checkUriPermissionLocked(grantUri, callingUid, modeFlags)) {
-                if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(pi.readPermission)) {
-                    throw new SecurityException(
-                            "UID " + callingUid + " does not have permission to " + grantUri
-                                    + "; you could obtain access using ACTION_OPEN_DOCUMENT "
-                                    + "or related APIs");
-                } else {
-                    throw new SecurityException(
-                            "UID " + callingUid + " does not have permission to " + grantUri);
-                }
-            }
-        }
-        return targetUid;
-    }
-
-    /**
-     * @param uri This uri must NOT contain an embedded userId.
-     * @param userId The userId in which the uri is to be resolved.
-     */
-    @Override
-    public int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri,
-            final int modeFlags, int userId) {
-        enforceNotIsolatedCaller("checkGrantUriPermission");
-        synchronized(this) {
-            return checkGrantUriPermissionLocked(callingUid, targetPkg,
-                    new GrantUri(userId, uri, false), modeFlags, -1);
-        }
-    }
-
-    @GuardedBy("this")
-    void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg, GrantUri grantUri,
-            final int modeFlags, UriPermissionOwner owner) {
-        if (!Intent.isAccessUriMode(modeFlags)) {
-            return;
-        }
-
-        // So here we are: the caller has the assumed permission
-        // to the uri, and the target doesn't.  Let's now give this to
-        // the target.
-
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                "Granting " + targetPkg + "/" + targetUid + " permission to " + grantUri);
-
-        final String authority = grantUri.uri.getAuthority();
-        final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId,
-                MATCH_DEBUG_TRIAGED_MISSING);
-        if (pi == null) {
-            Slog.w(TAG, "No content provider found for grant: " + grantUri.toSafeString());
-            return;
-        }
-
-        if ((modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0) {
-            grantUri.prefix = true;
-        }
-        final UriPermission perm = findOrCreateUriPermissionLocked(
-                pi.packageName, targetPkg, targetUid, grantUri);
-        perm.grantModes(modeFlags, owner);
-    }
-
-    @GuardedBy("this")
-    void grantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
-            final int modeFlags, UriPermissionOwner owner, int targetUserId) {
-        if (targetPkg == null) {
-            throw new NullPointerException("targetPkg");
-        }
-        int targetUid;
-        final IPackageManager pm = AppGlobals.getPackageManager();
-        try {
-            targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, targetUserId);
-        } catch (RemoteException ex) {
-            return;
-        }
-
-        targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, modeFlags,
-                targetUid);
-        if (targetUid < 0) {
-            return;
-        }
-
-        grantUriPermissionUncheckedLocked(targetUid, targetPkg, grantUri, modeFlags,
-                owner);
-    }
-
-    static class NeededUriGrants extends ArrayList<GrantUri> {
-        final String targetPkg;
-        final int targetUid;
-        final int flags;
-
-        NeededUriGrants(String targetPkg, int targetUid, int flags) {
-            this.targetPkg = targetPkg;
-            this.targetUid = targetUid;
-            this.flags = flags;
-        }
-
-        void writeToProto(ProtoOutputStream proto, long fieldId) {
-            long token = proto.start(fieldId);
-            proto.write(NeededUriGrantsProto.TARGET_PACKAGE, targetPkg);
-            proto.write(NeededUriGrantsProto.TARGET_UID, targetUid);
-            proto.write(NeededUriGrantsProto.FLAGS, flags);
-
-            final int N = this.size();
-            for (int i=0; i<N; i++) {
-                this.get(i).writeToProto(proto, NeededUriGrantsProto.GRANTS);
-            }
-            proto.end(token);
-        }
-    }
-
-    /**
-     * Like checkGrantUriPermissionLocked, but takes an Intent.
-     */
-    @GuardedBy("this")
-    NeededUriGrants checkGrantUriPermissionFromIntentLocked(int callingUid,
-            String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                "Checking URI perm to data=" + (intent != null ? intent.getData() : null)
-                + " clip=" + (intent != null ? intent.getClipData() : null)
-                + " from " + intent + "; flags=0x"
-                + Integer.toHexString(intent != null ? intent.getFlags() : 0));
-
-        if (targetPkg == null) {
-            throw new NullPointerException("targetPkg");
-        }
-
-        if (intent == null) {
-            return null;
-        }
-        Uri data = intent.getData();
-        ClipData clip = intent.getClipData();
-        if (data == null && clip == null) {
-            return null;
-        }
-        // Default userId for uris in the intent (if they don't specify it themselves)
-        int contentUserHint = intent.getContentUserHint();
-        if (contentUserHint == UserHandle.USER_CURRENT) {
-            contentUserHint = UserHandle.getUserId(callingUid);
-        }
-        final IPackageManager pm = AppGlobals.getPackageManager();
-        int targetUid;
-        if (needed != null) {
-            targetUid = needed.targetUid;
-        } else {
-            try {
-                targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
-                        targetUserId);
-            } catch (RemoteException ex) {
-                return null;
-            }
-            if (targetUid < 0) {
-                if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                        "Can't grant URI permission no uid for: " + targetPkg
-                        + " on user " + targetUserId);
-                return null;
-            }
-        }
-        if (data != null) {
-            GrantUri grantUri = GrantUri.resolve(contentUserHint, data);
-            targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
-                    targetUid);
-            if (targetUid > 0) {
-                if (needed == null) {
-                    needed = new NeededUriGrants(targetPkg, targetUid, mode);
-                }
-                needed.add(grantUri);
-            }
-        }
-        if (clip != null) {
-            for (int i=0; i<clip.getItemCount(); i++) {
-                Uri uri = clip.getItemAt(i).getUri();
-                if (uri != null) {
-                    GrantUri grantUri = GrantUri.resolve(contentUserHint, uri);
-                    targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
-                            targetUid);
-                    if (targetUid > 0) {
-                        if (needed == null) {
-                            needed = new NeededUriGrants(targetPkg, targetUid, mode);
-                        }
-                        needed.add(grantUri);
-                    }
-                } else {
-                    Intent clipIntent = clip.getItemAt(i).getIntent();
-                    if (clipIntent != null) {
-                        NeededUriGrants newNeeded = checkGrantUriPermissionFromIntentLocked(
-                                callingUid, targetPkg, clipIntent, mode, needed, targetUserId);
-                        if (newNeeded != null) {
-                            needed = newNeeded;
-                        }
-                    }
-                }
-            }
-        }
-
-        return needed;
-    }
-
-    /**
-     * Like grantUriPermissionUncheckedLocked, but takes an Intent.
-     */
-    @GuardedBy("this")
-    void grantUriPermissionUncheckedFromIntentLocked(NeededUriGrants needed,
-            UriPermissionOwner owner) {
-        if (needed != null) {
-            for (int i=0; i<needed.size(); i++) {
-                GrantUri grantUri = needed.get(i);
-                grantUriPermissionUncheckedLocked(needed.targetUid, needed.targetPkg,
-                        grantUri, needed.flags, owner);
-            }
-        }
-    }
-
-    @GuardedBy("this")
-    void grantUriPermissionFromIntentLocked(int callingUid,
-            String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId) {
-        NeededUriGrants needed = checkGrantUriPermissionFromIntentLocked(callingUid, targetPkg,
-                intent, intent != null ? intent.getFlags() : 0, null, targetUserId);
-        if (needed == null) {
-            return;
-        }
-
-        grantUriPermissionUncheckedFromIntentLocked(needed, owner);
+        return mUgmInternal.checkUriPermission(new GrantUri(userId, uri, false), uid, modeFlags)
+                ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
     }
 
     /**
@@ -9838,113 +7303,11 @@
                     | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                     | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
 
-            grantUriPermissionLocked(r.uid, targetPkg, grantUri, modeFlags, null,
+            mUgmInternal.grantUriPermission(r.uid, targetPkg, grantUri, modeFlags, null,
                     UserHandle.getUserId(r.uid));
         }
     }
 
-    @GuardedBy("this")
-    void removeUriPermissionIfNeededLocked(UriPermission perm) {
-        if (perm.modeFlags == 0) {
-            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(
-                    perm.targetUid);
-            if (perms != null) {
-                if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                        "Removing " + perm.targetUid + " permission to " + perm.uri);
-
-                perms.remove(perm.uri);
-                if (perms.isEmpty()) {
-                    mGrantedUriPermissions.remove(perm.targetUid);
-                }
-            }
-        }
-    }
-
-    @GuardedBy("this")
-    private void revokeUriPermissionLocked(String targetPackage, int callingUid, GrantUri grantUri,
-            final int modeFlags) {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                "Revoking all granted permissions to " + grantUri);
-
-        final IPackageManager pm = AppGlobals.getPackageManager();
-        final String authority = grantUri.uri.getAuthority();
-        final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId,
-                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
-        if (pi == null) {
-            Slog.w(TAG, "No content provider found for permission revoke: "
-                    + grantUri.toSafeString());
-            return;
-        }
-
-        // Does the caller have this permission on the URI?
-        if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
-            // If they don't have direct access to the URI, then revoke any
-            // ownerless URI permissions that have been granted to them.
-            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
-            if (perms != null) {
-                boolean persistChanged = false;
-                for (int i = perms.size()-1; i >= 0; i--) {
-                    final UriPermission perm = perms.valueAt(i);
-                    if (targetPackage != null && !targetPackage.equals(perm.targetPkg)) {
-                        continue;
-                    }
-                    if (perm.uri.sourceUserId == grantUri.sourceUserId
-                            && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
-                        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                                "Revoking non-owned " + perm.targetUid
-                                + " permission to " + perm.uri);
-                        persistChanged |= perm.revokeModes(
-                                modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
-                        if (perm.modeFlags == 0) {
-                            perms.removeAt(i);
-                        }
-                    }
-                }
-                if (perms.isEmpty()) {
-                    mGrantedUriPermissions.remove(callingUid);
-                }
-                if (persistChanged) {
-                    schedulePersistUriGrants();
-                }
-            }
-            return;
-        }
-
-        boolean persistChanged = false;
-
-        // Go through all of the permissions and remove any that match.
-        for (int i = mGrantedUriPermissions.size()-1; i >= 0; i--) {
-            final int targetUid = mGrantedUriPermissions.keyAt(i);
-            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
-
-            for (int j = perms.size()-1; j >= 0; j--) {
-                final UriPermission perm = perms.valueAt(j);
-                if (targetPackage != null && !targetPackage.equals(perm.targetPkg)) {
-                    continue;
-                }
-                if (perm.uri.sourceUserId == grantUri.sourceUserId
-                        && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
-                    if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                                "Revoking " + perm.targetUid + " permission to " + perm.uri);
-                    persistChanged |= perm.revokeModes(
-                            modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION,
-                            targetPackage == null);
-                    if (perm.modeFlags == 0) {
-                        perms.removeAt(j);
-                    }
-                }
-            }
-
-            if (perms.isEmpty()) {
-                mGrantedUriPermissions.removeAt(i);
-            }
-        }
-
-        if (persistChanged) {
-            schedulePersistUriGrants();
-        }
-    }
-
     /**
      * @param uri This uri must NOT contain an embedded userId.
      * @param userId The userId in which the uri is to be resolved.
@@ -9978,509 +7341,11 @@
                 return;
             }
 
-            revokeUriPermissionLocked(targetPackage, r.uid, new GrantUri(userId, uri, false),
+            mUgmInternal.revokeUriPermission(targetPackage, r.uid, new GrantUri(userId, uri, false),
                     modeFlags);
         }
     }
 
-    /**
-     * Remove any {@link UriPermission} granted <em>from</em> or <em>to</em> the
-     * given package.
-     *
-     * @param packageName Package name to match, or {@code null} to apply to all
-     *            packages.
-     * @param userHandle User to match, or {@link UserHandle#USER_ALL} to apply
-     *            to all users.
-     * @param persistable If persistable grants should be removed.
-     * @param targetOnly When {@code true}, only remove permissions where the app is the target,
-     * not source.
-     */
-    @GuardedBy("this")
-    private void removeUriPermissionsForPackageLocked(
-            String packageName, int userHandle, boolean persistable, boolean targetOnly) {
-        if (userHandle == UserHandle.USER_ALL && packageName == null) {
-            throw new IllegalArgumentException("Must narrow by either package or user");
-        }
-
-        boolean persistChanged = false;
-
-        int N = mGrantedUriPermissions.size();
-        for (int i = 0; i < N; i++) {
-            final int targetUid = mGrantedUriPermissions.keyAt(i);
-            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
-
-            // Only inspect grants matching user
-            if (userHandle == UserHandle.USER_ALL
-                    || userHandle == UserHandle.getUserId(targetUid)) {
-                for (Iterator<UriPermission> it = perms.values().iterator(); it.hasNext();) {
-                    final UriPermission perm = it.next();
-
-                    // Only inspect grants matching package
-                    if (packageName == null || (!targetOnly && perm.sourcePkg.equals(packageName))
-                            || perm.targetPkg.equals(packageName)) {
-                        // Hacky solution as part of fixing a security bug; ignore
-                        // grants associated with DownloadManager so we don't have
-                        // to immediately launch it to regrant the permissions
-                        if (Downloads.Impl.AUTHORITY.equals(perm.uri.uri.getAuthority())
-                                && !persistable) continue;
-
-                        persistChanged |= perm.revokeModes(persistable
-                                ? ~0 : ~Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, true);
-
-                        // Only remove when no modes remain; any persisted grants
-                        // will keep this alive.
-                        if (perm.modeFlags == 0) {
-                            it.remove();
-                        }
-                    }
-                }
-
-                if (perms.isEmpty()) {
-                    mGrantedUriPermissions.remove(targetUid);
-                    N--;
-                    i--;
-                }
-            }
-        }
-
-        if (persistChanged) {
-            schedulePersistUriGrants();
-        }
-    }
-
-    @Override
-    public IBinder newUriPermissionOwner(String name) {
-        enforceNotIsolatedCaller("newUriPermissionOwner");
-        synchronized(this) {
-            UriPermissionOwner owner = new UriPermissionOwner(this, name);
-            return owner.getExternalTokenLocked();
-        }
-    }
-
-    @Override
-    public IBinder getUriPermissionOwnerForActivity(IBinder activityToken) {
-        enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
-        synchronized(this) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
-            if (r == null) {
-                throw new IllegalArgumentException("Activity does not exist; token="
-                        + activityToken);
-            }
-            return r.getUriPermissionsLocked().getExternalTokenLocked();
-        }
-    }
-    /**
-     * @param uri This uri must NOT contain an embedded userId.
-     * @param sourceUserId The userId in which the uri is to be resolved.
-     * @param targetUserId The userId of the app that receives the grant.
-     */
-    @Override
-    public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, Uri uri,
-            final int modeFlags, int sourceUserId, int targetUserId) {
-        targetUserId = mUserController.handleIncomingUser(Binder.getCallingPid(),
-                Binder.getCallingUid(), targetUserId, false, ALLOW_FULL_ONLY,
-                "grantUriPermissionFromOwner", null);
-        synchronized(this) {
-            UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
-            if (owner == null) {
-                throw new IllegalArgumentException("Unknown owner: " + token);
-            }
-            if (fromUid != Binder.getCallingUid()) {
-                if (Binder.getCallingUid() != myUid()) {
-                    // Only system code can grant URI permissions on behalf
-                    // of other users.
-                    throw new SecurityException("nice try");
-                }
-            }
-            if (targetPkg == null) {
-                throw new IllegalArgumentException("null target");
-            }
-            if (uri == null) {
-                throw new IllegalArgumentException("null uri");
-            }
-
-            grantUriPermissionLocked(fromUid, targetPkg, new GrantUri(sourceUserId, uri, false),
-                    modeFlags, owner, targetUserId);
-        }
-    }
-
-    /**
-     * @param uri This uri must NOT contain an embedded userId.
-     * @param userId The userId in which the uri is to be resolved.
-     */
-    @Override
-    public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId) {
-        synchronized(this) {
-            UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
-            if (owner == null) {
-                throw new IllegalArgumentException("Unknown owner: " + token);
-            }
-
-            if (uri == null) {
-                owner.removeUriPermissionsLocked(mode);
-            } else {
-                final boolean prefix = (mode & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0;
-                owner.removeUriPermissionLocked(new GrantUri(userId, uri, prefix), mode);
-            }
-        }
-    }
-
-    private void schedulePersistUriGrants() {
-        if (!mHandler.hasMessages(PERSIST_URI_GRANTS_MSG)) {
-            mHandler.sendMessageDelayed(mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG),
-                    10 * DateUtils.SECOND_IN_MILLIS);
-        }
-    }
-
-    private void writeGrantedUriPermissions() {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "writeGrantedUriPermissions()");
-
-        final long startTime = SystemClock.uptimeMillis();
-
-        // Snapshot permissions so we can persist without lock
-        ArrayList<UriPermission.Snapshot> persist = Lists.newArrayList();
-        synchronized (this) {
-            final int size = mGrantedUriPermissions.size();
-            for (int i = 0; i < size; i++) {
-                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
-                for (UriPermission perm : perms.values()) {
-                    if (perm.persistedModeFlags != 0) {
-                        persist.add(perm.snapshot());
-                    }
-                }
-            }
-        }
-
-        FileOutputStream fos = null;
-        try {
-            fos = mGrantFile.startWrite(startTime);
-
-            XmlSerializer out = new FastXmlSerializer();
-            out.setOutput(fos, StandardCharsets.UTF_8.name());
-            out.startDocument(null, true);
-            out.startTag(null, TAG_URI_GRANTS);
-            for (UriPermission.Snapshot perm : persist) {
-                out.startTag(null, TAG_URI_GRANT);
-                writeIntAttribute(out, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId);
-                writeIntAttribute(out, ATTR_TARGET_USER_ID, perm.targetUserId);
-                out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg);
-                out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
-                out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri));
-                writeBooleanAttribute(out, ATTR_PREFIX, perm.uri.prefix);
-                writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags);
-                writeLongAttribute(out, ATTR_CREATED_TIME, perm.persistedCreateTime);
-                out.endTag(null, TAG_URI_GRANT);
-            }
-            out.endTag(null, TAG_URI_GRANTS);
-            out.endDocument();
-
-            mGrantFile.finishWrite(fos);
-        } catch (IOException e) {
-            if (fos != null) {
-                mGrantFile.failWrite(fos);
-            }
-        }
-    }
-
-    @GuardedBy("this")
-    private void readGrantedUriPermissionsLocked() {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "readGrantedUriPermissions()");
-
-        final long now = System.currentTimeMillis();
-
-        FileInputStream fis = null;
-        try {
-            fis = mGrantFile.openRead();
-            final XmlPullParser in = Xml.newPullParser();
-            in.setInput(fis, StandardCharsets.UTF_8.name());
-
-            int type;
-            while ((type = in.next()) != END_DOCUMENT) {
-                final String tag = in.getName();
-                if (type == START_TAG) {
-                    if (TAG_URI_GRANT.equals(tag)) {
-                        final int sourceUserId;
-                        final int targetUserId;
-                        final int userHandle = readIntAttribute(in,
-                                ATTR_USER_HANDLE, UserHandle.USER_NULL);
-                        if (userHandle != UserHandle.USER_NULL) {
-                            // For backwards compatibility.
-                            sourceUserId = userHandle;
-                            targetUserId = userHandle;
-                        } else {
-                            sourceUserId = readIntAttribute(in, ATTR_SOURCE_USER_ID);
-                            targetUserId = readIntAttribute(in, ATTR_TARGET_USER_ID);
-                        }
-                        final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG);
-                        final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG);
-                        final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
-                        final boolean prefix = readBooleanAttribute(in, ATTR_PREFIX);
-                        final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS);
-                        final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now);
-
-                        // Sanity check that provider still belongs to source package
-                        // Both direct boot aware and unaware packages are fine as we
-                        // will do filtering at query time to avoid multiple parsing.
-                        final ProviderInfo pi = getProviderInfoLocked(
-                                uri.getAuthority(), sourceUserId, MATCH_DIRECT_BOOT_AWARE
-                                        | MATCH_DIRECT_BOOT_UNAWARE);
-                        if (pi != null && sourcePkg.equals(pi.packageName)) {
-                            int targetUid = -1;
-                            try {
-                                targetUid = AppGlobals.getPackageManager().getPackageUid(
-                                        targetPkg, MATCH_UNINSTALLED_PACKAGES, targetUserId);
-                            } catch (RemoteException e) {
-                            }
-                            if (targetUid != -1) {
-                                final UriPermission perm = findOrCreateUriPermissionLocked(
-                                        sourcePkg, targetPkg, targetUid,
-                                        new GrantUri(sourceUserId, uri, prefix));
-                                perm.initPersistedModes(modeFlags, createdTime);
-                            }
-                        } else {
-                            Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
-                                    + " but instead found " + pi);
-                        }
-                    }
-                }
-            }
-        } catch (FileNotFoundException e) {
-            // Missing grants is okay
-        } catch (IOException e) {
-            Slog.wtf(TAG, "Failed reading Uri grants", e);
-        } catch (XmlPullParserException e) {
-            Slog.wtf(TAG, "Failed reading Uri grants", e);
-        } finally {
-            IoUtils.closeQuietly(fis);
-        }
-    }
-
-    /**
-     * @param uri This uri must NOT contain an embedded userId.
-     * @param toPackage Name of package whose uri is being granted to (if {@code null}, uses
-     * calling uid)
-     * @param userId The userId in which the uri is to be resolved.
-     */
-    @Override
-    public void takePersistableUriPermission(Uri uri, final int modeFlags,
-            @Nullable String toPackage, int userId) {
-        final int uid;
-        if (toPackage != null) {
-            enforceCallingPermission(android.Manifest.permission.FORCE_PERSISTABLE_URI_PERMISSIONS,
-                    "takePersistableUriPermission");
-            uid = mPackageManagerInt.getPackageUid(toPackage, 0, userId);
-        } else {
-            enforceNotIsolatedCaller("takePersistableUriPermission");
-            uid = Binder.getCallingUid();
-        }
-
-        Preconditions.checkFlagsArgument(modeFlags,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        synchronized (this) {
-            boolean persistChanged = false;
-            GrantUri grantUri = new GrantUri(userId, uri, false);
-
-            UriPermission exactPerm = findUriPermissionLocked(uid, grantUri);
-            UriPermission prefixPerm = findUriPermissionLocked(uid,
-                    new GrantUri(userId, uri, true));
-
-            final boolean exactValid = (exactPerm != null)
-                    && ((modeFlags & exactPerm.persistableModeFlags) == modeFlags);
-            final boolean prefixValid = (prefixPerm != null)
-                    && ((modeFlags & prefixPerm.persistableModeFlags) == modeFlags);
-
-            if (!(exactValid || prefixValid)) {
-                throw new SecurityException("No persistable permission grants found for UID "
-                        + uid + " and Uri " + grantUri.toSafeString());
-            }
-
-            if (exactValid) {
-                persistChanged |= exactPerm.takePersistableModes(modeFlags);
-            }
-            if (prefixValid) {
-                persistChanged |= prefixPerm.takePersistableModes(modeFlags);
-            }
-
-            persistChanged |= maybePrunePersistedUriGrantsLocked(uid);
-
-            if (persistChanged) {
-                schedulePersistUriGrants();
-            }
-        }
-    }
-
-    /**
-     * @param uri This uri must NOT contain an embedded userId.
-     * @param toPackage Name of the target package whose uri is being released (if {@code null},
-     * uses calling uid)
-     * @param userId The userId in which the uri is to be resolved.
-     */
-    @Override
-    public void releasePersistableUriPermission(Uri uri, final int modeFlags,
-            @Nullable String toPackage, int userId) {
-
-        final int uid;
-        if (toPackage != null) {
-            enforceCallingPermission(android.Manifest.permission.FORCE_PERSISTABLE_URI_PERMISSIONS,
-                    "releasePersistableUriPermission");
-            uid = mPackageManagerInt.getPackageUid(toPackage, 0, userId);
-        } else {
-            enforceNotIsolatedCaller("releasePersistableUriPermission");
-            uid = Binder.getCallingUid();
-        }
-
-        Preconditions.checkFlagsArgument(modeFlags,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        synchronized (this) {
-            boolean persistChanged = false;
-
-            UriPermission exactPerm = findUriPermissionLocked(uid,
-                    new GrantUri(userId, uri, false));
-            UriPermission prefixPerm = findUriPermissionLocked(uid,
-                    new GrantUri(userId, uri, true));
-            if (exactPerm == null && prefixPerm == null && toPackage == null) {
-                throw new SecurityException("No permission grants found for UID " + uid
-                        + " and Uri " + uri.toSafeString());
-            }
-
-            if (exactPerm != null) {
-                persistChanged |= exactPerm.releasePersistableModes(modeFlags);
-                removeUriPermissionIfNeededLocked(exactPerm);
-            }
-            if (prefixPerm != null) {
-                persistChanged |= prefixPerm.releasePersistableModes(modeFlags);
-                removeUriPermissionIfNeededLocked(prefixPerm);
-            }
-
-            if (persistChanged) {
-                schedulePersistUriGrants();
-            }
-        }
-    }
-
-    /**
-     * Prune any older {@link UriPermission} for the given UID until outstanding
-     * persisted grants are below {@link #MAX_PERSISTED_URI_GRANTS}.
-     *
-     * @return if any mutations occured that require persisting.
-     */
-    @GuardedBy("this")
-    private boolean maybePrunePersistedUriGrantsLocked(int uid) {
-        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid);
-        if (perms == null) return false;
-        if (perms.size() < MAX_PERSISTED_URI_GRANTS) return false;
-
-        final ArrayList<UriPermission> persisted = Lists.newArrayList();
-        for (UriPermission perm : perms.values()) {
-            if (perm.persistedModeFlags != 0) {
-                persisted.add(perm);
-            }
-        }
-
-        final int trimCount = persisted.size() - MAX_PERSISTED_URI_GRANTS;
-        if (trimCount <= 0) return false;
-
-        Collections.sort(persisted, new UriPermission.PersistedTimeComparator());
-        for (int i = 0; i < trimCount; i++) {
-            final UriPermission perm = persisted.get(i);
-
-            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                    "Trimming grant created at " + perm.persistedCreateTime);
-
-            perm.releasePersistableModes(~0);
-            removeUriPermissionIfNeededLocked(perm);
-        }
-
-        return true;
-    }
-
-    @Override
-    public ParceledListSlice<android.content.UriPermission> getPersistedUriPermissions(
-            String packageName, boolean incoming) {
-        enforceNotIsolatedCaller("getPersistedUriPermissions");
-        Preconditions.checkNotNull(packageName, "packageName");
-
-        final int callingUid = Binder.getCallingUid();
-        final int callingUserId = UserHandle.getUserId(callingUid);
-        final IPackageManager pm = AppGlobals.getPackageManager();
-        try {
-            final int packageUid = pm.getPackageUid(packageName,
-                    MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, callingUserId);
-            if (packageUid != callingUid) {
-                throw new SecurityException(
-                        "Package " + packageName + " does not belong to calling UID " + callingUid);
-            }
-        } catch (RemoteException e) {
-            throw new SecurityException("Failed to verify package name ownership");
-        }
-
-        final ArrayList<android.content.UriPermission> result = Lists.newArrayList();
-        synchronized (this) {
-            if (incoming) {
-                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(
-                        callingUid);
-                if (perms == null) {
-                    Slog.w(TAG, "No permission grants found for " + packageName);
-                } else {
-                    for (int j = 0; j < perms.size(); j++) {
-                        final UriPermission perm = perms.valueAt(j);
-                        if (packageName.equals(perm.targetPkg) && perm.persistedModeFlags != 0) {
-                            result.add(perm.buildPersistedPublicApiObject());
-                        }
-                    }
-                }
-            } else {
-                final int size = mGrantedUriPermissions.size();
-                for (int i = 0; i < size; i++) {
-                    final ArrayMap<GrantUri, UriPermission> perms =
-                            mGrantedUriPermissions.valueAt(i);
-                    for (int j = 0; j < perms.size(); j++) {
-                        final UriPermission perm = perms.valueAt(j);
-                        if (packageName.equals(perm.sourcePkg) && perm.persistedModeFlags != 0) {
-                            result.add(perm.buildPersistedPublicApiObject());
-                        }
-                    }
-                }
-            }
-        }
-        return new ParceledListSlice<android.content.UriPermission>(result);
-    }
-
-    @Override
-    public ParceledListSlice<GrantedUriPermission> getGrantedUriPermissions(
-            @Nullable String packageName, int userId) {
-        enforceCallingPermission(android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS,
-                "getGrantedUriPermissions");
-
-        final List<GrantedUriPermission> result = new ArrayList<>();
-        synchronized (this) {
-            final int size = mGrantedUriPermissions.size();
-            for (int i = 0; i < size; i++) {
-                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
-                for (int j = 0; j < perms.size(); j++) {
-                    final UriPermission perm = perms.valueAt(j);
-                    if ((packageName == null || packageName.equals(perm.targetPkg))
-                            && perm.targetUserId == userId
-                            && perm.persistedModeFlags != 0) {
-                        result.add(perm.buildGrantedUriPermission());
-                    }
-                }
-            }
-        }
-        return new ParceledListSlice<>(result);
-    }
-
-    @Override
-    public void clearGrantedUriPermissions(String packageName, int userId) {
-        enforceCallingPermission(android.Manifest.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS,
-                "clearGrantedUriPermissions");
-        synchronized(this) {
-            removeUriPermissionsForPackageLocked(packageName, userId, true, true);
-        }
-    }
-
     @Override
     public void showWaitingForDebugger(IApplicationThread who, boolean waiting) {
         synchronized (this) {
@@ -10518,494 +7383,55 @@
     // =========================================================
 
     @Override
-    public List<IBinder> getAppTasks(String callingPackage) {
-        int callingUid = Binder.getCallingUid();
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized(this) {
-                return mRecentTasks.getAppTasksList(callingUid, callingPackage);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
     public List<RunningTaskInfo> getTasks(int maxNum) {
-       return getFilteredTasks(maxNum, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED);
+        return mActivityTaskManager.getTasks(maxNum);
     }
 
     @Override
     public List<RunningTaskInfo> getFilteredTasks(int maxNum, @ActivityType int ignoreActivityType,
             @WindowingMode int ignoreWindowingMode) {
-        final int callingUid = Binder.getCallingUid();
-        ArrayList<RunningTaskInfo> list = new ArrayList<>();
-
-        synchronized(this) {
-            if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum);
-
-            final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
-                    callingUid);
-            mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
-                    ignoreWindowingMode, callingUid, allowed);
-        }
-
-        return list;
-    }
-
-    private boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
-        if (mRecentTasks.isCallerRecents(callingUid)) {
-            // Always allow the recents component to get tasks
-            return true;
-        }
-
-        boolean allowed = checkPermission(android.Manifest.permission.REAL_GET_TASKS,
-                callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
-        if (!allowed) {
-            if (checkPermission(android.Manifest.permission.GET_TASKS,
-                    callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) {
-                // Temporary compatibility: some existing apps on the system image may
-                // still be requesting the old permission and not switched to the new
-                // one; if so, we'll still allow them full access.  This means we need
-                // to see if they are holding the old permission and are a system app.
-                try {
-                    if (AppGlobals.getPackageManager().isUidPrivileged(callingUid)) {
-                        allowed = true;
-                        if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
-                                + " is using old GET_TASKS but privileged; allowing");
-                    }
-                } catch (RemoteException e) {
-                }
-            }
-        }
-        if (!allowed) {
-            if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
-                    + " does not hold REAL_GET_TASKS; limiting output");
-        }
-        return allowed;
-    }
-
-    @Override
-    public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
-            int userId) {
-        final int callingUid = Binder.getCallingUid();
-        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
-                false, ALLOW_FULL_ONLY, "getRecentTasks", null);
-        final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
-                callingUid);
-        final boolean detailed = checkCallingPermission(
-                android.Manifest.permission.GET_DETAILED_TASKS)
-                        == PackageManager.PERMISSION_GRANTED;
-
-        synchronized (this) {
-            return mRecentTasks.getRecentTasks(maxNum, flags, allowed, detailed, userId,
-                    callingUid);
-        }
-    }
-
-    @Override
-    public ActivityManager.TaskDescription getTaskDescription(int id) {
-        synchronized (this) {
-            enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getTaskDescription()");
-            final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
-                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
-            if (tr != null) {
-                return tr.lastTaskDescription;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public int addAppTask(IBinder activityToken, Intent intent,
-            ActivityManager.TaskDescription description, Bitmap thumbnail) throws RemoteException {
-        final int callingUid = Binder.getCallingUid();
-        final long callingIdent = Binder.clearCallingIdentity();
-
-        try {
-            synchronized (this) {
-                ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
-                if (r == null) {
-                    throw new IllegalArgumentException("Activity does not exist; token="
-                            + activityToken);
-                }
-                ComponentName comp = intent.getComponent();
-                if (comp == null) {
-                    throw new IllegalArgumentException("Intent " + intent
-                            + " must specify explicit component");
-                }
-                if (thumbnail.getWidth() != mThumbnailWidth
-                        || thumbnail.getHeight() != mThumbnailHeight) {
-                    throw new IllegalArgumentException("Bad thumbnail size: got "
-                            + thumbnail.getWidth() + "x" + thumbnail.getHeight() + ", require "
-                            + mThumbnailWidth + "x" + mThumbnailHeight);
-                }
-                if (intent.getSelector() != null) {
-                    intent.setSelector(null);
-                }
-                if (intent.getSourceBounds() != null) {
-                    intent.setSourceBounds(null);
-                }
-                if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0) {
-                    if ((intent.getFlags()&Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS) == 0) {
-                        // The caller has added this as an auto-remove task...  that makes no
-                        // sense, so turn off auto-remove.
-                        intent.addFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
-                    }
-                }
-                final ActivityInfo ainfo = AppGlobals.getPackageManager().getActivityInfo(comp,
-                        STOCK_PM_FLAGS, UserHandle.getUserId(callingUid));
-                if (ainfo.applicationInfo.uid != callingUid) {
-                    throw new SecurityException(
-                            "Can't add task for another application: target uid="
-                            + ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
-                }
-
-                final ActivityStack stack = r.getStack();
-                final TaskRecord task = stack.createTaskRecord(
-                        mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent,
-                        null /* voiceSession */, null /* voiceInteractor */, !ON_TOP);
-                if (!mRecentTasks.addToBottom(task)) {
-                    // The app has too many tasks already and we can't add any more
-                    stack.removeTask(task, "addAppTask", REMOVE_TASK_MODE_DESTROYING);
-                    return INVALID_TASK_ID;
-                }
-                task.lastTaskDescription.copyFrom(description);
-
-                // TODO: Send the thumbnail to WM to store it.
-
-                return task.taskId;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingIdent);
-        }
-    }
-
-    @Override
-    public Point getAppTaskThumbnailSize() {
-        synchronized (this) {
-            return new Point(mThumbnailWidth,  mThumbnailHeight);
-        }
-    }
-
-    @Override
-    public void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) {
-        synchronized (this) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r != null) {
-                r.setTaskDescription(td);
-                final TaskRecord task = r.getTask();
-                task.updateTaskDescription();
-                mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td);
-            }
-        }
-    }
-
-    @Override
-    public void setTaskResizeable(int taskId, int resizeableMode) {
-        synchronized (this) {
-            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
-                    taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
-            if (task == null) {
-                Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
-                return;
-            }
-            task.setResizeMode(resizeableMode);
-        }
-    }
-
-    @Override
-    public void resizeTask(int taskId, Rect bounds, int resizeMode) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()");
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task == null) {
-                    Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
-                    return;
-                }
-                // Place the task in the right stack if it isn't there already based on
-                // the requested bounds.
-                // The stack transition logic is:
-                // - a null bounds on a freeform task moves that task to fullscreen
-                // - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
-                //   that task to freeform
-                // - otherwise the task is not moved
-                ActivityStack stack = task.getStack();
-                if (!task.getWindowConfiguration().canResizeTask()) {
-                    throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
-                }
-                if (bounds == null && stack.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
-                    stack = stack.getDisplay().getOrCreateStack(
-                            WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
-                } else if (bounds != null && stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
-                    stack = stack.getDisplay().getOrCreateStack(
-                            WINDOWING_MODE_FREEFORM, stack.getActivityType(), ON_TOP);
-                }
-
-                // Reparent the task to the right stack if necessary
-                boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
-                if (stack != task.getStack()) {
-                    // Defer resume until the task is resized below
-                    task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
-                            DEFER_RESUME, "resizeTask");
-                    preserveWindow = false;
-                }
-
-                // After reparenting (which only resizes the task to the stack bounds), resize the
-                // task to the actual bounds provided
-                task.resize(bounds, resizeMode, preserveWindow, !DEFER_RESUME);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public Rect getTaskBounds(int taskId) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getTaskBounds()");
-        long ident = Binder.clearCallingIdentity();
-        Rect rect = new Rect();
-        try {
-            synchronized (this) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
-                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
-                if (task == null) {
-                    Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
-                    return rect;
-                }
-                if (task.getStack() != null) {
-                    // Return the bounds from window manager since it will be adjusted for various
-                    // things like the presense of a docked stack for tasks that aren't resizeable.
-                    task.getWindowContainerBounds(rect);
-                } else {
-                    // Task isn't in window manager yet since it isn't associated with a stack.
-                    // Return the persist value from activity manager
-                    if (!task.matchParentBounds()) {
-                        rect.set(task.getBounds());
-                    } else if (task.mLastNonFullscreenBounds != null) {
-                        rect.set(task.mLastNonFullscreenBounds);
-                    }
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-        return rect;
+        return mActivityTaskManager.getFilteredTasks(
+                maxNum, ignoreActivityType, ignoreWindowingMode);
     }
 
     @Override
     public void cancelTaskWindowTransition(int taskId) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "cancelTaskWindowTransition()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
-                        MATCH_TASK_IN_STACKS_ONLY);
-                if (task == null) {
-                    Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found");
-                    return;
-                }
-                task.cancelWindowTransition();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+        mActivityTaskManager.cancelTaskWindowTransition(taskId);
     }
 
     @Override
-    public TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
-        enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            final TaskRecord task;
-            synchronized (this) {
-                task = mStackSupervisor.anyTaskForIdLocked(taskId,
-                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
-                if (task == null) {
-                    Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
-                    return null;
-                }
-            }
-            // Don't call this while holding the lock as this operation might hit the disk.
-            return task.getSnapshot(reducedResolution);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+    public void setTaskResizeable(int taskId, int resizeableMode) {
+        mActivityTaskManager.setTaskResizeable(taskId, resizeableMode);
     }
 
     @Override
-    public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
-        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
-                userId, false, ALLOW_FULL_ONLY, "getTaskDescriptionIcon", null);
-
-        final File passedIconFile = new File(filePath);
-        final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId),
-                passedIconFile.getName());
-        if (!legitIconFile.getPath().equals(filePath)
-                || !filePath.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
-            throw new IllegalArgumentException("Bad file path: " + filePath
-                    + " passed for userId " + userId);
-        }
-        return mRecentTasks.getTaskDescriptionIcon(filePath);
+    public ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
+        return mActivityTaskManager.getTaskSnapshot(taskId, reducedResolution);
     }
 
     @Override
-    public void startInPlaceAnimationOnFrontMostApplication(Bundle opts)
-            throws RemoteException {
-        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(opts);
-        final ActivityOptions activityOptions = safeOptions != null
-                ? safeOptions.getOptions(mStackSupervisor)
-                : null;
-        if (activityOptions == null
-                || activityOptions.getAnimationType() != ActivityOptions.ANIM_CUSTOM_IN_PLACE
-                || activityOptions.getCustomInPlaceResId() == 0) {
-            throw new IllegalArgumentException("Expected in-place ActivityOption " +
-                    "with valid animation");
-        }
-        mWindowManager.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
-        mWindowManager.overridePendingAppTransitionInPlace(activityOptions.getPackageName(),
-                activityOptions.getCustomInPlaceResId());
-        mWindowManager.executeAppTransition();
+    public void resizeTask(int taskId, Rect bounds, int resizeMode) {
+        mActivityTaskManager.resizeTask(taskId, bounds, resizeMode);
+    }
+
+    @Override
+    public Rect getTaskBounds(int taskId) {
+        return mActivityTaskManager.getTaskBounds(taskId);
     }
 
     @Override
     public void removeStack(int stackId) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
-        synchronized (this) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
-                if (stack == null) {
-                    Slog.w(TAG, "removeStack: No stack with id=" + stackId);
-                    return;
-                }
-                if (!stack.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException(
-                            "Removing non-standard stack is not allowed.");
-                }
-                mStackSupervisor.removeStack(stack);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
-     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
-     */
-    @Override
-    public void removeStacksInWindowingModes(int[] windowingModes) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "removeStacksInWindowingModes()");
-        synchronized (this) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mStackSupervisor.removeStacksInWindowingModes(windowingModes);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void removeStacksWithActivityTypes(int[] activityTypes) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "removeStacksWithActivityTypes()");
-        synchronized (this) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mStackSupervisor.removeStacksWithActivityTypes(activityTypes);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void moveStackToDisplay(int stackId, int displayId) {
-        enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
-
-        synchronized (this) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveStackToDisplay: moving stackId=" + stackId
-                        + " to displayId=" + displayId);
-                mStackSupervisor.moveStackToDisplayLocked(stackId, displayId, ON_TOP);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
+        mActivityTaskManager.removeStack(stackId);
     }
 
     @Override
     public boolean removeTask(int taskId) {
-        enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
-        synchronized (this) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS,
-                        "remove-task");
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
+        return mActivityTaskManager.removeTask(taskId);
     }
 
-    /**
-     * TODO: Add mController hook
-     */
     @Override
     public void moveTaskToFront(int taskId, int flags, Bundle bOptions) {
-        enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()");
-
-        if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId);
-        synchronized(this) {
-            moveTaskToFrontLocked(taskId, flags, SafeActivityOptions.fromBundle(bOptions),
-                    false /* fromRecents */);
-        }
-    }
-
-    void moveTaskToFrontLocked(int taskId, int flags, SafeActivityOptions options,
-            boolean fromRecents) {
-
-        if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
-                Binder.getCallingUid(), -1, -1, "Task to front")) {
-            SafeActivityOptions.abort(options);
-            return;
-        }
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-            if (task == null) {
-                Slog.d(TAG, "Could not find task for id: "+ taskId);
-                return;
-            }
-            if (mLockTaskController.isLockTaskModeViolation(task)) {
-                Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
-                return;
-            }
-            ActivityOptions realOptions = options != null
-                    ? options.getOptions(mStackSupervisor)
-                    : null;
-            mStackSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",
-                    false /* forceNonResizable */);
-
-            final ActivityRecord topActivity = task.getTopActivity();
-            if (topActivity != null) {
-
-                // We are reshowing a task, use a starting window to hide the initial draw delay
-                // so the transition can start earlier.
-                topActivity.showStartingWindow(null /* prev */, false /* newTask */,
-                        true /* taskSwitch */, fromRecents);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-        SafeActivityOptions.abort(options);
+        mActivityTaskManager.moveTaskToFront(taskId, flags, bOptions);
     }
 
     /**
@@ -11029,290 +7455,25 @@
      */
     @Override
     public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
-        enforceNotIsolatedCaller("moveActivityTaskToBack");
-        synchronized(this) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task != null) {
-                    return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public void moveTaskBackwards(int task) {
-        enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
-                "moveTaskBackwards()");
-
-        synchronized(this) {
-            if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
-                    Binder.getCallingUid(), -1, -1, "Task backwards")) {
-                return;
-            }
-            final long origId = Binder.clearCallingIdentity();
-            moveTaskBackwardsLocked(task);
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    private final void moveTaskBackwardsLocked(int task) {
-        Slog.e(TAG, "moveTaskBackwards not yet implemented!");
-    }
-
-    @Override
-    public int createStackOnDisplay(int displayId) throws RemoteException {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()");
-        synchronized (this) {
-            final ActivityDisplay display =
-                    mStackSupervisor.getActivityDisplayOrCreateLocked(displayId);
-            if (display == null) {
-                return INVALID_STACK_ID;
-            }
-            // TODO(multi-display): Have the caller pass in the windowing mode and activity type.
-            final ActivityStack stack = display.createStack(
-                    WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
-                    ON_TOP);
-            return (stack != null) ? stack.mStackId : INVALID_STACK_ID;
-        }
-    }
-
-    @Override
-    public int getActivityDisplayId(IBinder activityToken) throws RemoteException {
-        synchronized (this) {
-            final ActivityStack stack = ActivityRecord.getStackLocked(activityToken);
-            if (stack != null && stack.mDisplayId != INVALID_DISPLAY) {
-                return stack.mDisplayId;
-            }
-            return DEFAULT_DISPLAY;
-        }
-    }
-
-    @Override
-    public void exitFreeformMode(IBinder token) throws RemoteException {
-        synchronized (this) {
-            long ident = Binder.clearCallingIdentity();
-            try {
-                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-                if (r == null) {
-                    throw new IllegalArgumentException(
-                            "exitFreeformMode: No activity record matching token=" + token);
-                }
-
-                final ActivityStack stack = r.getStack();
-                if (stack == null || !stack.inFreeformWindowingMode()) {
-                    throw new IllegalStateException(
-                            "exitFreeformMode: You can only go fullscreen from freeform.");
-                }
-
-                stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
-        if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
-                    toTop, ANIMATE, null /* initialBounds */, true /* showRecents */);
-            return;
-        }
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
-        synchronized (this) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task == null) {
-                    Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
-                    return;
-                }
-
-                if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId
-                        + " to windowingMode=" + windowingMode + " toTop=" + toTop);
-
-                if (!task.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
-                            + " non-standard task " + taskId + " to windowing mode="
-                            + windowingMode);
-                }
-
-                final ActivityStack stack = task.getStack();
-                if (toTop) {
-                    stack.moveToFront("setTaskWindowingMode", task);
-                }
-                stack.setWindowingMode(windowingMode);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    /**
-     * Moves the specified task to the primary-split-screen stack.
-     *
-     * @param taskId Id of task to move.
-     * @param createMode The mode the primary split screen stack should be created in if it doesn't
-     *                   exist already. See
-     *                   {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
-     *                   and
-     *                   {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
-     * @param toTop If the task and stack should be moved to the top.
-     * @param animate Whether we should play an animation for the moving the task.
-     * @param initialBounds If the primary stack gets created, it will use these bounds for the
-     *                      stack. Pass {@code null} to use default bounds.
-     * @param showRecents If the recents activity should be shown on the other side of the task
-     *                    going into split-screen mode.
-     */
-    @Override
-    public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
-            boolean animate, Rect initialBounds, boolean showRecents) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "setTaskWindowingModeSplitScreenPrimary()");
-        synchronized (this) {
-            long ident = Binder.clearCallingIdentity();
-            try {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task == null) {
-                    Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
-                    return false;
-                }
-                if (DEBUG_STACK) Slog.d(TAG_STACK,
-                        "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
-                        + " to createMode=" + createMode + " toTop=" + toTop);
-                if (!task.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
-                            + " non-standard task " + taskId + " to split-screen windowing mode");
-                }
-
-                mWindowManager.setDockedStackCreateState(createMode, initialBounds);
-                final int windowingMode = task.getWindowingMode();
-                final ActivityStack stack = task.getStack();
-                if (toTop) {
-                    stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
-                }
-                stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
-                        false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */);
-                return windowingMode != task.getWindowingMode();
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
+        return mActivityTaskManager.moveActivityTaskToBack(token, nonRoot);
     }
 
     @Override
     public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
-        synchronized (this) {
-            long ident = Binder.clearCallingIdentity();
-            try {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task == null) {
-                    Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
-                    return;
-                }
-
-                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
-                        + " to stackId=" + stackId + " toTop=" + toTop);
-
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
-                if (stack == null) {
-                    throw new IllegalStateException(
-                            "moveTaskToStack: No stack for stackId=" + stackId);
-                }
-                if (!stack.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException("moveTaskToStack: Attempt to move task "
-                            + taskId + " to stack " + stackId);
-                }
-                if (stack.inSplitScreenPrimaryWindowingMode()) {
-                    mWindowManager.setDockedStackCreateState(
-                            SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
-                }
-                task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
-                        "moveTaskToStack");
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
+        mActivityTaskManager.moveTaskToStack(taskId, stackId, toTop);
     }
 
-    /**
-     * Dismisses split-screen multi-window mode.
-     * @param toTop If true the current primary split-screen stack will be placed or left on top.
-     */
     @Override
-    public void dismissSplitScreenMode(boolean toTop) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                final ActivityStack stack =
-                        mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
-                if (stack == null) {
-                    Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
-                    return;
-                }
-
-                if (toTop) {
-                    // Caller wants the current split-screen primary stack to be the top stack after
-                    // it goes fullscreen, so move it to the front.
-                    stack.moveToFront("dismissSplitScreenMode");
-                } else if (mStackSupervisor.isFocusedStack(stack)) {
-                    // In this case the current split-screen primary stack shouldn't be the top
-                    // stack after it goes fullscreen, but it current has focus, so we move the
-                    // focus to the top-most split-screen secondary stack next to it.
-                    final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
-                            WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-                    if (otherStack != null) {
-                        otherStack.moveToFront("dismissSplitScreenMode_other");
-                    }
-                }
-
-                stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+    public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode,
+            boolean preserveWindows, boolean animate, int animationDuration) {
+        mActivityTaskManager.resizeStack(stackId, destBounds, allowResizeInDockedMode,
+                preserveWindows, animate, animationDuration);
     }
 
-    /**
-     * Dismisses Pip
-     * @param animate True if the dismissal should be animated.
-     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
-     *                          default animation duration should be used.
-     */
     @Override
-    public void dismissPip(boolean animate, int animationDuration) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                final PinnedActivityStack stack =
-                        mStackSupervisor.getDefaultDisplay().getPinnedStack();
-                if (stack == null) {
-                    Slog.w(TAG, "dismissPip: pinned stack not found.");
-                    return;
-                }
-                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
-                    throw new IllegalArgumentException("Stack: " + stack
-                            + " doesn't support animated resize.");
-                }
-                if (animate) {
-                    stack.animateResizePinnedStack(null /* sourceHintBounds */,
-                            null /* destBounds */, animationDuration, false /* fromFullscreen */);
-                } else {
-                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, true /* onTop */);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+    public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
+            int userId) {
+        return mActivityTaskManager.getRecentTasks(maxNum, flags, userId);
     }
 
     /**
@@ -11326,178 +7487,30 @@
      */
     @Override
     public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "moveTopActivityToPinnedStack()");
-        synchronized (this) {
-            if (!mSupportsPictureInPicture) {
-                throw new IllegalStateException("moveTopActivityToPinnedStack:"
-                        + "Device doesn't support picture-in-picture mode");
-            }
-
-            long ident = Binder.clearCallingIdentity();
-            try {
-                return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode,
-            boolean preserveWindows, boolean animate, int animationDuration) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                if (animate) {
-                    final PinnedActivityStack stack = mStackSupervisor.getStack(stackId);
-                    if (stack == null) {
-                        Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
-                        return;
-                    }
-                    if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
-                        throw new IllegalArgumentException("Stack: " + stackId
-                                + " doesn't support animated resize.");
-                    }
-                    stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
-                            animationDuration, false /* fromFullscreen */);
-                } else {
-                    final ActivityStack stack = mStackSupervisor.getStack(stackId);
-                    if (stack == null) {
-                        Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
-                        return;
-                    }
-                    mStackSupervisor.resizeStackLocked(stack, destBounds, null /* tempTaskBounds */,
-                            null /* tempTaskInsetBounds */, preserveWindows,
-                            allowResizeInDockedMode, !DEFER_RESUME);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+        return mActivityTaskManager.moveTopActivityToPinnedStack(stackId, bounds);
     }
 
     @Override
     public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
             Rect tempDockedTaskInsetBounds,
             Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()");
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                mStackSupervisor.resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds,
-                        tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds,
-                        PRESERVE_WINDOWS);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+        mActivityTaskManager.resizeDockedStack(dockedBounds, tempDockedTaskBounds,
+                tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds);
     }
 
     @Override
-    public void setSplitScreenResizing(boolean resizing) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setSplitScreenResizing()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                mStackSupervisor.setSplitScreenResizing(resizing);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                mStackSupervisor.resizePinnedStackLocked(pinnedBounds, tempPinnedTaskBounds);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    /**
-     * Try to place task to provided position. The final position might be different depending on
-     * current user and stacks state. The task will be moved to target stack if it's currently in
-     * different stack.
-     */
-    @Override
     public void positionTaskInStack(int taskId, int stackId, int position) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
-        synchronized (this) {
-            long ident = Binder.clearCallingIdentity();
-            try {
-                if (DEBUG_STACK) Slog.d(TAG_STACK, "positionTaskInStack: positioning task="
-                        + taskId + " in stackId=" + stackId + " at position=" + position);
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-                if (task == null) {
-                    throw new IllegalArgumentException("positionTaskInStack: no task for id="
-                            + taskId);
-                }
-
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
-
-                if (stack == null) {
-                    throw new IllegalArgumentException("positionTaskInStack: no stack for id="
-                            + stackId);
-                }
-                if (!stack.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException("positionTaskInStack: Attempt to change"
-                            + " the position of task " + taskId + " in/to non-standard stack");
-                }
-
-                // TODO: Have the callers of this API call a separate reparent method if that is
-                // what they intended to do vs. having this method also do reparenting.
-                if (task.getStack() == stack) {
-                    // Change position in current stack.
-                    stack.positionChildAt(task, position);
-                } else {
-                    // Reparent to new stack.
-                    task.reparent(stack, position, REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE,
-                            !DEFER_RESUME, "positionTaskInStack");
-                }
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
+        mActivityTaskManager.positionTaskInStack(taskId, stackId, position);
     }
 
     @Override
     public List<StackInfo> getAllStackInfos() {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                return mStackSupervisor.getAllStackInfosLocked();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public StackInfo getStackInfo(int windowingMode, int activityType) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                return mStackSupervisor.getStackInfo(windowingMode, activityType);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+        return mActivityTaskManager.getAllStackInfos();
     }
 
     @Override
     public int getTaskForActivity(IBinder token, boolean onlyRoot) {
-        synchronized(this) {
-            return ActivityRecord.getTaskForActivityLocked(token, onlyRoot);
-        }
+        return mActivityTaskManager.getTaskForActivity(token, onlyRoot);
     }
 
     @Override
@@ -11521,161 +7534,23 @@
         synchronized (this) {
             if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" +
                     Arrays.toString(packages));
-            mLockTaskController.updateLockTaskPackages(userId, packages);
-        }
-    }
-
-    @Override
-    public void updateLockTaskFeatures(int userId, int flags) {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != 0 && callingUid != SYSTEM_UID) {
-            enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
-                    "updateLockTaskFeatures()");
-        }
-        synchronized (this) {
-            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowing features " + userId + ":0x" +
-                    Integer.toHexString(flags));
-            mLockTaskController.updateLockTaskFeatures(userId, flags);
-        }
-    }
-
-    private void startLockTaskModeLocked(@Nullable TaskRecord task, boolean isSystemCaller) {
-        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "startLockTaskModeLocked: " + task);
-        if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
-            return;
-        }
-
-        final ActivityStack stack = mStackSupervisor.getFocusedStack();
-        if (stack == null || task != stack.topTask()) {
-            throw new IllegalArgumentException("Invalid task, not in foreground");
-        }
-
-        // {@code isSystemCaller} is used to distinguish whether this request is initiated by the
-        // system or a specific app.
-        // * System-initiated requests will only start the pinned mode (screen pinning)
-        // * App-initiated requests
-        //   - will put the device in fully locked mode (LockTask), if the app is whitelisted
-        //   - will start the pinned mode, otherwise
-        final int callingUid = Binder.getCallingUid();
-        long ident = Binder.clearCallingIdentity();
-        try {
-            // When a task is locked, dismiss the pinned stack if it exists
-            mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
-
-            mLockTaskController.startLockTaskMode(task, isSystemCaller, callingUid);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public void startLockTaskModeByToken(IBinder token) {
-        synchronized (this) {
-            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-            if (r == null) {
-                return;
-            }
-            startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */);
-        }
-    }
-
-    @Override
-    public void startSystemLockTaskMode(int taskId) throws RemoteException {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startSystemLockTaskMode");
-        // This makes inner call to look as if it was initiated by system.
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
-
-                // When starting lock task mode the stack must be in front and focused
-                task.getStack().moveToFront("startSystemLockTaskMode");
-                startLockTaskModeLocked(task, true /* isSystemCaller */);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public void stopLockTaskModeByToken(IBinder token) {
-        synchronized (this) {
-            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-            if (r == null) {
-                return;
-            }
-            stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */);
-        }
-    }
-
-    /**
-     * This API should be called by SystemUI only when user perform certain action to dismiss
-     * lock task mode. We should only dismiss pinned lock task mode in this case.
-     */
-    @Override
-    public void stopSystemLockTaskMode() throws RemoteException {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
-        stopLockTaskModeInternal(null, true /* isSystemCaller */);
-    }
-
-    private void stopLockTaskModeInternal(@Nullable TaskRecord task, boolean isSystemCaller) {
-        final int callingUid = Binder.getCallingUid();
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                mLockTaskController.stopLockTaskMode(task, isSystemCaller, callingUid);
-            }
-            // Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock
-            // task and jumping straight into a call in the case of emergency call back.
-            TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
-            if (tm != null) {
-                tm.showInCallScreen(false);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
+            mActivityTaskManager.getLockTaskController().updateLockTaskPackages(userId, packages);
         }
     }
 
     @Override
     public boolean isInLockTaskMode() {
-        return getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+        return mActivityTaskManager.isInLockTaskMode();
     }
 
     @Override
     public int getLockTaskModeState() {
-        synchronized (this) {
-            return mLockTaskController.getLockTaskModeState();
-        }
+        return mActivityTaskManager.getLockTaskModeState();
     }
 
     @Override
-    public void showLockTaskEscapeMessage(IBinder token) {
-        synchronized (this) {
-            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-            if (r == null) {
-                return;
-            }
-            mLockTaskController.showLockTaskToast();
-        }
-    }
-
-    @Override
-    public void setDisablePreviewScreenshots(IBinder token, boolean disable)
-            throws RemoteException {
-        synchronized (this) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                Slog.w(TAG, "setDisablePreviewScreenshots: Unable to find activity for token="
-                        + token);
-                return;
-            }
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                r.setDisablePreviewScreenshots(disable);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
+    public void startSystemLockTaskMode(int taskId) throws RemoteException {
+        mActivityTaskManager.startSystemLockTaskMode(taskId);
     }
 
     // =========================================================
@@ -11792,7 +7667,8 @@
             // Looking for cross-user grants before enforcing the typical cross-users permissions
             int tmpTargetUserId = mUserController.unsafeConvertIncomingUser(userId);
             if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
-                if (checkAuthorityGrants(callingUid, cpi, tmpTargetUserId, checkUser)) {
+                if (mUgmInternal.checkAuthorityGrants(
+                        callingUid, cpi, tmpTargetUserId, checkUser)) {
                     return null;
                 }
                 checkedGrants = true;
@@ -11838,7 +7714,8 @@
                 }
             }
         }
-        if (!checkedGrants && checkAuthorityGrants(callingUid, cpi, userId, checkUser)) {
+        if (!checkedGrants
+                && mUgmInternal.checkAuthorityGrants(callingUid, cpi, userId, checkUser)) {
             return null;
         }
 
@@ -11857,43 +7734,9 @@
         return msg;
     }
 
-    /**
-     * Returns if the ContentProvider has granted a uri to callingUid
-     */
-    boolean checkAuthorityGrants(int callingUid, ProviderInfo cpi, int userId, boolean checkUser) {
-        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
-        if (perms != null) {
-            for (int i=perms.size()-1; i>=0; i--) {
-                GrantUri grantUri = perms.keyAt(i);
-                if (grantUri.sourceUserId == userId || !checkUser) {
-                    if (matchesProvider(grantUri.uri, cpi)) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the uri authority is one of the authorities specified in the provider.
-     */
-    boolean matchesProvider(Uri uri, ProviderInfo cpi) {
-        String uriAuth = uri.getAuthority();
-        String cpiAuth = cpi.authority;
-        if (cpiAuth.indexOf(';') == -1) {
-            return cpiAuth.equals(uriAuth);
-        }
-        String[] cpiAuths = cpiAuth.split(";");
-        int length = cpiAuths.length;
-        for (int i = 0; i < length; i++) {
-            if (cpiAuths[i].equals(uriAuth)) return true;
-        }
-        return false;
-    }
-
     ContentProviderConnection incProviderCountLocked(ProcessRecord r,
-            final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
+            final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
+            String callingTag, boolean stable) {
         if (r != null) {
             for (int i=0; i<r.conProviders.size(); i++) {
                 ContentProviderConnection conn = r.conProviders.get(i);
@@ -11914,6 +7757,7 @@
                 }
             }
             ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
+            conn.startAssociationIfNeeded();
             if (stable) {
                 conn.stableCount = 1;
                 conn.numStableIncs = 1;
@@ -11924,10 +7768,10 @@
             cpr.connections.add(conn);
             r.conProviders.add(conn);
             startAssociationLocked(r.uid, r.processName, r.curProcState,
-                    cpr.uid, cpr.name, cpr.info.processName);
+                    cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
             return conn;
         }
-        cpr.addExternalProcessHandleLocked(externalProcessToken);
+        cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);
         return null;
     }
 
@@ -11946,9 +7790,10 @@
                 conn.unstableCount--;
             }
             if (conn.stableCount == 0 && conn.unstableCount == 0) {
+                conn.stopAssociation();
                 cpr.connections.remove(conn);
                 conn.client.conProviders.remove(conn);
-                if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+                if (conn.client.setProcState < PROCESS_STATE_LAST_ACTIVITY) {
                     // The client is more important than last activity -- note the time this
                     // is happening, so we keep the old provider process around a bit as last
                     // activity to avoid thrashing it.
@@ -11956,7 +7801,8 @@
                         cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
                     }
                 }
-                stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid, cpr.name);
+                stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
+                        cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
                 return true;
             }
             return false;
@@ -12002,7 +7848,8 @@
     }
 
     private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
-            String name, IBinder token, boolean stable, int userId) {
+            String name, IBinder token, int callingUid, String callingTag, boolean stable,
+            int userId) {
         ContentProviderRecord cpr;
         ContentProviderConnection conn = null;
         ProviderInfo cpi = null;
@@ -12095,7 +7942,7 @@
 
                 // In this case the provider instance already exists, so we can
                 // return it right away.
-                conn = incProviderCountLocked(r, cpr, token, stable);
+                conn = incProviderCountLocked(r, cpr, token, callingUid, 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,
@@ -12222,10 +8069,8 @@
                     // If permissions need a review before any of the app components can run,
                     // we return no provider and launch a review activity if the calling app
                     // is in the foreground.
-                    if (mPermissionReviewRequired) {
-                        if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
-                            return null;
-                        }
+                    if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
+                        return null;
                     }
 
                     try {
@@ -12339,7 +8184,7 @@
                 }
 
                 mProviderMap.putProviderByName(name, cpr);
-                conn = incProviderCountLocked(r, cpr, token, stable);
+                conn = incProviderCountLocked(r, cpr, token, callingUid, callingTag, stable);
                 if (conn != null) {
                     conn.waiting = true;
                 }
@@ -12431,18 +8276,6 @@
         return AppGlobals.getPackageManager();
     }
 
-    ActivityStartController getActivityStartController() {
-        return mActivityStartController;
-    }
-
-    LockTaskController getLockTaskController() {
-        return mLockTaskController;
-    }
-
-    ClientLifecycleManager getLifecycleManager() {
-        return mLifecycleManager;
-    }
-
     PackageManagerInternal getPackageManagerInternalLocked() {
         if (mPackageManagerInt == null) {
             mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
@@ -12462,21 +8295,23 @@
         }
         // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
         // with cross-user grant.
-        return getContentProviderImpl(caller, name, null, stable, userId);
+        return getContentProviderImpl(caller, name, null, Binder.getCallingUid(), null, stable,
+                userId);
     }
 
     public ContentProviderHolder getContentProviderExternal(
-            String name, int userId, IBinder token) {
+            String name, int userId, IBinder token, String tag) {
         enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
             "Do not have permission in call getContentProviderExternal()");
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, ALLOW_FULL_ONLY, "getContentProvider", null);
-        return getContentProviderExternalUnchecked(name, token, userId);
+        return getContentProviderExternalUnchecked(name, token, Binder.getCallingUid(),
+                tag != null ? tag : "*external*", userId);
     }
 
     private ContentProviderHolder getContentProviderExternalUnchecked(String name,
-            IBinder token, int userId) {
-        return getContentProviderImpl(null, name, token, true, userId);
+            IBinder token, int callingUid, String callingTag, int userId) {
+        return getContentProviderImpl(null, name, token, callingUid, callingTag, true, userId);
     }
 
     /**
@@ -12598,7 +8433,7 @@
                     }
                     synchronized (dst) {
                         dst.provider = src.provider;
-                        dst.proc = r;
+                        dst.setProcess(r);
                         dst.notifyAll();
                     }
                     updateOomAdjLocked(r, true);
@@ -12766,7 +8601,7 @@
 
         mConstants.start(mContext.getContentResolver());
         mCoreSettingsObserver = new CoreSettingsObserver(this);
-        mFontScaleSettingObserver = new FontScaleSettingObserver();
+        mActivityTaskManager.installSystemProviders();
         mDevelopmentSettingsObserver = new DevelopmentSettingsObserver();
         GlobalSettingsToPropertiesMapper.start(mContext.getContentResolver());
 
@@ -12868,7 +8703,8 @@
         }
         ContentProviderHolder holder = null;
         try {
-            holder = getContentProviderExternalUnchecked(name, null, userId);
+            holder = getContentProviderExternalUnchecked(name, null, callingUid,
+                    "*getmimetype*", userId);
             if (holder != null) {
                 return holder.provider.getType(uri);
             }
@@ -12916,7 +8752,6 @@
     final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
             boolean isolated, int isolatedUid) {
         String proc = customProcess != null ? customProcess : info.processName;
-        BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
         final int userId = UserHandle.getUserId(info.uid);
         int uid = info.uid;
         if (isolated) {
@@ -12955,14 +8790,14 @@
             StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
                     StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
         }
-        final ProcessRecord r = new ProcessRecord(this, stats, info, proc, uid);
+        final ProcessRecord r = new ProcessRecord(this, info, proc, uid);
         if (!mBooted && !mBooting
                 && userId == UserHandle.USER_SYSTEM
                 && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
             // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
-            r.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
             r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-            r.persistent = true;
+            r.setPersistent(true);
             r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
         }
         if (isolated && isolatedUid != 0) {
@@ -13034,6 +8869,7 @@
                 abiOverride);
     }
 
+    @GuardedBy("this")
     final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
             boolean disableHiddenApiChecks, String abiOverride) {
         ProcessRecord app;
@@ -13061,7 +8897,7 @@
         }
 
         if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
-            app.persistent = true;
+            app.setPersistent(true);
             app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
         }
         if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
@@ -13075,17 +8911,7 @@
     }
 
     public void unhandledBack() {
-        enforceCallingPermission(android.Manifest.permission.FORCE_BACK,
-                "unhandledBack()");
-
-        synchronized(this) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                getFocusedStack().unhandledBackLocked();
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
+        mActivityTaskManager.unhandledBack();
     }
 
     public ParcelFileDescriptor openContentUri(String uriString) throws RemoteException {
@@ -13093,7 +8919,8 @@
         final int userId = UserHandle.getCallingUserId();
         final Uri uri = Uri.parse(uriString);
         String name = uri.getAuthority();
-        ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null, userId);
+        ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null,
+                Binder.getCallingUid(), "*opencontent*", userId);
         ParcelFileDescriptor pfd = null;
         if (cph != null) {
             // We record the binder invoker's uid in thread-local storage before
@@ -13122,20 +8949,6 @@
         return pfd;
     }
 
-    // Actually is sleeping or shutting down or whatever else in the future
-    // is an inactive state.
-    boolean isSleepingOrShuttingDownLocked() {
-        return isSleepingLocked() || mShuttingDown;
-    }
-
-    boolean isShuttingDownLocked() {
-        return mShuttingDown;
-    }
-
-    boolean isSleepingLocked() {
-        return mSleeping;
-    }
-
     void reportGlobalUsageEventLocked(int event) {
         mUsageStatsService.reportEvent("android", mUserController.getCurrentUserId(), event);
         int[] profiles = mUserController.getCurrentProfileIds();
@@ -13152,12 +8965,6 @@
                 : UsageEvents.Event.SCREEN_NON_INTERACTIVE);
     }
 
-    void reportCurKeyguardUsageEventLocked() {
-        reportGlobalUsageEventLocked(mKeyguardShown
-                ? UsageEvents.Event.KEYGUARD_SHOWN
-                : UsageEvents.Event.KEYGUARD_HIDDEN);
-    }
-
     void onWakefulnessChanged(int wakefulness) {
         synchronized(this) {
             boolean wasAwake = mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
@@ -13168,81 +8975,13 @@
                 // Also update state in a special way for running foreground services UI.
                 mServices.updateScreenStateLocked(isAwake);
                 reportCurWakefulnessUsageEventLocked();
-                mHandler.obtainMessage(DISPATCH_SCREEN_AWAKE_MSG, isAwake ? 1 : 0, 0)
-                        .sendToTarget();
+                mActivityTaskManager.onScreenAwakeChanged(isAwake);
+                mOomAdjProfiler.onWakefulnessChanged(wakefulness);
             }
             updateOomAdjLocked();
         }
     }
 
-    @GuardedBy("this")
-    void finishRunningVoiceLocked() {
-        if (mRunningVoice != null) {
-            mRunningVoice = null;
-            mVoiceWakeLock.release();
-            updateSleepIfNeededLocked();
-        }
-    }
-
-    void startTimeTrackingFocusedActivityLocked() {
-        final ActivityRecord resumedActivity = mStackSupervisor.getResumedActivityLocked();
-        if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) {
-            mCurAppTimeTracker.start(resumedActivity.packageName);
-        }
-    }
-
-    @GuardedBy("this")
-    void updateSleepIfNeededLocked() {
-        final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
-        final boolean wasSleeping = mSleeping;
-
-        if (!shouldSleep) {
-            // If wasSleeping is true, we need to wake up activity manager state from when
-            // we started sleeping. In either case, we need to apply the sleep tokens, which
-            // will wake up stacks or put them to sleep as appropriate.
-            if (wasSleeping) {
-                mSleeping = false;
-                startTimeTrackingFocusedActivityLocked();
-                mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
-                mStackSupervisor.comeOutOfSleepIfNeededLocked();
-            }
-            mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
-            if (wasSleeping) {
-                updateOomAdjLocked();
-            }
-        } else if (!mSleeping && shouldSleep) {
-            mSleeping = true;
-            if (mCurAppTimeTracker != null) {
-                mCurAppTimeTracker.stop();
-            }
-            mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
-            mStackSupervisor.goingToSleepLocked();
-            updateResumedAppTrace(null /* resumed */);
-            updateOomAdjLocked();
-        }
-    }
-
-    /** Pokes the task persister. */
-    void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
-        mRecentTasks.notifyTaskPersisterLocked(task, flush);
-    }
-
-    /**
-     * Notifies all listeners when the pinned stack animation starts.
-     */
-    @Override
-    public void notifyPinnedStackAnimationStarted() {
-        mTaskChangeNotificationController.notifyPinnedStackAnimationStarted();
-    }
-
-    /**
-     * Notifies all listeners when the pinned stack animation ends.
-     */
-    @Override
-    public void notifyPinnedStackAnimationEnded() {
-        mTaskChangeNotificationController.notifyPinnedStackAnimationEnded();
-    }
-
     @Override
     public void notifyCleartextNetwork(int uid, byte[] firstPacket) {
         mHandler.obtainMessage(NOTIFY_CLEARTEXT_NETWORK_MSG, uid, 0, firstPacket).sendToTarget();
@@ -13256,14 +8995,7 @@
                     + android.Manifest.permission.SHUTDOWN);
         }
 
-        boolean timedout = false;
-
-        synchronized(this) {
-            mShuttingDown = true;
-            mStackSupervisor.prepareForShutdownLocked();
-            updateEventDispatchingLocked();
-            timedout = mStackSupervisor.shutdownLocked(timeout);
-        }
+        final boolean timedout = mAtmInternal.shuttingDown(mBooted, timeout);
 
         mAppOpsService.shutdown();
         if (mUsageStatsService != null) {
@@ -13272,70 +9004,17 @@
         mBatteryStatsService.shutdown();
         synchronized (this) {
             mProcessStats.shutdownLocked();
-            notifyTaskPersisterLocked(null, true);
+            mActivityTaskManager.notifyTaskPersisterLocked(null, true);
         }
 
         return timedout;
     }
 
-    public final void activitySlept(IBinder token) {
-        if (DEBUG_ALL) Slog.v(TAG, "Activity slept: token=" + token);
-
-        final long origId = Binder.clearCallingIdentity();
-
-        synchronized (this) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r != null) {
-                mStackSupervisor.activitySleptLocked(r);
-            }
-        }
-
-        Binder.restoreCallingIdentity(origId);
-    }
-
-    @GuardedBy("this")
-    void startRunningVoiceLocked(IVoiceInteractionSession session, int targetUid) {
-        Slog.d(TAG, "<<<  startRunningVoiceLocked()");
-        mVoiceWakeLock.setWorkSource(new WorkSource(targetUid));
-        if (mRunningVoice == null || mRunningVoice.asBinder() != session.asBinder()) {
-            boolean wasRunningVoice = mRunningVoice != null;
-            mRunningVoice = session;
-            if (!wasRunningVoice) {
-                mVoiceWakeLock.acquire();
-                updateSleepIfNeededLocked();
-            }
-        }
-    }
-
-    private void updateEventDispatchingLocked() {
-        mWindowManager.setEventDispatching(mBooted && !mShuttingDown);
-    }
-
     @Override
     public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing,
             int secondaryDisplayShowing) {
-        if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires permission "
-                    + android.Manifest.permission.DEVICE_POWER);
-        }
-
-        synchronized(this) {
-            long ident = Binder.clearCallingIdentity();
-            if (mKeyguardShown != keyguardShowing) {
-                mKeyguardShown = keyguardShowing;
-                reportCurKeyguardUsageEventLocked();
-            }
-            try {
-                mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing,
-                        secondaryDisplayShowing);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-
-        mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, keyguardShowing ? 1 : 0, 0)
-                .sendToTarget();
+        mActivityTaskManager.setLockScreenShown(
+                keyguardShowing, aodShowing, secondaryDisplayShowing);
     }
 
     @Override
@@ -13352,7 +9031,7 @@
             final long ident = Binder.clearCallingIdentity();
             try {
                 if (mUserController.shouldConfirmCredentials(userId)) {
-                    if (mKeyguardController.isKeyguardLocked()) {
+                    if (mActivityTaskManager.mKeyguardController.isKeyguardLocked()) {
                         // Showing launcher to avoid user entering credential twice.
                         final int currentUserId = mUserController.getCurrentUserId();
                         startHomeActivityLocked(currentUserId, "notifyLockedProfile");
@@ -13389,69 +9068,12 @@
 
     @Override
     public void stopAppSwitches() {
-        enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "stopAppSwitches");
-        synchronized(this) {
-            mAppSwitchesAllowedTime = SystemClock.uptimeMillis()
-                    + APP_SWITCH_DELAY_TIME;
-            mDidAppSwitch = false;
-            mActivityStartController.schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME);
-        }
+        mActivityTaskManager.stopAppSwitches();
     }
 
+    @Override
     public void resumeAppSwitches() {
-        enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
-        synchronized(this) {
-            // Note that we don't execute any pending app switches... we will
-            // let those wait until either the timeout, or the next start
-            // activity request.
-            mAppSwitchesAllowedTime = 0;
-        }
-    }
-
-    boolean checkAllowAppSwitchUid(int uid) {
-        ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(UserHandle.getUserId(uid));
-        if (types != null) {
-            for (int i = types.size() - 1; i >= 0; i--) {
-                if (types.valueAt(i).intValue() == uid) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
-            int callingPid, int callingUid, String name) {
-        if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
-            return true;
-        }
-
-        if (mRecentTasks.isCallerRecents(sourceUid)) {
-            return true;
-        }
-
-        int perm = checkComponentPermission(STOP_APP_SWITCHES, sourcePid, sourceUid, -1, true);
-        if (perm == PackageManager.PERMISSION_GRANTED) {
-            return true;
-        }
-        if (checkAllowAppSwitchUid(sourceUid)) {
-            return true;
-        }
-
-        // If the actual IPC caller is different from the logical source, then
-        // also see if they are allowed to control app switches.
-        if (callingUid != -1 && callingUid != sourceUid) {
-            perm = checkComponentPermission(STOP_APP_SWITCHES, callingPid, callingUid, -1, true);
-            if (perm == PackageManager.PERMISSION_GRANTED) {
-                return true;
-            }
-            if (checkAllowAppSwitchUid(callingUid)) {
-                return true;
-            }
-        }
-
-        Slog.w(TAG, name + " request from " + sourceUid + " stopped");
-        return false;
+        mActivityTaskManager.resumeAppSwitches();
     }
 
     public void setDebugApp(String packageName, boolean waitForDebugger,
@@ -13605,13 +9227,7 @@
 
     @Override
     public void setActivityController(IActivityController controller, boolean imAMonkey) {
-        enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
-                "setActivityController()");
-        synchronized (this) {
-            mController = controller;
-            mControllerIsAMonkey = imAMonkey;
-            Watchdog.getInstance().setActivityController(controller);
-        }
+        mActivityTaskManager.setActivityController(controller, imAMonkey);
     }
 
     @Override
@@ -13623,7 +9239,8 @@
                 if (proc == null) {
                     throw new SecurityException("Unknown process: " + callingPid);
                 }
-                if (proc.instr == null || proc.instr.mUiAutomationConnection == null) {
+                if (proc.getActiveInstrumentation() == null
+                        || proc.getActiveInstrumentation().mUiAutomationConnection == null) {
                     throw new SecurityException("Only an instrumentation process "
                             + "with a UiAutomation can call setUserIsMonkey");
                 }
@@ -13636,7 +9253,7 @@
     public boolean isUserAMonkey() {
         synchronized (this) {
             // If there is a controller also implies the user is a monkey.
-            return (mUserIsMonkey || (mController != null && mControllerIsAMonkey));
+            return mUserIsMonkey || mActivityTaskManager.isControllerAMonkey();
         }
     }
 
@@ -13746,346 +9363,6 @@
                 ActivityManager.BUGREPORT_OPTION_WIFI);
     }
 
-
-    public static long getInputDispatchingTimeoutLocked(ActivityRecord r) {
-        return r != null ? getInputDispatchingTimeoutLocked(r.app) : KEY_DISPATCHING_TIMEOUT;
-    }
-
-    public static long getInputDispatchingTimeoutLocked(ProcessRecord r) {
-        if (r != null && (r.instr != null || r.usingWrapper)) {
-            return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
-        }
-        return KEY_DISPATCHING_TIMEOUT;
-    }
-
-    @Override
-    public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
-        if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires permission "
-                    + android.Manifest.permission.FILTER_EVENTS);
-        }
-        ProcessRecord proc;
-        long timeout;
-        synchronized (this) {
-            synchronized (mPidsSelfLocked) {
-                proc = mPidsSelfLocked.get(pid);
-            }
-            timeout = getInputDispatchingTimeoutLocked(proc);
-        }
-
-        if (inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
-            return -1;
-        }
-
-        return timeout;
-    }
-
-    /**
-     * Handle input dispatching timeouts.
-     * Returns whether input dispatching should be aborted or not.
-     */
-    public boolean inputDispatchingTimedOut(final ProcessRecord proc,
-            final ActivityRecord activity, final ActivityRecord parent,
-            final boolean aboveSystem, String reason) {
-        if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires permission "
-                    + android.Manifest.permission.FILTER_EVENTS);
-        }
-
-        final String annotation;
-        if (reason == null) {
-            annotation = "Input dispatching timed out";
-        } else {
-            annotation = "Input dispatching timed out (" + reason + ")";
-        }
-
-        if (proc != null) {
-            synchronized (this) {
-                if (proc.debugging) {
-                    return false;
-                }
-
-                if (proc.instr != null) {
-                    Bundle info = new Bundle();
-                    info.putString("shortMsg", "keyDispatchingTimedOut");
-                    info.putString("longMsg", annotation);
-                    finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);
-                    return true;
-                }
-            }
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation);
-                }
-            });
-        }
-
-        return true;
-    }
-
-    @Override
-    public Bundle getAssistContextExtras(int requestType) {
-        PendingAssistExtras pae = enqueueAssistContext(requestType, null, null, null,
-                null, null, true /* focused */, true /* newSessionId */,
-                UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_TIMEOUT, 0);
-        if (pae == null) {
-            return null;
-        }
-        synchronized (pae) {
-            while (!pae.haveResult) {
-                try {
-                    pae.wait();
-                } catch (InterruptedException e) {
-                }
-            }
-        }
-        synchronized (this) {
-            buildAssistBundleLocked(pae, pae.result);
-            mPendingAssistExtras.remove(pae);
-            mUiHandler.removeCallbacks(pae);
-        }
-        return pae.extras;
-    }
-
-    @Override
-    public boolean isAssistDataAllowedOnCurrentActivity() {
-        int userId;
-        synchronized (this) {
-            final ActivityStack focusedStack = getFocusedStack();
-            if (focusedStack == null || focusedStack.isActivityTypeAssistant()) {
-                return false;
-            }
-
-            final ActivityRecord activity = focusedStack.getTopActivity();
-            if (activity == null) {
-                return false;
-            }
-            userId = activity.userId;
-        }
-        return !DevicePolicyCache.getInstance().getScreenCaptureDisabled(userId);
-    }
-
-    @Override
-    public boolean showAssistFromActivity(IBinder token, Bundle args) {
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                ActivityRecord caller = ActivityRecord.forTokenLocked(token);
-                ActivityRecord top = getFocusedStack().getTopActivity();
-                if (top != caller) {
-                    Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
-                            + " is not current top " + top);
-                    return false;
-                }
-                if (!top.nowVisible) {
-                    Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
-                            + " is not visible");
-                    return false;
-                }
-            }
-            return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION, null,
-                    token);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public boolean requestAssistContextExtras(int requestType, IAssistDataReceiver receiver,
-            Bundle receiverExtras, IBinder activityToken, boolean focused, boolean newSessionId) {
-        return enqueueAssistContext(requestType, null, null, receiver, receiverExtras,
-                activityToken, focused, newSessionId, UserHandle.getCallingUserId(), null,
-                PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0) != null;
-    }
-
-    @Override
-    public boolean requestAutofillData(IAssistDataReceiver receiver, Bundle receiverExtras,
-            IBinder activityToken, int flags) {
-        return enqueueAssistContext(ActivityManager.ASSIST_CONTEXT_AUTOFILL, null, null,
-                receiver, receiverExtras, activityToken, true, true, UserHandle.getCallingUserId(),
-                null, PENDING_AUTOFILL_ASSIST_STRUCTURE_TIMEOUT, flags) != null;
-    }
-
-    private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
-            IAssistDataReceiver receiver, Bundle receiverExtras, IBinder activityToken,
-            boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
-            int flags) {
-        enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
-                "enqueueAssistContext()");
-
-        synchronized (this) {
-            ActivityRecord activity = getFocusedStack().getTopActivity();
-            if (activity == null) {
-                Slog.w(TAG, "getAssistContextExtras failed: no top activity");
-                return null;
-            }
-            if (activity.app == null || activity.app.thread == null) {
-                Slog.w(TAG, "getAssistContextExtras failed: no process for " + activity);
-                return null;
-            }
-            if (focused) {
-                if (activityToken != null) {
-                    ActivityRecord caller = ActivityRecord.forTokenLocked(activityToken);
-                    if (activity != caller) {
-                        Slog.w(TAG, "enqueueAssistContext failed: caller " + caller
-                                + " is not current top " + activity);
-                        return null;
-                    }
-                }
-            } else {
-                activity = ActivityRecord.forTokenLocked(activityToken);
-                if (activity == null) {
-                    Slog.w(TAG, "enqueueAssistContext failed: activity for token=" + activityToken
-                            + " couldn't be found");
-                    return null;
-                }
-                if (activity.app == null || activity.app.thread == null) {
-                    Slog.w(TAG, "enqueueAssistContext failed: no process for " + activity);
-                    return null;
-                }
-            }
-
-            PendingAssistExtras pae;
-            Bundle extras = new Bundle();
-            if (args != null) {
-                extras.putAll(args);
-            }
-            extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName);
-            extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.uid);
-
-            pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, receiverExtras,
-                    userHandle);
-            pae.isHome = activity.isActivityTypeHome();
-
-            // Increment the sessionId if necessary
-            if (newSessionId) {
-                mViSessionId++;
-            }
-            try {
-                activity.app.thread.requestAssistContextExtras(activity.appToken, pae, requestType,
-                        mViSessionId, flags);
-                mPendingAssistExtras.add(pae);
-                mUiHandler.postDelayed(pae, timeout);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "getAssistContextExtras failed: crash calling " + activity);
-                return null;
-            }
-            return pae;
-        }
-    }
-
-    void pendingAssistExtrasTimedOut(PendingAssistExtras pae) {
-        IAssistDataReceiver receiver;
-        synchronized (this) {
-            mPendingAssistExtras.remove(pae);
-            receiver = pae.receiver;
-        }
-        if (receiver != null) {
-            // Caller wants result sent back to them.
-            Bundle sendBundle = new Bundle();
-            // At least return the receiver extras
-            sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
-            try {
-                pae.receiver.onHandleAssistData(sendBundle);
-            } catch (RemoteException e) {
-            }
-        }
-    }
-
-    private void buildAssistBundleLocked(PendingAssistExtras pae, Bundle result) {
-        if (result != null) {
-            pae.extras.putBundle(Intent.EXTRA_ASSIST_CONTEXT, result);
-        }
-        if (pae.hint != null) {
-            pae.extras.putBoolean(pae.hint, true);
-        }
-    }
-
-    /** Called from an app when assist data is ready. */
-    @Override
-    public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
-            AssistContent content, Uri referrer) {
-        PendingAssistExtras pae = (PendingAssistExtras)token;
-        synchronized (pae) {
-            pae.result = extras;
-            pae.structure = structure;
-            pae.content = content;
-            if (referrer != null) {
-                pae.extras.putParcelable(Intent.EXTRA_REFERRER, referrer);
-            }
-            if (structure != null) {
-                structure.setHomeActivity(pae.isHome);
-            }
-            pae.haveResult = true;
-            pae.notifyAll();
-            if (pae.intent == null && pae.receiver == null) {
-                // Caller is just waiting for the result.
-                return;
-            }
-        }
-        // We are now ready to launch the assist activity.
-        IAssistDataReceiver sendReceiver = null;
-        Bundle sendBundle = null;
-        synchronized (this) {
-            buildAssistBundleLocked(pae, extras);
-            boolean exists = mPendingAssistExtras.remove(pae);
-            mUiHandler.removeCallbacks(pae);
-            if (!exists) {
-                // Timed out.
-                return;
-            }
-
-            if ((sendReceiver=pae.receiver) != null) {
-                // Caller wants result sent back to them.
-                sendBundle = new Bundle();
-                sendBundle.putBundle(ASSIST_KEY_DATA, pae.extras);
-                sendBundle.putParcelable(ASSIST_KEY_STRUCTURE, pae.structure);
-                sendBundle.putParcelable(ASSIST_KEY_CONTENT, pae.content);
-                sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
-            }
-        }
-        if (sendReceiver != null) {
-            try {
-                sendReceiver.onHandleAssistData(sendBundle);
-            } catch (RemoteException e) {
-            }
-            return;
-        }
-
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            if (TextUtils.equals(pae.intent.getAction(),
-                    android.service.voice.VoiceInteractionService.SERVICE_INTERFACE)) {
-                pae.intent.putExtras(pae.extras);
-                mContext.startServiceAsUser(pae.intent, new UserHandle(pae.userHandle));
-            } else {
-                pae.intent.replaceExtras(pae.extras);
-                pae.intent.setFlags(FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_SINGLE_TOP
-                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-                closeSystemDialogs("assist");
-
-                try {
-                    mContext.startActivityAsUser(pae.intent, new UserHandle(pae.userHandle));
-                } catch (ActivityNotFoundException e) {
-                    Slog.w(TAG, "No activity to handle assist action.", e);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle,
-            Bundle args) {
-        return enqueueAssistContext(requestType, intent, hint, null, null, null,
-                true /* focused */, true /* newSessionId */, userHandle, args,
-                PENDING_ASSIST_EXTRAS_TIMEOUT, 0) != null;
-    }
-
     public void registerProcessObserver(IProcessObserver observer) {
         enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
                 "registerProcessObserver()");
@@ -14151,130 +9428,8 @@
     }
 
     @Override
-    public boolean convertFromTranslucent(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r == null) {
-                    return false;
-                }
-                final boolean translucentChanged = r.changeWindowTranslucency(true);
-                if (translucentChanged) {
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                }
-                mWindowManager.setAppFullscreen(token, true);
-                return translucentChanged;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public boolean convertToTranslucent(IBinder token, Bundle options) {
-        SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r == null) {
-                    return false;
-                }
-                final TaskRecord task = r.getTask();
-                int index = task.mActivities.lastIndexOf(r);
-                if (index > 0) {
-                    ActivityRecord under = task.mActivities.get(index - 1);
-                    under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
-                }
-                final boolean translucentChanged = r.changeWindowTranslucency(false);
-                if (translucentChanged) {
-                    r.getStack().convertActivityToTranslucent(r);
-                }
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                mWindowManager.setAppFullscreen(token, false);
-                return translucentChanged;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public Bundle getActivityOptions(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-                if (r != null) {
-                    final ActivityOptions activityOptions = r.takeOptionsLocked();
-                    return activityOptions == null ? null : activityOptions.toBundle();
-                }
-                return null;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
-    public void setImmersive(IBinder token, boolean immersive) {
-        synchronized(this) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                throw new IllegalArgumentException();
-            }
-            r.immersive = immersive;
-
-            // update associated state if we're frontmost
-            if (r == mStackSupervisor.getResumedActivityLocked()) {
-                if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
-                applyUpdateLockStateLocked(r);
-            }
-        }
-    }
-
-    @Override
-    public boolean isImmersive(IBinder token) {
-        synchronized (this) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                throw new IllegalArgumentException();
-            }
-            return r.immersive;
-        }
-    }
-
-    @Override
-    public void setVrThread(int tid) {
-        enforceSystemHasVrFeature();
-        synchronized (this) {
-            synchronized (mPidsSelfLocked) {
-                final int pid = Binder.getCallingPid();
-                final ProcessRecord proc = mPidsSelfLocked.get(pid);
-                mVrController.setVrThreadLocked(tid, pid, proc);
-            }
-        }
-    }
-
-    @Override
     public void setPersistentVrThread(int tid) {
-        if (checkCallingPermission(permission.RESTRICTED_VR_ACCESS) != PERMISSION_GRANTED) {
-            final String msg = "Permission Denial: setPersistentVrThread() from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid()
-                    + " requires " + permission.RESTRICTED_VR_ACCESS;
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-        enforceSystemHasVrFeature();
-        synchronized (this) {
-            synchronized (mPidsSelfLocked) {
-                final int pid = Binder.getCallingPid();
-                final ProcessRecord proc = mPidsSelfLocked.get(pid);
-                mVrController.setPersistentVrThreadLocked(tid, pid, proc);
-            }
-        }
+        mActivityTaskManager.setPersistentVrThread(tid);
     }
 
     /**
@@ -14325,17 +9480,6 @@
         return false;
     }
 
-    /**
-     * Check that we have the features required for VR-related API calls, and throw an exception if
-     * not.
-     */
-    private void enforceSystemHasVrFeature() {
-        if (!mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
-            throw new UnsupportedOperationException("VR mode not supported on this device!");
-        }
-    }
-
     @Override
     public void setRenderThread(int tid) {
         synchronized (this) {
@@ -14358,7 +9502,7 @@
                         Slog.d("UI_FIFO", "Set RenderThread tid " + tid + " for pid " + pid);
                     }
                     // promote to FIFO now
-                    if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+                    if (proc.getCurrentSchedulingGroup() == ProcessList.SCHED_GROUP_TOP_APP) {
                         if (DEBUG_OOM_ADJ) Slog.d("UI_FIFO", "Promoting " + tid + "out of band");
                         if (mUseFifoUiScheduling) {
                             setThreadScheduler(proc.renderThreadTid,
@@ -14389,46 +9533,8 @@
     }
 
     @Override
-    public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
-        enforceSystemHasVrFeature();
-
-        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
-
-        ActivityRecord r;
-        synchronized (this) {
-            r = ActivityRecord.isInStackLocked(token);
-        }
-
-        if (r == null) {
-            throw new IllegalArgumentException();
-        }
-
-        int err;
-        if ((err = vrService.hasVrPackage(packageName, r.userId)) !=
-                VrManagerInternal.NO_ERROR) {
-            return err;
-        }
-
-        // Clear the binder calling uid since this path may call moveToTask().
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            synchronized(this) {
-                r.requestedVrComponent = (enabled) ? packageName : null;
-
-                // Update associated state if this activity is currently focused
-                if (r == mStackSupervisor.getResumedActivityLocked()) {
-                    applyUpdateVrModeLocked(r);
-                }
-                return 0;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-    }
-
-    @Override
     public boolean isVrModePackageEnabled(ComponentName packageName) {
-        enforceSystemHasVrFeature();
+        mActivityTaskManager.enforceSystemHasVrFeature();
 
         final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
 
@@ -14437,29 +9543,12 @@
     }
 
     public boolean isTopActivityImmersive() {
-        enforceNotIsolatedCaller("startActivity");
-        synchronized (this) {
-            ActivityRecord r = getFocusedStack().topRunningActivityLocked();
-            return (r != null) ? r.immersive : false;
-        }
-    }
-
-    /**
-     * @return whether the system should disable UI modes incompatible with VR mode.
-     */
-    boolean shouldDisableNonVrUiLocked() {
-        return mVrController.shouldDisableNonVrUiLocked();
+        return mActivityTaskManager.isTopActivityImmersive();
     }
 
     @Override
     public boolean isTopOfTask(IBinder token) {
-        synchronized (this) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                throw new IllegalArgumentException();
-            }
-            return r.getTask().getTopActivity() == r;
-        }
+        return mActivityTaskManager.isTopOfTask(token);
     }
 
     @Override
@@ -14890,7 +9979,7 @@
                     proc.notCachedSinceIdle = true;
                     proc.initialIdlePss = 0;
                     proc.nextPssTime = ProcessList.computeNextPssTime(proc.setProcState, null,
-                            mTestPssMode, isSleepingLocked(), now);
+                            mTestPssMode, mAtmInternal.isSleeping(), now);
                 }
             }
         }
@@ -14918,78 +10007,22 @@
 
     private void retrieveSettings() {
         final ContentResolver resolver = mContext.getContentResolver();
-        final boolean freeformWindowManagement =
-                mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
-                        || Settings.Global.getInt(
-                                resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+        mActivityTaskManager.retrieveSettings(resolver);
 
-        final boolean supportsMultiWindow = ActivityManager.supportsMultiWindow(mContext);
-        final boolean supportsPictureInPicture = supportsMultiWindow &&
-                mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
-        final boolean supportsSplitScreenMultiWindow =
-                ActivityManager.supportsSplitScreenMultiWindow(mContext);
-        final boolean supportsMultiDisplay = mContext.getPackageManager()
-                .hasSystemFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
         final String debugApp = Settings.Global.getString(resolver, DEBUG_APP);
         final boolean waitForDebugger = Settings.Global.getInt(resolver, WAIT_FOR_DEBUGGER, 0) != 0;
         final boolean alwaysFinishActivities =
                 Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
-        final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
-        final boolean forceResizable = Settings.Global.getInt(
-                resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
         final long waitForNetworkTimeoutMs = Settings.Global.getLong(resolver,
                 NETWORK_ACCESS_TIMEOUT_MS, NETWORK_ACCESS_TIMEOUT_DEFAULT_MS);
-        final boolean supportsLeanbackOnly =
-                mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK_ONLY);
         mHiddenApiBlacklist.registerObserver();
 
-        // Transfer any global setting for forcing RTL layout, into a System Property
-        SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
-
-        final Configuration configuration = new Configuration();
-        Settings.System.getConfiguration(resolver, configuration);
-        if (forceRtl) {
-            // This will take care of setting the correct layout direction flags
-            configuration.setLayoutDirection(configuration.locale);
-        }
-
         synchronized (this) {
             mDebugApp = mOrigDebugApp = debugApp;
             mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
             mAlwaysFinishActivities = alwaysFinishActivities;
-            mSupportsLeanbackOnly = supportsLeanbackOnly;
-            mForceResizableActivities = forceResizable;
-            final boolean multiWindowFormEnabled = freeformWindowManagement
-                    || supportsSplitScreenMultiWindow
-                    || supportsPictureInPicture
-                    || supportsMultiDisplay;
-            if ((supportsMultiWindow || forceResizable) && multiWindowFormEnabled) {
-                mSupportsMultiWindow = true;
-                mSupportsFreeformWindowManagement = freeformWindowManagement;
-                mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
-                mSupportsPictureInPicture = supportsPictureInPicture;
-                mSupportsMultiDisplay = supportsMultiDisplay;
-            } else {
-                mSupportsMultiWindow = false;
-                mSupportsFreeformWindowManagement = false;
-                mSupportsSplitScreenMultiWindow = false;
-                mSupportsPictureInPicture = false;
-                mSupportsMultiDisplay = false;
-            }
-            mWindowManager.setForceResizableTasks(mForceResizableActivities);
-            mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
-            // This happens before any activities are started, so we can change global configuration
-            // in-place.
-            updateConfigurationLocked(configuration, null, true);
-            final Configuration globalConfig = getGlobalConfiguration();
-            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Initial config: " + globalConfig);
-
             // Load resources only after the current configuration has been set.
             final Resources res = mContext.getResources();
-            mThumbnailWidth = res.getDimensionPixelSize(
-                    com.android.internal.R.dimen.thumbnail_width);
-            mThumbnailHeight = res.getDimensionPixelSize(
-                    com.android.internal.R.dimen.thumbnail_height);
             mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
                     com.android.internal.R.string.config_appsNotReportingCrashes));
             mUserController.mUserSwitchUiEnabled = !res.getBoolean(
@@ -14997,14 +10030,6 @@
             mUserController.mMaxRunningUsers = res.getInteger(
                     com.android.internal.R.integer.config_multiuserMaxRunningUsers);
 
-            if ((globalConfig.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
-                mFullscreenThumbnailScale = (float) res
-                    .getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
-                    (float) globalConfig.screenWidthDp;
-            } else {
-                mFullscreenThumbnailScale = res.getFraction(
-                    com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
-            }
             mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs;
         }
     }
@@ -15025,11 +10050,9 @@
                     PackageManager.FEATURE_CANT_SAVE_STATE);
             mLocalDeviceIdleController
                     = LocalServices.getService(DeviceIdleController.LocalService.class);
-            mAssistUtils = new AssistUtils(mContext);
-            mVrController.onSystemReady();
+            mActivityTaskManager.onSystemReady();
             // Make sure we have the current profile info, since it is needed for security checks.
             mUserController.onSystemReady();
-            mRecentTasks.onSystemReadyLocked();
             mAppOpsService.systemReady();
             mSystemReady = true;
         }
@@ -15110,9 +10133,7 @@
 
         retrieveSettings();
         final int currentUserId = mUserController.getCurrentUserId();
-        synchronized (this) {
-            readGrantedUriPermissionsLocked();
-        }
+        mUgmInternal.onSystemReady();
 
         final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
         if (pmi != null) {
@@ -15156,19 +10177,7 @@
             }
             startHomeActivityLocked(currentUserId, "systemReady");
 
-            try {
-                if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
-                    Slog.e(TAG, "UIDs on the system are inconsistent, you need to wipe your"
-                            + " data partition or your device will be unstable.");
-                    mUiHandler.obtainMessage(SHOW_UID_ERROR_UI_MSG).sendToTarget();
-                }
-            } catch (RemoteException e) {
-            }
-
-            if (!Build.isBuildConsistent()) {
-                Slog.e(TAG, "Build fingerprint is not consistent, warning user");
-                mUiHandler.obtainMessage(SHOW_FINGERPRINT_ERROR_UI_MSG).sendToTarget();
-            }
+            mAtmInternal.showSystemReadyErrorDialogsIfNeeded();
 
             long ident = Binder.clearCallingIdentity();
             try {
@@ -15198,7 +10207,7 @@
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
-            mStackSupervisor.resumeFocusedStackTopActivityLocked();
+            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
             mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
 
             BinderInternal.nSetBinderProxyCountWatermarks(6000,5500);
@@ -15209,6 +10218,7 @@
                         public void onLimitReached(int uid) {
                             Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
                                     + Process.myUid());
+                            BinderProxy.dumpProxyDebugInfo();
                             if (uid == Process.SYSTEM_UID) {
                                 Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
                             } else {
@@ -15296,6 +10306,15 @@
                         : StatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__UNKNOWN
         );
 
+        final int relaunchReason = r == null ? ActivityRecord.RELAUNCH_REASON_NONE
+                        : r.getWindowProcessController().computeRelaunchReason();
+        final String relaunchReasonString = ActivityRecord.relaunchReasonToString(relaunchReason);
+        if (crashInfo.crashTag == null) {
+            crashInfo.crashTag = relaunchReasonString;
+        } else {
+            crashInfo.crashTag = crashInfo.crashTag + " " + relaunchReasonString;
+        }
+
         addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
 
         mAppErrors.crashApplication(r, crashInfo);
@@ -15303,13 +10322,13 @@
 
     public void handleApplicationStrictModeViolation(
             IBinder app,
-            int violationMask,
+            int penaltyMask,
             StrictMode.ViolationInfo info) {
         // We're okay if the ProcessRecord is missing; it probably means that
         // we're reporting a violation from the system process itself.
         final ProcessRecord r = findAppProcess(app, "StrictMode");
 
-        if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) {
+        if ((penaltyMask & StrictMode.PENALTY_DROPBOX) != 0) {
             Integer stackFingerprint = info.hashCode();
             boolean logIt = true;
             synchronized (mAlreadyLoggedViolatedStacks) {
@@ -15333,7 +10352,7 @@
             }
         }
 
-        if ((violationMask & StrictMode.PENALTY_DIALOG) != 0) {
+        if ((penaltyMask & StrictMode.PENALTY_DIALOG) != 0) {
             AppErrorResult result = new AppErrorResult();
             synchronized (this) {
                 final long origId = Binder.clearCallingIdentity();
@@ -15343,7 +10362,6 @@
                 HashMap<String, Object> data = new HashMap<String, Object>();
                 data.put("result", result);
                 data.put("app", r);
-                data.put("violationMask", violationMask);
                 data.put("info", info);
                 msg.obj = data;
                 mUiHandler.sendMessage(msg);
@@ -15447,7 +10465,7 @@
 
         final boolean isFatal = Build.IS_ENG || Settings.Global
                 .getInt(mContext.getContentResolver(), Settings.Global.WTF_IS_FATAL, 0) != 0;
-        final boolean isSystem = (r == null) || r.persistent;
+        final boolean isSystem = (r == null) || r.isPersistent();
 
         if (isFatal && !isSystem) {
             mAppErrors.crashApplication(r, crashInfo);
@@ -15610,8 +10628,8 @@
         if (activity != null) {
             sb.append("Activity: ").append(activity.shortComponentName).append("\n");
         }
-        if (parent != null && parent.app != null && parent.app.pid != process.pid) {
-            sb.append("Parent-Process: ").append(parent.app.processName).append("\n");
+        if (parent != null && parent.app != null && parent.app.getPid() != process.pid) {
+            sb.append("Parent-Process: ").append(parent.app.mName).append("\n");
         }
         if (parent != null && parent != activity) {
             sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n");
@@ -15623,6 +10641,9 @@
         if (Debug.isDebuggerConnected()) {
             sb.append("Debugger: Connected\n");
         }
+        if (crashInfo != null && crashInfo.crashTag != null && !crashInfo.crashTag.isEmpty()) {
+            sb.append("Crash-Tag: ").append(crashInfo.crashTag).append("\n");
+        }
         sb.append("\n");
 
         // Do the rest in a worker thread to avoid blocking the caller on I/O
@@ -15713,25 +10734,27 @@
                 if (!allUsers && app.userId != userId) {
                     continue;
                 }
-                if ((app.thread != null) && (app.crashing || app.notResponding)) {
+                final boolean crashing = app.isCrashing();
+                final boolean notResponding = app.isNotResponding();
+                if ((app.thread != null) && (crashing || notResponding)) {
                     // This one's in trouble, so we'll generate a report for it
                     // crashes are higher priority (in case there's a crash *and* an anr)
                     ActivityManager.ProcessErrorStateInfo report = null;
-                    if (app.crashing) {
+                    if (crashing) {
                         report = app.crashingReport;
-                    } else if (app.notResponding) {
+                    } else if (notResponding) {
                         report = app.notRespondingReport;
                     }
 
                     if (report != null) {
                         if (errList == null) {
-                            errList = new ArrayList<ActivityManager.ProcessErrorStateInfo>(1);
+                            errList = new ArrayList<>(1);
                         }
                         errList.add(report);
                     } else {
                         Slog.w(TAG, "Missing app error report, app = " + app.processName +
-                                " crashing = " + app.crashing +
-                                " notResponding = " + app.notResponding);
+                                " crashing = " + crashing +
+                                " notResponding = " + notResponding);
                     }
                 }
             }
@@ -15753,7 +10776,8 @@
         return imp;
     }
 
-    private void fillInProcMemInfo(ProcessRecord app,
+    @GuardedBy("this")
+    private void fillInProcMemInfoLocked(ProcessRecord app,
             ActivityManager.RunningAppProcessInfo outInfo,
             int clientTargetSdk) {
         outInfo.pid = app.pid;
@@ -15761,10 +10785,10 @@
         if (mHeavyWeightProcess == app) {
             outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
         }
-        if (app.persistent) {
+        if (app.isPersistent()) {
             outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
         }
-        if (app.activities.size() > 0) {
+        if (app.hasActivities()) {
             outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
         }
         outInfo.lastTrimLevel = app.trimMemoryLevel;
@@ -15773,6 +10797,8 @@
         outInfo.importance = procStateToImportance(procState, adj, outInfo, clientTargetSdk);
         outInfo.importanceReasonCode = app.adjTypeCode;
         outInfo.processState = app.curProcState;
+        outInfo.isFocused = (app == getTopAppLocked());
+        outInfo.lastActivityTime = app.lastActivityTime;
     }
 
     @Override
@@ -15787,7 +10813,7 @@
         final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
                 callingUid) == PackageManager.PERMISSION_GRANTED;
         final int userId = UserHandle.getUserId(callingUid);
-        final boolean allUids = isGetTasksAllowed(
+        final boolean allUids = mAtmInternal.isGetTasksAllowed(
                 "getRunningAppProcesses", Binder.getCallingPid(), callingUid);
 
         synchronized (this) {
@@ -15798,12 +10824,12 @@
                         || (!allUids && app.uid != callingUid)) {
                     continue;
                 }
-                if ((app.thread != null) && (!app.crashing && !app.notResponding)) {
+                if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) {
                     // Generate process state info for running application
                     ActivityManager.RunningAppProcessInfo currApp =
                         new ActivityManager.RunningAppProcessInfo(app.processName,
                                 app.pid, app.getPackageList());
-                    fillInProcMemInfo(app, currApp, clientTargetSdk);
+                    fillInProcMemInfoLocked(app, currApp, clientTargetSdk);
                     if (app.adjSource instanceof ProcessRecord) {
                         currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
                         currApp.importanceReasonImportance =
@@ -15811,7 +10837,7 @@
                                         app.adjSourceProcState);
                     } else if (app.adjSource instanceof ActivityRecord) {
                         ActivityRecord r = (ActivityRecord)app.adjSource;
-                        if (r.app != null) currApp.importanceReasonPid = r.app.pid;
+                        if (r.app != null) currApp.importanceReasonPid = r.app.getPid();
                     }
                     if (app.adjTarget instanceof ComponentName) {
                         currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
@@ -15872,7 +10898,7 @@
                 proc = mPidsSelfLocked.get(Binder.getCallingPid());
             }
             if (proc != null) {
-                fillInProcMemInfo(proc, outState, clientTargetSdk);
+                fillInProcMemInfoLocked(proc, outState, clientTargetSdk);
             }
         }
     }
@@ -15893,19 +10919,122 @@
                 this, in, out, err, args, callback, resultReceiver);
     }
 
-    SleepToken acquireSleepToken(String tag, int displayId) {
-        synchronized (this) {
-            final SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId);
-            updateSleepIfNeededLocked();
-            return token;
-        }
-    }
-
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         PriorityDump.dump(mPriorityDumper, fd, pw, args);
     }
 
+    private void dumpEverything(FileDescriptor fd, PrintWriter pw, String[] args, int opti,
+            boolean dumpAll, String dumpPackage, boolean dumpClient, boolean dumpNormalPriority,
+            int dumpAppId) {
+
+        ActiveServices.ServiceDumper sdumper;
+
+        synchronized(this) {
+            mConstants.dump(pw);
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+
+            }
+            dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            if (dumpAll || dumpPackage != null) {
+                dumpBroadcastStatsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+            }
+            dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            dumpPermissionsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+            pw.println();
+            sdumper = mServices.newServiceDumperLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+            if (!dumpClient) {
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                sdumper.dumpLocked();
+            }
+        }
+        // We drop the lock here because we can't call dumpWithClient() with the lock held;
+        // if the caller wants a consistent state for the !dumpClient case, it can call this
+        // method with the lock held.
+        if (dumpClient) {
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            sdumper.dumpWithClient();
+        }
+        synchronized(this) {
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            if (mActivityTaskManager.getRecentTasks() != null) {
+                mActivityTaskManager.getRecentTasks().dump(pw, dumpAll, dumpPackage);
+            }
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            dumpLastANRLocked(pw);
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            dumpActivityStarterLocked(pw, dumpPackage);
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            dumpActivityContainersLocked(pw);
+            // Activities section is dumped as part of the Critical priority dump. Exclude the
+            // section if priority is Normal.
+            if (!dumpNormalPriority) {
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+            }
+            if (mAssociations.size() > 0) {
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+            }
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            mOomAdjProfiler.dump(pw);
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            dumpBinderProxies(pw);
+        }
+    }
+
     /**
      * Wrapper function to print out debug data filtered by specified arguments.
     */
@@ -16044,6 +11173,10 @@
                 synchronized (this) {
                     dumpLastANRLocked(pw);
                 }
+            } else if ("lastanr-traces".equals(cmd)) {
+                synchronized (this) {
+                    dumpLastANRTracesLocked(pw);
+                }
             } else if ("starter".equals(cmd)) {
                 synchronized (this) {
                     dumpActivityStarterLocked(pw, dumpPackage);
@@ -16054,14 +11187,13 @@
                 }
             } else if ("recents".equals(cmd) || "r".equals(cmd)) {
                 synchronized (this) {
-                    if (mRecentTasks != null) {
-                        mRecentTasks.dump(pw, true /* dumpAll */, dumpPackage);
+                    if (mActivityTaskManager.getRecentTasks() != null) {
+                        mActivityTaskManager.getRecentTasks().dump(pw, true /* dumpAll */, dumpPackage);
                     }
                 }
             } else if ("binder-proxies".equals(cmd)) {
                 if (opti >= args.length) {
-                    dumpBinderProxiesCounts(pw, BinderInternal.nGetBinderProxyPerUidCounts(),
-                            "Counts of Binder Proxies held by SYSTEM");
+                    dumpBinderProxies(pw);
                 } else {
                     String uid = args[opti];
                     opti++;
@@ -16214,166 +11346,19 @@
         // No piece of data specified, dump everything.
         if (dumpCheckinFormat) {
             dumpBroadcastStatsCheckinLocked(fd, pw, args, opti, dumpCheckin, dumpPackage);
-        } else if (dumpClient) {
-            ActiveServices.ServiceDumper sdumper;
-            synchronized (this) {
-                mConstants.dump(pw);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                if (dumpAll || dumpPackage != null) {
-                    dumpBroadcastStatsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-                    pw.println();
-                    if (dumpAll) {
-                        pw.println("-------------------------------------------------------------------------------");
-                    }
-                }
-                dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpPermissionsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                sdumper = mServices.newServiceDumperLocked(fd, pw, args, opti, dumpAll,
-                        dumpPackage);
-            }
-            sdumper.dumpWithClient();
-            pw.println();
-            synchronized (this) {
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                if (mRecentTasks != null) {
-                    mRecentTasks.dump(pw, dumpAll, dumpPackage);
-                }
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpLastANRLocked(pw);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpActivityStarterLocked(pw, dumpPackage);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpActivityContainersLocked(pw);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
-                if (mAssociations.size() > 0) {
-                    pw.println();
-                    if (dumpAll) {
-                        pw.println("-------------------------------------------------------------------------------");
-                    }
-                    dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
-                }
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
-            }
-
         } else {
-            synchronized (this) {
-                mConstants.dump(pw);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
+            if (dumpClient) {
+                // dumpEverything() will take the lock when needed, and momentarily drop
+                // it for dumping client state.
+                dumpEverything(fd, pw, args, opti, dumpAll, dumpPackage, dumpClient,
+                        dumpNormalPriority, dumpAppId);
+            } else {
+                // Take the lock here, so we get a consistent state for the entire dump;
+                // dumpEverything() will take the lock as well, but that is fine.
+                synchronized(this) {
+                    dumpEverything(fd, pw, args, opti, dumpAll, dumpPackage, dumpClient,
+                            dumpNormalPriority, dumpAppId);
                 }
-                dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                if (dumpAll || dumpPackage != null) {
-                    dumpBroadcastStatsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-                    pw.println();
-                    if (dumpAll) {
-                        pw.println("-------------------------------------------------------------------------------");
-                    }
-                }
-                dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpPermissionsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                mServices.newServiceDumperLocked(fd, pw, args, opti, dumpAll, dumpPackage)
-                        .dumpLocked();
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                if (mRecentTasks != null) {
-                    mRecentTasks.dump(pw, dumpAll, dumpPackage);
-                }
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpLastANRLocked(pw);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpActivityStarterLocked(pw, dumpPackage);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpActivityContainersLocked(pw);
-                // Activities section is dumped as part of the Critical priority dump. Exclude the
-                // section if priority is Normal.
-                if (!dumpNormalPriority){
-                    pw.println();
-                    if (dumpAll) {
-                        pw.println("-------------------------------------------------------------------------------");
-                    }
-                    dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
-                }
-                if (mAssociations.size() > 0) {
-                    pw.println();
-                    if (dumpAll) {
-                        pw.println("-------------------------------------------------------------------------------");
-                    }
-                    dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
-                }
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
-                dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
             }
         }
         Binder.restoreCallingIdentity(origId);
@@ -16393,6 +11378,36 @@
         }
     }
 
+    private void dumpLastANRTracesLocked(PrintWriter pw) {
+        pw.println("ACTIVITY MANAGER LAST ANR TRACES (dumpsys activity lastanr-traces)");
+
+        final File[] files = new File(ANR_TRACE_DIR).listFiles();
+        if (ArrayUtils.isEmpty(files)) {
+            pw.println("  <no ANR has occurred since boot>");
+            return;
+        }
+        // Find the latest file.
+        File latest = null;
+        for (File f : files) {
+            if ((latest == null) || (latest.lastModified() < f.lastModified())) {
+                latest = f;
+            }
+        }
+        pw.print("File: ");
+        pw.print(latest.getName());
+        pw.println();
+        try (BufferedReader in = new BufferedReader(new FileReader(latest))) {
+            String line;
+            while ((line = in.readLine()) != null) {
+                pw.println(line);
+            }
+        } catch (IOException e) {
+            pw.print("Unable to read: ");
+            pw.print(e);
+            pw.println();
+        }
+    }
+
     private void dumpActivityContainersLocked(PrintWriter pw) {
         pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
         mStackSupervisor.dumpChildrenNames(pw, " ");
@@ -16401,7 +11416,7 @@
 
     private void dumpActivityStarterLocked(PrintWriter pw, String dumpPackage) {
         pw.println("ACTIVITY MANAGER STARTER (dumpsys activity starter)");
-        mActivityStartController.dump(pw, "", dumpPackage);
+        mActivityTaskManager.getActivityStartController().dump(pw, "", dumpPackage);
     }
 
     void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
@@ -16419,8 +11434,8 @@
         boolean needSep = printedAnything;
 
         boolean printed = ActivityStackSupervisor.printThisActivity(pw,
-                mStackSupervisor.getResumedActivityLocked(),
-                dumpPackage, needSep, "  ResumedActivity: ");
+                mStackSupervisor.getTopResumedActivity(),  dumpPackage, needSep,
+                "  ResumedActivity: ");
         if (printed) {
             printedAnything = true;
             needSep = false;
@@ -16564,7 +11579,17 @@
         return printed;
     }
 
-    boolean dumpBinderProxiesCounts(PrintWriter pw, SparseIntArray counts, String header) {
+    void dumpBinderProxyInterfaceCounts(PrintWriter pw, String header) {
+        final BinderProxy.InterfaceCount[] proxyCounts = BinderProxy.getSortedInterfaceCounts(50);
+
+        pw.println(header);
+        for (int i = 0; i < proxyCounts.length; i++) {
+            pw.println("    #" + (i + 1) + ": " + proxyCounts[i]);
+        }
+    }
+
+    boolean dumpBinderProxiesCounts(PrintWriter pw, String header) {
+        SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
         if(counts != null) {
             pw.println(header);
             for (int i = 0; i < counts.size(); i++) {
@@ -16592,6 +11617,13 @@
         return false;
     }
 
+    void dumpBinderProxies(PrintWriter pw) {
+        dumpBinderProxyInterfaceCounts(pw,
+                "Top proxy interface names held by SYSTEM");
+        dumpBinderProxiesCounts(pw,
+                "Counts of Binder Proxies held by SYSTEM");
+    }
+
     @GuardedBy("this")
     void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage, int dumpAppId) {
@@ -16614,11 +11646,11 @@
                         pw.println("  All known processes:");
                         needSep = true;
                     }
-                    pw.print(r.persistent ? "  *PERS*" : "  *APP*");
+                    pw.print(r.isPersistent() ? "  *PERS*" : "  *APP*");
                         pw.print(" UID "); pw.print(procs.keyAt(ia));
                         pw.print(" "); pw.println(r);
                     r.dump(pw, "    ");
-                    if (r.persistent) {
+                    if (r.isPersistent()) {
                         numPers++;
                     }
                 }
@@ -16770,27 +11802,27 @@
             needSep = false;
             mUserController.dump(pw, dumpAll);
         }
-        if (mHomeProcess != null && (dumpPackage == null
-                || mHomeProcess.pkgList.containsKey(dumpPackage))) {
+        if (mActivityTaskManager.mHomeProcess != null && (dumpPackage == null
+                || mActivityTaskManager.mHomeProcess.mPkgList.contains(dumpPackage))) {
             if (needSep) {
                 pw.println();
                 needSep = false;
             }
-            pw.println("  mHomeProcess: " + mHomeProcess);
+            pw.println("  mHomeProcess: " + mActivityTaskManager.mHomeProcess);
         }
-        if (mPreviousProcess != null && (dumpPackage == null
-                || mPreviousProcess.pkgList.containsKey(dumpPackage))) {
+        if (mActivityTaskManager.mPreviousProcess != null && (dumpPackage == null
+                || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
             if (needSep) {
                 pw.println();
                 needSep = false;
             }
-            pw.println("  mPreviousProcess: " + mPreviousProcess);
+            pw.println("  mPreviousProcess: " + mActivityTaskManager.mPreviousProcess);
         }
-        if (dumpAll && (mPreviousProcess == null || dumpPackage == null
-                || mPreviousProcess.pkgList.containsKey(dumpPackage))) {
+        if (dumpAll && (mActivityTaskManager.mPreviousProcess == null || dumpPackage == null
+                || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
             StringBuilder sb = new StringBuilder(128);
             sb.append("  mPreviousProcessVisibleTime: ");
-            TimeUtils.formatDuration(mPreviousProcessVisibleTime, sb);
+            TimeUtils.formatDuration(mActivityTaskManager.mPreviousProcessVisibleTime, sb);
             pw.println(sb);
         }
         if (mHeavyWeightProcess != null && (dumpPackage == null
@@ -16815,7 +11847,8 @@
         }
         if (dumpAll) {
             if (dumpPackage == null) {
-                pw.println("  mConfigWillChange: " + getFocusedStack().mConfigWillChange);
+                pw.println("  mConfigWillChange: "
+                        + mActivityTaskManager.getTopDisplayFocusedStack().mConfigWillChange);
             }
             if (mCompatModePackages.getPackages().size() > 0) {
                 boolean printed = false;
@@ -16891,14 +11924,7 @@
         if (dumpPackage == null) {
             pw.println("  mWakefulness="
                     + PowerManagerInternal.wakefulnessToString(mWakefulness));
-            pw.println("  mSleepTokens=" + mStackSupervisor.mSleepTokens);
-            pw.println("  mSleeping=" + mSleeping);
-            pw.println("  mShuttingDown=" + mShuttingDown + " mTestPssMode=" + mTestPssMode);
-            if (mRunningVoice != null) {
-                pw.println("  mRunningVoice=" + mRunningVoice);
-                pw.println("  mVoiceWakeLock" + mVoiceWakeLock);
-            }
-            pw.println("  mVrController=" + mVrController);
+            mActivityTaskManager.dumpSleepStates(pw, mTestPssMode);
         }
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
                 || mOrigWaitForDebugger) {
@@ -16913,8 +11939,8 @@
                         + " mOrigWaitForDebugger=" + mOrigWaitForDebugger);
             }
         }
-        if (mCurAppTimeTracker != null) {
-            mCurAppTimeTracker.dumpWithHeader(pw, "  ", true);
+        if (mActivityTaskManager.mCurAppTimeTracker != null) {
+            mActivityTaskManager.mCurAppTimeTracker.dumpWithHeader(pw, "  ", true);
         }
         if (mMemWatchProcesses.getMap().size() > 0) {
             pw.println("  Mem watch processes:");
@@ -16980,10 +12006,10 @@
                 pw.println("  mNativeDebuggingApp=" + mNativeDebuggingApp);
             }
         }
-        if (mAllowAppSwitchUids.size() > 0) {
+        if (mActivityTaskManager.mAllowAppSwitchUids.size() > 0) {
             boolean printed = false;
-            for (int i = 0; i < mAllowAppSwitchUids.size(); i++) {
-                ArrayMap<String, Integer> types = mAllowAppSwitchUids.valueAt(i);
+            for (int i = 0; i < mActivityTaskManager.mAllowAppSwitchUids.size(); i++) {
+                ArrayMap<String, Integer> types = mActivityTaskManager.mAllowAppSwitchUids.valueAt(i);
                 for (int j = 0; j < types.size(); j++) {
                     if (dumpPackage == null ||
                             UserHandle.getAppId(types.valueAt(j).intValue()) == dumpAppId) {
@@ -16996,7 +12022,7 @@
                             printed = true;
                         }
                         pw.print("    User ");
-                        pw.print(mAllowAppSwitchUids.keyAt(i));
+                        pw.print(mActivityTaskManager.mAllowAppSwitchUids.keyAt(i));
                         pw.print(": Type ");
                         pw.print(types.keyAt(j));
                         pw.print(" = ");
@@ -17010,9 +12036,9 @@
             if (mAlwaysFinishActivities) {
                 pw.println("  mAlwaysFinishActivities=" + mAlwaysFinishActivities);
             }
-            if (mController != null) {
-                pw.println("  mController=" + mController
-                        + " mControllerIsAMonkey=" + mControllerIsAMonkey);
+            if (mActivityTaskManager.mController != null) {
+                pw.println("  mController=" + mActivityTaskManager.mController
+                        + " mControllerIsAMonkey=" + mActivityTaskManager.mControllerIsAMonkey);
             }
             if (dumpAll) {
                 pw.println("  Total persistent processes: " + numPers);
@@ -17086,7 +12112,7 @@
                     continue;
                 }
                 r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS);
-                if (r.persistent) {
+                if (r.isPersistent()) {
                     numPers++;
                 }
             }
@@ -17193,18 +12219,18 @@
         if (dumpPackage == null) {
             mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
             getGlobalConfiguration().writeToProto(proto, ActivityManagerServiceDumpProcessesProto.GLOBAL_CONFIGURATION);
-            proto.write(ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE, getFocusedStack().mConfigWillChange);
+            proto.write(ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE, mActivityTaskManager.getTopDisplayFocusedStack().mConfigWillChange);
         }
 
-        if (mHomeProcess != null && (dumpPackage == null
-                || mHomeProcess.pkgList.containsKey(dumpPackage))) {
-            mHomeProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HOME_PROC);
+        if (mActivityTaskManager.mHomeProcess != null && (dumpPackage == null
+                || mActivityTaskManager.mHomeProcess.mPkgList.contains(dumpPackage))) {
+            ((ProcessRecord) mActivityTaskManager.mHomeProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HOME_PROC);
         }
 
-        if (mPreviousProcess != null && (dumpPackage == null
-                || mPreviousProcess.pkgList.containsKey(dumpPackage))) {
-            mPreviousProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC);
-            proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
+        if (mActivityTaskManager.mPreviousProcess != null && (dumpPackage == null
+                || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
+            ((ProcessRecord) mActivityTaskManager.mPreviousProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC);
+            proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mActivityTaskManager.mPreviousProcessVisibleTime);
         }
 
         if (mHeavyWeightProcess != null && (dumpPackage == null
@@ -17251,22 +12277,10 @@
             final long sleepToken = proto.start(ActivityManagerServiceDumpProcessesProto.SLEEP_STATUS);
             proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.WAKEFULNESS,
                     PowerManagerInternal.wakefulnessToProtoEnum(mWakefulness));
-            for (SleepToken st : mStackSupervisor.mSleepTokens) {
-                proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS, st.toString());
-            }
-            proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEPING, mSleeping);
-            proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SHUTTING_DOWN, mShuttingDown);
             proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.TEST_PSS_MODE, mTestPssMode);
             proto.end(sleepToken);
 
-            if (mRunningVoice != null) {
-                final long vrToken = proto.start(ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
-                proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION, mRunningVoice.toString());
-                mVoiceWakeLock.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK);
-                proto.end(vrToken);
-            }
-
-            mVrController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
+            mActivityTaskManager.writeSleepStateToProto(proto);
         }
 
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
@@ -17282,8 +12296,9 @@
             }
         }
 
-        if (mCurAppTimeTracker != null) {
-            mCurAppTimeTracker.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER, true);
+        if (mActivityTaskManager.mCurAppTimeTracker != null) {
+            mActivityTaskManager.mCurAppTimeTracker.writeToProto(
+                    proto, ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER, true);
         }
 
         if (mMemWatchProcesses.getMap().size() > 0) {
@@ -17342,10 +12357,10 @@
 
         if (dumpPackage == null) {
             proto.write(ActivityManagerServiceDumpProcessesProto.ALWAYS_FINISH_ACTIVITIES, mAlwaysFinishActivities);
-            if (mController != null) {
+            if (mActivityTaskManager.mController != null) {
                 final long token = proto.start(ActivityManagerServiceDumpProcessesProto.CONTROLLER);
-                proto.write(ActivityManagerServiceDumpProcessesProto.Controller.CONTROLLER, mController.toString());
-                proto.write(ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY, mControllerIsAMonkey);
+                proto.write(ActivityManagerServiceDumpProcessesProto.Controller.CONTROLLER, mActivityTaskManager.mController.toString());
+                proto.write(ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY, mActivityTaskManager.mControllerIsAMonkey);
                 proto.end(token);
             }
             proto.write(ActivityManagerServiceDumpProcessesProto.TOTAL_PERSISTENT_PROCS, numPers);
@@ -17474,8 +12489,8 @@
         dumpProcessesToGc(pw, needSep, null);
 
         pw.println();
-        pw.println("  mHomeProcess: " + mHomeProcess);
-        pw.println("  mPreviousProcess: " + mPreviousProcess);
+        pw.println("  mHomeProcess: " + mActivityTaskManager.mHomeProcess);
+        pw.println("  mPreviousProcess: " + mActivityTaskManager.mPreviousProcess);
         if (mHeavyWeightProcess != null) {
             pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
         }
@@ -17647,20 +12662,20 @@
             pw.print(prefix); pw.print("ACTIVITY "); pw.print(r.shortComponentName);
                     pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(r)));
                     pw.print(" pid=");
-                    if (r.app != null) pw.println(r.app.pid);
+                    if (r.hasProcess()) pw.println(r.app.getPid());
                     else pw.println("(not running)");
             if (dumpAll) {
                 r.dump(pw, innerPrefix);
             }
         }
-        if (r.app != null && r.app.thread != null) {
+        if (r.attachedToProcess()) {
             // flush anything that is already in the PrintWriter since the thread is going
             // to write to the file descriptor directly
             pw.flush();
             try {
                 TransferPipe tp = new TransferPipe();
                 try {
-                    r.app.thread.dumpActivity(tp.getWriteFd(),
+                    r.app.getThread().dumpActivity(tp.getWriteFd(),
                             r.appToken, innerPrefix, args);
                     tp.go(fd);
                 } finally {
@@ -17904,48 +12919,10 @@
     @GuardedBy("this")
     void dumpPermissionsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage) {
-        boolean needSep = false;
-        boolean printedAnything = false;
 
         pw.println("ACTIVITY MANAGER URI PERMISSIONS (dumpsys activity permissions)");
 
-        if (mGrantedUriPermissions.size() > 0) {
-            boolean printed = false;
-            int dumpUid = -2;
-            if (dumpPackage != null) {
-                try {
-                    dumpUid = mContext.getPackageManager().getPackageUidAsUser(dumpPackage,
-                            MATCH_ANY_USER, 0);
-                } catch (NameNotFoundException e) {
-                    dumpUid = -1;
-                }
-            }
-            for (int i=0; i<mGrantedUriPermissions.size(); i++) {
-                int uid = mGrantedUriPermissions.keyAt(i);
-                if (dumpUid >= -1 && UserHandle.getAppId(uid) != dumpUid) {
-                    continue;
-                }
-                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
-                if (!printed) {
-                    if (needSep) pw.println();
-                    needSep = true;
-                    pw.println("  Granted Uri Permissions:");
-                    printed = true;
-                    printedAnything = true;
-                }
-                pw.print("  * UID "); pw.print(uid); pw.println(" holds:");
-                for (UriPermission perm : perms.values()) {
-                    pw.print("    "); pw.println(perm);
-                    if (dumpAll) {
-                        perm.dump(pw, "      ");
-                    }
-                }
-            }
-        }
-
-        if (!printedAnything) {
-            pw.println("  (nothing)");
-        }
+        mUgmInternal.dump(pw, dumpAll, dumpPackage);
     }
 
     void dumpPendingIntentsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
@@ -18015,9 +12992,9 @@
                 continue;
             }
             pw.println(String.format("%s%s #%2d: %s",
-                    prefix, (r.persistent ? persistentLabel : normalLabel),
+                    prefix, (r.isPersistent() ? persistentLabel : normalLabel),
                     i, r.toString()));
-            if (r.persistent) {
+            if (r.isPersistent()) {
                 numPers++;
             }
         }
@@ -18070,7 +13047,7 @@
             ProcessRecord r = list.get(i).first;
             long token = proto.start(fieldId);
             String oomAdj = ProcessList.makeOomAdjString(r.setAdj);
-            proto.write(ProcessOomProto.PERSISTENT, r.persistent);
+            proto.write(ProcessOomProto.PERSISTENT, r.isPersistent());
             proto.write(ProcessOomProto.NUM, (origList.size()-1)-list.get(i).second);
             proto.write(ProcessOomProto.OOM_ADJ, oomAdj);
             int schedGroup = ProcessOomProto.SCHED_GROUP_UNKNOWN;
@@ -18093,7 +13070,7 @@
             }
             if (r.foregroundActivities) {
                 proto.write(ProcessOomProto.ACTIVITIES, true);
-            } else if (r.foregroundServices) {
+            } else if (r.hasForegroundServices()) {
                 proto.write(ProcessOomProto.SERVICES, true);
             }
             proto.write(ProcessOomProto.STATE, ProcessList.makeProcStateProtoEnum(r.curProcState));
@@ -18190,14 +13167,14 @@
             char foreground;
             if (r.foregroundActivities) {
                 foreground = 'A';
-            } else if (r.foregroundServices) {
+            } else if (r.hasForegroundServices()) {
                 foreground = 'S';
             } else {
                 foreground = ' ';
             }
             String procState = ProcessList.makeProcStateString(r.curProcState);
             pw.print(prefix);
-            pw.print(r.persistent ? persistentLabel : normalLabel);
+            pw.print(r.isPersistent() ? persistentLabel : normalLabel);
             pw.print(" #");
             int num = (origList.size()-1)-list.get(i).second;
             if (num < 10) pw.print(' ');
@@ -18812,7 +13789,7 @@
                 thread = r.thread;
                 pid = r.pid;
                 oomAdj = r.getSetAdjWithServices();
-                hasActivities = r.activities.size() > 0;
+                hasActivities = r.hasActivities();
             }
             if (thread != null) {
                 if (!opts.isCheckinRequest && opts.dumpDetails) {
@@ -18879,7 +13856,17 @@
                     if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
                         // Record this for posterity if the process has been stable.
                         r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true,
-                                reportType, endTime-startTime, r.pkgList);
+                                reportType, endTime-startTime, r.pkgList.mPkgList);
+                        for (int ipkg = r.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                            ProcessStats.ProcessStateHolder holder = r.pkgList.valueAt(ipkg);
+                            StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+                                    r.info.uid,
+                                    holder.state.getName(),
+                                    holder.state.getPackage(),
+                                    myTotalPss, myTotalUss, myTotalRss, reportType,
+                                    endTime-startTime,
+                                    holder.appVersion);
+                        }
                     }
                 }
 
@@ -19315,7 +14302,7 @@
                 thread = r.thread;
                 pid = r.pid;
                 oomAdj = r.getSetAdjWithServices();
-                hasActivities = r.activities.size() > 0;
+                hasActivities = r.hasActivities();
             }
             if (thread == null) {
                 continue;
@@ -19377,7 +14364,16 @@
                 if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
                     // Record this for posterity if the process has been stable.
                     r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true,
-                            reportType, endTime-startTime, r.pkgList);
+                            reportType, endTime-startTime, r.pkgList.mPkgList);
+                    for (int ipkg = r.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                        ProcessStats.ProcessStateHolder holder = r.pkgList.valueAt(ipkg);
+                        StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+                                r.info.uid,
+                                holder.state.getName(),
+                                holder.state.getPackage(),
+                                myTotalPss, myTotalUss, myTotalRss, reportType, endTime-startTime,
+                                holder.appVersion);
+                    }
                 }
             }
 
@@ -19647,6 +14643,7 @@
         }
         updateCpuStatsNow();
         long[] memtrackTmp = new long[1];
+        long[] swaptrackTmp = new long[2];
         final List<ProcessCpuTracker.Stats> stats;
         // Get a list of Stats that have vsize > 0
         synchronized (mProcessCpuTracker) {
@@ -19657,12 +14654,13 @@
         final int statsCount = stats.size();
         for (int i = 0; i < statsCount; i++) {
             ProcessCpuTracker.Stats st = stats.get(i);
-            long pss = Debug.getPss(st.pid, null, memtrackTmp);
+            long pss = Debug.getPss(st.pid, swaptrackTmp, memtrackTmp);
             if (pss > 0) {
                 if (infoMap.indexOfKey(st.pid) < 0) {
                     ProcessMemInfo mi = new ProcessMemInfo(st.name, st.pid,
                             ProcessList.NATIVE_ADJ, -1, "native", null);
                     mi.pss = pss;
+                    mi.swapPss = swaptrackTmp[1];
                     mi.memtrack = memtrackTmp[0];
                     memInfos.add(mi);
                 }
@@ -19670,14 +14668,17 @@
         }
 
         long totalPss = 0;
+        long totalSwapPss = 0;
         long totalMemtrack = 0;
         for (int i=0, N=memInfos.size(); i<N; i++) {
             ProcessMemInfo mi = memInfos.get(i);
             if (mi.pss == 0) {
-                mi.pss = Debug.getPss(mi.pid, null, memtrackTmp);
+                mi.pss = Debug.getPss(mi.pid, swaptrackTmp, memtrackTmp);
+                mi.swapPss = swaptrackTmp[1];
                 mi.memtrack = memtrackTmp[0];
             }
             totalPss += mi.pss;
+            totalSwapPss += mi.swapPss;
             totalMemtrack += mi.memtrack;
         }
         Collections.sort(memInfos, new Comparator<ProcessMemInfo>() {
@@ -19839,7 +14840,7 @@
         memInfoBuilder.append("\n");
         memInfoBuilder.append("  Lost RAM: ");
         memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb()
-                - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
+                - (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
                 - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb()));
         memInfoBuilder.append("\n");
         Slog.i(TAG, "Low on memory:");
@@ -19943,7 +14944,7 @@
             ProcessRecord capp = conn.client;
             conn.dead = true;
             if (conn.stableCount > 0) {
-                if (!capp.persistent && capp.thread != null
+                if (!capp.isPersistent() && capp.thread != null
                         && capp.pid != 0
                         && capp.pid != MY_PID) {
                     capp.kill("depends on provider "
@@ -19960,7 +14961,8 @@
                 // clean up this connection, we'll just remove it.
                 cpr.connections.remove(i);
                 if (conn.client.conProviders.remove(conn)) {
-                    stopAssociationLocked(capp.uid, capp.processName, cpr.uid, cpr.name);
+                    stopAssociationLocked(capp.uid, capp.processName, cpr.uid,
+                            cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
                 }
             }
         }
@@ -20005,8 +15007,8 @@
             app.waitDialog = null;
         }
 
-        app.crashing = false;
-        app.notResponding = false;
+        app.setCrashing(false);
+        app.setNotResponding(false);
 
         app.resetPackageList(mProcessStats);
         app.unlinkDeathRecipient();
@@ -20036,7 +15038,7 @@
             }
 
             cpr.provider = null;
-            cpr.proc = null;
+            cpr.setProcess(null);
         }
         app.pubProviders.clear();
 
@@ -20051,7 +15053,8 @@
                 ContentProviderConnection conn = app.conProviders.get(i);
                 conn.provider.connections.remove(conn);
                 stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
-                        conn.provider.name);
+                        conn.provider.appInfo.longVersionCode, conn.provider.name,
+                        conn.provider.info.processName);
             }
             app.conProviders.clear();
         }
@@ -20115,7 +15118,7 @@
             return false;
         }
 
-        if (!app.persistent || app.isolated) {
+        if (!app.isPersistent() || app.isolated) {
             if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                     "Removing non-persistent process during cleanup: " + app);
             if (!replacingPid) {
@@ -20139,12 +15142,7 @@
                 TAG_CLEANUP, "Clean-up removing on hold: " + app);
         mProcessesOnHold.remove(app);
 
-        if (app == mHomeProcess) {
-            mHomeProcess = null;
-        }
-        if (app == mPreviousProcess) {
-            mPreviousProcess = null;
-        }
+        mAtmInternal.onCleanUpApplicationRecord(app.getWindowProcessController());
 
         if (restart && !app.isolated) {
             // We have components that still need to be running in the
@@ -20212,8 +15210,8 @@
         final int callingUid = Binder.getCallingUid();
         final boolean canInteractAcrossUsers = (ActivityManager.checkUidPermission(
             INTERACT_ACROSS_USERS_FULL, callingUid) == PERMISSION_GRANTED);
-        final boolean allowed = isGetTasksAllowed("getServices", Binder.getCallingPid(),
-            callingUid);
+        final boolean allowed = mAtmInternal.isGetTasksAllowed("getServices",
+                Binder.getCallingPid(), callingUid);
         synchronized (this) {
             return mServices.getRunningServiceInfoLocked(maxNum, flags, callingUid,
                 allowed, canInteractAcrossUsers);
@@ -20588,6 +15586,7 @@
     // done with this agent
     public void unbindBackupAgent(ApplicationInfo appInfo) {
         if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "unbindBackupAgent: " + appInfo);
+        enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "unbindBackupAgent");
         if (appInfo == null) {
             Slog.w(TAG, "unbind backup agent for null app");
             return;
@@ -21188,7 +16187,7 @@
                 isCallerSystem = true;
                 break;
             default:
-                isCallerSystem = (callerApp != null) && callerApp.persistent;
+                isCallerSystem = (callerApp != null) && callerApp.isPersistent();
                 break;
         }
 
@@ -21281,14 +16280,16 @@
                                     forceStopPackageLocked(list[i], -1, false, true, true,
                                             false, false, userId, "storage unmount");
                                 }
-                                mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
+                                mActivityTaskManager.getRecentTasks().cleanupLocked(
+                                        UserHandle.USER_ALL);
                                 sendPackageBroadcastLocked(
                                         ApplicationThreadConstants.EXTERNAL_STORAGE_UNAVAILABLE,
                                         list, userId);
                             }
                             break;
                         case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
-                            mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
+                            mActivityTaskManager.getRecentTasks().cleanupLocked(
+                                    UserHandle.USER_ALL);
                             break;
                         case Intent.ACTION_PACKAGE_REMOVED:
                         case Intent.ACTION_PACKAGE_CHANGED:
@@ -21318,13 +16319,13 @@
                                                 intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);
 
                                         // Remove all permissions granted from/to this package
-                                        removeUriPermissionsForPackageLocked(ssp, userId, true,
-                                                false);
+                                        mUgmInternal.removeUriPermissionsForPackage(ssp, userId,
+                                                true, false);
 
-                                        mRecentTasks.removeTasksByPackageName(ssp, userId);
+                                        mActivityTaskManager.getRecentTasks().removeTasksByPackageName(ssp, userId);
 
                                         mServices.forceStopPackageLocked(ssp, userId);
-                                        mAppWarnings.onPackageUninstalled(ssp);
+                                        mAtmInternal.onPackageUninstalled(ssp);
                                         mCompatModePackages.handlePackageUninstalledLocked(ssp);
                                         mBatteryStatsService.notePackageUninstalled(ssp);
                                     }
@@ -21351,7 +16352,7 @@
                                     Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
 
                             synchronized(ActivityManagerService.this) {
-                                mRecentTasks.onPackagesSuspendedChanged(
+                                mActivityTaskManager.getRecentTasks().onPackagesSuspendedChanged(
                                         packageNames, suspended, userHandle);
                             }
                             break;
@@ -21405,7 +16406,7 @@
                     String ssp;
                     if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
                         mCompatModePackages.handlePackageDataClearedLocked(ssp);
-                        mAppWarnings.onPackageDataCleared(ssp);
+                        mAtmInternal.onPackageDataCleared(ssp);
                     }
                     break;
                 }
@@ -22036,7 +17037,7 @@
 
             ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
                     abiOverride);
-            app.instr = activeInstr;
+            app.setActiveInstrumentation(activeInstr);
             activeInstr.mFinished = false;
             activeInstr.mRunningProcesses.add(app);
             if (!mActiveInstrumentation.contains(activeInstr)) {
@@ -22069,16 +17070,17 @@
     }
 
     void addInstrumentationResultsLocked(ProcessRecord app, Bundle results) {
-        if (app.instr == null) {
+        final ActiveInstrumentation instr = app.getActiveInstrumentation();
+        if (instr == null) {
             Slog.w(TAG, "finishInstrumentation called on non-instrumented: " + app);
             return;
         }
 
-        if (!app.instr.mFinished && results != null) {
-            if (app.instr.mCurResults == null) {
-                app.instr.mCurResults = new Bundle(results);
+        if (!instr.mFinished && results != null) {
+            if (instr.mCurResults == null) {
+                instr.mCurResults = new Bundle(results);
             } else {
-                app.instr.mCurResults.putAll(results);
+                instr.mCurResults.putAll(results);
             }
         }
     }
@@ -22104,35 +17106,38 @@
 
     @GuardedBy("this")
     void finishInstrumentationLocked(ProcessRecord app, int resultCode, Bundle results) {
-        if (app.instr == null) {
+        final ActiveInstrumentation instr = app.getActiveInstrumentation();
+        if (instr == null) {
             Slog.w(TAG, "finishInstrumentation called on non-instrumented: " + app);
             return;
         }
 
-        if (!app.instr.mFinished) {
-            if (app.instr.mWatcher != null) {
-                Bundle finalResults = app.instr.mCurResults;
+        if (!instr.mFinished) {
+            if (instr.mWatcher != null) {
+                Bundle finalResults = instr.mCurResults;
                 if (finalResults != null) {
-                    if (app.instr.mCurResults != null && results != null) {
+                    if (instr.mCurResults != null && results != null) {
                         finalResults.putAll(results);
                     }
                 } else {
                     finalResults = results;
                 }
-                mInstrumentationReporter.reportFinished(app.instr.mWatcher,
-                        app.instr.mClass, resultCode, finalResults);
+                mInstrumentationReporter.reportFinished(instr.mWatcher,
+                        instr.mClass, resultCode, finalResults);
             }
 
             // Can't call out of the system process with a lock held, so post a message.
-            if (app.instr.mUiAutomationConnection != null) {
+            if (instr.mUiAutomationConnection != null) {
+                mAppOpsService.setAppOpsServiceDelegate(null);
+                getPackageManagerInternalLocked().setCheckPermissionDelegate(null);
                 mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
-                        app.instr.mUiAutomationConnection).sendToTarget();
+                        instr.mUiAutomationConnection).sendToTarget();
             }
-            app.instr.mFinished = true;
+            instr.mFinished = true;
         }
 
-        app.instr.removeProcess(app);
-        app.instr = null;
+        instr.removeProcess(app);
+        app.setActiveInstrumentation(null);
 
         forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false, app.userId,
                 "finished inst");
@@ -22182,69 +17187,19 @@
         return config;
     }
 
-    ActivityStack getFocusedStack() {
-        return mStackSupervisor.getFocusedStack();
+    @Override
+    public StackInfo getFocusedStackInfo() throws RemoteException {
+        return mActivityTaskManager.getFocusedStackInfo();
     }
 
     @Override
-    public StackInfo getFocusedStackInfo() throws RemoteException {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                ActivityStack focusedStack = getFocusedStack();
-                if (focusedStack != null) {
-                    return mStackSupervisor.getStackInfo(focusedStack.mStackId);
-                }
-                return null;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
     public Configuration getConfiguration() {
-        Configuration ci;
-        synchronized(this) {
-            ci = new Configuration(getGlobalConfiguration());
-            ci.userSetLocale = false;
-        }
-        return ci;
+        return mActivityTaskManager.getConfiguration();
     }
 
     @Override
     public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()");
-        synchronized (this) {
-            mSuppressResizeConfigChanges = suppress;
-        }
-    }
-
-    /**
-     * NOTE: For the pinned stack, this method is usually called after the bounds animation has
-     *       animated the stack to the fullscreen, but can also be called if we are relaunching an
-     *       activity and clearing the task at the same time.
-     */
-    @Override
-    // TODO: API should just be about changing windowing modes...
-    public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "moveTasksToFullscreenStack()");
-        synchronized (this) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
-                if (stack != null){
-                    if (!stack.isActivityTypeStandardOrUndefined()) {
-                        throw new IllegalArgumentException(
-                                "You can't move tasks from non-standard stacks.");
-                    }
-                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, onTop);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
+        mActivityTaskManager.suppressResizeConfigChanges(suppress);
     }
 
     @Override
@@ -22257,34 +17212,7 @@
 
         int userId = UserHandle.getCallingUserId();
 
-        synchronized(this) {
-            updatePersistentConfigurationLocked(values, userId);
-        }
-    }
-
-    private void updatePersistentConfigurationLocked(Configuration values, @UserIdInt int userId) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            updateConfigurationLocked(values, null, false, true, userId, false /* deferResume */);
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    private void updateFontScaleIfNeeded(@UserIdInt int userId) {
-        final float scaleFactor = Settings.System.getFloatForUser(mContext.getContentResolver(),
-                FONT_SCALE, 1.0f, userId);
-
-        synchronized (this) {
-            if (getGlobalConfiguration().fontScale == scaleFactor) {
-                return;
-            }
-
-            final Configuration configuration
-                    = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
-            configuration.fontScale = scaleFactor;
-            updatePersistentConfigurationLocked(configuration, userId);
-        }
+        mActivityTaskManager.updatePersistentConfiguration(values, userId);
     }
 
     private void enforceWriteSettingsPermission(String func) {
@@ -22308,489 +17236,16 @@
 
     @Override
     public boolean updateConfiguration(Configuration values) {
-        enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
-
-        synchronized(this) {
-            if (values == null && mWindowManager != null) {
-                // sentinel: fetch the current configuration from the window manager
-                values = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
-            }
-
-            if (mWindowManager != null) {
-                // Update OOM levels based on display size.
-                mProcessList.applyDisplaySize(mWindowManager);
-            }
-
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                if (values != null) {
-                    Settings.System.clearConfiguration(values);
-                }
-                updateConfigurationLocked(values, null, false, false /* persistent */,
-                        UserHandle.USER_NULL, false /* deferResume */,
-                        mTmpUpdateConfigurationResult);
-                return mTmpUpdateConfigurationResult.changes != 0;
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    void updateUserConfigurationLocked() {
-        final Configuration configuration = new Configuration(getGlobalConfiguration());
-        final int currentUserId = mUserController.getCurrentUserId();
-        Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration,
-                currentUserId, Settings.System.canWrite(mContext));
-        updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */,
-                false /* persistent */, currentUserId, false /* deferResume */);
-    }
-
-    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
-            boolean initLocale) {
-        return updateConfigurationLocked(values, starting, initLocale, false /* deferResume */);
-    }
-
-    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
-            boolean initLocale, boolean deferResume) {
-        // pass UserHandle.USER_NULL as userId because we don't persist configuration for any user
-        return updateConfigurationLocked(values, starting, initLocale, false /* persistent */,
-                UserHandle.USER_NULL, deferResume);
-    }
-
-    // To cache the list of supported system locales
-    private String[] mSupportedSystemLocales = null;
-
-    private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
-            boolean initLocale, boolean persistent, int userId, boolean deferResume) {
-        return updateConfigurationLocked(values, starting, initLocale, persistent, userId,
-                deferResume, null /* result */);
-    }
-
-    /**
-     * Do either or both things: (1) change the current configuration, and (2)
-     * make sure the given activity is running with the (now) current
-     * configuration.  Returns true if the activity has been left running, or
-     * false if <var>starting</var> is being destroyed to match the new
-     * configuration.
-     *
-     * @param userId is only used when persistent parameter is set to true to persist configuration
-     *               for that particular user
-     */
-    private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
-            boolean initLocale, boolean persistent, int userId, boolean deferResume,
-            UpdateConfigurationResult result) {
-        int changes = 0;
-        boolean kept = true;
-
-        if (mWindowManager != null) {
-            mWindowManager.deferSurfaceLayout();
-        }
-        try {
-            if (values != null) {
-                changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
-                        deferResume);
-            }
-
-            kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
-        } finally {
-            if (mWindowManager != null) {
-                mWindowManager.continueSurfaceLayout();
-            }
-        }
-
-        if (result != null) {
-            result.changes = changes;
-            result.activityRelaunched = !kept;
-        }
-        return kept;
-    }
-
-    /**
-     * Returns true if this configuration change is interesting enough to send an
-     * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
-     */
-    private static boolean isSplitConfigurationChange(int configDiff) {
-        return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
-    }
-
-    /** Update default (global) configuration and notify listeners about changes. */
-    private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
-            boolean persistent, int userId, boolean deferResume) {
-        mTempConfig.setTo(getGlobalConfiguration());
-        final int changes = mTempConfig.updateFrom(values);
-        if (changes == 0) {
-            // Since calling to Activity.setRequestedOrientation leads to freezing the window with
-            // setting WindowManagerService.mWaitingForConfig to true, it is important that we call
-            // performDisplayOverrideConfigUpdate in order to send the new display configuration
-            // (even if there are no actual changes) to unfreeze the window.
-            performDisplayOverrideConfigUpdate(values, deferResume, DEFAULT_DISPLAY);
-            return 0;
-        }
-
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION,
-                "Updating global configuration to: " + values);
-
-        EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
-        StatsLog.write(StatsLog.RESOURCE_CONFIGURATION_CHANGED,
-                values.colorMode,
-                values.densityDpi,
-                values.fontScale,
-                values.hardKeyboardHidden,
-                values.keyboard,
-                values.keyboardHidden,
-                values.mcc,
-                values.mnc,
-                values.navigation,
-                values.navigationHidden,
-                values.orientation,
-                values.screenHeightDp,
-                values.screenLayout,
-                values.screenWidthDp,
-                values.smallestScreenWidthDp,
-                values.touchscreen,
-                values.uiMode);
-
-
-        if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
-            final LocaleList locales = values.getLocales();
-            int bestLocaleIndex = 0;
-            if (locales.size() > 1) {
-                if (mSupportedSystemLocales == null) {
-                    mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
-                }
-                bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));
-            }
-            SystemProperties.set("persist.sys.locale",
-                    locales.get(bestLocaleIndex).toLanguageTag());
-            LocaleList.setDefault(locales, bestLocaleIndex);
-            mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
-                    locales.get(bestLocaleIndex)));
-        }
-
-        mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
-        mTempConfig.seq = mConfigurationSeq;
-
-        // Update stored global config and notify everyone about the change.
-        mStackSupervisor.onConfigurationChanged(mTempConfig);
-
-        Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
-        // TODO(multi-display): Update UsageEvents#Event to include displayId.
-        mUsageStatsService.reportConfigurationChange(mTempConfig,
-                mUserController.getCurrentUserId());
-
-        // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
-        updateShouldShowDialogsLocked(mTempConfig);
-
-        AttributeCache ac = AttributeCache.instance();
-        if (ac != null) {
-            ac.updateConfiguration(mTempConfig);
-        }
-
-        // Make sure all resources in our process are updated right now, so that anyone who is going
-        // to retrieve resource values after we return will be sure to get the new ones. This is
-        // especially important during boot, where the first config change needs to guarantee all
-        // resources have that config before following boot code is executed.
-        mSystemThread.applyConfigurationToResources(mTempConfig);
-
-        // We need another copy of global config because we're scheduling some calls instead of
-        // running them in place. We need to be sure that object we send will be handled unchanged.
-        final Configuration configCopy = new Configuration(mTempConfig);
-        if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
-            Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
-            msg.obj = configCopy;
-            msg.arg1 = userId;
-            mHandler.sendMessage(msg);
-        }
-
-        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
-            ProcessRecord app = mLruProcesses.get(i);
-            try {
-                if (app.thread != null) {
-                    if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
-                            + app.processName + " new config " + configCopy);
-                    mLifecycleManager.scheduleTransaction(app.thread,
-                            ConfigurationChangeItem.obtain(configCopy));
-                }
-            } catch (Exception e) {
-                Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
-            }
-        }
-
-        Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING
-                | Intent.FLAG_RECEIVER_FOREGROUND
-                | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-        broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
-                OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
-                UserHandle.USER_ALL);
-        if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
-            intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
-            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
-                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
-                    | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-            if (initLocale || !mProcessesReady) {
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-            }
-            broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
-                    OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
-                    UserHandle.USER_ALL);
-        }
-
-        // Send a broadcast to PackageInstallers if the configuration change is interesting
-        // for the purposes of installing additional splits.
-        if (!initLocale && isSplitConfigurationChange(changes)) {
-            intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
-            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
-                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-
-            // Typically only app stores will have this permission.
-            String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES };
-            broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions,
-                    OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
-        }
-
-        // Override configuration of the default display duplicates global config, so we need to
-        // update it also. This will also notify WindowManager about changes.
-        performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
-                DEFAULT_DISPLAY);
-
-        return changes;
+        return mActivityTaskManager.updateConfiguration(values);
     }
 
     @Override
-    public boolean updateDisplayOverrideConfiguration(Configuration values, int displayId) {
-        enforceCallingPermission(CHANGE_CONFIGURATION, "updateDisplayOverrideConfiguration()");
-
-        synchronized (this) {
-            // Check if display is initialized in AM.
-            if (!mStackSupervisor.isDisplayAdded(displayId)) {
-                // Call might come when display is not yet added or has already been removed.
-                if (DEBUG_CONFIGURATION) {
-                    Slog.w(TAG, "Trying to update display configuration for non-existing displayId="
-                            + displayId);
-                }
-                return false;
-            }
-
-            if (values == null && mWindowManager != null) {
-                // sentinel: fetch the current configuration from the window manager
-                values = mWindowManager.computeNewConfiguration(displayId);
-            }
-
-            if (mWindowManager != null) {
-                // Update OOM levels based on display size.
-                mProcessList.applyDisplaySize(mWindowManager);
-            }
-
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                if (values != null) {
-                    Settings.System.clearConfiguration(values);
-                }
-                updateDisplayOverrideConfigurationLocked(values, null /* starting */,
-                        false /* deferResume */, displayId, mTmpUpdateConfigurationResult);
-                return mTmpUpdateConfigurationResult.changes != 0;
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    boolean updateDisplayOverrideConfigurationLocked(Configuration values, ActivityRecord starting,
-            boolean deferResume, int displayId) {
-        return updateDisplayOverrideConfigurationLocked(values, starting, deferResume /* deferResume */,
-                displayId, null /* result */);
-    }
-
-    /**
-     * Updates override configuration specific for the selected display. If no config is provided,
-     * new one will be computed in WM based on current display info.
-     */
-    private boolean updateDisplayOverrideConfigurationLocked(Configuration values,
-            ActivityRecord starting, boolean deferResume, int displayId,
-            UpdateConfigurationResult result) {
-        int changes = 0;
-        boolean kept = true;
-
-        if (mWindowManager != null) {
-            mWindowManager.deferSurfaceLayout();
-        }
-        try {
-            if (values != null) {
-                if (displayId == DEFAULT_DISPLAY) {
-                    // Override configuration of the default display duplicates global config, so
-                    // we're calling global config update instead for default display. It will also
-                    // apply the correct override config.
-                    changes = updateGlobalConfigurationLocked(values, false /* initLocale */,
-                            false /* persistent */, UserHandle.USER_NULL /* userId */, deferResume);
-                } else {
-                    changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId);
-                }
-            }
-
-            kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
-        } finally {
-            if (mWindowManager != null) {
-                mWindowManager.continueSurfaceLayout();
-            }
-        }
-
-        if (result != null) {
-            result.changes = changes;
-            result.activityRelaunched = !kept;
-        }
-        return kept;
-    }
-
-    private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,
-            int displayId) {
-        mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));
-        final int changes = mTempConfig.updateFrom(values);
-        if (changes != 0) {
-            Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
-                    + mTempConfig + " for displayId=" + displayId);
-            mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId);
-
-            final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
-            if (isDensityChange && displayId == DEFAULT_DISPLAY) {
-                mAppWarnings.onDensityChanged();
-
-                killAllBackgroundProcessesExcept(N,
-                        ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
-            }
-        }
-
-        // Update the configuration with WM first and check if any of the stacks need to be resized
-        // due to the configuration change. If so, resize the stacks now and do any relaunches if
-        // necessary. This way we don't need to relaunch again afterwards in
-        // ensureActivityConfiguration().
-        if (mWindowManager != null) {
-            final int[] resizedStacks =
-                    mWindowManager.setNewDisplayOverrideConfiguration(mTempConfig, displayId);
-            if (resizedStacks != null) {
-                for (int stackId : resizedStacks) {
-                    resizeStackWithBoundsFromWindowManager(stackId, deferResume);
-                }
-            }
-        }
-
-        return changes;
-    }
-
-    /** Applies latest configuration and/or visibility updates if needed. */
-    private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
-        boolean kept = true;
-        final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
-        // mainStack is null during startup.
-        if (mainStack != null) {
-            if (changes != 0 && starting == null) {
-                // If the configuration changed, and the caller is not already
-                // in the process of starting an activity, then find the top
-                // activity to check if its configuration needs to change.
-                starting = mainStack.topRunningActivityLocked();
-            }
-
-            if (starting != null) {
-                kept = starting.ensureActivityConfiguration(changes,
-                        false /* preserveWindow */);
-                // And we need to make sure at this point that all other activities
-                // are made visible with the correct configuration.
-                mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
-                        !PRESERVE_WINDOWS);
-            }
-        }
-
-        return kept;
-    }
-
-    /** Helper method that requests bounds from WM and applies them to stack. */
-    private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
-        final Rect newStackBounds = new Rect();
-        final ActivityStack stack = mStackSupervisor.getStack(stackId);
-
-        // TODO(b/71548119): Revert CL introducing below once cause of mismatch is found.
-        if (stack == null) {
-            final StringWriter writer = new StringWriter();
-            final PrintWriter printWriter = new PrintWriter(writer);
-            mStackSupervisor.dumpDisplays(printWriter);
-            printWriter.flush();
-
-            Log.wtf(TAG, "stack not found:" + stackId + " displays:" + writer);
-        }
-
-        stack.getBoundsForNewConfiguration(newStackBounds);
-        mStackSupervisor.resizeStackLocked(
-                stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
-                null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
-                false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
-    }
-
-    /**
-     * Decide based on the configuration whether we should show the ANR,
-     * crash, etc dialogs.  The idea is that if there is no affordance to
-     * press the on-screen buttons, or the user experience would be more
-     * greatly impacted than the crash itself, we shouldn't show the dialog.
-     *
-     * A thought: SystemUI might also want to get told about this, the Power
-     * dialog / global actions also might want different behaviors.
-     */
-    private void updateShouldShowDialogsLocked(Configuration config) {
-        final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
-                                   && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
-                                   && config.navigation == Configuration.NAVIGATION_NONAV);
-        int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
-        final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR
-                && !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER)
-                && modeType != Configuration.UI_MODE_TYPE_TELEVISION
-                && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
-        final boolean hideDialogsSet = Settings.Global.getInt(mContext.getContentResolver(),
-                HIDE_ERROR_DIALOGS, 0) != 0;
-        mShowDialogs = inputMethodExists && uiModeSupportsDialogs && !hideDialogsSet;
-    }
-
-    @Override
-    public boolean shouldUpRecreateTask(IBinder token, String destAffinity) {
-        synchronized (this) {
-            ActivityRecord srec = ActivityRecord.forTokenLocked(token);
-            if (srec != null) {
-                return srec.getStack().shouldUpRecreateTaskLocked(srec, destAffinity);
-            }
-        }
-        return false;
-    }
-
-    public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
-            Intent resultData) {
-
-        synchronized (this) {
-            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-            if (r != null) {
-                return r.getStack().navigateUpToLocked(r, destIntent, resultCode, resultData);
-            }
-            return false;
-        }
-    }
-
     public int getLaunchedFromUid(IBinder activityToken) {
-        ActivityRecord srec;
-        synchronized (this) {
-            srec = ActivityRecord.forTokenLocked(activityToken);
-        }
-        if (srec == null) {
-            return -1;
-        }
-        return srec.launchedFromUid;
+        return mActivityTaskManager.getLaunchedFromUid(activityToken);
     }
 
     public String getLaunchedFromPackage(IBinder activityToken) {
-        ActivityRecord srec;
-        synchronized (this) {
-            srec = ActivityRecord.forTokenLocked(activityToken);
-        }
-        if (srec == null) {
-            return null;
-        }
-        return srec.launchedFromPackage;
+        return mActivityTaskManager.getLaunchedFromPackage(activityToken);
     }
 
     // =========================================================
@@ -22823,7 +17278,8 @@
     }
 
     Association startAssociationLocked(int sourceUid, String sourceProcess, int sourceState,
-            int targetUid, ComponentName targetComponent, String targetProcess) {
+            int targetUid, long targetVersionCode, ComponentName targetComponent,
+            String targetProcess) {
         if (!mTrackingAssociations) {
             return null;
         }
@@ -22859,7 +17315,7 @@
     }
 
     void stopAssociationLocked(int sourceUid, String sourceProcess, int targetUid,
-            ComponentName targetComponent) {
+            long targetVersionCode, ComponentName targetComponent, String targetProcess) {
         if (!mTrackingAssociations) {
             return;
         }
@@ -22919,6 +17375,129 @@
         }
     }
 
+    private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
+            new ComputeOomAdjWindowCallback();
+
+    private final class ComputeOomAdjWindowCallback
+            implements WindowProcessController.ComputeOomAdjCallback {
+
+        ProcessRecord app;
+        int adj;
+        boolean foregroundActivities;
+        int procState;
+        int schedGroup;
+        int appUid;
+        int logUid;
+        int processStateCurTop;
+
+        void initialize(ProcessRecord app, int adj, boolean foregroundActivities,
+                int procState, int schedGroup, int appUid, int logUid, int processStateCurTop) {
+            this.app = app;
+            this.adj = adj;
+            this.foregroundActivities = foregroundActivities;
+            this.procState = procState;
+            this.schedGroup = schedGroup;
+            this.appUid = appUid;
+            this.logUid = logUid;
+            this.processStateCurTop = processStateCurTop;
+        }
+
+        @Override
+        public void onVisibleActivity() {
+            // App has a visible activity; only upgrade adjustment.
+            if (adj > ProcessList.VISIBLE_APP_ADJ) {
+                adj = ProcessList.VISIBLE_APP_ADJ;
+                app.adjType = "vis-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to vis-activity: " + app);
+                }
+            }
+            if (procState > processStateCurTop) {
+                procState = processStateCurTop;
+                app.adjType = "vis-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise procstate to vis-activity (top): " + app);
+                }
+            }
+            if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            }
+            app.cached = false;
+            app.empty = false;
+            foregroundActivities = true;
+        }
+
+        @Override
+        public void onPausedActivity() {
+            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                app.adjType = "pause-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to pause-activity: "  + app);
+                }
+            }
+            if (procState > processStateCurTop) {
+                procState = processStateCurTop;
+                app.adjType = "pause-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise procstate to pause-activity (top): "  + app);
+                }
+            }
+            if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            }
+            app.cached = false;
+            app.empty = false;
+            foregroundActivities = true;
+        }
+
+        @Override
+        public void onStoppingActivity(boolean finishing) {
+            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                app.adjType = "stop-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise adj to stop-activity: "  + app);
+                }
+            }
+
+            // For the process state, we will at this point consider the process to be cached. It
+            // will be cached either as an activity or empty depending on whether the activity is
+            // finishing. We do this so that we can treat the process as cached for purposes of
+            // memory trimming (determining current memory level, trim command to send to process)
+            // since there can be an arbitrary number of stopping processes and they should soon all
+            // go into the cached state.
+            if (!finishing) {
+                if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+                    procState = PROCESS_STATE_LAST_ACTIVITY;
+                    app.adjType = "stop-activity";
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                "Raise procstate to stop-activity: " + app);
+                    }
+                }
+            }
+            app.cached = false;
+            app.empty = false;
+            foregroundActivities = true;
+        }
+
+        @Override
+        public void onOtherActivity() {
+            if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
+                procState = PROCESS_STATE_CACHED_ACTIVITY;
+                app.adjType = "cch-act";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise procstate to cached activity: " + app);
+                }
+            }
+        }
+    }
+
     private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
             boolean doingAll, long now) {
         if (mAdjSeq == app.adjSeq) {
@@ -22929,13 +17508,14 @@
                 // The process is being computed, so there is a cycle. We cannot
                 // rely on this process's state.
                 app.containsCycle = true;
+
                 return false;
             }
         }
 
         if (app.thread == null) {
             app.adjSeq = mAdjSeq;
-            app.curSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+            app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
             app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
             app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ;
             app.completedAdjSeq = app.adjSeq;
@@ -22948,11 +17528,12 @@
         app.empty = false;
         app.cached = false;
 
-        final int activitiesSize = app.activities.size();
+        final WindowProcessController wpc = app.getWindowProcessController();
         final int appUid = app.info.uid;
         final int logUid = mCurOomAdjUid;
 
         int prevAppAdj = app.curAdj;
+        int prevProcState = app.curProcState;
 
         if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
             // The max adjustment doesn't allow this app to be anything
@@ -22964,7 +17545,7 @@
             app.adjSeq = mAdjSeq;
             app.curRawAdj = app.maxAdj;
             app.foregroundActivities = false;
-            app.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
             app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
             // System processes can do UI, and when they do we want to have
             // them trim their memory after the user leaves the UI.  To
@@ -22973,29 +17554,24 @@
             app.systemNoUi = true;
             if (app == TOP_APP) {
                 app.systemNoUi = false;
-                app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+                app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
                 app.adjType = "pers-top-activity";
             } else if (app.hasTopUi) {
                 // sched group/proc state adjustment is below
                 app.systemNoUi = false;
                 app.adjType = "pers-top-ui";
-            } else if (activitiesSize > 0) {
-                for (int j = 0; j < activitiesSize; j++) {
-                    final ActivityRecord r = app.activities.get(j);
-                    if (r.visible) {
-                        app.systemNoUi = false;
-                    }
-                }
+            } else if (wpc.hasVisibleActivities()) {
+                app.systemNoUi = false;
             }
             if (!app.systemNoUi) {
               if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
                   // screen on, promote UI
                   app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI;
-                  app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+                  app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
               } else {
                   // screen off, restrict UI scheduling
                   app.curProcState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-                  app.curSchedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
+                  app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
               }
             }
             app.curAdj = app.maxAdj;
@@ -23006,7 +17582,7 @@
 
         app.systemNoUi = false;
 
-        final int PROCESS_STATE_CUR_TOP = mTopProcessState;
+        final int PROCESS_STATE_CUR_TOP = mAtmInternal.getTopProcessState();
 
         // Determine the importance of the process, starting with most
         // important to least, and assign an appropriate OOM adjustment.
@@ -23035,7 +17611,7 @@
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app);
             }
-        } else if (app.instr != null) {
+        } else if (app.getActiveInstrumentation() != null) {
             // Don't want to kill running instrumentation.
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
@@ -23094,120 +17670,23 @@
         }
 
         // Examine all activities if not already foreground.
-        if (!foregroundActivities && activitiesSize > 0) {
-            int minLayer = ProcessList.VISIBLE_APP_LAYER_MAX;
-            for (int j = 0; j < activitiesSize; j++) {
-                final ActivityRecord r = app.activities.get(j);
-                if (r.app != app) {
-                    Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
-                            + " instead of expected " + app);
-                    if (r.app == null || (r.app.uid == app.uid)) {
-                        // Only fix things up when they look sane
-                        r.setProcess(app);
-                    } else {
-                        continue;
-                    }
-                }
-                if (r.visible) {
-                    // App has a visible activity; only upgrade adjustment.
-                    if (adj > ProcessList.VISIBLE_APP_ADJ) {
-                        adj = ProcessList.VISIBLE_APP_ADJ;
-                        app.adjType = "vis-activity";
-                        if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                            reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                    "Raise adj to vis-activity: " + app);
-                        }
-                    }
-                    if (procState > PROCESS_STATE_CUR_TOP) {
-                        procState = PROCESS_STATE_CUR_TOP;
-                        app.adjType = "vis-activity";
-                        if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                            reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                    "Raise procstate to vis-activity (top): " + app);
-                        }
-                    }
-                    if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
-                        schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                    }
-                    app.cached = false;
-                    app.empty = false;
-                    foregroundActivities = true;
-                    final TaskRecord task = r.getTask();
-                    if (task != null && minLayer > 0) {
-                        final int layer = task.mLayerRank;
-                        if (layer >= 0 && minLayer > layer) {
-                            minLayer = layer;
-                        }
-                    }
-                    break;
-                } else if (r.isState(ActivityState.PAUSING, ActivityState.PAUSED)) {
-                    if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                        adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                        app.adjType = "pause-activity";
-                        if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                            reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                    "Raise adj to pause-activity: "  + app);
-                        }
-                    }
-                    if (procState > PROCESS_STATE_CUR_TOP) {
-                        procState = PROCESS_STATE_CUR_TOP;
-                        app.adjType = "pause-activity";
-                        if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                            reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                    "Raise procstate to pause-activity (top): "  + app);
-                        }
-                    }
-                    if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
-                        schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                    }
-                    app.cached = false;
-                    app.empty = false;
-                    foregroundActivities = true;
-                } else if (r.isState(ActivityState.STOPPING)) {
-                    if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                        adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                        app.adjType = "stop-activity";
-                        if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                            reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                    "Raise adj to stop-activity: "  + app);
-                        }
-                    }
-                    // For the process state, we will at this point consider the
-                    // process to be cached.  It will be cached either as an activity
-                    // or empty depending on whether the activity is finishing.  We do
-                    // this so that we can treat the process as cached for purposes of
-                    // memory trimming (determing current memory level, trim command to
-                    // send to process) since there can be an arbitrary number of stopping
-                    // processes and they should soon all go into the cached state.
-                    if (!r.finishing) {
-                        if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
-                            procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
-                            app.adjType = "stop-activity";
-                            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                                reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                        "Raise procstate to stop-activity: " + app);
-                            }
-                        }
-                    }
-                    app.cached = false;
-                    app.empty = false;
-                    foregroundActivities = true;
-                } else {
-                    if (procState > ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
-                        procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
-                        app.adjType = "cch-act";
-                        if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                            reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                    "Raise procstate to cached activity: " + app);
-                        }
-                    }
-                }
-            }
+        if (!foregroundActivities && wpc.hasActivities()) {
+            mTmpComputeOomAdjWindowCallback.initialize(app, adj, foregroundActivities, procState,
+                    schedGroup, appUid, logUid, PROCESS_STATE_CUR_TOP);
+            final int minLayer = wpc.computeOomAdjFromActivities(
+                    ProcessList.VISIBLE_APP_LAYER_MAX, mTmpComputeOomAdjWindowCallback);
+
+            adj = mTmpComputeOomAdjWindowCallback.adj;
+            foregroundActivities = mTmpComputeOomAdjWindowCallback.foregroundActivities;
+            procState = mTmpComputeOomAdjWindowCallback.procState;
+            schedGroup = mTmpComputeOomAdjWindowCallback.schedGroup;
+
             if (adj == ProcessList.VISIBLE_APP_ADJ) {
                 adj += minLayer;
             }
         }
-        if (procState > ActivityManager.PROCESS_STATE_CACHED_RECENT && app.recentTasks.size() > 0) {
+
+        if (procState > ActivityManager.PROCESS_STATE_CACHED_RECENT && app.hasRecentTasks()) {
             procState = ActivityManager.PROCESS_STATE_CACHED_RECENT;
             app.adjType = "cch-rec";
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -23217,7 +17696,7 @@
 
         if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
                 || procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
-            if (app.foregroundServices) {
+            if (app.hasForegroundServices()) {
                 // The user is aware of this app, so make it visible.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                 procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
@@ -23278,7 +17757,7 @@
             }
         }
 
-        if (app == mHomeProcess) {
+        if (wpc == mActivityTaskManager.mHomeProcess) {
             if (adj > ProcessList.HOME_APP_ADJ) {
                 // This process is hosting what we currently consider to be the
                 // home app, so we don't want to let it go into the background.
@@ -23299,7 +17778,7 @@
             }
         }
 
-        if (app == mPreviousProcess && app.activities.size() > 0) {
+        if (wpc == mActivityTaskManager.mPreviousProcess && app.hasActivities()) {
             if (adj > ProcessList.PREVIOUS_APP_ADJ) {
                 // This was the previous process that showed UI to the user.
                 // We want to try to keep it around more aggressively, to give
@@ -23312,8 +17791,8 @@
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
                 }
             }
-            if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
-                procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+            if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+                procState = PROCESS_STATE_LAST_ACTIVITY;
                 app.adjType = "previous";
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
@@ -23376,7 +17855,7 @@
                                 "Raise procstate to started service: " + app);
                     }
                 }
-                if (app.hasShownUi && app != mHomeProcess) {
+                if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess) {
                     // If this process has shown some UI, let it immediately
                     // go to the LRU list because it may be pretty heavy with
                     // UI stuff.  We'll tag it with a label just to help
@@ -23423,23 +17902,29 @@
                     // all connected clients.
                     ConnectionRecord cr = clist.get(i);
                     if (cr.binding.client == app) {
-                        // Binding to ourself is not interesting.
+                        // Binding to oneself is not interesting.
                         continue;
                     }
 
+                    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 ignore this connection and allow
-                            // this process to retry computeOomAdjLocked later in case a later-checked
-                            // connection from a client  would raise its priority legitimately.
+                            // 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;
-                            continue;
+                            // 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;
+                            }
                         }
                         int clientAdj = client.curRawAdj;
                         int clientProcState = client.curProcState;
-                        if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+                        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
                             // doesn't propagate except under certain conditions.
@@ -23449,7 +17934,7 @@
                         if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
                             // Not doing bind OOM management, so treat
                             // this guy more like a started service.
-                            if (app.hasShownUi && app != mHomeProcess) {
+                            if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess) {
                                 // If this process has shown some UI, let it immediately
                                 // go to the LRU list because it may be pretty heavy with
                                 // UI stuff.  We'll tag it with a label just to help
@@ -23482,7 +17967,7 @@
                             // about letting this process get into the LRU
                             // list to be killed and restarted if needed for
                             // memory.
-                            if (app.hasShownUi && app != mHomeProcess
+                            if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess
                                     && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                                 if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
                                     adjType = "cch-bound-ui-services";
@@ -23498,6 +17983,8 @@
                                         newAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
                                         schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                                         procState = ActivityManager.PROCESS_STATE_PERSISTENT;
+                                        cr.trackProcState(procState, mAdjSeq, now);
+                                        trackedProcState = true;
                                     }
                                 } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
                                         && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
@@ -23526,9 +18013,10 @@
                             // This will treat important bound services identically to
                             // the top app, which may behave differently than generic
                             // foreground work.
-                            if (client.curSchedGroup > schedGroup) {
+                            final int curSchedGroup = client.getCurrentSchedulingGroup();
+                            if (curSchedGroup > schedGroup) {
                                 if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
-                                    schedGroup = client.curSchedGroup;
+                                    schedGroup = curSchedGroup;
                                 } else {
                                     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                                 }
@@ -23582,6 +18070,9 @@
                                         ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
                             }
                         }
+                        if (!trackedProcState) {
+                            cr.trackProcState(clientProcState, mAdjSeq, now);
+                        }
                         if (procState > clientProcState) {
                             procState = clientProcState;
                             if (adjType == null) {
@@ -23658,22 +18149,27 @@
                 }
                 computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
                 if (client.containsCycle) {
-                    // We've detected a cycle. We should ignore this connection and allow
-                    // this process to retry computeOomAdjLocked later in case a later-checked
-                    // connection from a client  would raise its priority legitimately.
+                    // 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;
-                    continue;
+                    // 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;
+                    }
                 }
                 int clientAdj = client.curRawAdj;
                 int clientProcState = client.curProcState;
-                if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+                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.
                     clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
                 }
                 String adjType = null;
                 if (adj > clientAdj) {
-                    if (app.hasShownUi && app != mHomeProcess
+                    if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess
                             && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                         adjType = "cch-ui-provider";
                     } else {
@@ -23711,10 +18207,11 @@
                         }
                     }
                 }
+                conn.trackProcState(clientProcState, mAdjSeq, now);
                 if (procState > clientProcState) {
                     procState = clientProcState;
                 }
-                if (client.curSchedGroup > schedGroup) {
+                if (client.getCurrentSchedulingGroup() > schedGroup) {
                     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                 }
                 if (adjType != null) {
@@ -23749,8 +18246,10 @@
                 }
                 if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
                     procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise procstate to external provider: " + app);
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                "Raise procstate to external provider: " + app);
+                    }
                 }
             }
         }
@@ -23767,8 +18266,8 @@
                             "Raise adj to recent provider: " + app);
                 }
             }
-            if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
-                procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+            if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+                procState = PROCESS_STATE_LAST_ACTIVITY;
                 app.adjType = "recent-provider";
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ,
@@ -23830,7 +18329,7 @@
             } else if (app.treatLikeActivity) {
                 // This is a cached process, but somebody wants us to treat it like it has
                 // an activity, okay!
-                procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+                procState = PROCESS_STATE_CACHED_ACTIVITY;
                 app.adjType = "cch-as-act";
             }
         }
@@ -23889,13 +18388,13 @@
         // worry about this for max adj above, since max adj will always be used to
         // keep it out of the cached vaues.
         app.curAdj = app.modifyRawOomAdj(adj);
-        app.curSchedGroup = schedGroup;
+        app.setCurrentSchedulingGroup(schedGroup);
         app.curProcState = procState;
         app.foregroundActivities = foregroundActivities;
         app.completedAdjSeq = mAdjSeq;
 
-        // if curAdj is less than prevAppAdj, then this process was promoted
-        return app.curAdj < prevAppAdj;
+        // if curAdj or curProcState improved, then this process was promoted
+        return app.curAdj < prevAppAdj || app.curProcState < prevProcState;
     }
 
     /**
@@ -23906,7 +18405,17 @@
         EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024,
                 swapPss * 1024, rss * 1024, statType, procState, pssDuration);
         proc.lastPssTime = now;
-        proc.baseProcessTracker.addPss(pss, uss, rss, true, statType, pssDuration, proc.pkgList);
+        proc.baseProcessTracker.addPss(
+                pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList);
+        for (int ipkg = proc.pkgList.mPkgList.size() - 1; ipkg >= 0; ipkg--) {
+            ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+            StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+                    proc.info.uid,
+                    holder.state.getName(),
+                    holder.state.getPackage(),
+                    pss, uss, rss, statType, pssDuration,
+                    holder.appVersion);
+        }
         if (DEBUG_PSS) Slog.d(TAG_PSS,
                 "pss of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss
                 + " state=" + ProcessList.makeProcStateString(procState));
@@ -24047,7 +18556,7 @@
                 app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
                         : ProcessStats.ADD_PSS_INTERNAL_ALL_MEM;
                 app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
-                        app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now);
+                        app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
                 mPendingPssProcesses.add(app);
             }
         }
@@ -24097,7 +18606,7 @@
             }
         }
         return !processingBroadcasts
-                && (isSleepingLocked() || mStackSupervisor.allResumedActivitiesIdle());
+                && (mAtmInternal.isSleeping() || mStackSupervisor.allResumedActivitiesIdle());
     }
 
     /**
@@ -24257,7 +18766,15 @@
                         }
                         app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince
                                 + " dur=" + checkDur + " limit=" + cpuLimit, true);
-                        app.baseProcessTracker.reportExcessiveCpu(app.pkgList);
+                        app.baseProcessTracker.reportExcessiveCpu(app.pkgList.mPkgList);
+                        for (int ipkg = app.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                            ProcessStats.ProcessStateHolder holder = app.pkgList.valueAt(ipkg);
+                            StatsLog.write(StatsLog.EXCESSIVE_CPU_USAGE_REPORTED,
+                                    app.info.uid,
+                                    holder.state.getName(),
+                                    holder.state.getPackage(),
+                                    holder.appVersion);
+                        }
                     }
                 }
                 app.lastCpuTime = app.curCpuTime;
@@ -24286,12 +18803,13 @@
             app.verifiedAdj = ProcessList.INVALID_ADJ;
         }
 
-        if (app.setSchedGroup != app.curSchedGroup) {
+        final int curSchedGroup = app.getCurrentSchedulingGroup();
+        if (app.setSchedGroup != curSchedGroup) {
             int oldSchedGroup = app.setSchedGroup;
-            app.setSchedGroup = app.curSchedGroup;
+            app.setSchedGroup = curSchedGroup;
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.uid) {
                 String msg = "Setting sched group of " + app.processName
-                        + " to " + app.curSchedGroup + ": " + app.adjType;
+                        + " to " + curSchedGroup + ": " + app.adjType;
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
             }
             if (app.waitingToKill != null && app.curReceivers.isEmpty()
@@ -24300,7 +18818,7 @@
                 success = false;
             } else {
                 int processGroup;
-                switch (app.curSchedGroup) {
+                switch (curSchedGroup) {
                     case ProcessList.SCHED_GROUP_BACKGROUND:
                         processGroup = THREAD_GROUP_BG_NONINTERACTIVE;
                         break;
@@ -24318,10 +18836,11 @@
                 long oldId = Binder.clearCallingIdentity();
                 try {
                     setProcessGroup(app.pid, processGroup);
-                    if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+                    if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
                         // do nothing if we already switched to RT
                         if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
-                            mVrController.onTopProcChangedLocked(app);
+                            mActivityTaskManager.onTopProcChangedLocked(
+                                    app.getWindowProcessController());
                             if (mUseFifoUiScheduling) {
                                 // Switch UI pipeline for app to SCHED_FIFO
                                 app.savedPriority = Process.getThreadPriority(app.pid);
@@ -24352,8 +18871,9 @@
                             }
                         }
                     } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
-                               app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
-                        mVrController.onTopProcChangedLocked(app);
+                            curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+                        mActivityTaskManager.onTopProcChangedLocked(
+                                app.getWindowProcessController());
                         if (mUseFifoUiScheduling) {
                             try {
                                 // Reset UI pipeline to SCHED_OTHER
@@ -24382,7 +18902,7 @@
                 } catch (Exception e) {
                     if (false) {
                         Slog.w(TAG, "Failed setting process group of " + app.pid
-                                + " to " + app.curSchedGroup);
+                                + " to " + app.getCurrentSchedulingGroup());
                         Slog.w(TAG, "at location", e);
                     }
                 } finally {
@@ -24394,16 +18914,16 @@
             app.repForegroundActivities = app.foregroundActivities;
             changes |= ProcessChangeItem.CHANGE_ACTIVITIES;
         }
-        if (app.repProcState != app.curProcState) {
-            app.repProcState = app.curProcState;
+        if (app.getReportedProcState() != app.curProcState) {
+            app.setReportedProcState(app.curProcState);
             if (app.thread != null) {
                 try {
                     if (false) {
                         //RuntimeException h = new RuntimeException("here");
-                        Slog.i(TAG, "Sending new process state " + app.repProcState
+                        Slog.i(TAG, "Sending new process state " + app.getReportedProcState()
                                 + " to " + app /*, h*/);
                     }
-                    app.thread.setProcessState(app.repProcState);
+                    app.thread.setProcessState(app.getReportedProcState());
                 } catch (RemoteException e) {
                 }
             }
@@ -24428,7 +18948,7 @@
             }
             app.lastStateTime = now;
             app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
-                    app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now);
+                    app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
             if (DEBUG_PSS) Slog.d(TAG_PSS, "Process state change from "
                     + ProcessList.makeProcStateString(app.setProcState) + " to "
                     + ProcessList.makeProcStateString(app.curProcState) + " next pss in "
@@ -24439,7 +18959,7 @@
                     mTestPssMode)))) {
                 if (requestPssLocked(app, app.setProcState)) {
                     app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
-                            app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now);
+                            app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
                 }
             } else if (false && DEBUG_PSS) Slog.d(TAG_PSS,
                     "Not requesting pss of " + app + ": next=" + (app.nextPssTime-now));
@@ -24690,10 +19210,9 @@
     }
 
     private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
-        if (proc.thread != null) {
-            if (proc.baseProcessTracker != null) {
-                proc.baseProcessTracker.setState(proc.repProcState, memFactor, now, proc.pkgList);
-            }
+        if (proc.thread != null && proc.baseProcessTracker != null) {
+            proc.baseProcessTracker.setState(
+                    proc.getReportedProcState(), memFactor, now, proc.pkgList.mPkgList);
         }
     }
 
@@ -24711,8 +19230,8 @@
     @GuardedBy("this")
     final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
             boolean oomAdj) {
-        if (isForeground != proc.foregroundServices) {
-            proc.foregroundServices = isForeground;
+        if (isForeground != proc.hasForegroundServices()) {
+            proc.setHasForegroundServices(isForeground);
             ArrayList<ProcessRecord> curProcs = mForegroundPackages.get(proc.info.packageName,
                     proc.info.uid);
             if (isForeground) {
@@ -24743,8 +19262,10 @@
         }
     }
 
-    private final ActivityRecord resumedAppLocked() {
-        ActivityRecord act = mStackSupervisor.getResumedActivityLocked();
+    // TODO(b/111541062): This method is only used for updating OOM adjustments. We need to update
+    // the logic there and in mBatteryStatsService to make them aware of multiple resumed activities
+    private ActivityRecord resumedAppLocked() {
+        final ActivityRecord act = mStackSupervisor.getTopResumedActivity();
         String pkg;
         int uid;
         if (act != null) {
@@ -24757,16 +19278,23 @@
         // Has the UID or resumed package name changed?
         if (uid != mCurResumedUid || (pkg != mCurResumedPackage
                 && (pkg == null || !pkg.equals(mCurResumedPackage)))) {
-            if (mCurResumedPackage != null) {
-                mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_FINISH,
-                        mCurResumedPackage, mCurResumedUid);
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                if (mCurResumedPackage != null) {
+                    mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_FINISH,
+                            mCurResumedPackage, mCurResumedUid);
+                }
+                mCurResumedPackage = pkg;
+                mCurResumedUid = uid;
+                if (mCurResumedPackage != null) {
+                    mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_START,
+                            mCurResumedPackage, mCurResumedUid);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
-            mCurResumedPackage = pkg;
-            mCurResumedUid = uid;
-            if (mCurResumedPackage != null) {
-                mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_START,
-                        mCurResumedPackage, mCurResumedUid);
-            }
+
         }
         return act;
     }
@@ -24781,7 +19309,8 @@
     @GuardedBy("this")
     final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
         final ActivityRecord TOP_ACT = resumedAppLocked();
-        final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
+        final ProcessRecord TOP_APP = TOP_ACT != null && TOP_ACT.hasProcess()
+                ? (ProcessRecord) TOP_ACT.app.mOwner : null;
         final boolean wasCached = app.cached;
 
         mAdjSeq++;
@@ -24804,20 +19333,24 @@
     }
 
     @GuardedBy("this")
-    final void updateOomAdjLocked() {
+    ProcessRecord getTopAppLocked() {
         final ActivityRecord TOP_ACT = resumedAppLocked();
-        final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
+        if (TOP_ACT != null && TOP_ACT.hasProcess()) {
+            return (ProcessRecord) TOP_ACT.app.mOwner;
+        } else {
+            return null;
+        }
+    }
+
+    @GuardedBy("this")
+    final void updateOomAdjLocked() {
+        mOomAdjProfiler.oomAdjStarted();
+        final ProcessRecord TOP_APP = getTopAppLocked();
         final long now = SystemClock.uptimeMillis();
         final long nowElapsed = SystemClock.elapsedRealtime();
         final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
         final int N = mLruProcesses.size();
 
-        if (false) {
-            RuntimeException e = new RuntimeException();
-            e.fillInStackTrace();
-            Slog.i(TAG, "updateOomAdj: top=" + TOP_ACT, e);
-        }
-
         // Reset state in all uid records.
         for (int i=mActiveUids.size()-1; i>=0; i--) {
             final UidRecord uidRec = mActiveUids.valueAt(i);
@@ -24826,7 +19359,9 @@
             uidRec.reset();
         }
 
-        mStackSupervisor.rankTaskLayersIfNeeded();
+        if (mStackSupervisor != null) {
+            mStackSupervisor.rankTaskLayersIfNeeded();
+        }
 
         mAdjSeq++;
         mNewNumServiceProcs = 0;
@@ -24891,7 +19426,7 @@
                 // to the process, do that now.
                 if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
                     switch (app.curProcState) {
-                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+                        case PROCESS_STATE_CACHED_ACTIVITY:
                         case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                         case ActivityManager.PROCESS_STATE_CACHED_RECENT:
                             // This process is a cached process holding activities...
@@ -24949,7 +19484,7 @@
         // - Continue retrying until no process was promoted.
         // - Iterate from least important to most important.
         int cycleCount = 0;
-        while (retryCycles) {
+        while (retryCycles && cycleCount < 10) {
             cycleCount++;
             retryCycles = false;
 
@@ -24964,12 +19499,14 @@
             for (int i=0; i<N; i++) {
                 ProcessRecord app = mLruProcesses.get(i);
                 if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
+
                     if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) {
                         retryCycles = true;
                     }
                 }
             }
         }
+
         for (int i=N-1; i>=0; i--) {
             ProcessRecord app = mLruProcesses.get(i);
             if (!app.killedByAm && app.thread != null) {
@@ -24977,7 +19514,7 @@
 
                 // Count the number of process types.
                 switch (app.curProcState) {
-                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+                    case PROCESS_STATE_CACHED_ACTIVITY:
                     case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                         mNumCachedHiddenProcs++;
                         numCached++;
@@ -25020,7 +19557,7 @@
                         if (uidRec.curProcState > app.curProcState) {
                             uidRec.curProcState = app.curProcState;
                         }
-                        if (app.foregroundServices) {
+                        if (app.hasForegroundServices()) {
                             uidRec.foregroundServices = true;
                         }
                     }
@@ -25033,6 +19570,8 @@
             }
         }
 
+        mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
+
         incrementProcStateSeqAndNotifyAppsLocked();
 
         mNumServiceProcs = mNewNumServiceProcs;
@@ -25071,10 +19610,12 @@
         }
         if (memFactor != mLastMemoryLevel) {
             EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel);
+            StatsLog.write(StatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
         }
         mLastMemoryLevel = memFactor;
         mLastNumProcesses = mLruProcesses.size();
-        boolean allChanged = mProcessStats.setMemFactorLocked(memFactor, !isSleepingLocked(), now);
+        boolean allChanged = mProcessStats.setMemFactorLocked(
+                memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
         final int trackerMemFactor = mProcessStats.getMemFactorLocked();
         if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
             if (mLowRamStartTime == 0) {
@@ -25095,8 +19636,8 @@
             }
             int factor = numTrimming/3;
             int minFactor = 2;
-            if (mHomeProcess != null) minFactor++;
-            if (mPreviousProcess != null) minFactor++;
+            if (mActivityTaskManager.mHomeProcess != null) minFactor++;
+            if (mActivityTaskManager.mPreviousProcess != null) minFactor++;
             if (factor < minFactor) factor = minFactor;
             int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
             for (int i=N-1; i>=0; i--) {
@@ -25114,19 +19655,6 @@
                             app.thread.scheduleTrimMemory(curLevel);
                         } catch (RemoteException e) {
                         }
-                        if (false) {
-                            // For now we won't do this; our memory trimming seems
-                            // to be good enough at this point that destroying
-                            // activities causes more harm than good.
-                            if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE
-                                    && app != mHomeProcess && app != mPreviousProcess) {
-                                // Need to do this on its own message because the stack may not
-                                // be in a consistent state at this point.
-                                // For these apps we will also finish their activities
-                                // to help them free memory.
-                                mStackSupervisor.scheduleDestroyAllActivities(app, "trim");
-                            }
-                        }
                     }
                     app.trimMemoryLevel = curLevel;
                     step++;
@@ -25321,6 +19849,7 @@
                 Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms");
             }
         }
+        mOomAdjProfiler.oomAdjEnded();
     }
 
     @Override
@@ -25672,12 +20201,13 @@
         }
     }
 
+    @GuardedBy("this")
     final void trimApplicationsLocked() {
         // First remove any unused application processes whose package
         // has been removed.
         for (int i=mRemovedProcesses.size()-1; i>=0; i--) {
             final ProcessRecord app = mRemovedProcesses.get(i);
-            if (app.activities.size() == 0 && app.recentTasks.size() == 0
+            if (!app.hasActivitiesOrRecentTasks()
                     && app.curReceivers.isEmpty() && app.services.size() == 0) {
                 Slog.i(
                     TAG, "Exiting empty application process "
@@ -25696,7 +20226,7 @@
                 cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
                 mRemovedProcesses.remove(i);
 
-                if (app.persistent) {
+                if (app.isPersistent()) {
                     addAppLocked(app.info, null, false, null /* ABI override */);
                 }
             }
@@ -25722,7 +20252,7 @@
 
             for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
                 ProcessRecord r = mLruProcesses.get(i);
-                if (r.thread != null && r.persistent) {
+                if (r.thread != null && r.isPersistent()) {
                     sendSignal(r.pid, sig);
                 }
             }
@@ -25745,7 +20275,7 @@
         }
     }
 
-    private void clearProfilerLocked() {
+    void clearProfilerLocked() {
         if (mProfilerInfo !=null && mProfilerInfo.profileFd != null) {
             try {
                 mProfilerInfo.profileFd.close();
@@ -26192,15 +20722,6 @@
     @VisibleForTesting
     final class LocalService extends ActivityManagerInternal {
         @Override
-        public void grantUriPermissionFromIntent(int callingUid, String targetPkg, Intent intent,
-                int targetUserId) {
-            synchronized (ActivityManagerService.this) {
-                ActivityManagerService.this.grantUriPermissionFromIntentLocked(callingUid,
-                        targetPkg, intent, null, targetUserId);
-            }
-        }
-
-        @Override
         public String checkContentProviderAccess(String authority, int userId) {
             return ActivityManagerService.this.checkContentProviderAccess(authority, userId);
         }
@@ -26218,74 +20739,6 @@
         }
 
         @Override
-        public SleepToken acquireSleepToken(String tag, int displayId) {
-            Preconditions.checkNotNull(tag);
-            return ActivityManagerService.this.acquireSleepToken(tag, displayId);
-        }
-
-        @Override
-        public ComponentName getHomeActivityForUser(int userId) {
-            synchronized (ActivityManagerService.this) {
-                ActivityRecord homeActivity = mStackSupervisor.getHomeActivityForUser(userId);
-                return homeActivity == null ? null : homeActivity.realActivity;
-            }
-        }
-
-        @Override
-        public void onUserRemoved(int userId) {
-            synchronized (ActivityManagerService.this) {
-                ActivityManagerService.this.onUserStoppedLocked(userId);
-            }
-            mBatteryStatsService.onUserRemoved(userId);
-            mUserController.onUserRemoved(userId);
-        }
-
-        @Override
-        public void onLocalVoiceInteractionStarted(IBinder activity,
-                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
-            synchronized (ActivityManagerService.this) {
-                ActivityManagerService.this.onLocalVoiceInteractionStartedLocked(activity,
-                        voiceSession, voiceInteractor);
-            }
-        }
-
-        @Override
-        public void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp) {
-            synchronized (ActivityManagerService.this) {
-                mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
-                        reasons, timestamp);
-            }
-        }
-
-        @Override
-        public void notifyAppTransitionFinished() {
-            synchronized (ActivityManagerService.this) {
-                mStackSupervisor.notifyAppTransitionDone();
-            }
-        }
-
-        @Override
-        public void notifyAppTransitionCancelled() {
-            synchronized (ActivityManagerService.this) {
-                mStackSupervisor.notifyAppTransitionDone();
-            }
-        }
-
-        @Override
-        public List<IBinder> getTopVisibleActivities() {
-            synchronized (ActivityManagerService.this) {
-                return mStackSupervisor.getTopVisibleActivities();
-            }
-        }
-
-        @Override
-        public void notifyDockedStackMinimizedChanged(boolean minimized) {
-            synchronized (ActivityManagerService.this) {
-                mStackSupervisor.setDockedStackMinimized(minimized);
-            }
-        }
-
-        @Override
         public void killForegroundAppsForUser(int userHandle) {
             synchronized (ActivityManagerService.this) {
                 final ArrayList<ProcessRecord> procs = new ArrayList<>();
@@ -26295,7 +20748,7 @@
                     final int NA = apps.size();
                     for (int ia = 0; ia < NA; ia++) {
                         final ProcessRecord app = apps.valueAt(ia);
-                        if (app.persistent) {
+                        if (app.isPersistent()) {
                             // We don't kill persistent processes.
                             continue;
                         }
@@ -26344,106 +20797,16 @@
         }
 
         @Override
-        public void updatePersistentConfigurationForUser(@NonNull Configuration values,
-                int userId) {
-            Preconditions.checkNotNull(values, "Configuration must not be null");
-            Preconditions.checkArgumentNonnegative(userId, "userId " + userId + " not supported");
-            synchronized (ActivityManagerService.this) {
-                updateConfigurationLocked(values, null, false, true, userId,
-                        false /* deferResume */);
-            }
-        }
-
-        @Override
-        public int startActivitiesAsPackage(String packageName, int userId, Intent[] intents,
-                Bundle bOptions) {
-            Preconditions.checkNotNull(intents, "intents");
-            final String[] resolvedTypes = new String[intents.length];
-
-            // UID of the package on user userId.
-            // "= 0" is needed because otherwise catch(RemoteException) would make it look like
-            // packageUid may not be initialized.
-            int packageUid = 0;
-            final long ident = Binder.clearCallingIdentity();
-
-            try {
-                for (int i = 0; i < intents.length; i++) {
-                    resolvedTypes[i] =
-                            intents[i].resolveTypeIfNeeded(mContext.getContentResolver());
-                }
-
-                packageUid = AppGlobals.getPackageManager().getPackageUid(
-                        packageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
-            } catch (RemoteException e) {
-                // Shouldn't happen.
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-
-            synchronized (ActivityManagerService.this) {
-                return mActivityStartController.startActivitiesInPackage(
-                        packageUid, packageName,
-                        intents, resolvedTypes, null /* resultTo */,
-                        SafeActivityOptions.fromBundle(bOptions), userId,
-                        false /* validateIncomingUser */);
-            }
-        }
-
-        @Override
-        public int startActivityAsUser(IApplicationThread caller, String callerPacakge,
-                Intent intent, Bundle options, int userId) {
-            return ActivityManagerService.this.startActivityAsUser(
-                    caller, callerPacakge, intent,
-                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options, userId,
-                    false /*validateIncomingUser*/);
-        }
-
-        @Override
         public int getUidProcessState(int uid) {
             return getUidState(uid);
         }
 
         @Override
-        public void notifyKeyguardFlagsChanged(@Nullable Runnable callback) {
-            synchronized (ActivityManagerService.this) {
-
-                // We might change the visibilities here, so prepare an empty app transition which
-                // might be overridden later if we actually change visibilities.
-                final boolean wasTransitionSet =
-                        mWindowManager.getPendingAppTransition() != TRANSIT_NONE;
-                if (!wasTransitionSet) {
-                    mWindowManager.prepareAppTransition(TRANSIT_NONE,
-                            false /* alwaysKeepCurrent */);
-                }
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-
-                // If there was a transition set already we don't want to interfere with it as we
-                // might be starting it too early.
-                if (!wasTransitionSet) {
-                    mWindowManager.executeAppTransition();
-                }
-            }
-            if (callback != null) {
-                callback.run();
-            }
-        }
-
-        @Override
         public boolean isSystemReady() {
             // no need to synchronize(this) just to read & return the value
             return mSystemReady;
         }
 
-        @Override
-        public void notifyKeyguardTrustedChanged() {
-            synchronized (ActivityManagerService.this) {
-                if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                }
-            }
-        }
-
         /**
          * Sets if the given pid has an overlay UI or not.
          *
@@ -26471,11 +20834,6 @@
             }
         }
 
-        @Override
-        public void setRunningRemoteAnimation(int pid, boolean runningRemoteAnimation) {
-            ActivityManagerService.this.setRunningRemoteAnimation(pid, runningRemoteAnimation);
-        }
-
         /**
          * Called after the network policy rules are updated by
          * {@link com.android.server.net.NetworkPolicyManagerService} for a specific {@param uid}
@@ -26526,29 +20884,6 @@
         }
 
         @Override
-        public void notifyActiveVoiceInteractionServiceChanged(ComponentName component) {
-            synchronized (ActivityManagerService.this) {
-                mActiveVoiceInteractionServiceComponent = component;
-            }
-        }
-
-        /**
-         * Called after virtual display Id is updated by
-         * {@link com.android.server.vr.Vr2dDisplay} with a specific
-         * {@param vrVr2dDisplayId}.
-         */
-        @Override
-        public void setVr2dDisplayId(int vr2dDisplayId) {
-            if (DEBUG_STACK) {
-                Slog.d(TAG, "setVr2dDisplayId called for: " +
-                        vr2dDisplayId);
-            }
-            synchronized (ActivityManagerService.this) {
-                mVr2dDisplayId = vr2dDisplayId;
-            }
-        }
-
-        @Override
         public void saveANRState(String reason) {
             synchronized (ActivityManagerService.this) {
                 final StringWriter sw = new StringWriter();
@@ -26558,7 +20893,7 @@
                     pw.println("  Reason: " + reason);
                 }
                 pw.println();
-                mActivityStartController.dump(pw, "  ", null);
+                mActivityTaskManager.getActivityStartController().dump(pw, "  ", null);
                 pw.println();
                 pw.println("-------------------------------------------------------------------------------");
                 dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
@@ -26579,72 +20914,11 @@
         }
 
         @Override
-        public void setFocusedActivity(IBinder token) {
-            synchronized (ActivityManagerService.this) {
-                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-                if (r == null) {
-                    throw new IllegalArgumentException(
-                            "setFocusedActivity: No activity record matching token=" + token);
-                }
-                if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
-                        r, "setFocusedActivity")) {
-                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
-                }
-            }
-        }
-
-        @Override
-        public void setAllowAppSwitches(@NonNull String type, int uid, int userId) {
-            synchronized (ActivityManagerService.this) {
-                if (mUserController.isUserRunning(userId, ActivityManager.FLAG_OR_STOPPED)) {
-                    ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(userId);
-                    if (types == null) {
-                        if (uid < 0) {
-                            return;
-                        }
-                        types = new ArrayMap<>();
-                        mAllowAppSwitchUids.put(userId, types);
-                    }
-                    if (uid < 0) {
-                        types.remove(type);
-                    } else {
-                        types.put(type, uid);
-                    }
-                }
-            }
-        }
-
-        @Override
         public boolean isRuntimeRestarted() {
             return mSystemServiceManager.isRuntimeRestarted();
         }
 
         @Override
-        public boolean hasRunningActivity(int uid, @Nullable String packageName) {
-            if (packageName == null) return false;
-
-            synchronized (ActivityManagerService.this) {
-                for (int i = 0; i < mLruProcesses.size(); i++) {
-                    final ProcessRecord processRecord = mLruProcesses.get(i);
-                    if (processRecord.uid == uid) {
-                        for (int j = 0; j < processRecord.activities.size(); j++) {
-                            final ActivityRecord activityRecord = processRecord.activities.get(j);
-                            if (packageName.equals(activityRecord.packageName)) {
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-            return false;
-        }
-
-        @Override
-        public void registerScreenObserver(ScreenObserver observer) {
-            mScreenObservers.add(observer);
-        }
-
-        @Override
         public boolean canStartMoreUsers() {
             return mUserController.canStartMoreUsers();
         }
@@ -26665,21 +20939,6 @@
         }
 
         @Override
-        public boolean isCallerRecents(int callingUid) {
-            return getRecentTasks().isCallerRecents(callingUid);
-        }
-
-        @Override
-        public boolean isRecentsComponentHomeActivity(int userId) {
-            return getRecentTasks().isRecentsComponentHomeActivity(userId);
-        }
-
-        @Override
-        public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
-            ActivityManagerService.this.cancelRecentsAnimation(restoreHomeStackPosition);
-        }
-
-        @Override
         public boolean isUidActive(int uid) {
             synchronized (ActivityManagerService.this) {
                 return isUidActiveLocked(uid);
@@ -26687,6 +20946,11 @@
         }
 
         @Override
+        public void setRunningRemoteAnimation(int pid, boolean runningRemoteAnimation) {
+            ActivityManagerService.this.setRunningRemoteAnimation(pid, runningRemoteAnimation);
+        }
+
+        @Override
         public List<ProcessMemoryState> getMemoryStateForProcesses() {
             List<ProcessMemoryState> processMemoryStates = new ArrayList<>();
             synchronized (mPidsSelfLocked) {
@@ -26714,8 +20978,151 @@
         }
 
         @Override
-        public void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
-            ActivityManagerService.this.enforceCallerIsRecentsOrHasPermission(permission, func);
+        public int handleIncomingUser(int callingPid, int callingUid, int userId,
+                boolean allowAll, int allowMode, String name, String callerPackage) {
+            return mUserController.handleIncomingUser(callingPid, callingUid, userId, allowAll,
+                    allowMode, name, callerPackage);
+        }
+
+        @Override
+        public void enforceCallingPermission(String permission, String func) {
+            ActivityManagerService.this.enforceCallingPermission(permission, func);
+        }
+
+        @Override
+        public int getCurrentUserId() {
+            return mUserController.getCurrentUserIdLU();
+        }
+
+        @Override
+        public boolean isUserRunning(int userId, int flags) {
+            // Holding am lock isn't required to call into user controller.
+            return mUserController.isUserRunning(userId, flags);
+        }
+
+        @Override
+        public void trimApplications() {
+            ActivityManagerService.this.trimApplications();
+        }
+
+        public int getPackageScreenCompatMode(ApplicationInfo ai) {
+            synchronized (ActivityManagerService.this) {
+                return mCompatModePackages.computeCompatModeLocked(ai);
+            }
+        }
+
+        public void setPackageScreenCompatMode(ApplicationInfo ai, int mode) {
+            synchronized (ActivityManagerService.this) {
+                mCompatModePackages.setPackageScreenCompatModeLocked(ai, mode);
+            }
+        }
+
+        public void closeSystemDialogs(String reason) {
+            ActivityManagerService.this.closeSystemDialogs(reason);
+        }
+
+        public void killProcessesForRemovedTask(ArrayList<Object> procsToKill) {
+            synchronized (ActivityManagerService.this) {
+                for (int i = 0; i < procsToKill.size(); i++) {
+                    final WindowProcessController wpc =
+                            (WindowProcessController) procsToKill.get(i);
+                    final ProcessRecord pr = (ProcessRecord) wpc.mOwner;
+                    if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                            && pr.curReceivers.isEmpty()) {
+                        pr.kill("remove task", true);
+                    } else {
+                        // We delay killing processes that are not in the background or running a
+                        // receiver.
+                        pr.waitingToKill = "remove task";
+                    }
+                }
+            }
+        }
+
+        @Override
+        public boolean hasRunningActivity(int uid, @Nullable String packageName) {
+            if (packageName == null) return false;
+
+            synchronized (ActivityManagerService.this) {
+                for (int i = 0; i < mLruProcesses.size(); i++) {
+                    final ProcessRecord pr = mLruProcesses.get(i);
+                    if (pr.uid != uid) {
+                        continue;
+                    }
+                    if (pr.getWindowProcessController().hasRunningActivity(packageName)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public void updateOomAdj() {
+            synchronized (ActivityManagerService.this) {
+                ActivityManagerService.this.updateOomAdjLocked();
+            }
+        }
+
+        @Override
+        public void sendForegroundProfileChanged(int userId) {
+            mUserController.sendForegroundProfileChanged(userId);
+        }
+
+        @Override
+        public boolean shouldConfirmCredentials(int userId) {
+            return mUserController.shouldConfirmCredentials(userId);
+        }
+
+        @Override
+        public int[] getCurrentProfileIds() {
+            return mUserController.getCurrentProfileIds();
+        }
+
+        @Override
+        public UserInfo getCurrentUser() {
+            return mUserController.getCurrentUser();
+        }
+
+        @Override
+        public void ensureNotSpecialUser(int userId) {
+            mUserController.ensureNotSpecialUser(userId);
+        }
+
+        @Override
+        public boolean isCurrentProfile(int userId) {
+            return mUserController.isCurrentProfile(userId);
+        }
+
+        @Override
+        public boolean hasStartedUserState(int userId) {
+            return mUserController.hasStartedUserState(userId);
+        }
+
+        @Override
+        public void finishUserSwitch(Object uss) {
+            mUserController.finishUserSwitch((UserState) uss);
+        }
+
+        @Override
+        public Intent getHomeIntent() {
+            synchronized (ActivityManagerService.this) {
+                return ActivityManagerService.this.getHomeIntent();
+            }
+        }
+
+        @Override
+        public void scheduleAppGcs() {
+            synchronized (ActivityManagerService.this) {
+                ActivityManagerService.this.scheduleAppGcsLocked();
+            }
+        }
+
+        @Override
+        public int getTaskIdForActivity(IBinder token, boolean onlyRoot) {
+            synchronized (ActivityManagerService.this) {
+                return ActivityManagerService.this.getTaskForActivity(token, onlyRoot);
+            }
         }
     }
 
@@ -26816,21 +21223,6 @@
     }
 
     /**
-     * Return the user id of the last resumed activity.
-     */
-    @Override
-    public @UserIdInt int getLastResumedActivityUserId() {
-        enforceCallingPermission(
-                permission.INTERACT_ACROSS_USERS_FULL, "getLastResumedActivityUserId()");
-        synchronized (this) {
-            if (mLastResumedActivity == null) {
-                return mUserController.getCurrentUserId();
-            }
-            return mLastResumedActivity.userId;
-        }
-    }
-
-    /**
      * Kill processes for the user with id userId and that depend on the package named packageName
      */
     @Override
@@ -26864,21 +21256,6 @@
     }
 
     @Override
-    public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
-            CharSequence message) throws RemoteException {
-        if (message != null) {
-            enforceCallingPermission(permission.SHOW_KEYGUARD_MESSAGE,
-                    "dismissKeyguard()");
-        }
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            mKeyguardController.dismissKeyguard(token, callback, message);
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-    }
-
-    @Override
     public int restartUserInBackground(final int userId) {
         return mUserController.restartUser(userId, /* foreground */ false);
     }
@@ -26999,85 +21376,141 @@
     }
 
     @Override
-    public void setShowWhenLocked(IBinder token, boolean showWhenLocked)
-            throws RemoteException {
-        synchronized (this) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
+    public void startDelegateShellPermissionIdentity(int delegateUid) {
+        if (UserHandle.getCallingAppId() != Process.SHELL_UID
+                && UserHandle.getCallingAppId() != Process.ROOT_UID) {
+            throw new SecurityException("Only the shell can delegate its permissions");
+        }
+
+        // We allow delegation only to one instrumentation started from the shell
+        synchronized (ActivityManagerService.this) {
+            // If there is a delegate it should be the same instance for app ops and permissions.
+            if (mAppOpsService.getAppOpsServiceDelegate()
+                    != getPackageManagerInternalLocked().getCheckPermissionDelegate()) {
+                throw new IllegalStateException("Bad shell delegate state");
+            }
+
+            // If the delegate is already set up for the target UID, nothing to do.
+            if (mAppOpsService.getAppOpsServiceDelegate() != null) {
+                if (!(mAppOpsService.getAppOpsServiceDelegate() instanceof ShellDelegate)) {
+                    throw new IllegalStateException("Bad shell delegate state");
+                }
+                if (((ShellDelegate) mAppOpsService.getAppOpsServiceDelegate())
+                        .getDelegateUid() != delegateUid) {
+                    throw new SecurityException("Shell can delegate permissions only "
+                            + "to one instrumentation at a time");
+                }
                 return;
             }
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                r.setShowWhenLocked(showWhenLocked);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
 
-    @Override
-    public void setTurnScreenOn(IBinder token, boolean turnScreenOn) throws RemoteException {
-        synchronized (this) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
+            final int instrCount = mActiveInstrumentation.size();
+            for (int i = 0; i < instrCount; i++) {
+                final ActiveInstrumentation instr = mActiveInstrumentation.get(i);
+                if (instr.mTargetInfo.uid != delegateUid) {
+                    continue;
+                }
+                // If instrumentation started from the shell the connection is not null
+                if (instr.mUiAutomationConnection == null) {
+                    throw new SecurityException("Shell can delegate its permissions" +
+                            " only to an instrumentation started from the shell");
+                }
+
+                // Hook them up...
+                final ShellDelegate shellDelegate = new ShellDelegate(
+                        instr.mTargetInfo.packageName, delegateUid);
+                mAppOpsService.setAppOpsServiceDelegate(shellDelegate);
+                getPackageManagerInternalLocked().setCheckPermissionDelegate(shellDelegate);
                 return;
             }
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                r.setTurnScreenOn(turnScreenOn);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
         }
     }
 
     @Override
-    public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition)
-            throws RemoteException {
-        enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
-                "registerRemoteAnimations");
-        definition.setCallingPid(Binder.getCallingPid());
-        synchronized (this) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return;
-            }
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                r.registerRemoteAnimations(definition);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
+    public void stopDelegateShellPermissionIdentity() {
+        if (UserHandle.getCallingAppId() != Process.SHELL_UID
+                && UserHandle.getCallingAppId() != Process.ROOT_UID) {
+            throw new SecurityException("Only the shell can delegate its permissions");
+        }
+        synchronized (ActivityManagerService.this) {
+            mAppOpsService.setAppOpsServiceDelegate(null);
+            getPackageManagerInternalLocked().setCheckPermissionDelegate(null);
         }
     }
 
-    @Override
-    public void registerRemoteAnimationForNextActivityStart(String packageName,
-            RemoteAnimationAdapter adapter) throws RemoteException {
-        enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
-                "registerRemoteAnimationForNextActivityStart");
-        adapter.setCallingPid(Binder.getCallingPid());
-        synchronized (this) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                mActivityStartController.registerRemoteAnimationForNextActivityStart(packageName,
-                        adapter);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
+    private class ShellDelegate implements CheckOpsDelegate, CheckPermissionDelegate {
+        private final String mTargetPackageName;
+        private final int mTargetUid;
 
-    /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
-    @Override
-    public void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) {
-        synchronized (this) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                mAppWarnings.alwaysShowUnsupportedCompileSdkWarning(activity);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
+        ShellDelegate(String targetPacakgeName, int targetUid) {
+            mTargetPackageName = targetPacakgeName;
+            mTargetUid = targetUid;
+        }
+
+        int getDelegateUid() {
+            return mTargetUid;
+        }
+
+        @Override
+        public int checkOperation(int code, int uid, String packageName,
+                TriFunction<Integer, Integer, String, Integer> superImpl) {
+            if (uid == mTargetUid) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    return superImpl.apply(code, Process.SHELL_UID,
+                            "com.android.shell");
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
             }
+            return superImpl.apply(code, uid, packageName);
+        }
+
+        @Override
+        public int checkAudioOperation(int code, int usage, int uid, String packageName,
+                QuadFunction<Integer, Integer, Integer, String, Integer> superImpl) {
+            if (uid == mTargetUid) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    return superImpl.apply(code, usage, Process.SHELL_UID,
+                            "com.android.shell");
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+            return superImpl.apply(code, usage, uid, packageName);
+        }
+
+        @Override
+        public int noteOperation(int code, int uid, String packageName,
+                TriFunction<Integer, Integer, String, Integer> superImpl) {
+            if (uid == mTargetUid) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    return mAppOpsService.noteProxyOperation(code, Process.SHELL_UID,
+                            "com.android.shell", uid, packageName);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+            return superImpl.apply(code, uid, packageName);
+        }
+
+        @Override
+        public int checkPermission(String permName, String pkgName, int userId,
+                TriFunction<String, String, Integer, Integer> superImpl) {
+            if (mTargetPackageName.equals(pkgName)) {
+                return superImpl.apply(permName, "com.android.shell", userId);
+            }
+            return superImpl.apply(permName, pkgName, userId);
+        }
+
+        @Override
+        public int checkUidPermission(String permName, int uid,
+                BiFunction<String, Integer, Integer> superImpl) {
+            if (uid == mTargetUid) {
+                return superImpl.apply(permName, Process.SHELL_UID);
+            }
+            return superImpl.apply(permName, uid);
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 2567697..081d6f9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -18,9 +18,11 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
 import android.app.AppGlobals;
 import android.app.IActivityController;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.app.IStopUserCallback;
 import android.app.IUidObserver;
 import android.app.KeyguardManager;
@@ -100,9 +102,9 @@
 import javax.microedition.khronos.egl.EGLDisplay;
 import javax.microedition.khronos.egl.EGLSurface;
 
-import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
-import static android.app.ActivityManager.RESIZE_MODE_USER;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
+import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.INVALID_DISPLAY;
@@ -115,6 +117,7 @@
 
     // IPC interface to activity manager -- don't need to do additional security checks.
     final IActivityManager mInterface;
+    final IActivityTaskManager mTaskInterface;
 
     // Internal service impl -- must perform security checks before touching.
     final ActivityManagerService mInternal;
@@ -147,6 +150,7 @@
 
     ActivityManagerShellCommand(ActivityManagerService service, boolean dumping) {
         mInterface = service;
+        mTaskInterface = service.mActivityTaskManager;
         mInternal = service;
         mPm = AppGlobals.getPackageManager();
         mDumping = dumping;
@@ -475,12 +479,12 @@
                 options.setLockTaskEnabled(true);
             }
             if (mWaitOption) {
-                result = mInterface.startActivityAndWait(null, null, intent, mimeType,
+                result = mTaskInterface.startActivityAndWait(null, null, intent, mimeType,
                         null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
                 res = result.result;
             } else {
-                res = mInterface.startActivityAsUser(null, null, intent, mimeType,
+                res = mTaskInterface.startActivityAsUser(null, null, intent, mimeType,
                         null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
             }
@@ -574,7 +578,7 @@
             }
             mRepeat--;
             if (mRepeat > 0) {
-                mInterface.unhandledBack();
+                mTaskInterface.unhandledBack();
             }
         } while (mRepeat > 0);
         return 0;
@@ -2182,7 +2186,7 @@
 
     int runSuppressResizeConfigChanges(PrintWriter pw) throws RemoteException {
         boolean suppress = Boolean.valueOf(getNextArgRequired());
-        mInterface.suppressResizeConfigChanges(suppress);
+        mTaskInterface.suppressResizeConfigChanges(suppress);
         return 0;
     }
 
@@ -2389,8 +2393,6 @@
     int runStack(PrintWriter pw) throws RemoteException {
         String op = getNextArgRequired();
         switch (op) {
-            case "start":
-                return runStackStart(pw);
             case "move-task":
                 return runStackMoveTask(pw);
             case "resize":
@@ -2449,32 +2451,7 @@
         int stackId = Integer.parseInt(stackIdStr);
         String displayIdStr = getNextArgRequired();
         int displayId = Integer.parseInt(displayIdStr);
-        mInterface.moveStackToDisplay(stackId, displayId);
-        return 0;
-    }
-
-    int runStackStart(PrintWriter pw) throws RemoteException {
-        String displayIdStr = getNextArgRequired();
-        int displayId = Integer.parseInt(displayIdStr);
-        Intent intent;
-        try {
-            intent = makeIntent(UserHandle.USER_CURRENT);
-        } catch (URISyntaxException e) {
-            throw new RuntimeException(e.getMessage(), e);
-        }
-
-        final int stackId = mInterface.createStackOnDisplay(displayId);
-        if (stackId != INVALID_STACK_ID) {
-            // TODO: Need proper support if this is used by test...
-//            container.startActivity(intent);
-//            ActivityOptions options = ActivityOptions.makeBasic();
-//            options.setLaunchDisplayId(displayId);
-//            options.setLaunchStackId(stackId);
-//            mInterface.startAct
-//            mInterface.startActivityAsUser(null, null, intent, mimeType,
-//                    null, null, 0, mStartFlags, profilerInfo,
-//                    options != null ? options.toBundle() : null, mUserId);
-        }
+        mTaskInterface.moveStackToDisplay(stackId, displayId);
         return 0;
     }
 
@@ -2494,7 +2471,7 @@
             return -1;
         }
 
-        mInterface.moveTaskToStack(taskId, stackId, toTop);
+        mTaskInterface.moveTaskToStack(taskId, stackId, toTop);
         return 0;
     }
 
@@ -2528,7 +2505,7 @@
     int resizeStackUnchecked(int stackId, Rect bounds, int delayMs, boolean animate)
             throws RemoteException {
         try {
-            mInterface.resizeStack(stackId, bounds, false, false, animate, -1);
+            mTaskInterface.resizeStack(stackId, bounds, false, false, animate, -1);
             Thread.sleep(delayMs);
         } catch (InterruptedException e) {
         }
@@ -2542,7 +2519,7 @@
             getErrPrintWriter().println("Error: invalid input bounds");
             return -1;
         }
-        mInterface.resizeDockedStack(bounds, taskBounds, null, null, null);
+        mTaskInterface.resizeDockedStack(bounds, taskBounds, null, null, null);
         return 0;
     }
 
@@ -2562,12 +2539,12 @@
         String positionStr = getNextArgRequired();
         int position = Integer.parseInt(positionStr);
 
-        mInterface.positionTaskInStack(taskId, stackId, position);
+        mTaskInterface.positionTaskInStack(taskId, stackId, position);
         return 0;
     }
 
     int runStackList(PrintWriter pw) throws RemoteException {
-        List<ActivityManager.StackInfo> stacks = mInterface.getAllStackInfos();
+        List<ActivityManager.StackInfo> stacks = mTaskInterface.getAllStackInfos();
         for (ActivityManager.StackInfo info : stacks) {
             pw.println(info);
         }
@@ -2577,7 +2554,7 @@
     int runStackInfo(PrintWriter pw) throws RemoteException {
         int windowingMode = Integer.parseInt(getNextArgRequired());
         int activityType = Integer.parseInt(getNextArgRequired());
-        ActivityManager.StackInfo info = mInterface.getStackInfo(windowingMode, activityType);
+        ActivityManager.StackInfo info = mTaskInterface.getStackInfo(windowingMode, activityType);
         pw.println(info);
         return 0;
     }
@@ -2585,7 +2562,7 @@
     int runStackRemove(PrintWriter pw) throws RemoteException {
         String stackIdStr = getNextArgRequired();
         int stackId = Integer.parseInt(stackIdStr);
-        mInterface.removeStack(stackId);
+        mTaskInterface.removeStack(stackId);
         return 0;
     }
 
@@ -2597,7 +2574,7 @@
             return -1;
         }
 
-        if (!mInterface.moveTopActivityToPinnedStack(stackId, bounds)) {
+        if (!mTaskInterface.moveTopActivityToPinnedStack(stackId, bounds)) {
             getErrPrintWriter().println("Didn't move top activity to pinned stack.");
             return -1;
         }
@@ -2643,12 +2620,12 @@
     int runTaskLock(PrintWriter pw) throws RemoteException {
         String taskIdStr = getNextArgRequired();
         if (taskIdStr.equals("stop")) {
-            mInterface.stopSystemLockTaskMode();
+            mTaskInterface.stopSystemLockTaskMode();
         } else {
             int taskId = Integer.parseInt(taskIdStr);
-            mInterface.startSystemLockTaskMode(taskId);
+            mTaskInterface.startSystemLockTaskMode(taskId);
         }
-        pw.println("Activity manager is " + (mInterface.isInLockTaskMode() ? "" : "not ") +
+        pw.println("Activity manager is " + (mTaskInterface.isInLockTaskMode() ? "" : "not ") +
                 "in lockTaskMode");
         return 0;
     }
@@ -2658,7 +2635,7 @@
         final int taskId = Integer.parseInt(taskIdStr);
         final String resizeableStr = getNextArgRequired();
         final int resizeableMode = Integer.parseInt(resizeableStr);
-        mInterface.setTaskResizeable(taskId, resizeableMode);
+        mTaskInterface.setTaskResizeable(taskId, resizeableMode);
         return 0;
     }
 
@@ -2677,7 +2654,7 @@
     void taskResize(int taskId, Rect bounds, int delay_ms, boolean pretendUserResize)
             throws RemoteException {
         final int resizeMode = pretendUserResize ? RESIZE_MODE_USER : RESIZE_MODE_SYSTEM;
-        mInterface.resizeTask(taskId, bounds, resizeMode);
+        mTaskInterface.resizeTask(taskId, bounds, resizeMode);
         try {
             Thread.sleep(delay_ms);
         } catch (InterruptedException e) {
@@ -2749,14 +2726,14 @@
     int runTaskFocus(PrintWriter pw) throws RemoteException {
         final int taskId = Integer.parseInt(getNextArgRequired());
         pw.println("Setting focus to task " + taskId);
-        mInterface.setFocusedTask(taskId);
+        mTaskInterface.setFocusedTask(taskId);
         return 0;
     }
 
     int runWrite(PrintWriter pw) {
         mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
                 "registerUidObserver()");
-        mInternal.getRecentTasks().flush();
+        mInternal.mActivityTaskManager.getRecentTasks().flush();
         pw.println("All tasks persisted.");
         return 0;
     }
@@ -2781,7 +2758,7 @@
         if (res == null) {
             return -1;
         }
-        pw.println(ActivityManager.supportsMultiWindow(mInternal.mContext));
+        pw.println(ActivityTaskManager.supportsMultiWindow(mInternal.mContext));
         return 0;
     }
 
@@ -2790,7 +2767,7 @@
         if (res == null) {
             return -1;
         }
-        pw.println(ActivityManager.supportsSplitScreenMultiWindow(mInternal.mContext));
+        pw.println(ActivityTaskManager.supportsSplitScreenMultiWindow(mInternal.mContext));
         return 0;
     }
 
@@ -2900,6 +2877,7 @@
             pw.println("          specified then run as the current user.");
             pw.println("      --windowingMode <WINDOWING_MODE>: The windowing mode to launch the activity into.");
             pw.println("      --activityType <ACTIVITY_TYPE>: The activity type to launch the activity as.");
+            pw.println("      --display <DISPLAY_ID>: The display to launch the activity into.");
             pw.println("  start-service [--user <USER_ID> | current] <INTENT>");
             pw.println("      Start a Service.  Options are:");
             pw.println("      --user <USER_ID> | current: Specify which user to run as; if not");
@@ -3064,8 +3042,6 @@
             pw.println("       move-stack <STACK_ID> <DISPLAY_ID>");
             pw.println("           Move <STACK_ID> from its current display to <DISPLAY_ID>.");
             pw.println("  stack [COMMAND] [...]: sub-commands for operating on activity stacks.");
-            pw.println("       start <DISPLAY_ID> <INTENT>");
-            pw.println("           Start a new activity on <DISPLAY_ID> using <INTENT>");
             pw.println("       move-task <TASK_ID> <STACK_ID> [true|false]");
             pw.println("           Move <TASK_ID> from its current stack to the top (true) or");
             pw.println("           bottom (false) of <STACK_ID>.");
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 47d0423..263c34f 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -2,13 +2,15 @@
 
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
+import static android.app.ActivityManager.processStateAmToProto;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_ACTIVITY_START;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_BIND_APPLICATION_DELAY_MS;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_CALLING_PACKAGE_NAME;
@@ -21,8 +23,48 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_REPORTED_DRAWN_MS;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_FLAGS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_FULLSCREEN;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_VISIBLE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_LAUNCH_MODE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_PROCESS_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_REAL_ACTIVITY;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_PACKAGE_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_UID;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_UID_HAS_ANY_VISIBLE_WINDOW;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CALLING_UID_PROC_STATE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CLASS_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_COMING_FROM_PENDING_INTENT;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_LAUNCH_TOKEN;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INTENT_ACTION;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_CUR_PROC_STATE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_CLIENT_ACTIVITIES;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_FOREGROUND_ACTIVITIES;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_FOREGROUND_SERVICES;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_OVERLAY_UI;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_HAS_TOP_UI;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_MILLIS_SINCE_FG_INTERACTION;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_MILLIS_SINCE_LAST_INTERACTION_EVENT;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_MILLIS_SINCE_UNIMPORTANT;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_PENDING_UI_CLEAN;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_PROCESS_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_PROC_STATE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_HAS_ANY_VISIBLE_WINDOW;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_PACKAGE_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_SHORT_COMPONENT_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID_PROC_STATE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_WHITELIST_TAG;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_REASON;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_FILTER;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH;
@@ -37,6 +79,7 @@
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.dex.ArtManagerInternal;
 import android.content.pm.dex.PackageOptimizationInfo;
@@ -133,7 +176,7 @@
 
     private final class WindowingModeTransitionInfoSnapshot {
         final private ApplicationInfo applicationInfo;
-        final private ProcessRecord processRecord;
+        final private WindowProcessController processRecord;
         final private String packageName;
         final private String launchedActivityName;
         final private String launchedActivityLaunchedFromPackage;
@@ -154,7 +197,7 @@
             launchedActivityLaunchToken = info.launchedActivity.info.launchToken;
             launchedActivityAppRecordRequiredAbi = info.launchedActivity.app == null
                     ? null
-                    : info.launchedActivity.app.requiredAbi;
+                    : info.launchedActivity.app.getRequiredAbi();
             reason = info.reason;
             startingWindowDelayMs = info.startingWindowDelayMs;
             bindApplicationDelayMs = info.bindApplicationDelayMs;
@@ -184,7 +227,11 @@
         mLastLogTimeSecs = now;
 
         mWindowState = WINDOW_STATE_INVALID;
-        ActivityStack stack = mSupervisor.getFocusedStack();
+        ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+        if (stack == null) {
+            return;
+        }
+
         if (stack.isActivityTypeAssistant()) {
             mWindowState = WINDOW_STATE_ASSISTANT;
             return;
@@ -234,7 +281,7 @@
      * @param launchedActivity the activity that is being launched
      */
     void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity) {
-        final ProcessRecord processRecord = findProcessForActivity(launchedActivity);
+        final WindowProcessController processRecord = findProcessForActivity(launchedActivity);
         final boolean processRunning = processRecord != null;
 
         // We consider this a "process switch" if the process of the activity that gets launched
@@ -242,25 +289,11 @@
         // of caches might be purged so the time until it produces the first frame is very
         // interesting.
         final boolean processSwitch = processRecord == null
-                || !hasStartedActivity(processRecord, launchedActivity);
+                || !processRecord.hasStartedActivity(launchedActivity);
 
         notifyActivityLaunched(resultCode, launchedActivity, processRunning, processSwitch);
     }
 
-    private boolean hasStartedActivity(ProcessRecord record, ActivityRecord launchedActivity) {
-        final ArrayList<ActivityRecord> activities = record.activities;
-        for (int i = activities.size() - 1; i >= 0; i--) {
-            final ActivityRecord activity = activities.get(i);
-            if (launchedActivity == activity) {
-                continue;
-            }
-            if (!activity.stopped) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     /**
      * Notifies the tracker the the activity is actually launching.
      *
@@ -407,7 +440,7 @@
     }
 
     private void checkVisibility(TaskRecord t, ActivityRecord r) {
-        synchronized (mSupervisor.mService) {
+        synchronized (mSupervisor.mService.mGlobalLock) {
 
             final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
                     r.getWindowingMode());
@@ -622,6 +655,95 @@
                 startupTimeMs);
     }
 
+    void logActivityStart(Intent intent, ProcessRecord callerApp, ActivityRecord r,
+            int callingUid, String callingPackage, int callingUidProcState,
+            boolean callingUidHasAnyVisibleWindow,
+            int realCallingUid, int realCallingUidProcState,
+            boolean realCallingUidHasAnyVisibleWindow,
+            int targetUid, String targetPackage, int targetUidProcState,
+            boolean targetUidHasAnyVisibleWindow, String targetWhitelistTag,
+            boolean comingFromPendingIntent) {
+
+        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long nowUptime = SystemClock.uptimeMillis();
+        final LogMaker builder = new LogMaker(ACTION_ACTIVITY_START);
+        builder.setTimestamp(System.currentTimeMillis());
+        builder.addTaggedData(FIELD_CALLING_UID, callingUid);
+        builder.addTaggedData(FIELD_CALLING_PACKAGE_NAME, callingPackage);
+        builder.addTaggedData(FIELD_CALLING_UID_PROC_STATE,
+                processStateAmToProto(callingUidProcState));
+        builder.addTaggedData(FIELD_CALLING_UID_HAS_ANY_VISIBLE_WINDOW,
+                callingUidHasAnyVisibleWindow ? 1 : 0);
+        builder.addTaggedData(FIELD_REAL_CALLING_UID, realCallingUid);
+        builder.addTaggedData(FIELD_REAL_CALLING_UID_PROC_STATE,
+                processStateAmToProto(realCallingUidProcState));
+        builder.addTaggedData(FIELD_REAL_CALLING_UID_HAS_ANY_VISIBLE_WINDOW,
+                realCallingUidHasAnyVisibleWindow ? 1 : 0);
+        builder.addTaggedData(FIELD_TARGET_UID, targetUid);
+        builder.addTaggedData(FIELD_TARGET_PACKAGE_NAME, targetPackage);
+        builder.addTaggedData(FIELD_TARGET_UID_PROC_STATE,
+                processStateAmToProto(targetUidProcState));
+        builder.addTaggedData(FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW,
+                targetUidHasAnyVisibleWindow ? 1 : 0);
+        builder.addTaggedData(FIELD_TARGET_WHITELIST_TAG, targetWhitelistTag);
+        builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName);
+        builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0);
+        builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
+        if (callerApp != null) {
+            builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.processName);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_CUR_PROC_STATE,
+                    processStateAmToProto(callerApp.curProcState));
+            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_CLIENT_ACTIVITIES,
+                    callerApp.hasClientActivities ? 1 : 0);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_FOREGROUND_SERVICES,
+                    callerApp.hasForegroundServices() ? 1 : 0);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_FOREGROUND_ACTIVITIES,
+                    callerApp.foregroundActivities ? 1 : 0);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_TOP_UI, callerApp.hasTopUi ? 1 : 0);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_OVERLAY_UI,
+                    callerApp.hasOverlayUi ? 1 : 0);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_PENDING_UI_CLEAN,
+                    callerApp.pendingUiClean ? 1 : 0);
+            if (callerApp.interactionEventTime != 0) {
+                builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_LAST_INTERACTION_EVENT,
+                        (nowElapsed - callerApp.interactionEventTime));
+            }
+            if (callerApp.fgInteractionTime != 0) {
+                builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_FG_INTERACTION,
+                        (nowElapsed - callerApp.fgInteractionTime));
+            }
+            if (callerApp.whenUnimportant != 0) {
+                builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_UNIMPORTANT,
+                        (nowUptime - callerApp.whenUnimportant));
+            }
+        }
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY, r.realActivity.toShortString());
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0);
+        if (r.lastVisibleTime != 0) {
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE,
+                    (nowUptime - r.lastVisibleTime));
+        }
+        if (r.resultTo != null) {
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME, r.resultTo.packageName);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME,
+                    r.resultTo.shortComponentName);
+        }
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0);
+        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD,
+                r.visibleIgnoringKeyguard ? 1 : 0);
+        if (r.lastLaunchTime != 0) {
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH,
+                    (nowUptime - r.lastLaunchTime));
+        }
+        mMetricsLogger.write(builder);
+    }
+
     private int getTransitionType(WindowingModeTransitionInfo info) {
         if (info.currentTransitionProcessRunning) {
             if (info.startResult == START_SUCCESS) {
@@ -641,7 +763,7 @@
             return;
         }
 
-        final int pid = info.processRecord.pid;
+        final int pid = info.processRecord.getPid();
         final int uid = info.applicationInfo.uid;
         final MemoryStat memoryStat = readMemoryStatFromFilesystem(uid, pid);
         if (memoryStat == null) {
@@ -661,10 +783,10 @@
                 memoryStat.swapInBytes);
     }
 
-    private ProcessRecord findProcessForActivity(ActivityRecord launchedActivity) {
+    private WindowProcessController findProcessForActivity(ActivityRecord launchedActivity) {
         return launchedActivity != null
-                ? mSupervisor.mService.mProcessNames.get(launchedActivity.processName,
-                        launchedActivity.appInfo.uid)
+                ? mSupervisor.mService.mProcessNames.get(
+                        launchedActivity.processName, launchedActivity.appInfo.uid)
                 : null;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 75f2723..67abedc 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,7 +17,7 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
 import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
 import static android.app.ActivityOptions.ANIM_CUSTOM;
@@ -186,6 +186,7 @@
 import com.android.server.AttributeCache;
 import com.android.server.AttributeCache.Entry;
 import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.uri.UriPermissionOwner;
 import com.android.server.wm.AppWindowContainerController;
 import com.android.server.wm.AppWindowContainerListener;
 import com.android.server.wm.ConfigurationContainer;
@@ -230,7 +231,7 @@
     private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
     static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
 
-    final ActivityManagerService service; // owner
+    final ActivityTaskManagerService service; // owner
     final IApplicationToken.Stub appToken; // window manager token
     AppWindowContainerController mWindowContainerController;
     final ActivityInfo info; // all about me
@@ -288,7 +289,7 @@
     AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity
     HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
     UriPermissionOwner uriPermissions; // current special URI access perms.
-    ProcessRecord app;      // if non-null, hosting application
+    WindowProcessController app;      // if non-null, hosting application
     private ActivityState mState;    // current state we are in
     Bundle  icicle;         // last saved activity state
     PersistableBundle persistentState; // last persistently saved activity state
@@ -338,6 +339,17 @@
     int mStartingWindowState = STARTING_WINDOW_NOT_SHOWN;
     boolean mTaskOverlay = false; // Task is always on-top of other activities in the task.
 
+    // This activity is not being relaunched, or being relaunched for a non-resize reason.
+    static final int RELAUNCH_REASON_NONE = 0;
+    // This activity is being relaunched due to windowing mode change.
+    static final int RELAUNCH_REASON_WINDOWING_MODE_RESIZE = 1;
+    // This activity is being relaunched due to a free-resize operation.
+    static final int RELAUNCH_REASON_FREE_RESIZE = 2;
+    // Marking the reason why this activity is being relaunched. Mainly used to track that this
+    // activity is being relaunched to fulfill a resize request due to compatibility issues, e.g. in
+    // pre-NYC apps that don't have a sense of being resized.
+    int mRelaunchReason = RELAUNCH_REASON_NONE;
+
     TaskDescription taskDescription; // the recents information for this activity
     boolean mLaunchTaskBehind; // this activity is actively being launched with
         // ActivityOptions.setLaunchTaskBehind, will be cleared once launch is completed.
@@ -622,7 +634,7 @@
     }
 
     private void scheduleActivityMovedToDisplay(int displayId, Configuration config) {
-        if (app == null || app.thread == null) {
+        if (!attachedToProcess()) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.w(TAG,
                     "Can't report activity moved to display - client not running, activityRecord="
                             + this + ", displayId=" + displayId);
@@ -633,7 +645,7 @@
                     "Reporting activity moved to display" + ", activityRecord=" + this
                             + ", displayId=" + displayId + ", config=" + config);
 
-            service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     MoveToDisplayItem.obtain(displayId, config));
         } catch (RemoteException e) {
             // If process died, whatever.
@@ -641,7 +653,7 @@
     }
 
     private void scheduleConfigurationChanged(Configuration config) {
-        if (app == null || app.thread == null) {
+        if (!attachedToProcess()) {
             if (DEBUG_CONFIGURATION) Slog.w(TAG,
                     "Can't report activity configuration update - client not running"
                             + ", activityRecord=" + this);
@@ -651,7 +663,7 @@
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: "
                     + config);
 
-            service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     ActivityConfigurationChangeItem.obtain(config));
         } catch (RemoteException e) {
             // If process died, whatever.
@@ -659,7 +671,7 @@
     }
 
     void updateMultiWindowMode() {
-        if (task == null || task.getStack() == null || app == null || app.thread == null) {
+        if (task == null || task.getStack() == null || !attachedToProcess()) {
             return;
         }
 
@@ -678,16 +690,15 @@
 
     private void scheduleMultiWindowModeChanged(Configuration overrideConfig) {
         try {
-            service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
-                    MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode,
-                            overrideConfig));
+            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                    MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode, overrideConfig));
         } catch (Exception e) {
             // If process died, I don't care.
         }
     }
 
     void updatePictureInPictureMode(Rect targetStackBounds, boolean forceUpdate) {
-        if (task == null || task.getStack() == null || app == null || app.thread == null) {
+        if (task == null || task.getStack() == null || !attachedToProcess()) {
             return;
         }
 
@@ -706,7 +717,7 @@
 
     private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
         try {
-            service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     PipModeChangeItem.obtain(mLastReportedPictureInPictureMode,
                             overrideConfig));
         } catch (Exception e) {
@@ -847,7 +858,7 @@
         }
     }
 
-    ActivityRecord(ActivityManagerService _service, ProcessRecord _caller, int _launchedFromPid,
+    ActivityRecord(ActivityTaskManagerService _service, ProcessRecord _caller, int _launchedFromPid,
             int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
             ActivityInfo aInfo, Configuration _configuration,
             ActivityRecord _resultTo, String _resultWho, int _reqCode,
@@ -985,7 +996,7 @@
         }
     }
 
-    void setProcess(ProcessRecord proc) {
+    void setProcess(WindowProcessController proc) {
         app = proc;
         final ActivityRecord root = task != null ? task.getRootActivity() : null;
         if (root == this) {
@@ -993,6 +1004,14 @@
         }
     }
 
+    boolean hasProcess() {
+        return app != null;
+    }
+
+    boolean attachedToProcess() {
+        return hasProcess() && app.hasThread();
+    }
+
     AppWindowContainerController getWindowContainerController() {
         return mWindowContainerController;
     }
@@ -1017,7 +1036,7 @@
                 (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
                 task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
                 appInfo.targetSdkVersion, mRotationAnimationHint,
-                ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
+                ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
 
         task.addActivityToTop(this);
 
@@ -1111,7 +1130,8 @@
      * @return whether the given package name can launch an assist activity.
      */
     private boolean canLaunchAssistActivity(String packageName) {
-        final ComponentName assistComponent = service.mActiveVoiceInteractionServiceComponent;
+        final ComponentName assistComponent =
+                service.mActiveVoiceInteractionServiceComponent;
         if (assistComponent != null) {
             return assistComponent.getPackageName().equals(packageName);
         }
@@ -1131,8 +1151,8 @@
                 // We only allow home activities to be resizeable if they explicitly requested it.
                 info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
             }
-        } else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME) ||
-                service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
+        } else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME)
+                || service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
             activityType = ACTIVITY_TYPE_RECENTS;
         } else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
                 && canLaunchAssistActivity(launchedFromPackage)) {
@@ -1293,7 +1313,8 @@
         }
 
         boolean isKeyguardLocked = service.isKeyguardLocked();
-        boolean isCurrentAppLocked = service.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+        boolean isCurrentAppLocked =
+                service.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
         final ActivityDisplay display = getDisplay();
         boolean hasPinnedStack = display != null && display.hasPinnedStack();
         // Don't return early if !isNotLocked, since we want to throw an exception if the activity
@@ -1334,13 +1355,8 @@
      * @return Whether AppOps allows this package to enter picture-in-picture.
      */
     private boolean checkEnterPictureInPictureAppOpsState() {
-        try {
-            return service.getAppOpsService().checkOperation(OP_PICTURE_IN_PICTURE,
-                    appInfo.uid, packageName) == MODE_ALLOWED;
-        } catch (RemoteException e) {
-            // Local call
-        }
-        return false;
+        return service.getAppOpsService().checkOperation(
+                OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) == MODE_ALLOWED;
     }
 
     boolean isAlwaysFocusable() {
@@ -1366,13 +1382,13 @@
         }
 
         if (service != null) {
-            service.mTaskChangeNotificationController.notifyTaskStackChanged();
+            service.getTaskChangeNotificationController().notifyTaskStackChanged();
         }
     }
 
     UriPermissionOwner getUriPermissionsLocked() {
         if (uriPermissions == null) {
-            uriPermissions = new UriPermissionOwner(service, this);
+            uriPermissions = new UriPermissionOwner(service.mUgmInternal, this);
         }
         return uriPermissions;
     }
@@ -1424,7 +1440,7 @@
      */
     final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {
         // The activity now gets access to the data associated with this Intent.
-        service.grantUriPermissionFromIntentLocked(callingUid, packageName,
+        service.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
                 intent, getUriPermissionsLocked(), userId);
         final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
         boolean unsent = true;
@@ -1434,13 +1450,13 @@
         // - It is currently resumed or paused. i.e. it is currently visible to the user and we want
         //   the user to see the visual effects caused by the intent delivery now.
         // - The device is sleeping and it is the top activity behind the lock screen (b/6700897).
-        if ((mState == RESUMED || mState == PAUSED
-                || isTopActivityWhileSleeping) && app != null && app.thread != null) {
+        if ((mState == RESUMED || mState == PAUSED || isTopActivityWhileSleeping)
+                && attachedToProcess()) {
             try {
                 ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
                 ar.add(rintent);
-                service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
-                        NewIntentItem.obtain(ar, mState == PAUSED));
+                service.getLifecycleManager().scheduleTransaction(
+                        app.getThread(), appToken, NewIntentItem.obtain(ar, mState == PAUSED));
                 unsent = false;
             } catch (RemoteException e) {
                 Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
@@ -1584,7 +1600,7 @@
 
     void removeUriPermissionsLocked() {
         if (uriPermissions != null) {
-            uriPermissions.removeUriPermissionsLocked();
+            uriPermissions.removeUriPermissions();
             uriPermissions = null;
         }
     }
@@ -1743,7 +1759,7 @@
             }
             setVisible(true);
             sleeping = false;
-            app.pendingUiClean = true;
+            app.setPendingUiClean(true);
             if (reportToClient) {
                 makeClientVisible();
             } else {
@@ -1763,13 +1779,13 @@
     void makeClientVisible() {
         mClientVisibilityDeferred = false;
         try {
-            service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     WindowVisibilityItem.obtain(true /* showWindow */));
             if (shouldPauseWhenBecomingVisible()) {
                 // An activity must be in the {@link PAUSING} state for the system to validate
                 // the move to {@link PAUSED}.
                 setState(PAUSING, "makeVisibleIfNeeded");
-                service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+                service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                         PauseActivityItem.obtain(finishing, false /* userLeaving */,
                                 configChangeFlags, false /* dontReport */));
             }
@@ -1788,7 +1804,7 @@
         // considers the resumed activity, as normal means will bring the activity from STOPPED
         // to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
         if (!isState(STOPPED, STOPPING) || getStack().mTranslucentActivityWaiting != null
-                || mStackSupervisor.getResumedActivityLocked() == this) {
+                || isResumedActivityOnDisplay()) {
             return false;
         }
 
@@ -1815,7 +1831,7 @@
         stopFreezingScreenLocked(false);
         try {
             if (returningOptions != null) {
-                app.thread.scheduleOnNewActivityOptions(appToken, returningOptions.toBundle());
+                app.getThread().scheduleOnNewActivityOptions(appToken, returningOptions.toBundle());
             }
         } catch(RemoteException e) {
         }
@@ -1849,8 +1865,8 @@
         stopped = false;
 
         if (isActivityTypeHome()) {
-            ProcessRecord app = task.mActivities.get(0).app;
-            if (app != null && app != service.mHomeProcess) {
+            WindowProcessController app = task.mActivities.get(0).app;
+            if (hasProcess() && app != service.mHomeProcess) {
                 service.mHomeProcess = app;
             }
         }
@@ -1872,8 +1888,8 @@
         // Mark the point when the activity is resuming
         // TODO: To be more accurate, the mark should be before the onCreate,
         //       not after the onResume. But for subsequent starts, onResume is fine.
-        if (app != null) {
-            cpuTimeAtResume = service.mProcessCpuTracker.getCpuTimeForPid(app.pid);
+        if (hasProcess()) {
+            cpuTimeAtResume = app.getCpuTime();
         } else {
             cpuTimeAtResume = 0; // Couldn't get the cpu time of process
         }
@@ -1925,7 +1941,7 @@
             } else {
                 if (deferRelaunchUntilPaused) {
                     stack.destroyActivityLocked(this, true /* removeFromApp */, "stop-config");
-                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
                 } else {
                     mStackSupervisor.updatePreviousProcessLocked(this);
                 }
@@ -1969,15 +1985,15 @@
 
     // IApplicationToken
 
-    public boolean mayFreezeScreenLocked(ProcessRecord app) {
+    public boolean mayFreezeScreenLocked(WindowProcessController app) {
         // Only freeze the screen if this activity is currently attached to
         // an application, and that application is not blocked or unresponding.
         // In any other case, we can't count on getting the screen unfrozen,
         // so it is best to leave as-is.
-        return app != null && !app.crashing && !app.notResponding;
+        return hasProcess() && !app.isCrashing() && !app.isNotResponding();
     }
 
-    public void startFreezingScreenLocked(ProcessRecord app, int configChanges) {
+    public void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
         if (mayFreezeScreenLocked(app)) {
             mWindowContainerController.startFreezingScreen(configChanges);
         }
@@ -2066,7 +2082,7 @@
 
     @Override
     public void onStartingWindowDrawn(long timestamp) {
-        synchronized (service) {
+        synchronized (service.mGlobalLock) {
             mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
                     getWindowingMode(), timestamp);
         }
@@ -2074,7 +2090,7 @@
 
     @Override
     public void onWindowsDrawn(long timestamp) {
-        synchronized (service) {
+        synchronized (service.mGlobalLock) {
             mStackSupervisor.getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(),
                     timestamp);
             if (displayStartTime != 0) {
@@ -2091,7 +2107,7 @@
 
     @Override
     public void onWindowsVisible() {
-        synchronized (service) {
+        synchronized (service.mGlobalLock) {
             mStackSupervisor.reportActivityVisibleLocked(this);
             if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this);
             if (!nowVisible) {
@@ -2125,7 +2141,7 @@
 
     @Override
     public void onWindowsGone() {
-        synchronized (service) {
+        synchronized (service.mGlobalLock) {
             if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this);
             nowVisible = false;
         }
@@ -2134,13 +2150,13 @@
     @Override
     public boolean keyDispatchingTimedOut(String reason, int windowPid) {
         ActivityRecord anrActivity;
-        ProcessRecord anrApp;
+        WindowProcessController anrApp;
         boolean windowFromSameProcessAsActivity;
-        synchronized (service) {
+        synchronized (service.mGlobalLock) {
             anrActivity = getWaitingHistoryRecordLocked();
             anrApp = app;
             windowFromSameProcessAsActivity =
-                    app == null || app.pid == windowPid || windowPid == -1;
+                    !hasProcess() || app.getPid() == windowPid || windowPid == -1;
         }
         if (windowFromSameProcessAsActivity) {
             return service.inputDispatchingTimedOut(anrApp, anrActivity, this, false, reason);
@@ -2156,7 +2172,7 @@
         // another activity to start or has stopped, then the key dispatching
         // timeout should not be caused by this.
         if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this) || stopped) {
-            final ActivityStack stack = mStackSupervisor.getFocusedStack();
+            final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
             // Try to use the one which is closest to top.
             ActivityRecord r = stack.getResumedActivity();
             if (r == null) {
@@ -2180,7 +2196,7 @@
 
         return (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0
                 || (mStackSupervisor.isCurrentProfileLocked(userId)
-                && service.mUserController.isUserRunning(userId, 0 /* flags */));
+                && service.mAmInternal.isUserRunning(userId, 0 /* flags */));
     }
 
     /**
@@ -2199,9 +2215,9 @@
         if (!force && sleeping == _sleeping) {
             return;
         }
-        if (app != null && app.thread != null) {
+        if (attachedToProcess()) {
             try {
-                app.thread.scheduleSleeping(appToken, _sleeping);
+                app.getThread().scheduleSleeping(appToken, _sleeping);
                 if (_sleeping && !mStackSupervisor.mGoingToSleepActivities.contains(this)) {
                     mStackSupervisor.mGoingToSleepActivities.add(this);
                 }
@@ -2250,7 +2266,7 @@
     }
 
     final boolean isDestroyable() {
-        if (finishing || app == null) {
+        if (finishing || !hasProcess()) {
             // This would be redundant.
             return false;
         }
@@ -2311,7 +2327,7 @@
         }
 
         final CompatibilityInfo compatInfo =
-                service.compatibilityInfoForPackageLocked(info.applicationInfo);
+                service.mAm.compatibilityInfoForPackageLocked(info.applicationInfo);
         final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
                 compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                 prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
@@ -2346,10 +2362,10 @@
             frozenBeforeDestroy = true;
             if (!service.updateDisplayOverrideConfigurationLocked(config, this,
                     false /* deferResume */, displayId)) {
-                mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
             }
         }
-        service.mTaskChangeNotificationController.notifyActivityRequestedOrientationChanged(
+        service.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
                 task.taskId, requestedOrientation);
     }
 
@@ -2589,7 +2605,7 @@
 
         // If the activity isn't currently running, just leave the new configuration and it will
         // pick that up next time it starts.
-        if (app == null || app.thread == null) {
+        if (!attachedToProcess()) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                     "Configuration doesn't matter not running " + this);
             stopFreezingScreenLocked(false);
@@ -2610,7 +2626,16 @@
             startFreezingScreenLocked(app, globalChanges);
             forceNewConfig = false;
             preserveWindow &= isResizeOnlyChange(changes);
-            if (app == null || app.thread == null) {
+            final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
+            if (hasResizeChange) {
+                final boolean isDragResizing =
+                        getTask().getWindowContainerController().isDragResizing();
+                mRelaunchReason = isDragResizing ? RELAUNCH_REASON_FREE_RESIZE
+                        : RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+            } else {
+                mRelaunchReason = RELAUNCH_REASON_NONE;
+            }
+            if (!attachedToProcess()) {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                         "Config is destroying non-running " + this);
                 stack.destroyActivityLocked(this, true, "config");
@@ -2731,6 +2756,11 @@
                 | CONFIG_SCREEN_LAYOUT)) == 0;
     }
 
+    private static boolean hasResizeChange(int change) {
+        return (change & (CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_ORIENTATION
+                | CONFIG_SCREEN_LAYOUT)) != 0;
+    }
+
     void relaunchActivityLocked(boolean andResume, boolean preserveWindow) {
         if (service.mSuppressResizeConfigChanges && preserveWindow) {
             configChangeFlags = 0;
@@ -2770,7 +2800,7 @@
             } else {
                 lifecycleItem = PauseActivityItem.obtain();
             }
-            final ClientTransaction transaction = ClientTransaction.obtain(app.thread, appToken);
+            final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), appToken);
             transaction.addCallback(callbackItem);
             transaction.setLifecycleStateRequest(lifecycleItem);
             service.getLifecycleManager().scheduleTransaction(transaction);
@@ -2788,9 +2818,11 @@
             results = null;
             newIntents = null;
             service.getAppWarningsLocked().onResumeActivity(this);
-            service.showAskCompatModeDialogLocked(this);
         } else {
-            service.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
+            final ActivityStack stack = getStack();
+            if (stack != null) {
+                stack.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
+            }
             setState(PAUSED, "relaunchActivityLocked");
         }
 
@@ -2800,11 +2832,11 @@
     }
 
     private boolean isProcessRunning() {
-        ProcessRecord proc = app;
+        WindowProcessController proc = app;
         if (proc == null) {
             proc = service.mProcessNames.get(processName, info.applicationInfo.uid);
         }
-        return proc != null && proc.thread != null;
+        return proc != null && proc.hasThread();
     }
 
     /**
@@ -2926,7 +2958,7 @@
             throw new XmlPullParserException("restoreActivity error intent=" + intent);
         }
 
-        final ActivityManagerService service = stackSupervisor.mService;
+        final ActivityTaskManagerService service = stackSupervisor.mService;
         final ActivityInfo aInfo = stackSupervisor.resolveActivity(intent, resolvedType, 0, null,
                 userId, Binder.getCallingUid());
         if (aInfo == null) {
@@ -2997,10 +3029,30 @@
         return mStackSupervisor.topRunningActivityLocked() == this;
     }
 
+    /**
+     * @return {@code true} if this is the resumed activity on its current display, {@code false}
+     * otherwise.
+     */
+    boolean isResumedActivityOnDisplay() {
+        final ActivityDisplay display = getDisplay();
+        return display != null && this == display.getResumedActivity();
+    }
+
     void registerRemoteAnimations(RemoteAnimationDefinition definition) {
         mWindowContainerController.registerRemoteAnimations(definition);
     }
 
+    static String relaunchReasonToString(int relaunchReason) {
+        switch (relaunchReason) {
+            case RELAUNCH_REASON_WINDOWING_MODE_RESIZE:
+                return "window_resize";
+            case RELAUNCH_REASON_FREE_RESIZE:
+                return "free_resize";
+            default:
+                return null;
+        }
+    }
+
     @Override
     public String toString() {
         if (stringName != null) {
@@ -3033,8 +3085,8 @@
         proto.write(STATE, mState.toString());
         proto.write(VISIBLE, visible);
         proto.write(FRONT_OF_TASK, frontOfTask);
-        if (app != null) {
-            proto.write(PROC_ID, app.pid);
+        if (hasProcess()) {
+            proto.write(PROC_ID, app.getPid());
         }
         proto.write(TRANSLUCENT, !fullscreen);
         proto.end(token);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index cc7a230..fbf2855 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -72,6 +72,8 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
 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.ActivityRecord.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.am.ActivityRecord.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
 import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
@@ -271,7 +273,7 @@
     // the input bounds right or bottom side minus the width or height divided by this value.
     private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3;
 
-    final ActivityManagerService mService;
+    final ActivityTaskManagerService mService;
     private final WindowManagerService mWindowManager;
     T mWindowContainerController;
 
@@ -367,9 +369,9 @@
     static final int TRANSLUCENT_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
 
     private static class ScheduleDestroyArgs {
-        final ProcessRecord mOwner;
+        final WindowProcessController mOwner;
         final String mReason;
-        ScheduleDestroyArgs(ProcessRecord owner, String reason) {
+        ScheduleDestroyArgs(WindowProcessController owner, String reason) {
             mOwner = owner;
             mReason = reason;
         }
@@ -391,8 +393,8 @@
                     // We don't at this point know if the activity is fullscreen,
                     // so we need to be conservative and assume it isn't.
                     Slog.w(TAG, "Activity pause timeout for " + r);
-                    synchronized (mService) {
-                        if (r.app != null) {
+                    synchronized (mService.mGlobalLock) {
+                        if (r.hasProcess()) {
                             mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
                         }
                         activityPausedLocked(r.appToken, true);
@@ -400,7 +402,7 @@
                 } break;
                 case LAUNCH_TICK_MSG: {
                     ActivityRecord r = (ActivityRecord)msg.obj;
-                    synchronized (mService) {
+                    synchronized (mService.mGlobalLock) {
                         if (r.continueLaunchTickingLocked()) {
                             mService.logAppTooSlow(r.app, r.launchTickTime, "launching " + r);
                         }
@@ -411,7 +413,7 @@
                     // We don't at this point know if the activity is fullscreen,
                     // so we need to be conservative and assume it isn't.
                     Slog.w(TAG, "Activity destroy timeout for " + r);
-                    synchronized (mService) {
+                    synchronized (mService.mGlobalLock) {
                         activityDestroyedLocked(r != null ? r.appToken : null, "destroyTimeout");
                     }
                 } break;
@@ -420,7 +422,7 @@
                     // We don't at this point know if the activity is fullscreen,
                     // so we need to be conservative and assume it isn't.
                     Slog.w(TAG, "Activity stop timeout for " + r);
-                    synchronized (mService) {
+                    synchronized (mService.mGlobalLock) {
                         if (r.isInHistory()) {
                             r.activityStoppedLocked(null /* icicle */,
                                     null /* persistentState */, null /* description */);
@@ -429,12 +431,12 @@
                 } break;
                 case DESTROY_ACTIVITIES_MSG: {
                     ScheduleDestroyArgs args = (ScheduleDestroyArgs)msg.obj;
-                    synchronized (mService) {
+                    synchronized (mService.mGlobalLock) {
                         destroyActivitiesLocked(args.mOwner, args.mReason);
                     }
                 } break;
                 case TRANSLUCENT_TIMEOUT_MSG: {
-                    synchronized (mService) {
+                    synchronized (mService.mGlobalLock) {
                         notifyActivityDrawnLocked(null);
                     }
                 } break;
@@ -454,10 +456,10 @@
             int windowingMode, int activityType, boolean onTop) {
         mStackSupervisor = supervisor;
         mService = supervisor.mService;
-        mHandler = new ActivityStackHandler(mService.mHandler.getLooper());
+        mHandler = new ActivityStackHandler(supervisor.mLooper);
         mWindowManager = mService.mWindowManager;
         mStackId = stackId;
-        mCurrentUser = mService.mUserController.getCurrentUserId();
+        mCurrentUser = mService.mAmInternal.getCurrentUserId();
         mTmpRect2.setEmpty();
         // Set display id before setting activity and window type to make sure it won't affect
         // stacks on a wrong display.
@@ -495,7 +497,10 @@
             if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
                     + reason);
             setResumedActivity(record, reason + " - onActivityStateChanged");
-            mService.setResumedActivityUncheckLocked(record, reason);
+            if (record == mStackSupervisor.getTopResumedActivity()) {
+                // TODO(b/111361570): Support multiple focused apps in WM
+                mService.setResumedActivityUncheckLocked(record, reason);
+            }
             mStackSupervisor.mRecentTasks.add(record.getTask());
         }
     }
@@ -503,11 +508,21 @@
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         final int prevWindowingMode = getWindowingMode();
+        final boolean prevIsAlwaysOnTop = isAlwaysOnTop();
         super.onConfigurationChanged(newParentConfig);
         final ActivityDisplay display = getDisplay();
-        if (display != null && prevWindowingMode != getWindowingMode()) {
+        if (display == null) {
+          return;
+        }
+        if (prevWindowingMode != getWindowingMode()) {
             display.onStackWindowingModeChanged(this);
         }
+        if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
+            // Since always on top is only on when the stack is freeform or pinned, the state
+            // can be toggled when the windowing mode changes. We must make sure the stack is
+            // placed properly when always on top state changes.
+            display.positionChildAtTop(this, false /* includingParents */);
+        }
     }
 
     @Override
@@ -525,15 +540,17 @@
         final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
         mTmpOptions.setLaunchWindowingMode(preferredWindowingMode);
 
+        int windowingMode = preferredWindowingMode;
         // Need to make sure windowing mode is supported. If we in the process of creating the stack
         // no need to resolve the windowing mode again as it is already resolved to the right mode.
-        int windowingMode = creating
-                ? preferredWindowingMode
-                : display.resolveWindowingMode(
-                        null /* ActivityRecord */, mTmpOptions, topTask, getActivityType());
-        if (splitScreenStack == this && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
-            // Resolution to split-screen secondary for the primary split-screen stack means we want
-            // to go fullscreen.
+        if (!creating) {
+            windowingMode = display.validateWindowingMode(windowingMode,
+                    null /* ActivityRecord */, topTask, getActivityType());
+        }
+        if (splitScreenStack == this
+                && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+            // Resolution to split-screen secondary for the primary split-screen stack means
+            // we want to go fullscreen.
             windowingMode = WINDOWING_MODE_FULLSCREEN;
         }
 
@@ -552,7 +569,7 @@
                 // Looks like we can't launch in split screen mode or the stack we are launching
                 // doesn't support split-screen mode, go ahead an dismiss split-screen and display a
                 // warning toast about it.
-                mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
+                mService.getTaskChangeNotificationController().notifyActivityDismissingDockedStack();
                 display.getSplitScreenPrimaryStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN,
                         false /* animate */, false /* showRecents */,
                         false /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */);
@@ -573,7 +590,7 @@
             // Inform the user that they are starting an app that may not work correctly in
             // multi-window mode.
             final String packageName = topActivity.appInfo.packageName;
-            mService.mTaskChangeNotificationController.notifyActivityForcedResizable(
+            mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
                     topTask.taskId, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN, packageName);
         }
 
@@ -583,6 +600,9 @@
                 mStackSupervisor.mNoAnimActivities.add(topActivity);
             }
             super.setWindowingMode(windowingMode);
+            // setWindowingMode triggers an onConfigurationChanged cascade which can result in a
+            // different resolved windowing mode (usually when preferredWindowingMode is UNDEFINED).
+            windowingMode = getWindowingMode();
 
             if (creating) {
                 // Nothing else to do if we don't have a window container yet. E.g. call from ctor.
@@ -609,15 +629,6 @@
             mTmpRect2.setEmpty();
             if (windowingMode != WINDOWING_MODE_FULLSCREEN) {
                 mWindowContainerController.getRawBounds(mTmpRect2);
-                if (windowingMode == WINDOWING_MODE_FREEFORM) {
-                    if (topTask != null) {
-                        // TODO: Can we consolidate this and other sites that call this methods?
-                        Rect bounds = topTask().getLaunchBounds();
-                        if (bounds != null) {
-                            mTmpRect2.set(bounds);
-                        }
-                    }
-                }
             }
 
             if (!Objects.equals(getOverrideBounds(), mTmpRect2)) {
@@ -651,7 +662,7 @@
 
         if (!deferEnsuringVisibility) {
             mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-            mStackSupervisor.resumeFocusedStackTopActivityLocked();
+            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
         }
     }
 
@@ -684,7 +695,7 @@
         mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop);
         postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
         adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */);
-        mStackSupervisor.resumeFocusedStackTopActivityLocked();
+        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
         // Update visibility of activities before notifying WM. This way it won't try to resize
         // windows that are no longer visible.
         mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
@@ -837,7 +848,7 @@
         }
     }
 
-    private ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
+    ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked();
             if (r != null && (!focusableOnly || r.isFocusable())) {
@@ -1042,12 +1053,14 @@
         if (!isActivityTypeHome() && returnsToHomeStack()) {
             // Make sure the home stack is behind this stack since that is where we should return to
             // when this stack is no longer visible.
+            // TODO(b/111541062): Move home stack on the current display
             mStackSupervisor.moveHomeStackToFront(reason + " returnToHome");
         }
 
-        display.positionChildAtTop(this);
+        display.positionChildAtTop(this, true /* includingParents */);
         mStackSupervisor.setFocusStackUnchecked(reason, this);
         if (task != null) {
+            // This also moves the entire hierarchy branch to top, including parents
             insertTaskAtTop(task, null);
             return;
         }
@@ -1073,6 +1086,8 @@
         getDisplay().positionChildAtBottom(this);
         mStackSupervisor.setFocusStackUnchecked(reason, getDisplay().getTopStack());
         if (task != null) {
+            // TODO(b/111541062): We probably don't want to change display z-order to bottom just
+            // because one of its stacks moved to bottom.
             insertTaskAtBottom(task);
             return;
         }
@@ -1429,7 +1444,7 @@
         if (prev == null) {
             if (resuming == null) {
                 Slog.wtf(TAG, "Trying to pause when nothing is resumed");
-                mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
             }
             return false;
         }
@@ -1451,17 +1466,17 @@
 
         mStackSupervisor.getLaunchTimeTracker().stopFullyDrawnTraceIfNeeded(getWindowingMode());
 
-        mService.updateCpuStats();
+        mService.mAm.updateCpuStats();
 
-        if (prev.app != null && prev.app.thread != null) {
+        if (prev.attachedToProcess()) {
             if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
             try {
                 EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
                         prev.shortComponentName, "userLeaving=" + userLeaving);
-                mService.updateUsageStats(prev, false);
+                mService.mAm.updateUsageStats(prev, false);
 
-                mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
-                        PauseActivityItem.obtain(prev.finishing, userLeaving,
+                mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
+                        prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
                                 prev.configChangeFlags, pauseImmediately));
             } catch (Exception e) {
                 // Ignore exception, if process died other code will cleanup.
@@ -1509,7 +1524,7 @@
             // pause, so just treat it as being paused now.
             if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
             if (resuming == null) {
-                mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
             }
             return false;
         }
@@ -1563,7 +1578,7 @@
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
                 prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,
                         "completedPausedLocked");
-            } else if (prev.app != null) {
+            } else if (prev.hasProcess()) {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
                         + " wasStopping=" + wasStopping + " visible=" + prev.visible);
                 if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(prev)) {
@@ -1601,9 +1616,9 @@
         }
 
         if (resumeNext) {
-            final ActivityStack topStack = mStackSupervisor.getFocusedStack();
+            final ActivityStack topStack = mStackSupervisor.getTopDisplayFocusedStack();
             if (!topStack.shouldSleepOrShutDownActivities()) {
-                mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
+                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(topStack, prev, null);
             } else {
                 checkReadyForSleep();
                 ActivityRecord top = topStack.topRunningActivityLocked();
@@ -1612,7 +1627,7 @@
                     // something. Also if the top activity on the stack is not the just paused
                     // activity, we need to go ahead and resume it to ensure we complete an
                     // in-flight app switch.
-                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
                 }
             }
         }
@@ -1620,12 +1635,11 @@
         if (prev != null) {
             prev.resumeKeyDispatchingLocked();
 
-            if (prev.app != null && prev.cpuTimeAtResume > 0
-                    && mService.mBatteryStatsService.isOnBattery()) {
-                long diff = mService.mProcessCpuTracker.getCpuTimeForPid(prev.app.pid)
-                        - prev.cpuTimeAtResume;
+            if (prev.hasProcess() && prev.cpuTimeAtResume > 0
+                    && mService.mAm.mBatteryStatsService.isOnBattery()) {
+                long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume;
                 if (diff > 0) {
-                    BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics();
+                    BatteryStatsImpl bsi = mService.mAm.mBatteryStatsService.getActiveStatistics();
                     synchronized (bsi) {
                         BatteryStatsImpl.Uid.Proc ps =
                                 bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
@@ -1644,7 +1658,7 @@
         // task stack changes, because its positioning may depend on it.
         if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
                 || getDisplay().hasPinnedStack()) {
-            mService.mTaskChangeNotificationController.notifyTaskStackChanged();
+            mService.getTaskChangeNotificationController().notifyTaskStackChanged();
             mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
         }
 
@@ -1658,7 +1672,10 @@
             // Some activity is waiting for another activity to become visible before it's being
             // stopped, which means that we also want to wait with stopping this one to avoid
             // flickers.
-            if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.isEmpty()) {
+            if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.isEmpty()
+                    && !mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) {
+                if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "adding to waiting visible activity=" + r
+                        + " existing=" + mStackSupervisor.mActivitiesWaitingForVisibleActivity);
                 mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(r);
             }
         }
@@ -1722,7 +1739,17 @@
     }
 
     boolean isTopStackOnDisplay() {
-        return getDisplay().isTopStack(this);
+        final ActivityDisplay display = getDisplay();
+        return display != null && display.isTopStack(this);
+    }
+
+    /**
+     * @return {@code true} if this is the focused stack on its current display, {@code false}
+     * otherwise.
+     */
+    boolean isFocusedStackOnDisplay() {
+        final ActivityDisplay display = getDisplay();
+        return display != null && this == display.getFocusedStack();
     }
 
     boolean isTopActivityVisible() {
@@ -1739,9 +1766,6 @@
         if (!isAttached() || mForceHidden) {
             return false;
         }
-        if (mStackSupervisor.isFocusedStack(this)) {
-            return true;
-        }
 
         final ActivityRecord top = topRunningActivityLocked();
         if (top == null && isInStackLocked(starting) == null && !isTopStackOnDisplay()) {
@@ -1871,7 +1895,7 @@
             boolean aboveTop = top != null;
             final boolean stackShouldBeVisible = shouldBeVisible(starting);
             boolean behindFullscreenActivity = !stackShouldBeVisible;
-            boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this)
+            boolean resumeNextActivity = mStackSupervisor.isTopDisplayFocusedStack(this)
                     && (isInStackLocked(starting) == null);
             final boolean isTopNotPinnedStack =
                     isAttached() && getDisplay().isTopNotPinnedStack(this);
@@ -1911,7 +1935,7 @@
                                     true /* ignoreStopState */);
                         }
 
-                        if (r.app == null || r.app.thread == null) {
+                        if (!r.attachedToProcess()) {
                             if (makeVisibleAndRestartIfNeeded(starting, configChanges, isTop,
                                     resumeNextActivity, r)) {
                                 if (activityNdx >= activities.size()) {
@@ -2143,11 +2167,11 @@
             switch (r.getState()) {
                 case STOPPING:
                 case STOPPED:
-                    if (r.app != null && r.app.thread != null) {
+                    if (r.attachedToProcess()) {
                         if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
                                 "Scheduling invisibility: " + r);
-                        mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
-                                WindowVisibilityItem.obtain(false /* showWindow */));
+                        mService.getLifecycleManager().scheduleTransaction(r.app.getThread(),
+                                r.appToken, WindowVisibilityItem.obtain(false /* showWindow */));
                     }
 
                     // Reset the flag indicating that an app can enter picture-in-picture once the
@@ -2224,9 +2248,9 @@
 
             if (waitingActivity != null) {
                 mWindowManager.setWindowOpaque(waitingActivity.appToken, false);
-                if (waitingActivity.app != null && waitingActivity.app.thread != null) {
+                if (waitingActivity.attachedToProcess()) {
                     try {
-                        waitingActivity.app.thread.scheduleTranslucentConversionComplete(
+                        waitingActivity.app.getThread().scheduleTranslucentConversionComplete(
                                 waitingActivity.appToken, r != null);
                     } catch (RemoteException e) {
                     }
@@ -2282,7 +2306,7 @@
      *
      * NOTE: It is not safe to call this method directly as it can cause an activity in a
      *       non-focused stack to be resumed.
-     *       Use {@link ActivityStackSupervisor#resumeFocusedStackTopActivityLocked} to resume the
+     *       Use {@link ActivityStackSupervisor#resumeFocusedStacksTopActivitiesLocked} to resume the
      *       right activity for the current system state.
      */
     @GuardedBy("mService")
@@ -2335,7 +2359,7 @@
 
     @GuardedBy("mService")
     private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
-        if (!mService.mBooting && !mService.mBooted) {
+        if (!mService.mAm.mBooting && !mService.mAm.mBooted) {
             // Not ready yet!
             return false;
         }
@@ -2395,7 +2419,7 @@
         // Make sure that the user who owns this activity is started.  If not,
         // we will just leave it as is because someone should be bringing
         // another user's activities to the top of the stack.
-        if (!mService.mUserController.hasStartedUserState(next.userId)) {
+        if (!mService.mAmInternal.hasStartedUserState(next.userId)) {
             Slog.w(TAG, "Skipping resume of top activity " + next
                     + ": user " + next.userId + " is stopped");
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -2423,7 +2447,7 @@
 
         boolean lastResumedCanPip = false;
         ActivityRecord lastResumed = null;
-        final ActivityStack lastFocusedStack = mStackSupervisor.getLastStack();
+        final ActivityStack lastFocusedStack = mStackSupervisor.getTopDisplayLastFocusedStack();
         if (lastFocusedStack != null && lastFocusedStack != this) {
             // So, why aren't we using prev here??? See the param comment on the method. prev doesn't
             // represent the last resumed activity. However, the last focus stack does if it isn't null.
@@ -2445,7 +2469,7 @@
         final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0
                 && !lastResumedCanPip;
 
-        boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false);
+        boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
         if (mResumedActivity != null) {
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "resumeTopActivityLocked: Pausing " + mResumedActivity);
@@ -2458,8 +2482,9 @@
             // at the top of the LRU list, since we know we will be needing it
             // very soon and it would be a waste to let it get killed if it
             // happens to be sitting towards the end.
-            if (next.app != null && next.app.thread != null) {
-                mService.updateLruProcessLocked(next.app, true, null);
+            if (next.attachedToProcess()) {
+                next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+                        true /* updateLru */, true /* activityChange */, false /* updateOomAdj */);
             }
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             if (lastResumed != null) {
@@ -2583,8 +2608,8 @@
 
         mStackSupervisor.mNoAnimActivities.clear();
 
-        ActivityStack lastStack = mStackSupervisor.getLastStack();
-        if (next.app != null && next.app.thread != null) {
+        ActivityStack lastStack = mStackSupervisor.getTopDisplayLastFocusedStack();
+        if (next.attachedToProcess()) {
             if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
                     + " stopped=" + next.stopped + " visible=" + next.visible);
 
@@ -2618,22 +2643,22 @@
                         lastStack == null ? null :lastStack.mResumedActivity;
                 final ActivityState lastState = next.getState();
 
-                mService.updateCpuStats();
+                mService.mAm.updateCpuStats();
 
                 if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next
                         + " (in existing)");
 
                 next.setState(RESUMED, "resumeTopActivityInnerLocked");
 
-                mService.updateLruProcessLocked(next.app, true, null);
+                next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+                        true /* updateLru */, true /* activityChange */, true /* updateOomAdj */);
                 updateLRUListLocked(next);
-                mService.updateOomAdjLocked();
 
                 // Have the window manager re-evaluate the orientation of
                 // the screen based on the new activity order.
                 boolean notUpdated = true;
 
-                if (mStackSupervisor.isFocusedStack(this)) {
+                if (isFocusedStackOnDisplay()) {
                     // We have special rotation behavior when here is some active activity that
                     // requests specific orientation or Keyguard is locked. Make sure all activity
                     // visibilities are set correctly as well as the transition is updated if needed
@@ -2669,8 +2694,8 @@
                 }
 
                 try {
-                    final ClientTransaction transaction = ClientTransaction.obtain(next.app.thread,
-                            next.appToken);
+                    final ClientTransaction transaction =
+                            ClientTransaction.obtain(next.app.getThread(), next.appToken);
                     // Deliver all pending results.
                     ArrayList<ResultInfo> a = next.results;
                     if (a != null) {
@@ -2697,12 +2722,10 @@
 
                     next.sleeping = false;
                     mService.getAppWarningsLocked().onResumeActivity(next);
-                    mService.showAskCompatModeDialogLocked(next);
-                    next.app.pendingUiClean = true;
-                    next.app.forceProcessStateUpTo(mService.mTopProcessState);
+                    next.app.setPendingUiCleanAndForceProcessStateUpTo(mService.mTopProcessState);
                     next.clearOptionsLocked();
                     transaction.setLifecycleStateRequest(
-                            ResumeActivityItem.obtain(next.app.repProcState,
+                            ResumeActivityItem.obtain(next.app.getReportedProcState(),
                                     mService.isNextTransitionForward()));
                     mService.getLifecycleManager().scheduleTransaction(transaction);
 
@@ -2767,12 +2790,13 @@
 
     private boolean resumeTopActivityInNextFocusableStack(ActivityRecord prev,
             ActivityOptions options, String reason) {
-        if (adjustFocusToNextFocusableStack(reason)) {
+        final ActivityStack nextFocusedStack = adjustFocusToNextFocusableStack(reason);
+        if (nextFocusedStack != null) {
             // Try to move focus to the next visible stack with a running activity if this
             // stack is not covering the entire screen or is on a secondary display (with no home
             // stack).
-            return mStackSupervisor.resumeFocusedStackTopActivityLocked(
-                    mStackSupervisor.getFocusedStack(), prev, null);
+            return mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(nextFocusedStack, prev,
+                    null /* targetOptions */);
         }
 
         // Let's just start up the Launcher...
@@ -2785,20 +2809,6 @@
                 mStackSupervisor.resumeHomeStackTask(prev, reason);
     }
 
-    private TaskRecord getNextTask(TaskRecord targetTask) {
-        final int index = mTaskHistory.indexOf(targetTask);
-        if (index >= 0) {
-            final int numTasks = mTaskHistory.size();
-            for (int i = index + 1; i < numTasks; ++i) {
-                TaskRecord task = mTaskHistory.get(i);
-                if (task.userId == targetTask.userId) {
-                    return task;
-                }
-            }
-        }
-        return null;
-    }
-
     /** Returns the position the input task should be placed in this stack. */
     int getAdjustedPositionForTask(TaskRecord task, int suggestedPosition,
             ActivityRecord starting) {
@@ -2825,7 +2835,7 @@
 
     /**
      * Used from {@link ActivityStack#positionTask(TaskRecord, int)}.
-     * @see ActivityManagerService#positionTaskInStack(int, int, int).
+     * @see ActivityTaskManagerService#positionTaskInStack(int, int, int).
      */
     private void insertTaskAtPosition(TaskRecord task, int position) {
         if (position >= mTaskHistory.size()) {
@@ -3345,19 +3355,19 @@
             String resultWho, int requestCode, int resultCode, Intent data) {
 
         if (callingUid > 0) {
-            mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+            mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName,
                     data, r.getUriPermissionsLocked(), r.userId);
         }
 
         if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
                 + " : who=" + resultWho + " req=" + requestCode
                 + " res=" + resultCode + " data=" + data);
-        if (mResumedActivity == r && r.app != null && r.app.thread != null) {
+        if (mResumedActivity == r && r.attachedToProcess()) {
             try {
                 ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
                 list.add(new ResultInfo(resultWho, requestCode,
                         resultCode, data));
-                mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
+                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
                         ActivityResultItem.obtain(list));
                 return;
             } catch (Exception e) {
@@ -3385,7 +3395,7 @@
     }
 
     private void adjustFocusedActivityStack(ActivityRecord r, String reason) {
-        if (!mStackSupervisor.isFocusedStack(this) ||
+        if (!mStackSupervisor.isTopDisplayFocusedStack(this) ||
                 ((mResumedActivity != r) && (mResumedActivity != null))) {
             return;
         }
@@ -3413,7 +3423,15 @@
         }
 
         // Move focus to next focusable stack if possible.
-        if (adjustFocusToNextFocusableStack(myReason)) {
+        final ActivityStack nextFocusableStack = adjustFocusToNextFocusableStack(myReason);
+        if (nextFocusableStack != null) {
+            final ActivityRecord top = nextFocusableStack.topRunningActivityLocked();
+            if (top != null && top == mStackSupervisor.getTopResumedActivity()) {
+                // TODO(b/111361570): Remove this and update focused app per-display in
+                // WindowManager every time an activity becomes resumed in
+                // ActivityTaskManagerService#setResumedActivityUncheckLocked().
+                mService.setResumedActivityUncheckLocked(top, reason);
+            }
             return;
         }
 
@@ -3421,21 +3439,25 @@
         mStackSupervisor.moveHomeStackTaskToTop(myReason);
     }
 
-    /** Find next proper focusable stack and make it focused. */
-    boolean adjustFocusToNextFocusableStack(String reason) {
+    /**
+     * Find next proper focusable stack and make it focused.
+     * @return The stack that now got the focus, {@code null} if none found.
+     */
+    ActivityStack adjustFocusToNextFocusableStack(String reason) {
         return adjustFocusToNextFocusableStack(reason, false /* allowFocusSelf */);
     }
 
     /**
      * Find next proper focusable stack and make it focused.
      * @param allowFocusSelf Is the focus allowed to remain on the same stack.
+     * @return The stack that now got the focus, {@code null} if none found.
      */
-    private boolean adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) {
+    private ActivityStack adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) {
         final ActivityStack stack =
                 mStackSupervisor.getNextFocusableStackLocked(this, !allowFocusSelf);
         final String myReason = reason + " adjustFocusToNextFocusableStack";
         if (stack == null) {
-            return false;
+            return null;
         }
 
         final ActivityRecord top = stack.topRunningActivityLocked();
@@ -3443,11 +3465,12 @@
         if (stack.isActivityTypeHome() && (top == null || !top.visible)) {
             // If we will be focusing on the home stack next and its current top activity isn't
             // visible, then use the move the home stack task to top to make the activity visible.
-            return mStackSupervisor.moveHomeStackTaskToTop(reason);
+            mStackSupervisor.moveHomeStackTaskToTop(reason);
+            return stack;
         }
 
         stack.moveToFront(myReason);
-        return true;
+        return stack;
     }
 
     final void stopActivityLocked(ActivityRecord r) {
@@ -3471,7 +3494,7 @@
             }
         }
 
-        if (r.app != null && r.app.thread != null) {
+        if (r.attachedToProcess()) {
             adjustFocusedActivityStack(r, "stopActivity");
             r.resumeKeyDispatchingLocked();
             try {
@@ -3486,7 +3509,7 @@
                 }
                 EventLogTags.writeAmStopActivity(
                         r.userId, System.identityHashCode(r), r.shortComponentName);
-                mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
+                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
                         StopActivityItem.obtain(r.visible, r.configChangeFlags));
                 if (shouldSleepOrShutDownActivities()) {
                     r.setSleeping(true);
@@ -3542,7 +3565,7 @@
                 }
             }
         }
-        mService.updateOomAdjLocked();
+        mService.updateOomAdj();
     }
 
     /**
@@ -3553,7 +3576,7 @@
      * @return The task that was finished in this stack, {@code null} if top running activity does
      *         not belong to the crashed app.
      */
-    final TaskRecord finishTopCrashedActivityLocked(ProcessRecord app, String reason) {
+    final TaskRecord finishTopCrashedActivityLocked(WindowProcessController app, String reason) {
         ActivityRecord r = topRunningActivityLocked();
         TaskRecord finishedTask = null;
         if (r == null || r.app != app) {
@@ -3613,13 +3636,12 @@
                 // Check if any of the activities are using voice
                 for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
                     ActivityRecord r = tr.mActivities.get(activityNdx);
-                    if (r.voiceSession != null
-                            && r.voiceSession.asBinder() == sessionBinder) {
+                    if (r.voiceSession != null && r.voiceSession.asBinder() == sessionBinder) {
                         // Inform of cancellation
                         r.clearVoiceSessionLocked();
                         try {
-                            r.app.thread.scheduleLocalVoiceInteractionStarted((IBinder) r.appToken,
-                                    null);
+                            r.app.getThread().scheduleLocalVoiceInteractionStarted(
+                                    r.appToken, null);
                         } catch (RemoteException re) {
                             // Ok
                         }
@@ -3631,7 +3653,7 @@
         }
 
         if (didOne) {
-            mService.updateOomAdjLocked();
+            mService.updateOomAdj();
         }
     }
 
@@ -3660,12 +3682,11 @@
                 }
             }
             if (r.info.applicationInfo.uid > 0) {
-                mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
+                mService.mUgmInternal.grantUriPermissionFromIntent(r.info.applicationInfo.uid,
                         resultTo.packageName, resultData,
                         resultTo.getUriPermissionsLocked(), resultTo.userId);
             }
-            resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
-                                     resultData);
+            resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, resultData);
             r.resultTo = null;
         }
         else if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "No result destination from " + r);
@@ -3730,7 +3751,7 @@
                 if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                         "Prepare close transition: finishing " + r);
                 if (endTask) {
-                    mService.mTaskChangeNotificationController.notifyTaskRemovalStarted(
+                    mService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
                             task.taskId);
                 }
                 mWindowManager.prepareAppTransition(transit, false);
@@ -3821,7 +3842,7 @@
                     "Moving to STOPPING: "+ r + " (finish requested)");
             r.setState(STOPPING, "finishCurrentActivityLocked");
             if (oomAdj) {
-                mService.updateOomAdjLocked();
+                mService.updateOomAdj();
             }
             return r;
         }
@@ -3835,7 +3856,7 @@
 
         r.setState(FINISHING, "finishCurrentActivityLocked");
         final boolean finishingActivityInNonFocusedStack
-                = r.getStack() != mStackSupervisor.getFocusedStack()
+                = r.getStack() != mStackSupervisor.getTopDisplayFocusedStack()
                 && prevState == PAUSED && mode == FINISH_AFTER_VISIBLE;
 
         if (mode == FINISH_IMMEDIATELY
@@ -3855,7 +3876,7 @@
                         false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
             if (activityRemoved) {
-                mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
             }
             if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
                     "destroyActivityLocked: finishCurrentActivityLocked r=" + r +
@@ -3868,7 +3889,7 @@
         if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r);
         mStackSupervisor.mFinishingActivities.add(r);
         r.resumeKeyDispatchingLocked();
-        mStackSupervisor.resumeFocusedStackTopActivityLocked();
+        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
         return r;
     }
 
@@ -3964,6 +3985,8 @@
             }
         }
 
+        // TODO: There is a dup. of this block of code in ActivityTaskManagerService.finishActivity
+        // We should consolidate.
         IActivityController controller = mService.mController;
         if (controller != null) {
             ActivityRecord next = topRunningActivityLocked(srec.appToken, 0);
@@ -4008,7 +4031,7 @@
                     // TODO(b/64750076): Check if calling pid should really be -1.
                     final int res = mService.getActivityStartController()
                             .obtainStarter(destIntent, "navigateUpTo")
-                            .setCaller(srec.app.thread)
+                            .setCaller(srec.app.getThread())
                             .setActivityInfo(aInfo)
                             .setResultTo(parent.appToken)
                             .setCallingPid(-1)
@@ -4082,7 +4105,7 @@
             for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
                 PendingIntentRecord rec = apr.get();
                 if (rec != null) {
-                    mService.cancelIntentSenderLocked(rec, false);
+                    mService.mAm.cancelIntentSenderLocked(rec, false);
                 }
             }
             r.pendingResults = null;
@@ -4169,19 +4192,19 @@
             Iterator<ConnectionRecord> it = r.connections.iterator();
             while (it.hasNext()) {
                 ConnectionRecord c = it.next();
-                mService.mServices.removeConnectionLocked(c, null, r);
+                mService.mAm.mServices.removeConnectionLocked(c, null, r);
             }
             r.connections = null;
         }
     }
 
-    final void scheduleDestroyActivities(ProcessRecord owner, String reason) {
+    final void scheduleDestroyActivities(WindowProcessController owner, String reason) {
         Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
         msg.obj = new ScheduleDestroyArgs(owner, reason);
         mHandler.sendMessage(msg);
     }
 
-    private void destroyActivitiesLocked(ProcessRecord owner, String reason) {
+    private void destroyActivitiesLocked(WindowProcessController owner, String reason) {
         boolean lastIsOpaque = false;
         boolean activityRemoved = false;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -4212,7 +4235,7 @@
             }
         }
         if (activityRemoved) {
-            mStackSupervisor.resumeFocusedStackTopActivityLocked();
+            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
         }
     }
 
@@ -4226,7 +4249,7 @@
         return false;
     }
 
-    final int releaseSomeActivitiesLocked(ProcessRecord app, ArraySet<TaskRecord> tasks,
+    final int releaseSomeActivitiesLocked(WindowProcessController app, ArraySet<TaskRecord> tasks,
             String reason) {
         // Iterate over tasks starting at the back (oldest) first.
         if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + app);
@@ -4280,7 +4303,7 @@
     final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
         if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
                 "Removing activity from " + reason + ": token=" + r
-                        + ", app=" + (r.app != null ? r.app.processName : "(null)"));
+                        + ", app=" + (r.hasProcess() ? r.app.mName : "(null)"));
 
         if (r.isState(DESTROYING, DESTROYED)) {
             if (DEBUG_STATES) Slog.v(TAG_STATES, "activity " + r + " already destroying."
@@ -4296,23 +4319,23 @@
 
         cleanUpActivityLocked(r, false, false);
 
-        final boolean hadApp = r.app != null;
+        final boolean hadApp = r.hasProcess();
 
         if (hadApp) {
             if (removeFromApp) {
-                r.app.activities.remove(r);
-                if (mService.mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) {
-                    mService.mHeavyWeightProcess = null;
-                    mService.mHandler.sendEmptyMessage(
+                r.app.removeActivity(r);
+                if (mService.mAm.mHeavyWeightProcess != null
+                        && mService.mAm.mHeavyWeightProcess.getWindowProcessController() == r.app
+                        && !r.app.hasActivities()) {
+                    mService.mAm.mHeavyWeightProcess = null;
+                    mService.mAm.mHandler.sendEmptyMessage(
                             ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG);
                 }
-                if (r.app.activities.isEmpty()) {
+                if (!r.app.hasActivities()) {
                     // Update any services we are bound to that might care about whether
                     // their client may have activities.
-                    mService.mServices.updateServiceConnectionActivitiesLocked(r.app);
                     // No longer have activities, so update LRU list and oom adj.
-                    mService.updateLruProcessLocked(r.app, false, null);
-                    mService.updateOomAdjLocked();
+                    r.app.updateProcessInfo(true, true, false, true);
                 }
             }
 
@@ -4320,7 +4343,7 @@
 
             try {
                 if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
-                mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
+                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
                         DestroyActivityItem.obtain(r.finishing, r.configChangeFlags));
             } catch (Exception e) {
                 // We can just ignore exceptions here...  if the process
@@ -4407,11 +4430,11 @@
             }
         }
 
-        mStackSupervisor.resumeFocusedStackTopActivityLocked();
+        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
     }
 
     private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
-            ProcessRecord app, String listName) {
+            WindowProcessController app, String listName) {
         int i = list.size();
         if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
             "Removing app " + app + " from list " + listName + " with " + i + " entries");
@@ -4427,7 +4450,7 @@
         }
     }
 
-    private boolean removeHistoryRecordsForAppLocked(ProcessRecord app) {
+    private boolean removeHistoryRecordsForAppLocked(WindowProcessController app) {
         removeHistoryRecordsForAppLocked(mLRUActivities, app, "mLRUActivities");
         removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app,
                 "mStoppingActivities");
@@ -4460,7 +4483,14 @@
                         hasVisibleActivities = true;
                     }
                     final boolean remove;
-                    if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
+                    if ((r.mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
+                            || r.mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
+                            && r.launchCount < 3 && !r.finishing) {
+                        // If the process crashed during a resize, always try to relaunch it, unless
+                        // it has failed more than twice. Skip activities that's already finishing
+                        // cleanly by itself.
+                        remove = false;
+                    } else if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
                         // Don't currently have state for the activity, or
                         // it is finishing -- always remove it.
                         remove = true;
@@ -4490,7 +4520,7 @@
                                     r.getTask().taskId, r.shortComponentName,
                                     "proc died without state saved");
                             if (r.getState() == RESUMED) {
-                                mService.updateUsageStats(r, false);
+                                mService.mAm.updateUsageStats(r, false);
                             }
                         }
                     } else {
@@ -4633,10 +4663,10 @@
                 topActivity.supportsEnterPipOnTaskSwitch = true;
             }
 
-            mStackSupervisor.resumeFocusedStackTopActivityLocked();
+            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
             EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
 
-            mService.mTaskChangeNotificationController.notifyTaskMovedToFront(tr.taskId);
+            mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.taskId);
         } finally {
             getDisplay().continueUpdateImeTarget();
         }
@@ -4704,7 +4734,7 @@
             return true;
         }
 
-        mStackSupervisor.resumeFocusedStackTopActivityLocked();
+        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
         return true;
     }
 
@@ -4751,15 +4781,16 @@
         if (updatedConfig) {
             // Ensure the resumed state of the focus activity if we updated the configuration of
             // any activity.
-            mStackSupervisor.resumeFocusedStackTopActivityLocked();
+            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
         }
     }
 
     // TODO: Figure-out a way to consolidate with resize() method below.
     @Override
     public void requestResize(Rect bounds) {
-        mService.resizeStack(mStackId, bounds, true /* allowResizeInDockedMode */,
-                false /* preserveWindows */, false /* animate */, -1 /* animationDuration */);
+        mService.resizeStack(mStackId, bounds,
+                true /* allowResizeInDockedMode */, false /* preserveWindows */,
+                false /* animate */, -1 /* animationDuration */);
     }
 
     // TODO: Can only be called from special methods in ActivityStackSupervisor.
@@ -4781,13 +4812,16 @@
                 final TaskRecord task = mTaskHistory.get(i);
                 if (task.isResizeable()) {
                     if (inFreeformWindowingMode()) {
-                        // TODO: Can be removed now since each freeform task is in its own stack.
+                        // TODO(b/71028874): Can be removed since each freeform task is its own
+                        //                   stack.
                         // For freeform stack we don't adjust the size of the tasks to match that
                         // of the stack, but we do try to make sure the tasks are still contained
                         // with the bounds of the stack.
-                        mTmpRect2.set(task.getOverrideBounds());
-                        fitWithinBounds(mTmpRect2, bounds);
-                        task.updateOverrideConfiguration(mTmpRect2);
+                        if (task.getOverrideBounds() != null) {
+                            mTmpRect2.set(task.getOverrideBounds());
+                            fitWithinBounds(mTmpRect2, bounds);
+                            task.updateOverrideConfiguration(mTmpRect2);
+                        }
                     } else {
                         task.updateOverrideConfiguration(taskBounds, insetBounds);
                     }
@@ -4900,7 +4934,7 @@
                         || (packageName == null && r.userId == userId);
                 if ((userId == UserHandle.USER_ALL || r.userId == userId)
                         && (sameComponent || r.getTask() == lastTask)
-                        && (r.app == null || evenPersistent || !r.app.persistent)) {
+                        && (r.app == null || evenPersistent || !r.app.isPersistent())) {
                     if (!doit) {
                         if (r.finishing) {
                             // If this activity is just finishing, then it is not
@@ -4920,8 +4954,8 @@
                     didSomething = true;
                     Slog.i(TAG, "  Force finishing activity " + r);
                     if (sameComponent) {
-                        if (r.app != null) {
-                            r.app.removed = true;
+                        if (r.hasProcess()) {
+                            r.app.setRemoved(true);
                         }
                         r.app = null;
                     }
@@ -4941,7 +4975,7 @@
      */
     void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType,
             @WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed) {
-        boolean focusedStack = mStackSupervisor.getFocusedStack() == this;
+        boolean focusedStack = mStackSupervisor.getTopDisplayFocusedStack() == this;
         boolean topTask = true;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -4992,7 +5026,7 @@
      * @param app The app of the activity that died.
      * @return result from removeHistoryRecordsForAppLocked.
      */
-    boolean handleAppDiedLocked(ProcessRecord app) {
+    boolean handleAppDiedLocked(WindowProcessController app) {
         if (mPausingActivity != null && mPausingActivity.app == app) {
             if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG_PAUSE,
                     "App died while pausing: " + mPausingActivity);
@@ -5006,7 +5040,7 @@
         return removeHistoryRecordsForAppLocked(app);
     }
 
-    void handleAppCrashLocked(ProcessRecord app) {
+    void handleAppCrashLocked(WindowProcessController app) {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
@@ -5148,9 +5182,9 @@
             // We only need to adjust focused stack if this stack is in focus and we are not in the
             // process of moving the task to the top of the stack that will be focused.
             if (isOnHomeDisplay() && mode != REMOVE_TASK_MODE_MOVING_TO_TOP
-                    && mStackSupervisor.isFocusedStack(this)) {
+                    && mStackSupervisor.isTopDisplayFocusedStack(this)) {
                 String myReason = reason + " leftTaskHistoryEmpty";
-                if (!inMultiWindowMode() || !adjustFocusToNextFocusableStack(myReason)) {
+                if (!inMultiWindowMode() || adjustFocusToNextFocusableStack(myReason) == null) {
                     mStackSupervisor.moveHomeStackToFront(myReason);
                 }
             }
@@ -5166,7 +5200,7 @@
 
         // Notify if a task from the pinned stack is being removed (or moved depending on the mode)
         if (inPinnedWindowingMode()) {
-            mService.mTaskChangeNotificationController.notifyActivityUnpinned();
+            mService.getTaskChangeNotificationController().notifyActivityUnpinned();
         }
     }
 
@@ -5255,7 +5289,7 @@
         // The task might have already been running and its visibility needs to be synchronized with
         // the visibility of the stack / windows.
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        mStackSupervisor.resumeFocusedStackTopActivityLocked();
+        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
     }
 
     private ActivityStack preAddTask(TaskRecord task, String reason, boolean toTop) {
@@ -5284,6 +5318,20 @@
         }
     }
 
+    public void setAlwaysOnTop(boolean alwaysOnTop) {
+        if (isAlwaysOnTop() == alwaysOnTop) {
+            return;
+        }
+        super.setAlwaysOnTop(alwaysOnTop);
+        final ActivityDisplay display = getDisplay();
+        // positionChildAtTop() must be called even when always on top gets turned off because we
+        // need to make sure that the stack is moved from among always on top windows to below other
+        // always on top windows. Since the position the stack should be inserted into is calculated
+        // properly in {@link ActivityDisplay#getTopInsertPosition()} in both cases, we can just
+        // request that the stack is put at top here.
+        display.positionChildAtTop(this, false /* includingParents */);
+    }
+
     void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
             boolean setPause, String reason) {
         if (!moveToFront) {
@@ -5338,7 +5386,7 @@
 
         // Do not sleep activities in this stack if we're marked as focused and the keyguard
         // is in the process of going away.
-        if (mStackSupervisor.getFocusedStack() == this
+        if (mStackSupervisor.getTopDisplayFocusedStack() == this
                 && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
             return false;
         }
@@ -5347,7 +5395,7 @@
     }
 
     boolean shouldSleepOrShutDownActivities() {
-        return shouldSleepActivities() || mService.isShuttingDownLocked();
+        return shouldSleepActivities() || mService.mShuttingDown;
     }
 
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e034b82..9809bfa 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,14 +17,12 @@
 package com.android.server.am;
 
 import static android.Manifest.permission.ACTIVITY_EMBEDDING;
-import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.START_ANY_ACTIVITY;
-import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
 import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
@@ -51,7 +49,6 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Display.TYPE_VIRTUAL;
 
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE;
@@ -75,8 +72,8 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.ANIMATE;
 import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
+import static com.android.server.am.ActivityRecord.RELAUNCH_REASON_NONE;
 import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
@@ -96,6 +93,7 @@
 import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
 import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
 import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
+import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
 import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
 import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 
@@ -110,7 +108,9 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
-import android.app.ActivityManagerInternal.SleepToken;
+import android.app.ActivityManagerInternal;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
 import android.app.ProfilerInfo;
@@ -198,8 +198,8 @@
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
     private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
     private static final String TAG_STACK = TAG + POSTFIX_STACK;
-    private static final String TAG_STATES = TAG + POSTFIX_STATES;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+    static final String TAG_STATES = TAG + POSTFIX_STATES;
     static final String TAG_TASKS = TAG + POSTFIX_TASKS;
 
     /** How long we wait until giving up on the last activity telling us it is idle. */
@@ -305,7 +305,7 @@
     /** The number of distinct task ids that can be assigned to the tasks of a single user */
     private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
 
-    final ActivityManagerService mService;
+    ActivityTaskManagerService mService;
 
     /** The historial list of recent tasks including inactive tasks */
     RecentTasks mRecentTasks;
@@ -335,9 +335,6 @@
      * Display.DEFAULT_DISPLAY. */
     ActivityStack mHomeStack;
 
-    /** The stack currently receiving input or launching the next activity. */
-    ActivityStack mFocusedStack;
-
     /** If this is the same as mFocusedStack then the activity on the top of the focused stack has
      * been resumed. If stacks are changing position this will hold the old stack until the new
      * stack becomes resumed after which it will be set to mFocusedStack. */
@@ -581,21 +578,21 @@
         final ActivityRecord sourceRecord;
         final int startFlags;
         final ActivityStack stack;
-        final ProcessRecord callerApp;
+        final WindowProcessController callerApp;
 
         PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
-                int _startFlags, ActivityStack _stack, ProcessRecord _callerApp) {
+                int _startFlags, ActivityStack _stack, WindowProcessController app) {
             r = _r;
             sourceRecord = _sourceRecord;
             startFlags = _startFlags;
             stack = _stack;
-            callerApp = _callerApp;
+            callerApp = app;
         }
 
         void sendErrorResult(String message) {
             try {
-                if (callerApp.thread != null) {
-                    callerApp.thread.scheduleCrash(message);
+                if (callerApp.hasThread()) {
+                    callerApp.getThread().scheduleCrash(message);
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Exception scheduling crash of failed "
@@ -604,12 +601,17 @@
         }
     }
 
-    public ActivityStackSupervisor(ActivityManagerService service, Looper looper) {
+    public ActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
         mService = service;
         mLooper = looper;
         mHandler = new ActivityStackSupervisorHandler(looper);
     }
 
+    @VisibleForTesting
+    void setService(ActivityTaskManagerService service) {
+        mService = service;
+    }
+
     public void initialize() {
         if (mInitialized) {
             return;
@@ -617,8 +619,7 @@
 
         mInitialized = true;
         mRunningTasks = createRunningTasks();
-        mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
-                mHandler.getLooper());
+        mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext, mHandler.getLooper());
         mKeyguardController = new KeyguardController(mService, this);
 
         mLaunchParamsController = new LaunchParamsController(mService);
@@ -661,30 +662,78 @@
     }
 
     void setWindowManager(WindowManagerService wm) {
-        synchronized (mService) {
-            mWindowManager = wm;
-            getKeyguardController().setWindowManager(wm);
+        mWindowManager = wm;
+        getKeyguardController().setWindowManager(wm);
 
-            mDisplayManager =
-                    (DisplayManager)mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
-            mDisplayManager.registerDisplayListener(this, null);
-            mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+        mDisplayManager =
+                (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
+        mDisplayManager.registerDisplayListener(this, null);
+        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
 
-            Display[] displays = mDisplayManager.getDisplays();
-            for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
-                final Display display = displays[displayNdx];
-                ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
-                mActivityDisplays.put(display.getDisplayId(), activityDisplay);
-                calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
-            }
-
-            mHomeStack = mFocusedStack = mLastFocusedStack = getDefaultDisplay().getOrCreateStack(
-                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+        Display[] displays = mDisplayManager.getDisplays();
+        for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
+            final Display display = displays[displayNdx];
+            ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
+            mActivityDisplays.put(display.getDisplayId(), activityDisplay);
+            calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
         }
+
+        final ActivityDisplay defaultDisplay = getDefaultDisplay();
+        mHomeStack = mLastFocusedStack = defaultDisplay.getOrCreateStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
     }
 
-    ActivityStack getFocusedStack() {
-        return mFocusedStack;
+    ActivityStack getTopDisplayFocusedStack() {
+        mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
+
+        for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
+            final int displayId = mTmpOrderedDisplayIds.get(i);
+            final ActivityDisplay display = mActivityDisplays.get(displayId);
+
+            // If WindowManagerService has encountered the display before we have, ignore as there
+            // will be no stacks present and therefore no activities.
+            if (display == null) {
+                continue;
+            }
+            final ActivityStack focusedStack = display.getFocusedStack();
+            if (focusedStack != null) {
+                return focusedStack;
+            }
+        }
+        return null;
+    }
+
+    ActivityRecord getTopResumedActivity() {
+        if (mWindowManager == null) {
+            return null;
+        }
+
+        final ActivityStack focusedStack = getTopDisplayFocusedStack();
+        if (focusedStack == null) {
+            return null;
+        }
+        final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+        if (resumedActivity != null && resumedActivity.app != null) {
+            return resumedActivity;
+        }
+        // The top focused stack might not have a resumed activity yet - look on all displays in
+        // focus order.
+        mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
+        for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
+            final int displayId = mTmpOrderedDisplayIds.get(i);
+            final ActivityDisplay display = mActivityDisplays.get(displayId);
+
+            // If WindowManagerService has encountered the display before we have, ignore as there
+            // will be no stacks present and therefore no activities.
+            if (display == null) {
+                continue;
+            }
+            final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
+            if (resumedActivityOnDisplay != null) {
+                return resumedActivityOnDisplay;
+            }
+        }
+        return null;
     }
 
     boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
@@ -695,12 +744,12 @@
         return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
     }
 
-    ActivityStack getLastStack() {
+    ActivityStack getTopDisplayLastFocusedStack() {
         return mLastFocusedStack;
     }
 
-    boolean isFocusedStack(ActivityStack stack) {
-        return stack != null && stack == mFocusedStack;
+    boolean isTopDisplayFocusedStack(ActivityStack stack) {
+        return stack != null && stack == getTopDisplayFocusedStack();
     }
 
     /** NOTE: Should only be called from {@link ActivityStack#moveToFront} */
@@ -708,19 +757,24 @@
         if (!focusCandidate.isFocusable()) {
             // The focus candidate isn't focusable. Move focus to the top stack that is focusable.
             focusCandidate = getNextFocusableStackLocked(focusCandidate, false /* ignoreCurrent */);
+            if (focusCandidate == null) {
+                Slog.w(TAG,
+                        "setFocusStackUnchecked: No focusable stack found, focus home as default");
+                focusCandidate = mHomeStack;
+            }
         }
 
-        if (focusCandidate != mFocusedStack) {
-            mLastFocusedStack = mFocusedStack;
-            mFocusedStack = focusCandidate;
-
+        final ActivityStack currentFocusedStack = getTopDisplayFocusedStack();
+        if (currentFocusedStack != focusCandidate) {
+            mLastFocusedStack = currentFocusedStack;
+            // TODO(b/111541062): Update event log to include focus movements on all displays
             EventLogTags.writeAmFocusedStack(
-                    mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
+                    mCurrentUser, focusCandidate == null ? -1 : focusCandidate.getStackId(),
                     mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
         }
 
         final ActivityRecord r = topRunningActivityLocked();
-        if (mService.mBooting || !mService.mBooted) {
+        if (mService.mAm.mBooting || !mService.mAm.mBooted) {
             if (r != null && r.idle) {
                 checkFinishBootingLocked();
             }
@@ -752,7 +806,7 @@
     }
 
     boolean resumeHomeStackTask(ActivityRecord prev, String reason) {
-        if (!mService.mBooting && !mService.mBooted) {
+        if (!mService.mAm.mBooting && !mService.mAm.mBooted) {
             // Not ready yet!
             return false;
         }
@@ -764,9 +818,9 @@
         // Only resume home activity if isn't finishing.
         if (r != null && !r.finishing) {
             moveFocusableActivityStackToFrontLocked(r, myReason);
-            return resumeFocusedStackTopActivityLocked(mHomeStack, prev, null);
+            return resumeFocusedStacksTopActivitiesLocked(mHomeStack, prev, null);
         }
-        return mService.startHomeActivityLocked(mCurrentUser, myReason);
+        return mService.mAm.startHomeActivityLocked(mCurrentUser, myReason);
     }
 
     TaskRecord anyTaskForIdLocked(int id) {
@@ -904,7 +958,7 @@
                         // result to an activity belonging to userId. Example case: a document
                         // picker for personal files, opened by a work app, should still get locked.
                         if (taskTopActivityIsUser(task, userId)) {
-                            mService.mTaskChangeNotificationController.notifyTaskProfileLocked(
+                            mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
                                     task.taskId, userId);
                         }
                     }
@@ -953,21 +1007,6 @@
         return candidateTaskId;
     }
 
-    ActivityRecord getResumedActivityLocked() {
-        ActivityStack stack = mFocusedStack;
-        if (stack == null) {
-            return null;
-        }
-        ActivityRecord resumedActivity = stack.getResumedActivity();
-        if (resumedActivity == null || resumedActivity.app == null) {
-            resumedActivity = stack.mPausingActivity;
-            if (resumedActivity == null || resumedActivity.app == null) {
-                resumedActivity = stack.topRunningActivityLocked();
-            }
-        }
-        return resumedActivity;
-    }
-
     boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
         final String processName = app.processName;
         boolean didSomething = false;
@@ -975,7 +1014,7 @@
             final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
             for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = display.getChildAt(stackNdx);
-                if (!isFocusedStack(stack)) {
+                if (!isTopDisplayFocusedStack(stack)) {
                     continue;
                 }
                 stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
@@ -1010,7 +1049,7 @@
             final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
             for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = display.getChildAt(stackNdx);
-                if (!isFocusedStack(stack) || stack.numActivities() == 0) {
+                if (!isTopDisplayFocusedStack(stack) || stack.numActivities() == 0) {
                     continue;
                 }
                 final ActivityRecord resumedActivity = stack.getResumedActivity();
@@ -1031,7 +1070,7 @@
             final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
             for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = display.getChildAt(stackNdx);
-                if (isFocusedStack(stack)) {
+                if (isTopDisplayFocusedStack(stack)) {
                     final ActivityRecord r = stack.getResumedActivity();
                     if (r != null && !r.isState(RESUMED)) {
                         return false;
@@ -1040,10 +1079,11 @@
             }
         }
         // TODO: Not sure if this should check if all Paused are complete too.
+        final ActivityStack focusedStack = getTopDisplayFocusedStack();
         if (DEBUG_STACK) Slog.d(TAG_STACK,
-                "allResumedActivitiesComplete: mLastFocusedStack changing from=" +
-                mLastFocusedStack + " to=" + mFocusedStack);
-        mLastFocusedStack = mFocusedStack;
+                "allResumedActivitiesComplete: mLastFocusedStack changing from="
+                        + mLastFocusedStack + " to=" + focusedStack);
+        mLastFocusedStack = focusedStack;
         return true;
     }
 
@@ -1076,16 +1116,8 @@
     boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
         boolean someActivityPaused = false;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (!isFocusedStack(stack) && stack.getResumedActivity() != null) {
-                    if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
-                            " mResumedActivity=" + stack.getResumedActivity());
-                    someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
-                            dontWait);
-                }
-            }
+            someActivityPaused |= mActivityDisplays.valueAt(displayNdx)
+                    .pauseBackStacks(userLeaving, resuming, dontWait);
         }
         return someActivityPaused;
     }
@@ -1159,7 +1191,7 @@
             }
         }
         if (changed) {
-            mService.notifyAll();
+            mService.mGlobalLock.notifyAll();
         }
     }
 
@@ -1189,7 +1221,7 @@
         }
 
         if (changed) {
-            mService.notifyAll();
+            mService.mGlobalLock.notifyAll();
         }
     }
 
@@ -1210,7 +1242,7 @@
             }
         }
         if (changed) {
-            mService.notifyAll();
+            mService.mGlobalLock.notifyAll();
         }
     }
 
@@ -1227,7 +1259,7 @@
      * @return The top running activity. {@code null} if none is available.
      */
     ActivityRecord topRunningActivityLocked(boolean considerKeyguardState) {
-        final ActivityStack focusedStack = mFocusedStack;
+        final ActivityStack focusedStack = getTopDisplayFocusedStack();
         ActivityRecord r = focusedStack.topRunningActivityLocked();
         if (r != null && isValidTopRunningActivity(r, considerKeyguardState)) {
             return r;
@@ -1315,19 +1347,19 @@
             // Don't debug things in the system process
             if (!aInfo.processName.equals("system")) {
                 if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
-                    mService.setDebugApp(aInfo.processName, true, false);
+                    mService.mAm.setDebugApp(aInfo.processName, true, false);
                 }
 
                 if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
-                    mService.setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
+                    mService.mAm.setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
                 }
 
                 if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
-                    mService.setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
+                    mService.mAm.setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
                 }
 
                 if (profilerInfo != null) {
-                    mService.setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
+                    mService.mAm.setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
                 }
             }
             final String intentLaunchToken = intent.getLaunchToken();
@@ -1340,7 +1372,7 @@
 
     ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
             int filterCallingUid) {
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             try {
                 Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
                 int modifiedFlags = flags
@@ -1393,12 +1425,13 @@
         beginDeferResume();
 
         try {
-            r.startFreezingScreenLocked(app, 0);
+            final WindowProcessController proc = app.getWindowProcessController();
+            r.startFreezingScreenLocked(proc, 0);
 
             // schedule launch ticks to collect information about slow apps.
             r.startLaunchTickingLocked();
 
-            r.setProcess(app);
+            r.setProcess(proc);
 
             if (getKeyguardController().isKeyguardLocked()) {
                 r.notifyUnknownVisibilityLaunched();
@@ -1442,12 +1475,8 @@
 
             if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
 
-            int idx = app.activities.indexOf(r);
-            if (idx < 0) {
-                app.activities.add(r);
-            }
-            mService.updateLruProcessLocked(app, true, null);
-            mService.updateOomAdjLocked();
+            proc.addActivityIfNeeded(r);
+            proc.updateProcessInfo(false, true, true, true);
 
             final LockTaskController lockTaskController = mService.getLockTaskController();
             if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
@@ -1479,18 +1508,17 @@
                     // Home process is the root process of the task.
                     mService.mHomeProcess = task.mActivities.get(0).app;
                 }
-                mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
+                mService.mAm.notifyPackageUse(r.intent.getComponent().getPackageName(),
                         PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY);
                 r.sleeping = false;
                 r.forceNewConfig = false;
                 mService.getAppWarningsLocked().onStartActivity(r);
-                mService.showAskCompatModeDialogLocked(r);
-                r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
+                r.compat = mService.mAm.compatibilityInfoForPackageLocked(r.info.applicationInfo);
                 ProfilerInfo profilerInfo = null;
-                if (mService.mProfileApp != null && mService.mProfileApp.equals(app.processName)) {
-                    if (mService.mProfileProc == null || mService.mProfileProc == app) {
-                        mService.mProfileProc = app;
-                        ProfilerInfo profilerInfoSvc = mService.mProfilerInfo;
+                if (mService.mAm.mProfileApp != null && mService.mAm.mProfileApp.equals(app.processName)) {
+                    if (mService.mAm.mProfileProc == null || mService.mAm.mProfileProc == app) {
+                        mService.mAm.mProfileProc = app;
+                        ProfilerInfo profilerInfoSvc = mService.mAm.mProfilerInfo;
                         if (profilerInfoSvc != null && profilerInfoSvc.profileFile != null) {
                             if (profilerInfoSvc.profileFd != null) {
                                 try {
@@ -1528,9 +1556,9 @@
                         // and override configs.
                         mergedConfiguration.getGlobalConfiguration(),
                         mergedConfiguration.getOverrideConfiguration(), r.compat,
-                        r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
-                        r.persistentState, results, newIntents, mService.isNextTransitionForward(),
-                        profilerInfo));
+                        r.launchedFromPackage, task.voiceInteractor, app.getReportedProcState(),
+                        r.icicle, r.persistentState, results, newIntents,
+                        mService.isNextTransitionForward(), profilerInfo));
 
                 // Set desired final state.
                 final ActivityLifecycleItem lifecycleItem;
@@ -1544,25 +1572,24 @@
                 // Schedule transaction.
                 mService.getLifecycleManager().scheduleTransaction(clientTransaction);
 
-
                 if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
-                        && mService.mHasHeavyWeightFeature) {
+                        && mService.mAm.mHasHeavyWeightFeature) {
                     // This may be a heavy-weight process!  Note that the package
                     // manager will ensure that only activity can run in the main
                     // process of the .apk, which is the only thing that will be
                     // considered heavy-weight.
                     if (app.processName.equals(app.info.packageName)) {
-                        if (mService.mHeavyWeightProcess != null
-                                && mService.mHeavyWeightProcess != app) {
+                        if (mService.mAm.mHeavyWeightProcess != null
+                                && mService.mAm.mHeavyWeightProcess != app) {
                             Slog.w(TAG, "Starting new heavy weight process " + app
                                     + " when already running "
-                                    + mService.mHeavyWeightProcess);
+                                    + mService.mAm.mHeavyWeightProcess);
                         }
-                        mService.mHeavyWeightProcess = app;
-                        Message msg = mService.mHandler.obtainMessage(
+                        mService.mAm.mHeavyWeightProcess = app;
+                        Message msg = mService.mAm.mHandler.obtainMessage(
                                 ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
                         msg.obj = r;
-                        mService.mHandler.sendMessage(msg);
+                        mService.mAm.mHandler.sendMessage(msg);
                     }
                 }
 
@@ -1573,7 +1600,7 @@
                     Slog.e(TAG, "Second failure launching "
                             + r.intent.getComponent().flattenToShortString()
                             + ", giving up", e);
-                    mService.appDiedLocked(app);
+                    mService.mAm.appDiedLocked(app);
                     stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
                             "2nd-crash", false);
                     return false;
@@ -1582,7 +1609,7 @@
                 // This is the first time we failed -- restart process and
                 // retry.
                 r.launchFailed = true;
-                app.activities.remove(r);
+                proc.removeActivity(r);
                 throw e;
             }
         } finally {
@@ -1613,14 +1640,14 @@
         // launching the initial activity (that is, home), so that it can have
         // a chance to initialize itself while in the background, making the
         // switch back to it faster and look better.
-        if (isFocusedStack(stack)) {
+        if (isTopDisplayFocusedStack(stack)) {
             mService.getActivityStartController().startSetupActivity();
         }
 
         // Update any services we are bound to that might care about whether
         // their client may have activities.
         if (r.app != null) {
-            mService.mServices.updateServiceConnectionActivitiesLocked(r.app);
+            r.app.updateServiceConnectionActivities();
         }
 
         return true;
@@ -1634,6 +1661,8 @@
      * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
      *                                  {@code true} if config changed.
      * @param deferResume Whether to defer resume while updating config.
+     * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
+     *         because of configuration update.
      */
     boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
             boolean markFrozenIfConfigChanged, boolean deferResume) {
@@ -1644,6 +1673,11 @@
         ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
                 false /* preserveWindows */, false /* notifyClients */);
 
+        if (displayId == INVALID_DISPLAY) {
+            // The caller didn't provide a valid display id, skip updating config.
+            return true;
+        }
+
         // Force-update the orientation from the WindowManager, since we need the true configuration
         // to send to the client now.
         final Configuration config = mWindowManager.updateOrientationFromAppTokens(
@@ -1678,7 +1712,7 @@
     void startSpecificActivityLocked(ActivityRecord r,
             boolean andResume, boolean checkConfig) {
         // Is this activity's application already running?
-        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
+        ProcessRecord app = mService.mAm.getProcessRecordLocked(r.processName,
                 r.info.applicationInfo.uid, true);
 
         getLaunchTimeTracker().setLaunchTime(r);
@@ -1692,7 +1726,7 @@
                     // part of the framework so doesn't make sense to track as a
                     // separate apk in the process.
                     app.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode,
-                            mService.mProcessStats);
+                            mService.mAm.mProcessStats);
                 }
                 realStartActivityLocked(r, app, andResume, checkConfig);
                 return;
@@ -1705,7 +1739,7 @@
             // restart the application.
         }
 
-        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
+        mService.mAm.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                 "activity", r.intent.getComponent(), false, false, true);
     }
 
@@ -1713,24 +1747,39 @@
         boolean sendHint = forceSend;
 
         if (!sendHint) {
-            // If not forced, send power hint when the activity's process is different than the
-            // current resumed activity.
-            final ActivityRecord resumedActivity = getResumedActivityLocked();
-            sendHint = resumedActivity == null
-                    || resumedActivity.app == null
-                    || !resumedActivity.app.equals(targetActivity.app);
+            // Send power hint if we don't know what we're launching yet
+            sendHint = targetActivity == null || targetActivity.app == null;
         }
 
-        if (sendHint && mService.mLocalPowerManager != null) {
-            mService.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 1);
+        if (!sendHint) { // targetActivity != null
+            // Send power hint when the activity's process is different than the current resumed
+            // activity on all displays, or if there are no resumed activities in the system.
+            boolean noResumedActivities = true;
+            boolean allFocusedProcessesDiffer = true;
+            for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+                final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+                final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
+                final WindowProcessController resumedActivityProcess =
+                    resumedActivity == null ? null : resumedActivity.app;
+
+                noResumedActivities &= resumedActivityProcess == null;
+                if (resumedActivityProcess != null) {
+                    allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
+                }
+            }
+            sendHint = noResumedActivities || allFocusedProcessesDiffer;
+        }
+
+        if (sendHint && mService.mAm.mLocalPowerManager != null) {
+            mService.mAm.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 1);
             mPowerHintSent = true;
         }
     }
 
     void sendPowerHintForLaunchEndIfNeeded() {
         // Trigger launch power hint if activity is launched
-        if (mPowerHintSent && mService.mLocalPowerManager != null) {
-            mService.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 0);
+        if (mPowerHintSent && mService.mAm.mLocalPowerManager != null) {
+            mService.mAm.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 0);
             mPowerHintSent = false;
         }
     }
@@ -1739,8 +1788,8 @@
             String resultWho, int requestCode, int callingPid, int callingUid,
             String callingPackage, boolean ignoreTargetSecurity, boolean launchingInTask,
             ProcessRecord callerApp, ActivityRecord resultRecord, ActivityStack resultStack) {
-        final boolean isCallerRecents = mService.getRecentTasks() != null &&
-                mService.getRecentTasks().isCallerRecents(callingUid);
+        final boolean isCallerRecents = mService.getRecentTasks() != null
+                && mService.getRecentTasks().isCallerRecents(callingUid);
         final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
                 callingUid);
         if (startAnyPerm == PERMISSION_GRANTED || (isCallerRecents && launchingInTask)) {
@@ -1900,7 +1949,7 @@
 
     private int getComponentRestrictionForCallingPackage(ActivityInfo activityInfo,
             String callingPackage, int callingPid, int callingUid, boolean ignoreTargetSecurity) {
-        if (!ignoreTargetSecurity && mService.checkComponentPermission(activityInfo.permission,
+        if (!ignoreTargetSecurity && mService.mAm.checkComponentPermission(activityInfo.permission,
                 callingPid, callingUid, activityInfo.applicationInfo.uid, activityInfo.exported)
                 == PERMISSION_DENIED) {
             return ACTIVITY_RESTRICTION_PERMISSION;
@@ -1915,8 +1964,8 @@
             return ACTIVITY_RESTRICTION_NONE;
         }
 
-        if (mService.mAppOpsService.noteOperation(opCode, callingUid,
-                callingPackage) != AppOpsManager.MODE_ALLOWED) {
+        if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage)
+                != AppOpsManager.MODE_ALLOWED) {
             if (!ignoreTargetSecurity) {
                 return ACTIVITY_RESTRICTION_APPOP;
             }
@@ -1958,8 +2007,8 @@
             return ACTIVITY_RESTRICTION_NONE;
         }
 
-        if (mService.mAppOpsService.noteOperation(opCode, callingUid,
-                callingPackage) != AppOpsManager.MODE_ALLOWED) {
+        if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage)
+                != AppOpsManager.MODE_ALLOWED) {
             return ACTIVITY_RESTRICTION_APPOP;
         }
 
@@ -1983,19 +2032,19 @@
 
     /**
      * Called when the frontmost task is idle.
-     * @return the state of mService.mBooting before this was called.
+     * @return the state of mService.mAm.mBooting before this was called.
      */
     @GuardedBy("mService")
     private boolean checkFinishBootingLocked() {
-        final boolean booting = mService.mBooting;
+        final boolean booting = mService.mAm.mBooting;
         boolean enableScreen = false;
-        mService.mBooting = false;
-        if (!mService.mBooted) {
-            mService.mBooted = true;
+        mService.mAm.mBooting = false;
+        if (!mService.mAm.mBooted) {
+            mService.mAm.mBooted = true;
             enableScreen = true;
         }
         if (booting || enableScreen) {
-            mService.postFinishBooting(booting, enableScreen);
+            mService.mAm.postFinishBooting(booting, enableScreen);
         }
         return booting;
     }
@@ -2037,9 +2086,13 @@
             r.idle = true;
 
             //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
-            if (isFocusedStack(r.getStack()) || fromTimeout) {
+            if (isTopDisplayFocusedStack(r.getStack()) || fromTimeout) {
                 booting = checkFinishBootingLocked();
             }
+
+            // When activity is idle, we consider the relaunch must be successful, so let's clear
+            // the flag.
+            r.mRelaunchReason = RELAUNCH_REASON_NONE;
         }
 
         if (allResumedActivitiesIdle()) {
@@ -2101,23 +2154,23 @@
             // Complete user switch
             if (startingUsers != null) {
                 for (int i = 0; i < startingUsers.size(); i++) {
-                    mService.mUserController.finishUserSwitch(startingUsers.get(i));
+                    mService.mAmInternal.finishUserSwitch(startingUsers.get(i));
                 }
             }
         }
 
-        mService.trimApplications();
+        mService.mAmInternal.trimApplications();
         //dump();
         //mWindowManager.dump();
 
         if (activityRemoved) {
-            resumeFocusedStackTopActivityLocked();
+            resumeFocusedStacksTopActivitiesLocked();
         }
 
         return r;
     }
 
-    boolean handleAppDiedLocked(ProcessRecord app) {
+    boolean handleAppDiedLocked(WindowProcessController app) {
         boolean hasVisibleActivities = false;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
@@ -2180,12 +2233,12 @@
         // First, found out what is currently the foreground app, so that
         // we don't blow away the previous app if this activity is being
         // hosted by the process that is actually still the foreground.
-        ProcessRecord fgApp = null;
+        WindowProcessController fgApp = null;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
             for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = display.getChildAt(stackNdx);
-                if (isFocusedStack(stack)) {
+                if (isTopDisplayFocusedStack(stack)) {
                     final ActivityRecord resumedActivity = stack.getResumedActivity();
                     if (resumedActivity != null) {
                         fgApp = resumedActivity.app;
@@ -2199,7 +2252,7 @@
 
         // Now set this one as the previous process, only if that really
         // makes sense to.
-        if (r.app != null && fgApp != null && r.app != fgApp
+        if (r.hasProcess() && fgApp != null && r.app != fgApp
                 && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
                 && r.app != mService.mHomeProcess) {
             mService.mPreviousProcess = r.app;
@@ -2207,27 +2260,35 @@
         }
     }
 
-    boolean resumeFocusedStackTopActivityLocked() {
-        return resumeFocusedStackTopActivityLocked(null, null, null);
+    boolean resumeFocusedStacksTopActivitiesLocked() {
+        return resumeFocusedStacksTopActivitiesLocked(null, null, null);
     }
 
-    boolean resumeFocusedStackTopActivityLocked(
+    boolean resumeFocusedStacksTopActivitiesLocked(
             ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
 
         if (!readyToResume()) {
             return false;
         }
 
-        if (targetStack != null && isFocusedStack(targetStack)) {
+        if (targetStack != null && targetStack.isTopStackOnDisplay()) {
             return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
         }
 
-        final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
-        if (r == null || !r.isState(RESUMED)) {
-            mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
-        } else if (r.isState(RESUMED)) {
-            // Kick off any lingering app transitions form the MoveTaskToFront operation.
-            mFocusedStack.executeAppTransition(targetOptions);
+        // Resume all top activities in focused stacks on all displays.
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+            final ActivityStack focusedStack = display.getFocusedStack();
+            if (focusedStack == null) {
+                continue;
+            }
+            final ActivityRecord r = focusedStack.topRunningActivityLocked();
+            if (r == null || !r.isState(RESUMED)) {
+                focusedStack.resumeTopActivityUncheckedLocked(null, null);
+            } else if (r.isState(RESUMED)) {
+                // Kick off any lingering app transitions form the MoveTaskToFront operation.
+                focusedStack.executeAppTransition(targetOptions);
+            }
         }
 
         return false;
@@ -2249,9 +2310,9 @@
      * @param reason Reason to perform this action.
      * @return The task that was finished in this stack, {@code null} if haven't found any.
      */
-    TaskRecord finishTopCrashedActivitiesLocked(ProcessRecord app, String reason) {
+    TaskRecord finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
         TaskRecord finishedTask = null;
-        ActivityStack focusedStack = getFocusedStack();
+        ActivityStack focusedStack = getTopDisplayFocusedStack();
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
             // It is possible that request to finish activity might also remove its task and stack,
@@ -2278,6 +2339,9 @@
         }
     }
 
+    /**
+     * This doesn't just find a task, it also moves the task to front.
+     */
     void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
             boolean forceNonResizeable) {
         final ActivityStack currentStack = task.getStack();
@@ -2441,8 +2505,7 @@
         }
         if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
             if (r != null) {
-                // TODO: This should also take in the windowing mode and activity type into account.
-                stack = (T) getValidLaunchStackOnDisplay(displayId, r);
+                stack = (T) getValidLaunchStackOnDisplay(displayId, r, options);
                 if (stack != null) {
                     return stack;
                 }
@@ -2487,12 +2550,7 @@
             }
         }
 
-        if (display == null
-                || !canLaunchOnDisplay(r, display.mDisplayId)
-                // TODO: Can be removed once we figure-out how non-standard types should launch
-                // outside the default display.
-                || (activityType != ACTIVITY_TYPE_STANDARD
-                && activityType != ACTIVITY_TYPE_UNDEFINED)) {
+        if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
             display = getDefaultDisplay();
         }
 
@@ -2514,7 +2572,8 @@
      * @param r Activity that should be launched there.
      * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
      */
-    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r) {
+    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+            @Nullable ActivityOptions options) {
         final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
         if (activityDisplay == null) {
             throw new IllegalArgumentException(
@@ -2536,7 +2595,9 @@
         // If there is no valid stack on the external display - check if new dynamic stack will do.
         if (displayId != DEFAULT_DISPLAY) {
             return activityDisplay.createStack(
-                    r.getWindowingMode(), r.getActivityType(), true /*onTop*/);
+                    options != null ? options.getLaunchWindowingMode() : r.getWindowingMode(),
+                    options != null ? options.getLaunchActivityType() : r.getActivityType(),
+                    true /*onTop*/);
         }
 
         Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
@@ -2550,74 +2611,58 @@
             case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
             case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
         }
-        switch (stack.getWindowingMode()) {
-            case WINDOWING_MODE_FULLSCREEN: return true;
-            case WINDOWING_MODE_FREEFORM: return r.supportsFreeform();
-            case WINDOWING_MODE_PINNED: return r.supportsPictureInPicture();
-            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return r.supportsSplitScreenWindowingMode();
-            case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return r.supportsSplitScreenWindowingMode();
+        // 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.isOnHomeDisplay()) {
-            return r.canBeLaunchedOnDisplay(displayId);
-        }
-        Slog.e(TAG, "isValidLaunchStack: Unexpected stack=" + stack);
-        return false;
     }
 
     /**
-     * Get next focusable stack in the system. This will search across displays and stacks
-     * in last-focused order for a focusable and visible stack, different from the target stack.
+     * Get next focusable stack in the system. This will search through the stack on the same
+     * display as the current focused stack, looking for a focusable and visible stack, different
+     * from the target stack. If no valid candidates will be found, it will then go through all
+     * displays and stacks in last-focused order.
      *
      * @param currentFocus The stack that previously had focus.
      * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next
      *                     candidate.
-     * @return Next focusable {@link ActivityStack}, null if not found.
+     * @return Next focusable {@link ActivityStack}, {@code null} if not found.
      */
-    ActivityStack getNextFocusableStackLocked(ActivityStack currentFocus, boolean ignoreCurrent) {
-        mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
+    ActivityStack getNextFocusableStackLocked(@NonNull ActivityStack currentFocus,
+            boolean ignoreCurrent) {
+        // First look for next focusable stack on the same display
+        final ActivityDisplay preferredDisplay = currentFocus.getDisplay();
+        final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack(
+                currentFocus, ignoreCurrent);
+        if (preferredFocusableStack != null) {
+            return preferredFocusableStack;
+        }
 
-        final int currentWindowingMode = currentFocus != null
-                ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
-        ActivityStack candidate = null;
+        // Now look through all displays
+        mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
         for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
             final int displayId = mTmpOrderedDisplayIds.get(i);
+            if (displayId == preferredDisplay.mDisplayId) {
+                // We've already checked this one
+                continue;
+            }
             // If a display is registered in WM, it must also be available in AM.
             final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
             if (display == null) {
                 // Looks like the display no longer exists in the system...
                 continue;
             }
-            for (int j = display.getChildCount() - 1; j >= 0; --j) {
-                final ActivityStack stack = display.getChildAt(j);
-                if (ignoreCurrent && stack == currentFocus) {
-                    continue;
-                }
-                if (!stack.isFocusable() || !stack.shouldBeVisible(null)) {
-                    continue;
-                }
-
-                if (currentWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                        && candidate == null && stack.inSplitScreenPrimaryWindowingMode()) {
-                    // If the currently focused stack is in split-screen secondary we save off the
-                    // top primary split-screen stack as a candidate for focus because we might
-                    // prefer focus to move to an other stack to avoid primary split-screen stack
-                    // overlapping with a fullscreen stack when a fullscreen stack is higher in z
-                    // than the next split-screen stack. Assistant stack, I am looking at you...
-                    // We only move the focus to the primary-split screen stack if there isn't a
-                    // better alternative.
-                    candidate = stack;
-                    continue;
-                }
-                if (candidate != null && stack.inSplitScreenSecondaryWindowingMode()) {
-                    // Use the candidate stack since we are now at the secondary split-screen.
-                    return candidate;
-                }
-                return stack;
+            final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
+                    ignoreCurrent);
+            if (nextFocusableStack != null) {
+                return nextFocusableStack;
             }
         }
 
-        return candidate;
+        return null;
     }
 
     /**
@@ -2637,7 +2682,8 @@
             if (displayId == currentFocus) {
                 continue;
             }
-            final ActivityStack stack = getValidLaunchStackOnDisplay(displayId, r);
+            final ActivityStack stack = getValidLaunchStackOnDisplay(displayId, r,
+                    null /* options */);
             if (stack != null) {
                 return stack;
             }
@@ -2815,7 +2861,7 @@
             }
 
             ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-            resumeFocusedStackTopActivityLocked();
+            resumeFocusedStacksTopActivitiesLocked();
         } finally {
             mAllowDockedStackResize = true;
             mWindowManager.continueSurfaceLayout();
@@ -3091,7 +3137,7 @@
         }
 
         // Find any running services associated with this app and stop if needed.
-        mService.mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
+        mService.mAm.mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
 
         if (!killProcess) {
             return;
@@ -3099,14 +3145,15 @@
 
         // Determine if the process(es) for this task should be killed.
         final String pkg = component.getPackageName();
-        ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
-        ArrayMap<String, SparseArray<ProcessRecord>> pmap = mService.mProcessNames.getMap();
+        ArrayList<Object> procsToKill = new ArrayList<>();
+        ArrayMap<String, SparseArray<WindowProcessController>> pmap =
+                mService.mProcessNames.getMap();
         for (int i = 0; i < pmap.size(); i++) {
 
-            SparseArray<ProcessRecord> uids = pmap.valueAt(i);
+            SparseArray<WindowProcessController> uids = pmap.valueAt(i);
             for (int j = 0; j < uids.size(); j++) {
-                ProcessRecord proc = uids.valueAt(j);
-                if (proc.userId != tr.userId) {
+                WindowProcessController proc = uids.valueAt(j);
+                if (proc.mUserId != tr.userId) {
                     // Don't kill process for a different user.
                     continue;
                 }
@@ -3114,21 +3161,18 @@
                     // Don't kill the home process along with tasks from the same package.
                     continue;
                 }
-                if (!proc.pkgList.containsKey(pkg)) {
+                if (!proc.mPkgList.contains(pkg)) {
                     // Don't kill process that is not associated with this task.
                     continue;
                 }
 
-                for (int k = 0; k < proc.activities.size(); k++) {
-                    TaskRecord otherTask = proc.activities.get(k).getTask();
-                    if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
-                        // Don't kill process(es) that has an activity in a different task that is
-                        // also in recents.
-                        return;
-                    }
+                if (!proc.shouldKillProcessForRemovedTask(tr)) {
+                    // Don't kill process(es) that has an activity in a different task that is also
+                    // in recents, or has an activity not stopped.
+                    return;
                 }
 
-                if (proc.foregroundServices) {
+                if (proc.hasForegroundServices()) {
                     // Don't kill process(es) with foreground service.
                     return;
                 }
@@ -3138,17 +3182,12 @@
             }
         }
 
-        // Kill the running processes.
-        for (int i = 0; i < procsToKill.size(); i++) {
-            ProcessRecord pr = procsToKill.get(i);
-            if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                    && pr.curReceivers.isEmpty()) {
-                pr.kill("remove task", true);
-            } else {
-                // We delay killing processes that are not in the background or running a receiver.
-                pr.waitingToKill = "remove task";
-            }
-        }
+        // Kill the running processes. Post on handle since we don't want to hold the service lock
+        // while calling into AM.
+        final Runnable r = PooledLambda.obtainRunnable(
+                ActivityManagerInternal::killProcessesForRemovedTask, mService.mAmInternal,
+                procsToKill);
+        mService.mH.post(r);
     }
 
     /**
@@ -3191,12 +3230,12 @@
     }
 
     @Override
-    public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) {
+    public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
         if (wasTrimmed) {
             // Task was trimmed from the recent tasks list -- remove the active task record as well
             // since the user won't really be able to go back to it
-            removeTaskByIdLocked(task.taskId, false /* killProcess */,
-                    false /* removeFromRecents */, !PAUSE_IMMEDIATELY, "recent-task-trimmed");
+            removeTaskByIdLocked(task.taskId, killProcess, false /* removeFromRecents */,
+                    !PAUSE_IMMEDIATELY, "recent-task-trimmed");
         }
         task.removedFromRecents();
     }
@@ -3379,9 +3418,9 @@
         // drawn signal is scheduled after the bounds animation start call on the bounds animator
         // thread.
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        resumeFocusedStackTopActivityLocked();
+        resumeFocusedStacksTopActivitiesLocked();
 
-        mService.mTaskChangeNotificationController.notifyActivityPinned(r);
+        mService.getTaskChangeNotificationController().notifyActivityPinned(r);
     }
 
     /** Move activity with its stack to front and make the stack focused. */
@@ -3400,7 +3439,7 @@
             return false;
         }
 
-        if (stack == mFocusedStack && stack.topRunningActivityLocked() == r) {
+        if (r == getTopResumedActivity()) {
             if (DEBUG_FOCUS) Slog.d(TAG_FOCUS,
                     "moveActivityStackToFront: already on top, r=" + r);
             return false;
@@ -3410,6 +3449,11 @@
                 "moveActivityStackToFront: r=" + r);
 
         stack.moveToFront(reason, task);
+        // Report top activity change to tracking services and WM
+        if (r == getTopResumedActivity()) {
+            // TODO(b/111361570): Support multiple focused apps in WM
+            mService.setResumedActivityUncheckLocked(r, reason);
+        }
         return true;
     }
 
@@ -3488,7 +3532,7 @@
                     throw new IllegalStateException("Calling must be system uid");
                 }
                 mLaunchingActivity.release();
-                mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+                mService.mAm.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
             }
         }
 
@@ -3513,7 +3557,7 @@
                 long timeRemaining = endTime - System.currentTimeMillis();
                 if (timeRemaining > 0) {
                     try {
-                        mService.wait(timeRemaining);
+                        mService.mGlobalLock.wait(timeRemaining);
                     } catch (InterruptedException e) {
                     }
                 } else {
@@ -3560,13 +3604,13 @@
                     stack.goToSleepIfPossible(false /* shuttingDown */);
                 } else {
                     stack.awakeFromSleepingLocked();
-                    if (isFocusedStack(stack) && !getKeyguardController().isKeyguardOrAodShowing(
-                            display.mDisplayId)) {
+                    if (isTopDisplayFocusedStack(stack) && !getKeyguardController()
+                            .isKeyguardOrAodShowing(display.mDisplayId)) {
                         // If the keyguard is unlocked - resume immediately.
                         // It is possible that the display will not be awake at the time we
                         // process the keyguard going away, which can happen before the sleep token
                         // is released. As a result, it is important we resume the activity here.
-                        resumeFocusedStackTopActivityLocked();
+                        resumeFocusedStacksTopActivitiesLocked();
                     }
                 }
             }
@@ -3613,7 +3657,7 @@
             mGoingToSleep.release();
         }
         if (mService.mShuttingDown) {
-            mService.notifyAll();
+            mService.mGlobalLock.notifyAll();
         }
     }
 
@@ -3640,8 +3684,8 @@
         mStoppingActivities.remove(r);
 
         final ActivityStack stack = r.getStack();
-        if (isFocusedStack(stack)) {
-            mService.updateUsageStats(r, true);
+        if (isTopDisplayFocusedStack(stack)) {
+            mService.mAm.updateUsageStats(r, true);
         }
         if (allResumedActivitiesComplete()) {
             ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
@@ -3651,7 +3695,7 @@
         return false;
     }
 
-    void handleAppCrashLocked(ProcessRecord app) {
+    void handleAppCrashLocked(WindowProcessController app) {
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
             for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
@@ -3668,7 +3712,7 @@
 
         r.mLaunchTaskBehind = false;
         mRecentTasks.add(task);
-        mService.mTaskChangeNotificationController.notifyTaskStackChanged();
+        mService.getTaskChangeNotificationController().notifyTaskStackChanged();
         r.setVisibility(false);
 
         // When launching tasks behind, update the last active time of the top task after the new
@@ -3753,7 +3797,7 @@
         }
     }
 
-    void scheduleDestroyAllActivities(ProcessRecord app, String reason) {
+    void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
             for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
@@ -3763,44 +3807,9 @@
         }
     }
 
-    void releaseSomeActivitiesLocked(ProcessRecord app, String reason) {
-        // Examine all activities currently running in the process.
-        TaskRecord firstTask = null;
+    void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
         // Tasks is non-null only if two or more tasks are found.
-        ArraySet<TaskRecord> tasks = null;
-        if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + app);
-        for (int i = 0; i < app.activities.size(); i++) {
-            ActivityRecord r = app.activities.get(i);
-            // First, if we find an activity that is in the process of being destroyed,
-            // then we just aren't going to do anything for now; we want things to settle
-            // down before we try to prune more activities.
-            if (r.finishing || r.isState(DESTROYING, DESTROYED)) {
-                if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r);
-                return;
-            }
-            // Don't consider any activies that are currently not in a state where they
-            // can be destroyed.
-            if (r.visible || !r.stopped || !r.haveState
-                    || r.isState(RESUMED, PAUSING, PAUSED, STOPPING)) {
-                if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
-                continue;
-            }
-
-            final TaskRecord task = r.getTask();
-            if (task != null) {
-                if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task
-                        + " from " + r);
-                if (firstTask == null) {
-                    firstTask = task;
-                } else if (firstTask != task) {
-                    if (tasks == null) {
-                        tasks = new ArraySet<>();
-                        tasks.add(firstTask);
-                    }
-                    tasks.add(task);
-                }
-            }
-        }
+        ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks();
         if (tasks == null) {
             if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
             return;
@@ -3823,11 +3832,11 @@
     }
 
     boolean switchUserLocked(int userId, UserState uss) {
-        final int focusStackId = mFocusedStack.getStackId();
+        final int focusStackId = getTopDisplayFocusedStack().getStackId();
         // We dismiss the docked stack whenever we switch users.
         final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
         if (dockedStack != null) {
-            moveTasksToFullscreenStackLocked(dockedStack, mFocusedStack == dockedStack);
+            moveTasksToFullscreenStackLocked(dockedStack, dockedStack.isFocusedStackOnDisplay());
         }
         // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
         // also cause all tasks to be moved to the fullscreen stack at a position that is
@@ -3868,7 +3877,7 @@
     /** Checks whether the userid is a profile of the current user. */
     boolean isCurrentProfileLocked(int userId) {
         if (userId == mCurrentUser) return true;
-        return mService.mUserController.isCurrentProfile(userId);
+        return mService.mAmInternal.isCurrentProfile(userId);
     }
 
     /**
@@ -3930,6 +3939,10 @@
                         stops = new ArrayList<>();
                     }
                     stops.add(s);
+
+                    // Make sure to remove it in all cases in case we entered this block with
+                    // shouldSleepOrShutDown
+                    mActivitiesWaitingForVisibleActivity.remove(s);
                     mStoppingActivities.remove(activityNdx);
                 }
             }
@@ -3945,7 +3958,7 @@
                 final ActivityStack stack = display.getChildAt(stackNdx);
                 final ActivityRecord r = stack.topRunningActivityLocked();
                 final ActivityState state = r == null ? DESTROYED : r.getState();
-                if (isFocusedStack(stack)) {
+                if (isTopDisplayFocusedStack(stack)) {
                     if (r == null) Slog.e(TAG,
                             "validateTop...: null top activity, stack=" + stack);
                     else {
@@ -3979,7 +3992,7 @@
     }
 
     public void dump(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack);
+        pw.print(prefix); pw.print("mFocusedStack=" + getTopDisplayFocusedStack());
                 pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
         pw.print(prefix);
         pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
@@ -4005,13 +4018,15 @@
         final long token = proto.start(fieldId);
         super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
         for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
-            ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+            final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
             activityDisplay.writeToProto(proto, DISPLAYS);
         }
         getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
-        if (mFocusedStack != null) {
-            proto.write(FOCUSED_STACK_ID, mFocusedStack.mStackId);
-            ActivityRecord focusedActivity = getResumedActivityLocked();
+        // TODO(b/111541062): Update tests to look for resumed activities on all displays
+        final ActivityStack focusedStack = getTopDisplayFocusedStack();
+        if (focusedStack != null) {
+            proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+            final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
             if (focusedActivity != null) {
                 focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
             }
@@ -4020,6 +4035,7 @@
         }
         proto.write(IS_HOME_RECENTS_COMPONENT,
                 mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+        mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES);
         proto.end(token);
     }
 
@@ -4044,7 +4060,7 @@
     ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, boolean dumpVisibleStacksOnly,
             boolean dumpFocusedStackOnly) {
         if (dumpFocusedStackOnly) {
-            return mFocusedStack.getDumpActivitiesLocked(name);
+            return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
         } else {
             ArrayList<ActivityRecord> activities = new ArrayList<>();
             int numDisplays = mActivityDisplays.size();
@@ -4126,6 +4142,8 @@
                 }
                 needSep = printed;
             }
+            printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
+                    " ResumedActivity:");
         }
 
         printed |= dumpHistoryList(fd, pw, mFinishingActivities, "  ", "Fin", false, !dumpAll,
@@ -4193,16 +4211,16 @@
                     pw.print(innerPrefix); pw.println(r.app);
                 }
             }
-            if (client && r.app != null && r.app.thread != null) {
+            if (client && r.attachedToProcess()) {
                 // flush anything that is already in the PrintWriter since the thread is going
                 // to write to the file descriptor directly
                 pw.flush();
                 try {
                     TransferPipe tp = new TransferPipe();
                     try {
-                        r.app.thread.dumpActivity(tp.getWriteFd(), r.appToken, innerPrefix, args);
-                        // Short timeout, since blocking here can
-                        // deadlock with the application.
+                        r.app.getThread().dumpActivity(
+                                tp.getWriteFd(), r.appToken, innerPrefix, args);
+                        // Short timeout, since blocking here can deadlock with the application.
                         tp.go(fd, 2000);
                     } finally {
                         tp.kill();
@@ -4269,7 +4287,7 @@
     }
 
     private void handleDisplayAdded(int displayId) {
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             getActivityDisplayOrCreateLocked(displayId);
         }
     }
@@ -4332,7 +4350,7 @@
             throw new IllegalArgumentException("Can't remove the primary display.");
         }
 
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             final ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
             if (activityDisplay == null) {
                 return;
@@ -4347,7 +4365,7 @@
     }
 
     private void handleDisplayChanged(int displayId) {
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
             // TODO: The following code block should be moved into {@link ActivityDisplay}.
             if (activityDisplay != null) {
@@ -4501,7 +4519,7 @@
             if (preferredDisplayId != actualDisplayId) {
                 // Display a warning toast that we tried to put a non-resizeable task on a secondary
                 // display with config different from global config.
-                mService.mTaskChangeNotificationController
+                mService.getTaskChangeNotificationController()
                         .notifyActivityLaunchOnSecondaryDisplayFailed();
                 return;
             }
@@ -4510,7 +4528,7 @@
         if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
             // Display a warning toast that we tried to put an app that doesn't support split-screen
             // in split-screen.
-            mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
+            mService.getTaskChangeNotificationController().notifyActivityDismissingDockedStack();
 
             // Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
             // we need to move it to top of fullscreen stack, otherwise it will be covered.
@@ -4530,7 +4548,7 @@
             final int reason = isSecondaryDisplayPreferred
                     ? FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY
                     : FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
-            mService.mTaskChangeNotificationController.notifyActivityForcedResizable(
+            mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
                     task.taskId, reason, packageName);
         }
     }
@@ -4562,7 +4580,7 @@
 
         for (int i = task.mActivities.size() - 1; i >= 0; i--) {
             final ActivityRecord r = task.mActivities.get(i);
-            if (r.app != null && r.app.thread != null) {
+            if (r.attachedToProcess()) {
                 mMultiWindowModeChangedActivities.add(r);
             }
         }
@@ -4585,7 +4603,7 @@
     void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
         for (int i = task.mActivities.size() - 1; i >= 0; i--) {
             final ActivityRecord r = task.mActivities.get(i);
-            if (r.app != null && r.app.thread != null) {
+            if (r.attachedToProcess()) {
                 mPipModeChangedActivities.add(r);
                 // If we are scheduling pip change, then remove this activity from multi-window
                 // change list as the processing of pip change will make sure multi-window changed
@@ -4604,16 +4622,19 @@
         mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
         for (int i = task.mActivities.size() - 1; i >= 0; i--) {
             final ActivityRecord r = task.mActivities.get(i);
-            if (r.app != null && r.app.thread != null) {
+            if (r.attachedToProcess()) {
                 r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
             }
         }
     }
 
     void setDockedStackMinimized(boolean minimized) {
+        // Get currently focused stack before setting mIsDockMinimized. We do this because if
+        // split-screen is active, primary stack will not be focusable (see #isFocusable) while
+        // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
+        final ActivityStack current = getTopDisplayFocusedStack();
         mIsDockMinimized = minimized;
         if (mIsDockMinimized) {
-            final ActivityStack current = getFocusedStack();
             if (current.inSplitScreenPrimaryWindowingMode()) {
                 // The primary split-screen stack can't be focused while it is minimize, so move
                 // focus to something else.
@@ -4654,7 +4675,7 @@
         }
 
         void activityIdleInternal(ActivityRecord r, boolean processPausingActivities) {
-            synchronized (mService) {
+            synchronized (mService.mGlobalLock) {
                 activityIdleInternalLocked(r != null ? r.appToken : null, true /* fromTimeout */,
                         processPausingActivities, null);
             }
@@ -4664,7 +4685,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case REPORT_MULTI_WINDOW_MODE_CHANGED_MSG: {
-                    synchronized (mService) {
+                    synchronized (mService.mGlobalLock) {
                         for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
                             final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
                             r.updateMultiWindowMode();
@@ -4672,7 +4693,7 @@
                     }
                 } break;
                 case REPORT_PIP_MODE_CHANGED_MSG: {
-                    synchronized (mService) {
+                    synchronized (mService.mGlobalLock) {
                         for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
                             final ActivityRecord r = mPipModeChangedActivities.remove(i);
                             r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
@@ -4694,12 +4715,12 @@
                             false /* processPausingActivities */);
                 } break;
                 case RESUME_TOP_ACTIVITY_MSG: {
-                    synchronized (mService) {
-                        resumeFocusedStackTopActivityLocked();
+                    synchronized (mService.mGlobalLock) {
+                        resumeFocusedStacksTopActivitiesLocked();
                     }
                 } break;
                 case SLEEP_TIMEOUT_MSG: {
-                    synchronized (mService) {
+                    synchronized (mService.mGlobalLock) {
                         if (mService.isSleepingOrShuttingDownLocked()) {
                             Slog.w(TAG, "Sleep timeout!  Sleeping now.");
                             checkReadyForSleepLocked(false /* allowDelay */);
@@ -4707,7 +4728,7 @@
                     }
                 } break;
                 case LAUNCH_TIMEOUT_MSG: {
-                    synchronized (mService) {
+                    synchronized (mService.mGlobalLock) {
                         if (mLaunchingActivity.isHeld()) {
                             Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
                             if (VALIDATE_WAKE_LOCK_CALLER
@@ -4728,7 +4749,7 @@
                     handleDisplayRemoved(msg.arg1);
                 } break;
                 case LAUNCH_TASK_BEHIND_COMPLETE: {
-                    synchronized (mService) {
+                    synchronized (mService.mGlobalLock) {
                         ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
                         if (r != null) {
                             handleLaunchTaskBehindCompleteLocked(r);
@@ -4816,7 +4837,7 @@
 
             // If the user must confirm credentials (e.g. when first launching a work app and the
             // Work Challenge is present) let startActivityInPackage handle the intercepting.
-            if (!mService.mUserController.shouldConfirmCredentials(task.userId)
+            if (!mService.mAmInternal.shouldConfirmCredentials(task.userId)
                     && task.getRootActivity() != null) {
                 final ActivityRecord targetActivity = task.getTopActivity();
 
@@ -4842,7 +4863,7 @@
             return mService.getActivityStartController().startActivityInPackage(
                     task.mCallingUid, callingPid, callingUid, callingPackage, intent, null, null,
                     null, 0, 0, options, userId, task, "startActivityFromRecents",
-                    false /* validateIncomingUser */);
+                    false /* validateIncomingUser */, null /* originatingPendingIntent */);
         } 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
@@ -4877,6 +4898,7 @@
      */
     List<IBinder> getTopVisibleActivities() {
         final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
+        final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
         // Traverse all displays.
         for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
             final ActivityDisplay display = mActivityDisplays.valueAt(i);
@@ -4887,7 +4909,7 @@
                 if (stack.shouldBeVisible(null /* starting */)) {
                     final ActivityRecord top = stack.getTopActivity();
                     if (top != null) {
-                        if (stack == mFocusedStack) {
+                        if (stack == topFocusedStack) {
                             topActivityTokens.add(0, top.appToken);
                         } else {
                             topActivityTokens.add(top.appToken);
@@ -4944,7 +4966,7 @@
 
         @Override
         public void release() {
-            synchronized (mService) {
+            synchronized (mService.mGlobalLock) {
                 removeSleepTokenLocked(this);
             }
         }
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index a7c3200..6e3a79c 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -21,7 +21,7 @@
 
 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.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 
 import android.app.IApplicationThread;
 import android.content.ComponentName;
@@ -40,6 +40,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 import android.view.RemoteAnimationAdapter;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -65,7 +66,7 @@
 
     private static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 1;
 
-    private final ActivityManagerService mService;
+    private final ActivityTaskManagerService mService;
     private final ActivityStackSupervisor mSupervisor;
 
     /** Last home activity record we attempted to start. */
@@ -96,7 +97,7 @@
         public void handleMessage(Message msg) {
             switch(msg.what) {
                 case DO_PENDING_ACTIVITY_LAUNCHES_MSG:
-                    synchronized (mService) {
+                    synchronized (mService.mGlobalLock) {
                         doPendingActivityLaunches(true);
                     }
                     break;
@@ -111,22 +112,22 @@
      */
     private ActivityStarter mLastStarter;
 
-    ActivityStartController(ActivityManagerService service) {
+    ActivityStartController(ActivityTaskManagerService service) {
         this(service, service.mStackSupervisor,
                 new DefaultFactory(service, service.mStackSupervisor,
                     new ActivityStartInterceptor(service, service.mStackSupervisor)));
     }
 
     @VisibleForTesting
-    ActivityStartController(ActivityManagerService service, ActivityStackSupervisor supervisor,
+    ActivityStartController(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
             Factory factory) {
         mService = service;
         mSupervisor = supervisor;
-        mHandler = new StartHandler(mService.mHandlerThread.getLooper());
+        mHandler = new StartHandler(mService.mH.getLooper());
         mFactory = factory;
         mFactory.setController(this);
         mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service,
-                service.mHandler);
+                service.mH);
     }
 
     /**
@@ -182,7 +183,7 @@
      */
     void startSetupActivity() {
         // Only do this once per boot.
-        if (mService.getCheckedForSetup()) {
+        if (mService.mAm.getCheckedForSetup()) {
             return;
         }
 
@@ -190,10 +191,10 @@
         // version than the last one shown, and we are not running in
         // low-level factory test mode.
         final ContentResolver resolver = mService.mContext.getContentResolver();
-        if (mService.mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL &&
+        if (mService.mAm.mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL &&
                 Settings.Global.getInt(resolver,
                         Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
-            mService.setCheckedForSetup(true);
+            mService.mAm.setCheckedForSetup(true);
 
             // See if we should be showing the platform update setup UI.
             final Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
@@ -237,10 +238,10 @@
     int checkTargetUser(int targetUserId, boolean validateIncomingUser,
             int realCallingPid, int realCallingUid, String reason) {
         if (validateIncomingUser) {
-            return mService.mUserController.handleIncomingUser(realCallingPid, realCallingUid,
-                    targetUserId, false, ALLOW_FULL_ONLY, reason, null);
+            return mService.handleIncomingUser(
+                    realCallingPid, realCallingUid, targetUserId, reason);
         } else {
-            mService.mUserController.ensureNotSpecialUser(targetUserId);
+            mService.mAmInternal.ensureNotSpecialUser(targetUserId);
             return targetUserId;
         }
     }
@@ -248,7 +249,8 @@
     final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
             String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
-            int userId, TaskRecord inTask, String reason, boolean validateIncomingUser) {
+            int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
+            PendingIntentRecord originatingPendingIntent) {
 
         userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
                 reason);
@@ -267,6 +269,7 @@
                 .setActivityOptions(options)
                 .setMayWait(userId)
                 .setInTask(inTask)
+                .setOriginatingPendingIntent(originatingPendingIntent)
                 .execute();
     }
 
@@ -278,10 +281,12 @@
      * @param intents Intents to start.
      * @param userId Start the intents on this user.
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
+     * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
+     *        null if not originated by PendingIntent
      */
     final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
             String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-            boolean validateIncomingUser) {
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent) {
 
         final String reason = "startActivityInPackage";
 
@@ -290,12 +295,12 @@
 
         // TODO: Switch to user app stacks here.
         return startActivities(null, uid, callingPackage, intents, resolvedTypes, resultTo, options,
-                userId, reason);
+                userId, reason, originatingPendingIntent);
     }
 
     int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
-            int userId, String reason) {
+            int userId, String reason, PendingIntentRecord originatingPendingIntent) {
         if (intents == null) {
             throw new NullPointerException("intents is null");
         }
@@ -320,7 +325,7 @@
         }
         final long origId = Binder.clearCallingIdentity();
         try {
-            synchronized (mService) {
+            synchronized (mService.mGlobalLock) {
                 ActivityRecord[] outActivity = new ActivityRecord[1];
                 for (int i=0; i < intents.length; i++) {
                     Intent intent = intents[i];
@@ -343,7 +348,7 @@
                             null, userId, ActivityStarter.computeResolveFilterUid(
                                     callingUid, realCallingUid, UserHandle.USER_NULL));
                     // TODO: New, check if this is correct
-                    aInfo = mService.getActivityInfoForUser(aInfo, userId);
+                    aInfo = mService.mAm.getActivityInfoForUser(aInfo, userId);
 
                     if (aInfo != null &&
                             (aInfo.applicationInfo.privateFlags
@@ -374,6 +379,7 @@
                             // Top activity decides on animation being run, so we allow only for the
                             // top one as otherwise an activity below might consume it.
                             .setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
+                            .setOriginatingPendingIntent(originatingPendingIntent)
                             .execute();
 
                     if (res < 0) {
@@ -404,7 +410,7 @@
                     "pendingActivityLaunch");
             try {
                 starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags,
-                        resume, null, null, null /* outRecords */);
+                        resume, pal.r.pendingOptions, null, null /* outRecords */);
             } catch (Exception e) {
                 Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
                 pal.sendErrorResult(e.getMessage());
@@ -472,4 +478,10 @@
             pw.println("(nothing)");
         }
     }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        for (PendingActivityLaunch activity: mPendingActivityLaunches) {
+            activity.r.writeIdentifierToProto(proto, fieldId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index ff97db8..177e2f5 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -66,10 +66,9 @@
  */
 class ActivityStartInterceptor {
 
-    private final ActivityManagerService mService;
+    private final ActivityTaskManagerService mService;
     private final ActivityStackSupervisor mSupervisor;
     private final Context mServiceContext;
-    private final UserController mUserController;
 
     // UserManager cannot be final as it's not ready when this class is instantiated during boot
     private UserManager mUserManager;
@@ -99,17 +98,17 @@
     TaskRecord mInTask;
     ActivityOptions mActivityOptions;
 
-    ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
-        this(service, supervisor, service.mContext, service.mUserController);
+    ActivityStartInterceptor(
+            ActivityTaskManagerService service, ActivityStackSupervisor supervisor) {
+        this(service, supervisor, service.mContext);
     }
 
     @VisibleForTesting
-    ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor,
-            Context context, UserController userController) {
+    ActivityStartInterceptor(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
+            Context context) {
         mService = service;
         mSupervisor = supervisor;
         mServiceContext = context;
-        mUserController = userController;
     }
 
     /**
@@ -127,7 +126,7 @@
 
     private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) {
         Bundle activityOptions = deferCrossProfileAppsAnimationIfNecessary();
-        final IIntentSender target = mService.getIntentSenderLocked(
+        final IIntentSender target = mService.mAm.getIntentSenderLocked(
                 INTENT_SENDER_ACTIVITY, mCallingPackage, callingUid, mUserId, null /*token*/,
                 null /*resultCode*/, 0 /*requestCode*/,
                 new Intent[] { mIntent }, new String[] { mResolvedType },
@@ -297,7 +296,7 @@
      * @return The intercepting intent if needed.
      */
     private Intent interceptWithConfirmCredentialsIfNeeded(ActivityInfo aInfo, int userId) {
-        if (!mUserController.shouldConfirmCredentials(userId)) {
+        if (!mService.mAmInternal.shouldConfirmCredentials(userId)) {
             return null;
         }
         // TODO(b/28935539): should allow certain activities to bypass work challenge
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 73e3d33..dcf9344 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -99,6 +99,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.voice.IVoiceInteractionSession;
@@ -132,7 +133,7 @@
     private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
     private static final int INVALID_LAUNCH_MODE = -1;
 
-    private final ActivityManagerService mService;
+    private final ActivityTaskManagerService mService;
     private final ActivityStackSupervisor mSupervisor;
     private final ActivityStartInterceptor mInterceptor;
     private final ActivityStartController mController;
@@ -233,14 +234,14 @@
         private final int MAX_STARTER_COUNT = 3;
 
         private ActivityStartController mController;
-        private ActivityManagerService mService;
+        private ActivityTaskManagerService mService;
         private ActivityStackSupervisor mSupervisor;
         private ActivityStartInterceptor mInterceptor;
 
         private SynchronizedPool<ActivityStarter> mStarterPool =
                 new SynchronizedPool<>(MAX_STARTER_COUNT);
 
-        DefaultFactory(ActivityManagerService service,
+        DefaultFactory(ActivityTaskManagerService service,
                 ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
             mService = service;
             mSupervisor = supervisor;
@@ -313,6 +314,7 @@
         int userId;
         WaitResult waitResult;
         int filterCallingUid;
+        PendingIntentRecord originatingPendingIntent;
 
         /**
          * If set to {@code true}, allows this activity start to look into
@@ -369,6 +371,7 @@
             avoidMoveToFront = false;
             allowPendingRemoteAnimationRegistryLookup = true;
             filterCallingUid = UserHandle.USER_NULL;
+            originatingPendingIntent = null;
         }
 
         /**
@@ -407,10 +410,11 @@
             allowPendingRemoteAnimationRegistryLookup
                     = request.allowPendingRemoteAnimationRegistryLookup;
             filterCallingUid = request.filterCallingUid;
+            originatingPendingIntent = request.originatingPendingIntent;
         }
     }
 
-    ActivityStarter(ActivityStartController controller, ActivityManagerService service,
+    ActivityStarter(ActivityStartController controller, ActivityTaskManagerService service,
             ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
         mController = controller;
         mService = service;
@@ -490,7 +494,8 @@
                         mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
                         mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
                         mRequest.inTask, mRequest.reason,
-                        mRequest.allowPendingRemoteAnimationRegistryLookup);
+                        mRequest.allowPendingRemoteAnimationRegistryLookup,
+                        mRequest.originatingPendingIntent);
             } else {
                 return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
                         mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,
@@ -500,7 +505,8 @@
                         mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,
                         mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
                         mRequest.outActivity, mRequest.inTask, mRequest.reason,
-                        mRequest.allowPendingRemoteAnimationRegistryLookup);
+                        mRequest.allowPendingRemoteAnimationRegistryLookup,
+                        mRequest.originatingPendingIntent);
             }
         } finally {
             onExecutionComplete();
@@ -532,7 +538,8 @@
             String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
             SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
             ActivityRecord[] outActivity, TaskRecord inTask, String reason,
-            boolean allowPendingRemoteAnimationRegistryLookup) {
+            boolean allowPendingRemoteAnimationRegistryLookup,
+            PendingIntentRecord originatingPendingIntent) {
 
         if (TextUtils.isEmpty(reason)) {
             throw new IllegalArgumentException("Need to specify a reason.");
@@ -545,7 +552,7 @@
                 aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
                 callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
                 options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
-                inTask, allowPendingRemoteAnimationRegistryLookup);
+                inTask, allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent);
 
         if (outActivity != null) {
             // mLastStartActivityRecord[0] is set in the call to startActivity above.
@@ -575,7 +582,8 @@
             String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
             SafeActivityOptions options,
             boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
-            TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup) {
+            TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
+            PendingIntentRecord originatingPendingIntent) {
         int err = ActivityManager.START_SUCCESS;
         // Pull the optional Ephemeral Installer-only bundle out of the options early.
         final Bundle verificationBundle
@@ -583,7 +591,7 @@
 
         ProcessRecord callerApp = null;
         if (caller != null) {
-            callerApp = mService.getRecordForAppLocked(caller);
+            callerApp = mService.mAm.getRecordForAppLocked(caller);
             if (callerApp != null) {
                 callingPid = callerApp.pid;
                 callingUid = callerApp.info.uid;
@@ -717,13 +725,14 @@
         boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
                 requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
                 inTask != null, callerApp, resultRecord, resultStack);
-        abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
+        abort |= !mService.mAm.mIntentFirewall.checkStartActivity(intent, callingUid,
                 callingPid, resolvedType, aInfo.applicationInfo);
 
+        final WindowProcessController callerWpc =
+                callerApp != null ? callerApp.getWindowProcessController() : null;
         // Merge the two options bundles, while realCallerOptions takes precedence.
         ActivityOptions checkedOptions = options != null
-                ? options.getOptions(intent, aInfo, callerApp, mSupervisor)
-                : null;
+                ? options.getOptions(intent, aInfo, callerWpc, mSupervisor) : null;
         if (allowPendingRemoteAnimationRegistryLookup) {
             checkedOptions = mService.getActivityStartController()
                     .getPendingRemoteAnimationRegistry()
@@ -770,10 +779,10 @@
         // If permissions need a review before any of the app components can run, we
         // launch the review activity and pass a pending intent to start the activity
         // we are to launching now after the review is completed.
-        if (mService.mPermissionReviewRequired && aInfo != null) {
+        if (aInfo != null) {
             if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                     aInfo.packageName, userId)) {
-                IIntentSender target = mService.getIntentSenderLocked(
+                IIntentSender target = mService.mAm.getIntentSenderLocked(
                         ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
                         callingUid, userId, null, null, 0, new Intent[]{intent},
                         new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
@@ -801,10 +810,10 @@
                         null /*profilerInfo*/);
 
                 if (DEBUG_PERMISSIONS_REVIEW) {
+                    final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
                     Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
                             true, false) + "} from uid " + callingUid + " on display "
-                            + (mSupervisor.mFocusedStack == null
-                            ? DEFAULT_DISPLAY : mSupervisor.mFocusedStack.mDisplayId));
+                            + (focusedStack == null ? DEFAULT_DISPLAY : focusedStack.mDisplayId));
                 }
             }
         }
@@ -823,7 +832,8 @@
             aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
         }
 
-        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
+        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid,
+                callingUid,
                 callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
                 resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
                 mSupervisor, checkedOptions, sourceRecord);
@@ -837,7 +847,7 @@
             r.appTimeTracker = sourceRecord.appTimeTracker;
         }
 
-        final ActivityStack stack = mSupervisor.mFocusedStack;
+        final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
 
         // If we are starting an activity that is not from the same uid as the currently resumed
         // one, check whether app switches are allowed.
@@ -846,29 +856,67 @@
             if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
                     realCallingPid, realCallingUid, "Activity start")) {
                 mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
-                        sourceRecord, startFlags, stack, callerApp));
+                        sourceRecord, startFlags, stack, callerWpc));
                 ActivityOptions.abort(checkedOptions);
                 return ActivityManager.START_SWITCHES_CANCELED;
             }
         }
 
-        if (mService.mDidAppSwitch) {
-            // This is the second allowed switch since we stopped switches,
-            // so now just generally allow switches.  Use case: user presses
-            // home (switches disabled, switch to home, mDidAppSwitch now true);
-            // user taps a home icon (coming from home so allowed, we hit here
-            // and now allow anyone to switch again).
-            mService.mAppSwitchesAllowedTime = 0;
-        } else {
-            mService.mDidAppSwitch = true;
-        }
-
+        mService.onStartActivitySetDidAppSwitch();
         mController.doPendingActivityLaunches(false);
 
+        maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, r,
+                originatingPendingIntent);
+
         return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                 true /* doResume */, checkedOptions, inTask, outActivity);
     }
 
+    private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid,
+            Intent intent, ProcessRecord callerApp, ActivityRecord r,
+            PendingIntentRecord originatingPendingIntent) {
+        boolean callerAppHasForegroundActivity = (callerApp != null)
+                ? callerApp.foregroundActivities
+                : false;
+        if (!mService.mAm.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity
+                || r == null) {
+            // skip logging in this case
+            return;
+        }
+
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "logActivityStart");
+            final int callingUidProcState = mService.mAm.getUidStateLocked(callingUid);
+            final boolean callingUidHasAnyVisibleWindow =
+                    mService.mWindowManager.isAnyWindowVisibleForUid(callingUid);
+            final int realCallingUidProcState = (callingUid == realCallingUid)
+                    ? callingUidProcState
+                    : mService.mAm.getUidStateLocked(realCallingUid);
+            final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
+                    ? callingUidHasAnyVisibleWindow
+                    : mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid);
+            final String targetPackage = r.packageName;
+            final int targetUid = (r.appInfo != null) ? r.appInfo.uid : -1;
+            final int targetUidProcState = mService.mAm.getUidStateLocked(targetUid);
+            final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)
+                    ? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid)
+                    : false;
+            final String targetWhitelistTag = (targetUid != -1)
+                    ? mService.mAm.getPendingTempWhitelistTagForUidLocked(targetUid)
+                    : null;
+
+            mSupervisor.getActivityMetricsLogger().logActivityStart(intent, callerApp, r,
+                    callingUid, callingPackage, callingUidProcState,
+                    callingUidHasAnyVisibleWindow,
+                    realCallingUid, realCallingUidProcState,
+                    realCallingUidHasAnyVisibleWindow,
+                    targetUid, targetPackage, targetUidProcState,
+                    targetUidHasAnyVisibleWindow, targetWhitelistTag,
+                    (originatingPendingIntent != null));
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
 
     /**
      * Creates a launch intent for the given auxiliary resolution data.
@@ -929,7 +977,7 @@
             // anyone interested in this piece of information.
             switch (startedActivityStack.getWindowingMode()) {
                 case WINDOWING_MODE_PINNED:
-                    mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt(
+                    mService.getTaskChangeNotificationController().notifyPinnedActivityRestartAttempt(
                             clearedTask);
                     break;
                 case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
@@ -949,7 +997,8 @@
             ProfilerInfo profilerInfo, WaitResult outResult,
             Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
             int userId, TaskRecord inTask, String reason,
-            boolean allowPendingRemoteAnimationRegistryLookup) {
+            boolean allowPendingRemoteAnimationRegistryLookup,
+            PendingIntentRecord originatingPendingIntent) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -1020,8 +1069,8 @@
         // Collect information about the target of the Intent.
         ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
 
-        synchronized (mService) {
-            final ActivityStack stack = mSupervisor.mFocusedStack;
+        synchronized (mService.mGlobalLock) {
+            final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
             stack.mConfigWillChange = globalConfig != null
                     && mService.getGlobalConfiguration().diff(globalConfig) != 0;
             if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -1032,16 +1081,16 @@
             if (aInfo != null &&
                     (aInfo.applicationInfo.privateFlags
                             & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 &&
-                    mService.mHasHeavyWeightFeature) {
+                    mService.mAm.mHasHeavyWeightFeature) {
                 // This may be a heavy-weight process!  Check to see if we already
                 // have another, different heavy-weight process running.
                 if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
-                    final ProcessRecord heavy = mService.mHeavyWeightProcess;
+                    final ProcessRecord heavy = mService.mAm.mHeavyWeightProcess;
                     if (heavy != null && (heavy.info.uid != aInfo.applicationInfo.uid
                             || !heavy.processName.equals(aInfo.processName))) {
                         int appCallingUid = callingUid;
                         if (caller != null) {
-                            ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
+                            ProcessRecord callerApp = mService.mAm.getRecordForAppLocked(caller);
                             if (callerApp != null) {
                                 appCallingUid = callerApp.info.uid;
                             } else {
@@ -1053,7 +1102,7 @@
                             }
                         }
 
-                        IIntentSender target = mService.getIntentSenderLocked(
+                        IIntentSender target = mService.mAm.getIntentSenderLocked(
                                 ActivityManager.INTENT_SENDER_ACTIVITY, "android",
                                 appCallingUid, userId, null, null, 0, new Intent[] { intent },
                                 new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
@@ -1066,13 +1115,8 @@
                         }
                         newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
                                 new IntentSender(target));
-                        if (heavy.activities.size() > 0) {
-                            ActivityRecord hist = heavy.activities.get(0);
-                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
-                                    hist.packageName);
-                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
-                                    hist.getTask().taskId);
-                        }
+                        heavy.getWindowProcessController().updateIntentForHeavyWeightActivity(
+                                newIntent);
                         newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
                                 aInfo.packageName);
                         newIntent.setFlags(intent.getFlags());
@@ -1089,7 +1133,7 @@
                                         callingUid, realCallingUid, mRequest.filterCallingUid));
                         aInfo = rInfo != null ? rInfo.activityInfo : null;
                         if (aInfo != null) {
-                            aInfo = mService.getActivityInfoForUser(aInfo, userId);
+                            aInfo = mService.mAm.getActivityInfoForUser(aInfo, userId);
                         }
                     }
                 }
@@ -1100,7 +1144,7 @@
                     voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
                     callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
                     ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
-                    allowPendingRemoteAnimationRegistryLookup);
+                    allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent);
 
             Binder.restoreCallingIdentity(origId);
 
@@ -1109,7 +1153,7 @@
                 // do so now.  This allows a clean switch, as we are waiting
                 // for the current activity to pause (so we will not destroy
                 // it), and have not yet started the next activity.
-                mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
+                mService.mAmInternal.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
                         "updateConfiguration()");
                 stack.mConfigWillChange = false;
                 if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -1127,7 +1171,7 @@
                         mSupervisor.mWaitingActivityLaunched.add(outResult);
                         do {
                             try {
-                                mService.wait();
+                                mService.mGlobalLock.wait();
                             } catch (InterruptedException e) {
                             }
                         } while (outResult.result != START_TASK_TO_FRONT
@@ -1158,7 +1202,7 @@
                             // Note: the timeout variable is not currently not ever set.
                             do {
                                 try {
-                                    mService.wait();
+                                    mService.mGlobalLock.wait();
                                 } catch (InterruptedException e) {
                                 }
                             } while (!outResult.timeout && outResult.who == null);
@@ -1367,20 +1411,20 @@
 
         // If the activity being launched is the same as the one currently at the top, then
         // we need to check if it should only be launched once.
-        final ActivityStack topStack = mSupervisor.mFocusedStack;
+        final ActivityStack topStack = mSupervisor.getTopDisplayFocusedStack();
         final ActivityRecord topFocused = topStack.getTopActivity();
         final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
         final boolean dontStart = top != null && mStartActivity.resultTo == null
                 && top.realActivity.equals(mStartActivity.realActivity)
                 && top.userId == mStartActivity.userId
-                && top.app != null && top.app.thread != null
+                && top.attachedToProcess()
                 && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                 || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));
         if (dontStart) {
             // For paranoia, make sure we have correctly resumed the top activity.
             topStack.mLastPausedActivity = null;
             if (mDoResume) {
-                mSupervisor.resumeFocusedStackTopActivityLocked();
+                mSupervisor.resumeFocusedStacksTopActivitiesLocked();
             }
             ActivityOptions.abort(mOptions);
             if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
@@ -1422,9 +1466,9 @@
             return result;
         }
 
-        mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
+        mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
                 mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
-        mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
+        mService.mAm.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
                 mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid));
         if (newTask) {
             EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
@@ -1460,10 +1504,11 @@
                 // will not update the focused stack.  If starting the new activity now allows the
                 // task stack to be focusable, then ensure that we now update the focused stack
                 // accordingly.
-                if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) {
+                if (mTargetStack.isFocusable()
+                        && !mSupervisor.isTopDisplayFocusedStack(mTargetStack)) {
                     mTargetStack.moveToFront("startActivityUnchecked");
                 }
-                mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
+                mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, mStartActivity,
                         mOptions);
             }
         } else if (mStartActivity != null) {
@@ -1622,8 +1667,8 @@
         if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
             ActivityRecord checkedCaller = sourceRecord;
             if (checkedCaller == null) {
-                checkedCaller = mSupervisor.mFocusedStack.topRunningNonDelayedActivityLocked(
-                        mNotTop);
+                checkedCaller = mSupervisor.getTopDisplayFocusedStack()
+                        .topRunningNonDelayedActivityLocked(mNotTop);
             }
             if (!checkedCaller.realActivity.equals(r.realActivity)) {
                 // Caller is not the same as launcher, so always needed.
@@ -1856,7 +1901,7 @@
         // except...  well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
         // the same behavior as if a new instance was being started, which means not bringing it
         // to the front if the caller is not itself in the front.
-        final ActivityStack focusStack = mSupervisor.getFocusedStack();
+        final ActivityStack focusStack = mSupervisor.getTopDisplayFocusedStack();
         ActivityRecord curTop = (focusStack == null)
                 ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
 
@@ -2035,7 +2080,7 @@
 
     private void resumeTargetStackIfNeeded() {
         if (mDoResume) {
-            mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, null, mOptions);
+            mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, null, mOptions);
         } else {
             ActivityOptions.abort(mOptions);
         }
@@ -2114,13 +2159,13 @@
             // be not suitable. Let's check other displays.
             if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) {
                 // Can't use target display, lets find a stack on the source display.
-                mTargetStack = mService.mStackSupervisor.getValidLaunchStackOnDisplay(
-                        sourceStack.mDisplayId, mStartActivity);
+                mTargetStack = mSupervisor.getValidLaunchStackOnDisplay(
+                        sourceStack.mDisplayId, mStartActivity, mOptions);
             }
             if (mTargetStack == null) {
                 // There are no suitable stacks on the target and source display(s). Look on all
                 // displays.
-                mTargetStack = mService.mStackSupervisor.getNextValidLaunchStackLocked(
+                mTargetStack = mSupervisor.getNextValidLaunchStackLocked(
                         mStartActivity, -1 /* currentFocus */);
             }
         }
@@ -2151,7 +2196,7 @@
                 // For paranoia, make sure we have correctly resumed the top activity.
                 mTargetStack.mLastPausedActivity = null;
                 if (mDoResume) {
-                    mSupervisor.resumeFocusedStackTopActivityLocked();
+                    mSupervisor.resumeFocusedStacksTopActivitiesLocked();
                 }
                 ActivityOptions.abort(mOptions);
                 return START_DELIVERED_TO_TOP;
@@ -2169,7 +2214,7 @@
                 deliverNewIntent(top);
                 mTargetStack.mLastPausedActivity = null;
                 if (mDoResume) {
-                    mSupervisor.resumeFocusedStackTopActivityLocked();
+                    mSupervisor.resumeFocusedStacksTopActivitiesLocked();
                 }
                 return START_DELIVERED_TO_TOP;
             }
@@ -2251,7 +2296,8 @@
 
         final ActivityStack stack = task.getStack();
         if (stack != null && stack.resizeStackWithLaunchBounds()) {
-            mService.resizeStack(stack.mStackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
+            mService.resizeStack(
+                    stack.mStackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
         } else {
             task.updateOverrideConfiguration(bounds);
         }
@@ -2316,28 +2362,28 @@
         }
 
         final ActivityStack currentStack = task != null ? task.getStack() : null;
+        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
         if (currentStack != null) {
-            if (mSupervisor.mFocusedStack != currentStack) {
+            if (focusedStack != currentStack) {
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
                         "computeStackFocus: Setting " + "focused stack to r=" + r
                                 + " task=" + task);
             } else {
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                        "computeStackFocus: Focused stack already="
-                                + mSupervisor.mFocusedStack);
+                        "computeStackFocus: Focused stack already=" + focusedStack);
             }
             return currentStack;
         }
 
         if (canLaunchIntoFocusedStack(r, newTask)) {
             if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                    "computeStackFocus: Have a focused stack=" + mSupervisor.mFocusedStack);
-            return mSupervisor.mFocusedStack;
+                    "computeStackFocus: Have a focused stack=" + focusedStack);
+            return focusedStack;
         }
 
         if (mPreferredDisplayId != DEFAULT_DISPLAY) {
             // Try to put the activity in a stack on a secondary display.
-            stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r);
+            stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r, aOptions);
             if (stack == null) {
                 // If source display is not suitable - look for topmost valid stack in the system.
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
@@ -2368,7 +2414,7 @@
     /** Check if provided activity record can launch in currently focused stack. */
     // TODO: This method can probably be consolidated into getLaunchStack() below.
     private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
-        final ActivityStack focusedStack = mSupervisor.mFocusedStack;
+        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
         final boolean canUseFocusedStack;
         if (focusedStack.isActivityTypeAssistant()) {
             canUseFocusedStack = r.isActivityTypeAssistant();
@@ -2418,18 +2464,19 @@
         }
         // Otherwise handle adjacent launch.
 
+        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
         // The parent activity doesn't want to launch the activity on top of itself, but
         // instead tries to put it onto other side in side-by-side mode.
-        final ActivityStack parentStack = task != null ? task.getStack(): mSupervisor.mFocusedStack;
+        final ActivityStack parentStack = task != null ? task.getStack(): focusedStack;
 
-        if (parentStack != mSupervisor.mFocusedStack) {
+        if (parentStack != focusedStack) {
             // If task's parent stack is not focused - use it during adjacent launch.
             return parentStack;
         } else {
-            if (mSupervisor.mFocusedStack != null && task == mSupervisor.mFocusedStack.topTask()) {
+            if (focusedStack != null && task == focusedStack.topTask()) {
                 // If task is already on top of focused stack - use it. We don't want to move the
                 // existing focused task to adjacent stack, just deliver new intent in this case.
-                return mSupervisor.mFocusedStack;
+                return focusedStack;
             }
 
             if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
@@ -2625,6 +2672,11 @@
         return this;
     }
 
+    ActivityStarter setOriginatingPendingIntent(PendingIntentRecord originatingPendingIntent) {
+        mRequest.originatingPendingIntent = originatingPendingIntent;
+        return this;
+    }
+
     void dump(PrintWriter pw, String prefix) {
         prefix = prefix + "  ";
         pw.print(prefix);
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
new file mode 100644
index 0000000..11f8bb1
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -0,0 +1,5396 @@
+/*
+ * 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 static android.Manifest.permission.BIND_VOICE_INTERACTION;
+import static android.Manifest.permission.CHANGE_CONFIGURATION;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.FILTER_EVENTS;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+import static android.Manifest.permission.READ_FRAME_BUFFER;
+import static android.Manifest.permission.REMOVE_TASKS;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.Manifest.permission.STOP_APP_SWITCHES;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.content.pm.PackageManager.FEATURE_PC;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
+import static android.provider.Settings.System.FONT_SCALE;
+import static com.android.server.am.ActivityManagerService.dumpStackTraces;
+import static com.android.server.am.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
+import static com.android.server.am.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
+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_RECEIVER_EXTRAS;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
+import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.AppOpsManager.OP_NONE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
+import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
+import static android.os.Build.VERSION_CODES.N;
+import static android.os.Process.SYSTEM_UID;
+import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
+import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static com.android.server.am.ActivityManagerService.ANIMATE;
+import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.am.ActivityManagerService.SEND_LOCALE_TO_MOUNT_DAEMON_MSG;
+import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
+import static com.android.server.am.ActivityManagerService.UPDATE_CONFIGURATION_MSG;
+import static com.android.server.am.ActivityManagerService.checkComponentPermission;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
+import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.ActivityThread;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.database.ContentObserver;
+import android.os.IUserManager;
+import android.os.PowerManager;
+import android.os.ServiceManager;
+import android.os.Trace;
+import android.os.UserManager;
+import android.os.WorkSource;
+import android.view.WindowManager;
+import com.android.internal.R;
+import com.android.internal.app.IAppOpsService;
+import com.android.server.AppOpsService;
+import com.android.server.SystemServiceManager;
+import com.android.server.pm.UserManagerService;
+import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import android.app.AppGlobals;
+import android.app.IActivityController;
+import android.app.IActivityTaskManager;
+import android.app.IApplicationThread;
+import android.app.IAssistDataReceiver;
+import android.app.ITaskStackListener;
+import android.app.PictureInPictureParams;
+import android.app.ProfilerInfo;
+import android.app.RemoteAction;
+import android.app.WaitResult;
+import android.app.WindowConfiguration;
+import android.app.admin.DevicePolicyCache;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
+import android.app.servertransaction.ConfigurationChangeItem;
+import android.app.usage.UsageEvents;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.metrics.LogMaker;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.LocaleList;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UpdateLock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.voice.IVoiceInteractionSession;
+import android.service.voice.VoiceInteractionManagerInternal;
+import android.telecom.TelecomManager;
+import android.text.TextUtils;
+import android.text.format.Time;
+import android.util.ArrayMap;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.StatsLog;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.view.IRecentsAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.AssistUtils;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ProcessMap;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.KeyguardDismissCallback;
+import com.android.internal.util.Preconditions;
+import com.android.server.AttributeCache;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.Watchdog;
+import com.android.server.vr.VrManagerInternal;
+import com.android.server.wm.PinnedStackWindowController;
+import com.android.server.wm.WindowManagerService;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * System service for managing activities and their containers (task, stacks, displays,... ).
+ *
+ * {@hide}
+ */
+public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_AM;
+    private static final String TAG_STACK = TAG + POSTFIX_STACK;
+    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+    private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
+    private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
+    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
+    private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
+    private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
+
+    // How long we wait until we timeout on key dispatching.
+    private static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
+    // How long we wait until we timeout on key dispatching during instrumentation.
+    private static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000;
+
+    Context mContext;
+    /**
+     * This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can
+     * change at runtime. Use mContext for non-UI purposes.
+     */
+    final Context mUiContext;
+    H mH;
+    UiHandler mUiHandler;
+    ActivityManagerService mAm;
+    ActivityManagerInternal mAmInternal;
+    UriGrantsManagerInternal mUgmInternal;
+    private PackageManagerInternal mPmInternal;
+    /* Global service lock used by the package the owns this service. */
+    Object mGlobalLock;
+    ActivityStackSupervisor mStackSupervisor;
+    WindowManagerService mWindowManager;
+    private UserManagerService mUserManager;
+    private AppOpsService mAppOpsService;
+    /** All processes currently running that might have a window organized by name. */
+    final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>();
+    /** All processes we currently have running mapped by pid */
+    final SparseArray<WindowProcessController> mPidMap = new SparseArray<>();
+    /** This is the process holding what we currently consider to be the "home" activity. */
+    WindowProcessController mHomeProcess;
+    /**
+     * This is the process holding the activity the user last visited that is in a different process
+     * from the one they are currently in.
+     */
+    WindowProcessController mPreviousProcess;
+    /** The time at which the previous process was last visible. */
+    long mPreviousProcessVisibleTime;
+
+    /** List of intents that were used to start the most recent tasks. */
+    private RecentTasks mRecentTasks;
+    /** State of external calls telling us if the device is awake or asleep. */
+    private boolean mKeyguardShown = false;
+
+    // Wrapper around VoiceInteractionServiceManager
+    private AssistUtils mAssistUtils;
+
+    // VoiceInteraction session ID that changes for each new request except when
+    // being called for multi-window assist in a single session.
+    private int mViSessionId = 1000;
+
+    // How long to wait in getAssistContextExtras for the activity and foreground services
+    // to respond with the result.
+    private static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
+
+    // How long top wait when going through the modern assist (which doesn't need to block
+    // on getting this result before starting to launch its UI).
+    private static final int PENDING_ASSIST_EXTRAS_LONG_TIMEOUT = 2000;
+
+    // How long to wait in getAutofillAssistStructure() for the activity to respond with the result.
+    private static final int PENDING_AUTOFILL_ASSIST_STRUCTURE_TIMEOUT = 2000;
+
+    private final ArrayList<PendingAssistExtras> mPendingAssistExtras = new ArrayList<>();
+
+    // Keeps track of the active voice interaction service component, notified from
+    // VoiceInteractionManagerService
+    ComponentName mActiveVoiceInteractionServiceComponent;
+
+    private VrController mVrController;
+    KeyguardController mKeyguardController;
+    private final ClientLifecycleManager mLifecycleManager;
+    private TaskChangeNotificationController mTaskChangeNotificationController;
+    /** The controller for all operations related to locktask. */
+    private LockTaskController mLockTaskController;
+    private ActivityStartController mActivityStartController;
+
+    boolean mSuppressResizeConfigChanges;
+
+    private final UpdateConfigurationResult mTmpUpdateConfigurationResult =
+            new UpdateConfigurationResult();
+
+    static final class UpdateConfigurationResult {
+        // Configuration changes that were updated.
+        int changes;
+        // If the activity was relaunched to match the new configuration.
+        boolean activityRelaunched;
+
+        void reset() {
+            changes = 0;
+            activityRelaunched = false;
+        }
+    }
+
+    /** Current sequencing integer of the configuration, for skipping old configurations. */
+    private int mConfigurationSeq;
+    // To cache the list of supported system locales
+    private String[] mSupportedSystemLocales = null;
+
+    /**
+     * Temp object used when global and/or display override configuration is updated. It is also
+     * sent to outer world instead of {@link #getGlobalConfiguration} because we don't trust
+     * anyone...
+     */
+    private Configuration mTempConfig = new Configuration();
+
+    /** Temporary to avoid allocations. */
+    final StringBuilder mStringBuilder = new StringBuilder(256);
+
+    // Amount of time after a call to stopAppSwitches() during which we will
+    // prevent further untrusted switches from happening.
+    private static final long APP_SWITCH_DELAY_TIME = 5 * 1000;
+
+    /**
+     * The time at which we will allow normal application switches again,
+     * after a call to {@link #stopAppSwitches()}.
+     */
+    private long mAppSwitchesAllowedTime;
+    /**
+     * This is set to true after the first switch after mAppSwitchesAllowedTime
+     * is set; any switches after that will clear the time.
+     */
+    private boolean mDidAppSwitch;
+
+    IActivityController mController = null;
+    boolean mControllerIsAMonkey = false;
+
+    /**
+     * Used to retain an update lock when the foreground activity is in
+     * immersive mode.
+     */
+    private final UpdateLock mUpdateLock = new UpdateLock("immersive");
+
+    /**
+     * Packages that are being allowed to perform unrestricted app switches.  Mapping is
+     * User -> Type -> uid.
+     */
+    final SparseArray<ArrayMap<String, Integer>> mAllowAppSwitchUids = new SparseArray<>();
+
+    /** The dimensions of the thumbnails in the Recents UI. */
+    private int mThumbnailWidth;
+    private int mThumbnailHeight;
+    private float mFullscreenThumbnailScale;
+
+    /**
+     * Flag that indicates if multi-window is enabled.
+     *
+     * For any particular form of multi-window to be enabled, generic multi-window must be enabled
+     * in {@link com.android.internal.R.bool#config_supportsMultiWindow} config or
+     * {@link Settings.Global#DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES} development option set.
+     * At least one of the forms of multi-window must be enabled in order for this flag to be
+     * initialized to 'true'.
+     *
+     * @see #mSupportsSplitScreenMultiWindow
+     * @see #mSupportsFreeformWindowManagement
+     * @see #mSupportsPictureInPicture
+     * @see #mSupportsMultiDisplay
+     */
+    boolean mSupportsMultiWindow;
+    boolean mSupportsSplitScreenMultiWindow;
+    boolean mSupportsFreeformWindowManagement;
+    boolean mSupportsPictureInPicture;
+    boolean mSupportsMultiDisplay;
+    boolean mForceResizableActivities;
+
+    final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
+
+    // VR Vr2d Display Id.
+    int mVr2dDisplayId = INVALID_DISPLAY;
+
+    /**
+     * Set while we are wanting to sleep, to prevent any
+     * activities from being started/resumed.
+     *
+     * TODO(b/33594039): Clarify the actual state transitions represented by mSleeping.
+     *
+     * Currently mSleeping is set to true when transitioning into the sleep state, and remains true
+     * while in the sleep state until there is a pending transition out of sleep, in which case
+     * mSleeping is set to false, and remains false while awake.
+     *
+     * Whether mSleeping can quickly toggled between true/false without the device actually
+     * display changing states is undefined.
+     */
+    private boolean mSleeping = false;
+
+    /**
+     * The process state used for processes that are running the top activities.
+     * This changes between TOP and TOP_SLEEPING to following mSleeping.
+     */
+    int mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
+
+    // Whether we should show our dialogs (ANR, crash, etc) or just perform their default action
+    // automatically. Important for devices without direct input devices.
+    private boolean mShowDialogs = true;
+
+    /** Set if we are shutting down the system, similar to sleeping. */
+    boolean mShuttingDown = false;
+
+    /**
+     * We want to hold a wake lock while running a voice interaction session, since
+     * this may happen with the screen off and we need to keep the CPU running to
+     * be able to continue to interact with the user.
+     */
+    PowerManager.WakeLock mVoiceWakeLock;
+
+    /**
+     * Set while we are running a voice interaction. This overrides sleeping while it is active.
+     */
+    IVoiceInteractionSession mRunningVoice;
+
+    /**
+     * The last resumed activity. This is identical to the current resumed activity most
+     * of the time but could be different when we're pausing one activity before we resume
+     * another activity.
+     */
+    ActivityRecord mLastResumedActivity;
+
+    /**
+     * The activity that is currently being traced as the active resumed activity.
+     *
+     * @see #updateResumedAppTrace
+     */
+    private @Nullable ActivityRecord mTracedResumedActivity;
+
+    /** If non-null, we are tracking the time the user spends in the currently focused app. */
+    AppTimeTracker mCurAppTimeTracker;
+
+    private AppWarnings mAppWarnings;
+
+    private FontScaleSettingObserver mFontScaleSettingObserver;
+
+    private final class FontScaleSettingObserver extends ContentObserver {
+        private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE);
+        private final Uri mHideErrorDialogsUri = Settings.Global.getUriFor(HIDE_ERROR_DIALOGS);
+
+        public FontScaleSettingObserver() {
+            super(mH);
+            final ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(mFontScaleUri, false, this, UserHandle.USER_ALL);
+            resolver.registerContentObserver(mHideErrorDialogsUri, false, this,
+                    UserHandle.USER_ALL);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
+            if (mFontScaleUri.equals(uri)) {
+                updateFontScaleIfNeeded(userId);
+            } else if (mHideErrorDialogsUri.equals(uri)) {
+                synchronized (mGlobalLock) {
+                    updateShouldShowDialogsLocked(getGlobalConfiguration());
+                }
+            }
+        }
+    }
+
+    ActivityTaskManagerService(Context context) {
+        mContext = context;
+        mUiContext = ActivityThread.currentActivityThread().getSystemUiContext();
+        mLifecycleManager = new ClientLifecycleManager();
+    }
+
+    void onSystemReady() {
+        mAssistUtils = new AssistUtils(mContext);
+        mVrController.onSystemReady();
+        mRecentTasks.onSystemReadyLocked();
+    }
+
+    void onInitPowerManagement() {
+        mStackSupervisor.initPowerManagement();
+        final PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
+        mVoiceWakeLock.setReferenceCounted(false);
+    }
+
+    void installSystemProviders() {
+        mFontScaleSettingObserver = new FontScaleSettingObserver();
+    }
+
+    void retrieveSettings(ContentResolver resolver) {
+        final boolean freeformWindowManagement =
+                mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
+                        || Settings.Global.getInt(
+                        resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+
+        final boolean supportsMultiWindow = ActivityTaskManager.supportsMultiWindow(mContext);
+        final boolean supportsPictureInPicture = supportsMultiWindow &&
+                mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
+        final boolean supportsSplitScreenMultiWindow =
+                ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
+        final boolean supportsMultiDisplay = mContext.getPackageManager()
+                .hasSystemFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
+        final boolean alwaysFinishActivities =
+                Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
+        final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
+        final boolean forceResizable = Settings.Global.getInt(
+                resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
+        final boolean isPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
+
+        // Transfer any global setting for forcing RTL layout, into a System Property
+        SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
+
+        final Configuration configuration = new Configuration();
+        Settings.System.getConfiguration(resolver, configuration);
+        if (forceRtl) {
+            // This will take care of setting the correct layout direction flags
+            configuration.setLayoutDirection(configuration.locale);
+        }
+
+        synchronized (mGlobalLock) {
+            mForceResizableActivities = forceResizable;
+            final boolean multiWindowFormEnabled = freeformWindowManagement
+                    || supportsSplitScreenMultiWindow
+                    || supportsPictureInPicture
+                    || supportsMultiDisplay;
+            if ((supportsMultiWindow || forceResizable) && multiWindowFormEnabled) {
+                mSupportsMultiWindow = true;
+                mSupportsFreeformWindowManagement = freeformWindowManagement;
+                mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
+                mSupportsPictureInPicture = supportsPictureInPicture;
+                mSupportsMultiDisplay = supportsMultiDisplay;
+            } else {
+                mSupportsMultiWindow = false;
+                mSupportsFreeformWindowManagement = false;
+                mSupportsSplitScreenMultiWindow = false;
+                mSupportsPictureInPicture = false;
+                mSupportsMultiDisplay = false;
+            }
+            mWindowManager.setForceResizableTasks(mForceResizableActivities);
+            mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
+            mWindowManager.setSupportsFreeformWindowManagement(mSupportsFreeformWindowManagement);
+            mWindowManager.setIsPc(isPc);
+            // This happens before any activities are started, so we can change global configuration
+            // in-place.
+            updateConfigurationLocked(configuration, null, true);
+            final Configuration globalConfig = getGlobalConfiguration();
+            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Initial config: " + globalConfig);
+
+            // Load resources only after the current configuration has been set.
+            final Resources res = mContext.getResources();
+            mThumbnailWidth = res.getDimensionPixelSize(
+                    com.android.internal.R.dimen.thumbnail_width);
+            mThumbnailHeight = res.getDimensionPixelSize(
+                    com.android.internal.R.dimen.thumbnail_height);
+
+            if ((globalConfig.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
+                mFullscreenThumbnailScale = (float) res
+                        .getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
+                        (float) globalConfig.screenWidthDp;
+            } else {
+                mFullscreenThumbnailScale = res.getFraction(
+                        com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
+            }
+        }
+    }
+
+    // TODO: Will be converted to WM lock once transition is complete.
+    void setActivityManagerService(ActivityManagerService am) {
+        mAm = am;
+        mGlobalLock = mAm;
+        mH = new H(mAm.mHandlerThread.getLooper());
+        mUiHandler = new UiHandler();
+        mAppWarnings = new AppWarnings(
+                this, mUiContext, mH, mUiHandler, SystemServiceManager.ensureSystemDir());
+
+        mTempConfig.setToDefaults();
+        mTempConfig.setLocales(LocaleList.getDefault());
+        mConfigurationSeq = mTempConfig.seq = 1;
+        mStackSupervisor = createStackSupervisor();
+        mStackSupervisor.onConfigurationChanged(mTempConfig);
+
+        mTaskChangeNotificationController =
+                new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH);
+        mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mH);
+        mActivityStartController = new ActivityStartController(this);
+        mRecentTasks = createRecentTasks();
+        mStackSupervisor.setRecentTasks(mRecentTasks);
+        mVrController = new VrController(mGlobalLock);
+        mKeyguardController = mStackSupervisor.getKeyguardController();
+    }
+
+    void onActivityManagerInternalAdded() {
+        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
+    }
+
+    protected ActivityStackSupervisor createStackSupervisor() {
+        final ActivityStackSupervisor supervisor = new ActivityStackSupervisor(this, mH.getLooper());
+        supervisor.initialize();
+        return supervisor;
+    }
+
+    void setWindowManager(WindowManagerService wm) {
+        mWindowManager = wm;
+        mLockTaskController.setWindowManager(wm);
+    }
+
+    UserManagerService getUserManager() {
+        if (mUserManager == null) {
+            IBinder b = ServiceManager.getService(Context.USER_SERVICE);
+            mUserManager = (UserManagerService) IUserManager.Stub.asInterface(b);
+        }
+        return mUserManager;
+    }
+
+    AppOpsService getAppOpsService() {
+        if (mAppOpsService == null) {
+            IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+            mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b);
+        }
+        return mAppOpsService;
+    }
+
+    boolean hasUserRestriction(String restriction, int userId) {
+        return getUserManager().hasUserRestriction(restriction, userId);
+    }
+
+    protected RecentTasks createRecentTasks() {
+        return new RecentTasks(this, mStackSupervisor);
+    }
+
+    RecentTasks getRecentTasks() {
+        return mRecentTasks;
+    }
+
+    ClientLifecycleManager getLifecycleManager() {
+        return mLifecycleManager;
+    }
+
+    ActivityStartController getActivityStartController() {
+        return mActivityStartController;
+    }
+
+    TaskChangeNotificationController getTaskChangeNotificationController() {
+        return mTaskChangeNotificationController;
+    }
+
+    LockTaskController getLockTaskController() {
+        return mLockTaskController;
+    }
+
+    private void start() {
+        LocalServices.addService(ActivityTaskManagerInternal.class, new LocalService());
+    }
+
+    public static final class Lifecycle extends SystemService {
+        private final ActivityTaskManagerService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+            mService = new ActivityTaskManagerService(context);
+        }
+
+        @Override
+        public void onStart() {
+            publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);
+            mService.start();
+        }
+
+        public ActivityTaskManagerService getService() {
+            return mService;
+        }
+    }
+
+    @Override
+    public final int startActivity(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
+        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
+                resultWho, requestCode, startFlags, profilerInfo, bOptions,
+                UserHandle.getCallingUserId());
+    }
+
+    @Override
+    public final int startActivities(IApplicationThread caller, String callingPackage,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
+            int userId) {
+        final String reason = "startActivities";
+        enforceNotIsolatedCaller(reason);
+        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
+        // TODO: Switch to user app stacks here.
+        return getActivityStartController().startActivities(caller, -1, callingPackage, intents,
+                resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId, reason,
+                null /* originatingPendingIntent */);
+    }
+
+    @Override
+    public int startActivityAsUser(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
+                resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
+                true /*validateIncomingUser*/);
+    }
+
+    int startActivityAsUser(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
+            boolean validateIncomingUser) {
+        enforceNotIsolatedCaller("startActivityAsUser");
+
+        userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
+                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
+
+        // TODO: Switch to user app stacks here.
+        return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
+                .setCaller(caller)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setProfilerInfo(profilerInfo)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
+                .execute();
+
+    }
+
+    @Override
+    public int startActivityIntentSender(IApplicationThread caller, IIntentSender target,
+            IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) {
+        enforceNotIsolatedCaller("startActivityIntentSender");
+        // Refuse possible leaked file descriptors
+        if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+
+        if (!(target instanceof PendingIntentRecord)) {
+            throw new IllegalArgumentException("Bad PendingIntent object");
+        }
+
+        PendingIntentRecord pir = (PendingIntentRecord)target;
+
+        synchronized (mGlobalLock) {
+            // If this is coming from the currently resumed activity, it is
+            // effectively saying that app switches are allowed at this point.
+            final ActivityStack stack = getTopDisplayFocusedStack();
+            if (stack.mResumedActivity != null &&
+                    stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
+                mAppSwitchesAllowedTime = 0;
+            }
+        }
+        return pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null,
+                resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions);
+    }
+
+    @Override
+    public boolean startNextMatchingActivity(IBinder callingActivity, Intent intent,
+            Bundle bOptions) {
+        // Refuse possible leaked file descriptors
+        if (intent != null && intent.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+        SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
+
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(callingActivity);
+            if (r == null) {
+                SafeActivityOptions.abort(options);
+                return false;
+            }
+            if (!r.attachedToProcess()) {
+                // The caller is not running...  d'oh!
+                SafeActivityOptions.abort(options);
+                return false;
+            }
+            intent = new Intent(intent);
+            // The caller is not allowed to change the data.
+            intent.setDataAndType(r.intent.getData(), r.intent.getType());
+            // And we are resetting to find the next component...
+            intent.setComponent(null);
+
+            final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
+
+            ActivityInfo aInfo = null;
+            try {
+                List<ResolveInfo> resolves =
+                        AppGlobals.getPackageManager().queryIntentActivities(
+                                intent, r.resolvedType,
+                                PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS,
+                                UserHandle.getCallingUserId()).getList();
+
+                // Look for the original activity in the list...
+                final int N = resolves != null ? resolves.size() : 0;
+                for (int i=0; i<N; i++) {
+                    ResolveInfo rInfo = resolves.get(i);
+                    if (rInfo.activityInfo.packageName.equals(r.packageName)
+                            && rInfo.activityInfo.name.equals(r.info.name)) {
+                        // We found the current one...  the next matching is
+                        // after it.
+                        i++;
+                        if (i<N) {
+                            aInfo = resolves.get(i).activityInfo;
+                        }
+                        if (debug) {
+                            Slog.v(TAG, "Next matching activity: found current " + r.packageName
+                                    + "/" + r.info.name);
+                            Slog.v(TAG, "Next matching activity: next is " + ((aInfo == null)
+                                    ? "null" : aInfo.packageName + "/" + aInfo.name));
+                        }
+                        break;
+                    }
+                }
+            } catch (RemoteException e) {
+            }
+
+            if (aInfo == null) {
+                // Nobody who is next!
+                SafeActivityOptions.abort(options);
+                if (debug) Slog.d(TAG, "Next matching activity: nothing found");
+                return false;
+            }
+
+            intent.setComponent(new ComponentName(
+                    aInfo.applicationInfo.packageName, aInfo.name));
+            intent.setFlags(intent.getFlags()&~(
+                    Intent.FLAG_ACTIVITY_FORWARD_RESULT|
+                            Intent.FLAG_ACTIVITY_CLEAR_TOP|
+                            Intent.FLAG_ACTIVITY_MULTIPLE_TASK|
+                            FLAG_ACTIVITY_NEW_TASK));
+
+            // Okay now we need to start the new activity, replacing the currently running activity.
+            // This is a little tricky because we want to start the new one as if the current one is
+            // finished, but not finish the current one first so that there is no flicker.
+            // And thus...
+            final boolean wasFinishing = r.finishing;
+            r.finishing = true;
+
+            // Propagate reply information over to the new activity.
+            final ActivityRecord resultTo = r.resultTo;
+            final String resultWho = r.resultWho;
+            final int requestCode = r.requestCode;
+            r.resultTo = null;
+            if (resultTo != null) {
+                resultTo.removeResultsLocked(r, resultWho, requestCode);
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+            // TODO(b/64750076): Check if calling pid should really be -1.
+            final int res = getActivityStartController()
+                    .obtainStarter(intent, "startNextMatchingActivity")
+                    .setCaller(r.app.getThread())
+                    .setResolvedType(r.resolvedType)
+                    .setActivityInfo(aInfo)
+                    .setResultTo(resultTo != null ? resultTo.appToken : null)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setCallingPid(-1)
+                    .setCallingUid(r.launchedFromUid)
+                    .setCallingPackage(r.launchedFromPackage)
+                    .setRealCallingPid(-1)
+                    .setRealCallingUid(r.launchedFromUid)
+                    .setActivityOptions(options)
+                    .execute();
+            Binder.restoreCallingIdentity(origId);
+
+            r.finishing = wasFinishing;
+            if (res != ActivityManager.START_SUCCESS) {
+                return false;
+            }
+            return true;
+        }
+    }
+
+    @Override
+    public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+        final WaitResult res = new WaitResult();
+        synchronized (mGlobalLock) {
+            enforceNotIsolatedCaller("startActivityAndWait");
+            userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                    userId, "startActivityAndWait");
+            // TODO: Switch to user app stacks here.
+            getActivityStartController().obtainStarter(intent, "startActivityAndWait")
+                    .setCaller(caller)
+                    .setCallingPackage(callingPackage)
+                    .setResolvedType(resolvedType)
+                    .setResultTo(resultTo)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setStartFlags(startFlags)
+                    .setActivityOptions(bOptions)
+                    .setMayWait(userId)
+                    .setProfilerInfo(profilerInfo)
+                    .setWaitResult(res)
+                    .execute();
+        }
+        return res;
+    }
+
+    @Override
+    public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, Configuration config, Bundle bOptions, int userId) {
+        synchronized (mGlobalLock) {
+            enforceNotIsolatedCaller("startActivityWithConfig");
+            userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+                    "startActivityWithConfig");
+            // TODO: Switch to user app stacks here.
+            return getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
+                    .setCaller(caller)
+                    .setCallingPackage(callingPackage)
+                    .setResolvedType(resolvedType)
+                    .setResultTo(resultTo)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setStartFlags(startFlags)
+                    .setGlobalConfiguration(config)
+                    .setActivityOptions(bOptions)
+                    .setMayWait(userId)
+                    .execute();
+        }
+    }
+
+    @Override
+    public final int startActivityAsCaller(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, boolean ignoreTargetSecurity,
+            int userId) {
+
+        // This is very dangerous -- it allows you to perform a start activity (including
+        // permission grants) as any app that may launch one of your own activities.  So
+        // we will only allow this to be done from activities that are part of the core framework,
+        // and then only when they are running as the system.
+        final ActivityRecord sourceRecord;
+        final int targetUid;
+        final String targetPackage;
+        final boolean isResolver;
+        synchronized (mGlobalLock) {
+            if (resultTo == null) {
+                throw new SecurityException("Must be called from an activity");
+            }
+            sourceRecord = mStackSupervisor.isInAnyStackLocked(resultTo);
+            if (sourceRecord == null) {
+                throw new SecurityException("Called with bad activity token: " + resultTo);
+            }
+            if (!sourceRecord.info.packageName.equals("android")) {
+                throw new SecurityException(
+                        "Must be called from an activity that is declared in the android package");
+            }
+            if (sourceRecord.app == null) {
+                throw new SecurityException("Called without a process attached to activity");
+            }
+            if (UserHandle.getAppId(sourceRecord.app.mUid) != SYSTEM_UID) {
+                // This is still okay, as long as this activity is running under the
+                // uid of the original calling activity.
+                if (sourceRecord.app.mUid != sourceRecord.launchedFromUid) {
+                    throw new SecurityException(
+                            "Calling activity in uid " + sourceRecord.app.mUid
+                                    + " must be system uid or original calling uid "
+                                    + sourceRecord.launchedFromUid);
+                }
+            }
+            if (ignoreTargetSecurity) {
+                if (intent.getComponent() == null) {
+                    throw new SecurityException(
+                            "Component must be specified with ignoreTargetSecurity");
+                }
+                if (intent.getSelector() != null) {
+                    throw new SecurityException(
+                            "Selector not allowed with ignoreTargetSecurity");
+                }
+            }
+            targetUid = sourceRecord.launchedFromUid;
+            targetPackage = sourceRecord.launchedFromPackage;
+            isResolver = sourceRecord.isResolverOrChildActivity();
+        }
+
+        if (userId == UserHandle.USER_NULL) {
+            userId = UserHandle.getUserId(sourceRecord.app.mUid);
+        }
+
+        // TODO: Switch to user app stacks here.
+        try {
+            return getActivityStartController().obtainStarter(intent, "startActivityAsCaller")
+                    .setCallingUid(targetUid)
+                    .setCallingPackage(targetPackage)
+                    .setResolvedType(resolvedType)
+                    .setResultTo(resultTo)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setStartFlags(startFlags)
+                    .setActivityOptions(bOptions)
+                    .setMayWait(userId)
+                    .setIgnoreTargetSecurity(ignoreTargetSecurity)
+                    .setFilterCallingUid(isResolver ? 0 /* system */ : targetUid)
+                    .execute();
+        } catch (SecurityException e) {
+            // XXX need to figure out how to propagate to original app.
+            // A SecurityException here is generally actually a fault of the original
+            // calling activity (such as a fairly granting permissions), so propagate it
+            // back to them.
+            /*
+            StringBuilder msg = new StringBuilder();
+            msg.append("While launching");
+            msg.append(intent.toString());
+            msg.append(": ");
+            msg.append(e.getMessage());
+            */
+            throw e;
+        }
+    }
+
+    int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
+        return mAmInternal.handleIncomingUser(callingPid, callingUid, userId, false /* allowAll */,
+                ALLOW_FULL_ONLY, name, null /* callerPackage */);
+    }
+
+    @Override
+    public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
+            Intent intent, String resolvedType, IVoiceInteractionSession session,
+            IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
+            Bundle bOptions, int userId) {
+        mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
+        if (session == null || interactor == null) {
+            throw new NullPointerException("null session or interactor");
+        }
+        userId = handleIncomingUser(callingPid, callingUid, userId, "startVoiceActivity");
+        // TODO: Switch to user app stacks here.
+        return getActivityStartController().obtainStarter(intent, "startVoiceActivity")
+                .setCallingUid(callingUid)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setVoiceSession(session)
+                .setVoiceInteractor(interactor)
+                .setStartFlags(startFlags)
+                .setProfilerInfo(profilerInfo)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
+                .execute();
+    }
+
+    @Override
+    public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
+            Intent intent, String resolvedType, Bundle bOptions, int userId) {
+        mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
+        userId = handleIncomingUser(callingPid, callingUid, userId, "startAssistantActivity");
+
+        return getActivityStartController().obtainStarter(intent, "startAssistantActivity")
+                .setCallingUid(callingUid)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
+                .execute();
+    }
+
+    @Override
+    public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
+            IRecentsAnimationRunner recentsAnimationRunner) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
+        final int callingPid = Binder.getCallingPid();
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
+                final int recentsUid = mRecentTasks.getRecentsComponentUid();
+
+                // Start a new recents animation
+                final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
+                        getActivityStartController(), mWindowManager, callingPid);
+                anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
+                        recentsUid, assistDataReceiver);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public final int startActivityFromRecents(int taskId, Bundle bOptions) {
+        enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
+                "startActivityFromRecents()");
+
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
+        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
+                        safeOptions);
+            }
+        } 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.
+     * @param resultCode Result code, if any, from this Activity.
+     * @param resultData Result data (Intent), if any, from this Activity.
+     * @param finishTask Whether to finish the task associated with this Activity.
+     *
+     * @return Returns true if the activity successfully finished, or false if it is still running.
+     */
+    @Override
+    public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
+            int finishTask) {
+        // Refuse possible leaked file descriptors
+        if (resultData != null && resultData.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return true;
+            }
+            // Keep track of the root activity of the task before we finish it
+            TaskRecord tr = r.getTask();
+            ActivityRecord rootR = tr.getRootActivity();
+            if (rootR == null) {
+                Slog.w(TAG, "Finishing task with all activities already finished");
+            }
+            // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can
+            // finish.
+            if (getLockTaskController().activityBlockedFromFinish(r)) {
+                return false;
+            }
+
+            // TODO: There is a dup. of this block of code in ActivityStack.navigateUpToLocked
+            // We should consolidate.
+            if (mController != null) {
+                // Find the first activity that is not finishing.
+                ActivityRecord next = r.getStack().topRunningActivityLocked(token, 0);
+                if (next != null) {
+                    // ask watcher if this is allowed
+                    boolean resumeOK = true;
+                    try {
+                        resumeOK = mController.activityResuming(next.packageName);
+                    } catch (RemoteException e) {
+                        mController = null;
+                        Watchdog.getInstance().setActivityController(null);
+                    }
+
+                    if (!resumeOK) {
+                        Slog.i(TAG, "Not finishing activity because controller resumed");
+                        return false;
+                    }
+                }
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                boolean res;
+                final boolean finishWithRootActivity =
+                        finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
+                if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
+                        || (finishWithRootActivity && r == rootR)) {
+                    // If requested, remove the task that is associated to this activity only if it
+                    // was the root activity in the task. The result code and data is ignored
+                    // because we don't support returning them across task boundaries. Also, to
+                    // keep backwards compatibility we remove the task from recents when finishing
+                    // task with root activity.
+                    res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
+                            finishWithRootActivity, "finish-activity");
+                    if (!res) {
+                        Slog.i(TAG, "Removing task failed to finish activity");
+                    }
+                    // Explicitly dismissing the activity so reset its relaunch flag.
+                    r.mRelaunchReason = ActivityRecord.RELAUNCH_REASON_NONE;
+                } else {
+                    res = tr.getStack().requestFinishActivityLocked(token, resultCode,
+                            resultData, "app-request", true);
+                    if (!res) {
+                        Slog.i(TAG, "Failed to finish by app-request");
+                    }
+                }
+                return res;
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public boolean finishActivityAffinity(IBinder token) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+
+                // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps
+                // can finish.
+                final TaskRecord task = r.getTask();
+                if (getLockTaskController().activityBlockedFromFinish(r)) {
+                    return false;
+                }
+                return task.getStack().finishActivityAffinityLocked(r);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            WindowProcessController proc = null;
+            synchronized (mGlobalLock) {
+                ActivityStack stack = ActivityRecord.getStackLocked(token);
+                if (stack == null) {
+                    return;
+                }
+                final ActivityRecord r = mStackSupervisor.activityIdleInternalLocked(token,
+                        false /* fromTimeout */, false /* processPausingActivities */, config);
+                if (r != null) {
+                    proc = r.app;
+                }
+                if (stopProfiling && proc != null) {
+                    proc.clearProfilerIfNeeded();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public final void activityResumed(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        synchronized (mGlobalLock) {
+            ActivityRecord.activityResumedLocked(token);
+            mWindowManager.notifyAppResumedFinished(token);
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
+    public final void activityPaused(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        synchronized (mGlobalLock) {
+            ActivityStack stack = ActivityRecord.getStackLocked(token);
+            if (stack != null) {
+                stack.activityPausedLocked(token, false);
+            }
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
+    public final void activityStopped(IBinder token, Bundle icicle,
+            PersistableBundle persistentState, CharSequence description) {
+        if (DEBUG_ALL) Slog.v(TAG, "Activity stopped: token=" + token);
+
+        // Refuse possible leaked file descriptors
+        if (icicle != null && icicle.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Bundle");
+        }
+
+        final long origId = Binder.clearCallingIdentity();
+
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                r.activityStoppedLocked(icicle, persistentState, description);
+            }
+        }
+
+        mAmInternal.trimApplications();
+
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
+    public final void activityDestroyed(IBinder token) {
+        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
+        synchronized (mGlobalLock) {
+            ActivityStack stack = ActivityRecord.getStackLocked(token);
+            if (stack != null) {
+                stack.activityDestroyedLocked(token, "activityDestroyed");
+            }
+        }
+    }
+
+    @Override
+    public final void activityRelaunched(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        synchronized (mGlobalLock) {
+            mStackSupervisor.activityRelaunchedLocked(token);
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    public final void activitySlept(IBinder token) {
+        if (DEBUG_ALL) Slog.v(TAG, "Activity slept: token=" + token);
+
+        final long origId = Binder.clearCallingIdentity();
+
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                mStackSupervisor.activitySleptLocked(r);
+            }
+        }
+
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
+    public void setRequestedOrientation(IBinder token, int requestedOrientation) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setRequestedOrientation(requestedOrientation);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public int getRequestedOrientation(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+            }
+            return r.getRequestedOrientation();
+        }
+    }
+
+    @Override
+    public void setImmersive(IBinder token, boolean immersive) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                throw new IllegalArgumentException();
+            }
+            r.immersive = immersive;
+
+            // update associated state if we're frontmost
+            if (r.isResumedActivityOnDisplay()) {
+                if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
+                applyUpdateLockStateLocked(r);
+            }
+        }
+    }
+
+    void applyUpdateLockStateLocked(ActivityRecord r) {
+        // Modifications to the UpdateLock state are done on our handler, outside
+        // the activity manager's locks.  The new state is determined based on the
+        // state *now* of the relevant activity record.  The object is passed to
+        // the handler solely for logging detail, not to be consulted/modified.
+        final boolean nextState = r != null && r.immersive;
+        mH.post(() -> {
+            if (mUpdateLock.isHeld() != nextState) {
+                if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE,
+                        "Applying new update lock state '" + nextState + "' for " + r);
+                if (nextState) {
+                    mUpdateLock.acquire();
+                } else {
+                    mUpdateLock.release();
+                }
+            }
+        });
+    }
+
+    @Override
+    public boolean isImmersive(IBinder token) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                throw new IllegalArgumentException();
+            }
+            return r.immersive;
+        }
+    }
+
+    @Override
+    public boolean isTopActivityImmersive() {
+        enforceNotIsolatedCaller("isTopActivityImmersive");
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
+            return (r != null) ? r.immersive : false;
+        }
+    }
+
+    @Override
+    public void overridePendingTransition(IBinder token, String packageName,
+            int enterAnim, int exitAnim) {
+        synchronized (mGlobalLock) {
+            ActivityRecord self = ActivityRecord.isInStackLocked(token);
+            if (self == null) {
+                return;
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+
+            if (self.isState(
+                    ActivityStack.ActivityState.RESUMED, ActivityStack.ActivityState.PAUSING)) {
+                mWindowManager.overridePendingAppTransition(packageName,
+                        enterAnim, exitAnim, null);
+            }
+
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public int getFrontActivityScreenCompatMode() {
+        enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
+        ApplicationInfo ai;
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
+            if (r == null) {
+                return ActivityManager.COMPAT_MODE_UNKNOWN;
+            }
+            ai = r.info.applicationInfo;
+        }
+
+        return mAmInternal.getPackageScreenCompatMode(ai);
+    }
+
+    @Override
+    public void setFrontActivityScreenCompatMode(int mode) {
+        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+                "setFrontActivityScreenCompatMode");
+        ApplicationInfo ai;
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
+            if (r == null) {
+                Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
+                return;
+            }
+            ai = r.info.applicationInfo;
+        }
+
+        mAmInternal.setPackageScreenCompatMode(ai, mode);
+    }
+
+    @Override
+    public int getLaunchedFromUid(IBinder activityToken) {
+        ActivityRecord srec;
+        synchronized (mGlobalLock) {
+            srec = ActivityRecord.forTokenLocked(activityToken);
+        }
+        if (srec == null) {
+            return -1;
+        }
+        return srec.launchedFromUid;
+    }
+
+    @Override
+    public String getLaunchedFromPackage(IBinder activityToken) {
+        ActivityRecord srec;
+        synchronized (mGlobalLock) {
+            srec = ActivityRecord.forTokenLocked(activityToken);
+        }
+        if (srec == null) {
+            return null;
+        }
+        return srec.launchedFromPackage;
+    }
+
+    @Override
+    public boolean convertFromTranslucent(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+                final boolean translucentChanged = r.changeWindowTranslucency(true);
+                if (translucentChanged) {
+                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                }
+                mWindowManager.setAppFullscreen(token, true);
+                return translucentChanged;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public boolean convertToTranslucent(IBinder token, Bundle options) {
+        SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+                final TaskRecord task = r.getTask();
+                int index = task.mActivities.lastIndexOf(r);
+                if (index > 0) {
+                    ActivityRecord under = task.mActivities.get(index - 1);
+                    under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
+                }
+                final boolean translucentChanged = r.changeWindowTranslucency(false);
+                if (translucentChanged) {
+                    r.getStack().convertActivityToTranslucent(r);
+                }
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mWindowManager.setAppFullscreen(token, false);
+                return translucentChanged;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void notifyActivityDrawn(IBinder token) {
+        if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
+        synchronized (mGlobalLock) {
+            ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token);
+            if (r != null) {
+                r.getStack().notifyActivityDrawnLocked(r);
+            }
+        }
+    }
+
+    @Override
+    public void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            r.reportFullyDrawnLocked(restoredFromBundle);
+        }
+    }
+
+    @Override
+    public int getActivityDisplayId(IBinder activityToken) throws RemoteException {
+        synchronized (mGlobalLock) {
+            final ActivityStack stack = ActivityRecord.getStackLocked(activityToken);
+            if (stack != null && stack.mDisplayId != INVALID_DISPLAY) {
+                return stack.mDisplayId;
+            }
+            return DEFAULT_DISPLAY;
+        }
+    }
+
+    @Override
+    public ActivityManager.StackInfo getFocusedStackInfo() throws RemoteException {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                ActivityStack focusedStack = getTopDisplayFocusedStack();
+                if (focusedStack != null) {
+                    return mStackSupervisor.getStackInfo(focusedStack.mStackId);
+                }
+                return null;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void setFocusedStack(int stackId) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
+        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId);
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                if (stack == null) {
+                    Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
+                    return;
+                }
+                final ActivityRecord r = stack.topRunningActivityLocked();
+                if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
+                        r, "setFocusedStack")) {
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
+    public void setFocusedTask(int taskId) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
+        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    return;
+                }
+                final ActivityRecord r = task.topRunningActivityLocked();
+                if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, "setFocusedTask")) {
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
+    public boolean removeTask(int taskId) {
+        enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS,
+                        "remove-task");
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void removeAllVisibleRecentTasks() {
+        enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                getRecentTasks().removeAllVisibleTasks();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public boolean shouldUpRecreateTask(IBinder token, String destAffinity) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord srec = ActivityRecord.forTokenLocked(token);
+            if (srec != null) {
+                return srec.getStack().shouldUpRecreateTaskLocked(srec, destAffinity);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
+            Intent resultData) {
+
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r != null) {
+                return r.getStack().navigateUpToLocked(r, destIntent, resultCode, resultData);
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Attempts to move a task backwards in z-order (the order of activities within the task is
+     * unchanged).
+     *
+     * There are several possible results of this call:
+     * - if the task is locked, then we will show the lock toast
+     * - if there is a task behind the provided task, then that task is made visible and resumed as
+     *   this task is moved to the back
+     * - otherwise, if there are no other tasks in the stack:
+     *     - if this task is in the pinned stack, then we remove the stack completely, which will
+     *       have the effect of moving the task to the top or bottom of the fullscreen stack
+     *       (depending on whether it is visible)
+     *     - otherwise, we simply return home and hide this task
+     *
+     * @param token A reference to the activity we wish to move
+     * @param nonRoot If false then this only works if the activity is the root
+     *                of a task; if true it will work for any activity in a task.
+     * @return Returns true if the move completed, false if not.
+     */
+    @Override
+    public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
+        enforceNotIsolatedCaller("moveActivityTaskToBack");
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task != null) {
+                    return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public Rect getTaskBounds(int taskId) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getTaskBounds()");
+        long ident = Binder.clearCallingIdentity();
+        Rect rect = new Rect();
+        try {
+            synchronized (mGlobalLock) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+                if (task == null) {
+                    Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
+                    return rect;
+                }
+                if (task.getStack() != null) {
+                    // Return the bounds from window manager since it will be adjusted for various
+                    // things like the presense of a docked stack for tasks that aren't resizeable.
+                    task.getWindowContainerBounds(rect);
+                } else {
+                    // Task isn't in window manager yet since it isn't associated with a stack.
+                    // Return the persist value from activity manager
+                    if (!task.matchParentBounds()) {
+                        rect.set(task.getBounds());
+                    } else if (task.mLastNonFullscreenBounds != null) {
+                        rect.set(task.mLastNonFullscreenBounds);
+                    }
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return rect;
+    }
+
+    @Override
+    public ActivityManager.TaskDescription getTaskDescription(int id) {
+        synchronized (mGlobalLock) {
+            enforceCallerIsRecentsOrHasPermission(
+                    MANAGE_ACTIVITY_STACKS, "getTaskDescription()");
+            final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
+                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+            if (tr != null) {
+                return tr.lastTaskDescription;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
+        if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
+                    toTop, ANIMATE, null /* initialBounds */, true /* showRecents */);
+            return;
+        }
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
+                    return;
+                }
+
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId
+                        + " to windowingMode=" + windowingMode + " toTop=" + toTop);
+
+                if (!task.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+                            + " non-standard task " + taskId + " to windowing mode="
+                            + windowingMode);
+                }
+
+                final ActivityStack stack = task.getStack();
+                if (toTop) {
+                    stack.moveToFront("setTaskWindowingMode", task);
+                }
+                stack.setWindowingMode(windowingMode);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public String getCallingPackage(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = getCallingRecordLocked(token);
+            return r != null ? r.info.packageName : null;
+        }
+    }
+
+    @Override
+    public ComponentName getCallingActivity(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = getCallingRecordLocked(token);
+            return r != null ? r.intent.getComponent() : null;
+        }
+    }
+
+    private ActivityRecord getCallingRecordLocked(IBinder token) {
+        ActivityRecord r = ActivityRecord.isInStackLocked(token);
+        if (r == null) {
+            return null;
+        }
+        return r.resultTo;
+    }
+
+    @Override
+    public void unhandledBack() {
+        mAmInternal.enforceCallingPermission(android.Manifest.permission.FORCE_BACK, "unhandledBack()");
+
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                getTopDisplayFocusedStack().unhandledBackLocked();
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    /**
+     * TODO: Add mController hook
+     */
+    @Override
+    public void moveTaskToFront(int taskId, int flags, Bundle bOptions) {
+        mAmInternal.enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()");
+
+        if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId);
+        synchronized (mGlobalLock) {
+            moveTaskToFrontLocked(taskId, flags, SafeActivityOptions.fromBundle(bOptions),
+                    false /* fromRecents */);
+        }
+    }
+
+    void moveTaskToFrontLocked(int taskId, int flags, SafeActivityOptions options,
+            boolean fromRecents) {
+
+        if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
+                Binder.getCallingUid(), -1, -1, "Task to front")) {
+            SafeActivityOptions.abort(options);
+            return;
+        }
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+            if (task == null) {
+                Slog.d(TAG, "Could not find task for id: "+ taskId);
+                return;
+            }
+            if (getLockTaskController().isLockTaskModeViolation(task)) {
+                Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
+                return;
+            }
+            ActivityOptions realOptions = options != null
+                    ? options.getOptions(mStackSupervisor)
+                    : null;
+            mStackSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",
+                    false /* forceNonResizable */);
+
+            final ActivityRecord topActivity = task.getTopActivity();
+            if (topActivity != null) {
+
+                // We are reshowing a task, use a starting window to hide the initial draw delay
+                // so the transition can start earlier.
+                topActivity.showStartingWindow(null /* prev */, false /* newTask */,
+                        true /* taskSwitch */, fromRecents);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+        SafeActivityOptions.abort(options);
+    }
+
+    boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
+            int callingPid, int callingUid, String name) {
+        if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
+            return true;
+        }
+
+        if (getRecentTasks().isCallerRecents(sourceUid)) {
+            return true;
+        }
+
+        int perm = checkComponentPermission(STOP_APP_SWITCHES, sourcePid, sourceUid, -1, true);
+        if (perm == PackageManager.PERMISSION_GRANTED) {
+            return true;
+        }
+        if (checkAllowAppSwitchUid(sourceUid)) {
+            return true;
+        }
+
+        // If the actual IPC caller is different from the logical source, then
+        // also see if they are allowed to control app switches.
+        if (callingUid != -1 && callingUid != sourceUid) {
+            perm = checkComponentPermission(STOP_APP_SWITCHES, callingPid, callingUid, -1, true);
+            if (perm == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            }
+            if (checkAllowAppSwitchUid(callingUid)) {
+                return true;
+            }
+        }
+
+        Slog.w(TAG, name + " request from " + sourceUid + " stopped");
+        return false;
+    }
+
+    private boolean checkAllowAppSwitchUid(int uid) {
+        ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(UserHandle.getUserId(uid));
+        if (types != null) {
+            for (int i = types.size() - 1; i >= 0; i--) {
+                if (types.valueAt(i).intValue() == uid) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void setActivityController(IActivityController controller, boolean imAMonkey) {
+        mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+                "setActivityController()");
+        synchronized (mGlobalLock) {
+            mController = controller;
+            mControllerIsAMonkey = imAMonkey;
+            Watchdog.getInstance().setActivityController(controller);
+        }
+    }
+
+    boolean isControllerAMonkey() {
+        synchronized (mGlobalLock) {
+            return mController != null && mControllerIsAMonkey;
+        }
+    }
+
+    @Override
+    public int getTaskForActivity(IBinder token, boolean onlyRoot) {
+        synchronized (mGlobalLock) {
+            return ActivityRecord.getTaskForActivityLocked(token, onlyRoot);
+        }
+    }
+
+    @Override
+    public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
+        return getFilteredTasks(maxNum, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED);
+    }
+
+    @Override
+    public List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum,
+            @WindowConfiguration.ActivityType int ignoreActivityType,
+            @WindowConfiguration.WindowingMode int ignoreWindowingMode) {
+        final int callingUid = Binder.getCallingUid();
+        ArrayList<ActivityManager.RunningTaskInfo> list = new ArrayList<>();
+
+        synchronized (mGlobalLock) {
+            if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum);
+
+            final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
+                    callingUid);
+            mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
+                    ignoreWindowingMode, callingUid, allowed);
+        }
+
+        return list;
+    }
+
+    @Override
+    public final void finishSubActivity(IBinder token, String resultWho, int requestCode) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                r.getStack().finishSubActivityLocked(r, resultWho, requestCode);
+            }
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public boolean willActivityBeVisible(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityStack stack = ActivityRecord.getStackLocked(token);
+            if (stack != null) {
+                return stack.willActivityBeVisibleLocked(token);
+            }
+            return false;
+        }
+    }
+
+    @Override
+    public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
+                    return;
+                }
+
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
+                        + " to stackId=" + stackId + " toTop=" + toTop);
+
+                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                if (stack == null) {
+                    throw new IllegalStateException(
+                            "moveTaskToStack: No stack for stackId=" + stackId);
+                }
+                if (!stack.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException("moveTaskToStack: Attempt to move task "
+                            + taskId + " to stack " + stackId);
+                }
+                if (stack.inSplitScreenPrimaryWindowingMode()) {
+                    mWindowManager.setDockedStackCreateState(
+                            SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
+                }
+                task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
+                        "moveTaskToStack");
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode,
+            boolean preserveWindows, boolean animate, int animationDuration) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                if (animate) {
+                    final PinnedActivityStack stack = mStackSupervisor.getStack(stackId);
+                    if (stack == null) {
+                        Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
+                        return;
+                    }
+                    if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
+                        throw new IllegalArgumentException("Stack: " + stackId
+                                + " doesn't support animated resize.");
+                    }
+                    stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
+                            animationDuration, false /* fromFullscreen */);
+                } else {
+                    final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                    if (stack == null) {
+                        Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
+                        return;
+                    }
+                    mStackSupervisor.resizeStackLocked(stack, destBounds,
+                            null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+                            preserveWindows, allowResizeInDockedMode, !DEFER_RESUME);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Moves the specified task to the primary-split-screen stack.
+     *
+     * @param taskId Id of task to move.
+     * @param createMode The mode the primary split screen stack should be created in if it doesn't
+     *                   exist already. See
+     *                   {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
+     *                   and
+     *                   {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
+     * @param toTop If the task and stack should be moved to the top.
+     * @param animate Whether we should play an animation for the moving the task.
+     * @param initialBounds If the primary stack gets created, it will use these bounds for the
+     *                      stack. Pass {@code null} to use default bounds.
+     * @param showRecents If the recents activity should be shown on the other side of the task
+     *                    going into split-screen mode.
+     */
+    @Override
+    public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
+            boolean toTop, boolean animate, Rect initialBounds, boolean showRecents) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "setTaskWindowingModeSplitScreenPrimary()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
+                    return false;
+                }
+                if (DEBUG_STACK) Slog.d(TAG_STACK,
+                        "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
+                                + " to createMode=" + createMode + " toTop=" + toTop);
+                if (!task.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+                            + " non-standard task " + taskId + " to split-screen windowing mode");
+                }
+
+                mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+                final int windowingMode = task.getWindowingMode();
+                final ActivityStack stack = task.getStack();
+                if (toTop) {
+                    stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
+                }
+                stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
+                        false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */);
+                return windowingMode != task.getWindowingMode();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    /**
+     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+     */
+    @Override
+    public void removeStacksInWindowingModes(int[] windowingModes) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "removeStacksInWindowingModes()");
+
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mStackSupervisor.removeStacksInWindowingModes(windowingModes);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void removeStacksWithActivityTypes(int[] activityTypes) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "removeStacksWithActivityTypes()");
+
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mStackSupervisor.removeStacksWithActivityTypes(activityTypes);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
+            int userId) {
+        final int callingUid = Binder.getCallingUid();
+        userId = handleIncomingUser(Binder.getCallingPid(), callingUid, userId, "getRecentTasks");
+        final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
+                callingUid);
+        final boolean detailed = checkGetTasksPermission(
+                android.Manifest.permission.GET_DETAILED_TASKS, Binder.getCallingPid(),
+                UserHandle.getAppId(callingUid))
+                == PackageManager.PERMISSION_GRANTED;
+
+        synchronized (mGlobalLock) {
+            return mRecentTasks.getRecentTasks(maxNum, flags, allowed, detailed, userId,
+                    callingUid);
+        }
+    }
+
+    @Override
+    public List<ActivityManager.StackInfo> getAllStackInfos() {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.getAllStackInfosLocked();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.getStackInfo(windowingMode, activityType);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
+        final long callingUid = Binder.getCallingUid();
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                // Cancel the recents animation synchronously (do not hold the WM lock)
+                mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
+                        ? REORDER_MOVE_TO_ORIGINAL_POSITION
+                        : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void startLockTaskModeByToken(IBinder token) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r == null) {
+                return;
+            }
+            startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */);
+        }
+    }
+
+    @Override
+    public void startSystemLockTaskMode(int taskId) throws RemoteException {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startSystemLockTaskMode");
+        // This makes inner call to look as if it was initiated by system.
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+
+                // When starting lock task mode the stack must be in front and focused
+                task.getStack().moveToFront("startSystemLockTaskMode");
+                startLockTaskModeLocked(task, true /* isSystemCaller */);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void stopLockTaskModeByToken(IBinder token) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r == null) {
+                return;
+            }
+            stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */);
+        }
+    }
+
+    /**
+     * This API should be called by SystemUI only when user perform certain action to dismiss
+     * lock task mode. We should only dismiss pinned lock task mode in this case.
+     */
+    @Override
+    public void stopSystemLockTaskMode() throws RemoteException {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
+        stopLockTaskModeInternal(null, true /* isSystemCaller */);
+    }
+
+    private void startLockTaskModeLocked(@Nullable TaskRecord task, boolean isSystemCaller) {
+        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "startLockTaskModeLocked: " + task);
+        if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+            return;
+        }
+
+        final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
+        if (stack == null || task != stack.topTask()) {
+            throw new IllegalArgumentException("Invalid task, not in foreground");
+        }
+
+        // {@code isSystemCaller} is used to distinguish whether this request is initiated by the
+        // system or a specific app.
+        // * System-initiated requests will only start the pinned mode (screen pinning)
+        // * App-initiated requests
+        //   - will put the device in fully locked mode (LockTask), if the app is whitelisted
+        //   - will start the pinned mode, otherwise
+        final int callingUid = Binder.getCallingUid();
+        long ident = Binder.clearCallingIdentity();
+        try {
+            // When a task is locked, dismiss the pinned stack if it exists
+            mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+
+            getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void stopLockTaskModeInternal(@Nullable TaskRecord task, boolean isSystemCaller) {
+        final int callingUid = Binder.getCallingUid();
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                getLockTaskController().stopLockTaskMode(task, isSystemCaller, callingUid);
+            }
+            // Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock
+            // task and jumping straight into a call in the case of emergency call back.
+            TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+            if (tm != null) {
+                tm.showInCallScreen(false);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public boolean isInLockTaskMode() {
+        return getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+    }
+
+    @Override
+    public int getLockTaskModeState() {
+        synchronized (mGlobalLock) {
+            return getLockTaskController().getLockTaskModeState();
+        }
+    }
+
+    @Override
+    public void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                r.setTaskDescription(td);
+                final TaskRecord task = r.getTask();
+                task.updateTaskDescription();
+                mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td);
+            }
+        }
+    }
+
+    @Override
+    public Bundle getActivityOptions(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r != null) {
+                    final ActivityOptions activityOptions = r.takeOptionsLocked();
+                    return activityOptions == null ? null : activityOptions.toBundle();
+                }
+                return null;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public List<IBinder> getAppTasks(String callingPackage) {
+        int callingUid = Binder.getCallingUid();
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mRecentTasks.getAppTasksList(callingUid, callingPackage);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void finishVoiceTask(IVoiceInteractionSession session) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                // TODO: VI Consider treating local voice interactions and voice tasks
+                // differently here
+                mStackSupervisor.finishVoiceTask(session);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+
+    }
+
+    @Override
+    public boolean isTopOfTask(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            return r != null && r.getTask().getTopActivity() == r;
+        }
+    }
+
+    @Override
+    public void notifyLaunchTaskBehindComplete(IBinder token) {
+        mStackSupervisor.scheduleLaunchTaskBehindComplete(token);
+    }
+
+    @Override
+    public void notifyEnterAnimationComplete(IBinder token) {
+        mH.post(() -> {
+            synchronized (mGlobalLock) {
+                ActivityRecord r = ActivityRecord.forTokenLocked(token);
+                if (r != null && r.attachedToProcess()) {
+                    try {
+                        r.app.getThread().scheduleEnterAnimationComplete(r.appToken);
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+
+        });
+    }
+
+    /** Called from an app when assist data is ready. */
+    @Override
+    public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
+            AssistContent content, Uri referrer) {
+        PendingAssistExtras pae = (PendingAssistExtras) token;
+        synchronized (pae) {
+            pae.result = extras;
+            pae.structure = structure;
+            pae.content = content;
+            if (referrer != null) {
+                pae.extras.putParcelable(Intent.EXTRA_REFERRER, referrer);
+            }
+            if (structure != null) {
+                structure.setHomeActivity(pae.isHome);
+            }
+            pae.haveResult = true;
+            pae.notifyAll();
+            if (pae.intent == null && pae.receiver == null) {
+                // Caller is just waiting for the result.
+                return;
+            }
+        }
+        // We are now ready to launch the assist activity.
+        IAssistDataReceiver sendReceiver = null;
+        Bundle sendBundle = null;
+        synchronized (mGlobalLock) {
+            buildAssistBundleLocked(pae, extras);
+            boolean exists = mPendingAssistExtras.remove(pae);
+            mUiHandler.removeCallbacks(pae);
+            if (!exists) {
+                // Timed out.
+                return;
+            }
+
+            if ((sendReceiver = pae.receiver) != null) {
+                // Caller wants result sent back to them.
+                sendBundle = new Bundle();
+                sendBundle.putBundle(ASSIST_KEY_DATA, pae.extras);
+                sendBundle.putParcelable(ASSIST_KEY_STRUCTURE, pae.structure);
+                sendBundle.putParcelable(ASSIST_KEY_CONTENT, pae.content);
+                sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
+            }
+        }
+        if (sendReceiver != null) {
+            try {
+                sendReceiver.onHandleAssistData(sendBundle);
+            } catch (RemoteException e) {
+            }
+            return;
+        }
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            if (TextUtils.equals(pae.intent.getAction(),
+                    android.service.voice.VoiceInteractionService.SERVICE_INTERFACE)) {
+                pae.intent.putExtras(pae.extras);
+                mContext.startServiceAsUser(pae.intent, new UserHandle(pae.userHandle));
+            } else {
+                pae.intent.replaceExtras(pae.extras);
+                pae.intent.setFlags(FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_SINGLE_TOP
+                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                mAmInternal.closeSystemDialogs("assist");
+
+                try {
+                    mContext.startActivityAsUser(pae.intent, new UserHandle(pae.userHandle));
+                } catch (ActivityNotFoundException e) {
+                    Slog.w(TAG, "No activity to handle assist action.", e);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public int addAppTask(IBinder activityToken, Intent intent,
+            ActivityManager.TaskDescription description, Bitmap thumbnail) throws RemoteException {
+        final int callingUid = Binder.getCallingUid();
+        final long callingIdent = Binder.clearCallingIdentity();
+
+        try {
+            synchronized (mGlobalLock) {
+                ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
+                if (r == null) {
+                    throw new IllegalArgumentException("Activity does not exist; token="
+                            + activityToken);
+                }
+                ComponentName comp = intent.getComponent();
+                if (comp == null) {
+                    throw new IllegalArgumentException("Intent " + intent
+                            + " must specify explicit component");
+                }
+                if (thumbnail.getWidth() != mThumbnailWidth
+                        || thumbnail.getHeight() != mThumbnailHeight) {
+                    throw new IllegalArgumentException("Bad thumbnail size: got "
+                            + thumbnail.getWidth() + "x" + thumbnail.getHeight() + ", require "
+                            + mThumbnailWidth + "x" + mThumbnailHeight);
+                }
+                if (intent.getSelector() != null) {
+                    intent.setSelector(null);
+                }
+                if (intent.getSourceBounds() != null) {
+                    intent.setSourceBounds(null);
+                }
+                if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0) {
+                    if ((intent.getFlags()&Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS) == 0) {
+                        // The caller has added this as an auto-remove task...  that makes no
+                        // sense, so turn off auto-remove.
+                        intent.addFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
+                    }
+                }
+                final ActivityInfo ainfo = AppGlobals.getPackageManager().getActivityInfo(comp,
+                        STOCK_PM_FLAGS, UserHandle.getUserId(callingUid));
+                if (ainfo.applicationInfo.uid != callingUid) {
+                    throw new SecurityException(
+                            "Can't add task for another application: target uid="
+                                    + ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
+                }
+
+                final ActivityStack stack = r.getStack();
+                final TaskRecord task = stack.createTaskRecord(
+                        mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent,
+                        null /* voiceSession */, null /* voiceInteractor */, !ON_TOP);
+                if (!mRecentTasks.addToBottom(task)) {
+                    // The app has too many tasks already and we can't add any more
+                    stack.removeTask(task, "addAppTask", REMOVE_TASK_MODE_DESTROYING);
+                    return INVALID_TASK_ID;
+                }
+                task.lastTaskDescription.copyFrom(description);
+
+                // TODO: Send the thumbnail to WM to store it.
+
+                return task.taskId;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingIdent);
+        }
+    }
+
+    @Override
+    public Point getAppTaskThumbnailSize() {
+        synchronized (mGlobalLock) {
+            return new Point(mThumbnailWidth, mThumbnailHeight);
+        }
+    }
+
+    @Override
+    public void setTaskResizeable(int taskId, int resizeableMode) {
+        synchronized (mGlobalLock) {
+            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
+                    taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+            if (task == null) {
+                Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
+                return;
+            }
+            task.setResizeMode(resizeableMode);
+        }
+    }
+
+    @Override
+    public void resizeTask(int taskId, Rect bounds, int resizeMode) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
+                    return;
+                }
+                // Place the task in the right stack if it isn't there already based on
+                // the requested bounds.
+                // The stack transition logic is:
+                // - a null bounds on a freeform task moves that task to fullscreen
+                // - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
+                //   that task to freeform
+                // - otherwise the task is not moved
+                ActivityStack stack = task.getStack();
+                if (!task.getWindowConfiguration().canResizeTask()) {
+                    throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
+                }
+                if (bounds == null && stack.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+                    stack = stack.getDisplay().getOrCreateStack(
+                            WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
+                } else if (bounds != null && stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+                    stack = stack.getDisplay().getOrCreateStack(
+                            WINDOWING_MODE_FREEFORM, stack.getActivityType(), ON_TOP);
+                }
+
+                // Reparent the task to the right stack if necessary
+                boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
+                if (stack != task.getStack()) {
+                    // Defer resume until the task is resized below
+                    task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
+                            DEFER_RESUME, "resizeTask");
+                    preserveWindow = false;
+                }
+
+                // After reparenting (which only resizes the task to the stack bounds), resize the
+                // task to the actual bounds provided
+                task.resize(bounds, resizeMode, preserveWindow, !DEFER_RESUME);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public boolean releaseActivityInstance(IBinder token) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+                return r.getStack().safelyDestroyActivityLocked(r, "app-req");
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void releaseSomeActivities(IApplicationThread appInt) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                WindowProcessController app =
+                        mAm.getRecordForAppLocked(appInt).getWindowProcessController();
+                mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing,
+            int secondaryDisplayShowing) {
+        if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires permission "
+                    + android.Manifest.permission.DEVICE_POWER);
+        }
+
+        synchronized (mGlobalLock) {
+            long ident = Binder.clearCallingIdentity();
+            if (mKeyguardShown != keyguardShowing) {
+                mKeyguardShown = keyguardShowing;
+                reportCurKeyguardUsageEventLocked(keyguardShowing);
+            }
+            try {
+                mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing,
+                        secondaryDisplayShowing);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        mH.post(() -> {
+            for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+                mScreenObservers.get(i).onKeyguardStateChanged(keyguardShowing);
+            }
+        });
+    }
+
+    void onScreenAwakeChanged(boolean isAwake) {
+        mH.post(() -> {
+            for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+                mScreenObservers.get(i).onAwakeStateChanged(isAwake);
+            }
+        });
+    }
+
+    @Override
+    public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
+        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, "getTaskDescriptionIcon");
+
+        final File passedIconFile = new File(filePath);
+        final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId),
+                passedIconFile.getName());
+        if (!legitIconFile.getPath().equals(filePath)
+                || !filePath.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
+            throw new IllegalArgumentException("Bad file path: " + filePath
+                    + " passed for userId " + userId);
+        }
+        return mRecentTasks.getTaskDescriptionIcon(filePath);
+    }
+
+    @Override
+    public void startInPlaceAnimationOnFrontMostApplication(Bundle opts) {
+        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(opts);
+        final ActivityOptions activityOptions = safeOptions != null
+                ? safeOptions.getOptions(mStackSupervisor)
+                : null;
+        if (activityOptions == null
+                || activityOptions.getAnimationType() != ActivityOptions.ANIM_CUSTOM_IN_PLACE
+                || activityOptions.getCustomInPlaceResId() == 0) {
+            throw new IllegalArgumentException("Expected in-place ActivityOption " +
+                    "with valid animation");
+        }
+        mWindowManager.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
+        mWindowManager.overridePendingAppTransitionInPlace(activityOptions.getPackageName(),
+                activityOptions.getCustomInPlaceResId());
+        mWindowManager.executeAppTransition();
+    }
+
+    @Override
+    public void removeStack(int stackId) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                if (stack == null) {
+                    Slog.w(TAG, "removeStack: No stack with id=" + stackId);
+                    return;
+                }
+                if (!stack.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException(
+                            "Removing non-standard stack is not allowed.");
+                }
+                mStackSupervisor.removeStack(stack);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void moveStackToDisplay(int stackId, int displayId) {
+        mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
+
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveStackToDisplay: moving stackId=" + stackId
+                        + " to displayId=" + displayId);
+                mStackSupervisor.moveStackToDisplayLocked(stackId, displayId, ON_TOP);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void exitFreeformMode(IBinder token) {
+        synchronized (mGlobalLock) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+                if (r == null) {
+                    throw new IllegalArgumentException(
+                            "exitFreeformMode: No activity record matching token=" + token);
+                }
+
+                final ActivityStack stack = r.getStack();
+                if (stack == null || !stack.inFreeformWindowingMode()) {
+                    throw new IllegalStateException(
+                            "exitFreeformMode: You can only go fullscreen from freeform.");
+                }
+
+                stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    /** Sets the task stack listener that gets callbacks when a task stack changes. */
+    @Override
+    public void registerTaskStackListener(ITaskStackListener listener) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "registerTaskStackListener()");
+        mTaskChangeNotificationController.registerTaskStackListener(listener);
+    }
+
+    /** Unregister a task stack listener so that it stops receiving callbacks. */
+    @Override
+    public void unregisterTaskStackListener(ITaskStackListener listener) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "unregisterTaskStackListener()");
+        mTaskChangeNotificationController.unregisterTaskStackListener(listener);
+    }
+
+    private void reportCurKeyguardUsageEventLocked(boolean keyguardShowing) {
+        mAm.reportGlobalUsageEventLocked(keyguardShowing
+                ? UsageEvents.Event.KEYGUARD_SHOWN
+                : UsageEvents.Event.KEYGUARD_HIDDEN);
+    }
+
+    @Override
+    public boolean requestAssistContextExtras(int requestType, IAssistDataReceiver receiver,
+            Bundle receiverExtras, IBinder activityToken, boolean focused, boolean newSessionId) {
+        return enqueueAssistContext(requestType, null, null, receiver, receiverExtras,
+                activityToken, focused, newSessionId, UserHandle.getCallingUserId(), null,
+                PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0) != null;
+    }
+
+    @Override
+    public boolean requestAutofillData(IAssistDataReceiver receiver, Bundle receiverExtras,
+            IBinder activityToken, int flags) {
+        return enqueueAssistContext(ActivityManager.ASSIST_CONTEXT_AUTOFILL, null, null,
+                receiver, receiverExtras, activityToken, true, true, UserHandle.getCallingUserId(),
+                null, PENDING_AUTOFILL_ASSIST_STRUCTURE_TIMEOUT, flags) != null;
+    }
+
+    @Override
+    public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle,
+            Bundle args) {
+        return enqueueAssistContext(requestType, intent, hint, null, null, null,
+                true /* focused */, true /* newSessionId */, userHandle, args,
+                PENDING_ASSIST_EXTRAS_TIMEOUT, 0) != null;
+    }
+
+    @Override
+    public Bundle getAssistContextExtras(int requestType) {
+        PendingAssistExtras pae = enqueueAssistContext(requestType, null, null, null,
+                null, null, true /* focused */, true /* newSessionId */,
+                UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_TIMEOUT, 0);
+        if (pae == null) {
+            return null;
+        }
+        synchronized (pae) {
+            while (!pae.haveResult) {
+                try {
+                    pae.wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+        synchronized (mGlobalLock) {
+            buildAssistBundleLocked(pae, pae.result);
+            mPendingAssistExtras.remove(pae);
+            mUiHandler.removeCallbacks(pae);
+        }
+        return pae.extras;
+    }
+
+    /**
+     * Binder IPC calls go through the public entry point.
+     * This can be called with or without the global lock held.
+     */
+    private static int checkCallingPermission(String permission) {
+        return checkPermission(
+                permission, Binder.getCallingPid(), UserHandle.getAppId(Binder.getCallingUid()));
+    }
+
+    /** This can be called with or without the global lock held. */
+    void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
+        if (!getRecentTasks().isCallerRecents(Binder.getCallingUid())) {
+            mAmInternal.enforceCallingPermission(permission, func);
+        }
+    }
+
+    @VisibleForTesting
+    int checkGetTasksPermission(String permission, int pid, int uid) {
+        return checkPermission(permission, pid, uid);
+    }
+
+    static int checkPermission(String permission, int pid, int uid) {
+        if (permission == null) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+        return checkComponentPermission(permission, pid, uid, -1, true);
+    }
+
+    boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
+        if (getRecentTasks().isCallerRecents(callingUid)) {
+            // Always allow the recents component to get tasks
+            return true;
+        }
+
+        boolean allowed = checkGetTasksPermission(android.Manifest.permission.REAL_GET_TASKS,
+                callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+        if (!allowed) {
+            if (checkGetTasksPermission(android.Manifest.permission.GET_TASKS,
+                    callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) {
+                // Temporary compatibility: some existing apps on the system image may
+                // still be requesting the old permission and not switched to the new
+                // one; if so, we'll still allow them full access.  This means we need
+                // to see if they are holding the old permission and are a system app.
+                try {
+                    if (AppGlobals.getPackageManager().isUidPrivileged(callingUid)) {
+                        allowed = true;
+                        if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
+                                + " is using old GET_TASKS but privileged; allowing");
+                    }
+                } catch (RemoteException e) {
+                }
+            }
+            if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
+                    + " does not hold REAL_GET_TASKS; limiting output");
+        }
+        return allowed;
+    }
+
+    private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
+            IAssistDataReceiver receiver, Bundle receiverExtras, IBinder activityToken,
+            boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
+            int flags) {
+        mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
+                "enqueueAssistContext()");
+
+        synchronized (mGlobalLock) {
+            ActivityRecord activity = getTopDisplayFocusedStack().getTopActivity();
+            if (activity == null) {
+                Slog.w(TAG, "getAssistContextExtras failed: no top activity");
+                return null;
+            }
+            if (!activity.attachedToProcess()) {
+                Slog.w(TAG, "getAssistContextExtras failed: no process for " + activity);
+                return null;
+            }
+            if (focused) {
+                if (activityToken != null) {
+                    ActivityRecord caller = ActivityRecord.forTokenLocked(activityToken);
+                    if (activity != caller) {
+                        Slog.w(TAG, "enqueueAssistContext failed: caller " + caller
+                                + " is not current top " + activity);
+                        return null;
+                    }
+                }
+            } else {
+                activity = ActivityRecord.forTokenLocked(activityToken);
+                if (activity == null) {
+                    Slog.w(TAG, "enqueueAssistContext failed: activity for token=" + activityToken
+                            + " couldn't be found");
+                    return null;
+                }
+                if (!activity.attachedToProcess()) {
+                    Slog.w(TAG, "enqueueAssistContext failed: no process for " + activity);
+                    return null;
+                }
+            }
+
+            PendingAssistExtras pae;
+            Bundle extras = new Bundle();
+            if (args != null) {
+                extras.putAll(args);
+            }
+            extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName);
+            extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.mUid);
+
+            pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, receiverExtras,
+                    userHandle);
+            pae.isHome = activity.isActivityTypeHome();
+
+            // Increment the sessionId if necessary
+            if (newSessionId) {
+                mViSessionId++;
+            }
+            try {
+                activity.app.getThread().requestAssistContextExtras(activity.appToken, pae,
+                        requestType, mViSessionId, flags);
+                mPendingAssistExtras.add(pae);
+                mUiHandler.postDelayed(pae, timeout);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "getAssistContextExtras failed: crash calling " + activity);
+                return null;
+            }
+            return pae;
+        }
+    }
+
+    private void buildAssistBundleLocked(PendingAssistExtras pae, Bundle result) {
+        if (result != null) {
+            pae.extras.putBundle(Intent.EXTRA_ASSIST_CONTEXT, result);
+        }
+        if (pae.hint != null) {
+            pae.extras.putBoolean(pae.hint, true);
+        }
+    }
+
+    private void pendingAssistExtrasTimedOut(PendingAssistExtras pae) {
+        IAssistDataReceiver receiver;
+        synchronized (mGlobalLock) {
+            mPendingAssistExtras.remove(pae);
+            receiver = pae.receiver;
+        }
+        if (receiver != null) {
+            // Caller wants result sent back to them.
+            Bundle sendBundle = new Bundle();
+            // At least return the receiver extras
+            sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
+            try {
+                pae.receiver.onHandleAssistData(sendBundle);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    public class PendingAssistExtras extends Binder implements Runnable {
+        public final ActivityRecord activity;
+        public boolean isHome;
+        public final Bundle extras;
+        public final Intent intent;
+        public final String hint;
+        public final IAssistDataReceiver receiver;
+        public final int userHandle;
+        public boolean haveResult = false;
+        public Bundle result = null;
+        public AssistStructure structure = null;
+        public AssistContent content = null;
+        public Bundle receiverExtras;
+
+        public PendingAssistExtras(ActivityRecord _activity, Bundle _extras, Intent _intent,
+                String _hint, IAssistDataReceiver _receiver, Bundle _receiverExtras,
+                int _userHandle) {
+            activity = _activity;
+            extras = _extras;
+            intent = _intent;
+            hint = _hint;
+            receiver = _receiver;
+            receiverExtras = _receiverExtras;
+            userHandle = _userHandle;
+        }
+
+        @Override
+        public void run() {
+            Slog.w(TAG, "getAssistContextExtras failed: timeout retrieving from " + activity);
+            synchronized (this) {
+                haveResult = true;
+                notifyAll();
+            }
+            pendingAssistExtrasTimedOut(this);
+        }
+    }
+
+    @Override
+    public boolean isAssistDataAllowedOnCurrentActivity() {
+        int userId;
+        synchronized (mGlobalLock) {
+            final ActivityStack focusedStack = getTopDisplayFocusedStack();
+            if (focusedStack == null || focusedStack.isActivityTypeAssistant()) {
+                return false;
+            }
+
+            final ActivityRecord activity = focusedStack.getTopActivity();
+            if (activity == null) {
+                return false;
+            }
+            userId = activity.userId;
+        }
+        return !DevicePolicyCache.getInstance().getScreenCaptureDisabled(userId);
+    }
+
+    @Override
+    public boolean showAssistFromActivity(IBinder token, Bundle args) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                ActivityRecord caller = ActivityRecord.forTokenLocked(token);
+                ActivityRecord top = getTopDisplayFocusedStack().getTopActivity();
+                if (top != caller) {
+                    Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+                            + " is not current top " + top);
+                    return false;
+                }
+                if (!top.nowVisible) {
+                    Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+                            + " is not visible");
+                    return false;
+                }
+            }
+            return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION, null,
+                    token);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public boolean isRootVoiceInteraction(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return false;
+            }
+            return r.rootVoiceInteraction;
+        }
+    }
+
+    private void onLocalVoiceInteractionStartedLocked(IBinder activity,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
+        ActivityRecord activityToCallback = ActivityRecord.forTokenLocked(activity);
+        if (activityToCallback == null) return;
+        activityToCallback.setVoiceSessionLocked(voiceSession);
+
+        // Inform the activity
+        try {
+            activityToCallback.app.getThread().scheduleLocalVoiceInteractionStarted(activity,
+                    voiceInteractor);
+            long token = Binder.clearCallingIdentity();
+            try {
+                startRunningVoiceLocked(voiceSession, activityToCallback.appInfo.uid);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            // TODO: VI Should we cache the activity so that it's easier to find later
+            // rather than scan through all the stacks and activities?
+        } catch (RemoteException re) {
+            activityToCallback.clearVoiceSessionLocked();
+            // TODO: VI Should this terminate the voice session?
+        }
+    }
+
+    private void startRunningVoiceLocked(IVoiceInteractionSession session, int targetUid) {
+        Slog.d(TAG, "<<<  startRunningVoiceLocked()");
+        mVoiceWakeLock.setWorkSource(new WorkSource(targetUid));
+        if (mRunningVoice == null || mRunningVoice.asBinder() != session.asBinder()) {
+            boolean wasRunningVoice = mRunningVoice != null;
+            mRunningVoice = session;
+            if (!wasRunningVoice) {
+                mVoiceWakeLock.acquire();
+                updateSleepIfNeededLocked();
+            }
+        }
+    }
+
+    void finishRunningVoiceLocked() {
+        if (mRunningVoice != null) {
+            mRunningVoice = null;
+            mVoiceWakeLock.release();
+            updateSleepIfNeededLocked();
+        }
+    }
+
+    @Override
+    public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake) {
+        synchronized (mGlobalLock) {
+            if (mRunningVoice != null && mRunningVoice.asBinder() == session.asBinder()) {
+                if (keepAwake) {
+                    mVoiceWakeLock.acquire();
+                } else {
+                    mVoiceWakeLock.release();
+                }
+            }
+        }
+    }
+
+    @Override
+    public ComponentName getActivityClassForToken(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return null;
+            }
+            return r.intent.getComponent();
+        }
+    }
+
+    @Override
+    public String getPackageForToken(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return null;
+            }
+            return r.packageName;
+        }
+    }
+
+    @Override
+    public void showLockTaskEscapeMessage(IBinder token) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r == null) {
+                return;
+            }
+            getLockTaskController().showLockTaskToast();
+        }
+    }
+
+    @Override
+    public void keyguardGoingAway(int flags) {
+        enforceNotIsolatedCaller("keyguardGoingAway");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mKeyguardController.keyguardGoingAway(flags);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Try to place task to provided position. The final position might be different depending on
+     * current user and stacks state. The task will be moved to target stack if it's currently in
+     * different stack.
+     */
+    @Override
+    public void positionTaskInStack(int taskId, int stackId, int position) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
+        synchronized (mGlobalLock) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "positionTaskInStack: positioning task="
+                        + taskId + " in stackId=" + stackId + " at position=" + position);
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    throw new IllegalArgumentException("positionTaskInStack: no task for id="
+                            + taskId);
+                }
+
+                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+
+                if (stack == null) {
+                    throw new IllegalArgumentException("positionTaskInStack: no stack for id="
+                            + stackId);
+                }
+                if (!stack.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException("positionTaskInStack: Attempt to change"
+                            + " the position of task " + taskId + " in/to non-standard stack");
+                }
+
+                // TODO: Have the callers of this API call a separate reparent method if that is
+                // what they intended to do vs. having this method also do reparenting.
+                if (task.getStack() == stack) {
+                    // Change position in current stack.
+                    stack.positionChildAt(task, position);
+                } else {
+                    // Reparent to new stack.
+                    task.reparent(stack, position, REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE,
+                            !DEFER_RESUME, "positionTaskInStack");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
+            int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
+        if (DEBUG_CONFIGURATION) Slog.v(TAG, "Report configuration: " + token + " "
+                + horizontalSizeConfiguration + " " + verticalSizeConfigurations);
+        synchronized (mGlobalLock) {
+            ActivityRecord record = ActivityRecord.isInStackLocked(token);
+            if (record == null) {
+                throw new IllegalArgumentException("reportSizeConfigurations: ActivityRecord not "
+                        + "found for: " + token);
+            }
+            record.setSizeConfigurations(horizontalSizeConfiguration,
+                    verticalSizeConfigurations, smallestSizeConfigurations);
+        }
+    }
+
+    /**
+     * Dismisses split-screen multi-window mode.
+     * @param toTop If true the current primary split-screen stack will be placed or left on top.
+     */
+    @Override
+    public void dismissSplitScreenMode(boolean toTop) {
+        enforceCallerIsRecentsOrHasPermission(
+                MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityStack stack =
+                        mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+                if (stack == null) {
+                    Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
+                    return;
+                }
+
+                if (toTop) {
+                    // Caller wants the current split-screen primary stack to be the top stack after
+                    // it goes fullscreen, so move it to the front.
+                    stack.moveToFront("dismissSplitScreenMode");
+                } else if (mStackSupervisor.isTopDisplayFocusedStack(stack)) {
+                    // In this case the current split-screen primary stack shouldn't be the top
+                    // stack after it goes fullscreen, but it current has focus, so we move the
+                    // focus to the top-most split-screen secondary stack next to it.
+                    final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
+                            WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                    if (otherStack != null) {
+                        otherStack.moveToFront("dismissSplitScreenMode_other");
+                    }
+                }
+
+                stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Dismisses Pip
+     * @param animate True if the dismissal should be animated.
+     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
+     *                          default animation duration should be used.
+     */
+    @Override
+    public void dismissPip(boolean animate, int animationDuration) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final PinnedActivityStack stack =
+                        mStackSupervisor.getDefaultDisplay().getPinnedStack();
+                if (stack == null) {
+                    Slog.w(TAG, "dismissPip: pinned stack not found.");
+                    return;
+                }
+                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
+                    throw new IllegalArgumentException("Stack: " + stack
+                            + " doesn't support animated resize.");
+                }
+                if (animate) {
+                    stack.animateResizePinnedStack(null /* sourceHintBounds */,
+                            null /* destBounds */, animationDuration, false /* fromFullscreen */);
+                } else {
+                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, true /* onTop */);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()");
+        synchronized (mGlobalLock) {
+            mSuppressResizeConfigChanges = suppress;
+        }
+    }
+
+    /**
+     * NOTE: For the pinned stack, this method is usually called after the bounds animation has
+     *       animated the stack to the fullscreen, but can also be called if we are relaunching an
+     *       activity and clearing the task at the same time.
+     */
+    @Override
+    // TODO: API should just be about changing windowing modes...
+    public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "moveTasksToFullscreenStack()");
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
+                if (stack != null){
+                    if (!stack.isActivityTypeStandardOrUndefined()) {
+                        throw new IllegalArgumentException(
+                                "You can't move tasks from non-standard stacks.");
+                    }
+                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, onTop);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    /**
+     * Moves the top activity in the input stackId to the pinned stack.
+     *
+     * @param stackId Id of stack to move the top activity to pinned stack.
+     * @param bounds Bounds to use for pinned stack.
+     *
+     * @return True if the top activity of the input stack was successfully moved to the pinned
+     *          stack.
+     */
+    @Override
+    public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "moveTopActivityToPinnedStack()");
+        synchronized (mGlobalLock) {
+            if (!mSupportsPictureInPicture) {
+                throw new IllegalStateException("moveTopActivityToPinnedStack:"
+                        + "Device doesn't support picture-in-picture mode");
+            }
+
+            long ident = Binder.clearCallingIdentity();
+            try {
+                return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public boolean isInMultiWindowMode(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+                // An activity is consider to be in multi-window mode if its task isn't fullscreen.
+                return r.inMultiWindowMode();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public boolean isInPictureInPictureMode(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return isInPictureInPictureMode(ActivityRecord.forTokenLocked(token));
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    private boolean isInPictureInPictureMode(ActivityRecord r) {
+        if (r == null || r.getStack() == null || !r.inPinnedWindowingMode()
+                || r.getStack().isInStackLocked(r) == null) {
+            return false;
+        }
+
+        // If we are animating to fullscreen then we have already dispatched the PIP mode
+        // changed, so we should reflect that check here as well.
+        final PinnedActivityStack stack = r.getStack();
+        final PinnedStackWindowController windowController = stack.getWindowContainerController();
+        return !windowController.isAnimatingBoundsToFullscreen();
+    }
+
+    @Override
+    public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
+                        "enterPictureInPictureMode", token, params);
+
+                // If the activity is already in picture in picture mode, then just return early
+                if (isInPictureInPictureMode(r)) {
+                    return true;
+                }
+
+                // Activity supports picture-in-picture, now check that we can enter PiP at this
+                // point, if it is
+                if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode",
+                        false /* beforeStopping */)) {
+                    return false;
+                }
+
+                final Runnable enterPipRunnable = () -> {
+                    synchronized (mGlobalLock) {
+                        // Only update the saved args from the args that are set
+                        r.pictureInPictureArgs.copyOnlySet(params);
+                        final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
+                        final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
+                        // Adjust the source bounds by the insets for the transition down
+                        final Rect sourceBounds = new Rect(
+                                r.pictureInPictureArgs.getSourceRectHint());
+                        mStackSupervisor.moveActivityToPinnedStackLocked(
+                                r, sourceBounds, aspectRatio, "enterPictureInPictureMode");
+                        final PinnedActivityStack stack = r.getStack();
+                        stack.setPictureInPictureAspectRatio(aspectRatio);
+                        stack.setPictureInPictureActions(actions);
+                        MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
+                                r.shortComponentName, r.supportsEnterPipOnTaskSwitch);
+                        logPictureInPictureArgs(params);
+                    }
+                };
+
+                if (isKeyguardLocked()) {
+                    // If the keyguard is showing or occluded, then try and dismiss it before
+                    // entering picture-in-picture (this will prompt the user to authenticate if the
+                    // device is currently locked).
+                    dismissKeyguard(token, new KeyguardDismissCallback() {
+                        @Override
+                        public void onDismissSucceeded() {
+                            mH.post(enterPipRunnable);
+                        }
+                    }, null /* message */);
+                } else {
+                    // Enter picture in picture immediately otherwise
+                    enterPipRunnable.run();
+                }
+                return true;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
+                        "setPictureInPictureParams", token, params);
+
+                // Only update the saved args from the args that are set
+                r.pictureInPictureArgs.copyOnlySet(params);
+                if (r.inPinnedWindowingMode()) {
+                    // If the activity is already in picture-in-picture, update the pinned stack now
+                    // if it is not already expanding to fullscreen. Otherwise, the arguments will
+                    // be used the next time the activity enters PiP
+                    final PinnedActivityStack stack = r.getStack();
+                    if (!stack.isAnimatingBoundsToFullscreen()) {
+                        stack.setPictureInPictureAspectRatio(
+                                r.pictureInPictureArgs.getAspectRatio());
+                        stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
+                    }
+                }
+                logPictureInPictureArgs(params);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public int getMaxNumPictureInPictureActions(IBinder token) {
+        // Currently, this is a static constant, but later, we may change this to be dependent on
+        // the context of the activity
+        return 3;
+    }
+
+    private void logPictureInPictureArgs(PictureInPictureParams params) {
+        if (params.hasSetActions()) {
+            MetricsLogger.histogram(mContext, "tron_varz_picture_in_picture_actions_count",
+                    params.getActions().size());
+        }
+        if (params.hasSetAspectRatio()) {
+            LogMaker lm = new LogMaker(MetricsEvent.ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED);
+            lm.addTaggedData(MetricsEvent.PICTURE_IN_PICTURE_ASPECT_RATIO, params.getAspectRatio());
+            MetricsLogger.action(lm);
+        }
+    }
+
+    /**
+     * Checks the state of the system and the activity associated with the given {@param token} to
+     * verify that picture-in-picture is supported for that activity.
+     *
+     * @return the activity record for the given {@param token} if all the checks pass.
+     */
+    private ActivityRecord ensureValidPictureInPictureActivityParamsLocked(String caller,
+            IBinder token, PictureInPictureParams params) {
+        if (!mSupportsPictureInPicture) {
+            throw new IllegalStateException(caller
+                    + ": Device doesn't support picture-in-picture mode.");
+        }
+
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        if (r == null) {
+            throw new IllegalStateException(caller
+                    + ": Can't find activity for token=" + token);
+        }
+
+        if (!r.supportsPictureInPicture()) {
+            throw new IllegalStateException(caller
+                    + ": Current activity does not support picture-in-picture.");
+        }
+
+        if (params.hasSetAspectRatio()
+                && !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
+                params.getAspectRatio())) {
+            final float minAspectRatio = mContext.getResources().getFloat(
+                    com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
+            final float maxAspectRatio = mContext.getResources().getFloat(
+                    com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
+            throw new IllegalArgumentException(String.format(caller
+                            + ": Aspect ratio is too extreme (must be between %f and %f).",
+                    minAspectRatio, maxAspectRatio));
+        }
+
+        // Truncate the number of actions if necessary
+        params.truncateActions(getMaxNumPictureInPictureActions(token));
+
+        return r;
+    }
+
+    @Override
+    public IBinder getUriPermissionOwnerForActivity(IBinder activityToken) {
+        enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
+            if (r == null) {
+                throw new IllegalArgumentException("Activity does not exist; token="
+                        + activityToken);
+            }
+            return r.getUriPermissionsLocked().getExternalToken();
+        }
+    }
+
+    @Override
+    public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
+            Rect tempDockedTaskInsetBounds,
+            Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds,
+                        tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds,
+                        PRESERVE_WINDOWS);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void setSplitScreenResizing(boolean resizing) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setSplitScreenResizing()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.setSplitScreenResizing(resizing);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Check that we have the features required for VR-related API calls, and throw an exception if
+     * not.
+     */
+    void enforceSystemHasVrFeature() {
+        if (!mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
+            throw new UnsupportedOperationException("VR mode not supported on this device!");
+        }
+    }
+
+    @Override
+    public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
+        enforceSystemHasVrFeature();
+
+        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+
+        ActivityRecord r;
+        synchronized (mGlobalLock) {
+            r = ActivityRecord.isInStackLocked(token);
+        }
+
+        if (r == null) {
+            throw new IllegalArgumentException();
+        }
+
+        int err;
+        if ((err = vrService.hasVrPackage(packageName, r.userId)) !=
+                VrManagerInternal.NO_ERROR) {
+            return err;
+        }
+
+        // Clear the binder calling uid since this path may call moveToTask().
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                r.requestedVrComponent = (enabled) ? packageName : null;
+
+                // Update associated state if this activity is currently focused
+                if (r.isResumedActivityOnDisplay()) {
+                    applyUpdateVrModeLocked(r);
+                }
+                return 0;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
+    public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
+        Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
+        synchronized (mGlobalLock) {
+            ActivityRecord activity = getTopDisplayFocusedStack().getTopActivity();
+            if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
+                throw new SecurityException("Only focused activity can call startVoiceInteraction");
+            }
+            if (mRunningVoice != null || activity.getTask().voiceSession != null
+                    || activity.voiceSession != null) {
+                Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction");
+                return;
+            }
+            if (activity.pendingVoiceInteractionStart) {
+                Slog.w(TAG, "Pending start of voice interaction already.");
+                return;
+            }
+            activity.pendingVoiceInteractionStart = true;
+        }
+        LocalServices.getService(VoiceInteractionManagerInternal.class)
+                .startLocalVoiceInteraction(callingActivity, options);
+    }
+
+    @Override
+    public void stopLocalVoiceInteraction(IBinder callingActivity) {
+        LocalServices.getService(VoiceInteractionManagerInternal.class)
+                .stopLocalVoiceInteraction(callingActivity);
+    }
+
+    @Override
+    public boolean supportsLocalVoiceInteraction() {
+        return LocalServices.getService(VoiceInteractionManagerInternal.class)
+                .supportsLocalVoiceInteraction();
+    }
+
+    /** Notifies all listeners when the pinned stack animation starts. */
+    @Override
+    public void notifyPinnedStackAnimationStarted() {
+        mTaskChangeNotificationController.notifyPinnedStackAnimationStarted();
+    }
+
+    /** Notifies all listeners when the pinned stack animation ends. */
+    @Override
+    public void notifyPinnedStackAnimationEnded() {
+        mTaskChangeNotificationController.notifyPinnedStackAnimationEnded();
+    }
+
+    @Override
+    public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.resizePinnedStackLocked(pinnedBounds, tempPinnedTaskBounds);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public boolean updateDisplayOverrideConfiguration(Configuration values, int displayId) {
+        mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateDisplayOverrideConfiguration()");
+
+        synchronized (mGlobalLock) {
+            // Check if display is initialized in AM.
+            if (!mStackSupervisor.isDisplayAdded(displayId)) {
+                // Call might come when display is not yet added or has already been removed.
+                if (DEBUG_CONFIGURATION) {
+                    Slog.w(TAG, "Trying to update display configuration for non-existing displayId="
+                            + displayId);
+                }
+                return false;
+            }
+
+            if (values == null && mWindowManager != null) {
+                // sentinel: fetch the current configuration from the window manager
+                values = mWindowManager.computeNewConfiguration(displayId);
+            }
+
+            if (mWindowManager != null) {
+                // Update OOM levels based on display size.
+                mAm.mProcessList.applyDisplaySize(mWindowManager);
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                if (values != null) {
+                    Settings.System.clearConfiguration(values);
+                }
+                updateDisplayOverrideConfigurationLocked(values, null /* starting */,
+                        false /* deferResume */, displayId, mTmpUpdateConfigurationResult);
+                return mTmpUpdateConfigurationResult.changes != 0;
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public boolean updateConfiguration(Configuration values) {
+        mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
+
+        synchronized (mGlobalLock) {
+            if (values == null && mWindowManager != null) {
+                // sentinel: fetch the current configuration from the window manager
+                values = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
+            }
+
+            if (mWindowManager != null) {
+                // Update OOM levels based on display size.
+                mAm.mProcessList.applyDisplaySize(mWindowManager);
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                if (values != null) {
+                    Settings.System.clearConfiguration(values);
+                }
+                updateConfigurationLocked(values, null, false, false /* persistent */,
+                        UserHandle.USER_NULL, false /* deferResume */,
+                        mTmpUpdateConfigurationResult);
+                return mTmpUpdateConfigurationResult.changes != 0;
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
+            CharSequence message) {
+        if (message != null) {
+            mAmInternal.enforceCallingPermission(
+                    Manifest.permission.SHOW_KEYGUARD_MESSAGE, "dismissKeyguard()");
+        }
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mKeyguardController.dismissKeyguard(token, callback, message);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
+    public void cancelTaskWindowTransition(int taskId) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "cancelTaskWindowTransition()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_ONLY);
+                if (task == null) {
+                    Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found");
+                    return;
+                }
+                task.cancelWindowTransition();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
+        enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            final TaskRecord task;
+            synchronized (mGlobalLock) {
+                task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+                if (task == null) {
+                    Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
+                    return null;
+                }
+            }
+            // Don't call this while holding the lock as this operation might hit the disk.
+            return task.getSnapshot(reducedResolution);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void setDisablePreviewScreenshots(IBinder token, boolean disable) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                Slog.w(TAG, "setDisablePreviewScreenshots: Unable to find activity for token="
+                        + token);
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setDisablePreviewScreenshots(disable);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    /** Return the user id of the last resumed activity. */
+    @Override
+    public @UserIdInt
+    int getLastResumedActivityUserId() {
+        mAmInternal.enforceCallingPermission(
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL, "getLastResumedActivityUserId()");
+        synchronized (mGlobalLock) {
+            if (mLastResumedActivity == null) {
+                return getCurrentUserId();
+            }
+            return mLastResumedActivity.userId;
+        }
+    }
+
+    @Override
+    public void updateLockTaskFeatures(int userId, int flags) {
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != 0 && callingUid != SYSTEM_UID) {
+            mAmInternal.enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
+                    "updateLockTaskFeatures()");
+        }
+        synchronized (mGlobalLock) {
+            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowing features " + userId + ":0x" +
+                    Integer.toHexString(flags));
+            getLockTaskController().updateLockTaskFeatures(userId, flags);
+        }
+    }
+
+    @Override
+    public void setShowWhenLocked(IBinder token, boolean showWhenLocked) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setShowWhenLocked(showWhenLocked);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void setTurnScreenOn(IBinder token, boolean turnScreenOn) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setTurnScreenOn(turnScreenOn);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
+        mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+                "registerRemoteAnimations");
+        definition.setCallingPid(Binder.getCallingPid());
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.registerRemoteAnimations(definition);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void registerRemoteAnimationForNextActivityStart(String packageName,
+            RemoteAnimationAdapter adapter) {
+        mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+                "registerRemoteAnimationForNextActivityStart");
+        adapter.setCallingPid(Binder.getCallingPid());
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                getActivityStartController().registerRemoteAnimationForNextActivityStart(
+                        packageName, adapter);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
+    @Override
+    public void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                mAppWarnings.alwaysShowUnsupportedCompileSdkWarning(activity);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void setVrThread(int tid) {
+        enforceSystemHasVrFeature();
+        synchronized (mGlobalLock) {
+            synchronized (mAm.mPidsSelfLocked) {
+                final int pid = Binder.getCallingPid();
+                final ProcessRecord proc = mAm.mPidsSelfLocked.get(pid);
+                mVrController.setVrThreadLocked(tid, pid, proc.getWindowProcessController());
+            }
+        }
+    }
+
+    @Override
+    public void setPersistentVrThread(int tid) {
+        if (checkCallingPermission(Manifest.permission.RESTRICTED_VR_ACCESS)
+                != PERMISSION_GRANTED) {
+            final String msg = "Permission Denial: setPersistentVrThread() from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid()
+                    + " requires " + Manifest.permission.RESTRICTED_VR_ACCESS;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+        enforceSystemHasVrFeature();
+        synchronized (mGlobalLock) {
+            synchronized (mAm.mPidsSelfLocked) {
+                final int pid = Binder.getCallingPid();
+                final ProcessRecord proc = mAm.mPidsSelfLocked.get(pid);
+                mVrController.setPersistentVrThreadLocked(tid, pid, proc);
+            }
+        }
+    }
+
+    @Override
+    public void stopAppSwitches() {
+        enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "stopAppSwitches");
+        synchronized (mGlobalLock) {
+            mAppSwitchesAllowedTime = SystemClock.uptimeMillis() + APP_SWITCH_DELAY_TIME;
+            mDidAppSwitch = false;
+            getActivityStartController().schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME);
+        }
+    }
+
+    @Override
+    public void resumeAppSwitches() {
+        enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
+        synchronized (mGlobalLock) {
+            // Note that we don't execute any pending app switches... we will
+            // let those wait until either the timeout, or the next start
+            // activity request.
+            mAppSwitchesAllowedTime = 0;
+        }
+    }
+
+    void onStartActivitySetDidAppSwitch() {
+        if (mDidAppSwitch) {
+            // This is the second allowed switch since we stopped switches, so now just generally
+            // allow switches. Use case:
+            // - user presses home (switches disabled, switch to home, mDidAppSwitch now true);
+            // - user taps a home icon (coming from home so allowed, we hit here and now allow
+            // anyone to switch again).
+            mAppSwitchesAllowedTime = 0;
+        } else {
+            mDidAppSwitch = true;
+        }
+    }
+
+    /** @return whether the system should disable UI modes incompatible with VR mode. */
+    boolean shouldDisableNonVrUiLocked() {
+        return mVrController.shouldDisableNonVrUiLocked();
+    }
+
+    void applyUpdateVrModeLocked(ActivityRecord r) {
+        // VR apps are expected to run in a main display. If an app is turning on VR for
+        // itself, but lives in a dynamic stack, then make sure that it is moved to the main
+        // fullscreen stack before enabling VR Mode.
+        // TODO: The goal of this code is to keep the VR app on the main display. When the
+        // stack implementation changes in the future, keep in mind that the use of the fullscreen
+        // stack is a means to move the activity to the main display and a moveActivityToDisplay()
+        // option would be a better choice here.
+        if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
+            Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
+                    + " to main stack for VR");
+            final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+                    WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
+            moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */);
+        }
+        mH.post(() -> {
+            if (!mVrController.onVrModeChanged(r)) {
+                return;
+            }
+            synchronized (mGlobalLock) {
+                final boolean disableNonVrUi = mVrController.shouldDisableNonVrUiLocked();
+                mWindowManager.disableNonVrUi(disableNonVrUi);
+                if (disableNonVrUi) {
+                    // If we are in a VR mode where Picture-in-Picture mode is unsupported,
+                    // then remove the pinned stack.
+                    mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+                }
+            }
+        });
+    }
+
+    ActivityStack getTopDisplayFocusedStack() {
+        return mStackSupervisor.getTopDisplayFocusedStack();
+    }
+
+    /** Pokes the task persister. */
+    void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
+        mRecentTasks.notifyTaskPersisterLocked(task, flush);
+    }
+
+    void onTopProcChangedLocked(WindowProcessController proc) {
+        mVrController.onTopProcChangedLocked(proc);
+    }
+
+    boolean isKeyguardLocked() {
+        return mKeyguardController.isKeyguardLocked();
+    }
+
+    boolean isNextTransitionForward() {
+        int transit = mWindowManager.getPendingAppTransition();
+        return transit == TRANSIT_ACTIVITY_OPEN
+                || transit == TRANSIT_TASK_OPEN
+                || transit == TRANSIT_TASK_TO_FRONT;
+    }
+
+    void dumpSleepStates(PrintWriter pw, boolean testPssMode) {
+        synchronized (mGlobalLock) {
+            pw.println("  mSleepTokens=" + mStackSupervisor.mSleepTokens);
+            if (mRunningVoice != null) {
+                pw.println("  mRunningVoice=" + mRunningVoice);
+                pw.println("  mVoiceWakeLock" + mVoiceWakeLock);
+            }
+            pw.println("  mSleeping=" + mSleeping);
+            pw.println("  mShuttingDown=" + mShuttingDown + " mTestPssMode=" + testPssMode);
+            pw.println("  mVrController=" + mVrController);
+        }
+    }
+
+    void writeSleepStateToProto(ProtoOutputStream proto) {
+        for (ActivityTaskManagerInternal.SleepToken st : mStackSupervisor.mSleepTokens) {
+            proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS,
+                    st.toString());
+        }
+
+        if (mRunningVoice != null) {
+            final long vrToken = proto.start(
+                    ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
+            proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION,
+                    mRunningVoice.toString());
+            mVoiceWakeLock.writeToProto(
+                    proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK);
+            proto.end(vrToken);
+        }
+
+        proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEPING, mSleeping);
+        proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SHUTTING_DOWN,
+                mShuttingDown);
+        mVrController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
+    }
+
+    int getCurrentUserId() {
+        return mAmInternal.getCurrentUserId();
+    }
+
+    private void enforceNotIsolatedCaller(String caller) {
+        if (UserHandle.isIsolated(Binder.getCallingUid())) {
+            throw new SecurityException("Isolated process not allowed to call " + caller);
+        }
+    }
+
+    public Configuration getConfiguration() {
+        Configuration ci;
+        synchronized(mGlobalLock) {
+            ci = new Configuration(getGlobalConfiguration());
+            ci.userSetLocale = false;
+        }
+        return ci;
+    }
+
+    /**
+     * Current global configuration information. Contains general settings for the entire system,
+     * also corresponds to the merged configuration of the default display.
+     */
+    Configuration getGlobalConfiguration() {
+        return mStackSupervisor.getConfiguration();
+    }
+
+    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+            boolean initLocale) {
+        return updateConfigurationLocked(values, starting, initLocale, false /* deferResume */);
+    }
+
+    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+            boolean initLocale, boolean deferResume) {
+        // pass UserHandle.USER_NULL as userId because we don't persist configuration for any user
+        return updateConfigurationLocked(values, starting, initLocale, false /* persistent */,
+                UserHandle.USER_NULL, deferResume);
+    }
+
+    void updatePersistentConfiguration(Configuration values, @UserIdInt int userId) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                updateConfigurationLocked(values, null, false, true, userId,
+                        false /* deferResume */);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    void updateUserConfiguration() {
+        synchronized (mGlobalLock) {
+            final Configuration configuration = new Configuration(getGlobalConfiguration());
+            final int currentUserId = mAmInternal.getCurrentUserId();
+            Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration,
+                    currentUserId, Settings.System.canWrite(mContext));
+            updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */,
+                    false /* persistent */, currentUserId, false /* deferResume */);
+        }
+    }
+
+    private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+            boolean initLocale, boolean persistent, int userId, boolean deferResume) {
+        return updateConfigurationLocked(values, starting, initLocale, persistent, userId,
+                deferResume, null /* result */);
+    }
+
+    /**
+     * Do either or both things: (1) change the current configuration, and (2)
+     * make sure the given activity is running with the (now) current
+     * configuration.  Returns true if the activity has been left running, or
+     * false if <var>starting</var> is being destroyed to match the new
+     * configuration.
+     *
+     * @param userId is only used when persistent parameter is set to true to persist configuration
+     *               for that particular user
+     */
+    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+            boolean initLocale, boolean persistent, int userId, boolean deferResume,
+            ActivityTaskManagerService.UpdateConfigurationResult result) {
+        int changes = 0;
+        boolean kept = true;
+
+        if (mWindowManager != null) {
+            mWindowManager.deferSurfaceLayout();
+        }
+        try {
+            if (values != null) {
+                changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
+                        deferResume);
+            }
+
+            kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+        } finally {
+            if (mWindowManager != null) {
+                mWindowManager.continueSurfaceLayout();
+            }
+        }
+
+        if (result != null) {
+            result.changes = changes;
+            result.activityRelaunched = !kept;
+        }
+        return kept;
+    }
+
+    /**
+     * Returns true if this configuration change is interesting enough to send an
+     * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
+     */
+    private static boolean isSplitConfigurationChange(int configDiff) {
+        return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
+    }
+
+    /** Update default (global) configuration and notify listeners about changes. */
+    private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
+            boolean persistent, int userId, boolean deferResume) {
+        mTempConfig.setTo(getGlobalConfiguration());
+        final int changes = mTempConfig.updateFrom(values);
+        if (changes == 0) {
+            // Since calling to Activity.setRequestedOrientation leads to freezing the window with
+            // setting WindowManagerService.mWaitingForConfig to true, it is important that we call
+            // performDisplayOverrideConfigUpdate in order to send the new display configuration
+            // (even if there are no actual changes) to unfreeze the window.
+            performDisplayOverrideConfigUpdate(values, deferResume, DEFAULT_DISPLAY);
+            return 0;
+        }
+
+        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION,
+                "Updating global configuration to: " + values);
+
+        EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
+        StatsLog.write(StatsLog.RESOURCE_CONFIGURATION_CHANGED,
+                values.colorMode,
+                values.densityDpi,
+                values.fontScale,
+                values.hardKeyboardHidden,
+                values.keyboard,
+                values.keyboardHidden,
+                values.mcc,
+                values.mnc,
+                values.navigation,
+                values.navigationHidden,
+                values.orientation,
+                values.screenHeightDp,
+                values.screenLayout,
+                values.screenWidthDp,
+                values.smallestScreenWidthDp,
+                values.touchscreen,
+                values.uiMode);
+
+
+        if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
+            final LocaleList locales = values.getLocales();
+            int bestLocaleIndex = 0;
+            if (locales.size() > 1) {
+                if (mSupportedSystemLocales == null) {
+                    mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
+                }
+                bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));
+            }
+            SystemProperties.set("persist.sys.locale",
+                    locales.get(bestLocaleIndex).toLanguageTag());
+            LocaleList.setDefault(locales, bestLocaleIndex);
+            mAm.mHandler.sendMessage(mAm.mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
+                    locales.get(bestLocaleIndex)));
+        }
+
+        mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
+        mTempConfig.seq = mConfigurationSeq;
+
+        // Update stored global config and notify everyone about the change.
+        mStackSupervisor.onConfigurationChanged(mTempConfig);
+
+        Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
+        // TODO(multi-display): Update UsageEvents#Event to include displayId.
+        mAm.mUsageStatsService.reportConfigurationChange(
+                mTempConfig, mAmInternal.getCurrentUserId());
+
+        // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
+        updateShouldShowDialogsLocked(mTempConfig);
+
+        AttributeCache ac = AttributeCache.instance();
+        if (ac != null) {
+            ac.updateConfiguration(mTempConfig);
+        }
+
+        // Make sure all resources in our process are updated right now, so that anyone who is going
+        // to retrieve resource values after we return will be sure to get the new ones. This is
+        // especially important during boot, where the first config change needs to guarantee all
+        // resources have that config before following boot code is executed.
+        mAm.mSystemThread.applyConfigurationToResources(mTempConfig);
+
+        // We need another copy of global config because we're scheduling some calls instead of
+        // running them in place. We need to be sure that object we send will be handled unchanged.
+        final Configuration configCopy = new Configuration(mTempConfig);
+        if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
+            Message msg = mAm.mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
+            msg.obj = configCopy;
+            msg.arg1 = userId;
+            mAm.mHandler.sendMessage(msg);
+        }
+
+        for (int i = mAm.mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord app = mAm.mLruProcesses.get(i);
+            try {
+                if (app.thread != null) {
+                    if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
+                            + app.processName + " new config " + configCopy);
+                    getLifecycleManager().scheduleTransaction(app.thread,
+                            ConfigurationChangeItem.obtain(configCopy));
+                }
+            } catch (Exception e) {
+                Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
+            }
+        }
+
+        Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING
+                | Intent.FLAG_RECEIVER_FOREGROUND
+                | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+        mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+                UserHandle.USER_ALL);
+        if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
+            intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
+            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+                    | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+            if (initLocale || !mAm.mProcessesReady) {
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+            }
+            mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                    OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+                    UserHandle.USER_ALL);
+        }
+
+        // Send a broadcast to PackageInstallers if the configuration change is interesting
+        // for the purposes of installing additional splits.
+        if (!initLocale && isSplitConfigurationChange(changes)) {
+            intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
+            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+
+            // Typically only app stores will have this permission.
+            String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES };
+            mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions,
+                    OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
+        }
+
+        // Override configuration of the default display duplicates global config, so we need to
+        // update it also. This will also notify WindowManager about changes.
+        performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
+                DEFAULT_DISPLAY);
+
+        return changes;
+    }
+
+    boolean updateDisplayOverrideConfigurationLocked(Configuration values, ActivityRecord starting,
+            boolean deferResume, int displayId) {
+        return updateDisplayOverrideConfigurationLocked(values, starting, deferResume /* deferResume */,
+                displayId, null /* result */);
+    }
+
+    /**
+     * Updates override configuration specific for the selected display. If no config is provided,
+     * new one will be computed in WM based on current display info.
+     */
+    boolean updateDisplayOverrideConfigurationLocked(Configuration values,
+            ActivityRecord starting, boolean deferResume, int displayId,
+            ActivityTaskManagerService.UpdateConfigurationResult result) {
+        int changes = 0;
+        boolean kept = true;
+
+        if (mWindowManager != null) {
+            mWindowManager.deferSurfaceLayout();
+        }
+        try {
+            if (values != null) {
+                if (displayId == DEFAULT_DISPLAY) {
+                    // Override configuration of the default display duplicates global config, so
+                    // we're calling global config update instead for default display. It will also
+                    // apply the correct override config.
+                    changes = updateGlobalConfigurationLocked(values, false /* initLocale */,
+                            false /* persistent */, UserHandle.USER_NULL /* userId */, deferResume);
+                } else {
+                    changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId);
+                }
+            }
+
+            kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+        } finally {
+            if (mWindowManager != null) {
+                mWindowManager.continueSurfaceLayout();
+            }
+        }
+
+        if (result != null) {
+            result.changes = changes;
+            result.activityRelaunched = !kept;
+        }
+        return kept;
+    }
+
+    private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,
+            int displayId) {
+        mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));
+        final int changes = mTempConfig.updateFrom(values);
+        if (changes != 0) {
+            Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
+                    + mTempConfig + " for displayId=" + displayId);
+            mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId);
+
+            final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
+            if (isDensityChange && displayId == DEFAULT_DISPLAY) {
+                mAppWarnings.onDensityChanged();
+
+                mAm.killAllBackgroundProcessesExcept(N,
+                        ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+            }
+        }
+
+        // Update the configuration with WM first and check if any of the stacks need to be resized
+        // due to the configuration change. If so, resize the stacks now and do any relaunches if
+        // necessary. This way we don't need to relaunch again afterwards in
+        // ensureActivityConfiguration().
+        if (mWindowManager != null) {
+            final int[] resizedStacks =
+                    mWindowManager.setNewDisplayOverrideConfiguration(mTempConfig, displayId);
+            if (resizedStacks != null) {
+                for (int stackId : resizedStacks) {
+                    resizeStackWithBoundsFromWindowManager(stackId, deferResume);
+                }
+            }
+        }
+
+        return changes;
+    }
+
+    private void updateEventDispatchingLocked(boolean booted) {
+        mWindowManager.setEventDispatching(booted && !mShuttingDown);
+    }
+
+    void enableScreenAfterBoot(boolean booted) {
+        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
+                SystemClock.uptimeMillis());
+        mWindowManager.enableScreenAfterBoot();
+
+        synchronized (mGlobalLock) {
+            updateEventDispatchingLocked(booted);
+        }
+    }
+
+    boolean canShowErrorDialogs() {
+        return mShowDialogs && !mSleeping && !mShuttingDown
+                && !mKeyguardController.isKeyguardOrAodShowing(DEFAULT_DISPLAY)
+                && !hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
+                mAmInternal.getCurrentUserId())
+                && !(UserManager.isDeviceInDemoMode(mContext)
+                && mAmInternal.getCurrentUser().isDemo());
+    }
+
+    static long getInputDispatchingTimeoutLocked(ActivityRecord r) {
+        if (r == null || !r.hasProcess()) {
+            return KEY_DISPATCHING_TIMEOUT_MS;
+        }
+        return getInputDispatchingTimeoutLocked(r.app);
+    }
+
+    private static long getInputDispatchingTimeoutLocked(WindowProcessController r) {
+        if (r != null && (r.isInstrumenting() || r.isUsingWrapper())) {
+            return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS;
+        }
+        return KEY_DISPATCHING_TIMEOUT_MS;
+    }
+
+    long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
+        if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires permission " + FILTER_EVENTS);
+        }
+        WindowProcessController proc;
+        long timeout;
+        synchronized (mGlobalLock) {
+            proc = mPidMap.get(pid);
+            timeout = getInputDispatchingTimeoutLocked(proc);
+        }
+
+        if (inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
+            return -1;
+        }
+
+        return timeout;
+    }
+
+    /**
+     * Handle input dispatching timeouts.
+     * Returns whether input dispatching should be aborted or not.
+     */
+    boolean inputDispatchingTimedOut(final WindowProcessController proc,
+            final ActivityRecord activity, final ActivityRecord parent,
+            final boolean aboveSystem, String reason) {
+        if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires permission " + FILTER_EVENTS);
+        }
+
+        final String annotation;
+        if (reason == null) {
+            annotation = "Input dispatching timed out";
+        } else {
+            annotation = "Input dispatching timed out (" + reason + ")";
+        }
+
+        if (proc != null) {
+            synchronized (mGlobalLock) {
+                if (proc.isDebugging()) {
+                    return false;
+                }
+
+                if (proc.isInstrumenting()) {
+                    Bundle info = new Bundle();
+                    info.putString("shortMsg", "keyDispatchingTimedOut");
+                    info.putString("longMsg", annotation);
+                    mAm.finishInstrumentationLocked(
+                            (ProcessRecord) proc.mOwner, Activity.RESULT_CANCELED, info);
+                    return true;
+                }
+            }
+            mH.post(() -> {
+                mAm.mAppErrors.appNotResponding(
+                        (ProcessRecord) proc.mOwner, activity, parent, aboveSystem, annotation);
+            });
+        }
+
+        return true;
+    }
+
+    /**
+     * Decide based on the configuration whether we should show the ANR,
+     * crash, etc dialogs.  The idea is that if there is no affordance to
+     * press the on-screen buttons, or the user experience would be more
+     * greatly impacted than the crash itself, we shouldn't show the dialog.
+     *
+     * A thought: SystemUI might also want to get told about this, the Power
+     * dialog / global actions also might want different behaviors.
+     */
+    private void updateShouldShowDialogsLocked(Configuration config) {
+        final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
+                && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
+                && config.navigation == Configuration.NAVIGATION_NONAV);
+        int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
+        final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR
+                && !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER)
+                && modeType != Configuration.UI_MODE_TYPE_TELEVISION
+                && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
+        final boolean hideDialogsSet = Settings.Global.getInt(mContext.getContentResolver(),
+                HIDE_ERROR_DIALOGS, 0) != 0;
+        mShowDialogs = inputMethodExists && uiModeSupportsDialogs && !hideDialogsSet;
+    }
+
+    private void updateFontScaleIfNeeded(@UserIdInt int userId) {
+        final float scaleFactor = Settings.System.getFloatForUser(mContext.getContentResolver(),
+                FONT_SCALE, 1.0f, userId);
+
+        synchronized (this) {
+            if (getGlobalConfiguration().fontScale == scaleFactor) {
+                return;
+            }
+
+            final Configuration configuration
+                    = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
+            configuration.fontScale = scaleFactor;
+            updatePersistentConfiguration(configuration, userId);
+        }
+    }
+
+    // Actually is sleeping or shutting down or whatever else in the future
+    // is an inactive state.
+    boolean isSleepingOrShuttingDownLocked() {
+        return isSleepingLocked() || mShuttingDown;
+    }
+
+    boolean isSleepingLocked() {
+        return mSleeping;
+    }
+
+    /**
+     * Update AMS states when an activity is resumed. This should only be called by
+     * {@link ActivityStack#onActivityStateChanged(
+     * ActivityRecord, ActivityStack.ActivityState, String)} when an activity is resumed.
+     */
+    void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
+        final TaskRecord task = r.getTask();
+        if (task.isActivityTypeStandard()) {
+            if (mCurAppTimeTracker != r.appTimeTracker) {
+                // We are switching app tracking.  Complete the current one.
+                if (mCurAppTimeTracker != null) {
+                    mCurAppTimeTracker.stop();
+                    mH.obtainMessage(
+                            REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget();
+                    mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
+                    mCurAppTimeTracker = null;
+                }
+                if (r.appTimeTracker != null) {
+                    mCurAppTimeTracker = r.appTimeTracker;
+                    startTimeTrackingFocusedActivityLocked();
+                }
+            } else {
+                startTimeTrackingFocusedActivityLocked();
+            }
+        } else {
+            r.appTimeTracker = null;
+        }
+        // TODO: VI Maybe r.task.voiceInteractor || r.voiceInteractor != null
+        // TODO: Probably not, because we don't want to resume voice on switching
+        // back to this activity
+        if (task.voiceInteractor != null) {
+            startRunningVoiceLocked(task.voiceSession, r.info.applicationInfo.uid);
+        } else {
+            finishRunningVoiceLocked();
+
+            if (mLastResumedActivity != null) {
+                final IVoiceInteractionSession session;
+
+                final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask();
+                if (lastResumedActivityTask != null
+                        && lastResumedActivityTask.voiceSession != null) {
+                    session = lastResumedActivityTask.voiceSession;
+                } else {
+                    session = mLastResumedActivity.voiceSession;
+                }
+
+                if (session != null) {
+                    // We had been in a voice interaction session, but now focused has
+                    // move to something different.  Just finish the session, we can't
+                    // return to it and retain the proper state and synchronization with
+                    // the voice interaction service.
+                    finishVoiceTask(session);
+                }
+            }
+        }
+
+        if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) {
+            mAmInternal.sendForegroundProfileChanged(r.userId);
+        }
+        updateResumedAppTrace(r);
+        mLastResumedActivity = r;
+
+        // TODO(b/111361570): Support multiple focused apps in WM
+        mWindowManager.setFocusedApp(r.appToken, true);
+
+        applyUpdateLockStateLocked(r);
+        applyUpdateVrModeLocked(r);
+
+        EventLogTags.writeAmSetResumedActivity(
+                r == null ? -1 : r.userId,
+                r == null ? "NULL" : r.shortComponentName,
+                reason);
+    }
+
+    ActivityTaskManagerInternal.SleepToken acquireSleepToken(String tag, int displayId) {
+        synchronized (mGlobalLock) {
+            final ActivityTaskManagerInternal.SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId);
+            updateSleepIfNeededLocked();
+            return token;
+        }
+    }
+
+    void updateSleepIfNeededLocked() {
+        final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
+        final boolean wasSleeping = mSleeping;
+        boolean updateOomAdj = false;
+
+        if (!shouldSleep) {
+            // If wasSleeping is true, we need to wake up activity manager state from when
+            // we started sleeping. In either case, we need to apply the sleep tokens, which
+            // will wake up stacks or put them to sleep as appropriate.
+            if (wasSleeping) {
+                mSleeping = false;
+                StatsLog.write(StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
+                        StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__AWAKE);
+                startTimeTrackingFocusedActivityLocked();
+                mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
+                mStackSupervisor.comeOutOfSleepIfNeededLocked();
+            }
+            mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
+            if (wasSleeping) {
+                updateOomAdj = true;
+            }
+        } else if (!mSleeping && shouldSleep) {
+            mSleeping = true;
+            StatsLog.write(StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
+                    StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__ASLEEP);
+            if (mCurAppTimeTracker != null) {
+                mCurAppTimeTracker.stop();
+            }
+            mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
+            mStackSupervisor.goingToSleepLocked();
+            updateResumedAppTrace(null /* resumed */);
+            updateOomAdj = true;
+        }
+        if (updateOomAdj) {
+            mH.post(mAmInternal::updateOomAdj);
+        }
+    }
+
+    void updateOomAdj() {
+        mH.post(mAmInternal::updateOomAdj);
+    }
+
+    // TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities
+    private void startTimeTrackingFocusedActivityLocked() {
+        final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity();
+        if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) {
+            mCurAppTimeTracker.start(resumedActivity.packageName);
+        }
+    }
+
+    private void updateResumedAppTrace(@Nullable ActivityRecord resumed) {
+        if (mTracedResumedActivity != null) {
+            Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER,
+                    constructResumedTraceName(mTracedResumedActivity.packageName), 0);
+        }
+        if (resumed != null) {
+            Trace.asyncTraceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+                    constructResumedTraceName(resumed.packageName), 0);
+        }
+        mTracedResumedActivity = resumed;
+    }
+
+    private String constructResumedTraceName(String packageName) {
+        return "focused app: " + packageName;
+    }
+
+    /** Helper method that requests bounds from WM and applies them to stack. */
+    private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
+        final Rect newStackBounds = new Rect();
+        final ActivityStack stack = mStackSupervisor.getStack(stackId);
+
+        // TODO(b/71548119): Revert CL introducing below once cause of mismatch is found.
+        if (stack == null) {
+            final StringWriter writer = new StringWriter();
+            final PrintWriter printWriter = new PrintWriter(writer);
+            mStackSupervisor.dumpDisplays(printWriter);
+            printWriter.flush();
+
+            Log.wtf(TAG, "stack not found:" + stackId + " displays:" + writer);
+        }
+
+        stack.getBoundsForNewConfiguration(newStackBounds);
+        mStackSupervisor.resizeStackLocked(
+                stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
+                null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+                false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
+    }
+
+    /** Applies latest configuration and/or visibility updates if needed. */
+    private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
+        boolean kept = true;
+        final ActivityStack mainStack = mStackSupervisor.getTopDisplayFocusedStack();
+        // mainStack is null during startup.
+        if (mainStack != null) {
+            if (changes != 0 && starting == null) {
+                // If the configuration changed, and the caller is not already
+                // in the process of starting an activity, then find the top
+                // activity to check if its configuration needs to change.
+                starting = mainStack.topRunningActivityLocked();
+            }
+
+            if (starting != null) {
+                kept = starting.ensureActivityConfiguration(changes,
+                        false /* preserveWindow */);
+                // And we need to make sure at this point that all other activities
+                // are made visible with the correct configuration.
+                mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
+                        !PRESERVE_WINDOWS);
+            }
+        }
+
+        return kept;
+    }
+
+    void scheduleAppGcsLocked() {
+        mH.post(() -> mAmInternal.scheduleAppGcs());
+    }
+
+    /**
+     * Returns the PackageManager. Used by classes hosted by {@link ActivityTaskManagerService}. The
+     * PackageManager could be unavailable at construction time and therefore needs to be accessed
+     * on demand.
+     */
+    IPackageManager getPackageManager() {
+        return AppGlobals.getPackageManager();
+    }
+
+    PackageManagerInternal getPackageManagerInternalLocked() {
+        if (mPmInternal == null) {
+            mPmInternal = LocalServices.getService(PackageManagerInternal.class);
+        }
+        return mPmInternal;
+    }
+
+    AppWarnings getAppWarningsLocked() {
+        return mAppWarnings;
+    }
+
+    void logAppTooSlow(WindowProcessController app, long startTime, String msg) {
+        if (true || Build.IS_USER) {
+            return;
+        }
+
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        StrictMode.allowThreadDiskWrites();
+        try {
+            File tracesDir = new File("/data/anr");
+            File tracesFile = null;
+            try {
+                tracesFile = File.createTempFile("app_slow", null, tracesDir);
+
+                StringBuilder sb = new StringBuilder();
+                Time tobj = new Time();
+                tobj.set(System.currentTimeMillis());
+                sb.append(tobj.format("%Y-%m-%d %H:%M:%S"));
+                sb.append(": ");
+                TimeUtils.formatDuration(SystemClock.uptimeMillis()-startTime, sb);
+                sb.append(" since ");
+                sb.append(msg);
+                FileOutputStream fos = new FileOutputStream(tracesFile);
+                fos.write(sb.toString().getBytes());
+                if (app == null) {
+                    fos.write("\n*** No application process!".getBytes());
+                }
+                fos.close();
+                FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1); // -rw-rw-rw-
+            } catch (IOException e) {
+                Slog.w(TAG, "Unable to prepare slow app traces file: " + tracesFile, e);
+                return;
+            }
+
+            if (app != null && app.getPid() > 0) {
+                ArrayList<Integer> firstPids = new ArrayList<Integer>();
+                firstPids.add(app.getPid());
+                dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null);
+            }
+
+            File lastTracesFile = null;
+            File curTracesFile = null;
+            for (int i=9; i>=0; i--) {
+                String name = String.format(Locale.US, "slow%02d.txt", i);
+                curTracesFile = new File(tracesDir, name);
+                if (curTracesFile.exists()) {
+                    if (lastTracesFile != null) {
+                        curTracesFile.renameTo(lastTracesFile);
+                    } else {
+                        curTracesFile.delete();
+                    }
+                }
+                lastTracesFile = curTracesFile;
+            }
+            tracesFile.renameTo(curTracesFile);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+    }
+
+    final class H extends Handler {
+        static final int REPORT_TIME_TRACKER_MSG = 1;
+
+        public H(Looper looper) {
+            super(looper, null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case REPORT_TIME_TRACKER_MSG: {
+                    AppTimeTracker tracker = (AppTimeTracker) msg.obj;
+                    tracker.deliverResult(mContext);
+                } break;
+            }
+        }
+    }
+
+    final class UiHandler extends Handler {
+        static final int DISMISS_DIALOG_UI_MSG = 1;
+
+        public UiHandler() {
+            super(com.android.server.UiThread.get().getLooper(), null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case DISMISS_DIALOG_UI_MSG: {
+                    final Dialog d = (Dialog) msg.obj;
+                    d.dismiss();
+                    break;
+                }
+            }
+        }
+    }
+
+    final class LocalService extends ActivityTaskManagerInternal {
+        @Override
+        public SleepToken acquireSleepToken(String tag, int displayId) {
+            Preconditions.checkNotNull(tag);
+            return ActivityTaskManagerService.this.acquireSleepToken(tag, displayId);
+        }
+
+        @Override
+        public ComponentName getHomeActivityForUser(int userId) {
+            synchronized (mGlobalLock) {
+                ActivityRecord homeActivity = mStackSupervisor.getHomeActivityForUser(userId);
+                return homeActivity == null ? null : homeActivity.realActivity;
+            }
+        }
+
+        @Override
+        public void onLocalVoiceInteractionStarted(IBinder activity,
+                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
+            synchronized (mGlobalLock) {
+                onLocalVoiceInteractionStartedLocked(activity, voiceSession, voiceInteractor);
+            }
+        }
+
+        @Override
+        public void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
+                        reasons, timestamp);
+            }
+        }
+
+        @Override
+        public void notifyAppTransitionFinished() {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.notifyAppTransitionDone();
+            }
+        }
+
+        @Override
+        public void notifyAppTransitionCancelled() {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.notifyAppTransitionDone();
+            }
+        }
+
+        @Override
+        public List<IBinder> getTopVisibleActivities() {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.getTopVisibleActivities();
+            }
+        }
+
+        @Override
+        public void notifyDockedStackMinimizedChanged(boolean minimized) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.setDockedStackMinimized(minimized);
+            }
+        }
+
+        @Override
+        public int startActivitiesAsPackage(String packageName, int userId, Intent[] intents,
+                Bundle bOptions) {
+            Preconditions.checkNotNull(intents, "intents");
+            final String[] resolvedTypes = new String[intents.length];
+
+            // UID of the package on user userId.
+            // "= 0" is needed because otherwise catch(RemoteException) would make it look like
+            // packageUid may not be initialized.
+            int packageUid = 0;
+            final long ident = Binder.clearCallingIdentity();
+
+            try {
+                for (int i = 0; i < intents.length; i++) {
+                    resolvedTypes[i] =
+                            intents[i].resolveTypeIfNeeded(mContext.getContentResolver());
+                }
+
+                packageUid = AppGlobals.getPackageManager().getPackageUid(
+                        packageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
+            } catch (RemoteException e) {
+                // Shouldn't happen.
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+
+            synchronized (mGlobalLock) {
+                return getActivityStartController().startActivitiesInPackage(
+                        packageUid, packageName,
+                        intents, resolvedTypes, null /* resultTo */,
+                        SafeActivityOptions.fromBundle(bOptions), userId,
+                        false /* validateIncomingUser */, null /* originatingPendingIntent */);
+            }
+        }
+
+        @Override
+        public int startActivityAsUser(IApplicationThread caller, String callerPacakge,
+                Intent intent, Bundle options, int userId) {
+            return ActivityTaskManagerService.this.startActivityAsUser(
+                    caller, callerPacakge, intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options, userId,
+                    false /*validateIncomingUser*/);
+        }
+
+        @Override
+        public void notifyKeyguardFlagsChanged(@Nullable Runnable callback) {
+            synchronized (mGlobalLock) {
+
+                // We might change the visibilities here, so prepare an empty app transition which
+                // might be overridden later if we actually change visibilities.
+                final boolean wasTransitionSet =
+                        mWindowManager.getPendingAppTransition() != TRANSIT_NONE;
+                if (!wasTransitionSet) {
+                    mWindowManager.prepareAppTransition(TRANSIT_NONE,
+                            false /* alwaysKeepCurrent */);
+                }
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+
+                // If there was a transition set already we don't want to interfere with it as we
+                // might be starting it too early.
+                if (!wasTransitionSet) {
+                    mWindowManager.executeAppTransition();
+                }
+            }
+            if (callback != null) {
+                callback.run();
+            }
+        }
+
+        @Override
+        public void notifyKeyguardTrustedChanged() {
+            synchronized (mGlobalLock) {
+                if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
+                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                }
+            }
+        }
+
+        /**
+         * Called after virtual display Id is updated by
+         * {@link com.android.server.vr.Vr2dDisplay} with a specific
+         * {@param vrVr2dDisplayId}.
+         */
+        @Override
+        public void setVr2dDisplayId(int vr2dDisplayId) {
+            if (DEBUG_STACK) Slog.d(TAG, "setVr2dDisplayId called for: " + vr2dDisplayId);
+            synchronized (mGlobalLock) {
+                mVr2dDisplayId = vr2dDisplayId;
+            }
+        }
+
+        @Override
+        public void setFocusedActivity(IBinder token) {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+                if (r == null) {
+                    throw new IllegalArgumentException(
+                            "setFocusedActivity: No activity record matching token=" + token);
+                }
+                if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
+                        r, "setFocusedActivity")) {
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                }
+            }
+        }
+
+        @Override
+        public void registerScreenObserver(ScreenObserver observer) {
+            mScreenObservers.add(observer);
+        }
+
+        @Override
+        public boolean isCallerRecents(int callingUid) {
+            return getRecentTasks().isCallerRecents(callingUid);
+        }
+
+        @Override
+        public boolean isRecentsComponentHomeActivity(int userId) {
+            return getRecentTasks().isRecentsComponentHomeActivity(userId);
+        }
+
+        @Override
+        public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
+            ActivityTaskManagerService.this.cancelRecentsAnimation(restoreHomeStackPosition);
+        }
+
+        @Override
+        public void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
+            ActivityTaskManagerService.this.enforceCallerIsRecentsOrHasPermission(permission, func);
+        }
+
+        @Override
+        public void notifyActiveVoiceInteractionServiceChanged(ComponentName component) {
+            synchronized (mGlobalLock) {
+                mActiveVoiceInteractionServiceComponent = component;
+            }
+        }
+
+        @Override
+        public void setAllowAppSwitches(@NonNull String type, int uid, int userId) {
+            if (!mAmInternal.isUserRunning(userId, ActivityManager.FLAG_OR_STOPPED)) {
+                return;
+            }
+            synchronized (mGlobalLock) {
+                ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(userId);
+                if (types == null) {
+                    if (uid < 0) {
+                        return;
+                    }
+                    types = new ArrayMap<>();
+                    mAllowAppSwitchUids.put(userId, types);
+                }
+                if (uid < 0) {
+                    types.remove(type);
+                } else {
+                    types.put(type, uid);
+                }
+            }
+        }
+
+        @Override
+        public void onUserStopped(int userId) {
+            synchronized (mGlobalLock) {
+                getRecentTasks().unloadUserDataFromMemoryLocked(userId);
+                mAllowAppSwitchUids.remove(userId);
+            }
+        }
+
+        @Override
+        public boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
+            synchronized (mGlobalLock) {
+                return ActivityTaskManagerService.this.isGetTasksAllowed(
+                        caller, callingPid, callingUid);
+            }
+        }
+
+        @Override
+        public void onProcessAdded(WindowProcessController proc) {
+            synchronized (mGlobalLock) {
+                mProcessNames.put(proc.mName, proc.mUid, proc);
+            }
+        }
+
+        @Override
+        public void onProcessRemoved(String name, int uid) {
+            synchronized (mGlobalLock) {
+                mProcessNames.remove(name, uid);
+            }
+        }
+
+        @Override
+        public void onCleanUpApplicationRecord(WindowProcessController proc) {
+            synchronized (mGlobalLock) {
+                if (proc == mHomeProcess) {
+                    mHomeProcess = null;
+                }
+                if (proc == mPreviousProcess) {
+                    mPreviousProcess = null;
+                }
+            }
+        }
+
+        @Override
+        public int getTopProcessState() {
+            synchronized (mGlobalLock) {
+                return mTopProcessState;
+            }
+        }
+
+        @Override
+        public boolean isSleeping() {
+            synchronized (mGlobalLock) {
+                return isSleepingLocked();
+            }
+        }
+
+        @Override
+        public boolean isShuttingDown() {
+            synchronized (mGlobalLock) {
+                return mShuttingDown;
+            }
+        }
+
+        @Override
+        public boolean shuttingDown(boolean booted, int timeout) {
+            synchronized (mGlobalLock) {
+                mShuttingDown = true;
+                mStackSupervisor.prepareForShutdownLocked();
+                updateEventDispatchingLocked(booted);
+                return mStackSupervisor.shutdownLocked(timeout);
+            }
+        }
+
+        @Override
+        public void enableScreenAfterBoot(boolean booted) {
+            synchronized (mGlobalLock) {
+                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
+                        SystemClock.uptimeMillis());
+                mWindowManager.enableScreenAfterBoot();
+                updateEventDispatchingLocked(booted);
+            }
+        }
+
+        @Override
+        public boolean showStrictModeViolationDialog() {
+            synchronized (mGlobalLock) {
+                return mShowDialogs && !mSleeping && !mShuttingDown;
+            }
+        }
+
+        @Override
+        public void showSystemReadyErrorDialogsIfNeeded() {
+            synchronized (mGlobalLock) {
+                try {
+                    if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
+                        Slog.e(TAG, "UIDs on the system are inconsistent, you need to wipe your"
+                                + " data partition or your device will be unstable.");
+                        mUiHandler.post(() -> {
+                            if (mShowDialogs) {
+                                AlertDialog d = new BaseErrorDialog(mUiContext);
+                                d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+                                d.setCancelable(false);
+                                d.setTitle(mUiContext.getText(R.string.android_system_label));
+                                d.setMessage(mUiContext.getText(R.string.system_error_wipe_data));
+                                d.setButton(DialogInterface.BUTTON_POSITIVE,
+                                        mUiContext.getText(R.string.ok),
+                                        mUiHandler.obtainMessage(DISMISS_DIALOG_UI_MSG, d));
+                                d.show();
+                            }
+                        });
+                    }
+                } catch (RemoteException e) {
+                }
+
+                if (!Build.isBuildConsistent()) {
+                    Slog.e(TAG, "Build fingerprint is not consistent, warning user");
+                    mUiHandler.post(() -> {
+                        if (mShowDialogs) {
+                            AlertDialog d = new BaseErrorDialog(mUiContext);
+                            d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+                            d.setCancelable(false);
+                            d.setTitle(mUiContext.getText(R.string.android_system_label));
+                            d.setMessage(mUiContext.getText(R.string.system_error_manufacturer));
+                            d.setButton(DialogInterface.BUTTON_POSITIVE,
+                                    mUiContext.getText(R.string.ok),
+                                    mUiHandler.obtainMessage(DISMISS_DIALOG_UI_MSG, d));
+                            d.show();
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {
+            synchronized (mGlobalLock) {
+                return ActivityTaskManagerService.this.inputDispatchingTimedOut(
+                        pid, aboveSystem, reason);
+            }
+        }
+
+        @Override
+        public void onProcessMapped(int pid, WindowProcessController proc) {
+            synchronized (mGlobalLock) {
+                mPidMap.put(pid, proc);
+            }
+        }
+
+        @Override
+        public void onProcessUnMapped(int pid) {
+            synchronized (mGlobalLock) {
+                mPidMap.remove(pid);
+            }
+        }
+
+        @Override
+        public void onPackageDataCleared(String name) {
+            synchronized (mGlobalLock) {
+                mAppWarnings.onPackageDataCleared(name);
+            }
+        }
+
+        @Override
+        public void onPackageUninstalled(String name) {
+            synchronized (mGlobalLock) {
+                mAppWarnings.onPackageUninstalled(name);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index 68c63a2..cde633d 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -92,7 +92,7 @@
         attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
                 | WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
         getWindow().setAttributes(attrs);
-        if (mProc.persistent) {
+        if (mProc.isPersistent()) {
             getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
         }
 
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index a6dafbb..162f344 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -25,7 +25,6 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.app.ApplicationErrorReport;
 import android.app.Dialog;
@@ -45,7 +44,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
-import android.util.Log;
 import android.util.Slog;
 import android.util.StatsLog;
 import android.util.SparseArray;
@@ -57,7 +55,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Set;
 
 import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
@@ -314,9 +311,9 @@
     }
 
     void killAppAtUserRequestLocked(ProcessRecord app, Dialog fromDialog) {
-        app.crashing = false;
+        app.setCrashing(false);
         app.crashingReport = null;
-        app.notResponding = false;
+        app.setNotResponding(false);
         app.notRespondingReport = null;
         if (app.anrDialog == fromDialog) {
             app.anrDialog = null;
@@ -409,10 +406,14 @@
 
         // 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.persistent) {
+        if (r != null && r.isPersistent()) {
             RescueParty.notePersistentAppCrash(mContext, r.uid);
         }
 
+        final int relaunchReason = r != null
+                ? r.getWindowProcessController().computeRelaunchReason()
+                : ActivityRecord.RELAUNCH_REASON_NONE;
+
         AppErrorResult result = new AppErrorResult();
         TaskRecord task;
         synchronized (mService) {
@@ -425,11 +426,17 @@
                 return;
             }
 
+            // Suppress crash dialog if the process is being relaunched due to a crash during a free
+            // resize.
+            if (relaunchReason == ActivityRecord.RELAUNCH_REASON_FREE_RESIZE) {
+                return;
+            }
+
             /**
              * If this process was running instrumentation, finish now - it will be handled in
              * {@link ActivityManagerService#handleAppDiedLocked}.
              */
-            if (r != null && r.instr != null) {
+            if (r != null && r.getActiveInstrumentation() != null) {
                 return;
             }
 
@@ -471,7 +478,7 @@
                 mService.removeProcessLocked(r, false, true, "crash");
                 if (task != null) {
                     try {
-                        mService.startActivityFromRecents(task.taskId,
+                        mService.mActivityTaskManager.startActivityFromRecents(task.taskId,
                                 ActivityOptions.makeBasic().toBundle());
                     } catch (IllegalArgumentException e) {
                         // Hmm, that didn't work, app might have crashed before creating a
@@ -479,12 +486,13 @@
                         final Set<String> cats = task.intent != null
                                 ? task.intent.getCategories() : null;
                         if (cats != null && cats.contains(Intent.CATEGORY_LAUNCHER)) {
-                            mService.getActivityStartController().startActivityInPackage(
+                            mService.mActivityTaskManager.getActivityStartController().startActivityInPackage(
                                     task.mCallingUid, callingPid, callingUid, task.mCallingPackage,
                                     task.intent, null, null, null, 0, 0,
                                     new SafeActivityOptions(ActivityOptions.makeBasic()),
                                     task.userId, null,
-                                    "AppErrors", false /*validateIncomingUser*/);
+                                    "AppErrors", false /*validateIncomingUser*/,
+                                    null /* originatingPendingIntent */);
                         }
                     }
                 }
@@ -493,10 +501,10 @@
                 long orig = Binder.clearCallingIdentity();
                 try {
                     // Kill it with fire!
-                    mService.mStackSupervisor.handleAppCrashLocked(r);
-                    if (!r.persistent) {
+                    mService.mStackSupervisor.handleAppCrashLocked(r.getWindowProcessController());
+                    if (!r.isPersistent()) {
                         mService.removeProcessLocked(r, false, false, "crash");
-                        mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                        mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
                     }
                 } finally {
                     Binder.restoreCallingIdentity(orig);
@@ -532,7 +540,7 @@
                                                        String shortMsg, String longMsg,
                                                        String stackTrace, long timeMillis,
                                                        int callingPid, int callingUid) {
-        if (mService.mController == null) {
+        if (mService.mActivityTaskManager.mController == null) {
             return false;
         }
 
@@ -540,7 +548,7 @@
             String name = r != null ? r.processName : null;
             int pid = r != null ? r.pid : callingPid;
             int uid = r != null ? r.info.uid : callingUid;
-            if (!mService.mController.appCrashed(name, pid,
+            if (!mService.mActivityTaskManager.mController.appCrashed(name, pid,
                     shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
                 if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
                         && "Native crash".equals(crashInfo.exceptionClassName)) {
@@ -563,7 +571,7 @@
                 return true;
             }
         } catch (RemoteException e) {
-            mService.mController = null;
+            mService.mActivityTaskManager.mController = null;
             Watchdog.getInstance().setActivityController(null);
         }
         return false;
@@ -571,11 +579,11 @@
 
     private boolean makeAppCrashingLocked(ProcessRecord app,
             String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
-        app.crashing = true;
+        app.setCrashing(true);
         app.crashingReport = generateProcessError(app,
                 ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
         startAppProblemLocked(app);
-        app.stopFreezingAllLocked();
+        app.getWindowProcessController().stopFreezingActivities();
         return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
                 data);
     }
@@ -643,7 +651,7 @@
             return null;
         }
 
-        if (!r.crashing && !r.notResponding && !r.forceCrashReport) {
+        if (!r.isCrashing() && !r.isNotResponding() && !r.forceCrashReport) {
             return null;
         }
 
@@ -654,10 +662,10 @@
         report.time = timeMillis;
         report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
 
-        if (r.crashing || r.forceCrashReport) {
+        if (r.isCrashing() || r.forceCrashReport) {
             report.type = ApplicationErrorReport.TYPE_CRASH;
             report.crashInfo = crashInfo;
-        } else if (r.notResponding) {
+        } else if (r.isNotResponding()) {
             report.type = ApplicationErrorReport.TYPE_ANR;
             report.anrInfo = new ApplicationErrorReport.AnrInfo();
 
@@ -715,8 +723,8 @@
                     + " has crashed too many times: killing!");
             EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
                     app.userId, app.info.processName, app.uid);
-            mService.mStackSupervisor.handleAppCrashLocked(app);
-            if (!app.persistent) {
+            mService.mStackSupervisor.handleAppCrashLocked(app.getWindowProcessController());
+            if (!app.isPersistent()) {
                 // We don't want to start this process again until the user
                 // explicitly does so...  but for persistent process, we really
                 // need to keep it running.  If a persistent process is actually
@@ -736,15 +744,15 @@
                 // annoy the user repeatedly.  Unless it is persistent, since those
                 // processes run critical code.
                 mService.removeProcessLocked(app, false, tryAgain, "crash");
-                mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
                 if (!showBackground) {
                     return false;
                 }
             }
-            mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+            mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
         } else {
             final TaskRecord affectedTask =
-                    mService.mStackSupervisor.finishTopCrashedActivitiesLocked(app, reason);
+                    mService.mStackSupervisor.finishTopCrashedActivitiesLocked(app.getWindowProcessController(), reason);
             if (data != null) {
                 data.task = affectedTask;
             }
@@ -762,21 +770,11 @@
         // replaced by a third-party app, clear the package preferred activities from packages
         // with a home activity running in the process to prevent a repeatedly crashing app
         // from blocking the user to manually clear the list.
-        final ArrayList<ActivityRecord> activities = app.activities;
-        if (app == mService.mHomeProcess && activities.size() > 0
-                && (mService.mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if (r.isActivityTypeHome()) {
-                    Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
-                    try {
-                        ActivityThread.getPackageManager()
-                                .clearPackagePreferredActivities(r.packageName);
-                    } catch (RemoteException c) {
-                        // pm is in same process, this will never happen.
-                    }
-                }
-            }
+        final WindowProcessController proc = app.getWindowProcessController();
+        final WindowProcessController homeProc = mService.mActivityTaskManager.mHomeProcess;
+        if (proc == homeProc && proc.hasActivities()
+                && (homeProc.mInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+            proc.clearPackagePreferredForHomeActivities();
         }
 
         if (!app.isolated) {
@@ -837,7 +835,8 @@
                     mService.mUserController.getCurrentUserId()) != 0;
             final boolean crashSilenced = mAppsNotReportingCrashes != null &&
                     mAppsNotReportingCrashes.contains(proc.info.packageName);
-            if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced
+            if ((mService.mActivityTaskManager.canShowErrorDialogs() || showBackground)
+                    && !crashSilenced
                     && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
                 proc.crashDialog = dialogToShow = new AppErrorDialog(mContext, mService, data);
             } else {
@@ -887,16 +886,16 @@
         ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
         SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
 
-        if (mService.mController != null) {
+        if (mService.mActivityTaskManager.mController != null) {
             try {
                 // 0 == continue, -1 = kill process immediately
-                int res = mService.mController.appEarlyNotResponding(
+                int res = mService.mActivityTaskManager.mController.appEarlyNotResponding(
                         app.processName, app.pid, annotation);
                 if (res < 0 && app.pid != MY_PID) {
                     app.kill("anr", true);
                 }
             } catch (RemoteException e) {
-                mService.mController = null;
+                mService.mActivityTaskManager.mController = null;
                 Watchdog.getInstance().setActivityController(null);
             }
         }
@@ -914,13 +913,13 @@
 
         synchronized (mService) {
             // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
-            if (mService.mShuttingDown) {
+            if (mService.mActivityTaskManager.mShuttingDown) {
                 Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
                 return;
-            } else if (app.notResponding) {
+            } else if (app.isNotResponding()) {
                 Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
                 return;
-            } else if (app.crashing) {
+            } else if (app.isCrashing()) {
                 Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
                 return;
             } else if (app.killedByAm) {
@@ -933,7 +932,7 @@
 
             // In case we come through here for the same app before completing
             // this one, mark as anring now so we will bail out.
-            app.notResponding = true;
+            app.setNotResponding(true);
 
             // Log the ANR to the event log.
             EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
@@ -946,8 +945,8 @@
             isSilentANR = !showBackground && !isInterestingForBackgroundTraces(app);
             if (!isSilentANR) {
                 int parentPid = app.pid;
-                if (parent != null && parent.app != null && parent.app.pid > 0) {
-                    parentPid = parent.app.pid;
+                if (parent != null && parent.app != null && parent.app.getPid() > 0) {
+                    parentPid = parent.app.getPid();
                 }
                 if (parentPid != app.pid) firstPids.add(parentPid);
 
@@ -958,7 +957,7 @@
                     if (r != null && r.thread != null) {
                         int pid = r.pid;
                         if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
-                            if (r.persistent) {
+                            if (r.isPersistent()) {
                                 firstPids.add(pid);
                                 if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
                             } else if (r.treatLikeActivity) {
@@ -1054,10 +1053,10 @@
         mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
                 cpuInfo, tracesFile, null);
 
-        if (mService.mController != null) {
+        if (mService.mActivityTaskManager.mController != null) {
             try {
                 // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
-                int res = mService.mController.appNotResponding(
+                int res = mService.mActivityTaskManager.mController.appNotResponding(
                         app.processName, app.pid, info.toString());
                 if (res != 0) {
                     if (res < 0 && app.pid != MY_PID) {
@@ -1070,7 +1069,7 @@
                     return;
                 }
             } catch (RemoteException e) {
-                mService.mController = null;
+                mService.mActivityTaskManager.mController = null;
                 Watchdog.getInstance().setActivityController(null);
             }
         }
@@ -1100,12 +1099,12 @@
 
     private void makeAppNotRespondingLocked(ProcessRecord app,
             String activity, String shortMsg, String longMsg) {
-        app.notResponding = true;
+        app.setNotResponding(true);
         app.notRespondingReport = generateProcessError(app,
                 ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
                 activity, shortMsg, longMsg, null);
         startAppProblemLocked(app);
-        app.stopFreezingAllLocked();
+        app.getWindowProcessController().stopFreezingActivities();
     }
 
     void handleShowAnrUi(Message msg) {
@@ -1135,7 +1134,7 @@
 
             boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
                     Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
-            if (mService.canShowErrorDialogs() || showBackground) {
+            if (mService.mActivityTaskManager.canShowErrorDialogs() || showBackground) {
                 dialogToShow = new AppNotRespondingDialog(mService, mContext, data);
                 proc.anrDialog = dialogToShow;
             } else {
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index 8a88a69..7c983ff 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -157,7 +157,7 @@
                                     System.currentTimeMillis(), null);
                         }
 
-                        app.notResponding = false;
+                        app.setNotResponding(false);
                         app.notRespondingReport = null;
                         if (app.anrDialog == AppNotRespondingDialog.this) {
                             app.anrDialog = null;
diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java
index 5f5a504..a1f1ff9 100644
--- a/services/core/java/com/android/server/am/AppTaskImpl.java
+++ b/services/core/java/com/android/server/am/AppTaskImpl.java
@@ -20,7 +20,6 @@
 import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 
 import android.app.ActivityManager;
-import android.app.ActivityOptions;
 import android.app.IAppTask;
 import android.app.IApplicationThread;
 import android.content.Intent;
@@ -35,12 +34,12 @@
  * only the process that calls getAppTasks() can call the AppTask methods.
  */
 class AppTaskImpl extends IAppTask.Stub {
-    private ActivityManagerService mService;
+    private ActivityTaskManagerService mService;
 
     private int mTaskId;
     private int mCallingUid;
 
-    public AppTaskImpl(ActivityManagerService service, int taskId, int callingUid) {
+    public AppTaskImpl(ActivityTaskManagerService service, int taskId, int callingUid) {
         mService = service;
         mTaskId = taskId;
         mCallingUid = callingUid;
@@ -57,7 +56,7 @@
     public void finishAndRemoveTask() {
         checkCaller();
 
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             long origId = Binder.clearCallingIdentity();
             try {
                 // We remove the task from recents to preserve backwards
@@ -75,7 +74,7 @@
     public ActivityManager.RecentTaskInfo getTaskInfo() {
         checkCaller();
 
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             long origId = Binder.clearCallingIdentity();
             try {
                 TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
@@ -98,7 +97,7 @@
         final int callingUid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
-            synchronized (this) {
+            synchronized (mService.mGlobalLock) {
                 mService.mStackSupervisor.startActivityFromRecents(callingPid, callingUid, mTaskId,
                         null);
             }
@@ -115,7 +114,7 @@
         int callingUser = UserHandle.getCallingUserId();
         TaskRecord tr;
         IApplicationThread appThread;
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
                     MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
             if (tr == null) {
@@ -141,7 +140,7 @@
     public void setExcludeFromRecents(boolean exclude) {
         checkCaller();
 
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             long origId = Binder.clearCallingIdentity();
             try {
                 TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
diff --git a/services/core/java/com/android/server/am/AppWarnings.java b/services/core/java/com/android/server/am/AppWarnings.java
index 30a3844..a705180 100644
--- a/services/core/java/com/android/server/am/AppWarnings.java
+++ b/services/core/java/com/android/server/am/AppWarnings.java
@@ -17,7 +17,6 @@
 package com.android.server.am;
 
 import android.annotation.UiThread;
-import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -57,9 +56,9 @@
 
     private final HashMap<String, Integer> mPackageFlags = new HashMap<>();
 
-    private final ActivityManagerService mAms;
+    private final ActivityTaskManagerService mAtm;
     private final Context mUiContext;
-    private final ConfigHandler mAmsHandler;
+    private final ConfigHandler mHandler;
     private final UiHandler mUiHandler;
     private final AtomicFile mConfigFile;
 
@@ -81,17 +80,17 @@
      * <p>
      * <strong>Note:</strong> Must be called from the ActivityManagerService thread.
      *
-     * @param ams
+     * @param atm
      * @param uiContext
-     * @param amsHandler
+     * @param handler
      * @param uiHandler
      * @param systemDir
      */
-    public AppWarnings(ActivityManagerService ams, Context uiContext, Handler amsHandler,
+    public AppWarnings(ActivityTaskManagerService atm, Context uiContext, Handler handler,
             Handler uiHandler, File systemDir) {
-        mAms = ams;
+        mAtm = atm;
         mUiContext = uiContext;
-        mAmsHandler = new ConfigHandler(amsHandler.getLooper());
+        mHandler = new ConfigHandler(handler.getLooper());
         mUiHandler = new UiHandler(uiHandler.getLooper());
         mConfigFile = new AtomicFile(new File(systemDir, CONFIG_FILE_NAME), "warnings-config");
 
@@ -104,7 +103,7 @@
      * @param r activity record for which the warning may be displayed
      */
     public void showUnsupportedDisplaySizeDialogIfNeeded(ActivityRecord r) {
-        final Configuration globalConfig = mAms.getGlobalConfiguration();
+        final Configuration globalConfig = mAtm.getGlobalConfiguration();
         if (globalConfig.densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE
                 && r.appInfo.requiresSmallestWidthDp > globalConfig.smallestScreenWidthDp) {
             mUiHandler.showUnsupportedDisplaySizeDialog(r);
@@ -211,7 +210,7 @@
 
         synchronized (mPackageFlags) {
             mPackageFlags.remove(name);
-            mAmsHandler.scheduleWrite();
+            mHandler.scheduleWrite();
         }
     }
 
@@ -351,7 +350,7 @@
                 } else {
                     mPackageFlags.remove(name);
                 }
-                mAmsHandler.scheduleWrite();
+                mHandler.scheduleWrite();
             }
         }
     }
@@ -430,10 +429,10 @@
     }
 
     /**
-     * Handles messages on the ActivityManagerService thread.
+     * Handles messages on the ActivityTaskManagerService thread.
      */
     private final class ConfigHandler extends Handler {
-        private static final int MSG_WRITE = ActivityManagerService.FIRST_COMPAT_MODE_MSG;
+        private static final int MSG_WRITE = 1;
 
         private static final int DELAY_MSG_WRITE = 10000;
 
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index 9f7621f..395b0da 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -17,12 +17,12 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.ASSIST_CONTEXT_FULL;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_NONE;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
 
+import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
-import android.app.IActivityManager;
 import android.app.IAssistDataReceiver;
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -48,7 +48,6 @@
     public static final String KEY_RECEIVER_EXTRA_COUNT = "count";
     public static final String KEY_RECEIVER_EXTRA_INDEX = "index";
 
-    private IActivityManager mService;
     private IWindowManager mWindowManager;
     private Context mContext;
     private AppOpsManager mAppOpsManager;
@@ -117,14 +116,13 @@
      *                                This can be {@link AppOpsManager#OP_NONE} to indicate that
      *                                screenshots should never be fetched.
      */
-    public AssistDataRequester(Context context, IActivityManager service,
+    public AssistDataRequester(Context context,
             IWindowManager windowManager, AppOpsManager appOpsManager,
             AssistDataRequesterCallbacks callbacks, Object callbacksLock,
             int requestStructureAppOps, int requestScreenshotAppOps) {
         mCallbacks = callbacks;
         mCallbacksLock = callbacksLock;
         mWindowManager = windowManager;
-        mService = service;
         mContext = context;
         mAppOpsManager = appOpsManager;
         mRequestStructureAppOps = requestStructureAppOps;
@@ -163,7 +161,8 @@
         // Ensure that the current activity supports assist data
         boolean isAssistDataAllowed = false;
         try {
-            isAssistDataAllowed = mService.isAssistDataAllowedOnCurrentActivity();
+            isAssistDataAllowed =
+                    ActivityTaskManager.getService().isAssistDataAllowedOnCurrentActivity();
         } catch (RemoteException e) {
             // Should never happen
         }
@@ -188,9 +187,9 @@
                         Bundle receiverExtras = new Bundle();
                         receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
                         receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, numActivities);
-                        if (mService.requestAssistContextExtras(ASSIST_CONTEXT_FULL, this,
-                                receiverExtras, topActivity, /* focused= */ i == 0,
-                                    /* newSessionId= */ i == 0)) {
+                        if (ActivityTaskManager.getService().requestAssistContextExtras(
+                                ASSIST_CONTEXT_FULL, this, receiverExtras, topActivity,
+                                /* focused= */ i == 0, /* newSessionId= */ i == 0)) {
                             mPendingDataCount++;
                         } else if (i == 0) {
                             // Wasn't allowed... given that, let's not do the screenshot either.
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 094cf06..2541352 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.am;
 
+import static android.net.wifi.WifiManager.WIFI_FEATURE_LINK_LAYER_STATS;
+
 import android.annotation.Nullable;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.BluetoothAdapter;
@@ -410,8 +412,11 @@
 
             if (mWifiManager != null) {
                 try {
-                    wifiReceiver = new SynchronousResultReceiver("wifi");
-                    mWifiManager.requestActivityInfo(wifiReceiver);
+                    // Only fetch WiFi power data if it is supported.
+                    if ((mWifiManager.getSupportedFeatures() & WIFI_FEATURE_LINK_LAYER_STATS) != 0) {
+                        wifiReceiver = new SynchronousResultReceiver("wifi");
+                        mWifiManager.requestActivityInfo(wifiReceiver);
+                    }
                 } catch (RemoteException e) {
                     // Oh well.
                 }
@@ -546,14 +551,25 @@
         final long idleTimeMs = latest.mControllerIdleTimeMs - lastIdleMs;
         final long scanTimeMs = latest.mControllerScanTimeMs - lastScanMs;
 
-        if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0) {
+        if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0 || idleTimeMs < 0) {
             // The stats were reset by the WiFi system (which is why our delta is negative).
-            // Returns the unaltered stats.
-            delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
-            delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
-            delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
-            delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
-            delta.mControllerScanTimeMs = latest.mControllerScanTimeMs;
+            // Returns the unaltered stats. The total on time should not exceed the time
+            // duartion between reports.
+            final long totalOnTimeMs = latest.mControllerTxTimeMs + latest.mControllerRxTimeMs
+                        + latest.mControllerIdleTimeMs;
+            if (totalOnTimeMs <= timePeriodMs + MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS) {
+                delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
+                delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
+                delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
+                delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
+                delta.mControllerScanTimeMs = latest.mControllerScanTimeMs;
+            } else {
+                delta.mControllerEnergyUsed = 0;
+                delta.mControllerRxTimeMs = 0;
+                delta.mControllerTxTimeMs = 0;
+                delta.mControllerIdleTimeMs = 0;
+                delta.mControllerScanTimeMs = 0;
+            }
             Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
         } else {
             final long totalActiveTimeMs = txTimeMs + rxTimeMs;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ef23a83..ab9ba08 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -59,6 +59,7 @@
 import com.android.internal.os.PowerProfile;
 import com.android.internal.os.RpmStats;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.ParseUtils;
 import com.android.server.LocalServices;
 
 import java.io.File;
@@ -395,6 +396,7 @@
             mStats.writeToParcel(out, 0);
         }
         byte[] data = out.marshall();
+        if (DBG) Slog.d(TAG, "getStatisticsStream parcel size is:" + data.length);
         out.recycle();
         try {
             return ParcelFileDescriptor.fromData(data, "battery-stats");
@@ -1222,6 +1224,7 @@
         pw.println("  --proto: write the current aggregate stats (without history) in proto format.");
         pw.println("  --history: show only history data.");
         pw.println("  --history-start <num>: show only history data starting at given time offset.");
+        pw.println("  --history-create-events <num>: create <num> of battery history events.");
         pw.println("  --charged: only output data since last charged.");
         pw.println("  --daily: only output full daily data.");
         pw.println("  --reset: reset the stats, clearing all current data.");
@@ -1310,8 +1313,21 @@
                         dumpHelp(pw);
                         return;
                     }
-                    historyStart = Long.parseLong(args[i]);
+                    historyStart = ParseUtils.parseLong(args[i], 0);
                     writeData = true;
+                } else if ("--history-create-events".equals(arg)) {
+                    i++;
+                    if (i >= args.length) {
+                        pw.println("Missing events argument for --history-create-events");
+                        dumpHelp(pw);
+                        return;
+                    }
+                    final long events = ParseUtils.parseLong(args[i], 0);
+                    synchronized (mStats) {
+                        mStats.createFakeHistoryEvents(events);
+                        pw.println("Battery history create events started.");
+                        noOutput = true;
+                    }
                 } else if ("-c".equals(arg)) {
                     useCheckinFormat = true;
                     flags |= BatteryStats.DUMP_INCLUDE_HISTORY;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index c9a26cb..b36b5d3 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -304,7 +304,7 @@
             app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                     mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
                     r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
-                    app.repProcState);
+                    app.getReportedProcState());
             if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                     "Process cur broadcast " + r + " DELIVERED for app " + app);
             started = true;
@@ -492,7 +492,7 @@
                 // correctly ordered with other one-way calls.
                 try {
                     app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
-                            data, extras, ordered, sticky, sendingUser, app.repProcState);
+                            data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
                 // TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
                 // DeadObjectException when the process isn't actually dead.
                 //} catch (DeadObjectException ex) {
@@ -618,7 +618,7 @@
         }
 
         if (!skip && (filter.receiverList.app == null || filter.receiverList.app.killed
-                || filter.receiverList.app.crashing)) {
+                || filter.receiverList.app.isCrashing())) {
             Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
                     + " to " + filter.receiverList + ": process gone or crashing");
             skip = true;
@@ -664,12 +664,10 @@
         // the broadcast and if the calling app is in the foreground and the broadcast is
         // explicit we launch the review UI passing it a pending intent to send the skipped
         // broadcast.
-        if (mService.mPermissionReviewRequired) {
-            if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
-                    filter.owningUserId)) {
-                r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
-                return;
-            }
+        if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
+                filter.owningUserId)) {
+            r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
+            return;
         }
 
         r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
@@ -885,7 +883,7 @@
                 synchronized (mService.mPidsSelfLocked) {
                     ProcessRecord proc = mService.mPidsSelfLocked.get(
                             mPendingBroadcast.curApp.pid);
-                    isDead = proc == null || proc.crashing;
+                    isDead = proc == null || proc.isCrashing();
                 }
             } else {
                 final ProcessRecord proc = mService.mProcessNames.get(
@@ -1210,7 +1208,7 @@
                     + " (uid " + r.callingUid + ")");
             skip = true;
         }
-        if (r.curApp != null && r.curApp.crashing) {
+        if (r.curApp != null && r.curApp.isCrashing()) {
             // If the target process is crashing, just skip it.
             Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r
                     + " to " + r.curApp + ": process crashing");
@@ -1240,7 +1238,7 @@
         // the broadcast and if the calling app is in the foreground and the broadcast is
         // explicit we launch the review UI passing it a pending intent to send the skipped
         // broadcast.
-        if (mService.mPermissionReviewRequired && !skip) {
+        if (!skip) {
             if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
                     info.activityInfo.packageName, UserHandle.getUserId(
                             info.activityInfo.applicationInfo.uid))) {
@@ -1464,7 +1462,7 @@
         // If the receiver app is being debugged we quietly ignore unresponsiveness, just
         // tidying up and moving on to the next broadcast without crashing or ANRing this
         // app just because it's stopped at a breakpoint.
-        final boolean debugging = (r.curApp != null && r.curApp.debugging);
+        final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
 
         Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
                 + ", started " + (now - r.receiverTime) + "ms ago");
diff --git a/services/core/java/com/android/server/am/CompatModeDialog.java b/services/core/java/com/android/server/am/CompatModeDialog.java
deleted file mode 100644
index 202cc7c..0000000
--- a/services/core/java/com/android/server/am/CompatModeDialog.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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 android.app.ActivityManager;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.view.Gravity;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.Switch;
-
-public final class CompatModeDialog extends Dialog {
-    final ActivityManagerService mService;
-    final ApplicationInfo mAppInfo;
-
-    final Switch mCompatEnabled;
-    final CheckBox mAlwaysShow;
-    final View mHint;
-
-    public CompatModeDialog(ActivityManagerService service, Context context,
-            ApplicationInfo appInfo) {
-        super(context, com.android.internal.R.style.Theme_Holo_Dialog_MinWidth);
-        setCancelable(true);
-        setCanceledOnTouchOutside(true);
-        getWindow().requestFeature(Window.FEATURE_NO_TITLE);
-        getWindow().setType(WindowManager.LayoutParams.TYPE_PHONE);
-        getWindow().setGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL);
-        mService = service;
-        mAppInfo = appInfo;
-
-        setContentView(com.android.internal.R.layout.am_compat_mode_dialog);
-        mCompatEnabled = (Switch)findViewById(com.android.internal.R.id.compat_checkbox);
-        mCompatEnabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                synchronized (mService) {
-                    mService.mCompatModePackages.setPackageScreenCompatModeLocked(
-                            mAppInfo.packageName,
-                            mCompatEnabled.isChecked() ? ActivityManager.COMPAT_MODE_ENABLED
-                                    : ActivityManager.COMPAT_MODE_DISABLED);
-                    updateControls();
-                }
-            }
-        });
-        mAlwaysShow = (CheckBox)findViewById(com.android.internal.R.id.ask_checkbox);
-        mAlwaysShow.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                synchronized (mService) {
-                    mService.mCompatModePackages.setPackageAskCompatModeLocked(
-                            mAppInfo.packageName, mAlwaysShow.isChecked());
-                    updateControls();
-                }
-            }
-        });
-        mHint = findViewById(com.android.internal.R.id.reask_hint);
-
-        updateControls();
-    }
-
-    void updateControls() {
-        synchronized (mService) {
-            int mode = mService.mCompatModePackages.computeCompatModeLocked(mAppInfo);
-            mCompatEnabled.setChecked(mode == ActivityManager.COMPAT_MODE_ENABLED);
-            boolean ask = mService.mCompatModePackages.getPackageAskCompatModeLocked(
-                    mAppInfo.packageName);
-            mAlwaysShow.setChecked(ask);
-            mHint.setVisibility(ask ? View.INVISIBLE : View.VISIBLE);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index c6947ab..77efbfc 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -219,25 +219,10 @@
                 : ActivityManager.COMPAT_MODE_DISABLED;
     }
 
-    public boolean getFrontActivityAskCompatModeLocked() {
-        ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
-        if (r == null) {
-            return false;
-        }
-        return getPackageAskCompatModeLocked(r.packageName);
-    }
-
     public boolean getPackageAskCompatModeLocked(String packageName) {
         return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
     }
 
-    public void setFrontActivityAskCompatModeLocked(boolean ask) {
-        ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
-        if (r != null) {
-            setPackageAskCompatModeLocked(r.packageName, ask);
-        }
-    }
-
     public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
         setPackageFlagLocked(packageName, COMPAT_FLAG_DONT_ASK, ask);
     }
@@ -255,23 +240,6 @@
         }
     }
 
-    public int getFrontActivityScreenCompatModeLocked() {
-        ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
-        if (r == null) {
-            return ActivityManager.COMPAT_MODE_UNKNOWN;
-        }
-        return computeCompatModeLocked(r.info.applicationInfo);
-    }
-
-    public void setFrontActivityScreenCompatModeLocked(int mode) {
-        ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
-        if (r == null) {
-            Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
-            return;
-        }
-        setPackageScreenCompatModeLocked(r.info.applicationInfo, mode);
-    }
-
     public int getPackageScreenCompatModeLocked(String packageName) {
         ApplicationInfo ai = null;
         try {
@@ -297,7 +265,7 @@
         setPackageScreenCompatModeLocked(ai, mode);
     }
 
-    private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
+    void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
         final String packageName = ai.packageName;
 
         int curFlags = getPackageFlags(packageName);
@@ -349,7 +317,7 @@
 
             scheduleWrite();
 
-            final ActivityStack stack = mService.getFocusedStack();
+            final ActivityStack stack = mService.mActivityTaskManager.getTopDisplayFocusedStack();
             ActivityRecord starting = stack.restartPackage(packageName);
 
             // Tell all processes that loaded this package about the change.
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 679024ee..fa8e6c4 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -16,12 +16,18 @@
 
 package com.android.server.am;
 
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+
 import android.app.IServiceConnection;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
 
+import com.android.internal.app.procstats.AssociationState;
+import com.android.internal.app.procstats.ProcessStats;
+
 import java.io.PrintWriter;
 
 /**
@@ -34,6 +40,9 @@
     final int flags;                // Binding options.
     final int clientLabel;          // String resource labeling this client.
     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
+    public AssociationState.SourceState association; // Association tracking
     String stringName;              // Caching of toString.
     boolean serviceDead;            // Well is it?
 
@@ -83,14 +92,54 @@
     }
 
     ConnectionRecord(AppBindRecord _binding, ActivityRecord _activity,
-               IServiceConnection _conn, int _flags,
-               int _clientLabel, PendingIntent _clientIntent) {
+            IServiceConnection _conn, int _flags,
+            int _clientLabel, PendingIntent _clientIntent,
+            int _clientUid, String _clientProcessName) {
         binding = _binding;
         activity = _activity;
         conn = _conn;
         flags = _flags;
         clientLabel = _clientLabel;
         clientIntent = _clientIntent;
+        clientUid = _clientUid;
+        clientProcessName = _clientProcessName;
+    }
+
+    public void startAssociationIfNeeded() {
+        // If we don't already have an active association, create one...  but only if this
+        // is an association between two different processes.
+        if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS
+                && association == null && binding.service.app != null
+                && (binding.service.appInfo.uid != clientUid
+                        || !binding.service.processName.equals(clientProcessName))) {
+            ProcessStats.ProcessStateHolder holder = binding.service.app.pkgList.get(
+                    binding.service.name.getPackageName());
+            if (holder == null) {
+                Slog.wtf(TAG_AM, "No package in referenced service "
+                        + binding.service.name.toShortString() + ": proc=" + binding.service.app);
+            } else if (holder.pkg == null) {
+                Slog.wtf(TAG_AM, "Inactive holder in referenced service "
+                        + binding.service.name.toShortString() + ": proc=" + binding.service.app);
+            } else {
+                association = holder.pkg.getAssociationStateLocked(holder.state,
+                        binding.service.name.getClassName()).startSource(clientUid,
+                        clientProcessName);
+
+            }
+        }
+    }
+
+    public void trackProcState(int procState, int seq, long now) {
+        if (association != null) {
+            association.trackProcState(procState, seq, now);
+        }
+    }
+
+    public void stopAssociation() {
+        if (association != null) {
+            association.stop();
+            association = null;
+        }
     }
 
     public String toString() {
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index f2c9e2f..f2d4f73 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -18,14 +18,21 @@
 
 import android.os.Binder;
 import android.os.SystemClock;
+import android.util.Slog;
 import android.util.TimeUtils;
 
+import com.android.internal.app.procstats.AssociationState;
+import com.android.internal.app.procstats.ProcessStats;
+
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+
 /**
  * Represents a link between a content provider and client.
  */
 public final class ContentProviderConnection extends Binder {
     public final ContentProviderRecord provider;
     public final ProcessRecord client;
+    public AssociationState.SourceState association;
     public final long createTime;
     public int stableCount;
     public int unstableCount;
@@ -45,6 +52,42 @@
         createTime = SystemClock.elapsedRealtime();
     }
 
+    public void startAssociationIfNeeded() {
+        // If we don't already have an active association, create one...  but only if this
+        // is an association between two different processes.
+        if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS
+                && association == null && provider.proc != null
+                && (provider.appInfo.uid != client.uid
+                        || !provider.info.processName.equals(client.processName))) {
+            ProcessStats.ProcessStateHolder holder = provider.proc.pkgList.get(
+                    provider.name.getPackageName());
+            if (holder == null) {
+                Slog.wtf(TAG_AM, "No package in referenced provider "
+                        + provider.name.toShortString() + ": proc=" + provider.proc);
+            } else if (holder.pkg == null) {
+                Slog.wtf(TAG_AM, "Inactive holder in referenced provider "
+                        + provider.name.toShortString() + ": proc=" + provider.proc);
+            } else {
+                association = holder.pkg.getAssociationStateLocked(holder.state,
+                        provider.name.getClassName()).startSource(client.uid, client.processName);
+
+            }
+        }
+    }
+
+    public void trackProcState(int procState, int seq, long now) {
+        if (association != null) {
+            association.trackProcState(procState, seq, now);
+        }
+    }
+
+    public void stopAssociation() {
+        if (association != null) {
+            association.stop();
+            association = null;
+        }
+    }
+
     public String toString() {
         StringBuilder sb = new StringBuilder(128);
         sb.append("ContentProviderConnection{");
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index cd39bcd..2fc4adc 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+
 import android.app.ContentProviderHolder;
 import android.content.ComponentName;
 import android.content.IContentProvider;
@@ -26,11 +28,14 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.Slog;
 
+import com.android.internal.app.procstats.AssociationState;
+import com.android.internal.app.procstats.ProcessStats;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.HashMap;
 
 final class ContentProviderRecord implements ComponentName.WithComponentName {
     final ActivityManagerService service;
@@ -46,7 +51,7 @@
             = new ArrayList<ContentProviderConnection>();
     //final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
     // Handles for non-framework processes supported by this provider
-    HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
+    ArrayMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
     // Count for external process for which we have no handles.
     int externalProcessNoHandleCount;
     ProcessRecord proc; // if non-null, hosting process.
@@ -62,7 +67,8 @@
         appInfo = ai;
         name = _name;
         singleton = _singleton;
-        noReleaseNeeded = uid == 0 || uid == Process.SYSTEM_UID;
+        noReleaseNeeded = (uid == 0 || uid == Process.SYSTEM_UID)
+                && (_name == null || !"com.android.settings".equals(_name.getPackageName()));
     }
 
     public ContentProviderRecord(ContentProviderRecord cpr) {
@@ -83,22 +89,47 @@
         return holder;
     }
 
+    public void setProcess(ProcessRecord proc) {
+        this.proc = proc;
+        if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS) {
+            for (int iconn = connections.size() - 1; iconn >= 0; iconn--) {
+                final ContentProviderConnection conn = connections.get(iconn);
+                if (proc != null) {
+                    conn.startAssociationIfNeeded();
+                } else {
+                    conn.stopAssociation();
+                }
+            }
+            if (externalProcessTokenToHandle != null) {
+                for (int iext = externalProcessTokenToHandle.size() - 1; iext >= 0; iext--) {
+                    final ExternalProcessHandle handle = externalProcessTokenToHandle.valueAt(iext);
+                    if (proc != null) {
+                        handle.startAssociationIfNeeded(this);
+                    } else {
+                        handle.stopAssociation();
+                    }
+                }
+            }
+        }
+    }
+
     public boolean canRunHere(ProcessRecord app) {
         return (info.multiprocess || info.processName.equals(app.processName))
                 && uid == app.info.uid;
     }
 
-    public void addExternalProcessHandleLocked(IBinder token) {
+    public void addExternalProcessHandleLocked(IBinder token, int callingUid, String callingTag) {
         if (token == null) {
             externalProcessNoHandleCount++;
         } else {
             if (externalProcessTokenToHandle == null) {
-                externalProcessTokenToHandle = new HashMap<IBinder, ExternalProcessHandle>();
+                externalProcessTokenToHandle = new ArrayMap<>();
             }
             ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
             if (handle == null) {
-                handle = new ExternalProcessHandle(token);
+                handle = new ExternalProcessHandle(token, callingUid, callingTag);
                 externalProcessTokenToHandle.put(token, handle);
+                handle.startAssociationIfNeeded(this);
             }
             handle.mAcquisitionCount++;
         }
@@ -129,6 +160,7 @@
     private void removeExternalProcessHandleInternalLocked(IBinder token) {
         ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
         handle.unlinkFromOwnDeathLocked();
+        handle.stopAssociation();
         externalProcessTokenToHandle.remove(token);
         if (externalProcessTokenToHandle.size() == 0) {
             externalProcessTokenToHandle = null;
@@ -234,11 +266,16 @@
     private class ExternalProcessHandle implements DeathRecipient {
         private static final String LOG_TAG = "ExternalProcessHanldle";
 
-        private final IBinder mToken;
-        private int mAcquisitionCount;
+        final IBinder mToken;
+        final int mOwningUid;
+        final String mOwningProcessName;
+        int mAcquisitionCount;
+        AssociationState.SourceState mAssociation;
 
-        public ExternalProcessHandle(IBinder token) {
+        public ExternalProcessHandle(IBinder token, int owningUid, String owningProcessName) {
             mToken = token;
+            mOwningUid = owningUid;
+            mOwningProcessName = owningProcessName;
             try {
                 token.linkToDeath(this, 0);
             } catch (RemoteException re) {
@@ -250,6 +287,37 @@
             mToken.unlinkToDeath(this, 0);
         }
 
+        public void startAssociationIfNeeded(ContentProviderRecord provider) {
+            // If we don't already have an active association, create one...  but only if this
+            // is an association between two different processes.
+            if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS
+                    && mAssociation == null && provider.proc != null
+                    && (provider.appInfo.uid != mOwningUid
+                            || !provider.info.processName.equals(mOwningProcessName))) {
+                ProcessStats.ProcessStateHolder holder = provider.proc.pkgList.get(
+                        provider.name.getPackageName());
+                if (holder == null) {
+                    Slog.wtf(TAG_AM, "No package in referenced provider "
+                            + provider.name.toShortString() + ": proc=" + provider.proc);
+                } else if (holder.pkg == null) {
+                    Slog.wtf(TAG_AM, "Inactive holder in referenced provider "
+                            + provider.name.toShortString() + ": proc=" + provider.proc);
+                } else {
+                    mAssociation = holder.pkg.getAssociationStateLocked(holder.state,
+                            provider.name.getClassName()).startSource(mOwningUid,
+                            mOwningProcessName);
+
+                }
+            }
+        }
+
+        public void stopAssociation() {
+            if (mAssociation != null) {
+                mAssociation.stop();
+                mAssociation = null;
+            }
+        }
+
         @Override
         public void binderDied() {
             synchronized (service) {
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 30fa72f..ee4e36f 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -19,6 +19,13 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_UNSET;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
@@ -27,15 +34,7 @@
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED;
 import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
-import static android.view.WindowManager.TRANSIT_UNSET;
 
-import android.app.ActivityManagerInternal.SleepToken;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.Trace;
@@ -44,6 +43,7 @@
 
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
 import com.android.server.wm.WindowManagerService;
 
 import java.io.PrintWriter;
@@ -58,7 +58,7 @@
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_AM;
 
-    private final ActivityManagerService mService;
+    private final ActivityTaskManagerService mService;
     private final ActivityStackSupervisor mStackSupervisor;
     private WindowManagerService mWindowManager;
     private boolean mKeyguardShowing;
@@ -72,7 +72,7 @@
     private SleepToken mSleepToken;
     private int mSecondaryDisplayShowing = INVALID_DISPLAY;
 
-    KeyguardController(ActivityManagerService service,
+    KeyguardController(ActivityTaskManagerService service,
             ActivityStackSupervisor stackSupervisor) {
         mService = service;
         mStackSupervisor = stackSupervisor;
@@ -163,7 +163,7 @@
             updateKeyguardSleepToken();
 
             // Some stack visibility might change (e.g. docked stack)
-            mStackSupervisor.resumeFocusedStackTopActivityLocked();
+            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
             mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
             mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);
             mWindowManager.executeAppTransition();
@@ -272,7 +272,7 @@
                 // Only the top activity of the focused stack on the default display may control
                 // occluded state.
                 if (display.mDisplayId == DEFAULT_DISPLAY
-                        && mStackSupervisor.isFocusedStack(stack)) {
+                        && mStackSupervisor.isTopDisplayFocusedStack(stack)) {
 
                     // A dismissing activity occludes Keyguard in the insecure case for legacy
                     // reasons.
@@ -381,7 +381,7 @@
                 return;
             }
             mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
-                    mStackSupervisor.mFocusedStack == stack);
+                    stack.isFocusedStackOnDisplay());
         }
     }
 
diff --git a/services/core/java/com/android/server/am/LaunchParamsController.java b/services/core/java/com/android/server/am/LaunchParamsController.java
index 7ab7f98..6415c3e 100644
--- a/services/core/java/com/android/server/am/LaunchParamsController.java
+++ b/services/core/java/com/android/server/am/LaunchParamsController.java
@@ -38,7 +38,7 @@
  * registered {@link LaunchParamsModifier}s.
  */
 class LaunchParamsController {
-    private final ActivityManagerService mService;
+    private final ActivityTaskManagerService mService;
     private final List<LaunchParamsModifier> mModifiers = new ArrayList<>();
 
     // Temporary {@link LaunchParams} for internal calculations. This is kept separate from
@@ -48,7 +48,7 @@
     private final LaunchParams mTmpCurrent = new LaunchParams();
     private final LaunchParams mTmpResult = new LaunchParams();
 
-    LaunchParamsController(ActivityManagerService service) {
+    LaunchParamsController(ActivityTaskManagerService service) {
        mService = service;
     }
 
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index 151ef49..643c922 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -361,7 +361,7 @@
      * @param task the task that requested the end of lock task mode ({@code null} for quitting app
      *             pinning mode)
      * @param isSystemCaller indicates whether this request comes from the system via
-     *                       {@link ActivityManagerService#stopSystemLockTaskMode()}. If
+     *                       {@link ActivityTaskManagerService#stopSystemLockTaskMode()}. If
      *                       {@code true}, it means the user intends to stop pinned mode through UI;
      *                       otherwise, it's called by an app and we need to stop locked or pinned
      *                       mode, subject to checks.
@@ -446,7 +446,7 @@
             return;
         }
         task.performClearTaskLocked();
-        mSupervisor.resumeFocusedStackTopActivityLocked();
+        mSupervisor.resumeFocusedStacksTopActivitiesLocked();
     }
 
     /**
@@ -509,7 +509,7 @@
      *
      * @param task the task that should be locked.
      * @param isSystemCaller indicates whether this request was initiated by the system via
-     *                       {@link ActivityManagerService#startSystemLockTaskMode(int)}. If
+     *                       {@link ActivityTaskManagerService#startSystemLockTaskMode(int)}. If
      *                       {@code true}, this intends to start pinned mode; otherwise, we look
      *                       at the calling task's mLockTaskAuth to decide which mode to start.
      * @param callingUid the caller that requested the launch of lock task mode.
@@ -578,7 +578,7 @@
         if (andResume) {
             mSupervisor.findTaskToMoveToFront(task, 0, null, reason,
                     lockTaskModeState != LOCK_TASK_MODE_NONE);
-            mSupervisor.resumeFocusedStackTopActivityLocked();
+            mSupervisor.resumeFocusedStacksTopActivitiesLocked();
             mWindowManager.executeAppTransition();
         } else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
             mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
@@ -653,7 +653,7 @@
         }
 
         if (taskChanged) {
-            mSupervisor.resumeFocusedStackTopActivityLocked();
+            mSupervisor.resumeFocusedStacksTopActivitiesLocked();
         }
     }
 
diff --git a/services/core/java/com/android/server/am/NativeCrashListener.java b/services/core/java/com/android/server/am/NativeCrashListener.java
index 9348023..6e42aee 100644
--- a/services/core/java/com/android/server/am/NativeCrashListener.java
+++ b/services/core/java/com/android/server/am/NativeCrashListener.java
@@ -29,7 +29,6 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.InterruptedIOException;
-import java.net.InetSocketAddress;
 
 /**
  * Set up a Unix domain socket that debuggerd will connect() to in
@@ -226,7 +225,7 @@
                 }
                 if (pr != null) {
                     // Don't attempt crash reporting for persistent apps
-                    if (pr.persistent) {
+                    if (pr.isPersistent()) {
                         if (DEBUG) {
                             Slog.v(TAG, "Skipping report for persistent app " + pr);
                         }
@@ -260,7 +259,7 @@
                     // even though the process will vanish as soon as we let
                     // debuggerd proceed.
                     synchronized (mAm) {
-                        pr.crashing = true;
+                        pr.setCrashing(true);
                         pr.forceCrashReport = true;
                     }
 
diff --git a/services/core/java/com/android/server/am/OomAdjProfiler.java b/services/core/java/com/android/server/am/OomAdjProfiler.java
new file mode 100644
index 0000000..6230e0d
--- /dev/null
+++ b/services/core/java/com/android/server/am/OomAdjProfiler.java
@@ -0,0 +1,185 @@
+/*
+ * 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 android.os.PowerManagerInternal;
+import android.os.Process;
+import android.os.SystemClock;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.util.RingBuffer;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.io.PrintWriter;
+
+public class OomAdjProfiler {
+    @GuardedBy("this")
+    private boolean mOnBattery;
+    @GuardedBy("this")
+    private boolean mScreenOff;
+
+    @GuardedBy("this")
+    private long mOomAdjStartTimeMs;
+    @GuardedBy("this")
+    private boolean mOomAdjStarted;
+
+    @GuardedBy("this")
+    private CpuTimes mOomAdjRunTime = new CpuTimes();
+    @GuardedBy("this")
+    private CpuTimes mSystemServerCpuTime = new CpuTimes();
+
+    @GuardedBy("this")
+    private long mLastSystemServerCpuTimeMs;
+    @GuardedBy("this")
+    private boolean mSystemServerCpuTimeUpdateScheduled;
+    private final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker(false);
+
+    @GuardedBy("this")
+    final RingBuffer<CpuTimes> mOomAdjRunTimesHist = new RingBuffer<>(CpuTimes.class, 10);
+    @GuardedBy("this")
+    final RingBuffer<CpuTimes> mSystemServerCpuTimesHist = new RingBuffer<>(CpuTimes.class, 10);
+
+    void batteryPowerChanged(boolean onBattery) {
+        synchronized (this) {
+            scheduleSystemServerCpuTimeUpdate();
+            mOnBattery = onBattery;
+        }
+    }
+
+    void onWakefulnessChanged(int wakefulness) {
+        synchronized (this) {
+            scheduleSystemServerCpuTimeUpdate();
+            mScreenOff = wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE;
+        }
+    }
+
+    void oomAdjStarted() {
+        synchronized (this) {
+            mOomAdjStartTimeMs = SystemClock.currentThreadTimeMillis();
+            mOomAdjStarted = true;
+        }
+    }
+
+    void oomAdjEnded() {
+        synchronized (this) {
+            if (!mOomAdjStarted) {
+                return;
+            }
+            mOomAdjRunTime.addCpuTimeMs(SystemClock.currentThreadTimeMillis() - mOomAdjStartTimeMs);
+        }
+    }
+
+    private void scheduleSystemServerCpuTimeUpdate() {
+        synchronized (this) {
+            if (mSystemServerCpuTimeUpdateScheduled) {
+                return;
+            }
+            mSystemServerCpuTimeUpdateScheduled = true;
+            BackgroundThread.getHandler().post(PooledLambda.obtainRunnable(
+                    OomAdjProfiler::updateSystemServerCpuTime,
+                    this, mOnBattery, mScreenOff).recycleOnUse());
+        }
+    }
+
+    private void updateSystemServerCpuTime(boolean onBattery, boolean screenOff) {
+        final long cpuTimeMs = mProcessCpuTracker.getCpuTimeForPid(Process.myPid());
+        synchronized (this) {
+            mSystemServerCpuTime.addCpuTimeMs(
+                    cpuTimeMs - mLastSystemServerCpuTimeMs, onBattery, screenOff);
+            mLastSystemServerCpuTimeMs = cpuTimeMs;
+            mSystemServerCpuTimeUpdateScheduled = false;
+            notifyAll();
+        }
+    }
+
+    void reset() {
+        synchronized (this) {
+            if (mSystemServerCpuTime.isEmpty()) {
+                return;
+            }
+            mOomAdjRunTimesHist.append(mOomAdjRunTime);
+            mSystemServerCpuTimesHist.append(mSystemServerCpuTime);
+            mOomAdjRunTime = new CpuTimes();
+            mSystemServerCpuTime = new CpuTimes();
+        }
+    }
+
+    void dump(PrintWriter pw) {
+        synchronized (this) {
+            if (mSystemServerCpuTimeUpdateScheduled) {
+                while (mSystemServerCpuTimeUpdateScheduled) {
+                    try {
+                        wait();
+                    } catch (InterruptedException e) {
+                        Thread.currentThread().interrupt();
+                    }
+                }
+            } else {
+                updateSystemServerCpuTime(mOnBattery, mScreenOff);
+            }
+
+            pw.println("System server and oomAdj runtimes (ms) in recent battery sessions "
+                    + "(most recent first):");
+            if (!mSystemServerCpuTime.isEmpty()) {
+                pw.print("  ");
+                pw.print("system_server=");
+                pw.print(mSystemServerCpuTime);
+                pw.print("  ");
+                pw.print("oom_adj=");
+                pw.println(mOomAdjRunTime);
+            }
+            final CpuTimes[] systemServerCpuTimes = mSystemServerCpuTimesHist.toArray();
+            final CpuTimes[] oomAdjRunTimes = mOomAdjRunTimesHist.toArray();
+            for (int i = oomAdjRunTimes.length - 1; i >= 0; --i) {
+                pw.print("  ");
+                pw.print("system_server=");
+                pw.print(systemServerCpuTimes[i]);
+                pw.print("  ");
+                pw.print("oom_adj=");
+                pw.println(oomAdjRunTimes[i]);
+            }
+        }
+    }
+
+    private class CpuTimes {
+        private long mOnBatteryTimeMs;
+        private long mOnBatteryScreenOffTimeMs;
+
+        public void addCpuTimeMs(long cpuTimeMs) {
+            addCpuTimeMs(cpuTimeMs, mOnBattery, mScreenOff);
+        }
+
+        public void addCpuTimeMs(long cpuTimeMs, boolean onBattery, boolean screenOff) {
+            if (onBattery) {
+                mOnBatteryTimeMs += cpuTimeMs;
+                if (screenOff) {
+                    mOnBatteryScreenOffTimeMs += cpuTimeMs;
+                }
+            }
+        }
+
+        public boolean isEmpty() {
+            return mOnBatteryTimeMs == 0 && mOnBatteryScreenOffTimeMs == 0;
+        }
+
+        public String toString() {
+            return "[" + mOnBatteryTimeMs + "," + mOnBatteryScreenOffTimeMs + "]";
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index e0aa2a2..ee1166e 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -307,7 +307,7 @@
                             } else if (finalIntent.getComponent() != null) {
                                 finalIntent.getComponent().appendShortString(tag);
                             } else if (finalIntent.getData() != null) {
-                                tag.append(finalIntent.getData());
+                                tag.append(finalIntent.getData().toSafeString());
                             }
                             owner.tempWhitelistForPendingIntentLocked(callingPid,
                                     callingUid, uid, duration, tag.toString());
@@ -343,16 +343,18 @@
                                 allIntents[allIntents.length-1] = finalIntent;
                                 allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
 
-                                res = owner.getActivityStartController().startActivitiesInPackage(
+                                res = owner.mActivityTaskManager.getActivityStartController().startActivitiesInPackage(
                                         uid, key.packageName, allIntents, allResolvedTypes,
                                         resultTo, mergedOptions, userId,
-                                        false /* validateIncomingUser */);
+                                        false /* validateIncomingUser */,
+                                        this /* originatingPendingIntent */);
                             } else {
-                                res = owner.getActivityStartController().startActivityInPackage(uid,
+                                res = owner.mActivityTaskManager.getActivityStartController().startActivityInPackage(uid,
                                         callingPid, callingUid, key.packageName, finalIntent,
                                         resolvedType, resultTo, resultWho, requestCode, 0,
                                         mergedOptions, userId, null, "PendingIntentRecord",
-                                        false /* validateIncomingUser */);
+                                        false /* validateIncomingUser */,
+                                        this /* originatingPendingIntent */);
                             }
                         } catch (RuntimeException e) {
                             Slog.w(TAG, "Unable to send startActivity intent", e);
diff --git a/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java b/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java
index 77713f5..877d896 100644
--- a/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java
+++ b/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java
@@ -33,9 +33,9 @@
 
     private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
     private final Handler mHandler;
-    private final ActivityManagerService mService;
+    private final ActivityTaskManagerService mService;
 
-    PendingRemoteAnimationRegistry(ActivityManagerService service, Handler handler) {
+    PendingRemoteAnimationRegistry(ActivityTaskManagerService service, Handler handler) {
         mService = service;
         mHandler = handler;
     }
@@ -74,7 +74,7 @@
             this.packageName = packageName;
             this.adapter = adapter;
             mHandler.postDelayed(() -> {
-                synchronized (mService) {
+                synchronized (mService.mGlobalLock) {
                     final Entry entry = mEntries.get(packageName);
                     if (entry == this) {
                         mEntries.remove(packageName);
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index 2726979..edc6e53 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -100,7 +100,7 @@
         // It is guaranteed that the activities requiring the update will be in the pinned stack at
         // this point (either reparented before the animation into PiP, or before reparenting after
         // the animation out of PiP)
-        synchronized(this) {
+        synchronized (mService) {
             ArrayList<TaskRecord> tasks = getAllTasks();
             for (int i = 0; i < tasks.size(); i++ ) {
                 mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
diff --git a/services/core/java/com/android/server/am/ProcessMemInfo.java b/services/core/java/com/android/server/am/ProcessMemInfo.java
index 83d29e2..6c10a2a 100644
--- a/services/core/java/com/android/server/am/ProcessMemInfo.java
+++ b/services/core/java/com/android/server/am/ProcessMemInfo.java
@@ -24,6 +24,7 @@
     final String adjType;
     final String adjReason;
     long pss;
+    long swapPss;
     long memtrack;
 
     public ProcessMemInfo(String _name, int _pid, int _oomAdj, int _procState,
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index caf52e3..8c552b9 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -43,6 +43,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
@@ -54,18 +55,51 @@
  * Full information about a particular process that
  * is currently running.
  */
-final class ProcessRecord {
+final class ProcessRecord implements WindowProcessListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessRecord" : TAG_AM;
 
     private final ActivityManagerService mService; // where we came from
-    private final BatteryStatsImpl mBatteryStats; // where to collect runtime statistics
     final ApplicationInfo info; // all about the first app in the process
     final boolean isolated;     // true if this is a special isolated process
     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
     // List of packages running in the process
-    final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList = new ArrayMap<>();
+    final PackageList pkgList = new PackageList();
+    final class PackageList {
+        final ArrayMap<String, ProcessStats.ProcessStateHolder> mPkgList = new ArrayMap<>();
+
+        ProcessStats.ProcessStateHolder put(String key, ProcessStats.ProcessStateHolder value) {
+            mWindowProcessController.addPackage(key);
+            return mPkgList.put(key, value);
+        }
+
+        void clear() {
+            mPkgList.clear();
+            mWindowProcessController.clearPackageList();
+        }
+
+        int size() {
+            return mPkgList.size();
+        }
+
+        String keyAt(int index) {
+            return mPkgList.keyAt(index);
+        }
+
+        public ProcessStats.ProcessStateHolder valueAt(int index) {
+            return mPkgList.valueAt(index);
+        }
+
+        ProcessStats.ProcessStateHolder get(String pkgName) {
+            return mPkgList.get(pkgName);
+        }
+
+        boolean containsKey(Object key) {
+            return mPkgList.containsKey(key);
+        }
+    }
+
     final ProcessList.ProcStateMemTracker procStateMemTracker
             = new ProcessList.ProcStateMemTracker();
     UidRecord uidRecord;        // overall state of process's uid.
@@ -78,7 +112,7 @@
     int pid;                    // The process of this application; 0 if none
     String procStatFile;        // path to /proc/<pid>/stat
     int[] gids;                 // The gids this process was launched with
-    String requiredAbi;         // The ABI this process was launched with
+    private String mRequiredAbi;// The ABI this process was launched with
     String instructionSet;      // The instruction set this process was launched with
     boolean starting;           // True if the process is being started
     long lastActivityTime;      // For managing the LRU list
@@ -96,12 +130,11 @@
     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
-    int curSchedGroup;          // Currently desired scheduling class
+    private int mCurSchedGroup; // Currently desired scheduling class
     int setSchedGroup;          // Last set to background scheduling class
-    int vrThreadTid;            // Thread currently set for VR scheduling
     int trimMemoryLevel;        // Last selected memory trimming level
     int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
-    int repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
+    private int mRepProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
     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
@@ -112,7 +145,7 @@
     boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
     boolean hasClientActivities;  // Are there any client services with activities?
     boolean hasStartedServices; // Are there any started services running in this process?
-    boolean foregroundServices; // Running any services that are foreground?
+    private boolean mHasForegroundServices; // Running any services that are foreground?
     boolean foregroundActivities; // Running any activities that are foreground?
     boolean repForegroundActivities; // Last reported foreground activities.
     boolean systemNoUi;         // This is a system process, but not currently showing UI.
@@ -154,8 +187,9 @@
     int lruSeq;                 // Sequence id for identifying LRU update cycles
     CompatibilityInfo compat;   // last used compatibility mode
     IBinder.DeathRecipient deathRecipient; // Who is watching for the death.
-    ActiveInstrumentation instr;// Set to currently active instrumentation running in process
-    boolean usingWrapper;       // Set to true when process was launched with a wrapper attached
+    private ActiveInstrumentation mInstr; // Set to currently active instrumentation running in
+                                          // process.
+    private boolean mUsingWrapper; // Set to true when process was launched with a wrapper attached
     final ArraySet<BroadcastRecord> curReceivers = new ArraySet<BroadcastRecord>();// receivers currently running in the app
     long whenUnimportant;       // When (uptime) the process last became unimportant
     long lastCpuTime;           // How long proc has run CPU at last check
@@ -173,10 +207,8 @@
     Object adjTarget;           // Debugging: target component impacting oom_adj.
     Runnable crashHandler;      // Optional local handler to be invoked in the process crash.
 
-    // all activities running in the process
-    final ArrayList<ActivityRecord> activities = new ArrayList<>();
-    // any tasks this process had run root activities in
-    final ArrayList<TaskRecord> recentTasks = new ArrayList<>();
+    // Controller for driving the process state on the window manager side.
+    final private WindowProcessController mWindowProcessController;
     // all ServiceRecord running in this process
     final ArraySet<ServiceRecord> services = new ArraySet<>();
     // services that are currently executing code (need to remain foreground).
@@ -194,14 +226,14 @@
     String[] isolatedEntryPointArgs; // Arguments to pass to isolatedEntryPoint's main().
 
     boolean execServicesFg;     // do we need to be executing services in the foreground?
-    boolean persistent;         // always keep this application running?
-    boolean crashing;           // are we in the process of crashing?
+    private boolean mPersistent;// always keep this application running?
+    private boolean mCrashing;  // are we in the process of crashing?
     Dialog crashDialog;         // dialog being displayed due to crash.
     boolean forceCrashReport;   // suppress normal auto-dismiss of crash dialog & report UI?
-    boolean notResponding;      // does the app have a not responding dialog?
+    private boolean mNotResponding; // does the app have a not responding dialog?
     Dialog anrDialog;           // dialog being displayed due to app not resp.
     boolean removed;            // has app package been removed from device?
-    boolean debugging;          // was app launched for debugging?
+    private boolean mDebugging; // was app launched for debugging?
     boolean waitedForDebugger;  // has process show wait for debugger dialog?
     Dialog waitDialog;          // current wait for debugger dialog
 
@@ -259,7 +291,7 @@
             }
         }
         pw.println("}");
-        pw.print(prefix); pw.print("requiredAbi="); pw.print(requiredAbi);
+        pw.print(prefix); pw.print("mRequiredAbi="); pw.print(mRequiredAbi);
                 pw.print(" instructionSet="); pw.println(instructionSet);
         if (info.className != null) {
             pw.print(prefix); pw.print("class="); pw.println(info.className);
@@ -286,8 +318,8 @@
             pw.println("}");
         }
         pw.print(prefix); pw.print("compat="); pw.println(compat);
-        if (instr != null) {
-            pw.print(prefix); pw.print("instr="); pw.println(instr);
+        if (mInstr != null) {
+            pw.print(prefix); pw.print("mInstr="); pw.println(mInstr);
         }
         pw.print(prefix); pw.print("thread="); pw.println(thread);
         pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
@@ -324,15 +356,12 @@
                 pw.print(" setRaw="); pw.print(setRawAdj);
                 pw.print(" cur="); pw.print(curAdj);
                 pw.print(" set="); pw.println(setAdj);
-        pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup);
+        pw.print(prefix); pw.print("mCurSchedGroup="); pw.print(mCurSchedGroup);
                 pw.print(" setSchedGroup="); pw.print(setSchedGroup);
                 pw.print(" systemNoUi="); pw.print(systemNoUi);
                 pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
-        if (vrThreadTid != 0) {
-            pw.print(prefix); pw.print("vrThreadTid="); pw.println(vrThreadTid);
-        }
         pw.print(prefix); pw.print("curProcState="); pw.print(curProcState);
-                pw.print(" repProcState="); pw.print(repProcState);
+                pw.print(" mRepProcState="); pw.print(mRepProcState);
                 pw.print(" pssProcState="); pw.print(pssProcState);
                 pw.print(" setProcState="); pw.print(setProcState);
                 pw.print(" lastStateTime=");
@@ -349,8 +378,8 @@
                     pw.print(" hasOverlayUi="); pw.print(hasOverlayUi);
                     pw.print(" runningRemoteAnimation="); pw.println(runningRemoteAnimation);
         }
-        if (foregroundServices || forcingToImportant != null) {
-            pw.print(prefix); pw.print("foregroundServices="); pw.print(foregroundServices);
+        if (mHasForegroundServices || forcingToImportant != null) {
+            pw.print(prefix); pw.print("mHasForegroundServices="); pw.print(mHasForegroundServices);
                     pw.print(" forcingToImportant="); pw.println(forcingToImportant);
         }
         if (reportedInteraction || fgInteractionTime != 0) {
@@ -366,8 +395,8 @@
             }
             pw.println();
         }
-        if (persistent || removed) {
-            pw.print(prefix); pw.print("persistent="); pw.print(persistent);
+        if (mPersistent || removed) {
+            pw.print(prefix); pw.print("persistent="); pw.print(mPersistent);
                     pw.print(" removed="); pw.println(removed);
         }
         if (hasClientActivities || foregroundActivities || repForegroundActivities) {
@@ -407,16 +436,16 @@
                     pw.print(" killedByAm="); pw.print(killedByAm);
                     pw.print(" waitingToKill="); pw.println(waitingToKill);
         }
-        if (debugging || crashing || crashDialog != null || notResponding
+        if (mDebugging || mCrashing || crashDialog != null || mNotResponding
                 || anrDialog != null || bad) {
-            pw.print(prefix); pw.print("debugging="); pw.print(debugging);
-                    pw.print(" crashing="); pw.print(crashing);
+            pw.print(prefix); pw.print("mDebugging="); pw.print(mDebugging);
+                    pw.print(" mCrashing="); pw.print(mCrashing);
                     pw.print(" "); pw.print(crashDialog);
-                    pw.print(" notResponding="); pw.print(notResponding);
+                    pw.print(" mNotResponding="); pw.print(mNotResponding);
                     pw.print(" " ); pw.print(anrDialog);
                     pw.print(" bad="); pw.print(bad);
 
-                    // crashing or notResponding is always set before errorReportReceiver
+                    // mCrashing or mNotResponding is always set before errorReportReceiver
                     if (errorReportReceiver != null) {
                         pw.print(" errorReportReceiver=");
                         pw.print(errorReportReceiver.flattenToShortString());
@@ -431,18 +460,7 @@
             pw.print(prefix); pw.print("isolatedEntryPointArgs=");
             pw.println(Arrays.toString(isolatedEntryPointArgs));
         }
-        if (activities.size() > 0) {
-            pw.print(prefix); pw.println("Activities:");
-            for (int i=0; i<activities.size(); i++) {
-                pw.print(prefix); pw.print("  - "); pw.println(activities.get(i));
-            }
-        }
-        if (recentTasks.size() > 0) {
-            pw.print(prefix); pw.println("Recent Tasks:");
-            for (int i=0; i<recentTasks.size(); i++) {
-                pw.print(prefix); pw.print("  - "); pw.println(recentTasks.get(i));
-            }
-        }
+        mWindowProcessController.dump(pw, prefix);
         if (services.size() > 0) {
             pw.print(prefix); pw.println("Services:");
             for (int i=0; i<services.size(); i++) {
@@ -489,26 +507,28 @@
         }
     }
 
-    ProcessRecord(ActivityManagerService _service, BatteryStatsImpl _batteryStats,
-            ApplicationInfo _info, String _processName, int _uid) {
+    ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName,
+            int _uid) {
         mService = _service;
-        mBatteryStats = _batteryStats;
         info = _info;
         isolated = _info.uid != _uid;
         uid = _uid;
         userId = UserHandle.getUserId(_uid);
         processName = _processName;
-        pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
         maxAdj = ProcessList.UNKNOWN_ADJ;
         curRawAdj = setRawAdj = ProcessList.INVALID_ADJ;
         curAdj = setAdj = verifiedAdj = ProcessList.INVALID_ADJ;
-        persistent = false;
+        mPersistent = false;
         removed = false;
         lastStateTime = lastPssTime = nextPssTime = SystemClock.uptimeMillis();
+        mWindowProcessController = new WindowProcessController(
+                mService.mActivityTaskManager, info, processName, uid, userId, this, this);
+        pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
     }
 
     public void setPid(int _pid) {
         pid = _pid;
+        mWindowProcessController.setPid(pid);
         procStatFile = null;
         shortStringName = null;
         stringName = null;
@@ -519,7 +539,13 @@
             final ProcessState origBase = baseProcessTracker;
             if (origBase != null) {
                 origBase.setState(ProcessStats.STATE_NOTHING,
-                        tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList);
+                        tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
+                for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                    StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+                            uid, processName, pkgList.keyAt(ipkg),
+                            ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+                            pkgList.valueAt(ipkg).appVersion);
+                }
                 origBase.makeInactive();
             }
             baseProcessTracker = tracker.getProcessStateLocked(info.packageName, uid,
@@ -530,7 +556,7 @@
                 if (holder.state != null && holder.state != origBase) {
                     holder.state.makeInactive();
                 }
-                holder.state = tracker.getProcessStateLocked(pkgList.keyAt(i), uid,
+                tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), uid,
                         info.longVersionCode, processName);
                 if (holder.state != baseProcessTracker) {
                     holder.state.makeActive();
@@ -538,15 +564,23 @@
             }
         }
         thread = _thread;
+        mWindowProcessController.setThread(thread);
     }
 
     public void makeInactive(ProcessStatsService tracker) {
         thread = null;
+        mWindowProcessController.setThread(null);
         final ProcessState origBase = baseProcessTracker;
         if (origBase != null) {
             if (origBase != null) {
                 origBase.setState(ProcessStats.STATE_NOTHING,
-                        tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList);
+                        tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
+                for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                    StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+                            uid, processName, pkgList.keyAt(ipkg),
+                            ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+                            pkgList.valueAt(ipkg).appVersion);
+                }
                 origBase.makeInactive();
             }
             baseProcessTracker = null;
@@ -555,16 +589,30 @@
                 if (holder.state != null && holder.state != origBase) {
                     holder.state.makeInactive();
                 }
+                holder.pkg = null;
                 holder.state = null;
             }
         }
     }
 
-    public void clearRecentTasks() {
-        for (int i = recentTasks.size() - 1; i >= 0; i--) {
-            recentTasks.get(i).clearRootProcess();
-        }
-        recentTasks.clear();
+    boolean hasActivities() {
+        return mWindowProcessController.hasActivities();
+    }
+
+    void clearActivities() {
+        mWindowProcessController.clearActivities();
+    }
+
+    boolean hasActivitiesOrRecentTasks() {
+        return mWindowProcessController.hasActivitiesOrRecentTasks();
+    }
+
+    boolean hasRecentTasks() {
+        return mWindowProcessController.hasRecentTasks();
+    }
+
+    void clearRecentTasks() {
+        mWindowProcessController.clearRecentTasks();
     }
 
     /**
@@ -572,12 +620,8 @@
      * to the user. See HistoryRecord.isInterestingToUserLocked()
      */
     public boolean isInterestingToUserLocked() {
-        final int size = activities.size();
-        for (int i = 0 ; i < size ; i++) {
-            ActivityRecord r = activities.get(i);
-            if (r.isInterestingToUserLocked()) {
-                return true;
-            }
+        if (mWindowProcessController.isInterestingToUser()) {
+            return true;
         }
 
         final int servicesSize = services.size();
@@ -590,14 +634,6 @@
         return false;
     }
 
-    public void stopFreezingAllLocked() {
-        int i = activities.size();
-        while (i > 0) {
-            i--;
-            activities.get(i).stopFreezingScreenLocked(true);
-        }
-    }
-
     public void unlinkDeathRecipient() {
         if (deathRecipient != null && thread != null) {
             thread.asBinder().unlinkToDeath(deathRecipient, 0);
@@ -676,7 +712,7 @@
             } else {
                 pendingStart = false;
             }
-            if (!persistent) {
+            if (!mPersistent) {
                 killed = true;
                 killedByAm = true;
             }
@@ -697,7 +733,7 @@
                 proto.write(ProcessRecordProto.ISOLATED_APP_ID, UserHandle.getAppId(uid));
             }
         }
-        proto.write(ProcessRecordProto.PERSISTENT, persistent);
+        proto.write(ProcessRecordProto.PERSISTENT, mPersistent);
         proto.end(token);
     }
 
@@ -782,8 +818,7 @@
             ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
                     versionCode);
             if (baseProcessTracker != null) {
-                holder.state = tracker.getProcessStateLocked(
-                        pkg, uid, versionCode, processName);
+                tracker.updateProcessStateHolderLocked(holder, pkg, uid, versionCode, processName);
                 pkgList.put(pkg, holder);
                 if (holder.state != baseProcessTracker) {
                     holder.state.makeActive();
@@ -806,8 +841,14 @@
     }
 
     public void forceProcessStateUpTo(int newState) {
-        if (repProcState > newState) {
-            curProcState = repProcState = newState;
+        if (mRepProcState > newState) {
+            curProcState = mRepProcState = newState;
+            for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+                        uid, processName, pkgList.keyAt(ipkg),
+                        ActivityManager.processStateAmToProto(mRepProcState),
+                        pkgList.valueAt(ipkg).appVersion);
+            }
         }
     }
 
@@ -819,7 +860,13 @@
         if (baseProcessTracker != null) {
             long now = SystemClock.uptimeMillis();
             baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
-                    tracker.getMemFactorLocked(), now, pkgList);
+                    tracker.getMemFactorLocked(), now, pkgList.mPkgList);
+            for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+                        uid, processName, pkgList.keyAt(ipkg),
+                        ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+                        pkgList.valueAt(ipkg).appVersion);
+            }
             if (N != 1) {
                 for (int i=0; i<N; i++) {
                     ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
@@ -829,14 +876,13 @@
 
                 }
                 pkgList.clear();
-                ProcessState ps = tracker.getProcessStateLocked(
-                        info.packageName, uid, info.longVersionCode, processName);
                 ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
                         info.longVersionCode);
-                holder.state = ps;
+                tracker.updateProcessStateHolderLocked(holder, info.packageName, uid,
+                        info.longVersionCode, processName);
                 pkgList.put(info.packageName, holder);
-                if (ps != baseProcessTracker) {
-                    ps.makeActive();
+                if (holder.state != baseProcessTracker) {
+                    holder.state.makeActive();
                 }
             }
         } else if (N != 1) {
@@ -856,4 +902,168 @@
         }
         return list;
     }
+
+    WindowProcessController getWindowProcessController() {
+        return mWindowProcessController;
+    }
+
+    void setCurrentSchedulingGroup(int curSchedGroup) {
+        mCurSchedGroup = curSchedGroup;
+        mWindowProcessController.setCurrentSchedulingGroup(curSchedGroup);
+    }
+
+    int getCurrentSchedulingGroup() {
+        return mCurSchedGroup;
+    }
+
+    void setReportedProcState(int repProcState) {
+        mRepProcState = repProcState;
+        for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+            StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+                    uid, processName, pkgList.keyAt(ipkg),
+                    ActivityManager.processStateAmToProto(mRepProcState),
+                    pkgList.valueAt(ipkg).appVersion);
+        }
+        mWindowProcessController.setReportedProcState(repProcState);
+    }
+
+    int getReportedProcState() {
+        return mRepProcState;
+    }
+
+    void setCrashing(boolean crashing) {
+        mCrashing = crashing;
+        mWindowProcessController.setCrashing(crashing);
+    }
+
+    boolean isCrashing() {
+        return mCrashing;
+    }
+
+    void setNotResponding(boolean notResponding) {
+        mNotResponding = notResponding;
+        mWindowProcessController.setNotResponding(notResponding);
+    }
+
+    boolean isNotResponding() {
+        return mNotResponding;
+    }
+
+    void setPersistent(boolean persistent) {
+        mPersistent = persistent;
+        mWindowProcessController.setPersistent(persistent);
+    }
+
+    boolean isPersistent() {
+        return mPersistent;
+    }
+
+    public void setRequiredAbi(String requiredAbi) {
+        mRequiredAbi = requiredAbi;
+        mWindowProcessController.setRequiredAbi(requiredAbi);
+    }
+
+    String getRequiredAbi() {
+        return mRequiredAbi;
+    }
+
+    void setHasForegroundServices(boolean hasForegroundServices) {
+        mHasForegroundServices = hasForegroundServices;
+        mWindowProcessController.setHasForegroundServices(hasForegroundServices);
+    }
+
+    boolean hasForegroundServices() {
+        return mHasForegroundServices;
+    }
+
+    void setDebugging(boolean debugging) {
+        mDebugging = debugging;
+        mWindowProcessController.setDebugging(debugging);
+    }
+
+    boolean isDebugging() {
+        return mDebugging;
+    }
+
+    void setUsingWrapper(boolean usingWrapper) {
+        mUsingWrapper = usingWrapper;
+        mWindowProcessController.setUsingWrapper(usingWrapper);
+    }
+
+    boolean isUsingWrapper() {
+        return mUsingWrapper;
+    }
+
+    void setActiveInstrumentation(ActiveInstrumentation instr) {
+        mInstr = instr;
+        mWindowProcessController.setInstrumenting(instr != null);
+    }
+
+    ActiveInstrumentation getActiveInstrumentation() {
+        return mInstr;
+    }
+
+    @Override
+    public void clearProfilerIfNeeded() {
+        synchronized (mService) {
+            if (mService.mProfileProc == null || mService.mProfilerInfo == null
+                    || mService.mProfileProc != this) {
+                return;
+            }
+            mService.clearProfilerLocked();
+        }
+    }
+
+    @Override
+    public void updateServiceConnectionActivities() {
+        synchronized (mService) {
+            mService.mServices.updateServiceConnectionActivitiesLocked(this);
+        }
+    }
+
+    @Override
+    public void setPendingUiClean(boolean pendingUiClean) {
+        synchronized (mService) {
+            this.pendingUiClean = true;
+        }
+    }
+
+    @Override
+    public void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
+        synchronized (mService) {
+            pendingUiClean = true;
+            forceProcessStateUpTo(newState);
+        }
+    }
+
+    @Override
+    public void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
+            boolean activityChange, boolean updateOomAdj) {
+        synchronized (mService) {
+            if (updateServiceConnectionActivities) {
+                mService.mServices.updateServiceConnectionActivitiesLocked(this);
+            }
+            if (updateLru) {
+                mService.updateLruProcessLocked(this, activityChange, null);
+            }
+            if (updateOomAdj) {
+                mService.updateOomAdjLocked();
+            }
+        }
+    }
+
+    @Override
+    public void setRemoved(boolean removed) {
+        synchronized (mService) {
+            this.removed = removed;
+        }
+    }
+
+    /**
+     * Returns the total time (in milliseconds) spent executing in both user and system code.
+     * Safe to call without lock held.
+     */
+    public long getCpuTime() {
+        return mService.mProcessCpuTracker.getCpuTimeForPid(pid);
+    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index c10d81b..8ce650c 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -16,14 +16,12 @@
 
 package com.android.server.am;
 
-import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.service.procstats.ProcessStatsProto;
 import android.service.procstats.ProcessStatsServiceDumpProto;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
@@ -120,11 +118,20 @@
         }
     }
 
+    @GuardedBy("mAm")
+    public void updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder,
+            String packageName, int uid, long versionCode, String processName) {
+        holder.pkg = mProcessStats.getPackageStateLocked(packageName, uid, versionCode);
+        holder.state = mProcessStats.getProcessStateLocked(holder.pkg, processName);
+    }
+
+    @GuardedBy("mAm")
     public ProcessState getProcessStateLocked(String packageName,
             int uid, long versionCode, String processName) {
         return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName);
     }
 
+    @GuardedBy("mAm")
     public ServiceState getServiceStateLocked(String packageName, int uid,
             long versionCode, String processName, String className) {
         return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
@@ -174,15 +181,23 @@
         return false;
     }
 
+    @GuardedBy("mAm")
     public int getMemFactorLocked() {
         return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0;
     }
 
+    @GuardedBy("mAm")
     public void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem,
             long nativeMem) {
         mProcessStats.addSysMemUsage(cachedMem, freeMem, zramMem, kernelMem, nativeMem);
     }
 
+    @GuardedBy("mAm")
+    public void updateTrackingAssociationsLocked(int curSeq, long now) {
+        mProcessStats.updateTrackingAssociationsLocked(curSeq, now);
+    }
+
+    @GuardedBy("mAm")
     public boolean shouldWriteNowLocked(long now) {
         if (now > (mLastWriteTime+WRITE_PERIOD)) {
             if (SystemClock.elapsedRealtime()
@@ -196,6 +211,7 @@
         return false;
     }
 
+    @GuardedBy("mAm")
     public void shutdownLocked() {
         Slog.w(TAG, "Writing process stats before shutdown...");
         mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN;
@@ -203,14 +219,17 @@
         mShuttingDown = true;
     }
 
+    @GuardedBy("mAm")
     public void writeStateAsyncLocked() {
         writeStateLocked(false);
     }
 
+    @GuardedBy("mAm")
     public void writeStateSyncLocked() {
         writeStateLocked(true);
     }
 
+    @GuardedBy("mAm")
     private void writeStateLocked(boolean sync) {
         if (mShuttingDown) {
             return;
@@ -220,6 +239,7 @@
         writeStateLocked(sync, commitPending);
     }
 
+    @GuardedBy("mAm")
     public void writeStateLocked(boolean sync, final boolean commit) {
         final long totalTime;
         synchronized (mPendingWriteLock) {
@@ -298,6 +318,7 @@
         }
     }
 
+    @GuardedBy("mAm")
     boolean readLocked(ProcessStats stats, AtomicFile file) {
         try {
             FileInputStream stream = file.openRead();
@@ -342,6 +363,13 @@
                                             + ": " + pkgState.mServices.valueAt(isvc));
 
                                 }
+                                final int NASCS = pkgState.mAssociations.size();
+                                for (int iasc=0; iasc<NASCS; iasc++) {
+                                    Slog.w(TAG, "      Association "
+                                            + pkgState.mServices.keyAt(iasc)
+                                            + ": " + pkgState.mAssociations.valueAt(iasc));
+
+                                }
                             }
                         }
                     }
@@ -383,6 +411,7 @@
         return filesArray;
     }
 
+    @GuardedBy("mAm")
     public void trimHistoricStatesWriteLocked() {
         ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, false, true);
         if (filesArray == null) {
@@ -395,6 +424,7 @@
         }
     }
 
+    @GuardedBy("mAm")
     boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
             boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
             boolean sepProcStates, int[] procStates, long now, String reqPackage) {
@@ -582,7 +612,8 @@
             stats.dumpCheckinLocked(pw, reqPackage);
         } else {
             if (dumpDetails || dumpFullDetails) {
-                stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, activeOnly);
+                stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails, dumpAll,
+                        activeOnly);
             } else {
                 stats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
             }
@@ -944,8 +975,8 @@
                 if (checkedIn) pw.print(" (checked in)");
                 pw.println(":");
                 if (dumpDetails || dumpFullDetails) {
-                    processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll,
-                            activeOnly);
+                    processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
+                            dumpAll, activeOnly);
                     if (dumpAll) {
                         pw.print("  mFile="); pw.println(mFile.getBaseFile());
                     }
@@ -1000,7 +1031,7 @@
                                 // much crud.
                                 if (dumpFullDetails) {
                                     processStats.dumpLocked(pw, reqPackage, now, false, false,
-                                            activeOnly);
+                                            false, activeOnly);
                                 } else {
                                     processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
                                 }
@@ -1030,8 +1061,8 @@
                     }
                     pw.println("CURRENT STATS:");
                     if (dumpDetails || dumpFullDetails) {
-                        mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll,
-                                activeOnly);
+                        mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
+                                dumpAll, activeOnly);
                         if (dumpAll) {
                             pw.print("  mFile="); pw.println(mFile.getBaseFile());
                         }
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index 2f52002..29c1657 100644
--- a/services/core/java/com/android/server/am/ProviderMap.java
+++ b/services/core/java/com/android/server/am/ProviderMap.java
@@ -32,7 +32,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -203,7 +202,7 @@
                         && (filterByClasses == null
                             || filterByClasses.contains(provider.name.getClassName())));
             if (sameComponent
-                    && (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
+                    && (provider.proc == null || evenPersistent || !provider.proc.isPersistent())) {
                 if (!doit) {
                     return true;
                 }
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 06a3078..e11e003 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -26,10 +26,12 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
+import static android.os.Process.SYSTEM_UID;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
@@ -41,6 +43,7 @@
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -77,6 +80,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
@@ -102,7 +106,6 @@
     private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_AM;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
     private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
-    private static final boolean TRIMMED = true;
 
     private static final int DEFAULT_INITIAL_CAPACITY = 5;
 
@@ -130,15 +133,15 @@
         /**
          * Called when a task is removed from the recent tasks list.
          */
-        void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed);
+        void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess);
     }
 
     /**
      * Save recent tasks information across reboots.
      */
     private final TaskPersister mTaskPersister;
-    private final ActivityManagerService mService;
-    private final UserController mUserController;
+    private final ActivityTaskManagerService mService;
+    private final ActivityStackSupervisor mSupervisor;
 
     /**
      * Keeps track of the static recents package/component which is granted additional permissions
@@ -179,22 +182,21 @@
     private final TaskActivitiesReport mTmpReport = new TaskActivitiesReport();
 
     @VisibleForTesting
-    RecentTasks(ActivityManagerService service, TaskPersister taskPersister,
-            UserController userController) {
+    RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) {
         mService = service;
-        mUserController = userController;
+        mSupervisor = mService.mStackSupervisor;
         mTaskPersister = taskPersister;
-        mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic();
+        mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
         mHasVisibleRecentTasks = true;
     }
 
-    RecentTasks(ActivityManagerService service, ActivityStackSupervisor stackSupervisor) {
+    RecentTasks(ActivityTaskManagerService service, ActivityStackSupervisor stackSupervisor) {
         final File systemDir = Environment.getDataSystemDirectory();
         final Resources res = service.mContext.getResources();
         mService = service;
-        mUserController = service.mUserController;
+        mSupervisor = mService.mStackSupervisor;
         mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this);
-        mGlobalMaxNumTasks = ActivityManager.getMaxRecentTasksStatic();
+        mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
         mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
         loadParametersFromResources(res);
     }
@@ -319,9 +321,9 @@
         }
     }
 
-    private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed) {
+    private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
         for (int i = 0; i < mCallbacks.size(); i++) {
-            mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed);
+            mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed, killProcess);
         }
     }
 
@@ -441,7 +443,7 @@
     }
 
     void flush() {
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             syncPersistentTaskIdsLocked();
         }
         mTaskPersister.flush();
@@ -496,7 +498,7 @@
             if (tr.userId == userId) {
                 if(DEBUG_TASKS) Slog.i(TAG_TASKS,
                         "remove RecentTask " + tr + " when finishing user" + userId);
-                remove(mTasks.get(i));
+                remove(tr);
             }
         }
     }
@@ -511,7 +513,7 @@
                     && tr.realActivitySuspended != suspended) {
                tr.realActivitySuspended = suspended;
                if (suspended) {
-                   mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
+                   mSupervisor.removeTaskByIdLocked(tr.taskId, false,
                            REMOVE_FROM_RECENTS, "suspended-package");
                }
                notifyTaskPersisterLocked(tr, false);
@@ -539,11 +541,21 @@
             if (tr.userId != userId) continue;
             if (!taskPackageName.equals(packageName)) continue;
 
-            mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS,
+            mSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS,
                     "remove-package-task");
         }
     }
 
+    void removeAllVisibleTasks() {
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final TaskRecord tr = mTasks.get(i);
+            if (isVisibleRecentTask(tr)) {
+                mTasks.remove(i);
+                notifyTaskRemoved(tr, true /* wasTrimmed */, true /* killProcess */);
+            }
+        }
+    }
+
     void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses,
             int userId) {
         for (int i = mTasks.size() - 1; i >= 0; --i) {
@@ -556,7 +568,7 @@
             final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
                     && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
             if (sameComponent) {
-                mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
+                mSupervisor.removeTaskByIdLocked(tr.taskId, false,
                         REMOVE_FROM_RECENTS, "disabled-package");
             }
         }
@@ -588,8 +600,7 @@
             }
             if (task.autoRemoveRecents && task.getTopActivity() == null) {
                 // This situation is broken, and we should just get rid of it now.
-                mTasks.remove(i);
-                notifyTaskRemoved(task, !TRIMMED);
+                remove(task);
                 Slog.w(TAG, "Removing auto-remove without activity: " + task);
                 continue;
             }
@@ -635,8 +646,7 @@
                     if (app == NO_APPLICATION_INFO_TOKEN
                             || (app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
                         // Doesn't exist any more! Good-bye.
-                        mTasks.remove(i);
-                        notifyTaskRemoved(task, !TRIMMED);
+                        remove(task);
                         Slog.w(TAG, "Removing no longer valid recent: " + task);
                         continue;
                     } else {
@@ -701,27 +711,57 @@
             if (intent == null || !callingPackage.equals(intent.getComponent().getPackageName())) {
                 continue;
             }
-            ActivityManager.RecentTaskInfo taskInfo = createRecentTaskInfo(tr);
-            AppTaskImpl taskImpl = new AppTaskImpl(mService, taskInfo.persistentId, callingUid);
+            AppTaskImpl taskImpl = new AppTaskImpl(mService, tr.taskId, callingUid);
             list.add(taskImpl.asBinder());
         }
         return list;
     }
 
+    @VisibleForTesting
+    Set<Integer> getProfileIds(int userId) {
+        Set<Integer> userIds = new ArraySet<>();
+        final List<UserInfo> profiles = mService.getUserManager().getProfiles(userId,
+                false /* enabledOnly */);
+        for (int i = profiles.size() - 1; i >= 0; --i) {
+            userIds.add(profiles.get(i).id);
+        }
+        return userIds;
+    }
+
+    @VisibleForTesting
+    UserInfo getUserInfo(int userId) {
+        return mService.getUserManager().getUserInfo(userId);
+    }
+
+    @VisibleForTesting
+    int[] getCurrentProfileIds() {
+        return mService.mAmInternal.getCurrentProfileIds();
+    }
+
     /**
      * @return the list of recent tasks for presentation.
      */
     ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
             boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) {
+        return new ParceledListSlice<>(getRecentTasksImpl(maxNum, flags, getTasksAllowed,
+                getDetailedTasks, userId, callingUid));
+    }
+
+
+    /**
+     * @return the list of recent tasks for presentation.
+     */
+    ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags,
+            boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) {
         final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;
 
-        if (!mService.isUserRunning(userId, FLAG_AND_UNLOCKED)) {
+        if (!mService.mAm.isUserRunning(userId, FLAG_AND_UNLOCKED)) {
             Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
-            return ParceledListSlice.emptyList();
+            return new ArrayList<>();
         }
         loadUserRecentsLocked(userId);
 
-        final Set<Integer> includedUsers = mUserController.getProfileIds(userId);
+        final Set<Integer> includedUsers = getProfileIds(userId);
         includedUsers.add(Integer.valueOf(userId));
 
         final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>();
@@ -768,7 +808,7 @@
             if (i == 0
                     || withExcluded
                     || (tr.intent == null)
-                    || ((tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                    || ((tr.intent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
                     == 0)) {
                 if (!getTasksAllowed) {
                     // If the caller doesn't have the GET_TASKS permission, then only
@@ -805,7 +845,7 @@
                 res.add(rti);
             }
         }
-        return new ParceledListSlice<>(res);
+        return res;
     }
 
     /**
@@ -1017,7 +1057,7 @@
      */
     void remove(TaskRecord task) {
         mTasks.remove(task);
-        notifyTaskRemoved(task, !TRIMMED);
+        notifyTaskRemoved(task, false /* wasTrimmed */, false /* killProcess */);
     }
 
     /**
@@ -1029,17 +1069,17 @@
         // Remove from the end of the list until we reach the max number of recents
         while (recentsCount > mGlobalMaxNumTasks) {
             final TaskRecord tr = mTasks.remove(recentsCount - 1);
-            notifyTaskRemoved(tr, TRIMMED);
+            notifyTaskRemoved(tr, true /* wasTrimmed */, false /* killProcess */);
             recentsCount--;
             if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming over max-recents task=" + tr
                     + " max=" + mGlobalMaxNumTasks);
         }
 
         // Remove any tasks that belong to currently quiet profiles
-        final int[] profileUserIds = mUserController.getCurrentProfileIds();
+        final int[] profileUserIds = getCurrentProfileIds();
         mTmpQuietProfileUserIds.clear();
         for (int userId : profileUserIds) {
-            final UserInfo userInfo = mUserController.getUserInfo(userId);
+            final UserInfo userInfo = getUserInfo(userId);
             if (userInfo != null && userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) {
                 mTmpQuietProfileUserIds.put(userId, true);
             }
@@ -1083,7 +1123,7 @@
 
             // Task is no longer active, trim it from the list
             mTasks.remove(task);
-            notifyTaskRemoved(task, TRIMMED);
+            notifyTaskRemoved(task, true /* wasTrimmed */, false /* killProcess */);
             notifyTaskPersisterLocked(task, false /* flush */);
         }
     }
@@ -1137,9 +1177,8 @@
             case ACTIVITY_TYPE_ASSISTANT:
                 // Ignore assistant that chose to be excluded from Recents, even if it's a top
                 // task.
-                if ((task.getBaseIntent().getFlags()
-                        & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
-                        == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) {
+                if ((task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                        == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) {
                     return false;
                 }
         }
@@ -1171,8 +1210,8 @@
     private boolean isInVisibleRange(TaskRecord task, int numVisibleTasks) {
         // Keep the last most task even if it is excluded from recents
         final boolean isExcludeFromRecents =
-                (task.getBaseIntent().getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
-                        == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+                (task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                        == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
         if (isExcludeFromRecents) {
             if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\texcludeFromRecents=true");
             return numVisibleTasks == 1;
@@ -1205,7 +1244,7 @@
      */
     protected boolean isTrimmable(TaskRecord task) {
         final ActivityStack stack = task.getStack();
-        final ActivityStack homeStack = mService.mStackSupervisor.mHomeStack;
+        final ActivityStack homeStack = mSupervisor.mHomeStack;
 
         // No stack for task, just trim it
         if (stack == null) {
@@ -1238,7 +1277,7 @@
         // callbacks here.
         final TaskRecord removedTask = mTasks.remove(removeIndex);
         if (removedTask != task) {
-            notifyTaskRemoved(removedTask, !TRIMMED);
+            notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */);
             if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask
                     + " for addition of task=" + task);
         }
@@ -1490,6 +1529,7 @@
             return;
         }
 
+        // Dump raw recent task list
         boolean printedAnything = false;
         boolean printedHeader = false;
         final int size = mTasks.size();
@@ -1512,6 +1552,30 @@
             }
         }
 
+        // Dump visible recent task list
+        if (mHasVisibleRecentTasks) {
+            // Reset the header flag for the next block
+            printedHeader = false;
+            ArrayList<ActivityManager.RecentTaskInfo> tasks = getRecentTasksImpl(Integer.MAX_VALUE,
+                    0, true /* getTasksAllowed */, false /* getDetailedTasks */,
+                    mService.getCurrentUserId(), SYSTEM_UID);
+            for (int i = 0; i < tasks.size(); i++) {
+                final ActivityManager.RecentTaskInfo taskInfo = tasks.get(i);
+                if (!printedHeader) {
+                    if (printedAnything) {
+                        // Separate from the last block if it printed
+                        pw.println();
+                    }
+                    pw.println("  Visible recent tasks (most recent first):");
+                    printedHeader = true;
+                    printedAnything = true;
+                }
+
+                pw.print("  * RecentTaskInfo #"); pw.print(i); pw.print(": ");
+                taskInfo.dump(pw, "    ");
+            }
+        }
+
         if (!printedAnything) {
             pw.println("  (nothing)");
         }
@@ -1521,33 +1585,11 @@
      * Creates a new RecentTaskInfo from a TaskRecord.
      */
     ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) {
-        // Compose the recent task info
         ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
-        rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId;
-        rti.persistentId = tr.taskId;
-        rti.baseIntent = new Intent(tr.getBaseIntent());
-        rti.origActivity = tr.origActivity;
-        rti.realActivity = tr.realActivity;
-        rti.description = tr.lastDescription;
-        rti.stackId = tr.getStackId();
-        rti.userId = tr.userId;
-        rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
-        rti.lastActiveTime = tr.lastActiveTime;
-        rti.affiliatedTaskId = tr.mAffiliatedTaskId;
-        rti.affiliatedTaskColor = tr.mAffiliatedTaskColor;
-        rti.numActivities = 0;
-        if (!tr.matchParentBounds()) {
-            rti.bounds = new Rect(tr.getOverrideBounds());
-        }
-        rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode();
-        rti.resizeMode = tr.mResizeMode;
-        rti.configuration.setTo(tr.getConfiguration());
-
-        tr.getNumRunningActivities(mTmpReport);
-        rti.numActivities = mTmpReport.numActivities;
-        rti.baseActivity = (mTmpReport.base != null) ? mTmpReport.base.intent.getComponent() : null;
-        rti.topActivity = (mTmpReport.top != null) ? mTmpReport.top.intent.getComponent() : null;
-
+        tr.fillTaskInfo(rti, mTmpReport);
+        // Fill in some deprecated values
+        rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
+        rti.persistentId = rti.taskId;
         return rti;
     }
 
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index bd49bd1..c5586bb 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -54,11 +54,10 @@
     private static final String TAG = RecentsAnimation.class.getSimpleName();
     private static final boolean DEBUG = false;
 
-    private final ActivityManagerService mService;
+    private final ActivityTaskManagerService mService;
     private final ActivityStackSupervisor mStackSupervisor;
     private final ActivityStartController mActivityStartController;
     private final WindowManagerService mWindowManager;
-    private final UserController mUserController;
     private final ActivityDisplay mDefaultDisplay;
     private final int mCallingPid;
 
@@ -68,15 +67,14 @@
     // The stack to restore the target stack behind when the animation is finished
     private ActivityStack mRestoreTargetBehindStack;
 
-    RecentsAnimation(ActivityManagerService am, ActivityStackSupervisor stackSupervisor,
+    RecentsAnimation(ActivityTaskManagerService atm, ActivityStackSupervisor stackSupervisor,
             ActivityStartController activityStartController, WindowManagerService wm,
-            UserController userController, int callingPid) {
-        mService = am;
+            int callingPid) {
+        mService = atm;
         mStackSupervisor = stackSupervisor;
         mDefaultDisplay = stackSupervisor.getDefaultDisplay();
         mActivityStartController = activityStartController;
         mWindowManager = wm;
-        mUserController = userController;
         mCallingPid = callingPid;
     }
 
@@ -122,7 +120,7 @@
 
         mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
 
-        mService.setRunningRemoteAnimation(mCallingPid, true);
+        mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, true);
 
         mWindowManager.deferSurfaceLayout();
         try {
@@ -132,7 +130,7 @@
                         mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
                 final AssistDataReceiverProxy proxy = new AssistDataReceiverProxy(
                         assistDataReceiver, recentsComponent.getPackageName());
-                mAssistDataRequester = new AssistDataRequester(mService.mContext, mService,
+                mAssistDataRequester = new AssistDataRequester(mService.mContext,
                         mWindowManager, appOpsManager, proxy, this, OP_ASSIST_STRUCTURE, OP_NONE);
                 mAssistDataRequester.requestAssistData(mStackSupervisor.getTopVisibleActivities(),
                         true /* fetchData */, false /* fetchScreenshots */,
@@ -165,7 +163,7 @@
                         .setCallingUid(recentsUid)
                         .setCallingPackage(recentsComponent.getPackageName())
                         .setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
-                        .setMayWait(mUserController.getCurrentUserId())
+                        .setMayWait(mService.getCurrentUserId())
                         .execute();
                 mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
                 mWindowManager.executeAppTransition();
@@ -210,7 +208,7 @@
     }
 
     private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller="
                     + mWindowManager.getRecentsAnimationController()
                     + " reorderMode=" + reorderMode);
@@ -232,7 +230,7 @@
                 mStackSupervisor.sendPowerHintForLaunchEndIfNeeded();
             }
 
-            mService.setRunningRemoteAnimation(mCallingPid, false);
+            mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, false);
 
             mWindowManager.inSurfaceTransaction(() -> {
                 Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
@@ -289,7 +287,7 @@
 
                     mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
                     mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
-                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
 
                     // No reason to wait for the pausing activity in this case, as the hiding of
                     // surfaces needs to be done immediately.
@@ -317,7 +315,7 @@
         if (runSychronously) {
             finishAnimation(reorderMode);
         } else {
-            mService.mHandler.post(() -> finishAnimation(reorderMode));
+            mService.mH.post(() -> finishAnimation(reorderMode));
         }
     }
 
diff --git a/services/core/java/com/android/server/am/RunningTasks.java b/services/core/java/com/android/server/am/RunningTasks.java
index c860df8..8c92496 100644
--- a/services/core/java/com/android/server/am/RunningTasks.java
+++ b/services/core/java/com/android/server/am/RunningTasks.java
@@ -85,20 +85,10 @@
      * Constructs a {@link RunningTaskInfo} from a given {@param task}.
      */
     private RunningTaskInfo createRunningTaskInfo(TaskRecord task) {
-        task.getNumRunningActivities(mTmpReport);
-
-        final RunningTaskInfo ci = new RunningTaskInfo();
-        ci.id = task.taskId;
-        ci.stackId = task.getStackId();
-        ci.baseActivity = mTmpReport.base.intent.getComponent();
-        ci.topActivity = mTmpReport.top.intent.getComponent();
-        ci.lastActiveTime = task.lastActiveTime;
-        ci.description = task.lastDescription;
-        ci.numActivities = mTmpReport.numActivities;
-        ci.numRunning = mTmpReport.numRunning;
-        ci.supportsSplitScreenMultiWindow = task.supportsSplitScreenWindowingMode();
-        ci.resizeMode = task.mResizeMode;
-        ci.configuration.setTo(task.getConfiguration());
-        return ci;
+        final RunningTaskInfo rti = new RunningTaskInfo();
+        task.fillTaskInfo(rti, mTmpReport);
+        // Fill in some deprecated values
+        rti.id = rti.taskId;
+        return rti;
     }
 }
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
index 55d17a9..f7de7f4 100644
--- a/services/core/java/com/android/server/am/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/am/SafeActivityOptions.java
@@ -117,7 +117,7 @@
      * @param callerApp The record of the caller.
      */
     ActivityOptions getOptions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
-            @Nullable ProcessRecord callerApp,
+            @Nullable WindowProcessController callerApp,
             ActivityStackSupervisor supervisor) throws SecurityException {
         if (mOriginalOptions != null) {
             checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions,
@@ -186,13 +186,13 @@
     }
 
     private void checkPermissions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
-            @Nullable ProcessRecord callerApp, ActivityStackSupervisor supervisor,
+            @Nullable WindowProcessController callerApp, ActivityStackSupervisor supervisor,
             ActivityOptions options, int callingPid, int callingUid) {
         // If a launch task id is specified, then ensure that the caller is the recents
         // component or has the START_TASKS_FROM_RECENTS permission
         if (options.getLaunchTaskId() != INVALID_TASK_ID
                 && !supervisor.mRecentTasks.isCallerRecents(callingUid)) {
-            final int startInTaskPerm = supervisor.mService.checkPermission(
+            final int startInTaskPerm = ActivityTaskManagerService.checkPermission(
                     START_TASKS_FROM_RECENTS, callingPid, callingUid);
             if (startInTaskPerm == PERMISSION_DENIED) {
                 final String msg = "Permission Denial: starting " + getIntentString(intent)
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 4d89d01..d8f94c9 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -44,6 +44,8 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
+import com.android.server.uri.NeededUriGrants;
+import com.android.server.uri.UriPermissionOwner;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -129,7 +131,7 @@
         final int id;
         final int callingId;
         final Intent intent;
-        final ActivityManagerService.NeededUriGrants neededGrants;
+        final NeededUriGrants neededGrants;
         long deliveredTime;
         int deliveryCount;
         int doneExecutingCount;
@@ -138,7 +140,7 @@
         String stringName;      // caching of toString
 
         StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent,
-                ActivityManagerService.NeededUriGrants _neededGrants, int _callingId) {
+                NeededUriGrants _neededGrants, int _callingId) {
             sr = _sr;
             taskRemoved = _taskRemoved;
             id = _id;
@@ -149,14 +151,14 @@
 
         UriPermissionOwner getUriPermissionsLocked() {
             if (uriPermissions == null) {
-                uriPermissions = new UriPermissionOwner(sr.ams, this);
+                uriPermissions = new UriPermissionOwner(sr.ams.mUgmInternal, this);
             }
             return uriPermissions;
         }
 
         void removeUriPermissionsLocked() {
             if (uriPermissions != null) {
-                uriPermissions.removeUriPermissionsLocked();
+                uriPermissions.removeUriPermissions();
                 uriPermissions = null;
             }
         }
@@ -500,6 +502,23 @@
         restartTracker.setRestarting(true, memFactor, now);
     }
 
+    public void setProcess(ProcessRecord _proc) {
+        app = _proc;
+        if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS) {
+            for (int conni = connections.size() - 1; conni >= 0; conni--) {
+                ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
+                for (int i = 0; i < cr.size(); i++) {
+                    final ConnectionRecord conn = cr.get(i);
+                    if (_proc != null) {
+                        conn.startAssociationIfNeeded();
+                    } else {
+                        conn.stopAssociation();
+                    }
+                }
+            }
+        }
+    }
+
     public AppBindRecord retrieveAppBindingLocked(Intent intent,
             ProcessRecord app) {
         Intent.FilterComparison filter = new Intent.FilterComparison(intent);
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
new file mode 100644
index 0000000..9e11eb0
--- /dev/null
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -0,0 +1,70 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsActivityManagerDeviceTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.support.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsActivityManagerDeviceSdk25TestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.support.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsAppTestCases",
+      "options": [
+        {
+          "include-filter": "android.app.cts.TaskDescriptionTest"
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.support.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.am."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.support.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "CtsActivityManagerDeviceTestCases"
+    },
+    {
+      "name": "CtsActivityManagerDeviceSdk25TestCases"
+    },
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.am."
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
index 7896e2d..efb80be 100644
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -52,7 +52,8 @@
     // Delay in notifying task stack change listeners (in millis)
     private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
 
-    private final ActivityManagerService mService;
+    // Global lock used by the service the instantiate objects of this class.
+    private final Object mServiceLock;
     private final ActivityStackSupervisor mStackSupervisor;
     private final Handler mHandler;
 
@@ -149,7 +150,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case LOG_STACK_STATE_MSG: {
-                    synchronized (mService) {
+                    synchronized (mServiceLock) {
                         mStackSupervisor.logStackState();
                     }
                     break;
@@ -209,15 +210,15 @@
         }
     }
 
-    public TaskChangeNotificationController(ActivityManagerService service,
+    public TaskChangeNotificationController(Object serviceLock,
             ActivityStackSupervisor stackSupervisor, Handler handler) {
-        mService = service;
+        mServiceLock = serviceLock;
         mStackSupervisor = stackSupervisor;
         mHandler = new MainHandler(handler.getLooper());
     }
 
     public void registerTaskStackListener(ITaskStackListener listener) {
-        synchronized (mService) {
+        synchronized (mServiceLock) {
             if (listener != null) {
                 if (Binder.getCallingPid() == android.os.Process.myPid()) {
                     if (!mLocalTaskStackListeners.contains(listener)) {
@@ -231,7 +232,7 @@
     }
 
     public void unregisterTaskStackListener(ITaskStackListener listener) {
-        synchronized (mService) {
+        synchronized (mServiceLock) {
             if (listener != null) {
                 if (Binder.getCallingPid() == android.os.Process.myPid()) {
                     mLocalTaskStackListeners.remove(listener);
@@ -243,7 +244,7 @@
     }
 
     private void forAllRemoteListeners(TaskStackConsumer callback, Message message) {
-        synchronized (mService) {
+        synchronized (mServiceLock) {
             for (int i = mRemoteTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
                 try {
                     // Make a one-way callback to the listener
@@ -257,7 +258,7 @@
     }
 
     private void forAllLocalListeners(TaskStackConsumer callback, Message message) {
-        synchronized (mService) {
+        synchronized (mServiceLock) {
             for (int i = mLocalTaskStackListeners.size() - 1; i >= 0; i--) {
                 try {
                     callback.accept(mLocalTaskStackListeners.get(i), message);
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 2689d6a..481bb2b 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -83,7 +83,7 @@
 
     private static final String TAG_TASK = "task";
 
-    private final ActivityManagerService mService;
+    private final ActivityTaskManagerService mService;
     private final ActivityStackSupervisor mStackSupervisor;
     private final RecentTasks mRecentTasks;
     private final SparseArray<SparseBooleanArray> mTaskIdsInFile = new SparseArray<>();
@@ -125,7 +125,7 @@
     ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>();
 
     TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
-            ActivityManagerService service, RecentTasks recentTasks) {
+            ActivityTaskManagerService service, RecentTasks recentTasks) {
 
         final File legacyImagesDir = new File(systemDir, IMAGES_DIRNAME);
         if (legacyImagesDir.exists()) {
@@ -565,7 +565,7 @@
 
     private void writeTaskIdsFiles() {
         SparseArray<SparseBooleanArray> changedTaskIdsPerUser = new SparseArray<>();
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             for (int userId : mRecentTasks.usersWithRecentsLoadedLocked()) {
                 SparseBooleanArray taskIdsToSave = mRecentTasks.getTaskIdsForUser(userId);
                 SparseBooleanArray persistedIdsInFile = mTaskIdsInFile.get(userId);
@@ -586,7 +586,7 @@
 
     private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) {
         int[] candidateUserIds;
-        synchronized (mService) {
+        synchronized (mService.mGlobalLock) {
             // Remove only from directories of the users who have recents in memory synchronized
             // with persistent storage.
             candidateUserIds = mRecentTasks.usersWithRecentsLoadedLocked();
@@ -652,7 +652,7 @@
                 if (probablyDone) {
                     if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
                     persistentTaskIds.clear();
-                    synchronized (mService) {
+                    synchronized (mService.mGlobalLock) {
                         if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
                         mRecentTasks.getPersistableTaskIds(persistentTaskIds);
                         mService.mWindowManager.removeObsoleteTaskFiles(persistentTaskIds,
@@ -736,7 +736,7 @@
                 StringWriter stringWriter = null;
                 TaskRecord task = ((TaskWriteQueueItem) item).mTask;
                 if (DEBUG) Slog.d(TAG, "Writing task=" + task);
-                synchronized (mService) {
+                synchronized (mService.mGlobalLock) {
                     if (task.inRecents) {
                         // Still there.
                         try {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index d3ac7cb..7256e23 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -16,9 +16,9 @@
 
 package com.android.server.am;
 
-import static android.app.ActivityManager.RESIZE_MODE_FORCED;
-import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
+import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -63,6 +63,7 @@
 import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.am.TaskRecordProto.ACTIVITIES;
+import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
 import static com.android.server.am.TaskRecordProto.BOUNDS;
 import static com.android.server.am.TaskRecordProto.CONFIGURATION_CONTAINER;
 import static com.android.server.am.TaskRecordProto.FULLSCREEN;
@@ -74,7 +75,6 @@
 import static com.android.server.am.TaskRecordProto.REAL_ACTIVITY;
 import static com.android.server.am.TaskRecordProto.RESIZE_MODE;
 import static com.android.server.am.TaskRecordProto.STACK_ID;
-import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
 
 import static java.lang.Integer.MAX_VALUE;
 
@@ -85,8 +85,9 @@
 import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
 import android.app.AppGlobals;
-import android.app.IActivityManager;
+import android.app.TaskInfo;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -263,7 +264,7 @@
     /** The process that had previously hosted the root activity of this task.
      * Used to know that we should try harder to keep this process around, in case the
      * user wants to return to it. */
-    private ProcessRecord mRootProcess;
+    private WindowProcessController mRootProcess;
 
     /** Takes on same value as first root activity */
     boolean isPersistable = false;
@@ -294,7 +295,7 @@
     int mCallingUid;
     String mCallingPackage;
 
-    final ActivityManagerService mService;
+    final ActivityTaskManagerService mService;
 
     private final Rect mTmpStableBounds = new Rect();
     private final Rect mTmpNonDecorBounds = new Rect();
@@ -319,10 +320,10 @@
     private TaskWindowContainerController mWindowContainerController;
 
     /**
-     * Don't use constructor directly. Use {@link #create(ActivityManagerService, int, ActivityInfo,
-     * Intent, TaskDescription)} instead.
+     * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
+     * ActivityInfo, Intent, TaskDescription)} instead.
      */
-    TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
+    TaskRecord(ActivityTaskManagerService service, int _taskId, ActivityInfo info, Intent _intent,
             IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
         mService = service;
         userId = UserHandle.getUserId(info.applicationInfo.uid);
@@ -338,14 +339,15 @@
         setIntent(_intent, info);
         setMinDimensions(info);
         touchActiveTime();
-        mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
+        mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
     }
 
     /**
-     * Don't use constructor directly. Use {@link #create(ActivityManagerService, int, ActivityInfo,
+     * Don't use constructor directly.
+     * Use {@link #create(ActivityTaskManagerService, int, ActivityInfo,
      * Intent, IVoiceInteractionSession, IVoiceInteractor)} instead.
      */
-    TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
+    TaskRecord(ActivityTaskManagerService service, int _taskId, ActivityInfo info, Intent _intent,
             TaskDescription _taskDescription) {
         mService = service;
         userId = UserHandle.getUserId(info.applicationInfo.uid);
@@ -364,17 +366,17 @@
         isPersistable = true;
         // Clamp to [1, max].
         maxRecents = Math.min(Math.max(info.maxRecents, 1),
-                ActivityManager.getMaxAppRecentsLimitStatic());
+                ActivityTaskManager.getMaxAppRecentsLimitStatic());
 
         lastTaskDescription = _taskDescription;
         touchActiveTime();
-        mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
+        mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
     }
 
     /**
      * Don't use constructor directly. This is only used by XML parser.
      */
-    TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
+    TaskRecord(ActivityTaskManagerService service, int _taskId, Intent _intent,
             Intent _affinityIntent, String _affinity, String _rootAffinity,
             ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
             boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId,
@@ -418,7 +420,7 @@
         mSupportsPictureInPicture = supportsPictureInPicture;
         mMinWidth = minWidth;
         mMinHeight = minHeight;
-        mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
+        mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
     }
 
     TaskWindowContainerController getWindowContainerController() {
@@ -459,13 +461,13 @@
             // default configuration the next time it launches.
             updateOverrideConfiguration(null);
         }
-        mService.mTaskChangeNotificationController.notifyTaskRemoved(taskId);
+        mService.getTaskChangeNotificationController().notifyTaskRemoved(taskId);
         mWindowContainerController = null;
     }
 
     @Override
     public void onSnapshotChanged(TaskSnapshot snapshot) {
-        mService.mTaskChangeNotificationController.notifyTaskSnapshotChanged(taskId, snapshot);
+        mService.getTaskChangeNotificationController().notifyTaskSnapshotChanged(taskId, snapshot);
     }
 
     void setResizeMode(int resizeMode) {
@@ -475,7 +477,7 @@
         mResizeMode = resizeMode;
         mWindowContainerController.setResizeable(resizeMode);
         mService.mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+        mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
     }
 
     void setTaskDockedResizing(boolean resizing) {
@@ -540,10 +542,16 @@
                 if (r != null && !deferResume) {
                     kept = r.ensureActivityConfiguration(0 /* globalChanges */,
                             preserveWindow);
+                    // Preserve other windows for resizing because if resizing happens when there
+                    // is a dialog activity in the front, the activity that still shows some
+                    // content to the user will become black and cause flickers. Note in most cases
+                    // this won't cause tons of irrelevant windows being preserved because only
+                    // activities in this task may experience a bounds change. Configs for other
+                    // activities stay the same.
                     mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0,
-                            !PRESERVE_WINDOWS);
+                            preserveWindow);
                     if (!kept) {
-                        mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                        mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
                     }
                 }
             }
@@ -649,7 +657,7 @@
         boolean kept = true;
         try {
             final ActivityRecord r = topRunningActivityLocked();
-            final boolean wasFocused = r != null && supervisor.isFocusedStack(sourceStack)
+            final boolean wasFocused = r != null && supervisor.isTopDisplayFocusedStack(sourceStack)
                     && (topRunningActivityLocked() == r);
             final boolean wasResumed = r != null && sourceStack.getResumedActivity() == r;
             final boolean wasPaused = r != null && sourceStack.mPausingActivity == r;
@@ -743,7 +751,7 @@
             // The task might have already been running and its visibility needs to be synchronized
             // with the visibility of the stack / windows.
             supervisor.ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
-            supervisor.resumeFocusedStackTopActivityLocked();
+            supervisor.resumeFocusedStacksTopActivitiesLocked();
         }
 
         // TODO: Handle incorrect request to move before the actual move, not after.
@@ -1129,7 +1137,7 @@
                 // activity
                 reportOut.numRunning = 0;
             }
-            if (r.app != null && r.app.thread != null) {
+            if (r.attachedToProcess()) {
                 // Increment the number of actually running activities
                 reportOut.numRunning++;
             }
@@ -1233,7 +1241,7 @@
             mCallingPackage = r.launchedFromPackage;
             // Clamp to [1, max].
             maxRecents = Math.min(Math.max(r.info.maxRecents, 1),
-                    ActivityManager.getMaxAppRecentsLimitStatic());
+                    ActivityTaskManager.getMaxAppRecentsLimitStatic());
         } else {
             // Otherwise make all added activities match this one.
             r.setActivityType(getActivityType());
@@ -1301,7 +1309,7 @@
             // We normally notify listeners of task stack changes on pause, however pinned stack
             // activities are normally in the paused state so no notification will be sent there
             // before the activity is removed. We send it here so instead.
-            mService.mTaskChangeNotificationController.notifyTaskStackChanged();
+            mService.getTaskChangeNotificationController().notifyTaskStackChanged();
         }
 
         if (mActivities.isEmpty()) {
@@ -1739,12 +1747,20 @@
         return updateOverrideConfiguration(bounds, null /* insetBounds */);
     }
 
+    void setLastNonFullscreenBounds(Rect bounds) {
+        if (mLastNonFullscreenBounds == null) {
+            mLastNonFullscreenBounds = new Rect(bounds);
+        } else {
+            mLastNonFullscreenBounds.set(bounds);
+        }
+    }
+
     /**
      * Update task's override configuration based on the bounds.
      * @param bounds The bounds of the task.
      * @param insetBounds The bounds used to calculate the system insets, which is used here to
      *                    subtract the navigation bar/status bar size from the screen size reported
-     *                    to the application. See {@link IActivityManager#resizeDockedStack}.
+     *                    to the application. See {@link IActivityTaskManager#resizeDockedStack}.
      * @return True if the override configuration was updated.
      */
     boolean updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
@@ -1760,7 +1776,7 @@
         final boolean persistBounds = getWindowConfiguration().persistTaskBounds();
         if (matchParentBounds) {
             if (!currentBounds.isEmpty() && persistBounds) {
-                mLastNonFullscreenBounds = currentBounds;
+                setLastNonFullscreenBounds(currentBounds);
             }
             setBounds(null);
             newConfig.unset();
@@ -1770,7 +1786,7 @@
             setBounds(mTmpRect);
 
             if (mStack == null || persistBounds) {
-                mLastNonFullscreenBounds = getOverrideBounds();
+                setLastNonFullscreenBounds(getOverrideBounds());
             }
             computeOverrideConfiguration(newConfig, mTmpRect, insetBounds,
                     mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
@@ -1839,7 +1855,9 @@
         final int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
         // We're only overriding LONG, SIZE and COMPAT parts of screenLayout, so we start override
         // calculation with partial default.
-        final int sl = Configuration.SCREENLAYOUT_LONG_YES | Configuration.SCREENLAYOUT_SIZE_XLARGE;
+        // Reducing the screen layout starting from its parent config.
+        final int sl = parentConfig.screenLayout &
+                (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
         final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
         final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);
         config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
@@ -1907,18 +1925,18 @@
         }
     }
 
-    void setRootProcess(ProcessRecord proc) {
+    void setRootProcess(WindowProcessController proc) {
         clearRootProcess();
         if (intent != null &&
                 (intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0) {
             mRootProcess = proc;
-            proc.recentTasks.add(this);
+            mRootProcess.addRecentTask(this);
         }
     }
 
     void clearRootProcess() {
         if (mRootProcess != null) {
-            mRootProcess.recentTasks.remove(this);
+            mRootProcess.removeRecentTask(this);
             mRootProcess = null;
         }
     }
@@ -1929,6 +1947,35 @@
         }
     }
 
+    /**
+     * Fills in a {@link TaskInfo} with information from this task.
+     * @param info the {@link TaskInfo} to fill in
+     * @param reuseActivitiesReport a temporary activities report that we can reuse to fetch the
+     *                              running activities
+     */
+    void fillTaskInfo(TaskInfo info, TaskActivitiesReport reuseActivitiesReport) {
+        getNumRunningActivities(reuseActivitiesReport);
+        info.userId = userId;
+        info.stackId = getStackId();
+        info.taskId = taskId;
+        info.isRunning = getTopActivity() != null;
+        info.baseIntent = getBaseIntent();
+        info.baseActivity = reuseActivitiesReport.base != null
+                ? reuseActivitiesReport.base.intent.getComponent()
+                : null;
+        info.topActivity = reuseActivitiesReport.top != null
+                ? reuseActivitiesReport.top.intent.getComponent()
+                : null;
+        info.origActivity = origActivity;
+        info.realActivity = realActivity;
+        info.numActivities = reuseActivitiesReport.numActivities;
+        info.lastActiveTime = lastActiveTime;
+        info.taskDescription = new ActivityManager.TaskDescription(lastTaskDescription);
+        info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
+        info.resizeMode = mResizeMode;
+        info.configuration.setTo(getConfiguration());
+    }
+
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("userId="); pw.print(userId);
                 pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
@@ -2204,14 +2251,14 @@
         sTaskRecordFactory = factory;
     }
 
-    static TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+    static TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
             Intent intent, IVoiceInteractionSession voiceSession,
             IVoiceInteractor voiceInteractor) {
         return getTaskRecordFactory().create(
                 service, taskId, info, intent, voiceSession, voiceInteractor);
     }
 
-    static TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+    static TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
             Intent intent, TaskDescription taskDescription) {
         return getTaskRecordFactory().create(service, taskId, info, intent, taskDescription);
     }
@@ -2228,14 +2275,14 @@
      */
     static class TaskRecordFactory {
 
-        TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+        TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
                 Intent intent, IVoiceInteractionSession voiceSession,
                 IVoiceInteractor voiceInteractor) {
             return new TaskRecord(
                     service, taskId, info, intent, voiceSession, voiceInteractor);
         }
 
-        TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+        TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
                 Intent intent, TaskDescription taskDescription) {
             return new TaskRecord(service, taskId, info, intent, taskDescription);
         }
@@ -2243,7 +2290,7 @@
         /**
          * Should only be used when we're restoring {@link TaskRecord} from storage.
          */
-        TaskRecord create(ActivityManagerService service, int taskId, Intent intent,
+        TaskRecord create(ActivityTaskManagerService service, int taskId, Intent intent,
                 Intent affinityIntent, String affinity, String rootAffinity,
                 ComponentName realActivity, ComponentName origActivity, boolean rootWasReset,
                 boolean autoRemoveRecents, boolean askedCompatMode, int userId,
@@ -2467,7 +2514,8 @@
                 }
             }
 
-            final TaskRecord task = create(stackSupervisor.mService, taskId, intent, affinityIntent,
+            final TaskRecord task = create(stackSupervisor.mService,
+                    taskId, intent, affinityIntent,
                     affinity, rootAffinity, realActivity, origActivity, rootHasReset,
                     autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
                     activities, lastTimeOnTop, neverRelinquishIdentity, taskDescription,
diff --git a/services/core/java/com/android/server/am/UriPermission.java b/services/core/java/com/android/server/am/UriPermission.java
deleted file mode 100644
index 1e071aa..0000000
--- a/services/core/java/com/android/server/am/UriPermission.java
+++ /dev/null
@@ -1,395 +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 com.android.server.am;
-
-import android.app.GrantedUriPermission;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.UserHandle;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Slog;
-
-import com.android.server.am.ActivityManagerService.GrantUri;
-import com.google.android.collect.Sets;
-
-import java.io.PrintWriter;
-import java.util.Comparator;
-
-/**
- * Description of a permission granted to an app to access a particular URI.
- *
- * CTS tests for this functionality can be run with "runtest cts-appsecurity".
- *
- * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
- *      src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
- */
-final class UriPermission {
-    private static final String TAG = "UriPermission";
-
-    public static final int STRENGTH_NONE = 0;
-    public static final int STRENGTH_OWNED = 1;
-    public static final int STRENGTH_GLOBAL = 2;
-    public static final int STRENGTH_PERSISTABLE = 3;
-
-    final int targetUserId;
-    final String sourcePkg;
-    final String targetPkg;
-
-    /** Cached UID of {@link #targetPkg}; should not be persisted */
-    final int targetUid;
-
-    final GrantUri uri;
-
-    /**
-     * Allowed modes. All permission enforcement should use this field. Must
-     * always be a combination of {@link #ownedModeFlags},
-     * {@link #globalModeFlags}, {@link #persistableModeFlags}, and
-     * {@link #persistedModeFlags}. Mutations <em>must</em> only be performed by
-     * the owning class.
-     */
-    int modeFlags = 0;
-
-    /** Allowed modes with active owner. */
-    int ownedModeFlags = 0;
-    /** Allowed modes without explicit owner. */
-    int globalModeFlags = 0;
-    /** Allowed modes that have been offered for possible persisting. */
-    int persistableModeFlags = 0;
-
-    /** Allowed modes that should be persisted across device boots. */
-    int persistedModeFlags = 0;
-
-    /**
-     * Timestamp when {@link #persistedModeFlags} was first defined in
-     * {@link System#currentTimeMillis()} time base.
-     */
-    long persistedCreateTime = INVALID_TIME;
-
-    private static final long INVALID_TIME = Long.MIN_VALUE;
-
-    private ArraySet<UriPermissionOwner> mReadOwners;
-    private ArraySet<UriPermissionOwner> mWriteOwners;
-
-    private String stringName;
-
-    UriPermission(String sourcePkg, String targetPkg, int targetUid, GrantUri uri) {
-        this.targetUserId = UserHandle.getUserId(targetUid);
-        this.sourcePkg = sourcePkg;
-        this.targetPkg = targetPkg;
-        this.targetUid = targetUid;
-        this.uri = uri;
-    }
-
-    private void updateModeFlags() {
-        final int oldModeFlags = modeFlags;
-        modeFlags = ownedModeFlags | globalModeFlags | persistableModeFlags | persistedModeFlags;
-
-        if (Log.isLoggable(TAG, Log.VERBOSE) && (modeFlags != oldModeFlags)) {
-            Slog.d(TAG,
-                    "Permission for " + targetPkg + " to " + uri + " is changing from 0x"
-                            + Integer.toHexString(oldModeFlags) + " to 0x"
-                            + Integer.toHexString(modeFlags) + " via calling UID "
-                            + Binder.getCallingUid() + " PID " + Binder.getCallingPid(),
-                    new Throwable());
-        }
-    }
-
-    /**
-     * Initialize persisted modes as read from file. This doesn't issue any
-     * global or owner grants.
-     */
-    void initPersistedModes(int modeFlags, long createdTime) {
-        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        persistableModeFlags = modeFlags;
-        persistedModeFlags = modeFlags;
-        persistedCreateTime = createdTime;
-
-        updateModeFlags();
-    }
-
-    void grantModes(int modeFlags, UriPermissionOwner owner) {
-        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
-        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        if (persistable) {
-            persistableModeFlags |= modeFlags;
-        }
-
-        if (owner == null) {
-            globalModeFlags |= modeFlags;
-        } else {
-            if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
-                addReadOwner(owner);
-            }
-            if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
-                addWriteOwner(owner);
-            }
-        }
-
-        updateModeFlags();
-    }
-
-    /**
-     * @return if mode changes should trigger persisting.
-     */
-    boolean takePersistableModes(int modeFlags) {
-        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        if ((modeFlags & persistableModeFlags) != modeFlags) {
-            Slog.w(TAG, "Requested flags 0x"
-                    + Integer.toHexString(modeFlags) + ", but only 0x"
-                    + Integer.toHexString(persistableModeFlags) + " are allowed");
-            return false;
-        }
-
-        final int before = persistedModeFlags;
-        persistedModeFlags |= (persistableModeFlags & modeFlags);
-
-        if (persistedModeFlags != 0) {
-            persistedCreateTime = System.currentTimeMillis();
-        }
-
-        updateModeFlags();
-        return persistedModeFlags != before;
-    }
-
-    boolean releasePersistableModes(int modeFlags) {
-        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        final int before = persistedModeFlags;
-
-        persistableModeFlags &= ~modeFlags;
-        persistedModeFlags &= ~modeFlags;
-
-        if (persistedModeFlags == 0) {
-            persistedCreateTime = INVALID_TIME;
-        }
-
-        updateModeFlags();
-        return persistedModeFlags != before;
-    }
-
-    /**
-     * @return if mode changes should trigger persisting.
-     */
-    boolean revokeModes(int modeFlags, boolean includingOwners) {
-        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
-        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        final int before = persistedModeFlags;
-
-        if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
-            if (persistable) {
-                persistableModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-                persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-            }
-            globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-            if (mReadOwners != null && includingOwners) {
-                ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-                for (UriPermissionOwner r : mReadOwners) {
-                    r.removeReadPermission(this);
-                }
-                mReadOwners = null;
-            }
-        }
-        if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
-            if (persistable) {
-                persistableModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-                persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-            }
-            globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-            if (mWriteOwners != null && includingOwners) {
-                ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-                for (UriPermissionOwner r : mWriteOwners) {
-                    r.removeWritePermission(this);
-                }
-                mWriteOwners = null;
-            }
-        }
-
-        if (persistedModeFlags == 0) {
-            persistedCreateTime = INVALID_TIME;
-        }
-
-        updateModeFlags();
-        return persistedModeFlags != before;
-    }
-
-    /**
-     * Return strength of this permission grant for the given flags.
-     */
-    public int getStrength(int modeFlags) {
-        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        if ((persistableModeFlags & modeFlags) == modeFlags) {
-            return STRENGTH_PERSISTABLE;
-        } else if ((globalModeFlags & modeFlags) == modeFlags) {
-            return STRENGTH_GLOBAL;
-        } else if ((ownedModeFlags & modeFlags) == modeFlags) {
-            return STRENGTH_OWNED;
-        } else {
-            return STRENGTH_NONE;
-        }
-    }
-
-    private void addReadOwner(UriPermissionOwner owner) {
-        if (mReadOwners == null) {
-            mReadOwners = Sets.newArraySet();
-            ownedModeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION;
-            updateModeFlags();
-        }
-        if (mReadOwners.add(owner)) {
-            owner.addReadPermission(this);
-        }
-    }
-
-    /**
-     * Remove given read owner, updating {@Link #modeFlags} as needed.
-     */
-    void removeReadOwner(UriPermissionOwner owner) {
-        if (!mReadOwners.remove(owner)) {
-            Slog.wtf(TAG, "Unknown read owner " + owner + " in " + this);
-        }
-        if (mReadOwners.size() == 0) {
-            mReadOwners = null;
-            ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-            updateModeFlags();
-        }
-    }
-
-    private void addWriteOwner(UriPermissionOwner owner) {
-        if (mWriteOwners == null) {
-            mWriteOwners = Sets.newArraySet();
-            ownedModeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-            updateModeFlags();
-        }
-        if (mWriteOwners.add(owner)) {
-            owner.addWritePermission(this);
-        }
-    }
-
-    /**
-     * Remove given write owner, updating {@Link #modeFlags} as needed.
-     */
-    void removeWriteOwner(UriPermissionOwner owner) {
-        if (!mWriteOwners.remove(owner)) {
-            Slog.wtf(TAG, "Unknown write owner " + owner + " in " + this);
-        }
-        if (mWriteOwners.size() == 0) {
-            mWriteOwners = null;
-            ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-            updateModeFlags();
-        }
-    }
-
-    @Override
-    public String toString() {
-        if (stringName != null) {
-            return stringName;
-        }
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("UriPermission{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(' ');
-        sb.append(uri);
-        sb.append('}');
-        return stringName = sb.toString();
-    }
-
-    void dump(PrintWriter pw, String prefix) {
-        pw.print(prefix);
-        pw.print("targetUserId=" + targetUserId);
-        pw.print(" sourcePkg=" + sourcePkg);
-        pw.println(" targetPkg=" + targetPkg);
-
-        pw.print(prefix);
-        pw.print("mode=0x" + Integer.toHexString(modeFlags));
-        pw.print(" owned=0x" + Integer.toHexString(ownedModeFlags));
-        pw.print(" global=0x" + Integer.toHexString(globalModeFlags));
-        pw.print(" persistable=0x" + Integer.toHexString(persistableModeFlags));
-        pw.print(" persisted=0x" + Integer.toHexString(persistedModeFlags));
-        if (persistedCreateTime != INVALID_TIME) {
-            pw.print(" persistedCreate=" + persistedCreateTime);
-        }
-        pw.println();
-
-        if (mReadOwners != null) {
-            pw.print(prefix);
-            pw.println("readOwners:");
-            for (UriPermissionOwner owner : mReadOwners) {
-                pw.print(prefix);
-                pw.println("  * " + owner);
-            }
-        }
-        if (mWriteOwners != null) {
-            pw.print(prefix);
-            pw.println("writeOwners:");
-            for (UriPermissionOwner owner : mReadOwners) {
-                pw.print(prefix);
-                pw.println("  * " + owner);
-            }
-        }
-    }
-
-    public static class PersistedTimeComparator implements Comparator<UriPermission> {
-        @Override
-        public int compare(UriPermission lhs, UriPermission rhs) {
-            return Long.compare(lhs.persistedCreateTime, rhs.persistedCreateTime);
-        }
-    }
-
-    /**
-     * Snapshot of {@link UriPermission} with frozen
-     * {@link UriPermission#persistedModeFlags} state.
-     */
-    public static class Snapshot {
-        final int targetUserId;
-        final String sourcePkg;
-        final String targetPkg;
-        final GrantUri uri;
-        final int persistedModeFlags;
-        final long persistedCreateTime;
-
-        private Snapshot(UriPermission perm) {
-            this.targetUserId = perm.targetUserId;
-            this.sourcePkg = perm.sourcePkg;
-            this.targetPkg = perm.targetPkg;
-            this.uri = perm.uri;
-            this.persistedModeFlags = perm.persistedModeFlags;
-            this.persistedCreateTime = perm.persistedCreateTime;
-        }
-    }
-
-    public Snapshot snapshot() {
-        return new Snapshot(this);
-    }
-
-    public android.content.UriPermission buildPersistedPublicApiObject() {
-        return new android.content.UriPermission(uri.uri, persistedModeFlags, persistedCreateTime);
-    }
-
-    public GrantedUriPermission buildGrantedUriPermission() {
-        return new GrantedUriPermission(uri.uri, targetPkg);
-    }
-}
diff --git a/services/core/java/com/android/server/am/UriPermissionOwner.java b/services/core/java/com/android/server/am/UriPermissionOwner.java
deleted file mode 100644
index 8eda38e..0000000
--- a/services/core/java/com/android/server/am/UriPermissionOwner.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.am;
-
-import android.content.Intent;
-import android.os.Binder;
-import android.os.IBinder;
-import android.util.ArraySet;
-import android.util.proto.ProtoOutputStream;
-
-import com.google.android.collect.Sets;
-
-import java.io.PrintWriter;
-import java.util.Iterator;
-
-final class UriPermissionOwner {
-    final ActivityManagerService service;
-    final Object owner;
-
-    Binder externalToken;
-
-    private ArraySet<UriPermission> mReadPerms;
-    private ArraySet<UriPermission> mWritePerms;
-
-    class ExternalToken extends Binder {
-        UriPermissionOwner getOwner() {
-            return UriPermissionOwner.this;
-        }
-    }
-
-    UriPermissionOwner(ActivityManagerService service, Object owner) {
-        this.service = service;
-        this.owner = owner;
-    }
-
-    Binder getExternalTokenLocked() {
-        if (externalToken == null) {
-            externalToken = new ExternalToken();
-        }
-        return externalToken;
-    }
-
-    static UriPermissionOwner fromExternalToken(IBinder token) {
-        if (token instanceof ExternalToken) {
-            return ((ExternalToken)token).getOwner();
-        }
-        return null;
-    }
-
-    void removeUriPermissionsLocked() {
-        removeUriPermissionsLocked(Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-    }
-
-    void removeUriPermissionsLocked(int mode) {
-        removeUriPermissionLocked(null, mode);
-    }
-
-    void removeUriPermissionLocked(ActivityManagerService.GrantUri grantUri, int mode) {
-        if ((mode & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
-                && mReadPerms != null) {
-            Iterator<UriPermission> it = mReadPerms.iterator();
-            while (it.hasNext()) {
-                UriPermission perm = it.next();
-                if (grantUri == null || grantUri.equals(perm.uri)) {
-                    perm.removeReadOwner(this);
-                    service.removeUriPermissionIfNeededLocked(perm);
-                    it.remove();
-                }
-            }
-            if (mReadPerms.isEmpty()) {
-                mReadPerms = null;
-            }
-        }
-        if ((mode & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0
-                && mWritePerms != null) {
-            Iterator<UriPermission> it = mWritePerms.iterator();
-            while (it.hasNext()) {
-                UriPermission perm = it.next();
-                if (grantUri == null || grantUri.equals(perm.uri)) {
-                    perm.removeWriteOwner(this);
-                    service.removeUriPermissionIfNeededLocked(perm);
-                    it.remove();
-                }
-            }
-            if (mWritePerms.isEmpty()) {
-                mWritePerms = null;
-            }
-        }
-    }
-
-    public void addReadPermission(UriPermission perm) {
-        if (mReadPerms == null) {
-            mReadPerms = Sets.newArraySet();
-        }
-        mReadPerms.add(perm);
-    }
-
-    public void addWritePermission(UriPermission perm) {
-        if (mWritePerms == null) {
-            mWritePerms = Sets.newArraySet();
-        }
-        mWritePerms.add(perm);
-    }
-
-    public void removeReadPermission(UriPermission perm) {
-        mReadPerms.remove(perm);
-        if (mReadPerms.isEmpty()) {
-            mReadPerms = null;
-        }
-    }
-
-    public void removeWritePermission(UriPermission perm) {
-        mWritePerms.remove(perm);
-        if (mWritePerms.isEmpty()) {
-            mWritePerms = null;
-        }
-    }
-
-    public void dump(PrintWriter pw, String prefix) {
-        if (mReadPerms != null) {
-            pw.print(prefix); pw.print("readUriPermissions="); pw.println(mReadPerms);
-        }
-        if (mWritePerms != null) {
-            pw.print(prefix); pw.print("writeUriPermissions="); pw.println(mWritePerms);
-        }
-    }
-
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
-        long token = proto.start(fieldId);
-        proto.write(UriPermissionOwnerProto.OWNER, owner.toString());
-        if (mReadPerms != null) {
-            synchronized (mReadPerms) {
-                for (UriPermission p : mReadPerms) {
-                    p.uri.writeToProto(proto, UriPermissionOwnerProto.READ_PERMS);
-                }
-            }
-        }
-        if (mWritePerms != null) {
-            synchronized (mWritePerms) {
-                for (UriPermission p : mWritePerms) {
-                    p.uri.writeToProto(proto, UriPermissionOwnerProto.WRITE_PERMS);
-                }
-            }
-        }
-        proto.end(token);
-    }
-
-    @Override
-    public String toString() {
-        return owner.toString();
-    }
-}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 18c0957..f854df6 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -28,9 +28,9 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 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.ALLOW_FULL_ONLY;
-import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL;
-import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
 import static com.android.server.am.ActivityManagerService.MY_PID;
 import static com.android.server.am.UserState.STATE_BOOTING;
 import static com.android.server.am.UserState.STATE_RUNNING_LOCKED;
@@ -41,6 +41,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.Dialog;
@@ -80,7 +81,6 @@
 import android.text.format.DateUtils;
 import android.util.ArraySet;
 import android.util.IntArray;
-import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -88,7 +88,6 @@
 import android.util.TimingsTraceLog;
 import android.util.proto.ProtoOutputStream;
 
-import android.view.Window;
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -105,11 +104,9 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
-import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -260,13 +257,17 @@
     }
 
     void finishUserSwitch(UserState uss) {
-        finishUserBoot(uss);
-        startProfiles();
-        synchronized (mLock) {
-            stopRunningUsersLU(mMaxRunningUsers);
-        }
+        // This call holds the AM lock so we post to the handler.
+        mHandler.post(() -> {
+            finishUserBoot(uss);
+            startProfiles();
+            synchronized (mLock) {
+                stopRunningUsersLU(mMaxRunningUsers);
+            }
+        });
     }
 
+    @GuardedBy("mLock")
     List<Integer> getRunningUsersLU() {
         ArrayList<Integer> runningUsers = new ArrayList<>();
         for (Integer userId : mUserLru) {
@@ -291,6 +292,7 @@
         return runningUsers;
     }
 
+    @GuardedBy("mLock")
     void stopRunningUsersLU(int maxRunningUsers) {
         List<Integer> currentlyRunning = getRunningUsersLU();
         Iterator<Integer> iterator = currentlyRunning.iterator();
@@ -593,6 +595,7 @@
      * Stops the user along with its related users. The method calls
      * {@link #getUsersToStopLU(int)} to determine the list of users that should be stopped.
      */
+    @GuardedBy("mLock")
     private int stopUsersLU(final int userId, boolean force, final IStopUserCallback callback) {
         if (userId == UserHandle.USER_SYSTEM) {
             return USER_OP_ERROR_IS_SYSTEM;
@@ -624,6 +627,7 @@
         return USER_OP_SUCCESS;
     }
 
+    @GuardedBy("mLock")
     private void stopSingleUserLU(final int userId, final IStopUserCallback callback) {
         if (DEBUG_MU) Slog.i(TAG, "stopSingleUserLocked userId=" + userId);
         final UserState uss = mStartedUsers.get(userId);
@@ -781,6 +785,7 @@
      * Determines the list of users that should be stopped together with the specified
      * {@code userId}. The returned list includes {@code userId}.
      */
+    @GuardedBy("mLock")
     private @NonNull int[] getUsersToStopLU(int userId) {
         int startedUsersSize = mStartedUsers.size();
         IntArray userIds = new IntArray();
@@ -1366,6 +1371,7 @@
         mUserSwitchObservers.finishBroadcast();
     }
 
+    @GuardedBy("mLock")
     void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) {
         mCurWaitingUserSwitchCallbacks = null;
         mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
@@ -1574,9 +1580,12 @@
     }
 
     boolean hasStartedUserState(int userId) {
-        return mStartedUsers.get(userId) != null;
+        synchronized (mLock) {
+            return mStartedUsers.get(userId) != null;
+        }
     }
 
+    @GuardedBy("mLock")
     private void updateStartedUserArrayLU() {
         int num = 0;
         for (int i = 0; i < mStartedUsers.size(); i++) {
@@ -1715,6 +1724,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     UserInfo getCurrentUserLU() {
         int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
         return getUserInfo(userId);
@@ -1726,11 +1736,13 @@
         }
     }
 
+    @GuardedBy("mLock")
     int getCurrentOrTargetUserIdLU() {
         return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
     }
 
 
+    @GuardedBy("mLock")
     int getCurrentUserIdLU() {
         return mCurrentUserId;
     }
@@ -1741,6 +1753,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private boolean isCurrentUserLU(int userId) {
         return userId == getCurrentOrTargetUserIdLU();
     }
@@ -1750,7 +1763,7 @@
         return ums != null ? ums.getUserIds() : new int[] { 0 };
     }
 
-    UserInfo getUserInfo(int userId) {
+    private UserInfo getUserInfo(int userId) {
         return mInjector.getUserManager().getUserInfo(userId);
     }
 
@@ -1776,7 +1789,7 @@
         return mInjector.getUserManager().exists(userId);
     }
 
-    void enforceShellRestriction(String restriction, int userHandle) {
+    private void enforceShellRestriction(String restriction, int userHandle) {
         if (Binder.getCallingUid() == SHELL_UID) {
             if (userHandle < 0 || hasUserRestriction(restriction, userHandle)) {
                 throw new SecurityException("Shell does not have permission to access user "
@@ -1789,16 +1802,6 @@
         return mInjector.getUserManager().hasUserRestriction(restriction, userId);
     }
 
-    Set<Integer> getProfileIds(int userId) {
-        Set<Integer> userIds = new HashSet<>();
-        final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(userId,
-                false /* enabledOnly */);
-        for (UserInfo user : profiles) {
-            userIds.add(user.id);
-        }
-        return userIds;
-    }
-
     boolean isSameProfileGroup(int callingUserId, int targetUserId) {
         if (callingUserId == targetUserId) {
             return true;
@@ -2097,9 +2100,7 @@
             return mService.mWindowManager;
         }
         void activityManagerOnUserStopped(int userId) {
-            synchronized (mService) {
-                mService.onUserStoppedLocked(userId);
-            }
+            LocalServices.getService(ActivityTaskManagerInternal.class).onUserStopped(userId);
         }
 
         void systemServiceManagerCleanupUser(int userId) {
@@ -2174,9 +2175,7 @@
         }
 
         void updateUserConfiguration() {
-            synchronized (mService) {
-                mService.updateUserConfigurationLocked();
-            }
+            mService.mActivityTaskManager.updateUserConfiguration();
         }
 
         void clearBroadcastQueueForUser(int userId) {
@@ -2187,7 +2186,7 @@
 
         void loadUserRecents(int userId) {
             synchronized (mService) {
-                mService.getRecentTasks().loadUserRecentsLocked(userId);
+                mService.mActivityTaskManager.getRecentTasks().loadUserRecentsLocked(userId);
             }
         }
 
@@ -2242,18 +2241,18 @@
 
         protected void stackSupervisorResumeFocusedStackTopActivity() {
             synchronized (mService) {
-                mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
             }
         }
 
         protected void clearAllLockedTasks(String reason) {
             synchronized (mService) {
-                mService.getLockTaskController().clearLockedTasks(reason);
+                mService.mActivityTaskManager.getLockTaskController().clearLockedTasks(reason);
             }
         }
 
         protected boolean isCallerRecents(int callingUid) {
-            return mService.getRecentTasks().isCallerRecents(callingUid);
+            return mService.mActivityTaskManager.getRecentTasks().isCallerRecents(callingUid);
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/VrController.java b/services/core/java/com/android/server/am/VrController.java
index 45410d7..366f95a 100644
--- a/services/core/java/com/android/server/am/VrController.java
+++ b/services/core/java/com/android/server/am/VrController.java
@@ -147,14 +147,15 @@
      *
      * <p>Note: This must be called with the global ActivityManagerService lock held.
      *
-     * @param proc is the ProcessRecord of the process that entered or left the TOP_APP scheduling
-     *        group.
+     * @param proc is the WindowProcessController of the process that entered or left the TOP_APP
+     *            scheduling group.
      */
-    public void onTopProcChangedLocked(ProcessRecord proc) {
-        if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
-            setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, true);
+    public void onTopProcChangedLocked(WindowProcessController proc) {
+        final int curSchedGroup = proc.getCurrentSchedulingGroup();
+        if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+            setVrRenderThreadLocked(proc.mVrThreadTid, curSchedGroup, true);
         } else {
-            if (proc.vrThreadTid == mVrRenderThreadTid) {
+            if (proc.mVrThreadTid == mVrRenderThreadTid) {
                 clearVrRenderThreadLocked(true);
             }
         }
@@ -190,7 +191,7 @@
             changed = changeVrModeLocked(vrMode, record.app);
 
             if (record.app != null) {
-                processId = record.app.pid;
+                processId = record.app.getPid();
             }
         }
 
@@ -213,9 +214,9 @@
      *
      * @param tid the tid of the thread to set, or 0 to unset the current thread.
      * @param pid the pid of the process owning the thread to set.
-     * @param proc the ProcessRecord of the process owning the thread to set.
+     * @param proc the WindowProcessController of the process owning the thread to set.
      */
-    public void setVrThreadLocked(int tid, int pid, ProcessRecord proc) {
+    public void setVrThreadLocked(int tid, int pid, WindowProcessController proc) {
         if (hasPersistentVrFlagSet()) {
             Slog.w(TAG, "VR thread cannot be set in persistent VR mode!");
             return;
@@ -230,9 +231,9 @@
         if (!inVrMode()) {
             Slog.w(TAG, "VR thread cannot be set when not in VR mode!");
         } else {
-            setVrRenderThreadLocked(tid, proc.curSchedGroup, false);
+            setVrRenderThreadLocked(tid, proc.getCurrentSchedulingGroup(), false);
         }
-        proc.vrThreadTid = (tid > 0) ? tid : 0;
+        proc.mVrThreadTid = (tid > 0) ? tid : 0;
     }
 
     /**
@@ -280,11 +281,11 @@
      * <p>Note: This must be called with the global ActivityManagerService lock held.
      *
      * @param vrMode {@code true} if the system VR mode is being enabled.
-     * @param proc the ProcessRecord of the process enabling the system VR mode.
+     * @param proc the WindowProcessController of the process enabling the system VR mode.
      *
      * @return {@code true} if our state changed.
      */
-    private boolean changeVrModeLocked(boolean vrMode, ProcessRecord proc) {
+    private boolean changeVrModeLocked(boolean vrMode, WindowProcessController proc) {
         final int oldVrState = mVrState;
 
         // This is the only place where mVrState should have its FLAG_VR_MODE setting
@@ -299,8 +300,9 @@
 
         if (changed) {
             if (proc != null) {
-                if (proc.vrThreadTid > 0) {
-                    setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, false);
+                if (proc.mVrThreadTid > 0) {
+                    setVrRenderThreadLocked(
+                            proc.mVrThreadTid, proc.getCurrentSchedulingGroup(), false);
                 }
             } else {
               clearVrRenderThreadLocked(false);
diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java
new file mode 100644
index 0000000..e5551b5
--- /dev/null
+++ b/services/core/java/com/android/server/am/WindowProcessController.java
@@ -0,0 +1,579 @@
+/*
+ * 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 static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
+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.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
+
+import android.app.Activity;
+import android.app.ActivityTaskManager;
+import android.app.ActivityThread;
+import android.app.IApplicationThread;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledRunnable;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * The Activity Manager (AM) package manages the lifecycle of processes in the system through
+ * {@link ProcessRecord}. However, it is important for the Window Manager (WM) package to be aware
+ * of the processes and their state since it affects how WM manages windows and activities. This
+ * class that allows the {@link ProcessRecord} object in the AM package to communicate important
+ * changes to its state to the WM package in a structured way. WM package also uses
+ * {@link WindowProcessListener} to request changes to the process state on the AM side.
+ * Note that public calls into this class are assumed to be originating from outside the
+ * window manager so the window manager lock is held and appropriate permissions are checked before
+ * calls are allowed to proceed.
+ */
+public class WindowProcessController {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowProcessController" : TAG_AM;
+    private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
+
+    // all about the first app in the process
+    final ApplicationInfo mInfo;
+    final String mName;
+    final int mUid;
+    // The process of this application; 0 if none
+    private volatile int mPid;
+    // user of process.
+    final int mUserId;
+    // The owner of this window process controller object. Mainly for identification when we
+    // communicate back to the activity manager side.
+    public final Object mOwner;
+    // List of packages running in the process
+    final ArraySet<String> mPkgList = new ArraySet<>();
+    private final WindowProcessListener mListener;
+    private final ActivityTaskManagerService mAtm;
+    // The actual proc...  may be null only if 'persistent' is true (in which case we are in the
+    // process of launching the app)
+    private volatile IApplicationThread mThread;
+    // Currently desired scheduling class
+    private volatile int mCurSchedGroup;
+    // Last reported process state;
+    private volatile int mRepProcState = PROCESS_STATE_NONEXISTENT;
+    // are we in the process of crashing?
+    private volatile boolean mCrashing;
+    // does the app have a not responding dialog?
+    private volatile boolean mNotResponding;
+    // always keep this application running?
+    private volatile boolean mPersistent;
+    // The ABI this process was launched with
+    private volatile String mRequiredAbi;
+    // Running any services that are foreground?
+    private volatile boolean mHasForegroundServices;
+    // was app launched for debugging?
+    private volatile boolean mDebugging;
+    // Active instrumentation running in process?
+    private volatile boolean mInstrumenting;
+    // Set to true when process was launched with a wrapper attached
+    private volatile boolean mUsingWrapper;
+
+    // Thread currently set for VR scheduling
+    int mVrThreadTid;
+
+    // all activities running in the process
+    private final ArrayList<ActivityRecord> mActivities = new ArrayList<>();
+    // any tasks this process had run root activities in
+    private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<>();
+
+    WindowProcessController(ActivityTaskManagerService atm, ApplicationInfo info, String name,
+            int uid, int userId, Object owner, WindowProcessListener listener) {
+        mInfo = info;
+        mName = name;
+        mUid = uid;
+        mUserId = userId;
+        mOwner = owner;
+        mListener = listener;
+        mAtm = atm;
+    }
+
+    public void setPid(int pid) {
+        mPid = pid;
+    }
+
+    int getPid() {
+        return mPid;
+    }
+
+    public void setThread(IApplicationThread thread) {
+        mThread = thread;
+    }
+
+    IApplicationThread getThread() {
+        return mThread;
+    }
+
+    boolean hasThread() {
+        return mThread != null;
+    }
+
+    public void setCurrentSchedulingGroup(int curSchedGroup) {
+        mCurSchedGroup = curSchedGroup;
+    }
+
+    int getCurrentSchedulingGroup() {
+        return mCurSchedGroup;
+    }
+
+    public void setReportedProcState(int repProcState) {
+        mRepProcState = repProcState;
+    }
+
+    int getReportedProcState() {
+        return mRepProcState;
+    }
+
+    public void setCrashing(boolean crashing) {
+        mCrashing = crashing;
+    }
+
+    boolean isCrashing() {
+        return mCrashing;
+    }
+
+    public void setNotResponding(boolean notResponding) {
+        mNotResponding = notResponding;
+    }
+
+    boolean isNotResponding() {
+        return mNotResponding;
+    }
+
+    public void setPersistent(boolean persistent) {
+        mPersistent = persistent;
+    }
+
+    boolean isPersistent() {
+        return mPersistent;
+    }
+
+    public void setHasForegroundServices(boolean hasForegroundServices) {
+        mHasForegroundServices = hasForegroundServices;
+    }
+
+    boolean hasForegroundServices() {
+        return mHasForegroundServices;
+    }
+
+    public void setRequiredAbi(String requiredAbi) {
+        mRequiredAbi = requiredAbi;
+    }
+
+    String getRequiredAbi() {
+        return mRequiredAbi;
+    }
+
+    public void setDebugging(boolean debugging) {
+        mDebugging = debugging;
+    }
+
+    boolean isDebugging() {
+        return mDebugging;
+    }
+
+    public void setUsingWrapper(boolean usingWrapper) {
+        mUsingWrapper = usingWrapper;
+    }
+
+    boolean isUsingWrapper() {
+        return mUsingWrapper;
+    }
+
+    public void setInstrumenting(boolean instrumenting) {
+        mInstrumenting = instrumenting;
+    }
+
+    boolean isInstrumenting() {
+        return mInstrumenting;
+    }
+
+    public void addPackage(String packageName) {
+        synchronized (mAtm.mGlobalLock) {
+            mPkgList.add(packageName);
+        }
+    }
+
+    public void clearPackageList() {
+        synchronized (mAtm.mGlobalLock) {
+            mPkgList.clear();
+        }
+    }
+
+    void addActivityIfNeeded(ActivityRecord r) {
+        if (mActivities.contains(r)) {
+            return;
+        }
+        mActivities.add(r);
+    }
+
+    void removeActivity(ActivityRecord r) {
+        mActivities.remove(r);
+    }
+
+    public void clearActivities() {
+        synchronized (mAtm.mGlobalLock) {
+            mActivities.clear();
+        }
+    }
+
+    public boolean hasActivities() {
+        synchronized (mAtm.mGlobalLock) {
+            return !mActivities.isEmpty();
+        }
+    }
+
+    public boolean hasVisibleActivities() {
+        synchronized (mAtm.mGlobalLock) {
+            for (int i = mActivities.size() - 1; i >= 0; --i) {
+                final ActivityRecord r = mActivities.get(i);
+                if (r.visible) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public boolean hasActivitiesOrRecentTasks() {
+        synchronized (mAtm.mGlobalLock) {
+            return !mActivities.isEmpty() || !mRecentTasks.isEmpty();
+        }
+    }
+
+    public void stopFreezingActivities() {
+        synchronized (mAtm.mGlobalLock) {
+            int i = mActivities.size();
+            while (i > 0) {
+                i--;
+                mActivities.get(i).stopFreezingScreenLocked(true);
+            }
+        }
+    }
+
+    public void finishActivities() {
+        synchronized (mAtm.mGlobalLock) {
+            ArrayList<ActivityRecord> activities = new ArrayList<>(mActivities);
+            for (int i = 0; i < activities.size(); i++) {
+                final ActivityRecord r = activities.get(i);
+                if (!r.finishing && r.isInStackLocked()) {
+                    r.getStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
+                            null, "finish-heavy", true);
+                }
+            }
+        }
+    }
+
+    public boolean isInterestingToUser() {
+        synchronized (mAtm.mGlobalLock) {
+            final int size = mActivities.size();
+            for (int i = 0; i < size; i++) {
+                ActivityRecord r = mActivities.get(i);
+                if (r.isInterestingToUserLocked()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public boolean hasRunningActivity(String packageName) {
+        synchronized (mAtm.mGlobalLock) {
+            for (int i = mActivities.size() - 1; i >= 0; --i) {
+                final ActivityRecord r = mActivities.get(i);
+                if (packageName.equals(r.packageName)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public void clearPackagePreferredForHomeActivities() {
+        synchronized (mAtm.mGlobalLock) {
+            for (int i = mActivities.size() - 1; i >= 0; --i) {
+                final ActivityRecord r = mActivities.get(i);
+                if (r.isActivityTypeHome()) {
+                    Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
+                    try {
+                        ActivityThread.getPackageManager()
+                                .clearPackagePreferredActivities(r.packageName);
+                    } catch (RemoteException c) {
+                        // pm is in same process, this will never happen.
+                    }
+                }
+            }
+        }
+    }
+
+    boolean hasStartedActivity(ActivityRecord launchedActivity) {
+        for (int i = mActivities.size() - 1; i >= 0; i--) {
+            final ActivityRecord activity = mActivities.get(i);
+            if (launchedActivity == activity) {
+                continue;
+            }
+            if (!activity.stopped) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    public void updateIntentForHeavyWeightActivity(Intent intent) {
+        synchronized (mAtm.mGlobalLock) {
+            if (mActivities.isEmpty()) {
+                return;
+            }
+            ActivityRecord hist = mActivities.get(0);
+            intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName);
+            intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTask().taskId);
+        }
+    }
+
+    boolean shouldKillProcessForRemovedTask(TaskRecord tr) {
+        for (int k = 0; k < mActivities.size(); k++) {
+            final ActivityRecord activity = mActivities.get(k);
+            if (!activity.stopped) {
+                // Don't kill process(es) that has an activity not stopped.
+                return false;
+            }
+            final TaskRecord otherTask = activity.getTask();
+            if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
+                // Don't kill process(es) that has an activity in a different task that is
+                // also in recents.
+                return false;
+            }
+        }
+        return true;
+    }
+
+    ArraySet<TaskRecord> getReleaseSomeActivitiesTasks() {
+        // Examine all activities currently running in the process.
+        TaskRecord firstTask = null;
+        // Tasks is non-null only if two or more tasks are found.
+        ArraySet<TaskRecord> tasks = null;
+        if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + this);
+        for (int i = 0; i < mActivities.size(); i++) {
+            final ActivityRecord r = mActivities.get(i);
+            // First, if we find an activity that is in the process of being destroyed,
+            // then we just aren't going to do anything for now; we want things to settle
+            // down before we try to prune more activities.
+            if (r.finishing || r.isState(DESTROYING, DESTROYED)) {
+                if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r);
+                return null;
+            }
+            // Don't consider any activies that are currently not in a state where they
+            // can be destroyed.
+            if (r.visible || !r.stopped || !r.haveState
+                    || r.isState(RESUMED, PAUSING, PAUSED, STOPPING)) {
+                if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
+                continue;
+            }
+
+            final TaskRecord task = r.getTask();
+            if (task != null) {
+                if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task
+                        + " from " + r);
+                if (firstTask == null) {
+                    firstTask = task;
+                } else if (firstTask != task) {
+                    if (tasks == null) {
+                        tasks = new ArraySet<>();
+                        tasks.add(firstTask);
+                    }
+                    tasks.add(task);
+                }
+            }
+        }
+
+        return tasks;
+    }
+
+    public interface ComputeOomAdjCallback {
+        void onVisibleActivity();
+        void onPausedActivity();
+        void onStoppingActivity(boolean finishing);
+        void onOtherActivity();
+    }
+
+    public int computeOomAdjFromActivities(int minTaskLayer, ComputeOomAdjCallback callback) {
+        synchronized (mAtm.mGlobalLock) {
+            final int activitiesSize = mActivities.size();
+            for (int j = 0; j < activitiesSize; j++) {
+                final ActivityRecord r = mActivities.get(j);
+                if (r.app != this) {
+                    Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
+                            + " instead of expected " + this);
+                    if (r.app == null || (r.app.mUid == mUid)) {
+                        // Only fix things up when they look sane
+                        r.setProcess(this);
+                    } else {
+                        continue;
+                    }
+                }
+                if (r.visible) {
+                    callback.onVisibleActivity();
+                    final TaskRecord task = r.getTask();
+                    if (task != null && minTaskLayer > 0) {
+                        final int layer = task.mLayerRank;
+                        if (layer >= 0 && minTaskLayer > layer) {
+                            minTaskLayer = layer;
+                        }
+                    }
+                    break;
+                } else if (r.isState(PAUSING, PAUSED)) {
+                    callback.onPausedActivity();
+                } else if (r.isState(STOPPING)) {
+                    callback.onStoppingActivity(r.finishing);
+                } else {
+                    callback.onOtherActivity();
+                }
+            }
+        }
+
+        return minTaskLayer;
+    }
+
+    int computeRelaunchReason() {
+        synchronized (mAtm.mGlobalLock) {
+            final int activitiesSize = mActivities.size();
+            for (int i = activitiesSize - 1; i >= 0; i--) {
+                final ActivityRecord r = mActivities.get(i);
+                if (r.mRelaunchReason != ActivityRecord.RELAUNCH_REASON_NONE) {
+                    return r.mRelaunchReason;
+                }
+            }
+        }
+        return ActivityRecord.RELAUNCH_REASON_NONE;
+    }
+
+    void clearProfilerIfNeeded() {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        mAtm.mH.post(() -> mListener.clearProfilerIfNeeded());
+    }
+
+    void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
+            boolean activityChange, boolean updateOomAdj) {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Runnable r = PooledLambda.obtainRunnable(WindowProcessListener::updateProcessInfo,
+                mListener, updateServiceConnectionActivities, updateLru, activityChange,
+                updateOomAdj);
+        mAtm.mH.post(r);
+    }
+
+    void updateServiceConnectionActivities() {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        mAtm.mH.post(() -> mListener.updateServiceConnectionActivities());
+    }
+
+    void setPendingUiClean(boolean pendingUiClean) {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Runnable r = PooledLambda.obtainRunnable(
+                WindowProcessListener::setPendingUiClean, mListener, pendingUiClean);
+        mAtm.mH.post(r);
+    }
+
+    void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Runnable r = PooledLambda.obtainRunnable(
+                WindowProcessListener::setPendingUiCleanAndForceProcessStateUpTo,
+                mListener, newState);
+        mAtm.mH.post(r);
+    }
+
+    void setRemoved(boolean removed) {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Runnable r = PooledLambda.obtainRunnable(
+                WindowProcessListener::setRemoved, mListener, removed);
+        mAtm.mH.post(r);
+    }
+
+    /** Returns the total time (in milliseconds) spent executing in both user and system code. */
+    public long getCpuTime() {
+        return (mListener != null) ? mListener.getCpuTime() : 0;
+    }
+
+    void addRecentTask(TaskRecord task) {
+        mRecentTasks.add(task);
+    }
+
+    void removeRecentTask(TaskRecord task) {
+        mRecentTasks.remove(task);
+    }
+
+    public boolean hasRecentTasks() {
+        synchronized (mAtm.mGlobalLock) {
+            return !mRecentTasks.isEmpty();
+        }
+    }
+
+    public void clearRecentTasks() {
+        synchronized (mAtm.mGlobalLock) {
+            for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
+                mRecentTasks.get(i).clearRootProcess();
+            }
+            mRecentTasks.clear();
+        }
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        synchronized (mAtm.mGlobalLock) {
+            if (mActivities.size() > 0) {
+                pw.print(prefix); pw.println("Activities:");
+                for (int i = 0; i < mActivities.size(); i++) {
+                    pw.print(prefix); pw.print("  - "); pw.println(mActivities.get(i));
+                }
+            }
+
+            if (mRecentTasks.size() > 0) {
+                pw.println(prefix + "Recent Tasks:");
+                for (int i = 0; i < mRecentTasks.size(); i++) {
+                    pw.println(prefix + "  - " + mRecentTasks.get(i));
+                }
+            }
+
+            if (mVrThreadTid != 0) {
+                pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid);
+            }
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/am/WindowProcessListener.java b/services/core/java/com/android/server/am/WindowProcessListener.java
new file mode 100644
index 0000000..2de3e37
--- /dev/null
+++ b/services/core/java/com/android/server/am/WindowProcessListener.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 com.android.server.am;
+
+/**
+ * Interface used by the owner/creator of a process that owns windows to listen to changes from the
+ * WM side.
+ * @see WindowProcessController
+ */
+public interface WindowProcessListener {
+
+    /** Clear the profiler record if we are currently profiling this process. */
+    void clearProfilerIfNeeded();
+
+    /** Update the service connection for this process based on activities it might have. */
+    void updateServiceConnectionActivities();
+
+    /** Set or clear flag that we would like to clean-up UI resources for this process. */
+    void setPendingUiClean(boolean pendingUiClean);
+
+    /**
+     * Set flag that we would like to clean-up UI resources for this process and force new process
+     * state.
+     */
+    void setPendingUiCleanAndForceProcessStateUpTo(int newState);
+
+    /** Update the process information. */
+    void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
+            boolean activityChange, boolean updateOomAdj);
+
+    /** Set process package been removed from device. */
+    void setRemoved(boolean removed);
+
+    /** Returns the total time (in milliseconds) spent executing in both user and system code. */
+    long getCpuTime();
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9c0d4e0..8caa702 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -20,8 +20,6 @@
 import static android.media.AudioManager.RINGER_MODE_NORMAL;
 import static android.media.AudioManager.RINGER_MODE_SILENT;
 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
-import static android.media.AudioManager.STREAM_ALARM;
-import static android.media.AudioManager.STREAM_MUSIC;
 import static android.media.AudioManager.STREAM_SYSTEM;
 import static android.os.Process.FIRST_APPLICATION_UID;
 import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
@@ -59,6 +57,7 @@
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.database.ContentObserver;
+import android.hardware.hdmi.HdmiAudioSystemClient;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPlaybackClient;
 import android.hardware.hdmi.HdmiTvClient;
@@ -141,6 +140,7 @@
 import com.android.server.audio.AudioServiceEvents.VolumeEvent;
 import com.android.server.audio.AudioServiceEvents.WiredDevConnectEvent;
 import com.android.server.pm.UserManagerService;
+import com.android.server.wm.ActivityTaskManagerInternal;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -236,7 +236,6 @@
     private static final int MSG_PERSIST_RINGER_MODE = 3;
     private static final int MSG_AUDIO_SERVER_DIED = 4;
     private static final int MSG_PLAY_SOUND_EFFECT = 5;
-    private static final int MSG_BTA2DP_DOCK_TIMEOUT = 6;
     private static final int MSG_LOAD_SOUND_EFFECTS = 7;
     private static final int MSG_SET_FORCE_USE = 8;
     private static final int MSG_BT_HEADSET_CNCT_FAILED = 9;
@@ -268,6 +267,7 @@
     private static final int MSG_A2DP_DEVICE_CONFIG_CHANGE = 103;
     private static final int MSG_DISABLE_AUDIO_FOR_UID = 104;
     private static final int MSG_SET_HEARING_AID_CONNECTION_STATE = 105;
+    private static final int MSG_BTA2DP_DOCK_TIMEOUT = 106;
     // end of messages handled under wakelock
 
     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
@@ -927,14 +927,15 @@
         }
 
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
-            mHdmiManager = mContext.getSystemService(HdmiControlManager.class);
-            synchronized (mHdmiManager) {
+            synchronized (mHdmiClientLock) {
+                mHdmiManager = mContext.getSystemService(HdmiControlManager.class);
                 mHdmiTvClient = mHdmiManager.getTvClient();
                 if (mHdmiTvClient != null) {
                     mFixedVolumeDevices &= ~AudioSystem.DEVICE_ALL_HDMI_SYSTEM_AUDIO_AND_SPEAKER;
                 }
                 mHdmiPlaybackClient = mHdmiManager.getPlaybackClient();
                 mHdmiCecSink = false;
+                mHdmiAudioSystemClient = mHdmiManager.getAudioSystemClient();
             }
         }
 
@@ -1055,11 +1056,9 @@
             sendEncodedSurroundMode(mContentResolver, "onAudioServerDied");
             sendEnabledSurroundFormats(mContentResolver, true);
         }
-        if (mHdmiManager != null) {
-            synchronized (mHdmiManager) {
-                if (mHdmiTvClient != null) {
-                    setHdmiSystemAudioSupported(mHdmiSystemAudioSupported);
-                }
+        synchronized (mHdmiClientLock) {
+            if (mHdmiManager != null && mHdmiTvClient != null) {
+                setHdmiSystemAudioSupported(mHdmiSystemAudioSupported);
             }
         }
 
@@ -1764,24 +1763,34 @@
             if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
                 setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
             }
-            if (mHdmiManager != null) {
-                synchronized (mHdmiManager) {
+            synchronized (mHdmiClientLock) {
+                if (mHdmiManager != null) {
                     // mHdmiCecSink true => mHdmiPlaybackClient != null
                     if (mHdmiCecSink &&
                             streamTypeAlias == AudioSystem.STREAM_MUSIC &&
                             oldIndex != newIndex) {
-                        synchronized (mHdmiPlaybackClient) {
-                            int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
-                                    KeyEvent.KEYCODE_VOLUME_UP;
-                            final long ident = Binder.clearCallingIdentity();
-                            try {
-                                mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
-                                mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
-                            } finally {
-                                Binder.restoreCallingIdentity(ident);
-                            }
+                        int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
+                                KeyEvent.KEYCODE_VOLUME_UP;
+                        final long ident = Binder.clearCallingIdentity();
+                        try {
+                            mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
+                            mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
+                        } finally {
+                            Binder.restoreCallingIdentity(ident);
                         }
                     }
+
+                    if (mHdmiAudioSystemClient != null &&
+                            mHdmiSystemAudioSupported &&
+                            streamTypeAlias == AudioSystem.STREAM_MUSIC &&
+                            (oldIndex != newIndex || isMuteAdjust)) {
+                        final long identity = Binder.clearCallingIdentity();
+                        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(
+                                isMuteAdjust, getStreamVolume(AudioSystem.STREAM_MUSIC),
+                                getStreamMaxVolume(AudioSystem.STREAM_MUSIC),
+                                isStreamMute(AudioSystem.STREAM_MUSIC));
+                        Binder.restoreCallingIdentity(identity);
+                    }
                 }
             }
         }
@@ -1800,22 +1809,19 @@
     }
 
     private void setSystemAudioVolume(int oldVolume, int newVolume, int maxVolume, int flags) {
-        if (mHdmiManager == null
-                || mHdmiTvClient == null
-                || oldVolume == newVolume
-                || (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) != 0) return;
-
         // Sets the audio volume of AVR when we are in system audio mode. The new volume info
         // is tranformed to HDMI-CEC commands and passed through CEC bus.
-        synchronized (mHdmiManager) {
-            if (!mHdmiSystemAudioSupported) return;
-            synchronized (mHdmiTvClient) {
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    mHdmiTvClient.setSystemAudioVolume(oldVolume, newVolume, maxVolume);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
+        synchronized (mHdmiClientLock) {
+            if (mHdmiManager == null
+                    || mHdmiTvClient == null
+                    || oldVolume == newVolume
+                    || (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) != 0
+                    || !mHdmiSystemAudioSupported) return;
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mHdmiTvClient.setSystemAudioVolume(oldVolume, newVolume, maxVolume);
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
     }
@@ -2038,6 +2044,20 @@
                 index = mStreamStates[streamType].getIndex(device);
             }
         }
+        synchronized (mHdmiClientLock) {
+            if (mHdmiManager != null &&
+                    mHdmiAudioSystemClient != null &&
+                    mHdmiSystemAudioSupported &&
+                    streamTypeAlias == AudioSystem.STREAM_MUSIC &&
+                    (oldIndex != index)) {
+                final long identity = Binder.clearCallingIdentity();
+                mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(
+                        false, getStreamVolume(AudioSystem.STREAM_MUSIC),
+                        getStreamMaxVolume(AudioSystem.STREAM_MUSIC),
+                        isStreamMute(AudioSystem.STREAM_MUSIC));
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
         sendVolumeUpdate(streamType, oldIndex, index, flags);
     }
 
@@ -2178,8 +2198,8 @@
     // If Hdmi-CEC system audio mode is on, we show volume bar only when TV
     // receives volume notification from Audio Receiver.
     private int updateFlagsForSystemAudio(int flags) {
-        if (mHdmiTvClient != null) {
-            synchronized (mHdmiTvClient) {
+        synchronized (mHdmiClientLock) {
+            if (mHdmiTvClient != null) {
                 if (mHdmiSystemAudioSupported &&
                         ((flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) == 0)) {
                     flags &= ~AudioManager.FLAG_SHOW_UI;
@@ -2234,16 +2254,13 @@
     }
 
     private void setSystemAudioMute(boolean state) {
-        if (mHdmiManager == null || mHdmiTvClient == null) return;
-        synchronized (mHdmiManager) {
-            if (!mHdmiSystemAudioSupported) return;
-            synchronized (mHdmiTvClient) {
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    mHdmiTvClient.setSystemAudioMute(state);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
+        synchronized (mHdmiClientLock) {
+            if (mHdmiManager == null || mHdmiTvClient == null || !mHdmiSystemAudioSupported) return;
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mHdmiTvClient.setSystemAudioMute(state);
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
     }
@@ -2563,12 +2580,12 @@
                 toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_vibrate;
                 break;
         }
-        maybeVibrate(effect);
+        maybeVibrate(effect, reason);
         setRingerModeInternal(ringerMode, reason);
         Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT).show();
     }
 
-    private boolean maybeVibrate(VibrationEffect effect) {
+    private boolean maybeVibrate(VibrationEffect effect, String reason) {
         if (!mHasVibrator) {
             return false;
         }
@@ -2581,8 +2598,8 @@
         if (effect == null) {
             return false;
         }
-        mVibrator.vibrate(
-                Binder.getCallingUid(), mContext.getOpPackageName(), effect, VIBRATION_ATTRIBUTES);
+        mVibrator.vibrate(Binder.getCallingUid(), mContext.getOpPackageName(), effect,
+                reason, VIBRATION_ATTRIBUTES);
         return true;
     }
 
@@ -2636,6 +2653,7 @@
         broadcastRingerMode(AudioManager.RINGER_MODE_CHANGED_ACTION, ringerMode);
     }
 
+    @GuardedBy("mSettingsLock")
     private void muteRingerModeStreams() {
         // Mute stream if not previously muted by ringer mode and (ringer mode
         // is not RINGER_MODE_NORMAL OR stream is zen muted) and stream is affected by ringer mode.
@@ -2723,10 +2741,9 @@
         synchronized(mSettingsLock) {
             change = mRingerMode != ringerMode;
             mRingerMode = ringerMode;
+            muteRingerModeStreams();
         }
 
-        muteRingerModeStreams();
-
         // Post a persist ringer mode msg
         if (persist) {
             sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE,
@@ -3815,6 +3832,10 @@
                             int delay = checkSendBecomingNoisyIntent(
                                     AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, intState,
                                     AudioSystem.DEVICE_NONE);
+                            final String addr = btDevice == null ? "null" : btDevice.getAddress();
+                            mDeviceLogger.log(new AudioEventLogger.StringEvent(
+                                    "A2DP service connected: device addr=" + addr
+                                    + " state=" + state));
                             queueMsgUnderWakeLock(mAudioHandler,
                                     MSG_SET_A2DP_SINK_CONNECTION_STATE,
                                     state,
@@ -4525,13 +4546,21 @@
         }
         synchronized (mLastDeviceConnectMsgTime) {
             long time = SystemClock.uptimeMillis() + delay;
-            handler.sendMessageAtTime(handler.obtainMessage(msg, arg1, arg2, obj), time);
-            if (msg == MSG_SET_WIRED_DEVICE_CONNECTION_STATE ||
-                    msg == MSG_SET_A2DP_SRC_CONNECTION_STATE ||
-                    msg == MSG_SET_A2DP_SINK_CONNECTION_STATE ||
-                    msg == MSG_SET_HEARING_AID_CONNECTION_STATE) {
+
+            if (msg == MSG_SET_A2DP_SRC_CONNECTION_STATE ||
+                msg == MSG_SET_A2DP_SINK_CONNECTION_STATE ||
+                msg == MSG_SET_HEARING_AID_CONNECTION_STATE ||
+                msg == MSG_SET_WIRED_DEVICE_CONNECTION_STATE ||
+                msg == MSG_A2DP_DEVICE_CONFIG_CHANGE ||
+                msg == MSG_BTA2DP_DOCK_TIMEOUT) {
+                if (mLastDeviceConnectMsgTime >= time) {
+                  // add a little delay to make sure messages are ordered as expected
+                  time = mLastDeviceConnectMsgTime + 30;
+                }
                 mLastDeviceConnectMsgTime = time;
             }
+
+            handler.sendMessageAtTime(handler.obtainMessage(msg, arg1, arg2, obj), time);
         }
     }
 
@@ -4670,7 +4699,14 @@
     public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device,
                 int state, int profile, boolean suppressNoisyIntent, int a2dpVolume)
     {
+        mDeviceLogger.log(new AudioEventLogger.StringEvent(
+                "setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent state=" + state
+                // only querying address as this is the only readily available field on the device
+                + " addr=" + device.getAddress()
+                + " prof=" + profile + " supprNoisy=" + suppressNoisyIntent
+                + " vol=" + a2dpVolume));
         if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) {
+            mDeviceLogger.log(new AudioEventLogger.StringEvent("A2DP connection state ignored"));
             return 0;
         }
         return setBluetoothA2dpDeviceConnectionStateInt(
@@ -4693,6 +4729,13 @@
             } else {
                 delay = 0;
             }
+
+            if (DEBUG_DEVICES) {
+                Log.d(TAG, "setBluetoothA2dpDeviceConnectionStateInt device: " + device
+                      + " state: " + state + " delay(ms): " + delay
+                      + " suppressNoisyIntent: " + suppressNoisyIntent);
+            }
+
             queueMsgUnderWakeLock(mAudioHandler,
                     (profile == BluetoothProfile.A2DP ?
                         MSG_SET_A2DP_SINK_CONNECTION_STATE : MSG_SET_A2DP_SRC_CONNECTION_STATE),
@@ -5601,6 +5644,7 @@
                     synchronized (mConnectedDevices) {
                         makeA2dpDeviceUnavailableNow( (String) msg.obj );
                     }
+                    mAudioEventWakeLock.release();
                     break;
 
                 case MSG_SET_FORCE_USE:
@@ -5615,7 +5659,7 @@
                 case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
                     {   WiredDeviceConnectionState connectState =
                             (WiredDeviceConnectionState)msg.obj;
-                        mWiredDevLogger.log(new WiredDevConnectEvent(connectState));
+                        mDeviceLogger.log(new WiredDevConnectEvent(connectState));
                         onSetWiredDeviceConnectionState(connectState.mType, connectState.mState,
                                 connectState.mAddress, connectState.mName, connectState.mCaller);
                         mAudioEventWakeLock.release();
@@ -5831,6 +5875,9 @@
 
     // must be called synchronized on mConnectedDevices
     private void makeA2dpDeviceUnavailableNow(String address) {
+        if (address == null) {
+            return;
+        }
         synchronized (mA2dpAvrcpLock) {
             mAvrcpAbsVolSupported = false;
         }
@@ -5840,6 +5887,9 @@
                 makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
         // Remove A2DP routes as well
         setCurrentAudioRouteName(null);
+        if (mDockAddress == address) {
+            mDockAddress = null;
+        }
     }
 
     // must be called synchronized on mConnectedDevices
@@ -5851,9 +5901,12 @@
         mConnectedDevices.remove(
                 makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
         // send the delayed message to make the device unavailable later
-        Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
-        mAudioHandler.sendMessageDelayed(msg, delayMs);
-
+        queueMsgUnderWakeLock(mAudioHandler,
+            MSG_BTA2DP_DOCK_TIMEOUT,
+            0,
+            0,
+            address,
+            delayMs);
     }
 
     // must be called synchronized on mConnectedDevices
@@ -5925,7 +5978,8 @@
     private void onSetA2dpSinkConnectionState(BluetoothDevice btDevice, int state, int a2dpVolume)
     {
         if (DEBUG_DEVICES) {
-            Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice+"state=" + state);
+            Log.d(TAG, "onSetA2dpSinkConnectionState btDevice= " + btDevice+" state= " + state
+                + " is dock: "+btDevice.isBluetoothDock());
         }
         if (btDevice == null) {
             return;
@@ -5962,7 +6016,7 @@
                 } else {
                     // this could be a connection of another A2DP device before the timeout of
                     // a dock: cancel the dock timeout, and make the dock unavailable now
-                    if(hasScheduledA2dpDockTimeout()) {
+                    if (hasScheduledA2dpDockTimeout() && mDockAddress != null) {
                         cancelA2dpDeviceTimeout();
                         makeA2dpDeviceUnavailableNow(mDockAddress);
                     }
@@ -6060,10 +6114,14 @@
         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
             address = "";
         }
+        mDeviceLogger.log(new AudioEventLogger.StringEvent(
+                "onBluetoothA2dpDeviceConfigChange addr=" + address));
 
         int device = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
         synchronized (mConnectedDevices) {
             if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, btDevice)) {
+                mDeviceLogger.log(new AudioEventLogger.StringEvent(
+                        "A2dp config change ignored"));
                 return;
             }
             final String key = makeDeviceListKey(device, address);
@@ -6181,17 +6239,6 @@
             }
         }
 
-        if (mAudioHandler.hasMessages(MSG_SET_A2DP_SRC_CONNECTION_STATE) ||
-                mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE) ||
-                mAudioHandler.hasMessages(MSG_SET_HEARING_AID_CONNECTION_STATE) ||
-                mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) {
-            synchronized (mLastDeviceConnectMsgTime) {
-                long time = SystemClock.uptimeMillis();
-                if (mLastDeviceConnectMsgTime > time) {
-                    delay = (int)(mLastDeviceConnectMsgTime - time) + 30;
-                }
-            }
-        }
         return delay;
     }
 
@@ -6341,12 +6388,10 @@
                 if (isPlatformTelevision() && ((device & AudioSystem.DEVICE_OUT_HDMI) != 0)) {
                     mFixedVolumeDevices |= AudioSystem.DEVICE_OUT_HDMI;
                     checkAllFixedVolumeDevices();
-                    if (mHdmiManager != null) {
-                        synchronized (mHdmiManager) {
-                            if (mHdmiPlaybackClient != null) {
-                                mHdmiCecSink = false;
-                                mHdmiPlaybackClient.queryDisplayStatus(mHdmiDisplayStatusCallback);
-                            }
+                    synchronized (mHdmiClientLock) {
+                        if (mHdmiManager != null && mHdmiPlaybackClient != null) {
+                            mHdmiCecSink = false;
+                            mHdmiPlaybackClient.queryDisplayStatus(mHdmiDisplayStatusCallback);
                         }
                     }
                 }
@@ -6355,8 +6400,8 @@
                 }
             } else {
                 if (isPlatformTelevision() && ((device & AudioSystem.DEVICE_OUT_HDMI) != 0)) {
-                    if (mHdmiManager != null) {
-                        synchronized (mHdmiManager) {
+                    synchronized (mHdmiClientLock) {
+                        if (mHdmiManager != null) {
                             mHdmiCecSink = false;
                         }
                     }
@@ -6642,7 +6687,8 @@
         // when the user switches back. For managed profiles, we should kill all recording apps
         ComponentName homeActivityName = null;
         if (!oldUser.isManagedProfile()) {
-            homeActivityName = mActivityManagerInternal.getHomeActivityForUser(oldUser.id);
+            homeActivityName = LocalServices.getService(
+                    ActivityTaskManagerInternal.class).getHomeActivityForUser(oldUser.id);
         }
         final String[] permissions = { Manifest.permission.RECORD_AUDIO };
         List<PackageInfo> packages;
@@ -7024,8 +7070,8 @@
 
     private class MyDisplayStatusCallback implements HdmiPlaybackClient.DisplayStatusCallback {
         public void onComplete(int status) {
-            if (mHdmiManager != null) {
-                synchronized (mHdmiManager) {
+            synchronized (mHdmiClientLock) {
+                if (mHdmiManager != null) {
                     mHdmiCecSink = (status != HdmiControlManager.POWER_STATUS_UNKNOWN);
                     // Television devices without CEC service apply software volume on HDMI output
                     if (isPlatformTelevision() && !mHdmiCecSink) {
@@ -7035,43 +7081,49 @@
                 }
             }
         }
-    };
+    }
+
+    private final Object mHdmiClientLock = new Object();
 
     // If HDMI-CEC system audio is supported
     private boolean mHdmiSystemAudioSupported = false;
     // Set only when device is tv.
+    @GuardedBy("mHdmiClientLock")
     private HdmiTvClient mHdmiTvClient;
     // true if the device has system feature PackageManager.FEATURE_LEANBACK.
     // cached HdmiControlManager interface
+    @GuardedBy("mHdmiClientLock")
     private HdmiControlManager mHdmiManager;
     // Set only when device is a set-top box.
+    @GuardedBy("mHdmiClientLock")
     private HdmiPlaybackClient mHdmiPlaybackClient;
     // true if we are a set-top box, an HDMI sink is connected and it supports CEC.
     private boolean mHdmiCecSink;
+    // Set only when device is an audio system.
+    @GuardedBy("mHdmiClientLock")
+    private HdmiAudioSystemClient mHdmiAudioSystemClient;
 
     private MyDisplayStatusCallback mHdmiDisplayStatusCallback = new MyDisplayStatusCallback();
 
     @Override
     public int setHdmiSystemAudioSupported(boolean on) {
         int device = AudioSystem.DEVICE_NONE;
-        if (mHdmiManager != null) {
-            synchronized (mHdmiManager) {
-                if (mHdmiTvClient == null) {
-                    Log.w(TAG, "Only Hdmi-Cec enabled TV device supports system audio mode.");
+        synchronized (mHdmiClientLock) {
+            if (mHdmiManager != null) {
+                if (mHdmiTvClient == null && mHdmiAudioSystemClient == null) {
+                    Log.w(TAG, "Only Hdmi-Cec enabled TV or audio system device supports"
+                            + "system audio mode.");
                     return device;
                 }
-
-                synchronized (mHdmiTvClient) {
-                    if (mHdmiSystemAudioSupported != on) {
-                        mHdmiSystemAudioSupported = on;
-                        final int config = on ? AudioSystem.FORCE_HDMI_SYSTEM_AUDIO_ENFORCED :
-                            AudioSystem.FORCE_NONE;
-                        mForceUseLogger.log(new ForceUseEvent(AudioSystem.FOR_HDMI_SYSTEM_AUDIO,
-                                config, "setHdmiSystemAudioSupported"));
-                        AudioSystem.setForceUse(AudioSystem.FOR_HDMI_SYSTEM_AUDIO, config);
-                    }
-                    device = getDevicesForStream(AudioSystem.STREAM_MUSIC);
+                if (mHdmiSystemAudioSupported != on) {
+                    mHdmiSystemAudioSupported = on;
+                    final int config = on ? AudioSystem.FORCE_HDMI_SYSTEM_AUDIO_ENFORCED :
+                        AudioSystem.FORCE_NONE;
+                    mForceUseLogger.log(new ForceUseEvent(AudioSystem.FOR_HDMI_SYSTEM_AUDIO,
+                            config, "setHdmiSystemAudioSupported"));
+                    AudioSystem.setForceUse(AudioSystem.FOR_HDMI_SYSTEM_AUDIO, config);
                 }
+                device = getDevicesForStream(AudioSystem.STREAM_MUSIC);
             }
         }
         return device;
@@ -7170,19 +7222,20 @@
     //==========================================================================================
     // AudioService logging and dumpsys
     //==========================================================================================
-    final int LOG_NB_EVENTS_PHONE_STATE = 20;
-    final int LOG_NB_EVENTS_WIRED_DEV_CONNECTION = 30;
-    final int LOG_NB_EVENTS_FORCE_USE = 20;
-    final int LOG_NB_EVENTS_VOLUME = 40;
-    final int LOG_NB_EVENTS_DYN_POLICY = 10;
+    static final int LOG_NB_EVENTS_PHONE_STATE = 20;
+    static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 30;
+    static final int LOG_NB_EVENTS_FORCE_USE = 20;
+    static final int LOG_NB_EVENTS_VOLUME = 40;
+    static final int LOG_NB_EVENTS_DYN_POLICY = 10;
 
     final private AudioEventLogger mModeLogger = new AudioEventLogger(LOG_NB_EVENTS_PHONE_STATE,
             "phone state (logged after successfull call to AudioSystem.setPhoneState(int))");
 
-    final private AudioEventLogger mWiredDevLogger = new AudioEventLogger(
-            LOG_NB_EVENTS_WIRED_DEV_CONNECTION,
-            "wired device connection (logged before onSetWiredDeviceConnectionState() is executed)"
-            );
+    // logs for wired + A2DP device connections:
+    // - wired: logged before onSetWiredDeviceConnectionState() is executed
+    // - A2DP: logged at reception of method call
+    final private AudioEventLogger mDeviceLogger = new AudioEventLogger(
+            LOG_NB_EVENTS_DEVICE_CONNECTION, "wired/A2DP device connection");
 
     final private AudioEventLogger mForceUseLogger = new AudioEventLogger(
             LOG_NB_EVENTS_FORCE_USE,
@@ -7271,7 +7324,7 @@
         pw.println("\nEvent logs:");
         mModeLogger.dump(pw);
         pw.println("\n");
-        mWiredDevLogger.dump(pw);
+        mDeviceLogger.dump(pw);
         pw.println("\n");
         mForceUseLogger.dump(pw);
         pw.println("\n");
diff --git a/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
new file mode 100644
index 0000000..10a1b90
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
@@ -0,0 +1,334 @@
+/*
+ * 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.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.statusbar.IStatusBarService;
+
+/**
+ * A class to keep track of the authentication state for a given client.
+ */
+public abstract class AuthenticationClient extends ClientMonitor {
+    private long mOpId;
+
+    public abstract int handleFailedAttempt();
+    public abstract void resetFailedAttempts();
+
+    public static final int LOCKOUT_NONE = 0;
+    public static final int LOCKOUT_TIMED = 1;
+    public static final int LOCKOUT_PERMANENT = 2;
+
+    // Callback mechanism received from the client
+    // (BiometricPrompt -> FingerprintManager -> FingerprintService -> AuthenticationClient)
+    private IBiometricPromptReceiver mDialogReceiverFromClient;
+    private Bundle mBundle;
+    private IStatusBarService mStatusBarService;
+    private boolean mInLockout;
+    // TODO: BiometricManager, after other biometric modalities are introduced.
+    private final FingerprintManager mFingerprintManager;
+    protected boolean mDialogDismissed;
+
+    // Receives events from SystemUI and handles them before forwarding them to FingerprintDialog
+    protected IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
+        @Override // binder call
+        public void onDialogDismissed(int reason) {
+            if (mBundle != null && mDialogReceiverFromClient != null) {
+                try {
+                    mDialogReceiverFromClient.onDialogDismissed(reason);
+                    if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
+                        onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
+                                0 /* vendorCode */);
+                    }
+                    mDialogDismissed = true;
+                } catch (RemoteException e) {
+                    Slog.e(getLogTag(), "Unable to notify dialog dismissed", e);
+                }
+                stop(true /* initiatedByClient */);
+            }
+        }
+    };
+
+    /**
+     * This method is called when authentication starts.
+     */
+    public abstract void onStart();
+
+    /**
+     * This method is called when a fingerprint is authenticated or authentication is stopped
+     * (cancelled by the user, or an error such as lockout has occurred).
+     */
+    public abstract void onStop();
+
+    public AuthenticationClient(Context context, Metrics metrics,
+            BiometricService.DaemonWrapper daemon, long halDeviceId, IBinder token,
+            BiometricService.ServiceListener listener, int targetUserId, int groupId, long opId,
+            boolean restricted, String owner, Bundle bundle,
+            IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
+        super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
+                restricted, owner);
+        mOpId = opId;
+        mBundle = bundle;
+        mDialogReceiverFromClient = dialogReceiver;
+        mStatusBarService = statusBarService;
+        mFingerprintManager = (FingerprintManager) getContext()
+                .getSystemService(Context.FINGERPRINT_SERVICE);
+    }
+
+    @Override
+    public void binderDied() {
+        super.binderDied();
+        // When the binder dies, we should stop the client. This probably belongs in
+        // ClientMonitor's binderDied(), but testing all the cases would be tricky.
+        // AuthenticationClient is the most user-visible case.
+        stop(false /* initiatedByClient */);
+    }
+
+    @Override
+    public boolean onAcquired(int acquiredInfo, int vendorCode) {
+        // If the dialog is showing, the client doesn't need to receive onAcquired messages.
+        if (mBundle != null) {
+            try {
+                if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
+                    mStatusBarService.onBiometricHelp(
+                            mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
+                }
+                return false; // acquisition continues
+            } catch (RemoteException e) {
+                Slog.e(getLogTag(), "Remote exception when sending acquired message", e);
+                return true; // client failed
+            } finally {
+                // Good scans will keep the device awake
+                if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
+                    notifyUserActivity();
+                }
+            }
+        } else {
+            return super.onAcquired(acquiredInfo, vendorCode);
+        }
+    }
+
+    @Override
+    public boolean onError(long deviceId, int error, int vendorCode) {
+        if (mDialogDismissed) {
+            // If user cancels authentication, the application has already received the
+            // FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED message from onDialogDismissed()
+            // and stopped the fingerprint hardware, so there is no need to send a
+            // FingerprintManager.FINGERPRINT_ERROR_CANCELED message.
+            return true;
+        }
+        if (mBundle != null) {
+            try {
+                mStatusBarService.onBiometricError(
+                        mFingerprintManager.getErrorString(error, vendorCode));
+            } catch (RemoteException e) {
+                Slog.e(getLogTag(), "Remote exception when sending error", e);
+            }
+        }
+        return super.onError(deviceId, error, vendorCode);
+    }
+
+    @Override
+    public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+            boolean authenticated) {
+        boolean result = false;
+
+        // If the fingerprint dialog is showing, notify authentication succeeded
+        // TODO: this goes to BiometricPrompt, split between biometric modalities
+        if (mBundle != null) {
+            try {
+                if (authenticated) {
+                    mStatusBarService.onBiometricAuthenticated();
+                } else {
+                    mStatusBarService.onBiometricHelp(getContext().getResources().getString(
+                            com.android.internal.R.string.fingerprint_not_recognized));
+                }
+            } catch (RemoteException e) {
+                Slog.e(getLogTag(), "Failed to notify Authenticated:", e);
+            }
+        }
+
+        final BiometricService.ServiceListener listener = getListener();
+        if (listener != null) {
+            try {
+                mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
+                if (!authenticated) {
+                    listener.onAuthenticationFailed(getHalDeviceId());
+                } else {
+                    if (DEBUG) {
+                        Slog.v(getLogTag(), "onAuthenticated(owner=" + getOwnerString()
+                                + ", id=" + identifier.getBiometricId());
+                    }
+
+                    // Explicitly have if/else here to make it super obvious in case the code is
+                    // touched in the future.
+                    if (!getIsRestricted()) {
+                        listener.onAuthenticationSucceeded(
+                                getHalDeviceId(), identifier, getTargetUserId());
+                    } else {
+                        listener.onAuthenticationSucceeded(
+                                getHalDeviceId(), null, getTargetUserId());
+                    }
+                }
+            } catch (RemoteException e) {
+                Slog.w(getLogTag(), "Failed to notify Authenticated:", e);
+                result = true; // client failed
+            }
+        } else {
+            result = true; // client not listening
+        }
+        if (!authenticated) {
+            if (listener != null) {
+                vibrateError();
+            }
+            // allow system-defined limit of number of attempts before giving up
+            int lockoutMode =  handleFailedAttempt();
+            if (lockoutMode != LOCKOUT_NONE) {
+                try {
+                    mInLockout = true;
+                    Slog.w(getLogTag(), "Forcing lockout (fp driver code should do this!), mode(" +
+                            lockoutMode + ")");
+                    stop(false);
+                    int errorCode = lockoutMode == LOCKOUT_TIMED ?
+                            BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
+                            BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+
+                    // TODO: if the dialog is showing, this error should be delayed. On a similar
+                    // note, AuthenticationClient should override onError and delay all other errors
+                    // as well, if the dialog is showing
+                    listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
+
+                    // Send the lockout message to the system dialog
+                    if (mBundle != null) {
+                        mStatusBarService.onBiometricError(
+                                mFingerprintManager.getErrorString(errorCode, 0 /* vendorCode */));
+                    }
+                } catch (RemoteException e) {
+                    Slog.w(getLogTag(), "Failed to notify lockout:", e);
+                }
+            }
+            result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
+        } else {
+            if (listener != null) {
+                vibrateSuccess();
+            }
+            result |= true; // we have a valid fingerprint, done
+            resetFailedAttempts();
+            onStop();
+        }
+        return result;
+    }
+
+    /**
+     * Start authentication
+     */
+    @Override
+    public int start() {
+        onStart();
+        try {
+            final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
+            if (result != 0) {
+                Slog.w(getLogTag(), "startAuthentication failed, result=" + result);
+                mMetricsLogger.histogram(mMetrics.tagAuthStartError(), result);
+                onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+                        0 /* vendorCode */);
+                return result;
+            }
+            if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is authenticating...");
+
+            // If authenticating with system dialog, show the dialog
+            if (mBundle != null) {
+                try {
+                    mStatusBarService.showBiometricDialog(mBundle, mDialogReceiver);
+                } catch (RemoteException e) {
+                    Slog.e(getLogTag(), "Unable to show fingerprint dialog", e);
+                }
+            }
+        } catch (RemoteException e) {
+            Slog.e(getLogTag(), "startAuthentication failed", e);
+            return ERROR_ESRCH;
+        }
+        return 0; // success
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        if (mAlreadyCancelled) {
+            Slog.w(getLogTag(), "stopAuthentication: already cancelled!");
+            return 0;
+        }
+
+        onStop();
+
+        try {
+            final int result = getDaemonWrapper().cancel();
+            if (result != 0) {
+                Slog.w(getLogTag(), "stopAuthentication failed, result=" + result);
+                return result;
+            }
+            if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is no longer authenticating");
+        } catch (RemoteException e) {
+            Slog.e(getLogTag(), "stopAuthentication failed", e);
+            return ERROR_ESRCH;
+        } finally {
+            // If the user already cancelled authentication (via some interaction with the
+            // dialog, we do not need to hide it since it's already hidden.
+            // If the device is in lockout, don't hide the dialog - it will automatically hide
+            // after BiometricPrompt.HIDE_DIALOG_DELAY
+            if (mBundle != null && !mDialogDismissed && !mInLockout) {
+                try {
+                    mStatusBarService.hideBiometricDialog();
+                } catch (RemoteException e) {
+                    Slog.e(getLogTag(), "Unable to hide fingerprint dialog", e);
+                }
+            }
+        }
+
+        mAlreadyCancelled = true;
+        return 0; // success
+    }
+
+    @Override
+    public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
+            int remaining) {
+        if (DEBUG) Slog.w(getLogTag(), "onEnrollResult() called for authenticate!");
+        return true; // Invalid for Authenticate
+    }
+
+    @Override
+    public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
+        if (DEBUG) Slog.w(getLogTag(), "onRemoved() called for authenticate!");
+        return true; // Invalid for Authenticate
+    }
+
+    @Override
+    public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+            int remaining) {
+        if (DEBUG) Slog.w(getLogTag(), "onEnumerationResult() called for authenticate!");
+        return true; // Invalid for Authenticate
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/common/BiometricService.java b/services/core/java/com/android/server/biometrics/common/BiometricService.java
new file mode 100644
index 0000000..f54baef
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/BiometricService.java
@@ -0,0 +1,1101 @@
+/*
+ * 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.biometrics.common;
+
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
+import android.app.IActivityTaskManager;
+import android.app.PendingIntent;
+import android.app.SynchronousUserSwitchObserver;
+import android.app.TaskStackListener;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.IRemoteCallback;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.security.KeyStore;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.SystemService;
+import com.android.server.biometrics.face.FaceService;
+import com.android.server.biometrics.fingerprint.FingerprintService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Abstract base class containing all of the business logic for biometric services, e.g.
+ * Fingerprint, Face, Iris.
+ *
+ * @hide
+ */
+public abstract class BiometricService extends SystemService implements IHwBinder.DeathRecipient {
+
+    protected static final boolean DEBUG = true;
+
+    private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
+    private static final int MSG_USER_SWITCHING = 10;
+    private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000;
+    private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
+
+    private final Context mContext;
+    private final String mKeyguardPackage;
+    private final AppOpsManager mAppOps;
+    private final SparseBooleanArray mTimedLockoutCleared;
+    private final SparseIntArray mFailedAttempts;
+    private final IActivityTaskManager mActivityTaskManager;
+    private final AlarmManager mAlarmManager;
+    private final PowerManager mPowerManager;
+    private final UserManager mUserManager;
+    private final MetricsLogger mMetricsLogger;
+    private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
+    private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
+    private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
+    private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
+
+    protected final Map<Integer, Long> mAuthenticatorIds =
+            Collections.synchronizedMap(new HashMap<>());
+    protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
+            new ResetFailedAttemptsForUserRunnable();
+    protected final H mHandler = new H();
+
+    private ClientMonitor mCurrentClient;
+    private ClientMonitor mPendingClient;
+    private PerformanceStats mPerformanceStats;
+    protected int mCurrentUserId = UserHandle.USER_NULL;
+    // Normal authentications are tracked by mPerformanceMap.
+    protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
+    // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
+    protected HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
+
+    protected class PerformanceStats {
+        public int accept; // number of accepted biometrics
+        public int reject; // number of rejected biometrics
+        public int acquire; // total number of acquisitions. Should be >= accept+reject due to poor
+        // image acquisition in some cases (too fast, too slow, dirty sensor, etc.)
+        public int lockout; // total number of lockouts
+        public int permanentLockout; // total number of permanent lockouts
+    }
+
+    /**
+     * @return the log tag.
+     */
+    protected abstract String getTag();
+
+    /**
+     * @return the biometric utilities for a specific implementation.
+     */
+    protected abstract BiometricUtils getBiometricUtils();
+
+    /**
+     * @return the number of failed attempts after which the user will be temporarily locked out
+     *         from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
+     */
+    protected abstract int getFailedAttemptsLockoutTimed();
+
+    /**
+     * @return the number of failed attempts after which the user will be permanently locked out
+     *         from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
+     */
+    protected abstract int getFailedAttemptsLockoutPermanent();
+
+    /**
+     * @return the metrics constants for a biometric implementation.
+     */
+    protected abstract Metrics getMetrics();
+
+    /**
+     * @param userId
+     * @return true if the enrollment limit has been reached.
+     */
+    protected abstract boolean hasReachedEnrollmentLimit(int userId);
+
+    /**
+     * Notifies the HAL that the user has changed.
+     * @param userId
+     * @param clientPackage
+     */
+    protected abstract void updateActiveGroup(int userId, String clientPackage);
+
+    /**
+     * @return The protected intent to reset lockout for a specific biometric.
+     */
+    protected abstract String getLockoutResetIntent();
+
+    /**
+     * @return The permission the sender is required to have in order for the lockout reset intent
+     *         to be received by the BiometricService implementation.
+     */
+    protected abstract String getLockoutBroadcastPermission();
+
+    /**
+     * @return The HAL ID.
+     */
+    protected abstract long getHalDeviceId();
+
+    /**
+     * This method is called when the user switches. Implementations should probably notify the
+     * HAL.
+     * @param userId
+     */
+    protected abstract void handleUserSwitching(int userId);
+
+    /**
+     * @param userId
+     * @return Returns true if the user has any enrolled biometrics.
+     */
+    protected abstract boolean hasEnrolledBiometrics(int userId);
+
+    /**
+     * @return Returns the MANAGE_* permission string, which is required for enrollment, removal
+     * etc.
+     */
+    protected abstract String getManageBiometricPermission();
+
+    /**
+     * Checks if the caller has permission to use the biometric service - throws a SecurityException
+     * if not.
+     */
+    protected abstract void checkUseBiometricPermission();
+
+    /**
+     * @return Returns one of the {@link AppOpsManager} constants which pertains to the specific
+     *         biometric service.
+     */
+    protected abstract int getAppOp();
+
+
+    /**
+     * Notifies clients of any change in the biometric state (active / idle). This is mainly for
+     * Fingerprint navigation gestures.
+     * @param isActive
+     */
+    protected void notifyClientActiveCallbacks(boolean isActive) {}
+
+    protected class AuthenticationClientImpl extends AuthenticationClient {
+
+        public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+                IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
+                boolean restricted, String owner, Bundle bundle,
+                IBiometricPromptReceiver dialogReceiver,
+                IStatusBarService statusBarService) {
+            super(context, getMetrics(), daemon, halDeviceId, token, listener,
+                    targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver,
+                    statusBarService);
+        }
+
+        @Override
+        public void onStart() {
+            try {
+                mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Could not register task stack listener", e);
+            }
+        }
+
+        @Override
+        public void onStop() {
+            try {
+                mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Could not unregister task stack listener", e);
+            }
+        }
+
+        @Override
+        public void resetFailedAttempts() {
+            resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+                    ActivityManager.getCurrentUser());
+        }
+
+        @Override
+        public void notifyUserActivity() {
+            userActivity();
+        }
+
+        @Override
+        public int handleFailedAttempt() {
+            final int currentUser = ActivityManager.getCurrentUser();
+            mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
+            mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
+            final int lockoutMode = getLockoutMode();
+            if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
+                mPerformanceStats.permanentLockout++;
+            } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
+                mPerformanceStats.lockout++;
+            }
+
+            // Failing multiple times will continue to push out the lockout time
+            if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
+                scheduleLockoutResetForUser(currentUser);
+                return lockoutMode;
+            }
+            return AuthenticationClient.LOCKOUT_NONE;
+        }
+    }
+
+    protected class EnrollClientImpl extends EnrollClient {
+
+        public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+                IBinder token, ServiceListener listener, int userId, int groupId,
+                byte[] cryptoToken, boolean restricted, String owner) {
+            super(context, getMetrics(), daemon, halDeviceId, token, listener,
+                    userId, groupId, cryptoToken, restricted, owner, getBiometricUtils());
+        }
+
+        @Override
+        public void notifyUserActivity() {
+            userActivity();
+        }
+    }
+
+    protected class RemovalClientImpl extends RemovalClient {
+        private boolean mShouldNotify;
+
+        public RemovalClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+                IBinder token, ServiceListener listener, int fingerId, int groupId, int userId,
+                boolean restricted, String owner) {
+            super(context, getMetrics(), daemon, halDeviceId, token, listener, fingerId, groupId,
+                    userId, restricted, owner, getBiometricUtils());
+        }
+
+        public void setShouldNotifyUserActivity(boolean shouldNotify) {
+            mShouldNotify = shouldNotify;
+        }
+
+        @Override
+        public void notifyUserActivity() {
+            if (mShouldNotify) {
+                userActivity();
+            }
+        }
+    }
+
+    protected class EnumerateClientImpl extends EnumerateClient {
+
+        public EnumerateClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+                IBinder token, ServiceListener listener, int groupId, int userId,
+                boolean restricted, String owner) {
+            super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId,
+                    restricted, owner);
+        }
+
+        @Override
+        public void notifyUserActivity() {
+            userActivity();
+        }
+    }
+
+    /**
+     * Wraps the callback interface from Service -> Manager
+     */
+    protected interface ServiceListener {
+        void onEnrollResult(BiometricAuthenticator.Identifier identifier,
+                int remaining) throws RemoteException;
+
+        void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
+                throws RemoteException;
+
+        void onAuthenticationSucceeded(long deviceId,
+                BiometricAuthenticator.Identifier biometric, int userId)
+                throws RemoteException;
+
+        void onAuthenticationFailed(long deviceId)
+                throws RemoteException;
+
+        void onError(long deviceId, int error, int vendorCode)
+                throws RemoteException;
+
+        void onRemoved(BiometricAuthenticator.Identifier identifier,
+                int remaining) throws RemoteException;
+
+        void onEnumerated(BiometricAuthenticator.Identifier identifier,
+                int remaining) throws RemoteException;
+    }
+
+    /**
+     * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
+     * subclasses.
+     */
+    protected interface DaemonWrapper {
+        int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. see errno.h.
+        int authenticate(long operationId, int groupId) throws RemoteException;
+        int cancel() throws RemoteException;
+        int remove(int groupId, int biometricId) throws RemoteException;
+        int enumerate() throws RemoteException;
+        int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException;
+    }
+
+    /**
+     * Handler which all subclasses should post events to.
+     */
+    protected final class H extends Handler {
+        @Override
+        public void handleMessage(android.os.Message msg) {
+            switch (msg.what) {
+                case MSG_USER_SWITCHING:
+                    handleUserSwitching(msg.arg1);
+                    break;
+
+                default:
+                    Slog.w(getTag(), "Unknown message:" + msg.what);
+            }
+        }
+    }
+
+    private final class BiometricTaskStackListener extends TaskStackListener {
+        @Override
+        public void onTaskStackChanged() {
+            try {
+                if (!(mCurrentClient instanceof AuthenticationClient)) {
+                    return;
+                }
+                final String currentClient = mCurrentClient.getOwnerString();
+                if (isKeyguard(currentClient)) {
+                    return; // Keyguard is always allowed
+                }
+                List<ActivityManager.RunningTaskInfo> runningTasks =
+                        mActivityTaskManager.getTasks(1);
+                if (!runningTasks.isEmpty()) {
+                    final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+                    if (!topPackage.contentEquals(currentClient)) {
+                        Slog.e(getTag(), "Stopping background authentication, top: " + topPackage
+                                + " currentClient: " + currentClient);
+                        mCurrentClient.stop(false /* initiatedByClient */);
+                    }
+                }
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Unable to get running tasks", e);
+            }
+        }
+    }
+
+    private final class ResetClientStateRunnable implements Runnable {
+        @Override
+        public void run() {
+            /**
+             * Warning: if we get here, the driver never confirmed our call to cancel the current
+             * operation (authenticate, enroll, remove, enumerate, etc), which is
+             * really bad.  The result will be a 3-second delay in starting each new client.
+             * If you see this on a device, make certain the driver notifies with
+             * {@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} in response to cancel()
+             * once it has successfully switched to the IDLE state in the HAL.
+             * Additionally,{@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} should only be sent
+             * in response to an actual cancel() call.
+             */
+            Slog.w(getTag(), "Client "
+                    + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
+                    + " failed to respond to cancel, starting client "
+                    + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
+
+            mCurrentClient = null;
+            startClient(mPendingClient, false);
+        }
+    }
+
+    private final class LockoutReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Slog.v(getTag(), "Resetting lockout: " + intent.getAction());
+            if (getLockoutResetIntent().equals(intent.getAction())) {
+                final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
+                resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
+            }
+        }
+    }
+
+    private final class ResetFailedAttemptsForUserRunnable implements Runnable {
+        @Override
+        public void run() {
+            resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+                    ActivityManager.getCurrentUser());
+        }
+    }
+
+    private final class LockoutResetMonitor implements IBinder.DeathRecipient {
+        private static final long WAKELOCK_TIMEOUT_MS = 2000;
+        private final IBiometricServiceLockoutResetCallback mCallback;
+        private final PowerManager.WakeLock mWakeLock;
+
+        public LockoutResetMonitor(IBiometricServiceLockoutResetCallback callback) {
+            mCallback = callback;
+            mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                    "lockout reset callback");
+            try {
+                mCallback.asBinder().linkToDeath(LockoutResetMonitor.this, 0);
+            } catch (RemoteException e) {
+                Slog.w(getTag(), "caught remote exception in linkToDeath", e);
+            }
+        }
+
+        public void sendLockoutReset() {
+            if (mCallback != null) {
+                try {
+                    mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
+                    mCallback.onLockoutReset(getHalDeviceId(), new IRemoteCallback.Stub() {
+                        @Override
+                        public void sendResult(Bundle data) throws RemoteException {
+                            releaseWakelock();
+                        }
+                    });
+                } catch (DeadObjectException e) {
+                    Slog.w(getTag(), "Death object while invoking onLockoutReset: ", e);
+                    mHandler.post(mRemoveCallbackRunnable);
+                } catch (RemoteException e) {
+                    Slog.w(getTag(), "Failed to invoke onLockoutReset: ", e);
+                    releaseWakelock();
+                }
+            }
+        }
+
+        private final Runnable mRemoveCallbackRunnable = new Runnable() {
+            @Override
+            public void run() {
+                releaseWakelock();
+                removeLockoutResetCallback(LockoutResetMonitor.this);
+            }
+        };
+
+        @Override
+        public void binderDied() {
+            Slog.e(getTag(), "Lockout reset callback binder died");
+            mHandler.post(mRemoveCallbackRunnable);
+        }
+
+        private void releaseWakelock() {
+            if (mWakeLock.isHeld()) {
+                mWakeLock.release();
+            }
+        }
+    }
+
+    /**
+     * 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 BiometricService(Context context) {
+        super(context);
+        mContext = context;
+        mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
+                com.android.internal.R.string.config_keyguardComponent)).getPackageName();
+        mAppOps = context.getSystemService(AppOpsManager.class);
+        mTimedLockoutCleared = new SparseBooleanArray();
+        mFailedAttempts = new SparseIntArray();
+        mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
+                Context.ACTIVITY_TASK_SERVICE)).getService();
+        mPowerManager = mContext.getSystemService(PowerManager.class);
+        mAlarmManager = mContext.getSystemService(AlarmManager.class);
+        mUserManager = UserManager.get(mContext);
+        mMetricsLogger = new MetricsLogger();
+        mContext.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
+                getLockoutBroadcastPermission(), null /* handler */);
+    }
+
+    @Override
+    public void onStart() {
+        listenForUserSwitches();
+    }
+
+    @Override
+    public void serviceDied(long cookie) {
+        Slog.e(getTag(), "HAL died");
+        mMetricsLogger.count(getMetrics().tagHalDied(), 1);
+        handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+                0 /*vendorCode */);
+    }
+
+    protected ClientMonitor getCurrentClient() {
+        return mCurrentClient;
+    }
+
+    protected ClientMonitor getPendingClient() {
+        return mPendingClient;
+    }
+
+    /**
+     * Callback handlers from the daemon. The caller must put this on a handler.
+     */
+
+    protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
+            removeClient(client);
+        }
+        if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
+                && client instanceof AuthenticationClient) {
+            // ignore enrollment acquisitions or acquisitions when we're locked out
+            mPerformanceStats.acquire++;
+        }
+    }
+
+    protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
+            ArrayList<Byte> token) {
+        ClientMonitor client = mCurrentClient;
+        final boolean authenticated = identifier.getBiometricId() != 0;
+
+        if (authenticated) {
+            final byte[] byteToken = new byte[token.size()];
+            for (int i = 0; i < token.size(); i++) {
+                byteToken[i] = token.get(i);
+            }
+            KeyStore.getInstance().addAuthToken(byteToken);
+        }
+        if (client != null && client.onAuthenticated(identifier, authenticated)) {
+            removeClient(client);
+        }
+        if (authenticated) {
+            mPerformanceStats.accept++;
+        } else {
+            mPerformanceStats.reject++;
+        }
+    }
+
+    protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
+            int remaining) {
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onEnrollResult(identifier, remaining)) {
+            removeClient(client);
+            // When enrollment finishes, update this group's authenticator id, as the HAL has
+            // already generated a new authenticator id when the new biometric is enrolled.
+            if (identifier instanceof Fingerprint) {
+                updateActiveGroup(((Fingerprint)identifier).getGroupId(), null);
+            } else {
+                updateActiveGroup(mCurrentUserId, null);
+            }
+
+        }
+    }
+
+    protected void handleError(long deviceId, int error, int vendorCode) {
+        final ClientMonitor client = mCurrentClient;
+
+        if (DEBUG) Slog.v(getTag(), "handleError(client="
+                + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
+
+        if (client != null && client.onError(deviceId, error, vendorCode)) {
+            removeClient(client);
+        }
+
+        if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+            mHandler.removeCallbacks(mResetClientState);
+            if (mPendingClient != null) {
+                if (DEBUG) Slog.v(getTag(), "start pending client " + mPendingClient.getOwnerString());
+                startClient(mPendingClient, false);
+                mPendingClient = null;
+            }
+        }
+    }
+
+    protected void handleRemoved(BiometricAuthenticator.Identifier identifier,
+            final int remaining) {
+        if (DEBUG) Slog.w(getTag(), "Removed: fid=" + identifier.getBiometricId()
+                + ", dev=" + identifier.getDeviceId()
+                + ", rem=" + remaining);
+
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onRemoved(identifier, remaining)) {
+            removeClient(client);
+            // When the last biometric of a group is removed, update the authenticator id
+            int userId = mCurrentUserId;
+            if (identifier instanceof Fingerprint) {
+                userId = ((Fingerprint) identifier).getGroupId();
+            }
+            if (!hasEnrolledBiometrics(userId)) {
+                updateActiveGroup(userId, null);
+            }
+        }
+    }
+
+    /**
+     * Calls from the Manager. These are still on the calling binder's thread.
+     */
+
+    protected void enrollInternal(EnrollClientImpl client, int userId) {
+        if (hasReachedEnrollmentLimit(userId)) {
+            return;
+        }
+
+        // Group ID is arbitrarily set to parent profile user ID. It just represents
+        // the default biometrics for the user.
+        if (!isCurrentUserOrProfile(userId)) {
+            return;
+        }
+
+        mHandler.post(() -> {
+            startClient(client, true /* initiatedByClient */);
+        });
+    }
+
+    protected void cancelEnrollmentInternal(IBinder token) {
+        mHandler.post(() -> {
+            ClientMonitor client = mCurrentClient;
+            if (client instanceof EnrollClient && client.getToken() == token) {
+                client.stop(client.getToken() == token);
+            }
+        });
+    }
+
+    protected void authenticateInternal(AuthenticationClientImpl client, long opId,
+            String opPackageName) {
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        final int callingUserId = UserHandle.getCallingUserId();
+
+        if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
+                callingUserId)) {
+            if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
+            return;
+        }
+
+        mHandler.post(() -> {
+            mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
+
+            // Get performance stats object for this user.
+            HashMap<Integer, PerformanceStats> pmap
+                    = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
+            PerformanceStats stats = pmap.get(mCurrentUserId);
+            if (stats == null) {
+                stats = new PerformanceStats();
+                pmap.put(mCurrentUserId, stats);
+            }
+            mPerformanceStats = stats;
+
+            startAuthentication(client, opPackageName);
+        });
+    }
+
+    protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName) {
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        final int callingUserId = UserHandle.getCallingUserId();
+
+        if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
+                callingUserId)) {
+            if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
+            return;
+        }
+
+        mHandler.post(() -> {
+            ClientMonitor client = mCurrentClient;
+            if (client instanceof AuthenticationClient) {
+                if (client.getToken() == token) {
+                    if (DEBUG) Slog.v(getTag(), "stop client " + client.getOwnerString());
+                    client.stop(client.getToken() == token);
+                } else {
+                    if (DEBUG) Slog.v(getTag(), "can't stop client "
+                            + client.getOwnerString() + " since tokens don't match");
+                }
+            } else if (client != null) {
+                if (DEBUG) Slog.v(getTag(), "can't cancel non-authenticating client "
+                        + client.getOwnerString());
+            }
+        });
+    }
+
+    protected void setActiveUserInternal(int userId) {
+        mHandler.post(() -> {
+            updateActiveGroup(userId, null /* clientPackage */);
+        });
+    }
+
+    protected void removeInternal(RemovalClientImpl client) {
+        mHandler.post(() -> {
+            startClient(client, true /* initiatedByClient */);
+        });
+    }
+
+    protected void enumerateInternal(EnumerateClientImpl client) {
+        mHandler.post(() -> {
+            startClient(client, true /* initiatedByClient */);
+        });
+    }
+
+    // Should be done on a handler thread - not on the Binder's thread.
+    private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
+        updateActiveGroup(client.getGroupId(), opPackageName);
+
+        if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
+
+        int lockoutMode = getLockoutMode();
+        if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
+            Slog.v(getTag(), "In lockout mode(" + lockoutMode +
+                    ") ; disallowing authentication");
+            int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
+                    BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
+                    BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+            if (!client.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */)) {
+                Slog.w(getTag(), "Cannot send permanent lockout message to client");
+            }
+            return;
+        }
+        startClient(client, true /* initiatedByClient */);
+    }
+
+    protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) {
+        mHandler.post(() -> {
+           final LockoutResetMonitor monitor = new LockoutResetMonitor(callback);
+           if (!mLockoutMonitors.contains(monitor)) {
+               mLockoutMonitors.add(monitor);
+           }
+        });
+    }
+
+    /**
+     * Helper methods.
+     */
+
+    /**
+     * @param opPackageName name of package for caller
+     * @param requireForeground only allow this call while app is in the foreground
+     * @return true if caller can use the biometric API
+     */
+    protected boolean canUseBiometric(String opPackageName, boolean requireForeground, int uid,
+            int pid, int userId) {
+        checkUseBiometricPermission();
+
+        if (isKeyguard(opPackageName)) {
+            return true; // Keyguard is always allowed
+        }
+        if (!isCurrentUserOrProfile(userId)) {
+            Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile");
+            return false;
+        }
+        if (mAppOps.noteOp(getAppOp(), uid, opPackageName) != AppOpsManager.MODE_ALLOWED) {
+            Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied");
+            return false;
+        }
+        if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
+                opPackageName))) {
+            Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * @param opPackageName package of the caller
+     * @return true if this is the same client currently using the biometric
+     */
+    private boolean isCurrentClient(String opPackageName) {
+        return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
+    }
+
+    /**
+     * @return true if this is keyguard package
+     */
+    private boolean isKeyguard(String clientPackage) {
+        return mKeyguardPackage.equals(clientPackage);
+    }
+
+    private int getLockoutMode() {
+        final int currentUser = ActivityManager.getCurrentUser();
+        final int failedAttempts = mFailedAttempts.get(currentUser, 0);
+        if (failedAttempts >= getFailedAttemptsLockoutPermanent()) {
+            return AuthenticationClient.LOCKOUT_PERMANENT;
+        } else if (failedAttempts > 0 &&
+                mTimedLockoutCleared.get(currentUser, false) == false
+                && (failedAttempts % getFailedAttemptsLockoutTimed() == 0)) {
+            return AuthenticationClient.LOCKOUT_TIMED;
+        }
+        return AuthenticationClient.LOCKOUT_NONE;
+    }
+
+    private boolean isForegroundActivity(int uid, int pid) {
+        try {
+            List<ActivityManager.RunningAppProcessInfo> procs =
+                    ActivityManager.getService().getRunningAppProcesses();
+            int N = procs.size();
+            for (int i = 0; i < N; i++) {
+                ActivityManager.RunningAppProcessInfo proc = procs.get(i);
+                if (proc.pid == pid && proc.uid == uid
+                        && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
+                    return true;
+                }
+            }
+        } catch (RemoteException e) {
+            Slog.w(getTag(), "am.getRunningAppProcesses() failed");
+        }
+        return false;
+    }
+
+    /**
+     * Calls the HAL to switch states to the new task. If there's already a current task,
+     * it calls cancel() and sets mPendingClient to begin when the current task finishes
+     * ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
+     *
+     * @param newClient the new client that wants to connect
+     * @param initiatedByClient true for authenticate, remove and enroll
+     */
+    private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
+        ClientMonitor currentClient = mCurrentClient;
+        if (currentClient != null) {
+            if (DEBUG) Slog.v(getTag(), "request stop current client " +
+                    currentClient.getOwnerString());
+
+            // This check only matters for FingerprintService, since enumerate may call back
+            // multiple times.
+            if (currentClient instanceof FingerprintService.EnumerateClientImpl ||
+                    currentClient instanceof FingerprintService.RemovalClientImpl) {
+                // This condition means we're currently running internal diagnostics to
+                // remove extra fingerprints in the hardware and/or the software
+                // TODO: design an escape hatch in case client never finishes
+                if (newClient != null) {
+                    Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
+                            + newClient.getClass().getSuperclass().getSimpleName()
+                            + "(" + newClient.getOwnerString() + ")"
+                            + ", initiatedByClient = " + initiatedByClient);
+                }
+            } else {
+                currentClient.stop(initiatedByClient);
+            }
+            mPendingClient = newClient;
+            mHandler.removeCallbacks(mResetClientState);
+            mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
+        } else if (newClient != null) {
+            mCurrentClient = newClient;
+            if (DEBUG) Slog.v(getTag(), "starting client "
+                    + newClient.getClass().getSuperclass().getSimpleName()
+                    + "(" + newClient.getOwnerString() + ")"
+                    + ", initiatedByClient = " + initiatedByClient);
+            notifyClientActiveCallbacks(true);
+
+            newClient.start();
+        }
+    }
+
+    protected void removeClient(ClientMonitor client) {
+        if (client != null) {
+            client.destroy();
+            if (client != mCurrentClient && mCurrentClient != null) {
+                Slog.w(getTag(), "Unexpected client: " + client.getOwnerString() + "expected: "
+                        + mCurrentClient.getOwnerString());
+            }
+        }
+        if (mCurrentClient != null) {
+            if (DEBUG) Slog.v(getTag(), "Done with client: " + client.getOwnerString());
+            mCurrentClient = null;
+        }
+        if (mPendingClient == null) {
+            notifyClientActiveCallbacks(false);
+        }
+    }
+
+    /**
+     * Populates existing authenticator ids. To be used only during the start of the service.
+     */
+    protected void loadAuthenticatorIds() {
+        // This operation can be expensive, so keep track of the elapsed time. Might need to move to
+        // background if it takes too long.
+        long t = System.currentTimeMillis();
+        mAuthenticatorIds.clear();
+        for (UserInfo user : UserManager.get(getContext()).getUsers(true /* excludeDying */)) {
+            int userId = getUserOrWorkProfileId(null, user.id);
+            if (!mAuthenticatorIds.containsKey(userId)) {
+                updateActiveGroup(userId, null);
+            }
+        }
+
+        t = System.currentTimeMillis() - t;
+        if (t > 1000) {
+            Slog.w(getTag(), "loadAuthenticatorIds() taking too long: " + t + "ms");
+        }
+    }
+
+    /**
+     * @param clientPackage the package of the caller
+     * @return the profile id
+     */
+    protected int getUserOrWorkProfileId(String clientPackage, int userId) {
+        if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
+            return userId;
+        }
+        return getEffectiveUserId(userId);
+    }
+
+    protected boolean isRestricted() {
+        // Only give privileged apps (like Settings) access to biometric info
+        final boolean restricted = !hasPermission(getManageBiometricPermission());
+        return restricted;
+    }
+
+    protected boolean hasPermission(String permission) {
+        return getContext().checkCallingOrSelfPermission(permission)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    protected void checkPermission(String permission) {
+        getContext().enforceCallingOrSelfPermission(permission,
+                "Must have " + permission + " permission.");
+    }
+
+    protected boolean isCurrentUserOrProfile(int userId) {
+        UserManager um = UserManager.get(mContext);
+        if (um == null) {
+            Slog.e(getTag(), "Unable to acquire UserManager");
+            return false;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            // Allow current user or profiles of the current user...
+            for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
+                if (profileId == userId) {
+                    return true;
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+
+        return false;
+    }
+
+    /***
+     * @param opPackageName the name of the calling package
+     * @return authenticator id for the calling user
+     */
+    protected long getAuthenticatorId(String opPackageName) {
+        final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
+        return mAuthenticatorIds.getOrDefault(userId, 0L);
+    }
+
+    private void scheduleLockoutResetForUser(int userId) {
+        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
+                getLockoutResetIntentForUser(userId));
+    }
+
+    private PendingIntent getLockoutResetIntentForUser(int userId) {
+        return PendingIntent.getBroadcast(mContext, userId,
+                new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+
+    private void userActivity() {
+        long now = SystemClock.uptimeMillis();
+        mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
+    }
+
+    /**
+     * @param userId
+     * @return true if this is a work profile
+     */
+    private boolean isWorkProfile(int userId) {
+        UserInfo userInfo = null;
+        final long token = Binder.clearCallingIdentity();
+        try {
+            userInfo = mUserManager.getUserInfo(userId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        return userInfo != null && userInfo.isManagedProfile();
+    }
+
+
+    private int getEffectiveUserId(int userId) {
+        UserManager um = UserManager.get(mContext);
+        if (um != null) {
+            final long callingIdentity = Binder.clearCallingIdentity();
+            userId = um.getCredentialOwnerProfile(userId);
+            Binder.restoreCallingIdentity(callingIdentity);
+        } else {
+            Slog.e(getTag(), "Unable to acquire UserManager");
+        }
+        return userId;
+    }
+
+    // Attempt counter should only be cleared when Keyguard goes away or when
+    // a biometric is successfully authenticated.
+    private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
+        if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
+            Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter);
+        }
+        if (clearAttemptCounter) {
+            mFailedAttempts.put(userId, 0);
+        }
+        mTimedLockoutCleared.put(userId, true);
+        // If we're asked to reset failed attempts externally (i.e. from Keyguard),
+        // the alarm might still be pending; remove it.
+        cancelLockoutResetForUser(userId);
+        notifyLockoutResetMonitors();
+    }
+
+    private void cancelLockoutResetForUser(int userId) {
+        mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
+    }
+
+    private void listenForUserSwitches() {
+        try {
+            ActivityManager.getService().registerUserSwitchObserver(
+                    new SynchronousUserSwitchObserver() {
+                        @Override
+                        public void onUserSwitching(int newUserId) throws RemoteException {
+                            mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
+                                    .sendToTarget();
+                        }
+                    }, getTag());
+        } catch (RemoteException e) {
+            Slog.w(getTag(), "Failed to listen for user switching event" ,e);
+        }
+    }
+
+    private void notifyLockoutResetMonitors() {
+        for (int i = 0; i < mLockoutMonitors.size(); i++) {
+            mLockoutMonitors.get(i).sendLockoutReset();
+        }
+    }
+
+    private void removeLockoutResetCallback(
+            LockoutResetMonitor monitor) {
+        mLockoutMonitors.remove(monitor);
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/common/BiometricUserState.java b/services/core/java/com/android/server/biometrics/common/BiometricUserState.java
new file mode 100644
index 0000000..53eaac0
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/BiometricUserState.java
@@ -0,0 +1,213 @@
+/*
+ * 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.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Abstract base class for managing biometrics per user across device reboots.
+ * @hide
+ */
+public abstract class BiometricUserState<T extends BiometricAuthenticator.Identifier> {
+    private static final String TAG = "UserState";
+
+    @GuardedBy("this")
+    protected final ArrayList<T> mBiometrics = new ArrayList<>();
+    protected final Context mContext;
+    protected final File mFile;
+
+    private final Runnable mWriteStateRunnable = new Runnable() {
+        @Override
+        public void run() {
+            doWriteState();
+        }
+    };
+
+    /**
+     * @return The tag for the biometrics. There may be multiple instances of a biometric within.
+     */
+    protected abstract String getBiometricsTag();
+
+    /**
+     * @return The file where the biometric metadata should be stored.
+     */
+    protected abstract String getBiometricFile();
+
+    /**
+     * @return The resource for the name template, this is used to generate the default name.
+     */
+    protected abstract int getNameTemplateResource();
+
+    /**
+     * @return A copy of the list.
+     */
+    protected abstract ArrayList<T> getCopy(ArrayList<T> array);
+
+    /**
+     * @return Writes the cached data to persistent storage.
+     */
+    protected abstract void doWriteState();
+
+    /**
+     * @return
+     */
+    protected abstract void parseBiometricsLocked(XmlPullParser parser)
+            throws IOException, XmlPullParserException;
+
+
+    public BiometricUserState(Context context, int userId) {
+        mFile = getFileForUser(userId);
+        mContext = context;
+        synchronized (this) {
+            readStateSyncLocked();
+        }
+    }
+
+    public void addBiometric(T identifier) {
+        synchronized (this) {
+            mBiometrics.add(identifier);
+            scheduleWriteStateLocked();
+        }
+    }
+
+    public void removeBiometric(int biometricId) {
+        synchronized (this) {
+            for (int i = 0; i < mBiometrics.size(); i++) {
+                if (mBiometrics.get(i).getBiometricId() == biometricId) {
+                    mBiometrics.remove(i);
+                    scheduleWriteStateLocked();
+                    break;
+                }
+            }
+        }
+    }
+
+    public void renameBiometric(int biometricId, CharSequence name) {
+        synchronized (this) {
+            for (int i = 0; i < mBiometrics.size(); i++) {
+                if (mBiometrics.get(i).getBiometricId() == biometricId) {
+                    BiometricAuthenticator.Identifier identifier = mBiometrics.get(i);
+                    identifier.setName(name);
+                    scheduleWriteStateLocked();
+                    break;
+                }
+            }
+        }
+    }
+
+    public List<T> getBiometrics() {
+        synchronized (this) {
+            return getCopy(mBiometrics);
+        }
+    }
+
+    /**
+     * Finds a unique name for the given fingerprint
+     * @return unique name
+     */
+    public String getUniqueName() {
+        int guess = 1;
+        while (true) {
+            // Not the most efficient algorithm in the world, but there shouldn't be more than 10
+            String name = mContext.getString(getNameTemplateResource(), guess);
+            if (isUnique(name)) {
+                return name;
+            }
+            guess++;
+        }
+    }
+
+    private boolean isUnique(String name) {
+        for (T identifier : mBiometrics) {
+            if (identifier.getName().equals(name)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private File getFileForUser(int userId) {
+        return new File(Environment.getUserSystemDirectory(userId), getBiometricFile());
+    }
+
+    private void scheduleWriteStateLocked() {
+        AsyncTask.execute(mWriteStateRunnable);
+    }
+
+    @GuardedBy("this")
+    private void readStateSyncLocked() {
+        FileInputStream in;
+        if (!mFile.exists()) {
+            return;
+        }
+        try {
+            in = new FileInputStream(mFile);
+        } catch (FileNotFoundException fnfe) {
+            Slog.i(TAG, "No fingerprint state");
+            return;
+        }
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(in, null);
+            parseStateLocked(parser);
+
+        } catch (XmlPullParserException | IOException e) {
+            throw new IllegalStateException("Failed parsing settings file: "
+                    + mFile , e);
+        } finally {
+            IoUtils.closeQuietly(in);
+        }
+    }
+
+    @GuardedBy("this")
+    private void parseStateLocked(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals(getBiometricsTag())) {
+                parseBiometricsLocked(parser);
+            }
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/biometrics/common/BiometricUtils.java b/services/core/java/com/android/server/biometrics/common/BiometricUtils.java
new file mode 100644
index 0000000..8f076f1
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/BiometricUtils.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.server.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+
+import java.util.List;
+
+/**
+ * Interface for utilities managing biometrics and their relevant settings.
+ * @hide
+ */
+public interface BiometricUtils<T extends BiometricAuthenticator.Identifier> {
+    List<T> getBiometricsForUser(Context context, int userId);
+    void addBiometricForUser(Context context, int userId, T identifier);
+    void removeBiometricForUser(Context context, int userId, int biometricId);
+    void renameBiometricForUser(Context context, int userId, int biometricId, CharSequence name);
+    CharSequence getUniqueName(Context context, int userId);
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/common/ClientMonitor.java b/services/core/java/com/android/server/biometrics/common/ClientMonitor.java
new file mode 100644
index 0000000..1486754
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/ClientMonitor.java
@@ -0,0 +1,260 @@
+/*
+ * 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.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.media.AudioAttributes;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.util.Slog;
+
+import com.android.internal.logging.MetricsLogger;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Abstract base class for keeping track and dispatching events from the biometric's HAL to the
+ * the current client.  Subclasses are responsible for coordinating the interaction with
+ * the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
+ */
+public abstract class ClientMonitor implements IBinder.DeathRecipient {
+    protected static final int ERROR_ESRCH = 3; // Likely HAL is dead. See errno.h.
+    protected static final boolean DEBUG = BiometricService.DEBUG;
+    private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
+            new AudioAttributes.Builder()
+                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+                    .build();
+
+    private final Context mContext;
+    private final long mHalDeviceId;
+    private final int mTargetUserId;
+    private final int mGroupId;
+    // True if client does not have MANAGE_FINGERPRINT permission
+    private final boolean mIsRestricted;
+    private final String mOwner;
+    private final VibrationEffect mSuccessVibrationEffect;
+    private final VibrationEffect mErrorVibrationEffect;
+    private final BiometricService.DaemonWrapper mDaemon;
+
+    private IBinder mToken;
+    private BiometricService.ServiceListener mListener;
+
+    protected final MetricsLogger mMetricsLogger;
+    protected final Metrics mMetrics;
+
+    protected boolean mAlreadyCancelled;
+
+    /**
+     * @param context context of BiometricService
+     * @param daemon interface to call back to a specific biometric's daemon
+     * @param halDeviceId the HAL device ID of the associated biometric hardware
+     * @param token a unique token for the client
+     * @param listener recipient of related events (e.g. authentication)
+     * @param userId target user id for operation
+     * @param groupId groupId for the fingerprint set
+     * @param restricted whether or not client has the MANAGE_* permission
+     * permission
+     * @param owner name of the client that owns this
+     */
+    public ClientMonitor(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
+            long halDeviceId, IBinder token, BiometricService.ServiceListener listener, int userId,
+            int groupId, boolean restricted, String owner) {
+        mContext = context;
+        mMetrics = metrics;
+        mDaemon = daemon;
+        mHalDeviceId = halDeviceId;
+        mToken = token;
+        mListener = listener;
+        mTargetUserId = userId;
+        mGroupId = groupId;
+        mIsRestricted = restricted;
+        mOwner = owner;
+        mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+        mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+        mMetricsLogger = new MetricsLogger();
+        try {
+            if (token != null) {
+                token.linkToDeath(this, 0);
+            }
+        } catch (RemoteException e) {
+            Slog.w(getLogTag(), "caught remote exception in linkToDeath: ", e);
+        }
+    }
+
+    protected String getLogTag() {
+        return mMetrics.logTag();
+    }
+
+    /**
+     * Contacts the biometric's HAL to start the client.
+     * @return 0 on success, errno from driver on failure
+     */
+    public abstract int start();
+
+    /**
+     * Contacts the biometric's HAL to stop the client.
+     * @param initiatedByClient whether the operation is at the request of a client
+     */
+    public abstract int stop(boolean initiatedByClient);
+
+    /**
+     * Method to explicitly poke powermanager on events
+     */
+    public abstract void notifyUserActivity();
+
+    // Event callbacks from driver. Inappropriate calls is flagged/logged by the
+    // respective client (e.g. enrolling shouldn't get authenticate events).
+    // All of these return 'true' if the operation is completed and it's ok to move
+    // to the next client (e.g. authentication accepts or rejects a biometric).
+    public abstract boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
+            int remaining);
+    public abstract boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+            boolean authenticated);
+    public abstract boolean onRemoved(BiometricAuthenticator.Identifier identifier,
+            int remaining);
+    public abstract boolean onEnumerationResult(
+            BiometricAuthenticator.Identifier identifier, int remaining);
+
+    /**
+     * Called when we get notification from the biometric's HAL that an image has been acquired.
+     * Common to authenticate and enroll.
+     * @param acquiredInfo info about the current image acquisition
+     * @return true if client should be removed
+     */
+    public boolean onAcquired(int acquiredInfo, int vendorCode) {
+        try {
+            if (mListener != null) {
+                mListener.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
+            }
+            return false; // acquisition continues...
+        } catch (RemoteException e) {
+            Slog.w(getLogTag(), "Failed to invoke sendAcquired", e);
+            return true;
+        } finally {
+            // Good scans will keep the device awake
+            if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
+                notifyUserActivity();
+            }
+        }
+    }
+
+    /**
+     * Called when we get notification from the biometric's HAL that an error has occurred with the
+     * current operation. Common to authenticate, enroll, enumerate and remove.
+     * @param error
+     * @return true if client should be removed
+     */
+    public boolean onError(long deviceId, int error, int vendorCode) {
+        try {
+            if (mListener != null) {
+                mListener.onError(deviceId, error, vendorCode);
+            }
+        } catch (RemoteException e) {
+            Slog.w(getLogTag(), "Failed to invoke sendError", e);
+        }
+        return true; // errors always remove current client
+    }
+
+    public void destroy() {
+        if (mToken != null) {
+            try {
+                mToken.unlinkToDeath(this, 0);
+            } catch (NoSuchElementException e) {
+                // TODO: remove when duplicate call bug is found
+                Slog.e(getLogTag(), "destroy(): " + this + ":", new Exception("here"));
+            }
+            mToken = null;
+        }
+        mListener = null;
+    }
+
+    @Override
+    public void binderDied() {
+        mToken = null;
+        mListener = null;
+        onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+                0 /* vendorCode */);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mToken != null) {
+                if (DEBUG) Slog.w(getLogTag(), "removing leaked reference: " + mToken);
+                onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+                        0 /* vendorCode */);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    public final Context getContext() {
+        return mContext;
+    }
+
+    public final long getHalDeviceId() {
+        return mHalDeviceId;
+    }
+
+    public final String getOwnerString() {
+        return mOwner;
+    }
+
+    public final BiometricService.ServiceListener getListener() {
+        return mListener;
+    }
+
+    public final BiometricService.DaemonWrapper getDaemonWrapper() {
+        return mDaemon;
+    }
+
+    public final boolean getIsRestricted() {
+        return mIsRestricted;
+    }
+
+    public final int getTargetUserId() {
+        return mTargetUserId;
+    }
+
+    public final int getGroupId() {
+        return mGroupId;
+    }
+
+    public final IBinder getToken() {
+        return mToken;
+    }
+
+    public final void vibrateSuccess() {
+        Vibrator vibrator = mContext.getSystemService(Vibrator.class);
+        if (vibrator != null) {
+            vibrator.vibrate(mSuccessVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
+        }
+    }
+
+    public final void vibrateError() {
+        Vibrator vibrator = mContext.getSystemService(Vibrator.class);
+        if (vibrator != null) {
+            vibrator.vibrate(mErrorVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/common/EnrollClient.java b/services/core/java/com/android/server/biometrics/common/EnrollClient.java
new file mode 100644
index 0000000..aee772b
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/EnrollClient.java
@@ -0,0 +1,134 @@
+/*
+ * 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.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Arrays;
+
+/**
+ * A class to keep track of the enrollment state for a given client.
+ */
+public abstract class EnrollClient extends ClientMonitor {
+    private static final long MS_PER_SEC = 1000;
+    private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
+    private final byte[] mCryptoToken;
+    private final BiometricUtils mBiometricUtils;
+
+    public EnrollClient(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
+            long halDeviceId, IBinder token, BiometricService.ServiceListener listener, int userId,
+            int groupId, byte[] cryptoToken, boolean restricted, String owner,
+            BiometricUtils utils) {
+        super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+                owner);
+        mBiometricUtils = utils;
+        mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
+    }
+
+    @Override
+    public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
+            int remaining) {
+        if (remaining == 0) {
+            mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
+        }
+        return sendEnrollResult(identifier, remaining);
+    }
+
+    /*
+     * @return true if we're done.
+     */
+    private boolean sendEnrollResult(BiometricAuthenticator.Identifier identifier,
+            int remaining) {
+        vibrateSuccess();
+        mMetricsLogger.action(mMetrics.actionBiometricEnroll());
+        try {
+            getListener().onEnrollResult(identifier, remaining);
+            return remaining == 0;
+        } catch (RemoteException e) {
+            Slog.w(getLogTag(), "Failed to notify EnrollResult:", e);
+            return true;
+        }
+    }
+
+    @Override
+    public int start() {
+        final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
+        try {
+            final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), timeout);
+            if (result != 0) {
+                Slog.w(getLogTag(), "startEnroll failed, result=" + result);
+                mMetricsLogger.histogram(mMetrics.tagEnrollStartError(), result);
+                onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+                        0 /* vendorCode */);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(getLogTag(), "startEnroll failed", e);
+        }
+        return 0; // success
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        if (mAlreadyCancelled) {
+            Slog.w(getLogTag(), "stopEnroll: already cancelled!");
+            return 0;
+        }
+
+        try {
+            final int result = getDaemonWrapper().cancel();
+            if (result != 0) {
+                Slog.w(getLogTag(), "startEnrollCancel failed, result = " + result);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(getLogTag(), "stopEnrollment failed", e);
+        }
+        if (initiatedByClient) {
+            onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+                    0 /* vendorCode */);
+        }
+        mAlreadyCancelled = true;
+        return 0;
+    }
+
+    @Override
+    public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
+        if (DEBUG) Slog.w(getLogTag(), "onRemoved() called for enroll!");
+        return true; // Invalid for EnrollClient
+    }
+
+    @Override
+    public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+            int remaining) {
+        if (DEBUG) Slog.w(getLogTag(), "onEnumerationResult() called for enroll!");
+        return true; // Invalid for EnrollClient
+    }
+
+    @Override
+    public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+            boolean authenticated) {
+        if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for enroll!");
+        return true; // Invalid for EnrollClient
+    }
+
+}
diff --git a/services/core/java/com/android/server/biometrics/common/EnumerateClient.java b/services/core/java/com/android/server/biometrics/common/EnumerateClient.java
new file mode 100644
index 0000000..ee40ee9
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/EnumerateClient.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 com.android.server.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * A class to keep track of the enumeration state for a given client.
+ */
+public abstract class EnumerateClient extends ClientMonitor {
+    public EnumerateClient(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
+            long halDeviceId, IBinder token, BiometricService.ServiceListener listener, int groupId,
+            int userId, boolean restricted, String owner) {
+        super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+                owner);
+    }
+
+    @Override
+    public int start() {
+        // The biometric template ids will be removed when we get confirmation from the HAL
+        try {
+            final int result = getDaemonWrapper().enumerate();
+            if (result != 0) {
+                Slog.w(getLogTag(), "start enumerate for user " + getTargetUserId()
+                    + " failed, result=" + result);
+                mMetricsLogger.histogram(mMetrics.tagEnumerateStartError(), result);
+                onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+                        0 /* vendorCode */);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(getLogTag(), "startEnumeration failed", e);
+        }
+        return 0;
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        if (mAlreadyCancelled) {
+            Slog.w(getLogTag(), "stopEnumerate: already cancelled!");
+            return 0;
+        }
+
+        try {
+            final int result = getDaemonWrapper().cancel();
+            if (result != 0) {
+                Slog.w(getLogTag(), "stop enumeration failed, result=" + result);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(getLogTag(), "stopEnumeration failed", e);
+            return ERROR_ESRCH;
+        }
+
+        // We don't actually stop enumerate, but inform the client that the cancel operation
+        // succeeded so we can start the next operation.
+        if (initiatedByClient) {
+            onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+                    0 /* vendorCode */);
+        }
+        mAlreadyCancelled = true;
+        return 0; // success
+    }
+
+    @Override
+    public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+            int remaining) {
+        try {
+            getListener().onEnumerated(identifier, remaining);
+        } catch (RemoteException e) {
+            Slog.w(getLogTag(), "Failed to notify enumerated:", e);
+        }
+        return remaining == 0;
+    }
+
+    @Override
+    public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+            boolean authenticated) {
+        if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for enumerate!");
+        return true; // Invalid for Enumerate.
+    }
+
+    @Override
+    public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier, int rem) {
+        if (DEBUG) Slog.w(getLogTag(), "onEnrollResult() called for enumerate!");
+        return true; // Invalid for Enumerate.
+    }
+
+    @Override
+    public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
+        if (DEBUG) Slog.w(getLogTag(), "onRemoved() called for enumerate!");
+        return true; // Invalid for Enumerate.
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/common/Metrics.java b/services/core/java/com/android/server/biometrics/common/Metrics.java
new file mode 100644
index 0000000..eb1a1f8
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/Metrics.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.server.biometrics.common;
+
+public interface Metrics {
+    /** The log tag */
+    String logTag();
+
+    /** Strings for MetricsLogger.count() and MetricsLogger.histogram() */
+    String tagHalDied();
+    String tagAuthToken();
+    String tagAuthStartError();
+    String tagEnrollStartError();
+    String tagEnumerateStartError();
+    String tagRemoveStartError();
+
+    /** Integers for MetricsLogger.action() */
+    int actionBiometricAuth();
+    int actionBiometricEnroll();
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/common/RemovalClient.java b/services/core/java/com/android/server/biometrics/common/RemovalClient.java
new file mode 100644
index 0000000..27c42ab
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/RemovalClient.java
@@ -0,0 +1,125 @@
+/*
+ * 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.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * A class to keep track of the remove state for a given client.
+ */
+public abstract class RemovalClient extends ClientMonitor {
+    private final int mBiometricId;
+    private final BiometricUtils mBiometricUtils;
+
+    public RemovalClient(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
+            long halDeviceId, IBinder token, BiometricService.ServiceListener listener,
+            int biometricId, int groupId, int userId, boolean restricted, String owner,
+            BiometricUtils utils) {
+        super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+                owner);
+        mBiometricId = biometricId;
+        mBiometricUtils = utils;
+    }
+
+    @Override
+    public int start() {
+        // The biometric template ids will be removed when we get confirmation from the HAL
+        try {
+            final int result = getDaemonWrapper().remove(getGroupId(), mBiometricId);
+            if (result != 0) {
+                Slog.w(getLogTag(), "startRemove with id = " + mBiometricId + " failed, result=" +
+                        result);
+                mMetricsLogger.histogram(mMetrics.tagRemoveStartError(), result);
+                onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+                        0 /* vendorCode */);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(getLogTag(), "startRemove failed", e);
+        }
+        return 0;
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        if (mAlreadyCancelled) {
+            Slog.w(getLogTag(), "stopRemove: already cancelled!");
+            return 0;
+        }
+
+        try {
+            final int result = getDaemonWrapper().cancel();
+            if (result != 0) {
+                Slog.w(getLogTag(), "stopRemoval failed, result=" + result);
+                return result;
+            }
+            if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is no longer removing");
+        } catch (RemoteException e) {
+            Slog.e(getLogTag(), "stopRemoval failed", e);
+            return ERROR_ESRCH;
+        }
+        mAlreadyCancelled = true;
+        return 0; // success
+    }
+
+    /*
+     * @return true if we're done.
+     */
+    private boolean sendRemoved(BiometricAuthenticator.Identifier identifier,
+            int remaining) {
+        try {
+            getListener().onRemoved(identifier, remaining);
+        } catch (RemoteException e) {
+            Slog.w(getLogTag(), "Failed to notify Removed:", e);
+        }
+        return remaining == 0;
+    }
+
+    @Override
+    public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
+        if (identifier.getBiometricId() != 0) {
+            mBiometricUtils.removeBiometricForUser(getContext(), getTargetUserId(),
+                    identifier.getBiometricId());
+        }
+        return sendRemoved(identifier, remaining);
+    }
+
+    @Override
+    public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier, int rem) {
+        if (DEBUG) Slog.w(getLogTag(), "onEnrollResult() called for remove!");
+        return true; // Invalid for Remove
+    }
+
+    @Override
+    public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+            boolean authenticated) {
+        if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for remove!");
+        return true; // Invalid for Remove.
+    }
+
+    @Override
+    public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+            int remaining) {
+        if (DEBUG) Slog.w(getLogTag(), "onEnumerationResult() called for remove!");
+        return true; // Invalid for Remove.
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceMetrics.java b/services/core/java/com/android/server/biometrics/face/FaceMetrics.java
new file mode 100644
index 0000000..4c907b1
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceMetrics.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.biometrics.face;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.biometrics.common.Metrics;
+
+public class FaceMetrics implements Metrics {
+    @Override
+    public String logTag() {
+        return FaceService.TAG;
+    }
+
+    @Override
+    public String tagHalDied() {
+        return "faced_died";
+    }
+
+    @Override
+    public String tagAuthToken() {
+        return "face_token";
+    }
+
+    @Override
+    public String tagAuthStartError() {
+        return "faced_auth_start_error";
+    }
+
+    @Override
+    public String tagEnrollStartError() {
+        return "faced_enroll_start_error";
+    }
+
+    @Override
+    public String tagEnumerateStartError() {
+        return "faced_enum_start_error";
+    }
+
+    @Override
+    public String tagRemoveStartError() {
+        return "faced_remove_start_error";
+    }
+
+    @Override
+    public int actionBiometricAuth() {
+        return MetricsProto.MetricsEvent.ACTION_FACE_AUTH;
+    }
+
+    @Override
+    public int actionBiometricEnroll() {
+        return MetricsProto.MetricsEvent.ACTION_FACE_ENROLL;
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
new file mode 100644
index 0000000..f8ccef5
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -0,0 +1,786 @@
+/*
+ * 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.biometrics.face;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_FACE;
+import static android.Manifest.permission.RESET_FACE_LOCKOUT;
+import static android.Manifest.permission.USE_BIOMETRIC;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
+import android.hardware.face.Face;
+import android.hardware.face.IFaceService;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.common.BiometricService;
+import com.android.server.biometrics.common.BiometricUtils;
+import com.android.server.biometrics.common.Metrics;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A service to manage multiple clients that want to access the face HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * face -related events.
+ *
+ * @hide
+ */
+public class FaceService extends BiometricService {
+
+    protected static final String TAG = "FaceService";
+    private static final boolean DEBUG = true;
+    private static final String FACE_DATA_DIR = "facedata";
+    private static final String ACTION_LOCKOUT_RESET =
+            "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET";
+    private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 3;
+    private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 12;
+
+    /**
+     * Receives the incoming binder calls from FaceManager.
+     */
+    private final class FaceServiceWrapper extends IFaceService.Stub {
+
+        /**
+         * The following methods contain common code which is shared in biometrics/common.
+         */
+        @Override // Binder call
+        public long preEnroll(IBinder token) {
+            checkPermission(MANAGE_FACE);
+            return startPreEnroll(token);
+        }
+
+        @Override // Binder call
+        public int postEnroll(IBinder token) {
+            checkPermission(MANAGE_FACE);
+            return startPostEnroll(token);
+        }
+
+        @Override // Binder call
+        public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
+                final IFaceServiceReceiver receiver, final int flags,
+                final String opPackageName) {
+            checkPermission(MANAGE_FACE);
+
+            final boolean restricted = isRestricted();
+            final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
+                    mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId,
+                    0 /* groupId */, cryptoToken, restricted, opPackageName);
+
+            enrollInternal(client, userId);
+        }
+
+        @Override // Binder call
+        public void cancelEnrollment(final IBinder token) {
+            checkPermission(MANAGE_FACE);
+            cancelEnrollmentInternal(token);
+        }
+
+        @Override // Binder call
+        public void authenticate(final IBinder token, final long opId,
+                final IFaceServiceReceiver receiver, final int flags,
+                final String opPackageName, final Bundle bundle,
+                final IBiometricPromptReceiver dialogReceiver) {
+            final boolean restricted = isRestricted();
+            final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+                    mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
+                    mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, bundle,
+                    dialogReceiver, mStatusBarService);
+
+            authenticateInternal(client, opId, opPackageName);
+        }
+
+        @Override // Binder call
+        public void cancelAuthentication(final IBinder token, final String opPackageName) {
+            cancelAuthenticationInternal(token, opPackageName);
+        }
+
+        @Override // Binder call
+        public void setActiveUser(final int userId) {
+            checkPermission(MANAGE_FACE);
+            setActiveUserInternal(userId);
+        }
+
+        @Override // Binder call
+        public void remove(final IBinder token, final int faceId, final int userId,
+                final IFaceServiceReceiver receiver) {
+            checkPermission(MANAGE_FACE);
+
+            if (token == null) {
+                Slog.w(TAG, "remove(): token is null");
+                return;
+            }
+
+            final boolean restricted = isRestricted();
+            final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper,
+                    mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId, 0 /* groupId */,
+                    userId, restricted, token.toString());
+            client.setShouldNotifyUserActivity(true);
+            removeInternal(client);
+        }
+
+        @Override
+        public void enumerate(final IBinder token, final int userId,
+                final IFaceServiceReceiver receiver) {
+            checkPermission(MANAGE_FACE);
+
+            final boolean restricted = isRestricted();
+            final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
+                    mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId,
+                    restricted, getContext().getOpPackageName());
+            enumerateInternal(client);
+        }
+
+        @Override
+        public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
+                throws RemoteException {
+            FaceService.super.addLockoutResetCallback(callback);
+        }
+
+        @Override // Binder call
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
+                return;
+            }
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                if (args.length > 0 && "--proto".equals(args[0])) {
+                    dumpProto(fd);
+                } else {
+                    dumpInternal(pw);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        /**
+         * The following methods don't use any common code from BiometricService
+         */
+
+        // TODO: refactor out common code here
+        @Override // Binder call
+        public boolean isHardwareDetected(long deviceId, String opPackageName) {
+            if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid(),
+                    UserHandle.getCallingUserId())) {
+                return false;
+            }
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                IBiometricsFace daemon = getFaceDaemon();
+                return daemon != null && mHalDeviceId != 0;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
+        public void rename(final int faceId, final String name) {
+            checkPermission(MANAGE_FACE);
+            if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) {
+                return;
+            }
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    getBiometricUtils().renameBiometricForUser(getContext(), mCurrentUserId,
+                            faceId, name);
+                }
+            });
+        }
+
+        @Override // Binder call
+        public List<Face> getEnrolledFaces(int userId, String opPackageName) {
+            if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid(),
+                    UserHandle.getCallingUserId())) {
+                return null;
+            }
+
+            return FaceService.this.getEnrolledFaces(userId);
+        }
+
+        @Override // Binder call
+        public boolean hasEnrolledFaces(int userId, String opPackageName) {
+            if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid(),
+                    UserHandle.getCallingUserId())) {
+                return false;
+            }
+
+            return FaceService.this.hasEnrolledBiometrics(userId);
+        }
+
+        @Override // Binder call
+        public long getAuthenticatorId(String opPackageName) {
+            // In this method, we're not checking whether the caller is permitted to use face
+            // API because current authenticator ID is leaked (in a more contrived way) via Android
+            // Keystore (android.security.keystore package): the user of that API can create a key
+            // which requires face authentication for its use, and then query the key's
+            // characteristics (hidden API) which returns, among other things, face
+            // authenticator ID which was active at key creation time.
+            //
+            // Reason: The part of Android Keystore which runs inside an app's process invokes this
+            // method in certain cases. Those cases are not always where the developer demonstrates
+            // explicit intent to use face functionality. Thus, to avoiding throwing an
+            // unexpected SecurityException this method does not check whether its caller is
+            // permitted to use face API.
+            //
+            // The permission check should be restored once Android Keystore no longer invokes this
+            // method from inside app processes.
+
+            return FaceService.this.getAuthenticatorId(opPackageName);
+        }
+
+        @Override // Binder call
+        public void resetTimeout(byte[] token) {
+            checkPermission(RESET_FACE_LOCKOUT);
+            // TODO: confirm security token when we move timeout management into the HAL layer.
+            mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
+        }
+    }
+
+    /**
+     * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
+     * the FaceManager.
+     */
+    private class ServiceListenerImpl implements ServiceListener {
+
+        private IFaceServiceReceiver mFaceServiceReceiver;
+
+        public ServiceListenerImpl(IFaceServiceReceiver receiver) {
+            mFaceServiceReceiver = receiver;
+        }
+
+        @Override
+        public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)
+                throws RemoteException {
+            if (mFaceServiceReceiver != null) {
+                mFaceServiceReceiver.onEnrollResult(identifier.getDeviceId(),
+                        identifier.getBiometricId(),
+                        remaining);
+            }
+        }
+
+        @Override
+        public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
+                throws RemoteException {
+            if (mFaceServiceReceiver != null) {
+                mFaceServiceReceiver.onAcquired(deviceId, acquiredInfo, vendorCode);
+            }
+        }
+
+        @Override
+        public void onAuthenticationSucceeded(long deviceId,
+                BiometricAuthenticator.Identifier biometric, int userId)
+                throws RemoteException {
+            if (mFaceServiceReceiver != null) {
+                if (biometric instanceof Face) {
+                    mFaceServiceReceiver.onAuthenticationSucceeded(deviceId, (Face)biometric);
+                } else {
+                    Slog.e(TAG, "onAuthenticationSucceeded received non-face biometric");
+                }
+            }
+        }
+
+        @Override
+        public void onAuthenticationFailed(long deviceId) throws RemoteException {
+            if (mFaceServiceReceiver != null) {
+                mFaceServiceReceiver.onAuthenticationFailed(deviceId);
+            }
+        }
+
+        @Override
+        public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+            if (mFaceServiceReceiver != null) {
+                mFaceServiceReceiver.onError(deviceId, error, vendorCode);
+            }
+        }
+
+        @Override
+        public void onRemoved(BiometricAuthenticator.Identifier identifier,
+                int remaining) throws RemoteException {
+            if (mFaceServiceReceiver != null) {
+                mFaceServiceReceiver.onRemoved(identifier.getDeviceId(),
+                        identifier.getBiometricId(), remaining);
+            }
+        }
+
+        @Override
+        public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining)
+                throws RemoteException {
+            if (mFaceServiceReceiver != null) {
+
+            }
+        }
+    }
+
+    private final FaceMetrics mFaceMetrics = new FaceMetrics();
+
+    @GuardedBy("this")
+    private IBiometricsFace mDaemon;
+
+    private long mHalDeviceId;
+    private IStatusBarService mStatusBarService;
+
+    /**
+     * Receives callbacks from the HAL.
+     */
+    private IBiometricsFaceClientCallback mDaemonCallback =
+            new IBiometricsFaceClientCallback.Stub() {
+                @Override
+                public void onEnrollResult(final long deviceId, int faceId, int userId,
+                        int remaining) {
+                    mHandler.post(() -> {
+                        final Face face = new Face(getBiometricUtils()
+                                .getUniqueName(getContext(), userId), faceId, deviceId);
+                        FaceService.super.handleEnrollResult(face, remaining);
+                    });
+                }
+
+                @Override
+                public void onAcquired(final long deviceId, final int userId,
+                        final int acquiredInfo,
+                        final int vendorCode) {
+                    mHandler.post(() -> {
+                        FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
+                    });
+                }
+
+                @Override
+                public void onAuthenticated(final long deviceId, final int faceId, final int userId,
+                        ArrayList<Byte> token) {
+                    mHandler.post(() -> {
+                        Face face = new Face("", faceId, deviceId);
+                        FaceService.super.handleAuthenticated(face, token);
+                    });
+                }
+
+                @Override
+                public void onError(final long deviceId, final int userId, final int error,
+                        final int vendorCode) {
+                    mHandler.post(() -> {
+                        FaceService.super.handleError(deviceId, error, vendorCode);
+
+                        // TODO: this chunk of code should be common to all biometric services
+                        if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
+                            // If we get HW_UNAVAILABLE, try to connect again later...
+                            Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
+                            synchronized (this) {
+                                mDaemon = null;
+                                mHalDeviceId = 0;
+                                mCurrentUserId = UserHandle.USER_NULL;
+                            }
+                        }
+                    });
+                }
+
+                @Override
+                public void onRemoved(final long deviceId, final int faceId, final int userId,
+                        final int remaining) {
+                    mHandler.post(() -> {
+                        final Face face = new Face("", faceId, deviceId);
+                        FaceService.super.handleRemoved(face, remaining);
+                    });
+                }
+
+                @Override
+                public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
+                        throws RemoteException {
+                    // TODO
+                }
+            };
+
+    /**
+     * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
+     * can be shared between the multiple biometric services.
+     */
+    private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
+        @Override
+        public int authenticate(long operationId, int groupId) throws RemoteException {
+            IBiometricsFace daemon = getFaceDaemon();
+            if (daemon == null) {
+                Slog.w(TAG, "authenticate(): no face HAL!");
+                return ERROR_ESRCH;
+            }
+            return daemon.authenticate(operationId);
+        }
+
+        @Override
+        public int cancel() throws RemoteException {
+            IBiometricsFace daemon = getFaceDaemon();
+            if (daemon == null) {
+                Slog.w(TAG, "cancel(): no face HAL!");
+                return ERROR_ESRCH;
+            }
+            return daemon.cancel();
+        }
+
+        @Override
+        public int remove(int groupId, int biometricId) throws RemoteException {
+            IBiometricsFace daemon = getFaceDaemon();
+            if (daemon == null) {
+                Slog.w(TAG, "remove(): no face HAL!");
+                return ERROR_ESRCH;
+            }
+            return daemon.remove(biometricId);
+        }
+
+        @Override
+        public int enumerate() throws RemoteException {
+            IBiometricsFace daemon = getFaceDaemon();
+            if (daemon == null) {
+                Slog.w(TAG, "enumerate(): no face HAL!");
+                return ERROR_ESRCH;
+            }
+            return daemon.enumerate();
+        }
+
+        @Override
+        public int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException {
+            IBiometricsFace daemon = getFaceDaemon();
+            if (daemon == null) {
+                Slog.w(TAG, "enroll(): no face HAL!");
+                return ERROR_ESRCH;
+            }
+            final ArrayList<Byte> token = new ArrayList<>();
+            for (int i = 0; i < cryptoToken.length; i++) {
+                token.add(cryptoToken[i]);
+            }
+            return daemon.enroll(token, timeout);
+        }
+    };
+
+
+    public FaceService(Context context) {
+        super(context);
+        // TODO: can this be retrieved from AuthenticationClient, or BiometricService?
+        mStatusBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
+        SystemServerInitThreadPool.get().submit(this::getFaceDaemon, TAG + ".onStart");
+    }
+
+    @Override
+    public String getTag() {
+        return TAG;
+    }
+
+    @Override
+    protected BiometricUtils getBiometricUtils() {
+        return FaceUtils.getInstance();
+    }
+
+    @Override
+    protected int getFailedAttemptsLockoutTimed() {
+        return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED;
+    }
+
+    @Override
+    protected int getFailedAttemptsLockoutPermanent() {
+        return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT;
+    }
+
+    @Override
+    protected Metrics getMetrics() {
+        return mFaceMetrics;
+    }
+
+    @Override
+    protected boolean hasReachedEnrollmentLimit(int userId) {
+        final int limit = getContext().getResources().getInteger(
+                com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
+        final int enrolled = FaceService.this.getEnrolledFaces(userId).size();
+        if (enrolled >= limit) {
+            Slog.w(TAG, "Too many faces registered");
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected void updateActiveGroup(int userId, String clientPackage) {
+        IBiometricsFace daemon = getFaceDaemon();
+
+        if (daemon != null) {
+            try {
+                userId = getUserOrWorkProfileId(clientPackage, userId);
+                if (userId != mCurrentUserId) {
+                    final File baseDir = Environment.getDataVendorDeDirectory(userId);
+                    final File faceDir = new File(baseDir, FACE_DATA_DIR);
+                    if (!faceDir.exists()) {
+                        if (!faceDir.mkdir()) {
+                            Slog.v(TAG, "Cannot make directory: " + faceDir.getAbsolutePath());
+                            return;
+                        }
+                        // Calling mkdir() from this process will create a directory with our
+                        // permissions (inherited from the containing dir). This command fixes
+                        // the label.
+                        if (!SELinux.restorecon(faceDir)) {
+                            Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
+                            return;
+                        }
+                    }
+
+                    daemon.setActiveUser(userId, faceDir.getAbsolutePath());
+                    mCurrentUserId = userId;
+                }
+                mAuthenticatorIds.put(userId,
+                        hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId().value : 0L);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to setActiveUser():", e);
+            }
+        }
+    }
+
+    @Override
+    protected String getLockoutResetIntent() {
+        return ACTION_LOCKOUT_RESET;
+    }
+
+    @Override
+    protected String getLockoutBroadcastPermission() {
+        return RESET_FACE_LOCKOUT;
+    }
+
+    @Override
+    protected long getHalDeviceId() {
+        return mHalDeviceId;
+    }
+
+    @Override
+    protected void handleUserSwitching(int userId) {
+        updateActiveGroup(userId, null);
+    }
+
+    @Override
+    protected boolean hasEnrolledBiometrics(int userId) {
+        if (userId != UserHandle.getCallingUserId()) {
+            checkPermission(INTERACT_ACROSS_USERS);
+        }
+        return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0;
+    }
+
+    @Override
+    protected String getManageBiometricPermission() {
+        return MANAGE_FACE;
+    }
+
+    @Override
+    protected void checkUseBiometricPermission() {
+        checkPermission(USE_BIOMETRIC);
+    }
+
+    @Override
+    protected int getAppOp() {
+        return AppOpsManager.OP_USE_FACE;
+    }
+
+    @Override
+    protected void notifyClientActiveCallbacks(boolean isActive) {
+        // noop for Face.
+    }
+
+    /** Gets the face daemon */
+    private synchronized IBiometricsFace getFaceDaemon() {
+        if (mDaemon == null) {
+            Slog.v(TAG, "mDaemon was null, reconnect to face");
+            try {
+                mDaemon = IBiometricsFace.getService();
+            } catch (java.util.NoSuchElementException e) {
+                // Service doesn't exist or cannot be opened. Logged below.
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to get biometric interface", e);
+            }
+            if (mDaemon == null) {
+                Slog.w(TAG, "face HIDL not available");
+                return null;
+            }
+
+            mDaemon.asBinder().linkToDeath(this, 0);
+
+            try {
+                mHalDeviceId = mDaemon.setCallback(mDaemonCallback).value;
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to open face HAL", e);
+                mDaemon = null; // try again later!
+            }
+
+            if (DEBUG) Slog.v(TAG, "Face HAL id: " + mHalDeviceId);
+            if (mHalDeviceId != 0) {
+                loadAuthenticatorIds();
+                updateActiveGroup(ActivityManager.getCurrentUser(), null);
+            } else {
+                Slog.w(TAG, "Failed to open Face HAL!");
+                MetricsLogger.count(getContext(), "faced_openhal_error", 1);
+                mDaemon = null;
+            }
+        }
+        return mDaemon;
+    }
+
+    private long startPreEnroll(IBinder token) {
+        IBiometricsFace daemon = getFaceDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "startPreEnroll: no face HAL!");
+            return 0;
+        }
+        try {
+            return daemon.preEnroll().value;
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startPreEnroll failed", e);
+        }
+        return 0;
+    }
+
+    private int startPostEnroll(IBinder token) {
+        IBiometricsFace daemon = getFaceDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "startPostEnroll: no face HAL!");
+            return 0;
+        }
+        try {
+            return daemon.postEnroll();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startPostEnroll failed", e);
+        }
+        return 0;
+    }
+
+    private List<Face> getEnrolledFaces(int userId) {
+        return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+    }
+
+    private void dumpInternal(PrintWriter pw) {
+        JSONObject dump = new JSONObject();
+        try {
+            dump.put("service", "Face Manager");
+
+            JSONArray sets = new JSONArray();
+            for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+                final int userId = user.getUserHandle().getIdentifier();
+                final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size();
+                PerformanceStats stats = mPerformanceMap.get(userId);
+                PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
+                JSONObject set = new JSONObject();
+                set.put("id", userId);
+                set.put("count", N);
+                set.put("accept", (stats != null) ? stats.accept : 0);
+                set.put("reject", (stats != null) ? stats.reject : 0);
+                set.put("acquire", (stats != null) ? stats.acquire : 0);
+                set.put("lockout", (stats != null) ? stats.lockout : 0);
+                set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
+                // cryptoStats measures statistics about secure face transactions
+                // (e.g. to unlock password storage, make secure purchases, etc.)
+                set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
+                set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
+                set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
+                set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
+                set.put("permanentLockoutCrypto",
+                        (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
+                sets.put(set);
+            }
+
+            dump.put("prints", sets);
+        } catch (JSONException e) {
+            Slog.e(TAG, "dump formatting failure", e);
+        }
+        pw.println(dump);
+    }
+
+    private void dumpProto(FileDescriptor fd) {
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+        for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+            final int userId = user.getUserHandle().getIdentifier();
+
+            final long userToken = proto.start(FaceServiceDumpProto.USERS);
+
+            proto.write(FaceUserStatsProto.USER_ID, userId);
+            proto.write(FaceUserStatsProto.NUM_FACES,
+                    getBiometricUtils().getBiometricsForUser(getContext(), userId).size());
+
+            // Normal face authentications (e.g. lockscreen)
+            final PerformanceStats normal = mPerformanceMap.get(userId);
+            if (normal != null) {
+                final long countsToken = proto.start(FaceUserStatsProto.NORMAL);
+                proto.write(FaceActionStatsProto.ACCEPT, normal.accept);
+                proto.write(FaceActionStatsProto.REJECT, normal.reject);
+                proto.write(FaceActionStatsProto.ACQUIRE, normal.acquire);
+                proto.write(FaceActionStatsProto.LOCKOUT, normal.lockout);
+                proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
+                proto.end(countsToken);
+            }
+
+            // Statistics about secure face transactions (e.g. to unlock password
+            // storage, make secure purchases, etc.)
+            final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
+            if (crypto != null) {
+                final long countsToken = proto.start(FaceUserStatsProto.CRYPTO);
+                proto.write(FaceActionStatsProto.ACCEPT, crypto.accept);
+                proto.write(FaceActionStatsProto.REJECT, crypto.reject);
+                proto.write(FaceActionStatsProto.ACQUIRE, crypto.acquire);
+                proto.write(FaceActionStatsProto.LOCKOUT, crypto.lockout);
+                proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
+                proto.end(countsToken);
+            }
+
+            proto.end(userToken);
+        }
+        proto.flush();
+        mPerformanceMap.clear();
+        mCryptoPerformanceMap.clear();
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/face/FaceUserState.java
new file mode 100644
index 0000000..c438bfb
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceUserState.java
@@ -0,0 +1,158 @@
+/*
+ * 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.face.Face;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.biometrics.common.BiometricUserState;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+
+/**
+ * Class managing the set of faces per user across device reboots.
+ * @hide
+ */
+public class FaceUserState extends BiometricUserState {
+
+    private static final String TAG = "FaceState";
+    private static final String FACE_FILE = "settings_face.xml";
+
+    private static final String TAG_FACES = "faces";
+    private static final String TAG_FACE = "face";
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_FACE_ID = "faceId";
+    private static final String ATTR_DEVICE_ID = "deviceId";
+
+    public FaceUserState(Context ctx, int userId) {
+        super(ctx, userId);
+    }
+
+    @Override
+    protected String getBiometricsTag() {
+        return TAG_FACES;
+    }
+
+    @Override
+    protected String getBiometricFile() {
+        return FACE_FILE;
+    }
+
+    @Override
+    protected int getNameTemplateResource() {
+        return com.android.internal.R.string.face_name_template;
+    }
+
+    @Override
+    public void addBiometric(BiometricAuthenticator.Identifier identifier) {
+        if (identifier instanceof Face) {
+            super.addBiometric(identifier);
+        } else {
+            Slog.w(TAG, "Attempted to add non-face identifier");
+        }
+    }
+
+    @Override
+    protected ArrayList getCopy(ArrayList array) {
+        ArrayList<Face> result = new ArrayList<>(array.size());
+        for (int i = 0; i < array.size(); i++) {
+            Face f = (Face) array.get(i);
+            result.add(new Face(f.getName(), f.getBiometricId(), f.getDeviceId()));
+        }
+        return result;
+    }
+
+    @Override
+    protected void doWriteState() {
+        AtomicFile destination = new AtomicFile(mFile);
+
+        ArrayList<Face> faces;
+
+        synchronized (this) {
+            faces = getCopy(mBiometrics);
+        }
+
+        FileOutputStream out = null;
+        try {
+            out = destination.startWrite();
+
+            XmlSerializer serializer = Xml.newSerializer();
+            serializer.setOutput(out, "utf-8");
+            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            serializer.startDocument(null, true);
+            serializer.startTag(null, TAG_FACES);
+
+            final int count = faces.size();
+            for (int i = 0; i < count; i++) {
+                Face f = faces.get(i);
+                serializer.startTag(null, TAG_FACE);
+                serializer.attribute(null, ATTR_FACE_ID, Integer.toString(f.getBiometricId()));
+                serializer.attribute(null, ATTR_NAME, f.getName().toString());
+                serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(f.getDeviceId()));
+                serializer.endTag(null, TAG_FACE);
+            }
+
+            serializer.endTag(null, TAG_FACES);
+            serializer.endDocument();
+            destination.finishWrite(out);
+
+            // Any error while writing is fatal.
+        } catch (Throwable t) {
+            Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
+            destination.failWrite(out);
+            throw new IllegalStateException("Failed to write faces", t);
+        } finally {
+            IoUtils.closeQuietly(out);
+        }
+    }
+
+    @GuardedBy("this")
+    @Override
+    protected void parseBiometricsLocked(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals(TAG_FACE)) {
+                String name = parser.getAttributeValue(null, ATTR_NAME);
+                String faceId = parser.getAttributeValue(null, ATTR_FACE_ID);
+                String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
+                mBiometrics.add(new Face(name, Integer.parseInt(faceId), Integer.parseInt(deviceId)));
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceUtils.java b/services/core/java/com/android/server/biometrics/face/FaceUtils.java
new file mode 100644
index 0000000..a7e85e0
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceUtils.java
@@ -0,0 +1,94 @@
+/**
+ * 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.face.Face;
+import android.text.TextUtils;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.biometrics.common.BiometricUtils;
+
+import java.util.List;
+
+/**
+ * Utility class for dealing with faces and face settings.
+ */
+public class FaceUtils implements BiometricUtils {
+
+    private static final Object sInstanceLock = new Object();
+    private static FaceUtils sInstance;
+
+    @GuardedBy("this")
+    private final SparseArray<FaceUserState> mUsers = new SparseArray<>();
+
+    public static FaceUtils getInstance() {
+        synchronized (sInstanceLock) {
+            if (sInstance == null) {
+                sInstance = new FaceUtils();
+            }
+        }
+        return sInstance;
+    }
+
+    private FaceUtils() {
+    }
+
+    @Override
+    public List<Face> getBiometricsForUser(Context ctx, int userId) {
+        return getStateForUser(ctx, userId).getBiometrics();
+    }
+
+    @Override
+    public void addBiometricForUser(Context ctx, int userId,
+            BiometricAuthenticator.Identifier identifier) {
+        getStateForUser(ctx, userId).addBiometric(identifier);
+    }
+
+    @Override
+    public void removeBiometricForUser(Context ctx, int userId, int faceId) {
+        getStateForUser(ctx, userId).removeBiometric(faceId);
+    }
+
+    @Override
+    public void renameBiometricForUser(Context ctx, int userId, int faceId, CharSequence name) {
+        if (TextUtils.isEmpty(name)) {
+            // Don't do the rename if it's empty
+            return;
+        }
+        getStateForUser(ctx, userId).renameBiometric(faceId, name);
+    }
+
+    @Override
+    public CharSequence getUniqueName(Context context, int userId) {
+        return getStateForUser(context, userId).getUniqueName();
+    }
+
+    private FaceUserState getStateForUser(Context ctx, int userId) {
+        synchronized (this) {
+            FaceUserState state = mUsers.get(userId);
+            if (state == null) {
+                state = new FaceUserState(ctx, userId);
+                mUsers.put(userId, state);
+            }
+            return state;
+        }
+    }
+}
+
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java
new file mode 100644
index 0000000..ba8b3b3
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java
@@ -0,0 +1,68 @@
+/*
+ * 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.biometrics.fingerprint;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.biometrics.common.Metrics;
+
+public class FingerprintMetrics implements Metrics {
+
+    @Override
+    public String logTag() {
+        return FingerprintService.TAG;
+    }
+
+    @Override
+    public String tagHalDied() {
+        return "fingerprintd_died";
+    }
+
+    @Override
+    public String tagAuthToken() {
+        return "fingerprint_token";
+    }
+
+    @Override
+    public String tagAuthStartError() {
+        return "fingerprintd_auth_start_error";
+    }
+
+    @Override
+    public String tagEnrollStartError() {
+        return "fingerprintd_enroll_start_error";
+    }
+
+    @Override
+    public String tagEnumerateStartError() {
+        return "fingerprintd_enum_start_error";
+    }
+
+    @Override
+    public String tagRemoveStartError() {
+        return "fingerprintd_remove_start_error";
+    }
+
+    @Override
+    public int actionBiometricAuth() {
+        return MetricsProto.MetricsEvent.ACTION_FINGERPRINT_AUTH;
+    }
+
+    @Override
+    public int actionBiometricEnroll() {
+        return MetricsProto.MetricsEvent.ACTION_FINGERPRINT_ENROLL;
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
new file mode 100644
index 0000000..64b248e
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -0,0 +1,1027 @@
+/*
+ * 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.
+ */
+
+package com.android.server.biometrics.fingerprint;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_FINGERPRINT;
+import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
+import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_FINGERPRINT;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.IFingerprintClientActiveCallback;
+import android.hardware.fingerprint.IFingerprintService;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.common.BiometricService;
+import com.android.server.biometrics.common.BiometricUtils;
+import com.android.server.biometrics.common.ClientMonitor;
+import com.android.server.biometrics.common.EnumerateClient;
+import com.android.server.biometrics.common.Metrics;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * A service to manage multiple clients that want to access the fingerprint HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * fingerprint-related events.
+ *
+ * @hide
+ */
+public class FingerprintService extends BiometricService {
+
+    protected static final String TAG = "FingerprintService";
+    private static final boolean DEBUG = true;
+    private static final boolean CLEANUP_UNUSED_FP = true;
+    private static final String FP_DATA_DIR = "fpdata";
+    private static final String ACTION_LOCKOUT_RESET =
+            "com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET";
+    private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
+    private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
+
+    // TODO: This should be refactored into BiometricService
+    private final class UserFingerprint {
+        Fingerprint f;
+        int userId;
+        public UserFingerprint(Fingerprint f, int userId) {
+            this.f = f;
+            this.userId = userId;
+        }
+    }
+
+    /**
+     * Receives the incoming binder calls from FingerprintManager.
+     */
+    private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+
+        /**
+         * The following methods contain common code which is shared in biometrics/common.
+         */
+
+        @Override // Binder call
+        public long preEnroll(IBinder token) {
+            checkPermission(MANAGE_FINGERPRINT);
+            return startPreEnroll(token);
+        }
+
+        @Override // Binder call
+        public int postEnroll(IBinder token) {
+            checkPermission(MANAGE_FINGERPRINT);
+            return startPostEnroll(token);
+        }
+
+        @Override // Binder call
+        public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
+                final IFingerprintServiceReceiver receiver, final int flags,
+                final String opPackageName) {
+            checkPermission(MANAGE_FINGERPRINT);
+
+            final boolean restricted = isRestricted();
+            final int groupId = userId; // default group for fingerprint enrollment
+            final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
+                    mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId,
+                    cryptoToken, restricted, opPackageName);
+
+            enrollInternal(client, userId);
+        }
+
+        @Override // Binder call
+        public void cancelEnrollment(final IBinder token) {
+            checkPermission(MANAGE_FINGERPRINT);
+            cancelEnrollmentInternal(token);
+        }
+
+        @Override // Binder call
+        public void authenticate(final IBinder token, final long opId, final int groupId,
+                final IFingerprintServiceReceiver receiver, final int flags,
+                final String opPackageName, final Bundle bundle,
+                final IBiometricPromptReceiver dialogReceiver) {
+            final boolean restricted = isRestricted();
+            final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+                    mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
+                    mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
+                    dialogReceiver, mStatusBarService);
+
+            authenticateInternal(client, opId, opPackageName);
+        }
+
+        @Override // Binder call
+        public void cancelAuthentication(final IBinder token, final String opPackageName) {
+            cancelAuthenticationInternal(token, opPackageName);
+        }
+
+        @Override // Binder call
+        public void setActiveUser(final int userId) {
+            checkPermission(MANAGE_FINGERPRINT);
+            setActiveUserInternal(userId);
+        }
+
+        @Override // Binder call
+        public void remove(final IBinder token, final int fingerId, final int groupId,
+                final int userId, final IFingerprintServiceReceiver receiver) {
+            checkPermission(MANAGE_FINGERPRINT);
+
+            if (token == null) {
+                Slog.w(TAG, "remove(): token is null");
+                return;
+            }
+
+            final boolean restricted = isRestricted();
+            final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper,
+                    mHalDeviceId, token, new ServiceListenerImpl(receiver), fingerId, groupId,
+                    userId, restricted, token.toString());
+            client.setShouldNotifyUserActivity(true);
+            removeInternal(client);
+        }
+
+        @Override // Binder call
+        public void enumerate(final IBinder token, final int userId,
+                final IFingerprintServiceReceiver receiver) {
+            checkPermission(MANAGE_FINGERPRINT);
+
+            final boolean restricted = isRestricted();
+            final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
+                    mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId,
+                    restricted, getContext().getOpPackageName());
+            enumerateInternal(client);
+        }
+
+        @Override
+        public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
+                throws RemoteException {
+            FingerprintService.super.addLockoutResetCallback(callback);
+        }
+
+        @Override // Binder call
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
+                return;
+            }
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                if (args.length > 0 && "--proto".equals(args[0])) {
+                    dumpProto(fd);
+                } else {
+                    dumpInternal(pw);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        /**
+         * The following methods don't use any common code from BiometricService
+         */
+
+        // TODO: refactor out common code here
+        @Override // Binder call
+        public boolean isHardwareDetected(long deviceId, String opPackageName) {
+            if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid(),
+                    UserHandle.getCallingUserId())) {
+                return false;
+            }
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                IBiometricsFingerprint daemon = getFingerprintDaemon();
+                return daemon != null && mHalDeviceId != 0;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
+        public void rename(final int fingerId, final int groupId, final String name) {
+            checkPermission(MANAGE_FINGERPRINT);
+            if (!isCurrentUserOrProfile(groupId)) {
+                return;
+            }
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    getBiometricUtils().renameBiometricForUser(getContext(), groupId,
+                            fingerId, name);
+                }
+            });
+        }
+
+        @Override // Binder call
+        public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
+            if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid(),
+                    UserHandle.getCallingUserId())) {
+                return Collections.emptyList();
+            }
+
+            return FingerprintService.this.getEnrolledFingerprints(userId);
+        }
+
+        @Override // Binder call
+        public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
+            if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid(),
+                    UserHandle.getCallingUserId())) {
+                return false;
+            }
+
+            return FingerprintService.this.hasEnrolledBiometrics(userId);
+        }
+
+        @Override // Binder call
+        public long getAuthenticatorId(String opPackageName) {
+            // In this method, we're not checking whether the caller is permitted to use fingerprint
+            // API because current authenticator ID is leaked (in a more contrived way) via Android
+            // Keystore (android.security.keystore package): the user of that API can create a key
+            // which requires fingerprint authentication for its use, and then query the key's
+            // characteristics (hidden API) which returns, among other things, fingerprint
+            // authenticator ID which was active at key creation time.
+            //
+            // Reason: The part of Android Keystore which runs inside an app's process invokes this
+            // method in certain cases. Those cases are not always where the developer demonstrates
+            // explicit intent to use fingerprint functionality. Thus, to avoiding throwing an
+            // unexpected SecurityException this method does not check whether its caller is
+            // permitted to use fingerprint API.
+            //
+            // The permission check should be restored once Android Keystore no longer invokes this
+            // method from inside app processes.
+
+            return FingerprintService.super.getAuthenticatorId(opPackageName);
+        }
+
+        @Override // Binder call
+        public void resetTimeout(byte [] token) {
+            checkPermission(RESET_FINGERPRINT_LOCKOUT);
+            // TODO: confirm security token when we move timeout management into the HAL layer.
+            mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
+        }
+
+        @Override
+        public boolean isClientActive() {
+            checkPermission(MANAGE_FINGERPRINT);
+            synchronized(FingerprintService.this) {
+                return (getCurrentClient() != null) || (getPendingClient() != null);
+            }
+        }
+
+        @Override
+        public void addClientActiveCallback(IFingerprintClientActiveCallback callback) {
+            checkPermission(MANAGE_FINGERPRINT);
+            mClientActiveCallbacks.add(callback);
+        }
+
+        @Override
+        public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) {
+            checkPermission(MANAGE_FINGERPRINT);
+            mClientActiveCallbacks.remove(callback);
+        }
+    }
+
+    /**
+     * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
+     * the FingerprintManager.
+     */
+    private class ServiceListenerImpl implements ServiceListener {
+
+        private IFingerprintServiceReceiver mFingerprintServiceReceiver;
+
+        public ServiceListenerImpl(IFingerprintServiceReceiver receiver) {
+            mFingerprintServiceReceiver = receiver;
+        }
+
+        @Override
+        public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)
+                throws RemoteException {
+            if (mFingerprintServiceReceiver != null) {
+                // TODO: Pass up the fp directly instead
+                final Fingerprint fp = (Fingerprint) identifier;
+                mFingerprintServiceReceiver.onEnrollResult(fp.getDeviceId(), fp.getBiometricId(),
+                        fp.getGroupId(), remaining);
+            }
+        }
+
+        @Override
+        public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
+                throws RemoteException {
+            if (mFingerprintServiceReceiver != null) {
+                mFingerprintServiceReceiver.onAcquired(deviceId, acquiredInfo, vendorCode);
+            }
+        }
+
+        @Override
+        public void onAuthenticationSucceeded(long deviceId,
+                BiometricAuthenticator.Identifier biometric, int userId)
+                throws RemoteException {
+            if (mFingerprintServiceReceiver != null) {
+                if (biometric == null || biometric instanceof Fingerprint) {
+                    mFingerprintServiceReceiver
+                            .onAuthenticationSucceeded(deviceId, (Fingerprint) biometric, userId);
+                } else {
+                    Slog.e(TAG, "onAuthenticationSucceeded received non-fingerprint biometric");
+                }
+            }
+        }
+
+        @Override
+        public void onAuthenticationFailed(long deviceId) throws RemoteException {
+            if (mFingerprintServiceReceiver != null) {
+                mFingerprintServiceReceiver.onAuthenticationFailed(deviceId);
+            }
+        }
+
+        @Override
+        public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+            if (mFingerprintServiceReceiver != null) {
+                mFingerprintServiceReceiver.onError(deviceId, error, vendorCode);
+            }
+        }
+
+        @Override
+        public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining)
+                throws RemoteException {
+            if (mFingerprintServiceReceiver != null) {
+                // TODO: Pass up the fp directly instead
+                final Fingerprint fp = (Fingerprint) identifier;
+                mFingerprintServiceReceiver.onRemoved(fp.getDeviceId(), fp.getBiometricId(),
+                        fp.getGroupId(), remaining);
+            }
+        }
+
+        @Override
+        public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining)
+                throws RemoteException {
+            if (mFingerprintServiceReceiver != null) {
+                final Fingerprint fp = (Fingerprint) identifier;
+                mFingerprintServiceReceiver.onEnumerated(fp.getDeviceId(), fp.getBiometricId(),
+                        fp.getGroupId(), remaining);
+            }
+        }
+    }
+
+    /**
+     * An internal class to help clean up unknown fingerprints in the hardware and software.
+     */
+    private final class InternalEnumerateClient extends BiometricService.EnumerateClientImpl {
+
+        private List<Fingerprint> mEnrolledList;
+        private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete
+
+        public InternalEnumerateClient(Context context, DaemonWrapper daemon, long halDeviceId,
+                IBinder token, ServiceListener listener, int groupId, int userId,
+                boolean restricted, String owner, List<Fingerprint> enrolledList) {
+            super(context, daemon, halDeviceId, token, listener, groupId, userId, restricted,
+                    owner);
+            mEnrolledList = enrolledList;
+        }
+
+        private void handleEnumeratedFingerprint(
+                BiometricAuthenticator.Identifier identifier, int remaining) {
+            boolean matched = false;
+            for (int i = 0; i < mEnrolledList.size(); i++) {
+                if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) {
+                    mEnrolledList.remove(i);
+                    matched = true;
+                    break;
+                }
+            }
+
+            // fingerId 0 means no fingerprints are in hardware
+            if (!matched && identifier.getBiometricId() != 0) {
+                mUnknownFingerprints.add((Fingerprint) identifier);
+            }
+        }
+
+        private void doFingerprintCleanup() {
+            if (mEnrolledList == null) {
+                return;
+            }
+
+            for (Fingerprint f : mEnrolledList) {
+                Slog.e(TAG, "doFingerprintCleanup(): Removing dangling enrolled fingerprint: "
+                        + f.getName() + " " + f.getBiometricId() + " " + f.getGroupId()
+                        + " " + f.getDeviceId());
+                FingerprintUtils.getInstance().removeBiometricForUser(getContext(),
+                        getTargetUserId(), f.getBiometricId());
+            }
+            mEnrolledList.clear();
+        }
+
+        public List<Fingerprint> getUnknownFingerprints() {
+            return mUnknownFingerprints;
+        }
+
+        @Override
+        public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+                int remaining) {
+            handleEnumeratedFingerprint(identifier, remaining);
+            if (remaining == 0) {
+                doFingerprintCleanup();
+            }
+            return remaining == 0;
+        }
+    }
+
+    /**
+     * An internal class to help clean up unknown fingerprints in hardware and software.
+     */
+    private final class InternalRemovalClient extends BiometricService.RemovalClientImpl {
+        public InternalRemovalClient(Context context,
+                DaemonWrapper daemon, long halDeviceId, IBinder token,
+                ServiceListener listener, int fingerId, int groupId, int userId, boolean restricted,
+                String owner) {
+            super(context, daemon, halDeviceId, token, listener, fingerId, groupId, userId,
+                    restricted,
+                    owner);
+        }
+    }
+
+    private final FingerprintMetrics mFingerprintMetrics = new FingerprintMetrics();
+    private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
+            new CopyOnWriteArrayList<>();
+
+    @GuardedBy("this")
+    private IBiometricsFingerprint mDaemon;
+
+    private long mHalDeviceId;
+    private IStatusBarService mStatusBarService;
+    private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
+    private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
+
+    /**
+     * Receives callbacks from the HAL.
+     */
+    private IBiometricsFingerprintClientCallback mDaemonCallback =
+            new IBiometricsFingerprintClientCallback.Stub() {
+        @Override
+        public void onEnrollResult(final long deviceId, final int fingerId, final int groupId,
+                final int remaining) {
+            mHandler.post(() -> {
+                final Fingerprint fingerprint =
+                        new Fingerprint(getBiometricUtils().getUniqueName(getContext(), groupId),
+                                groupId, fingerId, deviceId);
+                FingerprintService.super.handleEnrollResult(fingerprint, remaining);
+            });
+        }
+
+        @Override
+        public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
+            mHandler.post(() -> {
+                FingerprintService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
+            });
+        }
+
+        @Override
+        public void onAuthenticated(final long deviceId, final int fingerId, final int groupId,
+                ArrayList<Byte> token) {
+            mHandler.post(() -> {
+                Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
+                FingerprintService.super.handleAuthenticated(fp, token);
+            });
+        }
+
+        @Override
+        public void onError(final long deviceId, final int error, final int vendorCode) {
+            mHandler.post(() -> {
+                ClientMonitor client = getCurrentClient();
+                if (client instanceof InternalRemovalClient
+                        || client instanceof InternalEnumerateClient) {
+                    clearEnumerateState();
+                }
+                FingerprintService.super.handleError(deviceId, error, vendorCode);
+
+                // TODO: this chunk of code should be common to all biometric services
+                if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
+                    // If we get HW_UNAVAILABLE, try to connect again later...
+                    Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
+                    synchronized (this) {
+                        mDaemon = null;
+                        mHalDeviceId = 0;
+                        mCurrentUserId = UserHandle.USER_NULL;
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void onRemoved(final long deviceId, final int fingerId, final int groupId,
+                final int remaining) {
+            mHandler.post(() -> {
+                ClientMonitor client = getCurrentClient();
+                final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
+                FingerprintService.super.handleRemoved(fp, remaining);
+                if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
+                    cleanupUnknownFingerprints();
+                } else if (client instanceof InternalRemovalClient){
+                    clearEnumerateState();
+                }
+            });
+        }
+
+        @Override
+        public void onEnumerate(final long deviceId, final int fingerId, final int groupId,
+                final int remaining) {
+            mHandler.post(() -> {
+                // TODO: factor out common enumerate logic if possible
+                final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
+                FingerprintService.this.handleEnumerate(fp, remaining);
+            });
+
+        }
+    };
+
+    /**
+     * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
+     * can be shared between the multiple biometric services.
+     */
+    private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
+        @Override
+        public int authenticate(long operationId, int groupId) throws RemoteException {
+            IBiometricsFingerprint daemon = getFingerprintDaemon();
+            if (daemon == null) {
+                Slog.w(TAG, "authenticate(): no fingerprint HAL!");
+                return ERROR_ESRCH;
+            }
+            return daemon.authenticate(operationId, groupId);
+        }
+
+        @Override
+        public int cancel() throws RemoteException {
+            IBiometricsFingerprint daemon = getFingerprintDaemon();
+            if (daemon == null) {
+                Slog.w(TAG, "cancel(): no fingerprint HAL!");
+                return ERROR_ESRCH;
+            }
+            return daemon.cancel();
+        }
+
+        @Override
+        public int remove(int groupId, int biometricId) throws RemoteException {
+            IBiometricsFingerprint daemon = getFingerprintDaemon();
+            if (daemon == null) {
+                Slog.w(TAG, "remove(): no fingerprint HAL!");
+                return ERROR_ESRCH;
+            }
+            return daemon.remove(groupId, biometricId);
+        }
+
+        @Override
+        public int enumerate() throws RemoteException {
+            IBiometricsFingerprint daemon = getFingerprintDaemon();
+            if (daemon == null) {
+                Slog.w(TAG, "enumerate(): no fingerprint HAL!");
+                return ERROR_ESRCH;
+            }
+            return daemon.enumerate();
+        }
+
+        @Override
+        public int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException {
+            IBiometricsFingerprint daemon = getFingerprintDaemon();
+            if (daemon == null) {
+                Slog.w(TAG, "enroll(): no fingerprint HAL!");
+                return ERROR_ESRCH;
+            }
+            return daemon.enroll(cryptoToken, groupId, timeout);
+        }
+    };
+
+    public FingerprintService(Context context) {
+        super(context);
+        // TODO: can this be retrieved from AuthenticationClient, or BiometricService?
+        mStatusBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
+        SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+    @Override
+    protected BiometricUtils getBiometricUtils() {
+        return FingerprintUtils.getInstance();
+    }
+
+    @Override
+    protected int getFailedAttemptsLockoutTimed() {
+        return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED;
+    }
+
+    @Override
+    protected int getFailedAttemptsLockoutPermanent() {
+        return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT;
+    }
+
+    @Override
+    protected Metrics getMetrics() {
+        return mFingerprintMetrics;
+    }
+
+    @Override
+    protected boolean hasReachedEnrollmentLimit(int userId) {
+        final int limit = getContext().getResources().getInteger(
+                com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
+        final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size();
+        if (enrolled >= limit) {
+            Slog.w(TAG, "Too many fingerprints registered");
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected void updateActiveGroup(int userId, String clientPackage) {
+        IBiometricsFingerprint daemon = getFingerprintDaemon();
+
+        if (daemon != null) {
+            try {
+                userId = getUserOrWorkProfileId(clientPackage, userId);
+                if (userId != mCurrentUserId) {
+                    int firstSdkInt = Build.VERSION.FIRST_SDK_INT;
+                    if (firstSdkInt < Build.VERSION_CODES.BASE) {
+                        Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " +
+                                "at least VERSION_CODES.BASE");
+                    }
+                    File baseDir;
+                    if (firstSdkInt <= Build.VERSION_CODES.O_MR1) {
+                        baseDir = Environment.getUserSystemDirectory(userId);
+                    } else {
+                        baseDir = Environment.getDataVendorDeDirectory(userId);
+                    }
+
+                    File fpDir = new File(baseDir, FP_DATA_DIR);
+                    if (!fpDir.exists()) {
+                        if (!fpDir.mkdir()) {
+                            Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
+                            return;
+                        }
+                        // Calling mkdir() from this process will create a directory with our
+                        // permissions (inherited from the containing dir). This command fixes
+                        // the label.
+                        if (!SELinux.restorecon(fpDir)) {
+                            Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
+                            return;
+                        }
+                    }
+
+                    daemon.setActiveGroup(userId, fpDir.getAbsolutePath());
+                    mCurrentUserId = userId;
+                }
+                mAuthenticatorIds.put(userId,
+                        hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId() : 0L);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to setActiveGroup():", e);
+            }
+        }
+    }
+
+    @Override
+    protected String getLockoutResetIntent() {
+        return ACTION_LOCKOUT_RESET;
+    }
+
+    @Override
+    protected String getLockoutBroadcastPermission() {
+        return RESET_FINGERPRINT_LOCKOUT;
+    }
+
+    @Override
+    protected long getHalDeviceId() {
+        return mHalDeviceId;
+    }
+
+    @Override
+    protected void handleUserSwitching(int userId) {
+        if (getCurrentClient() instanceof InternalRemovalClient
+                || getCurrentClient() instanceof InternalEnumerateClient) {
+            Slog.w(TAG, "User switched while performing cleanup");
+            removeClient(getCurrentClient());
+            clearEnumerateState();
+        }
+        updateActiveGroup(userId, null);
+        doFingerprintCleanupForUser(userId);
+    }
+
+
+    @Override
+    protected boolean hasEnrolledBiometrics(int userId) {
+        if (userId != UserHandle.getCallingUserId()) {
+            checkPermission(INTERACT_ACROSS_USERS);
+        }
+        return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0;
+    }
+
+    @Override
+    protected String getManageBiometricPermission() {
+        return MANAGE_FINGERPRINT;
+    }
+
+    @Override
+    protected void checkUseBiometricPermission() {
+        if (getContext().checkCallingPermission(USE_FINGERPRINT)
+                != PackageManager.PERMISSION_GRANTED) {
+            checkPermission(USE_BIOMETRIC);
+        }
+    }
+
+    @Override
+    protected int getAppOp() {
+        return AppOpsManager.OP_USE_FINGERPRINT;
+    }
+
+    @Override
+    protected void notifyClientActiveCallbacks(boolean isActive) {
+        List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
+        for (int i = 0; i < callbacks.size(); i++) {
+            try {
+                callbacks.get(i).onClientActiveChanged(isActive);
+            } catch (RemoteException re) {
+                // If the remote is dead, stop notifying it
+                mClientActiveCallbacks.remove(callbacks.get(i));
+            }
+        }
+    }
+
+    /** Gets the fingerprint daemon */
+    private synchronized IBiometricsFingerprint getFingerprintDaemon() {
+        if (mDaemon == null) {
+            Slog.v(TAG, "mDaemon was null, reconnect to fingerprint");
+            try {
+                mDaemon = IBiometricsFingerprint.getService();
+            } catch (java.util.NoSuchElementException e) {
+                // Service doesn't exist or cannot be opened. Logged below.
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to get biometric interface", e);
+            }
+            if (mDaemon == null) {
+                Slog.w(TAG, "fingerprint HIDL not available");
+                return null;
+            }
+
+            mDaemon.asBinder().linkToDeath(this, 0);
+
+            try {
+                mHalDeviceId = mDaemon.setNotify(mDaemonCallback);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to open fingerprint HAL", e);
+                mDaemon = null; // try again later!
+            }
+
+            if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
+            if (mHalDeviceId != 0) {
+                loadAuthenticatorIds();
+                updateActiveGroup(ActivityManager.getCurrentUser(), null);
+                doFingerprintCleanupForUser(ActivityManager.getCurrentUser());
+            } else {
+                Slog.w(TAG, "Failed to open Fingerprint HAL!");
+                MetricsLogger.count(getContext(), "fingerprintd_openhal_error", 1);
+                mDaemon = null;
+            }
+        }
+        return mDaemon;
+    }
+
+    /**
+     * This method should be called upon connection to the daemon, and when user switches.
+     * @param userId
+     */
+    private void doFingerprintCleanupForUser(int userId) {
+        if (CLEANUP_UNUSED_FP) {
+            enumerateUser(userId);
+        }
+    }
+
+    private void clearEnumerateState() {
+        if (DEBUG) Slog.v(TAG, "clearEnumerateState()");
+        mUnknownFingerprints.clear();
+    }
+
+    private void enumerateUser(int userId) {
+        if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")");
+
+        final boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
+        final List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
+
+        InternalEnumerateClient client = new InternalEnumerateClient(getContext(), mDaemonWrapper,
+                mHalDeviceId, mToken, new ServiceListenerImpl(null), userId, userId, restricted,
+                getContext().getOpPackageName(), enrolledList);
+        enumerateInternal(client);
+    }
+
+    // Remove unknown fingerprints from hardware
+    private void cleanupUnknownFingerprints() {
+        if (!mUnknownFingerprints.isEmpty()) {
+            UserFingerprint uf = mUnknownFingerprints.get(0);
+            mUnknownFingerprints.remove(uf);
+            boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
+            InternalRemovalClient client = new InternalRemovalClient(getContext(), mDaemonWrapper,
+                    mHalDeviceId, mToken, new ServiceListenerImpl(null), uf.f.getBiometricId(),
+                    uf.f.getGroupId(), uf.userId, restricted, getContext().getOpPackageName());
+            removeInternal(client);
+        } else {
+            clearEnumerateState();
+        }
+    }
+
+    private void handleEnumerate(Fingerprint fingerprint, int remaining) {
+        ClientMonitor client = getCurrentClient();
+
+        if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) {
+            return;
+        }
+        client.onEnumerationResult(fingerprint, remaining);
+
+        // All fingerprints in hardware for this user were enumerated
+        if (remaining == 0) {
+            if (client instanceof InternalEnumerateClient) {
+                List<Fingerprint> unknownFingerprints =
+                        ((InternalEnumerateClient) client).getUnknownFingerprints();
+
+                if (!unknownFingerprints.isEmpty()) {
+                    Slog.w(TAG, "Adding " + unknownFingerprints.size() +
+                            " fingerprints for deletion");
+                }
+                for (Fingerprint f : unknownFingerprints) {
+                    mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
+                }
+                removeClient(client);
+                cleanupUnknownFingerprints();
+            } else {
+                removeClient(client);
+            }
+        }
+    }
+
+    private long startPreEnroll(IBinder token) {
+        IBiometricsFingerprint daemon = getFingerprintDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "startPreEnroll: no fingerprint HAL!");
+            return 0;
+        }
+        try {
+            return daemon.preEnroll();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startPreEnroll failed", e);
+        }
+        return 0;
+    }
+
+    private int startPostEnroll(IBinder token) {
+        IBiometricsFingerprint daemon = getFingerprintDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "startPostEnroll: no fingerprint HAL!");
+            return 0;
+        }
+        try {
+            return daemon.postEnroll();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startPostEnroll failed", e);
+        }
+        return 0;
+    }
+
+    private List<Fingerprint> getEnrolledFingerprints(int userId) {
+        return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+    }
+
+    private void dumpInternal(PrintWriter pw) {
+        JSONObject dump = new JSONObject();
+        try {
+            dump.put("service", "Fingerprint Manager");
+
+            JSONArray sets = new JSONArray();
+            for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+                final int userId = user.getUserHandle().getIdentifier();
+                final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size();
+                PerformanceStats stats = mPerformanceMap.get(userId);
+                PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
+                JSONObject set = new JSONObject();
+                set.put("id", userId);
+                set.put("count", N);
+                set.put("accept", (stats != null) ? stats.accept : 0);
+                set.put("reject", (stats != null) ? stats.reject : 0);
+                set.put("acquire", (stats != null) ? stats.acquire : 0);
+                set.put("lockout", (stats != null) ? stats.lockout : 0);
+                set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
+                // cryptoStats measures statistics about secure fingerprint transactions
+                // (e.g. to unlock password storage, make secure purchases, etc.)
+                set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
+                set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
+                set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
+                set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
+                set.put("permanentLockoutCrypto",
+                    (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
+                sets.put(set);
+            }
+
+            dump.put("prints", sets);
+        } catch (JSONException e) {
+            Slog.e(TAG, "dump formatting failure", e);
+        }
+        pw.println(dump);
+    }
+
+    private void dumpProto(FileDescriptor fd) {
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+        for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+            final int userId = user.getUserHandle().getIdentifier();
+
+            final long userToken = proto.start(FingerprintServiceDumpProto.USERS);
+
+            proto.write(FingerprintUserStatsProto.USER_ID, userId);
+            proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
+                    getBiometricUtils().getBiometricsForUser(getContext(), userId).size());
+
+            // Normal fingerprint authentications (e.g. lockscreen)
+            final PerformanceStats normal = mPerformanceMap.get(userId);
+            if (normal != null) {
+                final long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
+                proto.write(PerformanceStatsProto.ACCEPT, normal.accept);
+                proto.write(PerformanceStatsProto.REJECT, normal.reject);
+                proto.write(PerformanceStatsProto.ACQUIRE, normal.acquire);
+                proto.write(PerformanceStatsProto.LOCKOUT, normal.lockout);
+                proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, normal.permanentLockout);
+                proto.end(countsToken);
+            }
+
+            // Statistics about secure fingerprint transactions (e.g. to unlock password
+            // storage, make secure purchases, etc.)
+            final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
+            if (crypto != null) {
+                final long countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
+                proto.write(PerformanceStatsProto.ACCEPT, crypto.accept);
+                proto.write(PerformanceStatsProto.REJECT, crypto.reject);
+                proto.write(PerformanceStatsProto.ACQUIRE, crypto.acquire);
+                proto.write(PerformanceStatsProto.LOCKOUT, crypto.lockout);
+                proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, crypto.permanentLockout);
+                proto.end(countsToken);
+            }
+
+            proto.end(userToken);
+        }
+        proto.flush();
+        mPerformanceMap.clear();
+        mCryptoPerformanceMap.clear();
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUserState.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUserState.java
new file mode 100644
index 0000000..9e34ee8
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUserState.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.biometrics.fingerprint;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.fingerprint.Fingerprint;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.biometrics.common.BiometricUserState;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Class managing the set of fingerprint per user across device reboots.
+ * @hide
+ */
+public class FingerprintUserState extends BiometricUserState {
+
+    private static final String TAG = "FingerprintState";
+    private static final String FINGERPRINT_FILE = "settings_fingerprint.xml";
+
+    private static final String TAG_FINGERPRINTS = "fingerprints";
+    private static final String TAG_FINGERPRINT = "fingerprint";
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_GROUP_ID = "groupId";
+    private static final String ATTR_FINGER_ID = "fingerId";
+    private static final String ATTR_DEVICE_ID = "deviceId";
+
+    public FingerprintUserState(Context context, int userId) {
+        super(context, userId);
+    }
+
+    @Override
+    protected String getBiometricsTag() {
+        return TAG_FINGERPRINTS;
+    }
+
+    @Override
+    protected String getBiometricFile() {
+        return FINGERPRINT_FILE;
+    }
+
+    @Override
+    protected int getNameTemplateResource() {
+        return com.android.internal.R.string.fingerprint_name_template;
+    }
+
+    @Override
+    public void addBiometric(BiometricAuthenticator.Identifier identifier) {
+        if (identifier instanceof Fingerprint) {
+            super.addBiometric(identifier);
+        } else {
+            Slog.w(TAG, "Attempted to add non-fingerprint identifier");
+        }
+    }
+
+    @Override
+    protected ArrayList getCopy(ArrayList array) {
+        ArrayList<Fingerprint> result = new ArrayList<>();
+        for (int i = 0; i < array.size(); i++) {
+            Fingerprint fp = (Fingerprint) array.get(i);
+            result.add(new Fingerprint(fp.getName(), fp.getGroupId(), fp.getBiometricId(),
+                    fp.getDeviceId()));
+        }
+        return result;
+    }
+
+    @Override
+    protected void doWriteState() {
+        AtomicFile destination = new AtomicFile(mFile);
+
+        ArrayList<Fingerprint> fingerprints;
+
+        synchronized (this) {
+            fingerprints = getCopy(mBiometrics);
+        }
+
+        FileOutputStream out = null;
+        try {
+            out = destination.startWrite();
+
+            XmlSerializer serializer = Xml.newSerializer();
+            serializer.setOutput(out, "utf-8");
+            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            serializer.startDocument(null, true);
+            serializer.startTag(null, TAG_FINGERPRINTS);
+
+            final int count = fingerprints.size();
+            for (int i = 0; i < count; i++) {
+                Fingerprint fp = fingerprints.get(i);
+                serializer.startTag(null, TAG_FINGERPRINT);
+                serializer.attribute(null, ATTR_FINGER_ID, Integer.toString(fp.getBiometricId()));
+                serializer.attribute(null, ATTR_NAME, fp.getName().toString());
+                serializer.attribute(null, ATTR_GROUP_ID, Integer.toString(fp.getGroupId()));
+                serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(fp.getDeviceId()));
+                serializer.endTag(null, TAG_FINGERPRINT);
+            }
+
+            serializer.endTag(null, TAG_FINGERPRINTS);
+            serializer.endDocument();
+            destination.finishWrite(out);
+
+            // Any error while writing is fatal.
+        } catch (Throwable t) {
+            Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
+            destination.failWrite(out);
+            throw new IllegalStateException("Failed to write fingerprints", t);
+        } finally {
+            IoUtils.closeQuietly(out);
+        }
+    }
+
+    @GuardedBy("this")
+    @Override
+    protected void parseBiometricsLocked(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals(TAG_FINGERPRINT)) {
+                String name = parser.getAttributeValue(null, ATTR_NAME);
+                String groupId = parser.getAttributeValue(null, ATTR_GROUP_ID);
+                String fingerId = parser.getAttributeValue(null, ATTR_FINGER_ID);
+                String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
+                mBiometrics.add(new Fingerprint(name, Integer.parseInt(groupId),
+                        Integer.parseInt(fingerId), Long.parseLong(deviceId)));
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUtils.java
new file mode 100644
index 0000000..41216e9
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUtils.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.fingerprint;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.fingerprint.Fingerprint;
+import android.text.TextUtils;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.biometrics.common.BiometricUtils;
+
+import java.util.List;
+
+/**
+ * Utility class for dealing with fingerprints and fingerprint settings.
+ */
+public class FingerprintUtils implements BiometricUtils {
+
+    private static final Object sInstanceLock = new Object();
+    private static FingerprintUtils sInstance;
+
+    @GuardedBy("this")
+    private final SparseArray<FingerprintUserState> mUsers = new SparseArray<>();
+
+    public static FingerprintUtils getInstance() {
+        synchronized (sInstanceLock) {
+            if (sInstance == null) {
+                sInstance = new FingerprintUtils();
+            }
+        }
+        return sInstance;
+    }
+
+    private FingerprintUtils() {
+    }
+
+    @Override
+    public List<Fingerprint> getBiometricsForUser(Context ctx, int userId) {
+        return getStateForUser(ctx, userId).getBiometrics();
+    }
+
+    @Override
+    public void addBiometricForUser(Context context, int userId,
+            BiometricAuthenticator.Identifier identifier) {
+        getStateForUser(context, userId).addBiometric(identifier);
+    }
+
+    @Override
+    public void removeBiometricForUser(Context context, int userId, int fingerId) {
+        getStateForUser(context, userId).removeBiometric(fingerId);
+    }
+
+    @Override
+    public void renameBiometricForUser(Context context, int userId, int fingerId,
+            CharSequence name) {
+        if (TextUtils.isEmpty(name)) {
+            // Don't do the rename if it's empty
+            return;
+        }
+        getStateForUser(context, userId).renameBiometric(fingerId, name);
+    }
+
+    @Override
+    public CharSequence getUniqueName(Context context, int userId) {
+        return getStateForUser(context, userId).getUniqueName();
+    }
+
+    private FingerprintUserState getStateForUser(Context ctx, int userId) {
+        synchronized (this) {
+            FingerprintUserState state = mUsers.get(userId);
+            if (state == null) {
+                state = new FingerprintUserState(ctx, userId);
+                mUsers.put(userId, state);
+            }
+            return state;
+        }
+    }
+}
+
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 5db20b0..e2ad5f5 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -21,7 +21,9 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
+import android.app.IUriGrantsManager;
 import android.app.KeyguardManager;
+import android.app.UriGrantsManager;
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.ContentProvider;
@@ -48,7 +50,9 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import java.io.IOException;
 import java.io.RandomAccessFile;
@@ -143,6 +147,8 @@
         SystemProperties.getBoolean("ro.kernel.qemu", false);
 
     private final IActivityManager mAm;
+    private final IUriGrantsManager mUgm;
+    private final UriGrantsManagerInternal mUgmInternal;
     private final IUserManager mUm;
     private final PackageManager mPm;
     private final AppOpsManager mAppOps;
@@ -159,15 +165,12 @@
         super(context);
 
         mAm = ActivityManager.getService();
+        mUgm = UriGrantsManager.getService();
+        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
         mPm = getContext().getPackageManager();
         mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
-        IBinder permOwner = null;
-        try {
-            permOwner = mAm.newUriPermissionOwner("clipboard");
-        } catch (RemoteException e) {
-            Slog.w("clipboard", "AM dead", e);
-        }
+        final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
         mPermissionOwner = permOwner;
         if (IS_EMULATOR) {
             mHostClipboardMonitor = new HostClipboardMonitor(
@@ -497,12 +500,10 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             // This will throw SecurityException if caller can't grant
-            mAm.checkGrantUriPermission(sourceUid, null,
+            mUgmInternal.checkGrantUriPermission(sourceUid, null,
                     ContentProvider.getUriWithoutUserId(uri),
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
-        } catch (RemoteException ignored) {
-            // Ignored because we're in same process
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -531,7 +532,7 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            mAm.grantUriPermissionFromOwner(mPermissionOwner, sourceUid, targetPkg,
+            mUgm.grantUriPermissionFromOwner(mPermissionOwner, sourceUid, targetPkg,
                     ContentProvider.getUriWithoutUserId(uri),
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)),
@@ -588,12 +589,10 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            mAm.revokeUriPermissionFromOwner(mPermissionOwner,
+            mUgmInternal.revokeUriPermissionFromOwner(mPermissionOwner,
                     ContentProvider.getUriWithoutUserId(uri),
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
-        } catch (RemoteException ignored) {
-            // Ignored because we're in same process
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 9b9a380..843ba2e 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -21,7 +21,10 @@
 import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS;
 import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC;
 import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL;
+import static android.net.metrics.ValidationProbeEvent.DNS_FAILURE;
+import static android.net.metrics.ValidationProbeEvent.DNS_SUCCESS;
 import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK;
+import static android.net.metrics.ValidationProbeEvent.PROBE_PRIVDNS;
 
 import android.annotation.Nullable;
 import android.app.PendingIntent;
@@ -258,7 +261,7 @@
     private final WifiManager mWifiManager;
     private final NetworkRequest mDefaultRequest;
     private final IpConnectivityLog mMetricsLog;
-    private final NetworkMonitorSettings mSettings;
+    private final Dependencies mDependencies;
 
     // Configuration values for captive portal detection probes.
     private final String mCaptivePortalUserAgent;
@@ -298,18 +301,19 @@
     // This variable is set before transitioning to the mCaptivePortalState.
     private CaptivePortalProbeResult mLastPortalProbeResult = CaptivePortalProbeResult.FAILED;
 
+    // Random generator to select fallback URL index
+    private final Random mRandom;
     private int mNextFallbackUrlIndex = 0;
 
     public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
             NetworkRequest defaultRequest) {
         this(context, handler, networkAgentInfo, defaultRequest, new IpConnectivityLog(),
-                NetworkMonitorSettings.DEFAULT);
+                Dependencies.DEFAULT);
     }
 
     @VisibleForTesting
     protected NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
-            NetworkRequest defaultRequest, IpConnectivityLog logger,
-            NetworkMonitorSettings settings) {
+            NetworkRequest defaultRequest, IpConnectivityLog logger, Dependencies deps) {
         // Add suffix indicating which NetworkMonitor we're talking about.
         super(TAG + networkAgentInfo.name());
 
@@ -320,9 +324,9 @@
         mContext = context;
         mMetricsLog = logger;
         mConnectivityServiceHandler = handler;
-        mSettings = settings;
+        mDependencies = deps;
         mNetworkAgentInfo = networkAgentInfo;
-        mNetwork = new OneAddressPerFamilyNetwork(networkAgentInfo.network());
+        mNetwork = deps.getNetwork(networkAgentInfo);
         mNetId = mNetwork.netId;
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
@@ -340,9 +344,10 @@
         mUseHttps = getUseHttpsValidation();
         mCaptivePortalUserAgent = getCaptivePortalUserAgent();
         mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl());
-        mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl(settings, context));
+        mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl(deps, context));
         mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
         mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs();
+        mRandom = deps.getRandom();
 
         start();
     }
@@ -799,8 +804,10 @@
                 final InetAddress[] ips = ResolvUtil.blockingResolveAllLocally(
                         mNetwork, mPrivateDnsProviderHostname, 0 /* aiFlags */);
                 mPrivateDnsConfig = new PrivateDnsConfig(mPrivateDnsProviderHostname, ips);
+                validationLog("Strict mode hostname resolved: " + mPrivateDnsConfig);
             } catch (UnknownHostException uhe) {
                 mPrivateDnsConfig = null;
+                validationLog("Strict mode hostname resolution failed: " + uhe.getMessage());
             }
         }
 
@@ -829,10 +836,21 @@
             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 Stopwatch watch = new Stopwatch().start();
             try {
                 final InetAddress[] ips = mNetworkAgentInfo.network().getAllByName(host);
-                return (ips != null && ips.length > 0);
-            } catch (UnknownHostException uhe) {}
+                final long time = watch.stop();
+                final String strIps = Arrays.toString(ips);
+                final boolean success = (ips != null && ips.length > 0);
+                validationLog(PROBE_PRIVDNS, host, String.format("%dms: %s", time, strIps));
+                logValidationProbe(time, PROBE_PRIVDNS, success ? DNS_SUCCESS : DNS_FAILURE);
+                return success;
+            } catch (UnknownHostException uhe) {
+                final long time = watch.stop();
+                validationLog(PROBE_PRIVDNS, host,
+                        String.format("%dms - Error: %s", time, uhe.getMessage()));
+                logValidationProbe(time, PROBE_PRIVDNS, DNS_FAILURE);
+            }
             return false;
         }
     }
@@ -867,40 +885,38 @@
     public boolean getIsCaptivePortalCheckEnabled() {
         String symbol = Settings.Global.CAPTIVE_PORTAL_MODE;
         int defaultValue = Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT;
-        int mode = mSettings.getSetting(mContext, symbol, defaultValue);
+        int mode = mDependencies.getSetting(mContext, symbol, defaultValue);
         return mode != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE;
     }
 
     public boolean getUseHttpsValidation() {
-        return mSettings.getSetting(mContext, Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
+        return mDependencies.getSetting(mContext, Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
     }
 
     public boolean getWifiScansAlwaysAvailableDisabled() {
-        return mSettings.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() {
-        return mSettings.getSetting(mContext,
+        return mDependencies.getSetting(mContext,
                 Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
     }
 
     // Static for direct access by ConnectivityService
     public static String getCaptivePortalServerHttpUrl(Context context) {
-        return getCaptivePortalServerHttpUrl(NetworkMonitorSettings.DEFAULT, context);
+        return getCaptivePortalServerHttpUrl(Dependencies.DEFAULT, context);
     }
 
-    public static String getCaptivePortalServerHttpUrl(
-            NetworkMonitorSettings settings, Context context) {
-        return settings.getSetting(
-                context, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, DEFAULT_HTTP_URL);
+    public static String getCaptivePortalServerHttpUrl(Dependencies deps, Context context) {
+        return deps.getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, DEFAULT_HTTP_URL);
     }
 
     private URL[] makeCaptivePortalFallbackUrls() {
         try {
             String separator = ",";
-            String firstUrl = mSettings.getSetting(mContext,
+            String firstUrl = mDependencies.getSetting(mContext,
                     Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, DEFAULT_FALLBACK_URL);
-            String joinedUrls = firstUrl + separator + mSettings.getSetting(mContext,
+            String joinedUrls = firstUrl + separator + mDependencies.getSetting(mContext,
                     Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS,
                     DEFAULT_OTHER_FALLBACK_URLS);
             List<URL> urls = new ArrayList<>();
@@ -924,7 +940,7 @@
 
     private CaptivePortalProbeSpec[] makeCaptivePortalFallbackProbeSpecs() {
         try {
-            final String settingsValue = mSettings.getSetting(
+            final String settingsValue = mDependencies.getSetting(
                     mContext, Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null);
             // Probe specs only used if configured in settings
             if (TextUtils.isEmpty(settingsValue)) {
@@ -940,7 +956,7 @@
     }
 
     private String getCaptivePortalUserAgent() {
-        return mSettings.getSetting(mContext,
+        return mDependencies.getSetting(mContext,
                 Settings.Global.CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT);
     }
 
@@ -949,7 +965,7 @@
             return null;
         }
         int idx = Math.abs(mNextFallbackUrlIndex) % mCaptivePortalFallbackUrls.length;
-        mNextFallbackUrlIndex += new Random().nextInt(); // randomely change url without memory.
+        mNextFallbackUrlIndex += mRandom.nextInt(); // randomly change url without memory.
         return mCaptivePortalFallbackUrls[idx];
     }
 
@@ -958,7 +974,7 @@
             return null;
         }
         // Randomly change spec without memory. Also randomize the first attempt.
-        final int idx = Math.abs(new Random().nextInt()) % mCaptivePortalFallbackSpecs.length;
+        final int idx = Math.abs(mRandom.nextInt()) % mCaptivePortalFallbackSpecs.length;
         return mCaptivePortalFallbackSpecs[idx];
     }
 
@@ -1376,15 +1392,15 @@
     }
 
     @VisibleForTesting
-    public interface NetworkMonitorSettings {
-        int getSetting(Context context, String symbol, int defaultValue);
-        String getSetting(Context context, String symbol, String defaultValue);
+    public static class Dependencies {
+        public Network getNetwork(NetworkAgentInfo networkAgentInfo) {
+            return new OneAddressPerFamilyNetwork(networkAgentInfo.network());
+        }
 
-        static NetworkMonitorSettings DEFAULT = new DefaultNetworkMonitorSettings();
-    }
+        public Random getRandom() {
+            return new Random();
+        }
 
-    @VisibleForTesting
-    public static class DefaultNetworkMonitorSettings implements NetworkMonitorSettings {
         public int getSetting(Context context, String symbol, int defaultValue) {
             return Settings.Global.getInt(context.getContentResolver(), symbol, defaultValue);
         }
@@ -1393,5 +1409,7 @@
             final String value = Settings.Global.getString(context.getContentResolver(), symbol);
             return value != null ? value : defaultValue;
         }
+
+        public static final Dependencies DEFAULT = new Dependencies();
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/OWNERS b/services/core/java/com/android/server/connectivity/OWNERS
index ce50558..7311eee 100644
--- a/services/core/java/com/android/server/connectivity/OWNERS
+++ b/services/core/java/com/android/server/connectivity/OWNERS
@@ -1,6 +1,8 @@
 set noparent
 
+codewiz@google.com
 ek@google.com
 jchalard@google.com
 lorenzo@google.com
+reminv@google.com
 satk@google.com
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 3a27fcb3..c370959 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -90,7 +90,7 @@
     private volatile boolean mHasDownloaded;
 
     private Handler mConnectivityHandler;
-    private int mProxyMessage;
+    private final int mProxyMessage;
 
     /**
      * Used for locking when setting mProxyService and all references to mCurrentPac.
@@ -99,7 +99,7 @@
 
     /**
      * Runnable to download PAC script.
-     * The behavior relies on the assamption it always run on mNetThread to guarantee that the
+     * The behavior relies on the assumption it always runs on mNetThread to guarantee that the
      * latest data fetched from mPacUrl is stored in mProxyService.
      */
     private Runnable mPacDownloader = new Runnable() {
@@ -133,8 +133,6 @@
         }
     };
 
-    private final HandlerThread mNetThread = new HandlerThread("android.pacmanager",
-            android.os.Process.THREAD_PRIORITY_DEFAULT);
     private final Handler mNetThreadHandler;
 
     class PacRefreshIntentReceiver extends BroadcastReceiver {
@@ -146,8 +144,10 @@
     public PacManager(Context context, Handler handler, int proxyMessage) {
         mContext = context;
         mLastPort = -1;
-        mNetThread.start();
-        mNetThreadHandler = new Handler(mNetThread.getLooper());
+        final HandlerThread netThread = new HandlerThread("android.pacmanager",
+                android.os.Process.THREAD_PRIORITY_DEFAULT);
+        netThread.start();
+        mNetThreadHandler = new Handler(netThread.getLooper());
 
         mPacRefreshIntent = PendingIntent.getBroadcast(
                 context, 0, new Intent(ACTION_PAC_REFRESH), 0);
@@ -166,14 +166,14 @@
 
     /**
      * Updates the PAC Manager with current Proxy information. This is called by
-     * the ConnectivityService directly before a broadcast takes place to allow
+     * the ProxyTracker directly before a broadcast takes place to allow
      * the PacManager to indicate that the broadcast should not be sent and the
      * 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
      */
-    public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
+    synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
         if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
             if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
                 // Allow to send broadcast, nothing to do.
@@ -208,7 +208,7 @@
     /**
      * Does a post and reports back the status code.
      *
-     * @throws IOException
+     * @throws IOException if the URL is malformed, or the PAC file is too big.
      */
     private static String get(Uri pacUri) throws IOException {
         URL url = new URL(pacUri.toString());
@@ -254,7 +254,7 @@
     private String getPacChangeDelay() {
         final ContentResolver cr = mContext.getContentResolver();
 
-        /** Check system properties for the default value then use secure settings value, if any. */
+        // Check system properties for the default value then use secure settings value, if any.
         String defaultDelay = SystemProperties.get(
                 "conn." + Settings.Global.PAC_CHANGE_DELAY,
                 DEFAULT_DELAYS);
@@ -276,10 +276,9 @@
         getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
     }
 
-    private boolean setCurrentProxyScript(String script) {
+    private void setCurrentProxyScript(String script) {
         if (mProxyService == null) {
             Log.e(TAG, "setCurrentProxyScript: no proxy service");
-            return false;
         }
         try {
             mProxyService.setPacFile(script);
@@ -287,7 +286,6 @@
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to set PAC file", e);
         }
-        return true;
     }
 
     private void bind() {
@@ -351,7 +349,7 @@
                     try {
                         callbackService.getProxyPort(new IProxyPortListener.Stub() {
                             @Override
-                            public void setProxyPort(int port) throws RemoteException {
+                            public void setProxyPort(int port) {
                                 if (mLastPort != -1) {
                                     // Always need to send if port changed
                                     mHasSentBroadcast = false;
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
new file mode 100644
index 0000000..dc65e1e
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -0,0 +1,256 @@
+/**
+ * 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.connectivity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Proxy;
+import android.net.ProxyInfo;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Objects;
+
+/**
+ * A class to handle proxy for ConnectivityService.
+ *
+ * @hide
+ */
+public class ProxyTracker {
+    private static final String TAG = ProxyTracker.class.getSimpleName();
+    private static final boolean DBG = true;
+
+    @NonNull
+    private final Context mContext;
+
+    // TODO : make this private and import as much managing logic from ConnectivityService as
+    // possible
+    @NonNull
+    public final Object mProxyLock = new Object();
+    // The global proxy is the proxy that is set device-wide, overriding any network-specific
+    // proxy. Note however that proxies are hints ; the system does not enforce their use. Hence
+    // this value is only for querying.
+    @Nullable
+    @GuardedBy("mProxyLock")
+    public ProxyInfo mGlobalProxy = null;
+    // The default proxy is the proxy that applies to no particular network if the global proxy
+    // is not set. Individual networks have their own settings that override this. This member
+    // is set through setDefaultProxy, which is called when the default network changes proxies
+    // in its LinkProperties, or when ConnectivityService switches to a new default network, or
+    // when PacManager resolves the proxy.
+    @Nullable
+    @GuardedBy("mProxyLock")
+    public volatile ProxyInfo mDefaultProxy = null;
+    // Whether the default proxy is disabled. TODO : make this mDefaultProxyEnabled
+    @GuardedBy("mProxyLock")
+    public boolean mDefaultProxyDisabled = false;
+
+    // The object responsible for Proxy Auto Configuration (PAC).
+    @NonNull
+    private final PacManager mPacManager;
+
+    public ProxyTracker(@NonNull final Context context,
+            @NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
+        mContext = context;
+        mPacManager = new PacManager(context, connectivityServiceInternalHandler, pacChangedEvent);
+    }
+
+    // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
+    // (e.g. if mGlobalProxy==null fall back to network-specific proxy, if network-specific
+    // proxy is null then there is no proxy in place).
+    @Nullable
+    private static ProxyInfo canonicalizeProxyInfo(@Nullable final ProxyInfo proxy) {
+        if (proxy != null && TextUtils.isEmpty(proxy.getHost())
+                && (proxy.getPacFileUrl() == null || Uri.EMPTY.equals(proxy.getPacFileUrl()))) {
+            return null;
+        }
+        return proxy;
+    }
+
+    // ProxyInfo equality functions with a couple modifications over ProxyInfo.equals() to make it
+    // better for determining if a new proxy broadcast is necessary:
+    // 1. Canonicalize empty ProxyInfos to null so an empty proxy compares equal to null so as to
+    //    avoid unnecessary broadcasts.
+    // 2. Make sure all parts of the ProxyInfo's compare true, including the host when a PAC URL
+    //    is in place.  This is important so legacy PAC resolver (see com.android.proxyhandler)
+    //    changes aren't missed.  The legacy PAC resolver pretends to be a simple HTTP proxy but
+    //    actually uses the PAC to resolve; this results in ProxyInfo's with PAC URL, host and port
+    //    all set.
+    public static boolean proxyInfoEqual(@Nullable final ProxyInfo a, @Nullable final ProxyInfo b) {
+        final ProxyInfo pa = canonicalizeProxyInfo(a);
+        final ProxyInfo pb = canonicalizeProxyInfo(b);
+        // ProxyInfo.equals() doesn't check hosts when PAC URLs are present, but we need to check
+        // hosts even when PAC URLs are present to account for the legacy PAC resolver.
+        return Objects.equals(pa, pb) && (pa == null || Objects.equals(pa.getHost(), pb.getHost()));
+    }
+
+    /**
+     * Gets the default system-wide proxy.
+     *
+     * This will return the global proxy if set, otherwise the default proxy if in use. Note
+     * that this is not necessarily the proxy that any given process should use, as the right
+     * proxy for a process is the proxy for the network this process will use, which may be
+     * different from this value. This value is simply the default in case there is no proxy set
+     * in the network that will be used by a specific process.
+     * @return The default system-wide proxy or null if none.
+     */
+    @Nullable
+    public ProxyInfo getDefaultProxy() {
+        // This information is already available as a world read/writable jvm property.
+        synchronized (mProxyLock) {
+            final ProxyInfo ret = mGlobalProxy;
+            if ((ret == null) && !mDefaultProxyDisabled) return mDefaultProxy;
+            return ret;
+        }
+    }
+
+    /**
+     * Gets the global proxy.
+     *
+     * @return The global proxy or null if none.
+     */
+    @Nullable
+    public ProxyInfo getGlobalProxy() {
+        // This information is already available as a world read/writable jvm property.
+        synchronized (mProxyLock) {
+            return mGlobalProxy;
+        }
+    }
+
+    /**
+     * Sends the system broadcast informing apps about a new proxy configuration.
+     *
+     * Confusingly this method also sets the PAC file URL. TODO : separate this, it has nothing
+     * to do in a "sendProxyBroadcast" method.
+     * @param proxyInfo the proxy spec, or null for no proxy.
+     */
+    // TODO : make the argument NonNull final and the method private
+    public void sendProxyBroadcast(@Nullable ProxyInfo proxyInfo) {
+        if (proxyInfo == null) proxyInfo = new ProxyInfo("", 0, "");
+        if (mPacManager.setCurrentProxyScriptUrl(proxyInfo)) 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 |
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxyInfo);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Sets the global proxy in memory. Also writes the values to the global settings of the device.
+     *
+     * @param proxyInfo the proxy spec, or null for no proxy.
+     */
+    public void setGlobalProxy(@Nullable ProxyInfo proxyInfo) {
+        synchronized (mProxyLock) {
+            // ProxyInfo#equals is not commutative :( and is public API, so it can't be fixed.
+            if (proxyInfo == mGlobalProxy) return;
+            if (proxyInfo != null && proxyInfo.equals(mGlobalProxy)) return;
+            if (mGlobalProxy != null && mGlobalProxy.equals(proxyInfo)) return;
+
+            final String host;
+            final int port;
+            final String exclList;
+            final String pacFileUrl;
+            if (proxyInfo != null && (!TextUtils.isEmpty(proxyInfo.getHost()) ||
+                    !Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))) {
+                if (!proxyInfo.isValid()) {
+                    if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
+                    return;
+                }
+                mGlobalProxy = new ProxyInfo(proxyInfo);
+                host = mGlobalProxy.getHost();
+                port = mGlobalProxy.getPort();
+                exclList = mGlobalProxy.getExclusionListAsString();
+                pacFileUrl = Uri.EMPTY.equals(proxyInfo.getPacFileUrl())
+                        ? "" : proxyInfo.getPacFileUrl().toString();
+            } else {
+                host = "";
+                port = 0;
+                exclList = "";
+                pacFileUrl = "";
+                mGlobalProxy = null;
+            }
+            final ContentResolver res = mContext.getContentResolver();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, host);
+                Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, port);
+                Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
+                        exclList);
+                Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+
+            sendProxyBroadcast(mGlobalProxy == null ? mDefaultProxy : proxyInfo);
+        }
+    }
+
+    /**
+     * Sets the default proxy for the device.
+     *
+     * The default proxy is the proxy used for networks that do not have a specific proxy.
+     * @param proxyInfo the proxy spec, or null for no proxy.
+     */
+    public void setDefaultProxy(@Nullable ProxyInfo proxyInfo) {
+        synchronized (mProxyLock) {
+            if (mDefaultProxy != null && mDefaultProxy.equals(proxyInfo)) {
+                return;
+            }
+            if (mDefaultProxy == proxyInfo) return; // catches repeated nulls
+            if (proxyInfo != null &&  !proxyInfo.isValid()) {
+                if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
+                return;
+            }
+
+            // This call could be coming from the PacManager, containing the port of the local
+            // proxy. If this new proxy matches the global proxy then copy this proxy to the
+            // global (to get the correct local port), and send a broadcast.
+            // TODO: Switch PacManager to have its own message to send back rather than
+            // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
+            if ((mGlobalProxy != null) && (proxyInfo != null)
+                    && (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
+                    && proxyInfo.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) {
+                mGlobalProxy = proxyInfo;
+                sendProxyBroadcast(mGlobalProxy);
+                return;
+            }
+            mDefaultProxy = proxyInfo;
+
+            if (mGlobalProxy != null) return;
+            if (!mDefaultProxyDisabled) {
+                sendProxyBroadcast(proxyInfo);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index df6a6f8..84bdbba 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -115,7 +115,6 @@
 import com.android.server.connectivity.tethering.IControlsTethering;
 import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
 import com.android.server.connectivity.tethering.OffloadController;
-import com.android.server.connectivity.tethering.SimChangeListener;
 import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
 import com.android.server.connectivity.tethering.TetheringConfiguration;
 import com.android.server.connectivity.tethering.TetheringDependencies;
@@ -201,8 +200,6 @@
     // into a single coherent structure.
     private final HashSet<TetherInterfaceStateMachine> mForwardedDownstreams;
     private final VersionedBroadcastListener mCarrierConfigChange;
-    // TODO: Delete SimChangeListener; it's obsolete.
-    private final SimChangeListener mSimChange;
     private final TetheringDependencies mDeps;
 
     private volatile TetheringConfiguration mConfig;
@@ -252,14 +249,6 @@
                     updateConfiguration();
                     reevaluateSimCardProvisioning();
                 });
-        // TODO: Remove SimChangeListener altogether. For now, we retain it
-        // for logging purposes in case we need to debug something that might
-        // be related to changing signals from ACTION_SIM_STATE_CHANGED to
-        // ACTION_CARRIER_CONFIG_CHANGED.
-        mSimChange = new SimChangeListener(
-                mContext, smHandler, () -> {
-                    mLog.log("OBSERVED SIM card change");
-                });
 
         mStateReceiver = new StateReceiver();
 
@@ -1350,8 +1339,11 @@
             // do not currently know how to watch for changes in DUN settings.
             maybeUpdateConfiguration();
 
-            final NetworkState ns = mUpstreamNetworkMonitor.selectPreferredUpstreamType(
-                    mConfig.preferredUpstreamIfaceTypes);
+            final TetheringConfiguration config = mConfig;
+            final NetworkState ns = (config.chooseUpstreamAutomatically)
+                    ? mUpstreamNetworkMonitor.getCurrentPreferredUpstream()
+                    : mUpstreamNetworkMonitor.selectPreferredUpstreamType(
+                            config.preferredUpstreamIfaceTypes);
             if (ns == null) {
                 if (tryCell) {
                     mUpstreamNetworkMonitor.registerMobileNetworkRequest();
@@ -1380,9 +1372,7 @@
             }
             notifyDownstreamsOfNewUpstreamIface(ifaces);
             if (ns != null && pertainsToCurrentUpstream(ns)) {
-                // If we already have NetworkState for this network examine
-                // it immediately, because there likely will be no second
-                // EVENT_ON_AVAILABLE (it was already received).
+                // If we already have NetworkState for this network update it immediately.
                 handleNewUpstreamNetworkState(ns);
             } else if (mCurrentUpstreamIfaceSet == null) {
                 // There are no available upstream networks.
@@ -1498,15 +1488,6 @@
             }
 
             switch (arg1) {
-                case UpstreamNetworkMonitor.EVENT_ON_AVAILABLE:
-                    // The default network changed, or DUN connected
-                    // before this callback was processed. Updates
-                    // for the current NetworkCapabilities and
-                    // LinkProperties have been requested (default
-                    // request) or are being sent shortly (DUN). Do
-                    // nothing until they arrive; if no updates
-                    // arrive there's nothing to do.
-                    break;
                 case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
                     handleNewUpstreamNetworkState(ns);
                     break;
@@ -1538,8 +1519,7 @@
                     return;
                 }
 
-                mSimChange.startListening();
-                mUpstreamNetworkMonitor.start();
+                mUpstreamNetworkMonitor.start(mDeps.getDefaultNetworkRequest());
 
                 // TODO: De-duplicate with updateUpstreamWanted() below.
                 if (upstreamWanted()) {
@@ -1554,7 +1534,6 @@
             public void exit() {
                 mOffload.stop();
                 mUpstreamNetworkMonitor.stop();
-                mSimChange.stopListening();
                 notifyDownstreamsOfNewUpstreamIface(null);
                 handleNewUpstreamNetworkState(null);
             }
diff --git a/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java b/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java
deleted file mode 100644
index 33c9355..0000000
--- a/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java
+++ /dev/null
@@ -1,79 +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.connectivity.tethering;
-
-import static com.android.internal.telephony.IccCardConstants.INTENT_VALUE_ICC_LOADED;
-import static com.android.internal.telephony.IccCardConstants.INTENT_KEY_ICC_STATE;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.util.VersionedBroadcastListener;
-import android.net.util.VersionedBroadcastListener.IntentCallback;
-import android.os.Handler;
-import android.util.Log;
-
-import com.android.internal.telephony.TelephonyIntents;
-
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-
-
-/**
- * A utility class that runs the provided callback on the provided handler when
- * observing a new SIM card having been loaded.
- *
- * @hide
- */
-public class SimChangeListener extends VersionedBroadcastListener {
-    private static final String TAG = SimChangeListener.class.getSimpleName();
-    private static final boolean DBG = false;
-
-    public SimChangeListener(Context ctx, Handler handler, Runnable onSimCardLoadedCallback) {
-        super(TAG, ctx, handler, makeIntentFilter(), makeCallback(onSimCardLoadedCallback));
-    }
-
-    private static IntentFilter makeIntentFilter() {
-        final IntentFilter filter = new IntentFilter();
-        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
-        return filter;
-    }
-
-    private static Consumer<Intent> makeCallback(Runnable onSimCardLoadedCallback) {
-        return new Consumer<Intent>() {
-            private boolean mSimNotLoadedSeen = false;
-
-            @Override
-            public void accept(Intent intent) {
-                final String state = intent.getStringExtra(INTENT_KEY_ICC_STATE);
-                Log.d(TAG, "got Sim changed to state " + state + ", mSimNotLoadedSeen=" +
-                        mSimNotLoadedSeen);
-
-                if (!INTENT_VALUE_ICC_LOADED.equals(state)) {
-                    mSimNotLoadedSeen = true;
-                    return;
-                }
-
-                if (mSimNotLoadedSeen) {
-                    mSimNotLoadedSeen = false;
-                    onSimCardLoadedCallback.run();
-                }
-            }
-        };
-    }
-}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index fbee86a..9e6b659 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -379,7 +379,7 @@
                     // that adding routes that already exist does not cause an
                     // error (EEXIST is silently ignored).
                     mNMService.addInterfaceToLocalNetwork(mIfaceName, toBeAdded);
-                } catch (RemoteException e) {
+                } catch (Exception e) {
                     mLog.e("Failed to add IPv6 routes to local table: " + e);
                 }
 
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 454c579..dd9fe05 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -27,6 +27,7 @@
 import static com.android.internal.R.array.config_tether_usb_regexs;
 import static com.android.internal.R.array.config_tether_upstream_types;
 import static com.android.internal.R.array.config_tether_wifi_regexs;
+import static com.android.internal.R.bool.config_tether_upstream_automatic;
 import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
 
 import android.content.Context;
@@ -86,6 +87,7 @@
     public final String[] tetherableBluetoothRegexs;
     public final int dunCheck;
     public final boolean isDunRequired;
+    public final boolean chooseUpstreamAutomatically;
     public final Collection<Integer> preferredUpstreamIfaceTypes;
     public final String[] dhcpRanges;
     public final String[] defaultIPv4DNS;
@@ -106,6 +108,7 @@
         dunCheck = checkDunRequired(ctx);
         configLog.log("DUN check returned: " + dunCheckString(dunCheck));
 
+        chooseUpstreamAutomatically = getResourceBoolean(ctx, config_tether_upstream_automatic);
         preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck);
         isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN);
 
@@ -142,6 +145,8 @@
         pw.print("isDunRequired: ");
         pw.println(isDunRequired);
 
+        pw.print("chooseUpstreamAutomatically: ");
+        pw.println(chooseUpstreamAutomatically);
         dumpStringArray(pw, "preferredUpstreamIfaceTypes",
                 preferredUpstreamNames(preferredUpstreamIfaceTypes));
 
@@ -160,6 +165,7 @@
         sj.add(String.format("tetherableBluetoothRegexs:%s",
                 makeString(tetherableBluetoothRegexs)));
         sj.add(String.format("isDunRequired:%s", isDunRequired));
+        sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically));
         sj.add(String.format("preferredUpstreamIfaceTypes:%s",
                 makeString(preferredUpstreamNames(preferredUpstreamIfaceTypes))));
         sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
@@ -286,6 +292,14 @@
         }
     }
 
+    private static boolean getResourceBoolean(Context ctx, int resId) {
+        try {
+            return ctx.getResources().getBoolean(resId);
+        } catch (Resources.NotFoundException e404) {
+            return false;
+        }
+    }
+
     private static String[] getResourceStringArray(Context ctx, int resId) {
         try {
             final String[] strArray = ctx.getResources().getStringArray(resId);
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 0ac7a36..605ee9c 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.net.INetd;
+import android.net.NetworkRequest;
 import android.net.ip.RouterAdvertisementDaemon;
 import android.net.util.InterfaceParams;
 import android.net.util.NetdService;
@@ -64,4 +65,8 @@
     public boolean isTetheringSupported() {
         return true;
     }
+
+    public NetworkRequest getDefaultNetworkRequest() {
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 3413291..f488be7 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -20,6 +20,9 @@
 import static android.net.ConnectivityManager.TYPE_NONE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 
 import android.content.Context;
 import android.os.Handler;
@@ -74,14 +77,13 @@
     private static final boolean DBG = false;
     private static final boolean VDBG = false;
 
-    public static final int EVENT_ON_AVAILABLE      = 1;
-    public static final int EVENT_ON_CAPABILITIES   = 2;
-    public static final int EVENT_ON_LINKPROPERTIES = 3;
-    public static final int EVENT_ON_LOST           = 4;
+    public static final int EVENT_ON_CAPABILITIES   = 1;
+    public static final int EVENT_ON_LINKPROPERTIES = 2;
+    public static final int EVENT_ON_LOST           = 3;
     public static final int NOTIFY_LOCAL_PREFIXES   = 10;
 
     private static final int CALLBACK_LISTEN_ALL = 1;
-    private static final int CALLBACK_TRACK_DEFAULT = 2;
+    private static final int CALLBACK_DEFAULT_INTERNET = 2;
     private static final int CALLBACK_MOBILE_REQUEST = 3;
 
     private final Context mContext;
@@ -117,7 +119,7 @@
         mCM = cm;
     }
 
-    public void start() {
+    public void start(NetworkRequest defaultNetworkRequest) {
         stop();
 
         final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
@@ -125,8 +127,16 @@
         mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
         cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
 
-        mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_TRACK_DEFAULT);
-        cm().registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
+        if (defaultNetworkRequest != null) {
+            // This is not really a "request", just a way of tracking the system default network.
+            // It's guaranteed not to actually bring up any networks because it's the same request
+            // as the ConnectivityService default request, and thus shares fate with it. We can't
+            // use registerDefaultNetworkCallback because it will not track the system default
+            // network if there is a VPN that applies to our UID.
+            final NetworkRequest trackDefaultRequest = new NetworkRequest(defaultNetworkRequest);
+            mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
+            cm().requestNetwork(trackDefaultRequest, mDefaultNetworkCallback, mHandler);
+        }
     }
 
     public void stop() {
@@ -225,6 +235,20 @@
         return typeStatePair.ns;
     }
 
+    // Returns null if no current upstream available.
+    public NetworkState getCurrentPreferredUpstream() {
+        final NetworkState dfltState = (mDefaultInternetNetwork != null)
+                ? mNetworkMap.get(mDefaultInternetNetwork)
+                : null;
+        if (!mDunRequired) return dfltState;
+
+        if (isNetworkUsableAndNotCellular(dfltState)) return dfltState;
+
+        // Find a DUN network. Note that code in Tethering causes a DUN request
+        // to be filed, but this might be moved into this class in future.
+        return findFirstDunNetwork(mNetworkMap.values());
+    }
+
     public void setCurrentUpstream(Network upstream) {
         mTetheringUpstreamNetwork = upstream;
     }
@@ -233,72 +257,16 @@
         return (Set<IpPrefix>) mLocalPrefixes.clone();
     }
 
-    private void handleAvailable(int callbackType, Network network) {
-        if (VDBG) Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
+    private void handleAvailable(Network network) {
+        if (mNetworkMap.containsKey(network)) return;
 
-        if (!mNetworkMap.containsKey(network)) {
-            mNetworkMap.put(network,
-                    new NetworkState(null, null, null, network, null, null));
-        }
-
-        // Always request whatever extra information we can, in case this
-        // was already up when start() was called, in which case we would
-        // not have been notified of any information that had not changed.
-        switch (callbackType) {
-            case CALLBACK_LISTEN_ALL:
-                break;
-
-            case CALLBACK_TRACK_DEFAULT:
-                if (mDefaultNetworkCallback == null) {
-                    // The callback was unregistered in the interval between
-                    // ConnectivityService enqueueing onAvailable() and our
-                    // handling of it here on the mHandler thread.
-                    //
-                    // Clean-up of this network entry is deferred to the
-                    // handling of onLost() by other callbacks.
-                    //
-                    // These request*() calls can be deleted post oag/339444.
-                    return;
-                }
-                mDefaultInternetNetwork = network;
-                break;
-
-            case CALLBACK_MOBILE_REQUEST:
-                if (mMobileNetworkCallback == null) {
-                    // The callback was unregistered in the interval between
-                    // ConnectivityService enqueueing onAvailable() and our
-                    // handling of it here on the mHandler thread.
-                    //
-                    // Clean-up of this network entry is deferred to the
-                    // handling of onLost() by other callbacks.
-                    return;
-                }
-                break;
-        }
-
-        // Requesting updates for mListenAllCallback is not currently possible
-        // because it's a "listen". Two possible solutions to getting updates
-        // about networks without waiting for a change (which might never come)
-        // are:
-        //
-        //     [1] extend request{NetworkCapabilities,LinkProperties}() to
-        //         take a Network argument and have ConnectivityService do
-        //         what's required (if the network satisfies the request)
-        //
-        //     [2] explicitly file a NetworkRequest for each connectivity type
-        //         listed as a preferred upstream and wait for these callbacks
-        //         to be notified (requires tracking many more callbacks).
-        //
-        // Until this is addressed, networks that exist prior to the "listen"
-        // registration and which do not subsequently change will not cause
-        // us to learn their NetworkCapabilities nor their LinkProperties.
-
-        // TODO: If sufficient information is available to select a more
-        // preferable upstream, do so now and notify the target.
-        notifyTarget(EVENT_ON_AVAILABLE, network);
+        if (VDBG) Log.d(TAG, "onAvailable for " + network);
+        mNetworkMap.put(network, new NetworkState(null, null, null, network, null, null));
     }
 
-    private void handleNetCap(Network network, NetworkCapabilities newNc) {
+    private void handleNetCap(int callbackType, Network network, NetworkCapabilities newNc) {
+        if (callbackType == CALLBACK_DEFAULT_INTERNET) mDefaultInternetNetwork = network;
+
         final NetworkState prev = mNetworkMap.get(network);
         if (prev == null || newNc.equals(prev.networkCapabilities)) {
             // Ignore notifications about networks for which we have not yet
@@ -360,13 +328,17 @@
     }
 
     private void handleLost(int callbackType, Network network) {
-        if (callbackType == CALLBACK_TRACK_DEFAULT) {
+        if (network.equals(mDefaultInternetNetwork)) {
             mDefaultInternetNetwork = null;
-            // Receiving onLost() for a default network does not necessarily
-            // mean the network is gone.  We wait for a separate notification
-            // on either the LISTEN_ALL or MOBILE_REQUEST callbacks before
-            // clearing all state.
-            return;
+            // There are few TODOs within ConnectivityService's rematching code
+            // pertaining to spurious onLost() notifications.
+            //
+            // TODO: simplify this, probably if favor of code that:
+            //     - selects a new upstream if mTetheringUpstreamNetwork has
+            //       been lost (by any callback)
+            //     - deletes the entry from the map only when the LISTEN_ALL
+            //       callback gets  notified.
+            if (callbackType == CALLBACK_DEFAULT_INTERNET) return;
         }
 
         if (!mNetworkMap.containsKey(network)) {
@@ -416,17 +388,19 @@
 
         @Override
         public void onAvailable(Network network) {
-            handleAvailable(mCallbackType, network);
+            handleAvailable(network);
         }
 
         @Override
         public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
-            handleNetCap(network, newNc);
+            handleNetCap(mCallbackType, network, newNc);
         }
 
         @Override
         public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
             handleLinkProp(network, newLp);
+            // TODO(b/110335330): reduce the number of times this is called by
+            // only recomputing on the LISTEN_ALL callback.
             recomputeLocalPrefixes();
         }
 
@@ -443,6 +417,8 @@
         @Override
         public void onLost(Network network) {
             handleLost(mCallbackType, network);
+            // TODO(b/110335330): reduce the number of times this is called by
+            // only recomputing on the LISTEN_ALL callback.
             recomputeLocalPrefixes();
         }
     }
@@ -509,4 +485,31 @@
         if (nc == null || !nc.hasSignalStrength()) return "unknown";
         return Integer.toString(nc.getSignalStrength());
     }
+
+    private static boolean isCellular(NetworkState ns) {
+        return (ns != null) && isCellular(ns.networkCapabilities);
+    }
+
+    private static boolean isCellular(NetworkCapabilities nc) {
+        return (nc != null) && nc.hasTransport(TRANSPORT_CELLULAR) &&
+               nc.hasCapability(NET_CAPABILITY_NOT_VPN);
+    }
+
+    private static boolean hasCapability(NetworkState ns, int netCap) {
+        return (ns != null) && (ns.networkCapabilities != null) &&
+               ns.networkCapabilities.hasCapability(netCap);
+    }
+
+    private static boolean isNetworkUsableAndNotCellular(NetworkState ns) {
+        return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null) &&
+               !isCellular(ns.networkCapabilities);
+    }
+
+    private static NetworkState findFirstDunNetwork(Iterable<NetworkState> netStates) {
+        for (NetworkState ns : netStates) {
+            if (isCellular(ns) && hasCapability(ns, NET_CAPABILITY_DUN)) return ns;
+        }
+
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index 089632d..bfcc629 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -16,12 +16,10 @@
 
 package com.android.server.content;
 
+import android.annotation.Nullable;
 import android.app.job.JobParameters;
 import android.app.job.JobService;
-import android.content.Intent;
 import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
 import android.util.Slog;
@@ -34,78 +32,86 @@
 public class SyncJobService extends JobService {
     private static final String TAG = "SyncManager";
 
-    public static final String EXTRA_MESSENGER = "messenger";
+    private static final Object sLock = new Object();
 
-    private Messenger mMessenger;
+    @GuardedBy("sLock")
+    private static SyncJobService sInstance;
 
-    private final Object mLock = new Object();
+    @GuardedBy("sLock")
+    private static final SparseArray<JobParameters> sJobParamsMap = new SparseArray<>();
 
-    @GuardedBy("mLock")
-    private final SparseArray<JobParameters> mJobParamsMap = new SparseArray<>();
+    @GuardedBy("sLock")
+    private static final SparseBooleanArray sStartedSyncs = new SparseBooleanArray();
 
-    @GuardedBy("mLock")
-    private final SparseBooleanArray mStartedSyncs = new SparseBooleanArray();
+    @GuardedBy("sLock")
+    private static final SparseLongArray sJobStartUptimes = new SparseLongArray();
 
-    @GuardedBy("mLock")
-    private final SparseLongArray mJobStartUptimes = new SparseLongArray();
+    private static final SyncLogger sLogger = SyncLogger.getInstance();
 
-    private final SyncLogger mLogger = SyncLogger.getInstance();
-
-    /**
-     * This service is started by the SyncManager which passes a messenger object to
-     * communicate back with it. It never stops while the device is running.
-     */
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        mMessenger = intent.getParcelableExtra(EXTRA_MESSENGER);
-        Message m = Message.obtain();
-        m.what = SyncManager.SyncHandler.MESSAGE_JOBSERVICE_OBJECT;
-        m.obj = this;
-        sendMessage(m);
-
-        return START_NOT_STICKY;
+    private void updateInstance() {
+        synchronized (SyncJobService.class) {
+            sInstance = this;
+        }
     }
 
-    private void sendMessage(Message message) {
-        if (mMessenger == null) {
-            Slog.e(TAG, "Messenger not initialized.");
-            return;
+    @Nullable
+    private static SyncJobService getInstance() {
+        synchronized (sLock) {
+            if (sInstance == null) {
+                Slog.wtf(TAG, "sInstance == null");
+            }
+            return sInstance;
         }
-        try {
-            mMessenger.send(message);
-        } catch (RemoteException e) {
-            Slog.e(TAG, e.toString());
+    }
+
+    public static boolean isReady() {
+        synchronized (sLock) {
+            return sInstance != null;
         }
     }
 
     @Override
     public boolean onStartJob(JobParameters params) {
+        updateInstance();
 
-        mLogger.purgeOldLogs();
+        sLogger.purgeOldLogs();
+
+        SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
+
+        if (op == null) {
+            Slog.wtf(TAG, "Got invalid job " + params.getJobId());
+            return false;
+        }
+
+        final boolean readyToSync = SyncManager.readyToSync(op.target.userId);
+
+        sLogger.log("onStartJob() jobid=", params.getJobId(), " op=", op,
+                " readyToSync", readyToSync);
+
+        if (!readyToSync) {
+            // If the user isn't unlocked or the device has been provisioned yet, just stop the job
+            // at this point. If it's a non-periodic sync, ask the job scheduler to reschedule it.
+            // If it's a periodic sync, then just wait until the next cycle.
+            final boolean wantsReschedule = !op.isPeriodic;
+            jobFinished(params, wantsReschedule);
+            return true;
+        }
 
         boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
-        synchronized (mLock) {
+        synchronized (sLock) {
             final int jobId = params.getJobId();
-            mJobParamsMap.put(jobId, params);
+            sJobParamsMap.put(jobId, params);
 
-            mStartedSyncs.delete(jobId);
-            mJobStartUptimes.put(jobId, SystemClock.uptimeMillis());
+            sStartedSyncs.delete(jobId);
+            sJobStartUptimes.put(jobId, SystemClock.uptimeMillis());
         }
         Message m = Message.obtain();
         m.what = SyncManager.SyncHandler.MESSAGE_START_SYNC;
-        SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
-
-        mLogger.log("onStartJob() jobid=", params.getJobId(), " op=", op);
-
-        if (op == null) {
-            Slog.e(TAG, "Got invalid job " + params.getJobId());
-            return false;
-        }
         if (isLoggable) {
             Slog.v(TAG, "Got start job message " + op.target);
         }
         m.obj = op;
-        sendMessage(m);
+        SyncManager.sendMessage(m);
         return true;
     }
 
@@ -115,15 +121,22 @@
             Slog.v(TAG, "onStopJob called " + params.getJobId() + ", reason: "
                     + params.getStopReason());
         }
-        final boolean readyToSync = SyncManager.readyToSync();
+        final SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
+        if (op == null) {
+            Slog.wtf(TAG, "Got invalid job " + params.getJobId());
+            return false;
+        }
 
-        mLogger.log("onStopJob() ", mLogger.jobParametersToString(params),
+        final boolean readyToSync = SyncManager.readyToSync(op.target.userId);
+
+        sLogger.log("onStopJob() ", sLogger.jobParametersToString(params),
                 " readyToSync=", readyToSync);
-        synchronized (mLock) {
-            final int jobId = params.getJobId();
-            mJobParamsMap.remove(jobId);
 
-            final long startUptime = mJobStartUptimes.get(jobId);
+        synchronized (sLock) {
+            final int jobId = params.getJobId();
+            sJobParamsMap.remove(jobId);
+
+            final long startUptime = sJobStartUptimes.get(jobId);
             final long nowUptime = SystemClock.uptimeMillis();
             final long runtime = nowUptime - startUptime;
 
@@ -135,61 +148,57 @@
                 // WTF if startSyncH() hasn't happened, *unless* onStopJob() was called too soon.
                 // (1 minute threshold.)
                 // Also don't wtf when it's not ready to sync.
-                if (readyToSync && !mStartedSyncs.get(jobId)) {
+                if (readyToSync && !sStartedSyncs.get(jobId)) {
                     wtf("Job " + jobId + " didn't start: "
                             + " startUptime=" + startUptime
                             + " nowUptime=" + nowUptime
                             + " params=" + jobParametersToString(params));
                 }
-            } else if (runtime < 10 * 1000) {
-                // This happens too in a normal case too, and it's rather too often.
-                // Disable it for now.
-//                // Job stopped too soon. WTF.
-//                wtf("Job " + jobId + " stopped in " + runtime + " ms: "
-//                        + " startUptime=" + startUptime
-//                        + " nowUptime=" + nowUptime
-//                        + " params=" + jobParametersToString(params));
             }
 
-            mStartedSyncs.delete(jobId);
-            mJobStartUptimes.delete(jobId);
+            sStartedSyncs.delete(jobId);
+            sJobStartUptimes.delete(jobId);
         }
         Message m = Message.obtain();
         m.what = SyncManager.SyncHandler.MESSAGE_STOP_SYNC;
-        m.obj = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
-        if (m.obj == null) {
-            return false;
-        }
+        m.obj = op;
 
         // Reschedule if this job was NOT explicitly canceled.
         m.arg1 = params.getStopReason() != JobParameters.REASON_CANCELED ? 1 : 0;
         // Apply backoff only if stop is called due to timeout.
         m.arg2 = params.getStopReason() == JobParameters.REASON_TIMEOUT ? 1 : 0;
 
-        sendMessage(m);
+        SyncManager.sendMessage(m);
         return false;
     }
 
-    public void callJobFinished(int jobId, boolean needsReschedule, String why) {
-        synchronized (mLock) {
-            JobParameters params = mJobParamsMap.get(jobId);
-            mLogger.log("callJobFinished()",
+    public static void callJobFinished(int jobId, boolean needsReschedule, String why) {
+        final SyncJobService instance = getInstance();
+        if (instance != null) {
+            instance.callJobFinishedInner(jobId, needsReschedule, why);
+        }
+    }
+
+    public void callJobFinishedInner(int jobId, boolean needsReschedule, String why) {
+        synchronized (sLock) {
+            JobParameters params = sJobParamsMap.get(jobId);
+            sLogger.log("callJobFinished()",
                     " jobid=", jobId,
                     " needsReschedule=", needsReschedule,
-                    " ", mLogger.jobParametersToString(params),
+                    " ", sLogger.jobParametersToString(params),
                     " why=", why);
             if (params != null) {
                 jobFinished(params, needsReschedule);
-                mJobParamsMap.remove(jobId);
+                sJobParamsMap.remove(jobId);
             } else {
                 Slog.e(TAG, "Job params not found for " + String.valueOf(jobId));
             }
         }
     }
 
-    public void markSyncStarted(int jobId) {
-        synchronized (mLock) {
-            mStartedSyncs.put(jobId, true);
+    public static void markSyncStarted(int jobId) {
+        synchronized (sLock) {
+            sStartedSyncs.put(jobId, true);
         }
     }
 
@@ -203,8 +212,8 @@
         }
     }
 
-    private void wtf(String message) {
-        mLogger.log(message);
+    private static void wtf(String message) {
+        sLogger.log(message);
         Slog.wtf(TAG, message);
     }
 }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 0a640b8..d06e785 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -21,6 +21,7 @@
 import android.accounts.AccountManager;
 import android.accounts.AccountManagerInternal;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -72,7 +73,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Messenger;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteCallback;
@@ -89,6 +89,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseBooleanArray;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -227,7 +228,6 @@
     // TODO: add better locking around mRunningAccounts
     private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY;
 
-    volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
     volatile private PowerManager.WakeLock mSyncManagerWakeLock;
     volatile private boolean mDataConnectionIsConnected = false;
     volatile private boolean mStorageIsLow = false;
@@ -238,7 +238,6 @@
     private final IBatteryStats mBatteryStats;
     private JobScheduler mJobScheduler;
     private JobSchedulerInternal mJobSchedulerInternal;
-    private SyncJobService mSyncJobService;
 
     private SyncStorageEngine mSyncStorageEngine;
 
@@ -318,16 +317,6 @@
                 }
             };
 
-    private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            mBootCompleted = true;
-            // Called because it gets all pending jobs and stores them in mScheduledSyncs cache.
-            verifyJobScheduler();
-            mSyncHandler.onBootCompleted();
-        }
-    };
-
     private final BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -371,14 +360,14 @@
         m.sendToTarget();
     }
 
-    private void doDatabaseCleanup() {
+    private void removeStaleAccounts() {
         for (UserInfo user : mUserManager.getUsers(true)) {
             // Skip any partially created/removed users
             if (user.partial) continue;
             Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(
                     user.id, mContext.getOpPackageName());
 
-            mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
+            mSyncStorageEngine.removeStaleAccounts(accountsForUser, user.id);
         }
     }
 
@@ -464,8 +453,8 @@
     private final SyncHandler mSyncHandler;
     private final SyncManagerConstants mConstants;
 
-    private volatile boolean mBootCompleted = false;
-    private volatile boolean mJobServiceReady = false;
+    @GuardedBy("mUnlockedUsers")
+    private final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
 
     private ConnectivityManager getConnectivityManager() {
         synchronized (this) {
@@ -641,12 +630,6 @@
         IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
         context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
 
-        if (!factoryTest) {
-            intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
-            intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-            context.registerReceiver(mBootCompletedReceiver, intentFilter);
-        }
-
         intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
         intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
         context.registerReceiver(mStorageIntentReceiver, intentFilter);
@@ -690,14 +673,6 @@
         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
                 BatteryStats.SERVICE_NAME));
 
-        // This WakeLock is used to ensure that we stay awake between the time that we receive
-        // a sync alarm notification and when we finish processing it. We need to do this
-        // because we don't do the work in the alarm handler, rather we do it in a message
-        // handler.
-        mHandleAlarmWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
-                HANDLE_SYNC_ALARM_WAKE_LOCK);
-        mHandleAlarmWakeLock.setReferenceCounted(false);
-
         // This WakeLock is used to ensure that we stay awake while running the sync loop
         // message handler. Normally we will hold a sync adapter wake lock while it is being
         // synced but during the execution of the sync loop it might finish a sync for
@@ -715,7 +690,6 @@
                         public void onChange(boolean selfChange) {
                             mProvisioned |= isDeviceProvisioned();
                             if (mProvisioned) {
-                                mSyncHandler.onDeviceProvisioned();
                                 resolver.unregisterContentObserver(this);
                             }
                         }
@@ -744,19 +718,6 @@
                     null, null);
         }
 
-        // Set up the communication channel between the scheduled job and the sync manager.
-        // This is posted to the *main* looper intentionally, to defer calling startService()
-        // until after the lengthy primary boot sequence completes on that thread, to avoid
-        // spurious ANR triggering.
-        final Intent startServiceIntent = new Intent(mContext, SyncJobService.class);
-        startServiceIntent.putExtra(SyncJobService.EXTRA_MESSENGER, new Messenger(mSyncHandler));
-        new Handler(mContext.getMainLooper()).post(new Runnable() {
-            @Override
-            public void run() {
-                mContext.startService(startServiceIntent);
-            }
-        });
-
         // Sync adapters were able to access the synced account without the accounts
         // permission which circumvents our permission model. Therefore, we require
         // sync adapters that don't have access to the account to get user consent.
@@ -768,16 +729,31 @@
         mLogger.log("Sync manager initialized: " + Build.FINGERPRINT);
     }
 
-    public void onStartUser(int userHandle) {
-        mSyncHandler.post(() -> mLogger.log("onStartUser: user=", userHandle));
+    public void onStartUser(int userId) {
+        // Log on the handler to avoid slowing down device boot.
+        mSyncHandler.post(() -> mLogger.log("onStartUser: user=", userId));
     }
 
-    public void onUnlockUser(int userHandle) {
-        mSyncHandler.post(() -> mLogger.log("onUnlockUser: user=", userHandle));
+    public void onUnlockUser(int userId) {
+        synchronized (mUnlockedUsers) {
+            mUnlockedUsers.put(userId, true);
+        }
+        // Log on the handler to avoid slowing down device boot.
+        mSyncHandler.post(() -> mLogger.log("onUnlockUser: user=", userId));
     }
 
-    public void onStopUser(int userHandle) {
-        mSyncHandler.post(() -> mLogger.log("onStopUser: user=", userHandle));
+    public void onStopUser(int userId) {
+        synchronized (mUnlockedUsers) {
+            mUnlockedUsers.put(userId, false);
+        }
+        // Log on the handler to avoid slowing down user switch.
+        mSyncHandler.post(() -> mLogger.log("onStopUser: user=", userId));
+    }
+
+    private boolean isUserUnlocked(int userId) {
+        synchronized (mUnlockedUsers) {
+            return mUnlockedUsers.get(userId);
+        }
     }
 
     public void onBootPhase(int phase) {
@@ -1820,7 +1796,7 @@
         updateRunningAccounts(null /* Don't sync any target */);
 
         // Clean up the storage engine database
-        mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
+        mSyncStorageEngine.removeStaleAccounts(null, userId);
         List<SyncOperation> ops = getAllPendingSyncs();
         for (SyncOperation op: ops) {
             if (op.target.userId == userId) {
@@ -2235,8 +2211,13 @@
         mSyncStorageEngine.resetTodayStats(/* force=*/ false);
 
         for (AccountAndUser account : accounts) {
-            pw.printf("Account %s u%d %s\n",
-                    account.account.name, account.userId, account.account.type);
+            final boolean unlocked;
+            synchronized (mUnlockedUsers) {
+                unlocked = mUnlockedUsers.get(account.userId);
+            }
+            pw.printf("Account %s u%d %s%s\n",
+                    account.account.name, account.userId, account.account.type,
+                    (unlocked ? "" : " (locked)"));
 
             pw.println("=======================================================================");
             final PrintTable table = new PrintTable(16);
@@ -2872,13 +2853,29 @@
         }
     }
 
-    /**
-     * @return whether the device is ready to run sync jobs.
-     */
-    public static boolean readyToSync() {
+    @Nullable
+    private static SyncManager getInstance() {
         synchronized (SyncManager.class) {
-            return sInstance != null && sInstance.mProvisioned && sInstance.mBootCompleted
-                    && sInstance.mJobServiceReady;
+            if (sInstance == null) {
+                Slog.wtf(TAG, "sInstance == null"); // Maybe called too early?
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * @return whether the device is ready to run sync jobs for a given user.
+     */
+    public static boolean readyToSync(int userId) {
+        final SyncManager instance = getInstance();
+        return (instance != null) && SyncJobService.isReady()
+                && instance.mProvisioned && instance.isUserUnlocked(userId);
+    }
+
+    public static void sendMessage(Message message) {
+        final SyncManager instance = getInstance();
+        if (instance != null) {
+            instance.mSyncHandler.sendMessage(message);
         }
     }
 
@@ -2889,11 +2886,9 @@
     class SyncHandler extends Handler {
         // Messages that can be sent on mHandler.
         private static final int MESSAGE_SYNC_FINISHED = 1;
-        private static final int MESSAGE_RELEASE_MESSAGES_FROM_QUEUE = 2;
         private static final int MESSAGE_SERVICE_CONNECTED = 4;
         private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
         private static final int MESSAGE_CANCEL = 6;
-        static final int MESSAGE_JOBSERVICE_OBJECT = 7;
         static final int MESSAGE_START_SYNC = 10;
         static final int MESSAGE_STOP_SYNC = 11;
         static final int MESSAGE_SCHEDULE_SYNC = 12;
@@ -2910,86 +2905,17 @@
         public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
         private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap();
 
-        private List<Message> mUnreadyQueue = new ArrayList<Message>();
-
-        void onBootCompleted() {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Slog.v(TAG, "Boot completed.");
-            }
-            checkIfDeviceReady();
-        }
-
-        void onDeviceProvisioned() {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "mProvisioned=" + mProvisioned);
-            }
-            checkIfDeviceReady();
-        }
-
-        void checkIfDeviceReady() {
-            if (mProvisioned && mBootCompleted && mJobServiceReady) {
-                synchronized(this) {
-                    mSyncStorageEngine.restoreAllPeriodicSyncs();
-                    // Dispatch any stashed messages.
-                    obtainMessage(MESSAGE_RELEASE_MESSAGES_FROM_QUEUE).sendToTarget();
-                }
-            }
-        }
-
-        /**
-         * Stash any messages that come to the handler before boot is complete or before the device
-         * is properly provisioned (i.e. out of set-up wizard).
-         * {@link #onBootCompleted()} and {@link SyncHandler#onDeviceProvisioned} both
-         * need to come in before we start syncing.
-         * @param msg Message to dispatch at a later point.
-         * @return true if a message was enqueued, false otherwise. This is to avoid losing the
-         * message if we manage to acquire the lock but by the time we do boot has completed.
-         */
-        private boolean tryEnqueueMessageUntilReadyToRun(Message msg) {
-            synchronized (this) {
-                if (!mBootCompleted || !mProvisioned || !mJobServiceReady) {
-                    // Need to copy the message bc looper will recycle it.
-                    Message m = Message.obtain(msg);
-                    mUnreadyQueue.add(m);
-                    return true;
-                } else {
-                    return false;
-                }
-            }
-        }
-
         public SyncHandler(Looper looper) {
             super(looper);
         }
 
         public void handleMessage(Message msg) {
+            // TODO Do we really need this wake lock?? If we actually needed it, this is probably
+            // not the best place to acquire the lock -- it's probably too late, because the device
+            // could have gone to sleep before we reach here.
+            mSyncManagerWakeLock.acquire();
             try {
-                mSyncManagerWakeLock.acquire();
-                // We only want to enqueue sync related messages until device is ready.
-                // Other messages are handled without enqueuing.
-                if (msg.what == MESSAGE_JOBSERVICE_OBJECT) {
-                    Slog.i(TAG, "Got SyncJobService instance.");
-                    mSyncJobService = (SyncJobService) msg.obj;
-                    mJobServiceReady = true;
-                    checkIfDeviceReady();
-                } else if (msg.what == SyncHandler.MESSAGE_ACCOUNTS_UPDATED) {
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
-                    }
-                    EndPoint targets = (EndPoint) msg.obj;
-                    updateRunningAccountsH(targets);
-                } else if (msg.what == MESSAGE_RELEASE_MESSAGES_FROM_QUEUE) {
-                    if (mUnreadyQueue != null) {
-                        for (Message m : mUnreadyQueue) {
-                            handleSyncMessage(m);
-                        }
-                        mUnreadyQueue = null;
-                    }
-                } else if (tryEnqueueMessageUntilReadyToRun(msg)) {
-                    // No work to be done.
-                } else {
-                    handleSyncMessage(msg);
-                }
+                handleSyncMessage(msg);
             } finally {
                 mSyncManagerWakeLock.release();
             }
@@ -3001,6 +2927,13 @@
             try {
                 mDataConnectionIsConnected = readDataConnectionState();
                 switch (msg.what) {
+                    case MESSAGE_ACCOUNTS_UPDATED:
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
+                        }
+                        EndPoint targets = (EndPoint) msg.obj;
+                        updateRunningAccountsH(targets);
+                        break;
                     case MESSAGE_SCHEDULE_SYNC:
                         ScheduleSyncMessagePayload syncPayload =
                                 (ScheduleSyncMessagePayload) msg.obj;
@@ -3069,7 +3002,7 @@
                         if (isLoggable) {
                             Slog.v(TAG, "syncFinished" + payload.activeSyncContext.mSyncOperation);
                         }
-                        mSyncJobService.callJobFinished(
+                        SyncJobService.callJobFinished(
                                 payload.activeSyncContext.mSyncOperation.jobId, false,
                                 "sync finished");
                         runSyncFinishedOrCanceledH(payload.syncResult,
@@ -3119,7 +3052,7 @@
                             // which is a soft error.
                             SyncResult syncResult = new SyncResult();
                             syncResult.stats.numIoExceptions++;
-                            mSyncJobService.callJobFinished(
+                            SyncJobService.callJobFinished(
                                     currentSyncContext.mSyncOperation.jobId, false,
                                     "service disconnected");
                             runSyncFinishedOrCanceledH(syncResult, currentSyncContext);
@@ -3138,7 +3071,7 @@
                             Log.w(TAG, String.format(
                                     "Detected sync making no progress for %s. cancelling.",
                                     monitoredSyncContext));
-                            mSyncJobService.callJobFinished(
+                            SyncJobService.callJobFinished(
                                     monitoredSyncContext.mSyncOperation.jobId, false,
                                     "no network activity");
                             runSyncFinishedOrCanceledH(
@@ -3175,7 +3108,7 @@
         private void deferSyncH(SyncOperation op, long delay, String why) {
             mLogger.log("deferSyncH() ", (op.isPeriodic ? "periodic " : ""),
                     "sync.  op=", op, " delay=", delay, " why=", why);
-            mSyncJobService.callJobFinished(op.jobId, false, why);
+            SyncJobService.callJobFinished(op.jobId, false, why);
             if (op.isPeriodic) {
                 scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
             } else {
@@ -3213,7 +3146,7 @@
             // assume the clock is correct.
             mSyncStorageEngine.setClockValid();
 
-            mSyncJobService.markSyncStarted(op.jobId);
+            SyncJobService.markSyncStarted(op.jobId);
 
             if (mStorageIsLow) {
                 deferSyncH(op, SYNC_DELAY_ON_LOW_STORAGE, "storage low");
@@ -3226,7 +3159,7 @@
                 List<SyncOperation> ops = getAllPendingSyncs();
                 for (SyncOperation syncOperation: ops) {
                     if (syncOperation.sourcePeriodicId == op.jobId) {
-                        mSyncJobService.callJobFinished(op.jobId, false,
+                        SyncJobService.callJobFinished(op.jobId, false,
                                 "periodic sync, pending");
                         return;
                     }
@@ -3235,7 +3168,7 @@
                 // executing according to some backoff criteria.
                 for (ActiveSyncContext asc: mActiveSyncContexts) {
                     if (asc.mSyncOperation.sourcePeriodicId == op.jobId) {
-                        mSyncJobService.callJobFinished(op.jobId, false,
+                        SyncJobService.callJobFinished(op.jobId, false,
                                 "periodic sync, already running");
                         return;
                     }
@@ -3272,13 +3205,13 @@
             switch (syncOpState) {
                 case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS:
                 case SYNC_OP_STATE_INVALID: {
-                    mSyncJobService.callJobFinished(op.jobId, false,
+                    SyncJobService.callJobFinished(op.jobId, false,
                             "invalid op state: " + syncOpState);
                 } return;
             }
 
             if (!dispatchSyncOperation(op)) {
-                mSyncJobService.callJobFinished(op.jobId, false, "dispatchSyncOperation() failed");
+                SyncJobService.callJobFinished(op.jobId, false, "dispatchSyncOperation() failed");
             }
 
             setAuthorityPendingState(op.target);
@@ -3306,9 +3239,7 @@
             if (mLogger.enabled()) {
                 mLogger.log("updateRunningAccountsH: ", Arrays.toString(mRunningAccounts));
             }
-            if (mBootCompleted) {
-                doDatabaseCleanup();
-            }
+            removeStaleAccounts();
 
             AccountAndUser[] accounts = mRunningAccounts;
             for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
@@ -3453,7 +3384,7 @@
                 if (op.sourcePeriodicId == syncOperation.jobId || op.jobId == syncOperation.jobId) {
                     ActiveSyncContext asc = findActiveSyncContextH(syncOperation.jobId);
                     if (asc != null) {
-                        mSyncJobService.callJobFinished(syncOperation.jobId, false,
+                        SyncJobService.callJobFinished(syncOperation.jobId, false,
                                 "removePeriodicSyncInternalH");
                         runSyncFinishedOrCanceledH(null, asc);
                     }
@@ -3662,7 +3593,7 @@
                                     false /* no config settings */)) {
                         continue;
                     }
-                    mSyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false,
+                    SyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false,
                             why);
                     runSyncFinishedOrCanceledH(null /* cancel => no result */, activeSyncContext);
                 }
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 11f0701..391e3b0 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -19,6 +19,7 @@
 import android.accounts.Account;
 import android.accounts.AccountAndUser;
 import android.accounts.AccountManager;
+import android.annotation.Nullable;
 import android.app.backup.BackupManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -30,10 +31,6 @@
 import android.content.SyncRequest;
 import android.content.SyncStatusInfo;
 import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteQueryBuilder;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
@@ -510,10 +507,6 @@
         readAccountInfoLocked();
         readStatusLocked();
         readStatisticsLocked();
-        readAndDeleteLegacyAccountInfoLocked();
-        writeAccountInfoLocked();
-        writeStatusLocked();
-        writeStatisticsLocked();
 
         if (mLogger.enabled()) {
             final int size = mAuthorities.size();
@@ -1013,7 +1006,7 @@
      * Called when the set of account has changed, given the new array of
      * active accounts.
      */
-    public void doDatabaseCleanup(Account[] accounts, int userId) {
+    public void removeStaleAccounts(@Nullable Account[] accounts, int userId) {
         synchronized (mAuthorities) {
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Slog.v(TAG, "Updating for new accounts...");
@@ -1022,8 +1015,9 @@
             Iterator<AccountInfo> accIt = mAccounts.values().iterator();
             while (accIt.hasNext()) {
                 AccountInfo acc = accIt.next();
-                if (!ArrayUtils.contains(accounts, acc.accountAndUser.account)
-                        && acc.accountAndUser.userId == userId) {
+                if ((accounts == null) || (
+                        (acc.accountAndUser.userId == userId)
+                        && !ArrayUtils.contains(accounts, acc.accountAndUser.account))) {
                     // This account no longer exists...
                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
                         Slog.v(TAG, "Account removed: " + acc.accountAndUser);
@@ -1581,7 +1575,6 @@
             readAccountInfoLocked();
             readStatusLocked();
             readStatisticsLocked();
-            readAndDeleteLegacyAccountInfoLocked();
             writeAccountInfoLocked();
             writeStatusLocked();
             writeStatisticsLocked();
@@ -1998,146 +1991,6 @@
         }
     }
 
-    static int getIntColumn(Cursor c, String name) {
-        return c.getInt(c.getColumnIndex(name));
-    }
-
-    static long getLongColumn(Cursor c, String name) {
-        return c.getLong(c.getColumnIndex(name));
-    }
-
-    /**
-     * TODO Remove it. It's super old code that was used to migrate the information from a sqlite
-     * database that we used a long time ago, and is no longer relevant.
-     */
-    private void readAndDeleteLegacyAccountInfoLocked() {
-        // Look for old database to initialize from.
-        File file = mContext.getDatabasePath("syncmanager.db");
-        if (!file.exists()) {
-            return;
-        }
-        String path = file.getPath();
-        SQLiteDatabase db = null;
-        try {
-            db = SQLiteDatabase.openDatabase(path, null,
-                    SQLiteDatabase.OPEN_READONLY);
-        } catch (SQLiteException e) {
-        }
-
-        if (db != null) {
-            final boolean hasType = db.getVersion() >= 11;
-
-            // Copy in all of the status information, as well as accounts.
-            if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
-                Slog.v(TAG_FILE, "Reading legacy sync accounts db");
-            }
-            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
-            qb.setTables("stats, status");
-            HashMap<String,String> map = new HashMap<String,String>();
-            map.put("_id", "status._id as _id");
-            map.put("account", "stats.account as account");
-            if (hasType) {
-                map.put("account_type", "stats.account_type as account_type");
-            }
-            map.put("authority", "stats.authority as authority");
-            map.put("totalElapsedTime", "totalElapsedTime");
-            map.put("numSyncs", "numSyncs");
-            map.put("numSourceLocal", "numSourceLocal");
-            map.put("numSourcePoll", "numSourcePoll");
-            map.put("numSourceServer", "numSourceServer");
-            map.put("numSourceUser", "numSourceUser");
-            map.put("lastSuccessSource", "lastSuccessSource");
-            map.put("lastSuccessTime", "lastSuccessTime");
-            map.put("lastFailureSource", "lastFailureSource");
-            map.put("lastFailureTime", "lastFailureTime");
-            map.put("lastFailureMesg", "lastFailureMesg");
-            map.put("pending", "pending");
-            qb.setProjectionMap(map);
-            qb.appendWhere("stats._id = status.stats_id");
-            Cursor c = qb.query(db, null, null, null, null, null, null);
-            while (c.moveToNext()) {
-                String accountName = c.getString(c.getColumnIndex("account"));
-                String accountType = hasType
-                        ? c.getString(c.getColumnIndex("account_type")) : null;
-                if (accountType == null) {
-                    accountType = "com.google";
-                }
-                String authorityName = c.getString(c.getColumnIndex("authority"));
-                AuthorityInfo authority =
-                        this.getOrCreateAuthorityLocked(
-                                new EndPoint(new Account(accountName, accountType),
-                                        authorityName,
-                                        0 /* legacy is single-user */)
-                                , -1,
-                                false);
-                if (authority != null) {
-                    int i = mSyncStatus.size();
-                    boolean found = false;
-                    SyncStatusInfo st = null;
-                    while (i > 0) {
-                        i--;
-                        st = mSyncStatus.valueAt(i);
-                        if (st.authorityId == authority.ident) {
-                            found = true;
-                            break;
-                        }
-                    }
-                    if (!found) {
-                        st = new SyncStatusInfo(authority.ident);
-                        mSyncStatus.put(authority.ident, st);
-                    }
-                    st.totalStats.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
-                    st.totalStats.numSyncs = getIntColumn(c, "numSyncs");
-                    st.totalStats.numSourceLocal = getIntColumn(c, "numSourceLocal");
-                    st.totalStats.numSourcePoll = getIntColumn(c, "numSourcePoll");
-                    st.totalStats.numSourceOther = getIntColumn(c, "numSourceServer");
-                    st.totalStats.numSourceUser = getIntColumn(c, "numSourceUser");
-                    st.totalStats.numSourcePeriodic = 0;
-                    st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
-                    st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
-                    st.lastFailureSource = getIntColumn(c, "lastFailureSource");
-                    st.lastFailureTime = getLongColumn(c, "lastFailureTime");
-                    st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg"));
-                    st.pending = getIntColumn(c, "pending") != 0;
-                }
-            }
-
-            c.close();
-
-            // Retrieve the settings.
-            qb = new SQLiteQueryBuilder();
-            qb.setTables("settings");
-            c = qb.query(db, null, null, null, null, null, null);
-            while (c.moveToNext()) {
-                String name = c.getString(c.getColumnIndex("name"));
-                String value = c.getString(c.getColumnIndex("value"));
-                if (name == null) continue;
-                if (name.equals("listen_for_tickles")) {
-                    setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0,
-                            ContentResolver.SYNC_EXEMPTION_NONE, SyncLogger.CALLING_UID_SELF);
-                } else if (name.startsWith("sync_provider_")) {
-                    String provider = name.substring("sync_provider_".length(),
-                            name.length());
-                    int i = mAuthorities.size();
-                    while (i > 0) {
-                        i--;
-                        AuthorityInfo authority = mAuthorities.valueAt(i);
-                        if (authority.target.provider.equals(provider)) {
-                            authority.enabled = value == null || Boolean.parseBoolean(value);
-                            authority.syncable = 1;
-                        }
-                    }
-                }
-            }
-
-            c.close();
-
-            db.close();
-
-            (new File(path)).delete();
-        }
-    }
-
     public static final int STATUS_FILE_END = 0;
     public static final int STATUS_FILE_ITEM = 100;
 
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 1c3342a..b97e904 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -400,6 +400,7 @@
         } else if (mLightSensorEnabled) {
             mLightSensorEnabled = false;
             mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
+            mScreenAutoBrightness = -1;
             mRecentLightSamples = 0;
             mAmbientLightRingBuffer.clear();
             mCurrentLightSensorRate = -1;
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index a568fe3..c473ef2 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -937,7 +938,7 @@
         }
 
         public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
-            return ActivityManager.getService().getFocusedStackInfo();
+            return ActivityTaskManager.getService().getFocusedStackInfo();
         }
 
         public void scheduleIdleJob(Context context) {
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 4f53ed4..33525fd 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -46,6 +46,7 @@
 import libcore.io.Streams;
 
 import com.android.server.LocalServices;
+import com.android.server.policy.WindowManagerPolicy;
 
 /**
  * <p>
@@ -63,7 +64,7 @@
 
     // The layer for the electron beam surface.
     // This is currently hardcoded to be one layer above the boot animation.
-    private static final int COLOR_FADE_LAYER = 0x40000001;
+    private static final int COLOR_FADE_LAYER = WindowManagerPolicy.COLOR_FADE_LAYER;
 
     // The number of frames to draw when preparing the animation so that it will
     // be ready to run smoothly.  We use 3 frames because we are triple-buffered.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0a77269..75b3556 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2165,6 +2165,24 @@
         }
 
         @Override
+        public boolean screenshot(int displayId, Surface outSurface) {
+            synchronized (mSyncRoot) {
+                final LogicalDisplay display = mLogicalDisplays.get(displayId);
+                if (display != null) {
+                    final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+                    if (device != null) {
+                        final IBinder token = device.getDisplayTokenLocked();
+                        if (token != null) {
+                            SurfaceControl.screenshot(token, outSurface);
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+
+        @Override
         public DisplayInfo getDisplayInfo(int displayId) {
             return getDisplayInfoInternal(displayId, Process.myUid());
         }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 99412c5..e2c8ef9 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -791,9 +791,6 @@
                     && mAutomaticBrightnessController != null;
 
         final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
-        if (userSetBrightnessChanged) {
-            mTemporaryScreenBrightness = -1;
-        }
 
         // Use the temporary screen brightness if there isn't an override, either from
         // WindowManager or based on the display state.
@@ -1514,11 +1511,13 @@
         }
         if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
             mPendingScreenBrightnessSetting = -1;
+            mTemporaryScreenBrightness = -1;
             return false;
         }
         mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
         mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
         mPendingScreenBrightnessSetting = -1;
+        mTemporaryScreenBrightness = -1;
         return true;
     }
 
@@ -1660,8 +1659,10 @@
         pw.println("  mReportedToPolicy=" +
                 reportedToPolicyToString(mReportedScreenStateToPolicy));
 
-        pw.println("  mScreenBrightnessRampAnimator.isAnimating()=" +
-                mScreenBrightnessRampAnimator.isAnimating());
+        if (mScreenBrightnessRampAnimator != null) {
+            pw.println("  mScreenBrightnessRampAnimator.isAnimating()=" +
+                    mScreenBrightnessRampAnimator.isAnimating());
+        }
 
         if (mColorFadeOnAnimator != null) {
             pw.println("  mColorFadeOnAnimator.isStarted()=" +
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index 57a4f0d..d6931e0 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -17,6 +17,7 @@
 package com.android.server.display;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.opengl.Matrix;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -309,7 +310,7 @@
 
     private void updateConfiguration() {
         try {
-            ActivityManager.getService().updateConfiguration(null);
+            ActivityTaskManager.getService().updateConfiguration(null);
         } catch (RemoteException e) {
             Log.e(TAG, "Could not update configuration", e);
         }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index b9a279a..21ae048 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -31,8 +31,6 @@
 import android.os.PowerManager;
 import android.os.SystemProperties;
 import android.os.Trace;
-import android.text.TextUtils;
-import android.util.PathParser;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
@@ -404,8 +402,8 @@
                             && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
                         mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
                     }
-                    mInfo.displayCutout = DisplayCutout.fromResources(res, mInfo.width,
-                            mInfo.height);
+                    mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
+                            mInfo.width, mInfo.height);
                     mInfo.type = Display.TYPE_BUILT_IN;
                     mInfo.densityDpi = (int)(phys.density * 160 + 0.5f);
                     mInfo.xDpi = phys.xDpi;
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
deleted file mode 100644
index afd1a94..0000000
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.statusbar.IStatusBarService;
-
-/**
- * A class to keep track of the authentication state for a given client.
- */
-public abstract class AuthenticationClient extends ClientMonitor {
-    private long mOpId;
-
-    public abstract int handleFailedAttempt();
-    public abstract void resetFailedAttempts();
-
-    public static final int LOCKOUT_NONE = 0;
-    public static final int LOCKOUT_TIMED = 1;
-    public static final int LOCKOUT_PERMANENT = 2;
-
-    // Callback mechanism received from the client
-    // (BiometricPrompt -> FingerprintManager -> FingerprintService -> AuthenticationClient)
-    private IBiometricPromptReceiver mDialogReceiverFromClient;
-    private Bundle mBundle;
-    private IStatusBarService mStatusBarService;
-    private boolean mInLockout;
-    private final FingerprintManager mFingerprintManager;
-    protected boolean mDialogDismissed;
-
-    // Receives events from SystemUI and handles them before forwarding them to FingerprintDialog
-    protected IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
-        @Override // binder call
-        public void onDialogDismissed(int reason) {
-            if (mBundle != null && mDialogReceiverFromClient != null) {
-                try {
-                    mDialogReceiverFromClient.onDialogDismissed(reason);
-                    if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
-                        onError(FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED,
-                                0 /* vendorCode */);
-                    }
-                    mDialogDismissed = true;
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to notify dialog dismissed", e);
-                }
-                stop(true /* initiatedByClient */);
-            }
-        }
-    };
-
-    /**
-     * This method is called when authentication starts.
-     */
-    public abstract void onStart();
-
-    /**
-     * This method is called when a fingerprint is authenticated or authentication is stopped
-     * (cancelled by the user, or an error such as lockout has occurred).
-     */
-    public abstract void onStop();
-
-    public AuthenticationClient(Context context, long halDeviceId, IBinder token,
-            IFingerprintServiceReceiver receiver, int targetUserId, int groupId, long opId,
-            boolean restricted, String owner, Bundle bundle,
-            IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
-        super(context, halDeviceId, token, receiver, targetUserId, groupId, restricted, owner);
-        mOpId = opId;
-        mBundle = bundle;
-        mDialogReceiverFromClient = dialogReceiver;
-        mStatusBarService = statusBarService;
-        mFingerprintManager = (FingerprintManager) getContext()
-                .getSystemService(Context.FINGERPRINT_SERVICE);
-    }
-
-    @Override
-    public void binderDied() {
-        super.binderDied();
-        // When the binder dies, we should stop the client. This probably belongs in
-        // ClientMonitor's binderDied(), but testing all the cases would be tricky.
-        // AuthenticationClient is the most user-visible case.
-        stop(false /* initiatedByClient */);
-    }
-
-    @Override
-    public boolean onAcquired(int acquiredInfo, int vendorCode) {
-        // If the dialog is showing, the client doesn't need to receive onAcquired messages.
-        if (mBundle != null) {
-            try {
-                if (acquiredInfo != FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
-                    mStatusBarService.onFingerprintHelp(
-                            mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
-                }
-                return false; // acquisition continues
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote exception when sending acquired message", e);
-                return true; // client failed
-            } finally {
-                // Good scans will keep the device awake
-                if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
-                    notifyUserActivity();
-                }
-            }
-        } else {
-            return super.onAcquired(acquiredInfo, vendorCode);
-        }
-    }
-
-    @Override
-    public boolean onError(int error, int vendorCode) {
-        if (mDialogDismissed) {
-            // If user cancels authentication, the application has already received the
-            // FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED message from onDialogDismissed()
-            // and stopped the fingerprint hardware, so there is no need to send a
-            // FingerprintManager.FINGERPRINT_ERROR_CANCELED message.
-            return true;
-        }
-        if (mBundle != null) {
-            try {
-                mStatusBarService.onFingerprintError(
-                        mFingerprintManager.getErrorString(error, vendorCode));
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote exception when sending error", e);
-            }
-        }
-        return super.onError(error, vendorCode);
-    }
-
-    @Override
-    public boolean onAuthenticated(int fingerId, int groupId) {
-        boolean result = false;
-        boolean authenticated = fingerId != 0;
-
-        // If the fingerprint dialog is showing, notify authentication succeeded
-        if (mBundle != null) {
-            try {
-                if (authenticated) {
-                    mStatusBarService.onFingerprintAuthenticated();
-                } else {
-                    mStatusBarService.onFingerprintHelp(getContext().getResources().getString(
-                            com.android.internal.R.string.fingerprint_not_recognized));
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to notify Authenticated:", e);
-            }
-        }
-
-        IFingerprintServiceReceiver receiver = getReceiver();
-        if (receiver != null) {
-            try {
-                MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_AUTH,
-                        authenticated);
-                if (!authenticated) {
-                    receiver.onAuthenticationFailed(getHalDeviceId());
-                } else {
-                    if (DEBUG) {
-                        Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString()
-                                + ", id=" + fingerId + ", gp=" + groupId + ")");
-                    }
-                    Fingerprint fp = !getIsRestricted()
-                            ? new Fingerprint("" /* TODO */, groupId, fingerId, getHalDeviceId())
-                            : null;
-                    receiver.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
-                }
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed to notify Authenticated:", e);
-                result = true; // client failed
-            }
-        } else {
-            result = true; // client not listening
-        }
-        if (!authenticated) {
-            if (receiver != null) {
-                vibrateError();
-            }
-            // allow system-defined limit of number of attempts before giving up
-            int lockoutMode =  handleFailedAttempt();
-            if (lockoutMode != LOCKOUT_NONE) {
-                try {
-                    mInLockout = true;
-                    Slog.w(TAG, "Forcing lockout (fp driver code should do this!), mode(" +
-                            lockoutMode + ")");
-                    stop(false);
-                    int errorCode = lockoutMode == LOCKOUT_TIMED ?
-                            FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
-                            FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
-
-                    // TODO: if the dialog is showing, this error should be delayed. On a similar
-                    // note, AuthenticationClient should override onError and delay all other errors
-                    // as well, if the dialog is showing
-                    receiver.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
-
-                    // Send the lockout message to the system dialog
-                    if (mBundle != null) {
-                        mStatusBarService.onFingerprintError(
-                                mFingerprintManager.getErrorString(errorCode, 0 /* vendorCode */));
-                    }
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to notify lockout:", e);
-                }
-            }
-            result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
-        } else {
-            if (receiver != null) {
-                vibrateSuccess();
-            }
-            result |= true; // we have a valid fingerprint, done
-            resetFailedAttempts();
-            onStop();
-        }
-        return result;
-    }
-
-    /**
-     * Start authentication
-     */
-    @Override
-    public int start() {
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "start authentication: no fingerprint HAL!");
-            return ERROR_ESRCH;
-        }
-        onStart();
-        try {
-            final int result = daemon.authenticate(mOpId, getGroupId());
-            if (result != 0) {
-                Slog.w(TAG, "startAuthentication failed, result=" + result);
-                MetricsLogger.histogram(getContext(), "fingeprintd_auth_start_error", result);
-                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
-                return result;
-            }
-            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
-
-            // If authenticating with system dialog, show the dialog
-            if (mBundle != null) {
-                try {
-                    mStatusBarService.showFingerprintDialog(mBundle, mDialogReceiver);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to show fingerprint dialog", e);
-                }
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "startAuthentication failed", e);
-            return ERROR_ESRCH;
-        }
-        return 0; // success
-    }
-
-    @Override
-    public int stop(boolean initiatedByClient) {
-        if (mAlreadyCancelled) {
-            Slog.w(TAG, "stopAuthentication: already cancelled!");
-            return 0;
-        }
-
-        onStop();
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
-            return ERROR_ESRCH;
-        }
-        try {
-            final int result = daemon.cancel();
-            if (result != 0) {
-                Slog.w(TAG, "stopAuthentication failed, result=" + result);
-                return result;
-            }
-            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer authenticating");
-        } catch (RemoteException e) {
-            Slog.e(TAG, "stopAuthentication failed", e);
-            return ERROR_ESRCH;
-        } finally {
-            // If the user already cancelled authentication (via some interaction with the
-            // dialog, we do not need to hide it since it's already hidden.
-            // If the device is in lockout, don't hide the dialog - it will automatically hide
-            // after BiometricPrompt.HIDE_DIALOG_DELAY
-            if (mBundle != null && !mDialogDismissed && !mInLockout) {
-                try {
-                    mStatusBarService.hideFingerprintDialog();
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to hide fingerprint dialog", e);
-                }
-            }
-        }
-        mAlreadyCancelled = true;
-        return 0; // success
-    }
-
-    @Override
-    public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
-        if (DEBUG) Slog.w(TAG, "onEnrollResult() called for authenticate!");
-        return true; // Invalid for Authenticate
-    }
-
-    @Override
-    public boolean onRemoved(int fingerId, int groupId, int remaining) {
-        if (DEBUG) Slog.w(TAG, "onRemoved() called for authenticate!");
-        return true; // Invalid for Authenticate
-    }
-
-    @Override
-    public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
-        if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for authenticate!");
-        return true; // Invalid for Authenticate
-    }
-}
diff --git a/services/core/java/com/android/server/fingerprint/ClientMonitor.java b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
deleted file mode 100644
index b935ba2..0000000
--- a/services/core/java/com/android/server/fingerprint/ClientMonitor.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.fingerprint;
-
-import android.Manifest;
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.media.AudioAttributes;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
-import android.util.Slog;
-
-import java.util.NoSuchElementException;
-
-/**
- * Abstract base class for keeping track and dispatching events from fingerprint HAL to the
- * the current client.  Subclasses are responsible for coordinating the interaction with
- * fingerprint HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
- */
-public abstract class ClientMonitor implements IBinder.DeathRecipient {
-    protected static final String TAG = FingerprintService.TAG; // TODO: get specific name
-    protected static final int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. See errno.h.
-    protected static final boolean DEBUG = FingerprintService.DEBUG;
-    private static final long[] DEFAULT_SUCCESS_VIBRATION_PATTERN = new long[] {0, 30};
-    private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
-            new AudioAttributes.Builder()
-                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
-                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
-                    .build();
-    private final Context mContext;
-    private final long mHalDeviceId;
-    private final int mTargetUserId;
-    private final int mGroupId;
-    // True if client does not have MANAGE_FINGERPRINT permission
-    private final boolean mIsRestricted;
-    private final String mOwner;
-    private final VibrationEffect mSuccessVibrationEffect;
-    private final VibrationEffect mErrorVibrationEffect;
-    private IBinder mToken;
-    private IFingerprintServiceReceiver mReceiver;
-    protected boolean mAlreadyCancelled;
-
-    /**
-     * @param context context of FingerprintService
-     * @param halDeviceId the HAL device ID of the associated fingerprint hardware
-     * @param token a unique token for the client
-     * @param receiver recipient of related events (e.g. authentication)
-     * @param userId target user id for operation
-     * @param groupId groupId for the fingerprint set
-     * @param restricted whether or not client has the {@link Manifest#MANAGE_FINGERPRINT}
-     * permission
-     * @param owner name of the client that owns this
-     */
-    public ClientMonitor(Context context, long halDeviceId, IBinder token,
-            IFingerprintServiceReceiver receiver, int userId, int groupId,boolean restricted,
-            String owner) {
-        mContext = context;
-        mHalDeviceId = halDeviceId;
-        mToken = token;
-        mReceiver = receiver;
-        mTargetUserId = userId;
-        mGroupId = groupId;
-        mIsRestricted = restricted;
-        mOwner = owner;
-        mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
-        mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
-        try {
-            if (token != null) {
-                token.linkToDeath(this, 0);
-            }
-        } catch (RemoteException e) {
-            Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
-        }
-    }
-
-    /**
-     * Contacts fingerprint HAL to start the client.
-     * @return 0 on success, errno from driver on failure
-     */
-    public abstract int start();
-
-    /**
-     * Contacts fingerprint HAL to stop the client.
-     * @param initiatedByClient whether the operation is at the request of a client
-     */
-    public abstract int stop(boolean initiatedByClient);
-
-    /**
-     * Method to explicitly poke powermanager on events
-     */
-    public abstract void notifyUserActivity();
-
-    /**
-     * Gets the fingerprint daemon from the cached state in the container class.
-     */
-    public abstract IBiometricsFingerprint getFingerprintDaemon();
-
-    // Event callbacks from driver. Inappropriate calls is flagged/logged by the
-    // respective client (e.g. enrolling shouldn't get authenticate events).
-    // All of these return 'true' if the operation is completed and it's ok to move
-    // to the next client (e.g. authentication accepts or rejects a fingerprint).
-    public abstract boolean onEnrollResult(int fingerId, int groupId, int rem);
-    public abstract boolean onAuthenticated(int fingerId, int groupId);
-    public abstract boolean onRemoved(int fingerId, int groupId, int remaining);
-    public abstract boolean onEnumerationResult(int fingerId, int groupId, int remaining);
-
-    /**
-     * Called when we get notification from fingerprint HAL that an image has been acquired.
-     * Common to authenticate and enroll.
-     * @param acquiredInfo info about the current image acquisition
-     * @return true if client should be removed
-     */
-    public boolean onAcquired(int acquiredInfo, int vendorCode) {
-        if (mReceiver == null)
-            return true; // client not connected
-        try {
-            mReceiver.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
-            return false; // acquisition continues...
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Failed to invoke sendAcquired:", e);
-            return true; // client failed
-        } finally {
-            // Good scans will keep the device awake
-            if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
-                notifyUserActivity();
-            }
-        }
-    }
-
-    /**
-     * Called when we get notification from fingerprint HAL that an error has occurred with the
-     * current operation. Common to authenticate, enroll, enumerate and remove.
-     * @param error
-     * @return true if client should be removed
-     */
-    public boolean onError(int error, int vendorCode) {
-        if (mReceiver != null) {
-            try {
-                mReceiver.onError(getHalDeviceId(), error, vendorCode);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed to invoke sendError:", e);
-            }
-        }
-        return true; // errors always remove current client
-    }
-
-    public void destroy() {
-        if (mToken != null) {
-            try {
-                mToken.unlinkToDeath(this, 0);
-            } catch (NoSuchElementException e) {
-                // TODO: remove when duplicate call bug is found
-                Slog.e(TAG, "destroy(): " + this + ":", new Exception("here"));
-            }
-            mToken = null;
-        }
-        mReceiver = null;
-    }
-
-    @Override
-    public void binderDied() {
-        mToken = null;
-        mReceiver = null;
-        onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if (mToken != null) {
-                if (DEBUG) Slog.w(TAG, "removing leaked reference: " + mToken);
-                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
-    public final Context getContext() {
-        return mContext;
-    }
-
-    public final long getHalDeviceId() {
-        return mHalDeviceId;
-    }
-
-    public final String getOwnerString() {
-        return mOwner;
-    }
-
-    public final IFingerprintServiceReceiver getReceiver() {
-        return mReceiver;
-    }
-
-    public final boolean getIsRestricted() {
-        return mIsRestricted;
-    }
-
-    public final int getTargetUserId() {
-        return mTargetUserId;
-    }
-
-    public final int getGroupId() {
-        return mGroupId;
-    }
-
-    public final IBinder getToken() {
-        return mToken;
-    }
-
-    public final void vibrateSuccess() {
-        Vibrator vibrator = mContext.getSystemService(Vibrator.class);
-        if (vibrator != null) {
-            vibrator.vibrate(mSuccessVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
-        }
-    }
-
-    public final void vibrateError() {
-        Vibrator vibrator = mContext.getSystemService(Vibrator.class);
-        if (vibrator != null) {
-            vibrator.vibrate(mErrorVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/fingerprint/EnrollClient.java b/services/core/java/com/android/server/fingerprint/EnrollClient.java
deleted file mode 100644
index c9efcf2..0000000
--- a/services/core/java/com/android/server/fingerprint/EnrollClient.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.util.Arrays;
-
-/**
- * A class to keep track of the enrollment state for a given client.
- */
-public abstract class EnrollClient extends ClientMonitor {
-    private static final long MS_PER_SEC = 1000;
-    private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
-    private byte[] mCryptoToken;
-
-    public EnrollClient(Context context, long halDeviceId, IBinder token,
-            IFingerprintServiceReceiver receiver, int userId, int groupId, byte [] cryptoToken,
-            boolean restricted, String owner) {
-        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
-        mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
-    }
-
-    @Override
-    public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
-        if (groupId != getGroupId()) {
-            Slog.w(TAG, "groupId != getGroupId(), groupId: " + groupId +
-                    " getGroupId():" + getGroupId());
-        }
-        if (remaining == 0) {
-            FingerprintUtils.getInstance().addFingerprintForUser(getContext(), fingerId,
-                    getTargetUserId());
-        }
-        return sendEnrollResult(fingerId, groupId, remaining);
-    }
-
-    /*
-     * @return true if we're done.
-     */
-    private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
-        IFingerprintServiceReceiver receiver = getReceiver();
-        if (receiver == null)
-            return true; // client not listening
-
-        vibrateSuccess();
-        MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_ENROLL);
-        try {
-            receiver.onEnrollResult(getHalDeviceId(), fpId, groupId, remaining);
-            return remaining == 0;
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Failed to notify EnrollResult:", e);
-            return true;
-        }
-    }
-
-    @Override
-    public int start() {
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "enroll: no fingerprint HAL!");
-            return ERROR_ESRCH;
-        }
-        final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
-        try {
-            final int result = daemon.enroll(mCryptoToken, getGroupId(), timeout);
-            if (result != 0) {
-                Slog.w(TAG, "startEnroll failed, result=" + result);
-                MetricsLogger.histogram(getContext(), "fingerprintd_enroll_start_error", result);
-                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
-                return result;
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "startEnroll failed", e);
-        }
-        return 0; // success
-    }
-
-    @Override
-    public int stop(boolean initiatedByClient) {
-        if (mAlreadyCancelled) {
-            Slog.w(TAG, "stopEnroll: already cancelled!");
-            return 0;
-        }
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "stopEnrollment: no fingerprint HAL!");
-            return ERROR_ESRCH;
-        }
-        try {
-            final int result = daemon.cancel();
-            if (result != 0) {
-                Slog.w(TAG, "startEnrollCancel failed, result = " + result);
-                return result;
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "stopEnrollment failed", e);
-        }
-        if (initiatedByClient) {
-            onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, 0 /* vendorCode */);
-        }
-        mAlreadyCancelled = true;
-        return 0;
-    }
-
-    @Override
-    public boolean onRemoved(int fingerId, int groupId, int remaining) {
-        if (DEBUG) Slog.w(TAG, "onRemoved() called for enroll!");
-        return true; // Invalid for EnrollClient
-    }
-
-    @Override
-    public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
-        if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for enroll!");
-        return true; // Invalid for EnrollClient
-    }
-
-    @Override
-    public boolean onAuthenticated(int fingerId, int groupId) {
-        if (DEBUG) Slog.w(TAG, "onAuthenticated() called for enroll!");
-        return true; // Invalid for EnrollClient
-    }
-
-}
diff --git a/services/core/java/com/android/server/fingerprint/EnumerateClient.java b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
deleted file mode 100644
index b6bbd1b..0000000
--- a/services/core/java/com/android/server/fingerprint/EnumerateClient.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-import com.android.internal.logging.MetricsLogger;
-
-/**
- * A class to keep track of the enumeration state for a given client.
- */
-public abstract class EnumerateClient extends ClientMonitor {
-    public EnumerateClient(Context context, long halDeviceId, IBinder token,
-        IFingerprintServiceReceiver receiver, int groupId, int userId,
-        boolean restricted, String owner) {
-        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
-    }
-
-    @Override
-    public int start() {
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-        // The fingerprint template ids will be removed when we get confirmation from the HAL
-        try {
-            final int result = daemon.enumerate();
-            if (result != 0) {
-                Slog.w(TAG, "start enumerate for user " + getTargetUserId()
-                    + " failed, result=" + result);
-                MetricsLogger.histogram(getContext(), "fingerprintd_enum_start_error", result);
-                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
-                return result;
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "startEnumeration failed", e);
-        }
-        return 0;
-    }
-
-    @Override
-    public int stop(boolean initiatedByClient) {
-        if (mAlreadyCancelled) {
-            Slog.w(TAG, "stopEnumerate: already cancelled!");
-            return 0;
-        }
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "stopEnumeration: no fingerprint HAL!");
-            return ERROR_ESRCH;
-        }
-        try {
-            final int result = daemon.cancel();
-            if (result != 0) {
-                Slog.w(TAG, "stop enumeration failed, result=" + result);
-                return result;
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "stopEnumeration failed", e);
-            return ERROR_ESRCH;
-        }
-
-        // We don't actually stop enumerate, but inform the client that the cancel operation
-        // succeeded so we can start the next operation.
-        if (initiatedByClient) {
-            onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, 0 /* vendorCode */);
-        }
-        mAlreadyCancelled = true;
-        return 0; // success
-    }
-
-    @Override
-    public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
-        IFingerprintServiceReceiver receiver = getReceiver();
-        if (receiver == null)
-            return true; // client not listening
-        try {
-            receiver.onEnumerated(getHalDeviceId(), fingerId, groupId, remaining);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Failed to notify enumerated:", e);
-        }
-        return remaining == 0;
-    }
-
-    @Override
-    public boolean onAuthenticated(int fingerId, int groupId) {
-        if (DEBUG) Slog.w(TAG, "onAuthenticated() called for enumerate!");
-        return true; // Invalid for Enumerate.
-    }
-
-    @Override
-    public boolean onEnrollResult(int fingerId, int groupId, int rem) {
-        if (DEBUG) Slog.w(TAG, "onEnrollResult() called for enumerate!");
-        return true; // Invalid for Enumerate.
-    }
-
-    @Override
-    public boolean onRemoved(int fingerId, int groupId, int remaining) {
-        if (DEBUG) Slog.w(TAG, "onRemoved() called for enumerate!");
-        return true; // Invalid for Enumerate.
-    }
-}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
deleted file mode 100644
index 06329e57..0000000
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ /dev/null
@@ -1,1589 +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.
- */
-
-package com.android.server.fingerprint;
-
-import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.MANAGE_FINGERPRINT;
-import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
-import static android.Manifest.permission.USE_BIOMETRIC;
-import static android.Manifest.permission.USE_FINGERPRINT;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningAppProcessInfo;
-import android.app.AlarmManager;
-import android.app.AppOpsManager;
-import android.app.IActivityManager;
-import android.app.PendingIntent;
-import android.app.SynchronousUserSwitchObserver;
-import android.app.TaskStackListener;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
-import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintClientActiveCallback;
-import android.hardware.fingerprint.IFingerprintService;
-import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.DeadObjectException;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IHwBinder;
-import android.os.IRemoteCallback;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.RemoteException;
-import android.os.SELinux;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.security.KeyStore;
-import android.util.Slog;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.DumpUtils;
-import com.android.server.SystemServerInitThreadPool;
-import com.android.server.SystemService;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * A service to manage multiple clients that want to access the fingerprint HAL API.
- * The service is responsible for maintaining a list of clients and dispatching all
- * fingerprint-related events.
- *
- * @hide
- */
-public class FingerprintService extends SystemService implements IHwBinder.DeathRecipient {
-    static final String TAG = "FingerprintService";
-    static final boolean DEBUG = true;
-    private static final boolean CLEANUP_UNUSED_FP = true;
-    private static final String FP_DATA_DIR = "fpdata";
-    private static final int MSG_USER_SWITCHING = 10;
-    private static final String ACTION_LOCKOUT_RESET =
-            "com.android.server.fingerprint.ACTION_LOCKOUT_RESET";
-    private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
-
-    private class PerformanceStats {
-        int accept; // number of accepted fingerprints
-        int reject; // number of rejected fingerprints
-        int acquire; // total number of acquisitions. Should be >= accept+reject due to poor image
-                     // acquisition in some cases (too fast, too slow, dirty sensor, etc.)
-        int lockout; // total number of lockouts
-        int permanentLockout; // total number of permanent lockouts
-    }
-
-    private final ArrayList<FingerprintServiceLockoutResetMonitor> mLockoutMonitors =
-            new ArrayList<>();
-    private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
-            new CopyOnWriteArrayList<>();
-    private final Map<Integer, Long> mAuthenticatorIds =
-            Collections.synchronizedMap(new HashMap<>());
-    private final AppOpsManager mAppOps;
-    private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
-    private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
-    private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
-
-    private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
-    private final String mKeyguardPackage;
-    private int mCurrentUserId = UserHandle.USER_NULL;
-    private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
-    private Context mContext;
-    private long mHalDeviceId;
-    private SparseBooleanArray mTimedLockoutCleared;
-    private SparseIntArray mFailedAttempts;
-    @GuardedBy("this")
-    private IBiometricsFingerprint mDaemon;
-    private IStatusBarService mStatusBarService;
-    private final IActivityManager mActivityManager;
-    private final PowerManager mPowerManager;
-    private final AlarmManager mAlarmManager;
-    private final UserManager mUserManager;
-    private ClientMonitor mCurrentClient;
-    private ClientMonitor mPendingClient;
-    private PerformanceStats mPerformanceStats;
-
-    private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
-    private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
-
-    private class UserFingerprint {
-        Fingerprint f;
-        int userId;
-        public UserFingerprint(Fingerprint f, int userId) {
-            this.f = f;
-            this.userId = userId;
-        }
-    }
-
-    // Normal fingerprint authentications are tracked by mPerformanceMap.
-    private HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
-
-    // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
-    private HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
-
-    private Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(android.os.Message msg) {
-            switch (msg.what) {
-                case MSG_USER_SWITCHING:
-                    handleUserSwitching(msg.arg1);
-                    break;
-
-                default:
-                    Slog.w(TAG, "Unknown message:" + msg.what);
-            }
-        }
-    };
-
-    private final BroadcastReceiver mLockoutReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (ACTION_LOCKOUT_RESET.equals(intent.getAction())) {
-                final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
-                resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
-            }
-        }
-    };
-
-    private final Runnable mResetFailedAttemptsForCurrentUserRunnable = new Runnable() {
-        @Override
-        public void run() {
-            resetFailedAttemptsForUser(true /* clearAttemptCounter */,
-                    ActivityManager.getCurrentUser());
-        }
-    };
-
-    private final Runnable mResetClientState = new Runnable() {
-        @Override
-        public void run() {
-            // Warning: if we get here, the driver never confirmed our call to cancel the current
-            // operation (authenticate, enroll, remove, enumerate, etc), which is
-            // really bad.  The result will be a 3-second delay in starting each new client.
-            // If you see this on a device, make certain the driver notifies with
-            // {@link FingerprintManager#FINGERPRINT_ERROR_CANCEL} in response to cancel()
-            // once it has successfully switched to the IDLE state in the fingerprint HAL.
-            // Additionally,{@link FingerprintManager#FINGERPRINT_ERROR_CANCEL} should only be sent
-            // in response to an actual cancel() call.
-            Slog.w(TAG, "Client "
-                    + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
-                    + " failed to respond to cancel, starting client "
-                    + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
-
-            mCurrentClient = null;
-            startClient(mPendingClient, false);
-        }
-    };
-
-    private final TaskStackListener mTaskStackListener = new TaskStackListener() {
-        @Override
-        public void onTaskStackChanged() {
-            try {
-                if (!(mCurrentClient instanceof AuthenticationClient)) {
-                    return;
-                }
-                final String currentClient = mCurrentClient.getOwnerString();
-                if (isKeyguard(currentClient)) {
-                    return; // Keyguard is always allowed
-                }
-                List<ActivityManager.RunningTaskInfo> runningTasks = mActivityManager.getTasks(1);
-                if (!runningTasks.isEmpty()) {
-                    final String topPackage = runningTasks.get(0).topActivity.getPackageName();
-                    if (!topPackage.contentEquals(currentClient)) {
-                        Slog.e(TAG, "Stopping background authentication, top: " + topPackage
-                                + " currentClient: " + currentClient);
-                        mCurrentClient.stop(false /* initiatedByClient */);
-                    }
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to get running tasks", e);
-            }
-        }
-    };
-
-    public FingerprintService(Context context) {
-        super(context);
-        mContext = context;
-        mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
-                com.android.internal.R.string.config_keyguardComponent)).getPackageName();
-        mAppOps = context.getSystemService(AppOpsManager.class);
-        mPowerManager = mContext.getSystemService(PowerManager.class);
-        mAlarmManager = mContext.getSystemService(AlarmManager.class);
-        mContext.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
-                RESET_FINGERPRINT_LOCKOUT, null /* handler */);
-        mUserManager = UserManager.get(mContext);
-        mTimedLockoutCleared = new SparseBooleanArray();
-        mFailedAttempts = new SparseIntArray();
-        mStatusBarService = IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-        mActivityManager = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
-                .getService();
-    }
-
-    @Override
-    public void serviceDied(long cookie) {
-        Slog.v(TAG, "fingerprint HAL died");
-        MetricsLogger.count(mContext, "fingerprintd_died", 1);
-        handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
-                0 /*vendorCode */);
-    }
-
-    public synchronized IBiometricsFingerprint getFingerprintDaemon() {
-        if (mDaemon == null) {
-            Slog.v(TAG, "mDaemon was null, reconnect to fingerprint");
-            try {
-                mDaemon = IBiometricsFingerprint.getService();
-            } catch (java.util.NoSuchElementException e) {
-                // Service doesn't exist or cannot be opened. Logged below.
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to get biometric interface", e);
-            }
-            if (mDaemon == null) {
-                Slog.w(TAG, "fingerprint HIDL not available");
-                return null;
-            }
-
-            mDaemon.asBinder().linkToDeath(this, 0);
-
-            try {
-                mHalDeviceId = mDaemon.setNotify(mDaemonCallback);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to open fingerprint HAL", e);
-                mDaemon = null; // try again later!
-            }
-
-            if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
-            if (mHalDeviceId != 0) {
-                loadAuthenticatorIds();
-                updateActiveGroup(ActivityManager.getCurrentUser(), null);
-                doFingerprintCleanupForUser(ActivityManager.getCurrentUser());
-            } else {
-                Slog.w(TAG, "Failed to open Fingerprint HAL!");
-                MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1);
-                mDaemon = null;
-            }
-        }
-        return mDaemon;
-    }
-
-    /** Populates existing authenticator ids. To be used only during the start of the service. */
-    private void loadAuthenticatorIds() {
-        // This operation can be expensive, so keep track of the elapsed time. Might need to move to
-        // background if it takes too long.
-        long t = System.currentTimeMillis();
-        mAuthenticatorIds.clear();
-        for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
-            int userId = getUserOrWorkProfileId(null, user.id);
-            if (!mAuthenticatorIds.containsKey(userId)) {
-                updateActiveGroup(userId, null);
-            }
-        }
-
-        t = System.currentTimeMillis() - t;
-        if (t > 1000) {
-            Slog.w(TAG, "loadAuthenticatorIds() taking too long: " + t + "ms");
-        }
-    }
-
-    /**
-     * This method should be called upon connection to the daemon, and when user switches.
-     * @param userId
-     */
-    private void doFingerprintCleanupForUser(int userId) {
-        if (CLEANUP_UNUSED_FP) {
-            enumerateUser(userId);
-        }
-    }
-
-    private void clearEnumerateState() {
-        if (DEBUG) Slog.v(TAG, "clearEnumerateState()");
-        mUnknownFingerprints.clear();
-    }
-
-    private void enumerateUser(int userId) {
-        if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")");
-        boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
-        startEnumerate(mToken, userId, null, restricted, true /* internal */);
-    }
-
-    // Remove unknown fingerprints from hardware
-    private void cleanupUnknownFingerprints() {
-        if (!mUnknownFingerprints.isEmpty()) {
-            UserFingerprint uf = mUnknownFingerprints.get(0);
-            mUnknownFingerprints.remove(uf);
-            boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
-            startRemove(mToken, uf.f.getFingerId(), uf.f.getGroupId(), uf.userId, null,
-                    restricted, true /* internal */);
-        } else {
-            clearEnumerateState();
-        }
-    }
-
-    protected void handleEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
-        ClientMonitor client = mCurrentClient;
-
-        if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) {
-            return;
-        }
-        client.onEnumerationResult(fingerId, groupId, remaining);
-
-        // All fingerprints in hardware for this user were enumerated
-        if (remaining == 0) {
-            if (client instanceof InternalEnumerateClient) {
-                List<Fingerprint> unknownFingerprints =
-                        ((InternalEnumerateClient) client).getUnknownFingerprints();
-
-                if (!unknownFingerprints.isEmpty()) {
-                    Slog.w(TAG, "Adding " + unknownFingerprints.size() +
-                            " fingerprints for deletion");
-                }
-                for (Fingerprint f : unknownFingerprints) {
-                    mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
-                }
-                removeClient(client);
-                cleanupUnknownFingerprints();
-            } else {
-                removeClient(client);
-            }
-        }
-    }
-
-    protected void handleError(long deviceId, int error, int vendorCode) {
-        ClientMonitor client = mCurrentClient;
-        if (client instanceof InternalRemovalClient || client instanceof InternalEnumerateClient) {
-            clearEnumerateState();
-        }
-        if (client != null && client.onError(error, vendorCode)) {
-            removeClient(client);
-        }
-
-        if (DEBUG) Slog.v(TAG, "handleError(client="
-                + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
-        // This is the magic code that starts the next client when the old client finishes.
-        if (error == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
-            mHandler.removeCallbacks(mResetClientState);
-            if (mPendingClient != null) {
-                if (DEBUG) Slog.v(TAG, "start pending client " + mPendingClient.getOwnerString());
-                startClient(mPendingClient, false);
-                mPendingClient = null;
-            }
-        } else if (error == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
-            // If we get HW_UNAVAILABLE, try to connect again later...
-            Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
-            synchronized (this) {
-                mDaemon = null;
-                mHalDeviceId = 0;
-                mCurrentUserId = UserHandle.USER_NULL;
-            }
-        }
-    }
-
-    protected void handleRemoved(long deviceId, int fingerId, int groupId, int remaining) {
-        if (DEBUG) Slog.w(TAG, "Removed: fid=" + fingerId
-                + ", gid=" + groupId
-                + ", dev=" + deviceId
-                + ", rem=" + remaining);
-
-        ClientMonitor client = mCurrentClient;
-        if (client != null && client.onRemoved(fingerId, groupId, remaining)) {
-            removeClient(client);
-            // When the last fingerprint of a group is removed, update the authenticator id
-            if (!hasEnrolledFingerprints(groupId)) {
-                updateActiveGroup(groupId, null);
-            }
-        }
-        if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
-            cleanupUnknownFingerprints();
-        } else if (client instanceof InternalRemovalClient){
-            clearEnumerateState();
-        }
-    }
-
-    protected void handleAuthenticated(long deviceId, int fingerId, int groupId,
-            ArrayList<Byte> token) {
-        ClientMonitor client = mCurrentClient;
-        if (fingerId != 0) {
-            // Ugh...
-            final byte[] byteToken = new byte[token.size()];
-            for (int i = 0; i < token.size(); i++) {
-                byteToken[i] = token.get(i);
-            }
-            // Send to Keystore
-            KeyStore.getInstance().addAuthToken(byteToken);
-        }
-        if (client != null && client.onAuthenticated(fingerId, groupId)) {
-            removeClient(client);
-        }
-        if (fingerId != 0) {
-            mPerformanceStats.accept++;
-        } else {
-            mPerformanceStats.reject++;
-        }
-    }
-
-    protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
-        ClientMonitor client = mCurrentClient;
-        if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
-            removeClient(client);
-        }
-        if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
-                && client instanceof AuthenticationClient) {
-            // ignore enrollment acquisitions or acquisitions when we're locked out
-            mPerformanceStats.acquire++;
-        }
-    }
-
-    protected void handleEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
-        ClientMonitor client = mCurrentClient;
-        if (client != null && client.onEnrollResult(fingerId, groupId, remaining)) {
-            removeClient(client);
-            // When enrollment finishes, update this group's authenticator id, as the HAL has
-            // already generated a new authenticator id when the new fingerprint is enrolled.
-            updateActiveGroup(groupId, null);
-        }
-    }
-
-    private void userActivity() {
-        long now = SystemClock.uptimeMillis();
-        mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
-    }
-
-    void handleUserSwitching(int userId) {
-        if (mCurrentClient instanceof InternalRemovalClient
-                || mCurrentClient instanceof InternalEnumerateClient) {
-            Slog.w(TAG, "User switched while performing cleanup");
-            removeClient(mCurrentClient);
-            clearEnumerateState();
-        }
-        updateActiveGroup(userId, null);
-        doFingerprintCleanupForUser(userId);
-    }
-
-    private void removeClient(ClientMonitor client) {
-        if (client != null) {
-            client.destroy();
-            if (client != mCurrentClient && mCurrentClient != null) {
-                Slog.w(TAG, "Unexpected client: " + client.getOwnerString() + "expected: "
-                        + mCurrentClient != null ? mCurrentClient.getOwnerString() : "null");
-            }
-        }
-        if (mCurrentClient != null) {
-            if (DEBUG) Slog.v(TAG, "Done with client: " + client.getOwnerString());
-            mCurrentClient = null;
-        }
-        if (mPendingClient == null) {
-            notifyClientActiveCallbacks(false);
-        }
-    }
-
-    private int getLockoutMode() {
-        final int currentUser = ActivityManager.getCurrentUser();
-        final int failedAttempts = mFailedAttempts.get(currentUser, 0);
-        if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
-            return AuthenticationClient.LOCKOUT_PERMANENT;
-        } else if (failedAttempts > 0 &&
-                mTimedLockoutCleared.get(currentUser, false) == false
-                && (failedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
-            return AuthenticationClient.LOCKOUT_TIMED;
-        }
-        return AuthenticationClient.LOCKOUT_NONE;
-    }
-
-    private void scheduleLockoutResetForUser(int userId) {
-        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
-                getLockoutResetIntentForUser(userId));
-    }
-
-    private void cancelLockoutResetForUser(int userId) {
-        mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
-    }
-
-    private PendingIntent getLockoutResetIntentForUser(int userId) {
-        return PendingIntent.getBroadcast(mContext, userId,
-                new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId),
-                PendingIntent.FLAG_UPDATE_CURRENT);
-    }
-
-    public long startPreEnroll(IBinder token) {
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "startPreEnroll: no fingerprint HAL!");
-            return 0;
-        }
-        try {
-            return daemon.preEnroll();
-        } catch (RemoteException e) {
-            Slog.e(TAG, "startPreEnroll failed", e);
-        }
-        return 0;
-    }
-
-    public int startPostEnroll(IBinder token) {
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "startPostEnroll: no fingerprint HAL!");
-            return 0;
-        }
-        try {
-            return daemon.postEnroll();
-        } catch (RemoteException e) {
-            Slog.e(TAG, "startPostEnroll failed", e);
-        }
-        return 0;
-    }
-
-    /**
-     * Calls fingerprint HAL to switch states to the new task. If there's already a current task,
-     * it calls cancel() and sets mPendingClient to begin when the current task finishes
-     * ({@link FingerprintManager#FINGERPRINT_ERROR_CANCELED}).
-     * @param newClient the new client that wants to connect
-     * @param initiatedByClient true for authenticate, remove and enroll
-     */
-    private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
-        ClientMonitor currentClient = mCurrentClient;
-        if (currentClient != null) {
-            if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
-            if (currentClient instanceof InternalEnumerateClient ||
-                    currentClient instanceof InternalRemovalClient) {
-                // This condition means we're currently running internal diagnostics to
-                // remove extra fingerprints in the hardware and/or the software
-                // TODO: design an escape hatch in case client never finishes
-                if (newClient != null) {
-                    Slog.w(TAG, "Internal cleanup in progress but trying to start client "
-                            + newClient.getClass().getSuperclass().getSimpleName()
-                            + "(" + newClient.getOwnerString() + ")"
-                            + ", initiatedByClient = " + initiatedByClient);
-                }
-            }
-            else {
-                currentClient.stop(initiatedByClient);
-            }
-            mPendingClient = newClient;
-            mHandler.removeCallbacks(mResetClientState);
-            mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
-        } else if (newClient != null) {
-            mCurrentClient = newClient;
-            if (DEBUG) Slog.v(TAG, "starting client "
-                    + newClient.getClass().getSuperclass().getSimpleName()
-                    + "(" + newClient.getOwnerString() + ")"
-                    + ", initiatedByClient = " + initiatedByClient);
-            notifyClientActiveCallbacks(true);
-
-            newClient.start();
-        }
-    }
-
-    void startRemove(IBinder token, int fingerId, int groupId, int userId,
-            IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
-        if (token == null) {
-            Slog.w(TAG, "startRemove: token is null");
-            return;
-        }
-        if (receiver == null) {
-            Slog.w(TAG, "startRemove: receiver is null");
-            return;
-        }
-
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "startRemove: no fingerprint HAL!");
-            return;
-        }
-
-        if (internal) {
-            Context context = getContext();
-            InternalRemovalClient client = new InternalRemovalClient(context, mHalDeviceId,
-                    token, receiver, fingerId, groupId, userId, restricted,
-                    context.getOpPackageName()) {
-                @Override
-                public void notifyUserActivity() {
-
-                }
-                @Override
-                public IBiometricsFingerprint getFingerprintDaemon() {
-                    return FingerprintService.this.getFingerprintDaemon();
-                }
-            };
-            startClient(client, true);
-        }
-        else {
-            RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
-                    receiver, fingerId, groupId, userId, restricted, token.toString()) {
-                @Override
-                public void notifyUserActivity() {
-                    FingerprintService.this.userActivity();
-                }
-
-                @Override
-                public IBiometricsFingerprint getFingerprintDaemon() {
-                    return FingerprintService.this.getFingerprintDaemon();
-                }
-            };
-            startClient(client, true);
-        }
-    }
-
-    void startEnumerate(IBinder token, int userId,
-        IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "startEnumerate: no fingerprint HAL!");
-            return;
-        }
-        if (internal) {
-            List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
-            Context context = getContext();
-            InternalEnumerateClient client = new InternalEnumerateClient(context, mHalDeviceId,
-                    token, receiver, userId, userId, restricted, context.getOpPackageName(),
-                    enrolledList) {
-                @Override
-                public void notifyUserActivity() {
-
-                }
-
-                @Override
-                public IBiometricsFingerprint getFingerprintDaemon() {
-                    return FingerprintService.this.getFingerprintDaemon();
-                }
-            };
-            startClient(client, true);
-        }
-        else {
-            EnumerateClient client = new EnumerateClient(getContext(), mHalDeviceId, token,
-                    receiver, userId, userId, restricted, token.toString()) {
-                @Override
-                public void notifyUserActivity() {
-                    FingerprintService.this.userActivity();
-                }
-
-                @Override
-                public IBiometricsFingerprint getFingerprintDaemon() {
-                    return FingerprintService.this.getFingerprintDaemon();
-                }
-            };
-            startClient(client, true);
-        }
-    }
-
-    public List<Fingerprint> getEnrolledFingerprints(int userId) {
-        return mFingerprintUtils.getFingerprintsForUser(mContext, userId);
-    }
-
-    public boolean hasEnrolledFingerprints(int userId) {
-        if (userId != UserHandle.getCallingUserId()) {
-            checkPermission(INTERACT_ACROSS_USERS);
-        }
-        return mFingerprintUtils.getFingerprintsForUser(mContext, userId).size() > 0;
-    }
-
-    boolean hasPermission(String permission) {
-        return getContext().checkCallingOrSelfPermission(permission)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
-    void checkPermission(String permission) {
-        getContext().enforceCallingOrSelfPermission(permission,
-                "Must have " + permission + " permission.");
-    }
-
-    int getEffectiveUserId(int userId) {
-        UserManager um = UserManager.get(mContext);
-        if (um != null) {
-            final long callingIdentity = Binder.clearCallingIdentity();
-            userId = um.getCredentialOwnerProfile(userId);
-            Binder.restoreCallingIdentity(callingIdentity);
-        } else {
-            Slog.e(TAG, "Unable to acquire UserManager");
-        }
-        return userId;
-    }
-
-    boolean isCurrentUserOrProfile(int userId) {
-        UserManager um = UserManager.get(mContext);
-        if (um == null) {
-            Slog.e(TAG, "Unable to acquire UserManager");
-            return false;
-        }
-
-        final long token = Binder.clearCallingIdentity();
-        try {
-            // Allow current user or profiles of the current user...
-            for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
-                if (profileId == userId) {
-                    return true;
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-
-        return false;
-    }
-
-    private boolean isForegroundActivity(int uid, int pid) {
-        try {
-            List<RunningAppProcessInfo> procs =
-                    ActivityManager.getService().getRunningAppProcesses();
-            int N = procs.size();
-            for (int i = 0; i < N; i++) {
-                RunningAppProcessInfo proc = procs.get(i);
-                if (proc.pid == pid && proc.uid == uid
-                        && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
-                    return true;
-                }
-            }
-        } catch (RemoteException e) {
-            Slog.w(TAG, "am.getRunningAppProcesses() failed");
-        }
-        return false;
-    }
-
-    /**
-     * @param opPackageName name of package for caller
-     * @param requireForeground only allow this call while app is in the foreground
-     * @return true if caller can use fingerprint API
-     */
-    private boolean canUseFingerprint(String opPackageName, boolean requireForeground, int uid,
-            int pid, int userId) {
-        if (getContext().checkCallingPermission(USE_FINGERPRINT)
-                != PackageManager.PERMISSION_GRANTED) {
-            checkPermission(USE_BIOMETRIC);
-        }
-
-        if (isKeyguard(opPackageName)) {
-            return true; // Keyguard is always allowed
-        }
-        if (!isCurrentUserOrProfile(userId)) {
-            Slog.w(TAG,"Rejecting " + opPackageName + " ; not a current user or profile");
-            return false;
-        }
-        if (mAppOps.noteOp(AppOpsManager.OP_USE_FINGERPRINT, uid, opPackageName)
-                != AppOpsManager.MODE_ALLOWED) {
-            Slog.w(TAG, "Rejecting " + opPackageName + " ; permission denied");
-            return false;
-        }
-        if (requireForeground && !(isForegroundActivity(uid, pid) || currentClient(opPackageName))){
-            Slog.w(TAG, "Rejecting " + opPackageName + " ; not in foreground");
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * @param opPackageName package of the caller
-     * @return true if this is the same client currently using fingerprint
-     */
-    private boolean currentClient(String opPackageName) {
-        return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
-    }
-
-    /**
-     * @param clientPackage
-     * @return true if this is keyguard package
-     */
-    private boolean isKeyguard(String clientPackage) {
-        return mKeyguardPackage.equals(clientPackage);
-    }
-
-    private void addLockoutResetMonitor(FingerprintServiceLockoutResetMonitor monitor) {
-        if (!mLockoutMonitors.contains(monitor)) {
-            mLockoutMonitors.add(monitor);
-        }
-    }
-
-    private void removeLockoutResetCallback(
-            FingerprintServiceLockoutResetMonitor monitor) {
-        mLockoutMonitors.remove(monitor);
-    }
-
-    private void notifyLockoutResetMonitors() {
-        for (int i = 0; i < mLockoutMonitors.size(); i++) {
-            mLockoutMonitors.get(i).sendLockoutReset();
-        }
-    }
-
-    private void notifyClientActiveCallbacks(boolean isActive) {
-        List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
-        for (int i = 0; i < callbacks.size(); i++) {
-            try {
-                callbacks.get(i).onClientActiveChanged(isActive);
-            } catch (RemoteException re) {
-                // If the remote is dead, stop notifying it
-                mClientActiveCallbacks.remove(callbacks.get(i));
-            }
-        }
-    }
-
-    private void startAuthentication(IBinder token, long opId, int callingUserId, int groupId,
-                IFingerprintServiceReceiver receiver, int flags, boolean restricted,
-                String opPackageName, Bundle bundle, IBiometricPromptReceiver dialogReceiver) {
-        updateActiveGroup(groupId, opPackageName);
-
-        if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
-
-        AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
-                receiver, mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
-                dialogReceiver, mStatusBarService) {
-            @Override
-            public void onStart() {
-                try {
-                    mActivityManager.registerTaskStackListener(mTaskStackListener);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Could not register task stack listener", e);
-                }
-            }
-
-            @Override
-            public void onStop() {
-                try {
-                    mActivityManager.unregisterTaskStackListener(mTaskStackListener);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Could not unregister task stack listener", e);
-                }
-            }
-
-            @Override
-            public int handleFailedAttempt() {
-                final int currentUser = ActivityManager.getCurrentUser();
-                mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
-                mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
-                final int lockoutMode = getLockoutMode();
-                if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
-                    mPerformanceStats.permanentLockout++;
-                } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
-                    mPerformanceStats.lockout++;
-                }
-
-                // Failing multiple times will continue to push out the lockout time
-                if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
-                    scheduleLockoutResetForUser(currentUser);
-                    return lockoutMode;
-                }
-                return AuthenticationClient.LOCKOUT_NONE;
-            }
-
-            @Override
-            public void resetFailedAttempts() {
-                FingerprintService.this.resetFailedAttemptsForUser(true /* clearAttemptCounter */,
-                        ActivityManager.getCurrentUser());
-            }
-
-            @Override
-            public void notifyUserActivity() {
-                FingerprintService.this.userActivity();
-            }
-
-            @Override
-            public IBiometricsFingerprint getFingerprintDaemon() {
-                return FingerprintService.this.getFingerprintDaemon();
-            }
-        };
-
-        int lockoutMode = getLockoutMode();
-        if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
-            Slog.v(TAG, "In lockout mode(" + lockoutMode +
-                    ") ; disallowing authentication");
-            int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
-                    FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
-                    FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
-            if (!client.onError(errorCode, 0 /* vendorCode */)) {
-                Slog.w(TAG, "Cannot send permanent lockout message to client");
-            }
-            return;
-        }
-        startClient(client, true /* initiatedByClient */);
-    }
-
-    private void startEnrollment(IBinder token, byte [] cryptoToken, int userId,
-            IFingerprintServiceReceiver receiver, int flags, boolean restricted,
-            String opPackageName) {
-        updateActiveGroup(userId, opPackageName);
-
-        final int groupId = userId; // default group for fingerprint enrollment
-
-        EnrollClient client = new EnrollClient(getContext(), mHalDeviceId, token, receiver,
-                userId, groupId, cryptoToken, restricted, opPackageName) {
-
-            @Override
-            public IBiometricsFingerprint getFingerprintDaemon() {
-                return FingerprintService.this.getFingerprintDaemon();
-            }
-
-            @Override
-            public void notifyUserActivity() {
-                FingerprintService.this.userActivity();
-            }
-        };
-        startClient(client, true /* initiatedByClient */);
-    }
-
-    // attempt counter should only be cleared when Keyguard goes away or when
-    // a fingerprint is successfully authenticated
-    protected void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
-        if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
-            Slog.v(TAG, "Reset fingerprint lockout, clearAttemptCounter=" + clearAttemptCounter);
-        }
-        if (clearAttemptCounter) {
-            mFailedAttempts.put(userId, 0);
-        }
-        mTimedLockoutCleared.put(userId, true);
-        // If we're asked to reset failed attempts externally (i.e. from Keyguard),
-        // the alarm might still be pending; remove it.
-        cancelLockoutResetForUser(userId);
-        notifyLockoutResetMonitors();
-    }
-
-    private class FingerprintServiceLockoutResetMonitor implements IBinder.DeathRecipient {
-
-        private static final long WAKELOCK_TIMEOUT_MS = 2000;
-        private final IFingerprintServiceLockoutResetCallback mCallback;
-        private final WakeLock mWakeLock;
-
-        public FingerprintServiceLockoutResetMonitor(
-                IFingerprintServiceLockoutResetCallback callback) {
-            mCallback = callback;
-            mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
-                    "lockout reset callback");
-            try {
-                mCallback.asBinder().linkToDeath(FingerprintServiceLockoutResetMonitor.this, 0);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "caught remote exception in linkToDeath", e);
-            }
-        }
-
-        public void sendLockoutReset() {
-            if (mCallback != null) {
-                try {
-                    mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
-                    mCallback.onLockoutReset(mHalDeviceId, new IRemoteCallback.Stub() {
-
-                        @Override
-                        public void sendResult(Bundle data) throws RemoteException {
-                            releaseWakelock();
-                        }
-                    });
-                } catch (DeadObjectException e) {
-                    Slog.w(TAG, "Death object while invoking onLockoutReset: ", e);
-                    mHandler.post(mRemoveCallbackRunnable);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to invoke onLockoutReset: ", e);
-                    releaseWakelock();
-                }
-            }
-        }
-
-        private final Runnable mRemoveCallbackRunnable = new Runnable() {
-            @Override
-            public void run() {
-                releaseWakelock();
-                removeLockoutResetCallback(FingerprintServiceLockoutResetMonitor.this);
-            }
-        };
-
-        @Override
-        public void binderDied() {
-            Slog.e(TAG, "Lockout reset callback binder died");
-            mHandler.post(mRemoveCallbackRunnable);
-        }
-
-        private void releaseWakelock() {
-            if (mWakeLock.isHeld()) {
-                mWakeLock.release();
-            }
-        }
-    }
-
-    private IBiometricsFingerprintClientCallback mDaemonCallback =
-            new IBiometricsFingerprintClientCallback.Stub() {
-
-        @Override
-        public void onEnrollResult(final long deviceId, final int fingerId, final int groupId,
-                final int remaining) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    handleEnrollResult(deviceId, fingerId, groupId, remaining);
-                }
-            });
-        }
-
-        @Override
-        public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    handleAcquired(deviceId, acquiredInfo, vendorCode);
-                }
-            });
-        }
-
-        @Override
-        public void onAuthenticated(final long deviceId, final int fingerId, final int groupId,
-                ArrayList<Byte> token) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    handleAuthenticated(deviceId, fingerId, groupId, token);
-                }
-            });
-        }
-
-        @Override
-        public void onError(final long deviceId, final int error, final int vendorCode) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    handleError(deviceId, error, vendorCode);
-                }
-            });
-        }
-
-        @Override
-        public void onRemoved(final long deviceId, final int fingerId, final int groupId, final int remaining) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    handleRemoved(deviceId, fingerId, groupId, remaining);
-                }
-            });
-        }
-
-        @Override
-        public void onEnumerate(final long deviceId, final int fingerId, final int groupId,
-                final int remaining) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    handleEnumerate(deviceId, fingerId, groupId, remaining);
-                }
-            });
-        }
-    };
-
-    private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
-        @Override // Binder call
-        public long preEnroll(IBinder token) {
-            checkPermission(MANAGE_FINGERPRINT);
-            return startPreEnroll(token);
-        }
-
-        @Override // Binder call
-        public int postEnroll(IBinder token) {
-            checkPermission(MANAGE_FINGERPRINT);
-            return startPostEnroll(token);
-        }
-
-        @Override // Binder call
-        public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
-                final IFingerprintServiceReceiver receiver, final int flags,
-                final String opPackageName) {
-            checkPermission(MANAGE_FINGERPRINT);
-            final int limit = mContext.getResources().getInteger(
-                    com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
-
-            final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size();
-            if (enrolled >= limit) {
-                Slog.w(TAG, "Too many fingerprints registered");
-                return;
-            }
-
-            // Group ID is arbitrarily set to parent profile user ID. It just represents
-            // the default fingerprints for the user.
-            if (!isCurrentUserOrProfile(userId)) {
-                return;
-            }
-
-            final boolean restricted = isRestricted();
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    startEnrollment(token, cryptoToken, userId, receiver, flags,
-                            restricted, opPackageName);
-                }
-            });
-        }
-
-        private boolean isRestricted() {
-            // Only give privileged apps (like Settings) access to fingerprint info
-            final boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
-            return restricted;
-        }
-
-        @Override // Binder call
-        public void cancelEnrollment(final IBinder token) {
-            checkPermission(MANAGE_FINGERPRINT);
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    ClientMonitor client = mCurrentClient;
-                    if (client instanceof EnrollClient && client.getToken() == token) {
-                        client.stop(client.getToken() == token);
-                    }
-                }
-            });
-        }
-
-        @Override // Binder call
-        public void authenticate(final IBinder token, final long opId, final int groupId,
-                final IFingerprintServiceReceiver receiver, final int flags,
-                final String opPackageName, final Bundle bundle,
-                final IBiometricPromptReceiver dialogReceiver) {
-            final int callingUid = Binder.getCallingUid();
-            final int callingPid = Binder.getCallingPid();
-            final int callingUserId = UserHandle.getCallingUserId();
-            final boolean restricted = isRestricted();
-
-            if (!canUseFingerprint(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
-                    callingUserId)) {
-                if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
-                return;
-            }
-
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
-
-                    // Get performance stats object for this user.
-                    HashMap<Integer, PerformanceStats> pmap
-                            = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
-                    PerformanceStats stats = pmap.get(mCurrentUserId);
-                    if (stats == null) {
-                        stats = new PerformanceStats();
-                        pmap.put(mCurrentUserId, stats);
-                    }
-                    mPerformanceStats = stats;
-
-                    startAuthentication(token, opId, callingUserId, groupId, receiver,
-                            flags, restricted, opPackageName, bundle, dialogReceiver);
-                }
-            });
-        }
-
-        @Override // Binder call
-        public void cancelAuthentication(final IBinder token, final String opPackageName) {
-            final int callingUid = Binder.getCallingUid();
-            final int callingPid = Binder.getCallingPid();
-            final int callingUserId = UserHandle.getCallingUserId();
-
-            if (!canUseFingerprint(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
-                    callingUserId)) {
-                if (DEBUG) Slog.v(TAG, "cancelAuthentication(): reject " + opPackageName);
-                return;
-            }
-
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    ClientMonitor client = mCurrentClient;
-                    if (client instanceof AuthenticationClient) {
-                        if (client.getToken() == token) {
-                            if (DEBUG) Slog.v(TAG, "stop client " + client.getOwnerString());
-                            client.stop(client.getToken() == token);
-                        } else {
-                            if (DEBUG) Slog.v(TAG, "can't stop client "
-                                    + client.getOwnerString() + " since tokens don't match");
-                        }
-                    } else if (client != null) {
-                        if (DEBUG) Slog.v(TAG, "can't cancel non-authenticating client "
-                                + client.getOwnerString());
-                    }
-                }
-            });
-        }
-
-        @Override // Binder call
-        public void setActiveUser(final int userId) {
-            checkPermission(MANAGE_FINGERPRINT);
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    updateActiveGroup(userId, null);
-                }
-            });
-        }
-
-        @Override // Binder call
-        public void remove(final IBinder token, final int fingerId, final int groupId,
-                final int userId, final IFingerprintServiceReceiver receiver) {
-            checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
-            final boolean restricted = isRestricted();
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    startRemove(token, fingerId, groupId, userId, receiver,
-                            restricted, false /* internal */);
-                }
-            });
-        }
-
-        @Override // Binder call
-        public void enumerate(final IBinder token, final int userId,
-            final IFingerprintServiceReceiver receiver) {
-            checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
-            final boolean restricted = isRestricted();
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    startEnumerate(token, userId, receiver, restricted, false /* internal */);
-                }
-            });
-        }
-
-        @Override // Binder call
-        public boolean isHardwareDetected(long deviceId, String opPackageName) {
-            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
-                    Binder.getCallingUid(), Binder.getCallingPid(),
-                    UserHandle.getCallingUserId())) {
-                return false;
-            }
-
-            final long token = Binder.clearCallingIdentity();
-            try {
-                IBiometricsFingerprint daemon = getFingerprintDaemon();
-                return daemon != null && mHalDeviceId != 0;
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override // Binder call
-        public void rename(final int fingerId, final int groupId, final String name) {
-            checkPermission(MANAGE_FINGERPRINT);
-            if (!isCurrentUserOrProfile(groupId)) {
-                return;
-            }
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mFingerprintUtils.renameFingerprintForUser(mContext, fingerId,
-                            groupId, name);
-                }
-            });
-        }
-
-        @Override // Binder call
-        public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
-            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
-                    Binder.getCallingUid(), Binder.getCallingPid(),
-                    UserHandle.getCallingUserId())) {
-                return Collections.emptyList();
-            }
-
-            return FingerprintService.this.getEnrolledFingerprints(userId);
-        }
-
-        @Override // Binder call
-        public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
-            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
-                    Binder.getCallingUid(), Binder.getCallingPid(),
-                    UserHandle.getCallingUserId())) {
-                return false;
-            }
-
-            return FingerprintService.this.hasEnrolledFingerprints(userId);
-        }
-
-        @Override // Binder call
-        public long getAuthenticatorId(String opPackageName) {
-            // In this method, we're not checking whether the caller is permitted to use fingerprint
-            // API because current authenticator ID is leaked (in a more contrived way) via Android
-            // Keystore (android.security.keystore package): the user of that API can create a key
-            // which requires fingerprint authentication for its use, and then query the key's
-            // characteristics (hidden API) which returns, among other things, fingerprint
-            // authenticator ID which was active at key creation time.
-            //
-            // Reason: The part of Android Keystore which runs inside an app's process invokes this
-            // method in certain cases. Those cases are not always where the developer demonstrates
-            // explicit intent to use fingerprint functionality. Thus, to avoiding throwing an
-            // unexpected SecurityException this method does not check whether its caller is
-            // permitted to use fingerprint API.
-            //
-            // The permission check should be restored once Android Keystore no longer invokes this
-            // method from inside app processes.
-
-            return FingerprintService.this.getAuthenticatorId(opPackageName);
-        }
-
-        @Override // Binder call
-        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                if (args.length > 0 && "--proto".equals(args[0])) {
-                    dumpProto(fd);
-                } else {
-                    dumpInternal(pw);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-
-        @Override // Binder call
-        public void resetTimeout(byte [] token) {
-            checkPermission(RESET_FINGERPRINT_LOCKOUT);
-            // TODO: confirm security token when we move timeout management into the HAL layer.
-            mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
-        }
-
-        @Override
-        public void addLockoutResetCallback(final IFingerprintServiceLockoutResetCallback callback)
-                throws RemoteException {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    addLockoutResetMonitor(
-                            new FingerprintServiceLockoutResetMonitor(callback));
-                }
-            });
-        }
-
-        @Override
-        public boolean isClientActive() {
-            checkPermission(MANAGE_FINGERPRINT);
-            synchronized(FingerprintService.this) {
-                return (mCurrentClient != null) || (mPendingClient != null);
-            }
-        }
-
-        @Override
-        public void addClientActiveCallback(IFingerprintClientActiveCallback callback) {
-            checkPermission(MANAGE_FINGERPRINT);
-            mClientActiveCallbacks.add(callback);
-        }
-
-        @Override
-        public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) {
-            checkPermission(MANAGE_FINGERPRINT);
-            mClientActiveCallbacks.remove(callback);
-        }
-    }
-
-    private void dumpInternal(PrintWriter pw) {
-        JSONObject dump = new JSONObject();
-        try {
-            dump.put("service", "Fingerprint Manager");
-
-            JSONArray sets = new JSONArray();
-            for (UserInfo user : UserManager.get(getContext()).getUsers()) {
-                final int userId = user.getUserHandle().getIdentifier();
-                final int N = mFingerprintUtils.getFingerprintsForUser(mContext, userId).size();
-                PerformanceStats stats = mPerformanceMap.get(userId);
-                PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
-                JSONObject set = new JSONObject();
-                set.put("id", userId);
-                set.put("count", N);
-                set.put("accept", (stats != null) ? stats.accept : 0);
-                set.put("reject", (stats != null) ? stats.reject : 0);
-                set.put("acquire", (stats != null) ? stats.acquire : 0);
-                set.put("lockout", (stats != null) ? stats.lockout : 0);
-                set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
-                // cryptoStats measures statistics about secure fingerprint transactions
-                // (e.g. to unlock password storage, make secure purchases, etc.)
-                set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
-                set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
-                set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
-                set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
-                set.put("permanentLockoutCrypto",
-                    (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
-                sets.put(set);
-            }
-
-            dump.put("prints", sets);
-        } catch (JSONException e) {
-            Slog.e(TAG, "dump formatting failure", e);
-        }
-        pw.println(dump);
-    }
-
-    private void dumpProto(FileDescriptor fd) {
-        final ProtoOutputStream proto = new ProtoOutputStream(fd);
-        for (UserInfo user : UserManager.get(getContext()).getUsers()) {
-            final int userId = user.getUserHandle().getIdentifier();
-
-            final long userToken = proto.start(FingerprintServiceDumpProto.USERS);
-
-            proto.write(FingerprintUserStatsProto.USER_ID, userId);
-            proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
-                    mFingerprintUtils.getFingerprintsForUser(mContext, userId).size());
-
-            // Normal fingerprint authentications (e.g. lockscreen)
-            final PerformanceStats normal = mPerformanceMap.get(userId);
-            if (normal != null) {
-                final long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
-                proto.write(PerformanceStatsProto.ACCEPT, normal.accept);
-                proto.write(PerformanceStatsProto.REJECT, normal.reject);
-                proto.write(PerformanceStatsProto.ACQUIRE, normal.acquire);
-                proto.write(PerformanceStatsProto.LOCKOUT, normal.lockout);
-                proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, normal.permanentLockout);
-                proto.end(countsToken);
-            }
-
-            // Statistics about secure fingerprint transactions (e.g. to unlock password
-            // storage, make secure purchases, etc.)
-            final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
-            if (crypto != null) {
-                final long countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
-                proto.write(PerformanceStatsProto.ACCEPT, crypto.accept);
-                proto.write(PerformanceStatsProto.REJECT, crypto.reject);
-                proto.write(PerformanceStatsProto.ACQUIRE, crypto.acquire);
-                proto.write(PerformanceStatsProto.LOCKOUT, crypto.lockout);
-                proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, crypto.permanentLockout);
-                proto.end(countsToken);
-            }
-
-            proto.end(userToken);
-        }
-        proto.flush();
-        mPerformanceMap.clear();
-        mCryptoPerformanceMap.clear();
-    }
-
-    @Override
-    public void onStart() {
-        publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
-        SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
-        listenForUserSwitches();
-    }
-
-    private void updateActiveGroup(int userId, String clientPackage) {
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-
-        if (daemon != null) {
-            try {
-                userId = getUserOrWorkProfileId(clientPackage, userId);
-                if (userId != mCurrentUserId) {
-                    int firstSdkInt = Build.VERSION.FIRST_SDK_INT;
-                    if (firstSdkInt < Build.VERSION_CODES.BASE) {
-                        Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " +
-                                "at least VERSION_CODES.BASE");
-                    }
-                    File baseDir;
-                    if (firstSdkInt <= Build.VERSION_CODES.O_MR1) {
-                        baseDir = Environment.getUserSystemDirectory(userId);
-                    } else {
-                        baseDir = Environment.getDataVendorDeDirectory(userId);
-                    }
-
-                    File fpDir = new File(baseDir, FP_DATA_DIR);
-                    if (!fpDir.exists()) {
-                        if (!fpDir.mkdir()) {
-                            Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
-                            return;
-                        }
-                        // Calling mkdir() from this process will create a directory with our
-                        // permissions (inherited from the containing dir). This command fixes
-                        // the label.
-                        if (!SELinux.restorecon(fpDir)) {
-                            Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
-                            return;
-                        }
-                    }
-
-                    daemon.setActiveGroup(userId, fpDir.getAbsolutePath());
-                    mCurrentUserId = userId;
-                }
-                mAuthenticatorIds.put(userId,
-                        hasEnrolledFingerprints(userId) ? daemon.getAuthenticatorId() : 0L);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to setActiveGroup():", e);
-            }
-        }
-    }
-
-    /**
-     * @param clientPackage the package of the caller
-     * @return the profile id
-     */
-    private int getUserOrWorkProfileId(String clientPackage, int userId) {
-        if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
-            return userId;
-        }
-        return getEffectiveUserId(userId);
-    }
-
-    /**
-     * @param userId
-     * @return true if this is a work profile
-     */
-    private boolean isWorkProfile(int userId) {
-        UserInfo userInfo = null;
-        final long token = Binder.clearCallingIdentity();
-        try {
-            userInfo = mUserManager.getUserInfo(userId);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-        return userInfo != null && userInfo.isManagedProfile();
-    }
-
-    private void listenForUserSwitches() {
-        try {
-            ActivityManager.getService().registerUserSwitchObserver(
-                new SynchronousUserSwitchObserver() {
-                    @Override
-                    public void onUserSwitching(int newUserId) throws RemoteException {
-                        mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
-                                .sendToTarget();
-                    }
-                }, TAG);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Failed to listen for user switching event" ,e);
-        }
-    }
-
-    /***
-     * @param opPackageName the name of the calling package
-     * @return authenticator id for the calling user
-     */
-    public long getAuthenticatorId(String opPackageName) {
-        final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
-        return mAuthenticatorIds.getOrDefault(userId, 0L);
-    }
-}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
deleted file mode 100644
index 5fbd735..0000000
--- a/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.fingerprint.Fingerprint;
-import android.text.TextUtils;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.List;
-
-/**
- * Utility class for dealing with fingerprints and fingerprint settings.
- */
-public class FingerprintUtils {
-
-    private static final Object sInstanceLock = new Object();
-    private static FingerprintUtils sInstance;
-
-    @GuardedBy("this")
-    private final SparseArray<FingerprintsUserState> mUsers = new SparseArray<>();
-
-    public static FingerprintUtils getInstance() {
-        synchronized (sInstanceLock) {
-            if (sInstance == null) {
-                sInstance = new FingerprintUtils();
-            }
-        }
-        return sInstance;
-    }
-
-    private FingerprintUtils() {
-    }
-
-    public List<Fingerprint> getFingerprintsForUser(Context ctx, int userId) {
-        return getStateForUser(ctx, userId).getFingerprints();
-    }
-
-    public void addFingerprintForUser(Context ctx, int fingerId, int userId) {
-        getStateForUser(ctx, userId).addFingerprint(fingerId, userId);
-    }
-
-    public void removeFingerprintIdForUser(Context ctx, int fingerId, int userId) {
-        getStateForUser(ctx, userId).removeFingerprint(fingerId);
-    }
-
-    public void renameFingerprintForUser(Context ctx, int fingerId, int userId, CharSequence name) {
-        if (TextUtils.isEmpty(name)) {
-            // Don't do the rename if it's empty
-            return;
-        }
-        getStateForUser(ctx, userId).renameFingerprint(fingerId, name);
-    }
-
-    private FingerprintsUserState getStateForUser(Context ctx, int userId) {
-        synchronized (this) {
-            FingerprintsUserState state = mUsers.get(userId);
-            if (state == null) {
-                state = new FingerprintsUserState(ctx, userId);
-                mUsers.put(userId, state);
-            }
-            return state;
-        }
-    }
-}
-
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java b/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
deleted file mode 100644
index b0cde2d..0000000
--- a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.fingerprint.Fingerprint;
-import android.os.AsyncTask;
-import android.os.Environment;
-import android.util.AtomicFile;
-import android.util.Slog;
-import android.util.Xml;
-
-import com.android.internal.annotations.GuardedBy;
-
-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.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class managing the set of fingerprint per user across device reboots.
- */
-class FingerprintsUserState {
-
-    private static final String TAG = "FingerprintState";
-    private static final String FINGERPRINT_FILE = "settings_fingerprint.xml";
-
-    private static final String TAG_FINGERPRINTS = "fingerprints";
-    private static final String TAG_FINGERPRINT = "fingerprint";
-    private static final String ATTR_NAME = "name";
-    private static final String ATTR_GROUP_ID = "groupId";
-    private static final String ATTR_FINGER_ID = "fingerId";
-    private static final String ATTR_DEVICE_ID = "deviceId";
-
-    private final File mFile;
-
-    @GuardedBy("this")
-    private final ArrayList<Fingerprint> mFingerprints = new ArrayList<Fingerprint>();
-    private final Context mCtx;
-
-    public FingerprintsUserState(Context ctx, int userId) {
-        mFile = getFileForUser(userId);
-        mCtx = ctx;
-        synchronized (this) {
-            readStateSyncLocked();
-        }
-    }
-
-    public void addFingerprint(int fingerId, int groupId) {
-        synchronized (this) {
-            mFingerprints.add(new Fingerprint(getUniqueName(), groupId, fingerId, 0));
-            scheduleWriteStateLocked();
-        }
-    }
-
-    public void removeFingerprint(int fingerId) {
-        synchronized (this) {
-            for (int i = 0; i < mFingerprints.size(); i++) {
-                if (mFingerprints.get(i).getFingerId() == fingerId) {
-                    mFingerprints.remove(i);
-                    scheduleWriteStateLocked();
-                    break;
-                }
-            }
-        }
-    }
-
-    public void renameFingerprint(int fingerId, CharSequence name) {
-        synchronized (this) {
-            for (int i = 0; i < mFingerprints.size(); i++) {
-                if (mFingerprints.get(i).getFingerId() == fingerId) {
-                    Fingerprint old = mFingerprints.get(i);
-                    mFingerprints.set(i, new Fingerprint(name, old.getGroupId(), old.getFingerId(),
-                            old.getDeviceId()));
-                    scheduleWriteStateLocked();
-                    break;
-                }
-            }
-        }
-    }
-
-    public List<Fingerprint> getFingerprints() {
-        synchronized (this) {
-            return getCopy(mFingerprints);
-        }
-    }
-
-    /**
-     * Finds a unique name for the given fingerprint
-     * @return unique name
-     */
-    private String getUniqueName() {
-        int guess = 1;
-        while (true) {
-            // Not the most efficient algorithm in the world, but there shouldn't be more than 10
-            String name = mCtx.getString(com.android.internal.R.string.fingerprint_name_template,
-                    guess);
-            if (isUnique(name)) {
-                return name;
-            }
-            guess++;
-        }
-    }
-
-    private boolean isUnique(String name) {
-        for (Fingerprint fp : mFingerprints) {
-            if (fp.getName().equals(name)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private static File getFileForUser(int userId) {
-        return new File(Environment.getUserSystemDirectory(userId), FINGERPRINT_FILE);
-    }
-
-    private final Runnable mWriteStateRunnable = new Runnable() {
-        @Override
-        public void run() {
-            doWriteState();
-        }
-    };
-
-    private void scheduleWriteStateLocked() {
-        AsyncTask.execute(mWriteStateRunnable);
-    }
-
-    private ArrayList<Fingerprint> getCopy(ArrayList<Fingerprint> array) {
-        ArrayList<Fingerprint> result = new ArrayList<Fingerprint>(array.size());
-        for (int i = 0; i < array.size(); i++) {
-            Fingerprint fp = array.get(i);
-            result.add(new Fingerprint(fp.getName(), fp.getGroupId(), fp.getFingerId(),
-                    fp.getDeviceId()));
-        }
-        return result;
-    }
-
-    private void doWriteState() {
-        AtomicFile destination = new AtomicFile(mFile);
-
-        ArrayList<Fingerprint> fingerprints;
-
-        synchronized (this) {
-            fingerprints = getCopy(mFingerprints);
-        }
-
-        FileOutputStream out = null;
-        try {
-            out = destination.startWrite();
-
-            XmlSerializer serializer = Xml.newSerializer();
-            serializer.setOutput(out, "utf-8");
-            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-            serializer.startDocument(null, true);
-            serializer.startTag(null, TAG_FINGERPRINTS);
-
-            final int count = fingerprints.size();
-            for (int i = 0; i < count; i++) {
-                Fingerprint fp = fingerprints.get(i);
-                serializer.startTag(null, TAG_FINGERPRINT);
-                serializer.attribute(null, ATTR_FINGER_ID, Integer.toString(fp.getFingerId()));
-                serializer.attribute(null, ATTR_NAME, fp.getName().toString());
-                serializer.attribute(null, ATTR_GROUP_ID, Integer.toString(fp.getGroupId()));
-                serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(fp.getDeviceId()));
-                serializer.endTag(null, TAG_FINGERPRINT);
-            }
-
-            serializer.endTag(null, TAG_FINGERPRINTS);
-            serializer.endDocument();
-            destination.finishWrite(out);
-
-            // Any error while writing is fatal.
-        } catch (Throwable t) {
-            Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
-            destination.failWrite(out);
-            throw new IllegalStateException("Failed to write fingerprints", t);
-        } finally {
-            IoUtils.closeQuietly(out);
-        }
-    }
-
-    @GuardedBy("this")
-    private void readStateSyncLocked() {
-        FileInputStream in;
-        if (!mFile.exists()) {
-            return;
-        }
-        try {
-            in = new FileInputStream(mFile);
-        } catch (FileNotFoundException fnfe) {
-            Slog.i(TAG, "No fingerprint state");
-            return;
-        }
-        try {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(in, null);
-            parseStateLocked(parser);
-
-        } catch (XmlPullParserException | IOException e) {
-            throw new IllegalStateException("Failed parsing settings file: "
-                    + mFile , e);
-        } finally {
-            IoUtils.closeQuietly(in);
-        }
-    }
-
-    @GuardedBy("this")
-    private void parseStateLocked(XmlPullParser parser)
-            throws IOException, XmlPullParserException {
-        final int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-
-            String tagName = parser.getName();
-            if (tagName.equals(TAG_FINGERPRINTS)) {
-                parseFingerprintsLocked(parser);
-            }
-        }
-    }
-
-    @GuardedBy("this")
-    private void parseFingerprintsLocked(XmlPullParser parser)
-            throws IOException, XmlPullParserException {
-
-        final int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-
-            String tagName = parser.getName();
-            if (tagName.equals(TAG_FINGERPRINT)) {
-                String name = parser.getAttributeValue(null, ATTR_NAME);
-                String groupId = parser.getAttributeValue(null, ATTR_GROUP_ID);
-                String fingerId = parser.getAttributeValue(null, ATTR_FINGER_ID);
-                String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
-                mFingerprints.add(new Fingerprint(name, Integer.parseInt(groupId),
-                        Integer.parseInt(fingerId), Integer.parseInt(deviceId)));
-            }
-        }
-    }
-
-}
diff --git a/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java b/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
deleted file mode 100644
index 434db98..0000000
--- a/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
+++ /dev/null
@@ -1,93 +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.fingerprint;
-
-import android.content.Context;
-import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.IBinder;
-import android.util.Slog;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An internal class to help clean up unknown fingerprints in the hardware and software
- */
-public abstract class InternalEnumerateClient extends EnumerateClient {
-
-    private List<Fingerprint> mEnrolledList;
-    private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete
-
-    public InternalEnumerateClient(Context context, long halDeviceId, IBinder token,
-            IFingerprintServiceReceiver receiver, int groupId, int userId,
-            boolean restricted, String owner, List<Fingerprint> enrolledList) {
-
-        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
-        mEnrolledList = enrolledList;
-    }
-
-    private void handleEnumeratedFingerprint(int fingerId, int groupId, int remaining) {
-
-        boolean matched = false;
-        for (int i=0; i<mEnrolledList.size(); i++) {
-            if (mEnrolledList.get(i).getFingerId() == fingerId) {
-                mEnrolledList.remove(i);
-                matched = true;
-                break;
-            }
-        }
-
-        // fingerId 0 means no fingerprints are in hardware
-        if (!matched && fingerId != 0) {
-            Fingerprint fingerprint = new Fingerprint("", groupId, fingerId, getHalDeviceId());
-            mUnknownFingerprints.add(fingerprint);
-        }
-    }
-
-    private void doFingerprintCleanup() {
-
-        if (mEnrolledList == null) {
-            return;
-        }
-
-        for (Fingerprint f : mEnrolledList) {
-            Slog.e(TAG, "Internal Enumerate: Removing dangling enrolled fingerprint: "
-                    + f.getName() + " " + f.getFingerId() + " " + f.getGroupId()
-                    + " " + f.getDeviceId());
-
-            FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(),
-                    f.getFingerId(), getTargetUserId());
-        }
-        mEnrolledList.clear();
-    }
-
-    public List<Fingerprint> getUnknownFingerprints() {
-        return mUnknownFingerprints;
-    }
-
-    @Override
-    public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
-
-        handleEnumeratedFingerprint(fingerId, groupId, remaining);
-        if (remaining == 0) {
-            doFingerprintCleanup();
-        }
-
-        return remaining == 0;
-    }
-
-}
diff --git a/services/core/java/com/android/server/fingerprint/InternalRemovalClient.java b/services/core/java/com/android/server/fingerprint/InternalRemovalClient.java
deleted file mode 100644
index 19f61fe..0000000
--- a/services/core/java/com/android/server/fingerprint/InternalRemovalClient.java
+++ /dev/null
@@ -1,33 +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.fingerprint;
-
-import android.content.Context;
-import android.os.IBinder;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import com.android.server.fingerprint.RemovalClient;
-
-public abstract class InternalRemovalClient extends RemovalClient {
-
-    public InternalRemovalClient(Context context, long halDeviceId, IBinder token,
-            IFingerprintServiceReceiver receiver, int fingerId, int groupId, int userId,
-            boolean restricted, String owner) {
-
-        super(context, halDeviceId, token, receiver, fingerId, groupId, userId, restricted, owner);
-
-    }
-}
diff --git a/services/core/java/com/android/server/fingerprint/RemovalClient.java b/services/core/java/com/android/server/fingerprint/RemovalClient.java
deleted file mode 100644
index ffc8488..0000000
--- a/services/core/java/com/android/server/fingerprint/RemovalClient.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Slog;
-import com.android.internal.logging.MetricsLogger;
-
-/**
- * A class to keep track of the remove state for a given client.
- */
-public abstract class RemovalClient extends ClientMonitor {
-    private int mFingerId;
-
-    public RemovalClient(Context context, long halDeviceId, IBinder token,
-            IFingerprintServiceReceiver receiver, int fingerId, int groupId, int userId,
-            boolean restricted, String owner) {
-        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
-        mFingerId = fingerId;
-    }
-
-    @Override
-    public int start() {
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-        // The fingerprint template ids will be removed when we get confirmation from the HAL
-        try {
-            final int result = daemon.remove(getGroupId(), mFingerId);
-            if (result != 0) {
-                Slog.w(TAG, "startRemove with id = " + mFingerId + " failed, result=" + result);
-                MetricsLogger.histogram(getContext(), "fingerprintd_remove_start_error", result);
-                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
-                return result;
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "startRemove failed", e);
-        }
-        return 0;
-    }
-
-    @Override
-    public int stop(boolean initiatedByClient) {
-        if (mAlreadyCancelled) {
-            Slog.w(TAG, "stopRemove: already cancelled!");
-            return 0;
-        }
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "stopRemoval: no fingerprint HAL!");
-            return ERROR_ESRCH;
-        }
-        try {
-            final int result = daemon.cancel();
-            if (result != 0) {
-                Slog.w(TAG, "stopRemoval failed, result=" + result);
-                return result;
-            }
-            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer removing");
-        } catch (RemoteException e) {
-            Slog.e(TAG, "stopRemoval failed", e);
-            return ERROR_ESRCH;
-        }
-        mAlreadyCancelled = true;
-        return 0; // success
-    }
-
-    /*
-     * @return true if we're done.
-     */
-    private boolean sendRemoved(int fingerId, int groupId, int remaining) {
-        IFingerprintServiceReceiver receiver = getReceiver();
-        try {
-            if (receiver != null) {
-                receiver.onRemoved(getHalDeviceId(), fingerId, groupId, remaining);
-            }
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Failed to notify Removed:", e);
-        }
-        return remaining == 0;
-    }
-
-    @Override
-    public boolean onRemoved(int fingerId, int groupId, int remaining) {
-        if (fingerId != 0) {
-            FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(), fingerId,
-                    getTargetUserId());
-        }
-        return sendRemoved(fingerId, getGroupId(), remaining);
-    }
-
-    @Override
-    public boolean onEnrollResult(int fingerId, int groupId, int rem) {
-        if (DEBUG) Slog.w(TAG, "onEnrollResult() called for remove!");
-        return true; // Invalid for Remove
-    }
-
-    @Override
-    public boolean onAuthenticated(int fingerId, int groupId) {
-        if (DEBUG) Slog.w(TAG, "onAuthenticated() called for remove!");
-        return true; // Invalid for Remove.
-    }
-
-    @Override
-    public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
-        if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for remove!");
-        return true; // Invalid for Remove.
-    }
-
-
-}
diff --git a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
new file mode 100644
index 0000000..ed17de5
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
@@ -0,0 +1,94 @@
+/*
+ * 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.hdmi;
+
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+
+/**
+ * Feature action that handles Audio Return Channel initiated by AVR devices.
+ */
+public class ArcInitiationActionFromAvr extends HdmiCecFeatureAction {
+    // TODO(shubang): add tests
+
+    // State in which waits for ARC response.
+    private static final int STATE_WAITING_FOR_INITIATE_ARC_RESPONSE = 1;
+    private static final int STATE_ARC_INITIATED = 2;
+
+    // the required maximum response time specified in CEC 9.2
+    private static final int TIMEOUT_MS = 1000;
+
+    ArcInitiationActionFromAvr(HdmiCecLocalDevice source) {
+        super(source);
+    }
+
+    @Override
+    boolean start() {
+        audioSystem().setArcStatus(true);
+        mState = STATE_WAITING_FOR_INITIATE_ARC_RESPONSE;
+        addTimer(mState, TIMEOUT_MS);
+        sendInitiateArc();
+        return true;
+    }
+
+    @Override
+    boolean processCommand(HdmiCecMessage cmd) {
+        if (mState != STATE_WAITING_FOR_INITIATE_ARC_RESPONSE) {
+            return false;
+        }
+        switch (cmd.getOpcode()) {
+            case Constants.MESSAGE_FEATURE_ABORT:
+            case Constants.MESSAGE_REPORT_ARC_TERMINATED:
+                audioSystem().setArcStatus(false);
+                finish();
+                return true;
+            case Constants.MESSAGE_REPORT_ARC_INITIATED:
+                mState = STATE_ARC_INITIATED;
+                finish();
+                return true;
+        }
+        return false;
+    }
+
+    @Override
+    void handleTimerEvent(int state) {
+        if (mState != state) {
+            return;
+        }
+
+        switch (mState) {
+            case STATE_WAITING_FOR_INITIATE_ARC_RESPONSE:
+                handleInitiateArcTimeout();
+                break;
+        }
+    }
+
+    protected void sendInitiateArc() {
+        sendCommand(HdmiCecMessageBuilder.buildInitiateArc(getSourceAddress(), Constants.ADDR_TV),
+                result -> {
+                    if (result != SendMessageResult.SUCCESS) {
+                        audioSystem().setArcStatus(false);
+                        finish();
+                    }
+                });
+    }
+
+    private void handleInitiateArcTimeout() {
+        HdmiLogger.debug("handleInitiateArcTimeout");
+        audioSystem().setArcStatus(false);
+        finish();
+    }
+
+}
diff --git a/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
new file mode 100644
index 0000000..7e73321
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
@@ -0,0 +1,86 @@
+/*
+ * 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.hdmi;
+
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+
+/**
+ * Feature action that handles Audio Return Channel terminated by AVR devices.
+ */
+public class ArcTerminationActionFromAvr extends HdmiCecFeatureAction {
+
+    // State in which waits for ARC response.
+    private static final int STATE_WAITING_FOR_INITIATE_ARC_RESPONSE = 1;
+    private static final int STATE_ARC_TERMINATED = 2;
+
+    // the required maximum response time specified in CEC 9.2
+    private static final int TIMEOUT_MS = 1000;
+
+    ArcTerminationActionFromAvr(HdmiCecLocalDevice source) {
+        super(source);
+    }
+
+    @Override
+    boolean start() {
+        mState = STATE_WAITING_FOR_INITIATE_ARC_RESPONSE;
+        addTimer(mState, TIMEOUT_MS);
+        sendTerminateArc();
+        return true;
+    }
+
+    @Override
+    boolean processCommand(HdmiCecMessage cmd) {
+        if (mState != STATE_WAITING_FOR_INITIATE_ARC_RESPONSE) {
+            return false;
+        }
+        switch (cmd.getOpcode()) {
+            case Constants.MESSAGE_REPORT_ARC_TERMINATED:
+                mState = STATE_ARC_TERMINATED;
+                audioSystem().setArcStatus(false);
+                finish();
+                return true;
+        }
+        return false;
+    }
+
+    @Override
+    void handleTimerEvent(int state) {
+        if (mState != state) {
+            return;
+        }
+
+        switch (mState) {
+            case STATE_WAITING_FOR_INITIATE_ARC_RESPONSE:
+                handleTerminateArcTimeout();
+                break;
+        }
+    }
+
+    protected void sendTerminateArc() {
+        sendCommand(HdmiCecMessageBuilder.buildTerminateArc(getSourceAddress(), Constants.ADDR_TV),
+            result -> {
+                if (result != SendMessageResult.SUCCESS) {
+                    HdmiLogger.debug("Terminate ARC was not successfully sent.");
+                    finish();
+                }
+            });
+    }
+
+    private void handleTerminateArcTimeout() {
+        HdmiLogger.debug("handleTerminateArcTimeout");
+        finish();
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index a2a55e5..d154830 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -16,12 +16,14 @@
 
 package com.android.server.hdmi;
 
+import android.annotation.IntDef;
 import android.hardware.hdmi.HdmiDeviceInfo;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
- * Defines constants related to HDMI-CEC protocol internal implementation.
- * If a constant will be used in the public api, it should be located in
- * {@link android.hardware.hdmi.HdmiControlManager}.
+ * Defines constants related to HDMI-CEC protocol internal implementation. If a constant will be
+ * used in the public api, it should be located in {@link android.hardware.hdmi.HdmiControlManager}.
  */
 final class Constants {
 
@@ -179,6 +181,46 @@
     static final int MENU_STATE_ACTIVATED = 0;
     static final int MENU_STATE_DEACTIVATED = 1;
 
+    // Audio Format Codes
+    // Refer to CEA Standard (CEA-861-D), Table 37 Audio Format Codes.
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        AUDIO_CODEC_NONE,
+        AUDIO_CODEC_LPCM,
+        AUDIO_CODEC_DD,
+        AUDIO_CODEC_MPEG1,
+        AUDIO_CODEC_MP3,
+        AUDIO_CODEC_MPEG2,
+        AUDIO_CODEC_AAC,
+        AUDIO_CODEC_DTS,
+        AUDIO_CODEC_ATRAC,
+        AUDIO_CODEC_ONEBITAUDIO,
+        AUDIO_CODEC_DDP,
+        AUDIO_CODEC_DTSHD,
+        AUDIO_CODEC_TRUEHD,
+        AUDIO_CODEC_DST,
+        AUDIO_CODEC_WMAPRO,
+        AUDIO_CODEC_MAX,
+    })
+    public @interface AudioCodec {}
+
+    static final int AUDIO_CODEC_NONE = 0x0;
+    static final int AUDIO_CODEC_LPCM = 0x1; // Support LPCMs
+    static final int AUDIO_CODEC_DD = 0x2; // Support DD
+    static final int AUDIO_CODEC_MPEG1 = 0x3; // Support MPEG1
+    static final int AUDIO_CODEC_MP3 = 0x4; // Support MP3
+    static final int AUDIO_CODEC_MPEG2 = 0x5; // Support MPEG2
+    static final int AUDIO_CODEC_AAC = 0x6; // Support AAC
+    static final int AUDIO_CODEC_DTS = 0x7; // Support DTS
+    static final int AUDIO_CODEC_ATRAC = 0x8; // Support ATRAC
+    static final int AUDIO_CODEC_ONEBITAUDIO = 0x9; // Support One-Bit Audio
+    static final int AUDIO_CODEC_DDP = 0xA; // Support DDP
+    static final int AUDIO_CODEC_DTSHD = 0xB; // Support DTSHD
+    static final int AUDIO_CODEC_TRUEHD = 0xC; // Support MLP/TRUE-HD
+    static final int AUDIO_CODEC_DST = 0xD; // Support DST
+    static final int AUDIO_CODEC_WMAPRO = 0xE; // Support WMA-Pro
+    static final int AUDIO_CODEC_MAX = 0xF;
+
     // Bit mask used to get the routing path of the top level device.
     // When &'d with the path 1.2.2.0 (0x1220), for instance, gives 1.0.0.0.
     static final int ROUTING_PATH_TOP_MASK = 0xF000;
@@ -190,16 +232,31 @@
 
     // Strategy for device polling.
     // Should use "OR(|) operation of POLL_STRATEGY_XXX and POLL_ITERATION_XXX.
-    static final int POLL_STRATEGY_MASK = 0x3;  // first and second bit.
+    static final int POLL_STRATEGY_MASK = 0x3; // first and second bit.
     static final int POLL_STRATEGY_REMOTES_DEVICES = 0x1;
     static final int POLL_STRATEGY_SYSTEM_AUDIO = 0x2;
 
-    static final int POLL_ITERATION_STRATEGY_MASK = 0x30000;  // first and second bit.
+    static final int POLL_ITERATION_STRATEGY_MASK = 0x30000; // first and second bit.
     static final int POLL_ITERATION_IN_ORDER = 0x10000;
     static final int POLL_ITERATION_REVERSE_ORDER = 0x20000;
 
     static final int UNKNOWN_VOLUME = -1;
 
+    // States of property PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON
+    // to decide if turn on the system audio control when power on the device
+    @IntDef({
+        ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON,
+        USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON,
+        NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON
+    })
+    @interface SystemAudioControlOnPowerOn {}
+
+    static final int ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON = 0;
+    static final int USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON = 1;
+    static final int NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON = 2;
+
+    static final String PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM =
+            "persist.sys.hdmi.addr.audiosystem";
     static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "persist.sys.hdmi.addr.playback";
     static final String PROPERTY_PREFERRED_ADDRESS_TV = "persist.sys.hdmi.addr.tv";
 
@@ -219,6 +276,59 @@
     // when it's an active source. True by default.
     static final String PROPERTY_KEEP_AWAKE = "persist.sys.hdmi.keep_awake";
 
+    // TODO(UI): Set this from UI to decide if turn on System Audio Mode when power on the device
+    /**
+     * Property to decide if turn on the system audio control when power on the device.
+     *
+     * <p>Default is always turn on. State must be a valid {@link SystemAudioControlOnPowerOn} int.
+     */
+    static final String PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON =
+            "persist.sys.hdmi.system_audio_control_on_power_on";
+
+    /**
+     * Property to record last state of system audio control before device powered off.
+     * <p>When {@link #PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON} is set to
+     * {@link #USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON}, restoring this state on power on.
+     * <p>State must be true or false. Default true.
+     */
+    static final String PROPERTY_LAST_SYSTEM_AUDIO_CONTROL =
+            "persist.sys.hdmi.last_system_audio_control";
+
+    /**
+     * Property to indicate if device supports ARC or not
+     * <p>Default is true.
+     */
+    static final String PROPERTY_ARC_SUPPORT =
+        "persist.sys.hdmi.property_arc_support";
+
+    /**
+     * Property to save the audio port to switch to when system audio control is on.
+     * <P>Audio system should switch to this port when cec active source is not its child in the tree
+     * or is not itself.
+     *
+     * <p>Default is ARC port.
+     */
+    static final String PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT =
+        "persist.sys.hdmi.property_sytem_audio_mode_audio_port";
+
+    /**
+     * Property to save the ARC port id on system audio device.
+     * <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";
+
+    /**
+     * Property to strip local audio of amplifier and use local speaker
+     * when TV does not support system audio mode.
+     *
+     * <p>This property applies to device with both audio system/playback types.
+     * <p>True means using local speaker when TV does not support system audio.
+     * <p>False means passing audio to TV. Default is true.
+     */
+    static final String PROPERTY_STRIP_AUDIO_TV_NO_SYSTEM_AUDIO =
+        "persist.sys.hdmi.property_strip_audio_tv_no_system_audio";
+
     static final int RECORDING_TYPE_DIGITAL_RF = 1;
     static final int RECORDING_TYPE_ANALOGUE_RF = 2;
     static final int RECORDING_TYPE_EXTERNAL_PHYSICAL_ADDRESS = 3;
@@ -243,5 +353,7 @@
     static final int DISABLED = 0;
     static final int ENABLED = 1;
 
-    private Constants() { /* cannot be instantiated */ }
+    private Constants() {
+        /* cannot be instantiated */
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
index 2c1a7d5..15ec486 100644
--- a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
+++ b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
@@ -85,7 +85,7 @@
 
     void processAllMessages() {
         // Use the copied buffer.
-        ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
+        ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<>(mBuffer);
         mBuffer.clear();
         for (HdmiCecMessage message : copiedBuffer) {
             mDevice.onMessage(message);
@@ -104,7 +104,7 @@
      *        are associated with
      */
     void processMessagesForDevice(int address) {
-        ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
+        ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<>(mBuffer);
         mBuffer.clear();
         HdmiLogger.debug("Checking message for address:" + address);
         for (HdmiCecMessage message : copiedBuffer) {
@@ -134,7 +134,7 @@
      * @param address logical address of the device to be the active source
      */
     void processActiveSource(int address) {
-        ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
+        ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<>(mBuffer);
         mBuffer.clear();
         for (HdmiCecMessage message : copiedBuffer) {
             if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE
diff --git a/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java b/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java
new file mode 100644
index 0000000..0495ff8
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java
@@ -0,0 +1,88 @@
+/*
+ * 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.hdmi;
+
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+
+import com.android.server.hdmi.HdmiCecLocalDeviceAudioSystem.TvSystemAudioModeSupportedCallback;
+
+/**
+ * Feature action that detects if TV supports system audio control.
+ */
+public class DetectTvSystemAudioModeSupportAction extends HdmiCecFeatureAction {
+
+    // State that waits for <Active Source> once send <Request Active Source>.
+    private static final int STATE_WAITING_FOR_FEATURE_ABORT = 1;
+
+    private TvSystemAudioModeSupportedCallback mCallback;
+    private int mState;
+
+    DetectTvSystemAudioModeSupportAction(HdmiCecLocalDevice source,
+            TvSystemAudioModeSupportedCallback callback) {
+        super(source);
+        mCallback = callback;
+    }
+
+    @Override
+    boolean start() {
+        mState = STATE_WAITING_FOR_FEATURE_ABORT;
+        addTimer(mState, HdmiConfig.TIMEOUT_MS);
+        sendSetSystemAudioMode();
+        return true;
+    }
+
+    @Override
+    boolean processCommand(HdmiCecMessage cmd) {
+        if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) {
+            if (mState != STATE_WAITING_FOR_FEATURE_ABORT) {
+                return false;
+            }
+            finishAction(false);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    void handleTimerEvent(int state) {
+        if (mState != state) {
+            return;
+        }
+
+        switch (mState) {
+            case STATE_WAITING_FOR_FEATURE_ABORT:
+                finishAction(true);
+                break;
+        }
+    }
+
+    protected void sendSetSystemAudioMode() {
+        sendCommand(
+                HdmiCecMessageBuilder.buildSetSystemAudioMode(getSourceAddress(),Constants.ADDR_TV,
+                        true),
+                result -> {
+                    if (result != SendMessageResult.SUCCESS) {
+                        finishAction(false);
+                    }
+                });
+    }
+
+    private void finishAction(boolean supported) {
+        mCallback.onResult(supported);
+        audioSystem().setTvSystemAudioModeSupport(supported);
+        finish();
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index db8dedb..b75e75f 100755
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -386,6 +386,7 @@
                 return;
             case STATE_WAITING_FOR_VENDOR_ID:
                 queryVendorId(address);
+                return;
             default:
                 return;
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index d26be57..11faa56 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -256,6 +256,10 @@
         return (HdmiCecLocalDeviceTv) mSource;
     }
 
+    protected final HdmiCecLocalDeviceAudioSystem audioSystem() {
+        return (HdmiCecLocalDeviceAudioSystem) mSource;
+    }
+
     protected final int getSourceAddress() {
         return mSource.getDeviceInfo().getLogicalAddress();
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 5681367..7860122 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -27,11 +27,9 @@
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
-
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
-
 import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -39,8 +37,8 @@
 import java.util.List;
 
 /**
- * Class that models a logical CEC device hosted in this system. Handles initialization,
- * CEC commands that call for actions customized per device type.
+ * Class that models a logical CEC device hosted in this system. Handles initialization, CEC
+ * commands that call for actions customized per device type.
  */
 abstract class HdmiCecLocalDevice {
     private static final String TAG = "HdmiCecLocalDevice";
@@ -69,47 +67,60 @@
         public ActiveSource() {
             invalidate();
         }
+
         public ActiveSource(int logical, int physical) {
             logicalAddress = logical;
             physicalAddress = physical;
         }
+
         public static ActiveSource of(ActiveSource source) {
             return new ActiveSource(source.logicalAddress, source.physicalAddress);
         }
+
         public static ActiveSource of(int logical, int physical) {
             return new ActiveSource(logical, physical);
         }
+
         public boolean isValid() {
             return HdmiUtils.isValidAddress(logicalAddress);
         }
+
         public void invalidate() {
             logicalAddress = Constants.ADDR_INVALID;
             physicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
         }
+
         public boolean equals(int logical, int physical) {
             return logicalAddress == logical && physicalAddress == physical;
         }
+
         @Override
         public boolean equals(Object obj) {
             if (obj instanceof ActiveSource) {
                 ActiveSource that = (ActiveSource) obj;
-                return that.logicalAddress == logicalAddress &&
-                       that.physicalAddress == physicalAddress;
+                return that.logicalAddress == logicalAddress
+                        && that.physicalAddress == physicalAddress;
             }
             return false;
         }
+
         @Override
         public int hashCode() {
             return logicalAddress * 29 + physicalAddress;
         }
+
         @Override
         public String toString() {
             StringBuffer s = new StringBuffer();
-            String logicalAddressString = (logicalAddress == Constants.ADDR_INVALID)
-                    ? "invalid" : String.format("0x%02x", logicalAddress);
+            String logicalAddressString =
+                    (logicalAddress == Constants.ADDR_INVALID)
+                            ? "invalid"
+                            : String.format("0x%02x", logicalAddress);
             s.append("(").append(logicalAddressString);
-            String physicalAddressString = (physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS)
-                    ? "invalid" : String.format("0x%04x", physicalAddress);
+            String physicalAddressString =
+                    (physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS)
+                            ? "invalid"
+                            : String.format("0x%04x", physicalAddress);
             s.append(", ").append(physicalAddressString).append(")");
             return s.toString();
         }
@@ -131,23 +142,24 @@
     // Note that access to this collection should happen in service thread.
     private final ArrayList<HdmiCecFeatureAction> mActions = new ArrayList<>();
 
-    private final Handler mHandler = new Handler () {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_DISABLE_DEVICE_TIMEOUT:
-                    handleDisableDeviceTimeout();
-                    break;
-                case MSG_USER_CONTROL_RELEASE_TIMEOUT:
-                    handleUserControlReleased();
-                    break;
-            }
-        }
-    };
+    private final Handler mHandler =
+            new Handler() {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case MSG_DISABLE_DEVICE_TIMEOUT:
+                            handleDisableDeviceTimeout();
+                            break;
+                        case MSG_USER_CONTROL_RELEASE_TIMEOUT:
+                            handleUserControlReleased();
+                            break;
+                    }
+                }
+            };
 
     /**
-     * A callback interface to get notified when all pending action is cleared.
-     * It can be called when timeout happened.
+     * A callback interface to get notified when all pending action is cleared. It can be called
+     * when timeout happened.
      */
     interface PendingActionClearedCallback {
         void onCleared(HdmiCecLocalDevice device);
@@ -165,12 +177,14 @@
     // Factory method that returns HdmiCecLocalDevice of corresponding type.
     static HdmiCecLocalDevice create(HdmiControlService service, int deviceType) {
         switch (deviceType) {
-        case HdmiDeviceInfo.DEVICE_TV:
-            return new HdmiCecLocalDeviceTv(service);
-        case HdmiDeviceInfo.DEVICE_PLAYBACK:
-            return new HdmiCecLocalDevicePlayback(service);
-        default:
-            return null;
+            case HdmiDeviceInfo.DEVICE_TV:
+                return new HdmiCecLocalDeviceTv(service);
+            case HdmiDeviceInfo.DEVICE_PLAYBACK:
+                return new HdmiCecLocalDevicePlayback(service);
+            case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
+                return new HdmiCecLocalDeviceAudioSystem(service);
+            default:
+                return null;
         }
     }
 
@@ -181,27 +195,22 @@
         mPendingActionClearedCallback = null;
     }
 
-    /**
-     * Called once a logical address of the local device is allocated.
-     */
+    /** Called once a logical address of the local device is allocated. */
     protected abstract void onAddressAllocated(int logicalAddress, int reason);
 
-    /**
-     * Get the preferred logical address from system properties.
-     */
+    /** Get the preferred logical address from system properties. */
     protected abstract int getPreferredAddress();
 
-    /**
-     * Set the preferred logical address to system properties.
-     */
+    /** Set the preferred logical address to system properties. */
     protected abstract void setPreferredAddress(int addr);
 
     /**
-     * Returns true if the TV input associated with the CEC device is ready
-     * to accept further processing such as input switching. This is used
-     * to buffer certain CEC commands and process it later if the input is not
-     * ready yet. For other types of local devices(non-TV), this method returns
-     * true by default to let the commands be processed right away.
+     * Returns true if the TV input associated with the CEC device is ready to accept further
+     * processing such as input switching.
+     *
+     * <p>This is used to buffer certain CEC commands and process it later if the input is not ready
+     * yet. For other types of local devices(non-TV), this method returns true by default to let the
+     * commands be processed right away.
      */
     protected boolean isInputReady(int deviceId) {
         return true;
@@ -209,7 +218,8 @@
 
     /**
      * Returns true if the local device allows the system to be put to standby.
-     * The default implementation returns true.
+     *
+     * <p>The default implementation returns true.
      */
     protected boolean canGoToStandby() {
         return true;
@@ -264,14 +274,28 @@
                 return handleRoutingChange(message);
             case Constants.MESSAGE_ROUTING_INFORMATION:
                 return handleRoutingInformation(message);
+            case Constants.MESSAGE_REQUEST_ARC_INITIATION:
+                return handleRequestArcInitiate(message);
+            case Constants.MESSAGE_REQUEST_ARC_TERMINATION:
+                return handleRequestArcTermination(message);
             case Constants.MESSAGE_INITIATE_ARC:
                 return handleInitiateArc(message);
             case Constants.MESSAGE_TERMINATE_ARC:
                 return handleTerminateArc(message);
+            case Constants.MESSAGE_REPORT_ARC_INITIATED:
+                return handleReportArcInitiate(message);
+            case Constants.MESSAGE_REPORT_ARC_TERMINATED:
+                return handleReportArcTermination(message);
+            case Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST:
+                return handleSystemAudioModeRequest(message);
             case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
                 return handleSetSystemAudioMode(message);
             case Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
                 return handleSystemAudioModeStatus(message);
+            case Constants.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS:
+                return handleGiveSystemAudioModeStatus(message);
+            case Constants.MESSAGE_GIVE_AUDIO_STATUS:
+                return handleGiveAudioStatus(message);
             case Constants.MESSAGE_REPORT_AUDIO_STATUS:
                 return handleReportAudioStatus(message);
             case Constants.MESSAGE_STANDBY:
@@ -308,6 +332,10 @@
                 return handleTimerStatus(message);
             case Constants.MESSAGE_RECORD_STATUS:
                 return handleRecordStatus(message);
+            case Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR:
+                return handleRequestShortAudioDescriptor(message);
+            case Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR:
+                return handleReportShortAudioDescriptor(message);
             default:
                 return false;
         }
@@ -331,8 +359,9 @@
         assertRunOnServiceThread();
 
         int physicalAddress = mService.getPhysicalAddress();
-        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
-                mAddress, physicalAddress, mDeviceType);
+        HdmiCecMessage cecMessage =
+                HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+                        mAddress, physicalAddress, mDeviceType);
         mService.sendCecCommand(cecMessage, callback);
         return true;
     }
@@ -341,8 +370,8 @@
     protected boolean handleGiveDeviceVendorId(@Nullable SendMessageCallback callback) {
         assertRunOnServiceThread();
         int vendorId = mService.getVendorId();
-        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
-                mAddress, vendorId);
+        HdmiCecMessage cecMessage =
+                HdmiCecMessageBuilder.buildDeviceVendorIdCommand(mAddress, vendorId);
         mService.sendCecCommand(cecMessage, callback);
         return true;
     }
@@ -351,8 +380,9 @@
     protected boolean handleGetCecVersion(HdmiCecMessage message) {
         assertRunOnServiceThread();
         int version = mService.getCecVersion();
-        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
-                message.getSource(), version);
+        HdmiCecMessage cecMessage =
+                HdmiCecMessageBuilder.buildCecVersion(
+                        message.getDestination(), message.getSource(), version);
         mService.sendCecCommand(cecMessage);
         return true;
     }
@@ -393,8 +423,9 @@
         assertRunOnServiceThread();
         // Note that since this method is called after logical address allocation is done,
         // mDeviceInfo should not be null.
-        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
-                mAddress, message.getSource(), mDeviceInfo.getDisplayName());
+        HdmiCecMessage cecMessage =
+                HdmiCecMessageBuilder.buildSetOsdNameCommand(
+                        mAddress, message.getSource(), mDeviceInfo.getDisplayName());
         if (cecMessage != null) {
             mService.sendCecCommand(cecMessage);
         } else {
@@ -419,10 +450,18 @@
         return false;
     }
 
+    protected boolean handleGiveSystemAudioModeStatus(HdmiCecMessage message) {
+        return false;
+    }
+
     protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
         return false;
     }
 
+    protected boolean handleSystemAudioModeRequest(HdmiCecMessage message) {
+        return false;
+    }
+
     protected boolean handleTerminateArc(HdmiCecMessage message) {
         return false;
     }
@@ -431,15 +470,44 @@
         return false;
     }
 
+    protected boolean handleRequestArcInitiate(HdmiCecMessage message) {
+        return false;
+    }
+
+    protected boolean handleRequestArcTermination(HdmiCecMessage message) {
+        return false;
+    }
+
+    protected boolean handleReportArcInitiate(HdmiCecMessage message) {
+        return false;
+    }
+
+    protected boolean handleReportArcTermination(HdmiCecMessage message) {
+        return false;
+    }
+
     protected boolean handleReportAudioStatus(HdmiCecMessage message) {
         return false;
     }
 
+    protected boolean handleGiveAudioStatus(HdmiCecMessage message) {
+        return false;
+    }
+
+    protected boolean handleRequestShortAudioDescriptor(HdmiCecMessage message) {
+        return false;
+    }
+
+    protected boolean handleReportShortAudioDescriptor(HdmiCecMessage message) {
+        return false;
+    }
+
     @ServiceThreadOnly
     protected boolean handleStandby(HdmiCecMessage message) {
         assertRunOnServiceThread();
         // Seq #12
-        if (mService.isControlEnabled() && !mService.isProhibitMode()
+        if (mService.isControlEnabled()
+                && !mService.isProhibitMode()
                 && mService.isPowerOnOrTransient()) {
             mService.standby();
             return true;
@@ -475,7 +543,8 @@
 
         if (keycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) {
             injectKeyEvent(downTime, KeyEvent.ACTION_DOWN, keycode, keyRepeatCount);
-            mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_USER_CONTROL_RELEASE_TIMEOUT),
+            mHandler.sendMessageDelayed(
+                    Message.obtain(mHandler, MSG_USER_CONTROL_RELEASE_TIMEOUT),
                     FOLLOWER_SAFETY_TIMEOUT);
             return true;
         }
@@ -497,13 +566,23 @@
     }
 
     static void injectKeyEvent(long time, int action, int keycode, int repeat) {
-        KeyEvent keyEvent = KeyEvent.obtain(time, time, action, keycode,
-                repeat, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
-                InputDevice.SOURCE_HDMI, null);
-        InputManager.getInstance().injectInputEvent(keyEvent,
-                InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+        KeyEvent keyEvent =
+                KeyEvent.obtain(
+                        time,
+                        time,
+                        action,
+                        keycode,
+                        repeat,
+                        0,
+                        KeyCharacterMap.VIRTUAL_KEYBOARD,
+                        0,
+                        KeyEvent.FLAG_FROM_SYSTEM,
+                        InputDevice.SOURCE_HDMI,
+                        null);
+        InputManager.getInstance()
+                .injectInputEvent(keyEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
         keyEvent.recycle();
-   }
+    }
 
     static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) {
         byte[] params = message.getParams();
@@ -516,8 +595,7 @@
     static boolean isPowerOffOrToggleCommand(HdmiCecMessage message) {
         byte[] params = message.getParams();
         return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
-                && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
-                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_OFF_FUNCTION
+                && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_OFF_FUNCTION
                         || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
     }
 
@@ -534,15 +612,17 @@
     }
 
     protected boolean handleGiveDevicePowerStatus(HdmiCecMessage message) {
-        mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPowerStatus(
-                mAddress, message.getSource(), mService.getPowerStatus()));
+        mService.sendCecCommand(
+                HdmiCecMessageBuilder.buildReportPowerStatus(
+                        mAddress, message.getSource(), mService.getPowerStatus()));
         return true;
     }
 
     protected boolean handleMenuRequest(HdmiCecMessage message) {
         // Always report menu active to receive Remote Control.
-        mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus(
-                mAddress, message.getSource(), Constants.MENU_STATE_ACTIVATED));
+        mService.sendCecCommand(
+                HdmiCecMessageBuilder.buildReportMenuStatus(
+                        mAddress, message.getSource(), Constants.MENU_STATE_ACTIVATED));
         return true;
     }
 
@@ -551,8 +631,12 @@
     }
 
     protected boolean handleVendorCommand(HdmiCecMessage message) {
-        if (!mService.invokeVendorCommandListenersOnReceived(mDeviceType, message.getSource(),
-                message.getDestination(), message.getParams(), false)) {
+        if (!mService.invokeVendorCommandListenersOnReceived(
+                mDeviceType,
+                message.getSource(),
+                message.getDestination(),
+                message.getParams(),
+                false)) {
             // Vendor command listener may not have been registered yet. Respond with
             // <Feature Abort> [NOT_IN_CORRECT_MODE] so that the sender can try again later.
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
@@ -564,12 +648,12 @@
         byte[] params = message.getParams();
         int vendorId = HdmiUtils.threeBytesToInt(params);
         if (vendorId == mService.getVendorId()) {
-            if (!mService.invokeVendorCommandListenersOnReceived(mDeviceType, message.getSource(),
-                    message.getDestination(), params, true)) {
+            if (!mService.invokeVendorCommandListenersOnReceived(
+                    mDeviceType, message.getSource(), message.getDestination(), params, true)) {
                 mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
             }
-        } else if (message.getDestination() != Constants.ADDR_BROADCAST &&
-                message.getSource() != Constants.ADDR_UNREGISTERED) {
+        } else if (message.getDestination() != Constants.ADDR_BROADCAST
+                && message.getSource() != Constants.ADDR_UNREGISTERED) {
             Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
         } else {
@@ -721,8 +805,8 @@
 
     // Remove all actions matched with the given Class type besides |exception|.
     @ServiceThreadOnly
-    <T extends HdmiCecFeatureAction> void removeActionExcept(final Class<T> clazz,
-            final HdmiCecFeatureAction exception) {
+    <T extends HdmiCecFeatureAction> void removeActionExcept(
+            final Class<T> clazz, final HdmiCecFeatureAction exception) {
         assertRunOnServiceThread();
         Iterator<HdmiCecFeatureAction> iter = mActions.iterator();
         while (iter.hasNext()) {
@@ -750,8 +834,7 @@
         }
     }
 
-    void setAutoDeviceOff(boolean enabled) {
-    }
+    void setAutoDeviceOff(boolean enabled) {}
 
     /**
      * Called when a hot-plug event issued.
@@ -759,8 +842,7 @@
      * @param portId id of port where a hot-plug event happened
      * @param connected whether to connected or not on the event
      */
-    void onHotplug(int portId, boolean connected) {
-    }
+    void onHotplug(int portId, boolean connected) {}
 
     final HdmiControlService getService() {
         return mService;
@@ -843,34 +925,34 @@
     /**
      * Called when the system goes to standby mode.
      *
-     * @param initiatedByCec true if this power sequence is initiated
-     *        by the reception the CEC messages like &lt;Standby&gt;
-     * @param standbyAction Intent action that drives the standby process,
-     *        either {@link HdmiControlService#STANDBY_SCREEN_OFF} or
-     *        {@link HdmiControlService#STANDBY_SHUTDOWN}
+     * @param initiatedByCec true if this power sequence is initiated by the reception the CEC
+     *     messages like &lt;Standby&gt;
+     * @param standbyAction Intent action that drives the standby process, either {@link
+     *     HdmiControlService#STANDBY_SCREEN_OFF} or {@link HdmiControlService#STANDBY_SHUTDOWN}
      */
     protected void onStandby(boolean initiatedByCec, int standbyAction) {}
 
     /**
-     * Disable device. {@code callback} is used to get notified when all pending
-     * actions are completed or timeout is issued.
+     * Disable device. {@code callback} is used to get notified when all pending actions are
+     * completed or timeout is issued.
      *
-     * @param initiatedByCec true if this sequence is initiated
-     *        by the reception the CEC messages like &lt;Standby&gt;
+     * @param initiatedByCec true if this sequence is initiated by the reception the CEC messages
+     *     like &lt;Standby&gt;
      * @param originalCallback callback interface to get notified when all pending actions are
-     *        cleared
+     *     cleared
      */
-    protected void disableDevice(boolean initiatedByCec,
-            final PendingActionClearedCallback originalCallback) {
-        mPendingActionClearedCallback = new PendingActionClearedCallback() {
-            @Override
-            public void onCleared(HdmiCecLocalDevice device) {
-                mHandler.removeMessages(MSG_DISABLE_DEVICE_TIMEOUT);
-                originalCallback.onCleared(device);
-            }
-        };
-        mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_DISABLE_DEVICE_TIMEOUT),
-                DEVICE_CLEANUP_TIMEOUT);
+    protected void disableDevice(
+            boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
+        mPendingActionClearedCallback =
+                new PendingActionClearedCallback() {
+                    @Override
+                    public void onCleared(HdmiCecLocalDevice device) {
+                        mHandler.removeMessages(MSG_DISABLE_DEVICE_TIMEOUT);
+                        originalCallback.onCleared(device);
+                    }
+                };
+        mHandler.sendMessageDelayed(
+                Message.obtain(mHandler, MSG_DISABLE_DEVICE_TIMEOUT), DEVICE_CLEANUP_TIMEOUT);
     }
 
     @ServiceThreadOnly
@@ -909,8 +991,14 @@
         int logicalAddress = findKeyReceiverAddress();
         if (logicalAddress == Constants.ADDR_INVALID || logicalAddress == mAddress) {
             // Don't send key event to invalid device or itself.
-            Slog.w(TAG, "Discard key event: " + keyCode + ", pressed:" + isPressed
-                    + ", receiverAddr=" + logicalAddress);
+            Slog.w(
+                    TAG,
+                    "Discard key event: "
+                            + keyCode
+                            + ", pressed:"
+                            + isPressed
+                            + ", receiverAddr="
+                            + logicalAddress);
         } else if (!action.isEmpty()) {
             action.get(0).processKeyEvent(keyCode, isPressed);
         } else if (isPressed) {
@@ -919,8 +1007,8 @@
     }
 
     /**
-     * Returns the logical address of the device which will receive key events via
-     * {@link #sendKeyEvent}.
+     * Returns the logical address of the device which will receive key events via {@link
+     * #sendKeyEvent}.
      *
      * @see #sendKeyEvent(int, boolean)
      */
@@ -930,15 +1018,13 @@
     }
 
     void sendUserControlPressedAndReleased(int targetAddress, int cecKeycode) {
-        mService.sendCecCommand(HdmiCecMessageBuilder.buildUserControlPressed(
-                mAddress, targetAddress, cecKeycode));
-        mService.sendCecCommand(HdmiCecMessageBuilder.buildUserControlReleased(
-                mAddress, targetAddress));
+        mService.sendCecCommand(
+                HdmiCecMessageBuilder.buildUserControlPressed(mAddress, targetAddress, cecKeycode));
+        mService.sendCecCommand(
+                HdmiCecMessageBuilder.buildUserControlReleased(mAddress, targetAddress));
     }
 
-    /**
-     * Dump internal status of HdmiCecLocalDevice object.
-     */
+    /** Dump internal status of HdmiCecLocalDevice object. */
     protected void dump(final IndentingPrintWriter pw) {
         pw.println("mDeviceType: " + mDeviceType);
         pw.println("mAddress: " + mAddress);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
new file mode 100644
index 0000000..14af15f
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -0,0 +1,437 @@
+/*
+ * 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.hdmi;
+
+import static com.android.server.hdmi.Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
+import static com.android.server.hdmi.Constants.PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
+import static com.android.server.hdmi.Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
+
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.os.SystemProperties;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
+
+/**
+ * Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android
+ * system.
+ */
+public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
+
+    private static final String TAG = "HdmiCecLocalDeviceAudioSystem";
+
+    // Whether System audio mode is activated or not.
+    // This becomes true only when all system audio sequences are finished.
+    @GuardedBy("mLock")
+    private boolean mSystemAudioActivated;
+
+    // Whether the System Audio Control feature is enabled or not. True by default.
+    @GuardedBy("mLock")
+    private boolean mSystemAudioControlFeatureEnabled;
+
+    private boolean mTvSystemAudioModeSupport;
+
+    // Whether ARC is available or not. "true" means that ARC is established between TV and
+    // AVR as audio receiver.
+    @ServiceThreadOnly
+    private boolean mArcEstablished = false;
+
+    protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
+        super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+        mSystemAudioControlFeatureEnabled = true;
+        // TODO(amyjojo) make System Audio Control controllable by users
+        /*mSystemAudioControlFeatureEnabled =
+        mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected void onStandby(boolean initiatedByCec, int standbyAction) {
+        assertRunOnServiceThread();
+        mTvSystemAudioModeSupport = false;
+        // Record the last state of System Audio Control before going to standby
+        synchronized (mLock) {
+            SystemProperties.set(
+                    Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL,
+                    mSystemAudioActivated ? "true" : "false");
+        }
+        terminateSystemAudioMode();
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected void onAddressAllocated(int logicalAddress, int reason) {
+        assertRunOnServiceThread();
+        mService.sendCecCommand(
+                HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+                        mAddress, mService.getPhysicalAddress(), mDeviceType));
+        mService.sendCecCommand(
+                HdmiCecMessageBuilder.buildDeviceVendorIdCommand(mAddress, mService.getVendorId()));
+        int systemAudioControlOnPowerOnProp =
+                SystemProperties.getInt(
+                        PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON,
+                        ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON);
+        boolean lastSystemAudioControlStatus =
+                SystemProperties.getBoolean(Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, true);
+        systemAudioControlOnPowerOn(systemAudioControlOnPowerOnProp, lastSystemAudioControlStatus);
+        startQueuedActions();
+    }
+
+    @VisibleForTesting
+    protected void systemAudioControlOnPowerOn(
+            int systemAudioOnPowerOnProp, boolean lastSystemAudioControlStatus) {
+        if ((systemAudioOnPowerOnProp == ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
+                || ((systemAudioOnPowerOnProp == USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
+                        && lastSystemAudioControlStatus)) {
+            addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
+        }
+    }
+
+    @ServiceThreadOnly
+    protected boolean handleActiveSource(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        int logicalAddress = message.getSource();
+        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+        ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
+        if (!mActiveSource.equals(activeSource)) {
+            setActiveSource(activeSource);
+        }
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected int getPreferredAddress() {
+        assertRunOnServiceThread();
+        return SystemProperties.getInt(
+                Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, Constants.ADDR_UNREGISTERED);
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected void setPreferredAddress(int addr) {
+        assertRunOnServiceThread();
+        SystemProperties.set(
+                Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, String.valueOf(addr));
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleReportAudioStatus(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        // TODO(amyjojo): implement report audio status handler
+        HdmiLogger.debug(TAG + "Stub handleReportAudioStatus");
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleInitiateArc(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        // TODO(amyjojo): implement initiate arc handler
+        HdmiLogger.debug(TAG + "Stub handleInitiateArc");
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleReportArcInitiate(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        // TODO(amyjojo): implement report arc initiate handler
+        HdmiLogger.debug(TAG + "Stub handleReportArcInitiate");
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleReportArcTermination(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        // TODO(amyjojo): implement report arc terminate handler
+        HdmiLogger.debug(TAG + "Stub handleReportArcTermination");
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleGiveAudioStatus(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+
+        reportAudioStatus(message);
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleGiveSystemAudioModeStatus(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        mService.sendCecCommand(
+                HdmiCecMessageBuilder.buildReportSystemAudioMode(
+                        mAddress, message.getSource(), mSystemAudioActivated));
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleRequestArcInitiate(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        if (!SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)) {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
+        } else if (!isDirectConnectToTv()) {
+            HdmiLogger.debug("AVR device is not directly connected with TV");
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
+        } else {
+            addAndStartAction(new ArcInitiationActionFromAvr(this));
+        }
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleRequestArcTermination(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        if (!SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)) {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
+        } else if (!isArcEnabled()) {
+            HdmiLogger.debug("ARC is not established between TV and AVR device");
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
+        } else {
+            addAndStartAction(new ArcTerminationActionFromAvr(this));
+        }
+        return true;
+    }
+
+    @ServiceThreadOnly
+    protected boolean handleRequestShortAudioDescriptor(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        // TODO(b/80297701): implement request short audio descriptor
+        HdmiLogger.debug(TAG + "Stub handleRequestShortAudioDescriptor");
+        mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleSystemAudioModeRequest(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        boolean systemAudioStatusOn = message.getParams().length != 0;
+        if (!setSystemAudioMode(systemAudioStatusOn)) {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+            return true;
+        }
+
+        mService.sendCecCommand(
+                HdmiCecMessageBuilder.buildSetSystemAudioMode(
+                        mAddress, Constants.ADDR_BROADCAST, systemAudioStatusOn));
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        if (!setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+        }
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        if (!setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+        }
+        return true;
+    }
+
+    @ServiceThreadOnly
+    void setArcStatus(boolean enabled) {
+        // TODO(shubang): add tests
+        assertRunOnServiceThread();
+
+        HdmiLogger.debug("Set Arc Status[old:%b new:%b]", mArcEstablished, enabled);
+        // 1. Enable/disable ARC circuit.
+        enableAudioReturnChannel(enabled);
+        // 2. Notify arc status to audio service.
+        notifyArcStatusToAudioService(enabled);
+        // 3. Update arc status;
+        mArcEstablished = enabled;
+    }
+
+    /**
+     * Switch hardware ARC circuit in the system.
+     */
+    @ServiceThreadOnly
+    private void enableAudioReturnChannel(boolean enabled) {
+        assertRunOnServiceThread();
+        mService.enableAudioReturnChannel(
+                SystemProperties.getInt(
+                        Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT, 0),
+                enabled);
+    }
+
+    private void notifyArcStatusToAudioService(boolean enabled) {
+        // Note that we don't set any name to ARC.
+        mService.getAudioManager().setWiredDeviceConnectionState(
+                AudioSystem.DEVICE_IN_HDMI,
+                enabled ? 1 : 0, "", "");
+    }
+
+    private void reportAudioStatus(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+
+        int volume = mService.getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC);
+        boolean mute = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
+        int maxVolume = mService.getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
+
+        mService.sendCecCommand(
+                HdmiCecMessageBuilder.buildReportAudioStatus(
+                        mAddress, message.getSource(), scaledVolume, mute));
+    }
+
+    protected boolean setSystemAudioMode(boolean newSystemAudioMode) {
+        if (!isSystemAudioControlFeatureEnabled()) {
+            HdmiLogger.debug(
+                    "Cannot turn "
+                            + (newSystemAudioMode ? "on" : "off")
+                            + "system audio mode "
+                            + "because the System Audio Control feature is disabled.");
+            return false;
+        }
+        HdmiLogger.debug(
+                "System Audio Mode change[old:%b new:%b]",
+                mSystemAudioActivated, newSystemAudioMode);
+        // Wake up device if System Audio Control is turned on but device is still on standby
+        if (newSystemAudioMode && mService.isPowerStandbyOrTransient()) {
+            mService.wakeUp();
+        }
+        int targetPhysicalAddress = getActiveSource().physicalAddress;
+        if (newSystemAudioMode &&
+            !isPhysicalAddressMeOrBelow(targetPhysicalAddress)) {
+            switchToAudioInput();
+        }
+        // TODO(b/80297700): Mute device when TV terminates the system audio control
+        updateAudioManagerForSystemAudio(newSystemAudioMode);
+        synchronized (mLock) {
+            if (mSystemAudioActivated != newSystemAudioMode) {
+                mSystemAudioActivated = newSystemAudioMode;
+                mService.announceSystemAudioModeChange(newSystemAudioMode);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Method to check if the target device belongs to the subtree of the current device or not.
+     * <p>Return true if it does or if the two devices share the same physical address.
+     *
+     * <p>This check assumes both device physical address and target address are valid.
+     * @param targetPhysicalAddress is the physical address of the target device
+     */
+    protected boolean isPhysicalAddressMeOrBelow(int targetPhysicalAddress) {
+        int myPhysicalAddress = mService.getPhysicalAddress();
+        int xor = targetPhysicalAddress ^ myPhysicalAddress;
+        // Return true if two addresses are the same
+        // or if they only differs for one byte, but not the first byte,
+        // and myPhysicalAddress is 0 after that byte
+        if (xor == 0
+            || ((xor & 0x0f00) == xor && (myPhysicalAddress & 0x0fff) == 0)
+            || ((xor & 0x00f0) == xor && (myPhysicalAddress & 0x00ff) == 0)
+            || ((xor & 0x000f) == xor && (myPhysicalAddress & 0x000f) == 0)) {
+            return true;
+        }
+        return false;
+    }
+
+    protected void switchToAudioInput() {
+        // TODO(b/111396634): switch input according to PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT
+    }
+
+    protected boolean isDirectConnectToTv() {
+        int myPhysicalAddress = mService.getPhysicalAddress();
+        return (myPhysicalAddress & Constants.ROUTING_PATH_TOP_MASK) == myPhysicalAddress;
+    }
+
+    private void updateAudioManagerForSystemAudio(boolean on) {
+        int device = mService.getAudioManager().setHdmiSystemAudioSupported(on);
+        HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
+    }
+
+    protected boolean isSystemAudioControlFeatureEnabled() {
+        synchronized (mLock) {
+            return mSystemAudioControlFeatureEnabled;
+        }
+    }
+
+    protected boolean isSystemAudioActivated() {
+        synchronized (mLock) {
+            return mSystemAudioActivated;
+        }
+    }
+
+    protected void terminateSystemAudioMode() {
+        // remove pending initiation actions
+        removeAction(SystemAudioInitiationActionFromAvr.class);
+        if (!isSystemAudioActivated()) {
+            return;
+        }
+
+        if (setSystemAudioMode(false)) {
+            // send <Set System Audio Mode> [“Off”]
+            mService.sendCecCommand(
+                    HdmiCecMessageBuilder.buildSetSystemAudioMode(
+                            mAddress, Constants.ADDR_BROADCAST, false));
+        }
+    }
+
+    /** Reports if System Audio Mode is supported by the connected TV */
+    interface TvSystemAudioModeSupportedCallback {
+
+        /** {@code supported} is true if the TV is connected and supports System Audio Mode. */
+        void onResult(boolean supported);
+    }
+
+    /**
+     * Queries the connected TV to detect if System Audio Mode is supported by the TV.
+     *
+     * <p>This query may take up to 2 seconds to complete.
+     *
+     * <p>The result of the query may be cached until Audio device type is put in standby or loses
+     * its physical address.
+     */
+    void queryTvSystemAudioModeSupport(TvSystemAudioModeSupportedCallback callback) {
+        if (!mTvSystemAudioModeSupport) {
+            addAndStartAction(new DetectTvSystemAudioModeSupportAction(this, callback));
+        } else {
+            callback.onResult(true);
+        }
+    }
+
+    void setTvSystemAudioModeSupport(boolean supported) {
+        mTvSystemAudioModeSupport = supported;
+    }
+
+    @VisibleForTesting
+    protected boolean isArcEnabled() {
+        synchronized (mLock) {
+            return mArcEstablished;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index fd0ff9d..c005615 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -16,9 +16,11 @@
 
 package com.android.server.hdmi;
 
+import android.annotation.Nullable;
 import libcore.util.EmptyArray;
 
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * A class to encapsulate HDMI-CEC message used for the devices connected via
@@ -44,6 +46,27 @@
         mParams = Arrays.copyOf(params, params.length);
     }
 
+    @Override
+    public boolean equals(@Nullable Object message) {
+        if (message instanceof HdmiCecMessage) {
+            HdmiCecMessage that = (HdmiCecMessage) message;
+            return this.mSource == that.getSource() &&
+                this.mDestination == that.getDestination() &&
+                this.mOpcode == that.getOpcode() &&
+                Arrays.equals(this.mParams, that.getParams());
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+            mSource,
+            mDestination,
+            mOpcode,
+            Arrays.hashCode(mParams));
+    }
+
     /**
      * Return the source address field of the message. It is the logical address
      * of the device which generated the message.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 9a51e3c..649a2da 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -18,6 +18,7 @@
 
 import java.io.UnsupportedEncodingException;
 import java.util.Arrays;
+import com.android.server.hdmi.Constants.AudioCodec;
 
 /**
  * A helper class to build {@link HdmiCecMessage} from various cec commands.
@@ -211,6 +212,28 @@
     }
 
     /**
+     * Build &lt;Initiate Arc&gt;
+     *
+     * @param src source address of command
+     * @param dest destination address of command
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildInitiateArc(int src, int dest) {
+        return buildCommand(src, dest, Constants.MESSAGE_INITIATE_ARC);
+    }
+
+    /**
+     * Build &lt;Terminate Arc&gt;
+     *
+     * @param src source address of command
+     * @param dest destination address of command
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildTerminateArc(int src, int dest) {
+        return buildCommand(src, dest, Constants.MESSAGE_TERMINATE_ARC);
+    }
+
+    /**
      * Build &lt;Request Arc Termination&gt;
      *
      * @param src source address of command
@@ -243,6 +266,25 @@
         return buildCommand(src, dest, Constants.MESSAGE_REPORT_ARC_TERMINATED);
     }
 
+
+    /**
+     * Build &lt;Request Short Audio Descriptor&gt; command.
+     *
+     * @param src source address of command
+     * @param dest destination address of command
+     * @param audioFormats the {@link AudioCodec}s desired
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildRequestShortAudioDescriptor(int src, int dest,
+            @AudioCodec int[] audioFormats) {
+        byte[] params = new byte[Math.min(audioFormats.length,4)] ;
+        for (int i = 0; i < params.length ; i++){
+            params[i] = (byte) (audioFormats[i] & 0xff);
+        }
+        return buildCommand(src, dest, Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR, params);
+    }
+
+
     /**
      * Build &lt;Text View On&gt; command.
      *
@@ -255,6 +297,16 @@
     }
 
     /**
+     * Build &lt;Request Active Source&gt; command.
+     *
+     * @param src source address of command
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildRequestActiveSource(int src) {
+        return buildCommand(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_REQUEST_ACTIVE_SOURCE);
+    }
+
+    /**
      * Build &lt;Active Source&gt; command.
      *
      * @param src source address of command
@@ -372,6 +424,34 @@
     }
 
     /**
+     * Build &lt;Set System Audio Mode&gt; command.
+     *
+     * @param src source address of command
+     * @param des destination address of command
+     * @param systemAudioStatus whether to set System Audio Mode on or off
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildSetSystemAudioMode(int src, int des, boolean systemAudioStatus) {
+        return buildCommandWithBooleanParam(src, des, Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
+            systemAudioStatus
+        );
+    }
+
+    /**
+     * Build &lt;Report System Audio Mode&gt; command.
+     *
+     * @param src source address of command
+     * @param des destination address of command
+     * @param systemAudioStatus whether System Audio Mode is on or off
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildReportSystemAudioMode(int src, int des, boolean systemAudioStatus) {
+        return buildCommandWithBooleanParam(src, des, Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
+            systemAudioStatus
+        );
+    }
+
+    /**
      * Build &lt;Give Audio Status&gt; command.
      *
      * @param src source address of command
@@ -383,6 +463,21 @@
     }
 
     /**
+     * Build &lt;Report Audio Status&gt; command.
+     *
+     * @param src source address of command
+     * @param dest destination address of command
+     * @param volume volume level of current device in param
+     * @param mute mute status of current device in param
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildReportAudioStatus(int src, int dest, int volume, boolean mute) {
+        byte status = (byte) ((byte) (mute ? 1 << 7 : 0) | ((byte) volume & 0x7F));
+        byte[] params = new byte[] { status };
+        return buildCommand(src, dest, Constants.MESSAGE_REPORT_AUDIO_STATUS, params);
+    }
+
+    /**
      * Build &lt;User Control Pressed&gt; command.
      *
      * @param src source address of command
@@ -592,6 +687,23 @@
         return new HdmiCecMessage(src, dest, opcode, params);
     }
 
+    /**
+     * Build a {@link HdmiCecMessage} with a boolean param and other given values.
+     *
+     * @param src source address of command
+     * @param des destination address of command
+     * @param opcode opcode for a message
+     * @param param boolean param for building the command
+     * @return newly created {@link HdmiCecMessage}
+     */
+    private static HdmiCecMessage buildCommandWithBooleanParam(int src, int des,
+        int opcode, boolean param) {
+        byte[] params = new byte[]{
+            param ? (byte) 0b1 : 0b0
+        };
+        return buildCommand(src, des, opcode, params);
+    }
+
     private static byte[] physicalAddressToParam(int physicalAddress) {
         return new byte[] {
                 (byte) ((physicalAddress >> 8) & 0xFF),
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index a1753e5..a2eb1c1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -96,7 +96,7 @@
 
     static final String PERMISSION = "android.permission.HDMI_CEC";
 
-    // The reason code to initiate intializeCec().
+    // The reason code to initiate initializeCec().
     static final int INITIATED_BY_ENABLE_CEC = 0;
     static final int INITIATED_BY_BOOT_UP = 1;
     static final int INITIATED_BY_SCREEN_ON = 2;
@@ -407,8 +407,9 @@
             Slog.i(TAG, "Device does not support HDMI-CEC.");
             return;
         }
-
-        mMhlController = HdmiMhlControllerStub.create(this);
+        if (mMhlController == null) {
+            mMhlController = HdmiMhlControllerStub.create(this);
+        }
         if (!mMhlController.isReady()) {
             Slog.i(TAG, "Device does not support MHL-control.");
         }
@@ -440,6 +441,11 @@
         mCecController = cecController;
     }
 
+    @VisibleForTesting
+    void setHdmiMhlController(HdmiMhlControllerStub hdmiMhlController) {
+        mMhlController = hdmiMhlController;
+    }
+
     @Override
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
@@ -598,7 +604,8 @@
     }
 
     @ServiceThreadOnly
-    private void allocateLogicalAddress(final ArrayList<HdmiCecLocalDevice> allocatingDevices,
+    @VisibleForTesting
+    protected void allocateLogicalAddress(final ArrayList<HdmiCecLocalDevice> allocatingDevices,
             final int initiatedBy) {
         assertRunOnServiceThread();
         mCecController.clearLogicalAddress();
@@ -665,7 +672,8 @@
     // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
     // keep them in one place.
     @ServiceThreadOnly
-    private void initPortInfo() {
+    @VisibleForTesting
+    protected void initPortInfo() {
         assertRunOnServiceThread();
         HdmiPortInfo[] cecPortInfo = null;
 
@@ -1709,6 +1717,38 @@
         }
 
         @Override
+        public void reportAudioStatus(final int deviceType, final int volume, final int maxVolume,
+                final boolean isMute) {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
+                    if (device == null) {
+                        Slog.w(TAG, "Local device not available");
+                        return;
+                    }
+                    if (audioSystem() == null) {
+                        Slog.w(TAG, "audio system is not available");
+                        return;
+                    }
+                    if (!audioSystem().isSystemAudioActivated()) {
+                        Slog.w(TAG, "audio system is not in system audio mode");
+                        return;
+                    }
+                    int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
+
+                    sendCecCommand(HdmiCecMessageBuilder
+                            .buildReportAudioStatus(
+                                    device.getDeviceInfo().getLogicalAddress(),
+                                    Constants.ADDR_TV,
+                                    scaledVolume,
+                                    isMute));
+                }
+            });
+        }
+
+        @Override
         protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) return;
             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
@@ -2001,6 +2041,10 @@
         return mLocalDevices.contains(HdmiDeviceInfo.DEVICE_TV);
     }
 
+    boolean isAudioSystemDevice() {
+        return mLocalDevices.contains(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+    }
+
     boolean isTvDeviceEnabled() {
         return isTvDevice() && tv() != null;
     }
@@ -2010,6 +2054,11 @@
                 mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
     }
 
+    public HdmiCecLocalDeviceAudioSystem audioSystem() {
+        return (HdmiCecLocalDeviceAudioSystem) mCecController.getLocalDevice(
+                HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+    }
+
     AudioManager getAudioManager() {
         return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
     }
@@ -2071,6 +2120,11 @@
         return mWakeUpMessageReceived;
     }
 
+    @VisibleForTesting
+    boolean isStandbyMessageReceived() {
+        return mStandbyMessageReceived;
+    }
+
     @ServiceThreadOnly
     private void onWakeUp() {
         assertRunOnServiceThread();
@@ -2090,17 +2144,23 @@
     }
 
     @ServiceThreadOnly
-    private void onStandby(final int standbyAction) {
+    @VisibleForTesting
+    protected void onStandby(final int standbyAction) {
         assertRunOnServiceThread();
         mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
         invokeVendorCommandListenersOnControlStateChanged(false,
                 HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY);
-        if (!canGoToStandby()) {
+
+        final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
+
+        if (!isStandbyMessageReceived() && !canGoToStandby()) {
             mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
+            for (HdmiCecLocalDevice device : devices) {
+                device.onStandby(mStandbyMessageReceived, standbyAction);
+            }
             return;
         }
 
-        final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
         disableDevices(new PendingActionClearedCallback() {
             @Override
             public void onCleared(HdmiCecLocalDevice device) {
@@ -2173,8 +2233,10 @@
             device.onStandby(mStandbyMessageReceived, standbyAction);
         }
         mStandbyMessageReceived = false;
-        mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, false);
-        mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
+        if (!isAudioSystemDevice()) {
+            mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, false);
+            mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
+        }
     }
 
     private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiLogger.java b/services/core/java/com/android/server/hdmi/HdmiLogger.java
index ebe52c0..2309293 100644
--- a/services/core/java/com/android/server/hdmi/HdmiLogger.java
+++ b/services/core/java/com/android/server/hdmi/HdmiLogger.java
@@ -17,7 +17,6 @@
 package com.android.server.hdmi;
 
 import android.annotation.Nullable;
-import android.os.Build;
 import android.os.SystemClock;
 import android.util.Pair;
 import android.util.Slog;
@@ -41,7 +40,7 @@
 final class HdmiLogger {
     private static final String TAG = "HDMI";
     // Logging duration for same error message.
-    private static final long ERROR_LOG_DURATTION_MILLIS = 20 * 1000;  // 20s
+    private static final long ERROR_LOG_DURATION_MILLIS = 20 * 1000;  // 20s
 
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -134,6 +133,6 @@
     }
 
     private static boolean shouldLogNow(@Nullable Pair<Long, Integer> timing, long curTime) {
-        return timing == null || curTime - timing.first > ERROR_LOG_DURATTION_MILLIS;
+        return timing == null || curTime - timing.first > ERROR_LOG_DURATION_MILLIS;
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 4ac3bba..2a8117f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -114,7 +114,7 @@
      *
      * @param logicalAddress the logical address to verify
      * @param deviceType the device type to check
-     * @throw IllegalArgumentException
+     * @throws IllegalArgumentException
      */
     static void verifyAddressType(int logicalAddress, int deviceType) {
         int actualDeviceType = getTypeFromAddress(logicalAddress);
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
index 6893012..a62d0b6 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
@@ -38,7 +38,7 @@
     private static final int INVALID_POWER_STATUS = POWER_STATUS_UNKNOWN - 1;
 
     // Monitoring interval (60s)
-    private static final int MONITIROING_INTERNAL_MS = 60000;
+    private static final int MONITORING_INTERNAL_MS = 60000;
 
     // Timeout once sending <Give Device Power Status>
     private static final int REPORT_POWER_STATUS_TIMEOUT_MS = 5000;
@@ -132,7 +132,7 @@
         mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
 
         // Add both timers, monitoring and timeout.
-        addTimer(STATE_WAIT_FOR_NEXT_MONITORING, MONITIROING_INTERNAL_MS);
+        addTimer(STATE_WAIT_FOR_NEXT_MONITORING, MONITORING_INTERNAL_MS);
         addTimer(STATE_WAIT_FOR_REPORT_POWER_STATUS, REPORT_POWER_STATUS_TIMEOUT_MS);
     }
 
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index 75a79cb..c70101c 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -35,7 +35,7 @@
      *
      * @param source {@link HdmiCecLocalDevice} instance
      * @param avrAddress address of AV receiver. It should be AUDIO_SYSTEM type
-     * @throw IllegalArugmentException if device type of sourceAddress and avrAddress
+     * @throws IllegalArgumentException if device type of sourceAddress and avrAddress
      *                      is invalid
      */
     RequestArcAction(HdmiCecLocalDevice source, int avrAddress) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index 449b208..a5477e8 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -60,7 +60,7 @@
      * @param avrAddress logical address of AVR device
      * @param targetStatus Whether to enable the system audio mode or not
      * @param callback callback interface to be notified when it's done
-     * @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid
+     * @throws IllegalArgumentException if device type of sourceAddress and avrAddress is invalid
      */
     SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus,
             IHdmiControlCallback callback) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
index eb5119b..6ddff91 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -32,7 +32,7 @@
      * @param avrAddress logical address of AVR device
      * @param targetStatus Whether to enable the system audio mode or not
      * @param callback callback interface to be notified when it's done
-     * @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid
+     * @throws IllegalArgumentException if device type of tvAddress and avrAddress is invalid
      */
     SystemAudioActionFromAvr(HdmiCecLocalDevice source, int avrAddress,
             boolean targetStatus, IHdmiControlCallback callback) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
index 02ecd13..5c0c272 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
@@ -32,7 +32,7 @@
      * @param avrAddress logical address of AVR device
      * @param targetStatus Whether to enable the system audio mode or not
      * @param callback callback interface to be notified when it's done
-     * @throw IllegalArugmentException if device type of tvAddress is invalid
+     * @throws IllegalArgumentException if device type of tvAddress is invalid
      */
     SystemAudioActionFromTv(HdmiCecLocalDevice sourceAddress, int avrAddress,
             boolean targetStatus, IHdmiControlCallback callback) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
new file mode 100644
index 0000000..d4932f9
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
@@ -0,0 +1,136 @@
+/*
+ * 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.hdmi;
+
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Feature action that handles System Audio Mode initiated by AVR devices.
+ */
+public class SystemAudioInitiationActionFromAvr extends HdmiCecFeatureAction {
+
+    // State that waits for <Active Source> once send <Request Active Source>.
+    private static final int STATE_WAITING_FOR_ACTIVE_SOURCE = 1;
+    @VisibleForTesting
+    static final int MAX_RETRY_COUNT = 5;
+
+    private int mSendRequestActiveSourceRetryCount = 0;
+    private int mSendSetSystemAudioModeRetryCount = 0;
+
+    SystemAudioInitiationActionFromAvr(HdmiCecLocalDevice source) {
+        super(source);
+    }
+
+    @Override
+    boolean start() {
+        if (audioSystem().mActiveSource.physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS) {
+            mState = STATE_WAITING_FOR_ACTIVE_SOURCE;
+            addTimer(mState, HdmiConfig.TIMEOUT_MS);
+            sendRequestActiveSource();
+        } else {
+            queryTvSystemAudioModeSupport();
+        }
+        return true;
+    }
+
+    @Override
+    boolean processCommand(HdmiCecMessage cmd) {
+        switch (cmd.getOpcode()) {
+            case Constants.MESSAGE_ACTIVE_SOURCE:
+                // received <Active Source>
+                if (mState != STATE_WAITING_FOR_ACTIVE_SOURCE) {
+                    return false;
+                }
+                mActionTimer.clearTimerMessage();
+                int physicalAddress = HdmiUtils.twoBytesToInt(cmd.getParams());
+                if (physicalAddress != getSourcePath()) {
+                    audioSystem().setActiveSource(cmd.getSource(), physicalAddress);
+                }
+                queryTvSystemAudioModeSupport();
+                return true;
+        }
+        return false;
+    }
+
+    @Override
+    void handleTimerEvent(int state) {
+        if (mState != state) {
+            return;
+        }
+
+        switch (mState) {
+            case STATE_WAITING_FOR_ACTIVE_SOURCE:
+                handleActiveSourceTimeout();
+                break;
+        }
+    }
+
+    protected void sendRequestActiveSource() {
+        sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()),
+                result -> {
+                    if (result != SendMessageResult.SUCCESS) {
+                        if (mSendRequestActiveSourceRetryCount < MAX_RETRY_COUNT) {
+                            mSendRequestActiveSourceRetryCount++;
+                            sendRequestActiveSource();
+                        } else {
+                            audioSystem().setSystemAudioMode(false);
+                            finish();
+                        }
+                    }
+                });
+    }
+
+    protected void sendSetSystemAudioMode(boolean on, int dest) {
+        sendCommand(HdmiCecMessageBuilder.buildSetSystemAudioMode(getSourceAddress(),
+                dest, on), result -> {
+                    if (result != SendMessageResult.SUCCESS) {
+                        if (mSendSetSystemAudioModeRetryCount < MAX_RETRY_COUNT) {
+                            mSendSetSystemAudioModeRetryCount++;
+                            sendSetSystemAudioMode(on, dest);
+                        } else {
+                            audioSystem().setSystemAudioMode(false);
+                            finish();
+                        }
+                    }
+                });
+    }
+
+    private void handleActiveSourceTimeout() {
+        HdmiLogger.debug("Cannot get active source.");
+        audioSystem().setSystemAudioMode(false);
+        finish();
+    }
+
+    private void queryTvSystemAudioModeSupport() {
+        audioSystem().queryTvSystemAudioModeSupport(
+                supported -> {
+                    if (supported) {
+                        if (audioSystem().setSystemAudioMode(true)) {
+                            sendSetSystemAudioMode(true, Constants.ADDR_BROADCAST);
+                        }
+                        finish();
+                    } else {
+                        audioSystem().setSystemAudioMode(false);
+                        finish();
+                    }
+                });
+    }
+
+    private void switchToRelevantInputForDeviceAt(int physicalAddress) {
+        // TODO(shubang): implement this method
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
index d41a36c..13f0f4ae 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
@@ -64,7 +64,7 @@
     }
 
     private void handleSendGiveAudioStatusFailure() {
-        // Inform to all application that the audio status (volumn, mute) of
+        // Inform to all application that the audio status (volume, mute) of
         // the audio amplifier is unknown.
         tv().setAudioStatus(false, Constants.UNKNOWN_VOLUME);
 
diff --git a/services/core/java/com/android/server/input/InputForwarder.java b/services/core/java/com/android/server/input/InputForwarder.java
index 38a1cd7..00af839 100644
--- a/services/core/java/com/android/server/input/InputForwarder.java
+++ b/services/core/java/com/android/server/input/InputForwarder.java
@@ -19,7 +19,6 @@
 import android.app.IInputForwarder;
 import android.hardware.input.InputManagerInternal;
 import android.view.InputEvent;
-import android.view.MotionEvent;
 
 import com.android.server.LocalServices;
 
@@ -40,9 +39,7 @@
 
     @Override
     public boolean forwardEvent(InputEvent event) {
-        if (event instanceof MotionEvent) {
-            ((MotionEvent) event).setDisplayId(mDisplayId);
-        }
+        event.setDisplayId(mDisplayId);
         return mInputManagerInternal.injectInputEvent(event, INJECT_INPUT_EVENT_MODE_ASYNC);
     }
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 9df9ba6..a5d7b27 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -212,7 +212,8 @@
             int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
             int policyFlags);
     private static native void nativeToggleCapsLock(long ptr, int deviceId);
-    private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles);
+    private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles,
+            int displayId);
     private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
     private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
     private static native void nativeSetFocusedApplication(long ptr,
@@ -1467,7 +1468,7 @@
     }
 
     public void setInputWindows(InputWindowHandle[] windowHandles,
-            InputWindowHandle focusedWindowHandle) {
+            InputWindowHandle focusedWindowHandle, int displayId) {
         final IWindow newFocusedWindow =
             focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
         if (mFocusedWindow != newFocusedWindow) {
@@ -1476,7 +1477,7 @@
                 setPointerCapture(false);
             }
         }
-        nativeSetInputWindows(mPtr, windowHandles);
+        nativeSetInputWindows(mPtr, windowHandles, displayId);
     }
 
     public void setFocusedApplication(InputApplicationHandle application) {
diff --git a/services/core/java/com/android/server/job/GrantedUriPermissions.java b/services/core/java/com/android/server/job/GrantedUriPermissions.java
index 8fecb8f..005b189 100644
--- a/services/core/java/com/android/server/job/GrantedUriPermissions.java
+++ b/services/core/java/com/android/server/job/GrantedUriPermissions.java
@@ -17,6 +17,7 @@
 package com.android.server.job;
 
 import android.app.IActivityManager;
+import android.app.UriGrantsManager;
 import android.content.ClipData;
 import android.content.ContentProvider;
 import android.content.Intent;
@@ -26,6 +27,8 @@
 import android.os.UserHandle;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
+import com.android.server.LocalServices;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -42,16 +45,14 @@
         mGrantFlags = grantFlags;
         mSourceUserId = UserHandle.getUserId(uid);
         mTag = tag;
-        mPermissionOwner = am.newUriPermissionOwner("job: " + tag);
+        mPermissionOwner = LocalServices
+                .getService(UriGrantsManagerInternal.class).newUriPermissionOwner("job: " + tag);
     }
 
     public void revoke(IActivityManager am) {
         for (int i = mUris.size()-1; i >= 0; i--) {
-            try {
-                am.revokeUriPermissionFromOwner(mPermissionOwner, mUris.get(i),
-                        mGrantFlags, mSourceUserId);
-            } catch (RemoteException e) {
-            }
+            LocalServices.getService(UriGrantsManagerInternal.class).revokeUriPermissionFromOwner(
+                    mPermissionOwner, mUris.get(i), mGrantFlags, mSourceUserId);
         }
         mUris.clear();
     }
@@ -119,8 +120,8 @@
             if (curPerms == null) {
                 curPerms = new GrantedUriPermissions(am, grantFlags, sourceUid, tag);
             }
-            am.grantUriPermissionFromOwner(curPerms.mPermissionOwner, sourceUid, targetPackage,
-                    uri, grantFlags, sourceUserId, targetUserId);
+            UriGrantsManager.getService().grantUriPermissionFromOwner(curPerms.mPermissionOwner,
+                    sourceUid, targetPackage, uri, grantFlags, sourceUserId, targetUserId);
             curPerms.mUris.add(uri);
         } catch (RemoteException e) {
             Slog.e("JobScheduler", "AM dead");
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 0b7c5b9..260633a 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -32,6 +32,7 @@
 import android.app.job.JobProtoEnums;
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
+import android.app.job.JobSnapshot;
 import android.app.job.JobWorkItem;
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
@@ -2724,6 +2725,55 @@
                 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
                         this, in, out, err, args, callback, resultReceiver);
         }
+
+        /**
+         * <b>For internal system user only!</b>
+         * Returns a list of all currently-executing jobs.
+         */
+        @Override
+        public List<JobInfo> getStartedJobs() {
+            final int uid = Binder.getCallingUid();
+            if (uid != Process.SYSTEM_UID) {
+                throw new SecurityException(
+                    "getStartedJobs() is system internal use only.");
+            }
+
+            final ArrayList<JobInfo> runningJobs;
+
+            synchronized (mLock) {
+                runningJobs = new ArrayList<>(mActiveServices.size());
+                for (JobServiceContext jsc : mActiveServices) {
+                    final JobStatus job = jsc.getRunningJobLocked();
+                    if (job != null) {
+                        runningJobs.add(job.getJob());
+                    }
+                }
+            }
+
+            return runningJobs;
+        }
+
+        /**
+         * <b>For internal system user only!</b>
+         * Returns a snapshot of the state of all jobs known to the system.
+         *
+         * <p class="note">This is a slow operation, so it should be called sparingly.
+         */
+        @Override
+        public List<JobSnapshot> getAllJobSnapshots() {
+            final int uid = Binder.getCallingUid();
+            if (uid != Process.SYSTEM_UID) {
+                throw new SecurityException(
+                    "getAllJobSnapshots() is system internal use only.");
+            }
+            synchronized (mLock) {
+                final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size());
+                mJobs.forEachJob((job) -> snapshots.add(
+                        new JobSnapshot(job.getJob(), job.getSatisfiedConstraintFlags(),
+                                isReadyToBeExecutedLocked(job))));
+                return snapshots;
+            }
+        }
     };
 
     // Shell command infrastructure: run the given job immediately
diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java
index 644f2c4..e3c311f 100644
--- a/services/core/java/com/android/server/job/controllers/IdleController.java
+++ b/services/core/java/com/android/server/job/controllers/IdleController.java
@@ -16,41 +16,33 @@
 
 package com.android.server.job.controllers;
 
-import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-
-import android.app.AlarmManager;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.os.UserHandle;
 import android.util.ArraySet;
-import android.util.Log;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.am.ActivityManagerService;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateControllerProto;
+import com.android.server.job.controllers.idle.CarIdlenessTracker;
+import com.android.server.job.controllers.idle.DeviceIdlenessTracker;
+import com.android.server.job.controllers.idle.IdlenessListener;
+import com.android.server.job.controllers.idle.IdlenessTracker;
 
 import java.util.function.Predicate;
 
-public final class IdleController extends StateController {
-    private static final String TAG = "JobScheduler.Idle";
-    private static final boolean DEBUG = JobSchedulerService.DEBUG
-            || Log.isLoggable(TAG, Log.DEBUG);
-
+public final class IdleController extends StateController implements IdlenessListener {
+    private static final String TAG = "JobScheduler.IdleController";
     // Policy: we decide that we're "idle" if the device has been unused /
     // screen off or dreaming or wireless charging dock idle for at least this long
-    private long mInactivityIdleThreshold;
-    private long mIdleWindowSlop;
     final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
     IdlenessTracker mIdleTracker;
 
     public IdleController(JobSchedulerService service) {
         super(service);
-        initIdleStateTracking();
+        initIdleStateTracking(mContext);
     }
 
     /**
@@ -74,9 +66,10 @@
     }
 
     /**
-     * Interaction with the task manager service
+     * State-change notifications from the idleness tracker
      */
-    void reportNewIdleState(boolean isIdle) {
+    @Override
+    public void reportNewIdleState(boolean isIdle) {
         synchronized (mLock) {
             for (int i = mTrackedTasks.size()-1; i >= 0; i--) {
                 mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(isIdle);
@@ -89,141 +82,22 @@
      * Idle state tracking, and messaging with the task manager when
      * significant state changes occur
      */
-    private void initIdleStateTracking() {
-        mInactivityIdleThreshold = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
-        mIdleWindowSlop = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
-        mIdleTracker = new IdlenessTracker();
-        mIdleTracker.startTracking();
-    }
-
-    final class IdlenessTracker extends BroadcastReceiver {
-        private AlarmManager mAlarm;
-
-        // After construction, mutations of idle/screen-on state will only happen
-        // on the main looper thread, either in onReceive() or in an alarm callback.
-        private boolean mIdle;
-        private boolean mScreenOn;
-        private boolean mDockIdle;
-
-        private AlarmManager.OnAlarmListener mIdleAlarmListener = () -> {
-            handleIdleTrigger();
-        };
-
-        public IdlenessTracker() {
-            mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-
-            // At boot we presume that the user has just "interacted" with the
-            // device in some meaningful way.
-            mIdle = false;
-            mScreenOn = true;
-            mDockIdle = false;
+    private void initIdleStateTracking(Context ctx) {
+        final boolean isCar = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE);
+        if (isCar) {
+            mIdleTracker = new CarIdlenessTracker();
+        } else {
+            mIdleTracker = new DeviceIdlenessTracker();
         }
-
-        public boolean isIdle() {
-            return mIdle;
-        }
-
-        public void startTracking() {
-            IntentFilter filter = new IntentFilter();
-
-            // Screen state
-            filter.addAction(Intent.ACTION_SCREEN_ON);
-            filter.addAction(Intent.ACTION_SCREEN_OFF);
-
-            // Dreaming state
-            filter.addAction(Intent.ACTION_DREAMING_STARTED);
-            filter.addAction(Intent.ACTION_DREAMING_STOPPED);
-
-            // Debugging/instrumentation
-            filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
-
-            // Wireless charging dock state
-            filter.addAction(Intent.ACTION_DOCK_IDLE);
-            filter.addAction(Intent.ACTION_DOCK_ACTIVE);
-
-            mContext.registerReceiver(this, filter);
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (action.equals(Intent.ACTION_SCREEN_ON)
-                    || action.equals(Intent.ACTION_DREAMING_STOPPED)
-                    || action.equals(Intent.ACTION_DOCK_ACTIVE)) {
-                if (action.equals(Intent.ACTION_DOCK_ACTIVE)) {
-                    if (!mScreenOn) {
-                        // Ignore this intent during screen off
-                        return;
-                    } else {
-                        mDockIdle = false;
-                    }
-                } else {
-                    mScreenOn = true;
-                    mDockIdle = false;
-                }
-                if (DEBUG) {
-                    Slog.v(TAG,"exiting idle : " + action);
-                }
-                //cancel the alarm
-                mAlarm.cancel(mIdleAlarmListener);
-                if (mIdle) {
-                // possible transition to not-idle
-                    mIdle = false;
-                    reportNewIdleState(mIdle);
-                }
-            } else if (action.equals(Intent.ACTION_SCREEN_OFF)
-                    || action.equals(Intent.ACTION_DREAMING_STARTED)
-                    || action.equals(Intent.ACTION_DOCK_IDLE)) {
-                // when the screen goes off or dreaming starts or wireless charging dock in idle,
-                // we schedule the alarm that will tell us when we have decided the device is
-                // truly idle.
-                if (action.equals(Intent.ACTION_DOCK_IDLE)) {
-                    if (!mScreenOn) {
-                        // Ignore this intent during screen off
-                        return;
-                    } else {
-                        mDockIdle = true;
-                    }
-                } else {
-                    mScreenOn = false;
-                    mDockIdle = false;
-                }
-                final long nowElapsed = sElapsedRealtimeClock.millis();
-                final long when = nowElapsed + mInactivityIdleThreshold;
-                if (DEBUG) {
-                    Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
-                            + when);
-                }
-                mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                        when, mIdleWindowSlop, "JS idleness", mIdleAlarmListener, null);
-            } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
-                handleIdleTrigger();
-            }
-        }
-
-        private void handleIdleTrigger() {
-            // idle time starts now. Do not set mIdle if screen is on.
-            if (!mIdle && (!mScreenOn || mDockIdle)) {
-                if (DEBUG) {
-                    Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis());
-                }
-                mIdle = true;
-                reportNewIdleState(mIdle);
-            } else {
-                if (DEBUG) {
-                    Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle="
-                            + mIdle + " screen=" + mScreenOn);
-                }
-            }
-        }
+        mIdleTracker.startTracking(ctx, this);
     }
 
     @Override
     public void dumpControllerStateLocked(IndentingPrintWriter pw,
             Predicate<JobStatus> predicate) {
         pw.println("Currently idle: " + mIdleTracker.isIdle());
+        pw.println("Idleness tracker:"); mIdleTracker.dump(pw);
         pw.println();
 
         for (int i = 0; i < mTrackedTasks.size(); i++) {
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index a1e066e..3f8941d 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -694,6 +694,10 @@
         mInternalFlags |= flags;
     }
 
+    public int getSatisfiedConstraintFlags() {
+        return satisfiedConstraints;
+    }
+
     public void maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker) {
         // Jobs with time constraints shouldn't be exempted.
         if (job.hasEarlyConstraint() || job.hasLateConstraint()) {
diff --git a/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java b/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
new file mode 100644
index 0000000..596a4c0
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
@@ -0,0 +1,155 @@
+/*
+ * 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.idle;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.job.JobSchedulerService;
+
+import java.io.PrintWriter;
+
+public final class CarIdlenessTracker extends BroadcastReceiver implements IdlenessTracker {
+    private static final String TAG = "JobScheduler.CarIdlenessTracker";
+    private static final boolean DEBUG = JobSchedulerService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
+
+    public static final String ACTION_GARAGE_MODE_ON =
+            "com.android.server.jobscheduler.GARAGE_MODE_ON";
+    public static final String ACTION_GARAGE_MODE_OFF =
+            "com.android.server.jobscheduler.GARAGE_MODE_OFF";
+
+    public static final String ACTION_FORCE_IDLE = "com.android.server.jobscheduler.FORCE_IDLE";
+    public static final String ACTION_UNFORCE_IDLE = "com.android.server.jobscheduler.UNFORCE_IDLE";
+
+    // After construction, mutations of idle/screen-on state will only happen
+    // on the main looper thread, either in onReceive() or in an alarm callback.
+    private boolean mIdle;
+    private boolean mGarageModeOn;
+    private boolean mForced;
+    private IdlenessListener mIdleListener;
+
+    public CarIdlenessTracker() {
+        // At boot we presume that the user has just "interacted" with the
+        // device in some meaningful way.
+        mIdle = false;
+        mGarageModeOn = false;
+        mForced = false;
+    }
+
+    @Override
+    public boolean isIdle() {
+        return mIdle;
+    }
+
+    @Override
+    public void startTracking(Context context, IdlenessListener listener) {
+        mIdleListener = listener;
+
+        IntentFilter filter = new IntentFilter();
+
+        // State of GarageMode
+        filter.addAction(ACTION_GARAGE_MODE_ON);
+        filter.addAction(ACTION_GARAGE_MODE_OFF);
+
+        // Debugging/instrumentation
+        filter.addAction(ACTION_FORCE_IDLE);
+        filter.addAction(ACTION_UNFORCE_IDLE);
+        filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
+
+        context.registerReceiver(this, filter);
+    }
+
+    @Override
+    public void dump(PrintWriter pw) {
+        pw.print("  mIdle: "); pw.println(mIdle);
+        pw.print("  mGarageModeOn: "); pw.println(mGarageModeOn);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        logIfDebug("Received action: " + action);
+
+        // Check for forced actions
+        if (action.equals(ACTION_FORCE_IDLE)) {
+            logIfDebug("Forcing idle...");
+            setForceIdleState(true);
+        } else if (action.equals(ACTION_UNFORCE_IDLE)) {
+            logIfDebug("Unforcing idle...");
+            setForceIdleState(false);
+        } else if (action.equals(ACTION_GARAGE_MODE_ON)) {
+            logIfDebug("GarageMode is on...");
+            mGarageModeOn = true;
+            updateIdlenessState();
+        } else if (action.equals(ACTION_GARAGE_MODE_OFF)) {
+            logIfDebug("GarageMode is off...");
+            mGarageModeOn = false;
+            updateIdlenessState();
+        } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
+            if (!mGarageModeOn) {
+                logIfDebug("Idle trigger fired...");
+                triggerIdlenessOnce();
+            } else {
+                logIfDebug("TRIGGER_IDLE received but not changing state; idle="
+                        + mIdle + " screen=" + mGarageModeOn);
+            }
+        }
+    }
+
+    private void setForceIdleState(boolean forced) {
+        mForced = forced;
+        updateIdlenessState();
+    }
+
+    private void updateIdlenessState() {
+        final boolean newState = (mForced || mGarageModeOn);
+        if (mIdle != newState) {
+            // State of idleness changed. Notifying idleness controller
+            logIfDebug("Device idleness changed. New idle=" + newState);
+            mIdle = newState;
+            mIdleListener.reportNewIdleState(mIdle);
+        } else {
+            // Nothing changed, device idleness is in the same state as new state
+            logIfDebug("Device idleness is the same. Current idle=" + newState);
+        }
+    }
+
+    private void triggerIdlenessOnce() {
+        // This is simply triggering idleness once until some constraint will switch it back off
+        if (mIdle) {
+            // Already in idle state. Nothing to do
+            logIfDebug("Device is already idle");
+        } else {
+            // Going idle once
+            logIfDebug("Device is going idle once");
+            mIdle = true;
+            mIdleListener.reportNewIdleState(mIdle);
+        }
+    }
+
+    private void logIfDebug(String msg) {
+        if (DEBUG) {
+            Slog.v(TAG, msg);
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java b/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
new file mode 100644
index 0000000..a85bd40
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
@@ -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
+ */
+
+package com.android.server.job.controllers.idle;
+
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.job.JobSchedulerService;
+
+import java.io.PrintWriter;
+
+public final class DeviceIdlenessTracker extends BroadcastReceiver implements IdlenessTracker {
+    private static final String TAG = "JobScheduler.DeviceIdlenessTracker";
+    private static final boolean DEBUG = JobSchedulerService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
+
+    private AlarmManager mAlarm;
+
+    // After construction, mutations of idle/screen-on state will only happen
+    // on the main looper thread, either in onReceive() or in an alarm callback.
+    private long mInactivityIdleThreshold;
+    private long mIdleWindowSlop;
+    private boolean mIdle;
+    private boolean mScreenOn;
+    private boolean mDockIdle;
+    private IdlenessListener mIdleListener;
+
+    private AlarmManager.OnAlarmListener mIdleAlarmListener = () -> {
+        handleIdleTrigger();
+    };
+
+    public DeviceIdlenessTracker() {
+        // At boot we presume that the user has just "interacted" with the
+        // device in some meaningful way.
+        mIdle = false;
+        mScreenOn = true;
+        mDockIdle = false;
+    }
+
+    @Override
+    public boolean isIdle() {
+        return mIdle;
+    }
+
+    @Override
+    public void startTracking(Context context, IdlenessListener listener) {
+        mIdleListener = listener;
+        mInactivityIdleThreshold = context.getResources().getInteger(
+                com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
+        mIdleWindowSlop = context.getResources().getInteger(
+                com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
+        mAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+
+        IntentFilter filter = new IntentFilter();
+
+        // Screen state
+        filter.addAction(Intent.ACTION_SCREEN_ON);
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+
+        // Dreaming state
+        filter.addAction(Intent.ACTION_DREAMING_STARTED);
+        filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+
+        // Debugging/instrumentation
+        filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
+
+        // Wireless charging dock state
+        filter.addAction(Intent.ACTION_DOCK_IDLE);
+        filter.addAction(Intent.ACTION_DOCK_ACTIVE);
+
+        context.registerReceiver(this, filter);
+    }
+
+    @Override
+    public void dump(PrintWriter pw) {
+        pw.print("  mIdle: "); pw.println(mIdle);
+        pw.print("  mScreenOn: "); pw.println(mScreenOn);
+        pw.print("  mDockIdle: "); pw.println(mDockIdle);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if (action.equals(Intent.ACTION_SCREEN_ON)
+                || action.equals(Intent.ACTION_DREAMING_STOPPED)
+                || action.equals(Intent.ACTION_DOCK_ACTIVE)) {
+            if (action.equals(Intent.ACTION_DOCK_ACTIVE)) {
+                if (!mScreenOn) {
+                    // Ignore this intent during screen off
+                    return;
+                } else {
+                    mDockIdle = false;
+                }
+            } else {
+                mScreenOn = true;
+                mDockIdle = false;
+            }
+            if (DEBUG) {
+                Slog.v(TAG,"exiting idle : " + action);
+            }
+            //cancel the alarm
+            mAlarm.cancel(mIdleAlarmListener);
+            if (mIdle) {
+            // possible transition to not-idle
+                mIdle = false;
+                mIdleListener.reportNewIdleState(mIdle);
+            }
+        } else if (action.equals(Intent.ACTION_SCREEN_OFF)
+                || action.equals(Intent.ACTION_DREAMING_STARTED)
+                || action.equals(Intent.ACTION_DOCK_IDLE)) {
+            // when the screen goes off or dreaming starts or wireless charging dock in idle,
+            // we schedule the alarm that will tell us when we have decided the device is
+            // truly idle.
+            if (action.equals(Intent.ACTION_DOCK_IDLE)) {
+                if (!mScreenOn) {
+                    // Ignore this intent during screen off
+                    return;
+                } else {
+                    mDockIdle = true;
+                }
+            } else {
+                mScreenOn = false;
+                mDockIdle = false;
+            }
+            final long nowElapsed = sElapsedRealtimeClock.millis();
+            final long when = nowElapsed + mInactivityIdleThreshold;
+            if (DEBUG) {
+                Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
+                        + when);
+            }
+            mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    when, mIdleWindowSlop, "JS idleness", mIdleAlarmListener, null);
+        } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
+            handleIdleTrigger();
+        }
+    }
+
+    private void handleIdleTrigger() {
+        // idle time starts now. Do not set mIdle if screen is on.
+        if (!mIdle && (!mScreenOn || mDockIdle)) {
+            if (DEBUG) {
+                Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis());
+            }
+            mIdle = true;
+            mIdleListener.reportNewIdleState(mIdle);
+        } else {
+            if (DEBUG) {
+                Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle="
+                        + mIdle + " screen=" + mScreenOn);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java b/services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java
new file mode 100644
index 0000000..7ffd7cd
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/idle/IdlenessListener.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.job.controllers.idle;
+
+/**
+ * Interface through which an IdlenessTracker informs the job scheduler of
+ * changes in the device's inactivity state.
+ */
+public interface IdlenessListener {
+    /**
+     * Tell the job scheduler that the device's idle state has changed.
+     *
+     * @param deviceIsIdle {@code true} to indicate that the device is now considered
+     * to be idle; {@code false} to indicate that the device is now being interacted with,
+     * so jobs with idle constraints should not be run.
+     */
+    void reportNewIdleState(boolean deviceIsIdle);
+}
diff --git a/services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java b/services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java
new file mode 100644
index 0000000..09f01c2
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.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 com.android.server.job.controllers.idle;
+
+import android.content.Context;
+
+import java.io.PrintWriter;
+
+public interface IdlenessTracker {
+    /**
+     * One-time initialization:  this method is called once, after construction of
+     * the IdlenessTracker instance.  This is when the tracker should actually begin
+     * monitoring whatever signals it consumes in deciding when the device is in a
+     * non-interacting state.  When the idle state changes thereafter, the given
+     * listener must be called to report the new state.
+     */
+    void startTracking(Context context, IdlenessListener listener);
+
+    /**
+     * Report whether the device is currently considered "idle" for purposes of
+     * running scheduled jobs with idleness constraints.
+     *
+     * @return {@code true} if the job scheduler should consider idleness
+     * constraints to be currently satisfied; {@code false} otherwise.
+     */
+    boolean isIdle();
+
+    /**
+     * Dump useful information about tracked idleness-related state in plaintext.
+     */
+    void dump(PrintWriter pw);
+}
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index 74930c8..4243f02 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -45,7 +45,7 @@
     /*
      * Local flag to enable debug logging.
      */
-    private static final boolean DEBUG_LOG_ENABLED = true;
+    private static final boolean DEBUG_LOG_ENABLED = false;
 
     /*
      * The context of the service.
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index dc95d41..27509de 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -77,6 +77,11 @@
 
     private static final int OS_APP_INSTANCE = -1;
 
+    /*
+     * Local flag to enable debug logging.
+     */
+    private static final boolean DEBUG_LOG_ENABLED = false;
+
     private final Context mContext;
 
     private final Map<Integer, ContextHubInfo> mContextHubIdToInfoMap;
@@ -779,12 +784,16 @@
 
         int msgVersion = 0;
         int callbacksCount = mCallbacksList.beginBroadcast();
-        Log.d(TAG, "Sending message " + msgType + " version " + msgVersion + " from hubHandle " +
-                contextHubHandle + ", appInstance " + appInstance + ", callBackCount "
-                + callbacksCount);
+        if (DEBUG_LOG_ENABLED) {
+            Log.v(TAG, "Sending message " + msgType + " version " + msgVersion + " from hubHandle "
+                    + contextHubHandle + ", appInstance " + appInstance + ", callBackCount "
+                    + callbacksCount);
+        }
 
         if (callbacksCount < 1) {
-            Log.v(TAG, "No message callbacks registered.");
+            if (DEBUG_LOG_ENABLED) {
+                Log.v(TAG, "No message callbacks registered.");
+            }
             return 0;
         }
 
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index d515ce6..4edd48f 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -68,7 +68,6 @@
 import android.os.WorkSource.WorkChain;
 import android.provider.Settings;
 import android.provider.Telephony.Carriers;
-import android.provider.Telephony.Sms.Intents;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
@@ -254,7 +253,7 @@
     // 1 second, or 1 Hz frequency.
     private static final long LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS = 1000;
     // Default update duration in milliseconds for REQUEST_LOCATION.
-    private static final long LOCATION_UPDATE_DURATION_MILLIS = 0;
+    private static final long LOCATION_UPDATE_DURATION_MILLIS = 10 * 1000;
 
     /** simpler wrapper for ProviderRequest + Worksource */
     private static class GpsRequest {
@@ -2402,28 +2401,7 @@
                     .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
 
             // listen for events
-            IntentFilter intentFilter;
-            if (native_is_agps_ril_supported()) {
-                intentFilter = new IntentFilter();
-                intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
-                intentFilter.addDataScheme("sms");
-                intentFilter.addDataAuthority("localhost", "7275");
-                mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
-
-                intentFilter = new IntentFilter();
-                intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
-                try {
-                    intentFilter.addDataType("application/vnd.omaloc-supl-init");
-                } catch (IntentFilter.MalformedMimeTypeException e) {
-                    Log.w(TAG, "Malformed SUPL init mime type");
-                }
-                mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
-            } else if (DEBUG) {
-                Log.d(TAG, "Skipped registration for SMS/WAP-PUSH messages because AGPS Ril in GPS"
-                        + " HAL is not supported");
-            }
-
-            intentFilter = new IntentFilter();
+            IntentFilter intentFilter = new IntentFilter();
             intentFilter.addAction(ALARM_WAKEUP);
             intentFilter.addAction(ALARM_TIMEOUT);
             intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 36b04fc..adfa8d5 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -955,12 +955,18 @@
     @GuardedBy("mSeparateChallengeLock")
     private void setSeparateProfileChallengeEnabledLocked(@UserIdInt int userId, boolean enabled,
             String managedUserPassword) {
+        final boolean old = getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
         setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId);
-        if (enabled) {
-            mStorage.removeChildProfileLock(userId);
-            removeKeystoreProfileKey(userId);
-        } else {
-            tieManagedProfileLockIfNecessary(userId, managedUserPassword);
+        try {
+            if (enabled) {
+                mStorage.removeChildProfileLock(userId);
+                removeKeystoreProfileKey(userId);
+            } else {
+                tieManagedProfileLockIfNecessary(userId, managedUserPassword);
+            }
+        } catch (IllegalStateException e) {
+            setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, old, userId);
+            throw e;
         }
     }
 
@@ -2006,7 +2012,7 @@
         enforceShell();
         final long origId = Binder.clearCallingIdentity();
         try {
-            (new LockSettingsShellCommand(mContext, new LockPatternUtils(mContext))).exec(
+            (new LockSettingsShellCommand(new LockPatternUtils(mContext))).exec(
                     this, in, out, err, args, callback, resultReceiver);
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 4d2cf32..07f23ce 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -21,13 +21,13 @@
 import static com.android.internal.widget.LockPatternUtils.stringToPattern;
 
 import android.app.ActivityManager;
-import android.content.Context;
-import android.os.RemoteException;
 import android.os.ShellCommand;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
 
+import java.io.PrintWriter;
+
 class LockSettingsShellCommand extends ShellCommand {
 
     private static final String COMMAND_SET_PATTERN = "set-pattern";
@@ -38,20 +38,22 @@
     private static final String COMMAND_SET_DISABLED = "set-disabled";
     private static final String COMMAND_VERIFY = "verify";
     private static final String COMMAND_GET_DISABLED = "get-disabled";
+    private static final String COMMAND_HELP = "help";
 
     private int mCurrentUserId;
     private final LockPatternUtils mLockPatternUtils;
-    private final Context mContext;
     private String mOld = "";
     private String mNew = "";
 
-    LockSettingsShellCommand(Context context, LockPatternUtils lockPatternUtils) {
-        mContext = context;
+    LockSettingsShellCommand(LockPatternUtils lockPatternUtils) {
         mLockPatternUtils = lockPatternUtils;
     }
 
     @Override
     public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
         try {
             mCurrentUserId = ActivityManager.getService().getCurrentUser().id;
 
@@ -84,6 +86,9 @@
                 case COMMAND_GET_DISABLED:
                     runGetDisabled();
                     break;
+                case COMMAND_HELP:
+                    onHelp();
+                    break;
                 default:
                     getErrPrintWriter().println("Unknown command: " + cmd);
                     break;
@@ -103,6 +108,43 @@
 
     @Override
     public void onHelp() {
+        try (final PrintWriter pw = getOutPrintWriter();) {
+            pw.println("lockSettings service commands:");
+            pw.println("");
+            pw.println("NOTE: when lock screen is set, all commands require the --old <CREDENTIAL>"
+                    + " argument.");
+            pw.println("");
+            pw.println("  help");
+            pw.println("    Prints this help text.");
+            pw.println("");
+            pw.println("  get-disabled [--old <CREDENTIAL>] [--user USER_ID]");
+            pw.println("    Checks whether lock screen is disabled.");
+            pw.println("");
+            pw.println("  set-disabled [--old <CREDENTIAL>] [--user USER_ID] <true|false>");
+            pw.println("    When true, disables lock screen.");
+            pw.println("");
+            pw.println("  set-pattern [--old <CREDENTIAL>] [--user USER_ID] <PATTERN>");
+            pw.println("    Sets the lock screen as pattern, using the given PATTERN to unlock.");
+            pw.println("");
+            pw.println("  set-pin [--old <CREDENTIAL>] [--user USER_ID] <PIN>");
+            pw.println("    Sets the lock screen as PIN, using the given PIN to unlock.");
+            pw.println("");
+            pw.println("  set-pin [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
+            pw.println("    Sets the lock screen as password, using the given PASSOWRD to unlock.");
+            pw.println("");
+            pw.println("  sp [--old <CREDENTIAL>] [--user USER_ID]");
+            pw.println("    Gets whether synthetic password is enabled.");
+            pw.println("");
+            pw.println("  sp [--old <CREDENTIAL>] [--user USER_ID] <1|0>");
+            pw.println("    Enables / disables synthetic password.");
+            pw.println("");
+            pw.println("  clear [--old <CREDENTIAL>] [--user USER_ID]");
+            pw.println("    Clears the lock credentials.");
+            pw.println("");
+            pw.println("  verify [--old <CREDENTIAL>] [--user USER_ID]");
+            pw.println("    Verifies the lock credentials.");
+            pw.println("");
+        }
     }
 
     private void parseArgs() {
@@ -134,27 +176,27 @@
                 mLockPatternUtils.isSyntheticPasswordEnabled()));
     }
 
-    private void runSetPattern() throws RemoteException {
+    private void runSetPattern() {
         mLockPatternUtils.saveLockPattern(stringToPattern(mNew), mOld, mCurrentUserId);
         getOutPrintWriter().println("Pattern set to '" + mNew + "'");
     }
 
-    private void runSetPassword() throws RemoteException {
+    private void runSetPassword() {
         mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_ALPHABETIC, mCurrentUserId);
         getOutPrintWriter().println("Password set to '" + mNew + "'");
     }
 
-    private void runSetPin() throws RemoteException {
+    private void runSetPin() {
         mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_NUMERIC, mCurrentUserId);
         getOutPrintWriter().println("Pin set to '" + mNew + "'");
     }
 
-    private void runClear() throws RemoteException {
+    private void runClear() {
         mLockPatternUtils.clearLock(mOld, mCurrentUserId);
         getOutPrintWriter().println("Lock credential cleared");
     }
 
-    private void runSetDisabled() throws RemoteException {
+    private void runSetDisabled() {
         final boolean disabled = Boolean.parseBoolean(mNew);
         mLockPatternUtils.setLockScreenDisabled(disabled, mCurrentUserId);
         getOutPrintWriter().println("Lock screen disabled set to " + disabled);
@@ -165,7 +207,7 @@
         getOutPrintWriter().println(isLockScreenDisabled);
     }
 
-    private boolean checkCredential() throws RemoteException {
+    private boolean checkCredential() {
         final boolean havePassword = mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId);
         final boolean havePattern = mLockPatternUtils.isLockPatternEnabled(mCurrentUserId);
         if (havePassword || havePattern) {
@@ -186,6 +228,9 @@
                         mLockPatternUtils.reportFailedPasswordAttempt(mCurrentUserId);
                     }
                     getOutPrintWriter().println("Old password '" + mOld + "' didn't match");
+                } else {
+                    // Resets the counter for failed password attempts to 0.
+                    mLockPatternUtils.reportSuccessfulPasswordAttempt(mCurrentUserId);
                 }
                 return result;
             } catch (RequestThrottledException e) {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index a7df0e2..3eb7321 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -414,7 +414,9 @@
             }
             // We don't need to change a2dp status when bluetooth is not connected.
             if (btDevice != null) {
-                Slog.v(TAG, "restoreBluetoothA2dp(" + a2dpOn + ")");
+                if (DEBUG) {
+                    Slog.d(TAG, "restoreBluetoothA2dp(" + a2dpOn + ")");
+                }
                 mAudioService.setBluetoothA2dpOn(a2dpOn);
             }
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 442354b..c938f5e 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -255,8 +255,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, useSuggested,
-                    previousFlagPlaySound);
+            postAdjustLocalVolume(stream, direction, flags, packageName, uid, asSystemService,
+                    useSuggested, previousFlagPlaySound);
         } else {
             if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
                 // Nothing to do, the volume cannot be changed
@@ -462,8 +462,11 @@
     }
 
     private void postAdjustLocalVolume(final int stream, final int direction, final int flags,
-            final String packageName, final int uid, final boolean useSuggested,
-            final int previousFlagPlaySound) {
+            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;
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -481,7 +484,7 @@
                         mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
                                 packageName, uid);
                     }
-                } catch (IllegalArgumentException e) {
+                } catch (IllegalArgumentException | SecurityException e) {
                     Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream="
                             + stream + ", flags=" + flags + ", packageName=" + packageName
                             + ", uid=" + uid + ", useSuggested=" + useSuggested
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 68b2a58..dc4405f 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -1844,7 +1844,7 @@
                             String packageName = getContext().getOpPackageName();
                             mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
                                     flags, packageName, TAG);
-                        } catch (RemoteException e) {
+                        } catch (RemoteException|SecurityException e) {
                             Log.e(TAG, "Error adjusting default volume.", e);
                         } catch (IllegalArgumentException e) {
                             Log.e(TAG, "Cannot adjust volume: direction=" + direction
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ab482bb..76f9695 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -430,6 +430,8 @@
 
     private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
 
+    private volatile boolean mNetworkManagerReady;
+
     /** Defined network policies. */
     @GuardedBy("mNetworkPoliciesSecondLock")
     final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>();
@@ -626,6 +628,7 @@
         mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     void updatePowerSaveWhitelistUL() {
         try {
             int[] whitelist = mDeviceIdleController.getAppIdWhitelistExceptIdle();
@@ -652,6 +655,7 @@
      *
      * @return whether any uid has been whitelisted.
      */
+    @GuardedBy("mUidRulesFirstLock")
     boolean addDefaultRestrictBackgroundWhitelistUidsUL() {
         final List<UserInfo> users = mUserManager.getUsers();
         final int numberUsers = users.size();
@@ -664,6 +668,7 @@
         return changed;
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     private boolean addDefaultRestrictBackgroundWhitelistUidsUL(int userId) {
         final SystemConfig sysConfig = SystemConfig.getInstance();
         final PackageManager pm = mContext.getPackageManager();
@@ -872,6 +877,7 @@
     }
 
     public CountDownLatch networkScoreAndNetworkManagementServiceReady() {
+        mNetworkManagerReady = true;
         final CountDownLatch initCompleteSignal = new CountDownLatch(1);
         mHandler.post(() -> initService(initCompleteSignal));
         return initCompleteSignal;
@@ -1117,6 +1123,7 @@
      * Check {@link NetworkPolicy} against current {@link INetworkStatsService}
      * to show visible notifications as needed.
      */
+    @GuardedBy("mNetworkPoliciesSecondLock")
     void updateNotificationsNL() {
         if (LOGV) Slog.v(TAG, "updateNotificationsNL()");
         Trace.traceBegin(TRACE_TAG_NETWORK, "updateNotificationsNL");
@@ -1263,6 +1270,7 @@
      * @return relevant subId, or {@link #INVALID_SUBSCRIPTION_ID} when no
      *         matching subId found.
      */
+    @GuardedBy("mNetworkPoliciesSecondLock")
     private int findRelevantSubIdNL(NetworkTemplate template) {
         // Mobile template is relevant when any active subscriber matches
         for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
@@ -1282,6 +1290,7 @@
      * Notify that given {@link NetworkTemplate} is over
      * {@link NetworkPolicy#limitBytes}, potentially showing dialog to user.
      */
+    @GuardedBy("mNetworkPoliciesSecondLock")
     private void notifyOverLimitNL(NetworkTemplate template) {
         if (!mOverLimitNotified.contains(template)) {
             mContext.startActivity(buildNetworkOverLimitIntent(mContext.getResources(), template));
@@ -1289,6 +1298,7 @@
         }
     }
 
+    @GuardedBy("mNetworkPoliciesSecondLock")
     private void notifyUnderLimitNL(NetworkTemplate template) {
         mOverLimitNotified.remove(template);
     }
@@ -1459,6 +1469,7 @@
      * @param subId that has its associated NetworkPolicy updated if necessary
      * @return if any policies were updated
      */
+    @GuardedBy("mNetworkPoliciesSecondLock")
     private boolean maybeUpdateMobilePolicyCycleAL(int subId, String subscriberId) {
         if (LOGV) Slog.v(TAG, "maybeUpdateMobilePolicyCycleAL()");
 
@@ -1616,6 +1627,7 @@
      * @param shouldNormalizePolicies true iff network policies need to be normalized after the
      *                                update.
      */
+    @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
     void handleNetworkPoliciesUpdateAL(boolean shouldNormalizePolicies) {
         if (shouldNormalizePolicies) {
             normalizePoliciesNL();
@@ -1630,6 +1642,7 @@
      * Proactively control network data connections when they exceed
      * {@link NetworkPolicy#limitBytes}.
      */
+    @GuardedBy("mNetworkPoliciesSecondLock")
     void updateNetworkEnabledNL() {
         if (LOGV) Slog.v(TAG, "updateNetworkEnabledNL()");
         Trace.traceBegin(TRACE_TAG_NETWORK, "updateNetworkEnabledNL");
@@ -1770,6 +1783,7 @@
      * {@link NetworkPolicy} that need to be enforced. When matches found, set
      * remaining quota based on usage cycle and historical stats.
      */
+    @GuardedBy("mNetworkPoliciesSecondLock")
     void updateNetworkRulesNL() {
         if (LOGV) Slog.v(TAG, "updateNetworkRulesNL()");
         Trace.traceBegin(TRACE_TAG_NETWORK, "updateNetworkRulesNL");
@@ -1954,6 +1968,7 @@
      * Once any {@link #mNetworkPolicy} are loaded from disk, ensure that we
      * have at least a default mobile policy defined.
      */
+    @GuardedBy("mNetworkPoliciesSecondLock")
     private void ensureActiveMobilePolicyAL() {
         if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyAL()");
         if (mSuppressDefaultPolicy) return;
@@ -1974,6 +1989,7 @@
      * @param subscriberId that we check for an existing policy
      * @return true if a mobile network policy was added, or false one already existed.
      */
+    @GuardedBy("mNetworkPoliciesSecondLock")
     private boolean ensureActiveMobilePolicyAL(int subId, String subscriberId) {
         // Poke around to see if we already have a policy
         final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
@@ -2033,6 +2049,7 @@
      *
      * @return if the policy was modified
      */
+    @GuardedBy("mNetworkPoliciesSecondLock")
     private boolean updateDefaultMobilePolicyAL(int subId, NetworkPolicy policy) {
         if (!policy.inferred) {
             if (LOGD) Slog.d(TAG, "Ignoring user-defined policy " + policy);
@@ -2088,6 +2105,7 @@
         }
     }
 
+    @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
     private void readPolicyAL() {
         if (LOGV) Slog.v(TAG, "readPolicyAL()");
 
@@ -2319,6 +2337,7 @@
      * Perform upgrade step of moving any user-defined meterness overrides over
      * into {@link WifiConfiguration}.
      */
+    @GuardedBy({"mNetworkPoliciesSecondLock", "mUidRulesFirstLock"})
     private void upgradeWifiMeteredOverrideAL() {
         boolean modified = false;
         final WifiManager wm = mContext.getSystemService(WifiManager.class);
@@ -2349,6 +2368,7 @@
         }
     }
 
+    @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
     void writePolicyAL() {
         if (LOGV) Slog.v(TAG, "writePolicyAL()");
 
@@ -2520,6 +2540,7 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     private void setUidPolicyUncheckedUL(int uid, int oldPolicy, int policy, boolean persist) {
         setUidPolicyUncheckedUL(uid, policy, false);
 
@@ -2551,6 +2572,7 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     private void setUidPolicyUncheckedUL(int uid, int policy, boolean persist) {
         if (policy == POLICY_NONE) {
             mUidPolicy.delete(uid);
@@ -2598,6 +2620,7 @@
      * Removes any persistable state associated with given {@link UserHandle}, persisting
      * if any changes that are made.
      */
+    @GuardedBy("mUidRulesFirstLock")
     boolean removeUserStateUL(int userId, boolean writePolicy) {
 
         mLogger.removingUserState(userId);
@@ -2699,10 +2722,12 @@
         }
     }
 
+    @GuardedBy("mNetworkPoliciesSecondLock")
     private void normalizePoliciesNL() {
         normalizePoliciesNL(getNetworkPolicies(mContext.getOpPackageName()));
     }
 
+    @GuardedBy("mNetworkPoliciesSecondLock")
     private void normalizePoliciesNL(NetworkPolicy[] policies) {
         mNetworkPolicy.clear();
         for (NetworkPolicy policy : policies) {
@@ -2792,6 +2817,7 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     private void setRestrictBackgroundUL(boolean restrictBackground) {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setRestrictBackgroundUL");
         try {
@@ -3444,11 +3470,13 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     private boolean isUidForegroundOnRestrictBackgroundUL(int uid) {
         final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
         return isProcStateAllowedWhileOnRestrictBackground(procState);
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     private boolean isUidForegroundOnRestrictPowerUL(int uid) {
         final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
         return isProcStateAllowedWhileIdleOrPowerSaveMode(procState);
@@ -3464,6 +3492,7 @@
      * {@link #updateRulesForDataUsageRestrictionsUL(int)} and
      * {@link #updateRulesForPowerRestrictionsUL(int)}
      */
+    @GuardedBy("mUidRulesFirstLock")
     private void updateUidStateUL(int uid, int uidState) {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateUidStateUL");
         try {
@@ -3490,6 +3519,7 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     private void removeUidStateUL(int uid) {
         final int index = mUidState.indexOfKey(uid);
         if (index >= 0) {
@@ -3534,6 +3564,7 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     void updateRulesForPowerSaveUL() {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForPowerSaveUL");
         try {
@@ -3544,10 +3575,12 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     void updateRuleForRestrictPowerUL(int uid) {
         updateRulesForWhitelistedPowerSaveUL(uid, mRestrictPower, FIREWALL_CHAIN_POWERSAVE);
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     void updateRulesForDeviceIdleUL() {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForDeviceIdleUL");
         try {
@@ -3558,12 +3591,14 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     void updateRuleForDeviceIdleUL(int uid) {
         updateRulesForWhitelistedPowerSaveUL(uid, mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE);
     }
 
     // NOTE: since both fw_dozable and fw_powersave uses the same map
     // (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method.
+    @GuardedBy("mUidRulesFirstLock")
     private void updateRulesForWhitelistedPowerSaveUL(boolean enabled, int chain,
             SparseIntArray rules) {
         if (enabled) {
@@ -3608,6 +3643,7 @@
      *        {@link #mPowerSaveWhitelistExceptIdleAppIds} for checking if the {@param uid} is
      *        whitelisted.
      */
+    @GuardedBy("mUidRulesFirstLock")
     private boolean isWhitelistedBatterySaverUL(int uid, boolean deviceIdleMode) {
         final int appId = UserHandle.getAppId(uid);
         boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId)
@@ -3620,6 +3656,7 @@
 
     // NOTE: since both fw_dozable and fw_powersave uses the same map
     // (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method.
+    @GuardedBy("mUidRulesFirstLock")
     private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) {
         if (enabled) {
             final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid,
@@ -3632,6 +3669,7 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     void updateRulesForAppIdleUL() {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForAppIdleUL");
         try {
@@ -3661,6 +3699,7 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     void updateRuleForAppIdleUL(int uid) {
         if (!isUidValidForBlacklistRules(uid)) return;
 
@@ -3684,6 +3723,7 @@
      * Toggle the firewall standby chain and inform listeners if the uid rules have effectively
      * changed.
      */
+    @GuardedBy("mUidRulesFirstLock")
     void updateRulesForAppIdleParoleUL() {
         boolean paroled = mUsageStats.isAppIdleParoleOn();
         boolean enableChain = !paroled;
@@ -3716,6 +3756,7 @@
      * Update rules that might be changed by {@link #mRestrictBackground},
      * {@link #mRestrictPower}, or {@link #mDeviceIdleMode} value.
      */
+    @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
     private void updateRulesForGlobalChangeAL(boolean restrictedNetworksChanged) {
         if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
             Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
@@ -3737,6 +3778,7 @@
     }
 
     // TODO: rename / document to make it clear these are global (not app-specific) rules
+    @GuardedBy("mUidRulesFirstLock")
     private void updateRulesForRestrictPowerUL() {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL");
         try {
@@ -3748,6 +3790,7 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     private void updateRulesForRestrictBackgroundUL() {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictBackgroundUL");
         try {
@@ -3768,6 +3811,7 @@
     }
 
     // TODO: refactor / consolidate all those updateXyz methods, there are way too many of them...
+    @GuardedBy("mUidRulesFirstLock")
     private void updateRulesForAllAppsUL(@RestrictType int type) {
         if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
             Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL-" + type);
@@ -3819,6 +3863,7 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     private void updateRulesForTempWhitelistChangeUL(int appId) {
         final List<UserInfo> users = mUserManager.getUsers();
         final int numUsers = users.size();
@@ -3883,6 +3928,7 @@
     /**
      * Clears all state - internal and external - associated with an UID.
      */
+    @GuardedBy("mUidRulesFirstLock")
     private void onUidDeletedUL(int uid) {
         // First cleanup in-memory state synchronously...
         mUidRules.delete(uid);
@@ -3911,6 +3957,7 @@
      *
      * <p>This method changes both the external firewall rules and the internal state.
      */
+    @GuardedBy("mUidRulesFirstLock")
     private void updateRestrictionRulesForUidUL(int uid) {
         // Methods below only changes the firewall rules for the power-related modes.
         updateRuleForDeviceIdleUL(uid);
@@ -4101,6 +4148,7 @@
      * <p>
      * <strong>NOTE: </strong>This method does not update the firewall rules on {@code netd}.
      */
+    @GuardedBy("mUidRulesFirstLock")
     private void updateRulesForPowerRestrictionsUL(int uid) {
         final int oldUidRules = mUidRules.get(uid, RULE_NONE);
 
@@ -4537,6 +4585,7 @@
      * @param uidRules new UID rules; if {@code null}, only toggles chain state.
      * @param toggle whether the chain should be enabled, disabled, or not changed.
      */
+    @GuardedBy("mUidRulesFirstLock")
     private void setUidFirewallRulesUL(int chain, @Nullable SparseIntArray uidRules,
             @ChainToggleType int toggle) {
         if (uidRules != null) {
@@ -4603,6 +4652,7 @@
     /**
      * Add or remove a uid to the firewall blacklist for all network ifaces.
      */
+    @GuardedBy("mUidRulesFirstLock")
     private void enableFirewallChainUL(int chain, boolean enable) {
         if (mFirewallChainStates.indexOfKey(chain) >= 0 &&
                 mFirewallChainStates.get(chain) == enable) {
@@ -4714,6 +4764,7 @@
         mHandler.getLooper().getQueue().addIdleHandler(handler);
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     @VisibleForTesting
     public void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) {
         mRestrictBackgroundPowerState = result;
@@ -5024,6 +5075,9 @@
 
     private void handleRestrictedPackagesChangeUL(Set<Integer> oldRestrictedUids,
             Set<Integer> newRestrictedUids) {
+        if (!mNetworkManagerReady) {
+            return;
+        }
         if (oldRestrictedUids == null) {
             for (int uid : newRestrictedUids) {
                 updateRulesForDataUsageRestrictionsUL(uid);
@@ -5042,6 +5096,7 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
     private boolean isRestrictedByAdminUL(int uid) {
         final Set<Integer> restrictedUids = mMeteredRestrictedUids.get(
                 UserHandle.getUserId(uid));
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 64dc98e..2e91f99 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -1,9 +1,11 @@
 set noparent
 
+codewiz@google.com
 ek@google.com
 jchalard@google.com
 jsharkey@android.com
 lorenzo@google.com
+reminv@google.com
 satk@google.com
 silberst@google.com
 sudheersai@google.com
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index c0fbfbb..18f4bc7 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -150,6 +150,7 @@
         try {
             provider.onConnected();
         } catch (RemoteException e) {
+            Slog.e(TAG, "can't connect to service " + info, e);
             // we tried
         }
         if (mCallback != null) {
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index ce805aad..b1cd627 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -32,16 +32,17 @@
     private static final String TAG = "GroupHelper";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    protected static final int AUTOGROUP_AT_COUNT = 4;
     protected static final String AUTOGROUP_KEY = "ranker_group";
 
     private final Callback mCallback;
+    private final int mAutoGroupAtCount;
 
     // Map of user : <Map of package : notification keys>. Only contains notifications that are not
     // grouped by the app (aka no group or sort key).
     Map<Integer, Map<String, LinkedHashSet<String>>> mUngroupedNotifications = new HashMap<>();
 
-    public GroupHelper(Callback callback) {;
+    public GroupHelper(int autoGroupAtCount, Callback callback) {
+        mAutoGroupAtCount = autoGroupAtCount;
         mCallback = callback;
     }
 
@@ -68,7 +69,7 @@
                     notificationsForPackage.add(sbn.getKey());
                     ungroupedNotificationsByUser.put(sbn.getPackageName(), notificationsForPackage);
 
-                    if (notificationsForPackage.size() >= AUTOGROUP_AT_COUNT
+                    if (notificationsForPackage.size() >= mAutoGroupAtCount
                             || autogroupSummaryExists) {
                         notificationsToGroup.addAll(notificationsForPackage);
                     }
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 5eb7397..efc18ad 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -54,6 +54,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
@@ -116,9 +117,12 @@
     // contains connections to all connected services, including app services
     // and system services
     private final ArrayList<ManagedServiceInfo> mServices = new ArrayList<>();
-    // things that will be put into mServices as soon as they're ready
-    private final ArrayList<String> mServicesBinding = new ArrayList<>();
-    private final ArraySet<String> mServicesRebinding = new ArraySet<>();
+    /**
+     * The services that have been bound by us. If the service is also connected, it will also
+     * be in {@link #mServices}.
+     */
+    private final ArrayList<Pair<ComponentName, Integer>> mServicesBound = new ArrayList<>();
+    private final ArraySet<Pair<ComponentName, Integer>> mServicesRebinding = new ArraySet<>();
 
     // lists the component names of all enabled (and therefore potentially connected)
     // app services for current profiles.
@@ -513,6 +517,30 @@
         return false;
     }
 
+    protected boolean isPackageAllowed(String pkg, int userId) {
+        if (pkg == null) {
+            return false;
+        }
+        ArrayMap<Boolean, ArraySet<String>> allowedByType =
+                mApproved.getOrDefault(userId, new ArrayMap<>());
+        for (int i = 0; i < allowedByType.size(); i++) {
+            ArraySet<String> allowed = allowedByType.valueAt(i);
+            for (String allowedEntry : allowed) {
+                ComponentName component = ComponentName.unflattenFromString(allowedEntry);
+                if (component != null) {
+                    if (pkg.equals(component.getPackageName())) {
+                        return true;
+                    }
+                } else {
+                    if (pkg.equals(allowedEntry)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
     public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uidList) {
         if (DEBUG) Slog.d(TAG, "onPackagesChanged removingPackage=" + removingPackage
                 + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
@@ -533,6 +561,11 @@
                 if (mEnabledServicesPackageNames.contains(pkgName)) {
                     anyServicesInvolved = true;
                 }
+                for (int uid : uidList) {
+                    if (isPackageAllowed(pkgName, UserHandle.getUserId(uid))) {
+                        anyServicesInvolved = true;
+                    }
+                }
             }
 
             if (anyServicesInvolved) {
@@ -596,6 +629,15 @@
                 + service + " " + service.getClass());
     }
 
+    public boolean isSameUser(IInterface service, int userId) {
+        checkNotNull(service);
+        ManagedServiceInfo info = getServiceFromTokenLocked(service);
+        if (info != null) {
+            return info.isSameUser(userId);
+        }
+        return false;
+    }
+
     public void unregisterService(IInterface service, int userid) {
         checkNotNull(service);
         // no need to check permissions; if your service binder is in the list,
@@ -646,7 +688,11 @@
 
             for (int userId : userIds) {
                 if (enabled) {
-                    registerServiceLocked(component, userId);
+                    if (isPackageOrComponentAllowed(component.toString(), userId)) {
+                        registerServiceLocked(component, userId);
+                    } else {
+                        Slog.d(TAG, component + " no longer has permission to be bound");
+                    }
                 } else {
                     unregisterServiceLocked(component, userId);
                 }
@@ -917,13 +963,13 @@
             final boolean isSystem) {
         if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid);
 
-        final String servicesBindingTag = name.toString() + "/" + userid;
-        if (mServicesBinding.contains(servicesBindingTag)) {
-            Slog.v(TAG, "Not registering " + name + " as bind is already in progress");
+        final Pair<ComponentName, Integer> servicesBindingTag = Pair.create(name, userid);
+        if (mServicesBound.contains(servicesBindingTag)) {
+            Slog.v(TAG, "Not registering " + name + " is already bound");
             // stop registering this thing already! we're working on it
             return;
         }
-        mServicesBinding.add(servicesBindingTag);
+        mServicesBound.add(servicesBindingTag);
 
         final int N = mServices.size();
         for (int i = N - 1; i >= 0; i--) {
@@ -934,11 +980,7 @@
                 Slog.v(TAG, "    disconnecting old " + getCaption() + ": " + info.service);
                 removeServiceLocked(i);
                 if (info.connection != null) {
-                    try {
-                        mContext.unbindService(info.connection);
-                    } catch (IllegalArgumentException e) {
-                        Slog.e(TAG, "failed to unbind " + name, e);
-                    }
+                    unbindService(info.connection, info.component, info.userid);
                 }
             }
         }
@@ -969,11 +1011,11 @@
 
                 @Override
                 public void onServiceConnected(ComponentName name, IBinder binder) {
+                    Slog.v(TAG, getCaption() + " service connected: " + name);
                     boolean added = false;
                     ManagedServiceInfo info = null;
                     synchronized (mMutex) {
                         mServicesRebinding.remove(servicesBindingTag);
-                        mServicesBinding.remove(servicesBindingTag);
                         try {
                             mService = asInterface(binder);
                             info = newServiceInfo(mService, name,
@@ -991,7 +1033,6 @@
 
                 @Override
                 public void onServiceDisconnected(ComponentName name) {
-                    mServicesBinding.remove(servicesBindingTag);
                     Slog.v(TAG, getCaption() + " connection lost: " + name);
                 }
 
@@ -999,12 +1040,7 @@
                 public void onBindingDied(ComponentName name) {
                     Slog.w(TAG, getCaption() + " binding died: " + name);
                     synchronized (mMutex) {
-                        mServicesBinding.remove(servicesBindingTag);
-                        try {
-                            mContext.unbindService(this);
-                        } catch (IllegalArgumentException e) {
-                            Slog.e(TAG, "failed to unbind " + name, e);
-                        }
+                        unbindService(this, name, userid);
                         if (!mServicesRebinding.contains(servicesBindingTag)) {
                             mServicesRebinding.add(servicesBindingTag);
                             mHandler.postDelayed(new Runnable() {
@@ -1024,12 +1060,12 @@
                 serviceConnection,
                 BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT,
                 new UserHandle(userid))) {
-                mServicesBinding.remove(servicesBindingTag);
+                mServicesBound.remove(servicesBindingTag);
                 Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
                 return;
             }
         } catch (SecurityException ex) {
-            mServicesBinding.remove(servicesBindingTag);
+            mServicesBound.remove(servicesBindingTag);
             Slog.e(TAG, "Unable to bind " + getCaption() + " service: " + intent, ex);
         }
     }
@@ -1050,13 +1086,7 @@
             if (name.equals(info.component) && info.userid == userid) {
                 removeServiceLocked(i);
                 if (info.connection != null) {
-                    try {
-                        mContext.unbindService(info.connection);
-                    } catch (IllegalArgumentException ex) {
-                        // something happened to the service: we think we have a connection
-                        // but it's bogus.
-                        Slog.e(TAG, getCaption() + " " + name + " could not be unbound: " + ex);
-                    }
+                    unbindService(info.connection, info.component, info.userid);
                 }
             }
         }
@@ -1121,7 +1151,18 @@
     private void unregisterServiceImpl(IInterface service, int userid) {
         ManagedServiceInfo info = removeServiceImpl(service, userid);
         if (info != null && info.connection != null && !info.isGuest(this)) {
-            mContext.unbindService(info.connection);
+            unbindService(info.connection, info.component, info.userid);
+        }
+    }
+
+    private void unbindService(ServiceConnection connection, ComponentName component, int userId) {
+        try {
+            mContext.unbindService(connection);
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, getCaption() + " " + component + " could not be unbound", e);
+        }
+        synchronized (mMutex) {
+            mServicesBound.remove(Pair.create(component, userId));
         }
     }
 
@@ -1173,6 +1214,13 @@
             proto.end(token);
         }
 
+        public boolean isSameUser(int userId) {
+            if (!isEnabledForCurrentProfiles()) {
+                return false;
+            }
+            return this.userid == userId;
+        }
+
         public boolean enabledAndUserMatches(int nid) {
             if (!isEnabledForCurrentProfiles()) {
                 return false;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0897b8a..f487200 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -91,6 +91,7 @@
 import android.app.IActivityManager;
 import android.app.INotificationManager;
 import android.app.ITransientNotification;
+import android.app.IUriGrantsManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
@@ -98,6 +99,7 @@
 import android.app.NotificationManager.Policy;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
+import android.app.UriGrantsManager;
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.backup.BackupManager;
@@ -204,6 +206,7 @@
 import com.android.server.notification.ManagedServices.UserProfiles;
 import com.android.server.policy.PhoneWindowManager;
 import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import libcore.io.IoUtils;
@@ -318,6 +321,8 @@
     private ICompanionDeviceManager mCompanionManager;
     private AccessibilityManager mAccessibilityManager;
     private IDeviceIdleController mDeviceIdleController;
+    private IUriGrantsManager mUgm;
+    private UriGrantsManagerInternal mUgmInternal;
 
     final IBinder mForegroundToken = new Binder();
     private WorkerHandler mHandler;
@@ -329,6 +334,8 @@
 
     private long[] mFallbackVibrationPattern;
     private boolean mUseAttentionLight;
+    boolean mHasLight = true;
+    boolean mLightEnabled;
     boolean mSystemReady;
 
     private boolean mDisableNotificationEffects;
@@ -343,9 +350,9 @@
     private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
 
     // for enabling and disabling notification pulse behavior
-    private boolean mScreenOn = true;
+    boolean mScreenOn = true;
     protected boolean mInCall = false;
-    private boolean mNotificationPulseEnabled;
+    boolean mNotificationPulseEnabled;
 
     private Uri mInCallNotificationUri;
     private AudioAttributes mInCallNotificationAudioAttributes;
@@ -384,6 +391,7 @@
     private static final String ATTR_VERSION = "version";
 
     private RankingHelper mRankingHelper;
+    private PreferencesHelper mPreferencesHelper;
 
     private final UserProfiles mUserProfiles = new UserProfiles();
     private NotificationListeners mListeners;
@@ -400,6 +408,7 @@
 
     private SnoozeHelper mSnoozeHelper;
     private GroupHelper mGroupHelper;
+    private int mAutoGroupAtCount;
     private boolean mIsTelevision;
 
     private MetricsLogger mMetricsLogger;
@@ -477,7 +486,7 @@
 
         String defaultDndAccess = getContext().getResources().getString(
                 com.android.internal.R.string.config_defaultDndAccessPackages);
-        if (defaultListenerAccess != null) {
+        if (defaultDndAccess != null) {
             for (String whitelisted :
                     defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR)) {
                 try {
@@ -522,8 +531,8 @@
         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
             if (ZenModeConfig.ZEN_TAG.equals(parser.getName())) {
                 mZenModeHelper.readXml(parser, forRestore);
-            } else if (RankingHelper.TAG_RANKING.equals(parser.getName())){
-                mRankingHelper.readXml(parser, forRestore);
+            } else if (PreferencesHelper.TAG_RANKING.equals(parser.getName())){
+                mPreferencesHelper.readXml(parser, forRestore);
             }
             if (mListeners.getConfig().xmlTag.equals(parser.getName())) {
                 mListeners.readXml(parser, mAllowedManagedServicePackages);
@@ -605,7 +614,7 @@
         out.startTag(null, TAG_NOTIFICATION_POLICY);
         out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
         mZenModeHelper.writeXml(out, forBackup, null);
-        mRankingHelper.writeXml(out, forBackup);
+        mPreferencesHelper.writeXml(out, forBackup);
         mListeners.writeXml(out, forBackup);
         mAssistants.writeXml(out, forBackup);
         mConditionProviders.writeXml(out, forBackup);
@@ -946,7 +955,7 @@
                 // update system notification channels
                 SystemNotificationChannels.createAll(context);
                 mZenModeHelper.updateDefaultZenRules();
-                mRankingHelper.onLocaleChanged(context, ActivityManager.getCurrentUser());
+                mPreferencesHelper.onLocaleChanged(context, ActivityManager.getCurrentUser());
             }
         }
     };
@@ -1089,7 +1098,8 @@
                 mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
                 mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
                 mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
-                mRankingHelper.onPackagesChanged(removingPackage, changeUserId, pkgList, uidList);
+                mPreferencesHelper.onPackagesChanged(
+                        removingPackage, changeUserId, pkgList, uidList);
                 savePolicyFile();
             }
         }
@@ -1149,7 +1159,7 @@
                 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
                 mUserProfiles.updateCache(context);
                 mZenModeHelper.onUserRemoved(user);
-                mRankingHelper.onUserRemoved(user);
+                mPreferencesHelper.onUserRemoved(user);
                 mListeners.onUserRemoved(user);
                 mConditionProviders.onUserRemoved(user);
                 mAssistants.onUserRemoved(user);
@@ -1195,7 +1205,8 @@
             ContentResolver resolver = getContext().getContentResolver();
             if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
                 boolean pulseEnabled = Settings.System.getIntForUser(resolver,
-                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
+                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT)
+                        != 0;
                 if (mNotificationPulseEnabled != pulseEnabled) {
                     mNotificationPulseEnabled = pulseEnabled;
                     updateNotificationPulse();
@@ -1206,7 +1217,7 @@
                             Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
             }
             if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
-                mRankingHelper.updateBadgingEnabled();
+                mPreferencesHelper.updateBadgingEnabled();
             }
         }
     }
@@ -1328,6 +1339,9 @@
     }
 
     @VisibleForTesting
+    void setPreferencesHelper(PreferencesHelper prefHelper) { mPreferencesHelper = prefHelper; }
+
+    @VisibleForTesting
     void setRankingHandler(RankingHandler rankingHandler) {
         mRankingHandler = rankingHandler;
     }
@@ -1356,7 +1370,8 @@
             ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
             NotificationUsageStats usageStats, AtomicFile policyFile,
             ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am,
-            UsageStatsManagerInternal appUsageStats, DevicePolicyManagerInternal dpm) {
+            UsageStatsManagerInternal appUsageStats, DevicePolicyManagerInternal dpm,
+            IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal) {
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
                 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -1365,6 +1380,8 @@
         mAccessibilityManager =
                 (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
         mAm = am;
+        mUgm = ugm;
+        mUgmInternal = ugmInternal;
         mPackageManager = packageManager;
         mPackageManagerClient = packageManagerClient;
         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
@@ -1415,9 +1432,13 @@
                 mRankingHandler.requestSort();
             }
         });
-        mRankingHelper = new RankingHelper(getContext(),
+        mPreferencesHelper = new PreferencesHelper(getContext(),
                 mPackageManagerClient,
                 mRankingHandler,
+                mZenModeHelper);
+        mRankingHelper = new RankingHelper(getContext(),
+                mRankingHandler,
+                mPreferencesHelper,
                 mZenModeHelper,
                 mUsageStats,
                 extractorNames);
@@ -1457,6 +1478,8 @@
         mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
 
         mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
+        mHasLight =
+                resources.getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed);
 
         // Don't start allowing notifications until the setup wizard has run once.
         // After that, including subsequent boots, init with notifications turned on.
@@ -1513,7 +1536,9 @@
                 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
                 getGroupHelper(), ActivityManager.getService(),
                 LocalServices.getService(UsageStatsManagerInternal.class),
-                LocalServices.getService(DevicePolicyManagerInternal.class));
+                LocalServices.getService(DevicePolicyManagerInternal.class),
+                UriGrantsManager.getService(),
+                LocalServices.getService(UriGrantsManagerInternal.class));
 
         // register for various Intents
         IntentFilter filter = new IntentFilter();
@@ -1565,7 +1590,9 @@
     }
 
     private GroupHelper getGroupHelper() {
-        return new GroupHelper(new GroupHelper.Callback() {
+        mAutoGroupAtCount =
+                getContext().getResources().getInteger(R.integer.config_autoGroupAtCount);
+        return new GroupHelper(mAutoGroupAtCount, new GroupHelper.Callback() {
             @Override
             public void addAutoGroup(String key) {
                 synchronized (mNotificationLock) {
@@ -1668,14 +1695,14 @@
             }
         }
         final NotificationChannel preUpdate =
-                mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
+                mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
 
-        mRankingHelper.updateNotificationChannel(pkg, uid, channel, true);
+        mPreferencesHelper.updateNotificationChannel(pkg, uid, channel, true);
         maybeNotifyChannelOwner(pkg, uid, preUpdate, channel);
 
         if (!fromListener) {
             final NotificationChannel modifiedChannel =
-                    mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
+                    mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
             mListeners.notifyNotificationChannelChanged(
                     pkg, UserHandle.getUserHandleForUid(uid),
                     modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
@@ -1712,8 +1739,8 @@
         Preconditions.checkNotNull(pkg);
 
         final NotificationChannelGroup preUpdate =
-                mRankingHelper.getNotificationChannelGroup(group.getId(), pkg, uid);
-        mRankingHelper.createNotificationChannelGroup(pkg, uid, group,
+                mPreferencesHelper.getNotificationChannelGroup(group.getId(), pkg, uid);
+        mPreferencesHelper.createNotificationChannelGroup(pkg, uid, group,
                 fromApp);
         if (!fromApp) {
             maybeNotifyChannelGroupOwner(pkg, uid, preUpdate, group);
@@ -2116,7 +2143,11 @@
         public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
             enforceSystemOrSystemUI("setNotificationsEnabledForPackage");
 
-            mRankingHelper.setEnabled(pkg, uid, enabled);
+            mPreferencesHelper.setEnabled(pkg, uid, enabled);
+            mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES)
+                    .setType(MetricsEvent.TYPE_ACTION)
+                    .setPackageName(pkg)
+                    .setSubtype(enabled ? 1 : 0));
             // Now, cancel any outstanding notifications that are part of a just-disabled app
             if (!enabled) {
                 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
@@ -2154,7 +2185,7 @@
                 String pkg, int uid, boolean enabled) {
             setNotificationsEnabledForPackage(pkg, uid, enabled);
 
-            mRankingHelper.setAppImportanceLocked(pkg, uid);
+            mPreferencesHelper.setAppImportanceLocked(pkg, uid);
         }
 
         /**
@@ -2172,25 +2203,25 @@
         public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
             checkCallerIsSystemOrSameApp(pkg);
 
-            return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
+            return mPreferencesHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
         }
 
         @Override
         public int getPackageImportance(String pkg) {
             checkCallerIsSystemOrSameApp(pkg);
-            return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
+            return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid());
         }
 
         @Override
         public boolean canShowBadge(String pkg, int uid) {
             checkCallerIsSystem();
-            return mRankingHelper.canShowBadge(pkg, uid);
+            return mPreferencesHelper.canShowBadge(pkg, uid);
         }
 
         @Override
         public void setShowBadge(String pkg, int uid, boolean showBadge) {
             checkCallerIsSystem();
-            mRankingHelper.setShowBadge(pkg, uid, showBadge);
+            mPreferencesHelper.setShowBadge(pkg, uid, showBadge);
             savePolicyFile();
         }
 
@@ -2222,12 +2253,12 @@
             for (int i = 0; i < channelsSize; i++) {
                 final NotificationChannel channel = channels.get(i);
                 Preconditions.checkNotNull(channel, "channel in list is null");
-                mRankingHelper.createNotificationChannel(pkg, uid, channel,
+                mPreferencesHelper.createNotificationChannel(pkg, uid, channel,
                         true /* fromTargetApp */, mConditionProviders.isPackageOrComponentAllowed(
                                 pkg, UserHandle.getUserId(uid)));
                 mListeners.notifyNotificationChannelChanged(pkg,
                         UserHandle.getUserHandleForUid(uid),
-                        mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
+                        mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
                         NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
             }
             savePolicyFile();
@@ -2250,7 +2281,7 @@
         @Override
         public NotificationChannel getNotificationChannel(String pkg, String channelId) {
             checkCallerIsSystemOrSameApp(pkg);
-            return mRankingHelper.getNotificationChannel(
+            return mPreferencesHelper.getNotificationChannel(
                     pkg, Binder.getCallingUid(), channelId, false /* includeDeleted */);
         }
 
@@ -2258,7 +2289,7 @@
         public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
                 String channelId, boolean includeDeleted) {
             checkCallerIsSystem();
-            return mRankingHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
+            return mPreferencesHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
         }
 
         @Override
@@ -2270,10 +2301,10 @@
             }
             cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
                     UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
-            mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
+            mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, channelId);
             mListeners.notifyNotificationChannelChanged(pkg,
                     UserHandle.getUserHandleForUid(callingUid),
-                    mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true),
+                    mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
                     NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
             savePolicyFile();
         }
@@ -2281,7 +2312,7 @@
         @Override
         public NotificationChannelGroup getNotificationChannelGroup(String pkg, String groupId) {
             checkCallerIsSystemOrSameApp(pkg);
-            return mRankingHelper.getNotificationChannelGroupWithChannels(
+            return mPreferencesHelper.getNotificationChannelGroupWithChannels(
                     pkg, Binder.getCallingUid(), groupId, false);
         }
 
@@ -2289,7 +2320,7 @@
         public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(
                 String pkg) {
             checkCallerIsSystemOrSameApp(pkg);
-            return mRankingHelper.getNotificationChannelGroups(
+            return mPreferencesHelper.getNotificationChannelGroups(
                     pkg, Binder.getCallingUid(), false, false);
         }
 
@@ -2299,10 +2330,10 @@
 
             final int callingUid = Binder.getCallingUid();
             NotificationChannelGroup groupToDelete =
-                    mRankingHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
+                    mPreferencesHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
             if (groupToDelete != null) {
                 List<NotificationChannel> deletedChannels =
-                        mRankingHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
+                        mPreferencesHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
                 for (int i = 0; i < deletedChannels.size(); i++) {
                     final NotificationChannel deletedChannel = deletedChannels.get(i);
                     cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
@@ -2333,47 +2364,47 @@
         public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
                 int uid, boolean includeDeleted) {
             enforceSystemOrSystemUI("getNotificationChannelsForPackage");
-            return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted);
+            return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted);
         }
 
         @Override
         public int getNumNotificationChannelsForPackage(String pkg, int uid,
                 boolean includeDeleted) {
             enforceSystemOrSystemUI("getNumNotificationChannelsForPackage");
-            return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted)
+            return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted)
                     .getList().size();
         }
 
         @Override
         public boolean onlyHasDefaultChannel(String pkg, int uid) {
             enforceSystemOrSystemUI("onlyHasDefaultChannel");
-            return mRankingHelper.onlyHasDefaultChannel(pkg, uid);
+            return mPreferencesHelper.onlyHasDefaultChannel(pkg, uid);
         }
 
         @Override
         public int getDeletedChannelCount(String pkg, int uid) {
             enforceSystemOrSystemUI("getDeletedChannelCount");
-            return mRankingHelper.getDeletedChannelCount(pkg, uid);
+            return mPreferencesHelper.getDeletedChannelCount(pkg, uid);
         }
 
         @Override
         public int getBlockedChannelCount(String pkg, int uid) {
             enforceSystemOrSystemUI("getBlockedChannelCount");
-            return mRankingHelper.getBlockedChannelCount(pkg, uid);
+            return mPreferencesHelper.getBlockedChannelCount(pkg, uid);
         }
 
         @Override
         public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
                 String pkg, int uid, boolean includeDeleted) {
             checkCallerIsSystem();
-            return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted, true);
+            return mPreferencesHelper.getNotificationChannelGroups(pkg, uid, includeDeleted, true);
         }
 
         @Override
         public NotificationChannelGroup getPopulatedNotificationChannelGroupForPackage(
                 String pkg, int uid, String groupId, boolean includeDeleted) {
             enforceSystemOrSystemUI("getPopulatedNotificationChannelGroupForPackage");
-            return mRankingHelper.getNotificationChannelGroupWithChannels(
+            return mPreferencesHelper.getNotificationChannelGroupWithChannels(
                     pkg, uid, groupId, includeDeleted);
         }
 
@@ -2381,13 +2412,13 @@
         public NotificationChannelGroup getNotificationChannelGroupForPackage(
                 String groupId, String pkg, int uid) {
             enforceSystemOrSystemUI("getNotificationChannelGroupForPackage");
-            return mRankingHelper.getNotificationChannelGroup(groupId, pkg, uid);
+            return mPreferencesHelper.getNotificationChannelGroup(groupId, pkg, uid);
         }
 
         @Override
         public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
             checkCallerIsSystemOrSameApp(pkg);
-            return mRankingHelper.getNotificationChannels(
+            return mPreferencesHelper.getNotificationChannels(
                     pkg, Binder.getCallingUid(), false /* includeDeleted */);
         }
 
@@ -2404,12 +2435,12 @@
         @Override
         public int getBlockedAppCount(int userId) {
             checkCallerIsSystem();
-            return mRankingHelper.getBlockedAppCount(userId);
+            return mPreferencesHelper.getBlockedAppCount(userId);
         }
 
         @Override
         public boolean areChannelsBypassingDnd() {
-            return mRankingHelper.areChannelsBypassingDnd();
+            return mPreferencesHelper.areChannelsBypassingDnd();
         }
 
         @Override
@@ -2432,7 +2463,7 @@
 
             // Reset notification preferences
             if (!fromApp) {
-                mRankingHelper.onPackagesChanged(
+                mPreferencesHelper.onPackagesChanged(
                         true, UserHandle.getCallingUserId(), packages, uids);
             }
 
@@ -3125,14 +3156,19 @@
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
             final DumpFilter filter = DumpFilter.parseFromArguments(args);
-            if (filter.stats) {
-                dumpJson(pw, filter);
-            } else if (filter.proto) {
-                dumpProto(fd, filter);
-            } else if (filter.criticalPriority) {
-                dumpNotificationRecords(pw, filter);
-            } else {
-                dumpImpl(pw, filter);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (filter.stats) {
+                    dumpJson(pw, filter);
+                } else if (filter.proto) {
+                    dumpProto(fd, filter);
+                } else if (filter.criticalPriority) {
+                    dumpNotificationRecords(pw, filter);
+                } else {
+                    dumpImpl(pw, filter);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
 
@@ -3408,20 +3444,28 @@
 
         @Override
         public void applyEnqueuedAdjustmentFromAssistant(INotificationListener token,
-                Adjustment adjustment) throws RemoteException {
+                Adjustment adjustment) {
+            boolean foundEnqueued = false;
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationLock) {
                     mAssistants.checkServiceTokenLocked(token);
                     int N = mEnqueuedNotifications.size();
                     for (int i = 0; i < N; i++) {
-                        final NotificationRecord n = mEnqueuedNotifications.get(i);
-                        if (Objects.equals(adjustment.getKey(), n.getKey())
-                                && Objects.equals(adjustment.getUser(), n.getUserId())) {
-                            applyAdjustment(n, adjustment);
+                        final NotificationRecord r = mEnqueuedNotifications.get(i);
+                        if (Objects.equals(adjustment.getKey(), r.getKey())
+                                && Objects.equals(adjustment.getUser(), r.getUserId())
+                                && mAssistants.isSameUser(token, r.getUserId())) {
+                            applyAdjustment(r, adjustment);
+                            r.applyAdjustments();
+                            foundEnqueued = true;
                             break;
                         }
                     }
+                    if (!foundEnqueued) {
+                        // adjustment arrived too late to apply to enqueued; apply to posted
+                        applyAdjustmentFromAssistant(token, adjustment);
+                    }
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -3430,34 +3474,32 @@
 
         @Override
         public void applyAdjustmentFromAssistant(INotificationListener token,
-                Adjustment adjustment) throws RemoteException {
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mNotificationLock) {
-                    mAssistants.checkServiceTokenLocked(token);
-                    NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
-                    applyAdjustment(n, adjustment);
-                }
-                mRankingHandler.requestSort();
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
+                Adjustment adjustment) {
+            List<Adjustment> adjustments = new ArrayList<>();
+            adjustments.add(adjustment);
+            applyAdjustmentsFromAssistant(token, adjustments);
         }
 
         @Override
         public void applyAdjustmentsFromAssistant(INotificationListener token,
-                List<Adjustment> adjustments) throws RemoteException {
+                List<Adjustment> adjustments) {
 
             final long identity = Binder.clearCallingIdentity();
             try {
+                boolean appliedAdjustment = false;
                 synchronized (mNotificationLock) {
                     mAssistants.checkServiceTokenLocked(token);
                     for (Adjustment adjustment : adjustments) {
-                        NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
-                        applyAdjustment(n, adjustment);
+                        NotificationRecord r = mNotificationsByKey.get(adjustment.getKey());
+                        if (r != null && mAssistants.isSameUser(token, r.getUserId())) {
+                            applyAdjustment(r, adjustment);
+                            appliedAdjustment = true;
+                        }
                     }
                 }
-                mRankingHandler.requestSort();
+                if (appliedAdjustment) {
+                    mRankingHandler.requestSort();
+                }
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -3492,7 +3534,7 @@
             Preconditions.checkNotNull(user);
             verifyPrivilegedListener(token, user);
 
-            return mRankingHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
+            return mPreferencesHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
                     false /* includeDeleted */);
         }
 
@@ -3505,7 +3547,7 @@
             verifyPrivilegedListener(token, user);
 
             List<NotificationChannelGroup> groups = new ArrayList<>();
-            groups.addAll(mRankingHelper.getNotificationChannelGroups(
+            groups.addAll(mPreferencesHelper.getNotificationChannelGroups(
                     pkg, getUidForPackageAndUser(pkg, user)));
             return new ParceledListSlice<>(groups);
         }
@@ -3686,10 +3728,10 @@
         JSONObject dump = new JSONObject();
         try {
             dump.put("service", "Notification Manager");
-            dump.put("bans", mRankingHelper.dumpBansJson(filter));
-            dump.put("ranking", mRankingHelper.dumpJson(filter));
+            dump.put("bans", mPreferencesHelper.dumpBansJson(filter));
+            dump.put("ranking", mPreferencesHelper.dumpJson(filter));
             dump.put("stats", mUsageStats.dumpJson(filter));
-            dump.put("channels", mRankingHelper.dumpChannelsJson(filter));
+            dump.put("channels", mPreferencesHelper.dumpChannelsJson(filter));
         } catch (JSONException e) {
             e.printStackTrace();
         }
@@ -3762,6 +3804,7 @@
 
             long rankingToken = proto.start(NotificationServiceDumpProto.RANKING_CONFIG);
             mRankingHelper.dump(proto, filter);
+            mPreferencesHelper.dump(proto, filter);
             proto.end(rankingToken);
         }
 
@@ -3828,6 +3871,7 @@
                         pw.println("  ");
                     }
                     pw.println("  mUseAttentionLight=" + mUseAttentionLight);
+                    pw.println("  mHasLight=" + mHasLight);
                     pw.println("  mNotificationPulseEnabled=" + mNotificationPulseEnabled);
                     pw.println("  mSoundNotificationKey=" + mSoundNotificationKey);
                     pw.println("  mVibrateNotificationKey=" + mVibrateNotificationKey);
@@ -3869,6 +3913,9 @@
                 pw.println("\n  Ranking Config:");
                 mRankingHelper.dump(pw, "    ", filter);
 
+                pw.println("\n Notification Preferences:");
+                mPreferencesHelper.dump(pw, "    ", filter);
+
                 pw.println("\n  Notification listeners:");
                 mListeners.dump(pw, filter);
                 pw.print("    mListenerHints: "); pw.println(mListenerHints);
@@ -3932,7 +3979,7 @@
         @Override
         public NotificationChannel getNotificationChannel(String pkg, int uid, String
                 channelId) {
-            return mRankingHelper.getNotificationChannel(pkg, uid, channelId, false);
+            return mPreferencesHelper.getNotificationChannel(pkg, uid, channelId, false);
         }
 
         @Override
@@ -3988,6 +4035,7 @@
                     + " notification=" + notification);
         }
         checkCallerIsSystemOrSameApp(pkg);
+        checkRestrictedCategories(notification);
 
         final int userId = ActivityManager.handleIncomingUser(callingPid,
                 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
@@ -4028,7 +4076,7 @@
         if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
             channelId = (new Notification.TvExtender(notification)).getChannelId();
         }
-        final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
+        final NotificationChannel channel = mPreferencesHelper.getNotificationChannel(pkg,
                 notificationUid, channelId, false /* includeDeleted */);
         if (channel == null) {
             final String noChannelStr = "No Channel found for "
@@ -4043,7 +4091,7 @@
                     + ", notificationUid=" + notificationUid
                     + ", notification=" + notification;
             Log.e(TAG, noChannelStr);
-            boolean appNotificationsOff = mRankingHelper.getImportance(pkg, notificationUid)
+            boolean appNotificationsOff = mPreferencesHelper.getImportance(pkg, notificationUid)
                     == NotificationManager.IMPORTANCE_NONE;
 
             if (!appNotificationsOff) {
@@ -4058,7 +4106,7 @@
                 pkg, opPkg, id, tag, notificationUid, callingPid, notification,
                 user, null, System.currentTimeMillis());
         final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
-        r.setIsAppImportanceLocked(mRankingHelper.getIsAppImportanceLocked(pkg, callingUid));
+        r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid));
 
         if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
             final boolean fgServiceShown = channel.isFgServiceShown();
@@ -4077,7 +4125,7 @@
                         channel.unlockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
                         channel.setFgServiceShown(true);
                     }
-                    mRankingHelper.updateNotificationChannel(pkg, notificationUid, channel, false);
+                    mPreferencesHelper.updateNotificationChannel(pkg, notificationUid, channel, false);
                     r.updateNotificationChannel(channel);
                 }
             } else if (!fgServiceShown && !TextUtils.isEmpty(channelId)
@@ -4252,8 +4300,8 @@
             return isPackageSuspended;
         }
         final boolean isBlocked =
-                mRankingHelper.isGroupBlocked(pkg, callingUid, r.getChannel().getGroup())
-                || mRankingHelper.getImportance(pkg, callingUid)
+                mPreferencesHelper.isGroupBlocked(pkg, callingUid, r.getChannel().getGroup())
+                || mPreferencesHelper.getImportance(pkg, callingUid)
                         == NotificationManager.IMPORTANCE_NONE
                 || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
         if (isBlocked) {
@@ -4525,6 +4573,15 @@
     @GuardedBy("mNotificationLock")
     @VisibleForTesting
     protected boolean isVisuallyInterruptive(NotificationRecord old, NotificationRecord r) {
+        // Ignore summary updates because we don't display most of the information.
+        if (r.sbn.isGroup() && r.sbn.getNotification().isGroupSummary()) {
+            if (DEBUG_INTERRUPTIVENESS) {
+                Log.v(TAG, "INTERRUPTIVENESS: "
+                        +  r.getKey() + " is not interruptive: summary");
+            }
+            return false;
+        }
+
         if (old == null) {
             if (DEBUG_INTERRUPTIVENESS) {
                 Log.v(TAG, "INTERRUPTIVENESS: "
@@ -4562,15 +4619,6 @@
             return false;
         }
 
-        // Ignore summary updates because we don't display most of the information.
-        if (r.sbn.isGroup() && r.sbn.getNotification().isGroupSummary()) {
-            if (DEBUG_INTERRUPTIVENESS) {
-                Log.v(TAG, "INTERRUPTIVENESS: "
-                        +  r.getKey() + " is not interruptive: summary");
-            }
-            return false;
-        }
-
         final String oldTitle = String.valueOf(oldN.extras.get(Notification.EXTRA_TITLE));
         final String newTitle = String.valueOf(newN.extras.get(Notification.EXTRA_TITLE));
         if (!Objects.equals(oldTitle, newTitle)) {
@@ -4810,6 +4858,8 @@
 
                         buzz = playVibration(record, vibration, hasValidSound);
                     }
+                } else if ((record.getFlags() & Notification.FLAG_INSISTENT) != 0) {
+                    hasValidSound = false;
                 }
             }
         }
@@ -4825,8 +4875,7 @@
         // light
         // release the light
         boolean wasShowLights = mLights.remove(key);
-        if (record.getLight() != null && aboveThreshold
-                && ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) == 0)) {
+        if (canShowLightsLocked(record, aboveThreshold)) {
             mLights.add(key);
             updateLightsLocked();
             if (mUseAttentionLight) {
@@ -4837,7 +4886,19 @@
             updateLightsLocked();
         }
         if (buzz || beep || blink) {
-            record.setInterruptive(true);
+            // Ignore summary updates because we don't display most of the information.
+            if (record.sbn.isGroup() && record.sbn.getNotification().isGroupSummary()) {
+                if (DEBUG_INTERRUPTIVENESS) {
+                    Log.v(TAG, "INTERRUPTIVENESS: "
+                            + record.getKey() + " is not interruptive: summary");
+                }
+            } else {
+                if (DEBUG_INTERRUPTIVENESS) {
+                    Log.v(TAG, "INTERRUPTIVENESS: "
+                            + record.getKey() + " is interruptive: alerted");
+                }
+                record.setInterruptive(true);
+            }
             MetricsLogger.action(record.getLogMaker()
                     .setCategory(MetricsEvent.NOTIFICATION_ALERT)
                     .setType(MetricsEvent.TYPE_OPEN)
@@ -4847,11 +4908,49 @@
     }
 
     @GuardedBy("mNotificationLock")
+    boolean canShowLightsLocked(final NotificationRecord record, boolean aboveThreshold) {
+        // device lacks light
+        if (!mHasLight) {
+            return false;
+        }
+        // user turned lights off globally
+        if (!mNotificationPulseEnabled) {
+            return false;
+        }
+        // the notification/channel has no light
+        if (record.getLight() == null) {
+            return false;
+        }
+        // unimportant notification
+        if (!aboveThreshold) {
+            return false;
+        }
+        // suppressed due to DND
+        if ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) != 0) {
+            return false;
+        }
+        // Suppressed because it's a silent update
+        final Notification notification = record.getNotification();
+        if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
+            return false;
+        }
+        // Suppressed because another notification in its group handles alerting
+        if (record.sbn.isGroup() && record.getNotification().suppressAlertingDueToGrouping()) {
+            return false;
+        }
+        // not if in call or the screen's on
+        if (mInCall || mScreenOn) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @GuardedBy("mNotificationLock")
     boolean shouldMuteNotificationLocked(final NotificationRecord record) {
         // Suppressed because it's a silent update
         final Notification notification = record.getNotification();
-        if(record.isUpdate
-                && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
+        if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
             return true;
         }
 
@@ -4938,11 +5037,11 @@
                         Thread.sleep(waitMs);
                     } catch (InterruptedException e) { }
                     mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
-                            effect, record.getAudioAttributes());
+                            effect, "Notification (delayed)", record.getAudioAttributes());
                 }).start();
             } else {
                 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
-                        effect, record.getAudioAttributes());
+                        effect, "Notification", record.getAudioAttributes());
             }
             return true;
         } finally{
@@ -4964,27 +5063,31 @@
     }
 
     protected void playInCallNotification() {
-        new Thread() {
-            @Override
-            public void run() {
-                final long identity = Binder.clearCallingIdentity();
-                try {
-                    final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
-                    if (player != null) {
-                        if (mCallNotificationToken != null) {
-                            player.stop(mCallNotificationToken);
+        if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL
+                && Settings.Secure.getInt(getContext().getContentResolver(),
+                Settings.Secure.IN_CALL_NOTIFICATION_ENABLED, 1) != 0) {
+            new Thread() {
+                @Override
+                public void run() {
+                    final long identity = Binder.clearCallingIdentity();
+                    try {
+                        final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
+                        if (player != null) {
+                            if (mCallNotificationToken != null) {
+                                player.stop(mCallNotificationToken);
+                            }
+                            mCallNotificationToken = new Binder();
+                            player.play(mCallNotificationToken, mInCallNotificationUri,
+                                    mInCallNotificationAudioAttributes,
+                                    mInCallNotificationVolume, false);
                         }
-                        mCallNotificationToken = new Binder();
-                        player.play(mCallNotificationToken, mInCallNotificationUri,
-                                mInCallNotificationAudioAttributes,
-                                mInCallNotificationVolume, false);
+                    } catch (RemoteException e) {
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
                     }
-                } catch (RemoteException e) {
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
                 }
-            }
-        }.start();
+            }.start();
+        }
     }
 
     @GuardedBy("mToastQueue")
@@ -5173,6 +5276,8 @@
             ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
             ArrayList<Integer> userSentimentBefore = new ArrayList<>(N);
             ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N);
+            ArrayList<ArrayList<Notification.Action>> smartActionsBefore = new ArrayList<>(N);
+            ArrayList<ArrayList<CharSequence>> smartRepliesBefore = new ArrayList<>(N);
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
                 orderBefore.add(r.getKey());
@@ -5184,6 +5289,8 @@
                 snoozeCriteriaBefore.add(r.getSnoozeCriteria());
                 userSentimentBefore.add(r.getUserSentiment());
                 suppressVisuallyBefore.add(r.getSuppressedVisualEffects());
+                smartActionsBefore.add(r.getSmartActions());
+                smartRepliesBefore.add(r.getSmartReplies());
                 mRankingHelper.extractSignals(r);
             }
             mRankingHelper.sort(mNotificationList);
@@ -5198,7 +5305,9 @@
                         || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())
                         || !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())
                         || !Objects.equals(suppressVisuallyBefore.get(i),
-                        r.getSuppressedVisualEffects())) {
+                        r.getSuppressedVisualEffects())
+                        || !Objects.equals(smartActionsBefore.get(i), r.getSmartActions())
+                        || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())) {
                     mHandler.scheduleSendRankingUpdate();
                     return;
                 }
@@ -5528,12 +5637,8 @@
 
         // If we have Uris to grant, but no owner yet, go create one
         if (newUris != null && permissionOwner == null) {
-            try {
-                if (DBG) Slog.d(TAG, key + ": creating owner");
-                permissionOwner = mAm.newUriPermissionOwner("NOTIF:" + key);
-            } catch (RemoteException ignored) {
-                // Ignored because we're in same process
-            }
+            if (DBG) Slog.d(TAG, key + ": creating owner");
+            permissionOwner = mUgmInternal.newUriPermissionOwner("NOTIF:" + key);
         }
 
         // If we have no Uris to grant, but an existing owner, go destroy it
@@ -5541,11 +5646,9 @@
             final long ident = Binder.clearCallingIdentity();
             try {
                 if (DBG) Slog.d(TAG, key + ": destroying owner");
-                mAm.revokeUriPermissionFromOwner(permissionOwner, null, ~0,
+                mUgmInternal.revokeUriPermissionFromOwner(permissionOwner, null, ~0,
                         UserHandle.getUserId(oldRecord.getUid()));
                 permissionOwner = null;
-            } catch (RemoteException ignored) {
-                // Ignored because we're in same process
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -5585,7 +5688,7 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            mAm.grantUriPermissionFromOwner(owner, sourceUid, targetPkg,
+            mUgm.grantUriPermissionFromOwner(owner, sourceUid, targetPkg,
                     ContentProvider.getUriWithoutUserId(uri),
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)),
@@ -5602,12 +5705,11 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            mAm.revokeUriPermissionFromOwner(owner,
+            mUgmInternal.revokeUriPermissionFromOwner(
+                    owner,
                     ContentProvider.getUriWithoutUserId(uri),
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
-        } catch (RemoteException ignored) {
-            // Ignored because we're in same process
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -6103,6 +6205,26 @@
         checkCallerIsSameApp(pkg);
     }
 
+    /**
+     * Check if the notification is of a category type that is restricted to system use only,
+     * if so throw SecurityException
+     */
+    private void checkRestrictedCategories(final Notification notification) {
+        try {
+            if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0)) {
+                return;
+            }
+        } catch (RemoteException re) {
+            if (DBG) Log.e(TAG, "Unable to confirm if it's safe to skip category "
+                    + "restrictions check thus the check will be done anyway");
+        }
+        if (Notification.CATEGORY_CAR_EMERGENCY.equals(notification.category)
+                || Notification.CATEGORY_CAR_WARNING.equals(notification.category)
+                || Notification.CATEGORY_CAR_INFORMATION.equals(notification.category)) {
+                    checkCallerIsSystem();
+        }
+    }
+
     private boolean isCallerInstantApp(String pkg) {
         // System is always allowed to act for ephemeral apps.
         if (isCallerSystemOrPhone()) {
@@ -6181,6 +6303,8 @@
         Bundle showBadge = new Bundle();
         Bundle userSentiment = new Bundle();
         Bundle hidden = new Bundle();
+        Bundle smartActions = new Bundle();
+        Bundle smartReplies = new Bundle();
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
             if (!isVisibleToListener(record.sbn, info)) {
@@ -6208,6 +6332,8 @@
             showBadge.putBoolean(key, record.canShowBadge());
             userSentiment.putInt(key, record.getUserSentiment());
             hidden.putBoolean(key, record.isHidden());
+            smartActions.putParcelableArrayList(key, record.getSmartActions());
+            smartReplies.putCharSequenceArrayList(key, record.getSmartReplies());
         }
         final int M = keys.size();
         String[] keysAr = keys.toArray(new String[M]);
@@ -6218,7 +6344,8 @@
         }
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
                 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
-                channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden);
+                channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden,
+                smartActions, smartReplies);
     }
 
     boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -6370,7 +6497,8 @@
             // There should be only one, but it's a list, so while we enforce
             // singularity elsewhere, we keep it general here, to avoid surprises.
             for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
-                boolean sbnVisible = isVisibleToListener(sbn, info);
+                boolean sbnVisible = isVisibleToListener(sbn, info)
+                        && info.isSameUser(r.getUserId());
                 if (!sbnVisible) {
                     continue;
                 }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 0f3f44e..469828b 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -70,6 +70,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import java.io.PrintWriter;
 import java.lang.reflect.Array;
@@ -95,6 +96,8 @@
     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
     private static final int MAX_LOGTAG_LENGTH = 35;
     final StatusBarNotification sbn;
+    IActivityManager mAm;
+    UriGrantsManagerInternal mUgmInternal;
     final int mTargetSdkVersion;
     final int mOriginalFlags;
     private final Context mContext;
@@ -127,6 +130,11 @@
     // The most recent update time, or the creation time if no updates.
     private 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;
+
     // Is this record an update of an old record?
     public boolean isUpdate;
     private int mPackagePriority;
@@ -153,6 +161,8 @@
     private Light mLight;
     private String mGroupLogTag;
     private String mChannelIdLogTag;
+    private ArrayList<Notification.Action> mSmartActions;
+    private ArrayList<CharSequence> mSmartReplies;
 
     private final List<Adjustment> mAdjustments;
     private final NotificationStats mStats;
@@ -174,10 +184,13 @@
         this.sbn = sbn;
         mTargetSdkVersion = LocalServices.getService(PackageManagerInternal.class)
                 .getPackageTargetSdkVersion(sbn.getPackageName());
+        mAm = ActivityManager.getService();
+        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
         mOriginalFlags = sbn.getNotification().flags;
         mRankingTimeMs = calculateRankingTimeMs(0L);
         mCreationTimeMs = sbn.getPostTime();
         mUpdateTimeMs = mCreationTimeMs;
+        mInterruptionTimeMs = mCreationTimeMs;
         mContext = context;
         stats = new NotificationUsageStats.SingleNotificationStats();
         mChannel = channel;
@@ -435,6 +448,8 @@
         pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
         pw.println(prefix + "contentIntent=" + notification.contentIntent);
         pw.println(prefix + "deleteIntent=" + notification.deleteIntent);
+        pw.println(prefix + "number=" + notification.number);
+        pw.println(prefix + "groupAlertBehavior=" + notification.getGroupAlertBehavior());
 
         pw.print(prefix + "tickerText=");
         if (!TextUtils.isEmpty(notification.tickerText)) {
@@ -523,6 +538,7 @@
         pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
         pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
         pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
+        pw.println(prefix + "mInterruptionTimeMs=" + mInterruptionTimeMs);
         pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
         if (mPreChannelsNotification) {
             pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
@@ -619,6 +635,12 @@
                                 Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
                     }
                 }
+                if (signals.containsKey(Adjustment.KEY_SMART_ACTIONS)) {
+                    setSmartActions(signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS));
+                }
+                if (signals.containsKey(Adjustment.KEY_SMART_REPLIES)) {
+                    setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES));
+                }
             }
         }
     }
@@ -784,6 +806,10 @@
         return mVisibleSinceMs == 0 ? 0 : (int) (now - mVisibleSinceMs);
     }
 
+    public int getInterruptionMs(long now) {
+        return (int) (now - mInterruptionTimeMs);
+    }
+
     /**
      * Set the visibility of the notification.
      */
@@ -842,7 +868,7 @@
     public void setSeen() {
         mStats.setSeen();
         if (mTextChanged) {
-            mIsInterruptive = true;
+            setInterruptive(true);
         }
     }
 
@@ -938,6 +964,17 @@
 
     public void setInterruptive(boolean interruptive) {
         mIsInterruptive = interruptive;
+        final long now = System.currentTimeMillis();
+        mInterruptionTimeMs = interruptive ? now : mInterruptionTimeMs;
+
+        if (interruptive) {
+            MetricsLogger.action(getLogMaker()
+                    .setCategory(MetricsEvent.NOTIFICATION_INTERRUPTION)
+                    .setType(MetricsEvent.TYPE_OPEN)
+                    .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS,
+                            getInterruptionMs(now)));
+            MetricsLogger.histogram(mContext, "note_interruptive", getInterruptionMs(now));
+        }
     }
 
     public void setTextChanged(boolean textChanged) {
@@ -1023,6 +1060,22 @@
         mHasSeenSmartReplies = hasSeenSmartReplies;
     }
 
+    public void setSmartActions(ArrayList<Notification.Action> smartActions) {
+        mSmartActions = smartActions;
+    }
+
+    public ArrayList<Notification.Action> getSmartActions() {
+        return mSmartActions;
+    }
+
+    public void setSmartReplies(ArrayList<CharSequence> smartReplies) {
+        mSmartReplies = smartReplies;
+    }
+
+    public ArrayList<CharSequence> getSmartReplies() {
+        return mSmartReplies;
+    }
+
     /**
      * @return all {@link Uri} that should have permission granted to whoever
      *         will be rendering it. This list has already been vetted to only
@@ -1036,16 +1089,17 @@
      * Collect all {@link Uri} that should have permission granted to whoever
      * will be rendering it.
      */
-    private void calculateGrantableUris() {
+    protected void calculateGrantableUris() {
         final Notification notification = getNotification();
         notification.visitUris((uri) -> {
-            visitGrantableUri(uri);
+            visitGrantableUri(uri, false);
         });
 
         if (notification.getChannelId() != null) {
             NotificationChannel channel = getChannel();
             if (channel != null) {
-                visitGrantableUri(channel.getSound());
+                visitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
+                        & NotificationChannel.USER_LOCKED_SOUND) != 0);
             }
         }
     }
@@ -1058,18 +1112,17 @@
      * {@link #mGrantableUris}. Otherwise, this will either log or throw
      * {@link SecurityException} depending on target SDK of enqueuing app.
      */
-    private void visitGrantableUri(Uri uri) {
+    private void visitGrantableUri(Uri uri, boolean userOverriddenUri) {
         if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
 
         // We can't grant Uri permissions from system
         final int sourceUid = sbn.getUid();
         if (sourceUid == android.os.Process.SYSTEM_UID) return;
 
-        final IActivityManager am = ActivityManager.getService();
         final long ident = Binder.clearCallingIdentity();
         try {
             // This will throw SecurityException if caller can't grant
-            am.checkGrantUriPermission(sourceUid, null,
+            mUgmInternal.checkGrantUriPermission(sourceUid, null,
                     ContentProvider.getUriWithoutUserId(uri),
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
@@ -1078,13 +1131,13 @@
                 mGrantableUris = new ArraySet<>();
             }
             mGrantableUris.add(uri);
-        } catch (RemoteException ignored) {
-            // Ignored because we're in same process
         } catch (SecurityException e) {
-            if (mTargetSdkVersion >= Build.VERSION_CODES.P) {
-                throw e;
-            } else {
-                Log.w(TAG, "Ignoring " + uri + " from " + sourceUid + ": " + e.getMessage());
+            if (!userOverriddenUri) {
+                if (mTargetSdkVersion >= Build.VERSION_CODES.P) {
+                    throw e;
+                } else {
+                    Log.w(TAG, "Ignoring " + uri + " from " + sourceUid + ": " + e.getMessage());
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -1112,7 +1165,9 @@
                         sbn.getNotification().isGroupSummary() ? 1 : 0)
                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
-                .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now));
+                .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now))
+                .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS,
+                        getInterruptionMs(now));
     }
 
     public LogMaker getLogMaker() {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
new file mode 100644
index 0000000..432d17c
--- /dev/null
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -0,0 +1,1372 @@
+/**
+ * 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 android.app.NotificationManager.IMPORTANCE_NONE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.metrics.LogMaker;
+import android.os.Build;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.RankingHelperProto;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class PreferencesHelper implements RankingConfig {
+    private static final String TAG = "NotificationPrefHelper";
+    private static final int XML_VERSION = 1;
+
+    @VisibleForTesting
+    static final String TAG_RANKING = "ranking";
+    private static final String TAG_PACKAGE = "package";
+    private static final String TAG_CHANNEL = "channel";
+    private static final String TAG_GROUP = "channelGroup";
+
+    private static final String ATT_VERSION = "version";
+    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_PRIORITY = "priority";
+    private static final String ATT_VISIBILITY = "visibility";
+    private static final String ATT_IMPORTANCE = "importance";
+    private static final String ATT_SHOW_BADGE = "show_badge";
+    private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
+
+    private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
+    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;
+    /**
+     * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
+     * fields.
+     */
+    private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
+
+    /**
+     * All user-lockable fields for a given application.
+     */
+    @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
+    public @interface LockableAppFields {
+        int USER_LOCKED_IMPORTANCE = 0x00000001;
+    }
+
+    // pkg|uid => PackagePreferences
+    private final ArrayMap<String, PackagePreferences> mPackagePreferencess = new ArrayMap<>();
+    // pkg => PackagePreferences
+    private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
+
+
+    private final Context mContext;
+    private final PackageManager mPm;
+    private final RankingHandler mRankingHandler;
+    private final ZenModeHelper mZenModeHelper;
+
+    private SparseBooleanArray mBadgingEnabled;
+    private boolean mAreChannelsBypassingDnd;
+
+
+    public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
+            ZenModeHelper zenHelper) {
+        mContext = context;
+        mZenModeHelper = zenHelper;
+        mRankingHandler = rankingHandler;
+        mPm = pm;
+
+        updateBadgingEnabled();
+
+        mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state &
+                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
+        updateChannelsBypassingDnd();
+
+    }
+
+    public void readXml(XmlPullParser parser, boolean forRestore)
+            throws XmlPullParserException, IOException {
+        int type = parser.getEventType();
+        if (type != XmlPullParser.START_TAG) return;
+        String tag = parser.getName();
+        if (!TAG_RANKING.equals(tag)) return;
+        // Clobber groups and channels with the xml, but don't delete other data that wasn't present
+        // at the time of serialization.
+        mRestoredWithoutUids.clear();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            tag = parser.getName();
+            if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
+                return;
+            }
+            if (type == XmlPullParser.START_TAG) {
+                if (TAG_PACKAGE.equals(tag)) {
+                    int uid = XmlUtils.readIntAttribute(parser, ATT_UID,
+                            PackagePreferences.UNKNOWN_UID);
+                    String name = parser.getAttributeValue(null, ATT_NAME);
+                    if (!TextUtils.isEmpty(name)) {
+                        if (forRestore) {
+                            try {
+                                //TODO: http://b/22388012
+                                uid = mPm.getPackageUidAsUser(name,
+                                        UserHandle.USER_SYSTEM);
+                            } catch (PackageManager.NameNotFoundException e) {
+                                // noop
+                            }
+                        }
+
+                        PackagePreferences r = getOrCreatePackagePreferences(name, uid,
+                                XmlUtils.readIntAttribute(
+                                        parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
+                                XmlUtils.readIntAttribute(parser, ATT_PRIORITY, DEFAULT_PRIORITY),
+                                XmlUtils.readIntAttribute(
+                                        parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
+                                XmlUtils.readBooleanAttribute(
+                                        parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE));
+                        r.importance = XmlUtils.readIntAttribute(
+                                parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
+                        r.priority = XmlUtils.readIntAttribute(
+                                parser, ATT_PRIORITY, DEFAULT_PRIORITY);
+                        r.visibility = XmlUtils.readIntAttribute(
+                                parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
+                        r.showBadge = XmlUtils.readBooleanAttribute(
+                                parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
+                        r.lockedAppFields = XmlUtils.readIntAttribute(parser,
+                                ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
+
+                        final int innerDepth = parser.getDepth();
+                        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                                && (type != XmlPullParser.END_TAG
+                                || parser.getDepth() > innerDepth)) {
+                            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                                continue;
+                            }
+
+                            String tagName = parser.getName();
+                            // Channel groups
+                            if (TAG_GROUP.equals(tagName)) {
+                                String id = parser.getAttributeValue(null, ATT_ID);
+                                CharSequence groupName = parser.getAttributeValue(null, ATT_NAME);
+                                if (!TextUtils.isEmpty(id)) {
+                                    NotificationChannelGroup group
+                                            = new NotificationChannelGroup(id, groupName);
+                                    group.populateFromXml(parser);
+                                    r.groups.put(id, group);
+                                }
+                            }
+                            // Channels
+                            if (TAG_CHANNEL.equals(tagName)) {
+                                String id = parser.getAttributeValue(null, ATT_ID);
+                                String channelName = parser.getAttributeValue(null, ATT_NAME);
+                                int channelImportance = XmlUtils.readIntAttribute(
+                                        parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
+                                if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
+                                    NotificationChannel channel = new NotificationChannel(id,
+                                            channelName, channelImportance);
+                                    if (forRestore) {
+                                        channel.populateFromXmlForRestore(parser, mContext);
+                                    } else {
+                                        channel.populateFromXml(parser);
+                                    }
+                                    r.channels.put(id, channel);
+                                }
+                            }
+                        }
+
+                        try {
+                            deleteDefaultChannelIfNeeded(r);
+                        } catch (PackageManager.NameNotFoundException e) {
+                            Slog.e(TAG, "deleteDefaultChannelIfNeeded - Exception: " + e);
+                        }
+                    }
+                }
+            }
+        }
+        throw new IllegalStateException("Failed to reach END_DOCUMENT");
+    }
+
+    private PackagePreferences getPackagePreferences(String pkg, int uid) {
+        final String key = packagePreferencesKey(pkg, uid);
+        synchronized (mPackagePreferencess) {
+            return mPackagePreferencess.get(key);
+        }
+    }
+
+    private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) {
+        return getOrCreatePackagePreferences(pkg, uid,
+                DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE);
+    }
+
+    private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance,
+            int priority, int visibility, boolean showBadge) {
+        final String key = packagePreferencesKey(pkg, uid);
+        synchronized (mPackagePreferencess) {
+            PackagePreferences
+                    r = (uid == PackagePreferences.UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
+                    : mPackagePreferencess.get(key);
+            if (r == null) {
+                r = new PackagePreferences();
+                r.pkg = pkg;
+                r.uid = uid;
+                r.importance = importance;
+                r.priority = priority;
+                r.visibility = visibility;
+                r.showBadge = showBadge;
+
+                try {
+                    createDefaultChannelIfNeeded(r);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Slog.e(TAG, "createDefaultChannelIfNeeded - Exception: " + e);
+                }
+
+                if (r.uid == PackagePreferences.UNKNOWN_UID) {
+                    mRestoredWithoutUids.put(pkg, r);
+                } else {
+                    mPackagePreferencess.put(key, r);
+                }
+            }
+            return r;
+        }
+    }
+
+    private boolean shouldHaveDefaultChannel(PackagePreferences r) throws
+            PackageManager.NameNotFoundException {
+        final int userId = UserHandle.getUserId(r.uid);
+        final ApplicationInfo applicationInfo =
+                mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
+        if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
+            // O apps should not have the default channel.
+            return false;
+        }
+
+        // Otherwise, this app should have the default channel.
+        return true;
+    }
+
+    private void deleteDefaultChannelIfNeeded(PackagePreferences r) throws
+            PackageManager.NameNotFoundException {
+        if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
+            // Not present
+            return;
+        }
+
+        if (shouldHaveDefaultChannel(r)) {
+            // Keep the default channel until upgraded.
+            return;
+        }
+
+        // Remove Default Channel.
+        r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
+    }
+
+    private void createDefaultChannelIfNeeded(PackagePreferences r) throws
+            PackageManager.NameNotFoundException {
+        if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
+            r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
+                    com.android.internal.R.string.default_notification_channel_label));
+            return;
+        }
+
+        if (!shouldHaveDefaultChannel(r)) {
+            // Keep the default channel until upgraded.
+            return;
+        }
+
+        // Create Default Channel
+        NotificationChannel channel;
+        channel = new NotificationChannel(
+                NotificationChannel.DEFAULT_CHANNEL_ID,
+                mContext.getString(R.string.default_notification_channel_label),
+                r.importance);
+        channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
+        channel.setLockscreenVisibility(r.visibility);
+        if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
+            channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+        }
+        if (r.priority != DEFAULT_PRIORITY) {
+            channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
+        }
+        if (r.visibility != DEFAULT_VISIBILITY) {
+            channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
+        }
+        r.channels.put(channel.getId(), channel);
+    }
+
+    public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
+        out.startTag(null, TAG_RANKING);
+        out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
+
+        synchronized (mPackagePreferencess) {
+            final int N = mPackagePreferencess.size();
+            for (int i = 0; i < N; i++) {
+                final PackagePreferences r = mPackagePreferencess.valueAt(i);
+                //TODO: http://b/22388012
+                if (forBackup && UserHandle.getUserId(r.uid) != UserHandle.USER_SYSTEM) {
+                    continue;
+                }
+                final boolean hasNonDefaultSettings =
+                        r.importance != DEFAULT_IMPORTANCE
+                                || r.priority != DEFAULT_PRIORITY
+                                || r.visibility != DEFAULT_VISIBILITY
+                                || r.showBadge != DEFAULT_SHOW_BADGE
+                                || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
+                                || r.channels.size() > 0
+                                || r.groups.size() > 0;
+                if (hasNonDefaultSettings) {
+                    out.startTag(null, TAG_PACKAGE);
+                    out.attribute(null, ATT_NAME, r.pkg);
+                    if (r.importance != DEFAULT_IMPORTANCE) {
+                        out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
+                    }
+                    if (r.priority != DEFAULT_PRIORITY) {
+                        out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
+                    }
+                    if (r.visibility != DEFAULT_VISIBILITY) {
+                        out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
+                    }
+                    out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
+                    out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
+                            Integer.toString(r.lockedAppFields));
+
+                    if (!forBackup) {
+                        out.attribute(null, ATT_UID, Integer.toString(r.uid));
+                    }
+
+                    for (NotificationChannelGroup group : r.groups.values()) {
+                        group.writeXml(out);
+                    }
+
+                    for (NotificationChannel channel : r.channels.values()) {
+                        if (forBackup) {
+                            if (!channel.isDeleted()) {
+                                channel.writeXmlForBackup(out, mContext);
+                            }
+                        } else {
+                            channel.writeXml(out);
+                        }
+                    }
+
+                    out.endTag(null, TAG_PACKAGE);
+                }
+            }
+        }
+        out.endTag(null, TAG_RANKING);
+    }
+
+    /**
+     * Gets importance.
+     */
+    @Override
+    public int getImportance(String packageName, int uid) {
+        return getOrCreatePackagePreferences(packageName, uid).importance;
+    }
+
+
+    /**
+     * Returns whether the importance of the corresponding notification is user-locked and shouldn't
+     * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
+     * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
+     */
+    public boolean getIsAppImportanceLocked(String packageName, int uid) {
+        int userLockedFields = getOrCreatePackagePreferences(packageName, uid).lockedAppFields;
+        return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
+    }
+
+    @Override
+    public boolean canShowBadge(String packageName, int uid) {
+        return getOrCreatePackagePreferences(packageName, uid).showBadge;
+    }
+
+    @Override
+    public void setShowBadge(String packageName, int uid, boolean showBadge) {
+        getOrCreatePackagePreferences(packageName, uid).showBadge = showBadge;
+        updateConfig();
+    }
+
+    @Override
+    public boolean isGroupBlocked(String packageName, int uid, String groupId) {
+        if (groupId == null) {
+            return false;
+        }
+        PackagePreferences r = getOrCreatePackagePreferences(packageName, uid);
+        NotificationChannelGroup group = r.groups.get(groupId);
+        if (group == null) {
+            return false;
+        }
+        return group.isBlocked();
+    }
+
+    int getPackagePriority(String pkg, int uid) {
+        return getOrCreatePackagePreferences(pkg, uid).priority;
+    }
+
+    int getPackageVisibility(String pkg, int uid) {
+        return getOrCreatePackagePreferences(pkg, uid).visibility;
+    }
+
+    @Override
+    public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
+            boolean fromTargetApp) {
+        Preconditions.checkNotNull(pkg);
+        Preconditions.checkNotNull(group);
+        Preconditions.checkNotNull(group.getId());
+        Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName()));
+        PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
+        if (r == null) {
+            throw new IllegalArgumentException("Invalid package");
+        }
+        final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
+        if (!group.equals(oldGroup)) {
+            // will log for new entries as well as name/description changes
+            MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
+        }
+        if (oldGroup != null) {
+            group.setChannels(oldGroup.getChannels());
+
+            if (fromTargetApp) {
+                group.setBlocked(oldGroup.isBlocked());
+            }
+        }
+        r.groups.put(group.getId(), group);
+    }
+
+    @Override
+    public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
+            boolean fromTargetApp, boolean hasDndAccess) {
+        Preconditions.checkNotNull(pkg);
+        Preconditions.checkNotNull(channel);
+        Preconditions.checkNotNull(channel.getId());
+        Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
+        PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
+        if (r == null) {
+            throw new IllegalArgumentException("Invalid package");
+        }
+        if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
+            throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
+        }
+        if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
+            throw new IllegalArgumentException("Reserved id");
+        }
+        NotificationChannel existing = r.channels.get(channel.getId());
+        // Keep most of the existing settings
+        if (existing != null && fromTargetApp) {
+            if (existing.isDeleted()) {
+                existing.setDeleted(false);
+
+                // log a resurrected channel as if it's new again
+                MetricsLogger.action(getChannelLog(channel, pkg).setType(
+                        com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
+            }
+
+            existing.setName(channel.getName().toString());
+            existing.setDescription(channel.getDescription());
+            existing.setBlockableSystem(channel.isBlockableSystem());
+            if (existing.getGroup() == null) {
+                existing.setGroup(channel.getGroup());
+            }
+
+            // Apps are allowed to downgrade channel importance if the user has not changed any
+            // fields on this channel yet.
+            if (existing.getUserLockedFields() == 0 &&
+                    channel.getImportance() < existing.getImportance()) {
+                existing.setImportance(channel.getImportance());
+            }
+
+            // system apps and dnd access apps can bypass dnd if the user hasn't changed any
+            // fields on the channel yet
+            if (existing.getUserLockedFields() == 0 && hasDndAccess) {
+                boolean bypassDnd = channel.canBypassDnd();
+                existing.setBypassDnd(bypassDnd);
+
+                if (bypassDnd != mAreChannelsBypassingDnd) {
+                    updateChannelsBypassingDnd();
+                }
+            }
+
+            updateConfig();
+            return;
+        }
+        if (channel.getImportance() < IMPORTANCE_NONE
+                || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
+            throw new IllegalArgumentException("Invalid importance level");
+        }
+
+        // Reset fields that apps aren't allowed to set.
+        if (fromTargetApp && !hasDndAccess) {
+            channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
+        }
+        if (fromTargetApp) {
+            channel.setLockscreenVisibility(r.visibility);
+        }
+        clearLockedFields(channel);
+        if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
+            channel.setLockscreenVisibility(
+                    NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
+        }
+        if (!r.showBadge) {
+            channel.setShowBadge(false);
+        }
+
+        r.channels.put(channel.getId(), channel);
+        if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
+            updateChannelsBypassingDnd();
+        }
+        MetricsLogger.action(getChannelLog(channel, pkg).setType(
+                com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
+    }
+
+    void clearLockedFields(NotificationChannel channel) {
+        channel.unlockFields(channel.getUserLockedFields());
+    }
+
+    @Override
+    public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
+            boolean fromUser) {
+        Preconditions.checkNotNull(updatedChannel);
+        Preconditions.checkNotNull(updatedChannel.getId());
+        PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
+        if (r == null) {
+            throw new IllegalArgumentException("Invalid package");
+        }
+        NotificationChannel channel = r.channels.get(updatedChannel.getId());
+        if (channel == null || channel.isDeleted()) {
+            throw new IllegalArgumentException("Channel does not exist");
+        }
+        if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
+            updatedChannel.setLockscreenVisibility(
+                    NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
+        }
+        if (fromUser) {
+            updatedChannel.lockFields(channel.getUserLockedFields());
+            lockFieldsForUpdate(channel, updatedChannel);
+        } else {
+            updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
+        }
+        r.channels.put(updatedChannel.getId(), updatedChannel);
+
+        if (onlyHasDefaultChannel(pkg, uid)) {
+            // copy settings to app level so they are inherited by new channels
+            // when the app migrates
+            r.importance = updatedChannel.getImportance();
+            r.priority = updatedChannel.canBypassDnd()
+                    ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
+            r.visibility = updatedChannel.getLockscreenVisibility();
+            r.showBadge = updatedChannel.canShowBadge();
+        }
+
+        if (!channel.equals(updatedChannel)) {
+            // only log if there are real changes
+            MetricsLogger.action(getChannelLog(updatedChannel, pkg));
+        }
+
+        if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd) {
+            updateChannelsBypassingDnd();
+        }
+        updateConfig();
+    }
+
+    @Override
+    public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
+            boolean includeDeleted) {
+        Preconditions.checkNotNull(pkg);
+        PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
+        if (r == null) {
+            return null;
+        }
+        if (channelId == null) {
+            channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
+        }
+        final NotificationChannel nc = r.channels.get(channelId);
+        if (nc != null && (includeDeleted || !nc.isDeleted())) {
+            return nc;
+        }
+        return null;
+    }
+
+    @Override
+    public void deleteNotificationChannel(String pkg, int uid, String channelId) {
+        PackagePreferences r = getPackagePreferences(pkg, uid);
+        if (r == null) {
+            return;
+        }
+        NotificationChannel channel = r.channels.get(channelId);
+        if (channel != null) {
+            channel.setDeleted(true);
+            LogMaker lm = getChannelLog(channel, pkg);
+            lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
+            MetricsLogger.action(lm);
+
+            if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
+                updateChannelsBypassingDnd();
+            }
+        }
+    }
+
+    @Override
+    @VisibleForTesting
+    public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
+        Preconditions.checkNotNull(pkg);
+        Preconditions.checkNotNull(channelId);
+        PackagePreferences r = getPackagePreferences(pkg, uid);
+        if (r == null) {
+            return;
+        }
+        r.channels.remove(channelId);
+    }
+
+    @Override
+    public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
+        Preconditions.checkNotNull(pkg);
+        PackagePreferences r = getPackagePreferences(pkg, uid);
+        if (r == null) {
+            return;
+        }
+        int N = r.channels.size() - 1;
+        for (int i = N; i >= 0; i--) {
+            String key = r.channels.keyAt(i);
+            if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
+                r.channels.remove(key);
+            }
+        }
+    }
+
+    public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
+            int uid, String groupId, boolean includeDeleted) {
+        Preconditions.checkNotNull(pkg);
+        PackagePreferences r = getPackagePreferences(pkg, uid);
+        if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
+            return null;
+        }
+        NotificationChannelGroup group = r.groups.get(groupId).clone();
+        group.setChannels(new ArrayList<>());
+        int N = r.channels.size();
+        for (int i = 0; i < N; i++) {
+            final NotificationChannel nc = r.channels.valueAt(i);
+            if (includeDeleted || !nc.isDeleted()) {
+                if (groupId.equals(nc.getGroup())) {
+                    group.addChannel(nc);
+                }
+            }
+        }
+        return group;
+    }
+
+    public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
+            int uid) {
+        Preconditions.checkNotNull(pkg);
+        PackagePreferences r = getPackagePreferences(pkg, uid);
+        if (r == null) {
+            return null;
+        }
+        return r.groups.get(groupId);
+    }
+
+    @Override
+    public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
+            int uid, boolean includeDeleted, boolean includeNonGrouped) {
+        Preconditions.checkNotNull(pkg);
+        Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
+        PackagePreferences r = getPackagePreferences(pkg, uid);
+        if (r == null) {
+            return ParceledListSlice.emptyList();
+        }
+        NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
+        int N = r.channels.size();
+        for (int i = 0; i < N; i++) {
+            final NotificationChannel nc = r.channels.valueAt(i);
+            if (includeDeleted || !nc.isDeleted()) {
+                if (nc.getGroup() != null) {
+                    if (r.groups.get(nc.getGroup()) != null) {
+                        NotificationChannelGroup ncg = groups.get(nc.getGroup());
+                        if (ncg == null) {
+                            ncg = r.groups.get(nc.getGroup()).clone();
+                            ncg.setChannels(new ArrayList<>());
+                            groups.put(nc.getGroup(), ncg);
+
+                        }
+                        ncg.addChannel(nc);
+                    }
+                } else {
+                    nonGrouped.addChannel(nc);
+                }
+            }
+        }
+        if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
+            groups.put(null, nonGrouped);
+        }
+        return new ParceledListSlice<>(new ArrayList<>(groups.values()));
+    }
+
+    public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
+            String groupId) {
+        List<NotificationChannel> deletedChannels = new ArrayList<>();
+        PackagePreferences r = getPackagePreferences(pkg, uid);
+        if (r == null || TextUtils.isEmpty(groupId)) {
+            return deletedChannels;
+        }
+
+        r.groups.remove(groupId);
+
+        int N = r.channels.size();
+        for (int i = 0; i < N; i++) {
+            final NotificationChannel nc = r.channels.valueAt(i);
+            if (groupId.equals(nc.getGroup())) {
+                nc.setDeleted(true);
+                deletedChannels.add(nc);
+            }
+        }
+        return deletedChannels;
+    }
+
+    @Override
+    public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
+            int uid) {
+        PackagePreferences r = getPackagePreferences(pkg, uid);
+        if (r == null) {
+            return new ArrayList<>();
+        }
+        return r.groups.values();
+    }
+
+    @Override
+    public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
+            boolean includeDeleted) {
+        Preconditions.checkNotNull(pkg);
+        List<NotificationChannel> channels = new ArrayList<>();
+        PackagePreferences r = getPackagePreferences(pkg, uid);
+        if (r == null) {
+            return ParceledListSlice.emptyList();
+        }
+        int N = r.channels.size();
+        for (int i = 0; i < N; i++) {
+            final NotificationChannel nc = r.channels.valueAt(i);
+            if (includeDeleted || !nc.isDeleted()) {
+                channels.add(nc);
+            }
+        }
+        return new ParceledListSlice<>(channels);
+    }
+
+    /**
+     * True for pre-O apps that only have the default channel, or pre O apps that have no
+     * channels yet. This method will create the default channel for pre-O apps that don't have it.
+     * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
+     * upgrades.
+     */
+    public boolean onlyHasDefaultChannel(String pkg, int uid) {
+        PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
+        if (r.channels.size() == 1
+                && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
+            return true;
+        }
+        return false;
+    }
+
+    public int getDeletedChannelCount(String pkg, int uid) {
+        Preconditions.checkNotNull(pkg);
+        int deletedCount = 0;
+        PackagePreferences r = getPackagePreferences(pkg, uid);
+        if (r == null) {
+            return deletedCount;
+        }
+        int N = r.channels.size();
+        for (int i = 0; i < N; i++) {
+            final NotificationChannel nc = r.channels.valueAt(i);
+            if (nc.isDeleted()) {
+                deletedCount++;
+            }
+        }
+        return deletedCount;
+    }
+
+    public int getBlockedChannelCount(String pkg, int uid) {
+        Preconditions.checkNotNull(pkg);
+        int blockedCount = 0;
+        PackagePreferences r = getPackagePreferences(pkg, uid);
+        if (r == null) {
+            return blockedCount;
+        }
+        int N = r.channels.size();
+        for (int i = 0; i < N; i++) {
+            final NotificationChannel nc = r.channels.valueAt(i);
+            if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
+                blockedCount++;
+            }
+        }
+        return blockedCount;
+    }
+
+    public int getBlockedAppCount(int userId) {
+        int count = 0;
+        synchronized (mPackagePreferencess) {
+            final int N = mPackagePreferencess.size();
+            for (int i = 0; i < N; i++) {
+                final PackagePreferences r = mPackagePreferencess.valueAt(i);
+                if (userId == UserHandle.getUserId(r.uid)
+                        && r.importance == IMPORTANCE_NONE) {
+                    count++;
+                }
+            }
+        }
+        return count;
+    }
+
+    public void updateChannelsBypassingDnd() {
+        synchronized (mPackagePreferencess) {
+            final int numPackagePreferencess = mPackagePreferencess.size();
+            for (int PackagePreferencesIndex = 0; PackagePreferencesIndex < numPackagePreferencess;
+                    PackagePreferencesIndex++) {
+                final PackagePreferences r = mPackagePreferencess.valueAt(PackagePreferencesIndex);
+                final int numChannels = r.channels.size();
+
+                for (int channelIndex = 0; channelIndex < numChannels; channelIndex++) {
+                    NotificationChannel channel = r.channels.valueAt(channelIndex);
+                    if (!channel.isDeleted() && channel.canBypassDnd()) {
+                        // If any channel bypasses DND, synchronize state and return early.
+                        if (!mAreChannelsBypassingDnd) {
+                            mAreChannelsBypassingDnd = true;
+                            updateZenPolicy(true);
+                        }
+                        return;
+                    }
+                }
+            }
+        }
+
+        // If no channels bypass DND, update the zen policy once to disable DND bypass.
+        if (mAreChannelsBypassingDnd) {
+            mAreChannelsBypassingDnd = false;
+            updateZenPolicy(false);
+        }
+    }
+
+    public void updateZenPolicy(boolean areChannelsBypassingDnd) {
+        NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
+        mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
+                policy.priorityCategories, policy.priorityCallSenders,
+                policy.priorityMessageSenders, policy.suppressedVisualEffects,
+                (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
+                        : 0)));
+    }
+
+    public boolean areChannelsBypassingDnd() {
+        return mAreChannelsBypassingDnd;
+    }
+
+    /**
+     * Sets importance.
+     */
+    @Override
+    public void setImportance(String pkgName, int uid, int importance) {
+        getOrCreatePackagePreferences(pkgName, uid).importance = importance;
+        updateConfig();
+    }
+
+    public void setEnabled(String packageName, int uid, boolean enabled) {
+        boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
+        if (wasEnabled == enabled) {
+            return;
+        }
+        setImportance(packageName, uid,
+                enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
+    }
+
+    /**
+     * Sets whether any notifications from the app, represented by the given {@code pkgName} and
+     * {@code uid}, have their importance locked by the user. Locked notifications don't get
+     * considered for sentiment adjustments (and thus never show a blocking helper).
+     */
+    public void setAppImportanceLocked(String packageName, int uid) {
+        PackagePreferences PackagePreferences = getOrCreatePackagePreferences(packageName, uid);
+        if ((PackagePreferences.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
+            return;
+        }
+
+        PackagePreferences.lockedAppFields =
+                PackagePreferences.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
+        updateConfig();
+    }
+
+    @VisibleForTesting
+    void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) {
+        if (original.canBypassDnd() != update.canBypassDnd()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
+        }
+        if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
+        }
+        if (original.getImportance() != update.getImportance()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+        }
+        if (original.shouldShowLights() != update.shouldShowLights()
+                || original.getLightColor() != update.getLightColor()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
+        }
+        if (!Objects.equals(original.getSound(), update.getSound())) {
+            update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
+        }
+        if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
+                || original.shouldVibrate() != update.shouldVibrate()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
+        }
+        if (original.canShowBadge() != update.canShowBadge()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
+        }
+    }
+
+    public void dump(PrintWriter pw, String prefix,
+            @NonNull NotificationManagerService.DumpFilter filter) {
+        pw.print(prefix);
+        pw.println("per-package config:");
+
+        pw.println("PackagePreferencess:");
+        synchronized (mPackagePreferencess) {
+            dumpPackagePreferencess(pw, prefix, filter, mPackagePreferencess);
+        }
+        pw.println("Restored without uid:");
+        dumpPackagePreferencess(pw, prefix, filter, mRestoredWithoutUids);
+    }
+
+    public void dump(ProtoOutputStream proto,
+            @NonNull NotificationManagerService.DumpFilter filter) {
+        synchronized (mPackagePreferencess) {
+            dumpPackagePreferencess(proto, RankingHelperProto.RECORDS, filter,
+                    mPackagePreferencess);
+        }
+        dumpPackagePreferencess(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
+                mRestoredWithoutUids);
+    }
+
+    private static void dumpPackagePreferencess(PrintWriter pw, String prefix,
+            @NonNull NotificationManagerService.DumpFilter filter,
+            ArrayMap<String, PackagePreferences> PackagePreferencess) {
+        final int N = PackagePreferencess.size();
+        for (int i = 0; i < N; i++) {
+            final PackagePreferences r = PackagePreferencess.valueAt(i);
+            if (filter.matches(r.pkg)) {
+                pw.print(prefix);
+                pw.print("  AppSettings: ");
+                pw.print(r.pkg);
+                pw.print(" (");
+                pw.print(r.uid == PackagePreferences.UNKNOWN_UID ? "UNKNOWN_UID"
+                        : Integer.toString(r.uid));
+                pw.print(')');
+                if (r.importance != DEFAULT_IMPORTANCE) {
+                    pw.print(" importance=");
+                    pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
+                }
+                if (r.priority != DEFAULT_PRIORITY) {
+                    pw.print(" priority=");
+                    pw.print(Notification.priorityToString(r.priority));
+                }
+                if (r.visibility != DEFAULT_VISIBILITY) {
+                    pw.print(" visibility=");
+                    pw.print(Notification.visibilityToString(r.visibility));
+                }
+                pw.print(" showBadge=");
+                pw.print(Boolean.toString(r.showBadge));
+                pw.println();
+                for (NotificationChannel channel : r.channels.values()) {
+                    pw.print(prefix);
+                    channel.dump(pw, "    ", filter.redact);
+                }
+                for (NotificationChannelGroup group : r.groups.values()) {
+                    pw.print(prefix);
+                    pw.print("  ");
+                    pw.print("  ");
+                    pw.println(group);
+                }
+            }
+        }
+    }
+
+    private static void dumpPackagePreferencess(ProtoOutputStream proto, long fieldId,
+            @NonNull NotificationManagerService.DumpFilter filter,
+            ArrayMap<String, PackagePreferences> PackagePreferencess) {
+        final int N = PackagePreferencess.size();
+        long fToken;
+        for (int i = 0; i < N; i++) {
+            final PackagePreferences r = PackagePreferencess.valueAt(i);
+            if (filter.matches(r.pkg)) {
+                fToken = proto.start(fieldId);
+
+                proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
+                proto.write(RankingHelperProto.RecordProto.UID, r.uid);
+                proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
+                proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
+                proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
+                proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge);
+
+                for (NotificationChannel channel : r.channels.values()) {
+                    channel.writeToProto(proto, RankingHelperProto.RecordProto.CHANNELS);
+                }
+                for (NotificationChannelGroup group : r.groups.values()) {
+                    group.writeToProto(proto, RankingHelperProto.RecordProto.CHANNEL_GROUPS);
+                }
+
+                proto.end(fToken);
+            }
+        }
+    }
+
+    public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
+        JSONObject ranking = new JSONObject();
+        JSONArray PackagePreferencess = new JSONArray();
+        try {
+            ranking.put("noUid", mRestoredWithoutUids.size());
+        } catch (JSONException e) {
+            // pass
+        }
+        synchronized (mPackagePreferencess) {
+            final int N = mPackagePreferencess.size();
+            for (int i = 0; i < N; i++) {
+                final PackagePreferences r = mPackagePreferencess.valueAt(i);
+                if (filter == null || filter.matches(r.pkg)) {
+                    JSONObject PackagePreferences = new JSONObject();
+                    try {
+                        PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
+                        PackagePreferences.put("packageName", r.pkg);
+                        if (r.importance != DEFAULT_IMPORTANCE) {
+                            PackagePreferences.put("importance",
+                                    NotificationListenerService.Ranking.importanceToString(
+                                            r.importance));
+                        }
+                        if (r.priority != DEFAULT_PRIORITY) {
+                            PackagePreferences.put("priority",
+                                    Notification.priorityToString(r.priority));
+                        }
+                        if (r.visibility != DEFAULT_VISIBILITY) {
+                            PackagePreferences.put("visibility",
+                                    Notification.visibilityToString(r.visibility));
+                        }
+                        if (r.showBadge != DEFAULT_SHOW_BADGE) {
+                            PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
+                        }
+                        JSONArray channels = new JSONArray();
+                        for (NotificationChannel channel : r.channels.values()) {
+                            channels.put(channel.toJson());
+                        }
+                        PackagePreferences.put("channels", channels);
+                        JSONArray groups = new JSONArray();
+                        for (NotificationChannelGroup group : r.groups.values()) {
+                            groups.put(group.toJson());
+                        }
+                        PackagePreferences.put("groups", groups);
+                    } catch (JSONException e) {
+                        // pass
+                    }
+                    PackagePreferencess.put(PackagePreferences);
+                }
+            }
+        }
+        try {
+            ranking.put("PackagePreferencess", PackagePreferencess);
+        } catch (JSONException e) {
+            // pass
+        }
+        return ranking;
+    }
+
+    /**
+     * Dump only the ban information as structured JSON for the stats collector.
+     *
+     * This is intentionally redundant with {#link dumpJson} because the old
+     * scraper will expect this format.
+     *
+     * @param filter
+     * @return
+     */
+    public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
+        JSONArray bans = new JSONArray();
+        Map<Integer, String> packageBans = getPackageBans();
+        for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
+            final int userId = UserHandle.getUserId(ban.getKey());
+            final String packageName = ban.getValue();
+            if (filter == null || filter.matches(packageName)) {
+                JSONObject banJson = new JSONObject();
+                try {
+                    banJson.put("userId", userId);
+                    banJson.put("packageName", packageName);
+                } catch (JSONException e) {
+                    e.printStackTrace();
+                }
+                bans.put(banJson);
+            }
+        }
+        return bans;
+    }
+
+    public Map<Integer, String> getPackageBans() {
+        synchronized (mPackagePreferencess) {
+            final int N = mPackagePreferencess.size();
+            ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
+            for (int i = 0; i < N; i++) {
+                final PackagePreferences r = mPackagePreferencess.valueAt(i);
+                if (r.importance == IMPORTANCE_NONE) {
+                    packageBans.put(r.uid, r.pkg);
+                }
+            }
+
+            return packageBans;
+        }
+    }
+
+    /**
+     * Dump only the channel information as structured JSON for the stats collector.
+     *
+     * This is intentionally redundant with {#link dumpJson} because the old
+     * scraper will expect this format.
+     *
+     * @param filter
+     * @return
+     */
+    public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
+        JSONArray channels = new JSONArray();
+        Map<String, Integer> packageChannels = getPackageChannels();
+        for (Map.Entry<String, Integer> channelCount : packageChannels.entrySet()) {
+            final String packageName = channelCount.getKey();
+            if (filter == null || filter.matches(packageName)) {
+                JSONObject channelCountJson = new JSONObject();
+                try {
+                    channelCountJson.put("packageName", packageName);
+                    channelCountJson.put("channelCount", channelCount.getValue());
+                } catch (JSONException e) {
+                    e.printStackTrace();
+                }
+                channels.put(channelCountJson);
+            }
+        }
+        return channels;
+    }
+
+    private Map<String, Integer> getPackageChannels() {
+        ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
+        synchronized (mPackagePreferencess) {
+            for (int i = 0; i < mPackagePreferencess.size(); i++) {
+                final PackagePreferences r = mPackagePreferencess.valueAt(i);
+                int channelCount = 0;
+                for (int j = 0; j < r.channels.size(); j++) {
+                    if (!r.channels.valueAt(j).isDeleted()) {
+                        channelCount++;
+                    }
+                }
+                packageChannels.put(r.pkg, channelCount);
+            }
+        }
+        return packageChannels;
+    }
+
+    public void onUserRemoved(int userId) {
+        synchronized (mPackagePreferencess) {
+            int N = mPackagePreferencess.size();
+            for (int i = N - 1; i >= 0; i--) {
+                PackagePreferences PackagePreferences = mPackagePreferencess.valueAt(i);
+                if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
+                    mPackagePreferencess.removeAt(i);
+                }
+            }
+        }
+    }
+
+    protected void onLocaleChanged(Context context, int userId) {
+        synchronized (mPackagePreferencess) {
+            int N = mPackagePreferencess.size();
+            for (int i = 0; i < N; i++) {
+                PackagePreferences PackagePreferences = mPackagePreferencess.valueAt(i);
+                if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
+                    if (PackagePreferences.channels.containsKey(
+                            NotificationChannel.DEFAULT_CHANNEL_ID)) {
+                        PackagePreferences.channels.get(
+                                NotificationChannel.DEFAULT_CHANNEL_ID).setName(
+                                context.getResources().getString(
+                                        R.string.default_notification_channel_label));
+                    }
+                }
+            }
+        }
+    }
+
+    public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
+            int[] uidList) {
+        if (pkgList == null || pkgList.length == 0) {
+            return; // nothing to do
+        }
+        boolean updated = false;
+        if (removingPackage) {
+            // Remove notification settings for uninstalled package
+            int size = Math.min(pkgList.length, uidList.length);
+            for (int i = 0; i < size; i++) {
+                final String pkg = pkgList[i];
+                final int uid = uidList[i];
+                synchronized (mPackagePreferencess) {
+                    mPackagePreferencess.remove(packagePreferencesKey(pkg, uid));
+                }
+                mRestoredWithoutUids.remove(pkg);
+                updated = true;
+            }
+        } else {
+            for (String pkg : pkgList) {
+                // Package install
+                final PackagePreferences r = mRestoredWithoutUids.get(pkg);
+                if (r != null) {
+                    try {
+                        r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
+                        mRestoredWithoutUids.remove(pkg);
+                        synchronized (mPackagePreferencess) {
+                            mPackagePreferencess.put(packagePreferencesKey(r.pkg, r.uid), r);
+                        }
+                        updated = true;
+                    } catch (PackageManager.NameNotFoundException e) {
+                        // noop
+                    }
+                }
+                // Package upgrade
+                try {
+                    PackagePreferences fullPackagePreferences = getPackagePreferences(pkg,
+                            mPm.getPackageUidAsUser(pkg, changeUserId));
+                    if (fullPackagePreferences != null) {
+                        createDefaultChannelIfNeeded(fullPackagePreferences);
+                        deleteDefaultChannelIfNeeded(fullPackagePreferences);
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                }
+            }
+        }
+
+        if (updated) {
+            updateConfig();
+        }
+    }
+
+    private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
+        return new LogMaker(
+                com.android.internal.logging.nano.MetricsProto.MetricsEvent
+                        .ACTION_NOTIFICATION_CHANNEL)
+                .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
+                .setPackageName(pkg)
+                .addTaggedData(
+                        com.android.internal.logging.nano.MetricsProto.MetricsEvent
+                                .FIELD_NOTIFICATION_CHANNEL_ID,
+                        channel.getId())
+                .addTaggedData(
+                        com.android.internal.logging.nano.MetricsProto.MetricsEvent
+                                .FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
+                        channel.getImportance());
+    }
+
+    private LogMaker getChannelGroupLog(String groupId, String pkg) {
+        return new LogMaker(
+                com.android.internal.logging.nano.MetricsProto.MetricsEvent
+                        .ACTION_NOTIFICATION_CHANNEL_GROUP)
+                .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
+                .addTaggedData(
+                        com.android.internal.logging.nano.MetricsProto.MetricsEvent
+                                .FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
+                        groupId)
+                .setPackageName(pkg);
+    }
+
+
+    public void updateBadgingEnabled() {
+        if (mBadgingEnabled == null) {
+            mBadgingEnabled = new SparseBooleanArray();
+        }
+        boolean changed = false;
+        // update the cached values
+        for (int index = 0; index < mBadgingEnabled.size(); index++) {
+            int userId = mBadgingEnabled.keyAt(index);
+            final boolean oldValue = mBadgingEnabled.get(userId);
+            final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                    Settings.Secure.NOTIFICATION_BADGING,
+                    DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
+            mBadgingEnabled.put(userId, newValue);
+            changed |= oldValue != newValue;
+        }
+        if (changed) {
+            updateConfig();
+        }
+    }
+
+    public boolean badgingEnabled(UserHandle userHandle) {
+        int userId = userHandle.getIdentifier();
+        if (userId == UserHandle.USER_ALL) {
+            return false;
+        }
+        if (mBadgingEnabled.indexOfKey(userId) < 0) {
+            mBadgingEnabled.put(userId,
+                    Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                            Settings.Secure.NOTIFICATION_BADGING,
+                            DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
+        }
+        return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
+    }
+
+    private void updateConfig() {
+        mRankingHandler.requestSort();
+    }
+
+    private static String packagePreferencesKey(String pkg, int uid) {
+        return pkg + "|" + uid;
+    }
+
+    private static class PackagePreferences {
+        static int UNKNOWN_UID = UserHandle.USER_NULL;
+
+        String pkg;
+        int uid = UNKNOWN_UID;
+        int importance = DEFAULT_IMPORTANCE;
+        int priority = DEFAULT_PRIORITY;
+        int visibility = DEFAULT_VISIBILITY;
+        boolean showBadge = DEFAULT_SHOW_BADGE;
+        int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
+
+        ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
+        Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
+    }
+}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 61b5415..f5e58ea 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -15,123 +15,37 @@
  */
 package com.android.server.notification;
 
-import static android.app.NotificationManager.IMPORTANCE_NONE;
-
-import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ParceledListSlice;
-import android.metrics.LogMaker;
-import android.os.Build;
-import android.os.UserHandle;
-import android.provider.Settings.Secure;
-import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.RankingHelperProto;
-import android.service.notification.RankingHelperProto.RecordProto;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
-import android.util.SparseBooleanArray;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
 
-public class RankingHelper implements RankingConfig {
+public class RankingHelper {
     private static final String TAG = "RankingHelper";
 
-    private static final int XML_VERSION = 1;
-
-    static final String TAG_RANKING = "ranking";
-    private static final String TAG_PACKAGE = "package";
-    private static final String TAG_CHANNEL = "channel";
-    private static final String TAG_GROUP = "channelGroup";
-
-    private static final String ATT_VERSION = "version";
-    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_PRIORITY = "priority";
-    private static final String ATT_VISIBILITY = "visibility";
-    private static final String ATT_IMPORTANCE = "importance";
-    private static final String ATT_SHOW_BADGE = "show_badge";
-    private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
-
-    private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
-    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;
-    /**
-     * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
-     * fields.
-     */
-    private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
-
-    /**
-     * All user-lockable fields for a given application.
-     */
-    @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
-    public @interface LockableAppFields {
-        int USER_LOCKED_IMPORTANCE = 0x00000001;
-    }
-
     private final NotificationSignalExtractor[] mSignalExtractors;
     private final NotificationComparator mPreliminaryComparator;
     private final GlobalSortKeyComparator mFinalComparator = new GlobalSortKeyComparator();
 
-    private final ArrayMap<String, Record> mRecords = new ArrayMap<>(); // pkg|uid => Record
     private final ArrayMap<String, NotificationRecord> mProxyByGroupTmp = new ArrayMap<>();
-    private final ArrayMap<String, Record> mRestoredWithoutUids = new ArrayMap<>(); // pkg => Record
 
     private final Context mContext;
     private final RankingHandler mRankingHandler;
-    private final PackageManager mPm;
-    private SparseBooleanArray mBadgingEnabled;
 
-    private boolean mAreChannelsBypassingDnd;
-    private ZenModeHelper mZenModeHelper;
 
-    public RankingHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
+    public RankingHelper(Context context, RankingHandler rankingHandler, RankingConfig config,
             ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames) {
         mContext = context;
         mRankingHandler = rankingHandler;
-        mPm = pm;
-        mZenModeHelper= zenHelper;
-
         mPreliminaryComparator = new NotificationComparator(mContext);
 
-        updateBadgingEnabled();
-
         final int N = extractorNames.length;
         mSignalExtractors = new NotificationSignalExtractor[N];
         for (int i = 0; i < N; i++) {
@@ -140,7 +54,7 @@
                 NotificationSignalExtractor extractor =
                         (NotificationSignalExtractor) extractorClass.newInstance();
                 extractor.initialize(mContext, usageStats);
-                extractor.setConfig(this);
+                extractor.setConfig(config);
                 extractor.setZenHelper(zenHelper);
                 mSignalExtractors[i] = extractor;
             } catch (ClassNotFoundException e) {
@@ -151,10 +65,6 @@
                 Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
             }
         }
-
-        mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state &
-                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
-        updateChannelsBypassingDnd();
     }
 
     @SuppressWarnings("unchecked")
@@ -184,279 +94,6 @@
         }
     }
 
-    public void readXml(XmlPullParser parser, boolean forRestore)
-            throws XmlPullParserException, IOException {
-        int type = parser.getEventType();
-        if (type != XmlPullParser.START_TAG) return;
-        String tag = parser.getName();
-        if (!TAG_RANKING.equals(tag)) return;
-        // Clobber groups and channels with the xml, but don't delete other data that wasn't present
-        // at the time of serialization.
-        mRestoredWithoutUids.clear();
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-            tag = parser.getName();
-            if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
-                return;
-            }
-            if (type == XmlPullParser.START_TAG) {
-                if (TAG_PACKAGE.equals(tag)) {
-                    int uid = XmlUtils.readIntAttribute(parser, ATT_UID, Record.UNKNOWN_UID);
-                    String name = parser.getAttributeValue(null, ATT_NAME);
-                    if (!TextUtils.isEmpty(name)) {
-                        if (forRestore) {
-                            try {
-                                //TODO: http://b/22388012
-                                uid = mPm.getPackageUidAsUser(name, UserHandle.USER_SYSTEM);
-                            } catch (NameNotFoundException e) {
-                                // noop
-                            }
-                        }
-
-                        Record r = getOrCreateRecord(name, uid,
-                                XmlUtils.readIntAttribute(
-                                        parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
-                                XmlUtils.readIntAttribute(parser, ATT_PRIORITY, DEFAULT_PRIORITY),
-                                XmlUtils.readIntAttribute(
-                                        parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
-                                XmlUtils.readBooleanAttribute(
-                                        parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE));
-                        r.importance = XmlUtils.readIntAttribute(
-                                parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
-                        r.priority = XmlUtils.readIntAttribute(
-                                parser, ATT_PRIORITY, DEFAULT_PRIORITY);
-                        r.visibility = XmlUtils.readIntAttribute(
-                                parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
-                        r.showBadge = XmlUtils.readBooleanAttribute(
-                                parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
-                        r.lockedAppFields = XmlUtils.readIntAttribute(parser,
-                                ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
-
-                        final int innerDepth = parser.getDepth();
-                        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                                && (type != XmlPullParser.END_TAG
-                                || parser.getDepth() > innerDepth)) {
-                            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                                continue;
-                            }
-
-                            String tagName = parser.getName();
-                            // Channel groups
-                            if (TAG_GROUP.equals(tagName)) {
-                                String id = parser.getAttributeValue(null, ATT_ID);
-                                CharSequence groupName = parser.getAttributeValue(null, ATT_NAME);
-                                if (!TextUtils.isEmpty(id)) {
-                                    NotificationChannelGroup group
-                                            = new NotificationChannelGroup(id, groupName);
-                                    group.populateFromXml(parser);
-                                    r.groups.put(id, group);
-                                }
-                            }
-                            // Channels
-                            if (TAG_CHANNEL.equals(tagName)) {
-                                String id = parser.getAttributeValue(null, ATT_ID);
-                                String channelName = parser.getAttributeValue(null, ATT_NAME);
-                                int channelImportance = XmlUtils.readIntAttribute(
-                                        parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
-                                if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
-                                    NotificationChannel channel = new NotificationChannel(id,
-                                            channelName, channelImportance);
-                                    if (forRestore) {
-                                        channel.populateFromXmlForRestore(parser, mContext);
-                                    } else {
-                                        channel.populateFromXml(parser);
-                                    }
-                                    r.channels.put(id, channel);
-                                }
-                            }
-                        }
-
-                        try {
-                            deleteDefaultChannelIfNeeded(r);
-                        } catch (NameNotFoundException e) {
-                            Slog.e(TAG, "deleteDefaultChannelIfNeeded - Exception: " + e);
-                        }
-                    }
-                }
-            }
-        }
-        throw new IllegalStateException("Failed to reach END_DOCUMENT");
-    }
-
-    private static String recordKey(String pkg, int uid) {
-        return pkg + "|" + uid;
-    }
-
-    private Record getRecord(String pkg, int uid) {
-        final String key = recordKey(pkg, uid);
-        synchronized (mRecords) {
-            return mRecords.get(key);
-        }
-    }
-
-    private Record getOrCreateRecord(String pkg, int uid) {
-        return getOrCreateRecord(pkg, uid,
-                DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE);
-    }
-
-    private Record getOrCreateRecord(String pkg, int uid, int importance, int priority,
-            int visibility, boolean showBadge) {
-        final String key = recordKey(pkg, uid);
-        synchronized (mRecords) {
-            Record r = (uid == Record.UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg) : mRecords.get(
-                    key);
-            if (r == null) {
-                r = new Record();
-                r.pkg = pkg;
-                r.uid = uid;
-                r.importance = importance;
-                r.priority = priority;
-                r.visibility = visibility;
-                r.showBadge = showBadge;
-
-                try {
-                    createDefaultChannelIfNeeded(r);
-                } catch (NameNotFoundException e) {
-                    Slog.e(TAG, "createDefaultChannelIfNeeded - Exception: " + e);
-                }
-
-                if (r.uid == Record.UNKNOWN_UID) {
-                    mRestoredWithoutUids.put(pkg, r);
-                } else {
-                    mRecords.put(key, r);
-                }
-            }
-            return r;
-        }
-    }
-
-    private boolean shouldHaveDefaultChannel(Record r) throws NameNotFoundException {
-        final int userId = UserHandle.getUserId(r.uid);
-        final ApplicationInfo applicationInfo = mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
-        if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
-            // O apps should not have the default channel.
-            return false;
-        }
-
-        // Otherwise, this app should have the default channel.
-        return true;
-    }
-
-    private void deleteDefaultChannelIfNeeded(Record r) throws NameNotFoundException {
-        if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
-            // Not present
-            return;
-        }
-
-        if (shouldHaveDefaultChannel(r)) {
-            // Keep the default channel until upgraded.
-            return;
-        }
-
-        // Remove Default Channel.
-        r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
-    }
-
-    private void createDefaultChannelIfNeeded(Record r) throws NameNotFoundException {
-        if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
-            r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(
-                    mContext.getString(R.string.default_notification_channel_label));
-            return;
-        }
-
-        if (!shouldHaveDefaultChannel(r)) {
-            // Keep the default channel until upgraded.
-            return;
-        }
-
-        // Create Default Channel
-        NotificationChannel channel;
-        channel = new NotificationChannel(
-                NotificationChannel.DEFAULT_CHANNEL_ID,
-                mContext.getString(R.string.default_notification_channel_label),
-                r.importance);
-        channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
-        channel.setLockscreenVisibility(r.visibility);
-        if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
-            channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-        }
-        if (r.priority != DEFAULT_PRIORITY) {
-            channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
-        }
-        if (r.visibility != DEFAULT_VISIBILITY) {
-            channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
-        }
-        r.channels.put(channel.getId(), channel);
-    }
-
-    public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
-        out.startTag(null, TAG_RANKING);
-        out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
-
-        synchronized (mRecords) {
-            final int N = mRecords.size();
-            for (int i = 0; i < N; i++) {
-                final Record r = mRecords.valueAt(i);
-                //TODO: http://b/22388012
-                if (forBackup && UserHandle.getUserId(r.uid) != UserHandle.USER_SYSTEM) {
-                    continue;
-                }
-                final boolean hasNonDefaultSettings =
-                        r.importance != DEFAULT_IMPORTANCE
-                            || r.priority != DEFAULT_PRIORITY
-                            || r.visibility != DEFAULT_VISIBILITY
-                            || r.showBadge != DEFAULT_SHOW_BADGE
-                            || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
-                            || r.channels.size() > 0
-                            || r.groups.size() > 0;
-                if (hasNonDefaultSettings) {
-                    out.startTag(null, TAG_PACKAGE);
-                    out.attribute(null, ATT_NAME, r.pkg);
-                    if (r.importance != DEFAULT_IMPORTANCE) {
-                        out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
-                    }
-                    if (r.priority != DEFAULT_PRIORITY) {
-                        out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
-                    }
-                    if (r.visibility != DEFAULT_VISIBILITY) {
-                        out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
-                    }
-                    out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
-                    out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
-                            Integer.toString(r.lockedAppFields));
-
-                    if (!forBackup) {
-                        out.attribute(null, ATT_UID, Integer.toString(r.uid));
-                    }
-
-                    for (NotificationChannelGroup group : r.groups.values()) {
-                        group.writeXml(out);
-                    }
-
-                    for (NotificationChannel channel : r.channels.values()) {
-                        if (forBackup) {
-                            if (!channel.isDeleted()) {
-                                channel.writeXmlForBackup(out, mContext);
-                            }
-                        } else {
-                            channel.writeXml(out);
-                        }
-                    }
-
-                    out.endTag(null, TAG_PACKAGE);
-                }
-            }
-        }
-        out.endTag(null, TAG_RANKING);
-    }
-
-    private void updateConfig() {
-        final int N = mSignalExtractors.length;
-        for (int i = 0; i < N; i++) {
-            mSignalExtractors[i].setConfig(this);
-        }
-        mRankingHandler.requestSort();
-    }
-
     public void sort(ArrayList<NotificationRecord> notificationList) {
         final int N = notificationList.size();
         // clear global sort keys
@@ -521,562 +158,6 @@
         return Collections.binarySearch(notificationList, target, mFinalComparator);
     }
 
-    /**
-     * Gets importance.
-     */
-    @Override
-    public int getImportance(String packageName, int uid) {
-        return getOrCreateRecord(packageName, uid).importance;
-    }
-
-
-    /**
-     * Returns whether the importance of the corresponding notification is user-locked and shouldn't
-     * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
-     * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
-     */
-    public boolean getIsAppImportanceLocked(String packageName, int uid) {
-        int userLockedFields = getOrCreateRecord(packageName, uid).lockedAppFields;
-        return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
-    }
-
-    @Override
-    public boolean canShowBadge(String packageName, int uid) {
-        return getOrCreateRecord(packageName, uid).showBadge;
-    }
-
-    @Override
-    public void setShowBadge(String packageName, int uid, boolean showBadge) {
-        getOrCreateRecord(packageName, uid).showBadge = showBadge;
-        updateConfig();
-    }
-
-    @Override
-    public boolean isGroupBlocked(String packageName, int uid, String groupId) {
-        if (groupId == null) {
-            return false;
-        }
-        Record r = getOrCreateRecord(packageName, uid);
-        NotificationChannelGroup group = r.groups.get(groupId);
-        if (group == null) {
-            return false;
-        }
-        return group.isBlocked();
-    }
-
-    int getPackagePriority(String pkg, int uid) {
-        return getOrCreateRecord(pkg, uid).priority;
-    }
-
-    int getPackageVisibility(String pkg, int uid) {
-        return getOrCreateRecord(pkg, uid).visibility;
-    }
-
-    @Override
-    public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
-            boolean fromTargetApp) {
-        Preconditions.checkNotNull(pkg);
-        Preconditions.checkNotNull(group);
-        Preconditions.checkNotNull(group.getId());
-        Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName()));
-        Record r = getOrCreateRecord(pkg, uid);
-        if (r == null) {
-            throw new IllegalArgumentException("Invalid package");
-        }
-        final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
-        if (!group.equals(oldGroup)) {
-            // will log for new entries as well as name/description changes
-            MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
-        }
-        if (oldGroup != null) {
-            group.setChannels(oldGroup.getChannels());
-
-            if (fromTargetApp) {
-                group.setBlocked(oldGroup.isBlocked());
-            }
-        }
-        r.groups.put(group.getId(), group);
-    }
-
-    @Override
-    public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
-            boolean fromTargetApp, boolean hasDndAccess) {
-        Preconditions.checkNotNull(pkg);
-        Preconditions.checkNotNull(channel);
-        Preconditions.checkNotNull(channel.getId());
-        Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
-        Record r = getOrCreateRecord(pkg, uid);
-        if (r == null) {
-            throw new IllegalArgumentException("Invalid package");
-        }
-        if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
-            throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
-        }
-        if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
-            throw new IllegalArgumentException("Reserved id");
-        }
-        NotificationChannel existing = r.channels.get(channel.getId());
-        // Keep most of the existing settings
-        if (existing != null && fromTargetApp) {
-            if (existing.isDeleted()) {
-                existing.setDeleted(false);
-
-                // log a resurrected channel as if it's new again
-                MetricsLogger.action(getChannelLog(channel, pkg).setType(
-                        MetricsProto.MetricsEvent.TYPE_OPEN));
-            }
-
-            existing.setName(channel.getName().toString());
-            existing.setDescription(channel.getDescription());
-            existing.setBlockableSystem(channel.isBlockableSystem());
-            if (existing.getGroup() == null) {
-                existing.setGroup(channel.getGroup());
-            }
-
-            // Apps are allowed to downgrade channel importance if the user has not changed any
-            // fields on this channel yet.
-            if (existing.getUserLockedFields() == 0 &&
-                    channel.getImportance() < existing.getImportance()) {
-                existing.setImportance(channel.getImportance());
-            }
-
-            // system apps and dnd access apps can bypass dnd if the user hasn't changed any
-            // fields on the channel yet
-            if (existing.getUserLockedFields() == 0 && hasDndAccess) {
-                boolean bypassDnd = channel.canBypassDnd();
-                existing.setBypassDnd(bypassDnd);
-
-                if (bypassDnd != mAreChannelsBypassingDnd) {
-                    updateChannelsBypassingDnd();
-                }
-            }
-
-            updateConfig();
-            return;
-        }
-        if (channel.getImportance() < IMPORTANCE_NONE
-                || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
-            throw new IllegalArgumentException("Invalid importance level");
-        }
-
-        // Reset fields that apps aren't allowed to set.
-        if (fromTargetApp && !hasDndAccess) {
-            channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
-        }
-        if (fromTargetApp) {
-            channel.setLockscreenVisibility(r.visibility);
-        }
-        clearLockedFields(channel);
-        if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
-            channel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
-        }
-        if (!r.showBadge) {
-            channel.setShowBadge(false);
-        }
-
-        r.channels.put(channel.getId(), channel);
-        if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
-            updateChannelsBypassingDnd();
-        }
-        MetricsLogger.action(getChannelLog(channel, pkg).setType(
-                MetricsProto.MetricsEvent.TYPE_OPEN));
-    }
-
-    void clearLockedFields(NotificationChannel channel) {
-        channel.unlockFields(channel.getUserLockedFields());
-    }
-
-    @Override
-    public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
-            boolean fromUser) {
-        Preconditions.checkNotNull(updatedChannel);
-        Preconditions.checkNotNull(updatedChannel.getId());
-        Record r = getOrCreateRecord(pkg, uid);
-        if (r == null) {
-            throw new IllegalArgumentException("Invalid package");
-        }
-        NotificationChannel channel = r.channels.get(updatedChannel.getId());
-        if (channel == null || channel.isDeleted()) {
-            throw new IllegalArgumentException("Channel does not exist");
-        }
-        if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
-            updatedChannel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
-        }
-        if (!fromUser) {
-            updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
-        }
-        if (fromUser) {
-            updatedChannel.lockFields(channel.getUserLockedFields());
-            lockFieldsForUpdate(channel, updatedChannel);
-        }
-        r.channels.put(updatedChannel.getId(), updatedChannel);
-
-        if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(updatedChannel.getId())) {
-            // copy settings to app level so they are inherited by new channels
-            // when the app migrates
-            r.importance = updatedChannel.getImportance();
-            r.priority = updatedChannel.canBypassDnd()
-                    ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
-            r.visibility = updatedChannel.getLockscreenVisibility();
-            r.showBadge = updatedChannel.canShowBadge();
-        }
-
-        if (!channel.equals(updatedChannel)) {
-            // only log if there are real changes
-            MetricsLogger.action(getChannelLog(updatedChannel, pkg));
-        }
-
-        if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd) {
-            updateChannelsBypassingDnd();
-        }
-        updateConfig();
-    }
-
-    @Override
-    public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
-            boolean includeDeleted) {
-        Preconditions.checkNotNull(pkg);
-        Record r = getOrCreateRecord(pkg, uid);
-        if (r == null) {
-            return null;
-        }
-        if (channelId == null) {
-            channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
-        }
-        final NotificationChannel nc = r.channels.get(channelId);
-        if (nc != null && (includeDeleted || !nc.isDeleted())) {
-            return nc;
-        }
-        return null;
-    }
-
-    @Override
-    public void deleteNotificationChannel(String pkg, int uid, String channelId) {
-        Record r = getRecord(pkg, uid);
-        if (r == null) {
-            return;
-        }
-        NotificationChannel channel = r.channels.get(channelId);
-        if (channel != null) {
-            channel.setDeleted(true);
-            LogMaker lm = getChannelLog(channel, pkg);
-            lm.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
-            MetricsLogger.action(lm);
-
-            if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
-                updateChannelsBypassingDnd();
-            }
-        }
-    }
-
-    @Override
-    @VisibleForTesting
-    public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
-        Preconditions.checkNotNull(pkg);
-        Preconditions.checkNotNull(channelId);
-        Record r = getRecord(pkg, uid);
-        if (r == null) {
-            return;
-        }
-        r.channels.remove(channelId);
-    }
-
-    @Override
-    public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
-        Preconditions.checkNotNull(pkg);
-        Record r = getRecord(pkg, uid);
-        if (r == null) {
-            return;
-        }
-        int N = r.channels.size() - 1;
-        for (int i = N; i >= 0; i--) {
-            String key = r.channels.keyAt(i);
-            if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
-                r.channels.remove(key);
-            }
-        }
-    }
-
-    public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
-            int uid, String groupId, boolean includeDeleted) {
-        Preconditions.checkNotNull(pkg);
-        Record r = getRecord(pkg, uid);
-        if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
-            return null;
-        }
-        NotificationChannelGroup group = r.groups.get(groupId).clone();
-        group.setChannels(new ArrayList<>());
-        int N = r.channels.size();
-        for (int i = 0; i < N; i++) {
-            final NotificationChannel nc = r.channels.valueAt(i);
-            if (includeDeleted || !nc.isDeleted()) {
-                if (groupId.equals(nc.getGroup())) {
-                    group.addChannel(nc);
-                }
-            }
-        }
-        return group;
-    }
-
-    public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
-            int uid) {
-        Preconditions.checkNotNull(pkg);
-        Record r = getRecord(pkg, uid);
-        if (r == null) {
-            return null;
-        }
-        return r.groups.get(groupId);
-    }
-
-    @Override
-    public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
-            int uid, boolean includeDeleted, boolean includeNonGrouped) {
-        Preconditions.checkNotNull(pkg);
-        Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
-        Record r = getRecord(pkg, uid);
-        if (r == null) {
-            return ParceledListSlice.emptyList();
-        }
-        NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
-        int N = r.channels.size();
-        for (int i = 0; i < N; i++) {
-            final NotificationChannel nc = r.channels.valueAt(i);
-            if (includeDeleted || !nc.isDeleted()) {
-                if (nc.getGroup() != null) {
-                    if (r.groups.get(nc.getGroup()) != null) {
-                        NotificationChannelGroup ncg = groups.get(nc.getGroup());
-                        if (ncg == null) {
-                            ncg = r.groups.get(nc.getGroup()).clone();
-                            ncg.setChannels(new ArrayList<>());
-                            groups.put(nc.getGroup(), ncg);
-
-                        }
-                        ncg.addChannel(nc);
-                    }
-                } else {
-                    nonGrouped.addChannel(nc);
-                }
-            }
-        }
-        if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
-            groups.put(null, nonGrouped);
-        }
-        return new ParceledListSlice<>(new ArrayList<>(groups.values()));
-    }
-
-    public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
-            String groupId) {
-        List<NotificationChannel> deletedChannels = new ArrayList<>();
-        Record r = getRecord(pkg, uid);
-        if (r == null || TextUtils.isEmpty(groupId)) {
-            return deletedChannels;
-        }
-
-        r.groups.remove(groupId);
-
-        int N = r.channels.size();
-        for (int i = 0; i < N; i++) {
-            final NotificationChannel nc = r.channels.valueAt(i);
-            if (groupId.equals(nc.getGroup())) {
-                nc.setDeleted(true);
-                deletedChannels.add(nc);
-            }
-        }
-        return deletedChannels;
-    }
-
-    @Override
-    public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
-            int uid) {
-        Record r = getRecord(pkg, uid);
-        if (r == null) {
-            return new ArrayList<>();
-        }
-        return r.groups.values();
-    }
-
-    @Override
-    public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
-            boolean includeDeleted) {
-        Preconditions.checkNotNull(pkg);
-        List<NotificationChannel> channels = new ArrayList<>();
-        Record r = getRecord(pkg, uid);
-        if (r == null) {
-            return ParceledListSlice.emptyList();
-        }
-        int N = r.channels.size();
-        for (int i = 0; i < N; i++) {
-            final NotificationChannel nc = r.channels.valueAt(i);
-            if (includeDeleted || !nc.isDeleted()) {
-                channels.add(nc);
-            }
-        }
-        return new ParceledListSlice<>(channels);
-    }
-
-    /**
-     * True for pre-O apps that only have the default channel, or pre O apps that have no
-     * channels yet. This method will create the default channel for pre-O apps that don't have it.
-     * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
-     * upgrades.
-     */
-    public boolean onlyHasDefaultChannel(String pkg, int uid) {
-        Record r = getOrCreateRecord(pkg, uid);
-        if (r.channels.size() == 1
-                && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
-            return true;
-        }
-        return false;
-    }
-
-    public int getDeletedChannelCount(String pkg, int uid) {
-        Preconditions.checkNotNull(pkg);
-        int deletedCount = 0;
-        Record r = getRecord(pkg, uid);
-        if (r == null) {
-            return deletedCount;
-        }
-        int N = r.channels.size();
-        for (int i = 0; i < N; i++) {
-            final NotificationChannel nc = r.channels.valueAt(i);
-            if (nc.isDeleted()) {
-                deletedCount++;
-            }
-        }
-        return deletedCount;
-    }
-
-    public int getBlockedChannelCount(String pkg, int uid) {
-        Preconditions.checkNotNull(pkg);
-        int blockedCount = 0;
-        Record r = getRecord(pkg, uid);
-        if (r == null) {
-            return blockedCount;
-        }
-        int N = r.channels.size();
-        for (int i = 0; i < N; i++) {
-            final NotificationChannel nc = r.channels.valueAt(i);
-            if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
-                blockedCount++;
-            }
-        }
-        return blockedCount;
-    }
-
-    public int getBlockedAppCount(int userId) {
-        int count = 0;
-        synchronized (mRecords) {
-            final int N = mRecords.size();
-            for (int i = 0; i < N; i++) {
-                final Record r = mRecords.valueAt(i);
-                if (userId == UserHandle.getUserId(r.uid)
-                        && r.importance == IMPORTANCE_NONE) {
-                    count++;
-                }
-            }
-        }
-        return count;
-    }
-
-    public void updateChannelsBypassingDnd() {
-        synchronized (mRecords) {
-            final int numRecords = mRecords.size();
-            for (int recordIndex = 0; recordIndex < numRecords; recordIndex++) {
-                final Record r = mRecords.valueAt(recordIndex);
-                final int numChannels = r.channels.size();
-
-                for (int channelIndex = 0; channelIndex < numChannels; channelIndex++) {
-                    NotificationChannel channel = r.channels.valueAt(channelIndex);
-                    if (!channel.isDeleted() && channel.canBypassDnd()) {
-                        if (!mAreChannelsBypassingDnd) {
-                            mAreChannelsBypassingDnd = true;
-                            updateZenPolicy(true);
-                        }
-                        return;
-                    }
-                }
-            }
-        }
-
-        if (mAreChannelsBypassingDnd) {
-            mAreChannelsBypassingDnd = false;
-            updateZenPolicy(false);
-        }
-    }
-
-    public void updateZenPolicy(boolean areChannelsBypassingDnd) {
-        NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
-        mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
-                policy.priorityCategories, policy.priorityCallSenders,
-                policy.priorityMessageSenders, policy.suppressedVisualEffects,
-                (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
-                        : 0)));
-    }
-
-    public boolean areChannelsBypassingDnd() {
-        return mAreChannelsBypassingDnd;
-    }
-
-    /**
-     * Sets importance.
-     */
-    @Override
-    public void setImportance(String pkgName, int uid, int importance) {
-        getOrCreateRecord(pkgName, uid).importance = importance;
-        updateConfig();
-    }
-
-    public void setEnabled(String packageName, int uid, boolean enabled) {
-        boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
-        if (wasEnabled == enabled) {
-            return;
-        }
-        setImportance(packageName, uid,
-                enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
-    }
-
-    /**
-     * Sets whether any notifications from the app, represented by the given {@code pkgName} and
-     * {@code uid}, have their importance locked by the user. Locked notifications don't get
-     * considered for sentiment adjustments (and thus never show a blocking helper).
-     */
-    public void setAppImportanceLocked(String packageName, int uid) {
-        Record record = getOrCreateRecord(packageName, uid);
-        if ((record.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
-            return;
-        }
-
-        record.lockedAppFields = record.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
-        updateConfig();
-    }
-
-    @VisibleForTesting
-    void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) {
-        if (original.canBypassDnd() != update.canBypassDnd()) {
-            update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
-        }
-        if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
-            update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
-        }
-        if (original.getImportance() != update.getImportance()) {
-            update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-        }
-        if (original.shouldShowLights() != update.shouldShowLights()
-                || original.getLightColor() != update.getLightColor()) {
-            update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
-        }
-        if (!Objects.equals(original.getSound(), update.getSound())) {
-            update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
-        }
-        if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
-                || original.shouldVibrate() != update.shouldVibrate()) {
-            update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
-        }
-        if (original.canShowBadge() != update.canShowBadge()) {
-            update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
-        }
-    }
-
     public void dump(PrintWriter pw, String prefix,
             @NonNull NotificationManagerService.DumpFilter filter) {
         final int N = mSignalExtractors.length;
@@ -1088,16 +169,6 @@
             pw.print("  ");
             pw.println(mSignalExtractors[i].getClass().getSimpleName());
         }
-
-        pw.print(prefix);
-        pw.println("per-package config:");
-
-        pw.println("Records:");
-        synchronized (mRecords) {
-            dumpRecords(pw, prefix, filter, mRecords);
-        }
-        pw.println("Restored without uid:");
-        dumpRecords(pw, prefix, filter, mRestoredWithoutUids);
     }
 
     public void dump(ProtoOutputStream proto,
@@ -1105,375 +176,7 @@
         final int N = mSignalExtractors.length;
         for (int i = 0; i < N; i++) {
             proto.write(RankingHelperProto.NOTIFICATION_SIGNAL_EXTRACTORS,
-                mSignalExtractors[i].getClass().getSimpleName());
-        }
-        synchronized (mRecords) {
-            dumpRecords(proto, RankingHelperProto.RECORDS, filter, mRecords);
-        }
-        dumpRecords(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
-            mRestoredWithoutUids);
-    }
-
-    private static void dumpRecords(ProtoOutputStream proto, long fieldId,
-            @NonNull NotificationManagerService.DumpFilter filter,
-            ArrayMap<String, Record> records) {
-        final int N = records.size();
-        long fToken;
-        for (int i = 0; i < N; i++) {
-            final Record r = records.valueAt(i);
-            if (filter.matches(r.pkg)) {
-                fToken = proto.start(fieldId);
-
-                proto.write(RecordProto.PACKAGE, r.pkg);
-                proto.write(RecordProto.UID, r.uid);
-                proto.write(RecordProto.IMPORTANCE, r.importance);
-                proto.write(RecordProto.PRIORITY, r.priority);
-                proto.write(RecordProto.VISIBILITY, r.visibility);
-                proto.write(RecordProto.SHOW_BADGE, r.showBadge);
-
-                for (NotificationChannel channel : r.channels.values()) {
-                    channel.writeToProto(proto, RecordProto.CHANNELS);
-                }
-                for (NotificationChannelGroup group : r.groups.values()) {
-                    group.writeToProto(proto, RecordProto.CHANNEL_GROUPS);
-                }
-
-                proto.end(fToken);
-            }
+                    mSignalExtractors[i].getClass().getSimpleName());
         }
     }
-
-    private static void dumpRecords(PrintWriter pw, String prefix,
-            @NonNull NotificationManagerService.DumpFilter filter,
-            ArrayMap<String, Record> records) {
-        final int N = records.size();
-        for (int i = 0; i < N; i++) {
-            final Record r = records.valueAt(i);
-            if (filter.matches(r.pkg)) {
-                pw.print(prefix);
-                pw.print("  AppSettings: ");
-                pw.print(r.pkg);
-                pw.print(" (");
-                pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
-                pw.print(')');
-                if (r.importance != DEFAULT_IMPORTANCE) {
-                    pw.print(" importance=");
-                    pw.print(Ranking.importanceToString(r.importance));
-                }
-                if (r.priority != DEFAULT_PRIORITY) {
-                    pw.print(" priority=");
-                    pw.print(Notification.priorityToString(r.priority));
-                }
-                if (r.visibility != DEFAULT_VISIBILITY) {
-                    pw.print(" visibility=");
-                    pw.print(Notification.visibilityToString(r.visibility));
-                }
-                pw.print(" showBadge=");
-                pw.print(Boolean.toString(r.showBadge));
-                pw.println();
-                for (NotificationChannel channel : r.channels.values()) {
-                    pw.print(prefix);
-                    pw.print("  ");
-                    pw.print("  ");
-                    pw.println(channel);
-                }
-                for (NotificationChannelGroup group : r.groups.values()) {
-                    pw.print(prefix);
-                    pw.print("  ");
-                    pw.print("  ");
-                    pw.println(group);
-                }
-            }
-        }
-    }
-
-    public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
-        JSONObject ranking = new JSONObject();
-        JSONArray records = new JSONArray();
-        try {
-            ranking.put("noUid", mRestoredWithoutUids.size());
-        } catch (JSONException e) {
-           // pass
-        }
-        synchronized (mRecords) {
-            final int N = mRecords.size();
-            for (int i = 0; i < N; i++) {
-                final Record r = mRecords.valueAt(i);
-                if (filter == null || filter.matches(r.pkg)) {
-                    JSONObject record = new JSONObject();
-                    try {
-                        record.put("userId", UserHandle.getUserId(r.uid));
-                        record.put("packageName", r.pkg);
-                        if (r.importance != DEFAULT_IMPORTANCE) {
-                            record.put("importance", Ranking.importanceToString(r.importance));
-                        }
-                        if (r.priority != DEFAULT_PRIORITY) {
-                            record.put("priority", Notification.priorityToString(r.priority));
-                        }
-                        if (r.visibility != DEFAULT_VISIBILITY) {
-                            record.put("visibility", Notification.visibilityToString(r.visibility));
-                        }
-                        if (r.showBadge != DEFAULT_SHOW_BADGE) {
-                            record.put("showBadge", Boolean.valueOf(r.showBadge));
-                        }
-                        JSONArray channels = new JSONArray();
-                        for (NotificationChannel channel : r.channels.values()) {
-                            channels.put(channel.toJson());
-                        }
-                        record.put("channels", channels);
-                        JSONArray groups = new JSONArray();
-                        for (NotificationChannelGroup group : r.groups.values()) {
-                            groups.put(group.toJson());
-                        }
-                        record.put("groups", groups);
-                    } catch (JSONException e) {
-                        // pass
-                    }
-                    records.put(record);
-                }
-            }
-        }
-        try {
-            ranking.put("records", records);
-        } catch (JSONException e) {
-            // pass
-        }
-        return ranking;
-    }
-
-    /**
-     * Dump only the ban information as structured JSON for the stats collector.
-     *
-     * This is intentionally redundant with {#link dumpJson} because the old
-     * scraper will expect this format.
-     *
-     * @param filter
-     * @return
-     */
-    public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
-        JSONArray bans = new JSONArray();
-        Map<Integer, String> packageBans = getPackageBans();
-        for(Entry<Integer, String> ban : packageBans.entrySet()) {
-            final int userId = UserHandle.getUserId(ban.getKey());
-            final String packageName = ban.getValue();
-            if (filter == null || filter.matches(packageName)) {
-                JSONObject banJson = new JSONObject();
-                try {
-                    banJson.put("userId", userId);
-                    banJson.put("packageName", packageName);
-                } catch (JSONException e) {
-                    e.printStackTrace();
-                }
-                bans.put(banJson);
-            }
-        }
-        return bans;
-    }
-
-    public Map<Integer, String> getPackageBans() {
-        synchronized (mRecords) {
-            final int N = mRecords.size();
-            ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
-            for (int i = 0; i < N; i++) {
-                final Record r = mRecords.valueAt(i);
-                if (r.importance == IMPORTANCE_NONE) {
-                    packageBans.put(r.uid, r.pkg);
-                }
-            }
-
-            return packageBans;
-        }
-    }
-
-    /**
-     * Dump only the channel information as structured JSON for the stats collector.
-     *
-     * This is intentionally redundant with {#link dumpJson} because the old
-     * scraper will expect this format.
-     *
-     * @param filter
-     * @return
-     */
-    public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
-        JSONArray channels = new JSONArray();
-        Map<String, Integer> packageChannels = getPackageChannels();
-        for(Entry<String, Integer> channelCount : packageChannels.entrySet()) {
-            final String packageName = channelCount.getKey();
-            if (filter == null || filter.matches(packageName)) {
-                JSONObject channelCountJson = new JSONObject();
-                try {
-                    channelCountJson.put("packageName", packageName);
-                    channelCountJson.put("channelCount", channelCount.getValue());
-                } catch (JSONException e) {
-                    e.printStackTrace();
-                }
-                channels.put(channelCountJson);
-            }
-        }
-        return channels;
-    }
-
-    private Map<String, Integer> getPackageChannels() {
-        ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
-        synchronized (mRecords) {
-            for (int i = 0; i < mRecords.size(); i++) {
-                final Record r = mRecords.valueAt(i);
-                int channelCount = 0;
-                for (int j = 0; j < r.channels.size(); j++) {
-                    if (!r.channels.valueAt(j).isDeleted()) {
-                        channelCount++;
-                    }
-                }
-                packageChannels.put(r.pkg, channelCount);
-            }
-        }
-        return packageChannels;
-    }
-
-    public void onUserRemoved(int userId) {
-        synchronized (mRecords) {
-            int N = mRecords.size();
-            for (int i = N - 1; i >= 0 ; i--) {
-                Record record = mRecords.valueAt(i);
-                if (UserHandle.getUserId(record.uid) == userId) {
-                    mRecords.removeAt(i);
-                }
-            }
-        }
-    }
-
-    protected void onLocaleChanged(Context context, int userId) {
-        synchronized (mRecords) {
-            int N = mRecords.size();
-            for (int i = 0; i < N; i++) {
-                Record record = mRecords.valueAt(i);
-                if (UserHandle.getUserId(record.uid) == userId) {
-                    if (record.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
-                        record.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(
-                                context.getResources().getString(
-                                        R.string.default_notification_channel_label));
-                    }
-                }
-            }
-        }
-    }
-
-    public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
-            int[] uidList) {
-        if (pkgList == null || pkgList.length == 0) {
-            return; // nothing to do
-        }
-        boolean updated = false;
-        if (removingPackage) {
-            // Remove notification settings for uninstalled package
-            int size = Math.min(pkgList.length, uidList.length);
-            for (int i = 0; i < size; i++) {
-                final String pkg = pkgList[i];
-                final int uid = uidList[i];
-                synchronized (mRecords) {
-                    mRecords.remove(recordKey(pkg, uid));
-                }
-                mRestoredWithoutUids.remove(pkg);
-                updated = true;
-            }
-        } else {
-            for (String pkg : pkgList) {
-                // Package install
-                final Record r = mRestoredWithoutUids.get(pkg);
-                if (r != null) {
-                    try {
-                        r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
-                        mRestoredWithoutUids.remove(pkg);
-                        synchronized (mRecords) {
-                            mRecords.put(recordKey(r.pkg, r.uid), r);
-                        }
-                        updated = true;
-                    } catch (NameNotFoundException e) {
-                        // noop
-                    }
-                }
-                // Package upgrade
-                try {
-                    Record fullRecord = getRecord(pkg,
-                            mPm.getPackageUidAsUser(pkg, changeUserId));
-                    if (fullRecord != null) {
-                        createDefaultChannelIfNeeded(fullRecord);
-                        deleteDefaultChannelIfNeeded(fullRecord);
-                    }
-                } catch (NameNotFoundException e) {}
-            }
-        }
-
-        if (updated) {
-            updateConfig();
-        }
-    }
-
-    private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
-        return new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL)
-                .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
-                .setPackageName(pkg)
-                .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID,
-                        channel.getId())
-                .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
-                        channel.getImportance());
-    }
-
-    private LogMaker getChannelGroupLog(String groupId, String pkg) {
-        return new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL_GROUP)
-                .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
-                .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
-                        groupId)
-                .setPackageName(pkg);
-    }
-
-    public void updateBadgingEnabled() {
-        if (mBadgingEnabled == null) {
-            mBadgingEnabled = new SparseBooleanArray();
-        }
-        boolean changed = false;
-        // update the cached values
-        for (int index = 0; index < mBadgingEnabled.size(); index++) {
-            int userId = mBadgingEnabled.keyAt(index);
-            final boolean oldValue = mBadgingEnabled.get(userId);
-            final boolean newValue = Secure.getIntForUser(mContext.getContentResolver(),
-                    Secure.NOTIFICATION_BADGING,
-                    DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
-            mBadgingEnabled.put(userId, newValue);
-            changed |= oldValue != newValue;
-        }
-        if (changed) {
-            updateConfig();
-        }
-    }
-
-    public boolean badgingEnabled(UserHandle userHandle) {
-        int userId = userHandle.getIdentifier();
-        if (userId == UserHandle.USER_ALL) {
-            return false;
-        }
-        if (mBadgingEnabled.indexOfKey(userId) < 0) {
-            mBadgingEnabled.put(userId,
-                    Secure.getIntForUser(mContext.getContentResolver(),
-                            Secure.NOTIFICATION_BADGING,
-                            DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
-        }
-        return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
-    }
-
-
-    private static class Record {
-        static int UNKNOWN_UID = UserHandle.USER_NULL;
-
-        String pkg;
-        int uid = UNKNOWN_UID;
-        int importance = DEFAULT_IMPORTANCE;
-        int priority = DEFAULT_PRIORITY;
-        int visibility = DEFAULT_VISIBILITY;
-        boolean showBadge = DEFAULT_SHOW_BADGE;
-        int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
-
-        ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
-        Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
-   }
 }
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 6760875..b016faf 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -36,7 +36,8 @@
 
 public class ZenLog {
     private static final String TAG = "ZenLog";
-    private static final boolean DEBUG = Build.IS_DEBUGGABLE;
+    // the ZenLog is *very* verbose, so be careful about setting this to true
+    private static final boolean DEBUG = false;
 
     private static final int SIZE = Build.IS_DEBUGGABLE ? 100 : 20;
 
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 63c0baf..8013f7a 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -59,7 +59,8 @@
         pw.print(prefix); pw.print("mSubscriptions="); pw.println(mSubscriptions);
     }
 
-    public void evaluateConfig(ZenModeConfig config, boolean processSubscriptions) {
+    public void evaluateConfig(ZenModeConfig config, ComponentName trigger,
+            boolean processSubscriptions) {
         if (config == null) return;
         if (config.manualRule != null && config.manualRule.condition != null
                 && !config.manualRule.isTrueOrUnknown()) {
@@ -67,9 +68,9 @@
             config.manualRule = null;
         }
         final ArraySet<Uri> current = new ArraySet<>();
-        evaluateRule(config.manualRule, current, processSubscriptions);
+        evaluateRule(config.manualRule, current, null, processSubscriptions);
         for (ZenRule automaticRule : config.automaticRules.values()) {
-            evaluateRule(automaticRule, current, processSubscriptions);
+            evaluateRule(automaticRule, current, trigger, processSubscriptions);
             updateSnoozing(automaticRule);
         }
 
@@ -102,7 +103,7 @@
     @Override
     public void onServiceAdded(ComponentName component) {
         if (DEBUG) Log.d(TAG, "onServiceAdded " + component);
-        mHelper.setConfig(mHelper.getConfig(), "zmc.onServiceAdded");
+        mHelper.setConfig(mHelper.getConfig(), component, "zmc.onServiceAdded");
     }
 
     @Override
@@ -110,17 +111,22 @@
         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, "conditionChanged");
+            mHelper.setConfig(config, trigger, "conditionChanged");
         }
     }
 
-    private void evaluateRule(ZenRule rule, ArraySet<Uri> current, boolean processSubscriptions) {
+    private void evaluateRule(ZenRule rule, ArraySet<Uri> current, ComponentName trigger,
+            boolean processSubscriptions) {
         if (rule == null || rule.conditionId == null) return;
         final Uri id = rule.conditionId;
         boolean isSystemCondition = false;
@@ -146,7 +152,8 @@
         if (current != null) {
             current.add(id);
         }
-        if (processSubscriptions) {
+        if (processSubscriptions && trigger != null && trigger.equals(rule.component)) {
+            if (DEBUG) Log.d(TAG, "Subscribing to " + rule.component);
             if (mConditionProviders.subscribeIfNecessary(rule.component, rule.conditionId)) {
                 synchronized (mSubscriptions) {
                     mSubscriptions.put(rule.conditionId, rule.component);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 669d556..7149720 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -225,7 +225,7 @@
             config.user = user;
         }
         synchronized (mConfig) {
-            setConfigLocked(config, reason);
+            setConfigLocked(config, null, reason);
         }
         cleanUpZenRules();
     }
@@ -312,7 +312,7 @@
             ZenRule rule = new ZenRule();
             populateZenRule(automaticZenRule, rule, true);
             newConfig.automaticRules.put(rule.id, rule);
-            if (setConfigLocked(newConfig, reason, true)) {
+            if (setConfigLocked(newConfig, reason, rule.component, true)) {
                 return rule.id;
             } else {
                 throw new AndroidRuntimeException("Could not create rule");
@@ -342,7 +342,7 @@
             }
             populateZenRule(automaticZenRule, rule, false);
             newConfig.automaticRules.put(ruleId, rule);
-            return setConfigLocked(newConfig, reason, true);
+            return setConfigLocked(newConfig, reason, rule.component, true);
         }
     }
 
@@ -360,7 +360,7 @@
                 throw new SecurityException(
                         "Cannot delete rules not owned by your condition provider");
             }
-            return setConfigLocked(newConfig, reason, true);
+            return setConfigLocked(newConfig, reason, null, true);
         }
     }
 
@@ -376,7 +376,7 @@
                     newConfig.automaticRules.removeAt(i);
                 }
             }
-            return setConfigLocked(newConfig, reason, true);
+            return setConfigLocked(newConfig, reason, null, true);
         }
     }
 
@@ -508,8 +508,8 @@
 
     public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) {
         setManualZenMode(zenMode, conditionId, reason, caller, true /*setRingerMode*/);
-        Settings.Global.putInt(mContext.getContentResolver(), Global.SHOW_ZEN_SETTINGS_SUGGESTION,
-                0);
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.SHOW_ZEN_SETTINGS_SUGGESTION, 0);
     }
 
     private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller,
@@ -537,7 +537,7 @@
                 newRule.enabler = caller;
                 newConfig.manualRule = newRule;
             }
-            setConfigLocked(newConfig, reason, setRingerMode);
+            setConfigLocked(newConfig, reason, null, setRingerMode);
         }
     }
 
@@ -612,7 +612,11 @@
                 config.manualRule = null;  // don't restore the manual rule
             }
 
-            boolean resetToDefaultRules = true;
+            // booleans to determine whether to reset the rules to the default rules
+            boolean allRulesDisabled = true;
+            boolean hasDefaultRules = config.automaticRules.containsAll(
+                    ZenModeConfig.DEFAULT_RULE_IDS);
+
             long time = System.currentTimeMillis();
             if (config.automaticRules != null && config.automaticRules.size() > 0) {
                 for (ZenRule automaticRule : config.automaticRules.values()) {
@@ -622,29 +626,32 @@
                         automaticRule.condition = null;
                         automaticRule.creationTime = time;
                     }
-                    resetToDefaultRules &= !automaticRule.enabled;
+
+                    allRulesDisabled &= !automaticRule.enabled;
                 }
             }
 
-            if (config.version < ZenModeConfig.XML_VERSION || forRestore) {
-                Settings.Global.putInt(mContext.getContentResolver(),
-                        Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
+            if (!hasDefaultRules && allRulesDisabled
+                    && (forRestore || config.version < ZenModeConfig.XML_VERSION)) {
+                // reset zen automatic rules to default on restore or upgrade if:
+                // - doesn't already have default rules and
+                // - all previous automatic rules were disabled
+                config.automaticRules = new ArrayMap<>();
+                appendDefaultRules(config);
+                reason += ", reset to default rules";
+            }
 
-                // resets zen automatic rules to default
-                // if all prev auto rules were disabled on update
-                if (resetToDefaultRules) {
-                    config.automaticRules = new ArrayMap<>();
-                    appendDefaultRules(config);
-                    reason += ", reset to default rules";
-                }
+            if (config.version < ZenModeConfig.XML_VERSION) {
+                Settings.Secure.putInt(mContext.getContentResolver(),
+                        Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
             } else {
                 // devices not restoring/upgrading already have updated zen settings
-                Settings.Global.putInt(mContext.getContentResolver(),
-                        Global.ZEN_SETTINGS_UPDATED, 1);
+                Settings.Secure.putInt(mContext.getContentResolver(),
+                        Settings.Secure.ZEN_SETTINGS_UPDATED, 1);
             }
             if (DEBUG) Log.d(TAG, reason);
             synchronized (mConfig) {
-                setConfigLocked(config, reason);
+                setConfigLocked(config, null, reason);
             }
         }
     }
@@ -673,7 +680,7 @@
         synchronized (mConfig) {
             final ZenModeConfig newConfig = mConfig.copy();
             newConfig.applyNotificationPolicy(policy);
-            setConfigLocked(newConfig, "setNotificationPolicy");
+            setConfigLocked(newConfig, null, "setNotificationPolicy");
         }
     }
 
@@ -697,7 +704,7 @@
                     }
                 }
             }
-            setConfigLocked(newConfig, "cleanUpZenRules");
+            setConfigLocked(newConfig, null, "cleanUpZenRules");
         }
     }
 
@@ -710,17 +717,19 @@
         }
     }
 
-    public boolean setConfigLocked(ZenModeConfig config, String reason) {
-        return setConfigLocked(config, reason, true /*setRingerMode*/);
+    public boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
+            String reason) {
+        return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/);
     }
 
-    public void setConfig(ZenModeConfig config, String reason) {
+    public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason) {
         synchronized (mConfig) {
-            setConfigLocked(config, reason);
+            setConfigLocked(config, triggeringComponent, reason);
         }
     }
 
-    private boolean setConfigLocked(ZenModeConfig config, String reason, boolean setRingerMode) {
+    private boolean setConfigLocked(ZenModeConfig config, String reason,
+            ComponentName triggeringComponent, boolean setRingerMode) {
         final long identity = Binder.clearCallingIdentity();
         try {
             if (config == null || !config.isValid()) {
@@ -733,7 +742,8 @@
                 if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
                 return true;
             }
-            mConditions.evaluateConfig(config, false /*processSubscriptions*/);  // may modify config
+            // 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);
@@ -746,7 +756,7 @@
                 dispatchOnPolicyChanged();
             }
             mConfig = config;
-            mHandler.postApplyConfig(config, reason, setRingerMode);
+            mHandler.postApplyConfig(config, reason, triggeringComponent, setRingerMode);
             return true;
         } catch (SecurityException e) {
             Log.wtf(TAG, "Invalid rule in config", e);
@@ -756,13 +766,14 @@
         }
     }
 
-    private void applyConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
+    private void applyConfig(ZenModeConfig config, String reason,
+            ComponentName triggeringComponent, boolean setRingerMode) {
         final String val = Integer.toString(config.hashCode());
         Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
         if (!evaluateZenMode(reason, setRingerMode)) {
             applyRestrictions();  // evaluateZenMode will also apply restrictions if changed
         }
-        mConditions.evaluateConfig(config, true /*processSubscriptions*/);
+        mConditions.evaluateConfig(config, triggeringComponent, true /*processSubscriptions*/);
     }
 
     private int getZenModeSetting() {
@@ -820,10 +831,10 @@
                 if (automaticRule.isAutomaticActive()) {
                     if (zenSeverity(automaticRule.zenMode) > zenSeverity(zen)) {
                         // automatic rule triggered dnd and user hasn't seen update dnd dialog
-                        if (Settings.Global.getInt(mContext.getContentResolver(),
-                                Global.ZEN_SETTINGS_SUGGESTION_VIEWED, 1) == 0) {
-                            Settings.Global.putInt(mContext.getContentResolver(),
-                                    Global.SHOW_ZEN_SETTINGS_SUGGESTION, 1);
+                        if (Settings.Secure.getInt(mContext.getContentResolver(),
+                                Settings.Secure.ZEN_SETTINGS_SUGGESTION_VIEWED, 1) == 0) {
+                            Settings.Secure.putInt(mContext.getContentResolver(),
+                                    Settings.Secure.SHOW_ZEN_SETTINGS_SUGGESTION, 1);
                         }
                         zen = automaticRule.zenMode;
                     }
@@ -1182,16 +1193,24 @@
     }
 
     private void showZenUpgradeNotification(int zen) {
+        final boolean isWatch = mContext.getPackageManager().hasSystemFeature(
+            PackageManager.FEATURE_WATCH);
         final boolean showNotification = mIsBootComplete
                 && zen != Global.ZEN_MODE_OFF
+                && !isWatch
                 && Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0;
+                Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0;
+
+        if (isWatch) {
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
+        }
 
         if (showNotification) {
             mNotificationManager.notify(TAG, SystemMessage.NOTE_ZEN_UPGRADE,
                     createZenUpgradeNotification());
-            Settings.Global.putInt(mContext.getContentResolver(),
-                    Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
         }
     }
 
@@ -1260,13 +1279,16 @@
 
         private final class ConfigMessageData {
             public final ZenModeConfig config;
+            public ComponentName triggeringComponent;
             public final String reason;
             public final boolean setRingerMode;
 
-            ConfigMessageData(ZenModeConfig config, String reason, boolean setRingerMode) {
+            ConfigMessageData(ZenModeConfig config, String reason,
+                    ComponentName triggeringComponent, boolean setRingerMode) {
                 this.config = config;
                 this.reason = reason;
                 this.setRingerMode = setRingerMode;
+                this.triggeringComponent = triggeringComponent;
             }
         }
 
@@ -1286,9 +1308,10 @@
             sendEmptyMessageDelayed(MSG_METRICS, METRICS_PERIOD_MS);
         }
 
-        private void postApplyConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
+        private void postApplyConfig(ZenModeConfig config, String reason,
+                ComponentName triggeringComponent, boolean setRingerMode) {
             sendMessage(obtainMessage(MSG_APPLY_CONFIG,
-                    new ConfigMessageData(config, reason, setRingerMode)));
+                    new ConfigMessageData(config, reason, triggeringComponent, setRingerMode)));
         }
 
         @Override
@@ -1303,7 +1326,7 @@
                 case MSG_APPLY_CONFIG:
                     ConfigMessageData applyConfigData = (ConfigMessageData) msg.obj;
                     applyConfig(applyConfigData.config, applyConfigData.reason,
-                            applyConfigData.setRingerMode);
+                            applyConfigData.triggeringComponent, applyConfigData.setRingerMode);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 0774672..d6ab5f7 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -489,7 +489,7 @@
         PinnerService pinnerService = LocalServices.getService(PinnerService.class);
         if (pinnerService != null) {
             Log.i(TAG, "Pinning optimized code " + updatedPackages);
-            pinnerService.update(updatedPackages);
+            pinnerService.update(updatedPackages, false /* force */);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 97f6aa2..65ccecd 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -41,6 +41,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
+import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -112,7 +113,7 @@
 
         launchIntent.setPackage(null);
         launchIntent.setComponent(component);
-        mInjector.getActivityManagerInternal().startActivityAsUser(
+        mInjector.getActivityTaskManagerInternal().startActivityAsUser(
                 caller, callingPackage, launchIntent,
                 ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(),
                 user.getIdentifier());
@@ -241,6 +242,11 @@
         public ActivityManagerInternal getActivityManagerInternal() {
             return LocalServices.getService(ActivityManagerInternal.class);
         }
+
+        @Override
+        public ActivityTaskManagerInternal getActivityTaskManagerInternal() {
+            return LocalServices.getService(ActivityTaskManagerInternal.class);
+        }
     }
 
     @VisibleForTesting
@@ -264,5 +270,7 @@
         AppOpsManager getAppOpsManager();
 
         ActivityManagerInternal getActivityManagerInternal();
+
+        ActivityTaskManagerInternal getActivityTaskManagerInternal();
     }
 }
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index f4ee0ce..fe81133 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -43,6 +43,7 @@
     public static final int DUMP_SERVICE_PERMISSIONS = 1 << 24;
 
     public static final int OPTION_SHOW_FILTERS = 1 << 0;
+    public static final int OPTION_DUMP_ALL_COMPONENTS = 1 << 1;
 
     private int mTypes;
 
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index f0807b9..72f11f7 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -34,6 +34,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.server.SystemService;
 
+import dalvik.system.BlockGuard;
 import dalvik.system.VMRuntime;
 
 import java.io.FileDescriptor;
@@ -239,6 +240,11 @@
             long[] ceDataInodes, String[] codePaths, PackageStats stats)
             throws InstallerException {
         if (!checkBeforeRemote()) return;
+        if (codePaths != null) {
+            for (String codePath : codePaths) {
+                BlockGuard.getVmPolicy().onPathAccess(codePath);
+            }
+        }
         try {
             final long[] res = mInstalld.getAppSize(uuid, packageNames, userId, flags,
                     appId, ceDataInodes, codePaths);
@@ -296,6 +302,9 @@
             @Nullable String profileName, @Nullable String dexMetadataPath,
             @Nullable String compilationReason) throws InstallerException {
         assertValidInstructionSet(instructionSet);
+        BlockGuard.getVmPolicy().onPathAccess(apkPath);
+        BlockGuard.getVmPolicy().onPathAccess(outputPath);
+        BlockGuard.getVmPolicy().onPathAccess(dexMetadataPath);
         if (!checkBeforeRemote()) return;
         try {
             mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
@@ -319,6 +328,7 @@
     public boolean dumpProfiles(int uid, String packageName, String profileName, String codePath)
             throws InstallerException {
         if (!checkBeforeRemote()) return false;
+        BlockGuard.getVmPolicy().onPathAccess(codePath);
         try {
             return mInstalld.dumpProfiles(uid, packageName, profileName, codePath);
         } catch (Exception e) {
@@ -339,6 +349,8 @@
     public void idmap(String targetApkPath, String overlayApkPath, int uid)
             throws InstallerException {
         if (!checkBeforeRemote()) return;
+        BlockGuard.getVmPolicy().onPathAccess(targetApkPath);
+        BlockGuard.getVmPolicy().onPathAccess(overlayApkPath);
         try {
             mInstalld.idmap(targetApkPath, overlayApkPath, uid);
         } catch (Exception e) {
@@ -348,6 +360,7 @@
 
     public void removeIdmap(String overlayApkPath) throws InstallerException {
         if (!checkBeforeRemote()) return;
+        BlockGuard.getVmPolicy().onPathAccess(overlayApkPath);
         try {
             mInstalld.removeIdmap(overlayApkPath);
         } catch (Exception e) {
@@ -358,6 +371,7 @@
     public void rmdex(String codePath, String instructionSet) throws InstallerException {
         assertValidInstructionSet(instructionSet);
         if (!checkBeforeRemote()) return;
+        BlockGuard.getVmPolicy().onPathAccess(codePath);
         try {
             mInstalld.rmdex(codePath, instructionSet);
         } catch (Exception e) {
@@ -367,6 +381,7 @@
 
     public void rmPackageDir(String packageDir) throws InstallerException {
         if (!checkBeforeRemote()) return;
+        BlockGuard.getVmPolicy().onPathAccess(packageDir);
         try {
             mInstalld.rmPackageDir(packageDir);
         } catch (Exception e) {
@@ -439,6 +454,7 @@
     public void linkNativeLibraryDirectory(String uuid, String packageName, String nativeLibPath32,
             int userId) throws InstallerException {
         if (!checkBeforeRemote()) return;
+        BlockGuard.getVmPolicy().onPathAccess(nativeLibPath32);
         try {
             mInstalld.linkNativeLibraryDirectory(uuid, packageName, nativeLibPath32, userId);
         } catch (Exception e) {
@@ -459,6 +475,8 @@
     public void linkFile(String relativePath, String fromBase, String toBase)
             throws InstallerException {
         if (!checkBeforeRemote()) return;
+        BlockGuard.getVmPolicy().onPathAccess(fromBase);
+        BlockGuard.getVmPolicy().onPathAccess(toBase);
         try {
             mInstalld.linkFile(relativePath, fromBase, toBase);
         } catch (Exception e) {
@@ -469,6 +487,8 @@
     public void moveAb(String apkPath, String instructionSet, String outputPath)
             throws InstallerException {
         if (!checkBeforeRemote()) return;
+        BlockGuard.getVmPolicy().onPathAccess(apkPath);
+        BlockGuard.getVmPolicy().onPathAccess(outputPath);
         try {
             mInstalld.moveAb(apkPath, instructionSet, outputPath);
         } catch (Exception e) {
@@ -479,6 +499,8 @@
     public void deleteOdex(String apkPath, String instructionSet, String outputPath)
             throws InstallerException {
         if (!checkBeforeRemote()) return;
+        BlockGuard.getVmPolicy().onPathAccess(apkPath);
+        BlockGuard.getVmPolicy().onPathAccess(outputPath);
         try {
             mInstalld.deleteOdex(apkPath, instructionSet, outputPath);
         } catch (Exception e) {
@@ -489,6 +511,7 @@
     public void installApkVerity(String filePath, FileDescriptor verityInput, int contentSize)
             throws InstallerException {
         if (!checkBeforeRemote()) return;
+        BlockGuard.getVmPolicy().onPathAccess(filePath);
         try {
             mInstalld.installApkVerity(filePath, verityInput, contentSize);
         } catch (Exception e) {
@@ -499,6 +522,7 @@
     public void assertFsverityRootHashMatches(String filePath, @NonNull byte[] expectedHash)
             throws InstallerException {
         if (!checkBeforeRemote()) return;
+        BlockGuard.getVmPolicy().onPathAccess(filePath);
         try {
             mInstalld.assertFsverityRootHashMatches(filePath, expectedHash);
         } catch (Exception e) {
@@ -512,6 +536,7 @@
             assertValidInstructionSet(isas[i]);
         }
         if (!checkBeforeRemote()) return false;
+        BlockGuard.getVmPolicy().onPathAccess(apkPath);
         try {
             return mInstalld.reconcileSecondaryDexFile(apkPath, packageName, uid, isas,
                     volumeUuid, flags);
@@ -523,6 +548,7 @@
     public byte[] hashSecondaryDexFile(String dexPath, String packageName, int uid,
             @Nullable String volumeUuid, int flags) throws InstallerException {
         if (!checkBeforeRemote()) return new byte[0];
+        BlockGuard.getVmPolicy().onPathAccess(dexPath);
         try {
             return mInstalld.hashSecondaryDexFile(dexPath, packageName, uid, volumeUuid, flags);
         } catch (Exception e) {
@@ -571,6 +597,8 @@
     public boolean prepareAppProfile(String pkg, @UserIdInt int userId, @AppIdInt int appId,
             String profileName, String codePath, String dexMetadataPath) throws InstallerException {
         if (!checkBeforeRemote()) return false;
+        BlockGuard.getVmPolicy().onPathAccess(codePath);
+        BlockGuard.getVmPolicy().onPathAccess(dexMetadataPath);
         try {
             return mInstalld.prepareAppProfile(pkg, userId, appId, profileName, codePath,
                     dexMetadataPath);
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index fde13ac..06f67cd 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -134,6 +134,7 @@
         mCookiePersistence = new CookiePersistence(BackgroundThread.getHandler().getLooper());
     }
 
+    @GuardedBy("mService.mPackages")
     public byte[] getInstantAppCookieLPw(@NonNull String packageName,
             @UserIdInt int userId) {
         // Only installed packages can get their own cookie
@@ -157,6 +158,7 @@
         return null;
     }
 
+    @GuardedBy("mService.mPackages")
     public boolean setInstantAppCookieLPw(@NonNull String packageName,
             @Nullable byte[] cookie, @UserIdInt int userId) {
         if (cookie != null && cookie.length > 0) {
@@ -249,6 +251,7 @@
 
     }
 
+    @GuardedBy("mService.mPackages")
     public @Nullable List<InstantAppInfo> getInstantAppsLPr(@UserIdInt int userId) {
         List<InstantAppInfo> installedApps = getInstalledInstantApplicationsLPr(userId);
         List<InstantAppInfo> uninstalledApps = getUninstalledInstantApplicationsLPr(userId);
@@ -261,6 +264,7 @@
         return uninstalledApps;
     }
 
+    @GuardedBy("mService.mPackages")
     public void onPackageInstalledLPw(@NonNull PackageParser.Package pkg, @NonNull int[] userIds) {
         PackageSetting ps = (PackageSetting) pkg.mExtras;
         if (ps == null) {
@@ -331,6 +335,7 @@
         }
     }
 
+    @GuardedBy("mService.mPackages")
     public void onPackageUninstalledLPw(@NonNull PackageParser.Package pkg,
             @NonNull int[] userIds) {
         PackageSetting ps = (PackageSetting) pkg.mExtras;
@@ -356,6 +361,7 @@
         }
     }
 
+    @GuardedBy("mService.mPackages")
     public void onUserRemovedLPw(int userId) {
         if (mUninstalledInstantApps != null) {
             mUninstalledInstantApps.remove(userId);
@@ -394,6 +400,7 @@
         return instantGrantList.get(instantAppId);
     }
 
+    @GuardedBy("mService.mPackages")
     public void grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent,
             int targetAppId, int instantAppId) {
         if (mInstalledInstantAppUids == null) {
@@ -428,6 +435,7 @@
         instantGrantList.put(instantAppId, true /*granted*/);
     }
 
+    @GuardedBy("mService.mPackages")
     public void addInstantAppLPw(@UserIdInt int userId, int instantAppId) {
         if (mInstalledInstantAppUids == null) {
             mInstalledInstantAppUids = new SparseArray<>();
@@ -440,6 +448,7 @@
         instantAppList.put(instantAppId, true /*installed*/);
     }
 
+    @GuardedBy("mService.mPackages")
     private void removeInstantAppLPw(@UserIdInt int userId, int instantAppId) {
         // remove from the installed list
         if (mInstalledInstantAppUids == null) {
@@ -465,6 +474,7 @@
         }
     }
 
+    @GuardedBy("mService.mPackages")
     private void removeAppLPw(@UserIdInt int userId, int targetAppId) {
         // remove from the installed list
         if (mInstantGrants == null) {
@@ -477,6 +487,7 @@
         targetAppList.delete(targetAppId);
     }
 
+    @GuardedBy("mService.mPackages")
     private void addUninstalledInstantAppLPw(@NonNull PackageParser.Package pkg,
             @UserIdInt int userId) {
         InstantAppInfo uninstalledApp = createInstantAppInfoForPackage(
@@ -531,11 +542,13 @@
         }
     }
 
+    @GuardedBy("mService.mPackages")
     boolean hasInstantApplicationMetadataLPr(String packageName, int userId) {
         return hasUninstalledInstantAppStateLPr(packageName, userId)
                 || hasInstantAppMetadataLPr(packageName, userId);
     }
 
+    @GuardedBy("mService.mPackages")
     public void deleteInstantApplicationMetadataLPw(@NonNull String packageName,
             @UserIdInt int userId) {
         removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
@@ -552,6 +565,7 @@
         }
     }
 
+    @GuardedBy("mService.mPackages")
     private void removeUninstalledInstantAppStateLPw(
             @NonNull Predicate<UninstalledInstantAppState> criteria, @UserIdInt int userId) {
         if (mUninstalledInstantApps == null) {
@@ -579,6 +593,7 @@
         }
     }
 
+    @GuardedBy("mService.mPackages")
     private boolean hasUninstalledInstantAppStateLPr(String packageName, @UserIdInt int userId) {
         if (mUninstalledInstantApps == null) {
             return false;
@@ -797,6 +812,7 @@
         return false;
     }
 
+    @GuardedBy("mService.mPackages")
     private @Nullable List<InstantAppInfo> getInstalledInstantApplicationsLPr(
             @UserIdInt int userId) {
         List<InstantAppInfo> result = null;
@@ -851,6 +867,7 @@
         }
     }
 
+    @GuardedBy("mService.mPackages")
     private @Nullable List<InstantAppInfo> getUninstalledInstantApplicationsLPr(
             @UserIdInt int userId) {
         List<UninstalledInstantAppState> uninstalledAppStates =
@@ -923,6 +940,7 @@
         return uninstalledAppState.mInstantAppInfo;
     }
 
+    @GuardedBy("mService.mPackages")
     private @Nullable List<UninstalledInstantAppState> getUninstalledInstantAppStatesLPr(
             @UserIdInt int userId) {
         List<UninstalledInstantAppState> uninstalledAppStates = null;
@@ -1158,15 +1176,13 @@
     private final class CookiePersistence extends Handler {
         private static final long PERSIST_COOKIE_DELAY_MILLIS = 1000L; /* one second */
 
-        // In case you wonder why we stash the cookies aside, we use
-        // the user id for the message id and the package for the payload.
-        // Handler allows removing messages by id and tag where the
-        // tag is compared using ==. So to allow cancelling the
-        // pending persistence for an app under a given user we use
-        // the fact that package are cached by the system so the ==
-        // comparison would match and we end up with a way to cancel
-        // persisting the cookie for a user and package.
-        private final SparseArray<ArrayMap<PackageParser.Package, SomeArgs>> mPendingPersistCookies
+        // The cookies are cached per package name per user-id in this sparse
+        // array. The caching is so that pending persistence can be canceled within
+        // a short interval. To ensure we still return pending persist cookies
+        // for a package that uninstalled and reinstalled while the persistence
+        // was still pending, we use the package name as a key for
+        // mPendingPersistCookies, since that stays stable across reinstalls.
+        private final SparseArray<ArrayMap<String, SomeArgs>> mPendingPersistCookies
                 = new SparseArray<>();
 
         public CookiePersistence(Looper looper) {
@@ -1196,10 +1212,10 @@
 
         public @Nullable byte[] getPendingPersistCookieLPr(@NonNull PackageParser.Package pkg,
                 @UserIdInt int userId) {
-            ArrayMap<PackageParser.Package, SomeArgs> pendingWorkForUser =
+            ArrayMap<String, SomeArgs> pendingWorkForUser =
                     mPendingPersistCookies.get(userId);
             if (pendingWorkForUser != null) {
-                SomeArgs state = pendingWorkForUser.get(pkg);
+                SomeArgs state = pendingWorkForUser.get(pkg.packageName);
                 if (state != null) {
                     return (byte[]) state.arg1;
                 }
@@ -1219,7 +1235,7 @@
         private void addPendingPersistCookieLPw(@UserIdInt int userId,
                 @NonNull PackageParser.Package pkg, @NonNull byte[] cookie,
                 @NonNull File cookieFile) {
-            ArrayMap<PackageParser.Package, SomeArgs> pendingWorkForUser =
+            ArrayMap<String, SomeArgs> pendingWorkForUser =
                     mPendingPersistCookies.get(userId);
             if (pendingWorkForUser == null) {
                 pendingWorkForUser = new ArrayMap<>();
@@ -1228,16 +1244,16 @@
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = cookie;
             args.arg2 = cookieFile;
-            pendingWorkForUser.put(pkg, args);
+            pendingWorkForUser.put(pkg.packageName, args);
         }
 
         private SomeArgs removePendingPersistCookieLPr(@NonNull PackageParser.Package pkg,
                 @UserIdInt int userId) {
-            ArrayMap<PackageParser.Package, SomeArgs> pendingWorkForUser =
+            ArrayMap<String, SomeArgs> pendingWorkForUser =
                     mPendingPersistCookies.get(userId);
             SomeArgs state = null;
             if (pendingWorkForUser != null) {
-                state = pendingWorkForUser.remove(pkg);
+                state = pendingWorkForUser.remove(pkg.packageName);
                 if (pendingWorkForUser.isEmpty()) {
                     mPendingPersistCookies.remove(userId);
                 }
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 9a5dd5e..19cefb8 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -265,13 +265,9 @@
                 | Intent.FLAG_ACTIVITY_NO_HISTORY
                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
         if (token != null) {
-            // TODO(b/72700831): remove populating old extra
-            intent.putExtra(Intent.EXTRA_EPHEMERAL_TOKEN, token);
             intent.putExtra(Intent.EXTRA_INSTANT_APP_TOKEN, token);
         }
         if (origIntent.getData() != null) {
-            // TODO(b/72700831): remove populating old extra
-            intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost());
             intent.putExtra(Intent.EXTRA_INSTANT_APP_HOSTNAME, origIntent.getData().getHost());
         }
         intent.putExtra(Intent.EXTRA_INSTANT_APP_ACTION, origIntent.getAction());
@@ -308,7 +304,6 @@
                                     null /*bOptions*/, userId);
                     IntentSender failureSender = new IntentSender(failureIntentTarget);
                     // TODO(b/72700831): remove populating old extra
-                    intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, failureSender);
                     intent.putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, failureSender);
                 } catch (RemoteException ignore) { /* ignore; same process */ }
             }
@@ -327,8 +322,6 @@
                                         | PendingIntent.FLAG_IMMUTABLE,
                                 null /*bOptions*/, userId);
                 IntentSender successSender = new IntentSender(successIntentTarget);
-                // TODO(b/72700831): remove populating old extra
-                intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, successSender);
                 intent.putExtra(Intent.EXTRA_INSTANT_APP_SUCCESS, successSender);
             } catch (RemoteException ignore) { /* ignore; same process */ }
             if (verificationBundle != null) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index feac8e6..9323040 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -63,6 +63,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.util.Collections;
 import java.util.List;
@@ -107,6 +108,7 @@
         private final UserManager mUm;
         private final UserManagerInternal mUserManagerInternal;
         private final ActivityManagerInternal mActivityManagerInternal;
+        private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
         private final ShortcutServiceInternal mShortcutServiceInternal;
         private final PackageCallbackList<IOnAppsChangedListener> mListeners
                 = new PackageCallbackList<IOnAppsChangedListener>();
@@ -122,6 +124,8 @@
                     LocalServices.getService(UserManagerInternal.class));
             mActivityManagerInternal = Preconditions.checkNotNull(
                     LocalServices.getService(ActivityManagerInternal.class));
+            mActivityTaskManagerInternal = Preconditions.checkNotNull(
+                    LocalServices.getService(ActivityTaskManagerInternal.class));
             mShortcutServiceInternal = Preconditions.checkNotNull(
                     LocalServices.getService(ShortcutServiceInternal.class));
             mShortcutServiceInternal.addListener(mPackageMonitor);
@@ -521,7 +525,7 @@
                 @NonNull String publisherPackage, Bundle startActivityOptions, int userId) {
             final int code;
             try {
-                code = mActivityManagerInternal.startActivitiesAsPackage(publisherPackage,
+                code = mActivityTaskManagerInternal.startActivitiesAsPackage(publisherPackage,
                         userId, intents, startActivityOptions);
                 if (ActivityManager.isStartResultSuccessful(code)) {
                     return true; // Success
@@ -618,7 +622,7 @@
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
-            mActivityManagerInternal.startActivityAsUser(caller, callingPackage,
+            mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
                     launchIntent, opts, user.getIdentifier());
         }
 
@@ -641,7 +645,7 @@
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
-            mActivityManagerInternal.startActivityAsUser(caller, callingPackage,
+            mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
                     intent, opts, user.getIdentifier());
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index b0be4a9..55b1940 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -34,6 +34,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.DexoptUtils;
@@ -289,7 +290,8 @@
             mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
                     compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
                     false /* downgrade*/, pkg.applicationInfo.targetSdkVersion,
-                    profileName, dexMetadataPath, getReasonName(compilationReason));
+                    profileName, dexMetadataPath,
+                    getAugmentedReasonName(compilationReason, dexMetadataPath != null));
 
             if (packageStats != null) {
                 long endTime = System.currentTimeMillis();
@@ -302,6 +304,12 @@
         }
     }
 
+    private String getAugmentedReasonName(int compilationReason, boolean useDexMetadata) {
+        String annotation = useDexMetadata
+                ? ArtManagerService.DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION : "";
+        return getReasonName(compilationReason) + annotation;
+    }
+
     /**
      * Performs dexopt on the secondary dex {@code path} belonging to the app {@code info}.
      *
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index fa934fe..d305032 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -685,13 +685,14 @@
                 // inserted above to hold the session active.
                 try {
                     final Int64Ref last = new Int64Ref(0);
-                    FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, (long progress) -> {
-                        if (params.sizeBytes > 0) {
-                            final long delta = progress - last.value;
-                            last.value = progress;
-                            addClientProgress((float) delta / (float) params.sizeBytes);
-                        }
-                    }, null, lengthBytes);
+                    FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, lengthBytes, null,
+                            Runnable::run, (long progress) -> {
+                                if (params.sizeBytes > 0) {
+                                    final long delta = progress - last.value;
+                                    last.value = progress;
+                                    addClientProgress((float) delta / (float) params.sizeBytes);
+                                }
+                            });
                 } finally {
                     IoUtils.closeQuietly(targetFd);
                     IoUtils.closeQuietly(incomingFd);
@@ -930,6 +931,10 @@
     @GuardedBy("mLock")
     private void commitLocked()
             throws PackageManagerException {
+        if (mRelinquished) {
+            Slog.d(TAG, "Ignoring commit after previous commit relinquished control");
+            return;
+        }
         if (mDestroyed) {
             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
         }
@@ -942,10 +947,10 @@
         Preconditions.checkNotNull(mResolvedBaseFile);
 
         if (needToAskForPermissionsLocked()) {
-            // User needs to accept permissions; give installer an intent they
-            // can use to involve user.
-            final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
-            intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
+            // User needs to confirm installation; give installer an intent they can use to involve
+            // user.
+            final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
+            intent.setPackage(mPm.getPackageInstallerPackageName());
             intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
             try {
                 mRemoteObserver.onUserActionRequired(intent);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0972357..3b67663 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -60,7 +60,6 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
 import static android.content.pm.PackageManager.INSTALL_INTERNAL;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
@@ -91,12 +90,10 @@
 import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDWR;
-
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
 import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
 import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
-import static com.android.internal.util.ArrayUtils.appendInt;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -172,10 +169,10 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManagerInternal.CheckPermissionDelegate;
 import android.content.pm.PackageManagerInternal.PackageListObserver;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.ActivityIntentInfo;
-import android.content.pm.PackageParser.Package;
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.PackageParser.ParseFlags;
@@ -322,11 +319,7 @@
 import com.android.server.pm.permission.PermissionsState.PermissionState;
 import com.android.server.security.VerityUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
-
-import dalvik.system.CloseGuard;
-import dalvik.system.VMRuntime;
-
-import libcore.io.IoUtils;
+import com.android.server.wm.ActivityTaskManagerInternal;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -372,6 +365,10 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Predicate;
 
+import dalvik.system.CloseGuard;
+import dalvik.system.VMRuntime;
+import libcore.io.IoUtils;
+
 /**
  * Keep track of all those APKs everywhere.
  * <p>
@@ -433,7 +430,6 @@
 
     private static final boolean DEBUG_ABI_SELECTION = false;
     private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
-    private static final boolean DEBUG_TRIAGED_MISSING = false;
     private static final boolean DEBUG_APP_DATA = false;
 
     /** REMOVE. According to Svet, this was only used to reset permissions during development. */
@@ -451,16 +447,11 @@
     private static final int SHELL_UID = Process.SHELL_UID;
     private static final int SE_UID = Process.SE_UID;
 
-    // Suffix used during package installation when copying/moving
-    // package apks to install directory.
-    private static final String INSTALL_PACKAGE_SUFFIX = "-";
-
     static final int SCAN_NO_DEX = 1<<0;
     static final int SCAN_UPDATE_SIGNATURE = 1<<1;
     static final int SCAN_NEW_INSTALL = 1<<2;
     static final int SCAN_UPDATE_TIME = 1<<3;
     static final int SCAN_BOOTING = 1<<4;
-    static final int SCAN_DELETE_DATA_ON_FAILURES = 1<<6;
     static final int SCAN_REQUIRE_KNOWN = 1<<7;
     static final int SCAN_MOVE = 1<<8;
     static final int SCAN_INITIAL = 1<<9;
@@ -483,7 +474,6 @@
             SCAN_NEW_INSTALL,
             SCAN_UPDATE_TIME,
             SCAN_BOOTING,
-            SCAN_DELETE_DATA_ON_FAILURES,
             SCAN_REQUIRE_KNOWN,
             SCAN_MOVE,
             SCAN_INITIAL,
@@ -692,11 +682,7 @@
     // as the lock for the global state.  Methods that must be called with
     // this lock held have the prefix "LP".
     @GuardedBy("mPackages")
-    final ArrayMap<String, PackageParser.Package> mPackages =
-            new ArrayMap<String, PackageParser.Package>();
-
-    final ArrayMap<String, Set<String>> mKnownCodebase =
-            new ArrayMap<String, Set<String>>();
+    final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<>();
 
     // Keys are isolated uids and values are the uid of the application
     // that created the isolated proccess.
@@ -795,18 +781,15 @@
             for (PackageParser.Package p : allPackages) {
                 if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) {
                     if (overlayPackages == null) {
-                        overlayPackages = new ArrayList<PackageParser.Package>();
+                        overlayPackages = new ArrayList<>();
                     }
                     overlayPackages.add(p);
                 }
             }
             if (overlayPackages != null) {
-                Comparator<PackageParser.Package> cmp = new Comparator<PackageParser.Package>() {
-                    public int compare(PackageParser.Package p1, PackageParser.Package p2) {
-                        return p1.mOverlayPriority - p2.mOverlayPriority;
-                    }
-                };
-                Collections.sort(overlayPackages, cmp);
+                Comparator<PackageParser.Package> cmp =
+                        Comparator.comparingInt(p -> p.mOverlayPriority);
+                overlayPackages.sort(cmp);
             }
             return overlayPackages;
         }
@@ -820,7 +803,7 @@
             for (PackageParser.Package overlayPackage : overlayPackages) {
                 if (targetPath == null) {
                     if (overlayPathList == null) {
-                        overlayPathList = new ArrayList<String>();
+                        overlayPathList = new ArrayList<>();
                     }
                     overlayPathList.add(overlayPackage.baseCodePath);
                     continue;
@@ -836,7 +819,7 @@
                             UserHandle.getSharedAppGid(
                                     UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
                     if (overlayPathList == null) {
-                        overlayPathList = new ArrayList<String>();
+                        overlayPathList = new ArrayList<>();
                     }
                     overlayPathList.add(overlayPackage.baseCodePath);
                 } catch (InstallerException e) {
@@ -878,7 +861,7 @@
                 for (PackageParser.Package p : mPackages.values()) {
                     if (p.mOverlayIsStatic) {
                         if (mOverlayPackages == null) {
-                            mOverlayPackages = new ArrayList<PackageParser.Package>();
+                            mOverlayPackages = new ArrayList<>();
                         }
                         mOverlayPackages.add(p);
                     }
@@ -939,24 +922,22 @@
 
     // Mapping from provider base names (first directory in content URI codePath)
     // to the provider information.
-    final ArrayMap<String, PackageParser.Provider> mProvidersByAuthority =
-            new ArrayMap<String, PackageParser.Provider>();
+    final ArrayMap<String, PackageParser.Provider> mProvidersByAuthority = new ArrayMap<>();
 
     // Mapping from instrumentation class names to info about them.
     final ArrayMap<ComponentName, PackageParser.Instrumentation> mInstrumentation =
-            new ArrayMap<ComponentName, PackageParser.Instrumentation>();
+            new ArrayMap<>();
 
     // Packages whose data we have transfered into another package, thus
     // should no longer exist.
-    final ArraySet<String> mTransferedPackages = new ArraySet<String>();
+    final ArraySet<String> mTransferedPackages = new ArraySet<>();
 
     // Broadcast actions that are only available to the system.
     @GuardedBy("mProtectedBroadcasts")
     final ArraySet<String> mProtectedBroadcasts = new ArraySet<>();
 
     /** List of packages waiting for verification. */
-    final SparseArray<PackageVerificationState> mPendingVerification
-            = new SparseArray<PackageVerificationState>();
+    final SparseArray<PackageVerificationState> mPendingVerification = new SparseArray<>();
 
     final PackageInstallerService mInstallerService;
 
@@ -1007,7 +988,7 @@
     final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
 
     final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
-            = new SparseArray<IntentFilterVerificationState>();
+            = new SparseArray<>();
 
     // TODO remove this and go through mPermissonManager directly
     final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
@@ -1018,6 +999,7 @@
 
     private UserManagerInternal mUserManagerInternal;
     private ActivityManagerInternal mActivityManagerInternal;
+    private ActivityTaskManagerInternal mActivityTaskManagerInternal;
 
     private DeviceIdleController.LocalService mDeviceIdleController;
 
@@ -1036,7 +1018,6 @@
             pkg = _pkg;
             replacing = _replacing;
             userId = _userId;
-            replacing = _replacing;
             verifierUid = _verifierUid;
         }
     }
@@ -1048,10 +1029,13 @@
         void receiveVerificationResponse(int verificationId);
     }
 
+    @GuardedBy("mPackages")
+    private CheckPermissionDelegate mCheckPermissionDelegate;
+
     private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
         private Context mContext;
         private ComponentName mIntentFilterVerifierComponent;
-        private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<Integer>();
+        private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<>();
 
         public IntentVerifierProxy(Context context, ComponentName verifierComponent) {
             mContext = context;
@@ -1141,7 +1125,7 @@
             mIntentFilterVerificationStates.remove(verificationId);
 
             final String packageName = ivs.getPackageName();
-            IntentFilterVerificationInfo ivi = null;
+            IntentFilterVerificationInfo ivi;
 
             synchronized (mPackages) {
                 ivi = mSettings.getIntentFilterVerificationLPr(packageName);
@@ -1246,7 +1230,7 @@
         final SparseArray<ArrayMap<String, ArrayList<String>>> mUidMap;
 
         public PendingPackageBroadcasts() {
-            mUidMap = new SparseArray<ArrayMap<String, ArrayList<String>>>(2);
+            mUidMap = new SparseArray<>(2);
         }
 
         public ArrayList<String> get(int userId, String packageName) {
@@ -1298,7 +1282,7 @@
         private ArrayMap<String, ArrayList<String>> getOrAllocate(int userId) {
             ArrayMap<String, ArrayList<String>> map = mUidMap.get(userId);
             if (map == null) {
-                map = new ArrayMap<String, ArrayList<String>>();
+                map = new ArrayMap<>();
                 mUidMap.put(userId, map);
             }
             return map;
@@ -1313,11 +1297,9 @@
 
     static final int SEND_PENDING_BROADCAST = 1;
     static final int MCS_BOUND = 3;
-    static final int END_COPY = 4;
     static final int INIT_COPY = 5;
     static final int MCS_UNBIND = 6;
     static final int START_CLEANING_PACKAGE = 7;
-    static final int FIND_INSTALL_LOC = 8;
     static final int POST_INSTALL = 9;
     static final int MCS_RECONNECT = 10;
     static final int MCS_GIVE_UP = 11;
@@ -1342,7 +1324,7 @@
     static UserManagerService sUserManager;
 
     // Stores a list of users whose package restrictions file needs to be updated
-    private ArraySet<Integer> mDirtyUsers = new ArraySet<Integer>();
+    private ArraySet<Integer> mDirtyUsers = new ArraySet<>();
 
     final private DefaultContainerConnection mDefContainerConn =
             new DefaultContainerConnection();
@@ -1371,7 +1353,7 @@
         }
     }
 
-    final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<PostInstallData>();
+    final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<>();
     int mNextInstallToken = 1;  // nonzero; will be wrapped back to 1 when ++ overflows
 
     // XML tags for backup/restore of various bits of state
@@ -1406,6 +1388,7 @@
     final @Nullable String mRequiredVerifierPackage;
     final @NonNull String mRequiredInstallerPackage;
     final @NonNull String mRequiredUninstallerPackage;
+    final String mRequiredPermissionControllerPackage;
     final @Nullable String mSetupWizardPackage;
     final @Nullable String mStorageManagerPackage;
     final @Nullable String mSystemTextClassifierPackage;
@@ -1418,7 +1401,7 @@
     class PackageHandler extends Handler {
         private boolean mBound = false;
         final ArrayList<HandlerParams> mPendingInstalls =
-            new ArrayList<HandlerParams>();
+                new ArrayList<>();
 
         private boolean connectToService() {
             if (DEBUG_INSTALL) Log.i(TAG, "Trying to bind to DefaultContainerService");
@@ -1624,9 +1607,6 @@
                     int uids[];
                     Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
                     synchronized (mPackages) {
-                        if (mPendingBroadcasts == null) {
-                            return;
-                        }
                         size = mPendingBroadcasts.size();
                         if (size <= 0) {
                             // Nothing to be done. Just return
@@ -1901,12 +1881,7 @@
     private PermissionCallback mPermissionCallback = new PermissionCallback() {
         @Override
         public void onGidsChanged(int appId, int userId) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
-                }
-            });
+            mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED));
         }
         @Override
         public void onPermissionGranted(int uid, int userId) {
@@ -2542,17 +2517,15 @@
                 }
             }
 
-            if (mFirstBoot) {
+            if (!mOnlyCore && mFirstBoot) {
                 requestCopyPreoptedFiles();
             }
 
-            String customResolverActivity = Resources.getSystem().getString(
+            String customResolverActivityName = Resources.getSystem().getString(
                     R.string.config_customResolverActivity);
-            if (TextUtils.isEmpty(customResolverActivity)) {
-                customResolverActivity = null;
-            } else {
+            if (!TextUtils.isEmpty(customResolverActivityName)) {
                 mCustomResolverComponentName = ComponentName.unflattenFromString(
-                        customResolverActivity);
+                        customResolverActivityName);
             }
 
             long startTime = SystemClock.uptimeMillis();
@@ -2590,6 +2563,8 @@
 
             mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
 
+            int preUpgradeSdkVersion = ver.sdkVersion;
+
             // save off the names of pre-existing system packages prior to scanning; we don't
             // want to automatically grant runtime permissions for new system apps
             if (mPromoteSystemApps) {
@@ -3183,6 +3158,58 @@
 
             checkDefaultBrowser();
 
+            // If a granted permission is split, all new permissions should be granted too
+            if (mIsUpgrade) {
+                final int callingUid = getCallingUid();
+
+                final int numSplitPerms = PackageParser.SPLIT_PERMISSIONS.length;
+                for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
+                    final PackageParser.SplitPermissionInfo splitPerm =
+                            PackageParser.SPLIT_PERMISSIONS[splitPermNum];
+                    final String rootPerm = splitPerm.rootPerm;
+
+                    if (preUpgradeSdkVersion >= splitPerm.targetSdk) {
+                        continue;
+                    }
+
+                    final int numPackages = mPackages.size();
+                    for (int packageNum = 0; packageNum < numPackages; packageNum++) {
+                        final PackageParser.Package pkg = mPackages.valueAt(packageNum);
+
+                        if (pkg.applicationInfo.targetSdkVersion >= splitPerm.targetSdk
+                                || !pkg.requestedPermissions.contains(rootPerm)) {
+                            continue;
+                        }
+
+                        final int userId = UserHandle.getUserId(pkg.applicationInfo.uid);
+                        final String pkgName = pkg.packageName;
+
+                        if (checkPermission(rootPerm, pkgName, userId) == PERMISSION_DENIED) {
+                            continue;
+                        }
+
+                        final String[] newPerms = splitPerm.newPerms;
+
+                        final int numNewPerms = newPerms.length;
+                        for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) {
+                            final String newPerm = newPerms[newPermNum];
+                            if (checkPermission(newPerm, pkgName, userId) == PERMISSION_GRANTED) {
+                                continue;
+                            }
+
+                            if (DEBUG_PERMISSIONS) {
+                                Slog.v(TAG, "Granting " + newPerm + " to " + pkgName
+                                        + " as the root permission " + rootPerm
+                                        + " is already granted");
+                            }
+
+                            mPermissionManager.grantRuntimePermission(newPerm, pkgName, true,
+                                    callingUid, userId, null);
+                        }
+                    }
+                }
+            }
+
             // clear only after permissions and other defaults have been updated
             mExistingSystemPackages.clear();
             mPromoteSystemApps = false;
@@ -3214,6 +3241,7 @@
                 mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
                         PackageManager.SYSTEM_SHARED_LIBRARY_SHARED,
                         SharedLibraryInfo.VERSION_UNDEFINED);
+                mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
             } else {
                 mRequiredVerifierPackage = null;
                 mRequiredInstallerPackage = null;
@@ -3222,6 +3250,7 @@
                 mIntentFilterVerifier = null;
                 mServicesSystemSharedLibraryPackageName = null;
                 mSharedSystemSharedLibraryPackageName = null;
+                mRequiredPermissionControllerPackage = null;
             }
 
             mInstallerService = new PackageInstallerService(context, this);
@@ -3402,7 +3431,7 @@
             }
         }
         if (ret != PackageManager.INSTALL_SUCCEEDED) {
-            if (dstCodePath == null || !dstCodePath.exists()) {
+            if (!dstCodePath.exists()) {
                 return null;
             }
             removeCodePathLI(dstCodePath);
@@ -3412,6 +3441,7 @@
         return dstCodePath;
     }
 
+    @GuardedBy("mPackages")
     private void updateInstantAppInstallerLocked(String modifiedPackage) {
         // we're only interested in updating the installer appliction when 1) it's not
         // already set or 2) the modified package is the installer
@@ -3568,6 +3598,25 @@
         return resolveInfo.getComponentInfo().packageName;
     }
 
+    private @NonNull String getRequiredPermissionControllerLPr() {
+        final Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSIONS);
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+
+        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                UserHandle.USER_SYSTEM);
+        if (matches.size() == 1) {
+            ResolveInfo resolveInfo = matches.get(0);
+            if (!resolveInfo.activityInfo.applicationInfo.isPrivilegedApp()) {
+                throw new RuntimeException("The permissions manager must be a privileged app");
+            }
+            return matches.get(0).getComponentInfo().packageName;
+        } else {
+            throw new RuntimeException("There must be exactly one permissions manager; found "
+                    + matches);
+        }
+    }
+
     private @NonNull ComponentName getIntentFilterVerifierComponentNameLPr() {
         final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
 
@@ -3666,6 +3715,7 @@
         return null;
     }
 
+    @GuardedBy("mPackages")
     private @Nullable ActivityInfo getInstantAppInstallerLPr() {
         String[] orderedActions = Build.IS_ENG
                 ? new String[]{
@@ -3732,6 +3782,7 @@
         return matches.get(0).getComponentInfo().getComponentName();
     }
 
+    @GuardedBy("mPackages")
     private void primeDomainVerificationsLPw(int userId) {
         if (DEBUG_DOMAIN_VERIFICATION) {
             Slog.d(TAG, "Priming domain verifications in user " + userId);
@@ -3753,7 +3804,7 @@
                     for (ActivityIntentInfo filter : a.intents) {
                         if (hasValidDomains(filter)) {
                             if (domains == null) {
-                                domains = new ArraySet<String>();
+                                domains = new ArraySet<>();
                             }
                             domains.addAll(filter.getHostsList());
                         }
@@ -3785,6 +3836,7 @@
         scheduleWriteSettingsLocked();
     }
 
+    @GuardedBy("mPackages")
     private void applyFactoryDefaultBrowserLPw(int userId) {
         // The default browser app's package name is stored in a string resource,
         // with a product-specific overlay used for vendor customization.
@@ -3808,6 +3860,7 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private void calculateDefaultBrowserLPw(int userId) {
         List<String> allBrowsers = resolveAllBrowserApps(userId);
         final String browserPkg = (allBrowsers.size() == 1) ? allBrowsers.get(0) : null;
@@ -3820,7 +3873,7 @@
                 PackageManager.MATCH_ALL, userId);
 
         final int count = list.size();
-        List<String> result = new ArrayList<String>(count);
+        List<String> result = new ArrayList<>(count);
         for (int i=0; i<count; i++) {
             ResolveInfo info = list.get(i);
             if (info.activityInfo == null
@@ -3875,16 +3928,6 @@
         }
     }
 
-    static int[] appendInts(int[] cur, int[] add) {
-        if (add == null) return cur;
-        if (cur == null) return add;
-        final int N = add.length;
-        for (int i=0; i<N; i++) {
-            cur = appendInt(cur, add[i]);
-        }
-        return cur;
-    }
-
     /**
      * Returns whether or not a full application can see an instant application.
      * <p>
@@ -3948,7 +3991,7 @@
                     ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
             // Compute granted permissions only if package has requested permissions
             final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
-                    ? Collections.<String>emptySet() : permissionsState.getPermissions(userId);
+                    ? Collections.emptySet() : permissionsState.getPermissions(userId);
 
             PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags,
                     ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
@@ -4178,6 +4221,7 @@
      *
      * @see #canViewInstantApps(int, int)
      */
+    @GuardedBy("mPackages")
     private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid,
             @Nullable ComponentName component, @ComponentType int componentType, int userId) {
         // if we're in an isolated process, get the real calling UID
@@ -4235,10 +4279,12 @@
     /**
      * @see #filterAppAccessLPr(PackageSetting, int, ComponentName, int, int)
      */
+    @GuardedBy("mPackages")
     private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, int userId) {
         return filterAppAccessLPr(ps, callingUid, null, TYPE_UNKNOWN, userId);
     }
 
+    @GuardedBy("mPackages")
     private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
             int flags) {
         // Callers can access only the libs they depend on, otherwise they need to explicitly
@@ -4437,6 +4483,7 @@
                 ? ParceledListSlice.emptyList() : new ParceledListSlice<>(permissionList);
     }
 
+    @GuardedBy("mPackages")
     private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
             int filterCallingUid, int userId) {
         if (!sUserManager.exists(userId)) return null;
@@ -4522,6 +4569,7 @@
         return null;
     }
 
+    @GuardedBy("mPackages")
     private String normalizePackageNameLPr(String packageName) {
         String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
         return normalizedPackageName != null ? normalizedPackageName : packageName;
@@ -4767,6 +4815,13 @@
         return mActivityManagerInternal;
     }
 
+    private ActivityTaskManagerInternal getActivityTaskManagerInternal() {
+        if (mActivityTaskManagerInternal == null) {
+            mActivityTaskManagerInternal =
+                    LocalServices.getService(ActivityTaskManagerInternal.class);
+        }
+        return mActivityTaskManagerInternal;
+    }
 
     private DeviceIdleController.LocalService getDeviceIdleController() {
         if (mDeviceIdleController == null) {
@@ -4781,22 +4836,6 @@
      */
     private int updateFlagsForPackage(int flags, int userId, Object cookie) {
         final boolean isCallerSystemUser = UserHandle.getCallingUserId() == UserHandle.USER_SYSTEM;
-        boolean triaged = true;
-        if ((flags & (PackageManager.GET_ACTIVITIES | PackageManager.GET_RECEIVERS
-                | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS)) != 0) {
-            // Caller is asking for component details, so they'd better be
-            // asking for specific encryption matching behavior, or be triaged
-            if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                    | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                    | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
-                triaged = false;
-            }
-        }
-        if ((flags & (PackageManager.MATCH_UNINSTALLED_PACKAGES
-                | PackageManager.MATCH_SYSTEM_ONLY
-                | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
-            triaged = false;
-        }
         if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
             // require the permission to be held; the calling uid and given user id referring
             // to the same user is not sufficient
@@ -4813,10 +4852,6 @@
             // MATCH_UNINSTALLED_PACKAGES to query apps in other profiles. b/31000380
             flags |= PackageManager.MATCH_ANY_USER;
         }
-        if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) {
-            Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
-                    + " with flags 0x" + Integer.toHexString(flags), new Throwable());
-        }
         return updateFlags(flags, userId);
     }
 
@@ -4831,25 +4866,6 @@
      * Update given flags when being used to request {@link ComponentInfo}.
      */
     private int updateFlagsForComponent(int flags, int userId, Object cookie) {
-        if (cookie instanceof Intent) {
-            if ((((Intent) cookie).getFlags() & Intent.FLAG_DEBUG_TRIAGED_MISSING) != 0) {
-                flags |= PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
-            }
-        }
-
-        boolean triaged = true;
-        // Caller is asking for component details, so they'd better be
-        // asking for specific encryption matching behavior, or be triaged
-        if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
-            triaged = false;
-        }
-        if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) {
-            Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
-                    + " with flags 0x" + Integer.toHexString(flags), new Throwable());
-        }
-
         return updateFlags(flags, userId);
     }
 
@@ -4956,7 +4972,7 @@
     }
 
     private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
-        if (!getActivityManagerInternal().isCallerRecents(callingUid)) {
+        if (!getActivityTaskManagerInternal().isCallerRecents(callingUid)) {
             return false;
         }
         final long token = Binder.clearCallingIdentity();
@@ -5092,6 +5108,7 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private List<VersionedPackage> getPackagesUsingSharedLibraryLPr(
             SharedLibraryInfo libInfo, int flags, int userId) {
         List<VersionedPackage> versionedPackages = null;
@@ -5247,6 +5264,7 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) {
         for (int i = userList.length - 1; i >= 0; --i) {
             final int userId = userList[i];
@@ -5331,11 +5349,35 @@
 
     @Override
     public int checkPermission(String permName, String pkgName, int userId) {
+        final CheckPermissionDelegate checkPermissionDelegate;
+        synchronized (mPackages) {
+            if (mCheckPermissionDelegate == null)  {
+                return checkPermissionImpl(permName, pkgName, userId);
+            }
+            checkPermissionDelegate = mCheckPermissionDelegate;
+        }
+        return checkPermissionDelegate.checkPermission(permName, pkgName, userId,
+                PackageManagerService.this::checkPermissionImpl);
+    }
+
+    private int checkPermissionImpl(String permName, String pkgName, int userId) {
         return mPermissionManager.checkPermission(permName, pkgName, getCallingUid(), userId);
     }
 
     @Override
     public int checkUidPermission(String permName, int uid) {
+        final CheckPermissionDelegate checkPermissionDelegate;
+        synchronized (mPackages) {
+            if (mCheckPermissionDelegate == null)  {
+                return checkUidPermissionImpl(permName, uid);
+            }
+            checkPermissionDelegate = mCheckPermissionDelegate;
+        }
+        return checkPermissionDelegate.checkUidPermission(permName, uid,
+                PackageManagerService.this::checkUidPermissionImpl);
+    }
+
+    private int checkUidPermissionImpl(String permName, int uid) {
         synchronized (mPackages) {
             final String[] packageNames = getPackagesForUid(uid);
             final PackageParser.Package pkg = (packageNames != null && packageNames.length > 0)
@@ -5381,6 +5423,12 @@
     @Override
     public String getPermissionControllerPackageName() {
         synchronized (mPackages) {
+            return mRequiredPermissionControllerPackage;
+        }
+    }
+
+    String getPackageInstallerPackageName() {
+        synchronized (mPackages) {
             return mRequiredInstallerPackage;
         }
     }
@@ -5757,7 +5805,7 @@
         final int callingUserId = UserHandle.getUserId(callingUid);
         synchronized (mPackages) {
             if (canViewInstantApps(callingUid, callingUserId)) {
-                return new ArrayList<String>(mPackages.keySet());
+                return new ArrayList<>(mPackages.keySet());
             }
             final String instantAppPkgName = getInstantAppPackageName(callingUid);
             final List<String> result = new ArrayList<>();
@@ -6256,6 +6304,7 @@
         return true;
     }
 
+    @GuardedBy("mPackages")
     private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType,
             int flags, List<ResolveInfo> query, boolean debug, int userId) {
         final int N = query.size();
@@ -6629,7 +6678,7 @@
         flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart,
                 comp != null || pkgName != null /*onlyExposedExplicitly*/);
         if (comp != null) {
-            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+            final List<ResolveInfo> list = new ArrayList<>(1);
             final ActivityInfo ai = getActivityInfo(comp, flags, userId);
             if (ai != null) {
                 // When specifying an explicit component, we prevent the activity from being
@@ -6685,7 +6734,7 @@
                 ResolveInfo xpResolveInfo  = querySkipCurrentProfileIntents(matchingFilters, intent,
                         resolvedType, flags, userId);
                 if (xpResolveInfo != null) {
-                    List<ResolveInfo> xpResult = new ArrayList<ResolveInfo>(1);
+                    List<ResolveInfo> xpResult = new ArrayList<>(1);
                     xpResult.add(xpResolveInfo);
                     return applyPostResolutionFilter(
                             filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
@@ -7110,12 +7159,12 @@
                     candidates.size());
         }
 
-        ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>();
-        ArrayList<ResolveInfo> alwaysList = new ArrayList<ResolveInfo>();
-        ArrayList<ResolveInfo> undefinedList = new ArrayList<ResolveInfo>();
-        ArrayList<ResolveInfo> alwaysAskList = new ArrayList<ResolveInfo>();
-        ArrayList<ResolveInfo> neverList = new ArrayList<ResolveInfo>();
-        ArrayList<ResolveInfo> matchAllList = new ArrayList<ResolveInfo>();
+        final ArrayList<ResolveInfo> result = new ArrayList<>();
+        final ArrayList<ResolveInfo> alwaysList = new ArrayList<>();
+        final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
+        final ArrayList<ResolveInfo> alwaysAskList = new ArrayList<>();
+        final ArrayList<ResolveInfo> neverList = new ArrayList<>();
+        final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
 
         synchronized (mPackages) {
             final int count = candidates.size();
@@ -7590,7 +7639,7 @@
             }
         }
         if (comp != null) {
-            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+            final List<ResolveInfo> list = new ArrayList<>(1);
             final ActivityInfo ai = getReceiverInfo(comp, flags, userId);
             if (ai != null) {
                 // When specifying an explicit component, we prevent the activity from being
@@ -7704,7 +7753,7 @@
             }
         }
         if (comp != null) {
-            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+            final List<ResolveInfo> list = new ArrayList<>(1);
             final ServiceInfo si = getServiceInfo(comp, flags, userId);
             if (si != null) {
                 // When specifying an explicit component, we prevent the service from being
@@ -7822,7 +7871,7 @@
             }
         }
         if (comp != null) {
-            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+            final List<ResolveInfo> list = new ArrayList<>(1);
             final ProviderInfo pi = getProviderInfo(comp, flags, userId);
             if (pi != null) {
                 // When specifying an explicit component, we prevent the provider from being
@@ -8021,7 +8070,7 @@
 
         // writer
         synchronized (mPackages) {
-            ArrayList<PackageInfo> list = new ArrayList<PackageInfo>();
+            ArrayList<PackageInfo> list = new ArrayList<>();
             boolean[] tmpBools = new boolean[permissions.length];
             if (listUninstalled) {
                 for (PackageSetting ps : mSettings.mPackages.values()) {
@@ -8038,7 +8087,7 @@
                 }
             }
 
-            return new ParceledListSlice<PackageInfo>(list);
+            return new ParceledListSlice<>(list);
         }
     }
 
@@ -8239,7 +8288,7 @@
     }
 
     private @NonNull List<ApplicationInfo> getPersistentApplicationsInternal(int flags) {
-        final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
+        final ArrayList<ApplicationInfo> finalList = new ArrayList<>();
 
         // reader
         synchronized (mPackages) {
@@ -8368,7 +8417,7 @@
                         continue;
                     }
                     if (finalList == null) {
-                        finalList = new ArrayList<ProviderInfo>(3);
+                        finalList = new ArrayList<>(3);
                     }
                     ProviderInfo info = PackageParser.generateProviderInfo(p, flags,
                             ps.readUserState(userId), userId);
@@ -8380,8 +8429,8 @@
         }
 
         if (finalList != null) {
-            Collections.sort(finalList, mProviderInitOrderSorter);
-            return new ParceledListSlice<ProviderInfo>(finalList);
+            finalList.sort(mProviderInitOrderSorter);
+            return new ParceledListSlice<>(finalList);
         }
 
         return ParceledListSlice.emptyList();
@@ -8417,7 +8466,7 @@
 
     private @NonNull List<InstrumentationInfo> queryInstrumentationInternal(String targetPackage,
             int flags) {
-        ArrayList<InstrumentationInfo> finalList = new ArrayList<InstrumentationInfo>();
+        ArrayList<InstrumentationInfo> finalList = new ArrayList<>();
 
         // reader
         synchronized (mPackages) {
@@ -8487,10 +8536,8 @@
                         renameStaticSharedLibraryPackage(parseResult.pkg);
                     }
                     try {
-                        if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
-                            scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
-                                    currentTime, null);
-                        }
+                        scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
+                                currentTime, null);
                     } catch (PackageManagerException e) {
                         errorCode = e.error;
                         Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
@@ -8586,6 +8633,7 @@
      *  Traces a package scan.
      *  @see #scanPackageLI(File, int, int, long, UserHandle)
      */
+    @GuardedBy("mInstallLock")
     private PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
             int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
@@ -8600,6 +8648,7 @@
      *  Scans a package and returns the newly parsed package.
      *  Returns {@code null} in case of errors and the error code is stored in mLastScanError
      */
+    @GuardedBy({"mInstallLock", "mPackages"})
     private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
             long currentTime, UserHandle user) throws PackageManagerException {
         if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
@@ -8631,6 +8680,7 @@
      *  Scans a package and returns the newly parsed package.
      *  @throws PackageManagerException on a parse error.
      */
+    @GuardedBy({"mInstallLock", "mPackages"})
     private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
             final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user)
@@ -8692,7 +8742,7 @@
      * match one in a trusted source, and should be done separately.
      */
     private boolean canSkipFullApkVerification(String apkPath) {
-        byte[] rootHashObserved = null;
+        final byte[] rootHashObserved;
         try {
             rootHashObserved = VerityUtils.generateFsverityRootHash(apkPath);
             if (rootHashObserved == null) {
@@ -8723,6 +8773,7 @@
      * structures and the package is made available to the rest of the system.
      * <p>NOTE: The return value should be removed. It's the passed in package object.
      */
+    @GuardedBy({"mInstallLock", "mPackages"})
     private PackageParser.Package addForInitLI(PackageParser.Package pkg,
             @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user)
@@ -8861,10 +8912,10 @@
                     + " better than this " + pkg.getLongVersionCode());
         }
 
-        // Verify certificates against what was last scanned. If it is an updated priv app, we will
-        // force re-collecting certificate.
-        final boolean forceCollect = PackageManagerServiceUtils.isApkVerificationForced(
-                disabledPkgSetting);
+        // Verify certificates against what was last scanned. If there was an upgrade or this is an
+        // updated priv app, we will force re-collecting certificate.
+        final boolean forceCollect = mIsUpgrade ||
+                PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting);
         // Full APK verification can be skipped during certificate collection, only if the file is
         // 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.
@@ -8926,15 +8977,20 @@
             }
         }
 
-        final PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags
+        final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags
                 | SCAN_UPDATE_SIGNATURE, currentTime, user);
+        if (scanResult.success) {
+            synchronized (mPackages) {
+                commitScanResultLocked(scanResult);
+            }
+        }
 
         if (shouldHideSystemApp) {
             synchronized (mPackages) {
                 mSettings.disableSystemPackageLPw(pkg.packageName, true);
             }
         }
-        return scannedPkg;
+        return scanResult.pkgSetting.pkg;
     }
 
     private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) {
@@ -8958,7 +9014,7 @@
      * @param message used as message if SecurityException is thrown
      * @throws SecurityException if the caller is not system or root
      */
-    private static final void enforceSystemOrRoot(String message) {
+    private static void enforceSystemOrRoot(String message) {
         final int uid = Binder.getCallingUid();
         if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) {
             throw new SecurityException(message);
@@ -9223,6 +9279,16 @@
     }
 
     @GuardedBy("mPackages")
+    public CheckPermissionDelegate getCheckPermissionDelegateLocked() {
+        return mCheckPermissionDelegate;
+    }
+
+    @GuardedBy("mPackages")
+    public void setCheckPermissionDelegateLocked(CheckPermissionDelegate delegate) {
+        mCheckPermissionDelegate = delegate;
+    }
+
+    @GuardedBy("mPackages")
     private void notifyPackageUseLocked(String packageName, int reason) {
         final PackageParser.Package p = mPackages.get(packageName);
         if (p == null) {
@@ -9361,7 +9427,7 @@
     }
 
     public ArraySet<String> getOptimizablePackages() {
-        ArraySet<String> pkgs = new ArraySet<String>();
+        ArraySet<String> pkgs = new ArraySet<>();
         synchronized (mPackages) {
             for (PackageParser.Package p : mPackages.values()) {
                 if (PackageDexOptimizer.canOptimizePackage(p)) {
@@ -9598,6 +9664,7 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) {
         if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
             Slog.w(TAG, "Unable to update from " + oldPkg.name
@@ -9613,6 +9680,7 @@
         return true;
     }
 
+    @GuardedBy("mInstallLock")
     void removeCodePathLI(File codePath) {
         if (codePath.isDirectory()) {
             try {
@@ -9740,6 +9808,7 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private void addSharedLibraryLPr(Set<String> usesLibraryFiles,
             SharedLibraryEntry file,
             PackageParser.Package changingLib) {
@@ -9765,6 +9834,7 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private void updateSharedLibrariesLPr(PackageParser.Package pkg,
             PackageParser.Package changingLib) throws PackageManagerException {
         if (pkg == null) {
@@ -9797,6 +9867,7 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private Set<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
             @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
             @NonNull String packageName, @Nullable PackageParser.Package changingLib,
@@ -9908,6 +9979,7 @@
         return false;
     }
 
+    @GuardedBy("mPackages")
     private ArrayList<PackageParser.Package> updateAllSharedLibrariesLPw(
             PackageParser.Package changingPkg) {
         ArrayList<PackageParser.Package> res = null;
@@ -9943,7 +10015,8 @@
         return res;
     }
 
-    private PackageParser.Package scanPackageTracedLI(PackageParser.Package pkg,
+    @GuardedBy({"mInstallLock", "mPackages"})
+    private List<ScanResult> scanPackageTracedLI(PackageParser.Package pkg,
             final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user) throws PackageManagerException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
@@ -9960,16 +10033,16 @@
             scanFlags &= ~SCAN_CHECK_ONLY;
         }
 
-        final PackageParser.Package scannedPkg;
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        final List<ScanResult> scanResults = new ArrayList<>(1 + childCount);
         try {
             // Scan the parent
-            scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags, currentTime, user);
+            scanResults.add(scanPackageNewLI(pkg, parseFlags, scanFlags, currentTime, user));
             // Scan the children
-            final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
             for (int i = 0; i < childCount; i++) {
                 PackageParser.Package childPkg = pkg.childPackages.get(i);
-                scanPackageNewLI(childPkg, parseFlags,
-                        scanFlags, currentTime, user);
+                scanResults.add(scanPackageNewLI(childPkg, parseFlags,
+                        scanFlags, currentTime, user));
             }
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -9979,11 +10052,13 @@
             return scanPackageTracedLI(pkg, parseFlags, scanFlags, currentTime, user);
         }
 
-        return scannedPkg;
+        return scanResults;
     }
 
     /** The result of a package scan. */
     private static class ScanResult {
+        /** The request that initiated the scan that produced this result. */
+        public final ScanRequest request;
         /** Whether or not the package scan was successful */
         public final boolean success;
         /**
@@ -9994,9 +10069,10 @@
         /** ABI code paths that have changed in the package scan */
         @Nullable public final List<String> changedAbiCodePath;
         public ScanResult(
-                boolean success,
+                ScanRequest request, boolean success,
                 @Nullable PackageSetting pkgSetting,
                 @Nullable List<String> changedAbiCodePath) {
+            this.request = request;
             this.success = success;
             this.pkgSetting = pkgSetting;
             this.changedAbiCodePath = changedAbiCodePath;
@@ -10138,8 +10214,8 @@
     // the results / removing app data needs to be moved up a level to the callers of this
     // method. Also, we need to solve the problem of potentially creating a new shared user
     // setting. That can probably be done later and patch things up after the fact.
-    @GuardedBy("mInstallLock")
-    private PackageParser.Package scanPackageNewLI(@NonNull PackageParser.Package pkg,
+    @GuardedBy({"mInstallLock", "mPackages"})
+    private ScanResult scanPackageNewLI(@NonNull PackageParser.Package pkg,
             final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user) throws PackageManagerException {
 
@@ -10175,28 +10251,30 @@
                                 + " packages=" + sharedUserSetting.packages);
                 }
             }
+            final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
+                    pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
+                    originalPkgSetting, realPkgName, parseFlags, scanFlags,
+                    (pkg == mPlatformPackage), user);
+            return scanPackageOnlyLI(request, mFactoryTest, currentTime);
+        }
+    }
 
-            boolean scanSucceeded = false;
-            try {
-                final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
-                        pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
-                        originalPkgSetting, realPkgName, parseFlags, scanFlags,
-                        (pkg == mPlatformPackage), user);
-                final ScanResult result = scanPackageOnlyLI(request, mFactoryTest, currentTime);
-                if (result.success) {
-                    commitScanResultsLocked(request, result);
+
+    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);
                 }
-                scanSucceeded = true;
-            } finally {
-                  if (!scanSucceeded && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
-                      // DELETE_DATA_ON_FAILURES is only used by frozen paths
-                      destroyAppDataLIF(pkg, UserHandle.USER_ALL,
-                              StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
-                      destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
-                  }
+            }
+            for (ScanResult result : results) {
+                commitScanResultLocked(result);
             }
         }
-        return pkg;
     }
 
     /**
@@ -10206,9 +10284,9 @@
      * This needs to be fixed so, once we get to this point, no errors are
      * possible and the system is not left in an inconsistent state.
      */
-    @GuardedBy("mPackages")
-    private void commitScanResultsLocked(@NonNull ScanRequest request, @NonNull ScanResult result)
-            throws PackageManagerException {
+    @GuardedBy({"mPackages", "mInstallLock"})
+    private void commitScanResultLocked(@NonNull ScanResult result) throws PackageManagerException {
+        final ScanRequest request = result.request;
         final PackageParser.Package pkg = request.pkg;
         final PackageParser.Package oldPkg = request.oldPkg;
         final @ParseFlags int parseFlags = request.parseFlags;
@@ -10789,7 +10867,7 @@
             pkgSetting.volumeUuid = volumeUuid;
         }
 
-        return new ScanResult(true, pkgSetting, changedAbiCodePath);
+        return new ScanResult(request, true, pkgSetting, changedAbiCodePath);
     }
 
     /**
@@ -12386,7 +12464,8 @@
             if (DEBUG_REMOVE) Log.d(TAG, "  Activities: " + r);
         }
 
-        mPermissionManager.removeAllPermissions(pkg, chatty);
+        final ArrayList<String> allPackageNames = new ArrayList<>(mPackages.keySet());
+        mPermissionManager.removeAllPermissions(pkg, allPackageNames, mPermissionCallback, chatty);
 
         N = pkg.instrumentation.size();
         r = null;
@@ -12937,9 +13016,7 @@
             if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = info;
             }
-            if (info != null) {
-                res.handleAllWebDataURI = info.handleAllWebDataURI();
-            }
+            res.handleAllWebDataURI = info.handleAllWebDataURI();
             res.priority = info.getPriority();
             res.preferredOrder = activity.owner.mPreferredOrder;
             //System.out.println("Result: " + res.activityInfo.className +
@@ -12961,7 +13038,7 @@
 
         @Override
         protected void sortResults(List<ResolveInfo> results) {
-            Collections.sort(results, mResolvePrioritySorter);
+            results.sort(mResolvePrioritySorter);
         }
 
         @Override
@@ -12994,7 +13071,7 @@
 
         // Keys are String (activity class name), values are Activity.
         private final ArrayMap<ComponentName, PackageParser.Activity> mActivities
-                = new ArrayMap<ComponentName, PackageParser.Activity>();
+                = new ArrayMap<>();
         private int mFlags;
     }
 
@@ -13024,8 +13101,7 @@
             mFlags = flags;
             final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
             final int N = packageServices.size();
-            ArrayList<PackageParser.ServiceIntentInfo[]> listCut =
-                new ArrayList<PackageParser.ServiceIntentInfo[]>(N);
+            ArrayList<PackageParser.ServiceIntentInfo[]> listCut = new ArrayList<>(N);
 
             ArrayList<PackageParser.ServiceIntentInfo> intentFilters;
             for (int i = 0; i < N; ++i) {
@@ -13178,7 +13254,7 @@
 
         @Override
         protected void sortResults(List<ResolveInfo> results) {
-            Collections.sort(results, mResolvePrioritySorter);
+            results.sort(mResolvePrioritySorter);
         }
 
         @Override
@@ -13227,8 +13303,7 @@
 //        }
 
         // Keys are String (activity class name), values are Activity.
-        private final ArrayMap<ComponentName, PackageParser.Service> mServices
-                = new ArrayMap<ComponentName, PackageParser.Service>();
+        private final ArrayMap<ComponentName, PackageParser.Service> mServices = new ArrayMap<>();
         private int mFlags;
     }
 
@@ -13260,8 +13335,7 @@
             mFlags = flags;
             final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
             final int N = packageProviders.size();
-            ArrayList<PackageParser.ProviderIntentInfo[]> listCut =
-                    new ArrayList<PackageParser.ProviderIntentInfo[]>(N);
+            ArrayList<PackageParser.ProviderIntentInfo[]> listCut = new ArrayList<>(N);
 
             ArrayList<PackageParser.ProviderIntentInfo> intentFilters;
             for (int i = 0; i < N; ++i) {
@@ -13421,7 +13495,7 @@
 
         @Override
         protected void sortResults(List<ResolveInfo> results) {
-            Collections.sort(results, mResolvePrioritySorter);
+            results.sort(mResolvePrioritySorter);
         }
 
         @Override
@@ -13454,7 +13528,7 @@
         }
 
         private final ArrayMap<ComponentName, PackageParser.Provider> mProviders
-                = new ArrayMap<ComponentName, PackageParser.Provider>();
+                = new ArrayMap<>();
         private int mFlags;
     }
 
@@ -13539,78 +13613,69 @@
         }
     }
 
-    private static final Comparator<ResolveInfo> mResolvePrioritySorter =
-            new Comparator<ResolveInfo>() {
-        public int compare(ResolveInfo r1, ResolveInfo r2) {
-            int v1 = r1.priority;
-            int v2 = r2.priority;
-            //System.out.println("Comparing: q1=" + q1 + " q2=" + q2);
-            if (v1 != v2) {
-                return (v1 > v2) ? -1 : 1;
-            }
-            v1 = r1.preferredOrder;
-            v2 = r2.preferredOrder;
-            if (v1 != v2) {
-                return (v1 > v2) ? -1 : 1;
-            }
-            if (r1.isDefault != r2.isDefault) {
-                return r1.isDefault ? -1 : 1;
-            }
-            v1 = r1.match;
-            v2 = r2.match;
-            //System.out.println("Comparing: m1=" + m1 + " m2=" + m2);
-            if (v1 != v2) {
-                return (v1 > v2) ? -1 : 1;
-            }
-            if (r1.system != r2.system) {
-                return r1.system ? -1 : 1;
-            }
-            if (r1.activityInfo != null) {
-                return r1.activityInfo.packageName.compareTo(r2.activityInfo.packageName);
-            }
-            if (r1.serviceInfo != null) {
-                return r1.serviceInfo.packageName.compareTo(r2.serviceInfo.packageName);
-            }
-            if (r1.providerInfo != null) {
-                return r1.providerInfo.packageName.compareTo(r2.providerInfo.packageName);
-            }
-            return 0;
+    private static final Comparator<ResolveInfo> mResolvePrioritySorter = (r1, r2) -> {
+        int v1 = r1.priority;
+        int v2 = r2.priority;
+        //System.out.println("Comparing: q1=" + q1 + " q2=" + q2);
+        if (v1 != v2) {
+            return (v1 > v2) ? -1 : 1;
         }
+        v1 = r1.preferredOrder;
+        v2 = r2.preferredOrder;
+        if (v1 != v2) {
+            return (v1 > v2) ? -1 : 1;
+        }
+        if (r1.isDefault != r2.isDefault) {
+            return r1.isDefault ? -1 : 1;
+        }
+        v1 = r1.match;
+        v2 = r2.match;
+        //System.out.println("Comparing: m1=" + m1 + " m2=" + m2);
+        if (v1 != v2) {
+            return (v1 > v2) ? -1 : 1;
+        }
+        if (r1.system != r2.system) {
+            return r1.system ? -1 : 1;
+        }
+        if (r1.activityInfo != null) {
+            return r1.activityInfo.packageName.compareTo(r2.activityInfo.packageName);
+        }
+        if (r1.serviceInfo != null) {
+            return r1.serviceInfo.packageName.compareTo(r2.serviceInfo.packageName);
+        }
+        if (r1.providerInfo != null) {
+            return r1.providerInfo.packageName.compareTo(r2.providerInfo.packageName);
+        }
+        return 0;
     };
 
-    private static final Comparator<ProviderInfo> mProviderInitOrderSorter =
-            new Comparator<ProviderInfo>() {
-        public int compare(ProviderInfo p1, ProviderInfo p2) {
-            final int v1 = p1.initOrder;
-            final int v2 = p2.initOrder;
-            return (v1 > v2) ? -1 : ((v1 < v2) ? 1 : 0);
-        }
+    private static final Comparator<ProviderInfo> mProviderInitOrderSorter = (p1, p2) -> {
+        final int v1 = p1.initOrder;
+        final int v2 = p2.initOrder;
+        return (v1 > v2) ? -1 : ((v1 < v2) ? 1 : 0);
     };
 
     @Override
     public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
             final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
             final int[] userIds, int[] instantUserIds) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    final IActivityManager am = ActivityManager.getService();
-                    if (am == null) return;
-                    final int[] resolvedUserIds;
-                    if (userIds == null) {
-                        resolvedUserIds = am.getRunningUserIds();
-                    } else {
-                        resolvedUserIds = userIds;
-                    }
-                    doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
-                            resolvedUserIds, false);
-                    if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
-                        doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
-                                instantUserIds, true);
-                    }
-                } catch (RemoteException ex) {
+        mHandler.post(() -> {
+            try {
+                final IActivityManager am = ActivityManager.getService();
+                if (am == null) return;
+                final int[] resolvedUserIds;
+                if (userIds == null) {
+                    resolvedUserIds = am.getRunningUserIds();
+                } else {
+                    resolvedUserIds = userIds;
                 }
+                doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
+                        resolvedUserIds, false);
+                if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
+                    doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
+                            instantUserIds, true);
+                }
+            } catch (RemoteException ex) {
             }
         });
     }
@@ -14009,6 +14074,68 @@
         return false;
     }
 
+    @Override
+    public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden) {
+        enforceSystemOrPhoneCaller("setSystemAppHiddenUntilInstalled");
+        synchronized (mPackages) {
+            final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
+            if (pkgSetting == null || !pkgSetting.isSystem()) {
+                return;
+            }
+            PackageParser.Package pkg = pkgSetting.pkg;
+            if (pkg != null && pkg.applicationInfo != null) {
+                pkg.applicationInfo.hiddenUntilInstalled = hidden;
+            }
+            final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(packageName);
+            if (disabledPs == null) {
+                return;
+            }
+            pkg = disabledPs.pkg;
+            if (pkg != null && pkg.applicationInfo != null) {
+                pkg.applicationInfo.hiddenUntilInstalled = hidden;
+            }
+        }
+    }
+
+    @Override
+    public boolean setSystemAppInstallState(String packageName, boolean installed, int userId) {
+        enforceSystemOrPhoneCaller("setSystemAppInstallState");
+        synchronized (mPackages) {
+            final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
+            // The target app should always be in system
+            if (pkgSetting == null || !pkgSetting.isSystem()) {
+                return false;
+            }
+            // Check if the install state is the same
+            if (pkgSetting.getInstalled(userId) == installed) {
+                return false;
+            }
+        }
+
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            if (installed) {
+                // install the app from uninstalled state
+                installExistingPackageAsUser(
+                        packageName,
+                        userId,
+                        0 /*installFlags*/,
+                        PackageManager.INSTALL_REASON_DEVICE_SETUP);
+                return true;
+            }
+
+            // uninstall the app from installed state
+            deletePackageVersioned(
+                    new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
+                    new LegacyPackageDeleteObserver(null).getBinder(),
+                    userId,
+                    PackageManager.DELETE_SYSTEM_APP);
+            return true;
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
     private void sendApplicationHiddenForUser(String packageName, PackageSetting pkgSetting,
             int userId) {
         final PackageRemovedInfo info = new PackageRemovedInfo(this);
@@ -14073,10 +14200,16 @@
     @Override
     public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
             int installReason) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
-                null);
-        PackageSetting pkgSetting;
         final int callingUid = Binder.getCallingUid();
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
+                != PackageManager.PERMISSION_GRANTED
+                && mContext.checkCallingOrSelfPermission(
+                        android.Manifest.permission.INSTALL_EXISTING_PACKAGES)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Neither user " + callingUid + " nor current process has "
+                    + android.Manifest.permission.INSTALL_PACKAGES + ".");
+        }
+        PackageSetting pkgSetting;
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, true /* checkShell */,
                 "installExistingPackage for user " + userId);
@@ -14220,7 +14353,7 @@
                         unactionedPackages.add(packageName);
                         continue;
                     }
-                    if (!canSuspendPackageForUserLocked(packageName, userId)) {
+                    if (suspended && !canSuspendPackageForUserLocked(packageName, userId)) {
                         unactionedPackages.add(packageName);
                         continue;
                     }
@@ -14277,25 +14410,22 @@
         } else {
             action = Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
         }
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    final IActivityManager am = ActivityManager.getService();
-                    if (am == null) {
-                        Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
-                                + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
-                        return;
-                    }
-                    final int[] targetUserIds = new int[] {userId};
-                    for (String packageName : affectedPackages) {
-                        doSendBroadcast(am, action, null, intentExtras,
-                                Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
-                                targetUserIds, false);
-                    }
-                } catch (RemoteException ex) {
-                    // Shouldn't happen as AMS is in the same process.
+        mHandler.post(() -> {
+            try {
+                final IActivityManager am = ActivityManager.getService();
+                if (am == null) {
+                    Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
+                            + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
+                    return;
                 }
+                final int[] targetUserIds = new int[] {userId};
+                for (String packageName : affectedPackages) {
+                    doSendBroadcast(am, action, null, intentExtras,
+                            Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
+                            targetUserIds, false);
+                }
+            } catch (RemoteException ex) {
+                // Shouldn't happen as AMS is in the same process.
             }
         });
     }
@@ -14375,44 +14505,50 @@
     @GuardedBy("mPackages")
     private boolean canSuspendPackageForUserLocked(String packageName, int userId) {
         if (isPackageDeviceAdmin(packageName, userId)) {
-            Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+            Slog.w(TAG, "Cannot suspend package \"" + packageName
                     + "\": has an active device admin");
             return false;
         }
 
         String activeLauncherPackageName = getActiveLauncherPackageName(userId);
         if (packageName.equals(activeLauncherPackageName)) {
-            Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+            Slog.w(TAG, "Cannot suspend package \"" + packageName
                     + "\": contains the active launcher");
             return false;
         }
 
         if (packageName.equals(mRequiredInstallerPackage)) {
-            Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+            Slog.w(TAG, "Cannot suspend package \"" + packageName
                     + "\": required for package installation");
             return false;
         }
 
         if (packageName.equals(mRequiredUninstallerPackage)) {
-            Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+            Slog.w(TAG, "Cannot suspend package \"" + packageName
                     + "\": required for package uninstallation");
             return false;
         }
 
         if (packageName.equals(mRequiredVerifierPackage)) {
-            Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+            Slog.w(TAG, "Cannot suspend package \"" + packageName
                     + "\": required for package verification");
             return false;
         }
 
         if (packageName.equals(getDefaultDialerPackageName(userId))) {
-            Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+            Slog.w(TAG, "Cannot suspend package \"" + packageName
                     + "\": is the default dialer");
             return false;
         }
 
+        if (packageName.equals(mRequiredPermissionControllerPackage)) {
+            Slog.w(TAG, "Cannot suspend package \"" + packageName
+                    + "\": required for permissions management");
+            return false;
+        }
+
         if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
-            Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+            Slog.w(TAG, "Cannot suspend package \"" + packageName
                     + "\": protected package");
             return false;
         }
@@ -14543,7 +14679,7 @@
         }
 
         final int N = pkgInfo.verifiers.length;
-        final List<ComponentName> sufficientVerifiers = new ArrayList<ComponentName>(N + 1);
+        final List<ComponentName> sufficientVerifiers = new ArrayList<>(N + 1);
         for (int i = 0; i < N; i++) {
             final VerifierInfo verifierInfo = pkgInfo.verifiers[i];
 
@@ -15053,36 +15189,33 @@
         // are coherent.  In the non-restore case, the app has already completed install
         // and been launched through some other means, so it is not in a problematic
         // state for observers to see the FIRST_LAUNCH signal.
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                for (int i = 0; i < mRunningInstalls.size(); i++) {
-                    final PostInstallData data = mRunningInstalls.valueAt(i);
-                    if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
-                        continue;
-                    }
-                    if (packageName.equals(data.res.pkg.applicationInfo.packageName)) {
-                        // right package; but is it for the right user?
-                        for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
-                            if (userId == data.res.newUsers[uIndex]) {
-                                if (DEBUG_BACKUP) {
-                                    Slog.i(TAG, "Package " + packageName
-                                            + " being restored so deferring FIRST_LAUNCH");
-                                }
-                                return;
+        mHandler.post(() -> {
+            for (int i = 0; i < mRunningInstalls.size(); i++) {
+                final PostInstallData data = mRunningInstalls.valueAt(i);
+                if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+                    continue;
+                }
+                if (packageName.equals(data.res.pkg.applicationInfo.packageName)) {
+                    // right package; but is it for the right user?
+                    for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
+                        if (userId == data.res.newUsers[uIndex]) {
+                            if (DEBUG_BACKUP) {
+                                Slog.i(TAG, "Package " + packageName
+                                        + " being restored so deferring FIRST_LAUNCH");
                             }
+                            return;
                         }
                     }
                 }
-                // didn't find it, so not being restored
-                if (DEBUG_BACKUP) {
-                    Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH");
-                }
-                final boolean isInstantApp = isInstantApp(packageName, userId);
-                final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
-                final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
-                sendFirstLaunchBroadcast(packageName, installerPackage, userIds, instantUserIds);
             }
+            // didn't find it, so not being restored
+            if (DEBUG_BACKUP) {
+                Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH");
+            }
+            final boolean isInstantApp = isInstantApp(packageName, userId);
+            final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
+            final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
+            sendFirstLaunchBroadcast(packageName, installerPackage, userIds, instantUserIds);
         });
     }
 
@@ -15764,7 +15897,7 @@
          * Rename package into final resting place. All paths on the given
          * scanned package should be updated to reflect the rename.
          */
-        abstract boolean doRename(int status, PackageParser.Package pkg, String oldCodePath);
+        abstract boolean doRename(int status, PackageParser.Package pkg);
         abstract int doPostInstall(int status, int uid);
 
         /** @see PackageSettingBase#codePathString */
@@ -15911,8 +16044,7 @@
                 }
             };
 
-            int ret = PackageManager.INSTALL_SUCCEEDED;
-            ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
+            int ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
             if (ret != PackageManager.INSTALL_SUCCEEDED) {
                 Slog.e(TAG, "Failed to copy package");
                 return ret;
@@ -15941,7 +16073,7 @@
             return status;
         }
 
-        boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
+        boolean doRename(int status, PackageParser.Package pkg) {
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 cleanUp();
                 return false;
@@ -16057,17 +16189,6 @@
     }
 
     /**
-     * Extract the StorageManagerService "container ID" from the full code path of an
-     * .apk.
-     */
-    static String cidFromCodePath(String fullCodePath) {
-        int eidx = fullCodePath.lastIndexOf("/");
-        String subStr1 = fullCodePath.substring(0, eidx);
-        int sidx = subStr1.lastIndexOf("/");
-        return subStr1.substring(sidx+1, eidx);
-    }
-
-    /**
      * Logic to handle movement of existing installed applications.
      */
     class MoveInstallArgs extends InstallArgs {
@@ -16111,7 +16232,7 @@
             return status;
         }
 
-        boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
+        boolean doRename(int status, PackageParser.Package pkg) {
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 cleanUp(move.toUuid);
                 return false;
@@ -16178,51 +16299,6 @@
         }
     }
 
-    static String getAsecPackageName(String packageCid) {
-        int idx = packageCid.lastIndexOf("-");
-        if (idx == -1) {
-            return packageCid;
-        }
-        return packageCid.substring(0, idx);
-    }
-
-    // Utility method used to create code paths based on package name and available index.
-    private static String getNextCodePath(String oldCodePath, String prefix, String suffix) {
-        String idxStr = "";
-        int idx = 1;
-        // Fall back to default value of idx=1 if prefix is not
-        // part of oldCodePath
-        if (oldCodePath != null) {
-            String subStr = oldCodePath;
-            // Drop the suffix right away
-            if (suffix != null && subStr.endsWith(suffix)) {
-                subStr = subStr.substring(0, subStr.length() - suffix.length());
-            }
-            // If oldCodePath already contains prefix find out the
-            // ending index to either increment or decrement.
-            int sidx = subStr.lastIndexOf(prefix);
-            if (sidx != -1) {
-                subStr = subStr.substring(sidx + prefix.length());
-                if (subStr != null) {
-                    if (subStr.startsWith(INSTALL_PACKAGE_SUFFIX)) {
-                        subStr = subStr.substring(INSTALL_PACKAGE_SUFFIX.length());
-                    }
-                    try {
-                        idx = Integer.parseInt(subStr);
-                        if (idx <= 1) {
-                            idx++;
-                        } else {
-                            idx--;
-                        }
-                    } catch(NumberFormatException e) {
-                    }
-                }
-            }
-        }
-        idxStr = INSTALL_PACKAGE_SUFFIX + Integer.toString(idx);
-        return prefix + idxStr;
-    }
-
     private File getNextCodePath(File targetDir, String packageName) {
         File result;
         SecureRandom random = new SecureRandom();
@@ -16350,14 +16426,18 @@
         }
 
         try {
-            PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags,
+            final PackageParser.Package newPackage;
+            List<ScanResult> scanResults = scanPackageTracedLI(pkg, parseFlags, scanFlags,
                     System.currentTimeMillis(), user);
-
+            commitSuccessfulScanResults(scanResults);
+            // TODO(b/109941548): Child packages may return >1 result with the first being the base;
+            // we need to treat child packages as an atomic install and remove this hack
+            final ScanResult basePackageScanResult = scanResults.get(0);
+            newPackage = basePackageScanResult.pkgSetting.pkg;
             updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);
 
             if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                 prepareAppDataAfterInstallLIF(newPackage);
-
             } else {
                 // Remove package from internal structures, but keep around any
                 // data that might have already existed
@@ -16365,6 +16445,9 @@
                         PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
             }
         } catch (PackageManagerException e) {
+            destroyAppDataLIF(pkg, UserHandle.USER_ALL,
+                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+            destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
             res.setError("Package couldn't be installed in " + pkg.codePath, e);
         }
 
@@ -16433,7 +16516,7 @@
 
             // don't allow a system upgrade unless the upgrade hash matches
             if (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) {
-                byte[] digestBytes = null;
+                final byte[] digestBytes;
                 try {
                     final MessageDigest digest = MessageDigest.getInstance("SHA-512");
                     updateDigest(digest, new File(pkg.baseCodePath));
@@ -16558,18 +16641,10 @@
         boolean sysPkg = (isSystemApp(oldPackage));
         if (sysPkg) {
             // Set the system/privileged/oem/vendor/product flags as needed
-            final boolean privileged =
-                    (oldPackage.applicationInfo.privateFlags
-                            & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
-            final boolean oem =
-                    (oldPackage.applicationInfo.privateFlags
-                            & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
-            final boolean vendor =
-                    (oldPackage.applicationInfo.privateFlags
-                            & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
-            final boolean product =
-                    (oldPackage.applicationInfo.privateFlags
-                            & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+            final boolean privileged = isPrivilegedApp(oldPackage);
+            final boolean oem = isOemApp(oldPackage);
+            final boolean vendor = isVendorApp(oldPackage);
+            final boolean product = isProductApp(oldPackage);
             final @ParseFlags int systemParseFlags = parseFlags;
             final @ScanFlags int systemScanFlags = scanFlags
                     | SCAN_AS_SYSTEM
@@ -16620,7 +16695,7 @@
                     Slog.i(TAG, "upgrading pkg " + deletedPackage + " is ASEC-hosted -> UNAVAILABLE");
                 }
                 final int[] uidArray = new int[] { deletedPackage.applicationInfo.uid };
-                final ArrayList<String> pkgList = new ArrayList<String>(1);
+                final ArrayList<String> pkgList = new ArrayList<>(1);
                 pkgList.add(deletedPackage.applicationInfo.packageName);
                 sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
             }
@@ -16629,8 +16704,10 @@
                     | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
 
             try {
-                final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,
+                final List<ScanResult> scanResults = scanPackageTracedLI(pkg, parseFlags,
                         scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
+                commitSuccessfulScanResults(scanResults);
+                final PackageParser.Package newPackage = scanResults.get(0).pkgSetting.pkg;
                 updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
                         installReason);
 
@@ -16768,8 +16845,11 @@
 
         PackageParser.Package newPackage = null;
         try {
+            final List<ScanResult> scanResults =
+                    scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user);
             // Add the package to the internal data structures
-            newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user);
+            commitSuccessfulScanResults(scanResults);
+            newPackage = scanResults.get(0).pkgSetting.pkg;
 
             // Set the update and install times
             PackageSetting deletedPkgSetting = (PackageSetting) deletedPackage.mExtras;
@@ -16826,7 +16906,9 @@
             }
             // Add back the old system package
             try {
-                scanPackageTracedLI(deletedPackage, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
+                final List<ScanResult> scanResults = scanPackageTracedLI(
+                        deletedPackage, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
+                commitSuccessfulScanResults(scanResults);
             } catch (PackageManagerException e) {
                 Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
             }
@@ -16902,6 +16984,7 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private void enableSystemPackageLPw(PackageParser.Package pkg) {
         // Enable the parent package
         mSettings.enableSystemPackageLPw(pkg.packageName);
@@ -16913,6 +16996,7 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private boolean disableSystemPackageLPw(PackageParser.Package oldPkg,
             PackageParser.Package newPkg) {
         // Disable the parent package (parent always replaced)
@@ -16927,6 +17011,7 @@
         return disabled;
     }
 
+    @GuardedBy("mPackages")
     private void setInstallerPackageNameLPw(PackageParser.Package pkg,
             String installerPackageName) {
         // Enable the parent package
@@ -17045,17 +17130,92 @@
 
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
     }
+    private static class InstallRequest {
+        final InstallArgs args;
+        final PackageInstalledInfo res;
 
+        private InstallRequest(InstallArgs args, PackageInstalledInfo res) {
+            this.args = args;
+            this.res = res;
+        }
+    }
+
+    @GuardedBy({"mInstallLock", "mPackages"})
     private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");
-            installPackageLI(args, res);
+            installPackagesLI(Collections.singletonList(new InstallRequest(args, res)));
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
     }
 
-    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
+    private static class CommitRequest {
+        final Map<String, ReconciledPackage> reconciledPackages;
+
+        private CommitRequest(Map<String, ReconciledPackage> reconciledPackages) {
+            this.reconciledPackages = reconciledPackages;
+        }
+    }
+    private static class ReconcileRequest {
+        final Map<String, ScanResult> scannedPackages;
+
+        private ReconcileRequest(Map<String, ScanResult> scannedPackages) {
+            this.scannedPackages = scannedPackages;
+        }
+    }
+    private static class ReconcileFailure extends PackageManagerException {
+        public ReconcileFailure(String message) {
+            super("Invalid reconcile request: " + message);
+        }
+    };
+
+    /**
+     * A container of all data needed to commit a package to in-memory data structures and to disk.
+     * Ideally most of the data contained in this class will move into a PackageSetting it contains.
+     */
+    private static class ReconciledPackage {}
+
+    @GuardedBy("mPackages")
+    private static Map<String, ReconciledPackage> reconcilePackagesLocked(
+            final ReconcileRequest request) throws ReconcileFailure {
+        return Collections.emptyMap();
+    }
+
+    @GuardedBy("mPackages")
+    private boolean commitPackagesLocked(final CommitRequest request) {
+        return true;
+    }
+
+    @GuardedBy({"mInstallLock", "mPackages", "PackageManagerService.mPackages"})
+    private void installPackagesLI(List<InstallRequest> requests) {
+        Map<String, ScanResult> scans = new ArrayMap<>(requests.size());
+        for (InstallRequest request : requests) {
+            // TODO(b/109941548): remove this once we've pulled everything from it and into scan,
+            // reconcile or commit.
+            preparePackageLI(request.args, request.res);
+
+            // TODO(b/109941548): scan package and get result
+        }
+        ReconcileRequest reconcileRequest = new ReconcileRequest(scans);
+        Map<String, ReconciledPackage> reconciledPackages;
+        try {
+            reconciledPackages = reconcilePackagesLocked(reconcileRequest);
+        } catch (ReconcileFailure e) {
+            // TODO(b/109941548): set install args error
+            return;
+        }
+        CommitRequest request = new CommitRequest(reconciledPackages);
+        if (!commitPackagesLocked(request)) {
+            // TODO(b/109941548): set install args error
+            return;
+        }
+        // TODO(b/109941548) post-commit actions (dex-opt, etc.)
+    }
+
+    @Deprecated
+    @GuardedBy("mInstallLock")
+    private void preparePackageLI(InstallArgs args, PackageInstalledInfo res) {
         final int installFlags = args.installFlags;
         final String installerPackageName = args.installerPackageName;
         final String volumeUuid = args.volumeUuid;
@@ -17133,13 +17293,6 @@
                         "Instant app package must target at least O");
                 return;
             }
-            if (pkg.applicationInfo.targetSandboxVersion != 2) {
-                Slog.w(TAG, "Instant app package " + pkg.packageName
-                        + " does not target targetSandboxVersion 2");
-                res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
-                        "Instant app package must use targetSandboxVersion 2");
-                return;
-            }
             if (pkg.mSharedUserId != null) {
                 Slog.w(TAG, "Instant app package " + pkg.packageName
                         + " may not declare sharedUserId.");
@@ -17227,7 +17380,6 @@
 
         // Get rid of all references to package scan path via parser.
         pp = null;
-        String oldCodePath = null;
         boolean systemApp = false;
         synchronized (mPackages) {
             // Check if installing already existing package
@@ -17339,7 +17491,6 @@
                     }
                 }
 
-                oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
                 if (ps.pkg != null && ps.pkg.applicationInfo != null) {
                     systemApp = (ps.pkg.applicationInfo.flags &
                             ApplicationInfo.FLAG_SYSTEM) != 0;
@@ -17489,7 +17640,7 @@
             }
         }
 
-        if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
+        if (!args.doRename(res.returnCode, pkg)) {
             res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
             return;
         }
@@ -17561,7 +17712,7 @@
                 replacePackageLIF(pkg, parseFlags, scanFlags, args.user,
                         installerPackageName, res, args.installReason);
             } else {
-                installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
+                installNewPackageLIF(pkg, parseFlags, scanFlags,
                         args.user, installerPackageName, volumeUuid, res, args.installReason);
             }
         }
@@ -17755,6 +17906,7 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private boolean needsNetworkVerificationLPr(ActivityIntentInfo filter) {
         final ComponentName cn  = filter.activity.getComponentName();
         final String packageName = cn.getPackageName();
@@ -17846,11 +17998,8 @@
     }
 
     private void deleteTempPackageFiles() {
-        final FilenameFilter filter = new FilenameFilter() {
-            public boolean accept(File dir, String name) {
-                return name.startsWith("vmdl") && name.endsWith(".tmp");
-            }
-        };
+        final FilenameFilter filter =
+                (dir, name) -> name.startsWith("vmdl") && name.endsWith(".tmp");
         for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) {
             file.delete();
         }
@@ -17992,6 +18141,7 @@
         return pkg.packageName;
     }
 
+    @GuardedBy("mPackages")
     private String resolveInternalPackageNameLPr(String packageName, long versionCode) {
         // Handle renamed packages
         String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
@@ -18187,8 +18337,8 @@
             return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
         }
 
-        PackageSetting uninstalledPs = null;
-        PackageParser.Package pkg = null;
+        PackageSetting uninstalledPs;
+        PackageParser.Package pkg;
 
         // for the uninstall-updates case and restricted profiles, remember the per-
         // user handle installed state
@@ -18498,13 +18648,10 @@
                             if (userIdToKill == UserHandle.USER_ALL
                                     || userIdToKill >= UserHandle.USER_SYSTEM) {
                                 // If gids changed for this user, kill all affected packages.
-                                mHandler.post(new Runnable() {
-                                    @Override
-                                    public void run() {
-                                        // This has to happen with no lock held.
-                                        killApplication(deletedPs.name, deletedPs.appId,
-                                                KILL_APP_REASON_GIDS_CHANGED);
-                                    }
+                                mHandler.post(() -> {
+                                    // This has to happen with no lock held.
+                                    killApplication(deletedPs.name, deletedPs.appId,
+                                            KILL_APP_REASON_GIDS_CHANGED);
                                 });
                                 break;
                             }
@@ -19061,6 +19208,7 @@
         return ret;
     }
 
+    @GuardedBy("mPackages")
     private void markPackageUninstalledForUserLPw(PackageSetting ps, UserHandle user) {
         final int[] userIds = (user == null || user.getIdentifier() == UserHandle.USER_ALL)
                 ? sUserManager.getUserIds() : new int[] {user.getIdentifier()};
@@ -19335,6 +19483,7 @@
      *
      * @param userId The device user for which to do a reset.
      */
+    @GuardedBy("mPackages")
     private void resetUserChangesToRuntimePermissionsAndFlagsLPw(int userId) {
         final int packageCount = mPackages.size();
         for (int i = 0; i < packageCount; i++) {
@@ -19354,6 +19503,7 @@
      * @param ps The package for which to reset.
      * @param userId The device user for which to do a reset.
      */
+    @GuardedBy("mPackages")
     private void resetUserChangesToRuntimePermissionsAndFlagsLPw(
             final PackageSetting ps, final int userId) {
         if (ps.pkg == null) {
@@ -19408,8 +19558,7 @@
             // If permission review is enabled and this is a legacy app, mark the
             // permission as requiring a review as this is the initial state.
             int flags = 0;
-            if (mSettings.mPermissions.mPermissionReviewRequired
-                    && ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+            if (ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) {
                 flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
             }
             if (permissionsState.updatePermissionFlags(bp, userId, userSettableMask, flags)) {
@@ -19446,12 +19595,8 @@
                     case PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
                         writeRuntimePermissions = true;
                         final int appId = ps.appId;
-                        mHandler.post(new Runnable() {
-                            @Override
-                            public void run() {
-                                killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
-                            }
-                        });
+                        mHandler.post(
+                                () -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
                     } break;
                 }
             }
@@ -19529,33 +19674,31 @@
         }
 
         // Queue up an async operation since the package deletion may take a little while.
-        mHandler.post(new Runnable() {
-            public void run() {
-                final PackageSetting ps = pkg == null ? null : (PackageSetting) pkg.mExtras;
-                boolean doClearData = true;
-                if (ps != null) {
-                    final boolean targetIsInstantApp =
-                            ps.getInstantApp(UserHandle.getUserId(callingUid));
-                    doClearData = !targetIsInstantApp
-                            || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
+        mHandler.post(() -> {
+            final PackageSetting ps = pkg == null ? null : (PackageSetting) pkg.mExtras;
+            boolean doClearData = true;
+            if (ps != null) {
+                final boolean targetIsInstantApp =
+                        ps.getInstantApp(UserHandle.getUserId(callingUid));
+                doClearData = !targetIsInstantApp
+                        || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
+            }
+            if (doClearData) {
+                synchronized (mInstallLock) {
+                    final int flags = StorageManager.FLAG_STORAGE_DE
+                            | StorageManager.FLAG_STORAGE_CE;
+                    // We're only clearing cache files, so we don't care if the
+                    // app is unfrozen and still able to run
+                    clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
+                    clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
                 }
-                if (doClearData) {
-                    synchronized (mInstallLock) {
-                        final int flags = StorageManager.FLAG_STORAGE_DE
-                                | StorageManager.FLAG_STORAGE_CE;
-                        // We're only clearing cache files, so we don't care if the
-                        // app is unfrozen and still able to run
-                        clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
-                        clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-                    }
-                    clearExternalStorageDataSync(packageName, userId, false);
-                }
-                if (observer != null) {
-                    try {
-                        observer.onRemoveCompleted(packageName, true);
-                    } catch (RemoteException e) {
-                        Log.i(TAG, "Observer no longer exists.");
-                    }
+                clearExternalStorageDataSync(packageName, userId, false);
+            }
+            if (observer != null) {
+                try {
+                    observer.onRemoveCompleted(packageName, true);
+                } catch (RemoteException e) {
+                    Log.i(TAG, "Observer no longer exists.");
                 }
             }
         });
@@ -19568,6 +19711,7 @@
                 "Shame on you for calling the hidden API getPackageSizeInfo(). Shame!");
     }
 
+    @GuardedBy("mInstallLock")
     private boolean getPackageSizeInfoLI(String packageName, int userId, PackageStats stats) {
         final PackageSetting ps;
         synchronized (mPackages) {
@@ -19602,6 +19746,7 @@
         return true;
     }
 
+    @GuardedBy("mPackages")
     private int getUidTargetSdkVersionLockedLPr(int uid) {
         Object obj = mSettings.getUserIdLPr(uid);
         if (obj instanceof SharedUserSetting) {
@@ -19625,6 +19770,7 @@
         return Build.VERSION_CODES.CUR_DEVELOPMENT;
     }
 
+    @GuardedBy("mPackages")
     private int getPackageTargetSdkVersionLockedLPr(String packageName) {
         final PackageParser.Package p = mPackages.get(packageName);
         if (p != null) {
@@ -19820,6 +19966,7 @@
     }
 
     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
+    @GuardedBy("mPackages")
     boolean clearPackagePreferredActivitiesLPw(String packageName, int userId) {
         ArrayList<PreferredActivity> removed = null;
         boolean changed = false;
@@ -19838,7 +19985,7 @@
                         (pa.mPref.mComponent.getPackageName().equals(packageName)
                                 && pa.mPref.mAlways)) {
                     if (removed == null) {
-                        removed = new ArrayList<PreferredActivity>();
+                        removed = new ArrayList<>();
                     }
                     removed.add(pa);
                 }
@@ -19858,6 +20005,7 @@
     }
 
     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
+    @GuardedBy("mPackages")
     private void clearIntentFilterVerificationsLPw(int userId) {
         final int packageCount = mPackages.size();
         for (int i = 0; i < packageCount; i++) {
@@ -19867,6 +20015,7 @@
     }
 
     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
+    @GuardedBy("mPackages")
     void clearIntentFilterVerificationsLPw(String packageName, int userId) {
         if (userId == UserHandle.USER_ALL) {
             if (mSettings.removeIntentFilterVerificationLPw(packageName,
@@ -20002,7 +20151,7 @@
                     // Mark entry for removal only if it matches the package name.
                     if (ppa.mComponent.getPackageName().equals(packageName)) {
                         if (removed == null) {
-                            removed = new ArrayList<PersistentPreferredActivity>();
+                            removed = new ArrayList<>();
                         }
                         removed.add(ppa);
                     }
@@ -20041,7 +20190,6 @@
             }
             return;
         }
-Slog.v(TAG, ":: restoreFromXml() : got to tag " + parser.getName());
         // this is supposed to be TAG_PREFERRED_BACKUP
         if (!expectedStartTag.equals(parser.getName())) {
             if (DEBUG_BACKUP) {
@@ -20052,12 +20200,11 @@
 
         // skip interfering stuff, then we're aligned with the backing implementation
         while ((type = parser.next()) == XmlPullParser.TEXT) { }
-Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
         functor.apply(parser, userId);
     }
 
     private interface BlobXmlRestorer {
-        public void apply(XmlPullParser parser, int userId) throws IOException, XmlPullParserException;
+        void apply(XmlPullParser parser, int userId) throws IOException, XmlPullParserException;
     }
 
     /**
@@ -20105,15 +20252,11 @@
             final XmlPullParser parser = Xml.newPullParser();
             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
             restoreFromXml(parser, userId, TAG_PREFERRED_BACKUP,
-                    new BlobXmlRestorer() {
-                        @Override
-                        public void apply(XmlPullParser parser, int userId)
-                                throws XmlPullParserException, IOException {
-                            synchronized (mPackages) {
-                                mSettings.readPreferredActivitiesLPw(parser, userId);
-                            }
+                    (readParser, readUserId) -> {
+                        synchronized (mPackages) {
+                            mSettings.readPreferredActivitiesLPw(readParser, readUserId);
                         }
-                    } );
+                    });
         } catch (Exception e) {
             if (DEBUG_BACKUP) {
                 Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
@@ -20166,15 +20309,11 @@
             final XmlPullParser parser = Xml.newPullParser();
             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
             restoreFromXml(parser, userId, TAG_DEFAULT_APPS,
-                    new BlobXmlRestorer() {
-                        @Override
-                        public void apply(XmlPullParser parser, int userId)
-                                throws XmlPullParserException, IOException {
-                            synchronized (mPackages) {
-                                mSettings.readDefaultAppsLPw(parser, userId);
-                            }
+                    (parser1, userId1) -> {
+                        synchronized (mPackages) {
+                            mSettings.readDefaultAppsLPw(parser1, userId1);
                         }
-                    } );
+                    });
         } catch (Exception e) {
             if (DEBUG_BACKUP) {
                 Slog.e(TAG, "Exception restoring default apps: " + e.getMessage());
@@ -20222,16 +20361,12 @@
             final XmlPullParser parser = Xml.newPullParser();
             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
             restoreFromXml(parser, userId, TAG_INTENT_FILTER_VERIFICATION,
-                    new BlobXmlRestorer() {
-                        @Override
-                        public void apply(XmlPullParser parser, int userId)
-                                throws XmlPullParserException, IOException {
-                            synchronized (mPackages) {
-                                mSettings.readAllDomainVerificationsLPr(parser, userId);
-                                mSettings.writeLPr();
-                            }
+                    (parser1, userId1) -> {
+                        synchronized (mPackages) {
+                            mSettings.readAllDomainVerificationsLPr(parser1, userId1);
+                            mSettings.writeLPr();
                         }
-                    } );
+                    });
         } catch (Exception e) {
             if (DEBUG_BACKUP) {
                 Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
@@ -20279,15 +20414,11 @@
             final XmlPullParser parser = Xml.newPullParser();
             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
             restoreFromXml(parser, userId, TAG_PERMISSION_BACKUP,
-                    new BlobXmlRestorer() {
-                        @Override
-                        public void apply(XmlPullParser parser, int userId)
-                                throws XmlPullParserException, IOException {
-                            synchronized (mPackages) {
-                                processRestoredPermissionGrantsLPr(parser, userId);
-                            }
+                    (parser1, userId1) -> {
+                        synchronized (mPackages) {
+                            processRestoredPermissionGrantsLPr(parser1, userId1);
                         }
-                    } );
+                    });
         } catch (Exception e) {
             if (DEBUG_BACKUP) {
                 Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
@@ -20295,6 +20426,7 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private void serializeRuntimePermissionGrantsLPr(XmlSerializer serializer, final int userId)
             throws IOException {
         serializer.startTag(null, TAG_ALL_GRANTS);
@@ -20354,6 +20486,7 @@
         serializer.endTag(null, TAG_ALL_GRANTS);
     }
 
+    @GuardedBy("mPackages")
     private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId)
             throws XmlPullParserException, IOException {
         String pkgName = null;
@@ -20475,7 +20608,7 @@
             CrossProfileIntentResolver resolver =
                     mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
             ArraySet<CrossProfileIntentFilter> set =
-                    new ArraySet<CrossProfileIntentFilter>(resolver.filterSet());
+                    new ArraySet<>(resolver.filterSet());
             for (CrossProfileIntentFilter filter : set) {
                 if (filter.getOwnerPackage().equals(ownerPackage)) {
                     resolver.removeFilter(filter);
@@ -20580,9 +20713,7 @@
 
         allHomeCandidates.clear();
         if (list != null) {
-            for (ResolveInfo ri : list) {
-                allHomeCandidates.add(ri);
-            }
+            allHomeCandidates.addAll(list);
         }
         return (preferred == null || preferred.activityInfo == null)
                 ? null
@@ -20712,7 +20843,6 @@
         boolean isApp = (className == null);
         final boolean isCallerInstantApp = (getInstantAppPackageName(callingUid) != null);
         String componentName = isApp ? packageName : className;
-        int packageUid = -1;
         ArrayList<String> components;
 
         // reader
@@ -20952,7 +21082,7 @@
             components = mPendingBroadcasts.get(userId, packageName);
             final boolean newPackage = components == null;
             if (newPackage) {
-                components = new ArrayList<String>();
+                components = new ArrayList<>();
             }
             if (!components.contains(componentName)) {
                 components.add(componentName);
@@ -20976,7 +21106,7 @@
         long callingId = Binder.clearCallingIdentity();
         try {
             if (sendNow) {
-                packageUid = UserHandle.getUid(userId, pkgSetting.appId);
+                int packageUid = UserHandle.getUid(userId, pkgSetting.appId);
                 sendPackageChangedBroadcast(packageName,
                         (flags&PackageManager.DONT_KILL_APP) != 0, components, packageUid);
             }
@@ -21090,7 +21220,8 @@
     }
 
     @Override
-    public int getComponentEnabledSetting(ComponentName component, int userId) {
+    public int getComponentEnabledSetting(@NonNull ComponentName component, int userId) {
+        if (component == null) return COMPONENT_ENABLED_STATE_DEFAULT;
         if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
         int callingUid = Binder.getCallingUid();
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
@@ -21139,6 +21270,8 @@
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this,
                 mContext.getContentResolver(), UserHandle.USER_SYSTEM);
 
+        disableSkuSpecificApps();
+
         // Read the compatibilty setting when the system is ready.
         boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
                 mContext.getContentResolver(),
@@ -21159,7 +21292,7 @@
             // possible for the user flow to never be able to return to that
             // situation so here we do a sanity check to make sure we haven't
             // left any junk around.
-            ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
+            ArrayList<PreferredActivity> removed = new ArrayList<>();
             for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
                 PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
                 removed.clear();
@@ -21287,16 +21420,16 @@
     }
 
     static String arrayToString(int[] array) {
-        StringBuffer buf = new StringBuffer(128);
-        buf.append('[');
+        StringBuilder stringBuilder = new StringBuilder(128);
+        stringBuilder.append('[');
         if (array != null) {
             for (int i=0; i<array.length; i++) {
-                if (i > 0) buf.append(", ");
-                buf.append(array[i]);
+                if (i > 0) stringBuilder.append(", ");
+                stringBuilder.append(array[i]);
             }
         }
-        buf.append(']');
-        return buf.toString();
+        stringBuilder.append(']');
+        return stringBuilder.toString();
     }
 
     @Override
@@ -21330,10 +21463,11 @@
                 // Right now we only know how to print all.
             } else if ("-h".equals(opt)) {
                 pw.println("Package manager dump options:");
-                pw.println("  [-h] [-f] [--checkin] [cmd] ...");
+                pw.println("  [-h] [-f] [--checkin] [--all-components] [cmd] ...");
                 pw.println("    --checkin: dump for a checkin");
                 pw.println("    -f: print details of intent filters");
                 pw.println("    -h: print this help");
+                pw.println("    --all-components: include all component names in package dump");
                 pw.println("  cmd may be one of:");
                 pw.println("    l[ibraries]: list known shared libraries");
                 pw.println("    f[eatures]: list device features");
@@ -21361,6 +21495,8 @@
                 return;
             } else if ("--checkin".equals(opt)) {
                 checkin = true;
+            } else if ("--all-components".equals(opt)) {
+                dumpState.setOptionEnabled(DumpState.OPTION_DUMP_ALL_COMPONENTS);
             } else if ("-f".equals(opt)) {
                 dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
             } else if ("--proto".equals(opt)) {
@@ -21926,6 +22062,27 @@
         }
     }
 
+    //TODO: b/111402650
+    private void disableSkuSpecificApps() {
+        String apkList[] = mContext.getResources().getStringArray(
+                R.array.config_disableApksUnlessMatchedSku_apk_list);
+        String skuArray[] = mContext.getResources().getStringArray(
+                R.array.config_disableApkUnlessMatchedSku_skus_list);
+        if (ArrayUtils.isEmpty(apkList)) {
+           return;
+        }
+        String sku = SystemProperties.get("ro.boot.hardware.sku");
+        if (!TextUtils.isEmpty(sku) && ArrayUtils.contains(skuArray, sku)) {
+            return;
+        }
+        for (String packageName : apkList) {
+            setSystemAppHiddenUntilInstalled(packageName, true);
+            for (UserInfo user : sUserManager.getUsers(false)) {
+                setSystemAppInstallState(packageName, false, user.id);
+            }
+        }
+    }
+
     private void dumpProto(FileDescriptor fd) {
         final ProtoOutputStream proto = new ProtoOutputStream(fd);
 
@@ -21999,12 +22156,13 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private void dumpDexoptStateLPr(PrintWriter pw, String packageName) {
         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
         ipw.println();
         ipw.println("Dexopt state:");
         ipw.increaseIndent();
-        Collection<PackageParser.Package> packages = null;
+        Collection<PackageParser.Package> packages;
         if (packageName != null) {
             PackageParser.Package targetPackage = mPackages.get(packageName);
             if (targetPackage != null) {
@@ -22026,12 +22184,13 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) {
         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
         ipw.println();
         ipw.println("Compiler stats:");
         ipw.increaseIndent();
-        Collection<PackageParser.Package> packages = null;
+        Collection<PackageParser.Package> packages;
         if (packageName != null) {
             PackageParser.Package targetPackage = mPackages.get(packageName);
             if (targetPackage != null) {
@@ -22066,9 +22225,7 @@
         ArraySet<String> result = new ArraySet<>();
         if (iviList.size() > 0) {
             for (IntentFilterVerificationInfo ivi : iviList) {
-                for (String host : ivi.getDomains()) {
-                    result.add(host);
-                }
+                result.addAll(ivi.getDomains());
             }
         }
         if (filters != null && filters.size() > 0) {
@@ -22160,12 +22317,7 @@
     }
 
     private void loadPrivatePackages(final VolumeInfo vol) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                loadPrivatePackagesInner(vol);
-            }
-        });
+        mHandler.post(() -> loadPrivatePackagesInner(vol));
     }
 
     private void loadPrivatePackagesInner(VolumeInfo vol) {
@@ -22256,12 +22408,7 @@
     }
 
     private void unloadPrivatePackages(final VolumeInfo vol) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                unloadPrivatePackagesInner(vol);
-            }
-        });
+        mHandler.post(() -> unloadPrivatePackagesInner(vol));
     }
 
     private void unloadPrivatePackagesInner(VolumeInfo vol) {
@@ -22316,23 +22463,6 @@
         }
     }
 
-    private void assertPackageKnown(String volumeUuid, String packageName)
-            throws PackageManagerException {
-        synchronized (mPackages) {
-            // Normalize package name to handle renamed packages
-            packageName = normalizePackageNameLPr(packageName);
-
-            final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (ps == null) {
-                throw new PackageManagerException("Package " + packageName + " is unknown");
-            } else if (!TextUtils.equals(volumeUuid, ps.volumeUuid)) {
-                throw new PackageManagerException(
-                        "Package " + packageName + " found on unknown volume " + volumeUuid
-                                + "; expected volume " + ps.volumeUuid);
-            }
-        }
-    }
-
     private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
             throws PackageManagerException {
         synchronized (mPackages) {
@@ -22432,6 +22562,7 @@
         }
     }
 
+    @GuardedBy("mInstallLock")
     private void reconcileAppsDataLI(String volumeUuid, int userId, int flags,
             boolean migrateAppData) {
         reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppData, false /* onlyCoreApps */);
@@ -22445,8 +22576,9 @@
      * <p>
      * Verifies that directories exist and that ownership and labeling is
      * correct for all installed apps.
-     * @returns list of skipped non-core packages (if {@code onlyCoreApps} is true)
+     * @return list of skipped non-core packages (if {@code onlyCoreApps} is true)
      */
+    @GuardedBy("mInstallLock")
     private List<String> reconcileAppsDataLI(String volumeUuid, int userId, int flags,
             boolean migrateAppData, boolean onlyCoreApps) {
         Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x"
@@ -22815,10 +22947,7 @@
         @Override
         protected void finalize() throws Throwable {
             try {
-                if (mCloseGuard != null) {
-                    mCloseGuard.warnIfOpen();
-                }
-
+                mCloseGuard.warnIfOpen();
                 close();
             } finally {
                 super.finalize();
@@ -22862,15 +22991,12 @@
         final int callingUid = Binder.getCallingUid();
         final UserHandle user = new UserHandle(UserHandle.getUserId(callingUid));
         final int moveId = mNextMoveId.getAndIncrement();
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    movePackageInternal(packageName, volumeUuid, moveId, callingUid, user);
-                } catch (PackageManagerException e) {
-                    Slog.w(TAG, "Failed to move " + packageName, e);
-                    mMoveCallbacks.notifyStatusChanged(moveId, e.error);
-                }
+        mHandler.post(() -> {
+            try {
+                movePackageInternal(packageName, volumeUuid, moveId, callingUid, user);
+            } catch (PackageManagerException e) {
+                Slog.w(TAG, "Failed to move " + packageName, e);
+                mMoveCallbacks.notifyStatusChanged(moveId, e.error);
             }
         });
         return moveId;
@@ -23070,24 +23196,21 @@
         final MoveInfo move;
         if (moveCompleteApp) {
             // Kick off a thread to report progress estimates
-            new Thread() {
-                @Override
-                public void run() {
-                    while (true) {
-                        try {
-                            if (installedLatch.await(1, TimeUnit.SECONDS)) {
-                                break;
-                            }
-                        } catch (InterruptedException ignored) {
+            new Thread(() -> {
+                while (true) {
+                    try {
+                        if (installedLatch.await(1, TimeUnit.SECONDS)) {
+                            break;
                         }
-
-                        final long deltaFreeBytes = startFreeBytes - measurePath.getUsableSpace();
-                        final int progress = 10 + (int) MathUtils.constrain(
-                                ((deltaFreeBytes * 80) / sizeBytes), 0, 80);
-                        mMoveCallbacks.notifyStatusChanged(moveId, progress);
+                    } catch (InterruptedException ignored) {
                     }
+
+                    final long deltaFreeBytes = startFreeBytes - measurePath.getUsableSpace();
+                    final int progress = 10 + (int) MathUtils.constrain(
+                            ((deltaFreeBytes * 80) / sizeBytes), 0, 80);
+                    mMoveCallbacks.notifyStatusChanged(moveId, progress);
                 }
-            }.start();
+            }).start();
 
             final String dataAppName = codeFile.getName();
             move = new MoveInfo(moveId, currentVolumeUuid, volumeUuid, packageName,
@@ -23203,6 +23326,7 @@
      * that are no longer in use by any other user.
      * @param userHandle the user being removed
      */
+    @GuardedBy("mPackages")
     private void removeUnusedPackagesLPw(UserManagerService userManager, final int userHandle) {
         final boolean DEBUG_CLEAN_APKS = false;
         int [] users = userManager.getUserIds();
@@ -23241,12 +23365,9 @@
                 if (DEBUG_CLEAN_APKS) {
                     Slog.i(TAG, "  Removing package " + packageName);
                 }
-                mHandler.post(new Runnable() {
-                    public void run() {
-                        deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
-                                userHandle, 0);
-                    } //end run
-                });
+                //end run
+                mHandler.post(() -> deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
+                        userHandle, 0));
             }
         }
     }
@@ -23267,17 +23388,10 @@
     void onNewUserCreated(final int userId) {
         mDefaultPermissionPolicy.grantDefaultPermissions(userId);
         synchronized(mPackages) {
-            // If permission review for legacy apps is required, we represent
-            // dagerous permissions for such apps as always granted runtime
-            // permissions to keep per user flag state whether review is needed.
-            // Hence, if a new user is added we have to propagate dangerous
-            // permission grants for these legacy apps.
-            if (mSettings.mPermissions.mPermissionReviewRequired) {
-// NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
-                mPermissionManager.updateAllPermissions(
-                        StorageManager.UUID_PRIVATE_INTERNAL, true, mPackages.values(),
-                        mPermissionCallback);
-            }
+            // NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
+            mPermissionManager.updateAllPermissions(
+                    StorageManager.UUID_PRIVATE_INTERNAL, true, mPackages.values(),
+                    mPermissionCallback);
         }
     }
 
@@ -23484,6 +23598,7 @@
         }
     }
 
+    @GuardedBy("mPackages")
     private void deletePackageIfUnusedLPr(final String packageName) {
         PackageSetting ps = mSettings.mPackages.get(packageName);
         if (ps == null) {
@@ -23493,12 +23608,8 @@
             // TODO Implement atomic delete if package is unused
             // It is currently possible that the package will be deleted even if it is installed
             // after this method returns.
-            mHandler.post(new Runnable() {
-                public void run() {
-                    deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
-                            0, PackageManager.DELETE_ALL_USERS);
-                }
-            });
+            mHandler.post(() -> deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
+                    0, PackageManager.DELETE_ALL_USERS));
         }
     }
 
@@ -23854,6 +23965,8 @@
                     return mRequiredVerifierPackage;
                 case PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER:
                     return mSystemTextClassifierPackage;
+                case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
+                    return mRequiredPermissionControllerPackage;
             }
             return null;
         }
@@ -24174,7 +24287,7 @@
         }
 
         @Override
-        public boolean isLegacySystemApp(Package pkg) {
+        public boolean isLegacySystemApp(PackageParser.Package pkg) {
             synchronized (mPackages) {
                 final PackageSetting ps = (PackageSetting) pkg.mExtras;
                 return mPromoteSystemApps
@@ -24315,6 +24428,37 @@
                 PackageManagerService.this.notifyPackageUseLocked(packageName, reason);
             }
         }
+
+        @Override
+        public CheckPermissionDelegate getCheckPermissionDelegate() {
+            synchronized (mPackages) {
+                return PackageManagerService.this.getCheckPermissionDelegateLocked();
+            }
+        }
+
+        @Override
+        public void setCheckPermissionDelegate(CheckPermissionDelegate delegate) {
+            synchronized (mPackages) {
+                PackageManagerService.this.setCheckPermissionDelegateLocked(delegate);
+            }
+        }
+
+        @Override
+        public SparseArray<String> getAppsWithSharedUserIds() {
+            synchronized (mPackages) {
+                return getAppsWithSharedUserIdsLocked();
+            }
+        }
+    }
+
+    private SparseArray<String> getAppsWithSharedUserIdsLocked() {
+        final SparseArray<String> sharedUserIds = new SparseArray<>();
+        synchronized (mPackages) {
+            for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
+                sharedUserIds.put(UserHandle.getAppId(setting.userId), setting.name);
+            }
+        }
+        return sharedUserIds;
     }
 
     @Override
@@ -24350,11 +24494,9 @@
             String[] packageNames, int userId) {
         enforceSystemOrPhoneCaller("grantDefaultPermissionsToEnabledTelephonyDataServices");
         synchronized (mPackages) {
-            Binder.withCleanCallingIdentity( () -> {
-                mDefaultPermissionPolicy.
-                        grantDefaultPermissionsToEnabledTelephonyDataServices(
-                                packageNames, userId);
-            });
+            Binder.withCleanCallingIdentity( () -> mDefaultPermissionPolicy.
+                    grantDefaultPermissionsToEnabledTelephonyDataServices(
+                            packageNames, userId));
         }
     }
 
@@ -24363,11 +24505,9 @@
             String[] packageNames, int userId) {
         enforceSystemOrPhoneCaller("revokeDefaultPermissionsFromDisabledTelephonyDataServices");
         synchronized (mPackages) {
-            Binder.withCleanCallingIdentity( () -> {
-                mDefaultPermissionPolicy.
-                        revokeDefaultPermissionsFromDisabledTelephonyDataServices(
-                                packageNames, userId);
-            });
+            Binder.withCleanCallingIdentity( () -> mDefaultPermissionPolicy.
+                    revokeDefaultPermissionsFromDisabledTelephonyDataServices(
+                            packageNames, userId));
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d06437e..3834a88 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -547,6 +547,10 @@
                     case "-e":
                         listEnabled = true;
                         break;
+                    case "-a":
+                        getFlags |= PackageManager.MATCH_KNOWN_PACKAGES;
+                        getFlags |= PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS;
+                        break;
                     case "-f":
                         showSourceDir = true;
                         break;
@@ -2419,30 +2423,30 @@
 
     private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
             boolean logSuccess) throws RemoteException {
-        final PrintWriter pw = getOutPrintWriter();
-        final ParcelFileDescriptor fd;
-        if (STDIN_PATH.equals(inPath)) {
-            fd = new ParcelFileDescriptor(getInFileDescriptor());
-        } else if (inPath != null) {
-            fd = openFileForSystem(inPath, "r");
-            if (fd == null) {
-                return -1;
-            }
-            sizeBytes = fd.getStatSize();
-            if (sizeBytes < 0) {
-                getErrPrintWriter().println("Unable to get size of: " + inPath);
-                return -1;
-            }
-        } else {
-            fd = new ParcelFileDescriptor(getInFileDescriptor());
-        }
-        if (sizeBytes <= 0) {
-            getErrPrintWriter().println("Error: must specify a APK size");
-            return 1;
-        }
-
         PackageInstaller.Session session = null;
         try {
+            final PrintWriter pw = getOutPrintWriter();
+            final ParcelFileDescriptor fd;
+            if (STDIN_PATH.equals(inPath)) {
+                fd = ParcelFileDescriptor.dup(getInFileDescriptor());
+            } else if (inPath != null) {
+                fd = openFileForSystem(inPath, "r");
+                if (fd == null) {
+                    return -1;
+                }
+                sizeBytes = fd.getStatSize();
+                if (sizeBytes < 0) {
+                    getErrPrintWriter().println("Unable to get size of: " + inPath);
+                    return -1;
+                }
+            } else {
+                fd = ParcelFileDescriptor.dup(getInFileDescriptor());
+            }
+            if (sizeBytes <= 0) {
+                getErrPrintWriter().println("Error: must specify a APK size");
+                return 1;
+            }
+
             session = new PackageInstaller.Session(
                     mInterface.getPackageInstaller().openSession(sessionId));
             session.write(splitName, 0, sizeBytes, fd);
@@ -2688,6 +2692,7 @@
         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");
+        pw.println("      -a: all known packages");
         pw.println("      -d: filter to only show disabled packages");
         pw.println("      -e: filter to only show enabled packages");
         pw.println("      -s: filter to only show system packages");
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 5177995..e9cd707 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -83,6 +83,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.JournaledFile;
@@ -4120,7 +4121,8 @@
                     continue;
                 }
                 final boolean shouldInstall = ps.isSystem() &&
-                        !ArrayUtils.contains(disallowedPackages, ps.name);
+                        !ArrayUtils.contains(disallowedPackages, ps.name) &&
+                        !ps.pkg.applicationInfo.hiddenUntilInstalled;
                 // Only system apps are initially installed.
                 ps.setInstalled(shouldInstall, userHandle);
                 if (!shouldInstall) {
@@ -4471,7 +4473,7 @@
 
     void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag,
             ArraySet<String> permissionNames, PackageSetting ps, SimpleDateFormat sdf,
-            Date date, List<UserInfo> users, boolean dumpAll) {
+            Date date, List<UserInfo> users, boolean dumpAll, boolean dumpAllComponents) {
         if (checkinTag != null) {
             pw.print(checkinTag);
             pw.print(",");
@@ -4756,6 +4758,10 @@
             dumpInstallPermissionsLPr(pw, prefix + "  ", permissionNames, permissionsState);
         }
 
+        if (dumpAllComponents) {
+            dumpComponents(pw, prefix + "  ", ps);
+        }
+
         for (UserInfo user : users) {
             pw.print(prefix); pw.print("  User "); pw.print(user.id); pw.print(": ");
             pw.print("ceDataInode=");
@@ -4835,6 +4841,8 @@
         final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         final Date date = new Date();
         boolean printedSomething = false;
+        final boolean dumpAllComponents =
+                dumpState.isOptionEnabled(DumpState.OPTION_DUMP_ALL_COMPONENTS);
         List<UserInfo> users = getAllUsers(UserManagerService.getInstance());
         for (final PackageSetting ps : mPackages.values()) {
             if (packageName != null && !packageName.equals(ps.realName)
@@ -4857,7 +4865,7 @@
                 printedSomething = true;
             }
             dumpPackageLPr(pw, "  ", checkin ? "pkg" : null, permissionNames, ps, sdf, date, users,
-                    packageName != null);
+                    packageName != null, dumpAllComponents);
         }
 
         printedSomething = false;
@@ -4898,7 +4906,7 @@
                     printedSomething = true;
                 }
                 dumpPackageLPr(pw, "  ", checkin ? "dis" : null, permissionNames, ps, sdf, date,
-                        users, packageName != null);
+                        users, packageName != null, dumpAllComponents);
             }
         }
     }
@@ -5107,6 +5115,28 @@
         }
     }
 
+    void dumpComponents(PrintWriter pw, String prefix, PackageSetting ps) {
+        dumpComponents(pw, prefix, ps, "activities:", ps.pkg.activities);
+        dumpComponents(pw, prefix, ps, "services:", ps.pkg.services);
+        dumpComponents(pw, prefix, ps, "receivers:", ps.pkg.receivers);
+        dumpComponents(pw, prefix, ps, "providers:", ps.pkg.providers);
+        dumpComponents(pw, prefix, ps, "instrumentations:", ps.pkg.instrumentation);
+    }
+
+    void dumpComponents(PrintWriter pw, String prefix, PackageSetting ps,
+            String label, List<? extends PackageParser.Component<?>> list) {
+        final int size = CollectionUtils.size(list);
+        if (size == 0) {
+            return;
+        }
+        pw.print(prefix);pw.println(label);
+        for (int i = 0; i < size; i++) {
+            final PackageParser.Component<?> component = list.get(i);
+            pw.print(prefix);pw.print("  ");
+            pw.println(component.getComponentName().flattenToShortString());
+        }
+    }
+
     public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) {
         if (sync) {
             mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId);
@@ -5142,10 +5172,12 @@
             mPersistenceLock = persistenceLock;
         }
 
+        @GuardedBy("Settings.this.mLock")
         public boolean areDefaultRuntimPermissionsGrantedLPr(int userId) {
             return mDefaultPermissionsGranted.get(userId);
         }
 
+        @GuardedBy("Settings.this.mLock")
         public void onDefaultRuntimePermissionsGrantedLPr(int userId) {
             mFingerprints.put(userId, Build.FINGERPRINT);
             writePermissionsForUserAsyncLPr(userId);
@@ -5156,6 +5188,7 @@
             writePermissionsSync(userId);
         }
 
+        @GuardedBy("Settings.this.mLock")
         public void writePermissionsForUserAsyncLPr(int userId) {
             final long currentTimeMillis = SystemClock.uptimeMillis();
 
@@ -5324,6 +5357,7 @@
             }
         }
 
+        @GuardedBy("Settings.this.mLock")
         private void onUserRemovedLPw(int userId) {
             // Make sure we do not
             mHandler.removeMessages(userId);
@@ -5357,6 +5391,7 @@
             getUserRuntimePermissionsFile(userId).delete();
         }
 
+        @GuardedBy("Settings.this.mLock")
         public void readStateForUserSyncLPr(int userId) {
             File permissionsFile = getUserRuntimePermissionsFile(userId);
             if (!permissionsFile.exists()) {
@@ -5409,6 +5444,7 @@
 
         // Private internals
 
+        @GuardedBy("Settings.this.mLock")
         private void parseRuntimePermissionsLPr(XmlPullParser parser, int userId)
                 throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 599e5a5..b9c3048 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -132,6 +132,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -1573,6 +1574,24 @@
                 "Ephemeral apps can't use ShortcutManager");
     }
 
+    private void verifyShortcutInfoPackage(String callerPackage, ShortcutInfo si) {
+        if (si == null) {
+            return;
+        }
+        if (!Objects.equals(callerPackage, si.getPackage())) {
+            android.util.EventLog.writeEvent(0x534e4554, "109824443", -1, "");
+            throw new SecurityException("Shortcut package name mismatch");
+        }
+    }
+
+    private void verifyShortcutInfoPackages(
+            String callerPackage, List<ShortcutInfo> list) {
+        final int size = list.size();
+        for (int i = 0; i < size; i++) {
+            verifyShortcutInfoPackage(callerPackage, list.get(i));
+        }
+    }
+
     // Overridden in unit tests to execute r synchronously.
     void injectPostToHandler(Runnable r) {
         mHandler.post(r);
@@ -1720,6 +1739,7 @@
         verifyCaller(packageName, userId);
 
         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+        verifyShortcutInfoPackages(packageName, newShortcuts);
         final int size = newShortcuts.size();
 
         final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
@@ -1774,6 +1794,7 @@
         verifyCaller(packageName, userId);
 
         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+        verifyShortcutInfoPackages(packageName, newShortcuts);
         final int size = newShortcuts.size();
 
         final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
@@ -1859,6 +1880,7 @@
         verifyCaller(packageName, userId);
 
         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+        verifyShortcutInfoPackages(packageName, newShortcuts);
         final int size = newShortcuts.size();
 
         final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
@@ -1921,6 +1943,7 @@
         Preconditions.checkNotNull(shortcut);
         Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
         verifyCaller(packageName, userId);
+        verifyShortcutInfoPackage(packageName, shortcut);
 
         final Intent ret;
         synchronized (mLock) {
@@ -1942,6 +1965,7 @@
     private boolean requestPinItem(String packageName, int userId, ShortcutInfo shortcut,
             AppWidgetProviderInfo appWidget, Bundle extras, IntentSender resultIntent) {
         verifyCaller(packageName, userId);
+        verifyShortcutInfoPackage(packageName, shortcut);
 
         final boolean ret;
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 407cb93..c18ca25 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -31,7 +31,6 @@
 import android.app.IStopUserCallback;
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -72,7 +71,6 @@
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.storage.StorageManager;
-import android.provider.Settings;
 import android.security.GateKeeper;
 import android.service.gatekeeper.IGateKeeperService;
 import android.util.AtomicFile;
@@ -82,7 +80,6 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
-import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 
@@ -101,8 +98,7 @@
 import com.android.server.SystemService;
 import com.android.server.am.UserState;
 import com.android.server.storage.DeviceStorageMonitorInternal;
-
-import libcore.io.IoUtils;
+import com.android.server.wm.ActivityTaskManagerInternal;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -125,6 +121,8 @@
 import java.util.List;
 import java.util.Objects;
 
+import libcore.io.IoUtils;
+
 /**
  * Service for {@link UserManager}.
  *
@@ -686,6 +684,7 @@
     }
 
     /** Assume permissions already checked and caller's identity cleared */
+    @GuardedBy("mUsersLock")
     private List<UserInfo> getProfilesLU(int userId, boolean enabledOnly, boolean fullInfo) {
         IntArray profileIds = getProfileIdsLU(userId, enabledOnly);
         ArrayList<UserInfo> users = new ArrayList<>(profileIds.size());
@@ -708,6 +707,7 @@
     /**
      *  Assume permissions already checked and caller's identity cleared
      */
+    @GuardedBy("mUsersLock")
     private IntArray getProfileIdsLU(int userId, boolean enabledOnly) {
         UserInfo user = getUserInfoLU(userId);
         IntArray result = new IntArray(mUsers.size());
@@ -786,6 +786,7 @@
         return mLocalService.getProfileParentId(userHandle);
     }
 
+    @GuardedBy("mUsersLock")
     private UserInfo getProfileParentLU(int userHandle) {
         UserInfo profile = getUserInfoLU(userHandle);
         if (profile == null) {
@@ -1208,6 +1209,7 @@
     /*
      * Should be locked on mUsers before calling this.
      */
+    @GuardedBy("mUsersLock")
     private UserInfo getUserInfoLU(int userId) {
         final UserData userData = mUsers.get(userId);
         // If it is partial and not in the process of being removed, return as unknown user.
@@ -1218,6 +1220,7 @@
         return userData != null ? userData.info : null;
     }
 
+    @GuardedBy("mUsersLock")
     private UserData getUserDataLU(int userId) {
         final UserData userData = mUsers.get(userId);
         // If it is partial and not in the process of being removed, return as unknown user.
@@ -1720,6 +1723,7 @@
     }
 
     // Package private for the inner class.
+    @GuardedBy("mRestrictionsLock")
     void applyUserRestrictionsLR(int userId) {
         updateUserRestrictionsInternalLR(null, userId);
     }
@@ -1800,6 +1804,7 @@
         }
     }
 
+    @GuardedBy("mUsersLock")
     private int getAliveUsersExcludingGuestsCountLU() {
         int aliveUserCount = 0;
         final int totalUserCount = mUsers.size();
@@ -1973,6 +1978,7 @@
         }
     }
 
+    @GuardedBy({"mRestrictionsLock", "mPackagesLock"})
     private void readUserListLP() {
         if (!mUserListFile.exists()) {
             fallbackToSingleUserLP();
@@ -2070,6 +2076,7 @@
      * Upgrade steps between versions, either for fixing bugs or changing the data format.
      * @param oldGlobalUserRestrictions Pre-O global device policy restrictions.
      */
+    @GuardedBy({"mRestrictionsLock", "mPackagesLock"})
     private void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions) {
         final int originalVersion = mUserVersion;
         int userVersion = mUserVersion;
@@ -2150,6 +2157,7 @@
         }
     }
 
+    @GuardedBy({"mPackagesLock", "mRestrictionsLock"})
     private void fallbackToSingleUserLP() {
         int flags = UserInfo.FLAG_INITIALIZED;
         // In split system user mode, the admin and primary flags are assigned to the first human
@@ -2319,6 +2327,7 @@
      *   <user id="2"></user>
      * </users>
      */
+    @GuardedBy({"mRestrictionsLock", "mPackagesLock"})
     private void writeUserListLP() {
         if (DBG) {
             debug("writeUserList");
@@ -2968,9 +2977,9 @@
                             new Thread() {
                                 @Override
                                 public void run() {
-                                    // Clean up any ActivityManager state
-                                    LocalServices.getService(ActivityManagerInternal.class)
-                                            .onUserRemoved(userHandle);
+                                    // Clean up any ActivityTaskManager state
+                                    LocalServices.getService(ActivityTaskManagerInternal.class)
+                                            .onUserStopped(userHandle);
                                     removeUserState(userHandle);
                                 }
                             }.start();
@@ -4062,6 +4071,7 @@
         }
     }
 
+    @GuardedBy("mUsersLock")
     @VisibleForTesting
     int getFreeProfileBadgeLU(int parentUserId) {
         int maxManagedProfiles = getMaxManagedProfiles();
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 0ba7822..21daa39 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -519,6 +519,11 @@
     private static final int TRON_COMPILATION_REASON_AB_OTA = 6;
     private static final int TRON_COMPILATION_REASON_INACTIVE = 7;
     private static final int TRON_COMPILATION_REASON_SHARED = 8;
+    private static final int TRON_COMPILATION_REASON_INSTALL_WITH_DEX_METADATA = 9;
+
+    // The annotation to add as a suffix to the compilation reason when dexopt was
+    // performed with dex metadata.
+    public static final String DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION = "-dm";
 
     /**
      * Convert the compilation reason to an int suitable to be logged to TRON.
@@ -534,6 +539,10 @@
             case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA;
             case "inactive" : return TRON_COMPILATION_REASON_INACTIVE;
             case "shared" : return TRON_COMPILATION_REASON_SHARED;
+            // This is a special marker for dex metadata installation that does not
+            // have an equivalent as a system property.
+            case "install" + DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION :
+                return TRON_COMPILATION_REASON_INSTALL_WITH_DEX_METADATA;
             default: return TRON_COMPILATION_REASON_UNKNOWN;
         }
     }
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 1ae59cb..50e6f8d 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1193,12 +1193,27 @@
         }
     }
 
-    private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
-            boolean systemFixed, boolean ignoreSystemPackage, int userId) {
+    private void grantRuntimePermissions(PackageParser.Package pkg,
+            Set<String> permissionsWithoutSplits, boolean systemFixed, boolean ignoreSystemPackage,
+            int userId) {
         if (pkg.requestedPermissions.isEmpty()) {
             return;
         }
 
+        final ArraySet<String> permissions = new ArraySet<>(permissionsWithoutSplits);
+
+        // Automatically attempt to grant split permissions to older APKs
+        final int numSplitPerms = PackageParser.SPLIT_PERMISSIONS.length;
+        for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
+            final PackageParser.SplitPermissionInfo splitPerm =
+                    PackageParser.SPLIT_PERMISSIONS[splitPermNum];
+
+            if (pkg.applicationInfo.targetSdkVersion < splitPerm.targetSdk
+                    && permissionsWithoutSplits.contains(splitPerm.rootPerm)) {
+                Collections.addAll(permissions, splitPerm.newPerms);
+            }
+        }
+
         List<String> requestedPermissions = pkg.requestedPermissions;
         Set<String> grantablePermissions = null;
 
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
index a042fed..80a5fbb6 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -21,19 +21,11 @@
 import android.content.pm.PackageParser;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManager.PermissionInfoFlags;
-import android.content.pm.PackageParser.Permission;
-
-import com.android.server.pm.SharedUserSetting;
-import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 /**
  * Internal interfaces to be used by other components within the system server.
@@ -115,7 +107,11 @@
      */
     public abstract void addAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
     public abstract void addAllPermissionGroups(@NonNull PackageParser.Package pkg, boolean chatty);
-    public abstract void removeAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
+    public abstract void removeAllPermissions(
+            @NonNull PackageParser.Package pkg,
+            @NonNull List<String> allPackageNames,
+            @Nullable PermissionCallback permissionCallback,
+            boolean chatty);
     public abstract boolean addDynamicPermission(@NonNull PermissionInfo info, boolean async,
             int callingUid, @Nullable PermissionCallback callback);
     public abstract void removeDynamicPermission(@NonNull String permName, int callingUid,
@@ -189,4 +185,4 @@
 
     /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
     public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
-}
\ No newline at end of file
+}
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 7f2d5c3..9dc0b29 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -31,6 +31,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageParser;
@@ -38,6 +39,7 @@
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.metrics.LogMaker;
+import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -159,7 +161,7 @@
         mLock = externalLock;
         mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
         mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
-        mSettings = new PermissionSettings(context, mLock);
+        mSettings = new PermissionSettings(mLock);
 
         mHandlerThread = new ServiceThread(TAG,
                 Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
@@ -476,8 +478,9 @@
                                         " to " + newPermissionGroupName);
 
                                 try {
-                                    revokeRuntimePermission(permissionName, packageName, false,
-                                            Process.SYSTEM_UID, userId, permissionCallback);
+                                    revokeRuntimePermission(permissionName, packageName,
+                                            mSettings.getPermission(permissionName), false,
+                                            Process.SYSTEM_UID, userId, permissionCallback, false);
                                 } catch (IllegalArgumentException e) {
                                     Slog.e(TAG, "Could not revoke " + permissionName + " from "
                                             + packageName, e);
@@ -570,9 +573,59 @@
 
     }
 
-    private void removeAllPermissions(PackageParser.Package pkg, boolean chatty) {
+    private void revokeAllPermissions(
+            @NonNull List<BasePermission> bps,
+            @NonNull List<String> allPackageNames,
+            @Nullable PermissionCallback permissionCallback) {
+        AsyncTask.execute(() -> {
+            final int numRemovedPermissions = bps.size();
+            for (int permissionNum = 0; permissionNum < numRemovedPermissions; permissionNum++) {
+                final int[] userIds = mUserManagerInt.getUserIds();
+                final int numUserIds = userIds.length;
+
+                final int numPackages = allPackageNames.size();
+                for (int packageNum = 0; packageNum < numPackages; packageNum++) {
+                    final String packageName = allPackageNames.get(packageNum);
+                    final ApplicationInfo applicationInfo = mPackageManagerInt.getApplicationInfo(
+                            packageName, 0, Process.SYSTEM_UID, UserHandle.USER_SYSTEM);
+                    if (applicationInfo != null
+                            && applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+                        continue;
+                    }
+                    for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
+                        final int userId = userIds[userIdNum];
+                        final String permissionName = bps.get(permissionNum).getName();
+                        if (checkPermission(permissionName, packageName, UserHandle.USER_SYSTEM,
+                                userId) == PackageManager.PERMISSION_GRANTED) {
+                            try {
+                                revokeRuntimePermission(
+                                        permissionName,
+                                        packageName,
+                                        bps.get(permissionNum),
+                                        false,
+                                        Process.SYSTEM_UID,
+                                        userId,
+                                        permissionCallback,
+                                        true);
+                            } catch (IllegalArgumentException e) {
+                                Slog.e(TAG, "Could not revoke " + permissionName + " from "
+                                        + packageName, e);
+                            }
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    private void removeAllPermissions(
+            @NonNull PackageParser.Package pkg,
+            @NonNull List<String> allPackageNames,
+            @Nullable PermissionCallback permissionCallback,
+            boolean chatty) {
         synchronized (mLock) {
             int N = pkg.permissions.size();
+            List<BasePermission> bps = new ArrayList<BasePermission>(N);
             StringBuilder r = null;
             for (int i=0; i<N; i++) {
                 PackageParser.Permission p = pkg.permissions.get(i);
@@ -581,6 +634,9 @@
                     bp = mSettings.mPermissionTrees.get(p.info.name);
                 }
                 if (bp != null && bp.isPermission(p)) {
+                    if ((p.info.getProtection() & PermissionInfo.PROTECTION_DANGEROUS) != 0) {
+                        bps.add(bp);
+                    }
                     bp.setPermission(null);
                     if (DEBUG_REMOVE && chatty) {
                         if (r == null) {
@@ -599,6 +655,7 @@
                     }
                 }
             }
+            revokeAllPermissions(bps, allPackageNames, permissionCallback);
             if (r != null) {
                 if (DEBUG_REMOVE) Log.d(TAG, "  Permissions: " + r);
             }
@@ -788,10 +845,7 @@
                     // their permissions as always granted runtime ones since we need
                     // to keep the review required permission flag per user while an
                     // install permission's state is shared across all users.
-                    if (!appSupportsRuntimePermissions && !mSettings.mPermissionReviewRequired) {
-                        // For legacy apps dangerous permissions are install time ones.
-                        grant = GRANT_INSTALL;
-                    } else if (origPermissions.hasInstallPermission(bp.getName())) {
+                    if (origPermissions.hasInstallPermission(bp.getName())) {
                         // For legacy apps that became modern, install becomes runtime.
                         grant = GRANT_UPGRADE;
                     } else if (isLegacySystemApp) {
@@ -877,7 +931,7 @@
                                         updatedUserIds = ArrayUtils.appendInt(
                                                 updatedUserIds, userId);
                                     }
-                                    if (!mSettings.mPermissionReviewRequired || !revokeOnUpgrade) {
+                                    if (!revokeOnUpgrade) {
                                         if (permissionsState.grantRuntimePermission(bp, userId) ==
                                                 PermissionsState.PERMISSION_OPERATION_FAILURE) {
                                             // If we cannot put the permission as it was,
@@ -888,8 +942,7 @@
                                     }
 
                                     // If the app supports runtime permissions no need for a review.
-                                    if (mSettings.mPermissionReviewRequired
-                                            && appSupportsRuntimePermissions
+                                    if (appSupportsRuntimePermissions
                                             && (flags & PackageManager
                                                     .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
                                         flags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
@@ -897,8 +950,7 @@
                                         updatedUserIds = ArrayUtils.appendInt(
                                                 updatedUserIds, userId);
                                     }
-                                } else if (mSettings.mPermissionReviewRequired
-                                        && !appSupportsRuntimePermissions) {
+                                } else if (!appSupportsRuntimePermissions) {
                                     // For legacy apps that need a permission review, every new
                                     // runtime permission is granted but it is pending a review.
                                     // We also need to review only platform defined runtime
@@ -1215,9 +1267,15 @@
                 // we still want to blindly grant it to old apps.
                 allowed = true;
             }
+            // TODO (moltmann): The installer now shares the platforms signature. Hence it does not
+            //                  need a separate flag anymore. Hence we need to check which
+            //                  permissions are needed by the permission controller
             if (!allowed && bp.isInstaller()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
-                            PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM))) {
+                    && (pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                            PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM))
+                    || pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                            PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER,
+                            UserHandle.USER_SYSTEM)))) {
                 // If this permission is to be granted to the system installer and
                 // this app is an installer, then it gets the permission.
                 allowed = true;
@@ -1272,10 +1330,6 @@
     }
 
     private boolean isPermissionsReviewRequired(PackageParser.Package pkg, int userId) {
-        if (!mSettings.mPermissionReviewRequired) {
-            return false;
-        }
-
         // Permission review applies only to apps not supporting the new permission model.
         if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
             return false;
@@ -1377,7 +1431,7 @@
                         grantRuntimePermission(permission, pkg.packageName, false, callingUid,
                                 userId, callback);
                     }
-                } else if (mSettings.mPermissionReviewRequired) {
+                } else {
                     // In permission review mode we clear the review flag when we
                     // are asked to install the app with all permissions granted.
                     if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
@@ -1428,8 +1482,7 @@
         // their permissions as always granted runtime ones since we need
         // to keep the review required permission flag per user while an
         // install permission's state is shared across all users.
-        if (mSettings.mPermissionReviewRequired
-                && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
                 && bp.isRuntime()) {
             return;
         }
@@ -1511,9 +1564,10 @@
         }
 
     }
-
-    private void revokeRuntimePermission(String permName, String packageName,
-            boolean overridePolicy, int callingUid, int userId, PermissionCallback callback) {
+    
+    private void revokeRuntimePermission(String permName, String packageName, BasePermission bp,
+            boolean overridePolicy, int callingUid, int userId, PermissionCallback callback,
+            boolean permissionRemoved) {
         if (!mUserManagerInt.exists(userId)) {
             Log.e(TAG, "No such user:" + userId);
             return;
@@ -1538,7 +1592,7 @@
         if (mPackageManagerInt.filterAppAccess(pkg, Binder.getCallingUid(), userId)) {
             throw new IllegalArgumentException("Unknown package: " + packageName);
         }
-        final BasePermission bp = mSettings.getPermissionLocked(permName);
+
         if (bp == null) {
             throw new IllegalArgumentException("Unknown permission: " + permName);
         }
@@ -1549,8 +1603,7 @@
         // their permissions as always granted runtime ones since we need
         // to keep the review required permission flag per user while an
         // install permission's state is shared across all users.
-        if (mSettings.mPermissionReviewRequired
-                && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
                 && bp.isRuntime()) {
             return;
         }
@@ -2001,6 +2054,7 @@
         }
     }
 
+    @GuardedBy({"mSettings.mLock", "mLock"})
     private int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
         int size = 0;
         for (BasePermission perm : mSettings.mPermissions.values()) {
@@ -2009,6 +2063,7 @@
         return size;
     }
 
+    @GuardedBy({"mSettings.mLock", "mLock"})
     private void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
         // We calculate the max size of permissions defined by this uid and throw
         // if that plus the size of 'info' would exceed our stated maximum.
@@ -2094,8 +2149,10 @@
             PermissionManagerService.this.addAllPermissionGroups(pkg, chatty);
         }
         @Override
-        public void removeAllPermissions(Package pkg, boolean chatty) {
-            PermissionManagerService.this.removeAllPermissions(pkg, chatty);
+        public void removeAllPermissions(Package pkg, List<String> allPackageNames,
+                PermissionCallback permissionCallback, boolean chatty) {
+            PermissionManagerService.this.removeAllPermissions(
+                    pkg, allPackageNames, permissionCallback, chatty);
         }
         @Override
         public boolean addDynamicPermission(PermissionInfo info, boolean async, int callingUid,
@@ -2131,7 +2188,8 @@
                 boolean overridePolicy, int callingUid, int userId,
                 PermissionCallback callback) {
             PermissionManagerService.this.revokeRuntimePermission(permName, packageName,
-                    overridePolicy, callingUid, userId, callback);
+                    mSettings.getPermission(permName), overridePolicy, callingUid, userId,
+                    callback, false);
         }
         @Override
         public void updatePermissions(String packageName, Package pkg, boolean replaceGrant,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
index b3f2833..9208032 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
@@ -47,8 +47,6 @@
  */
 public class PermissionSettings {
 
-    public final boolean mPermissionReviewRequired;
-
     /**
      * All of the permissions known to the system. The mapping is from permission
      * name to permission object.
@@ -82,9 +80,7 @@
 
     private final Object mLock;
 
-    PermissionSettings(@NonNull Context context, @NonNull Object lock) {
-        mPermissionReviewRequired =
-                context.getResources().getBoolean(R.bool.config_permissionReviewRequired);
+    PermissionSettings(@NonNull Object lock) {
         mLock = lock;
     }
 
diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java
index eca6f9f..14c985c 100644
--- a/services/core/java/com/android/server/policy/BarController.java
+++ b/services/core/java/com/android/server/policy/BarController.java
@@ -196,7 +196,7 @@
     }
 
     protected boolean skipAnimation() {
-        return false;
+        return !mWin.isDrawnLw();
     }
 
     private int computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index add8286..5769207 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -76,6 +76,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_BAR_EXPANDED;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
@@ -124,7 +125,6 @@
 import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
 import static android.view.WindowManagerGlobal.ADD_OKAY;
 import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
-
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -155,7 +155,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
-import android.app.ActivityManagerInternal.SleepToken;
+import android.app.ActivityTaskManager;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.app.IUiModeManager;
@@ -273,8 +273,8 @@
 import com.android.internal.policy.PhoneWindow;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.util.ScreenShapeHelper;
+import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.widget.PointerLocationView;
 import com.android.server.GestureLauncherService;
 import com.android.server.LocalServices;
@@ -284,10 +284,16 @@
 import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.vr.VrManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
 import com.android.server.wm.AppTransition;
 import com.android.server.wm.DisplayFrames;
+import com.android.server.wm.DisplayPolicy;
+import com.android.server.wm.DisplayRotation;
+import com.android.server.wm.WindowFrames;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import com.android.server.wm.utils.InsetUtils;
 
 import java.io.File;
 import java.io.FileReader;
@@ -453,6 +459,7 @@
     WindowManagerInternal mWindowManagerInternal;
     PowerManager mPowerManager;
     ActivityManagerInternal mActivityManagerInternal;
+    ActivityTaskManagerInternal mActivityTaskManagerInternal;
     AutofillManagerInternal mAutofillManagerInternal;
     InputManagerInternal mInputManagerInternal;
     InputMethodManagerInternal mInputMethodManagerInternal;
@@ -495,8 +502,6 @@
     WindowState mStatusBar = null;
     private final int[] mStatusBarHeightForRotation = new int[4];
     WindowState mNavigationBar = null;
-    boolean mHasNavigationBar = false;
-    boolean mNavigationBarCanMove = false; // can the navigation bar ever move to the side?
     @NavigationBarPosition
     int mNavigationBarPosition = NAV_BAR_BOTTOM;
     int[] mNavigationBarHeightForRotationDefault = new int[4];
@@ -551,8 +556,6 @@
     volatile boolean mRecentsVisible;
     volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true;
     volatile boolean mPictureInPictureVisible;
-    // Written by vr manager thread, only read in this class.
-    volatile private boolean mPersistentVrModeEnabled;
     volatile private boolean mDismissImeOnBackKeyPressed;
 
     // Used to hold the last user key used to wake the device.  This helps us prevent up events
@@ -562,40 +565,18 @@
     int mRecentAppsHeldModifiers;
     boolean mLanguageSwitchKeyPressed;
 
-    int mLidState = LID_ABSENT;
     int mCameraLensCoverState = CAMERA_LENS_COVER_ABSENT;
     boolean mHaveBuiltInKeyboard;
 
     boolean mSystemReady;
     boolean mSystemBooted;
-    boolean mHdmiPlugged;
     HdmiControl mHdmiControl;
     IUiModeManager mUiModeManager;
     int mUiMode;
-    int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
-    int mLidOpenRotation;
-    int mCarDockRotation;
-    int mDeskDockRotation;
-    int mUndockedHdmiRotation;
-    int mDemoHdmiRotation;
-    boolean mDemoHdmiRotationLock;
-    int mDemoRotation;
-    boolean mDemoRotationLock;
 
     boolean mWakeGestureEnabledSetting;
     MyWakeGestureListener mWakeGestureListener;
 
-    // Default display does not rotate, apps that require non-default orientation will have to
-    // have the orientation emulated.
-    private boolean mForceDefaultOrientation = false;
-
-    int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
-    int mUserRotation = Surface.ROTATION_0;
-
-    boolean mSupportAutoRotation;
-    int mAllowAllRotations = -1;
-    boolean mCarDockEnablesAccelerometer;
-    boolean mDeskDockEnablesAccelerometer;
     int mLidKeyboardAccessibility;
     int mLidNavigationAccessibility;
     boolean mLidControlsScreenLock;
@@ -608,14 +589,6 @@
     int mLongPressOnBackBehavior;
     int mShortPressOnSleepBehavior;
     int mShortPressOnWindowBehavior;
-    volatile boolean mAwake;
-    boolean mScreenOnEarly;
-    boolean mScreenOnFully;
-    ScreenOnListener mScreenOnListener;
-    boolean mKeyguardDrawComplete;
-    boolean mWindowManagerDrawComplete;
-    boolean mOrientationSensorEnabled = false;
-    int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     boolean mHasSoftInput = false;
     boolean mTranslucentDecorEnabled = true;
     boolean mUseTvRouting;
@@ -627,8 +600,10 @@
 
     int mPointerLocationMode = 0; // guarded by mLock
 
-    // The last window we were told about in focusChanged.
+    // The windows we were told about in focusChanged.
     WindowState mFocusedWindow;
+    WindowState mLastFocusedWindow;
+
     IApplicationToken mFocusedApp;
 
     PointerLocationView mPointerLocationView;
@@ -659,15 +634,7 @@
 
     InputConsumer mInputConsumer = null;
 
-    static final Rect mTmpParentFrame = new Rect();
-    static final Rect mTmpDisplayFrame = new Rect();
-    static final Rect mTmpOverscanFrame = new Rect();
-    static final Rect mTmpContentFrame = new Rect();
-    static final Rect mTmpVisibleFrame = new Rect();
-    static final Rect mTmpDecorFrame = new Rect();
-    static final Rect mTmpStableFrame = new Rect();
-    static final Rect mTmpNavigationFrame = new Rect();
-    static final Rect mTmpOutsetFrame = new Rect();
+    private final WindowFrames mWindowFrames = new WindowFrames();
     private static final Rect mTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
     private static final Rect mTmpRect = new Rect();
 
@@ -730,18 +697,14 @@
     // Behavior of Back button while in-call and screen on
     int mIncallBackBehavior;
 
-    // Behavior of rotation suggestions. (See Settings.Secure.SHOW_ROTATION_SUGGESTION)
-    int mShowRotationSuggestions;
-
     // Whether system navigation keys are enabled
     boolean mSystemNavigationKeysEnabled;
 
-    Display mDisplay;
-
-    int mLandscapeRotation = 0;  // default landscape rotation
-    int mSeascapeRotation = 0;   // "other" landscape rotation, 180 degrees from mLandscapeRotation
-    int mPortraitRotation = 0;   // default portrait rotation
-    int mUpsideDownRotation = 0; // "other" portrait rotation
+    // TODO(b/111361251): Remove default when the dependencies are multi-display ready.
+    Display mDefaultDisplay;
+    DisplayRotation mDefaultDisplayRotation;
+    DisplayPolicy mDefaultDisplayPolicy;
+    WindowOrientationListener mDefaultOrientationListener;
 
     // What we do when the user long presses on home
     private int mLongPressOnHomeBehavior;
@@ -965,7 +928,7 @@
     private UEventObserver mHDMIObserver = new UEventObserver() {
         @Override
         public void onUEvent(UEventObserver.UEvent event) {
-            setHdmiPlugged("1".equals(event.get("SWITCH_STATE")));
+            mDefaultDisplayPolicy.setHdmiPlugged("1".equals(event.get("SWITCH_STATE")));
         }
     };
 
@@ -990,12 +953,6 @@
                     Settings.Secure.WAKE_GESTURE_ENABLED), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.System.getUriFor(
-                    Settings.System.ACCELEROMETER_ROTATION), false, this,
-                    UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.System.getUriFor(
-                    Settings.System.USER_ROTATION), false, this,
-                    UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.System.getUriFor(
                     Settings.System.SCREEN_OFF_TIMEOUT), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.System.getUriFor(
@@ -1008,9 +965,6 @@
                     Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
-                    UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.VOLUME_HUSH_GESTURE), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.Global.getUriFor(
@@ -1037,7 +991,8 @@
         public void onWakeUp() {
             synchronized (mLock) {
                 if (shouldEnableWakeGestureLp()) {
-                    performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
+                    performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false,
+                            "Wake Up");
                     wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture,
                             "android.policy:GESTURE");
                 }
@@ -1045,53 +1000,11 @@
         }
     }
 
-    class MyOrientationListener extends WindowOrientationListener {
-
-        private SparseArray<Runnable> mRunnableCache;
-
-        MyOrientationListener(Context context, Handler handler) {
-            super(context, handler);
-            mRunnableCache = new SparseArray<>(5);
-        }
-
-        private class UpdateRunnable implements Runnable {
-            private final int mRotation;
-            UpdateRunnable(int rotation) {
-                mRotation = rotation;
-            }
-
-            @Override
-            public void run() {
-                // send interaction hint to improve redraw performance
-                mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
-                if (isRotationChoicePossible(mCurrentAppOrientation)) {
-                    final boolean isValid = isValidRotationChoice(mCurrentAppOrientation,
-                            mRotation);
-                    sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
-                } else {
-                    updateRotation(false);
-                }
-            }
-        }
-
-        @Override
-        public void onProposedRotationChanged(int rotation) {
-            if (localLOGV) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
-            Runnable r = mRunnableCache.get(rotation, null);
-            if (r == null){
-                r = new UpdateRunnable(rotation);
-                mRunnableCache.put(rotation, r);
-            }
-            mHandler.post(r);
-        }
-    }
-    MyOrientationListener mOrientationListener;
-
     final IPersistentVrStateCallbacks mPersistentVrModeListener =
             new IPersistentVrStateCallbacks.Stub() {
         @Override
         public void onPersistentVrStateChanged(boolean enabled) {
-            mPersistentVrModeEnabled = enabled;
+            mDefaultDisplayPolicy.setPersistentVrModeEnabled(enabled);
         }
     };
 
@@ -1117,7 +1030,7 @@
         if (mWindowSleepToken != null) {
             return;
         }
-        mWindowSleepToken = mActivityManagerInternal.acquireSleepToken("WindowSleepToken",
+        mWindowSleepToken = mActivityTaskManagerInternal.acquireSleepToken("WindowSleepToken",
                 DEFAULT_DISPLAY);
     };
 
@@ -1173,100 +1086,6 @@
         }
     }
 
-    /*
-     * We always let the sensor be switched on by default except when
-     * the user has explicitly disabled sensor based rotation or when the
-     * screen is switched off.
-     */
-    boolean needSensorRunningLp() {
-        if (mSupportAutoRotation) {
-            if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
-                    || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
-                    || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
-                    || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
-                // If the application has explicitly requested to follow the
-                // orientation, then we need to turn the sensor on.
-                return true;
-            }
-        }
-        if ((mCarDockEnablesAccelerometer && mDockMode == Intent.EXTRA_DOCK_STATE_CAR) ||
-                (mDeskDockEnablesAccelerometer && (mDockMode == Intent.EXTRA_DOCK_STATE_DESK
-                        || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
-                        || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) {
-            // enable accelerometer if we are docked in a dock that enables accelerometer
-            // orientation management,
-            return true;
-        }
-        if (mUserRotationMode == USER_ROTATION_LOCKED) {
-            // If the setting for using the sensor by default is enabled, then
-            // we will always leave it on.  Note that the user could go to
-            // a window that forces an orientation that does not use the
-            // sensor and in theory we could turn it off... however, when next
-            // turning it on we won't have a good value for the current
-            // orientation for a little bit, which can cause orientation
-            // changes to lag, so we'd like to keep it always on.  (It will
-            // still be turned off when the screen is off.)
-
-            // When locked we can provide rotation suggestions users can approve to change the
-            // current screen rotation. To do this the sensor needs to be running.
-            return mSupportAutoRotation &&
-                    mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED;
-        }
-        return mSupportAutoRotation;
-    }
-
-    /*
-     * Various use cases for invoking this function
-     * screen turning off, should always disable listeners if already enabled
-     * screen turned on and current app has sensor based orientation, enable listeners
-     * if not already enabled
-     * screen turned on and current app does not have sensor orientation, disable listeners if
-     * already enabled
-     * screen turning on and current app has sensor based orientation, enable listeners if needed
-     * screen turning on and current app has nosensor based orientation, do nothing
-     */
-    void updateOrientationListenerLp() {
-        if (!mOrientationListener.canDetectOrientation()) {
-            // If sensor is turned off or nonexistent for some reason
-            return;
-        }
-        // Could have been invoked due to screen turning on or off or
-        // change of the currently visible window's orientation.
-        if (localLOGV) Slog.v(TAG, "mScreenOnEarly=" + mScreenOnEarly
-                + ", mAwake=" + mAwake + ", mCurrentAppOrientation=" + mCurrentAppOrientation
-                + ", mOrientationSensorEnabled=" + mOrientationSensorEnabled
-                + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
-                + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
-
-        boolean disable = true;
-        // Note: We postpone the rotating of the screen until the keyguard as well as the
-        // window manager have reported a draw complete or the keyguard is going away in dismiss
-        // mode.
-        if (mScreenOnEarly && mAwake && ((mKeyguardDrawComplete && mWindowManagerDrawComplete))) {
-            if (needSensorRunningLp()) {
-                disable = false;
-                //enable listener if not already enabled
-                if (!mOrientationSensorEnabled) {
-                    // Don't clear the current sensor orientation if the keyguard is going away in
-                    // dismiss mode. This allows window manager to use the last sensor reading to
-                    // determine the orientation vs. falling back to the last known orientation if
-                    // the sensor reading was cleared which can cause it to relaunch the app that
-                    // will show in the wrong orientation first before correcting leading to app
-                    // launch delays.
-                    mOrientationListener.enable(true /* clearCurrentRotation */);
-                    if(localLOGV) Slog.v(TAG, "Enabling listeners");
-                    mOrientationSensorEnabled = true;
-                }
-            }
-        }
-        //check if sensors need to be disabled
-        if (disable && mOrientationSensorEnabled) {
-            mOrientationListener.disable();
-            if(localLOGV) Slog.v(TAG, "Disabling listeners");
-            mOrientationSensorEnabled = false;
-        }
-    }
-
     private void interceptBackKeyDown() {
         MetricsLogger.count(mContext, "key_back_down", 1);
         // Reset back key state for long press
@@ -1492,7 +1311,7 @@
     }
 
     private void powerPress(long eventTime, boolean interactive, int count) {
-        if (mScreenOnEarly && !mScreenOnFully) {
+        if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
             Slog.i(TAG, "Suppressed redundant power key press while "
                     + "already in the process of turning the screen on.");
             return;
@@ -1610,19 +1429,22 @@
             break;
         case LONG_PRESS_POWER_GLOBAL_ACTIONS:
             mPowerKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+                    "Power - Long Press - Global Actions");
             showGlobalActionsInternal();
             break;
         case LONG_PRESS_POWER_SHUT_OFF:
         case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
             mPowerKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+                    "Power - Long Press - Shut Off");
             sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
             mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
             break;
         case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
             mPowerKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+                    "Power - Long Press - Go To Voice Assist");
             final boolean keyguardActive = mKeyguardDelegate == null
                     ? false
                     : mKeyguardDelegate.isShowing();
@@ -1644,7 +1466,8 @@
             break;
         case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
             mPowerKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+                    "Power - Very Long Press - Show Global Actions");
             showGlobalActionsInternal();
             break;
         }
@@ -1799,7 +1622,8 @@
         @Override
         public void run() {
             mEndCallKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+                    "End Call - Long Press - Show Global Actions");
             showGlobalActionsInternal();
         }
     };
@@ -1926,7 +1750,8 @@
             return;
         }
         mHomeConsumed = true;
-        performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+        performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+                "Home - Long Press");
         switch (mLongPressOnHomeBehavior) {
             case LONG_PRESS_HOME_ALL_APPS:
                 launchAllAppsAction();
@@ -1992,6 +1817,14 @@
         return mContext.getResources().getConfiguration().isScreenRound();
     }
 
+    @Override
+    public void setDefaultDisplay(DisplayContentInfo displayContentInfo) {
+        mDefaultDisplay = displayContentInfo.getDisplay();
+        mDefaultDisplayRotation = displayContentInfo.getDisplayRotation();
+        mDefaultDisplayPolicy = mDefaultDisplayRotation.getDisplayPolicy();
+        mDefaultOrientationListener = mDefaultDisplayRotation.getOrientationListener();
+    }
+
     /** {@inheritDoc} */
     @Override
     public void init(Context context, IWindowManager windowManager,
@@ -2001,6 +1834,7 @@
         mWindowManagerFuncs = windowManagerFuncs;
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+        mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
         mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
@@ -2047,10 +1881,6 @@
 
         mHandler = new PolicyHandler();
         mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
-        mOrientationListener = new MyOrientationListener(mContext, mHandler);
-        try {
-            mOrientationListener.setCurrentRotation(windowManager.getDefaultDisplayRotation());
-        } catch (RemoteException ex) { }
         mSettingsObserver = new SettingsObserver(mHandler);
         mSettingsObserver.observe();
         mShortcutManager = new ShortcutManager(context);
@@ -2081,20 +1911,6 @@
         mPowerKeyWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                 "PhoneWindowManager.mPowerKeyWakeLock");
         mEnableShiftMenuBugReports = "1".equals(SystemProperties.get("ro.debuggable"));
-        mSupportAutoRotation = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_supportAutoRotation);
-        mLidOpenRotation = readRotation(
-                com.android.internal.R.integer.config_lidOpenRotation);
-        mCarDockRotation = readRotation(
-                com.android.internal.R.integer.config_carDockRotation);
-        mDeskDockRotation = readRotation(
-                com.android.internal.R.integer.config_deskDockRotation);
-        mUndockedHdmiRotation = readRotation(
-                com.android.internal.R.integer.config_undockedHdmiRotation);
-        mCarDockEnablesAccelerometer = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_carDockEnablesAccelerometer);
-        mDeskDockEnablesAccelerometer = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_deskDockEnablesAccelerometer);
         mLidKeyboardAccessibility = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_lidKeyboardAccessibility);
         mLidNavigationAccessibility = mContext.getResources().getInteger(
@@ -2168,8 +1984,8 @@
         Intent intent = context.registerReceiver(mDockReceiver, filter);
         if (intent != null) {
             // Retrieve current sticky dock event broadcast.
-            mDockMode = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
-                    Intent.EXTRA_DOCK_STATE_UNDOCKED);
+            mDefaultDisplayPolicy.setDockMode(intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+                    Intent.EXTRA_DOCK_STATE_UNDOCKED));
         }
 
         // register for dream-related broadcasts
@@ -2223,11 +2039,11 @@
                     }
                     @Override
                     public void onDown() {
-                        mOrientationListener.onTouchStart();
+                        mDefaultOrientationListener.onTouchStart();
                     }
                     @Override
                     public void onUpOrCancel() {
-                        mOrientationListener.onTouchEnd();
+                        mDefaultOrientationListener.onTouchEnd();
                     }
                     @Override
                     public void onMouseHoverAtTop() {
@@ -2334,110 +2150,12 @@
                 com.android.internal.R.integer.config_navBarOpacityMode);
     }
 
-    @Override
-    public void setInitialDisplaySize(Display display, int width, int height, int density) {
-        // This method might be called before the policy has been fully initialized
-        // or for other displays we don't care about.
-        // TODO(multi-display): Define policy for secondary displays.
-        if (mContext == null || display.getDisplayId() != DEFAULT_DISPLAY) {
-            return;
-        }
-        mDisplay = display;
-
-        final Resources res = mContext.getResources();
-        int shortSize, longSize;
-        if (width > height) {
-            shortSize = height;
-            longSize = width;
-            mLandscapeRotation = Surface.ROTATION_0;
-            mSeascapeRotation = Surface.ROTATION_180;
-            if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
-                mPortraitRotation = Surface.ROTATION_90;
-                mUpsideDownRotation = Surface.ROTATION_270;
-            } else {
-                mPortraitRotation = Surface.ROTATION_270;
-                mUpsideDownRotation = Surface.ROTATION_90;
-            }
-        } else {
-            shortSize = width;
-            longSize = height;
-            mPortraitRotation = Surface.ROTATION_0;
-            mUpsideDownRotation = Surface.ROTATION_180;
-            if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
-                mLandscapeRotation = Surface.ROTATION_270;
-                mSeascapeRotation = Surface.ROTATION_90;
-            } else {
-                mLandscapeRotation = Surface.ROTATION_90;
-                mSeascapeRotation = Surface.ROTATION_270;
-            }
-        }
-
-        // SystemUI (status bar) layout policy
-        int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / density;
-        int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / density;
-
-        // Allow the navigation bar to move on non-square small devices (phones).
-        mNavigationBarCanMove = width != height && shortSizeDp < 600;
-
-        mHasNavigationBar = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
-
-        // Allow a system property to override this. Used by the emulator.
-        // See also hasNavigationBar().
-        String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
-        if ("1".equals(navBarOverride)) {
-            mHasNavigationBar = false;
-        } else if ("0".equals(navBarOverride)) {
-            mHasNavigationBar = true;
-        }
-
-        // For demo purposes, allow the rotation of the HDMI display to be controlled.
-        // By default, HDMI locks rotation to landscape.
-        if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
-            mDemoHdmiRotation = mPortraitRotation;
-        } else {
-            mDemoHdmiRotation = mLandscapeRotation;
-        }
-        mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
-
-        // For demo purposes, allow the rotation of the remote display to be controlled.
-        // By default, remote display locks rotation to landscape.
-        if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) {
-            mDemoRotation = mPortraitRotation;
-        } else {
-            mDemoRotation = mLandscapeRotation;
-        }
-        mDemoRotationLock = SystemProperties.getBoolean(
-                "persist.demo.rotationlock", false);
-
-        // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
-        // http://developer.android.com/guide/practices/screens_support.html#range
-        // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen
-        // so if the orientation is forced, we need to respect that no matter what.
-        final boolean isCar = mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_AUTOMOTIVE);
-        // For TV, it's usually 960dp x 540dp, ignore the size limitation.
-        // so if the orientation is forced, we need to respect that no matter what.
-        final boolean isTv = mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_LEANBACK);
-        mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) &&
-                res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
-                // For debug purposes the next line turns this feature off with:
-                // $ adb shell setprop config.override_forced_orient true
-                // $ adb shell wm size reset
-                !"true".equals(SystemProperties.get("config.override_forced_orient"));
-    }
-
     /**
      * @return whether the navigation bar can be hidden, e.g. the device has a
      *         navigation bar and touch exploration is not enabled
      */
     private boolean canHideNavigationBar() {
-        return mHasNavigationBar;
-    }
-
-    @Override
-    public boolean isDefaultOrientationForced() {
-        return mForceDefaultOrientation;
+        return mDefaultDisplayPolicy.hasNavigationBar();
     }
 
     public void updateSettings() {
@@ -2466,15 +2184,6 @@
                     .getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) {
                 mRingerToggleChord = Settings.Secure.VOLUME_HUSH_OFF;
             }
-            // Configure rotation suggestions.
-            int showRotationSuggestions = Settings.Secure.getIntForUser(resolver,
-                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
-                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
-                    UserHandle.USER_CURRENT);
-            if (mShowRotationSuggestions != showRotationSuggestions) {
-                mShowRotationSuggestions = showRotationSuggestions;
-                updateOrientationListenerLp(); // Enable, disable the orientation listener
-            }
 
             // Configure wake gesture.
             boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
@@ -2485,24 +2194,6 @@
                 updateWakeGestureListenerLp();
             }
 
-            // Configure rotation lock.
-            int userRotation = Settings.System.getIntForUser(resolver,
-                    Settings.System.USER_ROTATION, Surface.ROTATION_0,
-                    UserHandle.USER_CURRENT);
-            if (mUserRotation != userRotation) {
-                mUserRotation = userRotation;
-                updateRotation = true;
-            }
-            int userRotationMode = Settings.System.getIntForUser(resolver,
-                    Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 ?
-                            WindowManagerPolicy.USER_ROTATION_FREE :
-                                    WindowManagerPolicy.USER_ROTATION_LOCKED;
-            if (mUserRotationMode != userRotationMode) {
-                mUserRotationMode = userRotationMode;
-                updateRotation = true;
-                updateOrientationListenerLp();
-            }
-
             if (mSystemReady) {
                 int pointerLocation = Settings.System.getIntForUser(resolver,
                         Settings.System.POINTER_LOCATION, 0, UserHandle.USER_CURRENT);
@@ -2543,8 +2234,8 @@
     }
 
     private boolean shouldEnableWakeGestureLp() {
-        return mWakeGestureEnabledSetting && !mAwake
-                && (!mLidControlsSleep || mLidState != LID_CLOSED)
+        return mWakeGestureEnabledSetting && !mDefaultDisplayPolicy.isAwake()
+                && (!mLidControlsSleep || mDefaultDisplayPolicy.getLidState() != LID_CLOSED)
                 && mWakeGestureListener.isSupported();
     }
 
@@ -2584,24 +2275,6 @@
         }
     }
 
-    private int readRotation(int resID) {
-        try {
-            int rotation = mContext.getResources().getInteger(resID);
-            switch (rotation) {
-                case 0:
-                    return Surface.ROTATION_0;
-                case 90:
-                    return Surface.ROTATION_90;
-                case 180:
-                    return Surface.ROTATION_180;
-                case 270:
-                    return Surface.ROTATION_270;
-            }
-        } catch (Resources.NotFoundException e) {
-            // fall through
-        }
-        return -1;
-    }
 
     /** {@inheritDoc} */
     @Override
@@ -2830,7 +2503,7 @@
     }
 
     void readLidState() {
-        mLidState = mWindowManagerFuncs.getLidState();
+        mDefaultDisplayPolicy.setLidState(mWindowManagerFuncs.getLidState());
     }
 
     private void readCameraLensCoverState() {
@@ -2838,11 +2511,12 @@
     }
 
     private boolean isHidden(int accessibilityMode) {
+        final int lidState = mDefaultDisplayPolicy.getLidState();
         switch (accessibilityMode) {
             case 1:
-                return mLidState == LID_CLOSED;
+                return lidState == LID_CLOSED;
             case 2:
-                return mLidState == LID_OPEN;
+                return lidState == LID_OPEN;
             default:
                 return false;
         }
@@ -2874,53 +2548,62 @@
     }
 
     @Override
-    public void onOverlayChangedLw() {
-        onConfigurationChanged();
+    public void onOverlayChangedLw(DisplayContentInfo displayContentInfo) {
+        onConfigurationChanged(displayContentInfo);
     }
 
     @Override
-    public void onConfigurationChanged() {
+    public void onConfigurationChanged(DisplayContentInfo displayContentInfo) {
+        final DisplayRotation displayRotation = displayContentInfo.getDisplayRotation();
         // TODO(multi-display): Define policy for secondary displays.
-        Context uiContext = getSystemUiContext();
-        final Resources res = uiContext.getResources();
+        if (!displayRotation.isDefaultDisplay) {
+            return;
+        }
 
-        mStatusBarHeightForRotation[mPortraitRotation] =
-                mStatusBarHeightForRotation[mUpsideDownRotation] = res.getDimensionPixelSize(
+        final Context uiContext = getSystemUiContext();
+        final Resources res = uiContext.getResources();
+        final int portraitRotation = displayRotation.getPortraitRotation();
+        final int upsideDownRotation = displayRotation.getUpsideDownRotation();
+        final int landscapeRotation = displayRotation.getLandscapeRotation();
+        final int seascapeRotation = displayRotation.getSeascapeRotation();
+
+        mStatusBarHeightForRotation[portraitRotation] =
+                mStatusBarHeightForRotation[upsideDownRotation] = res.getDimensionPixelSize(
                                 com.android.internal.R.dimen.status_bar_height_portrait);
-        mStatusBarHeightForRotation[mLandscapeRotation] =
-                mStatusBarHeightForRotation[mSeascapeRotation] = res.getDimensionPixelSize(
+        mStatusBarHeightForRotation[landscapeRotation] =
+                mStatusBarHeightForRotation[seascapeRotation] = res.getDimensionPixelSize(
                         com.android.internal.R.dimen.status_bar_height_landscape);
 
         // Height of the navigation bar when presented horizontally at bottom
-        mNavigationBarHeightForRotationDefault[mPortraitRotation] =
-        mNavigationBarHeightForRotationDefault[mUpsideDownRotation] =
+        mNavigationBarHeightForRotationDefault[portraitRotation] =
+        mNavigationBarHeightForRotationDefault[upsideDownRotation] =
                 res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
-        mNavigationBarHeightForRotationDefault[mLandscapeRotation] =
-        mNavigationBarHeightForRotationDefault[mSeascapeRotation] = res.getDimensionPixelSize(
+        mNavigationBarHeightForRotationDefault[landscapeRotation] =
+        mNavigationBarHeightForRotationDefault[seascapeRotation] = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.navigation_bar_height_landscape);
 
         // Width of the navigation bar when presented vertically along one side
-        mNavigationBarWidthForRotationDefault[mPortraitRotation] =
-        mNavigationBarWidthForRotationDefault[mUpsideDownRotation] =
-        mNavigationBarWidthForRotationDefault[mLandscapeRotation] =
-        mNavigationBarWidthForRotationDefault[mSeascapeRotation] =
+        mNavigationBarWidthForRotationDefault[portraitRotation] =
+        mNavigationBarWidthForRotationDefault[upsideDownRotation] =
+        mNavigationBarWidthForRotationDefault[landscapeRotation] =
+        mNavigationBarWidthForRotationDefault[seascapeRotation] =
                 res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
 
         if (ALTERNATE_CAR_MODE_NAV_SIZE) {
             // Height of the navigation bar when presented horizontally at bottom
-            mNavigationBarHeightForRotationInCarMode[mPortraitRotation] =
-            mNavigationBarHeightForRotationInCarMode[mUpsideDownRotation] =
+            mNavigationBarHeightForRotationInCarMode[portraitRotation] =
+            mNavigationBarHeightForRotationInCarMode[upsideDownRotation] =
                     res.getDimensionPixelSize(
                             com.android.internal.R.dimen.navigation_bar_height_car_mode);
-            mNavigationBarHeightForRotationInCarMode[mLandscapeRotation] =
-            mNavigationBarHeightForRotationInCarMode[mSeascapeRotation] = res.getDimensionPixelSize(
+            mNavigationBarHeightForRotationInCarMode[landscapeRotation] =
+            mNavigationBarHeightForRotationInCarMode[seascapeRotation] = res.getDimensionPixelSize(
                     com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode);
 
             // Width of the navigation bar when presented vertically along one side
-            mNavigationBarWidthForRotationInCarMode[mPortraitRotation] =
-            mNavigationBarWidthForRotationInCarMode[mUpsideDownRotation] =
-            mNavigationBarWidthForRotationInCarMode[mLandscapeRotation] =
-            mNavigationBarWidthForRotationInCarMode[mSeascapeRotation] =
+            mNavigationBarWidthForRotationInCarMode[portraitRotation] =
+            mNavigationBarWidthForRotationInCarMode[upsideDownRotation] =
+            mNavigationBarWidthForRotationInCarMode[landscapeRotation] =
+            mNavigationBarWidthForRotationInCarMode[seascapeRotation] =
                     res.getDimensionPixelSize(
                             com.android.internal.R.dimen.navigation_bar_width_car_mode);
         }
@@ -2949,10 +2632,10 @@
             int displayId, DisplayCutout displayCutout) {
         int width = fullWidth;
         // TODO(multi-display): Support navigation bar on secondary displays.
-        if (displayId == DEFAULT_DISPLAY && mHasNavigationBar) {
+        if (displayId == DEFAULT_DISPLAY && mDefaultDisplayPolicy.hasNavigationBar()) {
             // For a basic navigation bar, when we are in landscape mode we place
             // the navigation bar to the side.
-            if (mNavigationBarCanMove && fullWidth > fullHeight) {
+            if (mDefaultDisplayPolicy.navigationBarCanMove() && fullWidth > fullHeight) {
                 width -= getNavigationBarWidth(rotation, uiMode);
             }
         }
@@ -2975,10 +2658,10 @@
             int displayId, DisplayCutout displayCutout) {
         int height = fullHeight;
         // TODO(multi-display): Support navigation bar on secondary displays.
-        if (displayId == DEFAULT_DISPLAY && mHasNavigationBar) {
+        if (displayId == DEFAULT_DISPLAY && mDefaultDisplayPolicy.hasNavigationBar()) {
             // For a basic navigation bar, when we are in portrait mode we place
             // the navigation bar to the bottom.
-            if (!mNavigationBarCanMove || fullWidth < fullHeight) {
+            if (!mDefaultDisplayPolicy.navigationBarCanMove() || fullWidth < fullHeight) {
                 height -= getNavigationBarHeight(rotation, uiMode);
             }
         }
@@ -3066,8 +2749,8 @@
         // In that case, we want to continue hiding the IME until the windows have completed
         // drawing. This way, we know that the IME can be safely shown since the other windows are
         // now shown.
-        final boolean hideIme =
-                win.isInputMethodWindow() && (mAodShowing || !mWindowManagerDrawComplete);
+        final boolean hideIme = win.isInputMethodWindow()
+                && (mAodShowing || !mDefaultDisplayPolicy.isWindowManagerDrawComplete());
         return (keyguardLocked && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY)
                 || hideDockDivider || hideIme;
     }
@@ -3330,6 +3013,9 @@
             mNavigationBar = null;
             mNavigationBarController.setWindow(null);
         }
+        if (mLastFocusedWindow == win) {
+            mLastFocusedWindow = null;
+        }
         mScreenDecorWindows.remove(win);
     }
 
@@ -3442,7 +3128,7 @@
     @Override
     public void selectRotationAnimationLw(int anim[]) {
         // If the screen is off or non-interactive, force a jumpcut.
-        final boolean forceJumpcut = !mScreenOnFully || !okToAnimate();
+        final boolean forceJumpcut = !mDefaultDisplayPolicy.isScreenOnFully() || !okToAnimate();
         if (PRINT_ANIM) Slog.i(TAG, "selectRotationAnimation mTopFullscreen="
                 + mTopFullscreenOpaqueWindowState + " rotationAnimation="
                 + (mTopFullscreenOpaqueWindowState == null ?
@@ -3849,7 +3535,7 @@
             // If the device is in VR mode and keys are "internal" (e.g. on the side of the
             // device), then drop the volume keys and don't forward it to the application/dispatch
             // the audio event.
-            if (mPersistentVrModeEnabled) {
+            if (mDefaultDisplayPolicy.isPersistentVrModeEnabled()) {
                 final InputDevice d = event.getDevice();
                 if (d != null && !d.isExternal()) {
                     return -1;
@@ -4247,7 +3933,8 @@
     }
 
     private void launchAssistLongPressAction() {
-        performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+        performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+                "Assist - Long Press");
         sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);
 
         // launch the search activity
@@ -4389,17 +4076,9 @@
             if (isKeyguardShowingAndNotOccluded()) {
                 // don't launch home if keyguard showing
                 return;
-            } else if (mKeyguardOccluded && mKeyguardDelegate.isShowing()) {
-                mKeyguardDelegate.dismiss(new KeyguardDismissCallback() {
-                    @Override
-                    public void onDismissSucceeded() throws RemoteException {
-                        mHandler.post(() -> {
-                            startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
-                        });
-                    }
-                }, null /* message */);
-                return;
-            } else if (!mKeyguardOccluded && mKeyguardDelegate.isInputRestricted()) {
+            }
+
+            if (!mKeyguardOccluded && mKeyguardDelegate.isInputRestricted()) {
                 // when in keyguard restricted mode, must first verify unlock
                 // before launching home
                 mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
@@ -4533,16 +4212,15 @@
 
     @Override
     // TODO: Should probably be moved into DisplayFrames.
-    public boolean getLayoutHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
-            DisplayFrames displayFrames, Rect outFrame, Rect outContentInsets, Rect outStableInsets,
+    public boolean getLayoutHintLw(LayoutParams attrs, Rect taskBounds,
+            DisplayFrames displayFrames, boolean floatingStack, Rect outFrame,
+            Rect outContentInsets, Rect outStableInsets,
             Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
         final int fl = PolicyControl.getWindowFlags(null, attrs);
         final int pfl = attrs.privateFlags;
         final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs);
         final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs);
         final int displayRotation = displayFrames.mRotation;
-        final int displayWidth = displayFrames.mDisplayWidth;
-        final int displayHeight = displayFrames.mDisplayHeight;
 
         final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl);
         if (useOutsets) {
@@ -4566,45 +4244,40 @@
         final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
 
         if (layoutInScreenAndInsetDecor && !screenDecor) {
-            int availRight, availBottom;
             if (canHideNavigationBar() &&
                     (sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
                 outFrame.set(displayFrames.mUnrestricted);
-                availRight = displayFrames.mUnrestricted.right;
-                availBottom = displayFrames.mUnrestricted.bottom;
             } else {
                 outFrame.set(displayFrames.mRestricted);
-                availRight = displayFrames.mRestricted.right;
-                availBottom = displayFrames.mRestricted.bottom;
             }
-            outStableInsets.set(displayFrames.mStable.left, displayFrames.mStable.top,
-                    availRight - displayFrames.mStable.right,
-                    availBottom - displayFrames.mStable.bottom);
 
-            if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
+            final Rect sf;
+            if (floatingStack) {
+                sf = null;
+            } else {
+                sf = displayFrames.mStable;
+            }
+
+            final Rect cf;
+            if (floatingStack) {
+                cf = null;
+            } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
                 if ((fl & FLAG_FULLSCREEN) != 0) {
-                    outContentInsets.set(displayFrames.mStableFullscreen.left,
-                            displayFrames.mStableFullscreen.top,
-                            availRight - displayFrames.mStableFullscreen.right,
-                            availBottom - displayFrames.mStableFullscreen.bottom);
+                    cf = displayFrames.mStableFullscreen;
                 } else {
-                    outContentInsets.set(outStableInsets);
+                    cf = displayFrames.mStable;
                 }
             } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) {
-                outContentInsets.setEmpty();
+                cf = displayFrames.mOverscan;
             } else {
-                outContentInsets.set(displayFrames.mCurrent.left, displayFrames.mCurrent.top,
-                        availRight - displayFrames.mCurrent.right,
-                        availBottom - displayFrames.mCurrent.bottom);
+                cf = displayFrames.mCurrent;
             }
 
             if (taskBounds != null) {
-                calculateRelevantTaskInsets(taskBounds, outContentInsets,
-                        displayWidth, displayHeight);
-                calculateRelevantTaskInsets(taskBounds, outStableInsets,
-                        displayWidth, displayHeight);
                 outFrame.intersect(taskBounds);
             }
+            InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets);
+            InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets);
             outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame)
                     .getDisplayCutout());
             return mForceShowSystemBars;
@@ -4625,22 +4298,6 @@
         }
     }
 
-    /**
-     * For any given task bounds, the insets relevant for these bounds given the insets relevant
-     * for the entire display.
-     */
-    private void calculateRelevantTaskInsets(Rect taskBounds, Rect inOutInsets, int displayWidth,
-            int displayHeight) {
-        mTmpRect.set(0, 0, displayWidth, displayHeight);
-        mTmpRect.inset(inOutInsets);
-        mTmpRect.intersect(taskBounds);
-        int leftInset = mTmpRect.left - taskBounds.left;
-        int topInset = mTmpRect.top - taskBounds.top;
-        int rightInset = taskBounds.right - mTmpRect.right;
-        int bottomInset = taskBounds.bottom - mTmpRect.bottom;
-        inOutInsets.set(leftInset, topInset, rightInset, bottomInset);
-    }
-
     private boolean shouldUseOutsets(WindowManager.LayoutParams attrs, int fl) {
         return attrs.type == TYPE_WALLPAPER || (fl & (WindowManager.LayoutParams.FLAG_FULLSCREEN
                 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN)) != 0;
@@ -4656,17 +4313,8 @@
         mDockLayer = 0x10000000;
         mStatusBarLayer = -1;
 
-        // start with the current dock rect, which will be (0,0,displayWidth,displayHeight)
-        final Rect pf = mTmpParentFrame;
-        final Rect df = mTmpDisplayFrame;
-        final Rect of = mTmpOverscanFrame;
-        final Rect vf = mTmpVisibleFrame;
-        final Rect dcf = mTmpDecorFrame;
-        vf.set(displayFrames.mDock);
-        of.set(displayFrames.mDock);
-        df.set(displayFrames.mDock);
-        pf.set(displayFrames.mDock);
-        dcf.setEmpty();  // Decor frame N/A for system bars.
+        mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
+        mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
 
         if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
             // For purposes of putting out fake window up to steal focus, we will
@@ -4684,8 +4332,7 @@
                 navTranslucent &= areTranslucentBarsAllowed();
             }
             boolean statusBarExpandedNotKeyguard = !isKeyguardShowing && mStatusBar != null
-                    && mStatusBar.getAttrs().height == MATCH_PARENT
-                    && mStatusBar.getAttrs().width == MATCH_PARENT;
+                    && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_STATUS_BAR_EXPANDED) != 0;
 
             // When the navigation bar isn't visible, we put up a fake input window to catch all
             // touch events. This way we can detect when the user presses anywhere to bring back the
@@ -4699,7 +4346,8 @@
             } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
                 mInputConsumer = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(),
                         INPUT_CONSUMER_NAVIGATION,
-                        (channel, looper) -> new HideNavInputEventReceiver(channel, looper));
+                        (channel, looper) -> new HideNavInputEventReceiver(channel, looper),
+                        displayFrames.mDisplayId);
                 // As long as mInputConsumer is active, hover events are not dispatched to the app
                 // and the pointer icon is likely to become stale. Hide it to avoid confusion.
                 InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
@@ -4709,16 +4357,15 @@
             // be hidden (because of the screen aspect ratio), then take that into account.
             navVisible |= !canHideNavigationBar();
 
-            boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, dcf,
-                    navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard);
+            boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible,
+                    navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard);
             if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
-            updateSysUiVisibility |= layoutStatusBar(
-                    displayFrames, pf, df, of, vf, dcf, sysui, isKeyguardShowing);
+            updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, isKeyguardShowing);
             if (updateSysUiVisibility) {
                 updateSystemUiVisibilityLw();
             }
         }
-        layoutScreenDecorWindows(displayFrames, pf, df, dcf);
+        layoutScreenDecorWindows(displayFrames);
 
         if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
             // Make sure that the zone we're avoiding for the cutout is at least as tall as the
@@ -4729,11 +4376,18 @@
         }
     }
 
-    private void layoutScreenDecorWindows(DisplayFrames displayFrames, Rect pf, Rect df, Rect dcf) {
+    private void layoutScreenDecorWindows(DisplayFrames displayFrames) {
         if (mScreenDecorWindows.isEmpty()) {
             return;
         }
 
+        mTmpRect.setEmpty();
+        mWindowFrames.setFrames(displayFrames.mDock /* parentFrame */,
+                displayFrames.mDock /* displayFrame */, displayFrames.mDock /* overscanFrame */,
+                displayFrames.mDock /* contentFrame */, displayFrames.mDock /* visibleFrame */,
+                mTmpRect /* decorFrame */, displayFrames.mDock /* stableFrame */,
+                displayFrames.mDock /* outsetFrame */);
+
         final int displayId = displayFrames.mDisplayId;
         final Rect dockFrame = displayFrames.mDock;
         final int displayHeight = displayFrames.mDisplayHeight;
@@ -4746,10 +4400,7 @@
                 continue;
             }
 
-            w.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, df /* overlayFrame */,
-                    df /* contentFrame */, df /* visibleFrame */, dcf /* decorFrame */,
-                    df /* stableFrame */, df /* outsetFrame */, displayFrames.mDisplayCutout,
-                    false /* parentFrameWasClippedByDisplayCutout */);
+            w.computeFrameLw(mWindowFrames);
             final Rect frame = w.getFrameLw();
 
             if (frame.left <= 0 && frame.top <= 0) {
@@ -4794,25 +4445,24 @@
         displayFrames.mRestrictedOverscan.set(dockFrame);
     }
 
-    private boolean layoutStatusBar(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect vf,
-            Rect dcf, int sysui, boolean isKeyguardShowing) {
+    private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui,
+            boolean isKeyguardShowing) {
         // decide where the status bar goes ahead of time
         if (mStatusBar == null) {
             return false;
         }
         // apply any navigation bar insets
-        of.set(displayFrames.mUnrestricted);
-        df.set(displayFrames.mUnrestricted);
-        pf.set(displayFrames.mUnrestricted);
-        vf.set(displayFrames.mStable);
+        mTmpRect.setEmpty();
+        mWindowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */,
+                displayFrames.mUnrestricted /* displayFrame */,
+                displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
+                displayFrames.mStable /* visibleFrame */, mTmpRect /* decorFrame */,
+                displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
 
         mStatusBarLayer = mStatusBar.getSurfaceLayer();
 
         // Let the status bar determine its size.
-        mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
-                vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
-                dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */,
-                displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);
+        mStatusBar.computeFrameLw(mWindowFrames);
 
         // For layout, the status bar is always at the top with our fixed height.
         displayFrames.mStable.top = displayFrames.mUnrestricted.top
@@ -4859,12 +4509,14 @@
         return mStatusBarController.checkHiddenLw();
     }
 
-    private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf,
-            boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,
+    private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible,
+            boolean navTranslucent, boolean navAllowedHidden,
             boolean statusBarExpandedNotKeyguard) {
         if (mNavigationBar == null) {
             return false;
         }
+
+        final Rect navigationFrame = mWindowFrames.mParentFrame;
         boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
         // Force the navigation bar to its appropriate place and size. We need to do this directly,
         // instead of relying on it to bubble up from the nav bar, because this needs to change
@@ -4883,7 +4535,7 @@
             // It's a system nav bar or a portrait screen; nav bar goes on bottom.
             final int top = cutoutSafeUnrestricted.bottom
                     - getNavigationBarHeight(rotation, uiMode);
-            mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom);
+            navigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom);
             displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
             if (transientNavBarShowing) {
                 mNavigationBarController.setBarShowingLw(true);
@@ -4906,7 +4558,7 @@
             // Landscape screen; nav bar goes to the right.
             final int left = cutoutSafeUnrestricted.right
                     - getNavigationBarWidth(rotation, uiMode);
-            mTmpNavigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
+            navigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
             displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
             if (transientNavBarShowing) {
                 mNavigationBarController.setBarShowingLw(true);
@@ -4929,7 +4581,7 @@
             // Seascape screen; nav bar goes to the left.
             final int right = cutoutSafeUnrestricted.left
                     + getNavigationBarWidth(rotation, uiMode);
-            mTmpNavigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight);
+            navigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight);
             displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
             if (transientNavBarShowing) {
                 mNavigationBarController.setBarShowingLw(true);
@@ -4957,19 +4609,24 @@
         displayFrames.mContent.set(dockFrame);
         mStatusBarLayer = mNavigationBar.getSurfaceLayer();
         // And compute the final frame.
-        mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
-                mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, mTmpNavigationFrame, dcf,
-                mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe,
-                displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);
+        mTmpRect.setEmpty();
+        mWindowFrames.setFrames(navigationFrame /* parentFrame */,
+                navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */,
+                displayFrames.mDisplayCutoutSafe /* contentFrame */,
+                navigationFrame /* visibleFrame */, mTmpRect /* decorFrame */,
+                navigationFrame /* stableFrame */,
+                displayFrames.mDisplayCutoutSafe /* outsetFrame */);
+
+        mNavigationBar.computeFrameLw(mWindowFrames);
         mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());
 
-        if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
+        if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
         return mNavigationBarController.checkHiddenLw();
     }
 
     @NavigationBarPosition
     private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
-        if (mNavigationBarCanMove && displayWidth > displayHeight) {
+        if (mDefaultDisplayPolicy.navigationBarCanMove() && displayWidth > displayHeight) {
             if (displayRotation == Surface.ROTATION_270) {
                 return NAV_BAR_LEFT;
             } else {
@@ -5091,17 +4748,19 @@
         final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);
         final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
 
-        final Rect pf = mTmpParentFrame;
-        final Rect df = mTmpDisplayFrame;
-        final Rect of = mTmpOverscanFrame;
-        final Rect cf = mTmpContentFrame;
-        final Rect vf = mTmpVisibleFrame;
-        final Rect dcf = mTmpDecorFrame;
-        final Rect sf = mTmpStableFrame;
-        Rect osf = null;
+        final Rect pf = mWindowFrames.mParentFrame;
+        final Rect df = mWindowFrames.mDisplayFrame;
+        final Rect of = mWindowFrames.mOverscanFrame;
+        final Rect cf = mWindowFrames.mContentFrame;
+        final Rect vf = mWindowFrames.mVisibleFrame;
+        final Rect dcf = mWindowFrames.mDecorFrame;
+        final Rect sf = mWindowFrames.mStableFrame;
         dcf.setEmpty();
+        mWindowFrames.mOutsetFrame.setEmpty();
+        mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
+        mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
 
-        final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar
+        final boolean hasNavBar = (isDefaultDisplay && mDefaultDisplayPolicy.hasNavigationBar()
                 && mNavigationBar != null && mNavigationBar.isVisibleLw());
 
         final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
@@ -5419,7 +5078,6 @@
             }
         }
 
-        boolean parentFrameWasClippedByDisplayCutout = false;
         final int cutoutMode = attrs.layoutInDisplayCutoutMode;
         final boolean attachedInParent = attached != null && !layoutInScreen;
         final boolean requestedHideNavigation =
@@ -5470,7 +5128,7 @@
             if (!attachedInParent && !floatingInScreenWindow) {
                 mTmpRect.set(pf);
                 pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
-                parentFrameWasClippedByDisplayCutout |= !mTmpRect.equals(pf);
+                mWindowFrames.setParentFrameWasClippedByDisplayCutout(!mTmpRect.equals(pf));
             }
             // Make sure that NO_LIMITS windows clipped to the display don't extend under the
             // cutout.
@@ -5498,7 +5156,7 @@
         // apply the outsets to floating dialogs, because they wouldn't make sense there.
         final boolean useOutsets = shouldUseOutsets(attrs, fl);
         if (isDefaultDisplay && useOutsets) {
-            osf = mTmpOutsetFrame;
+            final Rect osf = mWindowFrames.mOutsetFrame;
             osf.set(cf.left, cf.top, cf.right, cf.bottom);
             int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
             if (outset > 0) {
@@ -5526,10 +5184,9 @@
                 + " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
                 + " dcf=" + dcf.toShortString()
                 + " sf=" + sf.toShortString()
-                + " osf=" + (osf == null ? "null" : osf.toShortString()));
+                + " osf=" + mWindowFrames.mOutsetFrame.toShortString());
 
-        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf, displayFrames.mDisplayCutout,
-                parentFrameWasClippedByDisplayCutout);
+        win.computeFrameLw(mWindowFrames);
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
         if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
@@ -5688,7 +5345,7 @@
         }
 
         // Take note if a window wants to acquire a sleep token.
-        if (win.isVisibleLw() && (attrs.privateFlags & PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN) != 0
+        if ((attrs.privateFlags & PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN) != 0
                 && win.canAcquireSleepToken()) {
             mWindowSleepTokenNeeded = true;
         }
@@ -5744,9 +5401,8 @@
                 mStatusBarController.setShowTransparent(true /* transparent */);
             }
 
-            WindowManager.LayoutParams statusBarAttrs = mStatusBar.getAttrs();
-            boolean statusBarExpanded = statusBarAttrs.height == MATCH_PARENT
-                    && statusBarAttrs.width == MATCH_PARENT;
+            boolean statusBarExpanded =
+                    (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_STATUS_BAR_EXPANDED) != 0;
             boolean topAppHidesStatusBar = topAppHidesStatusBar();
             if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent
                     || statusBarExpanded) {
@@ -5899,7 +5555,8 @@
     @Override
     public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
         mFocusedWindow = newFocus;
-        if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) {
+        mLastFocusedWindow = lastFocus;
+        if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
             // If the navigation bar has been hidden or shown, we need to do another
             // layout pass to update that window.
             return FINISH_LAYOUT_REDO_LAYOUT;
@@ -5912,11 +5569,11 @@
     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
         // lid changed state
         final int newLidState = lidOpen ? LID_OPEN : LID_CLOSED;
-        if (newLidState == mLidState) {
+        if (newLidState == mDefaultDisplayPolicy.getLidState()) {
             return;
         }
 
-        mLidState = newLidState;
+        mDefaultDisplayPolicy.setLidState(newLidState);
         applyLidSwitchState();
         updateRotation(true);
 
@@ -5951,17 +5608,6 @@
         mCameraLensCoverState = lensCoverState;
     }
 
-    void setHdmiPlugged(boolean plugged) {
-        if (mHdmiPlugged != plugged) {
-            mHdmiPlugged = plugged;
-            updateRotation(true, true);
-            Intent intent = new Intent(ACTION_HDMI_PLUGGED);
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged);
-            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
-        }
-    }
-
     void initializeHdmiState() {
         final int oldMask = StrictMode.allowThreadDiskReadsMask();
         try {
@@ -6001,8 +5647,7 @@
         }
         // This dance forces the code in setHdmiPlugged to run.
         // Always do this so the sticky intent is stuck (to false) if there is no hdmi.
-        mHdmiPlugged = !plugged;
-        setHdmiPlugged(!mHdmiPlugged);
+        mDefaultDisplayPolicy.setHdmiPlugged(plugged, true /* force */);
     }
 
 
@@ -6393,7 +6038,8 @@
         }
 
         if (useHapticFeedback) {
-            performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
+            performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false,
+                    "Virtual Key - Press");
         }
 
         if (isWakeKey) {
@@ -6442,16 +6088,6 @@
     }
 
     /**
-     * Notify the StatusBar that system rotation suggestion has changed.
-     */
-    private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
-        StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
-        if (statusBar != null) {
-            statusBar.onProposedRotationChanged(rotation, isValid);
-        }
-    }
-
-    /**
      * Returns true if the key can have global actions attached to it.
      * We reserve all power management keys for the system since they require
      * very careful handling.
@@ -6480,7 +6116,7 @@
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_DOWN:
             case KeyEvent.KEYCODE_VOLUME_MUTE:
-                return mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+                return mDefaultDisplayPolicy.getDockMode() != Intent.EXTRA_DOCK_STATE_UNDOCKED;
 
             // ignore media and camera keys
             case KeyEvent.KEYCODE_MUTE:
@@ -6528,7 +6164,8 @@
     }
 
     private boolean shouldDispatchInputWhenNonInteractive(KeyEvent event) {
-        final boolean displayOff = (mDisplay == null || mDisplay.getState() == STATE_OFF);
+        final boolean displayOff = (mDefaultDisplay == null
+                || mDefaultDisplay.getState() == STATE_OFF);
 
         if (displayOff && !mHasFeatureWatch) {
             return false;
@@ -6678,8 +6315,8 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) {
-                mDockMode = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
-                        Intent.EXTRA_DOCK_STATE_UNDOCKED);
+                mDefaultDisplayPolicy.setDockMode(intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+                        Intent.EXTRA_DOCK_STATE_UNDOCKED));
             } else {
                 try {
                     IUiModeManager uiModeService = IUiModeManager.Stub.asInterface(
@@ -6689,9 +6326,7 @@
                 }
             }
             updateRotation(true);
-            synchronized (mLock) {
-                updateOrientationListenerLp();
-            }
+            mDefaultDisplayRotation.updateOrientationListener();
         }
     };
 
@@ -6719,6 +6354,7 @@
                 // and then updates our own bookkeeping based on the now-
                 // current user.
                 mSettingsObserver.onChange(false);
+                mDefaultDisplayRotation.onUserSwitch();
 
                 // force a re-application of focused window sysui visibility.
                 // the window may never have been shown for this user
@@ -6792,15 +6428,16 @@
 
         mGoingToSleep = false;
         mRequestedOrGoingToSleep = false;
+        mDefaultDisplayPolicy.setAwake(false);
 
         // We must get this work done here because the power manager will drop
         // the wake lock and let the system suspend once this function returns.
         synchronized (mLock) {
-            mAwake = false;
             updateWakeGestureListenerLp();
-            updateOrientationListenerLp();
             updateLockScreenTimeout();
         }
+        mDefaultDisplayRotation.updateOrientationListener();
+
         if (mKeyguardDelegate != null) {
             mKeyguardDelegate.onFinishedGoingToSleep(why,
                     mCameraGestureTriggeredDuringGoingToSleep);
@@ -6814,17 +6451,17 @@
         EventLog.writeEvent(70000, 1);
         if (DEBUG_WAKEUP) Slog.i(TAG, "Started waking up...");
 
+        mDefaultDisplayPolicy.setAwake(true);
+
         // Since goToSleep performs these functions synchronously, we must
         // do the same here.  We cannot post this work to a handler because
         // that might cause it to become reordered with respect to what
         // may happen in a future call to goToSleep.
         synchronized (mLock) {
-            mAwake = true;
-
             updateWakeGestureListenerLp();
-            updateOrientationListenerLp();
             updateLockScreenTimeout();
         }
+        mDefaultDisplayRotation.updateOrientationListener();
 
         if (mKeyguardDelegate != null) {
             mKeyguardDelegate.onStartedWakingUp();
@@ -6861,16 +6498,14 @@
     }
 
     private void finishKeyguardDrawn() {
-        synchronized (mLock) {
-            if (!mScreenOnEarly || mKeyguardDrawComplete) {
-                return; // We are not awake yet or we have already informed of this event.
-            }
+        if (!mDefaultDisplayPolicy.finishKeyguardDrawn()) {
+            return;
+        }
 
-            mKeyguardDrawComplete = true;
+        synchronized (mLock) {
             if (mKeyguardDelegate != null) {
                 mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
             }
-            mWindowManagerDrawComplete = false;
         }
 
         // ... eventually calls finishWindowsDrawn which will finalize our screen turn on
@@ -6885,18 +6520,13 @@
         if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off...");
 
         updateScreenOffSleepToken(true);
+        mDefaultDisplayPolicy.screenTurnedOff();
         synchronized (mLock) {
-            mScreenOnEarly = false;
-            mScreenOnFully = false;
-            mKeyguardDrawComplete = false;
-            mWindowManagerDrawComplete = false;
-            mScreenOnListener = null;
-            updateOrientationListenerLp();
-
             if (mKeyguardDelegate != null) {
                 mKeyguardDelegate.onScreenTurnedOff();
             }
         }
+        mDefaultDisplayRotation.updateOrientationListener();
         reportScreenStateToVrManager(false);
     }
 
@@ -6913,13 +6543,9 @@
         if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on...");
 
         updateScreenOffSleepToken(false);
-        synchronized (mLock) {
-            mScreenOnEarly = true;
-            mScreenOnFully = false;
-            mKeyguardDrawComplete = false;
-            mWindowManagerDrawComplete = false;
-            mScreenOnListener = screenOnListener;
+        mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
 
+        synchronized (mLock) {
             if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
                 mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
                 mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
@@ -6962,46 +6588,29 @@
     }
 
     private void finishWindowsDrawn() {
-        synchronized (mLock) {
-            if (!mScreenOnEarly || mWindowManagerDrawComplete) {
-                return; // Screen is not turned on or we did already handle this case earlier.
-            }
-
-            mWindowManagerDrawComplete = true;
+        if (!mDefaultDisplayPolicy.finishWindowsDrawn()) {
+            return;
         }
 
         finishScreenTurningOn();
     }
 
     private void finishScreenTurningOn() {
-        synchronized (mLock) {
-            // We have just finished drawing screen content. Since the orientation listener
-            // gets only installed when all windows are drawn, we try to install it again.
-            updateOrientationListenerLp();
+        // We have just finished drawing screen content. Since the orientation listener
+        // gets only installed when all windows are drawn, we try to install it again.
+        mDefaultDisplayRotation.updateOrientationListener();
+
+        final ScreenOnListener listener = mDefaultDisplayPolicy.getScreenOnListener();
+        if (!mDefaultDisplayPolicy.finishScreenTurningOn()) {
+            return; // Spurious or not ready yet.
         }
-        final ScreenOnListener listener;
+
         final boolean enableScreen;
+        final boolean awake = mDefaultDisplayPolicy.isAwake();
         synchronized (mLock) {
-            if (DEBUG_WAKEUP) Slog.d(TAG,
-                    "finishScreenTurningOn: mAwake=" + mAwake
-                            + ", mScreenOnEarly=" + mScreenOnEarly
-                            + ", mScreenOnFully=" + mScreenOnFully
-                            + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
-                            + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
-
-            if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete
-                    || (mAwake && !mKeyguardDrawComplete)) {
-                return; // spurious or not ready yet
-            }
-
-            if (DEBUG_WAKEUP) Slog.i(TAG, "Finished screen turning on...");
-            listener = mScreenOnListener;
-            mScreenOnListener = null;
-            mScreenOnFully = true;
-
             // Remember the first time we draw the keyguard so we know when we're done with
             // the main part of booting and can enable the screen and hide boot messages.
-            if (!mKeyguardDrawnOnce && mAwake) {
+            if (!mKeyguardDrawnOnce && awake) {
                 mKeyguardDrawnOnce = true;
                 enableScreen = true;
                 if (mBootMessageNeedsHiding) {
@@ -7042,14 +6651,12 @@
 
     @Override
     public boolean isScreenOn() {
-        synchronized (mLock) {
-            return mScreenOnEarly;
-        }
+        return mDefaultDisplayPolicy.isScreenOnEarly();
     }
 
     @Override
     public boolean okToAnimate() {
-        return mAwake && !mGoingToSleep;
+        return mDefaultDisplayPolicy.isAwake() && !mGoingToSleep;
     }
 
     /** {@inheritDoc} */
@@ -7159,7 +6766,7 @@
         outInsets.setEmpty();
 
         // Only navigation bar
-        if (mHasNavigationBar) {
+        if (mDefaultDisplayPolicy.hasNavigationBar()) {
             int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
             if (position == NAV_BAR_BOTTOM) {
                 outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode);
@@ -7193,7 +6800,8 @@
     public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
             int displayHeight, int displayRotation) {
         final int barPosition = navigationBarPosition(displayWidth, displayHeight, displayRotation);
-        return isDockSideAllowed(dockSide, originalDockSide, barPosition, mNavigationBarCanMove);
+        return isDockSideAllowed(dockSide, originalDockSide, barPosition,
+                mDefaultDisplayPolicy.navigationBarCanMove());
     }
 
     @VisibleForTesting
@@ -7230,293 +6838,6 @@
     }
 
     @Override
-    public int rotationForOrientationLw(int orientation, int lastRotation, boolean defaultDisplay) {
-        if (false) {
-            Slog.v(TAG, "rotationForOrientationLw(orient="
-                        + orientation + ", last=" + lastRotation
-                        + "); user=" + mUserRotation + " "
-                        + ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED)
-                            ? "USER_ROTATION_LOCKED" : "")
-                        );
-        }
-
-        if (mForceDefaultOrientation) {
-            return Surface.ROTATION_0;
-        }
-
-        synchronized (mLock) {
-            int sensorRotation = mOrientationListener.getProposedRotation(); // may be -1
-            if (sensorRotation < 0) {
-                sensorRotation = lastRotation;
-            }
-
-            final int preferredRotation;
-            if (!defaultDisplay) {
-                // For secondary displays we ignore things like displays sensors, docking mode and
-                // rotation lock, and always prefer a default rotation.
-                preferredRotation = Surface.ROTATION_0;
-            } else if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
-                // Ignore sensor when lid switch is open and rotation is forced.
-                preferredRotation = mLidOpenRotation;
-            } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR
-                    && (mCarDockEnablesAccelerometer || mCarDockRotation >= 0)) {
-                // Ignore sensor when in car dock unless explicitly enabled.
-                // This case can override the behavior of NOSENSOR, and can also
-                // enable 180 degree rotation while docked.
-                preferredRotation = mCarDockEnablesAccelerometer
-                        ? sensorRotation : mCarDockRotation;
-            } else if ((mDockMode == Intent.EXTRA_DOCK_STATE_DESK
-                    || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
-                    || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
-                    && (mDeskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
-                // Ignore sensor when in desk dock unless explicitly enabled.
-                // This case can override the behavior of NOSENSOR, and can also
-                // enable 180 degree rotation while docked.
-                preferredRotation = mDeskDockEnablesAccelerometer
-                        ? sensorRotation : mDeskDockRotation;
-            } else if (mHdmiPlugged && mDemoHdmiRotationLock) {
-                // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
-                // Note that the dock orientation overrides the HDMI orientation.
-                preferredRotation = mDemoHdmiRotation;
-            } else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
-                    && mUndockedHdmiRotation >= 0) {
-                // Ignore sensor when plugged into HDMI and an undocked orientation has
-                // been specified in the configuration (only for legacy devices without
-                // full multi-display support).
-                // Note that the dock orientation overrides the HDMI orientation.
-                preferredRotation = mUndockedHdmiRotation;
-            } else if (mDemoRotationLock) {
-                // Ignore sensor when demo rotation lock is enabled.
-                // Note that the dock orientation and HDMI rotation lock override this.
-                preferredRotation = mDemoRotation;
-            } else if (mPersistentVrModeEnabled) {
-                // While in VR, apps always prefer a portrait rotation. This does not change
-                // any apps that explicitly set landscape, but does cause sensors be ignored,
-                // and ignored any orientation lock that the user has set (this conditional
-                // should remain above the ORIENTATION_LOCKED conditional below).
-                preferredRotation = mPortraitRotation;
-            } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
-                // Application just wants to remain locked in the last rotation.
-                preferredRotation = lastRotation;
-            } else if (!mSupportAutoRotation) {
-                // If we don't support auto-rotation then bail out here and ignore
-                // the sensor and any rotation lock settings.
-                preferredRotation = -1;
-            } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
-                            && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
-                                    || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
-                                    || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
-                                    || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
-                                    || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
-                    || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
-                    || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
-                    || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
-                    || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
-                // Otherwise, use sensor only if requested by the application or enabled
-                // by default for USER or UNSPECIFIED modes.  Does not apply to NOSENSOR.
-                if (mAllowAllRotations < 0) {
-                    // Can't read this during init() because the context doesn't
-                    // have display metrics at that time so we cannot determine
-                    // tablet vs. phone then.
-                    mAllowAllRotations = mContext.getResources().getBoolean(
-                            com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0;
-                }
-                if (sensorRotation != Surface.ROTATION_180
-                        || mAllowAllRotations == 1
-                        || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
-                        || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
-                    preferredRotation = sensorRotation;
-                } else {
-                    preferredRotation = lastRotation;
-                }
-            } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
-                    && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
-                // Apply rotation lock.  Does not apply to NOSENSOR.
-                // The idea is that the user rotation expresses a weak preference for the direction
-                // of gravity and as NOSENSOR is never affected by gravity, then neither should
-                // NOSENSOR be affected by rotation lock (although it will be affected by docks).
-                preferredRotation = mUserRotation;
-            } else {
-                // No overriding preference.
-                // We will do exactly what the application asked us to do.
-                preferredRotation = -1;
-            }
-
-            switch (orientation) {
-                case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
-                    // Return portrait unless overridden.
-                    if (isAnyPortrait(preferredRotation)) {
-                        return preferredRotation;
-                    }
-                    return mPortraitRotation;
-
-                case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
-                    // Return landscape unless overridden.
-                    if (isLandscapeOrSeascape(preferredRotation)) {
-                        return preferredRotation;
-                    }
-                    return mLandscapeRotation;
-
-                case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
-                    // Return reverse portrait unless overridden.
-                    if (isAnyPortrait(preferredRotation)) {
-                        return preferredRotation;
-                    }
-                    return mUpsideDownRotation;
-
-                case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
-                    // Return seascape unless overridden.
-                    if (isLandscapeOrSeascape(preferredRotation)) {
-                        return preferredRotation;
-                    }
-                    return mSeascapeRotation;
-
-                case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
-                case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
-                    // Return either landscape rotation.
-                    if (isLandscapeOrSeascape(preferredRotation)) {
-                        return preferredRotation;
-                    }
-                    if (isLandscapeOrSeascape(lastRotation)) {
-                        return lastRotation;
-                    }
-                    return mLandscapeRotation;
-
-                case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
-                case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
-                    // Return either portrait rotation.
-                    if (isAnyPortrait(preferredRotation)) {
-                        return preferredRotation;
-                    }
-                    if (isAnyPortrait(lastRotation)) {
-                        return lastRotation;
-                    }
-                    return mPortraitRotation;
-
-                default:
-                    // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR,
-                    // just return the preferred orientation we already calculated.
-                    if (preferredRotation >= 0) {
-                        return preferredRotation;
-                    }
-                    return Surface.ROTATION_0;
-            }
-        }
-    }
-
-    @Override
-    public boolean rotationHasCompatibleMetricsLw(int orientation, int rotation) {
-        switch (orientation) {
-            case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
-            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
-            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
-                return isAnyPortrait(rotation);
-
-            case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
-            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
-            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
-                return isLandscapeOrSeascape(rotation);
-
-            default:
-                return true;
-        }
-    }
-
-    @Override
-    public void setRotationLw(int rotation) {
-        mOrientationListener.setCurrentRotation(rotation);
-    }
-
-    public boolean isRotationChoicePossible(int orientation) {
-        // Rotation choice is only shown when the user is in locked mode.
-        if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
-
-        // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
-        // demo, hdmi, vr, etc mode
-
-        // Determine if the rotation is currently forced
-        if (mForceDefaultOrientation) {
-            return false; // Rotation is forced to default orientation
-
-        } else if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
-            return false; // Rotation is forced mLidOpenRotation
-
-        } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR && !mCarDockEnablesAccelerometer) {
-            return false; // Rotation forced to mCarDockRotation
-
-        } else if ((mDockMode == Intent.EXTRA_DOCK_STATE_DESK
-                || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
-                || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
-                && !mDeskDockEnablesAccelerometer) {
-            return false; // Rotation forced to mDeskDockRotation
-
-        } else if (mHdmiPlugged && mDemoHdmiRotationLock) {
-            return false; // Rotation forced to mDemoHdmiRotation
-
-        } else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
-                && mUndockedHdmiRotation >= 0) {
-            return false; // Rotation forced to mUndockedHdmiRotation
-
-        } else if (mDemoRotationLock) {
-            return false; // Rotation forced to mDemoRotation
-
-        } else if (mPersistentVrModeEnabled) {
-            return false; // Rotation forced to mPortraitRotation
-
-        } else if (!mSupportAutoRotation) {
-            return false;
-        }
-
-        // Ensure that some rotation choice is possible for the given orientation
-        switch (orientation) {
-            case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
-            case ActivityInfo.SCREEN_ORIENTATION_USER:
-            case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
-            case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
-            case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
-                // NOSENSOR description is ambiguous, in reality WM ignores user choice
-                return true;
-        }
-
-        // Rotation is forced, should be controlled by system
-        return false;
-    }
-
-    public boolean isValidRotationChoice(int orientation, final int preferredRotation) {
-        // Determine if the given app orientation is compatible with the provided rotation choice
-        switch (orientation) {
-            case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
-                // Works with any of the 4 rotations
-                return preferredRotation >= 0;
-
-            case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
-                // It's possible for the user pref to be set at 180 because of FULL_USER. This would
-                // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait
-                // but never to go to 180.
-                return preferredRotation == mPortraitRotation;
-
-            case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
-                // Works landscape or seascape
-                return isLandscapeOrSeascape(preferredRotation);
-
-            case ActivityInfo.SCREEN_ORIENTATION_USER:
-            case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
-                // Works with any rotation except upside down
-                return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
-        }
-
-        return false;
-    }
-
-    private boolean isLandscapeOrSeascape(int rotation) {
-        return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
-    }
-
-    private boolean isAnyPortrait(int rotation) {
-        return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
-    }
-
-    @Override
     public int getUserRotationMode() {
         return Settings.System.getIntForUser(mContext.getContentResolver(),
                 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 ?
@@ -7551,7 +6872,8 @@
     public void setSafeMode(boolean safeMode) {
         mSafeMode = safeMode;
         if (safeMode) {
-            performHapticFeedbackLw(null, HapticFeedbackConstants.SAFE_MODE_ENABLED, true);
+            performHapticFeedbackLw(null, HapticFeedbackConstants.SAFE_MODE_ENABLED, true,
+                    "Safe Mode Enabled");
         }
     }
 
@@ -7588,8 +6910,8 @@
 
         readCameraLensCoverState();
         updateUiMode();
+        mDefaultDisplayRotation.updateOrientationListener();
         synchronized (mLock) {
-            updateOrientationListenerLp();
             mSystemReady = true;
             mHandler.post(new Runnable() {
                 @Override
@@ -7627,9 +6949,7 @@
 
     @Override
     public boolean canDismissBootAnimation() {
-        synchronized (mLock) {
-            return mKeyguardDrawComplete;
-        }
+        return mDefaultDisplayPolicy.isKeyguardDrawComplete();
     }
 
     ProgressDialog mBootMsgDialog = null;
@@ -7729,7 +7049,7 @@
             }
         }
 
-        if (mAwake && mNotifyUserActivity) {
+        if (mDefaultDisplayPolicy.isAwake() && mNotifyUserActivity) {
             mHandler.sendEmptyMessageDelayed(MSG_NOTIFY_USER_ACTIVITY,
                     USER_ACTIVITY_NOTIFICATION_DELAY);
             mNotifyUserActivity = false;
@@ -7772,7 +7092,7 @@
 
     private void updateLockScreenTimeout() {
         synchronized (mScreenLockTimeout) {
-            boolean enable = (mAllowLockscreenWhenOn && mAwake &&
+            final boolean enable = (mAllowLockscreenWhenOn && mDefaultDisplayPolicy.isAwake() &&
                     mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId));
             if (mLockScreenTimerActive != enable) {
                 if (enable) {
@@ -7792,7 +7112,7 @@
     private void updateDreamingSleepToken(boolean acquire) {
         if (acquire) {
             if (mDreamingSleepToken == null) {
-                mDreamingSleepToken = mActivityManagerInternal.acquireSleepToken(
+                mDreamingSleepToken = mActivityTaskManagerInternal.acquireSleepToken(
                         "Dream", DEFAULT_DISPLAY);
             }
         } else {
@@ -7807,7 +7127,7 @@
     private void updateScreenOffSleepToken(boolean acquire) {
         if (acquire) {
             if (mScreenOffSleepToken == null) {
-                mScreenOffSleepToken = mActivityManagerInternal.acquireSleepToken(
+                mScreenOffSleepToken = mActivityTaskManagerInternal.acquireSleepToken(
                         "ScreenOff", DEFAULT_DISPLAY);
             }
         } else {
@@ -7827,10 +7147,11 @@
     }
 
     private void applyLidSwitchState() {
-        if (mLidState == LID_CLOSED && mLidControlsSleep) {
+        final int lidState = mDefaultDisplayPolicy.getLidState();
+        if (lidState == LID_CLOSED && mLidControlsSleep) {
             goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
                     PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
-        } else if (mLidState == LID_CLOSED && mLidControlsScreenLock) {
+        } else if (lidState == LID_CLOSED && mLidControlsScreenLock) {
             mWindowManagerFuncs.lockDeviceNow();
         }
 
@@ -7852,17 +7173,8 @@
 
     void updateRotation(boolean alwaysSendConfiguration) {
         try {
-            //set orientation on WindowManager
-            mWindowManager.updateRotation(alwaysSendConfiguration, false);
-        } catch (RemoteException e) {
-            // Ignore
-        }
-    }
-
-    void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
-        try {
-            //set orientation on WindowManager
-            mWindowManager.updateRotation(alwaysSendConfiguration, forceRelayout);
+            // Set orientation on WindowManager.
+            mWindowManager.updateRotation(alwaysSendConfiguration, false /* forceRelayout */);
         } catch (RemoteException e) {
             // Ignore
         }
@@ -7895,12 +7207,14 @@
             if (ENABLE_DESK_DOCK_HOME_CAPTURE) {
                 intent = mDeskDockIntent;
             }
-        } else if (mUiMode == Configuration.UI_MODE_TYPE_WATCH
-                && (mDockMode == Intent.EXTRA_DOCK_STATE_DESK
-                        || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK
-                        || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK)) {
-            // Always launch dock home from home when watch is docked, if it exists.
-            intent = mDeskDockIntent;
+        } else if (mUiMode == Configuration.UI_MODE_TYPE_WATCH) {
+            final int dockMode = mDefaultDisplayPolicy.getDockMode();
+            if (dockMode == Intent.EXTRA_DOCK_STATE_DESK
+                    || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK
+                    || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK) {
+                // Always launch dock home from home when watch is docked, if it exists.
+                intent = mDeskDockIntent;
+            }
         } else if (mUiMode == Configuration.UI_MODE_TYPE_VR_HEADSET) {
             if (ENABLE_VR_HEADSET_HOME_CAPTURE) {
                 intent = mVrHeadsetHomeIntent;
@@ -7988,7 +7302,7 @@
                     sendCloseSystemWindows();
                     Intent dock = createHomeDockIntent();
                     if (dock != null) {
-                        int result = ActivityManager.getService()
+                        int result = ActivityTaskManager.getService()
                                 .startActivityAsUser(null, null, dock,
                                         dock.resolveTypeIfNeeded(mContext.getContentResolver()),
                                         null, null, 0,
@@ -7999,7 +7313,7 @@
                         }
                     }
                 }
-                int result = ActivityManager.getService()
+                int result = ActivityTaskManager.getService()
                         .startActivityAsUser(null, null, mHomeIntent,
                                 mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
                                 null, null, 0,
@@ -8015,23 +7329,14 @@
         return true;
     }
 
-    @Override
-    public void setCurrentOrientationLw(int newOrientation) {
-        synchronized (mLock) {
-            if (newOrientation != mCurrentAppOrientation) {
-                mCurrentAppOrientation = newOrientation;
-                updateOrientationListenerLp();
-            }
-        }
-    }
-
     private boolean isTheaterModeEnabled() {
         return Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.THEATER_MODE_ON, 0) == 1;
     }
 
     @Override
-    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always) {
+    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always,
+            String reason) {
         if (!mVibrator.hasVibrator()) {
             return false;
         }
@@ -8055,7 +7360,7 @@
             owningUid = android.os.Process.myUid();
             owningPackage = mContext.getOpPackageName();
         }
-        mVibrator.vibrate(owningUid, owningPackage, effect, VIBRATION_ATTRIBUTES);
+        mVibrator.vibrate(owningUid, owningPackage, effect, reason, VIBRATION_ATTRIBUTES);
         return true;
     }
 
@@ -8125,10 +7430,19 @@
         if (winCandidate == null) {
             return 0;
         }
+
+        // The immersive mode confirmation should never affect the system bar visibility, otherwise
+        // it will unhide the navigation bar and hide itself.
         if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
-            // The immersive mode confirmation should never affect the system bar visibility,
-            // otherwise it will unhide the navigation bar and hide itself.
-            winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState;
+
+            // The immersive mode confirmation took the focus from mLastFocusedWindow which was
+            // controlling the system ui visibility. So if mLastFocusedWindow can still receive
+            // keys, we let it keep controlling the visibility.
+            final boolean lastFocusCanReceiveKeys =
+                    (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
+            winCandidate = isStatusBarKeyguard() ? mStatusBar
+                    : lastFocusCanReceiveKeys ? mLastFocusedWindow
+                    : mTopFullscreenOpaqueWindowState;
             if (winCandidate == null) {
                 return 0;
             }
@@ -8342,7 +7656,8 @@
         final long now = SystemClock.uptimeMillis();
         final boolean pendingPanic = mPendingPanicGestureUptime != 0
                 && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
-        if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard() && mKeyguardDrawComplete) {
+        if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard()
+                && mDefaultDisplayPolicy.isKeyguardDrawComplete()) {
             // The user performed the panic gesture recently, we're about to hide the bars,
             // we're no longer on the Keyguard and the screen is ready. We can now request the bars.
             mPendingPanicGestureUptime = 0;
@@ -8485,7 +7800,7 @@
     // overridden by qemu.hw.mainkeys in the emulator.
     @Override
     public boolean hasNavigationBar() {
-        return mHasNavigationBar;
+        return mDefaultDisplayPolicy.hasNavigationBar();
     }
 
     @Override
@@ -8530,19 +7845,21 @@
     }
 
     @Override
-    public boolean shouldRotateSeamlessly(int oldRotation, int newRotation) {
+    public boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation,
+            int newRotation) {
         // For the upside down rotation we don't rotate seamlessly as the navigation
         // bar moves position.
         // Note most apps (using orientation:sensor or user as opposed to fullSensor)
         // will not enter the reverse portrait orientation, so actually the
         // orientation won't change at all.
-        if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) {
+        if (oldRotation == displayRotation.getUpsideDownRotation()
+                || newRotation == displayRotation.getUpsideDownRotation()) {
             return false;
         }
         // If the navigation bar can't change sides, then it will
         // jump when we change orientations and we don't rotate
         // seamlessly.
-        if (!mNavigationBarCanMove) {
+        if (!displayRotation.getDisplayPolicy().navigationBarCanMove()) {
             return false;
         }
         int delta = newRotation - oldRotation;
@@ -8576,12 +7893,13 @@
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(LAST_SYSTEM_UI_FLAGS, mLastSystemUiFlags);
-        proto.write(ROTATION_MODE, mUserRotationMode);
-        proto.write(ROTATION, mUserRotation);
-        proto.write(ORIENTATION, mCurrentAppOrientation);
-        proto.write(SCREEN_ON_FULLY, mScreenOnFully);
-        proto.write(KEYGUARD_DRAW_COMPLETE, mKeyguardDrawComplete);
-        proto.write(WINDOW_MANAGER_DRAW_COMPLETE, mWindowManagerDrawComplete);
+        proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode());
+        proto.write(ROTATION, mDefaultDisplayRotation.getUserRotation());
+        proto.write(ORIENTATION, mDefaultDisplayRotation.getCurrentAppOrientation());
+        proto.write(SCREEN_ON_FULLY, mDefaultDisplayPolicy.isScreenOnFully());
+        proto.write(KEYGUARD_DRAW_COMPLETE, mDefaultDisplayPolicy.isKeyguardDrawComplete());
+        proto.write(WINDOW_MANAGER_DRAW_COMPLETE,
+                mDefaultDisplayPolicy.isWindowManagerDrawComplete());
         if (mFocusedApp != null) {
             proto.write(FOCUSED_APP_TOKEN, mFocusedApp.toString());
         }
@@ -8603,8 +7921,8 @@
         proto.write(FORCE_STATUS_BAR_FROM_KEYGUARD, mForceStatusBarFromKeyguard);
         mStatusBarController.writeToProto(proto, STATUS_BAR);
         mNavigationBarController.writeToProto(proto, NAVIGATION_BAR);
-        if (mOrientationListener != null) {
-            mOrientationListener.writeToProto(proto, ORIENTATION_LISTENER);
+        if (mDefaultOrientationListener != null) {
+            mDefaultOrientationListener.writeToProto(proto, ORIENTATION_LISTENER);
         }
         if (mKeyguardDelegate != null) {
             mKeyguardDelegate.writeToProto(proto, KEYGUARD_DELEGATE);
@@ -8617,13 +7935,8 @@
         pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode);
                 pw.print(" mSystemReady="); pw.print(mSystemReady);
                 pw.print(" mSystemBooted="); pw.println(mSystemBooted);
-        pw.print(prefix); pw.print("mLidState=");
-                pw.print(WindowManagerFuncs.lidStateToString(mLidState));
-                pw.print(" mLidOpenRotation=");
-                pw.println(Surface.rotationToString(mLidOpenRotation));
         pw.print(prefix); pw.print("mCameraLensCoverState=");
-                pw.print(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState));
-                pw.print(" mHdmiPlugged="); pw.println(mHdmiPlugged);
+                pw.println(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState));
         if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0
                 || mForceClearedSystemUiFlags != 0) {
             pw.print(prefix); pw.print("mLastSystemUiFlags=0x");
@@ -8641,27 +7954,9 @@
                 pw.println(mWakeGestureEnabledSetting);
 
         pw.print(prefix);
-                pw.print("mSupportAutoRotation="); pw.print(mSupportAutoRotation);
-                pw.print(" mOrientationSensorEnabled="); pw.println(mOrientationSensorEnabled);
-        pw.print(prefix); pw.print("mUiMode="); pw.print(Configuration.uiModeToString(mUiMode));
-                pw.print(" mDockMode="); pw.println(Intent.dockStateToString(mDockMode));
-        pw.print(prefix); pw.print("mEnableCarDockHomeCapture=");
-                pw.print(mEnableCarDockHomeCapture);
-                pw.print(" mCarDockRotation=");
-                pw.print(Surface.rotationToString(mCarDockRotation));
-                pw.print(" mDeskDockRotation=");
-                pw.println(Surface.rotationToString(mDeskDockRotation));
-        pw.print(prefix); pw.print("mUserRotationMode=");
-                pw.print(WindowManagerPolicy.userRotationModeToString(mUserRotationMode));
-                pw.print(" mUserRotation="); pw.print(Surface.rotationToString(mUserRotation));
-                pw.print(" mAllowAllRotations=");
-                pw.println(allowAllRotationsToString(mAllowAllRotations));
-        pw.print(prefix); pw.print("mCurrentAppOrientation=");
-                pw.println(ActivityInfo.screenOrientationToString(mCurrentAppOrientation));
-        pw.print(prefix); pw.print("mCarDockEnablesAccelerometer=");
-                pw.print(mCarDockEnablesAccelerometer);
-                pw.print(" mDeskDockEnablesAccelerometer=");
-                pw.println(mDeskDockEnablesAccelerometer);
+                pw.print("mUiMode=");
+                pw.print(Configuration.uiModeToString(mUiMode));
+                pw.print("mEnableCarDockHomeCapture="); pw.println(mEnableCarDockHomeCapture);
         pw.print(prefix); pw.print("mLidKeyboardAccessibility=");
                 pw.print(mLidKeyboardAccessibility);
                 pw.print(" mLidNavigationAccessibility="); pw.print(mLidNavigationAccessibility);
@@ -8711,12 +8006,6 @@
                 pw.print(" mEndcallBehavior=");
                 pw.println(endcallBehaviorToString(mEndcallBehavior));
         pw.print(prefix); pw.print("mHomePressed="); pw.println(mHomePressed);
-        pw.print(prefix);
-                pw.print("mAwake="); pw.print(mAwake);
-                pw.print("mScreenOnEarly="); pw.print(mScreenOnEarly);
-                pw.print(" mScreenOnFully="); pw.println(mScreenOnFully);
-        pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete);
-                pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete);
         pw.print(prefix); pw.print("mDockLayer="); pw.print(mDockLayer);
                 pw.print(" mStatusBarLayer="); pw.println(mStatusBarLayer);
         pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
@@ -8771,19 +8060,6 @@
         pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.print(mAllowLockscreenWhenOn);
                 pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
                 pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
-        pw.print(prefix); pw.print("mLandscapeRotation=");
-                pw.print(Surface.rotationToString(mLandscapeRotation));
-                pw.print(" mSeascapeRotation=");
-                pw.println(Surface.rotationToString(mSeascapeRotation));
-        pw.print(prefix); pw.print("mPortraitRotation=");
-                pw.print(Surface.rotationToString(mPortraitRotation));
-                pw.print(" mUpsideDownRotation=");
-                pw.println(Surface.rotationToString(mUpsideDownRotation));
-        pw.print(prefix); pw.print("mDemoHdmiRotation=");
-                pw.print(Surface.rotationToString(mDemoHdmiRotation));
-                pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock);
-        pw.print(prefix); pw.print("mUndockedHdmiRotation=");
-                pw.println(Surface.rotationToString(mUndockedHdmiRotation));
         if (mHasFeatureLeanback) {
             pw.print(prefix);
             pw.print("mAccessibilityTvKey1Pressed="); pw.println(mAccessibilityTvKey1Pressed);
@@ -8801,8 +8077,8 @@
         if (mWakeGestureListener != null) {
             mWakeGestureListener.dump(pw, prefix);
         }
-        if (mOrientationListener != null) {
-            mOrientationListener.dump(pw, prefix);
+        if (mDefaultOrientationListener != null) {
+            mDefaultOrientationListener.dump(pw, prefix);
         }
         if (mBurnInProtectionHelper != null) {
             mBurnInProtectionHelper.dump(prefix, pw);
@@ -8815,19 +8091,6 @@
         mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + "  ");
     }
 
-    private static String allowAllRotationsToString(int allowAll) {
-        switch (allowAll) {
-            case -1:
-                return "unknown";
-            case 0:
-                return "false";
-            case 1:
-                return "true";
-            default:
-                return Integer.toString(allowAll);
-        }
-    }
-
     private static String endcallBehaviorToString(int behavior) {
         StringBuilder sb = new StringBuilder();
         if ((behavior & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0 ) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 2efbc33..b55adeb 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -71,7 +71,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -94,6 +93,8 @@
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
 import com.android.server.wm.DisplayFrames;
+import com.android.server.wm.DisplayRotation;
+import com.android.server.wm.WindowFrames;
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import java.io.PrintWriter;
@@ -157,6 +158,8 @@
     int FINISH_LAYOUT_REDO_WALLPAPER = 0x0004;
     /** Need to recompute animations */
     int FINISH_LAYOUT_REDO_ANIM = 0x0008;
+    /** Layer for the screen off animation */
+    int COLOR_FADE_LAYER = 0x40000001;
 
     /**
      * Register shortcuts for window manager to dispatch.
@@ -175,7 +178,7 @@
     /**
      * Called when the resource overlays change.
      */
-    default void onOverlayChangedLw() {}
+    default void onOverlayChangedLw(DisplayContentInfo displayContentInfo) {}
 
     /**
      * Interface to the Window Manager state associated with a particular
@@ -198,35 +201,10 @@
          * getFrame() if so desired.  Must be called with the window manager
          * lock held.
          *
-         * @param parentFrame The frame of the parent container this window
-         * is in, used for computing its basic position.
-         * @param displayFrame The frame of the overall display in which this
-         * window can appear, used for constraining the overall dimensions
-         * of the window.
-         * @param overlayFrame The frame within the display that is inside
-         * of the overlay region.
-         * @param contentFrame The frame within the display in which we would
-         * like active content to appear.  This will cause windows behind to
-         * be resized to match the given content frame.
-         * @param visibleFrame The frame within the display that the window
-         * is actually visible, used for computing its visible insets to be
-         * given to windows behind.
-         * This can be used as a hint for scrolling (avoiding resizing)
-         * the window to make certain that parts of its content
-         * are visible.
-         * @param decorFrame The decor frame specified by policy specific to this window,
-         * to use for proper cropping during animation.
-         * @param stableFrame The frame around which stable system decoration is positioned.
-         * @param outsetFrame The frame that includes areas that aren't part of the surface but we
-         * want to treat them as such.
-         * @param displayCutout the display cutout
-         * @param parentFrameWasClippedByDisplayCutout true if the parent frame would have been
-         * different if there was no display cutout.
+         * @param windowFrames Container for all the window frames that affect how the window is
+         *                     laid out.
          */
-        public void computeFrameLw(Rect parentFrame, Rect displayFrame,
-                Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame,
-                Rect stableFrame, @Nullable Rect outsetFrame, WmDisplayCutout displayCutout,
-                boolean parentFrameWasClippedByDisplayCutout);
+        public void computeFrameLw(WindowFrames windowFrames);
 
         /**
          * Retrieve the current frame of the window that has been assigned by
@@ -492,6 +470,9 @@
          */
         boolean canAcquireSleepToken();
 
+        /** @return true if this window desires key events. */
+        boolean canReceiveKeys();
+
         /**
          * Writes {@link com.android.server.wm.IdentifierProto} to stream.
          */
@@ -545,7 +526,7 @@
          * Add a input consumer which will consume all input events going to any window below it.
          */
         public InputConsumer createInputConsumer(Looper looper, String name,
-                InputEventReceiver.Factory inputEventReceiverFactory);
+                InputEventReceiver.Factory inputEventReceiverFactory, int displayId);
 
         /**
          * Returns a code that describes the current state of the lid switch.
@@ -658,6 +639,27 @@
          * The keyguard showing state has changed
          */
         void onKeyguardShowingAndNotOccludedChanged();
+
+        DisplayContentInfo getDefaultDisplayContentInfo();
+    }
+
+    /**
+     * Provides the rotation of a device.
+     *
+     * @see com.android.server.policy.WindowOrientationListener
+     */
+    public interface RotationSource {
+        int getProposedRotation();
+
+        void setCurrentRotation(int rotation);
+    }
+
+    /**
+     * Interface to get public information of a display content.
+     */
+    public interface DisplayContentInfo {
+        DisplayRotation getDisplayRotation();
+        Display getDisplay();
     }
 
     /** Window has been added to the screen. */
@@ -689,6 +691,11 @@
     public final int USER_ROTATION_LOCKED = 1;
 
     /**
+     * Set the default display content to provide basic functions for the policy.
+     */
+    public void setDefaultDisplay(DisplayContentInfo displayContentInfo);
+
+    /**
      * Perform initialization of the policy.
      *
      * @param context The system context we are running in.
@@ -697,17 +704,6 @@
             WindowManagerFuncs windowManagerFuncs);
 
     /**
-     * @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true.
-     */
-    public boolean isDefaultOrientationForced();
-
-    /**
-     * Called by window manager once it has the initial, default native
-     * display dimensions.
-     */
-    public void setInitialDisplaySize(Display display, int width, int height, int density);
-
-    /**
      * Check permissions when adding a window.
      *
      * @param attrs The window's LayoutParams.
@@ -1180,6 +1176,7 @@
      * @param taskBounds The bounds of the task this window is on or {@code null} if no task is
      *                   associated with the window.
      * @param displayFrames display frames.
+     * @param floatingStack Whether the window's stack is floating.
      * @param outFrame The frame of the window.
      * @param outContentInsets The areas covered by system windows, expressed as positive insets.
      * @param outStableInsets The areas covered by stable system windows irrespective of their
@@ -1190,8 +1187,8 @@
      *         See {@link #isNavBarForcedShownLw(WindowState)}.
      */
     default boolean getLayoutHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
-            DisplayFrames displayFrames, Rect outFrame, Rect outContentInsets,
-            Rect outStableInsets, Rect outOutsets,
+            DisplayFrames displayFrames, boolean floatingStack,
+            Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
             DisplayCutout.ParcelableWrapper outDisplayCutout) {
         return false;
     }
@@ -1429,44 +1426,6 @@
     public boolean isShowingDreamLw();
 
     /**
-     * Given an orientation constant, returns the appropriate surface rotation,
-     * taking into account sensors, docking mode, rotation lock, and other factors.
-     *
-     * @param orientation An orientation constant, such as
-     * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
-     * @param lastRotation The most recently used rotation.
-     * @param defaultDisplay Flag indicating whether the rotation is computed for the default
-     *                       display. Currently for all non-default displays sensors, docking mode,
-     *                       rotation lock and other factors are ignored.
-     * @return The surface rotation to use.
-     */
-    public int rotationForOrientationLw(@ActivityInfo.ScreenOrientation int orientation,
-            int lastRotation, boolean defaultDisplay);
-
-    /**
-     * Given an orientation constant and a rotation, returns true if the rotation
-     * has compatible metrics to the requested orientation.  For example, if
-     * the application requested landscape and got seascape, then the rotation
-     * has compatible metrics; if the application requested portrait and got landscape,
-     * then the rotation has incompatible metrics; if the application did not specify
-     * a preference, then anything goes.
-     *
-     * @param orientation An orientation constant, such as
-     * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
-     * @param rotation The rotation to check.
-     * @return True if the rotation is compatible with the requested orientation.
-     */
-    public boolean rotationHasCompatibleMetricsLw(@ActivityInfo.ScreenOrientation int orientation,
-            int rotation);
-
-    /**
-     * Called by the window manager when the rotation changes.
-     *
-     * @param rotation The new rotation.
-     */
-    public void setRotationLw(int rotation);
-
-    /**
      * Called when the system is mostly done booting to set whether
      * the system should go into safe mode.
      */
@@ -1506,12 +1465,11 @@
      */
     public void enableScreenAfterBoot();
 
-    public void setCurrentOrientationLw(@ActivityInfo.ScreenOrientation int newOrientation);
-
     /**
      * Call from application to perform haptic feedback on its window.
      */
-    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always);
+    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always,
+            String reason);
 
     /**
      * Called when we have started keeping the screen on because a window
@@ -1721,9 +1679,10 @@
     /**
      * Called when the configuration has changed, and it's safe to load new values from resources.
      */
-    public void onConfigurationChanged();
+    public void onConfigurationChanged(DisplayContentInfo displayContentInfo);
 
-    public boolean shouldRotateSeamlessly(int oldRotation, int newRotation);
+    public boolean shouldRotateSeamlessly(DisplayRotation displayRotation,
+            int oldRotation, int newRotation);
 
     /**
      * Called when System UI has been started.
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index 1508c9e..d5adb5e 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -204,6 +204,10 @@
         }
     }
 
+    public Handler getHandler() {
+        return mHandler;
+    }
+
     /**
      * Sets the current rotation.
      *
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 81a8c55..1e0b52a 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -8,6 +8,7 @@
 import static com.android.server.wm.KeyguardServiceDelegateProto.SHOWING;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -213,7 +214,7 @@
                     // There are no longer any keyguard windows on secondary displays, so pass
                     // INVALID_DISPLAY. All that means is that showWhenLocked activities on
                     // secondary displays now get to show.
-                    ActivityManager.getService().setLockScreenShown(true /* keyguardShowing */,
+                    ActivityTaskManager.getService().setLockScreenShown(true /* keyguardShowing */,
                             false /* aodShowing */, INVALID_DISPLAY);
                 } catch (RemoteException e) {
                     // Local call.
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index d445611..b4dafc9 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -585,9 +585,9 @@
     }
 
     /**
-     * Called when wireless charging has started so as to provide user feedback (sound and visual).
+     * Called when wireless charging has started - to provide user feedback (sound and visual).
      */
-    public void onWirelessChargingStarted(int batteryLevel) {
+    public void onWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) {
         if (DEBUG) {
             Slog.d(TAG, "onWirelessChargingStarted");
         }
@@ -596,13 +596,14 @@
         Message msg = mHandler.obtainMessage(MSG_WIRELESS_CHARGING_STARTED);
         msg.setAsynchronous(true);
         msg.arg1 = batteryLevel;
+        msg.arg2 = userId;
         mHandler.sendMessage(msg);
     }
 
     /**
-     * Called when wired charging has started so as to provide user feedback
+     * Called when wired charging has started - to provide user feedback
      */
-    public void onWiredChargingStarted() {
+    public void onWiredChargingStarted(@UserIdInt int userId) {
         if (DEBUG) {
             Slog.d(TAG, "onWiredChargingStarted");
         }
@@ -610,6 +611,7 @@
         mSuspendBlocker.acquire();
         Message msg = mHandler.obtainMessage(MSG_WIRED_CHARGING_STARTED);
         msg.setAsynchronous(true);
+        msg.arg1 = userId;
         mHandler.sendMessage(msg);
     }
 
@@ -748,10 +750,10 @@
     /**
      * Plays the wireless charging sound for both wireless and non-wireless charging
      */
-    private void playChargingStartedSound() {
+    private void playChargingStartedSound(@UserIdInt int userId) {
         final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.CHARGING_STARTED_SOUND);
-        if (isChargingFeedbackEnabled() && soundPath != null) {
+        if (isChargingFeedbackEnabled(userId) && soundPath != null) {
             final Uri soundUri = Uri.parse("file://" + soundPath);
             if (soundUri != null) {
                 final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
@@ -763,17 +765,17 @@
         }
     }
 
-    private void showWirelessChargingStarted(int batteryLevel) {
-        playWirelessChargingVibration();
-        playChargingStartedSound();
+    private void showWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) {
+        playWirelessChargingVibration(userId);
+        playChargingStartedSound(userId);
         if (mStatusBarManagerInternal != null) {
             mStatusBarManagerInternal.showChargingAnimation(batteryLevel);
         }
         mSuspendBlocker.release();
     }
 
-    private void showWiredChargingStarted() {
-        playChargingStartedSound();
+    private void showWiredChargingStarted(@UserIdInt int userId) {
+        playChargingStartedSound(userId);
         mSuspendBlocker.release();
     }
 
@@ -781,17 +783,17 @@
         mTrustManager.setDeviceLockedForUser(userId, true /*locked*/);
     }
 
-    private void playWirelessChargingVibration() {
-        final boolean vibrateEnabled = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.CHARGING_VIBRATION_ENABLED, 0) != 0;
-        if (vibrateEnabled && isChargingFeedbackEnabled()) {
+    private void playWirelessChargingVibration(@UserIdInt int userId) {
+        final boolean vibrateEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
+        if (vibrateEnabled && isChargingFeedbackEnabled(userId)) {
             mVibrator.vibrate(WIRELESS_CHARGING_VIBRATION_EFFECT, VIBRATION_ATTRIBUTES);
         }
     }
 
-    private boolean isChargingFeedbackEnabled() {
-        final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.CHARGING_SOUNDS_ENABLED, 1) != 0;
+    private boolean isChargingFeedbackEnabled(@UserIdInt int userId) {
+        final boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                Settings.Secure.CHARGING_SOUNDS_ENABLED, 1, userId) != 0;
         final boolean dndOff = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
                 == Settings.Global.ZEN_MODE_OFF;
@@ -813,7 +815,7 @@
                     sendNextBroadcast();
                     break;
                 case MSG_WIRELESS_CHARGING_STARTED:
-                    showWirelessChargingStarted(msg.arg1);
+                    showWirelessChargingStarted(msg.arg1, msg.arg2);
                     break;
                 case MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED:
                     sendBrightnessBoostChangedBroadcast();
@@ -822,7 +824,7 @@
                     lockProfile(msg.arg1);
                     break;
                 case MSG_WIRED_CHARGING_STARTED:
-                    showWiredChargingStarted();
+                    showWiredChargingStarted(msg.arg1);
                     break;
             }
         }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 697801f..13800b6 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -219,8 +219,8 @@
     private static final int HALT_MODE_REBOOT = 1;
     private static final int HALT_MODE_REBOOT_SAFE_MODE = 2;
 
-    // Persistent property for last reboot reason
-    private static final String LAST_REBOOT_PROPERTY = "persist.sys.boot.reason";
+    // property for last reboot reason
+    private static final String REBOOT_PROPERTY = "sys.boot.reason";
 
     private final Context mContext;
     private final ServiceThread mHandlerThread;
@@ -1705,9 +1705,9 @@
                 if (mBootCompleted) {
                     if (mIsPowered && !BatteryManager.isPlugWired(oldPlugType)
                             && BatteryManager.isPlugWired(mPlugType)) {
-                        mNotifier.onWiredChargingStarted();
+                        mNotifier.onWiredChargingStarted(mForegroundProfile);
                     } else if (dockedOnWirelessCharger) {
-                        mNotifier.onWirelessChargingStarted(mBatteryLevel);
+                        mNotifier.onWirelessChargingStarted(mBatteryLevel, mForegroundProfile);
                     }
                 }
             }
@@ -4398,7 +4398,7 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                return getLastShutdownReasonInternal(LAST_REBOOT_PROPERTY);
+                return getLastShutdownReasonInternal(REBOOT_PROPERTY);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
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 dd6d71e..20ceed43 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -72,7 +72,7 @@
     @GuardedBy("mLock")
     private int mBatteryLevel;
 
-    /** Whether the battery level is considered to be "low" or not.*/
+    /** Whether the battery level is considered to be "low" or not. */
     @GuardedBy("mLock")
     private boolean mIsBatteryLevelLow;
 
@@ -84,6 +84,9 @@
     @GuardedBy("mLock")
     private boolean mSettingBatterySaverEnabledSticky;
 
+    /** Config flag to track if battery saver's sticky behaviour is disabled. */
+    private final boolean mBatterySaverStickyBehaviourDisabled;
+
     /**
      * Previously known value of Global.LOW_POWER_MODE_TRIGGER_LEVEL.
      * (Currently only used in dumpsys.)
@@ -124,6 +127,9 @@
         mLock = lock;
         mContext = context;
         mBatterySaverController = batterySaverController;
+
+        mBatterySaverStickyBehaviourDisabled = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled);
     }
 
     private boolean isBatterySaverEnabled() {
@@ -190,6 +196,7 @@
         h.postDelayed(r, delayMillis);
     }
 
+    @GuardedBy("mLock")
     void refreshSettingsLocked() {
         final boolean lowPowerModeEnabled = getGlobalSetting(
                 Settings.Global.LOW_POWER_MODE, 0) != 0;
@@ -208,6 +215,7 @@
      *
      * Note this will be called before {@link #onBootCompleted} too.
      */
+    @GuardedBy("mLock")
     @VisibleForTesting
     void setSettingsLocked(boolean batterySaverEnabled, boolean batterySaverEnabledSticky,
             int batterySaverTriggerThreshold) {
@@ -282,6 +290,7 @@
     /**
      * Decide whether to auto-start / stop battery saver.
      */
+    @GuardedBy("mLock")
     private void doAutoBatterySaverLocked() {
         if (DEBUG) {
             Slog.d(TAG, "doAutoBatterySaverLocked: mBootCompleted=" + mBootCompleted
@@ -304,7 +313,7 @@
                     BatterySaverController.REASON_PLUGGED_IN,
                     "Plugged in");
 
-        } else if (mSettingBatterySaverEnabledSticky) {
+        } else if (mSettingBatterySaverEnabledSticky && !mBatterySaverStickyBehaviourDisabled) {
             // Re-enable BS.
             enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true,
                     BatterySaverController.REASON_STICKY_RESTORE,
@@ -345,6 +354,7 @@
      * Actually enable / disable battery saver. Write the new state to the global settings
      * and propagate it to {@link #mBatterySaverController}.
      */
+    @GuardedBy("mLock")
     private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason,
             String strReason) {
         if (DEBUG) {
@@ -383,8 +393,9 @@
         putGlobalSetting(Global.LOW_POWER_MODE, enable ? 1 : 0);
 
         if (manual) {
-            mSettingBatterySaverEnabledSticky = enable;
-            putGlobalSetting(Global.LOW_POWER_MODE_STICKY, enable ? 1 : 0);
+            mSettingBatterySaverEnabledSticky = !mBatterySaverStickyBehaviourDisabled && enable;
+            putGlobalSetting(Global.LOW_POWER_MODE_STICKY,
+                    mSettingBatterySaverEnabledSticky ? 1 : 0);
         }
         mBatterySaverController.enableBatterySaver(enable, intReason);
 
@@ -395,6 +406,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void updateSnoozingLocked(boolean snoozing, String reason) {
         if (mBatterySaverSnoozing == snoozing) {
             return;
@@ -449,6 +461,8 @@
             pw.println(mSettingBatterySaverEnabledSticky);
             pw.print("  mSettingBatterySaverTriggerThreshold=");
             pw.println(mSettingBatterySaverTriggerThreshold);
+            pw.print("  mBatterySaverStickyBehaviourDisabled=");
+            pw.println(mBatterySaverStickyBehaviourDisabled);
         }
     }
 
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index f53a5dc..8213205 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -484,6 +484,7 @@
                 (BatterySaverState.MASK << BatterySaverState.SHIFT) |
                 (InteractiveState.MASK << InteractiveState.SHIFT);
 
+        @GuardedBy("BatterySavingStats.this.mLock")
         public void transitionStateLocked(
                 int newState, long now, int batteryLevel, int batteryPercent) {
             final boolean stateChanging =
@@ -503,6 +504,7 @@
             mLastState = newState;
         }
 
+        @GuardedBy("BatterySavingStats.this.mLock")
         void reportLocked(int state, long deltaTimeMs,
                 int startBatteryLevelUa, int startBatteryLevelPercent,
                 int endBatteryLevelUa, int endBatteryLevelPercent) {
diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java
index c3fa823..bc1a12f 100644
--- a/services/core/java/com/android/server/search/SearchManagerService.java
+++ b/services/core/java/com/android/server/search/SearchManagerService.java
@@ -17,7 +17,9 @@
 package com.android.server.search;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.app.ISearchManager;
 import android.app.SearchManager;
 import android.app.SearchableInfo;
@@ -307,7 +309,7 @@
             Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
             intent.setComponent(comp);
 
-            IActivityManager am = ActivityManager.getService();
+            IActivityTaskManager am = ActivityTaskManager.getService();
             if (args != null) {
                 args.putInt(Intent.EXTRA_KEY_EVENT, android.view.KeyEvent.KEYCODE_ASSIST);
             }
diff --git a/services/core/java/com/android/server/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java
index 4e7fb96..e139ab8 100644
--- a/services/core/java/com/android/server/slice/PinnedSliceState.java
+++ b/services/core/java/com/android/server/slice/PinnedSliceState.java
@@ -154,8 +154,8 @@
     }
 
     ContentProviderClient getClient() {
-        ContentProviderClient client =
-                mService.getContext().getContentResolver().acquireContentProviderClient(mUri);
+        ContentProviderClient client = mService.getContext().getContentResolver()
+                .acquireUnstableContentProviderClient(mUri);
         if (client == null) return null;
         client.setDetectNotResponding(SLICE_TIMEOUT);
         return client;
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index c3b9841..73775b4 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -42,6 +42,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Binder;
@@ -197,6 +198,7 @@
     public SliceSpec[] getPinnedSpecs(Uri uri, String pkg) throws RemoteException {
         verifyCaller(pkg);
         enforceAccess(pkg, uri);
+        uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
         return getPinnedSlice(uri).getSpecs();
     }
 
@@ -217,12 +219,12 @@
     }
 
     @Override
-    public int checkSlicePermission(Uri uri, String pkg, int pid, int uid,
+    public int checkSlicePermission(Uri uri, String callingPkg, String pkg, int pid, int uid,
             String[] autoGrantPermissions) {
         int userId = UserHandle.getUserId(uid);
         if (pkg == null) {
             for (String p : mContext.getPackageManager().getPackagesForUid(uid)) {
-                if (checkSlicePermission(uri, p, pid, uid, autoGrantPermissions)
+                if (checkSlicePermission(uri, callingPkg, p, pid, uid, autoGrantPermissions)
                         == PERMISSION_GRANTED) {
                     return PERMISSION_GRANTED;
                 }
@@ -235,9 +237,9 @@
         if (mPermissions.hasPermission(pkg, userId, uri)) {
             return PackageManager.PERMISSION_GRANTED;
         }
-        if (autoGrantPermissions != null) {
+        if (autoGrantPermissions != null && callingPkg != null) {
             // Need to own the Uri to call in with permissions to grant.
-            enforceOwner(pkg, uri, userId);
+            enforceOwner(callingPkg, uri, userId);
             for (String perm : autoGrantPermissions) {
                 if (mContext.checkPermission(perm, pid, uid) == PERMISSION_GRANTED) {
                     int providerUser = ContentProvider.getUserIdFromUri(uri, userId);
@@ -389,36 +391,17 @@
     }
 
     protected int checkAccess(String pkg, Uri uri, int uid, int pid) {
-        return checkSlicePermission(uri, pkg, uid, pid, null);
+        return checkSlicePermission(uri, null, pkg, uid, pid, null);
     }
 
     private String getProviderPkg(Uri uri, int user) {
         long ident = Binder.clearCallingIdentity();
         try {
-            IBinder token = new Binder();
-            IActivityManager activityManager = ActivityManager.getService();
-            ContentProviderHolder holder = null;
             String providerName = getUriWithoutUserId(uri).getAuthority();
-            try {
-                try {
-                    holder = activityManager.getContentProviderExternal(
-                            providerName, getUserIdFromUri(uri, user), token);
-                    if (holder != null && holder.info != null) {
-                        return holder.info.packageName;
-                    } else {
-                        return null;
-                    }
-                } finally {
-                    if (holder != null && holder.provider != null) {
-                        activityManager.removeContentProviderExternal(providerName, token);
-                    }
-                }
-            } catch (RemoteException e) {
-                // Can't happen.
-                throw e.rethrowAsRuntimeException();
-            }
+            ProviderInfo provider = mContext.getPackageManager().resolveContentProviderAsUser(
+                    providerName, 0, getUserIdFromUri(uri, user));
+            return provider.packageName;
         } finally {
-            // I know, the double finally seems ugly, but seems safest for the identity.
             Binder.restoreCallingIdentity(ident);
         }
     }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 4d65440..556038f 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -57,11 +57,13 @@
 import android.os.UserManager;
 import android.telephony.ModemActivityInfo;
 import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.StatsLog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.os.BinderCallsStats.ExportedCallStat;
 import com.android.internal.os.KernelCpuSpeedReader;
 import com.android.internal.os.KernelUidCpuTimeReader;
 import com.android.internal.os.KernelUidCpuClusterTimeReader;
@@ -71,6 +73,7 @@
 import com.android.internal.os.KernelWakelockStats;
 import com.android.internal.os.PowerProfile;
 import com.android.internal.util.DumpUtils;
+import com.android.server.BinderCallsStatsService;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
@@ -84,6 +87,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
@@ -324,6 +328,7 @@
                             PackageManager pm = context.getPackageManager();
                             String app = intent.getData().getSchemeSpecificPart();
                             sStatsd.informOnePackageRemoved(app, uid);
+                            StatsLog.write(StatsLog.GENERIC_ATOM, uid, 1000);
                         }
                     } else {
                         PackageManager pm = context.getPackageManager();
@@ -332,6 +337,7 @@
                         String app = intent.getData().getSchemeSpecificPart();
                         PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
                         sStatsd.informOnePackage(app, uid, pi.getLongVersionCode());
+                        StatsLog.write(StatsLog.GENERIC_ATOM, uid, 1001);
                     }
                 } catch (Exception e) {
                     Slog.w(TAG, "Failed to inform statsd of an app update", e);
@@ -891,6 +897,51 @@
         }
     }
 
+    private void pullBinderCallsStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+        BinderCallsStatsService.Internal binderStats =
+                LocalServices.getService(BinderCallsStatsService.Internal.class);
+        if (binderStats == null) {
+            return;
+        }
+
+        List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
+        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+        for (ExportedCallStat callStat : callStats) {
+            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 13 /* fields */);
+            e.writeInt(callStat.uid);
+            e.writeString(callStat.className);
+            e.writeString(callStat.methodName);
+            e.writeLong(callStat.callCount);
+            e.writeLong(callStat.exceptionCount);
+            e.writeLong(callStat.latencyMicros);
+            e.writeLong(callStat.maxLatencyMicros);
+            e.writeLong(callStat.cpuTimeMicros);
+            e.writeLong(callStat.maxCpuTimeMicros);
+            e.writeLong(callStat.maxReplySizeBytes);
+            e.writeLong(callStat.maxRequestSizeBytes);
+            e.writeLong(callStat.recordedCallCount);
+            e.writeInt(callStat.screenInteractive ? 1 : 0);
+            pulledData.add(e);
+        }
+    }
+
+    private void pullBinderCallsStatsExceptions(int tagId, List<StatsLogEventWrapper> pulledData) {
+        BinderCallsStatsService.Internal binderStats =
+                LocalServices.getService(BinderCallsStatsService.Internal.class);
+        if (binderStats == null) {
+            return;
+        }
+
+        ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats();
+        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+        for (Entry<String, Integer> entry : exceptionStats.entrySet()) {
+            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 2 /* fields */);
+            e.writeString(entry.getKey());
+            e.writeInt(entry.getValue());
+            pulledData.add(e);
+        }
+    }
+
     /**
      * Pulls various data.
      */
@@ -973,6 +1024,14 @@
                 pullProcessMemoryState(tagId, ret);
                 break;
             }
+            case StatsLog.BINDER_CALLS: {
+                pullBinderCallsStats(tagId, ret);
+                break;
+            }
+            case StatsLog.BINDER_CALLS_EXCEPTIONS: {
+                pullBinderCallsStatsExceptions(tagId, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
@@ -1152,6 +1211,7 @@
         }
     }
 
+    @GuardedBy("StatsCompanionService.sStatsdLock")
     private void forgetEverythingLocked() {
         sStatsd = null;
         mContext.unregisterReceiver(mAppUpdateReceiver);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 738b0ca..519881e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -547,50 +547,50 @@
     }
 
     @Override
-    public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
+    public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
         if (mBar != null) {
             try {
-                mBar.showFingerprintDialog(bundle, receiver);
+                mBar.showBiometricDialog(bundle, receiver);
             } catch (RemoteException ex) {
             }
         }
     }
 
     @Override
-    public void onFingerprintAuthenticated() {
+    public void onBiometricAuthenticated() {
         if (mBar != null) {
             try {
-                mBar.onFingerprintAuthenticated();
+                mBar.onBiometricAuthenticated();
             } catch (RemoteException ex) {
             }
         }
     }
 
     @Override
-    public void onFingerprintHelp(String message) {
+    public void onBiometricHelp(String message) {
         if (mBar != null) {
             try {
-                mBar.onFingerprintHelp(message);
+                mBar.onBiometricHelp(message);
             } catch (RemoteException ex) {
             }
         }
     }
 
     @Override
-    public void onFingerprintError(String error) {
+    public void onBiometricError(String error) {
         if (mBar != null) {
             try {
-                mBar.onFingerprintError(error);
+                mBar.onBiometricError(error);
             } catch (RemoteException ex) {
             }
         }
     }
 
     @Override
-    public void hideFingerprintDialog() {
+    public void hideBiometricDialog() {
         if (mBar != null) {
             try {
-                mBar.hideFingerprintDialog();
+                mBar.hideBiometricDialog();
             } catch (RemoteException ex) {
             }
         }
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 2742b0f..d36ab3f 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -28,6 +28,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.textclassifier.ITextClassificationCallback;
 import android.service.textclassifier.ITextClassifierService;
 import android.service.textclassifier.ITextLinksCallback;
@@ -38,16 +39,22 @@
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationContext;
+import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassificationSessionId;
+import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextLinks;
 import android.view.textclassifier.TextSelection;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FunctionalUtils;
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.server.SystemService;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.Queue;
 
@@ -242,10 +249,12 @@
         }
     }
 
+    @GuardedBy("mLock")
     private UserState getCallingUserStateLocked() {
         return getUserStateLocked(UserHandle.getCallingUserId());
     }
 
+    @GuardedBy("mLock")
     private UserState getUserStateLocked(int userId) {
         UserState result = mUserStates.get(userId);
         if (result == null) {
@@ -255,10 +264,19 @@
         return result;
     }
 
+    @GuardedBy("mLock")
     UserState peekUserStateLocked(int userId) {
         return mUserStates.get(userId);
     }
 
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, fout)) return;
+        IndentingPrintWriter pw = new IndentingPrintWriter(fout, "  ");
+        TextClassificationManager tcm = mContext.getSystemService(TextClassificationManager.class);
+        tcm.dump(pw);
+    }
+
     private static final class PendingRequest implements IBinder.DeathRecipient {
 
         @Nullable private final IBinder mBinder;
@@ -374,6 +392,7 @@
             }
         }
 
+        @GuardedBy("mLock")
         private boolean bindIfHasPendingRequestsLocked() {
             return !mPendingRequests.isEmpty() && bindLocked();
         }
@@ -382,6 +401,7 @@
          * @return true if the service is bound or in the process of being bound.
          *      Returns false otherwise.
          */
+        @GuardedBy("mLock")
         private boolean bindLocked() {
             if (isBoundLocked() || mBinding) {
                 return true;
diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
new file mode 100644
index 0000000..7bdc8a3
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
@@ -0,0 +1,232 @@
+/*
+ * 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.timedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.app.timedetector.TimeSignal;
+import android.content.Intent;
+import android.util.Slog;
+import android.util.TimestampedValue;
+
+import com.android.internal.telephony.TelephonyIntents;
+
+import java.io.PrintWriter;
+
+/**
+ * An implementation of TimeDetectorStrategy that passes only NITZ suggestions to
+ * {@link AlarmManager}. The TimeDetectorService handles thread safety: all calls to
+ * this class can be assumed to be single threaded (though the thread used may vary).
+ */
+// @NotThreadSafe
+public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
+
+    private final static String TAG = "timedetector.SimpleTimeDetectorStrategy";
+
+    /**
+     * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the
+     * actual system clock time before a warning is logged. Used to help identify situations where
+     * there is something other than this class setting the system clock.
+     */
+    private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
+
+    // @NonNull after initialize()
+    private Callback mCallback;
+
+    // NITZ state.
+    @Nullable private TimestampedValue<Long> mLastNitzTime;
+
+
+    // Information about the last time signal received: Used when toggling auto-time.
+    @Nullable private TimestampedValue<Long> mLastSystemClockTime;
+    private boolean mLastSystemClockTimeSendNetworkBroadcast;
+
+    // System clock state.
+    @Nullable private TimestampedValue<Long> mLastSystemClockTimeSet;
+
+    @Override
+    public void initialize(@NonNull Callback callback) {
+        mCallback = callback;
+    }
+
+    @Override
+    public void suggestTime(@NonNull TimeSignal timeSignal) {
+        if (!TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId())) {
+            Slog.w(TAG, "Ignoring signal from unsupported source: " + timeSignal);
+            return;
+        }
+
+        // NITZ logic
+
+        TimestampedValue<Long> newNitzUtcTime = timeSignal.getUtcTime();
+        boolean nitzTimeIsValid = validateNewNitzTime(newNitzUtcTime, mLastNitzTime);
+        if (!nitzTimeIsValid) {
+            return;
+        }
+        // Always store the last NITZ value received, regardless of whether we go on to use it to
+        // update the system clock. This is so that we can validate future NITZ signals.
+        mLastNitzTime = newNitzUtcTime;
+
+        // System clock update logic.
+
+        // Historically, Android has sent a telephony broadcast only when setting the time using
+        // NITZ.
+        final boolean sendNetworkBroadcast =
+                TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId());
+
+        final TimestampedValue<Long> newUtcTime = newNitzUtcTime;
+        setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast);
+    }
+
+    private static boolean validateNewNitzTime(TimestampedValue<Long> newNitzUtcTime,
+            TimestampedValue<Long> lastNitzTime) {
+
+        if (lastNitzTime != null) {
+            long referenceTimeDifference =
+                    TimestampedValue.referenceTimeDifference(newNitzUtcTime, lastNitzTime);
+            if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) {
+                // Out of order or bogus.
+                Slog.w(TAG, "validateNewNitzTime: Bad NITZ signal received."
+                        + " referenceTimeDifference=" + referenceTimeDifference
+                        + " lastNitzTime=" + lastNitzTime
+                        + " newNitzUtcTime=" + newNitzUtcTime);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void setSystemClockIfRequired(
+            TimestampedValue<Long> time, boolean sendNetworkBroadcast) {
+
+        // Store the last candidate we've seen in all cases so we can set the system clock
+        // when/if time detection is enabled.
+        mLastSystemClockTime = time;
+        mLastSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast;
+
+        if (!mCallback.isTimeDetectionEnabled()) {
+            Slog.d(TAG, "setSystemClockIfRequired: Time detection is not enabled. time=" + time);
+            return;
+        }
+
+        mCallback.acquireWakeLock();
+        try {
+            long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+            long actualTimeMillis = mCallback.systemClockMillis();
+
+            // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else
+            // may be setting the clock.
+            if (mLastSystemClockTimeSet != null) {
+                long expectedTimeMillis = TimeDetectorStrategy.getTimeAt(
+                        mLastSystemClockTimeSet, elapsedRealtimeMillis);
+                long absSystemClockDifference = Math.abs(expectedTimeMillis - actualTimeMillis);
+                if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) {
+                    Slog.w(TAG, "System clock has not tracked elapsed real time clock. A clock may"
+                            + " be inaccurate or something unexpectedly set the system clock."
+                            + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                            + " expectedTimeMillis=" + expectedTimeMillis
+                            + " actualTimeMillis=" + actualTimeMillis);
+                }
+            }
+
+            final String reason = "New time signal";
+            adjustAndSetDeviceSystemClock(
+                    time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, reason);
+        } finally {
+            mCallback.releaseWakeLock();
+        }
+    }
+
+    @Override
+    public void handleAutoTimeDetectionToggle(boolean enabled) {
+        // If automatic time detection is enabled we update the system clock instantly if we can.
+        // Conversely, if automatic time detection is disabled we leave the clock as it is.
+        if (enabled) {
+            if (mLastSystemClockTime != null) {
+                // Only send the network broadcast if the last candidate would have caused one.
+                final boolean sendNetworkBroadcast = mLastSystemClockTimeSendNetworkBroadcast;
+
+                mCallback.acquireWakeLock();
+                try {
+                    long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+                    long actualTimeMillis = mCallback.systemClockMillis();
+
+                    final String reason = "Automatic time detection enabled.";
+                    adjustAndSetDeviceSystemClock(mLastSystemClockTime, sendNetworkBroadcast,
+                            elapsedRealtimeMillis, actualTimeMillis, reason);
+                } finally {
+                    mCallback.releaseWakeLock();
+                }
+            }
+        } else {
+            // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what
+            // it should be in future.
+            mLastSystemClockTimeSet = null;
+        }
+    }
+
+    @Override
+    public void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
+        pw.println("mLastNitzTime=" + mLastNitzTime);
+        pw.println("mLastSystemClockTimeSet=" + mLastSystemClockTimeSet);
+        pw.println("mLastSystemClockTime=" + mLastSystemClockTime);
+        pw.println("mLastSystemClockTimeSendNetworkBroadcast="
+                + mLastSystemClockTimeSendNetworkBroadcast);
+    }
+
+    private void adjustAndSetDeviceSystemClock(
+            TimestampedValue<Long> newTime, boolean sendNetworkBroadcast,
+            long elapsedRealtimeMillis, long actualSystemClockMillis, String reason) {
+
+        // Adjust for the time that has elapsed since the signal was received.
+        long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis);
+
+        // Check if the new signal would make sufficient difference to the system clock. If it's
+        // below the threshold then ignore it.
+        long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
+        long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis();
+        if (absTimeDifference < systemClockUpdateThreshold) {
+            Slog.d(TAG, "adjustAndSetDeviceSystemClock: Not setting system clock. New time and"
+                    + " system clock are close enough."
+                    + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                    + " newTime=" + newTime
+                    + " reason=" + reason
+                    + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
+                    + " absTimeDifference=" + absTimeDifference);
+            return;
+        }
+
+        Slog.d(TAG, "Setting system clock using time=" + newTime
+                + " reason=" + reason
+                + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                + " newTimeMillis=" + newSystemClockMillis);
+        mCallback.setSystemClock(newSystemClockMillis);
+
+        // CLOCK_PARANOIA : Record the last time this class set the system clock.
+        mLastSystemClockTimeSet = newTime;
+
+        if (sendNetworkBroadcast) {
+            // Send a broadcast that telephony code used to send after setting the clock.
+            // TODO Remove this broadcast as soon as there are no remaining listeners.
+            Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+            intent.putExtra("time", newSystemClockMillis);
+            mCallback.sendStickyBroadcast(intent);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 0b63e29..9c83000 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -16,23 +16,33 @@
 
 package com.android.server.timedetector;
 
-import com.android.internal.util.DumpUtils;
-import com.android.server.SystemService;
-
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.timedetector.ITimeDetectorService;
+import android.app.timedetector.TimeSignal;
+import android.content.ContentResolver;
 import android.content.Context;
-import android.util.Slog;
+import android.database.ContentObserver;
+import android.os.Binder;
+import android.provider.Settings;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.server.FgThread;
+import com.android.server.SystemService;
+import com.android.server.timedetector.TimeDetectorStrategy.Callback;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Objects;
 
 public final class TimeDetectorService extends ITimeDetectorService.Stub {
-
     private static final String TAG = "timedetector.TimeDetectorService";
 
     public static class Lifecycle extends SystemService {
 
-        public Lifecycle(Context context) {
+        public Lifecycle(@NonNull Context context) {
             super(context);
         }
 
@@ -46,27 +56,79 @@
         }
     }
 
-    private final Context mContext;
+    @NonNull private final Context mContext;
+    @NonNull private final Callback mCallback;
 
-    private static TimeDetectorService create(Context context) {
-        return new TimeDetectorService(context);
+    // The lock used when call the strategy to ensure thread safety.
+    @NonNull private final Object mStrategyLock = new Object();
+
+    @GuardedBy("mStrategyLock")
+    @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
+
+    private static TimeDetectorService create(@NonNull Context context) {
+        final TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy();
+        final TimeDetectorStrategyCallbackImpl callback =
+                new TimeDetectorStrategyCallbackImpl(context);
+        timeDetector.initialize(callback);
+
+        TimeDetectorService timeDetectorService =
+                new TimeDetectorService(context, callback, timeDetector);
+
+        // Wire up event listening.
+        ContentResolver contentResolver = context.getContentResolver();
+        contentResolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
+                new ContentObserver(FgThread.getHandler()) {
+                    public void onChange(boolean selfChange) {
+                        timeDetectorService.handleAutoTimeDetectionToggle();
+                    }
+                });
+
+        return timeDetectorService;
     }
 
-    public TimeDetectorService(Context context) {
-        mContext = context;
+    @VisibleForTesting
+    public TimeDetectorService(@NonNull Context context, @NonNull Callback callback,
+            @NonNull TimeDetectorStrategy timeDetectorStrategy) {
+        mContext = Objects.requireNonNull(context);
+        mCallback = Objects.requireNonNull(callback);
+        mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy);
     }
 
     @Override
-    public void stubbedCall() {
-        // Empty call for initial tests.
-        Slog.d(TAG, "stubbedCall() called");
-        // TODO(nfuller): Remove when there are real methods.
+    public void suggestTime(@NonNull TimeSignal timeSignal) {
+        enforceSetTimePermission();
+        Objects.requireNonNull(timeSignal);
+
+        long idToken = Binder.clearCallingIdentity();
+        try {
+            synchronized (mStrategyLock) {
+                mTimeDetectorStrategy.suggestTime(timeSignal);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(idToken);
+        }
+    }
+
+    @VisibleForTesting
+    public void handleAutoTimeDetectionToggle() {
+        synchronized (mStrategyLock) {
+            final boolean timeDetectionEnabled = mCallback.isTimeDetectionEnabled();
+            mTimeDetectorStrategy.handleAutoTimeDetectionToggle(timeDetectionEnabled);
+        }
     }
 
     @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+    protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
+            @Nullable String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        // TODO(nfuller): Implement when there is state.
+        synchronized (mStrategyLock) {
+            mTimeDetectorStrategy.dump(pw, args);
+        }
+    }
+
+    private void enforceSetTimePermission() {
+        mContext.enforceCallingPermission(android.Manifest.permission.SET_TIME, "set time");
     }
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
new file mode 100644
index 0000000..e050865
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -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.
+ */
+
+package com.android.server.timedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.timedetector.TimeSignal;
+import android.content.Intent;
+import android.util.TimestampedValue;
+
+import java.io.PrintWriter;
+
+/**
+ * The interface for classes that implement the time detection algorithm used by the
+ * TimeDetectorService. The TimeDetectorService handles thread safety: all calls to implementations
+ * of this interface can be assumed to be single threaded (though the thread used may vary).
+ *
+ * @hide
+ */
+// @NotThreadSafe
+public interface TimeDetectorStrategy {
+
+    /**
+     * The interface used by the strategy to interact with the surrounding service.
+     */
+    interface Callback {
+
+        /**
+         * The absolute threshold below which the system clock need not be updated. i.e. if setting
+         * the system clock would adjust it by less than this (either backwards or forwards) then it
+         * need not be set.
+         */
+        int systemClockUpdateThresholdMillis();
+
+        /** Returns true if automatic time detection is enabled. */
+        boolean isTimeDetectionEnabled();
+
+        /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */
+        void acquireWakeLock();
+
+        /** Returns the elapsedRealtimeMillis clock value. The WakeLock must be held. */
+        long elapsedRealtimeMillis();
+
+        /** Returns the system clock value. The WakeLock must be held. */
+        long systemClockMillis();
+
+        /** Sets the device system clock. The WakeLock must be held. */
+        void setSystemClock(long newTimeMillis);
+
+        /** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */
+        void releaseWakeLock();
+
+        /** Send the supplied intent as a stick broadcast. */
+        void sendStickyBroadcast(@NonNull Intent intent);
+    }
+
+    /** Initialize the strategy. */
+    void initialize(@NonNull Callback callback);
+
+    /** Process the suggested time. */
+    void suggestTime(@NonNull TimeSignal timeSignal);
+
+    /** Handle the auto-time setting being toggled on or off. */
+    void handleAutoTimeDetectionToggle(boolean enabled);
+
+    /** Dump debug information. */
+    void dump(@NonNull PrintWriter pw, @Nullable String[] args);
+
+    // Utility methods below are to be moved to a better home when one becomes more obvious.
+
+    /**
+     * Adjusts the supplied time value by applying the difference between the reference time
+     * supplied and the reference time associated with the time.
+     */
+    static long getTimeAt(@NonNull TimestampedValue<Long> timeValue, long referenceClockMillisNow) {
+        return (referenceClockMillisNow - timeValue.getReferenceTimeMillis())
+                + timeValue.getValue();
+    }
+}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
new file mode 100644
index 0000000..77b9e62
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
@@ -0,0 +1,125 @@
+/*
+ * 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.timedetector;
+
+import android.annotation.NonNull;
+import android.app.AlarmManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+
+import java.util.Objects;
+
+/**
+ * The real implementation of {@link TimeDetectorStrategy.Callback} used on device.
+ */
+public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrategy.Callback {
+
+    private final static String TAG = "timedetector.TimeDetectorStrategyCallbackImpl";
+
+    private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000;
+
+    /**
+     * If a newly calculated system clock time and the current system clock time differs by this or
+     * more the system clock will actually be updated. Used to prevent the system clock being set
+     * for only minor differences.
+     */
+    private final int mSystemClockUpdateThresholdMillis;
+
+    @NonNull private final Context mContext;
+    @NonNull private final ContentResolver mContentResolver;
+    @NonNull private final PowerManager.WakeLock mWakeLock;
+    @NonNull private final AlarmManager mAlarmManager;
+
+    public TimeDetectorStrategyCallbackImpl(@NonNull Context context) {
+        mContext = Objects.requireNonNull(context);
+        mContentResolver = Objects.requireNonNull(context.getContentResolver());
+
+        PowerManager powerManager = context.getSystemService(PowerManager.class);
+        mWakeLock = Objects.requireNonNull(
+                powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG));
+
+        mAlarmManager = Objects.requireNonNull(context.getSystemService(AlarmManager.class));
+
+        mSystemClockUpdateThresholdMillis =
+                SystemProperties.getInt("ro.sys.time_detector_update_diff",
+                        SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT);
+    }
+
+    @Override
+    public int systemClockUpdateThresholdMillis() {
+        return mSystemClockUpdateThresholdMillis;
+    }
+
+    @Override
+    public boolean isTimeDetectionEnabled() {
+        try {
+            return Settings.Global.getInt(mContentResolver, Settings.Global.AUTO_TIME) != 0;
+        } catch (Settings.SettingNotFoundException snfe) {
+            return true;
+        }
+    }
+
+    @Override
+    public void acquireWakeLock() {
+        if (mWakeLock.isHeld()) {
+            Slog.wtf(TAG, "WakeLock " + mWakeLock + " already held");
+        }
+        mWakeLock.acquire();
+    }
+
+    @Override
+    public long elapsedRealtimeMillis() {
+        checkWakeLockHeld();
+        return SystemClock.elapsedRealtime();
+    }
+
+    @Override
+    public long systemClockMillis() {
+        checkWakeLockHeld();
+        return System.currentTimeMillis();
+    }
+
+    @Override
+    public void setSystemClock(long newTimeMillis) {
+        checkWakeLockHeld();
+        mAlarmManager.setTime(newTimeMillis);
+    }
+
+    @Override
+    public void releaseWakeLock() {
+        checkWakeLockHeld();
+        mWakeLock.release();
+    }
+
+    @Override
+    public void sendStickyBroadcast(@NonNull Intent intent) {
+        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+    }
+
+    private void checkWakeLockHeld() {
+        if (!mWakeLock.isHeld()) {
+            Slog.wtf(TAG, "WakeLock " + mWakeLock + " not held");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
new file mode 100644
index 0000000..5f71b0b
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.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.timezonedetector;
+
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemService;
+import android.app.timezonedetector.ITimeZoneDetectorService;
+import android.content.Context;
+import android.util.Slog;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub {
+    private static final String TAG = "timezonedetector.TimeZoneDetectorService";
+
+    public static class Lifecycle extends SystemService {
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            TimeZoneDetectorService service = TimeZoneDetectorService.create(getContext());
+            // Publish the binder service so it can be accessed from other (appropriately
+            // permissioned) processes.
+            publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service);
+        }
+    }
+
+    private final Context mContext;
+
+    private static TimeZoneDetectorService create(Context context) {
+        return new TimeZoneDetectorService(context);
+    }
+
+    public TimeZoneDetectorService(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public void stubbedCall() {
+        // Empty call for initial tests.
+        Slog.d(TAG, "stubbedCall() called");
+        // TODO: Remove when there are real methods.
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+        // TODO: Implement when there is state.
+    }
+}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 4413666..3291a45 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -20,6 +20,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
+import android.hardware.biometrics.BiometricSourceType;
 import android.app.trust.ITrustListener;
 import android.app.trust.ITrustManager;
 import android.content.BroadcastReceiver;
@@ -46,7 +47,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.service.trust.TrustAgentService;
 import android.text.TextUtils;
@@ -60,7 +60,6 @@
 import android.view.WindowManagerGlobal;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
-import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.SystemService;
@@ -130,8 +129,8 @@
     private final SparseBooleanArray mTrustUsuallyManagedForUser = new SparseBooleanArray();
 
     // set to true only if user can skip bouncer
-    @GuardedBy("mUsersUnlockedByFingerprint")
-    private final SparseBooleanArray mUsersUnlockedByFingerprint = new SparseBooleanArray();
+    @GuardedBy("mUsersUnlockedByBiometric")
+    private final SparseBooleanArray mUsersUnlockedByBiometric = new SparseBooleanArray();
 
     private final StrongAuthTracker mStrongAuthTracker;
 
@@ -431,20 +430,27 @@
         for (int i = 0; i < userInfos.size(); i++) {
             UserInfo info = userInfos.get(i);
 
-            if (info == null || info.partial || !info.isEnabled() || info.guestToRemove
-                    || !info.supportsSwitchToByUser()) {
+            if (info == null || info.partial || !info.isEnabled() || info.guestToRemove) {
                 continue;
             }
 
             int id = info.id;
             boolean secure = mLockPatternUtils.isSecure(id);
+
+            if (!info.supportsSwitchToByUser()) {
+                if (info.isManagedProfile() && !secure) {
+                    setDeviceLockedForUser(id, false);
+                }
+                continue;
+            }
+
             boolean trusted = aggregateIsTrusted(id);
             boolean showingKeyguard = true;
-            boolean fingerprintAuthenticated = false;
+            boolean biometricAuthenticated = false;
 
             if (mCurrentUser == id) {
-                synchronized(mUsersUnlockedByFingerprint) {
-                    fingerprintAuthenticated = mUsersUnlockedByFingerprint.get(id, false);
+                synchronized(mUsersUnlockedByBiometric) {
+                    biometricAuthenticated = mUsersUnlockedByBiometric.get(id, false);
                 }
                 try {
                     showingKeyguard = wm.isKeyguardLocked();
@@ -452,7 +458,7 @@
                 }
             }
             boolean deviceLocked = secure && showingKeyguard && !trusted &&
-                    !fingerprintAuthenticated;
+                    !biometricAuthenticated;
             setDeviceLockedForUser(id, deviceLocked);
         }
     }
@@ -992,7 +998,8 @@
             enforceReportPermission();
             final long identity = Binder.clearCallingIdentity();
             try {
-                if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+                if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
+                        && mLockPatternUtils.isSecure(userId)) {
                     synchronized (mDeviceLockedForUser) {
                         mDeviceLockedForUser.put(userId, locked);
                     }
@@ -1021,20 +1028,20 @@
         }
 
         @Override
-        public void unlockedByFingerprintForUser(int userId) {
+        public void unlockedByBiometricForUser(int userId, BiometricSourceType biometricSource) {
             enforceReportPermission();
-            synchronized(mUsersUnlockedByFingerprint) {
-                mUsersUnlockedByFingerprint.put(userId, true);
+            synchronized(mUsersUnlockedByBiometric) {
+                mUsersUnlockedByBiometric.put(userId, true);
             }
             mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, userId,
                     0 /* arg2 */).sendToTarget();
         }
 
         @Override
-        public void clearAllFingerprints() {
+        public void clearAllBiometricRecognized(BiometricSourceType biometricSource) {
             enforceReportPermission();
-            synchronized(mUsersUnlockedByFingerprint) {
-                mUsersUnlockedByFingerprint.clear();
+            synchronized(mUsersUnlockedByBiometric) {
+                mUsersUnlockedByBiometric.clear();
             }
             mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, UserHandle.USER_ALL,
                     0 /* arg2 */).sendToTarget();
@@ -1188,8 +1195,8 @@
                     synchronized (mTrustUsuallyManagedForUser) {
                         mTrustUsuallyManagedForUser.delete(userId);
                     }
-                    synchronized (mUsersUnlockedByFingerprint) {
-                        mUsersUnlockedByFingerprint.delete(userId);
+                    synchronized (mUsersUnlockedByBiometric) {
+                        mUsersUnlockedByBiometric.delete(userId);
                     }
                     refreshAgentList(userId);
                     refreshDeviceLockedForUser(userId);
diff --git a/services/core/java/com/android/server/uri/GrantUri.java b/services/core/java/com/android/server/uri/GrantUri.java
new file mode 100644
index 0000000..c694264
--- /dev/null
+++ b/services/core/java/com/android/server/uri/GrantUri.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.server.uri;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.GrantUriProto;
+
+/** A {@link Uri} that can be granted to app an to access with the right permission. */
+public class GrantUri {
+    public final int sourceUserId;
+    public final Uri uri;
+    public boolean prefix;
+
+    public GrantUri(int sourceUserId, Uri uri, boolean prefix) {
+        this.sourceUserId = sourceUserId;
+        this.uri = uri;
+        this.prefix = prefix;
+    }
+
+    @Override
+    public int hashCode() {
+        int hashCode = 1;
+        hashCode = 31 * hashCode + sourceUserId;
+        hashCode = 31 * hashCode + uri.hashCode();
+        hashCode = 31 * hashCode + (prefix ? 1231 : 1237);
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof GrantUri) {
+            GrantUri other = (GrantUri) o;
+            return uri.equals(other.uri) && (sourceUserId == other.sourceUserId)
+                    && prefix == other.prefix;
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        String result = uri.toString() + " [user " + sourceUserId + "]";
+        if (prefix) result += " [prefix]";
+        return result;
+    }
+
+    public String toSafeString() {
+        String result = uri.toSafeString() + " [user " + sourceUserId + "]";
+        if (prefix) result += " [prefix]";
+        return result;
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(GrantUriProto.URI, uri.toString());
+        proto.write(GrantUriProto.SOURCE_USER_ID, sourceUserId);
+        proto.end(token);
+    }
+
+    public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
+        if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+            return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
+                    ContentProvider.getUriWithoutUserId(uri), false);
+        } else {
+            return new GrantUri(defaultSourceUserHandle, uri, false);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/uri/NeededUriGrants.java b/services/core/java/com/android/server/uri/NeededUriGrants.java
new file mode 100644
index 0000000..87a2641
--- /dev/null
+++ b/services/core/java/com/android/server/uri/NeededUriGrants.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.uri;
+
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.NeededUriGrantsProto;
+
+import java.util.ArrayList;
+
+/** List of {@link GrantUri} a process needs. */
+public class NeededUriGrants extends ArrayList<GrantUri> {
+    final String targetPkg;
+    final int targetUid;
+    final int flags;
+
+    public NeededUriGrants(String targetPkg, int targetUid, int flags) {
+        this.targetPkg = targetPkg;
+        this.targetUid = targetUid;
+        this.flags = flags;
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(NeededUriGrantsProto.TARGET_PACKAGE, targetPkg);
+        proto.write(NeededUriGrantsProto.TARGET_UID, targetUid);
+        proto.write(NeededUriGrantsProto.FLAGS, flags);
+
+        final int N = this.size();
+        for (int i = 0; i < N; i++) {
+            this.get(i).writeToProto(proto, NeededUriGrantsProto.GRANTS);
+        }
+        proto.end(token);
+    }
+}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
new file mode 100644
index 0000000..2f50fcb
--- /dev/null
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.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.uri;
+
+import android.content.Intent;
+import android.content.pm.ProviderInfo;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+
+/**
+ * Uri Grants local system service interface.
+ * @hide Only for use within system server
+ */
+public interface UriGrantsManagerInternal {
+    void onSystemReady();
+    void onActivityManagerInternalAdded();
+    void removeUriPermissionIfNeeded(UriPermission perm);
+    void grantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
+            final int modeFlags, UriPermissionOwner owner, int targetUserId);
+    void revokeUriPermission(String targetPackage, int callingUid,
+            GrantUri grantUri, final int modeFlags);
+    boolean checkUriPermission(GrantUri grantUri, int uid, final int modeFlags);
+    int checkGrantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
+            final int modeFlags, int lastTargetUid);
+    int checkGrantUriPermission(
+            int callingUid, String targetPkg, Uri uri, int modeFlags, int userId);
+    NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid,
+            String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId);
+    /**
+     * Grant Uri permissions from one app to another. This method only extends
+     * permission grants if {@code callingUid} has permission to them.
+     */
+    void grantUriPermissionFromIntent(int callingUid,
+            String targetPkg, Intent intent, int targetUserId);
+    void grantUriPermissionFromIntent(int callingUid,
+            String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId);
+    void grantUriPermissionUncheckedFromIntent(
+            NeededUriGrants needed, UriPermissionOwner owner);
+    IBinder newUriPermissionOwner(String name);
+    /**
+     * Remove any {@link UriPermission} granted <em>from</em> or <em>to</em> the
+     * given package.
+     *
+     * @param packageName Package name to match, or {@code null} to apply to all
+     *            packages.
+     * @param userHandle User to match, or {@link UserHandle#USER_ALL} to apply
+     *            to all users.
+     * @param persistable If persistable grants should be removed.
+     * @param targetOnly When {@code true}, only remove permissions where the app is the target,
+     * not source.
+     */
+    void removeUriPermissionsForPackage(
+            String packageName, int userHandle, boolean persistable, boolean targetOnly);
+    /**
+     * @param uri This uri must NOT contain an embedded userId.
+     * @param userId The userId in which the uri is to be resolved.
+     */
+    void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId);
+    boolean checkAuthorityGrants(
+            int callingUid, ProviderInfo cpi, int userId, boolean checkUser);
+    void dump(PrintWriter pw, boolean dumpAll, String dumpPackage);
+}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
new file mode 100644
index 0000000..a731e9b
--- /dev/null
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -0,0 +1,1479 @@
+/*
+ * 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.uri;
+
+import static android.Manifest.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS;
+import static android.Manifest.permission.FORCE_PERSISTABLE_URI_PERMISSIONS;
+import static android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.ROOT_UID;
+import static android.os.Process.SYSTEM_UID;
+import static android.os.Process.myUid;
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readLongAttribute;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeLongAttribute;
+import static com.android.server.uri.UriGrantsManagerService.H.PERSIST_URI_GRANTS_MSG;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.AppGlobals;
+import android.app.GrantedUriPermission;
+import android.app.IUriGrantsManager;
+import android.content.ClipData;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.PathPermission;
+import android.content.pm.ProviderInfo;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Downloads;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
+import com.android.server.IoThread;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+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.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import libcore.io.IoUtils;
+
+/** Manages uri grants. */
+public class UriGrantsManagerService extends IUriGrantsManager.Stub {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "UriGrantsManagerService";
+    // Maximum number of persisted Uri grants a package is allowed
+    private static final int MAX_PERSISTED_URI_GRANTS = 128;
+
+    private final Object mLock = new Object();
+    private final Context mContext;
+    private final H mH;
+    ActivityManagerInternal mAmInternal;
+    PackageManagerInternal mPmInternal;
+
+    /** File storing persisted {@link #mGrantedUriPermissions}. */
+    private final AtomicFile mGrantFile;
+
+    /** XML constants used in {@link #mGrantFile} */
+    private static final String TAG_URI_GRANTS = "uri-grants";
+    private static final String TAG_URI_GRANT = "uri-grant";
+    private static final String ATTR_USER_HANDLE = "userHandle";
+    private static final String ATTR_SOURCE_USER_ID = "sourceUserId";
+    private static final String ATTR_TARGET_USER_ID = "targetUserId";
+    private static final String ATTR_SOURCE_PKG = "sourcePkg";
+    private static final String ATTR_TARGET_PKG = "targetPkg";
+    private static final String ATTR_URI = "uri";
+    private static final String ATTR_MODE_FLAGS = "modeFlags";
+    private static final String ATTR_CREATED_TIME = "createdTime";
+    private static final String ATTR_PREFIX = "prefix";
+
+    /**
+     * Global set of specific {@link Uri} permissions that have been granted.
+     * This optimized lookup structure maps from {@link UriPermission#targetUid}
+     * to {@link UriPermission#uri} to {@link UriPermission}.
+     */
+    private final SparseArray<ArrayMap<GrantUri, UriPermission>>
+            mGrantedUriPermissions = new SparseArray<>();
+
+    private UriGrantsManagerService(Context context) {
+        mContext = context;
+        mH = new H(IoThread.get().getLooper());
+        final File systemDir = SystemServiceManager.ensureSystemDir();
+        mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants");
+    }
+
+    private void start() {
+        LocalServices.addService(UriGrantsManagerInternal.class, new LocalService());
+    }
+
+    void onActivityManagerInternalAdded() {
+        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+        mPmInternal = LocalServices.getService(PackageManagerInternal.class);
+    }
+
+    public static final class Lifecycle extends SystemService {
+        private final UriGrantsManagerService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+            mService = new UriGrantsManagerService(context);
+        }
+
+        @Override
+        public void onStart() {
+            publishBinderService(Context.URI_GRANTS_SERVICE, mService);
+            mService.start();
+        }
+
+        public UriGrantsManagerService getService() {
+            return mService;
+        }
+    }
+
+    /**
+     * @param uri This uri must NOT contain an embedded userId.
+     * @param sourceUserId The userId in which the uri is to be resolved.
+     * @param targetUserId The userId of the app that receives the grant.
+     */
+    @Override
+    public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, Uri uri,
+            final int modeFlags, int sourceUserId, int targetUserId) {
+        targetUserId = mAmInternal.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), targetUserId, false, ALLOW_FULL_ONLY,
+                "grantUriPermissionFromOwner", null);
+        synchronized(mLock) {
+            UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+            if (owner == null) {
+                throw new IllegalArgumentException("Unknown owner: " + token);
+            }
+            if (fromUid != Binder.getCallingUid()) {
+                if (Binder.getCallingUid() != myUid()) {
+                    // Only system code can grant URI permissions on behalf
+                    // of other users.
+                    throw new SecurityException("nice try");
+                }
+            }
+            if (targetPkg == null) {
+                throw new IllegalArgumentException("null target");
+            }
+            if (uri == null) {
+                throw new IllegalArgumentException("null uri");
+            }
+
+            grantUriPermission(fromUid, targetPkg, new GrantUri(sourceUserId, uri, false),
+                    modeFlags, owner, targetUserId);
+        }
+    }
+
+    @Override
+    public ParceledListSlice<android.content.UriPermission> getPersistedUriPermissions(
+            String packageName, boolean incoming) {
+        enforceNotIsolatedCaller("getPersistedUriPermissions");
+        Preconditions.checkNotNull(packageName, "packageName");
+
+        final int callingUid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(callingUid);
+        final IPackageManager pm = AppGlobals.getPackageManager();
+        try {
+            final int packageUid = pm.getPackageUid(packageName,
+                    MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, callingUserId);
+            if (packageUid != callingUid) {
+                throw new SecurityException(
+                        "Package " + packageName + " does not belong to calling UID " + callingUid);
+            }
+        } catch (RemoteException e) {
+            throw new SecurityException("Failed to verify package name ownership");
+        }
+
+        final ArrayList<android.content.UriPermission> result = Lists.newArrayList();
+        synchronized (mLock) {
+            if (incoming) {
+                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(
+                        callingUid);
+                if (perms == null) {
+                    Slog.w(TAG, "No permission grants found for " + packageName);
+                } else {
+                    for (int j = 0; j < perms.size(); j++) {
+                        final UriPermission perm = perms.valueAt(j);
+                        if (packageName.equals(perm.targetPkg) && perm.persistedModeFlags != 0) {
+                            result.add(perm.buildPersistedPublicApiObject());
+                        }
+                    }
+                }
+            } else {
+                final int size = mGrantedUriPermissions.size();
+                for (int i = 0; i < size; i++) {
+                    final ArrayMap<GrantUri, UriPermission> perms =
+                            mGrantedUriPermissions.valueAt(i);
+                    for (int j = 0; j < perms.size(); j++) {
+                        final UriPermission perm = perms.valueAt(j);
+                        if (packageName.equals(perm.sourcePkg) && perm.persistedModeFlags != 0) {
+                            result.add(perm.buildPersistedPublicApiObject());
+                        }
+                    }
+                }
+            }
+        }
+        return new ParceledListSlice<>(result);
+    }
+
+    @Override
+    public ParceledListSlice<GrantedUriPermission> getGrantedUriPermissions(
+            @Nullable String packageName, int userId) {
+        mAmInternal.enforceCallingPermission(
+                GET_APP_GRANTED_URI_PERMISSIONS, "getGrantedUriPermissions");
+
+        final List<GrantedUriPermission> result = new ArrayList<>();
+        synchronized (mLock) {
+            final int size = mGrantedUriPermissions.size();
+            for (int i = 0; i < size; i++) {
+                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
+                for (int j = 0; j < perms.size(); j++) {
+                    final UriPermission perm = perms.valueAt(j);
+                    if ((packageName == null || packageName.equals(perm.targetPkg))
+                            && perm.targetUserId == userId
+                            && perm.persistedModeFlags != 0) {
+                        result.add(perm.buildGrantedUriPermission());
+                    }
+                }
+            }
+        }
+        return new ParceledListSlice<>(result);
+    }
+
+    /**
+     * @param uri This uri must NOT contain an embedded userId.
+     * @param toPackage Name of package whose uri is being granted to (if {@code null}, uses
+     * calling uid)
+     * @param userId The userId in which the uri is to be resolved.
+     */
+    @Override
+    public void takePersistableUriPermission(Uri uri, final int modeFlags,
+            @Nullable String toPackage, int userId) {
+        final int uid;
+        if (toPackage != null) {
+            mAmInternal.enforceCallingPermission(FORCE_PERSISTABLE_URI_PERMISSIONS,
+                    "takePersistableUriPermission");
+            uid = mPmInternal.getPackageUid(toPackage, 0, userId);
+        } else {
+            enforceNotIsolatedCaller("takePersistableUriPermission");
+            uid = Binder.getCallingUid();
+        }
+
+        Preconditions.checkFlagsArgument(modeFlags,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        synchronized (mLock) {
+            boolean persistChanged = false;
+            GrantUri grantUri = new GrantUri(userId, uri, false);
+
+            UriPermission exactPerm = findUriPermissionLocked(uid, grantUri);
+            UriPermission prefixPerm = findUriPermissionLocked(uid,
+                    new GrantUri(userId, uri, true));
+
+            final boolean exactValid = (exactPerm != null)
+                    && ((modeFlags & exactPerm.persistableModeFlags) == modeFlags);
+            final boolean prefixValid = (prefixPerm != null)
+                    && ((modeFlags & prefixPerm.persistableModeFlags) == modeFlags);
+
+            if (!(exactValid || prefixValid)) {
+                throw new SecurityException("No persistable permission grants found for UID "
+                        + uid + " and Uri " + grantUri.toSafeString());
+            }
+
+            if (exactValid) {
+                persistChanged |= exactPerm.takePersistableModes(modeFlags);
+            }
+            if (prefixValid) {
+                persistChanged |= prefixPerm.takePersistableModes(modeFlags);
+            }
+
+            persistChanged |= maybePrunePersistedUriGrants(uid);
+
+            if (persistChanged) {
+                schedulePersistUriGrants();
+            }
+        }
+    }
+
+    @Override
+    public void clearGrantedUriPermissions(String packageName, int userId) {
+        mAmInternal.enforceCallingPermission(
+                CLEAR_APP_GRANTED_URI_PERMISSIONS, "clearGrantedUriPermissions");
+        synchronized(mLock) {
+            removeUriPermissionsForPackage(packageName, userId, true, true);
+        }
+    }
+
+    /**
+     * @param uri This uri must NOT contain an embedded userId.
+     * @param toPackage Name of the target package whose uri is being released (if {@code null},
+     * uses calling uid)
+     * @param userId The userId in which the uri is to be resolved.
+     */
+    @Override
+    public void releasePersistableUriPermission(Uri uri, final int modeFlags,
+            @Nullable String toPackage, int userId) {
+
+        final int uid;
+        if (toPackage != null) {
+            mAmInternal.enforceCallingPermission(FORCE_PERSISTABLE_URI_PERMISSIONS,
+                    "releasePersistableUriPermission");
+            uid = mPmInternal.getPackageUid(toPackage, 0, userId);
+        } else {
+            enforceNotIsolatedCaller("releasePersistableUriPermission");
+            uid = Binder.getCallingUid();
+        }
+
+        Preconditions.checkFlagsArgument(modeFlags,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        synchronized (mLock) {
+            boolean persistChanged = false;
+
+            UriPermission exactPerm = findUriPermissionLocked(uid,
+                    new GrantUri(userId, uri, false));
+            UriPermission prefixPerm = findUriPermissionLocked(uid,
+                    new GrantUri(userId, uri, true));
+            if (exactPerm == null && prefixPerm == null && toPackage == null) {
+                throw new SecurityException("No permission grants found for UID " + uid
+                        + " and Uri " + uri.toSafeString());
+            }
+
+            if (exactPerm != null) {
+                persistChanged |= exactPerm.releasePersistableModes(modeFlags);
+                removeUriPermissionIfNeeded(exactPerm);
+            }
+            if (prefixPerm != null) {
+                persistChanged |= prefixPerm.releasePersistableModes(modeFlags);
+                removeUriPermissionIfNeeded(prefixPerm);
+            }
+
+            if (persistChanged) {
+                schedulePersistUriGrants();
+            }
+        }
+    }
+
+    /**
+     * Remove any {@link UriPermission} granted <em>from</em> or <em>to</em> the
+     * given package.
+     *
+     * @param packageName Package name to match, or {@code null} to apply to all
+     *            packages.
+     * @param userHandle User to match, or {@link UserHandle#USER_ALL} to apply
+     *            to all users.
+     * @param persistable If persistable grants should be removed.
+     * @param targetOnly When {@code true}, only remove permissions where the app is the target,
+     * not source.
+     */
+    void removeUriPermissionsForPackage(
+            String packageName, int userHandle, boolean persistable, boolean targetOnly) {
+        if (userHandle == UserHandle.USER_ALL && packageName == null) {
+            throw new IllegalArgumentException("Must narrow by either package or user");
+        }
+
+        boolean persistChanged = false;
+
+        int N = mGrantedUriPermissions.size();
+        for (int i = 0; i < N; i++) {
+            final int targetUid = mGrantedUriPermissions.keyAt(i);
+            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
+
+            // Only inspect grants matching user
+            if (userHandle == UserHandle.USER_ALL
+                    || userHandle == UserHandle.getUserId(targetUid)) {
+                for (Iterator<UriPermission> it = perms.values().iterator(); it.hasNext();) {
+                    final UriPermission perm = it.next();
+
+                    // Only inspect grants matching package
+                    if (packageName == null || (!targetOnly && perm.sourcePkg.equals(packageName))
+                            || perm.targetPkg.equals(packageName)) {
+                        // Hacky solution as part of fixing a security bug; ignore
+                        // grants associated with DownloadManager so we don't have
+                        // to immediately launch it to regrant the permissions
+                        if (Downloads.Impl.AUTHORITY.equals(perm.uri.uri.getAuthority())
+                                && !persistable) continue;
+
+                        persistChanged |= perm.revokeModes(persistable
+                                ? ~0 : ~Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, true);
+
+                        // Only remove when no modes remain; any persisted grants
+                        // will keep this alive.
+                        if (perm.modeFlags == 0) {
+                            it.remove();
+                        }
+                    }
+                }
+
+                if (perms.isEmpty()) {
+                    mGrantedUriPermissions.remove(targetUid);
+                    N--;
+                    i--;
+                }
+            }
+        }
+
+        if (persistChanged) {
+            schedulePersistUriGrants();
+        }
+    }
+
+    /** Returns if the ContentProvider has granted a uri to callingUid */
+    boolean checkAuthorityGrants(int callingUid, ProviderInfo cpi, int userId, boolean checkUser) {
+        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+        if (perms != null) {
+            for (int i = perms.size() - 1; i >= 0; i--) {
+                GrantUri grantUri = perms.keyAt(i);
+                if (grantUri.sourceUserId == userId || !checkUser) {
+                    if (matchesProvider(grantUri.uri, cpi)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /** Returns true if the uri authority is one of the authorities specified in the provider. */
+    private boolean matchesProvider(Uri uri, ProviderInfo cpi) {
+        String uriAuth = uri.getAuthority();
+        String cpiAuth = cpi.authority;
+        if (cpiAuth.indexOf(';') == -1) {
+            return cpiAuth.equals(uriAuth);
+        }
+        String[] cpiAuths = cpiAuth.split(";");
+        int length = cpiAuths.length;
+        for (int i = 0; i < length; i++) {
+            if (cpiAuths[i].equals(uriAuth)) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Prune any older {@link UriPermission} for the given UID until outstanding
+     * persisted grants are below {@link #MAX_PERSISTED_URI_GRANTS}.
+     *
+     * @return if any mutations occured that require persisting.
+     */
+    private boolean maybePrunePersistedUriGrants(int uid) {
+        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid);
+        if (perms == null) return false;
+        if (perms.size() < MAX_PERSISTED_URI_GRANTS) return false;
+
+        final ArrayList<UriPermission> persisted = Lists.newArrayList();
+        for (UriPermission perm : perms.values()) {
+            if (perm.persistedModeFlags != 0) {
+                persisted.add(perm);
+            }
+        }
+
+        final int trimCount = persisted.size() - MAX_PERSISTED_URI_GRANTS;
+        if (trimCount <= 0) return false;
+
+        Collections.sort(persisted, new UriPermission.PersistedTimeComparator());
+        for (int i = 0; i < trimCount; i++) {
+            final UriPermission perm = persisted.get(i);
+
+            if (DEBUG) Slog.v(TAG, "Trimming grant created at " + perm.persistedCreateTime);
+
+            perm.releasePersistableModes(~0);
+            removeUriPermissionIfNeeded(perm);
+        }
+
+        return true;
+    }
+
+    /** Like checkGrantUriPermission, but takes an Intent. */
+    NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid,
+            String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) {
+        if (DEBUG) Slog.v(TAG,
+                "Checking URI perm to data=" + (intent != null ? intent.getData() : null)
+                        + " clip=" + (intent != null ? intent.getClipData() : null)
+                        + " from " + intent + "; flags=0x"
+                        + Integer.toHexString(intent != null ? intent.getFlags() : 0));
+
+        if (targetPkg == null) {
+            throw new NullPointerException("targetPkg");
+        }
+
+        if (intent == null) {
+            return null;
+        }
+        Uri data = intent.getData();
+        ClipData clip = intent.getClipData();
+        if (data == null && clip == null) {
+            return null;
+        }
+        // Default userId for uris in the intent (if they don't specify it themselves)
+        int contentUserHint = intent.getContentUserHint();
+        if (contentUserHint == UserHandle.USER_CURRENT) {
+            contentUserHint = UserHandle.getUserId(callingUid);
+        }
+        final IPackageManager pm = AppGlobals.getPackageManager();
+        int targetUid;
+        if (needed != null) {
+            targetUid = needed.targetUid;
+        } else {
+            try {
+                targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, targetUserId);
+            } catch (RemoteException ex) {
+                return null;
+            }
+            if (targetUid < 0) {
+                if (DEBUG) Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg
+                        + " on user " + targetUserId);
+                return null;
+            }
+        }
+        if (data != null) {
+            GrantUri grantUri = GrantUri.resolve(contentUserHint, data);
+            targetUid = checkGrantUriPermission(callingUid, targetPkg, grantUri, mode, targetUid);
+            if (targetUid > 0) {
+                if (needed == null) {
+                    needed = new NeededUriGrants(targetPkg, targetUid, mode);
+                }
+                needed.add(grantUri);
+            }
+        }
+        if (clip != null) {
+            for (int i=0; i<clip.getItemCount(); i++) {
+                Uri uri = clip.getItemAt(i).getUri();
+                if (uri != null) {
+                    GrantUri grantUri = GrantUri.resolve(contentUserHint, uri);
+                    targetUid = checkGrantUriPermission(callingUid, targetPkg,
+                            grantUri, mode, targetUid);
+                    if (targetUid > 0) {
+                        if (needed == null) {
+                            needed = new NeededUriGrants(targetPkg, targetUid, mode);
+                        }
+                        needed.add(grantUri);
+                    }
+                } else {
+                    Intent clipIntent = clip.getItemAt(i).getIntent();
+                    if (clipIntent != null) {
+                        NeededUriGrants newNeeded = checkGrantUriPermissionFromIntent(
+                                callingUid, targetPkg, clipIntent, mode, needed, targetUserId);
+                        if (newNeeded != null) {
+                            needed = newNeeded;
+                        }
+                    }
+                }
+            }
+        }
+
+        return needed;
+    }
+
+    void grantUriPermissionFromIntent(int callingUid,
+            String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId) {
+        NeededUriGrants needed = checkGrantUriPermissionFromIntent(callingUid, targetPkg,
+                intent, intent != null ? intent.getFlags() : 0, null, targetUserId);
+        if (needed == null) {
+            return;
+        }
+
+        grantUriPermissionUncheckedFromIntent(needed, owner);
+    }
+
+    void readGrantedUriPermissions() {
+        if (DEBUG) Slog.v(TAG, "readGrantedUriPermissions()");
+
+        final long now = System.currentTimeMillis();
+
+        FileInputStream fis = null;
+        try {
+            fis = mGrantFile.openRead();
+            final XmlPullParser in = Xml.newPullParser();
+            in.setInput(fis, StandardCharsets.UTF_8.name());
+
+            int type;
+            while ((type = in.next()) != END_DOCUMENT) {
+                final String tag = in.getName();
+                if (type == START_TAG) {
+                    if (TAG_URI_GRANT.equals(tag)) {
+                        final int sourceUserId;
+                        final int targetUserId;
+                        final int userHandle = readIntAttribute(in,
+                                ATTR_USER_HANDLE, UserHandle.USER_NULL);
+                        if (userHandle != UserHandle.USER_NULL) {
+                            // For backwards compatibility.
+                            sourceUserId = userHandle;
+                            targetUserId = userHandle;
+                        } else {
+                            sourceUserId = readIntAttribute(in, ATTR_SOURCE_USER_ID);
+                            targetUserId = readIntAttribute(in, ATTR_TARGET_USER_ID);
+                        }
+                        final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG);
+                        final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG);
+                        final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
+                        final boolean prefix = readBooleanAttribute(in, ATTR_PREFIX);
+                        final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS);
+                        final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now);
+
+                        // Sanity check that provider still belongs to source package
+                        // Both direct boot aware and unaware packages are fine as we
+                        // will do filtering at query time to avoid multiple parsing.
+                        final ProviderInfo pi = getProviderInfo(uri.getAuthority(), sourceUserId,
+                                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
+                        if (pi != null && sourcePkg.equals(pi.packageName)) {
+                            int targetUid = -1;
+                            try {
+                                targetUid = AppGlobals.getPackageManager().getPackageUid(
+                                        targetPkg, MATCH_UNINSTALLED_PACKAGES, targetUserId);
+                            } catch (RemoteException e) {
+                            }
+                            if (targetUid != -1) {
+                                final UriPermission perm = findOrCreateUriPermission(
+                                        sourcePkg, targetPkg, targetUid,
+                                        new GrantUri(sourceUserId, uri, prefix));
+                                perm.initPersistedModes(modeFlags, createdTime);
+                            }
+                        } else {
+                            Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
+                                    + " but instead found " + pi);
+                        }
+                    }
+                }
+            }
+        } catch (FileNotFoundException e) {
+            // Missing grants is okay
+        } catch (IOException e) {
+            Slog.wtf(TAG, "Failed reading Uri grants", e);
+        } catch (XmlPullParserException e) {
+            Slog.wtf(TAG, "Failed reading Uri grants", e);
+        } finally {
+            IoUtils.closeQuietly(fis);
+        }
+    }
+
+    private UriPermission findOrCreateUriPermission(String sourcePkg,
+            String targetPkg, int targetUid, GrantUri grantUri) {
+        ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
+        if (targetUris == null) {
+            targetUris = Maps.newArrayMap();
+            mGrantedUriPermissions.put(targetUid, targetUris);
+        }
+
+        UriPermission perm = targetUris.get(grantUri);
+        if (perm == null) {
+            perm = new UriPermission(sourcePkg, targetPkg, targetUid, grantUri);
+            targetUris.put(grantUri, perm);
+        }
+
+        return perm;
+    }
+
+    private void grantUriPermissionUnchecked(int targetUid, String targetPkg, GrantUri grantUri,
+            final int modeFlags, UriPermissionOwner owner) {
+        if (!Intent.isAccessUriMode(modeFlags)) {
+            return;
+        }
+
+        // So here we are: the caller has the assumed permission to the uri, and the target doesn't.
+        // Let's now give this to the target.
+
+        if (DEBUG) Slog.v(TAG,
+                "Granting " + targetPkg + "/" + targetUid + " permission to " + grantUri);
+
+        final String authority = grantUri.uri.getAuthority();
+        final ProviderInfo pi = getProviderInfo(authority, grantUri.sourceUserId,
+                MATCH_DEBUG_TRIAGED_MISSING);
+        if (pi == null) {
+            Slog.w(TAG, "No content provider found for grant: " + grantUri.toSafeString());
+            return;
+        }
+
+        if ((modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0) {
+            grantUri.prefix = true;
+        }
+        final UriPermission perm = findOrCreateUriPermission(
+                pi.packageName, targetPkg, targetUid, grantUri);
+        perm.grantModes(modeFlags, owner);
+    }
+
+    /** Like grantUriPermissionUnchecked, but takes an Intent. */
+    void grantUriPermissionUncheckedFromIntent(NeededUriGrants needed, UriPermissionOwner owner) {
+        if (needed == null) {
+            return;
+        }
+        for (int i=0; i<needed.size(); i++) {
+            GrantUri grantUri = needed.get(i);
+            grantUriPermissionUnchecked(needed.targetUid, needed.targetPkg,
+                    grantUri, needed.flags, owner);
+        }
+    }
+
+    void grantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
+            final int modeFlags, UriPermissionOwner owner, int targetUserId) {
+        if (targetPkg == null) {
+            throw new NullPointerException("targetPkg");
+        }
+        int targetUid;
+        final IPackageManager pm = AppGlobals.getPackageManager();
+        try {
+            targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, targetUserId);
+        } catch (RemoteException ex) {
+            return;
+        }
+
+        targetUid = checkGrantUriPermission(callingUid, targetPkg, grantUri, modeFlags, targetUid);
+        if (targetUid < 0) {
+            return;
+        }
+
+        grantUriPermissionUnchecked(targetUid, targetPkg, grantUri, modeFlags, owner);
+    }
+
+    void revokeUriPermission(String targetPackage, int callingUid, GrantUri grantUri,
+            final int modeFlags) {
+        if (DEBUG) Slog.v(TAG, "Revoking all granted permissions to " + grantUri);
+
+        final IPackageManager pm = AppGlobals.getPackageManager();
+        final String authority = grantUri.uri.getAuthority();
+        final ProviderInfo pi = getProviderInfo(authority, grantUri.sourceUserId,
+                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
+        if (pi == null) {
+            Slog.w(TAG, "No content provider found for permission revoke: "
+                    + grantUri.toSafeString());
+            return;
+        }
+
+        // Does the caller have this permission on the URI?
+        if (!checkHoldingPermissions(pm, pi, grantUri, callingUid, modeFlags)) {
+            // If they don't have direct access to the URI, then revoke any
+            // ownerless URI permissions that have been granted to them.
+            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+            if (perms != null) {
+                boolean persistChanged = false;
+                for (int i = perms.size()-1; i >= 0; i--) {
+                    final UriPermission perm = perms.valueAt(i);
+                    if (targetPackage != null && !targetPackage.equals(perm.targetPkg)) {
+                        continue;
+                    }
+                    if (perm.uri.sourceUserId == grantUri.sourceUserId
+                            && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
+                        if (DEBUG) Slog.v(TAG, "Revoking non-owned "
+                                + perm.targetUid + " permission to " + perm.uri);
+                        persistChanged |= perm.revokeModes(
+                                modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
+                        if (perm.modeFlags == 0) {
+                            perms.removeAt(i);
+                        }
+                    }
+                }
+                if (perms.isEmpty()) {
+                    mGrantedUriPermissions.remove(callingUid);
+                }
+                if (persistChanged) {
+                    schedulePersistUriGrants();
+                }
+            }
+            return;
+        }
+
+        boolean persistChanged = false;
+
+        // Go through all of the permissions and remove any that match.
+        for (int i = mGrantedUriPermissions.size()-1; i >= 0; i--) {
+            final int targetUid = mGrantedUriPermissions.keyAt(i);
+            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
+
+            for (int j = perms.size()-1; j >= 0; j--) {
+                final UriPermission perm = perms.valueAt(j);
+                if (targetPackage != null && !targetPackage.equals(perm.targetPkg)) {
+                    continue;
+                }
+                if (perm.uri.sourceUserId == grantUri.sourceUserId
+                        && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
+                    if (DEBUG) Slog.v(TAG,
+                            "Revoking " + perm.targetUid + " permission to " + perm.uri);
+                    persistChanged |= perm.revokeModes(
+                            modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION,
+                            targetPackage == null);
+                    if (perm.modeFlags == 0) {
+                        perms.removeAt(j);
+                    }
+                }
+            }
+
+            if (perms.isEmpty()) {
+                mGrantedUriPermissions.removeAt(i);
+            }
+        }
+
+        if (persistChanged) {
+            schedulePersistUriGrants();
+        }
+    }
+
+    /**
+     * Determine if UID is holding permissions required to access {@link Uri} in
+     * the given {@link ProviderInfo}. Final permission checking is always done
+     * in {@link ContentProvider}.
+     */
+    private boolean checkHoldingPermissions(
+            IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
+        if (DEBUG) Slog.v(TAG, "checkHoldingPermissions: uri=" + grantUri + " uid=" + uid);
+        if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
+            if (ActivityManager.checkComponentPermission(INTERACT_ACROSS_USERS, uid, -1, true)
+                    != PERMISSION_GRANTED) {
+                return false;
+            }
+        }
+        return checkHoldingPermissionsInternal(pm, pi, grantUri, uid, modeFlags, true);
+    }
+
+    private boolean checkHoldingPermissionsInternal(IPackageManager pm, ProviderInfo pi,
+            GrantUri grantUri, int uid, final int modeFlags, boolean considerUidPermissions) {
+        if (pi.applicationInfo.uid == uid) {
+            return true;
+        } else if (!pi.exported) {
+            return false;
+        }
+
+        boolean readMet = (modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0;
+        boolean writeMet = (modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
+        try {
+            // check if target holds top-level <provider> permissions
+            if (!readMet && pi.readPermission != null && considerUidPermissions
+                    && (pm.checkUidPermission(pi.readPermission, uid) == PERMISSION_GRANTED)) {
+                readMet = true;
+            }
+            if (!writeMet && pi.writePermission != null && considerUidPermissions
+                    && (pm.checkUidPermission(pi.writePermission, uid) == PERMISSION_GRANTED)) {
+                writeMet = true;
+            }
+
+            // track if unprotected read/write is allowed; any denied
+            // <path-permission> below removes this ability
+            boolean allowDefaultRead = pi.readPermission == null;
+            boolean allowDefaultWrite = pi.writePermission == null;
+
+            // check if target holds any <path-permission> that match uri
+            final PathPermission[] pps = pi.pathPermissions;
+            if (pps != null) {
+                final String path = grantUri.uri.getPath();
+                int i = pps.length;
+                while (i > 0 && (!readMet || !writeMet)) {
+                    i--;
+                    PathPermission pp = pps[i];
+                    if (pp.match(path)) {
+                        if (!readMet) {
+                            final String pprperm = pp.getReadPermission();
+                            if (DEBUG) Slog.v(TAG,
+                                    "Checking read perm for " + pprperm + " for " + pp.getPath()
+                                            + ": match=" + pp.match(path)
+                                            + " check=" + pm.checkUidPermission(pprperm, uid));
+                            if (pprperm != null) {
+                                if (considerUidPermissions && pm.checkUidPermission(pprperm, uid)
+                                        == PERMISSION_GRANTED) {
+                                    readMet = true;
+                                } else {
+                                    allowDefaultRead = false;
+                                }
+                            }
+                        }
+                        if (!writeMet) {
+                            final String ppwperm = pp.getWritePermission();
+                            if (DEBUG) Slog.v(TAG,
+                                    "Checking write perm " + ppwperm + " for " + pp.getPath()
+                                            + ": match=" + pp.match(path)
+                                            + " check=" + pm.checkUidPermission(ppwperm, uid));
+                            if (ppwperm != null) {
+                                if (considerUidPermissions && pm.checkUidPermission(ppwperm, uid)
+                                        == PERMISSION_GRANTED) {
+                                    writeMet = true;
+                                } else {
+                                    allowDefaultWrite = false;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            // grant unprotected <provider> read/write, if not blocked by
+            // <path-permission> above
+            if (allowDefaultRead) readMet = true;
+            if (allowDefaultWrite) writeMet = true;
+
+        } catch (RemoteException e) {
+            return false;
+        }
+
+        return readMet && writeMet;
+    }
+
+    private void removeUriPermissionIfNeeded(UriPermission perm) {
+        if (perm.modeFlags != 0) {
+            return;
+        }
+        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(
+                perm.targetUid);
+        if (perms == null) {
+            return;
+        }
+        if (DEBUG) Slog.v(TAG, "Removing " + perm.targetUid + " permission to " + perm.uri);
+
+        perms.remove(perm.uri);
+        if (perms.isEmpty()) {
+            mGrantedUriPermissions.remove(perm.targetUid);
+        }
+    }
+
+    private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) {
+        final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
+        if (targetUris != null) {
+            return targetUris.get(grantUri);
+        }
+        return null;
+    }
+
+    private void schedulePersistUriGrants() {
+        if (!mH.hasMessages(PERSIST_URI_GRANTS_MSG)) {
+            mH.sendMessageDelayed(mH.obtainMessage(PERSIST_URI_GRANTS_MSG),
+                    10 * DateUtils.SECOND_IN_MILLIS);
+        }
+    }
+
+    private void enforceNotIsolatedCaller(String caller) {
+        if (UserHandle.isIsolated(Binder.getCallingUid())) {
+            throw new SecurityException("Isolated process not allowed to call " + caller);
+        }
+    }
+
+    private ProviderInfo getProviderInfo(String authority, int userHandle, int pmFlags) {
+        ProviderInfo pi = null;
+        try {
+            pi = AppGlobals.getPackageManager().resolveContentProvider(
+                    authority, PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags,
+                    userHandle);
+        } catch (RemoteException ex) {
+        }
+        return pi;
+    }
+
+    /**
+     * Check if the targetPkg can be granted permission to access uri by
+     * the callingUid using the given modeFlags.  Throws a security exception
+     * if callingUid is not allowed to do this.  Returns the uid of the target
+     * if the URI permission grant should be performed; returns -1 if it is not
+     * needed (for example targetPkg already has permission to access the URI).
+     * If you already know the uid of the target, you can supply it in
+     * lastTargetUid else set that to -1.
+     */
+    int checkGrantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
+            final int modeFlags, int lastTargetUid) {
+        if (!Intent.isAccessUriMode(modeFlags)) {
+            return -1;
+        }
+
+        if (targetPkg != null) {
+            if (DEBUG) Slog.v(TAG, "Checking grant " + targetPkg + " permission to " + grantUri);
+        }
+
+        final IPackageManager pm = AppGlobals.getPackageManager();
+
+        // If this is not a content: uri, we can't do anything with it.
+        if (!ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) {
+            if (DEBUG) Slog.v(TAG, "Can't grant URI permission for non-content URI: " + grantUri);
+            return -1;
+        }
+
+        // Bail early if system is trying to hand out permissions directly; it
+        // must always grant permissions on behalf of someone explicit.
+        final int callingAppId = UserHandle.getAppId(callingUid);
+        if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) {
+            if ("com.android.settings.files".equals(grantUri.uri.getAuthority())) {
+                // Exempted authority for
+                // 1. cropping user photos and sharing a generated license html
+                //    file in Settings app
+                // 2. sharing a generated license html file in TvSettings app
+            } else {
+                Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
+                        + " grant to " + grantUri + "; use startActivityAsCaller() instead");
+                return -1;
+            }
+        }
+
+        final String authority = grantUri.uri.getAuthority();
+        final ProviderInfo pi = getProviderInfo(authority, grantUri.sourceUserId,
+                MATCH_DEBUG_TRIAGED_MISSING);
+        if (pi == null) {
+            Slog.w(TAG, "No content provider found for permission check: " +
+                    grantUri.uri.toSafeString());
+            return -1;
+        }
+
+        int targetUid = lastTargetUid;
+        if (targetUid < 0 && targetPkg != null) {
+            try {
+                targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
+                        UserHandle.getUserId(callingUid));
+                if (targetUid < 0) {
+                    if (DEBUG) Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg);
+                    return -1;
+                }
+            } catch (RemoteException ex) {
+                return -1;
+            }
+        }
+
+        // Figure out the value returned when access is allowed
+        final int allowedResult;
+        if ((modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0) {
+            // If we're extending a persistable grant, then we need to return
+            // "targetUid" so that we always create a grant data structure to
+            // support take/release APIs
+            allowedResult = targetUid;
+        } else {
+            // Otherwise, we can return "-1" to indicate that no grant data
+            // structures need to be created
+            allowedResult = -1;
+        }
+
+        if (targetUid >= 0) {
+            // First...  does the target actually need this permission?
+            if (checkHoldingPermissions(pm, pi, grantUri, targetUid, modeFlags)) {
+                // No need to grant the target this permission.
+                if (DEBUG) Slog.v(TAG,
+                        "Target " + targetPkg + " already has full permission to " + grantUri);
+                return allowedResult;
+            }
+        } else {
+            // First...  there is no target package, so can anyone access it?
+            boolean allowed = pi.exported;
+            if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+                if (pi.readPermission != null) {
+                    allowed = false;
+                }
+            }
+            if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+                if (pi.writePermission != null) {
+                    allowed = false;
+                }
+            }
+            if (pi.pathPermissions != null) {
+                final int N = pi.pathPermissions.length;
+                for (int i=0; i<N; i++) {
+                    if (pi.pathPermissions[i] != null
+                            && pi.pathPermissions[i].match(grantUri.uri.getPath())) {
+                        if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+                            if (pi.pathPermissions[i].getReadPermission() != null) {
+                                allowed = false;
+                            }
+                        }
+                        if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+                            if (pi.pathPermissions[i].getWritePermission() != null) {
+                                allowed = false;
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+            if (allowed) {
+                return allowedResult;
+            }
+        }
+
+        /* There is a special cross user grant if:
+         * - The target is on another user.
+         * - Apps on the current user can access the uri without any uid permissions.
+         * In this case, we grant a uri permission, even if the ContentProvider does not normally
+         * grant uri permissions.
+         */
+        boolean specialCrossUserGrant = UserHandle.getUserId(targetUid) != grantUri.sourceUserId
+                && checkHoldingPermissionsInternal(pm, pi, grantUri, callingUid,
+                modeFlags, false /*without considering the uid permissions*/);
+
+        // Second...  is the provider allowing granting of URI permissions?
+        if (!specialCrossUserGrant) {
+            if (!pi.grantUriPermissions) {
+                throw new SecurityException("Provider " + pi.packageName
+                        + "/" + pi.name
+                        + " does not allow granting of Uri permissions (uri "
+                        + grantUri + ")");
+            }
+            if (pi.uriPermissionPatterns != null) {
+                final int N = pi.uriPermissionPatterns.length;
+                boolean allowed = false;
+                for (int i=0; i<N; i++) {
+                    if (pi.uriPermissionPatterns[i] != null
+                            && pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) {
+                        allowed = true;
+                        break;
+                    }
+                }
+                if (!allowed) {
+                    throw new SecurityException("Provider " + pi.packageName
+                            + "/" + pi.name
+                            + " does not allow granting of permission to path of Uri "
+                            + grantUri);
+                }
+            }
+        }
+
+        // Third...  does the caller itself have permission to access this uri?
+        if (!checkHoldingPermissions(pm, pi, grantUri, callingUid, modeFlags)) {
+            // Require they hold a strong enough Uri permission
+            if (!checkUriPermission(grantUri, callingUid, modeFlags)) {
+                if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(pi.readPermission)) {
+                    throw new SecurityException(
+                            "UID " + callingUid + " does not have permission to " + grantUri
+                                    + "; you could obtain access using ACTION_OPEN_DOCUMENT "
+                                    + "or related APIs");
+                } else {
+                    throw new SecurityException(
+                            "UID " + callingUid + " does not have permission to " + grantUri);
+                }
+            }
+        }
+        return targetUid;
+    }
+
+    /**
+     * @param userId The userId in which the uri is to be resolved.
+     */
+    int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri, int modeFlags,
+            int userId) {
+        return checkGrantUriPermission(callingUid, targetPkg,
+                new GrantUri(userId, uri, false), modeFlags, -1);
+    }
+
+    boolean checkUriPermission(GrantUri grantUri, int uid, final int modeFlags) {
+        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
+        final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE
+                : UriPermission.STRENGTH_OWNED;
+
+        // Root gets to do everything.
+        if (uid == 0) {
+            return true;
+        }
+
+        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid);
+        if (perms == null) return false;
+
+        // First look for exact match
+        final UriPermission exactPerm = perms.get(grantUri);
+        if (exactPerm != null && exactPerm.getStrength(modeFlags) >= minStrength) {
+            return true;
+        }
+
+        // No exact match, look for prefixes
+        final int N = perms.size();
+        for (int i = 0; i < N; i++) {
+            final UriPermission perm = perms.valueAt(i);
+            if (perm.uri.prefix && grantUri.uri.isPathPrefixMatch(perm.uri.uri)
+                    && perm.getStrength(modeFlags) >= minStrength) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private void writeGrantedUriPermissions() {
+        if (DEBUG) Slog.v(TAG, "writeGrantedUriPermissions()");
+
+        final long startTime = SystemClock.uptimeMillis();
+
+        // Snapshot permissions so we can persist without lock
+        ArrayList<UriPermission.Snapshot> persist = Lists.newArrayList();
+        synchronized (this) {
+            final int size = mGrantedUriPermissions.size();
+            for (int i = 0; i < size; i++) {
+                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
+                for (UriPermission perm : perms.values()) {
+                    if (perm.persistedModeFlags != 0) {
+                        persist.add(perm.snapshot());
+                    }
+                }
+            }
+        }
+
+        FileOutputStream fos = null;
+        try {
+            fos = mGrantFile.startWrite(startTime);
+
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(fos, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+            out.startTag(null, TAG_URI_GRANTS);
+            for (UriPermission.Snapshot perm : persist) {
+                out.startTag(null, TAG_URI_GRANT);
+                writeIntAttribute(out, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId);
+                writeIntAttribute(out, ATTR_TARGET_USER_ID, perm.targetUserId);
+                out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg);
+                out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
+                out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri));
+                writeBooleanAttribute(out, ATTR_PREFIX, perm.uri.prefix);
+                writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags);
+                writeLongAttribute(out, ATTR_CREATED_TIME, perm.persistedCreateTime);
+                out.endTag(null, TAG_URI_GRANT);
+            }
+            out.endTag(null, TAG_URI_GRANTS);
+            out.endDocument();
+
+            mGrantFile.finishWrite(fos);
+        } catch (IOException e) {
+            if (fos != null) {
+                mGrantFile.failWrite(fos);
+            }
+        }
+    }
+
+    final class H extends Handler {
+        static final int PERSIST_URI_GRANTS_MSG = 1;
+
+        public H(Looper looper) {
+            super(looper, null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case PERSIST_URI_GRANTS_MSG: {
+                    writeGrantedUriPermissions();
+                    break;
+                }
+            }
+        }
+    }
+
+    final class LocalService implements UriGrantsManagerInternal {
+
+        @Override
+        public void removeUriPermissionIfNeeded(UriPermission perm) {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.removeUriPermissionIfNeeded(perm);
+            }
+        }
+
+        @Override
+        public void grantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
+                int modeFlags, UriPermissionOwner owner, int targetUserId) {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.grantUriPermission(
+                        callingUid, targetPkg, grantUri, modeFlags, owner, targetUserId);
+            }
+        }
+
+        @Override
+        public void revokeUriPermission(String targetPackage, int callingUid, GrantUri grantUri,
+                int modeFlags) {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.revokeUriPermission(
+                        targetPackage, callingUid, grantUri, modeFlags);
+            }
+        }
+
+        @Override
+        public boolean checkUriPermission(GrantUri grantUri, int uid, int modeFlags) {
+            synchronized (mLock) {
+                return UriGrantsManagerService.this.checkUriPermission(grantUri, uid, modeFlags);
+            }
+        }
+
+        @Override
+        public int checkGrantUriPermission(int callingUid, String targetPkg, GrantUri uri,
+                int modeFlags, int userId) {
+            synchronized (mLock) {
+                return UriGrantsManagerService.this.checkGrantUriPermission(
+                        callingUid, targetPkg, uri, modeFlags, userId);
+            }
+        }
+
+        @Override
+        public int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri, int modeFlags,
+                int userId) {
+            enforceNotIsolatedCaller("checkGrantUriPermission");
+            synchronized (mLock) {
+                return UriGrantsManagerService.this.checkGrantUriPermission(
+                        callingUid, targetPkg, uri, modeFlags, userId);
+            }
+        }
+
+        @Override
+        public NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid, String targetPkg,
+                Intent intent, int mode, NeededUriGrants needed, int targetUserId) {
+            synchronized (mLock) {
+                return UriGrantsManagerService.this.checkGrantUriPermissionFromIntent(
+                        callingUid, targetPkg, intent, mode, needed, targetUserId);
+            }
+        }
+
+        @Override
+        public void grantUriPermissionFromIntent(int callingUid, String targetPkg, Intent intent,
+                int targetUserId) {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.grantUriPermissionFromIntent(
+                        callingUid, targetPkg, intent, null, targetUserId);
+            }
+        }
+
+        @Override
+        public void grantUriPermissionFromIntent(int callingUid, String targetPkg, Intent intent,
+                UriPermissionOwner owner, int targetUserId) {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.grantUriPermissionFromIntent(
+                        callingUid, targetPkg, intent, owner, targetUserId);
+            }
+        }
+
+        @Override
+        public void grantUriPermissionUncheckedFromIntent(NeededUriGrants needed,
+                UriPermissionOwner owner) {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.grantUriPermissionUncheckedFromIntent(needed, owner);
+            }
+        }
+
+        @Override
+        public void onSystemReady() {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.readGrantedUriPermissions();
+            }
+        }
+
+        @Override
+        public void onActivityManagerInternalAdded() {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.onActivityManagerInternalAdded();
+            }
+        }
+
+        @Override
+        public IBinder newUriPermissionOwner(String name) {
+            enforceNotIsolatedCaller("newUriPermissionOwner");
+            synchronized(mLock) {
+                UriPermissionOwner owner = new UriPermissionOwner(this, name);
+                return owner.getExternalToken();
+            }
+        }
+
+        @Override
+        public void removeUriPermissionsForPackage(String packageName, int userHandle,
+                boolean persistable, boolean targetOnly) {
+            synchronized(mLock) {
+                UriGrantsManagerService.this.removeUriPermissionsForPackage(
+                        packageName, userHandle, persistable, targetOnly);
+            }
+        }
+
+        @Override
+        public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId) {
+            synchronized(mLock) {
+                final UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+                if (owner == null) {
+                    throw new IllegalArgumentException("Unknown owner: " + token);
+                }
+
+                if (uri == null) {
+                    owner.removeUriPermissions(mode);
+                } else {
+                    final boolean prefix = (mode & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0;
+                    owner.removeUriPermission(new GrantUri(userId, uri, prefix), mode);
+                }
+            }
+        }
+
+        @Override
+        public boolean checkAuthorityGrants(int callingUid, ProviderInfo cpi, int userId,
+                boolean checkUser) {
+            synchronized(mLock) {
+                return UriGrantsManagerService.this.checkAuthorityGrants(
+                        callingUid, cpi, userId, checkUser);
+            }
+        }
+
+        @Override
+        public void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) {
+            synchronized(mLock) {
+                boolean needSep = false;
+                boolean printedAnything = false;
+                if (mGrantedUriPermissions.size() > 0) {
+                    boolean printed = false;
+                    int dumpUid = -2;
+                    if (dumpPackage != null) {
+                        try {
+                            dumpUid = mContext.getPackageManager().getPackageUidAsUser(dumpPackage,
+                                    MATCH_ANY_USER, 0);
+                        } catch (PackageManager.NameNotFoundException e) {
+                            dumpUid = -1;
+                        }
+                    }
+                    for (int i = 0; i < mGrantedUriPermissions.size(); i++) {
+                        int uid = mGrantedUriPermissions.keyAt(i);
+                        if (dumpUid >= -1 && UserHandle.getAppId(uid) != dumpUid) {
+                            continue;
+                        }
+                        final ArrayMap<GrantUri, UriPermission> perms =
+                                mGrantedUriPermissions.valueAt(i);
+                        if (!printed) {
+                            if (needSep) pw.println();
+                            needSep = true;
+                            pw.println("  Granted Uri Permissions:");
+                            printed = true;
+                            printedAnything = true;
+                        }
+                        pw.print("  * UID "); pw.print(uid); pw.println(" holds:");
+                        for (UriPermission perm : perms.values()) {
+                            pw.print("    "); pw.println(perm);
+                            if (dumpAll) {
+                                perm.dump(pw, "      ");
+                            }
+                        }
+                    }
+                }
+
+                if (!printedAnything) {
+                    pw.println("  (nothing)");
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/uri/UriPermission.java b/services/core/java/com/android/server/uri/UriPermission.java
new file mode 100644
index 0000000..bd6348a
--- /dev/null
+++ b/services/core/java/com/android/server/uri/UriPermission.java
@@ -0,0 +1,394 @@
+/*
+ * 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.uri;
+
+import android.app.GrantedUriPermission;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.google.android.collect.Sets;
+
+import java.io.PrintWriter;
+import java.util.Comparator;
+
+/**
+ * Description of a permission granted to an app to access a particular URI.
+ *
+ * CTS tests for this functionality can be run with "runtest cts-appsecurity".
+ *
+ * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
+ *      src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+ */
+final class UriPermission {
+    private static final String TAG = "UriPermission";
+
+    public static final int STRENGTH_NONE = 0;
+    public static final int STRENGTH_OWNED = 1;
+    public static final int STRENGTH_GLOBAL = 2;
+    public static final int STRENGTH_PERSISTABLE = 3;
+
+    final int targetUserId;
+    final String sourcePkg;
+    final String targetPkg;
+
+    /** Cached UID of {@link #targetPkg}; should not be persisted */
+    final int targetUid;
+
+    final GrantUri uri;
+
+    /**
+     * Allowed modes. All permission enforcement should use this field. Must
+     * always be a combination of {@link #ownedModeFlags},
+     * {@link #globalModeFlags}, {@link #persistableModeFlags}, and
+     * {@link #persistedModeFlags}. Mutations <em>must</em> only be performed by
+     * the owning class.
+     */
+    int modeFlags = 0;
+
+    /** Allowed modes with active owner. */
+    int ownedModeFlags = 0;
+    /** Allowed modes without explicit owner. */
+    int globalModeFlags = 0;
+    /** Allowed modes that have been offered for possible persisting. */
+    int persistableModeFlags = 0;
+
+    /** Allowed modes that should be persisted across device boots. */
+    int persistedModeFlags = 0;
+
+    /**
+     * Timestamp when {@link #persistedModeFlags} was first defined in
+     * {@link System#currentTimeMillis()} time base.
+     */
+    long persistedCreateTime = INVALID_TIME;
+
+    private static final long INVALID_TIME = Long.MIN_VALUE;
+
+    private ArraySet<UriPermissionOwner> mReadOwners;
+    private ArraySet<UriPermissionOwner> mWriteOwners;
+
+    private String stringName;
+
+    UriPermission(String sourcePkg, String targetPkg, int targetUid, GrantUri uri) {
+        this.targetUserId = UserHandle.getUserId(targetUid);
+        this.sourcePkg = sourcePkg;
+        this.targetPkg = targetPkg;
+        this.targetUid = targetUid;
+        this.uri = uri;
+    }
+
+    private void updateModeFlags() {
+        final int oldModeFlags = modeFlags;
+        modeFlags = ownedModeFlags | globalModeFlags | persistableModeFlags | persistedModeFlags;
+
+        if (Log.isLoggable(TAG, Log.VERBOSE) && (modeFlags != oldModeFlags)) {
+            Slog.d(TAG,
+                    "Permission for " + targetPkg + " to " + uri + " is changing from 0x"
+                            + Integer.toHexString(oldModeFlags) + " to 0x"
+                            + Integer.toHexString(modeFlags) + " via calling UID "
+                            + Binder.getCallingUid() + " PID " + Binder.getCallingPid(),
+                    new Throwable());
+        }
+    }
+
+    /**
+     * Initialize persisted modes as read from file. This doesn't issue any
+     * global or owner grants.
+     */
+    void initPersistedModes(int modeFlags, long createdTime) {
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        persistableModeFlags = modeFlags;
+        persistedModeFlags = modeFlags;
+        persistedCreateTime = createdTime;
+
+        updateModeFlags();
+    }
+
+    void grantModes(int modeFlags, UriPermissionOwner owner) {
+        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        if (persistable) {
+            persistableModeFlags |= modeFlags;
+        }
+
+        if (owner == null) {
+            globalModeFlags |= modeFlags;
+        } else {
+            if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+                addReadOwner(owner);
+            }
+            if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+                addWriteOwner(owner);
+            }
+        }
+
+        updateModeFlags();
+    }
+
+    /**
+     * @return if mode changes should trigger persisting.
+     */
+    boolean takePersistableModes(int modeFlags) {
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        if ((modeFlags & persistableModeFlags) != modeFlags) {
+            Slog.w(TAG, "Requested flags 0x"
+                    + Integer.toHexString(modeFlags) + ", but only 0x"
+                    + Integer.toHexString(persistableModeFlags) + " are allowed");
+            return false;
+        }
+
+        final int before = persistedModeFlags;
+        persistedModeFlags |= (persistableModeFlags & modeFlags);
+
+        if (persistedModeFlags != 0) {
+            persistedCreateTime = System.currentTimeMillis();
+        }
+
+        updateModeFlags();
+        return persistedModeFlags != before;
+    }
+
+    boolean releasePersistableModes(int modeFlags) {
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        final int before = persistedModeFlags;
+
+        persistableModeFlags &= ~modeFlags;
+        persistedModeFlags &= ~modeFlags;
+
+        if (persistedModeFlags == 0) {
+            persistedCreateTime = INVALID_TIME;
+        }
+
+        updateModeFlags();
+        return persistedModeFlags != before;
+    }
+
+    /**
+     * @return if mode changes should trigger persisting.
+     */
+    boolean revokeModes(int modeFlags, boolean includingOwners) {
+        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        final int before = persistedModeFlags;
+
+        if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+            if (persistable) {
+                persistableModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+                persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+            }
+            globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+            if (mReadOwners != null && includingOwners) {
+                ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+                for (UriPermissionOwner r : mReadOwners) {
+                    r.removeReadPermission(this);
+                }
+                mReadOwners = null;
+            }
+        }
+        if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+            if (persistable) {
+                persistableModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+                persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+            }
+            globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+            if (mWriteOwners != null && includingOwners) {
+                ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+                for (UriPermissionOwner r : mWriteOwners) {
+                    r.removeWritePermission(this);
+                }
+                mWriteOwners = null;
+            }
+        }
+
+        if (persistedModeFlags == 0) {
+            persistedCreateTime = INVALID_TIME;
+        }
+
+        updateModeFlags();
+        return persistedModeFlags != before;
+    }
+
+    /**
+     * Return strength of this permission grant for the given flags.
+     */
+    public int getStrength(int modeFlags) {
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        if ((persistableModeFlags & modeFlags) == modeFlags) {
+            return STRENGTH_PERSISTABLE;
+        } else if ((globalModeFlags & modeFlags) == modeFlags) {
+            return STRENGTH_GLOBAL;
+        } else if ((ownedModeFlags & modeFlags) == modeFlags) {
+            return STRENGTH_OWNED;
+        } else {
+            return STRENGTH_NONE;
+        }
+    }
+
+    private void addReadOwner(UriPermissionOwner owner) {
+        if (mReadOwners == null) {
+            mReadOwners = Sets.newArraySet();
+            ownedModeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION;
+            updateModeFlags();
+        }
+        if (mReadOwners.add(owner)) {
+            owner.addReadPermission(this);
+        }
+    }
+
+    /**
+     * Remove given read owner, updating {@Link #modeFlags} as needed.
+     */
+    void removeReadOwner(UriPermissionOwner owner) {
+        if (!mReadOwners.remove(owner)) {
+            Slog.wtf(TAG, "Unknown read owner " + owner + " in " + this);
+        }
+        if (mReadOwners.size() == 0) {
+            mReadOwners = null;
+            ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+            updateModeFlags();
+        }
+    }
+
+    private void addWriteOwner(UriPermissionOwner owner) {
+        if (mWriteOwners == null) {
+            mWriteOwners = Sets.newArraySet();
+            ownedModeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+            updateModeFlags();
+        }
+        if (mWriteOwners.add(owner)) {
+            owner.addWritePermission(this);
+        }
+    }
+
+    /**
+     * Remove given write owner, updating {@Link #modeFlags} as needed.
+     */
+    void removeWriteOwner(UriPermissionOwner owner) {
+        if (!mWriteOwners.remove(owner)) {
+            Slog.wtf(TAG, "Unknown write owner " + owner + " in " + this);
+        }
+        if (mWriteOwners.size() == 0) {
+            mWriteOwners = null;
+            ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+            updateModeFlags();
+        }
+    }
+
+    @Override
+    public String toString() {
+        if (stringName != null) {
+            return stringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("UriPermission{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        sb.append(uri);
+        sb.append('}');
+        return stringName = sb.toString();
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix);
+        pw.print("targetUserId=" + targetUserId);
+        pw.print(" sourcePkg=" + sourcePkg);
+        pw.println(" targetPkg=" + targetPkg);
+
+        pw.print(prefix);
+        pw.print("mode=0x" + Integer.toHexString(modeFlags));
+        pw.print(" owned=0x" + Integer.toHexString(ownedModeFlags));
+        pw.print(" global=0x" + Integer.toHexString(globalModeFlags));
+        pw.print(" persistable=0x" + Integer.toHexString(persistableModeFlags));
+        pw.print(" persisted=0x" + Integer.toHexString(persistedModeFlags));
+        if (persistedCreateTime != INVALID_TIME) {
+            pw.print(" persistedCreate=" + persistedCreateTime);
+        }
+        pw.println();
+
+        if (mReadOwners != null) {
+            pw.print(prefix);
+            pw.println("readOwners:");
+            for (UriPermissionOwner owner : mReadOwners) {
+                pw.print(prefix);
+                pw.println("  * " + owner);
+            }
+        }
+        if (mWriteOwners != null) {
+            pw.print(prefix);
+            pw.println("writeOwners:");
+            for (UriPermissionOwner owner : mReadOwners) {
+                pw.print(prefix);
+                pw.println("  * " + owner);
+            }
+        }
+    }
+
+    public static class PersistedTimeComparator implements Comparator<UriPermission> {
+        @Override
+        public int compare(UriPermission lhs, UriPermission rhs) {
+            return Long.compare(lhs.persistedCreateTime, rhs.persistedCreateTime);
+        }
+    }
+
+    /**
+     * Snapshot of {@link UriPermission} with frozen
+     * {@link UriPermission#persistedModeFlags} state.
+     */
+    public static class Snapshot {
+        final int targetUserId;
+        final String sourcePkg;
+        final String targetPkg;
+        final GrantUri uri;
+        final int persistedModeFlags;
+        final long persistedCreateTime;
+
+        private Snapshot(UriPermission perm) {
+            this.targetUserId = perm.targetUserId;
+            this.sourcePkg = perm.sourcePkg;
+            this.targetPkg = perm.targetPkg;
+            this.uri = perm.uri;
+            this.persistedModeFlags = perm.persistedModeFlags;
+            this.persistedCreateTime = perm.persistedCreateTime;
+        }
+    }
+
+    public Snapshot snapshot() {
+        return new Snapshot(this);
+    }
+
+    public android.content.UriPermission buildPersistedPublicApiObject() {
+        return new android.content.UriPermission(uri.uri, persistedModeFlags, persistedCreateTime);
+    }
+
+    public GrantedUriPermission buildGrantedUriPermission() {
+        return new GrantedUriPermission(uri.uri, targetPkg);
+    }
+}
diff --git a/services/core/java/com/android/server/uri/UriPermissionOwner.java b/services/core/java/com/android/server/uri/UriPermissionOwner.java
new file mode 100644
index 0000000..f814dc6
--- /dev/null
+++ b/services/core/java/com/android/server/uri/UriPermissionOwner.java
@@ -0,0 +1,168 @@
+/*
+ * 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.uri;
+
+import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
+import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.UriPermissionOwnerProto;
+import com.google.android.collect.Sets;
+
+import java.io.PrintWriter;
+import java.util.Iterator;
+
+public class UriPermissionOwner {
+    private final UriGrantsManagerInternal mService;
+    private final Object mOwner;
+
+    private Binder externalToken;
+
+    private ArraySet<UriPermission> mReadPerms;
+    private ArraySet<UriPermission> mWritePerms;
+
+    class ExternalToken extends Binder {
+        UriPermissionOwner getOwner() {
+            return UriPermissionOwner.this;
+        }
+    }
+
+    public UriPermissionOwner(UriGrantsManagerInternal service, Object owner) {
+        mService = service;
+        mOwner = owner;
+    }
+
+    public Binder getExternalToken() {
+        if (externalToken == null) {
+            externalToken = new ExternalToken();
+        }
+        return externalToken;
+    }
+
+    static UriPermissionOwner fromExternalToken(IBinder token) {
+        if (token instanceof ExternalToken) {
+            return ((ExternalToken)token).getOwner();
+        }
+        return null;
+    }
+
+    public void removeUriPermissions() {
+        removeUriPermissions(FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    void removeUriPermissions(int mode) {
+        removeUriPermission(null, mode);
+    }
+
+    void removeUriPermission(GrantUri grantUri, int mode) {
+        if ((mode & FLAG_GRANT_READ_URI_PERMISSION) != 0 && mReadPerms != null) {
+            Iterator<UriPermission> it = mReadPerms.iterator();
+            while (it.hasNext()) {
+                UriPermission perm = it.next();
+                if (grantUri == null || grantUri.equals(perm.uri)) {
+                    perm.removeReadOwner(this);
+                    mService.removeUriPermissionIfNeeded(perm);
+                    it.remove();
+                }
+            }
+            if (mReadPerms.isEmpty()) {
+                mReadPerms = null;
+            }
+        }
+        if ((mode & FLAG_GRANT_WRITE_URI_PERMISSION) != 0
+                && mWritePerms != null) {
+            Iterator<UriPermission> it = mWritePerms.iterator();
+            while (it.hasNext()) {
+                UriPermission perm = it.next();
+                if (grantUri == null || grantUri.equals(perm.uri)) {
+                    perm.removeWriteOwner(this);
+                    mService.removeUriPermissionIfNeeded(perm);
+                    it.remove();
+                }
+            }
+            if (mWritePerms.isEmpty()) {
+                mWritePerms = null;
+            }
+        }
+    }
+
+    public void addReadPermission(UriPermission perm) {
+        if (mReadPerms == null) {
+            mReadPerms = Sets.newArraySet();
+        }
+        mReadPerms.add(perm);
+    }
+
+    public void addWritePermission(UriPermission perm) {
+        if (mWritePerms == null) {
+            mWritePerms = Sets.newArraySet();
+        }
+        mWritePerms.add(perm);
+    }
+
+    public void removeReadPermission(UriPermission perm) {
+        mReadPerms.remove(perm);
+        if (mReadPerms.isEmpty()) {
+            mReadPerms = null;
+        }
+    }
+
+    public void removeWritePermission(UriPermission perm) {
+        mWritePerms.remove(perm);
+        if (mWritePerms.isEmpty()) {
+            mWritePerms = null;
+        }
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        if (mReadPerms != null) {
+            pw.print(prefix); pw.print("readUriPermissions="); pw.println(mReadPerms);
+        }
+        if (mWritePerms != null) {
+            pw.print(prefix); pw.print("writeUriPermissions="); pw.println(mWritePerms);
+        }
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(UriPermissionOwnerProto.OWNER, mOwner.toString());
+        if (mReadPerms != null) {
+            synchronized (mReadPerms) {
+                for (UriPermission p : mReadPerms) {
+                    p.uri.writeToProto(proto, UriPermissionOwnerProto.READ_PERMS);
+                }
+            }
+        }
+        if (mWritePerms != null) {
+            synchronized (mWritePerms) {
+                for (UriPermission p : mWritePerms) {
+                    p.uri.writeToProto(proto, UriPermissionOwnerProto.WRITE_PERMS);
+                }
+            }
+        }
+        proto.end(token);
+    }
+
+    @Override
+    public String toString() {
+        return mOwner.toString();
+    }
+}
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index 3c2d66b..a16dbb7 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -4,7 +4,6 @@
 
 import android.app.ActivityManagerInternal;
 import android.app.Vr2dDisplayProperties;
-import android.app.Service;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -13,18 +12,15 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.VirtualDisplay;
 import android.media.ImageReader;
-import android.os.Build;
 import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
 import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.service.vr.IPersistentVrStateCallbacks;
 import android.service.vr.IVrManager;
 import android.util.Log;
 import android.view.Surface;
 
+import com.android.server.LocalServices;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 /**
@@ -319,7 +315,7 @@
     }
 
     private void updateDisplayId(int displayId) {
-        mActivityManagerInternal.setVr2dDisplayId(displayId);
+        LocalServices.getService(ActivityTaskManagerInternal.class).setVr2dDisplayId(displayId);
         mWindowManagerInternal.setVr2dDisplayId(displayId);
     }
 
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index faa197e..5c45afc 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -18,14 +18,13 @@
 import static android.view.Display.INVALID_DISPLAY;
 
 import android.Manifest;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityManagerInternal.ScreenObserver;
+import android.annotation.NonNull;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.INotificationManager;
-import android.app.Vr2dDisplayProperties;
 import android.app.NotificationManager;
-import android.annotation.NonNull;
+import android.app.Vr2dDisplayProperties;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -59,30 +58,30 @@
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
-
-import com.android.server.FgThread;
-import com.android.server.wm.WindowManagerInternal;
 import android.view.inputmethod.InputMethodManagerInternal;
 
 import com.android.internal.R;
 import com.android.internal.util.DumpUtils;
+import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.SystemService;
-import com.android.server.utils.ManagedApplicationService.PendingEvent;
-import com.android.server.utils.ManagedApplicationService.LogEvent;
-import com.android.server.utils.ManagedApplicationService.LogFormattable;
-import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
 import com.android.server.utils.ManagedApplicationService;
 import com.android.server.utils.ManagedApplicationService.BinderChecker;
+import com.android.server.utils.ManagedApplicationService.LogEvent;
+import com.android.server.utils.ManagedApplicationService.LogFormattable;
+import com.android.server.utils.ManagedApplicationService.PendingEvent;
+import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver;
+import com.android.server.wm.WindowManagerInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.StringBuilder;
 import java.text.SimpleDateFormat;
-import java.util.Arrays;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Date;
 import java.util.List;
@@ -777,7 +776,7 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
-            LocalServices.getService(ActivityManagerInternal.class)
+            LocalServices.getService(ActivityTaskManagerInternal.class)
                     .registerScreenObserver(this);
 
             mNotificationManager = INotificationManager.Stub.asInterface(
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 547ab0e..c8bd211 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -51,7 +51,6 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
-import android.database.ContentObserver;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapRegionDecoder;
@@ -76,7 +75,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
 import android.service.wallpaper.IWallpaperConnection;
 import android.service.wallpaper.IWallpaperEngine;
 import android.service.wallpaper.IWallpaperService;
@@ -338,102 +336,6 @@
         }
     }
 
-    /**
-     * Observes changes of theme settings. It will check whether to call
-     * notifyWallpaperColorsChanged by the current theme and updated theme.
-     * The light theme and dark theme are controlled by the hint values in Wallpaper colors,
-     * threrfore, if light theme mode is chosen, HINT_SUPPORTS_DARK_THEME in hint will be
-     * removed and then notify listeners.
-     */
-    private class ThemeSettingsObserver extends ContentObserver {
-
-        public ThemeSettingsObserver(Handler handler) {
-            super(handler);
-        }
-
-        public void startObserving(Context context) {
-            context.getContentResolver().registerContentObserver(
-                    Settings.Secure.getUriFor(Settings.Secure.THEME_MODE),
-                    false,
-                    this);
-        }
-
-        public void stopObserving(Context context) {
-            context.getContentResolver().unregisterContentObserver(this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            onThemeSettingsChanged();
-        }
-    }
-
-    /**
-     * Check whether to call notifyWallpaperColorsChanged. Assumed that the theme mode
-     * was wallpaper theme mode and dark wallpaper was set, therefoe, the theme was dark.
-     * Then theme mode changing to dark theme mode, however, theme should not update since
-     * theme was dark already.
-     */
-    private boolean needUpdateLocked(WallpaperColors colors, int themeMode) {
-        if (colors == null) {
-            return false;
-        }
-
-        if (themeMode == mThemeMode) {
-            return false;
-        }
-
-        boolean result = true;
-        boolean supportDarkTheme =
-                (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
-        switch (themeMode) {
-            case Settings.Secure.THEME_MODE_WALLPAPER:
-                if (mThemeMode == Settings.Secure.THEME_MODE_LIGHT) {
-                    result = supportDarkTheme;
-                } else {
-                    result = !supportDarkTheme;
-                }
-                break;
-            case Settings.Secure.THEME_MODE_LIGHT:
-                if (mThemeMode == Settings.Secure.THEME_MODE_WALLPAPER) {
-                    result = supportDarkTheme;
-                }
-                break;
-            case Settings.Secure.THEME_MODE_DARK:
-                if (mThemeMode == Settings.Secure.THEME_MODE_WALLPAPER) {
-                    result = !supportDarkTheme;
-                }
-                break;
-            default:
-                Slog.w(TAG, "unkonwn theme mode " + themeMode);
-                return false;
-        }
-        mThemeMode = themeMode;
-        return result;
-    }
-
-    void onThemeSettingsChanged() {
-        WallpaperData wallpaper;
-        synchronized (mLock) {
-            wallpaper = mWallpaperMap.get(mCurrentUserId);
-            int updatedThemeMode = Settings.Secure.getInt(
-                    mContext.getContentResolver(), Settings.Secure.THEME_MODE,
-                    Settings.Secure.THEME_MODE_WALLPAPER);
-
-            if (DEBUG) {
-                Slog.v(TAG, "onThemeSettingsChanged, mode = " + updatedThemeMode);
-            }
-
-            if (!needUpdateLocked(wallpaper.primaryColors, updatedThemeMode)) {
-                return;
-            }
-        }
-
-        if (wallpaper != null) {
-            notifyWallpaperColorsChanged(wallpaper, FLAG_SYSTEM);
-        }
-    }
-
     void notifyLockWallpaperChanged() {
         final IWallpaperManagerCallback cb = mKeyguardListener;
         if (cb != null) {
@@ -511,7 +413,6 @@
                 }
                 userAllColorListeners.finishBroadcast();
             }
-            wallpaperColors = getThemeColorsLocked(wallpaperColors);
         }
 
         final int count = colorListeners.size();
@@ -580,40 +481,6 @@
     }
 
     /**
-     * We can easily change theme by modified colors hint. This function will check
-     * current theme mode and return the WallpaperColors fit current theme mode.
-     * If color need modified, it will return a copied WallpaperColors which
-     * its ColorsHint is modified to fit current theme mode.
-     *
-     * @param colors a wallpaper primary colors representation
-     */
-    private WallpaperColors getThemeColorsLocked(WallpaperColors colors) {
-        if (colors == null) {
-            Slog.w(TAG, "Cannot get theme colors because WallpaperColors is null.");
-            return null;
-        }
-
-        int colorHints = colors.getColorHints();
-        boolean supportDarkTheme = (colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
-        if (mThemeMode == Settings.Secure.THEME_MODE_WALLPAPER ||
-                (mThemeMode == Settings.Secure.THEME_MODE_LIGHT && !supportDarkTheme) ||
-                (mThemeMode == Settings.Secure.THEME_MODE_DARK && supportDarkTheme)) {
-            return colors;
-        }
-
-        WallpaperColors themeColors = new WallpaperColors(colors.getPrimaryColor(),
-                colors.getSecondaryColor(), colors.getTertiaryColor());
-
-        if (mThemeMode == Settings.Secure.THEME_MODE_LIGHT) {
-            colorHints &= ~WallpaperColors.HINT_SUPPORTS_DARK_THEME;
-        } else if (mThemeMode == Settings.Secure.THEME_MODE_DARK) {
-            colorHints |= WallpaperColors.HINT_SUPPORTS_DARK_THEME;
-        }
-        themeColors.setColorHints(colorHints);
-        return themeColors;
-    }
-
-    /**
      * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
      * for display.
      */
@@ -809,7 +676,6 @@
     final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>();
     int mCurrentUserId = UserHandle.USER_NULL;
     boolean mInAmbientMode;
-    int mThemeMode;
 
     static class WallpaperData {
 
@@ -868,7 +734,6 @@
         long lastDiedTime;
         boolean wallpaperUpdating;
         WallpaperObserver wallpaperObserver;
-        ThemeSettingsObserver themeSettingsObserver;
 
         /**
          * List of callbacks registered they should each be notified when the wallpaper is changed.
@@ -1100,7 +965,7 @@
                     }
                     mPaddingChanged = false;
                 }
-                if (mInfo != null && mInfo.getSupportsAmbientMode()) {
+                if (mInfo != null && mInfo.supportsAmbientMode()) {
                     try {
                         mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
                     } catch (RemoteException e) {
@@ -1414,10 +1279,6 @@
                 wallpaper.wallpaperObserver.stopWatching();
                 wallpaper.wallpaperObserver = null;
             }
-            if (wallpaper.themeSettingsObserver != null) {
-                wallpaper.themeSettingsObserver.stopObserving(mContext);
-                wallpaper.themeSettingsObserver = null;
-            }
         }
     }
 
@@ -1501,13 +1362,6 @@
                 systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
                 systemWallpaper.wallpaperObserver.startWatching();
             }
-            if (systemWallpaper.themeSettingsObserver == null) {
-                systemWallpaper.themeSettingsObserver = new ThemeSettingsObserver(null);
-                systemWallpaper.themeSettingsObserver.startObserving(mContext);
-            }
-            mThemeMode = Settings.Secure.getInt(
-                    mContext.getContentResolver(), Settings.Secure.THEME_MODE,
-                    Settings.Secure.THEME_MODE_WALLPAPER);
             switchWallpaper(systemWallpaper, reply);
         }
 
@@ -1923,7 +1777,7 @@
             mInAmbientMode = inAmbienMode;
             final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
             if (data != null && data.connection != null && data.connection.mInfo != null
-                    && data.connection.mInfo.getSupportsAmbientMode()) {
+                    && data.connection.mInfo.supportsAmbientMode()) {
                 engine = data.connection.mEngine;
             } else {
                 engine = null;
@@ -1981,7 +1835,7 @@
         }
 
         synchronized (mLock) {
-            return getThemeColorsLocked(wallpaperData.primaryColors);
+            return wallpaperData.primaryColors;
         }
     }
 
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand.java
index 39e28c7..3199ed4 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand.java
@@ -66,6 +66,12 @@
     private int setWebViewImplementation() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         String shellChosenPackage = getNextArg();
+        if (shellChosenPackage == null) {
+            pw.println("Failed to switch, no PACKAGE provided.");
+            pw.println("");
+            helpSetWebViewImplementation();
+            return 1;
+        }
         String newPackage = mInterface.changeProviderAndSetting(shellChosenPackage);
         if (shellChosenPackage.equals(newPackage)) {
             pw.println("Success");
@@ -85,6 +91,12 @@
         return 0;
     }
 
+    public void helpSetWebViewImplementation() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("  set-webview-implementation PACKAGE");
+        pw.println("    Set the WebView implementation to the specified package.");
+    }
+
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -99,8 +111,7 @@
         pw.println("  disable-redundant-packages");
         pw.println("    Disallow installing and enabling fallback packages when a more-preferred");
         pw.println("    package is available.");
-        pw.println("  set-webview-implementation PACKAGE");
-        pw.println("    Set the WebView implementation to the specified package.");
+        helpSetWebViewImplementation();
         pw.println("  enable-multiprocess");
         pw.println("    Enable multi-process mode for WebView");
         pw.println("  disable-multiprocess");
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdater.java b/services/core/java/com/android/server/webkit/WebViewUpdater.java
index 3e72d981..f270715 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdater.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdater.java
@@ -504,20 +504,20 @@
 
     private static boolean providerHasValidSignature(WebViewProviderInfo provider,
             PackageInfo packageInfo, SystemInterface systemInterface) {
-        if (systemInterface.systemIsDebuggable()) {
-            return true;
-        }
-        // If no signature is declared, instead check whether the package is included in the
-        // system.
-        if (provider.signatures == null || provider.signatures.length == 0) {
-            return packageInfo.applicationInfo.isSystemApp();
-        }
+        // Skip checking signatures on debuggable builds, for development purposes.
+        if (systemInterface.systemIsDebuggable()) return true;
+
+        // Allow system apps to be valid providers regardless of signature.
+        if (packageInfo.applicationInfo.isSystemApp()) return true;
+
+        // We don't support packages with multiple signatures.
         if (packageInfo.signatures.length != 1) return false;
 
-        // Return whether the package signature matches any of the valid signatures
+        // If any of the declared signatures match the package signature, it's valid.
         for (Signature signature : provider.signatures) {
             if (signature.equals(packageInfo.signatures[0])) return true;
         }
+
         return false;
     }
 
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index a6ec3cf..e449111 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -119,13 +119,13 @@
         }
     }
 
-    public void performComputeChangedWindowsNotLocked() {
+    public void performComputeChangedWindowsNotLocked(boolean forceSend) {
         WindowsForAccessibilityObserver observer = null;
         synchronized (mService) {
             observer = mWindowsForAccessibilityObserver;
         }
         if (observer != null) {
-            observer.performComputeChangedWindowsNotLocked();
+            observer.performComputeChangedWindowsNotLocked(forceSend);
         }
     }
 
@@ -193,7 +193,7 @@
             observer = mWindowsForAccessibilityObserver;
         }
         if (observer != null) {
-            observer.performComputeChangedWindowsNotLocked();
+            observer.performComputeChangedWindowsNotLocked(false);
         }
     }
 
@@ -549,7 +549,8 @@
                     touchableRegion.getBounds(touchableFrame);
                     RectF windowFrame = mTempRectF;
                     windowFrame.set(touchableFrame);
-                    windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+                    windowFrame.offset(-windowState.getFrameLw().left,
+                            -windowState.getFrameLw().top);
                     matrix.mapRect(windowFrame);
                     Region windowBounds = mTempRegion2;
                     windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
@@ -1010,12 +1011,12 @@
             mHandler = new MyHandler(mService.mH.getLooper());
             mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
                     .getSendRecurringAccessibilityEventsInterval();
-            computeChangedWindows();
+            computeChangedWindows(true);
         }
 
-        public void performComputeChangedWindowsNotLocked() {
+        public void performComputeChangedWindowsNotLocked(boolean forceSend) {
             mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
-            computeChangedWindows();
+            computeChangedWindows(forceSend);
         }
 
         public void scheduleComputeChangedWindowsLocked() {
@@ -1025,7 +1026,12 @@
             }
         }
 
-        public void computeChangedWindows() {
+        /**
+         * Check if windows have changed, and send them to the accessibilty subsystem if they have.
+         *
+         * @param forceSend Send the windows the accessibility even if they haven't changed.
+         */
+        public void computeChangedWindows(boolean forceSend) {
             if (DEBUG) {
                 Slog.i(LOG_TAG, "computeChangedWindows()");
             }
@@ -1170,36 +1176,38 @@
                 visibleWindows.clear();
                 addedWindows.clear();
 
-                // We computed the windows and if they changed notify the client.
-                if (mOldWindows.size() != windows.size()) {
-                    // Different size means something changed.
-                    windowsChanged = true;
-                } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
-                    // Since we always traverse windows from high to low layer
-                    // the old and new windows at the same index should be the
-                    // same, otherwise something changed.
-                    for (int i = 0; i < windowCount; i++) {
-                        WindowInfo oldWindow = mOldWindows.get(i);
-                        WindowInfo newWindow = windows.get(i);
-                        // We do not care for layer changes given the window
-                        // order does not change. This brings no new information
-                        // to the clients.
-                        if (windowChangedNoLayer(oldWindow, newWindow)) {
-                            windowsChanged = true;
-                            break;
+                if (!forceSend) {
+                    // We computed the windows and if they changed notify the client.
+                    if (mOldWindows.size() != windows.size()) {
+                        // Different size means something changed.
+                        windowsChanged = true;
+                    } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
+                        // Since we always traverse windows from high to low layer
+                        // the old and new windows at the same index should be the
+                        // same, otherwise something changed.
+                        for (int i = 0; i < windowCount; i++) {
+                            WindowInfo oldWindow = mOldWindows.get(i);
+                            WindowInfo newWindow = windows.get(i);
+                            // We do not care for layer changes given the window
+                            // order does not change. This brings no new information
+                            // to the clients.
+                            if (windowChangedNoLayer(oldWindow, newWindow)) {
+                                windowsChanged = true;
+                                break;
+                            }
                         }
                     }
                 }
 
-                if (windowsChanged) {
+                if (forceSend || windowsChanged) {
                     cacheWindows(windows);
                 }
             }
 
             // Now we do not hold the lock, so send the windows over.
-            if (windowsChanged) {
+            if (forceSend || windowsChanged) {
                 if (DEBUG) {
-                    Log.i(LOG_TAG, "Windows changed:" + windows);
+                    Log.i(LOG_TAG, "Windows changed or force sending:" + windows);
                 }
                 mCallback.onWindowsForAccessibilityChanged(windows);
             } else {
@@ -1222,7 +1230,7 @@
             // Move to origin as all transforms are captured by the matrix.
             RectF windowFrame = mTempRectF;
             windowFrame.set(touchableFrame);
-            windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+            windowFrame.offset(-windowState.getFrameLw().left, -windowState.getFrameLw().top);
 
             // Map the frame to get what appears on the screen.
             Matrix matrix = mTempMatrix;
@@ -1344,7 +1352,7 @@
             public void handleMessage(Message message) {
                 switch (message.what) {
                     case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
-                        computeChangedWindows();
+                        computeChangedWindows(false);
                     } break;
                 }
             }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
new file mode 100644
index 0000000..83075ed
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.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.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppProtoEnums;
+import android.app.IActivityManager;
+import android.app.IApplicationThread;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.SparseIntArray;
+import android.view.RemoteAnimationAdapter;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.server.am.WindowProcessController;
+
+import java.util.List;
+
+/**
+ * Activity Task manager local system service interface.
+ * @hide Only for use within system server
+ */
+public abstract class ActivityTaskManagerInternal {
+
+    /**
+     * Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew
+     * the splash screen.
+     */
+    public static final int APP_TRANSITION_SPLASH_SCREEN =
+              AppProtoEnums.APP_TRANSITION_SPLASH_SCREEN; // 1
+
+    /**
+     * Type for {@link #notifyAppTransitionStarting}: The transition was started because we all
+     * app windows were drawn
+     */
+    public static final int APP_TRANSITION_WINDOWS_DRAWN =
+              AppProtoEnums.APP_TRANSITION_WINDOWS_DRAWN; // 2
+
+    /**
+     * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
+     * timeout.
+     */
+    public static final int APP_TRANSITION_TIMEOUT =
+              AppProtoEnums.APP_TRANSITION_TIMEOUT; // 3
+
+    /**
+     * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
+     * we drew a task snapshot.
+     */
+    public static final int APP_TRANSITION_SNAPSHOT =
+              AppProtoEnums.APP_TRANSITION_SNAPSHOT; // 4
+
+    /**
+     * Type for {@link #notifyAppTransitionStarting}: The transition was started because it was a
+     * recents animation and we only needed to wait on the wallpaper.
+     */
+    public static final int APP_TRANSITION_RECENTS_ANIM =
+            AppProtoEnums.APP_TRANSITION_RECENTS_ANIM; // 5
+
+    /**
+     * The bundle key to extract the assist data.
+     */
+    public static final String ASSIST_KEY_DATA = "data";
+
+    /**
+     * The bundle key to extract the assist structure.
+     */
+    public static final String ASSIST_KEY_STRUCTURE = "structure";
+
+    /**
+     * The bundle key to extract the assist content.
+     */
+    public static final String ASSIST_KEY_CONTENT = "content";
+
+    /**
+     * The bundle key to extract the assist receiver extras.
+     */
+    public static final String ASSIST_KEY_RECEIVER_EXTRAS = "receiverExtras";
+
+    public interface ScreenObserver {
+        void onAwakeStateChanged(boolean isAwake);
+        void onKeyguardStateChanged(boolean isShowing);
+    }
+
+    /**
+     * Sleep tokens cause the activity manager to put the top activity to sleep.
+     * They are used by components such as dreams that may hide and block interaction
+     * with underlying activities.
+     */
+    public static abstract class SleepToken {
+
+        /** Releases the sleep token. */
+        public abstract void release();
+    }
+
+    /**
+     * Acquires a sleep token for the specified display with the specified tag.
+     *
+     * @param tag A string identifying the purpose of the token (eg. "Dream").
+     * @param displayId The display to apply the sleep token to.
+     */
+    public abstract SleepToken acquireSleepToken(@NonNull String tag, int displayId);
+
+    /**
+     * Returns home activity for the specified user.
+     *
+     * @param userId ID of the user or {@link android.os.UserHandle#USER_ALL}
+     */
+    public abstract ComponentName getHomeActivityForUser(int userId);
+
+    public abstract void onLocalVoiceInteractionStarted(IBinder callingActivity,
+            IVoiceInteractionSession mSession,
+            IVoiceInteractor mInteractor);
+
+    /**
+     * Callback for window manager to let activity manager know that we are finally starting the
+     * app transition;
+     *
+     * @param reasons A map from windowing mode to a reason integer why the transition was started,
+     *                which must be one of the APP_TRANSITION_* values.
+     * @param timestamp The time at which the app transition started in
+     *                  {@link SystemClock#uptimeMillis()} timebase.
+     */
+    public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp);
+
+    /**
+     * Callback for window manager to let activity manager know that the app transition was
+     * cancelled.
+     */
+    public abstract void notifyAppTransitionCancelled();
+
+    /**
+     * Callback for window manager to let activity manager know that the app transition is finished.
+     */
+    public abstract void notifyAppTransitionFinished();
+
+    /**
+     * Returns the top activity from each of the currently visible stacks. The first entry will be
+     * the focused activity.
+     */
+    public abstract List<IBinder> getTopVisibleActivities();
+
+    /**
+     * Callback for window manager to let activity manager know that docked stack changes its
+     * minimized state.
+     */
+    public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
+
+    /**
+     * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
+     *
+     * - DO NOT call it with the calling UID cleared.
+     * - All the necessary caller permission checks must be done at callsites.
+     *
+     * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
+     */
+    public abstract int startActivitiesAsPackage(String packageName,
+            int userId, Intent[] intents, Bundle bOptions);
+
+    /**
+     * Start activity {@code intent} without calling user-id check.
+     *
+     * - DO NOT call it with the calling UID cleared.
+     * - The caller must do the calling user ID check.
+     *
+     * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
+     */
+    public abstract int startActivityAsUser(IApplicationThread caller, String callingPackage,
+            Intent intent, @Nullable Bundle options, int userId);
+
+    /**
+     * Called when Keyguard flags might have changed.
+     *
+     * @param callback Callback to run after activity visibilities have been reevaluated. This can
+     *                 be used from window manager so that when the callback is called, it's
+     *                 guaranteed that all apps have their visibility updated accordingly.
+     */
+    public abstract void notifyKeyguardFlagsChanged(@Nullable Runnable callback);
+
+    /**
+     * Called when the trusted state of Keyguard has changed.
+     */
+    public abstract void notifyKeyguardTrustedChanged();
+
+    /**
+     * Called after virtual display Id is updated by
+     * {@link com.android.server.vr.Vr2dDisplay} with a specific
+     * {@param vr2dDisplayId}.
+     */
+    public abstract void setVr2dDisplayId(int vr2dDisplayId);
+
+    /**
+     * Set focus on an activity.
+     * @param token The IApplicationToken for the activity
+     */
+    public abstract void setFocusedActivity(IBinder token);
+
+    public abstract void registerScreenObserver(ScreenObserver observer);
+
+    /**
+     * Returns is the caller has the same uid as the Recents component
+     */
+    public abstract boolean isCallerRecents(int callingUid);
+
+    /**
+     * Returns whether the recents component is the home activity for the given user.
+     */
+    public abstract boolean isRecentsComponentHomeActivity(int userId);
+
+    /**
+     * Cancels any currently running recents animation.
+     */
+    public abstract void cancelRecentsAnimation(boolean restoreHomeStackPosition);
+
+    /**
+     * This enforces {@code func} can only be called if either the caller is Recents activity or
+     * has {@code permission}.
+     */
+    public abstract void enforceCallerIsRecentsOrHasPermission(String permission, String func);
+
+    /**
+     * Called after the voice interaction service has changed.
+     */
+    public abstract void notifyActiveVoiceInteractionServiceChanged(ComponentName component);
+
+    /**
+     * Set a uid that is allowed to bypass stopped app switches, launching an app
+     * whenever it wants.
+     *
+     * @param type Type of the caller -- unique string the caller supplies to identify itself
+     * and disambiguate with other calles.
+     * @param uid The uid of the app to be allowed, or -1 to clear the uid for this type.
+     * @param userId The user it is allowed for.
+     */
+    public abstract void setAllowAppSwitches(@NonNull String type, int uid, int userId);
+
+    /**
+     * Called when a user has been deleted. This can happen during normal device usage
+     * or just at startup, when partially removed users are purged. Any state persisted by the
+     * ActivityManager should be purged now.
+     *
+     * @param userId The user being cleaned up.
+     */
+    public abstract void onUserStopped(int userId);
+    public abstract boolean isGetTasksAllowed(String caller, int callingPid, int callingUid);
+
+    public abstract void onProcessAdded(WindowProcessController proc);
+    public abstract void onProcessRemoved(String name, int uid);
+    public abstract void onCleanUpApplicationRecord(WindowProcessController proc);
+    public abstract int getTopProcessState();
+
+    public abstract boolean isSleeping();
+    public abstract boolean isShuttingDown();
+    public abstract boolean shuttingDown(boolean booted, int timeout);
+    public abstract void enableScreenAfterBoot(boolean booted);
+    public abstract boolean showStrictModeViolationDialog();
+    public abstract void showSystemReadyErrorDialogsIfNeeded();
+
+    public abstract long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason);
+    public abstract void onProcessMapped(int pid, WindowProcessController proc);
+    public abstract void onProcessUnMapped(int pid);
+
+    public abstract void onPackageDataCleared(String name);
+    public abstract void onPackageUninstalled(String name);
+}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 4f15c5d..36280dd 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -648,7 +648,7 @@
     public void pauseKeyDispatching() {
         synchronized (mWindowMap) {
             if (mContainer != null) {
-                mService.mInputMonitor.pauseDispatchingLw(mContainer);
+                mContainer.getDisplayContent().getInputMonitor().pauseDispatchingLw(mContainer);
             }
         }
     }
@@ -656,7 +656,7 @@
     public void resumeKeyDispatching() {
         synchronized (mWindowMap) {
             if (mContainer != null) {
-                mService.mInputMonitor.resumeDispatchingLw(mContainer);
+                mContainer.getDisplayContent().getInputMonitor().resumeDispatchingLw(mContainer);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 08d0ae9..b775918 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.COLOR_MODE_DEFAULT;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
@@ -77,6 +78,7 @@
 import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW;
 import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL;
 import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 
 import android.annotation.CallSuper;
 import android.app.Activity;
@@ -263,6 +265,12 @@
      */
     private boolean mWillCloseOrEnterPip;
 
+    /** Layer used to constrain the animation to a token's stack bounds. */
+    SurfaceControl mAnimationBoundsLayer;
+
+    /** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */
+    boolean mNeedsAnimationBoundsLayer;
+
     AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
             DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen,
             boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint,
@@ -448,13 +456,13 @@
                     + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested);
 
             if (changed) {
-                mService.mInputMonitor.setUpdateInputWindowsNeededLw();
+                getDisplayContent().getInputMonitor().setUpdateInputWindowsNeededLw();
                 if (performLayout) {
                     mService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
                             false /*updateInputWindows*/);
                     mService.mWindowPlacerLocked.performSurfacePlacement();
                 }
-                mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+                getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/);
             }
         }
 
@@ -676,7 +684,7 @@
             if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this);
             mService.mFocusedApp = null;
             mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
-            mService.mInputMonitor.setFocusedAppLw(null);
+            getDisplayContent().getInputMonitor().setFocusedAppLw(null);
         }
 
         if (!delayed) {
@@ -1377,7 +1385,7 @@
             setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "checkAppWindowsReadyToShow");
 
             // We can now show all of the drawn windows!
-            if (!mService.mOpeningApps.contains(this)) {
+            if (!mService.mOpeningApps.contains(this) && canShowWindows()) {
                 showAllWindowsLocked();
             }
         }
@@ -1522,7 +1530,7 @@
             if (mLetterbox == null) {
                 mLetterbox = new Letterbox(() -> makeChildSurface(null));
             }
-            mLetterbox.layout(getParent().getBounds(), w.mFrame);
+            mLetterbox.layout(getParent().getBounds(), w.getFrameLw());
         } else if (mLetterbox != null) {
             mLetterbox.hide();
         }
@@ -1719,6 +1727,20 @@
         return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
     }
 
+    /**
+     * Creates a layer to apply crop to an animation.
+     */
+    private SurfaceControl createAnimationBoundsLayer(Transaction t) {
+        if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.i(TAG, "Creating animation bounds layer");
+        final SurfaceControl.Builder builder = makeAnimationLeash()
+                .setParent(getAnimationLeashParent())
+                .setName(getSurfaceControl() + " - animation-bounds")
+                .setSize(getSurfaceWidth(), getSurfaceHeight());
+        final SurfaceControl boundsLayer = builder.build();
+        t.show(boundsLayer);
+        return boundsLayer;
+    }
+
     boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
             boolean isVoiceInteraction) {
 
@@ -1752,12 +1774,15 @@
                 adapter = mService.mAppTransition.getRemoteAnimationController()
                         .createAnimationAdapter(this, mTmpPoint, mTmpRect);
             } else {
+                final int appStackClipMode = mService.mAppTransition.getAppStackClipMode();
+                mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM);
+
                 final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
                 if (a != null) {
                     adapter = new LocalAnimationAdapter(
                             new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
                                     mService.mAppTransition.canSkipFirstFrame(),
-                                    mService.mAppTransition.getAppStackClipMode(),
+                                    appStackClipMode,
                                     true /* isAppAnimation */),
                             mService.mSurfaceAnimationRunner);
                     if (a.getZAdjustment() == Animation.ZORDER_TOP) {
@@ -1807,7 +1832,7 @@
             // won't exactly match the final freeform window frame (e.g. when overlapping with
             // the status bar). In that case we need to use the final frame.
             if (freeform) {
-                frame.set(win.mFrame);
+                frame.set(win.getFrameLw());
             } else if (win.isLetterboxedAppWindow()) {
                 frame.set(getTask().getBounds());
             } else if (win.isDockedResizing()) {
@@ -1815,7 +1840,7 @@
                 // animation target (which will be different than the task bounds)
                 frame.set(getTask().getParent().getBounds());
             } else {
-                frame.set(win.mContainingFrame);
+                frame.set(win.getContainingFrame());
             }
             surfaceInsets = win.getAttrs().surfaceInsets;
             // XXX(b/72757033): These are insets relative to the window frame, but we're really
@@ -1857,6 +1882,11 @@
     @Override
     public void onAnimationLeashDestroyed(Transaction t) {
         super.onAnimationLeashDestroyed(t);
+        if (mAnimationBoundsLayer != null) {
+            t.destroy(mAnimationBoundsLayer);
+            mAnimationBoundsLayer = null;
+        }
+
         if (mAnimatingAppWindowTokenRegistry != null) {
             mAnimatingAppWindowTokenRegistry.notifyFinished(this);
         }
@@ -1907,6 +1937,26 @@
         if (mAnimatingAppWindowTokenRegistry != null) {
             mAnimatingAppWindowTokenRegistry.notifyStarting(this);
         }
+
+        // If the animation needs to be cropped then an animation bounds layer is created as a child
+        // of the pinned stack or animation layer. The leash is then reparented to this new layer.
+        if (mNeedsAnimationBoundsLayer) {
+            final TaskStack stack = getStack();
+            if (stack == null) {
+                return;
+            }
+            mAnimationBoundsLayer = createAnimationBoundsLayer(t);
+
+            // Set clip rect to stack bounds.
+            mTmpRect.setEmpty();
+            stack.getBounds(mTmpRect);
+
+            // Crop to stack bounds.
+            t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
+
+            // Reparent leash to animation bounds layer.
+            t.reparent(leash, mAnimationBoundsLayer.getHandle());
+        }
     }
 
     /**
@@ -1926,6 +1976,7 @@
         mTransit = TRANSIT_UNSET;
         mTransitFlags = 0;
         mNeedsZBoost = false;
+        mNeedsAnimationBoundsLayer = false;
 
         setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
                 "AppWindowToken");
@@ -2021,7 +2072,7 @@
         if (win == null) {
             return;
         }
-        final Rect frame = win.mFrame;
+        final Rect frame = win.getFrameLw();
         final int thumbnailDrawableRes = getTask().mUserId == mService.mCurrentUserId
                 ? R.drawable.ic_account_circle
                 : R.drawable.ic_corp_badge;
@@ -2033,7 +2084,8 @@
         }
         mThumbnail = new AppWindowThumbnail(getPendingTransaction(), this, thumbnail);
         final Animation animation =
-                mService.mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(win.mFrame);
+                mService.mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
+                        win.getFrameLw());
         mThumbnail.startAnimation(getPendingTransaction(), animation, new Point(frame.left,
                 frame.top));
     }
@@ -2270,4 +2322,21 @@
     boolean isClosingOrEnteringPip() {
         return (isAnimating() && hiddenRequested) || mWillCloseOrEnterPip;
     }
+
+    /**
+     * @return Whether we are allowed to show non-starting windows at the moment. We disallow
+     *         showing windows during transitions in case we have windows that have wide-color-gamut
+     *         color mode set to avoid jank in the middle of the transition.
+     */
+    boolean canShowWindows() {
+        return allDrawn && !(isReallyAnimating() && hasNonDefaultColorWindow());
+    }
+
+    /**
+     * @return true if we have a window that has a non-default color mode set; false otherwise.
+     */
+    private boolean hasNonDefaultColorWindow() {
+        return forAllWindows(ws -> ws.mAttrs.getColorMode() != COLOR_MODE_DEFAULT,
+                true /* topToBottom */);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 1977e12..fff1fa4 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -56,6 +56,7 @@
                     .setParent(null) // TODO: Work-around for b/69259549
                     .build();
 
+            transaction.setLayerStack(surface, dc.getDisplayId());
             transaction.setAlpha(surface, 1);
             transaction.setLayer(surface, layer);
             transaction.show(surface);
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index b2a12be..a693071 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -31,11 +31,13 @@
 import android.os.Debug;
 import android.util.ArrayMap;
 import android.util.Slog;
+import android.view.Choreographer;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -112,6 +114,7 @@
     private final Interpolator mFastOutSlowInInterpolator;
     private boolean mFinishAnimationAfterTransition = false;
     private final AnimationHandler mAnimationHandler;
+    private Choreographer mChoreographer;
 
     private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000;
 
@@ -123,6 +126,12 @@
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_slow_in);
         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));
+        }
     }
 
     @VisibleForTesting
@@ -210,13 +219,15 @@
             // Ensure that we have prepared the target for animation before we trigger any size
             // changes, so it can swap surfaces in to appropriate modes, or do as it wishes
             // otherwise.
+            boolean continueAnimation;
             if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) {
-                mTarget.onAnimationStart(mSchedulePipModeChangedState ==
+                continueAnimation = mTarget.onAnimationStart(mSchedulePipModeChangedState ==
                         SCHEDULE_PIP_MODE_CHANGED_ON_START, false /* forceUpdate */);
 
                 // When starting an animation from fullscreen, pause here and wait for the
                 // windows-drawn signal before we start the rest of the transition down into PiP.
-                if (mMoveFromFullscreen && mTarget.shouldDeferStartOnMoveToFullscreen()) {
+                if (continueAnimation && mMoveFromFullscreen
+                        && mTarget.shouldDeferStartOnMoveToFullscreen()) {
                     pause();
                 }
             } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END &&
@@ -225,8 +236,19 @@
                 // client will not currently receive any picture-in-picture mode change callbacks.
                 // However, we still need to report to them that they are leaving PiP, so this will
                 // force an update via a mode changed callback.
-                mTarget.onAnimationStart(true /* schedulePipModeChangedCallback */,
-                        true /* forceUpdate */);
+                continueAnimation = mTarget.onAnimationStart(
+                        true /* schedulePipModeChangedCallback */, true /* forceUpdate */);
+            } else {
+                // The animation is already running, but we should check that the TaskStack is still
+                // valid before continuing with the animation
+                continueAnimation = mTarget.isAttached();
+            }
+
+            if (!continueAnimation) {
+                // No point of trying to animate something that isn't attached to the hierarchy
+                // anymore.
+                cancel();
+                return;
             }
 
             // Immediately update the task bounds if they have to become larger, but preserve
@@ -348,6 +370,9 @@
             if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget);
             mSkipAnimationEnd = true;
             super.cancel();
+
+            // Reset the thread priority of the animation thread if the bounds animation is canceled
+            updateBooster();
         }
 
         /**
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
index 68be4e8..5cb80de 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
@@ -30,8 +30,9 @@
      *
      * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
      * callbacks
+     * @return whether to continue the animation
      */
-    void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate);
+    boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate);
 
     /**
      * @return Whether the animation should be paused waiting for the windows to draw before
@@ -65,4 +66,9 @@
      */
     void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
             boolean moveToFullscreen);
+
+    /** @return True if the target is attached to the window hierarchy. */
+    default boolean isAttached() {
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 7df057c..f2ce63c 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -302,6 +302,18 @@
         onOverrideConfigurationChanged(mTmpConfig);
     }
 
+    /** Sets the always on top flag for this configuration container.
+     *  When you call this function, make sure that the following functions are called as well to
+     *  keep proper z-order.
+     *  - {@Link DisplayContent#positionStackAt(POSITION_TOP, TaskStack)};
+     *  - {@Link ActivityDisplay#positionChildAtTop(ActivityStack)};
+     * */
+    public void setAlwaysOnTop(boolean alwaysOnTop) {
+        mTmpConfig.setTo(getOverrideConfiguration());
+        mTmpConfig.windowConfiguration.setAlwaysOnTop(alwaysOnTop);
+        onOverrideConfigurationChanged(mTmpConfig);
+    }
+
     /**
      * Returns true if this container is currently in multi-window mode. I.e. sharing the screen
      * with another activity.
@@ -513,7 +525,7 @@
         return toString();
     }
 
-    boolean isAlwaysOnTop() {
+    public boolean isAlwaysOnTop() {
         return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b0e6208..44d0187 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -173,7 +173,8 @@
  * IMPORTANT: No method from this class should ever be used without holding
  * WindowManagerService.mWindowMap.
  */
-class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer> {
+class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>
+        implements WindowManagerPolicy.DisplayContentInfo {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;
 
     /** Unique identifier of this stack. */
@@ -234,6 +235,8 @@
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
     private final Display mDisplay;
     private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+    private final DisplayPolicy mDisplayPolicy;
+    private DisplayRotation mDisplayRotation;
     DisplayFrames mDisplayFrames;
 
     /**
@@ -303,8 +306,12 @@
     // Accessed directly by all users.
     private boolean mLayoutNeeded;
     int pendingLayoutChanges;
+    int mDeferredRotationPauseCount;
+
     // TODO(multi-display): remove some of the usages.
+    @VisibleForTesting
     boolean isDefaultDisplay;
+
     /**
      * Flag indicating whether WindowManager should override info for this display in
      * DisplayManager.
@@ -400,11 +407,13 @@
 
     private MagnificationSpec mMagnificationSpec;
 
+    private InputMonitor mInputMonitor;
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         final AppWindowToken atoken = w.mAppToken;
         if (winAnimator.mDrawState == READY_TO_SHOW) {
-            if (atoken == null || atoken.allDrawn) {
+            if (atoken == null || atoken.canShowWindows()) {
                 if (w.performShowLocked()) {
                     pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
                     if (DEBUG_LAYOUT_REPEATS) {
@@ -578,9 +587,9 @@
                     w.mAppToken.layoutLetterbox(w);
                 }
 
-                if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + w.mFrame
-                        + " mContainingFrame=" + w.mContainingFrame
-                        + " mDisplayFrame=" + w.mDisplayFrame);
+                if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + w.getFrameLw()
+                        + " mContainingFrame=" + w.getContainingFrame()
+                        + " mDisplayFrame=" + w.getDisplayFrameLw());
             }
         }
     };
@@ -606,9 +615,9 @@
                 w.prelayout();
                 mService.mPolicy.layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
                 w.mLayoutSeq = mLayoutSeq;
-                if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
-                        + " mContainingFrame=" + w.mContainingFrame
-                        + " mDisplayFrame=" + w.mDisplayFrame);
+                if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw()
+                        + " mContainingFrame=" + w.getContainingFrame()
+                        + " mDisplayFrame=" + w.getDisplayFrameLw());
             }
         } else if (w.mAttrs.type == TYPE_DREAM) {
             // Don't layout windows behind a dream, so that if it does stuff like hide the
@@ -759,6 +768,13 @@
         mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo,
                 calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
         initializeDisplayBaseInfo();
+        mDisplayPolicy = new DisplayPolicy(service);
+        mDisplayRotation = new DisplayRotation(service, this);
+        if (isDefaultDisplay) {
+            // The policy may be invoked right after here, so it requires the necessary default
+            // fields of this display content.
+            mService.mPolicy.setDefaultDisplay(this);
+        }
         mDividerControllerLocked = new DockedStackDividerController(service, this);
         mPinnedStackControllerLocked = new PinnedStackController(service, this);
 
@@ -797,6 +813,8 @@
         // TODO(b/62541591): evaluate whether this is the best spot to declare the
         // {@link DisplayContent} ready for use.
         mDisplayReady = true;
+
+        mInputMonitor = new InputMonitor(service, mDisplayId);
     }
 
     boolean isReady() {
@@ -900,7 +918,8 @@
         appToken.onRemovedFromDisplay();
     }
 
-    Display getDisplay() {
+    @Override
+    public Display getDisplay() {
         return mDisplay;
     }
 
@@ -912,6 +931,20 @@
         return mDisplayMetrics;
     }
 
+    DisplayPolicy getDisplayPolicy() {
+        return mDisplayPolicy;
+    }
+
+    @Override
+    public DisplayRotation getDisplayRotation() {
+        return mDisplayRotation;
+    }
+
+    @VisibleForTesting
+    void setDisplayRotation(DisplayRotation displayRotation) {
+        mDisplayRotation = displayRotation;
+    }
+
     int getRotation() {
         return mRotation;
     }
@@ -919,6 +952,7 @@
     @VisibleForTesting
     void setRotation(int newRotation) {
         mRotation = newRotation;
+        mDisplayRotation.setRotation(newRotation);
     }
 
     int getLastOrientation() {
@@ -942,6 +976,67 @@
     }
 
     /**
+     * Temporarily pauses rotation changes until resumed.
+     *
+     * This can be used to prevent rotation changes from occurring while the user is
+     * performing certain operations, such as drag and drop.
+     *
+     * This call nests and must be matched by an equal number of calls to
+     * {@link #resumeRotationLocked}.
+     */
+    void pauseRotationLocked() {
+        mDeferredRotationPauseCount++;
+    }
+
+    /**
+     * Resumes normal rotation changes after being paused.
+     */
+    void resumeRotationLocked() {
+        if (mDeferredRotationPauseCount <= 0) {
+            return;
+        }
+
+        mDeferredRotationPauseCount--;
+        if (mDeferredRotationPauseCount == 0) {
+            updateRotationAndSendNewConfigIfNeeded();
+        }
+    }
+
+    /**
+     * If this is true we have updated our desired orientation, but not yet changed the real
+     * orientation our applied our screen rotation animation. For example, because a previous
+     * screen rotation was in progress.
+     *
+     * @return {@code true} if the there is an ongoing rotation change.
+     */
+    boolean rotationNeedsUpdate() {
+        final int lastOrientation = getLastOrientation();
+        final int oldRotation = getRotation();
+        final boolean oldAltOrientation = getAltOrientation();
+
+        final int rotation = mDisplayRotation.rotationForOrientation(lastOrientation, oldRotation);
+        final boolean altOrientation = !mDisplayRotation.rotationHasCompatibleMetrics(
+                lastOrientation, rotation);
+        if (oldRotation == rotation && oldAltOrientation == altOrientation) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Update rotation of the display and send configuration if the rotation is changed.
+     *
+     * @return {@code true} if the rotation has been changed and the new config is sent.
+     */
+    boolean updateRotationAndSendNewConfigIfNeeded() {
+        final boolean changed = updateRotationUnchecked(false /* forceUpdate */);
+        if (changed) {
+            mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mDisplayId).sendToTarget();
+        }
+        return changed;
+    }
+
+    /**
      * Update rotation of the display.
      *
      * @return {@code true} if the rotation has been changed.  In this case YOU MUST CALL
@@ -964,7 +1059,7 @@
     boolean updateRotationUnchecked(boolean forceUpdate) {
         ScreenRotationAnimation screenRotationAnimation;
         if (!forceUpdate) {
-            if (mService.mDeferredRotationPauseCount > 0) {
+            if (mDeferredRotationPauseCount > 0) {
                 // Rotation updates have been paused temporarily.  Defer the update until
                 // updates have been resumed.
                 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, rotation is paused.");
@@ -999,13 +1094,12 @@
         final int oldRotation = mRotation;
         final int lastOrientation = mLastOrientation;
         final boolean oldAltOrientation = mAltOrientation;
-        final int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation,
-                isDefaultDisplay);
+        final int rotation = mDisplayRotation.rotationForOrientation(lastOrientation, oldRotation);
         if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="
                 + mDisplayId + " based on lastOrientation=" + lastOrientation
                 + " and oldRotation=" + oldRotation);
-        boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
-                rotation);
+        boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(mDisplayRotation,
+                oldRotation, rotation);
 
         if (mayRotateSeamlessly) {
             final WindowState seamlessRotated = getWindow((w) -> w.mSeamlesslyRotated);
@@ -1036,7 +1130,7 @@
         //       an orientation that has different metrics than it expected.
         //       eg. Portrait instead of Landscape.
 
-        final boolean altOrientation = !mService.mPolicy.rotationHasCompatibleMetricsLw(
+        final boolean altOrientation = !mDisplayRotation.rotationHasCompatibleMetrics(
                 lastOrientation, rotation);
 
         if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
@@ -1058,16 +1152,12 @@
             mService.mWaitingForConfig = true;
         }
 
-        mRotation = rotation;
+        setRotation(rotation);
         mAltOrientation = altOrientation;
-        if (isDefaultDisplay) {
-            mService.mPolicy.setRotationLw(rotation);
-        }
 
         mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
-        mService.mH.removeMessages(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT);
-        mService.mH.sendEmptyMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
-                WINDOW_FREEZE_TIMEOUT_DURATION);
+        mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
+                this, WINDOW_FREEZE_TIMEOUT_DURATION);
 
         setLayoutNeeded();
         final int[] anim = new int[2];
@@ -1106,11 +1196,13 @@
             }
         }
 
+        forAllWindows(w -> {
+            w.forceSeamlesslyRotateIfAllowed(oldRotation, rotation);
+        }, true /* traverseTopToBottom */);
+
+        // TODO(b/111504081): Consolidate seamless rotation logic.
         if (rotateSeamlessly) {
-            forAllWindows(w -> {
-                    w.mWinAnimator.seamlesslyRotateWindow(getPendingTransaction(),
-                            oldRotation, rotation);
-            }, true /* traverseTopToBottom */);
+            seamlesslyRotate(getPendingTransaction(), oldRotation, rotation);
         }
 
         mService.mDisplayManagerInternal.performTraversal(getPendingTransaction());
@@ -1127,9 +1219,8 @@
         }, true /* traverseTopToBottom */);
 
         if (rotateSeamlessly) {
-            mService.mH.removeMessages(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT);
-            mService.mH.sendEmptyMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
-                    SEAMLESS_ROTATION_TIMEOUT_DURATION);
+            mService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
+                    this, SEAMLESS_ROTATION_TIMEOUT_DURATION);
         }
 
         for (int i = mService.mRotationWatchers.size() - 1; i >= 0; i--) {
@@ -1156,8 +1247,23 @@
     }
 
     void configureDisplayPolicy() {
-        mService.mPolicy.setInitialDisplaySize(getDisplay(),
-                mBaseDisplayWidth, mBaseDisplayHeight, mBaseDisplayDensity);
+        final int width = mBaseDisplayWidth;
+        final int height = mBaseDisplayHeight;
+        final int shortSize;
+        final int longSize;
+        if (width > height) {
+            shortSize = height;
+            longSize = width;
+        } else {
+            shortSize = width;
+            longSize = height;
+        }
+
+        final int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
+        final int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
+
+        mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
+        mDisplayPolicy.configure(width, height, shortSizeDp);
 
         mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
                 calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
@@ -1252,11 +1358,21 @@
                     cutout, mInitialDisplayWidth, mInitialDisplayHeight);
         }
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-        final Path bounds = cutout.getBounds().getBoundaryPath();
+        final List<Rect> bounds = WmDisplayCutout.computeSafeInsets(
+                        cutout, mInitialDisplayWidth, mInitialDisplayHeight)
+                .getDisplayCutout().getBoundingRects();
         transformPhysicalToLogicalCoordinates(rotation, mInitialDisplayWidth, mInitialDisplayHeight,
                 mTmpMatrix);
-        bounds.transform(mTmpMatrix);
-        return WmDisplayCutout.computeSafeInsets(DisplayCutout.fromBounds(bounds),
+        final Region region = Region.obtain();
+        for (int i = 0; i < bounds.size(); i++) {
+            final Rect rect = bounds.get(i);
+            final RectF rectF = new RectF(bounds.get(i));
+            mTmpMatrix.mapRect(rectF);
+            rectF.round(rect);
+            region.op(rect, Op.UNION);
+        }
+
+        return WmDisplayCutout.computeSafeInsets(DisplayCutout.fromBounds(region),
                 rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
                 rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
     }
@@ -1271,9 +1387,7 @@
         final int dw = displayInfo.logicalWidth;
         final int dh = displayInfo.logicalHeight;
         config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
-        // TODO: Probably best to set this based on some setting in the display content object,
-        // so the display can be configured for things like fullscreen.
-        config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        config.windowConfiguration.setWindowingMode(getWindowingMode());
 
         final float density = mDisplayMetrics.density;
         config.screenWidthDp =
@@ -1569,6 +1683,11 @@
     }
 
     @VisibleForTesting
+    WindowList<TaskStack> getStacks() {
+        return mTaskStackContainers.mChildren;
+    }
+
+    @VisibleForTesting
     TaskStack getTopStack() {
         return mTaskStackContainers.getTopStack();
     }
@@ -1881,8 +2000,8 @@
         getParent().positionChildAt(position, this, includingParents);
     }
 
-    void positionStackAt(int position, TaskStack child) {
-        mTaskStackContainers.positionChildAt(position, child, false /* includingParents */);
+    void positionStackAt(int position, TaskStack child, boolean includingParents) {
+        mTaskStackContainers.positionChildAt(position, child, includingParents);
         layoutAndAssignWindowLayersIfNeeded();
     }
 
@@ -2013,6 +2132,7 @@
             mRemovingDisplay = false;
         }
 
+        mInputMonitor.onRemoved();
         mService.onDisplayRemoved(mDisplayId);
     }
 
@@ -2270,6 +2390,8 @@
 
         pw.println();
         pw.print(prefix); pw.print("mLayoutSeq="); pw.println(mLayoutSeq);
+        pw.print(prefix);
+        pw.print("mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
 
         pw.println();
         pw.println(prefix + "Application tokens in top down Z order:");
@@ -2314,6 +2436,12 @@
 
         pw.println();
         mDisplayFrames.dump(prefix, pw);
+        pw.println();
+        mDisplayPolicy.dump(prefix, pw);
+        pw.println();
+        mDisplayRotation.dump(prefix, pw);
+        pw.println();
+        mInputMonitor.dump(pw, "  ");
     }
 
     @Override
@@ -2424,9 +2552,9 @@
             assignWindowLayers(false /* setLayoutNeeded */);
         }
 
-        mService.mInputMonitor.setUpdateInputWindowsNeededLw();
+        mInputMonitor.setUpdateInputWindowsNeededLw();
         mService.mWindowPlacerLocked.performSurfacePlacement();
-        mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+        mInputMonitor.updateInputWindowsLw(false /*force*/);
     }
 
     /** Returns true if a leaked surface was destroyed */
@@ -2505,11 +2633,12 @@
         if (DEBUG_INPUT_METHOD && updateImeTarget) Slog.v(TAG_WM,
                 "Proposed new IME target: " + target);
 
-        // Now, a special case -- if the last target's window is in the process of exiting, and the
-        // new target is home, keep on the last target to avoid flicker. Home is a special case
-        // since its above other stacks in the ordering list, but layed out below the others.
-        if (curTarget != null && curTarget.isDisplayedLw() && curTarget.isClosing()
-                && (target == null || target.isActivityTypeHome())) {
+        // Now, a special case -- if the last target's window is in the process of exiting, but
+        // not removed, and the new target is home, keep on the last target to avoid flicker.
+        // Home is a special case since its above other stacks in the ordering list, but layed
+        // out below the others.
+        if (curTarget != null && !curTarget.mRemoved && curTarget.isDisplayedLw()
+                && curTarget.isClosing() && (target == null || target.isActivityTypeHome())) {
             if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "New target is home while current target is "
                     + "closing, not changing");
             return curTarget;
@@ -2863,7 +2992,7 @@
                 mWallpaperController.adjustWallpaperWindows(this);
             }
 
-            if (isDefaultDisplay && (pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
+            if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
                 if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
                 if (mService.updateOrientationFromAppTokensLocked(mDisplayId)) {
                     setLayoutNeeded();
@@ -3008,10 +3137,10 @@
         forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);
 
         // Window frames may have changed. Tell the input dispatcher about it.
-        mService.mInputMonitor.layoutInputConsumers(dw, dh);
-        mService.mInputMonitor.setUpdateInputWindowsNeededLw();
+        mInputMonitor.layoutInputConsumers(dw, dh);
+        mInputMonitor.setUpdateInputWindowsNeededLw();
         if (updateInputWindows) {
-            mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+            mInputMonitor.updateInputWindowsLw(false /*force*/);
         }
 
         mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
@@ -3338,11 +3467,16 @@
 
         private void addStackReferenceIfNeeded(TaskStack stack) {
             if (stack.isActivityTypeHome()) {
+                // TODO(b/111363427) Rollback to throws exceptions once we figure out how to
+                // properly deal with home type stack when external display removed
                 if (mHomeStack != null) {
-                    throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
+                    // throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
+                    //        + mHomeStack + " already exist on display=" + this + " stack=" + stack);
+                    Slog.e(TAG, "addStackReferenceIfNeeded: home stack="
                             + mHomeStack + " already exist on display=" + this + " stack=" + stack);
+                } else {
+                    mHomeStack = stack;
                 }
-                mHomeStack = stack;
             }
             final int windowingMode = stack.getWindowingMode();
             if (windowingMode == WINDOWING_MODE_PINNED) {
@@ -3425,25 +3559,51 @@
          * @return The proper position for the stack.
          */
         private int findPositionForStack(int requestedPosition, TaskStack stack, boolean adding) {
+            if (stack.inPinnedWindowingMode()) {
+                return POSITION_TOP;
+            }
+
             final int topChildPosition = mChildren.size() - 1;
-            boolean toTop = requestedPosition == POSITION_TOP;
-            toTop |= adding ? requestedPosition >= topChildPosition + 1
-                    : requestedPosition >= topChildPosition;
-            int targetPosition = requestedPosition;
-
-            if (toTop && stack.getWindowingMode() != WINDOWING_MODE_PINNED && hasPinnedStack()) {
-                // The pinned stack is always the top most stack (always-on-top) when it is present.
-                TaskStack topStack = mChildren.get(topChildPosition);
-                if (topStack.getWindowingMode() != WINDOWING_MODE_PINNED) {
-                    throw new IllegalStateException("Pinned stack isn't top stack??? " + mChildren);
+            int belowAlwaysOnTopPosition = POSITION_BOTTOM;
+            for (int i = topChildPosition; i >= 0; --i) {
+                if (getStacks().get(i) != stack && !getStacks().get(i).isAlwaysOnTop()) {
+                    belowAlwaysOnTopPosition = i;
+                    break;
                 }
+            }
 
-                // So, stack is moved just below the pinned stack.
-                // When we're adding a new stack the target is the current pinned stack position.
-                // When we're positioning an existing stack the target is the position below pinned
-                // stack, because WindowContainer#positionAt() first removes element and then adds
-                // it to specified place.
-                targetPosition = adding ? topChildPosition : topChildPosition - 1;
+            // The max possible position we can insert the stack at.
+            int maxPosition = POSITION_TOP;
+            // The min possible position we can insert the stack at.
+            int minPosition = POSITION_BOTTOM;
+
+            if (stack.isAlwaysOnTop()) {
+                if (hasPinnedStack()) {
+                    // Always-on-top stacks go below the pinned stack.
+                    maxPosition = getStacks().indexOf(mPinnedStack) - 1;
+                }
+                // Always-on-top stacks need to be above all other stacks.
+                minPosition = belowAlwaysOnTopPosition !=
+                        POSITION_BOTTOM ? belowAlwaysOnTopPosition : topChildPosition;
+            } else {
+                // Other stacks need to be below the always-on-top stacks.
+                maxPosition = belowAlwaysOnTopPosition !=
+                        POSITION_BOTTOM ? belowAlwaysOnTopPosition : 0;
+            }
+
+            int targetPosition = requestedPosition;
+            targetPosition = Math.min(targetPosition, maxPosition);
+            targetPosition = Math.max(targetPosition, minPosition);
+
+            int prevPosition = getStacks().indexOf(stack);
+            // The positions we calculated above (maxPosition, minPosition) do not take into
+            // consideration the following edge cases.
+            // 1) We need to adjust the position depending on the value "adding".
+            // 2) When we are moving a stack to another position, we also need to adjust the
+            //    position depending on whether the stack is moving to a higher or lower position.
+            if ((targetPosition != requestedPosition) &&
+                    (adding || targetPosition < prevPosition)) {
+                targetPosition++;
             }
 
             return targetPosition;
@@ -3535,10 +3695,13 @@
             if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
                     || isStackVisible(WINDOWING_MODE_FREEFORM)) {
                 // Apps and their containers are not allowed to specify an orientation while the
-                // docked or freeform stack is visible...except for the home stack/task if the
-                // docked stack is minimized and it actually set something.
+                // docked or freeform stack is visible...except for the home stack if the docked
+                // stack is minimized and it actually set something and the bounds is different from
+                // the display.
                 if (mHomeStack != null && mHomeStack.isVisible()
-                        && mDividerControllerLocked.isMinimizedDock()) {
+                        && mDividerControllerLocked.isMinimizedDock()
+                        && !(mDividerControllerLocked.isHomeStackResizable()
+                            && mHomeStack.matchParentBounds())) {
                     final int orientation = mHomeStack.getOrientation();
                     if (orientation != SCREEN_ORIENTATION_UNSET) {
                         return orientation;
@@ -3694,6 +3857,19 @@
         }
 
         @Override
+        SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+            final SurfaceControl.Builder builder = super.makeChildSurface(child);
+            if (child instanceof WindowToken && ((WindowToken) child).mRoundedCornerOverlay) {
+                // To draw above the ColorFade layer during the screen off transition, the
+                // rounded corner overlays need to be at the root of the surface hierarchy.
+                // TODO: move the ColorLayer into the display overlay layer such that this is not
+                // necessary anymore.
+                builder.setParent(null);
+            }
+            return builder;
+        }
+
+        @Override
         void assignChildLayers(SurfaceControl.Transaction t) {
             assignChildLayers(t, null /* imeContainer */);
         }
@@ -3709,6 +3885,10 @@
                     wt.assignRelativeLayer(t, mTaskStackContainers.getSplitScreenDividerAnchor(), 1);
                     continue;
                 }
+                if (wt.mRoundedCornerOverlay) {
+                    wt.assignLayer(t, WindowManagerPolicy.COLOR_FADE_LAYER + 1);
+                    continue;
+                }
                 wt.assignLayer(t, j);
                 wt.assignChildLayers(t);
 
@@ -4012,4 +4192,8 @@
     private boolean canUpdateImeTarget() {
         return mDeferUpdateImeTargetCount == 0;
     }
+
+    InputMonitor getInputMonitor() {
+        return mInputMonitor;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
new file mode 100644
index 0000000..9151ddf
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -0,0 +1,260 @@
+/*
+ * 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.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED;
+import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.content.Intent;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
+import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
+
+import java.io.PrintWriter;
+
+/**
+ * The policy that provides the basic behaviors and states of a display to show UI.
+ */
+public class DisplayPolicy {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayPolicy" : TAG_WM;
+
+    private final WindowManagerService mService;
+    private final Object mLock;
+
+    private final boolean mCarDockEnablesAccelerometer;
+    private final boolean mDeskDockEnablesAccelerometer;
+
+    private volatile int mLidState = LID_ABSENT;
+    private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+    private volatile boolean mHdmiPlugged;
+
+    private volatile boolean mHasNavigationBar;
+    // Can the navigation bar ever move to the side?
+    private volatile boolean mNavigationBarCanMove;
+
+    // Written by vr manager thread, only read in this class.
+    private volatile boolean mPersistentVrModeEnabled;
+
+    private volatile boolean mAwake;
+    private volatile boolean mScreenOnEarly;
+    private volatile boolean mScreenOnFully;
+    private volatile ScreenOnListener mScreenOnListener;
+
+    private volatile boolean mKeyguardDrawComplete;
+    private volatile boolean mWindowManagerDrawComplete;
+
+    DisplayPolicy(WindowManagerService service) {
+        mService = service;
+        mLock = service.getWindowManagerLock();
+        mCarDockEnablesAccelerometer = service.mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_carDockEnablesAccelerometer);
+        mDeskDockEnablesAccelerometer = service.mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_deskDockEnablesAccelerometer);
+    }
+
+    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;
+
+        mHasNavigationBar = mService.mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_showNavigationBar);
+
+        // Allow a system property to override this. Used by the emulator.
+        // See also hasNavigationBar().
+        String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
+        if ("1".equals(navBarOverride)) {
+            mHasNavigationBar = false;
+        } else if ("0".equals(navBarOverride)) {
+            mHasNavigationBar = true;
+        }
+    }
+
+    public void setHdmiPlugged(boolean plugged) {
+        setHdmiPlugged(plugged, false /* force */);
+    }
+
+    public void setHdmiPlugged(boolean plugged, boolean force) {
+        if (force || mHdmiPlugged != plugged) {
+            mHdmiPlugged = plugged;
+            mService.updateRotation(true /* alwaysSendConfiguration */, true /* forceRelayout */);
+            final Intent intent = new Intent(ACTION_HDMI_PLUGGED);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged);
+            mService.mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+        }
+    }
+
+    boolean isHdmiPlugged() {
+        return mHdmiPlugged;
+    }
+
+    boolean isCarDockEnablesAccelerometer() {
+        return mCarDockEnablesAccelerometer;
+    }
+
+    boolean isDeskDockEnablesAccelerometer() {
+        return mDeskDockEnablesAccelerometer;
+    }
+
+    public void setPersistentVrModeEnabled(boolean persistentVrModeEnabled) {
+        mPersistentVrModeEnabled = persistentVrModeEnabled;
+    }
+
+    public boolean isPersistentVrModeEnabled() {
+        return mPersistentVrModeEnabled;
+    }
+
+    public void setDockMode(int dockMode) {
+        mDockMode = dockMode;
+    }
+
+    public int getDockMode() {
+        return mDockMode;
+    }
+
+    public boolean hasNavigationBar() {
+        return mHasNavigationBar;
+    }
+
+    public boolean navigationBarCanMove() {
+        return mNavigationBarCanMove;
+    }
+
+    public void setLidState(int lidState) {
+        mLidState = lidState;
+    }
+
+    public int getLidState() {
+        return mLidState;
+    }
+
+    public void setAwake(boolean awake) {
+        mAwake = awake;
+    }
+
+    public boolean isAwake() {
+        return mAwake;
+    }
+
+    public boolean isScreenOnEarly() {
+        return mScreenOnEarly;
+    }
+
+    public boolean isScreenOnFully() {
+        return mScreenOnFully;
+    }
+
+    public boolean isKeyguardDrawComplete() {
+        return mKeyguardDrawComplete;
+    }
+
+    public boolean isWindowManagerDrawComplete() {
+        return mWindowManagerDrawComplete;
+    }
+
+    public ScreenOnListener getScreenOnListener() {
+        return mScreenOnListener;
+    }
+
+    public void screenTurnedOn(ScreenOnListener screenOnListener) {
+        synchronized (mLock) {
+            mScreenOnEarly = true;
+            mScreenOnFully = false;
+            mKeyguardDrawComplete = false;
+            mWindowManagerDrawComplete = false;
+            mScreenOnListener = screenOnListener;
+        }
+    }
+
+    public void screenTurnedOff() {
+        synchronized (mLock) {
+            mScreenOnEarly = false;
+            mScreenOnFully = false;
+            mKeyguardDrawComplete = false;
+            mWindowManagerDrawComplete = false;
+            mScreenOnListener = null;
+        }
+    }
+
+    /** Return false if we are not awake yet or we have already informed of this event. */
+    public boolean finishKeyguardDrawn() {
+        synchronized (mLock) {
+            if (!mScreenOnEarly || mKeyguardDrawComplete) {
+                return false;
+            }
+
+            mKeyguardDrawComplete = true;
+            mWindowManagerDrawComplete = false;
+        }
+        return true;
+    }
+
+    /** Return false if screen is not turned on or we did already handle this case earlier. */
+    public boolean finishWindowsDrawn() {
+        synchronized (mLock) {
+            if (!mScreenOnEarly || mWindowManagerDrawComplete) {
+                return false;
+            }
+
+            mWindowManagerDrawComplete = true;
+        }
+        return true;
+    }
+
+    /** Return false if it is not ready to turn on. */
+    public boolean finishScreenTurningOn() {
+        synchronized (mLock) {
+            if (DEBUG_SCREEN_ON) Slog.d(TAG,
+                    "finishScreenTurningOn: mAwake=" + mAwake
+                            + ", mScreenOnEarly=" + mScreenOnEarly
+                            + ", mScreenOnFully=" + mScreenOnFully
+                            + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
+                            + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
+
+            if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete
+                    || (mAwake && !mKeyguardDrawComplete)) {
+                return false;
+            }
+
+            if (DEBUG_SCREEN_ON) Slog.i(TAG, "Finished screen turning on...");
+            mScreenOnListener = null;
+            mScreenOnFully = true;
+        }
+        return true;
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "DisplayPolicy");
+        pw.print(prefix + "  mCarDockEnablesAccelerometer=" + mCarDockEnablesAccelerometer);
+        pw.println(" mDeskDockEnablesAccelerometer=" + mDeskDockEnablesAccelerometer);
+        pw.print(prefix + "  mDockMode=" + Intent.dockStateToString(mDockMode));
+        pw.println(" mLidState=" + WindowManagerFuncs.lidStateToString(mLidState));
+        pw.print(prefix + "  mAwake=" + mAwake);
+        pw.print(" mScreenOnEarly=" + mScreenOnEarly);
+        pw.println(" mScreenOnFully=" + mScreenOnFully);
+        pw.print(prefix + "  mKeyguardDrawComplete=" + mKeyguardDrawComplete);
+        pw.println(" mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
+        pw.println(prefix + "  mHdmiPlugged=" + mHdmiPlugged);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
new file mode 100644
index 0000000..685c444
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -0,0 +1,881 @@
+/*
+ * 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 com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+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.res.Resources;
+import android.database.ContentObserver;
+import android.hardware.power.V1_0.PowerHint;
+import android.os.Handler;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Surface;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.UiThread;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.policy.WindowOrientationListener;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+import java.io.PrintWriter;
+
+/**
+ * Defines the mapping between orientation and rotation of a display.
+ * Non-public methods are assumed to run inside WM lock.
+ */
+public class DisplayRotation {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM;
+
+    private final WindowManagerService mService;
+    private final DisplayPolicy mDisplayPolicy;
+    private final Context mContext;
+    private final Object mLock;
+
+    public final boolean isDefaultDisplay;
+    private final boolean mSupportAutoRotation;
+    private final int mLidOpenRotation;
+    private final int mCarDockRotation;
+    private final int mDeskDockRotation;
+    private final int mUndockedHdmiRotation;
+
+    private OrientationListener mOrientationListener;
+    private StatusBarManagerInternal mStatusBarManagerInternal;
+    private SettingsObserver mSettingsObserver;
+
+    // Default display does not rotate, apps that require non-default orientation will have to
+    // have the orientation emulated.
+    private boolean mForceDefaultOrientation;
+
+    private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+    @VisibleForTesting
+    int mLandscapeRotation;  // default landscape
+    @VisibleForTesting
+    int mSeascapeRotation;   // "other" landscape, 180 degrees from mLandscapeRotation
+    @VisibleForTesting
+    int mPortraitRotation;   // default portrait
+    @VisibleForTesting
+    int mUpsideDownRotation; // "other" portrait
+
+    // Behavior of rotation suggestions. (See Settings.Secure.SHOW_ROTATION_SUGGESTION)
+    private int mShowRotationSuggestions;
+
+    private int mAllowAllRotations = -1;
+    private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
+    private int mUserRotation = Surface.ROTATION_0;
+
+    private int mDemoHdmiRotation;
+    private int mDemoRotation;
+    private boolean mDemoHdmiRotationLock;
+    private boolean mDemoRotationLock;
+
+    DisplayRotation(WindowManagerService service, DisplayContent displayContent) {
+        this(service, displayContent, displayContent.getDisplayPolicy(),
+                service.mContext, service.getWindowManagerLock());
+    }
+
+    @VisibleForTesting
+    DisplayRotation(WindowManagerService service, DisplayContent displayContent,
+            DisplayPolicy displayPolicy, Context context, Object lock) {
+        mService = service;
+        mDisplayPolicy = displayPolicy;
+        mContext = context;
+        mLock = lock;
+        isDefaultDisplay = displayContent.isDefaultDisplay;
+
+        mSupportAutoRotation = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_supportAutoRotation);
+        mLidOpenRotation = readRotation(
+                com.android.internal.R.integer.config_lidOpenRotation);
+        mCarDockRotation = readRotation(
+                com.android.internal.R.integer.config_carDockRotation);
+        mDeskDockRotation = readRotation(
+                com.android.internal.R.integer.config_deskDockRotation);
+        mUndockedHdmiRotation = readRotation(
+                com.android.internal.R.integer.config_undockedHdmiRotation);
+
+        if (isDefaultDisplay) {
+            final Handler uiHandler = UiThread.getHandler();
+            mOrientationListener = new OrientationListener(mContext, uiHandler);
+            mOrientationListener.setCurrentRotation(displayContent.getRotation());
+            mSettingsObserver = new SettingsObserver(uiHandler);
+            mSettingsObserver.observe();
+        }
+    }
+
+    private int readRotation(int resID) {
+        try {
+            final int rotation = mContext.getResources().getInteger(resID);
+            switch (rotation) {
+                case 0:
+                    return Surface.ROTATION_0;
+                case 90:
+                    return Surface.ROTATION_90;
+                case 180:
+                    return Surface.ROTATION_180;
+                case 270:
+                    return Surface.ROTATION_270;
+            }
+        } catch (Resources.NotFoundException e) {
+            // fall through
+        }
+        return -1;
+    }
+
+    void configure(int width, int height, int shortSizeDp, int longSizeDp) {
+        final Resources res = mContext.getResources();
+        if (width > height) {
+            mLandscapeRotation = Surface.ROTATION_0;
+            mSeascapeRotation = Surface.ROTATION_180;
+            if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
+                mPortraitRotation = Surface.ROTATION_90;
+                mUpsideDownRotation = Surface.ROTATION_270;
+            } else {
+                mPortraitRotation = Surface.ROTATION_270;
+                mUpsideDownRotation = Surface.ROTATION_90;
+            }
+        } else {
+            mPortraitRotation = Surface.ROTATION_0;
+            mUpsideDownRotation = Surface.ROTATION_180;
+            if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
+                mLandscapeRotation = Surface.ROTATION_270;
+                mSeascapeRotation = Surface.ROTATION_90;
+            } else {
+                mLandscapeRotation = Surface.ROTATION_90;
+                mSeascapeRotation = Surface.ROTATION_270;
+            }
+        }
+
+        // For demo purposes, allow the rotation of the HDMI display to be controlled.
+        // By default, HDMI locks rotation to landscape.
+        if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
+            mDemoHdmiRotation = mPortraitRotation;
+        } else {
+            mDemoHdmiRotation = mLandscapeRotation;
+        }
+        mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
+
+        // For demo purposes, allow the rotation of the remote display to be controlled.
+        // By default, remote display locks rotation to landscape.
+        if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) {
+            mDemoRotation = mPortraitRotation;
+        } else {
+            mDemoRotation = mLandscapeRotation;
+        }
+        mDemoRotationLock = SystemProperties.getBoolean("persist.demo.rotationlock", false);
+
+        // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
+        // http://developer.android.com/guide/practices/screens_support.html#range
+        // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen
+        // so if the orientation is forced, we need to respect that no matter what.
+        final boolean isCar = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE);
+        // For TV, it's usually 960dp x 540dp, ignore the size limitation.
+        // so if the orientation is forced, we need to respect that no matter what.
+        final boolean isTv = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_LEANBACK);
+        mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) &&
+                res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
+                // For debug purposes the next line turns this feature off with:
+                // $ adb shell setprop config.override_forced_orient true
+                // $ adb shell wm size reset
+                !"true".equals(SystemProperties.get("config.override_forced_orient"));
+    }
+
+    void setRotation(int rotation) {
+        if (mOrientationListener != null) {
+            mOrientationListener.setCurrentRotation(rotation);
+        }
+    }
+
+    void setCurrentOrientation(int newOrientation) {
+        if (newOrientation != mCurrentAppOrientation) {
+            mCurrentAppOrientation = newOrientation;
+            if (isDefaultDisplay) {
+                updateOrientationListenerLw();
+            }
+        }
+    }
+
+    /** @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true. */
+    boolean isDefaultOrientationForced() {
+        return mForceDefaultOrientation;
+    }
+
+    public int getLandscapeRotation() {
+        return mLandscapeRotation;
+    }
+
+    public int getSeascapeRotation() {
+        return mSeascapeRotation;
+    }
+
+    public int getPortraitRotation() {
+        return mPortraitRotation;
+    }
+
+    public int getUpsideDownRotation() {
+        return mUpsideDownRotation;
+    }
+
+    public int getCurrentAppOrientation() {
+        return mCurrentAppOrientation;
+    }
+
+    public DisplayPolicy getDisplayPolicy() {
+        return mDisplayPolicy;
+    }
+
+    public WindowOrientationListener getOrientationListener() {
+        return mOrientationListener;
+    }
+
+    public int getUserRotation() {
+        return mUserRotation;
+    }
+
+    public int getUserRotationMode() {
+        return mUserRotationMode;
+    }
+
+    public void updateOrientationListener() {
+        synchronized (mLock) {
+            updateOrientationListenerLw();
+        }
+    }
+
+    /**
+     * Various use cases for invoking this function:
+     * <li>Screen turning off, should always disable listeners if already enabled.</li>
+     * <li>Screen turned on and current app has sensor based orientation, enable listeners
+     *     if not already enabled.</li>
+     * <li>Screen turned on and current app does not have sensor orientation, disable listeners
+     *     if already enabled.</li>
+     * <li>Screen turning on and current app has sensor based orientation, enable listeners
+     *     if needed.</li>
+     * <li>screen turning on and current app has nosensor based orientation, do nothing.</li>
+     */
+    private void updateOrientationListenerLw() {
+        if (mOrientationListener == null || !mOrientationListener.canDetectOrientation()) {
+            // If sensor is turned off or nonexistent for some reason.
+            return;
+        }
+
+        final boolean screenOnEarly = mDisplayPolicy.isScreenOnEarly();
+        final boolean awake = mDisplayPolicy.isAwake();
+        final boolean keyguardDrawComplete = mDisplayPolicy.isKeyguardDrawComplete();
+        final boolean windowManagerDrawComplete = mDisplayPolicy.isWindowManagerDrawComplete();
+
+        // Could have been invoked due to screen turning on or off or
+        // change of the currently visible window's orientation.
+        if (DEBUG_ORIENTATION) Slog.v(TAG, "screenOnEarly=" + screenOnEarly
+                + ", awake=" + awake + ", currentAppOrientation=" + mCurrentAppOrientation
+                + ", orientationSensorEnabled=" + mOrientationListener.mEnabled
+                + ", keyguardDrawComplete=" + keyguardDrawComplete
+                + ", windowManagerDrawComplete=" + windowManagerDrawComplete);
+
+        boolean disable = true;
+        // Note: We postpone the rotating of the screen until the keyguard as well as the
+        // window manager have reported a draw complete or the keyguard is going away in dismiss
+        // mode.
+        if (screenOnEarly && awake && ((keyguardDrawComplete && windowManagerDrawComplete))) {
+            if (needSensorRunning()) {
+                disable = false;
+                // Enable listener if not already enabled.
+                if (!mOrientationListener.mEnabled) {
+                    // Don't clear the current sensor orientation if the keyguard is going away in
+                    // dismiss mode. This allows window manager to use the last sensor reading to
+                    // determine the orientation vs. falling back to the last known orientation if
+                    // the sensor reading was cleared which can cause it to relaunch the app that
+                    // will show in the wrong orientation first before correcting leading to app
+                    // launch delays.
+                    mOrientationListener.enable(true /* clearCurrentRotation */);
+                }
+            }
+        }
+        // Check if sensors need to be disabled.
+        if (disable && mOrientationListener.mEnabled) {
+            mOrientationListener.disable();
+        }
+    }
+
+    /**
+     * We always let the sensor be switched on by default except when
+     * the user has explicitly disabled sensor based rotation or when the
+     * screen is switched off.
+     */
+    private boolean needSensorRunning() {
+        if (mSupportAutoRotation) {
+            if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
+                    || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+                    || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
+                    || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
+                // If the application has explicitly requested to follow the
+                // orientation, then we need to turn the sensor on.
+                return true;
+            }
+        }
+
+        final int dockMode = mDisplayPolicy.getDockMode();
+        if ((mDisplayPolicy.isCarDockEnablesAccelerometer()
+                && dockMode == Intent.EXTRA_DOCK_STATE_CAR)
+                || (mDisplayPolicy.isDeskDockEnablesAccelerometer()
+                        && (dockMode == Intent.EXTRA_DOCK_STATE_DESK
+                                || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
+                                || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) {
+            // Enable accelerometer if we are docked in a dock that enables accelerometer
+            // orientation management.
+            return true;
+        }
+
+        if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
+            // If the setting for using the sensor by default is enabled, then
+            // we will always leave it on.  Note that the user could go to
+            // a window that forces an orientation that does not use the
+            // sensor and in theory we could turn it off... however, when next
+            // turning it on we won't have a good value for the current
+            // orientation for a little bit, which can cause orientation
+            // changes to lag, so we'd like to keep it always on.  (It will
+            // still be turned off when the screen is off.)
+
+            // When locked we can provide rotation suggestions users can approve to change the
+            // current screen rotation. To do this the sensor needs to be running.
+            return mSupportAutoRotation &&
+                    mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED;
+        }
+        return mSupportAutoRotation;
+    }
+
+    /**
+     * Given an orientation constant, returns the appropriate surface rotation,
+     * taking into account sensors, docking mode, rotation lock, and other factors.
+     *
+     * @param orientation An orientation constant, such as
+     * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
+     * @param lastRotation The most recently used rotation.
+     * @param defaultDisplay Flag indicating whether the rotation is computed for the default
+     *                       display. Currently for all non-default displays sensors, docking mode,
+     *                       rotation lock and other factors are ignored.
+     * @return The surface rotation to use.
+     */
+    int rotationForOrientation(int orientation, int lastRotation) {
+        if (DEBUG_ORIENTATION) {
+            Slog.v(TAG, "rotationForOrientation(orient="
+                        + orientation + ", last=" + lastRotation
+                        + "); user=" + mUserRotation + " "
+                        + (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
+                            ? "USER_ROTATION_LOCKED" : "")
+                        );
+        }
+
+        if (mForceDefaultOrientation) {
+            return Surface.ROTATION_0;
+        }
+
+        int sensorRotation = mOrientationListener != null
+                ? mOrientationListener.getProposedRotation() // may be -1
+                : -1;
+        if (sensorRotation < 0) {
+            sensorRotation = lastRotation;
+        }
+
+        final int lidState = mDisplayPolicy.getLidState();
+        final int dockMode = mDisplayPolicy.getDockMode();
+        final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
+        final boolean carDockEnablesAccelerometer =
+                mDisplayPolicy.isCarDockEnablesAccelerometer();
+        final boolean deskDockEnablesAccelerometer =
+                mDisplayPolicy.isDeskDockEnablesAccelerometer();
+
+        final int preferredRotation;
+        if (!isDefaultDisplay) {
+            // For secondary displays we ignore things like displays sensors, docking mode and
+            // rotation lock, and always prefer a default rotation.
+            preferredRotation = Surface.ROTATION_0;
+        } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
+            // Ignore sensor when lid switch is open and rotation is forced.
+            preferredRotation = mLidOpenRotation;
+        } else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR
+                && (carDockEnablesAccelerometer || mCarDockRotation >= 0)) {
+            // Ignore sensor when in car dock unless explicitly enabled.
+            // This case can override the behavior of NOSENSOR, and can also
+            // enable 180 degree rotation while docked.
+            preferredRotation = carDockEnablesAccelerometer ? sensorRotation : mCarDockRotation;
+        } else if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
+                || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
+                || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
+                && (deskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
+            // Ignore sensor when in desk dock unless explicitly enabled.
+            // This case can override the behavior of NOSENSOR, and can also
+            // enable 180 degree rotation while docked.
+            preferredRotation = deskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation;
+        } else if (hdmiPlugged && mDemoHdmiRotationLock) {
+            // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
+            // Note that the dock orientation overrides the HDMI orientation.
+            preferredRotation = mDemoHdmiRotation;
+        } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
+                && mUndockedHdmiRotation >= 0) {
+            // Ignore sensor when plugged into HDMI and an undocked orientation has
+            // been specified in the configuration (only for legacy devices without
+            // full multi-display support).
+            // Note that the dock orientation overrides the HDMI orientation.
+            preferredRotation = mUndockedHdmiRotation;
+        } else if (mDemoRotationLock) {
+            // Ignore sensor when demo rotation lock is enabled.
+            // Note that the dock orientation and HDMI rotation lock override this.
+            preferredRotation = mDemoRotation;
+        } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
+            // While in VR, apps always prefer a portrait rotation. This does not change
+            // any apps that explicitly set landscape, but does cause sensors be ignored,
+            // and ignored any orientation lock that the user has set (this conditional
+            // should remain above the ORIENTATION_LOCKED conditional below).
+            preferredRotation = mPortraitRotation;
+        } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+            // Application just wants to remain locked in the last rotation.
+            preferredRotation = lastRotation;
+        } else if (!mSupportAutoRotation) {
+            // If we don't support auto-rotation then bail out here and ignore
+            // the sensor and any rotation lock settings.
+            preferredRotation = -1;
+        } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
+                        && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
+                                || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+                                || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
+                                || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
+                                || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
+                || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
+                || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+                || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
+                || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
+            // Otherwise, use sensor only if requested by the application or enabled
+            // by default for USER or UNSPECIFIED modes.  Does not apply to NOSENSOR.
+            if (mAllowAllRotations < 0) {
+                // Can't read this during init() because the context doesn't
+                // have display metrics at that time so we cannot determine
+                // tablet vs. phone then.
+                mAllowAllRotations = mContext.getResources().getBoolean(
+                        com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0;
+            }
+            if (sensorRotation != Surface.ROTATION_180
+                    || mAllowAllRotations == 1
+                    || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+                    || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
+                preferredRotation = sensorRotation;
+            } else {
+                preferredRotation = lastRotation;
+            }
+        } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
+                && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+            // Apply rotation lock.  Does not apply to NOSENSOR.
+            // The idea is that the user rotation expresses a weak preference for the direction
+            // of gravity and as NOSENSOR is never affected by gravity, then neither should
+            // NOSENSOR be affected by rotation lock (although it will be affected by docks).
+            preferredRotation = mUserRotation;
+        } else {
+            // No overriding preference.
+            // We will do exactly what the application asked us to do.
+            preferredRotation = -1;
+        }
+
+        switch (orientation) {
+            case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
+                // Return portrait unless overridden.
+                if (isAnyPortrait(preferredRotation)) {
+                    return preferredRotation;
+                }
+                return mPortraitRotation;
+
+            case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
+                // Return landscape unless overridden.
+                if (isLandscapeOrSeascape(preferredRotation)) {
+                    return preferredRotation;
+                }
+                return mLandscapeRotation;
+
+            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+                // Return reverse portrait unless overridden.
+                if (isAnyPortrait(preferredRotation)) {
+                    return preferredRotation;
+                }
+                return mUpsideDownRotation;
+
+            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+                // Return seascape unless overridden.
+                if (isLandscapeOrSeascape(preferredRotation)) {
+                    return preferredRotation;
+                }
+                return mSeascapeRotation;
+
+            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+            case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+                // Return either landscape rotation.
+                if (isLandscapeOrSeascape(preferredRotation)) {
+                    return preferredRotation;
+                }
+                if (isLandscapeOrSeascape(lastRotation)) {
+                    return lastRotation;
+                }
+                return mLandscapeRotation;
+
+            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+            case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+                // Return either portrait rotation.
+                if (isAnyPortrait(preferredRotation)) {
+                    return preferredRotation;
+                }
+                if (isAnyPortrait(lastRotation)) {
+                    return lastRotation;
+                }
+                return mPortraitRotation;
+
+            default:
+                // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR,
+                // just return the preferred orientation we already calculated.
+                if (preferredRotation >= 0) {
+                    return preferredRotation;
+                }
+                return Surface.ROTATION_0;
+        }
+    }
+
+    private boolean isLandscapeOrSeascape(int rotation) {
+        return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
+    }
+
+    private boolean isAnyPortrait(int rotation) {
+        return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
+    }
+
+    /**
+     * Given an orientation constant and a rotation, returns true if the rotation
+     * has compatible metrics to the requested orientation.  For example, if
+     * the application requested landscape and got seascape, then the rotation
+     * has compatible metrics; if the application requested portrait and got landscape,
+     * then the rotation has incompatible metrics; if the application did not specify
+     * a preference, then anything goes.
+     *
+     * @param orientation An orientation constant, such as
+     * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
+     * @param rotation The rotation to check.
+     * @return True if the rotation is compatible with the requested orientation.
+     */
+    boolean rotationHasCompatibleMetrics(int orientation, int rotation) {
+        switch (orientation) {
+            case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
+            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+                return isAnyPortrait(rotation);
+
+            case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
+            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+                return isLandscapeOrSeascape(rotation);
+
+            default:
+                return true;
+        }
+    }
+
+    private boolean isValidRotationChoice(final int preferredRotation) {
+        // Determine if the given app orientation is compatible with the provided rotation choice.
+        switch (mCurrentAppOrientation) {
+            case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
+                // Works with any of the 4 rotations.
+                return preferredRotation >= 0;
+
+            case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+                // It's possible for the user pref to be set at 180 because of FULL_USER. This would
+                // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait
+                // but never to go to 180.
+                return preferredRotation == mPortraitRotation;
+
+            case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+                // Works landscape or seascape.
+                return isLandscapeOrSeascape(preferredRotation);
+
+            case ActivityInfo.SCREEN_ORIENTATION_USER:
+            case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
+                // Works with any rotation except upside down.
+                return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
+        }
+
+        return false;
+    }
+
+    private boolean isRotationChoicePossible(int orientation) {
+        // Rotation choice is only shown when the user is in locked mode.
+        if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
+
+        // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
+        // demo, hdmi, vr, etc mode.
+
+        // Determine if the rotation is currently forced.
+        if (mForceDefaultOrientation) {
+            return false; // Rotation is forced to default orientation.
+        }
+
+        final int lidState = mDisplayPolicy.getLidState();
+        if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
+            return false; // Rotation is forced mLidOpenRotation.
+        }
+
+        final int dockMode = mDisplayPolicy.getDockMode();
+        final boolean carDockEnablesAccelerometer = false;
+        if (dockMode == Intent.EXTRA_DOCK_STATE_CAR && !carDockEnablesAccelerometer) {
+            return false; // Rotation forced to mCarDockRotation.
+        }
+
+        final boolean deskDockEnablesAccelerometer =
+                mDisplayPolicy.isDeskDockEnablesAccelerometer();
+        if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
+                || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
+                || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
+                && !deskDockEnablesAccelerometer) {
+            return false; // Rotation forced to mDeskDockRotation.
+        }
+
+        final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
+        if (hdmiPlugged && mDemoHdmiRotationLock) {
+            return false; // Rotation forced to mDemoHdmiRotation.
+
+        } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
+                && mUndockedHdmiRotation >= 0) {
+            return false; // Rotation forced to mUndockedHdmiRotation.
+
+        } else if (mDemoRotationLock) {
+            return false; // Rotation forced to mDemoRotation.
+
+        } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
+            return false; // Rotation forced to mPortraitRotation.
+
+        } else if (!mSupportAutoRotation) {
+            return false;
+        }
+
+        // Ensure that some rotation choice is possible for the given orientation.
+        switch (orientation) {
+            case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
+            case ActivityInfo.SCREEN_ORIENTATION_USER:
+            case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
+            case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+            case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+                // NOSENSOR description is ambiguous, in reality WM ignores user choice.
+                return true;
+        }
+
+        // Rotation is forced, should be controlled by system.
+        return false;
+    }
+
+    /** Notify the StatusBar that system rotation suggestion has changed. */
+    private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
+        if (mStatusBarManagerInternal == null) {
+            mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
+        }
+        if (mStatusBarManagerInternal != null) {
+            mStatusBarManagerInternal.onProposedRotationChanged(rotation, isValid);
+        }
+    }
+
+    private static String allowAllRotationsToString(int allowAll) {
+        switch (allowAll) {
+            case -1:
+                return "unknown";
+            case 0:
+                return "false";
+            case 1:
+                return "true";
+            default:
+                return Integer.toString(allowAll);
+        }
+    }
+
+    public void onUserSwitch() {
+        if (mSettingsObserver != null) {
+            mSettingsObserver.onChange(false);
+        }
+    }
+
+    /** Return whether the rotation settings has changed. */
+    private boolean updateSettings() {
+        final ContentResolver resolver = mContext.getContentResolver();
+        boolean shouldUpdateRotation = false;
+
+        synchronized (mLock) {
+            boolean shouldUpdateOrientationListener = false;
+
+            // Configure rotation suggestions.
+            final int showRotationSuggestions = Settings.Secure.getIntForUser(resolver,
+                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
+                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
+                    UserHandle.USER_CURRENT);
+            if (mShowRotationSuggestions != showRotationSuggestions) {
+                mShowRotationSuggestions = showRotationSuggestions;
+                shouldUpdateOrientationListener = true;
+            }
+
+            // Configure rotation lock.
+            final int userRotation = Settings.System.getIntForUser(resolver,
+                    Settings.System.USER_ROTATION, Surface.ROTATION_0,
+                    UserHandle.USER_CURRENT);
+            if (mUserRotation != userRotation) {
+                mUserRotation = userRotation;
+                shouldUpdateRotation = true;
+            }
+
+            final int userRotationMode = Settings.System.getIntForUser(resolver,
+                    Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0
+                            ? WindowManagerPolicy.USER_ROTATION_FREE
+                            : WindowManagerPolicy.USER_ROTATION_LOCKED;
+            if (mUserRotationMode != userRotationMode) {
+                mUserRotationMode = userRotationMode;
+                shouldUpdateOrientationListener = true;
+                shouldUpdateRotation = true;
+            }
+
+            if (shouldUpdateOrientationListener) {
+                updateOrientationListenerLw(); // Enable or disable the orientation listener.
+            }
+        }
+
+        return shouldUpdateRotation;
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "DisplayRotation");
+        pw.println(prefix + "  mCurrentAppOrientation="
+                + ActivityInfo.screenOrientationToString(mCurrentAppOrientation));
+        pw.print(prefix + "  mLandscapeRotation=" + Surface.rotationToString(mLandscapeRotation));
+        pw.println(" mSeascapeRotation=" + Surface.rotationToString(mSeascapeRotation));
+        pw.print(prefix + "  mPortraitRotation=" + Surface.rotationToString(mPortraitRotation));
+        pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation));
+
+        pw.print(prefix + "  mSupportAutoRotation=" + mSupportAutoRotation);
+        if (mOrientationListener != null) {
+            pw.print(" mOrientationSensorEnabled=" + mOrientationListener.mEnabled);
+        }
+        pw.println();
+
+        pw.print(prefix + "  mCarDockRotation=" + Surface.rotationToString(mCarDockRotation));
+        pw.println(" mDeskDockRotation=" + Surface.rotationToString(mDeskDockRotation));
+        pw.print(prefix + "  mUserRotationMode="
+                + WindowManagerPolicy.userRotationModeToString(mUserRotationMode));
+        pw.print(" mUserRotation=" + Surface.rotationToString(mUserRotation));
+        pw.println(" mAllowAllRotations=" + allowAllRotationsToString(mAllowAllRotations));
+
+        pw.print(prefix + "  mDemoHdmiRotation=" + Surface.rotationToString(mDemoHdmiRotation));
+        pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock);
+        pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation));
+        pw.println(prefix + "  mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
+    }
+
+    private class OrientationListener extends WindowOrientationListener {
+        final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
+        boolean mEnabled;
+
+        OrientationListener(Context context, Handler handler) {
+            super(context, handler);
+        }
+
+        private class UpdateRunnable implements Runnable {
+            final int mRotation;
+
+            UpdateRunnable(int rotation) {
+                mRotation = rotation;
+            }
+
+            @Override
+            public void run() {
+                // Send interaction hint to improve redraw performance.
+                mService.mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
+                if (isRotationChoicePossible(mCurrentAppOrientation)) {
+                    final boolean isValid = isValidRotationChoice(mRotation);
+                    sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
+                } else {
+                    mService.updateRotation(false /* alwaysSendConfiguration */,
+                            false /* forceRelayout */);
+                }
+            }
+        }
+
+        @Override
+        public void onProposedRotationChanged(int rotation) {
+            if (DEBUG_ORIENTATION) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
+            Runnable r = mRunnableCache.get(rotation, null);
+            if (r == null) {
+                r = new UpdateRunnable(rotation);
+                mRunnableCache.put(rotation, r);
+            }
+            getHandler().post(r);
+        }
+
+        @Override
+        public void enable(boolean clearCurrentRotation) {
+            super.enable(clearCurrentRotation);
+            mEnabled = true;
+            if (DEBUG_ORIENTATION) Slog.v(TAG, "Enabling listeners");
+        }
+
+        @Override
+        public void disable() {
+            super.disable();
+            mEnabled = false;
+            if (DEBUG_ORIENTATION) Slog.v(TAG, "Disabling listeners");
+        }
+    }
+
+    private class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        void observe() {
+            final ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
+                    UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.ACCELEROMETER_ROTATION), false, this,
+                    UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.USER_ROTATION), false, this,
+                    UserHandle.USER_ALL);
+            updateSettings();
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            if (updateSettings()) {
+                mService.updateRotation(true /* alwaysSendConfiguration */,
+                        false /* forceRelayout */);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplaySettings.java b/services/core/java/com/android/server/wm/DisplaySettings.java
index 97b64dc..bbb690f 100644
--- a/services/core/java/com/android/server/wm/DisplaySettings.java
+++ b/services/core/java/com/android/server/wm/DisplaySettings.java
@@ -19,12 +19,19 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.os.Environment;
+import android.provider.Settings;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.Xml;
+import android.view.Display;
+import android.view.DisplayInfo;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 
@@ -43,37 +50,48 @@
 /**
  * Current persistent settings about a display
  */
-public class DisplaySettings {
+class DisplaySettings {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplaySettings" : TAG_WM;
 
+    private final WindowManagerService mService;
     private final AtomicFile mFile;
     private final HashMap<String, Entry> mEntries = new HashMap<String, Entry>();
 
-    public static class Entry {
-        public final String name;
-        public int overscanLeft;
-        public int overscanTop;
-        public int overscanRight;
-        public int overscanBottom;
+    private static class Entry {
+        private final String name;
+        private int overscanLeft;
+        private int overscanTop;
+        private int overscanRight;
+        private int overscanBottom;
+        private int windowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
-        public Entry(String _name) {
+        private Entry(String _name) {
             name = _name;
         }
     }
 
-    public DisplaySettings() {
-        File dataDir = Environment.getDataDirectory();
-        File systemDir = new File(dataDir, "system");
-        mFile = new AtomicFile(new File(systemDir, "display_settings.xml"), "wm-displays");
+    DisplaySettings(WindowManagerService service) {
+        this(service, new File(Environment.getDataDirectory(), "system"));
     }
 
-    public void getOverscanLocked(String name, String uniqueId, Rect outRect) {
+    @VisibleForTesting
+    DisplaySettings(WindowManagerService service, File folder) {
+        mService = service;
+        mFile = new AtomicFile(new File(folder, "display_settings.xml"), "wm-displays");
+    }
+
+    private Entry getEntry(String name, String uniqueId) {
         // Try to get the entry with the unique if possible.
         // Else, fall back on the display name.
         Entry entry;
         if (uniqueId == null || (entry = mEntries.get(uniqueId)) == null) {
             entry = mEntries.get(name);
         }
+        return entry;
+    }
+
+    private void getOverscanLocked(String name, String uniqueId, Rect outRect) {
+        final Entry entry = getEntry(name, uniqueId);
         if (entry != null) {
             outRect.left = entry.overscanLeft;
             outRect.top = entry.overscanTop;
@@ -84,7 +102,7 @@
         }
     }
 
-    public void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
+    void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
             int bottom) {
         if (left == 0 && top == 0 && right == 0 && bottom == 0) {
             // Right now all we are storing is overscan; if there is no overscan,
@@ -105,7 +123,47 @@
         entry.overscanBottom = bottom;
     }
 
-    public void readSettingsLocked() {
+    private int getWindowingModeLocked(String name, String uniqueId, int displayId) {
+        final Entry entry = getEntry(name, uniqueId);
+        int windowingMode = entry != null ? entry.windowingMode
+                : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+        // This display used to be in freeform, but we don't support freeform anymore, so fall
+        // back to fullscreen.
+        if (windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
+                && !mService.mSupportsFreeformWindowManagement) {
+            return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+        }
+        // No record is present so use default windowing mode policy.
+        if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+            if (displayId == Display.DEFAULT_DISPLAY) {
+                windowingMode = (mService.mIsPc && mService.mSupportsFreeformWindowManagement)
+                        ? WindowConfiguration.WINDOWING_MODE_FREEFORM
+                        : WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+            } else {
+                windowingMode = mService.mSupportsFreeformWindowManagement
+                        ? WindowConfiguration.WINDOWING_MODE_FREEFORM
+                        : WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+            }
+        }
+        return windowingMode;
+    }
+
+    void applySettingsToDisplayLocked(DisplayContent dc) {
+        final DisplayInfo displayInfo = dc.getDisplayInfo();
+
+        // Setting windowing mode first, because it may override overscan values later.
+        dc.setWindowingMode(getWindowingModeLocked(displayInfo.name, displayInfo.uniqueId,
+                dc.getDisplayId()));
+
+        final Rect rect = new Rect();
+        getOverscanLocked(displayInfo.name, displayInfo.uniqueId, rect);
+        displayInfo.overscanLeft = rect.left;
+        displayInfo.overscanTop = rect.top;
+        displayInfo.overscanRight = rect.right;
+        displayInfo.overscanBottom = rect.bottom;
+    }
+
+    void readSettingsLocked() {
         FileInputStream stream;
         try {
             stream = mFile.openRead();
@@ -169,11 +227,15 @@
     }
 
     private int getIntAttribute(XmlPullParser parser, String name) {
+        return getIntAttribute(parser, name, 0 /* defaultValue */);
+    }
+
+    private int getIntAttribute(XmlPullParser parser, String name, int defaultValue) {
         try {
             String str = parser.getAttributeValue(null, name);
-            return str != null ? Integer.parseInt(str) : 0;
+            return str != null ? Integer.parseInt(str) : defaultValue;
         } catch (NumberFormatException e) {
-            return 0;
+            return defaultValue;
         }
     }
 
@@ -186,12 +248,14 @@
             entry.overscanTop = getIntAttribute(parser, "overscanTop");
             entry.overscanRight = getIntAttribute(parser, "overscanRight");
             entry.overscanBottom = getIntAttribute(parser, "overscanBottom");
+            entry.windowingMode = getIntAttribute(parser, "windowingMode",
+                    WindowConfiguration.WINDOWING_MODE_UNDEFINED);
             mEntries.put(name, entry);
         }
         XmlUtils.skipCurrentTag(parser);
     }
 
-    public void writeSettingsLocked() {
+    void writeSettingsLocked() {
         FileOutputStream stream;
         try {
             stream = mFile.startWrite();
@@ -221,6 +285,9 @@
                 if (entry.overscanBottom != 0) {
                     out.attribute(null, "overscanBottom", Integer.toString(entry.overscanBottom));
                 }
+                if (entry.windowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+                    out.attribute(null, "windowingMode", Integer.toString(entry.windowingMode));
+                }
                 out.endTag(null, "display");
             }
 
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
index a1639c2..74a8a35 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -76,7 +76,8 @@
     /**
      * Positions the task stack at the given position in the task stack container.
      */
-    public void positionChildAt(StackWindowController child, int position) {
+    public void positionChildAt(StackWindowController child, int position,
+            boolean includingParents) {
         synchronized (mWindowMap) {
             if (DEBUG_STACK) Slog.i(TAG_WM, "positionTaskStackAt: positioning stack=" + child
                     + " at " + position);
@@ -90,7 +91,7 @@
                         "positionTaskStackAt: could not find stack=" + this);
                 return;
             }
-            mContainer.positionStackAt(position, child.mContainer);
+            mContainer.positionStackAt(position, child.mContainer, includingParents);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index cd8d677..e817d1a 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -651,7 +651,7 @@
         if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps)
                 && appTransition != TRANSIT_NONE &&
                 !AppTransition.isKeyguardGoingAwayTransit(appTransition)) {
-            if (mService.mAmInternal.isRecentsComponentHomeActivity(mService.mCurrentUserId)) {
+            if (mService.mAtmInternal.isRecentsComponentHomeActivity(mService.mCurrentUserId)) {
                 // When the home activity is the recents component and we are already minimized,
                 // then there is nothing to do here since home is already visible
             } else {
diff --git a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
index 70478fe..20a1333 100644
--- a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
+++ b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
@@ -17,6 +17,8 @@
 package com.android.server.wm;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.UriGrantsManager;
 import android.content.ClipData;
 import android.net.Uri;
 import android.os.Binder;
@@ -24,6 +26,8 @@
 import android.os.RemoteException;
 
 import com.android.internal.view.IDragAndDropPermissions;
+import com.android.server.LocalServices;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import java.util.ArrayList;
 
@@ -61,7 +65,7 @@
         mActivityToken = activityToken;
 
         // Will throw if Activity is not found.
-        IBinder permissionOwner = ActivityManager.getService().
+        IBinder permissionOwner = ActivityTaskManager.getService().
                 getUriPermissionOwnerForActivity(mActivityToken);
 
         doTake(permissionOwner);
@@ -71,7 +75,7 @@
         long origId = Binder.clearCallingIdentity();
         try {
             for (int i = 0; i < mUris.size(); i++) {
-                ActivityManager.getService().grantUriPermissionFromOwner(
+                UriGrantsManager.getService().grantUriPermissionFromOwner(
                         permissionOwner, mSourceUid, mTargetPackage, mUris.get(i), mMode,
                         mSourceUserId, mTargetUserId);
             }
@@ -85,7 +89,8 @@
         if (mActivityToken != null || mPermissionOwnerToken != null) {
             return;
         }
-        mPermissionOwnerToken = ActivityManager.getService().newUriPermissionOwner("drop");
+        mPermissionOwnerToken = LocalServices.getService(UriGrantsManagerInternal.class)
+                .newUriPermissionOwner("drop");
         mTransientToken = transientToken;
         mTransientToken.linkToDeath(this, 0);
 
@@ -101,7 +106,7 @@
         IBinder permissionOwner = null;
         if (mActivityToken != null) {
             try {
-                permissionOwner = ActivityManager.getService().
+                permissionOwner = ActivityTaskManager.getService().
                         getUriPermissionOwnerForActivity(mActivityToken);
             } catch (Exception e) {
                 // Activity is destroyed, permissions already revoked.
@@ -116,9 +121,9 @@
             mTransientToken = null;
         }
 
+        UriGrantsManagerInternal ugm = LocalServices.getService(UriGrantsManagerInternal.class);
         for (int i = 0; i < mUris.size(); ++i) {
-            ActivityManager.getService().revokeUriPermissionFromOwner(
-                    permissionOwner, mUris.get(i), mMode, mSourceUserId);
+            ugm.revokeUriPermissionFromOwner(permissionOwner, mUris.get(i), mMode, mSourceUserId);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index fc370d9..f42e979 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -151,6 +151,7 @@
                     mDragState.mUid = callerUid;
                     mDragState.mOriginalAlpha = alpha;
                     mDragState.mToken = dragToken;
+                    mDragState.mDisplayContent = displayContent;
 
                     final Display display = displayContent.getDisplay();
                     if (!mCallback.get().registerInputChannel(
@@ -160,7 +161,6 @@
                         return null;
                     }
 
-                    mDragState.mDisplayContent = displayContent;
                     mDragState.mData = data;
                     mDragState.broadcastDragStartedLocked(touchX, touchY);
                     mDragState.overridePointerIconLocked(touchSource);
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 1ac9b88..5483602 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -143,7 +143,7 @@
             mDragDropController.sendHandlerMessage(
                     MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT, mInputInterceptor);
             mInputInterceptor = null;
-            mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+            mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
         }
 
         // Send drag end broadcast if drag start has been sent.
@@ -255,7 +255,7 @@
             if (DEBUG_ORIENTATION) {
                 Slog.d(TAG_WM, "Pausing rotation during drag");
             }
-            mService.pauseRotationLocked();
+            mDisplayContent.pauseRotationLocked();
         }
 
         void tearDown() {
@@ -274,7 +274,7 @@
             if (DEBUG_ORIENTATION) {
                 Slog.d(TAG_WM, "Resuming rotation after drag");
             }
-            mService.resumeRotationLocked();
+            mDisplayContent.resumeRotationLocked();
         }
     }
 
@@ -296,7 +296,7 @@
             Slog.e(TAG_WM, "Duplicate register of drag input channel");
         } else {
             mInputInterceptor = new InputInterceptor(display);
-            mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+            mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java b/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java
new file mode 100644
index 0000000..f5e6e72
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java
@@ -0,0 +1,111 @@
+/*
+ * 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.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import android.graphics.Matrix;
+import android.view.DisplayInfo;
+import android.view.Surface.Rotation;
+
+import com.android.server.wm.utils.CoordinateTransforms;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Helper class for forced seamless rotation.
+ *
+ * Works by transforming the window token back into the old display rotation.
+ *
+ * Uses deferTransactionUntil instead of latching on the buffer size to allow for seamless 180
+ * degree rotations.
+ * TODO(b/111504081): Consolidate seamless rotation logic.
+ */
+public class ForcedSeamlessRotator {
+
+    private final Matrix mTransform = new Matrix();
+    private final float[] mFloat9 = new float[9];
+    private final int mOldRotation;
+    private final int mNewRotation;
+
+    public ForcedSeamlessRotator(int oldRotation, int newRotation, DisplayInfo info) {
+        mOldRotation = oldRotation;
+        mNewRotation = newRotation;
+
+        final boolean flipped = info.rotation == ROTATION_90 || info.rotation == ROTATION_270;
+        final int h = flipped ? info.logicalWidth : info.logicalHeight;
+        final int w = flipped ? info.logicalHeight : info.logicalWidth;
+
+        final Matrix tmp = new Matrix();
+        CoordinateTransforms.transformLogicalToPhysicalCoordinates(oldRotation, w, h, mTransform);
+        CoordinateTransforms.transformPhysicalToLogicalCoordinates(newRotation, w, h, tmp);
+        mTransform.postConcat(tmp);
+    }
+
+    /**
+     * Applies a transform to the window token's surface that undoes the effect of the global
+     * display rotation.
+     */
+    public void unrotate(WindowToken token) {
+        token.getPendingTransaction().setMatrix(token.getSurfaceControl(), mTransform, mFloat9);
+    }
+
+    /**
+     * Returns the rotation of the display before it started rotating.
+     *
+     * @return the old rotation of the display
+     */
+    @Rotation
+    public int getOldRotation() {
+        return mOldRotation;
+    }
+
+    /**
+     * Removes the transform to the window token's surface that undoes the effect of the global
+     * display rotation.
+     *
+     * Removing the transform and the result of the WindowState's layout are both tied to the
+     * WindowState's next frame, such that they apply at the same time the client draws the
+     * window in the new orientation.
+     */
+    public void finish(WindowToken token, WindowState win) {
+        mTransform.reset();
+        token.getPendingTransaction().setMatrix(token.mSurfaceControl, mTransform, mFloat9);
+        if (win.mWinAnimator.mSurfaceController != null) {
+            token.getPendingTransaction().deferTransactionUntil(token.mSurfaceControl,
+                    win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
+                    win.getFrameNumber());
+            win.getPendingTransaction().deferTransactionUntil(win.mSurfaceControl,
+                    win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
+                    win.getFrameNumber());
+        }
+    }
+
+    public void dump(PrintWriter pw) {
+        pw.print("{old="); pw.print(mOldRotation); pw.print(", new="); pw.print(mNewRotation);
+        pw.print("}");
+    }
+
+    @Override
+    public String toString() {
+        StringWriter sw = new StringWriter();
+        dump(new PrintWriter(sw));
+        return "ForcedSeamlessRotator" + sw.toString();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index d40db8c..af0eac6 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -20,7 +20,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.view.Display;
 import android.view.InputChannel;
 import android.view.WindowManager;
 import com.android.server.input.InputApplicationHandle;
@@ -40,7 +39,7 @@
     final UserHandle mClientUser;
 
     InputConsumerImpl(WindowManagerService service, IBinder token, String name,
-            InputChannel inputChannel, int clientPid, UserHandle clientUser) {
+            InputChannel inputChannel, int clientPid, UserHandle clientUser, int displayId) {
         mService = service;
         mToken = token;
         mName = name;
@@ -63,8 +62,7 @@
         mApplicationHandle.dispatchingTimeoutNanos =
                 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
 
-        mWindowHandle = new InputWindowHandle(mApplicationHandle, null, null,
-                Display.DEFAULT_DISPLAY);
+        mWindowHandle = new InputWindowHandle(mApplicationHandle, null, null, displayId);
         mWindowHandle.name = name;
         mWindowHandle.inputChannel = mServerChannel;
         mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
@@ -128,7 +126,9 @@
     public void binderDied() {
         synchronized (mService.getWindowManagerLock()) {
             // Clean up the input consumer
-            mService.mInputMonitor.destroyInputConsumer(mName);
+            final InputMonitor inputMonitor =
+                    mService.mRoot.getDisplayContent(mWindowHandle.displayId).getInputMonitor();
+            inputMonitor.destroyInputConsumer(mName);
             unlinkFromDeathRecipient();
         }
     }
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
new file mode 100644
index 0000000..b5e2f01
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -0,0 +1,275 @@
+package com.android.server.wm;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.app.ActivityManager;
+import android.os.Debug;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+
+import com.android.server.input.InputApplicationHandle;
+import com.android.server.input.InputManagerService;
+import com.android.server.input.InputWindowHandle;
+
+import java.io.PrintWriter;
+
+final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
+    private final WindowManagerService mService;
+
+    // Set to true when the first input device configuration change notification
+    // is received to indicate that the input devices are ready.
+    private final Object mInputDevicesReadyMonitor = new Object();
+    private boolean mInputDevicesReady;
+
+    // When true, prevents input dispatch from proceeding until set to false again.
+    private boolean mInputDispatchFrozen;
+
+    // The reason the input is currently frozen or null if the input isn't frozen.
+    private String mInputFreezeReason = null;
+
+    // When true, input dispatch proceeds normally.  Otherwise all events are dropped.
+    // Initially false, so that input does not get dispatched until boot is finished at
+    // which point the ActivityManager will enable dispatching.
+    private boolean mInputDispatchEnabled;
+
+    public InputManagerCallback(WindowManagerService service) {
+        mService = service;
+    }
+
+    /**
+     * Notifies the window manager about a broken input channel.
+     *
+     * Called by the InputManager.
+     */
+    @Override
+    public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
+        if (inputWindowHandle == null) {
+            return;
+        }
+
+        synchronized (mService.mWindowMap) {
+            WindowState windowState = (WindowState) inputWindowHandle.windowState;
+            if (windowState != null) {
+                Slog.i(TAG_WM, "WINDOW DIED " + windowState);
+                windowState.removeIfPossible();
+            }
+        }
+    }
+
+    /**
+     * Notifies the window manager about an application that is not responding.
+     * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
+     *
+     * Called by the InputManager.
+     */
+    @Override
+    public long notifyANR(InputApplicationHandle inputApplicationHandle,
+            InputWindowHandle inputWindowHandle, String reason) {
+        AppWindowToken appWindowToken = null;
+        WindowState windowState = null;
+        boolean aboveSystem = false;
+        synchronized (mService.mWindowMap) {
+            if (inputWindowHandle != null) {
+                windowState = (WindowState) inputWindowHandle.windowState;
+                if (windowState != null) {
+                    appWindowToken = windowState.mAppToken;
+                }
+            }
+            if (appWindowToken == null && inputApplicationHandle != null) {
+                appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;
+            }
+
+            if (windowState != null) {
+                Slog.i(TAG_WM, "Input event dispatching timed out "
+                        + "sending to " + windowState.mAttrs.getTitle()
+                        + ".  Reason: " + reason);
+                // Figure out whether this window is layered above system windows.
+                // We need to do this here to help the activity manager know how to
+                // layer its ANR dialog.
+                int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(
+                        TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);
+                aboveSystem = windowState.mBaseLayer > systemAlertLayer;
+            } else if (appWindowToken != null) {
+                Slog.i(TAG_WM, "Input event dispatching timed out "
+                        + "sending to application " + appWindowToken.stringName
+                        + ".  Reason: " + reason);
+            } else {
+                Slog.i(TAG_WM, "Input event dispatching timed out "
+                        + ".  Reason: " + reason);
+            }
+
+            mService.saveANRStateLocked(appWindowToken, windowState, reason);
+        }
+
+        // All the calls below need to happen without the WM lock held since they call into AM.
+        mService.mAmInternal.saveANRState(reason);
+
+        if (appWindowToken != null && appWindowToken.appToken != null) {
+            // Notify the activity manager about the timeout and let it decide whether
+            // to abort dispatching or keep waiting.
+            final AppWindowContainerController controller = appWindowToken.getController();
+            final boolean abort = controller != null
+                    && controller.keyDispatchingTimedOut(reason,
+                    (windowState != null) ? windowState.mSession.mPid : -1);
+            if (!abort) {
+                // The activity manager declined to abort dispatching.
+                // Wait a bit longer and timeout again later.
+                return appWindowToken.mInputDispatchingTimeoutNanos;
+            }
+        } else if (windowState != null) {
+            // Notify the activity manager about the timeout and let it decide whether
+            // to abort dispatching or keep waiting.
+            long timeout = mService.mAtmInternal.inputDispatchingTimedOut(
+                    windowState.mSession.mPid, aboveSystem, reason);
+            if (timeout >= 0) {
+                // The activity manager declined to abort dispatching.
+                // Wait a bit longer and timeout again later.
+                return timeout * 1000000L; // nanoseconds
+            }
+        }
+        return 0; // abort dispatching
+    }
+
+    /** Notifies that the input device configuration has changed. */
+    @Override
+    public void notifyConfigurationChanged() {
+        // TODO(multi-display): Notify proper displays that are associated with this input device.
+        mService.sendNewConfiguration(DEFAULT_DISPLAY);
+
+        synchronized (mInputDevicesReadyMonitor) {
+            if (!mInputDevicesReady) {
+                mInputDevicesReady = true;
+                mInputDevicesReadyMonitor.notifyAll();
+            }
+        }
+    }
+
+    /** Notifies that the lid switch changed state. */
+    @Override
+    public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
+        mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
+    }
+
+    /** Notifies that the camera lens cover state has changed. */
+    @Override
+    public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
+        mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
+    }
+
+    /**
+     * Provides an opportunity for the window manager policy to intercept early key
+     * processing as soon as the key has been read from the device.
+     */
+    @Override
+    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
+        return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
+    }
+
+    /**
+     * Provides an opportunity for the window manager policy to intercept early motion event
+     * processing when the device is in a non-interactive state since these events are normally
+     * dropped.
+     */
+    @Override
+    public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
+        return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
+                whenNanos, policyFlags);
+    }
+
+    /**
+     * Provides an opportunity for the window manager policy to process a key before
+     * ordinary dispatch.
+     */
+    @Override
+    public long interceptKeyBeforeDispatching(
+            InputWindowHandle focus, KeyEvent event, int policyFlags) {
+        WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
+        return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
+    }
+
+    /**
+     * Provides an opportunity for the window manager policy to process a key that
+     * the application did not handle.
+     */
+    @Override
+    public KeyEvent dispatchUnhandledKey(
+            InputWindowHandle focus, KeyEvent event, int policyFlags) {
+        WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
+        return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
+    }
+
+    /** Callback to get pointer layer. */
+    @Override
+    public int getPointerLayer() {
+        return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER)
+                * WindowManagerService.TYPE_LAYER_MULTIPLIER
+                + WindowManagerService.TYPE_LAYER_OFFSET;
+    }
+
+    /** Waits until the built-in input devices have been configured. */
+    public boolean waitForInputDevicesReady(long timeoutMillis) {
+        synchronized (mInputDevicesReadyMonitor) {
+            if (!mInputDevicesReady) {
+                try {
+                    mInputDevicesReadyMonitor.wait(timeoutMillis);
+                } catch (InterruptedException ex) {
+                }
+            }
+            return mInputDevicesReady;
+        }
+    }
+
+    public void freezeInputDispatchingLw() {
+        if (!mInputDispatchFrozen) {
+            if (DEBUG_INPUT) {
+                Slog.v(TAG_WM, "Freezing input dispatching");
+            }
+
+            mInputDispatchFrozen = true;
+
+            if (DEBUG_INPUT) {
+                mInputFreezeReason = Debug.getCallers(6);
+            }
+            updateInputDispatchModeLw();
+        }
+    }
+
+    public void thawInputDispatchingLw() {
+        if (mInputDispatchFrozen) {
+            if (DEBUG_INPUT) {
+                Slog.v(TAG_WM, "Thawing input dispatching");
+            }
+
+            mInputDispatchFrozen = false;
+            mInputFreezeReason = null;
+            updateInputDispatchModeLw();
+        }
+    }
+
+    public void setEventDispatchingLw(boolean enabled) {
+        if (mInputDispatchEnabled != enabled) {
+            if (DEBUG_INPUT) {
+                Slog.v(TAG_WM, "Setting event dispatching to " + enabled);
+            }
+
+            mInputDispatchEnabled = enabled;
+            updateInputDispatchModeLw();
+        }
+    }
+
+    private void updateInputDispatchModeLw() {
+        mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+        if (mInputFreezeReason != null) {
+            pw.println(prefix + "mInputFreezeReason=" + mInputFreezeReason);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 281e0a8..823a0de 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
@@ -24,7 +23,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
@@ -33,24 +31,19 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
-import android.app.ActivityManager;
 import android.graphics.Rect;
-import android.os.Debug;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Process;
-import android.os.RemoteException;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
 import android.view.InputChannel;
 import android.view.InputEventReceiver;
-import android.view.KeyEvent;
-import android.view.WindowManager;
 
 import com.android.server.input.InputApplicationHandle;
-import com.android.server.input.InputManagerService;
 import com.android.server.input.InputWindowHandle;
 import com.android.server.policy.WindowManagerPolicy;
 
@@ -59,23 +52,12 @@
 import java.util.Set;
 import java.util.function.Consumer;
 
-final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
+final class InputMonitor {
     private final WindowManagerService mService;
 
     // Current window with input focus for keys and other non-touch events.  May be null.
     private WindowState mInputFocus;
 
-    // When true, prevents input dispatch from proceeding until set to false again.
-    private boolean mInputDispatchFrozen;
-
-    // The reason the input is currently frozen or null if the input isn't frozen.
-    private String mInputFreezeReason = null;
-
-    // When true, input dispatch proceeds normally.  Otherwise all events are dropped.
-    // Initially false, so that input does not get dispatched until boot is finished at
-    // which point the ActivityManager will enable dispatching.
-    private boolean mInputDispatchEnabled;
-
     // When true, need to call updateInputWindowsLw().
     private boolean mUpdateInputWindowsNeeded = true;
 
@@ -84,19 +66,12 @@
     private int mInputWindowHandleCount;
     private InputWindowHandle mFocusedInputWindowHandle;
 
-    private boolean mAddInputConsumerHandle;
-    private boolean mAddPipInputConsumerHandle;
-    private boolean mAddWallpaperInputConsumerHandle;
-    private boolean mAddRecentsAnimationInputConsumerHandle;
     private boolean mDisableWallpaperTouchEvents;
     private final Rect mTmpRect = new Rect();
     private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer =
             new UpdateInputForAllWindowsConsumer();
 
-    // Set to true when the first input device configuration change notification
-    // is received to indicate that the input devices are ready.
-    private final Object mInputDevicesReadyMonitor = new Object();
-    private boolean mInputDevicesReady;
+    private int mDisplayId;
 
     /**
      * The set of input consumer added to the window manager by name, which consumes input events
@@ -112,8 +87,9 @@
         EventReceiverInputConsumer(WindowManagerService service, InputMonitor monitor,
                                    Looper looper, String name,
                                    InputEventReceiver.Factory inputEventReceiverFactory,
-                                   int clientPid, UserHandle clientUser) {
-            super(service, null /* token */, name, null /* inputChannel */, clientPid, clientUser);
+                                   int clientPid, UserHandle clientUser, int displayId) {
+            super(service, null /* token */, name, null /* inputChannel */, clientPid, clientUser,
+                    displayId);
             mInputMonitor = monitor;
             mInputEventReceiver = inputEventReceiverFactory.createInputEventReceiver(
                     mClientChannel, looper);
@@ -129,8 +105,9 @@
         }
     }
 
-    public InputMonitor(WindowManagerService service) {
+    public InputMonitor(WindowManagerService service, int displayId) {
         mService = service;
+        mDisplayId = displayId;
     }
 
     private void addInputConsumer(String name, InputConsumerImpl consumer) {
@@ -155,9 +132,8 @@
         return false;
     }
 
-    InputConsumerImpl getInputConsumer(String name, int displayId) {
-        // TODO(multi-display): Allow input consumers on non-default displays?
-        return (displayId == DEFAULT_DISPLAY) ? mInputConsumers.get(name) : null;
+    InputConsumerImpl getInputConsumer(String name) {
+        return mInputConsumers.get(name);
     }
 
     void layoutInputConsumers(int dw, int dh) {
@@ -169,12 +145,12 @@
     WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name,
             InputEventReceiver.Factory inputEventReceiverFactory) {
         if (mInputConsumers.containsKey(name)) {
-            throw new IllegalStateException("Existing input consumer found with name: " + name);
+            throw new IllegalStateException("Existing input consumer found with name: " + name
+                    + ", display: " + mDisplayId);
         }
-
         final EventReceiverInputConsumer consumer = new EventReceiverInputConsumer(mService,
                 this, looper, name, inputEventReceiverFactory, Process.myPid(),
-                UserHandle.SYSTEM);
+                UserHandle.SYSTEM, mDisplayId);
         addInputConsumer(name, consumer);
         return consumer;
     }
@@ -182,11 +158,12 @@
     void createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid,
             UserHandle clientUser) {
         if (mInputConsumers.containsKey(name)) {
-            throw new IllegalStateException("Existing input consumer found with name: " + name);
+            throw new IllegalStateException("Existing input consumer found with name: " + name
+                    + ", display: " + mDisplayId);
         }
 
         final InputConsumerImpl consumer = new InputConsumerImpl(mService, token, name,
-                inputChannel, clientPid, clientUser);
+                inputChannel, clientPid, clientUser, mDisplayId);
         switch (name) {
             case INPUT_CONSUMER_WALLPAPER:
                 consumer.mWindowHandle.hasWallpaper = true;
@@ -200,100 +177,6 @@
         addInputConsumer(name, consumer);
     }
 
-    /* Notifies the window manager about a broken input channel.
-     *
-     * Called by the InputManager.
-     */
-    @Override
-    public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
-        if (inputWindowHandle == null) {
-            return;
-        }
-
-        synchronized (mService.mWindowMap) {
-            WindowState windowState = (WindowState) inputWindowHandle.windowState;
-            if (windowState != null) {
-                Slog.i(TAG_WM, "WINDOW DIED " + windowState);
-                windowState.removeIfPossible();
-            }
-        }
-    }
-
-    /* Notifies the window manager about an application that is not responding.
-     * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
-     *
-     * Called by the InputManager.
-     */
-    @Override
-    public long notifyANR(InputApplicationHandle inputApplicationHandle,
-            InputWindowHandle inputWindowHandle, String reason) {
-        AppWindowToken appWindowToken = null;
-        WindowState windowState = null;
-        boolean aboveSystem = false;
-        synchronized (mService.mWindowMap) {
-            if (inputWindowHandle != null) {
-                windowState = (WindowState) inputWindowHandle.windowState;
-                if (windowState != null) {
-                    appWindowToken = windowState.mAppToken;
-                }
-            }
-            if (appWindowToken == null && inputApplicationHandle != null) {
-                appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;
-            }
-
-            if (windowState != null) {
-                Slog.i(TAG_WM, "Input event dispatching timed out "
-                        + "sending to " + windowState.mAttrs.getTitle()
-                        + ".  Reason: " + reason);
-                // Figure out whether this window is layered above system windows.
-                // We need to do this here to help the activity manager know how to
-                // layer its ANR dialog.
-                int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(
-                        TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);
-                aboveSystem = windowState.mBaseLayer > systemAlertLayer;
-            } else if (appWindowToken != null) {
-                Slog.i(TAG_WM, "Input event dispatching timed out "
-                        + "sending to application " + appWindowToken.stringName
-                        + ".  Reason: " + reason);
-            } else {
-                Slog.i(TAG_WM, "Input event dispatching timed out "
-                        + ".  Reason: " + reason);
-            }
-
-            mService.saveANRStateLocked(appWindowToken, windowState, reason);
-        }
-
-        // All the calls below need to happen without the WM lock held since they call into AM.
-        mService.mAmInternal.saveANRState(reason);
-
-        if (appWindowToken != null && appWindowToken.appToken != null) {
-            // Notify the activity manager about the timeout and let it decide whether
-            // to abort dispatching or keep waiting.
-            final AppWindowContainerController controller = appWindowToken.getController();
-            final boolean abort = controller != null
-                    && controller.keyDispatchingTimedOut(reason,
-                            (windowState != null) ? windowState.mSession.mPid : -1);
-            if (!abort) {
-                // The activity manager declined to abort dispatching.
-                // Wait a bit longer and timeout again later.
-                return appWindowToken.mInputDispatchingTimeoutNanos;
-            }
-        } else if (windowState != null) {
-            try {
-                // Notify the activity manager about the timeout and let it decide whether
-                // to abort dispatching or keep waiting.
-                long timeout = ActivityManager.getService().inputDispatchingTimedOut(
-                        windowState.mSession.mPid, aboveSystem, reason);
-                if (timeout >= 0) {
-                    // The activity manager declined to abort dispatching.
-                    // Wait a bit longer and timeout again later.
-                    return timeout * 1000000L; // nanoseconds
-                }
-            } catch (RemoteException ex) {
-            }
-        }
-        return 0; // abort dispatching
-    }
 
     private void addInputWindowHandle(final InputWindowHandle windowHandle) {
         if (mInputWindowHandles == null) {
@@ -324,8 +207,9 @@
         inputWindowHandle.ownerPid = child.mSession.mPid;
         inputWindowHandle.ownerUid = child.mSession.mUid;
         inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
+        inputWindowHandle.displayId = child.getDisplayId();
 
-        final Rect frame = child.mFrame;
+        final Rect frame = child.getFrameLw();
         inputWindowHandle.frameLeft = frame.left;
         inputWindowHandle.frameTop = frame.top;
         inputWindowHandle.frameRight = frame.right;
@@ -413,87 +297,6 @@
         if (false) Slog.d(TAG_WM, "<<<<<<< EXITED updateInputWindowsLw");
     }
 
-    /* Notifies that the input device configuration has changed. */
-    @Override
-    public void notifyConfigurationChanged() {
-        // TODO(multi-display): Notify proper displays that are associated with this input device.
-        mService.sendNewConfiguration(DEFAULT_DISPLAY);
-
-        synchronized (mInputDevicesReadyMonitor) {
-            if (!mInputDevicesReady) {
-                mInputDevicesReady = true;
-                mInputDevicesReadyMonitor.notifyAll();
-            }
-        }
-    }
-
-    /* Waits until the built-in input devices have been configured. */
-    public boolean waitForInputDevicesReady(long timeoutMillis) {
-        synchronized (mInputDevicesReadyMonitor) {
-            if (!mInputDevicesReady) {
-                try {
-                    mInputDevicesReadyMonitor.wait(timeoutMillis);
-                } catch (InterruptedException ex) {
-                }
-            }
-            return mInputDevicesReady;
-        }
-    }
-
-    /* Notifies that the lid switch changed state. */
-    @Override
-    public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
-        mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
-    }
-
-    /* Notifies that the camera lens cover state has changed. */
-    @Override
-    public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
-        mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
-    }
-
-    /* Provides an opportunity for the window manager policy to intercept early key
-     * processing as soon as the key has been read from the device. */
-    @Override
-    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
-        return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
-    }
-
-    /* Provides an opportunity for the window manager policy to intercept early motion event
-     * processing when the device is in a non-interactive state since these events are normally
-     * dropped. */
-    @Override
-    public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
-        return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
-                whenNanos, policyFlags);
-    }
-
-    /* Provides an opportunity for the window manager policy to process a key before
-     * ordinary dispatch. */
-    @Override
-    public long interceptKeyBeforeDispatching(
-            InputWindowHandle focus, KeyEvent event, int policyFlags) {
-        WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
-        return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
-    }
-
-    /* Provides an opportunity for the window manager policy to process a key that
-     * the application did not handle. */
-    @Override
-    public KeyEvent dispatchUnhandledKey(
-            InputWindowHandle focus, KeyEvent event, int policyFlags) {
-        WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
-        return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
-    }
-
-    /* Callback to get pointer layer. */
-    @Override
-    public int getPointerLayer() {
-        return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER)
-                * WindowManagerService.TYPE_LAYER_MULTIPLIER
-                + WindowManagerService.TYPE_LAYER_OFFSET;
-    }
-
     /* Called when the current input focus changes.
      * Layer assignment is assumed to be complete by the time this is called.
      */
@@ -554,52 +357,7 @@
         }
     }
 
-    public void freezeInputDispatchingLw() {
-        if (!mInputDispatchFrozen) {
-            if (DEBUG_INPUT) {
-                Slog.v(TAG_WM, "Freezing input dispatching");
-            }
-
-            mInputDispatchFrozen = true;
-
-            if (DEBUG_INPUT || true) {
-                mInputFreezeReason = Debug.getCallers(6);
-            }
-            updateInputDispatchModeLw();
-        }
-    }
-
-    public void thawInputDispatchingLw() {
-        if (mInputDispatchFrozen) {
-            if (DEBUG_INPUT) {
-                Slog.v(TAG_WM, "Thawing input dispatching");
-            }
-
-            mInputDispatchFrozen = false;
-            mInputFreezeReason = null;
-            updateInputDispatchModeLw();
-        }
-    }
-
-    public void setEventDispatchingLw(boolean enabled) {
-        if (mInputDispatchEnabled != enabled) {
-            if (DEBUG_INPUT) {
-                Slog.v(TAG_WM, "Setting event dispatching to " + enabled);
-            }
-
-            mInputDispatchEnabled = enabled;
-            updateInputDispatchModeLw();
-        }
-    }
-
-    private void updateInputDispatchModeLw() {
-        mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
-    }
-
     void dump(PrintWriter pw, String prefix) {
-        if (mInputFreezeReason != null) {
-            pw.println(prefix + "mInputFreezeReason=" + mInputFreezeReason);
-        }
         final Set<String> inputConsumerKeys = mInputConsumers.keySet();
         if (!inputConsumerKeys.isEmpty()) {
             pw.println(prefix + "InputConsumers:");
@@ -609,42 +367,59 @@
         }
     }
 
-    private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
+    void onRemoved() {
+        // If DisplayContent removed, we need find a way to remove window handles of this display
+        // from InputDispatcher, so pass an empty InputWindowHandles to remove them.
+        mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
+                mDisplayId);
+    }
 
+    private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
         InputConsumerImpl navInputConsumer;
         InputConsumerImpl pipInputConsumer;
         InputConsumerImpl wallpaperInputConsumer;
         InputConsumerImpl recentsAnimationInputConsumer;
+
+        private boolean mAddInputConsumerHandle;
+        private boolean mAddPipInputConsumerHandle;
+        private boolean mAddWallpaperInputConsumerHandle;
+        private boolean mAddRecentsAnimationInputConsumerHandle;
+
         boolean inDrag;
         WallpaperController wallpaperController;
 
         private void updateInputWindows(boolean inDrag) {
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
 
-            // TODO: multi-display
-            navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION, DEFAULT_DISPLAY);
-            pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP, DEFAULT_DISPLAY);
-            wallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER, DEFAULT_DISPLAY);
-            recentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION,
-                    DEFAULT_DISPLAY);
+            navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION);
+            pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP);
+            wallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER);
+            recentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+
             mAddInputConsumerHandle = navInputConsumer != null;
             mAddPipInputConsumerHandle = pipInputConsumer != null;
             mAddWallpaperInputConsumerHandle = wallpaperInputConsumer != null;
             mAddRecentsAnimationInputConsumerHandle = recentsAnimationInputConsumer != null;
+
             mTmpRect.setEmpty();
             mDisableWallpaperTouchEvents = false;
             this.inDrag = inDrag;
             wallpaperController = mService.mRoot.mWallpaperController;
 
-            mService.mRoot.forAllWindows(this, true /* traverseTopToBottom */);
+            mService.mRoot.getDisplayContent(mDisplayId).forAllWindows(this,
+                    true /* traverseTopToBottom */);
             if (mAddWallpaperInputConsumerHandle) {
                 // No visible wallpaper found, add the wallpaper input consumer at the end.
                 addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
             }
 
             // Send windows to native code.
-            mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle);
+            mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
+                    mDisplayId);
 
             clearInputWindowHandlesLw();
+
+            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         }
 
         @Override
@@ -660,7 +435,8 @@
             final int flags = w.mAttrs.flags;
             final int privateFlags = w.mAttrs.privateFlags;
             final int type = w.mAttrs.type;
-            final boolean hasFocus = w == mInputFocus;
+            // TODO(b/111361570): multi-display focus, one focus for all display in current.
+            final boolean hasFocus = w == mService.mCurrentFocus;//mInputFocus;
             final boolean isVisible = w.isVisibleLw();
 
             if (mAddRecentsAnimationInputConsumerHandle) {
@@ -669,7 +445,7 @@
                 if (recentsAnimationController != null
                         && recentsAnimationController.hasInputConsumerForApp(w.mAppToken)) {
                     if (recentsAnimationController.updateInputConsumerForApp(
-                            recentsAnimationInputConsumer, hasFocus)) {
+                            recentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
                         addInputWindowHandle(recentsAnimationInputConsumer.mWindowHandle);
                         mAddRecentsAnimationInputConsumerHandle = false;
                     }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 2723801..dea4076 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -16,13 +16,13 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManagerInternal.APP_TRANSITION_RECENTS_ANIM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
@@ -48,11 +48,15 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.inputmethod.InputMethodManagerInternal;
+
+import com.google.android.collect.Sets;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
+import com.android.server.input.InputWindowHandle;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 import com.android.server.wm.utils.InsetUtils;
-import com.google.android.collect.Sets;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
@@ -198,7 +202,9 @@
                     }
 
                     mInputConsumerEnabled = enabled;
-                    mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+                    final InputMonitor inputMonitor =
+                            mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
+                    inputMonitor.updateInputWindowsLw(true /*force*/);
                     mService.scheduleAnimationLocked();
                 }
             } finally {
@@ -309,12 +315,9 @@
     @VisibleForTesting
     AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
         if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "addAnimation(" + task.getName() + ")");
-        // TODO: Refactor this to use the task's animator
-        final SurfaceAnimator anim = new SurfaceAnimator(task, null /* animationFinishedCallback */,
-                mService);
         final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
                 isRecentTaskInvisible);
-        anim.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
+        task.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
         task.commitPendingTransaction();
         mPendingAnimations.add(taskAdapter);
         return taskAdapter;
@@ -358,6 +361,11 @@
                     new RemoteAnimationTarget[appAnimations.size()]);
             mPendingStart = false;
 
+            // Perform layout if it was scheduled before to make sure that we get correct content
+            // insets for the target app window after a rotation
+            final DisplayContent displayContent = mService.mRoot.getDisplayContent(mDisplayId);
+            displayContent.performLayout(false /* initial */, false /* updateInputWindows */);
+
             final Rect minimizedHomeBounds = mTargetAppToken != null
                     && mTargetAppToken.inSplitScreenSecondaryWindowingMode()
                             ? mMinimizedHomeBounds
@@ -366,8 +374,7 @@
                     && mTargetAppToken.findMainWindow() != null
                             ? mTargetAppToken.findMainWindow().mContentInsets
                             : null;
-            mRunner.onAnimationStart(mController, appTargets, contentInsets,
-                    minimizedHomeBounds);
+            mRunner.onAnimationStart(mController, appTargets, contentInsets, minimizedHomeBounds);
             if (DEBUG_RECENTS_ANIMATIONS) {
                 Slog.d(TAG, "startAnimation(): Notify animation start:");
                 for (int i = 0; i < mPendingAnimations.size(); i++) {
@@ -435,8 +442,10 @@
         mCanceled = true;
 
         // Clear associated input consumers
-        mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
-        mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+        final InputMonitor inputMonitor =
+                mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
+        inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+        inputMonitor.updateInputWindowsLw(true /*force*/);
 
         // We have deferred all notifications to the target app as a part of the recents animation,
         // so if we are actually transitioning there, notify again here
@@ -494,7 +503,7 @@
         return mInputConsumerEnabled && isAnimatingApp(appToken);
     }
 
-    boolean updateInputConsumerForApp(InputConsumerImpl recentsAnimationInputConsumer,
+    boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle,
             boolean hasFocus) {
         // Update the input consumer touchable region to match the target app main window
         final WindowState targetAppMainWindow = mTargetAppToken != null
@@ -502,8 +511,8 @@
                 : null;
         if (targetAppMainWindow != null) {
             targetAppMainWindow.getBounds(mTmpRect);
-            recentsAnimationInputConsumer.mWindowHandle.hasFocus = hasFocus;
-            recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
+            inputWindowHandle.hasFocus = hasFocus;
+            inputWindowHandle.touchableRegion.set(mTmpRect);
             return true;
         }
         return false;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index a709c55..a6bda37 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -134,6 +134,9 @@
     // transaction from the global transaction.
     private final SurfaceControl.Transaction mDisplayTransaction = new SurfaceControl.Transaction();
 
+    private final Consumer<DisplayContent> mDisplayContentConfigChangesConsumer =
+            mService.mPolicy::onConfigurationChanged;
+
     private final Consumer<WindowState> mCloseSystemDialogsConsumer = w -> {
         if (w.mHasSurface) {
             try {
@@ -221,16 +224,11 @@
 
         if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
 
-        final DisplayInfo displayInfo = dc.getDisplayInfo();
-        final Rect rect = new Rect();
-        mService.mDisplaySettings.getOverscanLocked(displayInfo.name, displayInfo.uniqueId, rect);
-        displayInfo.overscanLeft = rect.left;
-        displayInfo.overscanTop = rect.top;
-        displayInfo.overscanRight = rect.right;
-        displayInfo.overscanBottom = rect.bottom;
+        mService.mDisplaySettings.applySettingsToDisplayLocked(dc);
+
         if (mService.mDisplayManagerInternal != null) {
             mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
-                    displayId, displayInfo);
+                    displayId, dc.getDisplayInfo());
             dc.configureDisplayPolicy();
 
             // Tap Listeners are supported for:
@@ -379,7 +377,7 @@
         prepareFreezingTaskBounds();
         super.onConfigurationChanged(newParentConfig);
 
-        mService.mPolicy.onConfigurationChanged();
+        forAllDisplays(mDisplayContentConfigChangesConsumer);
     }
 
     /**
@@ -724,7 +722,9 @@
         }
 
         // Finally update all input windows now that the window changes have stabilized.
-        mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+        forAllDisplays(dc -> {
+            dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
+        });
 
         mService.setHoldScreenLocked(mHoldScreen);
         if (!mService.mDisplayFrozen) {
@@ -746,22 +746,7 @@
 
         if (mUpdateRotation) {
             if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
-            // TODO(multi-display): Update rotation for different displays separately.
-            final int displayId = defaultDisplay.getDisplayId();
-            if (defaultDisplay.updateRotationUnchecked()) {
-                mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
-            } else {
-                mUpdateRotation = false;
-            }
-            // Update rotation of VR virtual display separately. Currently this is the only kind of
-            // secondary display that can be rotated because of the single-display limitations in
-            // PhoneWindowManager.
-            final DisplayContent vrDisplay = mService.mVr2dDisplayId != INVALID_DISPLAY
-                    ? getDisplayContent(mService.mVr2dDisplayId) : null;
-            if (vrDisplay != null && vrDisplay.updateRotationUnchecked()) {
-                mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mService.mVr2dDisplayId)
-                        .sendToTarget();
-            }
+            mUpdateRotation = updateRotationUnchecked();
         }
 
         if (mService.mWaitingForDrawnCallback != null ||
@@ -799,7 +784,9 @@
         }
 
         if (updateInputWindowsNeeded) {
-            mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+            forAllDisplays(dc -> {
+                dc.getInputMonitor().updateInputWindowsLw(false /*force*/);
+            });
         }
         mService.setFocusTaskRegionLocked(null);
         if (touchExcludeRegionUpdateDisplays != null) {
@@ -958,6 +945,19 @@
         return displayHasContent;
     }
 
+    boolean updateRotationUnchecked() {
+        boolean changed = false;
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final DisplayContent displayContent = mChildren.get(i);
+            if (displayContent.updateRotationUnchecked()) {
+                changed = true;
+                mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
+                        .sendToTarget();
+            }
+        }
+        return changed;
+    }
+
     boolean copyAnimToLayoutParams() {
         boolean doRequest = false;
 
@@ -1093,4 +1093,15 @@
     void scheduleAnimation() {
         mService.scheduleAnimationLocked();
     }
+
+    /**
+     * For all display at or below this call the callback.
+     *
+     * @param callback Callback to be called for every display.
+     */
+    void forAllDisplays(Consumer<DisplayContent> callback) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            callback.accept(mChildren.get(i));
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index fa8a5c6..2f189a6 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -268,24 +268,18 @@
                     .setSecure(isSecure)
                     .build();
 
-            // capture a screenshot into the surface we just created
-            // TODO(multidisplay): we should use the proper display
-            final int displayId = SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN;
-            final IBinder displayHandle = SurfaceControl.getBuiltInDisplay(displayId);
-            // This null check below is to guard a race condition where WMS didn't have a chance to
-            // respond to display disconnection before handling rotation , that surfaceflinger may
-            // return a null handle here because it doesn't think that display is valid anymore.
-            if (displayHandle != null) {
-                Surface sur = new Surface();
-                sur.copyFrom(mSurfaceControl);
-                SurfaceControl.screenshot(displayHandle, sur);
+            // Capture a screenshot into the surface we just created.
+            final int displayId = display.getDisplayId();
+            final Surface surface = new Surface();
+            surface.copyFrom(mSurfaceControl);
+            if (mService.mDisplayManagerInternal.screenshot(displayId, surface)) {
                 t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
                 t.setAlpha(mSurfaceControl, 0);
                 t.show(mSurfaceControl);
-                sur.destroy();
             } else {
-                Slog.w(TAG, "Built-in display " + displayId + " is null.");
+                Slog.w(TAG, "Unable to take screenshot of display " + displayId);
             }
+            surface.destroy();
         } catch (OutOfResourcesException e) {
             Slog.w(TAG, "Unable to allocate freeze surface", e);
         }
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 4003d5a..5cf5e0d 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -189,15 +189,6 @@
     }
 
     @Override
-    public int add(IWindow window, int seq, WindowManager.LayoutParams attrs,
-            int viewVisibility, Rect outContentInsets, Rect outStableInsets,
-            InputChannel outInputChannel) {
-        return addToDisplay(window, seq, attrs, viewVisibility, Display.DEFAULT_DISPLAY,
-                new Rect() /* outFrame */, outContentInsets, outStableInsets, null /* outOutsets */,
-                new DisplayCutout.ParcelableWrapper()  /* cutout */, outInputChannel);
-    }
-
-    @Override
     public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
             Rect outStableInsets, Rect outOutsets,
@@ -207,13 +198,6 @@
     }
 
     @Override
-    public int addWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
-            int viewVisibility, Rect outContentInsets, Rect outStableInsets) {
-        return addToDisplayWithoutInputChannel(window, seq, attrs, viewVisibility,
-                Display.DEFAULT_DISPLAY, outContentInsets, outStableInsets);
-    }
-
-    @Override
     public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
@@ -303,7 +287,7 @@
             try {
                 return mService.mPolicy.performHapticFeedbackLw(
                         mService.windowForClientLocked(this, window, true),
-                        effectId, always);
+                        effectId, always, null);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 2bfff26..cb50460 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -26,6 +26,8 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.Nullable;
+import android.hardware.power.V1_0.PowerHint;
+import android.os.PowerManagerInternal;
 import android.util.ArrayMap;
 import android.view.Choreographer;
 import android.view.SurfaceControl;
@@ -57,6 +59,7 @@
     private final AnimationHandler mAnimationHandler;
     private final Transaction mFrameTransaction;
     private final AnimatorFactory mAnimatorFactory;
+    private final PowerManagerInternal mPowerManagerInternal;
     private boolean mApplyScheduled;
 
     @GuardedBy("mLock")
@@ -70,13 +73,15 @@
     @GuardedBy("mLock")
     private boolean mAnimationStartDeferred;
 
-    SurfaceAnimationRunner() {
-        this(null /* callbackProvider */, null /* animatorFactory */, new Transaction());
+    SurfaceAnimationRunner(PowerManagerInternal powerManagerInternal) {
+        this(null /* callbackProvider */, null /* animatorFactory */, new Transaction(),
+                powerManagerInternal);
     }
 
     @VisibleForTesting
     SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
-            AnimatorFactory animatorFactory, Transaction frameTransaction) {
+            AnimatorFactory animatorFactory, Transaction frameTransaction,
+            PowerManagerInternal powerManagerInternal) {
         SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(),
                 0 /* timeout */);
         mFrameTransaction = frameTransaction;
@@ -87,6 +92,7 @@
         mAnimatorFactory = animatorFactory != null
                 ? animatorFactory
                 : SfValueAnimator::new;
+        mPowerManagerInternal = powerManagerInternal;
     }
 
     /**
@@ -231,6 +237,7 @@
         synchronized (mLock) {
             startPendingAnimationsLocked();
         }
+        mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
     }
 
     private void scheduleApplyTransaction() {
diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING
new file mode 100644
index 0000000..e885afa
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TEST_MAPPING
@@ -0,0 +1,42 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsWindowManagerDeviceTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.support.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.wm."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.support.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "CtsWindowManagerDeviceTestCases"
+    },
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.wm."
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f87538a..71f34c9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
+import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
@@ -218,7 +218,7 @@
         }
     }
 
-    /** @see com.android.server.am.ActivityManagerService#positionTaskInStack(int, int, int). */
+    /** @see com.android.server.am.ActivityTaskManagerService#positionTaskInStack(int, int, int). */
     void positionAt(int position) {
         mStack.positionChildAt(position, this, false /* includingParents */);
     }
@@ -298,7 +298,7 @@
 
     /**
      * Sets the bounds used to calculate the insets. See
-     * {@link android.app.IActivityManager#resizeDockedStack} why this is needed.
+     * {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
      */
     void setTempInsetBounds(Rect tempInsetBounds) {
         if (tempInsetBounds != null) {
@@ -310,7 +310,7 @@
 
     /**
      * Gets the bounds used to calculate the insets. See
-     * {@link android.app.IActivityManager#resizeDockedStack} why this is needed.
+     * {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
      */
     void getTempInsetBounds(Rect out) {
         out.set(mTempInsetBounds);
@@ -409,7 +409,7 @@
      * @param out Rect containing the max visible bounds.
      * @return true if the task has some visible app windows; false otherwise.
      */
-    boolean getMaxVisibleBounds(Rect out) {
+    private boolean getMaxVisibleBounds(Rect out) {
         boolean foundTop = false;
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             final AppWindowToken token = mChildren.get(i);
@@ -422,22 +422,11 @@
                 continue;
             }
             if (!foundTop) {
-                out.set(win.mVisibleFrame);
                 foundTop = true;
-                continue;
+                out.setEmpty();
             }
-            if (win.mVisibleFrame.left < out.left) {
-                out.left = win.mVisibleFrame.left;
-            }
-            if (win.mVisibleFrame.top < out.top) {
-                out.top = win.mVisibleFrame.top;
-            }
-            if (win.mVisibleFrame.right > out.right) {
-                out.right = win.mVisibleFrame.right;
-            }
-            if (win.mVisibleFrame.bottom > out.bottom) {
-                out.bottom = win.mVisibleFrame.bottom;
-            }
+
+            win.getMaxVisibleBounds(out);
         }
         return foundTop;
     }
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index c85e1f9..8effc6b 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -16,8 +16,8 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManager.RESIZE_MODE_USER;
-import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
+import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
+import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
@@ -30,6 +30,7 @@
 
 import android.annotation.IntDef;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Looper;
@@ -94,9 +95,9 @@
     static final float MIN_ASPECT = 1.2f;
 
     private final WindowManagerService mService;
-    private final IActivityManager mActivityManager;
+    private final IActivityTaskManager mActivityManager;
     private WindowPositionerEventReceiver mInputEventReceiver;
-    private Display mDisplay;
+    private DisplayContent mDisplayContent;
     private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
     private Rect mTmpRect = new Rect();
     private int mSideMargin;
@@ -208,6 +209,7 @@
                     // Post back to WM to handle clean-ups. We still need the input
                     // event handler for the last finishInputEvent()!
                     mService.mTaskPositioningController.finishTaskPositioning();
+                    mTask.getDisplayContent().getInputMonitor().updateInputWindowsLw(true /*force*/);
                 }
                 handled = true;
             } catch (Exception e) {
@@ -219,14 +221,14 @@
     }
 
     @VisibleForTesting
-    TaskPositioner(WindowManagerService service, IActivityManager activityManager) {
+    TaskPositioner(WindowManagerService service, IActivityTaskManager activityManager) {
         mService = service;
         mActivityManager = activityManager;
     }
 
     /** Use {@link #create(WindowManagerService)} instead **/
     TaskPositioner(WindowManagerService service) {
-        this(service, service.mActivityManager);
+        this(service, service.mActivityTaskManager);
     }
 
     @VisibleForTesting
@@ -249,8 +251,8 @@
             return;
         }
 
-        mDisplay = display;
-        mDisplay.getMetrics(mDisplayMetrics);
+        mDisplayContent = displayContent;
+        display.getMetrics(mDisplayMetrics);
         final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
         mServerChannel = channels[0];
         mClientChannel = channels[1];
@@ -266,7 +268,7 @@
                 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
 
         mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
-                mDisplay.getDisplayId());
+                display.getDisplayId());
         mDragWindowHandle.name = TAG;
         mDragWindowHandle.inputChannel = mServerChannel;
         mDragWindowHandle.layer = mService.getDragLayerLocked();
@@ -291,7 +293,7 @@
         mDragWindowHandle.frameLeft = 0;
         mDragWindowHandle.frameTop = 0;
         final Point p = new Point();
-        mDisplay.getRealSize(p);
+        display.getRealSize(p);
         mDragWindowHandle.frameRight = p.x;
         mDragWindowHandle.frameBottom = p.y;
 
@@ -299,12 +301,12 @@
         if (DEBUG_ORIENTATION) {
             Slog.d(TAG, "Pausing rotation during re-position");
         }
-        mService.pauseRotationLocked();
+        mDisplayContent.pauseRotationLocked();
 
         mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
         mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
-        mDisplay.getRealSize(mMaxVisibleSize);
+        display.getRealSize(mMaxVisibleSize);
 
         mDragEnded = false;
     }
@@ -330,14 +332,14 @@
 
         mDragWindowHandle = null;
         mDragApplicationHandle = null;
-        mDisplay = null;
         mDragEnded = true;
 
         // Resume rotations after a drag.
         if (DEBUG_ORIENTATION) {
             Slog.d(TAG, "Resuming rotation after re-position");
         }
-        mService.resumeRotationLocked();
+        mDisplayContent.resumeRotationLocked();
+        mDisplayContent = null;
     }
 
     void startDrag(WindowState win, boolean resize, boolean preserveOrientation, float startX,
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index a3f4ee8..9cdc6b7 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -21,6 +21,7 @@
 
 import android.annotation.Nullable;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.os.RemoteException;
 import android.os.Handler;
 import android.os.Looper;
@@ -37,8 +38,7 @@
 class TaskPositioningController {
     private final WindowManagerService mService;
     private final InputManagerService mInputManager;
-    private final InputMonitor mInputMonitor;
-    private final IActivityManager mActivityManager;
+    private final IActivityTaskManager mActivityManager;
     private final Handler mHandler;
 
     @GuardedBy("WindowManagerSerivce.mWindowMap")
@@ -53,9 +53,8 @@
     }
 
     TaskPositioningController(WindowManagerService service, InputManagerService inputManager,
-            InputMonitor inputMonitor, IActivityManager activityManager, Looper looper) {
+            IActivityTaskManager activityManager, Looper looper) {
         mService = service;
-        mInputMonitor = inputMonitor;
         mInputManager = inputManager;
         mActivityManager = activityManager;
         mHandler = new Handler(looper);
@@ -128,7 +127,7 @@
         Display display = displayContent.getDisplay();
         mTaskPositioner = TaskPositioner.create(mService);
         mTaskPositioner.register(displayContent);
-        mInputMonitor.updateInputWindowsLw(true /*force*/);
+        displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
 
         // We need to grab the touch focus so that the touch events during the
         // resizing/scrolling are not sent to the app. 'win' is the main window
@@ -144,7 +143,7 @@
             Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
             mTaskPositioner.unregister();
             mTaskPositioner = null;
-            mInputMonitor.updateInputWindowsLw(true /*force*/);
+            displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
             return false;
         }
 
@@ -160,7 +159,6 @@
                 if (mTaskPositioner != null) {
                     mTaskPositioner.unregister();
                     mTaskPositioner = null;
-                    mInputMonitor.updateInputWindowsLw(true /*force*/);
                 }
             }
         });
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 733a248..6c8572a 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -38,7 +38,6 @@
 import android.view.RenderNode;
 import android.view.SurfaceControl;
 import android.view.ThreadedRenderer;
-import android.view.View;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -219,15 +218,32 @@
         return TaskSnapshotSurface.create(mService, token, snapshot);
     }
 
-    private TaskSnapshot snapshotTask(Task task) {
-        final AppWindowToken top = task.getTopChild();
-        if (top == null) {
-            return null;
+    /**
+     * Find the window for a given task to take a snapshot. Top child of the task is usually the one
+     * we're looking for, but during app transitions, trampoline activities can appear in the
+     * children, which should be ignored.
+     */
+    @Nullable private AppWindowToken findAppTokenForSnapshot(Task task) {
+        for (int i = task.getChildCount() - 1; i >= 0; --i) {
+            final AppWindowToken appWindowToken = task.getChildAt(i);
+            if (appWindowToken == null || !appWindowToken.isSurfaceShowing()
+                    || appWindowToken.findMainWindow() == null) {
+                continue;
+            }
+            final boolean hasVisibleChild = appWindowToken.forAllWindows(
+                    // Ensure at least one window for the top app is visible before attempting to
+                    // take a screenshot. Visible here means that the WSA surface is shown and has
+                    // an alpha greater than 0.
+                    ws -> ws.mWinAnimator != null && ws.mWinAnimator.getShown()
+                            && ws.mWinAnimator.mLastAlpha > 0f, true  /* traverseTopToBottom */);
+            if (hasVisibleChild) {
+                return appWindowToken;
+            }
         }
-        final WindowState mainWindow = top.findMainWindow();
-        if (mainWindow == null) {
-            return null;
-        }
+        return null;
+    }
+
+    @Nullable private TaskSnapshot snapshotTask(Task task) {
         if (!mService.mPolicy.isScreenOn()) {
             if (DEBUG_SCREENSHOT) {
                 Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
@@ -235,49 +251,49 @@
             return null;
         }
         if (task.getSurfaceControl() == null) {
-            return null;
-        }
-
-        if (top.hasCommittedReparentToAnimationLeash()) {
             if (DEBUG_SCREENSHOT) {
-                Slog.w(TAG_WM, "Failed to take screenshot. App is animating " + top);
+                Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task);
             }
             return null;
         }
 
-        final boolean hasVisibleChild = top.forAllWindows(
-                // Ensure at least one window for the top app is visible before attempting to take
-                // a screenshot. Visible here means that the WSA surface is shown and has an alpha
-                // greater than 0.
-                ws -> (ws.mAppToken == null || ws.mAppToken.isSurfaceShowing())
-                        && ws.mWinAnimator != null && ws.mWinAnimator.getShown()
-                        && ws.mWinAnimator.mLastAlpha > 0f, true);
-
-        if (!hasVisibleChild) {
+        final AppWindowToken appWindowToken = findAppTokenForSnapshot(task);
+        if (appWindowToken == null) {
             if (DEBUG_SCREENSHOT) {
                 Slog.w(TAG_WM, "Failed to take screenshot. No visible windows for " + task);
             }
             return null;
         }
+        if (appWindowToken.hasCommittedReparentToAnimationLeash()) {
+            if (DEBUG_SCREENSHOT) {
+                Slog.w(TAG_WM, "Failed to take screenshot. App is animating " + appWindowToken);
+            }
+            return null;
+        }
 
         final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
         final float scaleFraction = isLowRamDevice ? REDUCED_SCALE : 1f;
         task.getBounds(mTmpRect);
         mTmpRect.offsetTo(0, 0);
 
+        final WindowState mainWindow = appWindowToken.findMainWindow();
+        if (mainWindow == null) {
+            Slog.w(TAG_WM, "Failed to take screenshot. No main window for " + task);
+            return null;
+        }
         final GraphicBuffer buffer = SurfaceControl.captureLayers(
                 task.getSurfaceControl().getHandle(), mTmpRect, scaleFraction);
-        final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
         if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
             if (DEBUG_SCREENSHOT) {
                 Slog.w(TAG_WM, "Failed to take screenshot for " + task);
             }
             return null;
         }
-        return new TaskSnapshot(buffer, top.getConfiguration().orientation,
+        final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
+        return new TaskSnapshot(buffer, appWindowToken.getConfiguration().orientation,
                 getInsets(mainWindow), isLowRamDevice /* reduced */, scaleFraction /* scale */,
                 true /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task),
-                !top.fillsParent() || isWindowTranslucent);
+                !appWindowToken.fillsParent() || isWindowTranslucent);
     }
 
     private boolean shouldDisableSnapshots() {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index a642e6a..21e807e 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -39,6 +39,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayDeque;
+import java.util.Arrays;
 
 /**
  * Persists {@link TaskSnapshot}s to disk.
@@ -399,8 +400,8 @@
         @VisibleForTesting
         RemoveObsoleteFilesQueueItem(ArraySet<Integer> persistentTaskIds,
                 int[] runningUserIds) {
-            mPersistentTaskIds = persistentTaskIds;
-            mRunningUserIds = runningUserIds;
+            mPersistentTaskIds = new ArraySet<>(persistentTaskIds);
+            mRunningUserIds = Arrays.copyOf(runningUserIds, runningUserIds.length);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 76c9c26..2b84937 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -16,8 +16,8 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -726,18 +726,33 @@
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         final int prevWindowingMode = getWindowingMode();
+        final boolean prevIsAlwaysOnTop = isAlwaysOnTop();
         super.onConfigurationChanged(newParentConfig);
 
         // Only need to update surface size here since the super method will handle updating
         // surface position.
         updateSurfaceSize(getPendingTransaction());
         final int windowingMode = getWindowingMode();
+        final boolean isAlwaysOnTop = isAlwaysOnTop();
 
-        if (mDisplayContent == null || prevWindowingMode == windowingMode) {
+        if (mDisplayContent == null) {
             return;
         }
-        mDisplayContent.onStackWindowingModeChanged(this);
-        updateBoundsForWindowModeChange();
+
+        if (prevWindowingMode != windowingMode) {
+            mDisplayContent.onStackWindowingModeChanged(this);
+            updateBoundsForWindowModeChange();
+        }
+
+        if (prevIsAlwaysOnTop != isAlwaysOnTop) {
+            // positionStackAt(POSITION_TOP, this) must be called even when always on top gets
+            // turned off because we need to make sure that the stack is moved from among always on
+            // top windows to below other always on top windows. Since the position the stack should
+            // be inserted into is calculated properly in
+            // {@link DisplayContent#findPositionForStack()} in both cases, we can just request that
+            // the stack is put at top here.
+            mDisplayContent.positionStackAt(POSITION_TOP, this, false /* includingParents */);
+        }
     }
 
     private void updateSurfaceBounds() {
@@ -1614,7 +1629,7 @@
         }
 
         try {
-            mService.mActivityManager.resizePinnedStack(stackBounds, tempTaskBounds);
+            mService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds);
         } catch (RemoteException e) {
             // I don't believe you.
         }
@@ -1630,9 +1645,14 @@
     }
 
     @Override  // AnimatesBounds
-    public void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
+    public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
         // Hold the lock since this is called from the BoundsAnimator running on the UiThread
         synchronized (mService.mWindowMap) {
+            if (!isAttached()) {
+                // Don't run the animation if the stack is already detached
+                return false;
+            }
+
             mBoundsAnimatingRequested = false;
             mBoundsAnimating = true;
             mCancelCurrentBoundsAnimation = false;
@@ -1647,7 +1667,7 @@
 
         if (inPinnedWindowingMode()) {
             try {
-                mService.mActivityManager.notifyPinnedStackAnimationStarted();
+                mService.mActivityTaskManager.notifyPinnedStackAnimationStarted();
             } catch (RemoteException e) {
                 // I don't believe you...
             }
@@ -1662,6 +1682,7 @@
                 controller.updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate);
             }
         }
+        return true;
     }
 
     @Override  // AnimatesBounds
@@ -1689,9 +1710,9 @@
             }
 
             try {
-                mService.mActivityManager.notifyPinnedStackAnimationEnded();
+                mService.mActivityTaskManager.notifyPinnedStackAnimationEnded();
                 if (moveToFullscreen) {
-                    mService.mActivityManager.moveTasksToFullscreenStack(mStackId,
+                    mService.mActivityTaskManager.moveTasksToFullscreenStack(mStackId,
                             true /* onTop */);
                 }
             } catch (RemoteException e) {
@@ -1703,38 +1724,49 @@
         }
     }
 
+    @Override
+    public boolean isAttached() {
+        synchronized (mService.mWindowMap) {
+            return mDisplayContent != null;
+        }
+    }
+
     /**
      * Called immediately prior to resizing the tasks at the end of the pinned stack animation.
      */
     public void onPipAnimationEndResize() {
-        mBoundsAnimating = false;
-        for (int i = 0; i < mChildren.size(); i++) {
-            final Task t = mChildren.get(i);
-            t.clearPreserveNonFloatingState();
+        synchronized (mService.mWindowMap) {
+            mBoundsAnimating = false;
+            for (int i = 0; i < mChildren.size(); i++) {
+                final Task t = mChildren.get(i);
+                t.clearPreserveNonFloatingState();
+            }
+            mService.requestTraversal();
         }
-        mService.requestTraversal();
     }
 
     @Override
     public boolean shouldDeferStartOnMoveToFullscreen() {
-        // Workaround for the recents animation -- normally we need to wait for the new activity to
-        // show before starting the PiP animation, but because we start and show the home activity
-        // early for the recents animation prior to the PiP animation starting, there is no
-        // subsequent all-drawn signal. In this case, we can skip the pause when the home stack is
-        // already visible and drawn.
-        final TaskStack homeStack = mDisplayContent.getHomeStack();
-        if (homeStack == null) {
-            return true;
+        synchronized (mService.mWindowMap) {
+            // Workaround for the recents animation -- normally we need to wait for the new activity
+            // to show before starting the PiP animation, but because we start and show the home
+            // activity early for the recents animation prior to the PiP animation starting, there
+            // is no subsequent all-drawn signal. In this case, we can skip the pause when the home
+            // stack is already visible and drawn.
+            final TaskStack homeStack = mDisplayContent.getHomeStack();
+            if (homeStack == null) {
+                return true;
+            }
+            final Task homeTask = homeStack.getTopChild();
+            if (homeTask == null) {
+                return true;
+            }
+            final AppWindowToken homeApp = homeTask.getTopVisibleAppToken();
+            if (!homeTask.isVisible() || homeApp == null) {
+                return true;
+            }
+            return !homeApp.allDrawn;
         }
-        final Task homeTask = homeStack.getTopChild();
-        if (homeTask == null) {
-            return true;
-        }
-        final AppWindowToken homeApp = homeTask.getTopVisibleAppToken();
-        if (!homeTask.isVisible() || homeApp == null) {
-            return true;
-        }
-        return !homeApp.allDrawn;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index d83f28c..8b634b1 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -209,6 +209,12 @@
         }
     }
 
+    public boolean isDragResizing() {
+        synchronized (mWindowMap) {
+            return mContainer.isDragResizing();
+        }
+    }
+
     void reportSnapshotChanged(TaskSnapshot snapshot) {
         mHandler.obtainMessage(H.REPORT_SNAPSHOT_CHANGED, snapshot).sendToTarget();
     }
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index c63da77..3d349ce 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -295,7 +295,7 @@
         float defaultWallpaperX = wallpaperWin.isRtl() ? 1f : 0f;
         float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : defaultWallpaperX;
         float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
-        int availw = wallpaperWin.mFrame.right - wallpaperWin.mFrame.left - dw;
+        int availw = wallpaperWin.getFrameLw().right - wallpaperWin.getFrameLw().left - dw;
         int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0;
         if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
             offset += mLastWallpaperDisplayOffsetX;
@@ -310,7 +310,7 @@
 
         float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f;
         float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f;
-        int availh = wallpaperWin.mFrame.bottom - wallpaperWin.mFrame.top - dh;
+        int availh = wallpaperWin.getFrameLw().bottom - wallpaperWin.getFrameLw().top - dh;
         offset = availh > 0 ? -(int)(availh * wpy + .5f) : 0;
         if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
             offset += mLastWallpaperDisplayOffsetY;
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 548e23a..825255e 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -99,13 +99,7 @@
         tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
         t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);
         t.setAlpha(leash, tmp.transformation.getAlpha());
-        if (mStackClipMode == STACK_CLIP_NONE) {
-            t.setWindowCrop(leash, tmp.transformation.getClipRect());
-        } else if (mStackClipMode == STACK_CLIP_AFTER_ANIM) {
-            mTmpRect.set(mStackBounds);
-            // Offset stack bounds to stack position so the final crop is in screen space.
-            mTmpRect.offsetTo(mPosition.x, mPosition.y);
-            t.setFinalCrop(leash, mTmpRect);
+        if (mStackClipMode == STACK_CLIP_NONE || mStackClipMode == STACK_CLIP_AFTER_ANIM) {
             t.setWindowCrop(leash, tmp.transformation.getClipRect());
         } else {
             mTmpRect.set(mStackBounds);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 19c5a3d..fee0fcb 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -246,6 +246,19 @@
                     + " is already a child of container=" + child.getParent().getName()
                     + " can't add to container=" + getName());
         }
+
+        if ((index < 0 && index != POSITION_BOTTOM)
+                || (index > mChildren.size() && index != POSITION_TOP)) {
+            throw new IllegalArgumentException("addChild: invalid position=" + index
+                    + ", children number=" + mChildren.size());
+        }
+
+        if (index == POSITION_TOP) {
+            index = mChildren.size();
+        } else if (index == POSITION_BOTTOM) {
+            index = 0;
+        }
+
         mChildren.add(index, child);
         onChildAdded(child);
 
@@ -736,6 +749,20 @@
     }
 
     /**
+     * Seamlessly rotates the container, by recomputing the location in the new
+     * rotation, and rotating buffers until they are updated for the new rotation.
+     *
+     * @param t the transaction to perform the seamless rotation in
+     * @param oldRotation the rotation we are rotating from
+     * @param newRotation the rotation we are rotating to
+     */
+    void seamlesslyRotate(Transaction t, int oldRotation, int newRotation) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            mChildren.get(i).seamlesslyRotate(t, oldRotation, newRotation);
+        }
+    }
+
+    /**
      * Returns true if this container is opaque and fills all the space made available by its parent
      * container.
      *
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
new file mode 100644
index 0000000..5f41df7
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowFrames.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 com.android.server.wm.WindowFramesProto.CONTAINING_FRAME;
+import static com.android.server.wm.WindowFramesProto.CONTENT_FRAME;
+import static com.android.server.wm.WindowFramesProto.CUTOUT;
+import static com.android.server.wm.WindowFramesProto.DECOR_FRAME;
+import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME;
+import static com.android.server.wm.WindowFramesProto.FRAME;
+import static com.android.server.wm.WindowFramesProto.OUTSET_FRAME;
+import static com.android.server.wm.WindowFramesProto.OVERSCAN_FRAME;
+import static com.android.server.wm.WindowFramesProto.PARENT_FRAME;
+import static com.android.server.wm.WindowFramesProto.VISIBLE_FRAME;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
+import android.view.DisplayCutout;
+
+import com.android.server.wm.utils.WmDisplayCutout;
+
+/**
+ * Container class for all the window frames that affect how windows are laid out.
+ *
+ * TODO(b/111611553): Investigate which frames are still needed and which are duplicates
+ */
+public class WindowFrames {
+
+    /**
+     * In most cases, this is the area of the entire screen.
+     *
+     * TODO(b/111611553): The name is unclear and most likely should be swapped with
+     * {@link #mDisplayFrame}
+     * TODO(b/111611553): In some cases, it also includes top insets, like for IME. Determine
+     * whether this is still necessary to do.
+     */
+    public final Rect mParentFrame = new Rect();
+
+    /**
+     * The entire screen area of the {@link TaskStack} this window is in. Usually equal to the
+     * screen area of the device.
+     *
+     * TODO(b/111611553): The name is unclear and most likely should be swapped with
+     * {@link #mParentFrame}
+    */
+    public final Rect mDisplayFrame = new Rect();
+
+    /**
+     * The region of the display frame that the display type supports displaying content on. This
+     * is mostly a special case for TV where some displays don’t have the entire display usable.
+     * {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} flag can be used to
+     * allow window display contents to extend into the overscan region.
+     */
+    public final Rect mOverscanFrame = new Rect();
+
+    /**
+     * Legacy stuff. Generally equal to the content frame expect when the IME for older apps
+     * displays hint text.
+     */
+    public final Rect mVisibleFrame = new Rect();
+
+    /**
+     * The area not occupied by the status and navigation bars. So, if both status and navigation
+     * bars are visible, the decor frame is equal to the stable frame.
+     */
+    public final Rect mDecorFrame = new Rect();
+
+    /**
+     * Equal to the decor frame if the IME (e.g. keyboard) is not present. Equal to the decor frame
+     * minus the area occupied by the IME if the IME is present.
+     */
+    public final Rect mContentFrame = new Rect();
+
+    /**
+     * The display frame minus the stable insets. This value is always constant regardless of if
+     * the status bar or navigation bar is visible.
+     */
+    public final Rect mStableFrame = new Rect();
+
+    /**
+     * Frame that includes dead area outside of the surface but where we want to pretend that it's
+     * possible to draw.
+     */
+    final public Rect mOutsetFrame = new Rect();
+
+    /**
+     * Similar to {@link #mDisplayFrame}
+     *
+     * TODO: Why is this different than mDisplayFrame
+     */
+    final Rect mContainingFrame = new Rect();
+
+    /**
+     * "Real" frame that the application sees, in display coordinate space.
+     */
+    final Rect mFrame = new Rect();
+
+    /**
+     * The last real frame that was reported to the client.
+     */
+    final Rect mLastFrame = new Rect();
+
+    /**
+     * Whether the parent frame would have been different if there was no display cutout.
+     */
+    private boolean mParentFrameWasClippedByDisplayCutout;
+
+    /**
+     * Part of the display that has been cut away. See {@link DisplayCutout}.
+     */
+    WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
+
+    /**
+     * The last cutout that has been reported to the client.
+     */
+    WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT;
+
+    public WindowFrames() {
+    }
+
+    public WindowFrames(Rect parentFrame, Rect displayFrame, Rect overscanFrame, Rect contentFrame,
+            Rect visibleFrame, Rect decorFrame, Rect stableFrame, Rect outsetFrame) {
+        setFrames(parentFrame, displayFrame, overscanFrame, contentFrame, visibleFrame, decorFrame,
+                stableFrame, outsetFrame);
+    }
+
+    public void setFrames(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
+            Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
+            Rect outsetFrame) {
+        mParentFrame.set(parentFrame);
+        mDisplayFrame.set(displayFrame);
+        mOverscanFrame.set(overscanFrame);
+        mContentFrame.set(contentFrame);
+        mVisibleFrame.set(visibleFrame);
+        mDecorFrame.set(decorFrame);
+        mStableFrame.set(stableFrame);
+        mOutsetFrame.set(outsetFrame);
+    }
+
+    public void setParentFrameWasClippedByDisplayCutout(
+            boolean parentFrameWasClippedByDisplayCutout) {
+        mParentFrameWasClippedByDisplayCutout = parentFrameWasClippedByDisplayCutout;
+    }
+
+    boolean parentFrameWasClippedByDisplayCutout() {
+        return mParentFrameWasClippedByDisplayCutout;
+    }
+
+    public void setDisplayCutout(WmDisplayCutout displayCutout) {
+        mDisplayCutout = displayCutout;
+    }
+
+    /**
+     * @return true if the width or height has changed since last reported to the client.
+     */
+    boolean didFrameSizeChange() {
+        return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height());
+    }
+
+    /**
+     * @return true if the display cutout has changed since last reported to the client.
+     */
+    boolean didDisplayCutoutChange() {
+        return !mLastDisplayCutout.equals(mDisplayCutout);
+    }
+
+    public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        mParentFrame.writeToProto(proto, PARENT_FRAME);
+        mContentFrame.writeToProto(proto, CONTENT_FRAME);
+        mDisplayFrame.writeToProto(proto, DISPLAY_FRAME);
+        mOverscanFrame.writeToProto(proto, OVERSCAN_FRAME);
+        mVisibleFrame.writeToProto(proto, VISIBLE_FRAME);
+        mDecorFrame.writeToProto(proto, DECOR_FRAME);
+        mOutsetFrame.writeToProto(proto, OUTSET_FRAME);
+        mContainingFrame.writeToProto(proto, CONTAINING_FRAME);
+        mFrame.writeToProto(proto, FRAME);
+        mDisplayCutout.getDisplayCutout().writeToProto(proto, CUTOUT);
+        proto.end(token);
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.print("Frames: containing=");
+                mContainingFrame.printShortString(pw);
+                pw.print(" parent="); mParentFrame.printShortString(pw);
+                pw.println();
+        pw.print(prefix); pw.print("    display=");
+                mDisplayFrame.printShortString(pw);
+                pw.print(" overscan="); mOverscanFrame.printShortString(pw);
+                pw.println();
+        pw.print(prefix); pw.print("    content=");
+                mContentFrame.printShortString(pw);
+                pw.print(" visible="); mVisibleFrame.printShortString(pw);
+                pw.println();
+        pw.print(prefix); pw.print("    decor=");
+                mDecorFrame.printShortString(pw);
+                pw.println();
+        pw.print(prefix); pw.print("    outset=");
+                mOutsetFrame.printShortString(pw);
+                pw.println();
+        pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
+                pw.print(" last="); mLastFrame.printShortString(pw);
+                pw.println();
+        pw.print(prefix); pw.print(" cutout=" + mDisplayCutout.getDisplayCutout());
+                pw.print(" last=" + mLastDisplayCutout.getDisplayCutout());
+                pw.println();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 6686b80..90a763d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -368,14 +368,25 @@
      *                         to be synchronized with state in WindowManagerService.
      * @param dismissImeOnBackKeyPressed {@code true} if the software keyboard is shown and the back
      *                                   key is expected to dismiss the software keyboard.
-     * @param targetWindowToken token to identify the target window that the IME is associated with.
-     *                          {@code null} when application, system, or the IME itself decided to
-     *                          change its window visibility before being associated with any target
-     *                          window.
      */
     public abstract void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
-            boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed,
-            @Nullable IBinder targetWindowToken);
+            boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed);
+
+    /**
+     * Notifies WindowManagerService that the current IME window status is being changed.
+     *
+     * <p>Only {@link com.android.server.InputMethodManagerService} is the expected and tested
+     * caller of this method.</p>
+     *
+     * @param imeToken token to track the active input method. Corresponding IME windows can be
+     *                 identified by checking {@link android.view.WindowManager.LayoutParams#token}.
+     *                 Note that there is no guarantee that the corresponding window is already
+     *                 created
+     * @param imeTargetWindowToken token to identify the target window that the IME is associated
+     *                             with
+     */
+    public abstract void updateInputMethodTargetWindow(@NonNull IBinder imeToken,
+            @NonNull IBinder imeTargetWindowToken);
 
     /**
       * Returns true when the hardware keyboard is available.
@@ -399,7 +410,7 @@
     public abstract boolean isDockedDividerResizing();
 
     /**
-     * Requests the window manager to recompute the windows for accessibility.
+     * Requests the window manager to resend the windows for accessibility.
      */
     public abstract void computeWindowsForAccessibility();
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 947c937..3acecba 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -21,7 +21,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.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+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;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
@@ -116,9 +116,11 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityManagerInternal;
+import android.app.ActivityTaskManager;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.app.IAssistDataReceiver;
 import android.app.admin.DevicePolicyCache;
 import android.content.BroadcastReceiver;
@@ -267,6 +269,8 @@
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
         implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
@@ -342,6 +346,8 @@
     static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
     static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
     static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;
+    /** Indicates we are removing the focused window when updating the focus. */
+    static final int UPDATE_FOCUS_REMOVING_FOCUS = 4;
 
     private static final String SYSTEM_SECURE = "ro.secure";
     private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
@@ -427,7 +433,10 @@
     final WindowManagerPolicy mPolicy;
 
     final IActivityManager mActivityManager;
+    // TODO: Probably not needed once activities are fully in WM.
+    final IActivityTaskManager mActivityTaskManager;
     final ActivityManagerInternal mAmInternal;
+    final ActivityTaskManagerInternal mAtmInternal;
 
     final AppOpsManager mAppOps;
     final PackageManagerInternal mPmInternal;
@@ -556,6 +565,8 @@
 
     boolean mForceResizableTasks = false;
     boolean mSupportsPictureInPicture = false;
+    boolean mSupportsFreeformWindowManagement = false;
+    boolean mIsPc = false;
 
     boolean mDisableTransitionAnimation = false;
 
@@ -576,7 +587,6 @@
     }
 
     ArrayList<RotationWatcher> mRotationWatchers = new ArrayList<>();
-    int mDeferredRotationPauseCount;
     final WallpaperVisibilityListeners mWallpaperVisibilityListeners =
             new WallpaperVisibilityListeners();
 
@@ -859,7 +869,7 @@
             }
             if (atoken.mLaunchTaskBehind) {
                 try {
-                    mActivityManager.notifyLaunchTaskBehindComplete(atoken.token);
+                    mActivityTaskManager.notifyLaunchTaskBehindComplete(atoken.token);
                 } catch (RemoteException e) {
                 }
                 atoken.mLaunchTaskBehind = false;
@@ -876,7 +886,7 @@
                     } else {
                         atoken.mEnteringAnimation = false;
                         try {
-                            mActivityManager.notifyEnterAnimationComplete(atoken.token);
+                            mActivityTaskManager.notifyEnterAnimationComplete(atoken.token);
                         } catch (RemoteException e) {
                         }
                     }
@@ -945,7 +955,7 @@
                 com.android.internal.R.bool.config_disableTransitionAnimation);
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
-        mDisplaySettings = new DisplaySettings();
+        mDisplaySettings = new DisplaySettings(this);
         mDisplaySettings.readSettingsLocked();
 
         mPolicy = policy;
@@ -1004,12 +1014,13 @@
         mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
 
         final AnimationHandler animationHandler = new AnimationHandler();
-        animationHandler.setProvider(new SfVsyncFrameCallbackProvider());
         mBoundsAnimationController = new BoundsAnimationController(context, mAppTransition,
                 AnimationThread.getHandler(), animationHandler);
 
         mActivityManager = ActivityManager.getService();
+        mActivityTaskManager = ActivityTaskManager.getService();
         mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+        mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
         mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
         AppOpsManager.OnOpChangedInternalListener opListener =
                 new AppOpsManager.OnOpChangedInternalListener() {
@@ -1060,13 +1071,13 @@
                 PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
         mHoldingScreenWakeLock.setReferenceCounted(false);
 
-        mSurfaceAnimationRunner = new SurfaceAnimationRunner();
+        mSurfaceAnimationRunner = new SurfaceAnimationRunner(mPowerManagerInternal);
 
         mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
 
         mTaskPositioningController = new TaskPositioningController(
-                this, mInputManager, mInputMonitor, mActivityManager, mH.getLooper());
+                this, mInputManager, mActivityTaskManager, mH.getLooper());
         mDragDropController = new DragDropController(this, mH.getLooper());
 
         LocalServices.addService(WindowManagerInternal.class, new LocalService());
@@ -1092,9 +1103,8 @@
         showEmulatorDisplayOverlayIfNeeded();
     }
 
-
-    public InputMonitor getInputMonitor() {
-        return mInputMonitor;
+    public InputManagerCallback getInputManagerCallback() {
+        return mInputManagerCallback;
     }
 
     @Override
@@ -1460,14 +1470,18 @@
             displayFrames.onDisplayInfoUpdated(displayInfo,
                     displayContent.calculateDisplayCutoutForRotation(displayInfo.rotation));
             final Rect taskBounds;
+            final boolean floatingStack;
             if (atoken != null && atoken.getTask() != null) {
                 taskBounds = mTmpRect;
                 atoken.getTask().getBounds(mTmpRect);
+                floatingStack = atoken.getTask().isFloating();
             } else {
                 taskBounds = null;
+                floatingStack = false;
             }
-            if (mPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, outFrame,
-                    outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
+            if (mPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack,
+                    outFrame, outContentInsets, outStableInsets, outOutsets,
+                    outDisplayCutout)) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
             }
 
@@ -1478,7 +1492,7 @@
                 res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
             }
 
-            mInputMonitor.setUpdateInputWindowsNeededLw();
+            displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
 
             boolean focusChanged = false;
             if (win.canReceiveKeys()) {
@@ -1498,9 +1512,10 @@
             win.getParent().assignChildLayers();
 
             if (focusChanged) {
-                mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
+                displayContent.getInputMonitor().setInputFocusLw(mCurrentFocus,
+                        false /*updateInputWindows*/);
             }
-            mInputMonitor.updateInputWindowsLw(false /*force*/);
+            displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
 
             if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client "
                     + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));
@@ -1584,7 +1599,7 @@
         }
         // We use the visible frame, because we want the animation to morph the window from what
         // was visible to the user to the final destination of the new window.
-        Rect frame = replacedWindow.mVisibleFrame;
+        Rect frame = replacedWindow.getVisibleFrameLw();
         // We treat this as if this activity was opening, so we can trigger the app transition
         // animation and piggy-back on existing transition animation infrastructure.
         mOpeningApps.add(atoken);
@@ -1713,7 +1728,7 @@
             }
         }
 
-        mInputMonitor.updateInputWindowsLw(true /*force*/);
+        dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
     }
 
     void setInputMethodWindowLocked(WindowState win) {
@@ -1820,7 +1835,7 @@
                 outDisplayFrame.setEmpty();
                 return;
             }
-            outDisplayFrame.set(win.mDisplayFrame);
+            outDisplayFrame.set(win.getDisplayFrameLw());
         }
     }
 
@@ -1882,6 +1897,14 @@
             }
 
             win.setFrameNumber(frameNumber);
+
+            // TODO(b/111504081): Consolidate seamless rotation logic.
+            if (win.mPendingForcedSeamlessRotate != null && !mWaitingForConfig) {
+                win.mPendingForcedSeamlessRotate.finish(win.mToken, win);
+                win.mFinishForcedSeamlessRotateFrameNumber = win.getFrameNumber();
+                win.mPendingForcedSeamlessRotate = null;
+            }
+
             int attrChanges = 0;
             int flagChanges = 0;
             if (attrs != null) {
@@ -2025,7 +2048,7 @@
                 try {
                     result = createSurfaceControl(outSurface, result, win, winAnimator);
                 } catch (Exception e) {
-                    mInputMonitor.updateInputWindowsLw(true /*force*/);
+                    win.getDisplayContent().getInputMonitor().updateInputWindowsLw(true /*force*/);
 
                     Slog.w(TAG_WM, "Exception thrown when creating surface for client "
                              + client + " (" + win.mAttrs.getTitle() + ")",
@@ -2155,9 +2178,9 @@
             win.mLastRelayoutContentInsets.set(win.mContentInsets);
             outVisibleInsets.set(win.mVisibleInsets);
             outStableInsets.set(win.mStableInsets);
-            outCutout.set(win.mDisplayCutout.getDisplayCutout());
+            outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
             outOutsets.set(win.mOutsets);
-            outBackdropFrame.set(win.getBackdropFrame(win.mFrame));
+            outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
             if (localLOGV) Slog.v(
                 TAG_WM, "Relayout given client " + client.asBinder()
                 + ", requestedWidth=" + requestedWidth
@@ -2171,8 +2194,6 @@
 
             result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
 
-            mInputMonitor.updateInputWindowsLw(true /*force*/);
-
             if (DEBUG_LAYOUT) {
                 Slog.v(TAG_WM, "Relayout complete " + win + ": outFrame=" + outFrame.toShortString());
             }
@@ -2361,7 +2382,7 @@
                     return;
                 }
 
-                mInputMonitor.updateInputWindowsLw(true /*force*/);
+                dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -2462,10 +2483,7 @@
                 dc.setLastOrientation(req);
                 //send a message to Policy indicating orientation change to take
                 //action like disabling/enabling sensors etc.,
-                // TODO(multi-display): Implement policy for secondary displays.
-                if (dc.isDefaultDisplay) {
-                    mPolicy.setCurrentOrientationLw(req);
-                }
+                dc.getDisplayRotation().setCurrentOrientation(req);
                 return dc.updateRotationUnchecked(forceUpdate);
             }
             return false;
@@ -2474,27 +2492,6 @@
         }
     }
 
-    // If this is true we have updated our desired orientation, but not yet
-    // changed the real orientation our applied our screen rotation animation.
-    // For example, because a previous screen rotation was in progress.
-    boolean rotationNeedsUpdateLocked() {
-        // TODO(multi-display): Check for updates on all displays. Need to have per-display policy
-        // to implement WindowManagerPolicy#rotationForOrientationLw() correctly.
-        final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
-        final int lastOrientation = defaultDisplayContent.getLastOrientation();
-        final int oldRotation = defaultDisplayContent.getRotation();
-        final boolean oldAltOrientation = defaultDisplayContent.getAltOrientation();
-
-        final int rotation = mPolicy.rotationForOrientationLw(lastOrientation, oldRotation,
-                true /* defaultDisplay */);
-        boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
-                lastOrientation, rotation);
-        if (oldRotation == rotation && oldAltOrientation == altOrientation) {
-            return false;
-        }
-        return true;
-    }
-
     @Override
     public int[] setNewDisplayOverrideConfiguration(Configuration overrideConfig, int displayId) {
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "setNewDisplayOverrideConfiguration()")) {
@@ -2550,7 +2547,7 @@
             if (changed) {
                 AppWindowToken prev = mFocusedApp;
                 mFocusedApp = newFocus;
-                mInputMonitor.setFocusedAppLw(newFocus);
+                mFocusedApp.getDisplayContent().getInputMonitor().setFocusedAppLw(newFocus);
                 setFocusTaskRegionLocked(prev);
             }
 
@@ -3458,7 +3455,7 @@
             if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM, "******************** ENABLING SCREEN!");
 
             // Enable input dispatch.
-            mInputMonitor.setEventDispatchingLw(mEventDispatchingEnabled);
+            mInputManagerCallback.setEventDispatchingLw(mEventDispatchingEnabled);
         }
 
         try {
@@ -3813,37 +3810,6 @@
         updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
     }
 
-    /**
-     * Temporarily pauses rotation changes until resumed.
-     *
-     * This can be used to prevent rotation changes from occurring while the user is
-     * performing certain operations, such as drag and drop.
-     *
-     * This call nests and must be matched by an equal number of calls to
-     * {@link #resumeRotationLocked}.
-     */
-    void pauseRotationLocked() {
-        mDeferredRotationPauseCount += 1;
-    }
-
-    /**
-     * Resumes normal rotation changes after being paused.
-     */
-    void resumeRotationLocked() {
-        if (mDeferredRotationPauseCount > 0) {
-            mDeferredRotationPauseCount -= 1;
-            if (mDeferredRotationPauseCount == 0) {
-                // TODO(multi-display): Update rotation for different displays separately.
-                final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                final boolean changed = displayContent.updateRotationUnchecked();
-                if (changed) {
-                    mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
-                            .sendToTarget();
-                }
-            }
-        }
-    }
-
     private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
         if(DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked:"
                 + " alwaysSendConfiguration=" + alwaysSendConfiguration
@@ -3854,28 +3820,31 @@
         long origId = Binder.clearCallingIdentity();
 
         try {
-            // TODO(multi-display): Update rotation for different displays separately.
-            final boolean rotationChanged;
-            final int displayId;
             synchronized (mWindowMap) {
-                final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
-                rotationChanged = displayContent.updateRotationUnchecked();
-                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-                if (!rotationChanged || forceRelayout) {
-                    displayContent.setLayoutNeeded();
+                boolean layoutNeeded = false;
+                final int displayCount = mRoot.mChildren.size();
+                for (int i = 0; i < displayCount; ++i) {
+                    final DisplayContent displayContent = mRoot.mChildren.get(i);
+                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
+                    final boolean rotationChanged = displayContent.updateRotationUnchecked();
+                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+
+                    if (!rotationChanged || forceRelayout) {
+                        displayContent.setLayoutNeeded();
+                        layoutNeeded = true;
+                    }
+                    if (rotationChanged || alwaysSendConfiguration) {
+                        mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
+                                .sendToTarget();
+                    }
+                }
+
+                if (layoutNeeded) {
                     Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                             "updateRotation: performSurfacePlacement");
                     mWindowPlacerLocked.performSurfacePlacement();
                     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 }
-                displayId = displayContent.getDisplayId();
-            }
-
-            if (rotationChanged || alwaysSendConfiguration) {
-                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: sendNewConfiguration");
-                sendNewConfiguration(displayId);
-                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -3884,6 +3853,13 @@
     }
 
     @Override
+    public WindowManagerPolicy.DisplayContentInfo getDefaultDisplayContentInfo() {
+        synchronized (mWindowMap) {
+            return getDefaultDisplayContentLocked();
+        }
+    }
+
+    @Override
     public int getDefaultDisplayRotation() {
         synchronized (mWindowMap) {
             return getDefaultDisplayContentLocked().getRotation();
@@ -3897,6 +3873,15 @@
 
     @Override
     public int watchRotation(IRotationWatcher watcher, int displayId) {
+        final DisplayContent displayContent;
+        synchronized (mWindowMap) {
+            displayContent = mRoot.getDisplayContent(displayId);
+        }
+        if (displayContent == null) {
+            throw new IllegalArgumentException("Trying to register rotation event "
+                    + "for invalid display: " + displayId);
+        }
+
         final IBinder watcherBinder = watcher.asBinder();
         IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
             @Override
@@ -3924,7 +3909,7 @@
                 // Client died, no cleanup needed.
             }
 
-            return getDefaultDisplayRotation();
+            return displayContent.getRotation();
         }
     }
 
@@ -4372,7 +4357,7 @@
      */
     void sendNewConfiguration(int displayId) {
         try {
-            final boolean configUpdated = mActivityManager.updateDisplayOverrideConfiguration(
+            final boolean configUpdated = mActivityTaskManager.updateDisplayOverrideConfiguration(
                     null /* values */, displayId);
             if (!configUpdated) {
                 // Something changed (E.g. device rotation), but no configuration update is needed.
@@ -4427,7 +4412,7 @@
     // Input Events and Focus Management
     // -------------------------------------------------------------
 
-    final InputMonitor mInputMonitor = new InputMonitor(this);
+    final InputManagerCallback mInputManagerCallback = new InputManagerCallback(this);
     private boolean mEventDispatchingEnabled;
 
     @Override
@@ -4439,7 +4424,7 @@
         synchronized (mWindowMap) {
             mEventDispatchingEnabled = enabled;
             if (mDisplayEnabled) {
-                mInputMonitor.setEventDispatchingLw(enabled);
+                mInputManagerCallback.setEventDispatchingLw(enabled);
             }
         }
     }
@@ -4464,7 +4449,7 @@
     }
 
     public boolean detectSafeMode() {
-        if (!mInputMonitor.waitForInputDevicesReady(
+        if (!mInputManagerCallback.waitForInputDevicesReady(
                 INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) {
             Slog.w(TAG_WM, "Devices still not ready after waiting "
                    + INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS
@@ -4527,7 +4512,7 @@
         }
 
         try {
-            mActivityManager.updateConfiguration(null);
+            mActivityTaskManager.updateConfiguration(null);
         } catch (RemoteException e) {
         }
 
@@ -4538,7 +4523,7 @@
         }
 
         try {
-            mActivityManager.updateConfiguration(null);
+            mActivityTaskManager.updateConfiguration(null);
         } catch (RemoteException e) {
         }
 
@@ -4709,9 +4694,9 @@
                 } break;
 
                 case WINDOW_FREEZE_TIMEOUT: {
-                    // TODO(multidisplay): Can non-default displays rotate?
+                    final DisplayContent displayContent = (DisplayContent) msg.obj;
                     synchronized (mWindowMap) {
-                        getDefaultDisplayContentLocked().onWindowFreezeTimeout();
+                        displayContent.onWindowFreezeTimeout();
                     }
                     break;
                 }
@@ -4893,7 +4878,7 @@
 
                 case NOTIFY_ACTIVITY_DRAWN:
                     try {
-                        mActivityManager.notifyActivityDrawn((IBinder) msg.obj);
+                        mActivityTaskManager.notifyActivityDrawn((IBinder) msg.obj);
                     } catch (RemoteException e) {
                     }
                     break;
@@ -4980,16 +4965,16 @@
                 }
                 break;
                 case NOTIFY_APP_TRANSITION_STARTING: {
-                    mAmInternal.notifyAppTransitionStarting((SparseIntArray) msg.obj,
+                    mAtmInternal.notifyAppTransitionStarting((SparseIntArray) msg.obj,
                             msg.getWhen());
                 }
                 break;
                 case NOTIFY_APP_TRANSITION_CANCELLED: {
-                    mAmInternal.notifyAppTransitionCancelled();
+                    mAtmInternal.notifyAppTransitionCancelled();
                 }
                 break;
                 case NOTIFY_APP_TRANSITION_FINISHED: {
-                    mAmInternal.notifyAppTransitionFinished();
+                    mAtmInternal.notifyAppTransitionFinished();
                 }
                 break;
                 case WINDOW_HIDE_TIMEOUT: {
@@ -5014,7 +4999,7 @@
                 }
                 break;
                 case NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED: {
-                    mAmInternal.notifyDockedStackMinimizedChanged(msg.arg1 == 1);
+                    mAtmInternal.notifyDockedStackMinimizedChanged(msg.arg1 == 1);
                 }
                 break;
                 case RESTORE_POINTER_ICON: {
@@ -5024,20 +5009,18 @@
                 }
                 break;
                 case SEAMLESS_ROTATION_TIMEOUT: {
-                    // Rotation only supported on primary display.
-                    // TODO(multi-display)
-                    synchronized(mWindowMap) {
-                        final DisplayContent dc = getDefaultDisplayContentLocked();
-                        dc.onSeamlessRotationTimeout();
+                    final DisplayContent displayContent = (DisplayContent) msg.obj;
+                    synchronized (mWindowMap) {
+                        displayContent.onSeamlessRotationTimeout();
                     }
                 }
                 break;
                 case NOTIFY_KEYGUARD_FLAGS_CHANGED: {
-                    mAmInternal.notifyKeyguardFlagsChanged((Runnable) msg.obj);
+                    mAtmInternal.notifyKeyguardFlagsChanged((Runnable) msg.obj);
                 }
                 break;
                 case NOTIFY_KEYGUARD_TRUSTED_CHANGED: {
-                    mAmInternal.notifyKeyguardTrustedChanged();
+                    mAtmInternal.notifyKeyguardTrustedChanged();
                 }
                 break;
                 case SET_HAS_OVERLAY_UI: {
@@ -5068,6 +5051,12 @@
                 Slog.v(TAG_WM, "handleMessage: exit");
             }
         }
+
+        /** Remove the previous messages with the same 'what' and 'obj' then send the new one. */
+        void sendNewMessageDelayed(int what, Object obj, long delayMillis) {
+            removeMessages(what, obj);
+            sendMessageDelayed(obtainMessage(what, obj), delayMillis);
+        }
     }
 
     void destroyPreservedSurfaceLocked() {
@@ -5531,8 +5520,7 @@
                 mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
                 // XXX should probably keep timeout from
                 // when we first froze the display.
-                mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
-                mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT,
+                mH.sendNewMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, w.getDisplayContent(),
                         WINDOW_FREEZE_TIMEOUT_DURATION);
             }
         }
@@ -5657,9 +5645,9 @@
             boolean imWindowChanged = false;
             if (mInputMethodWindow != null) {
                 final WindowState prevTarget = mInputMethodTarget;
+
                 final WindowState newTarget =
                         displayContent.computeImeTarget(true /* updateImeTarget*/);
-
                 imWindowChanged = prevTarget != newTarget;
 
                 if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
@@ -5707,13 +5695,15 @@
                 displayContent.setLayoutNeeded();
                 if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                     displayContent.performLayout(true /*initial*/, updateInputWindows);
+                } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
+                    mRoot.performSurfacePlacement(false);
                 }
             }
 
             if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
                 // If we defer assigning layers, then the caller is responsible for
                 // doing this part.
-                mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows);
+                displayContent.getInputMonitor().setInputFocusLw(mCurrentFocus, updateInputWindows);
             }
 
             displayContent.adjustForImeIfNeeded();
@@ -5760,7 +5750,7 @@
         // As a result, we only track the display that has initially froze the screen.
         mFrozenDisplayId = displayContent.getDisplayId();
 
-        mInputMonitor.freezeInputDispatchingLw();
+        mInputManagerCallback.freezeInputDispatchingLw();
 
         // Clear the last input window -- that is just used for
         // clean transitions between IMEs, and if we are freezing
@@ -5777,8 +5767,7 @@
         }
 
         mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
-        // TODO(multidisplay): rotation on non-default displays
-        if (CUSTOM_SCREEN_ROTATION && displayContent.isDefaultDisplay) {
+        if (CUSTOM_SCREEN_ROTATION) {
             mExitAnimId = exitAnim;
             mEnterAnimId = enterAnim;
             ScreenRotationAnimation screenRotationAnimation =
@@ -5792,7 +5781,7 @@
 
             displayContent.updateDisplayInfo();
             screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
-                    mPolicy.isDefaultOrientationForced(), isSecure,
+                    displayContent.getDisplayRotation().isDefaultOrientationForced(), isSecure,
                     this);
             mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
                     screenRotationAnimation);
@@ -5827,7 +5816,7 @@
         final int displayId = mFrozenDisplayId;
         mFrozenDisplayId = INVALID_DISPLAY;
         mDisplayFrozen = false;
-        mInputMonitor.thawInputDispatchingLw();
+        mInputManagerCallback.thawInputDispatchingLw();
         mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime);
         StringBuilder sb = new StringBuilder(128);
         sb.append("Screen frozen for ");
@@ -5962,7 +5951,7 @@
 
     @Override
     public void setRecentsVisibility(boolean visible) {
-        mAmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
+        mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
                 "setRecentsVisibility()");
         synchronized (mWindowMap) {
             mPolicy.setRecentsVisibilityLw(visible);
@@ -5984,7 +5973,7 @@
 
     @Override
     public void setShelfHeight(boolean visible, int shelfHeight) {
-        mAmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
+        mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
                 "setShelfHeight()");
         synchronized (mWindowMap) {
             getDefaultDisplayContentLocked().getPinnedStackController().setAdjustedForShelf(visible,
@@ -6067,24 +6056,36 @@
 
     @Override
     public WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name,
-            InputEventReceiver.Factory inputEventReceiverFactory) {
+            InputEventReceiver.Factory inputEventReceiverFactory, int displayId) {
         synchronized (mWindowMap) {
-            return mInputMonitor.createInputConsumer(looper, name, inputEventReceiverFactory);
+            DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent != null) {
+                return displayContent.getInputMonitor().createInputConsumer(looper, name,
+                        inputEventReceiverFactory);
+            } else {
+                return null;
+            }
         }
     }
 
     @Override
     public void createInputConsumer(IBinder token, String name, InputChannel inputChannel) {
         synchronized (mWindowMap) {
-            mInputMonitor.createInputConsumer(token, name, inputChannel, Binder.getCallingPid(),
-                    Binder.getCallingUserHandle());
+            // TODO(b/112049699): multi-display inputConsumer, just support default in current.
+            // Need consider about the behavior from controller.
+            DisplayContent displayContent = getDefaultDisplayContentLocked();
+            displayContent.getInputMonitor().createInputConsumer(token, name, inputChannel,
+                    Binder.getCallingPid(), Binder.getCallingUserHandle());
         }
     }
 
     @Override
     public boolean destroyInputConsumer(String name) {
         synchronized (mWindowMap) {
-            return mInputMonitor.destroyInputConsumer(name);
+            // TODO(b/112049699): multi-display inputConsumer, just support default in current.
+            // Need consider about the behavior from controller.
+            DisplayContent displayContent = getDefaultDisplayContentLocked();
+            return displayContent.getInputMonitor().destroyInputConsumer(name);
         }
     }
 
@@ -6203,6 +6204,17 @@
     }
 
     /**
+     * Returns true if the callingUid has any window currently visible to the user.
+     */
+    public boolean isAnyWindowVisibleForUid(int callingUid) {
+        synchronized (mWindowMap) {
+            return mRoot.forAllWindows(w -> {
+                return w.getOwningUid() == callingUid && w.isVisible();
+            }, true /* traverseTopToBottom */);
+        }
+    }
+
+    /**
      * Called when a task has been removed from the recent tasks list.
      * <p>
      * Note: This doesn't go through {@link TaskWindowContainerController} yet as the window
@@ -6428,7 +6440,7 @@
                 pw.print(" mLastWakeLockObscuringWindow="); pw.print(mLastWakeLockObscuringWindow);
                 pw.println();
 
-        mInputMonitor.dump(pw, "  ");
+        mInputManagerCallback.dump(pw, "  ");
         mUnknownAppVisibilityController.dump(pw, "  ");
         mTaskSnapshotController.dump(pw, "  ");
 
@@ -6463,7 +6475,6 @@
                     pw.print(defaultDisplayContent.getLastWindowForcedOrientation());
                     pw.print(" mLastOrientation=");
                             pw.println(defaultDisplayContent.getLastOrientation());
-            pw.print("  mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
             pw.print("  Animation settings: disabled="); pw.print(mAnimationsDisabled);
                     pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
                     pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
@@ -6748,8 +6759,10 @@
 
     public void onOverlayChanged() {
         synchronized (mWindowMap) {
-            mPolicy.onOverlayChangedLw();
-            getDefaultDisplayContentLocked().updateDisplayInfo();
+            mRoot.forAllDisplays(displayContent -> {
+                mPolicy.onOverlayChangedLw(displayContent);
+                displayContent.updateDisplayInfo();
+            });
             requestTraversal();
         }
     }
@@ -6906,16 +6919,26 @@
         }
     }
 
+    public void setSupportsFreeformWindowManagement(boolean supportsFreeformWindowManagement) {
+        synchronized (mWindowMap) {
+            mSupportsFreeformWindowManagement = supportsFreeformWindowManagement;
+        }
+    }
+
+    public void setIsPc(boolean isPc) {
+        synchronized (mWindowMap) {
+            mIsPc = isPc;
+        }
+    }
+
     static int dipToPixel(int dip, DisplayMetrics displayMetrics) {
         return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
     }
 
     @Override
     public void registerDockedStackListener(IDockedStackListener listener) {
-        if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS,
-                "registerDockedStackListener()")) {
-            return;
-        }
+        mAtmInternal.enforceCallerIsRecentsOrHasPermission(REGISTER_WINDOW_MANAGER_LISTENERS,
+                "registerDockedStackListener()");
         synchronized (mWindowMap) {
             // TODO(multi-display): The listener is registered on the default display only.
             getDefaultDisplayContentLocked().mDividerControllerLocked.registerDockedStackListener(
@@ -7077,19 +7100,24 @@
 
     @Override
     public void dontOverrideDisplayInfo(int displayId) {
-        synchronized (mWindowMap) {
-            final DisplayContent dc = getDisplayContentOrCreate(displayId);
-            if (dc == null) {
-                throw new IllegalArgumentException(
-                        "Trying to register a non existent display.");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mWindowMap) {
+                final DisplayContent dc = getDisplayContentOrCreate(displayId);
+                if (dc == null) {
+                    throw new IllegalArgumentException(
+                            "Trying to register a non existent display.");
+                }
+                // We usually set the override info in DisplayManager so that we get consistent
+                // values when displays are changing. However, we don't do this for displays that
+                // serve as containers for ActivityViews because we don't want letter-/pillar-boxing
+                // during resize.
+                dc.mShouldOverrideDisplayConfiguration = false;
+                mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(displayId,
+                        null /* info */);
             }
-            // We usually set the override info in DisplayManager so that we get consistent
-            // values when displays are changing. However, we don't do this for displays that
-            // serve as containers for ActivityViews because we don't want letter-/pillar-boxing
-            // during resize.
-            dc.mShouldOverrideDisplayConfiguration = false;
-            mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(displayId,
-                    null /* info */);
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
     }
 
@@ -7128,11 +7156,7 @@
             }
             finishSeamlessRotation();
 
-            final DisplayContent displayContent = w.getDisplayContent();
-            if (displayContent.updateRotationUnchecked()) {
-                mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
-                        .sendToTarget();
-            }
+            w.getDisplayContent().updateRotationAndSendNewConfigIfNeeded();
         }
     }
 
@@ -7262,7 +7286,7 @@
             synchronized (mWindowMap) {
                 WindowState windowState = mWindowMap.get(token);
                 if (windowState != null) {
-                    outBounds.set(windowState.mFrame);
+                    outBounds.set(windowState.getFrameLw());
                 } else {
                     outBounds.setEmpty();
                 }
@@ -7352,16 +7376,18 @@
 
         @Override
         public void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
-                boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed,
-                @Nullable IBinder targetWindowToken) {
+                boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed) {
+            mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
+        }
+
+        @Override
+        public void updateInputMethodTargetWindow(@NonNull IBinder imeToken,
+                @NonNull IBinder imeTargetWindowToken) {
             // TODO (b/34628091): Use this method to address the window animation issue.
             if (DEBUG_INPUT_METHOD) {
-                Slog.w(TAG_WM, "updateInputMethodWindowStatus: imeToken=" + imeToken
-                        + " dismissImeOnBackKeyPressed=" + dismissImeOnBackKeyPressed
-                        + " imeWindowVisible=" + imeWindowVisible
-                        + " targetWindowToken=" + targetWindowToken);
+                Slog.w(TAG_WM, "updateInputMethodTargetWindow: imeToken=" + imeToken
+                        + " imeTargetWindowToken=" + imeTargetWindowToken);
             }
-            mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
         }
 
         @Override
@@ -7401,7 +7427,7 @@
                 accessibilityController = mAccessibilityController;
             }
             if (accessibilityController != null) {
-                accessibilityController.performComputeChangedWindowsNotLocked();
+                accessibilityController.performComputeChangedWindowsNotLocked(true);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0154e0a..58fb7a0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.OP_NONE;
@@ -100,6 +100,7 @@
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.localLOGV;
@@ -118,15 +119,10 @@
 import static com.android.server.wm.WindowStateProto.ANIMATOR;
 import static com.android.server.wm.WindowStateProto.ATTRIBUTES;
 import static com.android.server.wm.WindowStateProto.CHILD_WINDOWS;
-import static com.android.server.wm.WindowStateProto.CONTAINING_FRAME;
-import static com.android.server.wm.WindowStateProto.CONTENT_FRAME;
 import static com.android.server.wm.WindowStateProto.CONTENT_INSETS;
-import static com.android.server.wm.WindowStateProto.CUTOUT;
-import static com.android.server.wm.WindowStateProto.DECOR_FRAME;
 import static com.android.server.wm.WindowStateProto.DESTROYING;
-import static com.android.server.wm.WindowStateProto.DISPLAY_FRAME;
 import static com.android.server.wm.WindowStateProto.DISPLAY_ID;
-import static com.android.server.wm.WindowStateProto.FRAME;
+import static com.android.server.wm.WindowStateProto.FINISHED_FORCED_SEAMLESS_ROTATION_FRAME;
 import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
 import static com.android.server.wm.WindowStateProto.HAS_SURFACE;
 import static com.android.server.wm.WindowStateProto.IDENTIFIER;
@@ -134,10 +130,8 @@
 import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
 import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
 import static com.android.server.wm.WindowStateProto.OUTSETS;
-import static com.android.server.wm.WindowStateProto.OUTSET_FRAME;
-import static com.android.server.wm.WindowStateProto.OVERSCAN_FRAME;
 import static com.android.server.wm.WindowStateProto.OVERSCAN_INSETS;
-import static com.android.server.wm.WindowStateProto.PARENT_FRAME;
+import static com.android.server.wm.WindowStateProto.PENDING_FORCED_SEAMLESS_ROTATION;
 import static com.android.server.wm.WindowStateProto.REMOVED;
 import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
 import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
@@ -148,9 +142,11 @@
 import static com.android.server.wm.WindowStateProto.SURFACE_POSITION;
 import static com.android.server.wm.WindowStateProto.SYSTEM_UI_VISIBILITY;
 import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
-import static com.android.server.wm.WindowStateProto.VISIBLE_FRAME;
 import static com.android.server.wm.WindowStateProto.VISIBLE_INSETS;
 import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
+import static com.android.server.wm.utils.CoordinateTransforms.transformRect;
+import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
 
 import android.annotation.CallSuper;
 import android.app.AppOpsManager;
@@ -279,6 +275,15 @@
     private boolean mDragResizing;
     private boolean mDragResizingChangeReported = true;
     private int mResizeMode;
+    /**
+     * Special mode that is intended only for the rounded corner overlay: during rotation
+     * transition, we un-rotate the window token such that the window appears as it did before the
+     * rotation.
+     * TODO(b/111504081): Consolidate seamless rotation logic.
+     */
+    final boolean mForceSeamlesslyRotate;
+    ForcedSeamlessRotator mPendingForcedSeamlessRotate;
+    long mFinishForcedSeamlessRotateFrameNumber;
 
     private RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
 
@@ -355,9 +360,6 @@
     private final Rect mLastOutsets = new Rect();
     private boolean mOutsetsChanged = false;
 
-    /** Part of the display that has been cut away. See {@link DisplayCutout}. */
-    WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
-    private WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT;
     private boolean mDisplayCutoutChanged;
 
     /**
@@ -398,54 +400,16 @@
     float mLastHScale=1, mLastVScale=1;
     final Matrix mTmpMatrix = new Matrix();
 
-    // "Real" frame that the application sees, in display coordinate space.
-    final Rect mFrame = new Rect();
-    final Rect mLastFrame = new Rect();
     private boolean mFrameSizeChanged = false;
     // Frame that is scaled to the application's coordinate space when in
     // screen size compatibility mode.
     final Rect mCompatFrame = new Rect();
 
-    final Rect mContainingFrame = new Rect();
-
-    final Rect mParentFrame = new Rect();
-
-    /** Whether the parent frame would have been different if there was no display cutout. */
-    private boolean mParentFrameWasClippedByDisplayCutout;
-
-    // The entire screen area of the {@link TaskStack} this window is in. Usually equal to the
-    // screen area of the device.
-    final Rect mDisplayFrame = new Rect();
-
-    // The region of the display frame that the display type supports displaying content on. This
-    // is mostly a special case for TV where some displays don’t have the entire display usable.
-    // {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} flag can be used to allow
-    // window display contents to extend into the overscan region.
-    private final Rect mOverscanFrame = new Rect();
-
-    // The display frame minus the stable insets. This value is always constant regardless of if
-    // the status bar or navigation bar is visible.
-    private final Rect mStableFrame = new Rect();
-
-    // The area not occupied by the status and navigation bars. So, if both status and navigation
-    // bars are visible, the decor frame is equal to the stable frame.
-    final Rect mDecorFrame = new Rect();
-
-    // Equal to the decor frame if the IME (e.g. keyboard) is not present. Equal to the decor frame
-    // minus the area occupied by the IME if the IME is present.
-    private final Rect mContentFrame = new Rect();
-
-    // Legacy stuff. Generally equal to the content frame expect when the IME for older apps
-    // displays hint text.
-    final Rect mVisibleFrame = new Rect();
-
-    // Frame that includes dead area outside of the surface but where we want to pretend that it's
-    // possible to draw.
-    private final Rect mOutsetFrame = new Rect();
+    private final WindowFrames mWindowFrames = new WindowFrames();
 
     /**
      * Usually empty. Set to the task's tempInsetFrame. See
-     *{@link android.app.IActivityManager#resizeDockedStack}.
+     *{@link android.app.IActivityTaskManager#resizeDockedStack}.
      */
     private final Rect mInsetFrame = new Rect();
 
@@ -668,6 +632,18 @@
 
     private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
 
+    void forceSeamlesslyRotateIfAllowed(int oldRotation, int rotation) {
+        if (mForceSeamlesslyRotate) {
+            if (mPendingForcedSeamlessRotate != null) {
+                oldRotation = mPendingForcedSeamlessRotate.getOldRotation();
+            }
+
+            mPendingForcedSeamlessRotate = new ForcedSeamlessRotator(
+                    oldRotation, rotation, getDisplayInfo());
+            mPendingForcedSeamlessRotate.unrotate(this.mToken);
+        }
+    }
+
     interface PowerManagerWrapper {
         void wakeUp(long time, String reason);
 
@@ -714,6 +690,7 @@
         mSeq = seq;
         mEnforceSizeCompat = (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0;
         mPowerManagerWrapper = powerManagerWrapper;
+        mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
         if (localLOGV) Slog.v(
             TAG, "Window " + this + " client=" + c.asBinder()
             + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
@@ -847,10 +824,7 @@
     }
 
     @Override
-    public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
-            Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
-            Rect outsetFrame, WmDisplayCutout displayCutout,
-            boolean parentFrameWasClippedByDisplayCutout) {
+    public void computeFrameLw(WindowFrames windowFrames) {
         if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
             // This window is being replaced and either already got information that it's being
             // removed or we are still waiting for some information. Because of this we don't
@@ -859,7 +833,8 @@
             return;
         }
         mHaveFrame = true;
-        mParentFrameWasClippedByDisplayCutout = parentFrameWasClippedByDisplayCutout;
+        mWindowFrames.setParentFrameWasClippedByDisplayCutout(
+                windowFrames.parentFrameWasClippedByDisplayCutout());
 
         final Task task = getTask();
         final boolean inFullscreenContainer = inFullscreenContainer();
@@ -889,36 +864,39 @@
         final int layoutYDiff;
         if (inFullscreenContainer || layoutInParentFrame()) {
             // We use the parent frame as the containing frame for fullscreen and child windows
-            mContainingFrame.set(parentFrame);
-            mDisplayFrame.set(displayFrame);
-            layoutDisplayFrame = displayFrame;
-            layoutContainingFrame = parentFrame;
+            mWindowFrames.mContainingFrame.set(windowFrames.mParentFrame);
+            mWindowFrames.mDisplayFrame.set(windowFrames.mDisplayFrame);
+            layoutDisplayFrame = windowFrames.mDisplayFrame;
+            layoutContainingFrame = windowFrames.mParentFrame;
             layoutXDiff = 0;
             layoutYDiff = 0;
         } else {
-            getBounds(mContainingFrame);
+            getBounds(mWindowFrames.mContainingFrame);
             if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
 
                 // If the bounds are frozen, we still want to translate the window freely and only
                 // freeze the size.
                 Rect frozen = mAppToken.mFrozenBounds.peek();
-                mContainingFrame.right = mContainingFrame.left + frozen.width();
-                mContainingFrame.bottom = mContainingFrame.top + frozen.height();
+                mWindowFrames.mContainingFrame.right =
+                        mWindowFrames.mContainingFrame.left + frozen.width();
+                mWindowFrames.mContainingFrame.bottom =
+                        mWindowFrames.mContainingFrame.top + frozen.height();
             }
             final WindowState imeWin = mService.mInputMethodWindow;
             // IME is up and obscuring this window. Adjust the window position so it is visible.
             if (imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget()) {
-                if (inFreeformWindowingMode()
-                        && mContainingFrame.bottom > contentFrame.bottom) {
+                if (inFreeformWindowingMode() && mWindowFrames.mContainingFrame.bottom
+                        > windowFrames.mContentFrame.bottom) {
                     // In freeform we want to move the top up directly.
                     // TODO: Investigate why this is contentFrame not parentFrame.
-                    mContainingFrame.top -= mContainingFrame.bottom - contentFrame.bottom;
-                } else if (!inPinnedWindowingMode()
-                        && mContainingFrame.bottom > parentFrame.bottom) {
+                    mWindowFrames.mContainingFrame.top -= mWindowFrames.mContainingFrame.bottom
+                            - windowFrames.mContentFrame.bottom;
+                } else if (!inPinnedWindowingMode() && mWindowFrames.mContainingFrame.bottom
+                        > windowFrames.mParentFrame.bottom) {
                     // But in docked we want to behave like fullscreen and behave as if the task
                     // were given smaller bounds for the purposes of layout. Skip adjustments for
                     // the pinned stack, they are handled separately in the PinnedStackController.
-                    mContainingFrame.bottom = parentFrame.bottom;
+                    mWindowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom;
                 }
             }
 
@@ -926,8 +904,8 @@
                 // In floating modes (e.g. freeform, pinned) we have only to set the rectangle
                 // if it wasn't set already. No need to intersect it with the (visible)
                 // "content frame" since it is allowed to be outside the visible desktop.
-                if (mContainingFrame.isEmpty()) {
-                    mContainingFrame.set(contentFrame);
+                if (mWindowFrames.mContainingFrame.isEmpty()) {
+                    mWindowFrames.mContainingFrame.set(windowFrames.mContentFrame);
                 }
             }
 
@@ -937,31 +915,39 @@
                 // PIP edge case: When going from pinned to fullscreen, we apply a
                 // tempInsetFrame for the full task - but we're still at the start of the animation.
                 // To prevent a jump if there's a letterbox, restrict to the parent frame.
-                mInsetFrame.intersectUnchecked(parentFrame);
-                mContainingFrame.intersectUnchecked(parentFrame);
+                mInsetFrame.intersectUnchecked(windowFrames.mParentFrame);
+                mWindowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame);
             }
 
-            mDisplayFrame.set(mContainingFrame);
-            layoutXDiff = !mInsetFrame.isEmpty() ? mInsetFrame.left - mContainingFrame.left : 0;
-            layoutYDiff = !mInsetFrame.isEmpty() ? mInsetFrame.top - mContainingFrame.top : 0;
-            layoutContainingFrame = !mInsetFrame.isEmpty() ? mInsetFrame : mContainingFrame;
+            mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame);
+            layoutXDiff =
+                    !mInsetFrame.isEmpty() ? mInsetFrame.left - mWindowFrames.mContainingFrame.left
+                            : 0;
+            layoutYDiff =
+                    !mInsetFrame.isEmpty() ? mInsetFrame.top - mWindowFrames.mContainingFrame.top
+                            : 0;
+            layoutContainingFrame =
+                    !mInsetFrame.isEmpty() ? mInsetFrame : mWindowFrames.mContainingFrame;
             mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
-            subtractInsets(mDisplayFrame, layoutContainingFrame, displayFrame, mTmpRect);
+            subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame,
+                    windowFrames.mDisplayFrame, mTmpRect);
             if (!layoutInParentFrame()) {
-                subtractInsets(mContainingFrame, layoutContainingFrame, parentFrame, mTmpRect);
-                subtractInsets(mInsetFrame, layoutContainingFrame, parentFrame, mTmpRect);
+                subtractInsets(mWindowFrames.mContainingFrame, layoutContainingFrame,
+                        windowFrames.mParentFrame, mTmpRect);
+                subtractInsets(mInsetFrame, layoutContainingFrame, windowFrames.mParentFrame,
+                        mTmpRect);
             }
-            layoutDisplayFrame = displayFrame;
+            layoutDisplayFrame = windowFrames.mDisplayFrame;
             layoutDisplayFrame.intersect(layoutContainingFrame);
         }
 
-        final int pw = mContainingFrame.width();
-        final int ph = mContainingFrame.height();
+        final int pw = mWindowFrames.mContainingFrame.width();
+        final int ph = mWindowFrames.mContainingFrame.height();
 
-        if (!mParentFrame.equals(parentFrame)) {
+        if (!mWindowFrames.mParentFrame.equals(windowFrames.mParentFrame)) {
             //Slog.i(TAG_WM, "Window " + this + " content frame from " + mParentFrame
             //        + " to " + parentFrame);
-            mParentFrame.set(parentFrame);
+            mWindowFrames.mParentFrame.set(windowFrames.mParentFrame);
             mContentChanged = true;
         }
         if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
@@ -970,146 +956,161 @@
             mContentChanged = true;
         }
 
-        mOverscanFrame.set(overscanFrame);
-        mContentFrame.set(contentFrame);
-        mVisibleFrame.set(visibleFrame);
-        mDecorFrame.set(decorFrame);
-        mStableFrame.set(stableFrame);
-        final boolean hasOutsets = outsetFrame != null;
+        mWindowFrames.mOverscanFrame.set(windowFrames.mOverscanFrame);
+        mWindowFrames.mContentFrame.set(windowFrames.mContentFrame);
+        mWindowFrames.mVisibleFrame.set(windowFrames.mVisibleFrame);
+        mWindowFrames.mDecorFrame.set(windowFrames.mDecorFrame);
+        mWindowFrames.mStableFrame.set(windowFrames.mStableFrame);
+        final boolean hasOutsets = !windowFrames.mOutsetFrame.isEmpty();
         if (hasOutsets) {
-            mOutsetFrame.set(outsetFrame);
+            mWindowFrames.mOutsetFrame.set(windowFrames.mOutsetFrame);
         }
 
-        final int fw = mFrame.width();
-        final int fh = mFrame.height();
+        final int fw = mWindowFrames.mFrame.width();
+        final int fh = mWindowFrames.mFrame.height();
 
         applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
 
         // Calculate the outsets before the content frame gets shrinked to the window frame.
         if (hasOutsets) {
-            mOutsets.set(Math.max(mContentFrame.left - mOutsetFrame.left, 0),
-                    Math.max(mContentFrame.top - mOutsetFrame.top, 0),
-                    Math.max(mOutsetFrame.right - mContentFrame.right, 0),
-                    Math.max(mOutsetFrame.bottom - mContentFrame.bottom, 0));
+            mOutsets.set(
+                    Math.max(mWindowFrames.mContentFrame.left - mWindowFrames.mOutsetFrame.left, 0),
+                    Math.max(mWindowFrames.mContentFrame.top - mWindowFrames.mOutsetFrame.top, 0),
+                    Math.max(mWindowFrames.mOutsetFrame.right - mWindowFrames.mContentFrame.right,
+                            0),
+                    Math.max(mWindowFrames.mOutsetFrame.bottom - mWindowFrames.mContentFrame.bottom,
+                            0));
         } else {
             mOutsets.set(0, 0, 0, 0);
         }
 
         // Make sure the content and visible frames are inside of the
         // final window frame.
-        if (windowsAreFloating && !mFrame.isEmpty()) {
+        if (windowsAreFloating && !mWindowFrames.mFrame.isEmpty()) {
             // For pinned workspace the frame isn't limited in any particular
             // way since SystemUI controls the bounds. For freeform however
             // we want to keep things inside the content frame.
-            final Rect limitFrame = task.inPinnedWindowingMode() ? mFrame : mContentFrame;
+            final Rect limitFrame = task.inPinnedWindowingMode() ? mWindowFrames.mFrame
+                    : mWindowFrames.mContentFrame;
             // Keep the frame out of the blocked system area, limit it in size to the content area
             // and make sure that there is always a minimum visible so that the user can drag it
             // into a usable area..
-            final int height = Math.min(mFrame.height(), limitFrame.height());
-            final int width = Math.min(limitFrame.width(), mFrame.width());
+            final int height = Math.min(mWindowFrames.mFrame.height(), limitFrame.height());
+            final int width = Math.min(limitFrame.width(), mWindowFrames.mFrame.width());
             final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
             final int minVisibleHeight = Math.min(height, WindowManagerService.dipToPixel(
                     MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics));
             final int minVisibleWidth = Math.min(width, WindowManagerService.dipToPixel(
                     MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics));
             final int top = Math.max(limitFrame.top,
-                    Math.min(mFrame.top, limitFrame.bottom - minVisibleHeight));
+                    Math.min( mWindowFrames.mFrame.top, limitFrame.bottom - minVisibleHeight));
             final int left = Math.max(limitFrame.left + minVisibleWidth - width,
-                    Math.min(mFrame.left, limitFrame.right - minVisibleWidth));
-            mFrame.set(left, top, left + width, top + height);
-            mContentFrame.set(mFrame);
-            mVisibleFrame.set(mContentFrame);
-            mStableFrame.set(mContentFrame);
+                    Math.min( mWindowFrames.mFrame.left, limitFrame.right - minVisibleWidth));
+            mWindowFrames.mFrame.set(left, top, left + width, top + height);
+            mWindowFrames.mContentFrame.set( mWindowFrames.mFrame);
+            mWindowFrames.mVisibleFrame.set(mWindowFrames.mContentFrame);
+            mWindowFrames.mStableFrame.set(mWindowFrames.mContentFrame);
         } else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
-            dc.getDockedDividerController().positionDockedStackedDivider(mFrame);
-            mContentFrame.set(mFrame);
-            if (!mFrame.equals(mLastFrame)) {
+            dc.getDockedDividerController().positionDockedStackedDivider(mWindowFrames.mFrame);
+            mWindowFrames.mContentFrame.set(mWindowFrames.mFrame);
+            if (!mWindowFrames.mFrame.equals(mWindowFrames.mLastFrame)) {
                 mMovedByResize = true;
             }
         } else {
-            mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
-                    Math.max(mContentFrame.top, mFrame.top),
-                    Math.min(mContentFrame.right, mFrame.right),
-                    Math.min(mContentFrame.bottom, mFrame.bottom));
+            mWindowFrames.mContentFrame.set(
+                    Math.max(mWindowFrames.mContentFrame.left, mWindowFrames.mFrame.left),
+                    Math.max(mWindowFrames.mContentFrame.top, mWindowFrames.mFrame.top),
+                    Math.min(mWindowFrames.mContentFrame.right, mWindowFrames.mFrame.right),
+                    Math.min(mWindowFrames.mContentFrame.bottom, mWindowFrames.mFrame.bottom));
 
-            mVisibleFrame.set(Math.max(mVisibleFrame.left, mFrame.left),
-                    Math.max(mVisibleFrame.top, mFrame.top),
-                    Math.min(mVisibleFrame.right, mFrame.right),
-                    Math.min(mVisibleFrame.bottom, mFrame.bottom));
+            mWindowFrames.mVisibleFrame.set(
+                    Math.max(mWindowFrames.mVisibleFrame.left, mWindowFrames.mFrame.left),
+                    Math.max(mWindowFrames.mVisibleFrame.top, mWindowFrames.mFrame.top),
+                    Math.min(mWindowFrames.mVisibleFrame.right, mWindowFrames.mFrame.right),
+                    Math.min(mWindowFrames.mVisibleFrame.bottom, mWindowFrames.mFrame.bottom));
 
-            mStableFrame.set(Math.max(mStableFrame.left, mFrame.left),
-                    Math.max(mStableFrame.top, mFrame.top),
-                    Math.min(mStableFrame.right, mFrame.right),
-                    Math.min(mStableFrame.bottom, mFrame.bottom));
+            mWindowFrames.mStableFrame.set(
+                    Math.max(mWindowFrames.mStableFrame.left, mWindowFrames.mFrame.left),
+                    Math.max(mWindowFrames.mStableFrame.top, mWindowFrames.mFrame.top),
+                    Math.min(mWindowFrames.mStableFrame.right, mWindowFrames.mFrame.right),
+                    Math.min(mWindowFrames.mStableFrame.bottom, mWindowFrames.mFrame.bottom));
         }
 
         if (inFullscreenContainer && !windowsAreFloating) {
             // Windows that are not fullscreen can be positioned outside of the display frame,
             // but that is not a reason to provide them with overscan insets.
-            mOverscanInsets.set(Math.max(mOverscanFrame.left - layoutContainingFrame.left, 0),
-                    Math.max(mOverscanFrame.top - layoutContainingFrame.top, 0),
-                    Math.max(layoutContainingFrame.right - mOverscanFrame.right, 0),
-                    Math.max(layoutContainingFrame.bottom - mOverscanFrame.bottom, 0));
+            mOverscanInsets.set(
+                    Math.max(mWindowFrames.mOverscanFrame.left - layoutContainingFrame.left, 0),
+                    Math.max(mWindowFrames.mOverscanFrame.top - layoutContainingFrame.top, 0),
+                    Math.max(layoutContainingFrame.right - mWindowFrames.mOverscanFrame.right, 0),
+                    Math.max(layoutContainingFrame.bottom - mWindowFrames.mOverscanFrame.bottom,
+                            0));
         }
 
         if (mAttrs.type == TYPE_DOCK_DIVIDER) {
             // For the docked divider, we calculate the stable insets like a full-screen window
             // so it can use it to calculate the snap positions.
-            final WmDisplayCutout c = displayCutout.calculateRelativeTo(mDisplayFrame);
-            mTmpRect.set(mDisplayFrame);
+            final WmDisplayCutout c = windowFrames.mDisplayCutout.calculateRelativeTo(
+                    mWindowFrames.mDisplayFrame);
+            mTmpRect.set(mWindowFrames.mDisplayFrame);
             mTmpRect.inset(c.getDisplayCutout().getSafeInsets());
-            mTmpRect.intersectUnchecked(mStableFrame);
+            mTmpRect.intersectUnchecked(mWindowFrames.mStableFrame);
 
-            mStableInsets.set(Math.max(mTmpRect.left - mDisplayFrame.left, 0),
-                    Math.max(mTmpRect.top - mDisplayFrame.top, 0),
-                    Math.max(mDisplayFrame.right - mTmpRect.right, 0),
-                    Math.max(mDisplayFrame.bottom - mTmpRect.bottom, 0));
+            mStableInsets.set(Math.max(mTmpRect.left - mWindowFrames.mDisplayFrame.left, 0),
+                    Math.max(mTmpRect.top - mWindowFrames.mDisplayFrame.top, 0),
+                    Math.max(mWindowFrames.mDisplayFrame.right - mTmpRect.right, 0),
+                    Math.max(mWindowFrames.mDisplayFrame.bottom - mTmpRect.bottom, 0));
 
             // The divider doesn't care about insets in any case, so set it to empty so we don't
             // trigger a relayout when moving it.
             mContentInsets.setEmpty();
             mVisibleInsets.setEmpty();
-            displayCutout = WmDisplayCutout.NO_CUTOUT;
+            windowFrames.setDisplayCutout(WmDisplayCutout.NO_CUTOUT);
         } else {
             getDisplayContent().getBounds(mTmpRect);
             // Override right and/or bottom insets in case if the frame doesn't fit the screen in
             // non-fullscreen mode.
             boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer
-                    && mFrame.right > mTmpRect.right;
+                    && mWindowFrames.mFrame.right > mTmpRect.right;
             boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer
-                    && mFrame.bottom > mTmpRect.bottom;
-            mContentInsets.set(mContentFrame.left - mFrame.left,
-                    mContentFrame.top - mFrame.top,
-                    overrideRightInset ? mTmpRect.right - mContentFrame.right
-                            : mFrame.right - mContentFrame.right,
-                    overrideBottomInset ? mTmpRect.bottom - mContentFrame.bottom
-                            : mFrame.bottom - mContentFrame.bottom);
+                    && mWindowFrames.mFrame.bottom > mTmpRect.bottom;
+            mContentInsets.set(mWindowFrames.mContentFrame.left - mWindowFrames.mFrame.left,
+                    mWindowFrames.mContentFrame.top - mWindowFrames.mFrame.top,
+                    overrideRightInset ? mTmpRect.right - mWindowFrames.mContentFrame.right
+                            : mWindowFrames.mFrame.right - mWindowFrames.mContentFrame.right,
+                    overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mContentFrame.bottom
+                            : mWindowFrames.mFrame.bottom - mWindowFrames.mContentFrame.bottom);
 
-            mVisibleInsets.set(mVisibleFrame.left - mFrame.left,
-                    mVisibleFrame.top - mFrame.top,
-                    overrideRightInset ? mTmpRect.right - mVisibleFrame.right
-                            : mFrame.right - mVisibleFrame.right,
-                    overrideBottomInset ? mTmpRect.bottom - mVisibleFrame.bottom
-                            : mFrame.bottom - mVisibleFrame.bottom);
+            mVisibleInsets.set(mWindowFrames.mVisibleFrame.left - mWindowFrames.mFrame.left,
+                    mWindowFrames.mVisibleFrame.top - mWindowFrames.mFrame.top,
+                    overrideRightInset ? mTmpRect.right - mWindowFrames.mVisibleFrame.right
+                            : mWindowFrames.mFrame.right - mWindowFrames.mVisibleFrame.right,
+                    overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mVisibleFrame.bottom
+                            : mWindowFrames.mFrame.bottom - mWindowFrames.mVisibleFrame.bottom);
 
-            mStableInsets.set(Math.max(mStableFrame.left - mFrame.left, 0),
-                    Math.max(mStableFrame.top - mFrame.top, 0),
-                    overrideRightInset ? Math.max(mTmpRect.right - mStableFrame.right, 0)
-                            : Math.max(mFrame.right - mStableFrame.right, 0),
-                    overrideBottomInset ? Math.max(mTmpRect.bottom - mStableFrame.bottom, 0)
-                            :  Math.max(mFrame.bottom - mStableFrame.bottom, 0));
+            mStableInsets.set(
+                    Math.max(mWindowFrames.mStableFrame.left - mWindowFrames.mFrame.left, 0),
+                    Math.max(mWindowFrames.mStableFrame.top - mWindowFrames.mFrame.top, 0),
+                    overrideRightInset ? Math.max(mTmpRect.right - mWindowFrames.mStableFrame.right,
+                            0) : Math.max(
+                            mWindowFrames.mFrame.right - mWindowFrames.mStableFrame.right, 0),
+                    overrideBottomInset ? Math.max(
+                            mTmpRect.bottom - mWindowFrames.mStableFrame.bottom, 0) : Math.max(
+                            mWindowFrames.mFrame.bottom - mWindowFrames.mStableFrame.bottom, 0));
         }
 
-        mDisplayCutout = displayCutout.calculateRelativeTo(mFrame);
+
+        mWindowFrames.setDisplayCutout(
+                windowFrames.mDisplayCutout.calculateRelativeTo(windowFrames.mFrame));
 
         // Offset the actual frame by the amount layout frame is off.
-        mFrame.offset(-layoutXDiff, -layoutYDiff);
+        mWindowFrames.mFrame.offset(-layoutXDiff, -layoutYDiff);
         mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
-        mContentFrame.offset(-layoutXDiff, -layoutYDiff);
-        mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
-        mStableFrame.offset(-layoutXDiff, -layoutYDiff);
+        mWindowFrames.mContentFrame.offset(-layoutXDiff, -layoutYDiff);
+        mWindowFrames.mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
+        mWindowFrames.mStableFrame.offset(-layoutXDiff, -layoutYDiff);
 
-        mCompatFrame.set(mFrame);
+        mCompatFrame.set(mWindowFrames.mFrame);
         if (mEnforceSizeCompat) {
             // If there is a size compatibility scale being applied to the
             // window, we need to apply this to its insets so that they are
@@ -1125,12 +1126,13 @@
             mCompatFrame.scale(mInvGlobalScale);
         }
 
-        if (mIsWallpaper && (fw != mFrame.width() || fh != mFrame.height())) {
+        if (mIsWallpaper && (fw != mWindowFrames.mFrame.width()
+                || fh != mWindowFrames.mFrame.height())) {
             final DisplayContent displayContent = getDisplayContent();
             if (displayContent != null) {
                 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-                getDisplayContent().mWallpaperController.updateWallpaperOffset(
-                        this, displayInfo.logicalWidth, displayInfo.logicalHeight, false);
+                getDisplayContent().mWallpaperController.updateWallpaperOffset(this,
+                        displayInfo.logicalWidth, displayInfo.logicalHeight, false);
             }
         }
 
@@ -1138,7 +1140,7 @@
                 "Resolving (mRequestedWidth="
                 + mRequestedWidth + ", mRequestedheight="
                 + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
-                + "): frame=" + mFrame.toShortString()
+                + "): frame=" + mWindowFrames.mFrame.toShortString()
                 + " ci=" + mContentInsets.toShortString()
                 + " vi=" + mVisibleInsets.toShortString()
                 + " si=" + mStableInsets.toShortString()
@@ -1159,31 +1161,47 @@
 
     @Override
     public Rect getFrameLw() {
-        return mFrame;
+        return mWindowFrames.mFrame;
     }
 
     @Override
     public Rect getDisplayFrameLw() {
-        return mDisplayFrame;
+        return mWindowFrames.mDisplayFrame;
     }
 
     @Override
     public Rect getOverscanFrameLw() {
-        return mOverscanFrame;
+        return mWindowFrames.mOverscanFrame;
     }
 
     @Override
     public Rect getContentFrameLw() {
-        return mContentFrame;
+        return mWindowFrames.mContentFrame;
     }
 
     @Override
     public Rect getVisibleFrameLw() {
-        return mVisibleFrame;
+        return mWindowFrames.mVisibleFrame;
     }
 
     Rect getStableFrameLw() {
-        return mStableFrame;
+        return mWindowFrames.mStableFrame;
+    }
+
+    Rect getDecorFrame() {
+        return mWindowFrames.mDecorFrame;
+    }
+
+    Rect getParentFrame() {
+        return mWindowFrames.mParentFrame;
+    }
+
+    Rect getContainingFrame() {
+        return mWindowFrames.mContainingFrame;
+    }
+
+    WmDisplayCutout getWmDisplayCutout() {
+        return mWindowFrames.mDisplayCutout;
     }
 
     @Override
@@ -1242,9 +1260,8 @@
         mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets);
         mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets);
         mOutsetsChanged |= !mLastOutsets.equals(mOutsets);
-        mFrameSizeChanged |= (mLastFrame.width() != mFrame.width()) ||
-                (mLastFrame.height() != mFrame.height());
-        mDisplayCutoutChanged |= !mLastDisplayCutout.equals(mDisplayCutout);
+        mFrameSizeChanged |= mWindowFrames.didFrameSizeChange();
+        mDisplayCutoutChanged |= mWindowFrames.didDisplayCutoutChange();
         return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged
                 || mOutsetsChanged || mFrameSizeChanged || mDisplayCutoutChanged;
     }
@@ -1279,12 +1296,12 @@
                 && !isDragResizingChangeReported();
 
         if (localLOGV) Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged
-                + " dragResizingChanged=" + dragResizingChanged + " last=" + mLastFrame
-                + " frame=" + mFrame);
+                + " dragResizingChanged=" + dragResizingChanged
+                + " last=" + mWindowFrames.mLastFrame + " frame=" + mWindowFrames.mFrame);
 
         // We update mLastFrame always rather than in the conditional with the last inset
         // variables, because mFrameSizeChanged only tracks the width and height changing.
-        mLastFrame.set(mFrame);
+        mWindowFrames.mLastFrame.set(mWindowFrames.mFrame);
 
         if (mContentInsetsChanged
                 || mVisibleInsetsChanged
@@ -1439,13 +1456,13 @@
             }
         }
 
-        bounds.set(mVisibleFrame);
+        bounds.set(mWindowFrames.mVisibleFrame);
         if (intersectWithStackBounds) {
             bounds.intersect(mTmpRect);
         }
 
         if (bounds.isEmpty()) {
-            bounds.set(mFrame);
+            bounds.set(mWindowFrames.mFrame);
             if (intersectWithStackBounds) {
                 bounds.intersect(mTmpRect);
             }
@@ -1802,8 +1819,8 @@
 
         // Frame has moved, containing content frame has also moved, and we're not currently
         // animating... let's do something.
-        final int left = mFrame.left;
-        final int top = mFrame.top;
+        final int left = mWindowFrames.mFrame.left;
+        final int top = mWindowFrames.mFrame.top;
         final Task task = getTask();
         final boolean adjustedForMinimizedDockOrIme = task != null
                 && (task.mStack.isAdjustedForMinimizedDockedStack()
@@ -1812,7 +1829,8 @@
                 && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
                 && !isDragResizing() && !adjustedForMinimizedDockOrIme
                 && getWindowConfiguration().hasMovementAnimations()
-                && !mWinAnimator.mLastHidden) {
+                && !mWinAnimator.mLastHidden
+                && !mSeamlesslyRotated) {
             startMoveAnimation(left, top);
         }
 
@@ -1836,7 +1854,8 @@
     private boolean hasMoved() {
         return mHasSurface && (mContentChanged || mMovedByResize)
                 && !mAnimatingExit
-                && (mFrame.top != mLastFrame.top || mFrame.left != mLastFrame.left)
+                && (mWindowFrames.mFrame.top != mWindowFrames.mLastFrame.top
+                    || mWindowFrames.mFrame.left != mWindowFrames.mLastFrame.left)
                 && (!mIsChildWindow || !getParentWindow().hasMoved());
     }
 
@@ -1850,8 +1869,9 @@
 
     boolean fillsDisplay() {
         final DisplayInfo displayInfo = getDisplayInfo();
-        return mFrame.left <= 0 && mFrame.top <= 0
-                && mFrame.right >= displayInfo.appWidth && mFrame.bottom >= displayInfo.appHeight;
+        return mWindowFrames.mFrame.left <= 0 && mWindowFrames.mFrame.top <= 0
+                && mWindowFrames.mFrame.right >= displayInfo.appWidth
+                && mWindowFrames.mFrame.bottom >= displayInfo.appHeight;
     }
 
     /** Returns true if last applied config was not yet requested by client. */
@@ -2012,7 +2032,7 @@
                     // Set up a replacement input channel since the app is now dead.
                     // We need to catch tapping on the dead window to restart the app.
                     openInputChannel(null);
-                    mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+                    getDisplayContent().getInputMonitor().updateInputWindowsLw(true /*force*/);
                     return;
                 }
 
@@ -2061,7 +2081,10 @@
             if (wasVisible && mService.updateOrientationFromAppTokensLocked(displayId)) {
                 mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
             }
-            mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
+            mService.updateFocusedWindowLocked(mService.mCurrentFocus == this
+                            ? UPDATE_FOCUS_REMOVING_FOCUS
+                            : UPDATE_FOCUS_NORMAL,
+                    true /*updateInputWindows*/);
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
@@ -2076,7 +2099,7 @@
                 UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
         mService.mWindowPlacerLocked.performSurfacePlacement();
         if (focusChanged) {
-            mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+            getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/);
         }
     }
 
@@ -2448,7 +2471,7 @@
                     try {
                         // Note: this calls into ActivityManager, so we must *not* hold the window
                         // manager lock while calling this.
-                        mService.mActivityManager.setSplitScreenResizing(false);
+                        mService.mActivityTaskManager.setSplitScreenResizing(false);
                     } catch (RemoteException e) {
                         // Local call, shouldn't return RemoteException.
                         throw e.rethrowAsRuntimeException();
@@ -2486,8 +2509,8 @@
         return getWindowConfiguration().keepVisibleDeadAppWindowOnScreen();
     }
 
-    /** @return true if this window desires key events. */
-    boolean canReceiveKeys() {
+    @Override
+    public boolean canReceiveKeys() {
         return isVisibleOrAdding()
                 && (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
                 && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
@@ -2882,10 +2905,10 @@
             // All window frames that are fullscreen extend above status bar, but some don't extend
             // below navigation bar. Thus, check for display frame for top/left and stable frame for
             // bottom right.
-            if (win.mFrame.left <= win.mDisplayFrame.left
-                    && win.mFrame.top <= win.mDisplayFrame.top
-                    && win.mFrame.right >= win.mStableFrame.right
-                    && win.mFrame.bottom >= win.mStableFrame.bottom) {
+            if (win.getFrameLw().left <= win.getDisplayFrameLw().left
+                    && win.getFrameLw().top <= win.getDisplayFrameLw().top
+                    && win.getFrameLw().right >= win.getStableFrameLw().right
+                    && win.getFrameLw().bottom >= win.getStableFrameLw().bottom) {
                 // Is a fullscreen window, like the clock alarm. Show to everyone.
                 return false;
             }
@@ -2902,7 +2925,7 @@
     }
 
     void getTouchableRegion(Region outRegion) {
-        final Rect frame = mFrame;
+        final Rect frame = mWindowFrames.mFrame;
         switch (mTouchableInsets) {
             default:
             case TOUCHABLE_INSETS_FRAME:
@@ -2987,7 +3010,7 @@
             if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING)
                 Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
 
-            final Rect frame = mFrame;
+            final Rect frame = mWindowFrames.mFrame;
             final Rect overscanInsets = mLastOverscanInsets;
             final Rect contentInsets = mLastContentInsets;
             final Rect visibleInsets = mLastVisibleInsets;
@@ -2996,7 +3019,7 @@
             final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING;
             final boolean reportOrientation = mReportOrientationChanged;
             final int displayId = getDisplayId();
-            final DisplayCutout displayCutout = mDisplayCutout.getDisplayCutout();
+            final DisplayCutout displayCutout = getWmDisplayCutout().getDisplayCutout();
             if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
                     && mClient instanceof IWindow.Stub) {
                 // To prevent deadlock simulate one-way call if win.mClient is a local object.
@@ -3129,7 +3152,7 @@
             // Only windows with an AppWindowToken are letterboxed.
             return false;
         }
-        if (!mParentFrameWasClippedByDisplayCutout) {
+        if (!mWindowFrames.parentFrameWasClippedByDisplayCutout()) {
             // Cutout didn't make a difference, no letterbox
             return false;
         }
@@ -3152,7 +3175,7 @@
      */
     private boolean frameCoversEntireAppTokenBounds() {
         mTmpRect.set(mAppToken.getBounds());
-        mTmpRect.intersectUnchecked(mFrame);
+        mTmpRect.intersectUnchecked(mWindowFrames.mFrame);
         return mAppToken.getBounds().equals(mTmpRect);
     }
 
@@ -3254,10 +3277,7 @@
         proto.write(STACK_ID, getStackId());
         mAttrs.writeToProto(proto, ATTRIBUTES);
         mGivenContentInsets.writeToProto(proto, GIVEN_CONTENT_INSETS);
-        mFrame.writeToProto(proto, FRAME);
-        mContainingFrame.writeToProto(proto, CONTAINING_FRAME);
-        mParentFrame.writeToProto(proto, PARENT_FRAME);
-        mContentFrame.writeToProto(proto, CONTENT_FRAME);
+        mWindowFrames.writeToProto(proto, WINDOW_FRAMES);
         mContentInsets.writeToProto(proto, CONTENT_INSETS);
         mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
         mSurfacePosition.writeToProto(proto, SURFACE_POSITION);
@@ -3272,21 +3292,20 @@
         proto.write(SYSTEM_UI_VISIBILITY, mSystemUiVisibility);
         proto.write(HAS_SURFACE, mHasSurface);
         proto.write(IS_READY_FOR_DISPLAY, isReadyForDisplay());
-        mDisplayFrame.writeToProto(proto, DISPLAY_FRAME);
-        mOverscanFrame.writeToProto(proto, OVERSCAN_FRAME);
-        mVisibleFrame.writeToProto(proto, VISIBLE_FRAME);
-        mDecorFrame.writeToProto(proto, DECOR_FRAME);
-        mOutsetFrame.writeToProto(proto, OUTSET_FRAME);
         mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS);
         mVisibleInsets.writeToProto(proto, VISIBLE_INSETS);
         mStableInsets.writeToProto(proto, STABLE_INSETS);
         mOutsets.writeToProto(proto, OUTSETS);
-        mDisplayCutout.getDisplayCutout().writeToProto(proto, CUTOUT);
         proto.write(REMOVE_ON_EXIT, mRemoveOnExit);
         proto.write(DESTROYING, mDestroying);
         proto.write(REMOVED, mRemoved);
         proto.write(IS_ON_SCREEN, isOnScreen());
         proto.write(IS_VISIBLE, isVisible());
+        if (mForceSeamlesslyRotate) {
+            proto.write(PENDING_FORCED_SEAMLESS_ROTATION, mPendingForcedSeamlessRotate != null);
+            proto.write(FINISHED_FORCED_SEAMLESS_ROTATION_FRAME,
+                    mFinishForcedSeamlessRotateFrameNumber);
+        }
         proto.end(token);
     }
 
@@ -3398,30 +3417,12 @@
         pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
                 pw.print(" isReadyForDisplay()="); pw.print(isReadyForDisplay());
                 pw.print(" mWindowRemovalAllowed="); pw.println(mWindowRemovalAllowed);
-        if (dumpAll) {
-            pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
-                    pw.print(" last="); mLastFrame.printShortString(pw);
-                    pw.println();
-        }
         if (mEnforceSizeCompat) {
             pw.print(prefix); pw.print("mCompatFrame="); mCompatFrame.printShortString(pw);
                     pw.println();
         }
         if (dumpAll) {
-            pw.print(prefix); pw.print("Frames: containing=");
-                    mContainingFrame.printShortString(pw);
-                    pw.print(" parent="); mParentFrame.printShortString(pw);
-                    pw.println();
-            pw.print(prefix); pw.print("    display="); mDisplayFrame.printShortString(pw);
-                    pw.print(" overscan="); mOverscanFrame.printShortString(pw);
-                    pw.println();
-            pw.print(prefix); pw.print("    content="); mContentFrame.printShortString(pw);
-                    pw.print(" visible="); mVisibleFrame.printShortString(pw);
-                    pw.println();
-            pw.print(prefix); pw.print("    decor="); mDecorFrame.printShortString(pw);
-                    pw.println();
-            pw.print(prefix); pw.print("    outset="); mOutsetFrame.printShortString(pw);
-                    pw.println();
+            mWindowFrames.dump(pw, prefix);
             pw.print(prefix); pw.print("Cur insets: overscan=");
                     mOverscanInsets.printShortString(pw);
                     pw.print(" content="); mContentInsets.printShortString(pw);
@@ -3429,8 +3430,6 @@
                     pw.print(" stable="); mStableInsets.printShortString(pw);
                     pw.print(" surface="); mAttrs.surfaceInsets.printShortString(pw);
                     pw.print(" outsets="); mOutsets.printShortString(pw);
-            pw.print(" cutout=" + mDisplayCutout.getDisplayCutout());
-                    pw.println();
             pw.print(prefix); pw.print("Lst insets: overscan=");
                     mLastOverscanInsets.printShortString(pw);
                     pw.print(" content="); mLastContentInsets.printShortString(pw);
@@ -3438,7 +3437,6 @@
                     pw.print(" stable="); mLastStableInsets.printShortString(pw);
                     pw.print(" physical="); mLastOutsets.printShortString(pw);
                     pw.print(" outset="); mLastOutsets.printShortString(pw);
-                    pw.print(" cutout=" + mLastDisplayCutout);
                     pw.println();
         }
         super.dump(pw, prefix, dumpAll);
@@ -3463,6 +3461,16 @@
             pw.print(prefix); pw.print("mLastFreezeDuration=");
                     TimeUtils.formatDuration(mLastFreezeDuration, pw); pw.println();
         }
+        if (mForceSeamlesslyRotate) {
+            pw.print(prefix); pw.print("forceSeamlesslyRotate: pending=");
+            if (mPendingForcedSeamlessRotate != null) {
+                mPendingForcedSeamlessRotate.dump(pw);
+            } else {
+                pw.print("null");
+            }
+            pw.print(" finishedFrameNumber="); pw.print(mFinishForcedSeamlessRotateFrameNumber);
+            pw.println();
+        }
         if (mHScale != 1 || mVScale != 1) {
             pw.print(prefix); pw.print("mHScale="); pw.print(mHScale);
                     pw.print(" mVScale="); pw.println(mVScale);
@@ -3602,16 +3610,16 @@
         // Set mFrame
         Gravity.apply(mAttrs.gravity, w, h, containingFrame,
                 (int) (x + mAttrs.horizontalMargin * pw),
-                (int) (y + mAttrs.verticalMargin * ph), mFrame);
+                (int) (y + mAttrs.verticalMargin * ph), mWindowFrames.mFrame);
 
         // Now make sure the window fits in the overall display frame.
         if (fitToDisplay) {
-            Gravity.applyDisplay(mAttrs.gravity, displayFrame, mFrame);
+            Gravity.applyDisplay(mAttrs.gravity, displayFrame, mWindowFrames.mFrame);
         }
 
         // We need to make sure we update the CompatFrame as it is used for
         // cropping decisions, etc, on systems where we lack a decor layer.
-        mCompatFrame.set(mFrame);
+        mCompatFrame.set(mWindowFrames.mFrame);
         if (mEnforceSizeCompat) {
             // See comparable block in computeFrameLw.
             mCompatFrame.scale(mInvGlobalScale);
@@ -3724,7 +3732,7 @@
     }
 
     float translateToWindowX(float x) {
-        float winX = x - mFrame.left;
+        float winX = x - mWindowFrames.mFrame.left;
         if (mEnforceSizeCompat) {
             winX *= mGlobalScale;
         }
@@ -3732,7 +3740,7 @@
     }
 
     float translateToWindowY(float y) {
-        float winY = y - mFrame.top;
+        float winY = y - mWindowFrames.mFrame.top;
         if (mEnforceSizeCompat) {
             winY *= mGlobalScale;
         }
@@ -3884,7 +3892,8 @@
         final boolean isAccessibilityOverlay =
                 windowInfo.type == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
         if (TextUtils.isEmpty(windowInfo.title) && (isPanelWindow || isAccessibilityOverlay)) {
-            windowInfo.title = mAttrs.getTitle();
+            final CharSequence title = mAttrs.getTitle();
+            windowInfo.title = TextUtils.isEmpty(title) ? null : title;
         }
         windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
         windowInfo.focused = isFocused();
@@ -4296,7 +4305,7 @@
         // The decor frame is used to specify the region not covered by the system
         // decorations (nav bar, status bar). In case this is empty, for example with
         // FLAG_TRANSLUCENT_NAVIGATION, we don't need to do any cropping.
-        if (mDecorFrame.isEmpty()) {
+        if (mWindowFrames.mDecorFrame.isEmpty()) {
             return true;
         }
 
@@ -4341,12 +4350,12 @@
      * by system decorations.
      */
     private void calculateSystemDecorRect(Rect systemDecorRect) {
-        final Rect decorRect = mDecorFrame;
-        final int width = mFrame.width();
-        final int height = mFrame.height();
+        final Rect decorRect = mWindowFrames.mDecorFrame;
+        final int width = mWindowFrames.mFrame.width();
+        final int height = mWindowFrames.mFrame.height();
 
-        final int left = mFrame.left;
-        final int top = mFrame.top;
+        final int left = mWindowFrames.mFrame.left;
+        final int top = mWindowFrames.mFrame.top;
 
         // Initialize the decor rect to the entire frame.
         if (isDockedResizing()) {
@@ -4492,12 +4501,12 @@
         mLastVisibleInsets.set(mVisibleInsets);
         mLastStableInsets.set(mStableInsets);
         mLastOutsets.set(mOutsets);
-        mLastDisplayCutout = mDisplayCutout;
+        mWindowFrames.mLastDisplayCutout = mWindowFrames.mDisplayCutout;
     }
 
     void startAnimation(Animation anim) {
         final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
-        anim.initialize(mFrame.width(), mFrame.height(),
+        anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
                 displayInfo.appWidth, displayInfo.appHeight);
         anim.restrictDuration(MAX_ANIMATION_DURATION);
         anim.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
@@ -4512,7 +4521,8 @@
         if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this);
         final Point oldPosition = new Point();
         final Point newPosition = new Point();
-        transformFrameToSurfacePosition(mLastFrame.left, mLastFrame.top, oldPosition);
+        transformFrameToSurfacePosition(mWindowFrames.mLastFrame.left, mWindowFrames.mLastFrame.top,
+                oldPosition);
         transformFrameToSurfacePosition(left, top, newPosition);
         final AnimationAdapter adapter = new LocalAnimationAdapter(
                 new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y),
@@ -4547,8 +4557,8 @@
         final WindowContainer parent = getParent();
         if (isChildWindow()) {
             final WindowState parentWindow = getParentWindow();
-            x += parentWindow.mFrame.left - parentWindow.mAttrs.surfaceInsets.left;
-            y += parentWindow.mFrame.top - parentWindow.mAttrs.surfaceInsets.top;
+            x += parentWindow.mWindowFrames.mFrame.left - parentWindow.mAttrs.surfaceInsets.left;
+            y += parentWindow.mWindowFrames.mFrame.top - parentWindow.mAttrs.surfaceInsets.top;
         } else if (parent != null) {
             final Rect parentBounds = parent.getBounds();
             x += parentBounds.left;
@@ -4701,9 +4711,13 @@
             return;
         }
 
-        transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition);
+        transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
+                mSurfacePosition);
 
-        if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) {
+        // Freeze position while we're unrotated, so the surface remains at the position it was
+        // prior to the rotation.
+        if (!mSurfaceAnimator.hasLeash() && mPendingForcedSeamlessRotate == null &&
+                !mLastSurfacePosition.equals(mSurfacePosition)) {
             t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
             mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
             if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) {
@@ -4727,8 +4741,8 @@
             // Since the parent was outset by its surface insets, we need to undo the outsetting
             // with insetting by the same amount.
             final WindowState parent = getParentWindow();
-            outPoint.offset(-parent.mFrame.left + parent.mAttrs.surfaceInsets.left,
-                    -parent.mFrame.top + parent.mAttrs.surfaceInsets.top);
+            outPoint.offset(-parent.mWindowFrames.mFrame.left + parent.mAttrs.surfaceInsets.left,
+                    -parent.mWindowFrames.mFrame.top + parent.mAttrs.surfaceInsets.top);
         } else if (parentWindowContainer != null) {
             final Rect parentBounds = parentWindowContainer.getBounds();
             outPoint.offset(-parentBounds.left, -parentBounds.top);
@@ -4856,6 +4870,51 @@
         mFrameNumber = frameNumber;
     }
 
+    @Override
+    void seamlesslyRotate(Transaction t, int oldRotation, int newRotation) {
+        // Invisible windows, the wallpaper, and force seamlessly rotated windows do not participate
+        // in the regular seamless rotation animation.
+        if (!isVisibleNow() || mIsWallpaper || mForceSeamlesslyRotate) {
+            return;
+        }
+        final Matrix transform = mTmpMatrix;
+
+        mService.markForSeamlessRotation(this, true);
+
+        // We rotated the screen, but have not performed a new layout pass yet. In the mean time,
+        // we recompute the coordinates of mFrame in the new orientation, so the surface can be
+        // properly placed.
+        transformToRotation(oldRotation, newRotation, getDisplayInfo(), transform);
+        transformRect(transform, mWindowFrames.mFrame, null /* tmpRectF */);
+
+        updateSurfacePosition(t);
+        mWinAnimator.seamlesslyRotate(t, oldRotation, newRotation);
+
+        // Dispatch to children only after mFrame has been updated, as it's needed in the
+        // child's updateSurfacePosition.
+        super.seamlesslyRotate(t, oldRotation, newRotation);
+    }
+
+    public void getMaxVisibleBounds(Rect out) {
+        if (out.isEmpty()) {
+            out.set(mWindowFrames.mVisibleFrame);
+            return;
+        }
+
+        if (mWindowFrames.mVisibleFrame.left < out.left) {
+            out.left = mWindowFrames.mVisibleFrame.left;
+        }
+        if (mWindowFrames.mVisibleFrame.top < out.top) {
+            out.top = mWindowFrames.mVisibleFrame.top;
+        }
+        if (mWindowFrames.mVisibleFrame.right > out.right) {
+            out.right = mWindowFrames.mVisibleFrame.right;
+        }
+        if (mWindowFrames.mVisibleFrame.bottom > out.bottom) {
+            out.bottom = mWindowFrames.mVisibleFrame.bottom;
+        }
+    }
+
     private final class MoveAnimationSpec implements AnimationSpec {
 
         private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 561c9de..7966d5b 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -41,18 +41,18 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.logWithStack;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
 import static com.android.server.wm.WindowStateAnimatorProto.LAST_CLIP_RECT;
 import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
 import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
+import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
+import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
 
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.Region;
 import android.os.Debug;
 import android.os.Trace;
@@ -366,7 +366,8 @@
         mDrawState = READY_TO_SHOW;
         boolean result = false;
         final AppWindowToken atoken = mWin.mAppToken;
-        if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
+        if (atoken == null || atoken.canShowWindows()
+                || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
             result = mWin.performShowLocked();
         }
         return result;
@@ -539,13 +540,13 @@
         }
 
         if (WindowManagerService.localLOGV) Slog.v(TAG, "Got surface: " + mSurfaceController
-                + ", set left=" + w.mFrame.left + " top=" + w.mFrame.top
+                + ", set left=" + w.getFrameLw().left + " top=" + w.getFrameLw().top
                 + ", animLayer=" + mAnimLayer);
 
         if (SHOW_LIGHT_TRANSACTIONS) {
             Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
             WindowManagerService.logSurface(w, "CREATE pos=("
-                    + w.mFrame.left + "," + w.mFrame.top + ") ("
+                    + w.getFrameLw().left + "," + w.getFrameLw().top + ") ("
                     + width + "x" + height + "), layer=" + mAnimLayer + " HIDE", false);
         }
 
@@ -685,12 +686,16 @@
         final int displayId = mWin.getDisplayId();
         final ScreenRotationAnimation screenRotationAnimation =
                 mAnimator.getScreenRotationAnimationLocked(displayId);
-        final boolean screenAnimation =
-                screenRotationAnimation != null && screenRotationAnimation.isAnimating();
+        // TODO(b/111504081): Consolidate seamless rotation logic.
+        final boolean windowParticipatesInScreenRotationAnimation =
+                !mWin.mForceSeamlesslyRotate;
+        final boolean screenAnimation = screenRotationAnimation != null
+                && screenRotationAnimation.isAnimating()
+                && windowParticipatesInScreenRotationAnimation;
 
         if (screenAnimation) {
             // cache often used attributes locally
-            final Rect frame = mWin.mFrame;
+            final Rect frame = mWin.getFrameLw();
             final float tmpFloats[] = mService.mTmpFloats;
             final Matrix tmpMatrix = mWin.mTmpMatrix;
 
@@ -798,6 +803,13 @@
             return false;
         }
 
+        // During forced seamless rotation, the surface bounds get updated with the crop in the
+        // new rotation, which is not compatible with showing the surface in the old rotation.
+        // To work around that we disable cropping for such windows, as it is not necessary anyways.
+        if (w.mForceSeamlesslyRotate) {
+            return false;
+        }
+
         // If we're animating, the wallpaper should only
         // be updated at the end of the animation.
         if (w.mAttrs.type == TYPE_WALLPAPER) {
@@ -810,7 +822,7 @@
         w.calculatePolicyCrop(mSystemDecorRect);
 
         if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Applying decor to crop win=" + w + " mDecorFrame="
-                + w.mDecorFrame + " mSystemDecorRect=" + mSystemDecorRect);
+                + w.getDecorFrame() + " mSystemDecorRect=" + mSystemDecorRect);
 
         final Task task = w.getTask();
         final boolean fullscreen = w.fillsDisplay() || (task != null && task.isFullscreen());
@@ -930,9 +942,9 @@
 
             // Make sure that what we're animating to and from is actually the right size in case
             // the window cannot take up the full screen.
-            mTmpStackBounds.intersectUnchecked(w.mParentFrame);
-            mTmpSourceBounds.intersectUnchecked(w.mParentFrame);
-            mTmpAnimatingBounds.intersectUnchecked(w.mParentFrame);
+            mTmpStackBounds.intersectUnchecked(w.getParentFrame());
+            mTmpSourceBounds.intersectUnchecked(w.getParentFrame());
+            mTmpAnimatingBounds.intersectUnchecked(w.getParentFrame());
 
             if (!mTmpSourceBounds.isEmpty()) {
                 // Get the final target stack bounds, if we are not animating, this is just the
@@ -1497,40 +1509,16 @@
         }
     }
 
-    void seamlesslyRotateWindow(SurfaceControl.Transaction t,
-            int oldRotation, int newRotation) {
+    // TODO(b/111504081): Consolidate seamless rotation logic.
+    @Deprecated
+    void seamlesslyRotate(SurfaceControl.Transaction t, int oldRotation, int newRotation) {
         final WindowState w = mWin;
-        if (!w.isVisibleNow() || w.mIsWallpaper) {
-            return;
-        }
 
-        final Rect cropRect = mService.mTmpRect;
-        final Rect displayRect = mService.mTmpRect2;
-        final RectF frameRect = mService.mTmpRectF;
+        // We rotated the screen, but have not received a new buffer with the correct size yet. In
+        // the mean time, we rotate the buffer we have to the new orientation.
         final Matrix transform = mService.mTmpTransform;
-
-        final float x = w.mFrame.left;
-        final float y = w.mFrame.top;
-        final float width = w.mFrame.width();
-        final float height = w.mFrame.height();
-
-        mService.getDefaultDisplayContentLocked().getBounds(displayRect);
-        final float displayWidth = displayRect.width();
-        final float displayHeight = displayRect.height();
-
-        // Compute a transform matrix to undo the coordinate space transformation,
-        // and present the window at the same physical position it previously occupied.
-        final int deltaRotation = DisplayContent.deltaRotation(newRotation, oldRotation);
-        DisplayContent.createRotationMatrix(deltaRotation, x, y, displayWidth, displayHeight,
-                transform);
-
-        // We just need to apply a rotation matrix to the window. For example
-        // if we have a portrait window and rotate to landscape, the window is still portrait
-        // and now extends off the bottom of the screen (and only halfway across). Essentially we
-        // apply a transform to display the current buffer at it's old position
-        // (in the new coordinate space). We then freeze layer updates until the resize
-        // occurs, at which point we undo, them.
-        mService.markForSeamlessRotation(w, true);
+        transformToRotation(oldRotation, newRotation, w.getFrameLw().width(),
+                w.getFrameLw().height(), transform);
         transform.getValues(mService.mTmpFloats);
 
         float DsDx = mService.mTmpFloats[Matrix.MSCALE_X];
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index d247960..d6f9ac3 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -16,18 +16,11 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManagerInternal.APP_TRANSITION_SNAPSHOT;
-import static android.app.ActivityManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
-import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
-
-import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
@@ -39,12 +32,18 @@
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
 import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
-import static com.android.server.wm.AppTransition.isTaskTransit;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -571,7 +570,7 @@
             // animation after the old one finally finishes. It's better to defer the
             // app transition.
             if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() &&
-                    mService.rotationNeedsUpdateLocked()) {
+                    mService.getDefaultDisplayContentLocked().rotationNeedsUpdate()) {
                 if (DEBUG_APP_TRANSITIONS) {
                     Slog.v(TAG, "Delaying app transition for screen rotation animation to finish");
                 }
@@ -638,10 +637,15 @@
             return transit;
         }
 
-        // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper
         final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
-        final WindowState oldWallpaper = mWallpaperControllerLocked.isWallpaperTargetAnimating()
-                ? null : wallpaperTarget;
+        final boolean showWallpaper = wallpaperTarget != null
+                && (wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
+        // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
+        // don't consider upgrading to wallpaper transition.
+        final WindowState oldWallpaper =
+                (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper)
+                        ? null
+                        : wallpaperTarget;
         final ArraySet<AppWindowToken> openingApps = mService.mOpeningApps;
         final ArraySet<AppWindowToken> closingApps = mService.mClosingApps;
         final AppWindowToken topOpeningApp = getTopApp(mService.mOpeningApps,
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index b97460a..e411c0a 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -270,12 +270,6 @@
         dc.reParentWindowToken(this);
         mDisplayContent = dc;
 
-        // The rounded corner overlay should not be rotated. We ensure that by moving it outside
-        // the windowing layer.
-        if (mRoundedCornerOverlay) {
-            mDisplayContent.reparentToOverlay(mPendingTransaction, mSurfaceControl);
-        }
-
         // TODO(b/36740756): One day this should perhaps be hooked
         // up with goodToGo, so we don't move a window
         // to another display before the window behind
diff --git a/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java b/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java
index 09d7b5d..a2f37a5 100644
--- a/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java
+++ b/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java
@@ -22,7 +22,11 @@
 import static android.view.Surface.ROTATION_90;
 
 import android.annotation.Dimension;
+import android.annotation.Nullable;
 import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.DisplayInfo;
 import android.view.Surface.Rotation;
 
 public class CoordinateTransforms {
@@ -59,4 +63,93 @@
                 throw new IllegalArgumentException("Unknown rotation: " + rotation);
         }
     }
+
+    /**
+     * Sets a matrix such that given a rotation, it transforms that rotation's logical coordinates
+     * to physical coordinates.
+     *
+     * @param rotation the rotation to which the matrix should transform
+     * @param out      the matrix to be set
+     */
+    public static void transformLogicalToPhysicalCoordinates(@Rotation int rotation,
+            @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
+        switch (rotation) {
+            case ROTATION_0:
+                out.reset();
+                break;
+            case ROTATION_90:
+                out.setRotate(90);
+                out.preTranslate(0, -physicalWidth);
+                break;
+            case ROTATION_180:
+                out.setRotate(180);
+                out.preTranslate(-physicalWidth, -physicalHeight);
+                break;
+            case ROTATION_270:
+                out.setRotate(270);
+                out.preTranslate(-physicalHeight, 0);
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown rotation: " + rotation);
+        }
+    }
+
+    /**
+     * Sets a matrix such that given a two rotations, that it transforms coordinates given in the
+     * old rotation to coordinates that refer to the same physical location in the new rotation.
+     *
+     * @param oldRotation the rotation to transform from
+     * @param newRotation the rotation to transform to
+     * @param info the display info
+     * @param out a matrix that will be set to the transform
+     */
+    public static void transformToRotation(@Rotation int oldRotation,
+            @Rotation int newRotation, DisplayInfo info, Matrix out) {
+        final boolean flipped = info.rotation == ROTATION_90 || info.rotation == ROTATION_270;
+        final int h = flipped ? info.logicalWidth : info.logicalHeight;
+        final int w = flipped ? info.logicalHeight : info.logicalWidth;
+
+        final Matrix tmp = new Matrix();
+        transformLogicalToPhysicalCoordinates(oldRotation, w, h, out);
+        transformPhysicalToLogicalCoordinates(newRotation, w, h, tmp);
+        out.postConcat(tmp);
+    }
+
+    /**
+     * Sets a matrix such that given a two rotations, that it transforms coordinates given in the
+     * old rotation to coordinates that refer to the same physical location in the new rotation.
+     *
+     * @param oldRotation the rotation to transform from
+     * @param newRotation the rotation to transform to
+     * @param newWidth the width of the area to transform, in the new rotation
+     * @param newHeight the height of the area to transform, in the new rotation
+     * @param out a matrix that will be set to the transform
+     */
+    public static void transformToRotation(@Rotation int oldRotation,
+            @Rotation int newRotation, int newWidth, int newHeight, Matrix out) {
+        final boolean flipped = newRotation == ROTATION_90 || newRotation == ROTATION_270;
+        final int h = flipped ? newWidth : newHeight;
+        final int w = flipped ? newHeight : newWidth;
+
+        final Matrix tmp = new Matrix();
+        transformLogicalToPhysicalCoordinates(oldRotation, w, h, out);
+        transformPhysicalToLogicalCoordinates(newRotation, w, h, tmp);
+        out.postConcat(tmp);
+    }
+
+    /**
+     * Transforms a rect using a transformation matrix
+     *
+     * @param transform the transformation to apply to the rect
+     * @param inOutRect the rect to transform
+     * @param tmp a temporary value, if null the function will allocate its own.
+     */
+    public static void transformRect(Matrix transform, Rect inOutRect, @Nullable RectF tmp) {
+        if (tmp == null) {
+            tmp = new RectF();
+        }
+        tmp.set(inOutRect);
+        transform.mapRect(tmp);
+        inOutRect.set((int) tmp.left, (int) tmp.top, (int) tmp.right, (int) tmp.bottom);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/utils/InsetUtils.java b/services/core/java/com/android/server/wm/utils/InsetUtils.java
index b4a998a..c5b103f 100644
--- a/services/core/java/com/android/server/wm/utils/InsetUtils.java
+++ b/services/core/java/com/android/server/wm/utils/InsetUtils.java
@@ -16,8 +16,11 @@
 
 package com.android.server.wm.utils;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Rect;
 
+
 /**
  * Utility methods to handle insets represented as rects.
  */
@@ -35,4 +38,30 @@
         inOutInsets.right += insetsToAdd.right;
         inOutInsets.bottom += insetsToAdd.bottom;
     }
+
+    /**
+     * Calculates the insets from the {@code outerFrame} to the {@code innerFrame}.
+     *
+     * Note that if a side of the outer frame is not actually on the outside, the inset for that
+     * side will be clamped to zero.
+     *
+     * @param outerFrame the reference frame from which the insets are calculated
+     * @param innerFrame the inset frame, to which the insets are calculated,
+     *                   or null to clear the insets.
+     * @param outInsets is set to the result of the inset calculation.
+     */
+    public static void insetsBetweenFrames(@NonNull Rect outerFrame, @Nullable Rect innerFrame,
+            @NonNull Rect outInsets) {
+        if (innerFrame == null) {
+            outInsets.setEmpty();
+            return;
+        }
+        final int w = outerFrame.width();
+        final int h = outerFrame.height();
+        outInsets.set(
+                Math.min(w, Math.max(0, innerFrame.left - outerFrame.left)),
+                Math.min(h, Math.max(0, innerFrame.top - outerFrame.top)),
+                Math.min(w, Math.max(0, outerFrame.right - innerFrame.right)),
+                Math.min(h, Math.max(0, outerFrame.bottom - innerFrame.bottom)));
+    }
 }
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 89efe12..becde73 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -2,6 +2,8 @@
     name: "libservices.core",
     defaults: ["libservices.core-libs"],
 
+    cpp_std: "c++17",
+
     cflags: [
         "-Wall",
         "-Werror",
@@ -57,17 +59,6 @@
         "frameworks/native/services",
         "system/gatekeeper/include",
     ],
-
-    product_variables: {
-        arc: {
-            cflags: [
-                "-DUSE_ARC",
-            ],
-            srcs: [
-                "com_android_server_ArcVideoService.cpp",
-            ],
-        }
-    }
 }
 
 cc_defaults {
@@ -142,7 +133,9 @@
             shared_libs: [
                 "libarcbridge",
                 "libarcbridgeservice",
-                "libarcvideobridge",
+                "libarctimer",
+                "libbase",
+                "libcap",
                 "libchrome",
                 "libmojo",
             ],
diff --git a/services/core/jni/BroadcastRadio/NativeCallbackThread.cpp b/services/core/jni/BroadcastRadio/NativeCallbackThread.cpp
index 81d46f3..777d344 100644
--- a/services/core/jni/BroadcastRadio/NativeCallbackThread.cpp
+++ b/services/core/jni/BroadcastRadio/NativeCallbackThread.cpp
@@ -106,7 +106,7 @@
         mQueueCond.notify_one();
     }
 
-    if (mThread.get_id() == std::thread::id()) {
+    if (mThread.get_id() == std::this_thread::get_id()) {
         // you can't self-join a thread, but it's ok when calling from our sub-task
         ALOGD("About to stop native callback thread %p", this);
         mThread.detach();
diff --git a/services/core/jni/com_android_server_AlarmManagerService.cpp b/services/core/jni/com_android_server_AlarmManagerService.cpp
index 921eed9..e79612f 100644
--- a/services/core/jni/com_android_server_AlarmManagerService.cpp
+++ b/services/core/jni/com_android_server_AlarmManagerService.cpp
@@ -194,7 +194,9 @@
         uint32_t alarm_idx = events[i].data.u32;
         uint64_t unused;
         ssize_t err = read(fds[alarm_idx], &unused, sizeof(unused));
-        if (err < 0) {
+        // Worth evaluating even if read fails with EAGAIN, since epoll_wait
+        // returned. (see b/78560047#comment34)
+        if (err < 0 && errno != EAGAIN) {
             if (alarm_idx == ANDROID_ALARM_TYPE_COUNT && errno == ECANCELED) {
                 result |= ANDROID_ALARM_TIME_CHANGE_MASK;
             } else {
@@ -348,7 +350,7 @@
     }
 
     for (size_t i = 0; i < fds.size(); i++) {
-        fds[i] = timerfd_create(android_alarm_to_clockid[i], 0);
+        fds[i] = timerfd_create(android_alarm_to_clockid[i], TFD_NONBLOCK);
         if (fds[i] < 0) {
             log_timerfd_create_error(android_alarm_to_clockid[i]);
             close(epollfd);
diff --git a/services/core/jni/com_android_server_ArcVideoService.cpp b/services/core/jni/com_android_server_ArcVideoService.cpp
deleted file mode 100644
index f93cd90..0000000
--- a/services/core/jni/com_android_server_ArcVideoService.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright 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.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "ArcVideoService"
-
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <media/arcvideobridge/IArcVideoBridge.h>
-#include <utils/Log.h>
-
-#include <base/bind.h>
-#include <base/bind_helpers.h>
-#include <mojo/edk/embedder/embedder.h>
-#include <mojo/public/cpp/bindings/binding.h>
-
-#include <arc/ArcBridgeSupport.h>
-#include <arc/ArcService.h>
-#include <arc/Future.h>
-#include <arc/IArcBridgeService.h>
-#include <arc/MojoProcessSupport.h>
-#include <components/arc/common/video.mojom.h>
-
-namespace {
-
-// [MinVersion] of OnVideoInstanceReady method in arc_bridge.mojom.
-constexpr int kMinimumArcBridgeHostVersion = 6;
-
-void onCaptureResult(arc::Future<arc::MojoBootstrapResult>* future, uint32_t version,
-                     mojo::ScopedHandle handle, const std::string& token) {
-    mojo::edk::ScopedPlatformHandle scoped_platform_handle;
-    MojoResult result =
-            mojo::edk::PassWrappedPlatformHandle(handle.release().value(), &scoped_platform_handle);
-    if (result != MOJO_RESULT_OK) {
-        ALOGE("Received invalid file descriptor.");
-        future->set(arc::MojoBootstrapResult());
-        return;
-    }
-
-    base::ScopedFD fd(scoped_platform_handle.release().handle);
-    future->set(arc::MojoBootstrapResult(std::move(fd), token, version));
-}
-
-}  // namespace
-
-namespace arc {
-
-class VideoService : public mojom::VideoInstance,
-                     public ArcService,
-                     public android::BnArcVideoBridge {
-public:
-    explicit VideoService(MojoProcessSupport* mojoProcessSupport)
-          : mMojoProcessSupport(mojoProcessSupport), mBinding(this) {
-        mMojoProcessSupport->arc_bridge_support().requestArcBridgeProxyAsync(
-                this, kMinimumArcBridgeHostVersion);
-    }
-
-    ~VideoService() override { mMojoProcessSupport->disconnect(&mBinding, &mHostPtr); }
-
-    // VideoInstance overrides:
-    void InitDeprecated(mojom::VideoHostPtr hostPtr) override {
-        Init(std::move(hostPtr), base::Bind(&base::DoNothing));
-    }
-
-    void Init(mojom::VideoHostPtr hostPtr, const InitCallback& callback) override {
-        ALOGV("Init");
-        mHostPtr = std::move(hostPtr);
-        // A method must be called while we are still in a Mojo thread so the
-        // proxy can perform lazy initialization and be able to be called from
-        // non-Mojo threads later.
-        // This also caches the version number so it can be obtained by calling
-        // .version().
-        mHostPtr.QueryVersion(base::Bind(
-            [](const InitCallback& callback, uint32_t version) {
-                ALOGI("VideoService ready (version=%d)", version);
-                callback.Run();
-            },
-            callback));
-        ALOGV("Init done");
-    }
-
-    // ArcService overrides:
-    void ready(mojom::ArcBridgeHostPtr* bridgeHost) override {
-        (*bridgeHost)->OnVideoInstanceReady(mBinding.CreateInterfacePtrAndBind());
-    }
-
-    void versionMismatch(uint32_t version) override {
-        ALOGE("ArcBridgeHost version %d, does not support video (version %d)\n", version,
-              kMinimumArcBridgeHostVersion);
-    }
-
-    // BnArcVideoBridge overrides:
-    MojoBootstrapResult bootstrapVideoAcceleratorFactory() override {
-        ALOGV("VideoService::bootstrapVideoAcceleratorFactory");
-
-        Future<MojoBootstrapResult> future;
-        mMojoProcessSupport->mojo_thread().getTaskRunner()->PostTask(
-                FROM_HERE, base::Bind(&VideoService::bootstrapVideoAcceleratorFactoryOnMojoThread,
-                                      base::Unretained(this), &future));
-        return future.get();
-    }
-
-    int32_t hostVersion() override {
-        ALOGV("VideoService::hostVersion");
-        return mHostPtr.version();
-    }
-
-private:
-    void bootstrapVideoAcceleratorFactoryOnMojoThread(Future<MojoBootstrapResult>* future) {
-        if (!mHostPtr) {
-            ALOGE("mHostPtr is not ready yet");
-            future->set(MojoBootstrapResult());
-            return;
-        }
-        mHostPtr->OnBootstrapVideoAcceleratorFactory(
-                base::Bind(&onCaptureResult, base::Unretained(future), mHostPtr.version()));
-    }
-
-    // Outlives VideoService.
-    MojoProcessSupport* const mMojoProcessSupport;
-    mojo::Binding<mojom::VideoInstance> mBinding;
-    mojom::VideoHostPtr mHostPtr;
-};
-
-}  // namespace arc
-
-namespace android {
-
-int register_android_server_ArcVideoService() {
-    defaultServiceManager()->addService(
-            String16("android.os.IArcVideoBridge"),
-            new arc::VideoService(arc::MojoProcessSupport::getLeakyInstance()));
-    return 0;
-}
-
-}  // namespace android
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 52f2d67..f5f19f6 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -210,7 +210,7 @@
             const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
     status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
 
-    void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray);
+    void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int displayId);
     void setFocusedApplication(JNIEnv* env, jobject applicationHandleObj);
     void setInputDispatchMode(bool enabled, bool frozen);
     void setSystemUiVisibility(int32_t visibility);
@@ -736,7 +736,8 @@
     }
 }
 
-void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) {
+void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray,
+         int displayId) {
     Vector<sp<InputWindowHandle> > windowHandles;
 
     if (windowHandleObjArray) {
@@ -756,7 +757,7 @@
         }
     }
 
-    mInputManager->getDispatcher()->setInputWindows(windowHandles);
+    mInputManager->getDispatcher()->setInputWindows(windowHandles, displayId);
 
     // Do this after the dispatcher has updated the window handle state.
     bool newPointerGesturesEnabled = true;
@@ -1446,10 +1447,10 @@
 }
 
 static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */,
-        jlong ptr, jobjectArray windowHandleObjArray) {
+        jlong ptr, jobjectArray windowHandleObjArray, jint displayId) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
-    im->setInputWindows(env, windowHandleObjArray);
+    im->setInputWindows(env, windowHandleObjArray, displayId);
 }
 
 static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */,
@@ -1678,7 +1679,7 @@
             (void*) nativeInjectInputEvent },
     { "nativeToggleCapsLock", "(JI)V",
             (void*) nativeToggleCapsLock },
-    { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;)V",
+    { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;I)V",
             (void*) nativeSetInputWindows },
     { "nativeSetFocusedApplication", "(JLcom/android/server/input/InputApplicationHandle;)V",
             (void*) nativeSetFocusedApplication },
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 0ebef37..bb6e684 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -54,9 +54,6 @@
 int register_android_server_GraphicsStatsService(JNIEnv* env);
 int register_android_hardware_display_DisplayViewport(JNIEnv* env);
 int register_android_server_net_NetworkStatsService(JNIEnv* env);
-#ifdef USE_ARC
-int register_android_server_ArcVideoService();
-#endif
 };
 
 using namespace android;
@@ -104,8 +101,5 @@
     register_android_server_GraphicsStatsService(env);
     register_android_hardware_display_DisplayViewport(env);
     register_android_server_net_NetworkStatsService(env);
-#ifdef USE_ARC
-    register_android_server_ArcVideoService();
-#endif
     return JNI_VERSION_1_4;
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 1c9782f..84de6b4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -152,6 +152,11 @@
     }
 
     @Override
+    public long forceNetworkLogs() {
+        return 0;
+    }
+
+    @Override
     public long forceSecurityLogs() {
         return 0;
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 19ee42f..81ac6a4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -90,10 +90,12 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.ActivityTaskManager;
 import android.app.ActivityThread;
 import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
 import android.app.Notification;
@@ -231,6 +233,7 @@
 import com.android.server.pm.UserRestrictionsUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 
+import com.android.server.uri.UriGrantsManagerInternal;
 import com.google.android.collect.Sets;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -1865,6 +1868,10 @@
             return ActivityManager.getService();
         }
 
+        IActivityTaskManager getIActivityTaskManager() {
+            return ActivityTaskManager.getService();
+        }
+
         ActivityManagerInternal getActivityManagerInternal() {
             return LocalServices.getService(ActivityManagerInternal.class);
         }
@@ -3286,8 +3293,7 @@
     private void updateLockTaskFeaturesLocked(int flags, int userId) {
         long ident = mInjector.binderClearCallingIdentity();
         try {
-            mInjector.getIActivityManager()
-                    .updateLockTaskFeatures(userId, flags);
+            mInjector.getIActivityTaskManager().updateLockTaskFeatures(userId, flags);
         } catch (RemoteException e) {
             // Not gonna happen.
         } finally {
@@ -5190,6 +5196,12 @@
         }
     }
 
+    private void enforceDeviceOwner(ComponentName who) {
+        synchronized (getLockObject()) {
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        }
+    }
+
     private void enforceProfileOrDeviceOwner(ComponentName who) {
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -5364,9 +5376,7 @@
             }
         } else {
             // Caller provided - check it is the device owner.
-            synchronized (getLockObject()) {
-                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-            }
+            enforceDeviceOwner(who);
         }
     }
 
@@ -5456,7 +5466,14 @@
                 if (generationResult != KeyChain.KEY_GEN_SUCCESS) {
                     Log.e(LOG_TAG, String.format(
                             "KeyChain failed to generate a keypair, error %d.", generationResult));
-                    return false;
+                    switch (generationResult) {
+                        case KeyChain.KEY_GEN_STRONGBOX_UNAVAILABLE:
+                            throw new ServiceSpecificException(
+                                    DevicePolicyManager.KEY_GEN_STRONGBOX_UNAVAILABLE,
+                                    String.format("KeyChain error: %d", generationResult));
+                        default:
+                            return false;
+                    }
                 }
 
                 // Set a grant for the caller here so that when the client calls
@@ -6250,7 +6267,7 @@
     }
 
     @Override
-    public void reportFailedFingerprintAttempt(int userHandle) {
+    public void reportFailedBiometricAttempt(int userHandle) {
         enforceFullCrossUsersPermission(userHandle);
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -6261,7 +6278,7 @@
     }
 
     @Override
-    public void reportSuccessfulFingerprintAttempt(int userHandle) {
+    public void reportSuccessfulBiometricAttempt(int userHandle) {
         enforceFullCrossUsersPermission(userHandle);
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -6374,9 +6391,7 @@
 
     @Override
     public void setRecommendedGlobalProxy(ComponentName who, ProxyInfo proxyInfo) {
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(who);
         long token = mInjector.binderClearCallingIdentity();
         try {
             ConnectivityManager connectivityManager = (ConnectivityManager)
@@ -6959,7 +6974,7 @@
                 intent.putExtra(DeviceAdminReceiver.EXTRA_BUGREPORT_HASH, bugreportHash);
                 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
-                LocalServices.getService(ActivityManagerInternal.class)
+                LocalServices.getService(UriGrantsManagerInternal.class)
                         .grantUriPermissionFromIntent(Process.SHELL_UID,
                                 mOwners.getDeviceOwnerComponent().getPackageName(),
                                 intent, mOwners.getDeviceOwnerUserId());
@@ -7729,11 +7744,10 @@
     @Override
     public void setProfileName(ComponentName who, String profileName) {
         Preconditions.checkNotNull(who, "ComponentName is null");
-        int userId = UserHandle.getCallingUserId();
-        // Check if this is the profile owner (includes device owner).
-        getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        enforceProfileOrDeviceOwner(who);
 
-        long id = mInjector.binderClearCallingIdentity();
+        final int userId = UserHandle.getCallingUserId();
+        final long id = mInjector.binderClearCallingIdentity();
         try {
             mUserManager.setUserName(userId, profileName);
         } finally {
@@ -8198,9 +8212,7 @@
     @Override
     public void setDefaultSmsApplication(ComponentName admin, String packageName) {
         Preconditions.checkNotNull(admin, "ComponentName is null");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(admin);
         mInjector.binderWithCleanCallingIdentity(() ->
                 SmsApplication.setDefaultApplication(packageName, mContext));
     }
@@ -8987,10 +8999,7 @@
     public boolean removeUser(ComponentName who, UserHandle userHandle) {
         Preconditions.checkNotNull(who, "ComponentName is null");
         Preconditions.checkNotNull(userHandle, "UserHandle is null");
-
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(who);
 
         final int callingUserId = mInjector.userHandleGetCallingUserId();
         final long id = mInjector.binderClearCallingIdentity();
@@ -9050,10 +9059,7 @@
     public int startUserInBackground(ComponentName who, UserHandle userHandle) {
         Preconditions.checkNotNull(who, "ComponentName is null");
         Preconditions.checkNotNull(userHandle, "UserHandle is null");
-
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(who);
 
         final int userId = userHandle.getIdentifier();
         if (isManagedProfile(userId)) {
@@ -9085,10 +9091,7 @@
     public int stopUser(ComponentName who, UserHandle userHandle) {
         Preconditions.checkNotNull(who, "ComponentName is null");
         Preconditions.checkNotNull(userHandle, "UserHandle is null");
-
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(who);
 
         final int userId = userHandle.getIdentifier();
         if (isManagedProfile(userId)) {
@@ -9156,9 +9159,7 @@
     @Override
     public List<UserHandle> getSecondaryUsers(ComponentName who) {
         Preconditions.checkNotNull(who, "ComponentName is null");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(who);
 
         final long id = mInjector.binderClearCallingIdentity();
         try {
@@ -9180,9 +9181,7 @@
     @Override
     public boolean isEphemeralUser(ComponentName who) {
         Preconditions.checkNotNull(who, "ComponentName is null");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-        }
+        enforceProfileOrDeviceOwner(who);
 
         final int callingUserId = mInjector.userHandleGetCallingUserId();
         final long id = mInjector.binderClearCallingIdentity();
@@ -10001,7 +10000,7 @@
     @Override
     public boolean setTime(ComponentName who, long millis) {
         Preconditions.checkNotNull(who, "ComponentName is null in setTime");
-        getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        enforceDeviceOwner(who);
         // Don't allow set time when auto time is on.
         if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
             return false;
@@ -10013,7 +10012,7 @@
     @Override
     public boolean setTimeZone(ComponentName who, String timeZone) {
         Preconditions.checkNotNull(who, "ComponentName is null in setTimeZone");
-        getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        enforceDeviceOwner(who);
         // Don't allow set timezone when auto timezone is on.
         if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
             return false;
@@ -10169,7 +10168,7 @@
             if (policy.mStatusBarDisabled != disabled) {
                 boolean isLockTaskMode = false;
                 try {
-                    isLockTaskMode = mInjector.getIActivityManager().getLockTaskModeState()
+                    isLockTaskMode = mInjector.getIActivityTaskManager().getLockTaskModeState()
                             != LOCK_TASK_MODE_NONE;
                 } catch (RemoteException e) {
                     Slog.e(LOG_TAG, "Failed to get LockTask mode");
@@ -10703,7 +10702,6 @@
                     return false;
                 }
         }
-
         return false;
     }
 
@@ -11114,9 +11112,7 @@
     @Override
     public String getWifiMacAddress(ComponentName admin) {
         // Make sure caller has DO.
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(admin);
 
         final long ident = mInjector.binderClearCallingIdentity();
         try {
@@ -11154,9 +11150,7 @@
 
     @Override
     public boolean isSystemOnlyUser(ComponentName admin) {
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(admin);
         final int callingUserId = mInjector.userHandleGetCallingUserId();
         return UserManager.isSplitSystemUser() && callingUserId == UserHandle.USER_SYSTEM;
     }
@@ -11165,9 +11159,7 @@
     public void reboot(ComponentName admin) {
         Preconditions.checkNotNull(admin);
         // Make sure caller has DO.
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(admin);
         long ident = mInjector.binderClearCallingIdentity();
         try {
             // Make sure there are no ongoing calls on the device.
@@ -11986,9 +11978,7 @@
             return false;
         }
         Preconditions.checkNotNull(admin);
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(admin);
 
         final int callingUid = mInjector.binderGetCallingUid();
         final AtomicBoolean success = new AtomicBoolean(false);
@@ -12299,6 +12289,20 @@
         }
     }
 
+    @Override
+    public long forceNetworkLogs() {
+        enforceShell("forceNetworkLogs");
+        synchronized (getLockObject()) {
+            if (!isNetworkLoggingEnabledInternalLocked()) {
+                throw new IllegalStateException("logging is not available");
+            }
+            if (mNetworkLogger != null) {
+                return mNetworkLogger.forceBatchFinalization();
+            }
+            return 0;
+        }
+    }
+
     /** Pauses security and network logging if there are unaffiliated users on the device */
     private void maybePauseDeviceWideLoggingLocked() {
         if (!areAllUsersAffiliatedWithDeviceLocked()) {
@@ -12601,9 +12605,7 @@
         Preconditions.checkNotNull(admin, "ComponentName is null");
         Preconditions.checkNotNull(packageName, "packageName is null");
         Preconditions.checkNotNull(callback, "callback is null");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-        }
+        enforceProfileOrDeviceOwner(admin);
         final int userId = UserHandle.getCallingUserId();
 
         long ident = mInjector.binderClearCallingIdentity();
@@ -12911,9 +12913,7 @@
         }
         Preconditions.checkNotNull(who, "ComponentName is null in addOverrideApn");
         Preconditions.checkNotNull(apnSetting, "ApnSetting is null in addOverrideApn");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(who);
 
         int operatedId = -1;
         Uri resultUri;
@@ -12942,9 +12942,7 @@
         }
         Preconditions.checkNotNull(who, "ComponentName is null in updateOverrideApn");
         Preconditions.checkNotNull(apnSetting, "ApnSetting is null in updateOverrideApn");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(who);
 
         if (apnId < 0) {
             return false;
@@ -12965,9 +12963,7 @@
             return false;
         }
         Preconditions.checkNotNull(who, "ComponentName is null in removeOverrideApn");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(who);
 
         return removeOverrideApnUnchecked(apnId);
     }
@@ -12993,9 +12989,7 @@
             return Collections.emptyList();
         }
         Preconditions.checkNotNull(who, "ComponentName is null in getOverrideApns");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(who);
 
         return getOverrideApnsUnchecked();
     }
@@ -13031,9 +13025,7 @@
             return;
         }
         Preconditions.checkNotNull(who, "ComponentName is null in setOverrideApnEnabled");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(who);
 
         setOverrideApnsEnabledUnchecked(enabled);
     }
@@ -13056,9 +13048,7 @@
             return false;
         }
         Preconditions.checkNotNull(who, "ComponentName is null in isOverrideApnEnabled");
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
+        enforceDeviceOwner(who);
 
         Cursor enforceCursor;
         final long id = mInjector.binderClearCallingIdentity();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index 0967652..4514492 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -187,4 +187,8 @@
     List<NetworkEvent> retrieveLogs(long batchToken) {
         return mNetworkLoggingHandler.retrieveFullLogBatch(batchToken);
     }
+
+    long forceBatchFinalization() {
+        return mNetworkLoggingHandler.forceBatchFinalization();
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
index f91f959..0a7070f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
@@ -16,6 +16,8 @@
 
 package com.android.server.devicepolicy;
 
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
 import android.app.AlarmManager;
 import android.app.AlarmManager.OnAlarmListener;
 import android.app.admin.DeviceAdminReceiver;
@@ -33,6 +35,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A Handler class for managing network logging on a background thread.
@@ -60,6 +63,12 @@
     /** Delay after which older batches get discarded after a retrieval. */
     private static final long RETRIEVED_BATCH_DISCARD_DELAY_MS = 5 * 60 * 1000; // 5m
 
+    /** Throttle batch finalization to 10 seconds.*/
+    private static final long FORCE_FETCH_THROTTLE_NS = TimeUnit.SECONDS.toNanos(10);
+    /** Timestamp of the last call to finalise a batch. Used for throttling forced finalization.*/
+    @GuardedBy("this")
+    private long mLastFinalizationNanos = -1;
+
     /** Do not call into mDpm with locks held */
     private final DevicePolicyManagerService mDpm;
     private final AlarmManager mAlarmManager;
@@ -155,6 +164,26 @@
                 + "ms from now.");
     }
 
+    /**
+     * Forces batch finalisation. Throttled to 10 seconds per batch finalisation.
+     * @return the number of milliseconds to wait until batch finalisation can be forced.
+     */
+    long forceBatchFinalization() {
+        Bundle notificationExtras;
+        synchronized (this) {
+            final long toWaitNanos =
+                mLastFinalizationNanos + FORCE_FETCH_THROTTLE_NS - System.nanoTime();
+            if (toWaitNanos > 0) {
+                return NANOSECONDS.toMillis(toWaitNanos) + 1; // Round up.
+            }
+            notificationExtras = finalizeBatchAndBuildDeviceOwnerMessageLocked();
+        }
+        if (notificationExtras != null) {
+            notifyDeviceOwner(notificationExtras);
+        }
+        return 0;
+    }
+
     synchronized void pause() {
         Slog.d(TAG, "Paused network logging");
         mPaused = true;
@@ -192,6 +221,7 @@
     @GuardedBy("this")
     /** @returns extras if a message should be sent to the device owner */
     private Bundle finalizeBatchAndBuildDeviceOwnerMessageLocked() {
+        mLastFinalizationNanos = System.nanoTime();
         Bundle notificationExtras = null;
         if (mNetworkEvents.size() > 0) {
             // Assign ids to the events.
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index dc6a613..b6f3d3b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -63,6 +63,7 @@
 import com.android.internal.widget.ILockSettings;
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.am.ActivityManagerService;
+import com.android.server.am.ActivityTaskManagerService;
 import com.android.server.audio.AudioService;
 import com.android.server.broadcastradio.BroadcastRadioService;
 import com.android.server.camera.CameraServiceProxy;
@@ -74,7 +75,8 @@
 import com.android.server.display.DisplayManagerService;
 import com.android.server.dreams.DreamManagerService;
 import com.android.server.emergency.EmergencyAffordanceService;
-import com.android.server.fingerprint.FingerprintService;
+import com.android.server.biometrics.face.FaceService;
+import com.android.server.biometrics.fingerprint.FingerprintService;
 import com.android.server.hdmi.HdmiControlService;
 import com.android.server.input.InputManagerService;
 import com.android.server.job.JobSchedulerService;
@@ -116,6 +118,7 @@
 import com.android.server.tv.TvInputManagerService;
 import com.android.server.tv.TvRemoteService;
 import com.android.server.twilight.TwilightService;
+import com.android.server.uri.UriGrantsManagerService;
 import com.android.server.usage.UsageStatsService;
 import com.android.server.vr.VrManagerService;
 import com.android.server.webkit.WebViewUpdateService;
@@ -231,6 +234,10 @@
             "com.android.internal.car.CarServiceHelperService";
     private static final String TIME_DETECTOR_SERVICE_CLASS =
             "com.android.server.timedetector.TimeDetectorService$Lifecycle";
+    private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS =
+            "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
+    private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS =
+            "com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
 
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
 
@@ -556,10 +563,18 @@
         mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class);
         traceEnd();
 
+        // Uri Grants Manager.
+        traceBeginAndSlog("UriGrantsManagerService");
+        mSystemServiceManager.startService(UriGrantsManagerService.Lifecycle.class);
+        traceEnd();
+
         // Activity manager runs the show.
         traceBeginAndSlog("StartActivityManager");
-        mActivityManagerService = mSystemServiceManager.startService(
-                ActivityManagerService.Lifecycle.class).getService();
+        // TODO: Might need to move after migration to WM.
+        ActivityTaskManagerService atm = mSystemServiceManager.startService(
+                ActivityTaskManagerService.Lifecycle.class).getService();
+        mActivityManagerService = ActivityManagerService.Lifecycle.startService(
+                mSystemServiceManager, atm);
         mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
         mActivityManagerService.setInstaller(installer);
         traceEnd();
@@ -715,7 +730,7 @@
 
         // Tracks cpu time spent in binder calls
         traceBeginAndSlog("StartBinderCallsStatsService");
-        BinderCallsStatsService.start();
+        mSystemServiceManager.startService(BinderCallsStatsService.LifeCycle.class);
         traceEnd();
     }
 
@@ -894,7 +909,7 @@
             }
 
             traceBeginAndSlog("StartInputManager");
-            inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
+            inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
             inputManager.start();
             traceEnd();
 
@@ -950,8 +965,7 @@
 
             traceBeginAndSlog("StartAccessibilityManagerService");
             try {
-                ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
-                        new AccessibilityManagerService(context));
+                mSystemServiceManager.startService(ACCESSIBILITY_MANAGER_SERVICE_CLASS);
             } catch (Throwable e) {
                 reportWtf("starting Accessibility Manager", e);
             }
@@ -1238,13 +1252,24 @@
             }
             traceEnd();
 
-            traceBeginAndSlog("StartTimeDetectorService");
-            try {
-                mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS);
-            } catch (Throwable e) {
-                reportWtf("starting StartTimeDetectorService service", e);
+            final boolean useNewTimeServices = true;
+            if (useNewTimeServices) {
+                traceBeginAndSlog("StartTimeDetectorService");
+                try {
+                    mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS);
+                } catch (Throwable e) {
+                    reportWtf("starting StartTimeDetectorService service", e);
+                }
+                traceEnd();
+
+                traceBeginAndSlog("StartTimeZoneDetectorService");
+                try {
+                    mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS);
+                } catch (Throwable e) {
+                    reportWtf("starting StartTimeZoneDetectorService service", e);
+                }
+                traceEnd();
             }
-            traceEnd();
 
             if (!isWatch) {
                 traceBeginAndSlog("StartSearchManagerService");
@@ -1411,7 +1436,12 @@
             if (!isWatch) {
                 traceBeginAndSlog("StartNetworkTimeUpdateService");
                 try {
-                    networkTimeUpdater = new NetworkTimeUpdateService(context);
+                    if (useNewTimeServices) {
+                        networkTimeUpdater = new NewNetworkTimeUpdateService(context);
+                    } else {
+                        networkTimeUpdater = new OldNetworkTimeUpdateService(context);
+                    }
+                    Slog.d(TAG, "Using networkTimeUpdater class=" + networkTimeUpdater.getClass());
                     ServiceManager.addService("network_time_update_service", networkTimeUpdater);
                 } catch (Throwable e) {
                     reportWtf("starting NetworkTimeUpdate service", e);
@@ -1508,6 +1538,12 @@
             }
             traceEnd();
 
+            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+                traceBeginAndSlog("StartFaceSensor");
+                mSystemServiceManager.startService(FaceService.class);
+                traceEnd();
+            }
+
             if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
                 traceBeginAndSlog("StartFingerprintSensor");
                 mSystemServiceManager.startService(FingerprintService.class);
@@ -1522,13 +1558,15 @@
             }
             traceEnd();
 
-            traceBeginAndSlog("StartPruneInstantAppsJobService");
-            try {
-                PruneInstantAppsJobService.schedule(context);
-            } catch (Throwable e) {
-                reportWtf("StartPruneInstantAppsJobService", e);
+            if (!isWatch) {
+                traceBeginAndSlog("StartPruneInstantAppsJobService");
+                try {
+                    PruneInstantAppsJobService.schedule(context);
+                } catch (Throwable e) {
+                    reportWtf("StartPruneInstantAppsJobService", e);
+                }
+                traceEnd();
             }
-            traceEnd();
 
             // LauncherAppsService uses ShortcutService.
             traceBeginAndSlog("StartShortcutServiceLifecycle");
diff --git a/services/net/OWNERS b/services/net/OWNERS
index ce50558..7311eee 100644
--- a/services/net/OWNERS
+++ b/services/net/OWNERS
@@ -1,6 +1,8 @@
 set noparent
 
+codewiz@google.com
 ek@google.com
 jchalard@google.com
 lorenzo@google.com
+reminv@google.com
 satk@google.com
diff --git a/services/net/java/android/net/netlink/NetlinkSocket.java b/services/net/java/android/net/netlink/NetlinkSocket.java
index 5af3c29..cfcba3a 100644
--- a/services/net/java/android/net/netlink/NetlinkSocket.java
+++ b/services/net/java/android/net/netlink/NetlinkSocket.java
@@ -59,10 +59,9 @@
         final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage";
         final long IO_TIMEOUT = 300L;
 
-        FileDescriptor fd;
+        final FileDescriptor fd = forProto(nlProto);
 
         try {
-            fd = forProto(nlProto);
             connectToKernel(fd);
             sendMessage(fd, msg, 0, msg.length, IO_TIMEOUT);
             final ByteBuffer bytes = recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT);
@@ -96,9 +95,9 @@
         } catch (SocketException e) {
             Log.e(TAG, errPrefix, e);
             throw new ErrnoException(errPrefix, EIO, e);
+        } finally {
+            IoUtils.closeQuietly(fd);
         }
-
-        IoUtils.closeQuietly(fd);
     }
 
     public static FileDescriptor forProto(int nlProto) throws ErrnoException {
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index c1c32c2..774a3bc 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -708,8 +708,10 @@
         @Override
         public void onServiceDisconnected(ComponentName name) {
             synchronized (mLock) {
-                clearClientLocked();
-                mRemoteInstance = null;
+                if (mRemoteInstance != null) {
+                    clearClientLocked();
+                    mRemoteInstance = null;
+                }
             }
         }
     }
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 4fbc14c..e8266a5 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -243,7 +243,7 @@
             intent.setData(Uri.fromParts("printjob", printJob.getId().flattenToString(), null));
             intent.putExtra(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER, adapter.asBinder());
             intent.putExtra(PrintManager.EXTRA_PRINT_JOB, printJob);
-            intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, packageName);
+            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
 
             IntentSender intentSender = PendingIntent.getActivityAsUser(
                     mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
diff --git a/services/robotests/src/android/app/backup/BackupUtilsTest.java b/services/robotests/src/android/app/backup/BackupUtilsTest.java
new file mode 100644
index 0000000..04a2a14
--- /dev/null
+++ b/services/robotests/src/android/app/backup/BackupUtilsTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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.app.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"android.app.backup"})
+@Presubmit
+@DoNotInstrument
+public class BackupUtilsTest {
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = RuntimeEnvironment.application;
+    }
+
+    @Test
+    public void testIsFileSpecifiedInPathList_whenFileAndPathListHasIt() throws Exception {
+        boolean isSpecified =
+                BackupUtils.isFileSpecifiedInPathList(file("a/b.txt"), paths(file("a/b.txt")));
+
+        assertThat(isSpecified).isTrue();
+    }
+
+    @Test
+    public void testIsFileSpecifiedInPathList_whenFileAndPathListHasItsDirectory()
+            throws Exception {
+        boolean isSpecified =
+                BackupUtils.isFileSpecifiedInPathList(file("a/b.txt"), paths(directory("a")));
+
+        assertThat(isSpecified).isTrue();
+    }
+
+    @Test
+    public void testIsFileSpecifiedInPathList_whenFileAndPathListHasOtherFile() throws Exception {
+        boolean isSpecified =
+                BackupUtils.isFileSpecifiedInPathList(file("a/b.txt"), paths(file("a/c.txt")));
+
+        assertThat(isSpecified).isFalse();
+    }
+
+    @Test
+    public void testIsFileSpecifiedInPathList_whenFileAndPathListEmpty() throws Exception {
+        boolean isSpecified = BackupUtils.isFileSpecifiedInPathList(file("a/b.txt"), paths());
+
+        assertThat(isSpecified).isFalse();
+    }
+
+    @Test
+    public void testIsFileSpecifiedInPathList_whenDirectoryAndPathListHasIt() throws Exception {
+        boolean isSpecified =
+                BackupUtils.isFileSpecifiedInPathList(directory("a"), paths(directory("a")));
+
+        assertThat(isSpecified).isTrue();
+    }
+
+    @Test
+    public void testIsFileSpecifiedInPathList_whenDirectoryAndPathListEmpty() throws Exception {
+        boolean isSpecified = BackupUtils.isFileSpecifiedInPathList(directory("a"), paths());
+
+        assertThat(isSpecified).isFalse();
+    }
+
+    @Test
+    public void testIsFileSpecifiedInPathList_whenDirectoryAndPathListHasParent() throws Exception {
+        boolean isSpecified =
+                BackupUtils.isFileSpecifiedInPathList(directory("a/b"), paths(directory("a")));
+
+        assertThat(isSpecified).isFalse();
+    }
+
+    @Test
+    public void testIsFileSpecifiedInPathList_whenFileAndPathListDoesntContainDirectory()
+            throws Exception {
+        boolean isSpecified =
+                BackupUtils.isFileSpecifiedInPathList(file("a/b.txt"), paths(directory("c")));
+
+        assertThat(isSpecified).isFalse();
+    }
+
+    @Test
+    public void testIsFileSpecifiedInPathList_whenFileAndPathListHasDirectoryWhoseNameIsPrefix()
+            throws Exception {
+        boolean isSpecified =
+                BackupUtils.isFileSpecifiedInPathList(file("a/b.txt"), paths(directory("a/b")));
+
+        assertThat(isSpecified).isFalse();
+    }
+
+    @Test
+    public void testIsFileSpecifiedInPathList_whenFileAndPathListHasDirectoryWhoseNameIsPrefix2()
+            throws Exception {
+        boolean isSpecified =
+                BackupUtils.isFileSpecifiedInPathList(
+                        file("name/subname.txt"), paths(directory("nam")));
+
+        assertThat(isSpecified).isFalse();
+    }
+
+    @Test
+    public void
+            testIsFileSpecifiedInPathList_whenFileAndPathListContainsFirstNotRelatedAndSecondContainingDirectory()
+                    throws Exception {
+        boolean isSpecified =
+                BackupUtils.isFileSpecifiedInPathList(
+                        file("a/b.txt"), paths(directory("b"), directory("a")));
+
+        assertThat(isSpecified).isTrue();
+    }
+
+    @Test
+    public void
+            testIsFileSpecifiedInPathList_whenDirectoryAndPathListContainsFirstNotRelatedAndSecondSameDirectory()
+                    throws Exception {
+        boolean isSpecified =
+                BackupUtils.isFileSpecifiedInPathList(
+                        directory("a/b"), paths(directory("b"), directory("a/b")));
+
+        assertThat(isSpecified).isTrue();
+    }
+
+    @Test
+    public void
+            testIsFileSpecifiedInPathList_whenFileAndPathListContainsFirstNotRelatedFileAndSecondSameFile()
+                    throws Exception {
+        boolean isSpecified =
+                BackupUtils.isFileSpecifiedInPathList(
+                        file("a/b.txt"), paths(directory("b"), file("a/b.txt")));
+
+        assertThat(isSpecified).isTrue();
+    }
+
+    private File file(String path) throws IOException {
+        File file = new File(mContext.getDataDir(), path);
+        File parent = file.getParentFile();
+        parent.mkdirs();
+        file.createNewFile();
+        if (!file.isFile()) {
+            throw new IOException("Couldn't create file");
+        }
+        return file;
+    }
+
+    private File directory(String path) throws IOException {
+        File directory = new File(mContext.getDataDir(), path);
+        directory.mkdirs();
+        if (!directory.isDirectory()) {
+            throw new IOException("Couldn't create directory");
+        }
+        return directory;
+    }
+
+    private Collection<PathWithRequiredFlags> paths(File... files) {
+        return Stream.of(files)
+                .map(file -> new PathWithRequiredFlags(file.getPath(), 0))
+                .collect(Collectors.toList());
+    }
+}
diff --git a/services/robotests/src/android/app/backup/ForwardingBackupAgent.java b/services/robotests/src/android/app/backup/ForwardingBackupAgent.java
new file mode 100644
index 0000000..4ff5b7c
--- /dev/null
+++ b/services/robotests/src/android/app/backup/ForwardingBackupAgent.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.app.backup;
+
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Useful for spying in {@link BackupAgent} instances since their {@link BackupAgent#onBind()} is
+ * final and always points to the original instance, instead of the spy.
+ *
+ * <p>To use, construct a spy of the desired {@link BackupAgent}, spying on the methods of interest.
+ * Then, where you need to pass the agent, use {@link ForwardingBackupAgent#forward(BackupAgent)}
+ * with the spy.
+ */
+public class ForwardingBackupAgent extends BackupAgent {
+    /** Returns a {@link BackupAgent} that forwards method calls to {@code backupAgent}. */
+    public static BackupAgent forward(BackupAgent backupAgent) {
+        return new ForwardingBackupAgent(backupAgent);
+    }
+
+    private final BackupAgent mBackupAgent;
+
+    private ForwardingBackupAgent(BackupAgent backupAgent) {
+        mBackupAgent = backupAgent;
+    }
+
+    @Override
+    public void onCreate() {
+        mBackupAgent.onCreate();
+    }
+
+    @Override
+    public void onDestroy() {
+        mBackupAgent.onDestroy();
+    }
+
+    @Override
+    public void onBackup(
+            ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)
+            throws IOException {
+        mBackupAgent.onBackup(oldState, data, newState);
+    }
+
+    @Override
+    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+            throws IOException {
+        mBackupAgent.onRestore(data, appVersionCode, newState);
+    }
+
+    @Override
+    public void onRestore(BackupDataInput data, long appVersionCode, ParcelFileDescriptor newState)
+            throws IOException {
+        mBackupAgent.onRestore(data, appVersionCode, newState);
+    }
+
+    @Override
+    public void onFullBackup(FullBackupDataOutput data) throws IOException {
+        mBackupAgent.onFullBackup(data);
+    }
+
+    @Override
+    public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
+        mBackupAgent.onQuotaExceeded(backupDataBytes, quotaBytes);
+    }
+
+    @Override
+    public void onRestoreFile(
+            ParcelFileDescriptor data, long size, File destination, int type, long mode, long mtime)
+            throws IOException {
+        mBackupAgent.onRestoreFile(data, size, destination, type, mode, mtime);
+    }
+
+    @Override
+    protected void onRestoreFile(
+            ParcelFileDescriptor data,
+            long size,
+            int type,
+            String domain,
+            String path,
+            long mode,
+            long mtime)
+            throws IOException {
+        mBackupAgent.onRestoreFile(data, size, type, domain, path, mode, mtime);
+    }
+
+    @Override
+    public void onRestoreFinished() {
+        mBackupAgent.onRestoreFinished();
+    }
+
+    @Override
+    public void attach(Context context) {
+        mBackupAgent.attach(context);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index d16bc26..2e0ae02 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -16,7 +16,7 @@
 
 package com.android.server.backup;
 
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThread;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startSilentBackupThread;
 import static com.android.server.backup.testing.TransportData.backupTransport;
 import static com.android.server.backup.testing.TransportData.d2dTransport;
 import static com.android.server.backup.testing.TransportData.localTransport;
@@ -45,7 +45,8 @@
 import android.os.PowerSaveState;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
-import com.android.server.backup.internal.BackupRequest;
+import com.android.server.backup.keyvalue.BackupRequest;
+import com.android.server.backup.testing.BackupManagerServiceTestUtils;
 import com.android.server.backup.testing.TransportData;
 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
 import com.android.server.backup.transport.TransportNotRegisteredException;
@@ -55,7 +56,7 @@
 import com.android.server.testing.shadows.ShadowBackupPolicyEnforcer;
 import com.android.server.testing.shadows.ShadowBinder;
 import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
-import com.android.server.testing.shadows.ShadowPerformBackupTask;
+import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
 import java.io.File;
 import java.util.List;
 import org.junit.After;
@@ -68,11 +69,9 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implements;
 import org.robolectric.shadows.ShadowContextWrapper;
-import org.robolectric.shadows.ShadowLog;
 import org.robolectric.shadows.ShadowLooper;
 import org.robolectric.shadows.ShadowPackageManager;
 import org.robolectric.shadows.ShadowSettings;
-import org.robolectric.shadows.ShadowSystemClock;
 
 @RunWith(FrameworkRobolectricTestRunner.class)
 @Config(
@@ -104,7 +103,10 @@
         mTransport = backupTransport();
         mTransportName = mTransport.transportName;
 
-        mBackupThread = startBackupThread(this::uncaughtException);
+        // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
+        // we should not fail tests because of this. This is not flakiness, the exceptions thrown
+        // don't interfere with the tests.
+        mBackupThread = startSilentBackupThread(TAG);
         mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
 
         ContextWrapper context = RuntimeEnvironment.application;
@@ -113,8 +115,10 @@
         mShadowContext = shadowOf(context);
 
         File cacheDir = mContext.getCacheDir();
-        mBaseStateDir = new File(cacheDir, "base_state_dir");
-        mDataDir = new File(cacheDir, "data_dir");
+        // Corresponds to /data/backup
+        mBaseStateDir = new File(cacheDir, "base_state");
+        // Corresponds to /cache/backup_stage
+        mDataDir = new File(cacheDir, "data");
 
         ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
     }
@@ -126,13 +130,6 @@
         ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
     }
 
-    private void uncaughtException(Thread thread, Throwable e) {
-        // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
-        // we should not fail tests because of this. This is not flakiness, the exceptions thrown
-        // don't interfere with the tests.
-        ShadowLog.e(TAG, "Uncaught exception in test thread " + thread.getName(), e);
-    }
-
     /* Tests for destination string */
 
     @Test
@@ -670,7 +667,7 @@
     }
 
     private void tearDownForRequestBackup() {
-        ShadowPerformBackupTask.reset();
+        ShadowKeyValueBackupTask.reset();
     }
 
     @Test
@@ -757,12 +754,12 @@
 
         assertThat(result).isEqualTo(BackupManager.SUCCESS);
         verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-        // TODO: We probably don't need to kick-off PerformBackupTask when list is empty
+        // TODO: We probably don't need to kick-off KeyValueBackupTask when list is empty
         tearDownForRequestBackup();
     }
 
     @Test
-    @Config(shadows = ShadowPerformBackupTask.class)
+    @Config(shadows = ShadowKeyValueBackupTask.class)
     public void testRequestBackup_whenPackageIsKeyValue() throws Exception {
         setUpForRequestBackup(PACKAGE_1);
         BackupManagerService backupManagerService = createBackupManagerServiceForRequestBackup();
@@ -771,15 +768,15 @@
 
         mShadowBackupLooper.runToEndOfTasks();
         assertThat(result).isEqualTo(BackupManager.SUCCESS);
-        ShadowPerformBackupTask shadowTask = ShadowPerformBackupTask.getLastCreated();
+        ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
         assertThat(shadowTask.getQueue()).containsExactly(new BackupRequest(PACKAGE_1));
         assertThat(shadowTask.getPendingFullBackups()).isEmpty();
-        // TODO: Assert more about PerformBackupTask
+        // TODO: Assert more about KeyValueBackupTask
         tearDownForRequestBackup();
     }
 
     @Test
-    @Config(shadows = ShadowPerformBackupTask.class)
+    @Config(shadows = ShadowKeyValueBackupTask.class)
     public void testRequestBackup_whenPackageIsFullBackup() throws Exception {
         setUpForRequestBackup(PACKAGE_1);
         ShadowAppBackupUtils.setAppGetsFullBackup(PACKAGE_1);
@@ -789,10 +786,10 @@
 
         mShadowBackupLooper.runToEndOfTasks();
         assertThat(result).isEqualTo(BackupManager.SUCCESS);
-        ShadowPerformBackupTask shadowTask = ShadowPerformBackupTask.getLastCreated();
+        ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
         assertThat(shadowTask.getQueue()).isEmpty();
         assertThat(shadowTask.getPendingFullBackups()).containsExactly(PACKAGE_1);
-        // TODO: Assert more about PerformBackupTask
+        // TODO: Assert more about KeyValueBackupTask
         tearDownForRequestBackup();
     }
 
@@ -864,22 +861,8 @@
     }
 
     private BackupManagerService createInitializedBackupManagerService() {
-        BackupManagerService backupManagerService =
-                new BackupManagerService(
-                        mContext,
-                        new Trampoline(mContext),
-                        mBackupThread,
-                        mBaseStateDir,
-                        mDataDir,
-                        mTransportManager);
-        mShadowBackupLooper.runToEndOfTasks();
-        // Handler instances have their own clock, so advancing looper (with runToEndOfTasks())
-        // above does NOT advance the handlers' clock, hence whenever a handler post messages with
-        // specific time to the looper the time of those messages will be before the looper's time.
-        // To fix this we advance SystemClock as well since that is from where the handlers read
-        // time.
-        ShadowSystemClock.setCurrentTimeMillis(mShadowBackupLooper.getScheduler().getCurrentTime());
-        return backupManagerService;
+        return BackupManagerServiceTestUtils.createInitializedBackupManagerService(
+                mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
     }
 
     private void setUpPowerManager(BackupManagerService backupManagerService) {
diff --git a/services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java b/services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java
new file mode 100644
index 0000000..dd0a58d
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.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.server.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class KeyValueBackupJobTest {
+    private Context mContext;
+    private BackupManagerConstants mConstants;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = RuntimeEnvironment.application;
+        mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver());
+        mConstants.start();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mConstants.stop();
+        KeyValueBackupJob.cancel(mContext);
+    }
+
+    @Test
+    public void testIsScheduled_beforeScheduling_returnsFalse() {
+        boolean isScheduled = KeyValueBackupJob.isScheduled();
+
+        assertThat(isScheduled).isFalse();
+    }
+
+    @Test
+    public void testIsScheduled_afterScheduling_returnsTrue() {
+        KeyValueBackupJob.schedule(mContext, mConstants);
+
+        boolean isScheduled = KeyValueBackupJob.isScheduled();
+
+        assertThat(isScheduled).isTrue();
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
deleted file mode 100644
index 88a51a5..0000000
--- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
+++ /dev/null
@@ -1,821 +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.BackupManagerServiceTestUtils.createBackupWakeLock;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils.setUpBackupManagerServiceBasics;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThreadAndGetLooper;
-import static com.android.server.backup.testing.TestUtils.uncheck;
-import static com.android.server.backup.testing.TransportData.backupTransport;
-import static com.google.common.truth.Truth.assertThat;
-import static java.util.Collections.emptyList;
-import static java.util.stream.Collectors.toCollection;
-import static java.util.stream.Collectors.toList;
-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.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.intThat;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.robolectric.Shadows.shadowOf;
-
-import android.app.Application;
-import android.app.IBackupAgent;
-import android.app.backup.BackupAgent;
-import android.app.backup.BackupDataInput;
-import android.app.backup.BackupDataOutput;
-import android.app.backup.BackupManager;
-import android.app.backup.BackupTransport;
-import android.app.backup.IBackupManager;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.os.DeadObjectException;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.platform.test.annotations.Presubmit;
-import android.util.Pair;
-import com.android.internal.backup.IBackupTransport;
-import com.android.server.backup.internal.BackupHandler;
-import com.android.server.backup.internal.BackupRequest;
-import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.internal.PerformBackupTask;
-import com.android.server.backup.testing.TransportData;
-import com.android.server.backup.testing.TransportTestUtils;
-import com.android.server.backup.testing.TransportTestUtils.TransportMock;
-import com.android.server.backup.transport.TransportClient;
-import com.android.server.testing.FrameworkRobolectricTestRunner;
-import com.android.server.testing.SystemLoaderClasses;
-import com.android.server.testing.SystemLoaderPackages;
-import com.android.server.testing.shadows.ShadowBackupDataInput;
-import com.android.server.testing.shadows.ShadowBackupDataOutput;
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Stream;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.shadows.ShadowQueuedWork;
-
-@RunWith(FrameworkRobolectricTestRunner.class)
-@Config(
-        manifest = Config.NONE,
-        sdk = 26,
-        shadows = {
-            ShadowBackupDataInput.class,
-            ShadowBackupDataOutput.class,
-            ShadowQueuedWork.class
-        })
-@SystemLoaderPackages({"com.android.server.backup", "android.app.backup"})
-@SystemLoaderClasses({IBackupTransport.class, IBackupAgent.class, PackageInfo.class})
-@Presubmit
-public class PerformBackupTaskTest {
-    private static final String PACKAGE_1 = "com.example.package1";
-    private static final String PACKAGE_2 = "com.example.package2";
-
-    @Mock private BackupManagerService mBackupManagerService;
-    @Mock private TransportManager mTransportManager;
-    @Mock private DataChangedJournal mDataChangedJournal;
-    @Mock private IBackupObserver mObserver;
-    @Mock private IBackupManagerMonitor mMonitor;
-    @Mock private OnTaskFinishedListener mListener;
-    private TransportData mTransport;
-    private ShadowLooper mShadowBackupLooper;
-    private BackupHandler mBackupHandler;
-    private PowerManager.WakeLock mWakeLock;
-    private ShadowPackageManager mShadowPackageManager;
-    private FakeIBackupManager mBackupManager;
-    private File mBaseStateDir;
-    private Application mApplication;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        mTransport = backupTransport();
-
-        mApplication = RuntimeEnvironment.application;
-        File cacheDir = mApplication.getCacheDir();
-        mBaseStateDir = new File(cacheDir, "base_state_dir");
-        File dataDir = new File(cacheDir, "data_dir");
-        assertThat(mBaseStateDir.mkdir()).isTrue();
-        assertThat(dataDir.mkdir()).isTrue();
-
-        PackageManager packageManager = mApplication.getPackageManager();
-        mShadowPackageManager = shadowOf(packageManager);
-
-        mWakeLock = createBackupWakeLock(mApplication);
-
-        Looper backupLooper = startBackupThreadAndGetLooper();
-        mShadowBackupLooper = shadowOf(backupLooper);
-
-        Handler mainHandler = new Handler(Looper.getMainLooper());
-        BackupAgentTimeoutParameters agentTimeoutParameters =
-                new BackupAgentTimeoutParameters(mainHandler, mApplication.getContentResolver());
-        agentTimeoutParameters.start();
-
-        // We need to mock BMS timeout parameters before initializing the BackupHandler since
-        // the constructor of BackupHandler relies on the timeout parameters.
-        when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
-        mBackupHandler = new BackupHandler(mBackupManagerService, backupLooper);
-
-        mBackupManager = spy(FakeIBackupManager.class);
-
-        BackupManagerConstants constants =
-                new BackupManagerConstants(mainHandler, mApplication.getContentResolver());
-        constants.start();
-
-        setUpBackupManagerServiceBasics(
-                mBackupManagerService,
-                mApplication,
-                mTransportManager,
-                packageManager,
-                mBackupHandler,
-                mWakeLock,
-                agentTimeoutParameters);
-        when(mBackupManagerService.getBaseStateDir()).thenReturn(mBaseStateDir);
-        when(mBackupManagerService.getDataDir()).thenReturn(dataDir);
-        when(mBackupManagerService.getBackupManagerBinder()).thenReturn(mBackupManager);
-        when(mBackupManagerService.getConstants()).thenReturn(constants);
-        when(mBackupManagerService.makeMetadataAgent()).thenAnswer(invocation -> createPmAgent());
-    }
-
-    @Test
-    public void testRunTask_whenTransportProvidesFlags_passesThemToTheAgent() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        AgentMock agentMock = setUpAgent(PACKAGE_1);
-        int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
-        when(transportMock.transport.getTransportFlags()).thenReturn(flags);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-
-        runTask(task);
-
-        verify(agentMock.agent)
-                .onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
-    }
-
-    @Test
-    public void testRunTask_whenTransportDoesNotProvidesFlags() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        AgentMock agentMock = setUpAgent(PACKAGE_1);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-
-        runTask(task);
-
-        verify(agentMock.agent).onBackup(any(), argThat(dataOutputWithTransportFlags(0)), any());
-    }
-
-    @Test
-    public void testRunTask_whenTransportProvidesFlagsAndMultipleAgents_passesToAll()
-            throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        List<AgentMock> agentMocks = setUpAgents(PACKAGE_1, PACKAGE_2);
-        BackupAgent agent1 = agentMocks.get(0).agent;
-        BackupAgent agent2 = agentMocks.get(1).agent;
-        int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
-        when(transportMock.transport.getTransportFlags()).thenReturn(flags);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient,
-                        mTransport.transportDirName,
-                        PACKAGE_1,
-                        PACKAGE_2);
-
-        runTask(task);
-
-        verify(agent1).onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
-        verify(agent2).onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
-    }
-
-    @Test
-    public void testRunTask_whenTransportChangeFlagsAfterTaskCreation() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        AgentMock agentMock = setUpAgent(PACKAGE_1);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-        int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
-        when(transportMock.transport.getTransportFlags()).thenReturn(flags);
-
-        runTask(task);
-
-        verify(agentMock.agent)
-                .onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
-    }
-
-    @Test
-    public void testRunTask_callsListenerAndObserver() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        setUpAgent(PACKAGE_1);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-
-        runTask(task);
-
-        verify(mListener).onFinished(any());
-        verify(mObserver).backupFinished(eq(BackupManager.SUCCESS));
-    }
-
-    @Test
-    public void testRunTask_releasesWakeLock() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        setUpAgent(PACKAGE_1);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-
-        runTask(task);
-
-        assertThat(mWakeLock.isHeld()).isFalse();
-    }
-
-    @Test
-    public void testRunTask_callsTransportPerformBackupWithAgentData() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        IBackupTransport transportBinder = transportMock.transport;
-        AgentMock agentMock = setUpAgent(PACKAGE_1);
-        agentOnBackupDo(
-                agentMock.agent,
-                (oldState, dataOutput, newState) -> {
-                    writeData(dataOutput, "key1", "foo".getBytes());
-                    writeData(dataOutput, "key2", "bar".getBytes());
-                });
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-        // We need to verify at call time because the file is deleted right after
-        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
-                .then(this::mockAndVerifyTransportPerformBackupData);
-
-        runTask(task);
-
-        // Already verified data in mockAndVerifyPerformBackupData
-        verify(transportBinder).performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
-    }
-
-    private int mockAndVerifyTransportPerformBackupData(InvocationOnMock invocation)
-            throws IOException {
-        ParcelFileDescriptor data = invocation.getArgument(1);
-
-        // Verifying that what we passed to the transport is what the agent wrote
-        BackupDataInput dataInput = new BackupDataInput(data.getFileDescriptor());
-
-        // "key1" => "foo"
-        assertThat(dataInput.readNextHeader()).isTrue();
-        assertThat(dataInput.getKey()).isEqualTo("key1");
-        int size1 = dataInput.getDataSize();
-        byte[] data1 = new byte[size1];
-        dataInput.readEntityData(data1, 0, size1);
-        assertThat(data1).isEqualTo("foo".getBytes());
-
-        // "key2" => "bar"
-        assertThat(dataInput.readNextHeader()).isTrue();
-        assertThat(dataInput.getKey()).isEqualTo("key2");
-        int size2 = dataInput.getDataSize();
-        byte[] data2 = new byte[size2];
-        dataInput.readEntityData(data2, 0, size2);
-        assertThat(data2).isEqualTo("bar".getBytes());
-
-        // No more
-        assertThat(dataInput.readNextHeader()).isFalse();
-
-        return BackupTransport.TRANSPORT_OK;
-    }
-
-    @Test
-    public void testRunTask_whenPerformBackupSucceeds_callsTransportFinishBackup()
-            throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        IBackupTransport transportBinder = transportMock.transport;
-        setUpAgentWithData(PACKAGE_1);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
-                .thenReturn(BackupTransport.TRANSPORT_OK);
-
-        runTask(task);
-
-        verify(transportBinder).finishBackup();
-    }
-
-    @Test
-    public void testRunTask_whenProhibitedKey_failsAgent() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        AgentMock agentMock = setUpAgent(PACKAGE_1);
-        agentOnBackupDo(
-                agentMock.agent,
-                (oldState, dataOutput, newState) -> {
-                    char prohibitedChar = 0xff00;
-                    writeData(dataOutput, prohibitedChar + "key", "foo".getBytes());
-                });
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-
-        runTask(task);
-
-        verify(mListener).onFinished(any());
-        verify(mObserver).onResult(eq(PACKAGE_1), eq(BackupManager.ERROR_AGENT_FAILURE));
-        verify(agentMock.agentBinder).fail(any());
-        verify(mObserver).backupFinished(BackupManager.SUCCESS);
-    }
-
-    @Test
-    public void testRunTask_whenFirstAgentKeyProhibitedButLastPermitted() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        List<AgentMock> agentMocks = setUpAgents(PACKAGE_1, PACKAGE_2);
-        AgentMock agentMock1 = agentMocks.get(0);
-        AgentMock agentMock2 = agentMocks.get(1);
-        agentOnBackupDo(
-                agentMock1.agent,
-                (oldState, dataOutput, newState) -> {
-                    char prohibitedChar = 0xff00;
-                    writeData(dataOutput, prohibitedChar + "key", "foo".getBytes());
-                });
-        agentOnBackupDo(
-                agentMock2.agent,
-                (oldState, dataOutput, newState) -> {
-                    writeData(dataOutput, "key", "bar".getBytes());
-                });
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient,
-                        mTransport.transportDirName,
-                        PACKAGE_1,
-                        PACKAGE_2);
-
-        runTask(task);
-
-        verify(mListener).onFinished(any());
-        verify(mObserver).onResult(eq(PACKAGE_1), eq(BackupManager.ERROR_AGENT_FAILURE));
-        verify(agentMock1.agentBinder).fail(any());
-        verify(mObserver).onResult(eq(PACKAGE_2), eq(BackupManager.SUCCESS));
-        verify(mObserver).backupFinished(BackupManager.SUCCESS);
-    }
-
-    @Test
-    public void testRunTask_whenTransportUnavailable() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport.unavailable());
-        setUpAgentWithData(PACKAGE_1);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-
-        runTask(task);
-
-        verify(mListener).onFinished(any());
-        verify(mObserver).backupFinished(eq(BackupManager.ERROR_TRANSPORT_ABORTED));
-    }
-
-    @Test
-    public void testRunTask_whenTransportRejectsPackage() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        setUpAgentWithData(PACKAGE_1);
-        when(transportMock.transport.performBackup(
-                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
-                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-
-        runTask(task);
-
-        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
-        verify(mObserver).backupFinished(BackupManager.SUCCESS);
-    }
-
-    @Test
-    public void testRunTask_whenTransportRejectsFirstPackageButLastSucceeds() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        IBackupTransport transportBinder = transportMock.transport;
-        setUpAgentsWithData(PACKAGE_1, PACKAGE_2);
-        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
-                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
-        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt()))
-                .thenReturn(BackupTransport.TRANSPORT_OK);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient,
-                        mTransport.transportDirName,
-                        PACKAGE_1,
-                        PACKAGE_2);
-
-        runTask(task);
-
-        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
-        verify(mObserver).onResult(PACKAGE_2, BackupManager.SUCCESS);
-        verify(mObserver).backupFinished(BackupManager.SUCCESS);
-    }
-
-    @Test
-    public void testRunTask_whenTransportRejectsLastPackageButFirstSucceeds() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        IBackupTransport transportBinder = transportMock.transport;
-        setUpAgentsWithData(PACKAGE_1, PACKAGE_2);
-        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
-                .thenReturn(BackupTransport.TRANSPORT_OK);
-        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt()))
-                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient,
-                        mTransport.transportDirName,
-                        PACKAGE_1,
-                        PACKAGE_2);
-
-        runTask(task);
-
-        verify(mObserver).onResult(PACKAGE_1, BackupManager.SUCCESS);
-        verify(mObserver).onResult(PACKAGE_2, BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
-        verify(mObserver).backupFinished(BackupManager.SUCCESS);
-    }
-
-    @Test
-    public void testRunTask_whenTransportReturnsQuotaExceeded() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        AgentMock agentMock = setUpAgentWithData(PACKAGE_1);
-        when(transportMock.transport.performBackup(
-                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
-                .thenReturn(BackupTransport.TRANSPORT_QUOTA_EXCEEDED);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-
-        runTask(task);
-
-        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
-        verify(mObserver).backupFinished(BackupManager.SUCCESS);
-        verify(agentMock.agent).onQuotaExceeded(anyLong(), anyLong());
-    }
-
-    @Test
-    public void testRunTask_whenAgentUnknown() throws Exception {
-        // Not calling setUpAgent()
-        TransportMock transportMock = setUpTransport(mTransport);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-
-        runTask(task);
-
-        verify(transportMock.transport, never()).performBackup(any(), any(), anyInt());
-        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_PACKAGE_NOT_FOUND);
-        verify(mObserver).backupFinished(BackupManager.SUCCESS);
-    }
-
-    @Test
-    public void testRunTask_whenNonIncrementalAndTransportRequestsNonIncremental()
-            throws Exception {
-        // It's going to be non-incremental because we haven't created any previous state
-        TransportMock transportMock = setUpTransport(mTransport);
-        setUpAgentWithData(PACKAGE_1);
-        when(transportMock.transport.performBackup(
-                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
-                .thenReturn(BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient,
-                        mTransport.transportDirName,
-                        true,
-                        PACKAGE_1);
-
-        runTask(task);
-
-        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_TRANSPORT_ABORTED);
-        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
-    }
-
-    @Test
-    public void testRunTask_whenIncrementalAndTransportRequestsNonIncremental() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        AgentMock agentMock = setUpAgentWithData(PACKAGE_1);
-        IBackupTransport transport = transportMock.transport;
-        when(transport.performBackup(
-                        argThat(packageInfo(PACKAGE_1)),
-                        any(),
-                        intThat(flags -> (flags & BackupTransport.FLAG_INCREMENTAL) != 0)))
-                .thenReturn(BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED);
-        when(transport.performBackup(
-                        argThat(packageInfo(PACKAGE_1)),
-                        any(),
-                        intThat(flags -> (flags & BackupTransport.FLAG_NON_INCREMENTAL) != 0)))
-                .thenReturn(BackupTransport.TRANSPORT_OK);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient,
-                        mTransport.transportDirName,
-                        false,
-                        PACKAGE_1);
-        // Write content to be incremental
-        Files.write(
-                Paths.get(mBaseStateDir.getAbsolutePath(), mTransport.transportDirName, PACKAGE_1),
-                "existent".getBytes());
-
-        runTask(task);
-
-        verify(agentMock.agent, times(2)).onBackup(any(), any(), any());
-        verify(mObserver).onResult(PACKAGE_1, BackupManager.SUCCESS);
-        verify(mObserver).backupFinished(BackupManager.SUCCESS);
-    }
-
-    @Test
-    public void testRunTask_whenQueueEmpty() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient, mTransport.transportDirName, true);
-
-        runTask(task);
-
-        verify(mObserver).backupFinished(eq(BackupManager.SUCCESS));
-    }
-
-    @Test
-    public void testRunTask_whenIncrementalAndTransportUnavailableDuringPmBackup()
-            throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        IBackupTransport transportBinder = transportMock.transport;
-        setUpAgent(PACKAGE_1);
-        when(transportBinder.getBackupQuota(
-                        eq(BackupManagerService.PACKAGE_MANAGER_SENTINEL), anyBoolean()))
-                .thenThrow(DeadObjectException.class);
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient,
-                        mTransport.transportDirName,
-                        false,
-                        PACKAGE_1);
-
-        runTask(task);
-
-        verify(mListener).onFinished(any());
-        verify(mObserver).backupFinished(eq(BackupManager.ERROR_TRANSPORT_ABORTED));
-    }
-
-    @Test
-    public void testRunTask_whenIncrementalAndAgentFails() throws Exception {
-        TransportMock transportMock = setUpTransport(mTransport);
-        createFakePmAgent();
-        PerformBackupTask task =
-                createPerformBackupTask(
-                        transportMock.transportClient,
-                        mTransport.transportDirName,
-                        false,
-                        PACKAGE_1);
-
-        runTask(task);
-
-        verify(mListener).onFinished(any());
-        verify(mObserver).backupFinished(eq(BackupManager.ERROR_TRANSPORT_ABORTED));
-    }
-
-    private void runTask(PerformBackupTask task) {
-        Message message = mBackupHandler.obtainMessage(BackupHandler.MSG_BACKUP_RESTORE_STEP, task);
-        mBackupHandler.sendMessage(message);
-        while (mShadowBackupLooper.getScheduler().areAnyRunnable()) {
-            mShadowBackupLooper.runToEndOfTasks();
-        }
-    }
-
-    private TransportMock setUpTransport(TransportData transport) throws Exception {
-        TransportMock transportMock =
-                TransportTestUtils.setUpTransport(mTransportManager, transport);
-        File stateDir = new File(mBaseStateDir, transport.transportDirName);
-        assertThat(stateDir.mkdir()).isTrue();
-        return transportMock;
-    }
-
-    private List<AgentMock> setUpAgents(String... packageNames) {
-        return Stream.of(packageNames).map(this::setUpAgent).collect(toList());
-    }
-
-    private AgentMock setUpAgent(String packageName) {
-        try {
-            PackageInfo packageInfo = new PackageInfo();
-            packageInfo.packageName = packageName;
-            packageInfo.applicationInfo = new ApplicationInfo();
-            packageInfo.applicationInfo.flags = ApplicationInfo.FLAG_ALLOW_BACKUP;
-            packageInfo.applicationInfo.backupAgentName = "BackupAgent" + packageName;
-            packageInfo.applicationInfo.packageName = packageName;
-            mShadowPackageManager.setApplicationEnabledSetting(
-                    packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
-            mShadowPackageManager.addPackage(packageInfo);
-            BackupAgent backupAgent = spy(BackupAgent.class);
-            IBackupAgent backupAgentBinder =
-                    spy(IBackupAgent.Stub.asInterface(backupAgent.onBind()));
-            // Don't crash our only process (in production code this would crash the app, not us)
-            doNothing().when(backupAgentBinder).fail(any());
-            when(mBackupManagerService.bindToAgentSynchronous(
-                            eq(packageInfo.applicationInfo), anyInt()))
-                    .thenReturn(backupAgentBinder);
-            return new AgentMock(backupAgentBinder, backupAgent);
-        } catch (RemoteException e) {
-            // Never happens, compiler happy
-            throw new AssertionError(e);
-        }
-    }
-
-    private List<AgentMock> setUpAgentsWithData(String... packageNames) {
-        return Stream.of(packageNames).map(this::setUpAgentWithData).collect(toList());
-    }
-
-    private AgentMock setUpAgentWithData(String packageName) {
-        AgentMock agentMock = setUpAgent(packageName);
-        uncheck(
-                () ->
-                        agentOnBackupDo(
-                                agentMock.agent,
-                                (oldState, dataOutput, newState) ->
-                                        writeData(dataOutput, "key", packageName.getBytes())));
-        return agentMock;
-    }
-
-    private PerformBackupTask createPerformBackupTask(
-            TransportClient transportClient, String transportDirName, String... packages) {
-        return createPerformBackupTask(transportClient, transportDirName, true, packages);
-    }
-
-    private PerformBackupTask createPerformBackupTask(
-            TransportClient transportClient,
-            String transportDirName,
-            boolean nonIncremental,
-            String... packages) {
-        ArrayList<BackupRequest> backupRequests =
-                Stream.of(packages).map(BackupRequest::new).collect(toCollection(ArrayList::new));
-        mWakeLock.acquire();
-        PerformBackupTask task =
-                new PerformBackupTask(
-                        mBackupManagerService,
-                        transportClient,
-                        transportDirName,
-                        backupRequests,
-                        mDataChangedJournal,
-                        mObserver,
-                        mMonitor,
-                        mListener,
-                        emptyList(),
-                        /* userInitiated */ false,
-                        nonIncremental);
-        mBackupManager.setUp(mBackupHandler, task);
-        return task;
-    }
-
-    private PackageManagerBackupAgent createPmAgent() {
-        PackageManagerBackupAgent pmAgent =
-                new PackageManagerBackupAgent(mApplication.getPackageManager());
-        pmAgent.attach(mApplication);
-        pmAgent.onCreate();
-        return pmAgent;
-    }
-
-    /**
-     * Returns an implementation of PackageManagerBackupAgent that throws RuntimeException in {@link
-     * BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
-     */
-    private PackageManagerBackupAgent createFakePmAgent() {
-        PackageManagerBackupAgent fakePmAgent =
-                new FakePackageManagerBackupAgent(mApplication.getPackageManager());
-        fakePmAgent.attach(mApplication);
-        fakePmAgent.onCreate();
-        when(mBackupManagerService.makeMetadataAgent()).thenReturn(fakePmAgent);
-        return fakePmAgent;
-    }
-
-    /** Matches {@link PackageInfo} whose package name is {@code packageName}. */
-    private static ArgumentMatcher<PackageInfo> packageInfo(String packageName) {
-        // We have to test for packageInfo nulity because of Mockito's own stubbing with argThat().
-        // E.g. if you do:
-        //
-        //   1. when(object.method(argThat(str -> str.equals("foo")))).thenReturn(0)
-        //   2. when(object.method(argThat(str -> str.equals("bar")))).thenReturn(2)
-        //
-        // The second line will throw NPE because it will call lambda 1 with null, since argThat()
-        // returns null. So we guard against that by checking for null.
-        return packageInfo -> packageInfo != null && packageName.equals(packageInfo.packageName);
-    }
-
-    private static ArgumentMatcher<BackupDataOutput> dataOutputWithTransportFlags(int flags) {
-        return dataOutput -> dataOutput.getTransportFlags() == flags;
-    }
-
-    private static void writeData(BackupDataOutput dataOutput, String key, byte[] data)
-            throws IOException {
-        dataOutput.writeEntityHeader(key, data.length);
-        dataOutput.writeEntityData(data, data.length);
-    }
-
-    private static void agentOnBackupDo(BackupAgent agent, BackupAgentOnBackup function)
-            throws Exception {
-        doAnswer(function).when(agent).onBackup(any(), any(), any());
-    }
-
-    @FunctionalInterface
-    private interface BackupAgentOnBackup extends Answer<Void> {
-        void onBackup(
-                ParcelFileDescriptor oldState,
-                BackupDataOutput dataOutput,
-                ParcelFileDescriptor newState)
-                throws IOException;
-
-        @Override
-        default Void answer(InvocationOnMock invocation) throws Throwable {
-            onBackup(
-                    invocation.getArgument(0),
-                    invocation.getArgument(1),
-                    invocation.getArgument(2));
-            return null;
-        }
-    }
-
-    private static class AgentMock {
-        private final IBackupAgent agentBinder;
-        private final BackupAgent agent;
-
-        private AgentMock(IBackupAgent agentBinder, BackupAgent agent) {
-            this.agentBinder = agentBinder;
-            this.agent = agent;
-        }
-    }
-
-    private abstract static class FakeIBackupManager extends IBackupManager.Stub {
-        private Handler mBackupHandler;
-        private BackupRestoreTask mTask;
-
-        public FakeIBackupManager() {}
-
-        private void setUp(Handler backupHandler, BackupRestoreTask task) {
-            mBackupHandler = backupHandler;
-            mTask = task;
-        }
-
-        @Override
-        public void opComplete(int token, long result) throws RemoteException {
-            assertThat(mTask).isNotNull();
-            Message message =
-                    mBackupHandler.obtainMessage(
-                            BackupHandler.MSG_OP_COMPLETE, Pair.create(mTask, result));
-            mBackupHandler.sendMessage(message);
-        }
-    }
-
-    private static class FakePackageManagerBackupAgent extends PackageManagerBackupAgent {
-        public FakePackageManagerBackupAgent(PackageManager packageMgr) {
-            super(packageMgr);
-        }
-
-        @Override
-        public void onBackup(
-                ParcelFileDescriptor oldState,
-                BackupDataOutput data,
-                ParcelFileDescriptor newState) {
-            throw new RuntimeException();
-        }
-    }
-}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
new file mode 100644
index 0000000..de22201
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -0,0 +1,2007 @@
+/*
+ * 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.keyvalue;
+
+import static android.app.backup.BackupManager.ERROR_AGENT_FAILURE;
+import static android.app.backup.BackupManager.ERROR_BACKUP_NOT_ALLOWED;
+import static android.app.backup.BackupManager.ERROR_PACKAGE_NOT_FOUND;
+import static android.app.backup.BackupManager.ERROR_TRANSPORT_ABORTED;
+import static android.app.backup.BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED;
+import static android.app.backup.BackupManager.SUCCESS;
+import static android.app.backup.ForwardingBackupAgent.forward;
+
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createBackupWakeLock;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils
+        .createInitializedBackupManagerService;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils
+        .setUpBackupManagerServiceBasics;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils
+        .setUpBinderCallerAndApplicationAsSystem;
+import static com.android.server.backup.testing.PackageData.PM_PACKAGE;
+import static com.android.server.backup.testing.PackageData.fullBackupPackage;
+import static com.android.server.backup.testing.PackageData.keyValuePackage;
+import static com.android.server.backup.testing.TestUtils.assertEventLogged;
+import static com.android.server.backup.testing.TestUtils.uncheck;
+import static com.android.server.backup.testing.TransportData.backupTransport;
+import static com.android.server.backup.testing.Utils.oneTimeIterable;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.intThat;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+import static org.robolectric.shadow.api.Shadow.extract;
+
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static java.util.Collections.emptyList;
+import static java.util.stream.Collectors.toCollection;
+import static java.util.stream.Collectors.toList;
+
+import android.annotation.Nullable;
+import android.app.Application;
+import android.app.IBackupAgent;
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupTransport;
+import android.app.backup.IBackupManager;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+
+import com.android.internal.backup.IBackupTransport;
+import com.android.server.EventLogTags;
+import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.PackageManagerBackupAgent;
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.internal.BackupHandler;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.testing.PackageData;
+import com.android.server.backup.testing.TransportData;
+import com.android.server.backup.testing.TransportTestUtils;
+import com.android.server.backup.testing.TransportTestUtils.TransportMock;
+import com.android.server.backup.testing.Utils;
+import com.android.server.backup.transport.TransportClient;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderClasses;
+import com.android.server.testing.SystemLoaderPackages;
+import com.android.server.testing.shadows.FrameworkShadowLooper;
+import com.android.server.testing.shadows.ShadowBackupDataInput;
+import com.android.server.testing.shadows.ShadowBackupDataOutput;
+import com.android.server.testing.shadows.ShadowEventLog;
+
+import com.google.common.truth.IterableSubject;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+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;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+// TODO: When returning to RUNNING_QUEUE vs FINAL, RUNNING_QUEUE sets status = OK. Why? Verify?
+// TODO: Check queue in general, behavior w/ multiple packages
+// TODO: Test PM invocation
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(
+        manifest = Config.NONE,
+        sdk = 26,
+        shadows = {
+            FrameworkShadowLooper.class,
+            ShadowBackupDataInput.class,
+            ShadowBackupDataOutput.class,
+            ShadowEventLog.class,
+            ShadowQueuedWork.class,
+        })
+@SystemLoaderPackages({"com.android.server.backup", "android.app.backup"})
+@SystemLoaderClasses({IBackupTransport.class, IBackupAgent.class, PackageInfo.class})
+@Presubmit
+public class KeyValueBackupTaskTest {
+    private static final PackageData PACKAGE_1 = keyValuePackage(1);
+    private static final PackageData PACKAGE_2 = keyValuePackage(2);
+
+    @Mock private TransportManager mTransportManager;
+    @Mock private DataChangedJournal mOldJournal;
+    @Mock private IBackupObserver mObserver;
+    @Mock private IBackupManagerMonitor mMonitor;
+    @Mock private OnTaskFinishedListener mListener;
+    private BackupManagerService mBackupManagerService;
+    private TransportData mTransport;
+    private ShadowLooper mShadowBackupLooper;
+    private Handler mBackupHandler;
+    private PowerManager.WakeLock mWakeLock;
+    private ShadowPackageManager mShadowPackageManager;
+    private FakeIBackupManager mBackupManager;
+    private File mBaseStateDir;
+    private File mDataDir;
+    private Application mApplication;
+    private ShadowApplication mShadowApplication;
+    private FrameworkShadowLooper mShadowMainLooper;
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mTransport = backupTransport();
+
+        mApplication = RuntimeEnvironment.application;
+        mShadowApplication = shadowOf(mApplication);
+        mContext = mApplication;
+
+        mShadowMainLooper = extract(Looper.getMainLooper());
+
+        File cacheDir = mApplication.getCacheDir();
+        // Corresponds to /data/backup
+        mBaseStateDir = new File(cacheDir, "base_state");
+        // Corresponds to /cache/backup_stage
+        mDataDir = new File(cacheDir, "data");
+        // We create here simulating init.rc
+        mDataDir.mkdirs();
+        assertThat(mDataDir.isDirectory()).isTrue();
+
+        PackageManager packageManager = mApplication.getPackageManager();
+        mShadowPackageManager = shadowOf(packageManager);
+
+        mWakeLock = createBackupWakeLock(mApplication);
+
+        mBackupManager = spy(FakeIBackupManager.class);
+
+        // Needed to be able to use a real BMS instead of a mock
+        setUpBinderCallerAndApplicationAsSystem(mApplication);
+        mBackupManagerService =
+                spy(
+                        createInitializedBackupManagerService(
+                                mContext, mBaseStateDir, mDataDir, mTransportManager));
+        setUpBackupManagerServiceBasics(
+                mBackupManagerService,
+                mApplication,
+                mTransportManager,
+                packageManager,
+                mBackupManagerService.getBackupHandler(),
+                mWakeLock,
+                mBackupManagerService.getAgentTimeoutParameters());
+        when(mBackupManagerService.getBaseStateDir()).thenReturn(mBaseStateDir);
+        when(mBackupManagerService.getDataDir()).thenReturn(mDataDir);
+        when(mBackupManagerService.getBackupManagerBinder()).thenReturn(mBackupManager);
+
+        mBackupHandler = mBackupManagerService.getBackupHandler();
+        mShadowBackupLooper = shadowOf(mBackupHandler.getLooper());
+        ShadowEventLog.setUp();
+    }
+
+    @Test
+    public void testRunTask_whenQueueEmpty_updatesBookkeeping() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(mBackupManagerService.getCurrentToken()).thenReturn(0L);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, true);
+
+        runTask(task);
+
+        assertThat(mBackupManagerService.getPendingInits()).isEmpty();
+        assertThat(mBackupManagerService.isBackupRunning()).isFalse();
+        assertThat(mBackupManagerService.getCurrentOperations().size()).isEqualTo(0);
+        verify(mOldJournal).delete();
+    }
+
+    @Test
+    public void testRunTask_whenQueueEmpty_releasesWakeLock() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(mBackupManagerService.getCurrentToken()).thenReturn(0L);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, true);
+
+        runTask(task);
+
+        assertThat(mWakeLock.isHeld()).isFalse();
+    }
+
+    @Test
+    public void testRunTask_whenQueueEmpty_doesNotProduceData() throws Exception {
+        TransportMock transportMock = setUpTransport(mTransport);
+        when(mBackupManagerService.getCurrentToken()).thenReturn(0L);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, true);
+
+        runTask(task);
+
+        assertDirectory(getStateDirectory(mTransport)).isEmpty();
+        assertDirectory(mDataDir.toPath()).isEmpty();
+    }
+
+    @Test
+    public void testRunTask_whenQueueEmpty_doesNotCallTransport() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(mBackupManagerService.getCurrentToken()).thenReturn(0L);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, true);
+
+        runTask(task);
+
+        verify(transportMock.transport, never()).initializeDevice();
+        verify(transportMock.transport, never()).performBackup(any(), any(), anyInt());
+        verify(transportMock.transport, never()).finishBackup();
+    }
+
+    @Test
+    public void testRunTask_whenQueueEmpty_notifiesCorrectly() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(mBackupManagerService.getCurrentToken()).thenReturn(0L);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, true);
+
+        runTask(task);
+
+        verify(mListener).onFinished(any());
+        verify(mObserver, never()).onResult(any(), anyInt());
+        verify(mObserver).backupFinished(SUCCESS);
+    }
+
+    @Test
+    public void testRunTask_whenQueueEmpty_doesNotChangeStateFiles() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, true);
+        Files.write(getStateFile(mTransport, PM_PACKAGE), "pmState".getBytes());
+        Files.write(getStateFile(mTransport, PACKAGE_1), "packageState".getBytes());
+
+        runTask(task);
+
+        assertThat(Files.readAllBytes(getStateFile(mTransport, PM_PACKAGE)))
+                .isEqualTo("pmState".getBytes());
+        assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1)))
+                .isEqualTo("packageState".getBytes());
+    }
+
+    @Test
+    public void testRunTask_whenOnePackageAndTransportUnavailable() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport.unavailable());
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mListener).onFinished(any());
+        verify(mObserver).backupFinished(ERROR_TRANSPORT_ABORTED);
+        assertBackupPendingFor(PACKAGE_1);
+    }
+
+    @Test
+    public void testRunTask_whenOnePackage_logsBackupStartEvent() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        assertEventLogged(EventLogTags.BACKUP_START, mTransport.transportName);
+    }
+
+    @Test
+    public void testRunTask_whenOnePackage_releasesWakeLock() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        assertThat(mWakeLock.isHeld()).isFalse();
+    }
+
+    @Test
+    public void testRunTask_whenOnePackage_updatesBookkeeping() throws Exception {
+        // Transport has to be initialized to not reset current token
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        mBackupManagerService.setCurrentToken(0L);
+        when(transportMock.transport.getCurrentRestoreSet()).thenReturn(1234L);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        assertThat(mBackupManagerService.getPendingInits()).isEmpty();
+        assertThat(mBackupManagerService.isBackupRunning()).isFalse();
+        assertThat(mBackupManagerService.getCurrentOperations().size()).isEqualTo(0);
+        assertThat(mBackupManagerService.getCurrentToken()).isEqualTo(1234L);
+        verify(mBackupManagerService).writeRestoreTokens();
+        verify(mOldJournal).delete();
+    }
+
+    @Test
+    public void testRunTask_whenPackageWithOldStateAndIncremental_passesOldStateToAgent()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient,
+                        mTransport.transportDirName,
+                        false,
+                        PACKAGE_1);
+        Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
+
+        runTask(task);
+
+        assertThat(agentMock.oldState).isEqualTo("oldState".getBytes());
+    }
+
+    @Test
+    public void testRunTask_whenPackageWithOldStateAndNonIncremental_passesEmptyOldStateToAgent()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient,
+                        mTransport.transportDirName,
+                        true,
+                        PACKAGE_1);
+        Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
+
+        runTask(task);
+
+        assertThat(agentMock.oldState).isEqualTo(new byte[0]);
+    }
+
+    @Test
+    public void testRunTask_whenNonPmPackageAndNonIncremental_doesNotBackUpPm() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgentWithData(PACKAGE_1);
+        PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+        when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient,
+                        mTransport.transportDirName,
+                        true,
+                        PACKAGE_1);
+
+        runTask(task);
+
+        verify(pmAgent, never()).onBackup(any(), any(), any());
+    }
+
+    @Test
+    public void testRunTask_whenNonPmPackageAndPmAndNonIncremental_backsUpPm() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgentWithData(PACKAGE_1);
+        PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+        when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient,
+                        mTransport.transportDirName,
+                        true,
+                        PACKAGE_1,
+                        PM_PACKAGE);
+
+        runTask(task);
+
+        verify(pmAgent).onBackup(any(), any(), any());
+    }
+
+    @Test
+    public void testRunTask_whenNonPmPackageAndIncremental_backsUpPm() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgentWithData(PACKAGE_1);
+        PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+        when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient,
+                        mTransport.transportDirName,
+                        false,
+                        PACKAGE_1);
+
+        runTask(task);
+
+        verify(pmAgent).onBackup(any(), any(), any());
+    }
+
+    @Test
+    public void testRunTask_whenOnePackageAndNoPmState_initializesTransportAndResetsState()
+            throws Exception {
+        TransportMock transportMock = setUpTransport(mTransport);
+        // Need 2 packages to be able to verify state of package not involved in the task
+        setUpAgentsWithData(PACKAGE_1, PACKAGE_2);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+        deletePmStateFile();
+        Files.write(getStateFile(mTransport, PACKAGE_2), "package2State".getBytes());
+
+        runTask(task);
+
+        verify(transportMock.transport).initializeDevice();
+        verify(mBackupManagerService).resetBackupState(getStateDirectory(mTransport).toFile());
+        // Verifying that it deleted all the states (can't verify package 1 because it generated a
+        // new state in this task execution)
+        assertThat(Files.exists(getStateFile(mTransport, PACKAGE_2))).isFalse();
+        assertEventLogged(EventLogTags.BACKUP_INITIALIZE);
+    }
+
+    @Test
+    public void testRunTask_whenOnePackageAndWithPmState_doesNotInitializeTransportOrResetState()
+            throws Exception {
+        TransportMock transportMock = setUpTransport(mTransport);
+        setUpAgentsWithData(PACKAGE_1, PACKAGE_2);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+        createPmStateFile();
+        Files.write(getStateFile(mTransport, PACKAGE_2), "package2State".getBytes());
+
+        runTask(task);
+
+        verify(transportMock.transport, never()).initializeDevice();
+        assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_2)))
+                .isEqualTo("package2State".getBytes());
+    }
+
+    @Test
+    public void testRunTask_whenTransportReturnsErrorForInitialization() throws Exception {
+        TransportMock transportMock = setUpTransport(mTransport);
+        when(transportMock.transport.initializeDevice())
+                .thenReturn(BackupTransport.TRANSPORT_ERROR);
+        AgentMock agentMock = setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+        deletePmStateFile();
+
+        runTask(task);
+
+        // First for initialization and second because of the transport failure
+        verify(mBackupManagerService, times(2))
+                .resetBackupState(getStateDirectory(mTransport).toFile());
+        verify(agentMock.agent, never()).onBackup(any(), any(), any());
+        verify(transportMock.transport, never()).performBackup(any(), any(), anyInt());
+        assertBackupPendingFor(PACKAGE_1);
+        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
+    }
+
+    @Test
+    public void testRunTask_whenTransportThrowsDuringInitialization() throws Exception {
+        TransportMock transportMock = setUpTransport(mTransport);
+        when(transportMock.transport.initializeDevice()).thenThrow(RemoteException.class);
+        AgentMock agentMock = setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+        deletePmStateFile();
+
+        runTask(task);
+
+        // First for initialization and second because of the transport failure
+        verify(mBackupManagerService, times(2))
+                .resetBackupState(getStateDirectory(mTransport).toFile());
+        verify(agentMock.agent, never()).onBackup(any(), any(), any());
+        verify(transportMock.transport, never()).performBackup(any(), any(), anyInt());
+        assertBackupPendingFor(PACKAGE_1);
+    }
+
+    @Test
+    public void testRunTask_whenPackageNotEligibleForBackup() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgentWithData(PACKAGE_1.backupNotAllowed());
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(agentMock.agent, never()).onBackup(any(), any(), any());
+        verify(transportMock.transport, never())
+                .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_BACKUP_NOT_ALLOWED);
+        verify(mObserver).backupFinished(SUCCESS);
+        assertBackupNotPendingFor(PACKAGE_1);
+    }
+
+    @Test
+    public void testRunTask_whenPackageDoesFullBackup() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        PackageData packageData = fullBackupPackage(1);
+        AgentMock agentMock = setUpAgentWithData(packageData);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, packageData);
+
+        runTask(task);
+
+        verify(agentMock.agent, never()).onBackup(any(), any(), any());
+        verify(agentMock.agent, never()).onFullBackup(any());
+        verify(mObserver).onResult(packageData.packageName, ERROR_BACKUP_NOT_ALLOWED);
+        verify(mObserver).backupFinished(SUCCESS);
+        assertBackupNotPendingFor(PACKAGE_1);
+    }
+
+    @Test
+    public void testRunTask_whenPackageIsStopped() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgentWithData(PACKAGE_1.stopped());
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(agentMock.agent, never()).onBackup(any(), any(), any());
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_BACKUP_NOT_ALLOWED);
+        verify(mObserver).backupFinished(SUCCESS);
+        assertBackupNotPendingFor(PACKAGE_1);
+    }
+
+    @Test
+    public void testRunTask_whenPackageUnknown() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        // Not calling setUpAgent()
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(transportMock.transport, never())
+                .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_PACKAGE_NOT_FOUND);
+        verify(mObserver).backupFinished(SUCCESS);
+        assertBackupNotPendingFor(PACKAGE_1);
+    }
+
+    @Test
+    public void testRunTask_whenCallingAgent_setsWakeLockWorkSource() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    // In production (for non-system agents) the call is asynchronous, but here is
+                    // synchronous, so it's fine to verify here.
+                    // Verify has set work source and hasn't unset yet.
+                    verify(mBackupManagerService)
+                            .setWorkSource(
+                                    argThat(workSource -> workSource.get(0) == PACKAGE_1.uid));
+                    verify(mBackupManagerService, never()).setWorkSource(null);
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        // More verifications inside agent call above
+        verify(mBackupManagerService).setWorkSource(null);
+    }
+
+    /**
+     * Agent unavailable means {@link BackupManagerService#bindToAgentSynchronous(ApplicationInfo,
+     * int)} returns {@code null}.
+     *
+     * @see #setUpAgent(PackageData)
+     */
+    @Test
+    public void testRunTask_whenAgentUnavailable() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgent(PACKAGE_1.unavailable());
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mBackupManagerService).setWorkSource(null);
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
+        verify(mObserver).backupFinished(BackupManager.SUCCESS);
+    }
+
+    @Test
+    public void testRunTask_whenBindToAgentThrowsSecurityException() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgent(PACKAGE_1);
+        doThrow(SecurityException.class)
+                .when(mBackupManagerService)
+                .bindToAgentSynchronous(argThat(applicationInfo(PACKAGE_1)), anyInt());
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mBackupManagerService).setWorkSource(null);
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
+        verify(mObserver).backupFinished(BackupManager.SUCCESS);
+    }
+
+    @Test
+    public void testRunTask_whenTransportGetBackupQuotaThrows_notifiesCorrectly() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.getBackupQuota(PACKAGE_1.packageName, false))
+                .thenThrow(DeadObjectException.class);
+        setUpAgent(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mObserver, never()).onResult(eq(PACKAGE_1.packageName), anyInt());
+        verify(mObserver).backupFinished(ERROR_TRANSPORT_ABORTED);
+        verify(mListener).onFinished(any());
+    }
+
+    @Test
+    public void testRunTask_whenTransportGetBackupQuotaThrows_cleansUp() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.getBackupQuota(PACKAGE_1.packageName, false))
+                .thenThrow(DeadObjectException.class);
+        setUpAgent(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mBackupManagerService).setWorkSource(null);
+        verify(mBackupManagerService).unbindAgent(argThat(applicationInfo(PACKAGE_1)));
+    }
+
+    @Test
+    public void testRunTask_whenTransportGetBackupQuotaThrows_doesNotTouchFiles() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.getBackupQuota(PACKAGE_1.packageName, false))
+                .thenThrow(DeadObjectException.class);
+        setUpAgent(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+        Files.write(getStateFile(mTransport, PACKAGE_1), "packageState".getBytes());
+
+        runTask(task);
+
+        assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
+        assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+        assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1)))
+                .isEqualTo("packageState".getBytes());
+    }
+
+    @Test
+    public void testRunTask_whenTransportGetBackupQuotaThrows_revertsOperation() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.getBackupQuota(PACKAGE_1.packageName, false))
+                .thenThrow(DeadObjectException.class);
+        setUpAgent(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(transportMock.transport).requestBackupTime();
+        assertBackupPendingFor(PACKAGE_1);
+        assertThat(KeyValueBackupJob.isScheduled()).isTrue();
+    }
+
+    /**
+     * For local agents the exception is thrown in our stack, so it hits the catch clause around
+     * invocation earlier than the {@link KeyValueBackupTask#operationComplete(long)} code-path,
+     * invalidating the latter. Note that this happens because {@link
+     * BackupManagerService#opComplete(int, long)} schedules the actual execution to the backup
+     * handler.
+     */
+    @Test
+    public void testRunTask_whenLocalAgentOnBackupThrows() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    throw new RuntimeException();
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mBackupManagerService).setWorkSource(null);
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
+        verify(mObserver).backupFinished(SUCCESS);
+        assertEventLogged(
+                EventLogTags.BACKUP_AGENT_FAILURE,
+                PACKAGE_1.packageName,
+                new RuntimeException().toString());
+        assertBackupPendingFor(PACKAGE_1);
+    }
+
+    @Test
+    public void testRunTask_whenTransportProvidesFlags_passesThemToTheAgent() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
+        when(transportMock.transport.getTransportFlags()).thenReturn(flags);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(agentMock.agent)
+                .onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
+    }
+
+    @Test
+    public void testRunTask_whenTransportDoesNotProvidesFlags() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(agentMock.agent).onBackup(any(), argThat(dataOutputWithTransportFlags(0)), any());
+    }
+
+    @Test
+    public void testRunTask_whenTransportProvidesFlagsAndMultipleAgents_passesToAll()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
+        when(transportMock.transport.getTransportFlags()).thenReturn(flags);
+        List<AgentMock> agentMocks = setUpAgents(PACKAGE_1, PACKAGE_2);
+        BackupAgent agent1 = agentMocks.get(0).agent;
+        BackupAgent agent2 = agentMocks.get(1).agent;
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient,
+                        mTransport.transportDirName,
+                        PACKAGE_1,
+                        PACKAGE_2);
+
+        runTask(task);
+
+        verify(agent1).onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
+        verify(agent2).onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
+    }
+
+    @Test
+    public void testRunTask_whenTransportChangeFlagsAfterTaskCreation() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+        int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
+        when(transportMock.transport.getTransportFlags()).thenReturn(flags);
+
+        runTask(task);
+
+        verify(agentMock.agent)
+                .onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
+    }
+
+    @Test
+    public void testRunTask_whenAgentUsesProhibitedKey_failsAgent() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    char prohibitedChar = 0xff00;
+                    writeData(dataOutput, prohibitedChar + "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(agentMock.agentBinder).fail(any());
+        verify(mBackupManagerService).unbindAgent(argThat(applicationInfo(PACKAGE_1)));
+    }
+
+    @Test
+    public void testRunTask_whenAgentUsesProhibitedKey_updatesAndCleansUpFiles() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    char prohibitedChar = 0xff00;
+                    writeData(dataOutput, prohibitedChar + "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+        Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
+
+        runTask(task);
+
+        assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1)))
+                .isEqualTo("oldState".getBytes());
+        assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
+        assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+    }
+
+    @Test
+    public void testRunTask_whenAgentUsesProhibitedKey_doesNotCallTransport() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    char prohibitedChar = 0xff00;
+                    writeData(dataOutput, prohibitedChar + "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+        Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
+
+        runTask(task);
+
+        verify(transportMock.transport, never())
+                .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
+    }
+
+    @Test
+    public void testRunTask_whenAgentUsesProhibitedKey_notifiesCorrectly() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    char prohibitedChar = 0xff00;
+                    writeData(dataOutput, prohibitedChar + "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+        Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
+
+        runTask(task);
+
+        verify(mListener).onFinished(any());
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
+        verify(mObserver).backupFinished(SUCCESS);
+    }
+
+    @Test
+    public void testRunTask_whenAgentUsesProhibitedKey_logsAgentFailureEvent() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    char prohibitedChar = 0xff00;
+                    writeData(dataOutput, prohibitedChar + "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+        Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
+
+        runTask(task);
+
+        assertEventLogged(EventLogTags.BACKUP_AGENT_FAILURE, PACKAGE_1.packageName, "bad key");
+    }
+
+    @Test
+    public void testRunTask_whenFirstAgentUsesProhibitedKeyButLastAgentUsesPermittedKey()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        List<AgentMock> agentMocks = setUpAgents(PACKAGE_1, PACKAGE_2);
+        AgentMock agentMock1 = agentMocks.get(0);
+        AgentMock agentMock2 = agentMocks.get(1);
+        agentOnBackupDo(
+                agentMock1,
+                (oldState, dataOutput, newState) -> {
+                    char prohibitedChar = 0xff00;
+                    writeData(dataOutput, prohibitedChar + "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
+                });
+        agentOnBackupDo(
+                agentMock2,
+                (oldState, dataOutput, newState) -> {
+                    writeData(dataOutput, "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient,
+                        mTransport.transportDirName,
+                        PACKAGE_1,
+                        PACKAGE_2);
+
+        runTask(task);
+
+        verify(mListener).onFinished(any());
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
+        verify(agentMock1.agentBinder).fail(any());
+        verify(mObserver).onResult(PACKAGE_2.packageName, SUCCESS);
+        verify(mObserver).backupFinished(SUCCESS);
+    }
+
+    @Test
+    public void testRunTask_whenAgentDoesNotWriteData_doesNotCallTransport() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    // No-op
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(transportMock.transport, never())
+                .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
+    }
+
+    @Test
+    public void testRunTask_whenAgentDoesNotWriteData_logsEvents() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    // No-op
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        assertEventLogged(EventLogTags.BACKUP_PACKAGE, PACKAGE_1.packageName, 0L);
+        verify(mBackupManagerService).logBackupComplete(PACKAGE_1.packageName);
+    }
+
+    @Test
+    public void testRunTask_whenAgentDoesNotWriteData_notifiesCorrectly() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    // No-op
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mObserver).onResult(PACKAGE_1.packageName, SUCCESS);
+        verify(mObserver).backupFinished(SUCCESS);
+    }
+
+    @Test
+    public void testRunTask_whenAgentDoesNotWriteData_updatesBookkeeping() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    // No-op
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        assertBackupNotPendingFor(PACKAGE_1);
+    }
+
+    @Test
+    public void testRunTask_whenAgentDoesNotWriteData_updatesAndCleansUpFiles() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    // No-op
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1))).isEqualTo(new byte[0]);
+        assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
+        assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+    }
+
+    @Test
+    public void testRunTask_whenAgentWritesData_callsTransportPerformBackupWithAgentData()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        Path backupDataPath = createTemporaryFile();
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .then(copyBackupDataTo(backupDataPath));
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    writeData(dataOutput, "key1", "data1".getBytes());
+                    writeData(dataOutput, "key2", "data2".getBytes());
+                    writeState(newState, "newState".getBytes());
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(transportMock.transport)
+                .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
+        // Now verify data sent
+        try (FileInputStream inputStream = new FileInputStream(backupDataPath.toFile())) {
+            BackupDataInput backupData = new BackupDataInput(inputStream.getFD());
+            assertDataHasKeyValue(backupData, "key1", "data1".getBytes());
+            assertDataHasKeyValue(backupData, "key2", "data2".getBytes());
+            assertThat(backupData.readNextHeader()).isFalse();
+        }
+    }
+
+    @Test
+    public void testRunTask_whenPerformBackupSucceeds_callsTransportFinishBackup()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_OK);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        // First for PM, then for the package
+        verify(transportMock.transport, times(2)).finishBackup();
+    }
+
+    @Test
+    public void testRunTask_whenFinishBackupSucceeds_updatesAndCleansUpFiles() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    writeData(dataOutput, "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1)))
+                .isEqualTo("newState".getBytes());
+        assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
+        assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+    }
+
+    @Test
+    public void testRunTask_whenFinishBackupSucceeds_logsBackupPackageEvent() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgentWithData(PACKAGE_1);
+        Path backupData = createTemporaryFile();
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .then(copyBackupDataTo(backupData));
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        assertEventLogged(
+                EventLogTags.BACKUP_PACKAGE, PACKAGE_1.packageName, Files.size(backupData));
+    }
+
+    @Test
+    public void testRunTask_whenFinishBackupSucceeds_notifiesCorrectly() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mBackupManagerService).logBackupComplete(PACKAGE_1.packageName);
+        verify(mObserver).onResult(PACKAGE_1.packageName, SUCCESS);
+        verify(mObserver).backupFinished(SUCCESS);
+        verify(mListener).onFinished(any());
+    }
+
+    @Test
+    public void testRunTask_whenFinishBackupSucceeds_updatesBookkeeping() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        assertBackupNotPendingFor(PACKAGE_1);
+    }
+
+    @Test
+    public void testRunTask_whenTransportRejectsPackage_doesNotCallFinishBackup() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        // Called only for PM
+        verify(transportMock.transport, times(1)).finishBackup();
+    }
+
+    @Test
+    public void testRunTask_whenTransportRejectsPackage_updatesAndCleansUpFiles() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+        Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
+
+        runTask(task);
+
+        assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1)))
+                .isEqualTo("oldState".getBytes());
+        assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
+        assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+    }
+
+    @Test
+    public void testRunTask_whenTransportRejectsPackage_logsAgentFailureEvent() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        assertEventLogged(
+                EventLogTags.BACKUP_AGENT_FAILURE, PACKAGE_1.packageName, "Transport rejected");
+    }
+
+    @Test
+    public void testRunTask_whenTransportRejectsPackage_notifiesCorrectly() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_TRANSPORT_PACKAGE_REJECTED);
+        verify(mObserver).backupFinished(SUCCESS);
+        verify(mListener).onFinished(any());
+    }
+
+    @Test
+    public void testRunTask_whenTransportRejectsPackage_updatesBookkeeping() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        assertBackupNotPendingFor(PACKAGE_1);
+    }
+
+    @Test
+    public void testRunTask_whenTransportRejectsFirstPackageButLastSucceeds() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_2)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_OK);
+        setUpAgentsWithData(PACKAGE_1, PACKAGE_2);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient,
+                        mTransport.transportDirName,
+                        PACKAGE_1,
+                        PACKAGE_2);
+
+        runTask(task);
+
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_TRANSPORT_PACKAGE_REJECTED);
+        verify(mObserver).onResult(PACKAGE_2.packageName, SUCCESS);
+        verify(mObserver).backupFinished(SUCCESS);
+    }
+
+    @Test
+    public void testRunTask_whenTransportRejectsLastPackageButFirstSucceeds() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_OK);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_2)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+        setUpAgentsWithData(PACKAGE_1, PACKAGE_2);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient,
+                        mTransport.transportDirName,
+                        PACKAGE_1,
+                        PACKAGE_2);
+
+        runTask(task);
+
+        verify(mObserver).onResult(PACKAGE_1.packageName, SUCCESS);
+        verify(mObserver).onResult(PACKAGE_2.packageName, ERROR_TRANSPORT_PACKAGE_REJECTED);
+        verify(mObserver).backupFinished(SUCCESS);
+    }
+
+    @Test
+    public void testRunTask_whenTransportReturnsQuotaExceeded() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.getBackupQuota(PACKAGE_1.packageName, false))
+                .thenReturn(1234L);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_QUOTA_EXCEEDED);
+        AgentMock agentMock = setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mObserver)
+                .onResult(PACKAGE_1.packageName, BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
+        verify(mObserver).backupFinished(SUCCESS);
+        verify(agentMock.agent).onQuotaExceeded(anyLong(), eq(1234L));
+        assertEventLogged(EventLogTags.BACKUP_QUOTA_EXCEEDED, PACKAGE_1.packageName);
+        assertBackupNotPendingFor(PACKAGE_1);
+    }
+
+    @Test
+    public void testRunTask_whenNonIncrementalAndTransportRequestsNonIncremental()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient,
+                        mTransport.transportDirName,
+                        true,
+                        PACKAGE_1);
+        // Delete to be non-incremental
+        Files.deleteIfExists(getStateFile(mTransport, PACKAGE_1));
+
+        runTask(task);
+
+        // Error because it was non-incremental already, so transport can't request it
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_TRANSPORT_ABORTED);
+        verify(mObserver).backupFinished(ERROR_TRANSPORT_ABORTED);
+    }
+
+    @Test
+    public void testRunTask_whenIncrementalAndTransportRequestsNonIncremental() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        Path incrementalData = createTemporaryFile();
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)),
+                        any(),
+                        intThat(flags -> (flags & BackupTransport.FLAG_INCREMENTAL) != 0)))
+                .thenAnswer(
+                        copyBackupDataAndReturn(
+                                incrementalData,
+                                BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED));
+        Path nonIncrementalData = createTemporaryFile();
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)),
+                        any(),
+                        intThat(flags -> (flags & BackupTransport.FLAG_NON_INCREMENTAL) != 0)))
+                .thenAnswer(
+                        copyBackupDataAndReturn(nonIncrementalData, BackupTransport.TRANSPORT_OK));
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    // agentMock.oldState has already been updated by now.
+                    if (agentMock.oldState.length > 0) {
+                        writeData(dataOutput, "key", "dataForIncremental".getBytes());
+                        writeState(newState, "stateForIncremental".getBytes());
+                    } else {
+                        writeData(dataOutput, "key", "dataForNonIncremental".getBytes());
+                        writeState(newState, "stateForNonIncremental".getBytes());
+                    }
+                });
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient,
+                        mTransport.transportDirName,
+                        false,
+                        PACKAGE_1);
+        // Write state to be incremental
+        Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
+
+        runTask(task);
+
+        verify(agentMock.agent, times(2)).onBackup(any(), any(), any());
+        byte[] oldStateDuringIncremental = agentMock.oldStateHistory.get(0);
+        byte[] oldStateDuringNonIncremental = agentMock.oldStateHistory.get(1);
+        assertThat(oldStateDuringIncremental).isEqualTo("oldState".getBytes());
+        assertThat(oldStateDuringNonIncremental).isEqualTo(new byte[0]);
+        assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1)))
+                .isEqualTo("stateForNonIncremental".getBytes());
+        try (FileInputStream inputStream = new FileInputStream(incrementalData.toFile())) {
+            BackupDataInput backupData = new BackupDataInput(inputStream.getFD());
+            assertDataHasKeyValue(backupData, "key", "dataForIncremental".getBytes());
+            assertThat(backupData.readNextHeader()).isFalse();
+        }
+        try (FileInputStream inputStream = new FileInputStream(nonIncrementalData.toFile())) {
+            BackupDataInput backupData = new BackupDataInput(inputStream.getFD());
+            assertDataHasKeyValue(backupData, "key", "dataForNonIncremental".getBytes());
+            assertThat(backupData.readNextHeader()).isFalse();
+        }
+        verify(mObserver).onResult(PACKAGE_1.packageName, SUCCESS);
+        verify(mObserver).backupFinished(SUCCESS);
+    }
+
+    @Test
+    public void testRunTask_whenTransportReturnsError_notifiesCorrectly() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_ERROR);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_TRANSPORT_ABORTED);
+        verify(mObserver).backupFinished(ERROR_TRANSPORT_ABORTED);
+    }
+
+    @Test
+    public void testRunTask_whenTransportReturnsError_logsBackupTransportFailureEvent()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_ERROR);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_FAILURE, PACKAGE_1.packageName);
+    }
+
+    @Test
+    public void testRunTask_whenTransportReturnsError_revertsOperation() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_ERROR);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(transportMock.transport).requestBackupTime();
+        assertBackupPendingFor(PACKAGE_1);
+        assertThat(KeyValueBackupJob.isScheduled()).isTrue();
+    }
+
+    @Test
+    public void testRunTask_whenTransportReturnsError_updatesAndCleansUpFiles() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_ERROR);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+        Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
+
+        runTask(task);
+
+        assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1)))
+                .isEqualTo("oldState".getBytes());
+        // TODO: These should be true (Bug)
+        // assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
+        // assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+    }
+
+    @Test
+    public void testRunTask_whenTransportGetBackupQuotaThrowsForPm() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.getBackupQuota(PM_PACKAGE.packageName, false))
+                .thenThrow(DeadObjectException.class);
+        setUpAgentWithData(PACKAGE_1);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mListener).onFinished(any());
+        verify(mObserver).backupFinished(ERROR_TRANSPORT_ABORTED);
+        assertEventLogged(
+                EventLogTags.BACKUP_AGENT_FAILURE,
+                PM_PACKAGE.packageName,
+                new DeadObjectException().toString());
+    }
+
+    @Test
+    public void testRunTask_whenPmAgentFails() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        PackageManagerBackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
+        when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mListener).onFinished(any());
+        verify(mObserver).backupFinished(eq(ERROR_TRANSPORT_ABORTED));
+        assertEventLogged(
+                EventLogTags.BACKUP_AGENT_FAILURE,
+                PM_PACKAGE.packageName,
+                new RuntimeException().toString());
+    }
+
+    @Test
+    public void testRunTask_whenBackupRunning_doesNotThrow() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(mBackupManagerService.isBackupOperationInProgress()).thenReturn(true);
+        KeyValueBackupTask task =
+                createKeyValueBackupTask(
+                        transportMock.transportClient, mTransport.transportDirName);
+
+        runTask(task);
+    }
+
+    private void runTask(KeyValueBackupTask task) {
+        // Pretend we are not on the main-thread to prevent RemoteCall from complaining
+        mShadowMainLooper.setCurrentThread(false);
+        task.run();
+        mShadowMainLooper.reset();
+        assertTaskPostConditions();
+    }
+
+    private TransportMock setUpTransport(TransportData transport) throws Exception {
+        TransportMock transportMock =
+                TransportTestUtils.setUpTransport(mTransportManager, transport);
+        Files.createDirectories(getStateDirectory(transport));
+        return transportMock;
+    }
+
+    /** Sets up the transport and writes a PM state file in the transport state directory. */
+    private TransportMock setUpInitializedTransport(TransportData transport) throws Exception {
+        TransportMock transportMock = setUpTransport(transport);
+        createPmStateFile(transport);
+        return transportMock;
+    }
+
+    private Path getStateDirectory(TransportData transport) {
+        return mBaseStateDir.toPath().resolve(transport.transportDirName);
+    }
+
+    private Path getStateFile(TransportData transport, PackageData packageData) {
+        return getStateDirectory(transport).resolve(packageData.packageName);
+    }
+
+    private Path getTemporaryStateFile(TransportData transport, PackageData packageData) {
+        return getStateDirectory(transport)
+                .resolve(packageData.packageName + KeyValueBackupTask.NEW_STATE_FILE_SUFFIX);
+    }
+
+    private Path getStagingDirectory() {
+        return mDataDir.toPath();
+    }
+
+    private Path getStagingFile(PackageData packageData) {
+        return getStagingDirectory()
+                .resolve(packageData.packageName + KeyValueBackupTask.STAGING_FILE_SUFFIX);
+    }
+
+    private List<AgentMock> setUpAgents(PackageData... packageNames) {
+        return Stream.of(packageNames).map(this::setUpAgent).collect(toList());
+    }
+
+    private AgentMock setUpAgent(PackageData packageData) {
+        try {
+            mShadowPackageManager.setApplicationEnabledSetting(
+                    packageData.packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+            PackageInfo packageInfo = getPackageInfo(packageData);
+            mShadowPackageManager.addPackage(packageInfo);
+            mShadowApplication.sendBroadcast(getPackageAddedIntent(packageData));
+            // Run the backup looper because on the receiver we post MSG_SCHEDULE_BACKUP_PACKAGE
+            mShadowBackupLooper.runToEndOfTasks();
+            BackupAgent backupAgent = spy(BackupAgent.class);
+            IBackupAgent backupAgentBinder =
+                    spy(IBackupAgent.Stub.asInterface(backupAgent.onBind()));
+            // Don't crash our only process (in production code this would crash the app, not us)
+            doNothing().when(backupAgentBinder).fail(any());
+            if (packageData.available) {
+                doReturn(backupAgentBinder)
+                        .when(mBackupManagerService)
+                        .bindToAgentSynchronous(argThat(applicationInfo(packageData)), anyInt());
+            } else {
+                doReturn(null)
+                        .when(mBackupManagerService)
+                        .bindToAgentSynchronous(argThat(applicationInfo(packageData)), anyInt());
+            }
+            return new AgentMock(backupAgentBinder, backupAgent);
+        } catch (RemoteException e) {
+            // Never happens, compiler happy
+            throw new AssertionError(e);
+        }
+    }
+
+    private PackageInfo getPackageInfo(PackageData packageData) {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.packageName = packageData.packageName;
+        packageInfo.applicationInfo = new ApplicationInfo();
+        packageInfo.applicationInfo.uid = packageData.uid;
+        packageInfo.applicationInfo.flags = packageData.flags();
+        packageInfo.applicationInfo.backupAgentName = packageData.agentName;
+        packageInfo.applicationInfo.packageName = packageData.packageName;
+        return packageInfo;
+    }
+
+    private Intent getPackageAddedIntent(PackageData packageData) {
+        Intent intent =
+                new Intent(
+                        Intent.ACTION_PACKAGE_ADDED,
+                        Uri.parse("package:" + packageData.packageName));
+        intent.putExtra(Intent.EXTRA_UID, packageData.uid);
+        intent.putExtra(Intent.EXTRA_REPLACING, false);
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, 0);
+        return intent;
+    }
+
+    private List<AgentMock> setUpAgentsWithData(PackageData... packages) {
+        return Stream.of(packages).map(this::setUpAgentWithData).collect(toList());
+    }
+
+    private AgentMock setUpAgentWithData(PackageData packageData) {
+        AgentMock agentMock = setUpAgent(packageData);
+        String packageName = packageData.packageName;
+        uncheck(
+                () ->
+                        agentOnBackupDo(
+                                agentMock,
+                                (oldState, dataOutput, newState) -> {
+                                    writeData(dataOutput, "key", ("data" + packageName).getBytes());
+                                    writeState(newState, ("state" + packageName).getBytes());
+                                }));
+        return agentMock;
+    }
+
+    private KeyValueBackupTask createKeyValueBackupTask(
+            TransportClient transportClient, String transportDirName, PackageData... packages) {
+        return createKeyValueBackupTask(transportClient, transportDirName, false, packages);
+    }
+
+    private KeyValueBackupTask createKeyValueBackupTask(
+            TransportClient transportClient,
+            String transportDirName,
+            boolean nonIncremental,
+            PackageData... packages) {
+        ArrayList<BackupRequest> keyValueBackupRequests =
+                Stream.of(packages)
+                        .map(packageData -> packageData.packageName)
+                        .map(BackupRequest::new)
+                        .collect(toCollection(ArrayList::new));
+        mBackupManagerService.getPendingBackups().clear();
+        // mOldJournal is a mock, but it would be the value returned by BMS.getJournal() now
+        mBackupManagerService.setJournal(null);
+        mWakeLock.acquire();
+        KeyValueBackupTask task =
+                new KeyValueBackupTask(
+                        mBackupManagerService,
+                        transportClient,
+                        transportDirName,
+                        keyValueBackupRequests,
+                        mOldJournal,
+                        mObserver,
+                        mMonitor,
+                        mListener,
+                        emptyList(),
+                        /* userInitiated */ false,
+                        nonIncremental);
+        mBackupManager.setUp(mBackupHandler, task);
+        return task;
+    }
+
+    private PackageManagerBackupAgent createPmAgent() {
+        PackageManagerBackupAgent pmAgent =
+                new PackageManagerBackupAgent(mApplication.getPackageManager());
+        pmAgent.attach(mApplication);
+        pmAgent.onCreate();
+        return pmAgent;
+    }
+
+    /**
+     * Returns an implementation of PackageManagerBackupAgent that throws RuntimeException in {@link
+     * BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
+     */
+    private PackageManagerBackupAgent createThrowingPmAgent(RuntimeException exception) {
+        PackageManagerBackupAgent pmAgent =
+                new ThrowingPackageManagerBackupAgent(mApplication.getPackageManager(), exception);
+        pmAgent.attach(mApplication);
+        pmAgent.onCreate();
+        return pmAgent;
+    }
+
+    /** Matches {@link PackageInfo} whose package name is {@code packageData.packageName}. */
+    private static ArgumentMatcher<PackageInfo> packageInfo(PackageData packageData) {
+        // We have to test for packageInfo nulity because of Mockito's own stubbing with argThat().
+        // E.g. if you do:
+        //
+        //   1. when(object.method(argThat(str -> str.equals("foo")))).thenReturn(0)
+        //   2. when(object.method(argThat(str -> str.equals("bar")))).thenReturn(2)
+        //
+        // The second line will throw NPE because it will call lambda 1 with null, since argThat()
+        // returns null. So we guard against that by checking for null.
+        return packageInfo ->
+                packageInfo != null && packageData.packageName.equals(packageInfo.packageName);
+    }
+
+    /** Matches {@link ApplicationInfo} whose package name is {@code packageData.packageName}. */
+    private static ArgumentMatcher<ApplicationInfo> applicationInfo(PackageData packageData) {
+        return applicationInfo ->
+                applicationInfo != null
+                        && packageData.packageName.equals(applicationInfo.packageName);
+    }
+
+    private static ArgumentMatcher<BackupDataOutput> dataOutputWithTransportFlags(int flags) {
+        return dataOutput -> dataOutput.getTransportFlags() == flags;
+    }
+
+    private static void writeData(BackupDataOutput dataOutput, String key, byte[] data)
+            throws IOException {
+        dataOutput.writeEntityHeader(key, data.length);
+        dataOutput.writeEntityData(data, data.length);
+    }
+
+    private static void writeState(ParcelFileDescriptor newState, byte[] state) throws IOException {
+        OutputStream outputStream = new FileOutputStream(newState.getFileDescriptor());
+        outputStream.write(state);
+        outputStream.flush();
+    }
+
+    /**
+     * This is to prevent the following:
+     *
+     * <ul>
+     *   <li>The transport being initialized with {@link IBackupTransport#initializeDevice()}
+     *   <li>{@link BackupManagerService#resetBackupState(File)} being called, which will:
+     *       <ul>
+     *         <li>Reset processed packages journal.
+     *         <li>Reset current token to 0.
+     *         <li>Delete state files.
+     *         <li>Mark data changed for every key-value participant.
+     *       </ul>
+     * </ul>
+     */
+    private void createPmStateFile() throws IOException {
+        createPmStateFile(mTransport);
+    }
+
+    /** @see #createPmStateFile() */
+    private void createPmStateFile(TransportData transport) throws IOException {
+        Files.write(getStateFile(transport, PM_PACKAGE), "pmState".getBytes());
+    }
+
+    /**
+     * Forces transport initialization and call to {@link
+     * BackupManagerService#resetBackupState(File)}
+     */
+    private void deletePmStateFile() throws IOException {
+        Files.deleteIfExists(getStateFile(mTransport, PM_PACKAGE));
+    }
+
+    /**
+     * Implements {@code function} for {@link BackupAgent#onBackup(ParcelFileDescriptor,
+     * BackupDataOutput, ParcelFileDescriptor)} of {@code agentMock} and populates {@link
+     * AgentMock#oldState}.
+     */
+    private static void agentOnBackupDo(AgentMock agentMock, BackupAgentOnBackup function)
+            throws Exception {
+        doAnswer(
+                        (BackupAgentOnBackup)
+                                (oldState, dataOutput, newState) -> {
+                                    ByteArrayOutputStream outputStream =
+                                            new ByteArrayOutputStream();
+                                    Utils.transferStreamedData(
+                                            new FileInputStream(oldState.getFileDescriptor()),
+                                            outputStream);
+                                    agentMock.oldState = outputStream.toByteArray();
+                                    agentMock.oldStateHistory.add(agentMock.oldState);
+                                    function.onBackup(oldState, dataOutput, newState);
+                                })
+                .when(agentMock.agent)
+                .onBackup(any(), any(), any());
+    }
+
+    /**
+     * Returns an {@link Answer} that can be used for mocking {@link
+     * IBackupTransport#performBackup(PackageInfo, ParcelFileDescriptor, int)} that copies the
+     * backup data received to {@code backupDataPath} and returns {@code result}.
+     */
+    private static Answer<Integer> copyBackupDataAndReturn(Path backupDataPath, int result) {
+        return invocation -> {
+            ParcelFileDescriptor backupDataParcelFd = invocation.getArgument(1);
+            FileDescriptor backupDataFd = backupDataParcelFd.getFileDescriptor();
+            Files.copy(new FileInputStream(backupDataFd), backupDataPath, REPLACE_EXISTING);
+            backupDataParcelFd.close();
+            return result;
+        };
+    }
+
+    /**
+     * Same as {@link #copyBackupDataAndReturn(Path, int)}} with {@code result =
+     * BackupTransport.TRANSPORT_OK}.
+     */
+    private static Answer<Integer> copyBackupDataTo(Path backupDataPath) {
+        return copyBackupDataAndReturn(backupDataPath, BackupTransport.TRANSPORT_OK);
+    }
+
+    private Path createTemporaryFile() throws IOException {
+        return Files.createTempFile(mContext.getCacheDir().toPath(), "backup", ".tmp");
+    }
+
+    private static IterableSubject<
+                    ? extends IterableSubject<?, Path, Iterable<Path>>, Path, Iterable<Path>>
+            assertDirectory(Path directory) throws IOException {
+        return assertThat(oneTimeIterable(Files.newDirectoryStream(directory).iterator()))
+                .named("directory " + directory);
+    }
+
+    private static void assertJournalDoesNotContain(
+            @Nullable DataChangedJournal journal, String packageName) throws IOException {
+        List<String> packages = (journal == null) ? emptyList() : journal.getPackages();
+        assertThat(packages).doesNotContain(packageName);
+    }
+
+    private void assertBackupPendingFor(PackageData packageData) throws IOException {
+        String packageName = packageData.packageName;
+        // We verify the current journal, NOT the old one passed to KeyValueBackupTask constructor
+        assertThat(mBackupManagerService.getJournal().getPackages()).contains(packageName);
+        assertThat(mBackupManagerService.getPendingBackups()).containsKey(packageName);
+    }
+
+    private void assertBackupNotPendingFor(PackageData packageData) throws IOException {
+        String packageName = packageData.packageName;
+        // We verify the current journal, NOT the old one passed to KeyValueBackupTask constructor
+        assertJournalDoesNotContain(mBackupManagerService.getJournal(), packageName);
+        assertThat(mBackupManagerService.getPendingBackups()).doesNotContainKey(packageName);
+    }
+
+    private void assertDataHasKeyValue(BackupDataInput backupData, String key, byte[] value)
+            throws IOException {
+        assertThat(backupData.readNextHeader()).isTrue();
+        assertThat(backupData.getKey()).isEqualTo(key);
+        int size = backupData.getDataSize();
+        byte[] data1 = new byte[size];
+        backupData.readEntityData(data1, 0, size);
+        assertThat(data1).isEqualTo(value);
+    }
+
+    /**
+     * Put conditions that should *always* be true after task execution.
+     *
+     * <p>Note: We should generally NOT do this. For every different set of pre-conditions that
+     * result in different code-paths being executed there should be one test method verifying these
+     * post-conditions. Since there were a couple of methods here already and these post-conditions
+     * are pretty serious to be neglected it was decided to over-verify in this case.
+     */
+    private void assertTaskPostConditions() {
+        assertThat(mWakeLock.isHeld()).isFalse();
+    }
+
+    @FunctionalInterface
+    private interface BackupAgentOnBackup extends Answer<Void> {
+        void onBackup(
+                ParcelFileDescriptor oldState,
+                BackupDataOutput dataOutput,
+                ParcelFileDescriptor newState)
+                throws IOException;
+
+        @Override
+        default Void answer(InvocationOnMock invocation) throws Throwable {
+            onBackup(
+                    invocation.getArgument(0),
+                    invocation.getArgument(1),
+                    invocation.getArgument(2));
+            return null;
+        }
+    }
+
+    private static class AgentMock {
+        private final IBackupAgent agentBinder;
+        private final BackupAgent agent;
+        private final List<byte[]> oldStateHistory = new ArrayList<>();
+        private byte[] oldState;
+
+        private AgentMock(IBackupAgent agentBinder, BackupAgent agent) {
+            this.agentBinder = agentBinder;
+            this.agent = agent;
+        }
+    }
+
+    private abstract static class FakeIBackupManager extends IBackupManager.Stub {
+        private Handler mBackupHandler;
+        private BackupRestoreTask mTask;
+
+        public FakeIBackupManager() {}
+
+        private void setUp(Handler backupHandler, BackupRestoreTask task) {
+            mBackupHandler = backupHandler;
+            mTask = task;
+        }
+
+        @Override
+        public void opComplete(int token, long result) throws RemoteException {
+            assertThat(mTask).isNotNull();
+            Message message =
+                    mBackupHandler.obtainMessage(
+                            BackupHandler.MSG_OP_COMPLETE, Pair.create(mTask, result));
+            mBackupHandler.sendMessage(message);
+        }
+    }
+
+    private static class ThrowingPackageManagerBackupAgent extends PackageManagerBackupAgent {
+        private final RuntimeException mException;
+
+        ThrowingPackageManagerBackupAgent(
+                PackageManager packageManager, RuntimeException exception) {
+            super(packageManager);
+            mException = exception;
+        }
+
+        @Override
+        public void onBackup(
+                ParcelFileDescriptor oldState,
+                BackupDataOutput data,
+                ParcelFileDescriptor newState) {
+            throw mException;
+        }
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java b/services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
new file mode 100644
index 0000000..aec207d
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.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 com.android.server.backup.remote;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.util.concurrent.CompletableFuture;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class FutureBackupCallbackTest {
+    @Test
+    public void testOperationComplete_completesFuture() throws Exception {
+        CompletableFuture<RemoteResult> future = new CompletableFuture<>();
+        FutureBackupCallback callback = new FutureBackupCallback(future);
+
+        callback.operationComplete(7);
+
+        assertThat(future.get()).isEqualTo(RemoteResult.successful(7));
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java b/services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java
new file mode 100644
index 0000000..55db616
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java
@@ -0,0 +1,244 @@
+/*
+ * 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.remote;
+
+import static com.android.server.backup.testing.TestUtils.runToEndOfTasks;
+import static com.android.server.backup.testing.TestUtils.uncheck;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.IBackupCallback;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.testing.TestUtils.ThrowingRunnable;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class RemoteCallTest {
+    /** A {@link RemoteCallable} that calls the callback immediately. */
+    private final RemoteCallable<IBackupCallback> IMMEDIATE_CALLABLE =
+            callback -> callback.operationComplete(0);
+
+    @Mock private RemoteCallable<IBackupCallback> mCallable;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testCall_whenCancelledAndImmediateCallableAndTimeOut0_returnsCancel()
+            throws Exception {
+        RemoteCall remoteCall = new RemoteCall(true, IMMEDIATE_CALLABLE, 0);
+
+        RemoteResult result = runInWorkerThread(remoteCall::call);
+
+        assertThat(result).isEqualTo(RemoteResult.FAILED_CANCELLED);
+    }
+
+    @Test
+    public void testCall_whenCancelledAndImmediateCallableAndTimeOut0_doesNotCallCallable()
+            throws Exception {
+        RemoteCall remoteCall = new RemoteCall(true, IMMEDIATE_CALLABLE, 0);
+
+        runInWorkerThread(remoteCall::call);
+
+        verify(mCallable, never()).call(any());
+    }
+
+    @Test
+    public void testCall_whenImmediateCallableAndTimeOut0AndCancelIsCalledBeforeCall_returnsCancel()
+            throws Exception {
+        RemoteCall remoteCall = new RemoteCall(IMMEDIATE_CALLABLE, 0);
+        remoteCall.cancel();
+
+        RemoteResult result = runInWorkerThread(remoteCall::call);
+
+        assertThat(result).isEqualTo(RemoteResult.FAILED_CANCELLED);
+    }
+
+    @Test
+    public void
+            testCall_whenImmediateCallableAndTimeOut0AndCancelIsCalledBeforeCall_doesNotCallCallable()
+                    throws Exception {
+        RemoteCall remoteCall = new RemoteCall(IMMEDIATE_CALLABLE, 0);
+        remoteCall.cancel();
+
+        runInWorkerThread(remoteCall::call);
+
+        verify(mCallable, never()).call(any());
+    }
+
+    @Test
+    public void testCall_whenImmediateCallableAndTimeOut0_returnsTimeOut() throws Exception {
+        RemoteCall remoteCall = new RemoteCall(IMMEDIATE_CALLABLE, 0);
+
+        RemoteResult result = runInWorkerThread(remoteCall::call);
+
+        assertThat(result).isEqualTo(RemoteResult.FAILED_TIMED_OUT);
+    }
+
+    @Test
+    public void testCall_whenTimeOut0_doesNotCallCallable() throws Exception {
+        RemoteCall remoteCall = new RemoteCall(mCallable, 0);
+
+        runInWorkerThread(remoteCall::call);
+
+        verify(mCallable, never()).call(any());
+    }
+
+    @Test
+    public void testCall_whenTimesOutBeforeCallbackIsCalled_returnsTimeOut() throws Exception {
+        ConditionVariable scheduled = new ConditionVariable(false);
+        RemoteCall remoteCall =
+                new RemoteCall(
+                        callback -> {
+                            postDelayed(
+                                    Handler.getMain(), () -> callback.operationComplete(0), 1000);
+                            scheduled.open();
+                        },
+                        500);
+
+        Future<RemoteResult> result = runInWorkerThreadAsync(remoteCall::call);
+
+        // Method runToEndOfTasks() will execute what was posted to the main handler, which is the
+        // completion of the callback and the time-out (that was scheduled by RemoteCall). But to be
+        // able to execute everything we have to ensure that runToEndOfTasks() is called *after*
+        // everything has been scheduled, that's why we use the condition variable scheduled, that
+        // is set to true (i.e. opened) when everything is scheduled, allowing us to run the tasks.
+        scheduled.block();
+        runToEndOfTasks(Looper.getMainLooper());
+        assertThat(result.get()).isEqualTo(RemoteResult.FAILED_TIMED_OUT);
+    }
+
+    @Test
+    public void testCall_whenTimesOutBeforeCancelIsCalled_returnsTimeOut() throws Exception {
+        ConditionVariable scheduled = new ConditionVariable(false);
+        RemoteCall remoteCall = new RemoteCall(callback -> scheduled.open(), 500);
+
+        Future<RemoteResult> result = runInWorkerThreadAsync(remoteCall::call);
+
+        scheduled.block();
+        runToEndOfTasks(Looper.getMainLooper());
+        remoteCall.cancel();
+        assertThat(result.get()).isEqualTo(RemoteResult.FAILED_TIMED_OUT);
+    }
+
+    @Test
+    public void testCall_whenCallbackIsCalledBeforeTimeOut_returnsSuccess() throws Exception {
+        ConditionVariable scheduled = new ConditionVariable(false);
+        RemoteCall remoteCall =
+                new RemoteCall(
+                        callback -> {
+                            postDelayed(
+                                    Handler.getMain(), () -> callback.operationComplete(3), 500);
+                            scheduled.open();
+                        },
+                        1000);
+
+        Future<RemoteResult> result = runInWorkerThreadAsync(remoteCall::call);
+
+        scheduled.block();
+        runToEndOfTasks(Looper.getMainLooper());
+        assertThat(result.get()).isEqualTo(RemoteResult.successful(3));
+    }
+
+    @Test
+    public void testCall_whenCallbackIsCalledBeforeCancel_returnsSuccess() throws Exception {
+        CompletableFuture<IBackupCallback> callbackFuture = new CompletableFuture<>();
+        RemoteCall remoteCall = new RemoteCall(callbackFuture::complete, 1000);
+
+        Future<RemoteResult> result = runInWorkerThreadAsync(remoteCall::call);
+
+        // callbackFuture.get() will return when callable is executed (i.e. inside
+        // remoteCall.call()), at which point we can complete it.
+        IBackupCallback callback = callbackFuture.get();
+        callback.operationComplete(3);
+        remoteCall.cancel();
+        assertThat(result.get()).isEqualTo(RemoteResult.successful(3));
+    }
+
+    @Test
+    public void testCall_whenCancelIsCalledBeforeCallbackButAfterCall_returnsCancel()
+            throws Exception {
+        CompletableFuture<IBackupCallback> callbackFuture = new CompletableFuture<>();
+        RemoteCall remoteCall = new RemoteCall(callbackFuture::complete, 1000);
+
+        Future<RemoteResult> result = runInWorkerThreadAsync(remoteCall::call);
+
+        IBackupCallback callback = callbackFuture.get();
+        remoteCall.cancel();
+        callback.operationComplete(3);
+        assertThat(result.get()).isEqualTo(RemoteResult.FAILED_CANCELLED);
+    }
+
+    @Test
+    public void testCall_whenCancelIsCalledBeforeTimeOutButAfterCall_returnsCancel()
+            throws Exception {
+        ConditionVariable scheduled = new ConditionVariable(false);
+        RemoteCall remoteCall = new RemoteCall(callback -> scheduled.open(), 1000);
+
+        Future<RemoteResult> result = runInWorkerThreadAsync(remoteCall::call);
+
+        scheduled.block();
+        remoteCall.cancel();
+        runToEndOfTasks(Looper.getMainLooper());
+        assertThat(result.get()).isEqualTo(RemoteResult.FAILED_CANCELLED);
+    }
+
+    private static <T> Future<T> runInWorkerThreadAsync(Callable<T> supplier) {
+        CompletableFuture<T> future = new CompletableFuture<>();
+        new Thread(() -> future.complete(uncheck(supplier)), "test-worker-thread").start();
+        return future;
+    }
+
+    private static <T> T runInWorkerThread(Callable<T> supplier) throws Exception {
+        return runInWorkerThreadAsync(supplier).get();
+    }
+
+    /** Unchecked version of {@link Handler#postDelayed(Runnable, long)}. */
+    private static void postDelayed(Handler handler, ThrowingRunnable runnable, long delayMillis) {
+        handler.postDelayed(() -> uncheck(runnable), delayMillis);
+    }
+
+    /** Unchecked version of {@link Handler#post(Runnable)}. */
+    private static void post(Handler handler, ThrowingRunnable runnable) {
+        handler.post(() -> uncheck(runnable));
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java b/services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java
new file mode 100644
index 0000000..f1c4f27
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.remote;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class RemoteResultTest {
+    @Test
+    public void testSucceeded_whenSuccessfulResult_returnsTrue() {
+        RemoteResult result = RemoteResult.successful(3);
+
+        boolean succeeded = result.succeeded();
+
+        assertThat(succeeded).isTrue();
+    }
+
+    @Test
+    public void testSucceeded_whenFailedResults_returnsFalse() {
+        boolean timeOutSucceeded = RemoteResult.FAILED_TIMED_OUT.succeeded();
+        boolean cancelledSucceeded = RemoteResult.FAILED_CANCELLED.succeeded();
+        boolean threadInterruptedSucceeded = RemoteResult.FAILED_THREAD_INTERRUPTED.succeeded();
+
+        assertThat(timeOutSucceeded).isFalse();
+        assertThat(cancelledSucceeded).isFalse();
+        assertThat(threadInterruptedSucceeded).isFalse();
+    }
+
+    @Test
+    public void testGet_whenSuccessfulResult_returnsValue() {
+        RemoteResult result = RemoteResult.successful(7);
+
+        long value = result.get();
+
+        assertThat(value).isEqualTo(7);
+    }
+
+    @Test
+    public void testGet_whenFailedResult_throws() {
+        RemoteResult result = RemoteResult.FAILED_TIMED_OUT;
+
+        expectThrows(IllegalStateException.class, result::get);
+    }
+
+    @Test
+    public void testToString() {
+        assertThat(RemoteResult.successful(3).toString()).isEqualTo("RemoteResult{3}");
+        assertThat(RemoteResult.FAILED_TIMED_OUT.toString())
+                .isEqualTo("RemoteResult{FAILED_TIMED_OUT}");
+        assertThat(RemoteResult.FAILED_CANCELLED.toString())
+                .isEqualTo("RemoteResult{FAILED_CANCELLED}");
+        assertThat(RemoteResult.FAILED_THREAD_INTERRUPTED.toString())
+                .isEqualTo("RemoteResult{FAILED_THREAD_INTERRUPTED}");
+    }
+
+    @Test
+    public void testEquals() {
+        assertThat(RemoteResult.successful(3).equals(RemoteResult.successful(3))).isTrue();
+        assertThat(RemoteResult.successful(3).equals(RemoteResult.successful(7))).isFalse();
+        assertThat(RemoteResult.successful(-1).equals(RemoteResult.successful(1))).isFalse();
+        assertThat(RemoteResult.successful(Long.MAX_VALUE).equals(RemoteResult.successful(-1)))
+                .isFalse();
+        assertThat(RemoteResult.successful(3).equals(RemoteResult.FAILED_TIMED_OUT)).isFalse();
+        assertThat(RemoteResult.successful(3).equals("3")).isFalse();
+        assertThat(RemoteResult.successful(3).equals(null)).isFalse();
+        assertThat(RemoteResult.FAILED_TIMED_OUT.equals(RemoteResult.FAILED_TIMED_OUT)).isTrue();
+        assertThat(RemoteResult.FAILED_TIMED_OUT.equals(RemoteResult.FAILED_CANCELLED)).isFalse();
+    }
+
+    /** @see Object#hashCode() */
+    @Test
+    public void testHashCode() {
+        RemoteResult result3 = RemoteResult.successful(3);
+        assertThat(result3.hashCode()).isEqualTo(result3.hashCode());
+        assertThat(result3.hashCode()).isEqualTo(RemoteResult.successful(3).hashCode());
+        assertThat(RemoteResult.FAILED_TIMED_OUT.hashCode())
+                .isEqualTo(RemoteResult.FAILED_TIMED_OUT.hashCode());
+        assertThat(RemoteResult.FAILED_CANCELLED.hashCode())
+                .isEqualTo(RemoteResult.FAILED_CANCELLED.hashCode());
+        assertThat(RemoteResult.FAILED_THREAD_INTERRUPTED.hashCode())
+                .isEqualTo(RemoteResult.FAILED_THREAD_INTERRUPTED.hashCode());
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java b/services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
new file mode 100644
index 0000000..e0d3c0c
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.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 com.android.server.backup.remote;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.IBackupManager;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class ServiceBackupCallbackTest {
+    @Test
+    public void testOperationComplete_callsBackupManagerOpComplete() throws Exception {
+        IBackupManager backupManager = mock(IBackupManager.class);
+        ServiceBackupCallback callback = new ServiceBackupCallback(backupManager, 0x68e);
+
+        callback.operationComplete(7);
+
+        verify(backupManager).opComplete(0x68e, 7);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 92d6bbd..2717120 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -19,6 +19,9 @@
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createBackupWakeLock;
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils.setUpBackupManagerServiceBasics;
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThreadAndGetLooper;
+
+import static com.android.server.backup.testing.TestUtils.assertEventLogged;
+import static com.android.server.backup.testing.TestUtils.assertEventNotLogged;
 import static com.android.server.backup.testing.TransportData.backupTransport;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -77,10 +80,9 @@
 
 @RunWith(FrameworkRobolectricTestRunner.class)
 @Config(
-    manifest = Config.NONE,
-    sdk = 26,
-    shadows = {ShadowEventLog.class, ShadowPerformUnifiedRestoreTask.class, ShadowBinder.class}
-)
+        manifest = Config.NONE,
+        sdk = 26,
+        shadows = {ShadowEventLog.class, ShadowPerformUnifiedRestoreTask.class, ShadowBinder.class})
 @SystemLoaderPackages({"com.android.server.backup"})
 @Presubmit
 public class ActiveRestoreSessionTest {
@@ -130,6 +132,7 @@
 
         mWakeLock = createBackupWakeLock(application);
 
+        // TODO: Migrate to use spy(createInitializedBackupManagerService())
         setUpBackupManagerServiceBasics(
                 mBackupManagerService,
                 application,
@@ -210,7 +213,7 @@
         mShadowBackupLooper.runToEndOfTasks();
         assertThat(result).isEqualTo(0);
         verify(mObserver).restoreSetsAvailable(aryEq(new RestoreSet[0]));
-        assertThat(ShadowEventLog.hasEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE)).isFalse();
+        assertEventNotLogged(EventLogTags.RESTORE_TRANSPORT_FAILURE);
     }
 
     @Test
@@ -225,7 +228,7 @@
         mShadowBackupLooper.runToEndOfTasks();
         assertThat(result).isEqualTo(0);
         verify(mObserver).restoreSetsAvailable(isNull());
-        assertThat(ShadowEventLog.hasEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE)).isTrue();
+        assertEventLogged(EventLogTags.RESTORE_TRANSPORT_FAILURE);
         verify(mTransportManager)
                 .disposeOfTransportClient(eq(transportMock.transportClient), any());
         assertThat(mWakeLock.isHeld()).isFalse();
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 5a886e3..084f27f 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -16,46 +16,149 @@
 
 package com.android.server.backup.testing;
 
+import static com.android.server.backup.testing.TestUtils.runToEndOfTasks;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
 
+import android.annotation.Nullable;
 import android.app.Application;
 import android.app.IActivityManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.PowerManager;
+import android.os.Process;
 import android.util.SparseArray;
 
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.Trampoline;
 import com.android.server.backup.TransportManager;
-import com.android.server.backup.internal.BackupHandler;
+import com.android.server.backup.internal.Operation;
 
+import org.mockito.stubbing.Answer;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.shadows.ShadowBinder;
+import org.robolectric.shadows.ShadowLog;
+
+import java.io.File;
 import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.concurrent.atomic.AtomicReference;
 
 /** Test utils for {@link BackupManagerService} and friends. */
 public class BackupManagerServiceTestUtils {
-    /** Sets up basic mocks for {@link BackupManagerService}. */
+    /**
+     * 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.
+     */
+    public static BackupManagerService createInitializedBackupManagerService(
+            Context context, File baseStateDir, File dataDir, TransportManager transportManager) {
+        return createInitializedBackupManagerService(
+                context, startBackupThread(null), baseStateDir, dataDir, transportManager);
+    }
+
+    public static BackupManagerService createInitializedBackupManagerService(
+            Context context,
+            HandlerThread backupThread,
+            File baseStateDir,
+            File dataDir,
+            TransportManager transportManager) {
+        BackupManagerService backupManagerService =
+                new BackupManagerService(
+                        context,
+                        new Trampoline(context),
+                        backupThread,
+                        baseStateDir,
+                        dataDir,
+                        transportManager);
+        runToEndOfTasks(backupThread.getLooper());
+        return backupManagerService;
+    }
+
+    /**
+     * Sets up basic mocks for {@link BackupManagerService} mock. If {@code backupManagerService} is
+     * a spy, make sure you provide in the arguments the same objects that the original object uses.
+     *
+     * <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)}.
+     */
+    @SuppressWarnings("ResultOfMethodCallIgnored")
     public static void setUpBackupManagerServiceBasics(
             BackupManagerService backupManagerService,
-            Context context,
+            Application application,
             TransportManager transportManager,
             PackageManager packageManager,
-            BackupHandler backupHandler,
+            Handler backupHandler,
             PowerManager.WakeLock wakeLock,
             BackupAgentTimeoutParameters agentTimeoutParameters) {
-        when(backupManagerService.getContext()).thenReturn(context);
+        SparseArray<Operation> operations = new SparseArray<>();
+
+        when(backupManagerService.getContext()).thenReturn(application);
         when(backupManagerService.getTransportManager()).thenReturn(transportManager);
         when(backupManagerService.getPackageManager()).thenReturn(packageManager);
         when(backupManagerService.getBackupHandler()).thenReturn(backupHandler);
         when(backupManagerService.getCurrentOpLock()).thenReturn(new Object());
         when(backupManagerService.getQueueLock()).thenReturn(new Object());
-        when(backupManagerService.getCurrentOperations()).thenReturn(new SparseArray<>());
+        when(backupManagerService.getCurrentOperations()).thenReturn(operations);
         when(backupManagerService.getActivityManager()).thenReturn(mock(IActivityManager.class));
         when(backupManagerService.getWakelock()).thenReturn(wakeLock);
         when(backupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
+
+        AccessorMock backupEnabled = mockAccessor(false);
+        doAnswer(backupEnabled.getter).when(backupManagerService).isBackupEnabled();
+        doAnswer(backupEnabled.setter).when(backupManagerService).setBackupEnabled(anyBoolean());
+
+        AccessorMock backupRunning = mockAccessor(false);
+        doAnswer(backupEnabled.getter).when(backupManagerService).isBackupRunning();
+        doAnswer(backupRunning.setter).when(backupManagerService).setBackupRunning(anyBoolean());
+
+        doAnswer(
+                        invocation -> {
+                            operations.put(invocation.getArgument(0), invocation.getArgument(1));
+                            return null;
+                        })
+                .when(backupManagerService)
+                .putOperation(anyInt(), any());
+        doAnswer(
+                        invocation -> {
+                            int token = invocation.getArgument(0);
+                            operations.remove(token);
+                            return null;
+                        })
+                .when(backupManagerService)
+                .removeOperation(anyInt());
+    }
+
+    public static void setUpBinderCallerAndApplicationAsSystem(Application application) {
+        ShadowBinder.setCallingUid(Process.SYSTEM_UID);
+        ShadowBinder.setCallingPid(1211);
+        ShadowApplication shadowApplication = shadowOf(application);
+        shadowApplication.grantPermissions("android.permission.BACKUP");
+        shadowApplication.grantPermissions("android.permission.CONFIRM_FULL_BACKUP");
+    }
+
+    /**
+     * Returns one getter {@link Answer<T>} and one setter {@link Answer<T>} to be easily passed to
+     * Mockito mocking facilities.
+     *
+     * @param defaultValue Value returned by the getter if there was no setter call until then.
+     */
+    public static <T> AccessorMock<T> mockAccessor(T defaultValue) {
+        AtomicReference<T> holder = new AtomicReference<>(defaultValue);
+        return new AccessorMock<>(
+                invocation -> holder.get(),
+                invocation -> {
+                    holder.set(invocation.getArgument(0));
+                    return null;
+                });
     }
 
     public static PowerManager.WakeLock createBackupWakeLock(Application application) {
@@ -88,12 +191,38 @@
      * @return The backup thread.
      * @see #startBackupThreadAndGetLooper()
      */
-    public static HandlerThread startBackupThread(UncaughtExceptionHandler exceptionHandler) {
+    public static HandlerThread startBackupThread(
+            @Nullable UncaughtExceptionHandler exceptionHandler) {
         HandlerThread backupThread = new HandlerThread("backup");
         backupThread.setUncaughtExceptionHandler(exceptionHandler);
         backupThread.start();
         return backupThread;
     }
 
+    /**
+     * Similar to {@link #startBackupThread(UncaughtExceptionHandler)} but logging uncaught
+     * exceptions to logcat.
+     *
+     * @param tag Tag used for logging exceptions.
+     * @return The backup thread.
+     * @see #startBackupThread(UncaughtExceptionHandler)
+     */
+    public static HandlerThread startSilentBackupThread(String tag) {
+        return startBackupThread(
+                (thread, e) ->
+                        ShadowLog.e(
+                                tag, "Uncaught exception in test thread " + thread.getName(), e));
+    }
+
     private BackupManagerServiceTestUtils() {}
+
+    public static class AccessorMock<T> {
+        public Answer<T> getter;
+        public Answer<T> setter;
+
+        private AccessorMock(Answer<T> getter, Answer<T> setter) {
+            this.getter = getter;
+            this.setter = setter;
+        }
+    }
 }
diff --git a/services/robotests/src/com/android/server/backup/testing/PackageData.java b/services/robotests/src/com/android/server/backup/testing/PackageData.java
new file mode 100644
index 0000000..f9177a8
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/PackageData.java
@@ -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
+ */
+
+package com.android.server.backup.testing;
+
+import android.annotation.IntDef;
+import android.content.pm.ApplicationInfo;
+import android.os.Process;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+// TODO: Preconditions is not available, include its target in dependencies
+public class PackageData {
+    public static final PackageData PM_PACKAGE = new PmPackageData();
+
+    public static PackageData keyValuePackage(int identifier) {
+        return androidPackage(identifier, BackupStatus.KEY_VALUE_BACKUP);
+    }
+
+    public static PackageData fullBackupPackage(int identifier) {
+        return androidPackage(identifier, BackupStatus.FULL_BACKUP);
+    }
+
+    /** Returns {@link PackageData} for unique package identifier {@code identifier}. */
+    public static PackageData androidPackage(int identifier) {
+        return androidPackage(identifier, BackupStatus.KEY_VALUE_BACKUP);
+    }
+
+    public final String packageName;
+    public final String agentName;
+    @BackupStatus public final int backupStatus;
+    public final boolean available;
+    public final boolean stopped;
+    public final int uid;
+
+    private PackageData(
+            String packageName,
+            String agentName,
+            int backupStatus,
+            boolean stopped,
+            boolean available,
+            int uid) {
+        // checkArgument(!stopped || !available, "stopped => !available")
+
+        this.packageName = packageName;
+        this.agentName = agentName;
+        this.backupStatus = backupStatus;
+        this.stopped = stopped;
+        this.available = available;
+        this.uid = uid;
+    }
+
+    public int flags() {
+        int flags = 0;
+        if (backupStatus != BackupStatus.BACKUP_NOT_ALLOWED) {
+            flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
+        }
+        if (backupStatus == BackupStatus.FULL_BACKUP) {
+            flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
+        }
+        if (stopped) {
+            flags |= ApplicationInfo.FLAG_STOPPED;
+        }
+        return flags;
+    }
+
+    public PackageData backupNotAllowed() {
+        return new PackageData(
+                packageName, agentName, BackupStatus.BACKUP_NOT_ALLOWED, stopped, available, uid);
+    }
+
+    public PackageData stopped() {
+        return new PackageData(packageName, agentName, backupStatus, true, false, uid);
+    }
+
+    public PackageData unavailable() {
+        return new PackageData(packageName, agentName, backupStatus, stopped, false, uid);
+    }
+
+    public boolean isPm() {
+        return this == PM_PACKAGE;
+    }
+
+    private static PackageData androidPackage(int identifier, @BackupStatus int backupStatus) {
+        // checkArgument(identifier >= 0, "identifier can't be < 0");
+
+        String packageName = "com.sample.package" + identifier;
+        return new PackageData(
+                packageName,
+                packageName + ".BackupAgent",
+                backupStatus,
+                false,
+                true,
+                Process.FIRST_APPLICATION_UID + identifier);
+    }
+
+    private static class PmPackageData extends PackageData {
+        private PmPackageData() {
+            super(
+                    "@pm@",
+                    "com.android.server.backup.PackageManagerBackupAgent",
+                    BackupStatus.KEY_VALUE_BACKUP,
+                    false,
+                    true,
+                    Process.SYSTEM_UID);
+        }
+
+        @Override
+        public int flags() {
+            throw new UnsupportedOperationException("PM \"package\" has no flags");
+        }
+
+        @Override
+        public PackageData backupNotAllowed() {
+            throw new UnsupportedOperationException("PM \"package\" has backup allowed");
+        }
+
+        @Override
+        public PackageData stopped() {
+            throw new UnsupportedOperationException("PM \"package\" can't be stopped");
+        }
+
+        @Override
+        public PackageData unavailable() {
+            throw new UnsupportedOperationException("PM \"package\" is always available");
+        }
+    }
+
+    @IntDef({
+        BackupStatus.KEY_VALUE_BACKUP,
+        BackupStatus.FULL_BACKUP,
+        BackupStatus.BACKUP_NOT_ALLOWED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BackupStatus {
+        int KEY_VALUE_BACKUP = 0;
+        int FULL_BACKUP = 1;
+        int BACKUP_NOT_ALLOWED = 2;
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
index 3c84424..134cfd8 100644
--- a/services/robotests/src/com/android/server/backup/testing/TestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
@@ -18,21 +18,44 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import org.robolectric.shadows.ShadowLog;
+import static org.robolectric.Shadows.shadowOf;
 
+import android.os.Looper;
+
+import com.android.server.testing.shadows.ShadowEventLog;
+
+import org.robolectric.shadows.ShadowLog;
+import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowSystemClock;
+
+import java.util.Arrays;
 import java.util.concurrent.Callable;
 import java.util.function.Predicate;
 
 public class TestUtils {
-    /** Reset logcat with {@link ShadowLog#reset()} before the test case */
+    /** Version of {@link ShadowLooper#runToEndOfTasks()} that also advances the system clock. */
+    public static void runToEndOfTasks(Looper looper) {
+        ShadowLooper shadowLooper = shadowOf(looper);
+        shadowLooper.runToEndOfTasks();
+        // Handler instances have their own clock, so advancing looper (with runToEndOfTasks())
+        // above does NOT advance the handlers' clock, hence whenever a handler post messages with
+        // specific time to the looper the time of those messages will be before the looper's time.
+        // To fix this we advance SystemClock as well since that is from where the handlers read
+        // time.
+        ShadowSystemClock.setCurrentTimeMillis(shadowLooper.getScheduler().getCurrentTime());
+    }
+
+    /** Reset logcat with {@link ShadowLog#reset()} before the test case. */
     public static void assertLogcatAtMost(String tag, int level) {
         assertThat(ShadowLog.getLogsForTag(tag).stream().allMatch(logItem -> logItem.type <= level))
+                .named("All logs <= " + level)
                 .isTrue();
     }
 
-    /** Reset logcat with {@link ShadowLog#reset()} before the test case */
+    /** Reset logcat with {@link ShadowLog#reset()} before the test case. */
     public static void assertLogcatAtLeast(String tag, int level) {
         assertThat(ShadowLog.getLogsForTag(tag).stream().anyMatch(logItem -> logItem.type >= level))
+                .named("Any log >= " + level)
                 .isTrue();
     }
 
@@ -40,6 +63,20 @@
         assertThat(ShadowLog.getLogsForTag(tag).stream().anyMatch(predicate)).isTrue();
     }
 
+    /** Declare shadow {@link ShadowEventLog} to use this. */
+    public static void assertEventLogged(int tag, Object... values) {
+        assertThat(ShadowEventLog.getEntries())
+                .named("Event logs")
+                .contains(new ShadowEventLog.Entry(tag, Arrays.asList(values)));
+    }
+
+    /** Declare shadow {@link ShadowEventLog} to use this. */
+    public static void assertEventNotLogged(int tag, Object... values) {
+        assertThat(ShadowEventLog.getEntries())
+                .named("Event logs")
+                .doesNotContain(new ShadowEventLog.Entry(tag, Arrays.asList(values)));
+    }
+
     /**
      * Calls {@link Runnable#run()} and returns if no exception is thrown. Otherwise, if the
      * exception is unchecked, rethrow it; if it's checked wrap in a {@link RuntimeException} and
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportData.java b/services/robotests/src/com/android/server/backup/testing/TransportData.java
index 9feaa8e..4c67180 100644
--- a/services/robotests/src/com/android/server/backup/testing/TransportData.java
+++ b/services/robotests/src/com/android/server/backup/testing/TransportData.java
@@ -20,6 +20,8 @@
 import android.content.ComponentName;
 import android.content.Intent;
 
+import com.android.server.backup.testing.TransportTestUtils.TransportStatus;
+
 public class TransportData {
     // No constants since new Intent() can't be called in static context because of Robolectric
     public static TransportData backupTransport() {
@@ -66,19 +68,17 @@
                 "dataManagementLabel");
     }
 
-    @TransportTestUtils.TransportStatus
-    public int transportStatus;
+    @TransportStatus public int transportStatus;
     public final String transportName;
     private final String transportComponentShort;
-    @Nullable
-    public String transportDirName;
+    @Nullable public String transportDirName;
     @Nullable public Intent configurationIntent;
     @Nullable public String currentDestinationString;
     @Nullable public Intent dataManagementIntent;
     @Nullable public String dataManagementLabel;
 
     private TransportData(
-            @TransportTestUtils.TransportStatus int transportStatus,
+            @TransportStatus int transportStatus,
             String transportName,
             String transportComponentShort,
             String transportDirName,
@@ -105,7 +105,7 @@
             Intent dataManagementIntent,
             String dataManagementLabel) {
         this(
-                TransportTestUtils.TransportStatus.REGISTERED_AVAILABLE,
+                TransportStatus.REGISTERED_AVAILABLE,
                 transportName,
                 transportComponentShort,
                 transportDirName,
@@ -125,7 +125,7 @@
 
     public TransportData unavailable() {
         return new TransportData(
-                TransportTestUtils.TransportStatus.REGISTERED_UNAVAILABLE,
+                TransportStatus.REGISTERED_UNAVAILABLE,
                 transportName,
                 transportComponentShort,
                 transportDirName,
@@ -137,7 +137,7 @@
 
     public TransportData unregistered() {
         return new TransportData(
-                TransportTestUtils.TransportStatus.UNREGISTERED,
+                TransportStatus.UNREGISTERED,
                 transportName,
                 transportComponentShort,
                 transportDirName,
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
index 5844131..6625443 100644
--- a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
@@ -26,13 +26,13 @@
 
 import static java.util.stream.Collectors.toList;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.os.RemoteException;
-import android.support.annotation.IntDef;
 
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.backup.TransportManager;
@@ -42,6 +42,8 @@
 
 import org.robolectric.shadows.ShadowPackageManager;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.stream.Stream;
 
@@ -209,6 +211,7 @@
         TransportStatus.REGISTERED_UNAVAILABLE,
         TransportStatus.UNREGISTERED
     })
+    @Retention(RetentionPolicy.SOURCE)
     public @interface TransportStatus {
         int REGISTERED_AVAILABLE = 0;
         int REGISTERED_UNAVAILABLE = 1;
diff --git a/services/robotests/src/com/android/server/backup/testing/Utils.java b/services/robotests/src/com/android/server/backup/testing/Utils.java
new file mode 100644
index 0000000..bd8b4ef
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/testing/Utils.java
@@ -0,0 +1,45 @@
+/*
+ * 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.testing;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Iterator;
+
+public class Utils {
+    public static final int BUFFER_SIZE = 8192;
+
+    public static void transferStreamedData(InputStream in, OutputStream out) throws IOException {
+        transferStreamedData(in, out, BUFFER_SIZE);
+    }
+
+    public static void transferStreamedData(InputStream in, OutputStream out, int bufferSize)
+            throws IOException {
+        byte[] buffer = new byte[bufferSize];
+        int read;
+        while ((read = in.read(buffer)) != -1) {
+            out.write(buffer, 0, read);
+        }
+    }
+
+    public static <T> Iterable<T> oneTimeIterable(Iterator<T> iterator) {
+        return () -> iterator;
+    }
+
+    private Utils() {}
+}
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
index 49ef581f..f843b50 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
+++ b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
@@ -17,6 +17,7 @@
 package com.android.server.backup.transport;
 
 import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
+import static com.android.server.backup.testing.TestUtils.assertEventLogged;
 import static com.android.server.backup.testing.TestUtils.assertLogcatAtLeast;
 import static com.android.server.backup.testing.TestUtils.assertLogcatAtMost;
 
@@ -70,15 +71,14 @@
 
 @RunWith(FrameworkRobolectricTestRunner.class)
 @Config(
-    manifest = Config.NONE,
-    sdk = 26,
-    shadows = {
-        ShadowEventLog.class,
-        ShadowCloseGuard.class,
-        ShadowSlog.class,
-        FrameworkShadowLooper.class
-    }
-)
+        manifest = Config.NONE,
+        sdk = 26,
+        shadows = {
+            ShadowEventLog.class,
+            ShadowCloseGuard.class,
+            ShadowSlog.class,
+            FrameworkShadowLooper.class
+        })
 @SystemLoaderPackages({"com.android.server.backup"})
 @Presubmit
 public class TransportClientTest {
@@ -279,7 +279,7 @@
     }
 
     @Test
-    public void testConnectAsync_beforeFrameworkCall_logsBoundTransition() {
+    public void testConnectAsync_beforeFrameworkCall_logsBoundTransitionEvent() {
         ShadowEventLog.setUp();
 
         mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
@@ -288,7 +288,7 @@
     }
 
     @Test
-    public void testConnectAsync_afterOnServiceConnected_logsBoundAndConnectedTransitions() {
+    public void testConnectAsync_afterOnServiceConnected_logsBoundAndConnectedTransitionEvents() {
         ShadowEventLog.setUp();
         mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
         ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
@@ -300,7 +300,7 @@
     }
 
     @Test
-    public void testConnectAsync_afterOnBindingDied_logsBoundAndUnboundTransitions() {
+    public void testConnectAsync_afterOnBindingDied_logsBoundAndUnboundTransitionEvents() {
         ShadowEventLog.setUp();
         mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
         ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
@@ -372,7 +372,7 @@
     }
 
     @Test
-    public void testUnbind_whenConnected_logsDisconnectedAndUnboundTransitions() {
+    public void testUnbind_whenConnected_logsDisconnectedAndUnboundTransitionEvents() {
         mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
         ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
         connection.onServiceConnected(mTransportComponent, mTransportBinder);
@@ -385,7 +385,8 @@
     }
 
     @Test
-    public void testOnServiceDisconnected_whenConnected_logsDisconnectedAndUnboundTransitions() {
+    public void
+            testOnServiceDisconnected_whenConnected_logsDisconnectedAndUnboundTransitionEvents() {
         mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
         ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
         connection.onServiceConnected(mTransportComponent, mTransportBinder);
@@ -398,7 +399,7 @@
     }
 
     @Test
-    public void testOnBindingDied_whenConnected_logsDisconnectedAndUnboundTransitions() {
+    public void testOnBindingDied_whenConnected_logsDisconnectedAndUnboundTransitionEvents() {
         mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
         ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
         connection.onServiceConnected(mTransportComponent, mTransportBinder);
@@ -539,10 +540,6 @@
         return future.get();
     }
 
-    private void assertEventLogged(int tag, Object... values) {
-        assertThat(ShadowEventLog.hasEvent(tag, values)).isTrue();
-    }
-
     private ServiceConnection verifyBindServiceAsUserAndCaptureServiceConnection(Context context) {
         ArgumentCaptor<ServiceConnection> connectionCaptor =
                 ArgumentCaptor.forClass(ServiceConnection.class);
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataInput.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataInput.java
index 8016a8b..bc47dd5 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataInput.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataInput.java
@@ -33,6 +33,7 @@
  */
 @Implements(BackupDataInput.class)
 public class ShadowBackupDataInput {
+    private FileDescriptor mFileDescriptor;
     private ObjectInputStream mInput;
     private int mSize;
     private String mKey;
@@ -40,17 +41,14 @@
 
     @Implementation
     public void __constructor__(FileDescriptor fd) {
-        try {
-            mInput = new ObjectInputStream(new FileInputStream(fd));
-        } catch (IOException e) {
-            throw new AssertionError(e);
-        }
+        mFileDescriptor = fd;
     }
 
     @Implementation
     public boolean readNextHeader() throws IOException {
         mHeaderReady = false;
         try {
+            ensureInput();
             mSize = mInput.readInt();
         } catch (EOFException e) {
             return false;
@@ -93,4 +91,22 @@
             throw new IllegalStateException("Entity header not read");
         }
     }
+
+    /**
+     * Lazily initializing input to avoid throwing exception when stream is completely empty in
+     * constructor (Java Object IO writes/reads some header data).
+     *
+     * @throws EOFException When the input is empty.
+     */
+    private void ensureInput() throws EOFException {
+        if (mInput == null) {
+            try {
+                mInput = new ObjectInputStream(new FileInputStream(mFileDescriptor));
+            } catch (EOFException e) {
+                throw e;
+            } catch (IOException e) {
+                throw new AssertionError(e);
+            }
+        }
+    }
 }
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
index 0415235..ca04008 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
@@ -33,18 +33,14 @@
  */
 @Implements(BackupDataOutput.class)
 public class ShadowBackupDataOutput {
+    private FileDescriptor mFileDescriptor;
+    private ObjectOutputStream mOutput;
     private long mQuota;
     private int mTransportFlags;
-    private ObjectOutputStream mOutput;
 
     @Implementation
     public void __constructor__(FileDescriptor fd, long quota, int transportFlags) {
-        try {
-            // This writes 4 bytes
-            mOutput = new ObjectOutputStream(new FileOutputStream(fd));
-        } catch (IOException e) {
-            throw new AssertionError(e);
-        }
+        mFileDescriptor = fd;
         mQuota = quota;
         mTransportFlags = transportFlags;
     }
@@ -61,6 +57,7 @@
 
     @Implementation
     public int writeEntityHeader(String key, int dataSize) throws IOException {
+        ensureOutput();
         final int size;
         try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) {
             writeEntityHeader(new ObjectOutputStream(byteStream), key, dataSize);
@@ -80,8 +77,24 @@
 
     @Implementation
     public int writeEntityData(byte[] data, int size) throws IOException {
+        ensureOutput();
         mOutput.write(data, 0, size);
         mOutput.flush();
         return size;
     }
+
+    /**
+     * Lazily initializing output to avoid writing the header data below for when there is no data
+     * (Java Object IO writes/reads some header data).
+     */
+    private void ensureOutput() {
+        if (mOutput == null) {
+            try {
+                // This writes 4 bytes: Java Object IO writes/reads some header data
+                mOutput = new ObjectOutputStream(new FileOutputStream(mFileDescriptor));
+            } catch (IOException e) {
+                throw new AssertionError(e);
+            }
+        }
+    }
 }
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowEventLog.java b/services/robotests/src/com/android/server/testing/shadows/ShadowEventLog.java
index 4625684..3df1723 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowEventLog.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowEventLog.java
@@ -24,10 +24,12 @@
 import java.util.Arrays;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
+/** Don't forget to call {@link ShadowEventLog#setUp()} before every test. */
 @Implements(EventLog.class)
 public class ShadowEventLog {
-    private final static LinkedHashSet<Entry> ENTRIES = new LinkedHashSet<>();
+    private static final LinkedHashSet<Entry> ENTRIES = new LinkedHashSet<>();
 
     @Implementation
     public static int writeEvent(int tag, Object... values) {
@@ -36,11 +38,16 @@
         return 0;
     }
 
-    public static boolean hasEvent(int tag, Object... values) {
-        return ENTRIES.contains(new Entry(tag, Arrays.asList(values)));
+    @Implementation
+    public static int writeEvent(int tag, String string) {
+        return writeEvent(tag, (Object) string);
     }
 
-    /** Clears the entries */
+    public static Set<Entry> getEntries() {
+        return new LinkedHashSet<>(ENTRIES);
+    }
+
+    /** Clears the entries. */
     public static void setUp() {
         ENTRIES.clear();
     }
@@ -68,5 +75,10 @@
             result = 31 * result + values.hashCode();
             return result;
         }
+
+        @Override
+        public String toString() {
+            return "Entry{" + tag + ", " + values + '}';
+        }
     }
 }
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
new file mode 100644
index 0000000..838902d
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
@@ -0,0 +1,88 @@
+/*
+ * 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.testing.shadows;
+
+import android.annotation.Nullable;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+
+import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.keyvalue.BackupRequest;
+import com.android.server.backup.keyvalue.KeyValueBackupTask;
+import com.android.server.backup.transport.TransportClient;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.util.List;
+
+@Implements(KeyValueBackupTask.class)
+public class ShadowKeyValueBackupTask {
+    @Nullable private static ShadowKeyValueBackupTask sLastShadow;
+
+    /**
+     * Retrieves the shadow for the last {@link KeyValueBackupTask} object created.
+     *
+     * @return The shadow or {@code null} if no object created since last {@link #reset()}.
+     */
+    @Nullable
+    public static ShadowKeyValueBackupTask getLastCreated() {
+        return sLastShadow;
+    }
+
+    public static void reset() {
+        sLastShadow = null;
+    }
+
+    private OnTaskFinishedListener mListener;
+    private List<BackupRequest> mQueue;
+    private List<String> mPendingFullBackups;
+
+    @Implementation
+    public void __constructor__(
+            BackupManagerService backupManagerService,
+            TransportClient transportClient,
+            String dirName,
+            List<BackupRequest> queue,
+            @Nullable DataChangedJournal journal,
+            IBackupObserver observer,
+            IBackupManagerMonitor monitor,
+            @Nullable OnTaskFinishedListener listener,
+            List<String> pendingFullBackups,
+            boolean userInitiated,
+            boolean nonIncremental) {
+        mListener = listener;
+        mQueue = queue;
+        mPendingFullBackups = pendingFullBackups;
+        sLastShadow = this;
+    }
+
+    @Implementation
+    public void execute() {
+        mListener.onFinished("ShadowKeyValueBackupTask.execute()");
+    }
+
+    public List<BackupRequest> getQueue() {
+        return mQueue;
+    }
+
+    public List<String> getPendingFullBackups() {
+        return mPendingFullBackups;
+    }
+}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformBackupTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformBackupTask.java
deleted file mode 100644
index 7c10377..0000000
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformBackupTask.java
+++ /dev/null
@@ -1,89 +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.testing.shadows;
-
-import android.annotation.Nullable;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-
-import com.android.server.backup.BackupManagerService;
-import com.android.server.backup.DataChangedJournal;
-import com.android.server.backup.internal.BackupRequest;
-import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.internal.PerformBackupTask;
-import com.android.server.backup.transport.TransportClient;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@Implements(PerformBackupTask.class)
-public class ShadowPerformBackupTask {
-    @Nullable private static ShadowPerformBackupTask sLastShadow;
-
-    /**
-     * Retrieves the shadow for the last {@link PerformBackupTask} object created.
-     *
-     * @return The shadow or {@code null} if no object created since last {@link #reset()}.
-     */
-    @Nullable
-    public static ShadowPerformBackupTask getLastCreated() {
-        return sLastShadow;
-    }
-
-    public static void reset() {
-        sLastShadow = null;
-    }
-
-    private OnTaskFinishedListener mListener;
-    private ArrayList<BackupRequest> mQueue;
-    private List<String> mPendingFullBackups;
-
-    @Implementation
-    public void __constructor__(
-            BackupManagerService backupManagerService,
-            TransportClient transportClient,
-            String dirName,
-            ArrayList<BackupRequest> queue,
-            @Nullable DataChangedJournal journal,
-            IBackupObserver observer,
-            IBackupManagerMonitor monitor,
-            @Nullable OnTaskFinishedListener listener,
-            List<String> pendingFullBackups,
-            boolean userInitiated,
-            boolean nonIncremental) {
-        mListener = listener;
-        mQueue = queue;
-        mPendingFullBackups = pendingFullBackups;
-        sLastShadow = this;
-    }
-
-    @Implementation
-    public void execute() {
-        mListener.onFinished("ShadowPerformBackupTask.execute()");
-    }
-
-    public List<BackupRequest> getQueue() {
-        return mQueue;
-    }
-
-    public List<String> getPendingFullBackups() {
-        return mPendingFullBackups;
-    }
-}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index a85f21c..8f4e8e4 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -41,6 +41,7 @@
 
 LOCAL_JAVA_LIBRARIES := \
     android.hidl.manager-V1.0-java \
+    android.hardware.tv.cec-V1.0-java \
     android.test.mock \
     android.test.base android.test.runner \
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java
index 14abb8a1..41b834a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java
@@ -195,7 +195,7 @@
                     point.x, point.y, 0);
 
             // Send event.
-            mDetector.onMotionEvent(event, policyFlags);
+            mDetector.onMotionEvent(event, event, policyFlags);
             eventTimeMs += PATH_STEP_MILLISEC;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index e5c6c6e..412e844 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -141,4 +141,15 @@
         when(mMockUserState.getBindingServicesLocked())
                 .thenReturn(new HashSet<>(Arrays.asList(componentName)));
     }
+
+    @Test
+    public void binderDied_keysGetFlushed() {
+        IBinder mockBinder = mock(IBinder.class);
+        setServiceBinding(COMPONENT_NAME);
+        mConnection.bindLocked();
+        mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
+        mConnection.binderDied();
+        assertTrue(mConnection.getServiceInfo().crashed);
+        verify(mMockKeyEventDispatcher).flush(mConnection);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
index d3a30909..5253cb4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
@@ -453,6 +453,20 @@
     }
 
     @Test
+    public void testResetIfNeeded_resetsOnlyIfLastMagnifyingServiceIsDisabled() {
+        mMagnificationController.register();
+        PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
+        mMagnificationController
+                .setScale(2.0f, startCenter.x, startCenter.y, false, SERVICE_ID_1);
+        mMagnificationController
+                .setScale(1.5f, startCenter.x, startCenter.y, false, SERVICE_ID_2);
+        assertFalse(mMagnificationController.resetIfNeeded(SERVICE_ID_1));
+        assertTrue(mMagnificationController.isMagnifying());
+        assertTrue(mMagnificationController.resetIfNeeded(SERVICE_ID_2));
+        assertFalse(mMagnificationController.isMagnifying());
+    }
+
+    @Test
     public void testSetUserId_resetsOnlyIfIdChanges() {
         final int userId1 = 1;
         final int userId2 = 2;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
index f741c70..583a9dd 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
@@ -50,7 +50,7 @@
 @RunWith(AndroidJUnit4.class)
 public class ActivityLaunchParamsModifierTests extends ActivityTestsBase {
     private ActivityLaunchParamsModifier mModifier;
-    private ActivityManagerService mService;
+    private ActivityTaskManagerService mService;
     private ActivityStack mStack;
     private TaskRecord mTask;
     private ActivityRecord mActivity;
@@ -62,7 +62,7 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mService = createActivityManagerService();
+        mService = createActivityTaskManagerService();
         mModifier = new ActivityLaunchParamsModifier(mService.mStackSupervisor);
         mCurrent = new LaunchParams();
         mResult = new LaunchParams();
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 c70d1e1..aeae11a 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -66,7 +66,6 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.AppOpsService;
 
 import org.junit.After;
@@ -121,7 +120,6 @@
     @Mock private Context mContext;
     @Mock private AppOpsService mAppOpsService;
     @Mock private PackageManager mPackageManager;
-    @Mock private BatteryStatsImpl mBatteryStatsImpl;
 
     private TestInjector mInjector;
     private ActivityManagerService mAms;
@@ -258,8 +256,7 @@
         uidRec.hasInternetPermission = true;
         mAms.mActiveUids.put(uid, uidRec);
 
-        final ProcessRecord appRec = new ProcessRecord(null, mBatteryStatsImpl,
-                new ApplicationInfo(), TAG, uid);
+        final ProcessRecord appRec = new ProcessRecord(mAms, new ApplicationInfo(), TAG, uid);
         appRec.thread = Mockito.mock(IApplicationThread.class);
         mAms.mLruProcesses.add(appRec);
 
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityOptionsTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityOptionsTest.java
new file mode 100644
index 0000000..28b37c5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityOptionsTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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 static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
+import static org.junit.Assert.assertEquals;
+
+import android.app.ActivityOptions;
+import android.os.Bundle;
+import android.os.Debug;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * atest FrameworksServicesTests:ActivityOptionsTest
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActivityOptionsTest {
+
+    @Test
+    public void testMerge_NoClobber() {
+        // Construct some options with set values
+        ActivityOptions opts = ActivityOptions.makeBasic();
+        opts.setLaunchDisplayId(Integer.MAX_VALUE);
+        opts.setLaunchActivityType(ACTIVITY_TYPE_STANDARD);
+        opts.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        opts.setAvoidMoveToFront();
+        opts.setLaunchTaskId(Integer.MAX_VALUE);
+        opts.setLockTaskEnabled(true);
+        opts.setRotationAnimationHint(ROTATION_ANIMATION_ROTATE);
+        opts.setTaskOverlay(true, true);
+        opts.setSplitScreenCreateMode(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
+        Bundle optsBundle = opts.toBundle();
+
+        // Try and merge the constructed options with a new set of options
+        optsBundle.putAll(ActivityOptions.makeBasic().toBundle());
+
+        // Ensure the set values are not clobbered
+        ActivityOptions restoredOpts = ActivityOptions.fromBundle(optsBundle);
+        assertEquals(Integer.MAX_VALUE, restoredOpts.getLaunchDisplayId());
+        assertEquals(ACTIVITY_TYPE_STANDARD, restoredOpts.getLaunchActivityType());
+        assertEquals(WINDOWING_MODE_FULLSCREEN, restoredOpts.getLaunchWindowingMode());
+        assertEquals(true, restoredOpts.getAvoidMoveToFront());
+        assertEquals(Integer.MAX_VALUE, restoredOpts.getLaunchTaskId());
+        assertEquals(true, restoredOpts.getLockTaskMode());
+        assertEquals(ROTATION_ANIMATION_ROTATE, restoredOpts.getRotationAnimationHint());
+        assertEquals(true, restoredOpts.getTaskOverlay());
+        assertEquals(true, restoredOpts.canTaskOverlayResume());
+        assertEquals(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
+                restoredOpts.getSplitScreenCreateMode());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 5ee1c40..6cfa317 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -43,6 +43,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -73,7 +74,7 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityRecordTests extends ActivityTestsBase {
-    private ActivityManagerService mService;
+    private ActivityTaskManagerService mService;
     private TestActivityStack mStack;
     private TaskRecord mTask;
     private ActivityRecord mActivity;
@@ -83,7 +84,7 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mService = createActivityManagerService();
+        mService = createActivityTaskManagerService();
         mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
@@ -126,7 +127,7 @@
                 pauseFound.value = true;
             }
             return null;
-        }).when(mActivity.app.thread).scheduleTransaction(any());
+        }).when(mActivity.app.getThread()).scheduleTransaction(any());
 
         mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
 
@@ -136,7 +137,8 @@
         assertFalse(pauseFound.value);
 
         // Clear focused stack
-        mActivity.mStackSupervisor.mFocusedStack = null;
+        final ActivityDisplay display = mActivity.mStackSupervisor.getDefaultDisplay();
+        when(display.getFocusedStack()).thenReturn(null);
 
         // In the unfocused stack, the activity should move to paused.
         mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 1ce41a6..3c4fe18 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -68,7 +68,7 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityStackSupervisorTests extends ActivityTestsBase {
-    private ActivityManagerService mService;
+    private ActivityTaskManagerService mService;
     private ActivityStackSupervisor mSupervisor;
     private ActivityStack mFullscreenStack;
 
@@ -77,19 +77,19 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mService = createActivityManagerService();
+        mService = createActivityTaskManagerService();
         mSupervisor = mService.mStackSupervisor;
         mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
     }
 
     /**
-     * This test ensures that we do not try to restore a task based off an invalid task id. The
-     * stack supervisor is a test version so there will be no tasks present. We should expect
-     * {@code null} to be returned in this case.
+     * This test ensures that we do not try to restore a task based off an invalid task id. We
+     * should expect {@code null} to be returned in this case.
      */
     @Test
     public void testRestoringInvalidTask() throws Exception {
+        ((TestActivityDisplay) mSupervisor.getDefaultDisplay()).removeAllTasks();
         TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/,
                 MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
         assertNull(task);
@@ -109,7 +109,7 @@
                 .setStack(mFullscreenStack).build();
         final TaskRecord secondTask = secondActivity.getTask();
 
-        mSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack", mFullscreenStack);
+        mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
 
         // Ensure full screen stack has both tasks.
         ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
@@ -174,7 +174,7 @@
 
         // #notifyAll will be called on the ActivityManagerService. we must hold the object lock
         // when this happens.
-        synchronized (mSupervisor.mService) {
+        synchronized (mSupervisor.mService.mGlobalLock) {
             final WaitResult taskToFrontWait = new WaitResult();
             mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
             mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT);
@@ -239,7 +239,7 @@
         doReturn(displaySleeping).when(display).isSleeping();
         doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
 
-        mSupervisor.mFocusedStack = isFocusedStack ? stack : null;
+        doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
         mSupervisor.applySleepTokensLocked(true);
         verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
         verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
@@ -253,12 +253,11 @@
 
         doAnswer((InvocationOnMock invocationOnMock) -> {
             final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
-            displayIds.put(0, unknownDisplayId);
+            displayIds.put(0, 0);
+            displayIds.put(1, unknownDisplayId);
             return null;
         }).when(mSupervisor.mWindowManager).getDisplaysInFocusOrder(any());
 
-        mSupervisor.mFocusedStack = mock(ActivityStack.class);
-
         // Supervisor should skip over the non-existent display.
         assertEquals(null, mSupervisor.topRunningActivityLocked());
     }
@@ -278,7 +277,7 @@
         assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
 
         // Let's pretend that the app has crashed.
-        firstActivity.app.thread = null;
+        firstActivity.app.setThread(null);
         mService.mStackSupervisor.finishTopCrashedActivitiesLocked(firstActivity.app, "test");
 
         // Verify that the stack was removed.
@@ -330,8 +329,9 @@
     @Test
     public void testTopRunningActivity() throws Exception {
         // Create stack to hold focus
-        final ActivityStack emptyStack = mService.mStackSupervisor.getDefaultDisplay()
-                .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+        final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         final KeyguardController keyguard = mSupervisor.getKeyguardController();
         final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
@@ -339,11 +339,9 @@
         final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
                 .setStack(stack).build();
 
-        mSupervisor.mFocusedStack = emptyStack;
-
         doAnswer((InvocationOnMock invocationOnMock) -> {
             final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
-            displayIds.put(0, mSupervisor.getDefaultDisplay().mDisplayId);
+            displayIds.put(0, display.mDisplayId);
             return null;
         }).when(mSupervisor.mWindowManager).getDisplaysInFocusOrder(any());
 
@@ -359,7 +357,8 @@
                 true /* considerKeyguardState */));
 
         // Change focus to stack with activity.
-        mSupervisor.mFocusedStack = stack;
+        stack.moveToFront("focusChangeToTestStack");
+        assertEquals(stack, display.getFocusedStack());
         assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
         assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
                 true /* considerKeyguardState */));
@@ -377,10 +376,12 @@
                 true /* considerKeyguardState */));
 
         // Change focus back to empty stack
-        mSupervisor.mFocusedStack = emptyStack;
-        // Ensure the show when locked activity is returned when not the focused stack
-        assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked());
-        assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked(
+        emptyStack.moveToFront("focusChangeToEmptyStack");
+        assertEquals(emptyStack, display.getFocusedStack());
+        // Looking for running activity only in top and focused stack, so nothing should be returned
+        // from empty stack.
+        assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked());
+        assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
                 true /* considerKeyguardState */));
     }
 
@@ -407,4 +408,27 @@
         // Assert that the primary stack is returned.
         assertEquals(primaryStack, result);
     }
+
+    /**
+     * Verify split-screen primary stack & task can resized by
+     * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
+     */
+    @Test
+    public void testResizeDockedStackForSplitScreenPrimary() throws Exception {
+        final Rect TASK_SIZE = new Rect(0, 0, 600, 600);
+        final Rect STACK_SIZE = new Rect(0, 0, 300, 300);
+
+        // Create primary split-screen stack with a task.
+        final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
+                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                        true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+
+        // Resize dock stack.
+        mService.resizeDockedStack(STACK_SIZE, TASK_SIZE, null, null, null);
+
+        // Verify dock stack & its task bounds if is equal as resized result.
+        assertEquals(primaryStack.getBounds(), STACK_SIZE);
+        assertEquals(task.getBounds(), TASK_SIZE);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 01425ed..5669819 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -61,7 +62,7 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityStackTests extends ActivityTestsBase {
-    private ActivityManagerService mService;
+    private ActivityTaskManagerService mService;
     private ActivityStackSupervisor mSupervisor;
     private ActivityDisplay mDefaultDisplay;
     private ActivityStack mStack;
@@ -72,7 +73,7 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mService = createActivityManagerService();
+        mService = createActivityTaskManagerService();
         mSupervisor = mService.mStackSupervisor;
         mDefaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
         mStack = mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
@@ -166,7 +167,7 @@
     public void testStopActivityWhenActivityDestroyed() throws Exception {
         final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
         r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
-        mSupervisor.setFocusStackUnchecked("testStopActivityWithDestroy", mStack);
+        mStack.moveToFront("testStopActivityWithDestroy");
         mStack.stopActivityLocked(r);
         // Mostly testing to make sure there is a crash in the call part, so if we get here we are
         // good-to-go!
@@ -473,6 +474,51 @@
     }
 
     @Test
+    public void testSetAlwaysOnTop() {
+        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+        final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        assertTrue(mDefaultDisplay.getStackAbove(homeStack) == pinnedStack);
+
+        final TestActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        alwaysOnTopStack.setAlwaysOnTop(true);
+        assertTrue(alwaysOnTopStack.isAlwaysOnTop());
+        // Ensure (non-pinned) always on top stack is put below pinned stack.
+        assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack) == pinnedStack);
+
+        final TestActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        // Ensure non always on top stack is put below always on top stacks.
+        assertTrue(mDefaultDisplay.getStackAbove(nonAlwaysOnTopStack) == alwaysOnTopStack);
+
+        final TestActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
+                mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        alwaysOnTopStack2.setAlwaysOnTop(true);
+        assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
+        // Ensure newly created always on top stack is placed above other all always on top stacks.
+        assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == pinnedStack);
+
+        alwaysOnTopStack2.setAlwaysOnTop(false);
+        // Ensure, when always on top is turned off for a stack, the stack is put just below all
+        // other always on top stacks.
+        assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == alwaysOnTopStack);
+        alwaysOnTopStack2.setAlwaysOnTop(true);
+
+        // Ensure always on top state changes properly when windowing mode changes.
+        alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertFalse(alwaysOnTopStack2.isAlwaysOnTop());
+        assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == alwaysOnTopStack);
+        alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
+        assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == pinnedStack);
+    }
+
+    @Test
     public void testSplitScreenMoveToFront() throws Exception {
         final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
@@ -500,9 +546,20 @@
 
     private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
             ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
-        final T stack = display.createStack(windowingMode, activityType, onTop);
-        final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
-                .setCreateTask(true).build();
+        final T stack;
+        if (activityType == ACTIVITY_TYPE_HOME) {
+            // Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
+            stack = mDefaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+            if (onTop) {
+                mDefaultDisplay.positionChildAtTop(stack, false /* includingParents */);
+            } else {
+                mDefaultDisplay.positionChildAtBottom(stack);
+            }
+        } else {
+            stack = display.createStack(windowingMode, activityType, onTop);
+            final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
+                    .setCreateTask(true).build();
+        }
         return stack;
     }
 
@@ -608,14 +665,13 @@
 
     private void verifyShouldSleepActivities(boolean focusedStack,
             boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
-        mSupervisor.mFocusedStack = focusedStack ? mStack : null;
-
         final ActivityDisplay display = mock(ActivityDisplay.class);
         final KeyguardController keyguardController = mSupervisor.getKeyguardController();
 
         doReturn(display).when(mSupervisor).getActivityDisplay(anyInt());
         doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
         doReturn(displaySleeping).when(display).isSleeping();
+        doReturn(focusedStack ? mStack : null).when(mSupervisor).getTopDisplayFocusedStack();
 
         assertEquals(expected, mStack.shouldSleepActivities());
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
index 7948e4c..f5ae46cd 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
@@ -19,7 +19,9 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
+import android.app.IApplicationThread;
 import android.content.Intent;
+import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -49,7 +51,7 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityStartControllerTests extends ActivityTestsBase {
-    private ActivityManagerService mService;
+    private ActivityTaskManagerService mService;
     private ActivityStartController mController;
     private Factory mFactory;
     private ActivityStarter mStarter;
@@ -57,11 +59,11 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mService = createActivityManagerService();
+        mService = createActivityTaskManagerService();
         mFactory = mock(Factory.class);
         mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory);
-        mStarter = spy(new ActivityStarter(mController, mService, mService.mStackSupervisor,
-                mock(ActivityStartInterceptor.class)));
+        mStarter = spy(new ActivityStarter(mController, mService,
+                mService.mStackSupervisor, mock(ActivityStartInterceptor.class)));
         doReturn(mStarter).when(mFactory).obtain();
     }
 
@@ -77,11 +79,14 @@
         final int startFlags = random.nextInt();
         final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ProcessRecord process= new ProcessRecord(null, null,
-                mService.mContext.getApplicationInfo(), "name", 12345);
+        final WindowProcessController wpc = new WindowProcessController(mService,
+                mService.mContext.getApplicationInfo(), "name", 12345,
+                UserHandle.getUserId(12345), mock(Object.class),
+                mock(WindowProcessListener.class));
+        wpc.setThread(mock(IApplicationThread.class));
 
         mController.addPendingActivityLaunch(
-                new PendingActivityLaunch(activity, source, startFlags, stack, process));
+                new PendingActivityLaunch(activity, source, startFlags, stack, wpc));
         final boolean resume = random.nextBoolean();
         mController.doPendingActivityLaunches(resume);
 
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
index 9d35ef1..41c7f1d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManagerInternal;
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Context;
@@ -57,7 +58,7 @@
  * Unit tests for {@link ActivityStartInterceptorTest}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.am.ActivityStartInterceptorTest
+ *  atest FrameworksServicesTests:com.android.server.am.ActivityStartInterceptorTest
  */
 @Presubmit
 @SmallTest
@@ -82,7 +83,9 @@
     @Mock
     private Context mContext;
     @Mock
-    private ActivityManagerService mService;
+    private ActivityManagerService mAm;
+    @Mock
+    private ActivityTaskManagerService mService;
     @Mock
     private ActivityStackSupervisor mSupervisor;
     @Mock
@@ -92,11 +95,11 @@
     @Mock
     private UserManager mUserManager;
     @Mock
-    private UserController mUserController;
-    @Mock
     private KeyguardManager mKeyguardManager;
     @Mock
     private PackageManagerService mPackageManager;
+    @Mock
+    private ActivityManagerInternal mAmInternal;
 
     private ActivityStartInterceptor mInterceptor;
     private ActivityInfo mAInfo = new ActivityInfo();
@@ -104,17 +107,21 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext,
-                mUserController);
+        mService.mAm = mAm;
+        mService.mAmInternal = mAmInternal;
+        mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext);
         mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
                 TEST_START_FLAGS, TEST_CALLING_PACKAGE);
 
+        // Mock ActivityManagerInternal
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+        LocalServices.addService(ActivityManagerInternal.class, mAmInternal);
+
         // Mock DevicePolicyManagerInternal
         LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
         LocalServices.addService(DevicePolicyManagerInternal.class,
                 mDevicePolicyManager);
-        when(mDevicePolicyManager
-                        .createShowAdminSupportIntent(TEST_USER_ID, true))
+        when(mDevicePolicyManager.createShowAdminSupportIntent(TEST_USER_ID, true))
                 .thenReturn(ADMIN_SUPPORT_INTENT);
         when(mService.getPackageManagerInternalLocked()).thenReturn(mPackageManagerInternal);
 
@@ -191,7 +198,7 @@
     @Test
     public void testWorkChallenge() {
         // GIVEN that the user the activity is starting as is currently locked
-        when(mUserController.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
+        when(mAmInternal.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
 
         // THEN calling intercept returns true
         mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 1520859..90607ad 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -41,6 +41,7 @@
 import android.content.pm.ActivityInfo.WindowLayout;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -53,8 +54,10 @@
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
 import static com.android.server.am.ActivityManagerService.ANIMATE;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -62,21 +65,22 @@
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 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.times;
 
-import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.am.ActivityStarter.Factory;
 import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
 import com.android.server.am.TaskRecord.TaskRecordFactory;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Tests for the {@link ActivityStarter} class.
@@ -88,9 +92,10 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityStarterTests extends ActivityTestsBase {
-    private ActivityManagerService mService;
+    private ActivityTaskManagerService mService;
     private ActivityStarter mStarter;
     private ActivityStartController mController;
+    private ActivityMetricsLogger mActivityMetricsLogger;
 
     private static final int PRECONDITION_NO_CALLER_APP = 1;
     private static final int PRECONDITION_NO_INTENT_COMPONENT = 1 << 1;
@@ -104,11 +109,17 @@
     private static final int PRECONDITION_CANNOT_START_ANY_ACTIVITY = 1 << 9;
     private static final int PRECONDITION_DISALLOW_APP_SWITCHING = 1 << 10;
 
+    private static final int FAKE_CALLING_UID = 666;
+    private static final int FAKE_REAL_CALLING_UID = 667;
+    private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude";
+
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mService = createActivityManagerService();
+        mService = createActivityTaskManagerService();
         mController = mock(ActivityStartController.class);
+        mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
+        clearInvocations(mActivityMetricsLogger);
         mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor,
                 mock(ActivityStartInterceptor.class));
     }
@@ -189,20 +200,20 @@
      */
     private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
             int expectedResult) {
-        final ActivityManagerService service = createActivityManagerService();
+        final ActivityTaskManagerService service = mService;
         final IPackageManager packageManager = mock(IPackageManager.class);
         final ActivityStartController controller = mock(ActivityStartController.class);
 
         final ActivityStarter starter = new ActivityStarter(controller, service,
                 service.mStackSupervisor, mock(ActivityStartInterceptor.class));
+        prepareStarter(launchFlags);
         final IApplicationThread caller = mock(IApplicationThread.class);
 
         // If no caller app, return {@code null} {@link ProcessRecord}.
         final ProcessRecord record = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
-                ? null : new ProcessRecord(null, mock(BatteryStatsImpl.class),
-                mock(ApplicationInfo.class), null, 0);
+                ? null : new ProcessRecord(service.mAm, mock(ApplicationInfo.class), null, 0);
 
-        doReturn(record).when(service).getRecordForAppLocked(anyObject());
+        doReturn(record).when(service.mAm).getRecordForAppLocked(anyObject());
 
         final Intent intent = new Intent();
         intent.setFlags(launchFlags);
@@ -236,8 +247,8 @@
         }
 
         if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
-            doReturn(false).when(service).checkAppSwitchAllowedLocked(anyInt(), anyInt(), anyInt(),
-                    anyInt(), any());
+            doReturn(false).when(service).checkAppSwitchAllowedLocked(
+                    anyInt(), anyInt(), anyInt(), anyInt(), any());
         }
 
         if (containsConditions(preconditions,PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
@@ -311,9 +322,6 @@
                 .setCreateStack(false)
                 .build();
 
-        // supervisor needs a focused stack.
-        mService.mStackSupervisor.mFocusedStack = stack;
-
         // use factory that only returns spy task.
         final TaskRecordFactory factory = mock(TaskRecordFactory.class);
         TaskRecord.setTaskRecordFactory(factory);
@@ -327,6 +335,16 @@
         doReturn(stack).when(mService.mStackSupervisor)
                 .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
 
+        // Set up mock package manager internal and make sure no unmocked methods are called
+        PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class,
+                invocation -> {
+                    throw new RuntimeException("Not stubbed");
+                });
+        doReturn(mockPackageManager).when(mService.mAm).getPackageManagerInternalLocked();
+
+        // Never review permissions
+        doReturn(false).when(mockPackageManager).isPermissionsReviewRequired(any(), anyInt());
+
         final Intent intent = new Intent();
         intent.addFlags(launchFlags);
         intent.setComponent(ActivityBuilder.getDefaultComponent());
@@ -403,8 +421,8 @@
         reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
         // Set focus back to primary.
-        mService.mStackSupervisor.setFocusStackUnchecked("testSplitScreenDeliverToTop",
-                focusActivity.getStack());
+        final ActivityStack focusStack = focusActivity.getStack();
+        focusStack.moveToFront("testSplitScreenDeliverToTop");
 
         doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
 
@@ -452,6 +470,7 @@
     @Test
     public void testTaskModeViolation() {
         final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+        ((TestActivityDisplay) display).removeAllTasks();
         assertNoTasks(display);
 
         final ActivityStarter starter = prepareStarter(0);
@@ -471,4 +490,46 @@
             assertTrue(stack.getAllTasks().isEmpty());
         }
     }
+
+    /**
+     * This test ensures that activity starts are not being logged when the logging is disabled.
+     */
+    @Test
+    public void testActivityStartsLogging_noLoggingWhenDisabled() {
+        doReturn(false).when(mService.mAm).isActivityStartsLoggingEnabled();
+        doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
+
+        ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK);
+        starter.setReason("testActivityStartsLogging_noLoggingWhenDisabled").execute();
+
+        // verify logging wasn't done
+        verify(mActivityMetricsLogger, never()).logActivityStart(any(), any(), any(), anyInt(),
+                any(), anyInt(), anyBoolean(), anyInt(), anyInt(), anyBoolean(), anyInt(), any(),
+                anyInt(), anyBoolean(), any(), anyBoolean());
+    }
+
+    /**
+     * This test ensures that activity starts are being logged when the logging is enabled.
+     */
+    @Test
+    public void testActivityStartsLogging_logsWhenEnabled() {
+        // note: conveniently this package doesn't have any activity visible
+        doReturn(true).when(mService.mAm).isActivityStartsLoggingEnabled();
+        doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
+
+        ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
+                .setCallingUid(FAKE_CALLING_UID)
+                .setRealCallingUid(FAKE_REAL_CALLING_UID)
+                .setCallingPackage(FAKE_CALLING_PACKAGE)
+                .setOriginatingPendingIntent(null);
+
+        starter.setReason("testActivityStartsLogging_logsWhenEnabled").execute();
+
+        // verify the above activity start was logged
+        verify(mActivityMetricsLogger, times(1)).logActivityStart(any(), any(), any(),
+                eq(FAKE_CALLING_UID), eq(FAKE_CALLING_PACKAGE), anyInt(), anyBoolean(),
+                eq(FAKE_REAL_CALLING_UID), anyInt(), anyBoolean(), anyInt(),
+                eq(ActivityBuilder.getDefaultComponent().getPackageName()), anyInt(), anyBoolean(),
+                any(), eq(false));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 1cd111f..a9e0aa8 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -16,24 +16,36 @@
 
 package com.android.server.am;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
+import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
+import android.content.pm.PackageManagerInternal;
+import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.DisplayWindowController;
 
 import org.junit.Rule;
+import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 
 import android.app.IApplicationThread;
@@ -48,9 +60,11 @@
 import android.hardware.display.DisplayManager;
 import android.os.HandlerThread;
 import android.os.Looper;
+import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
 import android.support.test.InstrumentationRegistry;
 import android.testing.DexmakerShareClassLoaderRule;
+import android.util.SparseIntArray;
 
 
 import com.android.internal.app.IVoiceInteractor;
@@ -66,6 +80,8 @@
 import org.junit.Before;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
 
 /**
  * A base class to handle common operations in activity related unit tests.
@@ -91,6 +107,7 @@
         if (!sOneTimeSetupDone) {
             sOneTimeSetupDone = true;
             MockitoAnnotations.initMocks(this);
+            AttributeCache.init(mContext);
         }
         mHandlerThread = new HandlerThread("ActivityTestsBaseThread");
         mHandlerThread.start();
@@ -101,19 +118,45 @@
         mHandlerThread.quitSafely();
     }
 
-    protected ActivityManagerService createActivityManagerService() {
-        final ActivityManagerService service =
-                setupActivityManagerService(new TestActivityManagerService(mContext));
-        AttributeCache.init(mContext);
-        return service;
+    protected ActivityTaskManagerService createActivityTaskManagerService() {
+        final TestActivityTaskManagerService atm =
+                spy(new TestActivityTaskManagerService(mContext));
+        setupActivityManagerService(atm);
+        return atm;
     }
 
-    protected ActivityManagerService setupActivityManagerService(ActivityManagerService service) {
-        service = spy(service);
-        doReturn(mock(IPackageManager.class)).when(service).getPackageManager();
-        doNothing().when(service).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
-        service.mWindowManager = prepareMockWindowManager();
-        return service;
+    ActivityManagerService createActivityManagerService() {
+        final TestActivityTaskManagerService atm =
+                spy(new TestActivityTaskManagerService(mContext));
+        return setupActivityManagerService(atm);
+    }
+
+    ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
+        final TestActivityManagerService am = spy(new TestActivityManagerService(mContext, atm));
+        setupActivityManagerService(am, atm);
+        return am;
+    }
+
+    void setupActivityManagerService(
+            TestActivityManagerService am, TestActivityTaskManagerService atm) {
+        atm.setActivityManagerService(am);
+        atm.mAmInternal = am.getLocalService();
+        am.mAtmInternal = atm.getLocalService();
+        // Makes sure the supervisor is using with the spy object.
+        atm.mStackSupervisor.setService(atm);
+        doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
+        doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
+        am.mWindowManager = prepareMockWindowManager();
+        atm.setWindowManager(am.mWindowManager);
+
+        // Put a home stack on the default display, so that we'll always have something focusable.
+        final TestActivityStackSupervisor supervisor =
+                (TestActivityStackSupervisor) atm.mStackSupervisor;
+        supervisor.mHomeStack = supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_HOME, ON_TOP);
+        final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
+                .setStack(supervisor.mHomeStack).build();
+        new ActivityBuilder(atm).setTask(task).build();
     }
 
     /**
@@ -125,7 +168,7 @@
 
 
 
-        private final ActivityManagerService mService;
+        private final ActivityTaskManagerService mService;
 
         private ComponentName mComponent;
         private TaskRecord mTaskRecord;
@@ -134,7 +177,7 @@
         private ActivityStack mStack;
         private int mActivityFlags;
 
-        ActivityBuilder(ActivityManagerService service) {
+        ActivityBuilder(ActivityTaskManagerService service) {
             mService = service;
         }
 
@@ -205,10 +248,12 @@
                 mTaskRecord.addActivityToTop(activity);
             }
 
-            activity.setProcess(new ProcessRecord(null, null,
-                    mService.mContext.getApplicationInfo(), "name", 12345));
-            activity.app.thread = mock(IApplicationThread.class);
-
+            final WindowProcessController wpc = new WindowProcessController(mService,
+                    mService.mContext.getApplicationInfo(), "name", 12345,
+                    UserHandle.getUserId(12345), mock(Object.class),
+                    mock(WindowProcessListener.class));
+            wpc.setThread(mock(IApplicationThread.class));
+            activity.setProcess(wpc);
             return activity;
         }
     }
@@ -225,7 +270,8 @@
         private ComponentName mComponent;
         private String mPackage;
         private int mFlags = 0;
-        private int mTaskId = 0;
+        // Task id 0 is reserved in ARC for the home app.
+        private int mTaskId = 1;
         private int mUserId = 0;
         private IVoiceInteractionSession mVoiceSession;
         private boolean mCreateStack = true;
@@ -304,7 +350,7 @@
             task.userId = mUserId;
 
             if (mStack != null) {
-                mSupervisor.setFocusStackUnchecked("test", mStack);
+                mStack.moveToFront("test");
                 mStack.addTask(task, true, "creating test task");
                 task.setStack(mStack);
                 task.setWindowContainerController();
@@ -316,7 +362,7 @@
         }
 
         private static class TestTaskRecord extends TaskRecord {
-            TestTaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info,
+            TestTaskRecord(ActivityTaskManagerService service, int _taskId, ActivityInfo info,
                        Intent _intent, IVoiceInteractionSession _voiceSession,
                        IVoiceInteractor _voiceInteractor) {
                 super(service, _taskId, info, _intent, _voiceSession, _voiceInteractor);
@@ -333,32 +379,27 @@
         }
     }
 
-    /**
-     * An {@link ActivityManagerService} subclass which provides a test
-     * {@link ActivityStackSupervisor}.
-     */
-    protected static class TestActivityManagerService extends ActivityManagerService {
-        private ClientLifecycleManager mLifecycleManager;
+    protected static class TestActivityTaskManagerService extends ActivityTaskManagerService {
         private LockTaskController mLockTaskController;
+        private ActivityTaskManagerInternal mInternal;
+        private PackageManagerInternal mPmInternal;
 
-        TestActivityManagerService(Context context) {
+        TestActivityTaskManagerService(Context context) {
             super(context);
             mSupportsMultiWindow = true;
             mSupportsMultiDisplay = true;
             mSupportsSplitScreenMultiWindow = true;
             mSupportsFreeformWindowManagement = true;
             mSupportsPictureInPicture = true;
-            mWindowManager = WindowTestUtils.getMockWindowManagerService();
+            mUgmInternal = mock(UriGrantsManagerInternal.class);
         }
 
         @Override
-        public ClientLifecycleManager getLifecycleManager() {
-            if (mLifecycleManager == null) {
-                return super.getLifecycleManager();
-            }
-            return mLifecycleManager;
+        int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
+            return userId;
         }
 
+        @Override
         public LockTaskController getLockTaskController() {
             if (mLockTaskController == null) {
                 mLockTaskController = spy(super.getLockTaskController());
@@ -367,10 +408,6 @@
             return mLockTaskController;
         }
 
-        void setLifecycleManager(ClientLifecycleManager manager) {
-            mLifecycleManager = manager;
-        }
-
         @Override
         final protected ActivityStackSupervisor createStackSupervisor() {
             final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
@@ -397,12 +434,53 @@
         }
 
         protected ActivityStackSupervisor createTestSupervisor() {
-            return new TestActivityStackSupervisor(this, mHandlerThread.getLooper());
+            return new TestActivityStackSupervisor(this, mH.getLooper());
+        }
+
+        ActivityTaskManagerInternal getLocalService() {
+            if (mInternal == null) {
+                mInternal = new ActivityTaskManagerService.LocalService();
+            }
+            return mInternal;
+        }
+
+        PackageManagerInternal getPackageManagerInternalLocked() {
+            if (mPmInternal == null) {
+                mPmInternal = mock(PackageManagerInternal.class);
+                doReturn(false).when(mPmInternal).isPermissionsReviewRequired(anyString(), anyInt());
+            }
+            return mPmInternal;
+        }
+    }
+
+    /**
+     * An {@link ActivityManagerService} subclass which provides a test
+     * {@link ActivityStackSupervisor}.
+     */
+    static class TestActivityManagerService extends ActivityManagerService {
+
+        private ActivityManagerInternal mInternal;
+
+        TestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
+            super(context, atm);
+            mUgmInternal = mock(UriGrantsManagerInternal.class);
         }
 
         @Override
         void updateUsageStats(ActivityRecord component, boolean resumed) {
         }
+
+        @Override
+        Configuration getGlobalConfiguration() {
+            return mContext.getResources().getConfiguration();
+        }
+
+        ActivityManagerInternal getLocalService() {
+            if (mInternal == null) {
+                mInternal = new LocalService();
+            }
+            return mInternal;
+        }
     }
 
     /**
@@ -413,7 +491,7 @@
         private ActivityDisplay mDisplay;
         private KeyguardController mKeyguardController;
 
-        public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
+        public TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
             super(service, looper);
             mDisplayManager =
                     (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
@@ -437,13 +515,6 @@
         ActivityDisplay getDefaultDisplay() {
             return mDisplay;
         }
-
-        // Just return the current front task. This is called internally so we cannot use spy to mock this out.
-        @Override
-        ActivityStack getNextFocusableStackLocked(ActivityStack currentFocus,
-                boolean ignoreCurrent) {
-            return mFocusedStack;
-        }
     }
 
     protected static class TestActivityDisplay extends ActivityDisplay {
@@ -480,6 +551,15 @@
         protected DisplayWindowController createWindowContainerController() {
             return mock(DisplayWindowController.class);
         }
+
+        void removeAllTasks() {
+            for (int i = 0; i < getChildCount(); i++) {
+                final ActivityStack stack = getChildAt(i);
+                for (TaskRecord task : (List<TaskRecord>) stack.getAllTasks()) {
+                    stack.removeTask(task, "removeAllTasks", REMOVE_TASK_MODE_DESTROYING);
+                }
+            }
+        }
     }
 
     private static WindowManagerService prepareMockWindowManager() {
@@ -493,6 +573,12 @@
             return null;
         }).when(service).inSurfaceTransaction(any());
 
+        doAnswer((InvocationOnMock invocationOnMock) -> {
+            final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
+            displayIds.put(0, 0);
+            return null;
+        }).when(service).getDisplaysInFocusOrder(any());
+
         return service;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
index 3d11c4c..243c1b3 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -66,7 +66,7 @@
     @UiThreadTest
     public void testCreateWorks() throws Exception {
         AppErrorDialog.Data data = new AppErrorDialog.Data();
-        data.proc = new ProcessRecord(null, null, mContext.getApplicationInfo(), "name", 12345);
+        data.proc = new ProcessRecord(null, mContext.getApplicationInfo(), "name", 12345);
         data.result = new AppErrorResult();
 
         AppErrorDialog dialog = new AppErrorDialog(mContext, mService, data);
diff --git a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
index ce88d84..f74d116 100644
--- a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
@@ -35,6 +35,7 @@
 
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.os.Bundle;
@@ -87,6 +88,7 @@
     private Object mCallbacksLock;
     private Handler mHandler;
     private IActivityManager mAm;
+    private IActivityTaskManager mAtm;
     private IWindowManager mWm;
     private AppOpsManager mAppOpsManager;
 
@@ -104,13 +106,14 @@
     public void setUp() throws Exception {
         super.setUp();
         mAm = mock(IActivityManager.class);
+        mAtm = mock(IActivityTaskManager.class);
         mWm = mock(IWindowManager.class);
         mAppOpsManager = mock(AppOpsManager.class);
         mContext =  InstrumentationRegistry.getContext();
         mHandler = new Handler(Looper.getMainLooper());
         mCallbacksLock = new Object();
         mCallbacks = new Callbacks();
-        mDataRequester = new AssistDataRequester(mContext, mAm, mWm, mAppOpsManager, mCallbacks,
+        mDataRequester = new AssistDataRequester(mContext, mWm, mAppOpsManager, mCallbacks,
                 mCallbacksLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
 
         // Gate the continuation of the assist data callbacks until we are ready within the tests
@@ -125,7 +128,7 @@
                 }
             });
             return true;
-        }).when(mAm).requestAssistContextExtras(anyInt(), any(), any(), any(), anyBoolean(),
+        }).when(mAtm).requestAssistContextExtras(anyInt(), any(), any(), any(), anyBoolean(),
                 anyBoolean());
         doAnswer(invocation -> {
             mHandler.post(() -> {
@@ -142,7 +145,7 @@
 
     private void setupMocks(boolean currentActivityAssistAllowed, boolean assistStructureAllowed,
             boolean assistScreenshotAllowed) throws Exception {
-        doReturn(currentActivityAssistAllowed).when(mAm).isAssistDataAllowedOnCurrentActivity();
+        doReturn(currentActivityAssistAllowed).when(mAtm).isAssistDataAllowedOnCurrentActivity();
         doReturn(assistStructureAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
                 .checkOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString());
         doReturn(assistScreenshotAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
@@ -240,7 +243,7 @@
     public void testDisallowAssistContextExtras_expectNullDataCallbacks() throws Exception {
         setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
-        doReturn(false).when(mAm).requestAssistContextExtras(anyInt(), any(), any(), any(),
+        doReturn(false).when(mAtm).requestAssistContextExtras(anyInt(), any(), any(), any(),
                 anyBoolean(), anyBoolean());
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
@@ -371,4 +374,4 @@
             });
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
index 161c287..fbe552d 100644
--- a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
@@ -57,14 +57,14 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class LaunchParamsControllerTests extends ActivityTestsBase {
-    private ActivityManagerService mService;
+    private ActivityTaskManagerService mService;
     private LaunchParamsController mController;
 
     @Before
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mService = createActivityManagerService();
+        mService = createActivityTaskManagerService();
         mController = new LaunchParamsController(mService);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java b/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
index 2baf995..e73661b 100644
--- a/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
@@ -62,7 +62,7 @@
         mService.mHandlerThread.getThreadHandler().runWithScissors(() -> {
             mHandler = new TestHandler(null, mClock);
         }, 0);
-        mRegistry = new PendingRemoteAnimationRegistry(mService, mHandler);
+        mRegistry = new PendingRemoteAnimationRegistry(mService.mActivityTaskManager, mHandler);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index b73ac89..ee484d6 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -16,7 +16,8 @@
 
 package com.android.server.am;
 
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -40,10 +41,9 @@
 
 import static java.lang.Integer.MAX_VALUE;
 
-import android.annotation.TestApi;
-import android.app.ActivityManager;
 import android.app.ActivityManager.RecentTaskInfo;
 import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
 import android.content.Context;
@@ -53,7 +53,6 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.Debug;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -96,7 +95,7 @@
     private static int INVALID_STACK_ID = 999;
 
     private Context mContext = InstrumentationRegistry.getContext();
-    private ActivityManagerService mService;
+    private TestActivityTaskManagerService mService;
     private ActivityDisplay mDisplay;
     private ActivityDisplay mOtherDisplay;
     private ActivityStack mStack;
@@ -110,47 +109,19 @@
 
     private CallbacksRecorder mCallbacksRecorder;
 
-    class TestUserController extends UserController {
-        TestUserController(ActivityManagerService service) {
-            super(service);
-        }
-
-        @Override
-        int[] getCurrentProfileIds() {
-            return new int[] { TEST_USER_0_ID, TEST_QUIET_USER_ID };
-        }
-
-        @Override
-        Set<Integer> getProfileIds(int userId) {
-            Set<Integer> profileIds = new HashSet<>();
-            profileIds.add(TEST_USER_0_ID);
-            profileIds.add(TEST_QUIET_USER_ID);
-            return profileIds;
-        }
-
-        @Override
-        UserInfo getUserInfo(int userId) {
-            switch (userId) {
-                case TEST_USER_0_ID:
-                case TEST_USER_1_ID:
-                    return DEFAULT_USER_INFO;
-                case TEST_QUIET_USER_ID:
-                    return QUIET_USER_INFO;
-            }
-            return null;
-        }
-    }
-
     @Before
     @Override
     public void setUp() throws Exception {
         super.setUp();
 
         mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
-        mService = setupActivityManagerService(new MyTestActivityManagerService(mContext));
+        mService = spy(new MyTestActivityTaskManagerService(mContext));
+        final TestActivityManagerService am =
+                spy(new MyTestActivityManagerService(mContext, mService));
+        setupActivityManagerService(am, mService);
         mRecentTasks = (TestRecentTasks) mService.getRecentTasks();
         mRecentTasks.loadParametersFromResources(mContext.getResources());
-        mHomeStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+        mHomeStack = mService.mStackSupervisor.getDefaultDisplay().getOrCreateStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
         mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -306,13 +277,11 @@
     public void testAddTaskCompatibleActivityType_expectRemove() throws Exception {
         // Test with undefined activity type since the type is not persisted by the task persister
         // and we want to ensure that a new task will match a restored task
-        Configuration config1 = new Configuration();
-        config1.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
         TaskRecord task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .setStack(mStack)
                 .build();
-        task1.onConfigurationChanged(config1);
+        setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED);
         assertTrue(task1.getActivityType() == ACTIVITY_TYPE_UNDEFINED);
         mRecentTasks.add(task1);
         mCallbacksRecorder.clear();
@@ -332,14 +301,12 @@
 
     @Test
     public void testAddTaskCompatibleActivityTypeDifferentUser_expectNoRemove() throws Exception {
-        Configuration config1 = new Configuration();
-        config1.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
         TaskRecord task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .setStack(mStack)
                 .setUserId(TEST_USER_0_ID)
                 .build();
-        task1.onConfigurationChanged(config1);
+        setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED);
         assertTrue(task1.getActivityType() == ACTIVITY_TYPE_UNDEFINED);
         mRecentTasks.add(task1);
         mCallbacksRecorder.clear();
@@ -359,24 +326,20 @@
 
     @Test
     public void testAddTaskCompatibleWindowingMode_expectRemove() throws Exception {
-        Configuration config1 = new Configuration();
-        config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
         TaskRecord task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .setStack(mStack)
                 .build();
-        task1.onConfigurationChanged(config1);
+        setTaskWindowingMode(task1, WINDOWING_MODE_UNDEFINED);
         assertTrue(task1.getWindowingMode() == WINDOWING_MODE_UNDEFINED);
         mRecentTasks.add(task1);
         mCallbacksRecorder.clear();
 
-        Configuration config2 = new Configuration();
-        config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         TaskRecord task2 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .setStack(mStack)
                 .build();
-        task2.onConfigurationChanged(config2);
+        setTaskWindowingMode(task2, WINDOWING_MODE_FULLSCREEN);
         assertTrue(task2.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
         mRecentTasks.add(task2);
 
@@ -389,23 +352,19 @@
 
     @Test
     public void testAddTaskIncompatibleWindowingMode_expectNoRemove() throws Exception {
-        Configuration config1 = new Configuration();
-        config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         TaskRecord task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .setStack(mStack)
                 .build();
-        task1.onConfigurationChanged(config1);
+        setTaskWindowingMode(task1, WINDOWING_MODE_FULLSCREEN);
         assertTrue(task1.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
         mRecentTasks.add(task1);
 
-        Configuration config2 = new Configuration();
-        config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
         TaskRecord task2 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .setStack(mStack)
                 .build();
-        task2.onConfigurationChanged(config2);
+        setTaskWindowingMode(task2, WINDOWING_MODE_PINNED);
         assertTrue(task2.getWindowingMode() == WINDOWING_MODE_PINNED);
         mRecentTasks.add(task2);
 
@@ -674,10 +633,46 @@
     }
 
     @Test
-    public void testNotRecentsComponent_denyApiAccess() throws Exception {
-        doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(),
-                anyInt(), anyInt());
+    public void testRemoveAllVisibleTasks() throws Exception {
+        mRecentTasks.setParameters(-1 /* min */, 3 /* max */, 100 /* ms */);
 
+        // Create some set of tasks, some of which are visible and some are not
+        TaskRecord t1 = createTaskBuilder("com.android.pkg1", ".Task1").build();
+        mRecentTasks.add(t1);
+        mRecentTasks.add(setTaskActivityType(
+                createTaskBuilder("com.android.pkg1", ".HomeTask").build(),
+                ACTIVITY_TYPE_HOME));
+        TaskRecord t2 = createTaskBuilder("com.android.pkg2", ".Task2").build();
+        mRecentTasks.add(t2);
+        mRecentTasks.add(setTaskWindowingMode(
+                createTaskBuilder("com.android.pkg1", ".PipTask").build(),
+                WINDOWING_MODE_PINNED));
+        TaskRecord t3 = createTaskBuilder("com.android.pkg3", ".Task3").build();
+        mRecentTasks.add(t3);
+
+        // Create some more tasks that are out of visible range, but are still visible
+        TaskRecord t4 = createTaskBuilder("com.android.pkg3", ".Task4").build();
+        mRecentTasks.add(t4);
+        TaskRecord t5 = createTaskBuilder("com.android.pkg3", ".Task5").build();
+        mRecentTasks.add(t5);
+
+        // Create some more tasks that are out of the active session range, but are still visible
+        TaskRecord t6 = createTaskBuilder("com.android.pkg3", ".Task6").build();
+        t6.lastActiveTime = SystemClock.elapsedRealtime() - 200;
+        mRecentTasks.add(t6);
+        TaskRecord t7 = createTaskBuilder("com.android.pkg3", ".Task7").build();
+        t7.lastActiveTime = SystemClock.elapsedRealtime() - 200;
+        mRecentTasks.add(t7);
+
+        // Remove all the visible tasks and ensure that they are removed
+        mRecentTasks.removeAllVisibleTasks();
+        assertTrimmed(t1, t2, t3, t4, t5, t6, t7);
+    }
+
+    @Test
+    public void testNotRecentsComponent_denyApiAccess() throws Exception {
+        doReturn(PackageManager.PERMISSION_DENIED).when(mService)
+                .checkGetTasksPermission(anyString(), anyInt(), anyInt());
         // Expect the following methods to fail due to recents component not being set
         mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION);
         testRecentTasksApis(false /* expectNoSecurityException */);
@@ -688,8 +683,8 @@
 
     @Test
     public void testRecentsComponent_allowApiAccessWithoutPermissions() throws Exception {
-        doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(),
-                anyInt(), anyInt());
+        doReturn(PackageManager.PERMISSION_DENIED).when(mService)
+                .checkGetTasksPermission(anyString(), anyInt(), anyInt());
 
         // Set the recents component and ensure that the following calls do not fail
         mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.GRANT);
@@ -736,22 +731,9 @@
                 () -> mService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
         assertSecurityException(expectCallable,
                 () -> mService.startActivityFromRecents(0, new Bundle()));
-        assertSecurityException(expectCallable,
-                () -> mService.getTaskSnapshot(0, true));
-        assertSecurityException(expectCallable, () -> {
-            try {
-                mService.registerTaskStackListener(null);
-            } catch (RemoteException e) {
-                // Ignore
-            }
-        });
-        assertSecurityException(expectCallable, () -> {
-            try {
-                mService.unregisterTaskStackListener(null);
-            } catch (RemoteException e) {
-                // Ignore
-            }
-        });
+        assertSecurityException(expectCallable,() -> mService.getTaskSnapshot(0, true));
+        assertSecurityException(expectCallable,() -> mService.registerTaskStackListener(null));
+        assertSecurityException(expectCallable,() -> mService.unregisterTaskStackListener(null));
         assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
         assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
         assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null,
@@ -794,7 +776,23 @@
                 .setFlags(FLAG_ACTIVITY_NEW_DOCUMENT | flags)
                 .build();
         task.affinity = null;
-        task.maxRecents = ActivityManager.getMaxAppRecentsLimitStatic();
+        task.maxRecents = ActivityTaskManager.getMaxAppRecentsLimitStatic();
+        return task;
+    }
+
+    private TaskRecord setTaskActivityType(TaskRecord task,
+            @WindowConfiguration.ActivityType int activityType) {
+        Configuration config1 = new Configuration();
+        config1.windowConfiguration.setActivityType(activityType);
+        task.onConfigurationChanged(config1);
+        return task;
+    }
+
+    private TaskRecord setTaskWindowingMode(TaskRecord task,
+            @WindowConfiguration.WindowingMode int windowingMode) {
+        Configuration config1 = new Configuration();
+        config1.windowConfiguration.setWindowingMode(windowingMode);
+        task.onConfigurationChanged(config1);
         return task;
     }
 
@@ -836,19 +834,26 @@
         }
     }
 
-    private class MyTestActivityManagerService extends TestActivityManagerService {
-        MyTestActivityManagerService(Context context) {
+    private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
+        MyTestActivityTaskManagerService(Context context) {
             super(context);
         }
 
         @Override
-        protected ActivityStackSupervisor createTestSupervisor() {
-            return new MyTestActivityStackSupervisor(this, mHandlerThread.getLooper());
+        protected RecentTasks createRecentTasks() {
+            return new TestRecentTasks(this, mTaskPersister);
         }
 
         @Override
-        protected RecentTasks createRecentTasks() {
-            return new TestRecentTasks(this, mTaskPersister, new TestUserController(this));
+        protected ActivityStackSupervisor createTestSupervisor() {
+            return new MyTestActivityStackSupervisor(this, mH.getLooper());
+        }
+
+    }
+
+    private class MyTestActivityManagerService extends TestActivityManagerService {
+        MyTestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
+            super(context, atm);
         }
 
         @Override
@@ -858,7 +863,7 @@
     }
 
     private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor {
-        public MyTestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
+        public MyTestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
             super(service, looper);
         }
 
@@ -917,7 +922,7 @@
         }
 
         @Override
-        public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) {
+        public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
             if (wasTrimmed) {
                 trimmed.add(task);
             }
@@ -961,9 +966,33 @@
 
         boolean lastAllowed;
 
-        TestRecentTasks(ActivityManagerService service, TaskPersister taskPersister,
-                UserController userController) {
-            super(service, taskPersister, userController);
+        TestRecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) {
+            super(service, taskPersister);
+        }
+
+        @Override
+        Set<Integer> getProfileIds(int userId) {
+            Set<Integer> profileIds = new HashSet<>();
+            profileIds.add(TEST_USER_0_ID);
+            profileIds.add(TEST_QUIET_USER_ID);
+            return profileIds;
+        }
+
+        @Override
+        UserInfo getUserInfo(int userId) {
+            switch (userId) {
+                case TEST_USER_0_ID:
+                case TEST_USER_1_ID:
+                    return DEFAULT_USER_INFO;
+                case TEST_QUIET_USER_ID:
+                    return QUIET_USER_INFO;
+            }
+            return null;
+        }
+
+        @Override
+        int[] getCurrentProfileIds() {
+            return new int[] { TEST_USER_0_ID, TEST_QUIET_USER_ID };
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java b/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
index eefd973..b642d26 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
@@ -16,18 +16,16 @@
 
 package com.android.server.am;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -51,10 +49,9 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class RecentsAnimationTest extends ActivityTestsBase {
-    private static final int TEST_CALLING_PID = 3;
 
     private Context mContext = InstrumentationRegistry.getContext();
-    private ActivityManagerService mService;
+    private TestActivityTaskManagerService mService;
     private ComponentName mRecentsComponent;
 
     @Before
@@ -63,8 +60,8 @@
         super.setUp();
 
         mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
-        mService = setupActivityManagerService(new MyTestActivityManagerService(mContext));
-        AttributeCache.init(mContext);
+        mService = spy(new MyTestActivityTaskManagerService(mContext));
+        setupActivityManagerService(mService);
     }
 
     @Test
@@ -99,8 +96,8 @@
                 eq(REORDER_KEEP_IN_PLACE), any());
     }
 
-    private class MyTestActivityManagerService extends TestActivityManagerService {
-        MyTestActivityManagerService(Context context) {
+    private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
+        MyTestActivityTaskManagerService(Context context) {
             super(context);
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
index c6ce7e1..944f20f 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
@@ -50,7 +50,7 @@
 public class RunningTasksTest extends ActivityTestsBase {
 
     private Context mContext = InstrumentationRegistry.getContext();
-    private ActivityManagerService mService;
+    private ActivityTaskManagerService mService;
 
     private RunningTasks mRunningTasks;
 
@@ -59,7 +59,7 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mService = createActivityManagerService();
+        mService = createActivityTaskManagerService();
         mRunningTasks = new RunningTasks();
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
index 3d323f0..f71a6e7 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
@@ -57,7 +57,7 @@
 
     private final static Rect STACK_BOUNDS = new Rect(0, 0, STACK_WIDTH, STACK_HEIGHT);
 
-    private ActivityManagerService mService;
+    private ActivityTaskManagerService mService;
     private ActivityStack mStack;
     private TaskRecord mTask;
 
@@ -71,7 +71,7 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mService = createActivityManagerService();
+        mService = createActivityTaskManagerService();
         mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         mStack.requestResize(STACK_BOUNDS);
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
index 9e6055d..8d54bc2 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
@@ -44,7 +44,11 @@
         super.setUp();
         mUserManager = UserManager.get(getContext());
         mTaskPersister = new TaskPersister(getContext().getFilesDir());
-        testUserId = createUser(TEST_USER_NAME, 0);
+        // In ARC, the maximum number of supported users is one, which is different from the ones of
+        // most phones (more than 4). This prevents TaskPersisterTest from creating another user for
+        // test. However, since guest users can be added as much as possible, we create guest user
+        // in the test.
+        testUserId = createUser(TEST_USER_NAME, UserInfo.FLAG_GUEST);
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
index 057fdc8..b8680bf 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
@@ -155,16 +155,17 @@
     }
 
     private TaskRecord createTaskRecord(int taskId) {
-        return new TaskRecord(mService, taskId, new Intent(), null, null, null, null, null, false,
-                false, false, 0, 10050, null, new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0,
-                null, 0, false, false, false, 0, 0);
+        return new TaskRecord(mService.mActivityTaskManager, taskId, new Intent(), null, null, null,
+                ActivityBuilder.getDefaultComponent(), null, false, false, false, 0, 10050, null,
+                new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0, null, 0, false, false, false, 0, 0
+        );
     }
 
     private static class TestTaskRecordFactory extends TaskRecordFactory {
         private boolean mCreated = false;
 
         @Override
-        TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+        TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
                 Intent intent,
                 IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
             mCreated = true;
@@ -172,7 +173,7 @@
         }
 
         @Override
-        TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+        TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
                 Intent intent,
                 ActivityManager.TaskDescription taskDescription) {
             mCreated = true;
@@ -180,7 +181,7 @@
         }
 
         @Override
-        TaskRecord create(ActivityManagerService service, int taskId, Intent intent,
+        TaskRecord create(ActivityTaskManagerService service, int taskId, Intent intent,
                 Intent affinityIntent, String affinity, String rootAffinity,
                 ComponentName realActivity,
                 ComponentName origActivity, boolean rootWasReset, boolean autoRemoveRecents,
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java b/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
index 0359096..5cd410e 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
@@ -24,6 +24,7 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManager.TaskDescription;
+import android.app.ActivityTaskManager;
 import android.app.IActivityManager;
 import android.app.ITaskStackListener;
 import android.app.Instrumentation.ActivityMonitor;
@@ -68,7 +69,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mService.unregisterTaskStackListener(mTaskStackListener);
+        ActivityTaskManager.getService().unregisterTaskStackListener(mTaskStackListener);
         mTaskStackListener = null;
     }
 
@@ -180,6 +181,7 @@
 
         final ActivityTaskChangeCallbacks activity =
                 (ActivityTaskChangeCallbacks) startTestActivity(ActivityTaskChangeCallbacks.class);
+        activity.setDetachedFromWindowLatch(onDetachedFromWindowLatch);
         final int id = activity.getTaskId();
 
         // Test for onTaskCreated.
@@ -206,6 +208,7 @@
         assertEquals(1, taskRemovedLatch.getCount());
         waitForCallback(taskRemovedLatch);
         assertEquals(id, params[0]);
+        waitForCallback(onDetachedFromWindowLatch);
         assertTrue(activity.onDetachedFromWindowCalled);
     }
 
@@ -227,7 +230,7 @@
 
     private void registerTaskStackChangedListener(ITaskStackListener listener) throws Exception {
         mTaskStackListener = listener;
-        mService.registerTaskStackListener(listener);
+        ActivityTaskManager.getService().registerTaskStackListener(listener);
     }
 
     private void waitForCallback(CountDownLatch latch) {
@@ -287,11 +290,17 @@
     }
 
     public static class ActivityTaskChangeCallbacks extends Activity {
-        public boolean onDetachedFromWindowCalled = false;;
+        boolean onDetachedFromWindowCalled = false;
+        CountDownLatch onDetachedFromWindowCountDownLatch;
 
         @Override
         public void onDetachedFromWindow() {
             onDetachedFromWindowCalled = true;
+            onDetachedFromWindowCountDownLatch.countDown();
+        }
+
+        void setDetachedFromWindowLatch(CountDownLatch countDownLatch) {
+            onDetachedFromWindowCountDownLatch = countDownLatch;
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java b/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
index c27fd07..3cdeba6 100644
--- a/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
@@ -36,6 +36,7 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.util.ArrayList;
+import java.util.function.Consumer;
 
 @SmallTest
 @Presubmit
@@ -47,7 +48,7 @@
 
     @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
 
-    @Mock private DataChangedJournal.Consumer mConsumer;
+    @Mock private Consumer<String> mConsumer;
 
     private File mFile;
     private DataChangedJournal mJournal;
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
index 2d5afad..525135c 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
@@ -134,7 +134,12 @@
     }
 
     @Override
-    public boolean isPermissionReviewModeEnabled() {
+    public boolean arePermissionsIndividuallyControlled() {
+        return false;
+    }
+
+    @Override
+    public boolean isWirelessConsentModeEnabled() {
         return false;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index cd39285..2b5b812 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -18,6 +18,7 @@
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.backup.IBackupManager;
@@ -181,6 +182,11 @@
         }
 
         @Override
+        IActivityTaskManager getIActivityTaskManager() {
+            return services.iactivityTaskManager;
+        }
+
+        @Override
         ActivityManagerInternal getActivityManagerInternal() {
             return services.activityManagerInternal;
         }
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 1acecfc..a23636c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3726,7 +3726,7 @@
 
     private void verifyLockTaskState(int userId, String[] packages, int flags) throws Exception {
         verify(getServices().iactivityManager).updateLockTaskPackages(userId, packages);
-        verify(getServices().iactivityManager).updateLockTaskFeatures(userId, flags);
+        verify(getServices().iactivityTaskManager).updateLockTaskFeatures(userId, flags);
     }
 
     private void verifyCanSetLockTask(int uid, int userId, ComponentName who, String[] packages,
@@ -3819,7 +3819,7 @@
         // Lock task packages cleared when loading user data and when the user becomes unaffiliated.
         verify(getServices().iactivityManager, times(2)).updateLockTaskPackages(
                 MANAGED_PROFILE_USER_ID, new String[0]);
-        verify(getServices().iactivityManager, times(2)).updateLockTaskFeatures(
+        verify(getServices().iactivityTaskManager, times(2)).updateLockTaskFeatures(
                 MANAGED_PROFILE_USER_ID, DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
 
         // Verify that lock task packages were not cleared for the DO
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index e753df1..4724f1c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -28,6 +28,7 @@
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.app.NotificationManager;
 import android.app.backup.IBackupManager;
 import android.app.usage.UsageStatsManagerInternal;
@@ -91,6 +92,7 @@
     public final IIpConnectivityMetrics iipConnectivityMetrics;
     public final IWindowManager iwindowManager;
     public final IActivityManager iactivityManager;
+    public final IActivityTaskManager iactivityTaskManager;
     public ActivityManagerInternal activityManagerInternal;
     public final IPackageManager ipackageManager;
     public final IBackupManager ibackupManager;
@@ -129,6 +131,7 @@
         iipConnectivityMetrics = mock(IIpConnectivityMetrics.class);
         iwindowManager = mock(IWindowManager.class);
         iactivityManager = mock(IActivityManager.class);
+        iactivityTaskManager = mock(IActivityTaskManager.class);
         activityManagerInternal = mock(ActivityManagerInternal.class);
         ipackageManager = mock(IPackageManager.class);
         ibackupManager = mock(IBackupManager.class);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
new file mode 100644
index 0000000..14695c5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.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.server.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.Nullable;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link ArcTerminationActionFromAvr} */
+@SmallTest
+@RunWith(JUnit4.class)
+public class ArcTerminationActionFromAvrTest {
+
+    private HdmiDeviceInfo mDeviceInfoForTests;
+    private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+    private ArcTerminationActionFromAvr mAction;
+
+    private TestLooper mTestLooper = new TestLooper();
+    private boolean mSendCecCommandSuccess;
+    private boolean mShouldDispatchReportArcTerminated;
+    private boolean mArcEnabled;
+    private boolean mSetArcStatusCalled;
+
+    @Before
+    public void setUp() {
+        mDeviceInfoForTests = new HdmiDeviceInfo(1000, 1);
+
+        HdmiControlService hdmiControlService =
+                new HdmiControlService(null) {
+                    @Override
+                    void sendCecCommand(
+                            HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+                        switch (command.getOpcode()) {
+                            case Constants.MESSAGE_TERMINATE_ARC:
+                                if (callback != null) {
+                                    callback.onSendCompleted(
+                                            mSendCecCommandSuccess
+                                                    ? SendMessageResult.SUCCESS
+                                                    : SendMessageResult.NACK);
+                                }
+                                if (mShouldDispatchReportArcTerminated) {
+                                    mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
+                                            HdmiCecMessageBuilder.buildReportArcTerminated(
+                                                    Constants.ADDR_TV,
+                                                    mHdmiCecLocalDeviceAudioSystem.mAddress));
+                                }
+                                break;
+                            default:
+                                throw new IllegalArgumentException("Unexpected message");
+                        }
+                    }
+
+                    @Override
+                    boolean isPowerStandby() {
+                        return false;
+                    }
+
+                    @Override
+                    boolean isAddressAllocated() {
+                        return true;
+                    }
+
+                    @Override
+                    Looper getServiceLooper() {
+                        return mTestLooper.getLooper();
+                    }
+                };
+
+        mHdmiCecLocalDeviceAudioSystem =
+                new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
+                    @Override
+                    HdmiDeviceInfo getDeviceInfo() {
+                        return mDeviceInfoForTests;
+                    }
+
+                    @Override
+                    void setArcStatus(boolean enabled) {
+                        mSetArcStatusCalled = true;
+                        mArcEnabled = enabled;
+                    }
+                };
+        mHdmiCecLocalDeviceAudioSystem.init();
+        Looper looper = mTestLooper.getLooper();
+        hdmiControlService.setIoLooper(looper);
+
+        mArcEnabled = true;
+        mAction = new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
+    }
+
+    @Test
+    public void testSendMessage_NotSuccess() {
+        mSendCecCommandSuccess = false;
+        mShouldDispatchReportArcTerminated = false;
+        mSetArcStatusCalled = false;
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+
+        mTestLooper.dispatchAll();
+        assertThat(mSetArcStatusCalled).isFalse();
+        assertThat(mArcEnabled).isTrue();
+    }
+
+    @Test
+    public void testReportArcTerminated_NotReceived() {
+        mSendCecCommandSuccess = true;
+        mShouldDispatchReportArcTerminated = false;
+        mSetArcStatusCalled = false;
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+
+        mTestLooper.moveTimeForward(1000);
+        mTestLooper.dispatchAll();
+        assertThat(mSetArcStatusCalled).isFalse();
+        assertThat(mArcEnabled).isTrue();
+    }
+
+    @Test
+    public void testReportArcTerminated_Received() {
+        mSendCecCommandSuccess = true;
+        mShouldDispatchReportArcTerminated = true;
+        mSetArcStatusCalled = false;
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+
+        mTestLooper.moveTimeForward(1000);
+        mTestLooper.dispatchAll();
+        assertThat(mSetArcStatusCalled).isTrue();
+        assertThat(mArcEnabled).isFalse();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
new file mode 100644
index 0000000..e114e03
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.hdmi;
+
+import static org.junit.Assert.assertEquals;
+
+import android.annotation.Nullable;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import com.android.server.hdmi.HdmiCecLocalDeviceAudioSystem.TvSystemAudioModeSupportedCallback;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link DetectTvSystemAudioModeSupportAction} class. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class DetectTvSystemAudioModeSupportActionTest {
+
+    private HdmiDeviceInfo mDeviceInfoForTests;
+    private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+    private DetectTvSystemAudioModeSupportAction mAction;
+
+    private TestLooper mTestLooper = new TestLooper();
+    private boolean mSendCecCommandSuccess;
+    private boolean mShouldDispatchFeatureAbort;
+    private Boolean mSupported;
+
+    @Before
+    public void SetUp() {
+        mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
+        HdmiControlService hdmiControlService =
+                new HdmiControlService(null) {
+
+                    @Override
+                    void sendCecCommand(
+                            HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+                        switch (command.getOpcode()) {
+                            case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+                                if (callback != null) {
+                                    callback.onSendCompleted(
+                                            mSendCecCommandSuccess
+                                                    ? SendMessageResult.SUCCESS
+                                                    : SendMessageResult.NACK);
+                                }
+                                if (mShouldDispatchFeatureAbort) {
+                                    mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
+                                            HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                                                    Constants.ADDR_TV,
+                                                    mHdmiCecLocalDeviceAudioSystem.mAddress,
+                                                    Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
+                                                    Constants.ABORT_UNRECOGNIZED_OPCODE));
+                                }
+                                break;
+                            default:
+                                throw new IllegalArgumentException("Unexpected message");
+                        }
+                    }
+
+                    @Override
+                    boolean isPowerStandby() {
+                        return false;
+                    }
+
+                    @Override
+                    boolean isAddressAllocated() {
+                        return true;
+                    }
+
+                    @Override
+                    Looper getServiceLooper() {
+                        return mTestLooper.getLooper();
+                    }
+                };
+        mHdmiCecLocalDeviceAudioSystem =
+                new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
+                    @Override
+                    HdmiDeviceInfo getDeviceInfo() {
+                        return mDeviceInfoForTests;
+                    }
+                };
+        mHdmiCecLocalDeviceAudioSystem.init();
+        Looper looper = mTestLooper.getLooper();
+        hdmiControlService.setIoLooper(looper);
+
+        mAction =
+                new DetectTvSystemAudioModeSupportAction(
+                        mHdmiCecLocalDeviceAudioSystem,
+                        new TvSystemAudioModeSupportedCallback() {
+                            public void onResult(boolean supported) {
+                                mSupported = Boolean.valueOf(supported);
+                            }
+                        });
+        mSupported = null;
+    }
+
+    @Test
+    public void testSendCecCommandNotSucceed() {
+        mSendCecCommandSuccess = false;
+        mShouldDispatchFeatureAbort = false;
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+        assertEquals(Boolean.FALSE, mSupported);
+    }
+
+    @Test
+    public void testFeatureAbort() {
+        mSendCecCommandSuccess = true;
+        mShouldDispatchFeatureAbort = true;
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+        assertEquals(Boolean.FALSE, mSupported);
+    }
+
+    @Test
+    public void testSupported() {
+        mSendCecCommandSuccess = true;
+        mShouldDispatchFeatureAbort = false;
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+        mTestLooper.moveTimeForward(2000);
+        mTestLooper.dispatchAll();
+        assertEquals(Boolean.TRUE, mSupported);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
new file mode 100644
index 0000000..7484edd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -0,0 +1,130 @@
+/*
+ * 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.hdmi;
+
+import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.MessageQueue;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.HdmiCecController.NativeWrapper;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Fake {@link NativeWrapper} useful for testing. */
+final class FakeNativeWrapper implements NativeWrapper {
+    private final int[] mPollAddressResponse =
+            new int[] {
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+                SendMessageResult.NACK,
+            };
+
+    private final List<HdmiCecMessage> mResultMessages = new ArrayList<>();
+    private HdmiCecMessage mResultMessage;
+    private int mMyPhysicalAddress = 0;
+
+    @Override
+    public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
+        return 1L;
+    }
+
+    @Override
+    public int nativeSendCecCommand(
+            long controllerPtr, int srcAddress, int dstAddress, byte[] body) {
+        if (body.length == 0) {
+            return mPollAddressResponse[dstAddress];
+        } else {
+            mResultMessages.add(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
+        }
+        return SendMessageResult.SUCCESS;
+    }
+
+    @Override
+    public int nativeAddLogicalAddress(long controllerPtr, int logicalAddress) {
+        return 0;
+    }
+
+    @Override
+    public void nativeClearLogicalAddress(long controllerPtr) {}
+
+    @Override
+    public int nativeGetPhysicalAddress(long controllerPtr) {
+        return mMyPhysicalAddress;
+    }
+
+    @Override
+    public int nativeGetVersion(long controllerPtr) {
+        return 0;
+    }
+
+    @Override
+    public int nativeGetVendorId(long controllerPtr) {
+        return 0;
+    }
+
+    @Override
+    public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
+        HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[1];
+        hdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
+        return hdmiPortInfo;
+    }
+
+    @Override
+    public void nativeSetOption(long controllerPtr, int flag, boolean enabled) {}
+
+    @Override
+    public void nativeSetLanguage(long controllerPtr, String language) {}
+
+    @Override
+    public void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag) {}
+
+    @Override
+    public boolean nativeIsConnected(long controllerPtr, int port) {
+        return false;
+    }
+
+    public List<HdmiCecMessage> getResultMessages() {
+        return new ArrayList<>(mResultMessages);
+    }
+
+    public HdmiCecMessage getOnlyResultMessage() throws Exception {
+        if (mResultMessages.size() != 1) {
+            throw new Exception("There is not exactly one message");
+        }
+        return mResultMessages.get(0);
+    }
+
+    public void setPollAddressResponse(int logicalAddress, int response) {
+        mPollAddressResponse[logicalAddress] = response;
+    }
+
+    @VisibleForTesting
+    protected void setPhysicalAddress(int physicalAddress) {
+        mMyPhysicalAddress = physicalAddress;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index 1089f69..6cf5f00 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -15,16 +15,6 @@
  */
 package com.android.server.hdmi;
 
-import android.content.Context;
-import android.hardware.hdmi.HdmiPortInfo;
-import android.os.Looper;
-import android.os.MessageQueue;
-import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
-import android.util.Log;
-import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
-import com.android.server.hdmi.HdmiCecController.NativeWrapper;
-
 import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
 import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
 import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
@@ -36,81 +26,24 @@
 import static com.android.server.hdmi.Constants.ADDR_TV;
 import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
 import static junit.framework.Assert.assertEquals;
+
+import android.content.Context;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-/**
- * Tests for {@link com.android.server.hdmi.HdmiCecController} class.
- */
+/** Tests for {@link com.android.server.hdmi.HdmiCecController} class. */
 @SmallTest
 @RunWith(JUnit4.class)
 public class HdmiCecControllerTest {
 
-    private static final class NativeWrapperImpl implements NativeWrapper {
-
-        @Override
-        public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
-            return 1L;
-        }
-
-        @Override
-        public int nativeSendCecCommand(long controllerPtr, int srcAddress, int dstAddress,
-            byte[] body) {
-            return mOccupied[srcAddress] ? 0 : 1;
-        }
-
-        @Override
-        public int nativeAddLogicalAddress(long controllerPtr, int logicalAddress) {
-            return 0;
-        }
-
-        @Override
-        public void nativeClearLogicalAddress(long controllerPtr) {
-
-        }
-
-        @Override
-        public int nativeGetPhysicalAddress(long controllerPtr) {
-            return 0;
-        }
-
-        @Override
-        public int nativeGetVersion(long controllerPtr) {
-            return 0;
-        }
-
-        @Override
-        public int nativeGetVendorId(long controllerPtr) {
-            return 0;
-        }
-
-        @Override
-        public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
-            return new HdmiPortInfo[0];
-        }
-
-        @Override
-        public void nativeSetOption(long controllerPtr, int flag, boolean enabled) {
-
-        }
-
-        @Override
-        public void nativeSetLanguage(long controllerPtr, String language) {
-
-        }
-
-        @Override
-        public void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag) {
-
-        }
-
-        @Override
-        public boolean nativeIsConnected(long controllerPtr, int port) {
-            return false;
-        }
-    }
+    private FakeNativeWrapper mNativeWrapper;
 
     private class MyHdmiControlService extends HdmiControlService {
 
@@ -129,17 +62,16 @@
         }
     }
 
-    private static final String TAG = HdmiCecControllerTest.class.getSimpleName();
     private HdmiControlService mHdmiControlService;
     private HdmiCecController mHdmiCecController;
-    private static boolean[] mOccupied = new boolean[15];
     private int mLogicalAddress = 16;
-    private AllocateAddressCallback mCallback = new AllocateAddressCallback() {
-        @Override
-        public void onAllocated(int deviceType, int logicalAddress) {
-            mLogicalAddress = logicalAddress;
-        }
-    };
+    private AllocateAddressCallback mCallback =
+            new AllocateAddressCallback() {
+                @Override
+                public void onAllocated(int deviceType, int logicalAddress) {
+                    mLogicalAddress = logicalAddress;
+                }
+            };
     private Looper mMyLooper;
     private TestLooper mTestLooper = new TestLooper();
 
@@ -148,17 +80,14 @@
         mMyLooper = mTestLooper.getLooper();
         mMyLooper = mTestLooper.getLooper();
         mHdmiControlService = new MyHdmiControlService(null);
-        mHdmiCecController = HdmiCecController.createWithNativeWrapper(
-            mHdmiControlService, new NativeWrapperImpl());
+        mNativeWrapper = new FakeNativeWrapper();
+        mHdmiCecController =
+                HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
     }
 
-    /**
-     * Tests for {@link HdmiCecController#allocateLogicalAddress}
-     */
+    /** Tests for {@link HdmiCecController#allocateLogicalAddress} */
     @Test
     public void testAllocatLogicalAddress_TvDevicePreferredNotOcupied() {
-        mOccupied[ADDR_TV] = false;
-        mOccupied[ADDR_SPECIFIC_USE] = false;
         mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_TV, mCallback);
         mTestLooper.dispatchAll();
         assertEquals(ADDR_TV, mLogicalAddress);
@@ -166,8 +95,7 @@
 
     @Test
     public void testAllocatLogicalAddress_TvDeviceNonPreferredNotOcupied() {
-        mOccupied[ADDR_TV] = false;
-        mOccupied[ADDR_SPECIFIC_USE] = false;
+
         mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback);
         mTestLooper.dispatchAll();
         assertEquals(ADDR_TV, mLogicalAddress);
@@ -175,8 +103,7 @@
 
     @Test
     public void testAllocatLogicalAddress_TvDeviceNonPreferredFirstOcupied() {
-        mOccupied[ADDR_TV] = true;
-        mOccupied[ADDR_SPECIFIC_USE] = false;
+        mNativeWrapper.setPollAddressResponse(ADDR_TV, SendMessageResult.SUCCESS);
         mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback);
         mTestLooper.dispatchAll();
         assertEquals(ADDR_SPECIFIC_USE, mLogicalAddress);
@@ -184,8 +111,8 @@
 
     @Test
     public void testAllocatLogicalAddress_TvDeviceNonPreferredAllOcupied() {
-        mOccupied[ADDR_TV] = true;
-        mOccupied[ADDR_SPECIFIC_USE] = true;
+        mNativeWrapper.setPollAddressResponse(ADDR_TV, SendMessageResult.SUCCESS);
+        mNativeWrapper.setPollAddressResponse(ADDR_SPECIFIC_USE, SendMessageResult.SUCCESS);
         mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback);
         mTestLooper.dispatchAll();
         assertEquals(ADDR_UNREGISTERED, mLogicalAddress);
@@ -193,27 +120,23 @@
 
     @Test
     public void testAllocatLogicalAddress_AudioSystemNonPreferredNotOcupied() {
-        mOccupied[ADDR_AUDIO_SYSTEM] = false;
         mHdmiCecController.allocateLogicalAddress(
-            DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
+                DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
         mTestLooper.dispatchAll();
         assertEquals(ADDR_AUDIO_SYSTEM, mLogicalAddress);
     }
 
     @Test
     public void testAllocatLogicalAddress_AudioSystemNonPreferredAllOcupied() {
-        mOccupied[ADDR_AUDIO_SYSTEM] = true;
+        mNativeWrapper.setPollAddressResponse(ADDR_AUDIO_SYSTEM, SendMessageResult.SUCCESS);
         mHdmiCecController.allocateLogicalAddress(
-            DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
+                DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
         mTestLooper.dispatchAll();
         assertEquals(ADDR_UNREGISTERED, mLogicalAddress);
     }
 
     @Test
     public void testAllocatLogicalAddress_PlaybackPreferredNotOccupied() {
-        mOccupied[ADDR_PLAYBACK_1] = false;
-        mOccupied[ADDR_PLAYBACK_2] = false;
-        mOccupied[ADDR_PLAYBACK_3] = false;
         mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
         mTestLooper.dispatchAll();
         assertEquals(ADDR_PLAYBACK_1, mLogicalAddress);
@@ -221,20 +144,14 @@
 
     @Test
     public void testAllocatLogicalAddress_PlaybackPreferredOcuppied() {
-        mOccupied[ADDR_PLAYBACK_1] = true;
-        mOccupied[ADDR_PLAYBACK_2] = false;
-        mOccupied[ADDR_PLAYBACK_3] = false;
-        mHdmiCecController.allocateLogicalAddress(
-            DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
+        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+        mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
         mTestLooper.dispatchAll();
         assertEquals(ADDR_PLAYBACK_2, mLogicalAddress);
     }
 
     @Test
     public void testAllocatLogicalAddress_PlaybackNoPreferredNotOcuppied() {
-        mOccupied[ADDR_PLAYBACK_1] = false;
-        mOccupied[ADDR_PLAYBACK_2] = false;
-        mOccupied[ADDR_PLAYBACK_3] = false;
         mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
         mTestLooper.dispatchAll();
         assertEquals(ADDR_PLAYBACK_1, mLogicalAddress);
@@ -242,9 +159,7 @@
 
     @Test
     public void testAllocatLogicalAddress_PlaybackNoPreferredFirstOcuppied() {
-        mOccupied[ADDR_PLAYBACK_1] = true;
-        mOccupied[ADDR_PLAYBACK_2] = false;
-        mOccupied[ADDR_PLAYBACK_3] = false;
+        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
         mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
         mTestLooper.dispatchAll();
         assertEquals(ADDR_PLAYBACK_2, mLogicalAddress);
@@ -252,9 +167,8 @@
 
     @Test
     public void testAllocatLogicalAddress_PlaybackNonPreferredFirstTwoOcuppied() {
-        mOccupied[ADDR_PLAYBACK_1] = true;
-        mOccupied[ADDR_PLAYBACK_2] = true;
-        mOccupied[ADDR_PLAYBACK_3] = false;
+        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
         mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
         mTestLooper.dispatchAll();
         assertEquals(ADDR_PLAYBACK_3, mLogicalAddress);
@@ -262,9 +176,9 @@
 
     @Test
     public void testAllocatLogicalAddress_PlaybackNonPreferredAllOcupied() {
-        mOccupied[ADDR_PLAYBACK_1] = true;
-        mOccupied[ADDR_PLAYBACK_2] = true;
-        mOccupied[ADDR_PLAYBACK_3] = true;
+        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
+        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS);
         mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
         mTestLooper.dispatchAll();
         assertEquals(ADDR_UNREGISTERED, mLogicalAddress);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
new file mode 100644
index 0000000..0dc5130
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -0,0 +1,442 @@
+/*
+ * 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.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
+import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
+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 android.support.test.filters.SmallTest;
+import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.Ignore;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@RunWith(JUnit4.class)
+/** Tests for {@link HdmiCecLocalDeviceAudioSystem} class. */
+public class HdmiCecLocalDeviceAudioSystemTest {
+
+    private static final String TAG = "HdmiCecLocalDeviceAudioSystemTest";
+    private HdmiControlService mHdmiControlService;
+    private HdmiCecController mHdmiCecController;
+    private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+    private FakeNativeWrapper mNativeWrapper;
+    private Looper mMyLooper;
+    private TestLooper mTestLooper = new TestLooper();
+    private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+    private int mMusicVolume;
+    private int mMusicMaxVolume;
+    private boolean mMusicMute;
+
+    @Before
+    public void SetUp() {
+        mHdmiControlService =
+                new HdmiControlService(null) {
+                    @Override
+                    AudioManager getAudioManager() {
+                        return new AudioManager() {
+                            @Override
+                            public int getStreamVolume(int streamType) {
+                                switch (streamType) {
+                                    case STREAM_MUSIC:
+                                        return mMusicVolume;
+                                    default:
+                                        return 0;
+                                }
+                            }
+
+                            @Override
+                            public boolean isStreamMute(int streamType) {
+                                switch (streamType) {
+                                    case STREAM_MUSIC:
+                                        return mMusicMute;
+                                    default:
+                                        return false;
+                                }
+                            }
+
+                            @Override
+                            public int getStreamMaxVolume(int streamType) {
+                                switch (streamType) {
+                                    case STREAM_MUSIC:
+                                        return mMusicMaxVolume;
+                                    default:
+                                        return 100;
+                                }
+                            }
+
+                            @Override
+                            public void adjustStreamVolume(
+                                    int streamType, int direction, int flags) {
+                                switch (streamType) {
+                                    case STREAM_MUSIC:
+                                        if (direction == AudioManager.ADJUST_UNMUTE) {
+                                            mMusicMute = false;
+                                        } else if (direction == AudioManager.ADJUST_MUTE) {
+                                            mMusicMute = true;
+                                        }
+                                    default:
+                                }
+                            }
+
+                            @Override
+                            public void setWiredDeviceConnectionState(
+                                int type, int state, String address, String name) {
+                                // Do nothing.
+                            }
+                        };
+                    }
+
+                    @Override
+                    void wakeUp() {}
+                };
+
+        mMyLooper = mTestLooper.getLooper();
+        mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService);
+        mHdmiCecLocalDeviceAudioSystem.init();
+        mHdmiControlService.setIoLooper(mMyLooper);
+        mNativeWrapper = new FakeNativeWrapper();
+        mHdmiCecController =
+                HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+        mHdmiControlService.setCecController(mHdmiCecController);
+        mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+        mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
+        mHdmiControlService.initPortInfo();
+        // No TV device interacts with AVR so system audio control won't be turned on here
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mTestLooper.dispatchAll();
+        SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "true");
+    }
+
+    @Test
+    public void handleGiveAudioStatus_volume_10_mute_true() {
+        mMusicVolume = 10;
+        mMusicMute = true;
+        mMusicMaxVolume = 20;
+        int scaledVolume = VolumeControlAction.scaleToCecVolume(10, mMusicMaxVolume);
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildReportAudioStatus(
+                        ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
+        HdmiCecMessage messageGive =
+                HdmiCecMessageBuilder.buildGiveAudioStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive))
+                .isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+    }
+
+    @Test
+    public void handleGiveSystemAudioModeStatus_originalOff() {
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+        HdmiCecMessage messageGive =
+                HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
+                .isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+    }
+
+    @Ignore("b/80297700")
+    @Test
+    public void handleSetSystemAudioMode_setOn_orignalOff() {
+        mMusicMute = true;
+        HdmiCecMessage messageSet =
+                HdmiCecMessageBuilder.buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
+        HdmiCecMessage messageGive =
+                HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+        // Check if originally off
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
+                .isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        // Check if correctly turned on
+        expectedMessage =
+                HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet))
+                .isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
+                .isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        assertThat(mMusicMute).isFalse();
+    }
+
+    @Ignore("b/80297700")
+    @Test
+    public void handleSystemAudioModeRequest_turnOffByTv() {
+        assertThat(mMusicMute).isFalse();
+        // Check if feature correctly turned off
+        HdmiCecMessage messageGive =
+                HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+        HdmiCecMessage messageRequestOff =
+                HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+                        ADDR_TV, ADDR_AUDIO_SYSTEM, 2, false);
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildSetSystemAudioMode(
+                        ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(messageRequestOff))
+                .isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        expectedMessage =
+                HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
+                .isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        assertThat(mMusicMute).isTrue();
+    }
+
+    @Ignore("b/80297700")
+    @Test
+    public void onStandbyAudioSystem_currentSystemAudioControlOn() {
+        // Set system audio control on first
+        mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
+        // Check if standby correctly turns off the feature
+        mHdmiCecLocalDeviceAudioSystem.onStandby(false, STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildSetSystemAudioMode(
+                        ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+        assertThat(mMusicMute).isTrue();
+    }
+
+    @Test
+    public void systemAudioControlOnPowerOn_alwaysOn() {
+        mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
+        mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
+                Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
+        assertThat(
+                        mHdmiCecLocalDeviceAudioSystem.getActions(
+                                SystemAudioInitiationActionFromAvr.class))
+                .isNotEmpty();
+    }
+
+    @Test
+    public void systemAudioControlOnPowerOn_neverOn() {
+        mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
+        mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
+                Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
+        assertThat(
+                        mHdmiCecLocalDeviceAudioSystem.getActions(
+                                SystemAudioInitiationActionFromAvr.class))
+                .isEmpty();
+    }
+
+    @Test
+    public void systemAudioControlOnPowerOn_useLastState_off() {
+        mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
+        mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
+                Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
+        assertThat(
+                        mHdmiCecLocalDeviceAudioSystem.getActions(
+                                SystemAudioInitiationActionFromAvr.class))
+                .isEmpty();
+    }
+
+    @Test
+    public void systemAudioControlOnPowerOn_useLastState_on() {
+        mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
+        mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
+                Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
+        assertThat(
+                        mHdmiCecLocalDeviceAudioSystem.getActions(
+                                SystemAudioInitiationActionFromAvr.class))
+                .isNotEmpty();
+    }
+
+    @Test
+    public void handleActiveSource_updateActiveSource() {
+        HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        ActiveSource expectedActiveSource = new ActiveSource(ADDR_TV, 0x0000);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message))
+                .isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().equals(expectedActiveSource))
+                .isTrue();
+    }
+
+    @Test
+    public void terminateSystemAudioMode_systemAudioModeOff() {
+        mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
+        mMusicMute = false;
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildSetSystemAudioMode(
+                        ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+        mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
+        assertThat(mMusicMute).isFalse();
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(message);
+    }
+
+    @Ignore("b/80297700")
+    @Test
+    public void terminateSystemAudioMode_systemAudioModeOn() {
+        mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue();
+        mMusicMute = false;
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildSetSystemAudioMode(
+                        ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+        mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
+        assertThat(mMusicMute).isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+    }
+
+    @Test
+    public void isPhysicalAddressMeOrBelow_isMe() {
+        int targetPhysicalAddress = 0x1000;
+        mNativeWrapper.setPhysicalAddress(0x1000);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
+            .isTrue();
+    }
+
+    @Test
+    public void isPhysicalAddressMeOrBelow_isBelow() {
+        int targetPhysicalAddress = 0x1100;
+        mNativeWrapper.setPhysicalAddress(0x1000);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
+            .isTrue();
+    }
+
+    @Test
+    public void isPhysicalAddressMeOrBelow_neitherMeNorBelow() {
+        int targetPhysicalAddress = 0x3000;
+        mNativeWrapper.setPhysicalAddress(0x2000);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
+            .isFalse();
+
+        targetPhysicalAddress = 0x2200;
+        mNativeWrapper.setPhysicalAddress(0x3300);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
+            .isFalse();
+
+        targetPhysicalAddress = 0x2213;
+        mNativeWrapper.setPhysicalAddress(0x2212);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
+            .isFalse();
+
+        targetPhysicalAddress = 0x2340;
+        mNativeWrapper.setPhysicalAddress(0x2310);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
+            .isFalse();
+    }
+
+    @Test
+    public void handleRequestArcInitiate_isNotDirectConnectedToTv() {
+        HdmiCecMessage message = HdmiCecMessageBuilder
+            .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+        HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
+            .buildFeatureAbortCommand(
+                ADDR_AUDIO_SYSTEM, ADDR_TV,
+                Constants.MESSAGE_REQUEST_ARC_INITIATION,
+                Constants.ABORT_NOT_IN_CORRECT_MODE);
+        mNativeWrapper.setPhysicalAddress(0x1100);
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
+            .isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+    }
+
+    @Test
+    public void handleRequestArcInitiate_startArcInitiationActionFromAvr() {
+        HdmiCecMessage message = HdmiCecMessageBuilder
+            .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+        mNativeWrapper.setPhysicalAddress(0x1000);
+        mHdmiCecLocalDeviceAudioSystem.removeAction(
+            ArcInitiationActionFromAvr.class);
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
+            .isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mHdmiCecLocalDeviceAudioSystem
+            .getActions(ArcInitiationActionFromAvr.class)).isNotEmpty();
+    }
+
+    @Test
+    public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() {
+        mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
+
+        HdmiCecMessage message = HdmiCecMessageBuilder
+            .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
+        mHdmiCecLocalDeviceAudioSystem.removeAction(
+            ArcTerminationActionFromAvr.class);
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message))
+            .isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mHdmiCecLocalDeviceAudioSystem
+            .getActions(ArcTerminationActionFromAvr.class)).isNotEmpty();
+    }
+
+    @Test
+    public void handleRequestArcTerminate_arcIsNotOn() {
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
+        HdmiCecMessage message = HdmiCecMessageBuilder
+            .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
+        HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
+            .buildFeatureAbortCommand(
+                ADDR_AUDIO_SYSTEM, ADDR_TV,
+                Constants.MESSAGE_REQUEST_ARC_TERMINATION,
+                Constants.ABORT_NOT_IN_CORRECT_MODE);
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message))
+            .isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+    }
+
+    @Test
+    public void handleRequestArcInit_arcIsNotSupported() {
+        HdmiCecMessage message = HdmiCecMessageBuilder
+            .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+        HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
+            .buildFeatureAbortCommand(
+                ADDR_AUDIO_SYSTEM, ADDR_TV,
+                Constants.MESSAGE_REQUEST_ARC_INITIATION,
+                Constants.ABORT_UNRECOGNIZED_OPCODE);
+        SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "false");
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
+            .isTrue();
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 78cb56b..3cd8481 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -16,6 +16,7 @@
 package com.android.server.hdmi;
 
 import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
 import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
 import static com.android.server.hdmi.Constants.ADDR_TV;
 import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
@@ -25,12 +26,9 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
-import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.HdmiControlManager;
 import android.os.test.TestLooper;
 import android.support.test.filters.SmallTest;
-import android.os.MessageQueue;
-import com.android.server.hdmi.HdmiCecController.NativeWrapper;
-import junit.framework.Assert;
 import java.util.Arrays;
 import org.junit.Before;
 import org.junit.Test;
@@ -39,83 +37,19 @@
 
 @SmallTest
 @RunWith(JUnit4.class)
-/**
- * Tests for {@link HdmiCecLocalDevice} class.
- */
+/** Tests for {@link HdmiCecLocalDevice} class. */
 public class HdmiCecLocalDeviceTest {
 
-    private static final class NativeWrapperImpl implements NativeWrapper {
-
-        @Override
-        public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
-            return 1L;
-        }
-
-        @Override
-        public int nativeSendCecCommand(long controllerPtr, int srcAddress, int dstAddress,
-            byte[] body) {
-            return SendCecCommandFactory(srcAddress, dstAddress, body);
-        }
-
-        @Override
-        public int nativeAddLogicalAddress(long controllerPtr, int logicalAddress) {
-            return 0;
-        }
-
-        @Override
-        public void nativeClearLogicalAddress(long controllerPtr) {
-
-        }
-
-        @Override
-        public int nativeGetPhysicalAddress(long controllerPtr) {
-            return mPhysicalAddr;
-        }
-
-        @Override
-        public int nativeGetVersion(long controllerPtr) {
-            return 0;
-        }
-
-        @Override
-        public int nativeGetVendorId(long controllerPtr) {
-            return 0;
-        }
-
-        @Override
-        public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
-            return new HdmiPortInfo[0];
-        }
-
-        @Override
-        public void nativeSetOption(long controllerPtr, int flag, boolean enabled) {
-
-        }
-
-        @Override
-        public void nativeSetLanguage(long controllerPtr, String language) {
-
-        }
-
-        @Override
-        public void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag) {
-
-        }
-
-        @Override
-        public boolean nativeIsConnected(long controllerPtr, int port) {
-            return false;
-        }
-    }
-
     private static int SendCecCommandFactory(int srcAddress, int dstAddress, byte[] body) {
-        switch(body[0] & 0xFF) {
-            /** {@link Constants#MESSAGE_GIVE_PHYSICAL_ADDRESS} */
+        switch (body[0] & 0xFF) {
+                /** {@link Constants#MESSAGE_GIVE_PHYSICAL_ADDRESS} */
             case MESSAGE_REPORT_PHYSICAL_ADDRESS:
             case MESSAGE_DEVICE_VENDOR_ID:
-                return srcAddress == mSrcAddr &&
-                    dstAddress == mDesAddr &&
-                    Arrays.equals(Arrays.copyOfRange(body, 1, body.length), param)? 0 : 1;
+                return srcAddress == mSrcAddr
+                                && dstAddress == mDesAddr
+                                && Arrays.equals(Arrays.copyOfRange(body, 1, body.length), param)
+                        ? 0
+                        : 1;
             default:
                 return 1;
         }
@@ -123,15 +57,12 @@
 
     private class MyHdmiCecLocalDevice extends HdmiCecLocalDevice {
 
-
         protected MyHdmiCecLocalDevice(HdmiControlService service, int deviceType) {
             super(service, deviceType);
         }
 
         @Override
-        protected void onAddressAllocated(int logicalAddress, int reason) {
-
-        }
+        protected void onAddressAllocated(int logicalAddress, int reason) {}
 
         @Override
         protected int getPreferredAddress() {
@@ -139,9 +70,7 @@
         }
 
         @Override
-        protected void setPreferredAddress(int addr) {
-
-        }
+        protected void setPreferredAddress(int addr) {}
     }
 
     private MyHdmiCecLocalDevice mHdmiLocalDevice;
@@ -154,29 +83,54 @@
     private int callbackResult;
     private HdmiCecMessageValidator mMessageValidator;
     private static byte[] param;
+    private boolean mStandbyMessageReceived;
+    private boolean isControlEnabled;
+    private int mPowerStatus;
 
     @Before
     public void SetUp() {
-        mHdmiControlService = new HdmiControlService(null);
+        mHdmiControlService =
+                new HdmiControlService(null) {
+                    @Override
+                    boolean isControlEnabled() {
+                        return isControlEnabled;
+                    }
+
+                    @Override
+                    boolean isPowerOnOrTransient() {
+                        return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
+                                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
+                    }
+
+                    @Override
+                    void standby() {
+                        mStandbyMessageReceived = true;
+                    }
+                };
         mHdmiControlService.setIoLooper(mTestLooper.getLooper());
-        mHdmiCecController = HdmiCecController.createWithNativeWrapper(
-            mHdmiControlService, new NativeWrapperImpl());
+        mHdmiCecController =
+                HdmiCecController.createWithNativeWrapper(
+                        mHdmiControlService, new FakeNativeWrapper());
         mHdmiControlService.setCecController(mHdmiCecController);
-        mHdmiLocalDevice = new MyHdmiCecLocalDevice(
-            mHdmiControlService, DEVICE_TV);
-        mMessageValidator = new HdmiCecMessageValidator(mHdmiControlService){
-            @Override
-            int isValid(HdmiCecMessage message) {
-                return HdmiCecMessageValidator.OK;
-            }
-        };
+        mHdmiLocalDevice = new MyHdmiCecLocalDevice(mHdmiControlService, DEVICE_TV);
+        mMessageValidator =
+                new HdmiCecMessageValidator(mHdmiControlService) {
+                    @Override
+                    int isValid(HdmiCecMessage message) {
+                        return HdmiCecMessageValidator.OK;
+                    }
+                };
         mHdmiControlService.setMessageValidator(mMessageValidator);
     }
 
     @Test
     public void dispatchMessage_desNotValid() {
-        HdmiCecMessage msg = new HdmiCecMessage(
-            ADDR_TV, ADDR_TV, Constants.MESSAGE_CEC_VERSION, HdmiCecMessage.EMPTY_PARAM);
+        HdmiCecMessage msg =
+                new HdmiCecMessage(
+                        ADDR_TV,
+                        ADDR_TV,
+                        Constants.MESSAGE_CEC_VERSION,
+                        HdmiCecMessage.EMPTY_PARAM);
         boolean handleResult = mHdmiLocalDevice.dispatchMessage(msg);
         assertFalse(handleResult);
     }
@@ -185,18 +139,19 @@
     public void handleGivePhysicalAddress_success() {
         mSrcAddr = ADDR_UNREGISTERED;
         mDesAddr = ADDR_BROADCAST;
-        param = new byte[] {
-            (byte) ((mPhysicalAddr >> 8) & 0xFF),
-            (byte) (mPhysicalAddr & 0xFF),
-            (byte) (DEVICE_TV & 0xFF)
-        };
+        param =
+                new byte[] {
+                    (byte) ((mPhysicalAddr >> 8) & 0xFF),
+                    (byte) (mPhysicalAddr & 0xFF),
+                    (byte) (DEVICE_TV & 0xFF)
+                };
         callbackResult = -1;
-        boolean handleResult = mHdmiLocalDevice.handleGivePhysicalAddress(
-            (int finalResult) -> callbackResult = finalResult);
+        boolean handleResult =
+                mHdmiLocalDevice.handleGivePhysicalAddress(
+                        (int finalResult) -> callbackResult = finalResult);
         mTestLooper.dispatchAll();
         /**
-         * Test if CecMessage is sent successfully
-         * SendMessageResult#SUCCESS is defined in HAL as 0
+         * Test if CecMessage is sent successfully SendMessageResult#SUCCESS is defined in HAL as 0
          */
         assertEquals(0, callbackResult);
         assertTrue(handleResult);
@@ -207,15 +162,21 @@
         mSrcAddr = ADDR_UNREGISTERED;
         mDesAddr = ADDR_BROADCAST;
         /** nativeGetVendorId returns 0 */
-        param = new byte[] {
-            (byte) ((0 >> 8) & 0xFF),
-            (byte) (0 & 0xFF),
-            (byte) (0 & 0xFF)
-        };
+        param = new byte[] {(byte) ((0 >> 8) & 0xFF), (byte) (0 & 0xFF), (byte) (0 & 0xFF)};
         callbackResult = -1;
         mHdmiLocalDevice.handleGiveDeviceVendorId(
-            (int finalResult) -> callbackResult = finalResult);
+                (int finalResult) -> callbackResult = finalResult);
         mTestLooper.dispatchAll();
         assertEquals(0, callbackResult);
     }
+
+    @Test
+    public void handleStandby_isPowerOn() {
+        mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
+        isControlEnabled = true;
+        assertFalse(mStandbyMessageReceived);
+        mHdmiLocalDevice.handleStandby(
+                HdmiCecMessageBuilder.buildStandby(ADDR_TV, ADDR_AUDIO_SYSTEM));
+        assertTrue(mStandbyMessageReceived);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
new file mode 100644
index 0000000..1ca48d4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.support.test.filters.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@RunWith(JUnit4.class)
+/** Tests for {@link HdmiCecMessageBuilder}.. */
+public class HdmiCecMessageBuilderTest {
+
+    @Test
+    public void buildReportPhysicalAddressCommand() {
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+                        ADDR_PLAYBACK_1, 0x1234, HdmiDeviceInfo.DEVICE_PLAYBACK);
+        assertThat(message).isEqualTo(buildMessage("4f:84:12:34:04"));
+    }
+
+    @Test
+    public void buildRequestShortAudioDescriptor() {
+        HdmiCecMessage message =
+                HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+                        ADDR_TV,
+                        ADDR_AUDIO_SYSTEM,
+                        new int[] {Constants.AUDIO_CODEC_AAC, Constants.AUDIO_CODEC_LPCM});
+        assertThat(message).isEqualTo(buildMessage("05:A4:06:01"));
+    }
+
+    /**
+     * Build a CEC message from a hex byte string with bytes separated by {@code :}.
+     *
+     * <p>This format is used by both cec-client and www.cec-o-matic.com
+     */
+    private static HdmiCecMessage buildMessage(String message) {
+        String[] parts = message.split(":");
+        int src = Integer.parseInt(parts[0].substring(0, 1), 16);
+        int dest = Integer.parseInt(parts[0].substring(1, 2), 16);
+        int opcode = Integer.parseInt(parts[1], 16);
+        byte[] params = new byte[parts.length - 2];
+        for (int i = 0; i < params.length; i++) {
+            params[i] = Byte.parseByte(parts[i + 2], 16);
+        }
+        return new HdmiCecMessage(src, dest, opcode, params);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
new file mode 100644
index 0000000..7de637b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.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.server.hdmi;
+
+import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
+import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import java.util.ArrayList;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link HdmiControlService} class. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class HdmiControlServiceTest {
+
+    private class HdmiCecLocalDeviceMyDevice extends HdmiCecLocalDevice {
+
+        private boolean mCanGoToStandby;
+        private boolean mIsStandby;
+        private boolean mIsDisabled;
+
+        protected HdmiCecLocalDeviceMyDevice(HdmiControlService service, int deviceType) {
+            super(service, deviceType);
+        }
+
+        @Override
+        protected void onAddressAllocated(int logicalAddress, int reason) {}
+
+        @Override
+        protected int getPreferredAddress() {
+            return 0;
+        }
+
+        @Override
+        protected void setPreferredAddress(int addr) {}
+
+        @Override
+        protected boolean canGoToStandby() {
+            return mCanGoToStandby;
+        }
+
+        @Override
+        protected void disableDevice(
+                boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
+            mIsDisabled = true;
+            originalCallback.onCleared(this);
+        }
+
+        @Override
+        protected void onStandby(boolean initiatedByCec, int standbyAction) {
+            mIsStandby = true;
+        }
+
+        protected boolean isStandby() {
+            return mIsStandby;
+        }
+
+        protected boolean isDisabled() {
+            return mIsDisabled;
+        }
+
+        protected void setCanGoToStandby(boolean canGoToStandby) {
+            mCanGoToStandby = canGoToStandby;
+        }
+    }
+
+    private static final String TAG = "HdmiControlServiceTest";
+    private HdmiControlService mHdmiControlService;
+    private HdmiCecController mHdmiCecController;
+    private HdmiCecLocalDeviceMyDevice mMyAudioSystemDevice;
+    private HdmiCecLocalDeviceMyDevice mMyPlaybackDevice;
+    private FakeNativeWrapper mNativeWrapper;
+    private Looper mMyLooper;
+    private TestLooper mTestLooper = new TestLooper();
+    private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+    private boolean mStandbyMessageReceived;
+
+    @Before
+    public void SetUp() {
+        mHdmiControlService =
+                new HdmiControlService(null) {
+                    @Override
+                    boolean isStandbyMessageReceived() {
+                        return mStandbyMessageReceived;
+                    }
+                };
+        mMyLooper = mTestLooper.getLooper();
+
+        mMyAudioSystemDevice =
+                new HdmiCecLocalDeviceMyDevice(mHdmiControlService, DEVICE_AUDIO_SYSTEM);
+        mMyPlaybackDevice = new HdmiCecLocalDeviceMyDevice(mHdmiControlService, DEVICE_PLAYBACK);
+        mMyAudioSystemDevice.init();
+        mMyPlaybackDevice.init();
+
+        mHdmiControlService.setIoLooper(mMyLooper);
+
+        mNativeWrapper = new FakeNativeWrapper();
+        mHdmiCecController =
+                HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+        mHdmiControlService.setCecController(mHdmiCecController);
+        mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+
+        mLocalDevices.add(mMyAudioSystemDevice);
+        mLocalDevices.add(mMyPlaybackDevice);
+        mHdmiControlService.initPortInfo();
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+
+        mTestLooper.dispatchAll();
+    }
+
+    @Test
+    public void onStandby_notByCec_cannotGoToStandby() {
+        mStandbyMessageReceived = false;
+        mMyPlaybackDevice.setCanGoToStandby(false);
+
+        mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+        assertTrue(mMyPlaybackDevice.isStandby());
+        assertTrue(mMyAudioSystemDevice.isStandby());
+        assertFalse(mMyPlaybackDevice.isDisabled());
+        assertFalse(mMyAudioSystemDevice.isDisabled());
+    }
+
+    @Test
+    public void onStandby_byCec() {
+        mStandbyMessageReceived = true;
+
+        mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+        assertTrue(mMyPlaybackDevice.isStandby());
+        assertTrue(mMyAudioSystemDevice.isStandby());
+        assertTrue(mMyPlaybackDevice.isDisabled());
+        assertTrue(mMyAudioSystemDevice.isDisabled());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
new file mode 100644
index 0000000..e6ff143
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -0,0 +1,255 @@
+/*
+ * 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.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.Nullable;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.media.AudioManager;
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link SystemAudioInitiationActionFromAvr} */
+@SmallTest
+@RunWith(JUnit4.class)
+public class SystemAudioInitiationActionFromAvrTest {
+
+    private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+    private TestLooper mTestLooper = new TestLooper();
+
+    private boolean mShouldDispatchActiveSource;
+    private boolean mTvSystemAudioModeSupport;
+    private int mTryCountBeforeSucceed;
+    private HdmiDeviceInfo mDeviceInfoForTests;
+
+    private int mMsgRequestActiveSourceCount;
+    private int mMsgSetSystemAudioModeCount;
+    private int mQueryTvSystemAudioModeSupportCount;
+
+    @Before
+    public void SetUp() {
+        mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
+        HdmiControlService hdmiControlService =
+                new HdmiControlService(null) {
+
+                    @Override
+                    void sendCecCommand(
+                            HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+                        switch (command.getOpcode()) {
+                            case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
+                                mMsgRequestActiveSourceCount++;
+                                if (mTryCountBeforeSucceed >= mMsgRequestActiveSourceCount
+                                        && callback != null) {
+                                    callback.onSendCompleted(SendMessageResult.NACK);
+                                    break;
+                                }
+                                if (mShouldDispatchActiveSource) {
+                                    mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
+                                            HdmiCecMessageBuilder.buildActiveSource(
+                                                    Constants.ADDR_TV, 1002));
+                                }
+                                break;
+                            case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+                                mMsgSetSystemAudioModeCount++;
+                                if (mTryCountBeforeSucceed >= mMsgSetSystemAudioModeCount
+                                        && callback != null) {
+                                    callback.onSendCompleted(SendMessageResult.NACK);
+                                }
+                                break;
+                            default:
+                                throw new IllegalArgumentException("Unexpected message");
+                        }
+                    }
+
+                    @Override
+                    AudioManager getAudioManager() {
+                        return new AudioManager() {
+
+                            @Override
+                            public int setHdmiSystemAudioSupported(boolean on) {
+                                return 0;
+                            }
+
+                            @Override
+                            public int getStreamVolume(int streamType) {
+                                return 0;
+                            }
+
+                            @Override
+                            public boolean isStreamMute(int streamType) {
+                                return false;
+                            }
+
+                            @Override
+                            public int getStreamMaxVolume(int streamType) {
+                                return 100;
+                            }
+
+                            @Override
+                            public void adjustStreamVolume(
+                                    int streamType, int direction, int flags) {}
+                        };
+                    }
+
+                    @Override
+                    boolean isPowerStandby() {
+                        return false;
+                    }
+
+                    @Override
+                    boolean isAddressAllocated() {
+                        return true;
+                    }
+
+                    @Override
+                    void wakeUp() {}
+
+                    @Override
+                    int getPhysicalAddress() {
+                        return 0;
+                    }
+                };
+        mHdmiCecLocalDeviceAudioSystem =
+                new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
+                    @Override
+                    void queryTvSystemAudioModeSupport(
+                            TvSystemAudioModeSupportedCallback callback) {
+                        mQueryTvSystemAudioModeSupportCount++;
+                        if (callback != null) {
+                            callback.onResult(mTvSystemAudioModeSupport);
+                        }
+                    }
+
+                    @Override
+                    HdmiDeviceInfo getDeviceInfo() {
+                        return mDeviceInfoForTests;
+                    }
+                };
+        mHdmiCecLocalDeviceAudioSystem.init();
+        Looper looper = mTestLooper.getLooper();
+        hdmiControlService.setIoLooper(looper);
+    }
+
+    @Test
+    public void testNoActiveSourceMessageReceived() {
+        resetTestVariables();
+        mShouldDispatchActiveSource = false;
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress)
+                .isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
+
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+                new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
+        mTestLooper.dispatchAll();
+
+        assertThat(mMsgRequestActiveSourceCount).isEqualTo(1);
+        assertThat(mMsgSetSystemAudioModeCount).isEqualTo(0);
+        assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(0);
+        assertFalse(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress)
+                .isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
+    }
+
+    @Test
+    public void testTvNotSupport() {
+        resetTestVariables();
+        mShouldDispatchActiveSource = true;
+        mTvSystemAudioModeSupport = false;
+
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+                new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
+        mTestLooper.dispatchAll();
+
+        assertThat(mMsgRequestActiveSourceCount).isEqualTo(1);
+        assertThat(mMsgSetSystemAudioModeCount).isEqualTo(0);
+        assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
+        assertFalse(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
+    }
+
+    @Test
+    public void testTvSupport() {
+        resetTestVariables();
+        mShouldDispatchActiveSource = true;
+        mTvSystemAudioModeSupport = true;
+
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+                new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
+        mTestLooper.dispatchAll();
+
+        assertThat(mMsgRequestActiveSourceCount).isEqualTo(1);
+        assertThat(mMsgSetSystemAudioModeCount).isEqualTo(1);
+        assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
+        assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
+
+        assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress).isEqualTo(1002);
+    }
+
+    @Test
+    public void testKnownActiveSource() {
+        resetTestVariables();
+        mTvSystemAudioModeSupport = true;
+        mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress = 1001;
+
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+                new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
+        mTestLooper.dispatchAll();
+
+        assertThat(mMsgRequestActiveSourceCount).isEqualTo(0);
+        assertThat(mMsgSetSystemAudioModeCount).isEqualTo(1);
+        assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
+        assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
+    }
+
+    @Test
+    public void testRetry() {
+        resetTestVariables();
+        mTvSystemAudioModeSupport = true;
+        mShouldDispatchActiveSource = true;
+        mTryCountBeforeSucceed = 3;
+        assertThat(mTryCountBeforeSucceed)
+                .isAtMost(SystemAudioInitiationActionFromAvr.MAX_RETRY_COUNT);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress)
+                .isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
+
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+                new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
+        mTestLooper.dispatchAll();
+
+        assertThat(mMsgRequestActiveSourceCount).isEqualTo(4);
+        assertThat(mMsgSetSystemAudioModeCount).isEqualTo(4);
+        assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
+        assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
+    }
+
+    private void resetTestVariables() {
+        mMsgRequestActiveSourceCount = 0;
+        mMsgSetSystemAudioModeCount = 0;
+        mQueryTvSystemAudioModeSupportCount = 0;
+        mTryCountBeforeSucceed = 0;
+        mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress =
+                Constants.INVALID_PHYSICAL_ADDRESS;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index 424c08c..2214d74 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -80,7 +80,7 @@
         MockitoAnnotations.initMocks(this);
         final Context context = InstrumentationRegistry.getTargetContext();
         mUserId = ActivityManager.getCurrentUser();
-        mCommand = new LockSettingsShellCommand(context, mLockPatternUtils);
+        mCommand = new LockSettingsShellCommand(mLockPatternUtils);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 998ffa0..2de5d87 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -92,6 +92,7 @@
 import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
 import com.android.server.pm.ShortcutUser.PackageWithUser;
 
+import com.android.server.wm.ActivityTaskManagerInternal;
 import org.junit.Assert;
 import org.mockito.ArgumentCaptor;
 import org.mockito.invocation.InvocationOnMock;
@@ -613,6 +614,7 @@
     protected UserManagerInternal mMockUserManagerInternal;
     protected UsageStatsManagerInternal mMockUsageStatsManagerInternal;
     protected ActivityManagerInternal mMockActivityManagerInternal;
+    protected ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
 
     protected static final String CALLING_PACKAGE_1 = "com.android.test.1";
     protected static final int CALLING_UID_1 = 10001;
@@ -752,6 +754,7 @@
         mMockUserManagerInternal = mock(UserManagerInternal.class);
         mMockUsageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
         mMockActivityManagerInternal = mock(ActivityManagerInternal.class);
+        mMockActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
 
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
@@ -759,6 +762,7 @@
         LocalServices.addService(UsageStatsManagerInternal.class, mMockUsageStatsManagerInternal);
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
         LocalServices.addService(ActivityManagerInternal.class, mMockActivityManagerInternal);
+        LocalServices.addService(ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
         LocalServices.removeServiceForTest(UserManagerInternal.class);
         LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
 
@@ -1641,11 +1645,11 @@
 
     protected Intent[] launchShortcutAndGetIntentsInner(Runnable shortcutStarter,
             @NonNull String packageName, @NonNull String shortcutId, int userId) {
-        reset(mMockActivityManagerInternal);
+        reset(mMockActivityTaskManagerInternal);
         shortcutStarter.run();
 
         final ArgumentCaptor<Intent[]> intentsCaptor = ArgumentCaptor.forClass(Intent[].class);
-        verify(mMockActivityManagerInternal).startActivitiesAsPackage(
+        verify(mMockActivityTaskManagerInternal).startActivitiesAsPackage(
                 eq(packageName),
                 eq(userId),
                 intentsCaptor.capture(),
@@ -1695,7 +1699,7 @@
 
     protected void assertShortcutNotLaunched(@NonNull String packageName,
             @NonNull String shortcutId, int userId) {
-        reset(mMockActivityManagerInternal);
+        reset(mMockActivityTaskManagerInternal);
         try {
             mLauncherApps.startShortcut(packageName, shortcutId, null, null,
                     UserHandle.of(userId));
@@ -1703,7 +1707,7 @@
         } catch (ActivityNotFoundException expected) {
         }
         // This shouldn't have been called.
-        verify(mMockActivityManagerInternal, times(0)).startActivitiesAsPackage(
+        verify(mMockActivityTaskManagerInternal, times(0)).startActivitiesAsPackage(
                 anyString(),
                 anyInt(),
                 any(Intent[].class),
diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index 33acc44..c4c2ad9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -31,6 +31,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.SparseArray;
 
+import com.android.server.wm.ActivityTaskManagerInternal;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -72,6 +74,8 @@
     private AppOpsManager mAppOpsManager;
     @Mock
     private ActivityManagerInternal mActivityManagerInternal;
+    @Mock
+    private ActivityTaskManagerInternal mActivityTaskManagerInternal;
 
     private TestInjector mTestInjector;
     private ActivityInfo mActivityInfo;
@@ -210,7 +214,7 @@
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PRIMARY_USER)));
 
-        verify(mActivityManagerInternal, never())
+        verify(mActivityTaskManagerInternal, never())
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
@@ -232,7 +236,7 @@
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER)));
 
-        verify(mActivityManagerInternal, never())
+        verify(mActivityTaskManagerInternal, never())
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
@@ -252,7 +256,7 @@
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER)));
 
-        verify(mActivityManagerInternal, never())
+        verify(mActivityTaskManagerInternal, never())
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
@@ -274,7 +278,7 @@
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER)));
 
-        verify(mActivityManagerInternal, never())
+        verify(mActivityTaskManagerInternal, never())
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
@@ -294,7 +298,7 @@
                                 new ComponentName(PACKAGE_TWO, "test"),
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER)));
 
-        verify(mActivityManagerInternal, never())
+        verify(mActivityTaskManagerInternal, never())
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
@@ -314,7 +318,7 @@
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(SECONDARY_USER)));
 
-        verify(mActivityManagerInternal, never())
+        verify(mActivityTaskManagerInternal, never())
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
@@ -333,7 +337,7 @@
                 ACTIVITY_COMPONENT,
                 UserHandle.of(PRIMARY_USER));
 
-        verify(mActivityManagerInternal)
+        verify(mActivityTaskManagerInternal)
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         eq(PACKAGE_ONE),
@@ -432,5 +436,10 @@
         public ActivityManagerInternal getActivityManagerInternal() {
             return mActivityManagerInternal;
         }
+
+        @Override
+        public ActivityTaskManagerInternal getActivityTaskManagerInternal() {
+            return mActivityTaskManagerInternal;
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 7815004..6c6c9932 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -2904,7 +2904,7 @@
         runWithCaller(LAUNCHER_1, USER_0, () -> {
             // Not launchable.
             doReturn(ActivityManager.START_CLASS_NOT_FOUND)
-                    .when(mMockActivityManagerInternal).startActivitiesAsPackage(
+                    .when(mMockActivityTaskManagerInternal).startActivitiesAsPackage(
                     anyStringOrNull(), anyInt(),
                     anyOrNull(Intent[].class), anyOrNull(Bundle.class));
             assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_0,
@@ -2912,7 +2912,7 @@
 
             // Still not launchable.
             doReturn(ActivityManager.START_CLASS_NOT_FOUND)
-                    .when(mMockActivityManagerInternal)
+                    .when(mMockActivityTaskManagerInternal)
                     .startActivitiesAsPackage(
                             anyStringOrNull(), anyInt(),
                             anyOrNull(Intent[].class), anyOrNull(Bundle.class));
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index 7487d44..b238e43 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -25,20 +25,12 @@
 import android.view.IApplicationToken;
 import android.view.WindowManager;
 
+import com.android.server.wm.WindowFrames;
 import com.android.server.wm.utils.WmDisplayCutout;
 
 public class FakeWindowState implements WindowManagerPolicy.WindowState {
 
-    public final Rect parentFrame = new Rect();
-    public final Rect displayFrame = new Rect();
-    public final Rect overscanFrame = new Rect();
-    public final Rect contentFrame = new Rect();
-    public final Rect visibleFrame = new Rect();
-    public final Rect decorFrame = new Rect();
-    public final Rect stableFrame = new Rect();
-    public Rect outsetFrame = new Rect();
-
-    public WmDisplayCutout displayCutout;
+    private WindowFrames windowFrames;
 
     public WindowManager.LayoutParams attrs;
     public int displayId;
@@ -61,44 +53,41 @@
     }
 
     @Override
-    public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overlayFrame,
-            Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
-            @Nullable Rect outsetFrame, WmDisplayCutout displayCutout,
-            boolean parentFrameWasClippedByDisplayCutout) {
-        this.parentFrame.set(parentFrame);
-        this.displayFrame.set(displayFrame);
-        this.overscanFrame.set(overlayFrame);
-        this.contentFrame.set(contentFrame);
-        this.visibleFrame.set(visibleFrame);
-        this.decorFrame.set(decorFrame);
-        this.stableFrame.set(stableFrame);
-        this.outsetFrame = outsetFrame == null ? null : new Rect(outsetFrame);
-        this.displayCutout = displayCutout;
+    public void computeFrameLw(WindowFrames windowFrames) {
+        this.windowFrames = windowFrames;
     }
 
     @Override
     public Rect getFrameLw() {
-        return parentFrame;
+        return windowFrames.mParentFrame;
     }
 
     @Override
     public Rect getDisplayFrameLw() {
-        return displayFrame;
+        return windowFrames.mDisplayFrame;
     }
 
     @Override
     public Rect getOverscanFrameLw() {
-        return overscanFrame;
+        return windowFrames.mOverscanFrame;
     }
 
     @Override
     public Rect getContentFrameLw() {
-        return contentFrame;
+        return windowFrames.mContentFrame;
     }
 
     @Override
     public Rect getVisibleFrameLw() {
-        return visibleFrame;
+        return windowFrames.mVisibleFrame;
+    }
+
+    public Rect getStableFrame() {
+        return windowFrames.mStableFrame;
+    }
+
+    public Rect getDecorFrame() {
+        return windowFrames.mDecorFrame;
     }
 
     @Override
@@ -255,6 +244,9 @@
     }
 
     @Override
+    public boolean canReceiveKeys() { return false; }
+
+    @Override
     public void writeIdentifierToProto(ProtoOutputStream proto, long fieldId){
         throw new UnsupportedOperationException("not implemented");
     }
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
index 97a716f..cb9fab3 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
@@ -22,7 +22,6 @@
 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -76,11 +75,11 @@
         mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
 
-        assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
-        assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
-        assertInsetBy(mAppWindow.displayFrame, 0, 0, 0, 0);
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+        assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
     }
 
     @Test
@@ -91,11 +90,11 @@
         mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
 
-        assertInsetByTopBottom(mAppWindow.parentFrame, 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.decorFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.displayFrame, 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
     }
 
     @Test
@@ -107,11 +106,11 @@
         mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
 
-        assertInsetByTopBottom(mAppWindow.parentFrame, 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.decorFrame, 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.displayFrame, 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
     }
 
     @Test
@@ -132,11 +131,11 @@
         mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
 
-        assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
-        assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
-        assertInsetByTopBottom(mAppWindow.displayFrame, 0, 0);
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
     }
 
     @Test
@@ -149,11 +148,11 @@
         mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
 
-        assertInsetByTopBottom(mAppWindow.parentFrame, STATUS_BAR_HEIGHT, 0);
-        assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
-        assertInsetByTopBottom(mAppWindow.displayFrame, STATUS_BAR_HEIGHT, 0);
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
     }
 
     @Test
@@ -166,11 +165,11 @@
         mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
 
-        assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
-        assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
-        assertInsetBy(mAppWindow.displayFrame, 0, 0, 0, 0);
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+        assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
     }
 
     @Test
@@ -183,11 +182,11 @@
         mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
 
-        assertInsetByTopBottom(mAppWindow.parentFrame, STATUS_BAR_HEIGHT, 0);
-        assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
-        assertInsetByTopBottom(mAppWindow.displayFrame, STATUS_BAR_HEIGHT, 0);
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
     }
 
     @Test
@@ -201,11 +200,11 @@
         mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
 
-        assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
-        assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
-        assertInsetByTopBottom(mAppWindow.displayFrame, 0, 0);
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
     }
 
 
@@ -218,12 +217,12 @@
         mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
 
-        assertInsetBy(mAppWindow.parentFrame, DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-        assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.contentFrame,
+        assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mAppWindow.getContentFrameLw(),
                 DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
-        assertInsetBy(mAppWindow.displayFrame, DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mAppWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
     }
 
     @Test
@@ -235,12 +234,12 @@
         mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
 
-        assertInsetBy(mAppWindow.parentFrame, 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
-        assertInsetBy(mAppWindow.stableFrame, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
-        assertInsetBy(mAppWindow.contentFrame,
+        assertInsetBy(mAppWindow.getFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+        assertInsetBy(mAppWindow.getStableFrame(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
+        assertInsetBy(mAppWindow.getContentFrameLw(),
                 NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
-        assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
-        assertInsetBy(mAppWindow.displayFrame, 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
     }
 
     @Test
@@ -254,11 +253,11 @@
         mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
 
-        assertInsetBy(mAppWindow.parentFrame, DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-        assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.contentFrame,
+        assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mAppWindow.getContentFrameLw(),
                 DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
+        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
     }
 
     @Test
@@ -274,8 +273,8 @@
         mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
 
-        assertInsetByTopBottom(mAppWindow.parentFrame, 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mAppWindow.displayFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
     }
 
     @Test
@@ -290,11 +289,11 @@
         mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
 
-        assertInsetBy(mAppWindow.parentFrame, 0, 0, 0, 0);
-        assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.contentFrame,
+        assertInsetBy(mAppWindow.getFrameLw(), 0, 0, 0, 0);
+        assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mAppWindow.getContentFrameLw(),
                 DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
+        assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
     }
 
     @Test
@@ -309,8 +308,8 @@
         final Rect stable = new Rect();
         final Rect outsets = new Rect();
         final DisplayCutout.ParcelableWrapper cutout = new DisplayCutout.ParcelableWrapper();
-        mPolicy.getLayoutHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames, frame, content,
-                stable, outsets, cutout);
+        mPolicy.getLayoutHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames,
+                false /* floatingStack */, frame, content, stable, outsets, cutout);
 
         assertThat(frame, equalTo(mFrames.mUnrestricted));
         assertThat(content, equalTo(new Rect()));
@@ -331,8 +330,8 @@
         final DisplayCutout.ParcelableWrapper outDisplayCutout =
                 new DisplayCutout.ParcelableWrapper();
 
-        mPolicy.getLayoutHintLw(mAppWindow.attrs, null, mFrames, outFrame, outContentInsets,
-                outStableInsets, outOutsets, outDisplayCutout);
+        mPolicy.getLayoutHintLw(mAppWindow.attrs, null, mFrames, false /* floatingStack */,
+                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
 
         assertThat(outFrame, is(mFrames.mUnrestricted));
         assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
@@ -355,8 +354,35 @@
         final DisplayCutout.ParcelableWrapper outDisplayCutout =
                 new DisplayCutout.ParcelableWrapper();
 
-        mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, outFrame, outContentInsets,
-                outStableInsets, outOutsets, outDisplayCutout);
+        mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, false /* floatingStack */,
+                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
+
+        assertThat(outFrame, is(taskBounds));
+        assertThat(outContentInsets, is(new Rect()));
+        assertThat(outStableInsets, is(new Rect()));
+        assertThat(outOutsets, is(new Rect()));
+        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+    }
+
+    @Test
+    public void layoutHint_appWindowInTask_outsideContentFrame() {
+        // Initialize DisplayFrames
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+        // Task is in the nav bar area (usually does not happen, but this is similar enough to the
+        // possible overlap with the IME)
+        final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
+                200, mFrames.mContent.bottom + 10);
+
+        final Rect outFrame = new Rect();
+        final Rect outContentInsets = new Rect();
+        final Rect outStableInsets = new Rect();
+        final Rect outOutsets = new Rect();
+        final DisplayCutout.ParcelableWrapper outDisplayCutout =
+                new DisplayCutout.ParcelableWrapper();
+
+        mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, true /* floatingStack */,
+                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
 
         assertThat(outFrame, is(taskBounds));
         assertThat(outContentInsets, is(new Rect()));
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
index 2c47a94..ef87f9d 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -37,6 +37,7 @@
 import android.graphics.Path;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.IBinder;
 import android.os.UserHandle;
 import android.support.test.InstrumentationRegistry;
@@ -54,6 +55,7 @@
 
 import com.android.server.policy.keyguard.KeyguardServiceDelegate;
 import com.android.server.wm.DisplayFrames;
+import com.android.server.wm.WindowTestUtils.TestDisplayContent;
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.Before;
@@ -122,7 +124,6 @@
         mNavigationBar.attrs.gravity = Gravity.BOTTOM;
 
         mPolicy.addWindow(mNavigationBar);
-        mPolicy.mHasNavigationBar = true;
         mPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT;
     }
 
@@ -172,15 +173,14 @@
     }
 
     private static DisplayCutout displayCutoutForRotation(int rotation) {
-        Path p = new Path();
-        p.addRect(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT,
-                Path.Direction.CCW);
+        RectF rectF = new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT);
 
         Matrix m = new Matrix();
         transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m);
-        p.transform(m);
+        m.mapRect(rectF);
 
-        return DisplayCutout.fromBounds(p);
+        return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top,
+                (int) rectF.right, (int) rectF.bottom);
     }
 
     static class TestContextWrapper extends ContextWrapper {
@@ -245,12 +245,10 @@
                 policy[0].mAccessibilityManager = new AccessibilityManager(context,
                         mock(IAccessibilityManager.class), UserHandle.USER_CURRENT);
                 policy[0].mSystemGestures = mock(SystemGesturesPointerEventListener.class);
-                policy[0].mNavigationBarCanMove = true;
-                policy[0].mPortraitRotation = ROTATION_0;
-                policy[0].mLandscapeRotation = ROTATION_90;
-                policy[0].mUpsideDownRotation = ROTATION_180;
-                policy[0].mSeascapeRotation = ROTATION_270;
-                policy[0].onConfigurationChanged();
+
+                final TestDisplayContent displayContent = TestDisplayContent.create(context);
+                policy[0].setDefaultDisplay(displayContent);
+                policy[0].onConfigurationChanged(displayContent);
             });
             return policy[0];
         }
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
index 5b24725..0764a56 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
+import android.content.res.Resources;
 import android.provider.Settings.Global;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -48,12 +49,18 @@
     private BatterySaverController mMockBatterySaverController;
     private Device mDevice;
     private TestableBatterySaverStateMachine mTarget;
+    private Resources mMockResources;
 
     private class MyMockContext extends MockContext {
         @Override
         public ContentResolver getContentResolver() {
             return mMockContextResolver;
         }
+
+        @Override
+        public Resources getResources() {
+            return mMockResources;
+        }
     }
 
     private DevicePersistedState mPersistedState;
@@ -157,11 +164,15 @@
         mMockContext = new MyMockContext();
         mMockContextResolver = mock(ContentResolver.class);
         mMockBatterySaverController = mock(BatterySaverController.class);
+        mMockResources = mock(Resources.class);
 
         doAnswer((inv) -> mDevice.batterySaverEnabled = inv.getArgument(0))
                 .when(mMockBatterySaverController).enableBatterySaver(anyBoolean(), anyInt());
         when(mMockBatterySaverController.isEnabled())
                 .thenAnswer((inv) -> mDevice.batterySaverEnabled);
+        when(mMockResources.getBoolean(
+                com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled))
+                .thenReturn(false);
 
         mPersistedState = new DevicePersistedState();
         initDevice();
@@ -173,7 +184,6 @@
         mTarget = new TestableBatterySaverStateMachine();
 
         mDevice.pushBatteryStatus();
-        mDevice.pushGlobalSettings();
         mTarget.onBootCompleted();
     }
 
@@ -498,8 +508,83 @@
     }
 
     @Test
-    public void testNoAutoBatterySaver_fromAdb() {
+    public void testAutoBatterySaver_withStickyDisabled() {
+        when(mMockResources.getBoolean(
+                com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled))
+                .thenReturn(true);
+        initDevice();
+        mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50);
 
+        mTarget.setBatterySaverEnabledManually(true);
+
+        assertEquals(true, mDevice.batterySaverEnabled);
+        assertEquals(100, mPersistedState.batteryLevel);
+        assertEquals(false, mPersistedState.batteryLow);
+
+        mDevice.setBatteryLevel(30);
+
+        assertEquals(true, mDevice.batterySaverEnabled);
+        assertEquals(30, mPersistedState.batteryLevel);
+        assertEquals(true, mPersistedState.batteryLow);
+
+        mDevice.setBatteryLevel(80);
+
+        assertEquals(false, mDevice.batterySaverEnabled); // Not sticky.
+        assertEquals(80, mPersistedState.batteryLevel);
+        assertEquals(false, mPersistedState.batteryLow);
+
+        mDevice.setPowered(true);
+
+        assertEquals(false, mDevice.batterySaverEnabled);
+        assertEquals(80, mPersistedState.batteryLevel);
+        assertEquals(false, mPersistedState.batteryLow);
+
+        mDevice.setBatteryLevel(30);
+
+        assertEquals(false, mDevice.batterySaverEnabled);
+        assertEquals(30, mPersistedState.batteryLevel);
+        assertEquals(true, mPersistedState.batteryLow);
+
+        mDevice.setPowered(false);
+
+        assertEquals(true, mDevice.batterySaverEnabled); // Restores BS.
+        assertEquals(30, mPersistedState.batteryLevel);
+        assertEquals(true, mPersistedState.batteryLow);
+
+        mDevice.setPowered(true);
+        mDevice.setBatteryLevel(90);
+
+        assertEquals(false, mDevice.batterySaverEnabled);
+        assertEquals(90, mPersistedState.batteryLevel);
+        assertEquals(false, mPersistedState.batteryLow);
+
+        initDevice();
+
+        assertEquals(false, mDevice.batterySaverEnabled);
+        assertEquals(90, mPersistedState.batteryLevel);
+        assertEquals(false, mPersistedState.batteryLow);
+
+        mDevice.setPowered(false);
+
+        assertEquals(false, mDevice.batterySaverEnabled);
+        assertEquals(90, mPersistedState.batteryLevel);
+        assertEquals(false, mPersistedState.batteryLow);
+
+        mTarget.setBatterySaverEnabledManually(false);
+
+        assertEquals(false, mDevice.batterySaverEnabled);
+        assertEquals(90, mPersistedState.batteryLevel);
+        assertEquals(false, mPersistedState.batteryLow);
+
+        initDevice();
+
+        assertEquals(false, mDevice.batterySaverEnabled);
+        assertEquals(90, mPersistedState.batteryLevel);
+        assertEquals(false, mPersistedState.batteryLow);
+    }
+
+    @Test
+    public void testNoAutoBatterySaver_fromAdb() {
         assertEquals(0, mDevice.getLowPowerModeTriggerLevel());
 
         assertEquals(false, mDevice.batterySaverEnabled);
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
new file mode 100644
index 0000000..62f1433
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
@@ -0,0 +1,513 @@
+/*
+ * 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.timedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.timedetector.TimeSignal;
+import android.content.Intent;
+import android.icu.util.Calendar;
+import android.icu.util.GregorianCalendar;
+import android.icu.util.TimeZone;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.TimestampedValue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SimpleTimeZoneDetectorStrategyTest {
+
+    private static final Scenario SCENARIO_1 = new Scenario.Builder()
+            .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+            .setInitialDeviceRealtimeMillis(123456789L)
+            .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+            .build();
+
+    private Script mScript;
+
+    @Before
+    public void setUp() {
+        mScript = new Script();
+    }
+
+    @Test
+    public void testSuggestTime_nitz_timeDetectionEnabled() {
+        Scenario scenario = SCENARIO_1;
+        mScript.pokeFakeClocks(scenario)
+                .pokeTimeDetectionEnabled(true);
+
+        TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+        final int clockIncrement = 1000;
+        long expectSystemClockMillis = scenario.getActualTimeMillis() + clockIncrement;
+
+        mScript.simulateTimePassing(clockIncrement)
+                .simulateTimeSignalReceived(timeSignal)
+                .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis);
+    }
+
+    @Test
+    public void testSuggestTime_systemClockThreshold() {
+        Scenario scenario = SCENARIO_1;
+        final int systemClockUpdateThresholdMillis = 1000;
+        mScript.pokeFakeClocks(scenario)
+                .pokeThresholds(systemClockUpdateThresholdMillis)
+                .pokeTimeDetectionEnabled(true);
+
+        TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+        TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+
+        final int clockIncrement = 100;
+        // Increment the the device clocks to simulate the passage of time.
+        mScript.simulateTimePassing(clockIncrement);
+
+        long expectSystemClockMillis1 =
+                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+
+        // Send the first time signal. It should be used.
+        mScript.simulateTimeSignalReceived(timeSignal1)
+                .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis1);
+
+        // Now send another time signal, but one that is too similar to the last one and should be
+        // ignored.
+        int underThresholdMillis = systemClockUpdateThresholdMillis - 1;
+        TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
+                mScript.peekElapsedRealtimeMillis(),
+                mScript.peekSystemClockMillis() + underThresholdMillis);
+        TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
+        mScript.simulateTimePassing(clockIncrement)
+                .simulateTimeSignalReceived(timeSignal2)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Now send another time signal, but one that is on the threshold and so should be used.
+        TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
+                mScript.peekElapsedRealtimeMillis(),
+                mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
+
+        TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3);
+        mScript.simulateTimePassing(clockIncrement);
+
+        long expectSystemClockMillis3 =
+                TimeDetectorStrategy.getTimeAt(utcTime3, mScript.peekElapsedRealtimeMillis());
+
+        mScript.simulateTimeSignalReceived(timeSignal3)
+                .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis3);
+    }
+
+    @Test
+    public void testSuggestTime_nitz_timeDetectionDisabled() {
+        Scenario scenario = SCENARIO_1;
+        mScript.pokeFakeClocks(scenario)
+                .pokeTimeDetectionEnabled(false);
+
+        TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+        mScript.simulateTimeSignalReceived(timeSignal)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+    }
+
+    @Test
+    public void testSuggestTime_nitz_invalidNitzReferenceTimesIgnored() {
+        Scenario scenario = SCENARIO_1;
+        final int systemClockUpdateThreshold = 2000;
+        mScript.pokeFakeClocks(scenario)
+                .pokeThresholds(systemClockUpdateThreshold)
+                .pokeTimeDetectionEnabled(true);
+        TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+        TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+
+        // Initialize the strategy / device with a time set from NITZ.
+        mScript.simulateTimePassing(100);
+        long expectedSystemClockMillis1 =
+                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+        mScript.simulateTimeSignalReceived(timeSignal1)
+                .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1);
+
+        // The UTC time increment should be larger than the system clock update threshold so we
+        // know it shouldn't be ignored for other reasons.
+        long validUtcTimeMillis = utcTime1.getValue() + (2 * systemClockUpdateThreshold);
+
+        // Now supply a new signal that has an obviously bogus reference time : older than the last
+        // one.
+        long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1;
+        TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
+                referenceTimeBeforeLastSignalMillis, validUtcTimeMillis);
+        TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
+        mScript.simulateTimeSignalReceived(timeSignal2)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Now supply a new signal that has an obviously bogus reference time : substantially in the
+        // future.
+        long referenceTimeInFutureMillis =
+                utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1;
+        TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
+                referenceTimeInFutureMillis, validUtcTimeMillis);
+        TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3);
+        mScript.simulateTimeSignalReceived(timeSignal3)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Just to prove validUtcTimeMillis is valid.
+        long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100;
+        TimestampedValue<Long> utcTime4 = new TimestampedValue<>(
+                validReferenceTimeMillis, validUtcTimeMillis);
+        long expectedSystemClockMillis4 =
+                TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
+        TimeSignal timeSignal4 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime4);
+        mScript.simulateTimeSignalReceived(timeSignal4)
+                .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4);
+    }
+
+    @Test
+    public void testSuggestTime_timeDetectionToggled() {
+        Scenario scenario = SCENARIO_1;
+        final int clockIncrementMillis = 100;
+        final int systemClockUpdateThreshold = 2000;
+        mScript.pokeFakeClocks(scenario)
+                .pokeThresholds(systemClockUpdateThreshold)
+                .pokeTimeDetectionEnabled(false);
+
+        TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+        TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+
+        // Simulate time passing.
+        mScript.simulateTimePassing(clockIncrementMillis);
+
+        // Simulate the time signal being received. It should not be used because auto time
+        // detection is off but it should be recorded.
+        mScript.simulateTimeSignalReceived(timeSignal1)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Simulate more time passing.
+        mScript.simulateTimePassing(clockIncrementMillis);
+
+        long expectedSystemClockMillis1 =
+                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+
+        // Turn on auto time detection.
+        mScript.simulateAutoTimeDetectionToggle()
+                .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1);
+
+        // Turn off auto time detection.
+        mScript.simulateAutoTimeDetectionToggle()
+                .verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Receive another valid time signal.
+        // It should be on the threshold and accounting for the clock increments.
+        TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
+                mScript.peekElapsedRealtimeMillis(),
+                mScript.peekSystemClockMillis() + systemClockUpdateThreshold);
+        TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
+
+        // Simulate more time passing.
+        mScript.simulateTimePassing(clockIncrementMillis);
+
+        long expectedSystemClockMillis2 =
+                TimeDetectorStrategy.getTimeAt(utcTime2, mScript.peekElapsedRealtimeMillis());
+
+        // The new time, though valid, should not be set in the system clock because auto time is
+        // disabled.
+        mScript.simulateTimeSignalReceived(timeSignal2)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Turn on auto time detection.
+        mScript.simulateAutoTimeDetectionToggle()
+                .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2);
+    }
+
+    @Test
+    public void testSuggestTime_unknownSource() {
+        Scenario scenario = SCENARIO_1;
+        mScript.pokeFakeClocks(scenario)
+                .pokeTimeDetectionEnabled(true);
+
+        TimeSignal timeSignal = scenario.createTimeSignalForActual("unknown");
+        mScript.simulateTimeSignalReceived(timeSignal)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+    }
+
+    /**
+     * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
+     * like the real thing should, it also asserts preconditions.
+     */
+    private static class FakeCallback implements TimeDetectorStrategy.Callback {
+        private boolean mTimeDetectionEnabled;
+        private boolean mWakeLockAcquired;
+        private long mElapsedRealtimeMillis;
+        private long mSystemClockMillis;
+        private int mSystemClockUpdateThresholdMillis = 2000;
+
+        // Tracking operations.
+        private boolean mSystemClockWasSet;
+        private Intent mBroadcastSent;
+
+        @Override
+        public int systemClockUpdateThresholdMillis() {
+            return mSystemClockUpdateThresholdMillis;
+        }
+
+        @Override
+        public boolean isTimeDetectionEnabled() {
+            return mTimeDetectionEnabled;
+        }
+
+        @Override
+        public void acquireWakeLock() {
+            if (mWakeLockAcquired) {
+                fail("Wake lock already acquired");
+            }
+            mWakeLockAcquired = true;
+        }
+
+        @Override
+        public long elapsedRealtimeMillis() {
+            assertWakeLockAcquired();
+            return mElapsedRealtimeMillis;
+        }
+
+        @Override
+        public long systemClockMillis() {
+            assertWakeLockAcquired();
+            return mSystemClockMillis;
+        }
+
+        @Override
+        public void setSystemClock(long newTimeMillis) {
+            assertWakeLockAcquired();
+            mSystemClockWasSet = true;
+            mSystemClockMillis = newTimeMillis;
+        }
+
+        @Override
+        public void releaseWakeLock() {
+            assertWakeLockAcquired();
+            mWakeLockAcquired = false;
+        }
+
+        @Override
+        public void sendStickyBroadcast(Intent intent) {
+            assertNotNull(intent);
+            mBroadcastSent = intent;
+        }
+
+        // Methods below are for managing the fake's behavior.
+
+        public void pokeSystemClockUpdateThreshold(int thresholdMillis) {
+            mSystemClockUpdateThresholdMillis = thresholdMillis;
+        }
+
+        public void pokeElapsedRealtimeMillis(long elapsedRealtimeMillis) {
+            mElapsedRealtimeMillis = elapsedRealtimeMillis;
+        }
+
+        public void pokeSystemClockMillis(long systemClockMillis) {
+            mSystemClockMillis = systemClockMillis;
+        }
+
+        public void pokeTimeDetectionEnabled(boolean enabled) {
+            mTimeDetectionEnabled = enabled;
+        }
+
+        public long peekElapsedRealtimeMillis() {
+            return mElapsedRealtimeMillis;
+        }
+
+        public long peekSystemClockMillis() {
+            return mSystemClockMillis;
+        }
+
+        public void simulateTimePassing(int incrementMillis) {
+            mElapsedRealtimeMillis += incrementMillis;
+            mSystemClockMillis += incrementMillis;
+        }
+
+        public void verifySystemClockNotSet() {
+            assertFalse(mSystemClockWasSet);
+        }
+
+        public void verifySystemClockWasSet(long expectSystemClockMillis) {
+            assertTrue(mSystemClockWasSet);
+            assertEquals(expectSystemClockMillis, mSystemClockMillis);
+        }
+
+        public void verifyIntentWasBroadcast() {
+            assertTrue(mBroadcastSent != null);
+        }
+
+        public void verifyIntentWasNotBroadcast() {
+            assertNull(mBroadcastSent);
+        }
+
+        public void resetCallTracking() {
+            mSystemClockWasSet = false;
+            mBroadcastSent = null;
+        }
+
+        private void assertWakeLockAcquired() {
+            assertTrue("The operation must be performed only after acquiring the wakelock",
+                    mWakeLockAcquired);
+        }
+    }
+
+    /**
+     * A fluent helper class for tests.
+     */
+    private class Script {
+
+        private final FakeCallback mFakeCallback;
+        private final SimpleTimeDetectorStrategy mSimpleTimeDetectorStrategy;
+
+        public Script() {
+            mFakeCallback = new FakeCallback();
+            mSimpleTimeDetectorStrategy = new SimpleTimeDetectorStrategy();
+            mSimpleTimeDetectorStrategy.initialize(mFakeCallback);
+
+        }
+
+        Script pokeTimeDetectionEnabled(boolean enabled) {
+            mFakeCallback.pokeTimeDetectionEnabled(enabled);
+            return this;
+        }
+
+        Script pokeFakeClocks(Scenario scenario) {
+            mFakeCallback.pokeElapsedRealtimeMillis(scenario.getInitialRealTimeMillis());
+            mFakeCallback.pokeSystemClockMillis(scenario.getInitialSystemClockMillis());
+            return this;
+        }
+
+        Script pokeThresholds(int systemClockUpdateThreshold) {
+            mFakeCallback.pokeSystemClockUpdateThreshold(systemClockUpdateThreshold);
+            return this;
+        }
+
+        long peekElapsedRealtimeMillis() {
+            return mFakeCallback.peekElapsedRealtimeMillis();
+        }
+
+        long peekSystemClockMillis() {
+            return mFakeCallback.peekSystemClockMillis();
+        }
+
+        Script simulateTimeSignalReceived(TimeSignal timeSignal) {
+            mSimpleTimeDetectorStrategy.suggestTime(timeSignal);
+            return this;
+        }
+
+        Script simulateAutoTimeDetectionToggle() {
+            boolean enabled = !mFakeCallback.isTimeDetectionEnabled();
+            mFakeCallback.pokeTimeDetectionEnabled(enabled);
+            mSimpleTimeDetectorStrategy.handleAutoTimeDetectionToggle(enabled);
+            return this;
+        }
+
+        Script simulateTimePassing(int clockIncrement) {
+            mFakeCallback.simulateTimePassing(clockIncrement);
+            return this;
+        }
+
+        Script verifySystemClockWasNotSetAndResetCallTracking() {
+            mFakeCallback.verifySystemClockNotSet();
+            mFakeCallback.verifyIntentWasNotBroadcast();
+            mFakeCallback.resetCallTracking();
+            return this;
+        }
+
+        Script verifySystemClockWasSetAndResetCallTracking(long expectSystemClockMillis) {
+            mFakeCallback.verifySystemClockWasSet(expectSystemClockMillis);
+            mFakeCallback.verifyIntentWasBroadcast();
+            mFakeCallback.resetCallTracking();
+            return this;
+        }
+    }
+
+    /**
+     * A starting scenario used during tests. Describes a fictional "physical" reality.
+     */
+    private static class Scenario {
+
+        private final long mInitialDeviceSystemClockMillis;
+        private final long mInitialDeviceRealtimeMillis;
+        private final long mActualTimeMillis;
+
+        Scenario(long initialDeviceSystemClock, long elapsedRealtime, long timeMillis) {
+            mInitialDeviceSystemClockMillis = initialDeviceSystemClock;
+            mActualTimeMillis = timeMillis;
+            mInitialDeviceRealtimeMillis = elapsedRealtime;
+        }
+
+        long getInitialRealTimeMillis() {
+            return mInitialDeviceRealtimeMillis;
+        }
+
+        long getInitialSystemClockMillis() {
+            return mInitialDeviceSystemClockMillis;
+        }
+
+        long getActualTimeMillis() {
+            return mActualTimeMillis;
+        }
+
+        TimeSignal createTimeSignalForActual(String sourceId) {
+            TimestampedValue<Long> time = new TimestampedValue<>(
+                    mInitialDeviceRealtimeMillis, mActualTimeMillis);
+            return new TimeSignal(sourceId, time);
+        }
+
+        static class Builder {
+
+            private long mInitialDeviceSystemClockMillis;
+            private long mInitialDeviceRealtimeMillis;
+            private long mActualTimeMillis;
+
+            Builder setInitialDeviceSystemClockUtc(int year, int monthInYear, int day,
+                    int hourOfDay, int minute, int second) {
+                mInitialDeviceSystemClockMillis = createUtcTime(year, monthInYear, day, hourOfDay,
+                        minute, second);
+                return this;
+            }
+
+            Builder setInitialDeviceRealtimeMillis(long realtimeMillis) {
+                mInitialDeviceRealtimeMillis = realtimeMillis;
+                return this;
+            }
+
+            Builder setActualTimeUtc(int year, int monthInYear, int day, int hourOfDay,
+                    int minute, int second) {
+                mActualTimeMillis =
+                        createUtcTime(year, monthInYear, day, hourOfDay, minute, second);
+                return this;
+            }
+
+            Scenario build() {
+                return new Scenario(mInitialDeviceSystemClockMillis, mInitialDeviceRealtimeMillis,
+                        mActualTimeMillis);
+            }
+        }
+    }
+
+    private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute,
+            int second) {
+        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
+        cal.clear();
+        cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
+        return cal.getTimeInMillis();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index dc17eeb..ed74cd7 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -16,31 +16,155 @@
 
 package com.android.server.timedetector;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.timedetector.TimeSignal;
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
+import android.content.pm.PackageManager;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.TimestampedValue;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-/**
- * Unit tests for the {@link TimeDetectorService}.
- */
+import com.android.server.timedetector.TimeDetectorStrategy.Callback;
+
+import java.io.PrintWriter;
+
 @RunWith(AndroidJUnit4.class)
 public class TimeDetectorServiceTest {
 
+    private Context mMockContext;
+    private StubbedTimeDetectorStrategy mStubbedTimeDetectorStrategy;
+    private Callback mMockCallback;
+
     private TimeDetectorService mTimeDetectorService;
 
     @Before
     public void setUp() {
-        final Context context = InstrumentationRegistry.getContext();
-        mTimeDetectorService = new TimeDetectorService(context);
+        mMockContext = mock(Context.class);
+        mMockCallback = mock(Callback.class);
+        mStubbedTimeDetectorStrategy = new StubbedTimeDetectorStrategy();
+
+        mTimeDetectorService = new TimeDetectorService(
+                mMockContext, mMockCallback,
+                mStubbedTimeDetectorStrategy);
+    }
+
+    @Test(expected=SecurityException.class)
+    public void testStubbedCall_withoutPermission() {
+        doThrow(new SecurityException("Mock"))
+                .when(mMockContext).enforceCallingPermission(anyString(), any());
+        TimeSignal timeSignal = createNitzTimeSignal();
+
+        try {
+            mTimeDetectorService.suggestTime(timeSignal);
+        } finally {
+            verify(mMockContext).enforceCallingPermission(
+                    eq(android.Manifest.permission.SET_TIME), anyString());
+        }
     }
 
     @Test
-    public void testStubbedCall() {
-        mTimeDetectorService.stubbedCall();
+    public void testSuggestTime() {
+        doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+
+        TimeSignal timeSignal = createNitzTimeSignal();
+        mTimeDetectorService.suggestTime(timeSignal);
+
+        verify(mMockContext)
+                .enforceCallingPermission(eq(android.Manifest.permission.SET_TIME), anyString());
+        mStubbedTimeDetectorStrategy.verifySuggestTimeCalled(timeSignal);
     }
 
+    @Test
+    public void testDump() {
+        when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        mTimeDetectorService.dump(null, null, null);
+
+        verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
+        mStubbedTimeDetectorStrategy.verifyDumpCalled();
+    }
+
+    @Test
+    public void testAutoTimeDetectionToggle() {
+        when(mMockCallback.isTimeDetectionEnabled()).thenReturn(true);
+
+        mTimeDetectorService.handleAutoTimeDetectionToggle();
+
+        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(true);
+
+        when(mMockCallback.isTimeDetectionEnabled()).thenReturn(false);
+
+        mTimeDetectorService.handleAutoTimeDetectionToggle();
+
+        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(false);
+    }
+
+    private static TimeSignal createNitzTimeSignal() {
+        TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
+        return new TimeSignal(TimeSignal.SOURCE_ID_NITZ, timeValue);
+    }
+
+    private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
+
+        // Call tracking.
+        private TimeSignal mLastSuggestedTime;
+        private Boolean mLastAutoTimeDetectionToggle;
+        private boolean mDumpCalled;
+
+        @Override
+        public void initialize(Callback ignored) {
+        }
+
+        @Override
+        public void suggestTime(TimeSignal timeSignal) {
+            resetCallTracking();
+            mLastSuggestedTime = timeSignal;
+        }
+
+        @Override
+        public void handleAutoTimeDetectionToggle(boolean enabled) {
+            resetCallTracking();
+            mLastAutoTimeDetectionToggle = enabled;
+        }
+
+        @Override
+        public void dump(PrintWriter pw, String[] args) {
+            resetCallTracking();
+            mDumpCalled = true;
+        }
+
+        void resetCallTracking() {
+            mLastSuggestedTime = null;
+            mLastAutoTimeDetectionToggle = null;
+            mDumpCalled = false;
+        }
+
+        void verifySuggestTimeCalled(TimeSignal expectedSignal) {
+            assertEquals(expectedSignal, mLastSuggestedTime);
+        }
+
+        void verifyHandleAutoTimeDetectionToggleCalled(boolean expectedEnable) {
+            assertNotNull(mLastAutoTimeDetectionToggle);
+            assertEquals(expectedEnable, mLastAutoTimeDetectionToggle);
+        }
+
+        void verifyDumpCalled() {
+            assertTrue(mDumpCalled);
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.java
new file mode 100644
index 0000000..301ded4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyTest.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 com.android.server.timedetector;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.util.TimestampedValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class TimeDetectorStrategyTest {
+
+    @Test
+    public void testGetTimeAt() {
+        long timeMillis = 1000L;
+        int referenceTimeMillis = 100;
+        TimestampedValue<Long> timestampedValue =
+                new TimestampedValue<>(referenceTimeMillis, timeMillis);
+        // Reference time is after the timestamp.
+        assertEquals(
+                timeMillis + (125 - referenceTimeMillis),
+                TimeDetectorStrategy.getTimeAt(timestampedValue, 125));
+
+        // Reference time is before the timestamp.
+        assertEquals(
+                timeMillis + (75 - referenceTimeMillis),
+                TimeDetectorStrategy.getTimeAt(timestampedValue, 75));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
new file mode 100644
index 0000000..19d31cf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.timezonedetector;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for the {@link TimeZoneDetectorService}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class TimeZoneDetectorServiceTest {
+
+    private TimeZoneDetectorService mTimeZoneDetectorService;
+
+    @Before
+    public void setUp() {
+        final Context context = InstrumentationRegistry.getContext();
+        mTimeZoneDetectorService = new TimeZoneDetectorService(context);
+    }
+
+    @Test
+    public void testStubbedCall() {
+        mTimeZoneDetectorService.stubbedCall();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
new file mode 100644
index 0000000..c3907ff
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.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.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.SurfaceControl.Transaction;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.SurfaceControl;
+
+import com.android.server.wm.WindowTestUtils.TestAppWindowToken;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Animation related tests for the {@link AppWindowToken} class.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:com.android.server.wm.AppWindowTokenAnimationTests
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppWindowTokenAnimationTests extends WindowTestsBase {
+
+    private TestAppWindowToken mToken;
+
+    @Mock
+    private Transaction mTransaction;
+    @Mock
+    private AnimationAdapter mSpec;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+
+        mToken = createTestAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD);
+        mToken.setPendingTransaction(mTransaction);
+    }
+
+    @Test
+    public void clipAfterAnim_boundsLayerIsCreated() throws Exception {
+        mToken.mNeedsAnimationBoundsLayer = true;
+
+        mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
+                eq(mToken.mSurfaceAnimator.mLeash.getHandle()));
+        verify(mTransaction).reparent(eq(mToken.mSurfaceAnimator.mLeash),
+                eq(mToken.mAnimationBoundsLayer.getHandle()));
+    }
+
+    @Test
+    public void clipAfterAnim_boundsLayerIsDestroyed() throws Exception {
+        mToken.mNeedsAnimationBoundsLayer = true;
+        mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash;
+        final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer;
+        final ArgumentCaptor<SurfaceAnimator.OnAnimationFinishedCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        SurfaceAnimator.OnAnimationFinishedCallback.class);
+        verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+
+        callbackCaptor.getValue().onAnimationFinished(mSpec);
+        verify(mTransaction).destroy(eq(leash));
+        verify(mTransaction).destroy(eq(animationBoundsLayer));
+        assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
+    }
+
+    @Test
+    public void clipAfterAnimCancelled_boundsLayerIsDestroyed() throws Exception {
+        mToken.mNeedsAnimationBoundsLayer = true;
+        mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash;
+        final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer;
+
+        mToken.mSurfaceAnimator.cancelAnimation();
+        verify(mTransaction).destroy(eq(leash));
+        verify(mTransaction).destroy(eq(animationBoundsLayer));
+        assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
+    }
+
+    @Test
+    public void clipNoneAnim_boundsLayerIsNotCreated() throws Exception {
+        mToken.mNeedsAnimationBoundsLayer = false;
+
+        mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
+                eq(mToken.mSurfaceAnimator.mLeash.getHandle()));
+        assertThat(mToken.mAnimationBoundsLayer).isNull();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index f6599dc..e8d8022 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -42,6 +42,10 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
 /**
  * Tests for the {@link AppWindowToken} class.
  *
@@ -162,6 +166,9 @@
         sWm.mDisplayReady = true;
         sWm.mDisplayEnabled = true;
 
+        final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation());
+        mDisplayContent.setDisplayRotation(spiedRotation);
+
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("AppWindow");
@@ -169,20 +176,21 @@
         mToken.addWindow(appWindow);
 
         // Set initial orientation and update.
-        performRotation(Surface.ROTATION_90);
+        performRotation(spiedRotation, Surface.ROTATION_90);
         appWindow.resizeReported = false;
 
         // Update the rotation to perform 180 degree rotation and check that resize was reported.
-        performRotation(Surface.ROTATION_270);
+        performRotation(spiedRotation, Surface.ROTATION_270);
         assertTrue(appWindow.resizeReported);
+
         appWindow.removeImmediately();
     }
 
-    private void performRotation(int rotationToReport) {
-        ((TestWindowManagerPolicy) sWm.mPolicy).rotationToReport = rotationToReport;
+    private void performRotation(DisplayRotation spiedRotation, int rotationToReport) {
+        doReturn(rotationToReport).when(spiedRotation).rotationForOrientation(anyInt(), anyInt());
         sWm.updateRotation(false, false);
-        // Simulate animator finishing orientation change
-        sWm.mRoot.mOrientationChangeComplete = true;
+        // Prevent the next rotation from being deferred by animation.
+        sWm.mAnimator.setScreenRotationAnimationLocked(mDisplayContent.getDisplayId(), null);
         sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index ff631e7..08b522c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -151,11 +151,13 @@
         }
 
         @Override
-        public void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
+        public boolean onAnimationStart(boolean schedulePipModeChangedCallback,
+                boolean forceUpdate) {
             mAwaitingAnimationStart = false;
             mAnimationStarted = true;
             mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback;
             mForcePipModeChangedCallback = forceUpdate;
+            return true;
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index ac196f9..ef0e27b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -17,6 +17,8 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
@@ -35,6 +37,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -349,52 +353,94 @@
      */
     @Test
     public void testMaxUiWidth() throws Exception {
+        // Prevent base display metrics for test from being updated to the value of real display.
+        final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
         final int baseWidth = 1440;
         final int baseHeight = 2560;
         final int baseDensity = 300;
 
-        mDisplayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
+        displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
 
         final int maxWidth = 300;
         final int resultingHeight = (maxWidth * baseHeight) / baseWidth;
         final int resultingDensity = (maxWidth * baseDensity) / baseWidth;
 
-        mDisplayContent.setMaxUiWidth(maxWidth);
-        verifySizes(mDisplayContent, maxWidth, resultingHeight, resultingDensity);
+        displayContent.setMaxUiWidth(maxWidth);
+        verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity);
 
         // Assert setting values again does not change;
-        mDisplayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
-        verifySizes(mDisplayContent, maxWidth, resultingHeight, resultingDensity);
+        displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
+        verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity);
 
         final int smallerWidth = 200;
         final int smallerHeight = 400;
         final int smallerDensity = 100;
 
         // Specify smaller dimension, verify that it is honored
-        mDisplayContent.updateBaseDisplayMetrics(smallerWidth, smallerHeight, smallerDensity);
-        verifySizes(mDisplayContent, smallerWidth, smallerHeight, smallerDensity);
+        displayContent.updateBaseDisplayMetrics(smallerWidth, smallerHeight, smallerDensity);
+        verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity);
 
         // Verify that setting the max width to a greater value than the base width has no effect
-        mDisplayContent.setMaxUiWidth(maxWidth);
-        verifySizes(mDisplayContent, smallerWidth, smallerHeight, smallerDensity);
+        displayContent.setMaxUiWidth(maxWidth);
+        verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity);
     }
 
     /**
-     * This test enforces that the pinned stack is always kept as the top stack.
+     * This test enforces that alwaysOnTop stack is placed at proper position.
      */
     @Test
-    public void testPinnedStackLocation() {
+    public void testAlwaysOnTopStackLocation() {
+        final TaskStack alwaysOnTopStack = createStackControllerOnStackOnDisplay(
+                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
+        final Task task = createTaskInStack(alwaysOnTopStack, 0 /* userId */);
+        alwaysOnTopStack.setAlwaysOnTop(true);
+        mDisplayContent.positionStackAt(POSITION_TOP, alwaysOnTopStack,
+                false /* includingParents */);
+        assertTrue(alwaysOnTopStack.isAlwaysOnTop());
+        // Ensure always on top state is synced to the children of the stack.
+        assertTrue(alwaysOnTopStack.getTopChild().isAlwaysOnTop());
+        assertEquals(alwaysOnTopStack, mDisplayContent.getTopStack());
+
         final TaskStack pinnedStack = createStackControllerOnStackOnDisplay(
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
-        // Ensure that the pinned stack is the top stack
         assertEquals(pinnedStack, mDisplayContent.getPinnedStack());
         assertEquals(pinnedStack, mDisplayContent.getTopStack());
-        // By default, this should try to create a new stack on top
-        final TaskStack otherStack = createTaskStackOnDisplay(mDisplayContent);
-        // Ensure that the other stack is on the display.
-        assertEquals(mDisplayContent, otherStack.getDisplayContent());
-        // Ensure that the pinned stack is still on top
-        assertEquals(pinnedStack, mDisplayContent.getTopStack());
+
+        final TaskStack anotherAlwaysOnTopStack = createStackControllerOnStackOnDisplay(
+                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
+        anotherAlwaysOnTopStack.setAlwaysOnTop(true);
+        mDisplayContent.positionStackAt(POSITION_TOP, anotherAlwaysOnTopStack,
+                false /* includingParents */);
+        assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
+        int topPosition = mDisplayContent.getStacks().size() - 1;
+        // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
+        // existing alwaysOnTop stack.
+        assertEquals(anotherAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 1));
+
+        final TaskStack nonAlwaysOnTopStack = createStackControllerOnStackOnDisplay(
+                WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
+        assertEquals(mDisplayContent, nonAlwaysOnTopStack.getDisplayContent());
+        topPosition = mDisplayContent.getStacks().size() - 1;
+        // Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the
+        // existing other non-alwaysOnTop stacks.
+        assertEquals(nonAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 3));
+
+        anotherAlwaysOnTopStack.setAlwaysOnTop(false);
+        mDisplayContent.positionStackAt(POSITION_TOP, anotherAlwaysOnTopStack,
+                false /* includingParents */);
+        assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
+        // Ensure, when always on top is turned off for a stack, the stack is put just below all
+        // other always on top stacks.
+        assertEquals(anotherAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 2));
+        anotherAlwaysOnTopStack.setAlwaysOnTop(true);
+
+        // Ensure always on top state changes properly when windowing mode changes.
+        anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
+        assertEquals(anotherAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 2));
+        anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
+        assertEquals(anotherAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 1));
     }
 
     /**
@@ -437,22 +483,42 @@
     @Test
     public void testDisplayCutout_rot90() throws Exception {
         synchronized (sWm.getWindowManagerLock()) {
-            final DisplayContent dc = createNewDisplay();
-            dc.mInitialDisplayWidth = 200;
-            dc.mInitialDisplayHeight = 400;
-            Rect r1 = new Rect(80, 0, 120, 10);
+            // Prevent mInitialDisplayCutout from being updated from real display (e.g. null
+            // if the device has no cutout).
+            final DisplayContent dc = createDisplayNoUpdateDisplayInfo();
+            // Rotation may use real display info to compute bound, so here also uses the
+            // same width and height.
+            final int displayWidth = dc.mInitialDisplayWidth;
+            final int displayHeight = dc.mInitialDisplayHeight;
+            final int cutoutWidth = 40;
+            final int cutoutHeight = 10;
+            final int left = (displayWidth - cutoutWidth) / 2;
+            final int top = 0;
+            final int right = (displayWidth + cutoutWidth) / 2;
+            final int bottom = cutoutHeight;
+
+            final Rect r1 = new Rect(left, top, right, bottom);
             final DisplayCutout cutout = new WmDisplayCutout(
                     fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom), null)
-                    .computeSafeInsets(200, 400).getDisplayCutout();
+                    .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
 
             dc.mInitialDisplayCutout = cutout;
             dc.setRotation(Surface.ROTATION_90);
             dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo.
 
-            final Rect r = new Rect(0, 80, 10, 120);
+            // ----o----------      -------------
+            // |   |     |   |      |
+            // |   ------o   |      o---
+            // |             |      |  |
+            // |             |  ->  |  |
+            // |             |      ---o
+            // |             |      |
+            // |             |      -------------
+            final Rect r = new Rect(top, left, bottom, right);
             assertEquals(new WmDisplayCutout(
                     fromBoundingRect(r.left, r.top, r.right, r.bottom), null)
-                    .computeSafeInsets(400, 200).getDisplayCutout(), dc.getDisplayInfo().displayCutout);
+                    .computeSafeInsets(displayHeight, displayWidth)
+                    .getDisplayCutout(), dc.getDisplayInfo().displayCutout);
         }
     }
 
@@ -514,6 +580,16 @@
         assertEquals(displayContent.mBaseDisplayDensity, expectedBaseDensity);
     }
 
+    /**
+     * Create DisplayContent that does not update display base/initial values from device to keep
+     * the values set by test.
+     */
+    private DisplayContent createDisplayNoUpdateDisplayInfo() {
+        final DisplayContent displayContent = spy(createNewDisplay());
+        doNothing().when(displayContent).updateDisplayInfo();
+        return displayContent;
+    }
+
     private void assertForAllWindowsOrder(List<WindowState> expectedWindowsBottomToTop) {
         final LinkedList<WindowState> actualWindows = new LinkedList<>();
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
new file mode 100644
index 0000000..0ea56ed
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
@@ -0,0 +1,180 @@
+/*
+ * 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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.app.WindowConfiguration;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class DisplaySettingsTests extends WindowTestsBase {
+
+    private File mTestFolder;
+    private DisplaySettings mTarget;
+
+    private DisplayContent mPrimaryDisplay;
+    private DisplayContent mSecondaryDisplay;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mTestFolder = InstrumentationRegistry.getContext().getCacheDir();
+        deleteRecursively(mTestFolder);
+
+        sWm.setSupportsFreeformWindowManagement(false);
+        sWm.setIsPc(false);
+
+        mTarget = new DisplaySettings(sWm, mTestFolder);
+        mTarget.readSettingsLocked();
+
+        mPrimaryDisplay = sWm.getDefaultDisplayContentLocked();
+        mSecondaryDisplay = createNewDisplay();
+        assertNotEquals(Display.DEFAULT_DISPLAY, mSecondaryDisplay.getDisplayId());
+    }
+
+    @Test
+    public void testPrimaryDisplayDefaultToFullscreenWithoutFreeformSupport() {
+        mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+        assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
+                mPrimaryDisplay.getWindowingMode());
+    }
+
+    @Test
+    public void testPrimaryDisplayDefaultToFullscreenWithFreeformSupportNonPc() {
+        sWm.setSupportsFreeformWindowManagement(true);
+
+        mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+        assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
+                mPrimaryDisplay.getWindowingMode());
+    }
+
+    @Test
+    public void testPrimaryDisplayDefaultToFreeformWithFreeformIsPc() {
+        sWm.setSupportsFreeformWindowManagement(true);
+        sWm.setIsPc(true);
+
+        mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+        assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM,
+                mPrimaryDisplay.getWindowingMode());
+    }
+
+    @Test
+    public void testSecondaryDisplayDefaultToFullscreenWithoutFreeformSupport() {
+        mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+        assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
+                mSecondaryDisplay.getWindowingMode());
+    }
+
+    @Test
+    public void testSecondaryDisplayDefaultToFreeformWithFreeformSupportNonPc() {
+        sWm.setSupportsFreeformWindowManagement(true);
+
+        mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+        assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM,
+                mSecondaryDisplay.getWindowingMode());
+    }
+
+    @Test
+    public void testSecondaryDisplayDefaultToFreeformWithFreeformSupportIsPc() {
+        sWm.setSupportsFreeformWindowManagement(true);
+        sWm.setIsPc(true);
+
+        mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+        assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM,
+                mSecondaryDisplay.getWindowingMode());
+    }
+
+    @Test
+    public void testDefaultToZeroOverscan() {
+        mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+        assertOverscan(mPrimaryDisplay, 0 /* left */, 0 /* top */, 0 /* right */, 0 /* bottom */);
+    }
+
+    @Test
+    public void testPersistOverscanInSameInstance() {
+        final DisplayInfo info = mPrimaryDisplay.getDisplayInfo();
+        mTarget.setOverscanLocked(info.uniqueId, info.name, 1 /* left */, 2 /* top */,
+                3 /* right */, 4 /* bottom */);
+
+        mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+        assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */);
+    }
+
+    @Test
+    public void testPersistOverscanAcrossInstances() {
+        final DisplayInfo info = mPrimaryDisplay.getDisplayInfo();
+        mTarget.setOverscanLocked(info.uniqueId, info.name, 1 /* left */, 2 /* top */,
+                3 /* right */, 4 /* bottom */);
+        mTarget.writeSettingsLocked();
+
+        DisplaySettings target = new DisplaySettings(sWm, mTestFolder);
+        target.readSettingsLocked();
+
+        target.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+        assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */);
+    }
+
+    private static void assertOverscan(DisplayContent display, int left, int top, int right,
+            int bottom) {
+        final DisplayInfo info = display.getDisplayInfo();
+
+        assertEquals(left, info.overscanLeft);
+        assertEquals(top, info.overscanTop);
+        assertEquals(right, info.overscanRight);
+        assertEquals(bottom, info.overscanBottom);
+    }
+
+    private static boolean deleteRecursively(File file) {
+        if (file.isFile()) {
+            return file.delete();
+        }
+
+        boolean fullyDeleted = true;
+        final File[] files = file.listFiles();
+        for (File child : files) {
+            fullyDeleted &= deleteRecursively(child);
+        }
+        fullyDeleted &= file.delete();
+        return fullyDeleted;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
index a2ccee4..bfc99c8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -255,16 +255,32 @@
      * Asserts the top inset of {@param activity} is equal to {@param expected} waiting as needed.
      */
     private void assertTopInsetEquals(Activity activity, int expected) throws Exception {
-        waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected);
+        waitForTopInsetEqual(activity, expected);
         assertEquals(expected, getInsets(activity).getSystemWindowInsetTop());
     }
 
+    private void waitForTopInsetEqual(Activity activity, int expected) {
+        waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected);
+    }
+
     /**
      * Asserts the inset at {@param side} of {@param activity} is equal to {@param expected}
      * waiting as needed.
      */
     private void assertInsetGreaterOrEqual(Activity activity, int side, int expected)
             throws Exception {
+        waitForInsetGreaterOrEqual(activity, side, expected);
+
+        final WindowInsets insets = getInsets(activity);
+        switch (side) {
+            case TOP: assertGreaterOrEqual(insets.getSystemWindowInsetTop(), expected); break;
+            case BOTTOM: assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), expected); break;
+            case LEFT: assertGreaterOrEqual(insets.getSystemWindowInsetLeft(), expected); break;
+            case RIGHT: assertGreaterOrEqual(insets.getSystemWindowInsetRight(), expected); break;
+        }
+    }
+
+    private void waitForInsetGreaterOrEqual(Activity activity, int side, int expected) {
         waitFor(() -> {
             final WindowInsets insets = getInsets(activity);
             switch (side) {
@@ -275,14 +291,6 @@
                 default: return true;
             }
         });
-
-        final WindowInsets insets = getInsets(activity);
-        switch (side) {
-            case TOP: assertGreaterOrEqual(insets.getSystemWindowInsetTop(), expected); break;
-            case BOTTOM: assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), expected); break;
-            case LEFT: assertGreaterOrEqual(insets.getSystemWindowInsetLeft(), expected); break;
-            case RIGHT: assertGreaterOrEqual(insets.getSystemWindowInsetRight(), expected); break;
-        }
     }
 
     /** Asserts that the first entry is greater than or equal to the second entry. */
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index edac8a5..79e9bb4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -20,24 +20,23 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.animation.AnimationHandler;
 import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
 import android.animation.ValueAnimator;
 import android.graphics.Matrix;
 import android.graphics.Point;
+import android.os.PowerManagerInternal;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
 import android.view.Choreographer;
 import android.view.Choreographer.FrameCallback;
 import android.view.SurfaceControl;
@@ -46,7 +45,6 @@
 import android.view.animation.TranslateAnimation;
 
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
-import com.android.server.wm.SurfaceAnimationRunner.AnimatorFactory;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -71,6 +69,7 @@
     @Mock SurfaceControl mMockSurface;
     @Mock Transaction mMockTransaction;
     @Mock AnimationSpec mMockAnimationSpec;
+    @Mock PowerManagerInternal mMockPowerManager;
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     private SurfaceAnimationRunner mSurfaceAnimationRunner;
@@ -81,7 +80,7 @@
         super.setUp();
         mFinishCallbackLatch = new CountDownLatch(1);
         mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */, null,
-                mMockTransaction);
+                mMockTransaction, mMockPowerManager);
     }
 
     private void finishedCallback() {
@@ -113,7 +112,7 @@
     @Test
     public void testCancel_notStarted() throws Exception {
         mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
-                mMockTransaction);
+                mMockTransaction, mMockPowerManager);
         mSurfaceAnimationRunner
                 .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
                 this::finishedCallback);
@@ -126,7 +125,7 @@
     @Test
     public void testCancel_running() throws Exception {
         mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
-                mMockTransaction);
+                mMockTransaction, mMockPowerManager);
         mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface,
                 mMockTransaction, this::finishedCallback);
         waitUntilNextFrame();
@@ -156,7 +155,7 @@
                     listener.onAnimationUpdate(animation);
                 });
             }
-        }, mMockTransaction);
+        }, mMockTransaction, mMockPowerManager);
         when(mMockAnimationSpec.getDuration()).thenReturn(200L);
         mSurfaceAnimationRunner.startAnimation(mMockAnimationSpec, mMockSurface, mMockTransaction,
                 this::finishedCallback);
@@ -184,6 +183,19 @@
         assertFinishCallbackCalled();
     }
 
+    @Test
+    public void testPowerHint() throws Exception {
+        mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
+                mMockTransaction, mMockPowerManager);
+        mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface,
+                mMockTransaction, this::finishedCallback);
+        waitUntilNextFrame();
+
+        // TODO: For some reason we don't have access to PowerHint definition from the tests. For
+        // now let's just verify that we got some kind of hint.
+        verify(mMockPowerManager).powerHint(anyInt(), anyInt());
+    }
+
     private void waitUntilNextFrame() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         mSurfaceAnimationRunner.mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT,
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
index 4447b26..dc6bbbf 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
@@ -22,6 +22,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import android.app.IActivityTaskManager;
 import android.graphics.Rect;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -75,7 +76,7 @@
         mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, dm);
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
 
-        mPositioner = new TaskPositioner(sWm, Mockito.mock(IActivityManager.class));
+        mPositioner = new TaskPositioner(sWm, Mockito.mock(IActivityTaskManager.class));
         mPositioner.register(mDisplayContent);
 
         mWindow = Mockito.spy(createWindow(null, TYPE_BASE_APPLICATION, "window"));
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index b19373e..21402ce 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -59,7 +59,7 @@
     private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis,
             int windowFlags, Rect taskBounds) {
         final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888,
-                GraphicBuffer.USAGE_SW_READ_NEVER | GraphicBuffer.USAGE_SW_WRITE_NEVER);
+                GraphicBuffer.USAGE_SW_READ_RARELY | GraphicBuffer.USAGE_SW_WRITE_NEVER);
         final TaskSnapshot snapshot = new TaskSnapshot(buffer,
                 ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */,
                 WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index ee028ba..5fb8997 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -19,6 +19,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
 import android.annotation.Nullable;
@@ -30,7 +31,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.proto.ProtoOutputStream;
-import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.IWindow;
 import android.view.IWindowManager;
@@ -72,14 +72,7 @@
 
     }
 
-    @Override
-    public boolean isDefaultOrientationForced() {
-        return false;
-    }
-
-    @Override
-    public void setInitialDisplaySize(Display display, int width, int height, int density) {
-
+    public void setDefaultDisplay(DisplayContentInfo displayContentInfo) {
     }
 
     @Override
@@ -159,9 +152,11 @@
         final WindowManagerService wm = mWmSupplier.get();
         synchronized (wm.mWindowMap) {
             atoken = wm.mRoot.getAppWindowToken(appToken);
+            IWindow iWindow = mock(IWindow.class);
+            doReturn(mock(IBinder.class)).when(iWindow).asBinder();
             window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, atoken,
                     "Starting window", 0 /* ownerId */, false /* internalWindows */, wm,
-                    mock(Session.class), mock(IWindow.class));
+                    mock(Session.class), iWindow);
             atoken.startingWindow = window;
         }
         if (mRunnableWhenAddingSplashScreen != null) {
@@ -386,22 +381,6 @@
     public void onKeyguardOccludedChangedLw(boolean occluded) {
     }
 
-    @Override
-    public int rotationForOrientationLw(int orientation, int lastRotation, boolean defaultDisplay) {
-        return rotationToReport;
-    }
-
-    @Override
-    public boolean rotationHasCompatibleMetricsLw(int orientation, int rotation) {
-        return true;
-    }
-
-    @Override
-    public void setRotationLw(int rotation) {
-
-    }
-
-    @Override
     public void setSafeMode(boolean safeMode) {
 
     }
@@ -437,13 +416,8 @@
     }
 
     @Override
-    public void setCurrentOrientationLw(int newOrientation) {
-
-    }
-
-    @Override
     public boolean performHapticFeedbackLw(WindowState win, int effectId,
-            boolean always) {
+            boolean always, String reason) {
         return false;
     }
 
@@ -559,12 +533,13 @@
     }
 
     @Override
-    public void onConfigurationChanged() {
+    public void onConfigurationChanged(DisplayContentInfo displayContentInfo) {
 
     }
 
     @Override
-    public boolean shouldRotateSeamlessly(int oldRotation, int newRotation) {
+    public boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation,
+            int newRotation) {
         return false;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
index ca520ed..9dc0025 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
@@ -70,8 +70,6 @@
                 true /* isAppAnimation */);
         windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
         verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
-        verify(mTransaction).setFinalCrop(eq(mSurfaceControl),
-                argThat(rect -> rect.equals(mStackBounds)));
     }
 
     @Test
@@ -83,9 +81,6 @@
                 true /* isAppAnimation */);
         windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
         verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
-        verify(mTransaction).setFinalCrop(eq(mSurfaceControl),
-                argThat(rect -> rect.left == 20 && rect.top == 40 && rect.right == 30
-                        && rect.bottom == 50));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
index 513c1ec..10d7aad 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -31,6 +31,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOW_CONFIG_ALWAYS_ON_TOP;
 import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
 import static android.app.WindowConfiguration.WINDOW_CONFIG_WINDOWING_MODE;
 import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
@@ -81,6 +82,11 @@
         assertEquals(WINDOW_CONFIG_APP_BOUNDS | WINDOW_CONFIG_WINDOWING_MODE,
                 winConfig1.diff(winConfig2, false /* compareUndefined */));
 
+        winConfig2.setAlwaysOnTop(true);
+        assertEquals(WINDOW_CONFIG_APP_BOUNDS | WINDOW_CONFIG_WINDOWING_MODE
+                | WINDOW_CONFIG_ALWAYS_ON_TOP,
+                winConfig1.diff(winConfig2, false /* compareUndefined */));
+
         assertEquals(0, config1.diff(config3));
         assertEquals(0, config1.diffPublicOnly(config3));
         assertEquals(0, winConfig1.diff(winConfig3, false /* compareUndefined */));
@@ -108,6 +114,12 @@
         assertNotEquals(winConfig1.compareTo(winConfig2), 0);
         winConfig2.setWindowingMode(winConfig1.getWindowingMode());
 
+        // Different always on top state
+        winConfig2.setAlwaysOnTop(true);
+        assertNotEquals(config1.compareTo(config2), 0);
+        assertNotEquals(winConfig1.compareTo(winConfig2), 0);
+        winConfig2.setAlwaysOnTop(winConfig1.isAlwaysOnTop());
+
         // Different bounds
         winConfig2.setAppBounds(0, 2, 3, 4);
         assertNotEquals(config1.compareTo(config2), 0);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index 6c7830e..f8b2828 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -256,6 +256,33 @@
     }
 
     @Test
+    public void testAddChildByIndex() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).build();
+
+        final TestWindowContainer child = root.addChildWindow();
+
+        final TestWindowContainer child2 = builder.setLayer(1).build();
+        final TestWindowContainer child3 = builder.setLayer(2).build();
+        final TestWindowContainer child4 = builder.setLayer(3).build();
+
+        // Test adding at top.
+        root.addChild(child2, POSITION_TOP);
+        assertEquals(child2, root.getChildAt(root.getChildrenCount() - 1));
+
+        // Test adding at bottom.
+        root.addChild(child3, POSITION_BOTTOM);
+        assertEquals(child3, root.getChildAt(0));
+
+        // Test adding in the middle.
+        root.addChild(child4, 1);
+        assertEquals(child3, root.getChildAt(0));
+        assertEquals(child4, root.getChildAt(1));
+        assertEquals(child, root.getChildAt(2));
+        assertEquals(child2, root.getChildAt(3));
+    }
+
+    @Test
     public void testPositionChildAt() throws Exception {
         final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
         final TestWindowContainer root = builder.setLayer(0).build();
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 5a56332..431d1a7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -51,6 +51,7 @@
 
     private WindowToken mWindowToken;
     private final IWindow mIWindow = new TestIWindow();
+    private final Rect mEmptyRect = new Rect();
 
     class WindowStateWithTask extends WindowState {
         final Task mTask;
@@ -120,13 +121,52 @@
         mStubStack = new TaskStack(sWm, 0, null);
     }
 
-    public void assertRect(Rect rect, int left, int top, int right, int bottom) {
+    // Do not use this function directly in the tests below. Instead, use more explicit function
+    // such as assertFlame().
+    private void assertRect(Rect rect, int left, int top, int right, int bottom) {
         assertEquals(left, rect.left);
         assertEquals(top, rect.top);
         assertEquals(right, rect.right);
         assertEquals(bottom, rect.bottom);
     }
 
+    private void assertContentInset(WindowState w, int left, int top, int right, int bottom) {
+        assertRect(w.mContentInsets, left, top, right, bottom);
+    }
+
+    private void assertVisibleInset(WindowState w, int left, int top, int right, int bottom) {
+        assertRect(w.mVisibleInsets, left, top, right, bottom);
+    }
+
+    private void assertStableInset(WindowState w, int left, int top, int right, int bottom) {
+        assertRect(w.mStableInsets, left, top, right, bottom);
+    }
+
+    private void assertFrame(WindowState w, int left, int top, int right, int bottom) {
+        assertRect(w.getFrameLw(), left, top, right, bottom);
+    }
+
+    private void assertContentFrame(WindowState w, Rect expectedRect) {
+        assertRect(w.getContentFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right,
+                expectedRect.bottom);
+    }
+
+    private void assertVisibleFrame(WindowState w, Rect expectedRect) {
+        assertRect(w.getVisibleFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right,
+                expectedRect.bottom);
+    }
+
+    private void assertStableFrame(WindowState w, Rect expectedRect) {
+        assertRect(w.getStableFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right,
+                expectedRect.bottom);
+    }
+
+    private void assertPolicyCrop(WindowStateWithTask w, int left, int top, int right, int bottom) {
+        Rect policyCrop = new Rect();
+        w.calculatePolicyCrop(policyCrop);
+        assertRect(policyCrop, left, top, right, bottom);
+    }
+
     @Test
     public void testLayoutInFullscreenTaskInsets() throws Exception {
         Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
@@ -160,28 +200,28 @@
         // When mFrame extends past cf, the content insets are
         // the difference between mFrame and ContentFrame. Visible
         // and stable frames work the same way.
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
-        assertRect(w.mFrame,0, 0, 1000, 1000);
-        assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
-        assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
-        assertRect(w.mStableInsets, leftStableInset, 0, rightStableInset, 0);
-        // The frames remain as passed in shrunk to the window frame
-        assertTrue(cf.equals(w.getContentFrameLw()));
-        assertTrue(vf.equals(w.getVisibleFrameLw()));
-        assertTrue(sf.equals(w.getStableFrameLw()));
-        // On the other hand mFrame doesn't extend past cf we won't get any insets
+        final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+        w.computeFrameLw(windowFrames);
+        assertFrame(w, 0, 0, 1000, 1000);
+        assertContentInset(w, 0, topContentInset, 0, bottomContentInset);
+        assertVisibleInset(w, 0, topVisibleInset, 0, bottomVisibleInset);
+        assertStableInset(w, leftStableInset, 0, rightStableInset, 0);
+        assertContentFrame(w, cf);
+        assertVisibleFrame(w, vf);
+        assertStableFrame(w, sf);
+        // On the other hand getFrame() doesn't extend past cf we won't get any insets
         w.mAttrs.x = 100;
         w.mAttrs.y = 100;
         w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
         w.mRequestedWidth = 100;
         w.mRequestedHeight = 100;
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
-        assertRect(w.mFrame, 100, 100, 200, 200);
-        assertRect(w.mContentInsets, 0, 0, 0, 0);
+        w.computeFrameLw(windowFrames);
+        assertFrame(w, 100, 100, 200, 200);
+        assertContentInset(w, 0, 0, 0, 0);
         // In this case the frames are shrunk to the window frame.
-        assertTrue(w.mFrame.equals(w.getContentFrameLw()));
-        assertTrue(w.mFrame.equals(w.getVisibleFrameLw()));
-        assertTrue(w.mFrame.equals(w.getStableFrameLw()));
+        assertContentFrame(w, w.getFrameLw());
+        assertVisibleFrame(w, w.getFrameLw());
+        assertStableFrame(w, w.getFrameLw());
     }
 
     @Test
@@ -196,25 +236,26 @@
 
         // Here the window has FILL_PARENT, FILL_PARENT
         // so we expect it to fill the entire available frame.
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
-        assertRect(w.mFrame, 0, 0, 1000, 1000);
+        final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(windowFrames);
+        assertFrame(w, 0, 0, 1000, 1000);
 
         // It can select various widths and heights within the bounds.
         // Strangely the window attribute width is ignored for normal windows
         // and we use mRequestedWidth/mRequestedHeight
         w.mAttrs.width = 300;
         w.mAttrs.height = 300;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
+        w.computeFrameLw(windowFrames);
         // Explicit width and height without requested width/height
         // gets us nothing.
-        assertRect(w.mFrame, 0, 0, 0, 0);
+        assertFrame(w, 0, 0, 0, 0);
 
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
+        w.computeFrameLw(windowFrames);
         // With requestedWidth/Height we can freely choose our size within the
         // parent bounds.
-        assertRect(w.mFrame, 0, 0, 300, 300);
+        assertFrame(w, 0, 0, 300, 300);
 
         // With FLAG_SCALED though, requestedWidth/height is used to control
         // the unscaled surface size, and mAttrs.width/height becomes the
@@ -224,23 +265,23 @@
         w.mRequestedWidth = -1;
         w.mAttrs.width = 100;
         w.mAttrs.height = 100;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
-        assertRect(w.mFrame, 0, 0, 100, 100);
+        w.computeFrameLw(windowFrames);
+        assertFrame(w, 0, 0, 100, 100);
         w.mAttrs.flags = 0;
 
         // But sizes too large will be clipped to the containing frame
         w.mRequestedWidth = 1200;
         w.mRequestedHeight = 1200;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
-        assertRect(w.mFrame, 0, 0, 1000, 1000);
+        w.computeFrameLw(windowFrames);
+        assertFrame(w, 0, 0, 1000, 1000);
 
         // Before they are clipped though windows will be shifted
         w.mAttrs.x = 300;
         w.mAttrs.y = 300;
         w.mRequestedWidth = 1000;
         w.mRequestedHeight = 1000;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
-        assertRect(w.mFrame, 0, 0, 1000, 1000);
+        w.computeFrameLw(windowFrames);
+        assertFrame(w, 0, 0, 1000, 1000);
 
         // If there is room to move around in the parent frame the window will be shifted according
         // to gravity.
@@ -249,17 +290,17 @@
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
-        assertRect(w.mFrame, 700, 0, 1000, 300);
+        w.computeFrameLw(windowFrames);
+         assertFrame(w, 700, 0, 1000, 300);
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
-        assertRect(w.mFrame, 700, 700, 1000, 1000);
+        w.computeFrameLw(windowFrames);
+        assertFrame(w, 700, 700, 1000, 1000);
         // Window specified  x and y are interpreted as offsets in the opposite
         // direction of gravity
         w.mAttrs.x = 100;
         w.mAttrs.y = 100;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
-        assertRect(w.mFrame, 600, 600, 900, 900);
+        w.computeFrameLw(windowFrames);
+        assertFrame(w, 600, 600, 900, 900);
     }
 
     @Test
@@ -279,25 +320,27 @@
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null, WmDisplayCutout.NO_CUTOUT, false);
+        final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+        w.computeFrameLw(windowFrames);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
-        assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
-        assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
-        assertRect(w.mContentInsets, 0, 0, 0, 0);
+        assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
+        assertContentFrame(w, taskBounds);
+        assertContentInset(w, 0, 0, 0, 0);
 
         pf.set(0, 0, logicalWidth, logicalHeight);
         // We still produce insets against the containing frame the same way.
         final int cfRight = logicalWidth / 2;
         final int cfBottom = logicalHeight / 2;
         final Rect cf = new Rect(0, 0, cfRight, cfBottom);
-        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT, false);
-        assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
+        windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
+        w.computeFrameLw(windowFrames);
+        assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
         int contentInsetRight = taskRight - cfRight;
         int contentInsetBottom = taskBottom - cfBottom;
-        assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom);
-        assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight - contentInsetRight,
-                taskBottom - contentInsetBottom);
+        assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
+        assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight,
+                taskBottom - contentInsetBottom));
 
         pf.set(0, 0, logicalWidth, logicalHeight);
         // However if we set temp inset bounds, the insets will be computed
@@ -308,13 +351,14 @@
         final int insetRight = insetLeft + (taskRight - taskLeft);
         final int insetBottom = insetTop + (taskBottom - taskTop);
         task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
-        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT, false);
-        assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
+        windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
+        w.computeFrameLw(windowFrames);
+        assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
         contentInsetRight = insetRight - cfRight;
         contentInsetBottom = insetBottom - cfBottom;
-        assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom);
-        assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight - contentInsetRight,
-                taskBottom - contentInsetBottom);
+        assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
+        assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight,
+                taskBottom - contentInsetBottom));
     }
 
     @Test
@@ -336,41 +380,38 @@
         final Rect vf = cf;
         final Rect sf = vf;
         // We use a decor content frame with insets to produce cropping.
-        Rect dcf = cf;
+        Rect dcf = new Rect(cf);
 
-        final Rect policyCrop = new Rect();
+        final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+        w.computeFrameLw(windowFrames);
+        assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom);
 
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
-        w.calculatePolicyCrop(policyCrop);
-        assertRect(policyCrop, 0, cf.top, logicalWidth, cf.bottom);
-
-        dcf.setEmpty();
+        windowFrames.mDecorFrame.setEmpty();
         // Likewise with no decor frame we would get no crop
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
-        w.calculatePolicyCrop(policyCrop);
-        assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight);
+        w.computeFrameLw(windowFrames);
+        assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight);
 
         // Now we set up a window which doesn't fill the entire decor frame.
         // Normally it would be cropped to it's frame but in the case of docked resizing
         // we need to account for the fact the windows surface will be made
         // fullscreen and thus also make the crop fullscreen.
+
+        windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
         w.mAttrs.width = logicalWidth / 2;
         w.mAttrs.height = logicalHeight / 2;
         w.mRequestedWidth = logicalWidth / 2;
         w.mRequestedHeight = logicalHeight / 2;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
+        w.computeFrameLw(windowFrames);
 
-        w.calculatePolicyCrop(policyCrop);
         // Normally the crop is shrunk from the decor frame
         // to the computed window frame.
-        assertRect(policyCrop, 0, 0, logicalWidth / 2, logicalHeight / 2);
+        assertPolicyCrop(w, 0, 0, logicalWidth / 2, logicalHeight / 2);
 
         w.mDockedResizingForTest = true;
-        w.calculatePolicyCrop(policyCrop);
         // But if we are docked resizing it won't be, however we will still be
         // shrunk to the decor frame and the display.
-        assertRect(policyCrop, 0, 0,
+        assertPolicyCrop(w, 0, 0,
                 Math.min(pf.width(), displayInfo.logicalWidth),
                 Math.min(pf.height(), displayInfo.logicalHeight));
     }
@@ -394,14 +435,13 @@
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
-        w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
-                pf /* contentFrame */, pf /* visibleFrame */, pf /* decorFrame */,
-                pf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT, false);
+        final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+        w.computeFrameLw(windowFrames);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
-        assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
-        assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
-        assertRect(w.mContentInsets, 0, 0, 0, 0);
+        assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
+        assertContentFrame(w, taskBounds);
+        assertContentInset(w, 0, 0, 0, 0);
 
         // Now simulate switch to fullscreen for letterboxed app.
         final int xInset = logicalWidth / 10;
@@ -412,13 +452,11 @@
         w.mAppToken.onOverrideConfigurationChanged(config);
         pf.set(0, 0, logicalWidth, logicalHeight);
         task.mFullscreenForTest = true;
-
-        w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
-                cf /* contentFrame */, cf /* visibleFrame */, pf /* decorFrame */,
-                cf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT, false);
-        assertEquals(cf, w.mFrame);
-        assertEquals(cf, w.getContentFrameLw());
-        assertRect(w.mContentInsets, 0, 0, 0, 0);
+        windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
+        w.computeFrameLw(windowFrames);
+        assertFrame(w, cf.left, cf.top, cf.right, cf.bottom);
+        assertContentFrame(w, cf);
+        assertContentInset(w, 0, 0, 0, 0);
     }
 
     @Test
@@ -433,12 +471,14 @@
         final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
                 fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
 
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout, false);
+        final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+        windowFrames.setDisplayCutout(cutout);
+        w.computeFrameLw(windowFrames);
 
-        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetTop(), 50);
-        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetBottom(), 0);
-        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetLeft(), 0);
-        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetRight(), 0);
+        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
+        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
+        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetLeft(), 0);
+        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0);
     }
 
     @Test
@@ -455,12 +495,14 @@
         final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
                 fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
 
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout, false);
+        final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+        windowFrames.setDisplayCutout(cutout);
+        w.computeFrameLw(windowFrames);
 
-        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetTop(), 50);
-        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetBottom(), 0);
-        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetLeft(), 0);
-        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetRight(), 0);
+        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
+        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
+        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetLeft(), 0);
+        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0);
     }
 
     private WindowStateWithTask createWindow(Task task, int width, int height) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
index d91079e..d13c3c9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -91,15 +91,17 @@
 
                 LocalServices.addService(ActivityManagerInternal.class,
                         mock(ActivityManagerInternal.class));
-                final ActivityManagerInternal am =
-                        LocalServices.getService(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(am).notifyKeyguardFlagsChanged(any());
+                }).when(atm).notifyKeyguardFlagsChanged(any());
 
                 InputManagerService ims = mock(InputManagerService.class);
                 // InputChannel is final and can't be mocked.
@@ -125,6 +127,7 @@
                 LocalServices.removeServiceForTest(DisplayManagerInternal.class);
                 LocalServices.removeServiceForTest(PowerManagerInternal.class);
                 LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+                LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
                 LocalServices.removeServiceForTest(WindowManagerInternal.class);
                 LocalServices.removeServiceForTest(WindowManagerPolicy.class);
             }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 85e846d..0ddba6a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import android.graphics.Rect;
+import android.view.SurfaceControl;
 import android.view.WindowManager;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -29,6 +31,8 @@
 
 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.Surface.ROTATION_0;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -48,8 +52,10 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
@@ -349,6 +355,32 @@
         assertThat(app.getDisplayId(), is(mDisplayContent.getDisplayId()));
     }
 
+    @Test
+    public void testSeamlesslyRotateWindow() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+
+        app.mHasSurface = true;
+        app.mSurfaceControl = mock(SurfaceControl.class);
+        app.mWinAnimator.mSurfaceController = mock(WindowSurfaceController.class);
+        try {
+            app.getFrameLw().set(10, 20, 60, 80);
+
+            app.seamlesslyRotate(t, ROTATION_0, ROTATION_90);
+
+            assertTrue(app.mSeamlesslyRotated);
+            assertEquals(new Rect(20, mDisplayInfo.logicalWidth - 60,
+                    80, mDisplayInfo.logicalWidth - 10), app.getFrameLw());
+
+            verify(t).setPosition(app.mSurfaceControl, app.getFrameLw().left, app.getFrameLw().top);
+            verify(app.mWinAnimator.mSurfaceController).setPosition(t, 0, 50, false);
+            verify(app.mWinAnimator.mSurfaceController).setMatrix(t, 0, -1, 1, 0, false);
+        } finally {
+            app.mSurfaceControl = null;
+            app.mHasSurface = false;
+        }
+    }
+
     private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
         reset(mPowerManagerWrapper);
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 2e4740b..a1b1640 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -22,8 +22,12 @@
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
+import android.view.Display;
 import android.view.IApplicationToken;
 import android.view.IWindow;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
 import android.view.WindowManager;
 
 import static android.app.AppOpsManager.OP_NONE;
@@ -58,6 +62,37 @@
         return service;
     }
 
+    /** An extension of {@link DisplayContent} to gain package scoped access. */
+    public static class TestDisplayContent extends DisplayContent {
+
+        private TestDisplayContent(Display display, WindowManagerService service,
+                WallpaperController wallpaperController, DisplayWindowController controller) {
+            super(display, service, wallpaperController, controller);
+        }
+
+        /** Create a mocked default {@link DisplayContent}. */
+        public static TestDisplayContent create(Context context) {
+            final TestDisplayContent displayContent = mock(TestDisplayContent.class);
+            displayContent.isDefaultDisplay = true;
+
+            final DisplayPolicy displayPolicy = mock(DisplayPolicy.class);
+            when(displayPolicy.navigationBarCanMove()).thenReturn(true);
+            when(displayPolicy.hasNavigationBar()).thenReturn(true);
+
+            final DisplayRotation displayRotation = new DisplayRotation(
+                    mock(WindowManagerService.class), displayContent, displayPolicy,
+                    context, new Object());
+            displayRotation.mPortraitRotation = Surface.ROTATION_0;
+            displayRotation.mLandscapeRotation = Surface.ROTATION_90;
+            displayRotation.mUpsideDownRotation = Surface.ROTATION_180;
+            displayRotation.mSeascapeRotation = Surface.ROTATION_270;
+
+            when(displayContent.getDisplayRotation()).thenReturn(displayRotation);
+
+            return displayContent;
+        }
+    }
+
     /**
      * Creates a mock instance of {@link StackWindowController}.
      */
@@ -113,6 +148,7 @@
     /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
     public static class TestAppWindowToken extends AppWindowToken {
         boolean mOnTop = false;
+        private Transaction mPendingTransactionOverride;
 
         private TestAppWindowToken(DisplayContent dc) {
             super(dc.mService, new IApplicationToken.Stub() {
@@ -158,6 +194,17 @@
         boolean isOnTop() {
             return mOnTop;
         }
+
+        void setPendingTransaction(Transaction transaction) {
+            mPendingTransactionOverride = transaction;
+        }
+
+        @Override
+        public Transaction getPendingTransaction() {
+            return mPendingTransactionOverride == null
+                    ? super.getPendingTransaction()
+                    : mPendingTransactionOverride;
+        }
     }
 
     static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 473a287..ef019fe 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -231,6 +231,11 @@
     }
 
     AppWindowToken createAppWindowToken(DisplayContent dc, int windowingMode, int activityType) {
+        return createTestAppWindowToken(dc, windowingMode, activityType);
+    }
+
+    WindowTestUtils.TestAppWindowToken createTestAppWindowToken(DisplayContent dc, int
+            windowingMode, int activityType) {
         final TaskStack stack = createStackControllerOnStackOnDisplay(windowingMode, activityType,
                 dc).mContainer;
         final Task task = createTaskInStack(stack, 0 /* userId */);
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
index 40a10e0..f82b012 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
@@ -21,14 +21,19 @@
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 
+import static com.android.server.wm.utils.CoordinateTransforms.transformLogicalToPhysicalCoordinates;
 import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
 
+
+import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
+
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.*;
 
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.PointF;
+import android.view.DisplayInfo;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -41,6 +46,7 @@
     private static final int H = 400;
 
     private final Matrix mMatrix = new Matrix();
+    private final Matrix mMatrix2 = new Matrix();
 
     @Rule
     public final ErrorCollector mErrorCollector = new ErrorCollector();
@@ -48,39 +54,140 @@
     @Before
     public void setUp() throws Exception {
         mMatrix.setTranslate(0xdeadbeef, 0xdeadbeef);
+        mMatrix2.setTranslate(0xbeefdead, 0xbeefdead);
     }
 
     @Test
-    public void transformPhysicalToLogicalCoordinates_rot0() throws Exception {
+    public void transformPhysicalToLogicalCoordinates_rot0() {
         transformPhysicalToLogicalCoordinates(ROTATION_0, W, H, mMatrix);
         assertThat(mMatrix, is(Matrix.IDENTITY_MATRIX));
     }
 
     @Test
-    public void transformPhysicalToLogicalCoordinates_rot90() throws Exception {
+    public void transformPhysicalToLogicalCoordinates_rot90() {
         transformPhysicalToLogicalCoordinates(ROTATION_90, W, H, mMatrix);
 
-        checkDevicePoint(0, 0).mapsToLogicalPoint(0, W);
-        checkDevicePoint(W, H).mapsToLogicalPoint(H, 0);
+        checkPoint(0, 0).transformsTo(0, W);
+        checkPoint(W, H).transformsTo(H, 0);
     }
 
     @Test
-    public void transformPhysicalToLogicalCoordinates_rot180() throws Exception {
+    public void transformPhysicalToLogicalCoordinates_rot180() {
         transformPhysicalToLogicalCoordinates(ROTATION_180, W, H, mMatrix);
 
-        checkDevicePoint(0, 0).mapsToLogicalPoint(W, H);
-        checkDevicePoint(W, H).mapsToLogicalPoint(0, 0);
+        checkPoint(0, 0).transformsTo(W, H);
+        checkPoint(W, H).transformsTo(0, 0);
     }
 
     @Test
-    public void transformPhysicalToLogicalCoordinates_rot270() throws Exception {
+    public void transformPhysicalToLogicalCoordinates_rot270() {
         transformPhysicalToLogicalCoordinates(ROTATION_270, W, H, mMatrix);
 
-        checkDevicePoint(0, 0).mapsToLogicalPoint(H, 0);
-        checkDevicePoint(W, H).mapsToLogicalPoint(0, W);
+        checkPoint(0, 0).transformsTo(H, 0);
+        checkPoint(W, H).transformsTo(0, W);
     }
 
-    private DevicePointAssertable checkDevicePoint(int x, int y) {
+    @Test
+    public void transformLogicalToPhysicalCoordinates_rot0() {
+        transformLogicalToPhysicalCoordinates(ROTATION_0, W, H, mMatrix);
+        assertThat(mMatrix, is(Matrix.IDENTITY_MATRIX));
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinates_rot90() {
+        transformLogicalToPhysicalCoordinates(ROTATION_90, W, H, mMatrix);
+
+        checkPoint(0, W).transformsTo(0, 0);
+        checkPoint(H, 0).transformsTo(W, H);
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinates_rot180() {
+        transformLogicalToPhysicalCoordinates(ROTATION_180, W, H, mMatrix);
+
+        checkPoint(W, H).transformsTo(0, 0);
+        checkPoint(0, 0).transformsTo(W, H);
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinates_rot270() {
+        transformLogicalToPhysicalCoordinates(ROTATION_270, W, H, mMatrix);
+
+        checkPoint(H, 0).transformsTo(0, 0);
+        checkPoint(0, W).transformsTo(W, H);
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinatesIsInverse_rot0() {
+        transformLogicalToPhysicalCoordinates(ROTATION_0, W, H, mMatrix);
+        transformPhysicalToLogicalCoordinates(ROTATION_0, W, H, mMatrix2);
+
+        assertMatricesAreInverses(mMatrix, mMatrix2);
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinatesIsInverse_rot90() {
+        transformLogicalToPhysicalCoordinates(ROTATION_90, W, H, mMatrix);
+        transformPhysicalToLogicalCoordinates(ROTATION_90, W, H, mMatrix2);
+
+        assertMatricesAreInverses(mMatrix, mMatrix2);
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinatesIsInverse_rot180() {
+        transformLogicalToPhysicalCoordinates(ROTATION_180, W, H, mMatrix);
+        transformPhysicalToLogicalCoordinates(ROTATION_180, W, H, mMatrix2);
+
+        assertMatricesAreInverses(mMatrix, mMatrix2);
+    }
+
+    @Test
+    public void transformLogicalToPhysicalCoordinatesIsInverse_rot270() {
+        transformLogicalToPhysicalCoordinates(ROTATION_270, W, H, mMatrix);
+        transformPhysicalToLogicalCoordinates(ROTATION_270, W, H, mMatrix2);
+
+        assertMatricesAreInverses(mMatrix, mMatrix2);
+    }
+
+    @Test
+    public void transformBetweenRotations_rot180_rot270() {
+        // W,H are flipped, because they need to be given in the new orientation, i.e. ROT_270.
+        transformToRotation(ROTATION_180, ROTATION_270, H, W, mMatrix);
+
+        checkPoint(0, 0).transformsTo(0, W);
+        checkPoint(W, H).transformsTo(H, 0);
+    }
+
+    @Test
+    public void transformBetweenRotations_rot90_rot0() {
+        transformToRotation(ROTATION_180, ROTATION_270, W, H, mMatrix);
+
+        checkPoint(0, 0).transformsTo(0, H);
+        // H,W is bottom right in ROT_90
+        checkPoint(H, W).transformsTo(W, 0);
+    }
+
+    @Test
+    public void transformBetweenRotations_displayInfo() {
+        final DisplayInfo di = new DisplayInfo();
+        di.rotation = ROTATION_90;
+        di.logicalWidth = H;  // dimensions are flipped in ROT_90
+        di.logicalHeight = W;
+        transformToRotation(ROTATION_180, ROTATION_270, di, mMatrix);
+
+        // W,H are flipped, because they need to be given in the new orientation, i.e. ROT_270.
+        transformToRotation(ROTATION_180, ROTATION_270, H, W, mMatrix2);
+
+        assertEquals(mMatrix2, mMatrix);
+    }
+
+    private void assertMatricesAreInverses(Matrix matrix, Matrix matrix2) {
+        final Matrix concat = new Matrix();
+        concat.setConcat(matrix, matrix2);
+        assertTrue("expected identity, but was: " + concat, concat.isIdentity());
+    }
+
+    private TransformPointAssertable checkPoint(int x, int y) {
         final Point devicePoint = new Point(x, y);
         final float[] fs = new float[] {x, y};
         mMatrix.mapPoints(fs);
@@ -92,7 +199,7 @@
         };
     }
 
-    public interface DevicePointAssertable {
-        void mapsToLogicalPoint(int x, int y);
+    public interface TransformPointAssertable {
+        void transformsTo(int x, int y);
     }
 }
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
index 345c1d7..56d3a20 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
@@ -13,14 +13,20 @@
  */
 package com.android.server;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.when;
 
 import android.content.pm.PackageManagerInternal;
+import android.net.Uri;
 import android.os.Build;
 import android.support.test.InstrumentationRegistry;
 import android.testing.TestableContext;
 
+import com.android.server.uri.UriGrantsManagerInternal;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.mockito.Mock;
@@ -28,9 +34,11 @@
 
 public class UiServiceTestCase {
     @Mock protected PackageManagerInternal mPmi;
+    @Mock protected UriGrantsManagerInternal mUgmInternal;
 
     protected static final String PKG_N_MR1 = "com.example.n_mr1";
     protected static final String PKG_O = "com.example.o";
+    protected static final String PKG_P = "com.example.p";
 
     @Rule
     public final TestableContext mContext =
@@ -57,9 +65,16 @@
                             return Build.VERSION_CODES.N_MR1;
                         case PKG_O:
                             return Build.VERSION_CODES.O;
+                        case PKG_P:
+                            return Build.VERSION_CODES.P;
                         default:
                             return Build.VERSION_CODES.CUR_DEVELOPMENT;
                     }
                 });
+
+        LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+        LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
+        when(mUgmInternal.checkGrantUriPermission(
+                anyInt(), anyString(), any(Uri.class), anyInt(), anyInt())).thenReturn(-1);
     }
 }
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 7809999..7abf49e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -19,7 +19,9 @@
 import static android.app.Notification.GROUP_ALERT_CHILDREN;
 import static android.app.Notification.GROUP_ALERT_SUMMARY;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNull;
@@ -149,6 +151,9 @@
         mService.setFallbackVibrationPattern(FALLBACK_VIBRATION_PATTERN);
         mService.setUsageStats(mUsageStats);
         mService.setAccessibilityManager(accessibilityManager);
+        mService.mScreenOn = false;
+        mService.mInCall = false;
+        mService.mNotificationPulseEnabled = true;
     }
 
     //
@@ -190,6 +195,11 @@
                 true /* noisy */, false /* buzzy*/, false /* lights */);
     }
 
+    private NotificationRecord getInsistentBeepyOnceNotification() {
+        return getNotificationRecord(mId, true /* insistent */, true /* once */,
+                true /* noisy */, false /* buzzy*/, false /* lights */);
+    }
+
     private NotificationRecord getInsistentBeepyLeanbackNotification() {
         return getLeanbackNotificationRecord(mId, true /* insistent */, false /* once */,
                 true /* noisy */, false /* buzzy*/, false /* lights */);
@@ -216,8 +226,13 @@
     }
 
     private NotificationRecord getLightsNotification() {
+        return getNotificationRecord(mId, false /* insistent */, false /* once */,
+                false /* noisy */, false /* buzzy*/, true /* lights */);
+    }
+
+    private NotificationRecord getLightsOnceNotification() {
         return getNotificationRecord(mId, false /* insistent */, true /* once */,
-                false /* noisy */, true /* buzzy*/, true /* lights */);
+                false /* noisy */, false /* buzzy*/, true /* lights */);
     }
 
     private NotificationRecord getCustomLightsNotification() {
@@ -244,6 +259,12 @@
                 groupKey, groupAlertBehavior, false);
     }
 
+    private NotificationRecord getLightsNotificationRecord(String groupKey,
+            int groupAlertBehavior) {
+        return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true, true,
+                true, groupKey, groupAlertBehavior, false);
+    }
+
     private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
             boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
             boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
@@ -343,22 +364,23 @@
 
     private void verifyNeverVibrate() {
         verify(mVibrator, never()).vibrate(anyInt(), anyString(), (VibrationEffect) anyObject(),
-                (AudioAttributes) anyObject());
+                anyString(), (AudioAttributes) anyObject());
     }
 
     private void verifyVibrate() {
         verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher),
-                (AudioAttributes) anyObject());
+                anyString(), (AudioAttributes) anyObject());
     }
 
     private void verifyVibrateLooped() {
         verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateLoopMatcher),
-                (AudioAttributes) anyObject());
+                anyString(), (AudioAttributes) anyObject());
     }
 
     private void verifyDelayedVibrateLooped() {
         verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
-                argThat(mVibrateLoopMatcher), (AudioAttributes) anyObject());
+                argThat(mVibrateLoopMatcher), anyString(),
+                (AudioAttributes) anyObject());
     }
 
     private void verifyStopVibrate() {
@@ -369,6 +391,10 @@
         verify(mVibrator, never()).cancel();
     }
 
+    private void verifyNeverLights() {
+        verify(mLight, never()).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
+    }
+
     private void verifyLights() {
         verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
     }
@@ -507,6 +533,24 @@
         assertFalse(s.isInterruptive());
     }
 
+    /**
+     * Tests the case where the user re-posts a {@link Notification} with looping sound where
+     * {@link Notification.Builder#setOnlyAlertOnce(true)} has been called.  This should silence
+     * the sound associated with the notification.
+     * @throws Exception
+     */
+    @Test
+    public void testNoisyOnceUpdateDoesCancelAudio() throws Exception {
+        NotificationRecord r = getInsistentBeepyNotification();
+        NotificationRecord s = getInsistentBeepyOnceNotification();
+        s.isUpdate = true;
+
+        mService.buzzBeepBlinkLocked(r);
+        mService.buzzBeepBlinkLocked(s);
+
+        verifyStopAudio();
+    }
+
     @Test
     public void testQuietUpdateDoesNotCancelAudioFromOther() throws Exception {
         NotificationRecord r = getBeepyNotification();
@@ -603,7 +647,8 @@
         VibrationEffect effect = VibrationEffect.createWaveform(r.getVibration(), -1);
 
         verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
-                eq(effect), (AudioAttributes) anyObject());
+                eq(effect), anyString(),
+                (AudioAttributes) anyObject());
         assertTrue(r.isInterruptive());
     }
 
@@ -637,7 +682,7 @@
         mService.buzzBeepBlinkLocked(r);
 
         verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
-                eq(FALLBACK_VIBRATION), (AudioAttributes) anyObject());
+                eq(FALLBACK_VIBRATION), anyString(), (AudioAttributes) anyObject());
         verify(mRingtonePlayer, never()).playAsync
                 (anyObject(), anyObject(), anyBoolean(), anyObject());
         assertTrue(r.isInterruptive());
@@ -712,7 +757,8 @@
         mService.buzzBeepBlinkLocked(summary);
 
         verifyBeepLooped();
-        assertTrue(summary.isInterruptive());
+        // summaries are never interruptive for notification counts
+        assertFalse(summary.isInterruptive());
     }
 
     @Test
@@ -990,6 +1036,156 @@
         verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
     }
 
+    @Test
+    public void testLightsScreenOn() {
+        mService.mScreenOn = true;
+        NotificationRecord r = getLightsNotification();
+        mService.buzzBeepBlinkLocked(r);
+        verifyNeverLights();
+        assertFalse(r.isInterruptive());
+    }
+
+    @Test
+    public void testLightsInCall() {
+        mService.mInCall = true;
+        NotificationRecord r = getLightsNotification();
+        mService.buzzBeepBlinkLocked(r);
+        verifyNeverLights();
+        assertFalse(r.isInterruptive());
+    }
+
+    @Test
+    public void testLightsSilentUpdate() {
+        NotificationRecord r = getLightsOnceNotification();
+        mService.buzzBeepBlinkLocked(r);
+        verifyLights();
+        assertTrue(r.isInterruptive());
+
+        r = getLightsOnceNotification();
+        r.isUpdate = true;
+        mService.buzzBeepBlinkLocked(r);
+        // checks that lights happened once, i.e. this new call didn't trigger them again
+        verifyLights();
+        assertFalse(r.isInterruptive());
+    }
+
+    @Test
+    public void testLightsUnimportant() {
+        NotificationRecord r = getLightsNotification();
+        r.setImportance(IMPORTANCE_LOW, "testing");
+        mService.buzzBeepBlinkLocked(r);
+        verifyNeverLights();
+        assertFalse(r.isInterruptive());
+    }
+
+    @Test
+    public void testLightsNoLights() {
+        NotificationRecord r = getQuietNotification();
+        mService.buzzBeepBlinkLocked(r);
+        verifyNeverLights();
+        assertFalse(r.isInterruptive());
+    }
+
+    @Test
+    public void testLightsNoLightOnDevice() {
+        mService.mHasLight = false;
+        NotificationRecord r = getLightsNotification();
+        mService.buzzBeepBlinkLocked(r);
+        verifyNeverLights();
+        assertFalse(r.isInterruptive());
+    }
+
+    @Test
+    public void testLightsLightsOffGlobally() {
+        mService.mNotificationPulseEnabled = false;
+        NotificationRecord r = getLightsNotification();
+        mService.buzzBeepBlinkLocked(r);
+        verifyNeverLights();
+        assertFalse(r.isInterruptive());
+    }
+
+    @Test
+    public void testLightsDndIntercepted() {
+        NotificationRecord r = getLightsNotification();
+        r.setSuppressedVisualEffects(SUPPRESSED_EFFECT_LIGHTS);
+        mService.buzzBeepBlinkLocked(r);
+        verifyNeverLights();
+        assertFalse(r.isInterruptive());
+    }
+
+    @Test
+    public void testGroupAlertSummaryNoLightsChild() {
+        NotificationRecord child = getLightsNotificationRecord("a", GROUP_ALERT_SUMMARY);
+
+        mService.buzzBeepBlinkLocked(child);
+
+        verifyNeverLights();
+        assertFalse(child.isInterruptive());
+    }
+
+    @Test
+    public void testGroupAlertSummaryLightsSummary() {
+        NotificationRecord summary = getLightsNotificationRecord("a", GROUP_ALERT_SUMMARY);
+        summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
+
+        mService.buzzBeepBlinkLocked(summary);
+
+        verifyLights();
+        // summaries should never count for interruptiveness counts
+        assertFalse(summary.isInterruptive());
+    }
+
+    @Test
+    public void testGroupAlertSummaryLightsNonGroupChild() {
+        NotificationRecord nonGroup = getLightsNotificationRecord(null, GROUP_ALERT_SUMMARY);
+
+        mService.buzzBeepBlinkLocked(nonGroup);
+
+        verifyLights();
+        assertTrue(nonGroup.isInterruptive());
+    }
+
+    @Test
+    public void testGroupAlertChildNoLightsSummary() {
+        NotificationRecord summary = getLightsNotificationRecord("a", GROUP_ALERT_CHILDREN);
+        summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
+
+        mService.buzzBeepBlinkLocked(summary);
+
+        verifyNeverLights();
+        assertFalse(summary.isInterruptive());
+    }
+
+    @Test
+    public void testGroupAlertChildLightsChild() {
+        NotificationRecord child = getLightsNotificationRecord("a", GROUP_ALERT_CHILDREN);
+
+        mService.buzzBeepBlinkLocked(child);
+
+        verifyLights();
+        assertTrue(child.isInterruptive());
+    }
+
+    @Test
+    public void testGroupAlertChildLightsNonGroupSummary() {
+        NotificationRecord nonGroup = getLightsNotificationRecord(null, GROUP_ALERT_CHILDREN);
+
+        mService.buzzBeepBlinkLocked(nonGroup);
+
+        verifyLights();
+        assertTrue(nonGroup.isInterruptive());
+    }
+
+    @Test
+    public void testGroupAlertAllLightsGroup() {
+        NotificationRecord group = getLightsNotificationRecord("a", GROUP_ALERT_ALL);
+
+        mService.buzzBeepBlinkLocked(group);
+
+        verifyLights();
+        assertTrue(group.isInterruptive());
+    }
+
     static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
         private final int mRepeatIndex;
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 8d4c5b1..84ef0c9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -50,13 +50,14 @@
 public class GroupHelperTest extends UiServiceTestCase {
     private @Mock GroupHelper.Callback mCallback;
 
+    private final static int AUTOGROUP_AT_COUNT = 4;
     private GroupHelper mGroupHelper;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mGroupHelper = new GroupHelper(mCallback);
+        mGroupHelper = new GroupHelper(AUTOGROUP_AT_COUNT, mCallback);
     }
 
     private StatusBarNotification getSbn(String pkg, int id, String tag,
@@ -79,7 +80,7 @@
     @Test
     public void testNoGroup_postingUnderLimit() throws Exception {
         final String pkg = "package";
-        for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+        for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
             mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
                     false);
         }
@@ -94,12 +95,12 @@
     public void testNoGroup_multiPackage() throws Exception {
         final String pkg = "package";
         final String pkg2 = "package2";
-        for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+        for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
             mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
                     false);
         }
         mGroupHelper.onNotificationPosted(
-                getSbn(pkg2, GroupHelper.AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM), false);
+                getSbn(pkg2, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM), false);
         verify(mCallback, never()).addAutoGroupSummary(
                 eq(UserHandle.USER_SYSTEM), eq(pkg), anyString());
         verify(mCallback, never()).addAutoGroup(anyString());
@@ -110,13 +111,12 @@
     @Test
     public void testNoGroup_multiUser() throws Exception {
         final String pkg = "package";
-        for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+        for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
             mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
                     false);
         }
         mGroupHelper.onNotificationPosted(
-                getSbn(pkg, GroupHelper.AUTOGROUP_AT_COUNT, "four", UserHandle.ALL),
-                false);
+                getSbn(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.ALL), false);
         verify(mCallback, never()).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
         verify(mCallback, never()).addAutoGroup(anyString());
         verify(mCallback, never()).removeAutoGroup(anyString());
@@ -126,13 +126,12 @@
     @Test
     public void testNoGroup_someAreGrouped() throws Exception {
         final String pkg = "package";
-        for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+        for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
             mGroupHelper.onNotificationPosted(
                     getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false);
         }
         mGroupHelper.onNotificationPosted(
-                getSbn(pkg, GroupHelper.AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a"),
-                false);
+                getSbn(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a"), false);
         verify(mCallback, never()).addAutoGroupSummary(
                 eq(UserHandle.USER_SYSTEM), eq(pkg), anyString());
         verify(mCallback, never()).addAutoGroup(anyString());
@@ -144,12 +143,12 @@
     @Test
     public void testPostingOverLimit() throws Exception {
         final String pkg = "package";
-        for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT; i++) {
+        for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
             mGroupHelper.onNotificationPosted(
                     getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false);
         }
         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
-        verify(mCallback, times(GroupHelper.AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+        verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
         verify(mCallback, never()).removeAutoGroup(anyString());
         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
     }
@@ -158,18 +157,18 @@
     public void testDropToZeroRemoveGroup() throws Exception {
         final String pkg = "package";
         List<StatusBarNotification> posted = new ArrayList<>();
-        for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT; i++) {
+        for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
             final StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
             posted.add(sbn);
             mGroupHelper.onNotificationPosted(sbn, false);
         }
         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
-        verify(mCallback, times(GroupHelper.AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+        verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
         verify(mCallback, never()).removeAutoGroup(anyString());
         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
         Mockito.reset(mCallback);
 
-        for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+        for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
             mGroupHelper.onNotificationRemoved(posted.remove(0));
         }
         verify(mCallback, never()).removeAutoGroup(anyString());
@@ -185,28 +184,28 @@
     public void testAppStartsGrouping() throws Exception {
         final String pkg = "package";
         List<StatusBarNotification> posted = new ArrayList<>();
-        for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT; i++) {
+        for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
             final StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
             posted.add(sbn);
             mGroupHelper.onNotificationPosted(sbn, false);
         }
         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
-        verify(mCallback, times(GroupHelper.AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+        verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
         verify(mCallback, never()).removeAutoGroup(anyString());
         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
         Mockito.reset(mCallback);
 
         int i = 0;
-        for (i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 2; i++) {
+        for (i = 0; i < AUTOGROUP_AT_COUNT - 2; i++) {
             final StatusBarNotification sbn =
                     getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, "app group");
             mGroupHelper.onNotificationPosted(sbn, false);
         }
-        verify(mCallback, times(GroupHelper.AUTOGROUP_AT_COUNT - 2)).removeAutoGroup(anyString());
+        verify(mCallback, times(AUTOGROUP_AT_COUNT - 2)).removeAutoGroup(anyString());
         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
         Mockito.reset(mCallback);
 
-        for (; i < GroupHelper.AUTOGROUP_AT_COUNT; i++) {
+        for (; i < AUTOGROUP_AT_COUNT; i++) {
             final StatusBarNotification sbn =
                     getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, "app group");
             mGroupHelper.onNotificationPosted(sbn, false);
@@ -220,13 +219,13 @@
             throws Exception {
         final String pkg = "package";
         List<StatusBarNotification> posted = new ArrayList<>();
-        for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT; i++) {
+        for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
             final StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
             posted.add(sbn);
             mGroupHelper.onNotificationPosted(sbn, false);
         }
         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
-        verify(mCallback, times(GroupHelper.AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+        verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
         verify(mCallback, never()).removeAutoGroup(anyString());
         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
         Mockito.reset(mCallback);
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 d0a656c..95d4a15 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -474,6 +475,59 @@
     }
 
     @Test
+    public void testIsPackageAllowed() {
+        for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
+            ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                    mIpm, approvalLevel);
+            writeExpectedValuesToSettings(approvalLevel);
+            service.migrateToXml();
+
+            verifyExpectedApprovedPackages(service);
+        }
+    }
+
+    @Test
+    public void testUpgradeAppBindsNewServices() throws Exception {
+        // If the primary and secondary lists contain component names, only those components within
+        // the package should be matched
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                mIpm,
+                ManagedServices.APPROVAL_BY_PACKAGE);
+
+        List<String> packages = new ArrayList<>();
+        packages.add("package");
+        addExpectedServices(service, packages, 0);
+
+        // only 2 components are approved per package
+        mExpectedPrimaryComponentNames.clear();
+        mExpectedPrimaryPackages.clear();
+        mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
+        mExpectedSecondaryComponentNames.clear();
+        mExpectedSecondaryPackages.clear();
+
+        loadXml(service);
+
+        // new component expected
+        mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2:package/C3");
+
+        service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
+
+        // verify the 3 components per package are enabled (bound)
+        verifyExpectedBoundEntries(service, true);
+
+        // verify the last component per package is not enabled/we don't try to bind to it
+        for (String pkg : packages) {
+            ComponentName unapprovedAdditionalComponent =
+                    ComponentName.unflattenFromString(pkg + "/C3");
+            assertFalse(
+                    service.isComponentEnabledForCurrentProfiles(
+                            unapprovedAdditionalComponent));
+            verify(mIpm, never()).getServiceInfo(
+                    eq(unapprovedAdditionalComponent), anyInt(), anyInt());
+        }
+    }
+
+    @Test
     public void testSetPackageOrComponentEnabled() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -640,6 +694,20 @@
         }
     }
 
+    @Test
+    public void testIsSameUser() {
+        IInterface service = mock(IInterface.class);
+        when(service.asBinder()).thenReturn(mock(IBinder.class));
+        ManagedServices services = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                mIpm, APPROVAL_BY_PACKAGE);
+        services.registerService(service, null, 10);
+        ManagedServices.ManagedServiceInfo info = services.checkServiceTokenLocked(service);
+        info.isSystem = true;
+
+        assertFalse(services.isSameUser(service, 0));
+        assertTrue(services.isSameUser(service, 10));
+    }
+
     private void loadXml(ManagedServices service) throws Exception {
         final StringBuffer xml = new StringBuffer();
         xml.append("<" + service.getConfig().xmlTag + ">\n");
@@ -729,7 +797,7 @@
                     if (service.mApprovalLevel == APPROVAL_BY_PACKAGE) {
                         assertTrue(packageOrComponent,
                                 service.isComponentEnabledForPackage(packageOrComponent));
-                        for (int i = 1; i <= 3; i ++) {
+                        for (int i = 1; i <= 3; i++) {
                             ComponentName componentName = ComponentName.unflattenFromString(
                                     packageOrComponent +"/C" + i);
                             assertTrue(service.isComponentEnabledForCurrentProfiles(
@@ -774,6 +842,39 @@
         }
     }
 
+
+    private void verifyExpectedApprovedPackages(ManagedServices service) {
+        verifyExpectedApprovedPackages(service, true);
+        verifyExpectedApprovedPackages(service, false);
+    }
+
+    private void verifyExpectedApprovedPackages(ManagedServices service, boolean primary) {
+        ArrayMap<Integer, String> verifyMap = primary
+                ? mExpectedPrimary.get(service.mApprovalLevel)
+                : mExpectedSecondary.get(service.mApprovalLevel);
+        verifyExpectedApprovedPackages(service, verifyMap);
+    }
+
+    private void verifyExpectedApprovedPackages(ManagedServices service,
+            ArrayMap<Integer, String> verifyMap) {
+        for (int userId : verifyMap.keySet()) {
+            for (String verifyValue : verifyMap.get(userId).split(":")) {
+                if (!TextUtils.isEmpty(verifyValue)) {
+                    ComponentName component = ComponentName.unflattenFromString(verifyValue);
+                    if (component != null ) {
+                        assertTrue("service type " + service.mApprovalLevel + ":"
+                                        + verifyValue + " is not allowed for user " + userId,
+                                service.isPackageAllowed(component.getPackageName(), userId));
+                    } else {
+                        assertTrue("service type " + service.mApprovalLevel + ":"
+                                        + verifyValue + " is not allowed for user " + userId,
+                                service.isPackageAllowed(verifyValue, userId));
+                    }
+                }
+            }
+        }
+    }
+
     private void writeExpectedValuesToSettings(int approvalLevel) {
         for (int userId : mExpectedPrimary.get(approvalLevel).keySet()) {
             Settings.Secure.putStringForUser(getContext().getContentResolver(), SETTING,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
index fd674f0..f17a30d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
@@ -25,6 +25,9 @@
 
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.service.notification.Adjustment;
@@ -54,6 +57,9 @@
         ArrayList<String> people = new ArrayList<>();
         people.add("you");
         signals.putStringArrayList(Adjustment.KEY_PEOPLE, people);
+        ArrayList<Notification.Action> smartActions = new ArrayList<>();
+        smartActions.add(createAction());
+        signals.putParcelableArrayList(Adjustment.KEY_SMART_ACTIONS, smartActions);
         Adjustment adjustment = new Adjustment("pkg", r.getKey(), signals, "", 0);
         r.addAdjustment(adjustment);
 
@@ -66,6 +72,7 @@
         assertTrue(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
         assertEquals(people, r.getPeopleOverride());
         assertEquals(snoozeCriteria, r.getSnoozeCriteria());
+        assertEquals(smartActions, r.getSmartActions());
     }
 
     @Test
@@ -114,4 +121,11 @@
                 0, n, UserHandle.ALL, null, System.currentTimeMillis());
        return new NotificationRecord(getContext(), sbn, channel);
     }
+
+    private Notification.Action createAction() {
+        return new Notification.Action.Builder(
+                Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
+                "action",
+                PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
index 2241047..ca473c6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
@@ -19,20 +19,26 @@
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
 
 import android.app.NotificationChannel;
+import android.net.Uri;
 import android.os.Parcel;
 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.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
@@ -68,4 +74,23 @@
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         channel.writeXml(serializer);
     }
+
+    @Test
+    public void testBackupEmptySound() throws Exception {
+        NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+        channel.setSound(Uri.EMPTY, null);
+
+        XmlSerializer serializer = new FastXmlSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        channel.writeXmlForBackup(serializer, getContext());
+
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+        restored.populateFromXmlForRestore(parser, getContext());
+
+        assertNull(restored.getSound());
+    }
 }
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 ef9ba78..f255d49 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -31,11 +31,14 @@
 import static org.mockito.Mockito.when;
 
 import android.app.INotificationManager;
+import android.app.Notification;
 import android.app.NotificationChannel;
+import android.app.PendingIntent;
 import android.content.Intent;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Parcel;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.NotificationRankingUpdate;
@@ -91,6 +94,8 @@
             assertEquals(getShowBadge(i), ranking.canShowBadge());
             assertEquals(getUserSentiment(i), ranking.getUserSentiment());
             assertEquals(getHidden(i), ranking.isSuspended());
+            assertActionsEqual(getSmartActions(key, i), ranking.getSmartActions());
+            assertEquals(getSmartReplies(key, i), ranking.getSmartReplies());
         }
     }
 
@@ -107,6 +112,8 @@
         int[] importance = new int[mKeys.length];
         Bundle userSentiment = new Bundle();
         Bundle mHidden = new Bundle();
+        Bundle smartActions = new Bundle();
+        Bundle smartReplies = new Bundle();
 
         for (int i = 0; i < mKeys.length; i++) {
             String key = mKeys[i];
@@ -124,11 +131,14 @@
             showBadge.putBoolean(key, getShowBadge(i));
             userSentiment.putInt(key, getUserSentiment(i));
             mHidden.putBoolean(key, getHidden(i));
+            smartActions.putParcelableArrayList(key, getSmartActions(key, i));
+            smartReplies.putCharSequenceArrayList(key, getSmartReplies(key, i));
         }
         NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
                 interceptedKeys.toArray(new String[0]), visibilityOverrides,
                 suppressedVisualEffects, importance, explanation, overrideGroupKeys,
-                channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden);
+                channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden,
+                smartActions, smartReplies);
         return update;
     }
 
@@ -196,6 +206,37 @@
         return snooze;
     }
 
+    private ArrayList<Notification.Action> getSmartActions(String key, int index) {
+        ArrayList<Notification.Action> actions = new ArrayList<>();
+        for (int i = 0; i < index; i++) {
+            PendingIntent intent = PendingIntent.getBroadcast(
+                    getContext(),
+                    index /*requestCode*/,
+                    new Intent("ACTION_" + key),
+                    0 /*flags*/);
+            actions.add(new Notification.Action.Builder(null /*icon*/, key, intent).build());
+        }
+        return actions;
+    }
+
+    private ArrayList<CharSequence> getSmartReplies(String key, int index) {
+        ArrayList<CharSequence> choices = new ArrayList<>();
+        for (int i = 0; i < index; i++) {
+            choices.add("choice_" + key + "_" + i);
+        }
+        return choices;
+    }
+
+    private void assertActionsEqual(
+            List<Notification.Action> expecteds, List<Notification.Action> actuals) {
+        assertEquals(expecteds.size(), actuals.size());
+        for (int i = 0; i < expecteds.size(); i++) {
+            Notification.Action expected = expecteds.get(i);
+            Notification.Action actual = actuals.get(i);
+            assertEquals(expected.title, actual.title);
+        }
+    }
+
     public static class TestListenerService extends NotificationListenerService {
         private final IBinder binder = new LocalBinder();
 
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 3b21390..0cbb1aa 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -36,6 +36,10 @@
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.os.Build.VERSION_CODES.O_MR1;
 import static android.os.Build.VERSION_CODES.P;
+import static android.service.notification.NotificationListenerService.Ranking
+        .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking
+        .USER_SENTIMENT_NEUTRAL;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -50,7 +54,6 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -69,6 +72,7 @@
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
+import android.app.IUriGrantsManager;
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.UsageStatsManagerInternal;
@@ -83,7 +87,6 @@
 import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
 import android.graphics.Color;
-import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Binder;
@@ -96,7 +99,6 @@
 import android.provider.MediaStore;
 import android.provider.Settings.Secure;
 import android.service.notification.Adjustment;
-import android.service.notification.INotificationListener;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
 import android.service.notification.NotifyingApp;
@@ -109,14 +111,17 @@
 import android.text.Html;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
+import android.util.Log;
 
 import com.android.internal.R;
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.server.LocalServices;
 import com.android.server.UiServiceTestCase;
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
 import com.android.server.notification.NotificationManagerService.NotificationAssistants;
 import com.android.server.notification.NotificationManagerService.NotificationListeners;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import org.junit.After;
 import org.junit.Before;
@@ -158,6 +163,7 @@
     private TestableLooper mTestableLooper;
     @Mock
     private RankingHelper mRankingHelper;
+    @Mock private PreferencesHelper mPreferencesHelper;
     AtomicFile mPolicyFile;
     File mFile;
     @Mock
@@ -186,10 +192,15 @@
     IBinder mPermOwner;
     @Mock
     IActivityManager mAm;
+    @Mock
+    IUriGrantsManager mUgm;
+    @Mock
+    UriGrantsManagerInternal mUgmInternal;
 
     // Use a Testable subclass so we can simulate calls from the system without failing.
     private static class TestableNotificationManagerService extends NotificationManagerService {
         int countSystemChecks = 0;
+        boolean isSystemUid = true;
 
         public TestableNotificationManagerService(Context context) {
             super(context);
@@ -198,13 +209,13 @@
         @Override
         protected boolean isCallingUidSystem() {
             countSystemChecks++;
-            return true;
+            return isSystemUid;
         }
 
         @Override
         protected boolean isCallerSystemOrPhone() {
             countSystemChecks++;
-            return true;
+            return isSystemUid;
         }
 
         @Override
@@ -232,6 +243,9 @@
                 Secure.NOTIFICATION_BADGING, 1,
                 UserHandle.getUserHandleForUid(mUid).getIdentifier());
 
+        LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+        LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
+
         mService = new TestableNotificationManagerService(mContext);
 
         // Use this testable looper.
@@ -250,7 +264,7 @@
         when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class));
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
-        when(mAm.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
+        when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
 
         // write to a test file; the system file isn't readable from tests
         mFile = new File(mContext.getCacheDir(), "test.xml");
@@ -282,7 +296,7 @@
                     mListeners, mAssistants, mConditionProviders,
                     mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
                     mGroupHelper, mAm, mAppUsageStats,
-                    mock(DevicePolicyManagerInternal.class));
+                    mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal);
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
@@ -600,8 +614,8 @@
     @Test
     public void testBlockedNotifications_blockedChannelGroup() throws Exception {
         when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
-        mService.setRankingHelper(mRankingHelper);
-        when(mRankingHelper.isGroupBlocked(anyString(), anyInt(), anyString())).thenReturn(true);
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.isGroupBlocked(anyString(), anyInt(), anyString())).thenReturn(true);
 
         NotificationChannel channel = new NotificationChannel("id", "name",
                 NotificationManager.IMPORTANCE_HIGH);
@@ -639,6 +653,79 @@
         assertNull(mService.getNotificationRecord(sbn.getKey()));
     }
 
+    /**
+     * Confirm the system user on automotive devices can use car categories
+     */
+    @Test
+    public void testEnqueuedRestrictedNotifications_asSystem() throws Exception {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0))
+                .thenReturn(true);
+        List<String> categories = Arrays.asList(Notification.CATEGORY_CAR_EMERGENCY,
+                Notification.CATEGORY_CAR_WARNING,
+                Notification.CATEGORY_CAR_INFORMATION);
+        int id = 0;
+        for (String category: categories) {
+            final StatusBarNotification sbn =
+                    generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
+            sbn.getNotification().category = category;
+            mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+                    sbn.getId(), sbn.getNotification(), sbn.getUserId());
+        }
+        waitForIdle();
+        assertEquals(categories.size(), mBinderService.getActiveNotifications(PKG).length);
+    }
+
+
+    /**
+     * Confirm restricted notification categories only apply to automotive.
+     */
+    @Test
+    public void testEnqueuedRestrictedNotifications_notAutomotive() throws Exception {
+        mService.isSystemUid = false;
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0))
+                .thenReturn(false);
+        List<String> categories = Arrays.asList(Notification.CATEGORY_CAR_EMERGENCY,
+                Notification.CATEGORY_CAR_WARNING,
+                Notification.CATEGORY_CAR_INFORMATION);
+        int id = 0;
+        for (String category: categories) {
+            final StatusBarNotification sbn =
+                    generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
+            sbn.getNotification().category = category;
+            mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+                    sbn.getId(), sbn.getNotification(), sbn.getUserId());
+        }
+        waitForIdle();
+        assertEquals(categories.size(), mBinderService.getActiveNotifications(PKG).length);
+    }
+
+    /**
+     * Confirm if a non-system user tries to use the car categories on a automotive device that
+     * they will get a security exception
+     */
+    @Test
+    public void testEnqueuedRestrictedNotifications_badUser() throws Exception {
+        mService.isSystemUid = false;
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0))
+                .thenReturn(true);
+        List<String> categories = Arrays.asList(Notification.CATEGORY_CAR_EMERGENCY,
+                Notification.CATEGORY_CAR_WARNING,
+                Notification.CATEGORY_CAR_INFORMATION);
+        for (String category: categories) {
+            final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+            sbn.getNotification().category = category;
+            try {
+                mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+                        sbn.getId(), sbn.getNotification(), sbn.getUserId());
+                fail("Calls from non system apps should not allow use of restricted categories");
+            } catch (SecurityException e) {
+                // pass
+            }
+        }
+        waitForIdle();
+        assertEquals(0, mBinderService.getActiveNotifications(PKG).length);
+    }
+
     @Test
     public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception {
         mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
@@ -1222,36 +1309,36 @@
     @Test
     public void testTvExtenderChannelOverride_onTv() throws Exception {
         mService.setIsTelevision(true);
-        mService.setRankingHelper(mRankingHelper);
-        when(mRankingHelper.getNotificationChannel(
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getNotificationChannel(
                 anyString(), anyInt(), eq("foo"), anyBoolean())).thenReturn(
                         new NotificationChannel("foo", "foo", IMPORTANCE_HIGH));
 
         Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
         mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
                 generateNotificationRecord(null, tv).getNotification(), 0);
-        verify(mRankingHelper, times(1)).getNotificationChannel(
+        verify(mPreferencesHelper, times(1)).getNotificationChannel(
                 anyString(), anyInt(), eq("foo"), anyBoolean());
     }
 
     @Test
     public void testTvExtenderChannelOverride_notOnTv() throws Exception {
         mService.setIsTelevision(false);
-        mService.setRankingHelper(mRankingHelper);
-        when(mRankingHelper.getNotificationChannel(
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getNotificationChannel(
                 anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
                 mTestNotificationChannel);
 
         Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
         mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
                 generateNotificationRecord(null, tv).getNotification(), 0);
-        verify(mRankingHelper, times(1)).getNotificationChannel(
+        verify(mPreferencesHelper, times(1)).getNotificationChannel(
                 anyString(), anyInt(), eq(mTestNotificationChannel.getId()), anyBoolean());
     }
 
     @Test
     public void testUpdateAppNotifyCreatorBlock() throws Exception {
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
 
         mBinderService.setNotificationsEnabledForPackage(PKG, 0, false);
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
@@ -1265,7 +1352,7 @@
 
     @Test
     public void testUpdateAppNotifyCreatorUnblock() throws Exception {
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
 
         mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
@@ -1279,8 +1366,8 @@
 
     @Test
     public void testUpdateChannelNotifyCreatorBlock() throws Exception {
-        mService.setRankingHelper(mRankingHelper);
-        when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
                 eq(mTestNotificationChannel.getId()), anyBoolean()))
                 .thenReturn(mTestNotificationChannel);
 
@@ -1305,8 +1392,8 @@
         NotificationChannel existingChannel =
                 new NotificationChannel(mTestNotificationChannel.getId(),
                         mTestNotificationChannel.getName(), IMPORTANCE_NONE);
-        mService.setRankingHelper(mRankingHelper);
-        when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
                 eq(mTestNotificationChannel.getId()), anyBoolean()))
                 .thenReturn(existingChannel);
 
@@ -1327,8 +1414,8 @@
         NotificationChannel existingChannel =
                 new NotificationChannel(mTestNotificationChannel.getId(),
                         mTestNotificationChannel.getName(), IMPORTANCE_MAX);
-        mService.setRankingHelper(mRankingHelper);
-        when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
                 eq(mTestNotificationChannel.getId()), anyBoolean()))
                 .thenReturn(existingChannel);
 
@@ -1339,8 +1426,8 @@
     @Test
     public void testUpdateGroupNotifyCreatorBlock() throws Exception {
         NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
-        mService.setRankingHelper(mRankingHelper);
-        when(mRankingHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
                 .thenReturn(existing);
 
         NotificationChannelGroup updated = new NotificationChannelGroup("id", "name");
@@ -1362,8 +1449,8 @@
     public void testUpdateGroupNotifyCreatorUnblock() throws Exception {
         NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
         existing.setBlocked(true);
-        mService.setRankingHelper(mRankingHelper);
-        when(mRankingHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
                 .thenReturn(existing);
 
         mBinderService.updateNotificationChannelGroupForPackage(
@@ -1382,8 +1469,8 @@
     @Test
     public void testUpdateGroupNoNotifyCreatorOtherChanges() throws Exception {
         NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
-        mService.setRankingHelper(mRankingHelper);
-        when(mRankingHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
                 .thenReturn(existing);
 
         mBinderService.updateNotificationChannelGroupForPackage(
@@ -1396,12 +1483,12 @@
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
-        mService.setRankingHelper(mRankingHelper);
-        when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
                 eq(mTestNotificationChannel.getId()), anyBoolean()))
                 .thenReturn(mTestNotificationChannel);
         NotificationChannel channel2 = new NotificationChannel("a", "b", IMPORTANCE_LOW);
-        when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+        when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
                 eq(channel2.getId()), anyBoolean()))
                 .thenReturn(channel2);
 
@@ -1421,7 +1508,7 @@
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
         NotificationChannelGroup group1 = new NotificationChannelGroup("a", "b");
         NotificationChannelGroup group2 = new NotificationChannelGroup("n", "m");
 
@@ -1441,9 +1528,9 @@
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
         mTestNotificationChannel.setLightColor(Color.CYAN);
-        when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+        when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
                 eq(mTestNotificationChannel.getId()), anyBoolean()))
                 .thenReturn(mTestNotificationChannel);
 
@@ -1459,8 +1546,8 @@
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
-        mService.setRankingHelper(mRankingHelper);
-        when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
                 eq(mTestNotificationChannel.getId()), anyBoolean()))
                 .thenReturn(mTestNotificationChannel);
         reset(mListeners);
@@ -1476,8 +1563,8 @@
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
         NotificationChannelGroup ncg = new NotificationChannelGroup("a", "b/c");
-        mService.setRankingHelper(mRankingHelper);
-        when(mRankingHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt()))
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt()))
                 .thenReturn(ncg);
         reset(mListeners);
         mBinderService.deleteNotificationChannelGroup(PKG, ncg.getId());
@@ -1488,18 +1575,18 @@
 
     @Test
     public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception {
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
-        when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+        when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
                 eq(mTestNotificationChannel.getId()), anyBoolean()))
                 .thenReturn(mTestNotificationChannel);
 
         mBinderService.updateNotificationChannelFromPrivilegedListener(
                 null, PKG, Process.myUserHandle(), mTestNotificationChannel);
 
-        verify(mRankingHelper, times(1)).updateNotificationChannel(
+        verify(mPreferencesHelper, times(1)).updateNotificationChannel(
                 anyString(), anyInt(), any(), anyBoolean());
 
         verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG),
@@ -1509,7 +1596,7 @@
 
     @Test
     public void testUpdateNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
         List<String> associations = new ArrayList<>();
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
 
@@ -1521,7 +1608,7 @@
             // pass
         }
 
-        verify(mRankingHelper, never()).updateNotificationChannel(
+        verify(mPreferencesHelper, never()).updateNotificationChannel(
                 anyString(), anyInt(), any(), anyBoolean());
 
         verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG),
@@ -1531,7 +1618,7 @@
 
     @Test
     public void testUpdateNotificationChannelFromPrivilegedListener_badUser() throws Exception {
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1548,7 +1635,7 @@
             // pass
         }
 
-        verify(mRankingHelper, never()).updateNotificationChannel(
+        verify(mPreferencesHelper, never()).updateNotificationChannel(
                 anyString(), anyInt(), any(), anyBoolean());
 
         verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG),
@@ -1558,7 +1645,7 @@
 
     @Test
     public void testGetNotificationChannelFromPrivilegedListener_success() throws Exception {
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1566,13 +1653,13 @@
         mBinderService.getNotificationChannelsFromPrivilegedListener(
                 null, PKG, Process.myUserHandle());
 
-        verify(mRankingHelper, times(1)).getNotificationChannels(
+        verify(mPreferencesHelper, times(1)).getNotificationChannels(
                 anyString(), anyInt(), anyBoolean());
     }
 
     @Test
     public void testGetNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
         List<String> associations = new ArrayList<>();
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
 
@@ -1584,13 +1671,13 @@
             // pass
         }
 
-        verify(mRankingHelper, never()).getNotificationChannels(
+        verify(mPreferencesHelper, never()).getNotificationChannels(
                 anyString(), anyInt(), anyBoolean());
     }
 
     @Test
     public void testGetNotificationChannelFromPrivilegedListener_badUser() throws Exception {
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1606,13 +1693,13 @@
             // pass
         }
 
-        verify(mRankingHelper, never()).getNotificationChannels(
+        verify(mPreferencesHelper, never()).getNotificationChannels(
                 anyString(), anyInt(), anyBoolean());
     }
 
     @Test
     public void testGetNotificationChannelGroupsFromPrivilegedListener_success() throws Exception {
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1620,12 +1707,12 @@
         mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
                 null, PKG, Process.myUserHandle());
 
-        verify(mRankingHelper, times(1)).getNotificationChannelGroups(anyString(), anyInt());
+        verify(mPreferencesHelper, times(1)).getNotificationChannelGroups(anyString(), anyInt());
     }
 
     @Test
     public void testGetNotificationChannelGroupsFromPrivilegedListener_noAccess() throws Exception {
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
         List<String> associations = new ArrayList<>();
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
 
@@ -1637,12 +1724,12 @@
             // pass
         }
 
-        verify(mRankingHelper, never()).getNotificationChannelGroups(anyString(), anyInt());
+        verify(mPreferencesHelper, never()).getNotificationChannelGroups(anyString(), anyInt());
     }
 
     @Test
     public void testGetNotificationChannelGroupsFromPrivilegedListener_badUser() throws Exception {
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
         List<String> associations = new ArrayList<>();
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
         mListener = mock(ManagedServices.ManagedServiceInfo.class);
@@ -1657,7 +1744,7 @@
             // pass
         }
 
-        verify(mRankingHelper, never()).getNotificationChannelGroups(anyString(), anyInt());
+        verify(mPreferencesHelper, never()).getNotificationChannelGroups(anyString(), anyInt());
     }
 
     @Test
@@ -2182,7 +2269,7 @@
 
     @Test
     public void testHandleRankingSort_sendsUpdateOnSignalExtractorChange() throws Exception {
-        mService.setRankingHelper(mRankingHelper);
+        mService.setPreferencesHelper(mPreferencesHelper);
         NotificationManagerService.WorkerHandler handler = mock(
                 NotificationManagerService.WorkerHandler.class);
         mService.setHandler(handler);
@@ -2385,16 +2472,81 @@
     }
 
     @Test
-    public void testUserSentimentChangeTriggersUpdate() throws Exception {
+    public void testApplyAdjustmentMultiUser() throws Exception {
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         mService.addNotification(r);
         NotificationManagerService.WorkerHandler handler = mock(
                 NotificationManagerService.WorkerHandler.class);
         mService.setHandler(handler);
 
+        when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(false);
+
         Bundle signals = new Bundle();
         signals.putInt(Adjustment.KEY_USER_SENTIMENT,
-                NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+                USER_SENTIMENT_NEGATIVE);
+        Adjustment adjustment = new Adjustment(
+                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+
+        waitForIdle();
+
+        verify(handler, timeout(300).times(0)).scheduleSendRankingUpdate();
+    }
+
+    @Test
+    public void testApplyEnqueuedAdjustmentFromAssistant_singleUser() throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addEnqueuedNotification(r);
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mService.setHandler(handler);
+        when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
+
+        Bundle signals = new Bundle();
+        signals.putInt(Adjustment.KEY_USER_SENTIMENT,
+                USER_SENTIMENT_NEGATIVE);
+        Adjustment adjustment = new Adjustment(
+                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+        mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+
+        assertEquals(USER_SENTIMENT_NEGATIVE, r.getUserSentiment());
+    }
+
+    @Test
+    public void testApplyEnqueuedAdjustmentFromAssistant_crossUser() throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addEnqueuedNotification(r);
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mService.setHandler(handler);
+        when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(false);
+
+        Bundle signals = new Bundle();
+        signals.putInt(Adjustment.KEY_USER_SENTIMENT,
+                USER_SENTIMENT_NEGATIVE);
+        Adjustment adjustment = new Adjustment(
+                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+        mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+
+        assertEquals(USER_SENTIMENT_NEUTRAL, r.getUserSentiment());
+
+        waitForIdle();
+
+        verify(handler, timeout(300).times(0)).scheduleSendRankingUpdate();
+    }
+
+    @Test
+    public void testUserSentimentChangeTriggersUpdate() throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mService.setHandler(handler);
+        when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
+
+        Bundle signals = new Bundle();
+        signals.putInt(Adjustment.KEY_USER_SENTIMENT,
+                USER_SENTIMENT_NEGATIVE);
         Adjustment adjustment = new Adjustment(
                 r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
         mBinderService.applyAdjustmentFromAssistant(null, adjustment);
@@ -2405,6 +2557,47 @@
     }
 
     @Test
+    public void testTooLateAdjustmentTriggersUpdate() throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mService.setHandler(handler);
+        when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
+
+        Bundle signals = new Bundle();
+        signals.putInt(Adjustment.KEY_USER_SENTIMENT,
+                USER_SENTIMENT_NEGATIVE);
+        Adjustment adjustment = new Adjustment(
+                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+        mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+
+        waitForIdle();
+
+        verify(handler, timeout(300).times(1)).scheduleSendRankingUpdate();
+    }
+
+    @Test
+    public void testEnqueuedAdjustmentAppliesAdjustments() throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addEnqueuedNotification(r);
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mService.setHandler(handler);
+        when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
+
+        Bundle signals = new Bundle();
+        signals.putInt(Adjustment.KEY_USER_SENTIMENT,
+                USER_SENTIMENT_NEGATIVE);
+        Adjustment adjustment = new Adjustment(
+                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+        mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+
+        assertEquals(USER_SENTIMENT_NEGATIVE,
+                r.getUserSentiment());
+    }
+
+    @Test
     public void testRecents() throws Exception {
         Set<NotifyingApp> expected = new HashSet<>();
 
@@ -2538,13 +2731,14 @@
                 PKG, PKG, 0, "tag", mUid, 0, nbA.build(), new UserHandle(mUid), null, 0), c);
 
         // First post means we grant access to both
-        reset(mAm);
-        when(mAm.newUriPermissionOwner(any())).thenReturn(new Binder());
+        reset(mUgm);
+        reset(mUgmInternal);
+        when(mUgmInternal.newUriPermissionOwner(any())).thenReturn(new Binder());
         mService.updateUriPermissions(recordA, null, mContext.getPackageName(),
                 UserHandle.USER_SYSTEM);
-        verify(mAm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
+        verify(mUgm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
                 eq(message1.getDataUri()), anyInt(), anyInt(), anyInt());
-        verify(mAm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
+        verify(mUgm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
                 eq(message2.getDataUri()), anyInt(), anyInt(), anyInt());
 
         Notification.Builder nbB = new Notification.Builder(mContext, c.getId())
@@ -2555,24 +2749,24 @@
                 PKG, 0, "tag", mUid, 0, nbB.build(), new UserHandle(mUid), null, 0), c);
 
         // Update means we drop access to first
-        reset(mAm);
+        reset(mUgmInternal);
         mService.updateUriPermissions(recordB, recordA, mContext.getPackageName(),
                 UserHandle.USER_SYSTEM);
-        verify(mAm, times(1)).revokeUriPermissionFromOwner(any(), eq(message1.getDataUri()),
-                anyInt(), anyInt());
+        verify(mUgmInternal, times(1)).revokeUriPermissionFromOwner(any(),
+                eq(message1.getDataUri()), anyInt(), anyInt());
 
         // Update back means we grant access to first again
-        reset(mAm);
+        reset(mUgm);
         mService.updateUriPermissions(recordA, recordB, mContext.getPackageName(),
                 UserHandle.USER_SYSTEM);
-        verify(mAm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
+        verify(mUgm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
                 eq(message1.getDataUri()), anyInt(), anyInt(), anyInt());
 
         // And update to empty means we drop everything
-        reset(mAm);
+        reset(mUgmInternal);
         mService.updateUriPermissions(null, recordB, mContext.getPackageName(),
                 UserHandle.USER_SYSTEM);
-        verify(mAm, times(1)).revokeUriPermissionFromOwner(any(), eq(null),
+        verify(mUgmInternal, times(1)).revokeUriPermissionFromOwner(any(), eq(null),
                 anyInt(), anyInt());
     }
 
@@ -2964,6 +3158,20 @@
     }
 
     @Test
+    public void testVisualDifference_summaryNewNotification() {
+        Notification.Builder nb2 = new Notification.Builder(mContext, "")
+                .setGroup("bananas")
+                .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
+                .setContentText("bar");
+        StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb2.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r2 =
+                new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+        assertFalse(mService.isVisuallyInterruptive(null, r2));
+    }
+
+    @Test
     public void testHideAndUnhideNotificationsOnSuspendedPackageBroadcast() {
         // post 2 notification from this package
         final NotificationRecord notif1 = generateNotificationRecord(
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 c1868e7..6be633e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -29,9 +29,15 @@
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.IActivityManager;
 import android.app.Notification;
 import android.app.Notification.Builder;
 import android.app.NotificationChannel;
@@ -40,10 +46,12 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Color;
+import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
 import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.Adjustment;
@@ -51,23 +59,25 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.internal.R;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.server.UiServiceTestCase;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
 import java.util.Objects;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class NotificationRecordTest extends UiServiceTestCase {
 
-    private final Context mMockContext = Mockito.mock(Context.class);
+    private final Context mMockContext = mock(Context.class);
     @Mock PackageManager mPm;
 
     private final String pkg = PKG_N_MR1;
@@ -93,8 +103,6 @@
             new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "test",
                     NotificationManager.IMPORTANCE_UNSPECIFIED);
     private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
-    final ApplicationInfo legacy = new ApplicationInfo();
-    final ApplicationInfo upgrade = new ApplicationInfo();
 
     private static final long[] CUSTOM_VIBRATION = new long[] {
             300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
@@ -115,12 +123,12 @@
 
         when(mMockContext.getResources()).thenReturn(getContext().getResources());
         when(mMockContext.getPackageManager()).thenReturn(mPm);
+        when(mMockContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
     }
 
-    private StatusBarNotification getNotification(boolean preO, boolean noisy, boolean defaultSound,
+    private StatusBarNotification getNotification(String pkg, boolean noisy, boolean defaultSound,
             boolean buzzy, boolean defaultVibration, boolean lights, boolean defaultLights,
             String group) {
-        when(mMockContext.getApplicationInfo()).thenReturn(preO ? legacy : upgrade);
         final Builder builder = new Builder(mMockContext)
                 .setContentTitle("foo")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -159,22 +167,14 @@
         }
 
         builder.setDefaults(defaults);
-        if (!preO) {
-            builder.setChannelId(channelId);
-        }
+        builder.setChannelId(channelId);
 
         if(group != null) {
             builder.setGroup(group);
         }
 
         Notification n = builder.build();
-        if (preO) {
-            return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n,
-                    mUser, null, uid);
-        } else {
-            return new StatusBarNotification(pkg2, pkg2, id2, tag2, uid2, uid2, n,
-                    mUser, null, uid2);
-        }
+        return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
     }
 
     //
@@ -185,7 +185,7 @@
     public void testSound_default_preUpgradeUsesNotification() throws Exception {
         defaultChannel.setSound(null, null);
         // pre upgrade, default sound.
-        StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
 
@@ -198,7 +198,7 @@
     public void testSound_custom_preUpgradeUsesNotification() throws Exception {
         defaultChannel.setSound(null, null);
         // pre upgrade, custom sound.
-        StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
                 false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
 
@@ -212,7 +212,7 @@
         defaultChannel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
         defaultChannel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
         // pre upgrade, default sound.
-        StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
 
@@ -224,7 +224,7 @@
     @Test
     public void testSound_noSound_preUpgrade() throws Exception {
         // pre upgrade, default sound.
-        StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, false /* noisy */,
                 false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
 
@@ -237,7 +237,7 @@
     public void testSound_default_upgradeUsesChannel() throws Exception {
         channel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
         // post upgrade, default sound.
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
 
@@ -250,7 +250,7 @@
     public void testVibration_default_preUpgradeUsesNotification() throws Exception {
         defaultChannel.enableVibration(false);
         // pre upgrade, default vibration.
-        StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, false /* noisy */,
                 false /* defaultSound */, true /* buzzy */, true /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
 
@@ -262,7 +262,7 @@
     public void testVibration_custom_preUpgradeUsesNotification() throws Exception {
         defaultChannel.enableVibration(false);
         // pre upgrade, custom vibration.
-        StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, false /* noisy */,
                 false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
 
@@ -275,7 +275,7 @@
         defaultChannel.enableVibration(true);
         defaultChannel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
         // pre upgrade, custom vibration.
-        StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, false /* noisy */,
                 false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
 
@@ -287,7 +287,7 @@
     public void testVibration_custom_upgradeUsesChannel() throws Exception {
         channel.enableVibration(true);
         // post upgrade, custom vibration.
-        StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_O, false /* noisy */,
                 false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
 
@@ -297,7 +297,7 @@
 
     @Test
     public void testImportance_preUpgrade() throws Exception {
-        StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
@@ -308,7 +308,7 @@
     public void testImportance_locked_preUpgrade() throws Exception {
         defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         defaultChannel.lockFields(USER_LOCKED_IMPORTANCE);
-        StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
 
@@ -320,7 +320,7 @@
     public void testImportance_locked_unspecified_preUpgrade() throws Exception {
         defaultChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED);
         defaultChannel.lockFields(USER_LOCKED_IMPORTANCE);
-        StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
 
@@ -330,7 +330,7 @@
 
     @Test
     public void testImportance_upgrade() throws Exception {
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
@@ -339,7 +339,7 @@
 
     @Test
     public void testLights_preUpgrade_noLight() throws Exception {
-        StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
@@ -349,7 +349,7 @@
 
     @Test
     public void testLights_preUpgrade() throws Exception {
-        StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 true /* lights */, false /* defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
@@ -360,7 +360,7 @@
     public void testLights_locked_preUpgrade() throws Exception {
         defaultChannel.enableLights(true);
         defaultChannel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
-        StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 true /* lights */, false /* defaultLights */, null /* group */);
 
@@ -379,7 +379,7 @@
 
         NotificationRecord.Light expected = new NotificationRecord.Light(
                 defaultLightColor, defaultLightOn, defaultLightOff);
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 true /* lights */, true /* defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
@@ -395,7 +395,7 @@
 
         NotificationRecord.Light expected = new NotificationRecord.Light(
                 Color.BLUE, defaultLightOn, defaultLightOff);
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 true /* lights */, false /* defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
@@ -404,7 +404,7 @@
 
     @Test
     public void testLights_upgrade_noLight() throws Exception {
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
@@ -413,7 +413,7 @@
 
     @Test
     public void testLogmakerShortChannel() throws Exception {
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
@@ -426,7 +426,7 @@
 
     @Test
     public void testLogmakerLongChannel() throws Exception {
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
         true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
         false /* lights */, false /*defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channelLongId);
@@ -437,7 +437,7 @@
 
     @Test
     public void testLogmakerNoGroup() throws Exception {
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /*defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
@@ -446,7 +446,7 @@
 
     @Test
     public void testLogmakerShortGroup() throws Exception {
-        StatusBarNotification sbn = getNotification(false /*reO */, true /* noisy */,
+        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);
@@ -456,7 +456,7 @@
 
     @Test
     public void testLogmakerLongGroup() throws Exception {
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, groupIdLong /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
@@ -467,7 +467,7 @@
 
     @Test
     public void testLogmakerOverrideGroup() throws Exception {
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        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);
@@ -483,7 +483,7 @@
 
     @Test
     public void testNotificationStats() throws Exception {
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        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);
@@ -526,7 +526,7 @@
 
     @Test
     public void testUserSentiment() throws Exception {
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        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);
@@ -544,7 +544,7 @@
 
     @Test
     public void testUserSentiment_appImportanceUpdatesSentiment() throws Exception {
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        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);
@@ -556,7 +556,7 @@
 
     @Test
     public void testUserSentiment_appImportanceBlocksNegativeSentimentUpdate() throws Exception {
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        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);
@@ -573,7 +573,7 @@
     @Test
     public void testUserSentiment_userLocked() throws Exception {
         channel.lockFields(USER_LOCKED_IMPORTANCE);
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        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);
@@ -591,7 +591,7 @@
 
     @Test
     public void testAppImportance_returnsCorrectly() throws Exception {
-        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+        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);
@@ -605,7 +605,7 @@
 
     @Test
     public void testIsInterruptive_textChanged_notSeen() {
-        StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_O, false /* noisy */,
                 false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
@@ -618,7 +618,7 @@
 
     @Test
     public void testIsInterruptive_textChanged_seen() {
-        StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_O, false /* noisy */,
                 false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
@@ -632,7 +632,7 @@
 
     @Test
     public void testIsInterruptive_textNotChanged_seen() {
-        StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */,
+        StatusBarNotification sbn = getNotification(PKG_O, false /* noisy */,
                 false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
@@ -643,4 +643,79 @@
         record.setSeen();
         assertEquals(false, record.isInterruptive());
     }
+
+    @Test
+    public void testCalculateGrantableUris_PappProvided() throws RemoteException {
+        IActivityManager am = mock(IActivityManager.class);
+        UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
+        when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
+                anyInt(), anyInt())).thenThrow(new SecurityException());
+
+        Notification n = mock(Notification.class);
+        when(n.getChannelId()).thenReturn(channel.getId());
+        StatusBarNotification sbn =
+                new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        record.mAm = am;
+        record.mUgmInternal = ugm;
+
+        try {
+            record.calculateGrantableUris();
+            fail("App provided uri for p targeting app should throw exception");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testCalculateGrantableUris_PuserOverridden() throws RemoteException {
+        IActivityManager am = mock(IActivityManager.class);
+        UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
+        when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
+                anyInt(), anyInt())).thenThrow(new SecurityException());
+
+        channel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
+        Notification n = mock(Notification.class);
+        when(n.getChannelId()).thenReturn(channel.getId());
+        StatusBarNotification sbn =
+                new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        record.mAm = am;
+
+        record.calculateGrantableUris();
+    }
+
+    @Test
+    public void testCalculateGrantableUris_prePappProvided() throws RemoteException {
+        IActivityManager am = mock(IActivityManager.class);
+        UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
+        when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
+                anyInt(), anyInt())).thenThrow(new SecurityException());
+
+        Notification n = mock(Notification.class);
+        when(n.getChannelId()).thenReturn(channel.getId());
+        StatusBarNotification sbn =
+                new StatusBarNotification(PKG_O, PKG_O, id1, tag1, uid, uid, n, mUser, null, uid);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        record.mAm = am;
+
+        record.calculateGrantableUris();
+        // should not throw
+    }
+
+    @Test
+    public void testSmartActions() {
+        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);
+        assertNull(record.getSmartActions());
+
+        ArrayList<Notification.Action> smartActions = new ArrayList<>();
+        smartActions.add(new Notification.Action.Builder(
+                Icon.createWithResource(getContext(), R.drawable.btn_default),
+                "text", null).build());
+        record.setSmartActions(smartActions);
+        assertEquals(smartActions, record.getSmartActions());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
new file mode 100644
index 0000000..73adf25
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -0,0 +1,1790 @@
+/*
+ * 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 android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MAX;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.fail;
+
+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.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.IContentProvider;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.media.AudioAttributes;
+import android.net.Uri;
+import android.os.Build;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContentResolver;
+import android.util.ArrayMap;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.server.UiServiceTestCase;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+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;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ThreadLocalRandom;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PreferencesHelperTest extends UiServiceTestCase {
+    private static final int UID_N_MR1 = 0;
+    private static final UserHandle USER = UserHandle.of(0);
+    private static final int UID_O = 1111;
+    private static final String SYSTEM_PKG = "android";
+    private static final int SYSTEM_UID= 1000;
+    private static final UserHandle USER2 = UserHandle.of(10);
+    private static final String TEST_CHANNEL_ID = "test_channel_id";
+    private static final String TEST_AUTHORITY = "test";
+    private static final Uri SOUND_URI =
+            Uri.parse("content://" + TEST_AUTHORITY + "/internal/audio/media/10");
+    private static final Uri CANONICAL_SOUND_URI =
+            Uri.parse("content://" + TEST_AUTHORITY
+                    + "/internal/audio/media/10?title=Test&canonical=1");
+
+    @Mock NotificationUsageStats mUsageStats;
+    @Mock RankingHandler mHandler;
+    @Mock PackageManager mPm;
+    @Mock IContentProvider mTestIContentProvider;
+    @Mock Context mContext;
+    @Mock ZenModeHelper mMockZenModeHelper;
+
+    private NotificationManager.Policy mTestNotificationPolicy;
+
+    private PreferencesHelper mHelper;
+    private AudioAttributes mAudioAttributes;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        UserHandle user = UserHandle.ALL;
+
+        final ApplicationInfo legacy = new ApplicationInfo();
+        legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+        final ApplicationInfo upgrade = new ApplicationInfo();
+        upgrade.targetSdkVersion = Build.VERSION_CODES.O;
+        when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy);
+        when(mPm.getApplicationInfoAsUser(eq(PKG_O), anyInt(), anyInt())).thenReturn(upgrade);
+        when(mPm.getApplicationInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(upgrade);
+        when(mPm.getPackageUidAsUser(eq(PKG_N_MR1), anyInt())).thenReturn(UID_N_MR1);
+        when(mPm.getPackageUidAsUser(eq(PKG_O), anyInt())).thenReturn(UID_O);
+        when(mPm.getPackageUidAsUser(eq(SYSTEM_PKG), anyInt())).thenReturn(SYSTEM_UID);
+        PackageInfo info = mock(PackageInfo.class);
+        info.signatures = new Signature[] {mock(Signature.class)};
+        when(mPm.getPackageInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(info);
+        when(mPm.getPackageInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt()))
+                .thenReturn(mock(PackageInfo.class));
+        when(mContext.getResources()).thenReturn(
+                InstrumentationRegistry.getContext().getResources());
+        when(mContext.getContentResolver()).thenReturn(
+                InstrumentationRegistry.getContext().getContentResolver());
+        when(mContext.getPackageManager()).thenReturn(mPm);
+        when(mContext.getApplicationInfo()).thenReturn(legacy);
+        // most tests assume badging is enabled
+        TestableContentResolver contentResolver = getContext().getContentResolver();
+        contentResolver.setFallbackToExisting(false);
+        Secure.putIntForUser(contentResolver,
+                Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID_N_MR1));
+
+        ContentProvider testContentProvider = mock(ContentProvider.class);
+        when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
+        contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
+
+        when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI)))
+                .thenReturn(CANONICAL_SOUND_URI);
+        when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+                .thenReturn(CANONICAL_SOUND_URI);
+        when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+                .thenReturn(SOUND_URI);
+
+        mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
+                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
+        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+        resetZenModeHelper();
+
+        mAudioAttributes = new AudioAttributes.Builder()
+                .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
+                .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+                .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
+                .build();
+    }
+
+    private NotificationChannel getDefaultChannel() {
+        return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
+                IMPORTANCE_LOW);
+    }
+
+    private ByteArrayOutputStream writeXmlAndPurge(String pkg, int uid, boolean forBackup,
+            String... channelIds)
+            throws Exception {
+        XmlSerializer serializer = new FastXmlSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        serializer.startDocument(null, true);
+        mHelper.writeXml(serializer, forBackup);
+        serializer.endDocument();
+        serializer.flush();
+        for (String channelId : channelIds) {
+            mHelper.permanentlyDeleteNotificationChannel(pkg, uid, channelId);
+        }
+        return baos;
+    }
+
+    private void loadStreamXml(ByteArrayOutputStream stream, boolean forRestore) throws Exception {
+        loadByteArrayXml(stream.toByteArray(), forRestore);
+    }
+
+    private void loadByteArrayXml(byte[] byteArray, boolean forRestore) throws Exception {
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
+        parser.nextTag();
+        mHelper.readXml(parser, forRestore);
+    }
+
+    private void compareChannels(NotificationChannel expected, NotificationChannel actual) {
+        assertEquals(expected.getId(), actual.getId());
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getDescription(), actual.getDescription());
+        assertEquals(expected.shouldVibrate(), actual.shouldVibrate());
+        assertEquals(expected.shouldShowLights(), actual.shouldShowLights());
+        assertEquals(expected.getImportance(), actual.getImportance());
+        assertEquals(expected.getLockscreenVisibility(), actual.getLockscreenVisibility());
+        assertEquals(expected.getSound(), actual.getSound());
+        assertEquals(expected.canBypassDnd(), actual.canBypassDnd());
+        assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern()));
+        assertEquals(expected.getGroup(), actual.getGroup());
+        assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes());
+        assertEquals(expected.getLightColor(), actual.getLightColor());
+    }
+
+    private void compareGroups(NotificationChannelGroup expected, NotificationChannelGroup actual) {
+        assertEquals(expected.getId(), actual.getId());
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getDescription(), actual.getDescription());
+        assertEquals(expected.isBlocked(), actual.isBlocked());
+    }
+
+    private NotificationChannel getChannel() {
+        return new NotificationChannel("id", "name", IMPORTANCE_LOW);
+    }
+
+    private NotificationChannel findChannel(List<NotificationChannel> channels, String id) {
+        for (NotificationChannel channel : channels) {
+            if (channel.getId().equals(id)) {
+                return channel;
+            }
+        }
+        return null;
+    }
+
+    private void resetZenModeHelper() {
+        reset(mMockZenModeHelper);
+        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+    }
+
+    @Test
+    public void testChannelXml() throws Exception {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
+        ncg.setBlocked(true);
+        ncg.setDescription("group desc");
+        NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        NotificationChannel channel2 =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel2.setDescription("descriptions for all");
+        channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+        channel2.enableLights(true);
+        channel2.setBypassDnd(true);
+        channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+        channel2.enableVibration(true);
+        channel2.setGroup(ncg.getId());
+        channel2.setVibrationPattern(new long[]{100, 67, 145, 156});
+        channel2.setLightColor(Color.BLUE);
+
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
+
+        mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true);
+        mHelper.setAppImportanceLocked(PKG_N_MR1, UID_N_MR1);
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, channel1.getId(),
+                channel2.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
+        mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1}, new int[]{
+                UID_N_MR1});
+
+        loadStreamXml(baos, false);
+
+        assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+        assertTrue(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
+        assertEquals(channel1, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
+        compareChannels(channel2,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
+
+        List<NotificationChannelGroup> actualGroups =
+                mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, false, true).getList();
+        boolean foundNcg = false;
+        for (NotificationChannelGroup actual : actualGroups) {
+            if (ncg.getId().equals(actual.getId())) {
+                foundNcg = true;
+                compareGroups(ncg, actual);
+            } else if (ncg2.getId().equals(actual.getId())) {
+                compareGroups(ncg2, actual);
+            }
+        }
+        assertTrue(foundNcg);
+
+        boolean foundChannel2Group = false;
+        for (NotificationChannelGroup actual : actualGroups) {
+            if (channel2.getGroup().equals(actual.getChannels().get(0).getGroup())) {
+                foundChannel2Group = true;
+                break;
+            }
+        }
+        assertTrue(foundChannel2Group);
+    }
+
+    @Test
+    public void testChannelXmlForBackup() throws Exception {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
+        NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        NotificationChannel channel2 =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel2.setDescription("descriptions for all");
+        channel2.setSound(SOUND_URI, mAudioAttributes);
+        channel2.enableLights(true);
+        channel2.setBypassDnd(true);
+        channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+        channel2.enableVibration(false);
+        channel2.setGroup(ncg.getId());
+        channel2.setLightColor(Color.BLUE);
+        NotificationChannel channel3 = new NotificationChannel("id3", "NAM3", IMPORTANCE_HIGH);
+        channel3.enableVibration(true);
+
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false);
+        mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
+
+        mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true);
+
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE);
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel1.getId(),
+                channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
+        mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1, PKG_O},
+                new int[]{UID_N_MR1, UID_O});
+
+        mHelper.setShowBadge(PKG_O, UID_O, true);
+
+        loadStreamXml(baos, true);
+
+        assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_O, UID_O));
+        assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+        assertEquals(channel1, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
+        compareChannels(channel2,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
+        compareChannels(channel3,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
+
+        List<NotificationChannelGroup> actualGroups =
+                mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, false, true).getList();
+        boolean foundNcg = false;
+        for (NotificationChannelGroup actual : actualGroups) {
+            if (ncg.getId().equals(actual.getId())) {
+                foundNcg = true;
+                compareGroups(ncg, actual);
+            } else if (ncg2.getId().equals(actual.getId())) {
+                compareGroups(ncg2, actual);
+            }
+        }
+        assertTrue(foundNcg);
+
+        boolean foundChannel2Group = false;
+        for (NotificationChannelGroup actual : actualGroups) {
+            if (channel2.getGroup().equals(actual.getChannels().get(0).getGroup())) {
+                foundChannel2Group = true;
+                break;
+            }
+        }
+        assertTrue(foundChannel2Group);
+    }
+
+    @Test
+    public void testBackupXml_backupCanonicalizedSoundUri() throws Exception {
+        NotificationChannel channel =
+                new NotificationChannel("id", "name", IMPORTANCE_LOW);
+        channel.setSound(SOUND_URI, mAudioAttributes);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId());
+
+        // Testing that in restore we are given the canonical version
+        loadStreamXml(baos, true);
+        verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
+    }
+
+    @Test
+    public void testRestoreXml_withExistentCanonicalizedSoundUri() throws Exception {
+        Uri localUri = Uri.parse("content://" + TEST_AUTHORITY + "/local/url");
+        Uri canonicalBasedOnLocal = localUri.buildUpon()
+                .appendQueryParameter("title", "Test")
+                .appendQueryParameter("canonical", "1")
+                .build();
+        when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+                .thenReturn(canonicalBasedOnLocal);
+        when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+                .thenReturn(localUri);
+        when(mTestIContentProvider.uncanonicalize(any(), eq(canonicalBasedOnLocal)))
+                .thenReturn(localUri);
+
+        NotificationChannel channel =
+                new NotificationChannel("id", "name", IMPORTANCE_LOW);
+        channel.setSound(SOUND_URI, mAudioAttributes);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId());
+
+        loadStreamXml(baos, true);
+
+        NotificationChannel actualChannel = mHelper.getNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, channel.getId(), false);
+        assertEquals(localUri, actualChannel.getSound());
+    }
+
+    @Test
+    public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
+        Thread.sleep(3000);
+        when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+                .thenReturn(null);
+        when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+                .thenReturn(null);
+
+        NotificationChannel channel =
+                new NotificationChannel("id", "name", IMPORTANCE_LOW);
+        channel.setSound(SOUND_URI, mAudioAttributes);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId());
+
+        loadStreamXml(baos, true);
+
+        NotificationChannel actualChannel = mHelper.getNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, channel.getId(), false);
+        assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
+    }
+
+
+    /**
+     * Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to
+     * handle its restore properly.
+     */
+    @Test
+    public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
+        // Not a local uncanonicalized uri, simulating that it fails to exist locally
+        when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null);
+        String id = "id";
+        String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
+                + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
+                + "<channel id=\"" + id + "\" name=\"name\" importance=\"2\" "
+                + "sound=\"" + SOUND_URI + "\" "
+                + "usage=\"6\" content_type=\"0\" flags=\"1\" show_badge=\"true\" />\n"
+                + "<channel id=\"miscellaneous\" name=\"Uncategorized\" usage=\"5\" "
+                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+                + "</package>\n"
+                + "</ranking>\n";
+
+        loadByteArrayXml(backupWithUncanonicalizedSoundUri.getBytes(), true);
+
+        NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, id, false);
+        assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
+    }
+
+    @Test
+    public void testBackupRestoreXml_withNullSoundUri() throws Exception {
+        NotificationChannel channel =
+                new NotificationChannel("id", "name", IMPORTANCE_LOW);
+        channel.setSound(null, mAudioAttributes);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId());
+
+        loadStreamXml(baos, true);
+
+        NotificationChannel actualChannel = mHelper.getNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, channel.getId(), false);
+        assertEquals(null, actualChannel.getSound());
+    }
+
+    @Test
+    public void testChannelXml_backup() throws Exception {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
+        NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        NotificationChannel channel2 =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        NotificationChannel channel3 =
+                new NotificationChannel("id3", "name3", IMPORTANCE_LOW);
+        channel3.setGroup(ncg.getId());
+
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false);
+
+        mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId());
+        mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg.getId());
+        assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel1.getId(),
+                channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
+        mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1}, new int[]{
+                UID_N_MR1});
+
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
+                null);
+        parser.nextTag();
+        mHelper.readXml(parser, true);
+
+        assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
+        assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
+        assertNull(mHelper.getNotificationChannelGroup(ncg.getId(), PKG_N_MR1, UID_N_MR1));
+        //assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG_N_MR1, UID_N_MR1));
+        assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
+    }
+
+    @Test
+    public void testChannelXml_defaultChannelLegacyApp_noUserSettings() throws Exception {
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
+                NotificationChannel.DEFAULT_CHANNEL_ID);
+
+        loadStreamXml(baos, false);
+
+        final NotificationChannel updated = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1,
+                NotificationChannel.DEFAULT_CHANNEL_ID, false);
+        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, updated.getImportance());
+        assertFalse(updated.canBypassDnd());
+        assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE, updated.getLockscreenVisibility());
+        assertEquals(0, updated.getUserLockedFields());
+    }
+
+    @Test
+    public void testChannelXml_defaultChannelUpdatedApp_userSettings() throws Exception {
+        final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1,
+                UID_N_MR1,
+                NotificationChannel.DEFAULT_CHANNEL_ID, false);
+        defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
+                NotificationChannel.DEFAULT_CHANNEL_ID);
+
+        loadStreamXml(baos, false);
+
+        assertEquals(NotificationManager.IMPORTANCE_LOW, mHelper.getNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance());
+    }
+
+    @Test
+    public void testChannelXml_upgradeCreateDefaultChannel() throws Exception {
+        final String preupgradeXml = "<ranking version=\"1\">\n"
+                + "<package name=\"" + PKG_N_MR1
+                + "\" importance=\"" + NotificationManager.IMPORTANCE_HIGH
+                + "\" priority=\"" + Notification.PRIORITY_MAX + "\" visibility=\""
+                + Notification.VISIBILITY_SECRET + "\"" +" uid=\"" + UID_N_MR1 + "\" />\n"
+                + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" visibility=\""
+                + Notification.VISIBILITY_PRIVATE + "\" />\n"
+                + "</ranking>";
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())),
+                null);
+        parser.nextTag();
+        mHelper.readXml(parser, false);
+
+        final NotificationChannel updated1 =
+            mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+        assertEquals(NotificationManager.IMPORTANCE_HIGH, updated1.getImportance());
+        assertTrue(updated1.canBypassDnd());
+        assertEquals(Notification.VISIBILITY_SECRET, updated1.getLockscreenVisibility());
+        assertEquals(NotificationChannel.USER_LOCKED_IMPORTANCE
+                | NotificationChannel.USER_LOCKED_PRIORITY
+                | NotificationChannel.USER_LOCKED_VISIBILITY,
+                updated1.getUserLockedFields());
+
+        // No Default Channel created for updated packages
+        assertEquals(null, mHelper.getNotificationChannel(PKG_O, UID_O,
+                NotificationChannel.DEFAULT_CHANNEL_ID, false));
+    }
+
+    @Test
+    public void testChannelXml_upgradeDeletesDefaultChannel() throws Exception {
+        final NotificationChannel defaultChannel = mHelper.getNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+        assertTrue(defaultChannel != null);
+        ByteArrayOutputStream baos =
+                writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, NotificationChannel.DEFAULT_CHANNEL_ID);
+        // Load package at higher sdk.
+        final ApplicationInfo upgraded = new ApplicationInfo();
+        upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
+        when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(upgraded);
+        loadStreamXml(baos, false);
+
+        // Default Channel should be gone.
+        assertEquals(null, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1,
+                NotificationChannel.DEFAULT_CHANNEL_ID, false));
+    }
+
+    @Test
+    public void testDeletesDefaultChannelAfterChannelIsCreated() throws Exception {
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
+                NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
+
+        // Load package at higher sdk.
+        final ApplicationInfo upgraded = new ApplicationInfo();
+        upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
+        when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(upgraded);
+        loadStreamXml(baos, false);
+
+        // Default Channel should be gone.
+        assertEquals(null, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1,
+                NotificationChannel.DEFAULT_CHANNEL_ID, false));
+    }
+
+    @Test
+    public void testLoadingOldChannelsDoesNotDeleteNewlyCreatedChannels() throws Exception {
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
+                NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
+
+        loadStreamXml(baos, false);
+
+        // Should still have the newly created channel that wasn't in the xml.
+        assertTrue(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "bananas", false) != null);
+    }
+
+    @Test
+    public void testCreateChannel_blocked() throws Exception {
+        mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_NONE);
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
+    }
+
+    @Test
+    public void testCreateChannel_badImportance() throws Exception {
+        try {
+            mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
+                    new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1),
+                    true, false);
+            fail("Was allowed to create a channel with invalid importance");
+        } catch (IllegalArgumentException e) {
+            // yay
+        }
+        try {
+            mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
+                    new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED),
+                    true, false);
+            fail("Was allowed to create a channel with invalid importance");
+        } catch (IllegalArgumentException e) {
+            // yay
+        }
+        try {
+            mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
+                    new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1),
+                    true, false);
+            fail("Was allowed to create a channel with invalid importance");
+        } catch (IllegalArgumentException e) {
+            // yay
+        }
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true, false);
+    }
+
+
+    @Test
+    public void testUpdate() throws Exception {
+        // no fields locked by user
+        final NotificationChannel channel =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+        channel.enableLights(true);
+        channel.setBypassDnd(true);
+        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, false, false);
+
+        // same id, try to update all fields
+        final NotificationChannel channel2 =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+        channel2.setSound(new Uri.Builder().scheme("test2").build(), mAudioAttributes);
+        channel2.enableLights(false);
+        channel2.setBypassDnd(false);
+        channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
+
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true);
+
+        // all fields should be changed
+        assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), false));
+
+        verify(mHandler, times(1)).requestSort();
+    }
+
+    @Test
+    public void testUpdate_preUpgrade_updatesAppFields() throws Exception {
+        mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_UNSPECIFIED);
+        assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+        assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
+        assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
+                mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
+        assertFalse(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
+
+        NotificationChannel defaultChannel = mHelper.getNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+
+        defaultChannel.setShowBadge(false);
+        defaultChannel.setImportance(IMPORTANCE_NONE);
+        defaultChannel.setBypassDnd(true);
+        defaultChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+        mHelper.setAppImportanceLocked(PKG_N_MR1, UID_N_MR1);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
+
+        // ensure app level fields are changed
+        assertFalse(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+        assertEquals(Notification.PRIORITY_MAX, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
+        assertEquals(Notification.VISIBILITY_SECRET, mHelper.getPackageVisibility(PKG_N_MR1,
+                UID_N_MR1));
+        assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_N_MR1, UID_N_MR1));
+        assertTrue(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
+    }
+
+    @Test
+    public void testUpdate_postUpgrade_noUpdateAppFields() throws Exception {
+        final NotificationChannel channel = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel, false, false);
+        assertTrue(mHelper.canShowBadge(PKG_O, UID_O));
+        assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_O, UID_O));
+        assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
+                mHelper.getPackageVisibility(PKG_O, UID_O));
+
+        channel.setShowBadge(false);
+        channel.setImportance(IMPORTANCE_NONE);
+        channel.setBypassDnd(true);
+        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+        mHelper.updateNotificationChannel(PKG_O, UID_O, channel, true);
+
+        // ensure app level fields are not changed
+        assertTrue(mHelper.canShowBadge(PKG_O, UID_O));
+        assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_O, UID_O));
+        assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
+                mHelper.getPackageVisibility(PKG_O, UID_O));
+        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_O,
+                UID_O));
+        assertFalse(mHelper.getIsAppImportanceLocked(PKG_O, UID_O));
+    }
+
+    @Test
+    public void testUpdate_preUpgrade_noUpdateAppFieldsWithMultipleChannels() throws Exception {
+        final NotificationChannel channel = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, false, false);
+        assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+        assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
+        assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
+                mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
+
+        channel.setShowBadge(false);
+        channel.setImportance(IMPORTANCE_NONE);
+        channel.setBypassDnd(true);
+        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true);
+
+        NotificationChannel defaultChannel = mHelper.getNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+
+        defaultChannel.setShowBadge(false);
+        defaultChannel.setImportance(IMPORTANCE_NONE);
+        defaultChannel.setBypassDnd(true);
+        defaultChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
+
+        // ensure app level fields are not changed
+        assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+        assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
+        assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
+                mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
+        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
+                UID_N_MR1));
+    }
+
+    @Test
+    public void testGetNotificationChannel_ReturnsNullForUnknownChannel() throws Exception {
+        assertEquals(null, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "garbage", false));
+    }
+
+    @Test
+    public void testCreateChannel_CannotChangeHiddenFields() throws Exception {
+        final NotificationChannel channel =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+        channel.enableLights(true);
+        channel.setBypassDnd(true);
+        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+        channel.setShowBadge(true);
+        int lockMask = 0;
+        for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
+            lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
+        }
+        channel.lockFields(lockMask);
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+
+        NotificationChannel savedChannel =
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), false);
+
+        assertEquals(channel.getName(), savedChannel.getName());
+        assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
+        assertFalse(savedChannel.canBypassDnd());
+        assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
+        assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
+
+        verify(mHandler, never()).requestSort();
+    }
+
+    @Test
+    public void testCreateChannel_CannotChangeHiddenFieldsAssistant() throws Exception {
+        final NotificationChannel channel =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+        channel.enableLights(true);
+        channel.setBypassDnd(true);
+        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+        channel.setShowBadge(true);
+        int lockMask = 0;
+        for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
+            lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
+        }
+        channel.lockFields(lockMask);
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+
+        NotificationChannel savedChannel =
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), false);
+
+        assertEquals(channel.getName(), savedChannel.getName());
+        assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
+        assertFalse(savedChannel.canBypassDnd());
+        assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
+        assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
+    }
+
+    @Test
+    public void testClearLockedFields() throws Exception {
+        final NotificationChannel channel = getChannel();
+        mHelper.clearLockedFields(channel);
+        assertEquals(0, channel.getUserLockedFields());
+
+        channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY
+                | NotificationChannel.USER_LOCKED_IMPORTANCE);
+        mHelper.clearLockedFields(channel);
+        assertEquals(0, channel.getUserLockedFields());
+    }
+
+    @Test
+    public void testLockFields_soundAndVibration() throws Exception {
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
+
+        final NotificationChannel update1 = getChannel();
+        update1.setSound(new Uri.Builder().scheme("test").build(),
+                new AudioAttributes.Builder().build());
+        update1.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update1, true);
+        assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
+                | NotificationChannel.USER_LOCKED_SOUND,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update1.getId(), false)
+                        .getUserLockedFields());
+
+        NotificationChannel update2 = getChannel();
+        update2.enableVibration(true);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update2, true);
+        assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
+                        | NotificationChannel.USER_LOCKED_SOUND
+                        | NotificationChannel.USER_LOCKED_VIBRATION,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update2.getId(), false)
+                        .getUserLockedFields());
+    }
+
+    @Test
+    public void testLockFields_vibrationAndLights() throws Exception {
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
+
+        final NotificationChannel update1 = getChannel();
+        update1.setVibrationPattern(new long[]{7945, 46 ,246});
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update1, true);
+        assertEquals(NotificationChannel.USER_LOCKED_VIBRATION,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update1.getId(), false)
+                        .getUserLockedFields());
+
+        final NotificationChannel update2 = getChannel();
+        update2.enableLights(true);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update2, true);
+        assertEquals(NotificationChannel.USER_LOCKED_VIBRATION
+                        | NotificationChannel.USER_LOCKED_LIGHTS,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update2.getId(), false)
+                        .getUserLockedFields());
+    }
+
+    @Test
+    public void testLockFields_lightsAndImportance() throws Exception {
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
+
+        final NotificationChannel update1 = getChannel();
+        update1.setLightColor(Color.GREEN);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update1, true);
+        assertEquals(NotificationChannel.USER_LOCKED_LIGHTS,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update1.getId(), false)
+                        .getUserLockedFields());
+
+        final NotificationChannel update2 = getChannel();
+        update2.setImportance(IMPORTANCE_DEFAULT);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update2, true);
+        assertEquals(NotificationChannel.USER_LOCKED_LIGHTS
+                        | NotificationChannel.USER_LOCKED_IMPORTANCE,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update2.getId(), false)
+                        .getUserLockedFields());
+    }
+
+    @Test
+    public void testLockFields_visibilityAndDndAndBadge() throws Exception {
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
+        assertEquals(0,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel().getId(), false)
+                        .getUserLockedFields());
+
+        final NotificationChannel update1 = getChannel();
+        update1.setBypassDnd(true);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update1, true);
+        assertEquals(NotificationChannel.USER_LOCKED_PRIORITY,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update1.getId(), false)
+                        .getUserLockedFields());
+
+        final NotificationChannel update2 = getChannel();
+        update2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update2, true);
+        assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
+                        | NotificationChannel.USER_LOCKED_VISIBILITY,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update2.getId(), false)
+                        .getUserLockedFields());
+
+        final NotificationChannel update3 = getChannel();
+        update3.setShowBadge(false);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update3, true);
+        assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
+                        | NotificationChannel.USER_LOCKED_VISIBILITY
+                        | NotificationChannel.USER_LOCKED_SHOW_BADGE,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update3.getId(), false)
+                        .getUserLockedFields());
+    }
+
+    @Test
+    public void testDeleteNonExistentChannel() throws Exception {
+        mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, "does not exist");
+    }
+
+    @Test
+    public void testGetDeletedChannel() throws Exception {
+        NotificationChannel channel = getChannel();
+        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+        channel.enableLights(true);
+        channel.setBypassDnd(true);
+        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+        channel.enableVibration(true);
+        channel.setVibrationPattern(new long[]{100, 67, 145, 156});
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+        mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
+
+        // Does not return deleted channel
+        NotificationChannel response =
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), false);
+        assertNull(response);
+
+        // Returns deleted channel
+        response = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true);
+        compareChannels(channel, response);
+        assertTrue(response.isDeleted());
+    }
+
+    @Test
+    public void testGetDeletedChannels() throws Exception {
+        Map<String, NotificationChannel> channelMap = new HashMap<>();
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+        channel.enableLights(true);
+        channel.setBypassDnd(true);
+        channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
+        channel.enableVibration(true);
+        channel.setVibrationPattern(new long[]{100, 67, 145, 156});
+        channelMap.put(channel.getId(), channel);
+        NotificationChannel channel2 =
+                new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
+        channelMap.put(channel2.getId(), channel2);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false);
+
+        mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
+
+        // Returns only non-deleted channels
+        List<NotificationChannel> channels =
+                mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList();
+        assertEquals(2, channels.size());   // Default channel + non-deleted channel
+        for (NotificationChannel nc : channels) {
+            if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
+                compareChannels(channel2, nc);
+            }
+        }
+
+        // Returns deleted channels too
+        channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList();
+        assertEquals(3, channels.size());               // Includes default channel
+        for (NotificationChannel nc : channels) {
+            if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
+                compareChannels(channelMap.get(nc.getId()), nc);
+            }
+        }
+    }
+
+    @Test
+    public void testGetDeletedChannelCount() throws Exception {
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        NotificationChannel channel2 =
+                new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
+        NotificationChannel channel3 =
+                new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false);
+
+        mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
+        mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId());
+
+        assertEquals(2, mHelper.getDeletedChannelCount(PKG_N_MR1, UID_N_MR1));
+        assertEquals(0, mHelper.getDeletedChannelCount("pkg2", UID_O));
+    }
+
+    @Test
+    public void testGetBlockedChannelCount() throws Exception {
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        NotificationChannel channel2 =
+                new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_NONE);
+        NotificationChannel channel3 =
+                new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_NONE);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false);
+
+        mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId());
+
+        assertEquals(1, mHelper.getBlockedChannelCount(PKG_N_MR1, UID_N_MR1));
+        assertEquals(0, mHelper.getBlockedChannelCount("pkg2", UID_O));
+    }
+
+    @Test
+    public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception {
+        // create notification channel that can't bypass dnd
+        // expected result: areChannelsBypassingDnd = false
+        // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
+        NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
+        resetZenModeHelper();
+
+        // create notification channel that can bypass dnd
+        // expected result: areChannelsBypassingDnd = true
+        NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel2.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, true);
+        assertTrue(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+        resetZenModeHelper();
+
+        // delete channels
+        mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
+        assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
+        verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
+        resetZenModeHelper();
+
+        mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId());
+        assertFalse(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+        resetZenModeHelper();
+    }
+
+    @Test
+    public void testUpdateCanChannelsBypassDnd() throws Exception {
+        // create notification channel that can't bypass dnd
+        // expected result: areChannelsBypassingDnd = false
+        // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
+        NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
+        resetZenModeHelper();
+
+        // update channel so it CAN bypass dnd:
+        // expected result: areChannelsBypassingDnd = true
+        channel.setBypassDnd(true);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true);
+        assertTrue(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+        resetZenModeHelper();
+
+        // update channel so it can't bypass dnd:
+        // expected result: areChannelsBypassingDnd = false
+        channel.setBypassDnd(false);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+        resetZenModeHelper();
+    }
+
+    @Test
+    public void testSetupNewZenModeHelper_canBypass() {
+        // start notification policy off with mAreChannelsBypassingDnd = true, but
+        // RankingHelper should change to false
+        mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
+                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
+        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+        resetZenModeHelper();
+    }
+
+    @Test
+    public void testSetupNewZenModeHelper_cannotBypass() {
+        // start notification policy off with mAreChannelsBypassingDnd = false
+        mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0);
+        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
+        resetZenModeHelper();
+    }
+
+    @Test
+    public void testCreateDeletedChannel() throws Exception {
+        long[] vibration = new long[]{100, 67, 145, 156};
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel.setVibrationPattern(vibration);
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+        mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
+
+        NotificationChannel newChannel = new NotificationChannel(
+                channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
+        newChannel.setVibrationPattern(new long[]{100});
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel, true, false);
+
+        // No long deleted, using old settings
+        compareChannels(channel,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel.getId(), false));
+    }
+
+    @Test
+    public void testOnlyHasDefaultChannel() throws Exception {
+        assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+        assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
+        assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+    }
+
+    @Test
+    public void testCreateChannel_defaultChannelId() throws Exception {
+        try {
+            mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, new NotificationChannel(
+                    NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true, false);
+            fail("Allowed to create default channel");
+        } catch (IllegalArgumentException e) {
+            // pass
+        }
+    }
+
+    @Test
+    public void testCreateChannel_alreadyExists() throws Exception {
+        long[] vibration = new long[]{100, 67, 145, 156};
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel.setVibrationPattern(vibration);
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+
+        NotificationChannel newChannel = new NotificationChannel(
+                channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
+        newChannel.setVibrationPattern(new long[]{100});
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel, true, false);
+
+        // Old settings not overridden
+        compareChannels(channel,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel.getId(), false));
+    }
+
+    @Test
+    public void testCreateChannel_noOverrideSound() throws Exception {
+        Uri sound = new Uri.Builder().scheme("test").build();
+        final NotificationChannel channel = new NotificationChannel("id2", "name2",
+                 NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setSound(sound, mAudioAttributes);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+        assertEquals(sound, mHelper.getNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, channel.getId(), false).getSound());
+    }
+
+    @Test
+    public void testPermanentlyDeleteChannels() throws Exception {
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        NotificationChannel channel2 =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
+
+        mHelper.permanentlyDeleteNotificationChannels(PKG_N_MR1, UID_N_MR1);
+
+        // Only default channel remains
+        assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList().size());
+    }
+
+    @Test
+    public void testDeleteGroup() throws Exception {
+        NotificationChannelGroup notDeleted = new NotificationChannelGroup("not", "deleted");
+        NotificationChannelGroup deleted = new NotificationChannelGroup("totally", "deleted");
+        NotificationChannel nonGroupedNonDeletedChannel =
+                new NotificationChannel("no group", "so not deleted", IMPORTANCE_HIGH);
+        NotificationChannel groupedButNotDeleted =
+                new NotificationChannel("not deleted", "belongs to notDeleted", IMPORTANCE_DEFAULT);
+        groupedButNotDeleted.setGroup("not");
+        NotificationChannel groupedAndDeleted =
+                new NotificationChannel("deleted", "belongs to deleted", IMPORTANCE_DEFAULT);
+        groupedAndDeleted.setGroup("totally");
+
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, notDeleted, true);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, deleted, true);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nonGroupedNonDeletedChannel, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedButNotDeleted, true, false);
+
+        mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, deleted.getId());
+
+        assertNull(mHelper.getNotificationChannelGroup(deleted.getId(), PKG_N_MR1, UID_N_MR1));
+        assertNotNull(mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG_N_MR1, UID_N_MR1));
+
+        assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), false));
+        compareChannels(groupedAndDeleted,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), true));
+
+        compareChannels(groupedButNotDeleted,
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedButNotDeleted.getId(), false));
+        compareChannels(nonGroupedNonDeletedChannel, mHelper.getNotificationChannel(
+                PKG_N_MR1, UID_N_MR1, nonGroupedNonDeletedChannel.getId(), false));
+
+        // notDeleted
+        assertEquals(1, mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1).size());
+
+        verify(mHandler, never()).requestSort();
+    }
+
+    @Test
+    public void testOnUserRemoved() throws Exception {
+        int[] user0Uids = {98, 235, 16, 3782};
+        int[] user1Uids = new int[user0Uids.length];
+        for (int i = 0; i < user0Uids.length; i++) {
+            user1Uids[i] = UserHandle.PER_USER_RANGE + user0Uids[i];
+
+            final ApplicationInfo legacy = new ApplicationInfo();
+            legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+            when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy);
+
+            // create records with the default channel for all user 0 and user 1 uids
+            mHelper.getImportance(PKG_N_MR1, user0Uids[i]);
+            mHelper.getImportance(PKG_N_MR1, user1Uids[i]);
+        }
+
+        mHelper.onUserRemoved(1);
+
+        // user 0 records remain
+        for (int i = 0; i < user0Uids.length; i++) {
+            assertEquals(1,
+                    mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false).getList().size());
+        }
+        // user 1 records are gone
+        for (int i = 0; i < user1Uids.length; i++) {
+            assertEquals(0,
+                    mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false).getList().size());
+        }
+    }
+
+    @Test
+    public void testOnPackageChanged_packageRemoval() throws Exception {
+        // Deleted
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+
+        mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+                UID_N_MR1});
+
+        assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList().size());
+
+        // Not deleted
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+
+        mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+                UID_N_MR1});
+        assertEquals(2, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
+    }
+
+    @Test
+    public void testOnPackageChanged_packageRemoval_importance() throws Exception {
+        mHelper.setImportance(PKG_N_MR1, UID_N_MR1, NotificationManager.IMPORTANCE_HIGH);
+
+        mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+                UID_N_MR1});
+
+        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
+                UID_N_MR1));
+    }
+
+    @Test
+    public void testOnPackageChanged_packageRemoval_groups() throws Exception {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+        NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+
+        mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+                UID_N_MR1});
+
+        assertEquals(0,
+                mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList().size());
+    }
+
+    @Test
+    public void testOnPackageChange_downgradeTargetSdk() throws Exception {
+        // create channel as api 26
+        mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
+
+        // install new app version targeting 25
+        final ApplicationInfo legacy = new ApplicationInfo();
+        legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+        when(mPm.getApplicationInfoAsUser(eq(PKG_O), anyInt(), anyInt())).thenReturn(legacy);
+        mHelper.onPackagesChanged(
+                false, UserHandle.USER_SYSTEM, new String[]{PKG_O}, new int[]{UID_O});
+
+        // make sure the default channel was readded
+        //assertEquals(2, mHelper.getNotificationChannels(PKG_O, UID_O, false).getList().size());
+        assertNotNull(mHelper.getNotificationChannel(
+                PKG_O, UID_O, NotificationChannel.DEFAULT_CHANNEL_ID, false));
+    }
+
+    @Test
+    public void testRecordDefaults() throws Exception {
+        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
+                UID_N_MR1));
+        assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+        assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
+    }
+
+    @Test
+    public void testCreateGroup() throws Exception {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+        assertEquals(ncg, mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1).iterator().next());
+        verify(mHandler, never()).requestSort();
+    }
+
+    @Test
+    public void testCannotCreateChannel_badGroup() throws Exception {
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        channel1.setGroup("garbage");
+        try {
+            mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+            fail("Created a channel with a bad group");
+        } catch (IllegalArgumentException e) {
+        }
+    }
+
+    @Test
+    public void testCannotCreateChannel_goodGroup() throws Exception {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        channel1.setGroup(ncg.getId());
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+
+        assertEquals(ncg.getId(),
+                mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false).getGroup());
+    }
+
+    @Test
+    public void testGetChannelGroups() throws Exception {
+        NotificationChannelGroup unused = new NotificationChannelGroup("unused", "s");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, unused, true);
+        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+        NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        channel1.setGroup(ncg.getId());
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+        NotificationChannel channel1a =
+                new NotificationChannel("id1a", "name1", NotificationManager.IMPORTANCE_HIGH);
+        channel1a.setGroup(ncg.getId());
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1a, true, false);
+
+        NotificationChannel channel2 =
+                new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
+        channel2.setGroup(ncg2.getId());
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false);
+
+        NotificationChannel channel3 =
+                new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false);
+
+        List<NotificationChannelGroup> actual =
+                mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList();
+        assertEquals(3, actual.size());
+        for (NotificationChannelGroup group : actual) {
+            if (group.getId() == null) {
+                assertEquals(2, group.getChannels().size()); // misc channel too
+                assertTrue(channel3.getId().equals(group.getChannels().get(0).getId())
+                        || channel3.getId().equals(group.getChannels().get(1).getId()));
+            } else if (group.getId().equals(ncg.getId())) {
+                assertEquals(2, group.getChannels().size());
+                if (group.getChannels().get(0).getId().equals(channel1.getId())) {
+                    assertTrue(group.getChannels().get(1).getId().equals(channel1a.getId()));
+                } else if (group.getChannels().get(0).getId().equals(channel1a.getId())) {
+                    assertTrue(group.getChannels().get(1).getId().equals(channel1.getId()));
+                } else {
+                    fail("expected channel not found");
+                }
+            } else if (group.getId().equals(ncg2.getId())) {
+                assertEquals(1, group.getChannels().size());
+                assertEquals(channel2.getId(), group.getChannels().get(0).getId());
+            }
+        }
+    }
+
+    @Test
+    public void testGetChannelGroups_noSideEffects() throws Exception {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        channel1.setGroup(ncg.getId());
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+        mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList();
+
+        channel1.setImportance(IMPORTANCE_LOW);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true);
+
+        List<NotificationChannelGroup> actual =
+                mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList();
+
+        assertEquals(2, actual.size());
+        for (NotificationChannelGroup group : actual) {
+            if (Objects.equals(group.getId(), ncg.getId())) {
+                assertEquals(1, group.getChannels().size());
+            }
+        }
+    }
+
+    @Test
+    public void testCreateChannel_updateName() throws Exception {
+        NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
+        NotificationChannel actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
+        assertEquals("hello", actual.getName());
+
+        nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
+
+        actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
+        assertEquals("goodbye", actual.getName());
+        assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
+
+        verify(mHandler, times(1)).requestSort();
+    }
+
+    @Test
+    public void testCreateChannel_addToGroup() throws Exception {
+        NotificationChannelGroup group = new NotificationChannelGroup("group", "");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
+        NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
+        NotificationChannel actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
+        assertNull(actual.getGroup());
+
+        nc = new NotificationChannel("id", "hello", IMPORTANCE_HIGH);
+        nc.setGroup(group.getId());
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
+
+        actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
+        assertNotNull(actual.getGroup());
+        assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
+
+        verify(mHandler, times(1)).requestSort();
+    }
+
+    @Test
+    public void testDumpChannelsJson() throws Exception {
+        final ApplicationInfo upgrade = new ApplicationInfo();
+        upgrade.targetSdkVersion = Build.VERSION_CODES.O;
+        try {
+            when(mPm.getApplicationInfoAsUser(
+                    anyString(), anyInt(), anyInt())).thenReturn(upgrade);
+        } catch (PackageManager.NameNotFoundException e) {
+        }
+        ArrayMap<String, Integer> expectedChannels = new ArrayMap<>();
+        int numPackages = ThreadLocalRandom.current().nextInt(1, 5);
+        for (int i = 0; i < numPackages; i++) {
+            String pkgName = "pkg" + i;
+            int numChannels = ThreadLocalRandom.current().nextInt(1, 10);
+            for (int j = 0; j < numChannels; j++) {
+                mHelper.createNotificationChannel(pkgName, UID_N_MR1,
+                        new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false);
+            }
+            expectedChannels.put(pkgName, numChannels);
+        }
+
+        // delete the first channel of the first package
+        String pkg = expectedChannels.keyAt(0);
+        mHelper.deleteNotificationChannel("pkg" + 0, UID_N_MR1, "0");
+        // dump should not include deleted channels
+        int count = expectedChannels.get(pkg);
+        expectedChannels.put(pkg, count - 1);
+
+        JSONArray actual = mHelper.dumpChannelsJson(new NotificationManagerService.DumpFilter());
+        assertEquals(numPackages, actual.length());
+        for (int i = 0; i < numPackages; i++) {
+            JSONObject object = actual.getJSONObject(i);
+            assertTrue(expectedChannels.containsKey(object.get("packageName")));
+            assertEquals(expectedChannels.get(object.get("packageName")).intValue(),
+                    object.getInt("channelCount"));
+        }
+    }
+
+    @Test
+    public void testBadgingOverrideTrue() throws Exception {
+        Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.NOTIFICATION_BADGING, 1,
+                USER.getIdentifier());
+        mHelper.updateBadgingEnabled(); // would be called by settings observer
+        assertTrue(mHelper.badgingEnabled(USER));
+    }
+
+    @Test
+    public void testBadgingOverrideFalse() throws Exception {
+        Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.NOTIFICATION_BADGING, 0,
+                USER.getIdentifier());
+        mHelper.updateBadgingEnabled(); // would be called by settings observer
+        assertFalse(mHelper.badgingEnabled(USER));
+    }
+
+    @Test
+    public void testBadgingForUserAll() throws Exception {
+        try {
+            mHelper.badgingEnabled(UserHandle.ALL);
+        } catch (Exception e) {
+            fail("just don't throw");
+        }
+    }
+
+    @Test
+    public void testBadgingOverrideUserIsolation() throws Exception {
+        Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.NOTIFICATION_BADGING, 0,
+                USER.getIdentifier());
+        Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.NOTIFICATION_BADGING, 1,
+                USER2.getIdentifier());
+        mHelper.updateBadgingEnabled(); // would be called by settings observer
+        assertFalse(mHelper.badgingEnabled(USER));
+        assertTrue(mHelper.badgingEnabled(USER2));
+    }
+
+    @Test
+    public void testOnLocaleChanged_updatesDefaultChannels() throws Exception {
+        String newLabel = "bananas!";
+        final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1,
+                UID_N_MR1,
+                NotificationChannel.DEFAULT_CHANNEL_ID, false);
+        assertFalse(newLabel.equals(defaultChannel.getName()));
+
+        Resources res = mock(Resources.class);
+        when(mContext.getResources()).thenReturn(res);
+        when(res.getString(com.android.internal.R.string.default_notification_channel_label))
+                .thenReturn(newLabel);
+
+        mHelper.onLocaleChanged(mContext, USER.getIdentifier());
+
+        assertEquals(newLabel, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1,
+                NotificationChannel.DEFAULT_CHANNEL_ID, false).getName());
+    }
+
+    @Test
+    public void testIsGroupBlocked_noGroup() throws Exception {
+        assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, null));
+
+        assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, "non existent group"));
+    }
+
+    @Test
+    public void testIsGroupBlocked_notBlocked() throws Exception {
+        NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
+
+        assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
+    }
+
+    @Test
+    public void testIsGroupBlocked_blocked() throws Exception {
+        NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
+        group.setBlocked(true);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, false);
+
+        assertTrue(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
+    }
+
+    @Test
+    public void testIsGroup_appCannotResetBlock() throws Exception {
+        NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
+        NotificationChannelGroup group2 = group.clone();
+        group2.setBlocked(true);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group2, false);
+        assertTrue(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
+
+        NotificationChannelGroup group3 = group.clone();
+        group3.setBlocked(false);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group3, true);
+        assertTrue(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
+    }
+
+    @Test
+    public void testGetNotificationChannelGroupWithChannels() throws Exception {
+        NotificationChannelGroup group = new NotificationChannelGroup("group", "");
+        NotificationChannelGroup other = new NotificationChannelGroup("something else", "");
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, other, true);
+
+        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+        a.setGroup(group.getId());
+        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_DEFAULT);
+        b.setGroup(other.getId());
+        NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
+        c.setGroup(group.getId());
+        NotificationChannel d = new NotificationChannel("d", "d", IMPORTANCE_DEFAULT);
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, a, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, b, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, c, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, d, true, false);
+        mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, c.getId());
+
+        NotificationChannelGroup retrieved = mHelper.getNotificationChannelGroupWithChannels(
+                PKG_N_MR1, UID_N_MR1, group.getId(), true);
+        assertEquals(2, retrieved.getChannels().size());
+        compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
+        compareChannels(c, findChannel(retrieved.getChannels(), c.getId()));
+
+        retrieved = mHelper.getNotificationChannelGroupWithChannels(
+                PKG_N_MR1, UID_N_MR1, group.getId(), false);
+        assertEquals(1, retrieved.getChannels().size());
+        compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
+    }
+
+    @Test
+    public void testAndroidPkgCannotBypassDnd_creation() {
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        test.setBypassDnd(true);
+
+        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
+
+        assertFalse(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
+                .canBypassDnd());
+    }
+
+    @Test
+    public void testDndPkgCanBypassDnd_creation() {
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        test.setBypassDnd(true);
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, test, true, true);
+
+        assertTrue(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "A", false).canBypassDnd());
+    }
+
+    @Test
+    public void testNormalPkgCannotBypassDnd_creation() {
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        test.setBypassDnd(true);
+
+        mHelper.createNotificationChannel(PKG_N_MR1, 1000, test, true, false);
+
+        assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, 1000, "A", false).canBypassDnd());
+    }
+
+    @Test
+    public void testAndroidPkgCannotBypassDnd_update() throws Exception {
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
+
+        NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        update.setBypassDnd(true);
+        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true, false);
+
+        assertFalse(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
+                .canBypassDnd());
+    }
+
+    @Test
+    public void testDndPkgCanBypassDnd_update() throws Exception {
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, test, true, true);
+
+        NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        update.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, update, true, true);
+
+        assertTrue(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "A", false).canBypassDnd());
+    }
+
+    @Test
+    public void testNormalPkgCannotBypassDnd_update() {
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_N_MR1, 1000, test, true, false);
+        NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        update.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG_N_MR1, 1000, update, true, false);
+        assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, 1000, "A", false).canBypassDnd());
+    }
+
+    @Test
+    public void testGetBlockedAppCount_noApps() {
+        assertEquals(0, mHelper.getBlockedAppCount(0));
+    }
+
+    @Test
+    public void testGetBlockedAppCount_noAppsForUserId() {
+        mHelper.setEnabled(PKG_N_MR1, 100, false);
+        assertEquals(0, mHelper.getBlockedAppCount(9));
+    }
+
+    @Test
+    public void testGetBlockedAppCount_appsForUserId() {
+        mHelper.setEnabled(PKG_N_MR1, 1020, false);
+        mHelper.setEnabled(PKG_N_MR1, 1030, false);
+        mHelper.setEnabled(PKG_N_MR1, 1060, false);
+        mHelper.setEnabled(PKG_N_MR1, 1000, true);
+        assertEquals(3, mHelper.getBlockedAppCount(0));
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 98c6ec4..7e0fcc9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -15,35 +15,17 @@
  */
 package com.android.server.notification;
 
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MAX;
-import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.fail;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
 import android.app.NotificationChannel;
-import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
 import android.content.ContentProvider;
 import android.content.Context;
@@ -52,46 +34,26 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
-import android.content.res.Resources;
-import android.graphics.Color;
 import android.media.AudioAttributes;
 import android.net.Uri;
 import android.os.Build;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.TestableContentResolver;
-import android.util.ArrayMap;
-import android.util.Xml;
 
-import com.android.internal.util.FastXmlSerializer;
 import com.android.server.UiServiceTestCase;
 
-import org.json.JSONArray;
-import org.json.JSONObject;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-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;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ThreadLocalRandom;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -118,6 +80,7 @@
     @Mock IContentProvider mTestIContentProvider;
     @Mock Context mContext;
     @Mock ZenModeHelper mMockZenModeHelper;
+    @Mock RankingConfig mConfig;
 
     private NotificationManager.Policy mTestNotificationPolicy;
     private Notification mNotiGroupGSortA;
@@ -179,9 +142,8 @@
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new RankingHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+        mHelper = new RankingHelper(getContext(), mHandler, mConfig, mMockZenModeHelper,
                 mUsageStats, new String[] {ImportanceExtractor.class.getName()});
-        resetZenModeHelper();
 
         mNotiGroupGSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("A")
@@ -240,74 +202,6 @@
                 IMPORTANCE_LOW);
     }
 
-    private ByteArrayOutputStream writeXmlAndPurge(String pkg, int uid, boolean forBackup,
-            String... channelIds)
-            throws Exception {
-        XmlSerializer serializer = new FastXmlSerializer();
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
-        serializer.startDocument(null, true);
-        mHelper.writeXml(serializer, forBackup);
-        serializer.endDocument();
-        serializer.flush();
-        for (String channelId : channelIds) {
-            mHelper.permanentlyDeleteNotificationChannel(pkg, uid, channelId);
-        }
-        return baos;
-    }
-
-    private void loadStreamXml(ByteArrayOutputStream stream, boolean forRestore) throws Exception {
-        loadByteArrayXml(stream.toByteArray(), forRestore);
-    }
-
-    private void loadByteArrayXml(byte[] byteArray, boolean forRestore) throws Exception {
-        XmlPullParser parser = Xml.newPullParser();
-        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
-        parser.nextTag();
-        mHelper.readXml(parser, forRestore);
-    }
-
-    private void compareChannels(NotificationChannel expected, NotificationChannel actual) {
-        assertEquals(expected.getId(), actual.getId());
-        assertEquals(expected.getName(), actual.getName());
-        assertEquals(expected.getDescription(), actual.getDescription());
-        assertEquals(expected.shouldVibrate(), actual.shouldVibrate());
-        assertEquals(expected.shouldShowLights(), actual.shouldShowLights());
-        assertEquals(expected.getImportance(), actual.getImportance());
-        assertEquals(expected.getLockscreenVisibility(), actual.getLockscreenVisibility());
-        assertEquals(expected.getSound(), actual.getSound());
-        assertEquals(expected.canBypassDnd(), actual.canBypassDnd());
-        assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern()));
-        assertEquals(expected.getGroup(), actual.getGroup());
-        assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes());
-        assertEquals(expected.getLightColor(), actual.getLightColor());
-    }
-
-    private void compareGroups(NotificationChannelGroup expected, NotificationChannelGroup actual) {
-        assertEquals(expected.getId(), actual.getId());
-        assertEquals(expected.getName(), actual.getName());
-        assertEquals(expected.getDescription(), actual.getDescription());
-        assertEquals(expected.isBlocked(), actual.isBlocked());
-    }
-
-    private NotificationChannel getChannel() {
-        return new NotificationChannel("id", "name", IMPORTANCE_LOW);
-    }
-
-    private NotificationChannel findChannel(List<NotificationChannel> channels, String id) {
-        for (NotificationChannel channel : channels) {
-            if (channel.getId().equals(id)) {
-                return channel;
-            }
-        }
-        return null;
-    }
-
-    private void resetZenModeHelper() {
-        reset(mMockZenModeHelper);
-        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-    }
-
     @Test
     public void testFindAfterRankingWithASplitGroup() throws Exception {
         ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3);
@@ -357,1496 +251,4 @@
         ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
         mHelper.sort(notificationList);
     }
-
-    @Test
-    public void testChannelXml() throws Exception {
-        NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
-        ncg.setBlocked(true);
-        ncg.setDescription("group desc");
-        NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
-        NotificationChannel channel1 =
-                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
-        NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel2.setDescription("descriptions for all");
-        channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
-        channel2.enableLights(true);
-        channel2.setBypassDnd(true);
-        channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
-        channel2.enableVibration(true);
-        channel2.setGroup(ncg.getId());
-        channel2.setVibrationPattern(new long[]{100, 67, 145, 156});
-        channel2.setLightColor(Color.BLUE);
-
-        mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
-        mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
-        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
-        mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
-
-        mHelper.setShowBadge(PKG, UID, true);
-        mHelper.setAppImportanceLocked(PKG, UID);
-
-        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false, channel1.getId(),
-                channel2.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
-        mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG}, new int[]{UID});
-
-        loadStreamXml(baos, false);
-
-        assertTrue(mHelper.canShowBadge(PKG, UID));
-        assertTrue(mHelper.getIsAppImportanceLocked(PKG, UID));
-        assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
-        compareChannels(channel2,
-                mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
-
-        List<NotificationChannelGroup> actualGroups =
-                mHelper.getNotificationChannelGroups(PKG, UID, false, true).getList();
-        boolean foundNcg = false;
-        for (NotificationChannelGroup actual : actualGroups) {
-            if (ncg.getId().equals(actual.getId())) {
-                foundNcg = true;
-                compareGroups(ncg, actual);
-            } else if (ncg2.getId().equals(actual.getId())) {
-                compareGroups(ncg2, actual);
-            }
-        }
-        assertTrue(foundNcg);
-
-        boolean foundChannel2Group = false;
-        for (NotificationChannelGroup actual : actualGroups) {
-            if (channel2.getGroup().equals(actual.getChannels().get(0).getGroup())) {
-                foundChannel2Group = true;
-                break;
-            }
-        }
-        assertTrue(foundChannel2Group);
-    }
-
-    @Test
-    public void testChannelXmlForBackup() throws Exception {
-        NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
-        NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
-        NotificationChannel channel1 =
-                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
-        NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel2.setDescription("descriptions for all");
-        channel2.setSound(SOUND_URI, mAudioAttributes);
-        channel2.enableLights(true);
-        channel2.setBypassDnd(true);
-        channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
-        channel2.enableVibration(false);
-        channel2.setGroup(ncg.getId());
-        channel2.setLightColor(Color.BLUE);
-        NotificationChannel channel3 = new NotificationChannel("id3", "NAM3", IMPORTANCE_HIGH);
-        channel3.enableVibration(true);
-
-        mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
-        mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
-        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
-        mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
-        mHelper.createNotificationChannel(PKG, UID, channel3, false, false);
-        mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
-
-        mHelper.setShowBadge(PKG, UID, true);
-
-        mHelper.setImportance(UPDATED_PKG, UID2, IMPORTANCE_NONE);
-
-        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel1.getId(),
-                channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
-        mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG, UPDATED_PKG},
-                new int[]{UID, UID2});
-
-        mHelper.setShowBadge(UPDATED_PKG, UID2, true);
-
-        loadStreamXml(baos, true);
-
-        assertEquals(IMPORTANCE_NONE, mHelper.getImportance(UPDATED_PKG, UID2));
-        assertTrue(mHelper.canShowBadge(PKG, UID));
-        assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
-        compareChannels(channel2,
-                mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
-        compareChannels(channel3,
-                mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
-
-        List<NotificationChannelGroup> actualGroups =
-                mHelper.getNotificationChannelGroups(PKG, UID, false, true).getList();
-        boolean foundNcg = false;
-        for (NotificationChannelGroup actual : actualGroups) {
-            if (ncg.getId().equals(actual.getId())) {
-                foundNcg = true;
-                compareGroups(ncg, actual);
-            } else if (ncg2.getId().equals(actual.getId())) {
-                compareGroups(ncg2, actual);
-            }
-        }
-        assertTrue(foundNcg);
-
-        boolean foundChannel2Group = false;
-        for (NotificationChannelGroup actual : actualGroups) {
-            if (channel2.getGroup().equals(actual.getChannels().get(0).getGroup())) {
-                foundChannel2Group = true;
-                break;
-            }
-        }
-        assertTrue(foundChannel2Group);
-    }
-
-    @Test
-    public void testBackupXml_backupCanonicalizedSoundUri() throws Exception {
-        NotificationChannel channel =
-                new NotificationChannel("id", "name", IMPORTANCE_LOW);
-        channel.setSound(SOUND_URI, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-
-        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
-
-        // Testing that in restore we are given the canonical version
-        loadStreamXml(baos, true);
-        verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
-    }
-
-    @Test
-    public void testRestoreXml_withExistentCanonicalizedSoundUri() throws Exception {
-        Uri localUri = Uri.parse("content://" + TEST_AUTHORITY + "/local/url");
-        Uri canonicalBasedOnLocal = localUri.buildUpon()
-                .appendQueryParameter("title", "Test")
-                .appendQueryParameter("canonical", "1")
-                .build();
-        when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
-                .thenReturn(canonicalBasedOnLocal);
-        when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
-                .thenReturn(localUri);
-        when(mTestIContentProvider.uncanonicalize(any(), eq(canonicalBasedOnLocal)))
-                .thenReturn(localUri);
-
-        NotificationChannel channel =
-                new NotificationChannel("id", "name", IMPORTANCE_LOW);
-        channel.setSound(SOUND_URI, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
-
-        loadStreamXml(baos, true);
-
-        NotificationChannel actualChannel = mHelper.getNotificationChannel(
-                PKG, UID, channel.getId(), false);
-        assertEquals(localUri, actualChannel.getSound());
-    }
-
-    @Test
-    public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
-        Thread.sleep(3000);
-        when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
-                .thenReturn(null);
-        when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
-                .thenReturn(null);
-
-        NotificationChannel channel =
-                new NotificationChannel("id", "name", IMPORTANCE_LOW);
-        channel.setSound(SOUND_URI, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
-
-        loadStreamXml(baos, true);
-
-        NotificationChannel actualChannel = mHelper.getNotificationChannel(
-                PKG, UID, channel.getId(), false);
-        assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
-    }
-
-
-    /**
-     * Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to
-     * handle its restore properly.
-     */
-    @Test
-    public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
-        // Not a local uncanonicalized uri, simulating that it fails to exist locally
-        when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null);
-        String id = "id";
-        String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
-                + "<package name=\"com.android.server.notification\" show_badge=\"true\">\n"
-                + "<channel id=\"" + id + "\" name=\"name\" importance=\"2\" "
-                + "sound=\"" + SOUND_URI + "\" "
-                + "usage=\"6\" content_type=\"0\" flags=\"1\" show_badge=\"true\" />\n"
-                + "<channel id=\"miscellaneous\" name=\"Uncategorized\" usage=\"5\" "
-                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
-                + "</package>\n"
-                + "</ranking>\n";
-
-        loadByteArrayXml(backupWithUncanonicalizedSoundUri.getBytes(), true);
-
-        NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG, UID, id, false);
-        assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
-    }
-
-    @Test
-    public void testBackupRestoreXml_withNullSoundUri() throws Exception {
-        NotificationChannel channel =
-                new NotificationChannel("id", "name", IMPORTANCE_LOW);
-        channel.setSound(null, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
-
-        loadStreamXml(baos, true);
-
-        NotificationChannel actualChannel = mHelper.getNotificationChannel(
-                PKG, UID, channel.getId(), false);
-        assertEquals(null, actualChannel.getSound());
-    }
-
-    @Test
-    public void testChannelXml_backup() throws Exception {
-        NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
-        NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
-        NotificationChannel channel1 =
-                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
-        NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        NotificationChannel channel3 =
-                new NotificationChannel("id3", "name3", IMPORTANCE_LOW);
-        channel3.setGroup(ncg.getId());
-
-        mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
-        mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
-        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
-        mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
-        mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
-
-        mHelper.deleteNotificationChannel(PKG, UID, channel1.getId());
-        mHelper.deleteNotificationChannelGroup(PKG, UID, ncg.getId());
-        assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
-
-        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel1.getId(),
-                channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
-        mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG}, new int[]{UID});
-
-        XmlPullParser parser = Xml.newPullParser();
-        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
-                null);
-        parser.nextTag();
-        mHelper.readXml(parser, true);
-
-        assertNull(mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
-        assertNull(mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
-        assertNull(mHelper.getNotificationChannelGroup(ncg.getId(), PKG, UID));
-        //assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG, UID));
-        assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
-    }
-
-    @Test
-    public void testChannelXml_defaultChannelLegacyApp_noUserSettings() throws Exception {
-        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
-                NotificationChannel.DEFAULT_CHANNEL_ID);
-
-        loadStreamXml(baos, false);
-
-        final NotificationChannel updated = mHelper.getNotificationChannel(PKG, UID,
-                NotificationChannel.DEFAULT_CHANNEL_ID, false);
-        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, updated.getImportance());
-        assertFalse(updated.canBypassDnd());
-        assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE, updated.getLockscreenVisibility());
-        assertEquals(0, updated.getUserLockedFields());
-    }
-
-    @Test
-    public void testChannelXml_defaultChannelUpdatedApp_userSettings() throws Exception {
-        final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG, UID,
-                NotificationChannel.DEFAULT_CHANNEL_ID, false);
-        defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
-        mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true);
-
-        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
-                NotificationChannel.DEFAULT_CHANNEL_ID);
-
-        loadStreamXml(baos, false);
-
-        assertEquals(NotificationManager.IMPORTANCE_LOW, mHelper.getNotificationChannel(
-                PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance());
-    }
-
-    @Test
-    public void testChannelXml_upgradeCreateDefaultChannel() throws Exception {
-        final String preupgradeXml = "<ranking version=\"1\">\n"
-                + "<package name=\"" + PKG
-                + "\" importance=\"" + NotificationManager.IMPORTANCE_HIGH
-                + "\" priority=\"" + Notification.PRIORITY_MAX + "\" visibility=\""
-                + Notification.VISIBILITY_SECRET + "\"" +" uid=\"" + UID + "\" />\n"
-                + "<package name=\"" + UPDATED_PKG + "\" uid=\"" + UID2 + "\" visibility=\""
-                + Notification.VISIBILITY_PRIVATE + "\" />\n"
-                + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
-        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())),
-                null);
-        parser.nextTag();
-        mHelper.readXml(parser, false);
-
-        final NotificationChannel updated1 =
-            mHelper.getNotificationChannel(PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
-        assertEquals(NotificationManager.IMPORTANCE_HIGH, updated1.getImportance());
-        assertTrue(updated1.canBypassDnd());
-        assertEquals(Notification.VISIBILITY_SECRET, updated1.getLockscreenVisibility());
-        assertEquals(NotificationChannel.USER_LOCKED_IMPORTANCE
-                | NotificationChannel.USER_LOCKED_PRIORITY
-                | NotificationChannel.USER_LOCKED_VISIBILITY,
-                updated1.getUserLockedFields());
-
-        // No Default Channel created for updated packages
-        assertEquals(null, mHelper.getNotificationChannel(UPDATED_PKG, UID2,
-                NotificationChannel.DEFAULT_CHANNEL_ID, false));
-    }
-
-    @Test
-    public void testChannelXml_upgradeDeletesDefaultChannel() throws Exception {
-        final NotificationChannel defaultChannel = mHelper.getNotificationChannel(
-                PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
-        assertTrue(defaultChannel != null);
-        ByteArrayOutputStream baos =
-                writeXmlAndPurge(PKG, UID, false, NotificationChannel.DEFAULT_CHANNEL_ID);
-        // Load package at higher sdk.
-        final ApplicationInfo upgraded = new ApplicationInfo();
-        upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
-        when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded);
-        loadStreamXml(baos, false);
-
-        // Default Channel should be gone.
-        assertEquals(null, mHelper.getNotificationChannel(PKG, UID,
-                NotificationChannel.DEFAULT_CHANNEL_ID, false));
-    }
-
-    @Test
-    public void testDeletesDefaultChannelAfterChannelIsCreated() throws Exception {
-        mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
-        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
-                NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
-
-        // Load package at higher sdk.
-        final ApplicationInfo upgraded = new ApplicationInfo();
-        upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
-        when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded);
-        loadStreamXml(baos, false);
-
-        // Default Channel should be gone.
-        assertEquals(null, mHelper.getNotificationChannel(PKG, UID,
-                NotificationChannel.DEFAULT_CHANNEL_ID, false));
-    }
-
-    @Test
-    public void testLoadingOldChannelsDoesNotDeleteNewlyCreatedChannels() throws Exception {
-        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
-                NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
-        mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
-
-        loadStreamXml(baos, false);
-
-        // Should still have the newly created channel that wasn't in the xml.
-        assertTrue(mHelper.getNotificationChannel(PKG, UID, "bananas", false) != null);
-    }
-
-    @Test
-    public void testCreateChannel_blocked() throws Exception {
-        mHelper.setImportance(PKG, UID, IMPORTANCE_NONE);
-
-        mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
-    }
-
-    @Test
-    public void testCreateChannel_badImportance() throws Exception {
-        try {
-            mHelper.createNotificationChannel(PKG, UID,
-                    new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1),
-                    true, false);
-            fail("Was allowed to create a channel with invalid importance");
-        } catch (IllegalArgumentException e) {
-            // yay
-        }
-        try {
-            mHelper.createNotificationChannel(PKG, UID,
-                    new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED),
-                    true, false);
-            fail("Was allowed to create a channel with invalid importance");
-        } catch (IllegalArgumentException e) {
-            // yay
-        }
-        try {
-            mHelper.createNotificationChannel(PKG, UID,
-                    new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1),
-                    true, false);
-            fail("Was allowed to create a channel with invalid importance");
-        } catch (IllegalArgumentException e) {
-            // yay
-        }
-        mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true, false);
-        mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true, false);
-    }
-
-
-    @Test
-    public void testUpdate() throws Exception {
-        // no fields locked by user
-        final NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
-        channel.enableLights(true);
-        channel.setBypassDnd(true);
-        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
-
-        mHelper.createNotificationChannel(PKG, UID, channel, false, false);
-
-        // same id, try to update all fields
-        final NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
-        channel2.setSound(new Uri.Builder().scheme("test2").build(), mAudioAttributes);
-        channel2.enableLights(false);
-        channel2.setBypassDnd(false);
-        channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
-
-        mHelper.updateNotificationChannel(PKG, UID, channel2, true);
-
-        // all fields should be changed
-        assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
-
-        verify(mHandler, times(1)).requestSort();
-    }
-
-    @Test
-    public void testUpdate_preUpgrade_updatesAppFields() throws Exception {
-        mHelper.setImportance(PKG, UID, IMPORTANCE_UNSPECIFIED);
-        assertTrue(mHelper.canShowBadge(PKG, UID));
-        assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
-        assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
-                mHelper.getPackageVisibility(PKG, UID));
-        assertFalse(mHelper.getIsAppImportanceLocked(PKG, UID));
-
-        NotificationChannel defaultChannel = mHelper.getNotificationChannel(
-                PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
-
-        defaultChannel.setShowBadge(false);
-        defaultChannel.setImportance(IMPORTANCE_NONE);
-        defaultChannel.setBypassDnd(true);
-        defaultChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
-
-        mHelper.setAppImportanceLocked(PKG, UID);
-        mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true);
-
-        // ensure app level fields are changed
-        assertFalse(mHelper.canShowBadge(PKG, UID));
-        assertEquals(Notification.PRIORITY_MAX, mHelper.getPackagePriority(PKG, UID));
-        assertEquals(Notification.VISIBILITY_SECRET, mHelper.getPackageVisibility(PKG, UID));
-        assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG, UID));
-        assertTrue(mHelper.getIsAppImportanceLocked(PKG, UID));
-    }
-
-    @Test
-    public void testUpdate_postUpgrade_noUpdateAppFields() throws Exception {
-        final NotificationChannel channel = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-
-        mHelper.createNotificationChannel(PKG, UID, channel, false, false);
-        assertTrue(mHelper.canShowBadge(PKG, UID));
-        assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
-        assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
-                mHelper.getPackageVisibility(PKG, UID));
-
-        channel.setShowBadge(false);
-        channel.setImportance(IMPORTANCE_NONE);
-        channel.setBypassDnd(true);
-        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
-
-        mHelper.updateNotificationChannel(PKG, UID, channel, true);
-
-        // ensure app level fields are not changed
-        assertTrue(mHelper.canShowBadge(PKG, UID));
-        assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
-        assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
-                mHelper.getPackageVisibility(PKG, UID));
-        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
-    }
-
-    @Test
-    public void testGetNotificationChannel_ReturnsNullForUnknownChannel() throws Exception {
-        assertEquals(null, mHelper.getNotificationChannel(PKG, UID, "garbage", false));
-    }
-
-    @Test
-    public void testCreateChannel_CannotChangeHiddenFields() throws Exception {
-        final NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
-        channel.enableLights(true);
-        channel.setBypassDnd(true);
-        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
-        channel.setShowBadge(true);
-        int lockMask = 0;
-        for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
-            lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
-        }
-        channel.lockFields(lockMask);
-
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-
-        NotificationChannel savedChannel =
-                mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
-
-        assertEquals(channel.getName(), savedChannel.getName());
-        assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
-        assertFalse(savedChannel.canBypassDnd());
-        assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
-        assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
-
-        verify(mHandler, never()).requestSort();
-    }
-
-    @Test
-    public void testCreateChannel_CannotChangeHiddenFieldsAssistant() throws Exception {
-        final NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
-        channel.enableLights(true);
-        channel.setBypassDnd(true);
-        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
-        channel.setShowBadge(true);
-        int lockMask = 0;
-        for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
-            lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
-        }
-        channel.lockFields(lockMask);
-
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-
-        NotificationChannel savedChannel =
-                mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
-
-        assertEquals(channel.getName(), savedChannel.getName());
-        assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
-        assertFalse(savedChannel.canBypassDnd());
-        assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
-        assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
-    }
-
-    @Test
-    public void testClearLockedFields() throws Exception {
-        final NotificationChannel channel = getChannel();
-        mHelper.clearLockedFields(channel);
-        assertEquals(0, channel.getUserLockedFields());
-
-        channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY
-                | NotificationChannel.USER_LOCKED_IMPORTANCE);
-        mHelper.clearLockedFields(channel);
-        assertEquals(0, channel.getUserLockedFields());
-    }
-
-    @Test
-    public void testLockFields_soundAndVibration() throws Exception {
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
-
-        final NotificationChannel update1 = getChannel();
-        update1.setSound(new Uri.Builder().scheme("test").build(),
-                new AudioAttributes.Builder().build());
-        update1.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
-        mHelper.updateNotificationChannel(PKG, UID, update1, true);
-        assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
-                | NotificationChannel.USER_LOCKED_SOUND,
-                mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
-                        .getUserLockedFields());
-
-        NotificationChannel update2 = getChannel();
-        update2.enableVibration(true);
-        mHelper.updateNotificationChannel(PKG, UID, update2, true);
-        assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
-                        | NotificationChannel.USER_LOCKED_SOUND
-                        | NotificationChannel.USER_LOCKED_VIBRATION,
-                mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
-                        .getUserLockedFields());
-    }
-
-    @Test
-    public void testLockFields_vibrationAndLights() throws Exception {
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
-
-        final NotificationChannel update1 = getChannel();
-        update1.setVibrationPattern(new long[]{7945, 46 ,246});
-        mHelper.updateNotificationChannel(PKG, UID, update1, true);
-        assertEquals(NotificationChannel.USER_LOCKED_VIBRATION,
-                mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
-                        .getUserLockedFields());
-
-        final NotificationChannel update2 = getChannel();
-        update2.enableLights(true);
-        mHelper.updateNotificationChannel(PKG, UID, update2, true);
-        assertEquals(NotificationChannel.USER_LOCKED_VIBRATION
-                        | NotificationChannel.USER_LOCKED_LIGHTS,
-                mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
-                        .getUserLockedFields());
-    }
-
-    @Test
-    public void testLockFields_lightsAndImportance() throws Exception {
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
-
-        final NotificationChannel update1 = getChannel();
-        update1.setLightColor(Color.GREEN);
-        mHelper.updateNotificationChannel(PKG, UID, update1, true);
-        assertEquals(NotificationChannel.USER_LOCKED_LIGHTS,
-                mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
-                        .getUserLockedFields());
-
-        final NotificationChannel update2 = getChannel();
-        update2.setImportance(IMPORTANCE_DEFAULT);
-        mHelper.updateNotificationChannel(PKG, UID, update2, true);
-        assertEquals(NotificationChannel.USER_LOCKED_LIGHTS
-                        | NotificationChannel.USER_LOCKED_IMPORTANCE,
-                mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
-                        .getUserLockedFields());
-    }
-
-    @Test
-    public void testLockFields_visibilityAndDndAndBadge() throws Exception {
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
-        assertEquals(0,
-                mHelper.getNotificationChannel(PKG, UID, getChannel().getId(), false)
-                        .getUserLockedFields());
-
-        final NotificationChannel update1 = getChannel();
-        update1.setBypassDnd(true);
-        mHelper.updateNotificationChannel(PKG, UID, update1, true);
-        assertEquals(NotificationChannel.USER_LOCKED_PRIORITY,
-                mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
-                        .getUserLockedFields());
-
-        final NotificationChannel update2 = getChannel();
-        update2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
-        mHelper.updateNotificationChannel(PKG, UID, update2, true);
-        assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
-                        | NotificationChannel.USER_LOCKED_VISIBILITY,
-                mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
-                        .getUserLockedFields());
-
-        final NotificationChannel update3 = getChannel();
-        update3.setShowBadge(false);
-        mHelper.updateNotificationChannel(PKG, UID, update3, true);
-        assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
-                        | NotificationChannel.USER_LOCKED_VISIBILITY
-                        | NotificationChannel.USER_LOCKED_SHOW_BADGE,
-                mHelper.getNotificationChannel(PKG, UID, update3.getId(), false)
-                        .getUserLockedFields());
-    }
-
-    @Test
-    public void testDeleteNonExistentChannel() throws Exception {
-        mHelper.deleteNotificationChannelGroup(PKG, UID, "does not exist");
-    }
-
-    @Test
-    public void testGetDeletedChannel() throws Exception {
-        NotificationChannel channel = getChannel();
-        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
-        channel.enableLights(true);
-        channel.setBypassDnd(true);
-        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
-        channel.enableVibration(true);
-        channel.setVibrationPattern(new long[]{100, 67, 145, 156});
-
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-        mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
-
-        // Does not return deleted channel
-        NotificationChannel response =
-                mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
-        assertNull(response);
-
-        // Returns deleted channel
-        response = mHelper.getNotificationChannel(PKG, UID, channel.getId(), true);
-        compareChannels(channel, response);
-        assertTrue(response.isDeleted());
-    }
-
-    @Test
-    public void testGetDeletedChannels() throws Exception {
-        Map<String, NotificationChannel> channelMap = new HashMap<>();
-        NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
-        channel.enableLights(true);
-        channel.setBypassDnd(true);
-        channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
-        channel.enableVibration(true);
-        channel.setVibrationPattern(new long[]{100, 67, 145, 156});
-        channelMap.put(channel.getId(), channel);
-        NotificationChannel channel2 =
-                new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
-        channelMap.put(channel2.getId(), channel2);
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-        mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
-
-        mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
-
-        // Returns only non-deleted channels
-        List<NotificationChannel> channels =
-                mHelper.getNotificationChannels(PKG, UID, false).getList();
-        assertEquals(2, channels.size());   // Default channel + non-deleted channel
-        for (NotificationChannel nc : channels) {
-            if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
-                compareChannels(channel2, nc);
-            }
-        }
-
-        // Returns deleted channels too
-        channels = mHelper.getNotificationChannels(PKG, UID, true).getList();
-        assertEquals(3, channels.size());               // Includes default channel
-        for (NotificationChannel nc : channels) {
-            if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
-                compareChannels(channelMap.get(nc.getId()), nc);
-            }
-        }
-    }
-
-    @Test
-    public void testGetDeletedChannelCount() throws Exception {
-        NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        NotificationChannel channel2 =
-                new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
-        NotificationChannel channel3 =
-                new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_HIGH);
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-        mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
-        mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
-
-        mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
-        mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
-
-        assertEquals(2, mHelper.getDeletedChannelCount(PKG, UID));
-        assertEquals(0, mHelper.getDeletedChannelCount("pkg2", UID2));
-    }
-
-    @Test
-    public void testGetBlockedChannelCount() throws Exception {
-        NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        NotificationChannel channel2 =
-                new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_NONE);
-        NotificationChannel channel3 =
-                new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_NONE);
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-        mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
-        mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
-
-        mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
-
-        assertEquals(1, mHelper.getBlockedChannelCount(PKG, UID));
-        assertEquals(0, mHelper.getBlockedChannelCount("pkg2", UID2));
-    }
-
-    @Test
-    public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception {
-        // create notification channel that can't bypass dnd
-        // expected result: areChannelsBypassingDnd = false
-        // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
-        NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-        assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
-        resetZenModeHelper();
-
-        // create notification channel that can bypass dnd
-        // expected result: areChannelsBypassingDnd = true
-        NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel2.setBypassDnd(true);
-        mHelper.createNotificationChannel(PKG, UID, channel2, true, true);
-        assertTrue(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
-        resetZenModeHelper();
-
-        // delete channels
-        mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
-        assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
-        verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
-        resetZenModeHelper();
-
-        mHelper.deleteNotificationChannel(PKG, UID, channel2.getId());
-        assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
-        resetZenModeHelper();
-    }
-
-    @Test
-    public void testUpdateCanChannelsBypassDnd() throws Exception {
-        // create notification channel that can't bypass dnd
-        // expected result: areChannelsBypassingDnd = false
-        // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
-        NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-        assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
-        resetZenModeHelper();
-
-        // update channel so it CAN bypass dnd:
-        // expected result: areChannelsBypassingDnd = true
-        channel.setBypassDnd(true);
-        mHelper.updateNotificationChannel(PKG, UID, channel, true);
-        assertTrue(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
-        resetZenModeHelper();
-
-        // update channel so it can't bypass dnd:
-        // expected result: areChannelsBypassingDnd = false
-        channel.setBypassDnd(false);
-        mHelper.updateNotificationChannel(PKG, UID, channel, true);
-        assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
-        resetZenModeHelper();
-    }
-
-    @Test
-    public void testSetupNewZenModeHelper_canBypass() {
-        // start notification policy off with mAreChannelsBypassingDnd = true, but
-        // RankingHelper should change to false
-        mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
-                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
-        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new RankingHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mUsageStats, new String[] {ImportanceExtractor.class.getName()});
-        assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
-        resetZenModeHelper();
-    }
-
-    @Test
-    public void testSetupNewZenModeHelper_cannotBypass() {
-        // start notification policy off with mAreChannelsBypassingDnd = false
-        mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0);
-        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new RankingHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mUsageStats, new String[] {ImportanceExtractor.class.getName()});
-        assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
-        resetZenModeHelper();
-    }
-
-    @Test
-    public void testCreateDeletedChannel() throws Exception {
-        long[] vibration = new long[]{100, 67, 145, 156};
-        NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel.setVibrationPattern(vibration);
-
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-        mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
-
-        NotificationChannel newChannel = new NotificationChannel(
-                channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
-        newChannel.setVibrationPattern(new long[]{100});
-
-        mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
-
-        // No long deleted, using old settings
-        compareChannels(channel,
-                mHelper.getNotificationChannel(PKG, UID, newChannel.getId(), false));
-    }
-
-    @Test
-    public void testOnlyHasDefaultChannel() throws Exception {
-        assertTrue(mHelper.onlyHasDefaultChannel(PKG, UID));
-        assertFalse(mHelper.onlyHasDefaultChannel(UPDATED_PKG, UID2));
-
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
-        assertFalse(mHelper.onlyHasDefaultChannel(PKG, UID));
-    }
-
-    @Test
-    public void testCreateChannel_defaultChannelId() throws Exception {
-        try {
-            mHelper.createNotificationChannel(PKG, UID, new NotificationChannel(
-                    NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true, false);
-            fail("Allowed to create default channel");
-        } catch (IllegalArgumentException e) {
-            // pass
-        }
-    }
-
-    @Test
-    public void testCreateChannel_alreadyExists() throws Exception {
-        long[] vibration = new long[]{100, 67, 145, 156};
-        NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel.setVibrationPattern(vibration);
-
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-
-        NotificationChannel newChannel = new NotificationChannel(
-                channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
-        newChannel.setVibrationPattern(new long[]{100});
-
-        mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
-
-        // Old settings not overridden
-        compareChannels(channel,
-                mHelper.getNotificationChannel(PKG, UID, newChannel.getId(), false));
-    }
-
-    @Test
-    public void testCreateChannel_noOverrideSound() throws Exception {
-        Uri sound = new Uri.Builder().scheme("test").build();
-        final NotificationChannel channel = new NotificationChannel("id2", "name2",
-                 NotificationManager.IMPORTANCE_DEFAULT);
-        channel.setSound(sound, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-        assertEquals(sound, mHelper.getNotificationChannel(
-                PKG, UID, channel.getId(), false).getSound());
-    }
-
-    @Test
-    public void testPermanentlyDeleteChannels() throws Exception {
-        NotificationChannel channel1 =
-                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
-        NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-
-        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
-        mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
-
-        mHelper.permanentlyDeleteNotificationChannels(PKG, UID);
-
-        // Only default channel remains
-        assertEquals(1, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
-    }
-
-    @Test
-    public void testDeleteGroup() throws Exception {
-        NotificationChannelGroup notDeleted = new NotificationChannelGroup("not", "deleted");
-        NotificationChannelGroup deleted = new NotificationChannelGroup("totally", "deleted");
-        NotificationChannel nonGroupedNonDeletedChannel =
-                new NotificationChannel("no group", "so not deleted", IMPORTANCE_HIGH);
-        NotificationChannel groupedButNotDeleted =
-                new NotificationChannel("not deleted", "belongs to notDeleted", IMPORTANCE_DEFAULT);
-        groupedButNotDeleted.setGroup("not");
-        NotificationChannel groupedAndDeleted =
-                new NotificationChannel("deleted", "belongs to deleted", IMPORTANCE_DEFAULT);
-        groupedAndDeleted.setGroup("totally");
-
-        mHelper.createNotificationChannelGroup(PKG, UID, notDeleted, true);
-        mHelper.createNotificationChannelGroup(PKG, UID, deleted, true);
-        mHelper.createNotificationChannel(PKG, UID, nonGroupedNonDeletedChannel, true, false);
-        mHelper.createNotificationChannel(PKG, UID, groupedAndDeleted, true, false);
-        mHelper.createNotificationChannel(PKG, UID, groupedButNotDeleted, true, false);
-
-        mHelper.deleteNotificationChannelGroup(PKG, UID, deleted.getId());
-
-        assertNull(mHelper.getNotificationChannelGroup(deleted.getId(), PKG, UID));
-        assertNotNull(mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG, UID));
-
-        assertNull(mHelper.getNotificationChannel(PKG, UID, groupedAndDeleted.getId(), false));
-        compareChannels(groupedAndDeleted,
-                mHelper.getNotificationChannel(PKG, UID, groupedAndDeleted.getId(), true));
-
-        compareChannels(groupedButNotDeleted,
-                mHelper.getNotificationChannel(PKG, UID, groupedButNotDeleted.getId(), false));
-        compareChannels(nonGroupedNonDeletedChannel, mHelper.getNotificationChannel(
-                PKG, UID, nonGroupedNonDeletedChannel.getId(), false));
-
-        // notDeleted
-        assertEquals(1, mHelper.getNotificationChannelGroups(PKG, UID).size());
-
-        verify(mHandler, never()).requestSort();
-    }
-
-    @Test
-    public void testOnUserRemoved() throws Exception {
-        int[] user0Uids = {98, 235, 16, 3782};
-        int[] user1Uids = new int[user0Uids.length];
-        for (int i = 0; i < user0Uids.length; i++) {
-            user1Uids[i] = UserHandle.PER_USER_RANGE + user0Uids[i];
-
-            final ApplicationInfo legacy = new ApplicationInfo();
-            legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
-            when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
-
-            // create records with the default channel for all user 0 and user 1 uids
-            mHelper.getImportance(PKG, user0Uids[i]);
-            mHelper.getImportance(PKG, user1Uids[i]);
-        }
-
-        mHelper.onUserRemoved(1);
-
-        // user 0 records remain
-        for (int i = 0; i < user0Uids.length; i++) {
-            assertEquals(1,
-                    mHelper.getNotificationChannels(PKG, user0Uids[i], false).getList().size());
-        }
-        // user 1 records are gone
-        for (int i = 0; i < user1Uids.length; i++) {
-            assertEquals(0,
-                    mHelper.getNotificationChannels(PKG, user1Uids[i], false).getList().size());
-        }
-    }
-
-    @Test
-    public void testOnPackageChanged_packageRemoval() throws Exception {
-        // Deleted
-        NotificationChannel channel1 =
-                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
-        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
-
-        mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
-
-        assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
-
-        // Not deleted
-        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
-
-        mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
-        assertEquals(2, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
-    }
-
-    @Test
-    public void testOnPackageChanged_packageRemoval_importance() throws Exception {
-        mHelper.setImportance(PKG, UID, NotificationManager.IMPORTANCE_HIGH);
-
-        mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
-
-        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
-    }
-
-    @Test
-    public void testOnPackageChanged_packageRemoval_groups() throws Exception {
-        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
-        mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
-        NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
-        mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
-
-        mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
-
-        assertEquals(0,
-                mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList().size());
-    }
-
-    @Test
-    public void testOnPackageChange_downgradeTargetSdk() throws Exception {
-        // create channel as api 26
-        mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
-
-        // install new app version targeting 25
-        final ApplicationInfo legacy = new ApplicationInfo();
-        legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
-        when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(legacy);
-        mHelper.onPackagesChanged(
-                false, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2});
-
-        // make sure the default channel was readded
-        //assertEquals(2, mHelper.getNotificationChannels(UPDATED_PKG, UID2, false).getList().size());
-        assertNotNull(mHelper.getNotificationChannel(
-                UPDATED_PKG, UID2, NotificationChannel.DEFAULT_CHANNEL_ID, false));
-    }
-
-    @Test
-    public void testRecordDefaults() throws Exception {
-        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
-        assertEquals(true, mHelper.canShowBadge(PKG, UID));
-        assertEquals(1, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
-    }
-
-    @Test
-    public void testCreateGroup() throws Exception {
-        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
-        mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
-        assertEquals(ncg, mHelper.getNotificationChannelGroups(PKG, UID).iterator().next());
-        verify(mHandler, never()).requestSort();
-    }
-
-    @Test
-    public void testCannotCreateChannel_badGroup() throws Exception {
-        NotificationChannel channel1 =
-                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
-        channel1.setGroup("garbage");
-        try {
-            mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
-            fail("Created a channel with a bad group");
-        } catch (IllegalArgumentException e) {
-        }
-    }
-
-    @Test
-    public void testCannotCreateChannel_goodGroup() throws Exception {
-        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
-        mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
-        NotificationChannel channel1 =
-                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
-        channel1.setGroup(ncg.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
-
-        assertEquals(ncg.getId(),
-                mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false).getGroup());
-    }
-
-    @Test
-    public void testGetChannelGroups() throws Exception {
-        NotificationChannelGroup unused = new NotificationChannelGroup("unused", "s");
-        mHelper.createNotificationChannelGroup(PKG, UID, unused, true);
-        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
-        mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
-        NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
-        mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
-
-        NotificationChannel channel1 =
-                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
-        channel1.setGroup(ncg.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
-        NotificationChannel channel1a =
-                new NotificationChannel("id1a", "name1", NotificationManager.IMPORTANCE_HIGH);
-        channel1a.setGroup(ncg.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel1a, true, false);
-
-        NotificationChannel channel2 =
-                new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
-        channel2.setGroup(ncg2.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
-
-        NotificationChannel channel3 =
-                new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH);
-        mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
-
-        List<NotificationChannelGroup> actual =
-                mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
-        assertEquals(3, actual.size());
-        for (NotificationChannelGroup group : actual) {
-            if (group.getId() == null) {
-                assertEquals(2, group.getChannels().size()); // misc channel too
-                assertTrue(channel3.getId().equals(group.getChannels().get(0).getId())
-                        || channel3.getId().equals(group.getChannels().get(1).getId()));
-            } else if (group.getId().equals(ncg.getId())) {
-                assertEquals(2, group.getChannels().size());
-                if (group.getChannels().get(0).getId().equals(channel1.getId())) {
-                    assertTrue(group.getChannels().get(1).getId().equals(channel1a.getId()));
-                } else if (group.getChannels().get(0).getId().equals(channel1a.getId())) {
-                    assertTrue(group.getChannels().get(1).getId().equals(channel1.getId()));
-                } else {
-                    fail("expected channel not found");
-                }
-            } else if (group.getId().equals(ncg2.getId())) {
-                assertEquals(1, group.getChannels().size());
-                assertEquals(channel2.getId(), group.getChannels().get(0).getId());
-            }
-        }
-    }
-
-    @Test
-    public void testGetChannelGroups_noSideEffects() throws Exception {
-        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
-        mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
-
-        NotificationChannel channel1 =
-                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
-        channel1.setGroup(ncg.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
-        mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
-
-        channel1.setImportance(IMPORTANCE_LOW);
-        mHelper.updateNotificationChannel(PKG, UID, channel1, true);
-
-        List<NotificationChannelGroup> actual =
-                mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
-
-        assertEquals(2, actual.size());
-        for (NotificationChannelGroup group : actual) {
-            if (Objects.equals(group.getId(), ncg.getId())) {
-                assertEquals(1, group.getChannels().size());
-            }
-        }
-    }
-
-    @Test
-    public void testCreateChannel_updateName() throws Exception {
-        NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
-        mHelper.createNotificationChannel(PKG, UID, nc, true, false);
-        NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
-        assertEquals("hello", actual.getName());
-
-        nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH);
-        mHelper.createNotificationChannel(PKG, UID, nc, true, false);
-
-        actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
-        assertEquals("goodbye", actual.getName());
-        assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
-
-        verify(mHandler, times(1)).requestSort();
-    }
-
-    @Test
-    public void testCreateChannel_addToGroup() throws Exception {
-        NotificationChannelGroup group = new NotificationChannelGroup("group", "");
-        mHelper.createNotificationChannelGroup(PKG, UID, group, true);
-        NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
-        mHelper.createNotificationChannel(PKG, UID, nc, true, false);
-        NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
-        assertNull(actual.getGroup());
-
-        nc = new NotificationChannel("id", "hello", IMPORTANCE_HIGH);
-        nc.setGroup(group.getId());
-        mHelper.createNotificationChannel(PKG, UID, nc, true, false);
-
-        actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
-        assertNotNull(actual.getGroup());
-        assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
-
-        verify(mHandler, times(1)).requestSort();
-    }
-
-    @Test
-    public void testDumpChannelsJson() throws Exception {
-        final ApplicationInfo upgrade = new ApplicationInfo();
-        upgrade.targetSdkVersion = Build.VERSION_CODES.O;
-        try {
-            when(mPm.getApplicationInfoAsUser(
-                    anyString(), anyInt(), anyInt())).thenReturn(upgrade);
-        } catch (PackageManager.NameNotFoundException e) {
-        }
-        ArrayMap<String, Integer> expectedChannels = new ArrayMap<>();
-        int numPackages = ThreadLocalRandom.current().nextInt(1, 5);
-        for (int i = 0; i < numPackages; i++) {
-            String pkgName = "pkg" + i;
-            int numChannels = ThreadLocalRandom.current().nextInt(1, 10);
-            for (int j = 0; j < numChannels; j++) {
-                mHelper.createNotificationChannel(pkgName, UID,
-                        new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false);
-            }
-            expectedChannels.put(pkgName, numChannels);
-        }
-
-        // delete the first channel of the first package
-        String pkg = expectedChannels.keyAt(0);
-        mHelper.deleteNotificationChannel("pkg" + 0, UID, "0");
-        // dump should not include deleted channels
-        int count = expectedChannels.get(pkg);
-        expectedChannels.put(pkg, count - 1);
-
-        JSONArray actual = mHelper.dumpChannelsJson(new NotificationManagerService.DumpFilter());
-        assertEquals(numPackages, actual.length());
-        for (int i = 0; i < numPackages; i++) {
-            JSONObject object = actual.getJSONObject(i);
-            assertTrue(expectedChannels.containsKey(object.get("packageName")));
-            assertEquals(expectedChannels.get(object.get("packageName")).intValue(),
-                    object.getInt("channelCount"));
-        }
-    }
-
-    @Test
-    public void testBadgingOverrideTrue() throws Exception {
-        Secure.putIntForUser(getContext().getContentResolver(),
-                Secure.NOTIFICATION_BADGING, 1,
-                USER.getIdentifier());
-        mHelper.updateBadgingEnabled(); // would be called by settings observer
-        assertTrue(mHelper.badgingEnabled(USER));
-    }
-
-    @Test
-    public void testBadgingOverrideFalse() throws Exception {
-        Secure.putIntForUser(getContext().getContentResolver(),
-                Secure.NOTIFICATION_BADGING, 0,
-                USER.getIdentifier());
-        mHelper.updateBadgingEnabled(); // would be called by settings observer
-        assertFalse(mHelper.badgingEnabled(USER));
-    }
-
-    @Test
-    public void testBadgingForUserAll() throws Exception {
-        try {
-            mHelper.badgingEnabled(UserHandle.ALL);
-        } catch (Exception e) {
-            fail("just don't throw");
-        }
-    }
-
-    @Test
-    public void testBadgingOverrideUserIsolation() throws Exception {
-        Secure.putIntForUser(getContext().getContentResolver(),
-                Secure.NOTIFICATION_BADGING, 0,
-                USER.getIdentifier());
-        Secure.putIntForUser(getContext().getContentResolver(),
-                Secure.NOTIFICATION_BADGING, 1,
-                USER2.getIdentifier());
-        mHelper.updateBadgingEnabled(); // would be called by settings observer
-        assertFalse(mHelper.badgingEnabled(USER));
-        assertTrue(mHelper.badgingEnabled(USER2));
-    }
-
-    @Test
-    public void testOnLocaleChanged_updatesDefaultChannels() throws Exception {
-        String newLabel = "bananas!";
-        final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG, UID,
-                NotificationChannel.DEFAULT_CHANNEL_ID, false);
-        assertFalse(newLabel.equals(defaultChannel.getName()));
-
-        Resources res = mock(Resources.class);
-        when(mContext.getResources()).thenReturn(res);
-        when(res.getString(com.android.internal.R.string.default_notification_channel_label))
-                .thenReturn(newLabel);
-
-        mHelper.onLocaleChanged(mContext, USER.getIdentifier());
-
-        assertEquals(newLabel, mHelper.getNotificationChannel(PKG, UID,
-                NotificationChannel.DEFAULT_CHANNEL_ID, false).getName());
-    }
-
-    @Test
-    public void testIsGroupBlocked_noGroup() throws Exception {
-        assertFalse(mHelper.isGroupBlocked(PKG, UID, null));
-
-        assertFalse(mHelper.isGroupBlocked(PKG, UID, "non existent group"));
-    }
-
-    @Test
-    public void testIsGroupBlocked_notBlocked() throws Exception {
-        NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
-        mHelper.createNotificationChannelGroup(PKG, UID, group, true);
-
-        assertFalse(mHelper.isGroupBlocked(PKG, UID, group.getId()));
-    }
-
-    @Test
-    public void testIsGroupBlocked_blocked() throws Exception {
-        NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
-        mHelper.createNotificationChannelGroup(PKG, UID, group, true);
-        group.setBlocked(true);
-        mHelper.createNotificationChannelGroup(PKG, UID, group, false);
-
-        assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
-    }
-
-    @Test
-    public void testIsGroup_appCannotResetBlock() throws Exception {
-        NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
-        mHelper.createNotificationChannelGroup(PKG, UID, group, true);
-        NotificationChannelGroup group2 = group.clone();
-        group2.setBlocked(true);
-        mHelper.createNotificationChannelGroup(PKG, UID, group2, false);
-        assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
-
-        NotificationChannelGroup group3 = group.clone();
-        group3.setBlocked(false);
-        mHelper.createNotificationChannelGroup(PKG, UID, group3, true);
-        assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
-    }
-
-    @Test
-    public void testGetNotificationChannelGroupWithChannels() throws Exception {
-        NotificationChannelGroup group = new NotificationChannelGroup("group", "");
-        NotificationChannelGroup other = new NotificationChannelGroup("something else", "");
-        mHelper.createNotificationChannelGroup(PKG, UID, group, true);
-        mHelper.createNotificationChannelGroup(PKG, UID, other, true);
-
-        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
-        a.setGroup(group.getId());
-        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_DEFAULT);
-        b.setGroup(other.getId());
-        NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
-        c.setGroup(group.getId());
-        NotificationChannel d = new NotificationChannel("d", "d", IMPORTANCE_DEFAULT);
-
-        mHelper.createNotificationChannel(PKG, UID, a, true, false);
-        mHelper.createNotificationChannel(PKG, UID, b, true, false);
-        mHelper.createNotificationChannel(PKG, UID, c, true, false);
-        mHelper.createNotificationChannel(PKG, UID, d, true, false);
-        mHelper.deleteNotificationChannel(PKG, UID, c.getId());
-
-        NotificationChannelGroup retrieved = mHelper.getNotificationChannelGroupWithChannels(
-                PKG, UID, group.getId(), true);
-        assertEquals(2, retrieved.getChannels().size());
-        compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
-        compareChannels(c, findChannel(retrieved.getChannels(), c.getId()));
-
-        retrieved = mHelper.getNotificationChannelGroupWithChannels(
-                PKG, UID, group.getId(), false);
-        assertEquals(1, retrieved.getChannels().size());
-        compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
-    }
-
-    @Test
-    public void testAndroidPkgCannotBypassDnd_creation() {
-        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
-        test.setBypassDnd(true);
-
-        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
-
-        assertFalse(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
-                .canBypassDnd());
-    }
-
-    @Test
-    public void testDndPkgCanBypassDnd_creation() {
-        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
-        test.setBypassDnd(true);
-
-        mHelper.createNotificationChannel(PKG, UID, test, true, true);
-
-        assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
-    }
-
-    @Test
-    public void testNormalPkgCannotBypassDnd_creation() {
-        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
-        test.setBypassDnd(true);
-
-        mHelper.createNotificationChannel(PKG, 1000, test, true, false);
-
-        assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
-    }
-
-    @Test
-    public void testAndroidPkgCannotBypassDnd_update() throws Exception {
-        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
-
-        NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
-        update.setBypassDnd(true);
-        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true, false);
-
-        assertFalse(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
-                .canBypassDnd());
-    }
-
-    @Test
-    public void testDndPkgCanBypassDnd_update() throws Exception {
-        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG, UID, test, true, true);
-
-        NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
-        update.setBypassDnd(true);
-        mHelper.createNotificationChannel(PKG, UID, update, true, true);
-
-        assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
-    }
-
-    @Test
-    public void testNormalPkgCannotBypassDnd_update() {
-        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG, 1000, test, true, false);
-        NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
-        update.setBypassDnd(true);
-        mHelper.createNotificationChannel(PKG, 1000, update, true, false);
-        assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
-    }
-
-    @Test
-    public void testGetBlockedAppCount_noApps() {
-        assertEquals(0, mHelper.getBlockedAppCount(0));
-    }
-
-    @Test
-    public void testGetBlockedAppCount_noAppsForUserId() {
-        mHelper.setEnabled(PKG, 100, false);
-        assertEquals(0, mHelper.getBlockedAppCount(9));
-    }
-
-    @Test
-    public void testGetBlockedAppCount_appsForUserId() {
-        mHelper.setEnabled(PKG, 1020, false);
-        mHelper.setEnabled(PKG, 1030, false);
-        mHelper.setEnabled(PKG, 1060, false);
-        mHelper.setEnabled(PKG, 1000, true);
-        assertEquals(3, mHelper.getBlockedAppCount(0));
-    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 942a07a..96ac935 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -121,7 +121,7 @@
         cal.set(Calendar.MINUTE, 15);
         cal.set(Calendar.SECOND, 0);
         cal.set(Calendar.MILLISECOND, 0);
-        mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+        mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
         mScheduleInfo.startHour = 1;
         mScheduleInfo.endHour = 3;
         mScheduleInfo.startMinute = 15;
@@ -149,7 +149,7 @@
         cal.set(Calendar.MINUTE, 15);
         cal.set(Calendar.SECOND, 0);
         cal.set(Calendar.MILLISECOND, 0);
-        mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+        mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
         mScheduleInfo.startHour = 22;
         mScheduleInfo.endHour = 3;
         mScheduleInfo.startMinute = 15;
@@ -250,7 +250,7 @@
         calAlarm.add(Calendar.DATE, 1); // add a day
 
         // ScheduleInfo: day 1, day 2: 9pm-7am
-        mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+        mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
         mScheduleInfo.startHour = 21;
         mScheduleInfo.endHour = 7;
         mScheduleInfo.startMinute = 0;
@@ -418,7 +418,7 @@
         now.set(Calendar.MILLISECOND, 0);
         now.add(Calendar.DATE, 1); // add a day
 
-        mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+        mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
         mScheduleInfo.startHour = 22;
         mScheduleInfo.startMinute = 15;
         mScheduleInfo.endHour = 3;
@@ -446,7 +446,7 @@
         now.set(Calendar.MILLISECOND, 0);
         now.add(Calendar.DATE, 1); // add a day
 
-        mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+        mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
         mScheduleInfo.startHour = 22;
         mScheduleInfo.startMinute = 15;
         mScheduleInfo.endHour = 3;
@@ -464,4 +464,10 @@
     private int getTodayDay() {
         return new GregorianCalendar().get(Calendar.DAY_OF_WEEK);
     }
+
+    private int getTodayDay(int offset) {
+        Calendar cal = new GregorianCalendar();
+        cal.add(Calendar.DATE, offset);
+        return cal.get(Calendar.DAY_OF_WEEK);
+    }
 }
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 afc1263..24beb59 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -116,7 +116,7 @@
         mZenModeHelperSpy.writeXml(serializer, forBackup, version);
         serializer.endDocument();
         serializer.flush();
-        mZenModeHelperSpy.setConfig(new ZenModeConfig(), "writing xml");
+        mZenModeHelperSpy.setConfig(new ZenModeConfig(), null, "writing xml");
         return baos;
     }
 
@@ -618,7 +618,7 @@
     public void testReadXml() throws Exception {
         setupZenConfig();
 
-        // automatic zen rule is enabled on upgrade so rules should not be overriden by default
+        // automatic zen rule is enabled on upgrade so rules should not be overriden to default
         ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>();
         ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
         final ScheduleInfo weeknights = new ScheduleInfo();
@@ -648,7 +648,7 @@
         String xml = "<zen version=\"6\" user=\"0\">\n"
                 + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
                 + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
-                + "visualScreenOff=\"false\" alarms=\"true\" "
+                + "visualScreenOff=\"true\" alarms=\"true\" "
                 + "media=\"true\" system=\"false\" />\n"
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
@@ -664,7 +664,7 @@
         xml = "<zen version=\"6\" user=\"0\">\n"
                 + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
                 + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
-                + "visualScreenOn=\"false\" alarms=\"true\" "
+                + "visualScreenOn=\"true\" alarms=\"true\" "
                 + "media=\"true\" system=\"false\" />\n"
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
@@ -683,7 +683,7 @@
         String xml = "<zen version=\"6\" user=\"0\">\n"
                 + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
                 + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
-                + "visualScreenOff=\"false\" visualScreenOn=\"false\" alarms=\"true\" "
+                + "visualScreenOff=\"true\" visualScreenOn=\"true\" alarms=\"true\" "
                 + "media=\"true\" system=\"false\" />\n"
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
@@ -702,7 +702,7 @@
         String xml = "<zen version=\"6\" user=\"0\">\n"
                 + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
                 + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
-                + "visualScreenOff=\"true\" visualScreenOn=\"true\" alarms=\"true\" "
+                + "visualScreenOff=\"false\" visualScreenOn=\"false\" alarms=\"true\" "
                 + "media=\"true\" system=\"false\" />\n"
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
@@ -721,7 +721,7 @@
         xml = "<zen version=\"6\" user=\"0\">\n"
                 + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
                 + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
-                + "visualScreenOff=\"false\" visualScreenOn=\"true\" alarms=\"true\" "
+                + "visualScreenOff=\"true\" visualScreenOn=\"false\" alarms=\"true\" "
                 + "media=\"true\" system=\"false\" />\n"
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
@@ -737,7 +737,7 @@
         xml = "<zen version=\"6\" user=\"0\">\n"
                 + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
                 + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
-                + "visualScreenOff=\"true\" visualScreenOn=\"false\" alarms=\"true\" "
+                + "visualScreenOff=\"false\" visualScreenOn=\"true\" alarms=\"true\" "
                 + "media=\"true\" system=\"false\" />\n"
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
@@ -756,7 +756,8 @@
     public void testReadXmlResetDefaultRules() throws Exception {
         setupZenConfig();
 
-        // no enabled automatic zen rule, so rules should be overriden by default rules
+        // no enabled automatic zen rules and no default rules
+        // so rules should be overriden by default rules
         mZenModeHelperSpy.mConfig.automaticRules = new ArrayMap<>();
 
         // set previous version
@@ -782,8 +783,8 @@
     public void testReadXmlAllDisabledRulesResetDefaultRules() throws Exception {
         setupZenConfig();
 
-        // all automatic zen rules are diabled on upgrade so rules should be overriden by default
-        // rules
+        // all automatic zen rules are diabled on upgrade (and default rules don't already exist)
+        // so rules should be overriden by default rules
         ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>();
         ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
         final ScheduleInfo weeknights = new ScheduleInfo();
@@ -813,6 +814,108 @@
         setupZenConfigMaintained();
     }
 
+    @Test
+    public void testReadXmlOnlyOneDefaultRuleExists() throws Exception {
+        setupZenConfig();
+
+        // all automatic zen rules are disabled on upgrade and only one default rule exists
+        // so rules should be overriden to the default rules
+        ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
+        ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
+        final ScheduleInfo customRuleInfo = new ScheduleInfo();
+        customRule.enabled = false;
+        customRule.name = "Custom Rule";
+        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+        automaticRules.put("customRule", customRule);
+
+        ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule();
+        final ScheduleInfo defaultScheduleRuleInfo = new ScheduleInfo();
+        defaultScheduleRule.enabled = false;
+        defaultScheduleRule.name = "Default Schedule Rule";
+        defaultScheduleRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        defaultScheduleRule.conditionId = ZenModeConfig.toScheduleConditionId(
+                defaultScheduleRuleInfo);
+        defaultScheduleRule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
+        automaticRules.put(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, defaultScheduleRule);
+
+        mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
+
+        // set previous version
+        ByteArrayOutputStream baos = writeXmlAndPurge(false, 5);
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        mZenModeHelperSpy.readXml(parser, false);
+
+        // check default rules
+        ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelperSpy.mConfig.automaticRules;
+        assertTrue(rules.size() != 0);
+        for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) {
+            assertTrue(rules.containsKey(defaultId));
+        }
+        assertFalse(rules.containsKey("customRule"));
+
+        setupZenConfigMaintained();
+    }
+
+
+    @Test
+    public void testReadXmlDefaultRulesExist() throws Exception {
+        setupZenConfig();
+
+        // Default rules exist so rules should not be overridden by defaults
+        ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
+        ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
+        final ScheduleInfo customRuleInfo = new ScheduleInfo();
+        customRule.enabled = false;
+        customRule.name = "Custom Rule";
+        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
+        automaticRules.put("customRule", customRule);
+
+        ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule();
+        final ScheduleInfo defaultScheduleRuleInfo = new ScheduleInfo();
+        defaultScheduleRule.enabled = false;
+        defaultScheduleRule.name = "Default Schedule Rule";
+        defaultScheduleRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        defaultScheduleRule.conditionId = ZenModeConfig.toScheduleConditionId(
+                defaultScheduleRuleInfo);
+        defaultScheduleRule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
+        automaticRules.put(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, defaultScheduleRule);
+
+        ZenModeConfig.ZenRule defaultEventRule = new ZenModeConfig.ZenRule();
+        final ScheduleInfo defaultEventRuleInfo = new ScheduleInfo();
+        defaultEventRule.enabled = false;
+        defaultEventRule.name = "Default Event Rule";
+        defaultEventRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        defaultEventRule.conditionId = ZenModeConfig.toScheduleConditionId(
+                defaultEventRuleInfo);
+        defaultEventRule.id = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
+        automaticRules.put(ZenModeConfig.EVENTS_DEFAULT_RULE_ID, defaultEventRule);
+
+        mZenModeHelperSpy.mConfig.automaticRules = automaticRules;
+
+        // set previous version
+        ByteArrayOutputStream baos = writeXmlAndPurge(false, 5);
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        mZenModeHelperSpy.readXml(parser, false);
+
+        // check default rules
+        ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelperSpy.mConfig.automaticRules;
+        assertTrue(rules.size() != 0);
+        for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) {
+            assertTrue(rules.containsKey(defaultId));
+        }
+        assertTrue(rules.containsKey("customRule"));
+
+        setupZenConfigMaintained();
+    }
+
     private void setupZenConfig() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowAlarms = false;
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index 3c4e333..82e0fbe 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -1,16 +1,16 @@
 package com.android.server.slice;
 
+import static android.testing.TestableContentResolver.UNSTABLE;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 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.argThat;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -71,11 +71,12 @@
         mSliceService = mock(SliceManagerService.class);
         when(mSliceService.getContext()).thenReturn(mContext);
         when(mSliceService.getLock()).thenReturn(new Object());
-        when(mSliceService.getHandler()).thenReturn(new Handler(TestableLooper.get(this).getLooper()));
+        when(mSliceService.getHandler()).thenReturn(
+                new Handler(TestableLooper.get(this).getLooper()));
         mContentProvider = mock(ContentProvider.class);
         mIContentProvider = mock(IContentProvider.class);
         when(mContentProvider.getIContentProvider()).thenReturn(mIContentProvider);
-        mContext.getContentResolver().addProvider(AUTH, mContentProvider);
+        mContext.getContentResolver().addProvider(AUTH, mContentProvider, UNSTABLE);
         mPinnedSliceManager = new PinnedSliceState(mSliceService, TEST_URI, "pkg");
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 43a4e27..6d4f5f8 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -18,6 +18,7 @@
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -59,7 +60,7 @@
 public class SliceManagerServiceTest extends UiServiceTestCase {
 
     private static final String AUTH = "com.android.services.uitests";
-    private static final Uri TEST_URI = maybeAddUserId(Uri.parse("content://" + AUTH + "/path"), 0);
+    private static final Uri TEST_URI = Uri.parse("content://" + AUTH + "/path");
 
     private static final SliceSpec[] EMPTY_SPECS = new SliceSpec[]{
     };
@@ -93,7 +94,7 @@
 
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
-        verify(mService, times(1)).createPinnedSlice(eq(TEST_URI), anyString());
+        verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString());
     }
 
     @Test
@@ -119,11 +120,27 @@
                 .thenReturn(PERMISSION_DENIED);
         when(mContextSpy.checkPermission("perm2", Process.myPid(), Process.myUid()))
                 .thenReturn(PERMISSION_GRANTED);
-        mService.checkSlicePermission(TEST_URI, mContext.getPackageName(), Process.myPid(),
+        mService.checkSlicePermission(TEST_URI, mContext.getPackageName(),
+                mContext.getPackageName(), Process.myPid(),
                 Process.myUid(), testPerms);
 
         verify(mContextSpy).checkPermission(eq("perm1"), eq(Process.myPid()), eq(Process.myUid()));
         verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid()));
     }
 
+    @Test(expected = IllegalStateException.class)
+    public void testNoPinThrow() throws Exception {
+        mService.getPinnedSpecs(TEST_URI, "pkg");
+    }
+
+    @Test
+    public void testGetPinnedSpecs() throws Exception {
+        SliceSpec[] specs = new SliceSpec[] {
+            new SliceSpec("Something", 1) };
+        mService.pinSlice("pkg", TEST_URI, specs, mToken);
+
+        when(mCreatedSliceState.getSpecs()).thenReturn(specs);
+        assertEquals(specs, mService.getPinnedSpecs(TEST_URI, "pkg"));
+    }
+
 }
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 9c62700..77cb749 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -1452,6 +1452,23 @@
         TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
         pw.println();
 
+        pw.print("  mStrongUsageTimeoutMillis=");
+        TimeUtils.formatDuration(mStrongUsageTimeoutMillis, pw);
+        pw.println();
+        pw.print("  mNotificationSeenTimeoutMillis=");
+        TimeUtils.formatDuration(mNotificationSeenTimeoutMillis, pw);
+        pw.println();
+        pw.print("  mSyncAdapterTimeoutMillis=");
+        TimeUtils.formatDuration(mSyncAdapterTimeoutMillis, pw);
+        pw.println();
+        pw.print("  mSystemInteractionTimeoutMillis=");
+        TimeUtils.formatDuration(mSystemInteractionTimeoutMillis, pw);
+        pw.println();
+
+        pw.print("  mPredictionTimeoutMillis=");
+        TimeUtils.formatDuration(mPredictionTimeoutMillis, pw);
+        pw.println();
+
         pw.print("  mExemptedSyncScheduledNonDozeTimeoutMillis=");
         TimeUtils.formatDuration(mExemptedSyncScheduledNonDozeTimeoutMillis, pw);
         pw.println();
@@ -1462,6 +1479,14 @@
         TimeUtils.formatDuration(mExemptedSyncStartTimeoutMillis, pw);
         pw.println();
 
+        pw.print("  mSystemUpdateUsageTimeoutMillis=");
+        TimeUtils.formatDuration(mSystemUpdateUsageTimeoutMillis, pw);
+        pw.println();
+
+        pw.print("  mStableChargingThresholdMillis=");
+        TimeUtils.formatDuration(mStableChargingThresholdMillis, pw);
+        pw.println();
+
         pw.println();
         pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
         pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
@@ -1910,4 +1935,3 @@
         }
     }
 }
-
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 0dce738..4b7e21f 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -27,7 +27,9 @@
 
 import java.util.List;
 
-class IntervalStats {
+import com.android.internal.annotations.VisibleForTesting;
+
+public class IntervalStats {
     public long beginTime;
     public long endTime;
     public long lastTimeSaved;
@@ -149,7 +151,11 @@
                 && eventType != UsageEvents.Event.STANDBY_BUCKET_CHANGED;
     }
 
-    void update(String packageName, long timeStamp, int eventType) {
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public void update(String packageName, long timeStamp, int eventType) {
         UsageStats usageStats = getOrCreateUsageStats(packageName);
 
         // TODO(adamlesinski): Ensure that we recover from incorrect event sequences
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 9705469..5ab5dc2 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -42,7 +42,7 @@
 /**
  * Provides an interface to query for UsageStat data from an XML database.
  */
-class UsageStatsDatabase {
+public class UsageStatsDatabase {
     private static final int CURRENT_VERSION = 3;
 
     // Current version of the backup schema
@@ -369,7 +369,7 @@
     /**
      * Figures out what to extract from the given IntervalStats object.
      */
-    interface StatCombiner<T> {
+    public interface StatCombiner<T> {
 
         /**
          * Implementations should extract interesting from <code>stats</code> and add it
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 1d5eece..dd1ddfa 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -712,13 +712,13 @@
 
             if (mPackageManagerInternal.getPackageUid(pkg, /*flags=*/ 0,
                     callingUserId) != callingUid) {
-                throw new SecurityException("Calling uid " + pkg + " cannot query events"
+                throw new SecurityException("Calling uid " + callingUid + " cannot query events"
                         + "for package " + pkg);
             }
         }
 
         private boolean isCallingUidSystem() {
-            final int uid = Binder.getCallingUid();
+            final int uid = UserHandle.getAppId(Binder.getCallingUid()); // ignores user
             return uid == Process.SYSTEM_UID;
         }
 
@@ -866,7 +866,7 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 return UsageStatsService.this.queryEventsForPackage(userId, beginTime,
-                        endTime, callingPackage);
+                        endTime, pkg);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -924,8 +924,7 @@
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
-            final int packageUid = mPackageManagerInternal.getPackageUid(packageName,
-                    PackageManager.MATCH_ANY_USER, userId);
+            final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
             // If the calling app is asking about itself, continue, else check for permission.
             if (packageUid != callingUid) {
                 if (!hasPermission(callingPackage)) {
@@ -974,7 +973,8 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 final int packageUid = mPackageManagerInternal.getPackageUid(packageName,
-                        PackageManager.MATCH_ANY_USER, userId);
+                        PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                        | PackageManager.MATCH_DIRECT_BOOT_AWARE, userId);
                 // Caller cannot set their own standby state
                 if (packageUid == callingUid) {
                     throw new IllegalArgumentException("Cannot set your own standby bucket");
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index aa832ad..6a1e97a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -68,6 +68,7 @@
     private static final String SHORTCUT_ID_ATTR = "shortcutId";
     private static final String STANDBY_BUCKET_ATTR = "standbyBucket";
     private static final String APP_LAUNCH_COUNT_ATTR = "appLaunchCount";
+    private static final String NOTIFICATION_CHANNEL_ATTR = "notificationChannel";
 
     // Time attributes stored as an offset of the beginTime.
     private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
@@ -189,6 +190,11 @@
             case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
                 event.mBucketAndReason = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
                 break;
+            case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+                final String channelId =
+                        XmlUtils.readStringAttribute(parser, NOTIFICATION_CHANNEL_ATTR);
+                event.mNotificationChannelId = (channelId != null) ? channelId.intern() : null;
+                break;
         }
 
         if (statsOut.events == null) {
@@ -307,6 +313,13 @@
                 if (event.mBucketAndReason != 0) {
                     XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucketAndReason);
                 }
+                break;
+            case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+                if (event.mNotificationChannelId != null) {
+                    XmlUtils.writeStringAttribute(
+                            xml, NOTIFICATION_CHANNEL_ATTR, event.mNotificationChannelId);
+                }
+                break;
         }
 
         xml.endTag(null, EVENT_TAG);
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 9cb98f3..9b194e9 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -64,6 +64,9 @@
     private String mLastBackgroundedPackage;
     private final int mUserId;
 
+    // STOPSHIP: Temporary member variable for debugging b/110930764.
+    private UsageEvents.Event mLastEvent;
+
     private static final long[] INTERVAL_LENGTH = new long[] {
             UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS,
             UnixCalendar.MONTH_IN_MILLIS, UnixCalendar.YEAR_IN_MILLIS
@@ -156,6 +159,8 @@
                     + eventToString(event.mEventType));
         }
 
+        mLastEvent = new UsageEvents.Event(event);
+
         if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
             // Need to rollover
             rolloverStats(event.mTimeStamp);
@@ -306,6 +311,36 @@
                 Slog.d(TAG, mLogPrefix + "Requesting stats after " + beginTime + " but latest is "
                         + currentStats.endTime);
             }
+
+            // STOPSHIP: Temporary logging for b/110930764.
+            if (intervalType == UsageStatsManager.INTERVAL_DAILY
+                    && mLastEvent != null && mLastEvent.mTimeStamp >= beginTime) {
+                final IntervalStats diskStats = mDatabase.getLatestUsageStats(
+                        UsageStatsManager.INTERVAL_DAILY);
+                StringBuilder sb = new StringBuilder(256);
+                sb.append("Last 24 hours of UsageStats missing! timeRange : ");
+                sb.append(beginTime);
+                sb.append(", ");
+                sb.append(endTime);
+                sb.append("\nLast reported Usage Event time : ");
+                sb.append(mLastEvent.mTimeStamp);
+                if (currentStats == null) {
+                    sb.append("\nNo in memory event stats available.");
+                } else {
+                    sb.append("\nLast in memory event time : ");
+                    sb.append(currentStats.endTime);
+                    sb.append("\nLast save time: ");
+                    sb.append(currentStats.lastTimeSaved);
+                }
+                if (diskStats == null) {
+                    sb.append("\nNo on disk event stats available.");
+                } else {
+                    sb.append("\nLast on disk event time : ");
+                    sb.append(diskStats.endTime);
+                }
+                Slog.wtf(TAG, sb.toString());
+            }
+
             // Nothing newer available.
             return null;
         }
@@ -604,6 +639,9 @@
             pw.printPair("standbyBucket", event.getStandbyBucket());
             pw.printPair("reason", UsageStatsManager.reasonToString(event.getStandbyReason()));
         }
+        if (event.mNotificationChannelId != null) {
+            pw.printPair("channelId", event.mNotificationChannelId);
+        }
         pw.printHexPair("flags", event.mFlags);
         pw.println();
     }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 9f8042c..63945a9 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -20,7 +20,8 @@
 import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
 
 import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
+
+import com.android.server.wm.ActivityTaskManagerInternal;
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -98,7 +99,7 @@
 /**
  * UsbDeviceManager manages USB state in device mode.
  */
-public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver {
+public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObserver {
 
     private static final String TAG = UsbDeviceManager.class.getSimpleName();
     private static final boolean DEBUG = false;
@@ -386,7 +387,7 @@
     public void systemReady() {
         if (DEBUG) Slog.d(TAG, "systemReady");
 
-        LocalServices.getService(ActivityManagerInternal.class).registerScreenObserver(this);
+        LocalServices.getService(ActivityTaskManagerInternal.class).registerScreenObserver(this);
 
         mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
     }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
index 3fc5fe3..ff67667 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
@@ -126,6 +126,9 @@
     public static final int REQUEST_GET_CONFIGURATION  = 0x08;
     public static final int REQUEST_SET_CONFIGURATION  = 0x09;
 
+    // USB control transfer timeout
+    public static final int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
+
     /**
      * @throws IllegalArgumentException
      */
@@ -224,7 +227,7 @@
                         0,
                         sStringBuffer,
                         0xFF,
-                        0);
+                        USB_CONTROL_TRANSFER_TIMEOUT_MS);
                 if (rdo >= 0) {
                     usbStr = new String(sStringBuffer, 2, rdo - 2, "UTF-16LE");
                 } else {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 44f5551..c5d6dc7 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -19,6 +19,7 @@
 import android.Manifest;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -88,6 +89,7 @@
     final ContentResolver mResolver;
     final DatabaseHelper mDbHelper;
     final ActivityManagerInternal mAmInternal;
+    final ActivityTaskManagerInternal mAtmInternal;
     final UserManager mUserManager;
     final ArraySet<Integer> mLoadedKeyphraseIds = new ArraySet<>();
     ShortcutServiceInternal mShortcutServiceInternal;
@@ -104,6 +106,8 @@
         mServiceStub = new VoiceInteractionManagerServiceStub();
         mAmInternal = Preconditions.checkNotNull(
                 LocalServices.getService(ActivityManagerInternal.class));
+        mAtmInternal = Preconditions.checkNotNull(
+                LocalServices.getService(ActivityTaskManagerInternal.class));
         mUserManager = Preconditions.checkNotNull(
                 context.getSystemService(UserManager.class));
 
@@ -212,7 +216,7 @@
 
                             @Override
                             public void onShown() {
-                                mAmInternal.onLocalVoiceInteractionStarted(token,
+                                mAtmInternal.onLocalVoiceInteractionStarted(token,
                                         mImpl.mActiveSession.mSession,
                                         mImpl.mActiveSession.mInteractor);
                             }
@@ -429,11 +433,11 @@
                     if (hasComponent) {
                         mShortcutServiceInternal.setShortcutHostPackage(TAG,
                                 serviceComponent.getPackageName(), mCurUser);
-                        mAmInternal.setAllowAppSwitches(TAG,
+                        mAtmInternal.setAllowAppSwitches(TAG,
                                 serviceInfo.applicationInfo.uid, mCurUser);
                     } else {
                         mShortcutServiceInternal.setShortcutHostPackage(TAG, null, mCurUser);
-                        mAmInternal.setAllowAppSwitches(TAG, -1, mCurUser);
+                        mAtmInternal.setAllowAppSwitches(TAG, -1, mCurUser);
                     }
                 }
 
@@ -1181,7 +1185,7 @@
 
         private void setImplLocked(VoiceInteractionManagerServiceImpl impl) {
             mImpl = impl;
-            mAmInternal.notifyActiveVoiceInteractionServiceChanged(
+            mAtmInternal.notifyActiveVoiceInteractionServiceChanged(
                     getActiveServiceComponentName());
         }
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 7541b92..57e9f66 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -23,10 +23,11 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 
 import android.app.ActivityManager;
-import android.app.ActivityManager.StackId;
-import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -48,7 +49,6 @@
 import android.util.Slog;
 import android.view.IWindowManager;
 
-import com.android.internal.app.IVoiceInteractionSessionListener;
 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.server.LocalServices;
@@ -71,6 +71,7 @@
     final int mUser;
     final ComponentName mComponent;
     final IActivityManager mAm;
+    final IActivityTaskManager mAtm;
     final VoiceInteractionServiceInfo mInfo;
     final ComponentName mSessionComponentName;
     final IWindowManager mIWindowManager;
@@ -126,6 +127,7 @@
         mUser = userHandle;
         mComponent = service;
         mAm = ActivityManager.getService();
+        mAtm = ActivityTaskManager.getService();
         VoiceInteractionServiceInfo info;
         try {
             info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser);
@@ -168,7 +170,7 @@
             activityTokens.add(activityToken);
         } else {
             // Let's get top activities from all visible stacks
-            activityTokens = LocalServices.getService(ActivityManagerInternal.class)
+            activityTokens = LocalServices.getService(ActivityTaskManagerInternal.class)
                     .getTopVisibleActivities();
         }
         return mActiveSession.showLocked(args, flags, mDisabledShowContext, showCallback,
@@ -206,7 +208,7 @@
             intent = new Intent(intent);
             intent.addCategory(Intent.CATEGORY_VOICE);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
-            return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid,
+            return mAtm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid,
                     intent, resolvedType, mActiveSession.mSession, mActiveSession.mInteractor,
                     0, null, null, mUser);
         } catch (RemoteException e) {
@@ -229,7 +231,7 @@
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             final ActivityOptions options = ActivityOptions.makeBasic();
             options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT);
-            return mAm.startAssistantActivity(mComponent.getPackageName(), callingPid, callingUid,
+            return mAtm.startAssistantActivity(mComponent.getPackageName(), callingPid, callingUid,
                     intent, resolvedType, options.toBundle(), mUser);
         } catch (RemoteException e) {
             throw new IllegalStateException("Unexpected remote error", e);
@@ -242,7 +244,7 @@
                 Slog.w(TAG, "setKeepAwake does not match active session");
                 return;
             }
-            mAm.setVoiceKeepAwake(mActiveSession.mSession, keepAwake);
+            mAtm.setVoiceKeepAwake(mActiveSession.mSession, keepAwake);
         } catch (RemoteException e) {
             throw new IllegalStateException("Unexpected remote error", e);
         }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index e0d9c73..57729b5 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -16,9 +16,9 @@
 
 package com.android.server.voiceinteraction;
 
-import static android.app.ActivityManagerInternal.ASSIST_KEY_CONTENT;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
+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 static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT;
 import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
 import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
@@ -27,8 +27,10 @@
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
+import android.app.UriGrantsManager;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.content.ClipData;
@@ -61,6 +63,7 @@
 import com.android.server.am.AssistDataRequester;
 import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
 import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -81,6 +84,7 @@
     final int mCallingUid;
     final Handler mHandler;
     final IActivityManager mAm;
+    final UriGrantsManagerInternal mUgmInternal;
     final IWindowManager mIWindowManager;
     final AppOpsManager mAppOps;
     final IBinder mPermissionOwner;
@@ -140,19 +144,15 @@
         mCallingUid = callingUid;
         mHandler = handler;
         mAm = ActivityManager.getService();
+        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
         mIWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService(Context.WINDOW_SERVICE));
         mAppOps = context.getSystemService(AppOpsManager.class);
-        mAssistDataRequester = new AssistDataRequester(mContext, mAm, mIWindowManager,
+        mAssistDataRequester = new AssistDataRequester(mContext, mIWindowManager,
                 (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE),
                 this, mLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
-        IBinder permOwner = null;
-        try {
-            permOwner = mAm.newUriPermissionOwner("voicesession:"
+        final IBinder permOwner = mUgmInternal.newUriPermissionOwner("voicesession:"
                     + component.flattenToShortString());
-        } catch (RemoteException e) {
-            Slog.w("voicesession", "AM dead", e);
-        }
         mPermissionOwner = permOwner;
         mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
         mBindIntent.setComponent(mSessionComponentName);
@@ -302,13 +302,14 @@
         long ident = Binder.clearCallingIdentity();
         try {
             // This will throw SecurityException for us.
-            mAm.checkGrantUriPermission(srcUid, null, ContentProvider.getUriWithoutUserId(uri),
-                    mode, ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(srcUid)));
+            mUgmInternal.checkGrantUriPermission(srcUid, null,
+                    ContentProvider.getUriWithoutUserId(uri), mode,
+                    ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(srcUid)));
             // No security exception, do the grant.
             int sourceUserId = ContentProvider.getUserIdFromUri(uri, mUser);
             uri = ContentProvider.getUriWithoutUserId(uri);
-            mAm.grantUriPermissionFromOwner(mPermissionOwner, srcUid, destPkg,
-                    uri, FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser);
+            UriGrantsManager.getService().grantUriPermissionFromOwner(mPermissionOwner, srcUid,
+                    destPkg, uri, FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser);
         } catch (RemoteException e) {
         } catch (SecurityException e) {
             Slog.w(TAG, "Can't propagate permission", e);
@@ -351,15 +352,11 @@
                     } catch (RemoteException e) {
                     }
                 }
-                try {
-                    mAm.revokeUriPermissionFromOwner(mPermissionOwner, null,
-                            FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION,
-                            mUser);
-                } catch (RemoteException e) {
-                }
+                mUgmInternal.revokeUriPermissionFromOwner(mPermissionOwner, null,
+                        FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION, mUser);
                 if (mSession != null) {
                     try {
-                        mAm.finishVoiceTask(mSession);
+                        ActivityTaskManager.getService().finishVoiceTask(mSession);
                     } catch (RemoteException e) {
                     }
                 }
@@ -387,7 +384,7 @@
             }
             if (finishTask && mSession != null) {
                 try {
-                    mAm.finishVoiceTask(mSession);
+                    ActivityTaskManager.getService().finishVoiceTask(mSession);
                 } catch (RemoteException e) {
                 }
             }
diff --git a/telecomm/java/android/telecom/AudioState.java b/telecomm/java/android/telecom/AudioState.java
index 33013ac..eb202a7 100644
--- a/telecomm/java/android/telecom/AudioState.java
+++ b/telecomm/java/android/telecom/AudioState.java
@@ -17,6 +17,7 @@
 package android.telecom;
 
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -53,8 +54,11 @@
     private static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
             ROUTE_SPEAKER;
 
+    @UnsupportedAppUsage
     private final boolean isMuted;
+    @UnsupportedAppUsage
     private final int route;
+    @UnsupportedAppUsage
     private final int supportedRouteMask;
 
     public AudioState(boolean muted, int route, int supportedRouteMask) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 1c0e260..096cf37 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -308,6 +309,7 @@
          * Call can be upgraded to a video call.
          * @hide
          */
+        @UnsupportedAppUsage
         public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
 
         /**
diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java
new file mode 100644
index 0000000..9c874bf
--- /dev/null
+++ b/telecomm/java/android/telecom/CallRedirectionService.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.telecom;
+
+import android.annotation.SdkConstant;
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecom.ICallRedirectionService;
+import com.android.internal.telecom.ICallRedirectionAdapter;
+
+/**
+ * This service can be implemented to interact between Telecom and its implementor
+ * for making outgoing call with optional redirection/cancellation purposes.
+ *
+ * <p>
+ * Below is an example manifest registration for a {@code CallRedirectionService}.
+ * <pre>
+ * {@code
+ * <service android:name="your.package.YourCallRedirectionServiceImplementation"
+ *          android:permission="android.permission.BIND_REDIRECTION_SERVICE">
+ *      <intent-filter>
+ *          <action android:name="android.telecom.CallRedirectionService"/>
+ *      </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ */
+public abstract class CallRedirectionService extends Service {
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE = "android.telecom.CallRedirectionService";
+
+    /**
+     * An adapter to inform Telecom the response from the implementor of the Call
+     * Redirection service
+     */
+    private ICallRedirectionAdapter mCallRedirectionAdapter;
+
+    /**
+     * Telecom calls this method to inform the implemented {@link CallRedirectionService} of
+     * a new outgoing call which is being placed.
+     * @param handle the phone number dialed by the user
+     * @param targetPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed.
+     */
+    public abstract void onPlaceCall(Uri handle, PhoneAccountHandle targetPhoneAccount);
+
+    /**
+     * 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.
+     */
+    public final void placeCallUnmodified() {
+        try {
+            mCallRedirectionAdapter.placeCallUnmodified();
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * 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.
+     * @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.
+     */
+    public final void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount) {
+        try {
+            mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount);
+        } 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.
+     */
+    public final void cancelCall() {
+        try {
+            mCallRedirectionAdapter.cancelCall();
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * A handler message to process the attempt to place call with redirection service from Telecom
+     */
+    private static final int MSG_PLACE_CALL = 1;
+
+    /**
+     * A handler to process the attempt to place call with redirection service from Telecom
+     */
+    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_PLACE_CALL:
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        mCallRedirectionAdapter = (ICallRedirectionAdapter) args.arg1;
+                        onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+            }
+        }
+    };
+
+    private final class CallRedirectionBinder extends ICallRedirectionService.Stub {
+
+        /**
+         * 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
+         */
+        @Override
+        public void placeCall(ICallRedirectionAdapter adapter, Uri handle,
+                              PhoneAccountHandle targetPhoneAccount) {
+            Log.v(this, "placeCall");
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = adapter;
+            args.arg2 = handle;
+            args.arg3 = targetPhoneAccount;
+            mHandler.obtainMessage(MSG_PLACE_CALL, args).sendToTarget();
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.v(this, "onBind");
+        return new CallRedirectionBinder();
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        Log.v(this, "onUnbind");
+        return false;
+    }
+}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 3bf951d..3d2b397 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -23,6 +23,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.app.Notification;
 import android.bluetooth.BluetoothDevice;
 import android.content.Intent;
@@ -45,6 +46,7 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
+import java.nio.channels.Channels;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -842,6 +844,7 @@
         public void onRemoteRttRequest(Connection c) {}
         /** @hide */
         public void onPhoneAccountChanged(Connection c, PhoneAccountHandle pHandle) {}
+        public void onConnectionTimeReset(Connection c) {}
     }
 
     /**
@@ -853,6 +856,8 @@
         private final OutputStreamWriter mPipeToInCall;
         private final ParcelFileDescriptor mFdFromInCall;
         private final ParcelFileDescriptor mFdToInCall;
+
+        private final FileInputStream mFromInCallFileInputStream;
         private char[] mReadBuffer = new char[READ_BUFFER_SIZE];
 
         /**
@@ -861,8 +866,11 @@
         public RttTextStream(ParcelFileDescriptor toInCall, ParcelFileDescriptor fromInCall) {
             mFdFromInCall = fromInCall;
             mFdToInCall = toInCall;
+            mFromInCallFileInputStream = new FileInputStream(fromInCall.getFileDescriptor());
+
+            // Wrap the FileInputStream in a Channel so that it's interruptible.
             mPipeFromInCall = new InputStreamReader(
-                    new FileInputStream(fromInCall.getFileDescriptor()));
+                    Channels.newInputStream(Channels.newChannel(mFromInCallFileInputStream)));
             mPipeToInCall = new OutputStreamWriter(
                     new FileOutputStream(toInCall.getFileDescriptor()));
         }
@@ -910,7 +918,7 @@
          * not entered any new text yet.
          */
         public String readImmediately() throws IOException {
-            if (mPipeFromInCall.ready()) {
+            if (mFromInCallFileInputStream.available() > 0) {
                 return read();
             } else {
                 return null;
@@ -1273,6 +1281,7 @@
          * @param looper The looper.
          * @hide
          */
+        @UnsupportedAppUsage
         public VideoProvider(Looper looper) {
             mBinder = new VideoProvider.VideoProviderBinder();
             mMessageHandler = new VideoProvider.VideoProviderHandler(looper);
@@ -2366,6 +2375,16 @@
     }
 
     /**
+     * @hide
+     * Resets the cdma connection time.
+     */
+    public final void resetConnectionTime() {
+        for (Listener l : mListeners) {
+            l.onConnectionTimeReset(this);
+        }
+    }
+
+    /**
      * Returns the connections or conferences with which this connection can be conferenced.
      */
     public final List<Conferenceable> getConferenceables() {
@@ -2804,9 +2823,21 @@
     public void onReject(String replyMessage) {}
 
     /**
-     * Notifies the Connection of a request to silence the ringer.
-     *
-     * @hide
+     * Notifies this Connection of a request to silence the ringer.
+     * <p>
+     * The ringer may be silenced by any of the following methods:
+     * <ul>
+     *     <li>{@link TelecomManager#silenceRinger()}</li>
+     *     <li>The user presses the volume-down button while a call is ringing.</li>
+     * </ul>
+     * <p>
+     * Self-managed {@link ConnectionService} implementations should override this method in their
+     * {@link Connection} implementation and implement logic to silence their app's ringtone.  If
+     * your app set the ringtone as part of the incoming call {@link Notification} (see
+     * {@link #onShowIncomingCallUi()}), it should re-post the notification now, except call
+     * {@link android.app.Notification.Builder#setOnlyAlertOnce(boolean)} with {@code true}.  This
+     * will ensure the ringtone sound associated with your {@link android.app.NotificationChannel}
+     * stops playing.
      */
     public void onSilence() {}
 
@@ -2883,7 +2914,29 @@
      * <p>
      * You should trigger the display of the incoming call user interface for your application by
      * showing a {@link Notification} with a full-screen {@link Intent} specified.
-     * For example:
+     *
+     * In your application code, you should create a {@link android.app.NotificationChannel} for
+     * incoming call notifications from your app:
+     * <pre><code>
+     * NotificationChannel channel = new NotificationChannel(YOUR_CHANNEL_ID, "Incoming Calls",
+     *          NotificationManager.IMPORTANCE_MAX);
+     * // other channel setup stuff goes here.
+     *
+     * // We'll use the default system ringtone for our incoming call notification channel.  You can
+     * // use your own audio resource here.
+     * Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
+     * channel.setSound(ringtoneUri, new AudioAttributes.Builder()
+     *          // Setting the AudioAttributes is important as it identifies the purpose of your
+     *          // notification sound.
+     *          .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+     *          .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+     *      .build());
+     *
+     * NotificationManager mgr = getSystemService(NotificationManager.class);
+     * mgr.createNotificationChannel(channel);
+     * </code></pre>
+     * When it comes time to post a notification for your incoming call, ensure it uses your
+     * incoming call {@link android.app.NotificationChannel}.
      * <pre><code>
      *     // Create an intent which triggers your fullscreen incoming call user interface.
      *     Intent intent = new Intent(Intent.ACTION_MAIN, null);
@@ -2909,11 +2962,14 @@
      *     builder.setContentTitle("Your notification title");
      *     builder.setContentText("Your notification content.");
      *
-     *     // Use builder.addAction(..) to add buttons to answer or reject the call.
+     *     // Set notification as insistent to cause your ringtone to loop.
+     *     Notification notification = builder.build();
+     *     notification.flags |= Notification.FLAG_INSISTENT;
      *
+     *     // Use builder.addAction(..) to add buttons to answer or reject the call.
      *     NotificationManager notificationManager = mContext.getSystemService(
      *         NotificationManager.class);
-     *     notificationManager.notify(YOUR_TAG, YOUR_ID, builder.build());
+     *     notificationManager.notify(YOUR_CHANNEL_ID, YOUR_TAG, YOUR_ID, notification);
      * </code></pre>
      */
     public void onShowIncomingCallUi() {}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 2291090..61adcdd 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1474,6 +1474,13 @@
                 mAdapter.onPhoneAccountChanged(id, pHandle);
             }
         }
+
+        public void onConnectionTimeReset(Connection c) {
+            String id = mIdByConnection.get(c);
+            if (id != null) {
+                mAdapter.resetConnectionTime(id);
+            }
+        }
     };
 
     /** {@inheritDoc} */
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 0d319bb..520e7ed 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -255,6 +255,18 @@
     }
 
     /**
+        * Resets the cdma connection time.
+        */
+    void resetConnectionTime(String callId) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
+            try {
+                adapter.resetConnectionTime(callId, Log.getExternalSession());
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    /**
      * Indicates that the call no longer exists. Can be used with either a call or a conference
      * call.
      *
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 3e1bf77..78d65e6 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -610,6 +610,11 @@
         public void onConnectionServiceFocusReleased(Session.Info sessionInfo) {
             mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_RELEASED).sendToTarget();
         }
+
+        @Override
+        public void resetConnectionTime(String callId, Session.Info sessionInfo) {
+            // Do nothing
+        }
     };
 
     public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index bd25ab2..1aeeca7 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -47,13 +47,19 @@
  * before the telecom service will bind to its {@code InCallService} implementation.
  * <p>
  * Below is an example manifest registration for an {@code InCallService}. The meta-data
- * ({@link TelecomManager#METADATA_IN_CALL_SERVICE_UI}) indicates that this particular
+ * {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular
  * {@code InCallService} implementation intends to replace the built-in in-call UI.
+ * The meta-data {@link TelecomManager#METADATA_IN_CALL_SERVICE_RINGING} indicates that this
+ * {@link InCallService} will play the ringtone for incoming calls.  See
+ * <a href="#incomingCallNotification">below</a> for more information on showing the incoming call
+ * UI and playing the ringtone in your app.
  * <pre>
  * {@code
  * <service android:name="your.package.YourInCallServiceImplementation"
  *          android:permission="android.permission.BIND_INCALL_SERVICE">
  *      <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
+ *      <meta-data android:name="android.telecom.IN_CALL_SERVICE_RINGING"
+ *          android:value="true" />
  *      <intent-filter>
  *          <action android:name="android.telecom.InCallService"/>
  *      </intent-filter>
@@ -80,6 +86,72 @@
  * to see if they would like your application to be the new default phone app.  See the
  * {@link TelecomManager#ACTION_CHANGE_DEFAULT_DIALER} intent documentation for more information on
  * how to do this.
+ * <p id="incomingCallNotification">
+ * <h2>Showing the Incoming Call Notification</h2>
+ * When your app receives a new incoming call via {@link InCallService#onCallAdded(Call)}, it is
+ * responsible for displaying an incoming call UI for the incoming call.  It should do this using
+ * {@link android.app.NotificationManager} APIs to post a new incoming call notification.
+ * <p>
+ * Where your app declares the meta-data {@link TelecomManager#METADATA_IN_CALL_SERVICE_RINGING}, it
+ * is responsible for playing the ringtone for incoming calls.  Your app should create a
+ * {@link android.app.NotificationChannel} which specifies the desired ringtone.  For example:
+ * <pre><code>
+ * NotificationChannel channel = new NotificationChannel(YOUR_CHANNEL_ID, "Incoming Calls",
+ *          NotificationManager.IMPORTANCE_MAX);
+ * // other channel setup stuff goes here.
+ *
+ * // We'll use the default system ringtone for our incoming call notification channel.  You can
+ * // use your own audio resource here.
+ * Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
+ * channel.setSound(ringtoneUri, new AudioAttributes.Builder()
+ *          // Setting the AudioAttributes is important as it identifies the purpose of your
+ *          // notification sound.
+ *          .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+ *          .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ *      .build());
+ *
+ * NotificationManager mgr = getSystemService(NotificationManager.class);
+ * mgr.createNotificationChannel(channel);
+ * </code></pre>
+ * <p>
+ * When your app receives a new incoming call, it creates a {@link android.app.Notification} for the
+ * incoming call and associates it with your incoming call notification channel. You can specify a
+ * {@link android.app.PendingIntent} on the notification which will launch your full screen
+ * incoming call UI.  The notification manager framework will display your notification as a
+ * heads-up notification if the user is actively using the phone.  When the user is not using the
+ * phone, your full-screen incoming call UI is used instead.
+ * For example:
+ * <pre><code>
+ * // Create an intent which triggers your fullscreen incoming call user interface.
+ * Intent intent = new Intent(Intent.ACTION_MAIN, null);
+ * intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
+ * intent.setClass(context, YourIncomingCallActivity.class);
+ * PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, 0);
+ *
+ * // Build the notification as an ongoing high priority item; this ensures it will show as
+ * // a heads up notification which slides down over top of the current content.
+ * final Notification.Builder builder = new Notification.Builder(context);
+ * builder.setOngoing(true);
+ * builder.setPriority(Notification.PRIORITY_HIGH);
+ *
+ * // Set notification content intent to take user to the fullscreen UI if user taps on the
+ * // notification body.
+ * builder.setContentIntent(pendingIntent);
+ * // Set full screen intent to trigger display of the fullscreen UI when the notification
+ * // manager deems it appropriate.
+ * builder.setFullScreenIntent(pendingIntent, true);
+ *
+ * // Setup notification content.
+ * builder.setSmallIcon( yourIconResourceId );
+ * builder.setContentTitle("Your notification title");
+ * builder.setContentText("Your notification content.");
+ *
+ * // Use builder.addAction(..) to add buttons to answer or reject the call.
+ *
+ * NotificationManager notificationManager = mContext.getSystemService(
+ *     NotificationManager.class);
+ * notificationManager.notify(YOUR_CHANNEL_ID, YOUR_TAG, YOUR_ID, builder.build());
+ * </code></pre>
  */
 public abstract class InCallService extends Service {
 
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 6212a77..8b0211e 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.UnsupportedAppUsage;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -117,6 +118,7 @@
     }
 
     /** The unique ID of the call. */
+    @UnsupportedAppUsage
     public String getId() {
         return mId;
     }
@@ -130,6 +132,7 @@
      * Reason for disconnection, as described by {@link android.telecomm.DisconnectCause}. Valid
      * when call state is {@link CallState#DISCONNECTED}.
      */
+    @UnsupportedAppUsage
     public DisconnectCause getDisconnectCause() {
         return mDisconnectCause;
     }
@@ -155,11 +158,13 @@
     }
 
     /** The time that the call switched to the active state. */
+    @UnsupportedAppUsage
     public long getConnectTimeMillis() {
         return mConnectTimeMillis;
     }
 
     /** The endpoint to which the call is connected. */
+    @UnsupportedAppUsage
     public Uri getHandle() {
         return mHandle;
     }
@@ -300,6 +305,7 @@
     }
 
     /** Responsible for creating ParcelableCall objects for deserialized Parcels. */
+    @UnsupportedAppUsage
     public static final Parcelable.Creator<ParcelableCall> CREATOR =
             new Parcelable.Creator<ParcelableCall> () {
         @Override
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 99f94f2..d3ccd2c 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -17,6 +17,7 @@
 package android.telecom;
 
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.bluetooth.BluetoothDevice;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -330,6 +331,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public final void setProximitySensorOn() {
         mInCallAdapter.turnProximitySensorOn();
     }
@@ -345,6 +347,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public final void setProximitySensorOff(boolean screenOnImmediately) {
         mInCallAdapter.turnProximitySensorOff(screenOnImmediately);
     }
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index 77b510d..279804e 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -17,6 +17,7 @@
 package android.telecom;
 
 import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -40,7 +41,9 @@
  * See {@link PhoneAccount}, {@link TelecomManager}.
  */
 public final class PhoneAccountHandle implements Parcelable {
+    @UnsupportedAppUsage
     private final ComponentName mComponentName;
+    @UnsupportedAppUsage
     private final String mId;
     private final UserHandle mUserHandle;
 
@@ -164,6 +167,7 @@
         }
     };
 
+    @UnsupportedAppUsage
     private PhoneAccountHandle(Parcel in) {
         this(ComponentName.CREATOR.createFromParcel(in),
                 in.readString(),
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index bb4b483..9821dcb 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -466,6 +466,11 @@
                 Log.w(this, "onRemoteRttRequest called on a remote conference");
             }
         }
+
+        @Override
+        public void resetConnectionTime(String callId, Session.Info sessionInfo) {
+            // Do nothing
+        }
     };
 
     private final ConnectionServiceAdapterServant mServant =
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 75572b3..48c1e24 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -20,6 +20,7 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -376,6 +377,7 @@
      * The phone number of the call used by Telecom to determine which call should be handed over.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final String EXTRA_IS_HANDOVER = "android.telecom.extra.IS_HANDOVER";
 
     /**
@@ -493,6 +495,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int TTY_MODE_OFF = 0;
 
     /**
@@ -652,6 +655,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public static TelecomManager from(Context context) {
         return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
     }
@@ -721,6 +725,7 @@
      * @return The user outgoing phone account selected by the user.
      * @hide
      */
+    @UnsupportedAppUsage
     public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
         try {
             if (isServiceConnected()) {
@@ -736,6 +741,7 @@
      * Sets the user-chosen default for making outgoing phone calls.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
         try {
             if (isServiceConnected()) {
@@ -773,6 +779,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public PhoneAccountHandle getSimCallManager(int userId) {
         try {
             if (isServiceConnected()) {
@@ -876,6 +883,7 @@
      * @return A list of {@code PhoneAccountHandle} objects.
      * @hide
      */
+    @UnsupportedAppUsage
     public List<PhoneAccountHandle> getCallCapablePhoneAccounts(boolean includeDisabledAccounts) {
         try {
             if (isServiceConnected()) {
@@ -1109,6 +1117,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean setDefaultDialer(String packageName) {
         try {
             if (isServiceConnected()) {
@@ -1126,6 +1135,7 @@
      * @return package name for the system dialer package or null if no system dialer is preloaded.
      * @hide
      */
+    @UnsupportedAppUsage
     public String getSystemDialerPackage() {
         try {
             if (isServiceConnected()) {
@@ -1413,6 +1423,7 @@
      * - {@link TelecomManager#TTY_MODE_VCO}
      * @hide
      */
+    @UnsupportedAppUsage
     public int getCurrentTtyMode() {
         try {
             if (isServiceConnected()) {
diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java
index bae58ff..2c7fecb 100644
--- a/telecomm/java/android/telecom/VideoCallImpl.java
+++ b/telecomm/java/android/telecom/VideoCallImpl.java
@@ -16,6 +16,7 @@
 
 package android.telecom;
 
+import android.annotation.UnsupportedAppUsage;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
@@ -217,6 +218,7 @@
         mTargetSdkVersion = sdkVersion;
     }
 
+    @UnsupportedAppUsage
     public void destroy() {
         unregisterCallback(mCallback);
     }
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
index 90ed36f..bbac8eb 100644
--- a/telecomm/java/android/telecom/VideoProfile.java
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -17,6 +17,7 @@
 package android.telecom;
 
 import android.annotation.IntDef;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -377,6 +378,7 @@
          * @param maxZoom Maximum zoom supported by camera.
          * @hide
          */
+        @UnsupportedAppUsage
         public CameraCapabilities(int width, int height, boolean zoomSupported, float maxZoom) {
             mWidth = width;
             mHeight = height;
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
new file mode 100644
index 0000000..46bf983
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.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 com.android.internal.telecom;
+
+import android.net.Uri;
+import android.telecom.PhoneAccountHandle;
+
+/**
+ * Internal remote callback interface for call redirection services.
+ *
+ * @see android.telecom.CallRedirectionService
+ *
+ * {@hide}
+ */
+oneway interface ICallRedirectionAdapter {
+    void cancelCall();
+
+    void placeCallUnmodified();
+
+    void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount);
+}
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
new file mode 100644
index 0000000..d8d360b
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
@@ -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.internal.telecom;
+
+import android.net.Uri;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.internal.telecom.ICallRedirectionAdapter;
+
+/**
+ * Internal remote interface for a call redirection service.
+ *
+ * @see android.telecom.CallRedirectionService
+ *
+ * @hide
+ */
+oneway interface ICallRedirectionService {
+    void placeCall(in ICallRedirectionAdapter adapter, in Uri handle,
+            in PhoneAccountHandle targetPhoneAccount);
+}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index be474bd..0157a58 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -121,4 +121,6 @@
     in Session.Info sessionInfo);
 
     void onConnectionServiceFocusReleased(in Session.Info sessionInfo);
+
+    void resetConnectionTime(String callIdi, in Session.Info sessionInfo);
 }
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 2d096c0..8283e97 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -3542,6 +3542,12 @@
             public static final String ICCID_PREFIX = "iccid_prefix";
 
             /**
+             * Certificate for carrier privilege access rules.
+             * <P>Type: TEXT in hex string </P>
+             */
+            public static final String PRIVILEGE_ACCESS_RULE = "privilege_access_rule";
+
+            /**
              * The {@code content://} URI for this table.
              */
             public static final Uri CONTENT_URI = Uri.parse("content://carrier_id/all");
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index f66164c..ab94093 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -51,10 +51,13 @@
     public static final String EXTRA_SUBSCRIPTION_INDEX =
             SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX;
 
+    private final Context mContext;
+
     /**
      * @hide
      */
-    public CarrierConfigManager() {
+    public CarrierConfigManager(Context context) {
+        mContext = context;
     }
 
     /**
@@ -77,6 +80,30 @@
     public static final String
             KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
 
+    /**
+     * Boolean indicating if the "Call forwarding" item is visible in the Call Settings menu.
+     * true means visible. false means gone.
+     * @hide
+     */
+    public static final String KEY_CALL_FORWARDING_VISIBILITY_BOOL =
+            "call_forwarding_visibility_bool";
+
+    /**
+     * Boolean indicating if the "Caller ID" item is visible in the Additional Settings menu.
+     * true means visible. false means gone.
+     * @hide
+     */
+    public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL =
+            "additional_settings_caller_id_visibility_bool";
+
+    /**
+     * Boolean indicating if the "Call Waiting" item is visible in the Additional Settings menu.
+     * true means visible. false means gone.
+     * @hide
+     */
+    public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL =
+            "additional_settings_call_waiting_visibility_bool";
+
    /**
     * Boolean indicating if the "Call barring" item is visible in the Call Settings menu.
     * true means visible. false means gone.
@@ -113,6 +140,14 @@
     public static final String
             KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
 
+    /**
+     * Flag indicating whether or not sending emergency SMS messages over IMS
+     * is supported when in LTE/limited LTE (Emergency only) service mode..
+     *
+     */
+    public static final String
+            KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
+
     /** Flag indicating if the phone is a world phone */
     public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
 
@@ -189,6 +224,13 @@
     public static final String
             KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
 
+    /**
+     * A string array containing numbers that shouldn't be included in the call log.
+     * @hide
+     */
+    public static final String KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY =
+            "unloggable_numbers_string_array";
+
     /** If true, removes the Voice Privacy option from Call Settings */
     public static final String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
 
@@ -1031,6 +1073,26 @@
     public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
 
     /**
+     * Override the registered PLMN name using #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING.
+     *
+     * If true, then the registered PLMN name (only for CDMA/CDMA-LTE and only when not roaming)
+     * will be #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING. If false, or if phone type is not
+     * CDMA/CDMA-LTE or if roaming, then #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING will be ignored.
+     * @hide
+     */
+    public static final String KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL =
+            "cdma_home_registered_plmn_name_override_bool";
+
+    /**
+     * String to identify registered PLMN name in CarrierConfig app. This string overrides
+     * registered PLMN name if #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL is true, phone type
+     * is CDMA/CDMA-LTE and device is not in roaming state; otherwise, it will be ignored.
+     * @hide
+     */
+    public static final String KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING =
+            "cdma_home_registered_plmn_name_string";
+
+    /**
      * If this is true, the SIM card (through Customer Service Profile EF file) will be able to
      * prevent manual operator selection. If false, this SIM setting will be ignored and manual
      * operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more
@@ -1438,6 +1500,15 @@
             "always_play_remote_hold_tone_bool";
 
     /**
+     * When true, the Telephony stack will automatically turn off airplane mode and retry a wifi
+     * emergency call over the cell network if the initial attempt at dialing was met with a SIP 308
+     * error.
+     * @hide
+     */
+    public static final String KEY_AUTO_RETRY_FAILED_WIFI_EMERGENCY_CALL =
+            "auto_retry_failed_wifi_emergency_call";
+
+    /**
      * When true, indicates that adding a call is disabled when there is an ongoing video call
      * or when there is an ongoing call on wifi which was downgraded from video and VoWifi is
      * turned off.
@@ -1545,7 +1616,7 @@
     public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL =
             "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.
@@ -1957,6 +2028,15 @@
     public static final String KEY_UNDELIVERED_SMS_MESSAGE_EXPIRATION_TIME =
             "undelivered_sms_message_expiration_time";
 
+    /**
+     * Specifies a carrier-defined {@link CallRedirectionService} which Telecom will bind
+     * to for outgoing calls.  An empty string indicates that no carrier-defined
+     * {@link CallRedirectionService} is specified.
+     * @hide
+     */
+    public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING =
+            "call_redirection_service_component_name_string";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -1965,8 +2045,10 @@
         sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
+        sDefaults.putBoolean(KEY_AUTO_RETRY_FAILED_WIFI_EMERGENCY_CALL, false);
         sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
         sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false);
+        sDefaults.putStringArray(KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_ALLOW_LOCAL_DTMF_TONES_BOOL, true);
         sDefaults.putBoolean(KEY_PLAY_CALL_RECORDING_TONE_BOOL, false);
         sDefaults.putBoolean(KEY_APN_EXPAND_BOOL, true);
@@ -2015,6 +2097,9 @@
 
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false);
         sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false);
+        sDefaults.putBoolean(KEY_CALL_FORWARDING_VISIBILITY_BOOL, true);
+        sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL, true);
+        sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL, true);
         sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
         sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
         sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
@@ -2022,6 +2107,7 @@
         sDefaults.putBoolean(KEY_SHOW_APN_SETTING_CDMA_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_CDMA_CHOICES_BOOL, false);
         sDefaults.putBoolean(KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL, true);
         sDefaults.putBoolean(KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL, false);
@@ -2129,6 +2215,8 @@
         sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
         sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
         sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
+        sDefaults.putBoolean(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL, false);
+        sDefaults.putString(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING, "");
         sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false);
         sDefaults.putBoolean(KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL, false);
@@ -2305,7 +2393,7 @@
                         + " ICarrierConfigLoader is null");
                 return null;
             }
-            return loader.getConfigForSubId(subId);
+            return loader.getConfigForSubId(subId, mContext.getOpPackageName());
         } catch (RemoteException ex) {
             Rlog.e(TAG, "Error getting config for subId " + subId + ": "
                     + ex.toString());
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 890a6ea..2a41829 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -175,7 +175,10 @@
         }
 
         CellIdentity o = (CellIdentity) other;
-        return TextUtils.equals(mAlphaLong, o.mAlphaLong)
+        return mType == o.mType
+                && TextUtils.equals(mMccStr, o.mMccStr)
+                && TextUtils.equals(mMncStr, o.mMncStr)
+                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
                 && TextUtils.equals(mAlphaShort, o.mAlphaShort);
     }
 
@@ -233,4 +236,4 @@
     protected void log(String s) {
         Rlog.w(mTag, s);
     }
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 18ab6d4..b99fe46 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -16,9 +16,7 @@
 
 package android.telephony;
 
-import android.annotation.Nullable;
 import android.os.Parcel;
-import android.text.TextUtils;
 
 import java.util.Objects;
 
@@ -35,6 +33,8 @@
     private final int mCid;
     // 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown.
     private final int mCpid;
+    // 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
+    private final int mUarfcn;
 
     /**
      * @hide
@@ -44,6 +44,7 @@
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mCpid = Integer.MAX_VALUE;
+        mUarfcn = Integer.MAX_VALUE;
     }
 
     /**
@@ -52,11 +53,12 @@
      * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
      * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
      * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX 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) {
-        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid, null, null);
+    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);
     }
 
     /**
@@ -65,39 +67,24 @@
      * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
      * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
      * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
-     *
-     * FIXME: This is a temporary constructor to facilitate migration.
-     * @hide
-     */
-    public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid) {
-        super(TAG, TYPE_TDSCDMA, mcc, mnc, null, null);
-        mLac = lac;
-        mCid = cid;
-        mCpid = cpid;
-    }
-
-    /**
-     * @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, INT_MAX if unknown
-     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
-     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
      * @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 CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid,
+    public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid, int uarfcn,
             String alphal, String alphas) {
         super(TAG, TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
         mLac = lac;
         mCid = cid;
         mCpid = cpid;
+        mUarfcn = uarfcn;
     }
 
     private CellIdentityTdscdma(CellIdentityTdscdma cid) {
         this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid,
-                cid.mCpid, cid.mAlphaLong, cid.mAlphaShort);
+                cid.mCpid, cid.mUarfcn, cid.mAlphaLong, cid.mAlphaShort);
     }
 
     CellIdentityTdscdma copy() {
@@ -141,9 +128,10 @@
         return mCpid;
     }
 
+    /** @hide */
     @Override
-    public int hashCode() {
-        return Objects.hash(mLac, mCid, mCpid, super.hashCode());
+    public int getChannelNumber() {
+        return mUarfcn;
     }
 
     @Override
@@ -157,24 +145,29 @@
         }
 
         CellIdentityTdscdma o = (CellIdentityTdscdma) other;
-        return TextUtils.equals(mMccStr, o.mMccStr)
-                && TextUtils.equals(mMncStr, o.mMncStr)
-                && mLac == o.mLac
+        return  mLac == o.mLac
                 && mCid == o.mCid
                 && mCpid == o.mCpid
+                && mUarfcn == o.mUarfcn
                 && super.equals(other);
     }
 
     @Override
+    public int hashCode() {
+        return Objects.hash(mLac, mCid, mCpid, mUarfcn, super.hashCode());
+    }
+
+    @Override
     public String toString() {
         return new StringBuilder(TAG)
         .append(":{ mMcc=").append(mMccStr)
         .append(" mMnc=").append(mMncStr)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
         .append(" mLac=").append(mLac)
         .append(" mCid=").append(mCid)
         .append(" mCpid=").append(mCpid)
-        .append(" mAlphaLong=").append(mAlphaLong)
-        .append(" mAlphaShort=").append(mAlphaShort)
+        .append(" mUarfcn=").append(mUarfcn)
         .append("}").toString();
     }
 
@@ -186,6 +179,7 @@
         dest.writeInt(mLac);
         dest.writeInt(mCid);
         dest.writeInt(mCpid);
+        dest.writeInt(mUarfcn);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -194,7 +188,7 @@
         mLac = in.readInt();
         mCid = in.readInt();
         mCpid = in.readInt();
-
+        mUarfcn = in.readInt();
         if (DBG) log(toString());
     }
 
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 984483e..43f9406 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -35,7 +35,7 @@
     private final int mCid;
     // 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511
     private final int mPsc;
-    // 16-bit UMTS Absolute RF Channel Number
+    // 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.4
     private final int mUarfcn;
 
     /**
@@ -70,7 +70,7 @@
      * @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
+     * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
      *
      * @hide
      */
@@ -83,7 +83,7 @@
      * @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
+     * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
      * @param mccStr 3-digit Mobile Country Code in string format
      * @param mncStr 2 or 3-digit Mobile Network Code in string format
      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index 9232ed7..bffeb17 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -17,8 +17,10 @@
 package android.telephony;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -36,6 +38,8 @@
     protected static final int TYPE_LTE = 3;
     /** @hide */
     protected static final int TYPE_WCDMA = 4;
+    /** @hide */
+    protected static final int TYPE_TDCDMA = 5;
 
     // Type to distinguish where time stamp gets recorded.
 
@@ -121,6 +125,14 @@
         mTimeStamp = timeStamp;
     }
 
+    /** @hide */
+    @NonNull
+    public abstract CellIdentity getCellIdentity();
+
+    /** @hide */
+    @NonNull
+    public abstract CellSignalStrength getCellSignalStrength();
+
     /**
      * Gets the connection status of this cell.
      *
@@ -260,6 +272,7 @@
                     case TYPE_CDMA: return CellInfoCdma.createFromParcelBody(in);
                     case TYPE_LTE: return CellInfoLte.createFromParcelBody(in);
                     case TYPE_WCDMA: return CellInfoWcdma.createFromParcelBody(in);
+                    case TYPE_TDCDMA: return CellInfoTdscdma.createFromParcelBody(in);
                     default: throw new RuntimeException("Bad CellInfo Parcel");
                 }
         }
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index 6f2f1f6..8b8d1bb 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -21,7 +21,7 @@
 import android.telephony.Rlog;
 
 /**
- * Immutable cell information from a point in time.
+ * A {@link CellInfo} representing a CDMA cell that provides identity and measurement info.
  */
 public final class CellInfoCdma extends CellInfo implements Parcelable {
 
@@ -45,6 +45,7 @@
         this.mCellSignalStrengthCdma = ci.mCellSignalStrengthCdma.copy();
     }
 
+    @Override
     public CellIdentityCdma getCellIdentity() {
         return mCellIdentityCdma;
     }
@@ -53,6 +54,7 @@
         mCellIdentityCdma = cid;
     }
 
+    @Override
     public CellSignalStrengthCdma getCellSignalStrength() {
         return mCellSignalStrengthCdma;
     }
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index 1bedddb..f7af1b2 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -21,7 +21,7 @@
 import android.telephony.Rlog;
 
 /**
- * Immutable cell information from a point in time.
+ * A {@link CellInfo} representing a GSM cell that provides identity and measurement info.
  */
 public final class CellInfoGsm extends CellInfo implements Parcelable {
 
@@ -45,6 +45,7 @@
         this.mCellSignalStrengthGsm = ci.mCellSignalStrengthGsm.copy();
     }
 
+    @Override
     public CellIdentityGsm getCellIdentity() {
         return mCellIdentityGsm;
     }
@@ -53,6 +54,7 @@
         mCellIdentityGsm = cid;
     }
 
+    @Override
     public CellSignalStrengthGsm getCellSignalStrength() {
         return mCellSignalStrengthGsm;
     }
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index 287c9f0..97d856e 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -21,7 +21,7 @@
 import android.telephony.Rlog;
 
 /**
- * Immutable cell information from a point in time.
+ * A {@link CellInfo} representing an LTE cell that provides identity and measurement info.
  */
 public final class CellInfoLte extends CellInfo implements Parcelable {
 
@@ -45,6 +45,7 @@
         this.mCellSignalStrengthLte = ci.mCellSignalStrengthLte.copy();
     }
 
+    @Override
     public CellIdentityLte getCellIdentity() {
         if (DBG) log("getCellIdentity: " + mCellIdentityLte);
         return mCellIdentityLte;
@@ -55,6 +56,7 @@
         mCellIdentityLte = cid;
     }
 
+    @Override
     public CellSignalStrengthLte getCellSignalStrength() {
         if (DBG) log("getCellSignalStrength: " + mCellSignalStrengthLte);
         return mCellSignalStrengthLte;
diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java
new file mode 100644
index 0000000..4fb1bce
--- /dev/null
+++ b/telephony/java/android/telephony/CellInfoTdscdma.java
@@ -0,0 +1,153 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A {@link CellInfo} representing a TD-SCDMA cell that provides identity and measurement info.
+ *
+ * @hide
+ */
+public final class CellInfoTdscdma extends CellInfo implements Parcelable {
+
+    private static final String LOG_TAG = "CellInfoTdscdma";
+    private static final boolean DBG = false;
+
+    private CellIdentityTdscdma mCellIdentityTdscdma;
+    private CellSignalStrengthTdscdma mCellSignalStrengthTdscdma;
+
+    /** @hide */
+    public CellInfoTdscdma() {
+        super();
+        mCellIdentityTdscdma = new CellIdentityTdscdma();
+        mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma();
+    }
+
+    /** @hide */
+    public CellInfoTdscdma(CellInfoTdscdma ci) {
+        super(ci);
+        this.mCellIdentityTdscdma = ci.mCellIdentityTdscdma.copy();
+        this.mCellSignalStrengthTdscdma = ci.mCellSignalStrengthTdscdma.copy();
+    }
+
+    @Override
+    public CellIdentityTdscdma getCellIdentity() {
+        return mCellIdentityTdscdma;
+    }
+    /** @hide */
+    public void setCellIdentity(CellIdentityTdscdma cid) {
+        mCellIdentityTdscdma = cid;
+    }
+
+    @Override
+    public CellSignalStrengthTdscdma getCellSignalStrength() {
+        return mCellSignalStrengthTdscdma;
+    }
+    /** @hide */
+    public void setCellSignalStrength(CellSignalStrengthTdscdma css) {
+        mCellSignalStrengthTdscdma = css;
+    }
+
+    /**
+     * @return hash code
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mCellIdentityTdscdma, mCellSignalStrengthTdscdma);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!super.equals(other)) {
+            return false;
+        }
+        try {
+            CellInfoTdscdma o = (CellInfoTdscdma) other;
+            return mCellIdentityTdscdma.equals(o.mCellIdentityTdscdma)
+                    && mCellSignalStrengthTdscdma.equals(o.mCellSignalStrengthTdscdma);
+        } catch (ClassCastException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("CellInfoTdscdma:{");
+        sb.append(super.toString());
+        sb.append(" ").append(mCellIdentityTdscdma);
+        sb.append(" ").append(mCellSignalStrengthTdscdma);
+        sb.append("}");
+
+        return sb.toString();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags, TYPE_TDCDMA);
+        mCellIdentityTdscdma.writeToParcel(dest, flags);
+        mCellSignalStrengthTdscdma.writeToParcel(dest, flags);
+    }
+
+    /**
+     * Construct a CellInfoTdscdma object from the given parcel
+     * where the token is already been processed.
+     */
+    private CellInfoTdscdma(Parcel in) {
+        super(in);
+        mCellIdentityTdscdma = CellIdentityTdscdma.CREATOR.createFromParcel(in);
+        mCellSignalStrengthTdscdma = CellSignalStrengthTdscdma.CREATOR.createFromParcel(in);
+    }
+
+    /** Implement the Parcelable interface */
+    public static final Creator<CellInfoTdscdma> CREATOR = new Creator<CellInfoTdscdma>() {
+        @Override
+        public CellInfoTdscdma createFromParcel(Parcel in) {
+            in.readInt(); // Skip past token, we know what it is
+            return createFromParcelBody(in);
+        }
+
+        @Override
+        public CellInfoTdscdma[] newArray(int size) {
+            return new CellInfoTdscdma[size];
+        }
+    };
+
+    /** @hide */
+    protected static CellInfoTdscdma createFromParcelBody(Parcel in) {
+        return new CellInfoTdscdma(in);
+    }
+
+    /**
+     * log
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+}
diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java
index 0615702..4f9dcb1 100644
--- a/telephony/java/android/telephony/CellInfoWcdma.java
+++ b/telephony/java/android/telephony/CellInfoWcdma.java
@@ -20,8 +20,10 @@
 import android.os.Parcelable;
 import android.telephony.Rlog;
 
+import java.util.Objects;
+
 /**
- * Immutable cell information from a point in time.
+ * A {@link CellInfo} representing a WCDMA cell that provides identity and measurement info.
  */
 public final class CellInfoWcdma extends CellInfo implements Parcelable {
 
@@ -45,6 +47,7 @@
         this.mCellSignalStrengthWcdma = ci.mCellSignalStrengthWcdma.copy();
     }
 
+    @Override
     public CellIdentityWcdma getCellIdentity() {
         return mCellIdentityWcdma;
     }
@@ -53,6 +56,7 @@
         mCellIdentityWcdma = cid;
     }
 
+    @Override
     public CellSignalStrengthWcdma getCellSignalStrength() {
         return mCellSignalStrengthWcdma;
     }
@@ -66,7 +70,7 @@
      */
     @Override
     public int hashCode() {
-        return super.hashCode() + mCellIdentityWcdma.hashCode() + mCellSignalStrengthWcdma.hashCode();
+        return Objects.hash(super.hashCode(), mCellIdentityWcdma, mCellSignalStrengthWcdma);
     }
 
     @Override
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index 183f96d..aa6b207 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -104,7 +104,10 @@
     }
 
     /**
-     * Get signal level as an int from 0..4
+     * Retrieve an abstract level value for the overall signal strength.
+     *
+     * @return a single integer from 0 to 4 representing the general signal quality.
+     *     0 represents very poor signal strength while 4 represents a very strong signal strength.
      */
     @Override
     public int getLevel() {
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 8687cd1..cff159b 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -82,7 +82,10 @@
     }
 
     /**
-     * Get signal level as an int from 0..4
+     * Retrieve an abstract level value for the overall signal strength.
+     *
+     * @return a single integer from 0 to 4 representing the general signal quality.
+     *     0 represents very poor signal strength while 4 represents a very strong signal strength.
      */
     @Override
     public int getLevel() {
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 7e86966..2f059f4 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -86,7 +86,10 @@
     }
 
     /**
-     * Get signal level as an int from 0..4
+     * Retrieve an abstract level value for the overall signal strength.
+     *
+     * @return a single integer from 0 to 4 representing the general signal quality.
+     *     0 represents very poor signal strength while 4 represents a very strong signal strength.
      */
     @Override
     public int getLevel() {
@@ -121,6 +124,8 @@
 
     /**
      * Get reference signal received quality
+     *
+     * @return the RSRQ if available or Integer.MAX_VALUE if unavailable.
      */
     public int getRsrq() {
         return mRsrq;
@@ -128,13 +133,17 @@
 
     /**
      * Get reference signal signal-to-noise ratio
+     *
+     * @return the RSSNR if available or Integer.MAX_VALUE if unavailable.
      */
     public int getRssnr() {
         return mRssnr;
     }
 
     /**
-     * Get reference signal received power
+     * Get reference signal received power in dBm
+     *
+     * @return the RSRP of the measured cell.
      */
     public int getRsrp() {
         return mRsrp;
@@ -142,13 +151,17 @@
 
     /**
      * Get channel quality indicator
+     *
+     * @return the CQI if available or Integer.MAX_VALUE if unavailable.
      */
     public int getCqi() {
         return mCqi;
     }
 
     /**
-     * Get signal strength as dBm
+     * Get signal strength in dBm
+     *
+     * @return the RSRP of the measured cell.
      */
     @Override
     public int getDbm() {
@@ -175,7 +188,8 @@
      * Get the timing advance value for LTE, as a value in range of 0..1282.
      * Integer.MAX_VALUE is reported when there is no active RRC
      * connection. Refer to 3GPP 36.213 Sec 4.2.3
-     * @return the LTE timing advance, if available.
+     *
+     * @return the LTE timing advance if available or Integer.MAX_VALUE if unavailable.
      */
     public int getTimingAdvance() {
         return mTimingAdvance;
diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
new file mode 100644
index 0000000..41859a3
--- /dev/null
+++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.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 android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Tdscdma signal strength related information.
+ *
+ * @hide
+ */
+public final class CellSignalStrengthTdscdma extends CellSignalStrength implements Parcelable {
+
+    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 int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
+                                 // or Integer.MAX_VALUE if unknown
+    private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
+                               // Integer.MAX_VALUE if unknown
+    private int mRscp; // Pilot power (0-96, 255) as defined in TS 27.007 8.69 or Integer.MAX_VALUE
+                       // if unknown
+
+    /** @hide */
+    public CellSignalStrengthTdscdma() {
+        setDefaultValues();
+    }
+
+    /** @hide */
+    public CellSignalStrengthTdscdma(int ss, int ber, int rscp) {
+        mSignalStrength = ss;
+        mBitErrorRate = ber;
+        mRscp = rscp;
+    }
+
+    /** @hide */
+    public CellSignalStrengthTdscdma(CellSignalStrengthTdscdma s) {
+        copyFrom(s);
+    }
+
+    /** @hide */
+    protected void copyFrom(CellSignalStrengthTdscdma s) {
+        mSignalStrength = s.mSignalStrength;
+        mBitErrorRate = s.mBitErrorRate;
+        mRscp = s.mRscp;
+    }
+
+    /** @hide */
+    @Override
+    public CellSignalStrengthTdscdma copy() {
+        return new CellSignalStrengthTdscdma(this);
+    }
+
+    /** @hide */
+    @Override
+    public void setDefaultValues() {
+        mSignalStrength = Integer.MAX_VALUE;
+        mBitErrorRate = Integer.MAX_VALUE;
+        mRscp = Integer.MAX_VALUE;
+    }
+
+    /**
+     * Retrieve an abstract level value for the overall signal strength.
+     *
+     * @return a single integer from 0 to 4 representing the general signal quality.
+     *     0 represents very poor signal strength while 4 represents a very strong signal strength.
+     */
+    @Override
+    public int getLevel() {
+        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 = 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;
+    }
+
+    /**
+     * Get the signal strength as dBm
+     */
+    @Override
+    public int getDbm() {
+        int dBm;
+
+        int level = mSignalStrength;
+        int asu = (level == 99 ? Integer.MAX_VALUE : level);
+        if (asu != Integer.MAX_VALUE) {
+            dBm = -113 + (2 * asu);
+        } else {
+            dBm = Integer.MAX_VALUE;
+        }
+        if (DBG) log("getDbm=" + dBm);
+        return dBm;
+    }
+
+    /**
+     * 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
+     */
+    @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;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSignalStrength, mBitErrorRate);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        CellSignalStrengthTdscdma s;
+
+        try {
+            s = (CellSignalStrengthTdscdma) o;
+        } catch (ClassCastException ex) {
+            return false;
+        }
+
+        if (o == null) {
+            return false;
+        }
+
+        return mSignalStrength == s.mSignalStrength
+                && mBitErrorRate == s.mBitErrorRate
+                && mRscp == s.mRscp;
+    }
+
+    /**
+     * @return string representation.
+     */
+    @Override
+    public String toString() {
+        return "CellSignalStrengthTdscdma:"
+                + " ss=" + mSignalStrength
+                + " ber=" + mBitErrorRate
+                + " rscp=" + mRscp;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        dest.writeInt(mSignalStrength);
+        dest.writeInt(mBitErrorRate);
+        dest.writeInt(mRscp);
+    }
+
+    /**
+     * Construct a SignalStrength object from the given parcel
+     * where the token is already been processed.
+     */
+    private CellSignalStrengthTdscdma(Parcel in) {
+        mSignalStrength = in.readInt();
+        mBitErrorRate = in.readInt();
+        mRscp = in.readInt();
+        if (DBG) log("CellSignalStrengthTdscdma(Parcel): " + toString());
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @SuppressWarnings("hiding")
+    public static final Parcelable.Creator<CellSignalStrengthTdscdma> CREATOR =
+            new Parcelable.Creator<CellSignalStrengthTdscdma>() {
+        @Override
+        public CellSignalStrengthTdscdma createFromParcel(Parcel in) {
+            return new CellSignalStrengthTdscdma(in);
+        }
+
+        @Override
+        public CellSignalStrengthTdscdma[] newArray(int size) {
+            return new CellSignalStrengthTdscdma[size];
+        }
+    };
+
+    /**
+     * log
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+}
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index dd32a96..21cf0be 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -35,7 +35,13 @@
     private static final int WCDMA_SIGNAL_STRENGTH_MODERATE = 5;
 
     private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
-    private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+                                 // or Integer.MAX_VALUE if unknown
+    private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
+                               // Integer.MAX_VALUE if unknown
+    private int mRscp; // bit error rate (0-96, 255) as defined in TS 27.007 8.69 or
+                       // Integer.MAX_VALUE if unknown
+    private int mEcNo; // signal to noise radio (0-49, 255) as defined in TS 27.007 8.69 or
+                       // Integer.MAX_VALUE if unknown
 
     /** @hide */
     public CellSignalStrengthWcdma() {
@@ -43,9 +49,11 @@
     }
 
     /** @hide */
-    public CellSignalStrengthWcdma(int ss, int ber) {
+    public CellSignalStrengthWcdma(int ss, int ber, int rscp, int ecno) {
         mSignalStrength = ss;
         mBitErrorRate = ber;
+        mRscp = rscp;
+        mEcNo = ecno;
     }
 
     /** @hide */
@@ -57,6 +65,8 @@
     protected void copyFrom(CellSignalStrengthWcdma s) {
         mSignalStrength = s.mSignalStrength;
         mBitErrorRate = s.mBitErrorRate;
+        mRscp = s.mRscp;
+        mEcNo = s.mEcNo;
     }
 
     /** @hide */
@@ -70,10 +80,15 @@
     public void setDefaultValues() {
         mSignalStrength = Integer.MAX_VALUE;
         mBitErrorRate = Integer.MAX_VALUE;
+        mRscp = Integer.MAX_VALUE;
+        mEcNo = Integer.MAX_VALUE;
     }
 
     /**
-     * Get signal level as an int from 0..4
+     * Retrieve an abstract level value for the overall signal strength.
+     *
+     * @return a single integer from 0 to 4 representing the general signal quality.
+     *     0 represents very poor signal strength while 4 represents a very strong signal strength.
      */
     @Override
     public int getLevel() {
@@ -145,7 +160,10 @@
             return false;
         }
 
-        return mSignalStrength == s.mSignalStrength && mBitErrorRate == s.mBitErrorRate;
+        return mSignalStrength == s.mSignalStrength
+                && mBitErrorRate == s.mBitErrorRate
+                && mRscp == s.mRscp
+                && mEcNo == s.mEcNo;
     }
 
     /**
@@ -155,7 +173,9 @@
     public String toString() {
         return "CellSignalStrengthWcdma:"
                 + " ss=" + mSignalStrength
-                + " ber=" + mBitErrorRate;
+                + " ber=" + mBitErrorRate
+                + " rscp=" + mRscp
+                + " ecno=" + mEcNo;
     }
 
     /** Implement the Parcelable interface */
@@ -164,6 +184,8 @@
         if (DBG) log("writeToParcel(Parcel, int): " + toString());
         dest.writeInt(mSignalStrength);
         dest.writeInt(mBitErrorRate);
+        dest.writeInt(mRscp);
+        dest.writeInt(mEcNo);
     }
 
     /**
@@ -173,6 +195,8 @@
     private CellSignalStrengthWcdma(Parcel in) {
         mSignalStrength = in.readInt();
         mBitErrorRate = in.readInt();
+        mRscp = in.readInt();
+        mEcNo = in.readInt();
         if (DBG) log("CellSignalStrengthWcdma(Parcel): " + toString());
     }
 
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
index 6db8e82..53d69f4 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -39,7 +39,8 @@
  * @hide
  */
 public final class LocationAccessPolicy {
-    private static final String LOG_TAG = LocationAccessPolicy.class.getSimpleName();
+    private static final String TAG = "LocationAccessPolicy";
+    private static final boolean DBG = false;
 
     /**
      * API to determine if the caller has permissions to get cell location.
@@ -52,12 +53,13 @@
      */
     public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
             int uid, int pid, boolean throwOnDeniedPermission) throws SecurityException {
-        Trace.beginSection("TelephonyLohcationCheck");
+        Trace.beginSection("TelephonyLocationCheck");
         try {
-            // Always allow the phone process to access location. This avoid breaking legacy code
-            // that rely on public-facing APIs to access cell location, and it doesn't create a
-            // info leak risk because the cell location is stored in the phone process anyway.
-            if (uid == Process.PHONE_UID) {
+            // Always allow the phone process and system server to access location. This avoid
+            // breaking legacy code that rely on public-facing APIs to access cell location, and
+            // it doesn't create an info leak risk because the cell location is stored in the phone
+            // process anyway, and the system server already has location access.
+            if (uid == Process.PHONE_UID || uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) {
                 return true;
             }
 
@@ -72,15 +74,18 @@
                         pid, uid, "canAccessCellLocation");
             } else if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION,
                     pid, uid) == PackageManager.PERMISSION_DENIED) {
+                if (DBG) Log.w(TAG, "Permission checked failed (" + pid + "," + uid + ")");
                 return false;
             }
             final int opCode = AppOpsManager.permissionToOpCode(
                     Manifest.permission.ACCESS_COARSE_LOCATION);
             if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
                     .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
+                if (DBG) Log.w(TAG, "AppOp check failed (" + uid + "," + pkgName + ")");
                 return false;
             }
             if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
+                if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")");
                 return false;
             }
             // If the user or profile is current, permission is granted.
@@ -94,7 +99,7 @@
     private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
         LocationManager locationManager = context.getSystemService(LocationManager.class);
         if (locationManager == null) {
-            Log.w(LOG_TAG, "Couldn't get location manager, denying location access");
+            Log.w(TAG, "Couldn't get location manager, denying location access");
             return false;
         }
         return locationManager.isLocationEnabledForUser(UserHandle.of(userId));
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index bba779d..c393155 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -21,6 +21,7 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.TransportType;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -59,15 +60,15 @@
     /** Not registered. The device is not currently searching a new operator to register */
     public static final int REG_STATE_NOT_REG_NOT_SEARCHING = 0;
     /** Registered on home network */
-    public static final int REG_STATE_HOME = 1;
+    public static final int REG_STATE_HOME                  = 1;
     /** Not registered. The device is currently searching a new operator to register */
-    public static final int REG_STATE_NOT_REG_SEARCHING = 2;
+    public static final int REG_STATE_NOT_REG_SEARCHING     = 2;
     /** Registration denied */
-    public static final int REG_STATE_DENIED = 3;
+    public static final int REG_STATE_DENIED                = 3;
     /** Registration state is unknown */
-    public static final int REG_STATE_UNKNOWN = 4;
+    public static final int REG_STATE_UNKNOWN               = 4;
     /** Registered on roaming network */
-    public static final int REG_STATE_ROAMING = 5;
+    public static final int REG_STATE_ROAMING               = 5;
 
     /**
      * Supported service type
@@ -79,24 +80,24 @@
                     SERVICE_TYPE_EMERGENCY})
     public @interface ServiceType {}
 
-    public static final int SERVICE_TYPE_VOICE = 1;
-    public static final int SERVICE_TYPE_DATA = 2;
-    public static final int SERVICE_TYPE_SMS = 3;
-    public static final int SERVICE_TYPE_VIDEO = 4;
-    public static final int SERVICE_TYPE_EMERGENCY = 5;
-
-    /** {@link AccessNetworkConstants.TransportType}*/
-    private final int mTransportType;
+    public static final int SERVICE_TYPE_VOICE      = 1;
+    public static final int SERVICE_TYPE_DATA       = 2;
+    public static final int SERVICE_TYPE_SMS        = 3;
+    public static final int SERVICE_TYPE_VIDEO      = 4;
+    public static final int SERVICE_TYPE_EMERGENCY  = 5;
 
     @Domain
     private final int mDomain;
 
+    /** {@link TransportType} */
+    private final int mTransportType;
+
     @RegState
     private final int mRegState;
 
     private final int mAccessNetworkTechnology;
 
-    private final int mReasonForDenial;
+    private final int mRejectCause;
 
     private final boolean mEmergencyOnly;
 
@@ -112,22 +113,35 @@
     private DataSpecificRegistrationStates mDataSpecificStates;
 
     /**
-     * @param transportType Transport type. Must be {@link AccessNetworkConstants.TransportType}
-     * @param domain Network domain. Must be DOMAIN_CS or DOMAIN_PS.
-     * @param regState Network registration state.
-     * @param accessNetworkTechnology See TelephonyManager NETWORK_TYPE_XXXX.
-     * @param reasonForDenial Reason for denial if the registration state is DENIED.
-     * @param availableServices The supported service.
-     * @param cellIdentity The identity representing a unique cell
+     * @param domain Network domain. Must be a {@link Domain}. For {@link TransportType#WLAN}
+     * transport, this must set to {@link #DOMAIN_PS}.
+     * @param transportType Transport type. Must be one of the{@link TransportType}.
+     * @param regState Network registration state. Must be one of the {@link RegState}. For
+     * {@link TransportType#WLAN} transport, only {@link #REG_STATE_HOME} and
+     * {@link #REG_STATE_NOT_REG_NOT_SEARCHING} are valid states.
+     * @param accessNetworkTechnology Access network technology. Must be one of TelephonyManager
+     * NETWORK_TYPE_XXXX. For {@link TransportType#WLAN} transport, set to
+     * {@link TelephonyManager#NETWORK_TYPE_IWLAN}.
+     * @param rejectCause Reason for denial if the registration state is {@link #REG_STATE_DENIED}.
+     * Depending on {@code accessNetworkTechnology}, the values are defined in 3GPP TS 24.008
+     * 10.5.3.6 for UMTS, 3GPP TS 24.301 9.9.3.9 for LTE, and 3GPP2 A.S0001 6.2.2.44 for CDMA. If
+     * the reject cause is not supported or unknown, set it to 0.
+     * // TODO: Add IWLAN reject cause reference
+     * @param emergencyOnly True if this registration is for emergency only.
+     * @param availableServices The list of the supported services. Each element must be one of
+     * the {@link ServiceType}.
+     * @param cellIdentity The identity representing a unique cell or wifi AP. Set to null if the
+     * information is not available.
      */
-    public NetworkRegistrationState(int transportType, int domain, int regState,
-            int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
-            int[] availableServices, @Nullable CellIdentity cellIdentity) {
-        mTransportType = transportType;
+    public NetworkRegistrationState(@Domain int domain, int transportType, @RegState int regState,
+                                    int accessNetworkTechnology, int rejectCause,
+                                    boolean emergencyOnly, int[] availableServices,
+                                    @Nullable CellIdentity cellIdentity) {
         mDomain = domain;
+        mTransportType = transportType;
         mRegState = regState;
         mAccessNetworkTechnology = accessNetworkTechnology;
-        mReasonForDenial = reasonForDenial;
+        mRejectCause = rejectCause;
         mAvailableServices = availableServices;
         mCellIdentity = cellIdentity;
         mEmergencyOnly = emergencyOnly;
@@ -137,12 +151,14 @@
      * Constructor for voice network registration states.
      * @hide
      */
-    public NetworkRegistrationState(int transportType, int domain, int regState,
-            int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
-            int[] availableServices, @Nullable CellIdentity cellIdentity, boolean cssSupported,
-            int roamingIndicator, int systemIsInPrl, int defaultRoamingIndicator) {
-        this(transportType, domain, regState, accessNetworkTechnology,
-                reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+    public NetworkRegistrationState(int domain, int transportType, int regState,
+                                    int accessNetworkTechnology, int rejectCause,
+                                    boolean emergencyOnly, int[] availableServices,
+                                    @Nullable CellIdentity cellIdentity, boolean cssSupported,
+                                    int roamingIndicator, int systemIsInPrl,
+                                    int defaultRoamingIndicator) {
+        this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly,
+                availableServices, cellIdentity);
 
         mVoiceSpecificStates = new VoiceSpecificRegistrationStates(cssSupported, roamingIndicator,
                 systemIsInPrl, defaultRoamingIndicator);
@@ -152,21 +168,22 @@
      * Constructor for data network registration states.
      * @hide
      */
-    public NetworkRegistrationState(int transportType, int domain, int regState,
-            int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
-            int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls) {
-        this(transportType, domain, regState, accessNetworkTechnology,
-                reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+    public NetworkRegistrationState(int domain, int transportType, int regState,
+                                    int accessNetworkTechnology, int rejectCause,
+                                    boolean emergencyOnly, int[] availableServices,
+                                    @Nullable CellIdentity cellIdentity, int maxDataCalls) {
+        this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly,
+                availableServices, cellIdentity);
 
         mDataSpecificStates = new DataSpecificRegistrationStates(maxDataCalls);
     }
 
     protected NetworkRegistrationState(Parcel source) {
-        mTransportType = source.readInt();
         mDomain = source.readInt();
+        mTransportType = source.readInt();
         mRegState = source.readInt();
         mAccessNetworkTechnology = source.readInt();
-        mReasonForDenial = source.readInt();
+        mRejectCause = source.readInt();
         mEmergencyOnly = source.readBoolean();
         mAvailableServices = source.createIntArray();
         mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader());
@@ -211,10 +228,10 @@
     }
 
     /**
-     * @return Reason for denial from network.
+     * @return Network reject cause
      */
-    public int getReasonForDenial() {
-        return mReasonForDenial;
+    public int getRejectCause() {
+        return mRejectCause;
     }
 
     /**
@@ -260,12 +277,12 @@
     @Override
     public String toString() {
         return new StringBuilder("NetworkRegistrationState{")
-                .append("transportType=").append(mTransportType)
                 .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS")
+                .append("transportType=").append(mTransportType)
                 .append(" regState=").append(regStateToString(mRegState))
                 .append(" accessNetworkTechnology=")
                 .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology))
-                .append(" reasonForDenial=").append(mReasonForDenial)
+                .append(" rejectCause=").append(mRejectCause)
                 .append(" emergencyEnabled=").append(mEmergencyOnly)
                 .append(" supportedServices=").append(mAvailableServices)
                 .append(" cellIdentity=").append(mCellIdentity)
@@ -276,8 +293,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mTransportType, mDomain, mRegState, mAccessNetworkTechnology,
-                mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity,
+        return Objects.hash(mDomain, mTransportType, mRegState, mAccessNetworkTechnology,
+                mRejectCause, mEmergencyOnly, mAvailableServices, mCellIdentity,
                 mVoiceSpecificStates, mDataSpecificStates);
     }
 
@@ -290,11 +307,11 @@
         }
 
         NetworkRegistrationState other = (NetworkRegistrationState) o;
-        return mTransportType == other.mTransportType
-                && mDomain == other.mDomain
+        return mDomain == other.mDomain
+                && mTransportType == other.mTransportType
                 && mRegState == other.mRegState
                 && mAccessNetworkTechnology == other.mAccessNetworkTechnology
-                && mReasonForDenial == other.mReasonForDenial
+                && mRejectCause == other.mRejectCause
                 && mEmergencyOnly == other.mEmergencyOnly
                 && (mAvailableServices == other.mAvailableServices
                     || Arrays.equals(mAvailableServices, other.mAvailableServices))
@@ -305,11 +322,11 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mTransportType);
         dest.writeInt(mDomain);
+        dest.writeInt(mTransportType);
         dest.writeInt(mRegState);
         dest.writeInt(mAccessNetworkTechnology);
-        dest.writeInt(mReasonForDenial);
+        dest.writeInt(mRejectCause);
         dest.writeBoolean(mEmergencyOnly);
         dest.writeIntArray(mAvailableServices);
         dest.writeParcelable(mCellIdentity, 0);
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index f7e6840..4354314 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -36,8 +36,8 @@
 /**
  * Base class of network service. Services that extend NetworkService must register the service in
  * their AndroidManifest to be detected by the framework. They must be protected by the permission
- * "android.permission.BIND_NETWORK_SERVICE". The network service definition in the manifest must
- * follow the following format:
+ * "android.permission.BIND_TELEPHONY_NETWORK_SERVICE". The network service definition in the
+ * manifest must follow the following format:
  * ...
  * <service android:name=".xxxNetworkService"
  *     android:permission="android.permission.BIND_TELEPHONY_NETWORK_SERVICE" >
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index fa7988d..9e8529e 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -30,6 +30,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Contains phone state and service related information.
@@ -723,38 +724,40 @@
 
     @Override
     public int hashCode() {
-        return ((mVoiceRegState * 31)
-                + (mDataRegState * 37)
-                + mVoiceRoamingType
-                + mDataRoamingType
-                + mChannelNumber
-                + Arrays.hashCode(mCellBandwidths)
-                + (mIsManualNetworkSelection ? 1 : 0)
-                + ((null == mVoiceOperatorAlphaLong) ? 0 : mVoiceOperatorAlphaLong.hashCode())
-                + ((null == mVoiceOperatorAlphaShort) ? 0 : mVoiceOperatorAlphaShort.hashCode())
-                + ((null == mVoiceOperatorNumeric) ? 0 : mVoiceOperatorNumeric.hashCode())
-                + ((null == mDataOperatorAlphaLong) ? 0 : mDataOperatorAlphaLong.hashCode())
-                + ((null == mDataOperatorAlphaShort) ? 0 : mDataOperatorAlphaShort.hashCode())
-                + ((null == mDataOperatorNumeric) ? 0 : mDataOperatorNumeric.hashCode())
-                + mCdmaRoamingIndicator
-                + mCdmaDefaultRoamingIndicator
-                + (mIsEmergencyOnly ? 1 : 0)
-                + (mIsDataRoamingFromRegistration ? 1 : 0));
+        return Objects.hash(
+                mVoiceRegState,
+                mDataRegState,
+                mVoiceRoamingType,
+                mDataRoamingType,
+                mChannelNumber,
+                mCellBandwidths,
+                mVoiceOperatorAlphaLong,
+                mVoiceOperatorAlphaShort,
+                mVoiceOperatorNumeric,
+                mDataOperatorAlphaLong,
+                mDataOperatorAlphaShort,
+                mDataOperatorNumeric,
+                mIsManualNetworkSelection,
+                mRilVoiceRadioTechnology,
+                mRilDataRadioTechnology,
+                mCssIndicator,
+                mNetworkId,
+                mSystemId,
+                mCdmaRoamingIndicator,
+                mCdmaDefaultRoamingIndicator,
+                mCdmaEriIconIndex,
+                mCdmaEriIconMode,
+                mIsEmergencyOnly,
+                mIsDataRoamingFromRegistration,
+                mIsUsingCarrierAggregation,
+                mLteEarfcnRsrpBoost,
+                mNetworkRegistrationStates);
     }
 
     @Override
     public boolean equals (Object o) {
-        ServiceState s;
-
-        try {
-            s = (ServiceState) o;
-        } catch (ClassCastException ex) {
-            return false;
-        }
-
-        if (o == null) {
-            return false;
-        }
+        if (!(o instanceof ServiceState)) return false;
+        ServiceState s = (ServiceState) o;
 
         return (mVoiceRegState == s.mVoiceRegState
                 && mDataRegState == s.mDataRegState
@@ -1566,13 +1569,14 @@
     /**
      * Get the network registration states with given transport type and domain.
      *
+     * @param domain The network domain. Must be {@link NetworkRegistrationState#DOMAIN_CS} or
+     * {@link NetworkRegistrationState#DOMAIN_PS}.
      * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
-     * @param domain The network domain. Must be DOMAIN_CS or DOMAIN_PS.
      * @return The matching NetworkRegistrationState.
      * @hide
      */
     @SystemApi
-    public NetworkRegistrationState getNetworkRegistrationStates(int transportType, int domain) {
+    public NetworkRegistrationState getNetworkRegistrationStates(int domain, int transportType) {
         synchronized (mNetworkRegistrationStates) {
             for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
                 if (networkRegistrationState.getTransportType() == transportType
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 936505c..d76e39b 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -33,9 +33,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.DisplayMetrics;
+import android.util.Log;
 
 import java.util.Arrays;
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -105,12 +105,12 @@
     /**
      * Mobile Country Code
      */
-    private int mMcc;
+    private String mMcc;
 
     /**
      * Mobile Network Code
      */
-    private int mMnc;
+    private String mMnc;
 
     /**
      * ISO Country code for the subscription's provider
@@ -138,11 +138,11 @@
      * @hide
      */
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
-        CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
-        Bitmap icon, int mcc, int mnc, String countryIso) {
+            CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+            Bitmap icon, String mcc, String mnc, String countryIso) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
-            roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
-            null /* accessRules */, null /* accessRules */);
+                roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
+                null /* accessRules */, null /* accessRules */);
     }
 
     /**
@@ -150,7 +150,7 @@
      */
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
-            Bitmap icon, int mcc, int mnc, String countryIso,  boolean isEmbedded,
+            Bitmap icon, String mcc, String mnc, String countryIso,  boolean isEmbedded,
             @Nullable UiccAccessRule[] accessRules) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
                 roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, null /* cardId */);
@@ -161,7 +161,7 @@
      */
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
-            Bitmap icon, int mcc, int mnc, String countryIso, boolean isEmbedded,
+            Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
             @Nullable UiccAccessRule[] accessRules, String cardId) {
         this.mId = id;
         this.mIccId = iccId;
@@ -316,15 +316,43 @@
 
     /**
      * @return the MCC.
+     * @deprecated Use {@link #getMccString()} instead.
      */
+    @Deprecated
     public int getMcc() {
-        return this.mMcc;
+        try {
+            return this.mMcc == null ? 0 : Integer.valueOf(this.mMcc);
+        } catch (NumberFormatException e) {
+            Log.w(SubscriptionInfo.class.getSimpleName(), "MCC string is not a number");
+            return 0;
+        }
     }
 
     /**
      * @return the MNC.
+     * @deprecated Use {@link #getMncString()} instead.
      */
+    @Deprecated
     public int getMnc() {
+        try {
+            return this.mMnc == null ? 0 : Integer.valueOf(this.mMnc);
+        } catch (NumberFormatException e) {
+            Log.w(SubscriptionInfo.class.getSimpleName(), "MNC string is not a number");
+            return 0;
+        }
+    }
+
+    /**
+     * @return The MCC, as a string.
+     */
+    public String getMccString() {
+        return this.mMcc;
+    }
+
+    /**
+     * @return The MNC, as a string.
+     */
+    public String getMncString() {
         return this.mMnc;
     }
 
@@ -425,8 +453,8 @@
             int iconTint = source.readInt();
             String number = source.readString();
             int dataRoaming = source.readInt();
-            int mcc = source.readInt();
-            int mnc = source.readInt();
+            String mcc = source.readString();
+            String mnc = source.readString();
             String countryIso = source.readString();
             Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source);
             boolean isEmbedded = source.readBoolean();
@@ -455,8 +483,8 @@
         dest.writeInt(mIconTint);
         dest.writeString(mNumber);
         dest.writeInt(mDataRoaming);
-        dest.writeInt(mMcc);
-        dest.writeInt(mMnc);
+        dest.writeString(mMcc);
+        dest.writeString(mMnc);
         dest.writeString(mCountryIso);
         mIconBitmap.writeToParcel(dest, flags);
         dest.writeBoolean(mIsEmbedded);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index ece646c..8aa5bf6 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -253,6 +253,20 @@
     public static final int SIM_PROVISIONED = 0;
 
     /**
+     * TelephonyProvider column name for the MCC associated with a SIM, stored as a string.
+     * <P>Type: TEXT (String)</P>
+     * @hide
+     */
+    public static final String MCC_STRING = "mcc_string";
+
+    /**
+     * TelephonyProvider column name for the MNC associated with a SIM, stored as a string.
+     * <P>Type: TEXT (String)</P>
+     * @hide
+     */
+    public static final String MNC_STRING = "mnc_string";
+
+    /**
      * TelephonyProvider column name for the MCC associated with a SIM.
      * <P>Type: INTEGER (int)</P>
      * @hide
@@ -287,7 +301,7 @@
      * <P>Type: TEXT (String)</P>
      * @hide
      */
-     public static final String CARD_ID = "card_id";
+    public static final String CARD_ID = "card_id";
 
     /**
      * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
@@ -1037,24 +1051,9 @@
      */
     public int setIconTint(int tint, int subId) {
         if (VDBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
-        if (!isValidSubscriptionId(subId)) {
-            logd("[setIconTint]- fail");
-            return -1;
-        }
-
-        int result = 0;
-
-        try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
-            if (iSub != null) {
-                result = iSub.setIconTint(tint, subId);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-
-        return result;
-
+        return setSubscriptionPropertyHelper(subId, "setIconTint",
+                (iSub)-> iSub.setIconTint(tint, subId)
+        );
     }
 
     /**
@@ -1082,24 +1081,9 @@
             logd("[setDisplayName]+  displayName:" + displayName + " subId:" + subId
                     + " nameSource:" + nameSource);
         }
-        if (!isValidSubscriptionId(subId)) {
-            logd("[setDisplayName]- fail");
-            return -1;
-        }
-
-        int result = 0;
-
-        try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
-            if (iSub != null) {
-                result = iSub.setDisplayNameUsingSrc(displayName, subId, nameSource);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-
-        return result;
-
+        return setSubscriptionPropertyHelper(subId, "setDisplayName",
+                (iSub)-> iSub.setDisplayNameUsingSrc(displayName, subId, nameSource)
+        );
     }
 
     /**
@@ -1110,24 +1094,13 @@
      * @hide
      */
     public int setDisplayNumber(String number, int subId) {
-        if (number == null || !isValidSubscriptionId(subId)) {
+        if (number == null) {
             logd("[setDisplayNumber]- fail");
             return -1;
         }
-
-        int result = 0;
-
-        try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
-            if (iSub != null) {
-                result = iSub.setDisplayNumber(number, subId);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-
-        return result;
-
+        return setSubscriptionPropertyHelper(subId, "setDisplayNumber",
+                (iSub)-> iSub.setDisplayNumber(number, subId)
+        );
     }
 
     /**
@@ -1139,23 +1112,9 @@
      */
     public int setDataRoaming(int roaming, int subId) {
         if (VDBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
-        if (roaming < 0 || !isValidSubscriptionId(subId)) {
-            logd("[setDataRoaming]- fail");
-            return -1;
-        }
-
-        int result = 0;
-
-        try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
-            if (iSub != null) {
-                result = iSub.setDataRoaming(roaming, subId);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-
-        return result;
+        return setSubscriptionPropertyHelper(subId, "setDataRoaming",
+                (iSub)->iSub.setDataRoaming(roaming, subId)
+        );
     }
 
     /**
@@ -1980,4 +1939,29 @@
         }
         return false;
     }
+
+    private interface CallISubMethodHelper {
+        int callMethod(ISub iSub) throws RemoteException;
+    }
+
+    private int setSubscriptionPropertyHelper(int subId, String methodName,
+            CallISubMethodHelper helper) {
+        if (!isValidSubscriptionId(subId)) {
+            logd("[" + methodName + "]" + "- fail");
+            return -1;
+        }
+
+        int result = 0;
+
+        try {
+            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            if (iSub != null) {
+                result = helper.callMethod(iSub);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return result;
+    }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 31a81e1..b2eb5e0 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -335,11 +335,18 @@
      * <p>
      * The {@link #EXTRA_STATE} extra indicates the new call state.
      * If a receiving app has {@link android.Manifest.permission#READ_CALL_LOG} permission, a second
-     * extra {@link #EXTRA_INCOMING_NUMBER} provides the phone number for incoming and outoing calls
-     * as a String.  Note: If the receiving app has
+     * extra {@link #EXTRA_INCOMING_NUMBER} provides the phone number for incoming and outgoing
+     * calls as a String.
+     * <p>
+     * If the receiving app has
      * {@link android.Manifest.permission#READ_CALL_LOG} and
      * {@link android.Manifest.permission#READ_PHONE_STATE} permission, it will receive the
-     * broadcast twice; one with the phone number and another without it.
+     * broadcast twice; one with the {@link #EXTRA_INCOMING_NUMBER} populated with the phone number,
+     * and another with it blank.  Due to the nature of broadcasts, you cannot assume the order
+     * in which these broadcasts will arrive, however you are guaranteed to receive two in this
+     * case.  Apps which are interested in the {@link #EXTRA_INCOMING_NUMBER} can ignore the
+     * broadcasts where {@link #EXTRA_INCOMING_NUMBER} is not present in the extras (e.g. where
+     * {@link Intent#hasExtra(String)} returns {@code false}).
      * <p class="note">
      * This was a {@link android.content.Context#sendStickyBroadcast sticky}
      * broadcast in version 1.0, but it is no longer sticky.
@@ -488,10 +495,19 @@
     public static final String EXTRA_STATE_OFFHOOK = PhoneConstants.State.OFFHOOK.toString();
 
     /**
-     * The lookup key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast
-     * for a String containing the incoming phone number.
-     * Only valid when the new call state is RINGING.
-     *
+     * Extra key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast
+     * for a String containing the incoming or outgoing phone number.
+     * <p>
+     * This extra is only populated for receivers of the {@link #ACTION_PHONE_STATE_CHANGED}
+     * broadcast which have been granted the {@link android.Manifest.permission#READ_CALL_LOG} and
+     * {@link android.Manifest.permission#READ_PHONE_STATE} permissions.
+     * <p>
+     * For incoming calls, the phone number is only guaranteed to be populated when the
+     * {@link #EXTRA_STATE} changes from {@link #EXTRA_STATE_IDLE} to {@link #EXTRA_STATE_RINGING}.
+     * If the incoming caller is from an unknown number, the extra will be populated with an empty
+     * string.
+     * For outgoing calls, the phone number is only guaranteed to be populated when the
+     * {@link #EXTRA_STATE} changes from {@link #EXTRA_STATE_IDLE} to {@link #EXTRA_STATE_OFFHOOK}.
      * <p class="note">
      * Retrieve with
      * {@link android.content.Intent#getStringExtra(String)}.
@@ -1296,6 +1312,33 @@
     }
 
     /**
+     * Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not
+     * available.
+     */
+    public String getTypeAllocationCode() {
+        return getTypeAllocationCode(getSlotIndex());
+    }
+
+    /**
+     * Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not
+     * available.
+     *
+     * @param slotIndex of which Type Allocation Code is returned
+     */
+    public String getTypeAllocationCode(int slotIndex) {
+        ITelephony telephony = getITelephony();
+        if (telephony == null) return null;
+
+        try {
+            return telephony.getTypeAllocationCodeForSlot(slotIndex);
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -1331,6 +1374,33 @@
     }
 
     /**
+     * Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
+     * available.
+     */
+    public String getManufacturerCode() {
+        return getManufacturerCode(getSlotIndex());
+    }
+
+    /**
+     * Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
+     * available.
+     *
+     * @param slotIndex of which Type Allocation Code is returned
+     */
+    public String getManufacturerCode(int slotIndex) {
+        ITelephony telephony = getITelephony();
+        if (telephony == null) return null;
+
+        try {
+            return telephony.getManufacturerCodeForSlot(slotIndex);
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /**
      * Returns the Network Access Identifier (NAI). Return null if NAI is not available.
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -1483,7 +1553,8 @@
             ITelephony telephony = getITelephony();
             if (telephony == null)
                 return null;
-            return telephony.getNeighboringCellInfo(mContext.getOpPackageName());
+            return telephony.getNeighboringCellInfo(mContext.getOpPackageName(),
+                    mContext.getApplicationInfo().targetSdkVersion);
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -2767,7 +2838,8 @@
     }
 
     /**
-     * Gets all the UICC slots.
+     * 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.
      *
      * @return UiccSlotInfo array.
      *
@@ -5432,23 +5504,6 @@
     }
 
     /**
-     * @return true if the IMS resolver is busy resolving a binding and should not be considered
-     * available, false if the IMS resolver is idle.
-     * @hide
-     */
-    public boolean isResolvingImsBinding() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null) {
-                return telephony.isResolvingImsBinding();
-            }
-        } catch (RemoteException e) {
-            Rlog.e(TAG, "isResolvingImsBinding, RemoteException: " + e.getMessage());
-        }
-        return false;
-    }
-
-    /**
      * Set IMS registration state
      *
      * @param Registration state
@@ -6554,7 +6609,7 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.canChangeDtmfToneLength();
+                return telephony.canChangeDtmfToneLength(mSubId, getOpPackageName());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#canChangeDtmfToneLength", e);
@@ -6573,7 +6628,7 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.isWorldPhone();
+                return telephony.isWorldPhone(mSubId, getOpPackageName());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#isWorldPhone", e);
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index c68a780..5dc0cff 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -24,12 +24,14 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.Telephony;
+import android.provider.Telephony.Carriers;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.InetAddress;
@@ -54,20 +56,31 @@
     private static final String LOG_TAG = "ApnSetting";
     private static final boolean VDBG = false;
 
-    private static final Map<String, Integer> APN_TYPE_STRING_MAP;
-    private static final Map<Integer, String> APN_TYPE_INT_MAP;
-    private static final Map<String, Integer> PROTOCOL_STRING_MAP;
-    private static final Map<Integer, String> PROTOCOL_INT_MAP;
-    private static final Map<String, Integer> MVNO_TYPE_STRING_MAP;
-    private static final Map<Integer, String> MVNO_TYPE_INT_MAP;
-    private static final int NOT_IN_MAP_INT = -1;
-    private static final int NO_PORT_SPECIFIED = -1;
+    private static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*";
+    private static final String V3_FORMAT_REGEX = "^\\[ApnSettingV3\\]\\s*";
+    private static final String V4_FORMAT_REGEX = "^\\[ApnSettingV4\\]\\s*";
+    private static final String V5_FORMAT_REGEX = "^\\[ApnSettingV5\\]\\s*";
 
-    /** All APN types except IA. */
-    private static final int TYPE_ALL_BUT_IA = ApnTypes.ALL & (~ApnTypes.IA);
+    /**
+     * Default value for mtu if it's not set. Moved from PhoneConstants.
+     * @hide
+     */
+    public static final int UNSET_MTU = 0;
+    private static final int UNSPECIFIED_INT = -1;
+    private static final String UNSPECIFIED_STRING = "";
 
-    /** APN type for default data traffic and HiPri traffic. */
-    public static final int TYPE_DEFAULT = ApnTypes.DEFAULT | ApnTypes.HIPRI;
+    /**
+     * APN type for none. Should only be used for initialization.
+     * @hide
+     */
+    public static final int TYPE_NONE = ApnTypes.NONE;
+    /**
+     * APN type for all APNs.
+     * @hide
+     */
+    public static final int TYPE_ALL = ApnTypes.ALL;
+    /** APN type for default data traffic. */
+    public static final int TYPE_DEFAULT = ApnTypes.DEFAULT;
     /** APN type for MMS traffic. */
     public static final int TYPE_MMS = ApnTypes.MMS;
     /** APN type for SUPL assisted GPS. */
@@ -166,9 +179,16 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface MvnoType {}
 
+    private static final Map<String, Integer> APN_TYPE_STRING_MAP;
+    private static final Map<Integer, String> APN_TYPE_INT_MAP;
+    private static final Map<String, Integer> PROTOCOL_STRING_MAP;
+    private static final Map<Integer, String> PROTOCOL_INT_MAP;
+    private static final Map<String, Integer> MVNO_TYPE_STRING_MAP;
+    private static final Map<Integer, String> MVNO_TYPE_INT_MAP;
+
     static {
         APN_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
-        APN_TYPE_STRING_MAP.put("*", TYPE_ALL_BUT_IA);
+        APN_TYPE_STRING_MAP.put("*", TYPE_ALL);
         APN_TYPE_STRING_MAP.put("default", TYPE_DEFAULT);
         APN_TYPE_STRING_MAP.put("mms", TYPE_MMS);
         APN_TYPE_STRING_MAP.put("supl", TYPE_SUPL);
@@ -216,10 +236,10 @@
 
     private final String mEntryName;
     private final String mApnName;
-    private final InetAddress mProxyAddress;
+    private final String mProxyAddress;
     private final int mProxyPort;
     private final Uri mMmsc;
-    private final InetAddress mMmsProxyAddress;
+    private final String mMmsProxyAddress;
     private final int mMmsProxyPort;
     private final String mUser;
     private final String mPassword;
@@ -245,6 +265,8 @@
     private final int mMvnoType;
     private final String mMvnoMatchData;
 
+    private final int mApnSetId;
+
     private boolean mPermanentFailed = false;
 
     /**
@@ -322,6 +344,21 @@
     }
 
     /**
+     * Returns the APN set id.
+     *
+     * APNs that are part of the same set should be preferred together, e.g. if the
+     * user selects a default APN with apnSetId=1, then we will prefer all APNs with apnSetId = 1.
+     *
+     * If the apnSetId = Carriers.NO_SET_SET(=0) then the APN is not part of a set.
+     *
+     * @return the APN set id
+     * @hide
+     */
+    public int getApnSetId() {
+        return mApnSetId;
+    }
+
+    /**
      * Indicates this APN setting is permanently failed and cannot be
      * retried by the retry manager anymore.
      *
@@ -367,8 +404,19 @@
      * This is a known problem and will be addressed in a future release.
      *
      * @return the HTTP proxy address or {@code null} if DNS isn’t available to resolve a hostname
+     * @deprecated use {@link #getProxyAddressAsString()} instead.
      */
+    @Deprecated
     public InetAddress getProxyAddress() {
+        return inetAddressFromString(mProxyAddress);
+    }
+
+    /**
+     * Returns the proxy address of the APN.
+     *
+     * @return proxy address.
+     */
+    public String getProxyAddressAsString() {
         return mProxyAddress;
     }
 
@@ -396,8 +444,19 @@
      * restriction. This is a known problem and will be addressed in a future release.
      *
      * @return the MMS proxy address or {@code null} if DNS isn’t available to resolve a hostname
+     * @deprecated use {@link #getMmsProxyAddressAsString()} instead.
      */
+    @Deprecated
     public InetAddress getMmsProxyAddress() {
+        return inetAddressFromString(mMmsProxyAddress);
+    }
+
+    /**
+     * Returns the MMS proxy address of the APN.
+     *
+     * @return MMS proxy address.
+     */
+    public String getMmsProxyAddressAsString() {
         return mMmsProxyAddress;
     }
 
@@ -563,25 +622,27 @@
         this.mMaxConnsTime = builder.mMaxConnsTime;
         this.mMvnoType = builder.mMvnoType;
         this.mMvnoMatchData = builder.mMvnoMatchData;
+        this.mApnSetId = builder.mApnSetId;
     }
 
     /** @hide */
     public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName,
-            String apnName, InetAddress proxy, int port, Uri mmsc, InetAddress mmsProxy,
-            int mmsPort, String user, String password, int authType, int mApnTypeBitmask,
-            int protocol, int roamingProtocol, boolean carrierEnabled,
-            int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
-            int waitTime, int maxConnsTime, int mtu, int mvnoType, String mvnoMatchData) {
+            String apnName, String proxyAddress, int proxyPort, Uri mmsc,
+            String mmsProxyAddress, int mmsProxyPort, String user, String password,
+            int authType, int mApnTypeBitmask, int protocol, int roamingProtocol,
+            boolean carrierEnabled, int networkTypeBitmask, int profileId, boolean modemCognitive,
+            int maxConns, int waitTime, int maxConnsTime, int mtu, int mvnoType,
+            String mvnoMatchData, int apnSetId) {
         return new Builder()
                 .setId(id)
                 .setOperatorNumeric(operatorNumeric)
                 .setEntryName(entryName)
                 .setApnName(apnName)
-                .setProxyAddress(proxy)
-                .setProxyPort(port)
+                .setProxyAddress(proxyAddress)
+                .setProxyPort(proxyPort)
                 .setMmsc(mmsc)
-                .setMmsProxyAddress(mmsProxy)
-                .setMmsProxyPort(mmsPort)
+                .setMmsProxyAddress(mmsProxyAddress)
+                .setMmsProxyPort(mmsProxyPort)
                 .setUser(user)
                 .setPassword(password)
                 .setAuthType(authType)
@@ -598,12 +659,28 @@
                 .setMtu(mtu)
                 .setMvnoType(mvnoType)
                 .setMvnoMatchData(mvnoMatchData)
-                .build();
+                .setApnSetId(apnSetId)
+                .buildWithoutCheck();
+    }
+
+    /** @hide */
+    public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName,
+            String apnName, String proxyAddress, int proxyPort, Uri mmsc,
+            String mmsProxyAddress, int mmsProxyPort, String user, String password,
+            int authType, int mApnTypeBitmask, int protocol, int roamingProtocol,
+            boolean carrierEnabled, int networkTypeBitmask, int profileId, boolean modemCognitive,
+            int maxConns, int waitTime, int maxConnsTime, int mtu, int mvnoType,
+            String mvnoMatchData) {
+        return makeApnSetting(id, operatorNumeric, entryName, apnName, proxyAddress, proxyPort,
+            mmsc, mmsProxyAddress, mmsProxyPort, user, password, authType, mApnTypeBitmask,
+            protocol, roamingProtocol, carrierEnabled, networkTypeBitmask, profileId,
+            modemCognitive, maxConns, waitTime, maxConnsTime, mtu, mvnoType, mvnoMatchData,
+            Carriers.NO_SET_SET);
     }
 
     /** @hide */
     public static ApnSetting makeApnSetting(Cursor cursor) {
-        final int apnTypesBitmask = parseTypes(
+        final int apnTypesBitmask = getApnTypesBitmaskFromString(
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
         int networkTypeBitmask = cursor.getInt(
                 cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
@@ -619,25 +696,25 @@
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
-                inetAddressFromString(cursor.getString(
-                        cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
+                cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)),
                 portFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT))),
                 UriFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
-                inetAddressFromString(cursor.getString(
-                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
+                cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)),
                 portFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT))),
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
                 apnTypesBitmask,
-                nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
-                    cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)))),
-                nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
+                getProtocolIntFromString(
+                    cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL))),
+                getProtocolIntFromString(
                     cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.ROAMING_PROTOCOL)))),
+                        Telephony.Carriers.ROAMING_PROTOCOL))),
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.CARRIER_ENABLED)) == 1,
                 networkTypeBitmask,
@@ -649,41 +726,225 @@
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MAX_CONNS_TIME)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
-                nullToNotInMapInt(MVNO_TYPE_STRING_MAP.get(
+                getMvnoTypeIntFromString(
                     cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.MVNO_TYPE)))),
+                        Telephony.Carriers.MVNO_TYPE))),
                 cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.MVNO_MATCH_DATA)));
+                        Telephony.Carriers.MVNO_MATCH_DATA)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN_SET_ID)));
     }
 
     /** @hide */
     public static ApnSetting makeApnSetting(ApnSetting apn) {
         return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName,
-                apn.mProxyAddress, apn.mProxyPort, apn.mMmsc, apn.mMmsProxyAddress, apn.mMmsProxyPort, apn.mUser,
-                apn.mPassword, apn.mAuthType, apn.mApnTypeBitmask, apn.mProtocol, apn.mRoamingProtocol,
-                apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId,
-                apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
-                apn.mMvnoType, apn.mMvnoMatchData);
+                apn.mProxyAddress, apn.mProxyPort, apn.mMmsc, apn.mMmsProxyAddress,
+                apn.mMmsProxyPort, apn.mUser, apn.mPassword, apn.mAuthType, apn.mApnTypeBitmask,
+                apn.mProtocol, apn.mRoamingProtocol, apn.mCarrierEnabled, apn.mNetworkTypeBitmask,
+                apn.mProfileId, apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime,
+                apn.mMaxConnsTime, apn.mMtu, apn.mMvnoType, apn.mMvnoMatchData, apn.mApnSetId);
     }
 
-    /** @hide */
+    /**
+     * Creates an ApnSetting object from a string.
+     *
+     * @param data the string to read.
+     *
+     * The string must be in one of two formats (newlines added for clarity,
+     * spaces are optional):
+     *
+     * v1 format:
+     *   <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
+     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
+     *   <type>[| <type>...],
+     *
+     * v2 format:
+     *   [ApnSettingV2] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
+     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
+     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
+     *
+     * v3 format:
+     *   [ApnSettingV3] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
+     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
+     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
+     *   <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
+     *   <mvnoType>, <mvnoMatchData>
+     *
+     * v4 format:
+     *   [ApnSettingV4] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
+     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
+     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
+     *   <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
+     *   <mvnoType>, <mvnoMatchData>, <networkTypeBitmask>
+     *
+     * v5 format:
+     *   [ApnSettingV5] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
+     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
+     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
+     *   <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
+     *   <mvnoType>, <mvnoMatchData>, <networkTypeBitmask>, <apnSetId>
+     *
+     * Note that the strings generated by {@link #toString()} do not contain the username
+     * and password and thus cannot be read by this method.
+     *
+     * This method may return {@code null} if the input string is invalid.
+     *
+     * @hide
+     */
+    public static ApnSetting fromString(String data) {
+        if (data == null) return null;
+
+        int version;
+        // matches() operates on the whole string, so append .* to the regex.
+        if (data.matches(V5_FORMAT_REGEX + ".*")) {
+            version = 5;
+            data = data.replaceFirst(V5_FORMAT_REGEX, "");
+        } else if (data.matches(V4_FORMAT_REGEX + ".*")) {
+            version = 4;
+            data = data.replaceFirst(V4_FORMAT_REGEX, "");
+        } else if (data.matches(V3_FORMAT_REGEX + ".*")) {
+            version = 3;
+            data = data.replaceFirst(V3_FORMAT_REGEX, "");
+        } else if (data.matches(V2_FORMAT_REGEX + ".*")) {
+            version = 2;
+            data = data.replaceFirst(V2_FORMAT_REGEX, "");
+        } else {
+            version = 1;
+        }
+
+        String[] a = data.split("\\s*,\\s*");
+        if (a.length < 14) {
+            return null;
+        }
+
+        int authType;
+        try {
+            authType = Integer.parseInt(a[12]);
+        } catch (NumberFormatException e) {
+            authType = 0;
+        }
+
+        String[] typeArray;
+        String protocol, roamingProtocol;
+        boolean carrierEnabled;
+        int bearerBitmask = 0;
+        int networkTypeBitmask = 0;
+        int profileId = 0;
+        boolean modemCognitive = false;
+        int maxConns = 0;
+        int waitTime = 0;
+        int maxConnsTime = 0;
+        int mtu = UNSET_MTU;
+        String mvnoType = "";
+        String mvnoMatchData = "";
+        int apnSetId = Carriers.NO_SET_SET;
+        if (version == 1) {
+            typeArray = new String[a.length - 13];
+            System.arraycopy(a, 13, typeArray, 0, a.length - 13);
+            protocol = PROTOCOL_INT_MAP.get(PROTOCOL_IP);
+            roamingProtocol = PROTOCOL_INT_MAP.get(PROTOCOL_IP);
+            carrierEnabled = true;
+        } else {
+            if (a.length < 18) {
+                return null;
+            }
+            typeArray = a[13].split("\\s*\\|\\s*");
+            protocol = a[14];
+            roamingProtocol = a[15];
+            carrierEnabled = Boolean.parseBoolean(a[16]);
+
+            bearerBitmask = ServiceState.getBitmaskFromString(a[17]);
+
+            if (a.length > 22) {
+                modemCognitive = Boolean.parseBoolean(a[19]);
+                try {
+                    profileId = Integer.parseInt(a[18]);
+                    maxConns = Integer.parseInt(a[20]);
+                    waitTime = Integer.parseInt(a[21]);
+                    maxConnsTime = Integer.parseInt(a[22]);
+                } catch (NumberFormatException e) {
+                }
+            }
+            if (a.length > 23) {
+                try {
+                    mtu = Integer.parseInt(a[23]);
+                } catch (NumberFormatException e) {
+                }
+            }
+            if (a.length > 25) {
+                mvnoType = a[24];
+                mvnoMatchData = a[25];
+            }
+            if (a.length > 26) {
+                networkTypeBitmask = ServiceState.getBitmaskFromString(a[26]);
+            }
+            if (a.length > 27) {
+                apnSetId = Integer.parseInt(a[27]);
+            }
+        }
+
+        // If both bearerBitmask and networkTypeBitmask were specified, bearerBitmask would be
+        // ignored.
+        if (networkTypeBitmask == 0) {
+            networkTypeBitmask =
+                ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask);
+        }
+        return makeApnSetting(-1, a[10] + a[11], a[0], a[1], a[2],
+            portFromString(a[3]), UriFromString(a[7]), a[8],
+            portFromString(a[9]), a[4], a[5], authType,
+            getApnTypesBitmaskFromString(TextUtils.join(",", typeArray)),
+            getProtocolIntFromString(protocol), getProtocolIntFromString(roamingProtocol),
+            carrierEnabled, networkTypeBitmask, profileId, modemCognitive, maxConns, waitTime,
+            maxConnsTime, mtu, getMvnoTypeIntFromString(mvnoType), mvnoMatchData, apnSetId);
+    }
+
+    /**
+     * Creates an array of ApnSetting objects from a string.
+     *
+     * @param data the string to read.
+     *
+     * Builds on top of the same format used by fromString, but allows for multiple entries
+     * separated by ";".
+     *
+     * @hide
+     */
+    public static List<ApnSetting> arrayFromString(String data) {
+        List<ApnSetting> retVal = new ArrayList<ApnSetting>();
+        if (TextUtils.isEmpty(data)) {
+            return retVal;
+        }
+        String[] apnStrings = data.split("\\s*;\\s*");
+        for (String apnString : apnStrings) {
+            ApnSetting apn = fromString(apnString);
+            if (apn != null) {
+                retVal.add(apn);
+            }
+        }
+        return retVal;
+    }
+
+    /**
+     * Returns the string representation of ApnSetting.
+     *
+     * This method prints null for unset elements. The output doesn't contain password or user.
+     * @hide
+     */
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("[ApnSettingV4] ")
+        sb.append("[ApnSettingV5] ")
                 .append(mEntryName)
                 .append(", ").append(mId)
                 .append(", ").append(mOperatorNumeric)
                 .append(", ").append(mApnName)
-                .append(", ").append(inetAddressToString(mProxyAddress))
+                .append(", ").append(mProxyAddress)
                 .append(", ").append(UriToString(mMmsc))
-                .append(", ").append(inetAddressToString(mMmsProxyAddress))
+                .append(", ").append(mMmsProxyAddress)
                 .append(", ").append(portToString(mMmsProxyPort))
                 .append(", ").append(portToString(mProxyPort))
                 .append(", ").append(mAuthType).append(", ");
-        final String[] types = deParseTypes(mApnTypeBitmask).split(",");
-        sb.append(TextUtils.join(" | ", types)).append(", ");
-        sb.append(", ").append(mProtocol);
-        sb.append(", ").append(mRoamingProtocol);
+        final String[] types = getApnTypesStringFromBitmask(mApnTypeBitmask).split(",");
+        sb.append(TextUtils.join(" | ", types));
+        sb.append(", ").append(PROTOCOL_INT_MAP.get(mProtocol));
+        sb.append(", ").append(PROTOCOL_INT_MAP.get(mRoamingProtocol));
         sb.append(", ").append(mCarrierEnabled);
         sb.append(", ").append(mProfileId);
         sb.append(", ").append(mModemCognitive);
@@ -691,10 +952,11 @@
         sb.append(", ").append(mWaitTime);
         sb.append(", ").append(mMaxConnsTime);
         sb.append(", ").append(mMtu);
-        sb.append(", ").append(mMvnoType);
+        sb.append(", ").append(MVNO_TYPE_INT_MAP.get(mMvnoType));
         sb.append(", ").append(mMvnoMatchData);
         sb.append(", ").append(mPermanentFailed);
         sb.append(", ").append(mNetworkTypeBitmask);
+        sb.append(", ").append(mApnSetId);
         return sb.toString();
     }
 
@@ -703,22 +965,34 @@
      * @hide
      */
     public boolean hasMvnoParams() {
-        return (mMvnoType != NOT_IN_MAP_INT) && !TextUtils.isEmpty(mMvnoMatchData);
+        return !TextUtils.isEmpty(getMvnoTypeStringFromInt(mMvnoType))
+            && !TextUtils.isEmpty(mMvnoMatchData);
+    }
+
+    private boolean hasApnType(int type) {
+        return (mApnTypeBitmask & type) == type;
     }
 
     /** @hide */
     public boolean canHandleType(@ApnType int type) {
-        return mCarrierEnabled && ((mApnTypeBitmask & type) == type);
+        if (!mCarrierEnabled) {
+            return false;
+        }
+        // DEFAULT can handle HIPRI.
+        if (hasApnType(type) || (type == TYPE_HIPRI && hasApnType(TYPE_DEFAULT))) {
+            return true;
+        }
+        return false;
     }
 
-    // check whether the types of two APN same (even only one type of each APN is same)
+    // Check whether the types of two APN same (even only one type of each APN is same).
     private boolean typeSameAny(ApnSetting first, ApnSetting second) {
         if (VDBG) {
             StringBuilder apnType1 = new StringBuilder(first.mApnName + ": ");
-            apnType1.append(deParseTypes(first.mApnTypeBitmask));
+            apnType1.append(getApnTypesStringFromBitmask(first.mApnTypeBitmask));
 
             StringBuilder apnType2 = new StringBuilder(second.mApnName + ": ");
-            apnType2.append(deParseTypes(second.mApnTypeBitmask));
+            apnType2.append(getApnTypesStringFromBitmask(second.mApnTypeBitmask));
 
             Rlog.d(LOG_TAG, "APN1: is " + apnType1);
             Rlog.d(LOG_TAG, "APN2: is " + apnType2);
@@ -738,7 +1012,7 @@
     }
 
     // TODO - if we have this function we should also have hashCode.
-    // Also should handle changes in type order and perhaps case-insensitivity
+    // Also should handle changes in type order and perhaps case-insensitivity.
     /** @hide */
     public boolean equals(Object o) {
         if (o instanceof ApnSetting == false) {
@@ -771,7 +1045,8 @@
                 && Objects.equals(mMtu, other.mMtu)
                 && Objects.equals(mMvnoType, other.mMvnoType)
                 && Objects.equals(mMvnoMatchData, other.mMvnoMatchData)
-                && Objects.equals(mNetworkTypeBitmask, other.mNetworkTypeBitmask);
+                && Objects.equals(mNetworkTypeBitmask, other.mNetworkTypeBitmask)
+                && Objects.equals(mApnSetId, other.mApnSetId);
     }
 
     /**
@@ -815,7 +1090,8 @@
                 && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
                 && Objects.equals(mMtu, other.mMtu)
                 && Objects.equals(mMvnoType, other.mMvnoType)
-                && Objects.equals(mMvnoMatchData, other.mMvnoMatchData);
+                && Objects.equals(mMvnoMatchData, other.mMvnoMatchData)
+                && Objects.equals(mApnSetId, other.mApnSetId);
     }
 
     /**
@@ -831,7 +1107,7 @@
                 && Objects.equals(this.mApnName, other.mApnName)
                 && !typeSameAny(this, other)
                 && xorEquals(this.mProxyAddress, other.mProxyAddress)
-                && xorEqualsPort(this.mProxyPort, other.mProxyPort)
+                && xorEqualsInt(this.mProxyPort, other.mProxyPort)
                 && xorEquals(this.mProtocol, other.mProtocol)
                 && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
@@ -840,29 +1116,64 @@
                 && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
                 && xorEquals(this.mMmsc, other.mMmsc)
                 && xorEquals(this.mMmsProxyAddress, other.mMmsProxyAddress)
-                && xorEqualsPort(this.mMmsProxyPort, other.mMmsProxyPort))
-                && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask);
+                && xorEqualsInt(this.mMmsProxyPort, other.mMmsProxyPort))
+                && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask)
+                && Objects.equals(mApnSetId, other.mApnSetId);
     }
 
-    // Equal or one is not specified.
-    private boolean xorEquals(String first, String second) {
-        return (Objects.equals(first, second)
-                || TextUtils.isEmpty(first)
-                || TextUtils.isEmpty(second));
-    }
-
-    // Equal or one is not null.
+    // Equal or one is null.
     private boolean xorEquals(Object first, Object second) {
         return first == null || second == null || first.equals(second);
     }
 
     // Equal or one is not specified.
-    private boolean xorEqualsPort(int first, int second) {
-        return first == NO_PORT_SPECIFIED || second == NO_PORT_SPECIFIED
+    private boolean xorEqualsInt(int first, int second) {
+        return first == UNSPECIFIED_INT || second == UNSPECIFIED_INT
             || Objects.equals(first, second);
     }
 
-    private String deParseTypes(int apnTypeBitmask) {
+    private String nullToEmpty(String stringValue) {
+        return stringValue == null ? UNSPECIFIED_STRING : stringValue;
+    }
+
+    /**
+     * @hide
+     * Called by {@link android.app.admin.DevicePolicyManager} to convert this APN into
+     * ContentValue. If a field is not specified then we put "" instead of null.
+     */
+    public ContentValues toContentValues() {
+        ContentValues apnValue = new ContentValues();
+        apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric));
+        apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName));
+        apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName));
+        apnValue.put(Telephony.Carriers.PROXY, nullToEmpty(mProxyAddress));
+        apnValue.put(Telephony.Carriers.PORT, nullToEmpty(portToString(mProxyPort)));
+        apnValue.put(Telephony.Carriers.MMSC, nullToEmpty(UriToString(mMmsc)));
+        apnValue.put(Telephony.Carriers.MMSPORT, nullToEmpty(portToString(mMmsProxyPort)));
+        apnValue.put(Telephony.Carriers.MMSPROXY, nullToEmpty(
+                mMmsProxyAddress));
+        apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser));
+        apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword));
+        apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
+        String apnType = getApnTypesStringFromBitmask(mApnTypeBitmask);
+        apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType));
+        apnValue.put(Telephony.Carriers.PROTOCOL,
+                getProtocolStringFromInt(mProtocol));
+        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL,
+                getProtocolStringFromInt(mRoamingProtocol));
+        apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
+        apnValue.put(Telephony.Carriers.MVNO_TYPE, getMvnoTypeStringFromInt(mMvnoType));
+        apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
+
+        return apnValue;
+    }
+
+    /**
+     * @param apnTypeBitmask bitmask of APN types.
+     * @return comma delimited list of APN types.
+     * @hide
+     */
+    public static String getApnTypesStringFromBitmask(int apnTypeBitmask) {
         List<String> types = new ArrayList<>();
         for (Integer type : APN_TYPE_INT_MAP.keySet()) {
             if ((apnTypeBitmask & type) == type) {
@@ -872,54 +1183,19 @@
         return TextUtils.join(",", types);
     }
 
-    private String nullToEmpty(String stringValue) {
-        return stringValue == null ? "" : stringValue;
-    }
-
-    /** @hide */
-    // Called by DPM.
-    public ContentValues toContentValues() {
-        ContentValues apnValue = new ContentValues();
-        apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric));
-        apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName));
-        apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName));
-        apnValue.put(Telephony.Carriers.PROXY, mProxyAddress == null ? ""
-            : inetAddressToString(mProxyAddress));
-        apnValue.put(Telephony.Carriers.PORT, portToString(mProxyPort));
-        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : UriToString(mMmsc));
-        apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsProxyPort));
-        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxyAddress == null
-                ? "" : inetAddressToString(mMmsProxyAddress));
-        apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser));
-        apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword));
-        apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
-        String apnType = deParseTypes(mApnTypeBitmask);
-        apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType));
-        apnValue.put(Telephony.Carriers.PROTOCOL,
-            nullToEmpty(PROTOCOL_INT_MAP.get(mProtocol)));
-        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL,
-            nullToEmpty(PROTOCOL_INT_MAP.get(mRoamingProtocol)));
-        apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
-        apnValue.put(Telephony.Carriers.MVNO_TYPE,
-            nullToEmpty(MVNO_TYPE_INT_MAP.get(mMvnoType)));
-        apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
-
-        return apnValue;
-    }
-
     /**
-     * @param types comma delimited list of APN types
-     * @return bitmask of APN types
+     * @param types comma delimited list of APN types.
+     * @return bitmask of APN types.
      * @hide
      */
-    public static int parseTypes(String types) {
+    public static int getApnTypesBitmaskFromString(String types) {
         // If unset, set to ALL.
         if (TextUtils.isEmpty(types)) {
-            return TYPE_ALL_BUT_IA;
+            return TYPE_ALL;
         } else {
             int result = 0;
             for (String str : types.split(",")) {
-                Integer type = APN_TYPE_STRING_MAP.get(str);
+                Integer type = APN_TYPE_STRING_MAP.get(str.toLowerCase());
                 if (type != null) {
                     result |= type;
                 }
@@ -928,15 +1204,40 @@
         }
     }
 
+    /** @hide */
+    public static int getMvnoTypeIntFromString(String mvnoType) {
+        Integer mvnoTypeInt = MVNO_TYPE_STRING_MAP.get(mvnoType);
+        return  mvnoTypeInt == null ? UNSPECIFIED_INT : mvnoTypeInt;
+    }
+
+    /** @hide */
+    public static String getMvnoTypeStringFromInt(int mvnoType) {
+        String mvnoTypeString = MVNO_TYPE_INT_MAP.get(mvnoType);
+        return  mvnoTypeString == null ? UNSPECIFIED_STRING : mvnoTypeString;
+    }
+
+    /** @hide */
+    public static int getProtocolIntFromString(String protocol) {
+        Integer protocolInt = PROTOCOL_STRING_MAP.get(protocol);
+        return  protocolInt == null ? UNSPECIFIED_INT : protocolInt;
+    }
+
+    /** @hide */
+    public static String getProtocolStringFromInt(int protocol) {
+        String protocolString = PROTOCOL_INT_MAP.get(protocol);
+        return  protocolString == null ? UNSPECIFIED_STRING : protocolString;
+    }
+
     private static Uri UriFromString(String uri) {
         return TextUtils.isEmpty(uri) ? null : Uri.parse(uri);
     }
 
     private static String UriToString(Uri uri) {
-        return uri == null ? "" : uri.toString();
+        return uri == null ? null : uri.toString();
     }
 
-    private static InetAddress inetAddressFromString(String inetAddress) {
+    /** @hide */
+    public static InetAddress inetAddressFromString(String inetAddress) {
         if (TextUtils.isEmpty(inetAddress)) {
             return null;
         }
@@ -948,7 +1249,8 @@
         }
     }
 
-    private static String inetAddressToString(InetAddress inetAddress) {
+    /** @hide */
+    public static String inetAddressToString(InetAddress inetAddress) {
         if (inetAddress == null) {
             return null;
         }
@@ -965,7 +1267,7 @@
     }
 
     private static int portFromString(String strPort) {
-        int port = NO_PORT_SPECIFIED;
+        int port = UNSPECIFIED_INT;
         if (!TextUtils.isEmpty(strPort)) {
             try {
                 port = Integer.parseInt(strPort);
@@ -977,7 +1279,7 @@
     }
 
     private static String portToString(int port) {
-        return port == NO_PORT_SPECIFIED ? "" : Integer.toString(port);
+        return port == UNSPECIFIED_INT ? null : Integer.toString(port);
     }
 
     // Implement Parcelable.
@@ -994,10 +1296,10 @@
         dest.writeString(mOperatorNumeric);
         dest.writeString(mEntryName);
         dest.writeString(mApnName);
-        dest.writeValue(mProxyAddress);
+        dest.writeString(mProxyAddress);
         dest.writeInt(mProxyPort);
         dest.writeValue(mMmsc);
-        dest.writeValue(mMmsProxyAddress);
+        dest.writeString(mMmsProxyAddress);
         dest.writeInt(mMmsProxyPort);
         dest.writeString(mUser);
         dest.writeString(mPassword);
@@ -1005,7 +1307,7 @@
         dest.writeInt(mApnTypeBitmask);
         dest.writeInt(mProtocol);
         dest.writeInt(mRoamingProtocol);
-        dest.writeInt(mCarrierEnabled ? 1: 0);
+        dest.writeBoolean(mCarrierEnabled);
         dest.writeInt(mMvnoType);
         dest.writeInt(mNetworkTypeBitmask);
     }
@@ -1015,10 +1317,10 @@
         final String operatorNumeric = in.readString();
         final String entryName = in.readString();
         final String apnName = in.readString();
-        final InetAddress proxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
+        final String proxy = in.readString();
         final int port = in.readInt();
         final Uri mmsc = (Uri)in.readValue(Uri.class.getClassLoader());
-        final InetAddress mmsProxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
+        final String mmsProxy = in.readString();
         final int mmsPort = in.readInt();
         final String user = in.readString();
         final String password = in.readString();
@@ -1026,7 +1328,7 @@
         final int apnTypesBitmask = in.readInt();
         final int protocol = in.readInt();
         final int roamingProtocol = in.readInt();
-        final boolean carrierEnabled = in.readInt() > 0;
+        final boolean carrierEnabled = in.readBoolean();
         final int mvnoType = in.readInt();
         final int networkTypeBitmask = in.readInt();
 
@@ -1049,10 +1351,6 @@
                 }
             };
 
-    private static int nullToNotInMapInt(Integer value) {
-        return value == null ? NOT_IN_MAP_INT : value;
-    }
-
     /**
      * Provides a convenient way to set the fields of a {@link ApnSetting} when creating a new
      * instance. The following settings are required to build an {@code ApnSetting}:
@@ -1089,19 +1387,19 @@
     public static class Builder{
         private String mEntryName;
         private String mApnName;
-        private InetAddress mProxyAddress;
-        private int mProxyPort = NO_PORT_SPECIFIED;
+        private String mProxyAddress;
+        private int mProxyPort = UNSPECIFIED_INT;
         private Uri mMmsc;
-        private InetAddress mMmsProxyAddress;
-        private int mMmsProxyPort = NO_PORT_SPECIFIED;
+        private String mMmsProxyAddress;
+        private int mMmsProxyPort = UNSPECIFIED_INT;
         private String mUser;
         private String mPassword;
         private int mAuthType;
         private int mApnTypeBitmask;
         private int mId;
         private String mOperatorNumeric;
-        private int mProtocol = NOT_IN_MAP_INT;
-        private int mRoamingProtocol = NOT_IN_MAP_INT;
+        private int mProtocol = UNSPECIFIED_INT;
+        private int mRoamingProtocol = UNSPECIFIED_INT;
         private int mMtu;
         private int mNetworkTypeBitmask;
         private boolean mCarrierEnabled;
@@ -1110,8 +1408,9 @@
         private int mMaxConns;
         private int mWaitTime;
         private int mMaxConnsTime;
-        private int mMvnoType = NOT_IN_MAP_INT;
+        private int mMvnoType = UNSPECIFIED_INT;
         private String mMvnoMatchData;
+        private int mApnSetId;
 
         /**
          * Default constructor for Builder.
@@ -1206,6 +1505,17 @@
         }
 
         /**
+         * Sets the APN set id for the APN.
+         *
+         * @param apnSetId the set id for the APN
+         * @hide
+         */
+        public Builder setApnSetId(int apnSetId) {
+            this.mApnSetId = apnSetId;
+            return this;
+        }
+
+        /**
          * Sets a human-readable name that describes the APN.
          *
          * @param entryName the entry name to set for the APN
@@ -1237,8 +1547,20 @@
          * hostname and a dummy IP address. See {@link ApnSetting.Builder above} for an example.
          *
          * @param proxy the proxy address to set for the APN
+         * @deprecated use {@link #setProxyAddress(String)} instead.
          */
+        @Deprecated
         public Builder setProxyAddress(InetAddress proxy) {
+            this.mProxyAddress = inetAddressToString(proxy);
+            return this;
+        }
+
+        /**
+         * Sets the proxy address of the APN.
+         *
+         * @param proxy the proxy address to set for the APN
+         */
+        public Builder setProxyAddress(String proxy) {
             this.mProxyAddress = proxy;
             return this;
         }
@@ -1276,8 +1598,20 @@
          * hostname and a dummy IP address. See {@link ApnSetting.Builder above} for an example.
          *
          * @param mmsProxy the MMS proxy address to set for the APN
+         * @deprecated use {@link #setMmsProxyAddress(String)} instead.
          */
+        @Deprecated
         public Builder setMmsProxyAddress(InetAddress mmsProxy) {
+            this.mMmsProxyAddress = inetAddressToString(mmsProxy);
+            return this;
+        }
+
+        /**
+         * Sets the MMS proxy address of the APN.
+         *
+         * @param mmsProxy the MMS proxy address to set for the APN
+         */
+        public Builder setMmsProxyAddress(String mmsProxy) {
             this.mMmsProxyAddress = mmsProxy;
             return this;
         }
@@ -1419,5 +1753,15 @@
             }
             return new ApnSetting(this);
         }
+
+        /**
+         * Builds {@link ApnSetting} from this builder. This function doesn't check if
+         * {@link #setApnName(String)} or {@link #setEntryName(String)}, or
+         * {@link #setApnTypeBitmask(int)} is empty.
+         * @hide
+         */
+        public ApnSetting buildWithoutCheck() {
+            return new ApnSetting(this);
+        }
     }
 }
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 4ca5ce3..1db5850 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -44,11 +44,11 @@
 /**
  * Base class of data service. Services that extend DataService must register the service in
  * their AndroidManifest to be detected by the framework. They must be protected by the permission
- * "android.permission.BIND_DATA_SERVICE". The data service definition in the manifest must follow
- * the following format:
+ * "android.permission.BIND_TELEPHONY_DATA_SERVICE". The data service definition in the manifest
+ * must follow the following format:
  * ...
  * <service android:name=".xxxDataService"
- *     android:permission="android.permission.BIND_DATA_SERVICE" >
+ *     android:permission="android.permission.BIND_TELEPHONY_DATA_SERVICE" >
  *     <intent-filter>
  *         <action android:name="android.telephony.data.DataService" />
  *     </intent-filter>
diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java
index e82c115..e158fa8 100644
--- a/telephony/java/android/telephony/ims/ImsExternalCallState.java
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java
@@ -45,6 +45,7 @@
     private int mCallId;
     // Number
     private Uri mAddress;
+    private Uri mLocalAddress;
     private boolean mIsPullable;
     // CALL_STATE_CONFIRMED / CALL_STATE_TERMINATED
     private int mCallState;
@@ -69,10 +70,24 @@
     }
 
     /** @hide */
+    public ImsExternalCallState(int callId, Uri address, Uri localAddress,
+            boolean isPullable, int callState, int callType, boolean isCallheld) {
+        mCallId = callId;
+        mAddress = address;
+        mLocalAddress = localAddress;
+        mIsPullable = isPullable;
+        mCallState = callState;
+        mCallType = callType;
+        mIsHeld = isCallheld;
+        Rlog.d(TAG, "ImsExternalCallState = " + this);
+    }
+
+    /** @hide */
     public ImsExternalCallState(Parcel in) {
         mCallId = in.readInt();
         ClassLoader classLoader = ImsExternalCallState.class.getClassLoader();
         mAddress = in.readParcelable(classLoader);
+        mLocalAddress = in.readParcelable(classLoader);
         mIsPullable = (in.readInt() != 0);
         mCallState = in.readInt();
         mCallType = in.readInt();
@@ -89,6 +104,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mCallId);
         out.writeParcelable(mAddress, 0);
+        out.writeParcelable(mLocalAddress, 0);
         out.writeInt(mIsPullable ? 1 : 0);
         out.writeInt(mCallState);
         out.writeInt(mCallType);
@@ -117,6 +133,11 @@
         return mAddress;
     }
 
+    /** @hide */
+    public Uri getLocalAddress() {
+        return mLocalAddress;
+    }
+
     public boolean isCallPullable() {
         return mIsPullable;
     }
@@ -137,6 +158,7 @@
     public String toString() {
         return "ImsExternalCallState { mCallId = " + mCallId +
                 ", mAddress = " + Log.pii(mAddress) +
+                ", mLocalAddress = " + Log.pii(mLocalAddress) +
                 ", mIsPullable = " + mIsPullable +
                 ", mCallState = " + mCallState +
                 ", mCallType = " + mCallType +
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index d537699..b77881e 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -341,15 +341,15 @@
         }
     }
 
+    /** @hide */
+    protected Context mContext;
+    /** @hide */
+    protected final Object mLock = new Object();
+
     private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
             new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
     private @ImsState int mState = STATE_UNAVAILABLE;
     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
-    /**
-     * @hide
-     */
-    protected Context mContext;
-    private final Object mLock = new Object();
     private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
             = new RemoteCallbackList<>();
     private Capabilities mCapabilityStatus = new Capabilities();
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index bc790fb..7681aef 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -22,25 +22,25 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.telecom.TelecomManager;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.telephony.ims.stub.ImsCallSessionImplBase;
-import android.telephony.ims.stub.ImsSmsImplBase;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.aidl.IImsMmTelListener;
 import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
 import android.telephony.ims.stub.ImsEcbmImplBase;
 import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
 import android.telephony.ims.stub.ImsUtImplBase;
 import android.util.Log;
 
-import android.telephony.ims.ImsCallProfile;
-import android.telephony.ims.ImsReasonInfo;
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsEcbm;
 import com.android.ims.internal.IImsMultiEndpoint;
 import com.android.ims.internal.IImsUt;
-import android.telephony.ims.ImsCallSession;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
@@ -61,20 +61,16 @@
     private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
 
         @Override
-        public void setListener(IImsMmTelListener l) throws RemoteException {
-            synchronized (mLock) {
-                MmTelFeature.this.setListener(l);
-            }
+        public void setListener(IImsMmTelListener l) {
+            MmTelFeature.this.setListener(l);
         }
 
         @Override
         public int getFeatureState() throws RemoteException {
-            synchronized (mLock) {
-                try {
-                    return MmTelFeature.this.getFeatureState();
-                } catch (Exception e) {
-                    throw new RemoteException(e.getMessage());
-                }
+            try {
+                return MmTelFeature.this.getFeatureState();
+            } catch (Exception e) {
+                throw new RemoteException(e.getMessage());
             }
         }
 
@@ -138,10 +134,8 @@
         }
 
         @Override
-        public int queryCapabilityStatus() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
-            }
+        public int queryCapabilityStatus() {
+            return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
         }
 
         @Override
@@ -158,7 +152,7 @@
 
         @Override
         public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
-                IImsCapabilityCallback c) throws RemoteException {
+                IImsCapabilityCallback c) {
             synchronized (mLock) {
                 MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
             }
@@ -173,10 +167,8 @@
         }
 
         @Override
-        public void setSmsListener(IImsSmsListener l) throws RemoteException {
-            synchronized (mLock) {
-                MmTelFeature.this.setSmsListener(l);
-            }
+        public void setSmsListener(IImsSmsListener l) {
+            MmTelFeature.this.setSmsListener(l);
         }
 
         @Override
@@ -364,9 +356,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProcessCallResult {}
 
-
-    // Lock for feature synchronization
-    private final Object mLock = new Object();
     private IImsMmTelListener mListener;
 
     /**
@@ -376,9 +365,9 @@
     private void setListener(IImsMmTelListener listener) {
         synchronized (mLock) {
             mListener = listener;
-        }
-        if (mListener != null) {
-            onFeatureReady();
+            if (mListener != null) {
+                onFeatureReady();
+            }
         }
     }
 
diff --git a/telephony/java/com/android/ims/internal/uce/common/CapInfo.java b/telephony/java/com/android/ims/internal/uce/common/CapInfo.java
index 56969a8..a9847ba 100644
--- a/telephony/java/com/android/ims/internal/uce/common/CapInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/common/CapInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.common;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -73,6 +74,7 @@
     /**
      * Constructor for the CapInfo class.
      */
+    @UnsupportedAppUsage
     public CapInfo() {
     };
 
@@ -80,6 +82,7 @@
     /**
      * Checks whether IM is supported.
      */
+    @UnsupportedAppUsage
     public boolean isImSupported() {
         return mImSupported;
     }
@@ -87,6 +90,7 @@
     /**
      * Sets IM as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setImSupported(boolean imSupported) {
         this.mImSupported = imSupported;
     }
@@ -94,6 +98,7 @@
     /**
      * Checks whether FT Thumbnail is supported.
      */
+    @UnsupportedAppUsage
     public boolean isFtThumbSupported() {
         return mFtThumbSupported;
     }
@@ -101,6 +106,7 @@
     /**
      * Sets FT thumbnail as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setFtThumbSupported(boolean ftThumbSupported) {
         this.mFtThumbSupported = ftThumbSupported;
     }
@@ -110,6 +116,7 @@
     /**
      * Checks whether FT Store and Forward is supported
      */
+    @UnsupportedAppUsage
     public boolean isFtSnFSupported() {
         return  mFtSnFSupported;
     }
@@ -117,6 +124,7 @@
     /**
      * Sets FT Store and Forward as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setFtSnFSupported(boolean  ftSnFSupported) {
         this.mFtSnFSupported =  ftSnFSupported;
     }
@@ -124,6 +132,7 @@
    /**
     * Checks whether File transfer HTTP is supported.
     */
+   @UnsupportedAppUsage
    public boolean isFtHttpSupported() {
        return  mFtHttpSupported;
    }
@@ -131,6 +140,7 @@
    /**
     * Sets File transfer HTTP as supported or not supported.
     */
+   @UnsupportedAppUsage
    public void setFtHttpSupported(boolean  ftHttpSupported) {
        this.mFtHttpSupported =  ftHttpSupported;
    }
@@ -138,6 +148,7 @@
     /**
      * Checks whether FT is supported.
      */
+    @UnsupportedAppUsage
     public boolean isFtSupported() {
         return mFtSupported;
     }
@@ -145,6 +156,7 @@
     /**
      * Sets FT as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setFtSupported(boolean ftSupported) {
         this.mFtSupported = ftSupported;
     }
@@ -152,6 +164,7 @@
     /**
      * Checks whether IS is supported.
      */
+    @UnsupportedAppUsage
     public boolean isIsSupported() {
         return mIsSupported;
     }
@@ -159,6 +172,7 @@
     /**
      * Sets IS as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setIsSupported(boolean isSupported) {
         this.mIsSupported = isSupported;
     }
@@ -166,6 +180,7 @@
     /**
      * Checks whether video sharing is supported during a CS call.
      */
+    @UnsupportedAppUsage
     public boolean isVsDuringCSSupported() {
         return mVsDuringCSSupported;
     }
@@ -174,6 +189,7 @@
      *  Sets video sharing as supported or not supported during a CS
      *  call.
      */
+    @UnsupportedAppUsage
     public void setVsDuringCSSupported(boolean vsDuringCSSupported) {
         this.mVsDuringCSSupported = vsDuringCSSupported;
     }
@@ -182,6 +198,7 @@
      *  Checks whether video sharing outside a voice call is
      *   supported.
      */
+    @UnsupportedAppUsage
     public boolean isVsSupported() {
         return mVsSupported;
     }
@@ -189,6 +206,7 @@
     /**
      * Sets video sharing as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setVsSupported(boolean vsSupported) {
         this.mVsSupported = vsSupported;
     }
@@ -196,6 +214,7 @@
     /**
      * Checks whether social presence is supported.
      */
+    @UnsupportedAppUsage
     public boolean isSpSupported() {
         return mSpSupported;
     }
@@ -203,6 +222,7 @@
     /**
      * Sets social presence as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setSpSupported(boolean spSupported) {
         this.mSpSupported = spSupported;
     }
@@ -211,6 +231,7 @@
      * Checks whether capability discovery via presence is
      * supported.
      */
+    @UnsupportedAppUsage
     public boolean isCdViaPresenceSupported() {
         return mCdViaPresenceSupported;
     }
@@ -219,6 +240,7 @@
      * Sets capability discovery via presence as supported or not
      * supported.
      */
+    @UnsupportedAppUsage
     public void setCdViaPresenceSupported(boolean cdViaPresenceSupported) {
         this.mCdViaPresenceSupported = cdViaPresenceSupported;
     }
@@ -226,6 +248,7 @@
     /**
      * Checks whether IP voice call is supported.
      */
+    @UnsupportedAppUsage
     public boolean isIpVoiceSupported() {
         return mIpVoiceSupported;
     }
@@ -233,6 +256,7 @@
     /**
      * Sets IP voice call as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setIpVoiceSupported(boolean ipVoiceSupported) {
         this.mIpVoiceSupported = ipVoiceSupported;
     }
@@ -240,6 +264,7 @@
     /**
      * Checks whether IP video call is supported.
      */
+    @UnsupportedAppUsage
     public boolean isIpVideoSupported() {
         return mIpVideoSupported;
     }
@@ -247,6 +272,7 @@
     /**
      * Sets IP video call as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setIpVideoSupported(boolean ipVideoSupported) {
         this.mIpVideoSupported = ipVideoSupported;
     }
@@ -255,6 +281,7 @@
     * Checks whether Geo location Pull using File Transfer is
     * supported.
     */
+   @UnsupportedAppUsage
    public boolean isGeoPullFtSupported() {
        return mGeoPullFtSupported;
    }
@@ -263,6 +290,7 @@
     * Sets Geo location Pull using File Transfer as supported or
     * not supported.
     */
+   @UnsupportedAppUsage
    public void setGeoPullFtSupported(boolean geoPullFtSupported) {
        this.mGeoPullFtSupported = geoPullFtSupported;
    }
@@ -270,6 +298,7 @@
     /**
      * Checks whether Geo Pull is supported.
      */
+    @UnsupportedAppUsage
     public boolean isGeoPullSupported() {
         return mGeoPullSupported;
     }
@@ -277,6 +306,7 @@
     /**
      * Sets Geo Pull as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setGeoPullSupported(boolean geoPullSupported) {
         this.mGeoPullSupported = geoPullSupported;
     }
@@ -284,6 +314,7 @@
     /**
      * Checks whether Geo Push is supported.
      */
+    @UnsupportedAppUsage
     public boolean isGeoPushSupported() {
         return mGeoPushSupported;
     }
@@ -291,6 +322,7 @@
     /**
      * Sets Geo Push as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setGeoPushSupported(boolean geoPushSupported) {
         this.mGeoPushSupported = geoPushSupported;
     }
@@ -298,6 +330,7 @@
     /**
      * Checks whether short messaging is supported.
      */
+    @UnsupportedAppUsage
     public boolean isSmSupported() {
         return mSmSupported;
     }
@@ -305,6 +338,7 @@
     /**
      * Sets short messaging as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setSmSupported(boolean smSupported) {
         this.mSmSupported = smSupported;
     }
@@ -312,18 +346,22 @@
     /**
      * Checks whether store/forward and group chat are supported.
      */
+    @UnsupportedAppUsage
     public boolean isFullSnFGroupChatSupported() {
         return mFullSnFGroupChatSupported;
     }
 
+    @UnsupportedAppUsage
     public boolean isRcsIpVoiceCallSupported() {
         return mRcsIpVoiceCallSupported;
     }
 
+    @UnsupportedAppUsage
     public boolean isRcsIpVideoCallSupported() {
         return mRcsIpVideoCallSupported;
     }
 
+    @UnsupportedAppUsage
     public boolean isRcsIpVideoOnlyCallSupported() {
         return mRcsIpVideoOnlyCallSupported;
     }
@@ -331,16 +369,20 @@
     /**
      * Sets store/forward and group chat supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setFullSnFGroupChatSupported(boolean fullSnFGroupChatSupported) {
         this.mFullSnFGroupChatSupported = fullSnFGroupChatSupported;
     }
 
+    @UnsupportedAppUsage
     public void setRcsIpVoiceCallSupported(boolean rcsIpVoiceCallSupported) {
         this.mRcsIpVoiceCallSupported = rcsIpVoiceCallSupported;
     }
+    @UnsupportedAppUsage
     public void setRcsIpVideoCallSupported(boolean rcsIpVideoCallSupported) {
         this.mRcsIpVideoCallSupported = rcsIpVideoCallSupported;
     }
+    @UnsupportedAppUsage
     public void setRcsIpVideoOnlyCallSupported(boolean rcsIpVideoOnlyCallSupported) {
         this.mRcsIpVideoOnlyCallSupported = rcsIpVideoOnlyCallSupported;
     }
@@ -351,17 +393,20 @@
     }
 
     /** Sets the list of supported extensions. */
+    @UnsupportedAppUsage
     public void setExts(String[] exts) {
         this.mExts = exts;
     }
 
 
     /** Gets the time stamp for when to query again. */
+    @UnsupportedAppUsage
     public long getCapTimestamp() {
         return mCapTimestamp;
     }
 
     /** Sets the time stamp for when to query again. */
+    @UnsupportedAppUsage
     public void setCapTimestamp(long capTimestamp) {
         this.mCapTimestamp = capTimestamp;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/common/StatusCode.java b/telephony/java/com/android/ims/internal/uce/common/StatusCode.java
index ad9b669..3921cfb 100644
--- a/telephony/java/com/android/ims/internal/uce/common/StatusCode.java
+++ b/telephony/java/com/android/ims/internal/uce/common/StatusCode.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.common;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -71,12 +72,14 @@
      * Constructor for the StatusCode class.
      * @hide
      */
+    @UnsupportedAppUsage
     public StatusCode() {}
 
     /**
      *  Gets the status code.
      *  @hide
      */
+    @UnsupportedAppUsage
     public int getStatusCode() {
         return mStatusCode;
     }
@@ -85,6 +88,7 @@
      *  Sets the status code.
      *  @hide
      */
+    @UnsupportedAppUsage
     public void setStatusCode(int nStatusCode) {
         this.mStatusCode = nStatusCode;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/common/UceLong.java b/telephony/java/com/android/ims/internal/uce/common/UceLong.java
index fd07fe8..7207899 100644
--- a/telephony/java/com/android/ims/internal/uce/common/UceLong.java
+++ b/telephony/java/com/android/ims/internal/uce/common/UceLong.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.common;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -32,6 +33,7 @@
      * Constructor for the UceLong class.
      * @hide
      */
+    @UnsupportedAppUsage
     public UceLong() {
     };
 
@@ -39,6 +41,7 @@
      * Gets the long value.
      * @hide
      */
+    @UnsupportedAppUsage
     public long getUceLong() {
         return mUceLong;
     }
@@ -47,6 +50,7 @@
      * Sets the long value.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setUceLong(long uceLong) {
         this.mUceLong = uceLong;
     }
@@ -54,6 +58,7 @@
     /** Get the client ID as integer value.
      *  @hide
      */
+    @UnsupportedAppUsage
     public int getClientId() {
         return mClientId;
     }
@@ -62,6 +67,7 @@
      * Set the client ID as integer value.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setClientId(int nClientId) {
         this.mClientId = nClientId;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java b/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
index c570f49..bcb9f2d 100644
--- a/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
@@ -15,6 +15,7 @@
  */
 package com.android.ims.internal.uce.options;
 
+import android.annotation.UnsupportedAppUsage;
 import com.android.ims.internal.uce.common.CapInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -30,10 +31,12 @@
         return new OptionsCapInfo();
     }
 
+    @UnsupportedAppUsage
     public String getSdp() {
         return mSdp;
     }
 
+    @UnsupportedAppUsage
     public void setSdp(String sdp) {
         this.mSdp = sdp;
     }
@@ -41,16 +44,19 @@
     /**
      * Constructor for the OptionsCapInfo class.
      */
+    @UnsupportedAppUsage
     public OptionsCapInfo() {
         mCapInfo = new CapInfo();
     };
 
+    @UnsupportedAppUsage
     public CapInfo getCapInfo() {
         return mCapInfo;
     }
     /**
      * Sets the CapInfo
      */
+    @UnsupportedAppUsage
     public void setCapInfo(CapInfo capInfo) {
         this.mCapInfo = capInfo;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java b/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java
index 35f769c..14c64ac 100644
--- a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java
+++ b/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java
@@ -17,6 +17,7 @@
 package com.android.ims.internal.uce.options;
 
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -55,6 +56,7 @@
      * Sets the command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCmdId(int nCmdId) {
         this.mCmdId = nCmdId;
     }
@@ -63,6 +65,7 @@
      * Constructor for the OptionsCDCmdId class.
      * @hide
      */
+    @UnsupportedAppUsage
     public OptionsCmdId(){};
 
     /** @hide */
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java b/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
index dab191c..4af3e6e 100644
--- a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
+++ b/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
@@ -19,6 +19,7 @@
 import com.android.ims.internal.uce.common.StatusCode;
 import com.android.ims.internal.uce.common.CapInfo;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -41,6 +42,7 @@
      * Sets the command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCmdId(OptionsCmdId cmdId) {
         this.mCmdId = cmdId;
     }
@@ -56,6 +58,7 @@
     /**
        Sets the user data.
        @hide  */
+    @UnsupportedAppUsage
     public void setUserData(int userData) {
         this.mUserData = userData;
     }
@@ -72,6 +75,7 @@
      * Sets the status code.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setStatus(StatusCode status) {
         this.mStatus = status;
     }
@@ -80,6 +84,7 @@
      * Constructor for the OptionsCmdStatus class.
      * @hide
      */
+    @UnsupportedAppUsage
     public OptionsCmdStatus() {
         mStatus = new StatusCode();
         mCapInfo = new CapInfo();
@@ -96,6 +101,7 @@
      * Sets the CapInfo
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCapInfo(CapInfo capInfo) {
         this.mCapInfo = capInfo;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java b/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
index 0b9dd21..c5f333d 100644
--- a/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
+++ b/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.options;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -41,6 +42,7 @@
      * Sets the Options command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCmdId(OptionsCmdId cmdId) {
         this.mCmdId = cmdId;
     }
@@ -57,6 +59,7 @@
      * Sets the request ID
      * @hide
      */
+    @UnsupportedAppUsage
     public void setRequestId(int requestId) {
         this.mRequestId = requestId;
     }
@@ -73,6 +76,7 @@
      * Sets the SIP response code.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setSipResponseCode(int sipResponseCode) {
         this.mSipResponseCode = sipResponseCode;
     }
@@ -89,6 +93,7 @@
      * Sets the SIP response code reason phrase.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setReasonPhrase(String reasonPhrase) {
         this.mReasonPhrase = reasonPhrase;
     }
@@ -105,6 +110,7 @@
      * Sets the SIP retryAfter sec value
      * @hide
      */
+    @UnsupportedAppUsage
     public void setRetryAfter(int retryAfter) {
         this.mRetryAfter = retryAfter;
     }
@@ -113,6 +119,7 @@
      * Constructor for the OptionsSipResponse class.
      * @hide
      */
+    @UnsupportedAppUsage
     public OptionsSipResponse() {
         mCmdId = new OptionsCmdId();
     };
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java
index 60fc226..745df5b 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java
@@ -18,6 +18,7 @@
 
 import com.android.ims.internal.uce.common.CapInfo;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -26,12 +27,14 @@
 public class PresCapInfo implements Parcelable {
 
     private CapInfo mCapInfo;
+    @UnsupportedAppUsage
     private String mContactUri = "";
 
     /**
      * Gets the UCE capability information.
      * @hide
      */
+    @UnsupportedAppUsage
     public CapInfo getCapInfo() {
         return mCapInfo;
     }
@@ -48,6 +51,7 @@
      *  Gets the contact URI.
      *  @hide
      */
+    @UnsupportedAppUsage
     public String getContactUri() {
         return mContactUri;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java b/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java
index 395f3e8..41020ec 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -57,6 +58,7 @@
      * Sets the command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCmdId(int nCmdId) {
         this.mCmdId = nCmdId;
     }
@@ -66,6 +68,7 @@
     * Constructor for the PresCmdId class.
     * @hide
     */
+    @UnsupportedAppUsage
     public PresCmdId(){};
 
 
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java b/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
index a5b498b..ff8069c 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
@@ -18,6 +18,7 @@
 
 import com.android.ims.internal.uce.common.StatusCode;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -42,6 +43,7 @@
      * Sets the command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCmdId(PresCmdId cmdId) {
         this.mCmdId = cmdId;
     }
@@ -58,6 +60,7 @@
      * Sets the user data.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setUserData(int userData) {
         this.mUserData = userData;
     }
@@ -73,6 +76,7 @@
      * Sets the status code.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setStatus(StatusCode status) {
         this.mStatus = status;
     }
@@ -89,6 +93,7 @@
      * Sets the request ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setRequestId(int requestId) {
         this.mRequestId = requestId;
     }
@@ -97,6 +102,7 @@
      * Constructor for the PresCmdStatus class.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresCmdStatus() {
         mStatus = new StatusCode();
     };
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java b/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
index 3e8531a..87193e3 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -65,6 +66,7 @@
      * Sets the publish trigger type.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setPublishTrigeerType(int nPublishTriggerType) {
         this.mPublishTriggerType = nPublishTriggerType;
     }
@@ -74,6 +76,7 @@
      * Constructor for the PresPublishTriggerType class.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresPublishTriggerType(){};
 
     /** @hide */
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java
index a073a23..237c999 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -38,6 +39,7 @@
      * Sets the Presence service resource instance information.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setInstanceInfo(PresResInstanceInfo instanceInfo) {
         this.mInstanceInfo = instanceInfo;
     }
@@ -54,6 +56,7 @@
      * Sets the resource URI.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setResUri(String resUri) {
         this.mResUri = resUri;
     }
@@ -70,6 +73,7 @@
      * Sets the display name.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setDisplayName(String displayName) {
         this.mDisplayName = displayName;
     }
@@ -79,6 +83,7 @@
     * Constructor for the PresResInstanceInfo class.
     * @hide
     */
+    @UnsupportedAppUsage
     public PresResInfo() {
         mInstanceInfo = new PresResInstanceInfo();
     };
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
index 430cff1..29699ea 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import java.util.Arrays;
@@ -59,6 +60,7 @@
      * Sets the resource instance state.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setResInstanceState(int nResInstanceState) {
         this.mResInstanceState = nResInstanceState;
     }
@@ -75,6 +77,7 @@
      * Sets the resource ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setResId(String resourceId) {
         this.mId = resourceId;
     }
@@ -93,6 +96,7 @@
      * code.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setReason(String reason) {
         this.mReason = reason;
     }
@@ -109,6 +113,7 @@
      * Sets the entity URI.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setPresentityUri(String presentityUri) {
         this.mPresentityUri = presentityUri;
     }
@@ -125,6 +130,7 @@
      * Sets the tuple information.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setTupleInfo(PresTupleInfo[] tupleInfo) {
         this.mTupleInfoArray = new PresTupleInfo[tupleInfo.length];
         this.mTupleInfoArray = tupleInfo;
@@ -135,6 +141,7 @@
     * Constructor for the PresResInstanceInfo class.
     * @hide
     */
+    @UnsupportedAppUsage
     public PresResInstanceInfo(){
 
     };
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
index 987dd77..ab46e4b 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -60,6 +61,7 @@
      * Sets the URI.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setUri(String uri) {
         this.mUri = uri;
     }
@@ -76,6 +78,7 @@
      * Sets the version.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setVersion(int version) {
         this.mVersion = version;
     }
@@ -92,6 +95,7 @@
      * Sets the RLMI state.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setFullState(boolean fullState) {
         this.mFullState = fullState;
     }
@@ -108,6 +112,7 @@
      * Sets the RLMI list name.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setListName(String listName) {
         this.mListName = listName;
     }
@@ -124,6 +129,7 @@
      * Sets the subscription request ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setRequestId(int requestId) {
         this.mRequestId = requestId;
     }
@@ -140,6 +146,7 @@
      * Sets the presence subscription state.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setPresSubscriptionState(PresSubscriptionState presSubscriptionState) {
         this.mPresSubscriptionState = presSubscriptionState;
     }
@@ -156,6 +163,7 @@
      * Sets the presence subscription expiration time.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setSubscriptionExpireTime(int subscriptionExpireTime) {
         this.mSubscriptionExpireTime = subscriptionExpireTime;
     }
@@ -172,6 +180,7 @@
      * Sets the presence subscription terminated reason.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setSubscriptionTerminatedReason(String subscriptionTerminatedReason) {
         this.mSubscriptionTerminatedReason = subscriptionTerminatedReason;
     }
@@ -180,6 +189,7 @@
      * Constructor for the PresTupleInfo class.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresRlmiInfo(){};
 
     /** @hide */
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
index f7b7264..83ba722 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -45,6 +46,7 @@
      * Gets the media type.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getMediaType() {
         return mMediaCap;
     }
@@ -61,6 +63,7 @@
      * Gets the service ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public String getServiceId() {
         return mServiceID;
     }
@@ -76,6 +79,7 @@
      * Gets the service description.
      * @hide
      */
+    @UnsupportedAppUsage
     public String getServiceDesc() {
         return mServiceDesc;
     }
@@ -92,6 +96,7 @@
      * Gets the service version.
      * @hide
      */
+    @UnsupportedAppUsage
     public String getServiceVer() {
         return mServiceVer;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java b/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java
index 456b443..5e42592 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -32,6 +33,7 @@
      * Gets the Presence command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresCmdId getCmdId() {
         return mCmdId;
     }
@@ -40,6 +42,7 @@
      * Sets the Presence command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCmdId(PresCmdId cmdId) {
         this.mCmdId = cmdId;
     }
@@ -48,6 +51,7 @@
      * Gets the request ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getRequestId() {
         return mRequestId;
     }
@@ -56,6 +60,7 @@
      * Sets the request ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setRequestId(int requestId) {
         this.mRequestId = requestId;
     }
@@ -64,6 +69,7 @@
      * Gets the SIP response code.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getSipResponseCode() {
         return mSipResponseCode;
     }
@@ -72,6 +78,7 @@
      * Sets the SIP response code.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setSipResponseCode(int sipResponseCode) {
         this.mSipResponseCode = sipResponseCode;
     }
@@ -82,6 +89,7 @@
      * code.
      * @hide
      */
+    @UnsupportedAppUsage
     public String getReasonPhrase() {
         return mReasonPhrase;
     }
@@ -90,6 +98,7 @@
      * Sets the SIP response code reason phrase.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setReasonPhrase(String reasonPhrase) {
         this.mReasonPhrase = reasonPhrase;
     }
@@ -98,6 +107,7 @@
      * Gets the SIP retryAfter sec value.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getRetryAfter() {
         return mRetryAfter;
     }
@@ -106,6 +116,7 @@
      * Sets the SIP retryAfter sec value
      * @hide
      */
+    @UnsupportedAppUsage
     public void setRetryAfter(int retryAfter) {
         this.mRetryAfter = retryAfter;
     }
@@ -114,6 +125,7 @@
      * Constructor for the PresSipResponse class.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresSipResponse(){};
 
     /** @hide */
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java b/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
index 872bc23..bee928c 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -77,6 +78,7 @@
      * Constructor for the PresSubscriptionState class.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresSubscriptionState() {    };
 
     /**
@@ -92,6 +94,7 @@
      * Sets the Presence subscription state.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setPresSubscriptionState(int nPresSubscriptionState) {
         this.mPresSubscriptionState = nPresSubscriptionState;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
index e1867c5..7a47786 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -39,6 +40,7 @@
      * Sets the feature tag.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setFeatureTag(String featureTag) {
         this.mFeatureTag = featureTag;
     }
@@ -54,6 +56,7 @@
      * Sets the contact URI.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setContactUri(String contactUri) {
         this.mContactUri = contactUri;
     }
@@ -70,6 +73,7 @@
      * Sets the timestamp.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setTimestamp(String timestamp) {
         this.mTimestamp = timestamp;
     }
@@ -78,6 +82,7 @@
      * Constructor for the PresTupleInfo class.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresTupleInfo(){};
 
     /** @hide */
diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
index bcad554..a1bea4d 100644
--- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
@@ -21,7 +21,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
@@ -145,6 +144,18 @@
                         telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
                                 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
 
+                // add hiddenUntilInstalled flag for carrier apps and associated apps
+                packageManager.setSystemAppHiddenUntilInstalled(packageName, true);
+                List<ApplicationInfo> associatedAppList = associatedApps.get(packageName);
+                if (associatedAppList != null) {
+                    for (ApplicationInfo associatedApp : associatedAppList) {
+                        packageManager.setSystemAppHiddenUntilInstalled(
+                                associatedApp.packageName,
+                                true
+                        );
+                    }
+                }
+
                 if (hasPrivileges) {
                     // Only update enabled state for the app on /system. Once it has been
                     // updated we shouldn't touch it.
@@ -152,9 +163,14 @@
                             && (ai.enabledSetting ==
                             PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                             || ai.enabledSetting ==
-                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+                            || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0)) {
                         Slog.i(TAG, "Update state(" + packageName + "): ENABLED for user "
                                 + userId);
+                        packageManager.setSystemAppInstallState(
+                                packageName,
+                                true /*installed*/,
+                                userId);
                         packageManager.setApplicationEnabledSetting(
                                 packageName,
                                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
@@ -164,15 +180,20 @@
                     }
 
                     // Also enable any associated apps for this carrier app.
-                    List<ApplicationInfo> associatedAppList = associatedApps.get(packageName);
                     if (associatedAppList != null) {
                         for (ApplicationInfo associatedApp : associatedAppList) {
                             if (associatedApp.enabledSetting ==
                                     PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                                     || associatedApp.enabledSetting ==
-                                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+                                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+                                    || (associatedApp.flags
+                                    & ApplicationInfo.FLAG_INSTALLED) == 0) {
                                 Slog.i(TAG, "Update associated state(" + associatedApp.packageName
                                         + "): ENABLED for user " + userId);
+                                packageManager.setSystemAppInstallState(
+                                        associatedApp.packageName,
+                                        true /*installed*/,
+                                        userId);
                                 packageManager.setApplicationEnabledSetting(
                                         associatedApp.packageName,
                                         PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
@@ -190,36 +211,33 @@
                     // updated we shouldn't touch it.
                     if (!ai.isUpdatedSystemApp()
                             && ai.enabledSetting ==
-                            PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+                            PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                            && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
                         Slog.i(TAG, "Update state(" + packageName
                                 + "): DISABLED_UNTIL_USED for user " + userId);
-                        packageManager.setApplicationEnabledSetting(
+                        packageManager.setSystemAppInstallState(
                                 packageName,
-                                PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
-                                0,
-                                userId,
-                                callingPackage);
+                                false /*installed*/,
+                                userId);
                     }
 
                     // Also disable any associated apps for this carrier app if this is the first
                     // run. We avoid doing this a second time because it is brittle to rely on the
                     // distinction between "default" and "enabled".
                     if (!hasRunOnce) {
-                        List<ApplicationInfo> associatedAppList = associatedApps.get(packageName);
                         if (associatedAppList != null) {
                             for (ApplicationInfo associatedApp : associatedAppList) {
                                 if (associatedApp.enabledSetting
-                                        == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+                                        == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                                        && (associatedApp.flags
+                                        & ApplicationInfo.FLAG_INSTALLED) != 0) {
                                     Slog.i(TAG,
                                             "Update associated state(" + associatedApp.packageName
                                                     + "): DISABLED_UNTIL_USED for user " + userId);
-                                    packageManager.setApplicationEnabledSetting(
+                                    packageManager.setSystemAppInstallState(
                                             associatedApp.packageName,
-                                            PackageManager
-                                                    .COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
-                                            0,
-                                            userId,
-                                            callingPackage);
+                                            false /*installed*/,
+                                            userId);
                                 }
                             }
                         }
@@ -357,7 +375,8 @@
             String packageName) {
         try {
             ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
-                    PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, userId);
+                    PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                    | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, userId);
             if (ai != null && ai.isSystemApp()) {
                 return ai;
             }
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index d999c13..86cb1b7 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -63,34 +63,25 @@
     public static final int EVENT_RADIO_AVAILABLE = BASE + 1;
     public static final int EVENT_RECORDS_LOADED = BASE + 2;
     public static final int EVENT_TRY_SETUP_DATA = BASE + 3;
-    public static final int EVENT_DATA_STATE_CHANGED = BASE + 4;
-    public static final int EVENT_POLL_PDP = BASE + 5;
     public static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = BASE + 6;
     public static final int EVENT_VOICE_CALL_STARTED = BASE + 7;
     public static final int EVENT_VOICE_CALL_ENDED = BASE + 8;
     public static final int EVENT_DATA_CONNECTION_DETACHED = BASE + 9;
-    public static final int EVENT_LINK_STATE_CHANGED = BASE + 10;
     public static final int EVENT_ROAMING_ON = BASE + 11;
     public static final int EVENT_ROAMING_OFF = BASE + 12;
     public static final int EVENT_ENABLE_NEW_APN = BASE + 13;
-    public static final int EVENT_RESTORE_DEFAULT_APN = BASE + 14;
     public static final int EVENT_DISCONNECT_DONE = BASE + 15;
     public static final int EVENT_DATA_CONNECTION_ATTACHED = BASE + 16;
     public static final int EVENT_DATA_STALL_ALARM = BASE + 17;
     public static final int EVENT_DO_RECOVERY = BASE + 18;
     public static final int EVENT_APN_CHANGED = BASE + 19;
-    public static final int EVENT_CDMA_DATA_DETACHED = BASE + 20;
-    public static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = BASE + 21;
     public static final int EVENT_PS_RESTRICT_ENABLED = BASE + 22;
     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_CDMA_OTA_PROVISION = BASE + 25;
     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_RESET_DONE = BASE + 28;
     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_DEPENDENCY_MET = BASE + 31;
     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;
@@ -112,19 +103,6 @@
 
     /***** Constants *****/
 
-    public static final int APN_INVALID_ID = -1;
-    public static final int APN_DEFAULT_ID = 0;
-    public static final int APN_MMS_ID = 1;
-    public static final int APN_SUPL_ID = 2;
-    public static final int APN_DUN_ID = 3;
-    public static final int APN_HIPRI_ID = 4;
-    public static final int APN_IMS_ID = 5;
-    public static final int APN_FOTA_ID = 6;
-    public static final int APN_CBS_ID = 7;
-    public static final int APN_IA_ID = 8;
-    public static final int APN_EMERGENCY_ID = 9;
-    public static final int APN_NUM_TYPES = 10;
-
     public static final int INVALID = -1;
     public static final int DISABLED = 0;
     public static final int ENABLED = 1;
diff --git a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
index 5115731..d9471ae 100644
--- a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
+++ b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
@@ -23,7 +23,7 @@
  */
 interface ICarrierConfigLoader {
 
-    PersistableBundle getConfigForSubId(int subId);
+    PersistableBundle getConfigForSubId(int subId, String callingPackage);
 
     void notifyConfigChangedForSubId(int subId);
 
diff --git a/telephony/java/com/android/internal/telephony/ISmsImplBase.java b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
new file mode 100644
index 0000000..1cdf44d
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
@@ -0,0 +1,191 @@
+/*
+ * 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.telephony;
+
+import android.app.PendingIntent;
+import android.net.Uri;
+
+import java.util.List;
+
+/**
+ * Base class for ISms that facilitates forward compatibility with new features.
+ */
+public class ISmsImplBase extends ISms.Stub {
+
+    @Override
+    public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPkg) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean updateMessageOnIccEfForSubscriber(int subId, String callingPkg, int messageIndex,
+            int newStatus, byte[] pdu) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean copyMessageToIccEfForSubscriber(int subId, String callingPkg, int status,
+            byte[] pdu, byte[] smsc) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendDataForSubscriber(int subId, String callingPkg, String destAddr,
+            String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
+            PendingIntent deliveryIntent) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendDataForSubscriberWithSelfPermissions(int subId, String callingPkg,
+            String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
+            PendingIntent deliveryIntent) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendTextForSubscriber(int subId, String callingPkg, String destAddr,
+            String scAddr, String text, PendingIntent sentIntent,
+            PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendTextForSubscriberWithSelfPermissions(int subId, String callingPkg,
+            String destAddr, String scAddr, String text, PendingIntent sentIntent,
+            PendingIntent deliveryIntent, boolean persistMessage) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendTextForSubscriberWithOptions(int subId, String callingPkg, String destAddr,
+            String scAddr, String text, PendingIntent sentIntent,
+            PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp,
+            int priority, boolean expectMore, int validityPeriod) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void injectSmsPduForSubscriber(
+            int subId, byte[] pdu, String format, PendingIntent receivedIntent) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendMultipartTextForSubscriber(int subId, String callingPkg,
+            String destinationAddress, String scAddress,
+            List<String> parts, List<PendingIntent> sentIntents,
+            List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendMultipartTextForSubscriberWithOptions(int subId, String callingPkg,
+            String destinationAddress, String scAddress,
+            List<String> parts, List<PendingIntent> sentIntents,
+            List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp,
+            int priority, boolean expectMore, int validityPeriod) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean disableCellBroadcastForSubscriber(int subId, int messageIdentifier,
+            int ranType) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean enableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
+            int endMessageId, int ranType) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean disableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
+            int endMessageId, int ranType) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getPremiumSmsPermission(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getPremiumSmsPermissionForSubscriber(int subId, String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setPremiumSmsPermission(String packageName, int permission) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setPremiumSmsPermissionForSubscriber(int subId, String packageName,
+            int permission) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isImsSmsSupportedForSubscriber(int subId) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isSmsSimPickActivityNeeded(int subId) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getPreferredSmsSubscription() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getImsSmsFormatForSubscriber(int subId) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isSMSPromptEnabled() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendStoredText(int subId, String callingPkg, Uri messageUri, String scAddress,
+            PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendStoredMultipartText(int subId, String callingPkg, Uri messageUri,
+            String scAddress, List<PendingIntent> sentIntents,
+            List<PendingIntent> deliveryIntents) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String createAppSpecificSmsToken(int subId, String callingPkg, PendingIntent intent) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f02f27d..f9c3940 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -390,7 +390,7 @@
     /**
      * Returns the neighboring cell information of the device.
      */
-    List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg);
+    List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg, int targetSdk);
 
      int getCallState();
 
@@ -817,12 +817,6 @@
     IImsConfig getImsConfig(int slotId, int feature);
 
     /**
-     * @return true if the IMS resolver is busy resolving a binding and should not be considered
-     * available, false if the IMS resolver is idle.
-     */
-    boolean isResolvingImsBinding();
-
-    /**
     *  @return true if the ImsService to bind to for the slot id specified was set, false otherwise.
     */
     boolean setImsService(int slotId, boolean isCarrierImsService, String packageName);
@@ -1123,16 +1117,19 @@
     /**
      * Whether the DTMF tone length can be changed.
      *
+     * @param subId The subscription to use.
+     * @param callingPackage The package making the call.
      * @return {@code true} if the DTMF tone length can be changed.
      */
-    boolean canChangeDtmfToneLength();
+    boolean canChangeDtmfToneLength(int subId, String callingPackage);
 
     /**
      * Whether the device is a world phone.
      *
+     * @param callingPackage The package making the call.
      * @return {@code true} if the devices is a world phone.
      */
-    boolean isWorldPhone();
+    boolean isWorldPhone(int subId, String callingPackage);
 
     /**
      * Whether the phone supports TTY mode.
@@ -1198,6 +1195,13 @@
     String getImeiForSlot(int slotIndex, String callingPackage);
 
     /**
+     * Returns the Type Allocation Code from the IMEI for the given slot.
+     *
+     * @param slotIndex - Which slot to retrieve the Type Allocation Code from.
+     */
+    String getTypeAllocationCodeForSlot(int slotIndex);
+
+    /**
      * Returns the MEID for the given slot.
      *
      * @param slotIndex - device slot.
@@ -1208,6 +1212,13 @@
     String getMeidForSlot(int slotIndex, String callingPackage);
 
     /**
+     * Returns the Manufacturer Code from the MEID for the given slot.
+     *
+     * @param slotIndex - Which slot to retrieve the Manufacturer Code from.
+     */
+    String getManufacturerCodeForSlot(int slotIndex);
+
+    /**
      * Returns the device software version.
      *
      * @param slotIndex - device slot.
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 49fbd8f..3a26350 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -16,15 +16,6 @@
 
 package com.android.internal.telephony;
 
-/**
- * TODO: This should probably not be an interface see
- * http://www.javaworld.com/javaworld/javaqa/2001-06/01-qa-0608-constants.html and google with
- * http://www.google.com/search?q=interface+constants&ie=utf-8&oe=utf-8&aq=t&rls=com.ubuntu:en-US:unofficial&client=firefox-a
- *
- * Also they should all probably be static final.
- */
-
-import android.os.SystemProperties;
 import android.telephony.TelephonyManager;
 
 /**
@@ -231,13 +222,6 @@
     int LCE_STOPPED = 0;
     int LCE_ACTIVE = 1;
 
-/*
-cat include/telephony/ril.h | \
-   egrep '^#define' | \
-   sed -re 's/^#define +([^ ]+)* +([^ ]+)/    int \1 = \2;/' \
-   >>java/android/com.android.internal.telephony/gsm/RILConstants.java
-*/
-
     /**
      * No restriction at all including voice/SMS/USSD/SS/AV64
      * and packet data.
@@ -272,6 +256,18 @@
     public static final int DATA_PROFILE_OEM_BASE  = 1000;
     public static final int DATA_PROFILE_INVALID   = 0xFFFFFFFF;
 
+    /**
+     * The request/response/unsol message IDs below match RIL.h through Android O-MR1.
+     *
+     * RIL.h is at hardware/ril/include/telephony.ril.h; RIL support is deprecated and may
+     * be removed in the future.
+     *
+     * Messages defined after O-MR1 have no corresponding definition in RIL.h.
+     * P-and-later messages start at RIL_REQUEST_HAL_NON_RIL_BASE and
+     * RIL_UNSOL_HAL_NON_RIL_BASE.
+     */
+
+    /* Requests begin */
     int RIL_REQUEST_GET_SIM_STATUS = 1;
     int RIL_REQUEST_ENTER_SIM_PIN = 2;
     int RIL_REQUEST_ENTER_SIM_PUK = 3;
@@ -415,15 +411,20 @@
     int RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION = 141;
     int RIL_REQUEST_START_NETWORK_SCAN = 142;
     int RIL_REQUEST_STOP_NETWORK_SCAN = 143;
-    int RIL_REQUEST_GET_SLOT_STATUS = 144;
-    int RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING = 145;
-    int RIL_REQUEST_START_KEEPALIVE = 146;
-    int RIL_REQUEST_STOP_KEEPALIVE = 147;
-    int RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA = 148;
-    int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 149;
+    int RIL_REQUEST_START_KEEPALIVE = 144;
+    int RIL_REQUEST_STOP_KEEPALIVE = 145;
 
+    /* The following requests are not defined in RIL.h */
+    int RIL_REQUEST_HAL_NON_RIL_BASE = 200;
+    int RIL_REQUEST_GET_SLOT_STATUS = 200;
+    int RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING = 201;
+    int RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA = 202;
+    int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 203;
+
+    /* Responses begin */
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
 
+    /* Unsols begin */
     int RIL_UNSOL_RESPONSE_BASE = 1000;
     int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000;
     int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001;
@@ -475,7 +476,10 @@
     int RIL_UNSOL_MODEM_RESTART = 1047;
     int RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION = 1048;
     int RIL_UNSOL_NETWORK_SCAN_RESULT = 1049;
-    int RIL_UNSOL_ICC_SLOT_STATUS = 1050;
-    int RIL_UNSOL_KEEPALIVE_STATUS = 1051;
-    int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1052;
+    int RIL_UNSOL_KEEPALIVE_STATUS = 1050;
+
+    /* The following unsols are not defined in RIL.h */
+    int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
+    int RIL_UNSOL_ICC_SLOT_STATUS = 1100;
+    int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1101;
 }
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index c095438..4790b75 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -105,7 +105,7 @@
     /**
      * PLMN (MCC/MNC) is encoded as per 24.008 10.5.1.3
      * Returns a concatenated string of MCC+MNC, stripping
-     * all invalid character 'f'
+     * all invalid character 'F'
      */
     public static String bcdPlmnToString(byte[] data, int offset) {
         if (offset + 3 > data.length) {
@@ -117,9 +117,9 @@
         trans[2] = (byte) ((data[2 + offset] & 0xF0) | ((data[1 + offset] >> 4) & 0xF));
         String ret = bytesToHexString(trans);
 
-        // For a valid plmn we trim all character 'f'
-        if (ret.contains("f")) {
-            ret = ret.replaceAll("f", "");
+        // For a valid plmn we trim all character 'F'
+        if (ret.contains("F")) {
+            ret = ret.replaceAll("F", "");
         }
         return ret;
     }
diff --git a/test-base/Android.bp b/test-base/Android.bp
index a0e3985..d25b477 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -21,6 +21,7 @@
 // Also contains the com.android.internal.util.Predicate[s] classes.
 java_library {
     name: "android.test.base",
+    installable: true,
 
     srcs: ["src/**/*.java"],
 
@@ -42,6 +43,7 @@
 // Also contains the com.android.internal.util.Predicate[s] classes.
 java_library {
     name: "legacy-test",
+    installable: true,
 
     sdk_version: "current",
     static_libs: ["android.test.base"],
@@ -115,4 +117,5 @@
         },
     },
     sdk_version: "current",
+    compile_dex: true,
 }
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 51fa86b..8d3faae 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -18,6 +18,7 @@
 // ===================================
 java_library {
     name: "android.test.mock",
+    installable: true,
 
     java_version: "1.8",
     srcs: ["src/**/*.java"],
@@ -91,6 +92,7 @@
             enabled: false,
         },
     },
+    compile_dex: true,
 }
 
 java_library_static {
@@ -104,4 +106,5 @@
             enabled: false,
         },
     },
+    compile_dex: true,
 }
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index c2aca6b..89734e3 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -159,7 +159,13 @@
 
     /** @hide */
     @Override
-    public boolean isPermissionReviewModeEnabled() {
+    public boolean arePermissionsIndividuallyControlled() {
+        return false;
+    }
+
+    /** @hide */
+    @Override
+    public boolean isWirelessConsentModeEnabled() {
         return false;
     }
 
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index b50ba3b..2caa6c4 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -18,6 +18,7 @@
 // =====================================
 java_library {
     name: "android.test.runner",
+    installable: true,
 
     // Needs to be consistent with the repackaged version of this make target.
     java_version: "1.8",
@@ -120,4 +121,5 @@
         },
     },
     sdk_version: "current",
+    compile_dex: true,
 }
diff --git a/tests/ActivityViewTest/Android.mk b/tests/ActivityViewTest/Android.mk
new file mode 100644
index 0000000..9c80764
--- /dev/null
+++ b/tests/ActivityViewTest/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := ActivityViewTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/tests/ActivityViewTest/AndroidManifest.xml b/tests/ActivityViewTest/AndroidManifest.xml
new file mode 100644
index 0000000..de54cc9
--- /dev/null
+++ b/tests/ActivityViewTest/AndroidManifest.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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.google.android.test.activityview">
+    <uses-permission android:name="android.permission.INJECT_EVENTS"/>
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"/>
+    <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING"/>
+    <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
+
+    <uses-sdk android:targetSdkVersion="27"/>
+    <application android:label="ActivityViewTest">
+        <activity android:name=".ActivityViewMainActivity"
+                  android:label="AV Main"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".ActivityViewActivity"
+                  android:label="AV"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+        </activity>
+
+        <activity android:name=".ActivityViewResizeActivity"
+                  android:label="AV Resize"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+        </activity>
+
+        <activity android:name=".ActivityViewScrollActivity"
+                  android:label="AV Scroll"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+        </activity>
+
+        <activity android:name=".ActivityViewTestActivity"
+                  android:resizeableActivity="true"
+                  android:theme="@*android:style/Theme.NoTitleBar"
+                  android:exported="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/ActivityViewTest/res/layout/activity_view_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_activity.xml
new file mode 100644
index 0000000..67c01f8
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_activity.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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:background="#cfd8dc">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/activity_launch_button"
+            android:layout_width="200dp"
+            android:layout_height="wrap_content"
+            android:text="Launch test activity" />
+
+        <Button
+            android:id="@+id/activity_pick_launch_button"
+            android:layout_width="200dp"
+            android:layout_height="wrap_content"
+            android:text="Launch from picker" />
+
+    </LinearLayout>
+
+    <ActivityView
+        android:id="@+id/activity_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml
new file mode 100644
index 0000000..ba2e911
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml
@@ -0,0 +1,44 @@
+<?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="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <Button
+        android:id="@+id/activity_view_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Test ActivityView"
+        android:textAllCaps="false"/>
+
+    <Button
+        android:id="@+id/scroll_activity_view_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Test Scroll ActivityView"
+        android:textAllCaps="false"/>
+
+    <Button
+        android:id="@+id/resize_activity_view_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Test Resize ActivityView"
+        android:textAllCaps="false"/>
+
+</LinearLayout>
diff --git a/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml
new file mode 100644
index 0000000..18d86e3
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml
@@ -0,0 +1,51 @@
+<?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="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:background="#cfd8dc">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/activity_launch_button"
+            android:layout_width="100dp"
+            android:layout_height="wrap_content"
+            android:text="Launch" />
+
+        <Button
+            android:id="@+id/activity_resize_button"
+            android:layout_width="100dp"
+            android:layout_height="wrap_content"
+            android:text="Resize" />
+    </LinearLayout>
+
+    <SeekBar
+        android:id="@+id/activity_view_seek_bar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <ActivityView
+        android:id="@+id/activity_view"
+        android:layout_width="match_parent"
+        android:layout_height="600dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml
new file mode 100644
index 0000000..879c2c20
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml
@@ -0,0 +1,57 @@
+<?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="vertical" android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <Button
+        android:id="@+id/activity_launch_button"
+        android:layout_width="100dp"
+        android:layout_height="wrap_content"
+        android:text="Launch" />
+
+    <ScrollView
+        android:id="@+id/activity_view_host_scroll_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:color="#cfd8dc">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical" >
+
+            <View
+                android:layout_width="match_parent"
+                android:layout_height="300dp"
+                android:layout_gravity="center_horizontal"
+                android:background="#eeeeee" />
+
+            <ActivityView
+                android:id="@+id/activity_view"
+                android:layout_width="match_parent"
+                android:layout_height="300dp"
+                android:background="#fce4ec" />
+
+            <View
+                android:layout_width="match_parent"
+                android:layout_height="300dp"
+                android:layout_gravity="center_horizontal"
+                android:background="#eeeeee" />
+        </LinearLayout>
+    </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
new file mode 100644
index 0000000..f7ec562
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
@@ -0,0 +1,70 @@
+<?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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:orientation="vertical"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:background="#ffe0b2">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_centerInParent="true"
+        android:orientation="vertical"
+        android:background="#00000000" >
+        <TextView
+            android:id="@+id/test_activity_title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textColor="@android:color/black"
+            android:background="#00000000"
+            android:gravity="center" />
+        <TextView
+            android:id="@+id/test_activity_touch_state"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textColor="@android:color/black"
+            android:background="#00000000"
+            android:gravity="center" />
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/test_activity_width_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textColor="@android:color/black"
+        android:background="#00000000"
+        android:gravity="center" />
+
+    <TextView
+        android:id="@+id/test_activity_height_text"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentEnd="true"
+        android:textColor="@android:color/black"
+        android:background="#00000000"
+        android:gravity="center" />
+
+    <View
+        android:id="@+id/touch_intercept_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#00000000"
+    />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java
new file mode 100644
index 0000000..1548d6e
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.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.google.android.test.activityview;
+
+import android.app.Activity;
+import android.app.ActivityView;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Button;
+
+public class ActivityViewActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_view_activity);
+
+        final ActivityView activityView = findViewById(R.id.activity_view);
+        final Button launchButton = findViewById(R.id.activity_launch_button);
+        launchButton.setOnClickListener(v -> {
+            final Intent intent = new Intent(this, ActivityViewTestActivity.class);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+            activityView.startActivity(intent);
+        });
+        final Button pickActivityLaunchButton = findViewById(R.id.activity_pick_launch_button);
+        pickActivityLaunchButton.setOnClickListener(v -> {
+            final Intent intent = Intent.makeMainActivity(null);
+            final Intent chooser = Intent.createChooser(intent,
+                    "Pick an app to launch in ActivityView");
+            if (intent.resolveActivity(getPackageManager()) != null) {
+                chooser.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+                activityView.startActivity(chooser);
+            }
+        });
+    }
+}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java
new file mode 100644
index 0000000..66f0c6a
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java
@@ -0,0 +1,39 @@
+/**
+ * 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.test.activityview;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class ActivityViewMainActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_view_main_activity);
+
+        findViewById(R.id.activity_view_button).setOnClickListener(
+                v -> startActivity(new Intent(this, ActivityViewActivity.class)));
+
+        findViewById(R.id.scroll_activity_view_button).setOnClickListener(
+                v -> startActivity(new Intent(this, ActivityViewScrollActivity.class)));
+
+        findViewById(R.id.resize_activity_view_button).setOnClickListener(
+                v -> startActivity(new Intent(this, ActivityViewResizeActivity.class)));
+    }
+}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java
new file mode 100644
index 0000000..8860a77
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.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.google.android.test.activityview;
+
+import android.app.Activity;
+import android.app.ActivityView;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+
+public class ActivityViewResizeActivity extends Activity {
+    private static final int SMALL_SIZE = 600;
+    private static final int LARGE_SIZE = 1200;
+
+    private ActivityView mActivityView;
+
+    private boolean mFlipSize;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_view_resize_activity);
+
+        mActivityView = findViewById(R.id.activity_view);
+
+        final Button launchButton = findViewById(R.id.activity_launch_button);
+        launchButton.setOnClickListener(v -> {
+            final Intent intent = new Intent(this, ActivityViewTestActivity.class);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+            mActivityView.startActivity(intent);
+        });
+        final Button resizeButton = findViewById(R.id.activity_resize_button);
+        if (resizeButton != null) {
+            resizeButton.setOnClickListener(v -> {
+                LinearLayout.LayoutParams params =
+                        (LinearLayout.LayoutParams) mActivityView.getLayoutParams();
+                params.height = mFlipSize ? SMALL_SIZE : LARGE_SIZE;
+                mFlipSize = !mFlipSize;
+                mActivityView.setLayoutParams(params);
+            });
+        }
+        final SeekBar seekBar = findViewById(R.id.activity_view_seek_bar);
+        if (seekBar != null) {
+            seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+                @Override
+                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                    final LinearLayout.LayoutParams params =
+                            (LinearLayout.LayoutParams) mActivityView.getLayoutParams();
+                    params.height = SMALL_SIZE + progress * 10;
+                    mActivityView.setLayoutParams(params);
+                }
+
+                @Override
+                public void onStartTrackingTouch(SeekBar seekBar) {
+                }
+
+                @Override
+                public void onStopTrackingTouch(SeekBar seekBar) {
+                }
+            });
+        }
+    }
+}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java
new file mode 100644
index 0000000..5654366
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java
@@ -0,0 +1,44 @@
+/**
+ * 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.test.activityview;
+
+import android.app.Activity;
+import android.app.ActivityView;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+public class ActivityViewScrollActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_view_scroll_activity);
+
+        final ActivityView activityView = findViewById(R.id.activity_view);
+        final Button launchButton = findViewById(R.id.activity_launch_button);
+        launchButton.setOnClickListener(v -> {
+            final Intent intent = new Intent(this, ActivityViewTestActivity.class);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+            activityView.startActivity(intent);
+        });
+        findViewById(R.id.activity_view_host_scroll_view).setOnScrollChangeListener(
+                (View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)
+                        -> activityView.onLocationChanged());
+    }
+}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
new file mode 100644
index 0000000..0d62786
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
@@ -0,0 +1,115 @@
+/**
+ * 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.test.activityview;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.TextView;
+
+public class ActivityViewTestActivity extends Activity implements View.OnTouchListener {
+
+    private TextView mTextView;
+    private TextView mWidthTextView;
+    private TextView mHeightTextView;
+    private TextView mTouchStateTextView;
+    private View mTouchInterceptView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_view_test_activity);
+
+        mTextView = findViewById(R.id.test_activity_title);
+        mWidthTextView = findViewById(R.id.test_activity_width_text);
+        mHeightTextView = findViewById(R.id.test_activity_height_text);
+        mTouchStateTextView = findViewById(R.id.test_activity_touch_state);
+        mTouchInterceptView = findViewById(R.id.touch_intercept_view);
+        mTouchInterceptView.setOnTouchListener(this);
+        ViewTreeObserver viewTreeObserver = mTouchInterceptView.getViewTreeObserver();
+        if (viewTreeObserver.isAlive()) {
+            viewTreeObserver.addOnGlobalLayoutListener(this::updateDimensionTexts);
+        }
+        updateStateText("CREATED");
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        updateStateText("STARTED");
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        updateStateText("RESUMED");
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        updateStateText("PAUSED");
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        updateStateText("STOPPED");
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateDimensionTexts();
+    }
+
+    private void updateStateText(String state) {
+        mTextView.setText(state);
+    }
+
+    private void updateDimensionTexts() {
+        mWidthTextView.setText("" + mTouchInterceptView.getWidth());
+        mHeightTextView.setText("" + mTouchInterceptView.getHeight());
+    }
+
+    private void updateTouchState(MotionEvent event) {
+        switch (event.getAction()) {
+            case ACTION_DOWN:
+            case ACTION_MOVE:
+                mTouchStateTextView.setText("[" + event.getX() + "," + event.getY() + "]");
+                break;
+            case ACTION_UP:
+            case ACTION_CANCEL:
+                mTouchStateTextView.setText("");
+                break;
+        }
+    }
+
+    @Override
+    public boolean onTouch(View v, MotionEvent event) {
+        updateTouchState(event);
+        return true;
+    }
+}
diff --git a/tests/FlickerTests/Android.mk b/tests/FlickerTests/Android.mk
new file mode 100644
index 0000000..3c70f8b
--- /dev/null
+++ b/tests/FlickerTests/Android.mk
@@ -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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := FlickerTests
+LOCAL_MODULE_TAGS := tests optional
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_CERTIFICATE := platform
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    flickertestapplib \
+    flickerlib \
+    truth-prebuilt \
+    app-helpers-core
+
+include $(BUILD_PACKAGE)
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml
new file mode 100644
index 0000000..ba63940
--- /dev/null
+++ b/tests/FlickerTests/AndroidManifest.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.server.wm.flicker">
+
+    <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27"/>
+    <!-- Read and write traces from external storage -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <!-- Capture screen contents -->
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+    <!-- Run layers trace -->
+    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+    <application>
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.server.wm.flicker"
+                     android:label="WindowManager Flicker Tests">
+    </instrumentation>
+</manifest>
\ No newline at end of file
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
new file mode 100644
index 0000000..b31235b
--- /dev/null
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs WindowManager Flicker Tests">
+    <option name="test-tag" value="FlickerTests" />
+    <target_preparer class="com.google.android.tradefed.targetprep.GoogleDeviceSetup">
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on" />
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="FlickerTests.apk"/>
+        <option name="test-file-name" value="FlickerTestApp.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.server.wm.flicker"/>
+        <option name="shell-timeout" value="6600s" />
+        <option name="test-timeout" value="6000s" />
+        <option name="hidden-api-checks" value="false" />
+    </test>
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/sdcard/flicker" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
+</configuration>
diff --git a/tests/FlickerTests/README.md b/tests/FlickerTests/README.md
new file mode 100644
index 0000000..a7c9e20
--- /dev/null
+++ b/tests/FlickerTests/README.md
@@ -0,0 +1,146 @@
+# Flicker Test Library
+
+## Motivation
+Detect *flicker* &mdash; any discontinuous, or unpredictable behavior seen during UI transitions that is not due to performance. This is often the result of a logic error in the code and difficult to identify because the issue is transient and at times difficult to reproduce. This library helps create integration tests between `SurfaceFlinger`, `WindowManager` and `SystemUI` to identify flicker.
+
+## Adding a Test
+The library builds and runs UI transitions, captures Winscope traces and exposes common assertions that can be tested against each trace.
+
+### Building Transitions
+Start by defining common or error prone transitions using `TransitionRunner`.
+```java
+// Example: Build a transition that cold launches an app from launcher
+TransitionRunner transition = TransitionRunner.newBuilder()
+                // Specify a tag to identify the transition (optional)
+                .withTag("OpenAppCold_" + testApp.getLauncherName())
+
+                // Specify preconditions to setup the device
+                // Wake up device and go to home screen
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+
+                // Setup transition under test
+                // Press the home button and close the app to test a cold start
+                .runBefore(device::pressHome)
+                .runBefore(testApp::exit)
+
+                // Run the transition under test
+                // Open the app and wait for UI to be idle
+                // This is the part of the transition that will be tested.
+                .run(testApp::open)
+                .run(device::waitForIdle)
+
+                // Perform any tear downs
+                // Close the app
+                .runAfterAll(testApp::exit)
+
+                // Number of times to repeat the transition to catch any flaky issues
+                .repeat(5);
+```
+
+
+Run the transition to get a list of `TransitionResult` for each time the transition is repeated.
+```java
+    List<TransitionResult> results = transition.run();
+```
+`TransitionResult` contains paths to test artifacts such as Winscope traces and screen recordings.
+
+
+### Checking Assertions
+Each `TransitionResult` can be tested using an extension of the Google Truth library, `LayersTraceSubject` and `WmTraceSubject`. They try to balance test principles set out by Google Truth (not supporting nested assertions, keeping assertions simple) with providing support for common assertion use cases.
+
+Each trace can be represented as a ordered collection of trace entries, with an associated timestamp. Each trace entry has common assertion checks. The trace subjects expose methods to filter the range of entries and test for changing assertions.
+
+```java
+    TransitionResult result = results.get(0);
+    Rect displayBounds = getDisplayBounds();
+
+    // check all trace entries
+    assertThat(result).coversRegion(displayBounds).forAllEntries();
+
+    // check a range of entries
+    assertThat(result).coversRegion(displayBounds).forRange(startTime, endTime);
+
+    // check first entry
+    assertThat(result).coversRegion(displayBounds).inTheBeginning();
+
+    // check last entry
+    assertThat(result).coversRegion(displayBounds).atTheEnd();
+
+    // check a change in assertions, e.g. wallpaper window is visible,
+    // then wallpaper window becomes and stays invisible
+    assertThat(result)
+                .showsBelowAppWindow("wallpaper")
+                .then()
+                .hidesBelowAppWindow("wallpaper")
+                .forAllEntries();
+```
+
+All assertions return `Result` which contains a `success` flag, `assertionName` string identifier, and `reason` string to provide actionable details to the user. The `reason` string is build along the way with all the details as to why the assertions failed and any hints which might help the user determine the root cause. Failed assertion message will also contain a path to the trace that was tested. Example of a failed test:
+
+```
+    java.lang.AssertionError: Not true that <com.android.server.wm.flicker.LayersTrace@65da4cc>
+    Layers Trace can be found in: /layers_trace_emptyregion.pb
+    Timestamp: 2308008331271
+    Assertion: coversRegion
+    Reason:   Region to test: Rect(0, 0 - 1440, 2880)
+    first empty point: 0, 99
+    visible regions:
+    StatusBar#0Rect(0, 0 - 1440, 98)
+    NavigationBar#0Rect(0, 2712 - 1440, 2880)
+    ScreenDecorOverlay#0Rect(0, 0 - 1440, 91)
+    ...
+        at com.google.common.truth.FailureStrategy.fail(FailureStrategy.java:24)
+        ...
+```
+
+---
+
+## Running Tests
+
+The tests can be run as any other Android JUnit tests. `platform_testing/tests/flicker` uses the library to test common UI transitions. Run `atest FlickerTest` to execute these tests.
+
+---
+
+## Other Topics
+### Monitors
+Monitors capture test artifacts for each transition run. They are started before each iteration of the test transition (after the `runBefore` calls) and stopped after the transition is completed. Each iteration will produce a new test artifact. The following monitors are available:
+
+#### LayersTraceMonitor
+Captures Layers trace. This monitor is started by default. Build a transition with `skipLayersTrace()` to disable this monitor.
+#### WindowManagerTraceMonitor
+Captures Window Manager trace. This monitor is started by default. Build a transition with `skipWindowManagerTrace()` to disable this monitor.
+#### WindowAnimationFrameStatsMonitor
+Captures WindowAnimationFrameStats for the transition. This monitor is started by default and is used to eliminate *janky* runs. If an iteration has skipped frames, as determined by WindowAnimationFrameStats, the results for the iteration is skipped. If the list of results is empty after all iterations are completed, then the test should fail. Build a transition with `includeJankyRuns()` to disable this monitor.
+#### ScreenRecorder
+Captures screen to a video file. This monitor is disabled by default. Build a transition with `recordEachRun()` to capture each transition or build with `recordAllRuns()` to capture every transition including setup and teardown.
+
+---
+
+### Extending Assertions
+
+To add a new assertion, add a function to one of the trace entry classes, `LayersTrace.Entry` or `WindowManagerTrace.Entry`.
+
+```java
+    // Example adds an assertion to the check if layer is hidden by parent.
+    Result isHiddenByParent(String layerName) {
+        // Result should contain a details if assertion fails for any reason
+        // such as if layer is not found or layer is not hidden by parent
+        // or layer has no parent.
+        // ...
+    }
+```
+Then add a function to the trace subject `LayersTraceSubject` or `WmTraceSubject` which will add the assertion for testing. When the assertion is evaluated, the trace will first be filtered then the assertion will be applied to the remaining entries.
+
+```java
+    public LayersTraceSubject isHiddenByParent(String layerName) {
+        mChecker.add(entry -> entry.isHiddenByParent(layerName),
+                "isHiddenByParent(" + layerName + ")");
+        return this;
+    }
+```
+
+To use the new assertion:
+```java
+    // Check if "Chrome" layer is hidden by parent in the first trace entry.
+    assertThat(result).isHiddenByParent("Chrome").inTheBeginning();
+```
\ No newline at end of file
diff --git a/tests/FlickerTests/TEST_MAPPING b/tests/FlickerTests/TEST_MAPPING
new file mode 100644
index 0000000..55a6147
--- /dev/null
+++ b/tests/FlickerTests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "FlickerTests"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/Android.mk b/tests/FlickerTests/lib/Android.mk
new file mode 100644
index 0000000..6a8dfe8
--- /dev/null
+++ b/tests/FlickerTests/lib/Android.mk
@@ -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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := flickerlib
+LOCAL_MODULE_TAGS := tests optional
+# sign this with platform cert, so this test is allowed to call private platform apis
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+   ub-janktesthelper \
+   cts-amwm-util \
+   platformprotosnano \
+   layersprotosnano \
+   truth-prebuilt \
+   sysui-helper \
+   launcher-helper-lib \
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := flickerautomationhelperlib
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := src/com/android/server/wm/flicker/AutomationUtils.java \
+    src/com/android/server/wm/flicker/WindowUtils.java
+LOCAL_STATIC_JAVA_LIBRARIES := sysui-helper \
+    launcher-helper-lib \
+    compatibility-device-util
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java
new file mode 100644
index 0000000..84f9f871
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java
@@ -0,0 +1,134 @@
+/*
+ * 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.flicker;
+
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+
+/**
+ * Collection of functional interfaces and classes representing assertions and their associated
+ * results. Assertions are functions that are applied over a single trace entry and returns a
+ * result which includes a detailed reason if the assertion fails.
+ */
+class Assertions {
+    /**
+     * Checks assertion on a single trace entry.
+     *
+     * @param <T> trace entry type to perform the assertion on.
+     */
+    @FunctionalInterface
+    interface TraceAssertion<T> extends Function<T, Result> {
+        /**
+         * Returns an assertion that represents the logical negation of this assertion.
+         *
+         * @return a assertion that represents the logical negation of this assertion
+         */
+        default TraceAssertion<T> negate() {
+            return (T t) -> apply(t).negate();
+        }
+    }
+
+    /**
+     * Checks assertion on a single layers trace entry.
+     */
+    @FunctionalInterface
+    interface LayersTraceAssertion extends TraceAssertion<LayersTrace.Entry> {
+
+    }
+
+    /**
+     * Utility class to store assertions with an identifier to help generate more useful debug
+     * data when dealing with multiple assertions.
+     */
+    static class NamedAssertion<T> {
+        final TraceAssertion<T> assertion;
+        final String name;
+
+        NamedAssertion(TraceAssertion<T> assertion, String name) {
+            this.assertion = assertion;
+            this.name = name;
+        }
+    }
+
+    /**
+     * Contains the result of an assertion including the reason for failed assertions.
+     */
+    static class Result {
+        static final String NEGATION_PREFIX = "!";
+        final boolean success;
+        final long timestamp;
+        final String assertionName;
+        final String reason;
+
+        Result(boolean success, long timestamp, String assertionName, String reason) {
+            this.success = success;
+            this.timestamp = timestamp;
+            this.assertionName = assertionName;
+            this.reason = reason;
+        }
+
+        Result(boolean success, String reason) {
+            this.success = success;
+            this.reason = reason;
+            this.assertionName = "";
+            this.timestamp = 0;
+        }
+
+        /**
+         * Returns the negated {@code Result} and adds a negation prefix to the assertion name.
+         */
+        Result negate() {
+            String negatedAssertionName;
+            if (this.assertionName.startsWith(NEGATION_PREFIX)) {
+                negatedAssertionName = this.assertionName.substring(NEGATION_PREFIX.length() + 1);
+            } else {
+                negatedAssertionName = NEGATION_PREFIX + this.assertionName;
+            }
+            return new Result(!this.success, this.timestamp, negatedAssertionName, this.reason);
+        }
+
+        boolean passed() {
+            return this.success;
+        }
+
+        boolean failed() {
+            return !this.success;
+        }
+
+        @Override
+        public String toString() {
+            return "Timestamp: " + prettyTimestamp(timestamp)
+                    + "\nAssertion: " + assertionName
+                    + "\nReason:   " + reason;
+        }
+
+        private String prettyTimestamp(long timestamp_ns) {
+            StringBuilder prettyTimestamp = new StringBuilder();
+            TimeUnit[] timeUnits = {TimeUnit.HOURS, TimeUnit.MINUTES, TimeUnit.SECONDS, TimeUnit
+                    .MILLISECONDS};
+            String[] unitSuffixes = {"h", "m", "s", "ms"};
+
+            for (int i = 0; i < timeUnits.length; i++) {
+                long convertedTime = timeUnits[i].convert(timestamp_ns, TimeUnit.NANOSECONDS);
+                timestamp_ns -= TimeUnit.NANOSECONDS.convert(convertedTime, timeUnits[i]);
+                prettyTimestamp.append(convertedTime).append(unitSuffixes[i]);
+            }
+
+            return prettyTimestamp.toString();
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java
new file mode 100644
index 0000000..3c65d3c
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java
@@ -0,0 +1,183 @@
+/*
+ * 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.flicker;
+
+import com.android.server.wm.flicker.Assertions.NamedAssertion;
+import com.android.server.wm.flicker.Assertions.Result;
+import com.android.server.wm.flicker.Assertions.TraceAssertion;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Captures some of the common logic in {@link LayersTraceSubject} and {@link WmTraceSubject}
+ * used to filter trace entries and combine multiple assertions.
+ *
+ * @param <T> trace entry type
+ */
+public class AssertionsChecker<T extends ITraceEntry> {
+    private boolean mFilterEntriesByRange = false;
+    private long mFilterStartTime = 0;
+    private long mFilterEndTime = 0;
+    private AssertionOption mOption = AssertionOption.NONE;
+    private List<NamedAssertion<T>> mAssertions = new LinkedList<>();
+
+    void add(Assertions.TraceAssertion<T> assertion, String name) {
+        mAssertions.add(new NamedAssertion<>(assertion, name));
+    }
+
+    void filterByRange(long startTime, long endTime) {
+        mFilterEntriesByRange = true;
+        mFilterStartTime = startTime;
+        mFilterEndTime = endTime;
+    }
+
+    private void setOption(AssertionOption option) {
+        if (mOption != AssertionOption.NONE && option != mOption) {
+            throw new IllegalArgumentException("Cannot use " + mOption + " option with "
+                    + option + " option.");
+        }
+        mOption = option;
+    }
+
+    public void checkFirstEntry() {
+        setOption(AssertionOption.CHECK_FIRST_ENTRY);
+    }
+
+    public void checkLastEntry() {
+        setOption(AssertionOption.CHECK_LAST_ENTRY);
+    }
+
+    public void checkChangingAssertions() {
+        setOption(AssertionOption.CHECK_CHANGING_ASSERTIONS);
+    }
+
+
+    /**
+     * Filters trace entries then runs assertions returning a list of failures.
+     *
+     * @param entries list of entries to perform assertions on
+     * @return list of failed assertion results
+     */
+    List<Result> test(List<T> entries) {
+        List<T> filteredEntries;
+        List<Result> failures;
+
+        if (mFilterEntriesByRange) {
+            filteredEntries = entries.stream()
+                    .filter(e -> ((e.getTimestamp() >= mFilterStartTime)
+                            && (e.getTimestamp() <= mFilterEndTime)))
+                    .collect(Collectors.toList());
+        } else {
+            filteredEntries = entries;
+        }
+
+        switch (mOption) {
+            case CHECK_CHANGING_ASSERTIONS:
+                return assertChanges(filteredEntries);
+            case CHECK_FIRST_ENTRY:
+                return assertEntry(filteredEntries.get(0));
+            case CHECK_LAST_ENTRY:
+                return assertEntry(filteredEntries.get(filteredEntries.size() - 1));
+        }
+        return assertAll(filteredEntries);
+    }
+
+    /**
+     * Steps through each trace entry checking if provided assertions are true in the order they
+     * are added. Each assertion must be true for at least a single trace entry.
+     *
+     * This can be used to check for asserting a change in property over a trace. Such as visibility
+     * for a window changes from true to false or top-most window changes from A to Bb and back to A
+     * again.
+     */
+    private List<Result> assertChanges(List<T> entries) {
+        List<Result> failures = new ArrayList<>();
+        int entryIndex = 0;
+        int assertionIndex = 0;
+        int lastPassedAssertionIndex = -1;
+
+        if (mAssertions.size() == 0) {
+            return failures;
+        }
+
+        while (assertionIndex < mAssertions.size() && entryIndex < entries.size()) {
+            TraceAssertion<T> currentAssertion = mAssertions.get(assertionIndex).assertion;
+            Result result = currentAssertion.apply(entries.get(entryIndex));
+            if (result.passed()) {
+                lastPassedAssertionIndex = assertionIndex;
+                entryIndex++;
+                continue;
+            }
+
+            if (lastPassedAssertionIndex != assertionIndex) {
+                failures.add(result);
+                break;
+            }
+            assertionIndex++;
+
+            if (assertionIndex == mAssertions.size()) {
+                failures.add(result);
+                break;
+            }
+        }
+
+        if (failures.isEmpty()) {
+            if (assertionIndex != mAssertions.size() - 1) {
+                String reason = "\nAssertion " + mAssertions.get(assertionIndex).name
+                        + " never became false";
+                reason += "\nPassed assertions: " + mAssertions.stream().limit(assertionIndex)
+                        .map(assertion -> assertion.name).collect(Collectors.joining(","));
+                reason += "\nUntested assertions: " + mAssertions.stream().skip(assertionIndex + 1)
+                        .map(assertion -> assertion.name).collect(Collectors.joining(","));
+
+                Result result = new Result(false /* success */, 0 /* timestamp */,
+                        "assertChanges", "Not all assertions passed." + reason);
+                failures.add(result);
+            }
+        }
+        return failures;
+    }
+
+    private List<Result> assertEntry(T entry) {
+        List<Result> failures = new ArrayList<>();
+        for (NamedAssertion<T> assertion : mAssertions) {
+            Result result = assertion.assertion.apply(entry);
+            if (result.failed()) {
+                failures.add(result);
+            }
+        }
+        return failures;
+    }
+
+    private List<Result> assertAll(List<T> entries) {
+        return mAssertions.stream().flatMap(
+                assertion -> entries.stream()
+                        .map(assertion.assertion)
+                        .filter(Result::failed))
+                .collect(Collectors.toList());
+    }
+
+    private enum AssertionOption {
+        NONE,
+        CHECK_CHANGING_ASSERTIONS,
+        CHECK_FIRST_ENTRY,
+        CHECK_LAST_ENTRY,
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java
new file mode 100644
index 0000000..6bac675
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java
@@ -0,0 +1,260 @@
+/*
+ * 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.flicker;
+
+import static android.os.SystemClock.sleep;
+import static android.system.helpers.OverviewHelper.isRecentsInLauncher;
+import static android.view.Surface.ROTATION_0;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.launcherhelper.LauncherStrategyFactory;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.util.Log;
+import android.util.Rational;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+/**
+ * Collection of UI Automation helper functions.
+ */
+public class AutomationUtils {
+    private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+    private static final long FIND_TIMEOUT = 10000;
+    private static final long LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout() * 2L;
+    private static final String TAG = "FLICKER";
+
+    public static void wakeUpAndGoToHomeScreen() {
+        UiDevice device = UiDevice.getInstance(InstrumentationRegistry
+                .getInstrumentation());
+        try {
+            device.wakeUp();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+        device.pressHome();
+    }
+
+    /**
+     * Sets {@link android.app.UiAutomation#waitForIdle(long, long)} global timeout to 0 causing
+     * the {@link android.app.UiAutomation#waitForIdle(long, long)} function to timeout instantly.
+     * This removes some delays when using the UIAutomator library required to create fast UI
+     * transitions.
+     */
+    static void setFastWait() {
+        Configurator.getInstance().setWaitForIdleTimeout(0);
+    }
+
+    /**
+     * Reverts {@link android.app.UiAutomation#waitForIdle(long, long)} to default behavior.
+     */
+    static void setDefaultWait() {
+        Configurator.getInstance().setWaitForIdleTimeout(10000);
+    }
+
+    public static boolean isQuickstepEnabled(UiDevice device) {
+        return device.findObject(By.res(SYSTEMUI_PACKAGE, "recent_apps")) == null;
+    }
+
+    public static void openQuickstep(UiDevice device) {
+        if (isQuickstepEnabled(device)) {
+            int height = device.getDisplayHeight();
+            UiObject2 navBar = device.findObject(By.res(SYSTEMUI_PACKAGE, "navigation_bar_frame"));
+
+            Rect navBarVisibleBounds;
+
+            // TODO(vishnun) investigate why this object cannot be found.
+            if (navBar != null) {
+                navBarVisibleBounds = navBar.getVisibleBounds();
+            } else {
+                Log.e(TAG, "Could not find nav bar, infer location");
+                navBarVisibleBounds = WindowUtils.getNavigationBarPosition(ROTATION_0);
+            }
+
+            // Swipe from nav bar to 2/3rd down the screen.
+            device.swipe(
+                    navBarVisibleBounds.centerX(), navBarVisibleBounds.centerY(),
+                    navBarVisibleBounds.centerX(), height * 2 / 3,
+                    (navBarVisibleBounds.centerY() - height * 2 / 3) / 100); // 100 px/step
+        } else {
+            try {
+                device.pressRecentApps();
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        BySelector RECENTS = By.res(SYSTEMUI_PACKAGE, "recents_view");
+
+        // use a long timeout to wait until recents populated
+        if (device.wait(
+                Until.findObject(isRecentsInLauncher()
+                        ? getLauncherOverviewSelector(device) : RECENTS),
+                10000) == null) {
+            fail("Recents didn't appear");
+        }
+        device.waitForIdle();
+    }
+
+    static void clearRecents(UiDevice device) {
+        if (isQuickstepEnabled(device)) {
+            openQuickstep(device);
+
+            for (int i = 0; i < 5; i++) {
+                device.swipe(device.getDisplayWidth() / 2,
+                        device.getDisplayHeight() / 2, device.getDisplayWidth(),
+                        device.getDisplayHeight() / 2,
+                        5);
+
+                BySelector clearAllSelector = By.res("com.google.android.apps.nexuslauncher",
+                        "clear_all_button");
+                UiObject2 clearAllButton = device.wait(Until.findObject(clearAllSelector), 100);
+                if (clearAllButton != null) {
+                    clearAllButton.click();
+                    return;
+                }
+            }
+        }
+    }
+
+    private static BySelector getLauncherOverviewSelector(UiDevice device) {
+        return By.res(device.getLauncherPackageName(), "overview_panel");
+    }
+
+    private static void longPressRecents(UiDevice device) {
+        BySelector recentsSelector = By.res(SYSTEMUI_PACKAGE, "recent_apps");
+        UiObject2 recentsButton = device.wait(Until.findObject(recentsSelector), FIND_TIMEOUT);
+        assertNotNull("Unable to find recents button", recentsButton);
+        recentsButton.click(LONG_PRESS_TIMEOUT);
+    }
+
+    public static void launchSplitScreen(UiDevice device) {
+        String mLauncherPackage = LauncherStrategyFactory.getInstance(device)
+                .getLauncherStrategy().getSupportedLauncherPackage();
+
+        if (isQuickstepEnabled(device)) {
+            // Quickstep enabled
+            openQuickstep(device);
+
+            BySelector overviewIconSelector = By.res(mLauncherPackage, "icon")
+                    .clazz(View.class);
+            UiObject2 overviewIcon = device.wait(Until.findObject(overviewIconSelector),
+                    FIND_TIMEOUT);
+            assertNotNull("Unable to find app icon in Overview", overviewIcon);
+            overviewIcon.click();
+
+            BySelector splitscreenButtonSelector = By.text("Split screen");
+            UiObject2 splitscreenButton = device.wait(Until.findObject(splitscreenButtonSelector),
+                    FIND_TIMEOUT);
+            assertNotNull("Unable to find Split screen button in Overview", overviewIcon);
+            splitscreenButton.click();
+        } else {
+            // Classic long press recents
+            longPressRecents(device);
+        }
+        // Wait for animation to complete.
+        sleep(2000);
+    }
+
+    public static void exitSplitScreen(UiDevice device) {
+        if (isQuickstepEnabled(device)) {
+            // Quickstep enabled
+            BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle");
+            UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
+            assertNotNull("Unable to find Split screen divider", divider);
+
+            // Drag the split screen divider to the top of the screen
+            divider.drag(new Point(device.getDisplayWidth() / 2, 0), 400);
+        } else {
+            // Classic long press recents
+            longPressRecents(device);
+        }
+        // Wait for animation to complete.
+        sleep(2000);
+    }
+
+    static void resizeSplitScreen(UiDevice device, Rational windowHeightRatio) {
+        BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle");
+        UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
+        assertNotNull("Unable to find Split screen divider", divider);
+        int destHeight =
+                (int) (WindowUtils.getDisplayBounds().height() * windowHeightRatio.floatValue());
+        // Drag the split screen divider to so that the ratio of top window height and bottom
+        // window height is windowHeightRatio
+        device.drag(divider.getVisibleBounds().centerX(), divider.getVisibleBounds().centerY(),
+                device.getDisplayWidth() / 2, destHeight, 10);
+        //divider.drag(new Point(device.getDisplayWidth() / 2, destHeight), 400)
+        divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
+
+        // Wait for animation to complete.
+        sleep(2000);
+    }
+
+    static void closePipWindow(UiDevice device) {
+        UiObject2 pipWindow = device.findObject(
+                By.res(SYSTEMUI_PACKAGE, "background"));
+        pipWindow.click();
+        UiObject2 exitPipObject = device.findObject(
+                By.res(SYSTEMUI_PACKAGE, "dismiss"));
+        exitPipObject.click();
+        // Wait for animation to complete.
+        sleep(2000);
+    }
+
+    static void expandPipWindow(UiDevice device) {
+        UiObject2 pipWindow = device.findObject(
+                By.res(SYSTEMUI_PACKAGE, "background"));
+        pipWindow.click();
+        pipWindow.click();
+    }
+
+    public static void stopPackage(Context context, String packageName) {
+        runShellCommand("am force-stop " + packageName);
+        int packageUid;
+        try {
+            packageUid = context.getPackageManager().getPackageUid(packageName, /* flags= */0);
+        } catch (PackageManager.NameNotFoundException e) {
+            return;
+        }
+        while (targetPackageIsRunning(packageUid)) {
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                //ignore
+            }
+        }
+    }
+
+    private static boolean targetPackageIsRunning(int uid) {
+        final String result = runShellCommand(
+                String.format("cmd activity get-uid-state %d", uid));
+        return !result.contains("(NONEXISTENT)");
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java
new file mode 100644
index 0000000..9525f41
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java
@@ -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.
+ */
+
+package com.android.server.wm.flicker;
+
+/**
+ * Common interface for Layer and WindowManager trace entries.
+ */
+interface ITraceEntry {
+    /**
+     * @return timestamp of current entry
+     */
+    long getTimestamp();
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java
new file mode 100644
index 0000000..660ec0f
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java
@@ -0,0 +1,412 @@
+/*
+ * 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.flicker;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.surfaceflinger.nano.Layers.LayerProto;
+import android.surfaceflinger.nano.Layers.RectProto;
+import android.surfaceflinger.nano.Layers.RegionProto;
+import android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto;
+import android.surfaceflinger.nano.Layerstrace.LayersTraceProto;
+import android.util.SparseArray;
+
+import com.android.server.wm.flicker.Assertions.Result;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Contains a collection of parsed Layers trace entries and assertions to apply over
+ * a single entry.
+ *
+ * Each entry is parsed into a list of {@link LayersTrace.Entry} objects.
+ */
+public class LayersTrace {
+    final private List<Entry> mEntries;
+    @Nullable
+    final private Path mSource;
+
+    private LayersTrace(List<Entry> entries, Path source) {
+        this.mEntries = entries;
+        this.mSource = source;
+    }
+
+    /**
+     * Parses {@code LayersTraceFileProto} from {@code data} and uses the proto to generates a list
+     * of trace entries, storing the flattened layers into its hierarchical structure.
+     *
+     * @param data   binary proto data
+     * @param source Path to source of data for additional debug information
+     */
+    static LayersTrace parseFrom(byte[] data, Path source) {
+        List<Entry> entries = new ArrayList<>();
+        LayersTraceFileProto fileProto;
+        try {
+            fileProto = LayersTraceFileProto.parseFrom(data);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        for (LayersTraceProto traceProto : fileProto.entry) {
+            Entry entry = Entry.fromFlattenedLayers(traceProto.elapsedRealtimeNanos,
+                    traceProto.layers.layers);
+            entries.add(entry);
+        }
+        return new LayersTrace(entries, source);
+    }
+
+    /**
+     * Parses {@code LayersTraceFileProto} from {@code data} and uses the proto to generates a list
+     * of trace entries, storing the flattened layers into its hierarchical structure.
+     *
+     * @param data binary proto data
+     */
+    static LayersTrace parseFrom(byte[] data) {
+        return parseFrom(data, null);
+    }
+
+    List<Entry> getEntries() {
+        return mEntries;
+    }
+
+    Entry getEntry(long timestamp) {
+        Optional<Entry> entry = mEntries.stream()
+                .filter(e -> e.getTimestamp() == timestamp)
+                .findFirst();
+        if (!entry.isPresent()) {
+            throw new RuntimeException("Entry does not exist for timestamp " + timestamp);
+        }
+        return entry.get();
+    }
+
+    Optional<Path> getSource() {
+        return Optional.ofNullable(mSource);
+    }
+
+    /**
+     * Represents a single Layer trace entry.
+     */
+    static class Entry implements ITraceEntry {
+        private long mTimestamp;
+        private List<Layer> mRootLayers; // hierarchical representation of layers
+        private List<Layer> mFlattenedLayers = null;
+
+        private Entry(long timestamp, List<Layer> rootLayers) {
+            this.mTimestamp = timestamp;
+            this.mRootLayers = rootLayers;
+        }
+
+        /**
+         * Constructs the layer hierarchy from a flattened list of layers.
+         */
+        static Entry fromFlattenedLayers(long timestamp, LayerProto[] protos) {
+            SparseArray<Layer> layerMap = new SparseArray<>();
+            ArrayList<Layer> orphans = new ArrayList<>();
+            for (LayerProto proto : protos) {
+                int id = proto.id;
+                int parentId = proto.parent;
+
+                Layer newLayer = layerMap.get(id);
+                if (newLayer == null) {
+                    newLayer = new Layer(proto);
+                    layerMap.append(id, newLayer);
+                } else if (newLayer.mProto != null) {
+                    throw new RuntimeException("Duplicate layer id found:" + id);
+                } else {
+                    newLayer.mProto = proto;
+                    orphans.remove(newLayer);
+                }
+
+                // add parent placeholder
+                if (layerMap.get(parentId) == null) {
+                    Layer orphanLayer = new Layer(null);
+                    layerMap.append(parentId, orphanLayer);
+                    orphans.add(orphanLayer);
+                }
+                layerMap.get(parentId).addChild(newLayer);
+                newLayer.addParent(layerMap.get(parentId));
+            }
+
+            // Fail if we find orphan layers.
+            orphans.remove(layerMap.get(-1));
+            orphans.forEach(orphan -> {
+                String childNodes = orphan.mChildren.stream().map(node ->
+                        Integer.toString(node.getId())).collect(Collectors.joining(", "));
+                int orphanId = orphan.mChildren.get(0).mProto.parent;
+                throw new RuntimeException(
+                        "Failed to parse layers trace. Found orphan layers with parent "
+                                + "layer id:" + orphanId + " : " + childNodes);
+            });
+
+            return new Entry(timestamp, layerMap.get(-1).mChildren);
+        }
+
+        /**
+         * Extracts {@link Rect} from {@link RectProto}.
+         */
+        private static Rect extract(RectProto proto) {
+            return new Rect(proto.left, proto.top, proto.right, proto.bottom);
+        }
+
+        /**
+         * Extracts {@link Rect} from {@link RegionProto} by returning a rect that encompasses all
+         * the rects making up the region.
+         */
+        private static Rect extract(RegionProto regionProto) {
+            Rect region = new Rect();
+            for (RectProto proto : regionProto.rect) {
+                region.union(proto.left, proto.top, proto.right, proto.bottom);
+            }
+            return region;
+        }
+
+        /**
+         * Checks if a region specified by {@code testRect} is covered by all visible layers.
+         */
+        Result coversRegion(Rect testRect) {
+            String assertionName = "coversRegion";
+            Collection<Layer> layers = asFlattenedLayers();
+
+            for (int x = testRect.left; x < testRect.right; x++) {
+                for (int y = testRect.top; y < testRect.bottom; y++) {
+                    boolean emptyRegionFound = true;
+                    for (Layer layer : layers) {
+                        if (layer.isInvisible() || layer.isHiddenByParent()) {
+                            continue;
+                        }
+                        for (RectProto rectProto : layer.mProto.visibleRegion.rect) {
+                            Rect r = extract(rectProto);
+                            if (r.contains(x, y)) {
+                                y = r.bottom;
+                                emptyRegionFound = false;
+                            }
+                        }
+                    }
+                    if (emptyRegionFound) {
+                        String reason = "Region to test: " + testRect
+                                + "\nfirst empty point: " + x + ", " + y;
+                        reason += "\nvisible regions:";
+                        for (Layer layer : layers) {
+                            if (layer.isInvisible() || layer.isHiddenByParent()) {
+                                continue;
+                            }
+                            Rect r = extract(layer.mProto.visibleRegion);
+                            reason += "\n" + layer.mProto.name + r.toString();
+                        }
+                        return new Result(false /* success */, this.mTimestamp, assertionName,
+                                reason);
+                    }
+                }
+            }
+            String info = "Region covered: " + testRect;
+            return new Result(true /* success */, this.mTimestamp, assertionName, info);
+        }
+
+        /**
+         * Checks if a layer with name {@code layerName} has a visible region
+         * {@code expectedVisibleRegion}.
+         */
+        Result hasVisibleRegion(String layerName, Rect expectedVisibleRegion) {
+            String assertionName = "hasVisibleRegion";
+            String reason = "Could not find " + layerName;
+            for (Layer layer : asFlattenedLayers()) {
+                if (layer.mProto.name.contains(layerName)) {
+                    if (layer.isHiddenByParent()) {
+                        reason = layer.getHiddenByParentReason();
+                        continue;
+                    }
+                    if (layer.isInvisible()) {
+                        reason = layer.getVisibilityReason();
+                        continue;
+                    }
+                    Rect visibleRegion = extract(layer.mProto.visibleRegion);
+                    if (visibleRegion.equals(expectedVisibleRegion)) {
+                        return new Result(true /* success */, this.mTimestamp, assertionName,
+                                layer.mProto.name + "has visible region " + expectedVisibleRegion);
+                    }
+                    reason = layer.mProto.name + " has visible region:" + visibleRegion + " "
+                            + "expected:" + expectedVisibleRegion;
+                }
+            }
+            return new Result(false /* success */, this.mTimestamp, assertionName, reason);
+        }
+
+        /**
+         * Checks if a layer with name {@code layerName} is visible.
+         */
+        Result isVisible(String layerName) {
+            String assertionName = "isVisible";
+            String reason = "Could not find " + layerName;
+            for (Layer layer : asFlattenedLayers()) {
+                if (layer.mProto.name.contains(layerName)) {
+                    if (layer.isHiddenByParent()) {
+                        reason = layer.getHiddenByParentReason();
+                        continue;
+                    }
+                    if (layer.isInvisible()) {
+                        reason = layer.getVisibilityReason();
+                        continue;
+                    }
+                    return new Result(true /* success */, this.mTimestamp, assertionName,
+                            layer.mProto.name + " is visible");
+                }
+            }
+            return new Result(false /* success */, this.mTimestamp, assertionName, reason);
+        }
+
+        @Override
+        public long getTimestamp() {
+            return mTimestamp;
+        }
+
+        List<Layer> getRootLayers() {
+            return mRootLayers;
+        }
+
+        List<Layer> asFlattenedLayers() {
+            if (mFlattenedLayers == null) {
+                mFlattenedLayers = new ArrayList<>();
+                ArrayList<Layer> pendingLayers = new ArrayList<>(this.mRootLayers);
+                while (!pendingLayers.isEmpty()) {
+                    Layer layer = pendingLayers.remove(0);
+                    mFlattenedLayers.add(layer);
+                    pendingLayers.addAll(layer.mChildren);
+                }
+            }
+            return mFlattenedLayers;
+        }
+
+        Rect getVisibleBounds(String layerName) {
+            List<Layer> layers = asFlattenedLayers();
+            for (Layer layer : layers) {
+                if (layer.mProto.name.contains(layerName) && layer.isVisible()) {
+                    return extract(layer.mProto.visibleRegion);
+                }
+            }
+            return new Rect(0, 0, 0, 0);
+        }
+    }
+
+    /**
+     * Represents a single layer with links to its parent and child layers.
+     */
+    static class Layer {
+        @Nullable
+        LayerProto mProto;
+        List<Layer> mChildren;
+        @Nullable
+        Layer mParent = null;
+
+        private Layer(LayerProto proto) {
+            this.mProto = proto;
+            this.mChildren = new ArrayList<>();
+        }
+
+        private void addChild(Layer childLayer) {
+            this.mChildren.add(childLayer);
+        }
+
+        private void addParent(Layer parentLayer) {
+            this.mParent = parentLayer;
+        }
+
+        int getId() {
+            return mProto.id;
+        }
+
+        boolean isActiveBufferEmpty() {
+            return this.mProto.activeBuffer == null || this.mProto.activeBuffer.height == 0
+                    || this.mProto.activeBuffer.width == 0;
+        }
+
+        boolean isVisibleRegionEmpty() {
+            if (this.mProto.visibleRegion == null) {
+                return true;
+            }
+            Rect visibleRect = Entry.extract(this.mProto.visibleRegion);
+            return visibleRect.height() == 0 || visibleRect.width() == 0;
+        }
+
+        boolean isHidden() {
+            return (this.mProto.flags & /* FLAG_HIDDEN */ 0x1) != 0x0;
+        }
+
+        boolean isVisible() {
+            return (!isActiveBufferEmpty() || isColorLayer()) &&
+                    !isHidden() && this.mProto.color.a > 0 && !isVisibleRegionEmpty();
+        }
+
+        boolean isColorLayer() {
+            return this.mProto.type.equals("ColorLayer");
+        }
+
+        boolean isRootLayer() {
+            return mParent == null || mParent.mProto == null;
+        }
+
+        boolean isInvisible() {
+            return !isVisible();
+        }
+
+        boolean isHiddenByParent() {
+            return !isRootLayer() && (mParent.isHidden() || mParent.isHiddenByParent());
+        }
+
+        String getHiddenByParentReason() {
+            String reason = "Layer " + mProto.name;
+            if (isHiddenByParent()) {
+                reason += " is hidden by parent: " + mParent.mProto.name;
+            } else {
+                reason += " is not hidden by parent: " + mParent.mProto.name;
+            }
+            return reason;
+        }
+
+        String getVisibilityReason() {
+            String reason = "Layer " + mProto.name;
+            if (isVisible()) {
+                reason += " is visible:";
+            } else {
+                reason += " is invisible:";
+                if (this.mProto.activeBuffer == null) {
+                    reason += " activeBuffer=null";
+                } else if (this.mProto.activeBuffer.height == 0) {
+                    reason += " activeBuffer.height=0";
+                } else if (this.mProto.activeBuffer.width == 0) {
+                    reason += " activeBuffer.width=0";
+                }
+                if (!isColorLayer()) {
+                    reason += " type != ColorLayer";
+                }
+                if (isHidden()) {
+                    reason += " flags=" + this.mProto.flags + " (FLAG_HIDDEN set)";
+                }
+                if (this.mProto.color.a == 0) {
+                    reason += " color.a=0";
+                }
+                if (isVisibleRegionEmpty()) {
+                    reason += " visible region is empty";
+                }
+            }
+            return reason;
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java
new file mode 100644
index 0000000..b4c97e4
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.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.wm.flicker;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+
+import com.android.server.wm.flicker.Assertions.Result;
+import com.android.server.wm.flicker.LayersTrace.Entry;
+import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
+
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Truth subject for {@link LayersTrace} objects.
+ */
+public class LayersTraceSubject extends Subject<LayersTraceSubject, LayersTrace> {
+    // Boiler-plate Subject.Factory for LayersTraceSubject
+    private static final SubjectFactory<LayersTraceSubject, LayersTrace> FACTORY =
+            new SubjectFactory<LayersTraceSubject, LayersTrace>() {
+                @Override
+                public LayersTraceSubject getSubject(
+                        FailureStrategy fs, @Nullable LayersTrace target) {
+                    return new LayersTraceSubject(fs, target);
+                }
+            };
+
+    private AssertionsChecker<Entry> mChecker = new AssertionsChecker<>();
+
+    private LayersTraceSubject(FailureStrategy fs, @Nullable LayersTrace subject) {
+        super(fs, subject);
+    }
+
+    // User-defined entry point
+    public static LayersTraceSubject assertThat(@Nullable LayersTrace entry) {
+        return assertAbout(FACTORY).that(entry);
+    }
+
+    // User-defined entry point
+    public static LayersTraceSubject assertThat(@Nullable TransitionResult result) {
+        LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
+                result.getLayersTracePath());
+        return assertWithMessage(result.toString()).about(FACTORY).that(entries);
+    }
+
+    // Static method for getting the subject factory (for use with assertAbout())
+    public static SubjectFactory<LayersTraceSubject, LayersTrace> entries() {
+        return FACTORY;
+    }
+
+    public void forAllEntries() {
+        test();
+    }
+
+    public void forRange(long startTime, long endTime) {
+        mChecker.filterByRange(startTime, endTime);
+        test();
+    }
+
+    public LayersTraceSubject then() {
+        mChecker.checkChangingAssertions();
+        return this;
+    }
+
+    public void inTheBeginning() {
+        if (getSubject().getEntries().isEmpty()) {
+            fail("No entries found.");
+        }
+        mChecker.checkFirstEntry();
+        test();
+    }
+
+    public void atTheEnd() {
+        if (getSubject().getEntries().isEmpty()) {
+            fail("No entries found.");
+        }
+        mChecker.checkLastEntry();
+        test();
+    }
+
+    private void test() {
+        List<Result> failures = mChecker.test(getSubject().getEntries());
+        if (!failures.isEmpty()) {
+            String failureLogs = failures.stream().map(Result::toString)
+                    .collect(Collectors.joining("\n"));
+            String tracePath = "";
+            if (getSubject().getSource().isPresent()) {
+                tracePath = "\nLayers Trace can be found in: "
+                        + getSubject().getSource().get().toAbsolutePath() + "\n";
+            }
+            fail(tracePath + failureLogs);
+        }
+    }
+
+    public LayersTraceSubject coversRegion(Rect rect) {
+        mChecker.add(entry -> entry.coversRegion(rect),
+                "coversRegion(" + rect + ")");
+        return this;
+    }
+
+    public LayersTraceSubject hasVisibleRegion(String layerName, Rect size) {
+        mChecker.add(entry -> entry.hasVisibleRegion(layerName, size),
+                "hasVisibleRegion(" + layerName + size + ")");
+        return this;
+    }
+
+    public LayersTraceSubject showsLayer(String layerName) {
+        mChecker.add(entry -> entry.isVisible(layerName),
+                "showsLayer(" + layerName + ")");
+        return this;
+    }
+
+    public LayersTraceSubject hidesLayer(String layerName) {
+        mChecker.add(entry -> entry.isVisible(layerName).negate(),
+                "hidesLayer(" + layerName + ")");
+        return this;
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
new file mode 100644
index 0000000..f6e8192
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
@@ -0,0 +1,423 @@
+/*
+ * 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.flicker;
+
+import android.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import com.android.server.wm.flicker.monitor.ITransitionMonitor;
+import com.android.server.wm.flicker.monitor.LayersTraceMonitor;
+import com.android.server.wm.flicker.monitor.ScreenRecorder;
+import com.android.server.wm.flicker.monitor.WindowAnimationFrameStatsMonitor;
+import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor;
+
+import com.google.common.io.Files;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Builds and runs UI transitions capturing test artifacts.
+ *
+ * User can compose a transition from simpler steps, specifying setup and teardown steps. During
+ * a transition, Layers trace, WindowManager trace, screen recordings and window animation frame
+ * stats can be captured.
+ *
+ * <pre>
+ * Transition builder options:
+ *  {@link TransitionBuilder#run(Runnable)} run transition under test. Monitors will be started
+ *  before the transition and stopped after the transition is completed.
+ *  {@link TransitionBuilder#repeat(int)} repeat transitions under test multiple times recording
+ *  result for each run.
+ *  {@link TransitionBuilder#withTag(String)} specify a string identifier used to prefix logs and
+ *  artifacts generated.
+ *  {@link TransitionBuilder#runBeforeAll(Runnable)} run setup transitions once before all other
+ *  transition are run to set up an initial state on device.
+ *  {@link TransitionBuilder#runBefore(Runnable)} run setup transitions before each test transition
+ *  run.
+ *  {@link TransitionBuilder#runAfter(Runnable)} run teardown transitions after each test
+ *  transition.
+ *  {@link TransitionBuilder#runAfter(Runnable)} run teardown transitions once after all
+ *  other transition  are run.
+ *  {@link TransitionBuilder#includeJankyRuns()} disables {@link WindowAnimationFrameStatsMonitor}
+ *  to monitor janky frames. If janky frames are detected, then the test run is skipped. This
+ *  monitor is enabled by default.
+ *  {@link TransitionBuilder#skipLayersTrace()} disables {@link LayersTraceMonitor} used to
+ *  capture Layers trace during a transition. This monitor is enabled by default.
+ *  {@link TransitionBuilder#skipWindowManagerTrace()} disables {@link WindowManagerTraceMonitor}
+ *  used to capture WindowManager trace during a transition. This monitor is enabled by
+ *  default.
+ *  {@link TransitionBuilder#recordAllRuns()} records the screen contents and saves it to a file.
+ *  All the runs including setup and teardown transitions are included in the recording. This
+ *  monitor is used for debugging purposes.
+ *  {@link TransitionBuilder#recordEachRun()} records the screen contents during test transitions
+ *  and saves it to a file for each run. This monitor is used for debugging purposes.
+ *
+ * Example transition to capture WindowManager and Layers trace when opening a test app:
+ * {@code
+ * TransitionRunner.newBuilder()
+ *      .withTag("OpenTestAppFast")
+ *      .runBeforeAll(UiAutomationLib::wakeUp)
+ *      .runBeforeAll(UiAutomationLib::UnlockDevice)
+ *      .runBeforeAll(UiAutomationLib::openTestApp)
+ *      .runBefore(UiAutomationLib::closeTestApp)
+ *      .run(UiAutomationLib::openTestApp)
+ *      .runAfterAll(UiAutomationLib::closeTestApp)
+ *      .repeat(5)
+ *      .build()
+ *      .run();
+ * }
+ * </pre>
+ */
+class TransitionRunner {
+    private static final String TAG = "FLICKER";
+    private final ScreenRecorder mScreenRecorder;
+    private final WindowManagerTraceMonitor mWmTraceMonitor;
+    private final LayersTraceMonitor mLayersTraceMonitor;
+    private final WindowAnimationFrameStatsMonitor mFrameStatsMonitor;
+
+    private final List<ITransitionMonitor> mAllRunsMonitors;
+    private final List<ITransitionMonitor> mPerRunMonitors;
+    private final List<Runnable> mBeforeAlls;
+    private final List<Runnable> mBefores;
+    private final List<Runnable> mTransitions;
+    private final List<Runnable> mAfters;
+    private final List<Runnable> mAfterAlls;
+
+    private final int mIterations;
+    private final String mTestTag;
+
+    @Nullable
+    private List<TransitionResult> mResults = null;
+
+    private TransitionRunner(TransitionBuilder builder) {
+        mScreenRecorder = builder.mScreenRecorder;
+        mWmTraceMonitor = builder.mWmTraceMonitor;
+        mLayersTraceMonitor = builder.mLayersTraceMonitor;
+        mFrameStatsMonitor = builder.mFrameStatsMonitor;
+
+        mAllRunsMonitors = builder.mAllRunsMonitors;
+        mPerRunMonitors = builder.mPerRunMonitors;
+        mBeforeAlls = builder.mBeforeAlls;
+        mBefores = builder.mBefores;
+        mTransitions = builder.mTransitions;
+        mAfters = builder.mAfters;
+        mAfterAlls = builder.mAfterAlls;
+
+        mIterations = builder.mIterations;
+        mTestTag = builder.mTestTag;
+    }
+
+    static TransitionBuilder newBuilder() {
+        return new TransitionBuilder();
+    }
+
+    /**
+     * Runs the composed transition and calls monitors at the appropriate stages. If jank monitor
+     * is enabled, transitions with jank are skipped.
+     *
+     * @return itself
+     */
+    TransitionRunner run() {
+        mResults = new ArrayList<>();
+        mAllRunsMonitors.forEach(ITransitionMonitor::start);
+        mBeforeAlls.forEach(Runnable::run);
+        for (int iteration = 0; iteration < mIterations; iteration++) {
+            mBefores.forEach(Runnable::run);
+            mPerRunMonitors.forEach(ITransitionMonitor::start);
+            mTransitions.forEach(Runnable::run);
+            mPerRunMonitors.forEach(ITransitionMonitor::stop);
+            mAfters.forEach(Runnable::run);
+            if (runJankFree() && mFrameStatsMonitor.jankyFramesDetected()) {
+                String msg = String.format("Skipping iteration %d/%d for test %s due to jank. %s",
+                        iteration, mIterations - 1, mTestTag, mFrameStatsMonitor.toString());
+                Log.e(TAG, msg);
+                continue;
+            }
+            mResults.add(saveResult(iteration));
+        }
+        mAfterAlls.forEach(Runnable::run);
+        mAllRunsMonitors.forEach(monitor -> {
+            monitor.stop();
+            Path path = monitor.save(mTestTag);
+            Log.e(TAG, "Video saved to " + path.toString());
+        });
+        return this;
+    }
+
+    /**
+     * Returns a list of transition results.
+     *
+     * @return list of transition results.
+     */
+    List<TransitionResult> getResults() {
+        if (mResults == null) {
+            throw new IllegalStateException("Results do not exist!");
+        }
+        return mResults;
+    }
+
+    /**
+     * Deletes all transition results that are not marked for saving.
+     *
+     * @return list of transition results.
+     */
+    void deleteResults() {
+        if (mResults == null) {
+            return;
+        }
+        mResults.stream()
+                .filter(TransitionResult::canDelete)
+                .forEach(TransitionResult::delete);
+        mResults = null;
+    }
+
+    /**
+     * Saves monitor results to file.
+     *
+     * @return object containing paths to test artifacts
+     */
+    private TransitionResult saveResult(int iteration) {
+        Path windowTrace = null;
+        Path layerTrace = null;
+        Path screenCaptureVideo = null;
+
+        if (mPerRunMonitors.contains(mWmTraceMonitor)) {
+            windowTrace = mWmTraceMonitor.save(mTestTag, iteration);
+        }
+        if (mPerRunMonitors.contains(mLayersTraceMonitor)) {
+            layerTrace = mLayersTraceMonitor.save(mTestTag, iteration);
+        }
+        if (mPerRunMonitors.contains(mScreenRecorder)) {
+            screenCaptureVideo = mScreenRecorder.save(mTestTag, iteration);
+        }
+        return new TransitionResult(layerTrace, windowTrace, screenCaptureVideo);
+    }
+
+    private boolean runJankFree() {
+        return mPerRunMonitors.contains(mFrameStatsMonitor);
+    }
+
+    public String getTestTag() {
+        return mTestTag;
+    }
+
+    /**
+     * Stores paths to all test artifacts.
+     */
+    @VisibleForTesting
+    public static class TransitionResult {
+        @Nullable
+        final Path layersTrace;
+        @Nullable
+        final Path windowManagerTrace;
+        @Nullable
+        final Path screenCaptureVideo;
+        private boolean flaggedForSaving;
+
+        TransitionResult(@Nullable Path layersTrace, @Nullable Path windowManagerTrace,
+                @Nullable Path screenCaptureVideo) {
+            this.layersTrace = layersTrace;
+            this.windowManagerTrace = windowManagerTrace;
+            this.screenCaptureVideo = screenCaptureVideo;
+        }
+
+        void flagForSaving() {
+            flaggedForSaving = true;
+        }
+
+        boolean canDelete() {
+            return !flaggedForSaving;
+        }
+
+        boolean layersTraceExists() {
+            return layersTrace != null && layersTrace.toFile().exists();
+        }
+
+        byte[] getLayersTrace() {
+            try {
+                return Files.toByteArray(this.layersTrace.toFile());
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        Path getLayersTracePath() {
+            return layersTrace;
+        }
+
+        boolean windowManagerTraceExists() {
+            return windowManagerTrace != null && windowManagerTrace.toFile().exists();
+        }
+
+        public byte[] getWindowManagerTrace() {
+            try {
+                return Files.toByteArray(this.windowManagerTrace.toFile());
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        Path getWindowManagerTracePath() {
+            return windowManagerTrace;
+        }
+
+        boolean screenCaptureVideoExists() {
+            return screenCaptureVideo != null && screenCaptureVideo.toFile().exists();
+        }
+
+        Path screenCaptureVideoPath() {
+            return screenCaptureVideo;
+        }
+
+        void delete() {
+            if (layersTraceExists()) layersTrace.toFile().delete();
+            if (windowManagerTraceExists()) windowManagerTrace.toFile().delete();
+            if (screenCaptureVideoExists()) screenCaptureVideo.toFile().delete();
+        }
+    }
+
+    /**
+     * Builds a {@link TransitionRunner} instance.
+     */
+    static class TransitionBuilder {
+        private ScreenRecorder mScreenRecorder;
+        private WindowManagerTraceMonitor mWmTraceMonitor;
+        private LayersTraceMonitor mLayersTraceMonitor;
+        private WindowAnimationFrameStatsMonitor mFrameStatsMonitor;
+
+        private List<ITransitionMonitor> mAllRunsMonitors = new LinkedList<>();
+        private List<ITransitionMonitor> mPerRunMonitors = new LinkedList<>();
+        private List<Runnable> mBeforeAlls = new LinkedList<>();
+        private List<Runnable> mBefores = new LinkedList<>();
+        private List<Runnable> mTransitions = new LinkedList<>();
+        private List<Runnable> mAfters = new LinkedList<>();
+        private List<Runnable> mAfterAlls = new LinkedList<>();
+
+        private boolean mRunJankFree = true;
+        private boolean mCaptureWindowManagerTrace = true;
+        private boolean mCaptureLayersTrace = true;
+        private boolean mRecordEachRun = false;
+        private int mIterations = 1;
+        private String mTestTag = "";
+
+        private boolean mRecordAllRuns = false;
+
+        TransitionBuilder() {
+            mScreenRecorder = new ScreenRecorder();
+            mWmTraceMonitor = new WindowManagerTraceMonitor();
+            mLayersTraceMonitor = new LayersTraceMonitor();
+            mFrameStatsMonitor = new
+                    WindowAnimationFrameStatsMonitor(InstrumentationRegistry.getInstrumentation());
+        }
+
+        TransitionRunner build() {
+            if (mCaptureWindowManagerTrace) {
+                mPerRunMonitors.add(mWmTraceMonitor);
+            }
+
+            if (mCaptureLayersTrace) {
+                mPerRunMonitors.add(mLayersTraceMonitor);
+            }
+
+            if (mRunJankFree) {
+                mPerRunMonitors.add(mFrameStatsMonitor);
+            }
+
+            if (mRecordAllRuns) {
+                mAllRunsMonitors.add(mScreenRecorder);
+            }
+
+            if (mRecordEachRun) {
+                mPerRunMonitors.add(mScreenRecorder);
+            }
+
+            return new TransitionRunner(this);
+        }
+
+        TransitionBuilder runBeforeAll(Runnable runnable) {
+            mBeforeAlls.add(runnable);
+            return this;
+        }
+
+        TransitionBuilder runBefore(Runnable runnable) {
+            mBefores.add(runnable);
+            return this;
+        }
+
+        TransitionBuilder run(Runnable runnable) {
+            mTransitions.add(runnable);
+            return this;
+        }
+
+        TransitionBuilder runAfter(Runnable runnable) {
+            mAfters.add(runnable);
+            return this;
+        }
+
+        TransitionBuilder runAfterAll(Runnable runnable) {
+            mAfterAlls.add(runnable);
+            return this;
+        }
+
+        TransitionBuilder repeat(int iterations) {
+            mIterations = iterations;
+            return this;
+        }
+
+        TransitionBuilder skipWindowManagerTrace() {
+            mCaptureWindowManagerTrace = false;
+            return this;
+        }
+
+        TransitionBuilder skipLayersTrace() {
+            mCaptureLayersTrace = false;
+            return this;
+        }
+
+        TransitionBuilder includeJankyRuns() {
+            mRunJankFree = false;
+            return this;
+        }
+
+        TransitionBuilder recordEachRun() {
+            if (mRecordAllRuns) {
+                throw new IllegalArgumentException("Invalid option with recordAllRuns");
+            }
+            mRecordEachRun = true;
+            return this;
+        }
+
+        TransitionBuilder recordAllRuns() {
+            if (mRecordEachRun) {
+                throw new IllegalArgumentException("Invalid option with recordEachRun");
+            }
+            mRecordAllRuns = true;
+            return this;
+        }
+
+        TransitionBuilder withTag(String testTag) {
+            mTestTag = testTag;
+            return this;
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java
new file mode 100644
index 0000000..e3592eb
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java
@@ -0,0 +1,240 @@
+/*
+ * 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.flicker;
+
+import android.annotation.Nullable;
+
+import com.android.server.wm.flicker.Assertions.Result;
+import com.android.server.wm.nano.AppWindowTokenProto;
+import com.android.server.wm.nano.StackProto;
+import com.android.server.wm.nano.TaskProto;
+import com.android.server.wm.nano.WindowManagerTraceFileProto;
+import com.android.server.wm.nano.WindowManagerTraceProto;
+import com.android.server.wm.nano.WindowStateProto;
+import com.android.server.wm.nano.WindowTokenProto;
+
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Contains a collection of parsed WindowManager trace entries and assertions to apply over
+ * a single entry.
+ *
+ * Each entry is parsed into a list of {@link WindowManagerTrace.Entry} objects.
+ */
+public class WindowManagerTrace {
+    private static final int DEFAULT_DISPLAY = 0;
+    private final List<Entry> mEntries;
+    @Nullable
+    final private Path mSource;
+
+    private WindowManagerTrace(List<Entry> entries, Path source) {
+        this.mEntries = entries;
+        this.mSource = source;
+    }
+
+    /**
+     * Parses {@code WindowManagerTraceFileProto} from {@code data} and uses the proto to
+     * generates a list of trace entries.
+     *
+     * @param data   binary proto data
+     * @param source Path to source of data for additional debug information
+     */
+    static WindowManagerTrace parseFrom(byte[] data, Path source) {
+        List<Entry> entries = new ArrayList<>();
+
+        WindowManagerTraceFileProto fileProto;
+        try {
+            fileProto = WindowManagerTraceFileProto.parseFrom(data);
+        } catch (InvalidProtocolBufferNanoException e) {
+            throw new RuntimeException(e);
+        }
+        for (WindowManagerTraceProto entryProto : fileProto.entry) {
+            entries.add(new Entry(entryProto));
+        }
+        return new WindowManagerTrace(entries, source);
+    }
+
+    static WindowManagerTrace parseFrom(byte[] data) {
+        return parseFrom(data, null);
+    }
+
+    public List<Entry> getEntries() {
+        return mEntries;
+    }
+
+    Entry getEntry(long timestamp) {
+        Optional<Entry> entry = mEntries.stream()
+                .filter(e -> e.getTimestamp() == timestamp)
+                .findFirst();
+        if (!entry.isPresent()) {
+            throw new RuntimeException("Entry does not exist for timestamp " + timestamp);
+        }
+        return entry.get();
+    }
+
+    Optional<Path> getSource() {
+        return Optional.ofNullable(mSource);
+    }
+
+    /**
+     * Represents a single WindowManager trace entry.
+     */
+    static class Entry implements ITraceEntry {
+        private final WindowManagerTraceProto mProto;
+
+        Entry(WindowManagerTraceProto proto) {
+            mProto = proto;
+        }
+
+        private static Result isWindowVisible(String windowTitle,
+                WindowTokenProto[] windowTokenProtos) {
+            boolean titleFound = false;
+            for (WindowTokenProto windowToken : windowTokenProtos) {
+                for (WindowStateProto windowState : windowToken.windows) {
+                    if (windowState.identifier.title.contains(windowTitle)) {
+                        titleFound = true;
+                        if (isVisible(windowState)) {
+                            return new Result(true /* success */,
+                                    windowState.identifier.title + " is visible");
+                        }
+                    }
+                }
+            }
+
+            String reason;
+            if (!titleFound) {
+                reason = windowTitle + " cannot be found";
+            } else {
+                reason = windowTitle + " is invisible";
+            }
+            return new Result(false /* success */, reason);
+        }
+
+        private static boolean isVisible(WindowStateProto windowState) {
+            return windowState.windowContainer.visible;
+        }
+
+        @Override
+        public long getTimestamp() {
+            return mProto.elapsedRealtimeNanos;
+        }
+
+        /**
+         * Returns window title of the top most visible app window.
+         */
+        private String getTopVisibleAppWindow() {
+            StackProto[] stacks = mProto.windowManagerService.rootWindowContainer
+                    .displays[DEFAULT_DISPLAY].stacks;
+            for (StackProto stack : stacks) {
+                for (TaskProto task : stack.tasks) {
+                    for (AppWindowTokenProto token : task.appWindowTokens) {
+                        for (WindowStateProto windowState : token.windowToken.windows) {
+                            if (windowState.windowContainer.visible) {
+                                return task.appWindowTokens[0].name;
+                            }
+                        }
+                    }
+                }
+            }
+
+            return "";
+        }
+
+        /**
+         * Checks if aboveAppWindow with {@code windowTitle} is visible.
+         */
+        Result isAboveAppWindowVisible(String windowTitle) {
+            WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
+                    .rootWindowContainer
+                    .displays[DEFAULT_DISPLAY].aboveAppWindows;
+            Result result = isWindowVisible(windowTitle, windowTokenProtos);
+            return new Result(result.success, getTimestamp(), "showsAboveAppWindow", result.reason);
+        }
+
+        /**
+         * Checks if belowAppWindow with {@code windowTitle} is visible.
+         */
+        Result isBelowAppWindowVisible(String windowTitle) {
+            WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
+                    .rootWindowContainer
+                    .displays[DEFAULT_DISPLAY].belowAppWindows;
+            Result result = isWindowVisible(windowTitle, windowTokenProtos);
+            return new Result(result.success, getTimestamp(), "isBelowAppWindowVisible",
+                    result.reason);
+        }
+
+        /**
+         * Checks if imeWindow with {@code windowTitle} is visible.
+         */
+        Result isImeWindowVisible(String windowTitle) {
+            WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
+                    .rootWindowContainer
+                    .displays[DEFAULT_DISPLAY].imeWindows;
+            Result result = isWindowVisible(windowTitle, windowTokenProtos);
+            return new Result(result.success, getTimestamp(), "isImeWindowVisible",
+                    result.reason);
+        }
+
+        /**
+         * Checks if app window with {@code windowTitle} is on top.
+         */
+        Result isVisibleAppWindowOnTop(String windowTitle) {
+            String topAppWindow = getTopVisibleAppWindow();
+            boolean success = topAppWindow.contains(windowTitle);
+            String reason = "wanted=" + windowTitle + " found=" + topAppWindow;
+            return new Result(success, getTimestamp(), "isAppWindowOnTop", reason);
+        }
+
+        /**
+         * Checks if app window with {@code windowTitle} is visible.
+         */
+        Result isAppWindowVisible(String windowTitle) {
+            final String assertionName = "isAppWindowVisible";
+            boolean titleFound = false;
+            StackProto[] stacks = mProto.windowManagerService.rootWindowContainer
+                    .displays[DEFAULT_DISPLAY].stacks;
+            for (StackProto stack : stacks) {
+                for (TaskProto task : stack.tasks) {
+                    for (AppWindowTokenProto token : task.appWindowTokens) {
+                        if (token.name.contains(windowTitle)) {
+                            titleFound = true;
+                            for (WindowStateProto windowState : token.windowToken.windows) {
+                                if (windowState.windowContainer.visible) {
+                                    return new Result(true /* success */, getTimestamp(),
+                                            assertionName, "Window " + token.name +
+                                            "is visible");
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            String reason;
+            if (!titleFound) {
+                reason = "Window " + windowTitle + " cannot be found";
+            } else {
+                reason = "Window " + windowTitle + " is invisible";
+            }
+            return new Result(false /* success */, getTimestamp(), assertionName, reason);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
new file mode 100644
index 0000000..0da8761
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
@@ -0,0 +1,143 @@
+/*
+ * 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.flicker;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.view.Surface;
+import android.view.WindowManager;
+
+/**
+ * Helper functions to retrieve system window sizes and positions.
+ */
+class WindowUtils {
+
+    static Rect getDisplayBounds() {
+        Point display = new Point();
+        WindowManager wm =
+                (WindowManager) InstrumentationRegistry.getContext().getSystemService(
+                        Context.WINDOW_SERVICE);
+        wm.getDefaultDisplay().getRealSize(display);
+        return new Rect(0, 0, display.x, display.y);
+    }
+
+    private static int getCurrentRotation() {
+        WindowManager wm =
+                (WindowManager) InstrumentationRegistry.getContext().getSystemService(
+                        Context.WINDOW_SERVICE);
+        return wm.getDefaultDisplay().getRotation();
+    }
+
+    static Rect getDisplayBounds(int requestedRotation) {
+        Rect displayBounds = getDisplayBounds();
+        int currentDisplayRotation = getCurrentRotation();
+
+        boolean displayIsRotated = (currentDisplayRotation == Surface.ROTATION_90 ||
+                currentDisplayRotation == Surface.ROTATION_270);
+
+        boolean requestedDisplayIsRotated = requestedRotation == Surface.ROTATION_90 ||
+                requestedRotation == Surface.ROTATION_270;
+
+        // if the current orientation changes with the requested rotation,
+        // flip height and width of display bounds.
+        if (displayIsRotated != requestedDisplayIsRotated) {
+            return new Rect(0, 0, displayBounds.height(), displayBounds.width());
+        }
+
+        return new Rect(0, 0, displayBounds.width(), displayBounds.height());
+    }
+
+
+    static Rect getAppPosition(int requestedRotation) {
+        Rect displayBounds = getDisplayBounds();
+        int currentDisplayRotation = getCurrentRotation();
+
+        boolean displayIsRotated = currentDisplayRotation == Surface.ROTATION_90 ||
+                currentDisplayRotation == Surface.ROTATION_270;
+
+        boolean requestedAppIsRotated = requestedRotation == Surface.ROTATION_90 ||
+                requestedRotation == Surface.ROTATION_270;
+
+        // display size will change if the display is reflected. Flip height and width of app if the
+        // requested rotation is different from the current rotation.
+        if (displayIsRotated != requestedAppIsRotated) {
+            return new Rect(0, 0, displayBounds.height(), displayBounds.width());
+        }
+
+        return new Rect(0, 0, displayBounds.width(), displayBounds.height());
+    }
+
+    static Rect getStatusBarPosition(int requestedRotation) {
+        Resources resources = InstrumentationRegistry.getContext().getResources();
+        String resourceName;
+        Rect displayBounds = getDisplayBounds();
+        int width;
+        if (requestedRotation == Surface.ROTATION_0 || requestedRotation == Surface.ROTATION_180) {
+            resourceName = "status_bar_height_portrait";
+            width = Math.min(displayBounds.width(), displayBounds.height());
+        } else {
+            resourceName = "status_bar_height_landscape";
+            width = Math.max(displayBounds.width(), displayBounds.height());
+        }
+
+        int resourceId = resources.getIdentifier(resourceName, "dimen", "android");
+        int height = resources.getDimensionPixelSize(resourceId);
+
+        return new Rect(0, 0, width, height);
+    }
+
+    static Rect getNavigationBarPosition(int requestedRotation) {
+        Resources resources = InstrumentationRegistry.getContext().getResources();
+        Rect displayBounds = getDisplayBounds();
+        int displayWidth = Math.min(displayBounds.width(), displayBounds.height());
+        int displayHeight = Math.max(displayBounds.width(), displayBounds.height());
+        int resourceId;
+        if (requestedRotation == Surface.ROTATION_0 || requestedRotation == Surface.ROTATION_180) {
+            resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
+            int height = resources.getDimensionPixelSize(resourceId);
+            return new Rect(0, displayHeight - height, displayWidth, displayHeight);
+        } else {
+            resourceId = resources.getIdentifier("navigation_bar_width", "dimen", "android");
+            int width = resources.getDimensionPixelSize(resourceId);
+            // swap display dimensions in landscape or seascape mode
+            int temp = displayHeight;
+            displayHeight = displayWidth;
+            displayWidth = temp;
+            if (requestedRotation == Surface.ROTATION_90) {
+                return new Rect(0, 0, width, displayHeight);
+            } else {
+                return new Rect(displayWidth - width, 0, displayWidth, displayHeight);
+            }
+        }
+    }
+
+    static int getNavigationBarHeight() {
+        Resources resources = InstrumentationRegistry.getContext().getResources();
+        int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
+        return resources.getDimensionPixelSize(resourceId);
+    }
+
+    static int getDockedStackDividerInset() {
+        Resources resources = InstrumentationRegistry.getContext().getResources();
+        int resourceId = resources.getIdentifier("docked_stack_divider_insets", "dimen",
+                "android");
+        return resources.getDimensionPixelSize(resourceId);
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java
new file mode 100644
index 0000000..1fc7d59
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java
@@ -0,0 +1,193 @@
+/*
+ * 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.flicker;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.annotation.Nullable;
+
+import com.android.server.wm.flicker.Assertions.Result;
+import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
+
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Truth subject for {@link WindowManagerTrace} objects.
+ */
+public class WmTraceSubject extends Subject<WmTraceSubject, WindowManagerTrace> {
+    // Boiler-plate Subject.Factory for WmTraceSubject
+    private static final SubjectFactory<WmTraceSubject, WindowManagerTrace> FACTORY =
+            new SubjectFactory<WmTraceSubject, WindowManagerTrace>() {
+                @Override
+                public WmTraceSubject getSubject(
+                        FailureStrategy fs, @Nullable WindowManagerTrace target) {
+                    return new WmTraceSubject(fs, target);
+                }
+            };
+
+    private AssertionsChecker<WindowManagerTrace.Entry> mChecker = new AssertionsChecker<>();
+
+    private WmTraceSubject(FailureStrategy fs, @Nullable WindowManagerTrace subject) {
+        super(fs, subject);
+    }
+
+    // User-defined entry point
+    public static WmTraceSubject assertThat(@Nullable WindowManagerTrace entry) {
+        return assertAbout(FACTORY).that(entry);
+    }
+
+    // User-defined entry point
+    public static WmTraceSubject assertThat(@Nullable TransitionResult result) {
+        WindowManagerTrace entries = WindowManagerTrace.parseFrom(result.getWindowManagerTrace(),
+                result.getWindowManagerTracePath());
+        return assertWithMessage(result.toString()).about(FACTORY).that(entries);
+    }
+
+    // Static method for getting the subject factory (for use with assertAbout())
+    public static SubjectFactory<WmTraceSubject, WindowManagerTrace> entries() {
+        return FACTORY;
+    }
+
+    public void forAllEntries() {
+        test();
+    }
+
+    public void forRange(long startTime, long endTime) {
+        mChecker.filterByRange(startTime, endTime);
+        test();
+    }
+
+    public WmTraceSubject then() {
+        mChecker.checkChangingAssertions();
+        return this;
+    }
+
+    public void inTheBeginning() {
+        if (getSubject().getEntries().isEmpty()) {
+            fail("No entries found.");
+        }
+        mChecker.checkFirstEntry();
+        test();
+    }
+
+    public void atTheEnd() {
+        if (getSubject().getEntries().isEmpty()) {
+            fail("No entries found.");
+        }
+        mChecker.checkLastEntry();
+        test();
+    }
+
+    private void test() {
+        List<Result> failures = mChecker.test(getSubject().getEntries());
+        if (!failures.isEmpty()) {
+            Optional<Path> failureTracePath = getSubject().getSource();
+            String failureLogs = failures.stream().map(Result::toString)
+                    .collect(Collectors.joining("\n"));
+            String tracePath = "";
+            if (failureTracePath.isPresent()) {
+                tracePath = "\nWindowManager Trace can be found in: "
+                        + failureTracePath.get().toAbsolutePath() + "\n";
+            }
+            fail(tracePath + failureLogs);
+        }
+    }
+
+    public WmTraceSubject showsAboveAppWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isAboveAppWindowVisible(partialWindowTitle),
+                "showsAboveAppWindow(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject hidesAboveAppWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isAboveAppWindowVisible(partialWindowTitle).negate(),
+                "hidesAboveAppWindow" + "(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject showsBelowAppWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isBelowAppWindowVisible(partialWindowTitle),
+                "showsBelowAppWindow(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject hidesBelowAppWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isBelowAppWindowVisible(partialWindowTitle).negate(),
+                "hidesBelowAppWindow" + "(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject showsImeWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isImeWindowVisible(partialWindowTitle),
+                "showsBelowAppWindow(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject hidesImeWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isImeWindowVisible(partialWindowTitle).negate(),
+                "hidesImeWindow" + "(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject showsAppWindowOnTop(String partialWindowTitle) {
+        mChecker.add(
+                entry -> {
+                    Result result = entry.isAppWindowVisible(partialWindowTitle);
+                    if (result.passed()) {
+                        result = entry.isVisibleAppWindowOnTop(partialWindowTitle);
+                    }
+                    return result;
+                },
+                "showsAppWindowOnTop(" + partialWindowTitle + ")"
+        );
+        return this;
+    }
+
+    public WmTraceSubject hidesAppWindowOnTop(String partialWindowTitle) {
+        mChecker.add(
+                entry -> {
+                    Result result = entry.isAppWindowVisible(partialWindowTitle).negate();
+                    if (result.failed()) {
+                        result = entry.isVisibleAppWindowOnTop(partialWindowTitle).negate();
+                    }
+                    return result;
+                },
+                "hidesAppWindowOnTop(" + partialWindowTitle + ")"
+        );
+        return this;
+    }
+
+    public WmTraceSubject showsAppWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isAppWindowVisible(partialWindowTitle),
+                "showsAppWindow(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject hidesAppWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isAppWindowVisible(partialWindowTitle).negate(),
+                "hidesAppWindow(" + partialWindowTitle + ")");
+        return this;
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.java
new file mode 100644
index 0000000..67e0ecc
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.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.wm.flicker.monitor;
+
+import android.os.Environment;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Collects test artifacts during a UI transition.
+ */
+public interface ITransitionMonitor {
+    Path OUTPUT_DIR = Paths.get(Environment.getExternalStorageDirectory().toString(), "flicker");
+
+    /**
+     * Starts monitor.
+     */
+    void start();
+
+    /**
+     * Stops monitor.
+     */
+    void stop();
+
+    /**
+     * Saves any monitor artifacts to file adding {@code testTag} and {@code iteration}
+     * to the file name.
+     *
+     * @param testTag   suffix added to artifact name
+     * @param iteration suffix added to artifact name
+     *
+     * @return Path to saved artifact
+     */
+    default Path save(String testTag, int iteration) {
+        return save(testTag + "_" + iteration);
+    }
+
+    /**
+     * Saves any monitor artifacts to file adding {@code testTag} to the file name.
+     *
+     * @param testTag suffix added to artifact name
+     *
+     * @return Path to saved artifact
+     */
+    default Path save(String testTag) {
+        throw new UnsupportedOperationException("Save not implemented for this monitor");
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java
new file mode 100644
index 0000000..c55d068
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.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.wm.flicker.monitor;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * Captures Layers trace from SurfaceFlinger.
+ */
+public class LayersTraceMonitor extends TraceMonitor {
+    private static final String TAG = "LayersTraceMonitor";
+    private IBinder mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger");
+
+    public LayersTraceMonitor() {
+        traceFileName = "layers_trace.pb";
+    }
+
+    @Override
+    public void start() {
+        setEnabled(true);
+    }
+
+    @Override
+    public void stop() {
+        setEnabled(false);
+    }
+
+    @Override
+    public boolean isEnabled() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken("android.ui.ISurfaceComposer");
+        mSurfaceFlinger.transact(/* LAYER_TRACE_STATUS_CODE */ 1026,
+                data, reply, 0 /* flags */);
+        return reply.readBoolean();
+    }
+
+    private void setEnabled(boolean isEnabled) {
+        Parcel data = null;
+        try {
+            if (mSurfaceFlinger != null) {
+                data = Parcel.obtain();
+                data.writeInterfaceToken("android.ui.ISurfaceComposer");
+                data.writeInt(isEnabled ? 1 : 0);
+                mSurfaceFlinger.transact( /* LAYER_TRACE_CONTROL_CODE */ 1025,
+                        data, null, 0 /* flags */);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not set layer tracing." + e.toString());
+        } finally {
+            if (data != null) {
+                data.recycle();
+            }
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
new file mode 100644
index 0000000..4787586
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.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.wm.flicker.monitor;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Captures screen contents and saves it as a mp4 video file.
+ */
+public class ScreenRecorder implements ITransitionMonitor {
+    @VisibleForTesting
+    static final Path DEFAULT_OUTPUT_PATH = OUTPUT_DIR.resolve("transition.mp4");
+    private static final String TAG = "FLICKER";
+    private Thread recorderThread;
+
+    @VisibleForTesting
+    static Path getPath(String testTag) {
+        return OUTPUT_DIR.resolve(testTag + ".mp4");
+    }
+
+    @Override
+    public void start() {
+        OUTPUT_DIR.toFile().mkdirs();
+        String command = "screenrecord " + DEFAULT_OUTPUT_PATH;
+        recorderThread = new Thread(() -> {
+            try {
+                Runtime.getRuntime().exec(command);
+            } catch (IOException e) {
+                Log.e(TAG, "Error executing " + command, e);
+            }
+        });
+        recorderThread.start();
+    }
+
+    @Override
+    public void stop() {
+        runShellCommand("killall -s 2 screenrecord");
+        try {
+            recorderThread.join();
+        } catch (InterruptedException e) {
+            // ignore
+        }
+    }
+
+    @Override
+    public Path save(String testTag) {
+        try {
+            return Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag),
+                    REPLACE_EXISTING);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java
new file mode 100644
index 0000000..0e154ec
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.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.wm.flicker.monitor;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import android.os.RemoteException;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Locale;
+
+/**
+ * Base class for monitors containing common logic to read the trace
+ * as a byte array and save the trace to another location.
+ */
+public abstract class TraceMonitor implements ITransitionMonitor {
+    public static final String TAG = "FLICKER";
+    private static final String TRACE_DIR = "/data/misc/wmtrace/";
+
+    String traceFileName;
+
+    abstract boolean isEnabled() throws RemoteException;
+
+    /**
+     * Saves trace file to the external storage directory suffixing the name with the testtag
+     * and iteration.
+     *
+     * Moves the trace file from the default location via a shell command since the test app
+     * does not have security privileges to access /data/misc/wmtrace.
+     *
+     * @param testTag suffix added to trace name used to identify trace
+     *
+     * @return Path to saved trace file
+     */
+    @Override
+    public Path save(String testTag) {
+        OUTPUT_DIR.toFile().mkdirs();
+        Path traceFileCopy = getOutputTraceFilePath(testTag);
+        String copyCommand = String.format(Locale.getDefault(), "mv %s%s %s", TRACE_DIR,
+                traceFileName, traceFileCopy.toString());
+        runShellCommand(copyCommand);
+        return traceFileCopy;
+    }
+
+    @VisibleForTesting
+    Path getOutputTraceFilePath(String testTag) {
+        return OUTPUT_DIR.resolve(traceFileName + "_" + testTag);
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java
new file mode 100644
index 0000000..717d187
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.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.wm.flicker.monitor;
+
+import static android.view.FrameStats.UNDEFINED_TIME_NANO;
+
+import android.app.Instrumentation;
+import android.util.Log;
+import android.view.FrameStats;
+
+/**
+ * Monitors {@link android.view.WindowAnimationFrameStats} to detect janky frames.
+ *
+ * Adapted from {@link android.support.test.jank.internal.WindowAnimationFrameStatsMonitorImpl}
+ * using the same threshold to determine jank.
+ */
+public class WindowAnimationFrameStatsMonitor implements ITransitionMonitor {
+
+    private static final String TAG = "FLICKER";
+    // Maximum normalized error in frame duration before the frame is considered janky
+    private static final double MAX_ERROR = 0.5f;
+    // Maximum normalized frame duration before the frame is considered a pause
+    private static final double PAUSE_THRESHOLD = 15.0f;
+    private Instrumentation mInstrumentation;
+    private FrameStats stats;
+    private int numJankyFrames;
+    private long mLongestFrameNano = 0L;
+
+
+    /**
+     * Constructs a WindowAnimationFrameStatsMonitor instance.
+     */
+    public WindowAnimationFrameStatsMonitor(Instrumentation instrumentation) {
+        mInstrumentation = instrumentation;
+    }
+
+    private void analyze() {
+        int frameCount = stats.getFrameCount();
+        long refreshPeriodNano = stats.getRefreshPeriodNano();
+
+        // Skip first frame
+        for (int i = 2; i < frameCount; i++) {
+            // Handle frames that have not been presented.
+            if (stats.getFramePresentedTimeNano(i) == UNDEFINED_TIME_NANO) {
+                // The animation must not have completed. Warn and break out of the loop.
+                Log.w(TAG, "Skipping fenced frame.");
+                break;
+            }
+            long frameDurationNano = stats.getFramePresentedTimeNano(i) -
+                    stats.getFramePresentedTimeNano(i - 1);
+            double normalized = (double) frameDurationNano / refreshPeriodNano;
+            if (normalized < PAUSE_THRESHOLD) {
+                if (normalized > 1.0f + MAX_ERROR) {
+                    numJankyFrames++;
+                }
+                mLongestFrameNano = Math.max(mLongestFrameNano, frameDurationNano);
+            }
+        }
+    }
+
+    @Override
+    public void start() {
+        // Clear out any previous data
+        numJankyFrames = 0;
+        mLongestFrameNano = 0;
+        mInstrumentation.getUiAutomation().clearWindowAnimationFrameStats();
+    }
+
+    @Override
+    public void stop() {
+        stats = mInstrumentation.getUiAutomation().getWindowAnimationFrameStats();
+        analyze();
+    }
+
+    public boolean jankyFramesDetected() {
+        return stats.getFrameCount() > 0 && numJankyFrames > 0;
+    }
+
+    @Override
+    public String toString() {
+        return stats.toString() +
+                " RefreshPeriodNano:" + stats.getRefreshPeriodNano() +
+                " NumJankyFrames:" + numJankyFrames +
+                " LongestFrameNano:" + mLongestFrameNano;
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java
new file mode 100644
index 0000000..ae160b68
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java
@@ -0,0 +1,55 @@
+/*
+ * 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.flicker.monitor;
+
+import android.os.RemoteException;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+/**
+ * Captures WindowManager trace from WindowManager.
+ */
+public class WindowManagerTraceMonitor extends TraceMonitor {
+    private IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+
+    public WindowManagerTraceMonitor() {
+        traceFileName = "wm_trace.pb";
+    }
+
+    @Override
+    public void start() {
+        try {
+            wm.startWindowTrace();
+        } catch (RemoteException e) {
+            throw new RuntimeException("Could not start trace", e);
+        }
+    }
+
+    @Override
+    public void stop() {
+        try {
+            wm.stopWindowTrace();
+        } catch (RemoteException e) {
+            throw new RuntimeException("Could not stop trace", e);
+        }
+    }
+
+    @Override
+    public boolean isEnabled() throws RemoteException{
+        return wm.isWindowTraceEnabled();
+    }
+}
diff --git a/tests/FlickerTests/lib/test/Android.mk b/tests/FlickerTests/lib/test/Android.mk
new file mode 100644
index 0000000..0e3f58d
--- /dev/null
+++ b/tests/FlickerTests/lib/test/Android.mk
@@ -0,0 +1,36 @@
+#
+# 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)
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := FlickerLibTest
+LOCAL_MODULE_TAGS := tests optional
+# sign this with platform cert, so this test is allowed to call private platform apis
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_COMPATIBILITY_SUITE := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    platform-test-annotations \
+    truth-prebuilt \
+    platformprotosnano \
+    layersprotosnano \
+    flickerlib
+
+include $(BUILD_PACKAGE)
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/test/AndroidManifest.xml b/tests/FlickerTests/lib/test/AndroidManifest.xml
new file mode 100644
index 0000000..d30172d
--- /dev/null
+++ b/tests/FlickerTests/lib/test/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.server.wm.flicker">
+
+    <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27"/>
+    <!-- Read and write traces from external storage -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <!-- Capture screen contents -->
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+    <!-- Run layers trace -->
+    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+    <application android:label="FlickerLibTest">
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.server.wm.flicker"
+                     android:label="WindowManager Flicker Lib Test">
+    </instrumentation>
+
+</manifest>
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/test/AndroidTest.xml b/tests/FlickerTests/lib/test/AndroidTest.xml
new file mode 100644
index 0000000..e4cc298
--- /dev/null
+++ b/tests/FlickerTests/lib/test/AndroidTest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Config for WindowManager Flicker Tests">
+    <target_preparer class="com.google.android.tradefed.targetprep.GoogleDeviceSetup">
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on" />
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="FlickerLibTest.apk"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.server.wm.flicker"/>
+        <option name="hidden-api-checks" value="false" />
+    </test>
+</configuration>
diff --git a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pb b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pb
new file mode 100644
index 0000000..98ee6f3
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pb b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pb
new file mode 100644
index 0000000..20572d7
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pb b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pb
new file mode 100644
index 0000000..af40797
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pb b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pb
new file mode 100644
index 0000000..b3f3170
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pb b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pb
new file mode 100644
index 0000000..b3b73ce
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java
new file mode 100644
index 0000000..8e7fe1b
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.flicker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.wm.flicker.Assertions.Result;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains {@link AssertionsChecker} tests.
+ * To run this test: {@code atest FlickerLibTest:AssertionsCheckerTest}
+ */
+public class AssertionsCheckerTest {
+
+    /**
+     * Returns a list of SimpleEntry objects with {@code data} and incremental timestamps starting
+     * at 0.
+     */
+    private static List<SimpleEntry> getTestEntries(int... data) {
+        List<SimpleEntry> entries = new ArrayList<>();
+        for (int i = 0; i < data.length; i++) {
+            entries.add(new SimpleEntry(i, data[i]));
+        }
+        return entries;
+    }
+
+    @Test
+    public void canCheckAllEntries() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.add(SimpleEntry::isData42, "isData42");
+
+        List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
+
+        assertThat(failures).hasSize(5);
+    }
+
+    @Test
+    public void canCheckFirstEntry() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.checkFirstEntry();
+        checker.add(SimpleEntry::isData42, "isData42");
+
+        List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
+
+        assertThat(failures).hasSize(1);
+        assertThat(failures.get(0).timestamp).isEqualTo(0);
+    }
+
+    @Test
+    public void canCheckLastEntry() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.checkLastEntry();
+        checker.add(SimpleEntry::isData42, "isData42");
+
+        List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
+
+        assertThat(failures).hasSize(1);
+        assertThat(failures.get(0).timestamp).isEqualTo(4);
+    }
+
+    @Test
+    public void canCheckRangeOfEntries() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.filterByRange(1, 2);
+        checker.add(SimpleEntry::isData42, "isData42");
+
+        List<Result> failures = checker.test(getTestEntries(1, 42, 42, 1, 1));
+
+        assertThat(failures).hasSize(0);
+    }
+
+    @Test
+    public void emptyRangePasses() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.filterByRange(9, 10);
+        checker.add(SimpleEntry::isData42, "isData42");
+
+        List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
+
+        assertThat(failures).isEmpty();
+    }
+
+    @Test
+    public void canCheckChangingAssertions() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.add(SimpleEntry::isData42, "isData42");
+        checker.add(SimpleEntry::isData0, "isData0");
+        checker.checkChangingAssertions();
+
+        List<Result> failures = checker.test(getTestEntries(42, 0, 0, 0, 0));
+
+        assertThat(failures).isEmpty();
+    }
+
+    @Test
+    public void canCheckChangingAssertions_withNoAssertions() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.checkChangingAssertions();
+
+        List<Result> failures = checker.test(getTestEntries(42, 0, 0, 0, 0));
+
+        assertThat(failures).isEmpty();
+    }
+
+    @Test
+    public void canCheckChangingAssertions_withSingleAssertion() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.add(SimpleEntry::isData42, "isData42");
+        checker.checkChangingAssertions();
+
+        List<Result> failures = checker.test(getTestEntries(42, 42, 42, 42, 42));
+
+        assertThat(failures).isEmpty();
+    }
+
+    @Test
+    public void canFailCheckChangingAssertions_ifStartingAssertionFails() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.add(SimpleEntry::isData42, "isData42");
+        checker.add(SimpleEntry::isData0, "isData0");
+        checker.checkChangingAssertions();
+
+        List<Result> failures = checker.test(getTestEntries(0, 0, 0, 0, 0));
+
+        assertThat(failures).hasSize(1);
+    }
+
+    @Test
+    public void canFailCheckChangingAssertions_ifStartingAssertionAlwaysPasses() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.add(SimpleEntry::isData42, "isData42");
+        checker.add(SimpleEntry::isData0, "isData0");
+        checker.checkChangingAssertions();
+
+        List<Result> failures = checker.test(getTestEntries(0, 0, 0, 0, 0));
+
+        assertThat(failures).hasSize(1);
+    }
+
+    static class SimpleEntry implements ITraceEntry {
+        long timestamp;
+        int data;
+
+        SimpleEntry(long timestamp, int data) {
+            this.timestamp = timestamp;
+            this.data = data;
+        }
+
+        @Override
+        public long getTimestamp() {
+            return timestamp;
+        }
+
+        Result isData42() {
+            return new Result(this.data == 42, this.timestamp, "is42", "");
+        }
+
+        Result isData0() {
+            return new Result(this.data == 0, this.timestamp, "is42", "");
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java
new file mode 100644
index 0000000..7fd178c
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.flicker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.wm.flicker.Assertions.Result;
+
+import org.junit.Test;
+
+/**
+ * Contains {@link Assertions} tests.
+ * To run this test: {@code atest FlickerLibTest:AssertionsTest}
+ */
+public class AssertionsTest {
+    @Test
+    public void traceEntryAssertionCanNegateResult() {
+        Assertions.TraceAssertion<Integer> assertNumEquals42 =
+                getIntegerTraceEntryAssertion();
+
+        assertThat(assertNumEquals42.apply(1).success).isFalse();
+        assertThat(assertNumEquals42.negate().apply(1).success).isTrue();
+
+        assertThat(assertNumEquals42.apply(42).success).isTrue();
+        assertThat(assertNumEquals42.negate().apply(42).success).isFalse();
+    }
+
+    @Test
+    public void resultCanBeNegated() {
+        String reason = "Everything is fine!";
+        Result result = new Result(true, 0, "TestAssert", reason);
+        Result negatedResult = result.negate();
+        assertThat(negatedResult.success).isFalse();
+        assertThat(negatedResult.reason).isEqualTo(reason);
+        assertThat(negatedResult.assertionName).isEqualTo("!TestAssert");
+    }
+
+    private Assertions.TraceAssertion<Integer> getIntegerTraceEntryAssertion() {
+        return (num) -> {
+            if (num == 42) {
+                return new Result(true, "Num equals 42");
+            }
+            return new Result(false, "Num doesn't equal 42, actual:" + num);
+        };
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java
new file mode 100644
index 0000000..d06c5d7
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.flicker;
+
+import static com.android.server.wm.flicker.LayersTraceSubject.assertThat;
+import static com.android.server.wm.flicker.TestFileUtils.readTestFile;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+
+import android.graphics.Rect;
+
+import org.junit.Test;
+
+import java.nio.file.Paths;
+
+/**
+ * Contains {@link LayersTraceSubject} tests.
+ * To run this test: {@code atest FlickerLibTest:LayersTraceSubjectTest}
+ */
+public class LayersTraceSubjectTest {
+    private static final Rect displayRect = new Rect(0, 0, 1440, 2880);
+
+    private static LayersTrace readLayerTraceFromFile(String relativePath) {
+        try {
+            return LayersTrace.parseFrom(readTestFile(relativePath), Paths.get(relativePath));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void testCanDetectEmptyRegionFromLayerTrace() {
+        LayersTrace layersTraceEntries = readLayerTraceFromFile("layers_trace_emptyregion.pb");
+        try {
+            assertThat(layersTraceEntries).coversRegion(displayRect).forAllEntries();
+            fail("Assertion passed");
+        } catch (AssertionError e) {
+            assertWithMessage("Contains path to trace")
+                    .that(e.getMessage()).contains("layers_trace_emptyregion.pb");
+            assertWithMessage("Contains timestamp")
+                    .that(e.getMessage()).contains("0h38m28s8ms");
+            assertWithMessage("Contains assertion function")
+                    .that(e.getMessage()).contains("coversRegion");
+            assertWithMessage("Contains debug info")
+                    .that(e.getMessage()).contains("Region to test: " + displayRect);
+            assertWithMessage("Contains debug info")
+                    .that(e.getMessage()).contains("first empty point: 0, 99");
+        }
+    }
+
+    @Test
+    public void testCanDetectIncorrectVisibilityFromLayerTrace() {
+        LayersTrace layersTraceEntries = readLayerTraceFromFile(
+                "layers_trace_invalid_layer_visibility.pb");
+        try {
+            assertThat(layersTraceEntries).showsLayer("com.android.server.wm.flicker.testapp")
+                    .then().hidesLayer("com.android.server.wm.flicker.testapp").forAllEntries();
+            fail("Assertion passed");
+        } catch (AssertionError e) {
+            assertWithMessage("Contains path to trace")
+                    .that(e.getMessage()).contains("layers_trace_invalid_layer_visibility.pb");
+            assertWithMessage("Contains timestamp")
+                    .that(e.getMessage()).contains("70h13m14s303ms");
+            assertWithMessage("Contains assertion function")
+                    .that(e.getMessage()).contains("!isVisible");
+            assertWithMessage("Contains debug info")
+                    .that(e.getMessage()).contains(
+                    "com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp"
+                            + ".SimpleActivity#0 is visible");
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java
new file mode 100644
index 0000000..42b2aca
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java
@@ -0,0 +1,229 @@
+/*
+ * 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.flicker;
+
+import static com.android.server.wm.flicker.TestFileUtils.readTestFile;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.view.WindowManager;
+
+import org.junit.Test;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Contains {@link LayersTrace} tests.
+ * To run this test: {@code atest FlickerLibTest:LayersTraceTest}
+ */
+public class LayersTraceTest {
+    private static LayersTrace readLayerTraceFromFile(String relativePath) {
+        try {
+            return LayersTrace.parseFrom(readTestFile(relativePath));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Rect getDisplayBounds() {
+        Point display = new Point();
+        WindowManager wm =
+                (WindowManager) InstrumentationRegistry.getContext().getSystemService(
+                        Context.WINDOW_SERVICE);
+        wm.getDefaultDisplay().getRealSize(display);
+        return new Rect(0, 0, display.x, display.y);
+    }
+
+    @Test
+    public void canParseAllLayers() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        assertThat(trace.getEntries()).isNotEmpty();
+        assertThat(trace.getEntries().get(0).getTimestamp()).isEqualTo(2307984557311L);
+        assertThat(trace.getEntries().get(trace.getEntries().size() - 1).getTimestamp())
+                .isEqualTo(2308521813510L);
+        List<LayersTrace.Layer> flattenedLayers = trace.getEntries().get(0).asFlattenedLayers();
+        String msg = "Layers:\n" + flattenedLayers.stream().map(layer -> layer.mProto.name)
+                .collect(Collectors.joining("\n\t"));
+        assertWithMessage(msg).that(flattenedLayers).hasSize(47);
+    }
+
+    @Test
+    public void canParseVisibleLayers() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        assertThat(trace.getEntries()).isNotEmpty();
+        assertThat(trace.getEntries().get(0).getTimestamp()).isEqualTo(2307984557311L);
+        assertThat(trace.getEntries().get(trace.getEntries().size() - 1).getTimestamp())
+                .isEqualTo(2308521813510L);
+        List<LayersTrace.Layer> flattenedLayers = trace.getEntries().get(0).asFlattenedLayers();
+        List<LayersTrace.Layer> visibleLayers = flattenedLayers.stream()
+                .filter(layer -> layer.isVisible() && !layer.isHiddenByParent())
+                .collect(Collectors.toList());
+
+        String msg = "Visible Layers:\n" + visibleLayers.stream()
+                .map(layer -> layer.mProto.name)
+                .collect(Collectors.joining("\n\t"));
+
+        assertWithMessage(msg).that(visibleLayers).hasSize(9);
+    }
+
+    @Test
+    public void canParseLayerHierarchy() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        assertThat(trace.getEntries()).isNotEmpty();
+        assertThat(trace.getEntries().get(0).getTimestamp()).isEqualTo(2307984557311L);
+        assertThat(trace.getEntries().get(trace.getEntries().size() - 1).getTimestamp())
+                .isEqualTo(2308521813510L);
+        List<LayersTrace.Layer> layers = trace.getEntries().get(0).getRootLayers();
+        assertThat(layers).hasSize(2);
+        assertThat(layers.get(0).mChildren).hasSize(layers.get(0).mProto.children.length);
+        assertThat(layers.get(1).mChildren).hasSize(layers.get(1).mProto.children.length);
+    }
+
+    // b/76099859
+    @Test
+    public void canDetectOrphanLayers() {
+        try {
+            readLayerTraceFromFile(
+                    "layers_trace_orphanlayers.pb");
+            fail("Failed to detect orphaned layers.");
+        } catch (RuntimeException exception) {
+            assertThat(exception.getMessage()).contains(
+                    "Failed to parse layers trace. Found orphan layers "
+                            + "with parent layer id:1006 : 49");
+        }
+    }
+
+    // b/75276931
+    @Test
+    public void canDetectUncoveredRegion() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        LayersTrace.Entry entry = trace.getEntry(2308008331271L);
+
+        Assertions.Result result = entry.coversRegion(getDisplayBounds());
+
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains("Region to test: Rect(0, 0 - 1440, 2880)");
+        assertThat(result.reason).contains("first empty point: 0, 99");
+        assertThat(result.reason).contains("visible regions:");
+        assertWithMessage("Reason contains list of visible regions")
+                .that(result.reason).contains("StatusBar#0Rect(0, 0 - 1440, 98");
+    }
+
+    // Visible region tests
+    @Test
+    public void canTestLayerVisibleRegion_layerDoesNotExist() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        LayersTrace.Entry entry = trace.getEntry(2308008331271L);
+
+        final Rect expectedVisibleRegion = new Rect(0, 0, 1, 1);
+        Assertions.Result result = entry.hasVisibleRegion("ImaginaryLayer",
+                expectedVisibleRegion);
+
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains("Could not find ImaginaryLayer");
+    }
+
+    @Test
+    public void canTestLayerVisibleRegion_layerDoesNotHaveExpectedVisibleRegion() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        LayersTrace.Entry entry = trace.getEntry(2307993020072L);
+
+        final Rect expectedVisibleRegion = new Rect(0, 0, 1, 1);
+        Assertions.Result result = entry.hasVisibleRegion("NexusLauncherActivity#2",
+                expectedVisibleRegion);
+
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains(
+                "Layer com.google.android.apps.nexuslauncher/com.google.android.apps"
+                        + ".nexuslauncher.NexusLauncherActivity#2 is invisible: activeBuffer=null"
+                        + " type != ColorLayer flags=1 (FLAG_HIDDEN set) visible region is empty");
+    }
+
+    @Test
+    public void canTestLayerVisibleRegion_layerIsHiddenByParent() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        LayersTrace.Entry entry = trace.getEntry(2308455948035L);
+
+        final Rect expectedVisibleRegion = new Rect(0, 0, 1, 1);
+        Assertions.Result result = entry.hasVisibleRegion(
+                "SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main",
+                expectedVisibleRegion);
+
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains(
+                "Layer SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0 is "
+                        + "hidden by parent: com.android.chrome/com.google.android.apps.chrome"
+                        + ".Main#0");
+    }
+
+    @Test
+    public void canTestLayerVisibleRegion_incorrectRegionSize() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        LayersTrace.Entry entry = trace.getEntry(2308008331271L);
+
+        final Rect expectedVisibleRegion = new Rect(0, 0, 1440, 99);
+        Assertions.Result result = entry.hasVisibleRegion(
+                "StatusBar",
+                expectedVisibleRegion);
+
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains("StatusBar#0 has visible "
+                + "region:Rect(0, 0 - 1440, 98) expected:Rect(0, 0 - 1440, 99)");
+    }
+
+    @Test
+    public void canTestLayerVisibleRegion() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        LayersTrace.Entry entry = trace.getEntry(2308008331271L);
+
+        final Rect expectedVisibleRegion = new Rect(0, 0, 1440, 98);
+        Assertions.Result result = entry.hasVisibleRegion("StatusBar", expectedVisibleRegion);
+
+        assertThat(result.passed()).isTrue();
+    }
+
+    @Test
+    public void canTestLayerVisibleRegion_layerIsNotVisible() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_invalid_layer_visibility.pb");
+        LayersTrace.Entry entry = trace.getEntry(252794268378458L);
+
+        Assertions.Result result = entry.isVisible("com.android.server.wm.flicker.testapp");
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains(
+                "Layer com.android.server.wm.flicker.testapp/com.android.server.wm.flicker"
+                        + ".testapp.SimpleActivity#0 is invisible: type != ColorLayer visible "
+                        + "region is empty");
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java
new file mode 100644
index 0000000..5a24e6d
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java
@@ -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 com.android.server.wm.flicker;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+
+import com.google.common.io.ByteStreams;
+
+import java.io.InputStream;
+
+/**
+ * Helper functions for test file resources.
+ */
+class TestFileUtils {
+    static byte[] readTestFile(String relativePath) throws Exception {
+        Context context = InstrumentationRegistry.getContext();
+        InputStream in = context.getResources().getAssets().open("testdata/" + relativePath);
+        return ByteStreams.toByteArray(in);
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
new file mode 100644
index 0000000..9c5e2059a
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
@@ -0,0 +1,258 @@
+/*
+ * 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.flicker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.os.Environment;
+
+import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder;
+import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
+import com.android.server.wm.flicker.monitor.LayersTraceMonitor;
+import com.android.server.wm.flicker.monitor.ScreenRecorder;
+import com.android.server.wm.flicker.monitor.WindowAnimationFrameStatsMonitor;
+import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.List;
+
+/**
+ * Contains {@link TransitionRunner} tests.
+ * {@code atest FlickerLibTest:TransitionRunnerTest}
+ */
+public class TransitionRunnerTest {
+    @Mock
+    private SimpleUiTransitions mTransitionsMock;
+    @Mock
+    private ScreenRecorder mScreenRecorderMock;
+    @Mock
+    private WindowManagerTraceMonitor mWindowManagerTraceMonitorMock;
+    @Mock
+    private LayersTraceMonitor mLayersTraceMonitorMock;
+    @Mock
+    private WindowAnimationFrameStatsMonitor mWindowAnimationFrameStatsMonitor;
+    @InjectMocks
+    private TransitionBuilder mTransitionBuilder;
+
+    @Before
+    public void init() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void transitionsRunInOrder() {
+        TransitionRunner.newBuilder()
+                .runBeforeAll(mTransitionsMock::turnOnDevice)
+                .runBefore(mTransitionsMock::openApp)
+                .run(mTransitionsMock::performMagic)
+                .runAfter(mTransitionsMock::closeApp)
+                .runAfterAll(mTransitionsMock::cleanUpTracks)
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .build()
+                .run();
+
+        InOrder orderVerifier = inOrder(mTransitionsMock);
+        orderVerifier.verify(mTransitionsMock).turnOnDevice();
+        orderVerifier.verify(mTransitionsMock).openApp();
+        orderVerifier.verify(mTransitionsMock).performMagic();
+        orderVerifier.verify(mTransitionsMock).closeApp();
+        orderVerifier.verify(mTransitionsMock).cleanUpTracks();
+    }
+
+    @Test
+    public void canCombineTransitions() {
+        TransitionRunner.newBuilder()
+                .runBeforeAll(mTransitionsMock::turnOnDevice)
+                .runBeforeAll(mTransitionsMock::turnOnDevice)
+                .runBefore(mTransitionsMock::openApp)
+                .runBefore(mTransitionsMock::openApp)
+                .run(mTransitionsMock::performMagic)
+                .run(mTransitionsMock::performMagic)
+                .runAfter(mTransitionsMock::closeApp)
+                .runAfter(mTransitionsMock::closeApp)
+                .runAfterAll(mTransitionsMock::cleanUpTracks)
+                .runAfterAll(mTransitionsMock::cleanUpTracks)
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .build()
+                .run();
+
+        final int wantedNumberOfInvocations = 2;
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).turnOnDevice();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).openApp();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).performMagic();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).closeApp();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).cleanUpTracks();
+    }
+
+    @Test
+    public void emptyTransitionPasses() {
+        List<TransitionResult> results = TransitionRunner.newBuilder()
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .build()
+                .run()
+                .getResults();
+        assertThat(results).hasSize(1);
+        assertThat(results.get(0).layersTraceExists()).isFalse();
+        assertThat(results.get(0).windowManagerTraceExists()).isFalse();
+        assertThat(results.get(0).screenCaptureVideoExists()).isFalse();
+    }
+
+    @Test
+    public void canRepeatTransitions() {
+        final int wantedNumberOfInvocations = 10;
+        TransitionRunner.newBuilder()
+                .runBeforeAll(mTransitionsMock::turnOnDevice)
+                .runBefore(mTransitionsMock::openApp)
+                .run(mTransitionsMock::performMagic)
+                .runAfter(mTransitionsMock::closeApp)
+                .runAfterAll(mTransitionsMock::cleanUpTracks)
+                .repeat(wantedNumberOfInvocations)
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .build()
+                .run();
+        verify(mTransitionsMock).turnOnDevice();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).openApp();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).performMagic();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).closeApp();
+        verify(mTransitionsMock).cleanUpTracks();
+    }
+
+    private void emptyTask() {
+
+    }
+
+    @Test
+    public void canCaptureWindowManagerTrace() {
+        mTransitionBuilder
+                .run(this::emptyTask)
+                .includeJankyRuns()
+                .skipLayersTrace()
+                .withTag("mCaptureWmTraceTransitionRunner")
+                .build().run();
+        InOrder orderVerifier = inOrder(mWindowManagerTraceMonitorMock);
+        orderVerifier.verify(mWindowManagerTraceMonitorMock).start();
+        orderVerifier.verify(mWindowManagerTraceMonitorMock).stop();
+        orderVerifier.verify(mWindowManagerTraceMonitorMock)
+                .save("mCaptureWmTraceTransitionRunner", 0);
+        verifyNoMoreInteractions(mWindowManagerTraceMonitorMock);
+    }
+
+    @Test
+    public void canCaptureLayersTrace() {
+        mTransitionBuilder
+                .run(this::emptyTask)
+                .includeJankyRuns()
+                .skipWindowManagerTrace()
+                .withTag("mCaptureLayersTraceTransitionRunner")
+                .build().run();
+        InOrder orderVerifier = inOrder(mLayersTraceMonitorMock);
+        orderVerifier.verify(mLayersTraceMonitorMock).start();
+        orderVerifier.verify(mLayersTraceMonitorMock).stop();
+        orderVerifier.verify(mLayersTraceMonitorMock)
+                .save("mCaptureLayersTraceTransitionRunner", 0);
+        verifyNoMoreInteractions(mLayersTraceMonitorMock);
+    }
+
+    @Test
+    public void canRecordEachRun() throws IOException {
+        mTransitionBuilder
+                .run(this::emptyTask)
+                .withTag("mRecordEachRun")
+                .recordEachRun()
+                .includeJankyRuns()
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .repeat(2)
+                .build().run();
+        InOrder orderVerifier = inOrder(mScreenRecorderMock);
+        orderVerifier.verify(mScreenRecorderMock).start();
+        orderVerifier.verify(mScreenRecorderMock).stop();
+        orderVerifier.verify(mScreenRecorderMock).save("mRecordEachRun", 0);
+        orderVerifier.verify(mScreenRecorderMock).start();
+        orderVerifier.verify(mScreenRecorderMock).stop();
+        orderVerifier.verify(mScreenRecorderMock).save("mRecordEachRun", 1);
+        verifyNoMoreInteractions(mScreenRecorderMock);
+    }
+
+    @Test
+    public void canRecordAllRuns() throws IOException {
+        doReturn(Paths.get(Environment.getExternalStorageDirectory().getAbsolutePath(),
+                "mRecordAllRuns.mp4")).when(mScreenRecorderMock).save("mRecordAllRuns");
+        mTransitionBuilder
+                .run(this::emptyTask)
+                .recordAllRuns()
+                .includeJankyRuns()
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .withTag("mRecordAllRuns")
+                .repeat(2)
+                .build().run();
+        InOrder orderVerifier = inOrder(mScreenRecorderMock);
+        orderVerifier.verify(mScreenRecorderMock).start();
+        orderVerifier.verify(mScreenRecorderMock).stop();
+        orderVerifier.verify(mScreenRecorderMock).save("mRecordAllRuns");
+        verifyNoMoreInteractions(mScreenRecorderMock);
+    }
+
+    @Test
+    public void canSkipJankyRuns() {
+        doReturn(false).doReturn(true).doReturn(false)
+                .when(mWindowAnimationFrameStatsMonitor).jankyFramesDetected();
+        List<TransitionResult> results = mTransitionBuilder
+                .run(this::emptyTask)
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .repeat(3)
+                .build().run().getResults();
+        assertThat(results).hasSize(2);
+    }
+
+    public static class SimpleUiTransitions {
+        public void turnOnDevice() {
+        }
+
+        public void openApp() {
+        }
+
+        public void performMagic() {
+        }
+
+        public void closeApp() {
+        }
+
+        public void cleanUpTracks() {
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java
new file mode 100644
index 0000000..4927871
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.flicker;
+
+import static com.android.server.wm.flicker.TestFileUtils.readTestFile;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.wm.flicker.Assertions.Result;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Contains {@link WindowManagerTrace} tests.
+ * To run this test: {@code atest FlickerLibTest:WindowManagerTraceTest}
+ */
+public class WindowManagerTraceTest {
+    private WindowManagerTrace mTrace;
+
+    private static WindowManagerTrace readWindowManagerTraceFromFile(String relativePath) {
+        try {
+            return WindowManagerTrace.parseFrom(readTestFile(relativePath));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Before
+    public void setup() {
+        mTrace = readWindowManagerTraceFromFile("wm_trace_openchrome.pb");
+    }
+
+    @Test
+    public void canParseAllEntries() {
+        assertThat(mTrace.getEntries().get(0).getTimestamp()).isEqualTo(241777211939236L);
+        assertThat(mTrace.getEntries().get(mTrace.getEntries().size() - 1).getTimestamp()).isEqualTo
+                (241779809471942L);
+    }
+
+    @Test
+    public void canDetectAboveAppWindowVisibility() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+        Result result = entry.isAboveAppWindowVisible("NavigationBar");
+        assertThat(result.passed()).isTrue();
+    }
+
+    @Test
+    public void canDetectBelowAppWindowVisibility() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+        Result result = entry.isBelowAppWindowVisible("wallpaper");
+        assertThat(result.passed()).isTrue();
+    }
+
+    @Test
+    public void canDetectAppWindowVisibility() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+        Result result = entry.isAppWindowVisible("com.google.android.apps.nexuslauncher");
+        assertThat(result.passed()).isTrue();
+    }
+
+    @Test
+    public void canFailWithReasonForVisibilityChecks_windowNotFound() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+        Result result = entry.isAboveAppWindowVisible("ImaginaryWindow");
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains("ImaginaryWindow cannot be found");
+    }
+
+    @Test
+    public void canFailWithReasonForVisibilityChecks_windowNotVisible() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+        Result result = entry.isAboveAppWindowVisible("AssistPreviewPanel");
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains("AssistPreviewPanel is invisible");
+    }
+
+    @Test
+    public void canDetectAppZOrder() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241778130296410L);
+        Result result = entry.isVisibleAppWindowOnTop("com.google.android.apps.chrome");
+        assertThat(result.passed()).isTrue();
+    }
+
+    @Test
+    public void canFailWithReasonForZOrderChecks_windowNotOnTop() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241778130296410L);
+        Result result = entry.isVisibleAppWindowOnTop("com.google.android.apps.nexuslauncher");
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains("wanted=com.google.android.apps.nexuslauncher");
+        assertThat(result.reason).contains("found=com.android.chrome/"
+                + "com.google.android.apps.chrome.Main");
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.java
new file mode 100644
index 0000000..d547a18
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.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.wm.flicker;
+
+import static com.android.server.wm.flicker.TestFileUtils.readTestFile;
+import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+
+import org.junit.Test;
+
+/**
+ * Contains {@link WmTraceSubject} tests.
+ * To run this test: {@code atest FlickerLibTest:WmTraceSubjectTest}
+ */
+public class WmTraceSubjectTest {
+    private static WindowManagerTrace readWmTraceFromFile(String relativePath) {
+        try {
+            return WindowManagerTrace.parseFrom(readTestFile(relativePath));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void testCanTransitionInAppWindow() {
+        WindowManagerTrace trace = readWmTraceFromFile("wm_trace_openchrome2.pb");
+
+        assertThat(trace).showsAppWindowOnTop("com.google.android.apps.nexuslauncher/"
+                + ".NexusLauncherActivity").forRange(174684850717208L, 174685957511016L);
+        assertThat(trace).showsAppWindowOnTop(
+                "com.google.android.apps.nexuslauncher/.NexusLauncherActivity")
+                .then()
+                .showsAppWindowOnTop("com.android.chrome")
+                .forAllEntries();
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java
new file mode 100644
index 0000000..dbd6761
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.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.wm.flicker.monitor;
+
+import static android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto.MAGIC_NUMBER_H;
+import static android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto.MAGIC_NUMBER_L;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto;
+
+import com.google.common.io.Files;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+
+/**
+ * Contains {@link LayersTraceMonitor} tests.
+ * To run this test: {@code atest FlickerLibTest:LayersTraceMonitorTest}
+ */
+public class LayersTraceMonitorTest {
+    private LayersTraceMonitor mLayersTraceMonitor;
+
+    @Before
+    public void setup() {
+        mLayersTraceMonitor = new LayersTraceMonitor();
+    }
+
+    @After
+    public void teardown() {
+        mLayersTraceMonitor.stop();
+        mLayersTraceMonitor.getOutputTraceFilePath("captureLayersTrace").toFile().delete();
+    }
+
+    @Test
+    public void canStartLayersTrace() throws Exception {
+        mLayersTraceMonitor.start();
+        assertThat(mLayersTraceMonitor.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void canStopLayersTrace() throws Exception {
+        mLayersTraceMonitor.start();
+        assertThat(mLayersTraceMonitor.isEnabled()).isTrue();
+        mLayersTraceMonitor.stop();
+        assertThat(mLayersTraceMonitor.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void captureLayersTrace() throws Exception {
+        mLayersTraceMonitor.start();
+        mLayersTraceMonitor.stop();
+        File testFile = mLayersTraceMonitor.save("captureLayersTrace").toFile();
+        assertThat(testFile.exists()).isTrue();
+        byte[] trace = Files.toByteArray(testFile);
+        assertThat(trace.length).isGreaterThan(0);
+        LayersTraceFileProto mLayerTraceFileProto = LayersTraceFileProto.parseFrom(trace);
+        assertThat(mLayerTraceFileProto.magicNumber).isEqualTo(
+                (long) MAGIC_NUMBER_H << 32 | MAGIC_NUMBER_L);
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
new file mode 100644
index 0000000..e73eecc
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.flicker.monitor;
+
+import static android.os.SystemClock.sleep;
+
+import static com.android.server.wm.flicker.monitor.ScreenRecorder.DEFAULT_OUTPUT_PATH;
+import static com.android.server.wm.flicker.monitor.ScreenRecorder.getPath;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Contains {@link ScreenRecorder} tests.
+ * To run this test: {@code atest FlickerLibTest:ScreenRecorderTest}
+ */
+public class ScreenRecorderTest {
+    private static final String TEST_VIDEO_FILENAME = "test.mp4";
+    private ScreenRecorder mScreenRecorder;
+
+    @Before
+    public void setup() {
+        mScreenRecorder = new ScreenRecorder();
+    }
+
+    @After
+    public void teardown() {
+        DEFAULT_OUTPUT_PATH.toFile().delete();
+        getPath(TEST_VIDEO_FILENAME).toFile().delete();
+    }
+
+    @Test
+    public void videoIsRecorded() {
+        mScreenRecorder.start();
+        sleep(100);
+        mScreenRecorder.stop();
+        File file = DEFAULT_OUTPUT_PATH.toFile();
+        assertThat(file.exists()).isTrue();
+    }
+
+    @Test
+    public void videoCanBeSaved() {
+        mScreenRecorder.start();
+        sleep(100);
+        mScreenRecorder.stop();
+        mScreenRecorder.save(TEST_VIDEO_FILENAME);
+        File file = getPath(TEST_VIDEO_FILENAME).toFile();
+        assertThat(file.exists()).isTrue();
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java
new file mode 100644
index 0000000..f7fa0d5
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.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 com.android.server.wm.flicker.monitor;
+
+import static com.android.server.wm.flicker.AutomationUtils.wakeUpAndGoToHomeScreen;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Contains {@link WindowAnimationFrameStatsMonitor} tests.
+ * To run this test: {@code atest FlickerLibTest:WindowAnimationFrameStatsMonitorTest}
+ */
+public class WindowAnimationFrameStatsMonitorTest {
+    private WindowAnimationFrameStatsMonitor mWindowAnimationFrameStatsMonitor;
+
+    @Before
+    public void setup() {
+        mWindowAnimationFrameStatsMonitor = new WindowAnimationFrameStatsMonitor(
+                InstrumentationRegistry.getInstrumentation());
+        wakeUpAndGoToHomeScreen();
+    }
+
+    // TODO(vishnun)
+    @Ignore("Disabled until app-helper libraries are available.")
+    @Test
+    public void captureWindowAnimationFrameStats() throws Exception {
+        mWindowAnimationFrameStatsMonitor.start();
+        //AppHelperWrapper.getInstance().getHelper(CHROME).open();
+        //AppHelperWrapper.getInstance().getHelper(CHROME).exit();
+        mWindowAnimationFrameStatsMonitor.stop();
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java
new file mode 100644
index 0000000..56284d7
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.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.wm.flicker.monitor;
+
+import static com.android.server.wm.nano.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.nano.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.wm.nano.WindowManagerTraceFileProto;
+
+import com.google.common.io.Files;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+
+/**
+ * Contains {@link WindowManagerTraceMonitor} tests.
+ * To run this test: {@code atest FlickerLibTest:WindowManagerTraceMonitorTest}
+ */
+public class WindowManagerTraceMonitorTest {
+    private WindowManagerTraceMonitor mWindowManagerTraceMonitor;
+
+    @Before
+    public void setup() {
+        mWindowManagerTraceMonitor = new WindowManagerTraceMonitor();
+    }
+
+    @After
+    public void teardown() {
+        mWindowManagerTraceMonitor.stop();
+        mWindowManagerTraceMonitor.getOutputTraceFilePath("captureWindowTrace").toFile().delete();
+    }
+
+    @Test
+    public void canStartWindowTrace() throws Exception {
+        mWindowManagerTraceMonitor.start();
+        assertThat(mWindowManagerTraceMonitor.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void canStopWindowTrace() throws Exception {
+        mWindowManagerTraceMonitor.start();
+        assertThat(mWindowManagerTraceMonitor.isEnabled()).isTrue();
+        mWindowManagerTraceMonitor.stop();
+        assertThat(mWindowManagerTraceMonitor.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void captureWindowTrace() throws Exception {
+        mWindowManagerTraceMonitor.start();
+        mWindowManagerTraceMonitor.stop();
+        File testFile = mWindowManagerTraceMonitor.save("captureWindowTrace").toFile();
+        assertThat(testFile.exists()).isTrue();
+        byte[] trace = Files.toByteArray(testFile);
+        assertThat(trace.length).isGreaterThan(0);
+        WindowManagerTraceFileProto mWindowTraceFileProto = WindowManagerTraceFileProto.parseFrom(
+                trace);
+        assertThat(mWindowTraceFileProto.magicNumber).isEqualTo(
+                (long) MAGIC_NUMBER_H << 32 | MAGIC_NUMBER_L);
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
new file mode 100644
index 0000000..34f4ebb
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.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.wm.flicker;
+
+import static android.view.Surface.rotationToString;
+
+import static com.android.server.wm.flicker.CommonTransitions.changeAppRotation;
+import static com.android.server.wm.flicker.WindowUtils.getAppPosition;
+import static com.android.server.wm.flicker.WindowUtils.getNavigationBarPosition;
+import static com.android.server.wm.flicker.WindowUtils.getStatusBarPosition;
+import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.util.Log;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Cycle through supported app rotations.
+ * To run this test: {@code atest FlickerTest:ChangeAppRotationTest}
+ */
+@RunWith(Parameterized.class)
+@LargeTest
+public class ChangeAppRotationTest extends FlickerTestBase {
+    private int beginRotation;
+    private int endRotation;
+
+    public ChangeAppRotationTest(String beginRotationName, String endRotationName,
+            int beginRotation, int endRotation) {
+        this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "SimpleApp");
+        this.beginRotation = beginRotation;
+        this.endRotation = endRotation;
+    }
+
+    @Parameters(name = "{0}-{1}")
+    public static Collection<Object[]> getParams() {
+        int[] supportedRotations =
+                {Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
+        Collection<Object[]> params = new ArrayList<>();
+        for (int begin : supportedRotations) {
+            for (int end : supportedRotations) {
+                if (begin != end) {
+                    params.add(new Object[]{rotationToString(begin), rotationToString(end), begin,
+                            end});
+                }
+            }
+        }
+        return params;
+    }
+
+    @Before
+    public void runTransition() {
+        super.runTransition(
+                changeAppRotation(testApp, uiDevice, beginRotation, endRotation).build());
+    }
+
+    @Test
+    public void checkVisibility_navBarWindowIsAlwaysVisible() {
+        checkResults(result -> assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+        checkResults(result -> assertThat(result)
+                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkPosition_navBarLayerRotatesAndScales() {
+        Rect startingPos = getNavigationBarPosition(beginRotation);
+        Rect endingPos = getNavigationBarPosition(endRotation);
+        checkResults(result -> {
+                    LayersTraceSubject.assertThat(result)
+                            .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+                            .inTheBeginning();
+                    LayersTraceSubject.assertThat(result)
+                            .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos).atTheEnd();
+                }
+        );
+    }
+
+    @Test
+    public void checkPosition_appLayerRotates() {
+        Rect startingPos = getAppPosition(beginRotation);
+        Rect endingPos = getAppPosition(endRotation);
+        Log.e(TAG, "startingPos=" + startingPos + " endingPos=" + endingPos);
+        checkResults(result -> {
+                    LayersTraceSubject.assertThat(result)
+                            .hasVisibleRegion(testApp.getPackage(), startingPos).inTheBeginning();
+                    LayersTraceSubject.assertThat(result)
+                            .hasVisibleRegion(testApp.getPackage(), endingPos).atTheEnd();
+                }
+        );
+    }
+
+    @Test
+    public void checkPosition_statusBarLayerScales() {
+        Rect startingPos = getStatusBarPosition(beginRotation);
+        Rect endingPos = getStatusBarPosition(endRotation);
+        checkResults(result -> {
+                    LayersTraceSubject.assertThat(result)
+                            .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos)
+                            .inTheBeginning();
+                    LayersTraceSubject.assertThat(result)
+                            .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos).atTheEnd();
+                }
+        );
+    }
+
+    @Test
+    public void checkVisibility_navBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
new file mode 100644
index 0000000..2b62fcf
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.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.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToApp;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test IME window closing back to app window transitions.
+ * To run this test: {@code atest FlickerTests:CloseImeWindowToAppTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class CloseImeWindowToAppTest extends FlickerTestBase {
+
+    private static final String IME_WINDOW_TITLE = "InputMethod";
+    private IAppHelper mImeTestApp = new StandardAppHelper(
+            InstrumentationRegistry.getInstrumentation(),
+            "com.android.server.wm.flicker.testapp", "ImeApp");
+
+    @Before
+    public void runTransition() {
+        super.runTransition(editTextLoseFocusToApp(uiDevice)
+                .includeJankyRuns().build());
+    }
+
+    @Test
+    public void checkVisibility_imeLayerBecomesInvisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(IME_WINDOW_TITLE)
+                .then()
+                .hidesLayer(IME_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_imeAppLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(mImeTestApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_imeAppWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAppWindowOnTop(mImeTestApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+                getDisplayBounds()).forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
new file mode 100644
index 0000000..42b161f
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToHome;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test IME window closing to home transitions.
+ * To run this test: {@code atest FlickerTests:CloseImeWindowToHomeTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class CloseImeWindowToHomeTest extends FlickerTestBase {
+
+    private static final String IME_WINDOW_TITLE = "InputMethod";
+    private IAppHelper mImeTestApp = new StandardAppHelper(
+            InstrumentationRegistry.getInstrumentation(),
+            "com.android.server.wm.flicker.testapp", "ImeApp");
+
+    @Before
+    public void runTransition() {
+        super.runTransition(editTextLoseFocusToHome(uiDevice)
+                .includeJankyRuns().build());
+    }
+
+    @Test
+    public void checkVisibility_imeWindowBecomesInvisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsImeWindow(IME_WINDOW_TITLE)
+                .then()
+                .hidesImeWindow(IME_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_imeLayerBecomesInvisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(IME_WINDOW_TITLE)
+                .then()
+                .hidesLayer(IME_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_imeAppLayerBecomesInvisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(mImeTestApp.getPackage())
+                .then()
+                .hidesLayer(mImeTestApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_imeAppWindowBecomesInvisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAppWindowOnTop(mImeTestApp.getPackage())
+                .then()
+                .hidesAppWindowOnTop(mImeTestApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+                getDisplayBounds()).forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
new file mode 100644
index 0000000..92bb1ea
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
@@ -0,0 +1,317 @@
+/*
+ * 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.flicker;
+
+import static android.os.SystemClock.sleep;
+import static android.view.Surface.rotationToString;
+
+import static com.android.server.wm.flicker.AutomationUtils.clearRecents;
+import static com.android.server.wm.flicker.AutomationUtils.closePipWindow;
+import static com.android.server.wm.flicker.AutomationUtils.exitSplitScreen;
+import static com.android.server.wm.flicker.AutomationUtils.expandPipWindow;
+import static com.android.server.wm.flicker.AutomationUtils.launchSplitScreen;
+import static com.android.server.wm.flicker.AutomationUtils.stopPackage;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.util.Rational;
+import android.view.Surface;
+
+import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder;
+
+/**
+ * Collection of common transitions which can be used to test different apps or scenarios.
+ */
+class CommonTransitions {
+
+    public static final int ITERATIONS = 1;
+    private static final String TAG = "FLICKER";
+    private static final long APP_LAUNCH_TIMEOUT = 10000;
+
+    private static void setRotation(UiDevice device, int rotation) {
+        try {
+            switch (rotation) {
+                case Surface.ROTATION_270:
+                    device.setOrientationLeft();
+                    break;
+
+                case Surface.ROTATION_90:
+                    device.setOrientationRight();
+                    break;
+
+                case Surface.ROTATION_0:
+                default:
+                    device.setOrientationNatural();
+            }
+            // Wait for animation to complete
+            sleep(3000);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static void clickEditTextWidget(UiDevice device, IAppHelper testApp) {
+        UiObject2 editText = device.findObject(By.res(testApp.getPackage(), "plain_text_input"));
+        editText.click();
+        sleep(500);
+    }
+
+    private static void clickEnterPipButton(UiDevice device, IAppHelper testApp) {
+        UiObject2 enterPipButton = device.findObject(By.res(testApp.getPackage(), "enter_pip"));
+        enterPipButton.click();
+        sleep(500);
+    }
+
+    static TransitionBuilder openAppWarm(IAppHelper testApp, UiDevice
+            device) {
+        return TransitionRunner.newBuilder()
+                .withTag("OpenAppWarm_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBeforeAll(testApp::open)
+                .runBefore(device::pressHome)
+                .runBefore(device::waitForIdle)
+                .run(testApp::open)
+                .runAfterAll(testApp::exit)
+                .runAfterAll(AutomationUtils::setDefaultWait)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder closeAppWithBackKey(IAppHelper testApp, UiDevice
+            device) {
+        return TransitionRunner.newBuilder()
+                .withTag("closeAppWithBackKey_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(testApp::open)
+                .runBefore(device::waitForIdle)
+                .run(device::pressBack)
+                .run(device::waitForIdle)
+                .runAfterAll(testApp::exit)
+                .runAfterAll(AutomationUtils::setDefaultWait)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder closeAppWithHomeKey(IAppHelper testApp, UiDevice
+            device) {
+        return TransitionRunner.newBuilder()
+                .withTag("closeAppWithHomeKey_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(testApp::open)
+                .runBefore(device::waitForIdle)
+                .run(device::pressHome)
+                .run(device::waitForIdle)
+                .runAfterAll(testApp::exit)
+                .runAfterAll(AutomationUtils::setDefaultWait)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder getOpenAppCold(IAppHelper testApp,
+            UiDevice device) {
+        return TransitionRunner.newBuilder()
+                .withTag("OpenAppCold_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::exit)
+                .runBefore(device::waitForIdle)
+                .run(testApp::open)
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder changeAppRotation(IAppHelper testApp, UiDevice
+            device, int beginRotation, int endRotation) {
+        return TransitionRunner.newBuilder()
+                .withTag("changeAppRotation_" + testApp.getLauncherName()
+                        + rotationToString(beginRotation) + "_" +
+                        rotationToString(endRotation))
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBeforeAll(testApp::open)
+                .runBefore(() -> setRotation(device, beginRotation))
+                .run(() -> setRotation(device, endRotation))
+                .runAfterAll(testApp::exit)
+                .runAfterAll(() -> setRotation(device, Surface.ROTATION_0))
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder changeAppRotation(Intent intent, String intentId, Context context,
+            UiDevice
+                    device, int beginRotation, int endRotation) {
+        final String testTag = "changeAppRotation_" + intentId + "_" +
+                rotationToString(beginRotation) + "_" + rotationToString(endRotation);
+        return TransitionRunner.newBuilder()
+                .withTag(testTag)
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBeforeAll(() -> {
+                            context.startActivity(intent);
+                            device.wait(Until.hasObject(By.pkg(intent.getComponent()
+                                        .getPackageName()).depth(0)), APP_LAUNCH_TIMEOUT);
+                        }
+                )
+                .runBefore(() -> setRotation(device, beginRotation))
+                .run(() -> setRotation(device, endRotation))
+                .runAfterAll(() -> stopPackage(context, intent.getComponent().getPackageName()))
+                .runAfterAll(() -> setRotation(device, Surface.ROTATION_0))
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder appToSplitScreen(IAppHelper testApp, UiDevice device) {
+        return TransitionRunner.newBuilder()
+                .withTag("appToSplitScreen_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(testApp::open)
+                .runBefore(device::waitForIdle)
+                .runBefore(() -> sleep(500))
+                .run(() -> launchSplitScreen(device))
+                .runAfter(() -> exitSplitScreen(device))
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder splitScreenToLauncher(IAppHelper testApp, UiDevice device) {
+        return TransitionRunner.newBuilder()
+                .withTag("splitScreenToLauncher_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(testApp::open)
+                .runBefore(device::waitForIdle)
+                .runBefore(() -> launchSplitScreen(device))
+                .run(() -> exitSplitScreen(device))
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder editTextSetFocus(UiDevice device) {
+        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "ImeApp");
+        return TransitionRunner.newBuilder()
+                .withTag("editTextSetFocus_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::open)
+                .run(() -> clickEditTextWidget(device, testApp))
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder resizeSplitScreen(IAppHelper testAppTop, IAppHelper testAppBottom,
+            UiDevice device, Rational startRatio, Rational stopRatio) {
+        String testTag = "resizeSplitScreen_" + testAppTop.getLauncherName() + "_" +
+                testAppBottom.getLauncherName() + "_" +
+                startRatio.toString().replace("/", ":") + "_to_" +
+                stopRatio.toString().replace("/", ":");
+        return TransitionRunner.newBuilder()
+                .withTag(testTag)
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBeforeAll(() -> clearRecents(device))
+                .runBefore(testAppBottom::open)
+                .runBefore(device::pressHome)
+                .runBefore(testAppTop::open)
+                .runBefore(device::waitForIdle)
+                .runBefore(() -> launchSplitScreen(device))
+                .runBefore(() -> {
+                    UiObject2 snapshot = device.findObject(
+                            By.res("com.google.android.apps.nexuslauncher", "snapshot"));
+                    snapshot.click();
+                })
+                .runBefore(() -> AutomationUtils.resizeSplitScreen(device, startRatio))
+                .run(() -> AutomationUtils.resizeSplitScreen(device, stopRatio))
+                .runAfter(() -> exitSplitScreen(device))
+                .runAfter(device::pressHome)
+                .runAfterAll(testAppTop::exit)
+                .runAfterAll(testAppBottom::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder editTextLoseFocusToHome(UiDevice device) {
+        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "ImeApp");
+        return TransitionRunner.newBuilder()
+                .withTag("editTextLoseFocusToHome_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::open)
+                .runBefore(() -> clickEditTextWidget(device, testApp))
+                .run(device::pressHome)
+                .run(device::waitForIdle)
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder editTextLoseFocusToApp(UiDevice device) {
+        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "ImeApp");
+        return TransitionRunner.newBuilder()
+                .withTag("editTextLoseFocusToApp_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::open)
+                .runBefore(() -> clickEditTextWidget(device, testApp))
+                .run(device::pressBack)
+                .run(device::waitForIdle)
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder enterPipMode(UiDevice device) {
+        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "PipApp");
+        return TransitionRunner.newBuilder()
+                .withTag("enterPipMode_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::open)
+                .run(() -> clickEnterPipButton(device, testApp))
+                .runAfter(() -> closePipWindow(device))
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder exitPipModeToHome(UiDevice device) {
+        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "PipApp");
+        return TransitionRunner.newBuilder()
+                .withTag("exitPipModeToHome_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::open)
+                .runBefore(() -> clickEnterPipButton(device, testApp))
+                .run(() -> closePipWindow(device))
+                .run(device::waitForIdle)
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder exitPipModeToApp(UiDevice device) {
+        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "PipApp");
+        return TransitionRunner.newBuilder()
+                .withTag("exitPipModeToApp_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::open)
+                .runBefore(() -> clickEnterPipButton(device, testApp))
+                .run(() -> expandPipWindow(device))
+                .run(device::waitForIdle)
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
new file mode 100644
index 0000000..fec248c
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.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 com.android.server.wm.flicker;
+
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Rational;
+import android.view.Surface;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests to help debug individual transitions, capture video recordings and create test cases.
+ */
+@Ignore("Used for debugging transitions used in FlickerTests.")
+@RunWith(AndroidJUnit4.class)
+public class DebugTest {
+    private IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+            "com.android.server.wm.flicker.testapp", "SimpleApp");
+    private UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+    /**
+     * atest FlickerTest:DebugTests#openAppCold
+     */
+    @Test
+    public void openAppCold() {
+        CommonTransitions.getOpenAppCold(testApp, uiDevice).recordAllRuns().build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#openAppWarm
+     */
+    @Test
+    public void openAppWarm() {
+        CommonTransitions.openAppWarm(testApp, uiDevice).recordAllRuns().build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#changeOrientationFromNaturalToLeft
+     */
+    @Test
+    public void changeOrientationFromNaturalToLeft() {
+        CommonTransitions.changeAppRotation(testApp, uiDevice, Surface.ROTATION_0,
+                Surface.ROTATION_270).recordAllRuns().build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#closeAppWithBackKey
+     */
+    @Test
+    public void closeAppWithBackKey() {
+        CommonTransitions.closeAppWithBackKey(testApp, uiDevice).recordAllRuns().build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#closeAppWithHomeKey
+     */
+    @Test
+    public void closeAppWithHomeKey() {
+        CommonTransitions.closeAppWithHomeKey(testApp, uiDevice).recordAllRuns().build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#openAppToSplitScreen
+     */
+    @Test
+    public void openAppToSplitScreen() {
+        CommonTransitions.appToSplitScreen(testApp, uiDevice).includeJankyRuns().recordAllRuns()
+                .build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#splitScreenToLauncher
+     */
+    @Test
+    public void splitScreenToLauncher() {
+        CommonTransitions.splitScreenToLauncher(testApp,
+                uiDevice).includeJankyRuns().recordAllRuns()
+                .build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#resizeSplitScreen
+     */
+    @Test
+    public void resizeSplitScreen() {
+        IAppHelper bottomApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "ImeApp");
+        CommonTransitions.resizeSplitScreen(testApp, bottomApp, uiDevice, new Rational(1, 3),
+                new Rational(2, 3)).includeJankyRuns().recordEachRun().build().run();
+    }
+
+    // IME tests
+
+    /**
+     * atest FlickerTest:DebugTests#editTextSetFocus
+     */
+    @Test
+    public void editTextSetFocus() {
+        CommonTransitions.editTextSetFocus(uiDevice).includeJankyRuns().recordEachRun()
+                .build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#editTextLoseFocusToHome
+     */
+    @Test
+    public void editTextLoseFocusToHome() {
+        CommonTransitions.editTextLoseFocusToHome(uiDevice).includeJankyRuns().recordEachRun()
+                .build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#editTextLoseFocusToApp
+     */
+    @Test
+    public void editTextLoseFocusToApp() {
+        CommonTransitions.editTextLoseFocusToHome(uiDevice).includeJankyRuns().recordEachRun()
+                .build().run();
+    }
+
+    // PIP tests
+
+    /**
+     * atest FlickerTest:DebugTests#enterPipMode
+     */
+    @Test
+    public void enterPipMode() {
+        CommonTransitions.enterPipMode(uiDevice).includeJankyRuns().recordEachRun().build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#exitPipModeToHome
+     */
+    @Test
+    public void exitPipModeToHome() {
+        CommonTransitions.exitPipModeToHome(uiDevice).includeJankyRuns().recordEachRun()
+                .build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#exitPipModeToApp
+     */
+    @Test
+    public void exitPipModeToApp() {
+        CommonTransitions.exitPipModeToApp(uiDevice).includeJankyRuns().recordEachRun()
+                .build().run();
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
new file mode 100644
index 0000000..7061b23
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
@@ -0,0 +1,125 @@
+/*
+ * 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.flicker;
+
+import static com.android.server.wm.flicker.AutomationUtils.setDefaultWait;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
+
+import org.junit.After;
+import org.junit.AfterClass;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Base class of all Flicker test that performs common functions for all flicker tests:
+ * <p>
+ * - Caches transitions so that a transition is run once and the transition results are used by
+ * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods
+ * multiple times.
+ * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed.
+ * - Fails tests if results are not available for any test due to jank.
+ */
+public class FlickerTestBase {
+    public static final String TAG = "FLICKER";
+    static final String NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar";
+    static final String STATUS_BAR_WINDOW_TITLE = "StatusBar";
+    static final String DOCKED_STACK_DIVIDER = "DockedStackDivider";
+    private static HashMap<String, List<TransitionResult>> transitionResults =
+            new HashMap<>();
+    IAppHelper testApp;
+    UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+    private List<TransitionResult> results;
+    private TransitionResult lastResult = null;
+
+    /**
+     * Teardown any system settings and clean up test artifacts from the file system.
+     *
+     * Note: test artifacts for failed tests will remain on the device.
+     */
+    @AfterClass
+    public static void teardown() {
+        setDefaultWait();
+        transitionResults.values().stream()
+                .flatMap(List::stream)
+                .forEach(result -> {
+                    if (result.canDelete()) {
+                        result.delete();
+                    } else {
+                        if (result.layersTraceExists()) {
+                            Log.e(TAG, "Layers trace saved to " + result.getLayersTracePath());
+                        }
+                        if (result.windowManagerTraceExists()) {
+                            Log.e(TAG, "WindowManager trace saved to " + result
+                                    .getWindowManagerTracePath
+                                            ());
+                        }
+                        if (result.screenCaptureVideoExists()) {
+                            Log.e(TAG, "Screen capture video saved to " + result
+                                    .screenCaptureVideo.toString());
+                        }
+                    }
+                });
+    }
+
+    /**
+     * Runs a transition, returns a cached result if the transition has run before.
+     */
+    void runTransition(TransitionRunner transition) {
+        if (transitionResults.containsKey(transition.getTestTag())) {
+            results = transitionResults.get(transition.getTestTag());
+            return;
+        }
+        results = transition.run().getResults();
+        /* Fail if we don't have any results due to jank */
+        assertWithMessage("No results to test because all transition runs were invalid because "
+                + "of Jank").that(results).isNotEmpty();
+        transitionResults.put(transition.getTestTag(), results);
+    }
+
+    /**
+     * Goes through a list of transition results and checks assertions on each result.
+     */
+    void checkResults(Consumer<TransitionResult> assertion) {
+
+        for (TransitionResult result : results) {
+            lastResult = result;
+            assertion.accept(result);
+        }
+        lastResult = null;
+    }
+
+    /**
+     * Kludge to mark a file for saving. If {@code checkResults} fails, the last result is not
+     * cleared. This indicates the assertion failed for the result, so mark it for saving.
+     */
+    @After
+    public void markArtifactsForSaving() {
+        if (lastResult != null) {
+            lastResult.flagForSaving();
+        }
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
new file mode 100644
index 0000000..7e71369
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.getOpenAppCold;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cold launch app from launcher.
+ * To run this test: {@code atest FlickerTests:OpenAppColdTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class OpenAppColdTest extends FlickerTestBase {
+
+    public OpenAppColdTest() {
+        this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "SimpleApp");
+    }
+
+    @Before
+    public void runTransition() {
+        super.runTransition(getOpenAppCold(testApp, uiDevice).build());
+    }
+
+    @Test
+    public void checkVisibility_navBarWindowIsAlwaysVisible() {
+        checkResults(result -> assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+        checkResults(result -> assertThat(result)
+                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_wallpaperWindowBecomesInvisible() {
+        checkResults(result -> assertThat(result)
+                .showsBelowAppWindow("wallpaper")
+                .then()
+                .hidesBelowAppWindow("wallpaper")
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkZOrder_appWindowReplacesLauncherAsTopWindow() {
+        checkResults(result -> assertThat(result)
+                .showsAppWindowOnTop(
+                        "com.google.android.apps.nexuslauncher/.NexusLauncherActivity")
+                .then()
+                .showsAppWindowOnTop(testApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+                getDisplayBounds()).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_navBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_wallpaperLayerBecomesInvisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer("wallpaper")
+                .then()
+                .hidesLayer("wallpaper")
+                .forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
new file mode 100644
index 0000000..745569a
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
@@ -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.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.appToSplitScreen;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test open app to split screen.
+ * To run this test: {@code atest FlickerTests:OpenAppToSplitScreenTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class OpenAppToSplitScreenTest extends FlickerTestBase {
+
+    public OpenAppToSplitScreenTest() {
+        this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "SimpleApp");
+    }
+
+    @Before
+    public void runTransition() {
+        super.runTransition(appToSplitScreen(testApp, uiDevice).includeJankyRuns().build());
+    }
+
+    @Test
+    public void checkVisibility_navBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_dividerWindowBecomesVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
+                .then()
+                .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result ->
+                LayersTraceSubject.assertThat(result)
+                        .coversRegion(getDisplayBounds()).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_navBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_dividerLayerBecomesVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .hidesLayer(DOCKED_STACK_DIVIDER)
+                .then()
+                .showsLayer(DOCKED_STACK_DIVIDER)
+                .forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
new file mode 100644
index 0000000..de7639d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.openAppWarm;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test warm launch app.
+ * To run this test: {@code atest FlickerTests:OpenAppWarmTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class OpenAppWarmTest extends FlickerTestBase {
+
+    public OpenAppWarmTest() {
+        this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "SimpleApp");
+    }
+
+    @Before
+    public void runTransition() {
+        super.runTransition(openAppWarm(testApp, uiDevice).build());
+    }
+
+    @Test
+    public void checkVisibility_navBarIsAlwaysVisible() {
+        checkResults(result -> assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarIsAlwaysVisible() {
+        checkResults(result -> assertThat(result)
+                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_wallpaperBecomesInvisible() {
+        checkResults(result -> assertThat(result)
+                .showsBelowAppWindow("wallpaper")
+                .then()
+                .hidesBelowAppWindow("wallpaper")
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkZOrder_appWindowReplacesLauncherAsTopWindow() {
+        checkResults(result -> assertThat(result)
+                .showsAppWindowOnTop(
+                        "com.google.android.apps.nexuslauncher/.NexusLauncherActivity")
+                .then()
+                .showsAppWindowOnTop(testApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+                getDisplayBounds()).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_navBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_wallpaperLayerBecomesInvisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer("wallpaper")
+                .then()
+                .hidesLayer("wallpaper")
+                .forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
new file mode 100644
index 0000000..1bd519c
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.editTextSetFocus;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test IME window opening transitions.
+ * To run this test: {@code atest FlickerTests:OpenImeWindowTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class OpenImeWindowTest extends FlickerTestBase {
+
+    private static final String IME_WINDOW_TITLE = "InputMethod";
+
+    @Before
+    public void runTransition() {
+        super.runTransition(editTextSetFocus(uiDevice)
+                .includeJankyRuns().build());
+    }
+
+    @Test
+    public void checkVisibility_imeWindowBecomesVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .hidesImeWindow(IME_WINDOW_TITLE)
+                .then()
+                .showsImeWindow(IME_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_imeLayerBecomesVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .hidesLayer(IME_WINDOW_TITLE)
+                .then()
+                .showsLayer(IME_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+                getDisplayBounds()).forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
new file mode 100644
index 0000000..8a15cbd
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.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.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.resizeSplitScreen;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import static com.android.server.wm.flicker.WindowUtils.getDockedStackDividerInset;
+import static com.android.server.wm.flicker.WindowUtils.getNavigationBarHeight;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.Rect;
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Rational;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test split screen resizing window transitions.
+ * To run this test: {@code atest FlickerTests:ResizeSplitScreenTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class ResizeSplitScreenTest extends FlickerTestBase {
+
+    public ResizeSplitScreenTest() {
+        this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "SimpleApp");
+    }
+
+    @Before
+    public void runTransition() {
+        IAppHelper bottomApp = new StandardAppHelper(InstrumentationRegistry
+                .getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "ImeApp");
+        super.runTransition(resizeSplitScreen(testApp, bottomApp, uiDevice, new Rational(1, 3),
+                new Rational(2, 3)).includeJankyRuns().build());
+    }
+
+    @Test
+    public void checkVisibility_navBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(STATUS_BAR_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_topAppLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer("SimpleActivity")
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_bottomAppLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer("ImeActivity")
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_dividerLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(DOCKED_STACK_DIVIDER)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkPosition_appsStartingBounds() {
+        Rect displayBounds = getDisplayBounds();
+        checkResults(result -> {
+            LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
+                    result.getLayersTracePath());
+
+            assertThat(entries.getEntries()).isNotEmpty();
+            Rect startingDividerBounds = entries.getEntries().get(0).getVisibleBounds
+                    (DOCKED_STACK_DIVIDER);
+
+            Rect startingTopAppBounds = new Rect(0, 0, startingDividerBounds.right,
+                    startingDividerBounds.top + getDockedStackDividerInset());
+
+            Rect startingBottomAppBounds = new Rect(0,
+                    startingDividerBounds.bottom - getDockedStackDividerInset(),
+                    displayBounds.right,
+                    displayBounds.bottom - getNavigationBarHeight());
+
+            LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion("SimpleActivity", startingTopAppBounds)
+                    .inTheBeginning();
+
+            LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion("ImeActivity", startingBottomAppBounds)
+                    .inTheBeginning();
+        });
+    }
+
+    @Test
+    public void checkPosition_appsEndingBounds() {
+        Rect displayBounds = getDisplayBounds();
+        checkResults(result -> {
+            LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
+                    result.getLayersTracePath());
+
+            assertThat(entries.getEntries()).isNotEmpty();
+            Rect endingDividerBounds = entries.getEntries().get(
+                    entries.getEntries().size() - 1).getVisibleBounds(
+                    DOCKED_STACK_DIVIDER);
+
+            Rect startingTopAppBounds = new Rect(0, 0, endingDividerBounds.right,
+                    endingDividerBounds.top + getDockedStackDividerInset());
+
+            Rect startingBottomAppBounds = new Rect(0,
+                    endingDividerBounds.bottom - getDockedStackDividerInset(),
+                    displayBounds.right,
+                    displayBounds.bottom - getNavigationBarHeight());
+
+            LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion("SimpleActivity", startingTopAppBounds)
+                    .atTheEnd();
+
+            LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion("ImeActivity", startingBottomAppBounds)
+                    .atTheEnd();
+        });
+    }
+
+    @Test
+    public void checkVisibility_navBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_topAppWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAppWindow("SimpleActivity")
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_bottomAppWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAppWindow("ImeActivity")
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_dividerWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
+                .forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
new file mode 100644
index 0000000..3eab68d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.flicker;
+
+import static android.view.Surface.rotationToString;
+
+import static com.android.server.wm.flicker.CommonTransitions.changeAppRotation;
+import static com.android.server.wm.flicker.WindowUtils.getAppPosition;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import static com.android.server.wm.flicker.WindowUtils.getNavigationBarPosition;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.EXTRA_STARVE_UI_THREAD;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME;
+
+import android.content.Intent;
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Cycle through supported app rotations using seamless rotations.
+ * To run this test: {@code atest FlickerTests:SeamlessAppRotationTest}
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+public class SeamlessAppRotationTest extends FlickerTestBase {
+    private int mBeginRotation;
+    private int mEndRotation;
+    private Intent mIntent;
+
+    public SeamlessAppRotationTest(String testId, Intent intent, int beginRotation,
+            int endRotation) {
+        this.mIntent = intent;
+        this.mBeginRotation = beginRotation;
+        this.mEndRotation = endRotation;
+    }
+
+    @Parameters(name = "{0}")
+    public static Collection<Object[]> getParams() {
+        int[] supportedRotations =
+                {Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
+        Collection<Object[]> params = new ArrayList<>();
+
+        ArrayList<Intent> testIntents = new ArrayList<>();
+
+        // launch test activity that supports seamless rotation
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.setComponent(SEAMLESS_ACTIVITY_COMPONENT_NAME);
+        testIntents.add(intent);
+
+        // launch test activity that supports seamless rotation with a busy UI thread to miss frames
+        // when the app is asked to redraw
+        intent = new Intent(intent);
+        intent.putExtra(EXTRA_STARVE_UI_THREAD, true);
+        testIntents.add(intent);
+
+        for (Intent testIntent : testIntents) {
+            for (int begin : supportedRotations) {
+                for (int end : supportedRotations) {
+                    if (begin != end) {
+                        String testId = rotationToString(begin) + "_" + rotationToString(end);
+                        if (testIntent.getExtras() != null &&
+                                testIntent.getExtras().getBoolean(EXTRA_STARVE_UI_THREAD)) {
+                            testId += "_" + "BUSY_UI_THREAD";
+                        }
+                        params.add(new Object[]{testId, testIntent, begin, end});
+                    }
+                }
+            }
+        }
+        return params;
+    }
+
+    @Before
+    public void runTransition() {
+        String intentId = "";
+        if (mIntent.getExtras() != null &&
+                mIntent.getExtras().getBoolean(EXTRA_STARVE_UI_THREAD)) {
+            intentId = "BUSY_UI_THREAD";
+        }
+
+        super.runTransition(
+                changeAppRotation(mIntent, intentId, InstrumentationRegistry.getContext(),
+                        uiDevice, mBeginRotation, mEndRotation).repeat(5).build());
+    }
+
+    @Test
+    public void checkVisibility_navBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkPosition_navBarLayerRotatesAndScales() {
+        Rect startingPos = getNavigationBarPosition(mBeginRotation);
+        Rect endingPos = getNavigationBarPosition(mEndRotation);
+        if (startingPos.equals(endingPos)) {
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+                    .forAllEntries());
+        } else {
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+                    .inTheBeginning());
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos)
+                    .atTheEnd());
+        }
+    }
+
+    @Test
+    public void checkPosition_appLayerRotates() {
+        Rect startingPos = getAppPosition(mBeginRotation);
+        Rect endingPos = getAppPosition(mEndRotation);
+        if (startingPos.equals(endingPos)) {
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion(mIntent.getComponent().getPackageName(), startingPos)
+                    .forAllEntries());
+        } else {
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion(mIntent.getComponent().getPackageName(), startingPos)
+                    .then()
+                    .hasVisibleRegion(mIntent.getComponent().getPackageName(), endingPos)
+                    .forAllEntries());
+        }
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        Rect startingBounds = getDisplayBounds(mBeginRotation);
+        Rect endingBounds = getDisplayBounds(mEndRotation);
+        if (startingBounds.equals(endingBounds)) {
+            checkResults(result ->
+                    LayersTraceSubject.assertThat(result)
+                            .coversRegion(startingBounds)
+                            .forAllEntries());
+        } else {
+            checkResults(result ->
+                    LayersTraceSubject.assertThat(result)
+                            .coversRegion(startingBounds)
+                            .then()
+                            .coversRegion(endingBounds)
+                            .forAllEntries());
+        }
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
new file mode 100644
index 0000000..40bd4e9
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.splitScreenToLauncher;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test open app to split screen.
+ * To run this test: {@code atest FlickerTests:SplitScreenToLauncherTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class SplitScreenToLauncherTest extends FlickerTestBase {
+
+    public SplitScreenToLauncherTest() {
+        this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "SimpleApp");
+    }
+
+    @Before
+    public void runTransition() {
+        super.runTransition(splitScreenToLauncher(testApp, uiDevice).includeJankyRuns().build());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result ->
+                LayersTraceSubject.assertThat(result)
+                        .coversRegion(getDisplayBounds()).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_dividerLayerBecomesInVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(DOCKED_STACK_DIVIDER)
+                .then()
+                .hidesLayer(DOCKED_STACK_DIVIDER)
+                .forAllEntries());
+    }
+
+    @FlakyTest(bugId = 79686616)
+    @Test
+    public void checkVisibility_appLayerBecomesInVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(testApp.getPackage())
+                .then()
+                .hidesLayer(testApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_navBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_dividerWindowBecomesInVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
+                .then()
+                .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
+                .forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.java
new file mode 100644
index 0000000..79a0220
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.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.server.wm.flicker;
+
+import android.app.Instrumentation;
+import android.platform.helpers.AbstractStandardAppHelper;
+
+/**
+ * Class to take advantage of {@code IAppHelper} interface so the same test can be run against
+ * first party and third party apps.
+ */
+public class StandardAppHelper extends AbstractStandardAppHelper {
+    private final String mPackageName;
+    private final String mLauncherName;
+
+    public StandardAppHelper(Instrumentation instr, String packageName, String launcherName) {
+        super(instr);
+        mPackageName = packageName;
+        mLauncherName = launcherName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getPackage() {
+        return mPackageName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getLauncherName() {
+        return mLauncherName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void dismissInitialDialogs() {
+
+    }
+}
diff --git a/tests/FlickerTests/test-apps/Android.mk b/tests/FlickerTests/test-apps/Android.mk
new file mode 100644
index 0000000..9af9f444
--- /dev/null
+++ b/tests/FlickerTests/test-apps/Android.mk
@@ -0,0 +1,15 @@
+# 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 $(call all-subdir-makefiles)
diff --git a/tests/FlickerTests/test-apps/flickerapp/Android.mk b/tests/FlickerTests/test-apps/flickerapp/Android.mk
new file mode 100644
index 0000000..b916900
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := FlickerTestApp
+LOCAL_MODULE_TAGS := tests optional
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SDK_VERSION := current
+LOCAL_COMPATIBILITY_SUITE := device-tests
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := flickertestapplib
+LOCAL_MODULE_TAGS := tests optional
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
new file mode 100644
index 0000000..b694172
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -0,0 +1,64 @@
+<?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.server.wm.flicker.testapp">
+
+    <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="27"/>
+    <application
+        android:allowBackup="false"
+        android:supportsRtl="true">
+        <activity android:name=".SimpleActivity"
+                  android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
+                  android:label="SimpleApp">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <activity android:name=".ImeActivity"
+                  android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
+                  android:label="ImeApp">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <activity android:name=".PipActivity"
+                  android:resizeableActivity="true"
+                  android:supportsPictureInPicture="true"
+                  android:configChanges=
+                      "screenSize|smallestScreenSize|screenLayout|orientation"
+                  android:taskAffinity="com.android.server.wm.flicker.testapp.PipActivity"
+                  android:label="PipApp">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <activity android:name=".SeamlessRotationActivity"
+                  android:taskAffinity=
+                      "com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
+                  android:configChanges="orientation|screenSize"
+                  android:label="SeamlessApp">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
new file mode 100644
index 0000000..d5eb023
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/holo_green_light">
+    <EditText android:id="@+id/plain_text_input"
+              android:layout_height="wrap_content"
+              android:layout_width="match_parent"
+              android:inputType="text"/>
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
new file mode 100644
index 0000000..2c58d91
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/holo_blue_bright">
+    <Button android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/enter_pip"
+            android:text="Enter PIP"/>
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml
new file mode 100644
index 0000000..5d94e51
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/holo_orange_light">
+
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
new file mode 100644
index 0000000..1899411
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -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.server.wm.flicker.testapp;
+
+import android.content.ComponentName;
+
+public class ActivityOptions {
+    public static final String EXTRA_STARVE_UI_THREAD = "StarveUiThread";
+    public static final ComponentName SEAMLESS_ACTIVITY_COMPONENT_NAME =
+            new ComponentName("com.android.server.wm.flicker.testapp",
+                    "com.android.server.wm.flicker.testapp.SeamlessRotationActivity");
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java
new file mode 100644
index 0000000..df60460
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java
@@ -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.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class ImeActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        WindowManager.LayoutParams p = getWindow().getAttributes();
+        p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+                .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        getWindow().setAttributes(p);
+        setContentView(R.layout.activity_ime);
+    }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
new file mode 100644
index 0000000..9a8f399
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -0,0 +1,45 @@
+/*
+ * 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.flicker.testapp;
+
+import android.app.Activity;
+import android.app.PictureInPictureParams;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.Rational;
+import android.view.WindowManager;
+import android.widget.Button;
+
+public class PipActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        WindowManager.LayoutParams p = getWindow().getAttributes();
+        p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+                .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        getWindow().setAttributes(p);
+        setContentView(R.layout.activity_pip);
+        Button enterPip = (Button) findViewById(R.id.enter_pip);
+
+        PictureInPictureParams params = new PictureInPictureParams.Builder()
+                .setAspectRatio(new Rational(1, 1))
+                .setSourceRectHint(new Rect(0, 0, 100, 100))
+                .build();
+
+        enterPip.setOnClickListener((v) -> enterPictureInPictureMode(params));
+    }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
new file mode 100644
index 0000000..3a0c1c9
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
@@ -0,0 +1,72 @@
+/*
+ * 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.flicker.testapp;
+
+import static android.os.SystemClock.sleep;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+
+import static com.android.server.wm.flicker.testapp.ActivityOptions.EXTRA_STARVE_UI_THREAD;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.view.Window;
+import android.view.WindowManager;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class SeamlessRotationActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        enableSeamlessRotation();
+        setContentView(R.layout.activity_simple);
+        boolean starveUiThread = getIntent().getExtras() != null &&
+                getIntent().getExtras().getBoolean(EXTRA_STARVE_UI_THREAD);
+        if (starveUiThread) {
+            starveUiThread();
+        }
+    }
+
+    private void starveUiThread() {
+        Handler handler = new Handler(Looper.getMainLooper(), (Message unused) -> {
+            sleep(20);
+            return true;
+        });
+        new Timer().schedule(new TimerTask() {
+            @Override
+            public void run() {
+                handler.sendEmptyMessage(0);
+            }
+        }, 0, 21);
+    }
+
+    private void enableSeamlessRotation() {
+        WindowManager.LayoutParams p = getWindow().getAttributes();
+        p.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+        p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+                .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+                WindowManager.LayoutParams.FLAG_FULLSCREEN);
+        getWindow().setAttributes(p);
+    }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java
new file mode 100644
index 0000000..699abf8
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java
@@ -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.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class SimpleActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        WindowManager.LayoutParams p = getWindow().getAttributes();
+        p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+                .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        getWindow().setAttributes(p);
+        setContentView(R.layout.activity_simple);
+    }
+}
diff --git a/tests/Internal/res/xml/livewallpaper.xml b/tests/Internal/res/xml/livewallpaper.xml
index dbb0e47..6b5e84e 100644
--- a/tests/Internal/res/xml/livewallpaper.xml
+++ b/tests/Internal/res/xml/livewallpaper.xml
@@ -16,5 +16,4 @@
   -->
 <wallpaper
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    androidprv:supportsAmbientMode="true"/>
\ No newline at end of file
+    android:supportsAmbientMode="true"/>
\ No newline at end of file
diff --git a/tests/Internal/src/android/app/WallpaperInfoTest.java b/tests/Internal/src/android/app/WallpaperInfoTest.java
index 9d26270..98045ae 100644
--- a/tests/Internal/src/android/app/WallpaperInfoTest.java
+++ b/tests/Internal/src/android/app/WallpaperInfoTest.java
@@ -55,13 +55,13 @@
 
         // Defined as true in the XML
         assertTrue("supportsAmbientMode should be true, as defined in the XML.",
-                wallpaperInfo.getSupportsAmbientMode());
+                wallpaperInfo.supportsAmbientMode());
         Parcel parcel = Parcel.obtain();
         wallpaperInfo.writeToParcel(parcel, 0 /* flags */);
         parcel.setDataPosition(0);
         WallpaperInfo fromParcel = WallpaperInfo.CREATOR.createFromParcel(parcel);
         assertTrue("supportsAmbientMode should have been restored from parcelable",
-                fromParcel.getSupportsAmbientMode());
+                fromParcel.supportsAmbientMode());
         parcel.recycle();
     }
 }
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
index 1ae318a..653282d 100644
--- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -18,7 +18,9 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.ProcessErrorStateInfo;
 import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ActivityTaskManager;
 import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
 import android.app.UiAutomation;
 import android.content.Context;
 import android.content.Intent;
@@ -66,6 +68,7 @@
     private Map<String, String> mNameToResultKey;
     private Set<String> mPersistentProcesses;
     private IActivityManager mAm;
+    private IActivityTaskManager mAtm;
 
     @Override
     protected void setUp() throws Exception {
@@ -84,6 +87,7 @@
                 (MemoryUsageInstrumentation) getInstrumentation();
         Bundle args = instrumentation.getBundle();
         mAm = ActivityManager.getService();
+        mAtm = ActivityTaskManager.getService();
 
         createMappings();
         parseArgs(args);
@@ -316,7 +320,7 @@
                             UserHandle.USER_CURRENT);
                 }
 
-                mAm.startActivityAndWait(null, null, mLaunchIntent, mimeType,
+                mAtm.startActivityAndWait(null, null, mLaunchIntent, mimeType,
                         null, null, 0, mLaunchIntent.getFlags(), null, null,
                         UserHandle.USER_CURRENT_OR_SELF);
             } catch (RemoteException e) {
diff --git a/tests/SystemMemoryTest/Android.mk b/tests/SystemMemoryTest/Android.mk
new file mode 100644
index 0000000..09a1618
--- /dev/null
+++ b/tests/SystemMemoryTest/Android.mk
@@ -0,0 +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
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 $(call all-subdir-makefiles)
diff --git a/tests/SystemMemoryTest/README.txt b/tests/SystemMemoryTest/README.txt
new file mode 100644
index 0000000..de5042c
--- /dev/null
+++ b/tests/SystemMemoryTest/README.txt
@@ -0,0 +1,21 @@
+This directory contains a test for system server memory use.
+
+Directory structure
+===================
+device
+  - those parts of the test that run on device.
+
+host
+  - those parts of the test that run on host.
+
+Running the test
+================
+
+You can manually run the test as follows:
+
+  make tradefed-all system-memory-test SystemMemoryTestDevice
+  tradefed.sh run commandAndExit template/local_min --template:map test=system-memory-test
+
+This installs and runs the test on device. You can see the metrics in the
+tradefed output.
+
diff --git a/tests/SystemMemoryTest/device/Android.mk b/tests/SystemMemoryTest/device/Android.mk
new file mode 100644
index 0000000..75408df
--- /dev/null
+++ b/tests/SystemMemoryTest/device/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_PACKAGE_NAME := SystemMemoryTestDevice
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_COMPATIBILITY_SUITE := general-tests
+include $(BUILD_PACKAGE)
diff --git a/tests/SystemMemoryTest/device/AndroidManifest.xml b/tests/SystemMemoryTest/device/AndroidManifest.xml
new file mode 100644
index 0000000..e5e7844f
--- /dev/null
+++ b/tests/SystemMemoryTest/device/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?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.sysmem.device">
+
+    <uses-sdk android:minSdkVersion="19" />
+
+    <instrumentation
+            android:name="com.android.tests.sysmem.device.Cujs"
+            android:targetPackage="com.android.tests.sysmem.device" />
+
+    <application
+            android:allowBackup="false"
+            android:label="System Memory Test">
+    </application>
+</manifest>
diff --git a/tests/SystemMemoryTest/device/src/com/android/tests/sysmem/device/Cujs.java b/tests/SystemMemoryTest/device/src/com/android/tests/sysmem/device/Cujs.java
new file mode 100644
index 0000000..6c0c593
--- /dev/null
+++ b/tests/SystemMemoryTest/device/src/com/android/tests/sysmem/device/Cujs.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.sysmem.device;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Critical user journeys used to exercise the system for test, driven from
+ * the device.
+ */
+public class Cujs extends Instrumentation {
+
+    private static final String TAG = "SystemMemoryTest";
+
+    @Override
+    public void onCreate(Bundle arguments) {
+        start();
+    }
+
+    @Override
+    public void onStart() {
+        // TODO: Exercise the system in more interesting ways.
+        // Mostly what matters is that whatever we do here is sustainable: it
+        // can be repeated indefinitely on the system without causing
+        // problems.  For example, launching activities is fine. Installing
+        // applications is only fine if we also uninstall them as part of the
+        // test.
+        try {
+            Log.i(TAG, "Sleeping for 10 seconds...");
+            Thread.sleep(10 * 1000);
+        } catch (InterruptedException ignored) {
+        }
+
+        finish(Activity.RESULT_OK, null);
+    }
+}
diff --git a/tests/SystemMemoryTest/host/Android.mk b/tests/SystemMemoryTest/host/Android.mk
new file mode 100644
index 0000000..a516e38
--- /dev/null
+++ b/tests/SystemMemoryTest/host/Android.mk
@@ -0,0 +1,23 @@
+# 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)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE := system-memory-test
+LOCAL_MODULE_TAGS := optional
+LOCAL_JAVA_LIBRARIES := tradefed
+LOCAL_COMPATIBILITY_SUITE := general-tests
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tests/SystemMemoryTest/host/AndroidTest.xml b/tests/SystemMemoryTest/host/AndroidTest.xml
new file mode 100644
index 0000000..6d2c95f
--- /dev/null
+++ b/tests/SystemMemoryTest/host/AndroidTest.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 system memory tests">
+    <option name="test-suite-tag" value="system-memory-test" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="SystemMemoryTestDevice.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="class" value="com.android.tests.sysmem.host.MemoryTest" />
+    </test>
+</configuration>
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
new file mode 100644
index 0000000..579d972
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
@@ -0,0 +1,43 @@
+/*
+ * 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.sysmem.host;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+/**
+ * Critical user journeys with which to exercise the system, driven from the
+ * host.
+ */
+public class Cujs {
+    private ITestDevice device;
+
+    public Cujs(ITestDevice device) {
+        this.device = device;
+    }
+
+    /**
+     * Runs the critical user journeys.
+     */
+    public void run() throws DeviceNotAvailableException {
+        // Invoke the Device Cujs instrumentation to run the cujs.
+        // TODO: Consider exercising the system in other interesting ways as
+        // well.
+        String command = "am instrument -w com.android.tests.sysmem.device/.Cujs";
+        device.executeShellCommand(command);
+    }
+}
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java
new file mode 100644
index 0000000..bbec065
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.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.tests.sysmem.host;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
+import com.android.tradefed.testtype.IDeviceTest;
+import java.io.IOException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class MemoryTest implements IDeviceTest {
+
+    @Rule public TestMetrics testMetrics = new TestMetrics();
+    @Rule public TestLogData testLogs = new TestLogData();
+
+    private ITestDevice testDevice;
+    private int iterations = 0;     // Number of times cujs have been run.
+    private Metrics metrics;
+    private Cujs cujs;
+
+    @Override
+    public void setDevice(ITestDevice device) {
+        testDevice = device;
+        metrics = new Metrics(device, testMetrics, testLogs);
+        cujs = new Cujs(device);
+    }
+
+    @Override
+    public ITestDevice getDevice() {
+        return testDevice;
+    }
+
+    // Invoke a single iteration of running the cujs.
+    private void runCujs() throws DeviceNotAvailableException {
+        cujs.run();
+        iterations++;
+    }
+
+    // Sample desired memory.
+    private void sample()
+            throws DeviceNotAvailableException, IOException, Metrics.MetricsException {
+        metrics.sample(String.format("%03d", iterations));
+    }
+
+    @Test
+    public void run() throws Exception {
+        sample();   // Sample before running cujs
+        runCujs();
+        sample();   // Sample after first iteration of cujs
+
+        // Run cujs in a loop to highlight memory leaks.
+        for (int i = 0; i < 5; ++i) {
+            for (int j = 0; j < 5; j++) {
+                runCujs();
+            }
+            sample();
+        }
+    }
+}
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
new file mode 100644
index 0000000..7de092a
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
@@ -0,0 +1,148 @@
+/*
+ * 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.sysmem.host;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.FileInputStreamSource;
+import com.android.tradefed.result.LogDataType;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.InputMismatchException;
+import java.util.Scanner;
+
+/**
+ * Utilities for sampling and reporting memory metrics.
+ */
+class Metrics {
+
+    private ITestDevice device;
+    private TestMetrics metrics;
+    private TestLogData logs;
+
+    /**
+     * Exception thrown in case of error sampling metrics.
+     */
+    public static class MetricsException extends Exception {
+        public MetricsException(String msg) {
+            super(msg);
+        }
+
+        MetricsException(String msg, Exception cause) {
+            super(msg, cause);
+        }
+    }
+
+    /**
+     * Constructs a metrics instance that will output high level metrics and
+     * more detailed breakdowns using the given <code>metrics</code> and
+     * <code>logs</code> objects.
+     *
+     * @param device the device to sample metrics from
+     * @param metrics where to log the high level metrics when taking a sample
+     * @param logs where to log detailed breakdowns when taking a sample
+     */
+    public Metrics(ITestDevice device, TestMetrics metrics, TestLogData logs) {
+        this.device = device;
+        this.metrics = metrics;
+        this.logs = logs;
+    }
+
+    /**
+     * Returns the pid for the process with the given name.
+     */
+    private int getPidForProcess(String name)
+            throws DeviceNotAvailableException, IOException, MetricsException {
+        String psout = device.executeShellCommand("ps -A -o PID,CMD");
+        Scanner sc = new Scanner(psout);
+        try {
+            // ps output is of the form:
+            //  PID CMD            
+            //    1 init
+            //    2 kthreadd
+            //    ...
+            // 9693 ps
+            sc.nextLine();
+            while (sc.hasNextLine()) {
+                int pid = sc.nextInt();
+                String cmd = sc.next();
+
+                if (name.equals(cmd)) {
+                    return pid;
+                }
+            }
+        } catch (InputMismatchException e) {
+            throw new MetricsException("unexpected ps output format: " + psout, e);
+        }
+
+        throw new MetricsException("failed to get pid for process " + name);
+    }
+
+    /**
+     * Samples the current memory use on the system. Outputs high level test
+     * metrics and detailed breakdowns to the TestMetrics and TestLogData
+     * objects provided when constructing this Metrics instance. The metrics
+     * and log names are prefixed with the given label.
+     *
+     * @param label prefix to use for metrics and logs output for this sample.
+     */
+    void sample(String label) throws DeviceNotAvailableException, IOException, MetricsException {
+        // adb root access is required to get showmap
+        device.enableAdbRoot();
+
+        int pid = getPidForProcess("system_server");
+
+        // Read showmap for system server and add it as a test log
+        String showmap = device.executeShellCommand("showmap " + pid);
+        String showmapLabel = label + ".system_server.showmap";
+        File file = File.createTempFile(showmapLabel, "txt");
+        PrintStream ps = new PrintStream(file);
+        ps.print(showmap);
+        try (FileInputStreamSource dataStream = new FileInputStreamSource(file)) {
+            logs.addTestLog(showmapLabel, LogDataType.TEXT, dataStream);
+        }
+
+        // Extract VSS, PSS and RSS from the showmap and output them as metrics.
+        // The last lines of the showmap output looks something like:
+        // virtual                     shared   shared  private  private
+        //    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS   # object
+        //-------- -------- -------- -------- -------- -------- -------- -------- -------- ---- ------------------------------
+        //  928480   113016    24860    87348     7916     3632    14120     1968     1968 1900 TOTAL
+        try {
+            int pos = showmap.lastIndexOf("----");
+            Scanner sc = new Scanner(showmap.substring(pos));
+            sc.next();
+            long vss = sc.nextLong();
+            long rss = sc.nextLong();
+            long pss = sc.nextLong();
+
+            metrics.addTestMetric(String.format("%s.system_server.vss", label), Long.toString(vss));
+            metrics.addTestMetric(String.format("%s.system_server.rss", label), Long.toString(rss));
+            metrics.addTestMetric(String.format("%s.system_server.pss", label), Long.toString(pss));
+        } catch (InputMismatchException e) {
+            throw new MetricsException("unexpected showmap format", e);
+        }
+
+        // TODO: Experiment with other additional metrics.
+
+        // TODO: Consider launching an instrumentation to collect metrics from
+        // within the device itself.
+    }
+}
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
index b763c78..b4b5ca7 100644
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
@@ -30,6 +30,7 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
+import android.os.Trace;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -42,6 +43,7 @@
 
     public TouchLatencyView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        Trace.beginSection("TouchLatencyView constructor");
         setOnTouchListener(this);
         setWillNotDraw(false);
         mBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -63,48 +65,56 @@
         mBallY = 100.0f;
         mVelocityX = 7.0f;
         mVelocityY = 7.0f;
+        Trace.endSection();
     }
 
     @Override
     public boolean onTouch(View view, MotionEvent event) {
+        Trace.beginSection("TouchLatencyView onTouch");
         int action = event.getActionMasked();
         if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
             mTouching = true;
             invalidate();
+
+            mTouchX = event.getX();
+            mTouchY = event.getY();
         } else if (action == MotionEvent.ACTION_UP) {
             mTouching = false;
             invalidate();
-            return true;
-        } else {
-            return true;
         }
-        mTouchX = event.getX();
-        mTouchY = event.getY();
+        Trace.endSection();
         return true;
     }
 
     private void drawTouch(Canvas canvas) {
-        if (!mTouching) {
-            Log.d(LOG_TAG, "Filling background");
+        Trace.beginSection("TouchLatencyView drawTouch");
+
+        try {
+            if (!mTouching) {
+                Log.d(LOG_TAG, "Filling background");
+                canvas.drawColor(BACKGROUND_COLOR);
+                return;
+            }
+
+            float deltaX = (mTouchX - mLastDrawnX);
+            float deltaY = (mTouchY - mLastDrawnY);
+            float scaleFactor = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 1.5f;
+
+            mLastDrawnX = mTouchX;
+            mLastDrawnY = mTouchY;
+
             canvas.drawColor(BACKGROUND_COLOR);
-            return;
+            canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 3 * scaleFactor, mRedPaint);
+            canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 2 * scaleFactor, mYellowPaint);
+            canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + scaleFactor, mGreenPaint);
+            canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS, mBluePaint);
+        } finally {
+            Trace.endSection();
         }
-
-        float deltaX = (mTouchX - mLastDrawnX);
-        float deltaY = (mTouchY - mLastDrawnY);
-        float scaleFactor = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 1.5f;
-
-        mLastDrawnX = mTouchX;
-        mLastDrawnY = mTouchY;
-
-        canvas.drawColor(BACKGROUND_COLOR);
-        canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 3 * scaleFactor, mRedPaint);
-        canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 2 * scaleFactor, mYellowPaint);
-        canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + scaleFactor, mGreenPaint);
-        canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS, mBluePaint);
     }
 
     private void drawBall(Canvas canvas) {
+        Trace.beginSection("TouchLatencyView drawBall");
         int width = canvas.getWidth();
         int height = canvas.getHeight();
 
@@ -141,25 +151,29 @@
         canvas.drawColor(BACKGROUND_COLOR);
         canvas.drawOval(left, top, right, bottom, mYellowPaint);
         invalidate();
+        Trace.endSection();
     }
 
     @Override
     protected void onDraw(Canvas canvas) {
         super.onDraw(canvas);
-
+        Trace.beginSection("TouchLatencyView onDraw");
         if (mMode == 0) {
             drawTouch(canvas);
         } else {
             drawBall(canvas);
         }
+        Trace.endSection();
     }
 
     public void changeMode(MenuItem item) {
+        Trace.beginSection("TouchLatencyView changeMode");
         final int NUM_MODES = 2;
         final String modes[] = {"Touch", "Ball"};
         mMode = (mMode + 1) % NUM_MODES;
         invalidate();
         item.setTitle(modes[mMode]);
+        Trace.endSection();
     }
 
     private Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint;
@@ -178,21 +192,26 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        Trace.beginSection("TouchLatencyActivity onCreate");
         setContentView(R.layout.activity_touch_latency);
 
         mTouchView = findViewById(R.id.canvasView);
+        Trace.endSection();
     }
 
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
+        Trace.beginSection("TouchLatencyActivity onCreateOptionsMenu");
         // Inflate the menu; this adds items to the action bar if it is present.
         getMenuInflater().inflate(R.menu.menu_touch_latency, menu);
+        Trace.endSection();
         return true;
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
+        Trace.beginSection("TouchLatencyActivity onOptionsItemSelected");
         // Handle action bar item clicks here. The action bar will
         // automatically handle clicks on the Home/Up button, so long
         // as you specify a parent activity in AndroidManifest.xml.
@@ -203,6 +222,7 @@
             mTouchView.changeMode(item);
         }
 
+        Trace.endSection();
         return super.onOptionsItemSelected(item);
     }
 
diff --git a/tests/UsageStatsPerfTests/Android.mk b/tests/UsageStatsPerfTests/Android.mk
new file mode 100644
index 0000000..cd29b51
--- /dev/null
+++ b/tests/UsageStatsPerfTests/Android.mk
@@ -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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    apct-perftests-utils \
+    services.usage
+
+LOCAL_PACKAGE_NAME := UsageStatsPerfTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+# For android.permission.FORCE_STOP_PACKAGES permission
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/tests/UsageStatsPerfTests/AndroidManifest.xml b/tests/UsageStatsPerfTests/AndroidManifest.xml
new file mode 100644
index 0000000..596a79c
--- /dev/null
+++ b/tests/UsageStatsPerfTests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.frameworks.perftests.usage">
+    <uses-sdk
+            android:minSdkVersion="21" />
+    <uses-permission android:name="android.permission.DUMP" />
+    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.frameworks.perftests.usage"/>
+</manifest>
diff --git a/tests/UsageStatsPerfTests/AndroidTest.xml b/tests/UsageStatsPerfTests/AndroidTest.xml
new file mode 100644
index 0000000..c9b51dc
--- /dev/null
+++ b/tests/UsageStatsPerfTests/AndroidTest.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.
+-->
+<configuration description="Runs UsageStats Performance Tests">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="UsageStatsPerfTests.apk"/>
+        <option name="cleanup-apks" value="true"/>
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="UsageStatsPerfTests"/>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.frameworks.perftests.usage"/>
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
new file mode 100644
index 0000000..8467bee
--- /dev/null
+++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.frameworks.perftests.usage.tests;
+
+import static junit.framework.Assert.assertEquals;
+
+import com.android.server.usage.UsageStatsDatabase;
+import com.android.server.usage.UsageStatsDatabase.StatCombiner;
+import com.android.server.usage.IntervalStats;
+
+import android.app.usage.EventList;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.os.SystemClock;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.PerfManualStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class UsageStatsDatabasePerfTest {
+    protected static Context sContext;
+    private static UsageStatsDatabase sUsageStatsDatabase;
+    private static File mTestDir;
+
+    // Represents how many apps might have used in a day by a user with a few apps
+    final static int FEW_PKGS = 10;
+    // Represent how many apps might have used in a day by a user with many apps
+    final static int MANY_PKGS = 50;
+    // Represents how many usage events per app a device might have with light usage
+    final static int LIGHT_USE = 10;
+    // Represents how many usage events per app a device might have with heavy usage
+    final static int HEAVY_USE = 50;
+
+    private static final StatCombiner<UsageEvents.Event> sUsageStatsCombiner =
+            new StatCombiner<UsageEvents.Event>() {
+                @Override
+                public void combine(IntervalStats stats, boolean mutable,
+                        List<UsageEvents.Event> accResult) {
+                    final int size = stats.events.size();
+                    for (int i = 0; i < size; i++) {
+                        accResult.add(stats.events.get(i));
+                    }
+                }
+            };
+
+
+    @Rule
+    public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter();
+
+    @BeforeClass
+    public static void setUpOnce() {
+        sContext = InstrumentationRegistry.getTargetContext();
+        mTestDir = new File(sContext.getFilesDir(), "UsageStatsDatabasePerfTest");
+        sUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
+        sUsageStatsDatabase.init(1);
+    }
+
+    private static void populateIntervalStats(IntervalStats intervalStats, int packageCount,
+            int eventsPerPackage) {
+        if (intervalStats.events == null) {
+            intervalStats.events = new EventList();
+        }
+        for (int pkg = 0; pkg < packageCount; pkg++) {
+            UsageEvents.Event event = new UsageEvents.Event();
+            event.mPackage = "fake.package.name" + pkg;
+            event.mTimeStamp = 1;
+            event.mEventType = UsageEvents.Event.MOVE_TO_FOREGROUND;
+            for (int evt = 0; evt < eventsPerPackage; evt++) {
+                intervalStats.events.insert(event);
+                intervalStats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+            }
+        }
+    }
+
+    private static void clearUsageStatsFiles() {
+        File[] intervalDirs = mTestDir.listFiles();
+        for (File intervalDir : intervalDirs) {
+            if (intervalDir.isDirectory()) {
+                File[] usageFiles = intervalDir.listFiles();
+                for (File f : usageFiles) {
+                    f.delete();
+                }
+            }
+        }
+    }
+
+    private void runQueryUsageStatsTest(int packageCount, int eventsPerPackage) throws IOException {
+        final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+        IntervalStats intervalStats = new IntervalStats();
+        populateIntervalStats(intervalStats, packageCount, eventsPerPackage);
+        sUsageStatsDatabase.putUsageStats(0, intervalStats);
+        long elapsedTimeNs = 0;
+        while (benchmarkState.keepRunning(elapsedTimeNs)) {
+            final long startTime = SystemClock.elapsedRealtimeNanos();
+            List<UsageEvents.Event> temp = sUsageStatsDatabase.queryUsageStats(
+                    UsageStatsManager.INTERVAL_DAILY, 0, 2, sUsageStatsCombiner);
+            final long endTime = SystemClock.elapsedRealtimeNanos();
+            elapsedTimeNs = endTime - startTime;
+            assertEquals(packageCount * eventsPerPackage, temp.size());
+        }
+    }
+
+    private void runPutUsageStatsTest(int packageCount, int eventsPerPackage) throws IOException {
+        final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+        IntervalStats intervalStats = new IntervalStats();
+        populateIntervalStats(intervalStats, packageCount, eventsPerPackage);
+        long elapsedTimeNs = 0;
+        while (benchmarkState.keepRunning(elapsedTimeNs)) {
+            final long startTime = SystemClock.elapsedRealtimeNanos();
+            sUsageStatsDatabase.putUsageStats(0, intervalStats);
+            final long endTime = SystemClock.elapsedRealtimeNanos();
+            elapsedTimeNs = endTime - startTime;
+            clearUsageStatsFiles();
+        }
+    }
+
+    @Test
+    public void testQueryUsageStats_FewPkgsLightUse() throws IOException {
+        runQueryUsageStatsTest(FEW_PKGS, LIGHT_USE);
+    }
+
+    @Test
+    public void testPutUsageStats_FewPkgsLightUse() throws IOException {
+        runPutUsageStatsTest(FEW_PKGS, LIGHT_USE);
+    }
+
+    @Test
+    public void testQueryUsageStats_FewPkgsHeavyUse() throws IOException {
+        runQueryUsageStatsTest(FEW_PKGS, HEAVY_USE);
+    }
+
+    @Test
+    public void testPutUsageStats_FewPkgsHeavyUse() throws IOException {
+        runPutUsageStatsTest(FEW_PKGS, HEAVY_USE);
+    }
+
+    @Test
+    public void testQueryUsageStats_ManyPkgsLightUse() throws IOException {
+        runQueryUsageStatsTest(MANY_PKGS, LIGHT_USE);
+    }
+
+    @Test
+    public void testPutUsageStats_ManyPkgsLightUse() throws IOException {
+        runPutUsageStatsTest(MANY_PKGS, LIGHT_USE);
+    }
+
+    @Test
+    public void testQueryUsageStats_ManyPkgsHeavyUse() throws IOException {
+        runQueryUsageStatsTest(MANY_PKGS, HEAVY_USE);
+    }
+
+    @Test
+    public void testPutUsageStats_ManyPkgsHeavyUse() throws IOException {
+        runPutUsageStatsTest(MANY_PKGS, HEAVY_USE);
+    }
+}
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 6cc3dd3..e529b93 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -34,7 +34,6 @@
 # These are not normally accessible from apps so they must be explicitly included.
 LOCAL_JNI_SHARED_LIBRARIES := \
     android.hidl.token@1.0 \
-    $(UBSAN_RUNTIME_LIBRARY) \
     libartbase \
     libbacktrace \
     libbase \
diff --git a/tests/net/OWNERS b/tests/net/OWNERS
index ce50558..7311eee 100644
--- a/tests/net/OWNERS
+++ b/tests/net/OWNERS
@@ -1,6 +1,8 @@
 set noparent
 
+codewiz@google.com
 ek@google.com
 jchalard@google.com
 lorenzo@google.com
+reminv@google.com
 satk@google.com
diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
index a5ee8e3..2b172da 100644
--- a/tests/net/java/android/net/NetworkUtilsTest.java
+++ b/tests/net/java/android/net/NetworkUtilsTest.java
@@ -16,17 +16,32 @@
 
 package android.net;
 
-import android.net.NetworkUtils;
-import android.test.suitebuilder.annotation.SmallTest;
+import static android.net.NetworkUtils.getImplicitNetmask;
+import static android.net.NetworkUtils.inet4AddressToIntHTH;
+import static android.net.NetworkUtils.inet4AddressToIntHTL;
+import static android.net.NetworkUtils.intToInet4AddressHTH;
+import static android.net.NetworkUtils.intToInet4AddressHTL;
+import static android.net.NetworkUtils.netmaskToPrefixLength;
+import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTH;
+import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTL;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.fail;
+
+import android.support.test.runner.AndroidJUnit4;
 
 import java.math.BigInteger;
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.util.TreeSet;
 
-import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class NetworkUtilsTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@android.support.test.filters.SmallTest
+public class NetworkUtilsTest {
 
     private InetAddress Address(String addr) {
         return InetAddress.parseNumericAddress(addr);
@@ -36,41 +51,126 @@
         return (Inet4Address) Address(addr);
     }
 
-    @SmallTest
+    @Test
     public void testGetImplicitNetmask() {
-        assertEquals(8, NetworkUtils.getImplicitNetmask(IPv4Address("4.2.2.2")));
-        assertEquals(8, NetworkUtils.getImplicitNetmask(IPv4Address("10.5.6.7")));
-        assertEquals(16, NetworkUtils.getImplicitNetmask(IPv4Address("173.194.72.105")));
-        assertEquals(16, NetworkUtils.getImplicitNetmask(IPv4Address("172.23.68.145")));
-        assertEquals(24, NetworkUtils.getImplicitNetmask(IPv4Address("192.0.2.1")));
-        assertEquals(24, NetworkUtils.getImplicitNetmask(IPv4Address("192.168.5.1")));
-        assertEquals(32, NetworkUtils.getImplicitNetmask(IPv4Address("224.0.0.1")));
-        assertEquals(32, NetworkUtils.getImplicitNetmask(IPv4Address("255.6.7.8")));
+        assertEquals(8, getImplicitNetmask(IPv4Address("4.2.2.2")));
+        assertEquals(8, getImplicitNetmask(IPv4Address("10.5.6.7")));
+        assertEquals(16, getImplicitNetmask(IPv4Address("173.194.72.105")));
+        assertEquals(16, getImplicitNetmask(IPv4Address("172.23.68.145")));
+        assertEquals(24, getImplicitNetmask(IPv4Address("192.0.2.1")));
+        assertEquals(24, getImplicitNetmask(IPv4Address("192.168.5.1")));
+        assertEquals(32, getImplicitNetmask(IPv4Address("224.0.0.1")));
+        assertEquals(32, getImplicitNetmask(IPv4Address("255.6.7.8")));
     }
 
     private void assertInvalidNetworkMask(Inet4Address addr) {
         try {
-            NetworkUtils.netmaskToPrefixLength(addr);
+            netmaskToPrefixLength(addr);
             fail("Invalid netmask " + addr.getHostAddress() + " did not cause exception");
         } catch (IllegalArgumentException expected) {
         }
     }
 
-    @SmallTest
+    @Test
+    public void testInet4AddressToIntHTL() {
+        assertEquals(0, inet4AddressToIntHTL(IPv4Address("0.0.0.0")));
+        assertEquals(0x000080ff, inet4AddressToIntHTL(IPv4Address("255.128.0.0")));
+        assertEquals(0x0080ff0a, inet4AddressToIntHTL(IPv4Address("10.255.128.0")));
+        assertEquals(0x00feff0a, inet4AddressToIntHTL(IPv4Address("10.255.254.0")));
+        assertEquals(0xfeffa8c0, inet4AddressToIntHTL(IPv4Address("192.168.255.254")));
+        assertEquals(0xffffa8c0, inet4AddressToIntHTL(IPv4Address("192.168.255.255")));
+    }
+
+    @Test
+    public void testIntToInet4AddressHTL() {
+        assertEquals(IPv4Address("0.0.0.0"), intToInet4AddressHTL(0));
+        assertEquals(IPv4Address("255.128.0.0"), intToInet4AddressHTL(0x000080ff));
+        assertEquals(IPv4Address("10.255.128.0"), intToInet4AddressHTL(0x0080ff0a));
+        assertEquals(IPv4Address("10.255.254.0"), intToInet4AddressHTL(0x00feff0a));
+        assertEquals(IPv4Address("192.168.255.254"), intToInet4AddressHTL(0xfeffa8c0));
+        assertEquals(IPv4Address("192.168.255.255"), intToInet4AddressHTL(0xffffa8c0));
+    }
+
+    @Test
+    public void testInet4AddressToIntHTH() {
+        assertEquals(0, inet4AddressToIntHTH(IPv4Address("0.0.0.0")));
+        assertEquals(0xff800000, inet4AddressToIntHTH(IPv4Address("255.128.0.0")));
+        assertEquals(0x0aff8000, inet4AddressToIntHTH(IPv4Address("10.255.128.0")));
+        assertEquals(0x0afffe00, inet4AddressToIntHTH(IPv4Address("10.255.254.0")));
+        assertEquals(0xc0a8fffe, inet4AddressToIntHTH(IPv4Address("192.168.255.254")));
+        assertEquals(0xc0a8ffff, inet4AddressToIntHTH(IPv4Address("192.168.255.255")));
+    }
+
+    @Test
+    public void testIntToInet4AddressHTH() {
+        assertEquals(IPv4Address("0.0.0.0"), intToInet4AddressHTH(0));
+        assertEquals(IPv4Address("255.128.0.0"), intToInet4AddressHTH(0xff800000));
+        assertEquals(IPv4Address("10.255.128.0"), intToInet4AddressHTH(0x0aff8000));
+        assertEquals(IPv4Address("10.255.254.0"), intToInet4AddressHTH(0x0afffe00));
+        assertEquals(IPv4Address("192.168.255.254"), intToInet4AddressHTH(0xc0a8fffe));
+        assertEquals(IPv4Address("192.168.255.255"), intToInet4AddressHTH(0xc0a8ffff));
+    }
+
+    @Test
     public void testNetmaskToPrefixLength() {
-        assertEquals(0, NetworkUtils.netmaskToPrefixLength(IPv4Address("0.0.0.0")));
-        assertEquals(9, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.128.0.0")));
-        assertEquals(17, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.255.128.0")));
-        assertEquals(23, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.255.254.0")));
-        assertEquals(31, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.255.255.254")));
-        assertEquals(32, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.255.255.255")));
+        assertEquals(0, netmaskToPrefixLength(IPv4Address("0.0.0.0")));
+        assertEquals(9, netmaskToPrefixLength(IPv4Address("255.128.0.0")));
+        assertEquals(17, netmaskToPrefixLength(IPv4Address("255.255.128.0")));
+        assertEquals(23, netmaskToPrefixLength(IPv4Address("255.255.254.0")));
+        assertEquals(31, netmaskToPrefixLength(IPv4Address("255.255.255.254")));
+        assertEquals(32, netmaskToPrefixLength(IPv4Address("255.255.255.255")));
 
         assertInvalidNetworkMask(IPv4Address("0.0.0.1"));
         assertInvalidNetworkMask(IPv4Address("255.255.255.253"));
         assertInvalidNetworkMask(IPv4Address("255.255.0.255"));
     }
 
-    @SmallTest
+
+    @Test
+    public void testPrefixLengthToV4NetmaskIntHTL() {
+        assertEquals(0, prefixLengthToV4NetmaskIntHTL(0));
+        assertEquals(0x000080ff /* 255.128.0.0 */, prefixLengthToV4NetmaskIntHTL(9));
+        assertEquals(0x0080ffff /* 255.255.128.0 */, prefixLengthToV4NetmaskIntHTL(17));
+        assertEquals(0x00feffff /* 255.255.254.0 */, prefixLengthToV4NetmaskIntHTL(23));
+        assertEquals(0xfeffffff /* 255.255.255.254 */, prefixLengthToV4NetmaskIntHTL(31));
+        assertEquals(0xffffffff /* 255.255.255.255 */, prefixLengthToV4NetmaskIntHTL(32));
+    }
+
+    @Test
+    public void testPrefixLengthToV4NetmaskIntHTH() {
+        assertEquals(0, prefixLengthToV4NetmaskIntHTH(0));
+        assertEquals(0xff800000 /* 255.128.0.0 */, prefixLengthToV4NetmaskIntHTH(9));
+        assertEquals(0xffff8000 /* 255.255.128.0 */, prefixLengthToV4NetmaskIntHTH(17));
+        assertEquals(0xfffffe00 /* 255.255.254.0 */, prefixLengthToV4NetmaskIntHTH(23));
+        assertEquals(0xfffffffe /* 255.255.255.254 */, prefixLengthToV4NetmaskIntHTH(31));
+        assertEquals(0xffffffff /* 255.255.255.255 */, prefixLengthToV4NetmaskIntHTH(32));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testPrefixLengthToV4NetmaskIntHTH_NegativeLength() {
+        prefixLengthToV4NetmaskIntHTH(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testPrefixLengthToV4NetmaskIntHTH_LengthTooLarge() {
+        prefixLengthToV4NetmaskIntHTH(33);
+    }
+
+    private void checkAddressMasking(String expectedAddr, String addr, int prefixLength) {
+        final int prefix = prefixLengthToV4NetmaskIntHTH(prefixLength);
+        final int addrInt = inet4AddressToIntHTH(IPv4Address(addr));
+        assertEquals(IPv4Address(expectedAddr), intToInet4AddressHTH(prefix & addrInt));
+    }
+
+    @Test
+    public void testPrefixLengthToV4NetmaskIntHTH_MaskAddr() {
+        checkAddressMasking("192.168.0.0", "192.168.128.1", 16);
+        checkAddressMasking("255.240.0.0", "255.255.255.255", 12);
+        checkAddressMasking("255.255.255.255", "255.255.255.255", 32);
+        checkAddressMasking("0.0.0.0", "255.255.255.255", 0);
+    }
+
+    @Test
     public void testRoutedIPv4AddressCount() {
         final TreeSet<IpPrefix> set = new TreeSet<>(IpPrefix.lengthComparator());
         // No routes routes to no addresses.
@@ -118,7 +218,7 @@
         assertEquals(7l - 4 + 4 + 16 + 65536, NetworkUtils.routedIPv4AddressCount(set));
     }
 
-    @SmallTest
+    @Test
     public void testRoutedIPv6AddressCount() {
         final TreeSet<IpPrefix> set = new TreeSet<>(IpPrefix.lengthComparator());
         // No routes routes to no addresses.
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 0248e97..9838020 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -314,9 +314,9 @@
 
         // Test multiply.
         gen = new ApfGenerator(MIN_APF_VERSION);
-        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addLoadImmediate(Register.R0, 123456789);
         gen.addMul(2);
-        gen.addJumpIfR0Equals(1234567890 * 2, gen.DROP_LABEL);
+        gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL);
         assertDrop(gen);
 
         // Test divide.
@@ -379,10 +379,10 @@
 
         // Test multiply.
         gen = new ApfGenerator(MIN_APF_VERSION);
-        gen.addLoadImmediate(Register.R0, 1234567890);
+        gen.addLoadImmediate(Register.R0, 123456789);
         gen.addLoadImmediate(Register.R1, 2);
         gen.addMulR1();
-        gen.addJumpIfR0Equals(1234567890 * 2, gen.DROP_LABEL);
+        gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL);
         assertDrop(gen);
 
         // Test divide.
diff --git a/tests/net/java/android/net/ip/IpClientTest.java b/tests/net/java/android/net/ip/IpClientTest.java
index 89453e0..5a8d2cd 100644
--- a/tests/net/java/android/net/ip/IpClientTest.java
+++ b/tests/net/java/android/net/ip/IpClientTest.java
@@ -32,7 +32,6 @@
 import static org.mockito.Mockito.when;
 
 import android.app.AlarmManager;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.INetd;
@@ -62,8 +61,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.net.Inet4Address;
-import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.Arrays;
 import java.util.List;
@@ -84,6 +81,7 @@
     private static final int TEST_IFINDEX = 1001;
     // See RFC 7042#section-2.1.2 for EUI-48 documentation values.
     private static final MacAddress TEST_MAC = MacAddress.fromString("00:00:5E:00:53:01");
+    private static final int TEST_TIMEOUT_MS = 200;
 
     @Mock private Context mContext;
     @Mock private INetworkManagementService mNMService;
@@ -126,8 +124,8 @@
     private IpClient makeIpClient(String ifname) throws Exception {
         setTestInterfaceParams(ifname);
         final IpClient ipc = new IpClient(mContext, ifname, mCb, mDependecies);
-        verify(mNMService, timeout(100).times(1)).disableIpv6(ifname);
-        verify(mNMService, timeout(100).times(1)).clearInterfaceAddresses(ifname);
+        verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).disableIpv6(ifname);
+        verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).clearInterfaceAddresses(ifname);
         ArgumentCaptor<BaseNetworkObserver> arg =
                 ArgumentCaptor.forClass(BaseNetworkObserver.class);
         verify(mNMService, times(1)).registerObserver(arg.capture());
@@ -200,13 +198,13 @@
 
         ipc.startProvisioning(config);
         verify(mCb, times(1)).setNeighborDiscoveryOffload(true);
-        verify(mCb, timeout(100).times(1)).setFallbackMulticastFilter(false);
+        verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setFallbackMulticastFilter(false);
         verify(mCb, never()).onProvisioningFailure(any());
 
         ipc.shutdown();
-        verify(mNMService, timeout(100).times(1)).disableIpv6(iface);
-        verify(mNMService, timeout(100).times(1)).clearInterfaceAddresses(iface);
-        verify(mCb, timeout(100).times(1))
+        verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).disableIpv6(iface);
+        verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).clearInterfaceAddresses(iface);
+        verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
                 .onLinkPropertiesChange(eq(makeEmptyLinkProperties(iface)));
     }
 
@@ -230,12 +228,12 @@
 
         ipc.startProvisioning(config);
         verify(mCb, times(1)).setNeighborDiscoveryOffload(true);
-        verify(mCb, timeout(100).times(1)).setFallbackMulticastFilter(false);
+        verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setFallbackMulticastFilter(false);
         verify(mCb, never()).onProvisioningFailure(any());
 
         for (String addr : addresses) {
             String[] parts = addr.split("/");
-            verify(mNetd, timeout(100).times(1))
+            verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1))
                     .interfaceAddAddress(iface, parts[0], Integer.parseInt(parts[1]));
         }
 
@@ -244,7 +242,7 @@
         // Add N - 1 addresses
         for (int i = 0; i < lastAddr; i++) {
             mObserver.addressUpdated(iface, new LinkAddress(addresses[i]));
-            verify(mCb, timeout(100)).onLinkPropertiesChange(any());
+            verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(any());
             reset(mCb);
         }
 
@@ -252,12 +250,12 @@
         mObserver.addressUpdated(iface, new LinkAddress(addresses[lastAddr]));
         LinkProperties want = linkproperties(links(addresses), routes(prefixes));
         want.setInterfaceName(iface);
-        verify(mCb, timeout(100).times(1)).onProvisioningSuccess(eq(want));
+        verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(eq(want));
 
         ipc.shutdown();
-        verify(mNMService, timeout(100).times(1)).disableIpv6(iface);
-        verify(mNMService, timeout(100).times(1)).clearInterfaceAddresses(iface);
-        verify(mCb, timeout(100).times(1))
+        verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).disableIpv6(iface);
+        verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).clearInterfaceAddresses(iface);
+        verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
                 .onLinkPropertiesChange(eq(makeEmptyLinkProperties(iface)));
     }
 
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 2208580..e3db7e8 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -57,13 +57,12 @@
 
 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 static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.eq;
@@ -84,7 +83,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.net.CaptivePortal;
 import android.net.ConnectivityManager;
@@ -95,6 +93,7 @@
 import android.net.ConnectivityThread;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
+import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -112,7 +111,6 @@
 import android.net.RouteInfo;
 import android.net.StringNetworkSpecifier;
 import android.net.UidRange;
-import android.net.VpnService;
 import android.net.captiveportal.CaptivePortalProbeResult;
 import android.net.metrics.IpConnectivityLog;
 import android.net.util.MultinetworkPolicyTracker;
@@ -125,6 +123,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -145,6 +144,7 @@
 import com.android.server.connectivity.DnsManager;
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.MockableSystemProperties;
+import com.android.server.connectivity.Nat464Xlat;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkMonitor;
 import com.android.server.connectivity.Vpn;
@@ -161,10 +161,13 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
 
+import java.net.Inet4Address;
 import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -188,8 +191,18 @@
     private static final String TAG = "ConnectivityServiceTest";
 
     private static final int TIMEOUT_MS = 500;
-    private static final int TEST_LINGER_DELAY_MS = 120;
+    private static final int TEST_LINGER_DELAY_MS = 250;
+    // Chosen to be less than the linger timeout. This ensures that we can distinguish between a
+    // LOST callback that arrives immediately and a LOST callback that arrives after the linger
+    // timeout. For this, our assertions should run fast enough to leave less than
+    // (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
+    // supposedly fired, and the time we call expectCallback.
+    private final static int TEST_CALLBACK_TIMEOUT_MS = 200;
+    // Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to
+    // complete before callbacks are verified.
+    private final static int TEST_REQUEST_TIMEOUT_MS = 150;
 
+    private static final String CLAT_PREFIX = "v4-";
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
     private static final String WIFI_IFNAME = "test_wlan0";
 
@@ -867,7 +880,7 @@
                 NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
                 IpConnectivityLog log) {
             super(context, handler, networkAgentInfo, defaultRequest, log,
-                    NetworkMonitor.NetworkMonitorSettings.DEFAULT);
+                    NetworkMonitor.Dependencies.DEFAULT);
             connectivityHandler = handler;
         }
 
@@ -950,6 +963,10 @@
             return monitor;
         }
 
+        public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) {
+            return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
+        }
+
         @Override
         public MultinetworkPolicyTracker createMultinetworkPolicyTracker(
                 Context c, Handler h, Runnable r) {
@@ -1452,11 +1469,6 @@
      * received. assertNoCallback may be called at any time.
      */
     private class TestNetworkCallback extends NetworkCallback {
-        // Chosen to be much less than the linger timeout. This ensures that we can distinguish
-        // between a LOST callback that arrives immediately and a LOST callback that arrives after
-        // the linger timeout.
-        private final static int TIMEOUT_MS = 100;
-
         private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
         private Network mLastAvailableNetwork;
 
@@ -1532,20 +1544,20 @@
             if (state == CallbackState.LOSING) {
                 String msg = String.format(
                         "Invalid linger time value %d, must be between %d and %d",
-                        actual.arg, 0, TEST_LINGER_DELAY_MS);
+                        actual.arg, 0, mService.mLingerDelayMs);
                 int maxMsToLive = (Integer) actual.arg;
-                assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= TEST_LINGER_DELAY_MS);
+                assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= mService.mLingerDelayMs);
             }
 
             return actual;
         }
 
         CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent) {
-            return expectCallback(state, agent, TIMEOUT_MS);
+            return expectCallback(state, agent, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn) {
-            return expectCallbackLike(fn, TIMEOUT_MS);
+            return expectCallbackLike(fn, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn, int timeoutMs) {
@@ -1588,15 +1600,15 @@
 
         // Expects the available callbacks (validated), plus onSuspended.
         void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated) {
-            expectAvailableCallbacks(agent, true, expectValidated, TIMEOUT_MS);
+            expectAvailableCallbacks(agent, true, expectValidated, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         void expectAvailableCallbacksValidated(MockNetworkAgent agent) {
-            expectAvailableCallbacks(agent, false, true, TIMEOUT_MS);
+            expectAvailableCallbacks(agent, false, true, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) {
-            expectAvailableCallbacks(agent, false, false, TIMEOUT_MS);
+            expectAvailableCallbacks(agent, false, false, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         // Expects the available callbacks (where the onCapabilitiesChanged must contain the
@@ -1604,9 +1616,9 @@
         // one we just sent.
         // TODO: this is likely a bug. Fix it and remove this method.
         void expectAvailableDoubleValidatedCallbacks(MockNetworkAgent agent) {
-            expectCallback(CallbackState.AVAILABLE, agent, TIMEOUT_MS);
+            expectCallback(CallbackState.AVAILABLE, agent, TEST_CALLBACK_TIMEOUT_MS);
             NetworkCapabilities nc1 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
-            expectCallback(CallbackState.LINK_PROPERTIES, agent, TIMEOUT_MS);
+            expectCallback(CallbackState.LINK_PROPERTIES, agent, TEST_CALLBACK_TIMEOUT_MS);
             NetworkCapabilities nc2 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
             assertEquals(nc1, nc2);
         }
@@ -1620,7 +1632,7 @@
         }
 
         NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
-            return expectCapabilitiesWith(capability, agent, TIMEOUT_MS);
+            return expectCapabilitiesWith(capability, agent, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent,
@@ -1632,7 +1644,7 @@
         }
 
         NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
-            return expectCapabilitiesWithout(capability, agent, TIMEOUT_MS);
+            return expectCapabilitiesWithout(capability, agent, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent,
@@ -1756,6 +1768,12 @@
 
     @Test
     public void testMultipleLingering() {
+        // This test would be flaky with the default 120ms timer: that is short enough that
+        // lingered networks are torn down before assertions can be run. We don't want to mock the
+        // lingering timer to keep the WakeupMessage logic realistic: this has already proven useful
+        // in detecting races.
+        mService.mLingerDelayMs = 300;
+
         NetworkRequest request = new NetworkRequest.Builder()
                 .clearCapabilities().addCapability(NET_CAPABILITY_NOT_METERED)
                 .build();
@@ -1973,7 +1991,7 @@
 
         // Let linger run its course.
         callback.assertNoCallback();
-        final int lingerTimeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4;
+        final int lingerTimeoutMs = mService.mLingerDelayMs + mService.mLingerDelayMs / 4;
         callback.expectCallback(CallbackState.LOST, mCellNetworkAgent, lingerTimeoutMs);
 
         // Register a TRACK_DEFAULT request and check that it does not affect lingering.
@@ -3197,12 +3215,12 @@
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI).build();
         final TestNetworkCallback networkCallback = new TestNetworkCallback();
-        final int timeoutMs = 150;
-        mCm.requestNetwork(nr, networkCallback, timeoutMs);
+        mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS);
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, timeoutMs);
+        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+                TEST_CALLBACK_TIMEOUT_MS);
 
         // pass timeout and validate that UNAVAILABLE is not called
         networkCallback.assertNoCallback();
@@ -3217,13 +3235,12 @@
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI).build();
         final TestNetworkCallback networkCallback = new TestNetworkCallback();
-        final int requestTimeoutMs = 50;
-        mCm.requestNetwork(nr, networkCallback, requestTimeoutMs);
+        mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS);
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        final int assertTimeoutMs = 100;
-        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, assertTimeoutMs);
+        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+                TEST_CALLBACK_TIMEOUT_MS);
         mWiFiNetworkAgent.disconnect();
         networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
 
@@ -4422,4 +4439,97 @@
 
         mMockVpn.disconnect();
     }
+
+    /**
+     * Make simulated InterfaceConfig for Nat464Xlat to query clat lower layer info.
+     */
+    private InterfaceConfiguration getClatInterfaceConfig(LinkAddress la) {
+        InterfaceConfiguration cfg = new InterfaceConfiguration();
+        cfg.setHardwareAddress("11:22:33:44:55:66");
+        cfg.setLinkAddress(la);
+        return cfg;
+    }
+
+    /**
+     * Make expected stack link properties, copied from Nat464Xlat.
+     */
+    private LinkProperties makeClatLinkProperties(LinkAddress la) {
+        LinkAddress clatAddress = la;
+        LinkProperties stacked = new LinkProperties();
+        stacked.setInterfaceName(CLAT_PREFIX + MOBILE_IFNAME);
+        RouteInfo ipv4Default = new RouteInfo(
+                new LinkAddress(Inet4Address.ANY, 0),
+                clatAddress.getAddress(), CLAT_PREFIX + MOBILE_IFNAME);
+        stacked.addRoute(ipv4Default);
+        stacked.addLinkAddress(clatAddress);
+        return stacked;
+    }
+
+    @Test
+    public void testStackedLinkProperties() throws UnknownHostException, RemoteException {
+        final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
+        final LinkAddress myIpv6 = new LinkAddress("2001:db8:1::1/64");
+        final NetworkRequest networkRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        final TestNetworkCallback networkCallback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(networkRequest, networkCallback);
+
+        // Prepare ipv6 only link properties and connect.
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        final LinkProperties cellLp = new LinkProperties();
+        cellLp.setInterfaceName(MOBILE_IFNAME);
+        cellLp.addLinkAddress(myIpv6);
+        cellLp.addRoute(new RouteInfo((IpPrefix) null, myIpv6.getAddress(), MOBILE_IFNAME));
+        cellLp.addRoute(new RouteInfo(myIpv6, null, MOBILE_IFNAME));
+        reset(mNetworkManagementService);
+        when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
+                .thenReturn(getClatInterfaceConfig(myIpv4));
+
+        // Connect with ipv6 link properties, then expect clat setup ipv4 and update link
+        // properties properly.
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        mCellNetworkAgent.connect(true);
+        networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        verify(mNetworkManagementService, times(1)).startClatd(MOBILE_IFNAME);
+        Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
+
+        // Clat iface up, expect stack link updated.
+        clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
+        waitForIdle();
+        List<LinkProperties> stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork())
+                .getStackedLinks();
+        assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0));
+
+        // Change trivial linkproperties and see if stacked link is preserved.
+        cellLp.addDnsServer(InetAddress.getByName("8.8.8.8"));
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        waitForIdle();
+        networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+
+        List<LinkProperties> stackedLpsAfterChange =
+                mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getStackedLinks();
+        assertNotEquals(stackedLpsAfterChange, Collections.EMPTY_LIST);
+        assertEquals(makeClatLinkProperties(myIpv4), stackedLpsAfterChange.get(0));
+
+        // Add ipv4 address, expect stacked linkproperties be cleaned up
+        cellLp.addLinkAddress(myIpv4);
+        cellLp.addRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        waitForIdle();
+        networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        verify(mNetworkManagementService, times(1)).stopClatd(MOBILE_IFNAME);
+
+        // Clat iface removed, expect linkproperties revert to original one
+        clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME);
+        waitForIdle();
+        networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork());
+        assertEquals(cellLp, actualLpAfterIpv4);
+
+        // Clean up
+        mCellNetworkAgent.disconnect();
+        mCm.unregisterNetworkCallback(networkCallback);
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
index 27a897d..b017130 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
@@ -16,22 +16,35 @@
 
 package com.android.server.connectivity;
 
-import static org.junit.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
 import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
 import android.net.NetworkRequest;
+import android.net.captiveportal.CaptivePortalProbeResult;
 import android.net.metrics.IpConnectivityLog;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
+import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -39,38 +52,273 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.URL;
+import java.util.Random;
+
+import javax.net.ssl.SSLHandshakeException;
+
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class NetworkMonitorTest {
+    private static final String LOCATION_HEADER = "location";
 
-    static final int TEST_ID = 60; // should be less than min netid 100
+    private @Mock Context mContext;
+    private @Mock Handler mHandler;
+    private @Mock IpConnectivityLog mLogger;
+    private @Mock NetworkAgentInfo mAgent;
+    private @Mock NetworkInfo mNetworkInfo;
+    private @Mock NetworkRequest mRequest;
+    private @Mock TelephonyManager mTelephony;
+    private @Mock WifiManager mWifi;
+    private @Mock Network mNetwork;
+    private @Mock HttpURLConnection mHttpConnection;
+    private @Mock HttpURLConnection mHttpsConnection;
+    private @Mock HttpURLConnection mFallbackConnection;
+    private @Mock HttpURLConnection mOtherFallbackConnection;
+    private @Mock Random mRandom;
+    private @Mock NetworkMonitor.Dependencies mDependencies;
 
-    @Mock Context mContext;
-    @Mock Handler mHandler;
-    @Mock IpConnectivityLog mLogger;
-    @Mock NetworkAgentInfo mAgent;
-    @Mock NetworkMonitor.NetworkMonitorSettings mSettings;
-    @Mock NetworkRequest mRequest;
-    @Mock TelephonyManager mTelephony;
-    @Mock WifiManager mWifi;
+    private static final String TEST_HTTP_URL = "http://www.google.com/gen_204";
+    private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204";
+    private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204";
+    private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204";
 
     @Before
-    public void setUp() {
+    public void setUp() throws IOException {
         MockitoAnnotations.initMocks(this);
+        mAgent.linkProperties = new LinkProperties();
+        mAgent.networkCapabilities = new NetworkCapabilities()
+                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+        mAgent.networkInfo = mNetworkInfo;
 
-        when(mAgent.network()).thenReturn(new Network(TEST_ID));
+        when(mAgent.network()).thenReturn(mNetwork);
+        when(mDependencies.getNetwork(any())).thenReturn(mNetwork);
+        when(mDependencies.getRandom()).thenReturn(mRandom);
+        when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt()))
+                .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
+        when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_USE_HTTPS),
+                anyInt())).thenReturn(1);
+        when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL),
+                anyString())).thenReturn(TEST_HTTP_URL);
+        when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL),
+                anyString())).thenReturn(TEST_HTTPS_URL);
+
         when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony);
         when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi);
+
+        when(mNetworkInfo.getType()).thenReturn(ConnectivityManager.TYPE_WIFI);
+        setFallbackUrl(TEST_FALLBACK_URL);
+        setOtherFallbackUrls(TEST_OTHER_FALLBACK_URL);
+        setFallbackSpecs(null); // Test with no fallback spec by default
+        when(mRandom.nextInt()).thenReturn(0);
+
+        when(mNetwork.openConnection(any())).then((invocation) -> {
+            URL url = invocation.getArgument(0);
+            switch(url.toString()) {
+                case TEST_HTTP_URL:
+                    return mHttpConnection;
+                case TEST_HTTPS_URL:
+                    return mHttpsConnection;
+                case TEST_FALLBACK_URL:
+                    return mFallbackConnection;
+                case TEST_OTHER_FALLBACK_URL:
+                    return mOtherFallbackConnection;
+                default:
+                    fail("URL not mocked: " + url.toString());
+                    return null;
+            }
+        });
+        when(mHttpConnection.getRequestProperties()).thenReturn(new ArrayMap<>());
+        when(mHttpsConnection.getRequestProperties()).thenReturn(new ArrayMap<>());
+        when(mNetwork.getAllByName(any())).thenReturn(new InetAddress[] {
+            InetAddress.parseNumericAddress("192.168.0.0")
+        });
     }
 
     NetworkMonitor makeMonitor() {
-        return new NetworkMonitor(mContext, mHandler, mAgent, mRequest, mLogger, mSettings);
+        return new NetworkMonitor(
+                mContext, mHandler, mAgent, mRequest, mLogger, mDependencies);
     }
 
     @Test
-    public void testCreatingNetworkMonitor() {
-        NetworkMonitor monitor = makeMonitor();
+    public void testIsCaptivePortal_HttpProbeIsPortal() throws IOException {
+        setSslException(mHttpsConnection);
+        setPortal302(mHttpConnection);
+
+        assertPortal(makeMonitor().isCaptivePortal());
+    }
+
+    @Test
+    public void testIsCaptivePortal_HttpsProbeIsNotPortal() throws IOException {
+        setStatus(mHttpsConnection, 204);
+        setStatus(mHttpConnection, 500);
+
+        assertNotPortal(makeMonitor().isCaptivePortal());
+    }
+
+    @Test
+    public void testIsCaptivePortal_HttpsProbeFailedHttpSuccessNotUsed() throws IOException {
+        setSslException(mHttpsConnection);
+        // Even if HTTP returns a 204, do not use the result unless HTTPS succeeded
+        setStatus(mHttpConnection, 204);
+        setStatus(mFallbackConnection, 500);
+
+        assertFailed(makeMonitor().isCaptivePortal());
+    }
+
+    @Test
+    public void testIsCaptivePortal_FallbackProbeIsPortal() throws IOException {
+        setSslException(mHttpsConnection);
+        setStatus(mHttpConnection, 500);
+        setPortal302(mFallbackConnection);
+
+        assertPortal(makeMonitor().isCaptivePortal());
+    }
+
+    @Test
+    public void testIsCaptivePortal_FallbackProbeIsNotPortal() throws IOException {
+        setSslException(mHttpsConnection);
+        setStatus(mHttpConnection, 500);
+        setStatus(mFallbackConnection, 204);
+
+        // Fallback probe did not see portal, HTTPS failed -> inconclusive
+        assertFailed(makeMonitor().isCaptivePortal());
+    }
+
+    @Test
+    public void testIsCaptivePortal_OtherFallbackProbeIsPortal() throws IOException {
+        // Set all fallback probes but one to invalid URLs to verify they are being skipped
+        setFallbackUrl(TEST_FALLBACK_URL);
+        setOtherFallbackUrls(TEST_FALLBACK_URL + "," + TEST_OTHER_FALLBACK_URL);
+
+        setSslException(mHttpsConnection);
+        setStatus(mHttpConnection, 500);
+        setStatus(mFallbackConnection, 500);
+        setPortal302(mOtherFallbackConnection);
+
+        // TEST_OTHER_FALLBACK_URL is third
+        when(mRandom.nextInt()).thenReturn(2);
+
+        final NetworkMonitor monitor = makeMonitor();
+
+        // First check always uses the first fallback URL: inconclusive
+        assertFailed(monitor.isCaptivePortal());
+        verify(mFallbackConnection, times(1)).getResponseCode();
+        verify(mOtherFallbackConnection, never()).getResponseCode();
+
+        // Second check uses the URL chosen by Random
+        assertPortal(monitor.isCaptivePortal());
+        verify(mOtherFallbackConnection, times(1)).getResponseCode();
+    }
+
+    @Test
+    public void testIsCaptivePortal_AllProbesFailed() throws IOException {
+        setSslException(mHttpsConnection);
+        setStatus(mHttpConnection, 500);
+        setStatus(mFallbackConnection, 404);
+
+        assertFailed(makeMonitor().isCaptivePortal());
+        verify(mFallbackConnection, times(1)).getResponseCode();
+        verify(mOtherFallbackConnection, never()).getResponseCode();
+    }
+
+    @Test
+    public void testIsCaptivePortal_InvalidUrlSkipped() throws IOException {
+        setFallbackUrl("invalid");
+        setOtherFallbackUrls("otherinvalid," + TEST_OTHER_FALLBACK_URL + ",yetanotherinvalid");
+
+        setSslException(mHttpsConnection);
+        setStatus(mHttpConnection, 500);
+        setPortal302(mOtherFallbackConnection);
+
+        assertPortal(makeMonitor().isCaptivePortal());
+        verify(mOtherFallbackConnection, times(1)).getResponseCode();
+        verify(mFallbackConnection, never()).getResponseCode();
+    }
+
+    private void setupFallbackSpec() throws IOException {
+        setFallbackSpecs("http://example.com@@/@@204@@/@@"
+                + "@@,@@"
+                + TEST_OTHER_FALLBACK_URL + "@@/@@30[12]@@/@@https://(www\\.)?google.com/?.*");
+
+        setSslException(mHttpsConnection);
+        setStatus(mHttpConnection, 500);
+
+        // Use the 2nd fallback spec
+        when(mRandom.nextInt()).thenReturn(1);
+    }
+
+    @Test
+    public void testIsCaptivePortal_FallbackSpecIsNotPortal() throws IOException {
+        setupFallbackSpec();
+        set302(mOtherFallbackConnection, "https://www.google.com/test?q=3");
+
+        // HTTPS failed, fallback spec did not see a portal -> inconclusive
+        assertFailed(makeMonitor().isCaptivePortal());
+        verify(mOtherFallbackConnection, times(1)).getResponseCode();
+        verify(mFallbackConnection, never()).getResponseCode();
+    }
+
+    @Test
+    public void testIsCaptivePortal_FallbackSpecIsPortal() throws IOException {
+        setupFallbackSpec();
+        set302(mOtherFallbackConnection, "http://login.portal.example.com");
+
+        assertPortal(makeMonitor().isCaptivePortal());
+    }
+
+    private void setFallbackUrl(String url) {
+        when(mDependencies.getSetting(any(),
+                eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL), any())).thenReturn(url);
+    }
+
+    private void setOtherFallbackUrls(String urls) {
+        when(mDependencies.getSetting(any(),
+                eq(Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls);
+    }
+
+    private void setFallbackSpecs(String specs) {
+        when(mDependencies.getSetting(any(),
+                eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs);
+    }
+
+    private void assertPortal(CaptivePortalProbeResult result) {
+        assertTrue(result.isPortal());
+        assertFalse(result.isFailed());
+        assertFalse(result.isSuccessful());
+    }
+
+    private void assertNotPortal(CaptivePortalProbeResult result) {
+        assertFalse(result.isPortal());
+        assertFalse(result.isFailed());
+        assertTrue(result.isSuccessful());
+    }
+
+    private void assertFailed(CaptivePortalProbeResult result) {
+        assertFalse(result.isPortal());
+        assertTrue(result.isFailed());
+        assertFalse(result.isSuccessful());
+    }
+
+    private void setSslException(HttpURLConnection connection) throws IOException {
+        when(connection.getResponseCode()).thenThrow(new SSLHandshakeException("Invalid cert"));
+    }
+
+    private void set302(HttpURLConnection connection, String location) throws IOException {
+        setStatus(connection, 302);
+        when(connection.getHeaderField(LOCATION_HEADER)).thenReturn(location);
+    }
+
+    private void setPortal302(HttpURLConnection connection) throws IOException {
+        set302(connection, "http://login.example.com");
+    }
+
+    private void setStatus(HttpURLConnection connection, int status) throws IOException {
+        when(connection.getResponseCode()).thenReturn(status);
     }
 }
 
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 34ecde8..0d3b8e4 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -71,6 +71,7 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
+import android.net.NetworkRequest;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
@@ -128,6 +129,10 @@
     private static final String TEST_USB_IFNAME = "test_rndis0";
     private static final String TEST_WLAN_IFNAME = "test_wlan0";
 
+    // Actual contents of the request don't matter for this test. The lack of
+    // any specific TRANSPORT_* is sufficient to identify this request.
+    private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
+
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
     @Mock private INetworkManagementService mNMService;
@@ -238,6 +243,11 @@
             isTetheringSupportedCalls++;
             return true;
         }
+
+        @Override
+        public NetworkRequest getDefaultNetworkRequest() {
+            return mDefaultRequest;
+        }
     }
 
     private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
@@ -305,6 +315,8 @@
                 .thenReturn(new String[0]);
         when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
                 .thenReturn(new int[0]);
+        when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
+                .thenReturn(false);
         when(mNMService.listInterfaces())
                 .thenReturn(new String[] {
                         TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME});
@@ -458,6 +470,7 @@
     }
 
     private void prepareUsbTethering(NetworkState upstreamState) {
+        when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
         when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
                 .thenReturn(upstreamState);
 
@@ -519,7 +532,7 @@
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
         verifyNoMoreInteractions(mWifiManager);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
-        verify(mUpstreamNetworkMonitor, times(1)).start();
+        verify(mUpstreamNetworkMonitor, times(1)).start(any(NetworkRequest.class));
         // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
         assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
 
@@ -656,6 +669,24 @@
     }
 
     @Test
+    public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception {
+        when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
+                .thenReturn(true);
+        sendConfigurationChanged();
+
+        // Setup IPv6
+        final NetworkState upstreamState = buildMobileIPv6UpstreamState();
+        runUsbTethering(upstreamState);
+
+        // UpstreamNetworkMonitor should choose upstream automatically
+        // (in this specific case: choose the default network).
+        verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream();
+        verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any());
+
+        verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
+    }
+
+    @Test
     public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception {
         workingLocalOnlyHotspotEnrichedApBroadcast(true);
     }
@@ -718,7 +749,7 @@
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
         verifyNoMoreInteractions(mWifiManager);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
-        verify(mUpstreamNetworkMonitor, times(1)).start();
+        verify(mUpstreamNetworkMonitor, times(1)).start(any(NetworkRequest.class));
         // In tethering mode, in the default configuration, an explicit request
         // for a mobile network is also made.
         verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
diff --git a/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java b/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java
deleted file mode 100644
index f58ea7e..0000000
--- a/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java
+++ /dev/null
@@ -1,132 +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.connectivity.tethering;
-
-import static com.android.internal.telephony.IccCardConstants.INTENT_VALUE_ICC_ABSENT;
-import static com.android.internal.telephony.IccCardConstants.INTENT_VALUE_ICC_LOADED;
-import static com.android.internal.telephony.IccCardConstants.INTENT_KEY_ICC_STATE;
-import static com.android.internal.telephony.TelephonyIntents.ACTION_SIM_STATE_CHANGED;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.reset;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.test.BroadcastInterceptingContext;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.runner.RunWith;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class SimChangeListenerTest {
-    @Mock private Context mContext;
-    private BroadcastInterceptingContext mServiceContext;
-    private Handler mHandler;
-    private SimChangeListener mSCL;
-    private int mCallbackCount;
-
-    private void doCallback() { mCallbackCount++; }
-
-    private class MockContext extends BroadcastInterceptingContext {
-        MockContext(Context base) {
-            super(base);
-        }
-    }
-
-    @BeforeClass
-    public static void setUpBeforeClass() throws Exception {
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-    }
-
-    @Before public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        reset(mContext);
-        mServiceContext = new MockContext(mContext);
-        mHandler = new Handler(Looper.myLooper());
-        mCallbackCount = 0;
-        mSCL = new SimChangeListener(mServiceContext, mHandler, () -> doCallback());
-    }
-
-    @After public void tearDown() throws Exception {
-        if (mSCL != null) {
-            mSCL.stopListening();
-            mSCL = null;
-        }
-    }
-
-    private void sendSimStateChangeIntent(String state) {
-        final Intent intent = new Intent(ACTION_SIM_STATE_CHANGED);
-        intent.putExtra(INTENT_KEY_ICC_STATE, state);
-        mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
-    }
-
-    @Test
-    public void testNotSeenFollowedBySeenCallsCallback() {
-        mSCL.startListening();
-
-        sendSimStateChangeIntent(INTENT_VALUE_ICC_ABSENT);
-        sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
-        assertEquals(1, mCallbackCount);
-
-        sendSimStateChangeIntent(INTENT_VALUE_ICC_ABSENT);
-        sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
-        assertEquals(2, mCallbackCount);
-
-        mSCL.stopListening();
-    }
-
-    @Test
-    public void testNotListeningDoesNotCallback() {
-        sendSimStateChangeIntent(INTENT_VALUE_ICC_ABSENT);
-        sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
-        assertEquals(0, mCallbackCount);
-
-        sendSimStateChangeIntent(INTENT_VALUE_ICC_ABSENT);
-        sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
-        assertEquals(0, mCallbackCount);
-    }
-
-    @Test
-    public void testSeenOnlyDoesNotCallback() {
-        mSCL.startListening();
-
-        sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
-        assertEquals(0, mCallbackCount);
-
-        sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
-        assertEquals(0, mCallbackCount);
-
-        mSCL.stopListening();
-    }
-}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index 9661dc2..3e21a2c 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -31,6 +31,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -73,6 +74,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 
@@ -84,6 +86,10 @@
     private static final boolean INCLUDES = true;
     private static final boolean EXCLUDES = false;
 
+    // Actual contents of the request don't matter for this test. The lack of
+    // any specific TRANSPORT_* is sufficient to identify this request.
+    private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
+
     @Mock private Context mContext;
     @Mock private IConnectivityManager mCS;
     @Mock private SharedLog mLog;
@@ -113,6 +119,13 @@
     }
 
     @Test
+    public void testStopWithoutStartIsNonFatal() {
+        mUNM.stop();
+        mUNM.stop();
+        mUNM.stop();
+    }
+
+    @Test
     public void testDoesNothingBeforeStarted() {
         assertTrue(mCM.hasNoCallbacks());
         assertFalse(mUNM.mobileNetworkRequested());
@@ -127,7 +140,7 @@
     public void testDefaultNetworkIsTracked() throws Exception {
         assertEquals(0, mCM.trackingDefault.size());
 
-        mUNM.start();
+        mUNM.start(mDefaultRequest);
         assertEquals(1, mCM.trackingDefault.size());
 
         mUNM.stop();
@@ -138,7 +151,7 @@
     public void testListensForAllNetworks() throws Exception {
         assertTrue(mCM.listening.isEmpty());
 
-        mUNM.start();
+        mUNM.start(mDefaultRequest);
         assertFalse(mCM.listening.isEmpty());
         assertTrue(mCM.isListeningForAll());
 
@@ -148,9 +161,11 @@
 
     @Test
     public void testCallbacksRegistered() {
-        mUNM.start();
-        verify(mCM, times(1)).registerNetworkCallback(any(), any(), any());
-        verify(mCM, times(1)).registerDefaultNetworkCallback(any(), any());
+        mUNM.start(mDefaultRequest);
+        verify(mCM, times(1)).registerNetworkCallback(
+                any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
+        verify(mCM, times(1)).requestNetwork(
+                eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
 
         mUNM.stop();
         verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
@@ -161,7 +176,7 @@
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
-        mUNM.start();
+        mUNM.start(mDefaultRequest);
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
@@ -184,17 +199,17 @@
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
-        mUNM.start();
-        verify(mCM, Mockito.times(1)).registerNetworkCallback(
+        mUNM.start(mDefaultRequest);
+        verify(mCM, times(1)).registerNetworkCallback(
                 any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
-        verify(mCM, Mockito.times(1)).registerDefaultNetworkCallback(
-                any(NetworkCallback.class), any(Handler.class));
+        verify(mCM, times(1)).requestNetwork(
+                eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
         mUNM.updateMobileRequiresDun(true);
         mUNM.registerMobileNetworkRequest();
-        verify(mCM, Mockito.times(1)).requestNetwork(
+        verify(mCM, times(1)).requestNetwork(
                 any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt(),
                 any(Handler.class));
 
@@ -222,7 +237,7 @@
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
-        mUNM.start();
+        mUNM.start(mDefaultRequest);
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
@@ -242,7 +257,7 @@
 
     @Test
     public void testUpdateMobileRequiresDun() throws Exception {
-        mUNM.start();
+        mUNM.start(mDefaultRequest);
 
         // Test going from no-DUN to DUN correctly re-registers callbacks.
         mUNM.updateMobileRequiresDun(false);
@@ -270,7 +285,7 @@
         final Collection<Integer> preferredTypes = new ArrayList<>();
         preferredTypes.add(TYPE_WIFI);
 
-        mUNM.start();
+        mUNM.start(mDefaultRequest);
         // There are no networks, so there is nothing to select.
         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
 
@@ -334,8 +349,47 @@
     }
 
     @Test
+    public void testGetCurrentPreferredUpstream() throws Exception {
+        mUNM.start(mDefaultRequest);
+        mUNM.updateMobileRequiresDun(false);
+
+        // [0] Mobile connects, DUN not required -> mobile selected.
+        final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+        cellAgent.fakeConnect();
+        mCM.makeDefaultNetwork(cellAgent);
+        assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+
+        // [1] WiFi connects but not validated/promoted to default -> mobile selected.
+        final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
+        wifiAgent.fakeConnect();
+        assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+
+        // [2] WiFi validates and is promoted to the default network -> WiFi selected.
+        mCM.makeDefaultNetwork(wifiAgent);
+        assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+
+        // [3] DUN required, no other changes -> WiFi still selected
+        mUNM.updateMobileRequiresDun(true);
+        assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+
+        // [4] WiFi no longer validated, mobile becomes default, DUN required -> null selected.
+        mCM.makeDefaultNetwork(cellAgent);
+        assertEquals(null, mUNM.getCurrentPreferredUpstream());
+        // TODO: make sure that a DUN request has been filed. This is currently
+        // triggered by code over in Tethering, but once that has been moved
+        // into UNM we should test for this here.
+
+        // [5] DUN network arrives -> DUN selected
+        final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+        dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
+        dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
+        dunAgent.fakeConnect();
+        assertEquals(dunAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+    }
+
+    @Test
     public void testLocalPrefixes() throws Exception {
-        mUNM.start();
+        mUNM.start(mDefaultRequest);
 
         // [0] Test minimum set of local prefixes.
         Set<IpPrefix> local = mUNM.getLocalPrefixes();
@@ -345,7 +399,7 @@
 
         // [1] Pretend Wi-Fi connects.
         final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
-        final LinkProperties wifiLp = new LinkProperties();
+        final LinkProperties wifiLp = wifiAgent.linkProperties;
         wifiLp.setInterfaceName("wlan0");
         final String[] WIFI_ADDRS = {
                 "fe80::827a:bfff:fe6f:374d", "100.112.103.18",
@@ -358,7 +412,7 @@
             wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr));
         }
         wifiAgent.fakeConnect();
-        wifiAgent.sendLinkProperties(wifiLp);
+        wifiAgent.sendLinkProperties();
 
         local = mUNM.getLocalPrefixes();
         assertPrefixSet(local, INCLUDES, alreadySeen);
@@ -372,7 +426,7 @@
 
         // [2] Pretend mobile connects.
         final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
-        final LinkProperties cellLp = new LinkProperties();
+        final LinkProperties cellLp = cellAgent.linkProperties;
         cellLp.setInterfaceName("rmnet_data0");
         final String[] CELL_ADDRS = {
                 "10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d",
@@ -382,7 +436,7 @@
             cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
         }
         cellAgent.fakeConnect();
-        cellAgent.sendLinkProperties(cellLp);
+        cellAgent.sendLinkProperties();
 
         local = mUNM.getLocalPrefixes();
         assertPrefixSet(local, INCLUDES, alreadySeen);
@@ -394,17 +448,18 @@
         // [3] Pretend DUN connects.
         final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
         dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
-        final LinkProperties dunLp = new LinkProperties();
+        dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
+        final LinkProperties dunLp = dunAgent.linkProperties;
         dunLp.setInterfaceName("rmnet_data1");
         final String[] DUN_ADDRS = {
                 "192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d",
         };
         for (String addrStr : DUN_ADDRS) {
             final String cidr = addrStr.contains(":") ? "/64" : "/27";
-            cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
+            dunLp.addLinkAddress(new LinkAddress(addrStr + cidr));
         }
         dunAgent.fakeConnect();
-        dunAgent.sendLinkProperties(dunLp);
+        dunAgent.sendLinkProperties();
 
         local = mUNM.getLocalPrefixes();
         assertPrefixSet(local, INCLUDES, alreadySeen);
@@ -442,6 +497,7 @@
     public static class TestConnectivityManager extends ConnectivityManager {
         public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>();
         public Set<NetworkCallback> trackingDefault = new HashSet<>();
+        public TestNetworkAgent defaultNetwork = null;
         public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
         public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
         public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>();
@@ -483,12 +539,34 @@
 
         int getNetworkId() { return ++mNetworkId; }
 
+        void makeDefaultNetwork(TestNetworkAgent agent) {
+            if (Objects.equals(defaultNetwork, agent)) return;
+
+            final TestNetworkAgent formerDefault = defaultNetwork;
+            defaultNetwork = agent;
+
+            for (NetworkCallback cb : trackingDefault) {
+                if (defaultNetwork != null) {
+                    cb.onAvailable(defaultNetwork.networkId);
+                    cb.onCapabilitiesChanged(
+                            defaultNetwork.networkId, defaultNetwork.networkCapabilities);
+                    cb.onLinkPropertiesChanged(
+                            defaultNetwork.networkId, defaultNetwork.linkProperties);
+                }
+            }
+        }
+
         @Override
         public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
             assertFalse(allCallbacks.containsKey(cb));
             allCallbacks.put(cb, h);
-            assertFalse(requested.containsKey(cb));
-            requested.put(cb, req);
+            if (mDefaultRequest.equals(req)) {
+                assertFalse(trackingDefault.contains(cb));
+                trackingDefault.add(cb);
+            } else {
+                assertFalse(requested.containsKey(cb));
+                requested.put(cb, req);
+            }
         }
 
         @Override
@@ -524,10 +602,7 @@
 
         @Override
         public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) {
-            assertFalse(allCallbacks.containsKey(cb));
-            allCallbacks.put(cb, h);
-            assertFalse(trackingDefault.contains(cb));
-            trackingDefault.add(cb);
+            fail("Should never be called.");
         }
 
         @Override
@@ -561,6 +636,7 @@
         public final Network networkId;
         public final int transportType;
         public final NetworkCapabilities networkCapabilities;
+        public final LinkProperties linkProperties;
 
         public TestNetworkAgent(TestConnectivityManager cm, int transportType) {
             this.cm = cm;
@@ -569,12 +645,14 @@
             networkCapabilities = new NetworkCapabilities();
             networkCapabilities.addTransportType(transportType);
             networkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
+            linkProperties = new LinkProperties();
         }
 
         public void fakeConnect() {
             for (NetworkCallback cb : cm.listening.keySet()) {
                 cb.onAvailable(networkId);
                 cb.onCapabilitiesChanged(networkId, copy(networkCapabilities));
+                cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
             }
         }
 
@@ -584,11 +662,16 @@
             }
         }
 
-        public void sendLinkProperties(LinkProperties lp) {
+        public void sendLinkProperties() {
             for (NetworkCallback cb : cm.listening.keySet()) {
-                cb.onLinkPropertiesChanged(networkId, lp);
+                cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
             }
         }
+
+        @Override
+        public String toString() {
+            return String.format("TestNetworkAgent: %s %s", networkId, networkCapabilities);
+        }
     }
 
     public static class TestStateMachine extends StateMachine {
@@ -618,6 +701,10 @@
         return new NetworkCapabilities(nc);
     }
 
+    static LinkProperties copy(LinkProperties lp) {
+        return new LinkProperties(lp);
+    }
+
     static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected) {
         final Set<String> expectedSet = new HashSet<>();
         Collections.addAll(expectedSet, expected);
diff --git a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
index 89bd8d8..548a0c22 100644
--- a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
@@ -17,6 +17,7 @@
 package com.android.framework.permission.tests;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.IActivityManager;
 import android.content.res.Configuration;
 import android.os.RemoteException;
@@ -47,22 +48,12 @@
         } catch (RemoteException e) {
             fail("Unexpected remote exception");
         }
-
-        try {
-            mAm.moveTaskBackwards(-1);
-            fail("IActivityManager.moveTaskToFront did not throw SecurityException as"
-                    + " expected");
-        } catch (SecurityException e) {
-            // expected
-        } catch (RemoteException e) {
-            fail("Unexpected remote exception");
-        }
     }
 
     @SmallTest
     public void testCHANGE_CONFIGURATION() {
         try {
-            mAm.updateConfiguration(new Configuration());
+            ActivityTaskManager.getService().updateConfiguration(new Configuration());
             fail("IActivityManager.updateConfiguration did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
index 2757296..388c7d0 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
@@ -52,7 +52,7 @@
             final VibrationEffect effect =
                     VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);
             mVibratorService.vibrate(Process.myUid(), null, effect, AudioManager.STREAM_ALARM,
-                    new Binder());
+                    "testVibrate", new Binder());
             fail("vibrate did not throw SecurityException as expected");
         } catch (SecurityException e) {
             // expected
diff --git a/tests/testables/src/android/testing/TestableContentResolver.java b/tests/testables/src/android/testing/TestableContentResolver.java
index 0850916..a0afef8 100644
--- a/tests/testables/src/android/testing/TestableContentResolver.java
+++ b/tests/testables/src/android/testing/TestableContentResolver.java
@@ -20,6 +20,7 @@
 import android.content.IContentProvider;
 import android.database.ContentObserver;
 import android.net.Uri;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import com.google.android.collect.Maps;
@@ -35,7 +36,11 @@
  */
 public class TestableContentResolver extends ContentResolver {
 
-    private final Map<String, ContentProvider> mProviders = Maps.newHashMap();
+    public static final int STABLE = 1;
+    public static final int UNSTABLE = 2;
+
+    private final Map<String, ContentProvider> mProviders = new ArrayMap<>();
+    private final Map<String, ContentProvider> mUnstableProviders = new ArrayMap<>();
     private final ContentResolver mParent;
     private final ArraySet<ContentProvider> mInUse = new ArraySet<>();
     private boolean mFallbackToExisting;
@@ -62,7 +67,23 @@
      * subclasses, or null.
      */
     public void addProvider(String name, ContentProvider provider) {
-        mProviders.put(name, provider);
+        addProvider(name, provider, STABLE | UNSTABLE);
+    }
+
+    /**
+     * Adds access to a provider based on its authority
+     *
+     * @param name The authority name associated with the provider.
+     * @param provider An instance of {@link android.content.ContentProvider} or one of its
+     * subclasses, or null.
+     */
+    public void addProvider(String name, ContentProvider provider, int flags) {
+        if ((flags & STABLE) != 0) {
+            mProviders.put(name, provider);
+        }
+        if ((flags & UNSTABLE) != 0) {
+            mUnstableProviders.put(name, provider);
+        }
     }
 
     @Override
@@ -98,7 +119,7 @@
 
     @Override
     protected IContentProvider acquireUnstableProvider(Context c, String name) {
-        final ContentProvider provider = mProviders.get(name);
+        final ContentProvider provider = mUnstableProviders.get(name);
         if (provider != null) {
             return provider.getIContentProvider();
         } else {
@@ -128,7 +149,8 @@
     @Override
     public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
         if (!mFallbackToExisting) return;
-        if (!mProviders.containsKey(uri.getAuthority())) {
+        if (!mProviders.containsKey(uri.getAuthority())
+                && !mUnstableProviders.containsKey(uri.getAuthority())) {
             super.notifyChange(uri, observer, syncToNetwork);
         }
     }
diff --git a/tests/testables/src/android/testing/TestableInstrumentation.java b/tests/testables/src/android/testing/TestableInstrumentation.java
index 3207b48..c35dc68 100644
--- a/tests/testables/src/android/testing/TestableInstrumentation.java
+++ b/tests/testables/src/android/testing/TestableInstrumentation.java
@@ -38,22 +38,26 @@
 
     @Override
     public void onCreate(Bundle arguments) {
-        sManager = new MainLooperManager();
-        Log.setWtfHandler((tag, what, system) -> {
-            if (system) {
-                Log.e(TAG, "WTF!!", what);
-            } else {
-                // These normally kill the app, but we don't want that in a test, instead we want
-                // it to throw.
-                throw new RuntimeException(what);
-            }
-        });
+        if (TestableLooper.HOLD_MAIN_THREAD) {
+            sManager = new MainLooperManager();
+            Log.setWtfHandler((tag, what, system) -> {
+                if (system) {
+                    Log.e(TAG, "WTF!!", what);
+                } else {
+                    // These normally kill the app, but we don't want that in a test, instead we want
+                    // it to throw.
+                    throw new RuntimeException(what);
+                }
+            });
+        }
         super.onCreate(arguments);
     }
 
     @Override
     public void finish(int resultCode, Bundle results) {
-        sManager.destroy();
+        if (TestableLooper.HOLD_MAIN_THREAD) {
+            sManager.destroy();
+        }
         super.finish(resultCode, results);
     }
 
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index f8d223a..8b4cba1 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -39,6 +39,12 @@
  */
 public class TestableLooper {
 
+    /**
+     * Whether to hold onto the main thread through all tests in an attempt to
+     * catch crashes.
+     */
+    public static final boolean HOLD_MAIN_THREAD = false;
+
     private Looper mLooper;
     private MessageQueue mQueue;
     private MessageHandler mMessageHandler;
@@ -77,7 +83,7 @@
      */
     public void destroy() {
         mQueueWrapper.release();
-        if (mLooper == Looper.getMainLooper()) {
+        if (HOLD_MAIN_THREAD && mLooper == Looper.getMainLooper()) {
             TestableInstrumentation.releaseMain();
         }
     }
@@ -199,7 +205,7 @@
     }
 
     private static TestLooperManager acquireLooperManager(Looper l) {
-        if (l == Looper.getMainLooper()) {
+        if (HOLD_MAIN_THREAD && l == Looper.getMainLooper()) {
             TestableInstrumentation.acquireMain();
         }
         return InstrumentationRegistry.getInstrumentation().acquireLooperManager(l);
@@ -291,7 +297,7 @@
                 if (set) {
                     mTestableLooper.mQueueWrapper.release();
                     mTestableLooper.mQueueWrapper = null;
-                    if (mLooper == Looper.getMainLooper()) {
+                    if (HOLD_MAIN_THREAD && mLooper == Looper.getMainLooper()) {
                         TestableInstrumentation.releaseMain();
                     }
                 }
diff --git a/tests/testables/tests/src/android/testing/TestableContentResolverTest.java b/tests/testables/tests/src/android/testing/TestableContentResolverTest.java
new file mode 100644
index 0000000..71afda0
--- /dev/null
+++ b/tests/testables/tests/src/android/testing/TestableContentResolverTest.java
@@ -0,0 +1,61 @@
+package android.testing;
+
+import android.content.ContentProvider;
+import android.content.IContentProvider;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class TestableContentResolverTest {
+
+    @Rule
+    public TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
+    private TestableContentResolver mContentResolver;
+
+    @Before
+    public void setup() {
+        mContentResolver = new TestableContentResolver(mContext);
+        mContentResolver.setFallbackToExisting(false);
+    }
+
+    @Test
+    public void testDefaultContentProvider() {
+        ContentProvider provider = Mockito.mock(ContentProvider.class);
+        IContentProvider iprovider = Mockito.mock(IContentProvider.class);
+        Mockito.when(provider.getIContentProvider()).thenReturn(iprovider);
+        mContentResolver.addProvider("test", provider);
+
+        Assert.assertEquals(iprovider, mContentResolver.acquireProvider(mContext, "test"));
+        Assert.assertEquals(iprovider, mContentResolver.acquireUnstableProvider(mContext, "test"));
+    }
+
+    @Test
+    public void testStableContentProvider() {
+        ContentProvider provider = Mockito.mock(ContentProvider.class);
+        IContentProvider iprovider = Mockito.mock(IContentProvider.class);
+        Mockito.when(provider.getIContentProvider()).thenReturn(iprovider);
+        mContentResolver.addProvider("test", provider, TestableContentResolver.STABLE);
+
+        Assert.assertEquals(iprovider, mContentResolver.acquireProvider(mContext, "test"));
+        Assert.assertNull(mContentResolver.acquireUnstableProvider(mContext, "test"));
+    }
+
+    @Test
+    public void testUnstableContentProvider() {
+        ContentProvider provider = Mockito.mock(ContentProvider.class);
+        IContentProvider iprovider = Mockito.mock(IContentProvider.class);
+        Mockito.when(provider.getIContentProvider()).thenReturn(iprovider);
+        mContentResolver.addProvider("test", provider, TestableContentResolver.UNSTABLE);
+
+        Assert.assertEquals(iprovider, mContentResolver.acquireUnstableProvider(mContext, "test"));
+        Assert.assertNull(mContentResolver.acquireProvider(mContext, "test"));
+    }
+}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 7a33de0..2ecf25b 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -15,6 +15,7 @@
 //
 
 toolSources = [
+    "cmd/Command.cpp",
     "cmd/Compile.cpp",
     "cmd/Convert.cpp",
     "cmd/Diff.cpp",
@@ -124,7 +125,6 @@
         "ConfigDescription.cpp",
         "Debug.cpp",
         "DominatorTree.cpp",
-        "Flags.cpp",
         "java/AnnotationProcessor.cpp",
         "java/ClassDefinition.cpp",
         "java/JavaClassGenerator.cpp",
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h
index d6f5995..7512353 100644
--- a/tools/aapt2/AppInfo.h
+++ b/tools/aapt2/AppInfo.h
@@ -31,9 +31,12 @@
   // The app's minimum SDK version, if it is defined.
   Maybe<int> min_sdk_version;
 
-  // The app's version code, if it is defined.
+  // The app's version code (the lower 32 bits of the long version code), if it is defined.
   Maybe<uint32_t> version_code;
 
+  // The app's version code major (the upper 32 bits of the long version code), if it is defined.
+  Maybe<uint32_t> version_code_major;
+
   // The app's revision code, if it is defined.
   Maybe<uint32_t> revision_code;
 
diff --git a/tools/aapt2/Flags.cpp b/tools/aapt2/Flags.cpp
deleted file mode 100644
index 84977ab..0000000
--- a/tools/aapt2/Flags.cpp
+++ /dev/null
@@ -1,189 +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.
- */
-
-#include "Flags.h"
-
-#include <iomanip>
-#include <iostream>
-#include <string>
-#include <vector>
-
-#include "androidfw/StringPiece.h"
-
-#include "util/Util.h"
-
-using android::StringPiece;
-
-namespace aapt {
-
-Flags& Flags::RequiredFlag(const StringPiece& name,
-                           const StringPiece& description, std::string* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    *value = arg.to_string();
-    return true;
-  };
-
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
-  return *this;
-}
-
-Flags& Flags::RequiredFlagList(const StringPiece& name,
-                               const StringPiece& description,
-                               std::vector<std::string>* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    value->push_back(arg.to_string());
-    return true;
-  };
-
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
-  return *this;
-}
-
-Flags& Flags::OptionalFlag(const StringPiece& name,
-                           const StringPiece& description,
-                           Maybe<std::string>* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    *value = arg.to_string();
-    return true;
-  };
-
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
-  return *this;
-}
-
-Flags& Flags::OptionalFlagList(const StringPiece& name,
-                               const StringPiece& description,
-                               std::vector<std::string>* value) {
-  auto func = [value](const StringPiece& arg) -> bool {
-    value->push_back(arg.to_string());
-    return true;
-  };
-
-  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
-  return *this;
-}
-
-Flags& Flags::OptionalFlagList(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});
-  return *this;
-}
-
-Flags& Flags::OptionalSwitch(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});
-  return *this;
-}
-
-void Flags::Usage(const StringPiece& command, std::ostream* out) {
-  constexpr size_t kWidth = 50;
-
-  *out << command << " [options]";
-  for (const Flag& flag : flags_) {
-    if (flag.required) {
-      *out << " " << flag.name << " arg";
-    }
-  }
-
-  *out << " files...\n\nOptions:\n";
-
-  for (const Flag& flag : flags_) {
-    std::string argline = flag.name;
-    if (flag.num_args > 0) {
-      argline += " arg";
-    }
-
-    // Split the description by newlines and write out the argument (which is
-    // empty after
-    // the first line) followed by the description line. This will make sure
-    // that multiline
-    // descriptions are still right justified and aligned.
-    for (StringPiece line : util::Tokenize(flag.description, '\n')) {
-      *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
-      argline = " ";
-    }
-  }
-  *out << " " << std::setw(kWidth) << std::left << "-h"
-       << "Displays this help menu\n";
-  out->flush();
-}
-
-bool Flags::Parse(const StringPiece& command,
-                  const std::vector<StringPiece>& args,
-                  std::ostream* out_error) {
-  for (size_t i = 0; i < args.size(); i++) {
-    StringPiece arg = args[i];
-    if (*(arg.data()) != '-') {
-      args_.push_back(arg.to_string());
-      continue;
-    }
-
-    if (arg == "-h" || arg == "--help") {
-      Usage(command, out_error);
-      return false;
-    }
-
-    bool match = false;
-    for (Flag& flag : flags_) {
-      if (arg == flag.name) {
-        if (flag.num_args > 0) {
-          i++;
-          if (i >= args.size()) {
-            *out_error << flag.name << " missing argument.\n\n";
-            Usage(command, out_error);
-            return false;
-          }
-          flag.action(args[i]);
-        } else {
-          flag.action({});
-        }
-        flag.parsed = true;
-        match = true;
-        break;
-      }
-    }
-
-    if (!match) {
-      *out_error << "unknown option '" << arg << "'.\n\n";
-      Usage(command, out_error);
-      return false;
-    }
-  }
-
-  for (const Flag& flag : flags_) {
-    if (flag.required && !flag.parsed) {
-      *out_error << "missing required flag " << flag.name << "\n\n";
-      Usage(command, out_error);
-      return false;
-    }
-  }
-  return true;
-}
-
-const std::vector<std::string>& Flags::GetArgs() { return args_; }
-
-}  // namespace aapt
diff --git a/tools/aapt2/Flags.h b/tools/aapt2/Flags.h
deleted file mode 100644
index 3b3ae71..0000000
--- a/tools/aapt2/Flags.h
+++ /dev/null
@@ -1,71 +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.
- */
-
-#ifndef AAPT_FLAGS_H
-#define AAPT_FLAGS_H
-
-#include <functional>
-#include <ostream>
-#include <string>
-#include <unordered_set>
-#include <vector>
-
-#include "androidfw/StringPiece.h"
-
-#include "util/Maybe.h"
-
-namespace aapt {
-
-class Flags {
- public:
-  Flags& RequiredFlag(const android::StringPiece& name, const android::StringPiece& description,
-                      std::string* value);
-  Flags& RequiredFlagList(const android::StringPiece& name, const android::StringPiece& description,
-                          std::vector<std::string>* value);
-  Flags& OptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
-                      Maybe<std::string>* value);
-  Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description,
-                          std::vector<std::string>* value);
-  Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description,
-                          std::unordered_set<std::string>* value);
-  Flags& OptionalSwitch(const android::StringPiece& name, const android::StringPiece& description,
-                        bool* value);
-
-  void Usage(const android::StringPiece& command, std::ostream* out);
-
-  bool Parse(const android::StringPiece& command, const std::vector<android::StringPiece>& args,
-             std::ostream* outError);
-
-  const std::vector<std::string>& GetArgs();
-
- private:
-  struct Flag {
-    std::string name;
-    std::string description;
-    std::function<bool(const android::StringPiece& value)> action;
-    bool required;
-    size_t num_args;
-
-    bool parsed;
-  };
-
-  std::vector<Flag> flags_;
-  std::vector<std::string> args_;
-};
-
-}  // namespace aapt
-
-#endif  // AAPT_FLAGS_H
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 808b29c..23903c9e 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -29,6 +29,13 @@
 #include "androidfw/StringPiece.h"
 
 #include "Diagnostics.h"
+#include "cmd/Command.h"
+#include "cmd/Compile.h"
+#include "cmd/Convert.h"
+#include "cmd/Diff.h"
+#include "cmd/Dump.h"
+#include "cmd/Link.h"
+#include "cmd/Optimize.h"
 #include "util/Files.h"
 #include "util/Util.h"
 
@@ -43,114 +50,121 @@
 // Update minor version whenever a feature or flag is added.
 static const char* sMinorVersion = "19";
 
-static void PrintVersion() {
-  std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion,
-                            sMinorVersion)
-            << std::endl;
-}
+/** Prints the version information of AAPT2. */
+class VersionCommand : public Command {
+ public:
+  explicit VersionCommand() : Command("version") {
+    SetDescription("Prints the version of aapt.");
+  }
 
-static void PrintUsage() {
-  std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|convert|version] ..." << std::endl;
-}
-
-extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics);
-extern int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics);
-extern int Dump(const std::vector<StringPiece>& args);
-extern int Diff(const std::vector<StringPiece>& args);
-extern int Optimize(const std::vector<StringPiece>& args);
-extern int Convert(const std::vector<StringPiece>& args);
-
-static int ExecuteCommand(const StringPiece& command, const std::vector<StringPiece>& args,
-                          IDiagnostics* diagnostics) {
-  if (command == "compile" || command == "c") {
-    return Compile(args, diagnostics);
-  } else if (command == "link" || command == "l") {
-    return Link(args, diagnostics);
-  } else if (command == "dump" || command == "d") {
-    return Dump(args);
-  } else if (command == "diff") {
-    return Diff(args);
-  } else if (command == "optimize") {
-    return Optimize(args);
-  } else if (command == "convert") {
-    return Convert(args);
-  } else if (command == "version") {
-    PrintVersion();
+  int Action(const std::vector<std::string>& /* args */) override {
+    std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion,
+                              sMinorVersion)
+              << std::endl;
     return 0;
   }
-  diagnostics->Error(DiagMessage() << "unknown command '" << command << "'");
-  return -1;
-}
+};
 
-static void RunDaemon(IDiagnostics* diagnostics) {
-  std::cout << "Ready" << std::endl;
-
-  // Run in daemon mode. The first line of input is the command. This can be 'quit' which ends
-  // the daemon mode. Each subsequent line is a single parameter to the command. The end of a
-  // invocation is signaled by providing an empty line. At any point, an EOF signal or the
-  // command 'quit' will end the daemon mode.
-  while (true) {
-    std::vector<std::string> raw_args;
-    for (std::string line; std::getline(std::cin, line) && !line.empty();) {
-      raw_args.push_back(line);
-    }
-
-    if (!std::cin) {
-      break;
-    }
-
-    // An empty command does nothing.
-    if (raw_args.empty()) {
-      continue;
-    }
-
-    if (raw_args[0] == "quit") {
-      break;
-    }
-
-    std::vector<StringPiece> args;
-    args.insert(args.end(), ++raw_args.begin(), raw_args.end());
-    int ret = ExecuteCommand(raw_args[0], args, diagnostics);
-    if (ret != 0) {
-      std::cerr << "Error" << std::endl;
-    }
-    std::cerr << "Done" << std::endl;
+/** The main entry point of AAPT. */
+class MainCommand : public Command {
+ public:
+  explicit MainCommand(IDiagnostics* diagnostics) : Command("aapt2"), diagnostics_(diagnostics) {
+    AddOptionalSubcommand(util::make_unique<CompileCommand>(diagnostics));
+    AddOptionalSubcommand(util::make_unique<LinkCommand>(diagnostics));
+    AddOptionalSubcommand(util::make_unique<DumpCommand>());
+    AddOptionalSubcommand(util::make_unique<DiffCommand>());
+    AddOptionalSubcommand(util::make_unique<OptimizeCommand>());
+    AddOptionalSubcommand(util::make_unique<ConvertCommand>());
+    AddOptionalSubcommand(util::make_unique<VersionCommand>());
   }
-  std::cout << "Exiting daemon" << std::endl;
-}
+
+  int Action(const std::vector<std::string>& args) override {
+    if (args.size() == 0) {
+      diagnostics_->Error(DiagMessage() << "no subcommand specified");
+    } else {
+      diagnostics_->Error(DiagMessage() << "unknown subcommand '" << args[0] << "'");
+    }
+
+    Usage(&std::cerr);
+    return -1;
+  }
+
+ private:
+  IDiagnostics* diagnostics_;
+};
+
+/*
+ * Run in daemon mode. The first line of input is the command. This can be 'quit' which ends
+ * the daemon mode. Each subsequent line is a single parameter to the command. The end of a
+ * invocation is signaled by providing an empty line. At any point, an EOF signal or the
+ * command 'quit' will end the daemon mode.
+ */
+class DaemonCommand : public Command {
+ public:
+  explicit DaemonCommand(IDiagnostics* diagnostics) : Command("daemon", "m"),
+                                                      diagnostics_(diagnostics) {
+    SetDescription("Runs aapt in daemon mode. Each subsequent line is a single parameter to the\n"
+        "command. The end of an invocation is signaled by providing an empty line.");
+  }
+
+  int Action(const std::vector<std::string>& /* args */) override {
+    std::cout << "Ready" << std::endl;
+
+    while (true) {
+      std::vector<std::string> raw_args;
+      for (std::string line; std::getline(std::cin, line) && !line.empty();) {
+        raw_args.push_back(line);
+      }
+
+      if (!std::cin) {
+        break;
+      }
+
+      // An empty command does nothing.
+      if (raw_args.empty()) {
+        continue;
+      }
+
+      // End the dameon
+      if (raw_args[0] == "quit") {
+        break;
+      }
+
+      std::vector<StringPiece> args;
+      args.insert(args.end(), raw_args.begin(), raw_args.end());
+      if (MainCommand(diagnostics_).Execute(args, &std::cerr) != 0) {
+        std::cerr << "Error" << std::endl;
+      }
+      std::cerr << "Done" << std::endl;
+    }
+    std::cout << "Exiting daemon" << std::endl;
+
+    return 0;
+  }
+
+ private:
+  IDiagnostics* diagnostics_;
+};
 
 }  // namespace aapt
 
 int MainImpl(int argc, char** argv) {
-  if (argc < 2) {
-    std::cerr << "no command specified\n";
-    aapt::PrintUsage();
+  if (argc < 1) {
     return -1;
   }
 
-  argv += 1;
-  argc -= 1;
-
-  aapt::StdErrDiagnostics diagnostics;
-
   // Collect the arguments starting after the program name and command name.
   std::vector<StringPiece> args;
   for (int i = 1; i < argc; i++) {
     args.push_back(argv[i]);
   }
 
-  const StringPiece command(argv[0]);
-  if (command != "daemon" && command != "m") {
-    // Single execution.
-    const int result = aapt::ExecuteCommand(command, args, &diagnostics);
-    if (result < 0) {
-      aapt::PrintUsage();
-    }
-    return result;
-  }
+  // Add the daemon subcommand here so it cannot be called while executing the daemon
+  aapt::StdErrDiagnostics diagnostics;
+  auto main_command = new aapt::MainCommand(&diagnostics);
+  main_command->AddOptionalSubcommand(aapt::util::make_unique<aapt::DaemonCommand>(&diagnostics));
 
-  aapt::RunDaemon(&diagnostics);
-  return 0;
+  return main_command->Execute(args, &std::cerr);
 }
 
 int main(int argc, char** argv) {
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 089c9e2..8719a23 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -208,6 +208,15 @@
   }
 };
 
+// A chunk of text in the XML string within a CDATA tags.
+class CdataSegmentNode : public SegmentNode {
+ public:
+
+  void Build(StringBuilder* builder) const override {
+    builder->AppendText(data, /* preserve_spaces */ true);
+  }
+};
+
 // A tag that will be encoded into the final flattened string. Tags like <b> or <i>.
 class SpanNode : public Node {
  public:
@@ -244,6 +253,7 @@
   std::vector<Node*> node_stack;
   node_stack.push_back(&root);
 
+  bool cdata_block = false;
   bool saw_span_node = false;
   SegmentNode* first_segment = nullptr;
   SegmentNode* last_segment = nullptr;
@@ -253,11 +263,15 @@
     const xml::XmlPullParser::Event event = parser->event();
 
     // First take care of any SegmentNodes that should be created.
-    if (event == xml::XmlPullParser::Event::kStartElement ||
-        event == xml::XmlPullParser::Event::kEndElement) {
+    if (event == xml::XmlPullParser::Event::kStartElement
+        || event == xml::XmlPullParser::Event::kEndElement
+        || event == xml::XmlPullParser::Event::kCdataStart
+        || event == xml::XmlPullParser::Event::kCdataEnd) {
       if (!current_text.empty()) {
-        std::unique_ptr<SegmentNode> segment_node = util::make_unique<SegmentNode>();
+        std::unique_ptr<SegmentNode> segment_node = (cdata_block)
+            ? util::make_unique<CdataSegmentNode>() : util::make_unique<SegmentNode>();
         segment_node->data = std::move(current_text);
+
         last_segment = node_stack.back()->AddChild(std::move(segment_node));
         if (first_segment == nullptr) {
           first_segment = last_segment;
@@ -333,6 +347,16 @@
         }
       } break;
 
+      case xml::XmlPullParser::Event::kCdataStart: {
+        cdata_block = true;
+        break;
+      }
+
+      case xml::XmlPullParser::Event::kCdataEnd: {
+        cdata_block = false;
+        break;
+      }
+
       default:
         // ignore.
         break;
@@ -777,7 +801,8 @@
   if (allow_raw_value) {
     // We can't parse this so return a RawString if we are allowed.
     return util::make_unique<RawString>(
-        table_->string_pool.MakeRef(raw_value, StringPool::Context(config_)));
+        table_->string_pool.MakeRef(util::TrimWhitespace(raw_value),
+                                    StringPool::Context(config_)));
   }
   return {};
 }
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 41b4041..ee496d5 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -497,6 +497,24 @@
   EXPECT_THAT(style->entries[0].key.name, Eq(make_value(test::ParseNameOrDie("android:attr/bar"))));
 }
 
+TEST_F(ResourceParserTest, ParseStyleWithRawStringItem) {
+  std::string input = R"(
+      <style name="foo">
+        <item name="bar">
+          com.helloworld.AppClass
+        </item>
+      </style>)";
+  ASSERT_TRUE(TestParse(input));
+
+  Style* style = test::GetValue<Style>(&table_, "style/foo");
+  ASSERT_THAT(style, NotNull());
+  EXPECT_THAT(style->entries[0].value, NotNull());
+  RawString* value = ValueCast<RawString>(style->entries[0].value.get());
+  EXPECT_THAT(value, NotNull());
+  EXPECT_THAT(*value->value, StrEq(R"(com.helloworld.AppClass)"));
+}
+
+
 TEST_F(ResourceParserTest, ParseStyleWithInferredParent) {
   ASSERT_TRUE(TestParse(R"(<style name="foo.bar"/>)"));
 
@@ -971,4 +989,40 @@
   ASSERT_FALSE(TestParse(input));
 }
 
+TEST_F(ResourceParserTest, ParseCData) {
+  std::string input = R"(
+      <string name="foo"><![CDATA[some text and ' apostrophe]]></string>)";
+
+  ASSERT_TRUE(TestParse(input));
+  String* output = test::GetValue<String>(&table_, "string/foo");
+  ASSERT_THAT(output, NotNull());
+  EXPECT_THAT(*output, StrValueEq("some text and ' apostrophe"));
+
+  // Double quotes should not change the state of whitespace processing
+  input = R"(<string name="foo2">Hello<![CDATA[ "</string>' ]]>      World</string>)";
+  ASSERT_TRUE(TestParse(input));
+  output = test::GetValue<String>(&table_, "string/foo2");
+  ASSERT_THAT(output, NotNull());
+  EXPECT_THAT(*output, StrValueEq(std::string("Hello \"</string>'  World").data()));
+
+  // Cdata blocks should not have their whitespace trimmed
+  input = R"(<string name="foo3">     <![CDATA[ text ]]>     </string>)";
+  ASSERT_TRUE(TestParse(input));
+  output = test::GetValue<String>(&table_, "string/foo3");
+  ASSERT_THAT(output, NotNull());
+  EXPECT_THAT(*output, StrValueEq(std::string(" text ").data()));
+
+  input = R"(<string name="foo4">     <![CDATA[]]>     </string>)";
+  ASSERT_TRUE(TestParse(input));
+  output = test::GetValue<String>(&table_, "string/foo4");
+  ASSERT_THAT(output, NotNull());
+  EXPECT_THAT(*output, StrValueEq(std::string("").data()));
+
+  input = R"(<string name="foo5">     <![CDATA[    ]]>     </string>)";
+  ASSERT_TRUE(TestParse(input));
+  output = test::GetValue<String>(&table_, "string/foo5");
+  ASSERT_THAT(output, NotNull());
+  EXPECT_THAT(*output, StrValueEq(std::string("    ").data()));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 560077c..c48765b 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -797,16 +797,20 @@
     : preserve_spaces_(preserve_spaces), quote_(preserve_spaces) {
 }
 
-StringBuilder& StringBuilder::AppendText(const std::string& text) {
+StringBuilder& StringBuilder::AppendText(const std::string& text, bool preserve_spaces) {
   if (!error_.empty()) {
     return *this;
   }
 
+  // Enable preserving spaces if it is enabled for this append or the StringBuilder was constructed
+  // to preserve spaces
+  preserve_spaces = (preserve_spaces) ? preserve_spaces : preserve_spaces_;
+
   const size_t previous_len = xml_string_.text.size();
   Utf8Iterator iter(text);
   while (iter.HasNext()) {
     char32_t codepoint = iter.Next();
-    if (!quote_ && iswspace(codepoint)) {
+    if (!preserve_spaces && !quote_ && iswspace(codepoint)) {
       if (!last_codepoint_was_space_) {
         // Emit a space if it's the first.
         xml_string_.text += ' ';
@@ -827,7 +831,6 @@
           case U't':
             xml_string_.text += '\t';
             break;
-
           case U'n':
             xml_string_.text += '\n';
             break;
@@ -855,12 +858,12 @@
             break;
         }
       }
-    } else if (!preserve_spaces_ && codepoint == U'"') {
+    } else if (!preserve_spaces && codepoint == U'"') {
       // Only toggle the quote state when we are not preserving spaces.
       quote_ = !quote_;
 
-    } else if (!quote_ && codepoint == U'\'') {
-      // This should be escaped.
+    } else if (!preserve_spaces && !quote_ && codepoint == U'\'') {
+      // This should be escaped when we are not preserving spaces
       error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str());
       return *this;
 
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 7af2fe0..410ef28 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -267,8 +267,10 @@
   // single quotations can be used without escaping them.
   explicit StringBuilder(bool preserve_spaces = false);
 
-  // Appends a chunk of text.
-  StringBuilder& AppendText(const std::string& text);
+  // Appends a chunk of text. If preserve_spaces is true, whitespace removal is not performed, and
+  // single quotations can be used without escaping them for this append. Otherwise, the
+  // StringBuilder will behave as it was constructed.
+  StringBuilder& AppendText(const std::string& text, bool preserve_spaces = false);
 
   // Starts a Span (tag) with the given name. The name is expected to be of the form:
   //  "tag_name;attr1=value;attr2=value;"
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 11f3fa3..5ce4640 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -254,6 +254,29 @@
 TEST(ResourceUtilsTest, StringBuilderPreserveSpaces) {
   EXPECT_THAT(ResourceUtils::StringBuilder(true /*preserve_spaces*/).AppendText("\"").to_string(),
               Eq("\""));
+
+  // Single quotes should be able to be used without escaping them when preserving spaces and the
+  // spaces should not be trimmed
+  EXPECT_THAT(ResourceUtils::StringBuilder()
+                  .AppendText("    hey guys ")
+                  .AppendText(" 'this is so cool' ", /* preserve_spaces */ true)
+                  .AppendText(" wow    ")
+                  .to_string(),
+              Eq(" hey guys  'this is so cool'  wow "));
+
+  // Reading a double quote while preserving spaces should not change the quote state
+  EXPECT_THAT(ResourceUtils::StringBuilder()
+                  .AppendText("    hey guys ")
+                  .AppendText(" \"this is so cool' ", /* preserve_spaces */ true)
+                  .AppendText(" wow    ")
+                  .to_string(),
+              Eq(" hey guys  \"this is so cool'  wow "));
+  EXPECT_THAT(ResourceUtils::StringBuilder()
+                  .AppendText("    hey guys\"  ")
+                  .AppendText(" \"this is so cool' ", /* preserve_spaces */ true)
+                  .AppendText(" wow  \"  ")
+                  .to_string(),
+              Eq(" hey guys   \"this is so cool'  wow   "));
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index b37e1fb..8eabd32 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -367,7 +367,7 @@
 static bool EncodeString(const std::string& str, const bool utf8, BigBuffer* out,
                          IDiagnostics* diag) {
   if (utf8) {
-    const std::string& encoded = str;
+    const std::string& encoded = util::Utf8ToModifiedUtf8(str);
     const ssize_t utf16_length = utf8_to_utf16_length(
         reinterpret_cast<const uint8_t*>(encoded.data()), encoded.size());
     CHECK(utf16_length >= 0);
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 4b3afe2..0778564 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -303,6 +303,25 @@
   }
 }
 
+TEST(StringPoolTest, FlattenModifiedUTF8) {
+  using namespace android;  // For NO_ERROR on Windows.
+  StdErrDiagnostics diag;
+  StringPool pool;
+  StringPool::Ref ref_a = pool.MakeRef("\xF0\x90\x90\x80"); // 𐐀 (U+10400)
+  StringPool::Ref ref_b = pool.MakeRef("foo \xF0\x90\x90\xB7 bar"); // 𐐷 (U+10437)
+  StringPool::Ref ref_c = pool.MakeRef("\xF0\x90\x90\x80\xF0\x90\x90\xB7");
+
+  BigBuffer buffer(1024);
+  StringPool::FlattenUtf8(&buffer, pool, &diag);
+  std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
+
+  // Check that the 4 byte utf-8 codepoint is encoded using 2 3 byte surrogate pairs
+  ResStringPool test;
+  ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
+  EXPECT_THAT(util::GetString(test, 0), Eq("\xED\xA0\x81\xED\xB0\x80"));
+  EXPECT_THAT(util::GetString(test, 1), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar"));
+  EXPECT_THAT(util::GetString(test, 2), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7"));
+}
 
 TEST(StringPoolTest, MaxEncodingLength) {
   StdErrDiagnostics diag;
diff --git a/tools/aapt2/cmd/Command.cpp b/tools/aapt2/cmd/Command.cpp
new file mode 100644
index 0000000..09411b9
--- /dev/null
+++ b/tools/aapt2/cmd/Command.cpp
@@ -0,0 +1,222 @@
+/*
+ * 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 <iomanip>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "androidfw/StringPiece.h"
+
+#include "util/Util.h"
+
+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();
+    return true;
+  };
+
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
+}
+
+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());
+    return true;
+  };
+
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
+}
+
+void Command::AddOptionalFlag(const StringPiece& name,
+    const StringPiece& description,
+    Maybe<std::string>* value) {
+  auto func = [value](const StringPiece& arg) -> bool {
+    *value = arg.to_string();
+    return true;
+  };
+
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+}
+
+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());
+    return true;
+  };
+
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+}
+
+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});
+}
+
+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});
+}
+
+void Command::AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand) {
+  subcommand->fullname_ = name_ + " " + subcommand->name_;
+  subcommands_.push_back(std::move(subcommand));
+}
+
+void Command::SetDescription(const android::StringPiece& description) {
+  description_ = description.to_string();
+}
+
+void Command::Usage(std::ostream* out) {
+  constexpr size_t kWidth = 50;
+
+  *out << fullname_;
+
+  if (!subcommands_.empty()) {
+    *out << " [subcommand]";
+  }
+
+  *out << " [options]";
+  for (const Flag& flag : flags_) {
+    if (flag.required) {
+      *out << " " << flag.name << " arg";
+    }
+  }
+
+  *out << " files...\n";
+
+  if (!subcommands_.empty()) {
+    *out << "\nSubcommands:\n";
+    for (auto& subcommand : subcommands_) {
+      std::string argline = subcommand->name_;
+
+      // Split the description by newlines and write out the argument (which is
+      // empty after the first line) followed by the description line. This will make sure
+      // that multiline descriptions are still right justified and aligned.
+      for (StringPiece line : util::Tokenize(subcommand->description_, '\n')) {
+        *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
+        argline = " ";
+      }
+    }
+  }
+
+  *out << "\nOptions:\n";
+
+  for (const Flag& flag : flags_) {
+    std::string argline = flag.name;
+    if (flag.num_args > 0) {
+      argline += " arg";
+    }
+
+    // Split the description by newlines and write out the argument (which is
+    // empty after the first line) followed by the description line. This will make sure
+    // that multiline descriptions are still right justified and aligned.
+    for (StringPiece line : util::Tokenize(flag.description, '\n')) {
+      *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
+      argline = " ";
+    }
+  }
+  *out << " " << std::setw(kWidth) << std::left << "-h"
+       << "Displays this help menu\n";
+  out->flush();
+}
+
+int Command::Execute(const std::vector<android::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];
+    if (*(arg.data()) != '-') {
+      // Continue parsing as the sub command if the first argument matches one of the subcommands
+      if (i == 0) {
+        for (auto& subcommand : subcommands_) {
+          if (arg == subcommand->name_ || arg==subcommand->short_name_) {
+            return subcommand->Execute(
+                std::vector<android::StringPiece>(args.begin() + 1, args.end()), out_error);
+          }
+        }
+      }
+
+      file_args.push_back(arg.to_string());
+      continue;
+    }
+
+    if (arg == "-h" || arg == "--help") {
+      Usage(out_error);
+      return 1;
+    }
+
+    bool match = false;
+    for (Flag& flag : flags_) {
+      if (arg == flag.name) {
+        if (flag.num_args > 0) {
+          i++;
+          if (i >= args.size()) {
+            *out_error << flag.name << " missing argument.\n\n";
+            Usage(out_error);
+            return false;
+          }
+          flag.action(args[i]);
+        } else {
+          flag.action({});
+        }
+        flag.parsed = true;
+        match = true;
+        break;
+      }
+    }
+
+    if (!match) {
+      *out_error << "unknown option '" << arg << "'.\n\n";
+      Usage(out_error);
+      return 1;
+    }
+  }
+
+  for (const Flag& flag : flags_) {
+    if (flag.required && !flag.parsed) {
+      *out_error << "missing required flag " << flag.name << "\n\n";
+      Usage(out_error);
+      return 1;
+    }
+  }
+
+  return Action(file_args);
+}
+
+}  // namespace aapt
diff --git a/tools/aapt2/cmd/Command.h b/tools/aapt2/cmd/Command.h
new file mode 100644
index 0000000..71dc6fe
--- /dev/null
+++ b/tools/aapt2/cmd/Command.h
@@ -0,0 +1,90 @@
+/*
+ * 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_COMMAND_H
+#define AAPT_COMMAND_H
+
+#include <functional>
+#include <ostream>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "androidfw/StringPiece.h"
+
+#include "util/Maybe.h"
+
+namespace aapt {
+
+class Command {
+ public:
+  explicit Command(const android::StringPiece& name) : name_(name.to_string()),
+                                                       fullname_(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()) {}
+  virtual ~Command() = default;
+
+  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);
+  void AddOptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
+      Maybe<std::string>* value);
+  void AddOptionalFlagList(const android::StringPiece& name,
+      const android::StringPiece& description, std::vector<std::string>* value);
+  void AddOptionalFlagList(const android::StringPiece& name,
+      const android::StringPiece& description, std::unordered_set<std::string>* value);
+  void AddOptionalSwitch(const android::StringPiece& name, const android::StringPiece& description,
+      bool* value);
+  void AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand);
+
+  void SetDescription(const android::StringPiece& name);
+
+  /** 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.
+   **/
+  int Execute(const std::vector<android::StringPiece>& args, std::ostream* outError);
+
+  /** 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;
+
+    bool parsed;
+  };
+
+  std::string description_;
+  std::string name_;
+  std::string short_name_;
+  std::string fullname_;
+  std::vector<Flag> flags_;
+  std::vector<std::unique_ptr<Command>> subcommands_;
+};
+
+}  // namespace aapt
+
+#endif  // AAPT_COMMAND_H
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index a17a0d3..36b5578 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-#include <dirent.h>
+#include "Compile.h"
 
+#include <dirent.h>
 #include <string>
 
 #include "android-base/errors.h"
@@ -27,7 +28,6 @@
 
 #include "ConfigDescription.h"
 #include "Diagnostics.h"
-#include "Flags.h"
 #include "ResourceParser.h"
 #include "ResourceTable.h"
 #include "cmd/Util.h"
@@ -121,17 +121,6 @@
                           extension.to_string(), config_str.to_string(), config};
 }
 
-struct CompileOptions {
-  std::string output_path;
-  Maybe<std::string> res_dir;
-  Maybe<std::string> generate_text_symbols_path;
-  Maybe<Visibility::Level> visibility;
-  bool pseudolocalize = false;
-  bool no_png_crunch = false;
-  bool legacy_mode = false;
-  bool verbose = false;
-};
-
 static std::string BuildIntermediateContainerFilename(const ResourcePathData& data) {
   std::stringstream name;
   name << data.resource_dir;
@@ -701,10 +690,6 @@
     return 0;
   }
 
-  bool IsAutoNamespace() override {
-    return false;
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(CompileContext);
 
@@ -712,50 +697,21 @@
   bool verbose_ = false;
 };
 
-// Entry point for compilation phase. Parses arguments and dispatches to the correct steps.
-int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
-  CompileContext context(diagnostics);
-  CompileOptions options;
+int CompileCommand::Action(const std::vector<std::string>& args) {
+  CompileContext context(diagnostic_);
+  context.SetVerbose(options_.verbose);
 
-  bool verbose = false;
-  Maybe<std::string> visibility;
-  Flags flags =
-      Flags()
-          .RequiredFlag("-o", "Output path", &options.output_path)
-          .OptionalFlag("--dir", "Directory to scan for resources", &options.res_dir)
-          .OptionalFlag("--output-text-symbols",
-                        "Generates a text file containing the resource symbols in the\n"
-                        "specified file",
-                        &options.generate_text_symbols_path)
-          .OptionalSwitch("--pseudo-localize",
-                          "Generate resources for pseudo-locales "
-                          "(en-XA and ar-XB)",
-                          &options.pseudolocalize)
-          .OptionalSwitch("--no-crunch", "Disables PNG processing", &options.no_png_crunch)
-          .OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
-                          &options.legacy_mode)
-          .OptionalSwitch("-v", "Enables verbose logging", &verbose)
-          .OptionalFlag("--visibility",
-                        "Sets the visibility of the compiled resources to the specified\n"
-                        "level. Accepted levels: public, private, default",
-                        &visibility);
-  if (!flags.Parse("aapt2 compile", args, &std::cerr)) {
-    return 1;
-  }
-
-  context.SetVerbose(verbose);
-
-  if (visibility) {
-    if (visibility.value() == "public") {
-      options.visibility = Visibility::Level::kPublic;
-    } else if (visibility.value() == "private") {
-      options.visibility = Visibility::Level::kPrivate;
-    } else if (visibility.value() == "default") {
-      options.visibility = Visibility::Level::kUndefined;
+  if (visibility_) {
+    if (visibility_.value() == "public") {
+      options_.visibility = Visibility::Level::kPublic;
+    } else if (visibility_.value() == "private") {
+      options_.visibility = Visibility::Level::kPrivate;
+    } else if (visibility_.value() == "default") {
+      options_.visibility = Visibility::Level::kUndefined;
     } else {
       context.GetDiagnostics()->Error(
           DiagMessage() << "Unrecognized visibility level passes to --visibility: '"
-                        << visibility.value() << "'. Accepted levels: public, private, default");
+                        << visibility_.value() << "'. Accepted levels: public, private, default");
       return 1;
     }
   }
@@ -763,25 +719,25 @@
   std::unique_ptr<IArchiveWriter> archive_writer;
 
   std::vector<ResourcePathData> input_data;
-  if (options.res_dir) {
-    if (!flags.GetArgs().empty()) {
+  if (options_.res_dir) {
+    if (!args.empty()) {
       // Can't have both files and a resource directory.
       context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
-      flags.Usage("aapt2 compile", &std::cerr);
+      Usage(&std::cerr);
       return 1;
     }
 
-    if (!LoadInputFilesFromDir(&context, options, &input_data)) {
+    if (!LoadInputFilesFromDir(&context, options_, &input_data)) {
       return 1;
     }
 
-    archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options.output_path);
+    archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path);
 
   } else {
-    input_data.reserve(flags.GetArgs().size());
+    input_data.reserve(args.size());
 
     // Collect data from the path for each input file.
-    for (const std::string& arg : flags.GetArgs()) {
+    for (const std::string& arg : args) {
       std::string error_str;
       if (Maybe<ResourcePathData> path_data = ExtractResourcePathData(arg, &error_str)) {
         input_data.push_back(std::move(path_data.value()));
@@ -791,7 +747,7 @@
       }
     }
 
-    archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options.output_path);
+    archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options_.output_path);
   }
 
   if (!archive_writer) {
@@ -800,7 +756,7 @@
 
   bool error = false;
   for (ResourcePathData& path_data : input_data) {
-    if (options.verbose) {
+    if (options_.verbose) {
       context.GetDiagnostics()->Note(DiagMessage(path_data.source) << "processing");
     }
 
@@ -821,21 +777,21 @@
       if (*type != ResourceType::kRaw) {
         if (path_data.extension == "xml") {
           compile_func = &CompileXml;
-        } else if ((!options.no_png_crunch && path_data.extension == "png")
+        } else if ((!options_.no_png_crunch && path_data.extension == "png")
             || path_data.extension == "9.png") {
           compile_func = &CompilePng;
         }
       }
     } else {
       context.GetDiagnostics()->Error(DiagMessage()
-                                      << "invalid file path '" << path_data.source << "'");
+          << "invalid file path '" << path_data.source << "'");
       error = true;
       continue;
     }
 
     // Treat periods as a reserved character that should not be present in a file name
     // Legacy support for AAPT which did not reserve periods
-    if (compile_func != &CompileFile && !options.legacy_mode
+    if (compile_func != &CompileFile && !options_.legacy_mode
         && std::count(path_data.name.begin(), path_data.name.end(), '.') != 0) {
       error = true;
       context.GetDiagnostics()->Error(DiagMessage() << "resource file '" << path_data.source.path
@@ -846,7 +802,7 @@
 
     // Compile the file.
     const std::string out_path = BuildIntermediateContainerFilename(path_data);
-    error |= !compile_func(&context, options, path_data, archive_writer.get(), out_path);
+    error |= !compile_func(&context, options_, path_data, archive_writer.get(), out_path);
   }
   return error ? 1 : 0;
 }
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index d95cf1c..4151952 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -1,13 +1,69 @@
+/*
+ * 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 AAPT2_COMPILE_H
 #define AAPT2_COMPILE_H
 
 #include "androidfw/StringPiece.h"
 
+#include "Command.h"
 #include "Diagnostics.h"
+#include "ResourceTable.h"
 
 namespace aapt {
 
-  int Compile(const std::vector<android::StringPiece>& args, IDiagnostics* diagnostics);
+struct CompileOptions {
+  std::string output_path;
+  Maybe<std::string> res_dir;
+  Maybe<std::string> generate_text_symbols_path;
+  Maybe<Visibility::Level> visibility;
+  bool pseudolocalize = false;
+  bool no_png_crunch = false;
+  bool legacy_mode = false;
+  bool verbose = false;
+};
+
+class CompileCommand : public Command {
+ public:
+  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);
+    AddOptionalFlag("--output-text-symbols",
+        "Generates a text file containing the resource symbols in the\n"
+            "specified file", &options_.generate_text_symbols_path);
+    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);
+    AddOptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
+        &options_.legacy_mode);
+    AddOptionalSwitch("-v", "Enables verbose logging", &options_.verbose);
+    AddOptionalFlag("--visibility",
+        "Sets the visibility of the compiled resources to the specified\n"
+            "level. Accepted levels: public, private, default", &visibility_);
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+
+ private:
+  IDiagnostics* diagnostic_;
+  CompileOptions options_;
+  Maybe<std::string> visibility_;
+};
 
 }// namespace aapt
 
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index 212f2cf..d21addf 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -23,7 +23,8 @@
 
 namespace aapt {
 
-int TestCompile(std::string path, std::string outDir, bool legacy, StdErrDiagnostics& diag) {
+int TestCompile(const std::string& path, const std::string& outDir, bool legacy,
+    StdErrDiagnostics& diag) {
   std::vector<android::StringPiece> args;
   args.push_back(path);
   args.push_back("-o");
@@ -32,7 +33,7 @@
   if (legacy) {
     args.push_back("--legacy");
   }
-  return aapt::Compile(args, &diag);
+  return CompileCommand(&diag).Execute(args, &std::cerr);
 }
 
 TEST(CompilerTest, MultiplePeriods) {
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 3c8beaa..d57eaa1 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
+#include "Convert.h"
+
 #include <vector>
 
 #include "android-base/macros.h"
 #include "androidfw/StringPiece.h"
 
-#include "Flags.h"
 #include "LoadedApk.h"
 #include "ValueVisitor.h"
 #include "cmd/Util.h"
@@ -310,10 +311,6 @@
     return 0u;
   }
 
-  bool IsAutoNamespace() override {
-    return false;
-  }
-
   bool verbose_ = false;
   std::string package_;
 
@@ -325,37 +322,18 @@
   StdErrDiagnostics diag_;
 };
 
-int Convert(const vector<StringPiece>& args) {
+const char* ConvertCommand::kOutputFormatProto = "proto";
+const char* ConvertCommand::kOutputFormatBinary = "binary";
 
-  static const char* kOutputFormatProto = "proto";
-  static const char* kOutputFormatBinary = "binary";
+int ConvertCommand::Action(const std::vector<std::string>& args) {
+  if (args.size() != 1) {
+    std::cerr << "must supply a single proto APK\n";
+    Usage(&std::cerr);
+    return 1;
+  }
 
   Context context;
-  std::string output_path;
-  Maybe<std::string> output_format;
-  TableFlattenerOptions options;
-  Flags flags =
-      Flags()
-          .RequiredFlag("-o", "Output path", &output_path)
-          .OptionalFlag("--output-format", StringPrintf("Format of the output. Accepted values are "
-                        "'%s' and '%s'. When not set, defaults to '%s'.", kOutputFormatProto,
-                        kOutputFormatBinary, kOutputFormatBinary), &output_format)
-          .OptionalSwitch("--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)
-          .OptionalSwitch("-v", "Enables verbose logging", &context.verbose_);
-  if (!flags.Parse("aapt2 convert", args, &std::cerr)) {
-    return 1;
-  }
-
-  if (flags.GetArgs().size() != 1) {
-    std::cerr << "must supply a single proto APK\n";
-    flags.Usage("aapt2 convert", &std::cerr);
-    return 1;
-  }
-
-  const StringPiece& path = flags.GetArgs()[0];
+  const StringPiece& path = args[0];
   unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics());
   if (apk == nullptr) {
     context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK");
@@ -371,24 +349,24 @@
   context.package_ = app_info.value().package;
 
   unique_ptr<IArchiveWriter> writer =
-      CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path);
+      CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path_);
   if (writer == nullptr) {
     return 1;
   }
 
   unique_ptr<IApkSerializer> serializer;
-  if (!output_format || output_format.value() == kOutputFormatBinary) {
-    serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options));
-  } else if (output_format.value() == kOutputFormatProto) {
+  if (!output_format_ || output_format_.value() == ConvertCommand::kOutputFormatBinary) {
+
+    serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options_));
+  } else if (output_format_.value() == ConvertCommand::kOutputFormatProto) {
     serializer.reset(new ProtoApkSerializer(&context, apk->GetSource()));
   } else {
     context.GetDiagnostics()->Error(DiagMessage(path)
-                                    << "Invalid value for flag --output-format: "
-                                    << output_format.value());
+        << "Invalid value for flag --output-format: "
+        << output_format_.value());
     return 1;
   }
 
-
   return ConvertApk(&context, std::move(apk), serializer.get(), writer.get()) ? 0 : 1;
 }
 
diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h
new file mode 100644
index 0000000..fcec23d
--- /dev/null
+++ b/tools/aapt2/cmd/Convert.h
@@ -0,0 +1,54 @@
+/*
+ * 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 AAPT2_CONVERT_H
+#define AAPT2_CONVERT_H
+
+#include "Command.h"
+#include "format/binary/TableFlattener.h"
+
+namespace aapt {
+
+class ConvertCommand : public Command {
+ public:
+  explicit ConvertCommand() : Command("convert") {
+    SetDescription("Converts an apk between binary and proto formats.");
+    AddRequiredFlag("-o", "Output path", &output_path_);
+    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);
+    AddOptionalSwitch("-v", "Enables verbose logging", &verbose_);
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+
+ private:
+  const static char* kOutputFormatProto;
+  const static char* kOutputFormatBinary;
+
+  TableFlattenerOptions options_;
+  std::string output_path_;
+  Maybe<std::string> output_format_;
+  bool verbose_ = false;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_CONVERT_H
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 16c7bba..262f4fc4e 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
+#include "Diff.h"
+
 #include "android-base/macros.h"
 
-#include "Flags.h"
 #include "LoadedApk.h"
 #include "ValueVisitor.h"
 #include "process/IResourceTableConsumer.h"
@@ -64,10 +65,6 @@
     return 0;
   }
 
-  bool IsAutoNamespace() override {
-    return false;
-  }
-
  private:
   std::string empty_;
   StdErrDiagnostics diagnostics_;
@@ -348,23 +345,18 @@
   VisitAllValuesInTable(table, &visitor);
 }
 
-int Diff(const std::vector<StringPiece>& args) {
+int DiffCommand::Action(const std::vector<std::string>& args) {
   DiffContext context;
 
-  Flags flags;
-  if (!flags.Parse("aapt2 diff", args, &std::cerr)) {
-    return 1;
-  }
-
-  if (flags.GetArgs().size() != 2u) {
+  if (args.size() != 2u) {
     std::cerr << "must have two apks as arguments.\n\n";
-    flags.Usage("aapt2 diff", &std::cerr);
+    Usage(&std::cerr);
     return 1;
   }
 
   IDiagnostics* diag = context.GetDiagnostics();
-  std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(flags.GetArgs()[0], diag);
-  std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(flags.GetArgs()[1], diag);
+  std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(args[0], diag);
+  std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(args[1], diag);
   if (!apk_a || !apk_b) {
     return 1;
   }
diff --git a/tools/aapt2/cmd/Diff.h b/tools/aapt2/cmd/Diff.h
new file mode 100644
index 0000000..c388888
--- /dev/null
+++ b/tools/aapt2/cmd/Diff.h
@@ -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.
+ */
+
+#ifndef AAPT2_DIFF_H
+#define AAPT2_DIFF_H
+
+#include "Command.h"
+
+namespace aapt {
+
+class DiffCommand : public Command {
+ public:
+  explicit DiffCommand() : Command("diff") {
+    SetDescription("Prints the differences in resources of two apks.");
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_DIFF_H
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index fd133f3..8b1f672 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "Dump.h"
+
 #include <cinttypes>
 #include <vector>
 
@@ -22,7 +24,6 @@
 
 #include "Debug.h"
 #include "Diagnostics.h"
-#include "Flags.h"
 #include "format/Container.h"
 #include "format/binary/BinaryResourceParser.h"
 #include "format/proto/ProtoDeserialize.h"
@@ -38,13 +39,6 @@
 
 namespace aapt {
 
-struct DumpOptions {
-  DebugPrintTableOptions print_options;
-
-  // The path to a file within an APK to dump.
-  Maybe<std::string> file_to_dump_path;
-};
-
 static const char* ResourceFileTypeToString(const ResourceFile::Type& type) {
   switch (type) {
     case ResourceFile::Type::kPng:
@@ -298,10 +292,6 @@
     return 0;
   }
 
-  bool IsAutoNamespace() override {
-    return false;
-  }
-
  private:
   StdErrDiagnostics diagnostics_;
   bool verbose_ = false;
@@ -309,29 +299,13 @@
 
 }  // namespace
 
-// Entry point for dump command.
-int Dump(const std::vector<StringPiece>& args) {
-  bool verbose = false;
-  bool no_values = false;
-  DumpOptions options;
-  Flags flags = Flags()
-                    .OptionalSwitch("--no-values",
-                                    "Suppresses output of values when displaying resource tables.",
-                                    &no_values)
-                    .OptionalFlag("--file", "Dumps the specified file from the APK passed as arg.",
-                                  &options.file_to_dump_path)
-                    .OptionalSwitch("-v", "increase verbosity of output", &verbose);
-  if (!flags.Parse("aapt2 dump", args, &std::cerr)) {
-    return 1;
-  }
-
+int DumpCommand::Action(const std::vector<std::string>& args) {
   DumpContext context;
-  context.SetVerbose(verbose);
-
-  options.print_options.show_sources = true;
-  options.print_options.show_values = !no_values;
-  for (const std::string& arg : flags.GetArgs()) {
-    if (!TryDumpFile(&context, arg, options)) {
+  context.SetVerbose(verbose_);
+  options_.print_options.show_sources = true;
+  options_.print_options.show_values = !no_values_;
+  for (const std::string& arg : args) {
+    if (!TryDumpFile(&context, arg, options_)) {
       return 1;
     }
   }
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
new file mode 100644
index 0000000..4893c8b
--- /dev/null
+++ b/tools/aapt2/cmd/Dump.h
@@ -0,0 +1,54 @@
+/*
+ * 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 AAPT2_DUMP_H
+#define AAPT2_DUMP_H
+
+#include "Command.h"
+#include "Debug.h"
+
+namespace aapt {
+
+struct DumpOptions {
+  DebugPrintTableOptions print_options;
+
+  // The path to a file within an APK to dump.
+  Maybe<std::string> file_to_dump_path;
+};
+
+class DumpCommand : public Command {
+ public:
+  DumpCommand() : Command("dump", "d") {
+    SetDescription("Prints resource and manifest information.");
+    AddOptionalSwitch("--no-values", "Suppresses output of values when displaying resource tables.",
+        &no_values_);
+    AddOptionalFlag("--file", "Dumps the specified file from the APK passed as arg.",
+        &options_.file_to_dump_path);
+    AddOptionalSwitch("-v", "increase verbosity of output", &verbose_);
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+
+ private:
+  DumpOptions options_;
+
+  bool verbose_ = false;
+  bool no_values_ = false;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_DUMP_H
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 1a2da7f..c94b847 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "Link.h"
+
 #include <sys/stat.h>
 #include <cinttypes>
 
@@ -29,7 +31,6 @@
 
 #include "AppInfo.h"
 #include "Debug.h"
-#include "Flags.h"
 #include "LoadedApk.h"
 #include "Locale.h"
 #include "NameMangler.h"
@@ -74,74 +75,6 @@
 
 namespace aapt {
 
-enum class OutputFormat {
-  kApk,
-  kProto,
-};
-
-struct LinkOptions {
-  std::string output_path;
-  std::string manifest_path;
-  std::vector<std::string> include_paths;
-  std::vector<std::string> overlay_files;
-  std::vector<std::string> assets_dirs;
-  bool output_to_directory = false;
-  bool auto_add_overlay = false;
-  OutputFormat output_format = OutputFormat::kApk;
-
-  // Java/Proguard options.
-  Maybe<std::string> generate_java_class_path;
-  Maybe<std::string> custom_java_package;
-  std::set<std::string> extra_java_packages;
-  Maybe<std::string> generate_text_symbols_path;
-  Maybe<std::string> generate_proguard_rules_path;
-  Maybe<std::string> generate_main_dex_proguard_rules_path;
-  bool generate_conditional_proguard_rules = false;
-  bool generate_non_final_ids = false;
-  std::vector<std::string> javadoc_annotations;
-  Maybe<std::string> private_symbols;
-
-  // Optimizations/features.
-  bool no_auto_version = false;
-  bool no_version_vectors = false;
-  bool no_version_transitions = false;
-  bool no_resource_deduping = false;
-  bool no_xml_namespaces = false;
-  bool do_not_compress_anything = false;
-  std::unordered_set<std::string> extensions_to_not_compress;
-
-  // Static lib options.
-  bool no_static_lib_packages = false;
-  bool auto_namespace_static_lib = false;
-
-  // AndroidManifest.xml massaging options.
-  ManifestFixerOptions manifest_fixer_options;
-
-  // Products to use/filter on.
-  std::unordered_set<std::string> products;
-
-  // Flattening options.
-  TableFlattenerOptions table_flattener_options;
-
-  // Split APK options.
-  TableSplitterOptions table_splitter_options;
-  std::vector<SplitConstraints> split_constraints;
-  std::vector<std::string> split_paths;
-
-  // Stable ID options.
-  std::unordered_map<ResourceName, ResourceId> stable_id_map;
-  Maybe<std::string> resource_id_map_path;
-
-  // When 'true', allow reserved package IDs to be used for applications. Pre-O, the platform
-  // treats negative resource IDs [those with a package ID of 0x80 or higher] as invalid.
-  // In order to work around this limitation, we allow the use of traditionally reserved
-  // resource IDs [those between 0x02 and 0x7E].
-  bool allow_reserved_package_id = false;
-
-  // Whether we should fail on definitions of a resource with conflicting visibility.
-  bool strict_visibility = false;
-};
-
 class LinkContext : public IAaptContext {
  public:
   LinkContext(IDiagnostics* diagnostics)
@@ -204,14 +137,6 @@
     min_sdk_version_ = minSdk;
   }
 
-  bool IsAutoNamespace() override {
-    return auto_namespace_;
-  }
-
-  void SetAutoNamespace(bool val) {
-    auto_namespace_ = val;
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(LinkContext);
 
@@ -223,7 +148,6 @@
   SymbolTable symbols_;
   bool verbose_ = false;
   int min_sdk_version_ = 0;
-  bool auto_namespace_ = false;
 };
 
 // A custom delegate that generates compatible pre-O IDs for use with feature splits.
@@ -785,9 +709,9 @@
   return table.getTableCookie(idx);
 }
 
-class LinkCommand {
+class Linker {
  public:
-  LinkCommand(LinkContext* context, const LinkOptions& options)
+  Linker(LinkContext* context, const LinkOptions& options)
       : options_(options),
         context_(context),
         final_table_(),
@@ -975,6 +899,18 @@
       app_info.version_code = maybe_code.value();
     }
 
+    if (xml::Attribute* version_code_major_attr =
+        manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) {
+      Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_major_attr->value);
+      if (!maybe_code) {
+        diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
+                        << "invalid android:versionCodeMajor '"
+                        << version_code_major_attr->value << "'");
+        return {};
+      }
+      app_info.version_code_major = maybe_code.value();
+    }
+
     if (xml::Attribute* revision_code_attr =
             manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
       Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value);
@@ -2040,197 +1976,12 @@
   Maybe<std::string> included_feature_base_;
 };
 
-int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
-  LinkContext context(diagnostics);
-  LinkOptions options;
-  std::vector<std::string> overlay_arg_list;
-  std::vector<std::string> extra_java_packages;
-  Maybe<std::string> package_id;
-  std::vector<std::string> configs;
-  Maybe<std::string> preferred_density;
-  Maybe<std::string> product_list;
-  bool legacy_x_flag = false;
-  bool require_localization = false;
-  bool verbose = false;
-  bool shared_lib = false;
-  bool static_lib = false;
-  bool proto_format = false;
-  Maybe<std::string> stable_id_file_path;
-  std::vector<std::string> split_args;
-  Flags flags =
-      Flags()
-          .RequiredFlag("-o", "Output path.", &options.output_path)
-          .RequiredFlag("--manifest", "Path to the Android manifest to build.",
-                        &options.manifest_path)
-          .OptionalFlagList("-I", "Adds an Android APK to link against.", &options.include_paths)
-          .OptionalFlagList("-A",
-                            "An assets directory to include in the APK. These are unprocessed.",
-                            &options.assets_dirs)
-          .OptionalFlagList("-R",
-                            "Compilation unit to link, using `overlay` semantics.\n"
-                            "The last conflicting resource given takes precedence.",
-                            &overlay_arg_list)
-          .OptionalFlag("--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)
-          .OptionalFlag("--java", "Directory in which to generate R.java.",
-                        &options.generate_java_class_path)
-          .OptionalFlag("--proguard", "Output file for generated Proguard rules.",
-                        &options.generate_proguard_rules_path)
-          .OptionalFlag("--proguard-main-dex",
-                        "Output file for generated Proguard rules for the main dex.",
-                        &options.generate_main_dex_proguard_rules_path)
-          .OptionalSwitch("--proguard-conditional-keep-rules",
-                          "Generate conditional Proguard keep rules.",
-                          &options.generate_conditional_proguard_rules)
-          .OptionalSwitch("--no-auto-version",
-                          "Disables automatic style and layout SDK versioning.",
-                          &options.no_auto_version)
-          .OptionalSwitch("--no-version-vectors",
-                          "Disables automatic versioning of vector drawables. Use this only\n"
-                          "when building with vector drawable support library.",
-                          &options.no_version_vectors)
-          .OptionalSwitch("--no-version-transitions",
-                          "Disables automatic versioning of transition resources. Use this only\n"
-                          "when building with transition support library.",
-                          &options.no_version_transitions)
-          .OptionalSwitch("--no-resource-deduping",
-                          "Disables automatic deduping of resources with\n"
-                          "identical values across compatible configurations.",
-                          &options.no_resource_deduping)
-          .OptionalSwitch("--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.table_flattener_options.use_sparse_entries)
-          .OptionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01.",
-                          &legacy_x_flag)
-          .OptionalSwitch("-z", "Require localization of strings marked 'suggested'.",
-                          &require_localization)
-          .OptionalFlagList("-c",
-                            "Comma separated list of configurations to include. The default\n"
-                            "is all configurations.",
-                            &configs)
-          .OptionalFlag("--preferred-density",
-                        "Selects the closest matching density and strips out all others.",
-                        &preferred_density)
-          .OptionalFlag("--product", "Comma separated list of product names to keep", &product_list)
-          .OptionalSwitch("--output-to-dir",
-                          "Outputs the APK contents to a directory specified by -o.",
-                          &options.output_to_directory)
-          .OptionalSwitch("--no-xml-namespaces",
-                          "Removes XML namespace prefix and URI information from\n"
-                          "AndroidManifest.xml and XML binaries in res/*.",
-                          &options.no_xml_namespaces)
-          .OptionalFlag("--min-sdk-version",
-                        "Default minimum SDK version to use for AndroidManifest.xml.",
-                        &options.manifest_fixer_options.min_sdk_version_default)
-          .OptionalFlag("--target-sdk-version",
-                        "Default target SDK version to use for AndroidManifest.xml.",
-                        &options.manifest_fixer_options.target_sdk_version_default)
-          .OptionalFlag("--version-code",
-                        "Version code (integer) to inject into the AndroidManifest.xml if none is\n"
-                        "present.",
-                        &options.manifest_fixer_options.version_code_default)
-          .OptionalFlag("--version-name",
-                        "Version name to inject into the AndroidManifest.xml if none is present.",
-                        &options.manifest_fixer_options.version_name_default)
-          .OptionalSwitch("--replace-version",
-                         "If --version-code and/or --version-name are specified, these\n"
-                         "values will replace any value already in the manifest. By\n"
-                         "default, nothing is changed if the manifest already defines\n"
-                         "these attributes.",
-                         &options.manifest_fixer_options.replace_version)
-          .OptionalFlag("--compile-sdk-version-code",
-                        "Version code (integer) to inject into the AndroidManifest.xml if none is\n"
-                        "present.",
-                        &options.manifest_fixer_options.compile_sdk_version)
-          .OptionalFlag("--compile-sdk-version-name",
-                        "Version name to inject into the AndroidManifest.xml if none is present.",
-                        &options.manifest_fixer_options.compile_sdk_version_codename)
-          .OptionalSwitch("--shared-lib", "Generates a shared Android runtime library.",
-                          &shared_lib)
-          .OptionalSwitch("--static-lib", "Generate a static Android library.", &static_lib)
-          .OptionalSwitch("--proto-format",
-                          "Generates compiled resources in Protobuf format.\n"
-                          "Suitable as input to the bundle tool for generating an App Bundle.",
-                          &proto_format)
-          .OptionalSwitch("--no-static-lib-packages",
-                          "Merge all library resources under the app's package.",
-                          &options.no_static_lib_packages)
-          .OptionalSwitch("--auto-namespace-static-lib",
-                          "Automatically namespace resource references when building a static\n"
-                          "library.",
-                          &options.auto_namespace_static_lib)
-          .OptionalSwitch("--non-final-ids",
-                          "Generates R.java without the final modifier. This is implied when\n"
-                          "--static-lib is specified.",
-                          &options.generate_non_final_ids)
-          .OptionalFlag("--stable-ids", "File containing a list of name to ID mapping.",
-                        &stable_id_file_path)
-          .OptionalFlag("--emit-ids",
-                        "Emit a file at the given path with a list of name to ID mappings,\n"
-                        "suitable for use with --stable-ids.",
-                        &options.resource_id_map_path)
-          .OptionalFlag("--private-symbols",
-                        "Package name to use when generating R.java for private symbols.\n"
-                        "If not specified, public and private symbols will use the application's\n"
-                        "package name.",
-                        &options.private_symbols)
-          .OptionalFlag("--custom-package", "Custom Java package under which to generate R.java.",
-                        &options.custom_java_package)
-          .OptionalFlagList("--extra-packages",
-                            "Generate the same R.java but with different package names.",
-                            &extra_java_packages)
-          .OptionalFlagList("--add-javadoc-annotation",
-                            "Adds a JavaDoc annotation to all generated Java classes.",
-                            &options.javadoc_annotations)
-          .OptionalFlag("--output-text-symbols",
-                        "Generates a text file containing the resource symbols of the R class in\n"
-                        "the specified folder.",
-                        &options.generate_text_symbols_path)
-          .OptionalSwitch("--allow-reserved-package-id",
-                          "Allows the use of a reserved package ID. This should on be used for\n"
-                          "packages with a pre-O min-sdk\n",
-                          &options.allow_reserved_package_id)
-          .OptionalSwitch("--auto-add-overlay",
-                          "Allows the addition of new resources in overlays without\n"
-                          "<add-resource> tags.",
-                          &options.auto_add_overlay)
-          .OptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.",
-                        &options.manifest_fixer_options.rename_manifest_package)
-          .OptionalFlag("--rename-instrumentation-target-package",
-                        "Changes the name of the target package for instrumentation. Most useful\n"
-                        "when used in conjunction with --rename-manifest-package.",
-                        &options.manifest_fixer_options.rename_instrumentation_target_package)
-          .OptionalFlagList("-0", "File extensions not to compress.",
-                            &options.extensions_to_not_compress)
-          .OptionalSwitch("--no-compress", "Do not compress any resources.",
-                          &options.do_not_compress_anything)
-          .OptionalSwitch("--warn-manifest-validation",
-                          "Treat manifest validation errors as warnings.",
-                          &options.manifest_fixer_options.warn_validation)
-          .OptionalFlagList("--split",
-                            "Split resources matching a set of configs out to a Split APK.\n"
-                            "Syntax: path/to/output.apk:<config>[,<config>[...]].\n"
-                            "On Windows, use a semicolon ';' separator instead.",
-                            &split_args)
-          .OptionalSwitch("-v", "Enables verbose logging.", &verbose)
-          .OptionalSwitch("--debug-mode",
-                          "Inserts android:debuggable=\"true\" in to the application node of the\n"
-                          "manifest, making the application debuggable even on production devices.",
-                          &options.manifest_fixer_options.debug_mode)
-          .OptionalSwitch("--strict-visibility",
-                          "Do not allow overlays with different visibility levels.",
-                          &options.strict_visibility);
-
-  if (!flags.Parse("aapt2 link", args, &std::cerr)) {
-    return 1;
-  }
+int LinkCommand::Action(const std::vector<std::string>& args) {
+  LinkContext context(diag_);
 
   // Expand all argument-files passed into the command line. These start with '@'.
   std::vector<std::string> arg_list;
-  for (const std::string& arg : flags.GetArgs()) {
+  for (const std::string& arg : args) {
     if (util::StartsWith(arg, "@")) {
       const std::string path = arg.substr(1, arg.size() - 1);
       std::string error;
@@ -2244,27 +1995,27 @@
   }
 
   // Expand all argument-files passed to -R.
-  for (const std::string& arg : overlay_arg_list) {
+  for (const std::string& arg : overlay_arg_list_) {
     if (util::StartsWith(arg, "@")) {
       const std::string path = arg.substr(1, arg.size() - 1);
       std::string error;
-      if (!file::AppendArgsFromFile(path, &options.overlay_files, &error)) {
+      if (!file::AppendArgsFromFile(path, &options_.overlay_files, &error)) {
         context.GetDiagnostics()->Error(DiagMessage(path) << error);
         return 1;
       }
     } else {
-      options.overlay_files.push_back(arg);
+      options_.overlay_files.push_back(arg);
     }
   }
 
-  if (verbose) {
-    context.SetVerbose(verbose);
+  if (verbose_) {
+    context.SetVerbose(verbose_);
   }
 
-  if (int{shared_lib} + int{static_lib} + int{proto_format} > 1) {
+  if (int{shared_lib_} + int{static_lib_} + int{proto_format_} > 1) {
     context.GetDiagnostics()->Error(
         DiagMessage()
-        << "only one of --shared-lib, --static-lib, or --proto_format can be defined");
+            << "only one of --shared-lib, --static-lib, or --proto_format can be defined");
     return 1;
   }
 
@@ -2272,35 +2023,26 @@
   context.SetPackageType(PackageType::kApp);
   context.SetPackageId(kAppPackageId);
 
-  if (shared_lib) {
+  if (shared_lib_) {
     context.SetPackageType(PackageType::kSharedLib);
     context.SetPackageId(0x00);
-  } else if (static_lib) {
+  } else if (static_lib_) {
     context.SetPackageType(PackageType::kStaticLib);
-    options.output_format = OutputFormat::kProto;
-  } else if (proto_format) {
-    options.output_format = OutputFormat::kProto;
+    options_.output_format = OutputFormat::kProto;
+  } else if (proto_format_) {
+    options_.output_format = OutputFormat::kProto;
   }
 
-  if (options.auto_namespace_static_lib) {
-    if (!static_lib) {
-      context.GetDiagnostics()->Error(
-          DiagMessage() << "--auto-namespace-static-lib can only be used with --static-lib");
-      return 1;
-    }
-    context.SetAutoNamespace(true);
-  }
-
-  if (package_id) {
+  if (package_id_) {
     if (context.GetPackageType() != PackageType::kApp) {
       context.GetDiagnostics()->Error(
           DiagMessage() << "can't specify --package-id when not building a regular app");
       return 1;
     }
 
-    const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id.value());
+    const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id_.value());
     if (!maybe_package_id_int) {
-      context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id.value()
+      context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id_.value()
                                                     << "' is not a valid integer");
       return 1;
     }
@@ -2308,7 +2050,7 @@
     const uint32_t package_id_int = maybe_package_id_int.value();
     if (package_id_int > std::numeric_limits<uint8_t>::max()
         || package_id_int == kFrameworkPackageId
-        || (!options.allow_reserved_package_id && package_id_int < kAppPackageId)) {
+        || (!options_.allow_reserved_package_id && package_id_int < kAppPackageId)) {
       context.GetDiagnostics()->Error(
           DiagMessage() << StringPrintf(
               "invalid package ID 0x%02x. Must be in the range 0x7f-0xff.", package_id_int));
@@ -2318,71 +2060,71 @@
   }
 
   // Populate the set of extra packages for which to generate R.java.
-  for (std::string& extra_package : extra_java_packages) {
+  for (std::string& extra_package : extra_java_packages_) {
     // A given package can actually be a colon separated list of packages.
     for (StringPiece package : util::Split(extra_package, ':')) {
-      options.extra_java_packages.insert(package.to_string());
+      options_.extra_java_packages.insert(package.to_string());
     }
   }
 
-  if (product_list) {
-    for (StringPiece product : util::Tokenize(product_list.value(), ',')) {
+  if (product_list_) {
+    for (StringPiece product : util::Tokenize(product_list_.value(), ',')) {
       if (product != "" && product != "default") {
-        options.products.insert(product.to_string());
+        options_.products.insert(product.to_string());
       }
     }
   }
 
   std::unique_ptr<IConfigFilter> filter;
-  if (!configs.empty()) {
-    filter = ParseConfigFilterParameters(configs, context.GetDiagnostics());
+  if (!configs_.empty()) {
+    filter = ParseConfigFilterParameters(configs_, context.GetDiagnostics());
     if (filter == nullptr) {
       return 1;
     }
-    options.table_splitter_options.config_filter = filter.get();
+    options_.table_splitter_options.config_filter = filter.get();
   }
 
-  if (preferred_density) {
+  if (preferred_density_) {
     Maybe<uint16_t> density =
-        ParseTargetDensityParameter(preferred_density.value(), context.GetDiagnostics());
+        ParseTargetDensityParameter(preferred_density_.value(), context.GetDiagnostics());
     if (!density) {
       return 1;
     }
-    options.table_splitter_options.preferred_densities.push_back(density.value());
+    options_.table_splitter_options.preferred_densities.push_back(density.value());
   }
 
   // Parse the split parameters.
-  for (const std::string& split_arg : split_args) {
-    options.split_paths.push_back({});
-    options.split_constraints.push_back({});
-    if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options.split_paths.back(),
-                             &options.split_constraints.back())) {
+  for (const std::string& split_arg : split_args_) {
+    options_.split_paths.push_back({});
+    options_.split_constraints.push_back({});
+    if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options_.split_paths.back(),
+        &options_.split_constraints.back())) {
       return 1;
     }
   }
 
-  if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path) {
-    if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path.value(),
-                         &options.stable_id_map)) {
+  if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path_) {
+    if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path_.value(),
+        &options_.stable_id_map)) {
       return 1;
     }
   }
 
   // Populate some default no-compress extensions that are already compressed.
-  options.extensions_to_not_compress.insert(
+  options_.extensions_to_not_compress.insert(
       {".jpg",   ".jpeg", ".png",  ".gif", ".wav",  ".mp2",  ".mp3",  ".ogg",
-       ".aac",   ".mpg",  ".mpeg", ".mid", ".midi", ".smf",  ".jet",  ".rtttl",
-       ".imy",   ".xmf",  ".mp4",  ".m4a", ".m4v",  ".3gp",  ".3gpp", ".3g2",
-       ".3gpp2", ".amr",  ".awb",  ".wma", ".wmv",  ".webm", ".mkv"});
+          ".aac",   ".mpg",  ".mpeg", ".mid", ".midi", ".smf",  ".jet",  ".rtttl",
+          ".imy",   ".xmf",  ".mp4",  ".m4a", ".m4v",  ".3gp",  ".3gpp", ".3g2",
+          ".3gpp2", ".amr",  ".awb",  ".wma", ".wmv",  ".webm", ".mkv"});
 
   // Turn off auto versioning for static-libs.
   if (context.GetPackageType() == PackageType::kStaticLib) {
-    options.no_auto_version = true;
-    options.no_version_vectors = true;
-    options.no_version_transitions = true;
+    options_.no_auto_version = true;
+    options_.no_version_vectors = true;
+    options_.no_version_transitions = true;
   }
 
-  LinkCommand cmd(&context, options);
+  Linker cmd(&context, options_);
   return cmd.Run(arg_list);
 }
 
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
new file mode 100644
index 0000000..e58a93e
--- /dev/null
+++ b/tools/aapt2/cmd/Link.h
@@ -0,0 +1,279 @@
+/*
+ * 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 AAPT2_LINK_H
+#define AAPT2_LINK_H
+
+#include "Command.h"
+#include "Diagnostics.h"
+#include "Resource.h"
+#include "split/TableSplitter.h"
+#include "format/binary/TableFlattener.h"
+#include "link/ManifestFixer.h"
+
+namespace aapt {
+
+enum class OutputFormat {
+  kApk,
+  kProto,
+};
+
+struct LinkOptions {
+  std::string output_path;
+  std::string manifest_path;
+  std::vector<std::string> include_paths;
+  std::vector<std::string> overlay_files;
+  std::vector<std::string> assets_dirs;
+  bool output_to_directory = false;
+  bool auto_add_overlay = false;
+  OutputFormat output_format = OutputFormat::kApk;
+
+  // Java/Proguard options.
+  Maybe<std::string> generate_java_class_path;
+  Maybe<std::string> custom_java_package;
+  std::set<std::string> extra_java_packages;
+  Maybe<std::string> generate_text_symbols_path;
+  Maybe<std::string> generate_proguard_rules_path;
+  Maybe<std::string> generate_main_dex_proguard_rules_path;
+  bool generate_conditional_proguard_rules = false;
+  bool generate_non_final_ids = false;
+  std::vector<std::string> javadoc_annotations;
+  Maybe<std::string> private_symbols;
+
+  // Optimizations/features.
+  bool no_auto_version = false;
+  bool no_version_vectors = false;
+  bool no_version_transitions = false;
+  bool no_resource_deduping = false;
+  bool no_xml_namespaces = false;
+  bool do_not_compress_anything = false;
+  std::unordered_set<std::string> extensions_to_not_compress;
+
+  // Static lib options.
+  bool no_static_lib_packages = false;
+
+  // AndroidManifest.xml massaging options.
+  ManifestFixerOptions manifest_fixer_options;
+
+  // Products to use/filter on.
+  std::unordered_set<std::string> products;
+
+  // Flattening options.
+  TableFlattenerOptions table_flattener_options;
+
+  // Split APK options.
+  TableSplitterOptions table_splitter_options;
+  std::vector<SplitConstraints> split_constraints;
+  std::vector<std::string> split_paths;
+
+  // Stable ID options.
+  std::unordered_map<ResourceName, ResourceId> stable_id_map;
+  Maybe<std::string> resource_id_map_path;
+
+  // When 'true', allow reserved package IDs to be used for applications. Pre-O, the platform
+  // treats negative resource IDs [those with a package ID of 0x80 or higher] as invalid.
+  // In order to work around this limitation, we allow the use of traditionally reserved
+  // resource IDs [those between 0x02 and 0x7E].
+  bool allow_reserved_package_id = false;
+
+  // Whether we should fail on definitions of a resource with conflicting visibility.
+  bool strict_visibility = false;
+};
+
+class LinkCommand : public Command {
+ public:
+  explicit LinkCommand(IDiagnostics* diag) : Command("link", "l"),
+                                             diag_(diag) {
+    SetDescription("Links resources into an apk.");
+    AddRequiredFlag("-o", "Output path.", &options_.output_path);
+    AddRequiredFlag("--manifest", "Path to the Android manifest to build.",
+        &options_.manifest_path);
+    AddOptionalFlagList("-I", "Adds an Android APK to link against.", &options_.include_paths);
+    AddOptionalFlagList("-A", "An assets directory to include in the APK. These are unprocessed.",
+        &options_.assets_dirs);
+    AddOptionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
+        "The last conflicting resource given takes precedence.", &overlay_arg_list_);
+    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);
+    AddOptionalFlag("--proguard", "Output file for generated Proguard rules.",
+        &options_.generate_proguard_rules_path);
+    AddOptionalFlag("--proguard-main-dex",
+        "Output file for generated Proguard rules for the main dex.",
+        &options_.generate_main_dex_proguard_rules_path);
+    AddOptionalSwitch("--proguard-conditional-keep-rules",
+        "Generate conditional Proguard keep rules.",
+        &options_.generate_conditional_proguard_rules);
+    AddOptionalSwitch("--no-auto-version", "Disables automatic style and layout SDK versioning.",
+        &options_.no_auto_version);
+    AddOptionalSwitch("--no-version-vectors",
+        "Disables automatic versioning of vector drawables. Use this only\n"
+            "when building with vector drawable support library.",
+        &options_.no_version_vectors);
+    AddOptionalSwitch("--no-version-transitions",
+        "Disables automatic versioning of transition resources. Use this only\n"
+            "when building with transition support library.",
+        &options_.no_version_transitions);
+    AddOptionalSwitch("--no-resource-deduping", "Disables automatic deduping of resources with\n"
+            "identical values across compatible configurations.",
+        &options_.no_resource_deduping);
+    AddOptionalSwitch("--enable-sparse-encoding",
+        "This decreases APK size at the cost of resource retrieval performance.",
+        &options_.table_flattener_options.use_sparse_entries);
+    AddOptionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01.",
+        &legacy_x_flag_);
+    AddOptionalSwitch("-z", "Require localization of strings marked 'suggested'.",
+        &require_localization_);
+    AddOptionalFlagList("-c",
+        "Comma separated list of configurations to include. The default\n"
+            "is all configurations.", &configs_);
+    AddOptionalFlag("--preferred-density",
+        "Selects the closest matching density and strips out all others.",
+        &preferred_density_);
+    AddOptionalFlag("--product", "Comma separated list of product names to keep", &product_list_);
+    AddOptionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified by -o.",
+        &options_.output_to_directory);
+    AddOptionalSwitch("--no-xml-namespaces", "Removes XML namespace prefix and URI information\n"
+            "from AndroidManifest.xml and XML binaries in res/*.",
+        &options_.no_xml_namespaces);
+    AddOptionalFlag("--min-sdk-version",
+        "Default minimum SDK version to use for AndroidManifest.xml.",
+        &options_.manifest_fixer_options.min_sdk_version_default);
+    AddOptionalFlag("--target-sdk-version",
+        "Default target SDK version to use for AndroidManifest.xml.",
+        &options_.manifest_fixer_options.target_sdk_version_default);
+    AddOptionalFlag("--version-code",
+        "Version code (integer) to inject into the AndroidManifest.xml if none is\n"
+            "present.", &options_.manifest_fixer_options.version_code_default);
+    AddOptionalFlag("--version-code-major",
+        "Version code major (integer) to inject into the AndroidManifest.xml if none is\n"
+            "present.", &options_.manifest_fixer_options.version_code_major_default);
+    AddOptionalFlag("--version-name",
+        "Version name to inject into the AndroidManifest.xml if none is present.",
+        &options_.manifest_fixer_options.version_name_default);
+    AddOptionalSwitch("--replace-version",
+        "If --version-code and/or --version-name are specified, these\n"
+            "values will replace any value already in the manifest. By\n"
+            "default, nothing is changed if the manifest already defines\n"
+            "these attributes.",
+        &options_.manifest_fixer_options.replace_version);
+    AddOptionalFlag("--compile-sdk-version-code",
+        "Version code (integer) to inject into the AndroidManifest.xml if none is\n"
+            "present.",
+        &options_.manifest_fixer_options.compile_sdk_version);
+    AddOptionalFlag("--compile-sdk-version-name",
+        "Version name to inject into the AndroidManifest.xml if none is present.",
+        &options_.manifest_fixer_options.compile_sdk_version_codename);
+    AddOptionalSwitch("--shared-lib", "Generates a shared Android runtime library.",
+        &shared_lib_);
+    AddOptionalSwitch("--static-lib", "Generate a static Android library.", &static_lib_);
+    AddOptionalSwitch("--proto-format",
+        "Generates compiled resources in Protobuf format.\n"
+            "Suitable as input to the bundle tool for generating an App Bundle.",
+        &proto_format_);
+    AddOptionalSwitch("--no-static-lib-packages",
+        "Merge all library resources under the app's package.",
+        &options_.no_static_lib_packages);
+    AddOptionalSwitch("--non-final-ids",
+        "Generates R.java without the final modifier. This is implied when\n"
+            "--static-lib is specified.",
+        &options_.generate_non_final_ids);
+    AddOptionalFlag("--stable-ids", "File containing a list of name to ID mapping.",
+        &stable_id_file_path_);
+    AddOptionalFlag("--emit-ids",
+        "Emit a file at the given path with a list of name to ID mappings,\n"
+            "suitable for use with --stable-ids.",
+        &options_.resource_id_map_path);
+    AddOptionalFlag("--private-symbols",
+        "Package name to use when generating R.java for private symbols.\n"
+            "If not specified, public and private symbols will use the application's\n"
+            "package name.",
+        &options_.private_symbols);
+    AddOptionalFlag("--custom-package", "Custom Java package under which to generate R.java.",
+        &options_.custom_java_package);
+    AddOptionalFlagList("--extra-packages",
+        "Generate the same R.java but with different package names.",
+        &extra_java_packages_);
+    AddOptionalFlagList("--add-javadoc-annotation",
+        "Adds a JavaDoc annotation to all generated Java classes.",
+        &options_.javadoc_annotations);
+    AddOptionalFlag("--output-text-symbols",
+        "Generates a text file containing the resource symbols of the R class in\n"
+            "the specified folder.",
+        &options_.generate_text_symbols_path);
+    AddOptionalSwitch("--allow-reserved-package-id",
+        "Allows the use of a reserved package ID. This should on be used for\n"
+            "packages with a pre-O min-sdk\n",
+        &options_.allow_reserved_package_id);
+    AddOptionalSwitch("--auto-add-overlay",
+        "Allows the addition of new resources in overlays without\n"
+            "<add-resource> tags.",
+        &options_.auto_add_overlay);
+    AddOptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.",
+        &options_.manifest_fixer_options.rename_manifest_package);
+    AddOptionalFlag("--rename-instrumentation-target-package",
+        "Changes the name of the target package for instrumentation. Most useful\n"
+            "when used in conjunction with --rename-manifest-package.",
+        &options_.manifest_fixer_options.rename_instrumentation_target_package);
+    AddOptionalFlagList("-0", "File extensions not to compress.",
+        &options_.extensions_to_not_compress);
+    AddOptionalSwitch("--no-compress", "Do not compress any resources.",
+        &options_.do_not_compress_anything);
+    AddOptionalSwitch("--warn-manifest-validation",
+        "Treat manifest validation errors as warnings.",
+        &options_.manifest_fixer_options.warn_validation);
+    AddOptionalFlagList("--split",
+        "Split resources matching a set of configs out to a Split APK.\n"
+            "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.",
+        &options_.manifest_fixer_options.debug_mode);
+    AddOptionalSwitch("--strict-visibility",
+        "Do not allow overlays with different visibility levels.",
+        &options_.strict_visibility);
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+
+ private:
+  IDiagnostics* diag_;
+  LinkOptions options_;
+
+  std::vector<std::string> overlay_arg_list_;
+  std::vector<std::string> extra_java_packages_;
+  Maybe<std::string> package_id_;
+  std::vector<std::string> configs_;
+  Maybe<std::string> preferred_density_;
+  Maybe<std::string> product_list_;
+  bool legacy_x_flag_ = false;
+  bool require_localization_ = false;
+  bool verbose_ = false;
+  bool shared_lib_ = false;
+  bool static_lib_ = false;
+  bool proto_format_ = false;
+  Maybe<std::string> stable_id_file_path_;
+  std::vector<std::string> split_args_;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_LINK_H
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 4afa8f0..47288ec 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "Optimize.h"
+
 #include <memory>
 #include <vector>
 
@@ -24,7 +26,6 @@
 #include "androidfw/StringPiece.h"
 
 #include "Diagnostics.h"
-#include "Flags.h"
 #include "LoadedApk.h"
 #include "ResourceUtils.h"
 #include "SdkConstants.h"
@@ -54,36 +55,6 @@
 
 namespace aapt {
 
-struct OptimizeOptions {
-  // Path to the output APK.
-  Maybe<std::string> output_path;
-  // Path to the output APK directory for splits.
-  Maybe<std::string> output_dir;
-
-  // Details of the app extracted from the AndroidManifest.xml
-  AppInfo app_info;
-
-  // Blacklist of unused resources that should be removed from the apk.
-  std::unordered_set<ResourceName> resources_blacklist;
-
-  // Split APK options.
-  TableSplitterOptions table_splitter_options;
-
-  // List of output split paths. These are in the same order as `split_constraints`.
-  std::vector<std::string> split_paths;
-
-  // List of SplitConstraints governing what resources go into each split. Ordered by `split_paths`.
-  std::vector<SplitConstraints> split_constraints;
-
-  TableFlattenerOptions table_flattener_options;
-
-  Maybe<std::vector<OutputArtifact>> apk_artifacts;
-
-  // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
-  // are kept and will be written as output.
-  std::unordered_set<std::string> kept_artifacts;
-};
-
 class OptimizeContext : public IAaptContext {
  public:
   OptimizeContext() = default;
@@ -133,10 +104,6 @@
     return sdk_version_;
   }
 
-  bool IsAutoNamespace() override {
-    return false;
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(OptimizeContext);
 
@@ -145,9 +112,9 @@
   int sdk_version_ = 0;
 };
 
-class OptimizeCommand {
+class Optimizer {
  public:
-  OptimizeCommand(OptimizeContext* context, const OptimizeOptions& options)
+  Optimizer(OptimizeContext* context, const OptimizeOptions& options)
       : options_(options), context_(context) {
   }
 
@@ -378,82 +345,24 @@
   return true;
 }
 
-int Optimize(const std::vector<StringPiece>& args) {
-  OptimizeContext context;
-  OptimizeOptions options;
-  Maybe<std::string> config_path;
-  Maybe<std::string> whitelist_path;
-  Maybe<std::string> resources_config_path;
-  Maybe<std::string> target_densities;
-  std::vector<std::string> configs;
-  std::vector<std::string> split_args;
-  std::unordered_set<std::string> kept_artifacts;
-  bool verbose = false;
-  bool print_only = false;
-  Flags flags =
-      Flags()
-          .OptionalFlag("-o", "Path to the output APK.", &options.output_path)
-          .OptionalFlag("-d", "Path to the output directory (for splits).", &options.output_dir)
-          .OptionalFlag("-x", "Path to XML configuration file.", &config_path)
-          .OptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only)
-          .OptionalFlag(
-              "--target-densities",
-              "Comma separated list of the screen densities that the APK will be optimized for.\n"
-              "All the resources that would be unused on devices of the given densities will be \n"
-              "removed from the APK.",
-              &target_densities)
-          .OptionalFlag("--whitelist-path",
-                        "Path to the whitelist.cfg file containing whitelisted resources \n"
-                        "whose names should not be altered in final resource tables.",
-                        &whitelist_path)
-          .OptionalFlag("--resources-config-path",
-                        "Path to the resources.cfg file containing the list of resources and \n"
-                        "directives to each resource. \n"
-                        "Format: type/resource_name#[directive][,directive]",
-                        &resources_config_path)
-          .OptionalFlagList("-c",
-                            "Comma separated list of configurations to include. The default\n"
-                            "is all configurations.",
-                            &configs)
-          .OptionalFlagList("--split",
-                            "Split resources matching a set of configs out to a "
-                            "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n"
-                            "On Windows, use a semicolon ';' separator instead.",
-                            &split_args)
-          .OptionalFlagList("--keep-artifacts",
-                            "Comma separated list of artifacts to keep. If none are specified,\n"
-                            "all artifacts will be kept.",
-                            &kept_artifacts)
-          .OptionalSwitch("--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.table_flattener_options.use_sparse_entries)
-          .OptionalSwitch("--enable-resource-obfuscation",
-                          "Enables obfuscation of key string pool to single value",
-                          &options.table_flattener_options.collapse_key_stringpool)
-          .OptionalSwitch("-v", "Enables verbose logging", &verbose);
-
-  if (!flags.Parse("aapt2 optimize", args, &std::cerr)) {
-    return 1;
-  }
-
-  if (flags.GetArgs().size() != 1u) {
+int OptimizeCommand::Action(const std::vector<std::string>& args) {
+  if (args.size() != 1u) {
     std::cerr << "must have one APK as argument.\n\n";
-    flags.Usage("aapt2 optimize", &std::cerr);
+    Usage(&std::cerr);
     return 1;
   }
 
-  const std::string& apk_path = flags.GetArgs()[0];
-
-  context.SetVerbose(verbose);
+  const std::string& apk_path = args[0];
+  OptimizeContext context;
+  context.SetVerbose(verbose_);
   IDiagnostics* diag = context.GetDiagnostics();
 
-  if (config_path) {
-    std::string& path = config_path.value();
+  if (config_path_) {
+    std::string& path = config_path_.value();
     Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
     if (for_path) {
-      options.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path);
-      if (!options.apk_artifacts) {
+      options_.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path);
+      if (!options_.apk_artifacts) {
         diag->Error(DiagMessage() << "Failed to parse the output artifact list");
         return 1;
       }
@@ -463,28 +372,28 @@
       return 1;
     }
 
-    if (print_only) {
-      for (const OutputArtifact& artifact : options.apk_artifacts.value()) {
+    if (print_only_) {
+      for (const OutputArtifact& artifact : options_.apk_artifacts.value()) {
         std::cout << artifact.name << std::endl;
       }
       return 0;
     }
 
-    if (!kept_artifacts.empty()) {
-      for (const std::string& artifact_str : kept_artifacts) {
+    if (!kept_artifacts_.empty()) {
+      for (const std::string& artifact_str : kept_artifacts_) {
         for (const StringPiece& artifact : util::Tokenize(artifact_str, ',')) {
-          options.kept_artifacts.insert(artifact.to_string());
+          options_.kept_artifacts.insert(artifact.to_string());
         }
       }
     }
 
     // Since we know that we are going to process the APK (not just print targets), make sure we
     // have somewhere to write them to.
-    if (!options.output_dir) {
+    if (!options_.output_dir) {
       diag->Error(DiagMessage() << "Output directory is required when using a configuration file");
       return 1;
     }
-  } else if (print_only) {
+  } else if (print_only_) {
     diag->Error(DiagMessage() << "Asked to print artifacts without providing a configurations");
     return 1;
   }
@@ -494,57 +403,57 @@
     return 1;
   }
 
-  if (target_densities) {
+  if (target_densities_) {
     // Parse the target screen densities.
-    for (const StringPiece& config_str : util::Tokenize(target_densities.value(), ',')) {
+    for (const StringPiece& config_str : util::Tokenize(target_densities_.value(), ',')) {
       Maybe<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag);
       if (!target_density) {
         return 1;
       }
-      options.table_splitter_options.preferred_densities.push_back(target_density.value());
+      options_.table_splitter_options.preferred_densities.push_back(target_density.value());
     }
   }
 
   std::unique_ptr<IConfigFilter> filter;
-  if (!configs.empty()) {
-    filter = ParseConfigFilterParameters(configs, diag);
+  if (!configs_.empty()) {
+    filter = ParseConfigFilterParameters(configs_, diag);
     if (filter == nullptr) {
       return 1;
     }
-    options.table_splitter_options.config_filter = filter.get();
+    options_.table_splitter_options.config_filter = filter.get();
   }
 
   // Parse the split parameters.
-  for (const std::string& split_arg : split_args) {
-    options.split_paths.emplace_back();
-    options.split_constraints.emplace_back();
-    if (!ParseSplitParameter(split_arg, diag, &options.split_paths.back(),
-                             &options.split_constraints.back())) {
+  for (const std::string& split_arg : split_args_) {
+    options_.split_paths.emplace_back();
+    options_.split_constraints.emplace_back();
+    if (!ParseSplitParameter(split_arg, diag, &options_.split_paths.back(),
+        &options_.split_constraints.back())) {
       return 1;
     }
   }
 
-  if (options.table_flattener_options.collapse_key_stringpool) {
-    if (whitelist_path) {
-      std::string& path = whitelist_path.value();
-      if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options)) {
+  if (options_.table_flattener_options.collapse_key_stringpool) {
+    if (whitelist_path_) {
+      std::string& path = whitelist_path_.value();
+      if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options_)) {
         return 1;
       }
     }
   }
 
-  if (resources_config_path) {
-    std::string& path = resources_config_path.value();
-    if (!ExtractConfig(path, &context, &options)) {
+  if (resources_config_path_) {
+    std::string& path = resources_config_path_.value();
+    if (!ExtractConfig(path, &context, &options_)) {
       return 1;
     }
   }
 
-  if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {
+  if (!ExtractAppDataFromManifest(&context, apk.get(), &options_)) {
     return 1;
   }
 
-  OptimizeCommand cmd(&context, options);
+  Optimizer cmd(&context, options_);
   return cmd.Run(std::move(apk));
 }
 
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
new file mode 100644
index 0000000..43bc216
--- /dev/null
+++ b/tools/aapt2/cmd/Optimize.h
@@ -0,0 +1,124 @@
+/*
+ * 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 AAPT2_OPTIMIZE_H
+#define AAPT2_OPTIMIZE_H
+
+#include "AppInfo.h"
+#include "Command.h"
+#include "configuration/ConfigurationParser.h"
+#include "format/binary/TableFlattener.h"
+#include "split/TableSplitter.h"
+
+namespace aapt {
+
+struct OptimizeOptions {
+  friend class OptimizeCommand;
+
+  // Path to the output APK.
+  Maybe<std::string> output_path;
+  // Path to the output APK directory for splits.
+  Maybe<std::string> output_dir;
+
+  // Details of the app extracted from the AndroidManifest.xml
+  AppInfo app_info;
+
+  // Blacklist of unused resources that should be removed from the apk.
+  std::unordered_set<ResourceName> resources_blacklist;
+
+  // Split APK options.
+  TableSplitterOptions table_splitter_options;
+
+  // List of output split paths. These are in the same order as `split_constraints`.
+  std::vector<std::string> split_paths;
+
+  // List of SplitConstraints governing what resources go into each split. Ordered by `split_paths`.
+  std::vector<SplitConstraints> split_constraints;
+
+  TableFlattenerOptions table_flattener_options;
+
+  Maybe<std::vector<aapt::configuration::OutputArtifact>> apk_artifacts;
+
+  // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
+  // are kept and will be written as output.
+  std::unordered_set<std::string> kept_artifacts;
+};
+
+class OptimizeCommand : public Command {
+ 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_);
+    AddOptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only_);
+    AddOptionalFlag(
+        "--target-densities",
+        "Comma separated list of the screen densities that the APK will be optimized for.\n"
+            "All the resources that would be unused on devices of the given densities will be \n"
+            "removed from the APK.",
+        &target_densities_);
+    AddOptionalFlag("--whitelist-path",
+        "Path to the whitelist.cfg file containing whitelisted resources \n"
+            "whose names should not be altered in final resource tables.",
+        &whitelist_path_);
+    AddOptionalFlag("--resources-config-path",
+        "Path to the resources.cfg file containing the list of resources and \n"
+            "directives to each resource. \n"
+            "Format: type/resource_name#[directive][,directive]",
+        &resources_config_path_);
+    AddOptionalFlagList("-c",
+        "Comma separated list of configurations to include. The default\n"
+            "is all configurations.",
+        &configs_);
+    AddOptionalFlagList("--split",
+        "Split resources matching a set of configs out to a "
+            "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n"
+            "On Windows, use a semicolon ';' separator instead.",
+        &split_args_);
+    AddOptionalFlagList("--keep-artifacts",
+        "Comma separated list of artifacts to keep. If none are specified,\n"
+            "all artifacts will be kept.",
+        &kept_artifacts_);
+    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_.table_flattener_options.use_sparse_entries);
+    AddOptionalSwitch("--enable-resource-obfuscation",
+        "Enables obfuscation of key string pool to single value",
+        &options_.table_flattener_options.collapse_key_stringpool);
+    AddOptionalSwitch("-v", "Enables verbose logging", &verbose_);
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+
+ private:
+  OptimizeOptions options_;
+
+  Maybe<std::string> config_path_;
+  Maybe<std::string> whitelist_path_;
+  Maybe<std::string> resources_config_path_;
+  Maybe<std::string> target_densities_;
+  std::vector<std::string> configs_;
+  std::vector<std::string> split_args_;
+  std::unordered_set<std::string> kept_artifacts_;
+  bool print_only_ = false;
+  bool verbose_ = false;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_OPTIMIZE_H
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index 4e77e9a..c6c82b0 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -29,6 +29,7 @@
 #include "util/Util.h"
 
 using ::android::StringPiece;
+using ::android::base::StringPrintf;
 
 namespace aapt {
 
@@ -168,6 +169,7 @@
 std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
                                                         const SplitConstraints& constraints) {
   const ResourceId kVersionCode(0x0101021b);
+  const ResourceId kVersionCodeMajor(0x01010576);
   const ResourceId kRevisionCode(0x010104d5);
   const ResourceId kHasCode(0x0101000c);
 
@@ -184,6 +186,14 @@
         util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, version_code)});
   }
 
+  if (app_info.version_code_major) {
+    const uint32_t version_code_major = app_info.version_code_major.value();
+    manifest_el->attributes.push_back(xml::Attribute{
+        xml::kSchemaAndroid, "versionCodeMajor", std::to_string(version_code_major),
+        CreateAttributeWithId(kVersionCodeMajor),
+        util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, version_code_major)});
+  }
+
   if (app_info.revision_code) {
     const uint32_t revision_code = app_info.revision_code.value();
     manifest_el->attributes.push_back(xml::Attribute{
@@ -355,6 +365,17 @@
     app_info.version_code = maybe_code.value();
   }
 
+  if (const xml::Attribute* version_code_major_attr =
+      manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) {
+    Maybe<uint32_t> maybe_code = ExtractCompiledInt(*version_code_major_attr, &error_msg);
+    if (!maybe_code) {
+      diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
+                      << "invalid android:versionCodeMajor: " << error_msg);
+      return {};
+    }
+    app_info.version_code_major = maybe_code.value();
+  }
+
   if (const xml::Attribute* revision_code_attr =
           manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
     Maybe<uint32_t> maybe_code = ExtractCompiledInt(*revision_code_attr, &error_msg);
@@ -391,4 +412,21 @@
   return app_info;
 }
 
+void SetLongVersionCode(xml::Element* manifest, uint64_t version) {
+  // Write the low bits of the version code to android:versionCode
+  auto version_code = manifest->FindOrCreateAttribute(xml::kSchemaAndroid, "versionCode");
+  version_code->value = StringPrintf("0x%08x", (uint32_t) (version & 0xffffffff));
+  version_code->compiled_value = ResourceUtils::TryParseInt(version_code->value);
+
+  auto version_high = (uint32_t) (version >> 32);
+  if (version_high != 0) {
+    // Write the high bits of the version code to android:versionCodeMajor
+    auto version_major = manifest->FindOrCreateAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+    version_major->value = StringPrintf("0x%08x", version_high);
+    version_major->compiled_value = ResourceUtils::TryParseInt(version_major->value);
+  } else {
+    manifest->RemoveAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+  }
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h
index fb8753e..cf1443e 100644
--- a/tools/aapt2/cmd/Util.h
+++ b/tools/aapt2/cmd/Util.h
@@ -67,6 +67,11 @@
 // checks this at runtime.
 std::string MakePackageSafeName(const std::string &name);
 
+// Sets the versionCode and versionCodeMajor attributes to the version code. Attempts to encode the
+// version code using the versionCode attribute only, and encodes using both versionCode and
+// versionCodeMajor if the version code requires more than 32 bits.
+void SetLongVersionCode(xml::Element* manifest, uint64_t version_code);
+
 }  // namespace aapt
 
 #endif /* AAPT_SPLIT_UTIL_H */
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 0c527f6..b9fb5b2 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -18,6 +18,7 @@
 
 #include "AppInfo.h"
 #include "split/TableSplitter.h"
+#include "test/Builders.h"
 #include "test/Test.h"
 
 namespace aapt {
@@ -36,4 +37,51 @@
     EXPECT_EQ(root->FindAttribute("", "targetConfig")->value, "b+sr+Latn,en-rUS-land");
 }
 
+TEST (UtilTest, LongVersionCodeDefined) {
+  auto doc = test::BuildXmlDom(R"(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.aapt.test" android:versionCode="0x1" android:versionCodeMajor="0x1">
+      </manifest>)");
+  SetLongVersionCode(doc->root.get(), 42);
+
+  auto version_code = doc->root->FindAttribute(xml::kSchemaAndroid, "versionCode");
+  ASSERT_NE(version_code, nullptr);
+  EXPECT_EQ(version_code->value, "0x0000002a");
+
+  ASSERT_NE(version_code->compiled_value, nullptr);
+  auto compiled_version_code = ValueCast<BinaryPrimitive>(version_code->compiled_value.get());
+  ASSERT_NE(compiled_version_code, nullptr);
+  EXPECT_EQ(compiled_version_code->value.data, 42U);
+
+  auto version_code_major = doc->root->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+  EXPECT_EQ(version_code_major, nullptr);
+}
+
+TEST (UtilTest, LongVersionCodeUndefined) {
+  auto doc = test::BuildXmlDom(R"(
+        <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.aapt.test">
+        </manifest>)");
+  SetLongVersionCode(doc->root.get(), 420000000000);
+
+  auto version_code = doc->root->FindAttribute(xml::kSchemaAndroid, "versionCode");
+  ASSERT_NE(version_code, nullptr);
+  EXPECT_EQ(version_code->value, "0xc9f36800");
+
+  ASSERT_NE(version_code->compiled_value, nullptr);
+  auto compiled_version_code = ValueCast<BinaryPrimitive>(version_code->compiled_value.get());
+  ASSERT_NE(compiled_version_code, nullptr);
+  EXPECT_EQ(compiled_version_code->value.data, 0xc9f36800);
+
+  auto version_code_major = doc->root->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+  ASSERT_NE(version_code_major, nullptr);
+  EXPECT_EQ(version_code_major->value, "0x00000061");
+
+  ASSERT_NE(version_code_major->compiled_value, nullptr);
+  auto compiled_version_code_major = ValueCast<BinaryPrimitive>(
+      version_code_major->compiled_value.get());
+  ASSERT_NE(compiled_version_code_major, nullptr);
+  EXPECT_EQ(compiled_version_code_major->value.data, 0x61);
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk b/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk
deleted file mode 100644
index 5d7a6f7..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk
+++ /dev/null
@@ -1,18 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/Android.mk b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/Android.mk
deleted file mode 100644
index 91716b9..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/Android.mk
+++ /dev/null
@@ -1,29 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := AaptTestAutoNamespace_LibOne
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_AAPT_NAMESPACES := true
-LOCAL_AAPT_FLAGS := --auto-namespace-static-lib
-# We need this to compile the Java sources of AaptTestStaticLib_LibTwo using javac.
-LOCAL_JAR_EXCLUDE_FILES := none
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/AndroidManifest.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/AndroidManifest.xml
deleted file mode 100644
index f585840..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +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.
--->
-
-<manifest package="com.example.android.aapt2.autonamespace.staticlib.one" />
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/res/values/values.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/res/values/values.xml
deleted file mode 100644
index 3e57b0f..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/res/values/values.xml
+++ /dev/null
@@ -1,27 +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>
-    <!-- An attribute from StaticLibOne -->
-    <attr name="StaticLibOne_attr" format="string" />
-
-    <string name="Foo">Foo</string>
-
-    <declare-styleable name="Widget">
-        <attr name="StaticLibOne_attr" />
-        <attr name="android:text" />
-    </declare-styleable>
-</resources>
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/src/com/example/android/aapt2/autonamespace/staticlib/one/StaticLibOne.java b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/src/com/example/android/aapt2/autonamespace/staticlib/one/StaticLibOne.java
deleted file mode 100644
index 886d48c..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/src/com/example/android/aapt2/autonamespace/staticlib/one/StaticLibOne.java
+++ /dev/null
@@ -1,18 +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.example.android.aapt2.autonamespace.staticlib.one;
-
-public class StaticLibOne { public static int FooId = R.string.Foo; }
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/Android.mk b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/Android.mk
deleted file mode 100644
index c85496d..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/Android.mk
+++ /dev/null
@@ -1,29 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := AaptTestAutoNamespace_LibTwo
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestAutoNamespace_LibOne
-LOCAL_AAPT_NAMESPACES := true
-LOCAL_AAPT_FLAGS := --auto-namespace-static-lib
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/AndroidManifest.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/AndroidManifest.xml
deleted file mode 100644
index 8d3c506..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +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.
--->
-
-<manifest package="com.example.android.aapt2.autonamespace.staticlib.two" />
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/layout/layout_two.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/layout/layout_two.xml
deleted file mode 100644
index fb20220..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/layout/layout_two.xml
+++ /dev/null
@@ -1,18 +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.
--->
-
-<View xmlns:custom="http://schemas.android.com/apk/res-auto"
-      custom:StaticLibOne_attr="@string/FooBar" />
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/values/values.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/values/values.xml
deleted file mode 100644
index c532387..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/values/values.xml
+++ /dev/null
@@ -1,19 +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>
-    <string name="FooBar">@string/Foo</string>
-</resources>
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/src/com/example/android/aapt2/autonamespace/staticlib/two/StaticLibTwo.java b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/src/com/example/android/aapt2/autonamespace/staticlib/two/StaticLibTwo.java
deleted file mode 100644
index 323f53a..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/src/com/example/android/aapt2/autonamespace/staticlib/two/StaticLibTwo.java
+++ /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.
- */
-package com.example.android.aapt2.autonamespace.staticlib.two;
-
-public class StaticLibTwo {
-  // IDs from StaticLibOne
-  public static int FooId = com.example.android.aapt2.autonamespace.staticlib.one.R.string.Foo;
-
-  // IDs from StaticLibTwo
-  public static int FooBarId = R.string.FooBar;
-  public static int LayoutId = R.layout.layout_two;
-}
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk
index 021185f..c723d90 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk
@@ -23,6 +23,7 @@
 LOCAL_SDK_VERSION := current
 LOCAL_MODULE_TAGS := tests
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_MIN_SDK_VERSION := 21
 
 # We need this to retain the R.java generated for this library.
 LOCAL_JAR_EXCLUDE_FILES := none
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk
index 39bd481..90a7f62 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk
@@ -24,6 +24,7 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_MIN_SDK_VERSION := 21
 
 # We need this to retain the R.java generated for this library.
 LOCAL_JAR_EXCLUDE_FILES := none
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index db1561e..d1a70a7 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -256,9 +256,20 @@
     styleable_attr.field_name =
         TransformNestedAttr(attr.name.value(), array_field_name, package_name_to_generate);
 
+    Reference ref = attr;
+    if (attr.name.value().package.empty()) {
+
+      // If the resource does not have a package name, set the package to the unmangled package name
+      // of the styleable declaration because attributes without package names would have been
+      // declared in the same package as the styleable.
+      ref.name = ResourceName(package_name_to_generate, ref.name.value().type,
+                              ref.name.value().entry);
+    }
+
     // Look up the symbol so that we can write out in the comments what are possible legal values
     // for this attribute.
-    const SymbolTable::Symbol* symbol = context_->GetExternalSymbols()->FindByReference(attr);
+    const SymbolTable::Symbol* symbol = context_->GetExternalSymbols()->FindByReference(ref);
+
     if (symbol && symbol->attribute) {
       // Copy the symbol data structure because the returned instance can be destroyed.
       styleable_attr.symbol = *symbol;
@@ -303,7 +314,7 @@
       const ResourceName& attr_name = entry.attr_ref->name.value();
       styleable_comment << "<tr><td><code>{@link #" << entry.field_name << " "
                         << (!attr_name.package.empty() ? attr_name.package
-                                                       : context_->GetCompilationPackage())
+                                                       : package_name_to_generate)
                         << ":" << attr_name.entry << "}</code></td>";
 
       // Only use the comment up until the first '.'. This is to stay compatible with
@@ -374,7 +385,7 @@
 
       StringPiece package_name = attr_name.package;
       if (package_name.empty()) {
-        package_name = context_->GetCompilationPackage();
+        package_name = package_name_to_generate;
       }
 
       std::unique_ptr<IntMember> index_member =
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 10a97d8..fa208be 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -107,6 +107,55 @@
   EXPECT_THAT(output, Not(HasSubstr("com_foo$two")));
 }
 
+TEST(JavaClassGeneratorTest, StyleableAttributesWithDifferentPackageName) {
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("android", 0x01)
+          .SetPackageId("app", 0x7f)
+          .AddValue("app:attr/foo", ResourceId(0x7f010000),
+                    test::AttributeBuilder().Build())
+          .AddValue("app:attr/bar", ResourceId(0x7f010001),
+                    test::AttributeBuilder().Build())
+          .AddValue("android:attr/baz", ResourceId(0x01010000),
+                    test::AttributeBuilder().Build())
+          .AddValue("app:styleable/MyStyleable", ResourceId(0x7f030000),
+                    test::StyleableBuilder()
+                        .AddItem("app:attr/foo", ResourceId(0x7f010000))
+                        .AddItem("attr/bar", ResourceId(0x7f010001))
+                        .AddItem("android:attr/baz", ResourceId(0x01010000))
+                        .Build())
+          .Build();
+
+  std::unique_ptr<IAaptContext> context =
+      test::ContextBuilder()
+          .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+          .SetNameManglerPolicy(NameManglerPolicy{"custom"})
+          .SetCompilationPackage("custom")
+          .Build();
+  JavaClassGenerator generator(context.get(), table.get(), {});
+
+  std::string output;
+  StringOutputStream out(&output);
+  EXPECT_TRUE(generator.Generate("app", &out));
+  out.Flush();
+
+  EXPECT_THAT(output, Not(HasSubstr("public static final int baz=0x01010000;")));
+  EXPECT_THAT(output, HasSubstr("public static final int foo=0x7f010000;"));
+  EXPECT_THAT(output, HasSubstr("public static final int bar=0x7f010001;"));
+
+  EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_android_baz=0;"));
+  EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_foo=1;"));
+  EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_bar=2;"));
+
+  EXPECT_THAT(output, HasSubstr("@link #MyStyleable_android_baz android:baz"));
+  EXPECT_THAT(output, HasSubstr("@link #MyStyleable_foo app:foo"));
+  EXPECT_THAT(output, HasSubstr("@link #MyStyleable_bar app:bar"));
+
+  EXPECT_THAT(output, HasSubstr("@link android.R.attr#baz"));
+  EXPECT_THAT(output, HasSubstr("@link app.R.attr#foo"));
+  EXPECT_THAT(output, HasSubstr("@link app.R.attr#bar"));
+}
+
 TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index d7ebd8c..d40795a 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -39,7 +39,11 @@
  public:
   using xml::Visitor::Visit;
 
-  BaseVisitor(const ResourceFile& file, KeepSet* keep_set) : file_(file), keep_set_(keep_set) {
+  BaseVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set, "...") {
+  }
+
+  BaseVisitor(const ResourceFile& file, KeepSet* keep_set, const std::string& ctor_signature)
+      : file_(file), keep_set_(keep_set), ctor_signature_(ctor_signature) {
   }
 
   void Visit(xml::Element* node) override {
@@ -50,11 +54,11 @@
         // This is a custom view, let's figure out the class name from this.
         std::string package = maybe_package.value().package + "." + node->name;
         if (util::IsJavaClassName(package)) {
-          AddClass(node->line_number, package);
+          AddClass(node->line_number, package, ctor_signature_);
         }
       }
     } else if (util::IsJavaClassName(node->name)) {
-      AddClass(node->line_number, node->name);
+      AddClass(node->line_number, node->name, ctor_signature_);
     }
 
     for (const auto& child : node->children) {
@@ -74,13 +78,18 @@
  protected:
   ResourceFile file_;
   KeepSet* keep_set_;
+  std::string ctor_signature_;
 
-  virtual void AddClass(size_t line_number, const std::string& class_name) {
-    keep_set_->AddConditionalClass({file_.name, file_.source.WithLine(line_number)}, class_name);
+  virtual void AddClass(size_t line_number, const std::string& class_name,
+                        const std::string& ctor_signature) {
+    keep_set_->AddConditionalClass({file_.name, file_.source.WithLine(line_number)},
+        {class_name, ctor_signature});
   }
 
-  void AddMethod(size_t line_number, const std::string& method_name) {
-    keep_set_->AddMethod({file_.name, file_.source.WithLine(line_number)}, method_name);
+  void AddMethod(size_t line_number, const std::string& method_name,
+                 const std::string& method_signature) {
+    keep_set_->AddMethod({file_.name, file_.source.WithLine(line_number)},
+        {method_name, method_signature});
   }
 
   void AddReference(size_t line_number, Reference* ref) {
@@ -100,32 +109,39 @@
 
 class LayoutVisitor : public BaseVisitor {
  public:
-  LayoutVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
+  LayoutVisitor(const ResourceFile& file, KeepSet* keep_set)
+      : BaseVisitor(file, keep_set, "android.content.Context, android.util.AttributeSet") {
   }
 
   void Visit(xml::Element* node) override {
-    bool check_class = false;
-    bool check_name = false;
+    bool is_view = false;
+    bool is_fragment = false;
     if (node->namespace_uri.empty()) {
       if (node->name == "view") {
-        check_class = true;
+        is_view = true;
       } else if (node->name == "fragment") {
-        check_class = check_name = true;
+        is_fragment = true;
       }
     } else if (node->namespace_uri == xml::kSchemaAndroid) {
-      check_name = node->name == "fragment";
+      is_fragment = node->name == "fragment";
     }
 
     for (const auto& attr : node->attributes) {
-      if (check_class && attr.namespace_uri.empty() && attr.name == "class" &&
-          util::IsJavaClassName(attr.value)) {
-        AddClass(node->line_number, attr.value);
-      } else if (check_name && attr.namespace_uri == xml::kSchemaAndroid &&
-                 attr.name == "name" && util::IsJavaClassName(attr.value)) {
-        AddClass(node->line_number, attr.value);
-      } else if (attr.namespace_uri == xml::kSchemaAndroid &&
-                 attr.name == "onClick") {
-        AddMethod(node->line_number, attr.value);
+      if (attr.namespace_uri.empty() && attr.name == "class") {
+        if (util::IsJavaClassName(attr.value)) {
+          if (is_view) {
+            AddClass(node->line_number, attr.value,
+                "android.content.Context, android.util.AttributeSet");
+          } else if (is_fragment) {
+            AddClass(node->line_number, attr.value, "");
+          }
+        }
+      } else if (attr.namespace_uri == xml::kSchemaAndroid && attr.name == "name") {
+        if (is_fragment && util::IsJavaClassName(attr.value)) {
+          AddClass(node->line_number, attr.value, "");
+        }
+      } else if (attr.namespace_uri == xml::kSchemaAndroid && attr.name == "onClick") {
+        AddMethod(node->line_number, attr.value, "android.view.View");
       }
     }
 
@@ -147,9 +163,9 @@
         if (attr.namespace_uri == xml::kSchemaAndroid) {
           if ((attr.name == "actionViewClass" || attr.name == "actionProviderClass") &&
               util::IsJavaClassName(attr.value)) {
-            AddClass(node->line_number, attr.value);
+            AddClass(node->line_number, attr.value, "android.content.Context");
           } else if (attr.name == "onClick") {
-            AddMethod(node->line_number, attr.value);
+            AddMethod(node->line_number, attr.value, "android.view.MenuItem");
           }
         }
       }
@@ -178,7 +194,7 @@
       xml::Attribute* attr =
           node->FindAttribute(xml::kSchemaAndroid, "fragment");
       if (attr && util::IsJavaClassName(attr->value)) {
-        AddClass(node->line_number, attr->value);
+        AddClass(node->line_number, attr->value, "");
       }
     }
 
@@ -200,7 +216,7 @@
     if (attr != nullptr && !attr->value.empty()) {
       std::string name = (attr->value[0] == '.') ? package_ + attr->value : attr->value;
       if (util::IsJavaClassName(name)) {
-        AddClass(node->line_number, name);
+        AddClass(node->line_number, name, "...");
       }
     }
 
@@ -223,7 +239,8 @@
     if (check_class) {
       xml::Attribute* attr = node->FindAttribute({}, "class");
       if (attr && util::IsJavaClassName(attr->value)) {
-        AddClass(node->line_number, attr->value);
+        AddClass(node->line_number, attr->value,
+            "android.content.Context, android.util.AttributeSet");
       }
     }
 
@@ -254,7 +271,14 @@
         if (attr) {
           Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
           if (result) {
-            AddClass(node->line_number, result.value());
+            AddClass(node->line_number, result.value(), "");
+          }
+        }
+        attr = node->FindAttribute(xml::kSchemaAndroid, "appComponentFactory");
+        if (attr) {
+          Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+          if (result) {
+            AddClass(node->line_number, result.value(), "");
           }
         }
         if (main_dex_only_) {
@@ -285,7 +309,7 @@
         if (get_name) {
           Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
           if (result) {
-            AddClass(node->line_number, result.value());
+            AddClass(node->line_number, result.value(), "");
           }
         }
       }
@@ -293,7 +317,8 @@
     BaseVisitor::Visit(node);
   }
 
-  virtual void AddClass(size_t line_number, const std::string& class_name) override {
+  virtual void AddClass(size_t line_number, const std::string& class_name,
+                        const std::string& ctor_signature) override {
     keep_set_->AddManifestClass({file_.name, file_.source.WithLine(line_number)}, class_name);
   }
 
@@ -381,13 +406,15 @@
         printer.Print("-if class **.R$layout { int ")
             .Print(JavaClassGenerator::TransformToFieldName(location.name.entry))
             .Println("; }");
-        printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }");
+        printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(")
+            .Print(entry.first.signature).Println("); }");
       }
     } else {
       for (const UsageLocation& location : entry.second) {
         printer.Print("# Referenced at ").Println(location.source.to_string());
       }
-      printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }");
+      printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(")
+          .Print(entry.first.signature).Println("); }");
     }
     printer.Println();
   }
@@ -396,7 +423,8 @@
     for (const UsageLocation& location : entry.second) {
       printer.Print("# Referenced at ").Println(location.source.to_string());
     }
-    printer.Print("-keepclassmembers class * { *** ").Print(entry.first).Println("(...); }");
+    printer.Print("-keepclassmembers class * { *** ").Print(entry.first.name)
+        .Print("(").Print(entry.first.signature).Println("); }");
     printer.Println();
   }
 }
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index 343272e..01dad0b 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -40,6 +40,11 @@
   Source source;
 };
 
+struct NameAndSignature {
+  std::string name;
+  std::string signature;
+};
+
 class KeepSet {
  public:
   KeepSet() = default;
@@ -51,12 +56,13 @@
     manifest_class_set_[class_name].insert(file);
   }
 
-  inline void AddConditionalClass(const UsageLocation& file, const std::string& class_name) {
-    conditional_class_set_[class_name].insert(file);
+  inline void AddConditionalClass(const UsageLocation& file,
+                                  const NameAndSignature& class_and_signature) {
+    conditional_class_set_[class_and_signature].insert(file);
   }
 
-  inline void AddMethod(const UsageLocation& file, const std::string& method_name) {
-    method_set_[method_name].insert(file);
+  inline void AddMethod(const UsageLocation& file, const NameAndSignature& name_and_signature) {
+    method_set_[name_and_signature].insert(file);
   }
 
   inline void AddReference(const UsageLocation& file, const ResourceName& resource_name) {
@@ -71,8 +77,8 @@
 
   bool conditional_keep_rules_ = false;
   std::map<std::string, std::set<UsageLocation>> manifest_class_set_;
-  std::map<std::string, std::set<UsageLocation>> method_set_;
-  std::map<std::string, std::set<UsageLocation>> conditional_class_set_;
+  std::map<NameAndSignature, std::set<UsageLocation>> method_set_;
+  std::map<NameAndSignature, std::set<UsageLocation>> conditional_class_set_;
   std::map<ResourceName, std::set<UsageLocation>> reference_set_;
 };
 
@@ -100,6 +106,20 @@
   return lhs.name.compare(rhs.name);
 }
 
+//
+// NameAndSignature implementation.
+//
+
+inline bool operator<(const NameAndSignature& lhs, const NameAndSignature& rhs) {
+  if (lhs.name < rhs.name) {
+    return true;
+  }
+  if (lhs.name == rhs.name) {
+    return lhs.signature < rhs.signature;
+  }
+  return false;
+}
+
 }  // namespace proguard
 }  // namespace aapt
 
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index bbc6f9f..83c72d8 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -37,7 +37,11 @@
 TEST(ProguardRulesTest, ManifestRuleDefaultConstructorOnly) {
   std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"(
       <manifest xmlns:android="http://schemas.android.com/apk/res/android">
-        <application android:backupAgent="com.foo.BarBackupAgent">
+        <application
+            android:appComponentFactory="com.foo.BarAppComponentFactory"
+            android:backupAgent="com.foo.BarBackupAgent"
+            android:name="com.foo.BarApplication"
+            >
           <activity android:name="com.foo.BarActivity"/>
           <service android:name="com.foo.BarService"/>
           <receiver android:name="com.foo.BarReceiver"/>
@@ -51,7 +55,9 @@
 
   std::string actual = GetKeepSetString(set);
 
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarActivity { <init>(); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarService { <init>(); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarReceiver { <init>(); }"));
@@ -71,7 +77,7 @@
 
   std::string actual = GetKeepSetString(set);
 
-  EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
 }
 
 TEST(ProguardRulesTest, FragmentClassRuleIsEmitted) {
@@ -85,7 +91,7 @@
 
   std::string actual = GetKeepSetString(set);
 
-  EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
 }
 
 TEST(ProguardRulesTest, FragmentNameAndClassRulesAreEmitted) {
@@ -101,8 +107,8 @@
 
   std::string actual = GetKeepSetString(set);
 
-  EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
-  EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(); }"));
 }
 
 TEST(ProguardRulesTest, NavigationFragmentNameAndClassRulesAreEmitted) {
@@ -128,9 +134,9 @@
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), navigation.get(), &set));
 
   std::string actual = GetKeepSetString(set);
-  EXPECT_THAT(actual, HasSubstr("com.package.Foo"));
-  EXPECT_THAT(actual, HasSubstr("com.package.Bar"));
-  EXPECT_THAT(actual, HasSubstr("com.base.Nested"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.package.Foo { <init>(...); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.package.Bar { <init>(...); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.base.Nested { <init>(...); }"));
 }
 
 TEST(ProguardRulesTest, CustomViewRulesAreEmitted) {
@@ -146,7 +152,8 @@
 
   std::string actual = GetKeepSetString(set);
 
-  EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
+  EXPECT_THAT(actual, HasSubstr(
+      "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
 }
 
 TEST(ProguardRulesTest, IncludedLayoutRulesAreConditional) {
@@ -184,10 +191,10 @@
   std::string actual = GetKeepSetString(set);
 
   EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
-  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+  EXPECT_THAT(actual, HasSubstr(
+      "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
   EXPECT_THAT(actual, HasSubstr("int foo"));
   EXPECT_THAT(actual, HasSubstr("int bar"));
-  EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
 }
 
 TEST(ProguardRulesTest, AliasedLayoutRulesAreConditional) {
@@ -204,11 +211,11 @@
 
   std::string actual = GetKeepSetString(set);
 
-  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+  EXPECT_THAT(actual, HasSubstr(
+      "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
   EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
   EXPECT_THAT(actual, HasSubstr("int foo"));
   EXPECT_THAT(actual, HasSubstr("int bar"));
-  EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
 }
 
 TEST(ProguardRulesTest, NonLayoutReferencesAreUnconditional) {
@@ -226,7 +233,8 @@
   std::string actual = GetKeepSetString(set);
 
   EXPECT_THAT(actual, Not(HasSubstr("-if")));
-  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+  EXPECT_THAT(actual, HasSubstr(
+      "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
 }
 
 TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
@@ -241,7 +249,8 @@
 
   std::string actual = GetKeepSetString(set);
 
-  EXPECT_THAT(actual, HasSubstr("bar_method"));
+  EXPECT_THAT(actual, HasSubstr(
+      "-keepclassmembers class * { *** bar_method(android.view.View); }"));
 }
 
 TEST(ProguardRulesTest, MenuRulesAreEmitted) {
@@ -260,10 +269,45 @@
 
   std::string actual = GetKeepSetString(set);
 
-  EXPECT_THAT(actual, HasSubstr("on_click"));
-  EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
-  EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
+  EXPECT_THAT(actual, HasSubstr(
+      "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(android.content.Context); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(android.content.Context); }"));
   EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
 }
 
+TEST(ProguardRulesTest, TransitionPathMotionRulesAreEmitted) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  std::unique_ptr<xml::XmlResource> transition = test::BuildXmlDom(R"(
+      <changeBounds>
+        <pathMotion class="com.foo.Bar"/>
+      </changeBounds>)");
+  transition->file.name = test::ParseNameOrDie("transition/foo");
+
+  proguard::KeepSet set;
+  ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transition.get(), &set));
+
+  std::string actual = GetKeepSetString(set);
+
+  EXPECT_THAT(actual, HasSubstr(
+      "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+}
+
+TEST(ProguardRulesTest, TransitionRulesAreEmitted) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  std::unique_ptr<xml::XmlResource> transitionSet = test::BuildXmlDom(R"(
+      <transitionSet>
+        <transition class="com.foo.Bar"/>
+      </transitionSet>)");
+  transitionSet->file.name = test::ParseNameOrDie("transition/foo");
+
+  proguard::KeepSet set;
+  ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transitionSet.get(), &set));
+
+  std::string actual = GetKeepSetString(set);
+
+  EXPECT_THAT(actual, HasSubstr(
+      "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/jni/aapt2_jni.cpp b/tools/aapt2/jni/aapt2_jni.cpp
index ad5ad4c..ba9646f 100644
--- a/tools/aapt2/jni/aapt2_jni.cpp
+++ b/tools/aapt2/jni/aapt2_jni.cpp
@@ -25,15 +25,12 @@
 #include "ScopedUtfChars.h"
 
 #include "Diagnostics.h"
+#include "cmd/Compile.h"
+#include "cmd/Link.h"
 #include "util/Util.h"
 
 using android::StringPiece;
 
-namespace aapt {
-extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* iDiagnostics);
-extern int Link(const std::vector<StringPiece>& args, IDiagnostics* iDiagnostics);
-}
-
 /*
  * Converts a java List<String> into C++ vector<ScopedUtfChars>.
  */
@@ -126,7 +123,7 @@
       list_to_utfchars(env, arguments_obj);
   std::vector<StringPiece> compile_args = extract_pieces(compile_args_jni);
   JniDiagnostics diagnostics(env, diagnostics_obj);
-  return aapt::Compile(compile_args, &diagnostics);
+  return aapt::CompileCommand(&diagnostics).Execute(compile_args, &std::cerr);
 }
 
 JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeLink(JNIEnv* env,
@@ -137,7 +134,7 @@
       list_to_utfchars(env, arguments_obj);
   std::vector<StringPiece> link_args = extract_pieces(link_args_jni);
   JniDiagnostics diagnostics(env, diagnostics_obj);
-  return aapt::Link(link_args, &diagnostics);
+  return aapt::LinkCommand(&diagnostics).Execute(link_args, &std::cerr);
 }
 
 JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2Jni_ping(
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index ee4e702..55a32c8 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -282,6 +282,17 @@
       }
     }
 
+    if (options_.version_code_major_default) {
+      if (options_.replace_version) {
+        el->RemoveAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+      }
+      if (el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor") == nullptr) {
+        el->attributes.push_back(
+            xml::Attribute{xml::kSchemaAndroid, "versionCodeMajor",
+                           options_.version_code_major_default.value()});
+      }
+    }
+
     if (el->FindAttribute("", "platformBuildVersionCode") == nullptr) {
       auto versionCode = el->FindAttribute(xml::kSchemaAndroid, "versionCode");
       if (versionCode != nullptr) {
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 98d06fd..3ef57d0 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -52,6 +52,10 @@
   // replace_version is set.
   Maybe<std::string> version_code_default;
 
+  // The version code to set if 'android:versionCodeMajor' is not defined in <manifest> or if
+  // replace_version is set.
+  Maybe<std::string> version_code_major_default;
+
   // The version of the framework being compiled against to set for 'android:compileSdkVersion' in
   // the <manifest> tag.
   Maybe<std::string> compile_sdk_version;
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 5bc004d..adea627 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -329,6 +329,7 @@
   ManifestFixerOptions options;
   options.version_name_default = std::string("Beta");
   options.version_code_default = std::string("0x10000000");
+  options.version_code_major_default = std::string("0x20000000");
 
   std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -347,136 +348,199 @@
   attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
   ASSERT_THAT(attr, NotNull());
   EXPECT_THAT(attr->value, StrEq("0x10000000"));
+
+  attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x20000000"));
 }
 
 TEST_F(ManifestFixerTest, DontUseDefaultVersionNameAndCode) {
-ManifestFixerOptions options;
-options.version_name_default = std::string("Beta");
-options.version_code_default = std::string("0x10000000");
+  ManifestFixerOptions options;
+  options.version_name_default = std::string("Beta");
+  options.version_code_default = std::string("0x10000000");
+  options.version_code_major_default = std::string("0x20000000");
 
-std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
-      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-                package="android"
-                android:versionCode="0x20000000"
-                android:versionName="Alpha" />)EOF",
-                                                          options);
-ASSERT_THAT(doc, NotNull());
+  std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+        <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                  package="android"
+                  android:versionCode="0x00000001"
+                  android:versionCodeMajor="0x00000002"
+                  android:versionName="Alpha" />)EOF",
+                                                            options);
+  ASSERT_THAT(doc, NotNull());
 
-xml::Element* manifest_el = doc->root.get();
-ASSERT_THAT(manifest_el, NotNull());
+  xml::Element* manifest_el = doc->root.get();
+  ASSERT_THAT(manifest_el, NotNull());
 
-xml::Attribute* attr =
-    manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
-ASSERT_THAT(attr, NotNull());
-EXPECT_THAT(attr->value, StrEq("Alpha"));
+  xml::Attribute* attr =
+      manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("Alpha"));
 
-attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
-ASSERT_THAT(attr, NotNull());
-EXPECT_THAT(attr->value, StrEq("0x20000000"));
+  attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x00000001"));
+
+  attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x00000002"));
 }
 
 TEST_F(ManifestFixerTest, ReplaceVersionNameAndCode) {
-ManifestFixerOptions options;
-options.replace_version = true;
-options.version_name_default = std::string("Beta");
-options.version_code_default = std::string("0x10000000");
+  ManifestFixerOptions options;
+  options.replace_version = true;
+  options.version_name_default = std::string("Beta");
+  options.version_code_default = std::string("0x10000000");
+  options.version_code_major_default = std::string("0x20000000");
 
-std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
-    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-              package="android"
-              android:versionCode="0x20000000"
-              android:versionName="Alpha" />)EOF",
-                                                          options);
-ASSERT_THAT(doc, NotNull());
+  std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                package="android"
+                android:versionCode="0x00000001"
+                android:versionCodeMajor="0x00000002"
+                android:versionName="Alpha" />)EOF",
+                                                            options);
+  ASSERT_THAT(doc, NotNull());
 
-xml::Element* manifest_el = doc->root.get();
-ASSERT_THAT(manifest_el, NotNull());
+  xml::Element* manifest_el = doc->root.get();
+  ASSERT_THAT(manifest_el, NotNull());
 
-xml::Attribute* attr =
-    manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
-ASSERT_THAT(attr, NotNull());
-EXPECT_THAT(attr->value, StrEq("Beta"));
+  xml::Attribute* attr =
+      manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("Beta"));
 
-attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
-ASSERT_THAT(attr, NotNull());
-EXPECT_THAT(attr->value, StrEq("0x10000000"));
+  attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x10000000"));
+
+  attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x20000000"));
 }
 
 TEST_F(ManifestFixerTest, ReplaceVersionName) {
-ManifestFixerOptions options;
-options.replace_version = true;
-options.version_name_default = std::string("Beta");
+  ManifestFixerOptions options;
+  options.replace_version = true;
+  options.version_name_default = std::string("Beta");
 
-std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
-  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-            package="android"
-            android:versionCode="0x20000000"
-            android:versionName="Alpha" />)EOF",
-                                                          options);
-ASSERT_THAT(doc, NotNull());
 
-xml::Element* manifest_el = doc->root.get();
-ASSERT_THAT(manifest_el, NotNull());
+  std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+              package="android"
+              android:versionCode="0x00000001"
+              android:versionCodeMajor="0x00000002"
+              android:versionName="Alpha" />)EOF",
+                                                            options);
+  ASSERT_THAT(doc, NotNull());
 
-xml::Attribute* attr =
-    manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
-ASSERT_THAT(attr, NotNull());
-EXPECT_THAT(attr->value, StrEq("Beta"));
+  xml::Element* manifest_el = doc->root.get();
+  ASSERT_THAT(manifest_el, NotNull());
 
-attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
-ASSERT_THAT(attr, NotNull());
-EXPECT_THAT(attr->value, StrEq("0x20000000"));
+  xml::Attribute* attr =
+      manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("Beta"));
+
+  attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x00000001"));
+
+  attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x00000002"));
 }
 
 TEST_F(ManifestFixerTest, ReplaceVersionCode) {
-ManifestFixerOptions options;
-options.replace_version = true;
-options.version_code_default = std::string("0x10000000");
+  ManifestFixerOptions options;
+  options.replace_version = true;
+  options.version_code_default = std::string("0x10000000");
 
-std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+  std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+              package="android"
+              android:versionCode="0x00000001"
+              android:versionCodeMajor="0x00000002"
+              android:versionName="Alpha" />)EOF",
+                                                            options);
+  ASSERT_THAT(doc, NotNull());
+
+  xml::Element* manifest_el = doc->root.get();
+  ASSERT_THAT(manifest_el, NotNull());
+
+  xml::Attribute* attr =
+      manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("Alpha"));
+
+  attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x10000000"));
+
+  attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x00000002"));
+}
+
+TEST_F(ManifestFixerTest, ReplaceVersionCodeMajor) {
+  ManifestFixerOptions options;
+  options.replace_version = true;
+  options.version_code_major_default = std::string("0x20000000");
+
+  std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
   <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-            package="android"
-            android:versionCode="0x20000000"
-            android:versionName="Alpha" />)EOF",
-                                                          options);
-ASSERT_THAT(doc, NotNull());
+          package="android"
+          android:versionCode="0x00000001"
+          android:versionCodeMajor="0x00000002"
+          android:versionName="Alpha" />)EOF",
+                                                            options);
+  ASSERT_THAT(doc, NotNull());
 
-xml::Element* manifest_el = doc->root.get();
-ASSERT_THAT(manifest_el, NotNull());
+  xml::Element* manifest_el = doc->root.get();
+  ASSERT_THAT(manifest_el, NotNull());
 
-xml::Attribute* attr =
-    manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
-ASSERT_THAT(attr, NotNull());
-EXPECT_THAT(attr->value, StrEq("Alpha"));
+  xml::Attribute* attr =
+      manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("Alpha"));
 
-attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
-ASSERT_THAT(attr, NotNull());
-EXPECT_THAT(attr->value, StrEq("0x10000000"));
+  attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x00000001"));
+
+  attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x20000000"));
 }
 
 TEST_F(ManifestFixerTest, DontReplaceVersionNameOrCode) {
-ManifestFixerOptions options;
-options.replace_version = true;
+  ManifestFixerOptions options;
+  options.replace_version = true;
 
-std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android"
-          android:versionCode="0x20000000"
-          android:versionName="Alpha" />)EOF",
-                                                          options);
-ASSERT_THAT(doc, NotNull());
+  std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+            package="android"
+            android:versionCode="0x00000001"
+            android:versionCodeMajor="0x00000002"
+            android:versionName="Alpha" />)EOF",
+                                                            options);
+  ASSERT_THAT(doc, NotNull());
 
-xml::Element* manifest_el = doc->root.get();
-ASSERT_THAT(manifest_el, NotNull());
+  xml::Element* manifest_el = doc->root.get();
+  ASSERT_THAT(manifest_el, NotNull());
 
-xml::Attribute* attr =
-    manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
-ASSERT_THAT(attr, NotNull());
-EXPECT_THAT(attr->value, StrEq("Alpha"));
+  xml::Attribute* attr =
+      manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("Alpha"));
 
-attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
-ASSERT_THAT(attr, NotNull());
-EXPECT_THAT(attr->value, StrEq("0x20000000"));
+  attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x00000001"));
+
+  attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x00000002"));
 }
 
 TEST_F(ManifestFixerTest, EnsureManifestAttributesAreTyped) {
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 28e71cc..3a5d585 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -80,7 +80,7 @@
 
       // Find the attribute in the symbol table and check if it is visible from this callsite.
       const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
-          transformed_reference, callsite_, symbols_, context_->IsAutoNamespace(), &err_str);
+          transformed_reference, callsite_, symbols_, &err_str);
       if (symbol) {
         // Assign our style key the correct ID. The ID may not exist.
         entry.key.id = symbol->id;
@@ -202,18 +202,12 @@
 
 const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
                                                           const CallSite& callsite,
-                                                          SymbolTable* symbols,
-                                                          bool auto_namespace) {
+                                                          SymbolTable* symbols) {
   if (reference.name) {
     const ResourceName& name = reference.name.value();
     if (name.package.empty()) {
       // Use the callsite's package name if no package name was defined.
-      const SymbolTable::Symbol* local_symbol =
-          symbols->FindByName(ResourceName(callsite.package, name.type, name.entry));
-      if (!auto_namespace || local_symbol) {
-        return local_symbol;
-      }
-      return symbols->FindByNameInAnyPackage(name);
+      return symbols->FindByName(ResourceName(callsite.package, name.type, name.entry));
     }
     return symbols->FindByName(name);
   } else if (reference.id) {
@@ -226,9 +220,8 @@
 const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference,
                                                                          const CallSite& callsite,
                                                                          SymbolTable* symbols,
-                                                                         bool auto_namespace,
                                                                          std::string* out_error) {
-  const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols, auto_namespace);
+  const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols);
   if (!symbol) {
     if (out_error) *out_error = "not found";
     return nullptr;
@@ -242,10 +235,10 @@
 }
 
 const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
-    const Reference& reference, const CallSite& callsite, SymbolTable* symbols, bool auto_namespace,
+    const Reference& reference, const CallSite& callsite, SymbolTable* symbols,
     std::string* out_error) {
   const SymbolTable::Symbol* symbol =
-      ResolveSymbolCheckVisibility(reference, callsite, symbols, auto_namespace, out_error);
+      ResolveSymbolCheckVisibility(reference, callsite, symbols, out_error);
   if (!symbol) {
     return nullptr;
   }
@@ -260,10 +253,9 @@
 Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
                                                                const CallSite& callsite,
                                                                SymbolTable* symbols,
-                                                               bool auto_namespace,
                                                                std::string* out_error) {
   const SymbolTable::Symbol* symbol =
-      ResolveAttributeCheckVisibility(reference, callsite, symbols, auto_namespace, out_error);
+      ResolveAttributeCheckVisibility(reference, callsite, symbols, out_error);
   if (!symbol) {
     return {};
   }
@@ -341,8 +333,8 @@
   xml::ResolvePackage(decls, &transformed_reference);
 
   std::string err_str;
-  const SymbolTable::Symbol* s = ResolveSymbolCheckVisibility(
-      transformed_reference, callsite, symbols, context->IsAutoNamespace(), &err_str);
+  const SymbolTable::Symbol* s =
+      ResolveSymbolCheckVisibility(transformed_reference, callsite, symbols, &err_str);
   if (s) {
     // The ID may not exist. This is fine because of the possibility of building
     // against libraries without assigned IDs.
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 7887915..b0b4945 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -36,12 +36,10 @@
   ReferenceLinker() = default;
 
   // Performs name mangling and looks up the resource in the symbol table. Uses the callsite's
-  // package if the reference has no package name defined (implicit), or if auto_namespace is
-  // set try looking in all avaliable packages for a symbol of that name.
+  // package if the reference has no package name defined (implicit).
   // Returns nullptr if the symbol was not found.
   static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference,
-                                                  const CallSite& callsite, SymbolTable* symbols,
-                                                  bool auto_namespace);
+                                                  const CallSite& callsite, SymbolTable* symbols);
 
   // Performs name mangling and looks up the resource in the symbol table. If the symbol is not
   // visible by the reference at the callsite, nullptr is returned.
@@ -49,7 +47,6 @@
   static const SymbolTable::Symbol* ResolveSymbolCheckVisibility(const Reference& reference,
                                                                  const CallSite& callsite,
                                                                  SymbolTable* symbols,
-                                                                 bool auto_namespace,
                                                                  std::string* out_error);
 
   // Same as ResolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute.
@@ -57,14 +54,13 @@
   static const SymbolTable::Symbol* ResolveAttributeCheckVisibility(const Reference& reference,
                                                                     const CallSite& callsite,
                                                                     SymbolTable* symbols,
-                                                                    bool auto_namespace,
                                                                     std::string* out_error);
 
   // Resolves the attribute reference and returns an xml::AaptAttribute if successful.
   // If resolution fails, outError holds the error message.
   static Maybe<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
                                                        const CallSite& callsite,
-                                                       SymbolTable* symbols, bool auto_namespace,
+                                                       SymbolTable* symbols,
                                                        std::string* out_error);
 
   // Writes the resource name to the DiagMessage, using the
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 0b7b1ce..be38b96 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -267,7 +267,7 @@
   std::string error;
   const CallSite call_site{"com.app.test"};
   const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility(
-      *test::BuildReference("com.app.test:string/foo"), call_site, &table, false, &error);
+      *test::BuildReference("com.app.test:string/foo"), call_site, &table, &error);
   ASSERT_THAT(symbol, NotNull());
   EXPECT_TRUE(error.empty());
 }
@@ -285,13 +285,13 @@
   std::string error;
   const CallSite call_site{"com.app.ext"};
 
-  EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(*test::BuildReference("com.app.test:attr/foo"),
-                                                    call_site, &table, false, &error));
+  EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(
+      *test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error));
   EXPECT_FALSE(error.empty());
 
   error = "";
   ASSERT_TRUE(ReferenceLinker::CompileXmlAttribute(
-      *test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, false, &error));
+      *test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, &error));
   EXPECT_TRUE(error.empty());
 }
 
@@ -303,74 +303,19 @@
                          .AddSymbol("com.app.lib:string/foo", ResourceId(0x7f010001))
                          .Build());
 
-  const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(
-      *test::BuildReference("string/foo"), CallSite{"com.app.test"}, &table, false);
+  const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
+                                                                CallSite{"com.app.test"}, &table);
   ASSERT_THAT(s, NotNull());
   EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010000)));
 
   s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
-                                     &table, false);
+                                     &table);
   ASSERT_THAT(s, NotNull());
   EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010001)));
 
   EXPECT_THAT(ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
-                                             CallSite{"com.app.bad"}, &table, false),
+                                             CallSite{"com.app.bad"}, &table),
               IsNull());
 }
 
-TEST(ReferenceLinkerTest, AutomaticNamespace) {
-  NameMangler mangler(NameManglerPolicy{"com.example.thislib"});
-  SymbolTable table(&mangler);
-  table.AppendSource(
-      test::StaticSymbolSourceBuilder()
-          .AddSymbol("com.example.thislib:string/thislib_string", ResourceId(0x7f010006))
-          .AddSymbol("com.example.thislib:string/explicit_override_string", ResourceId(0x7f010007))
-          .Build());
-  // Lib2 is higher priority than lib1
-  table.AppendSource(
-      test::StaticSymbolSourceBuilder()
-          .AddSymbol("com.example.lib2:string/lib2_string", ResourceId(0x7f010003))
-          .AddSymbol("com.example.lib2:string/explicit_override_string", ResourceId(0x7f010004))
-          .AddSymbol("com.example.lib2:string/implicit_override_string", ResourceId(0x7f010005))
-          .Build());
-  table.AppendSource(
-      test::StaticSymbolSourceBuilder()
-          .AddSymbol("com.example.lib1:string/explicit_override_string", ResourceId(0x7f010001))
-          .AddSymbol("com.example.lib1:string/implicit_override_string", ResourceId(0x7f010002))
-          .Build());
-
-  // Sanity test: Local references are still fine.
-  const SymbolTable::Symbol* s =
-      ReferenceLinker::ResolveSymbol(*test::BuildReference("string/thislib_string"),
-                                     CallSite{"com.example.thislib"}, &table, true);
-  ASSERT_THAT(s, NotNull());
-  EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010006)));
-
-  // Local references are fine, even if clash with remote ones.
-  s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/explicit_override_string"),
-                                     CallSite{"com.example.thislib"}, &table, true);
-  ASSERT_THAT(s, NotNull());
-  EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010007)));
-
-  // An unqualified reference to lib2 is rewritten
-  s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/lib2_string"),
-                                     CallSite{"com.example.thislib"}, &table, true);
-  ASSERT_THAT(s, NotNull());
-  EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010003)));
-
-  // Qualified references are left alone.
-  s = ReferenceLinker::ResolveSymbol(
-      *test::BuildReference("com.example.lib2:string/explicit_override_string"),
-      CallSite{"com.example.thislib"}, &table, true);
-  ASSERT_THAT(s, NotNull());
-  EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010004)));
-
-  // Implicit overrides respect priority ordering.
-  s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/implicit_override_string"),
-                                     CallSite{"com.example.thislib"}, &table, true);
-  ASSERT_THAT(s, NotNull());
-  EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010005)));
-
-  //
-}
 }  // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 91a55b3..afb8ae0 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -281,8 +281,17 @@
           dst_config_value->value = std::move(new_file_ref);
 
         } else {
+          Maybe<std::string> original_comment = (dst_config_value->value)
+              ? dst_config_value->value->GetComment() : Maybe<std::string>();
+
           dst_config_value->value = std::unique_ptr<Value>(
               src_config_value->value->Clone(&master_table_->string_pool));
+
+          // Keep the comment from the original resource and ignore all comments from overlaying
+          // resources
+          if (overlay && original_comment) {
+            dst_config_value->value->SetComment(original_comment.value());
+          }
         }
       }
     }
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index cf504c4..79a734b 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -178,6 +178,49 @@
               Pointee(Field(&BinaryPrimitive::value, Field(&android::Res_value::data, Eq(0u)))));
 }
 
+TEST_F(TableMergerTest, DoNotOverrideResourceComment) {
+  std::unique_ptr<Value> foo_original = ResourceUtils::TryParseBool("true");
+  foo_original->SetComment(android::StringPiece("Original foo comment"));
+  std::unique_ptr<Value> bar_original = ResourceUtils::TryParseBool("true");
+
+  std::unique_ptr<Value> foo_overlay =  ResourceUtils::TryParseBool("false");
+  foo_overlay->SetComment(android::StringPiece("Overlay foo comment"));
+  std::unique_ptr<Value> bar_overlay =  ResourceUtils::TryParseBool("false");
+  bar_overlay->SetComment(android::StringPiece("Overlay bar comment"));
+  std::unique_ptr<Value> baz_overlay =  ResourceUtils::TryParseBool("false");
+  baz_overlay->SetComment(android::StringPiece("Overlay baz comment"));
+
+  std::unique_ptr<ResourceTable> base =
+      test::ResourceTableBuilder()
+          .SetPackageId("", 0x00)
+          .AddValue("bool/foo", std::move(foo_original))
+          .AddValue("bool/bar", std::move(bar_original))
+          .Build();
+
+  std::unique_ptr<ResourceTable> overlay =
+      test::ResourceTableBuilder()
+          .SetPackageId("", 0x00)
+          .AddValue("bool/foo", std::move(foo_overlay))
+          .AddValue("bool/bar", std::move(bar_overlay))
+          .AddValue("bool/baz", std::move(baz_overlay))
+          .Build();
+
+  ResourceTable final_table;
+  TableMergerOptions options;
+  options.auto_add_overlay = true;
+  TableMerger merger(context_.get(), &final_table, options);
+
+  ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/));
+  ASSERT_TRUE(merger.Merge({}, overlay.get(), true /*overlay*/));
+
+  BinaryPrimitive* foo = test::GetValue<BinaryPrimitive>(&final_table, "com.app.a:bool/foo");
+  EXPECT_THAT(foo, Pointee(Property(&BinaryPrimitive::GetComment, StrEq("Original foo comment"))));
+  BinaryPrimitive* bar = test::GetValue<BinaryPrimitive>(&final_table, "com.app.a:bool/bar");
+  EXPECT_THAT(bar, Pointee(Property(&BinaryPrimitive::GetComment, StrEq(""))));
+  BinaryPrimitive* baz = test::GetValue<BinaryPrimitive>(&final_table, "com.app.a:bool/baz");
+  EXPECT_THAT(baz, Pointee(Property(&BinaryPrimitive::GetComment, StrEq("Overlay baz comment"))));
+}
+
 TEST_F(TableMergerTest, OverrideSameResourceIdsWithOverlay) {
   std::unique_ptr<ResourceTable> base =
       test::ResourceTableBuilder()
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index 420a127..160ff92 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -97,8 +97,8 @@
         attr_ref.private_reference = maybe_package.value().private_namespace;
 
         std::string err_str;
-        attr.compiled_attribute = ReferenceLinker::CompileXmlAttribute(
-            attr_ref, callsite_, symbols_, context_->IsAutoNamespace(), &err_str);
+        attr.compiled_attribute =
+            ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, symbols_, &err_str);
 
         if (!attr.compiled_attribute) {
           DiagMessage error_msg(source);
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index d321f8f..ef99355 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -18,7 +18,6 @@
 
 #include "test/Test.h"
 
-using ::testing::Eq;
 using ::testing::IsNull;
 using ::testing::NotNull;
 
@@ -71,29 +70,12 @@
                                                 .Build())
                            .AddPublicSymbol("com.app.test:attr/attr", ResourceId(0x7f010002),
                                             test::AttributeBuilder().Build())
-                           .AddPublicSymbol("com.app.lib:string/lib_string", ResourceId(0x7f020003))
                            .Build())
                    .Build();
-
-    auto_namespace_context_ =
-        test::ContextBuilder()
-            .SetCompilationPackage("com.app.test")
-            .SetNameManglerPolicy(NameManglerPolicy{"com.app.test", {"com.android.support"}})
-            .SetAutoNamespace(true)
-            .AddSymbolSource(
-                test::StaticSymbolSourceBuilder()
-                    .AddPublicSymbol("android:attr/text", ResourceId(0x01010003),
-                                     test::AttributeBuilder()
-                                         .SetTypeMask(android::ResTable_map::TYPE_STRING)
-                                         .Build())
-                    .AddPublicSymbol("com.app.lib:string/lib_string", ResourceId(0x7f020003))
-                    .Build())
-            .Build();
   }
 
  protected:
   std::unique_ptr<IAaptContext> context_;
-  std::unique_ptr<IAaptContext> auto_namespace_context_;
 };
 
 TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
@@ -213,31 +195,6 @@
   EXPECT_EQ(make_value(ResourceId(0x7f020001)), ref->id);
 }
 
-TEST_F(XmlReferenceLinkerTest, LinkAutoNamespaceResReference) {
-  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
-      <View
-          xmlns:android="http://schemas.android.com/apk/res/android"
-          android:text="@string/lib_string" />)");
-
-  XmlReferenceLinker linker;
-  // Should not link with auto-namespace support disabled.
-  ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
-  // Should link with auto-namespace enabled.
-  ASSERT_TRUE(linker.Consume(auto_namespace_context_.get(), doc.get()));
-
-  xml::Element* view_el = doc->root.get();
-  ASSERT_THAT(view_el, NotNull());
-
-  xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "text");
-  ASSERT_THAT(xml_attr, NotNull());
-  ASSERT_TRUE(xml_attr->compiled_attribute);
-  EXPECT_EQ(make_value(ResourceId(0x01010003)), xml_attr->compiled_attribute.value().id);
-  Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
-  ASSERT_THAT(ref, NotNull());
-  ASSERT_TRUE(ref->name);
-  EXPECT_EQ(make_value(ResourceId(0x7f020003)), ref->id);
-}
-
 TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
       <View xmlns:app="http://schemas.android.com/apk/res/android" app:attr="@app:id/id">
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index 9cfd730..e92c121 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -26,6 +26,7 @@
 #include "ResourceUtils.h"
 #include "ValueVisitor.h"
 #include "configuration/ConfigurationParser.h"
+#include "cmd/Util.h"
 #include "filter/AbiFilter.h"
 #include "filter/Filter.h"
 #include "format/Archive.h"
@@ -98,10 +99,6 @@
         util::make_unique<SourcePathDiagnostics>(Source{source}, context_->GetDiagnostics());
   }
 
-  bool IsAutoNamespace() override {
-    return context_->IsAutoNamespace();
-  }
-
  private:
   IAaptContext* context_;
   std::unique_ptr<SourcePathDiagnostics> source_diag_;
@@ -269,7 +266,7 @@
 
   // Make sure the first element is <manifest> with package attribute.
   xml::Element* manifest_el = manifest->root.get();
-  if (manifest_el == nullptr) {
+  if (!manifest_el) {
     return false;
   }
 
@@ -278,21 +275,35 @@
     return false;
   }
 
-  // Update the versionCode attribute.
-  xml::Attribute* versionCode = manifest_el->FindAttribute(kSchemaAndroid, "versionCode");
-  if (versionCode == nullptr) {
+  // Retrieve the versionCode attribute.
+  auto version_code = manifest_el->FindAttribute(kSchemaAndroid, "versionCode");
+  if (!version_code) {
     diag->Error(DiagMessage(manifest->file.source) << "manifest must have a versionCode attribute");
     return false;
   }
 
-  auto* compiled_version = ValueCast<BinaryPrimitive>(versionCode->compiled_value.get());
-  if (compiled_version == nullptr) {
+  auto version_code_value = ValueCast<BinaryPrimitive>(version_code->compiled_value.get());
+  if (!version_code_value) {
     diag->Error(DiagMessage(manifest->file.source) << "versionCode is invalid");
     return false;
   }
 
-  int new_version = compiled_version->value.data + artifact.version;
-  versionCode->compiled_value = ResourceUtils::TryParseInt(std::to_string(new_version));
+  // Retrieve the versionCodeMajor attribute.
+  auto version_code_major = manifest_el->FindAttribute(kSchemaAndroid, "versionCodeMajor");
+  BinaryPrimitive* version_code_major_value = nullptr;
+  if (version_code_major) {
+    version_code_major_value = ValueCast<BinaryPrimitive>(version_code_major->compiled_value.get());
+    if (!version_code_major_value) {
+      diag->Error(DiagMessage(manifest->file.source) << "versionCodeMajor is invalid");
+      return false;
+    }
+  }
+
+  // Calculate and set the updated version code
+  uint64_t major = (version_code_major_value)
+                  ? ((uint64_t) version_code_major_value->value.data) << 32 : 0;
+  uint64_t new_version = (major | version_code_value->value.data) + artifact.version;
+  SetLongVersionCode(manifest_el, new_version);
 
   // Check to see if the minSdkVersion needs to be updated.
   if (artifact.android_sdk) {
diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
index a3a7719..30dad802 100644
--- a/tools/aapt2/process/IResourceTableConsumer.h
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -50,7 +50,6 @@
   virtual NameMangler* GetNameMangler() = 0;
   virtual bool IsVerbose() = 0;
   virtual int GetMinSdkVersion() = 0;
-  virtual bool IsAutoNamespace() = 0;
 };
 
 struct IResourceTableConsumer {
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index ef2e448..fc4c9b5 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -114,16 +114,6 @@
   return shared_symbol.get();
 }
 
-const SymbolTable::Symbol* SymbolTable::FindByNameInAnyPackage(const ResourceName& name) {
-  for (auto& source : sources_) {
-    std::string package = source->GetPackageForSymbol(name);
-    if (!package.empty()) {
-      return FindByName(ResourceName(package, name.type, name.entry));
-    }
-  }
-  return {};
-}
-
 const SymbolTable::Symbol* SymbolTable::FindById(const ResourceId& id) {
   if (const std::shared_ptr<Symbol>& s = id_cache_.get(id)) {
     return s.get();
@@ -221,25 +211,6 @@
   return symbol;
 }
 
-std::string ResourceTableSymbolSource::GetPackageForSymbol(const ResourceName& name) {
-  for (auto& package : table_->packages) {
-    ResourceTableType* type = package->FindType(name.type);
-    if (type == nullptr) {
-      continue;
-    }
-    ResourceEntry* entry = type->FindEntry(name.entry);
-    if (entry == nullptr) {
-      continue;
-    }
-    return package->name;
-  }
-  if (name.type == ResourceType::kAttr) {
-    // Recurse and try looking up a private attribute.
-    return GetPackageForSymbol(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
-  }
-  return {};
-}
-
 bool AssetManagerSymbolSource::AddAssetPath(const StringPiece& path) {
   int32_t cookie = 0;
   return assets_.addAssetPath(android::String8(path.data(), path.size()), &cookie);
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index c798cbb..51a2e37 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -89,13 +89,6 @@
   // results are stored in a cache which may evict entries on subsequent calls.
   const Symbol* FindByName(const ResourceName& name);
 
-  // Finds the symbol from any package, for use as part of automatic conversion to namespaces.
-  // This returns the symbol from the highest priority package,
-  // which mimics the behavior of the resource merger and overlays.
-  // NOTE: Never hold on to the result between calls to FindByXXX. The
-  // results are stored in a cache which may evict entries on subsequent calls.
-  const Symbol* FindByNameInAnyPackage(const ResourceName& name);
-
   // NOTE: Never hold on to the result between calls to FindByXXX. The
   // results are stored in a cache which may evict entries on subsequent calls.
   const Symbol* FindById(const ResourceId& id);
@@ -160,11 +153,6 @@
 
   virtual std::unique_ptr<SymbolTable::Symbol> FindByName(
       const ResourceName& name) = 0;
-  // Finds the name of a symbol from any package,
-  // for use as part of automatic conversion to namespaces.
-  // This returns the symbol from the highest priority package,
-  // which mimics the behavior of the resource merger and overlays.
-  virtual std::string GetPackageForSymbol(const ResourceName& name) = 0;
   virtual std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) = 0;
 
   // Default implementation tries the name if it exists, else the ID.
@@ -189,7 +177,6 @@
   std::unique_ptr<SymbolTable::Symbol> FindByName(
       const ResourceName& name) override;
 
-  std::string GetPackageForSymbol(const ResourceName& name) override;
   std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override {
     return {};
   }
@@ -210,9 +197,6 @@
 
   std::unique_ptr<SymbolTable::Symbol> FindByName(
       const ResourceName& name) override;
-  std::string GetPackageForSymbol(const ResourceName& name) override {
-    return {};
-  }
   std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override;
   std::unique_ptr<SymbolTable::Symbol> FindByReference(
       const Reference& ref) override;
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index df40b26..1f59d70 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -120,39 +120,4 @@
   EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.other:id/foo")), IsNull());
 }
 
-TEST(SymbolTableTest, FindByNameInAnyPackage) {
-  // This represents lib3 --depends-on--> lib2 --depends-on--> lib1
-
-  NameMangler mangler(NameManglerPolicy{"com.example.lib3"});
-  SymbolTable symbol_table(&mangler);
-  // Lib2 has higher precedence than lib1, as it is closer to the current library (lib3)
-  // in the dependency graph.
-
-  symbol_table.AppendSource(test::StaticSymbolSourceBuilder()
-                                .AddPublicSymbol("com.example.lib1:string/foo", ResourceId())
-                                .AddSymbol("com.example.lib1:attr/foo", ResourceId(),
-                                           test::AttributeBuilder()
-                                               .SetTypeMask(android::ResTable_map::TYPE_FLAGS)
-                                               .AddItem("one", 0x01)
-                                               .AddItem("two", 0x02)
-                                               .Build())
-                                .Build());
-  symbol_table.PrependSource(test::StaticSymbolSourceBuilder()
-                                 .AddPublicSymbol("com.example.lib2:string/foo", ResourceId())
-                                 .Build());
-
-  // Sanity test
-  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("string/foo")), IsNull());
-
-  // Test public symbol resolution
-  const SymbolTable::Symbol* const found_string =
-      symbol_table.FindByNameInAnyPackage(test::ParseNameOrDie("string/foo"));
-  ASSERT_THAT(found_string, NotNull());
-
-  // Test attr resolution
-  const SymbolTable::Symbol* const found_attr =
-      symbol_table.FindByNameInAnyPackage(test::ParseNameOrDie("attr/foo"));
-  ASSERT_THAT(found_attr, NotNull());
-}
-
 }  // namespace aapt
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index a07d79f..0564db0 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -81,10 +81,6 @@
     return min_sdk_version_;
   }
 
-  bool IsAutoNamespace() override {
-    return auto_namespace_;
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(Context);
 
@@ -97,7 +93,6 @@
   NameMangler name_mangler_;
   SymbolTable symbols_;
   int min_sdk_version_;
-  bool auto_namespace_;
 };
 
 class ContextBuilder {
@@ -132,11 +127,6 @@
     return *this;
   }
 
-  ContextBuilder& SetAutoNamespace(bool auto_namespace) {
-    context_->auto_namespace_ = auto_namespace;
-    return *this;
-  }
-
   std::unique_ptr<Context> Build() { return std::move(context_); }
 
  private:
@@ -182,15 +172,6 @@
       return nullptr;
     }
 
-    std::string GetPackageForSymbol(const ResourceName& name) override {
-      for (auto const& imap : name_map_) {
-        if (imap.first.type == name.type && imap.first.entry == name.entry) {
-          return imap.first.package;
-        }
-      }
-      return "";
-    }
-
     std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override {
       auto iter = id_map_.find(id);
       if (iter != id_map_.end()) {
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index d1c9ca1..9bef54e5 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -297,6 +297,53 @@
   return true;
 }
 
+std::string Utf8ToModifiedUtf8(const std::string& utf8) {
+  // Java uses Modified UTF-8 which only supports the 1, 2, and 3 byte formats of UTF-8. To encode
+  // 4 byte UTF-8 codepoints, Modified UTF-8 allows the use of surrogate pairs in the same format
+  // of CESU-8 surrogate pairs. Calculate the size of the utf8 string with all 4 byte UTF-8
+  // codepoints replaced with 2 3 byte surrogate pairs
+  size_t modified_size = 0;
+  const size_t size = utf8.size();
+  for (size_t i = 0; i < size; i++) {
+    if (((uint8_t) utf8[i] >> 4) == 0xF) {
+      modified_size += 6;
+      i += 3;
+    } else {
+      modified_size++;
+    }
+  }
+
+  // Early out if no 4 byte codepoints are found
+  if (size == modified_size) {
+    return utf8;
+  }
+
+  std::string output;
+  output.reserve(modified_size);
+  for (size_t i = 0; i < size; i++) {
+    if (((uint8_t) utf8[i] >> 4) == 0xF) {
+      auto codepoint = (char32_t) utf32_from_utf8_at(utf8.data(), size, i, nullptr);
+
+      // Calculate the high and low surrogates as UTF-16 would
+      char32_t high = ((codepoint - 0x10000) / 0x400) + 0xD800;
+      char32_t low = ((codepoint - 0x10000) % 0x400) + 0xDC00;
+
+      // Encode each surrogate in UTF-8
+      output.push_back((char) (0xE4 | ((high >> 12) & 0xF)));
+      output.push_back((char) (0x80 | ((high >> 6) & 0x3F)));
+      output.push_back((char) (0x80 | (high & 0x3F)));
+      output.push_back((char) (0xE4 | ((low >> 12) & 0xF)));
+      output.push_back((char) (0x80 | ((low >> 6) & 0x3F)));
+      output.push_back((char) (0x80 | (low & 0x3F)));
+      i += 3;
+    } else {
+      output.push_back(utf8[i]);
+    }
+  }
+
+  return output;
+}
+
 std::u16string Utf8ToUtf16(const StringPiece& utf8) {
   ssize_t utf16_length = utf8_to_utf16_length(
       reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 0eb35d1..36b7333 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -197,6 +197,9 @@
   return error_.empty();
 }
 
+// Converts a UTF8 string into Modified UTF8
+std::string Utf8ToModifiedUtf8(const std::string& utf8);
+
 // Converts a UTF8 string to a UTF16 string.
 std::u16string Utf8ToUtf16(const android::StringPiece& utf8);
 std::string Utf16ToUtf8(const android::StringPiece16& utf16);
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index 402e5a4..a023494 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -38,6 +38,7 @@
                               EndNamespaceHandler);
   XML_SetCharacterDataHandler(parser_, CharacterDataHandler);
   XML_SetCommentHandler(parser_, CommentDataHandler);
+  XML_SetCdataSectionHandler(parser_, StartCdataSectionHandler, EndCdataSectionHandler);
   event_queue_.push(EventData{Event::kStartDocument, 0, depth_++});
 }
 
@@ -287,6 +288,22 @@
                                       parser->depth_, comment});
 }
 
+void XMLCALL XmlPullParser::StartCdataSectionHandler(void* user_data) {
+  XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+
+  parser->event_queue_.push(EventData{Event::kCdataStart,
+                                      XML_GetCurrentLineNumber(parser->parser_),
+                                      parser->depth_ });
+}
+
+void XMLCALL XmlPullParser::EndCdataSectionHandler(void* user_data) {
+  XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+
+  parser->event_queue_.push(EventData{Event::kCdataEnd,
+                                      XML_GetCurrentLineNumber(parser->parser_),
+                                      parser->depth_ });
+}
+
 Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
                                  const StringPiece& name) {
   auto iter = parser->FindAttribute("", name);
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index 63db66f..6ebaa28 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -52,6 +52,8 @@
     kEndElement,
     kText,
     kComment,
+    kCdataStart,
+    kCdataEnd,
   };
 
   /**
@@ -159,6 +161,8 @@
   static void XMLCALL EndElementHandler(void* user_data, const char* name);
   static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix);
   static void XMLCALL CommentDataHandler(void* user_data, const char* comment);
+  static void XMLCALL StartCdataSectionHandler(void* user_data);
+  static void XMLCALL EndCdataSectionHandler(void* user_data);
 
   struct EventData {
     Event event;
@@ -223,6 +227,10 @@
       return out << "Text";
     case XmlPullParser::Event::kComment:
       return out << "Comment";
+    case XmlPullParser::Event::kCdataStart:
+      return out << "CdataStart";
+    case XmlPullParser::Event::kCdataEnd:
+      return out << "CdataEnd";
   }
   return out;
 }
@@ -240,6 +248,8 @@
       case Event::kText:
       case Event::kComment:
       case Event::kStartElement:
+      case Event::kCdataStart:
+      case Event::kCdataEnd:
         return true;
       default:
         break;
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 9db3f02..018e9c9 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -69,12 +69,19 @@
         self.raw = raw.strip(" {;")
         self.blame = blame
 
+        # drop generics for now; may need multiple passes
+        raw = re.sub("<[^<]+?>", "", raw)
+        raw = re.sub("<[^<]+?>", "", 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)
 
+        # 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] == "=":
@@ -97,25 +104,39 @@
         self.raw = raw.strip(" {;")
         self.blame = blame
 
-        # drop generics for now
-        raw = re.sub("<.+?>", "", raw)
+        # drop generics for now; may need multiple passes
+        raw = re.sub("<[^<]+?>", "", raw)
+        raw = re.sub("<[^<]+?>", "", raw)
 
-        raw = re.split("[\s(),;]+", raw)
+        # 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)
 
-        for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default"]:
+        for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default", "operator"]:
             while r in raw: raw.remove(r)
 
         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 throws
         self.throws = []
-        target = self.args
-        for r in raw[2:]:
-            if r == "throws": target = self.throws
-            else: target.append(r)
+        for throw in re.split(",\s*", raw_throws):
+            self.throws.append(throw)
+
         self.ident = ident(self.raw)
 
     def __hash__(self):
@@ -135,12 +156,18 @@
         self.fields = []
         self.methods = []
 
+        # 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))
 
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index a8411aa..ce9becd 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -524,9 +524,12 @@
     0xFE837: (ord('0'), COMBINING_KEYCAP),
 }
 
+# This is used to define the emoji that should have the same glyph.
+# i.e. previously we had gender based Kiss (0x1F48F), which had the same glyph
+# with Kiss: Woman, Man (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468)
+# in that case a valid row would be:
+# (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468): 0x1F48F,
 ZWJ_IDENTICALS = {
-    # KISS
-    (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468): 0x1F48F,
 }
 
 SAME_FLAG_MAPPINGS = [
diff --git a/tools/genprotos.sh b/tools/genprotos.sh
new file mode 100755
index 0000000..f901c9f
--- /dev/null
+++ b/tools/genprotos.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# TODO This should not be needed. If you set a custom OUT_DIR or OUT_DIR_COMMON_BASE you can
+# end up with a command that is extremely long, potentially going passed MAX_ARG_STRLEN due to
+# the way sbox rewrites the command. See b/70221552.
+
+set -e
+
+location_aprotoc=$1
+location_protoc=$2
+location_soong_zip=$3
+genDir=$4
+depfile=$5
+in=$6
+out=$7
+
+mkdir -p ${genDir}/${in} && \
+  ${location_aprotoc} --plugin=${location_protoc} \
+                      --dependency_out=${depfile} \
+                      --javastream_out=${genDir}/${in} \
+                      -Iexternal/protobuf/src \
+                      -I . \
+                      ${in} && \
+  ${location_soong_zip} -jar -o ${out} -C ${genDir}/${in} -D ${genDir}/${in}
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 3689a8f..639f980 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -110,9 +110,7 @@
     N = descriptor->field_count();
     for (int i=0; i<N; i++) {
         const FieldDescriptor* field = descriptor->field(i);
-        if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
-            sections[field->name()] = field;
-        }
+        sections[field->name()] = field;
     }
 
     printf("IncidentSection const INCIDENT_SECTIONS[] = {\n");
@@ -404,7 +402,7 @@
     for (int i=0; i<descriptor->field_count(); i++) {
         const FieldDescriptor* field = descriptor->field(i);
 
-        if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
+        if (field->type() != FieldDescriptor::TYPE_MESSAGE && field->type() != FieldDescriptor::TYPE_STRING) {
             continue;
         }
         const SectionFlags s = getSectionFlags(field);
@@ -412,8 +410,7 @@
             case SECTION_NONE:
                 continue;
             case SECTION_FILE:
-                printf("    new FileSection(%d, \"%s\", %s),\n", field->number(), s.args().c_str(),
-                       s.device_specific() ? "true" : "false");
+                printf("    new FileSection(%d, \"%s\"),\n", field->number(), s.args().c_str());
                 break;
             case SECTION_COMMAND:
                 printf("    new CommandSection(%d,", field->number());
@@ -457,13 +454,13 @@
         const FieldDescriptor* field = fieldsInOrder[i];
         const string fieldName = getFieldName(field);
         const Destination fieldDest = getFieldDest(field);
-        const string fieldMessageName = getMessageName(field->message_type(), fieldDest);
-
-        skip[i] = true;
-
         if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
+            printPrivacy(fieldName, field, "NULL", fieldDest, "NULL");
             continue;
         }
+
+        skip[i] = true;
+        const string fieldMessageName = getMessageName(field->message_type(), fieldDest);
         // generate privacy flags for each section.
         if (generatePrivacyFlags(field->message_type(), fieldDest, variableNames, &parents)) {
             printPrivacy(fieldName, field, fieldMessageName, fieldDest, "NULL");
diff --git a/vr/Android.bp b/vr/Android.bp
index b5904d6..775ec96 100644
--- a/vr/Android.bp
+++ b/vr/Android.bp
@@ -26,6 +26,7 @@
 // Java platform library for vr stuff.
 java_library {
     name: "com.google.vr.platform",
+    installable: true,
     owner: "google",
     required: [
         "libdvr_loader",
diff --git a/vr/java/com/google/vr/platform/DeviceInfo.java b/vr/java/com/google/vr/platform/DeviceInfo.java
index f6da66b..6a4617d 100644
--- a/vr/java/com/google/vr/platform/DeviceInfo.java
+++ b/vr/java/com/google/vr/platform/DeviceInfo.java
@@ -1,5 +1,6 @@
 package com.google.vr.platform;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.SystemProperties;
 
 /**
@@ -13,6 +14,7 @@
     /**
      * Returns true if this device boots directly in VR mode.
      */
+    @UnsupportedAppUsage
     public static boolean getVrBoot() {
         return SystemProperties.getBoolean(VR_MODE_BOOT, false);
     }
diff --git a/vr/java/com/google/vr/platform/Dvr.java b/vr/java/com/google/vr/platform/Dvr.java
index b07d634..41dcd87 100644
--- a/vr/java/com/google/vr/platform/Dvr.java
+++ b/vr/java/com/google/vr/platform/Dvr.java
@@ -1,5 +1,7 @@
 package com.google.vr.platform;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * Class to load the dvr api.
  * @hide
@@ -10,6 +12,7 @@
      *
      * @return A Long object describing the handle returned by dlopen.
      */
+    @UnsupportedAppUsage
     public static Long loadLibrary() {
         // Load a thin JNI library that runs dlopen on request.
         System.loadLibrary("dvr_loader");
diff --git a/wifi/OWNERS b/wifi/OWNERS
index 0efa464..0601047 100644
--- a/wifi/OWNERS
+++ b/wifi/OWNERS
@@ -1,5 +1,6 @@
 set noparent
 
 etancohen@google.com
+mplass@google.com
+rpius@google.com
 satk@google.com
-silberst@google.com
diff --git a/wifi/java/android/net/wifi/ITrafficStateCallback.aidl b/wifi/java/android/net/wifi/ITrafficStateCallback.aidl
new file mode 100644
index 0000000..0c8e777
--- /dev/null
+++ b/wifi/java/android/net/wifi/ITrafficStateCallback.aidl
@@ -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 android.net.wifi;
+
+/**
+ * Interface for Traffic state callback.
+ *
+ * @hide
+ */
+oneway interface ITrafficStateCallback
+{
+   /**
+    * Callback invoked to inform clients about the current traffic state.
+    *
+    * @param state One of the values: {@link #DATA_ACTIVITY_NONE}, {@link #DATA_ACTIVITY_IN},
+    * {@link #DATA_ACTIVITY_OUT} & {@link #DATA_ACTIVITY_INOUT}.
+    * @hide
+    */
+   void onStateChanged(int state);
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 66ccc6c..5631919 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -26,6 +26,7 @@
 import android.net.DhcpInfo;
 import android.net.Network;
 import android.net.wifi.ISoftApCallback;
+import android.net.wifi.ITrafficStateCallback;
 import android.net.wifi.PasspointManagementObjectDefinition;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiActivityEnergyInfo;
@@ -147,6 +148,8 @@
 
     boolean setWifiApConfiguration(in WifiConfiguration wifiConfig, String packageName);
 
+    void notifyUserOfApBandConversion(String packageName);
+
     Messenger getWifiServiceMessenger(String packageName);
 
     void enableTdls(String remoteIPAddress, boolean enable);
@@ -178,5 +181,9 @@
     void registerSoftApCallback(in IBinder binder, in ISoftApCallback callback, int callbackIdentifier);
 
     void unregisterSoftApCallback(int callbackIdentifier);
+
+    void registerTrafficStateCallback(in IBinder binder, in ITrafficStateCallback callback, int callbackIdentifier);
+
+    void unregisterTrafficStateCallback(int callbackIdentifier);
 }
 
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index bb3af3c..52f7a60 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -948,16 +948,15 @@
      * for Hotspot 2.0 defined matching of AAA server certs per WFA HS2.0 spec, section 7.3.3.2,
      * second paragraph.
      *
-     * From wpa_supplicant documentation:
-     * Constraint for server domain name. If set, this FQDN is used as a suffix match requirement
+     * <p>From wpa_supplicant documentation:
+     * <p>Constraint for server domain name. If set, this FQDN is used as a suffix match requirement
      * for the AAAserver certificate in SubjectAltName dNSName element(s). If a matching dNSName is
-     * found, this constraint is met. If no dNSName values are present, this constraint is matched
-     * against SubjectName CN using same suffix match comparison.
-     * Suffix match here means that the host/domain name is compared one label at a time starting
+     * found, this constraint is met.
+     * <p>Suffix match here means that the host/domain name is compared one label at a time starting
      * from the top-level domain and all the labels in domain_suffix_match shall be included in the
      * certificate. The certificate may include additional sub-level labels in addition to the
      * required labels.
-     * For example, domain_suffix_match=example.com would match test.example.com but would not
+     * <p>For example, domain_suffix_match=example.com would match test.example.com but would not
      * match test-example.com.
      * @param domain The domain value
      */
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 25f35d0..f16d006 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -21,7 +21,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
-import android.annotation.SuppressLint;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -32,9 +31,9 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
+import android.net.wifi.hotspot2.IProvisioningCallback;
 import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
-import android.net.wifi.hotspot2.IProvisioningCallback;
 import android.net.wifi.hotspot2.ProvisioningCallback;
 import android.os.Binder;
 import android.os.Build;
@@ -758,9 +757,12 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String BATCHED_SCAN_RESULTS_AVAILABLE_ACTION =
             "android.net.wifi.BATCHED_RESULTS";
+
     /**
      * The RSSI (signal strength) has changed.
-     * @see #EXTRA_NEW_RSSI
+     *
+     * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE
+     * @see {@link #EXTRA_NEW_RSSI}
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
@@ -920,22 +922,6 @@
      */
     public static final int WIFI_FREQUENCY_BAND_2GHZ = 2;
 
-    /** List of asyncronous notifications
-     * @hide
-     */
-    public static final int DATA_ACTIVITY_NOTIFICATION = 1;
-
-    //Lowest bit indicates data reception and the second lowest
-    //bit indicates data transmitted
-    /** @hide */
-    public static final int DATA_ACTIVITY_NONE         = 0x00;
-    /** @hide */
-    public static final int DATA_ACTIVITY_IN           = 0x01;
-    /** @hide */
-    public static final int DATA_ACTIVITY_OUT          = 0x02;
-    /** @hide */
-    public static final int DATA_ACTIVITY_INOUT        = 0x03;
-
     /** @hide */
     public static final boolean DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED = false;
 
@@ -1585,13 +1571,12 @@
      * Return the record of {@link WifiActivityEnergyInfo} object that
      * has the activity and energy info. This can be used to ascertain what
      * the controller has been up to, since the last sample.
-     * @param updateType Type of info, cached vs refreshed.
      *
      * @return a record with {@link WifiActivityEnergyInfo} or null if
      * report is unavailable or unsupported
      * @hide
      */
-    public WifiActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) {
+    public WifiActivityEnergyInfo getControllerActivityEnergyInfo() {
         if (mService == null) return null;
         try {
             synchronized(this) {
@@ -2170,6 +2155,21 @@
     }
 
     /**
+     * Method that triggers a notification to the user about a conversion to their saved AP config.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void notifyUserOfApBandConversion() {
+        Log.d(TAG, "apBand was converted, notify the user");
+        try {
+            mService.notifyUserOfApBandConversion(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Enable/Disable TDLS on a specific local route.
      *
      * <p>
@@ -2446,7 +2446,7 @@
         }
 
         @Override
-        public void onStateChanged(int state, int failureReason) throws RemoteException {
+        public void onStateChanged(int state, int failureReason) {
             Log.v(TAG, "SoftApCallbackProxy: onStateChanged: state=" + state + ", failureReason=" +
                     failureReason);
             mHandler.post(() -> {
@@ -2455,7 +2455,7 @@
         }
 
         @Override
-        public void onNumClientsChanged(int numClients) throws RemoteException {
+        public void onNumClientsChanged(int numClients) {
             Log.v(TAG, "SoftApCallbackProxy: onNumClientsChanged: numClients=" + numClients);
             mHandler.post(() -> {
                 mCallback.onNumClientsChanged(numClients);
@@ -3088,9 +3088,8 @@
      * an AsyncChannel communication with WifiService
      *
      * @return Messenger pointing to the WifiService handler
-     * @hide
      */
-    public Messenger getWifiServiceMessenger() {
+    private Messenger getWifiServiceMessenger() {
         try {
             return mService.getWifiServiceMessenger(mContext.getOpPackageName());
         } catch (RemoteException e) {
@@ -3704,4 +3703,107 @@
             });
         }
     }
+
+    /**
+     * Base class for Traffic state callback. Should be extended by applications and set when
+     * calling {@link WifiManager#registerTrafficStateCallback(TrafficStateCallback, Handler)}.
+     * @hide
+     */
+    public interface TrafficStateCallback {
+        /**
+         * Lowest bit indicates data reception and the second lowest
+         * bit indicates data transmitted
+         */
+        /** @hide */
+        int DATA_ACTIVITY_NONE         = 0x00;
+        /** @hide */
+        int DATA_ACTIVITY_IN           = 0x01;
+        /** @hide */
+        int DATA_ACTIVITY_OUT          = 0x02;
+        /** @hide */
+        int DATA_ACTIVITY_INOUT        = 0x03;
+
+        /**
+         * Callback invoked to inform clients about the current traffic state.
+         *
+         * @param state One of the values: {@link #DATA_ACTIVITY_NONE}, {@link #DATA_ACTIVITY_IN},
+         * {@link #DATA_ACTIVITY_OUT} & {@link #DATA_ACTIVITY_INOUT}.
+         * @hide
+         */
+        void onStateChanged(int state);
+    }
+
+    /**
+     * Callback proxy for TrafficStateCallback objects.
+     *
+     * @hide
+     */
+    private static class TrafficStateCallbackProxy extends ITrafficStateCallback.Stub {
+        private final Handler mHandler;
+        private final TrafficStateCallback mCallback;
+
+        TrafficStateCallbackProxy(Looper looper, TrafficStateCallback callback) {
+            mHandler = new Handler(looper);
+            mCallback = callback;
+        }
+
+        @Override
+        public void onStateChanged(int state) {
+            Log.v(TAG, "TrafficStateCallbackProxy: onStateChanged state=" + state);
+            mHandler.post(() -> {
+                mCallback.onStateChanged(state);
+            });
+        }
+    }
+
+    /**
+     * Registers a callback for monitoring traffic state. See {@link TrafficStateCallback}. These
+     * callbacks will be invoked periodically by platform to inform clients about the current
+     * traffic state. Caller can unregister a previously registered callback using
+     * {@link #unregisterTrafficStateCallback(TrafficStateCallback)}
+     * <p>
+     * Applications should have the
+     * {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers
+     * without the permission will trigger a {@link java.lang.SecurityException}.
+     * <p>
+     *
+     * @param callback Callback for traffic state events
+     * @param handler  The Handler on whose thread to execute the callbacks of the {@code callback}
+     *                 object. If null, then the application's main thread will be used.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void registerTrafficStateCallback(@NonNull TrafficStateCallback callback,
+                                             @Nullable Handler handler) {
+        if (callback == null) throw new IllegalArgumentException("callback cannot be null");
+        Log.v(TAG, "registerTrafficStateCallback: callback=" + callback + ", handler=" + handler);
+
+        Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
+        Binder binder = new Binder();
+        try {
+            mService.registerTrafficStateCallback(
+                    binder, new TrafficStateCallbackProxy(looper, callback), callback.hashCode());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Allow callers to unregister a previously registered callback. After calling this method,
+     * applications will no longer receive traffic state notifications.
+     *
+     * @param callback Callback to unregister for traffic state events
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void unregisterTrafficStateCallback(@NonNull TrafficStateCallback callback) {
+        if (callback == null) throw new IllegalArgumentException("callback cannot be null");
+        Log.v(TAG, "unregisterTrafficStateCallback: callback=" + callback);
+
+        try {
+            mService.unregisterTrafficStateCallback(callback.hashCode());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 928a1da..045291b 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi;
 
+import android.Manifest;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -721,6 +722,17 @@
     }
 
     /**
+     * Enable/Disable wifi scanning.
+     *
+     * {@hide}
+     */
+    @RequiresPermission(Manifest.permission.NETWORK_STACK)
+    public void setScanningEnabled(boolean enable) {
+        validateChannel();
+        mAsyncChannel.sendMessage(enable ? CMD_ENABLE : CMD_DISABLE);
+    }
+
+    /**
      * Register a listener that will receive results from all single scans
      * Either the onSuccess/onFailure will be called once when the listener is registered. After
      * (assuming onSuccess was called) all subsequent single scan results will be delivered to the
@@ -916,6 +928,7 @@
      *                 scans should also not share this object.
      * {@hide}
      */
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
             PnoScanListener listener) {
         Preconditions.checkNotNull(listener, "listener cannot be null");
@@ -930,9 +943,9 @@
      * Stop an ongoing wifi PNO scan
      * @param listener specifies which scan to cancel; must be same object as passed in {@link
      *  #startPnoScan}
-     * TODO(rpius): Check if we can remove pnoSettings param in stop.
      * {@hide}
      */
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     public void stopPnoScan(ScanListener listener) {
         Preconditions.checkNotNull(listener, "listener cannot be null");
         int key = removeListener(listener);
@@ -1164,6 +1177,10 @@
     public static final int CMD_DEREGISTER_SCAN_LISTENER    = BASE + 28;
     /** @hide */
     public static final int CMD_GET_SINGLE_SCAN_RESULTS     = BASE + 29;
+    /** @hide */
+    public static final int CMD_ENABLE                      = BASE + 30;
+    /** @hide */
+    public static final int CMD_DISABLE                     = BASE + 31;
 
     private Context mContext;
     private IWifiScanner mService;
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index 2ea6e79..66297fc 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -16,11 +16,12 @@
 
 package android.net.wifi.hotspot2;
 
+import android.net.wifi.WifiManager;
 import android.os.Handler;
 
 /**
  * Base class for provisioning callbacks. Should be extended by applications and set when calling
- * {@link WifiManager#startSubscriptionProvisiong(OsuProvider, ProvisioningCallback, Handler)}.
+ * {@link WifiManager#startSubscriptionProvisioning(OsuProvider, ProvisioningCallback, Handler)}.
  *
  * @hide
  */
@@ -28,84 +29,101 @@
 
     /**
      * The reason code for Provisioning Failure due to connection failure to OSU AP.
-     * @hide
      */
-    public static final int OSU_FAILURE_AP_CONNECTION      = 1;
+    public static final int OSU_FAILURE_AP_CONNECTION = 1;
 
     /**
-     * The reason code for Provisioning Failure due to connection failure to OSU AP.
-     * @hide
+     * The reason code for invalid server URL address.
      */
     public static final int OSU_FAILURE_SERVER_URL_INVALID = 2;
 
     /**
-     * The reason code for Provisioning Failure due to connection failure to OSU AP.
-     * @hide
+     * The reason code for provisioning failure due to connection failure to the server.
      */
-    public static final int OSU_FAILURE_SERVER_CONNECTION  = 3;
+    public static final int OSU_FAILURE_SERVER_CONNECTION = 3;
 
     /**
-     * The reason code for Provisioning Failure due to connection failure to OSU AP.
-     * @hide
+     * The reason code for provisioning failure due to invalid server certificate.
      */
-    public static final int OSU_FAILURE_SERVER_VALIDATION  = 4;
+    public static final int OSU_FAILURE_SERVER_VALIDATION = 4;
 
     /**
-     * The reason code for Provisioning Failure due to connection failure to OSU AP.
-     * @hide
+     * The reason code for provisioning failure due to invalid service provider.
      */
-    public static final int OSU_FAILURE_PROVIDER_VERIFICATION = 5;
+    public static final int OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION = 5;
 
     /**
-     * The reason code for Provisioning Failure when a provisioning flow is aborted.
-     * @hide
+     * The reason code for provisioning failure when a provisioning flow is aborted.
      */
     public static final int OSU_FAILURE_PROVISIONING_ABORTED = 6;
 
     /**
-     * The reason code for Provisioning Failure when a provisioning flow is aborted.
-     * @hide
+     * The reason code for provisioning failure when a provisioning flow is not possible.
      */
     public static final int OSU_FAILURE_PROVISIONING_NOT_AVAILABLE = 7;
 
     /**
-     * The status code for Provisioning flow to indicate connecting to OSU AP
-     * @hide
+     * The reason code for provisioning failure due to invalid server url.
      */
-    public static final int OSU_STATUS_AP_CONNECTING       = 1;
+    public static final int OSU_FAILURE_INVALID_SERVER_URL = 8;
 
     /**
-     * The status code for Provisioning flow to indicate connected to OSU AP
-     * @hide
+     * The reason code for provisioning failure when a command received is not the expected command
+     * type.
      */
-    public static final int OSU_STATUS_AP_CONNECTED        = 2;
+    public static final int OSU_FAILURE_UNEXPECTED_COMMAND_TYPE = 9;
 
     /**
-     * The status code for Provisioning flow to indicate connecting to OSU AP
-     * @hide
+     * The reason code for provisioning failure when a SOAP message is not the expected message
+     * type.
      */
-    public static final int OSU_STATUS_SERVER_CONNECTED    = 3;
+    public static final int OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE = 10;
 
     /**
-     * The status code for Provisioning flow to indicate connecting to OSU AP
-     * @hide
+     * The reason code for provisioning failure when a SOAP message exchange fails.
      */
-    public static final int OSU_STATUS_SERVER_VALIDATED    = 4;
+    public static final int OSU_FAILURE_SOAP_MESSAGE_EXCHANGE = 11;
 
     /**
-     * The status code for Provisioning flow to indicate connecting to OSU AP
-     * @hide
+     * The status code for provisioning flow to indicate connecting to OSU AP
      */
-    public static final int OSU_STATUS_PROVIDER_VERIFIED   = 5;
+    public static final int OSU_STATUS_AP_CONNECTING = 1;
+
+    /**
+     * The status code for provisioning flow to indicate the OSU AP is connected.
+     */
+    public static final int OSU_STATUS_AP_CONNECTED = 2;
+
+    /**
+     * The status code for provisioning flow to indicate the server connection is completed.
+     */
+    public static final int OSU_STATUS_SERVER_CONNECTED = 3;
+
+    /**
+     * The status code for provisioning flow to indicate the server certificate is validated.
+     */
+    public static final int OSU_STATUS_SERVER_VALIDATED = 4;
+
+    /**
+     * The status code for provisioning flow to indicate the service provider is verified.
+     */
+    public static final int OSU_STATUS_SERVICE_PROVIDER_VERIFIED = 5;
+
+    /**
+     * The status code for provisioning flow to indicate starting the SOAP exchange.
+     */
+    public static final int OSU_STATUS_INIT_SOAP_EXCHANGE = 6;
 
     /**
      * Provisioning status for OSU failure
+     *
      * @param status indicates error condition
      */
     public abstract void onProvisioningFailure(int status);
 
     /**
      * Provisioning status when OSU is in progress
+     *
      * @param status indicates status of OSU flow
      */
     public abstract void onProvisioningStatus(int status);
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java
index f6183fa..984cf7d 100644
--- a/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java
@@ -136,7 +136,7 @@
     private static final String NODE_UPDATE_IDENTIFIER = "UpdateIdentifier";
     private static final String NODE_AAA_SERVER_TRUST_ROOT = "AAAServerTrustRoot";
     private static final String NODE_SUBSCRIPTION_UPDATE = "SubscriptionUpdate";
-    private static final String NODE_SUBSCRIPTION_PARAMETER = "SubscriptionParameter";
+    private static final String NODE_SUBSCRIPTION_PARAMETER = "SubscriptionParameters";
     private static final String NODE_TYPE_OF_SUBSCRIPTION = "TypeOfSubscription";
     private static final String NODE_USAGE_LIMITS = "UsageLimits";
     private static final String NODE_DATA_LIMIT = "DataLimit";
diff --git a/wifi/tests/assets/pps/PerProviderSubscription.xml b/wifi/tests/assets/pps/PerProviderSubscription.xml
index 1fb8309..e4472ce 100644
--- a/wifi/tests/assets/pps/PerProviderSubscription.xml
+++ b/wifi/tests/assets/pps/PerProviderSubscription.xml
@@ -368,7 +368,7 @@
         </Node>
       </Node>
       <Node>
-        <NodeName>SubscriptionParameter</NodeName>
+        <NodeName>SubscriptionParameters</NodeName>
         <Node>
           <NodeName>CreationDate</NodeName>
           <Value>2016-02-01T10:00:00Z</Value>
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 20e49cd..5058080 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -25,9 +25,6 @@
 import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED;
 import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.REQUEST_REGISTERED;
 import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL;
-import static android.net.wifi.WifiManager.SAP_START_FAILURE_NO_CHANNEL;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
@@ -47,6 +44,7 @@
 import android.net.wifi.WifiManager.LocalOnlyHotspotReservation;
 import android.net.wifi.WifiManager.LocalOnlyHotspotSubscription;
 import android.net.wifi.WifiManager.SoftApCallback;
+import android.net.wifi.WifiManager.TrafficStateCallback;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -57,6 +55,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -77,6 +76,7 @@
     @Mock WifiConfiguration mApConfig;
     @Mock IBinder mAppBinder;
     @Mock SoftApCallback mSoftApCallback;
+    @Mock TrafficStateCallback mTrafficStateCallback;
 
     private Handler mHandler;
     private TestLooper mLooper;
@@ -702,7 +702,7 @@
     }
 
     /*
-     * Verify client provided callback is being called through callback proxy
+     * Verify client-provided callback is being called through callback proxy
      */
     @Test
     public void softApCallbackProxyCallsOnStateChanged() throws Exception {
@@ -718,7 +718,7 @@
     }
 
     /*
-     * Verify client provided callback is being called through callback proxy
+     * Verify client-provided callback is being called through callback proxy
      */
     @Test
     public void softApCallbackProxyCallsOnNumClientsChanged() throws Exception {
@@ -735,7 +735,7 @@
     }
 
     /*
-     * Verify client provided callback is being called through callback proxy on multiple events
+     * Verify client-provided callback is being called through callback proxy on multiple events
      */
     @Test
     public void softApCallbackProxyCallsOnMultipleUpdates() throws Exception {
@@ -757,7 +757,7 @@
     }
 
     /*
-     * Verify client provided callback is being called on the correct thread
+     * Verify client-provided callback is being called on the correct thread
      */
     @Test
     public void softApCallbackIsCalledOnCorrectThread() throws Exception {
@@ -1082,4 +1082,84 @@
         when(mWifiService.startScan(TEST_PACKAGE_NAME)).thenReturn(false);
         assertFalse(mWifiManager.startScan());
     }
+
+    /**
+     * Verify main looper is used when handler is not provided.
+     */
+    @Test
+    public void registerTrafficStateCallbackUsesMainLooperOnNullArgumentForHandler()
+            throws Exception {
+        when(mContext.getMainLooper()).thenReturn(mLooper.getLooper());
+        ArgumentCaptor<ITrafficStateCallback.Stub> callbackCaptor =
+                ArgumentCaptor.forClass(ITrafficStateCallback.Stub.class);
+        mWifiManager.registerTrafficStateCallback(mTrafficStateCallback, null);
+        verify(mWifiService).registerTrafficStateCallback(
+                any(IBinder.class), callbackCaptor.capture(), anyInt());
+
+        assertEquals(0, mLooper.dispatchAll());
+        callbackCaptor.getValue().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT);
+        assertEquals(1, mLooper.dispatchAll());
+        verify(mTrafficStateCallback).onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT);
+    }
+
+    /**
+     * Verify the call to unregisterTrafficStateCallback goes to WifiServiceImpl.
+     */
+    @Test
+    public void unregisterTrafficStateCallbackCallGoesToWifiServiceImpl() throws Exception {
+        ArgumentCaptor<Integer> callbackIdentifier = ArgumentCaptor.forClass(Integer.class);
+        mWifiManager.registerTrafficStateCallback(mTrafficStateCallback, mHandler);
+        verify(mWifiService).registerTrafficStateCallback(any(IBinder.class),
+                any(ITrafficStateCallback.Stub.class), callbackIdentifier.capture());
+
+        mWifiManager.unregisterTrafficStateCallback(mTrafficStateCallback);
+        verify(mWifiService).unregisterTrafficStateCallback(
+                eq((int) callbackIdentifier.getValue()));
+    }
+
+    /*
+     * Verify client-provided callback is being called through callback proxy on multiple events
+     */
+    @Test
+    public void trafficStateCallbackProxyCallsOnMultipleUpdates() throws Exception {
+        ArgumentCaptor<ITrafficStateCallback.Stub> callbackCaptor =
+                ArgumentCaptor.forClass(ITrafficStateCallback.Stub.class);
+        mWifiManager.registerTrafficStateCallback(mTrafficStateCallback, mHandler);
+        verify(mWifiService).registerTrafficStateCallback(
+                any(IBinder.class), callbackCaptor.capture(), anyInt());
+
+        InOrder inOrder = inOrder(mTrafficStateCallback);
+
+        callbackCaptor.getValue().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_IN);
+        callbackCaptor.getValue().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT);
+        callbackCaptor.getValue().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_OUT);
+
+        mLooper.dispatchAll();
+        inOrder.verify(mTrafficStateCallback).onStateChanged(
+                TrafficStateCallback.DATA_ACTIVITY_IN);
+        inOrder.verify(mTrafficStateCallback).onStateChanged(
+                TrafficStateCallback.DATA_ACTIVITY_INOUT);
+        inOrder.verify(mTrafficStateCallback).onStateChanged(
+                TrafficStateCallback.DATA_ACTIVITY_OUT);
+    }
+
+    /*
+     * Verify client-provided callback is being called on the correct thread
+     */
+    @Test
+    public void trafficStateCallbackIsCalledOnCorrectThread() throws Exception {
+        ArgumentCaptor<ITrafficStateCallback.Stub> callbackCaptor =
+                ArgumentCaptor.forClass(ITrafficStateCallback.Stub.class);
+        TestLooper altLooper = new TestLooper();
+        Handler altHandler = new Handler(altLooper.getLooper());
+        mWifiManager.registerTrafficStateCallback(mTrafficStateCallback, altHandler);
+        verify(mContext, never()).getMainLooper();
+        verify(mWifiService).registerTrafficStateCallback(
+                any(IBinder.class), callbackCaptor.capture(), anyInt());
+
+        assertEquals(0, altLooper.dispatchAll());
+        callbackCaptor.getValue().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT);
+        assertEquals(1, altLooper.dispatchAll());
+        verify(mTrafficStateCallback).onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT);
+    }
 }